From a229a9dd64821575b27e6e6e317a1ce97e23f6d7 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 17 Sep 2021 11:30:05 +0200 Subject: [PATCH 0001/1500] Fix T91461: Pose Library name filter not working since `AssetHandle` does not have a `name_property` (`RNA_def_struct_name_property`), and the UIList is just using the default `uilist_filter_items_default` it simply cannot filter on names (`RNA_struct_name_get_alloc` wont succeed). Adding a name_property also wont work since `AssetHandle` inherits `PropertyGroup` (which already sets name_property). So this adds a (temporary) hack exception for RNA_AssetHandle in uilist_filter_items_default until the design of `AssetHandle` progresses further. thx @Severin for additional feedback Maniphest Tasks: T91461 Differential Revision: https://developer.blender.org/D12541 --- .../editors/interface/interface_template_list.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 8246759ad36..845a7813da2 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -31,6 +31,7 @@ #include "BLT_translation.h" +#include "ED_asset.h" #include "ED_screen.h" #include "MEM_guardedalloc.h" @@ -216,7 +217,18 @@ static void uilist_filter_items_default(struct uiList *ui_list, RNA_PROP_BEGIN (dataptr, itemptr, prop) { bool do_order = false; - char *namebuf = RNA_struct_name_get_alloc(&itemptr, nullptr, 0, nullptr); + char *namebuf; + if (RNA_struct_is_a(itemptr.type, &RNA_AssetHandle)) { + /* XXX The AssetHandle design is hacky and meant to be temporary. It can't have a proper + * name property, so for now this hardcoded exception is needed. */ + AssetHandle *asset_handle = (AssetHandle *)itemptr.data; + const char *asset_name = ED_asset_handle_get_name(asset_handle); + namebuf = BLI_strdup(asset_name); + } + else { + namebuf = RNA_struct_name_get_alloc(&itemptr, nullptr, 0, nullptr); + } + const char *name = namebuf ? namebuf : ""; if (filter[0]) { From 136e357d8dd541eeabc3f77dce8079d807bce8b4 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 18 Sep 2021 13:35:05 +0200 Subject: [PATCH 0002/1500] Cleanup: typo --- source/blender/makesdna/intern/makesdna.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 0b7848b6662..9957808b63d 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -1134,7 +1134,7 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char * to the struct to resolve the problem. */ if ((size_64 % max_align_64 == 0) && (size_32 % max_align_32 == 4)) { fprintf(stderr, - "Sizeerror in 32 bit struct: %s (add paddding pointer)\n", + "Sizeerror in 32 bit struct: %s (add padding pointer)\n", types[structtype]); } else { From 2618df7d03bffff1ea2a750290d35ef4b6a8c072 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 18 Sep 2021 13:36:20 +0200 Subject: [PATCH 0003/1500] Cleanup: add missing includes --- source/blender/blenlib/intern/uuid.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index 7f1ec0e677b..0c381e5dcd2 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -22,7 +22,9 @@ #include #include +#include #include +#include /* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */ static_assert(sizeof(UUID) == 16, "expect UUIDs to be 128 bit exactly"); From 970c928f27106b26ec7cf6afa2316c60384ab4f1 Mon Sep 17 00:00:00 2001 From: Jorge Bernal Date: Sat, 18 Sep 2021 19:28:55 +0200 Subject: [PATCH 0004/1500] Py API Docs: Fix audio docs example After new AUD API changes from 2.8x what "buffer" function used to do has now become "cache" function (it caches a sound into RAM). Therefore, the basic aud example should call this new "cache" function instead of "buffer" function. Thanks to Michael-Z-Freeman for pointing out. --- doc/python_api/examples/aud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/python_api/examples/aud.py b/doc/python_api/examples/aud.py index a52258c1a45..0eb27647671 100644 --- a/doc/python_api/examples/aud.py +++ b/doc/python_api/examples/aud.py @@ -14,7 +14,7 @@ sound = aud.Sound('music.ogg') # play the audio, this return a handle to control play/pause handle = device.play(sound) # if the audio is not too big and will be used often you can buffer it -sound_buffered = aud.Sound.buffer(sound) +sound_buffered = aud.Sound.cache(sound) handle_buffered = device.play(sound_buffered) # stop the sounds (otherwise they play until their ends) From bdbc7e12a02e15ad7265dfc1ac21fb6d0016308f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20M=C3=BCller?= Date: Mon, 30 Aug 2021 22:36:02 +0200 Subject: [PATCH 0005/1500] Audaspace: added audio file streams functionality. On the blender side this commit fixes importing video files with audio and video streams that do not share the same start time and duration. Differential Revision: https://developer.blender.org/D12353 --- extern/audaspace/CMakeLists.txt | 6 +- .../bindings/C/AUD_PlaybackManager.h | 2 +- extern/audaspace/bindings/C/AUD_Sound.cpp | 42 +++++ extern/audaspace/bindings/C/AUD_Sound.h | 27 ++- extern/audaspace/bindings/C/AUD_Special.cpp | 8 +- extern/audaspace/bindings/C/AUD_Special.h | 2 +- extern/audaspace/bindings/C/AUD_Types.h | 14 +- extern/audaspace/bindings/python/PySound.cpp | 12 +- extern/audaspace/include/IReader.h | 6 - extern/audaspace/include/file/File.h | 23 ++- extern/audaspace/include/file/FileInfo.h | 42 +++++ extern/audaspace/include/file/FileManager.h | 24 ++- extern/audaspace/include/file/IFileInput.h | 24 ++- extern/audaspace/include/fx/VolumeReader.h | 2 +- extern/audaspace/plugins/ffmpeg/FFMPEG.cpp | 18 +- extern/audaspace/plugins/ffmpeg/FFMPEG.h | 6 +- .../audaspace/plugins/ffmpeg/FFMPEGReader.cpp | 169 +++++++++++------- .../audaspace/plugins/ffmpeg/FFMPEGReader.h | 42 ++--- .../audaspace/plugins/libsndfile/SndFile.cpp | 14 +- extern/audaspace/plugins/libsndfile/SndFile.h | 6 +- .../plugins/libsndfile/SndFileReader.cpp | 15 ++ .../plugins/libsndfile/SndFileReader.h | 12 ++ extern/audaspace/src/file/File.cpp | 20 ++- extern/audaspace/src/file/FileManager.cpp | 36 +++- extern/audaspace/src/fx/VolumeReader.cpp | 2 +- source/blender/blenkernel/BKE_sound.h | 13 +- source/blender/blenkernel/intern/sound.c | 47 ++++- .../blender/editors/space_graph/graph_edit.c | 3 +- .../editors/space_sequencer/sequencer_add.c | 62 ++++++- .../editors/space_sequencer/sequencer_draw.c | 4 + source/blender/imbuf/intern/anim_movie.c | 7 +- .../makesrna/intern/rna_sequencer_api.c | 4 +- source/blender/sequencer/SEQ_add.h | 2 +- source/blender/sequencer/intern/strip_add.c | 74 ++++---- 34 files changed, 605 insertions(+), 185 deletions(-) create mode 100644 extern/audaspace/include/file/FileInfo.h diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt index 1599c03cbad..552ff749512 100644 --- a/extern/audaspace/CMakeLists.txt +++ b/extern/audaspace/CMakeLists.txt @@ -152,6 +152,7 @@ set(PUBLIC_HDR include/devices/ThreadedDevice.h include/Exception.h include/file/File.h + include/file/FileInfo.h include/file/FileManager.h include/file/FileWriter.h include/file/IFileInput.h @@ -960,7 +961,10 @@ endif() if(BUILD_DEMOS) include_directories(${INCLUDE}) - set(DEMOS audaplay audaconvert audaremap signalgen randsounds dynamicmusic playbackmanager) + set(DEMOS audainfo audaplay audaconvert audaremap signalgen randsounds dynamicmusic playbackmanager) + + add_executable(audainfo demos/audainfo.cpp) + target_link_libraries(audainfo audaspace) add_executable(audaplay demos/audaplay.cpp) target_link_libraries(audaplay audaspace) diff --git a/extern/audaspace/bindings/C/AUD_PlaybackManager.h b/extern/audaspace/bindings/C/AUD_PlaybackManager.h index 0fa8171599d..a2f5134602a 100644 --- a/extern/audaspace/bindings/C/AUD_PlaybackManager.h +++ b/extern/audaspace/bindings/C/AUD_PlaybackManager.h @@ -39,7 +39,7 @@ extern AUD_API void AUD_PlaybackManager_free(AUD_PlaybackManager* manager); * Plays a sound through the playback manager, adding it into a category. * \param manager The PlaybackManager object. * \param sound The sound to be played. -* \param catKey The key of the category into which the sound will be added. If it doesn't exist a new one will be creatd. +* \param catKey The key of the category into which the sound will be added. If it doesn't exist a new one will be created. */ extern AUD_API void AUD_PlaybackManager_play(AUD_PlaybackManager* manager, AUD_Sound* sound, unsigned int catKey); diff --git a/extern/audaspace/bindings/C/AUD_Sound.cpp b/extern/audaspace/bindings/C/AUD_Sound.cpp index 8c99ce2341f..aa246b9a047 100644 --- a/extern/audaspace/bindings/C/AUD_Sound.cpp +++ b/extern/audaspace/bindings/C/AUD_Sound.cpp @@ -94,6 +94,36 @@ AUD_API int AUD_Sound_getLength(AUD_Sound* sound) return (*sound)->createReader()->getLength(); } +AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo **stream_infos) +{ + assert(sound); + + std::shared_ptr file = std::dynamic_pointer_cast(*sound); + + if(file) + { + auto streams = file->queryStreams(); + + size_t size = sizeof(AUD_StreamInfo) * streams.size(); + + if(!size) + { + *stream_infos = nullptr; + return 0; + } + + *stream_infos = reinterpret_cast(std::malloc(size)); + std::memcpy(*stream_infos, streams.data(), size); + + return streams.size(); + } + else + { + *stream_infos = nullptr; + return 0; + } +} + AUD_API sample_t* AUD_Sound_data(AUD_Sound* sound, int* length, AUD_Specs* specs) { assert(sound); @@ -252,6 +282,12 @@ AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size) return new AUD_Sound(new File(buffer, size)); } +AUD_API AUD_Sound* AUD_Sound_bufferFileStream(unsigned char* buffer, int size, int stream) +{ + assert(buffer); + return new AUD_Sound(new File(buffer, size, stream)); +} + AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound) { assert(sound); @@ -272,6 +308,12 @@ AUD_API AUD_Sound* AUD_Sound_file(const char* filename) return new AUD_Sound(new File(filename)); } +AUD_API AUD_Sound* AUD_Sound_fileStream(const char* filename, int stream) +{ + assert(filename); + return new AUD_Sound(new File(filename, stream)); +} + AUD_API AUD_Sound* AUD_Sound_sawtooth(float frequency, AUD_SampleRate rate) { return new AUD_Sound(new Sawtooth(frequency, rate)); diff --git a/extern/audaspace/bindings/C/AUD_Sound.h b/extern/audaspace/bindings/C/AUD_Sound.h index 53172616781..fc73a31e15c 100644 --- a/extern/audaspace/bindings/C/AUD_Sound.h +++ b/extern/audaspace/bindings/C/AUD_Sound.h @@ -36,7 +36,15 @@ extern AUD_API AUD_Specs AUD_Sound_getSpecs(AUD_Sound* sound); * \return The length of the sound in samples. * \note This function creates a reader from the sound and deletes it again. */ -extern AUD_API int AUD_getLength(AUD_Sound* sound); +extern AUD_API int AUD_Sound_getLength(AUD_Sound* sound); + +/** + * Retrieves the stream infos of a sound file. + * \param sound The sound to retrieve from which must be a file sound. + * \param infos A pointer to a AUD_StreamInfo array that will be allocated and must afterwards be freed by the caller. + * \return The number of items in the infos array. + */ +extern AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo** stream_infos); /** * Reads a sound's samples into memory. @@ -89,6 +97,15 @@ extern AUD_API AUD_Sound* AUD_Sound_buffer(sample_t* data, int length, AUD_Specs */ extern AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size); +/** + * Loads a sound file from a memory buffer. + * \param buffer The buffer which contains the sound file. + * \param size The size of the buffer. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. + * \return A handle of the sound file. + */ +extern AUD_API AUD_Sound* AUD_Sound_bufferFileStream(unsigned char* buffer, int size, int stream); + /** * Caches a sound into a memory buffer. * \param sound The sound to cache. @@ -103,6 +120,14 @@ extern AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound); */ extern AUD_API AUD_Sound* AUD_Sound_file(const char* filename); +/** + * Loads a sound file. + * \param filename The filename of the sound file. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. + * \return A handle of the sound file. + */ +extern AUD_API AUD_Sound* AUD_Sound_fileStream(const char* filename, int stream); + /** * Creates a sawtooth sound. * \param frequency The frequency of the generated sawtooth sound. diff --git a/extern/audaspace/bindings/C/AUD_Special.cpp b/extern/audaspace/bindings/C/AUD_Special.cpp index 5cc33525d1d..1ce25dcd41c 100644 --- a/extern/audaspace/bindings/C/AUD_Special.cpp +++ b/extern/audaspace/bindings/C/AUD_Special.cpp @@ -86,7 +86,6 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) info.specs.channels = AUD_CHANNELS_INVALID; info.specs.rate = AUD_RATE_INVALID; info.length = 0.0f; - info.start_offset = 0.0f; try { @@ -96,7 +95,6 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) { info.specs = convSpecToC(reader->getSpecs()); info.length = reader->getLength() / (float) info.specs.rate; - info.start_offset = reader->getStartOffset(); } } catch(Exception&) @@ -109,7 +107,7 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high, float attack, float release, float threshold, int accumulate, int additive, int square, - float sthreshold, double samplerate, int* length) + float sthreshold, double samplerate, int* length, int stream) { Buffer buffer; DeviceSpecs specs; @@ -117,7 +115,7 @@ AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high, specs.rate = (SampleRate)samplerate; std::shared_ptr sound; - std::shared_ptr file = std::shared_ptr(new File(filename)); + std::shared_ptr file = std::shared_ptr(new File(filename, stream)); int position = 0; @@ -247,7 +245,7 @@ AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int sampl buffer[i * 3] = min; buffer[i * 3 + 1] = max; - buffer[i * 3 + 2] = sqrt(power / len); // RMS + buffer[i * 3 + 2] = std::sqrt(power / len); if(overallmax < max) overallmax = max; diff --git a/extern/audaspace/bindings/C/AUD_Special.h b/extern/audaspace/bindings/C/AUD_Special.h index ce51fa2e04e..2f5d13c6fd9 100644 --- a/extern/audaspace/bindings/C/AUD_Special.h +++ b/extern/audaspace/bindings/C/AUD_Special.h @@ -37,7 +37,7 @@ extern AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float float attack, float release, float threshold, int accumulate, int additive, int square, float sthreshold, double samplerate, - int* length); + int* length, int stream); /** * Pauses a playing sound after a specific amount of time. diff --git a/extern/audaspace/bindings/C/AUD_Types.h b/extern/audaspace/bindings/C/AUD_Types.h index c6a96d30d3f..0f95366bc27 100644 --- a/extern/audaspace/bindings/C/AUD_Types.h +++ b/extern/audaspace/bindings/C/AUD_Types.h @@ -176,5 +176,17 @@ typedef struct { AUD_Specs specs; float length; - double start_offset; } AUD_SoundInfo; + +/// Specification of a sound source. +typedef struct +{ + /// Start time in seconds. + double start; + + /// Duration in seconds. May be estimated or 0 if unknown. + double duration; + + /// Audio data parameters. + AUD_DeviceSpecs specs; +} AUD_StreamInfo; diff --git a/extern/audaspace/bindings/python/PySound.cpp b/extern/audaspace/bindings/python/PySound.cpp index 33628307249..2236057e7d2 100644 --- a/extern/audaspace/bindings/python/PySound.cpp +++ b/extern/audaspace/bindings/python/PySound.cpp @@ -89,10 +89,11 @@ Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds) self = (Sound*)type->tp_alloc(type, 0); if(self != nullptr) { - static const char* kwlist[] = {"filename", nullptr}; + static const char* kwlist[] = {"filename", "stream", nullptr}; const char* filename = nullptr; + int stream = 0; - if(!PyArg_ParseTupleAndKeywords(args, kwds, "s:Sound", const_cast(kwlist), &filename)) + if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|i:Sound", const_cast(kwlist), &filename, &stream)) { Py_DECREF(self); return nullptr; @@ -100,7 +101,7 @@ Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds) try { - self->sound = new std::shared_ptr(new File(filename)); + self->sound = new std::shared_ptr(new File(filename, stream)); } catch(Exception& e) { @@ -407,8 +408,9 @@ static PyObject * Sound_file(PyTypeObject* type, PyObject* args) { const char* filename = nullptr; + int stream = 0; - if(!PyArg_ParseTuple(args, "s:file", &filename)) + if(!PyArg_ParseTuple(args, "s|i:file", &filename, &stream)) return nullptr; Sound* self; @@ -418,7 +420,7 @@ Sound_file(PyTypeObject* type, PyObject* args) { try { - self->sound = new std::shared_ptr(new File(filename)); + self->sound = new std::shared_ptr(new File(filename, stream)); } catch(Exception& e) { diff --git a/extern/audaspace/include/IReader.h b/extern/audaspace/include/IReader.h index f6070b0f23b..c29900ca579 100644 --- a/extern/audaspace/include/IReader.h +++ b/extern/audaspace/include/IReader.h @@ -70,12 +70,6 @@ public: */ virtual int getPosition() const=0; - /** - * Returns the start offset the sound should have to line up with related sources. - * \return The required start offset in seconds. - */ - virtual double getStartOffset() const { return 0.0;} - /** * Returns the specification of the reader. * \return The Specs structure. diff --git a/extern/audaspace/include/file/File.h b/extern/audaspace/include/file/File.h index 24745a757e8..ac490acba38 100644 --- a/extern/audaspace/include/file/File.h +++ b/extern/audaspace/include/file/File.h @@ -23,9 +23,11 @@ */ #include "ISound.h" +#include "FileInfo.h" #include #include +#include AUD_NAMESPACE_BEGIN @@ -48,6 +50,14 @@ private: */ std::shared_ptr m_buffer; + /** + * The index of the stream within the file if it contains multiple. + * The first audio stream in the file has index 0 and the index increments by one + * for every other audio stream in the file. Other types of streams in the file + * do not count. + */ + int m_stream; + // delete copy constructor and operator= File(const File&) = delete; File& operator=(const File&) = delete; @@ -57,16 +67,25 @@ public: * Creates a new sound. * The file is read from the file system using the given path. * \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); + File(std::string filename, int stream = 0); /** * Creates a new sound. * The file is read from memory using the supplied buffer. * \param buffer The buffer to read from. * \param size The size of the buffer. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. */ - File(const data_t* buffer, int size); + File(const data_t* buffer, int size, int stream = 0); + + /** + * Queries the streams of the file. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + std::vector queryStreams(); virtual std::shared_ptr createReader(); }; diff --git a/extern/audaspace/include/file/FileInfo.h b/extern/audaspace/include/file/FileInfo.h new file mode 100644 index 00000000000..53ba99a5f67 --- /dev/null +++ b/extern/audaspace/include/file/FileInfo.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#pragma once + +/** + * @file FileInfo.h + * @ingroup file + * The FileInfo data structures. + */ + +#include "respec/Specification.h" + +AUD_NAMESPACE_BEGIN + +/// Specification of a sound source. +struct StreamInfo +{ + /// Start time in seconds. + double start; + + /// Duration in seconds. May be estimated or 0 if unknown. + double duration; + + /// Audio data parameters. + DeviceSpecs specs; +}; + +AUD_NAMESPACE_END diff --git a/extern/audaspace/include/file/FileManager.h b/extern/audaspace/include/file/FileManager.h index 56708607ea6..e19eef65b1c 100644 --- a/extern/audaspace/include/file/FileManager.h +++ b/extern/audaspace/include/file/FileManager.h @@ -22,12 +22,14 @@ * The FileManager class. */ +#include "FileInfo.h" #include "respec/Specification.h" #include "IWriter.h" #include #include #include +#include AUD_NAMESPACE_BEGIN @@ -66,18 +68,36 @@ public: /** * Creates a file reader for the given filename if a registed IFileInput is able to read it. * @param filename The path to the file. + * @param stream The index of the audio stream within the file if it contains multiple audio streams. * @return The reader created. * @exception Exception If no file input can read the file an exception is thrown. */ - static std::shared_ptr createReader(std::string filename); + static std::shared_ptr createReader(std::string filename, int stream = 0); /** * Creates a file reader for the given buffer if a registed IFileInput is able to read it. * @param buffer The buffer to read the file from. + * @param stream The index of the audio stream within the file if it contains multiple audio streams. * @return The reader created. * @exception Exception If no file input can read the file an exception is thrown. */ - static std::shared_ptr createReader(std::shared_ptr buffer); + static std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0); + + /** + * Queries the streams of a sound file. + * \param filename Path to the file to be read. + * \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 queryStreams(std::string filename); + + /** + * Queries the streams of a sound file. + * \param buffer The in-memory file buffer. + * \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 queryStreams(std::shared_ptr buffer); /** * Creates a file writer that writes a sound to the given file path. diff --git a/extern/audaspace/include/file/IFileInput.h b/extern/audaspace/include/file/IFileInput.h index 64074910d13..4a3fe446852 100644 --- a/extern/audaspace/include/file/IFileInput.h +++ b/extern/audaspace/include/file/IFileInput.h @@ -23,9 +23,11 @@ */ #include "Audaspace.h" +#include "FileInfo.h" #include #include +#include AUD_NAMESPACE_BEGIN @@ -48,18 +50,36 @@ public: /** * Creates a reader for a file to be read. * \param filename Path to the file to be read. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \return The reader that reads the file. * \exception Exception Thrown if the file specified cannot be read. */ - virtual std::shared_ptr createReader(std::string filename)=0; + virtual std::shared_ptr createReader(std::string filename, int stream = 0)=0; /** * Creates a reader for a file to be read from memory. * \param buffer The in-memory file buffer. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \return The reader that reads the file. * \exception Exception Thrown if the file specified cannot be read. */ - virtual std::shared_ptr createReader(std::shared_ptr buffer)=0; + virtual std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0)=0; + + /** + * Queries the streams of a sound file. + * \param filename Path to the file to be read. + * \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 queryStreams(std::string filename)=0; + + /** + * Queries the streams of a sound file. + * \param buffer The in-memory file buffer. + * \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 queryStreams(std::shared_ptr buffer)=0; }; AUD_NAMESPACE_END diff --git a/extern/audaspace/include/fx/VolumeReader.h b/extern/audaspace/include/fx/VolumeReader.h index f7169f4c78b..13b6845e931 100644 --- a/extern/audaspace/include/fx/VolumeReader.h +++ b/extern/audaspace/include/fx/VolumeReader.h @@ -67,4 +67,4 @@ public: virtual void read(int& length, bool& eos, sample_t* buffer); }; -AUD_NAMESPACE_END +AUD_NAMESPACE_END \ No newline at end of file diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp index 3ffe963b2b9..07c0fee691a 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp @@ -35,14 +35,24 @@ void FFMPEG::registerPlugin() FileManager::registerOutput(plugin); } -std::shared_ptr FFMPEG::createReader(std::string filename) +std::shared_ptr FFMPEG::createReader(std::string filename, int stream) { - return std::shared_ptr(new FFMPEGReader(filename)); + return std::shared_ptr(new FFMPEGReader(filename, stream)); } -std::shared_ptr FFMPEG::createReader(std::shared_ptr buffer) +std::shared_ptr FFMPEG::createReader(std::shared_ptr buffer, int stream) { - return std::shared_ptr(new FFMPEGReader(buffer)); + return std::shared_ptr(new FFMPEGReader(buffer, stream)); +} + +std::vector FFMPEG::queryStreams(std::string filename) +{ + return FFMPEGReader(filename).queryStreams(); +} + +std::vector FFMPEG::queryStreams(std::shared_ptr buffer) +{ + return FFMPEGReader(buffer).queryStreams(); } std::shared_ptr FFMPEG::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate) diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEG.h b/extern/audaspace/plugins/ffmpeg/FFMPEG.h index 108ba547e0f..fb40ba05573 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEG.h +++ b/extern/audaspace/plugins/ffmpeg/FFMPEG.h @@ -52,8 +52,10 @@ public: */ static void registerPlugin(); - virtual std::shared_ptr createReader(std::string filename); - virtual std::shared_ptr createReader(std::shared_ptr buffer); + virtual std::shared_ptr createReader(std::string filename, int stream = 0); + virtual std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0); + virtual std::vector queryStreams(std::string filename); + virtual std::vector queryStreams(std::shared_ptr buffer); virtual std::shared_ptr createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate); }; diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp index afdc7fcfcc6..de3ca099696 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp @@ -31,6 +31,25 @@ AUD_NAMESPACE_BEGIN #define FFMPEG_OLD_CODE #endif +SampleFormat FFMPEGReader::convertSampleFormat(AVSampleFormat format) +{ + switch(av_get_packed_sample_fmt(format)) + { + case AV_SAMPLE_FMT_U8: + return FORMAT_U8; + case AV_SAMPLE_FMT_S16: + return FORMAT_S16; + case AV_SAMPLE_FMT_S32: + return FORMAT_S32; + case AV_SAMPLE_FMT_FLT: + return FORMAT_FLOAT32; + case AV_SAMPLE_FMT_DBL: + return FORMAT_FLOAT64; + default: + AUD_THROW(FileException, "FFMPEG sample format unknown."); + } +} + int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) { int buf_size = buffer.getSize(); @@ -68,7 +87,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) for(int i = 0; i < m_frame->nb_samples; i++) { std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size, - m_frame->data[channel] + i * single_size, single_size); + m_frame->data[channel] + i * single_size, single_size); } } } @@ -109,7 +128,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) for(int i = 0; i < m_frame->nb_samples; i++) { std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size, - m_frame->data[channel] + i * single_size, single_size); + m_frame->data[channel] + i * single_size, single_size); } } } @@ -123,13 +142,10 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) return buf_pos; } -void FFMPEGReader::init() +void FFMPEGReader::init(int stream) { m_position = 0; - m_start_offset = 0.0f; m_pkgbuf_left = 0; - m_st_time = 0; - m_duration = 0; if(avformat_find_stream_info(m_formatCtx, nullptr) < 0) AUD_THROW(FileException, "File couldn't be read, ffmpeg couldn't find the stream info."); @@ -137,43 +153,22 @@ void FFMPEGReader::init() // find audio stream and codec m_stream = -1; - double dur_sec = 0; - for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++) { #ifdef FFMPEG_OLD_CODE - if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) + if((m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) #else - if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) + if((m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) #endif + && (m_stream < 0)) { - AVStream *audio_stream = m_formatCtx->streams[i]; - double audio_timebase = av_q2d(audio_stream->time_base); - - if (audio_stream->start_time != AV_NOPTS_VALUE) + if(stream == 0) { - m_st_time = audio_stream->start_time; - } - - int64_t ctx_start_time = 0; - if (m_formatCtx->start_time != AV_NOPTS_VALUE) { - ctx_start_time = m_formatCtx->start_time; - } - - m_start_offset = m_st_time * audio_timebase - (double)ctx_start_time / AV_TIME_BASE; - - if(audio_stream->duration != AV_NOPTS_VALUE) - { - dur_sec = audio_stream->duration * audio_timebase; + m_stream=i; + break; } else - { - /* If the audio starts after the stream start time, subract this from the total duration. */ - dur_sec = (double)m_formatCtx->duration / AV_TIME_BASE - m_start_offset; - } - - m_stream=i; - break; + stream--; } } @@ -242,10 +237,9 @@ void FFMPEGReader::init() } m_specs.rate = (SampleRate) m_codecCtx->sample_rate; - m_duration = lround(dur_sec * m_codecCtx->sample_rate); } -FFMPEGReader::FFMPEGReader(std::string filename) : +FFMPEGReader::FFMPEGReader(std::string filename, int stream) : m_pkgbuf(), m_formatCtx(nullptr), m_codecCtx(nullptr), @@ -259,7 +253,7 @@ FFMPEGReader::FFMPEGReader(std::string filename) : try { - init(); + init(stream); } catch(Exception&) { @@ -268,7 +262,7 @@ FFMPEGReader::FFMPEGReader(std::string filename) : } } -FFMPEGReader::FFMPEGReader(std::shared_ptr buffer) : +FFMPEGReader::FFMPEGReader(std::shared_ptr buffer, int stream) : m_pkgbuf(), m_codecCtx(nullptr), m_frame(nullptr), @@ -295,7 +289,7 @@ FFMPEGReader::FFMPEGReader(std::shared_ptr buffer) : try { - init(); + init(stream); } catch(Exception&) { @@ -318,6 +312,51 @@ FFMPEGReader::~FFMPEGReader() avformat_close_input(&m_formatCtx); } +std::vector FFMPEGReader::queryStreams() +{ + std::vector result; + + for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++) + { +#ifdef FFMPEG_OLD_CODE + if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) +#else + if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) +#endif + { + StreamInfo info; + + double time_base = av_q2d(m_formatCtx->streams[i]->time_base); + + if(m_formatCtx->streams[i]->start_time != AV_NOPTS_VALUE) + info.start = m_formatCtx->streams[i]->start_time * time_base; + else + info.start = 0; + + if(m_formatCtx->streams[i]->duration != AV_NOPTS_VALUE) + info.duration = m_formatCtx->streams[i]->duration * time_base; + else if(m_formatCtx->duration != AV_NOPTS_VALUE) + info.duration = double(m_formatCtx->duration) / AV_TIME_BASE - info.start; + else + info.duration = 0; + +#ifdef FFMPEG_OLD_CODE + info.specs.channels = Channels(m_formatCtx->streams[i]->codec->channels); + info.specs.rate = m_formatCtx->streams[i]->codec->sample_rate; + info.specs.format = convertSampleFormat(m_formatCtx->streams[i]->codec->sample_fmt); +#else + info.specs.channels = Channels(m_formatCtx->streams[i]->codecpar->channels); + info.specs.rate = m_formatCtx->streams[i]->codecpar->sample_rate; + info.specs.format = convertSampleFormat(AVSampleFormat(m_formatCtx->streams[i]->codecpar->format)); +#endif + + result.emplace_back(info); + } + } + + return result; +} + int FFMPEGReader::read_packet(void* opaque, uint8_t* buf, int buf_size) { FFMPEGReader* reader = reinterpret_cast(opaque); @@ -368,18 +407,16 @@ void FFMPEGReader::seek(int position) { if(position >= 0) { - double pts_time_base = - av_q2d(m_formatCtx->streams[m_stream]->time_base); + double pts_time_base = av_q2d(m_formatCtx->streams[m_stream]->time_base); - uint64_t seek_pts = (((uint64_t)position) / ((uint64_t)m_specs.rate)) / pts_time_base; + uint64_t st_time = m_formatCtx->streams[m_stream]->start_time; + uint64_t seek_pos = (uint64_t)(position / (pts_time_base * m_specs.rate)); - if(m_st_time != AV_NOPTS_VALUE) { - seek_pts += m_st_time; - } + if(st_time != AV_NOPTS_VALUE) + seek_pos += st_time; // a value < 0 tells us that seeking failed - if(av_seek_frame(m_formatCtx, m_stream, seek_pts, - AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0) + if(av_seek_frame(m_formatCtx, m_stream, seek_pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0) { avcodec_flush_buffers(m_codecCtx); m_position = position; @@ -400,7 +437,7 @@ void FFMPEGReader::seek(int position) if(packet.pts != AV_NOPTS_VALUE) { // calculate real position, and read to frame! - m_position = (packet.pts - m_st_time) * pts_time_base * m_specs.rate; + m_position = (packet.pts - (st_time != AV_NOPTS_VALUE ? st_time : 0)) * pts_time_base * m_specs.rate; if(m_position < position) { @@ -430,8 +467,25 @@ void FFMPEGReader::seek(int position) int FFMPEGReader::getLength() const { + auto stream = m_formatCtx->streams[m_stream]; + + double time_base = av_q2d(stream->time_base); + double duration; + + if(stream->duration != AV_NOPTS_VALUE) + duration = stream->duration * time_base; + else if(m_formatCtx->duration != AV_NOPTS_VALUE) + { + duration = float(m_formatCtx->duration) / AV_TIME_BASE; + + if(stream->start_time != AV_NOPTS_VALUE) + duration -= stream->start_time * time_base; + } + else + duration = -1; + // return approximated remaning size - return m_duration - m_position; + return (int)(duration * m_codecCtx->sample_rate) - m_position; } int FFMPEGReader::getPosition() const @@ -439,11 +493,6 @@ int FFMPEGReader::getPosition() const return m_position; } -double FFMPEGReader::getStartOffset() const -{ - return m_start_offset; -} - Specs FFMPEGReader::getSpecs() const { return m_specs.specs; @@ -480,13 +529,11 @@ void FFMPEGReader::read(int& length, bool& eos, sample_t* buffer) // decode the package pkgbuf_pos = decode(packet, m_pkgbuf); - if (packet.pts >= m_st_time) { - // copy to output buffer - data_size = std::min(pkgbuf_pos, left * sample_size); - m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format)); - buf += data_size / AUD_FORMAT_SIZE(m_specs.format); - left -= data_size / sample_size; - } + // copy to output buffer + data_size = std::min(pkgbuf_pos, left * sample_size); + m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format)); + buf += data_size / AUD_FORMAT_SIZE(m_specs.format); + left -= data_size / sample_size; } av_packet_unref(&packet); } diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h index d613457c220..70f13911eca 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h @@ -29,9 +29,11 @@ #include "respec/ConverterFunctions.h" #include "IReader.h" #include "util/Buffer.h" +#include "file/FileInfo.h" #include #include +#include struct AVCodecContext; extern "C" { @@ -54,22 +56,6 @@ private: */ int m_position; - /** - * The start offset in seconds relative to the media container start time. - * IE how much the sound should be delayed to be kept in sync with the rest of the containter streams. - */ - double m_start_offset; - - /** - * The start time pts of the stream. All packets before this timestamp shouldn't be played back (only decoded). - */ - int64_t m_st_time; - - /** - * The duration of the audio stream in samples. - */ - int64_t m_duration; - /** * The specification of the audio data. */ @@ -135,6 +121,13 @@ private: */ bool m_tointerleave; + /** + * Converts an ffmpeg sample format to an audaspace one. + * \param format The AVSampleFormat sample format. + * \return The sample format as SampleFormat. + */ + AUD_LOCAL static SampleFormat convertSampleFormat(AVSampleFormat format); + /** * Decodes a packet into the given buffer. * \param packet The AVPacket to decode. @@ -145,8 +138,9 @@ private: /** * Initializes the object. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. */ - AUD_LOCAL void init(); + AUD_LOCAL void init(int stream); // delete copy constructor and operator= FFMPEGReader(const FFMPEGReader&) = delete; @@ -156,24 +150,33 @@ public: /** * Creates a new reader. * \param filename The path to the file to be read. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the file specified does not exist or * cannot be read with ffmpeg. */ - FFMPEGReader(std::string filename); + FFMPEGReader(std::string filename, int stream = 0); /** * Creates a new reader. * \param buffer The buffer to read from. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the buffer specified cannot be read * with ffmpeg. */ - FFMPEGReader(std::shared_ptr buffer); + FFMPEGReader(std::shared_ptr buffer, int stream = 0); /** * Destroys the reader and closes the file. */ virtual ~FFMPEGReader(); + /** + * Queries the streams of a sound file. + * \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 queryStreams(); + /** * Reads data to a memory buffer. * This function is used for avio only. @@ -198,7 +201,6 @@ public: virtual void seek(int position); virtual int getLength() const; virtual int getPosition() const; - virtual double getStartOffset() const; virtual Specs getSpecs() const; virtual void read(int& length, bool& eos, sample_t* buffer); }; diff --git a/extern/audaspace/plugins/libsndfile/SndFile.cpp b/extern/audaspace/plugins/libsndfile/SndFile.cpp index ba4ff24ad68..39335de9a1a 100644 --- a/extern/audaspace/plugins/libsndfile/SndFile.cpp +++ b/extern/audaspace/plugins/libsndfile/SndFile.cpp @@ -32,16 +32,26 @@ void SndFile::registerPlugin() FileManager::registerOutput(plugin); } -std::shared_ptr SndFile::createReader(std::string filename) +std::shared_ptr SndFile::createReader(std::string filename, int stream) { return std::shared_ptr(new SndFileReader(filename)); } -std::shared_ptr SndFile::createReader(std::shared_ptr buffer) +std::shared_ptr SndFile::createReader(std::shared_ptr buffer, int stream) { return std::shared_ptr(new SndFileReader(buffer)); } +std::vector SndFile::queryStreams(std::string filename) +{ + return SndFileReader(filename).queryStreams(); +} + +std::vector SndFile::queryStreams(std::shared_ptr buffer) +{ + return SndFileReader(buffer).queryStreams(); +} + std::shared_ptr SndFile::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate) { return std::shared_ptr(new SndFileWriter(filename, specs, format, codec, bitrate)); diff --git a/extern/audaspace/plugins/libsndfile/SndFile.h b/extern/audaspace/plugins/libsndfile/SndFile.h index 61afed1d564..10a7391180f 100644 --- a/extern/audaspace/plugins/libsndfile/SndFile.h +++ b/extern/audaspace/plugins/libsndfile/SndFile.h @@ -52,8 +52,10 @@ public: */ static void registerPlugin(); - virtual std::shared_ptr createReader(std::string filename); - virtual std::shared_ptr createReader(std::shared_ptr buffer); + virtual std::shared_ptr createReader(std::string filename, int stream = 0); + virtual std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0); + virtual std::vector queryStreams(std::string filename); + virtual std::vector queryStreams(std::shared_ptr buffer); virtual std::shared_ptr createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate); }; diff --git a/extern/audaspace/plugins/libsndfile/SndFileReader.cpp b/extern/audaspace/plugins/libsndfile/SndFileReader.cpp index d2d89814c07..21c733d8117 100644 --- a/extern/audaspace/plugins/libsndfile/SndFileReader.cpp +++ b/extern/audaspace/plugins/libsndfile/SndFileReader.cpp @@ -118,6 +118,21 @@ SndFileReader::~SndFileReader() sf_close(m_sndfile); } +std::vector SndFileReader::queryStreams() +{ + std::vector result; + + StreamInfo info; + info.start = 0; + info.duration = double(getLength()) / m_specs.rate; + info.specs.specs = m_specs; + info.specs.format = FORMAT_FLOAT32; + + result.emplace_back(info); + + return result; +} + bool SndFileReader::isSeekable() const { return m_seekable; diff --git a/extern/audaspace/plugins/libsndfile/SndFileReader.h b/extern/audaspace/plugins/libsndfile/SndFileReader.h index 081c29c686c..b4158d9091a 100644 --- a/extern/audaspace/plugins/libsndfile/SndFileReader.h +++ b/extern/audaspace/plugins/libsndfile/SndFileReader.h @@ -28,9 +28,12 @@ * The SndFileReader class. */ +#include "file/FileInfo.h" + #include #include #include +#include AUD_NAMESPACE_BEGIN @@ -96,6 +99,7 @@ public: /** * Creates a new reader. * \param filename The path to the file to be read. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the file specified does not exist or * cannot be read with libsndfile. */ @@ -104,6 +108,7 @@ public: /** * Creates a new reader. * \param buffer The buffer to read from. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the buffer specified cannot be read * with libsndfile. */ @@ -114,6 +119,13 @@ public: */ virtual ~SndFileReader(); + /** + * Queries the streams of a sound file. + * \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 queryStreams(); + virtual bool isSeekable() const; virtual void seek(int position); virtual int getLength() const; diff --git a/extern/audaspace/src/file/File.cpp b/extern/audaspace/src/file/File.cpp index 0cdecb03657..5d4bae482d6 100644 --- a/extern/audaspace/src/file/File.cpp +++ b/extern/audaspace/src/file/File.cpp @@ -23,23 +23,31 @@ AUD_NAMESPACE_BEGIN -File::File(std::string filename) : - m_filename(filename) +File::File(std::string filename, int stream) : + m_filename(filename), m_stream(stream) { } -File::File(const data_t* buffer, int size) : - m_buffer(new Buffer(size)) +File::File(const data_t* buffer, int size, int stream) : + m_buffer(new Buffer(size)), m_stream(stream) { std::memcpy(m_buffer->getBuffer(), buffer, size); } +std::vector File::queryStreams() +{ + if(m_buffer.get()) + return FileManager::queryStreams(m_buffer); + else + return FileManager::queryStreams(m_filename); +} + std::shared_ptr File::createReader() { if(m_buffer.get()) - return FileManager::createReader(m_buffer); + return FileManager::createReader(m_buffer, m_stream); else - return FileManager::createReader(m_filename); + return FileManager::createReader(m_filename, m_stream); } AUD_NAMESPACE_END diff --git a/extern/audaspace/src/file/FileManager.cpp b/extern/audaspace/src/file/FileManager.cpp index f8ef8deb409..7cbc0318f8c 100644 --- a/extern/audaspace/src/file/FileManager.cpp +++ b/extern/audaspace/src/file/FileManager.cpp @@ -43,13 +43,13 @@ void FileManager::registerOutput(std::shared_ptr output) outputs().push_back(output); } -std::shared_ptr FileManager::createReader(std::string filename) +std::shared_ptr FileManager::createReader(std::string filename, int stream) { for(std::shared_ptr input : inputs()) { try { - return input->createReader(filename); + return input->createReader(filename, stream); } catch(Exception&) {} } @@ -57,13 +57,41 @@ std::shared_ptr FileManager::createReader(std::string filename) AUD_THROW(FileException, "The file couldn't be read with any installed file reader."); } -std::shared_ptr FileManager::createReader(std::shared_ptr buffer) +std::shared_ptr FileManager::createReader(std::shared_ptr buffer, int stream) { for(std::shared_ptr input : inputs()) { try { - return input->createReader(buffer); + return input->createReader(buffer, stream); + } + catch(Exception&) {} + } + + AUD_THROW(FileException, "The file couldn't be read with any installed file reader."); +} + +std::vector FileManager::queryStreams(std::string filename) +{ + for(std::shared_ptr input : inputs()) + { + try + { + return input->queryStreams(filename); + } + catch(Exception&) {} + } + + AUD_THROW(FileException, "The file couldn't be read with any installed file reader."); +} + +std::vector FileManager::queryStreams(std::shared_ptr buffer) +{ + for(std::shared_ptr input : inputs()) + { + try + { + return input->queryStreams(buffer); } catch(Exception&) {} } diff --git a/extern/audaspace/src/fx/VolumeReader.cpp b/extern/audaspace/src/fx/VolumeReader.cpp index 627acbac9ef..ac1d4882a87 100644 --- a/extern/audaspace/src/fx/VolumeReader.cpp +++ b/extern/audaspace/src/fx/VolumeReader.cpp @@ -57,4 +57,4 @@ void VolumeReader::read(int& length, bool& eos, sample_t* buffer) buffer[i] = buffer[i] * m_volumeStorage->getVolume(); } -AUD_NAMESPACE_END +AUD_NAMESPACE_END \ No newline at end of file diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index fa58813c5f8..8796e2c18f3 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -96,13 +96,24 @@ typedef struct SoundInfo { eSoundChannels channels; } specs; float length; - double start_offset; } SoundInfo; +typedef struct SoundStreamInfo { + double duration; + double start; +} SoundStreamInfo; + /* Get information about given sound. Returns truth on success., false if sound can not be loaded * or if the codes is not supported. */ bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *sound_info); +/* Get information about given sound. Returns truth on success., false if sound can not be loaded + * or if the codes is not supported. */ +bool BKE_sound_stream_info_get(struct Main *main, + const char *filepath, + int stream, + SoundStreamInfo *sound_info); + #if defined(WITH_AUDASPACE) AUD_Device *BKE_sound_mixdown(const struct Scene *scene, AUD_DeviceSpecs specs, diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index c61fa793367..ccb10f080e3 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -1213,7 +1213,6 @@ static bool sound_info_from_playback_handle(void *playback_handle, SoundInfo *so AUD_SoundInfo info = AUD_getInfo(playback_handle); sound_info->specs.channels = (eSoundChannels)info.specs.channels; sound_info->length = info.length; - sound_info->start_offset = info.start_offset; return true; } @@ -1231,6 +1230,44 @@ bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *soun return result; } +bool BKE_sound_stream_info_get(struct Main *main, const char *filepath, int stream, SoundStreamInfo *sound_info) +{ + const char *path; + char str[FILE_MAX]; + AUD_Sound *sound; + AUD_StreamInfo *stream_infos; + int stream_count; + + BLI_strncpy(str, filepath, sizeof(str)); + path = BKE_main_blendfile_path(main); + BLI_path_abs(str, path); + + sound = AUD_Sound_file(str); + if (!sound) { + return false; + } + + stream_count = AUD_Sound_getFileStreams(sound, &stream_infos); + + AUD_Sound_free(sound); + + if (!stream_infos) { + return false; + } + + if ((stream < 0) || (stream >= stream_count)) { + free(stream_infos); + return false; + } + + sound_info->start = stream_infos[stream].start; + sound_info->duration = stream_infos[stream].duration; + + free(stream_infos); + + return true; +} + #else /* WITH_AUDASPACE */ # include "BLI_utildefines.h" @@ -1400,6 +1437,14 @@ bool BKE_sound_info_get(struct Main *UNUSED(main), return false; } +bool BKE_sound_stream_info_get(struct Main *UNUSED(main), + const char *UNUSED(filepath), + int UNUSED(stream), + SoundStreamInfo *UNUSED(sound_info)) +{ + return false; +} + #endif /* WITH_AUDASPACE */ void BKE_sound_reset_scene_runtime(Scene *scene) diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 2955c4ef7ae..872b17372de 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -1098,7 +1098,8 @@ static int graphkeys_sound_bake_exec(bContext *C, wmOperator *op) RNA_boolean_get(op->ptr, "use_square"), RNA_float_get(op->ptr, "sthreshold"), FPS, - &sbi.length); + &sbi.length, + 0); if (sbi.samples == NULL) { BKE_report(op->reports, RPT_ERROR, "Unsupported audio format"); diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 081f0241e94..bdfa639b327 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -47,6 +47,7 @@ #include "BKE_mask.h" #include "BKE_movieclip.h" #include "BKE_report.h" +#include "BKE_sound.h" #include "IMB_imbuf.h" @@ -643,7 +644,15 @@ static void sequencer_add_movie_multiple_strips(bContext *C, BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; - double video_start_offset; + double video_start_offset = -1; + double audio_start_offset = 0; + + if (RNA_boolean_get(op->ptr, "sound")) { + SoundStreamInfo sound_info; + if (BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_info)) { + audio_start_offset = video_start_offset = sound_info.start; + } + } load_data->channel++; seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); @@ -653,9 +662,30 @@ static void sequencer_add_movie_multiple_strips(bContext *C, } else { if (RNA_boolean_get(op->ptr, "sound")) { - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset); + int minimum_frame_offset = MIN2(video_start_offset, audio_start_offset) * FPS; + + int video_frame_offset = video_start_offset * FPS; + int audio_frame_offset = audio_start_offset * FPS; + + double video_frame_remainder = video_start_offset * FPS - video_frame_offset; + double audio_frame_remainder = audio_start_offset * FPS - audio_frame_offset; + + double audio_skip = (video_frame_remainder - audio_frame_remainder) / FPS; + + video_frame_offset -= minimum_frame_offset; + audio_frame_offset -= minimum_frame_offset; + + load_data->start_frame += audio_frame_offset; + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip); + + int min_startdisp = MIN2(seq_movie->startdisp, seq_sound->startdisp); + int max_enddisp = MAX2(seq_movie->enddisp, seq_sound->enddisp); + + load_data->start_frame += max_enddisp - min_startdisp - audio_frame_offset; + } + else { + load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; } - load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); seq_build_proxy(C, seq_movie); @@ -672,7 +702,15 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; - double video_start_offset; + double video_start_offset = -1; + double audio_start_offset = 0; + + if (RNA_boolean_get(op->ptr, "sound")) { + SoundStreamInfo sound_info; + if (BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_info)) { + audio_start_offset = video_start_offset = sound_info.start; + } + } load_data->channel++; seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); @@ -683,7 +721,21 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa return false; } if (RNA_boolean_get(op->ptr, "sound")) { - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset); + int minimum_frame_offset = MIN2(video_start_offset, audio_start_offset) * FPS; + + int video_frame_offset = video_start_offset * FPS; + int audio_frame_offset = audio_start_offset * FPS; + + double video_frame_remainder = video_start_offset * FPS - video_frame_offset; + double audio_frame_remainder = audio_start_offset * FPS - audio_frame_offset; + + double audio_skip = (video_frame_remainder - audio_frame_remainder) / FPS; + + video_frame_offset -= minimum_frame_offset; + audio_frame_offset -= minimum_frame_offset; + + load_data->start_frame += audio_frame_offset; + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip); } seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index b56ad48cec2..bf817005a08 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -420,6 +420,10 @@ static void draw_seq_waveform_overlay(View2D *v2d, float sample_offset = start_sample + i * samples_per_pix; int p = sample_offset; + if (p < 0) { + continue; + } + if (p >= waveform->length) { break; } diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index dbca16ca82b..13f9356751e 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -664,11 +664,6 @@ static int startffmpeg(struct anim *anim) anim->duration_in_frames = (int)(stream_dur * av_q2d(frame_rate) + 0.5f); } - double ctx_start = 0; - if (pFormatCtx->start_time != AV_NOPTS_VALUE) { - ctx_start = (double)pFormatCtx->start_time / AV_TIME_BASE; - } - frs_num = frame_rate.num; frs_den = frame_rate.den; @@ -683,7 +678,7 @@ static int startffmpeg(struct anim *anim) anim->frs_sec_base = frs_den; /* Save the relative start time for the video. IE the start time in relation to where playback * starts. */ - anim->start_offset = video_start - ctx_start; + anim->start_offset = video_start; anim->params = 0; diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index a0564d3435b..b43b57a35be 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -323,8 +323,8 @@ static Sequence *rna_Sequences_new_movie(ID *id, SEQ_add_load_data_init(&load_data, name, file, frame_start, channel); load_data.fit_method = fit_method; load_data.allow_invalid_file = true; - double video_start_offset; - Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &video_start_offset); + double start_offset = -1; + Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &start_offset); DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); diff --git a/source/blender/sequencer/SEQ_add.h b/source/blender/sequencer/SEQ_add.h index 4025f1a4a04..d2a731d9953 100644 --- a/source/blender/sequencer/SEQ_add.h +++ b/source/blender/sequencer/SEQ_add.h @@ -88,7 +88,7 @@ struct Sequence *SEQ_add_movie_strip(struct Main *bmain, struct Scene *scene, struct ListBase *seqbase, struct SeqLoadData *load_data, - double *r_video_start_offset); + double *r_start_offset); struct Sequence *SEQ_add_scene_strip(struct Scene *scene, struct ListBase *seqbase, struct SeqLoadData *load_data); diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 9081c655d2f..3cf7a4ebf4d 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -403,26 +403,8 @@ Sequence *SEQ_add_sound_strip(Main *bmain, return NULL; } - /* If this sound it part of a video, then the sound might start after the video. - * In this case we need to then offset the start frame of the audio so it syncs up - * properly with the video. - */ - int start_frame_offset = info.start_offset * FPS; - double start_frame_offset_remainer = (info.start_offset * FPS - start_frame_offset) / FPS; - - if (start_frame_offset_remainer > FLT_EPSILON) { - /* We can't represent a fraction of a frame, so skip the first frame fraction of sound so we - * start on a "whole" frame. - */ - start_frame_offset++; - } - - sound->offset_time += start_frame_offset_remainer; - - Sequence *seq = SEQ_sequence_alloc(seqbase, - load_data->start_frame + start_frame_offset, - load_data->channel, - SEQ_TYPE_SOUND_RAM); + Sequence *seq = SEQ_sequence_alloc( + seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_SOUND_RAM); seq->sound = sound; seq->scene_sound = NULL; @@ -508,7 +490,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data, - double *r_video_start_offset) + double *r_start_offset) { char path[sizeof(load_data->path)]; BLI_strncpy(path, load_data->path, sizeof(path)); @@ -554,8 +536,40 @@ Sequence *SEQ_add_movie_strip(Main *bmain, return NULL; } + int video_frame_offset = 0; + float video_fps = 0.0f; + + if (anim_arr[0] != NULL) { + short fps_denom; + float fps_num; + + IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true); + + video_fps = fps_denom / fps_num; + + /* Adjust scene's frame rate settings to match. */ + if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) { + scene->r.frs_sec = fps_denom; + scene->r.frs_sec_base = fps_num; + } + + double video_start_offset = IMD_anim_get_offset(anim_arr[0]); + int minimum_frame_offset; + + if (*r_start_offset >= 0) { + minimum_frame_offset = MIN2(video_start_offset, *r_start_offset) * FPS; + } + else { + minimum_frame_offset = video_start_offset * FPS; + } + + video_frame_offset = video_start_offset * FPS - minimum_frame_offset; + + *r_start_offset = video_start_offset; + } + Sequence *seq = SEQ_sequence_alloc( - seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MOVIE); + seqbase, load_data->start_frame + video_frame_offset, load_data->channel, SEQ_TYPE_MOVIE); /* Multiview settings. */ if (load_data->use_multiview) { @@ -579,27 +593,11 @@ Sequence *SEQ_add_movie_strip(Main *bmain, seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ - float video_fps = 0.0f; - if (anim_arr[0] != NULL) { seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); - *r_video_start_offset = IMD_anim_get_offset(anim_arr[0]); IMB_anim_load_metadata(anim_arr[0]); - short fps_denom; - float fps_num; - - IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true); - - video_fps = fps_denom / fps_num; - - /* Adjust scene's frame rate settings to match. */ - if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) { - scene->r.frs_sec = fps_denom; - scene->r.frs_sec_base = fps_num; - } - /* Set initial scale based on load_data->fit_method. */ orig_width = IMB_anim_get_image_width(anim_arr[0]); orig_height = IMB_anim_get_image_height(anim_arr[0]); From 25e548c96b3d8c1698fd4385b4dc395665b5a7f6 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Sun, 19 Sep 2021 13:01:24 +0800 Subject: [PATCH 0006/1500] GPencil: Curvature support for length modifier. --- source/blender/blenkernel/BKE_gpencil_geom.h | 7 +- .../blender/blenkernel/intern/gpencil_geom.cc | 262 +++++++++++++++--- .../intern/MOD_gpencillength.c | 134 +++++++-- .../makesdna/DNA_gpencil_modifier_defaults.h | 6 +- .../makesdna/DNA_gpencil_modifier_types.h | 7 +- .../makesrna/intern/rna_gpencil_modifier.c | 66 ++++- 6 files changed, 410 insertions(+), 72 deletions(-) diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index d472fd6f02b..20ee9199dba 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -114,7 +114,12 @@ void BKE_gpencil_dissolve_points(struct bGPdata *gpd, bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float overshoot_fac, - const short mode); + const short mode, + const bool follow_curvature, + const int extra_point_count, + const float edge_point_tradeoff, + const float max_angle, + const bool invert_curvature); bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps, const int index_from, const int index_to); diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 8ff026231f5..2d825705ab0 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -540,65 +540,242 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, return true; } +/** + * Give extra stroke points before and after the original tip points. + * \param gps: Target stroke + * \param count_before: how many extra points to be added before a stroke + * \param count_after: how many extra points to be added after a stroke + */ +static bool BKE_gpencil_stroke_extra_points(bGPDstroke *gps, + const int count_before, + const int count_after) +{ + bGPDspoint *pts = gps->points; + + BLI_assert(count_before >= 0); + BLI_assert(count_after >= 0); + if (!count_before && !count_after) { + return false; + } + + const int new_count = count_before + count_after + gps->totpoints; + + bGPDspoint *new_pts = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, __func__); + + for (int i = 0; i < count_before; i++) { + memcpy(&new_pts[i], &pts[0], sizeof(bGPDspoint)); + } + memcpy(&new_pts[count_before], pts, sizeof(bGPDspoint) * gps->totpoints); + for (int i = new_count - count_after; i < new_count; i++) { + memcpy(&new_pts[i], &pts[gps->totpoints - 1], sizeof(bGPDspoint)); + } + + if (gps->dvert) { + MDeformVert *new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count, __func__); + + for (int i = 0; i < new_count; i++) { + MDeformVert *dv = &gps->dvert[CLAMPIS(i - count_before, 0, gps->totpoints - 1)]; + int inew = i; + new_dv[inew].flag = dv->flag; + new_dv[inew].totweight = dv->totweight; + new_dv[inew].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, + __func__); + memcpy(new_dv[inew].dw, dv->dw, sizeof(MDeformWeight) * dv->totweight); + } + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + gps->dvert = new_dv; + } + + MEM_freeN(gps->points); + gps->points = new_pts; + gps->totpoints = new_count; + + return true; +} + /** * Backbone stretch similar to Freestyle. * \param gps: Stroke to sample. - * \param dist: Distance of one segment. - * \param overshoot_fac: How exact is the follow curve algorithm. + * \param dist: Length of the added section. + * \param overshoot_fac: Relative length of the curve which is used to determine the extension. * \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End) + * \param follow_curvature: True for appproximating curvature of given overshoot. + * \param extra_point_count: When follow_curvature is true, use this amount of extra points */ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float overshoot_fac, - const short mode) + const short mode, + const bool follow_curvature, + const int extra_point_count, + const float edge_point_tradeoff, + const float max_angle, + const bool invert_curvature) { #define BOTH 0 #define START 1 #define END 2 - bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt; - int i; - float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac); + const bool do_start = ELEM(mode, BOTH, START); + const bool do_end = ELEM(mode, BOTH, END); + float used_percent_length = overshoot_fac; + CLAMP(used_percent_length, 1e-4f, 1.0f); + if (!isfinite(used_percent_length)) { + /* If gps->totpoints == 2, then #overshoot_fac is allowed to be non finite. */ + used_percent_length = 0.1f; + } - if (gps->totpoints < 2 || dist < FLT_EPSILON) { + if (gps->totpoints <= 1 || dist < FLT_EPSILON || extra_point_count <= 0) { return false; } - last_pt = &pt[gps->totpoints - 1]; - second_last = &pt[gps->totpoints - 2]; - next_pt = &pt[1]; + /* NOTE: When it's just a straight line, we don't need to do the curvature stuff. */ + if (!follow_curvature || gps->totpoints <= 2) { + /* Not following curvature, just straight line. */ + /* NOTE: #overshoot_point_param can not be zero. */ + float overshoot_point_param = used_percent_length * (gps->totpoints - 1); + float result[3]; - if (mode == BOTH || mode == START) { - float len1 = 0.0f; - i = 1; - while (len1 < threshold && gps->totpoints > i) { - next_pt = &pt[i]; - len1 = len_v3v3(&next_pt->x, &pt->x); - i++; - } - float extend1 = (len1 + dist) / len1; - float result1[3]; - - interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1); - copy_v3_v3(&pt->x, result1); - } - - if (mode == BOTH || mode == END) { - float len2 = 0.0f; - i = 2; - while (len2 < threshold && gps->totpoints >= i) { - second_last = &pt[gps->totpoints - i]; - len2 = len_v3v3(&last_pt->x, &second_last->x); - i++; + if (do_start) { + int index1 = floor(overshoot_point_param); + int index2 = ceil(overshoot_point_param); + interp_v3_v3v3(result, + &gps->points[index1].x, + &gps->points[index2].x, + fmodf(overshoot_point_param, 1.0f)); + sub_v3_v3(result, &gps->points[0].x); + if (UNLIKELY(is_zero_v3(result))) { + sub_v3_v3v3(result, &gps->points[1].x, &gps->points[0].x); + } + madd_v3_v3fl(&gps->points[0].x, result, -dist / len_v3(result)); } - float extend2 = (len2 + dist) / len2; - float result2[3]; - interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2); - - copy_v3_v3(&last_pt->x, result2); + if (do_end) { + int index1 = gps->totpoints - 1 - floor(overshoot_point_param); + int index2 = gps->totpoints - 1 - ceil(overshoot_point_param); + interp_v3_v3v3(result, + &gps->points[index1].x, + &gps->points[index2].x, + fmodf(overshoot_point_param, 1.0f)); + sub_v3_v3(result, &gps->points[gps->totpoints - 1].x); + if (UNLIKELY(is_zero_v3(result))) { + sub_v3_v3v3( + result, &gps->points[gps->totpoints - 2].x, &gps->points[gps->totpoints - 1].x); + } + madd_v3_v3fl(&gps->points[gps->totpoints - 1].x, result, -dist / len_v3(result)); + } + return true; } + /* Curvature calculation. */ + + /* First allocate the new stroke size. */ + const int first_old_index = do_start ? extra_point_count : 0; + const int last_old_index = gps->totpoints - 1 + first_old_index; + const int orig_totpoints = gps->totpoints; + BKE_gpencil_stroke_extra_points(gps, first_old_index, do_end ? extra_point_count : 0); + + /* The overshoot length in terms of angles with at least 1 used angle. */ + const float overshoot_parameter = used_percent_length * (orig_totpoints - 2); + int overshoot_pointcount = ceil(overshoot_parameter); + CLAMP(overshoot_pointcount, 1, orig_totpoints - 2); + + float point_edge_tradeoff = 1.0f - edge_point_tradeoff; + + /* Do for both sides without code duplication. */ + float no[3], vec1[3], vec2[3], total_angle[3]; + for (int k = 0; k < 2; k++) { + if ((k == 0 && !do_start) || (k == 1 && !do_end)) { + continue; + } + + const int start_i = k == 0 ? first_old_index : + last_old_index; // first_old_index, last_old_index + const int dir_i = 1 - k * 2; // 1, -1 + + sub_v3_v3v3(vec1, &gps->points[start_i + dir_i].x, &gps->points[start_i].x); + zero_v3(total_angle); + float segment_length = normalize_v3(vec1); + float overshoot_length = 0.0f; + + /* Accumulate rotation angle and length. */ + int j = 0; + for (int i = start_i; j < overshoot_pointcount; i += dir_i, j++) { + /* Don't fully add last segment to get continuity in overshoot_fac. */ + float fac = fmin(overshoot_parameter - j, 1.0f); + + /* Read segments. */ + copy_v3_v3(vec2, vec1); + sub_v3_v3v3(vec1, &gps->points[i + dir_i * 2].x, &gps->points[i + dir_i].x); + const float len = normalize_v3(vec1); + float angle = angle_normalized_v3v3(vec1, vec2) * fac; + + /* Add half of both adjacent legs of the current angle. */ + const float added_len = (segment_length + len) * 0.5f * fac; + overshoot_length += added_len; + segment_length = len; + + if (angle > max_angle) { + continue; + } + if (angle > M_PI * 0.995f) { + continue; + } + + angle *= powf(added_len, point_edge_tradeoff); + + cross_v3_v3v3(no, vec1, vec2); + normalize_v3_length(no, angle); + add_v3_v3(total_angle, no); + } + + if (UNLIKELY(overshoot_length == 0.0f)) { + /* Don't do a proper extension if the used points are all in the same position. */ + continue; + } + + sub_v3_v3v3(vec1, &gps->points[start_i].x, &gps->points[start_i + dir_i].x); + /* In general curvature = 1/radius. For the curvature without the + * weights introduced point_edge_tradeoff the calculation is + * curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length */ + float curvature = normalize_v3(total_angle) / overshoot_length; + /* Compensate for the weights powf(added_len, point_edge_tradeoff). */ + curvature /= powf(overshoot_length / fminf(overshoot_parameter, (float)j), + point_edge_tradeoff); + if (invert_curvature) { + curvature = -curvature; + } + const float angle_step = curvature * dist / extra_point_count; + float step_length = dist / extra_point_count; + if (fabsf(angle_step) > FLT_EPSILON) { + /* Make a direct step length from the assigned arc step length. */ + step_length *= sin(angle_step * 0.5f) / (angle_step * 0.5f); + } + else { + zero_v3(total_angle); + } + const float prev_length = normalize_v3_length(vec1, step_length); + + /* Build rotation matrix here to get best performance. */ + float rot[3][3]; + float q[4]; + axis_angle_to_quat(q, total_angle, angle_step); + quat_to_mat3(rot, q); + + /* Rotate the starting direction to account for change in edge lengths. */ + axis_angle_to_quat(q, + total_angle, + fmaxf(0.0f, 1.0f - fabs(point_edge_tradeoff)) * + (curvature * prev_length - angle_step) / 2.0f); + mul_qt_v3(q, vec1); + + /* Now iteratively accumulate the segments with a rotating added direction. */ + for (int i = start_i - dir_i, j = 0; j < extra_point_count; i -= dir_i, j++) { + mul_v3_m3v3(vec1, rot, vec1); + add_v3_v3v3(&gps->points[i].x, vec1, &gps->points[i + dir_i].x); + } + } return true; } @@ -749,6 +926,7 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo second_last = &pt[gps->totpoints - 2]; + float len; float len1, cut_len1; float len2, cut_len2; len1 = len2 = cut_len1 = cut_len2 = 0.0f; @@ -759,11 +937,13 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo i = 0; index_end = gps->totpoints - 1; while (len1 < dist && gps->totpoints > i + 1) { - len1 += len_v3v3(&pt[i].x, &pt[i + 1].x); + len = len_v3v3(&pt[i].x, &pt[i + 1].x); + len1 += len; cut_len1 = len1 - dist; i++; } index_start = i - 1; + interp_v3_v3v3(&pt[index_start].x, &pt[index_start + 1].x, &pt[index_start].x, cut_len1 / len); } if (mode == END) { @@ -771,18 +951,20 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo i = 2; while (len2 < dist && gps->totpoints >= i) { second_last = &pt[gps->totpoints - i]; - len2 += len_v3v3(&second_last[1].x, &second_last->x); + len = len_v3v3(&second_last[1].x, &second_last->x); + len2 += len; cut_len2 = len2 - dist; i++; } index_end = gps->totpoints - i + 2; + interp_v3_v3v3(&pt[index_end].x, &pt[index_end - 1].x, &pt[index_end].x, cut_len2 / len); } if (index_end <= index_start) { index_start = index_end = 0; /* empty stroke */ } - if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) { + if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < 0)) { index_start = index_end = 0; /* no length left to cut */ } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index 6aa0e6c152e..b104d4685e0 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -72,9 +72,14 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target) } static bool gpencil_modify_stroke(bGPDstroke *gps, - float length, + const float length, const float overshoot_fac, - const short len_mode) + const short len_mode, + const bool use_curvature, + const int extra_point_count, + const float edge_point_tradeoff, + const float max_angle, + const bool invert_curvature) { bool changed = false; if (length == 0.0f) { @@ -82,10 +87,18 @@ static bool gpencil_modify_stroke(bGPDstroke *gps, } if (length > 0.0f) { - BKE_gpencil_stroke_stretch(gps, length, overshoot_fac, len_mode); + changed = BKE_gpencil_stroke_stretch(gps, + length, + overshoot_fac, + len_mode, + use_curvature, + extra_point_count, + edge_point_tradeoff, + max_angle, + invert_curvature); } else { - changed |= BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode); + changed = BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode); } return changed; @@ -96,12 +109,51 @@ static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke bool changed = false; const float len = (lmd->mode == GP_LENGTH_ABSOLUTE) ? 1.0f : BKE_gpencil_stroke_length(gps, true); + const int totpoints = gps->totpoints; if (len < FLT_EPSILON) { return; } - changed |= gpencil_modify_stroke(gps, len * lmd->start_fac, lmd->overshoot_fac, 1); - changed |= gpencil_modify_stroke(gps, len * lmd->end_fac, lmd->overshoot_fac, 2); + /* Always do the stretching first since it might depend on points which could be deleted by the + * shrink. */ + float first_fac = lmd->start_fac; + int first_mode = 1; + float second_fac = lmd->end_fac; + int second_mode = 2; + if (first_fac < 0) { + SWAP(float, first_fac, second_fac); + SWAP(int, first_mode, second_mode); + } + + const int first_extra_point_count = ceil(first_fac * lmd->point_density); + const int second_extra_point_count = ceil(second_fac * lmd->point_density); + + changed |= gpencil_modify_stroke(gps, + len * first_fac, + lmd->overshoot_fac, + first_mode, + lmd->flag & GP_LENGTH_USE_CURVATURE, + first_extra_point_count, + lmd->edge_point_tradeoff, + lmd->max_angle, + lmd->flag & GP_LENGTH_INVERT_CURVATURE); + /* HACK: The second #overshoot_fac needs to be adjusted because it is not + * done in the same stretch call, because it can have a different length. + * The adjustment needs to be stable when + * ceil(overshoot_fac*(gps->totpoints - 2)) is used in stretch and never + * produce a result highter than totpoints - 2. */ + const float second_overshoot_fac = lmd->overshoot_fac * (totpoints - 2) / + ((float)gps->totpoints - 2) * + (1.0f - 0.1f / (totpoints - 1.0f)); + changed |= gpencil_modify_stroke(gps, + len * second_fac, + second_overshoot_fac, + second_mode, + lmd->flag & GP_LENGTH_USE_CURVATURE, + second_extra_point_count, + lmd->edge_point_tradeoff, + lmd->max_angle, + lmd->flag & GP_LENGTH_INVERT_CURVATURE); if (changed) { BKE_gpencil_stroke_geometry_update(gpd, gps); @@ -117,20 +169,25 @@ static void deformStroke(GpencilModifierData *md, { bGPdata *gpd = ob->data; LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md; - if (is_stroke_affected_by_modifier(ob, - lmd->layername, - lmd->material, - lmd->pass_index, - lmd->layer_pass, - 1, - gpl, - gps, - lmd->flag & GP_LENGTH_INVERT_LAYER, - lmd->flag & GP_LENGTH_INVERT_PASS, - lmd->flag & GP_LENGTH_INVERT_LAYERPASS, - lmd->flag & GP_LENGTH_INVERT_MATERIAL)) { - applyLength(lmd, gpd, gps); + if (!is_stroke_affected_by_modifier(ob, + lmd->layername, + lmd->material, + lmd->pass_index, + lmd->layer_pass, + 1, + gpl, + gps, + lmd->flag & GP_LENGTH_INVERT_LAYER, + lmd->flag & GP_LENGTH_INVERT_PASS, + lmd->flag & GP_LENGTH_INVERT_LAYERPASS, + lmd->flag & GP_LENGTH_INVERT_MATERIAL)) { + return; } + if ((gps->flag & GP_STROKE_CYCLIC) != 0) { + /* Don't affect cyclic strokes as they have no start/end. */ + return; + } + applyLength(lmd, gpd, gps); } static void bakeModifier(Main *UNUSED(bmain), @@ -168,8 +225,14 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE); - uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE); + if (RNA_enum_get(ptr, "mode") == GP_LENGTH_RELATIVE) { + uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE); + uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE); + } + else { + uiItemR(col, ptr, "start_length", 0, IFACE_("Start"), ICON_NONE); + uiItemR(col, ptr, "end_length", 0, IFACE_("End"), ICON_NONE); + } uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Overshoot"), ICON_NONE); @@ -181,10 +244,39 @@ static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) gpencil_modifier_masking_panel_draw(panel, true, false); } +static void curvature_header_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiItemR(layout, ptr, "use_curvature", 0, IFACE_("Curvature"), ICON_NONE); +} + +static void curvature_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayoutSetPropSep(layout, true); + + uiLayout *col = uiLayoutColumn(layout, false); + + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_curvature")); + + uiItemR(col, ptr, "point_density", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "edge_point_tradeoff", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "max_angle", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "invert_curvature", 0, IFACE_("Invert"), ICON_NONE); +} + static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Length, panel_draw); + gpencil_modifier_subpanel_register( + region_type, "curvature", "", curvature_header_draw, curvature_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); } diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 450527c7443..9d7dc50bcbf 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -314,9 +314,13 @@ { \ .start_fac = 0.1f,\ .end_fac = 0.1f,\ - .overshoot_fac = 0.01f,\ + .overshoot_fac = 0.1f,\ .pass_index = 0,\ .material = NULL,\ + .flag = GP_LENGTH_USE_CURVATURE,\ + .point_density = 30.0f,\ + .edge_point_tradeoff = 1.0f,\ + .max_angle = DEG2RAD(170.0f),\ } #define _DNA_DEFAULT_DashGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index d3429329ef6..cd34717eb4b 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -493,7 +493,10 @@ typedef struct LengthGpencilModifierData { float overshoot_fac; /** Modifier mode. */ int mode; - char _pad[4]; + /* Curvature paramters. */ + float point_density; + float edge_point_tradeoff; + float max_angle; } LengthGpencilModifierData; typedef enum eLengthGpencil_Flag { @@ -501,6 +504,8 @@ typedef enum eLengthGpencil_Flag { GP_LENGTH_INVERT_PASS = (1 << 1), GP_LENGTH_INVERT_LAYERPASS = (1 << 2), GP_LENGTH_INVERT_MATERIAL = (1 << 3), + GP_LENGTH_USE_CURVATURE = (1 << 4), + GP_LENGTH_INVERT_CURVATURE = (1 << 5), } eLengthGpencil_Flag; typedef enum eLengthGpencil_Type { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 4fa33424994..9b8726d4491 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -74,6 +74,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_DASH, "Dot Dash", "Generate dot-dash styled strokes"}, + {eGpencilModifierType_Length, + "GP_LENGTH", + ICON_MOD_LENGTH, + "Length", + "Extend or shrink strokes"}, {eGpencilModifierType_Lineart, "GP_LINEART", ICON_MOD_LINEART, @@ -120,11 +125,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_LATTICE, "Lattice", "Deform strokes using lattice"}, - {eGpencilModifierType_Length, - "GP_LENGTH", - ICON_MOD_LENGTH, - "Length", - "Extend or shrink strokes"}, {eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"}, {eGpencilModifierType_Offset, "GP_OFFSET", @@ -3278,13 +3278,25 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) prop = RNA_def_property(srna, "start_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "start_fac"); - RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1); + RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2); RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "end_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "end_fac"); - RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1); + RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2); + RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "start_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "start_fac"); + RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3); + RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "end_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "end_fac"); + RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3); RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -3294,7 +3306,7 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Overshoot Factor", - "Defines how precise must follow the stroke trajectory for the overshoot extremes"); + "Defines what portion of the stroke is used for curvature approximation."); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); @@ -3303,6 +3315,44 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", "Mode to define length"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_curvature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_USE_CURVATURE); + RNA_def_property_ui_text(prop, "Use Curvature", "Follow the curvature of the stroke"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_curvature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_CURVATURE); + RNA_def_property_ui_text( + prop, "Invert Curvature", "Invert the curvature of the stroke's extension"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "point_density", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.1f, 1000.0f); + RNA_def_property_ui_range(prop, 0.1f, 1000.0f, 1.0f, 1); + RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC); + RNA_def_property_ui_text( + prop, "Point Density", "Multiplied by Start/End for the total added point count"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "edge_point_tradeoff", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, -2.0f, 3.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 2); + RNA_def_property_ui_text( + prop, + "Smoothness", + "Factor to determine how much to use a theoretical smooth arc between points instead of " + "straight lines when determining the extrapolation shape"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "max_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_ui_text(prop, + "Filter Angle", + "Ignore points on the stroke that deviate from their neighbors by more " + "than this angle when determining the extrapolation shape"); + RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f)); + RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(179.5f), 10.0f, 1); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "layername"); RNA_def_property_ui_text(prop, "Layer", "Layer name"); From 69697fcca96178d53b57b14b5e4a39685a8378a1 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Sun, 19 Sep 2021 13:18:27 +0800 Subject: [PATCH 0007/1500] Revert "GPencil: Curvature support for length modifier." Reason for revert: accidental push of a intermediate change locally. This reverts commit 25e548c96b3d8c1698fd4385b4dc395665b5a7f6. --- source/blender/blenkernel/BKE_gpencil_geom.h | 7 +- .../blender/blenkernel/intern/gpencil_geom.cc | 254 +++--------------- .../intern/MOD_gpencillength.c | 134 ++------- .../makesdna/DNA_gpencil_modifier_defaults.h | 6 +- .../makesdna/DNA_gpencil_modifier_types.h | 7 +- .../makesrna/intern/rna_gpencil_modifier.c | 66 +---- 6 files changed, 68 insertions(+), 406 deletions(-) diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index 20ee9199dba..d472fd6f02b 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -114,12 +114,7 @@ void BKE_gpencil_dissolve_points(struct bGPdata *gpd, bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float overshoot_fac, - const short mode, - const bool follow_curvature, - const int extra_point_count, - const float edge_point_tradeoff, - const float max_angle, - const bool invert_curvature); + const short mode); bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps, const int index_from, const int index_to); diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 2d825705ab0..8ff026231f5 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -540,242 +540,65 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, return true; } -/** - * Give extra stroke points before and after the original tip points. - * \param gps: Target stroke - * \param count_before: how many extra points to be added before a stroke - * \param count_after: how many extra points to be added after a stroke - */ -static bool BKE_gpencil_stroke_extra_points(bGPDstroke *gps, - const int count_before, - const int count_after) -{ - bGPDspoint *pts = gps->points; - - BLI_assert(count_before >= 0); - BLI_assert(count_after >= 0); - if (!count_before && !count_after) { - return false; - } - - const int new_count = count_before + count_after + gps->totpoints; - - bGPDspoint *new_pts = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, __func__); - - for (int i = 0; i < count_before; i++) { - memcpy(&new_pts[i], &pts[0], sizeof(bGPDspoint)); - } - memcpy(&new_pts[count_before], pts, sizeof(bGPDspoint) * gps->totpoints); - for (int i = new_count - count_after; i < new_count; i++) { - memcpy(&new_pts[i], &pts[gps->totpoints - 1], sizeof(bGPDspoint)); - } - - if (gps->dvert) { - MDeformVert *new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count, __func__); - - for (int i = 0; i < new_count; i++) { - MDeformVert *dv = &gps->dvert[CLAMPIS(i - count_before, 0, gps->totpoints - 1)]; - int inew = i; - new_dv[inew].flag = dv->flag; - new_dv[inew].totweight = dv->totweight; - new_dv[inew].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, - __func__); - memcpy(new_dv[inew].dw, dv->dw, sizeof(MDeformWeight) * dv->totweight); - } - BKE_gpencil_free_stroke_weights(gps); - MEM_freeN(gps->dvert); - gps->dvert = new_dv; - } - - MEM_freeN(gps->points); - gps->points = new_pts; - gps->totpoints = new_count; - - return true; -} - /** * Backbone stretch similar to Freestyle. * \param gps: Stroke to sample. - * \param dist: Length of the added section. - * \param overshoot_fac: Relative length of the curve which is used to determine the extension. + * \param dist: Distance of one segment. + * \param overshoot_fac: How exact is the follow curve algorithm. * \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End) - * \param follow_curvature: True for appproximating curvature of given overshoot. - * \param extra_point_count: When follow_curvature is true, use this amount of extra points */ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float overshoot_fac, - const short mode, - const bool follow_curvature, - const int extra_point_count, - const float edge_point_tradeoff, - const float max_angle, - const bool invert_curvature) + const short mode) { #define BOTH 0 #define START 1 #define END 2 - const bool do_start = ELEM(mode, BOTH, START); - const bool do_end = ELEM(mode, BOTH, END); - float used_percent_length = overshoot_fac; - CLAMP(used_percent_length, 1e-4f, 1.0f); - if (!isfinite(used_percent_length)) { - /* If gps->totpoints == 2, then #overshoot_fac is allowed to be non finite. */ - used_percent_length = 0.1f; - } + bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt; + int i; + float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac); - if (gps->totpoints <= 1 || dist < FLT_EPSILON || extra_point_count <= 0) { + if (gps->totpoints < 2 || dist < FLT_EPSILON) { return false; } - /* NOTE: When it's just a straight line, we don't need to do the curvature stuff. */ - if (!follow_curvature || gps->totpoints <= 2) { - /* Not following curvature, just straight line. */ - /* NOTE: #overshoot_point_param can not be zero. */ - float overshoot_point_param = used_percent_length * (gps->totpoints - 1); - float result[3]; + last_pt = &pt[gps->totpoints - 1]; + second_last = &pt[gps->totpoints - 2]; + next_pt = &pt[1]; - if (do_start) { - int index1 = floor(overshoot_point_param); - int index2 = ceil(overshoot_point_param); - interp_v3_v3v3(result, - &gps->points[index1].x, - &gps->points[index2].x, - fmodf(overshoot_point_param, 1.0f)); - sub_v3_v3(result, &gps->points[0].x); - if (UNLIKELY(is_zero_v3(result))) { - sub_v3_v3v3(result, &gps->points[1].x, &gps->points[0].x); - } - madd_v3_v3fl(&gps->points[0].x, result, -dist / len_v3(result)); + if (mode == BOTH || mode == START) { + float len1 = 0.0f; + i = 1; + while (len1 < threshold && gps->totpoints > i) { + next_pt = &pt[i]; + len1 = len_v3v3(&next_pt->x, &pt->x); + i++; } + float extend1 = (len1 + dist) / len1; + float result1[3]; - if (do_end) { - int index1 = gps->totpoints - 1 - floor(overshoot_point_param); - int index2 = gps->totpoints - 1 - ceil(overshoot_point_param); - interp_v3_v3v3(result, - &gps->points[index1].x, - &gps->points[index2].x, - fmodf(overshoot_point_param, 1.0f)); - sub_v3_v3(result, &gps->points[gps->totpoints - 1].x); - if (UNLIKELY(is_zero_v3(result))) { - sub_v3_v3v3( - result, &gps->points[gps->totpoints - 2].x, &gps->points[gps->totpoints - 1].x); - } - madd_v3_v3fl(&gps->points[gps->totpoints - 1].x, result, -dist / len_v3(result)); - } - return true; + interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1); + copy_v3_v3(&pt->x, result1); } - /* Curvature calculation. */ - - /* First allocate the new stroke size. */ - const int first_old_index = do_start ? extra_point_count : 0; - const int last_old_index = gps->totpoints - 1 + first_old_index; - const int orig_totpoints = gps->totpoints; - BKE_gpencil_stroke_extra_points(gps, first_old_index, do_end ? extra_point_count : 0); - - /* The overshoot length in terms of angles with at least 1 used angle. */ - const float overshoot_parameter = used_percent_length * (orig_totpoints - 2); - int overshoot_pointcount = ceil(overshoot_parameter); - CLAMP(overshoot_pointcount, 1, orig_totpoints - 2); - - float point_edge_tradeoff = 1.0f - edge_point_tradeoff; - - /* Do for both sides without code duplication. */ - float no[3], vec1[3], vec2[3], total_angle[3]; - for (int k = 0; k < 2; k++) { - if ((k == 0 && !do_start) || (k == 1 && !do_end)) { - continue; + if (mode == BOTH || mode == END) { + float len2 = 0.0f; + i = 2; + while (len2 < threshold && gps->totpoints >= i) { + second_last = &pt[gps->totpoints - i]; + len2 = len_v3v3(&last_pt->x, &second_last->x); + i++; } - const int start_i = k == 0 ? first_old_index : - last_old_index; // first_old_index, last_old_index - const int dir_i = 1 - k * 2; // 1, -1 + float extend2 = (len2 + dist) / len2; + float result2[3]; + interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2); - sub_v3_v3v3(vec1, &gps->points[start_i + dir_i].x, &gps->points[start_i].x); - zero_v3(total_angle); - float segment_length = normalize_v3(vec1); - float overshoot_length = 0.0f; - - /* Accumulate rotation angle and length. */ - int j = 0; - for (int i = start_i; j < overshoot_pointcount; i += dir_i, j++) { - /* Don't fully add last segment to get continuity in overshoot_fac. */ - float fac = fmin(overshoot_parameter - j, 1.0f); - - /* Read segments. */ - copy_v3_v3(vec2, vec1); - sub_v3_v3v3(vec1, &gps->points[i + dir_i * 2].x, &gps->points[i + dir_i].x); - const float len = normalize_v3(vec1); - float angle = angle_normalized_v3v3(vec1, vec2) * fac; - - /* Add half of both adjacent legs of the current angle. */ - const float added_len = (segment_length + len) * 0.5f * fac; - overshoot_length += added_len; - segment_length = len; - - if (angle > max_angle) { - continue; - } - if (angle > M_PI * 0.995f) { - continue; - } - - angle *= powf(added_len, point_edge_tradeoff); - - cross_v3_v3v3(no, vec1, vec2); - normalize_v3_length(no, angle); - add_v3_v3(total_angle, no); - } - - if (UNLIKELY(overshoot_length == 0.0f)) { - /* Don't do a proper extension if the used points are all in the same position. */ - continue; - } - - sub_v3_v3v3(vec1, &gps->points[start_i].x, &gps->points[start_i + dir_i].x); - /* In general curvature = 1/radius. For the curvature without the - * weights introduced point_edge_tradeoff the calculation is - * curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length */ - float curvature = normalize_v3(total_angle) / overshoot_length; - /* Compensate for the weights powf(added_len, point_edge_tradeoff). */ - curvature /= powf(overshoot_length / fminf(overshoot_parameter, (float)j), - point_edge_tradeoff); - if (invert_curvature) { - curvature = -curvature; - } - const float angle_step = curvature * dist / extra_point_count; - float step_length = dist / extra_point_count; - if (fabsf(angle_step) > FLT_EPSILON) { - /* Make a direct step length from the assigned arc step length. */ - step_length *= sin(angle_step * 0.5f) / (angle_step * 0.5f); - } - else { - zero_v3(total_angle); - } - const float prev_length = normalize_v3_length(vec1, step_length); - - /* Build rotation matrix here to get best performance. */ - float rot[3][3]; - float q[4]; - axis_angle_to_quat(q, total_angle, angle_step); - quat_to_mat3(rot, q); - - /* Rotate the starting direction to account for change in edge lengths. */ - axis_angle_to_quat(q, - total_angle, - fmaxf(0.0f, 1.0f - fabs(point_edge_tradeoff)) * - (curvature * prev_length - angle_step) / 2.0f); - mul_qt_v3(q, vec1); - - /* Now iteratively accumulate the segments with a rotating added direction. */ - for (int i = start_i - dir_i, j = 0; j < extra_point_count; i -= dir_i, j++) { - mul_v3_m3v3(vec1, rot, vec1); - add_v3_v3v3(&gps->points[i].x, vec1, &gps->points[i + dir_i].x); - } + copy_v3_v3(&last_pt->x, result2); } + return true; } @@ -926,7 +749,6 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo second_last = &pt[gps->totpoints - 2]; - float len; float len1, cut_len1; float len2, cut_len2; len1 = len2 = cut_len1 = cut_len2 = 0.0f; @@ -937,13 +759,11 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo i = 0; index_end = gps->totpoints - 1; while (len1 < dist && gps->totpoints > i + 1) { - len = len_v3v3(&pt[i].x, &pt[i + 1].x); - len1 += len; + len1 += len_v3v3(&pt[i].x, &pt[i + 1].x); cut_len1 = len1 - dist; i++; } index_start = i - 1; - interp_v3_v3v3(&pt[index_start].x, &pt[index_start + 1].x, &pt[index_start].x, cut_len1 / len); } if (mode == END) { @@ -951,20 +771,18 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo i = 2; while (len2 < dist && gps->totpoints >= i) { second_last = &pt[gps->totpoints - i]; - len = len_v3v3(&second_last[1].x, &second_last->x); - len2 += len; + len2 += len_v3v3(&second_last[1].x, &second_last->x); cut_len2 = len2 - dist; i++; } index_end = gps->totpoints - i + 2; - interp_v3_v3v3(&pt[index_end].x, &pt[index_end - 1].x, &pt[index_end].x, cut_len2 / len); } if (index_end <= index_start) { index_start = index_end = 0; /* empty stroke */ } - if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < 0)) { + if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) { index_start = index_end = 0; /* no length left to cut */ } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index b104d4685e0..6aa0e6c152e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -72,14 +72,9 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target) } static bool gpencil_modify_stroke(bGPDstroke *gps, - const float length, + float length, const float overshoot_fac, - const short len_mode, - const bool use_curvature, - const int extra_point_count, - const float edge_point_tradeoff, - const float max_angle, - const bool invert_curvature) + const short len_mode) { bool changed = false; if (length == 0.0f) { @@ -87,18 +82,10 @@ static bool gpencil_modify_stroke(bGPDstroke *gps, } if (length > 0.0f) { - changed = BKE_gpencil_stroke_stretch(gps, - length, - overshoot_fac, - len_mode, - use_curvature, - extra_point_count, - edge_point_tradeoff, - max_angle, - invert_curvature); + BKE_gpencil_stroke_stretch(gps, length, overshoot_fac, len_mode); } else { - changed = BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode); + changed |= BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode); } return changed; @@ -109,51 +96,12 @@ static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke bool changed = false; const float len = (lmd->mode == GP_LENGTH_ABSOLUTE) ? 1.0f : BKE_gpencil_stroke_length(gps, true); - const int totpoints = gps->totpoints; if (len < FLT_EPSILON) { return; } - /* Always do the stretching first since it might depend on points which could be deleted by the - * shrink. */ - float first_fac = lmd->start_fac; - int first_mode = 1; - float second_fac = lmd->end_fac; - int second_mode = 2; - if (first_fac < 0) { - SWAP(float, first_fac, second_fac); - SWAP(int, first_mode, second_mode); - } - - const int first_extra_point_count = ceil(first_fac * lmd->point_density); - const int second_extra_point_count = ceil(second_fac * lmd->point_density); - - changed |= gpencil_modify_stroke(gps, - len * first_fac, - lmd->overshoot_fac, - first_mode, - lmd->flag & GP_LENGTH_USE_CURVATURE, - first_extra_point_count, - lmd->edge_point_tradeoff, - lmd->max_angle, - lmd->flag & GP_LENGTH_INVERT_CURVATURE); - /* HACK: The second #overshoot_fac needs to be adjusted because it is not - * done in the same stretch call, because it can have a different length. - * The adjustment needs to be stable when - * ceil(overshoot_fac*(gps->totpoints - 2)) is used in stretch and never - * produce a result highter than totpoints - 2. */ - const float second_overshoot_fac = lmd->overshoot_fac * (totpoints - 2) / - ((float)gps->totpoints - 2) * - (1.0f - 0.1f / (totpoints - 1.0f)); - changed |= gpencil_modify_stroke(gps, - len * second_fac, - second_overshoot_fac, - second_mode, - lmd->flag & GP_LENGTH_USE_CURVATURE, - second_extra_point_count, - lmd->edge_point_tradeoff, - lmd->max_angle, - lmd->flag & GP_LENGTH_INVERT_CURVATURE); + changed |= gpencil_modify_stroke(gps, len * lmd->start_fac, lmd->overshoot_fac, 1); + changed |= gpencil_modify_stroke(gps, len * lmd->end_fac, lmd->overshoot_fac, 2); if (changed) { BKE_gpencil_stroke_geometry_update(gpd, gps); @@ -169,25 +117,20 @@ static void deformStroke(GpencilModifierData *md, { bGPdata *gpd = ob->data; LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md; - if (!is_stroke_affected_by_modifier(ob, - lmd->layername, - lmd->material, - lmd->pass_index, - lmd->layer_pass, - 1, - gpl, - gps, - lmd->flag & GP_LENGTH_INVERT_LAYER, - lmd->flag & GP_LENGTH_INVERT_PASS, - lmd->flag & GP_LENGTH_INVERT_LAYERPASS, - lmd->flag & GP_LENGTH_INVERT_MATERIAL)) { - return; + if (is_stroke_affected_by_modifier(ob, + lmd->layername, + lmd->material, + lmd->pass_index, + lmd->layer_pass, + 1, + gpl, + gps, + lmd->flag & GP_LENGTH_INVERT_LAYER, + lmd->flag & GP_LENGTH_INVERT_PASS, + lmd->flag & GP_LENGTH_INVERT_LAYERPASS, + lmd->flag & GP_LENGTH_INVERT_MATERIAL)) { + applyLength(lmd, gpd, gps); } - if ((gps->flag & GP_STROKE_CYCLIC) != 0) { - /* Don't affect cyclic strokes as they have no start/end. */ - return; - } - applyLength(lmd, gpd, gps); } static void bakeModifier(Main *UNUSED(bmain), @@ -225,14 +168,8 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *col = uiLayoutColumn(layout, true); - if (RNA_enum_get(ptr, "mode") == GP_LENGTH_RELATIVE) { - uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE); - uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE); - } - else { - uiItemR(col, ptr, "start_length", 0, IFACE_("Start"), ICON_NONE); - uiItemR(col, ptr, "end_length", 0, IFACE_("End"), ICON_NONE); - } + uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE); + uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE); uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Overshoot"), ICON_NONE); @@ -244,39 +181,10 @@ static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) gpencil_modifier_masking_panel_draw(panel, true, false); } -static void curvature_header_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *layout = panel->layout; - - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); - - uiItemR(layout, ptr, "use_curvature", 0, IFACE_("Curvature"), ICON_NONE); -} - -static void curvature_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *layout = panel->layout; - - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); - - uiLayoutSetPropSep(layout, true); - - uiLayout *col = uiLayoutColumn(layout, false); - - uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_curvature")); - - uiItemR(col, ptr, "point_density", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "edge_point_tradeoff", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "max_angle", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "invert_curvature", 0, IFACE_("Invert"), ICON_NONE); -} - static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Length, panel_draw); - gpencil_modifier_subpanel_register( - region_type, "curvature", "", curvature_header_draw, curvature_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); } diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 9d7dc50bcbf..450527c7443 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -314,13 +314,9 @@ { \ .start_fac = 0.1f,\ .end_fac = 0.1f,\ - .overshoot_fac = 0.1f,\ + .overshoot_fac = 0.01f,\ .pass_index = 0,\ .material = NULL,\ - .flag = GP_LENGTH_USE_CURVATURE,\ - .point_density = 30.0f,\ - .edge_point_tradeoff = 1.0f,\ - .max_angle = DEG2RAD(170.0f),\ } #define _DNA_DEFAULT_DashGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index cd34717eb4b..d3429329ef6 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -493,10 +493,7 @@ typedef struct LengthGpencilModifierData { float overshoot_fac; /** Modifier mode. */ int mode; - /* Curvature paramters. */ - float point_density; - float edge_point_tradeoff; - float max_angle; + char _pad[4]; } LengthGpencilModifierData; typedef enum eLengthGpencil_Flag { @@ -504,8 +501,6 @@ typedef enum eLengthGpencil_Flag { GP_LENGTH_INVERT_PASS = (1 << 1), GP_LENGTH_INVERT_LAYERPASS = (1 << 2), GP_LENGTH_INVERT_MATERIAL = (1 << 3), - GP_LENGTH_USE_CURVATURE = (1 << 4), - GP_LENGTH_INVERT_CURVATURE = (1 << 5), } eLengthGpencil_Flag; typedef enum eLengthGpencil_Type { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 9b8726d4491..4fa33424994 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -74,11 +74,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_DASH, "Dot Dash", "Generate dot-dash styled strokes"}, - {eGpencilModifierType_Length, - "GP_LENGTH", - ICON_MOD_LENGTH, - "Length", - "Extend or shrink strokes"}, {eGpencilModifierType_Lineart, "GP_LINEART", ICON_MOD_LINEART, @@ -125,6 +120,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_LATTICE, "Lattice", "Deform strokes using lattice"}, + {eGpencilModifierType_Length, + "GP_LENGTH", + ICON_MOD_LENGTH, + "Length", + "Extend or shrink strokes"}, {eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"}, {eGpencilModifierType_Offset, "GP_OFFSET", @@ -3278,25 +3278,13 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) prop = RNA_def_property(srna, "start_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "start_fac"); - RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2); + RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1); RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "end_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "end_fac"); - RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2); - RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "start_length", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_float_sdna(prop, NULL, "start_fac"); - RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3); - RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "end_length", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_float_sdna(prop, NULL, "end_fac"); - RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3); + RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1); RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -3306,7 +3294,7 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Overshoot Factor", - "Defines what portion of the stroke is used for curvature approximation."); + "Defines how precise must follow the stroke trajectory for the overshoot extremes"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); @@ -3315,44 +3303,6 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", "Mode to define length"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "use_curvature", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_USE_CURVATURE); - RNA_def_property_ui_text(prop, "Use Curvature", "Follow the curvature of the stroke"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "invert_curvature", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_CURVATURE); - RNA_def_property_ui_text( - prop, "Invert Curvature", "Invert the curvature of the stroke's extension"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "point_density", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, 0.1f, 1000.0f); - RNA_def_property_ui_range(prop, 0.1f, 1000.0f, 1.0f, 1); - RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC); - RNA_def_property_ui_text( - prop, "Point Density", "Multiplied by Start/End for the total added point count"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "edge_point_tradeoff", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_range(prop, -2.0f, 3.0f); - RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 2); - RNA_def_property_ui_text( - prop, - "Smoothness", - "Factor to determine how much to use a theoretical smooth arc between points instead of " - "straight lines when determining the extrapolation shape"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "max_angle", PROP_FLOAT, PROP_ANGLE); - RNA_def_property_ui_text(prop, - "Filter Angle", - "Ignore points on the stroke that deviate from their neighbors by more " - "than this angle when determining the extrapolation shape"); - RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f)); - RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(179.5f), 10.0f, 1); - RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "layername"); RNA_def_property_ui_text(prop, "Layer", "Layer name"); From 257c7753e9beb2cbd34936766d5fc6c4dc441778 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 19 Sep 2021 01:20:06 -0500 Subject: [PATCH 0008/1500] Cleanup: Rename curve node enums The enum called "interpolate" was really a choice of methods for mapping inputs to positions on the curve, whereas the "sample" enum was used to define a way to create a whole set of new points from the curve, without any input parameters. The "re-sample" vs. "sample" naming makes that distinction better. --- source/blender/makesdna/DNA_node_types.h | 14 +++++++------- .../geometry/nodes/node_geo_curve_resample.cc | 6 +++--- .../geometry/nodes/node_geo_curve_to_points.cc | 6 +++--- .../nodes/geometry/nodes/node_geo_curve_trim.cc | 6 ++---- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index c4cbc71762c..ee1da6be3f2 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1432,7 +1432,7 @@ typedef struct NodeGeometryCurvePrimitiveQuad { } NodeGeometryCurvePrimitiveQuad; typedef struct NodeGeometryCurveResample { - /* GeometryNodeCurveSampleMode. */ + /* GeometryNodeCurveResampleMode. */ uint8_t mode; } NodeGeometryCurveResample; @@ -1442,12 +1442,12 @@ typedef struct NodeGeometryCurveSubdivide { } NodeGeometryCurveSubdivide; typedef struct NodeGeometryCurveTrim { - /* GeometryNodeCurveInterpolateMode. */ + /* GeometryNodeCurveSampleMode. */ uint8_t mode; } NodeGeometryCurveTrim; typedef struct NodeGeometryCurveToPoints { - /* GeometryNodeCurveSampleMode. */ + /* GeometryNodeCurveResampleMode. */ uint8_t mode; } NodeGeometryCurveToPoints; @@ -2029,16 +2029,16 @@ typedef enum GeometryNodeCurvePrimitiveBezierSegmentMode { GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_OFFSET = 1, } GeometryNodeCurvePrimitiveBezierSegmentMode; -typedef enum GeometryNodeCurveSampleMode { +typedef enum GeometryNodeCurveResampleMode { GEO_NODE_CURVE_SAMPLE_COUNT = 0, GEO_NODE_CURVE_SAMPLE_LENGTH = 1, GEO_NODE_CURVE_SAMPLE_EVALUATED = 2, -} GeometryNodeCurveSampleMode; +} GeometryNodeCurveResampleMode; -typedef enum GeometryNodeCurveInterpolateMode { +typedef enum GeometryNodeCurveSampleMode { GEO_NODE_CURVE_INTERPOLATE_FACTOR = 0, GEO_NODE_CURVE_INTERPOLATE_LENGTH = 1, -} GeometryNodeCurveInterpolateMode; +} GeometryNodeCurveSampleMode; typedef enum GeometryNodeAttributeTransferMapMode { GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index e89d500fe57..f8b67cf83f2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -57,7 +57,7 @@ static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node) static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *length_socket = count_socket->next; @@ -67,7 +67,7 @@ static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node } struct SampleModeParam { - GeometryNodeCurveSampleMode mode; + GeometryNodeCurveResampleMode mode; std::optional length; std::optional count; }; @@ -215,7 +215,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) const CurveEval &input_curve = *geometry_set.get_curve_for_read(); NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; SampleModeParam mode_param; mode_param.mode = mode; if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 74740ba244f..623f2da8f11 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -53,7 +53,7 @@ static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node) static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *length_socket = count_socket->next; @@ -77,7 +77,7 @@ static void evaluate_splines(Span splines) } static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, - const GeometryNodeCurveSampleMode mode, + const GeometryNodeCurveResampleMode mode, const CurveEval &curve, const Span splines) { @@ -301,7 +301,7 @@ void curve_create_default_rotation_attribute(Span tangents, static void geo_node_curve_to_points_exec(GeoNodeExecParams params) { NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; GeometrySet geometry_set = params.extract_input("Geometry"); geometry_set = bke::geometry_set_realize_instances(geometry_set); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 4ad311d63c5..f4dc03f1779 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -53,8 +53,7 @@ static void geo_node_curve_trim_init(bNodeTree *UNUSED(tree), bNode *node) static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node) { const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)node->storage; - const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode) - node_storage.mode; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; bNodeSocket *start_fac = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *end_fac = start_fac->next; @@ -324,8 +323,7 @@ static void trim_bezier_spline(Spline &spline, static void geo_node_curve_trim_exec(GeoNodeExecParams params) { const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage; - const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode) - node_storage.mode; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; GeometrySet geometry_set = params.extract_input("Curve"); geometry_set = bke::geometry_set_realize_instances(geometry_set); From 942c471ce9692379d6b935b97c6f6019fec0c8a1 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 14 Sep 2021 17:25:47 +0200 Subject: [PATCH 0009/1500] Compositor: Fix Alpha Over node ignoring emissive colors It was an issue on Full Frame mode only. --- .../compositor/operations/COM_AlphaOverPremultiplyOperation.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc index a57e8c7f8a3..911e8d2df92 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc @@ -62,7 +62,8 @@ void AlphaOverPremultiplyOperation::update_memory_buffer_row(PixelCursor &p) const float *over_color = p.color2; const float value = *p.value; - if (over_color[3] <= 0.0f) { + /* Zero alpha values should still permit an add of RGB data. */ + if (over_color[3] < 0.0f) { copy_v4_v4(p.out, color1); } else if (value == 1.0f && over_color[3] >= 1.0f) { From f256bfb3e26c32af12c82dbd32d4b3bcfba252f3 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sat, 18 Sep 2021 19:04:47 +0200 Subject: [PATCH 0010/1500] Compositor: Fix crash exporting buffers on debug ImBuf allocates 4 channels, use copying to support buffers with 1 and 3 channels. --- source/blender/compositor/intern/COM_Debug.cc | 12 +++++++----- source/blender/compositor/intern/COM_MemoryBuffer.cc | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 007085ee528..f2dcba65b7c 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -468,11 +468,13 @@ static std::string get_operations_export_dir() void DebugInfo::export_operation(const NodeOperation *op, MemoryBuffer *render) { - ImBuf *ibuf = IMB_allocFromBuffer(nullptr, - render->getBuffer(), - render->getWidth(), - render->getHeight(), - render->get_num_channels()); + const int width = render->getWidth(); + const int height = render->getHeight(); + const int num_channels = render->get_num_channels(); + + ImBuf *ibuf = IMB_allocImBuf(width, height, 8 * num_channels, IB_rectfloat); + MemoryBuffer mem_ibuf(ibuf->rect_float, 4, width, height); + mem_ibuf.copy_from(render, render->get_rect(), 0, num_channels, 0); const std::string file_name = operation_class_name(op) + "_" + std::to_string(op->get_id()) + ".png"; diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 1fbf502fea6..5327be50b53 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -32,7 +32,7 @@ BLI_assert((buf)->get_rect().ymax >= (y) + BLI_rcti_size_y(&(area))) #define ASSERT_VALID_ELEM_SIZE(buf, channel_offset, elem_size) \ - BLI_assert((buf)->get_num_channels() <= (channel_offset) + (elem_size)) + BLI_assert((buf)->get_num_channels() >= (channel_offset) + (elem_size)) namespace blender::compositor { From 276eebb274744d819dcdab8a95770dd7382c0664 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sun, 19 Sep 2021 20:12:53 +0200 Subject: [PATCH 0011/1500] Compositor: Add OIDN prefiltering option to Denoise node It's equivalent to the OpenImageDenoise prefiltering option in Cycles. See D12043. Prefilter modes: - None: No prefiltering, use when guiding passes are noise-free. - Fast: Denoise image and guiding passes together. Improves quality when guiding passes are noisy using least amount of extra processing time. - Accurate: Prefilter noisy guiding passes before denoising image. Improves quality when guiding passes are noisy using extra processing time. Reviewed By: #compositing, jbakker, sergey Differential Revision: https://developer.blender.org/D12342 --- .../compositor/nodes/COM_DenoiseNode.cc | 30 +- .../operations/COM_DenoiseOperation.cc | 349 ++++++++++++------ .../operations/COM_DenoiseOperation.h | 50 ++- source/blender/editors/space_node/drawnode.cc | 2 + source/blender/makesdna/DNA_node_types.h | 9 + source/blender/makesrna/intern/rna_nodetree.c | 25 ++ .../composite/nodes/node_composite_denoise.c | 1 + 7 files changed, 346 insertions(+), 120 deletions(-) diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.cc b/source/blender/compositor/nodes/COM_DenoiseNode.cc index e58a9c7ba9a..cc9328414ef 100644 --- a/source/blender/compositor/nodes/COM_DenoiseNode.cc +++ b/source/blender/compositor/nodes/COM_DenoiseNode.cc @@ -31,6 +31,12 @@ DenoiseNode::DenoiseNode(bNode *editorNode) : Node(editorNode) void DenoiseNode::convertToOperations(NodeConverter &converter, const CompositorContext & /*context*/) const { + if (!COM_is_denoise_supported()) { + converter.mapOutputSocket(getOutputSocket(0), + converter.addInputProxy(getInputSocket(0), false)); + return; + } + bNode *node = this->getbNode(); NodeDenoise *denoise = (NodeDenoise *)node->storage; @@ -39,8 +45,28 @@ void DenoiseNode::convertToOperations(NodeConverter &converter, operation->setDenoiseSettings(denoise); converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); + if (denoise && denoise->prefilter == CMP_NODE_DENOISE_PREFILTER_ACCURATE) { + { + DenoisePrefilterOperation *normal_prefilter = new DenoisePrefilterOperation( + DataType::Vector); + normal_prefilter->set_image_name("normal"); + converter.addOperation(normal_prefilter); + converter.mapInputSocket(getInputSocket(1), normal_prefilter->getInputSocket(0)); + converter.addLink(normal_prefilter->getOutputSocket(), operation->getInputSocket(1)); + } + { + DenoisePrefilterOperation *albedo_prefilter = new DenoisePrefilterOperation(DataType::Color); + albedo_prefilter->set_image_name("albedo"); + converter.addOperation(albedo_prefilter); + converter.mapInputSocket(getInputSocket(2), albedo_prefilter->getInputSocket(0)); + converter.addLink(albedo_prefilter->getOutputSocket(), operation->getInputSocket(2)); + } + } + else { + converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); + } + converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); } diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index e7f2d5a740a..0c660e0b723 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -28,6 +28,137 @@ static pthread_mutex_t oidn_lock = BLI_MUTEX_INITIALIZER; namespace blender::compositor { +bool COM_is_denoise_supported() +{ +#ifdef WITH_OPENIMAGEDENOISE + /* Always supported through Accelerate framework BNNS on macOS. */ +# ifdef __APPLE__ + return true; +# else + return BLI_cpu_support_sse41(); +# endif + +#else + return false; +#endif +} + +class DenoiseFilter { + private: +#ifdef WITH_OPENIMAGEDENOISE + oidn::DeviceRef device; + oidn::FilterRef filter; +#endif + bool initialized_ = false; + + public: + ~DenoiseFilter() + { + BLI_assert(!initialized_); + } + +#ifdef WITH_OPENIMAGEDENOISE + void init_and_lock_denoiser(MemoryBuffer *output) + { + /* Since it's memory intensive, it's better to run only one instance of OIDN at a time. + * OpenImageDenoise is multithreaded internally and should use all available cores + * nonetheless. */ + BLI_mutex_lock(&oidn_lock); + + device = oidn::newDevice(); + device.commit(); + filter = device.newFilter("RT"); + initialized_ = true; + set_image("output", output); + } + + void deinit_and_unlock_denoiser() + { + BLI_mutex_unlock(&oidn_lock); + initialized_ = false; + } + + void set_image(const StringRef name, MemoryBuffer *buffer) + { + BLI_assert(initialized_); + BLI_assert(!buffer->is_a_single_elem()); + filter.setImage(name.data(), + buffer->getBuffer(), + oidn::Format::Float3, + buffer->getWidth(), + buffer->getHeight(), + 0, + buffer->get_elem_bytes_len()); + } + + template void set(const StringRef option_name, T value) + { + BLI_assert(initialized_); + filter.set(option_name.data(), value); + } + + void execute() + { + BLI_assert(initialized_); + filter.commit(); + filter.execute(); + } + +#else + void init_and_lock_denoiser(MemoryBuffer *UNUSED(output)) + { + } + + void deinit_and_unlock_denoiser() + { + } + + void set_image(const StringRef UNUSED(name), MemoryBuffer *UNUSED(buffer)) + { + } + + template void set(const StringRef UNUSED(option_name), T UNUSED(value)) + { + } + + void execute() + { + } +#endif +}; + +DenoiseBaseOperation::DenoiseBaseOperation() +{ + flags.is_fullframe_operation = true; + output_rendered_ = false; +} + +bool DenoiseBaseOperation::determineDependingAreaOfInterest(rcti * /*input*/, + ReadBufferOperation *readOperation, + rcti *output) +{ + if (isCached()) { + return false; + } + + rcti newInput; + newInput.xmax = this->getWidth(); + newInput.xmin = 0; + newInput.ymax = this->getHeight(); + newInput.ymin = 0; + return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); +} + +void DenoiseBaseOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + r_input_area.xmin = 0; + r_input_area.xmax = this->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = this->getHeight(); +} + DenoiseOperation::DenoiseOperation() { this->addInputSocket(DataType::Color); @@ -35,8 +166,6 @@ DenoiseOperation::DenoiseOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); this->m_settings = nullptr; - flags.is_fullframe_operation = true; - output_rendered_ = false; } void DenoiseOperation::initExecution() { @@ -54,6 +183,25 @@ void DenoiseOperation::deinitExecution() SingleThreadedOperation::deinitExecution(); } +static bool are_guiding_passes_noise_free(NodeDenoise *settings) +{ + switch (settings->prefilter) { + case CMP_NODE_DENOISE_PREFILTER_NONE: + case CMP_NODE_DENOISE_PREFILTER_ACCURATE: /* Prefiltered with #DenoisePrefilterOperation. */ + return true; + case CMP_NODE_DENOISE_PREFILTER_FAST: + default: + return false; + } +} + +void DenoiseOperation::hash_output_params() +{ + if (m_settings) { + hash_params((int)m_settings->hdr, are_guiding_passes_noise_free(m_settings)); + } +} + MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2) { MemoryBuffer *tileColor = (MemoryBuffer *)this->m_inputProgramColor->initializeTileData(rect2); @@ -69,22 +217,6 @@ MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2) return result; } -bool DenoiseOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) -{ - if (isCached()) { - return false; - } - - rcti newInput; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); -} - void DenoiseOperation::generateDenoise(MemoryBuffer *output, MemoryBuffer *input_color, MemoryBuffer *input_normal, @@ -96,104 +228,46 @@ void DenoiseOperation::generateDenoise(MemoryBuffer *output, return; } -#ifdef WITH_OPENIMAGEDENOISE - /* Always supported through Accelerate framework BNNS on macOS. */ -# ifndef __APPLE__ - if (BLI_cpu_support_sse41()) -# endif - { - /* OpenImageDenoise needs full buffers. */ - MemoryBuffer *buf_color = input_color->is_a_single_elem() ? input_color->inflate() : - input_color; - MemoryBuffer *buf_normal = input_normal && input_normal->is_a_single_elem() ? - input_normal->inflate() : - input_normal; - MemoryBuffer *buf_albedo = input_albedo && input_albedo->is_a_single_elem() ? - input_albedo->inflate() : - input_albedo; + BLI_assert(COM_is_denoise_supported()); + /* OpenImageDenoise needs full buffers. */ + MemoryBuffer *buf_color = input_color->is_a_single_elem() ? input_color->inflate() : input_color; + MemoryBuffer *buf_normal = input_normal && input_normal->is_a_single_elem() ? + input_normal->inflate() : + input_normal; + MemoryBuffer *buf_albedo = input_albedo && input_albedo->is_a_single_elem() ? + input_albedo->inflate() : + input_albedo; - /* Since it's memory intensive, it's better to run only one instance of OIDN at a time. - * OpenImageDenoise is multithreaded internally and should use all available cores nonetheless. - */ - BLI_mutex_lock(&oidn_lock); + DenoiseFilter filter; + filter.init_and_lock_denoiser(output); - oidn::DeviceRef device = oidn::newDevice(); - device.commit(); + filter.set_image("color", buf_color); + filter.set_image("normal", buf_normal); + filter.set_image("albedo", buf_albedo); - oidn::FilterRef filter = device.newFilter("RT"); - filter.setImage("color", - buf_color->getBuffer(), - oidn::Format::Float3, - buf_color->getWidth(), - buf_color->getHeight(), - 0, - sizeof(float[4])); - if (buf_normal && buf_normal->getBuffer()) { - filter.setImage("normal", - buf_normal->getBuffer(), - oidn::Format::Float3, - buf_normal->getWidth(), - buf_normal->getHeight(), - 0, - sizeof(float[3])); - } - if (buf_albedo && buf_albedo->getBuffer()) { - filter.setImage("albedo", - buf_albedo->getBuffer(), - oidn::Format::Float3, - buf_albedo->getWidth(), - buf_albedo->getHeight(), - 0, - sizeof(float[4])); - } - filter.setImage("output", - output->getBuffer(), - oidn::Format::Float3, - buf_color->getWidth(), - buf_color->getHeight(), - 0, - sizeof(float[4])); - - BLI_assert(settings); - if (settings) { - filter.set("hdr", settings->hdr); - filter.set("srgb", false); - } - - filter.commit(); - filter.execute(); - BLI_mutex_unlock(&oidn_lock); - - /* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */ - output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3); - - /* Delete inflated buffers. */ - if (input_color->is_a_single_elem()) { - delete buf_color; - } - if (input_normal && input_normal->is_a_single_elem()) { - delete buf_normal; - } - if (input_albedo && input_albedo->is_a_single_elem()) { - delete buf_albedo; - } - - return; + BLI_assert(settings); + if (settings) { + filter.set("hdr", settings->hdr); + filter.set("srgb", false); + filter.set("cleanAux", are_guiding_passes_noise_free(settings)); } -#endif - /* If built without OIDN or running on an unsupported CPU, just pass through. */ - UNUSED_VARS(input_albedo, input_normal, settings); - output->copy_from(input_color, input_color->get_rect()); -} -void DenoiseOperation::get_area_of_interest(const int UNUSED(input_idx), - const rcti &UNUSED(output_area), - rcti &r_input_area) -{ - r_input_area.xmin = 0; - r_input_area.xmax = this->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = this->getHeight(); + filter.execute(); + filter.deinit_and_unlock_denoiser(); + + /* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */ + output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3); + + /* Delete inflated buffers. */ + if (input_color->is_a_single_elem()) { + delete buf_color; + } + if (input_normal && input_normal->is_a_single_elem()) { + delete buf_normal; + } + if (input_albedo && input_albedo->is_a_single_elem()) { + delete buf_albedo; + } } void DenoiseOperation::update_memory_buffer(MemoryBuffer *output, @@ -206,4 +280,57 @@ void DenoiseOperation::update_memory_buffer(MemoryBuffer *output, } } +DenoisePrefilterOperation::DenoisePrefilterOperation(DataType data_type) +{ + this->addInputSocket(data_type); + this->addOutputSocket(data_type); + image_name_ = ""; +} + +void DenoisePrefilterOperation::hash_output_params() +{ + hash_param(image_name_); +} + +MemoryBuffer *DenoisePrefilterOperation::createMemoryBuffer(rcti *rect2) +{ + MemoryBuffer *input = (MemoryBuffer *)this->get_input_operation(0)->initializeTileData(rect2); + rcti rect; + BLI_rcti_init(&rect, 0, getWidth(), 0, getHeight()); + + MemoryBuffer *result = new MemoryBuffer(getOutputSocket()->getDataType(), rect); + generate_denoise(result, input); + + return result; +} + +void DenoisePrefilterOperation::generate_denoise(MemoryBuffer *output, MemoryBuffer *input) +{ + BLI_assert(COM_is_denoise_supported()); + + /* Denoising needs full buffers. */ + MemoryBuffer *input_buf = input->is_a_single_elem() ? input->inflate() : input; + + DenoiseFilter filter; + filter.init_and_lock_denoiser(output); + filter.set_image(image_name_, input_buf); + filter.execute(); + filter.deinit_and_unlock_denoiser(); + + /* Delete inflated buffers. */ + if (input->is_a_single_elem()) { + delete input_buf; + } +} + +void DenoisePrefilterOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &UNUSED(area), + Span inputs) +{ + if (!output_rendered_) { + this->generate_denoise(output, inputs[0]); + output_rendered_ = true; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h index 48209c3eacf..1b053b79c2d 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.h +++ b/source/blender/compositor/operations/COM_DenoiseOperation.h @@ -23,7 +23,24 @@ namespace blender::compositor { -class DenoiseOperation : public SingleThreadedOperation { +bool COM_is_denoise_supported(); + +class DenoiseBaseOperation : public SingleThreadedOperation { + protected: + bool output_rendered_; + + protected: + DenoiseBaseOperation(); + + public: + bool determineDependingAreaOfInterest(rcti *input, + ReadBufferOperation *readOperation, + rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; +}; + +class DenoiseOperation : public DenoiseBaseOperation { private: /** * \brief Cached reference to the input programs @@ -37,8 +54,6 @@ class DenoiseOperation : public SingleThreadedOperation { */ NodeDenoise *m_settings; - bool output_rendered_; - public: DenoiseOperation(); /** @@ -55,16 +70,13 @@ class DenoiseOperation : public SingleThreadedOperation { { this->m_settings = settings; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer(MemoryBuffer *output, const rcti &area, Span inputs) override; protected: + void hash_output_params() override; void generateDenoise(MemoryBuffer *output, MemoryBuffer *input_color, MemoryBuffer *input_normal, @@ -74,4 +86,28 @@ class DenoiseOperation : public SingleThreadedOperation { MemoryBuffer *createMemoryBuffer(rcti *rect) override; }; +class DenoisePrefilterOperation : public DenoiseBaseOperation { + private: + std::string image_name_; + + public: + DenoisePrefilterOperation(DataType data_type); + + void set_image_name(StringRef name) + { + image_name_ = name; + } + + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + + protected: + void hash_output_params() override; + MemoryBuffer *createMemoryBuffer(rcti *rect) override; + + private: + void generate_denoise(MemoryBuffer *output, MemoryBuffer *input); +}; + } // namespace blender::compositor diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 4b859de0ac9..62f40152416 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -2865,6 +2865,8 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po # endif #endif + uiItemL(layout, IFACE_("Prefilter:"), ICON_NONE); + uiItemR(layout, ptr, "prefilter", DEFAULT_FLAGS, nullptr, ICON_NONE); uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, nullptr, ICON_NONE); } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index ee1da6be3f2..74ed22ecafd 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1167,6 +1167,7 @@ typedef struct NodeCryptomatte { typedef struct NodeDenoise { char hdr; + char prefilter; } NodeDenoise; typedef struct NodeAttributeClamp { @@ -1840,6 +1841,14 @@ typedef enum CMPNodeSetAlphaMode { CMP_NODE_SETALPHA_MODE_REPLACE_ALPHA = 1, } CMPNodeSetAlphaMode; +/* Denoise Node. */ +/* `NodeDenoise.prefilter` */ +typedef enum CMPNodeDenoisePrefilter { + CMP_NODE_DENOISE_PREFILTER_FAST = 0, + CMP_NODE_DENOISE_PREFILTER_NONE = 1, + CMP_NODE_DENOISE_PREFILTER_ACCURATE = 2 +} CMPNodeDenoisePrefilter; + #define CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX 64 /* Point Density shader node */ diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 86e134799aa..d685692c8fa 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -8890,12 +8890,37 @@ static void def_cmp_denoise(StructRNA *srna) { PropertyRNA *prop; + static const EnumPropertyItem prefilter_items[] = { + {CMP_NODE_DENOISE_PREFILTER_NONE, + "NONE", + 0, + "None", + "No prefiltering, use when guiding passes are noise-free"}, + {CMP_NODE_DENOISE_PREFILTER_FAST, + "FAST", + 0, + "Fast", + "Denoise image and guiding passes together. Improves quality when guiding passes are noisy " + "using least amount of extra processing time"}, + {CMP_NODE_DENOISE_PREFILTER_ACCURATE, + "ACCURATE", + 0, + "Accurate", + "Prefilter noisy guiding passes before denoising image. Improves quality when guiding " + "passes are noisy using extra processing time"}, + {0, NULL, 0, NULL, NULL}}; + RNA_def_struct_sdna_from(srna, "NodeDenoise", "storage"); prop = RNA_def_property(srna, "use_hdr", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "hdr", 0); RNA_def_property_ui_text(prop, "HDR", "Process HDR images"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "prefilter", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prefilter_items); + RNA_def_property_ui_text(prop, "", "Denoising prefilter"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } static void def_cmp_antialiasing(StructRNA *srna) diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.c b/source/blender/nodes/composite/nodes/node_composite_denoise.c index 040b350627e..e2c7c7b995f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_denoise.c +++ b/source/blender/nodes/composite/nodes/node_composite_denoise.c @@ -36,6 +36,7 @@ static void node_composit_init_denonise(bNodeTree *UNUSED(ntree), bNode *node) { NodeDenoise *ndg = MEM_callocN(sizeof(NodeDenoise), "node denoise data"); ndg->hdr = true; + ndg->prefilter = CMP_NODE_DENOISE_PREFILTER_ACCURATE; node->storage = ndg; } From 25aa943e8cb8d5a33beb906207255e5d0ea08544 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 19 Sep 2021 18:54:03 -0500 Subject: [PATCH 0012/1500] Cleanup: Fix/improve variable names and comments --- .../blender/blenkernel/intern/curve_eval.cc | 2 +- .../blender/blenkernel/intern/spline_base.cc | 2 +- source/blender/makesdna/DNA_node_types.h | 10 ++++----- source/blender/makesrna/intern/rna_nodetree.c | 16 +++++++------- .../nodes/node_geo_attribute_capture.cc | 8 +++---- .../geometry/nodes/node_geo_curve_resample.cc | 16 +++++++------- .../nodes/node_geo_curve_to_points.cc | 18 ++++++++-------- .../geometry/nodes/node_geo_curve_trim.cc | 21 +++++++++---------- 8 files changed, 46 insertions(+), 47 deletions(-) diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 1c4f9c5a6ab..ea84766943d 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -110,7 +110,7 @@ void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluate } /** - * Return the start indices for each of the curve spline's evaluated points, as if they were part + * Return the start indices for each of the curve spline's control points, if they were part * of a flattened array. This can be used to facilitate parallelism by avoiding the need to * accumulate an offset while doing more complex calculations. * diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index a8871777420..807019f60a8 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -190,7 +190,7 @@ static void accumulate_lengths(Span positions, * Return non-owning access to the cache of accumulated lengths along the spline. Each item is the * length of the subsequent segment, i.e. the first value is the length of the first segment rather * than 0. This calculation is rather trivial, and only depends on the evaluated positions. - * However, the results are used often, so it makes sense to cache it. + * However, the results are used often, and it is necessarily single threaded, so it is cached. */ Span Spline::evaluated_lengths() const { diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 74ed22ecafd..39af3650558 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -2039,14 +2039,14 @@ typedef enum GeometryNodeCurvePrimitiveBezierSegmentMode { } GeometryNodeCurvePrimitiveBezierSegmentMode; typedef enum GeometryNodeCurveResampleMode { - GEO_NODE_CURVE_SAMPLE_COUNT = 0, - GEO_NODE_CURVE_SAMPLE_LENGTH = 1, - GEO_NODE_CURVE_SAMPLE_EVALUATED = 2, + GEO_NODE_CURVE_RESAMPLE_COUNT = 0, + GEO_NODE_CURVE_RESAMPLE_LENGTH = 1, + GEO_NODE_CURVE_RESAMPLE_EVALUATED = 2, } GeometryNodeCurveResampleMode; typedef enum GeometryNodeCurveSampleMode { - GEO_NODE_CURVE_INTERPOLATE_FACTOR = 0, - GEO_NODE_CURVE_INTERPOLATE_LENGTH = 1, + GEO_NODE_CURVE_SAMPLE_FACTOR = 0, + GEO_NODE_CURVE_RESAMPLE_LENGTH = 1, } GeometryNodeCurveSampleMode; typedef enum GeometryNodeAttributeTransferMapMode { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index d685692c8fa..76e37dbcdbc 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10116,18 +10116,18 @@ static void def_geo_curve_resample(StructRNA *srna) PropertyRNA *prop; static EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_SAMPLE_EVALUATED, + {GEO_NODE_CURVE_RESAMPLE_EVALUATED, "EVALUATED", 0, "Evaluated", "Output the input spline's evaluated points, based on the resolution attribute for NURBS " "and Bezier splines. Poly splines are unchanged"}, - {GEO_NODE_CURVE_SAMPLE_COUNT, + {GEO_NODE_CURVE_RESAMPLE_COUNT, "COUNT", 0, "Count", "Sample the specified number of points along each spline"}, - {GEO_NODE_CURVE_SAMPLE_LENGTH, + {GEO_NODE_CURVE_RESAMPLE_LENGTH, "LENGTH", 0, "Length", @@ -10161,18 +10161,18 @@ static void def_geo_curve_to_points(StructRNA *srna) PropertyRNA *prop; static EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_SAMPLE_EVALUATED, + {GEO_NODE_CURVE_RESAMPLE_EVALUATED, "EVALUATED", 0, "Evaluated", "Create points from the curve's evaluated points, based on the resolution attribute for " "NURBS and Bezier splines"}, - {GEO_NODE_CURVE_SAMPLE_COUNT, + {GEO_NODE_CURVE_RESAMPLE_COUNT, "COUNT", 0, "Count", "Sample each spline by evenly distributing the specified number of points"}, - {GEO_NODE_CURVE_SAMPLE_LENGTH, + {GEO_NODE_CURVE_RESAMPLE_LENGTH, "LENGTH", 0, "Length", @@ -10193,12 +10193,12 @@ static void def_geo_curve_trim(StructRNA *srna) PropertyRNA *prop; static EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_INTERPOLATE_FACTOR, + {GEO_NODE_CURVE_SAMPLE_FACTOR, "FACTOR", 0, "Factor", "Find the endpoint positions using a factor of each spline's length"}, - {GEO_NODE_CURVE_INTERPOLATE_LENGTH, + {GEO_NODE_CURVE_RESAMPLE_LENGTH, "LENGTH", 0, "Length", diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 1fa71d3f57d..c8a33205de4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -66,8 +66,8 @@ static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *n node->storage; const CustomDataType data_type = static_cast(storage.data_type); - bNodeSocket *socket_value_attribute_name = (bNodeSocket *)node->inputs.first; - bNodeSocket *socket_value_vector = socket_value_attribute_name->next; + bNodeSocket *socket_value_geometry = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_value_vector = socket_value_geometry->next; bNodeSocket *socket_value_float = socket_value_vector->next; bNodeSocket *socket_value_color4f = socket_value_float->next; bNodeSocket *socket_value_boolean = socket_value_color4f->next; @@ -79,8 +79,8 @@ static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *n nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL); nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32); - bNodeSocket *out_socket_value_attribute_name = (bNodeSocket *)node->outputs.first; - bNodeSocket *out_socket_value_vector = out_socket_value_attribute_name->next; + bNodeSocket *out_socket_value_geometry = (bNodeSocket *)node->outputs.first; + bNodeSocket *out_socket_value_vector = out_socket_value_geometry->next; bNodeSocket *out_socket_value_float = out_socket_value_vector->next; bNodeSocket *out_socket_value_color4f = out_socket_value_float->next; bNodeSocket *out_socket_value_boolean = out_socket_value_color4f->next; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index f8b67cf83f2..208525f17f6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -50,7 +50,7 @@ static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node) NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN( sizeof(NodeGeometryCurveResample), __func__); - data->mode = GEO_NODE_CURVE_SAMPLE_COUNT; + data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; } @@ -62,8 +62,8 @@ static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *length_socket = count_socket->next; - nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT); - nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } struct SampleModeParam { @@ -172,7 +172,7 @@ static std::unique_ptr resample_curve(const CurveEval &input_curve, output_curve->resize(input_splines.size()); MutableSpan output_splines = output_curve->splines(); - if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) { + if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { BLI_assert(mode_param.count); @@ -180,7 +180,7 @@ static std::unique_ptr resample_curve(const CurveEval &input_curve, } }); } - else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { const float length = input_splines[i]->length(); @@ -189,7 +189,7 @@ static std::unique_ptr resample_curve(const CurveEval &input_curve, } }); } - else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_EVALUATED) { + else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_EVALUATED) { threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { output_splines[i] = resample_spline_evaluated(*input_splines[i]); @@ -218,7 +218,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; SampleModeParam mode_param; mode_param.mode = mode; - if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) { + if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { const int count = params.extract_input("Count"); if (count < 1) { params.set_output("Geometry", GeometrySet()); @@ -226,7 +226,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) } mode_param.count.emplace(count); } - else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { /* Don't allow asymptotic count increase for low resolution values. */ const float resolution = std::max(params.extract_input("Length"), 0.0001f); mode_param.length.emplace(resolution); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 623f2da8f11..1e66b340f5c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -46,7 +46,7 @@ static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node) NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN( sizeof(NodeGeometryCurveToPoints), __func__); - data->mode = GEO_NODE_CURVE_SAMPLE_COUNT; + data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; } @@ -58,8 +58,8 @@ static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *nod bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *length_socket = count_socket->next; - nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT); - nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } /** @@ -83,7 +83,7 @@ static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, { const int size = curve.splines().size(); switch (mode) { - case GEO_NODE_CURVE_SAMPLE_COUNT: { + case GEO_NODE_CURVE_RESAMPLE_COUNT: { const int count = params.extract_input("Count"); if (count < 1) { return {0}; @@ -94,7 +94,7 @@ static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, } return offsets; } - case GEO_NODE_CURVE_SAMPLE_LENGTH: { + case GEO_NODE_CURVE_RESAMPLE_LENGTH: { /* Don't allow asymptotic count increase for low resolution values. */ const float resolution = std::max(params.extract_input("Length"), 0.0001f); Array offsets(size + 1); @@ -106,7 +106,7 @@ static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, offsets.last() = offset; return offsets; } - case GEO_NODE_CURVE_SAMPLE_EVALUATED: { + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: { return curve.evaluated_point_offsets(); } } @@ -331,11 +331,11 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params) CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component, curve); switch (mode) { - case GEO_NODE_CURVE_SAMPLE_COUNT: - case GEO_NODE_CURVE_SAMPLE_LENGTH: + case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_LENGTH: copy_uniform_sample_point_attributes(splines, offsets, new_attributes); break; - case GEO_NODE_CURVE_SAMPLE_EVALUATED: + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: copy_evaluated_point_attributes(splines, offsets, new_attributes); break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index f4dc03f1779..d2217656e20 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -46,7 +46,7 @@ static void geo_node_curve_trim_init(bNodeTree *UNUSED(tree), bNode *node) NodeGeometryCurveTrim *data = (NodeGeometryCurveTrim *)MEM_callocN(sizeof(NodeGeometryCurveTrim), __func__); - data->mode = GEO_NODE_CURVE_INTERPOLATE_FACTOR; + data->mode = GEO_NODE_CURVE_SAMPLE_FACTOR; node->storage = data; } @@ -60,10 +60,10 @@ static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node) bNodeSocket *start_len = end_fac->next; bNodeSocket *end_len = start_len->next; - nodeSetSocketAvailability(start_fac, mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR); - nodeSetSocketAvailability(end_fac, mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR); - nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH); - nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH); + nodeSetSocketAvailability(start_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); + nodeSetSocketAvailability(end_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); + nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); + nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } struct TrimLocation { @@ -336,12 +336,11 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params) CurveEval &curve = *curve_component.get_for_write(); MutableSpan splines = curve.splines(); - const float start = mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR ? + const float start = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? params.extract_input("Start") : params.extract_input("Start_001"); - const float end = mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR ? - params.extract_input("End") : - params.extract_input("End_001"); + const float end = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? params.extract_input("End") : + params.extract_input("End_001"); threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { @@ -360,11 +359,11 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params) } const Spline::LookupResult start_lookup = - (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) ? + (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) ? spline.lookup_evaluated_length(std::clamp(start, 0.0f, spline.length())) : spline.lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)); const Spline::LookupResult end_lookup = - (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) ? + (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) ? spline.lookup_evaluated_length(std::clamp(end, 0.0f, spline.length())) : spline.lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f)); From c9e835fec1bcd8ae42b57feaefaa6f4d946f546f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 19 Sep 2021 18:59:01 -0500 Subject: [PATCH 0013/1500] Fix build error after previous commit Incorrect renaming and use of enum after search and replace. --- source/blender/makesdna/DNA_node_types.h | 2 +- .../blender/nodes/geometry/nodes/node_geo_curve_trim.cc | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 39af3650558..f4c88333528 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -2046,7 +2046,7 @@ typedef enum GeometryNodeCurveResampleMode { typedef enum GeometryNodeCurveSampleMode { GEO_NODE_CURVE_SAMPLE_FACTOR = 0, - GEO_NODE_CURVE_RESAMPLE_LENGTH = 1, + GEO_NODE_CURVE_SAMPLE_LENGTH = 1, } GeometryNodeCurveSampleMode; typedef enum GeometryNodeAttributeTransferMapMode { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index d2217656e20..a90a7665ec7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -62,8 +62,8 @@ static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node) nodeSetSocketAvailability(start_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); nodeSetSocketAvailability(end_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); - nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); - nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); + nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); } struct TrimLocation { @@ -359,11 +359,11 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params) } const Spline::LookupResult start_lookup = - (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) ? + (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) ? spline.lookup_evaluated_length(std::clamp(start, 0.0f, spline.length())) : spline.lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)); const Spline::LookupResult end_lookup = - (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) ? + (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) ? spline.lookup_evaluated_length(std::clamp(end, 0.0f, spline.length())) : spline.lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f)); From f973e0b75a79ef6b05677e36d41fdeff70ea6d9d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 19 Sep 2021 19:00:50 -0500 Subject: [PATCH 0014/1500] Fix: Spline length calculation fails with no evaluated points The case that checked whether there were evaluated edges was incorrect, since two points are needed for an edge. Then also avoid running the accumulation for an empty span. --- source/blender/blenkernel/intern/spline_base.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 807019f60a8..663c1951ba3 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -142,7 +142,8 @@ void Spline::reverse() int Spline::evaluated_edges_size() const { const int eval_size = this->evaluated_points_size(); - if (eval_size == 1) { + if (eval_size < 2) { + /* Two points are required for an edge. */ return 0; } @@ -205,9 +206,10 @@ Span Spline::evaluated_lengths() const const int total = evaluated_edges_size(); evaluated_lengths_cache_.resize(total); - - Span positions = this->evaluated_positions(); - accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_); + if (total != 0) { + Span positions = this->evaluated_positions(); + accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_); + } length_cache_dirty_ = false; return evaluated_lengths_cache_; From c77344384567b4aad2e39691b2423f34306c963f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 19 Sep 2021 22:08:11 -0500 Subject: [PATCH 0015/1500] Fix: Incorrect default values for the curve trim node The default end factor should be 1. The proper value for the default end length is somewhat arbitrary, but it shouldn't be zero. --- source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index a90a7665ec7..2b6d25b6bf3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -30,9 +30,9 @@ static void geo_node_curve_trim_declare(NodeDeclarationBuilder &b) { b.add_input("Curve"); b.add_input("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("End").min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("End").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); b.add_input("Start", "Start_001").min(0.0f).subtype(PROP_DISTANCE); - b.add_input("End", "End_001").min(0.0f).subtype(PROP_DISTANCE); + b.add_input("End", "End_001").min(0.0f).default_value(1.0f).subtype(PROP_DISTANCE); b.add_output("Curve"); } From c5c8c68eec93e57ed46be4371d8831e2f0fe3fe2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 20 Sep 2021 16:42:07 +1000 Subject: [PATCH 0016/1500] Cleanup: spelling --- intern/ghost/intern/GHOST_DisplayManagerSDL.cpp | 2 +- source/blender/blenlib/BLI_uuid.h | 4 ++-- source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp index 18adf948e3b..5b026eb1632 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp +++ b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp @@ -106,7 +106,7 @@ GHOST_TSuccess GHOST_DisplayManagerSDL::setCurrentDisplaySetting( * ftp://ftp.idsoftware.com/idstuff/source/q2source-3.21.zip * See linux/gl_glx.c:GLimp_SetMode * http://wiki.bzflag.org/BZFlag_Source - * See src/platform/SDLDisplay.cxx:SDLDisplay and createWindow + * See: `src/platform/SDLDisplay.cxx:SDLDisplay` and `createWindow`. */ SDL_DisplayMode mode; const int num_modes = SDL_GetNumDisplayModes(display); diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 15350849f67..5440e9426bf 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -37,7 +37,7 @@ extern "C" { * This function is not thread-safe. */ UUID BLI_uuid_generate_random(void); -/** Compare two UUIDs, return true iff they are equal. */ +/** Compare two UUIDs, return true if they are equal. */ bool BLI_uuid_equal(UUID uuid1, UUID uuid2); /** @@ -48,7 +48,7 @@ void BLI_uuid_format(char *buffer, UUID uuid) ATTR_NONNULL(); /** * Parse a string as UUID. - * The string MUST be in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, + * The string MUST be in the format `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, * as produced by #BLI_uuid_format(). * * Return true if the string could be parsed, and false otherwise. In the latter case, the UUID may diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index f46440fd949..b8bdb3d71d6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -339,7 +339,7 @@ struct ResultAttributes { /** * Result attributes corresponding the attributes on the profile input, in the same order. The - * attributes are optional in case the attribute names correspond to a namse used by the curve + * attributes are optional in case the attribute names correspond to a names used by the curve * input, in which case the curve input attributes take precedence. */ Vector> profile_point_attributes; From 738f1dbeff7fcfa9431bf4fcc132b54fd24f5e3b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 20 Sep 2021 19:00:16 +1000 Subject: [PATCH 0017/1500] UI: rename "Save Screenshot (Area => Editor)" The term "area" isn't normally exposed in the UI. --- source/blender/editors/screen/screendump.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 8056e02d17a..bc1c15abb96 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -270,9 +270,10 @@ void SCREEN_OT_screenshot(wmOperatorType *ot) void SCREEN_OT_screenshot_area(wmOperatorType *ot) { - ot->name = "Save Screenshot (Area)"; + /* NOTE: the term "area" is a Blender internal name, "Editor" makes more sense for the UI. */ + ot->name = "Save Screenshot (Editor)"; ot->idname = "SCREEN_OT_screenshot_area"; - ot->description = "Capture a picture of the active area"; + ot->description = "Capture a picture of an editor"; screen_screenshot_impl(ot); From 1f51672d7126d8c0a0b962060c01632e6e07dd5f Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 20 Sep 2021 11:10:31 +0200 Subject: [PATCH 0018/1500] Fix T91511: GPencil weight_get and Vertex Groups not working at expected The API was checking the number of total weights with the first point of the stroke and this was not valid because each point can have different number of weight elemnts, --- source/blender/makesrna/intern/rna_gpencil.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index f06c8a5325c..2bce5f3f4b3 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -843,17 +843,17 @@ static float rna_GPencilStrokePoints_weight_get(bGPDstroke *stroke, return -1.0f; } - if (dvert->totweight <= vertex_group_index || vertex_group_index < 0) { - BKE_report(reports, RPT_ERROR, "Groups: index out of range"); - return -1.0f; - } - if (stroke->totpoints <= point_index || point_index < 0) { BKE_report(reports, RPT_ERROR, "GPencilStrokePoints: index out of range"); return -1.0f; } MDeformVert *pt_dvert = stroke->dvert + point_index; + if ((pt_dvert) && (pt_dvert->totweight <= vertex_group_index || vertex_group_index < 0)) { + BKE_report(reports, RPT_ERROR, "Groups: index out of range"); + return -1.0f; + } + MDeformWeight *dw = BKE_defvert_find_index(pt_dvert, vertex_group_index); if (dw) { return dw->weight; From 4eba920d15183e73ab8c2c9986b621aec4cbeca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 20 Sep 2021 12:01:59 +0200 Subject: [PATCH 0019/1500] UUID: include 'seconds' field of current time in RNG seed XOR the 'seconds' and 'nanoseconds' fields of the current time to seed the RNG used for generating random UUIDs. This ensures a better seed just in case the clock as no sub-second resolution. --- source/blender/blenlib/intern/uuid.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index 0c381e5dcd2..247928893f3 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -40,7 +40,10 @@ UUID BLI_uuid_generate_random() struct timespec ts; timespec_get(&ts, TIME_UTC); - rng.seed(ts.tv_nsec); + /* XOR the nanosecond and second fields, just in case the clock only has seconds resolution. */ + uint64_t seed = ts.tv_nsec; + seed ^= ts.tv_sec; + rng.seed(seed); return rng; }(); From 1e3c5fdb85cd27e37c658bf5f9277065d3f7ba13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 20 Sep 2021 12:09:03 +0200 Subject: [PATCH 0020/1500] Cleanup: UUID, prevent "missing braces" warning on macOS Add braces around initialization of sub-objects, as per the warning suggestion on macOS. No functional changes. --- source/blender/blenlib/tests/BLI_uuid_test.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc index 2c9da920897..bb5eb3817af 100644 --- a/source/blender/blenlib/tests/BLI_uuid_test.cc +++ b/source/blender/blenlib/tests/BLI_uuid_test.cc @@ -78,13 +78,13 @@ TEST(BLI_uuid, string_formatting) EXPECT_EQ("00000001-0002-0003-0405-060000000007", buffer); /* Somewhat more complex bit patterns. This is a version 1 UUID generated from Python. */ - const UUID uuid1 = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}; + const UUID uuid1 = {3540651616, 5282, 4588, 139, 153, {0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}}; BLI_uuid_format(buffer.data(), uuid1); EXPECT_EQ("d30a0e60-14a2-11ec-8b99-f7736944db8b", buffer); /* Namespace UUID, example listed in RFC4211. */ const UUID namespace_dns = { - 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}; + 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, {0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}}; BLI_uuid_format(buffer.data(), namespace_dns); EXPECT_EQ("6ba7b810-9dad-11d1-80b4-00c04fd430c8", buffer); } @@ -126,7 +126,7 @@ TEST(BLI_uuid, string_parsing_fail) TEST(BLI_uuid, stream_operator) { std::stringstream ss; - const UUID uuid = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}; + const UUID uuid = {3540651616, 5282, 4588, 139, 153, {0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}}; ss << uuid; EXPECT_EQ(ss.str(), "d30a0e60-14a2-11ec-8b99-f7736944db8b"); } From 07b482c2ffdf88c6930b621bb0b18c3a5d0c0130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 20 Sep 2021 12:10:19 +0200 Subject: [PATCH 0021/1500] UUID: fix seeding the RNG clock on macOS On Apple machines, call `clock_gettime()` instead of `timespec_get()`. macOS only introduced `timespec_get()` in version 10.15 (introduced approx two years ago, so in 2019), even though the function is from C11. --- source/blender/blenlib/intern/uuid.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index 247928893f3..5d4f7e1a520 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -39,7 +39,16 @@ UUID BLI_uuid_generate_random() static_assert(std::mt19937_64::max() == 0xffffffffffffffffLL); struct timespec ts; +#ifdef __APPLE__ + /* `timespec_get()` is only available on macOS 10.15+, so until that's the minimum version + * supported by Blender, use another function to get the timespec. + * + * `clock_gettime()` is only available on POSIX, so not on Windows; Linux uses the newer C++11 + * function `timespec_get()` as well. */ + clock_gettime(CLOCK_REALTIME, &ts); +#else timespec_get(&ts, TIME_UTC); +#endif /* XOR the nanosecond and second fields, just in case the clock only has seconds resolution. */ uint64_t seed = ts.tv_nsec; seed ^= ts.tv_sec; From 029d042e8518c53dffe2471d113d5daf4acf97d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 20 Sep 2021 12:15:37 +0200 Subject: [PATCH 0022/1500] UUID: add nil value for UUIDs Add `BLI_uuid_nil()` that returns the nil UUID (used to indicate "not set") and `BLI_uuid_is_nil(uuid)` to do an equality test with the nil value. --- source/blender/blenlib/BLI_uuid.h | 8 ++++++++ source/blender/blenlib/intern/uuid.cc | 11 +++++++++++ source/blender/blenlib/tests/BLI_uuid_test.cc | 13 +++++++++++++ 3 files changed, 32 insertions(+) diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 5440e9426bf..15913cc1017 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -37,6 +37,14 @@ extern "C" { * This function is not thread-safe. */ UUID BLI_uuid_generate_random(void); +/** + * Return the UUID nil value, consisting of all-zero fields. + */ +UUID BLI_uuid_nil(void); + +/** Return true iff this is the nil UUID. */ +bool BLI_uuid_is_nil(UUID uuid); + /** Compare two UUIDs, return true if they are equal. */ bool BLI_uuid_equal(UUID uuid1, UUID uuid2); diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index 5d4f7e1a520..f5edb356acc 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -78,6 +78,17 @@ UUID BLI_uuid_generate_random() return uuid; } +UUID BLI_uuid_nil(void) +{ + const UUID nil = {0, 0, 0, 0, 0, 0}; + return nil; +} + +bool BLI_uuid_is_nil(UUID uuid) +{ + return BLI_uuid_equal(BLI_uuid_nil(), uuid); +} + bool BLI_uuid_equal(const UUID uuid1, const UUID uuid2) { return std::memcmp(&uuid1, &uuid2, sizeof(uuid1)) == 0; diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc index bb5eb3817af..31c69002c1c 100644 --- a/source/blender/blenlib/tests/BLI_uuid_test.cc +++ b/source/blender/blenlib/tests/BLI_uuid_test.cc @@ -48,6 +48,19 @@ TEST(BLI_uuid, generate_many_random) } } +TEST(BLI_uuid, nil_value) +{ + const UUID nil_uuid = BLI_uuid_nil(); + const UUID zeroes_uuid = {0, 0, 0, 0, 0, 0}; + + EXPECT_TRUE(BLI_uuid_equal(nil_uuid, zeroes_uuid)); + EXPECT_TRUE(BLI_uuid_is_nil(nil_uuid)); + + std::string buffer(36, '\0'); + BLI_uuid_format(buffer.data(), nil_uuid); + EXPECT_EQ("00000000-0000-0000-0000-000000000000", buffer); +} + TEST(BLI_uuid, equality) { const UUID uuid1 = BLI_uuid_generate_random(); From 7da9da2b27ddddedb93f9390fd1b857fe736b00c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 20 Sep 2021 12:37:52 +0200 Subject: [PATCH 0023/1500] Geometry Nodes: new Realize Instances node This node has a simple geometry input and output. If the input geometry contains instances, they will be realized into actual geometry. When there are many instances, this can be very slow and memory intensive. Generally, instances should only be made real when necessary, e.g. when every instance should be deformed independently. Differential Revision: https://developer.blender.org/D12556 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_geo_realize_instances.cc | 48 +++++++++++++++++++ 7 files changed, 54 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index ecf07edb9f3..b4619c2c949 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -546,6 +546,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeJoinGeometry"), NodeItem("GeometryNodeSeparateComponents"), NodeItem("GeometryNodeSetPosition", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeRealizeInstances", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_INPUT", "Input", items=[ NodeItem("GeometryNodeObjectInfo"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a57281e4478..8e82ab6d6be 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1493,6 +1493,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ATTRIBUTE_CAPTURE 1080 #define GEO_NODE_MATERIAL_SELECTION 1081 #define GEO_NODE_MATERIAL_ASSIGN 1082 +#define GEO_NODE_REALIZE_INSTANCES 1083 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index b96e98a58ec..3a76cbf6f84 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5229,6 +5229,7 @@ static void registerGeometryNodes() register_node_type_geo_point_translate(); register_node_type_geo_points_to_volume(); register_node_type_geo_raycast(); + register_node_type_geo_realize_instances(); register_node_type_geo_sample_texture(); register_node_type_geo_select_by_handle_type(); register_node_type_geo_material_selection(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index b741461f820..b0fc55fab0c 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -218,6 +218,7 @@ set(SRC geometry/nodes/node_geo_point_translate.cc geometry/nodes/node_geo_points_to_volume.cc geometry/nodes/node_geo_raycast.cc + geometry/nodes/node_geo_realize_instances.cc geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_set_position.cc geometry/nodes/node_geo_subdivision_surface.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index a713da45f0b..0d31ae2143a 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -103,6 +103,7 @@ void register_node_type_geo_point_separate(void); void register_node_type_geo_point_translate(void); void register_node_type_geo_points_to_volume(void); void register_node_type_geo_raycast(void); +void register_node_type_geo_realize_instances(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_separate_components(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 51d59821d3c..b2f1fa5e83a 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -345,6 +345,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRI DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") +DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc new file mode 100644 index 00000000000..3be79d5ba3b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_realize_instances_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_output("Geometry"); +} + +static void geo_node_realize_instances_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_realize_instances() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_REALIZE_INSTANCES, "Realize Instances", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_realize_instances_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_realize_instances_exec; + nodeRegisterType(&ntype); +} From 11e11c41f2695f6f412079d438b675516aa8151a Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 20 Sep 2021 12:40:51 +0200 Subject: [PATCH 0024/1500] make_update: Fix case where a sub-module would not have required branch. Issue revealed by rB546314fc9669 change, also error itself exited before that commit. Now we do accept git command to fail when trying to checkout the specified branch from sub-modules, and only actually error in case the fall-back branch (aka master) cannot be properly checked out. Thanks fot Ray molenkamp (@LazyDodo) for report and initial patch (D12560). --- build_files/utils/make_update.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build_files/utils/make_update.py b/build_files/utils/make_update.py index a6399790bc9..f653639ec6d 100755 --- a/build_files/utils/make_update.py +++ b/build_files/utils/make_update.py @@ -200,13 +200,14 @@ def submodules_update(args, release_version, branch): if msg: skip_msg += submodule_path + " skipped: " + msg + "\n" else: + # We are using `exit_on_error=False` here because sub-modules are allowed to not have requested branch, + # in which case falling back to default back-up branch is fine. if make_utils.git_branch(args.git_command) != submodule_branch: call([args.git_command, "fetch", "origin"]) - call([args.git_command, "checkout", submodule_branch]) - call([args.git_command, "pull", "--rebase", "origin", submodule_branch]) + call([args.git_command, "checkout", submodule_branch], exit_on_error=False) + call([args.git_command, "pull", "--rebase", "origin", submodule_branch], exit_on_error=False) # If we cannot find the specified branch for this submodule, fallback to default one (aka master). if make_utils.git_branch(args.git_command) != submodule_branch: - call([args.git_command, "fetch", "origin"]) call([args.git_command, "checkout", submodule_branch_fallback]) call([args.git_command, "pull", "--rebase", "origin", submodule_branch_fallback]) finally: From 8c7c4549d1fba8eb2236fa397d95b32ad1262789 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 20 Sep 2021 12:49:11 +0200 Subject: [PATCH 0025/1500] Geometry Nodes: support Set Position node on instances Previously, the node would always realize instances implicitly. Now it can change the position of entire instances. The Realize Instances node can be used before if the old behavior is required. Differential Revision: https://developer.blender.org/D12555 --- source/blender/blenkernel/BKE_geometry_set.hh | 5 ++ .../intern/geometry_component_instances.cc | 83 +++++++++++++++++++ .../geometry/nodes/node_geo_set_position.cc | 7 +- 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index bf38294257a..98f5de43f84 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -591,12 +591,17 @@ class InstancesComponent : public GeometryComponent { blender::Span almost_unique_ids() const; + int attribute_domain_size(const AttributeDomain domain) const final; + bool is_empty() const final; bool owns_direct_data() const override; void ensure_owns_direct_data() override; static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES; + + private: + const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; }; /** A geometry component that stores volume grids. */ diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 26ef827d36d..c4e1fe2f8e9 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -25,6 +25,8 @@ #include "BKE_geometry_set.hh" +#include "attribute_access_intern.hh" + using blender::float4x4; using blender::Map; using blender::MutableSpan; @@ -225,4 +227,85 @@ blender::Span InstancesComponent::almost_unique_ids() const return almost_unique_ids_; } +int InstancesComponent::attribute_domain_size(const AttributeDomain domain) const +{ + if (domain != ATTR_DOMAIN_POINT) { + return 0; + } + return this->instances_amount(); +} + +namespace blender::bke { + +static float3 get_transform_position(const float4x4 &transform) +{ + return transform.translation(); +} + +static void set_transform_position(float4x4 &transform, const float3 position) +{ + copy_v3_v3(transform.values[3], position); +} + +class InstancePositionAttributeProvider final : public BuiltinAttributeProvider { + public: + InstancePositionAttributeProvider() + : BuiltinAttributeProvider( + "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, NonCreatable, Writable, NonDeletable) + { + } + + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final + { + const InstancesComponent &instances_component = static_cast( + component); + Span transforms = instances_component.instance_transforms(); + return std::make_unique>( + transforms); + } + + GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + { + InstancesComponent &instances_component = static_cast(component); + MutableSpan transforms = instances_component.instance_transforms(); + return std::make_unique>( + transforms); + } + + bool try_delete(GeometryComponent &UNUSED(component)) const final + { + return false; + } + + bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const final + { + return false; + } + + bool exists(const GeometryComponent &UNUSED(component)) const final + { + return true; + } +}; + +static ComponentAttributeProviders create_attribute_providers_for_instances() +{ + static InstancePositionAttributeProvider position; + + return ComponentAttributeProviders({&position}, {}); +} +} // namespace blender::bke + +const blender::bke::ComponentAttributeProviders *InstancesComponent::get_attribute_providers() + const +{ + static blender::bke::ComponentAttributeProviders providers = + blender::bke::create_attribute_providers_for_instances(); + return &providers; +} + /** \} */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 4c754ddb643..832db76e731 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -51,12 +51,13 @@ static void set_position_in_component(GeometryComponent &component, static void geo_node_set_position_exec(GeoNodeExecParams params) { GeometrySet geometry = params.extract_input("Geometry"); - geometry = geometry_set_realize_instances(geometry); Field selection_field = params.extract_input>("Selection"); Field position_field = params.extract_input>("Position"); - for (const GeometryComponentType type : - {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) { + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}) { if (geometry.has(type)) { set_position_in_component( geometry.get_component_for_write(type), selection_field, position_field); From fc4f82d2004c843691d81afff0f7abb38caace0a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 20 Sep 2021 13:12:25 +0200 Subject: [PATCH 0026/1500] Geometry Nodes: support Noise Texture node This makes the Noise Texture node available in geometry nodes. It should behave the same as in shader node, with the exception that it does not have an implicit position input yet. That will be added separately. Differential Revision: https://developer.blender.org/D12467 --- release/scripts/startup/nodeitems_builtins.py | 3 + .../functions/FN_multi_function_params.hh | 16 ++ .../shader/nodes/node_shader_tex_noise.cc | 159 +++++++++++++++++- 3 files changed, 177 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index b4619c2c949..aea9cbc5c62 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -604,6 +604,9 @@ geometry_node_categories = [ NodeItem("FunctionNodeFloatToInt"), NodeItem("GeometryNodeSwitch"), ]), + GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ + NodeItem("ShaderNodeTexNoise", poll=geometry_nodes_fields_poll), + ]), GeometryNodeCategory("GEO_VECTOR", "Vector", items=[ NodeItem("ShaderNodeVectorCurve"), NodeItem("ShaderNodeSeparateXYZ"), diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index fe4d2b90d80..d187985de9d 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -272,6 +272,22 @@ class MFParams { return span; } + /** + * Same as #uninitialized_single_output, but returns an empty span when the output is not + * required. + */ + template + MutableSpan uninitialized_single_output_if_required(int param_index, StringRef name = "") + { + return this->uninitialized_single_output_if_required(param_index, name).typed(); + } + GMutableSpan uninitialized_single_output_if_required(int param_index, StringRef name = "") + { + this->assert_correct_param(param_index, name, MFParamType::SingleOutput); + int data_index = builder_->signature_->data_index(param_index); + return builder_->mutable_spans_[data_index]; + } + template const VVectorArray &readonly_vector_input(int param_index, StringRef name = "") { diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index de8e0916f4d..c0deb232b2d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -19,6 +19,8 @@ #include "../node_shader_util.h" +#include "BLI_noise.hh" + /* **************** NOISE ******************** */ static bNodeSocketTemplate sh_node_tex_noise_in[] = { @@ -90,18 +92,173 @@ static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4); } +namespace blender::nodes { + +class NoiseFunction : public fn::MultiFunction { + private: + int dimensions_; + + public: + NoiseFunction(int dimensions) : dimensions_(dimensions) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + static std::array signatures{ + create_signature(1), + create_signature(2), + create_signature(3), + create_signature(4), + }; + this->set_signature(&signatures[dimensions - 1]); + } + + static fn::MFSignature create_signature(int dimensions) + { + fn::MFSignatureBuilder signature{"Noise"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + + signature.single_input("Scale"); + signature.single_input("Detail"); + signature.single_input("Roughness"); + signature.single_input("Distortion"); + + signature.single_output("Fac"); + signature.single_output("Color"); + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4); + const VArray &scale = params.readonly_single_input(param++, "Scale"); + const VArray &detail = params.readonly_single_input(param++, "Detail"); + const VArray &roughness = params.readonly_single_input(param++, "Roughness"); + const VArray &distortion = params.readonly_single_input(param++, "Distortion"); + + MutableSpan r_factor = params.uninitialized_single_output_if_required(param++, + "Fac"); + MutableSpan r_color = + params.uninitialized_single_output_if_required(param++, "Color"); + + const bool compute_factor = !r_factor.is_empty(); + const bool compute_color = !r_color.is_empty(); + + switch (dimensions_) { + case 1: { + const VArray &w = params.readonly_single_input(0, "W"); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::perlin_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + } + } + if (compute_color) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + const float3 c = noise::perlin_float3_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + break; + } + case 2: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + if (compute_factor) { + for (int64_t i : mask) { + const float2 position = vector[i] * scale[i]; + r_factor[i] = noise::perlin_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + } + } + if (compute_color) { + for (int64_t i : mask) { + const float2 position = vector[i] * scale[i]; + const float3 c = noise::perlin_float3_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + break; + } + case 3: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::perlin_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + } + } + if (compute_color) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + const float3 c = noise::perlin_float3_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + break; + } + case 4: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &w = params.readonly_single_input(1, "W"); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position_vector = vector[i] * scale[i]; + const float position_w = w[i] * scale[i]; + const float4 position{ + position_vector[0], position_vector[1], position_vector[2], position_w}; + r_factor[i] = noise::perlin_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + } + } + if (compute_color) { + for (int64_t i : mask) { + const float3 position_vector = vector[i] * scale[i]; + const float position_w = w[i] * scale[i]; + const float4 position{ + position_vector[0], position_vector[1], position_vector[2], position_w}; + const float3 c = noise::perlin_float3_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + break; + } + } + } +}; + +static void sh_node_noise_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexNoise *tex = (NodeTexNoise *)node.storage; + builder.construct_and_set_matching_fn(tex->dimensions); +} + +} // namespace blender::nodes + /* node type definition */ void register_node_type_sh_tex_noise(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0); node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out); node_type_init(&ntype, node_shader_init_tex_noise); node_type_storage( &ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_noise); node_type_update(&ntype, node_shader_update_tex_noise); + ntype.build_multi_function = blender::nodes::sh_node_noise_build_multi_function; nodeRegisterType(&ntype); } From da4796ebf7ea728dc81ff4b3c62410230520977a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 20 Sep 2021 13:17:01 +0200 Subject: [PATCH 0027/1500] Build: change make update to not print errors regarding submodule branches Instead of trying to checkout non-existent branches and getting confusing fatal error prints, check if the branch exists first. Ref D12560 --- build_files/utils/make_update.py | 24 ++++++++++++++---------- build_files/utils/make_utils.py | 16 +++++++++++++--- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/build_files/utils/make_update.py b/build_files/utils/make_update.py index f653639ec6d..30ef090efbb 100755 --- a/build_files/utils/make_update.py +++ b/build_files/utils/make_update.py @@ -200,16 +200,20 @@ def submodules_update(args, release_version, branch): if msg: skip_msg += submodule_path + " skipped: " + msg + "\n" else: - # We are using `exit_on_error=False` here because sub-modules are allowed to not have requested branch, - # in which case falling back to default back-up branch is fine. - if make_utils.git_branch(args.git_command) != submodule_branch: - call([args.git_command, "fetch", "origin"]) - call([args.git_command, "checkout", submodule_branch], exit_on_error=False) - call([args.git_command, "pull", "--rebase", "origin", submodule_branch], exit_on_error=False) - # If we cannot find the specified branch for this submodule, fallback to default one (aka master). - if make_utils.git_branch(args.git_command) != submodule_branch: - call([args.git_command, "checkout", submodule_branch_fallback]) - call([args.git_command, "pull", "--rebase", "origin", submodule_branch_fallback]) + # Find a matching branch that exists. + call([args.git_command, "fetch", "origin"]) + if make_utils.git_branch_exists(args.git_command, submodule_branch): + pass + elif make_utils.git_branch_exists(args.git_command, submodule_branch_fallback): + submodule_branch = submodule_branch_fallback + else: + submodule_branch = None + + # Switch to branch and pull. + if submodule_branch: + if make_utils.git_branch(args.git_command) != submodule_branch: + call([args.git_command, "checkout", submodule_branch]) + call([args.git_command, "pull", "--rebase", "origin", submodule_branch]) finally: os.chdir(cwd) diff --git a/build_files/utils/make_utils.py b/build_files/utils/make_utils.py index db352ff7e16..9def0059ceb 100755 --- a/build_files/utils/make_utils.py +++ b/build_files/utils/make_utils.py @@ -8,14 +8,19 @@ import subprocess import sys -def call(cmd, exit_on_error=True): - print(" ".join(cmd)) +def call(cmd, exit_on_error=True, silent=False): + if not silent: + print(" ".join(cmd)) # Flush to ensure correct order output on Windows. sys.stdout.flush() sys.stderr.flush() - retcode = subprocess.call(cmd) + if silent: + retcode = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + else: + retcode = subprocess.call(cmd) + if exit_on_error and retcode != 0: sys.exit(retcode) return retcode @@ -38,6 +43,11 @@ def check_output(cmd, exit_on_error=True): return output.strip() +def git_branch_exists(git_command, branch): + return call([git_command, "rev-parse", "--verify", branch], exit_on_error=False, silent=True) == 0 or \ + call([git_command, "rev-parse", "--verify", "remotes/origin/" + branch], exit_on_error=False, silent=True) == 0 + + def git_branch(git_command): # Get current branch name. try: From eaad219ee7127fdeba08cef92f1d6cf4279a30af Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 20 Sep 2021 22:27:51 +1000 Subject: [PATCH 0028/1500] Cleanup: spelling (correct c5c8c68eec93e57ed46be4371d8831e2f0fe3fe2) "iff" was intended as "if and only if". while exact use of abbreviations isn't clear cut, I assumed this was a typo & it's not used anywhere else in source/, expand to "only if" (suggested by Sybren). --- source/blender/blenlib/BLI_uuid.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 15913cc1017..1ce294ed723 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -42,10 +42,10 @@ UUID BLI_uuid_generate_random(void); */ UUID BLI_uuid_nil(void); -/** Return true iff this is the nil UUID. */ +/** Return true only if this is the nil UUID. */ bool BLI_uuid_is_nil(UUID uuid); -/** Compare two UUIDs, return true if they are equal. */ +/** Compare two UUIDs, return true only if they are equal. */ bool BLI_uuid_equal(UUID uuid1, UUID uuid2); /** From c5c189a158a330ba2370b4f78605207f14318c0c Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 20 Sep 2021 15:32:18 +0200 Subject: [PATCH 0029/1500] GPencil: Change Rotation tooltip The tooltip was not clear about in what shading modes works. Related to T91467 --- source/blender/makesrna/intern/rna_material.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 8d0d3adab8b..4078691444b 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -604,8 +604,10 @@ static void rna_def_material_greasepencil(BlenderRNA *brna) RNA_def_property_float_default(prop, 0.0f); RNA_def_property_range(prop, -DEG2RADF(90.0f), DEG2RADF(90.0f)); RNA_def_property_ui_range(prop, -DEG2RADF(90.0f), DEG2RADF(90.0f), 10, 3); - RNA_def_property_ui_text( - prop, "Rotation", "Additional rotation applied to dots and square strokes"); + RNA_def_property_ui_text(prop, + "Rotation", + "Additional rotation applied to dots and square texture of strokes. " + "Only valid in texture shading mode"); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); /* pass index for future compositing and editing tools */ From 9642447faf1054664cf68b302a563c0a1145b7d7 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 20 Sep 2021 15:37:54 +0200 Subject: [PATCH 0030/1500] GPencil: Fix error in previous commit By error I commited the previous version. --- source/blender/makesrna/intern/rna_material.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 4078691444b..22a75c0d992 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -607,7 +607,7 @@ static void rna_def_material_greasepencil(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Rotation", "Additional rotation applied to dots and square texture of strokes. " - "Only valid in texture shading mode"); + "Only applies in texture shading mode"); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); /* pass index for future compositing and editing tools */ From 7cb65e45814db6559ffa48c26b3d000e0f78c4bb Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 20 Sep 2021 16:21:40 +0200 Subject: [PATCH 0031/1500] Cleanup: Refactor VSE overlay settings Move overlay flags into SequencerPreviewOverlay and SequencerTimelineOverlay structs. Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D12569 --- .../scripts/startup/bl_ui/space_sequencer.py | 45 ++-- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_260.c | 2 +- .../blenloader/intern/versioning_280.c | 2 +- .../blenloader/intern/versioning_290.c | 4 +- .../blenloader/intern/versioning_300.c | 53 +++-- .../blenloader/intern/versioning_defaults.c | 8 +- .../blender/editors/gpencil/annotate_paint.c | 2 +- source/blender/editors/render/render_opengl.c | 2 +- .../editors/space_sequencer/sequencer_draw.c | 47 ++-- .../editors/space_sequencer/space_sequencer.c | 9 +- source/blender/makesdna/DNA_space_types.h | 58 +++-- source/blender/makesrna/intern/rna_space.c | 212 +++++++++++------- 13 files changed, 279 insertions(+), 167 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 258797c18da..88cf8db686c 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -205,13 +205,14 @@ class SEQUENCER_PT_preview_overlay(Panel): def draw(self, context): ed = context.scene.sequence_editor st = context.space_data + overlay_settings = st.preview_overlay layout = self.layout layout.active = st.show_strip_overlay layout.prop(ed, "show_overlay", text="Frame Overlay") - layout.prop(st, "show_safe_areas", text="Safe Areas") - layout.prop(st, "show_metadata", text="Metadata") - layout.prop(st, "show_annotation", text="Annotations") + layout.prop(overlay_settings, "show_safe_areas", text="Safe Areas") + layout.prop(overlay_settings, "show_metadata", text="Metadata") + layout.prop(overlay_settings, "show_annotation", text="Annotations") class SEQUENCER_PT_sequencer_overlay(Panel): @@ -227,23 +228,24 @@ class SEQUENCER_PT_sequencer_overlay(Panel): def draw(self, context): st = context.space_data + overlay_settings = st.timeline_overlay layout = self.layout layout.active = st.show_strip_overlay - layout.prop(st, "show_strip_name", text="Name") - layout.prop(st, "show_strip_source", text="Source") - layout.prop(st, "show_strip_duration", text="Duration") + layout.prop(overlay_settings, "show_strip_name", text="Name") + layout.prop(overlay_settings, "show_strip_source", text="Source") + layout.prop(overlay_settings, "show_strip_duration", text="Duration") layout.separator() - layout.prop(st, "show_strip_offset", text="Offsets") - layout.prop(st, "show_fcurves", text="F-Curves") - layout.prop(st, "show_grid", text="Grid") + layout.prop(overlay_settings, "show_strip_offset", text="Offsets") + layout.prop(overlay_settings, "show_fcurves", text="F-Curves") + layout.prop(overlay_settings, "show_grid", text="Grid") layout.separator() - layout.prop_menu_enum(st, "waveform_display_type") + layout.prop_menu_enum(overlay_settings, "waveform_display_type") class SEQUENCER_MT_view_cache(Menu): @@ -1652,6 +1654,7 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): layout.use_property_split = False st = context.space_data + overlay_settings = st.timeline_overlay strip = context.active_sequence_strip sound = strip.sound @@ -1663,7 +1666,7 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): split = col.split(factor=0.4) split.label(text="") split.prop(sound, "use_mono") - if st.waveform_display_type == 'DEFAULT_WAVEFORMS': + if overlay_settings.waveform_display_type == 'DEFAULT_WAVEFORMS': split = col.split(factor=0.4) split.label(text="") split.prop(strip, "show_waveform") @@ -2090,17 +2093,16 @@ class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel): return is_preview and (st.display_mode == 'IMAGE') def draw_header(self, context): - st = context.space_data - - self.layout.prop(st, "show_safe_areas", text="") + overlay_settings = context.space_data.preview_overlay + self.layout.prop(overlay_settings, "show_safe_areas", text="") def draw(self, context): layout = self.layout layout.use_property_split = True - st = context.space_data + overlay_settings = context.space_data.preview_overlay safe_data = context.scene.safe_areas - layout.active = st.show_safe_areas + layout.active = overlay_settings.show_safe_areas col = layout.column() @@ -2116,19 +2118,18 @@ class SEQUENCER_PT_view_safe_areas_center_cut(SequencerButtonsPanel_Output, Pane bl_category = "View" def draw_header(self, context): - st = context.space_data - layout = self.layout - layout.active = st.show_safe_areas - layout.prop(st, "show_safe_center", text="") + overlay_settings = context.space_data.preview_overlay + layout.active = overlay_settings.show_safe_areas + layout.prop(overlay_settings, "show_safe_center", text="") def draw(self, context): layout = self.layout layout.use_property_split = True safe_data = context.scene.safe_areas - st = context.space_data + overlay_settings = context.space_data.preview_overlay - layout.active = st.show_safe_areas and st.show_safe_center + layout.active = overlay_settings.show_safe_areas and overlay_settings.show_safe_center col = layout.column() col.prop(safe_data, "title_center", slider=True) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index d71cb559911..63d6b9121d2 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 22 +#define BLENDER_FILE_SUBVERSION 23 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index b71dd5a27bb..55252210a78 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -1800,7 +1800,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) } case SPACE_SEQ: { SpaceSeq *sseq = (SpaceSeq *)sl; - sseq->flag |= SEQ_SHOW_GPENCIL; + sseq->flag |= SEQ_PREVIEW_SHOW_GPENCIL; break; } case SPACE_IMAGE: { diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index bf0463432db..f667361d166 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -4966,7 +4966,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) { if (sl->spacetype == SPACE_SEQ) { SpaceSeq *sseq = (SpaceSeq *)sl; - sseq->flag |= SEQ_SHOW_FCURVES; + sseq->flag |= SEQ_TIMELINE_SHOW_FCURVES; } } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index f023813555f..bafba486c88 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1529,8 +1529,8 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { if (sl->spacetype == SPACE_SEQ) { SpaceSeq *sseq = (SpaceSeq *)sl; - sseq->flag |= (SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_SOURCE | - SEQ_SHOW_STRIP_DURATION); + sseq->flag |= (SEQ_SHOW_OVERLAY | SEQ_TIMELINE_SHOW_STRIP_NAME | + SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_DURATION); } } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 538634f4c9e..30e7c9bde4c 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1076,7 +1076,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { if (sl->spacetype == SPACE_SEQ) { SpaceSeq *sseq = (SpaceSeq *)sl; - sseq->flag |= SEQ_SHOW_GRID; + sseq->flag |= SEQ_TIMELINE_SHOW_GRID; } } } @@ -1250,18 +1250,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 300, 23)) { for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { @@ -1274,5 +1263,43 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + int seq_show_safe_margins = (sseq->flag & SEQ_PREVIEW_SHOW_SAFE_MARGINS); + int seq_show_gpencil = (sseq->flag & SEQ_PREVIEW_SHOW_GPENCIL); + int seq_show_fcurves = (sseq->flag & SEQ_TIMELINE_SHOW_FCURVES); + int seq_show_safe_center = (sseq->flag & SEQ_PREVIEW_SHOW_SAFE_CENTER); + int seq_show_metadata = (sseq->flag & SEQ_PREVIEW_SHOW_METADATA); + int seq_show_strip_name = (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_NAME); + int seq_show_strip_source = (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE); + int seq_show_strip_duration = (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_DURATION); + int seq_show_grid = (sseq->flag & SEQ_TIMELINE_SHOW_GRID); + int show_strip_offset = (sseq->draw_flag & SEQ_TIMELINE_SHOW_STRIP_OFFSETS); + sseq->preview_overlay.flag = (seq_show_safe_margins | seq_show_gpencil | + seq_show_safe_center | seq_show_metadata); + sseq->timeline_overlay.flag = (seq_show_fcurves | seq_show_strip_name | + seq_show_strip_source | seq_show_strip_duration | + seq_show_grid | show_strip_offset); + } + } + } + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index ae0dbb7f808..074cae669af 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -156,12 +156,10 @@ static void blo_update_defaults_screen(bScreen *screen, } else if (area->spacetype == SPACE_SEQ) { SpaceSeq *seq = area->spacedata.first; - seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | - SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_DURATION | - SEQ_SHOW_GRID; - + seq->flag |= SEQ_SHOW_MARKERS | SEQ_ZOOM_TO_FIT | SEQ_USE_PROXIES | SEQ_SHOW_OVERLAY; seq->render_size = SEQ_RENDER_SIZE_PROXY_100; - seq->flag |= SEQ_USE_PROXIES; + seq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_NAME | + SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID; } else if (area->spacetype == SPACE_TEXT) { /* Show syntax and line numbers in Script workspace text editor. */ diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 65c5b8ee573..68e2aece6e2 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1418,7 +1418,7 @@ static void annotation_visible_on_space(tGPsdata *p) } case SPACE_SEQ: { SpaceSeq *sseq = (SpaceSeq *)area->spacedata.first; - sseq->flag |= SEQ_SHOW_GPENCIL; + sseq->flag |= SEQ_PREVIEW_SHOW_GPENCIL; break; } case SPACE_IMAGE: { diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 834c023dde9..e4bbbfb0f57 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -303,7 +303,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R if (oglrender->is_sequencer) { SpaceSeq *sseq = oglrender->sseq; - struct bGPdata *gpd = (sseq && (sseq->flag & SEQ_SHOW_GPENCIL)) ? sseq->gpd : NULL; + struct bGPdata *gpd = (sseq && (sseq->flag & SEQ_PREVIEW_SHOW_GPENCIL)) ? sseq->gpd : NULL; /* use pre-calculated ImBuf (avoids deadlock), see: */ ImBuf *ibuf = oglrender->seq_data.ibufs_arr[oglrender->view_id]; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index bf817005a08..5b39feacfe3 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -327,7 +327,8 @@ static void draw_seq_waveform_overlay(View2D *v2d, float y2, float frames_per_pixel) { - if (seq->sound && ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { + if (seq->sound && + ((sseq->flag & SEQ_TIMELINE_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid * flickering when moving around the strip. * To do this we figure out the fractional offset in pixel space by checking where the @@ -876,12 +877,12 @@ static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq, const char *text_array[5]; int i = 0; - if (sseq->flag & SEQ_SHOW_STRIP_NAME) { + if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_NAME) { text_array[i++] = draw_seq_text_get_name(seq); } char source[FILE_MAX]; - if (sseq->flag & SEQ_SHOW_STRIP_SOURCE) { + if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE) { draw_seq_text_get_source(seq, source, sizeof(source)); if (source[0] != '\0') { if (i != 0) { @@ -892,7 +893,7 @@ static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq, } char strip_duration_text[16]; - if (sseq->flag & SEQ_SHOW_STRIP_DURATION) { + if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_DURATION) { const int strip_duration = seq->enddisp - seq->startdisp; SNPRINTF(strip_duration_text, "%d", strip_duration); if (i != 0) { @@ -1310,8 +1311,9 @@ static void draw_seq_strip(const bContext *C, float text_margin_y; bool y_threshold; - if ((sseq->flag & SEQ_SHOW_STRIP_NAME) || (sseq->flag & SEQ_SHOW_STRIP_SOURCE) || - (sseq->flag & SEQ_SHOW_STRIP_DURATION)) { + if ((sseq->flag & SEQ_TIMELINE_SHOW_STRIP_NAME) || + (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE) || + (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_DURATION)) { /* Calculate height needed for drawing text on strip. */ text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely); @@ -1335,9 +1337,10 @@ static void draw_seq_strip(const bContext *C, } /* Draw strip offsets when flag is enabled or during "solo preview". */ - if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { + if (sseq->flag & SEQ_SHOW_OVERLAY) { if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) { - if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) { + if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_OFFSETS) || + (seq == special_seq_update)) { draw_sequence_extensions_overlay(scene, seq, pos, pixely); } } @@ -1352,13 +1355,14 @@ static void draw_seq_strip(const bContext *C, drawmeta_contents(scene, seq, x1, y1, x2, y2); } - if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY) && (sseq->flag & SEQ_SHOW_FCURVES)) { + if ((sseq->flag & SEQ_SHOW_OVERLAY) && + (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_FCURVES)) { draw_seq_fcurve_overlay(scene, v2d, seq, x1, y1, x2, y2, pixelx); } /* Draw sound strip waveform. */ - if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) && - (sseq->flag & SEQ_NO_WAVEFORMS) == 0) { + if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_OVERLAY)) && + (sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) == 0) { draw_seq_waveform_overlay(v2d, C, sseq, @@ -1398,13 +1402,14 @@ static void draw_seq_strip(const bContext *C, /* If a waveform is drawn, avoid drawing text when there is not enough vertical space. */ if (seq->type == SEQ_TYPE_SOUND_RAM) { - if (!y_threshold && (sseq->flag & SEQ_NO_WAVEFORMS) == 0 && - ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { + if (!y_threshold && (sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) == 0 && + ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) || + (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { return; } } - if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { + if (sseq->flag & SEQ_SHOW_OVERLAY) { /* Don't draw strip if there is not enough vertical or horizontal space. */ if (((x2 - x1) > 32 * pixelx * U.dpi_fac) && ((y2 - y1) > 8 * pixely * U.dpi_fac)) { /* Depending on the vertical space, draw text on top or in the center of strip. */ @@ -1647,7 +1652,7 @@ static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, imm_draw_box_wire_2d(shdr_pos, x1 - 0.5f, y1 - 0.5f, x2 + 0.5f, y2 + 0.5f); /* Draw safety border. */ - if (sseq->flag & SEQ_SHOW_SAFE_MARGINS) { + if (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_SAFE_MARGINS) { immUniformThemeColorBlend(TH_VIEW_OVERLAY, TH_BACK, 0.25f); UI_draw_safe_areas(shdr_pos, @@ -1660,7 +1665,7 @@ static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, scene->safe_areas.title, scene->safe_areas.action); - if (sseq->flag & SEQ_SHOW_SAFE_CENTER) { + if (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_SAFE_CENTER) { UI_draw_safe_areas(shdr_pos, &(const rctf){ .xmin = x1, @@ -2067,7 +2072,7 @@ void sequencer_draw_preview(const bContext *C, struct ImBuf *scope = NULL; float viewrect[2]; const bool show_imbuf = ED_space_sequencer_check_show_imbuf(sseq); - const bool draw_gpencil = ((sseq->flag & SEQ_SHOW_GPENCIL) && sseq->gpd); + const bool draw_gpencil = ((sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_GPENCIL) && sseq->gpd); const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; sequencer_stop_running_jobs(C, scene); @@ -2118,16 +2123,16 @@ void sequencer_draw_preview(const bContext *C, C, scene, region, sseq, ibuf, scope, draw_overlay, draw_backdrop); /* Draw over image. */ - if (sseq->flag & SEQ_SHOW_METADATA && sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { + if (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_METADATA && sseq->flag & SEQ_SHOW_OVERLAY) { ED_region_image_metadata_draw(0.0, 0.0, ibuf, &v2d->tot, 1.0, 1.0); } } - if (show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + if (show_imbuf && (sseq->flag & SEQ_SHOW_OVERLAY)) { sequencer_draw_borders_overlay(sseq, v2d, scene); } - if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_OVERLAY)) { sequencer_draw_gpencil_overlay(C); } #if 0 @@ -2615,7 +2620,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region) /* Get timeline bound-box, needed for the scroll-bars. */ SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &v2d->tot); draw_seq_backdrop(v2d); - if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY) && (sseq->flag & SEQ_SHOW_GRID)) { + if ((sseq->flag & SEQ_SHOW_OVERLAY) && (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_GRID)) { U.v2d_min_gridsize *= 3; UI_view2d_draw_lines_x__discrete_frames_or_seconds( v2d, scene, (sseq->flag & SEQ_DRAWFRAMES) == 0, false); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 2a6e49edfb6..0d09f2564e8 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -98,9 +98,10 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce sseq->chanshown = 0; sseq->view = SEQ_VIEW_SEQUENCE; sseq->mainb = SEQ_DRAW_IMG_IMBUF; - sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | - SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME | - SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_DURATION | SEQ_SHOW_GRID; + sseq->flag = SEQ_PREVIEW_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | + SEQ_TIMELINE_SHOW_FCURVES | SEQ_ZOOM_TO_FIT | SEQ_SHOW_OVERLAY | + SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_SOURCE | + SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID; /* Tool header. */ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); @@ -699,7 +700,7 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && - (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)); + (sseq->flag & SEQ_SHOW_OVERLAY)); /* XXX temp fix for wrong setting in sseq->mainb */ if (sseq->mainb == SEQ_DRAW_SEQUENCE) { diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 5475e1bacd8..6505816256c 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -576,6 +576,36 @@ typedef enum eSpaceNla_Flag { /** \name Sequence Editor * \{ */ +typedef struct SequencerPreviewOverlay { + int flag; + char _pad0[4]; +} SequencerPreviewOverlay; + +/* SequencerPreviewOverlay.flag */ +typedef enum eSpaceSeq_SequencerPreviewOverlay_Flag { + SEQ_PREVIEW_SHOW_SAFE_MARGINS = (1 << 3), + SEQ_PREVIEW_SHOW_GPENCIL = (1 << 4), + SEQ_PREVIEW_SHOW_SAFE_CENTER = (1 << 9), + SEQ_PREVIEW_SHOW_METADATA = (1 << 10), +} eSpaceSeq_SequencerPreviewOverlay_Flag; + +typedef struct SequencerTimelineOverlay { + int flag; + char _pad0[4]; +} SequencerTimelineOverlay; + +/* SequencerTimelineOverlay.flag */ +typedef enum eSpaceSeq_SequencerTimelineOverlay_Flag { + SEQ_TIMELINE_SHOW_STRIP_OFFSETS = (1 << 1), + SEQ_TIMELINE_SHOW_FCURVES = (1 << 5), + SEQ_TIMELINE_ALL_WAVEFORMS = (1 << 7), /* draw all waveforms */ + SEQ_TIMELINE_NO_WAVEFORMS = (1 << 8), /* draw no waveforms */ + SEQ_TIMELINE_SHOW_STRIP_NAME = (1 << 14), + SEQ_TIMELINE_SHOW_STRIP_SOURCE = (1 << 15), + SEQ_TIMELINE_SHOW_STRIP_DURATION = (1 << 16), + SEQ_TIMELINE_SHOW_GRID = (1 << 18), +} eSpaceSeq_SequencerTimelineOverlay_Flag; + /* Sequencer */ typedef struct SpaceSeq { SpaceLink *next, *prev; @@ -612,10 +642,13 @@ typedef struct SpaceSeq { /** Different scoped displayed in space. */ struct SequencerScopes scopes; + struct SequencerPreviewOverlay preview_overlay; + struct SequencerTimelineOverlay timeline_overlay; /** Multiview current eye - for internal use. */ char multiview_eye; char _pad2[7]; + } SpaceSeq; /* SpaceSeq.mainb */ @@ -630,7 +663,7 @@ typedef enum eSpaceSeq_RegionType { /* SpaceSeq.draw_flag */ typedef enum eSpaceSeq_DrawFlag { SEQ_DRAW_BACKDROP = (1 << 0), - SEQ_DRAW_OFFSET_EXT = (1 << 1), + SEQ_DRAW_UNUSED_1 = (1 << 1), SEQ_DRAW_TRANSFORM_PREVIEW = (1 << 2), } eSpaceSeq_DrawFlag; @@ -639,22 +672,19 @@ typedef enum eSpaceSeq_Flag { SEQ_DRAWFRAMES = (1 << 0), SEQ_MARKER_TRANS = (1 << 1), SEQ_DRAW_COLOR_SEPARATED = (1 << 2), - SEQ_SHOW_SAFE_MARGINS = (1 << 3), - SEQ_SHOW_GPENCIL = (1 << 4), - SEQ_SHOW_FCURVES = (1 << 5), - SEQ_USE_ALPHA = (1 << 6), /* use RGBA display mode for preview */ - SEQ_ALL_WAVEFORMS = (1 << 7), /* draw all waveforms */ - SEQ_NO_WAVEFORMS = (1 << 8), /* draw no waveforms */ - SEQ_SHOW_SAFE_CENTER = (1 << 9), - SEQ_SHOW_METADATA = (1 << 10), + SPACE_SEQ_FLAG_UNUSED_3 = (1 << 3), + SPACE_SEQ_FLAG_UNUSED_4 = (1 << 4), + SPACE_SEQ_FLAG_UNUSED_5 = (1 << 5), + SEQ_USE_ALPHA = (1 << 6), /* use RGBA display mode for preview */ + SPACE_SEQ_FLAG_UNUSED_9 = (1 << 9), + SPACE_SEQ_FLAG_UNUSED_10 = (1 << 10), SEQ_SHOW_MARKERS = (1 << 11), /* show markers region */ SEQ_ZOOM_TO_FIT = (1 << 12), - SEQ_SHOW_STRIP_OVERLAY = (1 << 13), - SEQ_SHOW_STRIP_NAME = (1 << 14), - SEQ_SHOW_STRIP_SOURCE = (1 << 15), - SEQ_SHOW_STRIP_DURATION = (1 << 16), + SEQ_SHOW_OVERLAY = (1 << 13), + SPACE_SEQ_FLAG_UNUSED_14 = (1 << 14), + SPACE_SEQ_FLAG_UNUSED_15 = (1 << 15), + SPACE_SEQ_FLAG_UNUSED_16 = (1 << 16), SEQ_USE_PROXIES = (1 << 17), - SEQ_SHOW_GRID = (1 << 18), } eSpaceSeq_Flag; /* SpaceSeq.view */ diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index e3985b26eb0..8c331bd1911 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2326,6 +2326,16 @@ static void rna_Sequencer_view_type_update(Main *UNUSED(bmain), ED_area_tag_refresh(area); } +static char *rna_SpaceSequencerPreviewOverlay_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("preview_overlay"); +} + +static char *rna_SpaceSequencerTimelineOverlay_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("timeline_overlay"); +} + /* Space Node Editor */ static void rna_SpaceNodeEditor_node_tree_set(PointerRNA *ptr, @@ -5329,6 +5339,108 @@ static void rna_def_space_image(BlenderRNA *brna) rna_def_space_mask_info(srna, NC_SPACE | ND_SPACE_IMAGE, "rna_SpaceImageEditor_mask_set"); } +static void rna_def_space_sequencer_preview_overlay(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SequencerPreviewOverlay", NULL); + RNA_def_struct_sdna(srna, "SequencerPreviewOverlay"); + RNA_def_struct_nested(brna, srna, "SpaceSequenceEditor"); + RNA_def_struct_path_func(srna, "rna_SpaceSequencerPreviewOverlay_path"); + RNA_def_struct_ui_text(srna, "Preview Overlay Settings", ""); + + prop = RNA_def_property(srna, "show_safe_areas", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_SAFE_MARGINS); + RNA_def_property_ui_text( + prop, "Safe Areas", "Show TV title safe and action safe areas in preview"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_safe_center", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_SAFE_CENTER); + RNA_def_property_ui_text( + prop, "Center-Cut Safe Areas", "Show safe areas to fit content in a different aspect ratio"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_metadata", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_METADATA); + RNA_def_property_ui_text(prop, "Show Metadata", "Show metadata of first visible strip"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_GPENCIL); + RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); +} + +static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SequencerTimelineOverlay", NULL); + RNA_def_struct_sdna(srna, "SequencerTimelineOverlay"); + RNA_def_struct_nested(brna, srna, "SpaceSequenceEditor"); + RNA_def_struct_path_func(srna, "rna_SpaceSequencerTimelineOverlay_path"); + RNA_def_struct_ui_text(srna, "Timeline Overlay Settings", ""); + + static const EnumPropertyItem waveform_type_display_items[] = { + {SEQ_TIMELINE_NO_WAVEFORMS, + "NO_WAVEFORMS", + 0, + "Waveforms Off", + "Don't display waveforms for any sound strips"}, + {SEQ_TIMELINE_ALL_WAVEFORMS, + "ALL_WAVEFORMS", + 0, + "Waveforms On", + "Display waveforms for all sound strips"}, + {0, + "DEFAULT_WAVEFORMS", + 0, + "Use Strip Option", + "Display waveforms depending on strip setting"}, + {0, NULL, 0, NULL, NULL}, + }; + + prop = RNA_def_property(srna, "waveform_display_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, waveform_type_display_items); + RNA_def_property_ui_text(prop, "Waveform Display", "How Waveforms are displayed"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_fcurves", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_FCURVES); + RNA_def_property_ui_text(prop, "Show F-Curves", "Display strip opacity/volume curve"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_name", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_NAME); + RNA_def_property_ui_text(prop, "Show Name", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_source", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_SOURCE); + RNA_def_property_ui_text( + prop, "Show Source", "Display path to source file, or name of source datablock"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_duration", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_DURATION); + RNA_def_property_ui_text(prop, "Show Duration", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_grid", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_GRID); + RNA_def_property_ui_text(prop, "Show Grid", "Show vertical grid lines"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_offset", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_OFFSETS); + RNA_def_property_ui_text(prop, "Show Offsets", "Display strip in/out offsets"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); +} + static void rna_def_space_sequencer(BlenderRNA *brna) { StructRNA *srna; @@ -5369,25 +5481,6 @@ static void rna_def_space_sequencer(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem waveform_type_display_items[] = { - {SEQ_NO_WAVEFORMS, - "NO_WAVEFORMS", - 0, - "Waveforms Off", - "Don't display waveforms for any sound strips"}, - {SEQ_ALL_WAVEFORMS, - "ALL_WAVEFORMS", - 0, - "Waveforms On", - "Display waveforms for all sound strips"}, - {0, - "DEFAULT_WAVEFORMS", - 0, - "Use Strip Option", - "Display waveforms depending on strip setting"}, - {0, NULL, 0, NULL, NULL}, - }; - srna = RNA_def_struct(brna, "SpaceSequenceEditor", "Space"); RNA_def_struct_sdna(srna, "SpaceSeq"); RNA_def_struct_ui_text(srna, "Space Sequence Editor", "Sequence editor space data"); @@ -5428,23 +5521,6 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Separate Colors", "Separate color channels in preview"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_safe_areas", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_SAFE_MARGINS); - RNA_def_property_ui_text( - prop, "Safe Areas", "Show TV title safe and action safe areas in preview"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_safe_center", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_SAFE_CENTER); - RNA_def_property_ui_text( - prop, "Center-Cut Safe Areas", "Show safe areas to fit content in a different aspect ratio"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_metadata", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_METADATA); - RNA_def_property_ui_text(prop, "Show Metadata", "Show metadata of first visible strip"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_seconds", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_DRAWFRAMES); RNA_def_property_ui_text(prop, "Show Seconds", "Show timing in seconds not frames"); @@ -5458,11 +5534,6 @@ static void rna_def_space_sequencer(BlenderRNA *brna) "If any exists, show markers in a separate row at the bottom of the editor"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_GPENCIL); - RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "display_channel", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "chanshown"); RNA_def_property_ui_text( @@ -5478,12 +5549,6 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Display Channels", "Channels of the preview to display"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, "rna_SequenceEditor_update_cache"); - prop = RNA_def_property(srna, "waveform_display_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); - RNA_def_property_enum_items(prop, waveform_type_display_items); - RNA_def_property_ui_text(prop, "Waveform Display", "How Waveforms are displayed"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "use_zoom_to_fit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_ZOOM_TO_FIT); RNA_def_property_ui_text( @@ -5533,46 +5598,31 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Backdrop", "Display result under strips"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_strip_offset", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_OFFSET_EXT); - RNA_def_property_ui_text(prop, "Show Offsets", "Display strip in/out offsets"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_fcurves", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_FCURVES); - RNA_def_property_ui_text(prop, "Show F-Curves", "Display strip opacity/volume curve"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_strip_overlay", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_OVERLAY); - RNA_def_property_ui_text(prop, "Show Overlay", ""); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_strip_name", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_NAME); - RNA_def_property_ui_text(prop, "Show Name", ""); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_strip_source", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_SOURCE); - RNA_def_property_ui_text( - prop, "Show Source", "Display path to source file, or name of source datablock"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_strip_duration", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_DURATION); - RNA_def_property_ui_text(prop, "Show Duration", ""); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_transform_preview", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_TRANSFORM_PREVIEW); RNA_def_property_ui_text(prop, "Transform Preview", "Show preview of the transformed frames"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_grid", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_GRID); - RNA_def_property_ui_text(prop, "Show Grid", "Show vertical grid lines"); + /* Overlay settings. */ + prop = RNA_def_property(srna, "show_strip_overlay", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_OVERLAY); + RNA_def_property_ui_text(prop, "Show Overlay", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "preview_overlay", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "SequencerPreviewOverlay"); + RNA_def_property_pointer_sdna(prop, NULL, "preview_overlay"); + RNA_def_property_ui_text(prop, "Preview Overlay Settings", "Settings for display of overlays"); + + prop = RNA_def_property(srna, "timeline_overlay", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "SequencerTimelineOverlay"); + RNA_def_property_pointer_sdna(prop, NULL, "timeline_overlay"); + RNA_def_property_ui_text(prop, "Timeline Overlay Settings", "Settings for display of overlays"); + + rna_def_space_sequencer_preview_overlay(brna); + rna_def_space_sequencer_timeline_overlay(brna); } static void rna_def_space_text(BlenderRNA *brna) From a79c33e8f8f3cac25459b703f3ad21f4eabb4455 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Mon, 20 Sep 2021 07:41:29 -0700 Subject: [PATCH 0032/1500] UI: Change "Favorites" to "Bookmarks" Change of File Browser "Favorites" section header in source list (T panel) to "Bookmarks" to maintain consistency with all the other bookmark-related text and operations. See D10262 for more information and alternatives considered. Differential Revision: https://developer.blender.org/D10262 Reviewed by Julian Eisel --- release/scripts/startup/bl_ui/space_filebrowser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 8ba82a7d407..e52136fc416 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -300,7 +300,7 @@ class FILEBROWSER_PT_bookmarks_favorites(FileBrowserPanel, Panel): bl_space_type = 'FILE_BROWSER' bl_region_type = 'TOOLS' bl_category = "Bookmarks" - bl_label = "Favorites" + bl_label = "Bookmarks" @classmethod def poll(cls, context): From 32a4c7f188826d649b231b39baa1c1bcb7cbcbdc Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 20 Sep 2021 18:56:30 +0200 Subject: [PATCH 0033/1500] Geometry Nodes: implicit position input in Set Position node This change makes the Set Position node do nothing by default. Before, the geometry would always disappear, because it all points would be moved to (0, 0, 0). Differential Revision: https://developer.blender.org/D12553 --- .../blender/modifiers/intern/MOD_nodes_evaluator.cc | 11 +++++++++++ .../nodes/geometry/nodes/node_geo_set_position.cc | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index f67f7f967c9..a215ad4d21a 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -321,6 +321,17 @@ static const CPPType *get_socket_cpp_type(const DSocket socket) static void get_socket_value(const SocketRef &socket, void *r_value) { + const bNodeSocket &bsocket = *socket.bsocket(); + /* This is not supposed to be a long term solution. Eventually we want that nodes can specify + * more complex defaults (other than just single values) in their socket declarations. */ + if (bsocket.flag & SOCK_HIDE_VALUE) { + const bNode &bnode = *socket.bnode(); + if (bsocket.type == SOCK_VECTOR && bnode.type == GEO_NODE_SET_POSITION) { + new (r_value) Field( + std::make_shared("position", CPPType::get())); + return; + } + } const bNodeSocketType *typeinfo = socket.typeinfo(); typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 832db76e731..c5e10b788ac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -23,7 +23,7 @@ namespace blender::nodes { static void geo_node_set_position_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Position"); + b.add_input("Position").hide_value(); b.add_input("Selection").default_value(true).hide_value(); b.add_output("Geometry"); } From 3b8d702a2f071319d53aa622ebed0e9bc4ff3876 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 20 Sep 2021 19:01:08 +0200 Subject: [PATCH 0034/1500] Geometry Nodes: use implicit position input in noise node This is the same behavior as in shader nodes. --- source/blender/modifiers/intern/MOD_nodes_evaluator.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index a215ad4d21a..56de0f87ed8 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -326,7 +326,8 @@ static void get_socket_value(const SocketRef &socket, void *r_value) * more complex defaults (other than just single values) in their socket declarations. */ if (bsocket.flag & SOCK_HIDE_VALUE) { const bNode &bnode = *socket.bnode(); - if (bsocket.type == SOCK_VECTOR && bnode.type == GEO_NODE_SET_POSITION) { + if (bsocket.type == SOCK_VECTOR && + ELEM(bnode.type, GEO_NODE_SET_POSITION, SH_NODE_TEX_NOISE)) { new (r_value) Field( std::make_shared("position", CPPType::get())); return; From e43ecca01627393d0aabe56c2087d87938aed6c3 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 20 Sep 2021 18:44:52 +0200 Subject: [PATCH 0035/1500] Tests: measure time per frame in animation performance benchmark Instead of a fixed number of frames, so that benchmarking takes about the same time on any machine. --- tests/performance/tests/animation.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/performance/tests/animation.py b/tests/performance/tests/animation.py index 1a92f1a9718..c1a5c77860f 100644 --- a/tests/performance/tests/animation.py +++ b/tests/performance/tests/animation.py @@ -9,14 +9,20 @@ def _run(args): import time start_time = time.time() + elapsed_time = 0.0 + num_frames = 0 - scene = bpy.context.scene - for i in range(scene.frame_start, scene.frame_end): - scene.frame_set(scene.frame_start) + while elapsed_time < 10.0: + scene = bpy.context.scene + for i in range(scene.frame_start, scene.frame_end + 1): + scene.frame_set(i) - elapsed_time = time.time() - start_time + num_frames += scene.frame_end + 1 - scene.frame_start + elapsed_time = time.time() - start_time - result = {'time': elapsed_time} + time_per_frame = elapsed_time / num_frames + + result = {'time': time_per_frame} return result @@ -32,10 +38,10 @@ class AnimationTest(api.Test): def run(self, env, device_id): args = {} - result, _ = env.run_in_blender(_run, args) + result, _ = env.run_in_blender(_run, args, [self.filepath]) return result def generate(env): - filepaths = env.find_blend_files('animation') + filepaths = env.find_blend_files('animation/*') return [AnimationTest(filepath) for filepath in filepaths] From 15471d9bed4890f487b87be0eea89107c2161f7f Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 20 Sep 2021 13:40:10 -0400 Subject: [PATCH 0036/1500] Update RNA to user manual url mappings --- .../scripts/modules/rna_manual_reference.py | 228 +++++++++++++++--- 1 file changed, 198 insertions(+), 30 deletions(-) diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 24b5bb6b685..0e3cb7e3cab 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -39,6 +39,7 @@ if LANG is not None: url_manual_prefix = url_manual_prefix.replace("manual/en", "manual/" + LANG) url_manual_mapping = ( + ("bpy.types.cyclesworldsettings.sample_mbpy.types.cyclesworldsettings.sample_map_resolutionbpy.types.cyclesworldsettings.sample_map_resolutionap_resolution*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-sample-mbpy-types-cyclesworldsettings-sample-map-resolutionbpy-types-cyclesworldsettings-sample-map-resolutionap-resolution"), ("bpy.types.movietrackingsettings.refine_intrinsics_tangential_distortion*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-tangential-distortion"), ("bpy.types.movietrackingsettings.refine_intrinsics_radial_distortion*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-radial-distortion"), ("bpy.types.fluiddomainsettings.sndparticle_potential_max_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-trappedair"), @@ -46,6 +47,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_potential_max_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-wavecrest"), ("bpy.types.fluiddomainsettings.sndparticle_potential_min_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-wavecrest"), ("bpy.types.movietrackingsettings.refine_intrinsics_principal_point*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-principal-point"), + ("bpy.types.cyclesobjectsettings.shadow_terminator_geometry_offset*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-shadow-terminator-geometry-offset"), ("bpy.types.cyclesrenderlayersettings.denoising_optix_input_passes*", "render/layers/denoising.html#bpy-types-cyclesrenderlayersettings-denoising-optix-input-passes"), ("bpy.types.sequencertoolsettings.use_snap_current_frame_to_strips*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-use-snap-current-frame-to-strips"), ("bpy.types.fluiddomainsettings.sndparticle_potential_max_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-energy"), @@ -53,6 +55,7 @@ url_manual_mapping = ( ("bpy.types.movietrackingsettings.refine_intrinsics_focal_length*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-focal-length"), ("bpy.types.rigidbodyconstraint.rigidbodyconstraint.use_breaking*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-rigidbodyconstraint-use-breaking"), ("bpy.types.cyclesrendersettings.preview_denoising_input_passes*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoising-input-passes"), + ("bpy.types.cyclesrendersettings.preview_denoising_start_sample*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoising-start-sample"), ("bpy.types.fluiddomainsettings.sndparticle_sampling_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-trappedair"), ("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"), ("bpy.types.rigidbodyconstraint.use_override_solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-override-solver-iterations"), @@ -60,6 +63,8 @@ url_manual_mapping = ( ("bpy.types.rendersettings.use_sequencer_override_scene_strip*", "video_editing/preview/sidebar.html#bpy-types-rendersettings-use-sequencer-override-scene-strip"), ("bpy.types.toolsettings.use_transform_correct_keep_connected*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-keep-connected"), ("bpy.types.fluiddomainsettings.sndparticle_potential_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-radius"), + ("bpy.types.cyclesrendersettings.film_transparent_roughness*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-roughness"), + ("bpy.types.cyclesrendersettings.sample_all_lights_indirect*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sample-all-lights-indirect"), ("bpy.types.fluiddomainsettings.openvdb_cache_compress_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-cache-compress-type"), ("bpy.types.fluiddomainsettings.sndparticle_bubble_buoyancy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-buoyancy"), ("bpy.types.fluiddomainsettings.sndparticle_combined_export*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-combined-export"), @@ -71,13 +76,23 @@ url_manual_mapping = ( ("bpy.types.toolsettings.annotation_stroke_placement_view3d*", "interface/annotate_tool.html#bpy-types-toolsettings-annotation-stroke-placement-view3d"), ("bpy.types.fluiddomainsettings.use_collision_border_front*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-front"), ("bpy.types.fluiddomainsettings.use_collision_border_right*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-right"), + ("bpy.types.cyclesmaterialsettings.use_transparent_shadow*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-use-transparent-shadow"), + ("bpy.types.cyclesobjectsettings.shadow_terminator_offset*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-shadow-terminator-offset"), ("bpy.types.cyclesobjectsettings.use_adaptive_subdivision*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-use-adaptive-subdivision"), + ("bpy.types.cyclesrendersettings.debug_use_spatial_splits*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-use-spatial-splits"), + ("bpy.types.cyclesrendersettings.light_sampling_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-light-sampling-threshold"), + ("bpy.types.cyclesrendersettings.preview_start_resolution*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-preview-start-resolution"), + ("bpy.types.cyclesrendersettings.rolling_shutter_duration*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-rolling-shutter-duration"), + ("bpy.types.cyclesrendersettings.sample_all_lights_direct*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sample-all-lights-direct"), + ("bpy.types.cyclesrendersettings.volume_preview_step_rate*", "render/cycles/render_settings/volumes.html#bpy-types-cyclesrendersettings-volume-preview-step-rate"), ("bpy.types.fluiddomainsettings.sndparticle_update_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-update-radius"), ("bpy.types.fluiddomainsettings.use_collision_border_back*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-back"), ("bpy.types.fluiddomainsettings.use_collision_border_left*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-left"), ("bpy.types.rendersettings_simplify_gpencil_view_modifier*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-modifier"), ("bpy.types.brushgpencilsettings.use_settings_stabilizer*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-stabilizer"), ("bpy.types.colormanagedsequencercolorspacesettings.name*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings-name"), + ("bpy.types.cyclesrendersettings.max_transparent_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-max-transparent-bounces"), + ("bpy.types.cyclesrendersettings.min_transparent_bounces*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-min-transparent-bounces"), ("bpy.types.fluiddomainsettings.use_collision_border_top*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-top"), ("bpy.types.gpencilsculptsettings.use_multiframe_falloff*", "grease_pencil/multiframe.html#bpy-types-gpencilsculptsettings-use-multiframe-falloff"), ("bpy.types.movietrackingsettings.use_keyframe_selection*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-keyframe-selection"), @@ -85,31 +100,47 @@ url_manual_mapping = ( ("bpy.types.spaceoutliner.use_filter_lib_override_system*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override-system"), ("bpy.types.toolsettings.use_transform_pivot_point_align*", "scene_layout/object/tools/tool_settings.html#bpy-types-toolsettings-use-transform-pivot-point-align"), ("bpy.types.brush.show_multiplane_scrape_planes_preview*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-show-multiplane-scrape-planes-preview"), + ("bpy.types.cyclesmaterialsettings.volume_interpolation*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-interpolation"), + ("bpy.types.cyclesrendersettings.debug_optix_curves_api*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-optix-curves-api"), + ("bpy.types.cyclesrendersettings.film_transparent_glass*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-glass"), ("bpy.types.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"), + ("bpy.types.cyclesrendersettings.use_progressive_refine*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-use-progressive-refine"), ("bpy.types.fluiddomainsettings.sndparticle_bubble_drag*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-drag"), - ("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"), - ("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/sinus_displacement.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"), + ("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/view_layer/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"), + ("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/view_layer/line_style/modifiers/geometry/sinus_displacement.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"), ("bpy.types.sequencertoolsettings.snap_to_current_frame*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-to-current-frame"), ("bpy.types.colormanageddisplaysettings.display_device*", "render/color_management.html#bpy-types-colormanageddisplaysettings-display-device"), ("bpy.types.colormanagedviewsettings.use_curve_mapping*", "render/color_management.html#bpy-types-colormanagedviewsettings-use-curve-mapping"), + ("bpy.types.cyclesrendersettings.sample_clamp_indirect*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-sample-clamp-indirect"), + ("bpy.types.cyclesrendersettings.use_adaptive_sampling*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-adaptive-sampling"), + ("bpy.types.cyclesrendersettings.use_preview_denoising*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-preview-denoising"), ("bpy.types.fluiddomainsettings.color_ramp_field_scale*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-color-ramp-field-scale"), ("bpy.types.fluiddomainsettings.use_adaptive_timesteps*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-adaptive-timesteps"), ("bpy.types.fluiddomainsettings.use_dissolve_smoke_log*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-dissolve-smoke-log"), + ("bpy.types.freestylelineset.select_suggestive_contour*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-suggestive-contour"), ("bpy.types.gpencillayer.annotation_onion_before_color*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-before-color"), ("bpy.types.gpencillayer.annotation_onion_before_range*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-before-range"), ("bpy.types.gpencillayer.use_annotation_onion_skinning*", "interface/annotate_tool.html#bpy-types-gpencillayer-use-annotation-onion-skinning"), ("bpy.types.greasepencil.use_adaptive_curve_resolution*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-adaptive-curve-resolution"), - ("bpy.types.linestylegeometrymodifier_polygonalization*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/polygonization.html#bpy-types-linestylegeometrymodifier-polygonalization"), + ("bpy.types.linestylegeometrymodifier_polygonalization*", "render/freestyle/view_layer/line_style/modifiers/geometry/polygonization.html#bpy-types-linestylegeometrymodifier-polygonalization"), ("bpy.types.toolsettings.use_gpencil_automerge_strokes*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-automerge-strokes"), ("bpy.types.toolsettings.use_proportional_edit_objects*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-edit-objects"), ("bpy.ops.view3d.edit_mesh_extrude_move_shrink_fatten*", "modeling/meshes/editing/face/extrude_faces_normal.html#bpy-ops-view3d-edit-mesh-extrude-move-shrink-fatten"), + ("bpy.types.cyclesmaterialsettings.homogeneous_volume*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-homogeneous-volume"), + ("bpy.types.cyclesrendersettings.adaptive_min_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-adaptive-min-samples"), + ("bpy.types.cyclesrendersettings.debug_bvh_time_steps*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-bvh-time-steps"), ("bpy.types.cyclesrendersettings.distance_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-distance-cull-margin"), + ("bpy.types.cyclesrendersettings.motion_blur_position*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-motion-blur-position"), + ("bpy.types.cyclesrendersettings.rolling_shutter_type*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-rolling-shutter-type"), + ("bpy.types.cyclesrendersettings.transmission_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-transmission-bounces"), + ("bpy.types.cyclesrendersettings.transmission_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-transmission-samples"), ("bpy.types.fluiddomainsettings.display_interpolation*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-display-interpolation"), ("bpy.types.fluiddomainsettings.gridlines_cell_filter*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-cell-filter"), ("bpy.types.fluiddomainsettings.gridlines_color_field*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-color-field"), ("bpy.types.fluiddomainsettings.gridlines_lower_bound*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-lower-bound"), ("bpy.types.fluiddomainsettings.gridlines_range_color*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-range-color"), ("bpy.types.fluiddomainsettings.gridlines_upper_bound*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-upper-bound"), + ("bpy.types.freestylelineset.select_material_boundary*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-material-boundary"), ("bpy.types.gpencillayer.annotation_onion_after_color*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-after-color"), ("bpy.types.gpencillayer.annotation_onion_after_range*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-after-range"), ("bpy.types.materialgpencilstyle.use_fill_texture_mix*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-texture-mix"), @@ -123,13 +154,17 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.fill_simplify_level*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-simplify-level"), ("bpy.types.brushgpencilsettings.use_jitter_pressure*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-jitter-pressure"), ("bpy.types.brushgpencilsettings.use_settings_random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-random"), + ("bpy.types.cyclesrendersettings.preview_dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-preview-dicing-rate"), + ("bpy.types.cyclesrendersettings.sample_clamp_direct*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-sample-clamp-direct"), + ("bpy.types.cyclesworldsettings.volume_interpolation*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-interpolation"), ("bpy.types.fluiddomainsettings.mesh_particle_radius*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-mesh-particle-radius"), ("bpy.types.fluiddomainsettings.sndparticle_boundary*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-boundary"), ("bpy.types.fluiddomainsettings.sndparticle_life_max*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-life-max"), ("bpy.types.fluiddomainsettings.sndparticle_life_min*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-life-min"), ("bpy.types.fluiddomainsettings.sys_particle_maximum*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-sys-particle-maximum"), ("bpy.types.fluiddomainsettings.use_bubble_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-bubble-particles"), - ("bpy.types.linestylegeometrymodifier_simplification*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/simplification.html#bpy-types-linestylegeometrymodifier-simplification"), + ("bpy.types.freestylelineset.select_external_contour*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-external-contour"), + ("bpy.types.linestylegeometrymodifier_simplification*", "render/freestyle/view_layer/line_style/modifiers/geometry/simplification.html#bpy-types-linestylegeometrymodifier-simplification"), ("bpy.types.materialgpencilstyle.use_overlap_strokes*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-overlap-strokes"), ("bpy.types.spacespreadsheet.geometry_component_type*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-geometry-component-type"), ("bpy.types.toolsettings.use_gpencil_weight_data_add*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-weight-data-add"), @@ -141,7 +176,14 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.use_default_eraser*", "grease_pencil/modes/draw/tools/erase.html#bpy-types-brushgpencilsettings-use-default-eraser"), ("bpy.types.colormanagedsequencercolorspacesettings*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings"), ("bpy.types.colormanagedviewsettings.view_transform*", "render/color_management.html#bpy-types-colormanagedviewsettings-view-transform"), + ("bpy.types.cyclesmaterialsettings.volume_step_rate*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-step-rate"), + ("bpy.types.cyclesrendersettings.adaptive_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-adaptive-threshold"), ("bpy.types.cyclesrendersettings.camera_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-camera-cull-margin"), + ("bpy.types.cyclesrendersettings.debug_use_hair_bvh*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-use-hair-bvh"), + ("bpy.types.cyclesrendersettings.mesh_light_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-mesh-light-samples"), + ("bpy.types.cyclesrendersettings.preview_aa_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-aa-samples"), + ("bpy.types.cyclesrendersettings.subsurface_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-subsurface-samples"), + ("bpy.types.cyclesrendersettings.use_square_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-square-samples"), ("bpy.types.fluiddomainsettings.export_manta_script*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-export-manta-script"), ("bpy.types.fluiddomainsettings.fractions_threshold*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-fractions-threshold"), ("bpy.types.fluiddomainsettings.particle_band_width*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-particle-band-width"), @@ -150,11 +192,14 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.use_resumable_cache*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-use-resumable-cache"), ("bpy.types.fluiddomainsettings.use_spray_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-spray-particles"), ("bpy.types.fluiddomainsettings.vector_display_type*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-display-type"), + ("bpy.types.freestylelineset.select_by_image_border*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-by-image-border"), + ("bpy.types.freestylesettings.kr_derivative_epsilon*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-kr-derivative-epsilon"), ("bpy.types.geometrynodecurveprimitivebeziersegment*", "modeling/geometry_nodes/curve_primitives/bezier_segment.html#bpy-types-geometrynodecurveprimitivebeziersegment"), - ("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"), - ("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"), + ("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/view_layer/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"), + ("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/view_layer/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"), ("bpy.types.materialgpencilstyle.use_stroke_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-stroke-holdout"), ("bpy.types.movietrackingsettings.use_tripod_solver*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-tripod-solver"), + ("bpy.types.rendersettings.simplify_child_particles*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-child-particles"), ("bpy.types.rendersettings.use_high_quality_normals*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-use-high-quality-normals"), ("bpy.types.sequencertoolsettings.snap_ignore_muted*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-muted"), ("bpy.types.sequencertoolsettings.snap_ignore_sound*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-sound"), @@ -169,9 +214,18 @@ url_manual_mapping = ( ("bpy.ops.gpencil.vertex_color_brightness_contrast*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-brightness-contrast"), ("bpy.ops.view3d.edit_mesh_extrude_individual_move*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-view3d-edit-mesh-extrude-individual-move"), ("bpy.ops.view3d.edit_mesh_extrude_manifold_normal*", "modeling/meshes/tools/extrude_manifold.html#bpy-ops-view3d-edit-mesh-extrude-manifold-normal"), + ("bpy.types.cyclescurverendersettings.subdivisions*", "render/cycles/render_settings/hair.html#bpy-types-cyclescurverendersettings-subdivisions"), + ("bpy.types.cyclesmaterialsettings.sample_as_light*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-sample-as-light"), + ("bpy.types.cyclesmaterialsettings.volume_sampling*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-sampling"), + ("bpy.types.cyclesobjectsettings.use_deform_motion*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-deform-motion"), ("bpy.types.cyclesobjectsettings.use_distance_cull*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-distance-cull"), ("bpy.types.cyclesrendersettings.ao_bounces_render*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces-render"), + ("bpy.types.cyclesrendersettings.min_light_bounces*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-min-light-bounces"), + ("bpy.types.cyclesrendersettings.pixel_filter_type*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-pixel-filter-type"), + ("bpy.types.cyclesrendersettings.use_animated_seed*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-animated-seed"), ("bpy.types.cyclesrendersettings.use_distance_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-distance-cull"), + ("bpy.types.cyclesrendersettings.use_layer_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-layer-samples"), + ("bpy.types.cyclesworldsettings.homogeneous_volume*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-homogeneous-volume"), ("bpy.types.fluiddomainsettings.cache_frame_offset*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-offset"), ("bpy.types.fluiddomainsettings.delete_in_obstacle*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-delete-in-obstacle"), ("bpy.types.fluiddomainsettings.fractions_distance*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-fractions-distance"), @@ -185,9 +239,9 @@ url_manual_mapping = ( ("bpy.types.fluideffectorsettings.surface_distance*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-surface-distance"), ("bpy.types.fluidflowsettings.density_vertex_group*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density-vertex-group"), ("bpy.types.fluidflowsettings.use_initial_velocity*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-initial-velocity"), - ("bpy.types.linestylegeometrymodifier_guidinglines*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/guiding_lines.html#bpy-types-linestylegeometrymodifier-guidinglines"), - ("bpy.types.linestylegeometrymodifier_spatialnoise*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/spatial_noise.html#bpy-types-linestylegeometrymodifier-spatialnoise"), - ("bpy.types.linestylethicknessmodifier_calligraphy*", "render/freestyle/parameter_editor/line_style/modifiers/thickness/calligraphy.html#bpy-types-linestylethicknessmodifier-calligraphy"), + ("bpy.types.linestylegeometrymodifier_guidinglines*", "render/freestyle/view_layer/line_style/modifiers/geometry/guiding_lines.html#bpy-types-linestylegeometrymodifier-guidinglines"), + ("bpy.types.linestylegeometrymodifier_spatialnoise*", "render/freestyle/view_layer/line_style/modifiers/geometry/spatial_noise.html#bpy-types-linestylegeometrymodifier-spatialnoise"), + ("bpy.types.linestylethicknessmodifier_calligraphy*", "render/freestyle/view_layer/line_style/modifiers/thickness/calligraphy.html#bpy-types-linestylethicknessmodifier-calligraphy"), ("bpy.types.rendersettings_simplify_gpencil_onplay*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-onplay"), ("bpy.types.rigidbodyconstraint.breaking_threshold*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-breaking-threshold"), ("bpy.types.spaceclipeditor.use_manual_calibration*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-manual-calibration"), @@ -203,6 +257,10 @@ url_manual_mapping = ( ("bpy.types.view3doverlay.sculpt_mode_mask_opacity*", "sculpt_paint/sculpting/editing/mask.html#bpy-types-view3doverlay-sculpt-mode-mask-opacity"), ("bpy.ops.outliner.collection_indirect_only_clear*", "render/layers/introduction.html#bpy-ops-outliner-collection-indirect-only-clear"), ("bpy.types.cyclesrendersettings.max_subdivisions*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-max-subdivisions"), + ("bpy.types.cyclesrendersettings.preview_denoiser*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoiser"), + ("bpy.types.cyclesrendersettings.sampling_pattern*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sampling-pattern"), + ("bpy.types.cyclesrendersettings.volume_max_steps*", "render/cycles/render_settings/volumes.html#bpy-types-cyclesrendersettings-volume-max-steps"), + ("bpy.types.cyclesrendersettings.volume_step_rate*", "render/cycles/render_settings/volumes.html#bpy-types-cyclesrendersettings-volume-step-rate"), ("bpy.types.editbone.bbone_handle_use_scale_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-scale-start"), ("bpy.types.fluiddomainsettings.cache_data_format*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-data-format"), ("bpy.types.fluiddomainsettings.cache_frame_start*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-start"), @@ -217,10 +275,13 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.vector_show_mac_y*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-show-mac-y"), ("bpy.types.fluiddomainsettings.vector_show_mac_z*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-show-mac-z"), ("bpy.types.fluideffectorsettings.velocity_factor*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-velocity-factor"), - ("bpy.types.linestyle*modifier_distancefromcamera*", "render/freestyle/parameter_editor/line_style/modifiers/color/distance_from_camera.html#bpy-types-linestyle-modifier-distancefromcamera"), - ("bpy.types.linestyle*modifier_distancefromobject*", "render/freestyle/parameter_editor/line_style/modifiers/color/distance_from_object.html#bpy-types-linestyle-modifier-distancefromobject"), - ("bpy.types.linestylegeometrymodifier_2dtransform*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/2d_transform.html#bpy-types-linestylegeometrymodifier-2dtransform"), - ("bpy.types.linestylegeometrymodifier_beziercurve*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/bezier_curve.html#bpy-types-linestylegeometrymodifier-beziercurve"), + ("bpy.types.freestylelineset.select_by_collection*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-by-collection"), + ("bpy.types.freestylelineset.select_by_edge_types*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-by-edge-types"), + ("bpy.types.freestylelineset.select_by_face_marks*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-by-face-marks"), + ("bpy.types.linestyle*modifier_distancefromcamera*", "render/freestyle/view_layer/line_style/modifiers/color/distance_from_camera.html#bpy-types-linestyle-modifier-distancefromcamera"), + ("bpy.types.linestyle*modifier_distancefromobject*", "render/freestyle/view_layer/line_style/modifiers/color/distance_from_object.html#bpy-types-linestyle-modifier-distancefromobject"), + ("bpy.types.linestylegeometrymodifier_2dtransform*", "render/freestyle/view_layer/line_style/modifiers/geometry/2d_transform.html#bpy-types-linestylegeometrymodifier-2dtransform"), + ("bpy.types.linestylegeometrymodifier_beziercurve*", "render/freestyle/view_layer/line_style/modifiers/geometry/bezier_curve.html#bpy-types-linestylegeometrymodifier-beziercurve"), ("bpy.types.materialgpencilstyle.use_fill_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-holdout"), ("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"), ("bpy.types.rigidbodyconstraint.solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-solver-iterations"), @@ -239,13 +300,24 @@ url_manual_mapping = ( ("bpy.ops.armature.rigify_apply_selection_colors*", "addons/rigging/rigify/metarigs.html#bpy-ops-armature-rigify-apply-selection-colors"), ("bpy.types.brushgpencilsettings.fill_layer_mode*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-layer-mode"), ("bpy.types.cyclesobjectsettings.use_camera_cull*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-camera-cull"), + ("bpy.types.cyclesobjectsettings.use_motion_blur*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-motion-blur"), + ("bpy.types.cyclesrendersettings.diffuse_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-diffuse-bounces"), + ("bpy.types.cyclesrendersettings.diffuse_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-diffuse-samples"), + ("bpy.types.cyclesrendersettings.preview_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-samples"), ("bpy.types.cyclesrendersettings.use_camera_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-camera-cull"), + ("bpy.types.cyclesworldsettings.volume_step_size*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-step-size"), ("bpy.types.editbone.bbone_handle_use_ease_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-ease-start"), ("bpy.types.fluiddomainsettings.color_ramp_field*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-color-ramp-field"), ("bpy.types.fluiddomainsettings.guide_vel_factor*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-vel-factor"), ("bpy.types.fluideffectorsettings.use_plane_init*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-use-plane-init"), + ("bpy.types.freestylelineset.collection_negation*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-collection-negation"), + ("bpy.types.freestylelineset.face_mark_condition*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-face-mark-condition"), + ("bpy.types.freestylelineset.select_ridge_valley*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-ridge-valley"), + ("bpy.types.freestylelinestyle.material_boundary*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-material-boundary"), + ("bpy.types.freestylelinestyle.use_split_pattern*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-split-pattern"), + ("bpy.types.freestylesettings.use_view_map_cache*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-view-map-cache"), ("bpy.types.greasepencil.curve_edit_corner_angle*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-corner-angle"), - ("bpy.types.linestylegeometrymodifier_tipremover*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/tip_remover.html#bpy-types-linestylegeometrymodifier-tipremover"), + ("bpy.types.linestylegeometrymodifier_tipremover*", "render/freestyle/view_layer/line_style/modifiers/geometry/tip_remover.html#bpy-types-linestylegeometrymodifier-tipremover"), ("bpy.types.movieclipuser.use_render_undistorted*", "editors/clip/display/clip_display.html#bpy-types-movieclipuser-use-render-undistorted"), ("bpy.types.movietrackingcamera.distortion_model*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-distortion-model"), ("bpy.types.rendersettings.resolution_percentage*", "render/output/properties/format.html#bpy-types-rendersettings-resolution-percentage"), @@ -257,6 +329,7 @@ url_manual_mapping = ( ("bpy.types.toolsettings.use_snap_align_rotation*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-align-rotation"), ("bpy.types.viewlayer.use_pass_cryptomatte_asset*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-asset"), ("bpy.ops.outliner.collection_indirect_only_set*", "render/layers/introduction.html#bpy-ops-outliner-collection-indirect-only-set"), + ("bpy.ops.scene.freestyle_geometry_modifier_add*", "render/freestyle/view_layer/line_style/geometry.html#bpy-ops-scene-freestyle-geometry-modifier-add"), ("bpy.ops.sequencer.deinterlace_selected_movies*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-deinterlace-selected-movies"), ("bpy.types.bakesettings.use_selected_to_active*", "render/cycles/baking.html#bpy-types-bakesettings-use-selected-to-active"), ("bpy.types.brush.surface_smooth_current_vertex*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-current-vertex"), @@ -266,6 +339,13 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.fill_threshold*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-threshold"), ("bpy.types.clothsettings.vertex_group_pressure*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-pressure"), ("bpy.types.cyclesmaterialsettings.displacement*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-displacement"), + ("bpy.types.cyclesrendersettings.debug_bvh_type*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-bvh-type"), + ("bpy.types.cyclesrendersettings.glossy_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-glossy-bounces"), + ("bpy.types.cyclesrendersettings.glossy_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-glossy-samples"), + ("bpy.types.cyclesrendersettings.volume_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-volume-bounces"), + ("bpy.types.cyclesrendersettings.volume_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-volume-samples"), + ("bpy.types.cyclesworldsettings.sampling_method*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-sampling-method"), + ("bpy.types.cyclesworldsettings.volume_sampling*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-sampling"), ("bpy.types.editbone.bbone_handle_use_scale_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-scale-end"), ("bpy.types.fluiddomainsettings.adapt_threshold*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-adapt-threshold"), ("bpy.types.fluiddomainsettings.cache_directory*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-directory"), @@ -280,7 +360,10 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.viscosity_value*", "physics/fluid/type/domain/liquid/viscosity.html#bpy-types-fluiddomainsettings-viscosity-value"), ("bpy.types.fluideffectorsettings.effector_type*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-effector-type"), ("bpy.types.fluidflowsettings.use_particle_size*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-particle-size"), - ("bpy.types.linestylegeometrymodifier_blueprint*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/blueprint.html#bpy-types-linestylegeometrymodifier-blueprint"), + ("bpy.types.freestylelineset.face_mark_negation*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-face-mark-negation"), + ("bpy.types.freestylelinestyle.integration_type*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-integration-type"), + ("bpy.types.freestylelinestyle.use_split_length*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-split-length"), + ("bpy.types.linestylegeometrymodifier_blueprint*", "render/freestyle/view_layer/line_style/modifiers/geometry/blueprint.html#bpy-types-linestylegeometrymodifier-blueprint"), ("bpy.types.materialgpencilstyle.alignment_mode*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-alignment-mode"), ("bpy.types.particlesettings.use_modifier_stack*", "physics/particles/emitter/emission.html#bpy-types-particlesettings-use-modifier-stack"), ("bpy.types.rendersettings.sequencer_gl_preview*", "video_editing/preview/sidebar.html#bpy-types-rendersettings-sequencer-gl-preview"), @@ -297,6 +380,7 @@ url_manual_mapping = ( ("bpy.types.spacespreadsheetrowfilter.threshold*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-threshold"), ("bpy.types.toolsettings.use_snap_grid_absolute*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-grid-absolute"), ("bpy.types.view3doverlay.show_face_orientation*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-face-orientation"), + ("bpy.types.worldlighting.use_ambient_occlusion*", "render/cycles/world_settings.html#bpy-types-worldlighting-use-ambient-occlusion"), ("bpy.ops.object.blenderkit_material_thumbnail*", "addons/3d_view/blenderkit.html#bpy-ops-object-blenderkit-material-thumbnail"), ("bpy.ops.object.multires_higher_levels_delete*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-higher-levels-delete"), ("bpy.ops.object.vertex_group_copy_to_selected*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-copy-to-selected"), @@ -304,8 +388,11 @@ url_manual_mapping = ( ("bpy.ops.view3d.edit_mesh_extrude_move_normal*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-view3d-edit-mesh-extrude-move-normal"), ("bpy.types.bakesettings.use_pass_transmission*", "render/cycles/baking.html#bpy-types-bakesettings-use-pass-transmission"), ("bpy.types.clothsettings.internal_compression*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-compression"), + ("bpy.types.cyclescamerasettings.panorama_type*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-panorama-type"), ("bpy.types.cyclesrendersettings.dicing_camera*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-camera"), + ("bpy.types.cyclesrendersettings.film_exposure*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-exposure"), ("bpy.types.cyclesrendersettings.texture_limit*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-texture-limit"), + ("bpy.types.cyclesrendersettings.use_denoising*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-denoising"), ("bpy.types.editbone.bbone_custom_handle_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-custom-handle-start"), ("bpy.types.editbone.bbone_handle_use_ease_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-ease-end"), ("bpy.types.fluiddomainsettings.additional_res*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-additional-res"), @@ -323,12 +410,19 @@ url_manual_mapping = ( ("bpy.types.fluideffectorsettings.use_effector*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-use-effector"), ("bpy.types.fluidflowsettings.surface_distance*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-surface-distance"), ("bpy.types.fluidflowsettings.texture_map_type*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-texture-map-type"), + ("bpy.types.freestylelineset.select_silhouette*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-silhouette"), + ("bpy.types.freestylelinestyle.texture_spacing*", "render/freestyle/view_layer/line_style/texture.html#bpy-types-freestylelinestyle-texture-spacing"), + ("bpy.types.freestylelinestyle.use_chain_count*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-chain-count"), + ("bpy.types.freestylelinestyle.use_dashed_line*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-dashed-line"), + ("bpy.types.freestylelinestyle.use_same_object*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-same-object"), ("bpy.types.geometrynodeattributesampletexture*", "modeling/geometry_nodes/attribute/attribute_sample_texture.html#bpy-types-geometrynodeattributesampletexture"), ("bpy.types.gpencilsculptguide.reference_point*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-reference-point"), ("bpy.types.greasepencil.edit_curve_resolution*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-edit-curve-resolution"), - ("bpy.types.linestylegeometrymodifier_2doffset*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/2d_offset.html#bpy-types-linestylegeometrymodifier-2doffset"), - ("bpy.types.linestylegeometrymodifier_sampling*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/sampling.html#bpy-types-linestylegeometrymodifier-sampling"), + ("bpy.types.linestylegeometrymodifier_2doffset*", "render/freestyle/view_layer/line_style/modifiers/geometry/2d_offset.html#bpy-types-linestylegeometrymodifier-2doffset"), + ("bpy.types.linestylegeometrymodifier_sampling*", "render/freestyle/view_layer/line_style/modifiers/geometry/sampling.html#bpy-types-linestylegeometrymodifier-sampling"), ("bpy.types.nodesocketinterface*.default_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-default-value"), + ("bpy.types.rendersettings.line_thickness_mode*", "render/freestyle/render.html#bpy-types-rendersettings-line-thickness-mode"), + ("bpy.types.rendersettings.motion_blur_shutter*", "render/cycles/render_settings/motion_blur.html#bpy-types-rendersettings-motion-blur-shutter"), ("bpy.types.rendersettings.use_persistent_data*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-persistent-data"), ("bpy.types.sequencertoolsettings.overlap_mode*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-overlap-mode"), ("bpy.types.spaceclipeditor.show_green_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-green-channel"), @@ -340,7 +434,9 @@ url_manual_mapping = ( ("bpy.types.clothsettings.use_pressure_volume*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-use-pressure-volume"), ("bpy.types.clothsettings.vertex_group_intern*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-intern"), ("bpy.types.colormanagedviewsettings.exposure*", "render/color_management.html#bpy-types-colormanagedviewsettings-exposure"), - ("bpy.types.cyclesrendersettings.*dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-rate"), + ("bpy.types.cyclescamerasettings.fisheye_lens*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-fisheye-lens"), + ("bpy.types.cyclesobjectsettings.motion_steps*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-motion-steps"), + ("bpy.types.cyclesrendersettings.filter_width*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-filter-width"), ("bpy.types.fluiddomainsettings.cfl_condition*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-cfl-condition"), ("bpy.types.fluiddomainsettings.show_velocity*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-show-velocity"), ("bpy.types.fluiddomainsettings.timesteps_max*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-timesteps-max"), @@ -351,6 +447,9 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.particle_system*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-particle-system"), ("bpy.types.fluidflowsettings.velocity_factor*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-velocity-factor"), ("bpy.types.fluidflowsettings.velocity_normal*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-velocity-normal"), + ("bpy.types.freestylelineset.select_edge_mark*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-edge-mark"), + ("bpy.types.freestylelinestyle.use_length_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-length-max"), + ("bpy.types.freestylelinestyle.use_length_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-length-min"), ("bpy.types.geometrynodealignrotationtovector*", "modeling/geometry_nodes/point/align_rotation_to_vector.html#bpy-types-geometrynodealignrotationtovector"), ("bpy.types.geometrynodeattributevectorrotate*", "modeling/geometry_nodes/attribute/attribute_vector_rotate.html#bpy-types-geometrynodeattributevectorrotate"), ("bpy.types.greasepencil.curve_edit_threshold*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-threshold"), @@ -375,10 +474,18 @@ url_manual_mapping = ( ("bpy.ops.object.constraint_add_with_targets*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraint-add-with-targets"), ("bpy.ops.object.material_slot_remove_unused*", "scene_layout/object/editing/cleanup.html#bpy-ops-object-material-slot-remove-unused"), ("bpy.ops.outliner.collection_disable_render*", "editors/outliner/editing.html#bpy-ops-outliner-collection-disable-render"), + ("bpy.ops.scene.freestyle_alpha_modifier_add*", "render/freestyle/view_layer/line_style/alpha.html#bpy-ops-scene-freestyle-alpha-modifier-add"), + ("bpy.ops.scene.freestyle_color_modifier_add*", "render/freestyle/view_layer/line_style/color.html#bpy-ops-scene-freestyle-color-modifier-add"), ("bpy.types.brush.cloth_simulation_area_type*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-simulation-area-type"), ("bpy.types.brushgpencilsettings.fill_factor*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-factor"), ("bpy.types.curve.bevel_factor_mapping_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-start"), + ("bpy.types.cyclescamerasettings.fisheye_fov*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-fisheye-fov"), + ("bpy.types.cyclesobjectsettings.ao_distance*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-ao-distance"), ("bpy.types.cyclesobjectsettings.dicing_rate*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-dicing-rate"), + ("bpy.types.cyclesrendersettings.blur_glossy*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-blur-glossy"), + ("bpy.types.cyclesrendersettings.dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-rate"), + ("bpy.types.cyclesrendersettings.max_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-max-bounces"), + ("bpy.types.cyclesrendersettings.progressive*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-progressive"), ("bpy.types.cyclesrendersettings.use_fast_gi*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-use-fast-gi"), ("bpy.types.editbone.bbone_custom_handle_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-custom-handle-end"), ("bpy.types.editbone.bbone_handle_type_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-type-start"), @@ -395,6 +502,10 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.use_plane_init*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-plane-init"), ("bpy.types.fluidflowsettings.velocity_coord*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-velocity-coord"), ("bpy.types.fluidflowsettings.volume_density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-volume-density"), + ("bpy.types.freestylelinestyle.use_angle_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-angle-max"), + ("bpy.types.freestylelinestyle.use_angle_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-angle-min"), + ("bpy.types.freestylesettings.as_render_pass*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-as-render-pass"), + ("bpy.types.freestylesettings.use_smoothness*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-smoothness"), ("bpy.types.geometrynodecurvequadraticbezier*", "modeling/geometry_nodes/curve_primitives/quadratic_bezier.html#bpy-types-geometrynodecurvequadraticbezier"), ("bpy.types.materialgpencilstyle.show_stroke*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-show-stroke"), ("bpy.types.movietrackingcamera.focal_length*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-focal-length"), @@ -425,7 +536,13 @@ url_manual_mapping = ( ("bpy.types.brush.disconnected_distance_max*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-disconnected-distance-max"), ("bpy.types.brush.surface_smooth_iterations*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-iterations"), ("bpy.types.brushgpencilsettings.pen_jitter*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-jitter"), + ("bpy.types.cyclescurverendersettings.shape*", "render/cycles/render_settings/hair.html#bpy-types-cyclescurverendersettings-shape"), + ("bpy.types.cyclesrendersettings.aa_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-aa-samples"), ("bpy.types.cyclesrendersettings.ao_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces"), + ("bpy.types.cyclesrendersettings.ao_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-ao-samples"), + ("bpy.types.cyclesrendersettings.tile_order*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-tile-order"), + ("bpy.types.cyclesvisibilitysettings.camera*", "render/cycles/world_settings.html#bpy-types-cyclesvisibilitysettings-camera"), + ("bpy.types.cyclesworldsettings.max_bounces*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-max-bounces"), ("bpy.types.fluiddomainsettings.domain_type*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-domain-type"), ("bpy.types.fluiddomainsettings.flame_smoke*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-flame-smoke"), ("bpy.types.fluiddomainsettings.fluid_group*", "physics/fluid/type/domain/collections.html#bpy-types-fluiddomainsettings-fluid-group"), @@ -435,6 +552,10 @@ url_manual_mapping = ( ("bpy.types.fluideffectorsettings.subframes*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-subframes"), ("bpy.types.fluidflowsettings.flow_behavior*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-flow-behavior"), ("bpy.types.fluidflowsettings.noise_texture*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-noise-texture"), + ("bpy.types.freestylelineset.select_contour*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-contour"), + ("bpy.types.freestylelinestyle.split_length*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-split-length"), + ("bpy.types.freestylelinestyle.use_chaining*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-chaining"), + ("bpy.types.freestylesettings.sphere_radius*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-sphere-radius"), ("bpy.types.geometrynodeattributevectormath*", "modeling/geometry_nodes/attribute/attribute_vector_math.html#bpy-types-geometrynodeattributevectormath"), ("bpy.types.gpencillayer.annotation_opacity*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-opacity"), ("bpy.types.gpencillayer.use_onion_skinning*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-onion-skinning"), @@ -442,18 +563,18 @@ url_manual_mapping = ( ("bpy.types.gpencilsculptsettings.lock_axis*", "grease_pencil/modes/draw/drawing_planes.html#bpy-types-gpencilsculptsettings-lock-axis"), ("bpy.types.imageformatsettings.file_format*", "render/output/properties/output.html#bpy-types-imageformatsettings-file-format"), ("bpy.types.imagepaint.use_backface_culling*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-use-backface-culling"), - ("bpy.types.linestyle*modifier_curvature_3d*", "render/freestyle/parameter_editor/line_style/modifiers/color/curvature_3d.html#bpy-types-linestyle-modifier-curvature-3d"), + ("bpy.types.linestyle*modifier_curvature_3d*", "render/freestyle/view_layer/line_style/modifiers/color/curvature_3d.html#bpy-types-linestyle-modifier-curvature-3d"), ("bpy.types.materialgpencilstyle.fill_color*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-fill-color"), ("bpy.types.materialgpencilstyle.fill_style*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-fill-style"), ("bpy.types.materialgpencilstyle.mix_factor*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-mix-factor"), ("bpy.types.materialgpencilstyle.pass_index*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-pass-index"), ("bpy.types.nodesocketinterface.description*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-description"), ("bpy.types.rendersettings.dither_intensity*", "render/output/properties/post_processing.html#bpy-types-rendersettings-dither-intensity"), + ("bpy.types.rendersettings.film_transparent*", "render/cycles/render_settings/film.html#bpy-types-rendersettings-film-transparent"), ("bpy.types.rendersettings.simplify_volumes*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-volumes"), ("bpy.types.rendersettings.use_render_cache*", "render/output/properties/output.html#bpy-types-rendersettings-use-render-cache"), ("bpy.types.rendersettings.use_save_buffers*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-save-buffers"), ("bpy.types.rendersettings.use_single_layer*", "render/layers/view_layer.html#bpy-types-rendersettings-use-single-layer"), - ("bpy.types.rendersettings_simplify_gpencil*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil"), ("bpy.types.sceneeevee.use_taa_reprojection*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-use-taa-reprojection"), ("bpy.types.sequenceeditor.use_overlay_lock*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-overlay-lock"), ("bpy.types.spaceoutliner.use_filter_object*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object"), @@ -484,6 +605,7 @@ url_manual_mapping = ( ("bpy.types.colormanagedviewsettings.gamma*", "render/color_management.html#bpy-types-colormanagedviewsettings-gamma"), ("bpy.types.compositornodeplanetrackdeform*", "compositing/types/distort/plane_track_deform.html#bpy-types-compositornodeplanetrackdeform"), ("bpy.types.curve.bevel_factor_mapping_end*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-end"), + ("bpy.types.cyclescamerasettings.longitude*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-longitude"), ("bpy.types.editbone.bbone_handle_type_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-type-end"), ("bpy.types.editbone.use_endroll_as_inroll*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-use-endroll-as-inroll"), ("bpy.types.fluiddomainsettings.cache_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-type"), @@ -494,6 +616,11 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.time_scale*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-time-scale"), ("bpy.types.fluidflowsettings.texture_size*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-texture-size"), ("bpy.types.fluidflowsettings.use_absolute*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-absolute"), + ("bpy.types.freestylelineset.select_border*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-border"), + ("bpy.types.freestylelineset.select_crease*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-crease"), + ("bpy.types.freestylelinestyle.chain_count*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-chain-count"), + ("bpy.types.freestylelinestyle.use_sorting*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-sorting"), + ("bpy.types.freestylesettings.crease_angle*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-crease-angle"), ("bpy.types.geometrynodeattributecolorramp*", "modeling/geometry_nodes/attribute/attribute_color_ramp.html#bpy-types-geometrynodeattributecolorramp"), ("bpy.types.geometrynodeattributeproximity*", "modeling/geometry_nodes/attribute/attribute_proximity.html#bpy-types-geometrynodeattributeproximity"), ("bpy.types.geometrynodeattributerandomize*", "modeling/geometry_nodes/attribute/attribute_randomize.html#bpy-types-geometrynodeattributerandomize"), @@ -501,9 +628,9 @@ url_manual_mapping = ( ("bpy.types.geometrynodeseparatecomponents*", "modeling/geometry_nodes/geometry/separate_components.html#bpy-types-geometrynodeseparatecomponents"), ("bpy.types.geometrynodesubdivisionsurface*", "modeling/geometry_nodes/mesh/subdivision_surface.html#bpy-types-geometrynodesubdivisionsurface"), ("bpy.types.imageformatsettings.color_mode*", "render/output/properties/output.html#bpy-types-imageformatsettings-color-mode"), - ("bpy.types.linestyle*modifier_alongstroke*", "render/freestyle/parameter_editor/line_style/modifiers/color/along_stroke.html#bpy-types-linestyle-modifier-alongstroke"), - ("bpy.types.linestyle*modifier_creaseangle*", "render/freestyle/parameter_editor/line_style/modifiers/color/crease_angle.html#bpy-types-linestyle-modifier-creaseangle"), - ("bpy.types.linestylecolormodifier_tangent*", "render/freestyle/parameter_editor/line_style/modifiers/color/tangent.html#bpy-types-linestylecolormodifier-tangent"), + ("bpy.types.linestyle*modifier_alongstroke*", "render/freestyle/view_layer/line_style/modifiers/color/along_stroke.html#bpy-types-linestyle-modifier-alongstroke"), + ("bpy.types.linestyle*modifier_creaseangle*", "render/freestyle/view_layer/line_style/modifiers/color/crease_angle.html#bpy-types-linestyle-modifier-creaseangle"), + ("bpy.types.linestylecolormodifier_tangent*", "render/freestyle/view_layer/line_style/modifiers/color/tangent.html#bpy-types-linestylecolormodifier-tangent"), ("bpy.types.materialgpencilstyle.mix_color*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-mix-color"), ("bpy.types.materialgpencilstyle.show_fill*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-show-fill"), ("bpy.types.mesh.use_mirror_vertex_group_x*", "sculpt_paint/weight_paint/tool_settings/symmetry.html#bpy-types-mesh-use-mirror-vertex-group-x"), @@ -515,6 +642,7 @@ url_manual_mapping = ( ("bpy.types.nodesocketinterface.hide_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-hide-value"), ("bpy.types.objectlineart.crease_threshold*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-crease-threshold"), ("bpy.types.rendersettings.use_compositing*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-compositing"), + ("bpy.types.rendersettings.use_motion_blur*", "render/cycles/render_settings/motion_blur.html#bpy-types-rendersettings-use-motion-blur"), ("bpy.types.rendersettings.use_placeholder*", "render/output/properties/output.html#bpy-types-rendersettings-use-placeholder"), ("bpy.types.shadernodesubsurfacescattering*", "render/shader_nodes/shader/sss.html#bpy-types-shadernodesubsurfacescattering"), ("bpy.types.spaceclipeditor.lock_selection*", "editors/clip/introduction.html#bpy-types-spaceclipeditor-lock-selection"), @@ -544,6 +672,9 @@ url_manual_mapping = ( ("bpy.types.colormanagedviewsettings.look*", "render/color_management.html#bpy-types-colormanagedviewsettings-look"), ("bpy.types.compositornodecolorcorrection*", "compositing/types/color/color_correction.html#bpy-types-compositornodecolorcorrection"), ("bpy.types.compositornodemoviedistortion*", "compositing/types/distort/movie_distortion.html#bpy-types-compositornodemoviedistortion"), + ("bpy.types.cyclescamerasettings.latitude*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-latitude"), + ("bpy.types.cyclesrendersettings.caustics*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-caustics"), + ("bpy.types.cyclesrendersettings.denoiser*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoiser"), ("bpy.types.editbone.use_inherit_rotation*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-inherit-rotation"), ("bpy.types.fluiddomainsettings.use_guide*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-use-guide"), ("bpy.types.fluiddomainsettings.use_noise*", "physics/fluid/type/domain/gas/noise.html#bpy-types-fluiddomainsettings-use-noise"), @@ -555,6 +686,11 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.temperature*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-temperature"), ("bpy.types.fluidflowsettings.use_texture*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-texture"), ("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelopecontrolpoint"), + ("bpy.types.freestylelinestyle.length_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-length-max"), + ("bpy.types.freestylelinestyle.length_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-length-min"), + ("bpy.types.freestylelinestyle.sort_order*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-sort-order"), + ("bpy.types.freestylelinestyle.split_dash*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-split-dash"), + ("bpy.types.freestylesettings.use_culling*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-culling"), ("bpy.types.geometrynodeattributecurvemap*", "modeling/geometry_nodes/attribute/attribute_curve_map.html#bpy-types-geometrynodeattributecurvemap"), ("bpy.types.geometrynodeattributemaprange*", "modeling/geometry_nodes/attribute/attribute_map_range.html#bpy-types-geometrynodeattributemaprange"), ("bpy.types.geometrynodeattributetransfer*", "modeling/geometry_nodes/attribute/attribute_transfer.html#bpy-types-geometrynodeattributetransfer"), @@ -564,6 +700,7 @@ url_manual_mapping = ( ("bpy.types.movietrackingcamera.principal*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-principal"), ("bpy.types.object.use_camera_lock_parent*", "scene_layout/object/properties/relations.html#bpy-types-object-use-camera-lock-parent"), ("bpy.types.object.visible_volume_scatter*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-volume-scatter"), + ("bpy.types.rendersettings.line_thickness*", "render/freestyle/render.html#bpy-types-rendersettings-line-thickness"), ("bpy.types.rendersettings.pixel_aspect_x*", "render/output/properties/format.html#bpy-types-rendersettings-pixel-aspect-x"), ("bpy.types.rendersettings.pixel_aspect_y*", "render/output/properties/format.html#bpy-types-rendersettings-pixel-aspect-y"), ("bpy.types.rigidbodyconstraint.use_limit*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-limit"), @@ -600,8 +737,13 @@ url_manual_mapping = ( ("bpy.types.brush.use_pose_lock_rotation*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-use-pose-lock-rotation"), ("bpy.types.compositornodebrightcontrast*", "compositing/types/color/bright_contrast.html#bpy-types-compositornodebrightcontrast"), ("bpy.types.compositornodedoubleedgemask*", "compositing/types/matte/double_edge_mask.html#bpy-types-compositornodedoubleedgemask"), + ("bpy.types.cyclesrendersettings.samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-samples"), ("bpy.types.fluiddomainsettings.clipping*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-clipping"), ("bpy.types.fluiddomainsettings.use_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-use-mesh"), + ("bpy.types.freestylelinestyle.angle_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-angle-max"), + ("bpy.types.freestylelinestyle.angle_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-angle-min"), + ("bpy.types.freestylelinestyle.split_gap*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-split-gap"), + ("bpy.types.freestylelinestyle.use_nodes*", "render/freestyle/view_layer/line_style/texture.html#bpy-types-freestylelinestyle-use-nodes"), ("bpy.types.geometrynodeattributecompare*", "modeling/geometry_nodes/attribute/attribute_compare.html#bpy-types-geometrynodeattributecompare"), ("bpy.types.geometrynodeattributeconvert*", "modeling/geometry_nodes/attribute/attribute_convert.html#bpy-types-geometrynodeattributeconvert"), ("bpy.types.geometrynodeselectbymaterial*", "modeling/geometry_nodes/material/select_by_material.html#bpy-types-geometrynodeselectbymaterial"), @@ -660,13 +802,17 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.gravity*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-gravity"), ("bpy.types.fluidflowsettings.flow_type*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-flow-type"), ("bpy.types.fluidflowsettings.subframes*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-subframes"), + ("bpy.types.freestylelineset.collection*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-collection"), + ("bpy.types.freestylelineset.visibility*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-visibility"), + ("bpy.types.freestylelinestyle.chaining*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-chaining"), + ("bpy.types.freestylelinestyle.sort_key*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-sort-key"), ("bpy.types.geometrynodeattributeremove*", "modeling/geometry_nodes/attribute/attribute_remove.html#bpy-types-geometrynodeattributeremove"), ("bpy.types.geometrynodematerialreplace*", "modeling/geometry_nodes/material/replace.html#bpy-types-geometrynodematerialreplace"), ("bpy.types.geometrynodepointdistribute*", "modeling/geometry_nodes/point/point_distribute.html#bpy-types-geometrynodepointdistribute"), ("bpy.types.gpencillayer.use_mask_layer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-mask-layer"), ("bpy.types.greasepencil.use_curve_edit*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-curve-edit"), ("bpy.types.imagepaint.screen_grab_size*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-screen-grab-size"), - ("bpy.types.linestyle*modifier_material*", "render/freestyle/parameter_editor/line_style/modifiers/color/material.html#bpy-types-linestyle-modifier-material"), + ("bpy.types.linestyle*modifier_material*", "render/freestyle/view_layer/line_style/modifiers/color/material.html#bpy-types-linestyle-modifier-material"), ("bpy.types.movietrackingcamera.brown_k*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-brown-k"), ("bpy.types.movietrackingcamera.brown_p*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-brown-p"), ("bpy.types.object.visible_transmission*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-transmission"), @@ -774,6 +920,7 @@ url_manual_mapping = ( ("bpy.ops.outliner.collection_disable*", "editors/outliner/editing.html#bpy-ops-outliner-collection-disable"), ("bpy.ops.outliner.collection_isolate*", "editors/outliner/editing.html#bpy-ops-outliner-collection-isolate"), ("bpy.ops.pose.visual_transform_apply*", "animation/armatures/posing/editing/apply.html#bpy-ops-pose-visual-transform-apply"), + ("bpy.ops.render.shutter_curve_preset*", "render/cycles/render_settings/motion_blur.html#bpy-ops-render-shutter-curve-preset"), ("bpy.ops.sequencer.view_ghost_border*", "video_editing/preview/sidebar.html#bpy-ops-sequencer-view-ghost-border"), ("bpy.ops.ui.override_type_set_button*", "files/linked_libraries/library_overrides.html#bpy-ops-ui-override-type-set-button"), ("bpy.ops.view3d.blenderkit_asset_bar*", "addons/3d_view/blenderkit.html#bpy-ops-view3d-blenderkit-asset-bar"), @@ -792,10 +939,13 @@ url_manual_mapping = ( ("bpy.types.compositornodeellipsemask*", "compositing/types/matte/ellipse_mask.html#bpy-types-compositornodeellipsemask"), ("bpy.types.compositornodesplitviewer*", "compositing/types/output/split_viewer.html#bpy-types-compositornodesplitviewer"), ("bpy.types.curve.render_resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-render-resolution-u"), + ("bpy.types.cyclesrendersettings.seed*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-seed"), ("bpy.types.dynamicpaintbrushsettings*", "physics/dynamic_paint/brush.html#bpy-types-dynamicpaintbrushsettings"), ("bpy.types.editbone.use_scale_easing*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-use-scale-easing"), ("bpy.types.fluiddomainsettings.alpha*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-alpha"), ("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"), + ("bpy.types.freestylelineset.qi_start*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-qi-start"), + ("bpy.types.freestylelinestyle.rounds*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-rounds"), ("bpy.types.geometrynodeattributefill*", "modeling/geometry_nodes/attribute/attribute_fill.html#bpy-types-geometrynodeattributefill"), ("bpy.types.geometrynodeattributemath*", "modeling/geometry_nodes/attribute/attribute_math.html#bpy-types-geometrynodeattributemath"), ("bpy.types.geometrynodecurvetopoints*", "modeling/geometry_nodes/curve/curve_to_points.html#bpy-types-geometrynodecurvetopoints"), @@ -833,7 +983,9 @@ url_manual_mapping = ( ("bpy.types.spaceuveditor.lock_bounds*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-lock-bounds"), ("bpy.types.spline.tilt_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-tilt-interpolation"), ("bpy.types.transformorientation.name*", "editors/3dview/controls/orientation.html#bpy-types-transformorientation-name"), + ("bpy.types.viewlayer.use_motion_blur*", "render/layers/introduction.html#bpy-types-viewlayer-use-motion-blur"), ("bpy.types.volumedisplay.slice_depth*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-depth"), + ("bpy.types.worldmistsettings.falloff*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-falloff"), ("bpy.ops.clip.lock_selection_toggle*", "editors/clip/introduction.html#bpy-ops-clip-lock-selection-toggle"), ("bpy.ops.mesh.customdata_mask_clear*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-mesh-customdata-mask-clear"), ("bpy.ops.mesh.extrude_vertices_move*", "modeling/meshes/editing/vertex/extrude_vertices.html#bpy-ops-mesh-extrude-vertices-move"), @@ -879,6 +1031,9 @@ url_manual_mapping = ( ("bpy.types.curve.bevel_factor_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-start"), ("bpy.types.fluiddomainsettings.beta*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-beta"), ("bpy.types.fluidmodifier.fluid_type*", "physics/fluid/type/index.html#bpy-types-fluidmodifier-fluid-type"), + ("bpy.types.freestylelineset.exclude*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-exclude"), + ("bpy.types.freestylelinestyle.alpha*", "render/freestyle/view_layer/line_style/alpha.html#bpy-types-freestylelinestyle-alpha"), + ("bpy.types.freestylelinestyle.color*", "render/freestyle/view_layer/line_style/color.html#bpy-types-freestylelinestyle-color"), ("bpy.types.functionnodefloatcompare*", "modeling/geometry_nodes/utilities/float_compare.html#bpy-types-functionnodefloatcompare"), ("bpy.types.geometrynodeattributemix*", "modeling/geometry_nodes/attribute/attribute_mix.html#bpy-types-geometrynodeattributemix"), ("bpy.types.geometrynodecurvereverse*", "modeling/geometry_nodes/curve/curve_reverse.html#bpy-types-geometrynodecurvereverse"), @@ -888,7 +1043,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodevolumetomesh*", "modeling/geometry_nodes/volume/volume_to_mesh.html#bpy-types-geometrynodevolumetomesh"), ("bpy.types.image.use_half_precision*", "editors/image/image_settings.html#bpy-types-image-use-half-precision"), ("bpy.types.imagepaint.interpolation*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-types-imagepaint-interpolation"), - ("bpy.types.linestyle*modifier_noise*", "render/freestyle/parameter_editor/line_style/modifiers/color/noise.html#bpy-types-linestyle-modifier-noise"), + ("bpy.types.linestyle*modifier_noise*", "render/freestyle/view_layer/line_style/modifiers/color/noise.html#bpy-types-linestyle-modifier-noise"), ("bpy.types.maintainvolumeconstraint*", "animation/constraints/transform/maintain_volume.html#bpy-types-maintainvolumeconstraint"), ("bpy.types.mesh.use_mirror_topology*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-topology"), ("bpy.types.movieclip.display_aspect*", "editors/clip/display/clip_display.html#bpy-types-movieclip-display-aspect"), @@ -960,6 +1115,9 @@ url_manual_mapping = ( ("bpy.types.editbone.bbone_curveinz*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-curveinz"), ("bpy.types.editbone.bbone_scaleout*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-scaleout"), ("bpy.types.editbone.bbone_segments*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-segments"), + ("bpy.types.freestylelineset.qi_end*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-qi-end"), + ("bpy.types.freestylelinestyle.caps*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-caps"), + ("bpy.types.freestylelinestyle.dash*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-dash"), ("bpy.types.freestylemodulesettings*", "render/freestyle/python.html#bpy-types-freestylemodulesettings"), ("bpy.types.functionnodebooleanmath*", "modeling/geometry_nodes/utilities/boolean_math.html#bpy-types-functionnodebooleanmath"), ("bpy.types.functionnodeinputstring*", "modeling/geometry_nodes/input/string.html#bpy-types-functionnodeinputstring"), @@ -995,8 +1153,11 @@ url_manual_mapping = ( ("bpy.types.simplifygpencilmodifier*", "grease_pencil/modifiers/generate/simplify.html#bpy-types-simplifygpencilmodifier"), ("bpy.types.spacegrapheditor.cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-cursor"), ("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"), - ("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer.html#bpy-types-viewlayer-use-freestyle"), + ("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer/freestyle.html#bpy-types-viewlayer-use-freestyle"), ("bpy.types.volumedisplay.use_slice*", "modeling/volumes/properties.html#bpy-types-volumedisplay-use-slice"), + ("bpy.types.worldlighting.ao_factor*", "render/cycles/world_settings.html#bpy-types-worldlighting-ao-factor"), + ("bpy.types.worldmistsettings.depth*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-depth"), + ("bpy.types.worldmistsettings.start*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-start"), ("bpy.ops.armature.armature_layers*", "animation/armatures/bones/editing/change_layers.html#bpy-ops-armature-armature-layers"), ("bpy.ops.armature.select_linked()*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-linked"), ("bpy.ops.clip.stabilize_2d_select*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-stabilize-2d-select"), @@ -1066,6 +1227,8 @@ url_manual_mapping = ( ("bpy.types.editbone.bbone_rollout*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-rollout"), ("bpy.types.editbone.bbone_scalein*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-scalein"), ("bpy.types.editbone.inherit_scale*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-inherit-scale"), + ("bpy.types.freestylelinestyle.gap*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-gap"), + ("bpy.types.freestylesettings.mode*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-mode"), ("bpy.types.geometrynodeconvexhull*", "modeling/geometry_nodes/geometry/convex_hull.html#bpy-types-geometrynodeconvexhull"), ("bpy.types.geometrynodefloattoint*", "modeling/geometry_nodes/utilities/float_to_int.html#bpy-types-geometrynodefloattoint"), ("bpy.types.geometrynodeisviewport*", "modeling/geometry_nodes/input/is_viewport.html#bpy-types-geometrynodeisviewport"), @@ -1077,6 +1240,7 @@ url_manual_mapping = ( ("bpy.types.keyframe.interpolation*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-interpolation"), ("bpy.types.latticegpencilmodifier*", "grease_pencil/modifiers/deform/lattice.html#bpy-types-latticegpencilmodifier"), ("bpy.types.lineartgpencilmodifier*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier"), + ("bpy.types.material.line_priority*", "render/freestyle/material.html#bpy-types-material-line-priority"), ("bpy.types.mesh.auto_smooth_angle*", "modeling/meshes/structure.html#bpy-types-mesh-auto-smooth-angle"), ("bpy.types.modifier.show_viewport*", "modeling/modifiers/introduction.html#bpy-types-modifier-show-viewport"), ("bpy.types.object.visible_diffuse*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-diffuse"), @@ -1097,6 +1261,7 @@ url_manual_mapping = ( ("bpy.types.volumedisplacemodifier*", "modeling/modifiers/deform/volume_displace.html#bpy-types-volumedisplacemodifier"), ("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"), ("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"), + ("bpy.types.worldlighting.distance*", "render/cycles/world_settings.html#bpy-types-worldlighting-distance"), ("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"), ("bpy.ops.armature.calculate_roll*", "animation/armatures/bones/editing/bone_roll.html#bpy-ops-armature-calculate-roll"), ("bpy.ops.armature.duplicate_move*", "animation/armatures/bones/editing/duplicate.html#bpy-ops-armature-duplicate-move"), @@ -1215,6 +1380,7 @@ url_manual_mapping = ( ("bpy.types.spline.use_endpoint_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-use-endpoint-u"), ("bpy.types.surfacedeformmodifier*", "modeling/modifiers/deform/surface_deform.html#bpy-types-surfacedeformmodifier"), ("bpy.types.toolsettings.use_snap*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap"), + ("bpy.types.viewlayer.use_volumes*", "render/layers/introduction.html#bpy-types-viewlayer-use-volumes"), ("bpy.types.volume.frame_duration*", "modeling/volumes/properties.html#bpy-types-volume-frame-duration"), ("bpy.types.volumedisplay.density*", "modeling/volumes/properties.html#bpy-types-volumedisplay-density"), ("bpy.types.volumerender.clipping*", "modeling/volumes/properties.html#bpy-types-volumerender-clipping"), @@ -1383,6 +1549,7 @@ url_manual_mapping = ( ("bpy.types.hookgpencilmodifier*", "grease_pencil/modifiers/deform/hook.html#bpy-types-hookgpencilmodifier"), ("bpy.types.imageformatsettings*", "files/media/image_formats.html#bpy-types-imageformatsettings"), ("bpy.types.kinematicconstraint*", "animation/constraints/tracking/ik_solver.html#bpy-types-kinematicconstraint"), + ("bpy.types.material.line_color*", "render/freestyle/material.html#bpy-types-material-line-color"), ("bpy.types.mesh.use_paint_mask*", "sculpt_paint/brush/introduction.html#bpy-types-mesh-use-paint-mask"), ("bpy.types.movietrackingcamera*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera"), ("bpy.types.object.display_type*", "scene_layout/object/properties/display.html#bpy-types-object-display-type"), @@ -1488,7 +1655,7 @@ url_manual_mapping = ( ("bpy.types.curvepaintsettings*", "modeling/curves/tools/draw.html#bpy-types-curvepaintsettings"), ("bpy.types.fcurve.array_index*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-array-index"), ("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"), - ("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"), + ("bpy.types.freestylelinestyle*", "render/freestyle/view_layer/line_style/index.html#bpy-types-freestylelinestyle"), ("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/gamma_cross.html#bpy-types-gammacrosssequence"), ("bpy.types.geometrynodeswitch*", "modeling/geometry_nodes/utilities/switch.html#bpy-types-geometrynodeswitch"), ("bpy.types.geometrynodeviewer*", "modeling/geometry_nodes/output/viewer.html#bpy-types-geometrynodeviewer"), @@ -1606,7 +1773,7 @@ url_manual_mapping = ( ("bpy.types.fcurve.color_mode*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-color-mode"), ("bpy.types.fluidflowsettings*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings"), ("bpy.types.fmodifierenvelope*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelope"), - ("bpy.types.freestylesettings*", "render/freestyle/view_layer.html#bpy-types-freestylesettings"), + ("bpy.types.freestylesettings*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings"), ("bpy.types.geometrynodegroup*", "modeling/geometry_nodes/group.html#bpy-types-geometrynodegroup"), ("bpy.types.gpencillayer.hide*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-hide"), ("bpy.types.gpencillayer.lock*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-lock"), @@ -1710,7 +1877,7 @@ url_manual_mapping = ( ("bpy.types.editbone.bbone_z*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-z"), ("bpy.types.fcurve.data_path*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-data-path"), ("bpy.types.fmodifierstepped*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierstepped"), - ("bpy.types.freestylelineset*", "render/freestyle/parameter_editor/line_set.html#bpy-types-freestylelineset"), + ("bpy.types.freestylelineset*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset"), ("bpy.types.mask.frame_start*", "movie_clip/masking/sidebar.html#bpy-types-mask-frame-start"), ("bpy.types.mesh.*customdata*", "modeling/meshes/properties/custom_data.html#bpy-types-mesh-customdata"), ("bpy.types.multicamsequence*", "video_editing/sequencer/strips/effects/multicam.html#bpy-types-multicamsequence"), @@ -2005,6 +2172,7 @@ url_manual_mapping = ( ("bpy.types.viewlayer.use*", "render/layers/view_layer.html#bpy-types-viewlayer-use"), ("bpy.types.volumedisplay*", "modeling/volumes/properties.html#bpy-types-volumedisplay"), ("bpy.types.windowmanager*", "interface/index.html#bpy-types-windowmanager"), + ("bpy.types.worldlighting*", "render/cycles/world_settings.html#bpy-types-worldlighting"), ("bpy.ops.*.select_lasso*", "interface/selecting.html#bpy-ops-select-lasso"), ("bpy.ops.armature.align*", "animation/armatures/bones/editing/transform.html#bpy-ops-armature-align"), ("bpy.ops.armature.split*", "animation/armatures/bones/editing/split.html#bpy-ops-armature-split"), @@ -2240,7 +2408,7 @@ url_manual_mapping = ( ("bpy.types.editbone*", "animation/armatures/bones/editing/index.html#bpy-types-editbone"), ("bpy.types.facemaps*", "modeling/meshes/properties/object_data.html#bpy-types-facemaps"), ("bpy.types.keyframe*", "animation/keyframes/index.html#bpy-types-keyframe"), - ("bpy.types.linesets*", "render/freestyle/parameter_editor/line_set.html#bpy-types-linesets"), + ("bpy.types.linesets*", "render/freestyle/view_layer/line_set.html#bpy-types-linesets"), ("bpy.types.metaball*", "modeling/metas/index.html#bpy-types-metaball"), ("bpy.types.modifier*", "modeling/modifiers/index.html#bpy-types-modifier"), ("bpy.types.nlastrip*", "editors/nla/strips.html#bpy-types-nlastrip"), From 13a4bccdb196770b4f357c2a3c312bd4629ecb36 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 20 Sep 2021 22:01:28 +0200 Subject: [PATCH 0037/1500] Asset Browser: Redraw sidebars on mode switches There may be mode specific panels for some assets in the navigation or the asset metadata sidebar. For example the pose library will likely do this. So let the regions redraw on mode changes. --- source/blender/editors/space_file/space_file.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index a4f36c2a6ee..42a9c4aa2d5 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -738,8 +738,18 @@ static void file_tools_region_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } -static void file_tools_region_listener(const wmRegionListenerParams *UNUSED(listener_params)) +static void file_tools_region_listener(const wmRegionListenerParams *listener_params) { + const wmNotifier *wmn = listener_params->notifier; + ARegion *region = listener_params->region; + + switch (wmn->category) { + case NC_SCENE: + if (ELEM(wmn->data, ND_MODE)) { + ED_region_tag_redraw(region); + } + break; + } } static void file_tool_props_region_listener(const wmRegionListenerParams *listener_params) @@ -754,6 +764,11 @@ static void file_tool_props_region_listener(const wmRegionListenerParams *listen ED_region_tag_redraw(region); } break; + case NC_SCENE: + if (ELEM(wmn->data, ND_MODE)) { + ED_region_tag_redraw(region); + } + break; } } From 05f3f11d553e97f27fc88b1dada9387acdb768a6 Mon Sep 17 00:00:00 2001 From: Victor-Louis De Gusseme Date: Mon, 20 Sep 2021 18:39:39 -0500 Subject: [PATCH 0038/1500] Geometry Nodes: Attribute Statistic Node This nodes evaluates a field on a geometry and outputs various statistics about the entire data set, like min, max, or even the standard deviation. It works for float and vector types currently, though more types could be supported in the future. - All statistics are calculated element-wise for vectors. - "Product" was not added since the result could very easily overflow. - The "Size" output was not added since it isn't specific to an attribute and would fit better in another node. The implementation shares work as much as possible when multiple statistics are needed. This node has been in development since the beginning of this year, with additions from Johnny Matthews and Hans Goudey. Differential Revision: https://developer.blender.org/D10202 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/blenlib/BLI_array.hh | 15 + source/blender/makesrna/intern/rna_nodetree.c | 34 ++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_geo_attribute_statistic.cc | 378 ++++++++++++++++++ 9 files changed, 433 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index aea9cbc5c62..bc024ac96cf 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -503,6 +503,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_fields_legacy_poll), NodeItem("GeometryNodeAttributeCapture", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeAttributeStatistic", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ NodeItem("ShaderNodeMixRGB"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 8e82ab6d6be..45ce843ef78 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1494,6 +1494,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MATERIAL_SELECTION 1081 #define GEO_NODE_MATERIAL_ASSIGN 1082 #define GEO_NODE_REALIZE_INSTANCES 1083 +#define GEO_NODE_ATTRIBUTE_STATISTIC 1084 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3a76cbf6f84..7d679cfd076 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5175,6 +5175,7 @@ static void registerGeometryNodes() register_node_type_geo_attribute_randomize(); register_node_type_geo_attribute_remove(); register_node_type_geo_attribute_separate_xyz(); + register_node_type_geo_attribute_statistic(); register_node_type_geo_attribute_transfer(); register_node_type_geo_attribute_vector_math(); register_node_type_geo_attribute_vector_rotate(); diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index fc8fc615feb..352bf379d4d 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -276,6 +276,21 @@ class Array { initialized_fill_n(data_, size_, value); } + /** + * Return a reference to the first element in the array. + * This invokes undefined behavior when the array is empty. + */ + const T &first() const + { + BLI_assert(size_ > 0); + return *data_; + } + T &first() + { + BLI_assert(size_ > 0); + return *data_; + } + /** * Return a reference to the last element in the array. * This invokes undefined behavior when the array is empty. diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 76e37dbcdbc..ae5f8d5f5da 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2168,6 +2168,17 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeFill_type_itemf(bContext return itemf_function_check(rna_enum_attribute_type_items, attribute_fill_type_supported); } +static bool attribute_statistic_type_supported(const EnumPropertyItem *item) +{ + return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3); +} +static const EnumPropertyItem *rna_GeometryNodeAttributeStatistic_type_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_type_items, attribute_statistic_type_supported); +} + /** * This bit of ugly code makes sure the float / attribute option shows up instead of * vector / attribute if the node uses an operation that uses a float for input B or C. @@ -9219,6 +9230,29 @@ static void def_geo_attribute_convert(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_attribute_statistic(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeStatistic_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text( + prop, + "Data Type", + "The data type the attribute is converted to before calculating the results"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom2"); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Which domain to read the data from"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_attribute_math(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index b0fc55fab0c..6da7b0be4e9 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -161,6 +161,7 @@ set(SRC geometry/nodes/node_geo_attribute_remove.cc geometry/nodes/node_geo_attribute_sample_texture.cc geometry/nodes/node_geo_attribute_separate_xyz.cc + geometry/nodes/node_geo_attribute_statistic.cc geometry/nodes/node_geo_attribute_transfer.cc geometry/nodes/node_geo_attribute_vector_math.cc geometry/nodes/node_geo_attribute_vector_rotate.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 0d31ae2143a..5e8abce6eb8 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -48,6 +48,7 @@ void register_node_type_geo_attribute_proximity(void); void register_node_type_geo_attribute_randomize(void); void register_node_type_geo_attribute_remove(void); void register_node_type_geo_attribute_separate_xyz(void); +void register_node_type_geo_attribute_statistic(void); void register_node_type_geo_attribute_transfer(void); void register_node_type_geo_attribute_vector_math(void); void register_node_type_geo_attribute_vector_rotate(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index b2f1fa5e83a..0be5459c9e1 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -307,6 +307,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_M DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "") DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc new file mode 100644 index 00000000000..5001034518c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -0,0 +1,378 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BLI_math_base_safe.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Attribute").hide_value(); + b.add_input("Attribute", "Attribute_001").hide_value(); + + b.add_output("Mean"); + b.add_output("Median"); + b.add_output("Sum"); + b.add_output("Min"); + b.add_output("Max"); + b.add_output("Range"); + b.add_output("Standard Deviation"); + b.add_output("Variance"); + + b.add_output("Mean", "Mean_001"); + b.add_output("Median", "Median_001"); + b.add_output("Sum", "Sum_001"); + b.add_output("Min", "Min_001"); + b.add_output("Max", "Max_001"); + b.add_output("Range", "Range_001"); + b.add_output("Standard Deviation", "Standard Deviation_001"); + b.add_output("Variance", "Variance_001"); +} + +static void geo_node_attribute_statistic_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void geo_node_attribute_statistic_init(bNodeTree *UNUSED(tree), bNode *node) +{ + node->custom1 = CD_PROP_FLOAT; + node->custom2 = ATTR_DOMAIN_POINT; +} + +static void geo_node_attribute_statistic_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *socket_geo = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_float_attr = socket_geo->next; + bNodeSocket *socket_float3_attr = socket_float_attr->next; + + bNodeSocket *socket_float_mean = (bNodeSocket *)node->outputs.first; + bNodeSocket *socket_float_median = socket_float_mean->next; + bNodeSocket *socket_float_sum = socket_float_median->next; + bNodeSocket *socket_float_min = socket_float_sum->next; + bNodeSocket *socket_float_max = socket_float_min->next; + bNodeSocket *socket_float_range = socket_float_max->next; + bNodeSocket *socket_float_std = socket_float_range->next; + bNodeSocket *socket_float_variance = socket_float_std->next; + + bNodeSocket *socket_vector_mean = socket_float_variance->next; + bNodeSocket *socket_vector_median = socket_vector_mean->next; + bNodeSocket *socket_vector_sum = socket_vector_median->next; + bNodeSocket *socket_vector_min = socket_vector_sum->next; + bNodeSocket *socket_vector_max = socket_vector_min->next; + bNodeSocket *socket_vector_range = socket_vector_max->next; + bNodeSocket *socket_vector_std = socket_vector_range->next; + bNodeSocket *socket_vector_variance = socket_vector_std->next; + + const CustomDataType data_type = static_cast(node->custom1); + + nodeSetSocketAvailability(socket_float_attr, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_mean, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_median, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_sum, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_min, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_max, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_range, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_std, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_variance, data_type == CD_PROP_FLOAT); + + nodeSetSocketAvailability(socket_float3_attr, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_mean, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_median, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_sum, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_min, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_max, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_range, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_std, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_variance, data_type == CD_PROP_FLOAT3); +} + +template static T compute_sum(const Span data) +{ + return std::accumulate(data.begin(), data.end(), T()); +} + +static float compute_variance(const Span data, const float mean) +{ + if (data.size() <= 1) { + return 0.0f; + } + + float sum_of_squared_differences = std::accumulate( + data.begin(), data.end(), 0.0f, [mean](float accumulator, float value) { + float difference = mean - value; + return accumulator + difference * difference; + }); + + return sum_of_squared_differences / (data.size() - 1); +} + +static float median_of_sorted_span(const Span data) +{ + if (data.is_empty()) { + return 0.0f; + } + + const float median = data[data.size() / 2]; + + /* For spans of even length, the median is the average of the middle two elements. */ + if (data.size() % 2 == 0) { + return (median + data[data.size() / 2 - 1]) * 0.5f; + } + return median; +} +static void set_empty(CustomDataType data_type, GeoNodeExecParams ¶ms) +{ + if (data_type == CD_PROP_FLOAT) { + params.set_output("Mean", 0.0f); + params.set_output("Median", 0.0f); + params.set_output("Sum", 0.0f); + params.set_output("Min", 0.0f); + params.set_output("Max", 0.0f); + params.set_output("Range", 0.0f); + params.set_output("Standard Deviation", 0.0f); + params.set_output("Variance", 0.0f); + } + else if (data_type == CD_PROP_FLOAT3) { + params.set_output("Mean_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Median_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Sum_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Min_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Max_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Range_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Standard Deviation_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Variance_001", float3{0.0f, 0.0f, 0.0f}); + } +} + +static void geo_node_attribute_statistic_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.get_input("Geometry"); + + const bNode &node = params.node(); + const CustomDataType data_type = static_cast(node.custom1); + const AttributeDomain domain = static_cast(node.custom2); + + int64_t total_size = 0; + Vector components = geometry_set.get_components_for_read(); + + for (const GeometryComponent *component : components) { + if (component->attribute_domain_supported(domain)) { + total_size += component->attribute_domain_size(domain); + } + } + if (total_size == 0) { + set_empty(data_type, params); + return; + } + + switch (data_type) { + case CD_PROP_FLOAT: { + const Field input_field = params.get_input>("Attribute"); + Array data = Array(total_size); + int offset = 0; + for (const GeometryComponent *component : components) { + if (component->attribute_domain_supported(domain)) { + GeometryComponentFieldContext field_context{*component, domain}; + const int domain_size = component->attribute_domain_size(domain); + fn::FieldEvaluator data_evaluator{field_context, domain_size}; + MutableSpan component_result = data.as_mutable_span().slice(offset, domain_size); + data_evaluator.add_with_destination(input_field, component_result); + data_evaluator.evaluate(); + offset += domain_size; + } + } + + float mean = 0.0f; + float median = 0.0f; + float sum = 0.0f; + float min = 0.0f; + float max = 0.0f; + float range = 0.0f; + float standard_deviation = 0.0f; + float variance = 0.0f; + const bool sort_required = params.output_is_required("Min") || + params.output_is_required("Max") || + params.output_is_required("Range") || + params.output_is_required("Median"); + const bool sum_required = params.output_is_required("Sum") || + params.output_is_required("Mean"); + const bool variance_required = params.output_is_required("Standard Deviation") || + params.output_is_required("Variance"); + + if (total_size != 0) { + if (sort_required) { + std::sort(data.begin(), data.end()); + median = median_of_sorted_span(data); + + min = data.first(); + max = data.last(); + range = max - min; + } + if (sum_required || variance_required) { + sum = compute_sum(data); + mean = sum / total_size; + + if (variance_required) { + variance = compute_variance(data, mean); + standard_deviation = std::sqrt(variance); + } + } + } + + if (sum_required) { + params.set_output("Sum", sum); + params.set_output("Mean", mean); + } + if (sort_required) { + params.set_output("Min", min); + params.set_output("Max", max); + params.set_output("Range", range); + params.set_output("Median", median); + } + if (variance_required) { + params.set_output("Standard Deviation", standard_deviation); + params.set_output("Variance", variance); + } + break; + } + case CD_PROP_FLOAT3: { + const Field input_field = params.get_input>("Attribute_001"); + + Array data = Array(total_size); + int offset = 0; + for (const GeometryComponent *component : components) { + if (component->attribute_domain_supported(domain)) { + GeometryComponentFieldContext field_context{*component, domain}; + const int domain_size = component->attribute_domain_size(domain); + fn::FieldEvaluator data_evaluator{field_context, domain_size}; + MutableSpan component_result = data.as_mutable_span().slice(offset, domain_size); + data_evaluator.add_with_destination(input_field, component_result); + data_evaluator.evaluate(); + offset += domain_size; + } + } + + float3 median{0}; + float3 min{0}; + float3 max{0}; + float3 range{0}; + float3 sum{0}; + float3 mean{0}; + float3 variance{0}; + float3 standard_deviation{0}; + const bool sort_required = params.output_is_required("Min_001") || + params.output_is_required("Max_001") || + params.output_is_required("Range_001") || + params.output_is_required("Median_001"); + const bool sum_required = params.output_is_required("Sum_001") || + params.output_is_required("Mean_001"); + const bool variance_required = params.output_is_required("Standard Deviation_001") || + params.output_is_required("Variance_001"); + + Array data_x; + Array data_y; + Array data_z; + if (sort_required || variance_required) { + data_x.reinitialize(total_size); + data_y.reinitialize(total_size); + data_z.reinitialize(total_size); + for (const int i : data.index_range()) { + data_x[i] = data[i].x; + data_y[i] = data[i].y; + data_z[i] = data[i].z; + } + } + + if (total_size != 0) { + if (sort_required) { + std::sort(data_x.begin(), data_x.end()); + std::sort(data_y.begin(), data_y.end()); + std::sort(data_z.begin(), data_z.end()); + + const float x_median = median_of_sorted_span(data_x); + const float y_median = median_of_sorted_span(data_y); + const float z_median = median_of_sorted_span(data_z); + median = float3(x_median, y_median, z_median); + + min = float3(data_x.first(), data_y.first(), data_z.first()); + max = float3(data_x.last(), data_y.last(), data_z.last()); + range = max - min; + } + if (sum_required || variance_required) { + sum = compute_sum(data.as_span()); + mean = sum / total_size; + + if (variance_required) { + const float x_variance = compute_variance(data_x, mean.x); + const float y_variance = compute_variance(data_y, mean.y); + const float z_variance = compute_variance(data_z, mean.z); + variance = float3(x_variance, y_variance, z_variance); + standard_deviation = float3( + std::sqrt(variance.x), std::sqrt(variance.y), std::sqrt(variance.z)); + } + } + } + + if (sum_required) { + params.set_output("Sum_001", sum); + params.set_output("Mean_001", mean); + } + if (sort_required) { + params.set_output("Min_001", min); + params.set_output("Max_001", max); + params.set_output("Range_001", range); + params.set_output("Median_001", median); + } + if (variance_required) { + params.set_output("Standard Deviation_001", standard_deviation); + params.set_output("Variance_001", variance); + } + break; + } + default: + break; + } +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_statistic() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_STATISTIC, "Attribute Statistic", NODE_CLASS_ATTRIBUTE, 0); + + ntype.declare = blender::nodes::geo_node_attribute_statistic_declare; + node_type_init(&ntype, blender::nodes::geo_node_attribute_statistic_init); + node_type_update(&ntype, blender::nodes::geo_node_attribute_statistic_update); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_statistic_exec; + ntype.draw_buttons = blender::nodes::geo_node_attribute_statistic_layout; + nodeRegisterType(&ntype); +} From 4472a11017a0c48f77df26d990df2016f457058d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 20 Sep 2021 20:20:39 -0500 Subject: [PATCH 0039/1500] Fix: Crash with single point bezier spline auto handles There's no way to calculate auto or vector handles when there is only one point, and returning early allows avoiding checking for that case later on. --- source/blender/blenkernel/intern/spline_bezier.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 79d2137ee84..b36d7a21669 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -214,6 +214,11 @@ void BezierSpline::ensure_auto_handles() const return; } + if (this->size() == 1) { + auto_handles_dirty_ = false; + return; + } + for (const int i : IndexRange(this->size())) { if (ELEM(HandleType::Auto, handle_types_left_[i], handle_types_right_[i])) { const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i); From 9e939a614ec5cda5dd6e5392bf9c209d21127c33 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 20 Sep 2021 20:22:52 -0500 Subject: [PATCH 0040/1500] Functions: Fix incorrect assert for unused output Since the variable for an output parameter can be null, it is incorrect to use it later on in a reference. --- source/blender/functions/intern/multi_function_procedure.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/functions/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc index fa95e8de71e..986c5dff0c4 100644 --- a/source/blender/functions/intern/multi_function_procedure.cc +++ b/source/blender/functions/intern/multi_function_procedure.cc @@ -419,6 +419,10 @@ bool MFProcedure::validate_initialization() const const MultiFunction &fn = *instruction->fn_; for (const int param_index : fn.param_indices()) { const MFParamType param_type = fn.param_type(param_index); + /* If the parameter was an unneeded output, it could be null. */ + if (!instruction->params_[param_index]) { + continue; + } const MFVariable &variable = *instruction->params_[param_index]; const InitState state = this->find_initialization_state_before_instruction(*instruction, variable); From 17021adceaee28295b89301b4f715b6bcd8d5fca Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 20 Sep 2021 20:23:26 -0500 Subject: [PATCH 0041/1500] Geometry Nodes: Curve Sample Node This node allows sampling positions, tangents, and normals at any arbitrary point along a curve. The curve can include multiple splines, all are taken into account. The node does not yet support transferring generic attributes like radius, because some more general tooling will make that much more feasible and useful in different scenarios. This is a field node, so it is evaluated in the context of a data-flow node like "Set Position". One nice thing about that is it can easily be used to move an entire geometry like the follow path constraint. The point along the curve is chosen either with a factor of the total length of the curve, or a length into the curve, the same choice used in the curve trim node. Differential Revision: https://developer.blender.org/D12565 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/BKE_spline.hh | 1 + .../blender/blenkernel/intern/curve_eval.cc | 17 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesdna/DNA_node_types.h | 5 + source/blender/makesrna/intern/rna_nodetree.c | 24 ++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../geometry/nodes/node_geo_curve_sample.cc | 292 ++++++++++++++++++ 11 files changed, 345 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index bc024ac96cf..b8bb4e551d2 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -527,6 +527,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveFill"), NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), + NodeItem("GeometryNodeCurveSample", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ NodeItem("GeometryNodeCurvePrimitiveLine"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 45ce843ef78..e4e9a6eff3a 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1495,6 +1495,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MATERIAL_ASSIGN 1082 #define GEO_NODE_REALIZE_INSTANCES 1083 #define GEO_NODE_ATTRIBUTE_STATISTIC 1084 +#define GEO_NODE_CURVE_SAMPLE 1085 /** \} */ diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 0fbf39a52fa..541ff19c1cd 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -565,6 +565,7 @@ struct CurveEval { blender::Array control_point_offsets() const; blender::Array evaluated_point_offsets() const; + blender::Array accumulated_spline_lengths() const; void assert_valid_point_attributes() const; }; diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index ea84766943d..8eec7f5dfab 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -143,6 +143,23 @@ blender::Array CurveEval::evaluated_point_offsets() const return offsets; } +/** + * Return the accumulated length at the start of every spline in the curve. + * + * \note The result is one longer than the spline count; the last element is the total length. + */ +blender::Array CurveEval::accumulated_spline_lengths() const +{ + Array spline_lengths(splines_.size() + 1); + float spline_length = 0.0f; + for (const int i : splines_.index_range()) { + spline_lengths[i] = spline_length; + spline_length += splines_[i]->length(); + } + spline_lengths.last() = spline_length; + return spline_lengths; +} + static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) { switch (dna_handle_type) { diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 7d679cfd076..e2891f00119 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5183,6 +5183,7 @@ static void registerGeometryNodes() register_node_type_geo_bounding_box(); register_node_type_geo_collection_info(); register_node_type_geo_convex_hull(); + register_node_type_geo_curve_sample(); register_node_type_geo_curve_endpoints(); register_node_type_geo_curve_fill(); register_node_type_geo_curve_length(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index f4c88333528..49083542fd7 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1452,6 +1452,11 @@ typedef struct NodeGeometryCurveToPoints { uint8_t mode; } NodeGeometryCurveToPoints; +typedef struct NodeGeometryCurveSample { + /* GeometryNodeCurveSampleMode. */ + uint8_t mode; +} NodeGeometryCurveSample; + typedef struct NodeGeometryAttributeTransfer { /* AttributeDomain. */ int8_t domain; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index ae5f8d5f5da..d0bf60d5d02 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9088,6 +9088,30 @@ static void def_geo_curve_primitive_bezier_segment(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_sample(StructRNA *srna) +{ + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_SAMPLE_FACTOR, + "FACTOR", + 0, + "Factor", + "Find sample positions on the curve using a factor of its total length"}, + {GEO_NODE_CURVE_SAMPLE_LENGTH, + "LENGTH", + 0, + "Length", + "Find sample positions on the curve using a distance from its beginning"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSample", "storage"); + + PropertyRNA *prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "Method for sampling input"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_triangulate(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 6da7b0be4e9..842c76935d1 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -170,6 +170,7 @@ set(SRC geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_convex_hull.cc + geometry/nodes/node_geo_curve_sample.cc geometry/nodes/node_geo_curve_endpoints.cc geometry/nodes/node_geo_curve_fill.cc geometry/nodes/node_geo_curve_length.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 5e8abce6eb8..63330b7df62 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -59,6 +59,7 @@ void register_node_type_geo_convex_hull(void); void register_node_type_geo_curve_endpoints(void); void register_node_type_geo_curve_fill(void); void register_node_type_geo_curve_length(void); +void register_node_type_geo_curve_sample(void); void register_node_type_geo_curve_primitive_bezier_segment(void); void register_node_type_geo_curve_primitive_circle(void); void register_node_type_geo_curve_primitive_line(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 0be5459c9e1..918c82dec1c 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -312,6 +312,7 @@ DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Bo DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "") DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "") DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Curve Fill", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc new file mode 100644 index 00000000000..1dbb1f20915 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -0,0 +1,292 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Curve"); + b.add_input("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Length").min(0.0f).subtype(PROP_DISTANCE); + + b.add_output("Position"); + b.add_output("Tangent"); + b.add_output("Normal"); +} + +static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_curve_sample_type_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSample *data = (NodeGeometryCurveSample *)MEM_callocN( + sizeof(NodeGeometryCurveSample), __func__); + data->mode = GEO_NODE_CURVE_SAMPLE_LENGTH; + node->storage = data; +} + +static void geo_node_curve_sample_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)node->storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + + bNodeSocket *factor = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *length = factor->next; + + nodeSetSocketAvailability(factor, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); + nodeSetSocketAvailability(length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); +} + +template static T sample_with_lookup(const Spline::LookupResult lookup, Span data) +{ + return attribute_math::mix2( + lookup.factor, data[lookup.evaluated_index], data[lookup.next_evaluated_index]); +} + +class SampleCurveFunction : public fn::MultiFunction { + private: + /** + * The function holds a geometry set instead of a curve or a curve component in order to + * maintain a reference to the geometry while the field tree is being built, so that the + * curve is not freed before the function can execute. + */ + GeometrySet geometry_set_; + /** + * To support factor inputs, the node adds another field operation before this one to multiply by + * the curve's total length. Since that must calculate the spline lengths anyway, store them to + * reuse the calculation. + */ + Array spline_lengths_; + /** The last member of #spline_lengths_, extracted for convenience. */ + const float total_length_; + + public: + SampleCurveFunction(GeometrySet geometry_set, Array spline_lengths) + : geometry_set_(std::move(geometry_set)), + spline_lengths_(std::move(spline_lengths)), + total_length_(spline_lengths_.last()) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Curve Sample"}; + signature.single_input("Length"); + signature.single_output("Position"); + signature.single_output("Tangent"); + signature.single_output("Normal"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + // MutableSpan sampled_positions = params.uninitialized_single_output(1, + // "Position"); + // MutableSpan sampled_tangents = params.uninitialized_single_output(2, + // "Tangent"); + // MutableSpan sampled_normals = params.uninitialized_single_output(3, + // "Normal"); + MutableSpan sampled_positions = params.uninitialized_single_output_if_required( + 1, "Position"); + MutableSpan sampled_tangents = params.uninitialized_single_output_if_required( + 2, "Tangent"); + MutableSpan sampled_normals = params.uninitialized_single_output_if_required( + 3, "Normal"); + + auto return_default = [&]() { + if (!sampled_positions.is_empty()) { + sampled_positions.fill_indices(mask, {0, 0, 0}); + } + if (!sampled_tangents.is_empty()) { + sampled_tangents.fill_indices(mask, {0, 0, 0}); + } + if (!sampled_normals.is_empty()) { + sampled_normals.fill_indices(mask, {0, 0, 0}); + } + }; + + if (!geometry_set_.has_curve()) { + return return_default(); + } + + const CurveComponent *curve_component = geometry_set_.get_component_for_read(); + const CurveEval *curve = curve_component->get_for_read(); + Span splines = curve->splines(); + if (splines.is_empty()) { + return return_default(); + } + + const VArray &lengths_varray = params.readonly_single_input(0, "Length"); + const VArray_Span lengths{lengths_varray}; +#ifdef DEBUG + for (const float length : lengths) { + /* Lengths must be in range of the curve's total length. This is ensured in + * #get_length_input_field by adding another multi-function before this one + * to clamp the lengths. */ + BLI_assert(length >= 0.0f && length <= total_length_); + } +#endif + + Array spline_indices(mask.min_array_size()); + for (const int i : mask) { + const float *offset = std::lower_bound( + spline_lengths_.begin(), spline_lengths_.end(), lengths[i]); + const int index = offset - spline_lengths_.data() - 1; + spline_indices[i] = std::max(index, 0); + } + + Array lookups(mask.min_array_size()); + for (const int i : mask) { + const float length_in_spline = lengths[i] - spline_lengths_[spline_indices[i]]; + lookups[i] = splines[spline_indices[i]]->lookup_evaluated_length(length_in_spline); + } + + if (!sampled_positions.is_empty()) { + for (const int i : mask) { + const Spline::LookupResult &lookup = lookups[i]; + const Span evaluated_positions = splines[spline_indices[i]]->evaluated_positions(); + sampled_positions[i] = sample_with_lookup(lookup, evaluated_positions); + } + } + + if (!sampled_tangents.is_empty()) { + for (const int i : mask) { + const Spline::LookupResult &lookup = lookups[i]; + const Span evaluated_tangents = splines[spline_indices[i]]->evaluated_tangents(); + sampled_tangents[i] = sample_with_lookup(lookup, evaluated_tangents).normalized(); + } + } + + if (!sampled_normals.is_empty()) { + for (const int i : mask) { + const Spline::LookupResult &lookup = lookups[i]; + const Span evaluated_normals = splines[spline_indices[i]]->evaluated_normals(); + sampled_normals[i] = sample_with_lookup(lookup, evaluated_normals).normalized(); + } + } + } +}; + +/** + * Pre-process the lengths or factors used for the sampling, turning factors into lengths, and + * clamping between zero and the total length of the curve. Do this as a separate operation in the + * field tree to make the sampling simpler, and to let the evaluator optimize better. + * + * \todo Use a mutable single input instead when they are supported. + */ +static Field get_length_input_field(const GeoNodeExecParams ¶ms, + const float curve_total_length) +{ + const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)params.node().storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + + if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + return params.get_input>("Length"); + Field length_field = params.get_input>("Length"); + auto clamp_fn = std::make_unique>( + __func__, [curve_total_length](float length) { + return std::clamp(length, 0.0f, curve_total_length); + }); + auto clamp_op = std::make_shared( + FieldOperation(std::move(clamp_fn), {std::move(length_field)})); + + return Field(std::move(clamp_op), 0); + } + + Field factor_field = params.get_input>("Factor"); + auto clamp_fn = std::make_unique>( + __func__, [curve_total_length](float factor) { + const float length = factor * curve_total_length; + return std::clamp(length, 0.0f, curve_total_length); + }); + auto process_op = std::make_shared( + FieldOperation(std::move(clamp_fn), {std::move(factor_field)})); + + return Field(std::move(process_op), 0); +} + +static void geo_node_curve_sample_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Curve"); + + auto return_default = [&]() { + params.set_output("Position", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + params.set_output("Tangent", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + params.set_output("Normal", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + }; + + const CurveComponent *component = geometry_set.get_component_for_read(); + if (component == nullptr) { + return return_default(); + } + + const CurveEval *curve = component->get_for_read(); + if (curve == nullptr) { + return return_default(); + } + + if (curve->splines().is_empty()) { + return return_default(); + } + + Array spline_lengths = curve->accumulated_spline_lengths(); + const float total_length = spline_lengths.last(); + if (total_length == 0.0f) { + return return_default(); + } + + Field length_field = get_length_input_field(params, total_length); + + auto sample_fn = std::make_unique(std::move(geometry_set), + std::move(spline_lengths)); + auto sample_op = std::make_shared( + FieldOperation(std::move(sample_fn), {length_field})); + + params.set_output("Position", Field(sample_op, 0)); + params.set_output("Tangent", Field(sample_op, 1)); + params.set_output("Normal", Field(sample_op, 2)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_sample() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_SAMPLE, "Curve Sample", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_sample_exec; + ntype.declare = blender::nodes::geo_node_curve_sample_declare; + node_type_init(&ntype, blender::nodes::geo_node_curve_sample_type_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_sample_update); + node_type_storage( + &ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_curve_sample_layout; + + nodeRegisterType(&ntype); +} From 5eb505e3682260d47b319b56d8d56310de766cab Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 20 Sep 2021 20:37:49 -0500 Subject: [PATCH 0042/1500] Cleanup: Remove debugging change, add comments --- .../nodes/geometry/nodes/node_geo_curve_sample.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 1dbb1f20915..245394d3057 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -106,12 +106,6 @@ class SampleCurveFunction : public fn::MultiFunction { void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - // MutableSpan sampled_positions = params.uninitialized_single_output(1, - // "Position"); - // MutableSpan sampled_tangents = params.uninitialized_single_output(2, - // "Tangent"); - // MutableSpan sampled_normals = params.uninitialized_single_output(3, - // "Normal"); MutableSpan sampled_positions = params.uninitialized_single_output_if_required( 1, "Position"); MutableSpan sampled_tangents = params.uninitialized_single_output_if_required( @@ -161,6 +155,7 @@ class SampleCurveFunction : public fn::MultiFunction { spline_indices[i] = std::max(index, 0); } + /* Storing lookups in an array is unecessary but will simplify custom attribute transfer. */ Array lookups(mask.min_array_size()); for (const int i : mask) { const float length_in_spline = lengths[i] - spline_lengths_[spline_indices[i]]; @@ -207,7 +202,7 @@ static Field get_length_input_field(const GeoNodeExecParams ¶ms, const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - return params.get_input>("Length"); + /* Just make sure the length is in bounds of the curve. */ Field length_field = params.get_input>("Length"); auto clamp_fn = std::make_unique>( __func__, [curve_total_length](float length) { @@ -219,6 +214,7 @@ static Field get_length_input_field(const GeoNodeExecParams ¶ms, return Field(std::move(clamp_op), 0); } + /* Convert the factor to a length and clamp it to the bounds of the curve. */ Field factor_field = params.get_input>("Factor"); auto clamp_fn = std::make_unique>( __func__, [curve_total_length](float factor) { From 52bfa750e74952ddd4e8223d92d21140831902a7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 21 Sep 2021 17:18:26 +1000 Subject: [PATCH 0043/1500] WM: only return PASS_THROUGH on PRESS for selection operators Some selection operators return (PASS_THROUGH & FINISHED) so the tweak event isn't suppressed from the PRESS event having been handled. This is now restricted to events with a PRESS action. Without this, using CLICK for selection was passing the event through which could run other actions unintentionally. --- .../editors/space_view3d/view3d_select.c | 4 +++- source/blender/editors/uvedit/uvedit_select.c | 12 +++++++++--- source/blender/windowmanager/WM_api.h | 2 ++ .../windowmanager/intern/wm_operator_utils.c | 18 ++++++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 3f572bf9d5a..39aed131ea1 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2813,7 +2813,9 @@ static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *even { RNA_int_set_array(op->ptr, "location", event->mval); - return view3d_select_exec(C, op); + const int retval = view3d_select_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void VIEW3D_OT_select(wmOperatorType *ot) diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index c0ccf1b7095..86390882bed 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -2122,7 +2122,9 @@ static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); RNA_float_set_array(op->ptr, "location", co); - return uv_select_exec(C, op); + const int retval = uv_select_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void UV_OT_select(wmOperatorType *ot) @@ -2281,7 +2283,9 @@ static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *eve UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); RNA_float_set_array(op->ptr, "location", co); - return uv_select_loop_exec(C, op); + const int retval = uv_select_loop_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void UV_OT_select_loop(wmOperatorType *ot) @@ -2341,7 +2345,9 @@ static int uv_select_edge_ring_invoke(bContext *C, wmOperator *op, const wmEvent UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); RNA_float_set_array(op->ptr, "location", co); - return uv_select_edge_ring_exec(C, op); + const int retval = uv_select_edge_ring_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void UV_OT_select_edge_ring(wmOperatorType *ot) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 189a231616e..28d3cb326ae 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -707,6 +707,8 @@ void WM_event_fileselect_event(struct wmWindowManager *wm, void *ophandle, int e void WM_operator_region_active_win_set(struct bContext *C); +int WM_operator_flag_only_pass_through_on_press(int retval, const struct wmEvent *event); + /* drag and drop */ struct wmDrag *WM_event_start_drag( struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags); diff --git a/source/blender/windowmanager/intern/wm_operator_utils.c b/source/blender/windowmanager/intern/wm_operator_utils.c index 81b597f7484..85a0a28de79 100644 --- a/source/blender/windowmanager/intern/wm_operator_utils.c +++ b/source/blender/windowmanager/intern/wm_operator_utils.c @@ -40,6 +40,24 @@ #include "ED_object.h" #include "ED_screen.h" +/* -------------------------------------------------------------------- */ +/** \name Generic Utilities + * \{ */ + +/** + * Only finish + pass through for press events (allowing press-tweak). + */ +int WM_operator_flag_only_pass_through_on_press(int retval, const struct wmEvent *event) +{ + if ((event->val != KM_PRESS) && + ((retval & OPERATOR_PASS_THROUGH) && (retval & OPERATOR_FINISHED))) { + retval &= ~OPERATOR_PASS_THROUGH; + } + return retval; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Value Interaction Helper * From c9d9bfa84ad5cb985e3feccffa702b2f3cc2adf8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 21 Sep 2021 17:55:43 +1000 Subject: [PATCH 0044/1500] Keymap: preference for fallback-tool with RMB select Expose a key-map preference "Fallback Tool (RMB)", disabled by default. The right mouse button uses the fallback tool (currently visible selection tool in the toolbar), instead of always tweaking. When any selection tool is active, right mouse always tweaks. To enable fallback selection on RMB, set the "Right Mouse Select Action" to "Selection Tool". Internal changes: - Add fall-back key-maps, separate key-maps needed for when the tool is run as a fall-back. This is needed so RMB-select can support fall-back tools, so left-mouse can be used when it's the active tool and RMB can be used as a fall-back action when another tool is active. - Add options field to tools so tools without gizmos can enable the full-back tool keymap. - Support multiple key-maps for keymap handlers. - Fall-back keymaps now co-exist with the tool-keymaps. So both keymaps may be active at once - using different mouse buttons. When gizmos are in use, a highlighted gizmo prioritizes the tool-keymap over the fall-back keymap. Resolves T83690. Reviewed By: JulienKaspar Ref D12493 --- release/scripts/modules/bpy/utils/__init__.py | 1 + release/scripts/presets/keyconfig/Blender.py | 16 + .../keyconfig/keymap_data/blender_default.py | 432 ++++++++++++------ .../startup/bl_ui/space_toolsystem_common.py | 14 +- .../startup/bl_ui/space_toolsystem_toolbar.py | 15 + .../interface_template_search_menu.c | 36 +- .../mesh/editmesh_extrude_spin_gizmo.c | 4 +- source/blender/editors/screen/area.c | 12 +- .../space_view3d/view3d_gizmo_preselect.c | 4 +- source/blender/makesdna/DNA_workspace_types.h | 11 + .../makesrna/intern/rna_workspace_api.c | 9 + source/blender/windowmanager/WM_api.h | 26 +- .../gizmo/intern/wm_gizmo_group.c | 2 + .../windowmanager/intern/wm_event_system.c | 126 +++-- .../blender/windowmanager/intern/wm_keymap.c | 24 +- .../windowmanager/intern/wm_toolsystem.c | 5 +- 16 files changed, 523 insertions(+), 214 deletions(-) diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py index afa04a18ef6..3f0248970c6 100644 --- a/release/scripts/modules/bpy/utils/__init__.py +++ b/release/scripts/modules/bpy/utils/__init__.py @@ -858,6 +858,7 @@ def register_tool(tool_cls, *, after=None, separator=False, group=False): "description": getattr(tool_cls, "bl_description", tool_cls.__doc__), "icon": getattr(tool_cls, "bl_icon", None), "cursor": getattr(tool_cls, "bl_cursor", None), + "options": getattr(tool_cls, "bl_options", None), "widget": getattr(tool_cls, "bl_widget", None), "widget_properties": getattr(tool_cls, "bl_widget_properties", None), "keymap": getattr(tool_cls, "bl_keymap", None), diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index eb66c961472..1690b03184a 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -54,6 +54,19 @@ class Prefs(bpy.types.KeyConfigPreferences): default='PLAY', update=update_fn, ) + rmb_action: EnumProperty( + name="Right Mouse Select Action", + items=( + ('TWEAK', "Select & Tweak", + "Right mouse always tweaks"), + ('FALLBACK_TOOL', "Selection Tool", + "Right mouse uses the selection tool"), + ), + description=( + "Default action for the right mouse button" + ), + update=update_fn, + ) use_alt_click_leader: BoolProperty( name="Alt Click Tool Prompt", description=( @@ -179,6 +192,8 @@ class Prefs(bpy.types.KeyConfigPreferences): if is_select_left: col.row().prop(self, "gizmo_action", text="Activate Gizmo Event", expand=True) + else: + col.row().prop(self, "rmb_action", text="Right Mouse Select Action", expand=True) # Checkboxes sub-layout. col = layout.column() @@ -232,6 +247,7 @@ def load(): kc_prefs.select_mouse == 'LEFT' and kc_prefs.gizmo_action == 'DRAG' ), + use_fallback_tool=(True if (kc_prefs.select_mouse == 'LEFT') else (kc_prefs.rmb_action == 'FALLBACK_TOOL')), use_alt_click_leader=kc_prefs.use_alt_click_leader, use_pie_click_drag=kc_prefs.use_pie_click_drag, ), diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 44b77ab2aac..6a3064fc820 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -46,6 +46,8 @@ class Params: "use_select_all_toggle", # Activate gizmo on drag (which support it). "use_gizmo_drag", + # Use the fallback tool instead of tweak for RMB select. + "use_fallback_tool", # Use pie menu for tab by default (swap 'Tab/Ctrl-Tab'). "use_v3d_tab_menu", # Use extended pie menu for shading. @@ -59,6 +61,15 @@ class Params: "v3d_tilde_action", # Alt-MMB axis switching 'RELATIVE' or 'ABSOLUTE' axis switching. "v3d_alt_mmb_drag_action", + + # Convenience variables: + # (derived from other settings). + # + # This case needs to be checked often, + # convenience for: `params.use_fallback_tool if params.select_mouse == 'RIGHT' else False`. + "use_fallback_tool_rmb", + # Convenience for: `'CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value`. + "select_mouse_value_fallback", ) def __init__( @@ -72,6 +83,7 @@ class Params: spacebar_action='TOOL', use_select_all_toggle=False, use_gizmo_drag=True, + use_fallback_tool=False, use_v3d_tab_menu=False, use_v3d_shade_ex_pie=False, use_v3d_mmb_pan=False, @@ -96,6 +108,9 @@ class Params: self.context_menu_event = {"type": 'W', "value": 'PRESS'} self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'} self.cursor_tweak_event = None + self.use_fallback_tool = use_fallback_tool + self.use_fallback_tool_rmb = use_fallback_tool + self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value else: # Left mouse select uses Click event for selection. This is a little # less immediate, but is needed to distinguish between click and tweak @@ -115,6 +130,10 @@ class Params: self.cursor_set_event = {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True} self.cursor_tweak_event = {"type": 'EVT_TWEAK_R', "value": 'ANY', "shift": True} + self.use_fallback_tool = True + self.use_fallback_tool_rmb = False + self.select_mouse_value_fallback = self.select_mouse_value + self.use_mouse_emulate_3_button = use_mouse_emulate_3_button @@ -147,6 +166,15 @@ NUMBERS_1 = ('ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NI NUMBERS_0 = ('ZERO', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE') +# ------------------------------------------------------------------------------ +# Generic Utilities + +def _fallback_id(text, fallback): + if fallback: + return text + " (fallback)" + return text + + # ------------------------------------------------------------------------------ # Keymap Item Wrappers @@ -882,20 +910,25 @@ def km_uv_editor(params): items.extend([ # Selection modes. *_template_items_uv_select_mode(params), + *_template_uv_select( + type=params.select_mouse, + value=('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value), + legacy=params.legacy, + ), ("uv.mark_seam", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), - ("uv.select", {"type": params.select_mouse, "value": params.select_mouse_value}, - {"properties": [("deselect_all", not params.legacy)]}), - ("uv.select", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True}, - {"properties": [("extend", True)]}), ("uv.select_loop", {"type": params.select_mouse, "value": params.select_mouse_value, "alt": True}, None), ("uv.select_loop", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "alt": True}, {"properties": [("extend", True)]}), - ("uv.select_edge_ring", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "alt": True}, None), - ("uv.select_edge_ring", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "shift": True, "alt": True}, + ("uv.select_edge_ring", + {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "alt": True}, None), + ("uv.select_edge_ring", + {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "shift": True, "alt": True}, {"properties": [("extend", True)]}), - ("uv.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True}, + ("uv.shortest_path_pick", + {"type": params.select_mouse, "value": params.select_mouse_value_fallback, "ctrl": True}, {"properties": [("use_fill", False)]}), - ("uv.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "shift": True}, + ("uv.shortest_path_pick", + {"type": params.select_mouse, "value": params.select_mouse_value_fallback, "ctrl": True, "shift": True}, {"properties": [("use_fill", True)]}), ("uv.select_split", {"type": 'Y', "value": 'PRESS'}, None), ("uv.select_box", {"type": 'B', "value": 'PRESS'}, @@ -1196,20 +1229,11 @@ def km_view3d(params): ("view3d.view_axis", {"type": 'NDOF_BUTTON_TOP', "value": 'PRESS', "shift": True}, {"properties": [("type", 'TOP'), ("align_active", True)]}), # Selection. - *(( - "view3d.select", - {"type": params.select_mouse, "value": params.select_mouse_value, **{m: True for m in mods}}, - {"properties": [(c, True) for c in props]}, - ) for props, mods in ( - (("deselect_all",) if not params.legacy else (), ()), - (("toggle",), ("shift",)), - (("center", "object"), ("ctrl",)), - (("enumerate",), ("alt",)), - (("toggle", "center"), ("shift", "ctrl")), - (("center", "enumerate"), ("ctrl", "alt")), - (("toggle", "enumerate"), ("shift", "alt")), - (("toggle", "center", "enumerate"), ("shift", "ctrl", "alt")), - )), + *_template_view3d_select( + type=params.select_mouse, + value=('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value), + legacy=params.legacy, + ), ("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None), ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, {"properties": [("mode", 'ADD')]}), @@ -3203,7 +3227,7 @@ def km_grease_pencil(_params): return keymap -def _grease_pencil_selection(params): +def _grease_pencil_selection(params, use_select_mouse=True): return [ # Select all *_template_items_select_actions(params, "gpencil.select_all"), @@ -3225,13 +3249,12 @@ def _grease_pencil_selection(params): {"properties": [("mode", 'ADD')]}), ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, {"properties": [("mode", 'SUB')]}), - ("gpencil.select", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True}, - {"properties": [("extend", True), ("toggle", True)]}), - # Whole stroke select - ("gpencil.select", {"type": params.select_mouse, "value": params.select_mouse_value, "alt": True}, - {"properties": [("entire_strokes", True)]}), - ("gpencil.select", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "alt": True}, - {"properties": [("extend", True), ("entire_strokes", True)]}), + *_template_view3d_gpencil_select( + type=params.select_mouse, + value=params.select_mouse_value_fallback, + legacy=params.legacy, + use_select_mouse=use_select_mouse, + ), # Select linked ("gpencil.select_linked", {"type": 'L', "value": 'PRESS'}, None), ("gpencil.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), @@ -3266,9 +3289,6 @@ def km_grease_pencil_stroke_edit_mode(params): # Interpolation ("gpencil.interpolate", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), ("gpencil.interpolate_sequence", {"type": 'E', "value": 'PRESS', "shift": True, "ctrl": True}, None), - # Normal select - ("gpencil.select", {"type": params.select_mouse, "value": params.select_mouse_value}, - {"properties": [("deselect_all", not params.legacy)]}), # Selection *_grease_pencil_selection(params), # Duplicate and move selected points @@ -3560,7 +3580,7 @@ def km_grease_pencil_stroke_sculpt_mode(params): items.extend([ # Selection - *_grease_pencil_selection(params), + *_grease_pencil_selection(params, use_select_mouse=False), # Brush strength ("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True}, @@ -3846,7 +3866,7 @@ def km_grease_pencil_stroke_vertex_mode(params): items.extend([ # Selection - *_grease_pencil_selection(params), + *_grease_pencil_selection(params, use_select_mouse=False), # Brush strength ("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True}, {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength')]}), @@ -4267,7 +4287,8 @@ def km_curve(params): {"properties": [("deselect", False)]}), ("curve.select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True}, {"properties": [("deselect", True)]}), - ("curve.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True}, None), + ("curve.shortest_path_pick", + {"type": params.select_mouse, "value": params.select_mouse_value_fallback, "ctrl": True}, None), ("curve.separate", {"type": 'P', "value": 'PRESS'}, None), ("curve.split", {"type": 'Y', "value": 'PRESS'}, None), ("curve.extrude_move", {"type": 'E', "value": 'PRESS'}, None), @@ -4348,6 +4369,80 @@ def _template_paint_radial_control(paint, rotation=False, secondary_rotation=Fal return items +def _template_view3d_select(*, type, value, legacy): + return [( + "view3d.select", + {"type": type, "value": value, **{m: True for m in mods}}, + {"properties": [(c, True) for c in props]}, + ) for props, mods in ( + (("deselect_all",) if not legacy else (), ()), + (("toggle",), ("shift",)), + (("center", "object"), ("ctrl",)), + (("enumerate",), ("alt",)), + (("toggle", "center"), ("shift", "ctrl")), + (("center", "enumerate"), ("ctrl", "alt")), + (("toggle", "enumerate"), ("shift", "alt")), + (("toggle", "center", "enumerate"), ("shift", "ctrl", "alt")), + )] + + +def _template_view3d_select_for_fallback(params, fallback): + if (not fallback) and params.use_fallback_tool_rmb: + # Needed so we have immediate select+tweak when the default select tool is active. + return _template_view3d_select( + type=params.select_mouse, + value=params.select_mouse_value, + legacy=params.legacy, + ) + return [] + + +def _template_view3d_gpencil_select(*, type, value, legacy, use_select_mouse=True): + return [ + *([] if not use_select_mouse else [ + ("gpencil.select", {"type": type, "value": value}, + {"properties": [("deselect_all", not legacy)]})]), + ("gpencil.select", {"type": type, "value": value, "shift": True}, + {"properties": [("extend", True), ("toggle", True)]}), + # Whole stroke select + ("gpencil.select", {"type": type, "value": value, "alt": True}, + {"properties": [("entire_strokes", True)]}), + ("gpencil.select", {"type": type, "value": value, "shift": True, "alt": True}, + {"properties": [("extend", True), ("entire_strokes", True)]}), + ] + + +def _template_view3d_gpencil_select_for_fallback(params, fallback): + if (not fallback) and params.use_fallback_tool_rmb: + # Needed so we have immediate select+tweak when the default select tool is active. + return _template_view3d_gpencil_select( + type=params.select_mouse, + value=params.select_mouse_value, + legacy=params.legacy, + ) + return [] + + +def _template_uv_select(*, type, value, legacy): + return [ + ("uv.select", {"type": type, "value": value}, + {"properties": [("deselect_all", not legacy)]}), + ("uv.select", {"type": type, "value": value, "shift": True}, + {"properties": [("extend", True)]}), + ] + + +def _template_uv_select_for_fallback(params, fallback): + if (not fallback) and params.use_fallback_tool_rmb: + # Needed so we have immediate select+tweak when the default select tool is active. + return _template_uv_select( + type=params.select_mouse, + value=params.select_mouse_value, + legacy=params.legacy, + ) + return [] + + def km_image_paint(params): items = [] keymap = ( @@ -4651,9 +4746,11 @@ def km_mesh(params): ("mesh.edgering_select", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "alt": True}, None), ("mesh.edgering_select", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "ctrl": True, "alt": True}, {"properties": [("toggle", True)]}), - ("mesh.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True}, + ("mesh.shortest_path_pick", + {"type": params.select_mouse, "value": params.select_mouse_value_fallback, "ctrl": True}, {"properties": [("use_fill", False)]}), - ("mesh.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "ctrl": True}, + ("mesh.shortest_path_pick", + {"type": params.select_mouse, "value": params.select_mouse_value_fallback, "shift": True, "ctrl": True}, {"properties": [("use_fill", True)]}), *_template_items_select_actions(params, "mesh.select_all"), ("mesh.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), @@ -4801,7 +4898,8 @@ def km_armature(params): ("armature.select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True}, {"properties": [("deselect", True)]}), ("armature.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), - ("armature.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True}, None), + ("armature.shortest_path_pick", + {"type": params.select_mouse, "value": params.select_mouse_value_fallback, "ctrl": True}, None), # Editing. op_menu("VIEW3D_MT_edit_armature_delete", {"type": 'X', "value": 'PRESS'}), op_menu("VIEW3D_MT_edit_armature_delete", {"type": 'DEL', "value": 'PRESS'}), @@ -5825,38 +5923,60 @@ def km_image_editor_tool_uv_cursor(params): ) -def km_image_editor_tool_uv_select(params): +def km_image_editor_tool_uv_select(params, *, fallback): return ( - "Image Editor Tool: Uv, Tweak", + _fallback_id("Image Editor Tool: Uv, Tweak", fallback), {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select(params, "uv.select", "uv.cursor_set", extend="extend")}, + {"items": [ + *([] if fallback else _template_items_tool_select(params, "uv.select", "uv.cursor_set", extend="extend")), + *([] if (not params.use_fallback_tool_rmb) else _template_uv_select( + type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), + ]}, ) -def km_image_editor_tool_uv_select_box(params): +def km_image_editor_tool_uv_select_box(params, *, fallback): return ( - "Image Editor Tool: Uv, Select Box", + _fallback_id("Image Editor Tool: Uv, Select Box", fallback), {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions_simple("uv.select_box", type=params.tool_tweak, value='ANY')}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( + "uv.select_box", + type=params.select_tweak if fallback else params.tool_tweak, + value='ANY')), + *_template_uv_select_for_fallback(params, fallback), + ]}, ) -def km_image_editor_tool_uv_select_circle(params): +def km_image_editor_tool_uv_select_circle(params, *, fallback): return ( - "Image Editor Tool: Uv, Select Circle", + _fallback_id("Image Editor Tool: Uv, Select Circle", fallback), {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions_simple( - "uv.select_circle", type=params.tool_mouse, value='PRESS', - properties=[("wait_for_input", False)], - )}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( + "uv.select_circle", + type=params.select_tweak if fallback else params.tool_mouse, + value='ANY' if fallback else 'PRESS', + properties=[("wait_for_input", False)])), + # No selection fallback since this operates on press. + ]}, ) -def km_image_editor_tool_uv_select_lasso(params): +def km_image_editor_tool_uv_select_lasso(params, *, fallback): return ( - "Image Editor Tool: Uv, Select Lasso", + _fallback_id("Image Editor Tool: Uv, Select Lasso", fallback), {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions_simple("uv.select_lasso", type=params.tool_tweak, value='ANY')}, + + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( + "uv.select_lasso", + type=params.select_tweak if fallback else params.tool_tweak, + value='ANY') + ), + *_template_uv_select_for_fallback(params, fallback), + ]}, ) @@ -5923,47 +6043,53 @@ def km_image_editor_tool_uv_scale(params): ) -def km_node_editor_tool_select(params): +def km_node_editor_tool_select(params, *, fallback): return ( - "Node Tool: Tweak", + _fallback_id("Node Tool: Tweak", fallback), {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("node.select", {"type": params.select_mouse, "value": 'PRESS'}, - {"properties": [("deselect_all", not params.legacy)]}), + *([] if fallback else [ + ("node.select", {"type": params.select_mouse, "value": 'PRESS'}, + {"properties": [("deselect_all", not params.legacy)]}), + ]), ]}, ) -def km_node_editor_tool_select_box(params): +def km_node_editor_tool_select_box(params, *, fallback): return ( - "Node Tool: Select Box", + _fallback_id("Node Tool: Select Box", fallback), {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions_simple( - "node.select_box", type=params.tool_tweak, value='ANY', - properties=[("tweak", True)], - )}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( + "node.select_box", type=params.tool_tweak, value='ANY', + properties=[("tweak", True)], + )), + ]}, ) -def km_node_editor_tool_select_lasso(params): +def km_node_editor_tool_select_lasso(params, *, fallback): return ( - "Node Tool: Select Lasso", + _fallback_id("Node Tool: Select Lasso", fallback), {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions_simple( - "node.select_lasso", type=params.tool_mouse, value='PRESS', - properties=[("tweak", True)], - )}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( + "node.select_lasso", type=params.tool_mouse, value='PRESS', + properties=[("tweak", True)])) + ]}, ) -def km_node_editor_tool_select_circle(params): +def km_node_editor_tool_select_circle(params, *, fallback): return ( - "Node Tool: Select Circle", + _fallback_id("Node Tool: Select Circle", fallback), {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions_simple( - "node.select_circle", type=params.tool_mouse, value='PRESS', - properties=[("wait_for_input", False)], - )}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( + "node.select_circle", type=params.tool_mouse, value='PRESS', + properties=[("wait_for_input", False)])), + ]}, ) @@ -5989,38 +6115,61 @@ def km_3d_view_tool_cursor(params): ) -def km_3d_view_tool_select(params): +def km_3d_view_tool_select(params, *, fallback): return ( - "3D View Tool: Tweak", + _fallback_id("3D View Tool: Tweak", fallback), {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select(params, "view3d.select", "view3d.cursor3d", extend="toggle")}, + {"items": [ + *([] if fallback else _template_items_tool_select( + params, "view3d.select", "view3d.cursor3d", extend="toggle")), + *([] if (not params.use_fallback_tool_rmb) else _template_view3d_select( + type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), + ]}, ) -def km_3d_view_tool_select_box(params): +def km_3d_view_tool_select_box(params, *, fallback): return ( - "3D View Tool: Select Box", + _fallback_id("3D View Tool: Select Box", fallback), {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions("view3d.select_box", type=params.tool_tweak, value='ANY')}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( + "view3d.select_box", + type=params.select_tweak if fallback else params.tool_tweak, + value='ANY')), + *_template_view3d_select_for_fallback(params, fallback), + ]}, ) -def km_3d_view_tool_select_circle(params): +def km_3d_view_tool_select_circle(params, *, fallback): return ( - "3D View Tool: Select Circle", + _fallback_id("3D View Tool: Select Circle", fallback), {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions_simple( - "view3d.select_circle", type=params.tool_mouse, value='PRESS', - properties=[("wait_for_input", False)], - )}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( + "view3d.select_circle", + # Why circle select should be used on tweak? + # So that RMB or Shift-RMB is still able to set an element as active. + type=params.select_tweak if fallback else params.tool_mouse, + value='ANY' if fallback else 'PRESS', + properties=[("wait_for_input", False)])), + # No selection fallback since this operates on press. + ]}, ) -def km_3d_view_tool_select_lasso(params): +def km_3d_view_tool_select_lasso(params, *, fallback): return ( - "3D View Tool: Select Lasso", + _fallback_id("3D View Tool: Select Lasso", fallback), {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions("view3d.select_lasso", type=params.tool_tweak, value='ANY')}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( + "view3d.select_lasso", + type=params.select_tweak if fallback else params.tool_tweak, + value='ANY')), + *_template_view3d_select_for_fallback(params, fallback), + ]} ) @@ -6845,38 +6994,57 @@ def km_3d_view_tool_paint_gpencil_interpolate(params): ]}, ) -def km_3d_view_tool_edit_gpencil_select(params): +def km_3d_view_tool_edit_gpencil_select(params, *, fallback): return ( - "3D View Tool: Edit Gpencil, Tweak", + _fallback_id("3D View Tool: Edit Gpencil, Tweak", fallback), {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select(params, "gpencil.select", "view3d.cursor3d", extend="toggle")}, + {"items": [ + *([] if fallback else _template_items_tool_select( + params, "gpencil.select", "view3d.cursor3d", extend="toggle")), + *([] if (not params.use_fallback_tool_rmb) else _template_view3d_gpencil_select( + type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), + ]}, + ) + +def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback): + return ( + _fallback_id("3D View Tool: Edit Gpencil, Select Box", fallback), + {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( + "gpencil.select_box", type=params.select_tweak if fallback else params.tool_tweak, value='ANY')), + *_template_view3d_gpencil_select_for_fallback(params, fallback), + ]}, + ) + +def km_3d_view_tool_edit_gpencil_select_circle(params, *, fallback): + return ( + _fallback_id("3D View Tool: Edit Gpencil, Select Circle", fallback), + {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( + "gpencil.select_circle", + # Why circle select should be used on tweak? + # So that RMB or Shift-RMB is still able to set an element as active. + type=params.select_tweak if fallback else params.tool_mouse, + value='ANY' if fallback else 'PRESS', + properties=[("wait_for_input", False)])), + # No selection fallback since this operates on press. + ]}, ) -def km_3d_view_tool_edit_gpencil_select_box(params): +def km_3d_view_tool_edit_gpencil_select_lasso(params, *, fallback): return ( - "3D View Tool: Edit Gpencil, Select Box", + _fallback_id("3D View Tool: Edit Gpencil, Select Lasso", fallback), {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions("gpencil.select_box", type=params.tool_tweak, value='ANY')}, - ) - - -def km_3d_view_tool_edit_gpencil_select_circle(params): - return ( - "3D View Tool: Edit Gpencil, Select Circle", - {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions_simple( - "gpencil.select_circle", type=params.tool_mouse, value='PRESS', - properties=[("wait_for_input", False)], - )}, - ) - - -def km_3d_view_tool_edit_gpencil_select_lasso(params): - return ( - "3D View Tool: Edit Gpencil, Select Lasso", - {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions("gpencil.select_lasso", type=params.tool_tweak, value='ANY')}, + {"items": [ + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( + "gpencil.select_lasso", + type=params.select_tweak if fallback else params.tool_tweak, + value='ANY')), + *_template_view3d_gpencil_select_for_fallback(params, fallback), + ]} ) @@ -7184,25 +7352,25 @@ def generate_keymaps(params=None): km_image_editor_tool_generic_sample(params), km_image_editor_tool_uv_cursor(params), - km_image_editor_tool_uv_select(params), - km_image_editor_tool_uv_select_box(params), - km_image_editor_tool_uv_select_circle(params), - km_image_editor_tool_uv_select_lasso(params), + *(km_image_editor_tool_uv_select(params, fallback=fallback) for fallback in (False, True)), + *(km_image_editor_tool_uv_select_box(params, fallback=fallback) for fallback in (False, True)), + *(km_image_editor_tool_uv_select_circle(params, fallback=fallback) for fallback in (False, True)), + *(km_image_editor_tool_uv_select_lasso(params, fallback=fallback) for fallback in (False, True)), km_image_editor_tool_uv_rip_region(params), km_image_editor_tool_uv_sculpt_stroke(params), km_image_editor_tool_uv_move(params), km_image_editor_tool_uv_rotate(params), km_image_editor_tool_uv_scale(params), - km_node_editor_tool_select(params), - km_node_editor_tool_select_box(params), - km_node_editor_tool_select_lasso(params), - km_node_editor_tool_select_circle(params), + *(km_node_editor_tool_select(params, fallback=fallback) for fallback in (False, True)), + *(km_node_editor_tool_select_box(params, fallback=fallback) for fallback in (False, True)), + *(km_node_editor_tool_select_lasso(params, fallback=fallback) for fallback in (False, True)), + *(km_node_editor_tool_select_circle(params, fallback=fallback) for fallback in (False, True)), km_node_editor_tool_links_cut(params), km_3d_view_tool_cursor(params), - km_3d_view_tool_select(params), - km_3d_view_tool_select_box(params), - km_3d_view_tool_select_circle(params), - km_3d_view_tool_select_lasso(params), + *(km_3d_view_tool_select(params, fallback=fallback) for fallback in (False, True)), + *(km_3d_view_tool_select_box(params, fallback=fallback) for fallback in (False, True)), + *(km_3d_view_tool_select_circle(params, fallback=fallback) for fallback in (False, True)), + *(km_3d_view_tool_select_lasso(params, fallback=fallback) for fallback in (False, True)), km_3d_view_tool_transform(params), km_3d_view_tool_move(params), km_3d_view_tool_rotate(params), @@ -7273,10 +7441,10 @@ def generate_keymaps(params=None): km_3d_view_tool_paint_gpencil_cutter(params), km_3d_view_tool_paint_gpencil_eyedropper(params), km_3d_view_tool_paint_gpencil_interpolate(params), - km_3d_view_tool_edit_gpencil_select(params), - km_3d_view_tool_edit_gpencil_select_box(params), - km_3d_view_tool_edit_gpencil_select_circle(params), - km_3d_view_tool_edit_gpencil_select_lasso(params), + *(km_3d_view_tool_edit_gpencil_select(params, fallback=fallback) for fallback in (False, True)), + *(km_3d_view_tool_edit_gpencil_select_box(params, fallback=fallback) for fallback in (False, True)), + *(km_3d_view_tool_edit_gpencil_select_circle(params, fallback=fallback) for fallback in (False, True)), + *(km_3d_view_tool_edit_gpencil_select_lasso(params, fallback=fallback) for fallback in (False, True)), km_3d_view_tool_edit_gpencil_extrude(params), km_3d_view_tool_edit_gpencil_radius(params), km_3d_view_tool_edit_gpencil_bend(params), diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 28549098e51..1c3dbded083 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -118,6 +118,8 @@ ToolDef = namedtuple( "draw_settings", # Optional draw cursor. "draw_cursor", + # Various options, see: `bpy.types.WorkSpaceTool.setup` options argument. + "options", ) ) del namedtuple @@ -133,6 +135,7 @@ def from_dict(kw_args): "description": None, "icon": None, "cursor": None, + "options": None, "widget": None, "widget_properties": None, "keymap": None, @@ -536,6 +539,7 @@ class ToolSelectPanelHelper: visited.add(km_name) yield (km_name, cls.bl_space_type, 'WINDOW', []) + yield (km_name + " (fallback)", cls.bl_space_type, 'WINDOW', []) # ------------------------------------------------------------------------- # Layout Generators @@ -988,16 +992,22 @@ def _activate_by_item(context, space_type, item, index, *, as_fallback=False): gizmo_group = item.widget or "" + idname_fallback = (item_fallback and item_fallback.idname) or "" + keymap_fallback = (item_fallback and item_fallback.keymap and item_fallback.keymap[0]) or "" + if keymap_fallback: + keymap_fallback = keymap_fallback + " (fallback)" + tool.setup( idname=item.idname, keymap=item.keymap[0] if item.keymap is not None else "", cursor=item.cursor or 'DEFAULT', + options=item.options or set(), gizmo_group=gizmo_group, data_block=item.data_block or "", operator=item.operator or "", index=index, - idname_fallback=(item_fallback and item_fallback.idname) or "", - keymap_fallback=(item_fallback and item_fallback.keymap and item_fallback.keymap[0]) or "", + idname_fallback=idname_fallback, + keymap_fallback=keymap_fallback, ) if ( diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index c5191e80aef..a513ab4b2d4 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -105,6 +105,7 @@ class _defs_view3d_generic: icon="ops.generic.cursor", keymap="3D View Tool: Cursor", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -143,6 +144,7 @@ class _defs_view3d_generic: icon="ops.view3d.ruler", widget="VIEW3D_GGT_ruler", keymap="3D View Tool: Measure", + options={'KEYMAP_FALLBACK'}, ) @@ -237,6 +239,7 @@ class _defs_annotate: cursor='PAINT_BRUSH', keymap="Generic Tool: Annotate", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) @@ -248,6 +251,7 @@ class _defs_annotate: cursor='PAINT_BRUSH', keymap="Generic Tool: Annotate Line", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) @@ -259,6 +263,7 @@ class _defs_annotate: cursor='PAINT_BRUSH', keymap="Generic Tool: Annotate Polygon", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -274,6 +279,7 @@ class _defs_annotate: cursor='ERASER', keymap="Generic Tool: Annotate Eraser", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @@ -543,6 +549,7 @@ class _defs_view3d_add: widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -569,6 +576,7 @@ class _defs_view3d_add: widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -594,6 +602,7 @@ class _defs_view3d_add: widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -619,6 +628,7 @@ class _defs_view3d_add: widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -643,6 +653,7 @@ class _defs_view3d_add: widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @@ -1089,6 +1100,7 @@ class _defs_edit_mesh: widget=None, keymap=(), draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -1695,6 +1707,7 @@ class _defs_image_generic: ), icon="ops.generic.cursor", keymap=(), + options={'KEYMAP_FALLBACK'}, ) # Currently a place holder so we can switch away from the annotation tool. @@ -1846,6 +1859,7 @@ class _defs_image_uv_edit: # TODO: generic operator (UV version of `VIEW3D_GGT_tool_generic_handle_free`). widget=None, keymap=(), + options={'KEYMAP_FALLBACK'}, ) @@ -1879,6 +1893,7 @@ class _defs_image_uv_sculpt: operator="sculpt.uv_sculpt_stroke", keymap="Image Editor Tool: Uv, Sculpt Stroke", draw_cursor=draw_cursor, + options={'KEYMAP_FALLBACK'}, ), ) diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index 672f1b64943..3a5d65475f7 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -350,24 +350,28 @@ static void menu_types_add_from_keymap_items(bContext *C, if (handler_base->poll == NULL || handler_base->poll(region, win->eventstate)) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; - wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler); - if (keymap && WM_keymap_poll(C, keymap)) { - LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { - if (kmi->flag & KMI_INACTIVE) { - continue; - } - if (STR_ELEM(kmi->idname, "WM_OT_call_menu", "WM_OT_call_menu_pie")) { - char menu_idname[MAX_NAME]; - RNA_string_get(kmi->ptr, "name", menu_idname); - MenuType *mt = WM_menutype_find(menu_idname, false); + wmEventHandler_KeymapResult km_result; + WM_event_get_keymaps_from_handler(wm, handler, &km_result); + for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { + wmKeyMap *keymap = km_result.keymaps[km_index]; + if (keymap && WM_keymap_poll(C, keymap)) { + LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { + if (kmi->flag & KMI_INACTIVE) { + continue; + } + if (STR_ELEM(kmi->idname, "WM_OT_call_menu", "WM_OT_call_menu_pie")) { + char menu_idname[MAX_NAME]; + RNA_string_get(kmi->ptr, "name", menu_idname); + MenuType *mt = WM_menutype_find(menu_idname, false); - if (mt && BLI_gset_add(menu_tagged, mt)) { - /* Unlikely, but possible this will be included twice. */ - BLI_linklist_prepend(menuid_stack_p, mt); + if (mt && BLI_gset_add(menu_tagged, mt)) { + /* Unlikely, but possible this will be included twice. */ + BLI_linklist_prepend(menuid_stack_p, mt); - void **kmi_p; - if (!BLI_ghash_ensure_p(menu_to_kmi, mt, &kmi_p)) { - *kmi_p = kmi; + void **kmi_p; + if (!BLI_ghash_ensure_p(menu_to_kmi, mt, &kmi_p)) { + *kmi_p = kmi; + } } } } diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c index ae37d6c8deb..5faafa77bba 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c @@ -461,7 +461,7 @@ void MESH_GGT_spin(struct wmGizmoGroupType *gzgt) gzgt->name = "Mesh Spin Init"; gzgt->idname = "MESH_GGT_spin"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D; + gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -1063,7 +1063,7 @@ void MESH_GGT_spin_redo(struct wmGizmoGroupType *gzgt) gzgt->name = "Mesh Spin Redo"; gzgt->idname = "MESH_GGT_spin_redo"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D; + gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 9546035375c..c71e68df2fd 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1735,10 +1735,14 @@ static void ed_default_handlers( WM_event_add_keymap_handler(handlers, keymap); } if (flag & ED_KEYMAP_TOOL) { - WM_event_add_keymap_handler_dynamic( - ®ion->handlers, WM_event_get_keymap_from_toolsystem_fallback, area); - WM_event_add_keymap_handler_dynamic( - ®ion->handlers, WM_event_get_keymap_from_toolsystem, area); + if (flag & ED_KEYMAP_GIZMO) { + WM_event_add_keymap_handler_dynamic( + ®ion->handlers, WM_event_get_keymap_from_toolsystem_fallback, area); + } + else { + WM_event_add_keymap_handler_dynamic( + ®ion->handlers, WM_event_get_keymap_from_toolsystem, area); + } } if (flag & ED_KEYMAP_FRAMES) { /* frame changing/jumping (for all spaces) */ diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect.c index 441182d7a5f..918ecb14752 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect.c @@ -58,7 +58,7 @@ void VIEW3D_GGT_mesh_preselect_elem(wmGizmoGroupType *gzgt) gzgt->name = "Mesh Preselect Element"; gzgt->idname = "VIEW3D_GGT_mesh_preselect_elem"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D; + gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -95,7 +95,7 @@ void VIEW3D_GGT_mesh_preselect_edgering(wmGizmoGroupType *gzgt) gzgt->name = "Mesh Preselect Edge Ring"; gzgt->idname = "VIEW3D_GGT_mesh_preselect_edgering"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D; + gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index e0294d3534c..a0856588a58 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -29,6 +29,15 @@ extern "C" { #endif +/** #bToolRef_Runtime.flag */ +enum { + /** + * This tool should use the fallback key-map. + * Typically gizmos handle this but some tools (such as the knife tool) don't use a gizmo. + */ + TOOLREF_FLAG_FALLBACK_KEYMAP = (1 << 0), +}; + # # typedef struct bToolRef_Runtime { @@ -47,6 +56,8 @@ typedef struct bToolRef_Runtime { /** Index when a tool is a member of a group. */ int index; + /** Options: `TOOLREF_FLAG_*`. */ + int flag; } bToolRef_Runtime; /* Stored per mode. */ diff --git a/source/blender/makesrna/intern/rna_workspace_api.c b/source/blender/makesrna/intern/rna_workspace_api.c index a2bb89dd5ee..15230f1198b 100644 --- a/source/blender/makesrna/intern/rna_workspace_api.c +++ b/source/blender/makesrna/intern/rna_workspace_api.c @@ -29,6 +29,7 @@ #include "DNA_object_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "RNA_enum_types.h" /* own include */ @@ -51,6 +52,7 @@ static void rna_WorkSpaceTool_setup(ID *id, const char *data_block, const char *op_idname, int index, + int options, const char *idname_fallback, const char *keymap_fallback) { @@ -62,6 +64,7 @@ static void rna_WorkSpaceTool_setup(ID *id, STRNCPY(tref_rt.data_block, data_block); STRNCPY(tref_rt.op, op_idname); tref_rt.index = index; + tref_rt.flag = options; /* While it's logical to assign both these values from setup, * it's useful to stored this in DNA for re-use, exceptional case: write to the 'tref'. */ @@ -131,6 +134,11 @@ void RNA_api_workspace_tool(StructRNA *srna) PropertyRNA *parm; FunctionRNA *func; + static EnumPropertyItem options_items[] = { + {TOOLREF_FLAG_FALLBACK_KEYMAP, "KEYMAP_FALLBACK", 0, "Fallback", ""}, + {0, NULL, 0, NULL, NULL}, + }; + func = RNA_def_function(srna, "setup", "rna_WorkSpaceTool_setup"); RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_CONTEXT); RNA_def_function_ui_description(func, "Set the tool settings"); @@ -146,6 +154,7 @@ void RNA_api_workspace_tool(StructRNA *srna) RNA_def_string(func, "data_block", NULL, MAX_NAME, "Data Block", ""); RNA_def_string(func, "operator", NULL, MAX_NAME, "Operator", ""); RNA_def_int(func, "index", 0, INT_MIN, INT_MAX, "Index", "", INT_MIN, INT_MAX); + RNA_def_enum_flag(func, "options", options_items, 0, "Tool Options", ""); RNA_def_string(func, "idname_fallback", NULL, MAX_NAME, "Fallback Identifier", ""); RNA_def_string(func, "keymap_fallback", NULL, KMAP_MAX_NAME, "Fallback Key Map", ""); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 28d3cb326ae..4fe1530628b 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -262,14 +262,21 @@ struct wmEventHandler_Keymap *WM_event_add_keymap_handler_priority(ListBase *han wmKeyMap *keymap, int priority); -typedef struct wmKeyMap *(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm, - struct wmEventHandler_Keymap *handler) - ATTR_WARN_UNUSED_RESULT; +typedef struct wmEventHandler_KeymapResult { + wmKeyMap *keymaps[3]; + int keymaps_len; +} wmEventHandler_KeymapResult; -struct wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback( - struct wmWindowManager *wm, struct wmEventHandler_Keymap *handler); -struct wmKeyMap *WM_event_get_keymap_from_toolsystem(struct wmWindowManager *wm, - struct wmEventHandler_Keymap *handler); +typedef void(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm, + struct wmEventHandler_Keymap *handler, + struct wmEventHandler_KeymapResult *km_result); + +void WM_event_get_keymap_from_toolsystem_fallback(struct wmWindowManager *wm, + struct wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result); +void WM_event_get_keymap_from_toolsystem(struct wmWindowManager *wm, + struct wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result); struct wmEventHandler_Keymap *WM_event_add_keymap_handler_dynamic( ListBase *handlers, wmEventHandler_KeymapDynamicFn *keymap_fn, void *user_data); @@ -281,8 +288,9 @@ void WM_event_set_keymap_handler_post_callback(struct wmEventHandler_Keymap *han wmKeyMapItem *kmi, void *user_data), void *user_data); -wmKeyMap *WM_event_get_keymap_from_handler(wmWindowManager *wm, - struct wmEventHandler_Keymap *handler); +void WM_event_get_keymaps_from_handler(wmWindowManager *wm, + struct wmEventHandler_Keymap *handler, + struct wmEventHandler_KeymapResult *km_result); wmKeyMapItem *WM_event_match_keymap_item(struct bContext *C, wmKeyMap *keymap, diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index 213a3c2e342..22bdf65a169 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -265,6 +265,8 @@ void WM_gizmogroup_ensure_init(const bContext *C, wmGizmoGroup *gzgroup) { /* prepare for first draw */ if (UNLIKELY((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0)) { + + gzgroup->use_fallback_keymap = true; gzgroup->type->setup(C, gzgroup); /* Not ideal, initialize keymap here, needed for RNA runtime generated gizmos. */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index ae09786356a..14fcc1d69cc 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -2989,9 +2989,18 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers /* Handle all types here. */ if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; - wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler); - action |= wm_handlers_do_keymap_with_keymap_handler( - C, event, handlers, handler, keymap, do_debug_handler); + wmEventHandler_KeymapResult km_result; + WM_event_get_keymaps_from_handler(wm, handler, &km_result); + int action_iter = WM_HANDLER_CONTINUE; + for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { + wmKeyMap *keymap = km_result.keymaps[km_index]; + action_iter |= wm_handlers_do_keymap_with_keymap_handler( + C, event, handlers, handler, keymap, do_debug_handler); + if (action_iter & WM_HANDLER_BREAK) { + break; + } + } + action |= action_iter; /* Clear the tool-tip whenever a key binding is handled, without this tool-tips * are kept when a modal operators starts (annoying but otherwise harmless). */ @@ -3905,17 +3914,34 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap * * Follow #wmEventHandler_KeymapDynamicFn signature. */ -wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, - wmEventHandler_Keymap *handler) +void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result) { + memset(km_result, 0x0, sizeof(*km_result)); + + const char *keymap_id_list[ARRAY_SIZE(km_result->keymaps)]; + int keymap_id_list_len = 0; + ScrArea *area = handler->dynamic.user_data; handler->keymap_tool = NULL; bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL; - if (tref_rt && tref_rt->keymap_fallback[0]) { - const char *keymap_id = NULL; + if (tref_rt && tref_rt->keymap[0]) { + keymap_id_list[keymap_id_list_len++] = tref_rt->keymap; + } + + bool is_gizmo_visible = false; + bool is_gizmo_highlight = false; + + if (tref_rt && tref_rt->keymap_fallback[0]) { + bool add_keymap = false; /* Support for the gizmo owning the tool keymap. */ - if (tref_rt->gizmo_group[0] != '\0' && tref_rt->keymap_fallback[0] != '\0') { + + if (tref_rt->flag & TOOLREF_FLAG_FALLBACK_KEYMAP) { + add_keymap = true; + } + if (tref_rt->gizmo_group[0] != '\0') { wmGizmoMap *gzmap = NULL; wmGizmoGroup *gzgroup = NULL; LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { @@ -3931,32 +3957,49 @@ wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { /* If all are hidden, don't override. */ if (gzgroup->use_fallback_keymap) { + is_gizmo_visible = true; wmGizmo *highlight = wm_gizmomap_highlight_get(gzmap); - if (highlight == NULL) { - keymap_id = tref_rt->keymap_fallback; + if (highlight) { + is_gizmo_highlight = true; } + add_keymap = true; } } } } - - if (keymap_id && keymap_id[0]) { - wmKeyMap *km = WM_keymap_list_find_spaceid_or_empty( - &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW); - /* We shouldn't use keymaps from unrelated spaces. */ - if (km != NULL) { - handler->keymap_tool = area->runtime.tool; - return km; - } - printf( - "Keymap: '%s' not found for tool '%s'\n", tref_rt->keymap, area->runtime.tool->idname); + if (add_keymap) { + keymap_id_list[keymap_id_list_len++] = tref_rt->keymap_fallback; } } - return NULL; + + if (is_gizmo_visible && !is_gizmo_highlight) { + if (keymap_id_list_len == 2) { + SWAP(const char *, keymap_id_list[0], keymap_id_list[1]); + } + } + + for (int i = 0; i < keymap_id_list_len; i++) { + const char *keymap_id = keymap_id_list[i]; + BLI_assert(keymap_id && keymap_id[0]); + + wmKeyMap *km = WM_keymap_list_find_spaceid_or_empty( + &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW); + /* We shouldn't use keymaps from unrelated spaces. */ + if (km == NULL) { + printf("Keymap: '%s' not found for tool '%s'\n", keymap_id, area->runtime.tool->idname); + continue; + } + handler->keymap_tool = area->runtime.tool; + km_result->keymaps[km_result->keymaps_len++] = km; + } } -wmKeyMap *WM_event_get_keymap_from_toolsystem(wmWindowManager *wm, wmEventHandler_Keymap *handler) +void WM_event_get_keymap_from_toolsystem(wmWindowManager *wm, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result) { + memset(km_result, 0x0, sizeof(*km_result)); + ScrArea *area = handler->dynamic.user_data; handler->keymap_tool = NULL; bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL; @@ -3968,13 +4011,14 @@ wmKeyMap *WM_event_get_keymap_from_toolsystem(wmWindowManager *wm, wmEventHandle /* We shouldn't use keymaps from unrelated spaces. */ if (km != NULL) { handler->keymap_tool = area->runtime.tool; - return km; + km_result->keymaps[km_result->keymaps_len++] = km; + } + else { + printf( + "Keymap: '%s' not found for tool '%s'\n", tref_rt->keymap, area->runtime.tool->idname); } - printf( - "Keymap: '%s' not found for tool '%s'\n", tref_rt->keymap, area->runtime.tool->idname); } } - return NULL; } struct wmEventHandler_Keymap *WM_event_add_keymap_handler_dynamic( @@ -5088,18 +5132,22 @@ void WM_set_locked_interface(wmWindowManager *wm, bool lock) /** \name Event / Keymap Matching API * \{ */ -wmKeyMap *WM_event_get_keymap_from_handler(wmWindowManager *wm, wmEventHandler_Keymap *handler) +void WM_event_get_keymaps_from_handler(wmWindowManager *wm, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result) { - wmKeyMap *keymap; if (handler->dynamic.keymap_fn != NULL) { - keymap = handler->dynamic.keymap_fn(wm, handler); + handler->dynamic.keymap_fn(wm, handler, km_result); BLI_assert(handler->keymap == NULL); } else { - keymap = WM_keymap_active(wm, handler->keymap); + memset(km_result, 0x0, sizeof(*km_result)); + wmKeyMap *keymap = WM_keymap_active(wm, handler->keymap); BLI_assert(keymap != NULL); + if (keymap != NULL) { + km_result->keymaps[km_result->keymaps_len++] = keymap; + } } - return keymap; } wmKeyMapItem *WM_event_match_keymap_item(bContext *C, wmKeyMap *keymap, const wmEvent *event) @@ -5128,11 +5176,15 @@ wmKeyMapItem *WM_event_match_keymap_item_from_handlers(bContext *C, else if (handler_base->poll == NULL || handler_base->poll(CTX_wm_region(C), event)) { if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; - wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler); - if (keymap && WM_keymap_poll(C, keymap)) { - wmKeyMapItem *kmi = WM_event_match_keymap_item(C, keymap, event); - if (kmi != NULL) { - return kmi; + wmEventHandler_KeymapResult km_result; + WM_event_get_keymaps_from_handler(wm, handler, &km_result); + for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { + wmKeyMap *keymap = km_result.keymaps[km_index]; + if (WM_keymap_poll(C, keymap)) { + wmKeyMapItem *kmi = WM_event_match_keymap_item(C, keymap, event); + if (kmi != NULL) { + return kmi; + } } } } diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index f955abaed53..e5aedfc7f47 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -462,7 +462,9 @@ bool WM_keymap_poll(bContext *C, wmKeyMap *keymap) /* Empty key-maps may be missing more there may be a typo in the name. * Warn early to avoid losing time investigating each case. * When developing a customized Blender though you may want empty keymaps. */ - if (!U.app_template[0]) { + if (!U.app_template[0] && + /* Fallback key-maps may be intentionally empty, don't flood the output. */ + !BLI_str_endswith(keymap->idname, " (fallback)")) { CLOG_WARN(WM_LOG_KEYMAPS, "empty keymap '%s'", keymap->idname); } } @@ -1402,15 +1404,19 @@ static wmKeyMapItem *wm_keymap_item_find_handlers(const bContext *C, LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; - wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler); - if (keymap && WM_keymap_poll((bContext *)C, keymap)) { - wmKeyMapItem *kmi = wm_keymap_item_find_in_keymap( - keymap, opname, properties, is_strict, params); - if (kmi != NULL) { - if (r_keymap) { - *r_keymap = keymap; + wmEventHandler_KeymapResult km_result; + WM_event_get_keymaps_from_handler(wm, handler, &km_result); + for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { + wmKeyMap *keymap = km_result.keymaps[km_index]; + if (WM_keymap_poll((bContext *)C, keymap)) { + wmKeyMapItem *kmi = wm_keymap_item_find_in_keymap( + keymap, opname, properties, is_strict, params); + if (kmi != NULL) { + if (r_keymap) { + *r_keymap = keymap; + } + return kmi; } - return kmi; } } } diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index 5eaf026191f..0c24520d565 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -326,7 +326,10 @@ void WM_toolsystem_ref_set_from_runtime(struct bContext *C, bool use_fallback_keymap = false; if (tref->idname_fallback[0] || tref->runtime->keymap_fallback[0]) { - if (tref_rt->gizmo_group[0]) { + if (tref_rt->flag & TOOLREF_FLAG_FALLBACK_KEYMAP) { + use_fallback_keymap = true; + } + else if (tref_rt->gizmo_group[0]) { wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(tref_rt->gizmo_group, false); if (gzgt) { if (gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { From 8b8a678cdf1b134a83dc46afd8dbd285812a6603 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 21 Sep 2021 17:55:44 +1000 Subject: [PATCH 0045/1500] Keymap: preference for Alt-LMB to use the active tool With the LMB select key-map some tools required clicking on a gizmo. With this preference it's possible to hold Alt and click anywhere. Addresses T83689. --- release/scripts/presets/keyconfig/Blender.py | 14 ++- .../keyconfig/keymap_data/blender_default.py | 114 +++++++++++------- 2 files changed, 85 insertions(+), 43 deletions(-) diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index 1690b03184a..5ecb67d99ba 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -76,6 +76,14 @@ class Prefs(bpy.types.KeyConfigPreferences): default=False, update=update_fn, ) + use_alt_tool: BoolProperty( + name="Alt Tool Access", + description=( + "Hold Alt to use the active tool when the gizmo would normally be required" + ), + default=False, + update=update_fn, + ) use_select_all_toggle: BoolProperty( name="Select All Toggles", description=( @@ -199,8 +207,11 @@ class Prefs(bpy.types.KeyConfigPreferences): col = layout.column() sub = col.column(align=True) row = sub.row() - row.prop(self, "use_select_all_toggle") row.prop(self, "use_alt_click_leader") + if is_select_left: + row.prop(self, "use_alt_tool") + row = sub.row() + row.prop(self, "use_select_all_toggle") # 3DView settings. col = layout.column() @@ -248,6 +259,7 @@ def load(): kc_prefs.gizmo_action == 'DRAG' ), use_fallback_tool=(True if (kc_prefs.select_mouse == 'LEFT') else (kc_prefs.rmb_action == 'FALLBACK_TOOL')), + use_alt_tool=(kc_prefs.use_alt_tool and kc_prefs.select_mouse == 'LEFT'), use_alt_click_leader=kc_prefs.use_alt_click_leader, use_pie_click_drag=kc_prefs.use_pie_click_drag, ), diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 6a3064fc820..1f20f493505 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -56,6 +56,8 @@ class Params: "use_v3d_mmb_pan", # Alt click to access tools. "use_alt_click_leader", + # Optionally use a modifier to access tools. + "tool_modifier", # Experimental option. "use_pie_click_drag", "v3d_tilde_action", @@ -87,6 +89,7 @@ class Params: use_v3d_tab_menu=False, use_v3d_shade_ex_pie=False, use_v3d_mmb_pan=False, + use_alt_tool=False, use_alt_click_leader=False, use_pie_click_drag=False, v3d_tilde_action='VIEW', @@ -111,6 +114,7 @@ class Params: self.use_fallback_tool = use_fallback_tool self.use_fallback_tool_rmb = use_fallback_tool self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value + self.tool_modifier = {} else: # Left mouse select uses Click event for selection. This is a little # less immediate, but is needed to distinguish between click and tweak @@ -134,6 +138,12 @@ class Params: self.use_fallback_tool_rmb = False self.select_mouse_value_fallback = self.select_mouse_value + if use_alt_tool: + # Allow `Alt` to be pressed or not. + self.tool_modifier = {"alt": -1} + else: + self.tool_modifier = {} + self.use_mouse_emulate_3_button = use_mouse_emulate_3_button @@ -1231,7 +1241,7 @@ def km_view3d(params): # Selection. *_template_view3d_select( type=params.select_mouse, - value=('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value), + value=params.select_mouse_value_fallback, legacy=params.legacy, ), ("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None), @@ -5985,7 +5995,7 @@ def km_image_editor_tool_uv_rip_region(params): "Image Editor Tool: Uv, Rip Region", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("uv.rip_move", {"type": params.tool_tweak, "value": 'ANY'}, + ("uv.rip_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6015,7 +6025,7 @@ def km_image_editor_tool_uv_move(params): "Image Editor Tool: Uv, Move", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.translate", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6026,7 +6036,7 @@ def km_image_editor_tool_uv_rotate(params): "Image Editor Tool: Uv, Rotate", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.rotate", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.rotate", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6037,7 +6047,7 @@ def km_image_editor_tool_uv_scale(params): "Image Editor Tool: Uv, Scale", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.resize", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.resize", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6178,7 +6188,8 @@ def km_3d_view_tool_transform(params): "3D View Tool: Transform", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.from_gizmo", {"type": params.tool_tweak, "value": 'ANY'}, None), + ("transform.from_gizmo", + {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), ]}, ) @@ -6188,7 +6199,8 @@ def km_3d_view_tool_move(params): "3D View Tool: Move", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.translate", + {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6199,7 +6211,8 @@ def km_3d_view_tool_rotate(params): "3D View Tool: Rotate", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.rotate", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.rotate", + {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6210,7 +6223,8 @@ def km_3d_view_tool_scale(params): "3D View Tool: Scale", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.resize", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.resize", + {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6222,15 +6236,15 @@ def km_3d_view_tool_shear(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ ("transform.shear", - {"type": params.tool_tweak, "value": 'NORTH'}, + {"type": params.tool_tweak, "value": 'NORTH', **params.tool_modifier}, {"properties": [("release_confirm", True), ("orient_axis_ortho", 'Y')]}), ("transform.shear", - {"type": params.tool_tweak, "value": 'SOUTH'}, + {"type": params.tool_tweak, "value": 'SOUTH', **params.tool_modifier}, {"properties": [("release_confirm", True), ("orient_axis_ortho", 'Y')]}), # Use as fallback to catch diagonals too. ("transform.shear", - {"type": params.tool_tweak, "value": 'ANY'}, + {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True), ("orient_axis_ortho", 'X')]}), ]}, ) @@ -6253,7 +6267,7 @@ def km_3d_view_tool_pose_breakdowner(params): "3D View Tool: Pose, Breakdowner", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("pose.breakdown", {"type": params.tool_tweak, "value": 'ANY'}, None), + ("pose.breakdown", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), ]}, ) @@ -6263,7 +6277,8 @@ def km_3d_view_tool_pose_push(params): "3D View Tool: Pose, Push", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("pose.push", {"type": params.tool_tweak, "value": 'ANY'}, None), + ("pose.push", + {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), ]}, ) @@ -6273,7 +6288,8 @@ def km_3d_view_tool_pose_relax(params): "3D View Tool: Pose, Relax", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("pose.relax", {"type": params.tool_tweak, "value": 'ANY'}, None), + ("pose.relax", + {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), ]}, ) @@ -6283,7 +6299,8 @@ def km_3d_view_tool_edit_armature_roll(params): "3D View Tool: Edit Armature, Roll", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.transform", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.transform", + {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True), ("mode", 'BONE_ROLL')]}), ]}, ) @@ -6294,7 +6311,7 @@ def km_3d_view_tool_edit_armature_bone_size(params): "3D View Tool: Edit Armature, Bone Size", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.transform", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.transform", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True), ("mode", 'BONE_ENVELOPE')]}), ]}, ) @@ -6306,7 +6323,7 @@ def km_3d_view_tool_edit_armature_bone_envelope(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.bbone_resize", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.bbone_resize", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6317,7 +6334,7 @@ def km_3d_view_tool_edit_armature_extrude(params): "3D View Tool: Edit Armature, Extrude", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("armature.extrude_move", {"type": params.tool_tweak, "value": 'ANY'}, + ("armature.extrude_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6328,7 +6345,7 @@ def km_3d_view_tool_edit_armature_extrude_to_cursor(params): "3D View Tool: Edit Armature, Extrude to Cursor", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("armature.click_extrude", {"type": params.tool_mouse, "value": 'PRESS'}, None), + ("armature.click_extrude", {"type": params.tool_mouse, "value": 'PRESS', **params.tool_modifier}, None), ]}, ) @@ -6349,7 +6366,7 @@ def km_3d_view_tool_edit_mesh_extrude_region(params): "3D View Tool: Edit Mesh, Extrude Region", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.extrude_context_move", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.extrude_context_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6360,7 +6377,7 @@ def km_3d_view_tool_edit_mesh_extrude_manifold(params): "3D View Tool: Edit Mesh, Extrude Manifold", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.extrude_manifold", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.extrude_manifold", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [ ("MESH_OT_extrude_region", [("use_dissolve_ortho_edges", True)]), ("TRANSFORM_OT_translate", [ @@ -6379,7 +6396,7 @@ def km_3d_view_tool_edit_mesh_extrude_along_normals(params): "3D View Tool: Edit Mesh, Extrude Along Normals", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.extrude_region_shrink_fatten", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.extrude_region_shrink_fatten", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("TRANSFORM_OT_shrink_fatten", [("release_confirm", True)])]}), ]}, ) @@ -6390,7 +6407,7 @@ def km_3d_view_tool_edit_mesh_extrude_individual(params): "3D View Tool: Edit Mesh, Extrude Individual", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.extrude_faces_move", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.extrude_faces_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("TRANSFORM_OT_shrink_fatten", [("release_confirm", True)])]}), ]}, ) @@ -6401,6 +6418,7 @@ def km_3d_view_tool_edit_mesh_extrude_to_cursor(params): "3D View Tool: Edit Mesh, Extrude to Cursor", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("mesh.dupli_extrude_cursor", {"type": params.tool_mouse, "value": 'PRESS'}, None), ]}, ) @@ -6411,7 +6429,7 @@ def km_3d_view_tool_edit_mesh_inset_faces(params): "3D View Tool: Edit Mesh, Inset Faces", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.inset", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.inset", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6422,7 +6440,7 @@ def km_3d_view_tool_edit_mesh_bevel(params): "3D View Tool: Edit Mesh, Bevel", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.bevel", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.bevel", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6433,6 +6451,7 @@ def km_3d_view_tool_edit_mesh_loop_cut(params): "3D View Tool: Edit Mesh, Loop Cut", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("mesh.loopcut_slide", {"type": params.tool_mouse, "value": 'PRESS'}, {"properties": [("TRANSFORM_OT_edge_slide", [("release_confirm", True)])]}), ]}, @@ -6444,6 +6463,7 @@ def km_3d_view_tool_edit_mesh_offset_edge_loop_cut(params): "3D View Tool: Edit Mesh, Offset Edge Loop Cut", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("mesh.offset_edge_loops_slide", {"type": params.tool_mouse, "value": 'PRESS'}, None), ]}, ) @@ -6454,6 +6474,7 @@ def km_3d_view_tool_edit_mesh_knife(params): "3D View Tool: Edit Mesh, Knife", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("mesh.knife_tool", {"type": params.tool_mouse, "value": 'PRESS'}, {"properties": [("wait_for_input", False)]}), ]}, @@ -6465,6 +6486,7 @@ def km_3d_view_tool_edit_mesh_bisect(params): "3D View Tool: Edit Mesh, Bisect", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("mesh.bisect", {"type": params.tool_tweak, "value": 'ANY'}, None), ]}, ) @@ -6475,6 +6497,7 @@ def km_3d_view_tool_edit_mesh_poly_build(params): "3D View Tool: Edit Mesh, Poly Build", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("mesh.polybuild_extrude_at_cursor_move", {"type": params.tool_mouse, "value": 'PRESS'}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ("mesh.polybuild_face_at_cursor_move", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True}, @@ -6489,7 +6512,7 @@ def km_3d_view_tool_edit_mesh_spin(params): "3D View Tool: Edit Mesh, Spin", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.spin", {"type": params.tool_tweak, "value": 'ANY'}, None), + ("mesh.spin", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), ]}, ) @@ -6499,7 +6522,7 @@ def km_3d_view_tool_edit_mesh_spin_duplicate(params): "3D View Tool: Edit Mesh, Spin Duplicates", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.spin", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.spin", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("dupli", True)]}), ]}, ) @@ -6510,7 +6533,7 @@ def km_3d_view_tool_edit_mesh_smooth(params): "3D View Tool: Edit Mesh, Smooth", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.vertices_smooth", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.vertices_smooth", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("wait_for_input", False)]}), ]}, ) @@ -6521,7 +6544,7 @@ def km_3d_view_tool_edit_mesh_randomize(params): "3D View Tool: Edit Mesh, Randomize", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.vertex_random", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.vertex_random", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("wait_for_input", False)]}), ]}, ) @@ -6532,7 +6555,7 @@ def km_3d_view_tool_edit_mesh_edge_slide(params): "3D View Tool: Edit Mesh, Edge Slide", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.edge_slide", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.edge_slide", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6543,7 +6566,7 @@ def km_3d_view_tool_edit_mesh_vertex_slide(params): "3D View Tool: Edit Mesh, Vertex Slide", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.vert_slide", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.vert_slide", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6554,7 +6577,7 @@ def km_3d_view_tool_edit_mesh_shrink_fatten(params): "3D View Tool: Edit Mesh, Shrink/Fatten", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.shrink_fatten", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.shrink_fatten", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6565,7 +6588,7 @@ def km_3d_view_tool_edit_mesh_push_pull(params): "3D View Tool: Edit Mesh, Push/Pull", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.push_pull", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.push_pull", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6576,7 +6599,7 @@ def km_3d_view_tool_edit_mesh_to_sphere(params): "3D View Tool: Edit Mesh, To Sphere", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.tosphere", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.tosphere", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6587,7 +6610,7 @@ def km_3d_view_tool_edit_mesh_rip_region(params): "3D View Tool: Edit Mesh, Rip Region", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.rip_move", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.rip_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6598,7 +6621,7 @@ def km_3d_view_tool_edit_mesh_rip_edge(params): "3D View Tool: Edit Mesh, Rip Edge", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.rip_edge_move", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.rip_edge_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6609,6 +6632,7 @@ def km_3d_view_tool_edit_curve_draw(params): "3D View Tool: Edit Curve, Draw", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("curve.draw", {"type": params.tool_mouse, "value": 'PRESS'}, {"properties": [("wait_for_input", False)]}), ]}, @@ -6620,7 +6644,7 @@ def km_3d_view_tool_edit_curve_tilt(params): "3D View Tool: Edit Curve, Tilt", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.tilt", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.tilt", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6631,7 +6655,7 @@ def km_3d_view_tool_edit_curve_radius(params): "3D View Tool: Edit Curve, Radius", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.transform", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.transform", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("mode", 'CURVE_SHRINKFATTEN'), ("release_confirm", True)]}), ]}, ) @@ -6642,7 +6666,7 @@ def km_3d_view_tool_edit_curve_randomize(params): "3D View Tool: Edit Curve, Randomize", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.vertex_random", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.vertex_random", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("wait_for_input", False)]}), ]}, ) @@ -6653,7 +6677,7 @@ def km_3d_view_tool_edit_curve_extrude(params): "3D View Tool: Edit Curve, Extrude", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("curve.extrude_move", {"type": params.tool_tweak, "value": 'ANY'}, + ("curve.extrude_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6664,6 +6688,7 @@ def km_3d_view_tool_edit_curve_extrude_to_cursor(params): "3D View Tool: Edit Curve, Extrude to Cursor", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("curve.vertex_add", {"type": params.tool_mouse, "value": 'PRESS'}, None), ]}, ) @@ -7053,7 +7078,7 @@ def km_3d_view_tool_edit_gpencil_extrude(params): "3D View Tool: Edit Gpencil, Extrude", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.extrude_move", {"type": params.tool_tweak, "value": 'ANY'}, None), + ("gpencil.extrude_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), ]}, ) @@ -7063,6 +7088,7 @@ def km_3d_view_tool_edit_gpencil_radius(params): "3D View Tool: Edit Gpencil, Radius", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("transform.transform", {"type": params.tool_tweak, "value": 'ANY'}, {"properties": [("mode", 'GPENCIL_SHRINKFATTEN'), ("release_confirm", True)]}), ]}, @@ -7074,6 +7100,7 @@ def km_3d_view_tool_edit_gpencil_bend(params): "3D View Tool: Edit Gpencil, Bend", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("transform.bend", {"type": params.tool_tweak, "value": 'ANY'}, {"properties": [("release_confirm", True)]}), ]}, @@ -7085,6 +7112,7 @@ def km_3d_view_tool_edit_gpencil_shear(params): "3D View Tool: Edit Gpencil, Shear", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("transform.shear", {"type": params.tool_tweak, "value": 'ANY'}, {"properties": [("release_confirm", True)]}), ]}, @@ -7096,6 +7124,7 @@ def km_3d_view_tool_edit_gpencil_to_sphere(params): "3D View Tool: Edit Gpencil, To Sphere", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("transform.tosphere", {"type": params.tool_tweak, "value": 'ANY'}, {"properties": [("release_confirm", True)]}), ]}, @@ -7107,6 +7136,7 @@ def km_3d_view_tool_edit_gpencil_transform_fill(params): "3D View Tool: Edit Gpencil, Transform Fill", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ + # No need for `tool_modifier` since this takes all input. ("gpencil.transform_fill", {"type": params.tool_tweak, "value": 'ANY'}, {"properties": [("release_confirm", True)]}), ]}, From f5c6029cc51d69b39e39038cf22716ef8c50a947 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 21 Sep 2021 17:55:45 +1000 Subject: [PATCH 0046/1500] Keymap: preference for keys to set the active tool With "Keys Activate Tools" preference enabled, keys such as G/R/S activate the move/rotate/scale tool instead of the modal operator. Addresses T83692. --- release/scripts/presets/keyconfig/Blender.py | 11 ++ .../keyconfig/keymap_data/blender_default.py | 159 +++++++++++++----- 2 files changed, 130 insertions(+), 40 deletions(-) diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index 5ecb67d99ba..15cc6097979 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -54,6 +54,15 @@ class Prefs(bpy.types.KeyConfigPreferences): default='PLAY', update=update_fn, ) + use_key_activate_tools: BoolProperty( + name="Keys Activate Tools", + description=( + "Key shortcuts such as G, R, and S activate the tool instead of running it immediately" + ), + default=False, + update=update_fn, + ) + rmb_action: EnumProperty( name="Right Mouse Select Action", items=( @@ -212,6 +221,7 @@ class Prefs(bpy.types.KeyConfigPreferences): row.prop(self, "use_alt_tool") row = sub.row() row.prop(self, "use_select_all_toggle") + row.prop(self, "use_key_activate_tools", text="Key Activates Tools") # 3DView settings. col = layout.column() @@ -248,6 +258,7 @@ def load(): prefs.inputs.mouse_emulate_3_button_modifier == 'ALT' ), spacebar_action=kc_prefs.spacebar_action, + use_key_activate_tools=kc_prefs.use_key_activate_tools, v3d_tilde_action=kc_prefs.v3d_tilde_action, use_v3d_mmb_pan=(kc_prefs.v3d_mmb_action == 'PAN'), v3d_alt_mmb_drag_action=kc_prefs.v3d_alt_mmb_drag_action, diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 1f20f493505..4120006edad 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -56,6 +56,8 @@ class Params: "use_v3d_mmb_pan", # Alt click to access tools. "use_alt_click_leader", + # Transform keys G/S/R activate tools instead of immediately transforming. + "use_key_activate_tools", # Optionally use a modifier to access tools. "tool_modifier", # Experimental option. @@ -83,6 +85,7 @@ class Params: # User preferences. spacebar_action='TOOL', + use_key_activate_tools=False, use_select_all_toggle=False, use_gizmo_drag=True, use_fallback_tool=False, @@ -149,6 +152,7 @@ class Params: # User preferences self.spacebar_action = spacebar_action + self.use_key_activate_tools = use_key_activate_tools self.use_gizmo_drag = use_gizmo_drag self.use_select_all_toggle = use_select_all_toggle @@ -208,6 +212,16 @@ def op_tool_cycle(tool, kmi_args): return ("wm.tool_set_by_id", kmi_args, {"properties": [("name", tool), ("cycle", True)]}) +# Utility to select between an operator and a tool, +# without having to duplicate key map item arguments. +def op_tool_optional(op_args, tool_pair, params): + if params.use_key_activate_tools: + kmi_args = op_args[1] + op_tool_fn, tool_id = tool_pair + return op_tool_fn(tool_id, kmi_args) + return op_args + + # ------------------------------------------------------------------------------ # Keymap Templates @@ -982,10 +996,16 @@ def km_uv_editor(params): op_menu("IMAGE_MT_uvs_select_mode", {"type": 'TAB', "value": 'PRESS', "ctrl": True}), *_template_items_proportional_editing( params, connected=False, toggle_data_path='tool_settings.use_proportional_edit'), - ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), - ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), - ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), + op_tool_optional( + ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.move"), params), + op_tool_optional( + ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.rotate"), params), + op_tool_optional( + ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.scale"), params), ("transform.shear", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), ("transform.mirror", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), ("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True}, @@ -1262,13 +1282,23 @@ def km_view3d(params): ("view3d.copybuffer", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("view3d.pastebuffer", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), # Transform. - ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), - ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), - ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), + op_tool_optional( + ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.move"), params), + op_tool_optional( + ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.rotate"), params), + op_tool_optional( + ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.scale"), params), + op_tool_optional( + ("transform.tosphere", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None), + (op_tool_cycle, "builtin.to_sphere"), params), + op_tool_optional( + ("transform.shear", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), + (op_tool_cycle, "builtin.shear"), params), ("transform.bend", {"type": 'W', "value": 'PRESS', "shift": True}, None), - ("transform.tosphere", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None), - ("transform.shear", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), ("transform.mirror", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), ("object.transform_axis_target", {"type": 'T', "value": 'PRESS', "shift": True}, None), ("transform.skin_resize", {"type": 'A', "value": 'PRESS', "ctrl": True}, None), @@ -3297,14 +3327,18 @@ def km_grease_pencil_stroke_edit_mode(params): items.extend([ # Interpolation - ("gpencil.interpolate", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), + op_tool_optional( + ("gpencil.interpolate", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), + (op_tool_cycle, "builtin.interpolate"), params), ("gpencil.interpolate_sequence", {"type": 'E', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Selection *_grease_pencil_selection(params), # Duplicate and move selected points ("gpencil.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), # Extrude and move selected points - ("gpencil.extrude_move", {"type": 'E', "value": 'PRESS'}, None), + op_tool_optional( + ("gpencil.extrude_move", {"type": 'E', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.extrude"), params), # Delete op_menu("VIEW3D_MT_edit_gpencil_delete", {"type": 'X', "value": 'PRESS'}), op_menu("VIEW3D_MT_edit_gpencil_delete", {"type": 'DEL', "value": 'PRESS'}), @@ -3349,16 +3383,30 @@ def km_grease_pencil_stroke_edit_mode(params): # Merge Layer ("gpencil.layer_merge", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Transform tools - ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), - ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), - ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), + op_tool_optional( + ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.move"), params), + op_tool_optional( + ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.rotate"), params), + op_tool_optional( + ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.scale"), params), + op_tool_optional( + ("transform.tosphere", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None), + (op_tool_cycle, "builtin.to_sphere"), params), + op_tool_optional( + ("transform.shear", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), + (op_tool_cycle, "builtin.shear"), params), ("transform.mirror", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), - ("transform.bend", {"type": 'W', "value": 'PRESS', "shift": True}, None), - ("transform.tosphere", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None), - ("transform.shear", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), - ("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True}, - {"properties": [("mode", 'GPENCIL_SHRINKFATTEN')]}), + op_tool_optional( + ("transform.bend", {"type": 'W', "value": 'PRESS', "shift": True}, None), + (op_tool_cycle, "builtin.bend"), params), + op_tool_optional( + ("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True}, + {"properties": [("mode", 'GPENCIL_SHRINKFATTEN')]}), + (op_tool_cycle, "builtin.radius"), params), ("transform.transform", {"type": 'F', "value": 'PRESS', "shift": True}, {"properties": [("mode", 'GPENCIL_OPACITY')]}), # Proportional editing. @@ -3436,7 +3484,9 @@ def km_grease_pencil_stroke_paint_mode(params): ("gpencil.active_frames_delete_all", {"type": 'X', "value": 'PRESS', "shift": True}, None), ("gpencil.active_frames_delete_all", {"type": 'DEL', "value": 'PRESS', "shift": True}, None), # Interpolation - ("gpencil.interpolate", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), + op_tool_optional( + ("gpencil.interpolate", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), + (op_tool_cycle, "builtin.interpolate"), params), ("gpencil.interpolate_sequence", {"type": 'E', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Show/hide ("gpencil.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), @@ -4301,7 +4351,9 @@ def km_curve(params): {"type": params.select_mouse, "value": params.select_mouse_value_fallback, "ctrl": True}, None), ("curve.separate", {"type": 'P', "value": 'PRESS'}, None), ("curve.split", {"type": 'Y', "value": 'PRESS'}, None), - ("curve.extrude_move", {"type": 'E', "value": 'PRESS'}, None), + op_tool_optional( + ("curve.extrude_move", {"type": 'E', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.extrude"), params), ("curve.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), ("curve.make_segment", {"type": 'F', "value": 'PRESS'}, None), ("curve.cyclic_toggle", {"type": 'C', "value": 'PRESS', "alt": True}, None), @@ -4310,7 +4362,9 @@ def km_curve(params): ("curve.dissolve_verts", {"type": 'X', "value": 'PRESS', "ctrl": True}, None), ("curve.dissolve_verts", {"type": 'DEL', "value": 'PRESS', "ctrl": True}, None), ("curve.tilt_clear", {"type": 'T', "value": 'PRESS', "alt": True}, None), - ("transform.tilt", {"type": 'T', "value": 'PRESS', "ctrl": True}, None), + op_tool_optional( + ("transform.tilt", {"type": 'T', "value": 'PRESS', "ctrl": True}, None), + (op_tool_cycle, "builtin.tilt"), params), ("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True}, {"properties": [("mode", 'CURVE_SHRINKFATTEN')]}), ("curve.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), @@ -4737,13 +4791,21 @@ def km_mesh(params): items.extend([ # Tools. - ("mesh.loopcut_slide", {"type": 'R', "value": 'PRESS', "ctrl": True}, - {"properties": [("TRANSFORM_OT_edge_slide", [("release_confirm", False)],)]}), - ("mesh.offset_edge_loops_slide", {"type": 'R', "value": 'PRESS', "shift": True, "ctrl": True}, - {"properties": [("TRANSFORM_OT_edge_slide", [("release_confirm", False)],)]}), - ("mesh.inset", {"type": 'I', "value": 'PRESS'}, None), - ("mesh.bevel", {"type": 'B', "value": 'PRESS', "ctrl": True}, - {"properties": [("affect", 'EDGES')]}), + op_tool_optional( + ("mesh.loopcut_slide", {"type": 'R', "value": 'PRESS', "ctrl": True}, + {"properties": [("TRANSFORM_OT_edge_slide", [("release_confirm", False)],)]}), + (op_tool_cycle, "builtin.loop_cut"), params), + op_tool_optional( + ("mesh.offset_edge_loops_slide", {"type": 'R', "value": 'PRESS', "shift": True, "ctrl": True}, + {"properties": [("TRANSFORM_OT_edge_slide", [("release_confirm", False)],)]}), + (op_tool_cycle, "builtin.offset_edge_loop_cut"), params), + op_tool_optional( + ("mesh.inset", {"type": 'I', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.inset_faces"), params), + op_tool_optional( + ("mesh.bevel", {"type": 'B', "value": 'PRESS', "ctrl": True}, + {"properties": [("affect", 'EDGES')]}), + (op_tool_cycle, "builtin.bevel"), params), ("mesh.bevel", {"type": 'B', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("affect", 'VERTICES')]}), # Selection modes. @@ -4785,7 +4847,9 @@ def km_mesh(params): {"properties": [("inside", False)]}), ("mesh.normals_make_consistent", {"type": 'N', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("inside", True)]}), - ("view3d.edit_mesh_extrude_move_normal", {"type": 'E', "value": 'PRESS'}, None), + op_tool_optional( + ("view3d.edit_mesh_extrude_move_normal", {"type": 'E', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.extrude_region"), params), op_menu("VIEW3D_MT_edit_mesh_extrude", {"type": 'E', "value": 'PRESS', "alt": True}), ("transform.edge_crease", {"type": 'E', "value": 'PRESS', "shift": True}, None), ("mesh.fill", {"type": 'F', "value": 'PRESS', "alt": True}, None), @@ -4794,8 +4858,11 @@ def km_mesh(params): ("mesh.quads_convert_to_tris", {"type": 'T', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("quad_method", 'FIXED'), ("ngon_method", 'CLIP')]}), ("mesh.tris_convert_to_quads", {"type": 'J', "value": 'PRESS', "alt": True}, None), - ("mesh.rip_move", {"type": 'V', "value": 'PRESS'}, - {"properties": [("MESH_OT_rip", [("use_fill", False)],)]}), + op_tool_optional( + ("mesh.rip_move", {"type": 'V', "value": 'PRESS'}, + {"properties": [("MESH_OT_rip", [("use_fill", False)],)]}), + (op_tool_cycle, "builtin.rip_region"), params), + # No tool is available for this. ("mesh.rip_move", {"type": 'V', "value": 'PRESS', "alt": True}, {"properties": [("MESH_OT_rip", [("use_fill", True)],)]}), ("mesh.rip_edge_move", {"type": 'D', "value": 'PRESS', "alt": True}, None), @@ -4809,7 +4876,9 @@ def km_mesh(params): ("mesh.split", {"type": 'Y', "value": 'PRESS'}, None), ("mesh.vert_connect_path", {"type": 'J', "value": 'PRESS'}, None), ("mesh.point_normals", {"type": 'L', "value": 'PRESS', "alt": True}, None), - ("transform.vert_slide", {"type": 'V', "value": 'PRESS', "shift": True}, None), + op_tool_optional( + ("transform.vert_slide", {"type": 'V', "value": 'PRESS', "shift": True}, None), + (op_tool_cycle, "builtin.vertex_slide"), params), ("mesh.dupli_extrude_cursor", {"type": params.action_mouse, "value": 'CLICK', "ctrl": True}, {"properties": [("rotate_source", True)]}), ("mesh.dupli_extrude_cursor", {"type": params.action_mouse, "value": 'CLICK', "shift": True, "ctrl": True}, @@ -4818,8 +4887,10 @@ def km_mesh(params): op_menu("VIEW3D_MT_edit_mesh_delete", {"type": 'DEL', "value": 'PRESS'}), ("mesh.dissolve_mode", {"type": 'X', "value": 'PRESS', "ctrl": True}, None), ("mesh.dissolve_mode", {"type": 'DEL', "value": 'PRESS', "ctrl": True}, None), - ("mesh.knife_tool", {"type": 'K', "value": 'PRESS'}, - {"properties": [("use_occlude_geometry", True), ("only_selected", False)]}), + op_tool_optional( + ("mesh.knife_tool", {"type": 'K', "value": 'PRESS'}, + {"properties": [("use_occlude_geometry", True), ("only_selected", False)]}), + (op_tool_cycle, "builtin.knife"), params), ("mesh.knife_tool", {"type": 'K', "value": 'PRESS', "shift": True}, {"properties": [("use_occlude_geometry", False), ("only_selected", True)]}), ("object.vertex_parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None), @@ -4916,7 +4987,9 @@ def km_armature(params): ("armature.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), ("armature.dissolve", {"type": 'X', "value": 'PRESS', "ctrl": True}, None), ("armature.dissolve", {"type": 'DEL', "value": 'PRESS', "ctrl": True}, None), - ("armature.extrude_move", {"type": 'E', "value": 'PRESS'}, None), + op_tool_optional( + ("armature.extrude_move", {"type": 'E', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.extrude"), params), ("armature.extrude_forked", {"type": 'E', "value": 'PRESS', "shift": True}, None), ("armature.click_extrude", {"type": params.action_mouse, "value": 'CLICK', "ctrl": True}, None), ("armature.fill", {"type": 'F', "value": 'PRESS'}, None), @@ -4931,11 +5004,17 @@ def km_armature(params): ("armature.armature_layers", {"type": 'M', "value": 'PRESS', "shift": True}, None), ("armature.bone_layers", {"type": 'M', "value": 'PRESS'}, None), # Special transforms. - ("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, None), - ("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True}, - {"properties": [("mode", 'BONE_ENVELOPE')]}), - ("transform.transform", {"type": 'R', "value": 'PRESS', "ctrl": True}, - {"properties": [("mode", 'BONE_ROLL')]}), + op_tool_optional( + ("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, None), + (op_tool_cycle, "builtin.bone_size"), params), + op_tool_optional( + ("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True}, + {"properties": [("mode", 'BONE_ENVELOPE')]}), + (op_tool_cycle, "builtin.bone_envelope"), params), + op_tool_optional( + ("transform.transform", {"type": 'R', "value": 'PRESS', "ctrl": True}, + {"properties": [("mode", 'BONE_ROLL')]}), + (op_tool_cycle, "builtin.roll"), params), # Menus. *_template_items_context_menu("VIEW3D_MT_armature_context_menu", params.context_menu_event), ]) From 26f9b1ef49772d568560f8ac1d8d2d15bb77849f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 20 Sep 2021 16:41:39 +0200 Subject: [PATCH 0047/1500] LibOverride: make `view_layer` API parameter optional. This is used to find a valid collection in which to instantiate stray objects and collections. In some cases there will be no such active view layer, in which case we can consider using the Scene's master collections children hierarchy instead to find a valid instantiated parent collection for those stray data. --- source/blender/blenkernel/intern/lib_override.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 3fead8b0f39..c60a9104144 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -865,7 +865,9 @@ static void lib_override_library_create_post_process(Main *bmain, Object *ob_ref = (Object *)id_ref; LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { if (BKE_collection_has_object(collection, ob_ref) && - BKE_view_layer_has_collection(view_layer, collection) && + (view_layer != NULL ? + BKE_view_layer_has_collection(view_layer, collection) : + BKE_collection_has_collection(scene->master_collection, collection)) && !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { default_instantiating_collection = collection; } @@ -897,6 +899,8 @@ static void lib_override_library_create_post_process(Main *bmain, * \note It will override all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at * its beginning, so caller code can add extra data-blocks to be overridden as well. * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). * \param id_root: The root ID to create an override from. * \param id_reference: Some reference ID used to do some post-processing after overrides have been * created, may be NULL. Typically, the Empty object instantiating the linked collection we @@ -960,6 +964,8 @@ bool BKE_lib_override_library_template_create(struct ID *id) * \note This is a thin wrapper around \a BKE_lib_override_library_create, only extra work is to * actually convert the proxy itself into an override first. * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). * \return true if override was successfully created. */ bool BKE_lib_override_library_proxy_convert(Main *bmain, @@ -1002,6 +1008,8 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, * data, from an existing override hierarchy. * * \param id_root: The root liboverride ID to resync from. + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). * \return true if override was successfully resynced. */ bool BKE_lib_override_library_resync(Main *bmain, @@ -1723,6 +1731,9 @@ static int lib_override_libraries_index_define(Main *bmain) * * Then it will handle the resync of necessary IDs (through calls to * #BKE_lib_override_library_resync). + * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). */ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, From fa2c1698b077f510175e79adf3dbf3e1602b1030 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 21 Sep 2021 09:38:30 +0200 Subject: [PATCH 0048/1500] VSE: Image transform tools Add tools for image manipulation in sequencer preview region. This includes: - Translate, rotate and resize operators, tools and gizmos - Origin for image transformation - Median point and individual origins pivot modes - Select and Box select operator works in preview - Image overlay drawing ref T90156 Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D12105 --- .../keyconfig/keymap_data/blender_default.py | 51 ++++- .../scripts/startup/bl_ui/space_sequencer.py | 13 +- .../startup/bl_ui/space_toolsystem_toolbar.py | 40 ++++ .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_300.c | 30 +++ .../blenloader/intern/versioning_defaults.c | 1 + source/blender/editors/animation/anim_ops.c | 5 + .../editors/space_sequencer/sequencer_draw.c | 67 ++++++ .../space_sequencer/sequencer_select.c | 123 ++++++++++- .../editors/space_sequencer/space_sequencer.c | 73 ++++++- .../blender/editors/transform/CMakeLists.txt | 1 + source/blender/editors/transform/transform.c | 12 +- source/blender/editors/transform/transform.h | 12 +- .../editors/transform/transform_convert.c | 18 +- .../editors/transform/transform_convert.h | 4 + .../transform_convert_sequencer_image.c | 195 ++++++++++++++++++ .../transform/transform_draw_cursors.c | 2 +- .../editors/transform/transform_generics.c | 7 + .../editors/transform/transform_gizmo_2d.c | 71 ++++++- .../editors/transform/transform_mode.c | 2 +- .../transform/transform_snap_sequencer.c | 4 + source/blender/makesdna/DNA_scene_types.h | 1 + source/blender/makesdna/DNA_sequence_types.h | 2 + source/blender/makesdna/DNA_space_types.h | 2 + source/blender/makesrna/intern/rna_scene.c | 14 ++ .../blender/makesrna/intern/rna_sequencer.c | 6 + source/blender/makesrna/intern/rna_space.c | 5 + source/blender/sequencer/SEQ_iterator.h | 8 +- source/blender/sequencer/SEQ_sequencer.h | 1 + source/blender/sequencer/SEQ_transform.h | 9 + source/blender/sequencer/intern/iterator.c | 120 +++++++++++ source/blender/sequencer/intern/render.c | 99 +-------- source/blender/sequencer/intern/sequencer.c | 9 + .../sequencer/intern/strip_transform.c | 98 +++++++++ 34 files changed, 969 insertions(+), 138 deletions(-) create mode 100644 source/blender/editors/transform/transform_convert_sequencer_image.c diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 4120006edad..a2edf5e541d 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2588,6 +2588,9 @@ def km_sequencercommon(params): ("wm.context_toggle_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, {"properties": [("data_path", 'space_data.view_type'), ("value_1", 'SEQUENCER'), ("value_2", 'PREVIEW')]}), ("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), + ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, None), + ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, + {"properties": [("extend", True)]}), ]) if params.select_mouse == 'LEFTMOUSE' and not params.legacy: @@ -2670,9 +2673,6 @@ def km_sequencer(params): for i in range(10) ) ), - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, None), - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, - {"properties": [("extend", True)]}), ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "alt": True}, {"properties": [("linked_handle", True)]}), ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "alt": True}, @@ -2749,6 +2749,15 @@ def km_sequencerpreview(params): ("sequencer.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS'}, {"properties": [("ratio", 0.125)]}), ("sequencer.sample", {"type": params.action_mouse, "value": 'PRESS'}, None), + ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), + ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), + ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), + ("sequencer.strip_transform_clear", {"type": 'G', "alt": True, "value": 'PRESS'}, + {"properties": [("property", 'POSITION')]}), + ("sequencer.strip_transform_clear", {"type": 'S', "alt": True, "value": 'PRESS'}, + {"properties": [("property", 'SCALE')]}), + ("sequencer.strip_transform_clear", {"type": 'R', "alt": True, "value": 'PRESS'}, + {"properties": [("property", 'ROTATION')]}), ]) return keymap @@ -7316,6 +7325,39 @@ def km_sequencer_editor_tool_blade(_params): ) +def km_sequencer_editor_tool_move(params): + return ( + "Sequencer Tool: Move", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'}, + {"properties": [("release_confirm", True)]}), + ]}, + ) + + +def km_sequencer_editor_tool_rotate(params): + return ( + "Sequencer Tool: Rotate", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("transform.rotate", {"type": params.tool_tweak, "value": 'ANY'}, + {"properties": [("release_confirm", True)]}), + ]}, + ) + + +def km_sequencer_editor_tool_scale(params): + return ( + "Sequencer Tool: Scale", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("transform.resize", {"type": params.tool_tweak, "value": 'ANY'}, + {"properties": [("release_confirm", True)]}), + ]}, + ) + + # ------------------------------------------------------------------------------ # Full Configuration @@ -7569,6 +7611,9 @@ def generate_keymaps(params=None): km_sequencer_editor_tool_select_box(params), km_sequencer_editor_tool_blade(params), km_sequencer_editor_tool_generic_sample(params), + km_sequencer_editor_tool_scale(params), + km_sequencer_editor_tool_rotate(params), + km_sequencer_editor_tool_move(params), ] # ------------------------------------------------------------------------------ diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 88cf8db686c..806b6100cc8 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -141,9 +141,14 @@ class SEQUENCER_HT_header(Header): layout.separator_spacer() + tool_settings = context.tool_settings + sequencer_tool_settings = tool_settings.sequencer_tool_settings + + if st.view_type == 'PREVIEW': + layout.prop(sequencer_tool_settings, "pivot_point", text="", icon_only=True) + layout.separator_spacer() + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: - tool_settings = context.tool_settings - sequencer_tool_settings = tool_settings.sequencer_tool_settings row = layout.row(align=True) row.prop(sequencer_tool_settings, "overlap_mode", text="") row = layout.row(align=True) @@ -209,6 +214,7 @@ class SEQUENCER_PT_preview_overlay(Panel): layout = self.layout layout.active = st.show_strip_overlay + layout.prop(overlay_settings, "show_image_outline") layout.prop(ed, "show_overlay", text="Frame Overlay") layout.prop(overlay_settings, "show_safe_areas", text="Safe Areas") layout.prop(overlay_settings, "show_metadata", text="Metadata") @@ -1756,6 +1762,9 @@ class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel): col = layout.column(align=True) col.prop(strip.transform, "rotation", text="Rotation") + col = layout.column(align=True) + col.prop(strip.transform, "origin") + row = layout.row(heading="Mirror") sub = row.row(align=True) sub.prop(strip, "use_flip_x", text="X", toggle=True) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index a513ab4b2d4..77a6ff79598 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2454,6 +2454,39 @@ class _defs_sequencer_generic: keymap="Sequencer Tool: Sample", ) + @ToolDef.from_fn + def translate(): + return dict( + idname="builtin.move", + label="Move", + icon="ops.transform.translate", + widget="SEQUENCER_GGT_gizmo2d_translate", + operator="transform.translate", + keymap="Sequencer Tool: Move", + ) + + @ToolDef.from_fn + def rotate(): + return dict( + idname="builtin.rotate", + label="Rotate", + icon="ops.transform.rotate", + widget="SEQUENCER_GGT_gizmo2d_rotate", + operator="transform.rotate", + keymap="Sequencer Tool: Rotate", + ) + + @ToolDef.from_fn + def scale(): + return dict( + idname="builtin.scale", + label="Scale", + icon="ops.transform.resize", + widget="SEQUENCER_GGT_gizmo2d_resize", + operator="transform.resize", + keymap="Sequencer Tool: Scale", + ) + class _defs_sequencer_select: @ToolDef.from_fn @@ -3045,6 +3078,10 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): None: [ ], 'PREVIEW': [ + *_tools_select, + _defs_sequencer_generic.translate, + _defs_sequencer_generic.rotate, + _defs_sequencer_generic.scale, _defs_sequencer_generic.sample, *_tools_annotate, ], @@ -3054,6 +3091,9 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): ], 'SEQUENCER_PREVIEW': [ *_tools_select, + _defs_sequencer_generic.translate, + _defs_sequencer_generic.rotate, + _defs_sequencer_generic.scale, _defs_sequencer_generic.blade, _defs_sequencer_generic.sample, *_tools_annotate, diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 63d6b9121d2..b3ee2f411d7 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 23 +#define BLENDER_FILE_SUBVERSION 24 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 30e7c9bde4c..d0fa6c282f4 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -64,6 +64,7 @@ #include "MEM_guardedalloc.h" #include "readfile.h" +#include "SEQ_iterator.h" #include "SEQ_sequencer.h" #include "RNA_access.h" @@ -774,6 +775,14 @@ static void version_geometry_nodes_change_legacy_names(bNodeTree *ntree) } } } +static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data)) +{ + StripTransform *transform = seq->strip->transform; + if (seq->strip->transform != NULL) { + transform->origin[0] = transform->origin[1] = 0.5f; + } + return true; +} /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) @@ -1290,6 +1299,27 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 24)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene); + sequencer_tool_settings->pivot_point = V3D_AROUND_CENTER_MEDIAN; + + if (scene->ed != NULL) { + SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_origin_set, NULL); + } + } + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 074cae669af..f2d5896be03 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -160,6 +160,7 @@ static void blo_update_defaults_screen(bScreen *screen, seq->render_size = SEQ_RENDER_SIZE_PROXY_100; seq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID; + seq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; } else if (area->spacetype == SPACE_TEXT) { /* Show syntax and line numbers in Script workspace text editor. */ diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 450d7cd100e..b4ea33920b2 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -241,6 +241,11 @@ static bool use_sequencer_snapping(bContext *C) /* Modal Operator init */ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + ARegion *region = CTX_wm_region(C); + if (CTX_wm_space_seq(C) != NULL && region->regiontype == RGN_TYPE_PREVIEW) { + return OPERATOR_CANCELLED; + } + /* Change to frame that mouse is over before adding modal handler, * as user could click on a single frame (jump to frame) as well as * click-dragging over a range (modal scrubbing). diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 5b39feacfe3..e063e12a9a8 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -71,6 +71,7 @@ #include "BIF_glutil.h" #include "SEQ_effects.h" +#include "SEQ_iterator.h" #include "SEQ_prefetch.h" #include "SEQ_proxy.h" #include "SEQ_relations.h" @@ -2056,6 +2057,64 @@ static int sequencer_draw_get_transform_preview_frame(Scene *scene) return preview_frame; } +static void seq_draw_image_origin_and_outline(const bContext *C, Sequence *seq) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + if ((seq->flag & SELECT) == 0) { + return; + } + if (ED_screen_animation_no_scrub(CTX_wm_manager(C))) { + return; + } + if ((sseq->flag & SEQ_SHOW_OVERLAY) == 0 || + (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_OUTLINE_SELECTED) == 0) { + return; + } + if (ELEM(sseq->mainb, SEQ_DRAW_IMG_WAVEFORM, SEQ_DRAW_IMG_VECTORSCOPE, SEQ_DRAW_IMG_HISTOGRAM)) { + return; + } + + float origin[2]; + SEQ_image_transform_origin_offset_pixelspace_get(CTX_data_scene(C), seq, origin); + + /* Origin. */ + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA); + immUniform1f("outlineWidth", 1.5f); + immUniformColor3f(1.0f, 1.0f, 1.0f); + immUniform4f("outlineColor", 0.0f, 0.0f, 0.0f, 1.0f); + immUniform1f("size", 15.0f * U.pixelsize); + immBegin(GPU_PRIM_POINTS, 1); + immVertex2f(pos, origin[0], origin[1]); + immEnd(); + immUnbindProgram(); + + /* Outline. */ + float seq_image_quad[4][2]; + SEQ_image_transform_final_quad_get(CTX_data_scene(C), seq, seq_image_quad); + + GPU_line_smooth(true); + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_width(2); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + float col[3]; + UI_GetThemeColor3fv(TH_SEQ_SELECTED, col); + immUniformColor3fv(col); + immUniform1f("lineWidth", U.pixelsize); + immBegin(GPU_PRIM_LINE_LOOP, 4); + immVertex2f(pos, seq_image_quad[0][0], seq_image_quad[0][1]); + immVertex2f(pos, seq_image_quad[1][0], seq_image_quad[1][1]); + immVertex2f(pos, seq_image_quad[2][0], seq_image_quad[2][1]); + immVertex2f(pos, seq_image_quad[3][0], seq_image_quad[3][1]); + immEnd(); + immUnbindProgram(); + GPU_line_width(1); + GPU_blend(GPU_BLEND_NONE); + GPU_line_smooth(false); +} + void sequencer_draw_preview(const bContext *C, Scene *scene, ARegion *region, @@ -2132,9 +2191,17 @@ void sequencer_draw_preview(const bContext *C, sequencer_draw_borders_overlay(sseq, v2d, scene); } + SeqCollection *collection = SEQ_query_rendered_strips(&scene->ed->seqbase, timeline_frame, 0); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + seq_draw_image_origin_and_outline(C, seq); + } + SEQ_collection_free(collection); + if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_OVERLAY)) { sequencer_draw_gpencil_overlay(C); } + #if 0 sequencer_draw_maskedit(C, scene, region, sseq); #endif diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 80d3e2cbdaa..aa6599a7c53 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -44,6 +44,7 @@ #include "SEQ_sequencer.h" #include "SEQ_time.h" #include "SEQ_transform.h" +#include "SEQ_utils.h" /* For menu, popup, icons, etc. */ @@ -385,6 +386,20 @@ void recurs_sel_seq(Sequence *seq_meta) } } +static bool seq_point_image_isect(const Scene *scene, const Sequence *seq, float point[2]) +{ + float seq_image_quad[4][2]; + SEQ_image_transform_final_quad_get(scene, seq, seq_image_quad); + return isect_point_quad_v2( + point, seq_image_quad[0], seq_image_quad[1], seq_image_quad[2], seq_image_quad[3]); +} + +static void sequencer_select_do_updates(bContext *C, Scene *scene) +{ + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -523,12 +538,6 @@ static void sequencer_select_set_active(Scene *scene, Sequence *seq) recurs_sel_seq(seq); } -static void sequencer_select_do_updates(bContext *C, Scene *scene) -{ - ED_outliner_select_sync_from_sequence_tag(C); - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); -} - static void sequencer_select_side_of_frame(const bContext *C, const View2D *v2d, const int mval[2], @@ -626,6 +635,45 @@ static void sequencer_select_linked_handle(const bContext *C, } } +/* Check if click happened on image which belongs to strip. If multiple strips are found, loop + * through them in order. */ +static Sequence *seq_select_seq_from_preview(const bContext *C, const int mval[2]) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SpaceSeq *sseq = CTX_wm_space_seq(C); + View2D *v2d = UI_view2d_fromcontext(C); + + float mouseco_view[2]; + UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseco_view[0], &mouseco_view[1]); + + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown); + ListBase strips_ordered = {NULL}; + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + if (seq_point_image_isect(scene, seq, mouseco_view)) { + BLI_remlink(seqbase, seq); + BLI_addtail(&strips_ordered, seq); + } + } + SEQ_collection_free(strips); + SEQ_sort(&strips_ordered); + + Sequence *seq_active = SEQ_select_active_get(scene); + Sequence *seq_select = strips_ordered.first; + LISTBASE_FOREACH (Sequence *, seq_iter, &strips_ordered) { + if (seq_iter == seq_active && seq_iter->next != NULL) { + seq_select = seq_iter->next; + break; + } + } + + BLI_movelisttolist(seqbase, &strips_ordered); + + return seq_select; +} + static bool element_already_selected(const Sequence *seq, const int handle_clicked) { const bool handle_already_selected = ((handle_clicked == SEQ_SIDE_LEFT) && @@ -680,8 +728,15 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) mval[0] = RNA_int_get(op->ptr, "mouse_x"); mval[1] = RNA_int_get(op->ptr, "mouse_y"); - int handle_clicked; - Sequence *seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); + ARegion *region = CTX_wm_region(C); + int handle_clicked = SEQ_SIDE_NONE; + Sequence *seq = NULL; + if (region->regiontype == RGN_TYPE_PREVIEW) { + seq = seq_select_seq_from_preview(C, mval); + } + else { + seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); + } /* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one keymap, * therefore both properties can be true at the same time. */ @@ -1311,6 +1366,47 @@ void SEQUENCER_OT_select_side(wmOperatorType *ot) /** \name Box Select Operator * \{ */ +static bool seq_box_select_rect_image_isect(const Scene *scene, const Sequence *seq, rctf *rect) +{ + float seq_image_quad[4][2]; + SEQ_image_transform_final_quad_get(scene, seq, seq_image_quad); + float rect_quad[4][2] = {{rect->xmax, rect->ymax}, + {rect->xmax, rect->ymin}, + {rect->xmin, rect->ymin}, + {rect->xmin, rect->ymax}}; + + return seq_point_image_isect(scene, seq, rect_quad[0]) || + seq_point_image_isect(scene, seq, rect_quad[1]) || + seq_point_image_isect(scene, seq, rect_quad[2]) || + seq_point_image_isect(scene, seq, rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[0], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[1], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[2], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[3], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]); +} + +static void seq_box_select_seq_from_preview(const bContext *C, rctf *rect) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SpaceSeq *sseq = CTX_wm_space_seq(C); + + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + if (seq_box_select_rect_image_isect(scene, seq, rect)) { + seq->flag |= SELECT; + } + } + + SEQ_collection_free(strips); +} + static int sequencer_box_select_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -1333,6 +1429,13 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) WM_operator_properties_border_to_rctf(op, &rectf); UI_view2d_region_to_view_rctf(v2d, &rectf, &rectf); + ARegion *region = CTX_wm_region(C); + if (region->regiontype == RGN_TYPE_PREVIEW) { + seq_box_select_seq_from_preview(C, &rectf); + sequencer_select_do_updates(C, scene); + return OPERATOR_FINISHED; + } + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { rctf rq; seq_rectf(seq, &rq); @@ -1378,9 +1481,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) } } - ED_outliner_select_sync_from_sequence_tag(C); - - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); + sequencer_select_do_updates(C, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 0d09f2564e8..cb5fe6f0ef3 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -42,6 +42,7 @@ #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_transform.h" #include "ED_view3d.h" #include "ED_view3d_offscreen.h" /* Only for sequencer view3d drawing callback. */ @@ -98,10 +99,11 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce sseq->chanshown = 0; sseq->view = SEQ_VIEW_SEQUENCE; sseq->mainb = SEQ_DRAW_IMG_IMBUF; - sseq->flag = SEQ_PREVIEW_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | - SEQ_TIMELINE_SHOW_FCURVES | SEQ_ZOOM_TO_FIT | SEQ_SHOW_OVERLAY | - SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_SOURCE | - SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID; + sseq->flag = SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_ZOOM_TO_FIT | SEQ_SHOW_OVERLAY; + sseq->preview_overlay.flag = SEQ_PREVIEW_SHOW_GPENCIL | SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; + sseq->timeline_overlay.flag = SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_SOURCE | + SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID | + SEQ_TIMELINE_SHOW_FCURVES; /* Tool header. */ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); @@ -481,11 +483,72 @@ static void SEQUENCER_GGT_navigate(wmGizmoGroupType *gzgt) VIEW2D_GGT_navigate_impl(gzgt, "SEQUENCER_GGT_navigate"); } +static void SEQUENCER_GGT_gizmo2d(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Transform Gizmo"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_xform_callbacks_set(gzgt); +} + +static void SEQUENCER_GGT_gizmo2d_translate(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Translate Gizmo"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d_translate"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(gzgt); +} + +static void SEQUENCER_GGT_gizmo2d_resize(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Transform Gizmo Resize"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d_resize"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_resize_callbacks_set(gzgt); +} + +static void SEQUENCER_GGT_gizmo2d_rotate(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Transform Gizmo Resize"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d_rotate"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_rotate_callbacks_set(gzgt); +} + static void sequencer_gizmos(void) { wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure( &(const struct wmGizmoMapType_Params){SPACE_SEQ, RGN_TYPE_PREVIEW}); + WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d); + WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_translate); + WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_resize); + WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_rotate); + WM_gizmogrouptype_append_and_link(gzmap_type, SEQUENCER_GGT_navigate); } @@ -742,6 +805,8 @@ static void sequencer_preview_region_listener(const wmRegionListenerParams *para ARegion *region = params->region; wmNotifier *wmn = params->notifier; + WM_gizmomap_tag_refresh(region->gizmo_map); + /* Context changes. */ switch (wmn->category) { case NC_GPENCIL: diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index e9efed3cd61..64a720322c1 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -60,6 +60,7 @@ set(SRC transform_convert_particle.c transform_convert_sculpt.c transform_convert_sequencer.c + transform_convert_sequencer_image.c transform_convert_tracking.c transform_draw_cursors.c transform_generics.c diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 58491f8c2d3..e58e524e341 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1703,11 +1703,13 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->draw_handle_cursor = WM_paint_cursor_activate( SPACE_TYPE_ANY, RGN_TYPE_ANY, transform_draw_cursor_poll, transform_draw_cursor_draw, t); } - else if (t->spacetype == SPACE_SEQ) { - t->draw_handle_view = ED_region_draw_cb_activate( - t->region->type, drawTransformView, t, REGION_DRAW_POST_VIEW); - } - else if (ELEM(t->spacetype, SPACE_IMAGE, SPACE_CLIP, SPACE_NODE, SPACE_GRAPH, SPACE_ACTION)) { + else if (ELEM(t->spacetype, + SPACE_IMAGE, + SPACE_CLIP, + SPACE_NODE, + SPACE_GRAPH, + SPACE_ACTION, + SPACE_SEQ)) { t->draw_handle_view = ED_region_draw_cb_activate( t->region->type, drawTransformView, t, REGION_DRAW_POST_VIEW); t->draw_handle_cursor = WM_paint_cursor_activate( diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index d1a1937cef1..7f4e533ccd7 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -87,15 +87,16 @@ typedef enum { CTX_PAINT_CURVE = (1 << 7), CTX_POSE_BONE = (1 << 8), CTX_TEXTURE_SPACE = (1 << 9), + CTX_SEQUENCER_IMAGE = (1 << 10), - CTX_NO_PET = (1 << 10), - CTX_AUTOCONFIRM = (1 << 11), + CTX_NO_PET = (1 << 11), + CTX_AUTOCONFIRM = (1 << 12), /** When transforming object's, adjust the object data so it stays in the same place. */ - CTX_OBMODE_XFORM_OBDATA = (1 << 12), + CTX_OBMODE_XFORM_OBDATA = (1 << 13), /** Transform object parents without moving their children. */ - CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 13), + CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 14), /** Enable edge scrolling in 2D views */ - CTX_VIEW2D_EDGE_PAN = (1 << 14), + CTX_VIEW2D_EDGE_PAN = (1 << 15), } eTContext; /** #TransInfo.flag */ @@ -240,6 +241,7 @@ typedef enum { TC_PARTICLE_VERTS, TC_SCULPT, TC_SEQ_DATA, + TC_SEQ_IMAGE_DATA, TC_TRACKING_DATA, } eTConvertType; diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index d756e2c90a6..557fa79e7ac 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -955,6 +955,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) case TC_OBJECT_TEXSPACE: case TC_PAINT_CURVE_VERTS: case TC_PARTICLE_VERTS: + case TC_SEQ_IMAGE_DATA: case TC_NONE: default: break; @@ -1042,6 +1043,7 @@ static void init_proportional_edit(TransInfo *t) case TC_PAINT_CURVE_VERTS: case TC_SCULPT: case TC_SEQ_DATA: + case TC_SEQ_IMAGE_DATA: case TC_TRACKING_DATA: case TC_NONE: default: @@ -1120,6 +1122,7 @@ static void init_TransDataContainers(TransInfo *t, case TC_PARTICLE_VERTS: case TC_SCULPT: case TC_SEQ_DATA: + case TC_SEQ_IMAGE_DATA: case TC_TRACKING_DATA: case TC_NONE: default: @@ -1204,6 +1207,7 @@ static eTFlag flags_from_data_type(eTConvertType data_type) case TC_NODE_DATA: case TC_PAINT_CURVE_VERTS: case TC_SEQ_DATA: + case TC_SEQ_IMAGE_DATA: case TC_TRACKING_DATA: return T_POINTS | T_2D_EDIT; case TC_ARMATURE_VERTS: @@ -1282,7 +1286,12 @@ static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armatur convert_type = TC_NLA_DATA; } else if (t->spacetype == SPACE_SEQ) { - convert_type = TC_SEQ_DATA; + if (t->options & CTX_SEQUENCER_IMAGE) { + convert_type = TC_SEQ_IMAGE_DATA; + } + else { + convert_type = TC_SEQ_DATA; + } } else if (t->spacetype == SPACE_GRAPH) { convert_type = TC_GRAPH_EDIT_DATA; @@ -1470,6 +1479,10 @@ void createTransData(bContext *C, TransInfo *t) t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transform. */ createTransSeqData(t); break; + case TC_SEQ_IMAGE_DATA: + t->obedit_type = -1; + createTransSeqImageData(t); + break; case TC_TRACKING_DATA: createTransTrackingData(C, t); break; @@ -1746,6 +1759,9 @@ void recalcData(TransInfo *t) case TC_SEQ_DATA: recalcData_sequencer(t); break; + case TC_SEQ_IMAGE_DATA: + recalcData_sequencer_image(t); + break; case TC_TRACKING_DATA: recalcData_tracking(t); break; diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 9cb0400cad9..66d84bca2d2 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -218,6 +218,10 @@ void createTransSeqData(TransInfo *t); void recalcData_sequencer(TransInfo *t); void special_aftertrans_update__sequencer(bContext *C, TransInfo *t); +/* transform_convert_sequencer_image.c */ +void createTransSeqImageData(TransInfo *t); +void recalcData_sequencer_image(TransInfo *t); + /* transform_convert_tracking.c */ void createTransTrackingData(bContext *C, TransInfo *t); void recalcData_tracking(TransInfo *t); diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c new file mode 100644 index 00000000000..465f8b9a694 --- /dev/null +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -0,0 +1,195 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edtransform + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_space_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_report.h" + +#include "SEQ_iterator.h" +#include "SEQ_relations.h" +#include "SEQ_sequencer.h" +#include "SEQ_time.h" +#include "SEQ_transform.h" +#include "SEQ_utils.h" + +#include "UI_view2d.h" + +#include "transform.h" +#include "transform_convert.h" + +/** Used for sequencer transform. */ +typedef struct TransDataSeq { + struct Sequence *seq; + float orig_origin_position[2]; + float orig_translation[2]; + float orig_scale[2]; + float orig_rotation; +} TransDataSeq; + +static TransData *SeqToTransData(const Scene *scene, + Sequence *seq, + TransData *td, + TransData2D *td2d, + TransDataSeq *tdseq, + int vert_index) +{ + const StripTransform *transform = seq->strip->transform; + float origin[2]; + SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin); + float vertex[2] = {origin[0], origin[1]}; + + /* Add control vertex, so rotation and scale can be calculated. */ + if (vert_index == 1) { + vertex[0] += 1.0f; + } + else if (vert_index == 2) { + vertex[1] += 1.0f; + } + + td2d->loc[0] = vertex[0]; + td2d->loc[1] = vertex[1]; + td2d->loc2d = NULL; + td->loc = td2d->loc; + copy_v3_v3(td->iloc, td->loc); + + td->center[0] = origin[0]; + td->center[1] = origin[1]; + + memset(td->axismtx, 0, sizeof(td->axismtx)); + td->axismtx[2][2] = 1.0f; + unit_m3(td->mtx); + unit_m3(td->smtx); + + tdseq->seq = seq; + copy_v2_v2(tdseq->orig_origin_position, origin); + tdseq->orig_translation[0] = transform->xofs; + tdseq->orig_translation[1] = transform->yofs; + tdseq->orig_scale[0] = transform->scale_x; + tdseq->orig_scale[1] = transform->scale_y; + tdseq->orig_rotation = transform->rotation; + + td->extra = (void *)tdseq; + td->ext = NULL; + td->flag |= TD_SELECTED; + td->dist = 0.0; + + return td; +} + +static void freeSeqData(TransInfo *UNUSED(t), TransDataContainer *tc, TransCustomData *UNUSED(custom_data)) +{ + TransData *td = (TransData *)tc->data; + MEM_freeN(td->extra); +} + +void createTransSeqImageData(TransInfo *t) +{ + Editing *ed = SEQ_editing_get(t->scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, t->scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + + const int count = SEQ_collection_len(strips); + if (ed == NULL || count == 0) { + SEQ_collection_free(strips); + return; + } + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + tc->custom.type.free_cb = freeSeqData; + + tc->data_len = count * 3; /* 3 vertices per sequence are needed. */ + TransData *td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransSeq TransData"); + TransData2D *td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), + "TransSeq TransData2D"); + TransDataSeq *tdseq = MEM_callocN(tc->data_len * sizeof(TransDataSeq), "TransSeq TransDataSeq"); + + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + /* One `Sequence` needs 3 `TransData` entries - center point placed in image origin, then 2 + * points offset by 1 in X and Y direction respectively, so rotation and scale can be + * calculated from these points. */ + SeqToTransData(t->scene, seq, td++, td2d++, tdseq++, 0); + SeqToTransData(t->scene, seq, td++, td2d++, tdseq++, 1); + SeqToTransData(t->scene, seq, td++, td2d++, tdseq++, 2); + } + + SEQ_collection_free(strips); +} + +void recalcData_sequencer_image(TransInfo *t) +{ + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + TransData *td = NULL; + TransData2D *td2d = NULL; + int i; + + for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) { + /* Origin. */ + float loc[2]; + copy_v2_v2(loc, td2d->loc); + i++, td++, td2d++; + + /* X and Y control points used to read scale and rotation. */ + float handle_x[2]; + copy_v2_v2(handle_x, td2d->loc); + sub_v2_v2(handle_x, loc); + i++, td++, td2d++; + float handle_y[2]; + copy_v2_v2(handle_y, td2d->loc); + sub_v2_v2(handle_y, loc); + + TransDataSeq *tdseq = td->extra; + Sequence *seq = tdseq->seq; + StripTransform *transform = seq->strip->transform; + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + + /* Calculate translation. */ + float translation[2]; + copy_v2_v2(translation, tdseq->orig_origin_position); + sub_v2_v2(translation, loc); + mul_v2_v2(translation, mirror); + transform->xofs = tdseq->orig_translation[0] - translation[0]; + transform->yofs = tdseq->orig_translation[1] - translation[1]; + + /* Scale. */ + transform->scale_x = tdseq->orig_scale[0] * fabs(len_v2(handle_x)); + transform->scale_y = tdseq->orig_scale[1] * fabs(len_v2(handle_y)); + + /* Rotation. Scaling can cause negative rotation. */ + if (t->mode == TFM_ROTATION) { + float rotation = angle_signed_v2v2(handle_x, (float[]){1, 0}) * mirror[0] * mirror[1]; + transform->rotation = tdseq->orig_rotation + rotation; + transform->rotation += DEG2RAD(360.0); + transform->rotation = fmod(transform->rotation, DEG2RAD(360.0)); + } + SEQ_relations_invalidate_cache_preprocessed(t->scene, seq); + } +} diff --git a/source/blender/editors/transform/transform_draw_cursors.c b/source/blender/editors/transform/transform_draw_cursors.c index ead8eae0997..af1f3cb72a4 100644 --- a/source/blender/editors/transform/transform_draw_cursors.c +++ b/source/blender/editors/transform/transform_draw_cursors.c @@ -95,7 +95,7 @@ static void drawArrow(const uint pos_id, const enum eArrowDirection dir) bool transform_draw_cursor_poll(bContext *C) { ARegion *region = CTX_wm_region(C); - return (region && region->regiontype == RGN_TYPE_WINDOW) ? 1 : 0; + return (region && ELEM(region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_PREVIEW)) ? 1 : 0; } /** diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index c493b9bd102..fa323f0c1f7 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -59,6 +59,8 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "SEQ_sequencer.h" + #include "transform.h" #include "transform_convert.h" #include "transform_mode.h" @@ -335,6 +337,11 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->options |= CTX_MASK; } } + else if (t->spacetype == SPACE_SEQ && region->regiontype == RGN_TYPE_PREVIEW) { + t->view = ®ion->v2d; + t->around = SEQ_tool_settings_pivot_point_get(t->scene); + t->options |= CTX_SEQUENCER_IMAGE; + } else { if (region) { /* XXX: For now, get View2D from the active region. */ diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 0b677e2560b..0d66db0d7e1 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -49,6 +49,11 @@ #include "ED_screen.h" #include "ED_uvedit.h" +#include "SEQ_iterator.h" +#include "SEQ_sequencer.h" +#include "SEQ_time.h" +#include "SEQ_transform.h" + #include "transform.h" /* own include */ /* -------------------------------------------------------------------- */ @@ -234,17 +239,66 @@ static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min return changed; } +static float gizmo2d_calc_rotation(const bContext *C) +{ + ScrArea *area = CTX_wm_area(C); + if (area->spacetype != SPACE_SEQ) { + return 0.0f; + } + + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + if (seq == ed->act_seq) { + StripTransform *transform = seq->strip->transform; + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + SEQ_collection_free(strips); + return transform->rotation * mirror[0] * mirror[1]; + } + } + + SEQ_collection_free(strips); + return 0.0f; +} + static bool gizmo2d_calc_center(const bContext *C, float r_center[2]) { ScrArea *area = CTX_wm_area(C); + Scene *scene = CTX_data_scene(C); bool has_select = false; zero_v2(r_center); if (area->spacetype == SPACE_IMAGE) { SpaceImage *sima = area->spacedata.first; - Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); ED_uvedit_center_from_pivot_ex(sima, scene, view_layer, r_center, sima->around, &has_select); } + else if (area->spacetype == SPACE_SEQ) { + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + + if (SEQ_collection_len(strips) <= 0) { + SEQ_collection_free(strips); + return false; + } + + has_select = true; + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + float origin[2]; + SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin); + add_v2_v2(r_center, origin); + } + mul_v2_fl(r_center, 1.0f / SEQ_collection_len(strips)); + + SEQ_collection_free(strips); + } return has_select; } @@ -338,7 +392,7 @@ static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup } } - RNA_boolean_set(ptr, "release_confirm", 1); + RNA_boolean_set(ptr, "release_confirm", true); } { @@ -539,6 +593,7 @@ void ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(wmGizmoGroupType *gzgt) typedef struct GizmoGroup_Resize2D { wmGizmo *gizmo_xy[3]; float origin[2]; + float rotation; } GizmoGroup_Resize2D; static GizmoGroup_Resize2D *gizmogroup2d_resize_init(wmGizmoGroup *gzgroup) @@ -571,6 +626,7 @@ static void gizmo2d_resize_refresh(const bContext *C, wmGizmoGroup *gzgroup) ggd->gizmo_xy[i]->flag &= ~WM_GIZMO_HIDDEN; } copy_v2_v2(ggd->origin, origin); + ggd->rotation = gizmo2d_calc_rotation(C); } } @@ -595,6 +651,13 @@ static void gizmo2d_resize_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) { wmGizmo *gz = ggd->gizmo_xy[i]; WM_gizmo_set_matrix_location(gz, origin); + + if (i < 2) { + float axis[3] = {0.0f}, rotated_axis[3]; + axis[i] = 1.0f; + rotate_v3_v3v3fl(rotated_axis, axis, (float[3]){0, 0, 1}, ggd->rotation); + WM_gizmo_set_matrix_rotation_from_z_axis(gz, rotated_axis); + } } } @@ -617,10 +680,6 @@ static void gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgrou /* set up widget data */ RNA_float_set(gz->ptr, "length", 1.0f); - float axis[3] = {0.0f}; - axis[i] = 1.0f; - WM_gizmo_set_matrix_rotation_from_z_axis(gz, axis); - RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX); WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH); diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index b9fb8a86752..b14d499cb66 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -75,7 +75,7 @@ bool transdata_check_local_center(const TransInfo *t, short around) /* implicit: (t->flag & T_EDIT) */ (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) || (t->spacetype == SPACE_GRAPH) || - (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE)))); + (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE | CTX_SEQUENCER_IMAGE)))); } /* Informs if the mode can be switched during modal. */ diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index e82a00bcc77..2acdf5cfd9c 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -254,6 +254,10 @@ static int seq_snap_threshold_get_frame_distance(const TransInfo *t) TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t) { + if (t->data_type == TC_SEQ_IMAGE_DATA) { + return NULL; + } + TransSeqSnapData *snap_data = MEM_callocN(sizeof(TransSeqSnapData), __func__); ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene)); diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 7800e7f9efe..f2244b4ae61 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1344,6 +1344,7 @@ typedef struct SequencerToolSettings { /** When there are many snap points, 0-1 range corresponds to resolution from boundbox to all * possible snap points. */ int snap_distance; + int pivot_point; } SequencerToolSettings; typedef enum eSeqOverlapMode { diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 03c38eb71a0..782b4ac34aa 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -74,6 +74,8 @@ typedef struct StripTransform { float scale_x; float scale_y; float rotation; + /** 0-1 range, use SEQ_image_transform_origin_offset_pixelspace_get to convert to pixel space. */ + float origin[2]; } StripTransform; typedef struct StripColorBalance { diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 6505816256c..84dc58345e3 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -583,6 +583,7 @@ typedef struct SequencerPreviewOverlay { /* SequencerPreviewOverlay.flag */ typedef enum eSpaceSeq_SequencerPreviewOverlay_Flag { + SEQ_PREVIEW_SHOW_OUTLINE_SELECTED = (1 << 2), SEQ_PREVIEW_SHOW_SAFE_MARGINS = (1 << 3), SEQ_PREVIEW_SHOW_GPENCIL = (1 << 4), SEQ_PREVIEW_SHOW_SAFE_CENTER = (1 << 9), @@ -685,6 +686,7 @@ typedef enum eSpaceSeq_Flag { SPACE_SEQ_FLAG_UNUSED_15 = (1 << 15), SPACE_SEQ_FLAG_UNUSED_16 = (1 << 16), SEQ_USE_PROXIES = (1 << 17), + SEQ_SHOW_GRID = (1 << 18), } eSpaceSeq_Flag; /* SpaceSeq.view */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index badaaa14aa4..1762b964f8d 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3525,6 +3525,16 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem pivot_points[] = { + {V3D_AROUND_CENTER_MEDIAN, "MEDIAN", ICON_PIVOT_MEDIAN, "Median Point", ""}, + {V3D_AROUND_LOCAL_ORIGINS, + "INDIVIDUAL_ORIGINS", + ICON_PIVOT_INDIVIDUAL, + "Individual Origins", + "Pivot around each selected island's own median point"}, + {0, NULL, 0, NULL, NULL}, + + }; srna = RNA_def_struct(brna, "SequencerToolSettings", NULL); RNA_def_struct_path_func(srna, "rna_SequencerToolSettings_path"); RNA_def_struct_ui_text(srna, "Sequencer Tool Settings", ""); @@ -3568,6 +3578,10 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "overlap_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, scale_overlap_modes); RNA_def_property_ui_text(prop, "Overlap Mode", "How to resolve overlap after transformation"); + + prop = RNA_def_property(srna, "pivot_point", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, pivot_points); + RNA_def_property_ui_text(prop, "Pivot Point", "Rotation or scaling pivot point"); } static void rna_def_unified_paint_settings(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index cd87e4d10c1..b713ffb68b4 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1442,6 +1442,12 @@ static void rna_def_strip_transform(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Rotation", "Rotate around image center"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update"); + prop = RNA_def_property(srna, "origin", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "origin"); + RNA_def_property_ui_text(prop, "Origin", "Origin of image for transformation"); + RNA_def_property_ui_range(prop, 0, 1, 1, 3); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update"); + RNA_def_struct_path_func(srna, "rna_SequenceTransform_path"); } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 8c331bd1911..70fe2dbc75a 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5371,6 +5371,11 @@ static void rna_def_space_sequencer_preview_overlay(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_GPENCIL); RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_image_outline", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_OUTLINE_SELECTED); + RNA_def_property_ui_text(prop, "Image Outline", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna) diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index 4f7d603fd6a..d2a47a13db3 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -94,11 +94,15 @@ SeqCollection *SEQ_query_by_reference(struct Sequence *seq_reference, SeqCollection *collection)); SeqCollection *SEQ_query_selected_strips(struct ListBase *seqbase); SeqCollection *SEQ_query_unselected_strips(struct ListBase *seqbase); -SeqCollection *SEQ_query_all_strips(struct ListBase *seqbase); -SeqCollection *SEQ_query_all_strips_recursive(struct ListBase *seqbase); +SeqCollection *SEQ_query_all_strips(ListBase *seqbase); +SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase); +SeqCollection *SEQ_query_rendered_strips(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel); void SEQ_query_strip_effect_chain(struct Sequence *seq_reference, struct ListBase *seqbase, SeqCollection *collection); +void SEQ_filter_selected_strips(SeqCollection *collection); #ifdef __cplusplus } diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index d7800d208a4..7e733817630 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -64,6 +64,7 @@ short SEQ_tool_settings_snap_flag_get(struct Scene *scene); short SEQ_tool_settings_snap_mode_get(struct Scene *scene); int SEQ_tool_settings_snap_distance_get(struct Scene *scene); eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(struct Scene *scene); +int SEQ_tool_settings_pivot_point_get(struct Scene *scene); struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings); struct Editing *SEQ_editing_get(const struct Scene *scene); struct Editing *SEQ_editing_ensure(struct Scene *scene); diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index 1977835f627..328efb9424a 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -61,6 +61,15 @@ void SEQ_transform_offset_after_frame(struct Scene *scene, const int delta, const int timeline_frame); +/* Image transformation. */ +void SEQ_image_transform_mirror_factor_get(const struct Sequence *seq, float r_mirror[2]); +void SEQ_image_transform_origin_offset_pixelspace_get(const struct Scene *scene, + const struct Sequence *seq, + float r_origin[2]); +void SEQ_image_transform_final_quad_get(const struct Scene *scene, + const struct Sequence *seq, + float r_quad[4][2]); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index 58f68205f51..2429405350b 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -37,6 +37,8 @@ #include "BKE_scene.h" #include "SEQ_iterator.h" +#include "SEQ_time.h" +#include "render.h" /* -------------------------------------------------------------------- */ /** \Iterator API @@ -340,6 +342,114 @@ SeqCollection *SEQ_query_selected_strips(ListBase *seqbase) return collection; } +static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame) +{ + SeqCollection *collection = SEQ_collection_create(__func__); + + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) { + SEQ_collection_append_strip(seq, collection); + } + } + return collection; +} + +static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + if (seq->machine <= channel) { + continue; + } + SEQ_collection_remove_strip(seq, collection); + } +} + +static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input) +{ + if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input || + seq_effect->seq3 == possibly_input) { + return true; + } + return false; +} + +/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself. + * Order of applying these conditions is important. */ +static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_timeline_frame) +{ + bool seq_have_effect_in_stack = false; + Sequence *seq_iter; + SEQ_ITERATOR_FOREACH (seq_iter, strips_at_timeline_frame) { + /* Strips is below another strip with replace blending are not rendered. */ + if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) { + return false; + } + + if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) { + /* Strips in same channel or higher than its effect are rendered. */ + if (seq->machine >= seq_iter->machine) { + return true; + } + /* Mark that this strip has effect in stack, that is above the strip. */ + seq_have_effect_in_stack = true; + } + } + + /* All effects are rendered (with respect to conditions above). */ + if ((seq->type & SEQ_TYPE_EFFECT) != 0) { + return true; + } + + /* If strip has effects in stack, and all effects are above this strip, it is not rendered. */ + if (seq_have_effect_in_stack) { + return false; + } + + return true; +} + +/* Remove strips we don't want to render from collection. */ +static void collection_filter_rendered_strips(SeqCollection *collection) +{ + Sequence *seq; + + /* Remove sound strips and muted strips from collection, because these are not rendered. + * Function #must_render_strip() don't have to check for these strips anymore. */ + SEQ_ITERATOR_FOREACH (seq, collection) { + if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) { + SEQ_collection_remove_strip(seq, collection); + } + } + + SEQ_ITERATOR_FOREACH (seq, collection) { + if (must_render_strip(seq, collection)) { + continue; + } + SEQ_collection_remove_strip(seq, collection); + } +} + +/** + * Query strips that are rendered at \a timeline_frame when \a displayed channel is viewed + * + * \param seqbase: ListBase in which strips are queried + * \param timeline_frame: viewed frame + * \param displayed_channel: viewed channel. when set to 0, no channel filter is applied + * \return strip collection + */ +SeqCollection *SEQ_query_rendered_strips(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel) +{ + SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame); + if (displayed_channel != 0) { + collection_filter_channel_up_to_incl(collection, displayed_channel); + } + collection_filter_rendered_strips(collection); + return collection; +} + /** * Query all unselected strips in seqbase. * @@ -396,3 +506,13 @@ void SEQ_query_strip_effect_chain(Sequence *seq_reference, } } } + +void SEQ_filter_selected_strips(SeqCollection *collection) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + if ((seq->flag & SELECT) == 0) { + SEQ_collection_remove_strip(seq, collection); + } + } +} diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 6c4502a3608..e42a3f4d6e9 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -72,6 +72,7 @@ #include "SEQ_render.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" +#include "SEQ_transform.h" #include "SEQ_utils.h" #include "effects.h" @@ -262,94 +263,6 @@ StripElem *SEQ_render_give_stripelem(Sequence *seq, int timeline_frame) return se; } -static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input) -{ - if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input || - seq_effect->seq3 == possibly_input) { - return true; - } - return false; -} - -/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself. - * Order of applying these conditions is important. */ -static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_timeline_frame) -{ - bool seq_have_effect_in_stack = false; - Sequence *seq_iter; - SEQ_ITERATOR_FOREACH (seq_iter, strips_at_timeline_frame) { - /* Strips is below another strip with replace blending are not rendered. */ - if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) { - return false; - } - - if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) { - /* Strips in same channel or higher than its effect are rendered. */ - if (seq->machine >= seq_iter->machine) { - return true; - } - /* Mark that this strip has effect in stack, that is above the strip. */ - seq_have_effect_in_stack = true; - } - } - - /* All effects are rendered (with respect to conditions above). */ - if ((seq->type & SEQ_TYPE_EFFECT) != 0) { - return true; - } - - /* If strip has effects in stack, and all effects are above this strip, it is not rendered. */ - if (seq_have_effect_in_stack) { - return false; - } - - return true; -} - -static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame) -{ - SeqCollection *collection = SEQ_collection_create(__func__); - - LISTBASE_FOREACH (Sequence *, seq, seqbase) { - if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) { - SEQ_collection_append_strip(seq, collection); - } - } - return collection; -} - -static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel) -{ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, collection) { - if (seq->machine <= channel) { - continue; - } - SEQ_collection_remove_strip(seq, collection); - } -} - -/* Remove strips we don't want to render from collection. */ -static void collection_filter_rendered_strips(SeqCollection *collection) -{ - Sequence *seq; - - /* Remove sound strips and muted strips from collection, because these are not rendered. - * Function #must_render_strip() don't have to check for these strips anymore. */ - SEQ_ITERATOR_FOREACH (seq, collection) { - if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) { - SEQ_collection_remove_strip(seq, collection); - } - } - - SEQ_ITERATOR_FOREACH (seq, collection) { - if (must_render_strip(seq, collection)) { - continue; - } - SEQ_collection_remove_strip(seq, collection); - } -} - static int seq_channel_cmp_fn(const void *a, const void *b) { return (*(Sequence **)a)->machine - (*(Sequence **)b)->machine; @@ -360,13 +273,7 @@ int seq_get_shown_sequences(ListBase *seqbase, const int chanshown, Sequence **r_seq_arr) { - SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame); - - if (chanshown != 0) { - collection_filter_channel_up_to_incl(collection, chanshown); - } - collection_filter_rendered_strips(collection); - + SeqCollection *collection = SEQ_query_rendered_strips(seqbase, timeline_frame, chanshown); const int strip_count = BLI_gset_len(collection->set); if (strip_count > MAXSEQ) { @@ -504,7 +411,7 @@ static void sequencer_image_crop_transform_matrix(const Sequence *seq, const float image_center_offs_y = (out->y - in->y) / 2; const float translate_x = transform->xofs * preview_scale_factor + image_center_offs_x; const float translate_y = transform->yofs * preview_scale_factor + image_center_offs_y; - const float pivot[2] = {in->x / 2, in->y / 2}; + const float pivot[2] = {in->x * transform->origin[0], in->y * transform->origin[1]}; loc_rot_size_to_mat3(r_transform_matrix, (const float[]){translate_x, translate_y}, transform->rotation, diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index bf5942090c9..d3f8411cf0a 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -79,6 +79,8 @@ static Strip *seq_strip_alloc(int type) strip->transform = MEM_callocN(sizeof(struct StripTransform), "StripTransform"); strip->transform->scale_x = 1; strip->transform->scale_y = 1; + strip->transform->origin[0] = 0.5f; + strip->transform->origin[1] = 0.5f; strip->crop = MEM_callocN(sizeof(struct StripCrop), "StripCrop"); } @@ -321,6 +323,7 @@ SequencerToolSettings *SEQ_tool_settings_init(void) SEQ_SNAP_TO_STRIP_HOLD; tool_settings->snap_distance = 15; tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE; + tool_settings->pivot_point = V3D_AROUND_LOCAL_ORIGINS; return tool_settings; } @@ -377,6 +380,12 @@ eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(Scene *scene) return tool_settings->overlap_mode; } +int SEQ_tool_settings_pivot_point_get(Scene *scene) +{ + const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); + return tool_settings->pivot_point; +} + /** * Get seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase * diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 3a5f93a72b0..d5ff455c694 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -421,3 +421,101 @@ void SEQ_transform_offset_after_frame(Scene *scene, } } } + +void SEQ_image_transform_mirror_factor_get(const Sequence *seq, float r_mirror[2]) +{ + r_mirror[0] = 1.0f; + r_mirror[1] = 1.0f; + + if ((seq->flag & SEQ_FLIPX) != 0) { + r_mirror[0] = -1.0f; + } + if ((seq->flag & SEQ_FLIPY) != 0) { + r_mirror[1] = -1.0f; + } +} + +/** + * Get strip transform origin offset from image center + * Note: This function does not apply axis mirror. + * + * \param scene: Scene in which strips are located + * \param seq: Sequence to calculate image transform origin + * \param r_origin: return value + */ +void SEQ_image_transform_origin_offset_pixelspace_get(const Scene *scene, + const Sequence *seq, + float r_origin[2]) +{ + float image_size[2]; + StripElem *strip_elem = seq->strip->stripdata; + if (strip_elem == NULL) { + image_size[0] = scene->r.xsch; + image_size[1] = scene->r.ysch; + } + else { + image_size[0] = strip_elem->orig_width; + image_size[1] = strip_elem->orig_height; + } + + const StripTransform *transform = seq->strip->transform; + r_origin[0] = (image_size[0] * transform->origin[0]) - (image_size[0] * 0.5f) + transform->xofs; + r_origin[1] = (image_size[1] * transform->origin[1]) - (image_size[1] * 0.5f) + transform->yofs; + + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + mul_v2_v2(r_origin, mirror); +} + +/** + * Get strip transform origin offset from image center + * + * \param scene: Scene in which strips are located + * \param seq: Sequence to calculate image transform origin + * \param r_origin: return value + */ + +void SEQ_image_transform_final_quad_get(const Scene *scene, + const Sequence *seq, + float r_quad[4][2]) +{ + StripTransform *transform = seq->strip->transform; + StripCrop *crop = seq->strip->crop; + + int imgage_size[2] = {scene->r.xsch, scene->r.ysch}; + if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) { + imgage_size[0] = seq->strip->stripdata->orig_width; + imgage_size[1] = seq->strip->stripdata->orig_height; + } + + float transform_matrix[3][3]; + loc_rot_size_to_mat3(transform_matrix, + (const float[]){transform->xofs, transform->yofs}, + transform->rotation, + (const float[]){transform->scale_x, transform->scale_y}); + const float origin[2] = {imgage_size[0] * transform->origin[0], + imgage_size[1] * transform->origin[1]}; + const float pivot[2] = {origin[0] - (imgage_size[0] / 2), origin[1] - (imgage_size[1] / 2)}; + transform_pivot_set_m3(transform_matrix, pivot); + + r_quad[0][0] = (imgage_size[0] / 2) - crop->right; + r_quad[0][1] = (imgage_size[1] / 2) - crop->top; + r_quad[1][0] = (imgage_size[0] / 2) - crop->right; + r_quad[1][1] = (-imgage_size[1] / 2) + crop->bottom; + r_quad[2][0] = (-imgage_size[0] / 2) + crop->left; + r_quad[2][1] = (-imgage_size[1] / 2) + crop->bottom; + r_quad[3][0] = (-imgage_size[0] / 2) + crop->left; + r_quad[3][1] = (imgage_size[1] / 2) - crop->top; + + mul_m3_v2(transform_matrix, r_quad[0]); + mul_m3_v2(transform_matrix, r_quad[1]); + mul_m3_v2(transform_matrix, r_quad[2]); + mul_m3_v2(transform_matrix, r_quad[3]); + + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + mul_v2_v2(r_quad[0], mirror); + mul_v2_v2(r_quad[1], mirror); + mul_v2_v2(r_quad[2], mirror); + mul_v2_v2(r_quad[3], mirror); +} From 997b5fe45dab8bd0e2976c8b673e56266134fc80 Mon Sep 17 00:00:00 2001 From: Aditya Y Jeppu Date: Tue, 21 Sep 2021 10:38:15 +0200 Subject: [PATCH 0049/1500] VSE strip thumbnails Draw thumbnails as strip overlay. This works for movie and image strips. To draw thumbnails, this overlay has to be enabled and strips must be tall enough. The thumbnails are loaded from source file using separate thread and stored in cache. Drawing code uses only images stored in cache, and if any is missing, background rendering job is started. If job can not render thumbnail, to prevent endless loop of creating job for missing image it sets `SEQ_FLAG_SKIP_THUMBNAILS` bit of `Sequence` flag. To prevent visual glitches during timeline panning and zooming, `View2D` flag `V2D_IS_NAVIGATING` is implemented. If bit is set, drawing code will look for set of evenly distributed thumbnails that should be guaranteed to exist and also set of previously displayed thumbnails. Due to volatile nature of cache these thumbnails can be missing anyway, in which case no new thumbnails will be drawn for particular strip. Cache capacity is limited to 5000 thumbnails and performs cleanup of non visible images when limit is reached. ref T89143 Reviewed By: ISS Differential Revision: https://developer.blender.org/D12266 --- .../scripts/startup/bl_ui/space_sequencer.py | 1 + source/blender/blenkernel/intern/screen.c | 2 + .../blenloader/intern/versioning_280.c | 2 +- .../blenloader/intern/versioning_300.c | 16 + .../editors/interface/interface_templates.c | 5 + source/blender/editors/interface/view2d_ops.c | 18 +- .../editors/space_sequencer/sequencer_draw.c | 522 ++++++++++++++++++ .../editors/space_sequencer/sequencer_edit.c | 6 +- .../space_sequencer/sequencer_intern.h | 1 + .../editors/space_sequencer/space_sequencer.c | 14 +- source/blender/makesdna/DNA_sequence_types.h | 3 +- source/blender/makesdna/DNA_space_types.h | 9 + source/blender/makesdna/DNA_view2d_types.h | 2 + source/blender/makesrna/intern/rna_space.c | 5 + source/blender/sequencer/SEQ_render.h | 21 + source/blender/sequencer/SEQ_utils.h | 2 +- source/blender/sequencer/intern/image_cache.c | 81 ++- source/blender/sequencer/intern/image_cache.h | 6 + source/blender/sequencer/intern/render.c | 184 +++++- source/blender/sequencer/intern/sequencer.c | 2 + source/blender/sequencer/intern/utils.c | 1 + source/blender/windowmanager/WM_api.h | 1 + source/blender/windowmanager/intern/wm_jobs.c | 2 +- 23 files changed, 893 insertions(+), 13 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 806b6100cc8..543164f25fc 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -247,6 +247,7 @@ class SEQUENCER_PT_sequencer_overlay(Panel): layout.prop(overlay_settings, "show_strip_offset", text="Offsets") layout.prop(overlay_settings, "show_fcurves", text="F-Curves") + layout.prop(overlay_settings, "show_thumbnails", text="Thumbnails") layout.prop(overlay_settings, "show_grid", text="Grid") layout.separator() diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 73e25a22225..4c38536b662 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -1679,6 +1679,8 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) sseq->scopes.sep_waveform_ibuf = NULL; sseq->scopes.vector_ibuf = NULL; sseq->scopes.histogram_ibuf = NULL; + memset(&sseq->runtime, 0x0, sizeof(sseq->runtime)); + } else if (sl->spacetype == SPACE_PROPERTIES) { SpaceProperties *sbuts = (SpaceProperties *)sl; diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index f667361d166..9f2c090c242 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -1774,7 +1774,7 @@ static void do_versions_seq_set_cache_defaults(Editing *ed) static bool seq_update_flags_cb(Sequence *seq, void *UNUSED(user_data)) { - seq->flag &= ~(SEQ_FLAG_UNUSED_6 | SEQ_FLAG_UNUSED_18 | SEQ_FLAG_UNUSED_19 | SEQ_FLAG_UNUSED_21); + seq->flag &= ~((1 << 6) | (1 << 18) | (1 << 19) | (1 << 21)); if (seq->type == SEQ_TYPE_SPEED) { SpeedControlVars *s = (SpeedControlVars *)seq->effectdata; s->flags &= ~(SEQ_SPEED_UNUSED_1); diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index d0fa6c282f4..1a19bbbee5c 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1318,6 +1318,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + LISTBASE_FOREACH (ARegion *, region, regionbase) { + if (region->regiontype == RGN_TYPE_WINDOW) { + region->v2d.min[1] = 4.0f; + } + } + } + } + } + } } /** diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 0c9eb20af19..320371ad9ea 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -5823,6 +5823,11 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C) icon = ICON_SEQUENCE; break; } + if (WM_jobs_test(wm, scene, WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL)) { + handle_event = B_STOPSEQ; + icon = ICON_SEQUENCE; + break; + } if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_BUILD_PROXY)) { handle_event = B_STOPCLIP; icon = ICON_TRACKER; diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 1fd1b6c984d..8fab34d486c 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -147,6 +147,8 @@ static void view_pan_init(bContext *C, wmOperator *op) const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1); vpd->facx = (BLI_rctf_size_x(&vpd->v2d->cur)) / winx; vpd->facy = (BLI_rctf_size_y(&vpd->v2d->cur)) / winy; + + vpd->v2d->flag |= V2D_IS_NAVIGATING; } /* apply transform to view (i.e. adjust 'cur' rect) */ @@ -190,6 +192,8 @@ static void view_pan_apply(bContext *C, wmOperator *op) /* Cleanup temp custom-data. */ static void view_pan_exit(wmOperator *op) { + v2dViewPanData *vpd = op->customdata; + vpd->v2d->flag &= ~V2D_IS_NAVIGATING; MEM_SAFE_FREE(op->customdata); } @@ -305,7 +309,7 @@ static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -static void view_pan_cancel(bContext *UNUSED(C), wmOperator *op) +static void view_pan_cancel(bContext *C, wmOperator *op) { view_pan_exit(op); } @@ -358,6 +362,7 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event View2DEdgePanData *vpd = op->customdata; if (event->val == KM_RELEASE || event->type == EVT_ESCKEY) { + vpd->v2d->flag &= ~V2D_IS_NAVIGATING; MEM_SAFE_FREE(op->customdata); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); } @@ -371,6 +376,8 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event static void view_edge_pan_cancel(bContext *UNUSED(C), wmOperator *op) { + v2dViewPanData *vpd = op->customdata; + vpd->v2d->flag &= ~V2D_IS_NAVIGATING; MEM_SAFE_FREE(op->customdata); } @@ -680,6 +687,8 @@ static void view_zoomdrag_init(bContext *C, wmOperator *op) vzd->v2d = &vzd->region->v2d; /* False by default. Interactive callbacks (ie invoke()) can set it to true. */ vzd->zoom_to_mouse_pos = false; + + vzd->v2d->flag |= V2D_IS_NAVIGATING; } /* apply transform to view (i.e. adjust 'cur' rect) */ @@ -809,7 +818,8 @@ static void view_zoomstep_apply(bContext *C, wmOperator *op) static void view_zoomstep_exit(wmOperator *op) { UI_view2d_zoom_cache_reset(); - + v2dViewZoomData *vzd = op->customdata; + vzd->v2d->flag &= ~V2D_IS_NAVIGATING; MEM_SAFE_FREE(op->customdata); } @@ -1041,6 +1051,7 @@ static void view_zoomdrag_exit(bContext *C, wmOperator *op) if (op->customdata) { v2dViewZoomData *vzd = op->customdata; + vzd->v2d->flag &= ~V2D_IS_NAVIGATING; if (vzd->timer) { WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), vzd->timer); @@ -1911,6 +1922,8 @@ static void scroller_activate_init(bContext *C, vsm->scrollbar_orig = ((scrollers.vert_max + scrollers.vert_min) / 2) + region->winrct.ymin; } + vsm->v2d->flag |= V2D_IS_NAVIGATING; + ED_region_tag_redraw_no_rebuild(region); } @@ -1921,6 +1934,7 @@ static void scroller_activate_exit(bContext *C, wmOperator *op) v2dScrollerMove *vsm = op->customdata; vsm->v2d->scroll_ui &= ~(V2D_SCROLL_H_ACTIVE | V2D_SCROLL_V_ACTIVE); + vsm->v2d->flag &= ~V2D_IS_NAVIGATING; MEM_freeN(op->customdata); op->customdata = NULL; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index e063e12a9a8..e9e29ae3677 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -25,6 +25,7 @@ #include #include "BLI_blenlib.h" +#include "BLI_ghash.h" #include "BLI_math.h" #include "BLI_string_utils.h" #include "BLI_threads.h" @@ -44,6 +45,7 @@ #include "BKE_context.h" #include "BKE_fcurve.h" #include "BKE_global.h" +#include "BKE_main.h" #include "BKE_scene.h" #include "BKE_sound.h" @@ -1283,6 +1285,520 @@ static void draw_seq_fcurve_overlay( } } +typedef struct ThumbnailDrawJob { + SeqRenderData context; + GHash *sequences_ghash; + Scene *scene; + rctf *view_area; + float pixelx; + float pixely; +} ThumbnailDrawJob; + +typedef struct ThumbDataItem { + Sequence *seq_dupli; + Scene *scene; +} ThumbDataItem; + +static void thumbnail_hash_data_free(void *val) +{ + ThumbDataItem *item = val; + SEQ_sequence_free(item->scene, item->seq_dupli, 0); + MEM_freeN(val); +} + +static void thumbnail_freejob(void *data) +{ + ThumbnailDrawJob *tj = data; + BLI_ghash_free(tj->sequences_ghash, NULL, thumbnail_hash_data_free); + MEM_freeN(tj->view_area); + MEM_freeN(tj); +} + +static void thumbnail_endjob(void *data) +{ + ThumbnailDrawJob *tj = data; + WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, tj->scene); +} + +static bool check_seq_need_thumbnails(Sequence *seq, rctf *view_area) +{ + if (seq->type != SEQ_TYPE_MOVIE && seq->type != SEQ_TYPE_IMAGE) { + return false; + } + if (min_ii(seq->startdisp, seq->start) > view_area->xmax) { + return false; + } + else if (max_ii(seq->enddisp, seq->start + seq->len) < view_area->xmin) { + return false; + } + else if (seq->machine + 1.0f < view_area->ymin) { + return false; + } + else if (seq->machine > view_area->ymax) { + return false; + } + + return true; +} + +static void seq_get_thumb_image_dimensions(Sequence *seq, + float pixelx, + float pixely, + float *r_thumb_width, + float *r_thumb_height, + float *r_image_width, + float *r_image_height) +{ + float image_width = seq->strip->stripdata->orig_width; + float image_height = seq->strip->stripdata->orig_height; + + /* Fix the dimensions to be max SEQ_RENDER_THUMB_SIZE (256) for x or y. */ + float aspect_ratio = (float)image_width / image_height; + if (image_width > image_height) { + image_width = SEQ_RENDER_THUMB_SIZE; + image_height = round_fl_to_int(image_width / aspect_ratio); + } + else { + image_height = SEQ_RENDER_THUMB_SIZE; + image_width = round_fl_to_int(image_height * aspect_ratio); + } + + /* Calculate thumb dimensions. */ + float thumb_height = (SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM) - (20 * U.dpi_fac * pixely); + aspect_ratio = ((float)image_width) / image_height; + float thumb_h_px = thumb_height / pixely; + float thumb_width = aspect_ratio * thumb_h_px * pixelx; + + if (r_thumb_height == NULL) { + *r_thumb_width = thumb_width; + return; + } + + *r_thumb_height = thumb_height; + *r_image_width = image_width; + *r_image_height = image_height; + *r_thumb_width = thumb_width; +} + +static float seq_thumbnail_get_start_frame(Sequence *seq, float frame_step, rctf *view_area) +{ + if (seq->start > view_area->xmin && seq->start < view_area->xmax) { + return seq->start; + } + + /* Drawing and caching both check to see if strip is in view area or not before calling this + * function so assuming strip/part of strip in view. */ + + int no_invisible_thumbs = (view_area->xmin - seq->start) / frame_step; + return ((no_invisible_thumbs - 1) * frame_step) + seq->start; +} + +static void thumbnail_start_job(void *data, short *stop, short *do_update, float *progress) +{ + ThumbnailDrawJob *tj = data; + float start_frame, frame_step; + + GHashIterator gh_iter; + BLI_ghashIterator_init(&gh_iter, tj->sequences_ghash); + while (!BLI_ghashIterator_done(&gh_iter) & !*stop) { + Sequence *seq_orig = BLI_ghashIterator_getKey(&gh_iter); + ThumbDataItem *val = BLI_ghash_lookup(tj->sequences_ghash, seq_orig); + + if (check_seq_need_thumbnails(seq_orig, tj->view_area)) { + seq_get_thumb_image_dimensions( + val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, NULL, NULL, NULL); + start_frame = seq_thumbnail_get_start_frame(seq_orig, frame_step, tj->view_area); + SEQ_render_thumbnails( + &tj->context, val->seq_dupli, seq_orig, start_frame, frame_step, tj->view_area, stop); + SEQ_render_thumbnails_base_set(&tj->context, val->seq_dupli, seq_orig, tj->view_area, stop); + } + BLI_ghashIterator_step(&gh_iter); + } + UNUSED_VARS(do_update, progress); +} + +static SeqRenderData sequencer_thumbnail_context_init(const bContext *C) +{ + struct Main *bmain = CTX_data_main(C); + struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Scene *scene = CTX_data_scene(C); + SpaceSeq *sseq = CTX_wm_space_seq(C); + SeqRenderData context = {0}; + + /* Taking rectx and recty as 0 as dimensions not known here, and context is used to calculate + * hash key but not necessary as other variables of SeqRenderData are unique enough. */ + SEQ_render_new_render_data(bmain, depsgraph, scene, 0, 0, sseq->render_size, false, &context); + context.view_id = BKE_scene_multiview_view_id_get(&scene->r, STEREO_LEFT_NAME); + context.use_proxies = false; + + return context; +} + +static GHash *sequencer_thumbnail_ghash_init(const bContext *C, View2D *v2d, Editing *ed) +{ + Scene *scene = CTX_data_scene(C); + + /* Set the data for thumbnail caching job. */ + GHash *thumb_data_hash = BLI_ghash_ptr_new("seq_duplicates_and_origs"); + + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { + ThumbDataItem *val_need_update = BLI_ghash_lookup(thumb_data_hash, seq); + if (val_need_update == NULL && check_seq_need_thumbnails(seq, &v2d->cur)) { + ThumbDataItem *val = MEM_callocN(sizeof(ThumbDataItem), "Thumbnail Hash Values"); + val->seq_dupli = SEQ_sequence_dupli_recursive(scene, scene, NULL, seq, 0); + val->scene = scene; + BLI_ghash_insert(thumb_data_hash, seq, val); + } + else { + if (val_need_update != NULL) { + val_need_update->seq_dupli->start = seq->start; + val_need_update->seq_dupli->startdisp = seq->startdisp; + } + } + } + + return thumb_data_hash; +} + +static void sequencer_thumbnail_init_job(const bContext *C, View2D *v2d, Editing *ed) +{ + wmJob *wm_job; + ThumbnailDrawJob *tj = NULL; + ScrArea *area = CTX_wm_area(C); + wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + CTX_data_scene(C), + "Draw Thumbnails", + 0, + WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL); + + /* Get the thumbnail job if it exists. */ + tj = WM_jobs_customdata_get(wm_job); + if (!tj) { + tj = MEM_callocN(sizeof(ThumbnailDrawJob), "Thumbnail cache job"); + + /* Duplicate value of v2d->cur and v2d->tot to have module separation. */ + rctf *view_area = MEM_callocN(sizeof(struct rctf), "viewport area"); + view_area->xmax = v2d->cur.xmax; + view_area->xmin = v2d->cur.xmin; + view_area->ymax = v2d->cur.ymax; + view_area->ymin = v2d->cur.ymin; + + tj->scene = CTX_data_scene(C); + tj->view_area = view_area; + tj->context = sequencer_thumbnail_context_init(C); + tj->sequences_ghash = sequencer_thumbnail_ghash_init(C, v2d, ed); + tj->pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask); + tj->pixely = BLI_rctf_size_y(&v2d->cur) / BLI_rcti_size_y(&v2d->mask); + WM_jobs_customdata_set(wm_job, tj, thumbnail_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_SEQUENCER, NC_SCENE | ND_SEQUENCER); + WM_jobs_callbacks(wm_job, thumbnail_start_job, NULL, NULL, thumbnail_endjob); + } + + if (!WM_jobs_is_running(wm_job)) { + G.is_break = false; + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL); + } + + ED_area_tag_redraw(area); +} + +static bool sequencer_thumbnail_v2d_is_navigating(const bContext *C) +{ + ARegion *region = CTX_wm_region(C); + View2D *v2d = ®ion->v2d; + return (v2d->flag & V2D_IS_NAVIGATING) != 0; +} + +static void sequencer_thumbnail_start_job_if_necessary(const bContext *C, + Editing *ed, + View2D *v2d, + bool thumbnail_is_missing) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + + if (sequencer_thumbnail_v2d_is_navigating(C)) { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL); + return; + } + + /* `thumbnail_is_missing` should be set to true if missing image in strip. False when normal call + * to all strips done. */ + if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || + v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax || thumbnail_is_missing) { + + /* Stop the job first as view has changed. Pointless to continue old job. */ + if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || + v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax) { + WM_jobs_stop(CTX_wm_manager(C), NULL, thumbnail_start_job); + } + + sequencer_thumbnail_init_job(C, v2d, ed); + sseq->runtime.last_thumbnail_area = v2d->cur; + } +} + +void last_displayed_thumbnails_list_free(void *val) +{ + BLI_gset_free(val, NULL); +} + +static GSet *last_displayed_thumbnails_list_ensure(const bContext *C, Sequence *seq) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + if (sseq->runtime.last_displayed_thumbnails == NULL) { + sseq->runtime.last_displayed_thumbnails = BLI_ghash_ptr_new(__func__); + } + + GSet *displayed_thumbnails = BLI_ghash_lookup(sseq->runtime.last_displayed_thumbnails, seq); + if (displayed_thumbnails == NULL) { + displayed_thumbnails = BLI_gset_int_new(__func__); + BLI_ghash_insert(sseq->runtime.last_displayed_thumbnails, seq, displayed_thumbnails); + } + + return displayed_thumbnails; +} + +static void last_displayed_thumbnails_list_cleanup(GSet *previously_displayed, + float range_start, + float range_end) +{ + GSetIterator gset_iter; + BLI_gsetIterator_init(&gset_iter, previously_displayed); + while (!BLI_gsetIterator_done(&gset_iter)) { + int frame = (float)POINTER_AS_INT(BLI_gsetIterator_getKey(&gset_iter)); + BLI_gsetIterator_step(&gset_iter); + + if (frame > range_start && frame < range_end) { + BLI_gset_remove(previously_displayed, POINTER_FROM_INT(frame), NULL); + } + } +} + +static int sequencer_thumbnail_closest_previous_frame_get(int timeline_frame, + GSet *previously_displayed) +{ + int best_diff = INT_MAX; + int best_frame = timeline_frame; + + /* Previously displayed thumbnails. */ + GSetIterator gset_iter; + BLI_gsetIterator_init(&gset_iter, previously_displayed); + while (!BLI_gsetIterator_done(&gset_iter)) { + int frame = POINTER_AS_INT(BLI_gsetIterator_getKey(&gset_iter)); + int diff = abs(frame - timeline_frame); + if (diff < best_diff) { + best_diff = diff; + best_frame = frame; + } + BLI_gsetIterator_step(&gset_iter); + } + return best_frame; +} + +static int sequencer_thumbnail_closest_guaranteed_frame_get(Sequence *seq, int timeline_frame) +{ + if (timeline_frame <= seq->startdisp) { + return seq->startdisp; + } + + /* Set of "guaranteed" thumbnails. */ + const int frame_index = timeline_frame - seq->startdisp; + const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(seq); + const int relative_base_frame = round_fl_to_int((frame_index / (float)frame_step)) * frame_step; + const int nearest_guaranted_absolute_frame = relative_base_frame + seq->startdisp; + return nearest_guaranted_absolute_frame; +} + +static ImBuf *sequencer_thumbnail_closest_from_memory(const SeqRenderData *context, + Sequence *seq, + int timeline_frame, + GSet *previously_displayed, + rcti *crop, + bool clipped) +{ + int frame_previous = sequencer_thumbnail_closest_previous_frame_get(timeline_frame, + previously_displayed); + ImBuf *ibuf_previous = SEQ_get_thumbnail(context, seq, frame_previous, crop, clipped); + + int frame_guaranteed = sequencer_thumbnail_closest_guaranteed_frame_get(seq, timeline_frame); + ImBuf *ibuf_guaranteed = SEQ_get_thumbnail(context, seq, frame_guaranteed, crop, clipped); + + if (ibuf_previous && ibuf_guaranteed && + abs(frame_previous - timeline_frame) < abs(frame_guaranteed - timeline_frame)) { + + IMB_freeImBuf(ibuf_guaranteed); + return ibuf_previous; + } + else { + IMB_freeImBuf(ibuf_previous); + return ibuf_guaranteed; + } + + if (ibuf_previous == NULL) { + return ibuf_guaranteed; + } + + if (ibuf_guaranteed == NULL) { + return ibuf_previous; + } +} + +static void draw_seq_strip_thumbnail(View2D *v2d, + const bContext *C, + Scene *scene, + Sequence *seq, + float y1, + float y2, + float pixelx, + float pixely) +{ + bool clipped = false; + float image_height, image_width, thumb_width, thumb_height; + rcti crop; + + /* If width of the strip too small ignore drawing thumbnails. */ + if ((y2 - y1) / pixely <= 40 * U.dpi_fac) { + return; + } + + SeqRenderData context = sequencer_thumbnail_context_init(C); + + if ((seq->flag & SEQ_FLAG_SKIP_THUMBNAILS) != 0) { + return; + } + + seq_get_thumb_image_dimensions( + seq, pixelx, pixely, &thumb_width, &thumb_height, &image_width, &image_height); + + float thumb_y_end = y1 + thumb_height - pixely; + + float cut_off = 0; + float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; + if (seq->type == SEQ_TYPE_IMAGE) { + upper_thumb_bound = seq->enddisp; + } + + float thumb_x_start = seq_thumbnail_get_start_frame(seq, thumb_width, &v2d->cur); + float thumb_x_end; + + while (thumb_x_start + thumb_width < v2d->cur.xmin) { + thumb_x_start += thumb_width; + } + + /* Ignore thumbs to the left of strip. */ + while (thumb_x_start + thumb_width < seq->startdisp) { + thumb_x_start += thumb_width; + } + + GSet *last_displayed_thumbnails = last_displayed_thumbnails_list_ensure(C, seq); + /* Cleanup thumbnail list outside of rendered range, which is cleaned up one by one to prevent + * flickering after zooming. */ + if (!sequencer_thumbnail_v2d_is_navigating(C)) { + last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, -FLT_MAX, thumb_x_start); + } + + /* Start drawing. */ + while (thumb_x_start < upper_thumb_bound) { + thumb_x_end = thumb_x_start + thumb_width; + clipped = false; + + /* Checks to make sure that thumbs are loaded only when in view and within the confines of the + * strip. Some may not be required but better to have conditions for safety as x1 here is + * point to start caching from and not drawing. */ + if (thumb_x_start > v2d->cur.xmax) { + break; + } + + /* Set the clipping bound to show the left handle moving over thumbs and not shift thumbs. */ + if (IN_RANGE_INCL(seq->startdisp, thumb_x_start, thumb_x_end)) { + cut_off = seq->startdisp - thumb_x_start; + clipped = true; + } + + /* Clip if full thumbnail cannot be displayed. */ + if (thumb_x_end > (upper_thumb_bound)) { + thumb_x_end = upper_thumb_bound; + clipped = true; + if (thumb_x_end - thumb_x_start < 1) { + break; + } + } + + float zoom_x = thumb_width / image_width; + float zoom_y = thumb_height / image_height; + + float cropx_min = (cut_off / pixelx) / (zoom_y / pixely); + float cropx_max = ((thumb_x_end - thumb_x_start) / pixelx) / (zoom_y / pixely); + if (cropx_max == (thumb_x_end - thumb_x_start)) { + cropx_max = cropx_max + 1; + } + BLI_rcti_init(&crop, (int)(cropx_min), (int)cropx_max, 0, (int)(image_height)-1); + + int timeline_frame = round_fl_to_int(thumb_x_start); + + /* Get the image. */ + ImBuf *ibuf = SEQ_get_thumbnail(&context, seq, timeline_frame, &crop, clipped); + + if (!ibuf) { + sequencer_thumbnail_start_job_if_necessary(C, scene->ed, v2d, true); + + ibuf = sequencer_thumbnail_closest_from_memory( + &context, seq, timeline_frame, last_displayed_thumbnails, &crop, clipped); + } + /* Store recently rendered frames, so they can be reused when zooming. */ + else if (!sequencer_thumbnail_v2d_is_navigating(C)) { + /* Clear images in frame range occupied bynew thumbnail. */ + last_displayed_thumbnails_list_cleanup( + last_displayed_thumbnails, thumb_x_start, thumb_x_end); + /* Insert new thumbnail frame to list. */ + BLI_gset_add(last_displayed_thumbnails, POINTER_FROM_INT(timeline_frame)); + } + + /* If there is no image still, abort. */ + if (!ibuf) { + break; + } + + /* Transparency on overlap. */ + if (seq->flag & SEQ_OVERLAP) { + GPU_blend(GPU_BLEND_ALPHA); + if (ibuf->rect) { + unsigned char *buf = (unsigned char *)ibuf->rect; + for (int pixel = ibuf->x * ibuf->y; pixel--; buf += 4) { + buf[3] = OVERLAP_ALPHA; + } + } + else if (ibuf->rect_float) { + float *buf = (float *)ibuf->rect_float; + for (int pixel = ibuf->x * ibuf->y; pixel--; buf += ibuf->channels) { + buf[3] = (OVERLAP_ALPHA / 255.0f); + } + } + } + + ED_draw_imbuf_ctx_clipping(C, + ibuf, + thumb_x_start + cut_off, + y1, + true, + thumb_x_start + cut_off, + y1, + thumb_x_end, + thumb_y_end, + zoom_x, + zoom_y); + IMB_freeImBuf(ibuf); + GPU_blend(GPU_BLEND_NONE); + cut_off = 0; + thumb_x_start += thumb_width; + } + last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, thumb_x_start, FLT_MAX); +} + /* Draw visible strips. Bounds check are already made. */ static void draw_seq_strip(const bContext *C, SpaceSeq *sseq, @@ -1356,6 +1872,12 @@ static void draw_seq_strip(const bContext *C, drawmeta_contents(scene, seq, x1, y1, x2, y2); } + if ((sseq->flag & SEQ_SHOW_OVERLAY) && + (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_THUMBNAILS) && + (seq->type == SEQ_TYPE_MOVIE || seq->type == SEQ_TYPE_IMAGE)) { + draw_seq_strip_thumbnail(v2d, C, scene, seq, y1, y2, pixelx, pixely); + } + if ((sseq->flag & SEQ_SHOW_OVERLAY) && (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_FCURVES)) { draw_seq_fcurve_overlay(scene, v2d, seq, x1, y1, x2, y2, pixelx); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index b95b7fa0620..9f21fc0676c 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -579,7 +579,6 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) { /* Only data types supported for now. */ - Editing *ed = SEQ_editing_get(scene); bool changed = false; /* Iterate in reverse so meta-strips are iterated after their children. */ @@ -633,7 +632,10 @@ static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) } } if (changed) { - SEQ_relations_free_imbuf(scene, &ed->seqbase, false); + for (int i = data->num_seq - 1; i >= 0; i--) { + Sequence *seq = data->seq_array[i]; + SEQ_relations_invalidate_cache_preprocessed(scene, seq); + } } return changed; } diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 767ac76efe6..5b5c381509f 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -67,6 +67,7 @@ struct ImBuf *sequencer_ibuf_get(struct Main *bmain, int timeline_frame, int frame_ofs, const char *viewname); +void last_displayed_thumbnails_list_free(void *val); /* sequencer_edit.c */ struct View2D; diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index cb5fe6f0ef3..0f23c61a2ca 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -32,6 +32,7 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -105,6 +106,9 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID | SEQ_TIMELINE_SHOW_FCURVES; + BLI_rctf_init(&sseq->runtime.last_thumbnail_area, 0.0f, 0.0f, 0.0f, 0.0f); + sseq->runtime.last_displayed_thumbnails = NULL; + /* Tool header. */ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); @@ -174,7 +178,7 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce region->v2d.cur = region->v2d.tot; region->v2d.min[0] = 10.0f; - region->v2d.min[1] = 0.5f; + region->v2d.min[1] = 4.0f; region->v2d.max[0] = MAXFRAMEF; region->v2d.max[1] = MAXSEQ; @@ -188,6 +192,8 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce region->v2d.keeptot = 0; region->v2d.align = V2D_ALIGN_NO_NEG_Y; + sseq->runtime.last_displayed_thumbnails = NULL; + return (SpaceLink *)sseq; } @@ -218,6 +224,12 @@ static void sequencer_free(SpaceLink *sl) if (scopes->histogram_ibuf) { IMB_freeImBuf(scopes->histogram_ibuf); } + + if (sseq->runtime.last_displayed_thumbnails) { + BLI_ghash_free( + sseq->runtime.last_displayed_thumbnails, NULL, last_displayed_thumbnails_list_free); + sseq->runtime.last_displayed_thumbnails = NULL; + } } /* Spacetype init callback. */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 782b4ac34aa..25330acd486 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -518,7 +518,7 @@ enum { SEQ_OVERLAP = (1 << 3), SEQ_FILTERY = (1 << 4), SEQ_MUTE = (1 << 5), - SEQ_FLAG_UNUSED_6 = (1 << 6), /* cleared */ + SEQ_FLAG_SKIP_THUMBNAILS = (1 << 6), SEQ_REVERSE_FRAMES = (1 << 7), SEQ_IPO_FRAME_LOCKED = (1 << 8), SEQ_EFFECT_NOT_LOADED = (1 << 9), @@ -724,6 +724,7 @@ enum { SEQ_CACHE_PREFETCH_ENABLE = (1 << 10), SEQ_CACHE_DISK_CACHE_ENABLE = (1 << 11), + SEQ_CACHE_STORE_THUMBNAIL = (1 << 12), }; #ifdef __cplusplus diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 84dc58345e3..e849039fa93 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -598,6 +598,7 @@ typedef struct SequencerTimelineOverlay { /* SequencerTimelineOverlay.flag */ typedef enum eSpaceSeq_SequencerTimelineOverlay_Flag { SEQ_TIMELINE_SHOW_STRIP_OFFSETS = (1 << 1), + SEQ_TIMELINE_SHOW_THUMBNAILS = (1 << 2), SEQ_TIMELINE_SHOW_FCURVES = (1 << 5), SEQ_TIMELINE_ALL_WAVEFORMS = (1 << 7), /* draw all waveforms */ SEQ_TIMELINE_NO_WAVEFORMS = (1 << 8), /* draw no waveforms */ @@ -607,6 +608,13 @@ typedef enum eSpaceSeq_SequencerTimelineOverlay_Flag { SEQ_TIMELINE_SHOW_GRID = (1 << 18), } eSpaceSeq_SequencerTimelineOverlay_Flag; +typedef struct SpaceSeqRuntime { + /** Required for Thumbnail job start condition. */ + struct rctf last_thumbnail_area; + /** Stores lists of most recently displayed thumbnails. */ + struct GHash *last_displayed_thumbnails; +} SpaceSeqRuntime; + /* Sequencer */ typedef struct SpaceSeq { SpaceLink *next, *prev; @@ -650,6 +658,7 @@ typedef struct SpaceSeq { char multiview_eye; char _pad2[7]; + SpaceSeqRuntime runtime; } SpaceSeq; /* SpaceSeq.mainb */ diff --git a/source/blender/makesdna/DNA_view2d_types.h b/source/blender/makesdna/DNA_view2d_types.h index c385ac04bd3..f8166305fd9 100644 --- a/source/blender/makesdna/DNA_view2d_types.h +++ b/source/blender/makesdna/DNA_view2d_types.h @@ -132,6 +132,8 @@ enum { V2D_PIXELOFS_X = (1 << 2), /* apply pixel offsets on y-axis when setting view matrices */ V2D_PIXELOFS_Y = (1 << 3), + /* zoom, pan or similar action is in progress */ + V2D_IS_NAVIGATING = (1 << 9), /* view settings need to be set still... */ V2D_IS_INIT = (1 << 10), }; diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 70fe2dbc75a..a05cef7a1cd 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5444,6 +5444,11 @@ static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_OFFSETS); RNA_def_property_ui_text(prop, "Show Offsets", "Display strip in/out offsets"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_thumbnails", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_THUMBNAILS); + RNA_def_property_ui_text(prop, "Show Thumbnails", "Show strip thumbnails"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } static void rna_def_space_sequencer(BlenderRNA *brna) diff --git a/source/blender/sequencer/SEQ_render.h b/source/blender/sequencer/SEQ_render.h index c138daf1318..50280db55ff 100644 --- a/source/blender/sequencer/SEQ_render.h +++ b/source/blender/sequencer/SEQ_render.h @@ -27,6 +27,8 @@ extern "C" { #endif +#define SEQ_RENDER_THUMB_SIZE 256 + struct ListBase; struct Main; struct Scene; @@ -67,6 +69,25 @@ struct ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, struct ImBuf *SEQ_render_give_ibuf_direct(const SeqRenderData *context, float timeline_frame, struct Sequence *seq); +void SEQ_render_thumbnails(const struct SeqRenderData *context, + struct Sequence *seq, + struct Sequence *seq_orig, + float start_frame, + float frame_step, + rctf *view_area, + short *stop); +struct ImBuf *SEQ_get_thumbnail(const struct SeqRenderData *context, + struct Sequence *seq, + float timeline_frame, + rcti *crop, + bool clipped); +int SEQ_render_thumbnails_guaranteed_set_frame_step_get(const struct Sequence *seq); +void SEQ_render_thumbnails_base_set(const struct SeqRenderData *context, + struct Sequence *seq, + struct Sequence *seq_orig, + rctf *view_area, + short *stop); + void SEQ_render_init_colorspace(struct Sequence *seq); void SEQ_render_new_render_data(struct Main *bmain, struct Depsgraph *depsgraph, diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h index 09de7bc67e9..d30a1b2d7ae 100644 --- a/source/blender/sequencer/SEQ_utils.h +++ b/source/blender/sequencer/SEQ_utils.h @@ -34,6 +34,7 @@ struct Mask; struct Scene; struct Sequence; struct StripElem; +struct SeqRenderData; void SEQ_sort(struct ListBase *seqbase); void SEQ_sequence_base_unique_name_recursive(struct Scene *scene, @@ -57,7 +58,6 @@ void SEQ_set_scale_to_fit(const struct Sequence *seq, const int preview_height, const eSeqImageFitMethod fit_method); void SEQ_ensure_unique_name(struct Sequence *seq, struct Scene *scene); - #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index 86bd840ce31..86c198075e9 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -104,6 +104,7 @@ #define DCACHE_IMAGES_PER_FILE 100 #define DCACHE_CURRENT_VERSION 2 #define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */ +#define THUMB_CACHE_LIMIT 5000 typedef struct DiskCacheHeaderEntry { unsigned char encoding; @@ -148,6 +149,7 @@ typedef struct SeqCache { struct BLI_mempool *items_pool; struct SeqCacheKey *last_key; SeqDiskCache *disk_cache; + int thumbnail_count; } SeqCache; typedef struct SeqCacheItem { @@ -776,7 +778,7 @@ static float seq_cache_timeline_frame_to_frame_index(Sequence *seq, float timeli /* With raw images, map timeline_frame to strip input media frame range. This means that static * images or extended frame range of movies will only generate one cache entry. No special * treatment in converting frame index to timeline_frame is needed. */ - if (type == SEQ_CACHE_STORE_RAW) { + if (type == SEQ_CACHE_STORE_RAW || type == SEQ_CACHE_STORE_THUMBNAIL) { return seq_give_frame_index(seq, timeline_frame); } @@ -875,7 +877,7 @@ static void seq_cache_put_ex(Scene *scene, SeqCacheKey *key, ImBuf *ibuf) if (BLI_ghash_reinsert(cache->hash, key, item, seq_cache_keyfree, seq_cache_valfree)) { IMB_refImBuf(ibuf); - if (!key->is_temp_cache) { + if (!key->is_temp_cache || key->type != SEQ_CACHE_STORE_THUMBNAIL) { cache->last_key = key; } } @@ -1161,6 +1163,7 @@ static void seq_cache_create(Main *bmain, Scene *scene) cache->hash = BLI_ghash_new(seq_cache_hashhash, seq_cache_hashcmp, "SeqCache hash"); cache->last_key = NULL; cache->bmain = bmain; + cache->thumbnail_count = 0; BLI_mutex_init(&cache->iterator_mutex); scene->ed->cache = cache; @@ -1217,7 +1220,7 @@ void seq_cache_free_temp_cache(Scene *scene, short id, int timeline_frame) SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); BLI_ghashIterator_step(&gh_iter); - if (key->is_temp_cache && key->task_id == id) { + if (key->is_temp_cache && key->task_id == id && key->type != SEQ_CACHE_STORE_THUMBNAIL) { /* Use frame_index here to avoid freeing raw images if they are used for multiple frames. */ float frame_index = seq_cache_timeline_frame_to_frame_index( key->seq, timeline_frame, key->type); @@ -1278,6 +1281,7 @@ void SEQ_cache_cleanup(Scene *scene) BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); } cache->last_key = NULL; + cache->thumbnail_count = 0; seq_cache_unlock(scene); } @@ -1345,6 +1349,46 @@ void seq_cache_cleanup_sequence(Scene *scene, seq_cache_unlock(scene); } +void seq_cache_thumbnail_cleanup(Scene *scene, rctf *view_area_safe) +{ + /* Add offsets to the left and right end to keep some frames in cache. */ + view_area_safe->xmax += 200; + view_area_safe->xmin -= 200; + view_area_safe->ymin -= 1; + view_area_safe->ymax += 1; + + SeqCache *cache = seq_cache_get_from_scene(scene); + if (!cache) { + return; + } + + GHashIterator gh_iter; + BLI_ghashIterator_init(&gh_iter, cache->hash); + while (!BLI_ghashIterator_done(&gh_iter)) { + SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); + BLI_ghashIterator_step(&gh_iter); + + const int frame_index = key->timeline_frame - key->seq->startdisp; + const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(key->seq); + const int relative_base_frame = round_fl_to_int((frame_index / (float)frame_step)) * + frame_step; + const int nearest_guaranted_absolute_frame = relative_base_frame + key->seq->startdisp; + + if (nearest_guaranted_absolute_frame == key->timeline_frame) { + continue; + } + + if ((key->type & SEQ_CACHE_STORE_THUMBNAIL) && + (key->timeline_frame > view_area_safe->xmax || + key->timeline_frame < view_area_safe->xmin || key->seq->machine > view_area_safe->ymax || + key->seq->machine < view_area_safe->ymin)) { + BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); + cache->thumbnail_count--; + } + } + cache->last_key = NULL; +} + struct ImBuf *seq_cache_get(const SeqRenderData *context, Sequence *seq, float timeline_frame, @@ -1436,6 +1480,37 @@ bool seq_cache_put_if_possible( return false; } +void seq_cache_thumbnail_put( + const SeqRenderData *context, Sequence *seq, float timeline_frame, ImBuf *i, rctf *view_area) +{ + Scene *scene = context->scene; + + if (!scene->ed->cache) { + seq_cache_create(context->bmain, scene); + } + + seq_cache_lock(scene); + SeqCache *cache = seq_cache_get_from_scene(scene); + SeqCacheKey *key = seq_cache_allocate_key( + cache, context, seq, timeline_frame, SEQ_CACHE_STORE_THUMBNAIL); + + /* Prevent reinserting, it breaks cache key linking. */ + if (BLI_ghash_haskey(cache->hash, key)) { + seq_cache_unlock(scene); + return; + } + + /* Limit cache to THUMB_CACHE_LIMIT (5000) images stored. */ + if (cache->thumbnail_count >= THUMB_CACHE_LIMIT) { + rctf view_area_safe = *view_area; + seq_cache_thumbnail_cleanup(scene, &view_area_safe); + } + + seq_cache_put_ex(scene, key, i); + cache->thumbnail_count++; + seq_cache_unlock(scene); +} + void seq_cache_put( const SeqRenderData *context, Sequence *seq, float timeline_frame, int type, ImBuf *i) { diff --git a/source/blender/sequencer/intern/image_cache.h b/source/blender/sequencer/intern/image_cache.h index 63c559caee9..60031311985 100644 --- a/source/blender/sequencer/intern/image_cache.h +++ b/source/blender/sequencer/intern/image_cache.h @@ -46,6 +46,11 @@ void seq_cache_put(const struct SeqRenderData *context, float timeline_frame, int type, struct ImBuf *i); +void seq_cache_thumbnail_put(const struct SeqRenderData *context, + struct Sequence *seq, + float timeline_frame, + struct ImBuf *i, + rctf *view_area); bool seq_cache_put_if_possible(const struct SeqRenderData *context, struct Sequence *seq, float timeline_frame, @@ -60,6 +65,7 @@ void seq_cache_cleanup_sequence(struct Scene *scene, struct Sequence *seq_changed, int invalidate_types, bool force_seq_changed_range); +void seq_cache_thumbnail_cleanup(Scene *scene, rctf *view_area); bool seq_cache_is_full(void); #ifdef __cplusplus diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index e42a3f4d6e9..b2642228c91 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -434,6 +434,31 @@ static void sequencer_image_crop_init(const Sequence *seq, BLI_rctf_init(r_crop, left, in->x - right, bottom, in->y - top); } +static void sequencer_thumbnail_transform(ImBuf *in, ImBuf *out) +{ + float image_scale_factor = (float)out->x / in->x; + float transform_matrix[3][3]; + + /* Set to keep same loc,scale,rot but change scale to thumb size limit. */ + const float scale_x = 1 * image_scale_factor; + const float scale_y = 1 * image_scale_factor; + const float image_center_offs_x = (out->x - in->x) / 2; + const float image_center_offs_y = (out->y - in->y) / 2; + const float pivot[2] = {in->x / 2, in->y / 2}; + loc_rot_size_to_mat3(transform_matrix, + (const float[]){image_center_offs_x, image_center_offs_y}, + 0, + (const float[]){scale_x, scale_y}); + transform_pivot_set_m3(transform_matrix, pivot); + invert_m3(transform_matrix); + + /* No crop. */ + rctf source_crop; + BLI_rctf_init(&source_crop, 0, in->x, 0, in->y); + + IMB_transform(in, out, transform_matrix, &source_crop, IMB_FILTER_NEAREST); +} + static void sequencer_preprocess_transform_crop( ImBuf *in, ImBuf *out, const SeqRenderData *context, Sequence *seq, const bool is_proxy_image) { @@ -1896,7 +1921,164 @@ ImBuf *SEQ_render_give_ibuf_direct(const SeqRenderData *context, seq_render_state_init(&state); ImBuf *ibuf = seq_render_strip(context, &state, seq, timeline_frame); - return ibuf; } + +/* Gets the direct image from source and scales to thumbnail size. */ +static ImBuf *seq_get_uncached_thumbnail(const SeqRenderData *context, + SeqRenderState *state, + Sequence *seq, + float timeline_frame) +{ + bool is_proxy_image = false; + ImBuf *ibuf = do_render_strip_uncached(context, state, seq, timeline_frame, &is_proxy_image); + + if (ibuf == NULL) { + return NULL; + } + + float aspect_ratio = (float)ibuf->x / ibuf->y; + int rectx, recty; + /* Calculate new dimensions - THUMB_SIZE (256) for x or y. */ + if (ibuf->x > ibuf->y) { + rectx = SEQ_RENDER_THUMB_SIZE; + recty = round_fl_to_int(rectx / aspect_ratio); + } + else { + recty = SEQ_RENDER_THUMB_SIZE; + rectx = round_fl_to_int(recty * aspect_ratio); + } + + /* Scale ibuf to thumbnail size. */ + ImBuf *scaled_ibuf = IMB_allocImBuf(rectx, recty, 32, ibuf->rect_float ? IB_rectfloat : IB_rect); + sequencer_thumbnail_transform(ibuf, scaled_ibuf); + seq_imbuf_assign_spaces(context->scene, scaled_ibuf); + IMB_freeImBuf(ibuf); + + return scaled_ibuf; +} + +/* Get cached thumbnails. */ +ImBuf *SEQ_get_thumbnail( + const SeqRenderData *context, Sequence *seq, float timeline_frame, rcti *crop, bool clipped) +{ + ImBuf *ibuf = seq_cache_get(context, seq, roundf(timeline_frame), SEQ_CACHE_STORE_THUMBNAIL); + + if (!clipped || ibuf == NULL) { + return ibuf; + } + + /* Do clipping. */ + ImBuf *ibuf_cropped = IMB_dupImBuf(ibuf); + if (crop->xmin < 0 || crop->ymin < 0) { + crop->xmin = 0; + crop->ymin = 0; + } + if (crop->xmax >= ibuf->x || crop->ymax >= ibuf->y) { + crop->xmax = ibuf->x - 1; + crop->ymax = ibuf->y - 1; + } + IMB_rect_crop(ibuf_cropped, crop); + IMB_freeImBuf(ibuf); + return ibuf_cropped; +} + +/* Render the series of thumbnails and store in cache. */ +void SEQ_render_thumbnails(const SeqRenderData *context, + Sequence *seq, + Sequence *seq_orig, + float start_frame, + float frame_step, + rctf *view_area, + short *stop) +{ + SeqRenderState state; + seq_render_state_init(&state); + + /* Adding the hold offset value (seq->anim_startofs) to the start frame. Position of image not + * affected, but frame loaded affected. */ + start_frame = start_frame - frame_step; + float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; + upper_thumb_bound = (upper_thumb_bound > view_area->xmax) ? view_area->xmax + frame_step : + upper_thumb_bound; + + while ((start_frame < upper_thumb_bound) & !*stop) { + ImBuf *ibuf = seq_cache_get( + context, seq_orig, round_fl_to_int(start_frame), SEQ_CACHE_STORE_THUMBNAIL); + if (ibuf) { + IMB_freeImBuf(ibuf); + start_frame += frame_step; + continue; + } + + ibuf = seq_get_uncached_thumbnail(context, &state, seq, round_fl_to_int(start_frame)); + + if (ibuf) { + seq_cache_thumbnail_put(context, seq_orig, round_fl_to_int(start_frame), ibuf, view_area); + IMB_freeImBuf(ibuf); + seq_orig->flag &= ~SEQ_FLAG_SKIP_THUMBNAILS; + } + else { + /* Can not open source file. */ + seq_orig->flag |= SEQ_FLAG_SKIP_THUMBNAILS; + return; + } + + start_frame += frame_step; + } +} + +/* Get frame step for equally spaced thumbnails. These thumbnails should always be present in + * memory, so they can be used when zooming.*/ +int SEQ_render_thumbnails_guaranteed_set_frame_step_get(const Sequence *seq) +{ + const int content_len = (seq->enddisp - seq->startdisp - seq->startstill - seq->endstill); + + /* Arbitrary, but due to performance reasons should be as low as possible. */ + const int thumbnails_base_set_count = min_ii(content_len / 100, 30); + if (thumbnails_base_set_count <= 0) { + return 0; + } + return content_len / thumbnails_base_set_count; +} + +/* Render set of evenly spaced thumbnails that are drawn when zooming. */ +void SEQ_render_thumbnails_base_set( + const SeqRenderData *context, Sequence *seq, Sequence *seq_orig, rctf *view_area, short *stop) +{ + SeqRenderState state; + seq_render_state_init(&state); + + int timeline_frame = seq->startdisp; + const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(seq); + + while (timeline_frame < seq->enddisp && !*stop) { + ImBuf *ibuf = seq_cache_get( + context, seq_orig, roundf(timeline_frame), SEQ_CACHE_STORE_THUMBNAIL); + if (ibuf) { + IMB_freeImBuf(ibuf); + + if (frame_step == 0) { + return; + } + + timeline_frame += frame_step; + continue; + } + + ibuf = seq_get_uncached_thumbnail(context, &state, seq, timeline_frame); + + if (ibuf) { + seq_cache_thumbnail_put(context, seq_orig, timeline_frame, ibuf, view_area); + IMB_freeImBuf(ibuf); + } + + if (frame_step == 0) { + return; + } + + timeline_frame += frame_step; + } +} + /** \} */ diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index d3f8411cf0a..382bd51aae1 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -957,6 +957,8 @@ static bool seq_read_lib_cb(Sequence *seq, void *user_data) BLI_listbase_clear(&seq->anims); SEQ_modifier_blend_read_lib(reader, sce, &seq->modifiers); + + seq->flag &= ~SEQ_FLAG_SKIP_THUMBNAILS; return true; } diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index 1d3e7e4a223..8421aab5217 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -42,6 +42,7 @@ #include "SEQ_edit.h" #include "SEQ_iterator.h" #include "SEQ_relations.h" +#include "SEQ_render.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 4fe1530628b..6794b1f4091 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -797,6 +797,7 @@ enum { WM_JOB_TYPE_QUADRIFLOW_REMESH, WM_JOB_TYPE_TRACE_IMAGE, WM_JOB_TYPE_LINEART, + WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL, /* add as needed, bake, seq proxy build * if having hard coded values is a problem */ }; diff --git a/source/blender/windowmanager/intern/wm_jobs.c b/source/blender/windowmanager/intern/wm_jobs.c index 6494c337c10..2604105896d 100644 --- a/source/blender/windowmanager/intern/wm_jobs.c +++ b/source/blender/windowmanager/intern/wm_jobs.c @@ -230,7 +230,7 @@ bool WM_jobs_test(const wmWindowManager *wm, const void *owner, int job_type) LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) { if (wm_job->owner == owner) { if (ELEM(job_type, WM_JOB_TYPE_ANY, wm_job->job_type)) { - if (wm_job->running || wm_job->suspended) { + if ((wm_job->flag & WM_JOB_PROGRESS) && (wm_job->running || wm_job->suspended)) { return true; } } From 69928307c5430eea1bb181348e2abafc768959f8 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 21 Sep 2021 14:18:31 +0200 Subject: [PATCH 0050/1500] Cleanup: Silence unused variable warning --- source/blender/editors/interface/view2d_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 8fab34d486c..4ef4c3dbc6d 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -309,7 +309,7 @@ static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -static void view_pan_cancel(bContext *C, wmOperator *op) +static void view_pan_cancel(bContext *UNUSED(C), wmOperator *op) { view_pan_exit(op); } From fa6b1007bad065440950cd67deb16a04f368856f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 21 Sep 2021 22:17:08 +1000 Subject: [PATCH 0051/1500] Fix script_load_keymap failure from c9d9bfa84ad5cb985e3feccffa702b2f3cc2adf8 Scanning the keymap hierarchy (used in the preferences), caused the test to fail. Don't attempt add fallback keymaps for dynamic keymap callbacks. --- release/scripts/startup/bl_ui/space_toolsystem_common.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 1c3dbded083..98e29d3baba 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -539,7 +539,9 @@ class ToolSelectPanelHelper: visited.add(km_name) yield (km_name, cls.bl_space_type, 'WINDOW', []) - yield (km_name + " (fallback)", cls.bl_space_type, 'WINDOW', []) + # Callable types don't use fall-backs. + if isinstance(km_name, str): + yield (km_name + " (fallback)", cls.bl_space_type, 'WINDOW', []) # ------------------------------------------------------------------------- # Layout Generators From 08031197250aeecbaca3803254e6f25b8c7b7b37 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 20 Sep 2021 17:59:20 +0200 Subject: [PATCH 0052/1500] Cycles: merge of cycles-x branch, a major update to the renderer This includes much improved GPU rendering performance, viewport interactivity, new shadow catcher, revamped sampling settings, subsurface scattering anisotropy, new GPU volume sampling, improved PMJ sampling pattern, and more. Some features have also been removed or changed, breaking backwards compatibility. Including the removal of the OpenCL backend, for which alternatives are under development. Release notes and code docs: https://wiki.blender.org/wiki/Reference/Release_Notes/3.0/Cycles https://wiki.blender.org/wiki/Source/Render/Cycles Credits: * Sergey Sharybin * Brecht Van Lommel * Patrick Mours (OptiX backend) * Christophe Hery (subsurface scattering anisotropy) * William Leeson (PMJ sampling pattern) * Alaska (various fixes and tweaks) * Thomas Dinges (various fixes) For the full commit history, see the cycles-x branch. This squashes together all the changes since intermediate changes would often fail building or tests. Ref T87839, T87837, T87836 Fixes T90734, T89353, T80267, T80267, T77185, T69800 --- CMakeLists.txt | 8 +- build_files/cmake/Modules/FindOptiX.cmake | 17 +- build_files/config/pipeline_config.yaml | 2 +- extern/cuew/include/cuew.h | 3 +- intern/cycles/CMakeLists.txt | 24 +- intern/cycles/app/CMakeLists.txt | 18 - intern/cycles/app/cycles_standalone.cpp | 8 +- intern/cycles/app/cycles_xml.cpp | 2 +- intern/cycles/blender/CMakeLists.txt | 10 +- intern/cycles/blender/addon/__init__.py | 9 +- intern/cycles/blender/addon/engine.py | 132 +- intern/cycles/blender/addon/presets.py | 50 +- intern/cycles/blender/addon/properties.py | 501 +-- intern/cycles/blender/addon/ui.py | 522 +--- intern/cycles/blender/addon/version_update.py | 44 +- intern/cycles/blender/blender_camera.cpp | 13 +- intern/cycles/blender/blender_device.cpp | 11 - intern/cycles/blender/blender_gpu_display.cpp | 761 +++++ intern/cycles/blender/blender_gpu_display.h | 211 ++ intern/cycles/blender/blender_light.cpp | 18 +- intern/cycles/blender/blender_object.cpp | 2 +- intern/cycles/blender/blender_python.cpp | 262 +- intern/cycles/blender/blender_session.cpp | 701 ++--- intern/cycles/blender/blender_session.h | 55 +- intern/cycles/blender/blender_shader.cpp | 33 +- intern/cycles/blender/blender_sync.cpp | 468 ++- intern/cycles/blender/blender_sync.h | 35 +- intern/cycles/blender/blender_viewport.cpp | 43 +- intern/cycles/blender/blender_viewport.h | 5 +- intern/cycles/bvh/bvh_build.cpp | 18 +- intern/cycles/bvh/bvh_embree.cpp | 89 +- intern/cycles/bvh/bvh_params.h | 21 + intern/cycles/cmake/external_libs.cmake | 3 - intern/cycles/device/CMakeLists.txt | 110 +- intern/cycles/device/cpu/device.cpp | 64 + .../cpu/device.h} | 27 +- intern/cycles/device/cpu/device_impl.cpp | 481 +++ intern/cycles/device/cpu/device_impl.h | 99 + intern/cycles/device/cpu/kernel.cpp | 61 + intern/cycles/device/cpu/kernel.h | 111 + intern/cycles/device/cpu/kernel_function.h | 124 + .../device/cpu/kernel_thread_globals.cpp | 85 + .../cycles/device/cpu/kernel_thread_globals.h | 57 + .../{device_cuda.cpp => cuda/device.cpp} | 51 +- .../cuda/device.h} | 29 +- intern/cycles/device/cuda/device_cuda.h | 270 -- .../cycles/device/cuda/device_cuda_impl.cpp | 2714 ----------------- intern/cycles/device/cuda/device_impl.cpp | 1370 +++++++++ intern/cycles/device/cuda/device_impl.h | 155 + .../cycles/device/cuda/graphics_interop.cpp | 102 + intern/cycles/device/cuda/graphics_interop.h | 66 + intern/cycles/device/cuda/kernel.cpp | 69 + intern/cycles/device/cuda/kernel.h | 56 + intern/cycles/device/cuda/queue.cpp | 220 ++ intern/cycles/device/cuda/queue.h | 67 + intern/cycles/device/cuda/util.cpp | 61 + intern/cycles/device/cuda/util.h | 65 + intern/cycles/device/device.cpp | 484 +-- intern/cycles/device/device.h | 364 +-- intern/cycles/device/device_cpu.cpp | 1680 ---------- intern/cycles/device/device_denoise.cpp | 88 + intern/cycles/device/device_denoise.h | 110 + intern/cycles/device/device_denoising.cpp | 353 --- intern/cycles/device/device_denoising.h | 197 -- .../device_graphics_interop.cpp} | 11 +- .../cycles/device/device_graphics_interop.h | 55 + intern/cycles/device/device_intern.h | 58 - intern/cycles/device/device_kernel.cpp | 157 + .../device_kernel.h} | 25 +- intern/cycles/device/device_memory.cpp | 7 +- intern/cycles/device/device_memory.h | 136 +- intern/cycles/device/device_multi.cpp | 826 ----- intern/cycles/device/device_network.cpp | 812 ----- intern/cycles/device/device_network.h | 490 --- intern/cycles/device/device_opencl.cpp | 245 -- intern/cycles/device/device_optix.cpp | 1936 ------------ intern/cycles/device/device_queue.cpp | 87 + intern/cycles/device/device_queue.h | 113 + intern/cycles/device/device_split_kernel.cpp | 389 --- intern/cycles/device/device_split_kernel.h | 145 - intern/cycles/device/device_task.cpp | 182 -- intern/cycles/device/device_task.h | 188 -- .../{device_dummy.cpp => dummy/device.cpp} | 24 +- .../dummy/device.h} | 21 +- intern/cycles/device/multi/device.cpp | 423 +++ .../multi/device.h} | 21 +- intern/cycles/device/opencl/device_opencl.h | 658 ---- .../device/opencl/device_opencl_impl.cpp | 2113 ------------- .../cycles/device/opencl/memory_manager.cpp | 264 -- intern/cycles/device/opencl/memory_manager.h | 105 - intern/cycles/device/opencl/opencl_util.cpp | 1326 -------- intern/cycles/device/optix/device.cpp | 105 + intern/cycles/device/optix/device.h | 35 + intern/cycles/device/optix/device_impl.cpp | 1573 ++++++++++ intern/cycles/device/optix/device_impl.h | 186 ++ intern/cycles/device/optix/queue.cpp | 144 + intern/cycles/device/optix/queue.h | 39 + intern/cycles/device/optix/util.h | 45 + intern/cycles/graph/node.cpp | 2 +- intern/cycles/graph/node.h | 18 +- intern/cycles/integrator/CMakeLists.txt | 76 + .../cycles/integrator/adaptive_sampling.cpp | 71 + intern/cycles/integrator/adaptive_sampling.h | 55 + intern/cycles/integrator/denoiser.cpp | 204 ++ intern/cycles/integrator/denoiser.h | 135 + intern/cycles/integrator/denoiser_device.cpp | 106 + intern/cycles/integrator/denoiser_device.h | 40 + intern/cycles/integrator/denoiser_oidn.cpp | 628 ++++ intern/cycles/integrator/denoiser_oidn.h | 47 + .../denoiser_optix.cpp} | 26 +- .../denoiser_optix.h} | 21 +- intern/cycles/integrator/pass_accessor.cpp | 318 ++ intern/cycles/integrator/pass_accessor.h | 160 + .../cycles/integrator/pass_accessor_cpu.cpp | 183 ++ intern/cycles/integrator/pass_accessor_cpu.h | 77 + .../cycles/integrator/pass_accessor_gpu.cpp | 118 + intern/cycles/integrator/pass_accessor_gpu.h | 68 + intern/cycles/integrator/path_trace.cpp | 1147 +++++++ intern/cycles/integrator/path_trace.h | 324 ++ intern/cycles/integrator/path_trace_work.cpp | 203 ++ intern/cycles/integrator/path_trace_work.h | 194 ++ .../cycles/integrator/path_trace_work_cpu.cpp | 281 ++ .../cycles/integrator/path_trace_work_cpu.h | 82 + .../cycles/integrator/path_trace_work_gpu.cpp | 933 ++++++ .../cycles/integrator/path_trace_work_gpu.h | 165 + intern/cycles/integrator/render_scheduler.cpp | 1187 +++++++ intern/cycles/integrator/render_scheduler.h | 466 +++ intern/cycles/integrator/shader_eval.cpp | 173 ++ intern/cycles/integrator/shader_eval.h | 61 + intern/cycles/integrator/tile.cpp | 108 + intern/cycles/integrator/tile.h | 56 + intern/cycles/integrator/work_balancer.cpp | 99 + intern/cycles/integrator/work_balancer.h | 42 + .../cycles/integrator/work_tile_scheduler.cpp | 138 + .../cycles/integrator/work_tile_scheduler.h | 98 + intern/cycles/kernel/CMakeLists.txt | 316 +- intern/cycles/kernel/bvh/bvh.h | 32 +- intern/cycles/kernel/bvh/bvh_embree.h | 21 +- intern/cycles/kernel/bvh/bvh_local.h | 8 +- intern/cycles/kernel/bvh/bvh_nodes.h | 10 +- intern/cycles/kernel/bvh/bvh_shadow_all.h | 105 +- intern/cycles/kernel/bvh/bvh_traversal.h | 26 +- intern/cycles/kernel/bvh/bvh_types.h | 5 +- intern/cycles/kernel/bvh/bvh_util.h | 110 +- intern/cycles/kernel/bvh/bvh_volume.h | 13 +- intern/cycles/kernel/bvh/bvh_volume_all.h | 14 +- intern/cycles/kernel/closure/alloc.h | 2 + intern/cycles/kernel/closure/bsdf.h | 91 +- .../kernel/closure/bsdf_ashikhmin_shirley.h | 25 +- .../kernel/closure/bsdf_ashikhmin_velvet.h | 15 +- intern/cycles/kernel/closure/bsdf_diffuse.h | 13 +- .../cycles/kernel/closure/bsdf_diffuse_ramp.h | 5 +- intern/cycles/kernel/closure/bsdf_hair.h | 14 +- .../kernel/closure/bsdf_hair_principled.h | 25 +- .../cycles/kernel/closure/bsdf_microfacet.h | 31 +- .../kernel/closure/bsdf_microfacet_multi.h | 6 +- .../cycles/kernel/closure/bsdf_oren_nayar.h | 13 +- .../cycles/kernel/closure/bsdf_phong_ramp.h | 5 +- .../kernel/closure/bsdf_principled_diffuse.h | 15 +- .../kernel/closure/bsdf_principled_sheen.h | 7 +- .../cycles/kernel/closure/bsdf_reflection.h | 5 +- .../cycles/kernel/closure/bsdf_refraction.h | 5 +- intern/cycles/kernel/closure/bsdf_toon.h | 14 +- .../cycles/kernel/closure/bsdf_transparent.h | 5 +- intern/cycles/kernel/closure/bsdf_util.h | 5 +- intern/cycles/kernel/closure/bssrdf.h | 412 +-- intern/cycles/kernel/closure/emissive.h | 2 + intern/cycles/kernel/closure/volume.h | 107 +- .../cpu/compat.h} | 59 +- intern/cycles/kernel/device/cpu/globals.h | 61 + .../kernel_cpu_image.h => device/cpu/image.h} | 9 +- .../kernel/{kernels => device}/cpu/kernel.cpp | 4 +- .../cycles/kernel/{ => device/cpu}/kernel.h | 25 +- intern/cycles/kernel/device/cpu/kernel_arch.h | 113 + .../kernel/device/cpu/kernel_arch_impl.h | 235 ++ .../{kernels => device}/cpu/kernel_avx.cpp | 4 +- .../{kernels => device}/cpu/kernel_avx2.cpp | 4 +- .../{kernels => device}/cpu/kernel_sse2.cpp | 4 +- .../{kernels => device}/cpu/kernel_sse3.cpp | 4 +- .../{kernels => device}/cpu/kernel_sse41.cpp | 4 +- .../cuda/compat.h} | 147 +- intern/cycles/kernel/device/cuda/config.h | 114 + intern/cycles/kernel/device/cuda/globals.h | 48 + .../cuda/kernel.cu} | 18 +- .../gpu/image.h} | 55 +- intern/cycles/kernel/device/gpu/kernel.h | 843 +++++ .../kernel/device/gpu/parallel_active_index.h | 83 + .../kernel/device/gpu/parallel_prefix_sum.h | 46 + .../kernel/device/gpu/parallel_reduce.h | 83 + .../kernel/device/gpu/parallel_sorted_index.h | 49 + .../optix/compat.h} | 90 +- intern/cycles/kernel/device/optix/globals.h | 59 + .../optix/kernel.cu} | 176 +- .../device/optix/kernel_shader_raytrace.cu | 29 + intern/cycles/kernel/filter/filter.h | 52 - intern/cycles/kernel/filter/filter_defines.h | 72 - intern/cycles/kernel/filter/filter_features.h | 156 - .../kernel/filter/filter_features_sse.h | 118 - intern/cycles/kernel/filter/filter_kernel.h | 50 - intern/cycles/kernel/filter/filter_nlm_cpu.h | 254 -- intern/cycles/kernel/filter/filter_nlm_gpu.h | 255 -- .../cycles/kernel/filter/filter_prefilter.h | 303 -- .../kernel/filter/filter_reconstruction.h | 140 - .../cycles/kernel/filter/filter_transform.h | 120 - .../kernel/filter/filter_transform_gpu.h | 129 - .../kernel/filter/filter_transform_sse.h | 129 - intern/cycles/kernel/geom/geom.h | 3 + intern/cycles/kernel/geom/geom_attribute.h | 12 +- intern/cycles/kernel/geom/geom_curve.h | 21 +- .../cycles/kernel/geom/geom_curve_intersect.h | 68 +- intern/cycles/kernel/geom/geom_motion_curve.h | 12 +- .../cycles/kernel/geom/geom_motion_triangle.h | 12 +- .../geom/geom_motion_triangle_intersect.h | 76 +- .../kernel/geom/geom_motion_triangle_shader.h | 16 +- intern/cycles/kernel/geom/geom_object.h | 243 +- intern/cycles/kernel/geom/geom_patch.h | 20 +- intern/cycles/kernel/geom/geom_primitive.h | 39 +- intern/cycles/kernel/geom/geom_shader_data.h | 373 +++ .../cycles/kernel/geom/geom_subd_triangle.h | 29 +- intern/cycles/kernel/geom/geom_triangle.h | 37 +- .../kernel/geom/geom_triangle_intersect.h | 81 +- intern/cycles/kernel/geom/geom_volume.h | 6 +- .../integrator/integrator_init_from_bake.h | 181 ++ .../integrator/integrator_init_from_camera.h | 120 + .../integrator/integrator_intersect_closest.h | 248 ++ .../integrator/integrator_intersect_shadow.h | 144 + .../integrator_intersect_subsurface.h} | 27 +- .../integrator_intersect_volume_stack.h | 198 ++ .../kernel/integrator/integrator_megakernel.h | 93 + .../integrator/integrator_shade_background.h | 215 ++ .../integrator/integrator_shade_light.h | 126 + .../integrator/integrator_shade_shadow.h | 182 ++ .../integrator/integrator_shade_surface.h | 502 +++ .../integrator/integrator_shade_volume.h | 1015 ++++++ .../kernel/integrator/integrator_state.h | 185 ++ .../kernel/integrator/integrator_state_flow.h | 144 + .../integrator/integrator_state_template.h | 163 + .../kernel/integrator/integrator_state_util.h | 273 ++ .../kernel/integrator/integrator_subsurface.h | 623 ++++ .../integrator/integrator_volume_stack.h | 223 ++ intern/cycles/kernel/kernel_accumulate.h | 1030 +++---- .../cycles/kernel/kernel_adaptive_sampling.h | 262 +- intern/cycles/kernel/kernel_bake.h | 518 +--- intern/cycles/kernel/kernel_camera.h | 70 +- intern/cycles/kernel/kernel_color.h | 9 +- intern/cycles/kernel/kernel_compat_opencl.h | 177 -- intern/cycles/kernel/kernel_differential.h | 73 +- intern/cycles/kernel/kernel_emission.h | 378 +-- intern/cycles/kernel/kernel_film.h | 567 +++- intern/cycles/kernel/kernel_globals.h | 248 -- intern/cycles/kernel/kernel_id_passes.h | 35 +- intern/cycles/kernel/kernel_jitter.h | 262 +- intern/cycles/kernel/kernel_light.h | 402 ++- .../cycles/kernel/kernel_light_background.h | 25 +- intern/cycles/kernel/kernel_light_common.h | 6 +- intern/cycles/kernel/kernel_lookup_table.h | 56 + intern/cycles/kernel/kernel_math.h | 5 +- intern/cycles/kernel/kernel_montecarlo.h | 5 +- intern/cycles/kernel/kernel_passes.h | 418 +-- intern/cycles/kernel/kernel_path.h | 709 ----- intern/cycles/kernel/kernel_path_branched.h | 556 ---- intern/cycles/kernel/kernel_path_common.h | 48 - intern/cycles/kernel/kernel_path_state.h | 385 ++- intern/cycles/kernel/kernel_path_subsurface.h | 139 - intern/cycles/kernel/kernel_path_surface.h | 360 --- intern/cycles/kernel/kernel_path_volume.h | 260 -- intern/cycles/kernel/kernel_profiling.h | 24 +- intern/cycles/kernel/kernel_projection.h | 5 +- intern/cycles/kernel/kernel_queues.h | 147 - intern/cycles/kernel/kernel_random.h | 232 +- intern/cycles/kernel/kernel_shader.h | 1055 ++----- intern/cycles/kernel/kernel_shadow.h | 466 --- intern/cycles/kernel/kernel_shadow_catcher.h | 116 + intern/cycles/kernel/kernel_subsurface.h | 724 ----- intern/cycles/kernel/kernel_textures.h | 2 +- intern/cycles/kernel/kernel_types.h | 1030 +++---- intern/cycles/kernel/kernel_volume.h | 1440 --------- intern/cycles/kernel/kernel_work_stealing.h | 87 +- intern/cycles/kernel/kernel_write_passes.h | 61 +- intern/cycles/kernel/kernels/cpu/filter.cpp | 61 - .../cycles/kernel/kernels/cpu/filter_avx.cpp | 39 - .../cycles/kernel/kernels/cpu/filter_avx2.cpp | 40 - intern/cycles/kernel/kernels/cpu/filter_cpu.h | 143 - .../kernel/kernels/cpu/filter_cpu_impl.h | 331 -- .../cycles/kernel/kernels/cpu/filter_sse2.cpp | 34 - .../cycles/kernel/kernels/cpu/filter_sse3.cpp | 36 - .../kernel/kernels/cpu/filter_sse41.cpp | 38 - intern/cycles/kernel/kernels/cpu/kernel_cpu.h | 100 - .../kernel/kernels/cpu/kernel_cpu_impl.h | 232 -- .../kernel/kernels/cpu/kernel_split.cpp | 62 - .../kernel/kernels/cpu/kernel_split_avx.cpp | 41 - .../kernel/kernels/cpu/kernel_split_avx2.cpp | 42 - .../kernel/kernels/cpu/kernel_split_sse2.cpp | 36 - .../kernel/kernels/cpu/kernel_split_sse3.cpp | 38 - .../kernel/kernels/cpu/kernel_split_sse41.cpp | 39 - intern/cycles/kernel/kernels/cuda/filter.cu | 413 --- intern/cycles/kernel/kernels/cuda/kernel.cu | 232 -- .../kernel/kernels/cuda/kernel_config.h | 121 - .../kernel/kernels/cuda/kernel_split.cu | 156 - intern/cycles/kernel/kernels/opencl/filter.cl | 321 -- .../opencl/kernel_adaptive_adjust_samples.cl | 23 - .../opencl/kernel_adaptive_filter_x.cl | 23 - .../opencl/kernel_adaptive_filter_y.cl | 23 - .../opencl/kernel_adaptive_stopping.cl | 23 - .../kernels/opencl/kernel_background.cl | 35 - .../kernel/kernels/opencl/kernel_bake.cl | 36 - .../kernel/kernels/opencl/kernel_base.cl | 88 - .../kernel/kernels/opencl/kernel_data_init.cl | 53 - .../kernel/kernels/opencl/kernel_displace.cl | 36 - .../opencl/kernel_next_iteration_setup.cl | 26 - .../kernels/opencl/kernel_opencl_image.h | 358 --- .../kernels/opencl/kernel_queue_enqueue.cl | 26 - .../kernels/opencl/kernel_scene_intersect.cl | 24 - .../kernels/opencl/kernel_shader_eval.cl | 24 - .../kernels/opencl/kernel_shader_setup.cl | 26 - .../kernels/opencl/kernel_shader_sort.cl | 27 - .../opencl/kernel_shadow_blocked_ao.cl | 24 - .../opencl/kernel_shadow_blocked_dl.cl | 24 - .../kernels/opencl/kernel_split_bundle.cl | 34 - .../kernels/opencl/kernel_split_function.h | 67 - .../opencl/kernel_subsurface_scatter.cl | 24 - intern/cycles/kernel/osl/background.cpp | 2 +- .../cycles/kernel/osl/bsdf_diffuse_ramp.cpp | 2 +- intern/cycles/kernel/osl/bsdf_phong_ramp.cpp | 2 +- intern/cycles/kernel/osl/emissive.cpp | 2 +- intern/cycles/kernel/osl/osl_bssrdf.cpp | 40 +- intern/cycles/kernel/osl/osl_closures.cpp | 8 +- intern/cycles/kernel/osl/osl_services.cpp | 158 +- intern/cycles/kernel/osl/osl_services.h | 16 +- intern/cycles/kernel/osl/osl_shader.cpp | 40 +- intern/cycles/kernel/osl/osl_shader.h | 26 +- .../kernel/shaders/node_principled_bsdf.osl | 31 +- .../shaders/node_subsurface_scattering.osl | 25 +- .../split/kernel_adaptive_adjust_samples.h | 43 - .../kernel/split/kernel_adaptive_filter_x.h | 30 - .../kernel/split/kernel_adaptive_filter_y.h | 29 - .../kernel/split/kernel_adaptive_stopping.h | 37 - intern/cycles/kernel/split/kernel_branched.h | 231 -- .../kernel/split/kernel_buffer_update.h | 154 - intern/cycles/kernel/split/kernel_data_init.h | 115 - .../kernel/split/kernel_direct_lighting.h | 152 - intern/cycles/kernel/split/kernel_do_volume.h | 227 -- .../kernel/split/kernel_enqueue_inactive.h | 46 - ...out_emission_blurring_pathtermination_ao.h | 149 - .../kernel/split/kernel_indirect_background.h | 69 - .../kernel/split/kernel_indirect_subsurface.h | 67 - .../kernel/split/kernel_lamp_emission.h | 67 - .../split/kernel_next_iteration_setup.h | 258 -- intern/cycles/kernel/split/kernel_path_init.h | 78 - .../kernel/split/kernel_queue_enqueue.h | 87 - .../kernel/split/kernel_scene_intersect.h | 83 - .../cycles/kernel/split/kernel_shader_eval.h | 69 - .../cycles/kernel/split/kernel_shader_setup.h | 74 - .../cycles/kernel/split/kernel_shader_sort.h | 97 - .../kernel/split/kernel_shadow_blocked_ao.h | 59 - .../kernel/split/kernel_shadow_blocked_dl.h | 98 - .../cycles/kernel/split/kernel_split_common.h | 106 - .../cycles/kernel/split/kernel_split_data.h | 77 - .../kernel/split/kernel_split_data_types.h | 180 -- .../kernel/split/kernel_subsurface_scatter.h | 264 -- intern/cycles/kernel/svm/svm.h | 227 +- intern/cycles/kernel/svm/svm_ao.h | 53 +- intern/cycles/kernel/svm/svm_aov.h | 42 +- intern/cycles/kernel/svm/svm_attribute.h | 57 +- intern/cycles/kernel/svm/svm_bevel.h | 143 +- intern/cycles/kernel/svm/svm_blackbody.h | 7 +- intern/cycles/kernel/svm/svm_brick.h | 11 +- intern/cycles/kernel/svm/svm_brightness.h | 2 +- intern/cycles/kernel/svm/svm_bump.h | 16 +- intern/cycles/kernel/svm/svm_camera.h | 12 +- intern/cycles/kernel/svm/svm_checker.h | 5 +- intern/cycles/kernel/svm/svm_clamp.h | 17 +- intern/cycles/kernel/svm/svm_closure.h | 121 +- intern/cycles/kernel/svm/svm_convert.h | 4 +- intern/cycles/kernel/svm/svm_displace.h | 21 +- intern/cycles/kernel/svm/svm_fresnel.h | 4 +- intern/cycles/kernel/svm/svm_gamma.h | 2 +- intern/cycles/kernel/svm/svm_geometry.h | 24 +- intern/cycles/kernel/svm/svm_gradient.h | 2 +- intern/cycles/kernel/svm/svm_hsv.h | 6 +- intern/cycles/kernel/svm/svm_ies.h | 10 +- intern/cycles/kernel/svm/svm_image.h | 26 +- intern/cycles/kernel/svm/svm_invert.h | 2 +- intern/cycles/kernel/svm/svm_light_path.h | 50 +- intern/cycles/kernel/svm/svm_magic.h | 7 +- intern/cycles/kernel/svm/svm_map_range.h | 19 +- intern/cycles/kernel/svm/svm_mapping.h | 41 +- intern/cycles/kernel/svm/svm_math.h | 30 +- intern/cycles/kernel/svm/svm_mix.h | 17 +- intern/cycles/kernel/svm/svm_musgrave.h | 19 +- intern/cycles/kernel/svm/svm_noise.h | 10 +- intern/cycles/kernel/svm/svm_noisetex.h | 19 +- intern/cycles/kernel/svm/svm_normal.h | 17 +- intern/cycles/kernel/svm/svm_ramp.h | 34 +- intern/cycles/kernel/svm/svm_sepcomb_hsv.h | 34 +- intern/cycles/kernel/svm/svm_sky.h | 33 +- intern/cycles/kernel/svm/svm_tex_coord.h | 55 +- intern/cycles/kernel/svm/svm_types.h | 43 +- intern/cycles/kernel/svm/svm_value.h | 9 +- intern/cycles/kernel/svm/svm_vector_rotate.h | 10 +- .../cycles/kernel/svm/svm_vector_transform.h | 8 +- intern/cycles/kernel/svm/svm_vertex_color.h | 48 +- intern/cycles/kernel/svm/svm_voronoi.h | 148 +- intern/cycles/kernel/svm/svm_voxel.h | 11 +- intern/cycles/kernel/svm/svm_wave.h | 9 +- intern/cycles/kernel/svm/svm_wavelength.h | 4 +- intern/cycles/kernel/svm/svm_white_noise.h | 13 +- intern/cycles/kernel/svm/svm_wireframe.h | 18 +- intern/cycles/render/CMakeLists.txt | 7 +- intern/cycles/render/background.cpp | 12 - intern/cycles/render/background.h | 4 - intern/cycles/render/bake.cpp | 110 +- intern/cycles/render/bake.h | 6 +- intern/cycles/render/buffers.cpp | 733 ++--- intern/cycles/render/buffers.h | 256 +- intern/cycles/render/camera.cpp | 19 +- intern/cycles/render/camera.h | 3 +- intern/cycles/render/coverage.cpp | 155 - intern/cycles/render/coverage.h | 52 - intern/cycles/render/denoising.cpp | 31 +- intern/cycles/render/denoising.h | 35 +- intern/cycles/render/film.cpp | 744 +++-- intern/cycles/render/film.h | 55 +- intern/cycles/render/geometry.cpp | 14 +- intern/cycles/render/gpu_display.cpp | 227 ++ intern/cycles/render/gpu_display.h | 247 ++ intern/cycles/render/graph.h | 15 +- intern/cycles/render/integrator.cpp | 214 +- intern/cycles/render/integrator.h | 36 +- intern/cycles/render/jitter.cpp | 6 - intern/cycles/render/light.cpp | 132 +- intern/cycles/render/light.h | 5 +- intern/cycles/render/mesh_displace.cpp | 165 +- intern/cycles/render/nodes.cpp | 80 +- intern/cycles/render/nodes.h | 267 +- intern/cycles/render/object.cpp | 20 +- intern/cycles/render/osl.cpp | 52 +- intern/cycles/render/pass.cpp | 427 +++ intern/cycles/render/pass.h | 106 + intern/cycles/render/scene.cpp | 189 +- intern/cycles/render/scene.h | 48 +- intern/cycles/render/session.cpp | 1361 +++------ intern/cycles/render/session.h | 225 +- intern/cycles/render/shader.cpp | 60 +- intern/cycles/render/shader.h | 7 +- intern/cycles/render/stats.cpp | 65 +- intern/cycles/render/svm.cpp | 17 +- intern/cycles/render/svm.h | 3 + intern/cycles/render/tile.cpp | 1050 +++---- intern/cycles/render/tile.h | 228 +- intern/cycles/test/CMakeLists.txt | 5 + .../integrator_adaptive_sampling_test.cpp | 116 + .../test/integrator_render_scheduler_test.cpp | 37 + intern/cycles/test/integrator_tile_test.cpp | 47 + .../test/render_graph_finalize_test.cpp | 2 +- intern/cycles/test/util_math_test.cpp | 61 + intern/cycles/test/util_string_test.cpp | 36 + intern/cycles/util/util_atomic.h | 50 - intern/cycles/util/util_debug.cpp | 83 +- intern/cycles/util/util_debug.h | 67 +- intern/cycles/util/util_defines.h | 4 +- intern/cycles/util/util_half.h | 46 +- intern/cycles/util/util_logging.h | 1 + intern/cycles/util/util_math.h | 97 +- intern/cycles/util/util_math_float2.h | 5 - intern/cycles/util/util_math_float3.h | 128 +- intern/cycles/util/util_math_float4.h | 145 +- intern/cycles/util/util_math_int2.h | 4 - intern/cycles/util/util_math_int3.h | 40 +- intern/cycles/util/util_path.cpp | 184 +- intern/cycles/util/util_path.h | 8 +- intern/cycles/util/util_profiling.cpp | 8 +- intern/cycles/util/util_profiling.h | 112 +- intern/cycles/util/util_progress.h | 22 - intern/cycles/util/util_simd.h | 14 +- intern/cycles/util/util_static_assert.h | 4 +- intern/cycles/util/util_string.cpp | 36 +- intern/cycles/util/util_string.h | 12 +- intern/cycles/util/util_system.cpp | 9 + intern/cycles/util/util_system.h | 3 + intern/cycles/util/util_tbb.h | 1 + intern/cycles/util/util_texture.h | 2 - intern/cycles/util/util_transform.h | 34 +- intern/cycles/util/util_types.h | 10 +- intern/cycles/util/util_unique_ptr.h | 1 + .../scripts/modules/rna_manual_reference.py | 2 - .../scripts/presets/cycles/sampling/Final.py | 24 +- .../presets/cycles/sampling/Preview.py | 24 +- .../presets/cycles/viewport_sampling/Final.py | 11 + .../cycles/viewport_sampling/Preview.py | 11 + .../startup/bl_ui/properties_view_layer.py | 2 - .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/layer.c | 1 - .../blenloader/intern/versioning_270.c | 16 - .../blenloader/intern/versioning_280.c | 2 +- .../blenloader/intern/versioning_290.c | 1 - .../blenloader/intern/versioning_300.c | 33 + .../blenloader/intern/versioning_cycles.c | 26 +- .../blenloader/intern/versioning_defaults.c | 11 + .../compositor/nodes/COM_IDMaskNode.cc | 4 +- .../compositor/nodes/COM_ZCombineNode.cc | 4 +- source/blender/draw/DRW_engine.h | 3 + .../draw/engines/eevee/eevee_cryptomatte.c | 10 +- .../blender/draw/engines/eevee/eevee_engine.c | 2 + .../draw/engines/eevee/eevee_private.h | 1 - .../draw/engines/external/external_engine.c | 202 +- .../draw/engines/external/external_engine.h | 8 + .../draw/engines/select/select_engine.c | 2 + .../draw/engines/workbench/workbench_engine.c | 2 + source/blender/draw/intern/DRW_render.h | 1 + source/blender/draw/intern/draw_manager.c | 78 +- .../blender/draw/intern/draw_manager_exec.c | 5 + .../blender/editors/object/object_bake_api.c | 18 +- .../blender/editors/render/render_preview.c | 9 - .../BlenderStrokeRenderer.cpp | 12 +- source/blender/gpu/GPU_material.h | 5 +- source/blender/gpu/intern/gpu_material.c | 114 +- .../blender/gpu/intern/gpu_material_library.h | 2 +- .../gpu_shader_material_principled.glsl | 4 +- ...shader_material_subsurface_scattering.glsl | 14 +- source/blender/makesdna/DNA_layer_types.h | 2 +- source/blender/makesdna/DNA_node_types.h | 12 +- source/blender/makesdna/DNA_scene_defaults.h | 2 - source/blender/makesdna/DNA_scene_types.h | 19 +- source/blender/makesrna/intern/rna_nodetree.c | 39 +- source/blender/makesrna/intern/rna_render.c | 98 +- source/blender/makesrna/intern/rna_scene.c | 63 +- .../composite/nodes/node_composite_image.c | 45 +- .../nodes/node_shader_bsdf_principled.c | 29 +- .../nodes/node_shader_subsurface_scattering.c | 30 +- source/blender/render/CMakeLists.txt | 1 - source/blender/render/RE_engine.h | 46 +- source/blender/render/RE_pipeline.h | 3 - source/blender/render/intern/bake.c | 19 +- source/blender/render/intern/engine.c | 282 +- source/blender/render/intern/initrender.c | 91 - source/blender/render/intern/initrender.h | 38 - source/blender/render/intern/pipeline.c | 64 +- source/blender/render/intern/render_result.c | 253 +- source/blender/render/intern/render_result.h | 18 - source/blender/render/intern/render_types.h | 30 +- .../blender/windowmanager/intern/wm_window.c | 13 +- tests/performance/tests/cycles.py | 24 +- tests/python/CMakeLists.txt | 1 - 544 files changed, 34250 insertions(+), 43628 deletions(-) create mode 100644 intern/cycles/blender/blender_gpu_display.cpp create mode 100644 intern/cycles/blender/blender_gpu_display.h create mode 100644 intern/cycles/device/cpu/device.cpp rename intern/cycles/{kernel/kernels/opencl/kernel_buffer_update.cl => device/cpu/device.h} (59%) create mode 100644 intern/cycles/device/cpu/device_impl.cpp create mode 100644 intern/cycles/device/cpu/device_impl.h create mode 100644 intern/cycles/device/cpu/kernel.cpp create mode 100644 intern/cycles/device/cpu/kernel.h create mode 100644 intern/cycles/device/cpu/kernel_function.h create mode 100644 intern/cycles/device/cpu/kernel_thread_globals.cpp create mode 100644 intern/cycles/device/cpu/kernel_thread_globals.h rename intern/cycles/device/{device_cuda.cpp => cuda/device.cpp} (92%) rename intern/cycles/{kernel/kernels/opencl/kernel_enqueue_inactive.cl => device/cuda/device.h} (57%) delete mode 100644 intern/cycles/device/cuda/device_cuda.h delete mode 100644 intern/cycles/device/cuda/device_cuda_impl.cpp create mode 100644 intern/cycles/device/cuda/device_impl.cpp create mode 100644 intern/cycles/device/cuda/device_impl.h create mode 100644 intern/cycles/device/cuda/graphics_interop.cpp create mode 100644 intern/cycles/device/cuda/graphics_interop.h create mode 100644 intern/cycles/device/cuda/kernel.cpp create mode 100644 intern/cycles/device/cuda/kernel.h create mode 100644 intern/cycles/device/cuda/queue.cpp create mode 100644 intern/cycles/device/cuda/queue.h create mode 100644 intern/cycles/device/cuda/util.cpp create mode 100644 intern/cycles/device/cuda/util.h delete mode 100644 intern/cycles/device/device_cpu.cpp create mode 100644 intern/cycles/device/device_denoise.cpp create mode 100644 intern/cycles/device/device_denoise.h delete mode 100644 intern/cycles/device/device_denoising.cpp delete mode 100644 intern/cycles/device/device_denoising.h rename intern/cycles/{kernel/kernels/opencl/kernel_path_init.cl => device/device_graphics_interop.cpp} (66%) create mode 100644 intern/cycles/device/device_graphics_interop.h delete mode 100644 intern/cycles/device/device_intern.h create mode 100644 intern/cycles/device/device_kernel.cpp rename intern/cycles/{kernel/kernels/opencl/kernel_holdout_emission_blurring_pathtermination_ao.cl => device/device_kernel.h} (58%) delete mode 100644 intern/cycles/device/device_multi.cpp delete mode 100644 intern/cycles/device/device_network.cpp delete mode 100644 intern/cycles/device/device_network.h delete mode 100644 intern/cycles/device/device_opencl.cpp delete mode 100644 intern/cycles/device/device_optix.cpp create mode 100644 intern/cycles/device/device_queue.cpp create mode 100644 intern/cycles/device/device_queue.h delete mode 100644 intern/cycles/device/device_split_kernel.cpp delete mode 100644 intern/cycles/device/device_split_kernel.h delete mode 100644 intern/cycles/device/device_task.cpp delete mode 100644 intern/cycles/device/device_task.h rename intern/cycles/device/{device_dummy.cpp => dummy/device.cpp} (73%) rename intern/cycles/{kernel/kernels/opencl/kernel_do_volume.cl => device/dummy/device.h} (64%) create mode 100644 intern/cycles/device/multi/device.cpp rename intern/cycles/{kernel/kernels/opencl/kernel_indirect_background.cl => device/multi/device.h} (64%) delete mode 100644 intern/cycles/device/opencl/device_opencl.h delete mode 100644 intern/cycles/device/opencl/device_opencl_impl.cpp delete mode 100644 intern/cycles/device/opencl/memory_manager.cpp delete mode 100644 intern/cycles/device/opencl/memory_manager.h delete mode 100644 intern/cycles/device/opencl/opencl_util.cpp create mode 100644 intern/cycles/device/optix/device.cpp create mode 100644 intern/cycles/device/optix/device.h create mode 100644 intern/cycles/device/optix/device_impl.cpp create mode 100644 intern/cycles/device/optix/device_impl.h create mode 100644 intern/cycles/device/optix/queue.cpp create mode 100644 intern/cycles/device/optix/queue.h create mode 100644 intern/cycles/device/optix/util.h create mode 100644 intern/cycles/integrator/CMakeLists.txt create mode 100644 intern/cycles/integrator/adaptive_sampling.cpp create mode 100644 intern/cycles/integrator/adaptive_sampling.h create mode 100644 intern/cycles/integrator/denoiser.cpp create mode 100644 intern/cycles/integrator/denoiser.h create mode 100644 intern/cycles/integrator/denoiser_device.cpp create mode 100644 intern/cycles/integrator/denoiser_device.h create mode 100644 intern/cycles/integrator/denoiser_oidn.cpp create mode 100644 intern/cycles/integrator/denoiser_oidn.h rename intern/cycles/{kernel/kernels/opencl/kernel_direct_lighting.cl => integrator/denoiser_optix.cpp} (58%) rename intern/cycles/{kernel/kernels/opencl/kernel_lamp_emission.cl => integrator/denoiser_optix.h} (62%) create mode 100644 intern/cycles/integrator/pass_accessor.cpp create mode 100644 intern/cycles/integrator/pass_accessor.h create mode 100644 intern/cycles/integrator/pass_accessor_cpu.cpp create mode 100644 intern/cycles/integrator/pass_accessor_cpu.h create mode 100644 intern/cycles/integrator/pass_accessor_gpu.cpp create mode 100644 intern/cycles/integrator/pass_accessor_gpu.h create mode 100644 intern/cycles/integrator/path_trace.cpp create mode 100644 intern/cycles/integrator/path_trace.h create mode 100644 intern/cycles/integrator/path_trace_work.cpp create mode 100644 intern/cycles/integrator/path_trace_work.h create mode 100644 intern/cycles/integrator/path_trace_work_cpu.cpp create mode 100644 intern/cycles/integrator/path_trace_work_cpu.h create mode 100644 intern/cycles/integrator/path_trace_work_gpu.cpp create mode 100644 intern/cycles/integrator/path_trace_work_gpu.h create mode 100644 intern/cycles/integrator/render_scheduler.cpp create mode 100644 intern/cycles/integrator/render_scheduler.h create mode 100644 intern/cycles/integrator/shader_eval.cpp create mode 100644 intern/cycles/integrator/shader_eval.h create mode 100644 intern/cycles/integrator/tile.cpp create mode 100644 intern/cycles/integrator/tile.h create mode 100644 intern/cycles/integrator/work_balancer.cpp create mode 100644 intern/cycles/integrator/work_balancer.h create mode 100644 intern/cycles/integrator/work_tile_scheduler.cpp create mode 100644 intern/cycles/integrator/work_tile_scheduler.h rename intern/cycles/kernel/{kernel_compat_cpu.h => device/cpu/compat.h} (61%) create mode 100644 intern/cycles/kernel/device/cpu/globals.h rename intern/cycles/kernel/{kernels/cpu/kernel_cpu_image.h => device/cpu/image.h} (98%) rename intern/cycles/kernel/{kernels => device}/cpu/kernel.cpp (96%) rename intern/cycles/kernel/{ => device/cpu}/kernel.h (77%) create mode 100644 intern/cycles/kernel/device/cpu/kernel_arch.h create mode 100644 intern/cycles/kernel/device/cpu/kernel_arch_impl.h rename intern/cycles/kernel/{kernels => device}/cpu/kernel_avx.cpp (93%) rename intern/cycles/kernel/{kernels => device}/cpu/kernel_avx2.cpp (93%) rename intern/cycles/kernel/{kernels => device}/cpu/kernel_sse2.cpp (93%) rename intern/cycles/kernel/{kernels => device}/cpu/kernel_sse3.cpp (93%) rename intern/cycles/kernel/{kernels => device}/cpu/kernel_sse41.cpp (93%) rename intern/cycles/kernel/{kernel_compat_cuda.h => device/cuda/compat.h} (56%) create mode 100644 intern/cycles/kernel/device/cuda/config.h create mode 100644 intern/cycles/kernel/device/cuda/globals.h rename intern/cycles/kernel/{kernels/opencl/kernel_indirect_subsurface.cl => device/cuda/kernel.cu} (64%) rename intern/cycles/kernel/{kernels/cuda/kernel_cuda_image.h => device/gpu/image.h} (77%) create mode 100644 intern/cycles/kernel/device/gpu/kernel.h create mode 100644 intern/cycles/kernel/device/gpu/parallel_active_index.h create mode 100644 intern/cycles/kernel/device/gpu/parallel_prefix_sum.h create mode 100644 intern/cycles/kernel/device/gpu/parallel_reduce.h create mode 100644 intern/cycles/kernel/device/gpu/parallel_sorted_index.h rename intern/cycles/kernel/{kernel_compat_optix.h => device/optix/compat.h} (54%) create mode 100644 intern/cycles/kernel/device/optix/globals.h rename intern/cycles/kernel/{kernels/optix/kernel_optix.cu => device/optix/kernel.cu} (66%) create mode 100644 intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu delete mode 100644 intern/cycles/kernel/filter/filter.h delete mode 100644 intern/cycles/kernel/filter/filter_defines.h delete mode 100644 intern/cycles/kernel/filter/filter_features.h delete mode 100644 intern/cycles/kernel/filter/filter_features_sse.h delete mode 100644 intern/cycles/kernel/filter/filter_kernel.h delete mode 100644 intern/cycles/kernel/filter/filter_nlm_cpu.h delete mode 100644 intern/cycles/kernel/filter/filter_nlm_gpu.h delete mode 100644 intern/cycles/kernel/filter/filter_prefilter.h delete mode 100644 intern/cycles/kernel/filter/filter_reconstruction.h delete mode 100644 intern/cycles/kernel/filter/filter_transform.h delete mode 100644 intern/cycles/kernel/filter/filter_transform_gpu.h delete mode 100644 intern/cycles/kernel/filter/filter_transform_sse.h create mode 100644 intern/cycles/kernel/geom/geom_shader_data.h create mode 100644 intern/cycles/kernel/integrator/integrator_init_from_bake.h create mode 100644 intern/cycles/kernel/integrator/integrator_init_from_camera.h create mode 100644 intern/cycles/kernel/integrator/integrator_intersect_closest.h create mode 100644 intern/cycles/kernel/integrator/integrator_intersect_shadow.h rename intern/cycles/kernel/{kernels/opencl/kernel_state_buffer_size.cl => integrator/integrator_intersect_subsurface.h} (55%) create mode 100644 intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h create mode 100644 intern/cycles/kernel/integrator/integrator_megakernel.h create mode 100644 intern/cycles/kernel/integrator/integrator_shade_background.h create mode 100644 intern/cycles/kernel/integrator/integrator_shade_light.h create mode 100644 intern/cycles/kernel/integrator/integrator_shade_shadow.h create mode 100644 intern/cycles/kernel/integrator/integrator_shade_surface.h create mode 100644 intern/cycles/kernel/integrator/integrator_shade_volume.h create mode 100644 intern/cycles/kernel/integrator/integrator_state.h create mode 100644 intern/cycles/kernel/integrator/integrator_state_flow.h create mode 100644 intern/cycles/kernel/integrator/integrator_state_template.h create mode 100644 intern/cycles/kernel/integrator/integrator_state_util.h create mode 100644 intern/cycles/kernel/integrator/integrator_subsurface.h create mode 100644 intern/cycles/kernel/integrator/integrator_volume_stack.h delete mode 100644 intern/cycles/kernel/kernel_compat_opencl.h delete mode 100644 intern/cycles/kernel/kernel_globals.h create mode 100644 intern/cycles/kernel/kernel_lookup_table.h delete mode 100644 intern/cycles/kernel/kernel_path.h delete mode 100644 intern/cycles/kernel/kernel_path_branched.h delete mode 100644 intern/cycles/kernel/kernel_path_common.h delete mode 100644 intern/cycles/kernel/kernel_path_subsurface.h delete mode 100644 intern/cycles/kernel/kernel_path_surface.h delete mode 100644 intern/cycles/kernel/kernel_path_volume.h delete mode 100644 intern/cycles/kernel/kernel_queues.h delete mode 100644 intern/cycles/kernel/kernel_shadow.h create mode 100644 intern/cycles/kernel/kernel_shadow_catcher.h delete mode 100644 intern/cycles/kernel/kernel_subsurface.h delete mode 100644 intern/cycles/kernel/kernel_volume.h delete mode 100644 intern/cycles/kernel/kernels/cpu/filter.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/filter_avx.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/filter_avx2.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/filter_cpu.h delete mode 100644 intern/cycles/kernel/kernels/cpu/filter_cpu_impl.h delete mode 100644 intern/cycles/kernel/kernels/cpu/filter_sse2.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/filter_sse3.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/filter_sse41.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/kernel_cpu.h delete mode 100644 intern/cycles/kernel/kernels/cpu/kernel_cpu_impl.h delete mode 100644 intern/cycles/kernel/kernels/cpu/kernel_split.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/kernel_split_avx.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/kernel_split_avx2.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/kernel_split_sse2.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/kernel_split_sse3.cpp delete mode 100644 intern/cycles/kernel/kernels/cpu/kernel_split_sse41.cpp delete mode 100644 intern/cycles/kernel/kernels/cuda/filter.cu delete mode 100644 intern/cycles/kernel/kernels/cuda/kernel.cu delete mode 100644 intern/cycles/kernel/kernels/cuda/kernel_config.h delete mode 100644 intern/cycles/kernel/kernels/cuda/kernel_split.cu delete mode 100644 intern/cycles/kernel/kernels/opencl/filter.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_adaptive_adjust_samples.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_x.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_y.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_adaptive_stopping.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_background.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_bake.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_base.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_data_init.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_displace.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_next_iteration_setup.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_queue_enqueue.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_scene_intersect.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_shader_eval.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_shader_setup.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_shader_sort.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_ao.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_dl.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_split_function.h delete mode 100644 intern/cycles/kernel/kernels/opencl/kernel_subsurface_scatter.cl delete mode 100644 intern/cycles/kernel/split/kernel_adaptive_adjust_samples.h delete mode 100644 intern/cycles/kernel/split/kernel_adaptive_filter_x.h delete mode 100644 intern/cycles/kernel/split/kernel_adaptive_filter_y.h delete mode 100644 intern/cycles/kernel/split/kernel_adaptive_stopping.h delete mode 100644 intern/cycles/kernel/split/kernel_branched.h delete mode 100644 intern/cycles/kernel/split/kernel_buffer_update.h delete mode 100644 intern/cycles/kernel/split/kernel_data_init.h delete mode 100644 intern/cycles/kernel/split/kernel_direct_lighting.h delete mode 100644 intern/cycles/kernel/split/kernel_do_volume.h delete mode 100644 intern/cycles/kernel/split/kernel_enqueue_inactive.h delete mode 100644 intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h delete mode 100644 intern/cycles/kernel/split/kernel_indirect_background.h delete mode 100644 intern/cycles/kernel/split/kernel_indirect_subsurface.h delete mode 100644 intern/cycles/kernel/split/kernel_lamp_emission.h delete mode 100644 intern/cycles/kernel/split/kernel_next_iteration_setup.h delete mode 100644 intern/cycles/kernel/split/kernel_path_init.h delete mode 100644 intern/cycles/kernel/split/kernel_queue_enqueue.h delete mode 100644 intern/cycles/kernel/split/kernel_scene_intersect.h delete mode 100644 intern/cycles/kernel/split/kernel_shader_eval.h delete mode 100644 intern/cycles/kernel/split/kernel_shader_setup.h delete mode 100644 intern/cycles/kernel/split/kernel_shader_sort.h delete mode 100644 intern/cycles/kernel/split/kernel_shadow_blocked_ao.h delete mode 100644 intern/cycles/kernel/split/kernel_shadow_blocked_dl.h delete mode 100644 intern/cycles/kernel/split/kernel_split_common.h delete mode 100644 intern/cycles/kernel/split/kernel_split_data.h delete mode 100644 intern/cycles/kernel/split/kernel_split_data_types.h delete mode 100644 intern/cycles/kernel/split/kernel_subsurface_scatter.h delete mode 100644 intern/cycles/render/coverage.cpp delete mode 100644 intern/cycles/render/coverage.h create mode 100644 intern/cycles/render/gpu_display.cpp create mode 100644 intern/cycles/render/gpu_display.h create mode 100644 intern/cycles/render/pass.cpp create mode 100644 intern/cycles/render/pass.h create mode 100644 intern/cycles/test/integrator_adaptive_sampling_test.cpp create mode 100644 intern/cycles/test/integrator_render_scheduler_test.cpp create mode 100644 intern/cycles/test/integrator_tile_test.cpp create mode 100644 intern/cycles/test/util_math_test.cpp create mode 100644 release/scripts/presets/cycles/viewport_sampling/Final.py create mode 100644 release/scripts/presets/cycles/viewport_sampling/Preview.py delete mode 100644 source/blender/render/intern/initrender.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 47712f0ac1e..8e807b84e22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -403,7 +403,7 @@ option(WITH_CYCLES_CUDA_BINARIES "Build Cycles CUDA binaries" OFF) option(WITH_CYCLES_CUBIN_COMPILER "Build cubins with nvrtc based compiler instead of nvcc" OFF) option(WITH_CYCLES_CUDA_BUILD_SERIAL "Build cubins one after another (useful on machines with limited RAM)" OFF) mark_as_advanced(WITH_CYCLES_CUDA_BUILD_SERIAL) -set(CYCLES_TEST_DEVICES CPU CACHE STRING "Run regression tests on the specified device types (CPU CUDA OPTIX OPENCL)" ) +set(CYCLES_TEST_DEVICES CPU CACHE STRING "Run regression tests on the specified device types (CPU CUDA OPTIX)" ) set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 compute_75 CACHE STRING "CUDA architectures to build binaries for") mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH) unset(PLATFORM_DEFAULT) @@ -418,12 +418,8 @@ mark_as_advanced(WITH_CYCLES_DEBUG_NAN) mark_as_advanced(WITH_CYCLES_NATIVE_ONLY) option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles CUDA compute support" ON) -option(WITH_CYCLES_DEVICE_OPTIX "Enable Cycles OptiX support" OFF) -option(WITH_CYCLES_DEVICE_OPENCL "Enable Cycles OpenCL compute support" ON) -option(WITH_CYCLES_NETWORK "Enable Cycles compute over network support (EXPERIMENTAL and unfinished)" OFF) +option(WITH_CYCLES_DEVICE_OPTIX "Enable Cycles OptiX support" ON) mark_as_advanced(WITH_CYCLES_DEVICE_CUDA) -mark_as_advanced(WITH_CYCLES_DEVICE_OPENCL) -mark_as_advanced(WITH_CYCLES_NETWORK) option(WITH_CUDA_DYNLOAD "Dynamically load CUDA libraries at runtime" ON) mark_as_advanced(WITH_CUDA_DYNLOAD) diff --git a/build_files/cmake/Modules/FindOptiX.cmake b/build_files/cmake/Modules/FindOptiX.cmake index cfcdd9cd23b..67106740f57 100644 --- a/build_files/cmake/Modules/FindOptiX.cmake +++ b/build_files/cmake/Modules/FindOptiX.cmake @@ -33,11 +33,23 @@ FIND_PATH(OPTIX_INCLUDE_DIR include ) +IF(EXISTS "${OPTIX_INCLUDE_DIR}/optix.h") + FILE(STRINGS "${OPTIX_INCLUDE_DIR}/optix.h" _optix_version REGEX "^#define OPTIX_VERSION[ \t].*$") + STRING(REGEX MATCHALL "[0-9]+" _optix_version ${_optix_version}) + + MATH(EXPR _optix_version_major "${_optix_version} / 10000") + MATH(EXPR _optix_version_minor "(${_optix_version} % 10000) / 100") + MATH(EXPR _optix_version_patch "${_optix_version} % 100") + + SET(OPTIX_VERSION "${_optix_version_major}.${_optix_version_minor}.${_optix_version_patch}") +ENDIF() + # handle the QUIETLY and REQUIRED arguments and set OPTIX_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(OptiX DEFAULT_MSG - OPTIX_INCLUDE_DIR) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(OptiX + REQUIRED_VARS OPTIX_INCLUDE_DIR + VERSION_VAR OPTIX_VERSION) IF(OPTIX_FOUND) SET(OPTIX_INCLUDE_DIRS ${OPTIX_INCLUDE_DIR}) @@ -45,6 +57,7 @@ ENDIF() MARK_AS_ADVANCED( OPTIX_INCLUDE_DIR + OPTIX_VERSION ) UNSET(_optix_SEARCH_DIRS) diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml index 5d1a24a30f1..8222f2ff0b9 100644 --- a/build_files/config/pipeline_config.yaml +++ b/build_files/config/pipeline_config.yaml @@ -55,7 +55,7 @@ buildbot: cuda11: version: '11.4.1' optix: - version: '7.1.0' + version: '7.3.0' cmake: default: version: any diff --git a/extern/cuew/include/cuew.h b/extern/cuew/include/cuew.h index 0fa0f1291fa..a2142b8f2ba 100644 --- a/extern/cuew/include/cuew.h +++ b/extern/cuew/include/cuew.h @@ -645,7 +645,8 @@ typedef enum CUdevice_P2PAttribute_enum { CU_DEVICE_P2P_ATTRIBUTE_PERFORMANCE_RANK = 0x01, CU_DEVICE_P2P_ATTRIBUTE_ACCESS_SUPPORTED = 0x02, CU_DEVICE_P2P_ATTRIBUTE_NATIVE_ATOMIC_SUPPORTED = 0x03, - CU_DEVICE_P2P_ATTRIBUTE_ARRAY_ACCESS_ACCESS_SUPPORTED = 0x04, + CU_DEVICE_P2P_ATTRIBUTE_ACCESS_ACCESS_SUPPORTED = 0x04, + CU_DEVICE_P2P_ATTRIBUTE_CUDA_ARRAY_ACCESS_SUPPORTED = 0x04, } CUdevice_P2PAttribute; typedef void (CUDA_CB *CUstreamCallback)(CUstream hStream, CUresult status, void* userData); diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index 381248e9bf1..17096d441f0 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -247,7 +247,7 @@ if(WITH_CYCLES_OSL) endif() if(WITH_CYCLES_DEVICE_OPTIX) - find_package(OptiX) + find_package(OptiX 7.3.0) if(OPTIX_FOUND) add_definitions(-DWITH_OPTIX) @@ -286,11 +286,17 @@ if(WITH_OPENSUBDIV) ) endif() +if(WITH_OPENIMAGEDENOISE) + add_definitions(-DWITH_OPENIMAGEDENOISE) + add_definitions(-DOIDN_STATIC_LIB) + include_directories( + SYSTEM + ${OPENIMAGEDENOISE_INCLUDE_DIRS} + ) +endif() + if(WITH_CYCLES_STANDALONE) - set(WITH_CYCLES_DEVICE_OPENCL TRUE) set(WITH_CYCLES_DEVICE_CUDA TRUE) - # Experimental and unfinished. - set(WITH_CYCLES_NETWORK FALSE) endif() # TODO(sergey): Consider removing it, only causes confusion in interface. set(WITH_CYCLES_DEVICE_MULTI TRUE) @@ -386,18 +392,12 @@ if(WITH_CYCLES_BLENDER) add_subdirectory(blender) endif() -if(WITH_CYCLES_NETWORK) - add_definitions(-DWITH_NETWORK) -endif() - -if(WITH_CYCLES_STANDALONE OR WITH_CYCLES_NETWORK OR WITH_CYCLES_CUBIN_COMPILER) - add_subdirectory(app) -endif() - +add_subdirectory(app) add_subdirectory(bvh) add_subdirectory(device) add_subdirectory(doc) add_subdirectory(graph) +add_subdirectory(integrator) add_subdirectory(kernel) add_subdirectory(render) add_subdirectory(subd) diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index 7a1e5d62dd2..f9dc5f00802 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -90,24 +90,6 @@ if(WITH_CYCLES_STANDALONE) endif() endif() -##################################################################### -# Cycles network server executable -##################################################################### - -if(WITH_CYCLES_NETWORK) - set(SRC - cycles_server.cpp - ) - add_executable(cycles_server ${SRC}) - target_link_libraries(cycles_server ${LIBRARIES}) - cycles_target_link_libraries(cycles_server) - - if(UNIX AND NOT APPLE) - set_target_properties(cycles_server PROPERTIES INSTALL_RPATH $ORIGIN/lib) - endif() - unset(SRC) -endif() - ##################################################################### # Cycles cubin compiler executable ##################################################################### diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp index 6b3513b065a..270096d70b0 100644 --- a/intern/cycles/app/cycles_standalone.cpp +++ b/intern/cycles/app/cycles_standalone.cpp @@ -126,7 +126,7 @@ static BufferParams &session_buffer_params() static void scene_init() { - options.scene = new Scene(options.scene_params, options.session->device); + options.scene = options.session->scene; /* Read XML */ xml_read_file(options.scene, options.filepath.c_str()); @@ -148,7 +148,7 @@ static void scene_init() static void session_init() { options.session_params.write_render_cb = write_render; - options.session = new Session(options.session_params); + options.session = new Session(options.session_params, options.scene_params); if (options.session_params.background && !options.quiet) options.session->progress.set_update_callback(function_bind(&session_print_status)); @@ -159,7 +159,6 @@ static void session_init() /* load scene */ scene_init(); - options.session->scene = options.scene; options.session->reset(session_buffer_params(), options.session_params.samples); options.session->start(); @@ -527,9 +526,6 @@ static void options_parse(int argc, const char **argv) fprintf(stderr, "No file path specified\n"); exit(EXIT_FAILURE); } - - /* For smoother Viewport */ - options.session_params.start_resolution = 64; } CCL_NAMESPACE_END diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 276d850f1b3..54f97fddbd9 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -703,7 +703,7 @@ void xml_read_file(Scene *scene, const char *filepath) xml_read_include(state, path_filename(filepath)); - scene->params.bvh_type = SceneParams::BVH_STATIC; + scene->params.bvh_type = BVH_TYPE_STATIC; } CCL_NAMESPACE_END diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index ee5c6157338..5bdcfd56a4d 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -33,6 +33,7 @@ set(SRC blender_device.cpp blender_image.cpp blender_geometry.cpp + blender_gpu_display.cpp blender_light.cpp blender_mesh.cpp blender_object.cpp @@ -50,6 +51,7 @@ set(SRC CCL_api.h blender_device.h + blender_gpu_display.h blender_id_map.h blender_image.h blender_object_cull.h @@ -93,14 +95,6 @@ set(ADDON_FILES add_definitions(${GL_DEFINITIONS}) -if(WITH_CYCLES_DEVICE_OPENCL) - add_definitions(-DWITH_OPENCL) -endif() - -if(WITH_CYCLES_NETWORK) - add_definitions(-DWITH_NETWORK) -endif() - if(WITH_MOD_FLUID) add_definitions(-DWITH_FLUID) endif() diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py index f728050a3cf..1ce25a253f9 100644 --- a/intern/cycles/blender/addon/__init__.py +++ b/intern/cycles/blender/addon/__init__.py @@ -58,7 +58,6 @@ class CyclesRender(bpy.types.RenderEngine): bl_use_eevee_viewport = True bl_use_preview = True bl_use_exclude_layers = True - bl_use_save_buffers = True bl_use_spherical_stereo = True bl_use_custom_freestyle = True bl_use_alembic_procedural = True @@ -85,6 +84,12 @@ class CyclesRender(bpy.types.RenderEngine): def render(self, depsgraph): engine.render(self, depsgraph) + def render_frame_finish(self): + engine.render_frame_finish(self) + + def draw(self, context, depsgraph): + engine.draw(self, depsgraph, context.space_data) + def bake(self, depsgraph, obj, pass_type, pass_filter, width, height): engine.bake(self, depsgraph, obj, pass_type, pass_filter, width, height) @@ -98,7 +103,7 @@ class CyclesRender(bpy.types.RenderEngine): engine.sync(self, depsgraph, context.blend_data) def view_draw(self, context, depsgraph): - engine.draw(self, depsgraph, context.region, context.space_data, context.region_data) + engine.view_draw(self, depsgraph, context.region, context.space_data, context.region_data) def update_script_node(self, node): if engine.with_osl(): diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index 489a883f098..e0e8ca10bef 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -18,62 +18,17 @@ from __future__ import annotations -def _is_using_buggy_driver(): - import gpu - # We need to be conservative here because in multi-GPU systems display card - # might be quite old, but others one might be just good. - # - # So We shouldn't disable possible good dedicated cards just because display - # card seems weak. And instead we only blacklist configurations which are - # proven to cause problems. - if gpu.platform.vendor_get() == "ATI Technologies Inc.": - import re - version = gpu.platform.version_get() - if version.endswith("Compatibility Profile Context"): - # Old HD 4xxx and 5xxx series drivers did not have driver version - # in the version string, but those cards do not quite work and - # causing crashes. - return True - regex = re.compile(".*Compatibility Profile Context ([0-9]+(\\.[0-9]+)+)$") - if not regex.match(version): - # Skip cards like FireGL - return False - version = regex.sub("\\1", version).split('.') - return int(version[0]) == 8 - return False - - -def _workaround_buggy_drivers(): - if _is_using_buggy_driver(): - import _cycles - if hasattr(_cycles, "opencl_disable"): - print("Cycles: OpenGL driver known to be buggy, disabling OpenCL platform.") - _cycles.opencl_disable() - - def _configure_argument_parser(): import argparse # No help because it conflicts with general Python scripts argument parsing parser = argparse.ArgumentParser(description="Cycles Addon argument parser", add_help=False) - parser.add_argument("--cycles-resumable-num-chunks", - help="Number of chunks to split sample range into", - default=None) - parser.add_argument("--cycles-resumable-current-chunk", - help="Current chunk of samples range to render", - default=None) - parser.add_argument("--cycles-resumable-start-chunk", - help="Start chunk to render", - default=None) - parser.add_argument("--cycles-resumable-end-chunk", - help="End chunk to render", - default=None) parser.add_argument("--cycles-print-stats", help="Print rendering statistics to stderr", action='store_true') parser.add_argument("--cycles-device", help="Set the device to use for Cycles, overriding user preferences and the scene setting." - "Valid options are 'CPU', 'CUDA', 'OPTIX' or 'OPENCL'." + "Valid options are 'CPU', 'CUDA' or 'OPTIX'." "Additionally, you can append '+CPU' to any GPU type for hybrid rendering.", default=None) return parser @@ -89,21 +44,6 @@ def _parse_command_line(): parser = _configure_argument_parser() args, _ = parser.parse_known_args(argv[argv.index("--") + 1:]) - if args.cycles_resumable_num_chunks is not None: - if args.cycles_resumable_current_chunk is not None: - import _cycles - _cycles.set_resumable_chunk( - int(args.cycles_resumable_num_chunks), - int(args.cycles_resumable_current_chunk), - ) - elif args.cycles_resumable_start_chunk is not None and \ - args.cycles_resumable_end_chunk: - import _cycles - _cycles.set_resumable_chunk_range( - int(args.cycles_resumable_num_chunks), - int(args.cycles_resumable_start_chunk), - int(args.cycles_resumable_end_chunk), - ) if args.cycles_print_stats: import _cycles _cycles.enable_print_stats() @@ -118,23 +58,11 @@ def init(): import _cycles import os.path - # Workaround possibly buggy legacy drivers which crashes on the OpenCL - # device enumeration. - # - # This checks are not really correct because they might still fail - # in the case of multiple GPUs. However, currently buggy drivers - # are really old and likely to be used in single GPU systems only - # anyway. - # - # Can't do it in the background mode, so we hope OpenCL is no enabled - # in the user preferences. - if not bpy.app.background: - _workaround_buggy_drivers() - path = os.path.dirname(__file__) user_path = os.path.dirname(os.path.abspath(bpy.utils.user_resource('CONFIG', path=''))) + temp_path = bpy.app.tempdir - _cycles.init(path, user_path, bpy.app.background) + _cycles.init(path, user_path, temp_path, bpy.app.background) _parse_command_line() @@ -177,6 +105,25 @@ def render(engine, depsgraph): _cycles.render(engine.session, depsgraph.as_pointer()) +def render_frame_finish(engine): + if not engine.session: + return + + import _cycles + _cycles.render_frame_finish(engine.session) + +def draw(engine, depsgraph, space_image): + if not engine.session: + return + + depsgraph_ptr = depsgraph.as_pointer() + space_image_ptr = space_image.as_pointer() + screen_ptr = space_image.id_data.as_pointer() + + import _cycles + _cycles.draw(engine.session, depsgraph_ptr, screen_ptr, space_image_ptr) + + def bake(engine, depsgraph, obj, pass_type, pass_filter, width, height): import _cycles session = getattr(engine, "session", None) @@ -204,14 +151,14 @@ def sync(engine, depsgraph, data): _cycles.sync(engine.session, depsgraph.as_pointer()) -def draw(engine, depsgraph, region, v3d, rv3d): +def view_draw(engine, depsgraph, region, v3d, rv3d): import _cycles depsgraph = depsgraph.as_pointer() v3d = v3d.as_pointer() rv3d = rv3d.as_pointer() # draw render image - _cycles.draw(engine.session, depsgraph, v3d, rv3d) + _cycles.view_draw(engine.session, depsgraph, v3d, rv3d) def available_devices(): @@ -224,11 +171,6 @@ def with_osl(): return _cycles.with_osl -def with_network(): - import _cycles - return _cycles.with_network - - def system_info(): import _cycles return _cycles.system_info() @@ -243,6 +185,7 @@ def list_render_passes(scene, srl): # Data passes. if srl.use_pass_z: yield ("Depth", "Z", 'VALUE') if srl.use_pass_mist: yield ("Mist", "Z", 'VALUE') + if srl.use_pass_position: yield ("Position", "XYZ", 'VECTOR') if srl.use_pass_normal: yield ("Normal", "XYZ", 'VECTOR') if srl.use_pass_vector: yield ("Vector", "XYZW", 'VECTOR') if srl.use_pass_uv: yield ("UV", "UVA", 'VECTOR') @@ -265,6 +208,7 @@ def list_render_passes(scene, srl): if srl.use_pass_environment: yield ("Env", "RGB", 'COLOR') if srl.use_pass_shadow: yield ("Shadow", "RGB", 'COLOR') if srl.use_pass_ambient_occlusion: yield ("AO", "RGB", 'COLOR') + if crl.use_pass_shadow_catcher: yield ("Shadow Catcher", "RGB", 'COLOR') # Debug passes. if crl.pass_debug_render_time: yield ("Debug Render Time", "X", 'VALUE') @@ -283,30 +227,20 @@ def list_render_passes(scene, srl): yield ("CryptoAsset" + '{:02d}'.format(i), "RGBA", 'COLOR') # Denoising passes. - if (scene.cycles.use_denoising and crl.use_denoising) or crl.denoising_store_passes: + if scene.cycles.use_denoising and crl.use_denoising: yield ("Noisy Image", "RGBA", 'COLOR') - if crl.denoising_store_passes: - yield ("Denoising Normal", "XYZ", 'VECTOR') - yield ("Denoising Albedo", "RGB", 'COLOR') - yield ("Denoising Depth", "Z", 'VALUE') - - if scene.cycles.denoiser == 'NLM': - yield ("Denoising Shadowing", "X", 'VALUE') - yield ("Denoising Variance", "RGB", 'COLOR') - yield ("Denoising Intensity", "X", 'VALUE') - - clean_options = ("denoising_diffuse_direct", "denoising_diffuse_indirect", - "denoising_glossy_direct", "denoising_glossy_indirect", - "denoising_transmission_direct", "denoising_transmission_indirect") - if any(getattr(crl, option) for option in clean_options): - yield ("Denoising Clean", "RGB", 'COLOR') + if crl.use_pass_shadow_catcher: + yield ("Noisy Shadow Catcher", "RGBA", 'COLOR') + if crl.denoising_store_passes: + yield ("Denoising Normal", "XYZ", 'VECTOR') + yield ("Denoising Albedo", "RGB", 'COLOR') # Custom AOV passes. for aov in srl.aovs: if aov.type == 'VALUE': yield (aov.name, "X", 'VALUE') else: - yield (aov.name, "RGBA", 'COLOR') + yield (aov.name, "RGB", 'COLOR') def register_passes(engine, scene, view_layer): diff --git a/intern/cycles/blender/addon/presets.py b/intern/cycles/blender/addon/presets.py index bf33e5dc010..37c39904e30 100644 --- a/intern/cycles/blender/addon/presets.py +++ b/intern/cycles/blender/addon/presets.py @@ -60,32 +60,48 @@ class AddPresetSampling(AddPresetBase, Operator): ] preset_values = [ + "cycles.use_adaptive_sampling", "cycles.samples", - "cycles.preview_samples", - "cycles.aa_samples", - "cycles.preview_aa_samples", - "cycles.diffuse_samples", - "cycles.glossy_samples", - "cycles.transmission_samples", - "cycles.ao_samples", - "cycles.mesh_light_samples", - "cycles.subsurface_samples", - "cycles.volume_samples", - "cycles.use_square_samples", - "cycles.progressive", - "cycles.seed", - "cycles.sample_clamp_direct", - "cycles.sample_clamp_indirect", - "cycles.sample_all_lights_direct", - "cycles.sample_all_lights_indirect", + "cycles.adaptive_threshold", + "cycles.adaptive_min_samples", + "cycles.time_limit", + "cycles.use_denoising", + "cycles.denoiser", + "cycles.denoising_input_passes", + "cycles.denoising_prefilter", ] preset_subdir = "cycles/sampling" +class AddPresetViewportSampling(AddPresetBase, Operator): + '''Add a Viewport Sampling Preset''' + bl_idname = "render.cycles_viewport_sampling_preset_add" + bl_label = "Add Viewport Sampling Preset" + preset_menu = "CYCLES_PT_viewport_sampling_presets" + + preset_defines = [ + "cycles = bpy.context.scene.cycles" + ] + + preset_values = [ + "cycles.use_preview_adaptive_sampling", + "cycles.preview_samples", + "cycles.preview_adaptive_threshold", + "cycles.preview_adaptive_min_samples", + "cycles.use_preview_denoising", + "cycles.preview_denoiser", + "cycles.preview_denoising_input_passes", + "cycles.preview_denoising_prefilter", + "cycles.preview_denoising_start_sample", + ] + + preset_subdir = "cycles/viewport_sampling" + classes = ( AddPresetIntegrator, AddPresetSampling, + AddPresetViewportSampling, ) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 0c3af3fabeb..c2570e71efd 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -39,11 +39,6 @@ enum_devices = ( ('GPU', "GPU Compute", "Use GPU compute device for rendering, configured in the system tab in the user preferences"), ) -from _cycles import with_network -if with_network: - enum_devices += (('NETWORK', "Networked Device", "Use networked device for rendering"),) -del with_network - enum_feature_set = ( ('SUPPORTED', "Supported", "Only use finished and supported features"), ('EXPERIMENTAL', "Experimental", "Use experimental and incomplete features that might be broken or change in the future", 'ERROR', 1), @@ -84,15 +79,6 @@ enum_curve_shape = ( ('THICK', "3D Curves", "Render hair as 3D curve, for accurate results when viewing hair close up"), ) -enum_tile_order = ( - ('CENTER', "Center", "Render from center to the edges"), - ('RIGHT_TO_LEFT', "Right to Left", "Render from right to left"), - ('LEFT_TO_RIGHT', "Left to Right", "Render from left to right"), - ('TOP_TO_BOTTOM', "Top to Bottom", "Render from top to bottom"), - ('BOTTOM_TO_TOP', "Bottom to Top", "Render from bottom to top"), - ('HILBERT_SPIRAL', "Hilbert Spiral", "Render in a Hilbert Spiral"), -) - enum_use_layer_samples = ( ('USE', "Use", "Per render layer number of samples override scene samples"), ('BOUNDED', "Bounded", "Bound per render layer number of samples by global samples"), @@ -101,15 +87,9 @@ enum_use_layer_samples = ( enum_sampling_pattern = ( ('SOBOL', "Sobol", "Use Sobol random sampling pattern"), - ('CORRELATED_MUTI_JITTER', "Correlated Multi-Jitter", "Use Correlated Multi-Jitter random sampling pattern"), ('PROGRESSIVE_MUTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern"), ) -enum_integrator = ( - ('BRANCHED_PATH', "Branched Path Tracing", "Path tracing integrator that branches on the first bounce, giving more control over the number of light and material samples"), - ('PATH', "Path Tracing", "Pure path tracing integrator"), -) - enum_volume_sampling = ( ('DISTANCE', "Distance", "Use distance sampling, best for dense volumes with lights far away"), ('EQUIANGULAR', "Equiangular", "Use equiangular sampling, best for volumes with low density with light inside or near the volume"), @@ -131,7 +111,6 @@ enum_device_type = ( ('CPU', "CPU", "CPU", 0), ('CUDA', "CUDA", "CUDA", 1), ('OPTIX', "OptiX", "OptiX", 3), - ('OPENCL', "OpenCL", "OpenCL", 2) ) enum_texture_limit = ( @@ -144,39 +123,46 @@ enum_texture_limit = ( ('4096', "4096", "Limit texture size to 4096 pixels", 6), ('8192', "8192", "Limit texture size to 8192 pixels", 7), ) - + +# NOTE: Identifiers are expected to be an upper case version of identifiers from `Pass::get_type_enum()` enum_view3d_shading_render_pass = ( ('', "General", ""), - ('COMBINED', "Combined", "Show the Combined Render pass", 1), - ('EMISSION', "Emission", "Show the Emission render pass", 33), - ('BACKGROUND', "Background", "Show the Background render pass", 34), - ('AO', "Ambient Occlusion", "Show the Ambient Occlusion render pass", 35), + ('COMBINED', "Combined", "Show the Combined Render pass"), + ('EMISSION', "Emission", "Show the Emission render pass"), + ('BACKGROUND', "Background", "Show the Background render pass"), + ('AO', "Ambient Occlusion", "Show the Ambient Occlusion render pass"), + ('SHADOW', "Shadow", "Show the Shadow render pass"), + ('SHADOW_CATCHER', "Shadow Catcher", "Show the Shadow Catcher render pass"), ('', "Light", ""), - ('DIFFUSE_DIRECT', "Diffuse Direct", "Show the Diffuse Direct render pass", 38), - ('DIFFUSE_INDIRECT', "Diffuse Indirect", "Show the Diffuse Indirect render pass", 39), - ('DIFFUSE_COLOR', "Diffuse Color", "Show the Diffuse Color render pass", 40), + ('DIFFUSE_DIRECT', "Diffuse Direct", "Show the Diffuse Direct render pass"), + ('DIFFUSE_INDIRECT', "Diffuse Indirect", "Show the Diffuse Indirect render pass"), + ('DIFFUSE_COLOR', "Diffuse Color", "Show the Diffuse Color render pass"), - ('GLOSSY_DIRECT', "Glossy Direct", "Show the Glossy Direct render pass", 41), - ('GLOSSY_INDIRECT', "Glossy Indirect", "Show the Glossy Indirect render pass", 42), - ('GLOSSY_COLOR', "Glossy Color", "Show the Glossy Color render pass", 43), + ('GLOSSY_DIRECT', "Glossy Direct", "Show the Glossy Direct render pass"), + ('GLOSSY_INDIRECT', "Glossy Indirect", "Show the Glossy Indirect render pass"), + ('GLOSSY_COLOR', "Glossy Color", "Show the Glossy Color render pass"), ('', "", ""), - ('TRANSMISSION_DIRECT', "Transmission Direct", "Show the Transmission Direct render pass", 44), - ('TRANSMISSION_INDIRECT', "Transmission Indirect", "Show the Transmission Indirect render pass", 45), - ('TRANSMISSION_COLOR', "Transmission Color", "Show the Transmission Color render pass", 46), + ('TRANSMISSION_DIRECT', "Transmission Direct", "Show the Transmission Direct render pass"), + ('TRANSMISSION_INDIRECT', "Transmission Indirect", "Show the Transmission Indirect render pass"), + ('TRANSMISSION_COLOR', "Transmission Color", "Show the Transmission Color render pass"), - ('VOLUME_DIRECT', "Volume Direct", "Show the Volume Direct render pass", 50), - ('VOLUME_INDIRECT', "Volume Indirect", "Show the Volume Indirect render pass", 51), + ('VOLUME_DIRECT', "Volume Direct", "Show the Volume Direct render pass"), + ('VOLUME_INDIRECT', "Volume Indirect", "Show the Volume Indirect render pass"), ('', "Data", ""), - ('NORMAL', "Normal", "Show the Normal render pass", 3), - ('UV', "UV", "Show the UV render pass", 4), - ('MIST', "Mist", "Show the Mist render pass", 32), + ('POSITION', "Position", "Show the Position render pass"), + ('NORMAL', "Normal", "Show the Normal render pass"), + ('UV', "UV", "Show the UV render pass"), + ('MIST', "Mist", "Show the Mist render pass"), + ('DENOISING_ALBEDO', "Denoising Albedo", "Albedo pass used by denoiser"), + ('DENOISING_NORMAL', "Denoising Normal", "Normal pass used by denoiser"), + ('SAMPLE_COUNT', "Sample Count", "Per-pixel number of samples"), ) @@ -208,18 +194,23 @@ def enum_preview_denoiser(self, context): def enum_denoiser(self, context): - items = [('NLM', "NLM", "Cycles native non-local means denoiser, running on any compute device", 1)] + items = [] items += enum_optix_denoiser(self, context) items += enum_openimagedenoise_denoiser(self, context) return items enum_denoising_input_passes = ( - ('RGB', "Color", "Use only color as input", 1), - ('RGB_ALBEDO', "Color + Albedo", "Use color and albedo data as input", 2), - ('RGB_ALBEDO_NORMAL', "Color + Albedo + Normal", "Use color, albedo and normal data as input", 3), + ('RGB', "None", "Don't use utility passes for denoising", 1), + ('RGB_ALBEDO', "Albedo", "Use albedo pass for denoising", 2), + ('RGB_ALBEDO_NORMAL', "Albedo and Normal", "Use albedo and normal passes for denoising", 3), ) +enum_denoising_prefilter = ( + ('NONE', "None", "No prefiltering, use when guiding passes are noise-free", 1), + ('FAST', "Fast", "Denoise color and guiding passes together. Improves quality when guiding passes are noisy using least amount of extra processing time", 2), + ('ACCURATE', "Accurate", "Prefilter noisy guiding passes before denoising color. Improves quality when guiding passes are noisy using extra processing time", 3), +) def update_render_passes(self, context): scene = context.scene @@ -252,13 +243,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Use Open Shading Language (CPU rendering only)", ) - progressive: EnumProperty( - name="Integrator", - description="Method to sample lights and materials", - items=enum_integrator, - default='PATH', - ) - preview_pause: BoolProperty( name="Pause Preview", description="Pause all viewport preview renders", @@ -268,110 +252,88 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): use_denoising: BoolProperty( name="Use Denoising", description="Denoise the rendered image", - default=False, + default=True, update=update_render_passes, ) + denoiser: EnumProperty( + name="Denoiser", + description="Denoise the image with the selected denoiser. " + "For denoising the image after rendering", + items=enum_denoiser, + default=4, # Use integer to avoid error in builds without OpenImageDenoise. + update=update_render_passes, + ) + denoising_prefilter: EnumProperty( + name="Denoising Prefilter", + description="Prefilter noisy guiding (albedo and normal) passes to improve denoising quality when using OpenImageDenoiser", + items=enum_denoising_prefilter, + default='ACCURATE', + ) + denoising_input_passes: EnumProperty( + name="Denoising Input Passes", + description="Passes used by the denoiser to distinguish noise from shader and geometry detail", + items=enum_denoising_input_passes, + default='RGB_ALBEDO_NORMAL', + ) + use_preview_denoising: BoolProperty( name="Use Viewport Denoising", description="Denoise the image in the 3D viewport", default=False, ) - - denoiser: EnumProperty( - name="Denoiser", - description="Denoise the image with the selected denoiser. " - "For denoising the image after rendering, denoising data render passes " - "also adapt to the selected denoiser", - items=enum_denoiser, - default=1, - update=update_render_passes, - ) preview_denoiser: EnumProperty( name="Viewport Denoiser", description="Denoise the image after each preview update with the selected denoiser", items=enum_preview_denoiser, default=0, ) - - use_square_samples: BoolProperty( - name="Square Samples", - description="Square sampling values for easier artist control", - default=False, + preview_denoising_prefilter: EnumProperty( + name="Viewport Denoising Prefilter", + description="Prefilter noisy guiding (albedo and normal) passes to improve denoising quality when using OpenImageDenoiser", + items=enum_denoising_prefilter, + default='FAST', + ) + preview_denoising_input_passes: EnumProperty( + name="Viewport Denoising Input Passes", + description="Passes used by the denoiser to distinguish noise from shader and geometry detail", + items=enum_denoising_input_passes, + default='RGB_ALBEDO', + ) + preview_denoising_start_sample: IntProperty( + name="Start Denoising", + description="Sample to start denoising the preview at", + min=0, max=(1 << 24), + default=1, ) samples: IntProperty( name="Samples", description="Number of samples to render for each pixel", min=1, max=(1 << 24), - default=128, + default=4096, ) preview_samples: IntProperty( name="Viewport Samples", description="Number of samples to render in the viewport, unlimited if 0", min=0, max=(1 << 24), - default=32, - ) - aa_samples: IntProperty( - name="AA Samples", - description="Number of antialiasing samples to render for each pixel", - min=1, max=2097151, - default=128, - ) - preview_aa_samples: IntProperty( - name="AA Samples", - description="Number of antialiasing samples to render in the viewport, unlimited if 0", - min=0, max=2097151, - default=32, + default=1024, ) - diffuse_samples: IntProperty( - name="Diffuse Samples", - description="Number of diffuse bounce samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - glossy_samples: IntProperty( - name="Glossy Samples", - description="Number of glossy bounce samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - transmission_samples: IntProperty( - name="Transmission Samples", - description="Number of transmission bounce samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - ao_samples: IntProperty( - name="Ambient Occlusion Samples", - description="Number of ambient occlusion samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - mesh_light_samples: IntProperty( - name="Mesh Light Samples", - description="Number of mesh emission light samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - subsurface_samples: IntProperty( - name="Subsurface Samples", - description="Number of subsurface scattering samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - volume_samples: IntProperty( - name="Volume Samples", - description="Number of volume scattering samples to render for each AA sample", - min=1, max=1024, - default=1, + time_limit: FloatProperty( + name="Time Limit", + description="Limit the render time (excluding synchronization time)." + "Zero disables the limit", + min=0.0, + default=0.0, + step=100.0, + unit='TIME_ABSOLUTE', ) sampling_pattern: EnumProperty( name="Sampling Pattern", description="Random sampling pattern used by the integrator", items=enum_sampling_pattern, - default='SOBOL', + default='PROGRESSIVE_MUTI_JITTER', ) use_layer_samples: EnumProperty( @@ -381,17 +343,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): default='USE', ) - sample_all_lights_direct: BoolProperty( - name="Sample All Direct Lights", - description="Sample all lights (for direct samples), rather than randomly picking one", - default=True, - ) - - sample_all_lights_indirect: BoolProperty( - name="Sample All Indirect Lights", - description="Sample all lights (for indirect samples), rather than randomly picking one", - default=True, - ) light_sampling_threshold: FloatProperty( name="Light Sampling Threshold", description="Probabilistically terminate light samples when the light contribution is below this threshold (more noise but faster rendering). " @@ -403,19 +354,39 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): use_adaptive_sampling: BoolProperty( name="Use Adaptive Sampling", description="Automatically reduce the number of samples per pixel based on estimated noise level", - default=False, + default=True, ) - adaptive_threshold: FloatProperty( name="Adaptive Sampling Threshold", description="Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples", min=0.0, max=1.0, - default=0.0, + soft_min=0.001, + default=0.01, precision=4, ) adaptive_min_samples: IntProperty( name="Adaptive Min Samples", - description="Minimum AA samples for adaptive sampling, to discover noisy features before stopping sampling. Zero for automatic setting based on number of AA samples", + description="Minimum AA samples for adaptive sampling, to discover noisy features before stopping sampling. Zero for automatic setting based on noise threshold", + min=0, max=4096, + default=0, + ) + + use_preview_adaptive_sampling: BoolProperty( + name="Use Adaptive Sampling", + description="Automatically reduce the number of samples per pixel based on estimated noise level, for viewport renders", + default=True, + ) + preview_adaptive_threshold: FloatProperty( + name="Adaptive Sampling Threshold", + description="Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples, for viewport renders", + min=0.0, max=1.0, + soft_min=0.001, + default=0.1, + precision=4, + ) + preview_adaptive_min_samples: IntProperty( + name="Adaptive Min Samples", + description="Minimum AA samples for adaptive sampling, to discover noisy features before stopping sampling. Zero for automatic setting based on noise threshold, for viewport renders", min=0, max=4096, default=0, ) @@ -632,53 +603,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): default=10.0, ) - debug_tile_size: IntProperty( - name="Tile Size", - description="", - min=1, max=4096, - default=1024, - ) - - preview_start_resolution: IntProperty( - name="Start Resolution", - description="Resolution to start rendering preview at, " - "progressively increasing it to the full viewport size", - min=8, max=16384, - default=64, - subtype='PIXEL' - ) - preview_denoising_start_sample: IntProperty( - name="Start Denoising", - description="Sample to start denoising the preview at", - min=0, max=(1 << 24), - default=1, - ) - preview_denoising_input_passes: EnumProperty( - name="Viewport Input Passes", - description="Passes used by the denoiser to distinguish noise from shader and geometry detail", - items=enum_denoising_input_passes, - default='RGB_ALBEDO', - ) - - debug_reset_timeout: FloatProperty( - name="Reset timeout", - description="", - min=0.01, max=10.0, - default=0.1, - ) - debug_cancel_timeout: FloatProperty( - name="Cancel timeout", - description="", - min=0.01, max=10.0, - default=0.1, - ) - debug_text_timeout: FloatProperty( - name="Text timeout", - description="", - min=0.01, max=10.0, - default=1.0, - ) - debug_bvh_type: EnumProperty( name="Viewport BVH Type", description="Choose between faster updates, or faster render", @@ -701,38 +625,24 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): default=0, min=0, max=16, ) - tile_order: EnumProperty( - name="Tile Order", - description="Tile order for rendering", - items=enum_tile_order, - default='HILBERT_SPIRAL', - options=set(), # Not animatable! - ) - use_progressive_refine: BoolProperty( - name="Progressive Refine", - description="Instead of rendering each tile until it is finished, " - "refine the whole image progressively " - "(this renders somewhat slower, " - "but time can be saved by manually stopping the render when the noise is low enough)", - default=False, - ) bake_type: EnumProperty( name="Bake Type", default='COMBINED', description="Type of pass to bake", items=( - ('COMBINED', "Combined", ""), - ('AO', "Ambient Occlusion", ""), - ('SHADOW', "Shadow", ""), - ('NORMAL', "Normal", ""), - ('UV', "UV", ""), - ('ROUGHNESS', "Roughness", ""), - ('EMIT', "Emit", ""), - ('ENVIRONMENT', "Environment", ""), - ('DIFFUSE', "Diffuse", ""), - ('GLOSSY', "Glossy", ""), - ('TRANSMISSION', "Transmission", ""), + ('COMBINED', "Combined", "", 0), + ('AO', "Ambient Occlusion", "", 1), + ('SHADOW', "Shadow", "", 2), + ('POSITION', "Position", "", 11), + ('NORMAL', "Normal", "", 3), + ('UV', "UV", "", 4), + ('ROUGHNESS', "Roughness", "", 5), + ('EMIT', "Emit", "", 6), + ('ENVIRONMENT', "Environment", "", 7), + ('DIFFUSE', "Diffuse", "", 8), + ('GLOSSY', "Glossy", "", 9), + ('TRANSMISSION', "Transmission", "", 10), ), ) @@ -827,6 +737,18 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): min=0, max=1024, ) + use_auto_tile: BoolProperty( + name="Auto Tiles", + description="Automatically split image into tiles", + default=True, + ) + tile_size: IntProperty( + name="Tile Size", + default=2048, + description="", + min=0, max=16384, + ) + # Various fine-tuning debug flags def _devices_update_callback(self, context): @@ -844,45 +766,13 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): items=enum_bvh_layouts, default='EMBREE', ) - debug_use_cpu_split_kernel: BoolProperty(name="Split Kernel", default=False) debug_use_cuda_adaptive_compile: BoolProperty(name="Adaptive Compile", default=False) - debug_use_cuda_split_kernel: BoolProperty(name="Split Kernel", default=False) - debug_optix_cuda_streams: IntProperty(name="CUDA Streams", default=1, min=1) - debug_optix_curves_api: BoolProperty(name="Native OptiX Curve Primitive", default=False) - - debug_opencl_kernel_type: EnumProperty( - name="OpenCL Kernel Type", - default='DEFAULT', - items=( - ('DEFAULT', "Default", ""), - ('MEGA', "Mega", ""), - ('SPLIT', "Split", ""), - ), - update=CyclesRenderSettings._devices_update_callback - ) - - debug_opencl_device_type: EnumProperty( - name="OpenCL Device Type", - default='ALL', - items=( - ('NONE', "None", ""), - ('ALL', "All", ""), - ('DEFAULT', "Default", ""), - ('CPU', "CPU", ""), - ('GPU', "GPU", ""), - ('ACCELERATOR', "Accelerator", ""), - ), - update=CyclesRenderSettings._devices_update_callback - ) - - debug_use_opencl_debug: BoolProperty(name="Debug OpenCL", default=False) - - debug_opencl_mem_limit: IntProperty( - name="Memory limit", - default=0, - description="Artificial limit on OpenCL memory usage in MB (0 to disable limit)" + debug_use_optix_debug: BoolProperty( + name="OptiX Module Debug", + description="Load OptiX module in debug mode: lower logging verbosity level, enable validations, and lower optimization level", + default=False ) @classmethod @@ -1031,12 +921,6 @@ class CyclesLightSettings(bpy.types.PropertyGroup): description="Light casts shadows", default=True, ) - samples: IntProperty( - name="Samples", - description="Number of light samples to render for each AA sample", - min=1, max=10000, - default=1, - ) max_bounces: IntProperty( name="Max Bounces", description="Maximum number of bounces the light will contribute to the render", @@ -1084,12 +968,6 @@ class CyclesWorldSettings(bpy.types.PropertyGroup): min=4, max=8192, default=1024, ) - samples: IntProperty( - name="Samples", - description="Number of light samples to render for each AA sample", - min=1, max=10000, - default=1, - ) max_bounces: IntProperty( name="Max Bounces", description="Maximum number of bounces the background light will contribute to the render", @@ -1343,91 +1221,25 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup): update=update_render_passes, ) + use_pass_shadow_catcher: BoolProperty( + name="Shadow Catcher", + description="Pass containing shadows and light which is to be multiplied into backdrop", + default=False, + update=update_render_passes, + ) + use_denoising: BoolProperty( name="Use Denoising", description="Denoise the rendered image", default=True, update=update_render_passes, ) - denoising_diffuse_direct: BoolProperty( - name="Diffuse Direct", - description="Denoise the direct diffuse lighting", - default=True, - ) - denoising_diffuse_indirect: BoolProperty( - name="Diffuse Indirect", - description="Denoise the indirect diffuse lighting", - default=True, - ) - denoising_glossy_direct: BoolProperty( - name="Glossy Direct", - description="Denoise the direct glossy lighting", - default=True, - ) - denoising_glossy_indirect: BoolProperty( - name="Glossy Indirect", - description="Denoise the indirect glossy lighting", - default=True, - ) - denoising_transmission_direct: BoolProperty( - name="Transmission Direct", - description="Denoise the direct transmission lighting", - default=True, - ) - denoising_transmission_indirect: BoolProperty( - name="Transmission Indirect", - description="Denoise the indirect transmission lighting", - default=True, - ) - denoising_strength: FloatProperty( - name="Denoising Strength", - description="Controls neighbor pixel weighting for the denoising filter (lower values preserve more detail, but aren't as smooth)", - min=0.0, max=1.0, - default=0.5, - ) - denoising_feature_strength: FloatProperty( - name="Denoising Feature Strength", - description="Controls removal of noisy image feature passes (lower values preserve more detail, but aren't as smooth)", - min=0.0, max=1.0, - default=0.5, - ) - denoising_radius: IntProperty( - name="Denoising Radius", - description="Size of the image area that's used to denoise a pixel (higher values are smoother, but might lose detail and are slower)", - min=1, max=25, - default=8, - subtype="PIXEL", - ) - denoising_relative_pca: BoolProperty( - name="Relative Filter", - description="When removing pixels that don't carry information, use a relative threshold instead of an absolute one (can help to reduce artifacts, but might cause detail loss around edges)", - default=False, - ) denoising_store_passes: BoolProperty( name="Store Denoising Passes", description="Store the denoising feature passes and the noisy image. The passes adapt to the denoiser selected for rendering", default=False, update=update_render_passes, ) - denoising_neighbor_frames: IntProperty( - name="Neighbor Frames", - description="Number of neighboring frames to use for denoising animations (more frames produce smoother results at the cost of performance)", - min=0, max=7, - default=0, - ) - - denoising_optix_input_passes: EnumProperty( - name="Input Passes", - description="Passes used by the denoiser to distinguish noise from shader and geometry detail", - items=enum_denoising_input_passes, - default='RGB_ALBEDO', - ) - denoising_openimagedenoise_input_passes: EnumProperty( - name="Input Passes", - description="Passes used by the denoiser to distinguish noise from shader and geometry detail", - items=enum_denoising_input_passes, - default='RGB_ALBEDO_NORMAL', - ) @classmethod def register(cls): @@ -1454,14 +1266,12 @@ class CyclesPreferences(bpy.types.AddonPreferences): def get_device_types(self, context): import _cycles - has_cuda, has_optix, has_opencl = _cycles.get_device_types() + has_cuda, has_optix = _cycles.get_device_types() list = [('NONE', "None", "Don't use compute device", 0)] if has_cuda: list.append(('CUDA', "CUDA", "Use CUDA for GPU acceleration", 1)) if has_optix: list.append(('OPTIX', "OptiX", "Use OptiX for GPU acceleration", 3)) - if has_opencl: - list.append(('OPENCL', "OpenCL", "Use OpenCL for GPU acceleration", 2)) return list compute_device_type: EnumProperty( @@ -1486,7 +1296,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): def update_device_entries(self, device_list): for device in device_list: - if not device[1] in {'CUDA', 'OPTIX', 'OPENCL', 'CPU'}: + if not device[1] in {'CUDA', 'OPTIX', 'CPU'}: continue # Try to find existing Device entry entry = self.find_existing_device_entry(device) @@ -1520,22 +1330,23 @@ class CyclesPreferences(bpy.types.AddonPreferences): elif entry.type == 'CPU': cpu_devices.append(entry) # Extend all GPU devices with CPU. - if compute_device_type in {'CUDA', 'OPTIX', 'OPENCL'}: + if compute_device_type != 'CPU': devices.extend(cpu_devices) return devices - # For backwards compatibility, only returns CUDA and OpenCL but still - # refreshes all devices. - def get_devices(self, compute_device_type=''): + # Refresh device list. This does not happen automatically on Blender + # startup due to unstable OpenCL implementations that can cause crashes. + def refresh_devices(self): import _cycles # Ensure `self.devices` is not re-allocated when the second call to # get_devices_for_type is made, freeing items from the first list. for device_type in ('CUDA', 'OPTIX', 'OPENCL'): self.update_device_entries(_cycles.available_devices(device_type)) - cuda_devices = self.get_devices_for_type('CUDA') - opencl_devices = self.get_devices_for_type('OPENCL') - return cuda_devices, opencl_devices + # Deprecated: use refresh_devices instead. + def get_devices(self, compute_device_type=''): + self.refresh_devices() + return None def get_num_gpu_devices(self): import _cycles @@ -1601,6 +1412,10 @@ class CyclesView3DShadingSettings(bpy.types.PropertyGroup): items=enum_view3d_shading_render_pass, default='COMBINED', ) + show_active_pixels: BoolProperty( + name="Show Active Pixels", + description="When using adaptive sampling highlight pixels which are being sampled", + ) def register(): diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 47f7b4c6d73..d02627b9936 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -34,6 +34,12 @@ class CYCLES_PT_sampling_presets(PresetPanel, Panel): preset_add_operator = "render.cycles_sampling_preset_add" COMPAT_ENGINES = {'CYCLES'} +class CYCLES_PT_viewport_sampling_presets(PresetPanel, Panel): + bl_label = "Viewport Sampling Presets" + preset_subdir = "cycles/viewport_sampling" + preset_operator = "script.execute_preset" + preset_add_operator = "render.cycles_viewport_sampling_preset_add" + COMPAT_ENGINES = {'CYCLES'} class CYCLES_PT_integrator_presets(PresetPanel, Panel): bl_label = "Integrator Presets" @@ -54,6 +60,15 @@ class CyclesButtonsPanel: return context.engine in cls.COMPAT_ENGINES +class CyclesDebugButtonsPanel(CyclesButtonsPanel): + @classmethod + def poll(cls, context): + prefs = bpy.context.preferences + return (CyclesButtonsPanel.poll(context) + and prefs.experimental.use_cycles_debug + and prefs.view.show_developer_ui) + + # Adapt properties editor panel to display in node editor. We have to # copy the class rather than inherit due to the way bpy registration works. def node_panel(cls): @@ -78,12 +93,6 @@ def use_cpu(context): return (get_device_type(context) == 'NONE' or cscene.device == 'CPU') -def use_opencl(context): - cscene = context.scene.cycles - - return (get_device_type(context) == 'OPENCL' and cscene.device == 'GPU') - - def use_cuda(context): cscene = context.scene.cycles @@ -96,12 +105,6 @@ def use_optix(context): return (get_device_type(context) == 'OPTIX' and cscene.device == 'GPU') -def use_branched_path(context): - cscene = context.scene.cycles - - return (cscene.progressive == 'BRANCHED_PATH' and not use_optix(context)) - - def use_sample_all_lights(context): cscene = context.scene.cycles @@ -115,55 +118,93 @@ def show_device_active(context): return context.preferences.addons[__package__].preferences.has_active_device() -def draw_samples_info(layout, context): - cscene = context.scene.cycles - integrator = cscene.progressive +def get_effective_preview_denoiser(context): + scene = context.scene + cscene = scene.cycles - # Calculate sample values - if integrator == 'PATH': - aa = cscene.samples - if cscene.use_square_samples: - aa = aa * aa - else: - aa = cscene.aa_samples - d = cscene.diffuse_samples - g = cscene.glossy_samples - t = cscene.transmission_samples - ao = cscene.ao_samples - ml = cscene.mesh_light_samples - sss = cscene.subsurface_samples - vol = cscene.volume_samples + if cscene.preview_denoiser != "AUTO": + return cscene.preview_denoiser - if cscene.use_square_samples: - aa = aa * aa - d = d * d - g = g * g - t = t * t - ao = ao * ao - ml = ml * ml - sss = sss * sss - vol = vol * vol + if context.preferences.addons[__package__].preferences.get_devices_for_type('OPTIX'): + return 'OPTIX' + + return 'OIDN' - # Draw interface - # Do not draw for progressive, when Square Samples are disabled - if use_branched_path(context) or (cscene.use_square_samples and integrator == 'PATH'): - col = layout.column(align=True) - col.scale_y = 0.6 - col.label(text="Total Samples:") - col.separator() - if integrator == 'PATH': - col.label(text="%s AA" % aa) - else: - col.label(text="%s AA, %s Diffuse, %s Glossy, %s Transmission" % - (aa, d * aa, g * aa, t * aa)) - col.separator() - col.label(text="%s AO, %s Mesh Light, %s Subsurface, %s Volume" % - (ao * aa, ml * aa, sss * aa, vol * aa)) class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel): bl_label = "Sampling" + def draw(self, context): + pass + + +class CYCLES_RENDER_PT_sampling_viewport(CyclesButtonsPanel, Panel): + bl_label = "Viewport" + bl_parent_id = "CYCLES_RENDER_PT_sampling" + + def draw_header_preset(self, context): + CYCLES_PT_viewport_sampling_presets.draw_panel_header(self.layout) + + def draw(self, context): + layout = self.layout + + scene = context.scene + cscene = scene.cycles + + layout.use_property_split = True + layout.use_property_decorate = False + + heading = layout.column(align=True, heading="Noise Threshold") + row = heading.row(align=True) + row.prop(cscene, "use_preview_adaptive_sampling", text="") + sub = row.row() + sub.active = cscene.use_preview_adaptive_sampling + sub.prop(cscene, "preview_adaptive_threshold", text="") + + if cscene.use_preview_adaptive_sampling: + col = layout.column(align=True) + col.prop(cscene, "preview_samples", text=" Max Samples") + col.prop(cscene, "preview_adaptive_min_samples", text="Min Samples") + else: + layout.prop(cscene, "preview_samples", text="Samples") + + +class CYCLES_RENDER_PT_sampling_viewport_denoise(CyclesButtonsPanel, Panel): + bl_label = "Denoise" + bl_parent_id = 'CYCLES_RENDER_PT_sampling_viewport' + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + scene = context.scene + cscene = scene.cycles + + self.layout.prop(context.scene.cycles, "use_preview_denoising", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + scene = context.scene + cscene = scene.cycles + + col = layout.column() + col.active = cscene.use_preview_denoising + col.prop(cscene, "preview_denoiser", text="Denoiser") + col.prop(cscene, "preview_denoising_input_passes", text="Passes") + + effective_preview_denoiser = get_effective_preview_denoiser(context) + if effective_preview_denoiser == 'OPENIMAGEDENOISE': + col.prop(cscene, "preview_denoising_prefilter", text="Prefilter") + + col.prop(cscene, "preview_denoising_start_sample", text="Start Sample") + + +class CYCLES_RENDER_PT_sampling_render(CyclesButtonsPanel, Panel): + bl_label = "Render" + bl_parent_id = "CYCLES_RENDER_PT_sampling" + def draw_header_preset(self, context): CYCLES_PT_sampling_presets.draw_panel_header(self.layout) @@ -176,64 +217,32 @@ class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False - if not use_optix(context): - layout.prop(cscene, "progressive") - - if not use_branched_path(context): - col = layout.column(align=True) - col.prop(cscene, "samples", text="Render") - col.prop(cscene, "preview_samples", text="Viewport") - else: - col = layout.column(align=True) - col.prop(cscene, "aa_samples", text="Render") - col.prop(cscene, "preview_aa_samples", text="Viewport") - - if not use_branched_path(context): - draw_samples_info(layout, context) - - -class CYCLES_RENDER_PT_sampling_sub_samples(CyclesButtonsPanel, Panel): - bl_label = "Sub Samples" - bl_parent_id = "CYCLES_RENDER_PT_sampling" - - @classmethod - def poll(cls, context): - return use_branched_path(context) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - scene = context.scene - cscene = scene.cycles + heading = layout.column(align=True, heading="Noise Threshold") + row = heading.row(align=True) + row.prop(cscene, "use_adaptive_sampling", text="") + sub = row.row() + sub.active = cscene.use_adaptive_sampling + sub.prop(cscene, "adaptive_threshold", text="") col = layout.column(align=True) - col.prop(cscene, "diffuse_samples", text="Diffuse") - col.prop(cscene, "glossy_samples", text="Glossy") - col.prop(cscene, "transmission_samples", text="Transmission") - col.prop(cscene, "ao_samples", text="AO") - - sub = col.row(align=True) - sub.active = use_sample_all_lights(context) - sub.prop(cscene, "mesh_light_samples", text="Mesh Light") - col.prop(cscene, "subsurface_samples", text="Subsurface") - col.prop(cscene, "volume_samples", text="Volume") - - draw_samples_info(layout, context) + if cscene.use_adaptive_sampling: + col.prop(cscene, "samples", text=" Max Samples") + col.prop(cscene, "adaptive_min_samples", text="Min Samples") + else: + col.prop(cscene, "samples", text="Samples") + col.prop(cscene, "time_limit") -class CYCLES_RENDER_PT_sampling_adaptive(CyclesButtonsPanel, Panel): - bl_label = "Adaptive Sampling" - bl_parent_id = "CYCLES_RENDER_PT_sampling" +class CYCLES_RENDER_PT_sampling_render_denoise(CyclesButtonsPanel, Panel): + bl_label = "Denoise" + bl_parent_id = 'CYCLES_RENDER_PT_sampling_render' bl_options = {'DEFAULT_CLOSED'} def draw_header(self, context): - layout = self.layout scene = context.scene cscene = scene.cycles - layout.prop(cscene, "use_adaptive_sampling", text="") + self.layout.prop(context.scene.cycles, "use_denoising", text="") def draw(self, context): layout = self.layout @@ -243,53 +252,12 @@ class CYCLES_RENDER_PT_sampling_adaptive(CyclesButtonsPanel, Panel): scene = context.scene cscene = scene.cycles - layout.active = cscene.use_adaptive_sampling - - col = layout.column(align=True) - col.prop(cscene, "adaptive_threshold", text="Noise Threshold") - col.prop(cscene, "adaptive_min_samples", text="Min Samples") - - -class CYCLES_RENDER_PT_sampling_denoising(CyclesButtonsPanel, Panel): - bl_label = "Denoising" - bl_parent_id = "CYCLES_RENDER_PT_sampling" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - scene = context.scene - cscene = scene.cycles - - heading = layout.column(align=True, heading="Render") - row = heading.row(align=True) - row.prop(cscene, "use_denoising", text="") - sub = row.row() - - sub.active = cscene.use_denoising - for view_layer in scene.view_layers: - if view_layer.cycles.denoising_store_passes: - sub.active = True - - sub.prop(cscene, "denoiser", text="") - - layout.separator() - - heading = layout.column(align=False, heading="Viewport") - row = heading.row(align=True) - row.prop(cscene, "use_preview_denoising", text="") - sub = row.row() - sub.active = cscene.use_preview_denoising - sub.prop(cscene, "preview_denoiser", text="") - - sub = heading.row(align=True) - sub.active = cscene.use_preview_denoising - sub.prop(cscene, "preview_denoising_start_sample", text="Start Sample") - sub = heading.row(align=True) - sub.active = cscene.use_preview_denoising - sub.prop(cscene, "preview_denoising_input_passes", text="Input Passes") + col = layout.column() + col.active = cscene.use_denoising + col.prop(cscene, "denoiser", text="Denoiser") + col.prop(cscene, "denoising_input_passes", text="Passes") + if cscene.denoiser == 'OPENIMAGEDENOISE': + col.prop(cscene, "denoising_prefilter", text="Prefilter") class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): @@ -313,8 +281,6 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): col.active = not(cscene.use_adaptive_sampling) col.prop(cscene, "sampling_pattern", text="Pattern") - layout.prop(cscene, "use_square_samples") - layout.separator() col = layout.column(align=True) @@ -322,11 +288,6 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): col.prop(cscene, "min_transparent_bounces") col.prop(cscene, "light_sampling_threshold", text="Light Threshold") - if cscene.progressive != 'PATH' and use_branched_path(context): - col = layout.column(align=True) - col.prop(cscene, "sample_all_lights_direct") - col.prop(cscene, "sample_all_lights_indirect") - for view_layer in scene.view_layers: if view_layer.samples > 0: layout.separator() @@ -334,62 +295,6 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): break -class CYCLES_RENDER_PT_sampling_total(CyclesButtonsPanel, Panel): - bl_label = "Total Samples" - bl_parent_id = "CYCLES_RENDER_PT_sampling" - - @classmethod - def poll(cls, context): - scene = context.scene - cscene = scene.cycles - - if cscene.use_square_samples: - return True - - return cscene.progressive != 'PATH' and use_branched_path(context) - - def draw(self, context): - layout = self.layout - cscene = context.scene.cycles - integrator = cscene.progressive - - # Calculate sample values - if integrator == 'PATH': - aa = cscene.samples - if cscene.use_square_samples: - aa = aa * aa - else: - aa = cscene.aa_samples - d = cscene.diffuse_samples - g = cscene.glossy_samples - t = cscene.transmission_samples - ao = cscene.ao_samples - ml = cscene.mesh_light_samples - sss = cscene.subsurface_samples - vol = cscene.volume_samples - - if cscene.use_square_samples: - aa = aa * aa - d = d * d - g = g * g - t = t * t - ao = ao * ao - ml = ml * ml - sss = sss * sss - vol = vol * vol - - col = layout.column(align=True) - col.scale_y = 0.6 - if integrator == 'PATH': - col.label(text="%s AA" % aa) - else: - col.label(text="%s AA, %s Diffuse, %s Glossy, %s Transmission" % - (aa, d * aa, g * aa, t * aa)) - col.separator() - col.label(text="%s AO, %s Mesh Light, %s Subsurface, %s Volume" % - (ao * aa, ml * aa, sss * aa, vol * aa)) - - class CYCLES_RENDER_PT_subdivision(CyclesButtonsPanel, Panel): bl_label = "Subdivision" bl_options = {'DEFAULT_CLOSED'} @@ -548,6 +453,8 @@ class CYCLES_RENDER_PT_light_paths_fast_gi(CyclesButtonsPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False + layout.active = cscene.use_fast_gi + col = layout.column(align=True) col.prop(cscene, "ao_bounces", text="Viewport Bounces") col.prop(cscene, "ao_bounces_render", text="Render Bounces") @@ -716,19 +623,13 @@ class CYCLES_RENDER_PT_performance_tiles(CyclesButtonsPanel, Panel): layout.use_property_decorate = False scene = context.scene - rd = scene.render cscene = scene.cycles col = layout.column() - - sub = col.column(align=True) - sub.prop(rd, "tile_x", text="Tiles X") - sub.prop(rd, "tile_y", text="Y") - col.prop(cscene, "tile_order", text="Order") - + col.prop(cscene, "use_auto_tile") sub = col.column() - sub.active = not rd.use_save_buffers and not cscene.use_adaptive_sampling - sub.prop(cscene, "use_progressive_refine") + sub.active = cscene.use_auto_tile + sub.prop(cscene, "tile_size") class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Panel): @@ -778,7 +679,6 @@ class CYCLES_RENDER_PT_performance_final_render(CyclesButtonsPanel, Panel): col = layout.column() - col.prop(rd, "use_save_buffers") col.prop(rd, "use_persistent_data", text="Persistent Data") @@ -797,7 +697,6 @@ class CYCLES_RENDER_PT_performance_viewport(CyclesButtonsPanel, Panel): col = layout.column() col.prop(rd, "preview_pixel_size", text="Pixel Size") - col.prop(cscene, "preview_start_resolution", text="Start Pixels") class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel): @@ -818,7 +717,6 @@ class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel): col = layout.column(heading="Include") col.prop(view_layer, "use_sky", text="Environment") - col.prop(view_layer, "use_ao", text="Ambient Occlusion") col.prop(view_layer, "use_solid", text="Surfaces") col.prop(view_layer, "use_strand", text="Hair") col.prop(view_layer, "use_volumes", text="Volumes") @@ -827,6 +725,9 @@ class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel): sub = col.row() sub.prop(view_layer, "use_motion_blur", text="Motion Blur") sub.active = rd.use_motion_blur + sub = col.row() + sub.prop(view_layer.cycles, 'use_denoising', text='Denoising') + sub.active = scene.cycles.use_denoising class CYCLES_RENDER_PT_override(CyclesButtonsPanel, Panel): @@ -872,6 +773,7 @@ class CYCLES_RENDER_PT_passes_data(CyclesButtonsPanel, Panel): col.prop(view_layer, "use_pass_combined") col.prop(view_layer, "use_pass_z") col.prop(view_layer, "use_pass_mist") + col.prop(view_layer, "use_pass_position") col.prop(view_layer, "use_pass_normal") sub = col.column() sub.active = not rd.use_motion_blur @@ -928,6 +830,7 @@ class CYCLES_RENDER_PT_passes_light(CyclesButtonsPanel, Panel): col.prop(view_layer, "use_pass_environment") col.prop(view_layer, "use_pass_shadow") col.prop(view_layer, "use_pass_ambient_occlusion", text="Ambient Occlusion") + col.prop(cycles_view_layer, "use_pass_shadow_catcher") class CYCLES_RENDER_PT_passes_crypto(CyclesButtonsPanel, ViewLayerCryptomattePanel, Panel): @@ -942,70 +845,6 @@ class CYCLES_RENDER_PT_passes_aov(CyclesButtonsPanel, ViewLayerAOVPanel): bl_parent_id = "CYCLES_RENDER_PT_passes" -class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel): - bl_label = "Denoising" - bl_context = "view_layer" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - cscene = context.scene.cycles - return CyclesButtonsPanel.poll(context) and cscene.use_denoising - - def draw_header(self, context): - scene = context.scene - view_layer = context.view_layer - cycles_view_layer = view_layer.cycles - - layout = self.layout - layout.prop(cycles_view_layer, "use_denoising", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - scene = context.scene - view_layer = context.view_layer - cycles_view_layer = view_layer.cycles - denoiser = scene.cycles.denoiser - - layout.active = denoiser != 'NONE' and cycles_view_layer.use_denoising - - col = layout.column() - - if denoiser == 'OPTIX': - col.prop(cycles_view_layer, "denoising_optix_input_passes") - return - elif denoiser == 'OPENIMAGEDENOISE': - col.prop(cycles_view_layer, "denoising_openimagedenoise_input_passes") - return - - col.prop(cycles_view_layer, "denoising_radius", text="Radius") - - col = layout.column() - col.prop(cycles_view_layer, "denoising_strength", slider=True, text="Strength") - col.prop(cycles_view_layer, "denoising_feature_strength", slider=True, text="Feature Strength") - col.prop(cycles_view_layer, "denoising_relative_pca") - - layout.separator() - - col = layout.column() - col.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes - - row = col.row(heading="Diffuse", align=True) - row.prop(cycles_view_layer, "denoising_diffuse_direct", text="Direct", toggle=True) - row.prop(cycles_view_layer, "denoising_diffuse_indirect", text="Indirect", toggle=True) - - row = col.row(heading="Glossy", align=True) - row.prop(cycles_view_layer, "denoising_glossy_direct", text="Direct", toggle=True) - row.prop(cycles_view_layer, "denoising_glossy_indirect", text="Indirect", toggle=True) - - row = col.row(heading="Transmission", align=True) - row.prop(cycles_view_layer, "denoising_transmission_direct", text="Direct", toggle=True) - row.prop(cycles_view_layer, "denoising_transmission_indirect", text="Indirect", toggle=True) - - class CYCLES_PT_post_processing(CyclesButtonsPanel, Panel): bl_label = "Post Processing" bl_options = {'DEFAULT_CLOSED'} @@ -1417,10 +1256,6 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel): if not (light.type == 'AREA' and clamp.is_portal): sub = col.column() - if use_branched_path(context): - subsub = sub.row(align=True) - subsub.active = use_sample_all_lights(context) - subsub.prop(clamp, "samples") sub.prop(clamp, "max_bounces") sub = col.column(align=True) @@ -1526,34 +1361,6 @@ class CYCLES_WORLD_PT_volume(CyclesButtonsPanel, Panel): panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Volume') -class CYCLES_WORLD_PT_ambient_occlusion(CyclesButtonsPanel, Panel): - bl_label = "Ambient Occlusion" - bl_context = "world" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - return context.world and CyclesButtonsPanel.poll(context) - - def draw_header(self, context): - light = context.world.light_settings - self.layout.prop(light, "use_ambient_occlusion", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - light = context.world.light_settings - scene = context.scene - - col = layout.column() - sub = col.column() - sub.active = light.use_ambient_occlusion or scene.render.use_simplify - sub.prop(light, "ao_factor", text="Factor") - col.prop(light, "distance", text="Distance") - - class CYCLES_WORLD_PT_mist(CyclesButtonsPanel, Panel): bl_label = "Mist Pass" bl_context = "world" @@ -1650,10 +1457,6 @@ class CYCLES_WORLD_PT_settings_surface(CyclesButtonsPanel, Panel): subsub = sub.row(align=True) subsub.active = cworld.sampling_method == 'MANUAL' subsub.prop(cworld, "sample_map_resolution") - if use_branched_path(context): - subsub = sub.column(align=True) - subsub.active = use_sample_all_lights(context) - subsub.prop(cworld, "samples") sub.prop(cworld, "max_bounces") @@ -1677,8 +1480,7 @@ class CYCLES_WORLD_PT_settings_volume(CyclesButtonsPanel, Panel): col = layout.column() sub = col.column() - sub.active = use_cpu(context) - sub.prop(cworld, "volume_sampling", text="Sampling") + col.prop(cworld, "volume_sampling", text="Sampling") col.prop(cworld, "volume_interpolation", text="Interpolation") col.prop(cworld, "homogeneous_volume", text="Homogeneous") sub = col.column() @@ -1817,8 +1619,7 @@ class CYCLES_MATERIAL_PT_settings_volume(CyclesButtonsPanel, Panel): col = layout.column() sub = col.column() - sub.active = use_cpu(context) - sub.prop(cmat, "volume_sampling", text="Sampling") + col.prop(cmat, "volume_sampling", text="Sampling") col.prop(cmat, "volume_interpolation", text="Interpolation") col.prop(cmat, "homogeneous_volume", text="Homogeneous") sub = col.column() @@ -1845,9 +1646,6 @@ class CYCLES_RENDER_PT_bake(CyclesButtonsPanel, Panel): cbk = scene.render.bake rd = scene.render - if use_optix(context): - layout.label(text="Baking is performed using CUDA instead of OptiX", icon='INFO') - if rd.use_bake_multires: layout.operator("object.bake_image", icon='RENDER_STILL') layout.prop(rd, "use_bake_multires") @@ -1905,7 +1703,6 @@ class CYCLES_RENDER_PT_bake_influence(CyclesButtonsPanel, Panel): col.prop(cbk, "use_pass_diffuse") col.prop(cbk, "use_pass_glossy") col.prop(cbk, "use_pass_transmission") - col.prop(cbk, "use_pass_ambient_occlusion") col.prop(cbk, "use_pass_emit") elif cscene.bake_type in {'DIFFUSE', 'GLOSSY', 'TRANSMISSION'}: @@ -1989,19 +1786,12 @@ class CYCLES_RENDER_PT_bake_output(CyclesButtonsPanel, Panel): layout.prop(cbk, "use_clear", text="Clear Image") -class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel): +class CYCLES_RENDER_PT_debug(CyclesDebugButtonsPanel, Panel): bl_label = "Debug" bl_context = "render" bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'CYCLES'} - @classmethod - def poll(cls, context): - prefs = bpy.context.preferences - return (CyclesButtonsPanel.poll(context) - and prefs.experimental.use_cycles_debug - and prefs.view.show_developer_ui) - def draw(self, context): layout = self.layout @@ -2018,29 +1808,18 @@ class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel): row.prop(cscene, "debug_use_cpu_avx", toggle=True) row.prop(cscene, "debug_use_cpu_avx2", toggle=True) col.prop(cscene, "debug_bvh_layout") - col.prop(cscene, "debug_use_cpu_split_kernel") col.separator() col = layout.column() col.label(text="CUDA Flags:") col.prop(cscene, "debug_use_cuda_adaptive_compile") - col.prop(cscene, "debug_use_cuda_split_kernel") col.separator() col = layout.column() col.label(text="OptiX Flags:") - col.prop(cscene, "debug_optix_cuda_streams") - col.prop(cscene, "debug_optix_curves_api") - - col.separator() - - col = layout.column() - col.label(text="OpenCL Flags:") - col.prop(cscene, "debug_opencl_device_type", text="Device") - col.prop(cscene, "debug_use_opencl_debug", text="Debug") - col.prop(cscene, "debug_opencl_mem_limit") + col.prop(cscene, "debug_use_optix_debug") col.separator() @@ -2141,20 +1920,22 @@ class CYCLES_RENDER_PT_simplify_culling(CyclesButtonsPanel, Panel): sub.prop(cscene, "distance_cull_margin", text="") -class CYCLES_VIEW3D_PT_shading_render_pass(Panel): +class CyclesShadingButtonsPanel(CyclesButtonsPanel): bl_space_type = 'VIEW_3D' bl_region_type = 'HEADER' - bl_label = "Render Pass" bl_parent_id = 'VIEW3D_PT_shading' - COMPAT_ENGINES = {'CYCLES'} @classmethod def poll(cls, context): return ( - context.engine in cls.COMPAT_ENGINES and + CyclesButtonsPanel.poll(context) and context.space_data.shading.type == 'RENDERED' ) + +class CYCLES_VIEW3D_PT_shading_render_pass(CyclesShadingButtonsPanel, Panel): + bl_label = "Render Pass" + def draw(self, context): shading = context.space_data.shading @@ -2162,6 +1943,26 @@ class CYCLES_VIEW3D_PT_shading_render_pass(Panel): layout.prop(shading.cycles, "render_pass", text="") +class CYCLES_VIEW3D_PT_shading_debug(CyclesDebugButtonsPanel, + CyclesShadingButtonsPanel, + Panel): + bl_label = "Debug" + + @classmethod + def poll(cls, context): + return ( + CyclesDebugButtonsPanel.poll(context) and + CyclesShadingButtonsPanel.poll(context) + ) + + def draw(self, context): + shading = context.space_data.shading + + layout = self.layout + layout.active = context.scene.cycles.use_preview_adaptive_sampling + layout.prop(shading.cycles, "show_active_pixels") + + class CYCLES_VIEW3D_PT_shading_lighting(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'HEADER' @@ -2275,11 +2076,13 @@ def get_panels(): classes = ( CYCLES_PT_sampling_presets, + CYCLES_PT_viewport_sampling_presets, CYCLES_PT_integrator_presets, CYCLES_RENDER_PT_sampling, - CYCLES_RENDER_PT_sampling_sub_samples, - CYCLES_RENDER_PT_sampling_adaptive, - CYCLES_RENDER_PT_sampling_denoising, + CYCLES_RENDER_PT_sampling_viewport, + CYCLES_RENDER_PT_sampling_viewport_denoise, + CYCLES_RENDER_PT_sampling_render, + CYCLES_RENDER_PT_sampling_render_denoise, CYCLES_RENDER_PT_sampling_advanced, CYCLES_RENDER_PT_light_paths, CYCLES_RENDER_PT_light_paths_max_bounces, @@ -2296,6 +2099,7 @@ classes = ( CYCLES_VIEW3D_PT_simplify_greasepencil, CYCLES_VIEW3D_PT_shading_lighting, CYCLES_VIEW3D_PT_shading_render_pass, + CYCLES_VIEW3D_PT_shading_debug, CYCLES_RENDER_PT_motion_blur, CYCLES_RENDER_PT_motion_blur_curve, CYCLES_RENDER_PT_film, @@ -2314,7 +2118,6 @@ classes = ( CYCLES_RENDER_PT_passes_aov, CYCLES_RENDER_PT_filter, CYCLES_RENDER_PT_override, - CYCLES_RENDER_PT_denoising, CYCLES_PT_post_processing, CYCLES_CAMERA_PT_dof, CYCLES_CAMERA_PT_dof_aperture, @@ -2333,7 +2136,6 @@ classes = ( CYCLES_WORLD_PT_preview, CYCLES_WORLD_PT_surface, CYCLES_WORLD_PT_volume, - CYCLES_WORLD_PT_ambient_occlusion, CYCLES_WORLD_PT_mist, CYCLES_WORLD_PT_ray_visibility, CYCLES_WORLD_PT_settings, diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py index 827f84b9873..57da7d7995c 100644 --- a/intern/cycles/blender/addon/version_update.py +++ b/intern/cycles/blender/addon/version_update.py @@ -109,7 +109,7 @@ def do_versions(self): library_versions.setdefault(library.version, []).append(library) # Do versioning per library, since they might have different versions. - max_need_versioning = (2, 93, 7) + max_need_versioning = (3, 0, 25) for version, libraries in library_versions.items(): if version > max_need_versioning: continue @@ -166,10 +166,6 @@ def do_versions(self): if not cscene.is_property_set("filter_type"): cscene.pixel_filter_type = 'GAUSSIAN' - # Tile Order - if not cscene.is_property_set("tile_order"): - cscene.tile_order = 'CENTER' - if version <= (2, 76, 10): cscene = scene.cycles if cscene.is_property_set("filter_type"): @@ -186,10 +182,6 @@ def do_versions(self): if version <= (2, 79, 0): cscene = scene.cycles # Default changes - if not cscene.is_property_set("aa_samples"): - cscene.aa_samples = 4 - if not cscene.is_property_set("preview_aa_samples"): - cscene.preview_aa_samples = 4 if not cscene.is_property_set("blur_glossy"): cscene.blur_glossy = 0.0 if not cscene.is_property_set("sample_clamp_indirect"): @@ -203,7 +195,6 @@ def do_versions(self): view_layer.use_pass_cryptomatte_material = cview_layer.get("use_pass_crypto_material", False) view_layer.use_pass_cryptomatte_asset = cview_layer.get("use_pass_crypto_asset", False) view_layer.pass_cryptomatte_depth = cview_layer.get("pass_crypto_depth", 6) - view_layer.use_pass_cryptomatte_accurate = cview_layer.get("pass_crypto_accurate", True) if version <= (2, 93, 7): if scene.render.engine == 'CYCLES': @@ -229,6 +220,35 @@ def do_versions(self): cscene.ao_bounces = 1 cscene.ao_bounces_render = 1 + if version <= (3, 0, 25): + cscene = scene.cycles + + # Default changes. + if not cscene.is_property_set("samples"): + cscene.samples = 128 + if not cscene.is_property_set("preview_samples"): + cscene.preview_samples = 32 + if not cscene.is_property_set("use_adaptive_sampling"): + cscene.use_adaptive_sampling = False + cscene.use_preview_adaptive_sampling = False + if not cscene.is_property_set("use_denoising"): + cscene.use_denoising = False + if not cscene.is_property_set("use_preview_denoising"): + cscene.use_preview_denoising = False + if not cscene.is_property_set("sampling_pattern"): + cscene.sampling_pattern = 'PROGRESSIVE_MUTI_JITTER' + + # Removal of square samples. + cscene = scene.cycles + use_square_samples = cscene.get("use_square_samples", False) + + if use_square_samples: + cscene.samples *= cscene.samples + cscene.preview_samples *= cscene.preview_samples + for layer in scene.view_layers: + layer.samples *= layer.samples + cscene["use_square_samples"] = False + # Lamps for light in bpy.data.lights: if light.library not in libraries: @@ -249,10 +269,6 @@ def do_versions(self): if version <= (2, 76, 9): cworld = world.cycles - # World MIS Samples - if not cworld.is_property_set("samples"): - cworld.samples = 4 - # World MIS Resolution if not cworld.is_property_set("sample_map_resolution"): cworld.sample_map_resolution = 256 diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index 6954c5c2f26..4e8df5a99a6 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -894,12 +894,8 @@ void BlenderSync::sync_view(BL::SpaceView3D &b_v3d, } } -BufferParams BlenderSync::get_buffer_params(BL::SpaceView3D &b_v3d, - BL::RegionView3D &b_rv3d, - Camera *cam, - int width, - int height, - const bool use_denoiser) +BufferParams BlenderSync::get_buffer_params( + BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, int height) { BufferParams params; bool use_border = false; @@ -931,11 +927,6 @@ BufferParams BlenderSync::get_buffer_params(BL::SpaceView3D &b_v3d, params.height = height; } - PassType display_pass = update_viewport_display_passes(b_v3d, params.passes); - - /* Can only denoise the combined image pass */ - params.denoising_data_pass = display_pass == PASS_COMBINED && use_denoiser; - return params; } diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/blender_device.cpp index d51b31de638..ce1770f18a3 100644 --- a/intern/cycles/blender/blender_device.cpp +++ b/intern/cycles/blender/blender_device.cpp @@ -25,7 +25,6 @@ CCL_NAMESPACE_BEGIN enum ComputeDevice { COMPUTE_DEVICE_CPU = 0, COMPUTE_DEVICE_CUDA = 1, - COMPUTE_DEVICE_OPENCL = 2, COMPUTE_DEVICE_OPTIX = 3, COMPUTE_DEVICE_NUM @@ -68,13 +67,6 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen device = Device::get_multi_device(devices, threads, background); } } - else if (get_enum(cscene, "device") == 2) { - /* Find network device. */ - vector devices = Device::available_devices(DEVICE_MASK_NETWORK); - if (!devices.empty()) { - device = devices.front(); - } - } else if (get_enum(cscene, "device") == 1) { /* Test if we are using GPU devices. */ ComputeDevice compute_device = (ComputeDevice)get_enum( @@ -89,9 +81,6 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen else if (compute_device == COMPUTE_DEVICE_OPTIX) { mask |= DEVICE_MASK_OPTIX; } - else if (compute_device == COMPUTE_DEVICE_OPENCL) { - mask |= DEVICE_MASK_OPENCL; - } vector devices = Device::available_devices(mask); /* Match device preferences and available devices. */ diff --git a/intern/cycles/blender/blender_gpu_display.cpp b/intern/cycles/blender/blender_gpu_display.cpp new file mode 100644 index 00000000000..a79232af71f --- /dev/null +++ b/intern/cycles/blender/blender_gpu_display.cpp @@ -0,0 +1,761 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "blender/blender_gpu_display.h" + +#include "device/device.h" +#include "util/util_logging.h" +#include "util/util_opengl.h" + +extern "C" { +struct RenderEngine; + +bool RE_engine_has_render_context(struct RenderEngine *engine); +void RE_engine_render_context_enable(struct RenderEngine *engine); +void RE_engine_render_context_disable(struct RenderEngine *engine); + +bool DRW_opengl_context_release(); +void DRW_opengl_context_activate(bool drw_state); + +void *WM_opengl_context_create(); +void WM_opengl_context_activate(void *gl_context); +void WM_opengl_context_dispose(void *gl_context); +void WM_opengl_context_release(void *context); +} + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * BlenderDisplayShader. + */ + +unique_ptr BlenderDisplayShader::create(BL::RenderEngine &b_engine, + BL::Scene &b_scene) +{ + if (b_engine.support_display_space_shader(b_scene)) { + return make_unique(b_engine, b_scene); + } + + return make_unique(); +} + +int BlenderDisplayShader::get_position_attrib_location() +{ + if (position_attribute_location_ == -1) { + const uint shader_program = get_shader_program(); + position_attribute_location_ = glGetAttribLocation(shader_program, position_attribute_name); + } + return position_attribute_location_; +} + +int BlenderDisplayShader::get_tex_coord_attrib_location() +{ + if (tex_coord_attribute_location_ == -1) { + const uint shader_program = get_shader_program(); + tex_coord_attribute_location_ = glGetAttribLocation(shader_program, tex_coord_attribute_name); + } + return tex_coord_attribute_location_; +} + +/* -------------------------------------------------------------------- + * BlenderFallbackDisplayShader. + */ + +/* TODO move shaders to standalone .glsl file. */ +static const char *FALLBACK_VERTEX_SHADER = + "#version 330\n" + "uniform vec2 fullscreen;\n" + "in vec2 texCoord;\n" + "in vec2 pos;\n" + "out vec2 texCoord_interp;\n" + "\n" + "vec2 normalize_coordinates()\n" + "{\n" + " return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n" + "}\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n" + " texCoord_interp = texCoord;\n" + "}\n\0"; + +static const char *FALLBACK_FRAGMENT_SHADER = + "#version 330\n" + "uniform sampler2D image_texture;\n" + "in vec2 texCoord_interp;\n" + "out vec4 fragColor;\n" + "\n" + "void main()\n" + "{\n" + " fragColor = texture(image_texture, texCoord_interp);\n" + "}\n\0"; + +static void shader_print_errors(const char *task, const char *log, const char *code) +{ + LOG(ERROR) << "Shader: " << task << " error:"; + LOG(ERROR) << "===== shader string ===="; + + stringstream stream(code); + string partial; + + int line = 1; + while (getline(stream, partial, '\n')) { + if (line < 10) { + LOG(ERROR) << " " << line << " " << partial; + } + else { + LOG(ERROR) << line << " " << partial; + } + line++; + } + LOG(ERROR) << log; +} + +static int compile_fallback_shader(void) +{ + const struct Shader { + const char *source; + const GLenum type; + } shaders[2] = {{FALLBACK_VERTEX_SHADER, GL_VERTEX_SHADER}, + {FALLBACK_FRAGMENT_SHADER, GL_FRAGMENT_SHADER}}; + + const GLuint program = glCreateProgram(); + + for (int i = 0; i < 2; i++) { + const GLuint shader = glCreateShader(shaders[i].type); + + string source_str = shaders[i].source; + const char *c_str = source_str.c_str(); + + glShaderSource(shader, 1, &c_str, NULL); + glCompileShader(shader); + + GLint compile_status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); + + if (!compile_status) { + GLchar log[5000]; + GLsizei length = 0; + glGetShaderInfoLog(shader, sizeof(log), &length, log); + shader_print_errors("compile", log, c_str); + return 0; + } + + glAttachShader(program, shader); + } + + /* Link output. */ + glBindFragDataLocation(program, 0, "fragColor"); + + /* Link and error check. */ + glLinkProgram(program); + + /* TODO(sergey): Find a way to nicely de-duplicate the error checking. */ + GLint link_status; + glGetProgramiv(program, GL_LINK_STATUS, &link_status); + if (!link_status) { + GLchar log[5000]; + GLsizei length = 0; + /* TODO(sergey): Is it really program passed to glGetShaderInfoLog? */ + glGetShaderInfoLog(program, sizeof(log), &length, log); + shader_print_errors("linking", log, FALLBACK_VERTEX_SHADER); + shader_print_errors("linking", log, FALLBACK_FRAGMENT_SHADER); + return 0; + } + + return program; +} + +void BlenderFallbackDisplayShader::bind(int width, int height) +{ + create_shader_if_needed(); + + if (!shader_program_) { + return; + } + + glUseProgram(shader_program_); + glUniform1i(image_texture_location_, 0); + glUniform2f(fullscreen_location_, width, height); +} + +void BlenderFallbackDisplayShader::unbind() +{ +} + +uint BlenderFallbackDisplayShader::get_shader_program() +{ + return shader_program_; +} + +void BlenderFallbackDisplayShader::create_shader_if_needed() +{ + if (shader_program_ || shader_compile_attempted_) { + return; + } + + shader_compile_attempted_ = true; + + shader_program_ = compile_fallback_shader(); + if (!shader_program_) { + return; + } + + glUseProgram(shader_program_); + + image_texture_location_ = glGetUniformLocation(shader_program_, "image_texture"); + if (image_texture_location_ < 0) { + LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform."; + destroy_shader(); + return; + } + + fullscreen_location_ = glGetUniformLocation(shader_program_, "fullscreen"); + if (fullscreen_location_ < 0) { + LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform."; + destroy_shader(); + return; + } +} + +void BlenderFallbackDisplayShader::destroy_shader() +{ + glDeleteProgram(shader_program_); + shader_program_ = 0; +} + +/* -------------------------------------------------------------------- + * BlenderDisplaySpaceShader. + */ + +BlenderDisplaySpaceShader::BlenderDisplaySpaceShader(BL::RenderEngine &b_engine, + BL::Scene &b_scene) + : b_engine_(b_engine), b_scene_(b_scene) +{ + DCHECK(b_engine_.support_display_space_shader(b_scene_)); +} + +void BlenderDisplaySpaceShader::bind(int /*width*/, int /*height*/) +{ + b_engine_.bind_display_space_shader(b_scene_); +} + +void BlenderDisplaySpaceShader::unbind() +{ + b_engine_.unbind_display_space_shader(); +} + +uint BlenderDisplaySpaceShader::get_shader_program() +{ + if (!shader_program_) { + glGetIntegerv(GL_CURRENT_PROGRAM, reinterpret_cast(&shader_program_)); + } + + if (!shader_program_) { + LOG(ERROR) << "Error retrieving shader program for display space shader."; + } + + return shader_program_; +} + +/* -------------------------------------------------------------------- + * BlenderGPUDisplay. + */ + +BlenderGPUDisplay::BlenderGPUDisplay(BL::RenderEngine &b_engine, BL::Scene &b_scene) + : b_engine_(b_engine), display_shader_(BlenderDisplayShader::create(b_engine, b_scene)) +{ + /* Create context while on the main thread. */ + gl_context_create(); +} + +BlenderGPUDisplay::~BlenderGPUDisplay() +{ + gl_resources_destroy(); +} + +/* -------------------------------------------------------------------- + * Update procedure. + */ + +bool BlenderGPUDisplay::do_update_begin(const GPUDisplayParams ¶ms, + int texture_width, + int texture_height) +{ + /* Note that it's the responsibility of BlenderGPUDisplay to ensure updating and drawing + * the texture does not happen at the same time. This is achieved indirectly. + * + * When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock. + * This same lock is also held when do_draw() is called, which together ensure mutual + * exclusion. + * + * This locking is not performed at the GPU display level, because that would cause lock + * inversion. */ + if (!gl_context_enable()) { + return false; + } + + if (gl_render_sync_) { + glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED); + } + + if (!gl_texture_resources_ensure()) { + gl_context_disable(); + return false; + } + + /* Update texture dimensions if needed. */ + if (texture_.width != texture_width || texture_.height != texture_height) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA16F, texture_width, texture_height, 0, GL_RGBA, GL_HALF_FLOAT, 0); + texture_.width = texture_width; + texture_.height = texture_height; + glBindTexture(GL_TEXTURE_2D, 0); + + /* Texture did change, and no pixel storage was provided. Tag for an explicit zeroing out to + * avoid undefined content. */ + texture_.need_clear = true; + } + + /* Update PBO dimensions if needed. + * + * NOTE: Allocate the PBO for the the size which will fit the final render resolution (as in, + * at a resolution divider 1. This was we don't need to recreate graphics interoperability + * objects which are costly and which are tied to the specific underlying buffer size. + * The downside of this approach is that when graphics interopeability is not used we are sending + * too much data to GPU when resolution divider is not 1. */ + /* TODO(sergey): Investigate whether keeping the PBO exact size of the texute makes non-interop + * mode faster. */ + const int buffer_width = params.full_size.x; + const int buffer_height = params.full_size.y; + if (texture_.buffer_width != buffer_width || texture_.buffer_height != buffer_height) { + const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height; + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, 0, GL_DYNAMIC_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + texture_.buffer_width = buffer_width; + texture_.buffer_height = buffer_height; + } + + /* New content will be provided to the texture in one way or another, so mark this in a + * centralized place. */ + texture_.need_update = true; + + return true; +} + +void BlenderGPUDisplay::do_update_end() +{ + gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + + gl_context_disable(); +} + +/* -------------------------------------------------------------------- + * Texture update from CPU buffer. + */ + +void BlenderGPUDisplay::do_copy_pixels_to_texture( + const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height) +{ + /* This call copies pixels to a Pixel Buffer Object (PBO) which is much cheaper from CPU time + * point of view than to copy data directly to the OpenGL texture. + * + * The possible downside of this approach is that it might require a higher peak memory when + * doing partial updates of the texture (although, in practice even partial updates might peak + * with a full-frame buffer stored on the CPU if the GPU is currently occupied). */ + + half4 *mapped_rgba_pixels = map_texture_buffer(); + if (!mapped_rgba_pixels) { + return; + } + + if (texture_x == 0 && texture_y == 0 && pixels_width == texture_.width && + pixels_height == texture_.height) { + const size_t size_in_bytes = sizeof(half4) * texture_.width * texture_.height; + memcpy(mapped_rgba_pixels, rgba_pixels, size_in_bytes); + } + else { + const half4 *rgba_row = rgba_pixels; + half4 *mapped_rgba_row = mapped_rgba_pixels + texture_y * texture_.width + texture_x; + for (int y = 0; y < pixels_height; + ++y, rgba_row += pixels_width, mapped_rgba_row += texture_.width) { + memcpy(mapped_rgba_row, rgba_row, sizeof(half4) * pixels_width); + } + } + + unmap_texture_buffer(); +} + +/* -------------------------------------------------------------------- + * Texture buffer mapping. + */ + +half4 *BlenderGPUDisplay::do_map_texture_buffer() +{ + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + + half4 *mapped_rgba_pixels = reinterpret_cast( + glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); + if (!mapped_rgba_pixels) { + LOG(ERROR) << "Error mapping BlenderGPUDisplay pixel buffer object."; + } + + if (texture_.need_clear) { + const int64_t texture_width = texture_.width; + const int64_t texture_height = texture_.height; + memset(reinterpret_cast(mapped_rgba_pixels), + 0, + texture_width * texture_height * sizeof(half4)); + texture_.need_clear = false; + } + + return mapped_rgba_pixels; +} + +void BlenderGPUDisplay::do_unmap_texture_buffer() +{ + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + +/* -------------------------------------------------------------------- + * Graphics interoperability. + */ + +DeviceGraphicsInteropDestination BlenderGPUDisplay::do_graphics_interop_get() +{ + DeviceGraphicsInteropDestination interop_dst; + + interop_dst.buffer_width = texture_.buffer_width; + interop_dst.buffer_height = texture_.buffer_height; + interop_dst.opengl_pbo_id = texture_.gl_pbo_id; + + interop_dst.need_clear = texture_.need_clear; + texture_.need_clear = false; + + return interop_dst; +} + +void BlenderGPUDisplay::graphics_interop_activate() +{ + gl_context_enable(); +} + +void BlenderGPUDisplay::graphics_interop_deactivate() +{ + gl_context_disable(); +} + +/* -------------------------------------------------------------------- + * Drawing. + */ + +void BlenderGPUDisplay::clear() +{ + texture_.need_clear = true; +} + +void BlenderGPUDisplay::do_draw(const GPUDisplayParams ¶ms) +{ + /* See do_update_begin() for why no locking is required here. */ + const bool transparent = true; // TODO(sergey): Derive this from Film. + + if (texture_.need_clear) { + /* Texture is requested to be cleared and was not yet cleared. + * Do early return which should be equivalent of drawing all-zero texture. */ + return; + } + + if (!gl_draw_resources_ensure()) { + return; + } + + if (use_gl_context_) { + gl_context_mutex_.lock(); + } + + if (gl_upload_sync_) { + glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); + } + + if (transparent) { + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + + display_shader_->bind(params.full_size.x, params.full_size.y); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); + + texture_update_if_needed(); + vertex_buffer_update(params); + + /* TODO(sergey): Does it make sense/possible to cache/reuse the VAO? */ + GLuint vertex_array_object; + glGenVertexArrays(1, &vertex_array_object); + glBindVertexArray(vertex_array_object); + + const int texcoord_attribute = display_shader_->get_tex_coord_attrib_location(); + const int position_attribute = display_shader_->get_position_attrib_location(); + + glEnableVertexAttribArray(texcoord_attribute); + glEnableVertexAttribArray(position_attribute); + + glVertexAttribPointer( + texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0); + glVertexAttribPointer(position_attribute, + 2, + GL_FLOAT, + GL_FALSE, + 4 * sizeof(float), + (const GLvoid *)(sizeof(float) * 2)); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + glDeleteVertexArrays(1, &vertex_array_object); + + display_shader_->unbind(); + + if (transparent) { + glDisable(GL_BLEND); + } + + gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + + if (use_gl_context_) { + gl_context_mutex_.unlock(); + } +} + +void BlenderGPUDisplay::gl_context_create() +{ + /* When rendering in viewport there is no render context available via engine. + * Check whether own context is to be created here. + * + * NOTE: If the `b_engine_`'s context is not available, we are expected to be on a main thread + * here. */ + use_gl_context_ = !RE_engine_has_render_context( + reinterpret_cast(b_engine_.ptr.data)); + + if (use_gl_context_) { + const bool drw_state = DRW_opengl_context_release(); + gl_context_ = WM_opengl_context_create(); + if (gl_context_) { + /* On Windows an old context is restored after creation, and subsequent release of context + * generates a Win32 error. Harmless for users, but annoying to have possible misleading + * error prints in the console. */ +#ifndef _WIN32 + WM_opengl_context_release(gl_context_); +#endif + } + else { + LOG(ERROR) << "Error creating OpenGL context."; + } + + DRW_opengl_context_activate(drw_state); + } +} + +bool BlenderGPUDisplay::gl_context_enable() +{ + if (use_gl_context_) { + if (!gl_context_) { + return false; + } + gl_context_mutex_.lock(); + WM_opengl_context_activate(gl_context_); + return true; + } + + RE_engine_render_context_enable(reinterpret_cast(b_engine_.ptr.data)); + return true; +} + +void BlenderGPUDisplay::gl_context_disable() +{ + if (use_gl_context_) { + if (gl_context_) { + WM_opengl_context_release(gl_context_); + gl_context_mutex_.unlock(); + } + return; + } + + RE_engine_render_context_disable(reinterpret_cast(b_engine_.ptr.data)); +} + +void BlenderGPUDisplay::gl_context_dispose() +{ + if (gl_context_) { + const bool drw_state = DRW_opengl_context_release(); + + WM_opengl_context_activate(gl_context_); + WM_opengl_context_dispose(gl_context_); + + DRW_opengl_context_activate(drw_state); + } +} + +bool BlenderGPUDisplay::gl_draw_resources_ensure() +{ + if (!texture_.gl_id) { + /* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can + * can not continue. Note that this is not an unrecoverable error, so once the texture is known + * we will come back here and create all the GPU resources needed for draw. */ + return false; + } + + if (gl_draw_resource_creation_attempted_) { + return gl_draw_resources_created_; + } + gl_draw_resource_creation_attempted_ = true; + + if (!vertex_buffer_) { + glGenBuffers(1, &vertex_buffer_); + if (!vertex_buffer_) { + LOG(ERROR) << "Error creating vertex buffer."; + return false; + } + } + + gl_draw_resources_created_ = true; + + return true; +} + +void BlenderGPUDisplay::gl_resources_destroy() +{ + gl_context_enable(); + + if (vertex_buffer_ != 0) { + glDeleteBuffers(1, &vertex_buffer_); + } + + if (texture_.gl_pbo_id) { + glDeleteBuffers(1, &texture_.gl_pbo_id); + texture_.gl_pbo_id = 0; + } + + if (texture_.gl_id) { + glDeleteTextures(1, &texture_.gl_id); + texture_.gl_id = 0; + } + + gl_context_disable(); + + gl_context_dispose(); +} + +bool BlenderGPUDisplay::gl_texture_resources_ensure() +{ + if (texture_.creation_attempted) { + return texture_.is_created; + } + texture_.creation_attempted = true; + + DCHECK(!texture_.gl_id); + DCHECK(!texture_.gl_pbo_id); + + /* Create texture. */ + glGenTextures(1, &texture_.gl_id); + if (!texture_.gl_id) { + LOG(ERROR) << "Error creating texture."; + return false; + } + + /* Configure the texture. */ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + + /* Create PBO for the texture. */ + glGenBuffers(1, &texture_.gl_pbo_id); + if (!texture_.gl_pbo_id) { + LOG(ERROR) << "Error creating texture pixel buffer object."; + return false; + } + + /* Creation finished with a success. */ + texture_.is_created = true; + + return true; +} + +void BlenderGPUDisplay::texture_update_if_needed() +{ + if (!texture_.need_update) { + return; + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + glTexSubImage2D( + GL_TEXTURE_2D, 0, 0, 0, texture_.width, texture_.height, GL_RGBA, GL_HALF_FLOAT, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + texture_.need_update = false; +} + +void BlenderGPUDisplay::vertex_buffer_update(const GPUDisplayParams ¶ms) +{ + /* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be + * rendered. */ + glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW); + + float *vpointer = reinterpret_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); + if (!vpointer) { + return; + } + + vpointer[0] = 0.0f; + vpointer[1] = 0.0f; + vpointer[2] = params.offset.x; + vpointer[3] = params.offset.y; + + vpointer[4] = 1.0f; + vpointer[5] = 0.0f; + vpointer[6] = (float)params.size.x + params.offset.x; + vpointer[7] = params.offset.y; + + vpointer[8] = 1.0f; + vpointer[9] = 1.0f; + vpointer[10] = (float)params.size.x + params.offset.x; + vpointer[11] = (float)params.size.y + params.offset.y; + + vpointer[12] = 0.0f; + vpointer[13] = 1.0f; + vpointer[14] = params.offset.x; + vpointer[15] = (float)params.size.y + params.offset.y; + + glUnmapBuffer(GL_ARRAY_BUFFER); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_gpu_display.h b/intern/cycles/blender/blender_gpu_display.h new file mode 100644 index 00000000000..b7eddf0afa7 --- /dev/null +++ b/intern/cycles/blender/blender_gpu_display.h @@ -0,0 +1,211 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "MEM_guardedalloc.h" + +#include "RNA_blender_cpp.h" + +#include "render/gpu_display.h" +#include "util/util_unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +/* Base class of shader used for GPU display rendering. */ +class BlenderDisplayShader { + public: + static constexpr const char *position_attribute_name = "pos"; + static constexpr const char *tex_coord_attribute_name = "texCoord"; + + /* Create shader implementation suitable for the given render engine and scene configuration. */ + static unique_ptr create(BL::RenderEngine &b_engine, BL::Scene &b_scene); + + BlenderDisplayShader() = default; + virtual ~BlenderDisplayShader() = default; + + virtual void bind(int width, int height) = 0; + virtual void unbind() = 0; + + /* Get attribute location for position and texture coordinate respectively. + * NOTE: The shader needs to be bound to have access to those. */ + virtual int get_position_attrib_location(); + virtual int get_tex_coord_attrib_location(); + + protected: + /* Get program of this display shader. + * NOTE: The shader needs to be bound to have access to this. */ + virtual uint get_shader_program() = 0; + + /* Cached values of various OpenGL resources. */ + int position_attribute_location_ = -1; + int tex_coord_attribute_location_ = -1; +}; + +/* Implementation of display rendering shader used in the case when render engine does not support + * display space shader. */ +class BlenderFallbackDisplayShader : public BlenderDisplayShader { + public: + virtual void bind(int width, int height) override; + virtual void unbind() override; + + protected: + virtual uint get_shader_program() override; + + void create_shader_if_needed(); + void destroy_shader(); + + uint shader_program_ = 0; + int image_texture_location_ = -1; + int fullscreen_location_ = -1; + + /* Shader compilation attempted. Which means, that if the shader program is 0 then compilation or + * linking has failed. Do not attempt to re-compile the shader. */ + bool shader_compile_attempted_ = false; +}; + +class BlenderDisplaySpaceShader : public BlenderDisplayShader { + public: + BlenderDisplaySpaceShader(BL::RenderEngine &b_engine, BL::Scene &b_scene); + + virtual void bind(int width, int height) override; + virtual void unbind() override; + + protected: + virtual uint get_shader_program() override; + + BL::RenderEngine b_engine_; + BL::Scene &b_scene_; + + /* Cached values of various OpenGL resources. */ + uint shader_program_ = 0; +}; + +/* GPU display implementation which is specific for Blender viewport integration. */ +class BlenderGPUDisplay : public GPUDisplay { + public: + BlenderGPUDisplay(BL::RenderEngine &b_engine, BL::Scene &b_scene); + ~BlenderGPUDisplay(); + + virtual void graphics_interop_activate() override; + virtual void graphics_interop_deactivate() override; + + virtual void clear() override; + + protected: + virtual bool do_update_begin(const GPUDisplayParams ¶ms, + int texture_width, + int texture_height) override; + virtual void do_update_end() override; + + virtual void do_copy_pixels_to_texture(const half4 *rgba_pixels, + int texture_x, + int texture_y, + int pixels_width, + int pixels_height) override; + virtual void do_draw(const GPUDisplayParams ¶ms) override; + + virtual half4 *do_map_texture_buffer() override; + virtual void do_unmap_texture_buffer() override; + + virtual DeviceGraphicsInteropDestination do_graphics_interop_get() override; + + /* Helper function which allocates new GPU context. */ + void gl_context_create(); + bool gl_context_enable(); + void gl_context_disable(); + void gl_context_dispose(); + + /* Make sure texture is allocated and its initial configuration is performed. */ + bool gl_texture_resources_ensure(); + + /* Ensure all runtime GPU resources needefd for drawing are allocated. + * Returns true if all resources needed for drawing are available. */ + bool gl_draw_resources_ensure(); + + /* Destroy all GPU resources which are being used by this object. */ + void gl_resources_destroy(); + + /* Update GPU texture dimensions and content if needed (new pixel data was provided). + * + * NOTE: The texture needs to be bound. */ + void texture_update_if_needed(); + + /* Update vetrex buffer with new coordinates of vertex positions and texture coordinates. + * This buffer is used to render texture in the viewport. + * + * NOTE: The buffer needs to be bound. */ + void vertex_buffer_update(const GPUDisplayParams ¶ms); + + BL::RenderEngine b_engine_; + + /* OpenGL context which is used the render engine doesn't have its own. */ + void *gl_context_ = nullptr; + /* The when Blender RenderEngine side context is not available and the GPUDisplay is to create + * its own context. */ + bool use_gl_context_ = false; + /* Mutex used to guard the `gl_context_`. */ + thread_mutex gl_context_mutex_; + + /* Texture which contains pixels of the render result. */ + struct { + /* Indicates whether texture creation was attempted and succeeded. + * Used to avoid multiple attempts of texture creation on GPU issues or GPU context + * misconfiguration. */ + bool creation_attempted = false; + bool is_created = false; + + /* OpenGL resource IDs of the texture itself and Pixel Buffer Object (PBO) used to write + * pixels to it. + * + * NOTE: Allocated on the engine's context. */ + uint gl_id = 0; + uint gl_pbo_id = 0; + + /* Is true when new data was written to the PBO, meaning, the texture might need to be resized + * and new data is to be uploaded to the GPU. */ + bool need_update = false; + + /* Content of the texture is to be filled with zeroes. */ + std::atomic need_clear = true; + + /* Dimensions of the texture in pixels. */ + int width = 0; + int height = 0; + + /* Dimensions of the underlying PBO. */ + int buffer_width = 0; + int buffer_height = 0; + } texture_; + + unique_ptr display_shader_; + + /* Special track of whether GPU resources were attempted to be created, to avoid attempts of + * their re-creation on failure on every redraw. */ + bool gl_draw_resource_creation_attempted_ = false; + bool gl_draw_resources_created_ = false; + + /* Vertex buffer which hold vertrices of a triangle fan which is textures with the texture + * holding the render result. */ + uint vertex_buffer_ = 0; + + void *gl_render_sync_ = nullptr; + void *gl_upload_sync_ = nullptr; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/blender_light.cpp index 542028f4b2f..4df1e720dde 100644 --- a/intern/cycles/blender/blender_light.cpp +++ b/intern/cycles/blender/blender_light.cpp @@ -125,17 +125,10 @@ void BlenderSync::sync_light(BL::Object &b_parent, light->set_shader(static_cast(used_shaders[0])); /* shadow */ - PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); PointerRNA clight = RNA_pointer_get(&b_light.ptr, "cycles"); light->set_cast_shadow(get_boolean(clight, "cast_shadow")); light->set_use_mis(get_boolean(clight, "use_multiple_importance_sampling")); - int samples = get_int(clight, "samples"); - if (get_boolean(cscene, "use_square_samples")) - light->set_samples(samples * samples); - else - light->set_samples(samples); - light->set_max_bounces(get_int(clight, "max_bounces")); if (b_ob_info.real_object != b_ob_info.iter_object) { @@ -155,10 +148,12 @@ void BlenderSync::sync_light(BL::Object &b_parent, /* visibility */ uint visibility = object_ray_visibility(b_ob_info.real_object); + light->set_use_camera((visibility & PATH_RAY_CAMERA) != 0); light->set_use_diffuse((visibility & PATH_RAY_DIFFUSE) != 0); light->set_use_glossy((visibility & PATH_RAY_GLOSSY) != 0); light->set_use_transmission((visibility & PATH_RAY_TRANSMIT) != 0); light->set_use_scatter((visibility & PATH_RAY_VOLUME_SCATTER) != 0); + light->set_is_shadow_catcher(b_ob_info.real_object.is_shadow_catcher()); /* tag */ light->tag_update(scene); @@ -169,7 +164,6 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal) BL::World b_world = b_scene.world(); if (b_world) { - PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles"); enum SamplingMethod { SAMPLING_NONE = 0, SAMPLING_AUTOMATIC, SAMPLING_MANUAL, SAMPLING_NUM }; @@ -197,12 +191,6 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal) /* force enable light again when world is resynced */ light->set_is_enabled(true); - int samples = get_int(cworld, "samples"); - if (get_boolean(cscene, "use_square_samples")) - light->set_samples(samples * samples); - else - light->set_samples(samples); - light->tag_update(scene); light_map.set_recalc(b_world); } @@ -211,7 +199,7 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal) world_map = b_world.ptr.data; world_recalc = false; - viewport_parameters = BlenderViewportParameters(b_v3d); + viewport_parameters = BlenderViewportParameters(b_v3d, use_developer_ui); } CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 22d6edeb099..95da4a2df84 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -568,7 +568,7 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, /* object loop */ bool cancel = false; bool use_portal = false; - const bool show_lights = BlenderViewportParameters(b_v3d).use_scene_lights; + const bool show_lights = BlenderViewportParameters(b_v3d, use_developer_ui).use_scene_lights; BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval(); BL::Depsgraph::object_instances_iterator b_instance_iter; diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index 6e06b6a468f..694d8454422 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -45,10 +45,6 @@ # include #endif -#ifdef WITH_OPENCL -# include "device/device_intern.h" -#endif - CCL_NAMESPACE_BEGIN namespace { @@ -72,12 +68,10 @@ PyObject *pyunicode_from_string(const char *str) /* Synchronize debug flags from a given Blender scene. * Return truth when device list needs invalidation. */ -bool debug_flags_sync_from_scene(BL::Scene b_scene) +static void debug_flags_sync_from_scene(BL::Scene b_scene) { DebugFlagsRef flags = DebugFlags(); PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); - /* Backup some settings for comparison. */ - DebugFlags::OpenCL::DeviceType opencl_device_type = flags.opencl.device_type; /* Synchronize shared flags. */ flags.viewport_static_bvh = get_enum(cscene, "debug_bvh_type"); /* Synchronize CPU flags. */ @@ -87,50 +81,19 @@ bool debug_flags_sync_from_scene(BL::Scene b_scene) flags.cpu.sse3 = get_boolean(cscene, "debug_use_cpu_sse3"); flags.cpu.sse2 = get_boolean(cscene, "debug_use_cpu_sse2"); flags.cpu.bvh_layout = (BVHLayout)get_enum(cscene, "debug_bvh_layout"); - flags.cpu.split_kernel = get_boolean(cscene, "debug_use_cpu_split_kernel"); /* Synchronize CUDA flags. */ flags.cuda.adaptive_compile = get_boolean(cscene, "debug_use_cuda_adaptive_compile"); - flags.cuda.split_kernel = get_boolean(cscene, "debug_use_cuda_split_kernel"); /* Synchronize OptiX flags. */ - flags.optix.cuda_streams = get_int(cscene, "debug_optix_cuda_streams"); - flags.optix.curves_api = get_boolean(cscene, "debug_optix_curves_api"); - /* Synchronize OpenCL device type. */ - switch (get_enum(cscene, "debug_opencl_device_type")) { - case 0: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_NONE; - break; - case 1: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_ALL; - break; - case 2: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_DEFAULT; - break; - case 3: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_CPU; - break; - case 4: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_GPU; - break; - case 5: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_ACCELERATOR; - break; - } - /* Synchronize other OpenCL flags. */ - flags.opencl.debug = get_boolean(cscene, "debug_use_opencl_debug"); - flags.opencl.mem_limit = ((size_t)get_int(cscene, "debug_opencl_mem_limit")) * 1024 * 1024; - return flags.opencl.device_type != opencl_device_type; + flags.optix.use_debug = get_boolean(cscene, "debug_use_optix_debug"); } /* Reset debug flags to default values. * Return truth when device list needs invalidation. */ -bool debug_flags_reset() +static void debug_flags_reset() { DebugFlagsRef flags = DebugFlags(); - /* Backup some settings for comparison. */ - DebugFlags::OpenCL::DeviceType opencl_device_type = flags.opencl.device_type; flags.reset(); - return flags.opencl.device_type != opencl_device_type; } } /* namespace */ @@ -175,18 +138,20 @@ static const char *PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce) static PyObject *init_func(PyObject * /*self*/, PyObject *args) { - PyObject *path, *user_path; + PyObject *path, *user_path, *temp_path; int headless; - if (!PyArg_ParseTuple(args, "OOi", &path, &user_path, &headless)) { - return NULL; + if (!PyArg_ParseTuple(args, "OOOi", &path, &user_path, &temp_path, &headless)) { + return nullptr; } - PyObject *path_coerce = NULL, *user_path_coerce = NULL; + PyObject *path_coerce = nullptr, *user_path_coerce = nullptr, *temp_path_coerce = nullptr; path_init(PyC_UnicodeAsByte(path, &path_coerce), - PyC_UnicodeAsByte(user_path, &user_path_coerce)); + PyC_UnicodeAsByte(user_path, &user_path_coerce), + PyC_UnicodeAsByte(temp_path, &temp_path_coerce)); Py_XDECREF(path_coerce); Py_XDECREF(user_path_coerce); + Py_XDECREF(temp_path_coerce); BlenderSession::headless = headless; @@ -299,6 +264,50 @@ static PyObject *render_func(PyObject * /*self*/, PyObject *args) Py_RETURN_NONE; } +static PyObject *render_frame_finish_func(PyObject * /*self*/, PyObject *args) +{ + PyObject *pysession; + + if (!PyArg_ParseTuple(args, "O", &pysession)) { + return nullptr; + } + + BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession); + + /* Allow Blender to execute other Python scripts. */ + python_thread_state_save(&session->python_thread_state); + + session->render_frame_finish(); + + python_thread_state_restore(&session->python_thread_state); + + Py_RETURN_NONE; +} + +static PyObject *draw_func(PyObject * /*self*/, PyObject *args) +{ + PyObject *py_session, *py_graph, *py_screen, *py_space_image; + + if (!PyArg_ParseTuple(args, "OOOO", &py_session, &py_graph, &py_screen, &py_space_image)) { + return nullptr; + } + + BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(py_session); + + ID *b_screen = (ID *)PyLong_AsVoidPtr(py_screen); + + PointerRNA b_space_image_ptr; + RNA_pointer_create(b_screen, + &RNA_SpaceImageEditor, + pylong_as_voidptr_typesafe(py_space_image), + &b_space_image_ptr); + BL::SpaceImageEditor b_space_image(b_space_image_ptr); + + session->draw(b_space_image); + + Py_RETURN_NONE; +} + /* pixel_array and result passed as pointers */ static PyObject *bake_func(PyObject * /*self*/, PyObject *args) { @@ -336,7 +345,7 @@ static PyObject *bake_func(PyObject * /*self*/, PyObject *args) Py_RETURN_NONE; } -static PyObject *draw_func(PyObject * /*self*/, PyObject *args) +static PyObject *view_draw_func(PyObject * /*self*/, PyObject *args) { PyObject *pysession, *pygraph, *pyv3d, *pyrv3d; @@ -350,7 +359,7 @@ static PyObject *draw_func(PyObject * /*self*/, PyObject *args) int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); - session->draw(viewport[2], viewport[3]); + session->view_draw(viewport[2], viewport[3]); } Py_RETURN_NONE; @@ -697,40 +706,6 @@ static PyObject *system_info_func(PyObject * /*self*/, PyObject * /*value*/) return pyunicode_from_string(system_info.c_str()); } -#ifdef WITH_OPENCL -static PyObject *opencl_disable_func(PyObject * /*self*/, PyObject * /*value*/) -{ - VLOG(2) << "Disabling OpenCL platform."; - DebugFlags().opencl.device_type = DebugFlags::OpenCL::DEVICE_NONE; - Py_RETURN_NONE; -} - -static PyObject *opencl_compile_func(PyObject * /*self*/, PyObject *args) -{ - PyObject *sequence = PySequence_Fast(args, "Arguments must be a sequence"); - if (sequence == NULL) { - Py_RETURN_FALSE; - } - - vector parameters; - for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(sequence); i++) { - PyObject *item = PySequence_Fast_GET_ITEM(sequence, i); - PyObject *item_as_string = PyObject_Str(item); - const char *parameter_string = PyUnicode_AsUTF8(item_as_string); - parameters.push_back(parameter_string); - Py_DECREF(item_as_string); - } - Py_DECREF(sequence); - - if (device_opencl_compile_kernel(parameters)) { - Py_RETURN_TRUE; - } - else { - Py_RETURN_FALSE; - } -} -#endif - static bool image_parse_filepaths(PyObject *pyfilepaths, vector &filepaths) { if (PyUnicode_Check(pyfilepaths)) { @@ -762,6 +737,10 @@ static bool image_parse_filepaths(PyObject *pyfilepaths, vector &filepat static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *keywords) { +#if 1 + (void)args; + (void)keywords; +#else static const char *keyword_list[] = { "preferences", "scene", "view_layer", "input", "output", "tile_size", "samples", NULL}; PyObject *pypreferences, *pyscene, *pyviewlayer; @@ -835,7 +814,7 @@ static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *key } /* Create denoiser. */ - Denoiser denoiser(device); + DenoiserPipeline denoiser(device); denoiser.params = params; denoiser.input = input; denoiser.output = output; @@ -852,6 +831,7 @@ static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *key PyErr_SetString(PyExc_ValueError, denoiser.error.c_str()); return NULL; } +#endif Py_RETURN_NONE; } @@ -903,10 +883,7 @@ static PyObject *debug_flags_update_func(PyObject * /*self*/, PyObject *args) RNA_id_pointer_create((ID *)PyLong_AsVoidPtr(pyscene), &sceneptr); BL::Scene b_scene(sceneptr); - if (debug_flags_sync_from_scene(b_scene)) { - VLOG(2) << "Tagging device list for update."; - Device::tag_update(); - } + debug_flags_sync_from_scene(b_scene); VLOG(2) << "Debug flags set to:\n" << DebugFlags(); @@ -917,10 +894,7 @@ static PyObject *debug_flags_update_func(PyObject * /*self*/, PyObject *args) static PyObject *debug_flags_reset_func(PyObject * /*self*/, PyObject * /*args*/) { - if (debug_flags_reset()) { - VLOG(2) << "Tagging device list for update."; - Device::tag_update(); - } + debug_flags_reset(); if (debug_flags_set) { VLOG(2) << "Debug flags reset to:\n" << DebugFlags(); debug_flags_set = false; @@ -928,84 +902,6 @@ static PyObject *debug_flags_reset_func(PyObject * /*self*/, PyObject * /*args*/ Py_RETURN_NONE; } -static PyObject *set_resumable_chunk_func(PyObject * /*self*/, PyObject *args) -{ - int num_resumable_chunks, current_resumable_chunk; - if (!PyArg_ParseTuple(args, "ii", &num_resumable_chunks, ¤t_resumable_chunk)) { - Py_RETURN_NONE; - } - - if (num_resumable_chunks <= 0) { - fprintf(stderr, "Cycles: Bad value for number of resumable chunks.\n"); - abort(); - Py_RETURN_NONE; - } - if (current_resumable_chunk < 1 || current_resumable_chunk > num_resumable_chunks) { - fprintf(stderr, "Cycles: Bad value for current resumable chunk number.\n"); - abort(); - Py_RETURN_NONE; - } - - VLOG(1) << "Initialized resumable render: " - << "num_resumable_chunks=" << num_resumable_chunks << ", " - << "current_resumable_chunk=" << current_resumable_chunk; - BlenderSession::num_resumable_chunks = num_resumable_chunks; - BlenderSession::current_resumable_chunk = current_resumable_chunk; - - printf("Cycles: Will render chunk %d of %d\n", current_resumable_chunk, num_resumable_chunks); - - Py_RETURN_NONE; -} - -static PyObject *set_resumable_chunk_range_func(PyObject * /*self*/, PyObject *args) -{ - int num_chunks, start_chunk, end_chunk; - if (!PyArg_ParseTuple(args, "iii", &num_chunks, &start_chunk, &end_chunk)) { - Py_RETURN_NONE; - } - - if (num_chunks <= 0) { - fprintf(stderr, "Cycles: Bad value for number of resumable chunks.\n"); - abort(); - Py_RETURN_NONE; - } - if (start_chunk < 1 || start_chunk > num_chunks) { - fprintf(stderr, "Cycles: Bad value for start chunk number.\n"); - abort(); - Py_RETURN_NONE; - } - if (end_chunk < 1 || end_chunk > num_chunks) { - fprintf(stderr, "Cycles: Bad value for start chunk number.\n"); - abort(); - Py_RETURN_NONE; - } - if (start_chunk > end_chunk) { - fprintf(stderr, "Cycles: End chunk should be higher than start one.\n"); - abort(); - Py_RETURN_NONE; - } - - VLOG(1) << "Initialized resumable render: " - << "num_resumable_chunks=" << num_chunks << ", " - << "start_resumable_chunk=" << start_chunk << "end_resumable_chunk=" << end_chunk; - BlenderSession::num_resumable_chunks = num_chunks; - BlenderSession::start_resumable_chunk = start_chunk; - BlenderSession::end_resumable_chunk = end_chunk; - - printf("Cycles: Will render chunks %d to %d of %d\n", start_chunk, end_chunk, num_chunks); - - Py_RETURN_NONE; -} - -static PyObject *clear_resumable_chunk_func(PyObject * /*self*/, PyObject * /*value*/) -{ - VLOG(1) << "Clear resumable render"; - BlenderSession::num_resumable_chunks = 0; - BlenderSession::current_resumable_chunk = 0; - - Py_RETURN_NONE; -} - static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args*/) { BlenderSession::print_render_stats = true; @@ -1015,16 +911,14 @@ static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args* static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/) { vector device_types = Device::available_types(); - bool has_cuda = false, has_optix = false, has_opencl = false; + bool has_cuda = false, has_optix = false; foreach (DeviceType device_type, device_types) { has_cuda |= (device_type == DEVICE_CUDA); has_optix |= (device_type == DEVICE_OPTIX); - has_opencl |= (device_type == DEVICE_OPENCL); } - PyObject *list = PyTuple_New(3); + PyObject *list = PyTuple_New(2); PyTuple_SET_ITEM(list, 0, PyBool_FromLong(has_cuda)); PyTuple_SET_ITEM(list, 1, PyBool_FromLong(has_optix)); - PyTuple_SET_ITEM(list, 2, PyBool_FromLong(has_opencl)); return list; } @@ -1044,9 +938,6 @@ static PyObject *set_device_override_func(PyObject * /*self*/, PyObject *arg) if (override == "CPU") { BlenderSession::device_override = DEVICE_MASK_CPU; } - else if (override == "OPENCL") { - BlenderSession::device_override = DEVICE_MASK_OPENCL; - } else if (override == "CUDA") { BlenderSession::device_override = DEVICE_MASK_CUDA; } @@ -1072,8 +963,10 @@ static PyMethodDef methods[] = { {"create", create_func, METH_VARARGS, ""}, {"free", free_func, METH_O, ""}, {"render", render_func, METH_VARARGS, ""}, - {"bake", bake_func, METH_VARARGS, ""}, + {"render_frame_finish", render_frame_finish_func, METH_VARARGS, ""}, {"draw", draw_func, METH_VARARGS, ""}, + {"bake", bake_func, METH_VARARGS, ""}, + {"view_draw", view_draw_func, METH_VARARGS, ""}, {"sync", sync_func, METH_VARARGS, ""}, {"reset", reset_func, METH_VARARGS, ""}, #ifdef WITH_OSL @@ -1082,10 +975,6 @@ static PyMethodDef methods[] = { #endif {"available_devices", available_devices_func, METH_VARARGS, ""}, {"system_info", system_info_func, METH_NOARGS, ""}, -#ifdef WITH_OPENCL - {"opencl_disable", opencl_disable_func, METH_NOARGS, ""}, - {"opencl_compile", opencl_compile_func, METH_VARARGS, ""}, -#endif /* Standalone denoising */ {"denoise", (PyCFunction)denoise_func, METH_VARARGS | METH_KEYWORDS, ""}, @@ -1098,11 +987,6 @@ static PyMethodDef methods[] = { /* Statistics. */ {"enable_print_stats", enable_print_stats_func, METH_NOARGS, ""}, - /* Resumable render */ - {"set_resumable_chunk", set_resumable_chunk_func, METH_VARARGS, ""}, - {"set_resumable_chunk_range", set_resumable_chunk_range_func, METH_VARARGS, ""}, - {"clear_resumable_chunk", clear_resumable_chunk_func, METH_NOARGS, ""}, - /* Compute Device selection */ {"get_device_types", get_device_types_func, METH_VARARGS, ""}, {"set_device_override", set_device_override_func, METH_O, ""}, @@ -1153,14 +1037,6 @@ void *CCL_python_module_init() PyModule_AddStringConstant(mod, "osl_version_string", "unknown"); #endif -#ifdef WITH_NETWORK - PyModule_AddObject(mod, "with_network", Py_True); - Py_INCREF(Py_True); -#else /* WITH_NETWORK */ - PyModule_AddObject(mod, "with_network", Py_False); - Py_INCREF(Py_False); -#endif /* WITH_NETWORK */ - #ifdef WITH_EMBREE PyModule_AddObject(mod, "with_embree", Py_True); Py_INCREF(Py_True); diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 29de886e4ff..5aafa605526 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -38,9 +38,11 @@ #include "util/util_hash.h" #include "util/util_logging.h" #include "util/util_murmurhash.h" +#include "util/util_path.h" #include "util/util_progress.h" #include "util/util_time.h" +#include "blender/blender_gpu_display.h" #include "blender/blender_session.h" #include "blender/blender_sync.h" #include "blender/blender_util.h" @@ -49,10 +51,6 @@ CCL_NAMESPACE_BEGIN DeviceTypeMask BlenderSession::device_override = DEVICE_MASK_ALL; bool BlenderSession::headless = false; -int BlenderSession::num_resumable_chunks = 0; -int BlenderSession::current_resumable_chunk = 0; -int BlenderSession::start_resumable_chunk = 0; -int BlenderSession::end_resumable_chunk = 0; bool BlenderSession::print_render_stats = false; BlenderSession::BlenderSession(BL::RenderEngine &b_engine, @@ -103,7 +101,9 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine, width(width), height(height), preview_osl(false), - python_thread_state(NULL) + python_thread_state(NULL), + use_developer_ui(b_userpref.experimental().use_cycles_debug() && + b_userpref.view().show_developer_ui()) { /* 3d view render */ background = false; @@ -119,10 +119,10 @@ BlenderSession::~BlenderSession() void BlenderSession::create_session() { - SessionParams session_params = BlenderSync::get_session_params( + const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); - bool session_pause = BlenderSync::get_session_pause(b_scene, background); + const SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); + const bool session_pause = BlenderSync::get_session_pause(b_scene, background); /* reset status/progress */ last_status = ""; @@ -131,20 +131,18 @@ void BlenderSession::create_session() start_resize_time = 0.0; /* create session */ - session = new Session(session_params); - session->scene = scene; + session = new Session(session_params, scene_params); session->progress.set_update_callback(function_bind(&BlenderSession::tag_redraw, this)); session->progress.set_cancel_callback(function_bind(&BlenderSession::test_cancel, this)); session->set_pause(session_pause); /* create scene */ - scene = new Scene(scene_params, session->device); + scene = session->scene; scene->name = b_scene.name(); - session->scene = scene; - /* create sync */ - sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress); + sync = new BlenderSync( + b_engine, b_data, b_scene, scene, !background, use_developer_ui, session->progress); BL::Object b_camera_override(b_engine.camera_override()); if (b_v3d) { sync->sync_view(b_v3d, b_rv3d, width, height); @@ -154,13 +152,23 @@ void BlenderSession::create_session() } /* set buffer parameters */ - BufferParams buffer_params = BlenderSync::get_buffer_params( - b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); - session->reset(buffer_params, session_params.samples); + const BufferParams buffer_params = BlenderSync::get_buffer_params( + b_v3d, b_rv3d, scene->camera, width, height); + session->reset(session_params, buffer_params); - b_engine.use_highlight_tiles(session_params.progressive_refine == false); + /* Create GPU display. */ + if (!b_engine.is_preview() && !headless) { + session->set_gpu_display(make_unique(b_engine, b_scene)); + } - update_resumable_tile_manager(session_params.samples); + /* Viewport and preview (as in, material preview) does not do tiled rendering, so can inform + * engine that no tracking of the tiles state is needed. + * The offline rendering will make a decision when tile is being written. The penalty of asking + * the engine to keep track of tiles state is minimal, so there is nothing to worry about here + * about possible single-tiled final render. */ + if (!b_engine.is_preview() && !b_v3d) { + b_engine.use_highlight_tiles(true); + } } void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsgraph) @@ -202,9 +210,9 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg return; } - SessionParams session_params = BlenderSync::get_session_params( + const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); + const SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); if (scene->params.modified(scene_params) || session->params.modified(session_params) || !this->b_render.use_persistent_data()) { @@ -220,8 +228,6 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg session->progress.reset(); - session->tile_manager.set_tile_order(session_params.tile_order); - /* peak memory usage should show current render peak, not peak for all renders * made by this render session */ @@ -230,7 +236,8 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg if (is_new_session) { /* Sync object should be re-created for new scene. */ delete sync; - sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress); + sync = new BlenderSync( + b_engine, b_data, b_scene, scene, !background, use_developer_ui, session->progress); } else { /* Sync recalculations to do just the required updates. */ @@ -242,103 +249,44 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL); BL::RegionView3D b_null_region_view3d(PointerRNA_NULL); - BufferParams buffer_params = BlenderSync::get_buffer_params(b_null_space_view3d, - b_null_region_view3d, - scene->camera, - width, - height, - session_params.denoising.use); - session->reset(buffer_params, session_params.samples); - - b_engine.use_highlight_tiles(session_params.progressive_refine == false); + const BufferParams buffer_params = BlenderSync::get_buffer_params( + b_null_space_view3d, b_null_region_view3d, scene->camera, width, height); + session->reset(session_params, buffer_params); /* reset time */ start_resize_time = 0.0; + + { + thread_scoped_lock lock(draw_state_.mutex); + draw_state_.last_pass_index = -1; + } } void BlenderSession::free_session() { - session->cancel(); + if (session) { + session->cancel(true); + } delete sync; + sync = nullptr; + delete session; + session = nullptr; } -static ShaderEvalType get_shader_type(const string &pass_type) +void BlenderSession::read_render_tile() { - const char *shader_type = pass_type.c_str(); - - /* data passes */ - if (strcmp(shader_type, "NORMAL") == 0) - return SHADER_EVAL_NORMAL; - else if (strcmp(shader_type, "UV") == 0) - return SHADER_EVAL_UV; - else if (strcmp(shader_type, "ROUGHNESS") == 0) - return SHADER_EVAL_ROUGHNESS; - else if (strcmp(shader_type, "DIFFUSE_COLOR") == 0) - return SHADER_EVAL_DIFFUSE_COLOR; - else if (strcmp(shader_type, "GLOSSY_COLOR") == 0) - return SHADER_EVAL_GLOSSY_COLOR; - else if (strcmp(shader_type, "TRANSMISSION_COLOR") == 0) - return SHADER_EVAL_TRANSMISSION_COLOR; - else if (strcmp(shader_type, "EMIT") == 0) - return SHADER_EVAL_EMISSION; - - /* light passes */ - else if (strcmp(shader_type, "AO") == 0) - return SHADER_EVAL_AO; - else if (strcmp(shader_type, "COMBINED") == 0) - return SHADER_EVAL_COMBINED; - else if (strcmp(shader_type, "SHADOW") == 0) - return SHADER_EVAL_SHADOW; - else if (strcmp(shader_type, "DIFFUSE") == 0) - return SHADER_EVAL_DIFFUSE; - else if (strcmp(shader_type, "GLOSSY") == 0) - return SHADER_EVAL_GLOSSY; - else if (strcmp(shader_type, "TRANSMISSION") == 0) - return SHADER_EVAL_TRANSMISSION; - - /* extra */ - else if (strcmp(shader_type, "ENVIRONMENT") == 0) - return SHADER_EVAL_ENVIRONMENT; - - else - return SHADER_EVAL_BAKE; -} - -static BL::RenderResult begin_render_result(BL::RenderEngine &b_engine, - int x, - int y, - int w, - int h, - const char *layername, - const char *viewname) -{ - return b_engine.begin_result(x, y, w, h, layername, viewname); -} - -static void end_render_result(BL::RenderEngine &b_engine, - BL::RenderResult &b_rr, - bool cancel, - bool highlight, - bool do_merge_results) -{ - b_engine.end_result(b_rr, (int)cancel, (int)highlight, (int)do_merge_results); -} - -void BlenderSession::do_write_update_render_tile(RenderTile &rtile, - bool do_update_only, - bool do_read_only, - bool highlight) -{ - int x = rtile.x - session->tile_manager.params.full_x; - int y = rtile.y - session->tile_manager.params.full_y; - int w = rtile.w; - int h = rtile.h; + const int2 tile_offset = session->get_render_tile_offset(); + const int2 tile_size = session->get_render_tile_size(); /* get render result */ - BL::RenderResult b_rr = begin_render_result( - b_engine, x, y, w, h, b_rlay_name.c_str(), b_rview_name.c_str()); + BL::RenderResult b_rr = b_engine.begin_result(tile_offset.x, + tile_offset.y, + tile_size.x, + tile_size.y, + b_rlay_name.c_str(), + b_rview_name.c_str()); /* can happen if the intersected rectangle gives 0 width or height */ if (b_rr.ptr.data == NULL) { @@ -354,59 +302,70 @@ void BlenderSession::do_write_update_render_tile(RenderTile &rtile, BL::RenderLayer b_rlay = *b_single_rlay; - if (do_read_only) { - /* copy each pass */ - for (BL::RenderPass &b_pass : b_rlay.passes) { - /* find matching pass type */ - PassType pass_type = BlenderSync::get_pass_type(b_pass); - int components = b_pass.channels(); + vector pixels(tile_size.x * tile_size.y * 4); - rtile.buffers->set_pass_rect( - pass_type, components, (float *)b_pass.rect(), rtile.num_samples); - } - - end_render_result(b_engine, b_rr, false, false, false); - } - else if (do_update_only) { - /* Sample would be zero at initial tile update, which is only needed - * to tag tile form blender side as IN PROGRESS for proper highlight - * no buffers should be sent to blender yet. For denoise we also - * keep showing the noisy buffers until denoise is done. */ - bool merge = (rtile.sample != 0) && (rtile.task != RenderTile::DENOISE); - - if (merge) { - update_render_result(b_rlay, rtile); - } - - end_render_result(b_engine, b_rr, true, highlight, merge); - } - else { - /* Write final render result. */ - write_render_result(b_rlay, rtile); - end_render_result(b_engine, b_rr, false, false, true); + /* Copy each pass. + * TODO:copy only the required ones for better performance? */ + for (BL::RenderPass &b_pass : b_rlay.passes) { + session->set_render_tile_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect()); } } -void BlenderSession::read_render_tile(RenderTile &rtile) +void BlenderSession::write_render_tile() { - do_write_update_render_tile(rtile, false, true, false); + const int2 tile_offset = session->get_render_tile_offset(); + const int2 tile_size = session->get_render_tile_size(); + + const string_view render_layer_name = session->get_render_tile_layer(); + const string_view render_view_name = session->get_render_tile_view(); + + b_engine.tile_highlight_clear_all(); + + /* get render result */ + BL::RenderResult b_rr = b_engine.begin_result(tile_offset.x, + tile_offset.y, + tile_size.x, + tile_size.y, + render_layer_name.c_str(), + render_view_name.c_str()); + + /* can happen if the intersected rectangle gives 0 width or height */ + if (b_rr.ptr.data == NULL) { + return; + } + + BL::RenderResult::layers_iterator b_single_rlay; + b_rr.layers.begin(b_single_rlay); + + /* layer will be missing if it was disabled in the UI */ + if (b_single_rlay == b_rr.layers.end()) { + return; + } + + BL::RenderLayer b_rlay = *b_single_rlay; + + write_render_result(b_rlay); + + b_engine.end_result(b_rr, true, false, true); } -void BlenderSession::write_render_tile(RenderTile &rtile) +void BlenderSession::update_render_tile() { - do_write_update_render_tile(rtile, false, false, false); + if (!session->has_multiple_render_tiles()) { + /* Don't highlight full-frame tile. */ + return; + } + + const int2 tile_offset = session->get_render_tile_offset(); + const int2 tile_size = session->get_render_tile_size(); + + b_engine.tile_highlight_clear_all(); + b_engine.tile_highlight_set(tile_offset.x, tile_offset.y, tile_size.x, tile_size.y, true); } -void BlenderSession::update_render_tile(RenderTile &rtile, bool highlight) +void BlenderSession::full_buffer_written(string_view filename) { - /* use final write for preview renders, otherwise render result wouldn't be - * be updated in blender side - * would need to be investigated a bit further, but for now shall be fine - */ - if (!b_engine.is_preview()) - do_write_update_render_tile(rtile, true, false, highlight); - else - do_write_update_render_tile(rtile, false, false, false); + full_buffer_files_.emplace_back(filename); } static void add_cryptomatte_layer(BL::RenderResult &b_rr, string name, string manifest) @@ -430,12 +389,15 @@ void BlenderSession::stamp_view_layer_metadata(Scene *scene, const string &view_ to_string(session->params.samples).c_str()); /* Store ranged samples information. */ + /* TODO(sergey): Need to bring this information back. */ +#if 0 if (session->tile_manager.range_num_samples != -1) { b_rr.stamp_data_add_field((prefix + "range_start_sample").c_str(), to_string(session->tile_manager.range_start_sample).c_str()); b_rr.stamp_data_add_field((prefix + "range_num_samples").c_str(), to_string(session->tile_manager.range_num_samples).c_str()); } +#endif /* Write cryptomatte metadata. */ if (scene->film->get_cryptomatte_passes() & CRYPT_OBJECT) { @@ -475,38 +437,44 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) } /* set callback to write out render results */ - session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1); - session->update_render_tile_cb = function_bind( - &BlenderSession::update_render_tile, this, _1, _2); + session->write_render_tile_cb = [&]() { write_render_tile(); }; + + /* Use final write for preview renders, otherwise render result wouldn't be be updated on Blender + * side. */ + /* TODO(sergey): Investigate whether GPUDisplay can be used for the preview as well. */ + if (b_engine.is_preview()) { + session->update_render_tile_cb = [&]() { write_render_tile(); }; + } + else { + session->update_render_tile_cb = [&]() { update_render_tile(); }; + } + + session->full_buffer_written_cb = [&](string_view filename) { full_buffer_written(filename); }; BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval(); /* get buffer parameters */ - SessionParams session_params = BlenderSync::get_session_params( - b_engine, b_userpref, b_scene, background, b_view_layer); + const SessionParams session_params = BlenderSync::get_session_params( + b_engine, b_userpref, b_scene, background); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height); /* temporary render result to find needed passes and views */ - BL::RenderResult b_rr = begin_render_result( - b_engine, 0, 0, 1, 1, b_view_layer.name().c_str(), NULL); + BL::RenderResult b_rr = b_engine.begin_result(0, 0, 1, 1, b_view_layer.name().c_str(), NULL); BL::RenderResult::layers_iterator b_single_rlay; b_rr.layers.begin(b_single_rlay); BL::RenderLayer b_rlay = *b_single_rlay; - b_rlay_name = b_view_layer.name(); - /* Update denoising parameters. */ - session->set_denoising(session_params.denoising); + { + thread_scoped_lock lock(draw_state_.mutex); + b_rlay_name = b_view_layer.name(); + + /* Signal that the display pass is to be updated. */ + draw_state_.last_pass_index = -1; + } /* Compute render passes and film settings. */ - vector passes = sync->sync_render_passes( - b_scene, b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising); - - /* Set buffer params, using film settings from sync_render_passes. */ - buffer_params.passes = passes; - buffer_params.denoising_data_pass = scene->film->get_denoising_data_pass(); - buffer_params.denoising_clean_pass = scene->film->get_denoising_clean_pass(); - buffer_params.denoising_prefiltered_pass = scene->film->get_denoising_prefiltered_pass(); + sync->sync_render_passes(b_rlay, b_view_layer); BL::RenderResult::views_iterator b_view_iter; @@ -520,6 +488,9 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) ++b_view_iter, ++view_index) { b_rview_name = b_view_iter->name(); + buffer_params.layer = b_view_layer.name(); + buffer_params.view = b_rview_name; + /* set the current view */ b_engine.active_view_set(b_rview_name.c_str()); @@ -549,20 +520,16 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) } /* Update number of samples per layer. */ - int samples = sync->get_layer_samples(); - bool bound_samples = sync->get_layer_bound_samples(); - int effective_layer_samples; + const int samples = sync->get_layer_samples(); + const bool bound_samples = sync->get_layer_bound_samples(); - if (samples != 0 && (!bound_samples || (samples < session_params.samples))) - effective_layer_samples = samples; - else - effective_layer_samples = session_params.samples; - - /* Update tile manager if we're doing resumable render. */ - update_resumable_tile_manager(effective_layer_samples); + SessionParams effective_session_params = session_params; + if (samples != 0 && (!bound_samples || (samples < session_params.samples))) { + effective_session_params.samples = samples; + } /* Update session itself. */ - session->reset(buffer_params, effective_layer_samples); + session->reset(effective_session_params, buffer_params); /* render */ if (!b_engine.is_preview() && background && print_render_stats) { @@ -586,65 +553,146 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) stamp_view_layer_metadata(scene, b_rlay_name); /* free result without merging */ - end_render_result(b_engine, b_rr, true, true, false); + b_engine.end_result(b_rr, true, false, false); double total_time, render_time; session->progress.get_time(total_time, render_time); VLOG(1) << "Total render time: " << total_time; VLOG(1) << "Render time (without synchronization): " << render_time; +} + +void BlenderSession::render_frame_finish() +{ + /* Processing of all layers and views is done. Clear the strings so that we can communicate + * progress about reading files and denoising them. */ + b_rlay_name = ""; + b_rview_name = ""; + + if (!b_render.use_persistent_data()) { + /* Free the sync object so that it can properly dereference nodes from the scene graph before + * the graph is freed. */ + delete sync; + sync = nullptr; + + session->device_free(); + } + + for (string_view filename : full_buffer_files_) { + session->process_full_buffer_from_disk(filename); + path_remove(filename); + } /* clear callback */ session->write_render_tile_cb = function_null; session->update_render_tile_cb = function_null; + session->full_buffer_written_cb = function_null; } -static int bake_pass_filter_get(const int pass_filter) +static PassType bake_type_to_pass(const string &bake_type_str, const int bake_filter) { - int flag = BAKE_FILTER_NONE; + const char *bake_type = bake_type_str.c_str(); - if ((pass_filter & BL::BakeSettings::pass_filter_DIRECT) != 0) - flag |= BAKE_FILTER_DIRECT; - if ((pass_filter & BL::BakeSettings::pass_filter_INDIRECT) != 0) - flag |= BAKE_FILTER_INDIRECT; - if ((pass_filter & BL::BakeSettings::pass_filter_COLOR) != 0) - flag |= BAKE_FILTER_COLOR; + /* data passes */ + if (strcmp(bake_type, "POSITION") == 0) { + return PASS_POSITION; + } + else if (strcmp(bake_type, "NORMAL") == 0) { + return PASS_NORMAL; + } + else if (strcmp(bake_type, "UV") == 0) { + return PASS_UV; + } + else if (strcmp(bake_type, "ROUGHNESS") == 0) { + return PASS_ROUGHNESS; + } + else if (strcmp(bake_type, "EMIT") == 0) { + return PASS_EMISSION; + } + /* light passes */ + else if (strcmp(bake_type, "AO") == 0) { + return PASS_AO; + } + else if (strcmp(bake_type, "COMBINED") == 0) { + return PASS_COMBINED; + } + else if (strcmp(bake_type, "SHADOW") == 0) { + return PASS_SHADOW; + } + else if (strcmp(bake_type, "DIFFUSE") == 0) { + if ((bake_filter & BL::BakeSettings::pass_filter_DIRECT) && + bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_DIFFUSE; + } + else if (bake_filter & BL::BakeSettings::pass_filter_DIRECT) { + return PASS_DIFFUSE_DIRECT; + } + else if (bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_DIFFUSE_INDIRECT; + } + else { + return PASS_DIFFUSE_COLOR; + } + } + else if (strcmp(bake_type, "GLOSSY") == 0) { + if ((bake_filter & BL::BakeSettings::pass_filter_DIRECT) && + bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_GLOSSY; + } + else if (bake_filter & BL::BakeSettings::pass_filter_DIRECT) { + return PASS_GLOSSY_DIRECT; + } + else if (bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_GLOSSY_INDIRECT; + } + else { + return PASS_GLOSSY_COLOR; + } + } + else if (strcmp(bake_type, "TRANSMISSION") == 0) { + if ((bake_filter & BL::BakeSettings::pass_filter_DIRECT) && + bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_TRANSMISSION; + } + else if (bake_filter & BL::BakeSettings::pass_filter_DIRECT) { + return PASS_TRANSMISSION_DIRECT; + } + else if (bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_TRANSMISSION_INDIRECT; + } + else { + return PASS_TRANSMISSION_COLOR; + } + } + /* extra */ + else if (strcmp(bake_type, "ENVIRONMENT") == 0) { + return PASS_BACKGROUND; + } - if ((pass_filter & BL::BakeSettings::pass_filter_DIFFUSE) != 0) - flag |= BAKE_FILTER_DIFFUSE; - if ((pass_filter & BL::BakeSettings::pass_filter_GLOSSY) != 0) - flag |= BAKE_FILTER_GLOSSY; - if ((pass_filter & BL::BakeSettings::pass_filter_TRANSMISSION) != 0) - flag |= BAKE_FILTER_TRANSMISSION; - - if ((pass_filter & BL::BakeSettings::pass_filter_EMIT) != 0) - flag |= BAKE_FILTER_EMISSION; - if ((pass_filter & BL::BakeSettings::pass_filter_AO) != 0) - flag |= BAKE_FILTER_AO; - - return flag; + return PASS_COMBINED; } void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, BL::Object &b_object, - const string &pass_type, - const int pass_filter, + const string &bake_type, + const int bake_filter, const int bake_width, const int bake_height) { b_depsgraph = b_depsgraph_; - ShaderEvalType shader_type = get_shader_type(pass_type); - int bake_pass_filter = bake_pass_filter_get(pass_filter); - /* Initialize bake manager, before we load the baking kernels. */ - scene->bake_manager->set(scene, b_object.name(), shader_type, bake_pass_filter); + scene->bake_manager->set(scene, b_object.name()); - /* Passes are identified by name, so in order to return the combined pass we need to set the - * name. */ - Pass::add(PASS_COMBINED, scene->passes, "Combined"); + /* Add render pass that we want to bake, and name it Combined so that it is + * used as that on the Blender side. */ + Pass *pass = scene->create_node(); + pass->set_name(ustring("Combined")); + pass->set_type(bake_type_to_pass(bake_type, bake_filter)); + pass->set_include_albedo((bake_filter & BL::BakeSettings::pass_filter_COLOR)); - session->read_bake_tile_cb = function_bind(&BlenderSession::read_render_tile, this, _1); - session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1); + session->read_render_tile_cb = [&]() { read_render_tile(); }; + session->write_render_tile_cb = [&]() { write_render_tile(); }; + session->set_gpu_display(nullptr); if (!session->progress.get_cancel()) { /* Sync scene. */ @@ -667,18 +715,15 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, if (object_found && !session->progress.get_cancel()) { /* Get session and buffer parameters. */ - SessionParams session_params = BlenderSync::get_session_params( + const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - session_params.progressive_refine = false; BufferParams buffer_params; buffer_params.width = bake_width; buffer_params.height = bake_height; - buffer_params.passes = scene->passes; /* Update session. */ - session->tile_manager.set_samples(session_params.samples); - session->reset(buffer_params, session_params.samples); + session->reset(session_params, buffer_params); session->progress.set_update_callback( function_bind(&BlenderSession::update_bake_progress, this)); @@ -690,71 +735,43 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, session->wait(); } - session->read_bake_tile_cb = function_null; + session->read_render_tile_cb = function_null; session->write_render_tile_cb = function_null; } -void BlenderSession::do_write_update_render_result(BL::RenderLayer &b_rlay, - RenderTile &rtile, - bool do_update_only) +void BlenderSession::write_render_result(BL::RenderLayer &b_rlay) { - RenderBuffers *buffers = rtile.buffers; - - /* copy data from device */ - if (!buffers->copy_from_device()) + if (!session->copy_render_tile_from_device()) { return; - - float exposure = scene->film->get_exposure(); - - vector pixels(rtile.w * rtile.h * 4); - - /* Adjust absolute sample number to the range. */ - int sample = rtile.sample; - const int range_start_sample = session->tile_manager.range_start_sample; - if (range_start_sample != -1) { - sample -= range_start_sample; } - if (!do_update_only) { - /* copy each pass */ - for (BL::RenderPass &b_pass : b_rlay.passes) { - int components = b_pass.channels(); + const int2 tile_size = session->get_render_tile_size(); + vector pixels(tile_size.x * tile_size.y * 4); - /* Copy pixels from regular render passes. */ - bool read = buffers->get_pass_rect(b_pass.name(), exposure, sample, components, &pixels[0]); - - /* If denoising pass, */ - if (!read) { - int denoising_offset = BlenderSync::get_denoising_pass(b_pass); - if (denoising_offset >= 0) { - read = buffers->get_denoising_pass_rect( - denoising_offset, exposure, sample, components, &pixels[0]); - } - } - - if (!read) { - memset(&pixels[0], 0, pixels.size() * sizeof(float)); - } - - b_pass.rect(&pixels[0]); + /* Copy each pass. */ + for (BL::RenderPass &b_pass : b_rlay.passes) { + if (!session->get_render_tile_pixels(b_pass.name(), b_pass.channels(), &pixels[0])) { + memset(&pixels[0], 0, pixels.size() * sizeof(float)); } - } - else { - /* copy combined pass */ - BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str())); - if (buffers->get_pass_rect("Combined", exposure, sample, 4, &pixels[0])) - b_combined_pass.rect(&pixels[0]); + + b_pass.rect(&pixels[0]); } } -void BlenderSession::write_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile) +void BlenderSession::update_render_result(BL::RenderLayer &b_rlay) { - do_write_update_render_result(b_rlay, rtile, false); -} + if (!session->copy_render_tile_from_device()) { + return; + } -void BlenderSession::update_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile) -{ - do_write_update_render_result(b_rlay, rtile, true); + const int2 tile_size = session->get_render_tile_size(); + vector pixels(tile_size.x * tile_size.y * 4); + + /* Copy combined pass. */ + BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str())); + if (session->get_render_tile_pixels("Combined", b_combined_pass.channels(), &pixels[0])) { + b_combined_pass.rect(&pixels[0]); + } } void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) @@ -764,19 +781,19 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) return; /* on session/scene parameter changes, we recreate session entirely */ - SessionParams session_params = BlenderSync::get_session_params( + const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); - bool session_pause = BlenderSync::get_session_pause(b_scene, background); + const SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); + const bool session_pause = BlenderSync::get_session_pause(b_scene, background); if (session->params.modified(session_params) || scene->params.modified(scene_params)) { free_session(); create_session(); } - /* increase samples, but never decrease */ + /* increase samples and render time, but never decrease */ session->set_samples(session_params.samples); - session->set_denoising_start_sample(session_params.denoising.start_sample); + session->set_time_limit(session_params.time_limit); session->set_pause(session_pause); /* copy recalc flags, outside of mutex so we can decide to do the real @@ -808,21 +825,12 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) sync->sync_camera(b_render, b_camera_override, width, height, ""); /* get buffer parameters */ - BufferParams buffer_params = BlenderSync::get_buffer_params( - b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); - - if (!buffer_params.denoising_data_pass) { - session_params.denoising.use = false; - } - - session->set_denoising(session_params.denoising); - - /* Update film if denoising data was enabled or disabled. */ - scene->film->set_denoising_data_pass(buffer_params.denoising_data_pass); + const BufferParams buffer_params = BlenderSync::get_buffer_params( + b_v3d, b_rv3d, scene->camera, width, height); /* reset if needed */ if (scene->need_reset()) { - session->reset(buffer_params, session_params.samples); + session->reset(session_params, buffer_params); /* After session reset, so device is not accessing image data anymore. */ builtin_images_load(); @@ -839,7 +847,41 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) session->start(); } -bool BlenderSession::draw(int w, int h) +void BlenderSession::draw(BL::SpaceImageEditor &space_image) +{ + if (!session || !session->scene) { + /* Offline render drawing does not force the render engine update, which means it's possible + * that the Session is not created yet. */ + return; + } + + thread_scoped_lock lock(draw_state_.mutex); + + const int pass_index = space_image.image_user().multilayer_pass(); + if (pass_index != draw_state_.last_pass_index) { + BL::RenderPass b_display_pass(b_engine.pass_by_index_get(b_rlay_name.c_str(), pass_index)); + if (!b_display_pass) { + return; + } + + Scene *scene = session->scene; + + thread_scoped_lock lock(scene->mutex); + + const Pass *pass = Pass::find(scene->passes, b_display_pass.name()); + if (!pass) { + return; + } + + scene->film->set_display_pass(pass->get_type()); + + draw_state_.last_pass_index = pass_index; + } + + session->draw(); +} + +void BlenderSession::view_draw(int w, int h) { /* pause in redraw in case update is not being called due to final render */ session->set_pause(BlenderSync::get_session_pause(b_scene, background)); @@ -885,14 +927,14 @@ bool BlenderSession::draw(int w, int h) /* reset if requested */ if (reset) { - SessionParams session_params = BlenderSync::get_session_params( + const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - BufferParams buffer_params = BlenderSync::get_buffer_params( - b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); - bool session_pause = BlenderSync::get_session_pause(b_scene, background); + const BufferParams buffer_params = BlenderSync::get_buffer_params( + b_v3d, b_rv3d, scene->camera, width, height); + const bool session_pause = BlenderSync::get_session_pause(b_scene, background); if (session_pause == false) { - session->reset(buffer_params, session_params.samples); + session->reset(session_params, buffer_params); start_resize_time = 0.0; } } @@ -905,18 +947,7 @@ bool BlenderSession::draw(int w, int h) update_status_progress(); /* draw */ - BufferParams buffer_params = BlenderSync::get_buffer_params( - b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); - DeviceDrawParams draw_params; - - if (session->params.display_buffer_linear) { - draw_params.bind_display_space_shader_cb = function_bind( - &BL::RenderEngine::bind_display_space_shader, &b_engine, b_scene); - draw_params.unbind_display_space_shader_cb = function_bind( - &BL::RenderEngine::unbind_display_space_shader, &b_engine); - } - - return !session->draw(buffer_params, draw_params); + session->draw(); } void BlenderSession::get_status(string &status, string &substatus) @@ -924,11 +955,6 @@ void BlenderSession::get_status(string &status, string &substatus) session->progress.get_status(status, substatus); } -void BlenderSession::get_kernel_status(string &kernel_status) -{ - session->progress.get_kernel_status(kernel_status); -} - void BlenderSession::get_progress(float &progress, double &total_time, double &render_time) { session->progress.get_time(total_time, render_time); @@ -947,7 +973,7 @@ void BlenderSession::update_bake_progress() void BlenderSession::update_status_progress() { - string timestatus, status, substatus, kernel_status; + string timestatus, status, substatus; string scene_status = ""; float progress; double total_time, remaining_time = 0, render_time; @@ -955,7 +981,6 @@ void BlenderSession::update_status_progress() float mem_peak = (float)session->stats.mem_peak / 1024.0f / 1024.0f; get_status(status, substatus); - get_kernel_status(kernel_status); get_progress(progress, total_time, render_time); if (progress > 0) @@ -980,14 +1005,12 @@ void BlenderSession::update_status_progress() status = " | " + status; if (substatus.size() > 0) status += " | " + substatus; - if (kernel_status.size() > 0) - status += " | " + kernel_status; } double current_time = time_dt(); - /* When rendering in a window, redraw the status at least once per second to keep the elapsed and - * remaining time up-to-date. For headless rendering, only report when something significant - * changes to keep the console output readable. */ + /* When rendering in a window, redraw the status at least once per second to keep the elapsed + * and remaining time up-to-date. For headless rendering, only report when something + * significant changes to keep the console output readable. */ if (status != last_status || (!headless && (current_time - last_status_time) > 1.0)) { b_engine.update_stats("", (timestatus + scene_status + status).c_str()); b_engine.update_memory_stats(mem_used, mem_peak); @@ -1048,56 +1071,6 @@ void BlenderSession::test_cancel() session->progress.set_cancel("Cancelled"); } -void BlenderSession::update_resumable_tile_manager(int num_samples) -{ - const int num_resumable_chunks = BlenderSession::num_resumable_chunks, - current_resumable_chunk = BlenderSession::current_resumable_chunk; - if (num_resumable_chunks == 0) { - return; - } - - if (num_resumable_chunks > num_samples) { - fprintf(stderr, - "Cycles warning: more sample chunks (%d) than samples (%d), " - "this will cause some samples to be included in multiple chunks.\n", - num_resumable_chunks, - num_samples); - } - - const float num_samples_per_chunk = (float)num_samples / num_resumable_chunks; - - float range_start_sample, range_num_samples; - if (current_resumable_chunk != 0) { - /* Single chunk rendering. */ - range_start_sample = num_samples_per_chunk * (current_resumable_chunk - 1); - range_num_samples = num_samples_per_chunk; - } - else { - /* Ranged-chunks. */ - const int num_chunks = end_resumable_chunk - start_resumable_chunk + 1; - range_start_sample = num_samples_per_chunk * (start_resumable_chunk - 1); - range_num_samples = num_chunks * num_samples_per_chunk; - } - - /* Round after doing the multiplications with num_chunks and num_samples_per_chunk - * to allow for many small chunks. */ - int rounded_range_start_sample = (int)floorf(range_start_sample + 0.5f); - int rounded_range_num_samples = max((int)floorf(range_num_samples + 0.5f), 1); - - /* Make sure we don't overshoot. */ - if (rounded_range_start_sample + rounded_range_num_samples > num_samples) { - rounded_range_num_samples = num_samples - rounded_range_num_samples; - } - - VLOG(1) << "Samples range start is " << range_start_sample << ", " - << "number of samples to render is " << range_num_samples; - - scene->integrator->set_start_sample(rounded_range_start_sample); - - session->tile_manager.range_start_sample = rounded_range_start_sample; - session->tile_manager.range_num_samples = rounded_range_num_samples; -} - void BlenderSession::free_blender_memory_if_possible() { if (!background) { diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index d967b81c854..cf52359ea5d 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -33,8 +33,6 @@ class BlenderSync; class ImageMetaData; class Scene; class Session; -class RenderBuffers; -class RenderTile; class BlenderSession { public: @@ -62,6 +60,8 @@ class BlenderSession { /* offline render */ void render(BL::Depsgraph &b_depsgraph); + void render_frame_finish(); + void bake(BL::Depsgraph &b_depsgrah, BL::Object &b_object, const string &pass_type, @@ -69,24 +69,29 @@ class BlenderSession { const int bake_width, const int bake_height); - void write_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile); - void write_render_tile(RenderTile &rtile); - void read_render_tile(RenderTile &rtile); + void write_render_result(BL::RenderLayer &b_rlay); + void write_render_tile(); + + void update_render_tile(); + + void full_buffer_written(string_view filename); /* update functions are used to update display buffer only after sample was rendered * only needed for better visual feedback */ - void update_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile); - void update_render_tile(RenderTile &rtile, bool highlight); + void update_render_result(BL::RenderLayer &b_rlay); + + /* read functions for baking input */ + void read_render_tile(); /* interactive updates */ void synchronize(BL::Depsgraph &b_depsgraph); /* drawing */ - bool draw(int w, int h); + void draw(BL::SpaceImageEditor &space_image); + void view_draw(int w, int h); void tag_redraw(); void tag_update(); void get_status(string &status, string &substatus); - void get_kernel_status(string &kernel_status); void get_progress(float &progress, double &total_time, double &render_time); void test_cancel(); void update_status_progress(); @@ -123,6 +128,8 @@ class BlenderSession { void *python_thread_state; + bool use_developer_ui; + /* Global state which is common for all render sessions created from Blender. * Usually denotes command line arguments. */ @@ -134,41 +141,25 @@ class BlenderSession { */ static bool headless; - /* ** Resumable render ** */ - - /* Overall number of chunks in which the sample range is to be divided. */ - static int num_resumable_chunks; - - /* Current resumable chunk index to render. */ - static int current_resumable_chunk; - - /* Alternative to single-chunk rendering to render a range of chunks. */ - static int start_resumable_chunk; - static int end_resumable_chunk; - static bool print_render_stats; protected: void stamp_view_layer_metadata(Scene *scene, const string &view_layer_name); - void do_write_update_render_result(BL::RenderLayer &b_rlay, - RenderTile &rtile, - bool do_update_only); - void do_write_update_render_tile(RenderTile &rtile, - bool do_update_only, - bool do_read_only, - bool highlight); - void builtin_images_load(); - /* Update tile manager to reflect resumable render settings. */ - void update_resumable_tile_manager(int num_samples); - /* Is used after each render layer synchronization is done with the goal * of freeing render engine data which is held from Blender side (for * example, dependency graph). */ void free_blender_memory_if_possible(); + + struct { + thread_mutex mutex; + int last_pass_index = -1; + } draw_state_; + + vector full_buffer_files_; }; CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index de7b2761d00..8c4f789ffd0 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -17,6 +17,7 @@ #include "render/background.h" #include "render/colorspace.h" #include "render/graph.h" +#include "render/integrator.h" #include "render/light.h" #include "render/nodes.h" #include "render/osl.h" @@ -475,17 +476,11 @@ static ShaderNode *add_node(Scene *scene, SubsurfaceScatteringNode *subsurface = graph->create_node(); switch (b_subsurface_node.falloff()) { - case BL::ShaderNodeSubsurfaceScattering::falloff_CUBIC: - subsurface->set_falloff(CLOSURE_BSSRDF_CUBIC_ID); - break; - case BL::ShaderNodeSubsurfaceScattering::falloff_GAUSSIAN: - subsurface->set_falloff(CLOSURE_BSSRDF_GAUSSIAN_ID); - break; - case BL::ShaderNodeSubsurfaceScattering::falloff_BURLEY: - subsurface->set_falloff(CLOSURE_BSSRDF_BURLEY_ID); + case BL::ShaderNodeSubsurfaceScattering::falloff_RANDOM_WALK_FIXED_RADIUS: + subsurface->set_method(CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); break; case BL::ShaderNodeSubsurfaceScattering::falloff_RANDOM_WALK: - subsurface->set_falloff(CLOSURE_BSSRDF_RANDOM_WALK_ID); + subsurface->set_method(CLOSURE_BSSRDF_RANDOM_WALK_ID); break; } @@ -597,11 +592,11 @@ static ShaderNode *add_node(Scene *scene, break; } switch (b_principled_node.subsurface_method()) { - case BL::ShaderNodeBsdfPrincipled::subsurface_method_BURLEY: - principled->set_subsurface_method(CLOSURE_BSSRDF_PRINCIPLED_ID); + case BL::ShaderNodeBsdfPrincipled::subsurface_method_RANDOM_WALK_FIXED_RADIUS: + principled->set_subsurface_method(CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); break; case BL::ShaderNodeBsdfPrincipled::subsurface_method_RANDOM_WALK: - principled->set_subsurface_method(CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID); + principled->set_subsurface_method(CLOSURE_BSSRDF_RANDOM_WALK_ID); break; } node = principled; @@ -1360,10 +1355,11 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all) void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all) { Background *background = scene->background; + Integrator *integrator = scene->integrator; BL::World b_world = b_scene.world(); - BlenderViewportParameters new_viewport_parameters(b_v3d); + BlenderViewportParameters new_viewport_parameters(b_v3d, use_developer_ui); if (world_recalc || update_all || b_world.ptr.data != world_map || viewport_parameters.shader_modified(new_viewport_parameters)) { @@ -1455,9 +1451,8 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, /* AO */ BL::WorldLighting b_light = b_world.light_settings(); - background->set_use_ao(b_light.use_ambient_occlusion()); - background->set_ao_factor(b_light.ao_factor()); - background->set_ao_distance(b_light.distance()); + integrator->set_ao_factor(b_light.ao_factor()); + integrator->set_ao_distance(b_light.distance()); /* visibility */ PointerRNA cvisibility = RNA_pointer_get(&b_world.ptr, "cycles_visibility"); @@ -1472,9 +1467,8 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, background->set_visibility(visibility); } else { - background->set_use_ao(false); - background->set_ao_factor(0.0f); - background->set_ao_distance(FLT_MAX); + integrator->set_ao_factor(1.0f); + integrator->set_ao_distance(10.0f); } shader->set_graph(graph); @@ -1496,7 +1490,6 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, background->set_use_shader(view_layer.use_background_shader || viewport_parameters.use_custom_shader()); - background->set_use_ao(background->get_use_ao() && view_layer.use_background_ao); background->tag_update(scene); } diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 26d64b7bf85..d6fc7ee1723 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -53,6 +53,7 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine, BL::Scene &b_scene, Scene *scene, bool preview, + bool use_developer_ui, Progress &progress) : b_engine(b_engine), b_data(b_data), @@ -68,6 +69,7 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine, scene(scene), preview(preview), experimental(false), + use_developer_ui(use_developer_ui), dicing_rate(1.0f), max_subdivisions(12), progress(progress), @@ -224,7 +226,7 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d } if (b_v3d) { - BlenderViewportParameters new_viewport_parameters(b_v3d); + BlenderViewportParameters new_viewport_parameters(b_v3d, use_developer_ui); if (viewport_parameters.shader_modified(new_viewport_parameters)) { world_recalc = true; @@ -251,9 +253,13 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render, BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval(); + /* TODO(sergey): This feels weak to pass view layer to the integrator, and even weaker to have an + * implicit check on whether it is a background render or not. What is the nicer thing here? */ + const bool background = !b_v3d; + sync_view_layer(b_view_layer); - sync_integrator(); - sync_film(b_v3d); + sync_integrator(b_view_layer, background); + sync_film(b_view_layer, b_v3d); sync_shaders(b_depsgraph, b_v3d); sync_images(); @@ -280,7 +286,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render, /* Integrator */ -void BlenderSync::sync_integrator() +void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background) { PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); @@ -328,59 +334,24 @@ void BlenderSync::sync_integrator() integrator->set_motion_blur(view_layer.use_motion_blur); } - integrator->set_method((Integrator::Method)get_enum( - cscene, "progressive", Integrator::NUM_METHODS, Integrator::PATH)); - - integrator->set_sample_all_lights_direct(get_boolean(cscene, "sample_all_lights_direct")); - integrator->set_sample_all_lights_indirect(get_boolean(cscene, "sample_all_lights_indirect")); integrator->set_light_sampling_threshold(get_float(cscene, "light_sampling_threshold")); SamplingPattern sampling_pattern = (SamplingPattern)get_enum( cscene, "sampling_pattern", SAMPLING_NUM_PATTERNS, SAMPLING_PATTERN_SOBOL); - - int adaptive_min_samples = INT_MAX; - - if (RNA_boolean_get(&cscene, "use_adaptive_sampling")) { - sampling_pattern = SAMPLING_PATTERN_PMJ; - adaptive_min_samples = get_int(cscene, "adaptive_min_samples"); - integrator->set_adaptive_threshold(get_float(cscene, "adaptive_threshold")); - } - else { - integrator->set_adaptive_threshold(0.0f); - } - integrator->set_sampling_pattern(sampling_pattern); - int diffuse_samples = get_int(cscene, "diffuse_samples"); - int glossy_samples = get_int(cscene, "glossy_samples"); - int transmission_samples = get_int(cscene, "transmission_samples"); - int ao_samples = get_int(cscene, "ao_samples"); - int mesh_light_samples = get_int(cscene, "mesh_light_samples"); - int subsurface_samples = get_int(cscene, "subsurface_samples"); - int volume_samples = get_int(cscene, "volume_samples"); - - if (get_boolean(cscene, "use_square_samples")) { - integrator->set_diffuse_samples(diffuse_samples * diffuse_samples); - integrator->set_glossy_samples(glossy_samples * glossy_samples); - integrator->set_transmission_samples(transmission_samples * transmission_samples); - integrator->set_ao_samples(ao_samples * ao_samples); - integrator->set_mesh_light_samples(mesh_light_samples * mesh_light_samples); - integrator->set_subsurface_samples(subsurface_samples * subsurface_samples); - integrator->set_volume_samples(volume_samples * volume_samples); - adaptive_min_samples = min(adaptive_min_samples * adaptive_min_samples, INT_MAX); + if (preview) { + integrator->set_use_adaptive_sampling( + RNA_boolean_get(&cscene, "use_preview_adaptive_sampling")); + integrator->set_adaptive_threshold(get_float(cscene, "preview_adaptive_threshold")); + integrator->set_adaptive_min_samples(get_int(cscene, "preview_adaptive_min_samples")); } else { - integrator->set_diffuse_samples(diffuse_samples); - integrator->set_glossy_samples(glossy_samples); - integrator->set_transmission_samples(transmission_samples); - integrator->set_ao_samples(ao_samples); - integrator->set_mesh_light_samples(mesh_light_samples); - integrator->set_subsurface_samples(subsurface_samples); - integrator->set_volume_samples(volume_samples); + integrator->set_use_adaptive_sampling(RNA_boolean_get(&cscene, "use_adaptive_sampling")); + integrator->set_adaptive_threshold(get_float(cscene, "adaptive_threshold")); + integrator->set_adaptive_min_samples(get_int(cscene, "adaptive_min_samples")); } - integrator->set_adaptive_min_samples(adaptive_min_samples); - if (get_boolean(cscene, "use_fast_gi")) { if (preview) { integrator->set_ao_bounces(get_int(cscene, "ao_bounces")); @@ -393,20 +364,38 @@ void BlenderSync::sync_integrator() integrator->set_ao_bounces(0); } - /* UPDATE_NONE as we don't want to tag the integrator as modified, just tag dependent things */ + const DenoiseParams denoise_params = get_denoise_params(b_scene, b_view_layer, background); + integrator->set_use_denoise(denoise_params.use); + + /* Only update denoiser parameters if the denoiser is actually used. This allows to tweak + * denoiser parameters before enabling it without render resetting on every change. The downside + * is that the interface and the integrator are technically out of sync. */ + if (denoise_params.use) { + integrator->set_denoiser_type(denoise_params.type); + integrator->set_denoise_start_sample(denoise_params.start_sample); + integrator->set_use_denoise_pass_albedo(denoise_params.use_pass_albedo); + integrator->set_use_denoise_pass_normal(denoise_params.use_pass_normal); + integrator->set_denoiser_prefilter(denoise_params.prefilter); + } + + /* UPDATE_NONE as we don't want to tag the integrator as modified (this was done by the + * set calls above), but we need to make sure that the dependent things are tagged. */ integrator->tag_update(scene, Integrator::UPDATE_NONE); } /* Film */ -void BlenderSync::sync_film(BL::SpaceView3D &b_v3d) +void BlenderSync::sync_film(BL::ViewLayer &b_view_layer, BL::SpaceView3D &b_v3d) { PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles"); Film *film = scene->film; if (b_v3d) { - film->set_display_pass(update_viewport_display_passes(b_v3d, scene->passes)); + const BlenderViewportParameters new_viewport_parameters(b_v3d, use_developer_ui); + film->set_display_pass(new_viewport_parameters.display_pass); + film->set_show_active_pixels(new_viewport_parameters.show_active_pixels); } film->set_exposure(get_float(cscene, "film_exposure")); @@ -434,6 +423,15 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d) break; } } + + /* Blender viewport does not support proper shadow catcher compositing, so force an approximate + * mode to improve visual feedback. */ + if (b_v3d) { + film->set_use_approximate_shadow_catcher(true); + } + else { + film->set_use_approximate_shadow_catcher(!get_boolean(crl, "use_pass_shadow_catcher")); + } } /* Render Layer */ @@ -444,7 +442,6 @@ void BlenderSync::sync_view_layer(BL::ViewLayer &b_view_layer) /* Filter. */ view_layer.use_background_shader = b_view_layer.use_sky(); - view_layer.use_background_ao = b_view_layer.use_ao(); /* Always enable surfaces for baking, otherwise there is nothing to bake to. */ view_layer.use_surfaces = b_view_layer.use_solid() || scene->bake_manager->get_baking(); view_layer.use_hair = b_view_layer.use_strand(); @@ -464,10 +461,7 @@ void BlenderSync::sync_view_layer(BL::ViewLayer &b_view_layer) if (use_layer_samples != 2) { int samples = b_view_layer.samples(); - if (get_boolean(cscene, "use_square_samples")) - view_layer.samples = samples * samples; - else - view_layer.samples = samples; + view_layer.samples = samples; } } @@ -499,7 +493,8 @@ void BlenderSync::sync_images() } /* Passes */ -PassType BlenderSync::get_pass_type(BL::RenderPass &b_pass) + +static PassType get_blender_pass_type(BL::RenderPass &b_pass) { string name = b_pass.name(); #define MAP_PASS(passname, passtype) \ @@ -507,10 +502,15 @@ PassType BlenderSync::get_pass_type(BL::RenderPass &b_pass) return passtype; \ } \ ((void)0) + /* NOTE: Keep in sync with defined names from DNA_scene_types.h */ + MAP_PASS("Combined", PASS_COMBINED); + MAP_PASS("Noisy Image", PASS_COMBINED); + MAP_PASS("Depth", PASS_DEPTH); MAP_PASS("Mist", PASS_MIST); + MAP_PASS("Position", PASS_POSITION); MAP_PASS("Normal", PASS_NORMAL); MAP_PASS("IndexOB", PASS_OBJECT_ID); MAP_PASS("UV", PASS_UV); @@ -539,118 +539,92 @@ PassType BlenderSync::get_pass_type(BL::RenderPass &b_pass) MAP_PASS("BakePrimitive", PASS_BAKE_PRIMITIVE); MAP_PASS("BakeDifferential", PASS_BAKE_DIFFERENTIAL); + MAP_PASS("Denoising Normal", PASS_DENOISING_NORMAL); + MAP_PASS("Denoising Albedo", PASS_DENOISING_ALBEDO); + + MAP_PASS("Shadow Catcher", PASS_SHADOW_CATCHER); + MAP_PASS("Noisy Shadow Catcher", PASS_SHADOW_CATCHER); + MAP_PASS("Debug Render Time", PASS_RENDER_TIME); + MAP_PASS("AdaptiveAuxBuffer", PASS_ADAPTIVE_AUX_BUFFER); MAP_PASS("Debug Sample Count", PASS_SAMPLE_COUNT); + if (string_startswith(name, cryptomatte_prefix)) { return PASS_CRYPTOMATTE; } + #undef MAP_PASS return PASS_NONE; } -int BlenderSync::get_denoising_pass(BL::RenderPass &b_pass) +static Pass *pass_add(Scene *scene, + PassType type, + const char *name, + PassMode mode = PassMode::DENOISED) { - string name = b_pass.name(); + Pass *pass = scene->create_node(); - if (name == "Noisy Image") - return DENOISING_PASS_PREFILTERED_COLOR; + pass->set_type(type); + pass->set_name(ustring(name)); + pass->set_mode(mode); - if (name.substr(0, 10) != "Denoising ") { - return -1; - } - name = name.substr(10); - -#define MAP_PASS(passname, offset) \ - if (name == passname) { \ - return offset; \ - } \ - ((void)0) - MAP_PASS("Normal", DENOISING_PASS_PREFILTERED_NORMAL); - MAP_PASS("Albedo", DENOISING_PASS_PREFILTERED_ALBEDO); - MAP_PASS("Depth", DENOISING_PASS_PREFILTERED_DEPTH); - MAP_PASS("Shadowing", DENOISING_PASS_PREFILTERED_SHADOWING); - MAP_PASS("Variance", DENOISING_PASS_PREFILTERED_VARIANCE); - MAP_PASS("Intensity", DENOISING_PASS_PREFILTERED_INTENSITY); - MAP_PASS("Clean", DENOISING_PASS_CLEAN); -#undef MAP_PASS - - return -1; + return pass; } -vector BlenderSync::sync_render_passes(BL::Scene &b_scene, - BL::RenderLayer &b_rlay, - BL::ViewLayer &b_view_layer, - bool adaptive_sampling, - const DenoiseParams &denoising) +void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_view_layer) { - vector passes; + PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); - /* loop over passes */ + /* Delete all existing passes. */ + set clear_passes(scene->passes.begin(), scene->passes.end()); + scene->delete_nodes(clear_passes); + + /* Always add combined pass. */ + pass_add(scene, PASS_COMBINED, "Combined"); + + /* Blender built-in data and light passes. */ for (BL::RenderPass &b_pass : b_rlay.passes) { - PassType pass_type = get_pass_type(b_pass); + const PassType pass_type = get_blender_pass_type(b_pass); + + if (pass_type == PASS_NONE) { + LOG(ERROR) << "Unknown pass " << b_pass.name(); + continue; + } if (pass_type == PASS_MOTION && (b_view_layer.use_motion_blur() && b_scene.render().use_motion_blur())) { continue; } - if (pass_type != PASS_NONE) - Pass::add(pass_type, passes, b_pass.name().c_str()); + + pass_add(scene, pass_type, b_pass.name().c_str()); } PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles"); - int denoising_flags = 0; - if (denoising.use || denoising.store_passes) { - if (denoising.type == DENOISER_NLM) { -#define MAP_OPTION(name, flag) \ - if (!get_boolean(crl, name)) { \ - denoising_flags |= flag; \ - } \ - ((void)0) - MAP_OPTION("denoising_diffuse_direct", DENOISING_CLEAN_DIFFUSE_DIR); - MAP_OPTION("denoising_diffuse_indirect", DENOISING_CLEAN_DIFFUSE_IND); - MAP_OPTION("denoising_glossy_direct", DENOISING_CLEAN_GLOSSY_DIR); - MAP_OPTION("denoising_glossy_indirect", DENOISING_CLEAN_GLOSSY_IND); - MAP_OPTION("denoising_transmission_direct", DENOISING_CLEAN_TRANSMISSION_DIR); - MAP_OPTION("denoising_transmission_indirect", DENOISING_CLEAN_TRANSMISSION_IND); -#undef MAP_OPTION - } - b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str()); - } - scene->film->set_denoising_flags(denoising_flags); - - if (denoising.store_passes) { - b_engine.add_pass("Denoising Normal", 3, "XYZ", b_view_layer.name().c_str()); - b_engine.add_pass("Denoising Albedo", 3, "RGB", b_view_layer.name().c_str()); - b_engine.add_pass("Denoising Depth", 1, "Z", b_view_layer.name().c_str()); - if (denoising.type == DENOISER_NLM) { - b_engine.add_pass("Denoising Shadowing", 1, "X", b_view_layer.name().c_str()); - b_engine.add_pass("Denoising Variance", 3, "RGB", b_view_layer.name().c_str()); - b_engine.add_pass("Denoising Intensity", 1, "X", b_view_layer.name().c_str()); - } - - if (scene->film->get_denoising_flags() & DENOISING_CLEAN_ALL_PASSES) { - b_engine.add_pass("Denoising Clean", 3, "RGB", b_view_layer.name().c_str()); - } - } - + /* Debug passes. */ if (get_boolean(crl, "pass_debug_render_time")) { b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_RENDER_TIME, passes, "Debug Render Time"); + pass_add(scene, PASS_RENDER_TIME, "Debug Render Time"); } if (get_boolean(crl, "pass_debug_sample_count")) { b_engine.add_pass("Debug Sample Count", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_SAMPLE_COUNT, passes, "Debug Sample Count"); + pass_add(scene, PASS_SAMPLE_COUNT, "Debug Sample Count"); } + + /* Cycles specific passes. */ if (get_boolean(crl, "use_pass_volume_direct")) { b_engine.add_pass("VolumeDir", 3, "RGB", b_view_layer.name().c_str()); - Pass::add(PASS_VOLUME_DIRECT, passes, "VolumeDir"); + pass_add(scene, PASS_VOLUME_DIRECT, "VolumeDir"); } if (get_boolean(crl, "use_pass_volume_indirect")) { b_engine.add_pass("VolumeInd", 3, "RGB", b_view_layer.name().c_str()); - Pass::add(PASS_VOLUME_INDIRECT, passes, "VolumeInd"); + pass_add(scene, PASS_VOLUME_INDIRECT, "VolumeInd"); + } + if (get_boolean(crl, "use_pass_shadow_catcher")) { + b_engine.add_pass("Shadow Catcher", 3, "RGB", b_view_layer.name().c_str()); + pass_add(scene, PASS_SHADOW_CATCHER, "Shadow Catcher"); } /* Cryptomatte stores two ID/weight pairs per RGBA layer. @@ -662,7 +636,7 @@ vector BlenderSync::sync_render_passes(BL::Scene &b_scene, for (int i = 0; i < crypto_depth; i++) { string passname = cryptomatte_prefix + string_printf("Object%02d", i); b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str()); - Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str()); + pass_add(scene, PASS_CRYPTOMATTE, passname.c_str()); } cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_OBJECT); } @@ -670,7 +644,7 @@ vector BlenderSync::sync_render_passes(BL::Scene &b_scene, for (int i = 0; i < crypto_depth; i++) { string passname = cryptomatte_prefix + string_printf("Material%02d", i); b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str()); - Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str()); + pass_add(scene, PASS_CRYPTOMATTE, passname.c_str()); } cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_MATERIAL); } @@ -678,22 +652,33 @@ vector BlenderSync::sync_render_passes(BL::Scene &b_scene, for (int i = 0; i < crypto_depth; i++) { string passname = cryptomatte_prefix + string_printf("Asset%02d", i); b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str()); - Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str()); + pass_add(scene, PASS_CRYPTOMATTE, passname.c_str()); } cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_ASSET); } - if (b_view_layer.use_pass_cryptomatte_accurate() && cryptomatte_passes != CRYPT_NONE) { - cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_ACCURATE); - } scene->film->set_cryptomatte_passes(cryptomatte_passes); - if (adaptive_sampling) { - Pass::add(PASS_ADAPTIVE_AUX_BUFFER, passes); - if (!get_boolean(crl, "pass_debug_sample_count")) { - Pass::add(PASS_SAMPLE_COUNT, passes); + /* Denoising passes. */ + const bool use_denoising = get_boolean(cscene, "use_denoising") && + get_boolean(crl, "use_denoising"); + const bool store_denoising_passes = get_boolean(crl, "denoising_store_passes"); + if (use_denoising) { + b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str()); + pass_add(scene, PASS_COMBINED, "Noisy Image", PassMode::NOISY); + if (get_boolean(crl, "use_pass_shadow_catcher")) { + b_engine.add_pass("Noisy Shadow Catcher", 3, "RGB", b_view_layer.name().c_str()); + pass_add(scene, PASS_SHADOW_CATCHER, "Noisy Shadow Catcher", PassMode::NOISY); } } + if (store_denoising_passes) { + b_engine.add_pass("Denoising Normal", 3, "XYZ", b_view_layer.name().c_str()); + pass_add(scene, PASS_DENOISING_NORMAL, "Denoising Normal", PassMode::NOISY); + b_engine.add_pass("Denoising Albedo", 3, "RGB", b_view_layer.name().c_str()); + pass_add(scene, PASS_DENOISING_ALBEDO, "Denoising Albedo", PassMode::NOISY); + } + + /* Custom AOV passes. */ BL::ViewLayer::aovs_iterator b_aov_iter; for (b_view_layer.aovs.begin(b_aov_iter); b_aov_iter != b_view_layer.aovs.end(); ++b_aov_iter) { BL::AOV b_aov(*b_aov_iter); @@ -706,28 +691,15 @@ vector BlenderSync::sync_render_passes(BL::Scene &b_scene, if (is_color) { b_engine.add_pass(name.c_str(), 4, "RGBA", b_view_layer.name().c_str()); - Pass::add(PASS_AOV_COLOR, passes, name.c_str()); + pass_add(scene, PASS_AOV_COLOR, name.c_str()); } else { b_engine.add_pass(name.c_str(), 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_AOV_VALUE, passes, name.c_str()); + pass_add(scene, PASS_AOV_VALUE, name.c_str()); } } - scene->film->set_denoising_data_pass(denoising.use || denoising.store_passes); - scene->film->set_denoising_clean_pass(scene->film->get_denoising_flags() & - DENOISING_CLEAN_ALL_PASSES); - scene->film->set_denoising_prefiltered_pass(denoising.store_passes && - denoising.type == DENOISER_NLM); scene->film->set_pass_alpha_threshold(b_view_layer.pass_alpha_threshold()); - - if (!Pass::equals(passes, scene->passes)) { - scene->film->tag_passes_update(scene, passes); - scene->film->tag_modified(); - scene->integrator->tag_update(scene, Integrator::UPDATE_ALL); - } - - return passes; } void BlenderSync::free_data_after_sync(BL::Depsgraph &b_depsgraph) @@ -773,9 +745,9 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background) params.shadingsystem = SHADINGSYSTEM_OSL; if (background || DebugFlags().viewport_static_bvh) - params.bvh_type = SceneParams::BVH_STATIC; + params.bvh_type = BVH_TYPE_STATIC; else - params.bvh_type = SceneParams::BVH_DYNAMIC; + params.bvh_type = BVH_TYPE_DYNAMIC; params.use_bvh_spatial_split = RNA_boolean_get(&cscene, "debug_use_spatial_splits"); params.use_bvh_unaligned_nodes = RNA_boolean_get(&cscene, "debug_use_hair_bvh"); @@ -818,8 +790,7 @@ bool BlenderSync::get_session_pause(BL::Scene &b_scene, bool background) SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, BL::Preferences &b_preferences, BL::Scene &b_scene, - bool background, - BL::ViewLayer b_view_layer) + bool background) { SessionParams params; PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); @@ -827,7 +798,8 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, /* feature set */ params.experimental = (get_enum(cscene, "feature_set") != 0); - /* Background */ + /* Headless and background rendering. */ + params.headless = BlenderSession::headless; params.background = background; /* Device */ @@ -836,111 +808,26 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, /* samples */ int samples = get_int(cscene, "samples"); - int aa_samples = get_int(cscene, "aa_samples"); int preview_samples = get_int(cscene, "preview_samples"); - int preview_aa_samples = get_int(cscene, "preview_aa_samples"); - if (get_boolean(cscene, "use_square_samples")) { - aa_samples = aa_samples * aa_samples; - preview_aa_samples = preview_aa_samples * preview_aa_samples; - - samples = samples * samples; - preview_samples = preview_samples * preview_samples; - } - - if (get_enum(cscene, "progressive") == 0 && params.device.has_branched_path) { - if (background) { - params.samples = aa_samples; - } - else { - params.samples = preview_aa_samples; - if (params.samples == 0) - params.samples = INT_MAX; - } + if (background) { + params.samples = samples; } else { - if (background) { - params.samples = samples; - } - else { - params.samples = preview_samples; - if (params.samples == 0) - params.samples = INT_MAX; - } + params.samples = preview_samples; + if (params.samples == 0) + params.samples = INT_MAX; } /* Clamp samples. */ params.samples = min(params.samples, Integrator::MAX_SAMPLES); - /* Adaptive sampling. */ - params.adaptive_sampling = RNA_boolean_get(&cscene, "use_adaptive_sampling"); - - /* tiles */ - const bool is_cpu = (params.device.type == DEVICE_CPU); - if (!is_cpu && !background) { - /* currently GPU could be much slower than CPU when using tiles, - * still need to be investigated, but meanwhile make it possible - * to work in viewport smoothly - */ - int debug_tile_size = get_int(cscene, "debug_tile_size"); - - params.tile_size = make_int2(debug_tile_size, debug_tile_size); - } - else { - int tile_x = b_engine.tile_x(); - int tile_y = b_engine.tile_y(); - - params.tile_size = make_int2(tile_x, tile_y); - } - - if ((BlenderSession::headless == false) && background) { - params.tile_order = (TileOrder)get_enum(cscene, "tile_order"); - } - else { - params.tile_order = TILE_BOTTOM_TO_TOP; - } - - /* Denoising */ - params.denoising = get_denoise_params(b_scene, b_view_layer, background); - - if (params.denoising.use) { - /* Add additional denoising devices if we are rendering and denoising - * with different devices. */ - params.device.add_denoising_devices(params.denoising.type); - - /* Check if denoiser is supported by device. */ - if (!(params.device.denoisers & params.denoising.type)) { - params.denoising.use = false; - } - } - /* Viewport Performance */ - params.start_resolution = get_int(cscene, "preview_start_resolution"); params.pixel_size = b_engine.get_preview_pixel_size(b_scene); - /* other parameters */ - params.cancel_timeout = (double)get_float(cscene, "debug_cancel_timeout"); - params.reset_timeout = (double)get_float(cscene, "debug_reset_timeout"); - params.text_timeout = (double)get_float(cscene, "debug_text_timeout"); - - /* progressive refine */ - BL::RenderSettings b_r = b_scene.render(); - params.progressive_refine = b_engine.is_preview() || - get_boolean(cscene, "use_progressive_refine"); - if (b_r.use_save_buffers() || params.adaptive_sampling) - params.progressive_refine = false; - if (background) { - if (params.progressive_refine) - params.progressive = true; - else - params.progressive = false; - - params.start_resolution = INT_MAX; params.pixel_size = 1; } - else - params.progressive = true; /* shading system - scene level needs full refresh */ const bool shadingsystem = RNA_boolean_get(&cscene, "shading_system"); @@ -950,19 +837,30 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, else if (shadingsystem == 1) params.shadingsystem = SHADINGSYSTEM_OSL; - /* Color management. */ - params.display_buffer_linear = b_engine.support_display_space_shader(b_scene); - - if (b_engine.is_preview()) { - /* For preview rendering we're using same timeout as - * blender's job update. - */ - params.progressive_update_timeout = 0.1; + /* Time limit. */ + if (background) { + params.time_limit = get_float(cscene, "time_limit"); + } + else { + /* For the viewport it kind of makes more sense to think in terms of the noise floor, which is + * usually higher than acceptable level for the final frame. */ + /* TODO: It might be useful to support time limit in the viewport as well, but needs some + * extra thoughts and input. */ + params.time_limit = 0.0; } + /* Profiling. */ params.use_profiling = params.device.has_profiling && !b_engine.is_preview() && background && BlenderSession::print_render_stats; + if (background) { + params.use_auto_tile = RNA_boolean_get(&cscene, "use_auto_tile"); + params.tile_size = get_int(cscene, "tile_size"); + } + else { + params.use_auto_tile = false; + } + return params; } @@ -970,33 +868,34 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene, BL::ViewLayer &b_view_layer, bool background) { + enum DenoiserInput { + DENOISER_INPUT_RGB = 1, + DENOISER_INPUT_RGB_ALBEDO = 2, + DENOISER_INPUT_RGB_ALBEDO_NORMAL = 3, + + DENOISER_INPUT_NUM, + }; + DenoiseParams denoising; PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + int input_passes = -1; + if (background) { /* Final Render Denoising */ denoising.use = get_boolean(cscene, "use_denoising"); denoising.type = (DenoiserType)get_enum(cscene, "denoiser", DENOISER_NUM, DENOISER_NONE); + denoising.prefilter = (DenoiserPrefilter)get_enum( + cscene, "denoising_prefilter", DENOISER_PREFILTER_NUM, DENOISER_PREFILTER_NONE); + + input_passes = (DenoiserInput)get_enum( + cscene, "denoising_input_passes", DENOISER_INPUT_NUM, DENOISER_INPUT_RGB_ALBEDO_NORMAL); if (b_view_layer) { PointerRNA clayer = RNA_pointer_get(&b_view_layer.ptr, "cycles"); if (!get_boolean(clayer, "use_denoising")) { denoising.use = false; } - - denoising.radius = get_int(clayer, "denoising_radius"); - denoising.strength = get_float(clayer, "denoising_strength"); - denoising.feature_strength = get_float(clayer, "denoising_feature_strength"); - denoising.relative_pca = get_boolean(clayer, "denoising_relative_pca"); - - denoising.input_passes = (DenoiserInput)get_enum( - clayer, - (denoising.type == DENOISER_OPTIX) ? "denoising_optix_input_passes" : - "denoising_openimagedenoise_input_passes", - DENOISER_INPUT_NUM, - DENOISER_INPUT_RGB_ALBEDO_NORMAL); - - denoising.store_passes = get_boolean(clayer, "denoising_store_passes"); } } else { @@ -1004,10 +903,12 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene, denoising.use = get_boolean(cscene, "use_preview_denoising"); denoising.type = (DenoiserType)get_enum( cscene, "preview_denoiser", DENOISER_NUM, DENOISER_NONE); + denoising.prefilter = (DenoiserPrefilter)get_enum( + cscene, "preview_denoising_prefilter", DENOISER_PREFILTER_NUM, DENOISER_PREFILTER_FAST); denoising.start_sample = get_int(cscene, "preview_denoising_start_sample"); - denoising.input_passes = (DenoiserInput)get_enum( - cscene, "preview_denoising_input_passes", DENOISER_INPUT_NUM, (int)denoising.input_passes); + input_passes = (DenoiserInput)get_enum( + cscene, "preview_denoising_input_passes", DENOISER_INPUT_NUM, DENOISER_INPUT_RGB_ALBEDO); /* Auto select fastest denoiser. */ if (denoising.type == DENOISER_NONE) { @@ -1023,6 +924,27 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene, } } + switch (input_passes) { + case DENOISER_INPUT_RGB: + denoising.use_pass_albedo = false; + denoising.use_pass_normal = false; + break; + + case DENOISER_INPUT_RGB_ALBEDO: + denoising.use_pass_albedo = true; + denoising.use_pass_normal = false; + break; + + case DENOISER_INPUT_RGB_ALBEDO_NORMAL: + denoising.use_pass_albedo = true; + denoising.use_pass_normal = true; + break; + + default: + LOG(ERROR) << "Unhandled input passes enum " << input_passes; + break; + } + return denoising; } diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index d25c0ce1bc3..786479ac0f8 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -60,6 +60,7 @@ class BlenderSync { BL::Scene &b_scene, Scene *scene, bool preview, + bool use_developer_ui, Progress &progress); ~BlenderSync(); @@ -75,12 +76,8 @@ class BlenderSync { int height, void **python_thread_state); void sync_view_layer(BL::ViewLayer &b_view_layer); - vector sync_render_passes(BL::Scene &b_scene, - BL::RenderLayer &b_render_layer, - BL::ViewLayer &b_view_layer, - bool adaptive_sampling, - const DenoiseParams &denoising); - void sync_integrator(); + void sync_render_passes(BL::RenderLayer &b_render_layer, BL::ViewLayer &b_view_layer); + void sync_integrator(BL::ViewLayer &b_view_layer, bool background); void sync_camera(BL::RenderSettings &b_render, BL::Object &b_override, int width, @@ -98,22 +95,13 @@ class BlenderSync { /* get parameters */ static SceneParams get_scene_params(BL::Scene &b_scene, bool background); - static SessionParams get_session_params( - BL::RenderEngine &b_engine, - BL::Preferences &b_userpref, - BL::Scene &b_scene, - bool background, - BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL)); + static SessionParams get_session_params(BL::RenderEngine &b_engine, + BL::Preferences &b_userpref, + BL::Scene &b_scene, + bool background); static bool get_session_pause(BL::Scene &b_scene, bool background); - static BufferParams get_buffer_params(BL::SpaceView3D &b_v3d, - BL::RegionView3D &b_rv3d, - Camera *cam, - int width, - int height, - const bool use_denoiser); - - static PassType get_pass_type(BL::RenderPass &b_pass); - static int get_denoising_pass(BL::RenderPass &b_pass); + static BufferParams get_buffer_params( + BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, int height); private: static DenoiseParams get_denoise_params(BL::Scene &b_scene, @@ -131,7 +119,7 @@ class BlenderSync { int width, int height, void **python_thread_state); - void sync_film(BL::SpaceView3D &b_v3d); + void sync_film(BL::ViewLayer &b_view_layer, BL::SpaceView3D &b_v3d); void sync_view(); /* Shader */ @@ -245,6 +233,7 @@ class BlenderSync { Scene *scene; bool preview; bool experimental; + bool use_developer_ui; float dicing_rate; int max_subdivisions; @@ -253,7 +242,6 @@ class BlenderSync { RenderLayerInfo() : material_override(PointerRNA_NULL), use_background_shader(true), - use_background_ao(true), use_surfaces(true), use_hair(true), use_volumes(true), @@ -266,7 +254,6 @@ class BlenderSync { string name; BL::Material material_override; bool use_background_shader; - bool use_background_ao; bool use_surfaces; bool use_hair; bool use_volumes; diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp index 18bdfc74de0..62e32240bba 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/blender_viewport.cpp @@ -17,6 +17,8 @@ #include "blender_viewport.h" #include "blender_util.h" +#include "render/pass.h" +#include "util/util_logging.h" CCL_NAMESPACE_BEGIN @@ -26,11 +28,12 @@ BlenderViewportParameters::BlenderViewportParameters() studiolight_rotate_z(0.0f), studiolight_intensity(1.0f), studiolight_background_alpha(1.0f), - display_pass(PASS_COMBINED) + display_pass(PASS_COMBINED), + show_active_pixels(false) { } -BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d) +BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d, bool use_developer_ui) : BlenderViewportParameters() { if (!b_v3d) { @@ -55,7 +58,25 @@ BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d) } /* Film. */ - display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1); + + /* Lookup display pass based on the enum identifier. + * This is because integer values of python enum are not aligned with the passes definition in + * the kernel. */ + + display_pass = PASS_COMBINED; + + const string display_pass_identifier = get_enum_identifier(cshading, "render_pass"); + if (!display_pass_identifier.empty()) { + const ustring pass_type_identifier(string_to_lower(display_pass_identifier)); + const NodeEnum *pass_type_enum = Pass::get_type_enum(); + if (pass_type_enum->exists(pass_type_identifier)) { + display_pass = static_cast((*pass_type_enum)[pass_type_identifier]); + } + } + + if (use_developer_ui) { + show_active_pixels = get_boolean(cshading, "show_active_pixels"); + } } bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters &other) const @@ -69,7 +90,7 @@ bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters bool BlenderViewportParameters::film_modified(const BlenderViewportParameters &other) const { - return display_pass != other.display_pass; + return display_pass != other.display_pass || show_active_pixels != other.show_active_pixels; } bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const @@ -82,18 +103,4 @@ bool BlenderViewportParameters::use_custom_shader() const return !(use_scene_world && use_scene_lights); } -PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector &passes) -{ - if (b_v3d) { - const BlenderViewportParameters viewport_parameters(b_v3d); - const PassType display_pass = viewport_parameters.display_pass; - - passes.clear(); - Pass::add(display_pass, passes); - - return display_pass; - } - return PASS_NONE; -} - CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h index d6518597053..b5adafc30c9 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/blender_viewport.h @@ -39,9 +39,10 @@ class BlenderViewportParameters { /* Film. */ PassType display_pass; + bool show_active_pixels; BlenderViewportParameters(); - explicit BlenderViewportParameters(BL::SpaceView3D &b_v3d); + BlenderViewportParameters(BL::SpaceView3D &b_v3d, bool use_developer_ui); /* Check whether any of shading related settings are different from the given parameters. */ bool shader_modified(const BlenderViewportParameters &other) const; @@ -57,8 +58,6 @@ class BlenderViewportParameters { bool use_custom_shader() const; }; -PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector &passes); - CCL_NAMESPACE_END #endif diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp index 048c2b95e40..d3497f3a8d8 100644 --- a/intern/cycles/bvh/bvh_build.cpp +++ b/intern/cycles/bvh/bvh_build.cpp @@ -832,18 +832,18 @@ BVHNode *BVHBuild::create_leaf_node(const BVHRange &range, const vector LeafTimeStackAllocator; typedef StackAllocator<256, BVHReference> LeafReferenceStackAllocator; - vector p_type[PRIMITIVE_NUM_TOTAL]; - vector p_index[PRIMITIVE_NUM_TOTAL]; - vector p_object[PRIMITIVE_NUM_TOTAL]; - vector p_time[PRIMITIVE_NUM_TOTAL]; - vector p_ref[PRIMITIVE_NUM_TOTAL]; + vector p_type[PRIMITIVE_NUM]; + vector p_index[PRIMITIVE_NUM]; + vector p_object[PRIMITIVE_NUM]; + vector p_time[PRIMITIVE_NUM]; + vector p_ref[PRIMITIVE_NUM]; /* TODO(sergey): In theory we should be able to store references. */ vector object_references; - uint visibility[PRIMITIVE_NUM_TOTAL] = {0}; + uint visibility[PRIMITIVE_NUM] = {0}; /* NOTE: Keep initialization in sync with actual number of primitives. */ - BoundBox bounds[PRIMITIVE_NUM_TOTAL] = { + BoundBox bounds[PRIMITIVE_NUM] = { BoundBox::empty, BoundBox::empty, BoundBox::empty, BoundBox::empty}; int ob_num = 0; int num_new_prims = 0; @@ -877,7 +877,7 @@ BVHNode *BVHBuild::create_leaf_node(const BVHRange &range, const vector local_prim_type, local_prim_index, local_prim_object; @@ -888,7 +888,7 @@ BVHNode *BVHBuild::create_leaf_node(const BVHRange &range, const vectorray; RTCHit *hit = (RTCHit *)args->hit; CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt; - KernelGlobals *kg = ctx->kg; + const KernelGlobals *kg = ctx->kg; switch (ctx->type) { case CCLIntersectContext::RAY_SHADOW_ALL: { - /* Append the intersection to the end of the array. */ - if (ctx->num_hits < ctx->max_hits) { - Intersection current_isect; - kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - for (size_t i = 0; i < ctx->max_hits; ++i) { + Intersection current_isect; + kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); + + /* If no transparent shadows, all light is blocked. */ + const int flags = intersection_get_shader_flags(kg, ¤t_isect); + if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->max_hits == 0) { + ctx->opaque_hit = true; + return; + } + + /* Test if we need to record this transparent intersection. */ + if (ctx->num_hits < ctx->max_hits || ray->tfar < ctx->max_t) { + /* Skip already recorded intersections. */ + int num_recorded_hits = min(ctx->num_hits, ctx->max_hits); + + for (int i = 0; i < num_recorded_hits; ++i) { if (current_isect.object == ctx->isect_s[i].object && current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) { /* This intersection was already recorded, skip it. */ *args->valid = 0; - break; + return; } } - Intersection *isect = &ctx->isect_s[ctx->num_hits]; - ++ctx->num_hits; - *isect = current_isect; - int prim = kernel_tex_fetch(__prim_index, isect->prim); - int shader = 0; - if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) { - shader = kernel_tex_fetch(__tri_shader, prim); - } - else { - float4 str = kernel_tex_fetch(__curves, prim); - shader = __float_as_int(str.z); - } - int flag = kernel_tex_fetch(__shaders, shader & SHADER_MASK).flags; - /* If no transparent shadows, all light is blocked. */ - if (flag & (SD_HAS_TRANSPARENT_SHADOW)) { - /* This tells Embree to continue tracing. */ - *args->valid = 0; + + /* If maximum number of hits was reached, replace the intersection with the + * highest distance. We want to find the N closest intersections. */ + int isect_index = num_recorded_hits; + if (num_recorded_hits + 1 >= ctx->max_hits) { + float max_t = ctx->isect_s[0].t; + int max_recorded_hit = 0; + + for (int i = 1; i < num_recorded_hits; ++i) { + if (ctx->isect_s[i].t > max_t) { + max_recorded_hit = i; + max_t = ctx->isect_s[i].t; + } + } + + if (num_recorded_hits >= ctx->max_hits) { + isect_index = max_recorded_hit; + } + + /* Limit the ray distance and stop counting hits beyond this. + * TODO: is there some way we can tell Embree to stop intersecting beyond + * this distance when max number of hits is reached?. Or maybe it will + * become irrelevant if we make max_hits a very high number on the CPU. */ + ctx->max_t = max(current_isect.t, max_t); } + + ctx->isect_s[isect_index] = current_isect; } - else { - /* Increase the number of hits beyond ray.max_hits - * so that the caller can detect this as opaque. */ - ++ctx->num_hits; - } + + /* Always increase the number of hits, even beyond ray.max_hits so that + * the caller can detect this as and consider it opaque, or trace another + * ray. */ + ++ctx->num_hits; + + /* This tells Embree to continue tracing. */ + *args->valid = 0; break; } case CCLIntersectContext::RAY_LOCAL: @@ -329,7 +352,7 @@ void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_) scene = NULL; } - const bool dynamic = params.bvh_type == SceneParams::BVH_DYNAMIC; + const bool dynamic = params.bvh_type == BVH_TYPE_DYNAMIC; scene = rtcNewScene(rtc_device); const RTCSceneFlags scene_flags = (dynamic ? RTC_SCENE_FLAG_DYNAMIC : RTC_SCENE_FLAG_NONE) | diff --git a/intern/cycles/bvh/bvh_params.h b/intern/cycles/bvh/bvh_params.h index 2dc10f30363..31b3971c110 100644 --- a/intern/cycles/bvh/bvh_params.h +++ b/intern/cycles/bvh/bvh_params.h @@ -31,6 +31,27 @@ CCL_NAMESPACE_BEGIN */ typedef KernelBVHLayout BVHLayout; +/* Type of BVH, in terms whether it is supported dynamic updates of meshes + * or whether modifying geometry requires full BVH rebuild. + */ +enum BVHType { + /* BVH supports dynamic updates of geometry. + * + * Faster for updating BVH tree when doing modifications in viewport, + * but slower for rendering. + */ + BVH_TYPE_DYNAMIC = 0, + /* BVH tree is calculated for specific scene, updates in geometry + * requires full tree rebuild. + * + * Slower to update BVH tree when modifying objects in viewport, also + * slower to build final BVH tree but gives best possible render speed. + */ + BVH_TYPE_STATIC = 1, + + BVH_NUM_TYPES, +}; + /* Names bitflag type to denote which BVH layouts are supported by * particular area. * diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake index 04ff598621a..da259171844 100644 --- a/intern/cycles/cmake/external_libs.cmake +++ b/intern/cycles/cmake/external_libs.cmake @@ -287,9 +287,6 @@ if(CYCLES_STANDALONE_REPOSITORY) endif() set(__boost_packages filesystem regex system thread date_time) - if(WITH_CYCLES_NETWORK) - list(APPEND __boost_packages serialization) - endif() if(WITH_CYCLES_OSL) list(APPEND __boost_packages wave) endif() diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt index 928249931a3..d18f4360aef 100644 --- a/intern/cycles/device/CMakeLists.txt +++ b/intern/cycles/device/CMakeLists.txt @@ -36,49 +36,70 @@ endif() set(SRC device.cpp - device_cpu.cpp - device_cuda.cpp - device_denoising.cpp - device_dummy.cpp + device_denoise.cpp + device_graphics_interop.cpp + device_kernel.cpp device_memory.cpp - device_multi.cpp - device_opencl.cpp - device_optix.cpp - device_split_kernel.cpp - device_task.cpp + device_queue.cpp +) + +set(SRC_CPU + cpu/device.cpp + cpu/device.h + cpu/device_impl.cpp + cpu/device_impl.h + cpu/kernel.cpp + cpu/kernel.h + cpu/kernel_function.h + cpu/kernel_thread_globals.cpp + cpu/kernel_thread_globals.h ) set(SRC_CUDA - cuda/device_cuda.h - cuda/device_cuda_impl.cpp + cuda/device.cpp + cuda/device.h + cuda/device_impl.cpp + cuda/device_impl.h + cuda/graphics_interop.cpp + cuda/graphics_interop.h + cuda/kernel.cpp + cuda/kernel.h + cuda/queue.cpp + cuda/queue.h + cuda/util.cpp + cuda/util.h ) -set(SRC_OPENCL - opencl/device_opencl.h - opencl/device_opencl_impl.cpp - opencl/memory_manager.h - opencl/memory_manager.cpp - opencl/opencl_util.cpp +set(SRC_DUMMY + dummy/device.cpp + dummy/device.h ) -if(WITH_CYCLES_NETWORK) - list(APPEND SRC - device_network.cpp - ) -endif() +set(SRC_MULTI + multi/device.cpp + multi/device.h +) + +set(SRC_OPTIX + optix/device.cpp + optix/device.h + optix/device_impl.cpp + optix/device_impl.h + optix/queue.cpp + optix/queue.h + optix/util.h +) set(SRC_HEADERS device.h - device_denoising.h + device_denoise.h + device_graphics_interop.h device_memory.h - device_intern.h - device_network.h - device_split_kernel.h - device_task.h + device_kernel.h + device_queue.h ) set(LIB - cycles_render cycles_kernel cycles_util ${CYCLES_GL_LIBRARIES} @@ -95,15 +116,7 @@ else() endif() add_definitions(${GL_DEFINITIONS}) -if(WITH_CYCLES_NETWORK) - add_definitions(-DWITH_NETWORK) -endif() -if(WITH_CYCLES_DEVICE_OPENCL) - list(APPEND LIB - extern_clew - ) - add_definitions(-DWITH_OPENCL) -endif() + if(WITH_CYCLES_DEVICE_CUDA) add_definitions(-DWITH_CUDA) endif() @@ -115,18 +128,27 @@ if(WITH_CYCLES_DEVICE_MULTI) endif() if(WITH_OPENIMAGEDENOISE) - add_definitions(-DWITH_OPENIMAGEDENOISE) - add_definitions(-DOIDN_STATIC_LIB) - list(APPEND INC_SYS - ${OPENIMAGEDENOISE_INCLUDE_DIRS} - ) list(APPEND LIB ${OPENIMAGEDENOISE_LIBRARIES} - ${TBB_LIBRARIES} ) endif() include_directories(${INC}) include_directories(SYSTEM ${INC_SYS}) -cycles_add_library(cycles_device "${LIB}" ${SRC} ${SRC_CUDA} ${SRC_OPENCL} ${SRC_HEADERS}) +cycles_add_library(cycles_device "${LIB}" + ${SRC} + ${SRC_CPU} + ${SRC_CUDA} + ${SRC_DUMMY} + ${SRC_MULTI} + ${SRC_OPTIX} + ${SRC_HEADERS} +) + +source_group("cpu" FILES ${SRC_CPU}) +source_group("cuda" FILES ${SRC_CUDA}) +source_group("dummy" FILES ${SRC_DUMMY}) +source_group("multi" FILES ${SRC_MULTI}) +source_group("optix" FILES ${SRC_OPTIX}) +source_group("common" FILES ${SRC} ${SRC_HEADERS}) diff --git a/intern/cycles/device/cpu/device.cpp b/intern/cycles/device/cpu/device.cpp new file mode 100644 index 00000000000..68ca8e8bb22 --- /dev/null +++ b/intern/cycles/device/cpu/device.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/cpu/device.h" +#include "device/cpu/device_impl.h" + +/* Used for `info.denoisers`. */ +/* TODO(sergey): The denoisers are probably to be moved completely out of the device into their + * own class. But until then keep API consistent with how it used to work before. */ +#include "util/util_openimagedenoise.h" + +CCL_NAMESPACE_BEGIN + +Device *device_cpu_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) +{ + return new CPUDevice(info, stats, profiler); +} + +void device_cpu_info(vector &devices) +{ + DeviceInfo info; + + info.type = DEVICE_CPU; + info.description = system_cpu_brand_string(); + info.id = "CPU"; + info.num = 0; + info.has_osl = true; + info.has_half_images = true; + info.has_nanovdb = true; + info.has_profiling = true; + if (openimagedenoise_supported()) { + info.denoisers |= DENOISER_OPENIMAGEDENOISE; + } + + devices.insert(devices.begin(), info); +} + +string device_cpu_capabilities() +{ + string capabilities = ""; + capabilities += system_cpu_support_sse2() ? "SSE2 " : ""; + capabilities += system_cpu_support_sse3() ? "SSE3 " : ""; + capabilities += system_cpu_support_sse41() ? "SSE41 " : ""; + capabilities += system_cpu_support_avx() ? "AVX " : ""; + capabilities += system_cpu_support_avx2() ? "AVX2" : ""; + if (capabilities[capabilities.size() - 1] == ' ') + capabilities.resize(capabilities.size() - 1); + return capabilities; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_buffer_update.cl b/intern/cycles/device/cpu/device.h similarity index 59% rename from intern/cycles/kernel/kernels/opencl/kernel_buffer_update.cl rename to intern/cycles/device/cpu/device.h index dcea2630aef..9cb2e80068d 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_buffer_update.cl +++ b/intern/cycles/device/cpu/device.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,22 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_buffer_update.h" +#pragma once -#define KERNEL_NAME buffer_update -#define LOCALS_TYPE unsigned int -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE +#include "util/util_string.h" +#include "util/util_vector.h" +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +Device *device_cpu_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +void device_cpu_info(vector &devices); + +string device_cpu_capabilities(); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp new file mode 100644 index 00000000000..3b0db6bdd0e --- /dev/null +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -0,0 +1,481 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/cpu/device_impl.h" + +#include +#include + +/* So ImathMath is included before our kernel_cpu_compat. */ +#ifdef WITH_OSL +/* So no context pollution happens from indirectly included windows.h */ +# include "util/util_windows.h" +# include +#endif + +#ifdef WITH_EMBREE +# include +#endif + +#include "device/cpu/kernel.h" +#include "device/cpu/kernel_thread_globals.h" + +#include "device/device.h" + +// clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" +#include "kernel/device/cpu/kernel.h" +#include "kernel/kernel_types.h" + +#include "kernel/osl/osl_shader.h" +#include "kernel/osl/osl_globals.h" +// clang-format on + +#include "bvh/bvh_embree.h" + +#include "render/buffers.h" + +#include "util/util_debug.h" +#include "util/util_foreach.h" +#include "util/util_function.h" +#include "util/util_logging.h" +#include "util/util_map.h" +#include "util/util_opengl.h" +#include "util/util_openimagedenoise.h" +#include "util/util_optimization.h" +#include "util/util_progress.h" +#include "util/util_system.h" +#include "util/util_task.h" +#include "util/util_thread.h" + +CCL_NAMESPACE_BEGIN + +CPUDevice::CPUDevice(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_) + : Device(info_, stats_, profiler_), texture_info(this, "__texture_info", MEM_GLOBAL) +{ + /* Pick any kernel, all of them are supposed to have same level of microarchitecture + * optimization. */ + VLOG(1) << "Will be using " << kernels.integrator_init_from_camera.get_uarch_name() + << " kernels."; + + if (info.cpu_threads == 0) { + info.cpu_threads = TaskScheduler::num_threads(); + } + +#ifdef WITH_OSL + kernel_globals.osl = &osl_globals; +#endif +#ifdef WITH_EMBREE + embree_device = rtcNewDevice("verbose=0"); +#endif + need_texture_info = false; +} + +CPUDevice::~CPUDevice() +{ +#ifdef WITH_EMBREE + rtcReleaseDevice(embree_device); +#endif + + texture_info.free(); +} + +bool CPUDevice::show_samples() const +{ + return (info.cpu_threads == 1); +} + +BVHLayoutMask CPUDevice::get_bvh_layout_mask() const +{ + BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_BVH2; +#ifdef WITH_EMBREE + bvh_layout_mask |= BVH_LAYOUT_EMBREE; +#endif /* WITH_EMBREE */ + return bvh_layout_mask; +} + +bool CPUDevice::load_texture_info() +{ + if (!need_texture_info) { + return false; + } + + texture_info.copy_to_device(); + need_texture_info = false; + + return true; +} + +void CPUDevice::mem_alloc(device_memory &mem) +{ + if (mem.type == MEM_TEXTURE) { + assert(!"mem_alloc not supported for textures."); + } + else if (mem.type == MEM_GLOBAL) { + assert(!"mem_alloc not supported for global memory."); + } + else { + if (mem.name) { + VLOG(1) << "Buffer allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; + } + + if (mem.type == MEM_DEVICE_ONLY) { + assert(!mem.host_pointer); + size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES; + void *data = util_aligned_malloc(mem.memory_size(), alignment); + mem.device_pointer = (device_ptr)data; + } + else { + mem.device_pointer = (device_ptr)mem.host_pointer; + } + + mem.device_size = mem.memory_size(); + stats.mem_alloc(mem.device_size); + } +} + +void CPUDevice::mem_copy_to(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + global_alloc(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + tex_alloc((device_texture &)mem); + } + else { + if (!mem.device_pointer) { + mem_alloc(mem); + } + + /* copy is no-op */ + } +} + +void CPUDevice::mem_copy_from( + device_memory & /*mem*/, int /*y*/, int /*w*/, int /*h*/, int /*elem*/) +{ + /* no-op */ +} + +void CPUDevice::mem_zero(device_memory &mem) +{ + if (!mem.device_pointer) { + mem_alloc(mem); + } + + if (mem.device_pointer) { + memset((void *)mem.device_pointer, 0, mem.memory_size()); + } +} + +void CPUDevice::mem_free(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + } + else if (mem.device_pointer) { + if (mem.type == MEM_DEVICE_ONLY) { + util_aligned_free((void *)mem.device_pointer); + } + mem.device_pointer = 0; + stats.mem_free(mem.device_size); + mem.device_size = 0; + } +} + +device_ptr CPUDevice::mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) +{ + return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); +} + +void CPUDevice::const_copy_to(const char *name, void *host, size_t size) +{ +#if WITH_EMBREE + if (strcmp(name, "__data") == 0) { + assert(size <= sizeof(KernelData)); + + // Update scene handle (since it is different for each device on multi devices) + KernelData *const data = (KernelData *)host; + data->bvh.scene = embree_scene; + } +#endif + kernel_const_copy(&kernel_globals, name, host, size); +} + +void CPUDevice::global_alloc(device_memory &mem) +{ + VLOG(1) << "Global memory allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; + + kernel_global_memory_copy(&kernel_globals, mem.name, mem.host_pointer, mem.data_size); + + mem.device_pointer = (device_ptr)mem.host_pointer; + mem.device_size = mem.memory_size(); + stats.mem_alloc(mem.device_size); +} + +void CPUDevice::global_free(device_memory &mem) +{ + if (mem.device_pointer) { + mem.device_pointer = 0; + stats.mem_free(mem.device_size); + mem.device_size = 0; + } +} + +void CPUDevice::tex_alloc(device_texture &mem) +{ + VLOG(1) << "Texture allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; + + mem.device_pointer = (device_ptr)mem.host_pointer; + mem.device_size = mem.memory_size(); + stats.mem_alloc(mem.device_size); + + const uint slot = mem.slot; + if (slot >= texture_info.size()) { + /* Allocate some slots in advance, to reduce amount of re-allocations. */ + texture_info.resize(slot + 128); + } + + texture_info[slot] = mem.info; + texture_info[slot].data = (uint64_t)mem.host_pointer; + need_texture_info = true; +} + +void CPUDevice::tex_free(device_texture &mem) +{ + if (mem.device_pointer) { + mem.device_pointer = 0; + stats.mem_free(mem.device_size); + mem.device_size = 0; + need_texture_info = true; + } +} + +void CPUDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) +{ +#ifdef WITH_EMBREE + if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE || + bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) { + BVHEmbree *const bvh_embree = static_cast(bvh); + if (refit) { + bvh_embree->refit(progress); + } + else { + bvh_embree->build(progress, &stats, embree_device); + } + + if (bvh->params.top_level) { + embree_scene = bvh_embree->scene; + } + } + else +#endif + Device::build_bvh(bvh, progress, refit); +} + +#if 0 +void CPUDevice::render(DeviceTask &task, RenderTile &tile, KernelGlobals *kg) +{ + const bool use_coverage = kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE; + + scoped_timer timer(&tile.buffers->render_time); + + Coverage coverage(kg, tile); + if (use_coverage) { + coverage.init_path_trace(); + } + + float *render_buffer = (float *)tile.buffer; + int start_sample = tile.start_sample; + int end_sample = tile.start_sample + tile.num_samples; + + /* Needed for Embree. */ + SIMD_SET_FLUSH_TO_ZERO; + + for (int sample = start_sample; sample < end_sample; sample++) { + if (task.get_cancel() || TaskPool::canceled()) { + if (task.need_finish_queue == false) + break; + } + + if (tile.stealing_state == RenderTile::CAN_BE_STOLEN && task.get_tile_stolen()) { + tile.stealing_state = RenderTile::WAS_STOLEN; + break; + } + + if (tile.task == RenderTile::PATH_TRACE) { + for (int y = tile.y; y < tile.y + tile.h; y++) { + for (int x = tile.x; x < tile.x + tile.w; x++) { + if (use_coverage) { + coverage.init_pixel(x, y); + } + kernels.path_trace(kg, render_buffer, sample, x, y, tile.offset, tile.stride); + } + } + } + else { + for (int y = tile.y; y < tile.y + tile.h; y++) { + for (int x = tile.x; x < tile.x + tile.w; x++) { + kernels.bake(kg, render_buffer, sample, x, y, tile.offset, tile.stride); + } + } + } + tile.sample = sample + 1; + + if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(sample)) { + const bool stop = adaptive_sampling_filter(kg, tile, sample); + if (stop) { + const int num_progress_samples = end_sample - sample; + tile.sample = end_sample; + task.update_progress(&tile, tile.w * tile.h * num_progress_samples); + break; + } + } + + task.update_progress(&tile, tile.w * tile.h); + } + if (use_coverage) { + coverage.finalize(); + } + + if (task.adaptive_sampling.use && (tile.stealing_state != RenderTile::WAS_STOLEN)) { + adaptive_sampling_post(tile, kg); + } +} + +void CPUDevice::thread_render(DeviceTask &task) +{ + if (TaskPool::canceled()) { + if (task.need_finish_queue == false) + return; + } + + /* allocate buffer for kernel globals */ + CPUKernelThreadGlobals kg(kernel_globals, get_cpu_osl_memory()); + + profiler.add_state(&kg.profiler); + + /* NLM denoiser. */ + DenoisingTask *denoising = NULL; + + /* OpenImageDenoise: we can only denoise with one thread at a time, so to + * avoid waiting with mutex locks in the denoiser, we let only a single + * thread acquire denoising tiles. */ + uint tile_types = task.tile_types; + bool hold_denoise_lock = false; + if ((tile_types & RenderTile::DENOISE) && task.denoising.type == DENOISER_OPENIMAGEDENOISE) { + if (!oidn_task_lock.try_lock()) { + tile_types &= ~RenderTile::DENOISE; + hold_denoise_lock = true; + } + } + + RenderTile tile; + while (task.acquire_tile(this, tile, tile_types)) { + if (tile.task == RenderTile::PATH_TRACE) { + render(task, tile, &kg); + } + else if (tile.task == RenderTile::BAKE) { + render(task, tile, &kg); + } + else if (tile.task == RenderTile::DENOISE) { + denoise_openimagedenoise(task, tile); + task.update_progress(&tile, tile.w * tile.h); + } + + task.release_tile(tile); + + if (TaskPool::canceled()) { + if (task.need_finish_queue == false) + break; + } + } + + if (hold_denoise_lock) { + oidn_task_lock.unlock(); + } + + profiler.remove_state(&kg.profiler); + + delete denoising; +} + +void CPUDevice::thread_denoise(DeviceTask &task) +{ + RenderTile tile; + tile.x = task.x; + tile.y = task.y; + tile.w = task.w; + tile.h = task.h; + tile.buffer = task.buffer; + tile.sample = task.sample + task.num_samples; + tile.num_samples = task.num_samples; + tile.start_sample = task.sample; + tile.offset = task.offset; + tile.stride = task.stride; + tile.buffers = task.buffers; + + denoise_openimagedenoise(task, tile); + + task.update_progress(&tile, tile.w * tile.h); +} +#endif + +const CPUKernels *CPUDevice::get_cpu_kernels() const +{ + return &kernels; +} + +void CPUDevice::get_cpu_kernel_thread_globals( + vector &kernel_thread_globals) +{ + /* Ensure latest texture info is loaded into kernel globals before returning. */ + load_texture_info(); + + kernel_thread_globals.clear(); + void *osl_memory = get_cpu_osl_memory(); + for (int i = 0; i < info.cpu_threads; i++) { + kernel_thread_globals.emplace_back(kernel_globals, osl_memory, profiler); + } +} + +void *CPUDevice::get_cpu_osl_memory() +{ +#ifdef WITH_OSL + return &osl_globals; +#else + return NULL; +#endif +} + +bool CPUDevice::load_kernels(const uint /*kernel_features*/) +{ + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/device_impl.h b/intern/cycles/device/cpu/device_impl.h new file mode 100644 index 00000000000..7d222808652 --- /dev/null +++ b/intern/cycles/device/cpu/device_impl.h @@ -0,0 +1,99 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +/* So ImathMath is included before our kernel_cpu_compat. */ +#ifdef WITH_OSL +/* So no context pollution happens from indirectly included windows.h */ +# include "util/util_windows.h" +# include +#endif + +#ifdef WITH_EMBREE +# include +#endif + +#include "device/cpu/kernel.h" +#include "device/device.h" +#include "device/device_memory.h" + +// clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/kernel.h" +#include "kernel/device/cpu/globals.h" + +#include "kernel/osl/osl_shader.h" +#include "kernel/osl/osl_globals.h" +// clang-format on + +CCL_NAMESPACE_BEGIN + +class CPUDevice : public Device { + public: + KernelGlobals kernel_globals; + + device_vector texture_info; + bool need_texture_info; + +#ifdef WITH_OSL + OSLGlobals osl_globals; +#endif +#ifdef WITH_EMBREE + RTCScene embree_scene = NULL; + RTCDevice embree_device; +#endif + + CPUKernels kernels; + + CPUDevice(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_); + ~CPUDevice(); + + virtual bool show_samples() const override; + + virtual BVHLayoutMask get_bvh_layout_mask() const override; + + /* Returns true if the texture info was copied to the device (meaning, some more + * re-initialization might be needed). */ + bool load_texture_info(); + + virtual void mem_alloc(device_memory &mem) override; + virtual void mem_copy_to(device_memory &mem) override; + virtual void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override; + virtual void mem_zero(device_memory &mem) override; + virtual void mem_free(device_memory &mem) override; + virtual device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override; + + virtual void const_copy_to(const char *name, void *host, size_t size) override; + + void global_alloc(device_memory &mem); + void global_free(device_memory &mem); + + void tex_alloc(device_texture &mem); + void tex_free(device_texture &mem); + + void build_bvh(BVH *bvh, Progress &progress, bool refit) override; + + virtual const CPUKernels *get_cpu_kernels() const override; + virtual void get_cpu_kernel_thread_globals( + vector &kernel_thread_globals) override; + virtual void *get_cpu_osl_memory() override; + + protected: + virtual bool load_kernels(uint /*kernel_features*/) override; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/kernel.cpp b/intern/cycles/device/cpu/kernel.cpp new file mode 100644 index 00000000000..0ab58ff8600 --- /dev/null +++ b/intern/cycles/device/cpu/kernel.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/cpu/kernel.h" + +#include "kernel/device/cpu/kernel.h" + +CCL_NAMESPACE_BEGIN + +#define KERNEL_FUNCTIONS(name) \ + KERNEL_NAME_EVAL(cpu, name), KERNEL_NAME_EVAL(cpu_sse2, name), \ + KERNEL_NAME_EVAL(cpu_sse3, name), KERNEL_NAME_EVAL(cpu_sse41, name), \ + KERNEL_NAME_EVAL(cpu_avx, name), KERNEL_NAME_EVAL(cpu_avx2, name) + +#define REGISTER_KERNEL(name) name(KERNEL_FUNCTIONS(name)) + +CPUKernels::CPUKernels() + : /* Integrator. */ + REGISTER_KERNEL(integrator_init_from_camera), + REGISTER_KERNEL(integrator_init_from_bake), + REGISTER_KERNEL(integrator_intersect_closest), + REGISTER_KERNEL(integrator_intersect_shadow), + REGISTER_KERNEL(integrator_intersect_subsurface), + REGISTER_KERNEL(integrator_intersect_volume_stack), + REGISTER_KERNEL(integrator_shade_background), + REGISTER_KERNEL(integrator_shade_light), + REGISTER_KERNEL(integrator_shade_shadow), + REGISTER_KERNEL(integrator_shade_surface), + REGISTER_KERNEL(integrator_shade_volume), + REGISTER_KERNEL(integrator_megakernel), + /* Shader evaluation. */ + REGISTER_KERNEL(shader_eval_displace), + REGISTER_KERNEL(shader_eval_background), + /* Adaptive campling. */ + REGISTER_KERNEL(adaptive_sampling_convergence_check), + REGISTER_KERNEL(adaptive_sampling_filter_x), + REGISTER_KERNEL(adaptive_sampling_filter_y), + /* Cryptomatte. */ + REGISTER_KERNEL(cryptomatte_postprocess), + /* Bake. */ + REGISTER_KERNEL(bake) +{ +} + +#undef REGISTER_KERNEL +#undef KERNEL_FUNCTIONS + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/kernel.h b/intern/cycles/device/cpu/kernel.h new file mode 100644 index 00000000000..54b18308544 --- /dev/null +++ b/intern/cycles/device/cpu/kernel.h @@ -0,0 +1,111 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "device/cpu/kernel_function.h" +#include "util/util_types.h" + +CCL_NAMESPACE_BEGIN + +struct KernelGlobals; +struct IntegratorStateCPU; +struct TileInfo; + +class CPUKernels { + public: + /* Integrator. */ + + using IntegratorFunction = + CPUKernelFunction; + using IntegratorShadeFunction = CPUKernelFunction; + using IntegratorInitFunction = CPUKernelFunction; + + IntegratorInitFunction integrator_init_from_camera; + IntegratorInitFunction integrator_init_from_bake; + IntegratorFunction integrator_intersect_closest; + IntegratorFunction integrator_intersect_shadow; + IntegratorFunction integrator_intersect_subsurface; + IntegratorFunction integrator_intersect_volume_stack; + IntegratorShadeFunction integrator_shade_background; + IntegratorShadeFunction integrator_shade_light; + IntegratorShadeFunction integrator_shade_shadow; + IntegratorShadeFunction integrator_shade_surface; + IntegratorShadeFunction integrator_shade_volume; + IntegratorShadeFunction integrator_megakernel; + + /* Shader evaluation. */ + + using ShaderEvalFunction = CPUKernelFunction; + + ShaderEvalFunction shader_eval_displace; + ShaderEvalFunction shader_eval_background; + + /* Adaptive stopping. */ + + using AdaptiveSamplingConvergenceCheckFunction = + CPUKernelFunction; + + using AdaptiveSamplingFilterXFunction = + CPUKernelFunction; + + using AdaptiveSamplingFilterYFunction = + CPUKernelFunction; + + AdaptiveSamplingConvergenceCheckFunction adaptive_sampling_convergence_check; + + AdaptiveSamplingFilterXFunction adaptive_sampling_filter_x; + AdaptiveSamplingFilterYFunction adaptive_sampling_filter_y; + + /* Cryptomatte. */ + + using CryptomattePostprocessFunction = CPUKernelFunction; + + CryptomattePostprocessFunction cryptomatte_postprocess; + + /* Bake. */ + + CPUKernelFunction bake; + + CPUKernels(); +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/kernel_function.h b/intern/cycles/device/cpu/kernel_function.h new file mode 100644 index 00000000000..aa18720cc24 --- /dev/null +++ b/intern/cycles/device/cpu/kernel_function.h @@ -0,0 +1,124 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/util_debug.h" +#include "util/util_system.h" + +CCL_NAMESPACE_BEGIN + +/* A wrapper around per-microarchitecture variant of a kernel function. + * + * Provides a function-call-like API which gets routed to the most suitable implementation. + * + * For example, on a computer which only has SSE4.1 the kernel_sse41 will be used. */ +template class CPUKernelFunction { + public: + CPUKernelFunction(FunctionType kernel_default, + FunctionType kernel_sse2, + FunctionType kernel_sse3, + FunctionType kernel_sse41, + FunctionType kernel_avx, + FunctionType kernel_avx2) + { + kernel_info_ = get_best_kernel_info( + kernel_default, kernel_sse2, kernel_sse3, kernel_sse41, kernel_avx, kernel_avx2); + } + + template inline auto operator()(Args... args) const + { + assert(kernel_info_.kernel); + + return kernel_info_.kernel(args...); + } + + const char *get_uarch_name() const + { + return kernel_info_.uarch_name; + } + + protected: + /* Helper class which allows to pass human-readable microarchitecture name together with function + * pointer. */ + class KernelInfo { + public: + KernelInfo() : KernelInfo("", nullptr) + { + } + + /* TODO(sergey): Use string view, to have higher-level functionality (i.e. comparison) without + * memory allocation. */ + KernelInfo(const char *uarch_name, FunctionType kernel) + : uarch_name(uarch_name), kernel(kernel) + { + } + + const char *uarch_name; + FunctionType kernel; + }; + + KernelInfo get_best_kernel_info(FunctionType kernel_default, + FunctionType kernel_sse2, + FunctionType kernel_sse3, + FunctionType kernel_sse41, + FunctionType kernel_avx, + FunctionType kernel_avx2) + { + /* Silence warnings about unused variables when compiling without some architectures. */ + (void)kernel_sse2; + (void)kernel_sse3; + (void)kernel_sse41; + (void)kernel_avx; + (void)kernel_avx2; + +#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 + if (DebugFlags().cpu.has_avx2() && system_cpu_support_avx2()) { + return KernelInfo("AVX2", kernel_avx2); + } +#endif + +#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX + if (DebugFlags().cpu.has_avx() && system_cpu_support_avx()) { + return KernelInfo("AVX", kernel_avx); + } +#endif + +#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 + if (DebugFlags().cpu.has_sse41() && system_cpu_support_sse41()) { + return KernelInfo("SSE4.1", kernel_sse41); + } +#endif + +#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 + if (DebugFlags().cpu.has_sse3() && system_cpu_support_sse3()) { + return KernelInfo("SSE3", kernel_sse3); + } +#endif + +#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 + if (DebugFlags().cpu.has_sse2() && system_cpu_support_sse2()) { + return KernelInfo("SSE2", kernel_sse2); + } +#endif + + return KernelInfo("default", kernel_default); + } + + KernelInfo kernel_info_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/kernel_thread_globals.cpp b/intern/cycles/device/cpu/kernel_thread_globals.cpp new file mode 100644 index 00000000000..988b00cd1f0 --- /dev/null +++ b/intern/cycles/device/cpu/kernel_thread_globals.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/cpu/kernel_thread_globals.h" + +// clang-format off +#include "kernel/osl/osl_shader.h" +#include "kernel/osl/osl_globals.h" +// clang-format on + +#include "util/util_profiling.h" + +CCL_NAMESPACE_BEGIN + +CPUKernelThreadGlobals::CPUKernelThreadGlobals(const KernelGlobals &kernel_globals, + void *osl_globals_memory, + Profiler &cpu_profiler) + : KernelGlobals(kernel_globals), cpu_profiler_(cpu_profiler) +{ + reset_runtime_memory(); + +#ifdef WITH_OSL + OSLShader::thread_init(this, reinterpret_cast(osl_globals_memory)); +#else + (void)osl_globals_memory; +#endif +} + +CPUKernelThreadGlobals::CPUKernelThreadGlobals(CPUKernelThreadGlobals &&other) noexcept + : KernelGlobals(std::move(other)), cpu_profiler_(other.cpu_profiler_) +{ + other.reset_runtime_memory(); +} + +CPUKernelThreadGlobals::~CPUKernelThreadGlobals() +{ +#ifdef WITH_OSL + OSLShader::thread_free(this); +#endif +} + +CPUKernelThreadGlobals &CPUKernelThreadGlobals::operator=(CPUKernelThreadGlobals &&other) +{ + if (this == &other) { + return *this; + } + + *static_cast(this) = *static_cast(&other); + + other.reset_runtime_memory(); + + return *this; +} + +void CPUKernelThreadGlobals::reset_runtime_memory() +{ +#ifdef WITH_OSL + osl = nullptr; +#endif +} + +void CPUKernelThreadGlobals::start_profiling() +{ + cpu_profiler_.add_state(&profiler); +} + +void CPUKernelThreadGlobals::stop_profiling() +{ + cpu_profiler_.remove_state(&profiler); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/kernel_thread_globals.h b/intern/cycles/device/cpu/kernel_thread_globals.h new file mode 100644 index 00000000000..d005c3bb56c --- /dev/null +++ b/intern/cycles/device/cpu/kernel_thread_globals.h @@ -0,0 +1,57 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + +CCL_NAMESPACE_BEGIN + +class Profiler; + +/* A special class which extends memory ownership of the `KernelGlobals` decoupling any resource + * which is not thread-safe for access. Every worker thread which needs to operate on + * `KernelGlobals` needs to initialize its own copy of this object. + * + * NOTE: Only minimal subset of objects are copied: `KernelData` is never copied. This means that + * there is no unnecessary data duplication happening when using this object. */ +class CPUKernelThreadGlobals : public KernelGlobals { + public: + /* TODO(sergey): Would be nice to have properly typed OSLGlobals even in the case when building + * without OSL support. Will avoid need to those unnamed pointers and casts. */ + CPUKernelThreadGlobals(const KernelGlobals &kernel_globals, + void *osl_globals_memory, + Profiler &cpu_profiler); + + ~CPUKernelThreadGlobals(); + + CPUKernelThreadGlobals(const CPUKernelThreadGlobals &other) = delete; + CPUKernelThreadGlobals(CPUKernelThreadGlobals &&other) noexcept; + + CPUKernelThreadGlobals &operator=(const CPUKernelThreadGlobals &other) = delete; + CPUKernelThreadGlobals &operator=(CPUKernelThreadGlobals &&other); + + void start_profiling(); + void stop_profiling(); + + protected: + void reset_runtime_memory(); + + Profiler &cpu_profiler_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/cuda/device.cpp similarity index 92% rename from intern/cycles/device/device_cuda.cpp rename to intern/cycles/device/cuda/device.cpp index 2e225ecfaf8..84becd6d081 100644 --- a/intern/cycles/device/device_cuda.cpp +++ b/intern/cycles/device/cuda/device.cpp @@ -14,21 +14,25 @@ * limitations under the License. */ +#include "device/cuda/device.h" + +#include "util/util_logging.h" + #ifdef WITH_CUDA - -# include "device/cuda/device_cuda.h" +# include "device/cuda/device_impl.h" # include "device/device.h" -# include "device/device_intern.h" -# include "util/util_logging.h" # include "util/util_string.h" # include "util/util_windows.h" +#endif /* WITH_CUDA */ CCL_NAMESPACE_BEGIN bool device_cuda_init() { -# ifdef WITH_CUDA_DYNLOAD +#if !defined(WITH_CUDA) + return false; +#elif defined(WITH_CUDA_DYNLOAD) static bool initialized = false; static bool result = false; @@ -59,16 +63,27 @@ bool device_cuda_init() } return result; -# else /* WITH_CUDA_DYNLOAD */ +#else /* WITH_CUDA_DYNLOAD */ return true; -# endif /* WITH_CUDA_DYNLOAD */ +#endif /* WITH_CUDA_DYNLOAD */ } -Device *device_cuda_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) +Device *device_cuda_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) { - return new CUDADevice(info, stats, profiler, background); +#ifdef WITH_CUDA + return new CUDADevice(info, stats, profiler); +#else + (void)info; + (void)stats; + (void)profiler; + + LOG(FATAL) << "Request to create CUDA device without compiled-in support. Should never happen."; + + return nullptr; +#endif } +#ifdef WITH_CUDA static CUresult device_cuda_safe_init() { # ifdef _WIN32 @@ -86,9 +101,11 @@ static CUresult device_cuda_safe_init() return cuInit(0); # endif } +#endif /* WITH_CUDA */ void device_cuda_info(vector &devices) { +#ifdef WITH_CUDA CUresult result = device_cuda_safe_init(); if (result != CUDA_SUCCESS) { if (result != CUDA_ERROR_NO_DEVICE) @@ -129,9 +146,9 @@ void device_cuda_info(vector &devices) info.has_half_images = (major >= 3); info.has_nanovdb = true; - info.has_volume_decoupled = false; - info.has_adaptive_stop_per_sample = false; - info.denoisers = DENOISER_NLM; + info.denoisers = 0; + + info.has_gpu_queue = true; /* Check if the device has P2P access to any other device in the system. */ for (int peer_num = 0; peer_num < count && !info.has_peer_memory; peer_num++) { @@ -182,10 +199,14 @@ void device_cuda_info(vector &devices) if (!display_devices.empty()) devices.insert(devices.end(), display_devices.begin(), display_devices.end()); +#else /* WITH_CUDA */ + (void)devices; +#endif /* WITH_CUDA */ } string device_cuda_capabilities() { +#ifdef WITH_CUDA CUresult result = device_cuda_safe_init(); if (result != CUDA_SUCCESS) { if (result != CUDA_ERROR_NO_DEVICE) { @@ -310,8 +331,10 @@ string device_cuda_capabilities() } return capabilities; + +#else /* WITH_CUDA */ + return ""; +#endif /* WITH_CUDA */ } CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/kernel/kernels/opencl/kernel_enqueue_inactive.cl b/intern/cycles/device/cuda/device.h similarity index 57% rename from intern/cycles/kernel/kernels/opencl/kernel_enqueue_inactive.cl rename to intern/cycles/device/cuda/device.h index e68d4104a91..b0484904d1a 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_enqueue_inactive.cl +++ b/intern/cycles/device/cuda/device.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,24 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_enqueue_inactive.h" +#pragma once -#define KERNEL_NAME enqueue_inactive -#define LOCALS_TYPE unsigned int -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE +#include "util/util_string.h" +#include "util/util_vector.h" +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +bool device_cuda_init(); + +Device *device_cuda_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +void device_cuda_info(vector &devices); + +string device_cuda_capabilities(); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cuda/device_cuda.h b/intern/cycles/device/cuda/device_cuda.h deleted file mode 100644 index c3271c3cfcf..00000000000 --- a/intern/cycles/device/cuda/device_cuda.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_CUDA - -# include "device/device.h" -# include "device/device_denoising.h" -# include "device/device_split_kernel.h" - -# include "util/util_map.h" -# include "util/util_task.h" - -# ifdef WITH_CUDA_DYNLOAD -# include "cuew.h" -# else -# include "util/util_opengl.h" -# include -# include -# endif - -CCL_NAMESPACE_BEGIN - -class CUDASplitKernel; - -class CUDADevice : public Device { - - friend class CUDASplitKernelFunction; - friend class CUDASplitKernel; - friend class CUDAContextScope; - - public: - DedicatedTaskPool task_pool; - CUdevice cuDevice; - CUcontext cuContext; - CUmodule cuModule, cuFilterModule; - size_t device_texture_headroom; - size_t device_working_headroom; - bool move_texture_to_host; - size_t map_host_used; - size_t map_host_limit; - int can_map_host; - int pitch_alignment; - int cuDevId; - int cuDevArchitecture; - bool first_error; - CUDASplitKernel *split_kernel; - - struct CUDAMem { - CUDAMem() : texobject(0), array(0), use_mapped_host(false) - { - } - - CUtexObject texobject; - CUarray array; - - /* If true, a mapped host memory in shared_pointer is being used. */ - bool use_mapped_host; - }; - typedef map CUDAMemMap; - CUDAMemMap cuda_mem_map; - thread_mutex cuda_mem_map_mutex; - - struct PixelMem { - GLuint cuPBO; - CUgraphicsResource cuPBOresource; - GLuint cuTexId; - int w, h; - }; - map pixel_mem_map; - - /* Bindless Textures */ - device_vector texture_info; - bool need_texture_info; - - /* Kernels */ - struct { - bool loaded; - - CUfunction adaptive_stopping; - CUfunction adaptive_filter_x; - CUfunction adaptive_filter_y; - CUfunction adaptive_scale_samples; - int adaptive_num_threads_per_block; - } functions; - - static bool have_precompiled_kernels(); - - virtual bool show_samples() const override; - - virtual BVHLayoutMask get_bvh_layout_mask() const override; - - void set_error(const string &error) override; - - CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background_); - - virtual ~CUDADevice(); - - bool support_device(const DeviceRequestedFeatures & /*requested_features*/); - - bool check_peer_access(Device *peer_device) override; - - bool use_adaptive_compilation(); - - bool use_split_kernel(); - - virtual string compile_kernel_get_common_cflags( - const DeviceRequestedFeatures &requested_features, bool filter = false, bool split = false); - - string compile_kernel(const DeviceRequestedFeatures &requested_features, - const char *name, - const char *base = "cuda", - bool force_ptx = false); - - virtual bool load_kernels(const DeviceRequestedFeatures &requested_features) override; - - void load_functions(); - - void reserve_local_memory(const DeviceRequestedFeatures &requested_features); - - void init_host_memory(); - - void load_texture_info(); - - void move_textures_to_host(size_t size, bool for_texture); - - CUDAMem *generic_alloc(device_memory &mem, size_t pitch_padding = 0); - - void generic_copy_to(device_memory &mem); - - void generic_free(device_memory &mem); - - void mem_alloc(device_memory &mem) override; - - void mem_copy_to(device_memory &mem) override; - - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override; - - void mem_zero(device_memory &mem) override; - - void mem_free(device_memory &mem) override; - - device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override; - - virtual void const_copy_to(const char *name, void *host, size_t size) override; - - void global_alloc(device_memory &mem); - - void global_free(device_memory &mem); - - void tex_alloc(device_texture &mem); - - void tex_free(device_texture &mem); - - bool denoising_non_local_means(device_ptr image_ptr, - device_ptr guide_ptr, - device_ptr variance_ptr, - device_ptr out_ptr, - DenoisingTask *task); - - bool denoising_construct_transform(DenoisingTask *task); - - bool denoising_accumulate(device_ptr color_ptr, - device_ptr color_variance_ptr, - device_ptr scale_ptr, - int frame, - DenoisingTask *task); - - bool denoising_solve(device_ptr output_ptr, DenoisingTask *task); - - bool denoising_combine_halves(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr mean_ptr, - device_ptr variance_ptr, - int r, - int4 rect, - DenoisingTask *task); - - bool denoising_divide_shadow(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr sample_variance_ptr, - device_ptr sv_variance_ptr, - device_ptr buffer_variance_ptr, - DenoisingTask *task); - - bool denoising_get_feature(int mean_offset, - int variance_offset, - device_ptr mean_ptr, - device_ptr variance_ptr, - float scale, - DenoisingTask *task); - - bool denoising_write_feature(int out_offset, - device_ptr from_ptr, - device_ptr buffer_ptr, - DenoisingTask *task); - - bool denoising_detect_outliers(device_ptr image_ptr, - device_ptr variance_ptr, - device_ptr depth_ptr, - device_ptr output_ptr, - DenoisingTask *task); - - void denoise(RenderTile &rtile, DenoisingTask &denoising); - - void adaptive_sampling_filter(uint filter_sample, - WorkTile *wtile, - CUdeviceptr d_wtile, - CUstream stream = 0); - void adaptive_sampling_post(RenderTile &rtile, - WorkTile *wtile, - CUdeviceptr d_wtile, - CUstream stream = 0); - - void render(DeviceTask &task, RenderTile &rtile, device_vector &work_tiles); - - void film_convert(DeviceTask &task, - device_ptr buffer, - device_ptr rgba_byte, - device_ptr rgba_half); - - void shader(DeviceTask &task); - - CUdeviceptr map_pixels(device_ptr mem); - - void unmap_pixels(device_ptr mem); - - void pixels_alloc(device_memory &mem); - - void pixels_copy_from(device_memory &mem, int y, int w, int h); - - void pixels_free(device_memory &mem); - - void draw_pixels(device_memory &mem, - int y, - int w, - int h, - int width, - int height, - int dx, - int dy, - int dw, - int dh, - bool transparent, - const DeviceDrawParams &draw_params) override; - - void thread_run(DeviceTask &task); - - virtual void task_add(DeviceTask &task) override; - - virtual void task_wait() override; - - virtual void task_cancel() override; -}; - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/cuda/device_cuda_impl.cpp b/intern/cycles/device/cuda/device_cuda_impl.cpp deleted file mode 100644 index 2d2fcb38705..00000000000 --- a/intern/cycles/device/cuda/device_cuda_impl.cpp +++ /dev/null @@ -1,2714 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_CUDA - -# include -# include -# include -# include -# include - -# include "device/cuda/device_cuda.h" -# include "device/device_intern.h" -# include "device/device_split_kernel.h" - -# include "render/buffers.h" - -# include "kernel/filter/filter_defines.h" - -# include "util/util_debug.h" -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_map.h" -# include "util/util_md5.h" -# include "util/util_opengl.h" -# include "util/util_path.h" -# include "util/util_string.h" -# include "util/util_system.h" -# include "util/util_time.h" -# include "util/util_types.h" -# include "util/util_windows.h" - -# include "kernel/split/kernel_split_data_types.h" - -CCL_NAMESPACE_BEGIN - -# ifndef WITH_CUDA_DYNLOAD - -/* Transparently implement some functions, so majority of the file does not need - * to worry about difference between dynamically loaded and linked CUDA at all. - */ - -namespace { - -const char *cuewErrorString(CUresult result) -{ - /* We can only give error code here without major code duplication, that - * should be enough since dynamic loading is only being disabled by folks - * who knows what they're doing anyway. - * - * NOTE: Avoid call from several threads. - */ - static string error; - error = string_printf("%d", result); - return error.c_str(); -} - -const char *cuewCompilerPath() -{ - return CYCLES_CUDA_NVCC_EXECUTABLE; -} - -int cuewCompilerVersion() -{ - return (CUDA_VERSION / 100) + (CUDA_VERSION % 100 / 10); -} - -} /* namespace */ -# endif /* WITH_CUDA_DYNLOAD */ - -class CUDADevice; - -class CUDASplitKernel : public DeviceSplitKernel { - CUDADevice *device; - - public: - explicit CUDASplitKernel(CUDADevice *device); - - virtual uint64_t state_buffer_size(device_memory &kg, device_memory &data, size_t num_threads); - - virtual bool enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory &kernel_globals, - device_memory &kernel_data_, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flag, - device_memory &work_pool_wgs); - - virtual SplitKernelFunction *get_split_kernel_function(const string &kernel_name, - const DeviceRequestedFeatures &); - virtual int2 split_kernel_local_size(); - virtual int2 split_kernel_global_size(device_memory &kg, device_memory &data, DeviceTask &task); -}; - -/* Utility to push/pop CUDA context. */ -class CUDAContextScope { - public: - CUDAContextScope(CUDADevice *device); - ~CUDAContextScope(); - - private: - CUDADevice *device; -}; - -bool CUDADevice::have_precompiled_kernels() -{ - string cubins_path = path_get("lib"); - return path_exists(cubins_path); -} - -bool CUDADevice::show_samples() const -{ - /* The CUDADevice only processes one tile at a time, so showing samples is fine. */ - return true; -} - -BVHLayoutMask CUDADevice::get_bvh_layout_mask() const -{ - return BVH_LAYOUT_BVH2; -} - -void CUDADevice::set_error(const string &error) -{ - Device::set_error(error); - - if (first_error) { - fprintf(stderr, "\nRefer to the Cycles GPU rendering documentation for possible solutions:\n"); - fprintf(stderr, - "https://docs.blender.org/manual/en/latest/render/cycles/gpu_rendering.html\n\n"); - first_error = false; - } -} - -# define cuda_assert(stmt) \ - { \ - CUresult result = stmt; \ - if (result != CUDA_SUCCESS) { \ - const char *name = cuewErrorString(result); \ - set_error(string_printf("%s in %s (device_cuda_impl.cpp:%d)", name, #stmt, __LINE__)); \ - } \ - } \ - (void)0 - -CUDADevice::CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background_) - : Device(info, stats, profiler, background_), texture_info(this, "__texture_info", MEM_GLOBAL) -{ - first_error = true; - background = background_; - - cuDevId = info.num; - cuDevice = 0; - cuContext = 0; - - cuModule = 0; - cuFilterModule = 0; - - split_kernel = NULL; - - need_texture_info = false; - - device_texture_headroom = 0; - device_working_headroom = 0; - move_texture_to_host = false; - map_host_limit = 0; - map_host_used = 0; - can_map_host = 0; - pitch_alignment = 0; - - functions.loaded = false; - - /* Initialize CUDA. */ - CUresult result = cuInit(0); - if (result != CUDA_SUCCESS) { - set_error(string_printf("Failed to initialize CUDA runtime (%s)", cuewErrorString(result))); - return; - } - - /* Setup device and context. */ - result = cuDeviceGet(&cuDevice, cuDevId); - if (result != CUDA_SUCCESS) { - set_error(string_printf("Failed to get CUDA device handle from ordinal (%s)", - cuewErrorString(result))); - return; - } - - /* CU_CTX_MAP_HOST for mapping host memory when out of device memory. - * CU_CTX_LMEM_RESIZE_TO_MAX for reserving local memory ahead of render, - * so we can predict which memory to map to host. */ - cuda_assert( - cuDeviceGetAttribute(&can_map_host, CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY, cuDevice)); - - cuda_assert(cuDeviceGetAttribute( - &pitch_alignment, CU_DEVICE_ATTRIBUTE_TEXTURE_PITCH_ALIGNMENT, cuDevice)); - - unsigned int ctx_flags = CU_CTX_LMEM_RESIZE_TO_MAX; - if (can_map_host) { - ctx_flags |= CU_CTX_MAP_HOST; - init_host_memory(); - } - - /* Create context. */ - if (background) { - result = cuCtxCreate(&cuContext, ctx_flags, cuDevice); - } - else { - result = cuGLCtxCreate(&cuContext, ctx_flags, cuDevice); - - if (result != CUDA_SUCCESS) { - result = cuCtxCreate(&cuContext, ctx_flags, cuDevice); - background = true; - } - } - - if (result != CUDA_SUCCESS) { - set_error(string_printf("Failed to create CUDA context (%s)", cuewErrorString(result))); - return; - } - - int major, minor; - cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); - cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); - cuDevArchitecture = major * 100 + minor * 10; - - /* Pop context set by cuCtxCreate. */ - cuCtxPopCurrent(NULL); -} - -CUDADevice::~CUDADevice() -{ - task_pool.cancel(); - - delete split_kernel; - - texture_info.free(); - - cuda_assert(cuCtxDestroy(cuContext)); -} - -bool CUDADevice::support_device(const DeviceRequestedFeatures & /*requested_features*/) -{ - int major, minor; - cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); - cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); - - /* We only support sm_30 and above */ - if (major < 3) { - set_error(string_printf( - "CUDA backend requires compute capability 3.0 or up, but found %d.%d.", major, minor)); - return false; - } - - return true; -} - -bool CUDADevice::check_peer_access(Device *peer_device) -{ - if (peer_device == this) { - return false; - } - if (peer_device->info.type != DEVICE_CUDA && peer_device->info.type != DEVICE_OPTIX) { - return false; - } - - CUDADevice *const peer_device_cuda = static_cast(peer_device); - - int can_access = 0; - cuda_assert(cuDeviceCanAccessPeer(&can_access, cuDevice, peer_device_cuda->cuDevice)); - if (can_access == 0) { - return false; - } - - // Ensure array access over the link is possible as well (for 3D textures) - cuda_assert(cuDeviceGetP2PAttribute(&can_access, - CU_DEVICE_P2P_ATTRIBUTE_ARRAY_ACCESS_ACCESS_SUPPORTED, - cuDevice, - peer_device_cuda->cuDevice)); - if (can_access == 0) { - return false; - } - - // Enable peer access in both directions - { - const CUDAContextScope scope(this); - CUresult result = cuCtxEnablePeerAccess(peer_device_cuda->cuContext, 0); - if (result != CUDA_SUCCESS) { - set_error(string_printf("Failed to enable peer access on CUDA context (%s)", - cuewErrorString(result))); - return false; - } - } - { - const CUDAContextScope scope(peer_device_cuda); - CUresult result = cuCtxEnablePeerAccess(cuContext, 0); - if (result != CUDA_SUCCESS) { - set_error(string_printf("Failed to enable peer access on CUDA context (%s)", - cuewErrorString(result))); - return false; - } - } - - return true; -} - -bool CUDADevice::use_adaptive_compilation() -{ - return DebugFlags().cuda.adaptive_compile; -} - -bool CUDADevice::use_split_kernel() -{ - return DebugFlags().cuda.split_kernel; -} - -/* Common NVCC flags which stays the same regardless of shading model, - * kernel sources md5 and only depends on compiler or compilation settings. - */ -string CUDADevice::compile_kernel_get_common_cflags( - const DeviceRequestedFeatures &requested_features, bool filter, bool split) -{ - const int machine = system_cpu_bits(); - const string source_path = path_get("source"); - const string include_path = source_path; - string cflags = string_printf( - "-m%d " - "--ptxas-options=\"-v\" " - "--use_fast_math " - "-DNVCC " - "-I\"%s\"", - machine, - include_path.c_str()); - if (!filter && use_adaptive_compilation()) { - cflags += " " + requested_features.get_build_options(); - } - const char *extra_cflags = getenv("CYCLES_CUDA_EXTRA_CFLAGS"); - if (extra_cflags) { - cflags += string(" ") + string(extra_cflags); - } - - if (split) { - cflags += " -D__SPLIT__"; - } - -# ifdef WITH_NANOVDB - cflags += " -DWITH_NANOVDB"; -# endif - - return cflags; -} - -string CUDADevice::compile_kernel(const DeviceRequestedFeatures &requested_features, - const char *name, - const char *base, - bool force_ptx) -{ - /* Compute kernel name. */ - int major, minor; - cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); - cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); - - /* Attempt to use kernel provided with Blender. */ - if (!use_adaptive_compilation()) { - if (!force_ptx) { - const string cubin = path_get(string_printf("lib/%s_sm_%d%d.cubin", name, major, minor)); - VLOG(1) << "Testing for pre-compiled kernel " << cubin << "."; - if (path_exists(cubin)) { - VLOG(1) << "Using precompiled kernel."; - return cubin; - } - } - - /* The driver can JIT-compile PTX generated for older generations, so find the closest one. */ - int ptx_major = major, ptx_minor = minor; - while (ptx_major >= 3) { - const string ptx = path_get( - string_printf("lib/%s_compute_%d%d.ptx", name, ptx_major, ptx_minor)); - VLOG(1) << "Testing for pre-compiled kernel " << ptx << "."; - if (path_exists(ptx)) { - VLOG(1) << "Using precompiled kernel."; - return ptx; - } - - if (ptx_minor > 0) { - ptx_minor--; - } - else { - ptx_major--; - ptx_minor = 9; - } - } - } - - /* Try to use locally compiled kernel. */ - string source_path = path_get("source"); - const string source_md5 = path_files_md5_hash(source_path); - - /* We include cflags into md5 so changing cuda toolkit or changing other - * compiler command line arguments makes sure cubin gets re-built. - */ - string common_cflags = compile_kernel_get_common_cflags( - requested_features, strstr(name, "filter") != NULL, strstr(name, "split") != NULL); - const string kernel_md5 = util_md5_string(source_md5 + common_cflags); - - const char *const kernel_ext = force_ptx ? "ptx" : "cubin"; - const char *const kernel_arch = force_ptx ? "compute" : "sm"; - const string cubin_file = string_printf( - "cycles_%s_%s_%d%d_%s.%s", name, kernel_arch, major, minor, kernel_md5.c_str(), kernel_ext); - const string cubin = path_cache_get(path_join("kernels", cubin_file)); - VLOG(1) << "Testing for locally compiled kernel " << cubin << "."; - if (path_exists(cubin)) { - VLOG(1) << "Using locally compiled kernel."; - return cubin; - } - -# ifdef _WIN32 - if (!use_adaptive_compilation() && have_precompiled_kernels()) { - if (major < 3) { - set_error( - string_printf("CUDA backend requires compute capability 3.0 or up, but found %d.%d. " - "Your GPU is not supported.", - major, - minor)); - } - else { - set_error( - string_printf("CUDA binary kernel for this graphics card compute " - "capability (%d.%d) not found.", - major, - minor)); - } - return string(); - } -# endif - - /* Compile. */ - const char *const nvcc = cuewCompilerPath(); - if (nvcc == NULL) { - set_error( - "CUDA nvcc compiler not found. " - "Install CUDA toolkit in default location."); - return string(); - } - - const int nvcc_cuda_version = cuewCompilerVersion(); - VLOG(1) << "Found nvcc " << nvcc << ", CUDA version " << nvcc_cuda_version << "."; - if (nvcc_cuda_version < 101) { - printf( - "Unsupported CUDA version %d.%d detected, " - "you need CUDA 10.1 or newer.\n", - nvcc_cuda_version / 10, - nvcc_cuda_version % 10); - return string(); - } - else if (!(nvcc_cuda_version == 101 || nvcc_cuda_version == 102 || nvcc_cuda_version == 111 || - nvcc_cuda_version == 112 || nvcc_cuda_version == 113 || nvcc_cuda_version == 114)) { - printf( - "CUDA version %d.%d detected, build may succeed but only " - "CUDA 10.1 to 11.4 are officially supported.\n", - nvcc_cuda_version / 10, - nvcc_cuda_version % 10); - } - - double starttime = time_dt(); - - path_create_directories(cubin); - - source_path = path_join(path_join(source_path, "kernel"), - path_join("kernels", path_join(base, string_printf("%s.cu", name)))); - - string command = string_printf( - "\"%s\" " - "-arch=%s_%d%d " - "--%s \"%s\" " - "-o \"%s\" " - "%s", - nvcc, - kernel_arch, - major, - minor, - kernel_ext, - source_path.c_str(), - cubin.c_str(), - common_cflags.c_str()); - - printf("Compiling CUDA kernel ...\n%s\n", command.c_str()); - -# ifdef _WIN32 - command = "call " + command; -# endif - if (system(command.c_str()) != 0) { - set_error( - "Failed to execute compilation command, " - "see console for details."); - return string(); - } - - /* Verify if compilation succeeded */ - if (!path_exists(cubin)) { - set_error( - "CUDA kernel compilation failed, " - "see console for details."); - return string(); - } - - printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime); - - return cubin; -} - -bool CUDADevice::load_kernels(const DeviceRequestedFeatures &requested_features) -{ - /* TODO(sergey): Support kernels re-load for CUDA devices. - * - * Currently re-loading kernel will invalidate memory pointers, - * causing problems in cuCtxSynchronize. - */ - if (cuFilterModule && cuModule) { - VLOG(1) << "Skipping kernel reload, not currently supported."; - return true; - } - - /* check if cuda init succeeded */ - if (cuContext == 0) - return false; - - /* check if GPU is supported */ - if (!support_device(requested_features)) - return false; - - /* get kernel */ - const char *kernel_name = use_split_kernel() ? "kernel_split" : "kernel"; - string cubin = compile_kernel(requested_features, kernel_name); - if (cubin.empty()) - return false; - - const char *filter_name = "filter"; - string filter_cubin = compile_kernel(requested_features, filter_name); - if (filter_cubin.empty()) - return false; - - /* open module */ - CUDAContextScope scope(this); - - string cubin_data; - CUresult result; - - if (path_read_text(cubin, cubin_data)) - result = cuModuleLoadData(&cuModule, cubin_data.c_str()); - else - result = CUDA_ERROR_FILE_NOT_FOUND; - - if (result != CUDA_SUCCESS) - set_error(string_printf( - "Failed to load CUDA kernel from '%s' (%s)", cubin.c_str(), cuewErrorString(result))); - - if (path_read_text(filter_cubin, cubin_data)) - result = cuModuleLoadData(&cuFilterModule, cubin_data.c_str()); - else - result = CUDA_ERROR_FILE_NOT_FOUND; - - if (result != CUDA_SUCCESS) - set_error(string_printf("Failed to load CUDA kernel from '%s' (%s)", - filter_cubin.c_str(), - cuewErrorString(result))); - - if (result == CUDA_SUCCESS) { - reserve_local_memory(requested_features); - } - - load_functions(); - - return (result == CUDA_SUCCESS); -} - -void CUDADevice::load_functions() -{ - /* TODO: load all functions here. */ - if (functions.loaded) { - return; - } - functions.loaded = true; - - cuda_assert(cuModuleGetFunction( - &functions.adaptive_stopping, cuModule, "kernel_cuda_adaptive_stopping")); - cuda_assert(cuModuleGetFunction( - &functions.adaptive_filter_x, cuModule, "kernel_cuda_adaptive_filter_x")); - cuda_assert(cuModuleGetFunction( - &functions.adaptive_filter_y, cuModule, "kernel_cuda_adaptive_filter_y")); - cuda_assert(cuModuleGetFunction( - &functions.adaptive_scale_samples, cuModule, "kernel_cuda_adaptive_scale_samples")); - - cuda_assert(cuFuncSetCacheConfig(functions.adaptive_stopping, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(functions.adaptive_filter_x, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(functions.adaptive_filter_y, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(functions.adaptive_scale_samples, CU_FUNC_CACHE_PREFER_L1)); - - int unused_min_blocks; - cuda_assert(cuOccupancyMaxPotentialBlockSize(&unused_min_blocks, - &functions.adaptive_num_threads_per_block, - functions.adaptive_scale_samples, - NULL, - 0, - 0)); -} - -void CUDADevice::reserve_local_memory(const DeviceRequestedFeatures &requested_features) -{ - if (use_split_kernel()) { - /* Split kernel mostly uses global memory and adaptive compilation, - * difficult to predict how much is needed currently. */ - return; - } - - /* Together with CU_CTX_LMEM_RESIZE_TO_MAX, this reserves local memory - * needed for kernel launches, so that we can reliably figure out when - * to allocate scene data in mapped host memory. */ - CUDAContextScope scope(this); - - size_t total = 0, free_before = 0, free_after = 0; - cuMemGetInfo(&free_before, &total); - - /* Get kernel function. */ - CUfunction cuRender; - - if (requested_features.use_baking) { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_bake")); - } - else if (requested_features.use_integrator_branched) { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_branched_path_trace")); - } - else { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_path_trace")); - } - - cuda_assert(cuFuncSetCacheConfig(cuRender, CU_FUNC_CACHE_PREFER_L1)); - - int min_blocks, num_threads_per_block; - cuda_assert( - cuOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, cuRender, NULL, 0, 0)); - - /* Launch kernel, using just 1 block appears sufficient to reserve - * memory for all multiprocessors. It would be good to do this in - * parallel for the multi GPU case still to make it faster. */ - CUdeviceptr d_work_tiles = 0; - uint total_work_size = 0; - - void *args[] = {&d_work_tiles, &total_work_size}; - - cuda_assert(cuLaunchKernel(cuRender, 1, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0)); - - cuda_assert(cuCtxSynchronize()); - - cuMemGetInfo(&free_after, &total); - VLOG(1) << "Local memory reserved " << string_human_readable_number(free_before - free_after) - << " bytes. (" << string_human_readable_size(free_before - free_after) << ")"; - -# if 0 - /* For testing mapped host memory, fill up device memory. */ - const size_t keep_mb = 1024; - - while (free_after > keep_mb * 1024 * 1024LL) { - CUdeviceptr tmp; - cuda_assert(cuMemAlloc(&tmp, 10 * 1024 * 1024LL)); - cuMemGetInfo(&free_after, &total); - } -# endif -} - -void CUDADevice::init_host_memory() -{ - /* Limit amount of host mapped memory, because allocating too much can - * cause system instability. Leave at least half or 4 GB of system - * memory free, whichever is smaller. */ - size_t default_limit = 4 * 1024 * 1024 * 1024LL; - size_t system_ram = system_physical_ram(); - - if (system_ram > 0) { - if (system_ram / 2 > default_limit) { - map_host_limit = system_ram - default_limit; - } - else { - map_host_limit = system_ram / 2; - } - } - else { - VLOG(1) << "Mapped host memory disabled, failed to get system RAM"; - map_host_limit = 0; - } - - /* Amount of device memory to keep is free after texture memory - * and working memory allocations respectively. We set the working - * memory limit headroom lower so that some space is left after all - * texture memory allocations. */ - device_working_headroom = 32 * 1024 * 1024LL; // 32MB - device_texture_headroom = 128 * 1024 * 1024LL; // 128MB - - VLOG(1) << "Mapped host memory limit set to " << string_human_readable_number(map_host_limit) - << " bytes. (" << string_human_readable_size(map_host_limit) << ")"; -} - -void CUDADevice::load_texture_info() -{ - if (need_texture_info) { - /* Unset flag before copying, so this does not loop indefinitely if the copy below calls - * into 'move_textures_to_host' (which calls 'load_texture_info' again). */ - need_texture_info = false; - texture_info.copy_to_device(); - } -} - -void CUDADevice::move_textures_to_host(size_t size, bool for_texture) -{ - /* Break out of recursive call, which can happen when moving memory on a multi device. */ - static bool any_device_moving_textures_to_host = false; - if (any_device_moving_textures_to_host) { - return; - } - - /* Signal to reallocate textures in host memory only. */ - move_texture_to_host = true; - - while (size > 0) { - /* Find suitable memory allocation to move. */ - device_memory *max_mem = NULL; - size_t max_size = 0; - bool max_is_image = false; - - thread_scoped_lock lock(cuda_mem_map_mutex); - foreach (CUDAMemMap::value_type &pair, cuda_mem_map) { - device_memory &mem = *pair.first; - CUDAMem *cmem = &pair.second; - - /* Can only move textures allocated on this device (and not those from peer devices). - * And need to ignore memory that is already on the host. */ - if (!mem.is_resident(this) || cmem->use_mapped_host) { - continue; - } - - bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && - (&mem != &texture_info); - bool is_image = is_texture && (mem.data_height > 1); - - /* Can't move this type of memory. */ - if (!is_texture || cmem->array) { - continue; - } - - /* For other textures, only move image textures. */ - if (for_texture && !is_image) { - continue; - } - - /* Try to move largest allocation, prefer moving images. */ - if (is_image > max_is_image || (is_image == max_is_image && mem.device_size > max_size)) { - max_is_image = is_image; - max_size = mem.device_size; - max_mem = &mem; - } - } - lock.unlock(); - - /* Move to host memory. This part is mutex protected since - * multiple CUDA devices could be moving the memory. The - * first one will do it, and the rest will adopt the pointer. */ - if (max_mem) { - VLOG(1) << "Move memory from device to host: " << max_mem->name; - - static thread_mutex move_mutex; - thread_scoped_lock lock(move_mutex); - - any_device_moving_textures_to_host = true; - - /* Potentially need to call back into multi device, so pointer mapping - * and peer devices are updated. This is also necessary since the device - * pointer may just be a key here, so cannot be accessed and freed directly. - * Unfortunately it does mean that memory is reallocated on all other - * devices as well, which is potentially dangerous when still in use (since - * a thread rendering on another devices would only be caught in this mutex - * if it so happens to do an allocation at the same time as well. */ - max_mem->device_copy_to(); - size = (max_size >= size) ? 0 : size - max_size; - - any_device_moving_textures_to_host = false; - } - else { - break; - } - } - - /* Unset flag before texture info is reloaded, since it should stay in device memory. */ - move_texture_to_host = false; - - /* Update texture info array with new pointers. */ - load_texture_info(); -} - -CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_padding) -{ - CUDAContextScope scope(this); - - CUdeviceptr device_pointer = 0; - size_t size = mem.memory_size() + pitch_padding; - - CUresult mem_alloc_result = CUDA_ERROR_OUT_OF_MEMORY; - const char *status = ""; - - /* First try allocating in device memory, respecting headroom. We make - * an exception for texture info. It is small and frequently accessed, - * so treat it as working memory. - * - * If there is not enough room for working memory, we will try to move - * textures to host memory, assuming the performance impact would have - * been worse for working memory. */ - bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && (&mem != &texture_info); - bool is_image = is_texture && (mem.data_height > 1); - - size_t headroom = (is_texture) ? device_texture_headroom : device_working_headroom; - - size_t total = 0, free = 0; - cuMemGetInfo(&free, &total); - - /* Move textures to host memory if needed. */ - if (!move_texture_to_host && !is_image && (size + headroom) >= free && can_map_host) { - move_textures_to_host(size + headroom - free, is_texture); - cuMemGetInfo(&free, &total); - } - - /* Allocate in device memory. */ - if (!move_texture_to_host && (size + headroom) < free) { - mem_alloc_result = cuMemAlloc(&device_pointer, size); - if (mem_alloc_result == CUDA_SUCCESS) { - status = " in device memory"; - } - } - - /* Fall back to mapped host memory if needed and possible. */ - - void *shared_pointer = 0; - - if (mem_alloc_result != CUDA_SUCCESS && can_map_host && mem.type != MEM_DEVICE_ONLY) { - if (mem.shared_pointer) { - /* Another device already allocated host memory. */ - mem_alloc_result = CUDA_SUCCESS; - shared_pointer = mem.shared_pointer; - } - else if (map_host_used + size < map_host_limit) { - /* Allocate host memory ourselves. */ - mem_alloc_result = cuMemHostAlloc( - &shared_pointer, size, CU_MEMHOSTALLOC_DEVICEMAP | CU_MEMHOSTALLOC_WRITECOMBINED); - - assert((mem_alloc_result == CUDA_SUCCESS && shared_pointer != 0) || - (mem_alloc_result != CUDA_SUCCESS && shared_pointer == 0)); - } - - if (mem_alloc_result == CUDA_SUCCESS) { - cuda_assert(cuMemHostGetDevicePointer_v2(&device_pointer, shared_pointer, 0)); - map_host_used += size; - status = " in host memory"; - } - } - - if (mem_alloc_result != CUDA_SUCCESS) { - if (mem.type == MEM_DEVICE_ONLY) { - status = " failed, out of device memory"; - set_error("System is out of GPU memory"); - } - else { - status = " failed, out of device and host memory"; - set_error("System is out of GPU and shared host memory"); - } - } - - if (mem.name) { - VLOG(1) << "Buffer allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")" << status; - } - - mem.device_pointer = (device_ptr)device_pointer; - mem.device_size = size; - stats.mem_alloc(size); - - if (!mem.device_pointer) { - return NULL; - } - - /* Insert into map of allocations. */ - thread_scoped_lock lock(cuda_mem_map_mutex); - CUDAMem *cmem = &cuda_mem_map[&mem]; - if (shared_pointer != 0) { - /* Replace host pointer with our host allocation. Only works if - * CUDA memory layout is the same and has no pitch padding. Also - * does not work if we move textures to host during a render, - * since other devices might be using the memory. */ - - if (!move_texture_to_host && pitch_padding == 0 && mem.host_pointer && - mem.host_pointer != shared_pointer) { - memcpy(shared_pointer, mem.host_pointer, size); - - /* A Call to device_memory::host_free() should be preceded by - * a call to device_memory::device_free() for host memory - * allocated by a device to be handled properly. Two exceptions - * are here and a call in OptiXDevice::generic_alloc(), where - * the current host memory can be assumed to be allocated by - * device_memory::host_alloc(), not by a device */ - - mem.host_free(); - mem.host_pointer = shared_pointer; - } - mem.shared_pointer = shared_pointer; - mem.shared_counter++; - cmem->use_mapped_host = true; - } - else { - cmem->use_mapped_host = false; - } - - return cmem; -} - -void CUDADevice::generic_copy_to(device_memory &mem) -{ - if (!mem.host_pointer || !mem.device_pointer) { - return; - } - - /* If use_mapped_host of mem is false, the current device only uses device memory allocated by - * cuMemAlloc regardless of mem.host_pointer and mem.shared_pointer, and should copy data from - * mem.host_pointer. */ - thread_scoped_lock lock(cuda_mem_map_mutex); - if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { - const CUDAContextScope scope(this); - cuda_assert( - cuMemcpyHtoD((CUdeviceptr)mem.device_pointer, mem.host_pointer, mem.memory_size())); - } -} - -void CUDADevice::generic_free(device_memory &mem) -{ - if (mem.device_pointer) { - CUDAContextScope scope(this); - thread_scoped_lock lock(cuda_mem_map_mutex); - const CUDAMem &cmem = cuda_mem_map[&mem]; - - /* If cmem.use_mapped_host is true, reference counting is used - * to safely free a mapped host memory. */ - - if (cmem.use_mapped_host) { - assert(mem.shared_pointer); - if (mem.shared_pointer) { - assert(mem.shared_counter > 0); - if (--mem.shared_counter == 0) { - if (mem.host_pointer == mem.shared_pointer) { - mem.host_pointer = 0; - } - cuMemFreeHost(mem.shared_pointer); - mem.shared_pointer = 0; - } - } - map_host_used -= mem.device_size; - } - else { - /* Free device memory. */ - cuda_assert(cuMemFree(mem.device_pointer)); - } - - stats.mem_free(mem.device_size); - mem.device_pointer = 0; - mem.device_size = 0; - - cuda_mem_map.erase(cuda_mem_map.find(&mem)); - } -} - -void CUDADevice::mem_alloc(device_memory &mem) -{ - if (mem.type == MEM_PIXELS && !background) { - pixels_alloc(mem); - } - else if (mem.type == MEM_TEXTURE) { - assert(!"mem_alloc not supported for textures."); - } - else if (mem.type == MEM_GLOBAL) { - assert(!"mem_alloc not supported for global memory."); - } - else { - generic_alloc(mem); - } -} - -void CUDADevice::mem_copy_to(device_memory &mem) -{ - if (mem.type == MEM_PIXELS) { - assert(!"mem_copy_to not supported for pixels."); - } - else if (mem.type == MEM_GLOBAL) { - global_free(mem); - global_alloc(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - tex_alloc((device_texture &)mem); - } - else { - if (!mem.device_pointer) { - generic_alloc(mem); - } - generic_copy_to(mem); - } -} - -void CUDADevice::mem_copy_from(device_memory &mem, int y, int w, int h, int elem) -{ - if (mem.type == MEM_PIXELS && !background) { - pixels_copy_from(mem, y, w, h); - } - else if (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) { - assert(!"mem_copy_from not supported for textures."); - } - else if (mem.host_pointer) { - const size_t size = elem * w * h; - const size_t offset = elem * y * w; - - if (mem.device_pointer) { - const CUDAContextScope scope(this); - cuda_assert(cuMemcpyDtoH( - (char *)mem.host_pointer + offset, (CUdeviceptr)mem.device_pointer + offset, size)); - } - else { - memset((char *)mem.host_pointer + offset, 0, size); - } - } -} - -void CUDADevice::mem_zero(device_memory &mem) -{ - if (!mem.device_pointer) { - mem_alloc(mem); - } - if (!mem.device_pointer) { - return; - } - - /* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory - * regardless of mem.host_pointer and mem.shared_pointer. */ - thread_scoped_lock lock(cuda_mem_map_mutex); - if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { - const CUDAContextScope scope(this); - cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size())); - } - else if (mem.host_pointer) { - memset(mem.host_pointer, 0, mem.memory_size()); - } -} - -void CUDADevice::mem_free(device_memory &mem) -{ - if (mem.type == MEM_PIXELS && !background) { - pixels_free(mem); - } - else if (mem.type == MEM_GLOBAL) { - global_free(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - } - else { - generic_free(mem); - } -} - -device_ptr CUDADevice::mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) -{ - return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); -} - -void CUDADevice::const_copy_to(const char *name, void *host, size_t size) -{ - CUDAContextScope scope(this); - CUdeviceptr mem; - size_t bytes; - - cuda_assert(cuModuleGetGlobal(&mem, &bytes, cuModule, name)); - // assert(bytes == size); - cuda_assert(cuMemcpyHtoD(mem, host, size)); -} - -void CUDADevice::global_alloc(device_memory &mem) -{ - if (mem.is_resident(this)) { - generic_alloc(mem); - generic_copy_to(mem); - } - - const_copy_to(mem.name, &mem.device_pointer, sizeof(mem.device_pointer)); -} - -void CUDADevice::global_free(device_memory &mem) -{ - if (mem.is_resident(this) && mem.device_pointer) { - generic_free(mem); - } -} - -void CUDADevice::tex_alloc(device_texture &mem) -{ - CUDAContextScope scope(this); - - /* General variables for both architectures */ - string bind_name = mem.name; - size_t dsize = datatype_size(mem.data_type); - size_t size = mem.memory_size(); - - CUaddress_mode address_mode = CU_TR_ADDRESS_MODE_WRAP; - switch (mem.info.extension) { - case EXTENSION_REPEAT: - address_mode = CU_TR_ADDRESS_MODE_WRAP; - break; - case EXTENSION_EXTEND: - address_mode = CU_TR_ADDRESS_MODE_CLAMP; - break; - case EXTENSION_CLIP: - address_mode = CU_TR_ADDRESS_MODE_BORDER; - break; - default: - assert(0); - break; - } - - CUfilter_mode filter_mode; - if (mem.info.interpolation == INTERPOLATION_CLOSEST) { - filter_mode = CU_TR_FILTER_MODE_POINT; - } - else { - filter_mode = CU_TR_FILTER_MODE_LINEAR; - } - - /* Image Texture Storage */ - CUarray_format_enum format; - switch (mem.data_type) { - case TYPE_UCHAR: - format = CU_AD_FORMAT_UNSIGNED_INT8; - break; - case TYPE_UINT16: - format = CU_AD_FORMAT_UNSIGNED_INT16; - break; - case TYPE_UINT: - format = CU_AD_FORMAT_UNSIGNED_INT32; - break; - case TYPE_INT: - format = CU_AD_FORMAT_SIGNED_INT32; - break; - case TYPE_FLOAT: - format = CU_AD_FORMAT_FLOAT; - break; - case TYPE_HALF: - format = CU_AD_FORMAT_HALF; - break; - default: - assert(0); - return; - } - - CUDAMem *cmem = NULL; - CUarray array_3d = NULL; - size_t src_pitch = mem.data_width * dsize * mem.data_elements; - size_t dst_pitch = src_pitch; - - if (!mem.is_resident(this)) { - thread_scoped_lock lock(cuda_mem_map_mutex); - cmem = &cuda_mem_map[&mem]; - cmem->texobject = 0; - - if (mem.data_depth > 1) { - array_3d = (CUarray)mem.device_pointer; - cmem->array = array_3d; - } - else if (mem.data_height > 0) { - dst_pitch = align_up(src_pitch, pitch_alignment); - } - } - else if (mem.data_depth > 1) { - /* 3D texture using array, there is no API for linear memory. */ - CUDA_ARRAY3D_DESCRIPTOR desc; - - desc.Width = mem.data_width; - desc.Height = mem.data_height; - desc.Depth = mem.data_depth; - desc.Format = format; - desc.NumChannels = mem.data_elements; - desc.Flags = 0; - - VLOG(1) << "Array 3D allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - - cuda_assert(cuArray3DCreate(&array_3d, &desc)); - - if (!array_3d) { - return; - } - - CUDA_MEMCPY3D param; - memset(¶m, 0, sizeof(param)); - param.dstMemoryType = CU_MEMORYTYPE_ARRAY; - param.dstArray = array_3d; - param.srcMemoryType = CU_MEMORYTYPE_HOST; - param.srcHost = mem.host_pointer; - param.srcPitch = src_pitch; - param.WidthInBytes = param.srcPitch; - param.Height = mem.data_height; - param.Depth = mem.data_depth; - - cuda_assert(cuMemcpy3D(¶m)); - - mem.device_pointer = (device_ptr)array_3d; - mem.device_size = size; - stats.mem_alloc(size); - - thread_scoped_lock lock(cuda_mem_map_mutex); - cmem = &cuda_mem_map[&mem]; - cmem->texobject = 0; - cmem->array = array_3d; - } - else if (mem.data_height > 0) { - /* 2D texture, using pitch aligned linear memory. */ - dst_pitch = align_up(src_pitch, pitch_alignment); - size_t dst_size = dst_pitch * mem.data_height; - - cmem = generic_alloc(mem, dst_size - mem.memory_size()); - if (!cmem) { - return; - } - - CUDA_MEMCPY2D param; - memset(¶m, 0, sizeof(param)); - param.dstMemoryType = CU_MEMORYTYPE_DEVICE; - param.dstDevice = mem.device_pointer; - param.dstPitch = dst_pitch; - param.srcMemoryType = CU_MEMORYTYPE_HOST; - param.srcHost = mem.host_pointer; - param.srcPitch = src_pitch; - param.WidthInBytes = param.srcPitch; - param.Height = mem.data_height; - - cuda_assert(cuMemcpy2DUnaligned(¶m)); - } - else { - /* 1D texture, using linear memory. */ - cmem = generic_alloc(mem); - if (!cmem) { - return; - } - - cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size)); - } - - /* Resize once */ - const uint slot = mem.slot; - if (slot >= texture_info.size()) { - /* Allocate some slots in advance, to reduce amount - * of re-allocations. */ - texture_info.resize(slot + 128); - } - - /* Set Mapping and tag that we need to (re-)upload to device */ - texture_info[slot] = mem.info; - need_texture_info = true; - - if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && - mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { - /* Kepler+, bindless textures. */ - CUDA_RESOURCE_DESC resDesc; - memset(&resDesc, 0, sizeof(resDesc)); - - if (array_3d) { - resDesc.resType = CU_RESOURCE_TYPE_ARRAY; - resDesc.res.array.hArray = array_3d; - resDesc.flags = 0; - } - else if (mem.data_height > 0) { - resDesc.resType = CU_RESOURCE_TYPE_PITCH2D; - resDesc.res.pitch2D.devPtr = mem.device_pointer; - resDesc.res.pitch2D.format = format; - resDesc.res.pitch2D.numChannels = mem.data_elements; - resDesc.res.pitch2D.height = mem.data_height; - resDesc.res.pitch2D.width = mem.data_width; - resDesc.res.pitch2D.pitchInBytes = dst_pitch; - } - else { - resDesc.resType = CU_RESOURCE_TYPE_LINEAR; - resDesc.res.linear.devPtr = mem.device_pointer; - resDesc.res.linear.format = format; - resDesc.res.linear.numChannels = mem.data_elements; - resDesc.res.linear.sizeInBytes = mem.device_size; - } - - CUDA_TEXTURE_DESC texDesc; - memset(&texDesc, 0, sizeof(texDesc)); - texDesc.addressMode[0] = address_mode; - texDesc.addressMode[1] = address_mode; - texDesc.addressMode[2] = address_mode; - texDesc.filterMode = filter_mode; - texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; - - thread_scoped_lock lock(cuda_mem_map_mutex); - cmem = &cuda_mem_map[&mem]; - - cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL)); - - texture_info[slot].data = (uint64_t)cmem->texobject; - } - else { - texture_info[slot].data = (uint64_t)mem.device_pointer; - } -} - -void CUDADevice::tex_free(device_texture &mem) -{ - if (mem.device_pointer) { - CUDAContextScope scope(this); - thread_scoped_lock lock(cuda_mem_map_mutex); - const CUDAMem &cmem = cuda_mem_map[&mem]; - - if (cmem.texobject) { - /* Free bindless texture. */ - cuTexObjectDestroy(cmem.texobject); - } - - if (!mem.is_resident(this)) { - /* Do not free memory here, since it was allocated on a different device. */ - cuda_mem_map.erase(cuda_mem_map.find(&mem)); - } - else if (cmem.array) { - /* Free array. */ - cuArrayDestroy(cmem.array); - stats.mem_free(mem.device_size); - mem.device_pointer = 0; - mem.device_size = 0; - - cuda_mem_map.erase(cuda_mem_map.find(&mem)); - } - else { - lock.unlock(); - generic_free(mem); - } - } -} - -# define CUDA_GET_BLOCKSIZE(func, w, h) \ - int threads_per_block; \ - cuda_assert( \ - cuFuncGetAttribute(&threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, func)); \ - int threads = (int)sqrt((float)threads_per_block); \ - int xblocks = ((w) + threads - 1) / threads; \ - int yblocks = ((h) + threads - 1) / threads; - -# define CUDA_LAUNCH_KERNEL(func, args) \ - cuda_assert(cuLaunchKernel(func, xblocks, yblocks, 1, threads, threads, 1, 0, 0, args, 0)); - -/* Similar as above, but for 1-dimensional blocks. */ -# define CUDA_GET_BLOCKSIZE_1D(func, w, h) \ - int threads_per_block; \ - cuda_assert( \ - cuFuncGetAttribute(&threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, func)); \ - int xblocks = ((w) + threads_per_block - 1) / threads_per_block; \ - int yblocks = h; - -# define CUDA_LAUNCH_KERNEL_1D(func, args) \ - cuda_assert(cuLaunchKernel(func, xblocks, yblocks, 1, threads_per_block, 1, 1, 0, 0, args, 0)); - -bool CUDADevice::denoising_non_local_means(device_ptr image_ptr, - device_ptr guide_ptr, - device_ptr variance_ptr, - device_ptr out_ptr, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - int stride = task->buffer.stride; - int w = task->buffer.width; - int h = task->buffer.h; - int r = task->nlm_state.r; - int f = task->nlm_state.f; - float a = task->nlm_state.a; - float k_2 = task->nlm_state.k_2; - - int pass_stride = task->buffer.pass_stride; - int num_shifts = (2 * r + 1) * (2 * r + 1); - int channel_offset = task->nlm_state.is_color ? task->buffer.pass_stride : 0; - int frame_offset = 0; - - if (have_error()) - return false; - - CUdeviceptr difference = (CUdeviceptr)task->buffer.temporary_mem.device_pointer; - CUdeviceptr blurDifference = difference + sizeof(float) * pass_stride * num_shifts; - CUdeviceptr weightAccum = difference + 2 * sizeof(float) * pass_stride * num_shifts; - CUdeviceptr scale_ptr = 0; - - cuda_assert(cuMemsetD8(weightAccum, 0, sizeof(float) * pass_stride)); - cuda_assert(cuMemsetD8(out_ptr, 0, sizeof(float) * pass_stride)); - - { - CUfunction cuNLMCalcDifference, cuNLMBlur, cuNLMCalcWeight, cuNLMUpdateOutput; - cuda_assert(cuModuleGetFunction( - &cuNLMCalcDifference, cuFilterModule, "kernel_cuda_filter_nlm_calc_difference")); - cuda_assert(cuModuleGetFunction(&cuNLMBlur, cuFilterModule, "kernel_cuda_filter_nlm_blur")); - cuda_assert(cuModuleGetFunction( - &cuNLMCalcWeight, cuFilterModule, "kernel_cuda_filter_nlm_calc_weight")); - cuda_assert(cuModuleGetFunction( - &cuNLMUpdateOutput, cuFilterModule, "kernel_cuda_filter_nlm_update_output")); - - cuda_assert(cuFuncSetCacheConfig(cuNLMCalcDifference, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMBlur, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMCalcWeight, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMUpdateOutput, CU_FUNC_CACHE_PREFER_L1)); - - CUDA_GET_BLOCKSIZE_1D(cuNLMCalcDifference, w * h, num_shifts); - - void *calc_difference_args[] = {&guide_ptr, - &variance_ptr, - &scale_ptr, - &difference, - &w, - &h, - &stride, - &pass_stride, - &r, - &channel_offset, - &frame_offset, - &a, - &k_2}; - void *blur_args[] = {&difference, &blurDifference, &w, &h, &stride, &pass_stride, &r, &f}; - void *calc_weight_args[] = { - &blurDifference, &difference, &w, &h, &stride, &pass_stride, &r, &f}; - void *update_output_args[] = {&blurDifference, - &image_ptr, - &out_ptr, - &weightAccum, - &w, - &h, - &stride, - &pass_stride, - &channel_offset, - &r, - &f}; - - CUDA_LAUNCH_KERNEL_1D(cuNLMCalcDifference, calc_difference_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMBlur, blur_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMCalcWeight, calc_weight_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMBlur, blur_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMUpdateOutput, update_output_args); - } - - { - CUfunction cuNLMNormalize; - cuda_assert( - cuModuleGetFunction(&cuNLMNormalize, cuFilterModule, "kernel_cuda_filter_nlm_normalize")); - cuda_assert(cuFuncSetCacheConfig(cuNLMNormalize, CU_FUNC_CACHE_PREFER_L1)); - void *normalize_args[] = {&out_ptr, &weightAccum, &w, &h, &stride}; - CUDA_GET_BLOCKSIZE(cuNLMNormalize, w, h); - CUDA_LAUNCH_KERNEL(cuNLMNormalize, normalize_args); - cuda_assert(cuCtxSynchronize()); - } - - return !have_error(); -} - -bool CUDADevice::denoising_construct_transform(DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterConstructTransform; - cuda_assert(cuModuleGetFunction( - &cuFilterConstructTransform, cuFilterModule, "kernel_cuda_filter_construct_transform")); - cuda_assert(cuFuncSetCacheConfig(cuFilterConstructTransform, CU_FUNC_CACHE_PREFER_SHARED)); - CUDA_GET_BLOCKSIZE(cuFilterConstructTransform, task->storage.w, task->storage.h); - - void *args[] = {&task->buffer.mem.device_pointer, - &task->tile_info_mem.device_pointer, - &task->storage.transform.device_pointer, - &task->storage.rank.device_pointer, - &task->filter_area, - &task->rect, - &task->radius, - &task->pca_threshold, - &task->buffer.pass_stride, - &task->buffer.frame_stride, - &task->buffer.use_time}; - CUDA_LAUNCH_KERNEL(cuFilterConstructTransform, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_accumulate(device_ptr color_ptr, - device_ptr color_variance_ptr, - device_ptr scale_ptr, - int frame, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - int r = task->radius; - int f = 4; - float a = 1.0f; - float k_2 = task->nlm_k_2; - - int w = task->reconstruction_state.source_w; - int h = task->reconstruction_state.source_h; - int stride = task->buffer.stride; - int frame_offset = frame * task->buffer.frame_stride; - int t = task->tile_info->frames[frame]; - - int pass_stride = task->buffer.pass_stride; - int num_shifts = (2 * r + 1) * (2 * r + 1); - - if (have_error()) - return false; - - CUdeviceptr difference = (CUdeviceptr)task->buffer.temporary_mem.device_pointer; - CUdeviceptr blurDifference = difference + sizeof(float) * pass_stride * num_shifts; - - CUfunction cuNLMCalcDifference, cuNLMBlur, cuNLMCalcWeight, cuNLMConstructGramian; - cuda_assert(cuModuleGetFunction( - &cuNLMCalcDifference, cuFilterModule, "kernel_cuda_filter_nlm_calc_difference")); - cuda_assert(cuModuleGetFunction(&cuNLMBlur, cuFilterModule, "kernel_cuda_filter_nlm_blur")); - cuda_assert( - cuModuleGetFunction(&cuNLMCalcWeight, cuFilterModule, "kernel_cuda_filter_nlm_calc_weight")); - cuda_assert(cuModuleGetFunction( - &cuNLMConstructGramian, cuFilterModule, "kernel_cuda_filter_nlm_construct_gramian")); - - cuda_assert(cuFuncSetCacheConfig(cuNLMCalcDifference, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMBlur, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMCalcWeight, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMConstructGramian, CU_FUNC_CACHE_PREFER_SHARED)); - - CUDA_GET_BLOCKSIZE_1D(cuNLMCalcDifference, - task->reconstruction_state.source_w * task->reconstruction_state.source_h, - num_shifts); - - void *calc_difference_args[] = {&color_ptr, - &color_variance_ptr, - &scale_ptr, - &difference, - &w, - &h, - &stride, - &pass_stride, - &r, - &pass_stride, - &frame_offset, - &a, - &k_2}; - void *blur_args[] = {&difference, &blurDifference, &w, &h, &stride, &pass_stride, &r, &f}; - void *calc_weight_args[] = {&blurDifference, &difference, &w, &h, &stride, &pass_stride, &r, &f}; - void *construct_gramian_args[] = {&t, - &blurDifference, - &task->buffer.mem.device_pointer, - &task->storage.transform.device_pointer, - &task->storage.rank.device_pointer, - &task->storage.XtWX.device_pointer, - &task->storage.XtWY.device_pointer, - &task->reconstruction_state.filter_window, - &w, - &h, - &stride, - &pass_stride, - &r, - &f, - &frame_offset, - &task->buffer.use_time}; - - CUDA_LAUNCH_KERNEL_1D(cuNLMCalcDifference, calc_difference_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMBlur, blur_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMCalcWeight, calc_weight_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMBlur, blur_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMConstructGramian, construct_gramian_args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_solve(device_ptr output_ptr, DenoisingTask *task) -{ - CUfunction cuFinalize; - cuda_assert(cuModuleGetFunction(&cuFinalize, cuFilterModule, "kernel_cuda_filter_finalize")); - cuda_assert(cuFuncSetCacheConfig(cuFinalize, CU_FUNC_CACHE_PREFER_L1)); - void *finalize_args[] = {&output_ptr, - &task->storage.rank.device_pointer, - &task->storage.XtWX.device_pointer, - &task->storage.XtWY.device_pointer, - &task->filter_area, - &task->reconstruction_state.buffer_params.x, - &task->render_buffer.samples}; - CUDA_GET_BLOCKSIZE( - cuFinalize, task->reconstruction_state.source_w, task->reconstruction_state.source_h); - CUDA_LAUNCH_KERNEL(cuFinalize, finalize_args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_combine_halves(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr mean_ptr, - device_ptr variance_ptr, - int r, - int4 rect, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterCombineHalves; - cuda_assert(cuModuleGetFunction( - &cuFilterCombineHalves, cuFilterModule, "kernel_cuda_filter_combine_halves")); - cuda_assert(cuFuncSetCacheConfig(cuFilterCombineHalves, CU_FUNC_CACHE_PREFER_L1)); - CUDA_GET_BLOCKSIZE( - cuFilterCombineHalves, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - void *args[] = {&mean_ptr, &variance_ptr, &a_ptr, &b_ptr, &rect, &r}; - CUDA_LAUNCH_KERNEL(cuFilterCombineHalves, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_divide_shadow(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr sample_variance_ptr, - device_ptr sv_variance_ptr, - device_ptr buffer_variance_ptr, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterDivideShadow; - cuda_assert(cuModuleGetFunction( - &cuFilterDivideShadow, cuFilterModule, "kernel_cuda_filter_divide_shadow")); - cuda_assert(cuFuncSetCacheConfig(cuFilterDivideShadow, CU_FUNC_CACHE_PREFER_L1)); - CUDA_GET_BLOCKSIZE( - cuFilterDivideShadow, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - void *args[] = {&task->render_buffer.samples, - &task->tile_info_mem.device_pointer, - &a_ptr, - &b_ptr, - &sample_variance_ptr, - &sv_variance_ptr, - &buffer_variance_ptr, - &task->rect, - &task->render_buffer.pass_stride, - &task->render_buffer.offset}; - CUDA_LAUNCH_KERNEL(cuFilterDivideShadow, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_get_feature(int mean_offset, - int variance_offset, - device_ptr mean_ptr, - device_ptr variance_ptr, - float scale, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterGetFeature; - cuda_assert( - cuModuleGetFunction(&cuFilterGetFeature, cuFilterModule, "kernel_cuda_filter_get_feature")); - cuda_assert(cuFuncSetCacheConfig(cuFilterGetFeature, CU_FUNC_CACHE_PREFER_L1)); - CUDA_GET_BLOCKSIZE(cuFilterGetFeature, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - void *args[] = {&task->render_buffer.samples, - &task->tile_info_mem.device_pointer, - &mean_offset, - &variance_offset, - &mean_ptr, - &variance_ptr, - &scale, - &task->rect, - &task->render_buffer.pass_stride, - &task->render_buffer.offset}; - CUDA_LAUNCH_KERNEL(cuFilterGetFeature, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_write_feature(int out_offset, - device_ptr from_ptr, - device_ptr buffer_ptr, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterWriteFeature; - cuda_assert(cuModuleGetFunction( - &cuFilterWriteFeature, cuFilterModule, "kernel_cuda_filter_write_feature")); - cuda_assert(cuFuncSetCacheConfig(cuFilterWriteFeature, CU_FUNC_CACHE_PREFER_L1)); - CUDA_GET_BLOCKSIZE(cuFilterWriteFeature, task->filter_area.z, task->filter_area.w); - - void *args[] = {&task->render_buffer.samples, - &task->reconstruction_state.buffer_params, - &task->filter_area, - &from_ptr, - &buffer_ptr, - &out_offset, - &task->rect}; - CUDA_LAUNCH_KERNEL(cuFilterWriteFeature, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_detect_outliers(device_ptr image_ptr, - device_ptr variance_ptr, - device_ptr depth_ptr, - device_ptr output_ptr, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterDetectOutliers; - cuda_assert(cuModuleGetFunction( - &cuFilterDetectOutliers, cuFilterModule, "kernel_cuda_filter_detect_outliers")); - cuda_assert(cuFuncSetCacheConfig(cuFilterDetectOutliers, CU_FUNC_CACHE_PREFER_L1)); - CUDA_GET_BLOCKSIZE( - cuFilterDetectOutliers, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - void *args[] = { - &image_ptr, &variance_ptr, &depth_ptr, &output_ptr, &task->rect, &task->buffer.pass_stride}; - - CUDA_LAUNCH_KERNEL(cuFilterDetectOutliers, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -void CUDADevice::denoise(RenderTile &rtile, DenoisingTask &denoising) -{ - denoising.functions.construct_transform = function_bind( - &CUDADevice::denoising_construct_transform, this, &denoising); - denoising.functions.accumulate = function_bind( - &CUDADevice::denoising_accumulate, this, _1, _2, _3, _4, &denoising); - denoising.functions.solve = function_bind(&CUDADevice::denoising_solve, this, _1, &denoising); - denoising.functions.divide_shadow = function_bind( - &CUDADevice::denoising_divide_shadow, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.non_local_means = function_bind( - &CUDADevice::denoising_non_local_means, this, _1, _2, _3, _4, &denoising); - denoising.functions.combine_halves = function_bind( - &CUDADevice::denoising_combine_halves, this, _1, _2, _3, _4, _5, _6, &denoising); - denoising.functions.get_feature = function_bind( - &CUDADevice::denoising_get_feature, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.write_feature = function_bind( - &CUDADevice::denoising_write_feature, this, _1, _2, _3, &denoising); - denoising.functions.detect_outliers = function_bind( - &CUDADevice::denoising_detect_outliers, this, _1, _2, _3, _4, &denoising); - - denoising.filter_area = make_int4(rtile.x, rtile.y, rtile.w, rtile.h); - denoising.render_buffer.samples = rtile.sample; - denoising.buffer.gpu_temporary_mem = true; - - denoising.run_denoising(rtile); -} - -void CUDADevice::adaptive_sampling_filter(uint filter_sample, - WorkTile *wtile, - CUdeviceptr d_wtile, - CUstream stream) -{ - const int num_threads_per_block = functions.adaptive_num_threads_per_block; - - /* These are a series of tiny kernels because there is no grid synchronization - * from within a kernel, so multiple kernel launches it is. */ - uint total_work_size = wtile->h * wtile->w; - void *args2[] = {&d_wtile, &filter_sample, &total_work_size}; - uint num_blocks = divide_up(total_work_size, num_threads_per_block); - cuda_assert(cuLaunchKernel(functions.adaptive_stopping, - num_blocks, - 1, - 1, - num_threads_per_block, - 1, - 1, - 0, - stream, - args2, - 0)); - total_work_size = wtile->h; - num_blocks = divide_up(total_work_size, num_threads_per_block); - cuda_assert(cuLaunchKernel(functions.adaptive_filter_x, - num_blocks, - 1, - 1, - num_threads_per_block, - 1, - 1, - 0, - stream, - args2, - 0)); - total_work_size = wtile->w; - num_blocks = divide_up(total_work_size, num_threads_per_block); - cuda_assert(cuLaunchKernel(functions.adaptive_filter_y, - num_blocks, - 1, - 1, - num_threads_per_block, - 1, - 1, - 0, - stream, - args2, - 0)); -} - -void CUDADevice::adaptive_sampling_post(RenderTile &rtile, - WorkTile *wtile, - CUdeviceptr d_wtile, - CUstream stream) -{ - const int num_threads_per_block = functions.adaptive_num_threads_per_block; - uint total_work_size = wtile->h * wtile->w; - - void *args[] = {&d_wtile, &rtile.start_sample, &rtile.sample, &total_work_size}; - uint num_blocks = divide_up(total_work_size, num_threads_per_block); - cuda_assert(cuLaunchKernel(functions.adaptive_scale_samples, - num_blocks, - 1, - 1, - num_threads_per_block, - 1, - 1, - 0, - stream, - args, - 0)); -} - -void CUDADevice::render(DeviceTask &task, RenderTile &rtile, device_vector &work_tiles) -{ - scoped_timer timer(&rtile.buffers->render_time); - - if (have_error()) - return; - - CUDAContextScope scope(this); - CUfunction cuRender; - - /* Get kernel function. */ - if (rtile.task == RenderTile::BAKE) { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_bake")); - } - else if (task.integrator_branched) { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_branched_path_trace")); - } - else { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_path_trace")); - } - - if (have_error()) { - return; - } - - cuda_assert(cuFuncSetCacheConfig(cuRender, CU_FUNC_CACHE_PREFER_L1)); - - /* Allocate work tile. */ - work_tiles.alloc(1); - - WorkTile *wtile = work_tiles.data(); - wtile->x = rtile.x; - wtile->y = rtile.y; - wtile->w = rtile.w; - wtile->h = rtile.h; - wtile->offset = rtile.offset; - wtile->stride = rtile.stride; - wtile->buffer = (float *)(CUdeviceptr)rtile.buffer; - - /* Prepare work size. More step samples render faster, but for now we - * remain conservative for GPUs connected to a display to avoid driver - * timeouts and display freezing. */ - int min_blocks, num_threads_per_block; - cuda_assert( - cuOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, cuRender, NULL, 0, 0)); - if (!info.display_device) { - min_blocks *= 8; - } - - uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h); - - /* Render all samples. */ - int start_sample = rtile.start_sample; - int end_sample = rtile.start_sample + rtile.num_samples; - - for (int sample = start_sample; sample < end_sample;) { - /* Setup and copy work tile to device. */ - wtile->start_sample = sample; - wtile->num_samples = step_samples; - if (task.adaptive_sampling.use) { - wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples); - } - wtile->num_samples = min(wtile->num_samples, end_sample - sample); - work_tiles.copy_to_device(); - - CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer; - uint total_work_size = wtile->w * wtile->h * wtile->num_samples; - uint num_blocks = divide_up(total_work_size, num_threads_per_block); - - /* Launch kernel. */ - void *args[] = {&d_work_tiles, &total_work_size}; - - cuda_assert( - cuLaunchKernel(cuRender, num_blocks, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0)); - - /* Run the adaptive sampling kernels at selected samples aligned to step samples. */ - uint filter_sample = sample + wtile->num_samples - 1; - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) { - adaptive_sampling_filter(filter_sample, wtile, d_work_tiles); - } - - cuda_assert(cuCtxSynchronize()); - - /* Update progress. */ - sample += wtile->num_samples; - rtile.sample = sample; - task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); - - if (task.get_cancel()) { - if (task.need_finish_queue == false) - break; - } - } - - /* Finalize adaptive sampling. */ - if (task.adaptive_sampling.use) { - CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer; - adaptive_sampling_post(rtile, wtile, d_work_tiles); - cuda_assert(cuCtxSynchronize()); - task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); - } -} - -void CUDADevice::film_convert(DeviceTask &task, - device_ptr buffer, - device_ptr rgba_byte, - device_ptr rgba_half) -{ - if (have_error()) - return; - - CUDAContextScope scope(this); - - CUfunction cuFilmConvert; - CUdeviceptr d_rgba = map_pixels((rgba_byte) ? rgba_byte : rgba_half); - CUdeviceptr d_buffer = (CUdeviceptr)buffer; - - /* get kernel function */ - if (rgba_half) { - cuda_assert( - cuModuleGetFunction(&cuFilmConvert, cuModule, "kernel_cuda_convert_to_half_float")); - } - else { - cuda_assert(cuModuleGetFunction(&cuFilmConvert, cuModule, "kernel_cuda_convert_to_byte")); - } - - float sample_scale = 1.0f / (task.sample + 1); - - /* pass in parameters */ - void *args[] = {&d_rgba, - &d_buffer, - &sample_scale, - &task.x, - &task.y, - &task.w, - &task.h, - &task.offset, - &task.stride}; - - /* launch kernel */ - int threads_per_block; - cuda_assert(cuFuncGetAttribute( - &threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, cuFilmConvert)); - - int xthreads = (int)sqrt(threads_per_block); - int ythreads = (int)sqrt(threads_per_block); - int xblocks = (task.w + xthreads - 1) / xthreads; - int yblocks = (task.h + ythreads - 1) / ythreads; - - cuda_assert(cuFuncSetCacheConfig(cuFilmConvert, CU_FUNC_CACHE_PREFER_L1)); - - cuda_assert(cuLaunchKernel(cuFilmConvert, - xblocks, - yblocks, - 1, /* blocks */ - xthreads, - ythreads, - 1, /* threads */ - 0, - 0, - args, - 0)); - - unmap_pixels((rgba_byte) ? rgba_byte : rgba_half); - - cuda_assert(cuCtxSynchronize()); -} - -void CUDADevice::shader(DeviceTask &task) -{ - if (have_error()) - return; - - CUDAContextScope scope(this); - - CUfunction cuShader; - CUdeviceptr d_input = (CUdeviceptr)task.shader_input; - CUdeviceptr d_output = (CUdeviceptr)task.shader_output; - - /* get kernel function */ - if (task.shader_eval_type == SHADER_EVAL_DISPLACE) { - cuda_assert(cuModuleGetFunction(&cuShader, cuModule, "kernel_cuda_displace")); - } - else { - cuda_assert(cuModuleGetFunction(&cuShader, cuModule, "kernel_cuda_background")); - } - - /* do tasks in smaller chunks, so we can cancel it */ - const int shader_chunk_size = 65536; - const int start = task.shader_x; - const int end = task.shader_x + task.shader_w; - int offset = task.offset; - - bool canceled = false; - for (int sample = 0; sample < task.num_samples && !canceled; sample++) { - for (int shader_x = start; shader_x < end; shader_x += shader_chunk_size) { - int shader_w = min(shader_chunk_size, end - shader_x); - - /* pass in parameters */ - void *args[8]; - int arg = 0; - args[arg++] = &d_input; - args[arg++] = &d_output; - args[arg++] = &task.shader_eval_type; - if (task.shader_eval_type >= SHADER_EVAL_BAKE) { - args[arg++] = &task.shader_filter; - } - args[arg++] = &shader_x; - args[arg++] = &shader_w; - args[arg++] = &offset; - args[arg++] = &sample; - - /* launch kernel */ - int threads_per_block; - cuda_assert(cuFuncGetAttribute( - &threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, cuShader)); - - int xblocks = (shader_w + threads_per_block - 1) / threads_per_block; - - cuda_assert(cuFuncSetCacheConfig(cuShader, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuLaunchKernel(cuShader, - xblocks, - 1, - 1, /* blocks */ - threads_per_block, - 1, - 1, /* threads */ - 0, - 0, - args, - 0)); - - cuda_assert(cuCtxSynchronize()); - - if (task.get_cancel()) { - canceled = true; - break; - } - } - - task.update_progress(NULL); - } -} - -CUdeviceptr CUDADevice::map_pixels(device_ptr mem) -{ - if (!background) { - PixelMem pmem = pixel_mem_map[mem]; - CUdeviceptr buffer; - - size_t bytes; - cuda_assert(cuGraphicsMapResources(1, &pmem.cuPBOresource, 0)); - cuda_assert(cuGraphicsResourceGetMappedPointer(&buffer, &bytes, pmem.cuPBOresource)); - - return buffer; - } - - return (CUdeviceptr)mem; -} - -void CUDADevice::unmap_pixels(device_ptr mem) -{ - if (!background) { - PixelMem pmem = pixel_mem_map[mem]; - - cuda_assert(cuGraphicsUnmapResources(1, &pmem.cuPBOresource, 0)); - } -} - -void CUDADevice::pixels_alloc(device_memory &mem) -{ - PixelMem pmem; - - pmem.w = mem.data_width; - pmem.h = mem.data_height; - - CUDAContextScope scope(this); - - glGenBuffers(1, &pmem.cuPBO); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO); - if (mem.data_type == TYPE_HALF) - glBufferData( - GL_PIXEL_UNPACK_BUFFER, pmem.w * pmem.h * sizeof(GLhalf) * 4, NULL, GL_DYNAMIC_DRAW); - else - glBufferData( - GL_PIXEL_UNPACK_BUFFER, pmem.w * pmem.h * sizeof(uint8_t) * 4, NULL, GL_DYNAMIC_DRAW); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - - glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &pmem.cuTexId); - glBindTexture(GL_TEXTURE_2D, pmem.cuTexId); - if (mem.data_type == TYPE_HALF) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, pmem.w, pmem.h, 0, GL_RGBA, GL_HALF_FLOAT, NULL); - else - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, pmem.w, pmem.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glBindTexture(GL_TEXTURE_2D, 0); - - CUresult result = cuGraphicsGLRegisterBuffer( - &pmem.cuPBOresource, pmem.cuPBO, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE); - - if (result == CUDA_SUCCESS) { - mem.device_pointer = pmem.cuTexId; - pixel_mem_map[mem.device_pointer] = pmem; - - mem.device_size = mem.memory_size(); - stats.mem_alloc(mem.device_size); - - return; - } - else { - /* failed to register buffer, fallback to no interop */ - glDeleteBuffers(1, &pmem.cuPBO); - glDeleteTextures(1, &pmem.cuTexId); - - background = true; - } -} - -void CUDADevice::pixels_copy_from(device_memory &mem, int y, int w, int h) -{ - PixelMem pmem = pixel_mem_map[mem.device_pointer]; - - CUDAContextScope scope(this); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO); - uchar *pixels = (uchar *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_ONLY); - size_t offset = sizeof(uchar) * 4 * y * w; - memcpy((uchar *)mem.host_pointer + offset, pixels + offset, sizeof(uchar) * 4 * w * h); - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); -} - -void CUDADevice::pixels_free(device_memory &mem) -{ - if (mem.device_pointer) { - PixelMem pmem = pixel_mem_map[mem.device_pointer]; - - CUDAContextScope scope(this); - - cuda_assert(cuGraphicsUnregisterResource(pmem.cuPBOresource)); - glDeleteBuffers(1, &pmem.cuPBO); - glDeleteTextures(1, &pmem.cuTexId); - - pixel_mem_map.erase(pixel_mem_map.find(mem.device_pointer)); - mem.device_pointer = 0; - - stats.mem_free(mem.device_size); - mem.device_size = 0; - } -} - -void CUDADevice::draw_pixels(device_memory &mem, - int y, - int w, - int h, - int width, - int height, - int dx, - int dy, - int dw, - int dh, - bool transparent, - const DeviceDrawParams &draw_params) -{ - assert(mem.type == MEM_PIXELS); - - if (!background) { - const bool use_fallback_shader = (draw_params.bind_display_space_shader_cb == NULL); - PixelMem pmem = pixel_mem_map[mem.device_pointer]; - float *vpointer; - - CUDAContextScope scope(this); - - /* for multi devices, this assumes the inefficient method that we allocate - * all pixels on the device even though we only render to a subset */ - size_t offset = 4 * y * w; - - if (mem.data_type == TYPE_HALF) - offset *= sizeof(GLhalf); - else - offset *= sizeof(uint8_t); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, pmem.cuTexId); - if (mem.data_type == TYPE_HALF) { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_HALF_FLOAT, (void *)offset); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void *)offset); - } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - - if (transparent) { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } - - GLint shader_program; - if (use_fallback_shader) { - if (!bind_fallback_display_space_shader(dw, dh)) { - return; - } - shader_program = fallback_shader_program; - } - else { - draw_params.bind_display_space_shader_cb(); - glGetIntegerv(GL_CURRENT_PROGRAM, &shader_program); - } - - if (!vertex_buffer) { - glGenBuffers(1, &vertex_buffer); - } - - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); - /* invalidate old contents - - * avoids stalling if buffer is still waiting in queue to be rendered */ - glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW); - - vpointer = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - - if (vpointer) { - /* texture coordinate - vertex pair */ - vpointer[0] = 0.0f; - vpointer[1] = 0.0f; - vpointer[2] = dx; - vpointer[3] = dy; - - vpointer[4] = (float)w / (float)pmem.w; - vpointer[5] = 0.0f; - vpointer[6] = (float)width + dx; - vpointer[7] = dy; - - vpointer[8] = (float)w / (float)pmem.w; - vpointer[9] = (float)h / (float)pmem.h; - vpointer[10] = (float)width + dx; - vpointer[11] = (float)height + dy; - - vpointer[12] = 0.0f; - vpointer[13] = (float)h / (float)pmem.h; - vpointer[14] = dx; - vpointer[15] = (float)height + dy; - - glUnmapBuffer(GL_ARRAY_BUFFER); - } - - GLuint vertex_array_object; - GLuint position_attribute, texcoord_attribute; - - glGenVertexArrays(1, &vertex_array_object); - glBindVertexArray(vertex_array_object); - - texcoord_attribute = glGetAttribLocation(shader_program, "texCoord"); - position_attribute = glGetAttribLocation(shader_program, "pos"); - - glEnableVertexAttribArray(texcoord_attribute); - glEnableVertexAttribArray(position_attribute); - - glVertexAttribPointer( - texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0); - glVertexAttribPointer(position_attribute, - 2, - GL_FLOAT, - GL_FALSE, - 4 * sizeof(float), - (const GLvoid *)(sizeof(float) * 2)); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - if (use_fallback_shader) { - glUseProgram(0); - } - else { - draw_params.unbind_display_space_shader_cb(); - } - - if (transparent) { - glDisable(GL_BLEND); - } - - glBindTexture(GL_TEXTURE_2D, 0); - - return; - } - - Device::draw_pixels(mem, y, w, h, width, height, dx, dy, dw, dh, transparent, draw_params); -} - -void CUDADevice::thread_run(DeviceTask &task) -{ - CUDAContextScope scope(this); - - if (task.type == DeviceTask::RENDER) { - DeviceRequestedFeatures requested_features; - if (use_split_kernel()) { - if (split_kernel == NULL) { - split_kernel = new CUDASplitKernel(this); - split_kernel->load_kernels(requested_features); - } - } - - device_vector work_tiles(this, "work_tiles", MEM_READ_ONLY); - - /* keep rendering tiles until done */ - RenderTile tile; - DenoisingTask denoising(this, task); - - while (task.acquire_tile(this, tile, task.tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) { - if (use_split_kernel()) { - device_only_memory void_buffer(this, "void_buffer"); - split_kernel->path_trace(task, tile, void_buffer, void_buffer); - } - else { - render(task, tile, work_tiles); - } - } - else if (tile.task == RenderTile::BAKE) { - render(task, tile, work_tiles); - } - else if (tile.task == RenderTile::DENOISE) { - tile.sample = tile.start_sample + tile.num_samples; - - denoise(tile, denoising); - - task.update_progress(&tile, tile.w * tile.h); - } - - task.release_tile(tile); - - if (task.get_cancel()) { - if (task.need_finish_queue == false) - break; - } - } - - work_tiles.free(); - } - else if (task.type == DeviceTask::SHADER) { - shader(task); - - cuda_assert(cuCtxSynchronize()); - } - else if (task.type == DeviceTask::DENOISE_BUFFER) { - RenderTile tile; - tile.x = task.x; - tile.y = task.y; - tile.w = task.w; - tile.h = task.h; - tile.buffer = task.buffer; - tile.sample = task.sample + task.num_samples; - tile.num_samples = task.num_samples; - tile.start_sample = task.sample; - tile.offset = task.offset; - tile.stride = task.stride; - tile.buffers = task.buffers; - - DenoisingTask denoising(this, task); - denoise(tile, denoising); - task.update_progress(&tile, tile.w * tile.h); - } -} - -void CUDADevice::task_add(DeviceTask &task) -{ - CUDAContextScope scope(this); - - /* Load texture info. */ - load_texture_info(); - - /* Synchronize all memory copies before executing task. */ - cuda_assert(cuCtxSynchronize()); - - if (task.type == DeviceTask::FILM_CONVERT) { - /* must be done in main thread due to opengl access */ - film_convert(task, task.buffer, task.rgba_byte, task.rgba_half); - } - else { - task_pool.push([=] { - DeviceTask task_copy = task; - thread_run(task_copy); - }); - } -} - -void CUDADevice::task_wait() -{ - task_pool.wait(); -} - -void CUDADevice::task_cancel() -{ - task_pool.cancel(); -} - -/* redefine the cuda_assert macro so it can be used outside of the CUDADevice class - * now that the definition of that class is complete - */ -# undef cuda_assert -# define cuda_assert(stmt) \ - { \ - CUresult result = stmt; \ - if (result != CUDA_SUCCESS) { \ - const char *name = cuewErrorString(result); \ - device->set_error( \ - string_printf("%s in %s (device_cuda_impl.cpp:%d)", name, #stmt, __LINE__)); \ - } \ - } \ - (void)0 - -/* CUDA context scope. */ - -CUDAContextScope::CUDAContextScope(CUDADevice *device) : device(device) -{ - cuda_assert(cuCtxPushCurrent(device->cuContext)); -} - -CUDAContextScope::~CUDAContextScope() -{ - cuda_assert(cuCtxPopCurrent(NULL)); -} - -/* split kernel */ - -class CUDASplitKernelFunction : public SplitKernelFunction { - CUDADevice *device; - CUfunction func; - - public: - CUDASplitKernelFunction(CUDADevice *device, CUfunction func) : device(device), func(func) - { - } - - /* enqueue the kernel, returns false if there is an error */ - bool enqueue(const KernelDimensions &dim, device_memory & /*kg*/, device_memory & /*data*/) - { - return enqueue(dim, NULL); - } - - /* enqueue the kernel, returns false if there is an error */ - bool enqueue(const KernelDimensions &dim, void *args[]) - { - if (device->have_error()) - return false; - - CUDAContextScope scope(device); - - /* we ignore dim.local_size for now, as this is faster */ - int threads_per_block; - cuda_assert( - cuFuncGetAttribute(&threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, func)); - - int xblocks = (dim.global_size[0] * dim.global_size[1] + threads_per_block - 1) / - threads_per_block; - - cuda_assert(cuFuncSetCacheConfig(func, CU_FUNC_CACHE_PREFER_L1)); - - cuda_assert(cuLaunchKernel(func, - xblocks, - 1, - 1, /* blocks */ - threads_per_block, - 1, - 1, /* threads */ - 0, - 0, - args, - 0)); - - return !device->have_error(); - } -}; - -CUDASplitKernel::CUDASplitKernel(CUDADevice *device) : DeviceSplitKernel(device), device(device) -{ -} - -uint64_t CUDASplitKernel::state_buffer_size(device_memory & /*kg*/, - device_memory & /*data*/, - size_t num_threads) -{ - CUDAContextScope scope(device); - - device_vector size_buffer(device, "size_buffer", MEM_READ_WRITE); - size_buffer.alloc(1); - size_buffer.zero_to_device(); - - uint threads = num_threads; - CUdeviceptr d_size = (CUdeviceptr)size_buffer.device_pointer; - - struct args_t { - uint *num_threads; - CUdeviceptr *size; - }; - - args_t args = {&threads, &d_size}; - - CUfunction state_buffer_size; - cuda_assert( - cuModuleGetFunction(&state_buffer_size, device->cuModule, "kernel_cuda_state_buffer_size")); - - cuda_assert(cuLaunchKernel(state_buffer_size, 1, 1, 1, 1, 1, 1, 0, 0, (void **)&args, 0)); - - size_buffer.copy_from_device(0, 1, 1); - size_t size = size_buffer[0]; - size_buffer.free(); - - return size; -} - -bool CUDASplitKernel::enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory & /*kernel_globals*/, - device_memory & /*kernel_data*/, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flag, - device_memory &work_pool_wgs) -{ - CUDAContextScope scope(device); - - CUdeviceptr d_split_data = (CUdeviceptr)split_data.device_pointer; - CUdeviceptr d_ray_state = (CUdeviceptr)ray_state.device_pointer; - CUdeviceptr d_queue_index = (CUdeviceptr)queue_index.device_pointer; - CUdeviceptr d_use_queues_flag = (CUdeviceptr)use_queues_flag.device_pointer; - CUdeviceptr d_work_pool_wgs = (CUdeviceptr)work_pool_wgs.device_pointer; - - CUdeviceptr d_buffer = (CUdeviceptr)rtile.buffer; - - int end_sample = rtile.start_sample + rtile.num_samples; - int queue_size = dim.global_size[0] * dim.global_size[1]; - - struct args_t { - CUdeviceptr *split_data_buffer; - int *num_elements; - CUdeviceptr *ray_state; - int *start_sample; - int *end_sample; - int *sx; - int *sy; - int *sw; - int *sh; - int *offset; - int *stride; - CUdeviceptr *queue_index; - int *queuesize; - CUdeviceptr *use_queues_flag; - CUdeviceptr *work_pool_wgs; - int *num_samples; - CUdeviceptr *buffer; - }; - - args_t args = {&d_split_data, - &num_global_elements, - &d_ray_state, - &rtile.start_sample, - &end_sample, - &rtile.x, - &rtile.y, - &rtile.w, - &rtile.h, - &rtile.offset, - &rtile.stride, - &d_queue_index, - &queue_size, - &d_use_queues_flag, - &d_work_pool_wgs, - &rtile.num_samples, - &d_buffer}; - - CUfunction data_init; - cuda_assert( - cuModuleGetFunction(&data_init, device->cuModule, "kernel_cuda_path_trace_data_init")); - if (device->have_error()) { - return false; - } - - CUDASplitKernelFunction(device, data_init).enqueue(dim, (void **)&args); - - return !device->have_error(); -} - -SplitKernelFunction *CUDASplitKernel::get_split_kernel_function(const string &kernel_name, - const DeviceRequestedFeatures &) -{ - const CUDAContextScope scope(device); - - CUfunction func; - const CUresult result = cuModuleGetFunction( - &func, device->cuModule, (string("kernel_cuda_") + kernel_name).data()); - if (result != CUDA_SUCCESS) { - device->set_error(string_printf("Could not find kernel \"kernel_cuda_%s\" in module (%s)", - kernel_name.data(), - cuewErrorString(result))); - return NULL; - } - - return new CUDASplitKernelFunction(device, func); -} - -int2 CUDASplitKernel::split_kernel_local_size() -{ - return make_int2(32, 1); -} - -int2 CUDASplitKernel::split_kernel_global_size(device_memory &kg, - device_memory &data, - DeviceTask & /*task*/) -{ - CUDAContextScope scope(device); - size_t free; - size_t total; - - cuda_assert(cuMemGetInfo(&free, &total)); - - VLOG(1) << "Maximum device allocation size: " << string_human_readable_number(free) - << " bytes. (" << string_human_readable_size(free) << ")."; - - size_t num_elements = max_elements_for_max_buffer_size(kg, data, free / 2); - size_t side = round_down((int)sqrt(num_elements), 32); - int2 global_size = make_int2(side, round_down(num_elements / side, 16)); - VLOG(1) << "Global size: " << global_size << "."; - return global_size; -} - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp new file mode 100644 index 00000000000..37fab8f8293 --- /dev/null +++ b/intern/cycles/device/cuda/device_impl.cpp @@ -0,0 +1,1370 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include +# include +# include +# include +# include + +# include "device/cuda/device_impl.h" + +# include "render/buffers.h" + +# include "util/util_debug.h" +# include "util/util_foreach.h" +# include "util/util_logging.h" +# include "util/util_map.h" +# include "util/util_md5.h" +# include "util/util_opengl.h" +# include "util/util_path.h" +# include "util/util_string.h" +# include "util/util_system.h" +# include "util/util_time.h" +# include "util/util_types.h" +# include "util/util_windows.h" + +CCL_NAMESPACE_BEGIN + +class CUDADevice; + +bool CUDADevice::have_precompiled_kernels() +{ + string cubins_path = path_get("lib"); + return path_exists(cubins_path); +} + +bool CUDADevice::show_samples() const +{ + /* The CUDADevice only processes one tile at a time, so showing samples is fine. */ + return true; +} + +BVHLayoutMask CUDADevice::get_bvh_layout_mask() const +{ + return BVH_LAYOUT_BVH2; +} + +void CUDADevice::set_error(const string &error) +{ + Device::set_error(error); + + if (first_error) { + fprintf(stderr, "\nRefer to the Cycles GPU rendering documentation for possible solutions:\n"); + fprintf(stderr, + "https://docs.blender.org/manual/en/latest/render/cycles/gpu_rendering.html\n\n"); + first_error = false; + } +} + +CUDADevice::CUDADevice(const DeviceInfo &info, Stats &stats, Profiler &profiler) + : Device(info, stats, profiler), texture_info(this, "__texture_info", MEM_GLOBAL) +{ + first_error = true; + + cuDevId = info.num; + cuDevice = 0; + cuContext = 0; + + cuModule = 0; + + need_texture_info = false; + + device_texture_headroom = 0; + device_working_headroom = 0; + move_texture_to_host = false; + map_host_limit = 0; + map_host_used = 0; + can_map_host = 0; + pitch_alignment = 0; + + /* Initialize CUDA. */ + CUresult result = cuInit(0); + if (result != CUDA_SUCCESS) { + set_error(string_printf("Failed to initialize CUDA runtime (%s)", cuewErrorString(result))); + return; + } + + /* Setup device and context. */ + result = cuDeviceGet(&cuDevice, cuDevId); + if (result != CUDA_SUCCESS) { + set_error(string_printf("Failed to get CUDA device handle from ordinal (%s)", + cuewErrorString(result))); + return; + } + + /* CU_CTX_MAP_HOST for mapping host memory when out of device memory. + * CU_CTX_LMEM_RESIZE_TO_MAX for reserving local memory ahead of render, + * so we can predict which memory to map to host. */ + cuda_assert( + cuDeviceGetAttribute(&can_map_host, CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY, cuDevice)); + + cuda_assert(cuDeviceGetAttribute( + &pitch_alignment, CU_DEVICE_ATTRIBUTE_TEXTURE_PITCH_ALIGNMENT, cuDevice)); + + unsigned int ctx_flags = CU_CTX_LMEM_RESIZE_TO_MAX; + if (can_map_host) { + ctx_flags |= CU_CTX_MAP_HOST; + init_host_memory(); + } + + /* Create context. */ + result = cuCtxCreate(&cuContext, ctx_flags, cuDevice); + + if (result != CUDA_SUCCESS) { + set_error(string_printf("Failed to create CUDA context (%s)", cuewErrorString(result))); + return; + } + + int major, minor; + cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); + cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); + cuDevArchitecture = major * 100 + minor * 10; + + /* Pop context set by cuCtxCreate. */ + cuCtxPopCurrent(NULL); +} + +CUDADevice::~CUDADevice() +{ + texture_info.free(); + + cuda_assert(cuCtxDestroy(cuContext)); +} + +bool CUDADevice::support_device(const uint /*kernel_features*/) +{ + int major, minor; + cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); + cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); + + /* We only support sm_30 and above */ + if (major < 3) { + set_error(string_printf( + "CUDA backend requires compute capability 3.0 or up, but found %d.%d.", major, minor)); + return false; + } + + return true; +} + +bool CUDADevice::check_peer_access(Device *peer_device) +{ + if (peer_device == this) { + return false; + } + if (peer_device->info.type != DEVICE_CUDA && peer_device->info.type != DEVICE_OPTIX) { + return false; + } + + CUDADevice *const peer_device_cuda = static_cast(peer_device); + + int can_access = 0; + cuda_assert(cuDeviceCanAccessPeer(&can_access, cuDevice, peer_device_cuda->cuDevice)); + if (can_access == 0) { + return false; + } + + // Ensure array access over the link is possible as well (for 3D textures) + cuda_assert(cuDeviceGetP2PAttribute(&can_access, + CU_DEVICE_P2P_ATTRIBUTE_CUDA_ARRAY_ACCESS_SUPPORTED, + cuDevice, + peer_device_cuda->cuDevice)); + if (can_access == 0) { + return false; + } + + // Enable peer access in both directions + { + const CUDAContextScope scope(this); + CUresult result = cuCtxEnablePeerAccess(peer_device_cuda->cuContext, 0); + if (result != CUDA_SUCCESS) { + set_error(string_printf("Failed to enable peer access on CUDA context (%s)", + cuewErrorString(result))); + return false; + } + } + { + const CUDAContextScope scope(peer_device_cuda); + CUresult result = cuCtxEnablePeerAccess(cuContext, 0); + if (result != CUDA_SUCCESS) { + set_error(string_printf("Failed to enable peer access on CUDA context (%s)", + cuewErrorString(result))); + return false; + } + } + + return true; +} + +bool CUDADevice::use_adaptive_compilation() +{ + return DebugFlags().cuda.adaptive_compile; +} + +/* Common NVCC flags which stays the same regardless of shading model, + * kernel sources md5 and only depends on compiler or compilation settings. + */ +string CUDADevice::compile_kernel_get_common_cflags(const uint kernel_features) +{ + const int machine = system_cpu_bits(); + const string source_path = path_get("source"); + const string include_path = source_path; + string cflags = string_printf( + "-m%d " + "--ptxas-options=\"-v\" " + "--use_fast_math " + "-DNVCC " + "-I\"%s\"", + machine, + include_path.c_str()); + if (use_adaptive_compilation()) { + cflags += " -D__KERNEL_FEATURES__=" + to_string(kernel_features); + } + const char *extra_cflags = getenv("CYCLES_CUDA_EXTRA_CFLAGS"); + if (extra_cflags) { + cflags += string(" ") + string(extra_cflags); + } + +# ifdef WITH_NANOVDB + cflags += " -DWITH_NANOVDB"; +# endif + + return cflags; +} + +string CUDADevice::compile_kernel(const uint kernel_features, + const char *name, + const char *base, + bool force_ptx) +{ + /* Compute kernel name. */ + int major, minor; + cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); + cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); + + /* Attempt to use kernel provided with Blender. */ + if (!use_adaptive_compilation()) { + if (!force_ptx) { + const string cubin = path_get(string_printf("lib/%s_sm_%d%d.cubin", name, major, minor)); + VLOG(1) << "Testing for pre-compiled kernel " << cubin << "."; + if (path_exists(cubin)) { + VLOG(1) << "Using precompiled kernel."; + return cubin; + } + } + + /* The driver can JIT-compile PTX generated for older generations, so find the closest one. */ + int ptx_major = major, ptx_minor = minor; + while (ptx_major >= 3) { + const string ptx = path_get( + string_printf("lib/%s_compute_%d%d.ptx", name, ptx_major, ptx_minor)); + VLOG(1) << "Testing for pre-compiled kernel " << ptx << "."; + if (path_exists(ptx)) { + VLOG(1) << "Using precompiled kernel."; + return ptx; + } + + if (ptx_minor > 0) { + ptx_minor--; + } + else { + ptx_major--; + ptx_minor = 9; + } + } + } + + /* Try to use locally compiled kernel. */ + string source_path = path_get("source"); + const string source_md5 = path_files_md5_hash(source_path); + + /* We include cflags into md5 so changing cuda toolkit or changing other + * compiler command line arguments makes sure cubin gets re-built. + */ + string common_cflags = compile_kernel_get_common_cflags(kernel_features); + const string kernel_md5 = util_md5_string(source_md5 + common_cflags); + + const char *const kernel_ext = force_ptx ? "ptx" : "cubin"; + const char *const kernel_arch = force_ptx ? "compute" : "sm"; + const string cubin_file = string_printf( + "cycles_%s_%s_%d%d_%s.%s", name, kernel_arch, major, minor, kernel_md5.c_str(), kernel_ext); + const string cubin = path_cache_get(path_join("kernels", cubin_file)); + VLOG(1) << "Testing for locally compiled kernel " << cubin << "."; + if (path_exists(cubin)) { + VLOG(1) << "Using locally compiled kernel."; + return cubin; + } + +# ifdef _WIN32 + if (!use_adaptive_compilation() && have_precompiled_kernels()) { + if (major < 3) { + set_error( + string_printf("CUDA backend requires compute capability 3.0 or up, but found %d.%d. " + "Your GPU is not supported.", + major, + minor)); + } + else { + set_error( + string_printf("CUDA binary kernel for this graphics card compute " + "capability (%d.%d) not found.", + major, + minor)); + } + return string(); + } +# endif + + /* Compile. */ + const char *const nvcc = cuewCompilerPath(); + if (nvcc == NULL) { + set_error( + "CUDA nvcc compiler not found. " + "Install CUDA toolkit in default location."); + return string(); + } + + const int nvcc_cuda_version = cuewCompilerVersion(); + VLOG(1) << "Found nvcc " << nvcc << ", CUDA version " << nvcc_cuda_version << "."; + if (nvcc_cuda_version < 101) { + printf( + "Unsupported CUDA version %d.%d detected, " + "you need CUDA 10.1 or newer.\n", + nvcc_cuda_version / 10, + nvcc_cuda_version % 10); + return string(); + } + else if (!(nvcc_cuda_version == 101 || nvcc_cuda_version == 102 || nvcc_cuda_version == 111 || + nvcc_cuda_version == 112 || nvcc_cuda_version == 113 || nvcc_cuda_version == 114)) { + printf( + "CUDA version %d.%d detected, build may succeed but only " + "CUDA 10.1 to 11.4 are officially supported.\n", + nvcc_cuda_version / 10, + nvcc_cuda_version % 10); + } + + double starttime = time_dt(); + + path_create_directories(cubin); + + source_path = path_join(path_join(source_path, "kernel"), + path_join("device", path_join(base, string_printf("%s.cu", name)))); + + string command = string_printf( + "\"%s\" " + "-arch=%s_%d%d " + "--%s \"%s\" " + "-o \"%s\" " + "%s", + nvcc, + kernel_arch, + major, + minor, + kernel_ext, + source_path.c_str(), + cubin.c_str(), + common_cflags.c_str()); + + printf("Compiling CUDA kernel ...\n%s\n", command.c_str()); + +# ifdef _WIN32 + command = "call " + command; +# endif + if (system(command.c_str()) != 0) { + set_error( + "Failed to execute compilation command, " + "see console for details."); + return string(); + } + + /* Verify if compilation succeeded */ + if (!path_exists(cubin)) { + set_error( + "CUDA kernel compilation failed, " + "see console for details."); + return string(); + } + + printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime); + + return cubin; +} + +bool CUDADevice::load_kernels(const uint kernel_features) +{ + /* TODO(sergey): Support kernels re-load for CUDA devices. + * + * Currently re-loading kernel will invalidate memory pointers, + * causing problems in cuCtxSynchronize. + */ + if (cuModule) { + VLOG(1) << "Skipping kernel reload, not currently supported."; + return true; + } + + /* check if cuda init succeeded */ + if (cuContext == 0) + return false; + + /* check if GPU is supported */ + if (!support_device(kernel_features)) + return false; + + /* get kernel */ + const char *kernel_name = "kernel"; + string cubin = compile_kernel(kernel_features, kernel_name); + if (cubin.empty()) + return false; + + /* open module */ + CUDAContextScope scope(this); + + string cubin_data; + CUresult result; + + if (path_read_text(cubin, cubin_data)) + result = cuModuleLoadData(&cuModule, cubin_data.c_str()); + else + result = CUDA_ERROR_FILE_NOT_FOUND; + + if (result != CUDA_SUCCESS) + set_error(string_printf( + "Failed to load CUDA kernel from '%s' (%s)", cubin.c_str(), cuewErrorString(result))); + + if (result == CUDA_SUCCESS) { + kernels.load(this); + reserve_local_memory(kernel_features); + } + + return (result == CUDA_SUCCESS); +} + +void CUDADevice::reserve_local_memory(const uint /* kernel_features */) +{ + /* Together with CU_CTX_LMEM_RESIZE_TO_MAX, this reserves local memory + * needed for kernel launches, so that we can reliably figure out when + * to allocate scene data in mapped host memory. */ + size_t total = 0, free_before = 0, free_after = 0; + + { + CUDAContextScope scope(this); + cuMemGetInfo(&free_before, &total); + } + + { + /* Use the biggest kernel for estimation. */ + const DeviceKernel test_kernel = DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE; + + /* Launch kernel, using just 1 block appears sufficient to reserve memory for all + * multiprocessors. It would be good to do this in parallel for the multi GPU case + * still to make it faster. */ + CUDADeviceQueue queue(this); + + void *d_path_index = nullptr; + void *d_render_buffer = nullptr; + int d_work_size = 0; + void *args[] = {&d_path_index, &d_render_buffer, &d_work_size}; + + queue.init_execution(); + queue.enqueue(test_kernel, 1, args); + queue.synchronize(); + } + + { + CUDAContextScope scope(this); + cuMemGetInfo(&free_after, &total); + } + + VLOG(1) << "Local memory reserved " << string_human_readable_number(free_before - free_after) + << " bytes. (" << string_human_readable_size(free_before - free_after) << ")"; + +# if 0 + /* For testing mapped host memory, fill up device memory. */ + const size_t keep_mb = 1024; + + while (free_after > keep_mb * 1024 * 1024LL) { + CUdeviceptr tmp; + cuda_assert(cuMemAlloc(&tmp, 10 * 1024 * 1024LL)); + cuMemGetInfo(&free_after, &total); + } +# endif +} + +void CUDADevice::init_host_memory() +{ + /* Limit amount of host mapped memory, because allocating too much can + * cause system instability. Leave at least half or 4 GB of system + * memory free, whichever is smaller. */ + size_t default_limit = 4 * 1024 * 1024 * 1024LL; + size_t system_ram = system_physical_ram(); + + if (system_ram > 0) { + if (system_ram / 2 > default_limit) { + map_host_limit = system_ram - default_limit; + } + else { + map_host_limit = system_ram / 2; + } + } + else { + VLOG(1) << "Mapped host memory disabled, failed to get system RAM"; + map_host_limit = 0; + } + + /* Amount of device memory to keep is free after texture memory + * and working memory allocations respectively. We set the working + * memory limit headroom lower so that some space is left after all + * texture memory allocations. */ + device_working_headroom = 32 * 1024 * 1024LL; // 32MB + device_texture_headroom = 128 * 1024 * 1024LL; // 128MB + + VLOG(1) << "Mapped host memory limit set to " << string_human_readable_number(map_host_limit) + << " bytes. (" << string_human_readable_size(map_host_limit) << ")"; +} + +void CUDADevice::load_texture_info() +{ + if (need_texture_info) { + /* Unset flag before copying, so this does not loop indefinitely if the copy below calls + * into 'move_textures_to_host' (which calls 'load_texture_info' again). */ + need_texture_info = false; + texture_info.copy_to_device(); + } +} + +void CUDADevice::move_textures_to_host(size_t size, bool for_texture) +{ + /* Break out of recursive call, which can happen when moving memory on a multi device. */ + static bool any_device_moving_textures_to_host = false; + if (any_device_moving_textures_to_host) { + return; + } + + /* Signal to reallocate textures in host memory only. */ + move_texture_to_host = true; + + while (size > 0) { + /* Find suitable memory allocation to move. */ + device_memory *max_mem = NULL; + size_t max_size = 0; + bool max_is_image = false; + + thread_scoped_lock lock(cuda_mem_map_mutex); + foreach (CUDAMemMap::value_type &pair, cuda_mem_map) { + device_memory &mem = *pair.first; + CUDAMem *cmem = &pair.second; + + /* Can only move textures allocated on this device (and not those from peer devices). + * And need to ignore memory that is already on the host. */ + if (!mem.is_resident(this) || cmem->use_mapped_host) { + continue; + } + + bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && + (&mem != &texture_info); + bool is_image = is_texture && (mem.data_height > 1); + + /* Can't move this type of memory. */ + if (!is_texture || cmem->array) { + continue; + } + + /* For other textures, only move image textures. */ + if (for_texture && !is_image) { + continue; + } + + /* Try to move largest allocation, prefer moving images. */ + if (is_image > max_is_image || (is_image == max_is_image && mem.device_size > max_size)) { + max_is_image = is_image; + max_size = mem.device_size; + max_mem = &mem; + } + } + lock.unlock(); + + /* Move to host memory. This part is mutex protected since + * multiple CUDA devices could be moving the memory. The + * first one will do it, and the rest will adopt the pointer. */ + if (max_mem) { + VLOG(1) << "Move memory from device to host: " << max_mem->name; + + static thread_mutex move_mutex; + thread_scoped_lock lock(move_mutex); + + any_device_moving_textures_to_host = true; + + /* Potentially need to call back into multi device, so pointer mapping + * and peer devices are updated. This is also necessary since the device + * pointer may just be a key here, so cannot be accessed and freed directly. + * Unfortunately it does mean that memory is reallocated on all other + * devices as well, which is potentially dangerous when still in use (since + * a thread rendering on another devices would only be caught in this mutex + * if it so happens to do an allocation at the same time as well. */ + max_mem->device_copy_to(); + size = (max_size >= size) ? 0 : size - max_size; + + any_device_moving_textures_to_host = false; + } + else { + break; + } + } + + /* Unset flag before texture info is reloaded, since it should stay in device memory. */ + move_texture_to_host = false; + + /* Update texture info array with new pointers. */ + load_texture_info(); +} + +CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_padding) +{ + CUDAContextScope scope(this); + + CUdeviceptr device_pointer = 0; + size_t size = mem.memory_size() + pitch_padding; + + CUresult mem_alloc_result = CUDA_ERROR_OUT_OF_MEMORY; + const char *status = ""; + + /* First try allocating in device memory, respecting headroom. We make + * an exception for texture info. It is small and frequently accessed, + * so treat it as working memory. + * + * If there is not enough room for working memory, we will try to move + * textures to host memory, assuming the performance impact would have + * been worse for working memory. */ + bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && (&mem != &texture_info); + bool is_image = is_texture && (mem.data_height > 1); + + size_t headroom = (is_texture) ? device_texture_headroom : device_working_headroom; + + size_t total = 0, free = 0; + cuMemGetInfo(&free, &total); + + /* Move textures to host memory if needed. */ + if (!move_texture_to_host && !is_image && (size + headroom) >= free && can_map_host) { + move_textures_to_host(size + headroom - free, is_texture); + cuMemGetInfo(&free, &total); + } + + /* Allocate in device memory. */ + if (!move_texture_to_host && (size + headroom) < free) { + mem_alloc_result = cuMemAlloc(&device_pointer, size); + if (mem_alloc_result == CUDA_SUCCESS) { + status = " in device memory"; + } + } + + /* Fall back to mapped host memory if needed and possible. */ + + void *shared_pointer = 0; + + if (mem_alloc_result != CUDA_SUCCESS && can_map_host) { + if (mem.shared_pointer) { + /* Another device already allocated host memory. */ + mem_alloc_result = CUDA_SUCCESS; + shared_pointer = mem.shared_pointer; + } + else if (map_host_used + size < map_host_limit) { + /* Allocate host memory ourselves. */ + mem_alloc_result = cuMemHostAlloc( + &shared_pointer, size, CU_MEMHOSTALLOC_DEVICEMAP | CU_MEMHOSTALLOC_WRITECOMBINED); + + assert((mem_alloc_result == CUDA_SUCCESS && shared_pointer != 0) || + (mem_alloc_result != CUDA_SUCCESS && shared_pointer == 0)); + } + + if (mem_alloc_result == CUDA_SUCCESS) { + cuda_assert(cuMemHostGetDevicePointer_v2(&device_pointer, shared_pointer, 0)); + map_host_used += size; + status = " in host memory"; + } + } + + if (mem_alloc_result != CUDA_SUCCESS) { + status = " failed, out of device and host memory"; + set_error("System is out of GPU and shared host memory"); + } + + if (mem.name) { + VLOG(1) << "Buffer allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")" << status; + } + + mem.device_pointer = (device_ptr)device_pointer; + mem.device_size = size; + stats.mem_alloc(size); + + if (!mem.device_pointer) { + return NULL; + } + + /* Insert into map of allocations. */ + thread_scoped_lock lock(cuda_mem_map_mutex); + CUDAMem *cmem = &cuda_mem_map[&mem]; + if (shared_pointer != 0) { + /* Replace host pointer with our host allocation. Only works if + * CUDA memory layout is the same and has no pitch padding. Also + * does not work if we move textures to host during a render, + * since other devices might be using the memory. */ + + if (!move_texture_to_host && pitch_padding == 0 && mem.host_pointer && + mem.host_pointer != shared_pointer) { + memcpy(shared_pointer, mem.host_pointer, size); + + /* A Call to device_memory::host_free() should be preceded by + * a call to device_memory::device_free() for host memory + * allocated by a device to be handled properly. Two exceptions + * are here and a call in OptiXDevice::generic_alloc(), where + * the current host memory can be assumed to be allocated by + * device_memory::host_alloc(), not by a device */ + + mem.host_free(); + mem.host_pointer = shared_pointer; + } + mem.shared_pointer = shared_pointer; + mem.shared_counter++; + cmem->use_mapped_host = true; + } + else { + cmem->use_mapped_host = false; + } + + return cmem; +} + +void CUDADevice::generic_copy_to(device_memory &mem) +{ + if (!mem.host_pointer || !mem.device_pointer) { + return; + } + + /* If use_mapped_host of mem is false, the current device only uses device memory allocated by + * cuMemAlloc regardless of mem.host_pointer and mem.shared_pointer, and should copy data from + * mem.host_pointer. */ + thread_scoped_lock lock(cuda_mem_map_mutex); + if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { + const CUDAContextScope scope(this); + cuda_assert( + cuMemcpyHtoD((CUdeviceptr)mem.device_pointer, mem.host_pointer, mem.memory_size())); + } +} + +void CUDADevice::generic_free(device_memory &mem) +{ + if (mem.device_pointer) { + CUDAContextScope scope(this); + thread_scoped_lock lock(cuda_mem_map_mutex); + const CUDAMem &cmem = cuda_mem_map[&mem]; + + /* If cmem.use_mapped_host is true, reference counting is used + * to safely free a mapped host memory. */ + + if (cmem.use_mapped_host) { + assert(mem.shared_pointer); + if (mem.shared_pointer) { + assert(mem.shared_counter > 0); + if (--mem.shared_counter == 0) { + if (mem.host_pointer == mem.shared_pointer) { + mem.host_pointer = 0; + } + cuMemFreeHost(mem.shared_pointer); + mem.shared_pointer = 0; + } + } + map_host_used -= mem.device_size; + } + else { + /* Free device memory. */ + cuda_assert(cuMemFree(mem.device_pointer)); + } + + stats.mem_free(mem.device_size); + mem.device_pointer = 0; + mem.device_size = 0; + + cuda_mem_map.erase(cuda_mem_map.find(&mem)); + } +} + +void CUDADevice::mem_alloc(device_memory &mem) +{ + if (mem.type == MEM_TEXTURE) { + assert(!"mem_alloc not supported for textures."); + } + else if (mem.type == MEM_GLOBAL) { + assert(!"mem_alloc not supported for global memory."); + } + else { + generic_alloc(mem); + } +} + +void CUDADevice::mem_copy_to(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + global_alloc(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + tex_alloc((device_texture &)mem); + } + else { + if (!mem.device_pointer) { + generic_alloc(mem); + } + generic_copy_to(mem); + } +} + +void CUDADevice::mem_copy_from(device_memory &mem, int y, int w, int h, int elem) +{ + if (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) { + assert(!"mem_copy_from not supported for textures."); + } + else if (mem.host_pointer) { + const size_t size = elem * w * h; + const size_t offset = elem * y * w; + + if (mem.device_pointer) { + const CUDAContextScope scope(this); + cuda_assert(cuMemcpyDtoH( + (char *)mem.host_pointer + offset, (CUdeviceptr)mem.device_pointer + offset, size)); + } + else { + memset((char *)mem.host_pointer + offset, 0, size); + } + } +} + +void CUDADevice::mem_zero(device_memory &mem) +{ + if (!mem.device_pointer) { + mem_alloc(mem); + } + if (!mem.device_pointer) { + return; + } + + /* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory + * regardless of mem.host_pointer and mem.shared_pointer. */ + thread_scoped_lock lock(cuda_mem_map_mutex); + if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { + const CUDAContextScope scope(this); + cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size())); + } + else if (mem.host_pointer) { + memset(mem.host_pointer, 0, mem.memory_size()); + } +} + +void CUDADevice::mem_free(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + } + else { + generic_free(mem); + } +} + +device_ptr CUDADevice::mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) +{ + return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); +} + +void CUDADevice::const_copy_to(const char *name, void *host, size_t size) +{ + CUDAContextScope scope(this); + CUdeviceptr mem; + size_t bytes; + + cuda_assert(cuModuleGetGlobal(&mem, &bytes, cuModule, name)); + // assert(bytes == size); + cuda_assert(cuMemcpyHtoD(mem, host, size)); +} + +void CUDADevice::global_alloc(device_memory &mem) +{ + if (mem.is_resident(this)) { + generic_alloc(mem); + generic_copy_to(mem); + } + + const_copy_to(mem.name, &mem.device_pointer, sizeof(mem.device_pointer)); +} + +void CUDADevice::global_free(device_memory &mem) +{ + if (mem.is_resident(this) && mem.device_pointer) { + generic_free(mem); + } +} + +void CUDADevice::tex_alloc(device_texture &mem) +{ + CUDAContextScope scope(this); + + /* General variables for both architectures */ + string bind_name = mem.name; + size_t dsize = datatype_size(mem.data_type); + size_t size = mem.memory_size(); + + CUaddress_mode address_mode = CU_TR_ADDRESS_MODE_WRAP; + switch (mem.info.extension) { + case EXTENSION_REPEAT: + address_mode = CU_TR_ADDRESS_MODE_WRAP; + break; + case EXTENSION_EXTEND: + address_mode = CU_TR_ADDRESS_MODE_CLAMP; + break; + case EXTENSION_CLIP: + address_mode = CU_TR_ADDRESS_MODE_BORDER; + break; + default: + assert(0); + break; + } + + CUfilter_mode filter_mode; + if (mem.info.interpolation == INTERPOLATION_CLOSEST) { + filter_mode = CU_TR_FILTER_MODE_POINT; + } + else { + filter_mode = CU_TR_FILTER_MODE_LINEAR; + } + + /* Image Texture Storage */ + CUarray_format_enum format; + switch (mem.data_type) { + case TYPE_UCHAR: + format = CU_AD_FORMAT_UNSIGNED_INT8; + break; + case TYPE_UINT16: + format = CU_AD_FORMAT_UNSIGNED_INT16; + break; + case TYPE_UINT: + format = CU_AD_FORMAT_UNSIGNED_INT32; + break; + case TYPE_INT: + format = CU_AD_FORMAT_SIGNED_INT32; + break; + case TYPE_FLOAT: + format = CU_AD_FORMAT_FLOAT; + break; + case TYPE_HALF: + format = CU_AD_FORMAT_HALF; + break; + default: + assert(0); + return; + } + + CUDAMem *cmem = NULL; + CUarray array_3d = NULL; + size_t src_pitch = mem.data_width * dsize * mem.data_elements; + size_t dst_pitch = src_pitch; + + if (!mem.is_resident(this)) { + thread_scoped_lock lock(cuda_mem_map_mutex); + cmem = &cuda_mem_map[&mem]; + cmem->texobject = 0; + + if (mem.data_depth > 1) { + array_3d = (CUarray)mem.device_pointer; + cmem->array = array_3d; + } + else if (mem.data_height > 0) { + dst_pitch = align_up(src_pitch, pitch_alignment); + } + } + else if (mem.data_depth > 1) { + /* 3D texture using array, there is no API for linear memory. */ + CUDA_ARRAY3D_DESCRIPTOR desc; + + desc.Width = mem.data_width; + desc.Height = mem.data_height; + desc.Depth = mem.data_depth; + desc.Format = format; + desc.NumChannels = mem.data_elements; + desc.Flags = 0; + + VLOG(1) << "Array 3D allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; + + cuda_assert(cuArray3DCreate(&array_3d, &desc)); + + if (!array_3d) { + return; + } + + CUDA_MEMCPY3D param; + memset(¶m, 0, sizeof(param)); + param.dstMemoryType = CU_MEMORYTYPE_ARRAY; + param.dstArray = array_3d; + param.srcMemoryType = CU_MEMORYTYPE_HOST; + param.srcHost = mem.host_pointer; + param.srcPitch = src_pitch; + param.WidthInBytes = param.srcPitch; + param.Height = mem.data_height; + param.Depth = mem.data_depth; + + cuda_assert(cuMemcpy3D(¶m)); + + mem.device_pointer = (device_ptr)array_3d; + mem.device_size = size; + stats.mem_alloc(size); + + thread_scoped_lock lock(cuda_mem_map_mutex); + cmem = &cuda_mem_map[&mem]; + cmem->texobject = 0; + cmem->array = array_3d; + } + else if (mem.data_height > 0) { + /* 2D texture, using pitch aligned linear memory. */ + dst_pitch = align_up(src_pitch, pitch_alignment); + size_t dst_size = dst_pitch * mem.data_height; + + cmem = generic_alloc(mem, dst_size - mem.memory_size()); + if (!cmem) { + return; + } + + CUDA_MEMCPY2D param; + memset(¶m, 0, sizeof(param)); + param.dstMemoryType = CU_MEMORYTYPE_DEVICE; + param.dstDevice = mem.device_pointer; + param.dstPitch = dst_pitch; + param.srcMemoryType = CU_MEMORYTYPE_HOST; + param.srcHost = mem.host_pointer; + param.srcPitch = src_pitch; + param.WidthInBytes = param.srcPitch; + param.Height = mem.data_height; + + cuda_assert(cuMemcpy2DUnaligned(¶m)); + } + else { + /* 1D texture, using linear memory. */ + cmem = generic_alloc(mem); + if (!cmem) { + return; + } + + cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size)); + } + + /* Resize once */ + const uint slot = mem.slot; + if (slot >= texture_info.size()) { + /* Allocate some slots in advance, to reduce amount + * of re-allocations. */ + texture_info.resize(slot + 128); + } + + /* Set Mapping and tag that we need to (re-)upload to device */ + texture_info[slot] = mem.info; + need_texture_info = true; + + if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && + mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { + /* Kepler+, bindless textures. */ + CUDA_RESOURCE_DESC resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + + if (array_3d) { + resDesc.resType = CU_RESOURCE_TYPE_ARRAY; + resDesc.res.array.hArray = array_3d; + resDesc.flags = 0; + } + else if (mem.data_height > 0) { + resDesc.resType = CU_RESOURCE_TYPE_PITCH2D; + resDesc.res.pitch2D.devPtr = mem.device_pointer; + resDesc.res.pitch2D.format = format; + resDesc.res.pitch2D.numChannels = mem.data_elements; + resDesc.res.pitch2D.height = mem.data_height; + resDesc.res.pitch2D.width = mem.data_width; + resDesc.res.pitch2D.pitchInBytes = dst_pitch; + } + else { + resDesc.resType = CU_RESOURCE_TYPE_LINEAR; + resDesc.res.linear.devPtr = mem.device_pointer; + resDesc.res.linear.format = format; + resDesc.res.linear.numChannels = mem.data_elements; + resDesc.res.linear.sizeInBytes = mem.device_size; + } + + CUDA_TEXTURE_DESC texDesc; + memset(&texDesc, 0, sizeof(texDesc)); + texDesc.addressMode[0] = address_mode; + texDesc.addressMode[1] = address_mode; + texDesc.addressMode[2] = address_mode; + texDesc.filterMode = filter_mode; + texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; + + thread_scoped_lock lock(cuda_mem_map_mutex); + cmem = &cuda_mem_map[&mem]; + + cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL)); + + texture_info[slot].data = (uint64_t)cmem->texobject; + } + else { + texture_info[slot].data = (uint64_t)mem.device_pointer; + } +} + +void CUDADevice::tex_free(device_texture &mem) +{ + if (mem.device_pointer) { + CUDAContextScope scope(this); + thread_scoped_lock lock(cuda_mem_map_mutex); + const CUDAMem &cmem = cuda_mem_map[&mem]; + + if (cmem.texobject) { + /* Free bindless texture. */ + cuTexObjectDestroy(cmem.texobject); + } + + if (!mem.is_resident(this)) { + /* Do not free memory here, since it was allocated on a different device. */ + cuda_mem_map.erase(cuda_mem_map.find(&mem)); + } + else if (cmem.array) { + /* Free array. */ + cuArrayDestroy(cmem.array); + stats.mem_free(mem.device_size); + mem.device_pointer = 0; + mem.device_size = 0; + + cuda_mem_map.erase(cuda_mem_map.find(&mem)); + } + else { + lock.unlock(); + generic_free(mem); + } + } +} + +# if 0 +void CUDADevice::render(DeviceTask &task, + RenderTile &rtile, + device_vector &work_tiles) +{ + scoped_timer timer(&rtile.buffers->render_time); + + if (have_error()) + return; + + CUDAContextScope scope(this); + CUfunction cuRender; + + /* Get kernel function. */ + if (rtile.task == RenderTile::BAKE) { + cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_bake")); + } + else { + cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_path_trace")); + } + + if (have_error()) { + return; + } + + cuda_assert(cuFuncSetCacheConfig(cuRender, CU_FUNC_CACHE_PREFER_L1)); + + /* Allocate work tile. */ + work_tiles.alloc(1); + + KernelWorkTile *wtile = work_tiles.data(); + wtile->x = rtile.x; + wtile->y = rtile.y; + wtile->w = rtile.w; + wtile->h = rtile.h; + wtile->offset = rtile.offset; + wtile->stride = rtile.stride; + wtile->buffer = (float *)(CUdeviceptr)rtile.buffer; + + /* Prepare work size. More step samples render faster, but for now we + * remain conservative for GPUs connected to a display to avoid driver + * timeouts and display freezing. */ + int min_blocks, num_threads_per_block; + cuda_assert( + cuOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, cuRender, NULL, 0, 0)); + if (!info.display_device) { + min_blocks *= 8; + } + + uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h); + + /* Render all samples. */ + uint start_sample = rtile.start_sample; + uint end_sample = rtile.start_sample + rtile.num_samples; + + for (int sample = start_sample; sample < end_sample;) { + /* Setup and copy work tile to device. */ + wtile->start_sample = sample; + wtile->num_samples = step_samples; + if (task.adaptive_sampling.use) { + wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples); + } + wtile->num_samples = min(wtile->num_samples, end_sample - sample); + work_tiles.copy_to_device(); + + CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer; + uint total_work_size = wtile->w * wtile->h * wtile->num_samples; + uint num_blocks = divide_up(total_work_size, num_threads_per_block); + + /* Launch kernel. */ + void *args[] = {&d_work_tiles, &total_work_size}; + + cuda_assert( + cuLaunchKernel(cuRender, num_blocks, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0)); + + /* Run the adaptive sampling kernels at selected samples aligned to step samples. */ + uint filter_sample = sample + wtile->num_samples - 1; + if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) { + adaptive_sampling_filter(filter_sample, wtile, d_work_tiles); + } + + cuda_assert(cuCtxSynchronize()); + + /* Update progress. */ + sample += wtile->num_samples; + rtile.sample = sample; + task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); + + if (task.get_cancel()) { + if (task.need_finish_queue == false) + break; + } + } + + /* Finalize adaptive sampling. */ + if (task.adaptive_sampling.use) { + CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer; + adaptive_sampling_post(rtile, wtile, d_work_tiles); + cuda_assert(cuCtxSynchronize()); + task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); + } +} + +void CUDADevice::thread_run(DeviceTask &task) +{ + CUDAContextScope scope(this); + + if (task.type == DeviceTask::RENDER) { + device_vector work_tiles(this, "work_tiles", MEM_READ_ONLY); + + /* keep rendering tiles until done */ + RenderTile tile; + DenoisingTask denoising(this, task); + + while (task.acquire_tile(this, tile, task.tile_types)) { + if (tile.task == RenderTile::PATH_TRACE) { + render(task, tile, work_tiles); + } + else if (tile.task == RenderTile::BAKE) { + render(task, tile, work_tiles); + } + + task.release_tile(tile); + + if (task.get_cancel()) { + if (task.need_finish_queue == false) + break; + } + } + + work_tiles.free(); + } +} +# endif + +unique_ptr CUDADevice::gpu_queue_create() +{ + return make_unique(this); +} + +bool CUDADevice::should_use_graphics_interop() +{ + /* Check whether this device is part of OpenGL context. + * + * Using CUDA device for graphics interoperability which is not part of the OpenGL context is + * possible, but from the empiric measurements it can be considerably slower than using naive + * pixels copy. */ + + CUDAContextScope scope(this); + + int num_all_devices = 0; + cuda_assert(cuDeviceGetCount(&num_all_devices)); + + if (num_all_devices == 0) { + return false; + } + + vector gl_devices(num_all_devices); + uint num_gl_devices; + cuGLGetDevices(&num_gl_devices, gl_devices.data(), num_all_devices, CU_GL_DEVICE_LIST_ALL); + + for (CUdevice gl_device : gl_devices) { + if (gl_device == cuDevice) { + return true; + } + } + + return false; +} + +int CUDADevice::get_num_multiprocessors() +{ + return get_device_default_attribute(CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, 0); +} + +int CUDADevice::get_max_num_threads_per_multiprocessor() +{ + return get_device_default_attribute(CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_MULTIPROCESSOR, 0); +} + +bool CUDADevice::get_device_attribute(CUdevice_attribute attribute, int *value) +{ + CUDAContextScope scope(this); + + return cuDeviceGetAttribute(value, attribute, cuDevice) == CUDA_SUCCESS; +} + +int CUDADevice::get_device_default_attribute(CUdevice_attribute attribute, int default_value) +{ + int value = 0; + if (!get_device_attribute(attribute, &value)) { + return default_value; + } + return value; +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/cuda/device_impl.h b/intern/cycles/device/cuda/device_impl.h new file mode 100644 index 00000000000..6b27db54ab4 --- /dev/null +++ b/intern/cycles/device/cuda/device_impl.h @@ -0,0 +1,155 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/cuda/kernel.h" +# include "device/cuda/queue.h" +# include "device/cuda/util.h" +# include "device/device.h" + +# include "util/util_map.h" + +# ifdef WITH_CUDA_DYNLOAD +# include "cuew.h" +# else +# include "util/util_opengl.h" +# include +# include +# endif + +CCL_NAMESPACE_BEGIN + +class DeviceQueue; + +class CUDADevice : public Device { + + friend class CUDAContextScope; + + public: + CUdevice cuDevice; + CUcontext cuContext; + CUmodule cuModule; + size_t device_texture_headroom; + size_t device_working_headroom; + bool move_texture_to_host; + size_t map_host_used; + size_t map_host_limit; + int can_map_host; + int pitch_alignment; + int cuDevId; + int cuDevArchitecture; + bool first_error; + + struct CUDAMem { + CUDAMem() : texobject(0), array(0), use_mapped_host(false) + { + } + + CUtexObject texobject; + CUarray array; + + /* If true, a mapped host memory in shared_pointer is being used. */ + bool use_mapped_host; + }; + typedef map CUDAMemMap; + CUDAMemMap cuda_mem_map; + thread_mutex cuda_mem_map_mutex; + + /* Bindless Textures */ + device_vector texture_info; + bool need_texture_info; + + CUDADeviceKernels kernels; + + static bool have_precompiled_kernels(); + + virtual bool show_samples() const override; + + virtual BVHLayoutMask get_bvh_layout_mask() const override; + + void set_error(const string &error) override; + + CUDADevice(const DeviceInfo &info, Stats &stats, Profiler &profiler); + + virtual ~CUDADevice(); + + bool support_device(const uint /*kernel_features*/); + + bool check_peer_access(Device *peer_device) override; + + bool use_adaptive_compilation(); + + virtual string compile_kernel_get_common_cflags(const uint kernel_features); + + string compile_kernel(const uint kernel_features, + const char *name, + const char *base = "cuda", + bool force_ptx = false); + + virtual bool load_kernels(const uint kernel_features) override; + + void reserve_local_memory(const uint kernel_features); + + void init_host_memory(); + + void load_texture_info(); + + void move_textures_to_host(size_t size, bool for_texture); + + CUDAMem *generic_alloc(device_memory &mem, size_t pitch_padding = 0); + + void generic_copy_to(device_memory &mem); + + void generic_free(device_memory &mem); + + void mem_alloc(device_memory &mem) override; + + void mem_copy_to(device_memory &mem) override; + + void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override; + + void mem_zero(device_memory &mem) override; + + void mem_free(device_memory &mem) override; + + device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override; + + virtual void const_copy_to(const char *name, void *host, size_t size) override; + + void global_alloc(device_memory &mem); + + void global_free(device_memory &mem); + + void tex_alloc(device_texture &mem); + + void tex_free(device_texture &mem); + + virtual bool should_use_graphics_interop() override; + + virtual unique_ptr gpu_queue_create() override; + + int get_num_multiprocessors(); + int get_max_num_threads_per_multiprocessor(); + + protected: + bool get_device_attribute(CUdevice_attribute attribute, int *value); + int get_device_default_attribute(CUdevice_attribute attribute, int default_value); +}; + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/cuda/graphics_interop.cpp b/intern/cycles/device/cuda/graphics_interop.cpp new file mode 100644 index 00000000000..e8ca8b90eae --- /dev/null +++ b/intern/cycles/device/cuda/graphics_interop.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/cuda/graphics_interop.h" + +# include "device/cuda/device_impl.h" +# include "device/cuda/util.h" + +CCL_NAMESPACE_BEGIN + +CUDADeviceGraphicsInterop::CUDADeviceGraphicsInterop(CUDADeviceQueue *queue) + : queue_(queue), device_(static_cast(queue->device)) +{ +} + +CUDADeviceGraphicsInterop::~CUDADeviceGraphicsInterop() +{ + CUDAContextScope scope(device_); + + if (cu_graphics_resource_) { + cuda_device_assert(device_, cuGraphicsUnregisterResource(cu_graphics_resource_)); + } +} + +void CUDADeviceGraphicsInterop::set_destination( + const DeviceGraphicsInteropDestination &destination) +{ + const int64_t new_buffer_area = int64_t(destination.buffer_width) * destination.buffer_height; + + need_clear_ = destination.need_clear; + + if (opengl_pbo_id_ == destination.opengl_pbo_id && buffer_area_ == new_buffer_area) { + return; + } + + CUDAContextScope scope(device_); + + if (cu_graphics_resource_) { + cuda_device_assert(device_, cuGraphicsUnregisterResource(cu_graphics_resource_)); + } + + const CUresult result = cuGraphicsGLRegisterBuffer( + &cu_graphics_resource_, destination.opengl_pbo_id, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE); + if (result != CUDA_SUCCESS) { + LOG(ERROR) << "Error registering OpenGL buffer: " << cuewErrorString(result); + } + + opengl_pbo_id_ = destination.opengl_pbo_id; + buffer_area_ = new_buffer_area; +} + +device_ptr CUDADeviceGraphicsInterop::map() +{ + if (!cu_graphics_resource_) { + return 0; + } + + CUDAContextScope scope(device_); + + CUdeviceptr cu_buffer; + size_t bytes; + + cuda_device_assert(device_, cuGraphicsMapResources(1, &cu_graphics_resource_, queue_->stream())); + cuda_device_assert( + device_, cuGraphicsResourceGetMappedPointer(&cu_buffer, &bytes, cu_graphics_resource_)); + + if (need_clear_) { + cuda_device_assert( + device_, cuMemsetD8Async(static_cast(cu_buffer), 0, bytes, queue_->stream())); + + need_clear_ = false; + } + + return static_cast(cu_buffer); +} + +void CUDADeviceGraphicsInterop::unmap() +{ + CUDAContextScope scope(device_); + + cuda_device_assert(device_, + cuGraphicsUnmapResources(1, &cu_graphics_resource_, queue_->stream())); +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/cuda/graphics_interop.h b/intern/cycles/device/cuda/graphics_interop.h new file mode 100644 index 00000000000..8a70c8aa71d --- /dev/null +++ b/intern/cycles/device/cuda/graphics_interop.h @@ -0,0 +1,66 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/device_graphics_interop.h" + +# ifdef WITH_CUDA_DYNLOAD +# include "cuew.h" +# else +# include +# endif + +CCL_NAMESPACE_BEGIN + +class CUDADevice; +class CUDADeviceQueue; + +class CUDADeviceGraphicsInterop : public DeviceGraphicsInterop { + public: + explicit CUDADeviceGraphicsInterop(CUDADeviceQueue *queue); + + CUDADeviceGraphicsInterop(const CUDADeviceGraphicsInterop &other) = delete; + CUDADeviceGraphicsInterop(CUDADeviceGraphicsInterop &&other) noexcept = delete; + + ~CUDADeviceGraphicsInterop(); + + CUDADeviceGraphicsInterop &operator=(const CUDADeviceGraphicsInterop &other) = delete; + CUDADeviceGraphicsInterop &operator=(CUDADeviceGraphicsInterop &&other) = delete; + + virtual void set_destination(const DeviceGraphicsInteropDestination &destination) override; + + virtual device_ptr map() override; + virtual void unmap() override; + + protected: + CUDADeviceQueue *queue_ = nullptr; + CUDADevice *device_ = nullptr; + + /* OpenGL PBO which is currently registered as the destination for the CUDA buffer. */ + uint opengl_pbo_id_ = 0; + /* Buffer area in pixels of the corresponding PBO. */ + int64_t buffer_area_ = 0; + + /* The destination was requested to be cleared. */ + bool need_clear_ = false; + + CUgraphicsResource cu_graphics_resource_ = nullptr; +}; + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/cuda/kernel.cpp b/intern/cycles/device/cuda/kernel.cpp new file mode 100644 index 00000000000..0ed20ddf8e6 --- /dev/null +++ b/intern/cycles/device/cuda/kernel.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/cuda/kernel.h" +# include "device/cuda/device_impl.h" + +CCL_NAMESPACE_BEGIN + +void CUDADeviceKernels::load(CUDADevice *device) +{ + CUmodule cuModule = device->cuModule; + + for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) { + CUDADeviceKernel &kernel = kernels_[i]; + + /* No megakernel used for GPU. */ + if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { + continue; + } + + const std::string function_name = std::string("kernel_gpu_") + + device_kernel_as_string((DeviceKernel)i); + cuda_device_assert(device, + cuModuleGetFunction(&kernel.function, cuModule, function_name.c_str())); + + if (kernel.function) { + cuda_device_assert(device, cuFuncSetCacheConfig(kernel.function, CU_FUNC_CACHE_PREFER_L1)); + + cuda_device_assert( + device, + cuOccupancyMaxPotentialBlockSize( + &kernel.min_blocks, &kernel.num_threads_per_block, kernel.function, NULL, 0, 0)); + } + else { + LOG(ERROR) << "Unable to load kernel " << function_name; + } + } + + loaded = true; +} + +const CUDADeviceKernel &CUDADeviceKernels::get(DeviceKernel kernel) const +{ + return kernels_[(int)kernel]; +} + +bool CUDADeviceKernels::available(DeviceKernel kernel) const +{ + return kernels_[(int)kernel].function != nullptr; +} + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA*/ diff --git a/intern/cycles/device/cuda/kernel.h b/intern/cycles/device/cuda/kernel.h new file mode 100644 index 00000000000..b489547a350 --- /dev/null +++ b/intern/cycles/device/cuda/kernel.h @@ -0,0 +1,56 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_CUDA + +# include "device/device_kernel.h" + +# ifdef WITH_CUDA_DYNLOAD +# include "cuew.h" +# else +# include +# endif + +CCL_NAMESPACE_BEGIN + +class CUDADevice; + +/* CUDA kernel and associate occupancy information. */ +class CUDADeviceKernel { + public: + CUfunction function = nullptr; + + int num_threads_per_block = 0; + int min_blocks = 0; +}; + +/* Cache of CUDA kernels for each DeviceKernel. */ +class CUDADeviceKernels { + public: + void load(CUDADevice *device); + const CUDADeviceKernel &get(DeviceKernel kernel) const; + bool available(DeviceKernel kernel) const; + + protected: + CUDADeviceKernel kernels_[DEVICE_KERNEL_NUM]; + bool loaded = false; +}; + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA */ diff --git a/intern/cycles/device/cuda/queue.cpp b/intern/cycles/device/cuda/queue.cpp new file mode 100644 index 00000000000..b7f86c10553 --- /dev/null +++ b/intern/cycles/device/cuda/queue.cpp @@ -0,0 +1,220 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/cuda/queue.h" + +# include "device/cuda/device_impl.h" +# include "device/cuda/graphics_interop.h" +# include "device/cuda/kernel.h" + +CCL_NAMESPACE_BEGIN + +/* CUDADeviceQueue */ + +CUDADeviceQueue::CUDADeviceQueue(CUDADevice *device) + : DeviceQueue(device), cuda_device_(device), cuda_stream_(nullptr) +{ + const CUDAContextScope scope(cuda_device_); + cuda_device_assert(cuda_device_, cuStreamCreate(&cuda_stream_, CU_STREAM_NON_BLOCKING)); +} + +CUDADeviceQueue::~CUDADeviceQueue() +{ + const CUDAContextScope scope(cuda_device_); + cuStreamDestroy(cuda_stream_); +} + +int CUDADeviceQueue::num_concurrent_states(const size_t state_size) const +{ + int num_states = max(cuda_device_->get_num_multiprocessors() * + cuda_device_->get_max_num_threads_per_multiprocessor() * 16, + 1048576); + + const char *factor_str = getenv("CYCLES_CONCURRENT_STATES_FACTOR"); + if (factor_str) { + num_states = max((int)(num_states * atof(factor_str)), 1024); + } + + VLOG(3) << "GPU queue concurrent states: " << num_states << ", using up to " + << string_human_readable_size(num_states * state_size); + + return num_states; +} + +int CUDADeviceQueue::num_concurrent_busy_states() const +{ + const int max_num_threads = cuda_device_->get_num_multiprocessors() * + cuda_device_->get_max_num_threads_per_multiprocessor(); + + if (max_num_threads == 0) { + return 65536; + } + + return 4 * max_num_threads; +} + +void CUDADeviceQueue::init_execution() +{ + /* Synchronize all textures and memory copies before executing task. */ + CUDAContextScope scope(cuda_device_); + cuda_device_->load_texture_info(); + cuda_device_assert(cuda_device_, cuCtxSynchronize()); + + debug_init_execution(); +} + +bool CUDADeviceQueue::kernel_available(DeviceKernel kernel) const +{ + return cuda_device_->kernels.available(kernel); +} + +bool CUDADeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *args[]) +{ + if (cuda_device_->have_error()) { + return false; + } + + debug_enqueue(kernel, work_size); + + const CUDAContextScope scope(cuda_device_); + const CUDADeviceKernel &cuda_kernel = cuda_device_->kernels.get(kernel); + + /* Compute kernel launch parameters. */ + const int num_threads_per_block = cuda_kernel.num_threads_per_block; + const int num_blocks = divide_up(work_size, num_threads_per_block); + + int shared_mem_bytes = 0; + + switch (kernel) { + case DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY: + /* See parall_active_index.h for why this amount of shared memory is needed. */ + shared_mem_bytes = (num_threads_per_block + 1) * sizeof(int); + break; + + default: + break; + } + + /* Launch kernel. */ + cuda_device_assert(cuda_device_, + cuLaunchKernel(cuda_kernel.function, + num_blocks, + 1, + 1, + num_threads_per_block, + 1, + 1, + shared_mem_bytes, + cuda_stream_, + args, + 0)); + + return !(cuda_device_->have_error()); +} + +bool CUDADeviceQueue::synchronize() +{ + if (cuda_device_->have_error()) { + return false; + } + + const CUDAContextScope scope(cuda_device_); + cuda_device_assert(cuda_device_, cuStreamSynchronize(cuda_stream_)); + debug_synchronize(); + + return !(cuda_device_->have_error()); +} + +void CUDADeviceQueue::zero_to_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + /* Allocate on demand. */ + if (mem.device_pointer == 0) { + cuda_device_->mem_alloc(mem); + } + + /* Zero memory on device. */ + assert(mem.device_pointer != 0); + + const CUDAContextScope scope(cuda_device_); + cuda_device_assert( + cuda_device_, + cuMemsetD8Async((CUdeviceptr)mem.device_pointer, 0, mem.memory_size(), cuda_stream_)); +} + +void CUDADeviceQueue::copy_to_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + /* Allocate on demand. */ + if (mem.device_pointer == 0) { + cuda_device_->mem_alloc(mem); + } + + assert(mem.device_pointer != 0); + assert(mem.host_pointer != nullptr); + + /* Copy memory to device. */ + const CUDAContextScope scope(cuda_device_); + cuda_device_assert( + cuda_device_, + cuMemcpyHtoDAsync( + (CUdeviceptr)mem.device_pointer, mem.host_pointer, mem.memory_size(), cuda_stream_)); +} + +void CUDADeviceQueue::copy_from_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + assert(mem.device_pointer != 0); + assert(mem.host_pointer != nullptr); + + /* Copy memory from device. */ + const CUDAContextScope scope(cuda_device_); + cuda_device_assert( + cuda_device_, + cuMemcpyDtoHAsync( + mem.host_pointer, (CUdeviceptr)mem.device_pointer, mem.memory_size(), cuda_stream_)); +} + +unique_ptr CUDADeviceQueue::graphics_interop_create() +{ + return make_unique(this); +} + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA */ diff --git a/intern/cycles/device/cuda/queue.h b/intern/cycles/device/cuda/queue.h new file mode 100644 index 00000000000..62e3aa3d6c2 --- /dev/null +++ b/intern/cycles/device/cuda/queue.h @@ -0,0 +1,67 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_CUDA + +# include "device/device_kernel.h" +# include "device/device_memory.h" +# include "device/device_queue.h" + +# include "device/cuda/util.h" + +CCL_NAMESPACE_BEGIN + +class CUDADevice; +class device_memory; + +/* Base class for CUDA queues. */ +class CUDADeviceQueue : public DeviceQueue { + public: + CUDADeviceQueue(CUDADevice *device); + ~CUDADeviceQueue(); + + virtual int num_concurrent_states(const size_t state_size) const override; + virtual int num_concurrent_busy_states() const override; + + virtual void init_execution() override; + + virtual bool kernel_available(DeviceKernel kernel) const override; + + virtual bool enqueue(DeviceKernel kernel, const int work_size, void *args[]) override; + + virtual bool synchronize() override; + + virtual void zero_to_device(device_memory &mem) override; + virtual void copy_to_device(device_memory &mem) override; + virtual void copy_from_device(device_memory &mem) override; + + virtual CUstream stream() + { + return cuda_stream_; + } + + virtual unique_ptr graphics_interop_create() override; + + protected: + CUDADevice *cuda_device_; + CUstream cuda_stream_; +}; + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA */ diff --git a/intern/cycles/device/cuda/util.cpp b/intern/cycles/device/cuda/util.cpp new file mode 100644 index 00000000000..8f657cc10fe --- /dev/null +++ b/intern/cycles/device/cuda/util.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/cuda/util.h" +# include "device/cuda/device_impl.h" + +CCL_NAMESPACE_BEGIN + +CUDAContextScope::CUDAContextScope(CUDADevice *device) : device(device) +{ + cuda_device_assert(device, cuCtxPushCurrent(device->cuContext)); +} + +CUDAContextScope::~CUDAContextScope() +{ + cuda_device_assert(device, cuCtxPopCurrent(NULL)); +} + +# ifndef WITH_CUDA_DYNLOAD +const char *cuewErrorString(CUresult result) +{ + /* We can only give error code here without major code duplication, that + * should be enough since dynamic loading is only being disabled by folks + * who knows what they're doing anyway. + * + * NOTE: Avoid call from several threads. + */ + static string error; + error = string_printf("%d", result); + return error.c_str(); +} + +const char *cuewCompilerPath() +{ + return CYCLES_CUDA_NVCC_EXECUTABLE; +} + +int cuewCompilerVersion() +{ + return (CUDA_VERSION / 100) + (CUDA_VERSION % 100 / 10); +} +# endif + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA */ diff --git a/intern/cycles/device/cuda/util.h b/intern/cycles/device/cuda/util.h new file mode 100644 index 00000000000..a0898094c08 --- /dev/null +++ b/intern/cycles/device/cuda/util.h @@ -0,0 +1,65 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_CUDA + +# ifdef WITH_CUDA_DYNLOAD +# include "cuew.h" +# else +# include +# endif + +CCL_NAMESPACE_BEGIN + +class CUDADevice; + +/* Utility to push/pop CUDA context. */ +class CUDAContextScope { + public: + CUDAContextScope(CUDADevice *device); + ~CUDAContextScope(); + + private: + CUDADevice *device; +}; + +/* Utility for checking return values of CUDA function calls. */ +# define cuda_device_assert(cuda_device, stmt) \ + { \ + CUresult result = stmt; \ + if (result != CUDA_SUCCESS) { \ + const char *name = cuewErrorString(result); \ + cuda_device->set_error( \ + string_printf("%s in %s (%s:%d)", name, #stmt, __FILE__, __LINE__)); \ + } \ + } \ + (void)0 + +# define cuda_assert(stmt) cuda_device_assert(this, stmt) + +# ifndef WITH_CUDA_DYNLOAD +/* Transparently implement some functions, so majority of the file does not need + * to worry about difference between dynamically loaded and linked CUDA at all. */ +const char *cuewErrorString(CUresult result); +const char *cuewCompilerPath(); +int cuewCompilerVersion(); +# endif /* WITH_CUDA_DYNLOAD */ + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA */ diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index ed53fbb54ae..6ccedcf54ef 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -20,7 +20,13 @@ #include "bvh/bvh2.h" #include "device/device.h" -#include "device/device_intern.h" +#include "device/device_queue.h" + +#include "device/cpu/device.h" +#include "device/cuda/device.h" +#include "device/dummy/device.h" +#include "device/multi/device.h" +#include "device/optix/device.h" #include "util/util_foreach.h" #include "util/util_half.h" @@ -38,332 +44,15 @@ CCL_NAMESPACE_BEGIN bool Device::need_types_update = true; bool Device::need_devices_update = true; thread_mutex Device::device_mutex; -vector Device::opencl_devices; vector Device::cuda_devices; vector Device::optix_devices; vector Device::cpu_devices; -vector Device::network_devices; uint Device::devices_initialized_mask = 0; -/* Device Requested Features */ - -std::ostream &operator<<(std::ostream &os, const DeviceRequestedFeatures &requested_features) -{ - os << "Experimental features: " << (requested_features.experimental ? "On" : "Off") << std::endl; - os << "Max nodes group: " << requested_features.max_nodes_group << std::endl; - /* TODO(sergey): Decode bitflag into list of names. */ - os << "Nodes features: " << requested_features.nodes_features << std::endl; - os << "Use Hair: " << string_from_bool(requested_features.use_hair) << std::endl; - os << "Use Object Motion: " << string_from_bool(requested_features.use_object_motion) - << std::endl; - os << "Use Camera Motion: " << string_from_bool(requested_features.use_camera_motion) - << std::endl; - os << "Use Baking: " << string_from_bool(requested_features.use_baking) << std::endl; - os << "Use Subsurface: " << string_from_bool(requested_features.use_subsurface) << std::endl; - os << "Use Volume: " << string_from_bool(requested_features.use_volume) << std::endl; - os << "Use Branched Integrator: " << string_from_bool(requested_features.use_integrator_branched) - << std::endl; - os << "Use Patch Evaluation: " << string_from_bool(requested_features.use_patch_evaluation) - << std::endl; - os << "Use Transparent Shadows: " << string_from_bool(requested_features.use_transparent) - << std::endl; - os << "Use Principled BSDF: " << string_from_bool(requested_features.use_principled) - << std::endl; - os << "Use Denoising: " << string_from_bool(requested_features.use_denoising) << std::endl; - os << "Use Displacement: " << string_from_bool(requested_features.use_true_displacement) - << std::endl; - os << "Use Background Light: " << string_from_bool(requested_features.use_background_light) - << std::endl; - return os; -} - /* Device */ Device::~Device() noexcept(false) { - if (!background) { - if (vertex_buffer != 0) { - glDeleteBuffers(1, &vertex_buffer); - } - if (fallback_shader_program != 0) { - glDeleteProgram(fallback_shader_program); - } - } -} - -/* TODO move shaders to standalone .glsl file. */ -const char *FALLBACK_VERTEX_SHADER = - "#version 330\n" - "uniform vec2 fullscreen;\n" - "in vec2 texCoord;\n" - "in vec2 pos;\n" - "out vec2 texCoord_interp;\n" - "\n" - "vec2 normalize_coordinates()\n" - "{\n" - " return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n" - "}\n" - "\n" - "void main()\n" - "{\n" - " gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n" - " texCoord_interp = texCoord;\n" - "}\n\0"; - -const char *FALLBACK_FRAGMENT_SHADER = - "#version 330\n" - "uniform sampler2D image_texture;\n" - "in vec2 texCoord_interp;\n" - "out vec4 fragColor;\n" - "\n" - "void main()\n" - "{\n" - " fragColor = texture(image_texture, texCoord_interp);\n" - "}\n\0"; - -static void shader_print_errors(const char *task, const char *log, const char *code) -{ - LOG(ERROR) << "Shader: " << task << " error:"; - LOG(ERROR) << "===== shader string ===="; - - stringstream stream(code); - string partial; - - int line = 1; - while (getline(stream, partial, '\n')) { - if (line < 10) { - LOG(ERROR) << " " << line << " " << partial; - } - else { - LOG(ERROR) << line << " " << partial; - } - line++; - } - LOG(ERROR) << log; -} - -static int bind_fallback_shader(void) -{ - GLint status; - GLchar log[5000]; - GLsizei length = 0; - GLuint program = 0; - - struct Shader { - const char *source; - GLenum type; - } shaders[2] = {{FALLBACK_VERTEX_SHADER, GL_VERTEX_SHADER}, - {FALLBACK_FRAGMENT_SHADER, GL_FRAGMENT_SHADER}}; - - program = glCreateProgram(); - - for (int i = 0; i < 2; i++) { - GLuint shader = glCreateShader(shaders[i].type); - - string source_str = shaders[i].source; - const char *c_str = source_str.c_str(); - - glShaderSource(shader, 1, &c_str, NULL); - glCompileShader(shader); - - glGetShaderiv(shader, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader, sizeof(log), &length, log); - shader_print_errors("compile", log, c_str); - return 0; - } - - glAttachShader(program, shader); - } - - /* Link output. */ - glBindFragDataLocation(program, 0, "fragColor"); - - /* Link and error check. */ - glLinkProgram(program); - - glGetProgramiv(program, GL_LINK_STATUS, &status); - if (!status) { - glGetShaderInfoLog(program, sizeof(log), &length, log); - shader_print_errors("linking", log, FALLBACK_VERTEX_SHADER); - shader_print_errors("linking", log, FALLBACK_FRAGMENT_SHADER); - return 0; - } - - return program; -} - -bool Device::bind_fallback_display_space_shader(const float width, const float height) -{ - if (fallback_status == FALLBACK_SHADER_STATUS_ERROR) { - return false; - } - - if (fallback_status == FALLBACK_SHADER_STATUS_NONE) { - fallback_shader_program = bind_fallback_shader(); - fallback_status = FALLBACK_SHADER_STATUS_ERROR; - - if (fallback_shader_program == 0) { - return false; - } - - glUseProgram(fallback_shader_program); - image_texture_location = glGetUniformLocation(fallback_shader_program, "image_texture"); - if (image_texture_location < 0) { - LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform."; - return false; - } - - fullscreen_location = glGetUniformLocation(fallback_shader_program, "fullscreen"); - if (fullscreen_location < 0) { - LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform."; - return false; - } - - fallback_status = FALLBACK_SHADER_STATUS_SUCCESS; - } - - /* Run this every time. */ - glUseProgram(fallback_shader_program); - glUniform1i(image_texture_location, 0); - glUniform2f(fullscreen_location, width, height); - return true; -} - -void Device::draw_pixels(device_memory &rgba, - int y, - int w, - int h, - int width, - int height, - int dx, - int dy, - int dw, - int dh, - bool transparent, - const DeviceDrawParams &draw_params) -{ - const bool use_fallback_shader = (draw_params.bind_display_space_shader_cb == NULL); - - assert(rgba.type == MEM_PIXELS); - mem_copy_from(rgba, y, w, h, rgba.memory_elements_size(1)); - - GLuint texid; - glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &texid); - glBindTexture(GL_TEXTURE_2D, texid); - - if (rgba.data_type == TYPE_HALF) { - GLhalf *data_pointer = (GLhalf *)rgba.host_pointer; - data_pointer += 4 * y * w; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, w, h, 0, GL_RGBA, GL_HALF_FLOAT, data_pointer); - } - else { - uint8_t *data_pointer = (uint8_t *)rgba.host_pointer; - data_pointer += 4 * y * w; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data_pointer); - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - if (transparent) { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } - - GLint shader_program; - if (use_fallback_shader) { - if (!bind_fallback_display_space_shader(dw, dh)) { - return; - } - shader_program = fallback_shader_program; - } - else { - draw_params.bind_display_space_shader_cb(); - glGetIntegerv(GL_CURRENT_PROGRAM, &shader_program); - } - - if (!vertex_buffer) { - glGenBuffers(1, &vertex_buffer); - } - - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); - /* invalidate old contents - avoids stalling if buffer is still waiting in queue to be rendered - */ - glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW); - - float *vpointer = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - - if (vpointer) { - /* texture coordinate - vertex pair */ - vpointer[0] = 0.0f; - vpointer[1] = 0.0f; - vpointer[2] = dx; - vpointer[3] = dy; - - vpointer[4] = 1.0f; - vpointer[5] = 0.0f; - vpointer[6] = (float)width + dx; - vpointer[7] = dy; - - vpointer[8] = 1.0f; - vpointer[9] = 1.0f; - vpointer[10] = (float)width + dx; - vpointer[11] = (float)height + dy; - - vpointer[12] = 0.0f; - vpointer[13] = 1.0f; - vpointer[14] = dx; - vpointer[15] = (float)height + dy; - - if (vertex_buffer) { - glUnmapBuffer(GL_ARRAY_BUFFER); - } - } - - GLuint vertex_array_object; - GLuint position_attribute, texcoord_attribute; - - glGenVertexArrays(1, &vertex_array_object); - glBindVertexArray(vertex_array_object); - - texcoord_attribute = glGetAttribLocation(shader_program, "texCoord"); - position_attribute = glGetAttribLocation(shader_program, "pos"); - - glEnableVertexAttribArray(texcoord_attribute); - glEnableVertexAttribArray(position_attribute); - - glVertexAttribPointer( - texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0); - glVertexAttribPointer(position_attribute, - 2, - GL_FLOAT, - GL_FALSE, - 4 * sizeof(float), - (const GLvoid *)(sizeof(float) * 2)); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - if (vertex_buffer) { - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - - if (use_fallback_shader) { - glUseProgram(0); - } - else { - draw_params.unbind_display_space_shader_cb(); - } - - glDeleteVertexArrays(1, &vertex_array_object); - glBindTexture(GL_TEXTURE_2D, 0); - glDeleteTextures(1, &texid); - - if (transparent) { - glDisable(GL_BLEND); - } } void Device::build_bvh(BVH *bvh, Progress &progress, bool refit) @@ -379,14 +68,14 @@ void Device::build_bvh(BVH *bvh, Progress &progress, bool refit) } } -Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) +Device *Device::create(const DeviceInfo &info, Stats &stats, Profiler &profiler) { #ifdef WITH_MULTI if (!info.multi_devices.empty()) { /* Always create a multi device when info contains multiple devices. * This is done so that the type can still be e.g. DEVICE_CPU to indicate * that it is a homogeneous collection of devices, which simplifies checks. */ - return device_multi_create(info, stats, profiler, background); + return device_multi_create(info, stats, profiler); } #endif @@ -394,29 +83,18 @@ Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool switch (info.type) { case DEVICE_CPU: - device = device_cpu_create(info, stats, profiler, background); + device = device_cpu_create(info, stats, profiler); break; #ifdef WITH_CUDA case DEVICE_CUDA: if (device_cuda_init()) - device = device_cuda_create(info, stats, profiler, background); + device = device_cuda_create(info, stats, profiler); break; #endif #ifdef WITH_OPTIX case DEVICE_OPTIX: if (device_optix_init()) - device = device_optix_create(info, stats, profiler, background); - break; -#endif -#ifdef WITH_NETWORK - case DEVICE_NETWORK: - device = device_network_create(info, stats, profiler, "127.0.0.1"); - break; -#endif -#ifdef WITH_OPENCL - case DEVICE_OPENCL: - if (device_opencl_init()) - device = device_opencl_create(info, stats, profiler, background); + device = device_optix_create(info, stats, profiler); break; #endif default: @@ -424,7 +102,7 @@ Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool } if (device == NULL) { - device = device_dummy_create(info, stats, profiler, background); + device = device_dummy_create(info, stats, profiler); } return device; @@ -438,10 +116,6 @@ DeviceType Device::type_from_string(const char *name) return DEVICE_CUDA; else if (strcmp(name, "OPTIX") == 0) return DEVICE_OPTIX; - else if (strcmp(name, "OPENCL") == 0) - return DEVICE_OPENCL; - else if (strcmp(name, "NETWORK") == 0) - return DEVICE_NETWORK; else if (strcmp(name, "MULTI") == 0) return DEVICE_MULTI; @@ -456,10 +130,6 @@ string Device::string_from_type(DeviceType type) return "CUDA"; else if (type == DEVICE_OPTIX) return "OPTIX"; - else if (type == DEVICE_OPENCL) - return "OPENCL"; - else if (type == DEVICE_NETWORK) - return "NETWORK"; else if (type == DEVICE_MULTI) return "MULTI"; @@ -475,12 +145,6 @@ vector Device::available_types() #endif #ifdef WITH_OPTIX types.push_back(DEVICE_OPTIX); -#endif -#ifdef WITH_OPENCL - types.push_back(DEVICE_OPENCL); -#endif -#ifdef WITH_NETWORK - types.push_back(DEVICE_NETWORK); #endif return types; } @@ -493,20 +157,6 @@ vector Device::available_devices(uint mask) thread_scoped_lock lock(device_mutex); vector devices; -#ifdef WITH_OPENCL - if (mask & DEVICE_MASK_OPENCL) { - if (!(devices_initialized_mask & DEVICE_MASK_OPENCL)) { - if (device_opencl_init()) { - device_opencl_info(opencl_devices); - } - devices_initialized_mask |= DEVICE_MASK_OPENCL; - } - foreach (DeviceInfo &info, opencl_devices) { - devices.push_back(info); - } - } -#endif - #if defined(WITH_CUDA) || defined(WITH_OPTIX) if (mask & (DEVICE_MASK_CUDA | DEVICE_MASK_OPTIX)) { if (!(devices_initialized_mask & DEVICE_MASK_CUDA)) { @@ -547,18 +197,6 @@ vector Device::available_devices(uint mask) } } -#ifdef WITH_NETWORK - if (mask & DEVICE_MASK_NETWORK) { - if (!(devices_initialized_mask & DEVICE_MASK_NETWORK)) { - device_network_info(network_devices); - devices_initialized_mask |= DEVICE_MASK_NETWORK; - } - foreach (DeviceInfo &info, network_devices) { - devices.push_back(info); - } - } -#endif - return devices; } @@ -580,15 +218,6 @@ string Device::device_capabilities(uint mask) capabilities += device_cpu_capabilities() + "\n"; } -#ifdef WITH_OPENCL - if (mask & DEVICE_MASK_OPENCL) { - if (device_opencl_init()) { - capabilities += "\nOpenCL device capabilities:\n"; - capabilities += device_opencl_capabilities(); - } - } -#endif - #ifdef WITH_CUDA if (mask & DEVICE_MASK_CUDA) { if (device_cuda_init()) { @@ -613,16 +242,13 @@ DeviceInfo Device::get_multi_device(const vector &subdevices, } DeviceInfo info; - info.type = subdevices.front().type; + info.type = DEVICE_NONE; info.id = "MULTI"; info.description = "Multi Device"; info.num = 0; info.has_half_images = true; info.has_nanovdb = true; - info.has_volume_decoupled = true; - info.has_branched_path = true; - info.has_adaptive_stop_per_sample = true; info.has_osl = true; info.has_profiling = true; info.has_peer_memory = false; @@ -660,16 +286,16 @@ DeviceInfo Device::get_multi_device(const vector &subdevices, info.id += device.id; /* Set device type to MULTI if subdevices are not of a common type. */ - if (device.type != info.type) { + if (info.type == DEVICE_NONE) { + info.type = device.type; + } + else if (device.type != info.type) { info.type = DEVICE_MULTI; } /* Accumulate device info. */ info.has_half_images &= device.has_half_images; info.has_nanovdb &= device.has_nanovdb; - info.has_volume_decoupled &= device.has_volume_decoupled; - info.has_branched_path &= device.has_branched_path; - info.has_adaptive_stop_per_sample &= device.has_adaptive_stop_per_sample; info.has_osl &= device.has_osl; info.has_profiling &= device.has_profiling; info.has_peer_memory |= device.has_peer_memory; @@ -689,60 +315,32 @@ void Device::free_memory() devices_initialized_mask = 0; cuda_devices.free_memory(); optix_devices.free_memory(); - opencl_devices.free_memory(); cpu_devices.free_memory(); - network_devices.free_memory(); +} + +unique_ptr Device::gpu_queue_create() +{ + LOG(FATAL) << "Device does not support queues."; + return nullptr; +} + +const CPUKernels *Device::get_cpu_kernels() const +{ + LOG(FATAL) << "Device does not support CPU kernels."; + return nullptr; +} + +void Device::get_cpu_kernel_thread_globals( + vector & /*kernel_thread_globals*/) +{ + LOG(FATAL) << "Device does not support CPU kernels."; +} + +void *Device::get_cpu_osl_memory() +{ + return nullptr; } /* DeviceInfo */ -void DeviceInfo::add_denoising_devices(DenoiserType denoiser_type) -{ - assert(denoising_devices.empty()); - - if (denoiser_type == DENOISER_OPTIX && type != DEVICE_OPTIX) { - vector optix_devices = Device::available_devices(DEVICE_MASK_OPTIX); - if (!optix_devices.empty()) { - /* Convert to a special multi device with separate denoising devices. */ - if (multi_devices.empty()) { - multi_devices.push_back(*this); - } - - /* Try to use the same physical devices for denoising. */ - for (const DeviceInfo &cuda_device : multi_devices) { - if (cuda_device.type == DEVICE_CUDA) { - for (const DeviceInfo &optix_device : optix_devices) { - if (cuda_device.num == optix_device.num) { - id += optix_device.id; - denoising_devices.push_back(optix_device); - break; - } - } - } - } - - if (denoising_devices.empty()) { - /* Simply use the first available OptiX device. */ - const DeviceInfo optix_device = optix_devices.front(); - id += optix_device.id; /* Uniquely identify this special multi device. */ - denoising_devices.push_back(optix_device); - } - - denoisers = denoiser_type; - } - } - else if (denoiser_type == DENOISER_OPENIMAGEDENOISE && type != DEVICE_CPU) { - /* Convert to a special multi device with separate denoising devices. */ - if (multi_devices.empty()) { - multi_devices.push_back(*this); - } - - /* Add CPU denoising devices. */ - DeviceInfo cpu_device = Device::available_devices(DEVICE_MASK_CPU).front(); - denoising_devices.push_back(cpu_device); - - denoisers = denoiser_type; - } -} - CCL_NAMESPACE_END diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index ecf79bcdfa6..02b6edb56d0 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -21,31 +21,34 @@ #include "bvh/bvh_params.h" +#include "device/device_denoise.h" #include "device/device_memory.h" -#include "device/device_task.h" +#include "util/util_function.h" #include "util/util_list.h" +#include "util/util_logging.h" #include "util/util_stats.h" #include "util/util_string.h" #include "util/util_texture.h" #include "util/util_thread.h" #include "util/util_types.h" +#include "util/util_unique_ptr.h" #include "util/util_vector.h" CCL_NAMESPACE_BEGIN class BVH; +class DeviceQueue; class Progress; -class RenderTile; +class CPUKernels; +class CPUKernelThreadGlobals; /* Device Types */ enum DeviceType { DEVICE_NONE = 0, DEVICE_CPU, - DEVICE_OPENCL, DEVICE_CUDA, - DEVICE_NETWORK, DEVICE_MULTI, DEVICE_OPTIX, DEVICE_DUMMY, @@ -53,20 +56,11 @@ enum DeviceType { enum DeviceTypeMask { DEVICE_MASK_CPU = (1 << DEVICE_CPU), - DEVICE_MASK_OPENCL = (1 << DEVICE_OPENCL), DEVICE_MASK_CUDA = (1 << DEVICE_CUDA), DEVICE_MASK_OPTIX = (1 << DEVICE_OPTIX), - DEVICE_MASK_NETWORK = (1 << DEVICE_NETWORK), DEVICE_MASK_ALL = ~0 }; -enum DeviceKernelStatus { - DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE, - DEVICE_KERNEL_USING_FEATURE_KERNEL, - DEVICE_KERNEL_FEATURE_KERNEL_INVALID, - DEVICE_KERNEL_UNKNOWN, -}; - #define DEVICE_MASK(type) (DeviceTypeMask)(1 << type) class DeviceInfo { @@ -75,20 +69,16 @@ class DeviceInfo { string description; string id; /* used for user preferences, should stay fixed with changing hardware config */ int num; - bool display_device; /* GPU is used as a display device. */ - bool has_half_images; /* Support half-float textures. */ - bool has_nanovdb; /* Support NanoVDB volumes. */ - bool has_volume_decoupled; /* Decoupled volume shading. */ - bool has_branched_path; /* Supports branched path tracing. */ - bool has_adaptive_stop_per_sample; /* Per-sample adaptive sampling stopping. */ - bool has_osl; /* Support Open Shading Language. */ - bool use_split_kernel; /* Use split or mega kernel. */ - bool has_profiling; /* Supports runtime collection of profiling info. */ - bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */ - DenoiserTypeMask denoisers; /* Supported denoiser types. */ + bool display_device; /* GPU is used as a display device. */ + bool has_nanovdb; /* Support NanoVDB volumes. */ + bool has_half_images; /* Support half-float textures. */ + bool has_osl; /* Support Open Shading Language. */ + bool has_profiling; /* Supports runtime collection of profiling info. */ + bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */ + bool has_gpu_queue; /* Device supports GPU queue. */ + DenoiserTypeMask denoisers; /* Supported denoiser types. */ int cpu_threads; vector multi_devices; - vector denoising_devices; string error_msg; DeviceInfo() @@ -100,227 +90,35 @@ class DeviceInfo { display_device = false; has_half_images = false; has_nanovdb = false; - has_volume_decoupled = false; - has_branched_path = true; - has_adaptive_stop_per_sample = false; has_osl = false; - use_split_kernel = false; has_profiling = false; has_peer_memory = false; + has_gpu_queue = false; denoisers = DENOISER_NONE; } - bool operator==(const DeviceInfo &info) + bool operator==(const DeviceInfo &info) const { /* Multiple Devices with the same ID would be very bad. */ assert(id != info.id || (type == info.type && num == info.num && description == info.description)); return id == info.id; } - - /* Add additional devices needed for the specified denoiser. */ - void add_denoising_devices(DenoiserType denoiser_type); }; -class DeviceRequestedFeatures { - public: - /* Use experimental feature set. */ - bool experimental; - - /* Selective nodes compilation. */ - - /* Identifier of a node group up to which all the nodes needs to be - * compiled in. Nodes from higher group indices will be ignores. - */ - int max_nodes_group; - - /* Features bitfield indicating which features from the requested group - * will be compiled in. Nodes which corresponds to features which are not - * in this bitfield will be ignored even if they're in the requested group. - */ - int nodes_features; - - /* BVH/sampling kernel features. */ - bool use_hair; - bool use_hair_thick; - bool use_object_motion; - bool use_camera_motion; - - /* Denotes whether baking functionality is needed. */ - bool use_baking; - - /* Use subsurface scattering materials. */ - bool use_subsurface; - - /* Use volume materials. */ - bool use_volume; - - /* Use branched integrator. */ - bool use_integrator_branched; - - /* Use OpenSubdiv patch evaluation */ - bool use_patch_evaluation; - - /* Use Transparent shadows */ - bool use_transparent; - - /* Use various shadow tricks, such as shadow catcher. */ - bool use_shadow_tricks; - - /* Per-uber shader usage flags. */ - bool use_principled; - - /* Denoising features. */ - bool use_denoising; - - /* Use raytracing in shaders. */ - bool use_shader_raytrace; - - /* Use true displacement */ - bool use_true_displacement; - - /* Use background lights */ - bool use_background_light; - - DeviceRequestedFeatures() - { - /* TODO(sergey): Find more meaningful defaults. */ - max_nodes_group = 0; - nodes_features = 0; - use_hair = false; - use_hair_thick = false; - use_object_motion = false; - use_camera_motion = false; - use_baking = false; - use_subsurface = false; - use_volume = false; - use_integrator_branched = false; - use_patch_evaluation = false; - use_transparent = false; - use_shadow_tricks = false; - use_principled = false; - use_denoising = false; - use_shader_raytrace = false; - use_true_displacement = false; - use_background_light = false; - } - - bool modified(const DeviceRequestedFeatures &requested_features) - { - return !(max_nodes_group == requested_features.max_nodes_group && - nodes_features == requested_features.nodes_features && - use_hair == requested_features.use_hair && - use_hair_thick == requested_features.use_hair_thick && - use_object_motion == requested_features.use_object_motion && - use_camera_motion == requested_features.use_camera_motion && - use_baking == requested_features.use_baking && - use_subsurface == requested_features.use_subsurface && - use_volume == requested_features.use_volume && - use_integrator_branched == requested_features.use_integrator_branched && - use_patch_evaluation == requested_features.use_patch_evaluation && - use_transparent == requested_features.use_transparent && - use_shadow_tricks == requested_features.use_shadow_tricks && - use_principled == requested_features.use_principled && - use_denoising == requested_features.use_denoising && - use_shader_raytrace == requested_features.use_shader_raytrace && - use_true_displacement == requested_features.use_true_displacement && - use_background_light == requested_features.use_background_light); - } - - /* Convert the requested features structure to a build options, - * which could then be passed to compilers. - */ - string get_build_options() const - { - string build_options = ""; - if (experimental) { - build_options += "-D__KERNEL_EXPERIMENTAL__ "; - } - build_options += "-D__NODES_MAX_GROUP__=" + string_printf("%d", max_nodes_group); - build_options += " -D__NODES_FEATURES__=" + string_printf("%d", nodes_features); - if (!use_hair) { - build_options += " -D__NO_HAIR__"; - } - if (!use_object_motion) { - build_options += " -D__NO_OBJECT_MOTION__"; - } - if (!use_camera_motion) { - build_options += " -D__NO_CAMERA_MOTION__"; - } - if (!use_baking) { - build_options += " -D__NO_BAKING__"; - } - if (!use_volume) { - build_options += " -D__NO_VOLUME__"; - } - if (!use_subsurface) { - build_options += " -D__NO_SUBSURFACE__"; - } - if (!use_integrator_branched) { - build_options += " -D__NO_BRANCHED_PATH__"; - } - if (!use_patch_evaluation) { - build_options += " -D__NO_PATCH_EVAL__"; - } - if (!use_transparent && !use_volume) { - build_options += " -D__NO_TRANSPARENT__"; - } - if (!use_shadow_tricks) { - build_options += " -D__NO_SHADOW_TRICKS__"; - } - if (!use_principled) { - build_options += " -D__NO_PRINCIPLED__"; - } - if (!use_denoising) { - build_options += " -D__NO_DENOISING__"; - } - if (!use_shader_raytrace) { - build_options += " -D__NO_SHADER_RAYTRACE__"; - } - return build_options; - } -}; - -std::ostream &operator<<(std::ostream &os, const DeviceRequestedFeatures &requested_features); - /* Device */ -struct DeviceDrawParams { - function bind_display_space_shader_cb; - function unbind_display_space_shader_cb; -}; - class Device { friend class device_sub_ptr; protected: - enum { - FALLBACK_SHADER_STATUS_NONE = 0, - FALLBACK_SHADER_STATUS_ERROR, - FALLBACK_SHADER_STATUS_SUCCESS, - }; - - Device(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background) - : background(background), - vertex_buffer(0), - fallback_status(FALLBACK_SHADER_STATUS_NONE), - fallback_shader_program(0), - info(info_), - stats(stats_), - profiler(profiler_) + Device(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_) + : info(info_), stats(stats_), profiler(profiler_) { } - bool background; string error_msg; - /* used for real time display */ - unsigned int vertex_buffer; - int fallback_status, fallback_shader_program; - int image_texture_location, fullscreen_location; - - bool bind_fallback_display_space_shader(const float width, const float height); - virtual device_ptr mem_alloc_sub_ptr(device_memory & /*mem*/, int /*offset*/, int /*size*/) { /* Only required for devices that implement denoising. */ @@ -361,67 +159,31 @@ class Device { Stats &stats; Profiler &profiler; - /* memory alignment */ - virtual int mem_sub_ptr_alignment() - { - return MIN_ALIGNMENT_CPU_DATA_TYPES; - } - /* constant memory */ virtual void const_copy_to(const char *name, void *host, size_t size) = 0; - /* open shading language, only for CPU device */ - virtual void *osl_memory() - { - return NULL; - } - /* load/compile kernels, must be called before adding tasks */ - virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/) + virtual bool load_kernels(uint /*kernel_features*/) { return true; } - /* Wait for device to become available to upload data and receive tasks - * This method is used by the OpenCL device to load the - * optimized kernels or when not (yet) available load the - * generic kernels (only during foreground rendering) */ - virtual bool wait_for_availability(const DeviceRequestedFeatures & /*requested_features*/) - { - return true; - } - /* Check if there are 'better' kernels available to be used - * We can switch over to these kernels - * This method is used to determine if we can switch the preview kernels - * to regular kernels */ - virtual DeviceKernelStatus get_active_kernel_switch_state() - { - return DEVICE_KERNEL_USING_FEATURE_KERNEL; - } + /* GPU device only functions. + * These may not be used on CPU or multi-devices. */ - /* tasks */ - virtual int get_split_task_count(DeviceTask &) - { - return 1; - } + /* Create new queue for executing kernels in. */ + virtual unique_ptr gpu_queue_create(); - virtual void task_add(DeviceTask &task) = 0; - virtual void task_wait() = 0; - virtual void task_cancel() = 0; + /* CPU device only functions. + * These may not be used on GPU or multi-devices. */ - /* opengl drawing */ - virtual void draw_pixels(device_memory &mem, - int y, - int w, - int h, - int width, - int height, - int dx, - int dy, - int dw, - int dh, - bool transparent, - const DeviceDrawParams &draw_params); + /* Get CPU kernel functions for native instruction set. */ + virtual const CPUKernels *get_cpu_kernels() const; + /* Get kernel globals to pass to kernels. */ + virtual void get_cpu_kernel_thread_globals( + vector & /*kernel_thread_globals*/); + /* Get OpenShadingLanguage memory buffer. */ + virtual void *get_cpu_osl_memory(); /* acceleration structure building */ virtual void build_bvh(BVH *bvh, Progress &progress, bool refit); @@ -429,25 +191,11 @@ class Device { /* OptiX specific destructor. */ virtual void release_optix_bvh(BVH * /*bvh*/){}; -#ifdef WITH_NETWORK - /* networking */ - void server_run(); -#endif - /* multi device */ - virtual void map_tile(Device * /*sub_device*/, RenderTile & /*tile*/) - { - } virtual int device_number(Device * /*sub_device*/) { return 0; } - virtual void map_neighbor_tiles(Device * /*sub_device*/, RenderTileNeighbors & /*neighbors*/) - { - } - virtual void unmap_neighbor_tiles(Device * /*sub_device*/, RenderTileNeighbors & /*neighbors*/) - { - } virtual bool is_resident(device_ptr /*key*/, Device *sub_device) { @@ -460,11 +208,47 @@ class Device { return false; } + /* Graphics resources interoperability. + * + * The interoperability comes here by the meaning that the device is capable of computing result + * directly into an OpenGL (or other graphics library) buffer. */ + + /* Check display si to be updated using graphics interoperability. + * The interoperability can not be used is it is not supported by the device. But the device + * might also force disable the interoperability if it detects that it will be slower than + * copying pixels from the render buffer. */ + virtual bool should_use_graphics_interop() + { + return false; + } + + /* Buffer denoising. */ + + /* Returns true if task is fully handled. */ + virtual bool denoise_buffer(const DeviceDenoiseTask & /*task*/) + { + LOG(ERROR) << "Request buffer denoising from a device which does not support it."; + return false; + } + + virtual DeviceQueue *get_denoise_queue() + { + LOG(ERROR) << "Request denoising queue from a device which does not support it."; + return nullptr; + } + + /* Sub-devices */ + + /* Run given callback for every individual device which will be handling rendering. + * For the single device the callback is called for the device itself. For the multi-device the + * callback is only called for the sub-devices. */ + virtual void foreach_device(const function &callback) + { + callback(this); + } + /* static */ - static Device *create(DeviceInfo &info, - Stats &stats, - Profiler &profiler, - bool background = true); + static Device *create(const DeviceInfo &info, Stats &stats, Profiler &profiler); static DeviceType type_from_string(const char *name); static string string_from_type(DeviceType type); @@ -499,9 +283,7 @@ class Device { static thread_mutex device_mutex; static vector cuda_devices; static vector optix_devices; - static vector opencl_devices; static vector cpu_devices; - static vector network_devices; static uint devices_initialized_mask; }; diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp deleted file mode 100644 index 4a6e77d6eaa..00000000000 --- a/intern/cycles/device/device_cpu.cpp +++ /dev/null @@ -1,1680 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -/* So ImathMath is included before our kernel_cpu_compat. */ -#ifdef WITH_OSL -/* So no context pollution happens from indirectly included windows.h */ -# include "util/util_windows.h" -# include -#endif - -#ifdef WITH_EMBREE -# include -#endif - -#include "device/device.h" -#include "device/device_denoising.h" -#include "device/device_intern.h" -#include "device/device_split_kernel.h" - -// clang-format off -#include "kernel/kernel.h" -#include "kernel/kernel_compat_cpu.h" -#include "kernel/kernel_types.h" -#include "kernel/split/kernel_split_data.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_adaptive_sampling.h" - -#include "kernel/filter/filter.h" - -#include "kernel/osl/osl_shader.h" -#include "kernel/osl/osl_globals.h" -// clang-format on - -#include "bvh/bvh_embree.h" - -#include "render/buffers.h" -#include "render/coverage.h" - -#include "util/util_debug.h" -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_opengl.h" -#include "util/util_openimagedenoise.h" -#include "util/util_optimization.h" -#include "util/util_progress.h" -#include "util/util_system.h" -#include "util/util_task.h" -#include "util/util_thread.h" - -CCL_NAMESPACE_BEGIN - -class CPUDevice; - -/* Has to be outside of the class to be shared across template instantiations. */ -static const char *logged_architecture = ""; - -template class KernelFunctions { - public: - KernelFunctions() - { - kernel = (F)NULL; - } - - KernelFunctions( - F kernel_default, F kernel_sse2, F kernel_sse3, F kernel_sse41, F kernel_avx, F kernel_avx2) - { - const char *architecture_name = "default"; - kernel = kernel_default; - - /* Silence potential warnings about unused variables - * when compiling without some architectures. */ - (void)kernel_sse2; - (void)kernel_sse3; - (void)kernel_sse41; - (void)kernel_avx; - (void)kernel_avx2; -#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 - if (DebugFlags().cpu.has_avx2() && system_cpu_support_avx2()) { - architecture_name = "AVX2"; - kernel = kernel_avx2; - } - else -#endif -#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX - if (DebugFlags().cpu.has_avx() && system_cpu_support_avx()) { - architecture_name = "AVX"; - kernel = kernel_avx; - } - else -#endif -#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 - if (DebugFlags().cpu.has_sse41() && system_cpu_support_sse41()) { - architecture_name = "SSE4.1"; - kernel = kernel_sse41; - } - else -#endif -#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 - if (DebugFlags().cpu.has_sse3() && system_cpu_support_sse3()) { - architecture_name = "SSE3"; - kernel = kernel_sse3; - } - else -#endif -#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 - if (DebugFlags().cpu.has_sse2() && system_cpu_support_sse2()) { - architecture_name = "SSE2"; - kernel = kernel_sse2; - } -#else - { - /* Dummy to prevent the architecture if below become - * conditional when WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 - * is not defined. */ - } -#endif - - if (strcmp(architecture_name, logged_architecture) != 0) { - VLOG(1) << "Will be using " << architecture_name << " kernels."; - logged_architecture = architecture_name; - } - } - - inline F operator()() const - { - assert(kernel); - return kernel; - } - - protected: - F kernel; -}; - -class CPUSplitKernel : public DeviceSplitKernel { - CPUDevice *device; - - public: - explicit CPUSplitKernel(CPUDevice *device); - - virtual bool enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory &kernel_globals, - device_memory &kernel_data_, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flag, - device_memory &work_pool_wgs); - - virtual SplitKernelFunction *get_split_kernel_function(const string &kernel_name, - const DeviceRequestedFeatures &); - virtual int2 split_kernel_local_size(); - virtual int2 split_kernel_global_size(device_memory &kg, device_memory &data, DeviceTask &task); - virtual uint64_t state_buffer_size(device_memory &kg, device_memory &data, size_t num_threads); -}; - -class CPUDevice : public Device { - public: - TaskPool task_pool; - KernelGlobals kernel_globals; - - device_vector texture_info; - bool need_texture_info; - -#ifdef WITH_OSL - OSLGlobals osl_globals; -#endif -#ifdef WITH_OPENIMAGEDENOISE - oidn::DeviceRef oidn_device; - oidn::FilterRef oidn_filter; -#endif - thread_spin_lock oidn_task_lock; -#ifdef WITH_EMBREE - RTCScene embree_scene = NULL; - RTCDevice embree_device; -#endif - - bool use_split_kernel; - - DeviceRequestedFeatures requested_features; - - KernelFunctions path_trace_kernel; - KernelFunctions - convert_to_half_float_kernel; - KernelFunctions - convert_to_byte_kernel; - KernelFunctions - shader_kernel; - KernelFunctions bake_kernel; - - KernelFunctions - filter_divide_shadow_kernel; - KernelFunctions - filter_get_feature_kernel; - KernelFunctions - filter_write_feature_kernel; - KernelFunctions - filter_detect_outliers_kernel; - KernelFunctions - filter_combine_halves_kernel; - - KernelFunctions - filter_nlm_calc_difference_kernel; - KernelFunctions filter_nlm_blur_kernel; - KernelFunctions filter_nlm_calc_weight_kernel; - KernelFunctions - filter_nlm_update_output_kernel; - KernelFunctions filter_nlm_normalize_kernel; - - KernelFunctions - filter_construct_transform_kernel; - KernelFunctions - filter_nlm_construct_gramian_kernel; - KernelFunctions - filter_finalize_kernel; - - KernelFunctions - data_init_kernel; - unordered_map> split_kernels; - -#define KERNEL_FUNCTIONS(name) \ - KERNEL_NAME_EVAL(cpu, name), KERNEL_NAME_EVAL(cpu_sse2, name), \ - KERNEL_NAME_EVAL(cpu_sse3, name), KERNEL_NAME_EVAL(cpu_sse41, name), \ - KERNEL_NAME_EVAL(cpu_avx, name), KERNEL_NAME_EVAL(cpu_avx2, name) - - CPUDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_) - : Device(info_, stats_, profiler_, background_), - texture_info(this, "__texture_info", MEM_GLOBAL), -#define REGISTER_KERNEL(name) name##_kernel(KERNEL_FUNCTIONS(name)) - REGISTER_KERNEL(path_trace), - REGISTER_KERNEL(convert_to_half_float), - REGISTER_KERNEL(convert_to_byte), - REGISTER_KERNEL(shader), - REGISTER_KERNEL(bake), - REGISTER_KERNEL(filter_divide_shadow), - REGISTER_KERNEL(filter_get_feature), - REGISTER_KERNEL(filter_write_feature), - REGISTER_KERNEL(filter_detect_outliers), - REGISTER_KERNEL(filter_combine_halves), - REGISTER_KERNEL(filter_nlm_calc_difference), - REGISTER_KERNEL(filter_nlm_blur), - REGISTER_KERNEL(filter_nlm_calc_weight), - REGISTER_KERNEL(filter_nlm_update_output), - REGISTER_KERNEL(filter_nlm_normalize), - REGISTER_KERNEL(filter_construct_transform), - REGISTER_KERNEL(filter_nlm_construct_gramian), - REGISTER_KERNEL(filter_finalize), - REGISTER_KERNEL(data_init) -#undef REGISTER_KERNEL - { - if (info.cpu_threads == 0) { - info.cpu_threads = TaskScheduler::num_threads(); - } - -#ifdef WITH_OSL - kernel_globals.osl = &osl_globals; -#endif -#ifdef WITH_EMBREE - embree_device = rtcNewDevice("verbose=0"); -#endif - use_split_kernel = DebugFlags().cpu.split_kernel; - if (use_split_kernel) { - VLOG(1) << "Will be using split kernel."; - } - need_texture_info = false; - -#define REGISTER_SPLIT_KERNEL(name) \ - split_kernels[#name] = KernelFunctions( \ - KERNEL_FUNCTIONS(name)) - REGISTER_SPLIT_KERNEL(path_init); - REGISTER_SPLIT_KERNEL(scene_intersect); - REGISTER_SPLIT_KERNEL(lamp_emission); - REGISTER_SPLIT_KERNEL(do_volume); - REGISTER_SPLIT_KERNEL(queue_enqueue); - REGISTER_SPLIT_KERNEL(indirect_background); - REGISTER_SPLIT_KERNEL(shader_setup); - REGISTER_SPLIT_KERNEL(shader_sort); - REGISTER_SPLIT_KERNEL(shader_eval); - REGISTER_SPLIT_KERNEL(holdout_emission_blurring_pathtermination_ao); - REGISTER_SPLIT_KERNEL(subsurface_scatter); - REGISTER_SPLIT_KERNEL(direct_lighting); - REGISTER_SPLIT_KERNEL(shadow_blocked_ao); - REGISTER_SPLIT_KERNEL(shadow_blocked_dl); - REGISTER_SPLIT_KERNEL(enqueue_inactive); - REGISTER_SPLIT_KERNEL(next_iteration_setup); - REGISTER_SPLIT_KERNEL(indirect_subsurface); - REGISTER_SPLIT_KERNEL(buffer_update); - REGISTER_SPLIT_KERNEL(adaptive_stopping); - REGISTER_SPLIT_KERNEL(adaptive_filter_x); - REGISTER_SPLIT_KERNEL(adaptive_filter_y); - REGISTER_SPLIT_KERNEL(adaptive_adjust_samples); -#undef REGISTER_SPLIT_KERNEL -#undef KERNEL_FUNCTIONS - } - - ~CPUDevice() - { -#ifdef WITH_EMBREE - rtcReleaseDevice(embree_device); -#endif - task_pool.cancel(); - texture_info.free(); - } - - virtual bool show_samples() const override - { - return (info.cpu_threads == 1); - } - - virtual BVHLayoutMask get_bvh_layout_mask() const override - { - BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_BVH2; -#ifdef WITH_EMBREE - bvh_layout_mask |= BVH_LAYOUT_EMBREE; -#endif /* WITH_EMBREE */ - return bvh_layout_mask; - } - - void load_texture_info() - { - if (need_texture_info) { - texture_info.copy_to_device(); - need_texture_info = false; - } - } - - virtual void mem_alloc(device_memory &mem) override - { - if (mem.type == MEM_TEXTURE) { - assert(!"mem_alloc not supported for textures."); - } - else if (mem.type == MEM_GLOBAL) { - assert(!"mem_alloc not supported for global memory."); - } - else { - if (mem.name) { - VLOG(1) << "Buffer allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - } - - if (mem.type == MEM_DEVICE_ONLY || !mem.host_pointer) { - size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES; - void *data = util_aligned_malloc(mem.memory_size(), alignment); - mem.device_pointer = (device_ptr)data; - } - else { - mem.device_pointer = (device_ptr)mem.host_pointer; - } - - mem.device_size = mem.memory_size(); - stats.mem_alloc(mem.device_size); - } - } - - virtual void mem_copy_to(device_memory &mem) override - { - if (mem.type == MEM_GLOBAL) { - global_free(mem); - global_alloc(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - tex_alloc((device_texture &)mem); - } - else if (mem.type == MEM_PIXELS) { - assert(!"mem_copy_to not supported for pixels."); - } - else { - if (!mem.device_pointer) { - mem_alloc(mem); - } - - /* copy is no-op */ - } - } - - virtual void mem_copy_from( - device_memory & /*mem*/, int /*y*/, int /*w*/, int /*h*/, int /*elem*/) override - { - /* no-op */ - } - - virtual void mem_zero(device_memory &mem) override - { - if (!mem.device_pointer) { - mem_alloc(mem); - } - - if (mem.device_pointer) { - memset((void *)mem.device_pointer, 0, mem.memory_size()); - } - } - - virtual void mem_free(device_memory &mem) override - { - if (mem.type == MEM_GLOBAL) { - global_free(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - } - else if (mem.device_pointer) { - if (mem.type == MEM_DEVICE_ONLY || !mem.host_pointer) { - util_aligned_free((void *)mem.device_pointer); - } - mem.device_pointer = 0; - stats.mem_free(mem.device_size); - mem.device_size = 0; - } - } - - virtual device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override - { - return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); - } - - virtual void const_copy_to(const char *name, void *host, size_t size) override - { -#if WITH_EMBREE - if (strcmp(name, "__data") == 0) { - assert(size <= sizeof(KernelData)); - - // Update scene handle (since it is different for each device on multi devices) - KernelData *const data = (KernelData *)host; - data->bvh.scene = embree_scene; - } -#endif - kernel_const_copy(&kernel_globals, name, host, size); - } - - void global_alloc(device_memory &mem) - { - VLOG(1) << "Global memory allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - - kernel_global_memory_copy(&kernel_globals, mem.name, mem.host_pointer, mem.data_size); - - mem.device_pointer = (device_ptr)mem.host_pointer; - mem.device_size = mem.memory_size(); - stats.mem_alloc(mem.device_size); - } - - void global_free(device_memory &mem) - { - if (mem.device_pointer) { - mem.device_pointer = 0; - stats.mem_free(mem.device_size); - mem.device_size = 0; - } - } - - void tex_alloc(device_texture &mem) - { - VLOG(1) << "Texture allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - - mem.device_pointer = (device_ptr)mem.host_pointer; - mem.device_size = mem.memory_size(); - stats.mem_alloc(mem.device_size); - - const uint slot = mem.slot; - if (slot >= texture_info.size()) { - /* Allocate some slots in advance, to reduce amount of re-allocations. */ - texture_info.resize(slot + 128); - } - - texture_info[slot] = mem.info; - texture_info[slot].data = (uint64_t)mem.host_pointer; - need_texture_info = true; - } - - void tex_free(device_texture &mem) - { - if (mem.device_pointer) { - mem.device_pointer = 0; - stats.mem_free(mem.device_size); - mem.device_size = 0; - need_texture_info = true; - } - } - - virtual void *osl_memory() override - { -#ifdef WITH_OSL - return &osl_globals; -#else - return NULL; -#endif - } - - void build_bvh(BVH *bvh, Progress &progress, bool refit) override - { -#ifdef WITH_EMBREE - if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE || - bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) { - BVHEmbree *const bvh_embree = static_cast(bvh); - if (refit) { - bvh_embree->refit(progress); - } - else { - bvh_embree->build(progress, &stats, embree_device); - } - - if (bvh->params.top_level) { - embree_scene = bvh_embree->scene; - } - } - else -#endif - Device::build_bvh(bvh, progress, refit); - } - - void thread_run(DeviceTask &task) - { - if (task.type == DeviceTask::RENDER) - thread_render(task); - else if (task.type == DeviceTask::SHADER) - thread_shader(task); - else if (task.type == DeviceTask::FILM_CONVERT) - thread_film_convert(task); - else if (task.type == DeviceTask::DENOISE_BUFFER) - thread_denoise(task); - } - - bool denoising_non_local_means(device_ptr image_ptr, - device_ptr guide_ptr, - device_ptr variance_ptr, - device_ptr out_ptr, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_NON_LOCAL_MEANS); - - int4 rect = task->rect; - int r = task->nlm_state.r; - int f = task->nlm_state.f; - float a = task->nlm_state.a; - float k_2 = task->nlm_state.k_2; - - int w = align_up(rect.z - rect.x, 4); - int h = rect.w - rect.y; - int stride = task->buffer.stride; - int channel_offset = task->nlm_state.is_color ? task->buffer.pass_stride : 0; - - float *temporary_mem = (float *)task->buffer.temporary_mem.device_pointer; - float *blurDifference = temporary_mem; - float *difference = temporary_mem + task->buffer.pass_stride; - float *weightAccum = temporary_mem + 2 * task->buffer.pass_stride; - - memset(weightAccum, 0, sizeof(float) * w * h); - memset((float *)out_ptr, 0, sizeof(float) * w * h); - - for (int i = 0; i < (2 * r + 1) * (2 * r + 1); i++) { - int dy = i / (2 * r + 1) - r; - int dx = i % (2 * r + 1) - r; - - int local_rect[4] = { - max(0, -dx), max(0, -dy), rect.z - rect.x - max(0, dx), rect.w - rect.y - max(0, dy)}; - filter_nlm_calc_difference_kernel()(dx, - dy, - (float *)guide_ptr, - (float *)variance_ptr, - NULL, - difference, - local_rect, - w, - channel_offset, - 0, - a, - k_2); - - filter_nlm_blur_kernel()(difference, blurDifference, local_rect, w, f); - filter_nlm_calc_weight_kernel()(blurDifference, difference, local_rect, w, f); - filter_nlm_blur_kernel()(difference, blurDifference, local_rect, w, f); - - filter_nlm_update_output_kernel()(dx, - dy, - blurDifference, - (float *)image_ptr, - difference, - (float *)out_ptr, - weightAccum, - local_rect, - channel_offset, - stride, - f); - } - - int local_rect[4] = {0, 0, rect.z - rect.x, rect.w - rect.y}; - filter_nlm_normalize_kernel()((float *)out_ptr, weightAccum, local_rect, w); - - return true; - } - - bool denoising_construct_transform(DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_CONSTRUCT_TRANSFORM); - - for (int y = 0; y < task->filter_area.w; y++) { - for (int x = 0; x < task->filter_area.z; x++) { - filter_construct_transform_kernel()((float *)task->buffer.mem.device_pointer, - task->tile_info, - x + task->filter_area.x, - y + task->filter_area.y, - y * task->filter_area.z + x, - (float *)task->storage.transform.device_pointer, - (int *)task->storage.rank.device_pointer, - &task->rect.x, - task->buffer.pass_stride, - task->buffer.frame_stride, - task->buffer.use_time, - task->radius, - task->pca_threshold); - } - } - return true; - } - - bool denoising_accumulate(device_ptr color_ptr, - device_ptr color_variance_ptr, - device_ptr scale_ptr, - int frame, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_RECONSTRUCT); - - float *temporary_mem = (float *)task->buffer.temporary_mem.device_pointer; - float *difference = temporary_mem; - float *blurDifference = temporary_mem + task->buffer.pass_stride; - - int r = task->radius; - int frame_offset = frame * task->buffer.frame_stride; - for (int i = 0; i < (2 * r + 1) * (2 * r + 1); i++) { - int dy = i / (2 * r + 1) - r; - int dx = i % (2 * r + 1) - r; - - int local_rect[4] = {max(0, -dx), - max(0, -dy), - task->reconstruction_state.source_w - max(0, dx), - task->reconstruction_state.source_h - max(0, dy)}; - filter_nlm_calc_difference_kernel()(dx, - dy, - (float *)color_ptr, - (float *)color_variance_ptr, - (float *)scale_ptr, - difference, - local_rect, - task->buffer.stride, - task->buffer.pass_stride, - frame_offset, - 1.0f, - task->nlm_k_2); - filter_nlm_blur_kernel()(difference, blurDifference, local_rect, task->buffer.stride, 4); - filter_nlm_calc_weight_kernel()( - blurDifference, difference, local_rect, task->buffer.stride, 4); - filter_nlm_blur_kernel()(difference, blurDifference, local_rect, task->buffer.stride, 4); - filter_nlm_construct_gramian_kernel()(dx, - dy, - task->tile_info->frames[frame], - blurDifference, - (float *)task->buffer.mem.device_pointer, - (float *)task->storage.transform.device_pointer, - (int *)task->storage.rank.device_pointer, - (float *)task->storage.XtWX.device_pointer, - (float3 *)task->storage.XtWY.device_pointer, - local_rect, - &task->reconstruction_state.filter_window.x, - task->buffer.stride, - 4, - task->buffer.pass_stride, - frame_offset, - task->buffer.use_time); - } - - return true; - } - - bool denoising_solve(device_ptr output_ptr, DenoisingTask *task) - { - for (int y = 0; y < task->filter_area.w; y++) { - for (int x = 0; x < task->filter_area.z; x++) { - filter_finalize_kernel()(x, - y, - y * task->filter_area.z + x, - (float *)output_ptr, - (int *)task->storage.rank.device_pointer, - (float *)task->storage.XtWX.device_pointer, - (float3 *)task->storage.XtWY.device_pointer, - &task->reconstruction_state.buffer_params.x, - task->render_buffer.samples); - } - } - return true; - } - - bool denoising_combine_halves(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr mean_ptr, - device_ptr variance_ptr, - int r, - int4 rect, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_COMBINE_HALVES); - - for (int y = rect.y; y < rect.w; y++) { - for (int x = rect.x; x < rect.z; x++) { - filter_combine_halves_kernel()(x, - y, - (float *)mean_ptr, - (float *)variance_ptr, - (float *)a_ptr, - (float *)b_ptr, - &rect.x, - r); - } - } - return true; - } - - bool denoising_divide_shadow(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr sample_variance_ptr, - device_ptr sv_variance_ptr, - device_ptr buffer_variance_ptr, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_DIVIDE_SHADOW); - - for (int y = task->rect.y; y < task->rect.w; y++) { - for (int x = task->rect.x; x < task->rect.z; x++) { - filter_divide_shadow_kernel()(task->render_buffer.samples, - task->tile_info, - x, - y, - (float *)a_ptr, - (float *)b_ptr, - (float *)sample_variance_ptr, - (float *)sv_variance_ptr, - (float *)buffer_variance_ptr, - &task->rect.x, - task->render_buffer.pass_stride, - task->render_buffer.offset); - } - } - return true; - } - - bool denoising_get_feature(int mean_offset, - int variance_offset, - device_ptr mean_ptr, - device_ptr variance_ptr, - float scale, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_GET_FEATURE); - - for (int y = task->rect.y; y < task->rect.w; y++) { - for (int x = task->rect.x; x < task->rect.z; x++) { - filter_get_feature_kernel()(task->render_buffer.samples, - task->tile_info, - mean_offset, - variance_offset, - x, - y, - (float *)mean_ptr, - (float *)variance_ptr, - scale, - &task->rect.x, - task->render_buffer.pass_stride, - task->render_buffer.offset); - } - } - return true; - } - - bool denoising_write_feature(int out_offset, - device_ptr from_ptr, - device_ptr buffer_ptr, - DenoisingTask *task) - { - for (int y = 0; y < task->filter_area.w; y++) { - for (int x = 0; x < task->filter_area.z; x++) { - filter_write_feature_kernel()(task->render_buffer.samples, - x + task->filter_area.x, - y + task->filter_area.y, - &task->reconstruction_state.buffer_params.x, - (float *)from_ptr, - (float *)buffer_ptr, - out_offset, - &task->rect.x); - } - } - return true; - } - - bool denoising_detect_outliers(device_ptr image_ptr, - device_ptr variance_ptr, - device_ptr depth_ptr, - device_ptr output_ptr, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_DETECT_OUTLIERS); - - for (int y = task->rect.y; y < task->rect.w; y++) { - for (int x = task->rect.x; x < task->rect.z; x++) { - filter_detect_outliers_kernel()(x, - y, - (float *)image_ptr, - (float *)variance_ptr, - (float *)depth_ptr, - (float *)output_ptr, - &task->rect.x, - task->buffer.pass_stride); - } - } - return true; - } - - bool adaptive_sampling_filter(KernelGlobals *kg, RenderTile &tile, int sample) - { - WorkTile wtile; - wtile.x = tile.x; - wtile.y = tile.y; - wtile.w = tile.w; - wtile.h = tile.h; - wtile.offset = tile.offset; - wtile.stride = tile.stride; - wtile.buffer = (float *)tile.buffer; - - /* For CPU we do adaptive stopping per sample so we can stop earlier, but - * for combined CPU + GPU rendering we match the GPU and do it per tile - * after a given number of sample steps. */ - if (!kernel_data.integrator.adaptive_stop_per_sample) { - for (int y = wtile.y; y < wtile.y + wtile.h; ++y) { - for (int x = wtile.x; x < wtile.x + wtile.w; ++x) { - const int index = wtile.offset + x + y * wtile.stride; - float *buffer = wtile.buffer + index * kernel_data.film.pass_stride; - kernel_do_adaptive_stopping(kg, buffer, sample); - } - } - } - - bool any = false; - for (int y = wtile.y; y < wtile.y + wtile.h; ++y) { - any |= kernel_do_adaptive_filter_x(kg, y, &wtile); - } - for (int x = wtile.x; x < wtile.x + wtile.w; ++x) { - any |= kernel_do_adaptive_filter_y(kg, x, &wtile); - } - return (!any); - } - - void adaptive_sampling_post(const RenderTile &tile, KernelGlobals *kg) - { - float *render_buffer = (float *)tile.buffer; - for (int y = tile.y; y < tile.y + tile.h; y++) { - for (int x = tile.x; x < tile.x + tile.w; x++) { - int index = tile.offset + x + y * tile.stride; - ccl_global float *buffer = render_buffer + index * kernel_data.film.pass_stride; - if (buffer[kernel_data.film.pass_sample_count] < 0.0f) { - buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count]; - float sample_multiplier = tile.sample / buffer[kernel_data.film.pass_sample_count]; - if (sample_multiplier != 1.0f) { - kernel_adaptive_post_adjust(kg, buffer, sample_multiplier); - } - } - else { - kernel_adaptive_post_adjust(kg, buffer, tile.sample / (tile.sample - 1.0f)); - } - } - } - } - - void render(DeviceTask &task, RenderTile &tile, KernelGlobals *kg) - { - const bool use_coverage = kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE; - - scoped_timer timer(&tile.buffers->render_time); - - Coverage coverage(kg, tile); - if (use_coverage) { - coverage.init_path_trace(); - } - - float *render_buffer = (float *)tile.buffer; - int start_sample = tile.start_sample; - int end_sample = tile.start_sample + tile.num_samples; - - /* Needed for Embree. */ - SIMD_SET_FLUSH_TO_ZERO; - - for (int sample = start_sample; sample < end_sample; sample++) { - if (task.get_cancel() || TaskPool::canceled()) { - if (task.need_finish_queue == false) - break; - } - - if (tile.stealing_state == RenderTile::CAN_BE_STOLEN && task.get_tile_stolen()) { - tile.stealing_state = RenderTile::WAS_STOLEN; - break; - } - - if (tile.task == RenderTile::PATH_TRACE) { - for (int y = tile.y; y < tile.y + tile.h; y++) { - for (int x = tile.x; x < tile.x + tile.w; x++) { - if (use_coverage) { - coverage.init_pixel(x, y); - } - path_trace_kernel()(kg, render_buffer, sample, x, y, tile.offset, tile.stride); - } - } - } - else { - for (int y = tile.y; y < tile.y + tile.h; y++) { - for (int x = tile.x; x < tile.x + tile.w; x++) { - bake_kernel()(kg, render_buffer, sample, x, y, tile.offset, tile.stride); - } - } - } - tile.sample = sample + 1; - - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(sample)) { - const bool stop = adaptive_sampling_filter(kg, tile, sample); - if (stop) { - const int num_progress_samples = end_sample - sample; - tile.sample = end_sample; - task.update_progress(&tile, tile.w * tile.h * num_progress_samples); - break; - } - } - - task.update_progress(&tile, tile.w * tile.h); - } - if (use_coverage) { - coverage.finalize(); - } - - if (task.adaptive_sampling.use && (tile.stealing_state != RenderTile::WAS_STOLEN)) { - adaptive_sampling_post(tile, kg); - } - } - - void denoise_openimagedenoise_buffer(DeviceTask &task, - float *buffer, - const size_t offset, - const size_t stride, - const size_t x, - const size_t y, - const size_t w, - const size_t h, - const float scale) - { -#ifdef WITH_OPENIMAGEDENOISE - assert(openimagedenoise_supported()); - - /* Only one at a time, since OpenImageDenoise itself is multithreaded for full - * buffers, and for tiled rendering because creating multiple devices and filters - * is slow and memory hungry as well. - * - * TODO: optimize tiled rendering case, by batching together denoising of many - * tiles somehow? */ - static thread_mutex mutex; - thread_scoped_lock lock(mutex); - - /* Create device and filter, cached for reuse. */ - if (!oidn_device) { - oidn_device = oidn::newDevice(); - oidn_device.commit(); - } - if (!oidn_filter) { - oidn_filter = oidn_device.newFilter("RT"); - oidn_filter.set("hdr", true); - oidn_filter.set("srgb", false); - } - - /* Set images with appropriate stride for our interleaved pass storage. */ - struct { - const char *name; - const int offset; - const bool scale; - const bool use; - array scaled_buffer; - } passes[] = {{"color", task.pass_denoising_data + DENOISING_PASS_COLOR, false, true}, - {"albedo", - task.pass_denoising_data + DENOISING_PASS_ALBEDO, - true, - task.denoising.input_passes >= DENOISER_INPUT_RGB_ALBEDO}, - {"normal", - task.pass_denoising_data + DENOISING_PASS_NORMAL, - true, - task.denoising.input_passes >= DENOISER_INPUT_RGB_ALBEDO_NORMAL}, - {"output", 0, false, true}, - { NULL, - 0 }}; - - for (int i = 0; passes[i].name; i++) { - if (!passes[i].use) { - continue; - } - - const int64_t pixel_offset = offset + x + y * stride; - const int64_t buffer_offset = (pixel_offset * task.pass_stride + passes[i].offset); - const int64_t pixel_stride = task.pass_stride; - const int64_t row_stride = stride * pixel_stride; - - if (passes[i].scale && scale != 1.0f) { - /* Normalize albedo and normal passes as they are scaled by the number of samples. - * For the color passes OIDN will perform auto-exposure making it unnecessary. */ - array &scaled_buffer = passes[i].scaled_buffer; - scaled_buffer.resize(w * h * 3); - - for (int y = 0; y < h; y++) { - const float *pass_row = buffer + buffer_offset + y * row_stride; - float *scaled_row = scaled_buffer.data() + y * w * 3; - - for (int x = 0; x < w; x++) { - scaled_row[x * 3 + 0] = pass_row[x * pixel_stride + 0] * scale; - scaled_row[x * 3 + 1] = pass_row[x * pixel_stride + 1] * scale; - scaled_row[x * 3 + 2] = pass_row[x * pixel_stride + 2] * scale; - } - } - - oidn_filter.setImage( - passes[i].name, scaled_buffer.data(), oidn::Format::Float3, w, h, 0, 0, 0); - } - else { - oidn_filter.setImage(passes[i].name, - buffer + buffer_offset, - oidn::Format::Float3, - w, - h, - 0, - pixel_stride * sizeof(float), - row_stride * sizeof(float)); - } - } - - /* Execute filter. */ - oidn_filter.commit(); - oidn_filter.execute(); -#else - (void)task; - (void)buffer; - (void)offset; - (void)stride; - (void)x; - (void)y; - (void)w; - (void)h; - (void)scale; -#endif - } - - void denoise_openimagedenoise(DeviceTask &task, RenderTile &rtile) - { - if (task.type == DeviceTask::DENOISE_BUFFER) { - /* Copy pixels from compute device to CPU (no-op for CPU device). */ - rtile.buffers->buffer.copy_from_device(); - - denoise_openimagedenoise_buffer(task, - (float *)rtile.buffer, - rtile.offset, - rtile.stride, - rtile.x, - rtile.y, - rtile.w, - rtile.h, - 1.0f / rtile.sample); - - /* todo: it may be possible to avoid this copy, but we have to ensure that - * when other code copies data from the device it doesn't overwrite the - * denoiser buffers. */ - rtile.buffers->buffer.copy_to_device(); - } - else { - /* Per-tile denoising. */ - rtile.sample = rtile.start_sample + rtile.num_samples; - const float scale = 1.0f / rtile.sample; - const float invscale = rtile.sample; - const size_t pass_stride = task.pass_stride; - - /* Map neighboring tiles into one buffer for denoising. */ - RenderTileNeighbors neighbors(rtile); - task.map_neighbor_tiles(neighbors, this); - RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; - rtile = center_tile; - - /* Calculate size of the tile to denoise (including overlap). The overlap - * size was chosen empirically. OpenImageDenoise specifies an overlap size - * of 128 but this is significantly bigger than typical tile size. */ - const int4 rect = rect_clip(rect_expand(center_tile.bounds(), 64), neighbors.bounds()); - const int2 rect_size = make_int2(rect.z - rect.x, rect.w - rect.y); - - /* Adjacent tiles are in separate memory regions, copy into single buffer. */ - array merged(rect_size.x * rect_size.y * task.pass_stride); - - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - RenderTile &ntile = neighbors.tiles[i]; - if (!ntile.buffer) { - continue; - } - - const int xmin = max(ntile.x, rect.x); - const int ymin = max(ntile.y, rect.y); - const int xmax = min(ntile.x + ntile.w, rect.z); - const int ymax = min(ntile.y + ntile.h, rect.w); - - const size_t tile_offset = ntile.offset + xmin + ymin * ntile.stride; - const float *tile_buffer = (float *)ntile.buffer + tile_offset * pass_stride; - - const size_t merged_stride = rect_size.x; - const size_t merged_offset = (xmin - rect.x) + (ymin - rect.y) * merged_stride; - float *merged_buffer = merged.data() + merged_offset * pass_stride; - - for (int y = ymin; y < ymax; y++) { - for (int x = 0; x < pass_stride * (xmax - xmin); x++) { - merged_buffer[x] = tile_buffer[x] * scale; - } - tile_buffer += ntile.stride * pass_stride; - merged_buffer += merged_stride * pass_stride; - } - } - - /* Denoise */ - denoise_openimagedenoise_buffer( - task, merged.data(), 0, rect_size.x, 0, 0, rect_size.x, rect_size.y, 1.0f); - - /* Copy back result from merged buffer. */ - RenderTile &ntile = neighbors.target; - if (ntile.buffer) { - const int xmin = max(ntile.x, rect.x); - const int ymin = max(ntile.y, rect.y); - const int xmax = min(ntile.x + ntile.w, rect.z); - const int ymax = min(ntile.y + ntile.h, rect.w); - - const size_t tile_offset = ntile.offset + xmin + ymin * ntile.stride; - float *tile_buffer = (float *)ntile.buffer + tile_offset * pass_stride; - - const size_t merged_stride = rect_size.x; - const size_t merged_offset = (xmin - rect.x) + (ymin - rect.y) * merged_stride; - const float *merged_buffer = merged.data() + merged_offset * pass_stride; - - for (int y = ymin; y < ymax; y++) { - for (int x = 0; x < pass_stride * (xmax - xmin); x += pass_stride) { - tile_buffer[x + 0] = merged_buffer[x + 0] * invscale; - tile_buffer[x + 1] = merged_buffer[x + 1] * invscale; - tile_buffer[x + 2] = merged_buffer[x + 2] * invscale; - } - tile_buffer += ntile.stride * pass_stride; - merged_buffer += merged_stride * pass_stride; - } - } - - task.unmap_neighbor_tiles(neighbors, this); - } - } - - void denoise_nlm(DenoisingTask &denoising, RenderTile &tile) - { - ProfilingHelper profiling(denoising.profiler, PROFILING_DENOISING); - - tile.sample = tile.start_sample + tile.num_samples; - - denoising.functions.construct_transform = function_bind( - &CPUDevice::denoising_construct_transform, this, &denoising); - denoising.functions.accumulate = function_bind( - &CPUDevice::denoising_accumulate, this, _1, _2, _3, _4, &denoising); - denoising.functions.solve = function_bind(&CPUDevice::denoising_solve, this, _1, &denoising); - denoising.functions.divide_shadow = function_bind( - &CPUDevice::denoising_divide_shadow, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.non_local_means = function_bind( - &CPUDevice::denoising_non_local_means, this, _1, _2, _3, _4, &denoising); - denoising.functions.combine_halves = function_bind( - &CPUDevice::denoising_combine_halves, this, _1, _2, _3, _4, _5, _6, &denoising); - denoising.functions.get_feature = function_bind( - &CPUDevice::denoising_get_feature, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.write_feature = function_bind( - &CPUDevice::denoising_write_feature, this, _1, _2, _3, &denoising); - denoising.functions.detect_outliers = function_bind( - &CPUDevice::denoising_detect_outliers, this, _1, _2, _3, _4, &denoising); - - denoising.filter_area = make_int4(tile.x, tile.y, tile.w, tile.h); - denoising.render_buffer.samples = tile.sample; - denoising.buffer.gpu_temporary_mem = false; - - denoising.run_denoising(tile); - } - - void thread_render(DeviceTask &task) - { - if (TaskPool::canceled()) { - if (task.need_finish_queue == false) - return; - } - - /* allocate buffer for kernel globals */ - device_only_memory kgbuffer(this, "kernel_globals"); - kgbuffer.alloc_to_device(1); - - KernelGlobals *kg = new ((void *)kgbuffer.device_pointer) - KernelGlobals(thread_kernel_globals_init()); - - profiler.add_state(&kg->profiler); - - CPUSplitKernel *split_kernel = NULL; - if (use_split_kernel) { - split_kernel = new CPUSplitKernel(this); - if (!split_kernel->load_kernels(requested_features)) { - thread_kernel_globals_free((KernelGlobals *)kgbuffer.device_pointer); - kgbuffer.free(); - delete split_kernel; - return; - } - } - - /* NLM denoiser. */ - DenoisingTask *denoising = NULL; - - /* OpenImageDenoise: we can only denoise with one thread at a time, so to - * avoid waiting with mutex locks in the denoiser, we let only a single - * thread acquire denoising tiles. */ - uint tile_types = task.tile_types; - bool hold_denoise_lock = false; - if ((tile_types & RenderTile::DENOISE) && task.denoising.type == DENOISER_OPENIMAGEDENOISE) { - if (!oidn_task_lock.try_lock()) { - tile_types &= ~RenderTile::DENOISE; - hold_denoise_lock = true; - } - } - - RenderTile tile; - while (task.acquire_tile(this, tile, tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) { - if (use_split_kernel) { - device_only_memory void_buffer(this, "void_buffer"); - split_kernel->path_trace(task, tile, kgbuffer, void_buffer); - } - else { - render(task, tile, kg); - } - } - else if (tile.task == RenderTile::BAKE) { - render(task, tile, kg); - } - else if (tile.task == RenderTile::DENOISE) { - if (task.denoising.type == DENOISER_OPENIMAGEDENOISE) { - denoise_openimagedenoise(task, tile); - } - else if (task.denoising.type == DENOISER_NLM) { - if (denoising == NULL) { - denoising = new DenoisingTask(this, task); - denoising->profiler = &kg->profiler; - } - denoise_nlm(*denoising, tile); - } - task.update_progress(&tile, tile.w * tile.h); - } - - task.release_tile(tile); - - if (TaskPool::canceled()) { - if (task.need_finish_queue == false) - break; - } - } - - if (hold_denoise_lock) { - oidn_task_lock.unlock(); - } - - profiler.remove_state(&kg->profiler); - - thread_kernel_globals_free((KernelGlobals *)kgbuffer.device_pointer); - kg->~KernelGlobals(); - kgbuffer.free(); - delete split_kernel; - delete denoising; - } - - void thread_denoise(DeviceTask &task) - { - RenderTile tile; - tile.x = task.x; - tile.y = task.y; - tile.w = task.w; - tile.h = task.h; - tile.buffer = task.buffer; - tile.sample = task.sample + task.num_samples; - tile.num_samples = task.num_samples; - tile.start_sample = task.sample; - tile.offset = task.offset; - tile.stride = task.stride; - tile.buffers = task.buffers; - - if (task.denoising.type == DENOISER_OPENIMAGEDENOISE) { - denoise_openimagedenoise(task, tile); - } - else { - DenoisingTask denoising(this, task); - - ProfilingState denoising_profiler_state; - profiler.add_state(&denoising_profiler_state); - denoising.profiler = &denoising_profiler_state; - - denoise_nlm(denoising, tile); - - profiler.remove_state(&denoising_profiler_state); - } - - task.update_progress(&tile, tile.w * tile.h); - } - - void thread_film_convert(DeviceTask &task) - { - float sample_scale = 1.0f / (task.sample + 1); - - if (task.rgba_half) { - for (int y = task.y; y < task.y + task.h; y++) - for (int x = task.x; x < task.x + task.w; x++) - convert_to_half_float_kernel()(&kernel_globals, - (uchar4 *)task.rgba_half, - (float *)task.buffer, - sample_scale, - x, - y, - task.offset, - task.stride); - } - else { - for (int y = task.y; y < task.y + task.h; y++) - for (int x = task.x; x < task.x + task.w; x++) - convert_to_byte_kernel()(&kernel_globals, - (uchar4 *)task.rgba_byte, - (float *)task.buffer, - sample_scale, - x, - y, - task.offset, - task.stride); - } - } - - void thread_shader(DeviceTask &task) - { - KernelGlobals *kg = new KernelGlobals(thread_kernel_globals_init()); - - for (int sample = 0; sample < task.num_samples; sample++) { - for (int x = task.shader_x; x < task.shader_x + task.shader_w; x++) - shader_kernel()(kg, - (uint4 *)task.shader_input, - (float4 *)task.shader_output, - task.shader_eval_type, - task.shader_filter, - x, - task.offset, - sample); - - if (task.get_cancel() || TaskPool::canceled()) - break; - - task.update_progress(NULL); - } - - thread_kernel_globals_free(kg); - delete kg; - } - - virtual int get_split_task_count(DeviceTask &task) override - { - if (task.type == DeviceTask::SHADER) - return task.get_subtask_count(info.cpu_threads, 256); - else - return task.get_subtask_count(info.cpu_threads); - } - - virtual void task_add(DeviceTask &task) override - { - /* Load texture info. */ - load_texture_info(); - - /* split task into smaller ones */ - list tasks; - - if (task.type == DeviceTask::DENOISE_BUFFER && - task.denoising.type == DENOISER_OPENIMAGEDENOISE) { - /* Denoise entire buffer at once with OIDN, it has own threading. */ - tasks.push_back(task); - } - else if (task.type == DeviceTask::SHADER) { - task.split(tasks, info.cpu_threads, 256); - } - else { - task.split(tasks, info.cpu_threads); - } - - foreach (DeviceTask &task, tasks) { - task_pool.push([=] { - DeviceTask task_copy = task; - thread_run(task_copy); - }); - } - } - - virtual void task_wait() override - { - task_pool.wait_work(); - } - - virtual void task_cancel() override - { - task_pool.cancel(); - } - - protected: - inline KernelGlobals thread_kernel_globals_init() - { - KernelGlobals kg = kernel_globals; - kg.transparent_shadow_intersections = NULL; - const int decoupled_count = sizeof(kg.decoupled_volume_steps) / - sizeof(*kg.decoupled_volume_steps); - for (int i = 0; i < decoupled_count; ++i) { - kg.decoupled_volume_steps[i] = NULL; - } - kg.decoupled_volume_steps_index = 0; - kg.coverage_asset = kg.coverage_object = kg.coverage_material = NULL; -#ifdef WITH_OSL - OSLShader::thread_init(&kg, &kernel_globals, &osl_globals); -#endif - return kg; - } - - inline void thread_kernel_globals_free(KernelGlobals *kg) - { - if (kg == NULL) { - return; - } - - if (kg->transparent_shadow_intersections != NULL) { - free(kg->transparent_shadow_intersections); - } - const int decoupled_count = sizeof(kg->decoupled_volume_steps) / - sizeof(*kg->decoupled_volume_steps); - for (int i = 0; i < decoupled_count; ++i) { - if (kg->decoupled_volume_steps[i] != NULL) { - free(kg->decoupled_volume_steps[i]); - } - } -#ifdef WITH_OSL - OSLShader::thread_free(kg); -#endif - } - - virtual bool load_kernels(const DeviceRequestedFeatures &requested_features_) override - { - requested_features = requested_features_; - - return true; - } -}; - -/* split kernel */ - -class CPUSplitKernelFunction : public SplitKernelFunction { - public: - CPUDevice *device; - void (*func)(KernelGlobals *kg, KernelData *data); - - CPUSplitKernelFunction(CPUDevice *device) : device(device), func(NULL) - { - } - ~CPUSplitKernelFunction() - { - } - - virtual bool enqueue(const KernelDimensions &dim, - device_memory &kernel_globals, - device_memory &data) - { - if (!func) { - return false; - } - - KernelGlobals *kg = (KernelGlobals *)kernel_globals.device_pointer; - kg->global_size = make_int2(dim.global_size[0], dim.global_size[1]); - - for (int y = 0; y < dim.global_size[1]; y++) { - for (int x = 0; x < dim.global_size[0]; x++) { - kg->global_id = make_int2(x, y); - - func(kg, (KernelData *)data.device_pointer); - } - } - - return true; - } -}; - -CPUSplitKernel::CPUSplitKernel(CPUDevice *device) : DeviceSplitKernel(device), device(device) -{ -} - -bool CPUSplitKernel::enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory &kernel_globals, - device_memory &data, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flags, - device_memory &work_pool_wgs) -{ - KernelGlobals *kg = (KernelGlobals *)kernel_globals.device_pointer; - kg->global_size = make_int2(dim.global_size[0], dim.global_size[1]); - - for (int y = 0; y < dim.global_size[1]; y++) { - for (int x = 0; x < dim.global_size[0]; x++) { - kg->global_id = make_int2(x, y); - - device->data_init_kernel()((KernelGlobals *)kernel_globals.device_pointer, - (KernelData *)data.device_pointer, - (void *)split_data.device_pointer, - num_global_elements, - (char *)ray_state.device_pointer, - rtile.start_sample, - rtile.start_sample + rtile.num_samples, - rtile.x, - rtile.y, - rtile.w, - rtile.h, - rtile.offset, - rtile.stride, - (int *)queue_index.device_pointer, - dim.global_size[0] * dim.global_size[1], - (char *)use_queues_flags.device_pointer, - (uint *)work_pool_wgs.device_pointer, - rtile.num_samples, - (float *)rtile.buffer); - } - } - - return true; -} - -SplitKernelFunction *CPUSplitKernel::get_split_kernel_function(const string &kernel_name, - const DeviceRequestedFeatures &) -{ - CPUSplitKernelFunction *kernel = new CPUSplitKernelFunction(device); - - kernel->func = device->split_kernels[kernel_name](); - if (!kernel->func) { - delete kernel; - return NULL; - } - - return kernel; -} - -int2 CPUSplitKernel::split_kernel_local_size() -{ - return make_int2(1, 1); -} - -int2 CPUSplitKernel::split_kernel_global_size(device_memory & /*kg*/, - device_memory & /*data*/, - DeviceTask & /*task*/) -{ - return make_int2(1, 1); -} - -uint64_t CPUSplitKernel::state_buffer_size(device_memory &kernel_globals, - device_memory & /*data*/, - size_t num_threads) -{ - KernelGlobals *kg = (KernelGlobals *)kernel_globals.device_pointer; - - return split_data_buffer_size(kg, num_threads); -} - -Device *device_cpu_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) -{ - return new CPUDevice(info, stats, profiler, background); -} - -void device_cpu_info(vector &devices) -{ - DeviceInfo info; - - info.type = DEVICE_CPU; - info.description = system_cpu_brand_string(); - info.id = "CPU"; - info.num = 0; - info.has_volume_decoupled = true; - info.has_adaptive_stop_per_sample = true; - info.has_osl = true; - info.has_half_images = true; - info.has_nanovdb = true; - info.has_profiling = true; - info.denoisers = DENOISER_NLM; - if (openimagedenoise_supported()) { - info.denoisers |= DENOISER_OPENIMAGEDENOISE; - } - - devices.insert(devices.begin(), info); -} - -string device_cpu_capabilities() -{ - string capabilities = ""; - capabilities += system_cpu_support_sse2() ? "SSE2 " : ""; - capabilities += system_cpu_support_sse3() ? "SSE3 " : ""; - capabilities += system_cpu_support_sse41() ? "SSE41 " : ""; - capabilities += system_cpu_support_avx() ? "AVX " : ""; - capabilities += system_cpu_support_avx2() ? "AVX2" : ""; - if (capabilities[capabilities.size() - 1] == ' ') - capabilities.resize(capabilities.size() - 1); - return capabilities; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_denoise.cpp b/intern/cycles/device/device_denoise.cpp new file mode 100644 index 00000000000..aea7868f65d --- /dev/null +++ b/intern/cycles/device/device_denoise.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/device_denoise.h" + +CCL_NAMESPACE_BEGIN + +const char *denoiserTypeToHumanReadable(DenoiserType type) +{ + switch (type) { + case DENOISER_OPTIX: + return "OptiX"; + case DENOISER_OPENIMAGEDENOISE: + return "OpenImageDenoise"; + + case DENOISER_NUM: + case DENOISER_NONE: + case DENOISER_ALL: + return "UNKNOWN"; + } + + return "UNKNOWN"; +} + +const NodeEnum *DenoiseParams::get_type_enum() +{ + static NodeEnum type_enum; + + if (type_enum.empty()) { + type_enum.insert("optix", DENOISER_OPTIX); + type_enum.insert("openimageio", DENOISER_OPENIMAGEDENOISE); + } + + return &type_enum; +} + +const NodeEnum *DenoiseParams::get_prefilter_enum() +{ + static NodeEnum prefilter_enum; + + if (prefilter_enum.empty()) { + prefilter_enum.insert("none", DENOISER_PREFILTER_NONE); + prefilter_enum.insert("fast", DENOISER_PREFILTER_FAST); + prefilter_enum.insert("accurate", DENOISER_PREFILTER_ACCURATE); + } + + return &prefilter_enum; +} + +NODE_DEFINE(DenoiseParams) +{ + NodeType *type = NodeType::add("denoise_params", create); + + const NodeEnum *type_enum = get_type_enum(); + const NodeEnum *prefilter_enum = get_prefilter_enum(); + + SOCKET_BOOLEAN(use, "Use", false); + + SOCKET_ENUM(type, "Type", *type_enum, DENOISER_OPENIMAGEDENOISE); + + SOCKET_INT(start_sample, "Start Sample", 0); + + SOCKET_BOOLEAN(use_pass_albedo, "Use Pass Albedo", true); + SOCKET_BOOLEAN(use_pass_normal, "Use Pass Normal", false); + + SOCKET_ENUM(prefilter, "Prefilter", *prefilter_enum, DENOISER_PREFILTER_FAST); + + return type; +} + +DenoiseParams::DenoiseParams() : Node(get_node_type()) +{ +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_denoise.h b/intern/cycles/device/device_denoise.h new file mode 100644 index 00000000000..02ee63fb0ad --- /dev/null +++ b/intern/cycles/device/device_denoise.h @@ -0,0 +1,110 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "device/device_memory.h" +#include "graph/node.h" +#include "render/buffers.h" + +CCL_NAMESPACE_BEGIN + +enum DenoiserType { + DENOISER_OPTIX = 2, + DENOISER_OPENIMAGEDENOISE = 4, + DENOISER_NUM, + + DENOISER_NONE = 0, + DENOISER_ALL = ~0, +}; + +/* COnstruct human-readable string which denotes the denoiser type. */ +const char *denoiserTypeToHumanReadable(DenoiserType type); + +typedef int DenoiserTypeMask; + +enum DenoiserPrefilter { + /* Best quality of the result without extra processing time, but requires guiding passes to be + * noise-free. */ + DENOISER_PREFILTER_NONE = 1, + + /* Denoise color and guiding passes together. + * Improves quality when guiding passes are noisy using least amount of extra processing time. */ + DENOISER_PREFILTER_FAST = 2, + + /* Prefilter noisy guiding passes before denoising color. + * Improves quality when guiding passes are noisy using extra processing time. */ + DENOISER_PREFILTER_ACCURATE = 3, + + DENOISER_PREFILTER_NUM, +}; + +/* NOTE: Is not a real scene node. Using Node API for ease of (de)serialization. + * The default values here do not really matter as they are always initialized from the + * Integrator node. */ +class DenoiseParams : public Node { + public: + NODE_DECLARE + + /* Apply denoiser to image. */ + bool use = false; + + /* Denoiser type. */ + DenoiserType type = DENOISER_OPENIMAGEDENOISE; + + /* Viewport start sample. */ + int start_sample = 0; + + /* Auxiliry passes. */ + bool use_pass_albedo = true; + bool use_pass_normal = true; + + DenoiserPrefilter prefilter = DENOISER_PREFILTER_FAST; + + static const NodeEnum *get_type_enum(); + static const NodeEnum *get_prefilter_enum(); + + DenoiseParams(); + + bool modified(const DenoiseParams &other) const + { + return !(use == other.use && type == other.type && start_sample == other.start_sample && + use_pass_albedo == other.use_pass_albedo && + use_pass_normal == other.use_pass_normal && prefilter == other.prefilter); + } +}; + +/* All the parameters needed to perform buffer denoising on a device. + * Is not really a task in its canonical terms (as in, is not an asynchronous running task). Is + * more like a wrapper for all the arguments and parameters needed to perform denoising. Is a + * single place where they are all listed, so that it's not required to modify all device methods + * when these parameters do change. */ +class DeviceDenoiseTask { + public: + DenoiseParams params; + + int num_samples; + + RenderBuffers *render_buffers; + BufferParams buffer_params; + + /* Allow to do in-place modification of the input passes (scaling them down i.e.). This will + * lower the memory footprint of the denoiser but will make input passes "invalid" (from path + * tracer) point of view. */ + bool allow_inplace_modification; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_denoising.cpp b/intern/cycles/device/device_denoising.cpp deleted file mode 100644 index 38c42d15cab..00000000000 --- a/intern/cycles/device/device_denoising.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "device/device_denoising.h" - -#include "kernel/filter/filter_defines.h" - -CCL_NAMESPACE_BEGIN - -DenoisingTask::DenoisingTask(Device *device, const DeviceTask &task) - : tile_info_mem(device, "denoising tile info mem", MEM_READ_WRITE), - profiler(NULL), - storage(device), - buffer(device), - device(device) -{ - radius = task.denoising.radius; - nlm_k_2 = powf(2.0f, lerp(-5.0f, 3.0f, task.denoising.strength)); - if (task.denoising.relative_pca) { - pca_threshold = -powf(10.0f, lerp(-8.0f, 0.0f, task.denoising.feature_strength)); - } - else { - pca_threshold = powf(10.0f, lerp(-5.0f, 3.0f, task.denoising.feature_strength)); - } - - render_buffer.frame_stride = task.frame_stride; - render_buffer.pass_stride = task.pass_stride; - render_buffer.offset = task.pass_denoising_data; - - target_buffer.pass_stride = task.target_pass_stride; - target_buffer.denoising_clean_offset = task.pass_denoising_clean; - target_buffer.offset = 0; - - functions.map_neighbor_tiles = function_bind(task.map_neighbor_tiles, _1, device); - functions.unmap_neighbor_tiles = function_bind(task.unmap_neighbor_tiles, _1, device); - - tile_info = (TileInfo *)tile_info_mem.alloc(sizeof(TileInfo) / sizeof(int)); - tile_info->from_render = task.denoising_from_render ? 1 : 0; - - tile_info->frames[0] = 0; - tile_info->num_frames = min(task.denoising_frames.size() + 1, DENOISE_MAX_FRAMES); - for (int i = 1; i < tile_info->num_frames; i++) { - tile_info->frames[i] = task.denoising_frames[i - 1]; - } - - do_prefilter = task.denoising.store_passes && task.denoising.type == DENOISER_NLM; - do_filter = task.denoising.use && task.denoising.type == DENOISER_NLM; -} - -DenoisingTask::~DenoisingTask() -{ - storage.XtWX.free(); - storage.XtWY.free(); - storage.transform.free(); - storage.rank.free(); - buffer.mem.free(); - buffer.temporary_mem.free(); - tile_info_mem.free(); -} - -void DenoisingTask::set_render_buffer(RenderTileNeighbors &neighbors) -{ - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - RenderTile &rtile = neighbors.tiles[i]; - tile_info->offsets[i] = rtile.offset; - tile_info->strides[i] = rtile.stride; - tile_info->buffers[i] = rtile.buffer; - } - tile_info->x[0] = neighbors.tiles[3].x; - tile_info->x[1] = neighbors.tiles[4].x; - tile_info->x[2] = neighbors.tiles[5].x; - tile_info->x[3] = neighbors.tiles[5].x + neighbors.tiles[5].w; - tile_info->y[0] = neighbors.tiles[1].y; - tile_info->y[1] = neighbors.tiles[4].y; - tile_info->y[2] = neighbors.tiles[7].y; - tile_info->y[3] = neighbors.tiles[7].y + neighbors.tiles[7].h; - - target_buffer.offset = neighbors.target.offset; - target_buffer.stride = neighbors.target.stride; - target_buffer.ptr = neighbors.target.buffer; - - if (do_prefilter && neighbors.target.buffers) { - target_buffer.denoising_output_offset = - neighbors.target.buffers->params.get_denoising_prefiltered_offset(); - } - else { - target_buffer.denoising_output_offset = 0; - } - - tile_info_mem.copy_to_device(); -} - -void DenoisingTask::setup_denoising_buffer() -{ - /* Expand filter_area by radius pixels and clamp the result to the extent of the neighboring - * tiles */ - rect = rect_from_shape(filter_area.x, filter_area.y, filter_area.z, filter_area.w); - rect = rect_expand(rect, radius); - rect = rect_clip(rect, - make_int4(tile_info->x[0], tile_info->y[0], tile_info->x[3], tile_info->y[3])); - - buffer.use_intensity = do_prefilter || (tile_info->num_frames > 1); - buffer.passes = buffer.use_intensity ? 15 : 14; - buffer.width = rect.z - rect.x; - buffer.stride = align_up(buffer.width, 4); - buffer.h = rect.w - rect.y; - int alignment_floats = divide_up(device->mem_sub_ptr_alignment(), sizeof(float)); - buffer.pass_stride = align_up(buffer.stride * buffer.h, alignment_floats); - buffer.frame_stride = buffer.pass_stride * buffer.passes; - /* Pad the total size by four floats since the SIMD kernels might go a bit over the end. */ - int mem_size = align_up(tile_info->num_frames * buffer.frame_stride + 4, alignment_floats); - buffer.mem.alloc_to_device(mem_size, false); - buffer.use_time = (tile_info->num_frames > 1); - - /* CPUs process shifts sequentially while GPUs process them in parallel. */ - int num_layers; - if (buffer.gpu_temporary_mem) { - /* Shadowing prefiltering uses a radius of 6, so allocate at least that much. */ - int max_radius = max(radius, 6); - int num_shifts = (2 * max_radius + 1) * (2 * max_radius + 1); - num_layers = 2 * num_shifts + 1; - } - else { - num_layers = 3; - } - /* Allocate two layers per shift as well as one for the weight accumulation. */ - buffer.temporary_mem.alloc_to_device(num_layers * buffer.pass_stride); -} - -void DenoisingTask::prefilter_shadowing() -{ - device_ptr null_ptr = (device_ptr)0; - - device_sub_ptr unfiltered_a(buffer.mem, 0, buffer.pass_stride); - device_sub_ptr unfiltered_b(buffer.mem, 1 * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr sample_var(buffer.mem, 2 * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr sample_var_var(buffer.mem, 3 * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr buffer_var(buffer.mem, 5 * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr filtered_var(buffer.mem, 6 * buffer.pass_stride, buffer.pass_stride); - - /* Get the A/B unfiltered passes, the combined sample variance, the estimated variance of the - * sample variance and the buffer variance. */ - functions.divide_shadow(*unfiltered_a, *unfiltered_b, *sample_var, *sample_var_var, *buffer_var); - - /* Smooth the (generally pretty noisy) buffer variance using the spatial information from the - * sample variance. */ - nlm_state.set_parameters(6, 3, 4.0f, 1.0f, false); - functions.non_local_means(*buffer_var, *sample_var, *sample_var_var, *filtered_var); - - /* Reuse memory, the previous data isn't needed anymore. */ - device_ptr filtered_a = *buffer_var, filtered_b = *sample_var; - /* Use the smoothed variance to filter the two shadow half images using each other for weight - * calculation. */ - nlm_state.set_parameters(5, 3, 1.0f, 0.25f, false); - functions.non_local_means(*unfiltered_a, *unfiltered_b, *filtered_var, filtered_a); - functions.non_local_means(*unfiltered_b, *unfiltered_a, *filtered_var, filtered_b); - - device_ptr residual_var = *sample_var_var; - /* Estimate the residual variance between the two filtered halves. */ - functions.combine_halves(filtered_a, filtered_b, null_ptr, residual_var, 2, rect); - - device_ptr final_a = *unfiltered_a, final_b = *unfiltered_b; - /* Use the residual variance for a second filter pass. */ - nlm_state.set_parameters(4, 2, 1.0f, 0.5f, false); - functions.non_local_means(filtered_a, filtered_b, residual_var, final_a); - functions.non_local_means(filtered_b, filtered_a, residual_var, final_b); - - /* Combine the two double-filtered halves to a final shadow feature. */ - device_sub_ptr shadow_pass(buffer.mem, 4 * buffer.pass_stride, buffer.pass_stride); - functions.combine_halves(final_a, final_b, *shadow_pass, null_ptr, 0, rect); -} - -void DenoisingTask::prefilter_features() -{ - device_sub_ptr unfiltered(buffer.mem, 8 * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr variance(buffer.mem, 9 * buffer.pass_stride, buffer.pass_stride); - - int mean_from[] = {0, 1, 2, 12, 6, 7, 8}; - int variance_from[] = {3, 4, 5, 13, 9, 10, 11}; - int pass_to[] = {1, 2, 3, 0, 5, 6, 7}; - for (int pass = 0; pass < 7; pass++) { - device_sub_ptr feature_pass( - buffer.mem, pass_to[pass] * buffer.pass_stride, buffer.pass_stride); - /* Get the unfiltered pass and its variance from the RenderBuffers. */ - functions.get_feature(mean_from[pass], - variance_from[pass], - *unfiltered, - *variance, - 1.0f / render_buffer.samples); - /* Smooth the pass and store the result in the denoising buffers. */ - nlm_state.set_parameters(2, 2, 1.0f, 0.25f, false); - functions.non_local_means(*unfiltered, *unfiltered, *variance, *feature_pass); - } -} - -void DenoisingTask::prefilter_color() -{ - int mean_from[] = {20, 21, 22}; - int variance_from[] = {23, 24, 25}; - int mean_to[] = {8, 9, 10}; - int variance_to[] = {11, 12, 13}; - int num_color_passes = 3; - - device_only_memory temporary_color(device, "denoising temporary color"); - temporary_color.alloc_to_device(6 * buffer.pass_stride, false); - - for (int pass = 0; pass < num_color_passes; pass++) { - device_sub_ptr color_pass(temporary_color, pass * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr color_var_pass( - temporary_color, (pass + 3) * buffer.pass_stride, buffer.pass_stride); - functions.get_feature(mean_from[pass], - variance_from[pass], - *color_pass, - *color_var_pass, - 1.0f / render_buffer.samples); - } - - device_sub_ptr depth_pass(buffer.mem, 0, buffer.pass_stride); - device_sub_ptr color_var_pass( - buffer.mem, variance_to[0] * buffer.pass_stride, 3 * buffer.pass_stride); - device_sub_ptr output_pass(buffer.mem, mean_to[0] * buffer.pass_stride, 3 * buffer.pass_stride); - functions.detect_outliers( - temporary_color.device_pointer, *color_var_pass, *depth_pass, *output_pass); - - if (buffer.use_intensity) { - device_sub_ptr intensity_pass(buffer.mem, 14 * buffer.pass_stride, buffer.pass_stride); - nlm_state.set_parameters(radius, 4, 2.0f, nlm_k_2 * 4.0f, true); - functions.non_local_means(*output_pass, *output_pass, *color_var_pass, *intensity_pass); - } -} - -void DenoisingTask::load_buffer() -{ - device_ptr null_ptr = (device_ptr)0; - - int original_offset = render_buffer.offset; - - int num_passes = buffer.use_intensity ? 15 : 14; - for (int i = 0; i < tile_info->num_frames; i++) { - for (int pass = 0; pass < num_passes; pass++) { - device_sub_ptr to_pass( - buffer.mem, i * buffer.frame_stride + pass * buffer.pass_stride, buffer.pass_stride); - bool is_variance = (pass >= 11) && (pass <= 13); - functions.get_feature( - pass, -1, *to_pass, null_ptr, is_variance ? (1.0f / render_buffer.samples) : 1.0f); - } - render_buffer.offset += render_buffer.frame_stride; - } - - render_buffer.offset = original_offset; -} - -void DenoisingTask::write_buffer() -{ - reconstruction_state.buffer_params = make_int4(target_buffer.offset, - target_buffer.stride, - target_buffer.pass_stride, - target_buffer.denoising_clean_offset); - int num_passes = buffer.use_intensity ? 15 : 14; - for (int pass = 0; pass < num_passes; pass++) { - device_sub_ptr from_pass(buffer.mem, pass * buffer.pass_stride, buffer.pass_stride); - int out_offset = pass + target_buffer.denoising_output_offset; - functions.write_feature(out_offset, *from_pass, target_buffer.ptr); - } -} - -void DenoisingTask::construct_transform() -{ - storage.w = filter_area.z; - storage.h = filter_area.w; - - storage.transform.alloc_to_device(storage.w * storage.h * TRANSFORM_SIZE, false); - storage.rank.alloc_to_device(storage.w * storage.h, false); - - functions.construct_transform(); -} - -void DenoisingTask::reconstruct() -{ - storage.XtWX.alloc_to_device(storage.w * storage.h * XTWX_SIZE, false); - storage.XtWY.alloc_to_device(storage.w * storage.h * XTWY_SIZE, false); - storage.XtWX.zero_to_device(); - storage.XtWY.zero_to_device(); - - reconstruction_state.filter_window = rect_from_shape( - filter_area.x - rect.x, filter_area.y - rect.y, storage.w, storage.h); - int tile_coordinate_offset = filter_area.y * target_buffer.stride + filter_area.x; - reconstruction_state.buffer_params = make_int4(target_buffer.offset + tile_coordinate_offset, - target_buffer.stride, - target_buffer.pass_stride, - target_buffer.denoising_clean_offset); - reconstruction_state.source_w = rect.z - rect.x; - reconstruction_state.source_h = rect.w - rect.y; - - device_sub_ptr color_ptr(buffer.mem, 8 * buffer.pass_stride, 3 * buffer.pass_stride); - device_sub_ptr color_var_ptr(buffer.mem, 11 * buffer.pass_stride, 3 * buffer.pass_stride); - for (int f = 0; f < tile_info->num_frames; f++) { - device_ptr scale_ptr = 0; - device_sub_ptr *scale_sub_ptr = NULL; - if (tile_info->frames[f] != 0 && (tile_info->num_frames > 1)) { - scale_sub_ptr = new device_sub_ptr(buffer.mem, 14 * buffer.pass_stride, buffer.pass_stride); - scale_ptr = **scale_sub_ptr; - } - - functions.accumulate(*color_ptr, *color_var_ptr, scale_ptr, f); - delete scale_sub_ptr; - } - functions.solve(target_buffer.ptr); -} - -void DenoisingTask::run_denoising(RenderTile &tile) -{ - RenderTileNeighbors neighbors(tile); - functions.map_neighbor_tiles(neighbors); - set_render_buffer(neighbors); - - setup_denoising_buffer(); - - if (tile_info->from_render) { - prefilter_shadowing(); - prefilter_features(); - prefilter_color(); - } - else { - load_buffer(); - } - - if (do_filter) { - construct_transform(); - reconstruct(); - } - - if (do_prefilter) { - write_buffer(); - } - - functions.unmap_neighbor_tiles(neighbors); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_denoising.h b/intern/cycles/device/device_denoising.h deleted file mode 100644 index bb8bdfdd225..00000000000 --- a/intern/cycles/device/device_denoising.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEVICE_DENOISING_H__ -#define __DEVICE_DENOISING_H__ - -#include "device/device.h" - -#include "render/buffers.h" - -#include "kernel/filter/filter_defines.h" - -#include "util/util_profiling.h" - -CCL_NAMESPACE_BEGIN - -class DenoisingTask { - public: - /* Parameters of the denoising algorithm. */ - int radius; - float nlm_k_2; - float pca_threshold; - - /* Parameters of the RenderBuffers. */ - struct RenderBuffers { - int offset; - int pass_stride; - int frame_stride; - int samples; - } render_buffer; - - /* Pointer and parameters of the target buffer. */ - struct TargetBuffer { - int offset; - int stride; - int pass_stride; - int denoising_clean_offset; - int denoising_output_offset; - device_ptr ptr; - } target_buffer; - - TileInfo *tile_info; - device_vector tile_info_mem; - - ProfilingState *profiler; - - int4 rect; - int4 filter_area; - - bool do_prefilter; - bool do_filter; - - struct DeviceFunctions { - function - non_local_means; - function - accumulate; - function solve; - function construct_transform; - - function - combine_halves; - function - divide_shadow; - function - get_feature; - function - detect_outliers; - function write_feature; - function map_neighbor_tiles; - function unmap_neighbor_tiles; - } functions; - - /* Stores state of the current Reconstruction operation, - * which is accessed by the device in order to perform the operation. */ - struct ReconstructionState { - int4 filter_window; - int4 buffer_params; - - int source_w; - int source_h; - } reconstruction_state; - - /* Stores state of the current NLM operation, - * which is accessed by the device in order to perform the operation. */ - struct NLMState { - int r; /* Search radius of the filter. */ - int f; /* Patch size of the filter. */ - float a; /* Variance compensation factor in the MSE estimation. */ - float k_2; /* Squared value of the k parameter of the filter. */ - bool is_color; - - void set_parameters(int r_, int f_, float a_, float k_2_, bool is_color_) - { - r = r_; - f = f_; - a = a_, k_2 = k_2_; - is_color = is_color_; - } - } nlm_state; - - struct Storage { - device_only_memory transform; - device_only_memory rank; - device_only_memory XtWX; - device_only_memory XtWY; - int w; - int h; - - Storage(Device *device) - : transform(device, "denoising transform"), - rank(device, "denoising rank"), - XtWX(device, "denoising XtWX"), - XtWY(device, "denoising XtWY") - { - } - } storage; - - DenoisingTask(Device *device, const DeviceTask &task); - ~DenoisingTask(); - - void run_denoising(RenderTile &tile); - - struct DenoiseBuffers { - int pass_stride; - int passes; - int stride; - int h; - int width; - int frame_stride; - device_only_memory mem; - device_only_memory temporary_mem; - bool use_time; - bool use_intensity; - - bool gpu_temporary_mem; - - DenoiseBuffers(Device *device) - : mem(device, "denoising pixel buffer"), - temporary_mem(device, "denoising temporary mem", true) - { - } - } buffer; - - protected: - Device *device; - - void set_render_buffer(RenderTileNeighbors &neighbors); - void setup_denoising_buffer(); - void prefilter_shadowing(); - void prefilter_features(); - void prefilter_color(); - void construct_transform(); - void reconstruct(); - - void load_buffer(); - void write_buffer(); -}; - -CCL_NAMESPACE_END - -#endif /* __DEVICE_DENOISING_H__ */ diff --git a/intern/cycles/kernel/kernels/opencl/kernel_path_init.cl b/intern/cycles/device/device_graphics_interop.cpp similarity index 66% rename from intern/cycles/kernel/kernels/opencl/kernel_path_init.cl rename to intern/cycles/device/device_graphics_interop.cpp index fa210e747c0..a80a236759f 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_path_init.cl +++ b/intern/cycles/device/device_graphics_interop.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,8 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_path_init.h" +#include "device/device_graphics_interop.h" -#define KERNEL_NAME path_init -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME +CCL_NAMESPACE_BEGIN +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_graphics_interop.h b/intern/cycles/device/device_graphics_interop.h new file mode 100644 index 00000000000..671b1c189d7 --- /dev/null +++ b/intern/cycles/device/device_graphics_interop.h @@ -0,0 +1,55 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/util_types.h" + +CCL_NAMESPACE_BEGIN + +/* Information about interoperability destination. + * Is provided by the GPUDisplay. */ +class DeviceGraphicsInteropDestination { + public: + /* Dimensions of the buffer, in pixels. */ + int buffer_width = 0; + int buffer_height = 0; + + /* OpenGL pixel buffer object. */ + int opengl_pbo_id = 0; + + /* Clear the entire destination before doing partial write to it. */ + bool need_clear = false; +}; + +/* Device-side graphics interoperability support. + * + * Takes care of holding all the handlers needed by the device to implement interoperability with + * the graphics library. */ +class DeviceGraphicsInterop { + public: + DeviceGraphicsInterop() = default; + virtual ~DeviceGraphicsInterop() = default; + + /* Update this device-side graphics interoperability object with the given destination resource + * information. */ + virtual void set_destination(const DeviceGraphicsInteropDestination &destination) = 0; + + virtual device_ptr map() = 0; + virtual void unmap() = 0; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_intern.h b/intern/cycles/device/device_intern.h deleted file mode 100644 index ecc79c5d7ee..00000000000 --- a/intern/cycles/device/device_intern.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEVICE_INTERN_H__ -#define __DEVICE_INTERN_H__ - -#include "util/util_string.h" -#include "util/util_vector.h" - -CCL_NAMESPACE_BEGIN - -class Device; -class DeviceInfo; -class Profiler; -class Stats; - -Device *device_cpu_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); -bool device_opencl_init(); -Device *device_opencl_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); -bool device_opencl_compile_kernel(const vector ¶meters); -bool device_cuda_init(); -Device *device_cuda_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); -bool device_optix_init(); -Device *device_optix_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); -Device *device_dummy_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); - -Device *device_network_create(DeviceInfo &info, - Stats &stats, - Profiler &profiler, - const char *address); -Device *device_multi_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); - -void device_cpu_info(vector &devices); -void device_opencl_info(vector &devices); -void device_cuda_info(vector &devices); -void device_optix_info(const vector &cuda_devices, vector &devices); -void device_network_info(vector &devices); - -string device_cpu_capabilities(); -string device_opencl_capabilities(); -string device_cuda_capabilities(); - -CCL_NAMESPACE_END - -#endif /* __DEVICE_INTERN_H__ */ diff --git a/intern/cycles/device/device_kernel.cpp b/intern/cycles/device/device_kernel.cpp new file mode 100644 index 00000000000..ceaddee4756 --- /dev/null +++ b/intern/cycles/device/device_kernel.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/device_kernel.h" + +#include "util/util_logging.h" + +CCL_NAMESPACE_BEGIN + +const char *device_kernel_as_string(DeviceKernel kernel) +{ + switch (kernel) { + /* Integrator. */ + case DEVICE_KERNEL_INTEGRATOR_INIT_FROM_CAMERA: + return "integrator_init_from_camera"; + case DEVICE_KERNEL_INTEGRATOR_INIT_FROM_BAKE: + return "integrator_init_from_bake"; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: + return "integrator_intersect_closest"; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: + return "integrator_intersect_shadow"; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE: + return "integrator_intersect_subsurface"; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: + return "integrator_intersect_volume_stack"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND: + return "integrator_shade_background"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT: + return "integrator_shade_light"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: + return "integrator_shade_shadow"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE: + return "integrator_shade_surface"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE: + return "integrator_shade_surface_raytrace"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME: + return "integrator_shade_volume"; + case DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL: + return "integrator_megakernel"; + case DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY: + return "integrator_queued_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY: + return "integrator_queued_shadow_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY: + return "integrator_active_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY: + return "integrator_terminated_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY: + return "integrator_sorted_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY: + return "integrator_compact_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES: + return "integrator_compact_states"; + case DEVICE_KERNEL_INTEGRATOR_RESET: + return "integrator_reset"; + case DEVICE_KERNEL_INTEGRATOR_SHADOW_CATCHER_COUNT_POSSIBLE_SPLITS: + return "integrator_shadow_catcher_count_possible_splits"; + + /* Shader evaluation. */ + case DEVICE_KERNEL_SHADER_EVAL_DISPLACE: + return "shader_eval_displace"; + case DEVICE_KERNEL_SHADER_EVAL_BACKGROUND: + return "shader_eval_background"; + + /* Film. */ + +#define FILM_CONVERT_KERNEL_AS_STRING(variant, variant_lowercase) \ + case DEVICE_KERNEL_FILM_CONVERT_##variant: \ + return "film_convert_" #variant_lowercase; \ + case DEVICE_KERNEL_FILM_CONVERT_##variant##_HALF_RGBA: \ + return "film_convert_" #variant_lowercase "_half_rgba"; + + FILM_CONVERT_KERNEL_AS_STRING(DEPTH, depth) + FILM_CONVERT_KERNEL_AS_STRING(MIST, mist) + FILM_CONVERT_KERNEL_AS_STRING(SAMPLE_COUNT, sample_count) + FILM_CONVERT_KERNEL_AS_STRING(FLOAT, float) + FILM_CONVERT_KERNEL_AS_STRING(LIGHT_PATH, light_path) + FILM_CONVERT_KERNEL_AS_STRING(FLOAT3, float3) + FILM_CONVERT_KERNEL_AS_STRING(MOTION, motion) + FILM_CONVERT_KERNEL_AS_STRING(CRYPTOMATTE, cryptomatte) + FILM_CONVERT_KERNEL_AS_STRING(SHADOW_CATCHER, shadow_catcher) + FILM_CONVERT_KERNEL_AS_STRING(SHADOW_CATCHER_MATTE_WITH_SHADOW, + shadow_catcher_matte_with_shadow) + FILM_CONVERT_KERNEL_AS_STRING(COMBINED, combined) + FILM_CONVERT_KERNEL_AS_STRING(FLOAT4, float4) + +#undef FILM_CONVERT_KERNEL_AS_STRING + + /* Adaptive sampling. */ + case DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_CHECK: + return "adaptive_sampling_convergence_check"; + case DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_X: + return "adaptive_sampling_filter_x"; + case DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_Y: + return "adaptive_sampling_filter_y"; + + /* Denoising. */ + case DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS: + return "filter_guiding_preprocess"; + case DEVICE_KERNEL_FILTER_GUIDING_SET_FAKE_ALBEDO: + return "filter_guiding_set_fake_albedo"; + case DEVICE_KERNEL_FILTER_COLOR_PREPROCESS: + return "filter_color_preprocess"; + case DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS: + return "filter_color_postprocess"; + + /* Cryptomatte. */ + case DEVICE_KERNEL_CRYPTOMATTE_POSTPROCESS: + return "cryptomatte_postprocess"; + + /* Generic */ + case DEVICE_KERNEL_PREFIX_SUM: + return "prefix_sum"; + + case DEVICE_KERNEL_NUM: + break; + }; + LOG(FATAL) << "Unhandled kernel " << static_cast(kernel) << ", should never happen."; + return "UNKNOWN"; +} + +std::ostream &operator<<(std::ostream &os, DeviceKernel kernel) +{ + os << device_kernel_as_string(kernel); + return os; +} + +string device_kernel_mask_as_string(DeviceKernelMask mask) +{ + string str; + + for (uint64_t i = 0; i < sizeof(DeviceKernelMask) * 8; i++) { + if (mask & (uint64_t(1) << i)) { + if (!str.empty()) { + str += " "; + } + str += device_kernel_as_string((DeviceKernel)i); + } + } + + return str; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_holdout_emission_blurring_pathtermination_ao.cl b/intern/cycles/device/device_kernel.h similarity index 58% rename from intern/cycles/kernel/kernels/opencl/kernel_holdout_emission_blurring_pathtermination_ao.cl rename to intern/cycles/device/device_kernel.h index 9e1e57beba6..83d959ca87b 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_holdout_emission_blurring_pathtermination_ao.cl +++ b/intern/cycles/device/device_kernel.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,20 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h" +#pragma once -#define KERNEL_NAME holdout_emission_blurring_pathtermination_ao -#define LOCALS_TYPE BackgroundAOLocals -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE +#include "kernel/kernel_types.h" +#include "util/util_string.h" + +#include // NOLINT + +CCL_NAMESPACE_BEGIN + +const char *device_kernel_as_string(DeviceKernel kernel); +std::ostream &operator<<(std::ostream &os, DeviceKernel kernel); + +typedef uint64_t DeviceKernelMask; +string device_kernel_mask_as_string(DeviceKernelMask mask); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_memory.cpp b/intern/cycles/device/device_memory.cpp index 80a05fc32fe..c4d45829b83 100644 --- a/intern/cycles/device/device_memory.cpp +++ b/intern/cycles/device/device_memory.cpp @@ -23,7 +23,7 @@ CCL_NAMESPACE_BEGIN device_memory::device_memory(Device *device, const char *name, MemoryType type) : data_type(device_type_traits::data_type), - data_elements(device_type_traits::num_elements), + data_elements(device_type_traits::num_elements_cpu), data_size(0), device_size(0), data_width(0), @@ -149,6 +149,11 @@ void device_memory::device_zero() } } +bool device_memory::device_is_cpu() +{ + return (device->info.type == DEVICE_CPU); +} + void device_memory::swap_device(Device *new_device, size_t new_device_size, device_ptr new_device_ptr) diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/device_memory.h index 80f4d7b0468..c51594b8580 100644 --- a/intern/cycles/device/device_memory.h +++ b/intern/cycles/device/device_memory.h @@ -38,7 +38,6 @@ enum MemoryType { MEM_DEVICE_ONLY, MEM_GLOBAL, MEM_TEXTURE, - MEM_PIXELS }; /* Supported Data Types */ @@ -54,7 +53,7 @@ enum DataType { TYPE_UINT64, }; -static inline size_t datatype_size(DataType datatype) +static constexpr size_t datatype_size(DataType datatype) { switch (datatype) { case TYPE_UNKNOWN: @@ -82,112 +81,155 @@ static inline size_t datatype_size(DataType datatype) template struct device_type_traits { static const DataType data_type = TYPE_UNKNOWN; - static const int num_elements = sizeof(T); + static const int num_elements_cpu = sizeof(T); + static const int num_elements_gpu = sizeof(T); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements = 1; + static const int num_elements_cpu = 1; + static const int num_elements_gpu = 1; + static_assert(sizeof(uchar) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements = 2; + static const int num_elements_cpu = 2; + static const int num_elements_gpu = 2; + static_assert(sizeof(uchar2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements = 3; + static const int num_elements_cpu = 3; + static const int num_elements_gpu = 3; + static_assert(sizeof(uchar3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements = 4; + static const int num_elements_cpu = 4; + static const int num_elements_gpu = 4; + static_assert(sizeof(uchar4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements = 1; + static const int num_elements_cpu = 1; + static const int num_elements_gpu = 1; + static_assert(sizeof(uint) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements = 2; + static const int num_elements_cpu = 2; + static const int num_elements_gpu = 2; + static_assert(sizeof(uint2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements = 3; + static const int num_elements_cpu = 3; + static const int num_elements_gpu = 3; + static_assert(sizeof(uint3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements = 4; + static const int num_elements_cpu = 4; + static const int num_elements_gpu = 4; + static_assert(sizeof(uint4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements = 1; + static const int num_elements_cpu = 1; + static const int num_elements_gpu = 1; + static_assert(sizeof(int) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements = 2; + static const int num_elements_cpu = 2; + static const int num_elements_gpu = 2; + static_assert(sizeof(int2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements = 3; + static const int num_elements_cpu = 4; + static const int num_elements_gpu = 3; + static_assert(sizeof(int3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements = 4; + static const int num_elements_cpu = 4; + static const int num_elements_gpu = 4; + static_assert(sizeof(int4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements = 1; + static const int num_elements_cpu = 1; + static const int num_elements_gpu = 1; + static_assert(sizeof(float) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements = 2; + static const int num_elements_cpu = 2; + static const int num_elements_gpu = 2; + static_assert(sizeof(float2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements = 4; + static const int num_elements_cpu = 4; + static const int num_elements_gpu = 3; + static_assert(sizeof(float3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements = 4; + static const int num_elements_cpu = 4; + static const int num_elements_gpu = 4; + static_assert(sizeof(float4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_HALF; - static const int num_elements = 1; + static const int num_elements_cpu = 1; + static const int num_elements_gpu = 1; + static_assert(sizeof(half) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT16; - static const int num_elements = 4; + static const int num_elements_cpu = 4; + static const int num_elements_gpu = 4; + static_assert(sizeof(ushort4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT16; - static const int num_elements = 1; + static const int num_elements_cpu = 1; + static const int num_elements_gpu = 1; + static_assert(sizeof(uint16_t) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_HALF; - static const int num_elements = 4; + static const int num_elements_cpu = 4; + static const int num_elements_gpu = 4; + static_assert(sizeof(half4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT64; - static const int num_elements = 1; + static const int num_elements_cpu = 1; + static const int num_elements_gpu = 1; + static_assert(sizeof(uint64_t) == num_elements_cpu * datatype_size(data_type)); }; /* Device Memory @@ -257,6 +299,8 @@ class device_memory { void device_copy_from(int y, int w, int h, int elem); void device_zero(); + bool device_is_cpu(); + device_ptr original_device_ptr; size_t original_device_size; Device *original_device; @@ -275,7 +319,9 @@ template class device_only_memory : public device_memory { : device_memory(device, name, allow_host_memory_fallback ? MEM_READ_WRITE : MEM_DEVICE_ONLY) { data_type = device_type_traits::data_type; - data_elements = max(device_type_traits::num_elements, 1); + data_elements = max(device_is_cpu() ? device_type_traits::num_elements_cpu : + device_type_traits::num_elements_gpu, + 1); } device_only_memory(device_only_memory &&other) noexcept : device_memory(std::move(other)) @@ -331,11 +377,15 @@ template class device_only_memory : public device_memory { template class device_vector : public device_memory { public: + /* Can only use this for types that have the same size on CPU and GPU. */ + static_assert(device_type_traits::num_elements_cpu == + device_type_traits::num_elements_gpu); + device_vector(Device *device, const char *name, MemoryType type) : device_memory(device, name, type) { data_type = device_type_traits::data_type; - data_elements = device_type_traits::num_elements; + data_elements = device_type_traits::num_elements_cpu; modified = true; need_realloc_ = true; @@ -477,6 +527,11 @@ template class device_vector : public device_memory { return (T *)host_pointer; } + const T *data() const + { + return (T *)host_pointer; + } + T &operator[](size_t i) { assert(i < data_size); @@ -507,7 +562,7 @@ template class device_vector : public device_memory { void copy_from_device() { - device_copy_from(0, data_width, data_height, sizeof(T)); + device_copy_from(0, data_width, (data_height == 0) ? 1 : data_height, sizeof(T)); } void copy_from_device(int y, int w, int h) @@ -535,33 +590,6 @@ template class device_vector : public device_memory { } }; -/* Pixel Memory - * - * Device memory to efficiently draw as pixels to the screen in interactive - * rendering. Only copying pixels from the device is supported, not copying to. */ - -template class device_pixels : public device_vector { - public: - device_pixels(Device *device, const char *name) : device_vector(device, name, MEM_PIXELS) - { - } - - void alloc_to_device(size_t width, size_t height, size_t depth = 0) - { - device_vector::alloc(width, height, depth); - - if (!device_memory::device_pointer) { - device_memory::device_alloc(); - } - } - - T *copy_from_device(int y, int w, int h) - { - device_memory::device_copy_from(y, w, h, sizeof(T)); - return device_vector::data(); - } -}; - /* Device Sub Memory * * Pointer into existing memory. It is not allocated separately, but created diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp deleted file mode 100644 index 85ffa5fcd52..00000000000 --- a/intern/cycles/device/device_multi.cpp +++ /dev/null @@ -1,826 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "bvh/bvh_multi.h" - -#include "device/device.h" -#include "device/device_intern.h" -#include "device/device_network.h" - -#include "render/buffers.h" -#include "render/geometry.h" - -#include "util/util_foreach.h" -#include "util/util_list.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_time.h" - -CCL_NAMESPACE_BEGIN - -class MultiDevice : public Device { - public: - struct SubDevice { - Stats stats; - Device *device; - map ptr_map; - int peer_island_index = -1; - }; - - list devices, denoising_devices; - device_ptr unique_key; - vector> peer_islands; - bool use_denoising; - bool matching_rendering_and_denoising_devices; - - MultiDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background_) - : Device(info, stats, profiler, background_), - unique_key(1), - use_denoising(!info.denoising_devices.empty()) - { - foreach (DeviceInfo &subinfo, info.multi_devices) { - /* Always add CPU devices at the back since GPU devices can change - * host memory pointers, which CPU uses as device pointer. */ - SubDevice *sub; - if (subinfo.type == DEVICE_CPU) { - devices.emplace_back(); - sub = &devices.back(); - } - else { - devices.emplace_front(); - sub = &devices.front(); - } - - /* The pointer to 'sub->stats' will stay valid even after new devices - * are added, since 'devices' is a linked list. */ - sub->device = Device::create(subinfo, sub->stats, profiler, background); - } - - foreach (DeviceInfo &subinfo, info.denoising_devices) { - denoising_devices.emplace_front(); - SubDevice *sub = &denoising_devices.front(); - - sub->device = Device::create(subinfo, sub->stats, profiler, background); - } - - /* Build a list of peer islands for the available render devices */ - foreach (SubDevice &sub, devices) { - /* First ensure that every device is in at least once peer island */ - if (sub.peer_island_index < 0) { - peer_islands.emplace_back(); - sub.peer_island_index = (int)peer_islands.size() - 1; - peer_islands[sub.peer_island_index].push_back(&sub); - } - - if (!info.has_peer_memory) { - continue; - } - - /* Second check peer access between devices and fill up the islands accordingly */ - foreach (SubDevice &peer_sub, devices) { - if (peer_sub.peer_island_index < 0 && - peer_sub.device->info.type == sub.device->info.type && - peer_sub.device->check_peer_access(sub.device)) { - peer_sub.peer_island_index = sub.peer_island_index; - peer_islands[sub.peer_island_index].push_back(&peer_sub); - } - } - } - - /* Try to re-use memory when denoising and render devices use the same physical devices - * (e.g. OptiX denoising and CUDA rendering device pointing to the same GPU). - * Ordering has to match as well, so that 'DeviceTask::split' behaves consistent. */ - matching_rendering_and_denoising_devices = denoising_devices.empty() || - (devices.size() == denoising_devices.size()); - if (matching_rendering_and_denoising_devices) { - for (list::iterator device_it = devices.begin(), - denoising_device_it = denoising_devices.begin(); - device_it != devices.end() && denoising_device_it != denoising_devices.end(); - ++device_it, ++denoising_device_it) { - const DeviceInfo &info = device_it->device->info; - const DeviceInfo &denoising_info = denoising_device_it->device->info; - if ((info.type != DEVICE_CUDA && info.type != DEVICE_OPTIX) || - (denoising_info.type != DEVICE_CUDA && denoising_info.type != DEVICE_OPTIX) || - info.num != denoising_info.num) { - matching_rendering_and_denoising_devices = false; - break; - } - } - } - -#ifdef WITH_NETWORK - /* try to add network devices */ - ServerDiscovery discovery(true); - time_sleep(1.0); - - vector servers = discovery.get_server_list(); - - foreach (string &server, servers) { - Device *device = device_network_create(info, stats, profiler, server.c_str()); - if (device) - devices.push_back(SubDevice(device)); - } -#endif - } - - ~MultiDevice() - { - foreach (SubDevice &sub, devices) - delete sub.device; - foreach (SubDevice &sub, denoising_devices) - delete sub.device; - } - - const string &error_message() override - { - error_msg.clear(); - - foreach (SubDevice &sub, devices) - error_msg += sub.device->error_message(); - foreach (SubDevice &sub, denoising_devices) - error_msg += sub.device->error_message(); - - return error_msg; - } - - virtual bool show_samples() const override - { - if (devices.size() > 1) { - return false; - } - return devices.front().device->show_samples(); - } - - virtual BVHLayoutMask get_bvh_layout_mask() const override - { - BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL; - BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE; - foreach (const SubDevice &sub_device, devices) { - BVHLayoutMask device_bvh_layout_mask = sub_device.device->get_bvh_layout_mask(); - bvh_layout_mask &= device_bvh_layout_mask; - bvh_layout_mask_all |= device_bvh_layout_mask; - } - - /* With multiple OptiX devices, every device needs its own acceleration structure */ - if (bvh_layout_mask == BVH_LAYOUT_OPTIX) { - return BVH_LAYOUT_MULTI_OPTIX; - } - - /* When devices do not share a common BVH layout, fall back to creating one for each */ - const BVHLayoutMask BVH_LAYOUT_OPTIX_EMBREE = (BVH_LAYOUT_OPTIX | BVH_LAYOUT_EMBREE); - if ((bvh_layout_mask_all & BVH_LAYOUT_OPTIX_EMBREE) == BVH_LAYOUT_OPTIX_EMBREE) { - return BVH_LAYOUT_MULTI_OPTIX_EMBREE; - } - - return bvh_layout_mask; - } - - bool load_kernels(const DeviceRequestedFeatures &requested_features) override - { - foreach (SubDevice &sub, devices) - if (!sub.device->load_kernels(requested_features)) - return false; - - use_denoising = requested_features.use_denoising; - if (requested_features.use_denoising) { - /* Only need denoising feature, everything else is unused. */ - DeviceRequestedFeatures denoising_features; - denoising_features.use_denoising = true; - foreach (SubDevice &sub, denoising_devices) - if (!sub.device->load_kernels(denoising_features)) - return false; - } - - return true; - } - - bool wait_for_availability(const DeviceRequestedFeatures &requested_features) override - { - foreach (SubDevice &sub, devices) - if (!sub.device->wait_for_availability(requested_features)) - return false; - - if (requested_features.use_denoising) { - foreach (SubDevice &sub, denoising_devices) - if (!sub.device->wait_for_availability(requested_features)) - return false; - } - - return true; - } - - DeviceKernelStatus get_active_kernel_switch_state() override - { - DeviceKernelStatus result = DEVICE_KERNEL_USING_FEATURE_KERNEL; - - foreach (SubDevice &sub, devices) { - DeviceKernelStatus subresult = sub.device->get_active_kernel_switch_state(); - switch (subresult) { - case DEVICE_KERNEL_FEATURE_KERNEL_INVALID: - case DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE: - return subresult; - - case DEVICE_KERNEL_USING_FEATURE_KERNEL: - case DEVICE_KERNEL_UNKNOWN: - break; - } - } - - return result; - } - - void build_bvh(BVH *bvh, Progress &progress, bool refit) override - { - /* Try to build and share a single acceleration structure, if possible */ - if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2 || bvh->params.bvh_layout == BVH_LAYOUT_EMBREE) { - devices.back().device->build_bvh(bvh, progress, refit); - return; - } - - assert(bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX || - bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE); - - BVHMulti *const bvh_multi = static_cast(bvh); - bvh_multi->sub_bvhs.resize(devices.size()); - - vector geom_bvhs; - geom_bvhs.reserve(bvh->geometry.size()); - foreach (Geometry *geom, bvh->geometry) { - geom_bvhs.push_back(static_cast(geom->bvh)); - } - - /* Broadcast acceleration structure build to all render devices */ - size_t i = 0; - foreach (SubDevice &sub, devices) { - /* Change geometry BVH pointers to the sub BVH */ - for (size_t k = 0; k < bvh->geometry.size(); ++k) { - bvh->geometry[k]->bvh = geom_bvhs[k]->sub_bvhs[i]; - } - - if (!bvh_multi->sub_bvhs[i]) { - BVHParams params = bvh->params; - if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX) - params.bvh_layout = BVH_LAYOUT_OPTIX; - else if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) - params.bvh_layout = sub.device->info.type == DEVICE_OPTIX ? BVH_LAYOUT_OPTIX : - BVH_LAYOUT_EMBREE; - - /* Skip building a bottom level acceleration structure for non-instanced geometry on Embree - * (since they are put into the top level directly, see bvh_embree.cpp) */ - if (!params.top_level && params.bvh_layout == BVH_LAYOUT_EMBREE && - !bvh->geometry[0]->is_instanced()) { - i++; - continue; - } - - bvh_multi->sub_bvhs[i] = BVH::create(params, bvh->geometry, bvh->objects, sub.device); - } - - sub.device->build_bvh(bvh_multi->sub_bvhs[i], progress, refit); - i++; - } - - /* Change geometry BVH pointers back to the multi BVH. */ - for (size_t k = 0; k < bvh->geometry.size(); ++k) { - bvh->geometry[k]->bvh = geom_bvhs[k]; - } - } - - virtual void *osl_memory() override - { - if (devices.size() > 1) { - return NULL; - } - return devices.front().device->osl_memory(); - } - - bool is_resident(device_ptr key, Device *sub_device) override - { - foreach (SubDevice &sub, devices) { - if (sub.device == sub_device) { - return find_matching_mem_device(key, sub)->device == sub_device; - } - } - return false; - } - - SubDevice *find_matching_mem_device(device_ptr key, SubDevice &sub) - { - assert(key != 0 && (sub.peer_island_index >= 0 || sub.ptr_map.find(key) != sub.ptr_map.end())); - - /* Get the memory owner of this key (first try current device, then peer devices) */ - SubDevice *owner_sub = ⊂ - if (owner_sub->ptr_map.find(key) == owner_sub->ptr_map.end()) { - foreach (SubDevice *island_sub, peer_islands[sub.peer_island_index]) { - if (island_sub != owner_sub && - island_sub->ptr_map.find(key) != island_sub->ptr_map.end()) { - owner_sub = island_sub; - } - } - } - return owner_sub; - } - - SubDevice *find_suitable_mem_device(device_ptr key, const vector &island) - { - assert(!island.empty()); - - /* Get the memory owner of this key or the device with the lowest memory usage when new */ - SubDevice *owner_sub = island.front(); - foreach (SubDevice *island_sub, island) { - if (key ? (island_sub->ptr_map.find(key) != island_sub->ptr_map.end()) : - (island_sub->device->stats.mem_used < owner_sub->device->stats.mem_used)) { - owner_sub = island_sub; - } - } - return owner_sub; - } - - inline device_ptr find_matching_mem(device_ptr key, SubDevice &sub) - { - return find_matching_mem_device(key, sub)->ptr_map[key]; - } - - void mem_alloc(device_memory &mem) override - { - device_ptr key = unique_key++; - - if (mem.type == MEM_PIXELS) { - /* Always allocate pixels memory on all devices - * This is necessary to ensure PBOs are registered everywhere, which FILM_CONVERT uses */ - foreach (SubDevice &sub, devices) { - mem.device = sub.device; - mem.device_pointer = 0; - mem.device_size = 0; - - sub.device->mem_alloc(mem); - sub.ptr_map[key] = mem.device_pointer; - } - } - else { - assert(mem.type == MEM_READ_ONLY || mem.type == MEM_READ_WRITE || - mem.type == MEM_DEVICE_ONLY); - /* The remaining memory types can be distributed across devices */ - foreach (const vector &island, peer_islands) { - SubDevice *owner_sub = find_suitable_mem_device(key, island); - mem.device = owner_sub->device; - mem.device_pointer = 0; - mem.device_size = 0; - - owner_sub->device->mem_alloc(mem); - owner_sub->ptr_map[key] = mem.device_pointer; - } - } - - mem.device = this; - mem.device_pointer = key; - stats.mem_alloc(mem.device_size); - } - - void mem_copy_to(device_memory &mem) override - { - device_ptr existing_key = mem.device_pointer; - device_ptr key = (existing_key) ? existing_key : unique_key++; - size_t existing_size = mem.device_size; - - /* The tile buffers are allocated on each device (see below), so copy to all of them */ - if (strcmp(mem.name, "RenderBuffers") == 0 && use_denoising) { - foreach (SubDevice &sub, devices) { - mem.device = sub.device; - mem.device_pointer = (existing_key) ? sub.ptr_map[existing_key] : 0; - mem.device_size = existing_size; - - sub.device->mem_copy_to(mem); - sub.ptr_map[key] = mem.device_pointer; - } - } - else { - foreach (const vector &island, peer_islands) { - SubDevice *owner_sub = find_suitable_mem_device(existing_key, island); - mem.device = owner_sub->device; - mem.device_pointer = (existing_key) ? owner_sub->ptr_map[existing_key] : 0; - mem.device_size = existing_size; - - owner_sub->device->mem_copy_to(mem); - owner_sub->ptr_map[key] = mem.device_pointer; - - if (mem.type == MEM_GLOBAL || mem.type == MEM_TEXTURE) { - /* Need to create texture objects and update pointer in kernel globals on all devices */ - foreach (SubDevice *island_sub, island) { - if (island_sub != owner_sub) { - island_sub->device->mem_copy_to(mem); - } - } - } - } - } - - mem.device = this; - mem.device_pointer = key; - stats.mem_alloc(mem.device_size - existing_size); - } - - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override - { - device_ptr key = mem.device_pointer; - int i = 0, sub_h = h / devices.size(); - - foreach (SubDevice &sub, devices) { - int sy = y + i * sub_h; - int sh = (i == (int)devices.size() - 1) ? h - sub_h * i : sub_h; - - SubDevice *owner_sub = find_matching_mem_device(key, sub); - mem.device = owner_sub->device; - mem.device_pointer = owner_sub->ptr_map[key]; - - owner_sub->device->mem_copy_from(mem, sy, w, sh, elem); - i++; - } - - mem.device = this; - mem.device_pointer = key; - } - - void mem_zero(device_memory &mem) override - { - device_ptr existing_key = mem.device_pointer; - device_ptr key = (existing_key) ? existing_key : unique_key++; - size_t existing_size = mem.device_size; - - /* This is a hack to only allocate the tile buffers on denoising devices - * Similarly the tile buffers also need to be allocated separately on all devices so any - * overlap rendered for denoising does not interfere with each other */ - if (strcmp(mem.name, "RenderBuffers") == 0 && use_denoising) { - vector device_pointers; - device_pointers.reserve(devices.size()); - - foreach (SubDevice &sub, devices) { - mem.device = sub.device; - mem.device_pointer = (existing_key) ? sub.ptr_map[existing_key] : 0; - mem.device_size = existing_size; - - sub.device->mem_zero(mem); - sub.ptr_map[key] = mem.device_pointer; - - device_pointers.push_back(mem.device_pointer); - } - foreach (SubDevice &sub, denoising_devices) { - if (matching_rendering_and_denoising_devices) { - sub.ptr_map[key] = device_pointers.front(); - device_pointers.erase(device_pointers.begin()); - } - else { - mem.device = sub.device; - mem.device_pointer = (existing_key) ? sub.ptr_map[existing_key] : 0; - mem.device_size = existing_size; - - sub.device->mem_zero(mem); - sub.ptr_map[key] = mem.device_pointer; - } - } - } - else { - foreach (const vector &island, peer_islands) { - SubDevice *owner_sub = find_suitable_mem_device(existing_key, island); - mem.device = owner_sub->device; - mem.device_pointer = (existing_key) ? owner_sub->ptr_map[existing_key] : 0; - mem.device_size = existing_size; - - owner_sub->device->mem_zero(mem); - owner_sub->ptr_map[key] = mem.device_pointer; - } - } - - mem.device = this; - mem.device_pointer = key; - stats.mem_alloc(mem.device_size - existing_size); - } - - void mem_free(device_memory &mem) override - { - device_ptr key = mem.device_pointer; - size_t existing_size = mem.device_size; - - /* Free memory that was allocated for all devices (see above) on each device */ - if (mem.type == MEM_PIXELS || (strcmp(mem.name, "RenderBuffers") == 0 && use_denoising)) { - foreach (SubDevice &sub, devices) { - mem.device = sub.device; - mem.device_pointer = sub.ptr_map[key]; - mem.device_size = existing_size; - - sub.device->mem_free(mem); - sub.ptr_map.erase(sub.ptr_map.find(key)); - } - foreach (SubDevice &sub, denoising_devices) { - if (matching_rendering_and_denoising_devices) { - sub.ptr_map.erase(key); - } - else { - mem.device = sub.device; - mem.device_pointer = sub.ptr_map[key]; - mem.device_size = existing_size; - - sub.device->mem_free(mem); - sub.ptr_map.erase(sub.ptr_map.find(key)); - } - } - } - else { - foreach (const vector &island, peer_islands) { - SubDevice *owner_sub = find_matching_mem_device(key, *island.front()); - mem.device = owner_sub->device; - mem.device_pointer = owner_sub->ptr_map[key]; - mem.device_size = existing_size; - - owner_sub->device->mem_free(mem); - owner_sub->ptr_map.erase(owner_sub->ptr_map.find(key)); - - if (mem.type == MEM_TEXTURE) { - /* Free texture objects on all devices */ - foreach (SubDevice *island_sub, island) { - if (island_sub != owner_sub) { - island_sub->device->mem_free(mem); - } - } - } - } - } - - mem.device = this; - mem.device_pointer = 0; - mem.device_size = 0; - stats.mem_free(existing_size); - } - - void const_copy_to(const char *name, void *host, size_t size) override - { - foreach (SubDevice &sub, devices) - sub.device->const_copy_to(name, host, size); - } - - void draw_pixels(device_memory &rgba, - int y, - int w, - int h, - int width, - int height, - int dx, - int dy, - int dw, - int dh, - bool transparent, - const DeviceDrawParams &draw_params) override - { - assert(rgba.type == MEM_PIXELS); - - device_ptr key = rgba.device_pointer; - int i = 0, sub_h = h / devices.size(); - int sub_height = height / devices.size(); - - foreach (SubDevice &sub, devices) { - int sy = y + i * sub_h; - int sh = (i == (int)devices.size() - 1) ? h - sub_h * i : sub_h; - int sheight = (i == (int)devices.size() - 1) ? height - sub_height * i : sub_height; - int sdy = dy + i * sub_height; - /* adjust math for w/width */ - - rgba.device_pointer = sub.ptr_map[key]; - sub.device->draw_pixels( - rgba, sy, w, sh, width, sheight, dx, sdy, dw, dh, transparent, draw_params); - i++; - } - - rgba.device_pointer = key; - } - - void map_tile(Device *sub_device, RenderTile &tile) override - { - if (!tile.buffer) { - return; - } - - foreach (SubDevice &sub, devices) { - if (sub.device == sub_device) { - tile.buffer = find_matching_mem(tile.buffer, sub); - return; - } - } - - foreach (SubDevice &sub, denoising_devices) { - if (sub.device == sub_device) { - tile.buffer = sub.ptr_map[tile.buffer]; - return; - } - } - } - - int device_number(Device *sub_device) override - { - int i = 0; - - foreach (SubDevice &sub, devices) { - if (sub.device == sub_device) - return i; - i++; - } - - foreach (SubDevice &sub, denoising_devices) { - if (sub.device == sub_device) - return i; - i++; - } - - return -1; - } - - void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override - { - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - RenderTile &tile = neighbors.tiles[i]; - - if (!tile.buffers) { - continue; - } - - device_vector &mem = tile.buffers->buffer; - tile.buffer = mem.device_pointer; - - if (mem.device == this && matching_rendering_and_denoising_devices) { - /* Skip unnecessary copies in viewport mode (buffer covers the - * whole image), but still need to fix up the tile device pointer. */ - map_tile(sub_device, tile); - continue; - } - - /* If the tile was rendered on another device, copy its memory to - * to the current device now, for the duration of the denoising task. - * Note that this temporarily modifies the RenderBuffers and calls - * the device, so this function is not thread safe. */ - if (mem.device != sub_device) { - /* Only copy from device to host once. This is faster, but - * also required for the case where a CPU thread is denoising - * a tile rendered on the GPU. In that case we have to avoid - * overwriting the buffer being de-noised by the CPU thread. */ - if (!tile.buffers->map_neighbor_copied) { - tile.buffers->map_neighbor_copied = true; - mem.copy_from_device(); - } - - if (mem.device == this) { - /* Can re-use memory if tile is already allocated on the sub device. */ - map_tile(sub_device, tile); - mem.swap_device(sub_device, mem.device_size, tile.buffer); - } - else { - mem.swap_device(sub_device, 0, 0); - } - - mem.copy_to_device(); - - tile.buffer = mem.device_pointer; - tile.device_size = mem.device_size; - - mem.restore_device(); - } - } - } - - void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override - { - RenderTile &target_tile = neighbors.target; - device_vector &mem = target_tile.buffers->buffer; - - if (mem.device == this && matching_rendering_and_denoising_devices) { - return; - } - - /* Copy denoised result back to the host. */ - mem.swap_device(sub_device, target_tile.device_size, target_tile.buffer); - mem.copy_from_device(); - mem.restore_device(); - - /* Copy denoised result to the original device. */ - mem.copy_to_device(); - - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - RenderTile &tile = neighbors.tiles[i]; - if (!tile.buffers) { - continue; - } - - device_vector &mem = tile.buffers->buffer; - - if (mem.device != sub_device && mem.device != this) { - /* Free up memory again if it was allocated for the copy above. */ - mem.swap_device(sub_device, tile.device_size, tile.buffer); - sub_device->mem_free(mem); - mem.restore_device(); - } - } - } - - int get_split_task_count(DeviceTask &task) override - { - int total_tasks = 0; - list tasks; - task.split(tasks, devices.size()); - foreach (SubDevice &sub, devices) { - if (!tasks.empty()) { - DeviceTask subtask = tasks.front(); - tasks.pop_front(); - - total_tasks += sub.device->get_split_task_count(subtask); - } - } - return total_tasks; - } - - void task_add(DeviceTask &task) override - { - list task_devices = devices; - if (!denoising_devices.empty()) { - if (task.type == DeviceTask::DENOISE_BUFFER) { - /* Denoising tasks should be redirected to the denoising devices entirely. */ - task_devices = denoising_devices; - } - else if (task.type == DeviceTask::RENDER && (task.tile_types & RenderTile::DENOISE)) { - const uint tile_types = task.tile_types; - /* For normal rendering tasks only redirect the denoising part to the denoising devices. - * Do not need to split the task here, since they all run through 'acquire_tile'. */ - task.tile_types = RenderTile::DENOISE; - foreach (SubDevice &sub, denoising_devices) { - sub.device->task_add(task); - } - /* Rendering itself should still be executed on the rendering devices. */ - task.tile_types = tile_types ^ RenderTile::DENOISE; - } - } - - list tasks; - task.split(tasks, task_devices.size()); - - foreach (SubDevice &sub, task_devices) { - if (!tasks.empty()) { - DeviceTask subtask = tasks.front(); - tasks.pop_front(); - - if (task.buffer) - subtask.buffer = find_matching_mem(task.buffer, sub); - if (task.rgba_byte) - subtask.rgba_byte = sub.ptr_map[task.rgba_byte]; - if (task.rgba_half) - subtask.rgba_half = sub.ptr_map[task.rgba_half]; - if (task.shader_input) - subtask.shader_input = find_matching_mem(task.shader_input, sub); - if (task.shader_output) - subtask.shader_output = find_matching_mem(task.shader_output, sub); - - sub.device->task_add(subtask); - - if (task.buffers && task.buffers->buffer.device == this) { - /* Synchronize access to RenderBuffers, since 'map_neighbor_tiles' is not thread-safe. */ - sub.device->task_wait(); - } - } - } - } - - void task_wait() override - { - foreach (SubDevice &sub, devices) - sub.device->task_wait(); - foreach (SubDevice &sub, denoising_devices) - sub.device->task_wait(); - } - - void task_cancel() override - { - foreach (SubDevice &sub, devices) - sub.device->task_cancel(); - foreach (SubDevice &sub, denoising_devices) - sub.device->task_cancel(); - } -}; - -Device *device_multi_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) -{ - return new MultiDevice(info, stats, profiler, background); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_network.cpp b/intern/cycles/device/device_network.cpp deleted file mode 100644 index 8904b517e92..00000000000 --- a/intern/cycles/device/device_network.cpp +++ /dev/null @@ -1,812 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "device/device_network.h" -#include "device/device.h" -#include "device/device_intern.h" - -#include "util/util_foreach.h" -#include "util/util_logging.h" - -#if defined(WITH_NETWORK) - -CCL_NAMESPACE_BEGIN - -typedef map PtrMap; -typedef vector DataVector; -typedef map DataMap; - -/* tile list */ -typedef vector TileList; - -/* search a list of tiles and find the one that matches the passed render tile */ -static TileList::iterator tile_list_find(TileList &tile_list, RenderTile &tile) -{ - for (TileList::iterator it = tile_list.begin(); it != tile_list.end(); ++it) - if (tile.x == it->x && tile.y == it->y && tile.start_sample == it->start_sample) - return it; - return tile_list.end(); -} - -class NetworkDevice : public Device { - public: - boost::asio::io_service io_service; - tcp::socket socket; - device_ptr mem_counter; - DeviceTask the_task; /* todo: handle multiple tasks */ - - thread_mutex rpc_lock; - - virtual bool show_samples() const - { - return false; - } - - NetworkDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, const char *address) - : Device(info, stats, profiler, true), socket(io_service) - { - error_func = NetworkError(); - stringstream portstr; - portstr << SERVER_PORT; - - tcp::resolver resolver(io_service); - tcp::resolver::query query(address, portstr.str()); - tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); - tcp::resolver::iterator end; - - boost::system::error_code error = boost::asio::error::host_not_found; - while (error && endpoint_iterator != end) { - socket.close(); - socket.connect(*endpoint_iterator++, error); - } - - if (error) - error_func.network_error(error.message()); - - mem_counter = 0; - } - - ~NetworkDevice() - { - RPCSend snd(socket, &error_func, "stop"); - snd.write(); - } - - virtual BVHLayoutMask get_bvh_layout_mask() const - { - return BVH_LAYOUT_BVH2; - } - - void mem_alloc(device_memory &mem) - { - if (mem.name) { - VLOG(1) << "Buffer allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - } - - thread_scoped_lock lock(rpc_lock); - - mem.device_pointer = ++mem_counter; - - RPCSend snd(socket, &error_func, "mem_alloc"); - snd.add(mem); - snd.write(); - } - - void mem_copy_to(device_memory &mem) - { - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "mem_copy_to"); - - snd.add(mem); - snd.write(); - snd.write_buffer(mem.host_pointer, mem.memory_size()); - } - - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) - { - thread_scoped_lock lock(rpc_lock); - - size_t data_size = mem.memory_size(); - - RPCSend snd(socket, &error_func, "mem_copy_from"); - - snd.add(mem); - snd.add(y); - snd.add(w); - snd.add(h); - snd.add(elem); - snd.write(); - - RPCReceive rcv(socket, &error_func); - rcv.read_buffer(mem.host_pointer, data_size); - } - - void mem_zero(device_memory &mem) - { - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "mem_zero"); - - snd.add(mem); - snd.write(); - } - - void mem_free(device_memory &mem) - { - if (mem.device_pointer) { - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "mem_free"); - - snd.add(mem); - snd.write(); - - mem.device_pointer = 0; - } - } - - void const_copy_to(const char *name, void *host, size_t size) - { - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "const_copy_to"); - - string name_string(name); - - snd.add(name_string); - snd.add(size); - snd.write(); - snd.write_buffer(host, size); - } - - bool load_kernels(const DeviceRequestedFeatures &requested_features) - { - if (error_func.have_error()) - return false; - - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "load_kernels"); - snd.add(requested_features.experimental); - snd.add(requested_features.max_closure); - snd.add(requested_features.max_nodes_group); - snd.add(requested_features.nodes_features); - snd.write(); - - bool result; - RPCReceive rcv(socket, &error_func); - rcv.read(result); - - return result; - } - - void task_add(DeviceTask &task) - { - thread_scoped_lock lock(rpc_lock); - - the_task = task; - - RPCSend snd(socket, &error_func, "task_add"); - snd.add(task); - snd.write(); - } - - void task_wait() - { - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "task_wait"); - snd.write(); - - lock.unlock(); - - TileList the_tiles; - - /* todo: run this threaded for connecting to multiple clients */ - for (;;) { - if (error_func.have_error()) - break; - - RenderTile tile; - - lock.lock(); - RPCReceive rcv(socket, &error_func); - - if (rcv.name == "acquire_tile") { - lock.unlock(); - - /* todo: watch out for recursive calls! */ - if (the_task.acquire_tile(this, tile)) { /* write return as bool */ - the_tiles.push_back(tile); - - lock.lock(); - RPCSend snd(socket, &error_func, "acquire_tile"); - snd.add(tile); - snd.write(); - lock.unlock(); - } - else { - lock.lock(); - RPCSend snd(socket, &error_func, "acquire_tile_none"); - snd.write(); - lock.unlock(); - } - } - else if (rcv.name == "release_tile") { - rcv.read(tile); - lock.unlock(); - - TileList::iterator it = tile_list_find(the_tiles, tile); - if (it != the_tiles.end()) { - tile.buffers = it->buffers; - the_tiles.erase(it); - } - - assert(tile.buffers != NULL); - - the_task.release_tile(tile); - - lock.lock(); - RPCSend snd(socket, &error_func, "release_tile"); - snd.write(); - lock.unlock(); - } - else if (rcv.name == "task_wait_done") { - lock.unlock(); - break; - } - else - lock.unlock(); - } - } - - void task_cancel() - { - thread_scoped_lock lock(rpc_lock); - RPCSend snd(socket, &error_func, "task_cancel"); - snd.write(); - } - - int get_split_task_count(DeviceTask &) - { - return 1; - } - - private: - NetworkError error_func; -}; - -Device *device_network_create(DeviceInfo &info, - Stats &stats, - Profiler &profiler, - const char *address) -{ - return new NetworkDevice(info, stats, profiler, address); -} - -void device_network_info(vector &devices) -{ - DeviceInfo info; - - info.type = DEVICE_NETWORK; - info.description = "Network Device"; - info.id = "NETWORK"; - info.num = 0; - - /* todo: get this info from device */ - info.has_volume_decoupled = false; - info.has_adaptive_stop_per_sample = false; - info.has_osl = false; - info.denoisers = DENOISER_NONE; - - devices.push_back(info); -} - -class DeviceServer { - public: - thread_mutex rpc_lock; - - void network_error(const string &message) - { - error_func.network_error(message); - } - - bool have_error() - { - return error_func.have_error(); - } - - DeviceServer(Device *device_, tcp::socket &socket_) - : device(device_), socket(socket_), stop(false), blocked_waiting(false) - { - error_func = NetworkError(); - } - - void listen() - { - /* receive remote function calls */ - for (;;) { - listen_step(); - - if (stop) - break; - } - } - - protected: - void listen_step() - { - thread_scoped_lock lock(rpc_lock); - RPCReceive rcv(socket, &error_func); - - if (rcv.name == "stop") - stop = true; - else - process(rcv, lock); - } - - /* create a memory buffer for a device buffer and insert it into mem_data */ - DataVector &data_vector_insert(device_ptr client_pointer, size_t data_size) - { - /* create a new DataVector and insert it into mem_data */ - pair data_ins = mem_data.insert( - DataMap::value_type(client_pointer, DataVector())); - - /* make sure it was a unique insertion */ - assert(data_ins.second); - - /* get a reference to the inserted vector */ - DataVector &data_v = data_ins.first->second; - - /* size the vector */ - data_v.resize(data_size); - - return data_v; - } - - DataVector &data_vector_find(device_ptr client_pointer) - { - DataMap::iterator i = mem_data.find(client_pointer); - assert(i != mem_data.end()); - return i->second; - } - - /* setup mapping and reverse mapping of client_pointer<->real_pointer */ - void pointer_mapping_insert(device_ptr client_pointer, device_ptr real_pointer) - { - pair mapins; - - /* insert mapping from client pointer to our real device pointer */ - mapins = ptr_map.insert(PtrMap::value_type(client_pointer, real_pointer)); - assert(mapins.second); - - /* insert reverse mapping from real our device pointer to client pointer */ - mapins = ptr_imap.insert(PtrMap::value_type(real_pointer, client_pointer)); - assert(mapins.second); - } - - device_ptr device_ptr_from_client_pointer(device_ptr client_pointer) - { - PtrMap::iterator i = ptr_map.find(client_pointer); - assert(i != ptr_map.end()); - return i->second; - } - - device_ptr device_ptr_from_client_pointer_erase(device_ptr client_pointer) - { - PtrMap::iterator i = ptr_map.find(client_pointer); - assert(i != ptr_map.end()); - - device_ptr result = i->second; - - /* erase the mapping */ - ptr_map.erase(i); - - /* erase the reverse mapping */ - PtrMap::iterator irev = ptr_imap.find(result); - assert(irev != ptr_imap.end()); - ptr_imap.erase(irev); - - /* erase the data vector */ - DataMap::iterator idata = mem_data.find(client_pointer); - assert(idata != mem_data.end()); - mem_data.erase(idata); - - return result; - } - - /* note that the lock must be already acquired upon entry. - * This is necessary because the caller often peeks at - * the header and delegates control to here when it doesn't - * specifically handle the current RPC. - * The lock must be unlocked before returning */ - void process(RPCReceive &rcv, thread_scoped_lock &lock) - { - if (rcv.name == "mem_alloc") { - string name; - network_device_memory mem(device); - rcv.read(mem, name); - lock.unlock(); - - /* Allocate host side data buffer. */ - size_t data_size = mem.memory_size(); - device_ptr client_pointer = mem.device_pointer; - - DataVector &data_v = data_vector_insert(client_pointer, data_size); - mem.host_pointer = (data_size) ? (void *)&(data_v[0]) : 0; - - /* Perform the allocation on the actual device. */ - device->mem_alloc(mem); - - /* Store a mapping to/from client_pointer and real device pointer. */ - pointer_mapping_insert(client_pointer, mem.device_pointer); - } - else if (rcv.name == "mem_copy_to") { - string name; - network_device_memory mem(device); - rcv.read(mem, name); - lock.unlock(); - - size_t data_size = mem.memory_size(); - device_ptr client_pointer = mem.device_pointer; - - if (client_pointer) { - /* Lookup existing host side data buffer. */ - DataVector &data_v = data_vector_find(client_pointer); - mem.host_pointer = (void *)&data_v[0]; - - /* Translate the client pointer to a real device pointer. */ - mem.device_pointer = device_ptr_from_client_pointer(client_pointer); - } - else { - /* Allocate host side data buffer. */ - DataVector &data_v = data_vector_insert(client_pointer, data_size); - mem.host_pointer = (data_size) ? (void *)&(data_v[0]) : 0; - } - - /* Copy data from network into memory buffer. */ - rcv.read_buffer((uint8_t *)mem.host_pointer, data_size); - - /* Copy the data from the memory buffer to the device buffer. */ - device->mem_copy_to(mem); - - if (!client_pointer) { - /* Store a mapping to/from client_pointer and real device pointer. */ - pointer_mapping_insert(client_pointer, mem.device_pointer); - } - } - else if (rcv.name == "mem_copy_from") { - string name; - network_device_memory mem(device); - int y, w, h, elem; - - rcv.read(mem, name); - rcv.read(y); - rcv.read(w); - rcv.read(h); - rcv.read(elem); - - device_ptr client_pointer = mem.device_pointer; - mem.device_pointer = device_ptr_from_client_pointer(client_pointer); - - DataVector &data_v = data_vector_find(client_pointer); - - mem.host_pointer = (device_ptr) & (data_v[0]); - - device->mem_copy_from(mem, y, w, h, elem); - - size_t data_size = mem.memory_size(); - - RPCSend snd(socket, &error_func, "mem_copy_from"); - snd.write(); - snd.write_buffer((uint8_t *)mem.host_pointer, data_size); - lock.unlock(); - } - else if (rcv.name == "mem_zero") { - string name; - network_device_memory mem(device); - rcv.read(mem, name); - lock.unlock(); - - size_t data_size = mem.memory_size(); - device_ptr client_pointer = mem.device_pointer; - - if (client_pointer) { - /* Lookup existing host side data buffer. */ - DataVector &data_v = data_vector_find(client_pointer); - mem.host_pointer = (void *)&data_v[0]; - - /* Translate the client pointer to a real device pointer. */ - mem.device_pointer = device_ptr_from_client_pointer(client_pointer); - } - else { - /* Allocate host side data buffer. */ - DataVector &data_v = data_vector_insert(client_pointer, data_size); - mem.host_pointer = (void *) ? (device_ptr) & (data_v[0]) : 0; - } - - /* Zero memory. */ - device->mem_zero(mem); - - if (!client_pointer) { - /* Store a mapping to/from client_pointer and real device pointer. */ - pointer_mapping_insert(client_pointer, mem.device_pointer); - } - } - else if (rcv.name == "mem_free") { - string name; - network_device_memory mem(device); - - rcv.read(mem, name); - lock.unlock(); - - device_ptr client_pointer = mem.device_pointer; - - mem.device_pointer = device_ptr_from_client_pointer_erase(client_pointer); - - device->mem_free(mem); - } - else if (rcv.name == "const_copy_to") { - string name_string; - size_t size; - - rcv.read(name_string); - rcv.read(size); - - vector host_vector(size); - rcv.read_buffer(&host_vector[0], size); - lock.unlock(); - - device->const_copy_to(name_string.c_str(), &host_vector[0], size); - } - else if (rcv.name == "load_kernels") { - DeviceRequestedFeatures requested_features; - rcv.read(requested_features.experimental); - rcv.read(requested_features.max_closure); - rcv.read(requested_features.max_nodes_group); - rcv.read(requested_features.nodes_features); - - bool result; - result = device->load_kernels(requested_features); - RPCSend snd(socket, &error_func, "load_kernels"); - snd.add(result); - snd.write(); - lock.unlock(); - } - else if (rcv.name == "task_add") { - DeviceTask task; - - rcv.read(task); - lock.unlock(); - - if (task.buffer) - task.buffer = device_ptr_from_client_pointer(task.buffer); - - if (task.rgba_half) - task.rgba_half = device_ptr_from_client_pointer(task.rgba_half); - - if (task.rgba_byte) - task.rgba_byte = device_ptr_from_client_pointer(task.rgba_byte); - - if (task.shader_input) - task.shader_input = device_ptr_from_client_pointer(task.shader_input); - - if (task.shader_output) - task.shader_output = device_ptr_from_client_pointer(task.shader_output); - - task.acquire_tile = function_bind(&DeviceServer::task_acquire_tile, this, _1, _2); - task.release_tile = function_bind(&DeviceServer::task_release_tile, this, _1); - task.update_progress_sample = function_bind(&DeviceServer::task_update_progress_sample, - this); - task.update_tile_sample = function_bind(&DeviceServer::task_update_tile_sample, this, _1); - task.get_cancel = function_bind(&DeviceServer::task_get_cancel, this); - - device->task_add(task); - } - else if (rcv.name == "task_wait") { - lock.unlock(); - - blocked_waiting = true; - device->task_wait(); - blocked_waiting = false; - - lock.lock(); - RPCSend snd(socket, &error_func, "task_wait_done"); - snd.write(); - lock.unlock(); - } - else if (rcv.name == "task_cancel") { - lock.unlock(); - device->task_cancel(); - } - else if (rcv.name == "acquire_tile") { - AcquireEntry entry; - entry.name = rcv.name; - rcv.read(entry.tile); - acquire_queue.push_back(entry); - lock.unlock(); - } - else if (rcv.name == "acquire_tile_none") { - AcquireEntry entry; - entry.name = rcv.name; - acquire_queue.push_back(entry); - lock.unlock(); - } - else if (rcv.name == "release_tile") { - AcquireEntry entry; - entry.name = rcv.name; - acquire_queue.push_back(entry); - lock.unlock(); - } - else { - cout << "Error: unexpected RPC receive call \"" + rcv.name + "\"\n"; - lock.unlock(); - } - } - - bool task_acquire_tile(Device *, RenderTile &tile) - { - thread_scoped_lock acquire_lock(acquire_mutex); - - bool result = false; - - RPCSend snd(socket, &error_func, "acquire_tile"); - snd.write(); - - do { - if (blocked_waiting) - listen_step(); - - /* todo: avoid busy wait loop */ - thread_scoped_lock lock(rpc_lock); - - if (!acquire_queue.empty()) { - AcquireEntry entry = acquire_queue.front(); - acquire_queue.pop_front(); - - if (entry.name == "acquire_tile") { - tile = entry.tile; - - if (tile.buffer) - tile.buffer = ptr_map[tile.buffer]; - - result = true; - break; - } - else if (entry.name == "acquire_tile_none") { - break; - } - else { - cout << "Error: unexpected acquire RPC receive call \"" + entry.name + "\"\n"; - } - } - } while (acquire_queue.empty() && !stop && !have_error()); - - return result; - } - - void task_update_progress_sample() - { - ; /* skip */ - } - - void task_update_tile_sample(RenderTile &) - { - ; /* skip */ - } - - void task_release_tile(RenderTile &tile) - { - thread_scoped_lock acquire_lock(acquire_mutex); - - if (tile.buffer) - tile.buffer = ptr_imap[tile.buffer]; - - { - thread_scoped_lock lock(rpc_lock); - RPCSend snd(socket, &error_func, "release_tile"); - snd.add(tile); - snd.write(); - lock.unlock(); - } - - do { - if (blocked_waiting) - listen_step(); - - /* todo: avoid busy wait loop */ - thread_scoped_lock lock(rpc_lock); - - if (!acquire_queue.empty()) { - AcquireEntry entry = acquire_queue.front(); - acquire_queue.pop_front(); - - if (entry.name == "release_tile") { - lock.unlock(); - break; - } - else { - cout << "Error: unexpected release RPC receive call \"" + entry.name + "\"\n"; - } - } - } while (acquire_queue.empty() && !stop); - } - - bool task_get_cancel() - { - return false; - } - - /* properties */ - Device *device; - tcp::socket &socket; - - /* mapping of remote to local pointer */ - PtrMap ptr_map; - PtrMap ptr_imap; - DataMap mem_data; - - struct AcquireEntry { - string name; - RenderTile tile; - }; - - thread_mutex acquire_mutex; - list acquire_queue; - - bool stop; - bool blocked_waiting; - - private: - NetworkError error_func; - - /* todo: free memory and device (osl) on network error */ -}; - -void Device::server_run() -{ - try { - /* starts thread that responds to discovery requests */ - ServerDiscovery discovery; - - for (;;) { - /* accept connection */ - boost::asio::io_service io_service; - tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), SERVER_PORT)); - - tcp::socket socket(io_service); - acceptor.accept(socket); - - string remote_address = socket.remote_endpoint().address().to_string(); - printf("Connected to remote client at: %s\n", remote_address.c_str()); - - DeviceServer server(this, socket); - server.listen(); - - printf("Disconnected.\n"); - } - } - catch (exception &e) { - fprintf(stderr, "Network server exception: %s\n", e.what()); - } -} - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/device_network.h b/intern/cycles/device/device_network.h deleted file mode 100644 index b3a0f6daa57..00000000000 --- a/intern/cycles/device/device_network.h +++ /dev/null @@ -1,490 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEVICE_NETWORK_H__ -#define __DEVICE_NETWORK_H__ - -#ifdef WITH_NETWORK - -# include -# include -# include -# include -# include -# include -# include -# include -# include - -# include -# include -# include - -# include "render/buffers.h" - -# include "util/util_foreach.h" -# include "util/util_list.h" -# include "util/util_map.h" -# include "util/util_param.h" -# include "util/util_string.h" - -CCL_NAMESPACE_BEGIN - -using std::cerr; -using std::cout; -using std::exception; -using std::hex; -using std::setw; - -using boost::asio::ip::tcp; - -static const int SERVER_PORT = 5120; -static const int DISCOVER_PORT = 5121; -static const string DISCOVER_REQUEST_MSG = "REQUEST_RENDER_SERVER_IP"; -static const string DISCOVER_REPLY_MSG = "REPLY_RENDER_SERVER_IP"; - -# if 0 -typedef boost::archive::text_oarchive o_archive; -typedef boost::archive::text_iarchive i_archive; -# else -typedef boost::archive::binary_oarchive o_archive; -typedef boost::archive::binary_iarchive i_archive; -# endif - -/* Serialization of device memory */ - -class network_device_memory : public device_memory { - public: - network_device_memory(Device *device) : device_memory(device, "", MEM_READ_ONLY) - { - } - - ~network_device_memory() - { - device_pointer = 0; - }; - - vector local_data; -}; - -/* Common network error function / object for both DeviceNetwork and DeviceServer. */ -class NetworkError { - public: - NetworkError() - { - error = ""; - error_count = 0; - } - - ~NetworkError() - { - } - - void network_error(const string &message) - { - error = message; - error_count += 1; - } - - bool have_error() - { - return true ? error_count > 0 : false; - } - - private: - string error; - int error_count; -}; - -/* Remote procedure call Send */ - -class RPCSend { - public: - RPCSend(tcp::socket &socket_, NetworkError *e, const string &name_ = "") - : name(name_), socket(socket_), archive(archive_stream), sent(false) - { - archive &name_; - error_func = e; - fprintf(stderr, "rpc send %s\n", name.c_str()); - } - - ~RPCSend() - { - } - - void add(const device_memory &mem) - { - archive &mem.data_type &mem.data_elements &mem.data_size; - archive &mem.data_width &mem.data_height &mem.data_depth &mem.device_pointer; - archive &mem.type &string(mem.name); - archive &mem.interpolation &mem.extension; - archive &mem.device_pointer; - } - - template void add(const T &data) - { - archive &data; - } - - void add(const DeviceTask &task) - { - int type = (int)task.type; - archive &type &task.x &task.y &task.w &task.h; - archive &task.rgba_byte &task.rgba_half &task.buffer &task.sample &task.num_samples; - archive &task.offset &task.stride; - archive &task.shader_input &task.shader_output &task.shader_eval_type; - archive &task.shader_x &task.shader_w; - archive &task.need_finish_queue; - } - - void add(const RenderTile &tile) - { - archive &tile.x &tile.y &tile.w &tile.h; - archive &tile.start_sample &tile.num_samples &tile.sample; - archive &tile.resolution &tile.offset &tile.stride; - archive &tile.buffer; - } - - void write() - { - boost::system::error_code error; - - /* get string from stream */ - string archive_str = archive_stream.str(); - - /* first send fixed size header with size of following data */ - ostringstream header_stream; - header_stream << setw(8) << hex << archive_str.size(); - string header_str = header_stream.str(); - - boost::asio::write( - socket, boost::asio::buffer(header_str), boost::asio::transfer_all(), error); - - if (error.value()) - error_func->network_error(error.message()); - - /* then send actual data */ - boost::asio::write( - socket, boost::asio::buffer(archive_str), boost::asio::transfer_all(), error); - - if (error.value()) - error_func->network_error(error.message()); - - sent = true; - } - - void write_buffer(void *buffer, size_t size) - { - boost::system::error_code error; - - boost::asio::write( - socket, boost::asio::buffer(buffer, size), boost::asio::transfer_all(), error); - - if (error.value()) - error_func->network_error(error.message()); - } - - protected: - string name; - tcp::socket &socket; - ostringstream archive_stream; - o_archive archive; - bool sent; - NetworkError *error_func; -}; - -/* Remote procedure call Receive */ - -class RPCReceive { - public: - RPCReceive(tcp::socket &socket_, NetworkError *e) - : socket(socket_), archive_stream(NULL), archive(NULL) - { - error_func = e; - /* read head with fixed size */ - vector header(8); - boost::system::error_code error; - size_t len = boost::asio::read(socket, boost::asio::buffer(header), error); - - if (error.value()) { - error_func->network_error(error.message()); - } - - /* verify if we got something */ - if (len == header.size()) { - /* decode header */ - string header_str(&header[0], header.size()); - istringstream header_stream(header_str); - - size_t data_size; - - if ((header_stream >> hex >> data_size)) { - - vector data(data_size); - size_t len = boost::asio::read(socket, boost::asio::buffer(data), error); - - if (error.value()) - error_func->network_error(error.message()); - - if (len == data_size) { - archive_str = (data.size()) ? string(&data[0], data.size()) : string(""); - - archive_stream = new istringstream(archive_str); - archive = new i_archive(*archive_stream); - - *archive &name; - fprintf(stderr, "rpc receive %s\n", name.c_str()); - } - else { - error_func->network_error("Network receive error: data size doesn't match header"); - } - } - else { - error_func->network_error("Network receive error: can't decode data size from header"); - } - } - else { - error_func->network_error("Network receive error: invalid header size"); - } - } - - ~RPCReceive() - { - delete archive; - delete archive_stream; - } - - void read(network_device_memory &mem, string &name) - { - *archive &mem.data_type &mem.data_elements &mem.data_size; - *archive &mem.data_width &mem.data_height &mem.data_depth &mem.device_pointer; - *archive &mem.type &name; - *archive &mem.interpolation &mem.extension; - *archive &mem.device_pointer; - - mem.name = name.c_str(); - mem.host_pointer = 0; - - /* Can't transfer OpenGL texture over network. */ - if (mem.type == MEM_PIXELS) { - mem.type = MEM_READ_WRITE; - } - } - - template void read(T &data) - { - *archive &data; - } - - void read_buffer(void *buffer, size_t size) - { - boost::system::error_code error; - size_t len = boost::asio::read(socket, boost::asio::buffer(buffer, size), error); - - if (error.value()) { - error_func->network_error(error.message()); - } - - if (len != size) - cout << "Network receive error: buffer size doesn't match expected size\n"; - } - - void read(DeviceTask &task) - { - int type; - - *archive &type &task.x &task.y &task.w &task.h; - *archive &task.rgba_byte &task.rgba_half &task.buffer &task.sample &task.num_samples; - *archive &task.offset &task.stride; - *archive &task.shader_input &task.shader_output &task.shader_eval_type; - *archive &task.shader_x &task.shader_w; - *archive &task.need_finish_queue; - - task.type = (DeviceTask::Type)type; - } - - void read(RenderTile &tile) - { - *archive &tile.x &tile.y &tile.w &tile.h; - *archive &tile.start_sample &tile.num_samples &tile.sample; - *archive &tile.resolution &tile.offset &tile.stride; - *archive &tile.buffer; - - tile.buffers = NULL; - } - - string name; - - protected: - tcp::socket &socket; - string archive_str; - istringstream *archive_stream; - i_archive *archive; - NetworkError *error_func; -}; - -/* Server auto discovery */ - -class ServerDiscovery { - public: - explicit ServerDiscovery(bool discover = false) - : listen_socket(io_service), collect_servers(false) - { - /* setup listen socket */ - listen_endpoint.address(boost::asio::ip::address_v4::any()); - listen_endpoint.port(DISCOVER_PORT); - - listen_socket.open(listen_endpoint.protocol()); - - boost::asio::socket_base::reuse_address option(true); - listen_socket.set_option(option); - - listen_socket.bind(listen_endpoint); - - /* setup receive callback */ - async_receive(); - - /* start server discovery */ - if (discover) { - collect_servers = true; - servers.clear(); - - broadcast_message(DISCOVER_REQUEST_MSG); - } - - /* start thread */ - work = new boost::asio::io_service::work(io_service); - thread = new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service)); - } - - ~ServerDiscovery() - { - io_service.stop(); - thread->join(); - delete thread; - delete work; - } - - vector get_server_list() - { - vector result; - - mutex.lock(); - result = vector(servers.begin(), servers.end()); - mutex.unlock(); - - return result; - } - - private: - void handle_receive_from(const boost::system::error_code &error, size_t size) - { - if (error) { - cout << "Server discovery receive error: " << error.message() << "\n"; - return; - } - - if (size > 0) { - string msg = string(receive_buffer, size); - - /* handle incoming message */ - if (collect_servers) { - if (msg == DISCOVER_REPLY_MSG) { - string address = receive_endpoint.address().to_string(); - - mutex.lock(); - - /* add address if it's not already in the list */ - bool found = std::find(servers.begin(), servers.end(), address) != servers.end(); - - if (!found) - servers.push_back(address); - - mutex.unlock(); - } - } - else { - /* reply to request */ - if (msg == DISCOVER_REQUEST_MSG) - broadcast_message(DISCOVER_REPLY_MSG); - } - } - - async_receive(); - } - - void async_receive() - { - listen_socket.async_receive_from(boost::asio::buffer(receive_buffer), - receive_endpoint, - boost::bind(&ServerDiscovery::handle_receive_from, - this, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - } - - void broadcast_message(const string &msg) - { - /* setup broadcast socket */ - boost::asio::ip::udp::socket socket(io_service); - - socket.open(boost::asio::ip::udp::v4()); - - boost::asio::socket_base::broadcast option(true); - socket.set_option(option); - - boost::asio::ip::udp::endpoint broadcast_endpoint( - boost::asio::ip::address::from_string("255.255.255.255"), DISCOVER_PORT); - - /* broadcast message */ - socket.send_to(boost::asio::buffer(msg), broadcast_endpoint); - } - - /* network service and socket */ - boost::asio::io_service io_service; - boost::asio::ip::udp::endpoint listen_endpoint; - boost::asio::ip::udp::socket listen_socket; - - /* threading */ - boost::thread *thread; - boost::asio::io_service::work *work; - boost::mutex mutex; - - /* buffer and endpoint for receiving messages */ - char receive_buffer[256]; - boost::asio::ip::udp::endpoint receive_endpoint; - - // os, version, devices, status, host name, group name, ip as far as fields go - struct ServerInfo { - string cycles_version; - string os; - int device_count; - string status; - string host_name; - string group_name; - string host_addr; - }; - - /* collection of server addresses in list */ - bool collect_servers; - vector servers; -}; - -CCL_NAMESPACE_END - -#endif - -#endif /* __DEVICE_NETWORK_H__ */ diff --git a/intern/cycles/device/device_opencl.cpp b/intern/cycles/device/device_opencl.cpp deleted file mode 100644 index 9abb7cfb7fe..00000000000 --- a/intern/cycles/device/device_opencl.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPENCL - -# include "device/opencl/device_opencl.h" -# include "device/device.h" -# include "device/device_intern.h" - -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_set.h" -# include "util/util_string.h" - -CCL_NAMESPACE_BEGIN - -Device *device_opencl_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) -{ - return opencl_create_split_device(info, stats, profiler, background); -} - -bool device_opencl_init() -{ - static bool initialized = false; - static bool result = false; - - if (initialized) - return result; - - initialized = true; - - if (OpenCLInfo::device_type() != 0) { - int clew_result = clewInit(); - if (clew_result == CLEW_SUCCESS) { - VLOG(1) << "CLEW initialization succeeded."; - result = true; - } - else { - VLOG(1) << "CLEW initialization failed: " - << ((clew_result == CLEW_ERROR_ATEXIT_FAILED) ? "Error setting up atexit() handler" : - "Error opening the library"); - } - } - else { - VLOG(1) << "Skip initializing CLEW, platform is force disabled."; - result = false; - } - - return result; -} - -static cl_int device_opencl_get_num_platforms_safe(cl_uint *num_platforms) -{ -# ifdef _WIN32 - __try { - return clGetPlatformIDs(0, NULL, num_platforms); - } - __except (EXCEPTION_EXECUTE_HANDLER) { - /* Ignore crashes inside the OpenCL driver and hope we can - * survive even with corrupted OpenCL installs. */ - fprintf(stderr, "Cycles OpenCL: driver crashed, continuing without OpenCL.\n"); - } - - *num_platforms = 0; - return CL_DEVICE_NOT_FOUND; -# else - return clGetPlatformIDs(0, NULL, num_platforms); -# endif -} - -void device_opencl_info(vector &devices) -{ - cl_uint num_platforms = 0; - device_opencl_get_num_platforms_safe(&num_platforms); - if (num_platforms == 0) { - return; - } - - vector usable_devices; - OpenCLInfo::get_usable_devices(&usable_devices); - /* Devices are numbered consecutively across platforms. */ - int num_devices = 0; - set unique_ids; - foreach (OpenCLPlatformDevice &platform_device, usable_devices) { - /* Compute unique ID for persistent user preferences. */ - const string &platform_name = platform_device.platform_name; - const string &device_name = platform_device.device_name; - string hardware_id = platform_device.hardware_id; - if (hardware_id == "") { - hardware_id = string_printf("ID_%d", num_devices); - } - string id = string("OPENCL_") + platform_name + "_" + device_name + "_" + hardware_id; - - /* Hardware ID might not be unique, add device number in that case. */ - if (unique_ids.find(id) != unique_ids.end()) { - id += string_printf("_ID_%d", num_devices); - } - unique_ids.insert(id); - - /* Create DeviceInfo. */ - DeviceInfo info; - info.type = DEVICE_OPENCL; - info.description = string_remove_trademark(string(device_name)); - info.num = num_devices; - /* We don't know if it's used for display, but assume it is. */ - info.display_device = true; - info.use_split_kernel = true; - info.has_volume_decoupled = false; - info.has_adaptive_stop_per_sample = false; - info.denoisers = DENOISER_NLM; - info.id = id; - - /* Check OpenCL extensions */ - info.has_half_images = platform_device.device_extensions.find("cl_khr_fp16") != string::npos; - - /* Disabled for now due to apparent AMD driver bug. */ - info.has_nanovdb = platform_name != "AMD Accelerated Parallel Processing"; - - devices.push_back(info); - num_devices++; - } -} - -string device_opencl_capabilities() -{ - if (OpenCLInfo::device_type() == 0) { - return "All OpenCL devices are forced to be OFF"; - } - string result = ""; - string error_msg = ""; /* Only used by opencl_assert(), but in the future - * it could also be nicely reported to the console. - */ - cl_uint num_platforms = 0; - opencl_assert(device_opencl_get_num_platforms_safe(&num_platforms)); - if (num_platforms == 0) { - return "No OpenCL platforms found\n"; - } - result += string_printf("Number of platforms: %u\n", num_platforms); - - vector platform_ids; - platform_ids.resize(num_platforms); - opencl_assert(clGetPlatformIDs(num_platforms, &platform_ids[0], NULL)); - -# define APPEND_INFO(func, id, name, what, type) \ - do { \ - type data; \ - memset(&data, 0, sizeof(data)); \ - opencl_assert(func(id, what, sizeof(data), &data, NULL)); \ - result += string_printf("%s: %s\n", name, to_string(data).c_str()); \ - } while (false) -# define APPEND_STRING_INFO_IMPL(func, id, name, what, is_optional) \ - do { \ - string value; \ - size_t length = 0; \ - if (func(id, what, 0, NULL, &length) == CL_SUCCESS) { \ - vector buffer(length + 1); \ - if (func(id, what, buffer.size(), buffer.data(), NULL) == CL_SUCCESS) { \ - value = string(buffer.data()); \ - } \ - } \ - if (is_optional && !(length != 0 && value[0] != '\0')) { \ - break; \ - } \ - result += string_printf("%s: %s\n", name, value.c_str()); \ - } while (false) -# define APPEND_PLATFORM_STRING_INFO(id, name, what) \ - APPEND_STRING_INFO_IMPL(clGetPlatformInfo, id, "\tPlatform " name, what, false) -# define APPEND_STRING_EXTENSION_INFO(func, id, name, what) \ - APPEND_STRING_INFO_IMPL(clGetPlatformInfo, id, "\tPlatform " name, what, true) -# define APPEND_PLATFORM_INFO(id, name, what, type) \ - APPEND_INFO(clGetPlatformInfo, id, "\tPlatform " name, what, type) -# define APPEND_DEVICE_INFO(id, name, what, type) \ - APPEND_INFO(clGetDeviceInfo, id, "\t\t\tDevice " name, what, type) -# define APPEND_DEVICE_STRING_INFO(id, name, what) \ - APPEND_STRING_INFO_IMPL(clGetDeviceInfo, id, "\t\t\tDevice " name, what, false) -# define APPEND_DEVICE_STRING_EXTENSION_INFO(id, name, what) \ - APPEND_STRING_INFO_IMPL(clGetDeviceInfo, id, "\t\t\tDevice " name, what, true) - - vector device_ids; - for (cl_uint platform = 0; platform < num_platforms; ++platform) { - cl_platform_id platform_id = platform_ids[platform]; - - result += string_printf("Platform #%u\n", platform); - - APPEND_PLATFORM_STRING_INFO(platform_id, "Name", CL_PLATFORM_NAME); - APPEND_PLATFORM_STRING_INFO(platform_id, "Vendor", CL_PLATFORM_VENDOR); - APPEND_PLATFORM_STRING_INFO(platform_id, "Version", CL_PLATFORM_VERSION); - APPEND_PLATFORM_STRING_INFO(platform_id, "Profile", CL_PLATFORM_PROFILE); - APPEND_PLATFORM_STRING_INFO(platform_id, "Extensions", CL_PLATFORM_EXTENSIONS); - - cl_uint num_devices = 0; - opencl_assert( - clGetDeviceIDs(platform_ids[platform], CL_DEVICE_TYPE_ALL, 0, NULL, &num_devices)); - result += string_printf("\tNumber of devices: %u\n", num_devices); - - device_ids.resize(num_devices); - opencl_assert(clGetDeviceIDs( - platform_ids[platform], CL_DEVICE_TYPE_ALL, num_devices, &device_ids[0], NULL)); - for (cl_uint device = 0; device < num_devices; ++device) { - cl_device_id device_id = device_ids[device]; - - result += string_printf("\t\tDevice: #%u\n", device); - - APPEND_DEVICE_STRING_INFO(device_id, "Name", CL_DEVICE_NAME); - APPEND_DEVICE_STRING_EXTENSION_INFO(device_id, "Board Name", CL_DEVICE_BOARD_NAME_AMD); - APPEND_DEVICE_STRING_INFO(device_id, "Vendor", CL_DEVICE_VENDOR); - APPEND_DEVICE_STRING_INFO(device_id, "OpenCL C Version", CL_DEVICE_OPENCL_C_VERSION); - APPEND_DEVICE_STRING_INFO(device_id, "Profile", CL_DEVICE_PROFILE); - APPEND_DEVICE_STRING_INFO(device_id, "Version", CL_DEVICE_VERSION); - APPEND_DEVICE_STRING_INFO(device_id, "Extensions", CL_DEVICE_EXTENSIONS); - APPEND_DEVICE_INFO( - device_id, "Max clock frequency (MHz)", CL_DEVICE_MAX_CLOCK_FREQUENCY, cl_uint); - APPEND_DEVICE_INFO(device_id, "Max compute units", CL_DEVICE_MAX_COMPUTE_UNITS, cl_uint); - APPEND_DEVICE_INFO(device_id, "Max work group size", CL_DEVICE_MAX_WORK_GROUP_SIZE, size_t); - } - } - -# undef APPEND_INFO -# undef APPEND_STRING_INFO_IMPL -# undef APPEND_PLATFORM_STRING_INFO -# undef APPEND_STRING_EXTENSION_INFO -# undef APPEND_PLATFORM_INFO -# undef APPEND_DEVICE_INFO -# undef APPEND_DEVICE_STRING_INFO -# undef APPEND_DEVICE_STRING_EXTENSION_INFO - - return result; -} - -CCL_NAMESPACE_END - -#endif /* WITH_OPENCL */ diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp deleted file mode 100644 index 6f9a7943722..00000000000 --- a/intern/cycles/device/device_optix.cpp +++ /dev/null @@ -1,1936 +0,0 @@ -/* - * Copyright 2019, NVIDIA Corporation. - * Copyright 2019, Blender Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPTIX - -# include "bvh/bvh.h" -# include "bvh/bvh_optix.h" -# include "device/cuda/device_cuda.h" -# include "device/device_denoising.h" -# include "device/device_intern.h" -# include "render/buffers.h" -# include "render/hair.h" -# include "render/mesh.h" -# include "render/object.h" -# include "render/scene.h" -# include "util/util_debug.h" -# include "util/util_logging.h" -# include "util/util_md5.h" -# include "util/util_path.h" -# include "util/util_progress.h" -# include "util/util_time.h" - -# ifdef WITH_CUDA_DYNLOAD -# include -// Do not use CUDA SDK headers when using CUEW -# define OPTIX_DONT_INCLUDE_CUDA -# endif -# include -# include - -// TODO(pmours): Disable this once drivers have native support -# define OPTIX_DENOISER_NO_PIXEL_STRIDE 1 - -CCL_NAMESPACE_BEGIN - -/* Make sure this stays in sync with kernel_globals.h */ -struct ShaderParams { - uint4 *input; - float4 *output; - int type; - int filter; - int sx; - int offset; - int sample; -}; -struct KernelParams { - WorkTile tile; - KernelData data; - ShaderParams shader; -# define KERNEL_TEX(type, name) const type *name; -# include "kernel/kernel_textures.h" -# undef KERNEL_TEX -}; - -# define check_result_cuda(stmt) \ - { \ - CUresult res = stmt; \ - if (res != CUDA_SUCCESS) { \ - const char *name; \ - cuGetErrorName(res, &name); \ - set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \ - return; \ - } \ - } \ - (void)0 -# define check_result_cuda_ret(stmt) \ - { \ - CUresult res = stmt; \ - if (res != CUDA_SUCCESS) { \ - const char *name; \ - cuGetErrorName(res, &name); \ - set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \ - return false; \ - } \ - } \ - (void)0 - -# define check_result_optix(stmt) \ - { \ - enum OptixResult res = stmt; \ - if (res != OPTIX_SUCCESS) { \ - const char *name = optixGetErrorName(res); \ - set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \ - return; \ - } \ - } \ - (void)0 -# define check_result_optix_ret(stmt) \ - { \ - enum OptixResult res = stmt; \ - if (res != OPTIX_SUCCESS) { \ - const char *name = optixGetErrorName(res); \ - set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \ - return false; \ - } \ - } \ - (void)0 - -# define launch_filter_kernel(func_name, w, h, args) \ - { \ - CUfunction func; \ - check_result_cuda_ret(cuModuleGetFunction(&func, cuFilterModule, func_name)); \ - check_result_cuda_ret(cuFuncSetCacheConfig(func, CU_FUNC_CACHE_PREFER_L1)); \ - int threads; \ - check_result_cuda_ret( \ - cuFuncGetAttribute(&threads, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, func)); \ - threads = (int)sqrt((float)threads); \ - int xblocks = ((w) + threads - 1) / threads; \ - int yblocks = ((h) + threads - 1) / threads; \ - check_result_cuda_ret( \ - cuLaunchKernel(func, xblocks, yblocks, 1, threads, threads, 1, 0, 0, args, 0)); \ - } \ - (void)0 - -class OptiXDevice : public CUDADevice { - - // List of OptiX program groups - enum { - PG_RGEN, - PG_MISS, - PG_HITD, // Default hit group - PG_HITS, // __SHADOW_RECORD_ALL__ hit group - PG_HITL, // __BVH_LOCAL__ hit group (only used for triangles) -# if OPTIX_ABI_VERSION >= 36 - PG_HITD_MOTION, - PG_HITS_MOTION, -# endif - PG_BAKE, // kernel_bake_evaluate - PG_DISP, // kernel_displace_evaluate - PG_BACK, // kernel_background_evaluate - PG_CALL, - NUM_PROGRAM_GROUPS = PG_CALL + 3 - }; - - // List of OptiX pipelines - enum { PIP_PATH_TRACE, PIP_SHADER_EVAL, NUM_PIPELINES }; - - // A single shader binding table entry - struct SbtRecord { - char header[OPTIX_SBT_RECORD_HEADER_SIZE]; - }; - - // Information stored about CUDA memory allocations - struct CUDAMem { - bool free_map_host = false; - CUarray array = NULL; - CUtexObject texobject = 0; - bool use_mapped_host = false; - }; - - // Helper class to manage current CUDA context - struct CUDAContextScope { - CUDAContextScope(CUcontext ctx) - { - cuCtxPushCurrent(ctx); - } - ~CUDAContextScope() - { - cuCtxPopCurrent(NULL); - } - }; - - // Use a pool with multiple threads to support launches with multiple CUDA streams - TaskPool task_pool; - - vector cuda_stream; - OptixDeviceContext context = NULL; - - OptixModule optix_module = NULL; // All necessary OptiX kernels are in one module - OptixModule builtin_modules[2] = {}; - OptixPipeline pipelines[NUM_PIPELINES] = {}; - - bool motion_blur = false; - device_vector sbt_data; - device_only_memory launch_params; - OptixTraversableHandle tlas_handle = 0; - - OptixDenoiser denoiser = NULL; - device_only_memory denoiser_state; - int denoiser_input_passes = 0; - - vector> delayed_free_bvh_memory; - thread_mutex delayed_free_bvh_mutex; - - public: - OptiXDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_) - : CUDADevice(info_, stats_, profiler_, background_), - sbt_data(this, "__sbt", MEM_READ_ONLY), - launch_params(this, "__params", false), - denoiser_state(this, "__denoiser_state", true) - { - // Store number of CUDA streams in device info - info.cpu_threads = DebugFlags().optix.cuda_streams; - - // Make the CUDA context current - if (!cuContext) { - return; // Do not initialize if CUDA context creation failed already - } - const CUDAContextScope scope(cuContext); - - // Create OptiX context for this device - OptixDeviceContextOptions options = {}; -# ifdef WITH_CYCLES_LOGGING - options.logCallbackLevel = 4; // Fatal = 1, Error = 2, Warning = 3, Print = 4 - options.logCallbackFunction = - [](unsigned int level, const char *, const char *message, void *) { - switch (level) { - case 1: - LOG_IF(FATAL, VLOG_IS_ON(1)) << message; - break; - case 2: - LOG_IF(ERROR, VLOG_IS_ON(1)) << message; - break; - case 3: - LOG_IF(WARNING, VLOG_IS_ON(1)) << message; - break; - case 4: - LOG_IF(INFO, VLOG_IS_ON(1)) << message; - break; - } - }; -# endif - check_result_optix(optixDeviceContextCreate(cuContext, &options, &context)); -# ifdef WITH_CYCLES_LOGGING - check_result_optix(optixDeviceContextSetLogCallback( - context, options.logCallbackFunction, options.logCallbackData, options.logCallbackLevel)); -# endif - - // Create launch streams - cuda_stream.resize(info.cpu_threads); - for (int i = 0; i < info.cpu_threads; ++i) - check_result_cuda(cuStreamCreate(&cuda_stream[i], CU_STREAM_NON_BLOCKING)); - - // Fix weird compiler bug that assigns wrong size - launch_params.data_elements = sizeof(KernelParams); - // Allocate launch parameter buffer memory on device - launch_params.alloc_to_device(info.cpu_threads); - } - ~OptiXDevice() - { - // Stop processing any more tasks - task_pool.cancel(); - - // Make CUDA context current - const CUDAContextScope scope(cuContext); - - free_bvh_memory_delayed(); - - sbt_data.free(); - texture_info.free(); - launch_params.free(); - denoiser_state.free(); - - // Unload modules - if (optix_module != NULL) - optixModuleDestroy(optix_module); - for (unsigned int i = 0; i < 2; ++i) - if (builtin_modules[i] != NULL) - optixModuleDestroy(builtin_modules[i]); - for (unsigned int i = 0; i < NUM_PIPELINES; ++i) - if (pipelines[i] != NULL) - optixPipelineDestroy(pipelines[i]); - - // Destroy launch streams - for (CUstream stream : cuda_stream) - cuStreamDestroy(stream); - - if (denoiser != NULL) - optixDenoiserDestroy(denoiser); - - optixDeviceContextDestroy(context); - } - - private: - bool show_samples() const override - { - // Only show samples if not rendering multiple tiles in parallel - return info.cpu_threads == 1; - } - - BVHLayoutMask get_bvh_layout_mask() const override - { - // CUDA kernels are used when doing baking, so need to build a BVH those can understand too! - if (optix_module == NULL) - return CUDADevice::get_bvh_layout_mask(); - - // OptiX has its own internal acceleration structure format - return BVH_LAYOUT_OPTIX; - } - - string compile_kernel_get_common_cflags(const DeviceRequestedFeatures &requested_features, - bool filter, - bool /*split*/) override - { - // Split kernel is not supported in OptiX - string common_cflags = CUDADevice::compile_kernel_get_common_cflags( - requested_features, filter, false); - - // Add OptiX SDK include directory to include paths - const char *optix_sdk_path = getenv("OPTIX_ROOT_DIR"); - if (optix_sdk_path) { - common_cflags += string_printf(" -I\"%s/include\"", optix_sdk_path); - } - - // Specialization for shader raytracing - if (requested_features.use_shader_raytrace) { - common_cflags += " --keep-device-functions"; - } - else { - common_cflags += " -D __NO_SHADER_RAYTRACE__"; - } - - return common_cflags; - } - - bool load_kernels(const DeviceRequestedFeatures &requested_features) override - { - if (have_error()) { - // Abort early if context creation failed already - return false; - } - - // Load CUDA modules because we need some of the utility kernels - if (!CUDADevice::load_kernels(requested_features)) { - return false; - } - - // Baking is currently performed using CUDA, so no need to load OptiX kernels - if (requested_features.use_baking) { - return true; - } - - const CUDAContextScope scope(cuContext); - - // Unload existing OptiX module and pipelines first - if (optix_module != NULL) { - optixModuleDestroy(optix_module); - optix_module = NULL; - } - for (unsigned int i = 0; i < 2; ++i) { - if (builtin_modules[i] != NULL) { - optixModuleDestroy(builtin_modules[i]); - builtin_modules[i] = NULL; - } - } - for (unsigned int i = 0; i < NUM_PIPELINES; ++i) { - if (pipelines[i] != NULL) { - optixPipelineDestroy(pipelines[i]); - pipelines[i] = NULL; - } - } - - OptixModuleCompileOptions module_options = {}; - module_options.maxRegisterCount = 0; // Do not set an explicit register limit - module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_3; - module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; - -# if OPTIX_ABI_VERSION >= 41 - module_options.boundValues = nullptr; - module_options.numBoundValues = 0; -# endif - - OptixPipelineCompileOptions pipeline_options = {}; - // Default to no motion blur and two-level graph, since it is the fastest option - pipeline_options.usesMotionBlur = false; - pipeline_options.traversableGraphFlags = - OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING; - pipeline_options.numPayloadValues = 6; - pipeline_options.numAttributeValues = 2; // u, v - pipeline_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE; - pipeline_options.pipelineLaunchParamsVariableName = "__params"; // See kernel_globals.h - -# if OPTIX_ABI_VERSION >= 36 - pipeline_options.usesPrimitiveTypeFlags = OPTIX_PRIMITIVE_TYPE_FLAGS_TRIANGLE; - if (requested_features.use_hair) { - if (DebugFlags().optix.curves_api && requested_features.use_hair_thick) { - pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_ROUND_CUBIC_BSPLINE; - } - else { - pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM; - } - } -# endif - - // Keep track of whether motion blur is enabled, so to enable/disable motion in BVH builds - // This is necessary since objects may be reported to have motion if the Vector pass is - // active, but may still need to be rendered without motion blur if that isn't active as well - motion_blur = requested_features.use_object_motion; - - if (motion_blur) { - pipeline_options.usesMotionBlur = true; - // Motion blur can insert motion transforms into the traversal graph - // It is no longer a two-level graph then, so need to set flags to allow any configuration - pipeline_options.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_ANY; - } - - { // Load and compile PTX module with OptiX kernels - string ptx_data, ptx_filename = path_get(requested_features.use_shader_raytrace ? - "lib/kernel_optix_shader_raytrace.ptx" : - "lib/kernel_optix.ptx"); - if (use_adaptive_compilation() || path_file_size(ptx_filename) == -1) { - if (!getenv("OPTIX_ROOT_DIR")) { - set_error( - "Missing OPTIX_ROOT_DIR environment variable (which must be set with the path to " - "the Optix SDK to be able to compile Optix kernels on demand)."); - return false; - } - ptx_filename = compile_kernel(requested_features, "kernel_optix", "optix", true); - } - if (ptx_filename.empty() || !path_read_text(ptx_filename, ptx_data)) { - set_error("Failed to load OptiX kernel from '" + ptx_filename + "'"); - return false; - } - - check_result_optix_ret(optixModuleCreateFromPTX(context, - &module_options, - &pipeline_options, - ptx_data.data(), - ptx_data.size(), - nullptr, - 0, - &optix_module)); - } - - // Create program groups - OptixProgramGroup groups[NUM_PROGRAM_GROUPS] = {}; - OptixProgramGroupDesc group_descs[NUM_PROGRAM_GROUPS] = {}; - OptixProgramGroupOptions group_options = {}; // There are no options currently - group_descs[PG_RGEN].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; - group_descs[PG_RGEN].raygen.module = optix_module; - // Ignore branched integrator for now (see "requested_features.use_integrator_branched") - group_descs[PG_RGEN].raygen.entryFunctionName = "__raygen__kernel_optix_path_trace"; - group_descs[PG_MISS].kind = OPTIX_PROGRAM_GROUP_KIND_MISS; - group_descs[PG_MISS].miss.module = optix_module; - group_descs[PG_MISS].miss.entryFunctionName = "__miss__kernel_optix_miss"; - group_descs[PG_HITD].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; - group_descs[PG_HITD].hitgroup.moduleCH = optix_module; - group_descs[PG_HITD].hitgroup.entryFunctionNameCH = "__closesthit__kernel_optix_hit"; - group_descs[PG_HITD].hitgroup.moduleAH = optix_module; - group_descs[PG_HITD].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_visibility_test"; - group_descs[PG_HITS].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; - group_descs[PG_HITS].hitgroup.moduleAH = optix_module; - group_descs[PG_HITS].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_shadow_all_hit"; - - if (requested_features.use_hair) { - group_descs[PG_HITD].hitgroup.moduleIS = optix_module; - group_descs[PG_HITS].hitgroup.moduleIS = optix_module; - - // Add curve intersection programs - if (requested_features.use_hair_thick) { - // Slower programs for thick hair since that also slows down ribbons. - // Ideally this should not be needed. - group_descs[PG_HITD].hitgroup.entryFunctionNameIS = "__intersection__curve_all"; - group_descs[PG_HITS].hitgroup.entryFunctionNameIS = "__intersection__curve_all"; - } - else { - group_descs[PG_HITD].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon"; - group_descs[PG_HITS].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon"; - } - -# if OPTIX_ABI_VERSION >= 36 - if (DebugFlags().optix.curves_api && requested_features.use_hair_thick) { - OptixBuiltinISOptions builtin_options = {}; - builtin_options.builtinISModuleType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE; - builtin_options.usesMotionBlur = false; - - check_result_optix_ret(optixBuiltinISModuleGet( - context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[0])); - - group_descs[PG_HITD].hitgroup.moduleIS = builtin_modules[0]; - group_descs[PG_HITD].hitgroup.entryFunctionNameIS = nullptr; - group_descs[PG_HITS].hitgroup.moduleIS = builtin_modules[0]; - group_descs[PG_HITS].hitgroup.entryFunctionNameIS = nullptr; - - if (motion_blur) { - builtin_options.usesMotionBlur = true; - - check_result_optix_ret(optixBuiltinISModuleGet( - context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[1])); - - group_descs[PG_HITD_MOTION] = group_descs[PG_HITD]; - group_descs[PG_HITD_MOTION].hitgroup.moduleIS = builtin_modules[1]; - group_descs[PG_HITS_MOTION] = group_descs[PG_HITS]; - group_descs[PG_HITS_MOTION].hitgroup.moduleIS = builtin_modules[1]; - } - } -# endif - } - - if (requested_features.use_subsurface || requested_features.use_shader_raytrace) { - // Add hit group for local intersections - group_descs[PG_HITL].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; - group_descs[PG_HITL].hitgroup.moduleAH = optix_module; - group_descs[PG_HITL].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_local_hit"; - } - - if (requested_features.use_baking) { - group_descs[PG_BAKE].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; - group_descs[PG_BAKE].raygen.module = optix_module; - group_descs[PG_BAKE].raygen.entryFunctionName = "__raygen__kernel_optix_bake"; - } - - if (requested_features.use_true_displacement) { - group_descs[PG_DISP].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; - group_descs[PG_DISP].raygen.module = optix_module; - group_descs[PG_DISP].raygen.entryFunctionName = "__raygen__kernel_optix_displace"; - } - - if (requested_features.use_background_light) { - group_descs[PG_BACK].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; - group_descs[PG_BACK].raygen.module = optix_module; - group_descs[PG_BACK].raygen.entryFunctionName = "__raygen__kernel_optix_background"; - } - - // Shader raytracing replaces some functions with direct callables - if (requested_features.use_shader_raytrace) { - group_descs[PG_CALL + 0].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; - group_descs[PG_CALL + 0].callables.moduleDC = optix_module; - group_descs[PG_CALL + 0].callables.entryFunctionNameDC = "__direct_callable__svm_eval_nodes"; - group_descs[PG_CALL + 1].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; - group_descs[PG_CALL + 1].callables.moduleDC = optix_module; - group_descs[PG_CALL + 1].callables.entryFunctionNameDC = - "__direct_callable__kernel_volume_shadow"; - group_descs[PG_CALL + 2].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; - group_descs[PG_CALL + 2].callables.moduleDC = optix_module; - group_descs[PG_CALL + 2].callables.entryFunctionNameDC = - "__direct_callable__subsurface_scatter_multi_setup"; - } - - check_result_optix_ret(optixProgramGroupCreate( - context, group_descs, NUM_PROGRAM_GROUPS, &group_options, nullptr, 0, groups)); - - // Get program stack sizes - OptixStackSizes stack_size[NUM_PROGRAM_GROUPS] = {}; - // Set up SBT, which in this case is used only to select between different programs - sbt_data.alloc(NUM_PROGRAM_GROUPS); - memset(sbt_data.host_pointer, 0, sizeof(SbtRecord) * NUM_PROGRAM_GROUPS); - for (unsigned int i = 0; i < NUM_PROGRAM_GROUPS; ++i) { - check_result_optix_ret(optixSbtRecordPackHeader(groups[i], &sbt_data[i])); - check_result_optix_ret(optixProgramGroupGetStackSize(groups[i], &stack_size[i])); - } - sbt_data.copy_to_device(); // Upload SBT to device - - // Calculate maximum trace continuation stack size - unsigned int trace_css = stack_size[PG_HITD].cssCH; - // This is based on the maximum of closest-hit and any-hit/intersection programs - trace_css = std::max(trace_css, stack_size[PG_HITD].cssIS + stack_size[PG_HITD].cssAH); - trace_css = std::max(trace_css, stack_size[PG_HITS].cssIS + stack_size[PG_HITS].cssAH); - trace_css = std::max(trace_css, stack_size[PG_HITL].cssIS + stack_size[PG_HITL].cssAH); -# if OPTIX_ABI_VERSION >= 36 - trace_css = std::max(trace_css, - stack_size[PG_HITD_MOTION].cssIS + stack_size[PG_HITD_MOTION].cssAH); - trace_css = std::max(trace_css, - stack_size[PG_HITS_MOTION].cssIS + stack_size[PG_HITS_MOTION].cssAH); -# endif - - OptixPipelineLinkOptions link_options = {}; - link_options.maxTraceDepth = 1; - link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; -# if OPTIX_ABI_VERSION < 24 - link_options.overrideUsesMotionBlur = motion_blur; -# endif - - { // Create path tracing pipeline - vector pipeline_groups; - pipeline_groups.reserve(NUM_PROGRAM_GROUPS); - pipeline_groups.push_back(groups[PG_RGEN]); - pipeline_groups.push_back(groups[PG_MISS]); - pipeline_groups.push_back(groups[PG_HITD]); - pipeline_groups.push_back(groups[PG_HITS]); - pipeline_groups.push_back(groups[PG_HITL]); -# if OPTIX_ABI_VERSION >= 36 - if (motion_blur) { - pipeline_groups.push_back(groups[PG_HITD_MOTION]); - pipeline_groups.push_back(groups[PG_HITS_MOTION]); - } -# endif - if (requested_features.use_shader_raytrace) { - pipeline_groups.push_back(groups[PG_CALL + 0]); - pipeline_groups.push_back(groups[PG_CALL + 1]); - pipeline_groups.push_back(groups[PG_CALL + 2]); - } - - check_result_optix_ret(optixPipelineCreate(context, - &pipeline_options, - &link_options, - pipeline_groups.data(), - pipeline_groups.size(), - nullptr, - 0, - &pipelines[PIP_PATH_TRACE])); - - // Combine ray generation and trace continuation stack size - const unsigned int css = stack_size[PG_RGEN].cssRG + link_options.maxTraceDepth * trace_css; - // Max direct callable depth is one of the following, so combine accordingly - // - __raygen__ -> svm_eval_nodes - // - __raygen__ -> kernel_volume_shadow -> svm_eval_nodes - // - __raygen__ -> subsurface_scatter_multi_setup -> svm_eval_nodes - const unsigned int dss = stack_size[PG_CALL + 0].dssDC + - std::max(stack_size[PG_CALL + 1].dssDC, - stack_size[PG_CALL + 2].dssDC); - - // Set stack size depending on pipeline options - check_result_optix_ret( - optixPipelineSetStackSize(pipelines[PIP_PATH_TRACE], - 0, - requested_features.use_shader_raytrace ? dss : 0, - css, - motion_blur ? 3 : 2)); - } - - // Only need to create shader evaluation pipeline if one of these features is used: - const bool use_shader_eval_pipeline = requested_features.use_baking || - requested_features.use_background_light || - requested_features.use_true_displacement; - - if (use_shader_eval_pipeline) { // Create shader evaluation pipeline - vector pipeline_groups; - pipeline_groups.reserve(NUM_PROGRAM_GROUPS); - pipeline_groups.push_back(groups[PG_BAKE]); - pipeline_groups.push_back(groups[PG_DISP]); - pipeline_groups.push_back(groups[PG_BACK]); - pipeline_groups.push_back(groups[PG_MISS]); - pipeline_groups.push_back(groups[PG_HITD]); - pipeline_groups.push_back(groups[PG_HITS]); - pipeline_groups.push_back(groups[PG_HITL]); -# if OPTIX_ABI_VERSION >= 36 - if (motion_blur) { - pipeline_groups.push_back(groups[PG_HITD_MOTION]); - pipeline_groups.push_back(groups[PG_HITS_MOTION]); - } -# endif - if (requested_features.use_shader_raytrace) { - pipeline_groups.push_back(groups[PG_CALL + 0]); - pipeline_groups.push_back(groups[PG_CALL + 1]); - pipeline_groups.push_back(groups[PG_CALL + 2]); - } - - check_result_optix_ret(optixPipelineCreate(context, - &pipeline_options, - &link_options, - pipeline_groups.data(), - pipeline_groups.size(), - nullptr, - 0, - &pipelines[PIP_SHADER_EVAL])); - - // Calculate continuation stack size based on the maximum of all ray generation stack sizes - const unsigned int css = std::max(stack_size[PG_BAKE].cssRG, - std::max(stack_size[PG_DISP].cssRG, - stack_size[PG_BACK].cssRG)) + - link_options.maxTraceDepth * trace_css; - const unsigned int dss = stack_size[PG_CALL + 0].dssDC + - std::max(stack_size[PG_CALL + 1].dssDC, - stack_size[PG_CALL + 2].dssDC); - - check_result_optix_ret( - optixPipelineSetStackSize(pipelines[PIP_SHADER_EVAL], - 0, - requested_features.use_shader_raytrace ? dss : 0, - css, - motion_blur ? 3 : 2)); - } - - // Clean up program group objects - for (unsigned int i = 0; i < NUM_PROGRAM_GROUPS; ++i) { - optixProgramGroupDestroy(groups[i]); - } - - return true; - } - - void thread_run(DeviceTask &task, int thread_index) // Main task entry point - { - if (have_error()) - return; // Abort early if there was an error previously - - if (task.type == DeviceTask::RENDER) { - if (thread_index != 0) { - // Only execute denoising in a single thread (see also 'task_add') - task.tile_types &= ~RenderTile::DENOISE; - } - - RenderTile tile; - while (task.acquire_tile(this, tile, task.tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) - launch_render(task, tile, thread_index); - else if (tile.task == RenderTile::BAKE) { - // Perform baking using CUDA, since it is not currently implemented in OptiX - device_vector work_tiles(this, "work_tiles", MEM_READ_ONLY); - CUDADevice::render(task, tile, work_tiles); - } - else if (tile.task == RenderTile::DENOISE) - launch_denoise(task, tile); - task.release_tile(tile); - if (task.get_cancel() && !task.need_finish_queue) - break; // User requested cancellation - else if (have_error()) - break; // Abort rendering when encountering an error - } - } - else if (task.type == DeviceTask::SHADER) { - // CUDA kernels are used when doing baking - if (optix_module == NULL) - CUDADevice::shader(task); - else - launch_shader_eval(task, thread_index); - } - else if (task.type == DeviceTask::DENOISE_BUFFER) { - // Set up a single tile that covers the whole task and denoise it - RenderTile tile; - tile.x = task.x; - tile.y = task.y; - tile.w = task.w; - tile.h = task.h; - tile.buffer = task.buffer; - tile.num_samples = task.num_samples; - tile.start_sample = task.sample; - tile.offset = task.offset; - tile.stride = task.stride; - tile.buffers = task.buffers; - - launch_denoise(task, tile); - } - } - - void launch_render(DeviceTask &task, RenderTile &rtile, int thread_index) - { - assert(thread_index < launch_params.data_size); - - // Keep track of total render time of this tile - const scoped_timer timer(&rtile.buffers->render_time); - - WorkTile wtile; - wtile.x = rtile.x; - wtile.y = rtile.y; - wtile.w = rtile.w; - wtile.h = rtile.h; - wtile.offset = rtile.offset; - wtile.stride = rtile.stride; - wtile.buffer = (float *)rtile.buffer; - - const int end_sample = rtile.start_sample + rtile.num_samples; - // Keep this number reasonable to avoid running into TDRs - int step_samples = (info.display_device ? 8 : 32); - - // Offset into launch params buffer so that streams use separate data - device_ptr launch_params_ptr = launch_params.device_pointer + - thread_index * launch_params.data_elements; - - const CUDAContextScope scope(cuContext); - - for (int sample = rtile.start_sample; sample < end_sample;) { - // Copy work tile information to device - wtile.start_sample = sample; - wtile.num_samples = step_samples; - if (task.adaptive_sampling.use) { - wtile.num_samples = task.adaptive_sampling.align_samples(sample, step_samples); - } - wtile.num_samples = min(wtile.num_samples, end_sample - sample); - device_ptr d_wtile_ptr = launch_params_ptr + offsetof(KernelParams, tile); - check_result_cuda( - cuMemcpyHtoDAsync(d_wtile_ptr, &wtile, sizeof(wtile), cuda_stream[thread_index])); - - OptixShaderBindingTable sbt_params = {}; - sbt_params.raygenRecord = sbt_data.device_pointer + PG_RGEN * sizeof(SbtRecord); - sbt_params.missRecordBase = sbt_data.device_pointer + PG_MISS * sizeof(SbtRecord); - sbt_params.missRecordStrideInBytes = sizeof(SbtRecord); - sbt_params.missRecordCount = 1; - sbt_params.hitgroupRecordBase = sbt_data.device_pointer + PG_HITD * sizeof(SbtRecord); - sbt_params.hitgroupRecordStrideInBytes = sizeof(SbtRecord); -# if OPTIX_ABI_VERSION >= 36 - sbt_params.hitgroupRecordCount = 5; // PG_HITD(_MOTION), PG_HITS(_MOTION), PG_HITL -# else - sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITS, PG_HITL -# endif - sbt_params.callablesRecordBase = sbt_data.device_pointer + PG_CALL * sizeof(SbtRecord); - sbt_params.callablesRecordCount = 3; - sbt_params.callablesRecordStrideInBytes = sizeof(SbtRecord); - - // Launch the ray generation program - check_result_optix(optixLaunch(pipelines[PIP_PATH_TRACE], - cuda_stream[thread_index], - launch_params_ptr, - launch_params.data_elements, - &sbt_params, - // Launch with samples close to each other for better locality - wtile.w * wtile.num_samples, - wtile.h, - 1)); - - // Run the adaptive sampling kernels at selected samples aligned to step samples. - uint filter_sample = wtile.start_sample + wtile.num_samples - 1; - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) { - adaptive_sampling_filter(filter_sample, &wtile, d_wtile_ptr, cuda_stream[thread_index]); - } - - // Wait for launch to finish - check_result_cuda(cuStreamSynchronize(cuda_stream[thread_index])); - - // Update current sample, so it is displayed correctly - sample += wtile.num_samples; - rtile.sample = sample; - // Update task progress after the kernel completed rendering - task.update_progress(&rtile, wtile.w * wtile.h * wtile.num_samples); - - if (task.get_cancel() && !task.need_finish_queue) - return; // Cancel rendering - } - - // Finalize adaptive sampling - if (task.adaptive_sampling.use) { - device_ptr d_wtile_ptr = launch_params_ptr + offsetof(KernelParams, tile); - adaptive_sampling_post(rtile, &wtile, d_wtile_ptr, cuda_stream[thread_index]); - check_result_cuda(cuStreamSynchronize(cuda_stream[thread_index])); - task.update_progress(&rtile, rtile.w * rtile.h * wtile.num_samples); - } - } - - bool launch_denoise(DeviceTask &task, RenderTile &rtile) - { - // Update current sample (for display and NLM denoising task) - rtile.sample = rtile.start_sample + rtile.num_samples; - - // Make CUDA context current now, since it is used for both denoising tasks - const CUDAContextScope scope(cuContext); - - // Choose between OptiX and NLM denoising - if (task.denoising.type == DENOISER_OPTIX) { - // Map neighboring tiles onto this device, indices are as following: - // Where index 4 is the center tile and index 9 is the target for the result. - // 0 1 2 - // 3 4 5 - // 6 7 8 9 - RenderTileNeighbors neighbors(rtile); - task.map_neighbor_tiles(neighbors, this); - RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; - RenderTile &target_tile = neighbors.target; - rtile = center_tile; // Tile may have been modified by mapping code - - // Calculate size of the tile to denoise (including overlap) - int4 rect = center_tile.bounds(); - // Overlap between tiles has to be at least 64 pixels - // TODO(pmours): Query this value from OptiX - rect = rect_expand(rect, 64); - int4 clip_rect = neighbors.bounds(); - rect = rect_clip(rect, clip_rect); - int2 rect_size = make_int2(rect.z - rect.x, rect.w - rect.y); - int2 overlap_offset = make_int2(rtile.x - rect.x, rtile.y - rect.y); - - // Calculate byte offsets and strides - int pixel_stride = task.pass_stride * (int)sizeof(float); - int pixel_offset = (rtile.offset + rtile.x + rtile.y * rtile.stride) * pixel_stride; - const int pass_offset[3] = { - (task.pass_denoising_data + DENOISING_PASS_COLOR) * (int)sizeof(float), - (task.pass_denoising_data + DENOISING_PASS_ALBEDO) * (int)sizeof(float), - (task.pass_denoising_data + DENOISING_PASS_NORMAL) * (int)sizeof(float)}; - - // Start with the current tile pointer offset - int input_stride = pixel_stride; - device_ptr input_ptr = rtile.buffer + pixel_offset; - - // Copy tile data into a common buffer if necessary - device_only_memory input(this, "denoiser input", true); - device_vector tile_info_mem(this, "denoiser tile info", MEM_READ_ONLY); - - bool contiguous_memory = true; - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - if (neighbors.tiles[i].buffer && neighbors.tiles[i].buffer != rtile.buffer) { - contiguous_memory = false; - } - } - - if (contiguous_memory) { - // Tiles are in continous memory, so can just subtract overlap offset - input_ptr -= (overlap_offset.x + overlap_offset.y * rtile.stride) * pixel_stride; - // Stride covers the whole width of the image and not just a single tile - input_stride *= rtile.stride; - } - else { - // Adjacent tiles are in separate memory regions, so need to copy them into a single one - input.alloc_to_device(rect_size.x * rect_size.y * task.pass_stride); - // Start with the new input buffer - input_ptr = input.device_pointer; - // Stride covers the width of the new input buffer, which includes tile width and overlap - input_stride *= rect_size.x; - - TileInfo *tile_info = tile_info_mem.alloc(1); - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - tile_info->offsets[i] = neighbors.tiles[i].offset; - tile_info->strides[i] = neighbors.tiles[i].stride; - tile_info->buffers[i] = neighbors.tiles[i].buffer; - } - tile_info->x[0] = neighbors.tiles[3].x; - tile_info->x[1] = neighbors.tiles[4].x; - tile_info->x[2] = neighbors.tiles[5].x; - tile_info->x[3] = neighbors.tiles[5].x + neighbors.tiles[5].w; - tile_info->y[0] = neighbors.tiles[1].y; - tile_info->y[1] = neighbors.tiles[4].y; - tile_info->y[2] = neighbors.tiles[7].y; - tile_info->y[3] = neighbors.tiles[7].y + neighbors.tiles[7].h; - tile_info_mem.copy_to_device(); - - void *args[] = { - &input.device_pointer, &tile_info_mem.device_pointer, &rect.x, &task.pass_stride}; - launch_filter_kernel("kernel_cuda_filter_copy_input", rect_size.x, rect_size.y, args); - } - -# if OPTIX_DENOISER_NO_PIXEL_STRIDE - device_only_memory input_rgb(this, "denoiser input rgb", true); - input_rgb.alloc_to_device(rect_size.x * rect_size.y * 3 * task.denoising.input_passes); - - void *input_args[] = {&input_rgb.device_pointer, - &input_ptr, - &rect_size.x, - &rect_size.y, - &input_stride, - &task.pass_stride, - const_cast(pass_offset), - &task.denoising.input_passes, - &rtile.sample}; - launch_filter_kernel( - "kernel_cuda_filter_convert_to_rgb", rect_size.x, rect_size.y, input_args); - - input_ptr = input_rgb.device_pointer; - pixel_stride = 3 * sizeof(float); - input_stride = rect_size.x * pixel_stride; -# endif - - const bool recreate_denoiser = (denoiser == NULL) || - (task.denoising.input_passes != denoiser_input_passes); - if (recreate_denoiser) { - // Destroy existing handle before creating new one - if (denoiser != NULL) { - optixDenoiserDestroy(denoiser); - } - - // Create OptiX denoiser handle on demand when it is first used - OptixDenoiserOptions denoiser_options = {}; - assert(task.denoising.input_passes >= 1 && task.denoising.input_passes <= 3); -# if OPTIX_ABI_VERSION >= 47 - denoiser_options.guideAlbedo = task.denoising.input_passes >= 2; - denoiser_options.guideNormal = task.denoising.input_passes >= 3; - check_result_optix_ret(optixDenoiserCreate( - context, OPTIX_DENOISER_MODEL_KIND_HDR, &denoiser_options, &denoiser)); -# else - denoiser_options.inputKind = static_cast( - OPTIX_DENOISER_INPUT_RGB + (task.denoising.input_passes - 1)); -# if OPTIX_ABI_VERSION < 28 - denoiser_options.pixelFormat = OPTIX_PIXEL_FORMAT_FLOAT3; -# endif - check_result_optix_ret(optixDenoiserCreate(context, &denoiser_options, &denoiser)); - check_result_optix_ret( - optixDenoiserSetModel(denoiser, OPTIX_DENOISER_MODEL_KIND_HDR, NULL, 0)); -# endif - - // OptiX denoiser handle was created with the requested number of input passes - denoiser_input_passes = task.denoising.input_passes; - } - - OptixDenoiserSizes sizes = {}; - check_result_optix_ret( - optixDenoiserComputeMemoryResources(denoiser, rect_size.x, rect_size.y, &sizes)); - -# if OPTIX_ABI_VERSION < 28 - const size_t scratch_size = sizes.recommendedScratchSizeInBytes; -# else - const size_t scratch_size = sizes.withOverlapScratchSizeInBytes; -# endif - const size_t scratch_offset = sizes.stateSizeInBytes; - - // Allocate denoiser state if tile size has changed since last setup - if (recreate_denoiser || (denoiser_state.data_width != rect_size.x || - denoiser_state.data_height != rect_size.y)) { - denoiser_state.alloc_to_device(scratch_offset + scratch_size); - - // Initialize denoiser state for the current tile size - check_result_optix_ret(optixDenoiserSetup(denoiser, - 0, - rect_size.x, - rect_size.y, - denoiser_state.device_pointer, - scratch_offset, - denoiser_state.device_pointer + scratch_offset, - scratch_size)); - - denoiser_state.data_width = rect_size.x; - denoiser_state.data_height = rect_size.y; - } - - // Set up input and output layer information - OptixImage2D input_layers[3] = {}; - OptixImage2D output_layers[1] = {}; - - for (int i = 0; i < 3; ++i) { -# if OPTIX_DENOISER_NO_PIXEL_STRIDE - input_layers[i].data = input_ptr + (rect_size.x * rect_size.y * pixel_stride * i); -# else - input_layers[i].data = input_ptr + pass_offset[i]; -# endif - input_layers[i].width = rect_size.x; - input_layers[i].height = rect_size.y; - input_layers[i].rowStrideInBytes = input_stride; - input_layers[i].pixelStrideInBytes = pixel_stride; - input_layers[i].format = OPTIX_PIXEL_FORMAT_FLOAT3; - } - -# if OPTIX_DENOISER_NO_PIXEL_STRIDE - output_layers[0].data = input_ptr; - output_layers[0].width = rect_size.x; - output_layers[0].height = rect_size.y; - output_layers[0].rowStrideInBytes = input_stride; - output_layers[0].pixelStrideInBytes = pixel_stride; - int2 output_offset = overlap_offset; - overlap_offset = make_int2(0, 0); // Not supported by denoiser API, so apply manually -# else - output_layers[0].data = target_tile.buffer + pixel_offset; - output_layers[0].width = target_tile.w; - output_layers[0].height = target_tile.h; - output_layers[0].rowStrideInBytes = target_tile.stride * pixel_stride; - output_layers[0].pixelStrideInBytes = pixel_stride; -# endif - output_layers[0].format = OPTIX_PIXEL_FORMAT_FLOAT3; - -# if OPTIX_ABI_VERSION >= 47 - OptixDenoiserLayer image_layers = {}; - image_layers.input = input_layers[0]; - image_layers.output = output_layers[0]; - - OptixDenoiserGuideLayer guide_layers = {}; - guide_layers.albedo = input_layers[1]; - guide_layers.normal = input_layers[2]; -# endif - - // Finally run denonising - OptixDenoiserParams params = {}; // All parameters are disabled/zero -# if OPTIX_ABI_VERSION >= 47 - check_result_optix_ret(optixDenoiserInvoke(denoiser, - NULL, - ¶ms, - denoiser_state.device_pointer, - scratch_offset, - &guide_layers, - &image_layers, - 1, - overlap_offset.x, - overlap_offset.y, - denoiser_state.device_pointer + scratch_offset, - scratch_size)); -# else - check_result_optix_ret(optixDenoiserInvoke(denoiser, - NULL, - ¶ms, - denoiser_state.device_pointer, - scratch_offset, - input_layers, - task.denoising.input_passes, - overlap_offset.x, - overlap_offset.y, - output_layers, - denoiser_state.device_pointer + scratch_offset, - scratch_size)); -# endif - -# if OPTIX_DENOISER_NO_PIXEL_STRIDE - void *output_args[] = {&input_ptr, - &target_tile.buffer, - &output_offset.x, - &output_offset.y, - &rect_size.x, - &rect_size.y, - &target_tile.x, - &target_tile.y, - &target_tile.w, - &target_tile.h, - &target_tile.offset, - &target_tile.stride, - &task.pass_stride, - &rtile.sample}; - launch_filter_kernel( - "kernel_cuda_filter_convert_from_rgb", target_tile.w, target_tile.h, output_args); -# endif - - check_result_cuda_ret(cuStreamSynchronize(0)); - - task.unmap_neighbor_tiles(neighbors, this); - } - else { - // Run CUDA denoising kernels - DenoisingTask denoising(this, task); - CUDADevice::denoise(rtile, denoising); - } - - // Update task progress after the denoiser completed processing - task.update_progress(&rtile, rtile.w * rtile.h); - - return true; - } - - void launch_shader_eval(DeviceTask &task, int thread_index) - { - unsigned int rgen_index = PG_BACK; - if (task.shader_eval_type >= SHADER_EVAL_BAKE) - rgen_index = PG_BAKE; - if (task.shader_eval_type == SHADER_EVAL_DISPLACE) - rgen_index = PG_DISP; - - const CUDAContextScope scope(cuContext); - - device_ptr launch_params_ptr = launch_params.device_pointer + - thread_index * launch_params.data_elements; - - for (int sample = 0; sample < task.num_samples; ++sample) { - ShaderParams params; - params.input = (uint4 *)task.shader_input; - params.output = (float4 *)task.shader_output; - params.type = task.shader_eval_type; - params.filter = task.shader_filter; - params.sx = task.shader_x; - params.offset = task.offset; - params.sample = sample; - - check_result_cuda(cuMemcpyHtoDAsync(launch_params_ptr + offsetof(KernelParams, shader), - ¶ms, - sizeof(params), - cuda_stream[thread_index])); - - OptixShaderBindingTable sbt_params = {}; - sbt_params.raygenRecord = sbt_data.device_pointer + rgen_index * sizeof(SbtRecord); - sbt_params.missRecordBase = sbt_data.device_pointer + PG_MISS * sizeof(SbtRecord); - sbt_params.missRecordStrideInBytes = sizeof(SbtRecord); - sbt_params.missRecordCount = 1; - sbt_params.hitgroupRecordBase = sbt_data.device_pointer + PG_HITD * sizeof(SbtRecord); - sbt_params.hitgroupRecordStrideInBytes = sizeof(SbtRecord); -# if OPTIX_ABI_VERSION >= 36 - sbt_params.hitgroupRecordCount = 5; // PG_HITD(_MOTION), PG_HITS(_MOTION), PG_HITL -# else - sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITS, PG_HITL -# endif - sbt_params.callablesRecordBase = sbt_data.device_pointer + PG_CALL * sizeof(SbtRecord); - sbt_params.callablesRecordCount = 3; - sbt_params.callablesRecordStrideInBytes = sizeof(SbtRecord); - - check_result_optix(optixLaunch(pipelines[PIP_SHADER_EVAL], - cuda_stream[thread_index], - launch_params_ptr, - launch_params.data_elements, - &sbt_params, - task.shader_w, - 1, - 1)); - - check_result_cuda(cuStreamSynchronize(cuda_stream[thread_index])); - - task.update_progress(NULL); - } - } - - bool build_optix_bvh(BVHOptiX *bvh, - OptixBuildOperation operation, - const OptixBuildInput &build_input, - uint16_t num_motion_steps) - { - /* Allocate and build acceleration structures only one at a time, to prevent parallel builds - * from running out of memory (since both original and compacted acceleration structure memory - * may be allocated at the same time for the duration of this function). The builds would - * otherwise happen on the same CUDA stream anyway. */ - static thread_mutex mutex; - thread_scoped_lock lock(mutex); - - const CUDAContextScope scope(cuContext); - - const bool use_fast_trace_bvh = (bvh->params.bvh_type == SceneParams::BVH_STATIC); - - // Compute memory usage - OptixAccelBufferSizes sizes = {}; - OptixAccelBuildOptions options = {}; - options.operation = operation; - if (use_fast_trace_bvh) { - VLOG(2) << "Using fast to trace OptiX BVH"; - options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_TRACE | OPTIX_BUILD_FLAG_ALLOW_COMPACTION; - } - else { - VLOG(2) << "Using fast to update OptiX BVH"; - options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_BUILD | OPTIX_BUILD_FLAG_ALLOW_UPDATE; - } - - options.motionOptions.numKeys = num_motion_steps; - options.motionOptions.flags = OPTIX_MOTION_FLAG_START_VANISH | OPTIX_MOTION_FLAG_END_VANISH; - options.motionOptions.timeBegin = 0.0f; - options.motionOptions.timeEnd = 1.0f; - - check_result_optix_ret( - optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes)); - - // Allocate required output buffers - device_only_memory temp_mem(this, "optix temp as build mem", true); - temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8); - if (!temp_mem.device_pointer) - return false; // Make sure temporary memory allocation succeeded - - // Acceleration structure memory has to be allocated on the device (not allowed to be on host) - device_only_memory &out_data = bvh->as_data; - if (operation == OPTIX_BUILD_OPERATION_BUILD) { - assert(out_data.device == this); - out_data.alloc_to_device(sizes.outputSizeInBytes); - if (!out_data.device_pointer) - return false; - } - else { - assert(out_data.device_pointer && out_data.device_size >= sizes.outputSizeInBytes); - } - - // Finally build the acceleration structure - OptixAccelEmitDesc compacted_size_prop = {}; - compacted_size_prop.type = OPTIX_PROPERTY_TYPE_COMPACTED_SIZE; - // A tiny space was allocated for this property at the end of the temporary buffer above - // Make sure this pointer is 8-byte aligned - compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8); - - OptixTraversableHandle out_handle = 0; - check_result_optix_ret(optixAccelBuild(context, - NULL, - &options, - &build_input, - 1, - temp_mem.device_pointer, - sizes.tempSizeInBytes, - out_data.device_pointer, - sizes.outputSizeInBytes, - &out_handle, - use_fast_trace_bvh ? &compacted_size_prop : NULL, - use_fast_trace_bvh ? 1 : 0)); - bvh->traversable_handle = static_cast(out_handle); - - // Wait for all operations to finish - check_result_cuda_ret(cuStreamSynchronize(NULL)); - - // Compact acceleration structure to save memory (only if using fast trace as the - // OPTIX_BUILD_FLAG_ALLOW_COMPACTION flag is only set in this case). - if (use_fast_trace_bvh) { - uint64_t compacted_size = sizes.outputSizeInBytes; - check_result_cuda_ret( - cuMemcpyDtoH(&compacted_size, compacted_size_prop.result, sizeof(compacted_size))); - - // Temporary memory is no longer needed, so free it now to make space - temp_mem.free(); - - // There is no point compacting if the size does not change - if (compacted_size < sizes.outputSizeInBytes) { - device_only_memory compacted_data(this, "optix compacted as", false); - compacted_data.alloc_to_device(compacted_size); - if (!compacted_data.device_pointer) - // Do not compact if memory allocation for compacted acceleration structure fails - // Can just use the uncompacted one then, so succeed here regardless - return true; - - check_result_optix_ret(optixAccelCompact(context, - NULL, - out_handle, - compacted_data.device_pointer, - compacted_size, - &out_handle)); - bvh->traversable_handle = static_cast(out_handle); - - // Wait for compaction to finish - check_result_cuda_ret(cuStreamSynchronize(NULL)); - - std::swap(out_data.device_size, compacted_data.device_size); - std::swap(out_data.device_pointer, compacted_data.device_pointer); - // Original acceleration structure memory is freed when 'compacted_data' goes out of scope - } - } - - return true; - } - - void build_bvh(BVH *bvh, Progress &progress, bool refit) override - { - if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2) { - /* For baking CUDA is used, build appropriate BVH for that. */ - Device::build_bvh(bvh, progress, refit); - return; - } - - const bool use_fast_trace_bvh = (bvh->params.bvh_type == SceneParams::BVH_STATIC); - - free_bvh_memory_delayed(); - - BVHOptiX *const bvh_optix = static_cast(bvh); - - progress.set_substatus("Building OptiX acceleration structure"); - - if (!bvh->params.top_level) { - assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1); - - OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD; - /* Refit is only possible when using fast to trace BVH (because AS is built with - * OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above). */ - if (refit && !use_fast_trace_bvh) { - assert(bvh_optix->traversable_handle != 0); - operation = OPTIX_BUILD_OPERATION_UPDATE; - } - else { - bvh_optix->as_data.free(); - bvh_optix->traversable_handle = 0; - } - - // Build bottom level acceleration structures (BLAS) - Geometry *const geom = bvh->geometry[0]; - if (geom->geometry_type == Geometry::HAIR) { - // Build BLAS for curve primitives - Hair *const hair = static_cast(geom); - if (hair->num_curves() == 0) { - return; - } - - const size_t num_segments = hair->num_segments(); - - size_t num_motion_steps = 1; - Attribute *motion_keys = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - if (motion_blur && hair->get_use_motion_blur() && motion_keys) { - num_motion_steps = hair->get_motion_steps(); - } - - device_vector aabb_data(this, "optix temp aabb data", MEM_READ_ONLY); -# if OPTIX_ABI_VERSION >= 36 - device_vector index_data(this, "optix temp index data", MEM_READ_ONLY); - device_vector vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); - // Four control points for each curve segment - const size_t num_vertices = num_segments * 4; - if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { - index_data.alloc(num_segments); - vertex_data.alloc(num_vertices * num_motion_steps); - } - else -# endif - aabb_data.alloc(num_segments * num_motion_steps); - - // Get AABBs for each motion step - for (size_t step = 0; step < num_motion_steps; ++step) { - // The center step for motion vertices is not stored in the attribute - const float3 *keys = hair->get_curve_keys().data(); - size_t center_step = (num_motion_steps - 1) / 2; - if (step != center_step) { - size_t attr_offset = (step > center_step) ? step - 1 : step; - // Technically this is a float4 array, but sizeof(float3) == sizeof(float4) - keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size(); - } - - for (size_t j = 0, i = 0; j < hair->num_curves(); ++j) { - const Hair::Curve curve = hair->get_curve(j); -# if OPTIX_ABI_VERSION >= 36 - const array &curve_radius = hair->get_curve_radius(); -# endif - - for (int segment = 0; segment < curve.num_segments(); ++segment, ++i) { -# if OPTIX_ABI_VERSION >= 36 - if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { - int k0 = curve.first_key + segment; - int k1 = k0 + 1; - int ka = max(k0 - 1, curve.first_key); - int kb = min(k1 + 1, curve.first_key + curve.num_keys - 1); - - const float4 px = make_float4(keys[ka].x, keys[k0].x, keys[k1].x, keys[kb].x); - const float4 py = make_float4(keys[ka].y, keys[k0].y, keys[k1].y, keys[kb].y); - const float4 pz = make_float4(keys[ka].z, keys[k0].z, keys[k1].z, keys[kb].z); - const float4 pw = make_float4( - curve_radius[ka], curve_radius[k0], curve_radius[k1], curve_radius[kb]); - - // Convert Catmull-Rom data to Bezier spline - static const float4 cr2bsp0 = make_float4(+7, -4, +5, -2) / 6.f; - static const float4 cr2bsp1 = make_float4(-2, 11, -4, +1) / 6.f; - static const float4 cr2bsp2 = make_float4(+1, -4, 11, -2) / 6.f; - static const float4 cr2bsp3 = make_float4(-2, +5, -4, +7) / 6.f; - - index_data[i] = i * 4; - float4 *const v = vertex_data.data() + step * num_vertices + index_data[i]; - v[0] = make_float4( - dot(cr2bsp0, px), dot(cr2bsp0, py), dot(cr2bsp0, pz), dot(cr2bsp0, pw)); - v[1] = make_float4( - dot(cr2bsp1, px), dot(cr2bsp1, py), dot(cr2bsp1, pz), dot(cr2bsp1, pw)); - v[2] = make_float4( - dot(cr2bsp2, px), dot(cr2bsp2, py), dot(cr2bsp2, pz), dot(cr2bsp2, pw)); - v[3] = make_float4( - dot(cr2bsp3, px), dot(cr2bsp3, py), dot(cr2bsp3, pz), dot(cr2bsp3, pw)); - } - else -# endif - { - BoundBox bounds = BoundBox::empty; - curve.bounds_grow(segment, keys, hair->get_curve_radius().data(), bounds); - - const size_t index = step * num_segments + i; - aabb_data[index].minX = bounds.min.x; - aabb_data[index].minY = bounds.min.y; - aabb_data[index].minZ = bounds.min.z; - aabb_data[index].maxX = bounds.max.x; - aabb_data[index].maxY = bounds.max.y; - aabb_data[index].maxZ = bounds.max.z; - } - } - } - } - - // Upload AABB data to GPU - aabb_data.copy_to_device(); -# if OPTIX_ABI_VERSION >= 36 - index_data.copy_to_device(); - vertex_data.copy_to_device(); -# endif - - vector aabb_ptrs; - aabb_ptrs.reserve(num_motion_steps); -# if OPTIX_ABI_VERSION >= 36 - vector width_ptrs; - vector vertex_ptrs; - width_ptrs.reserve(num_motion_steps); - vertex_ptrs.reserve(num_motion_steps); -# endif - for (size_t step = 0; step < num_motion_steps; ++step) { - aabb_ptrs.push_back(aabb_data.device_pointer + step * num_segments * sizeof(OptixAabb)); -# if OPTIX_ABI_VERSION >= 36 - const device_ptr base_ptr = vertex_data.device_pointer + - step * num_vertices * sizeof(float4); - width_ptrs.push_back(base_ptr + 3 * sizeof(float)); // Offset by vertex size - vertex_ptrs.push_back(base_ptr); -# endif - } - - // Force a single any-hit call, so shadow record-all behavior works correctly - unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL; - OptixBuildInput build_input = {}; -# if OPTIX_ABI_VERSION >= 36 - if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { - build_input.type = OPTIX_BUILD_INPUT_TYPE_CURVES; - build_input.curveArray.curveType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE; - build_input.curveArray.numPrimitives = num_segments; - build_input.curveArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data(); - build_input.curveArray.numVertices = num_vertices; - build_input.curveArray.vertexStrideInBytes = sizeof(float4); - build_input.curveArray.widthBuffers = (CUdeviceptr *)width_ptrs.data(); - build_input.curveArray.widthStrideInBytes = sizeof(float4); - build_input.curveArray.indexBuffer = (CUdeviceptr)index_data.device_pointer; - build_input.curveArray.indexStrideInBytes = sizeof(int); - build_input.curveArray.flag = build_flags; - build_input.curveArray.primitiveIndexOffset = hair->optix_prim_offset; - } - else -# endif - { - // Disable visibility test any-hit program, since it is already checked during - // intersection. Those trace calls that require anyhit can force it with a ray flag. - build_flags |= OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT; - - build_input.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES; -# if OPTIX_ABI_VERSION < 23 - build_input.aabbArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data(); - build_input.aabbArray.numPrimitives = num_segments; - build_input.aabbArray.strideInBytes = sizeof(OptixAabb); - build_input.aabbArray.flags = &build_flags; - build_input.aabbArray.numSbtRecords = 1; - build_input.aabbArray.primitiveIndexOffset = hair->optix_prim_offset; -# else - build_input.customPrimitiveArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data(); - build_input.customPrimitiveArray.numPrimitives = num_segments; - build_input.customPrimitiveArray.strideInBytes = sizeof(OptixAabb); - build_input.customPrimitiveArray.flags = &build_flags; - build_input.customPrimitiveArray.numSbtRecords = 1; - build_input.customPrimitiveArray.primitiveIndexOffset = hair->optix_prim_offset; -# endif - } - - if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { - progress.set_error("Failed to build OptiX acceleration structure"); - } - } - else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - // Build BLAS for triangle primitives - Mesh *const mesh = static_cast(geom); - if (mesh->num_triangles() == 0) { - return; - } - - const size_t num_verts = mesh->get_verts().size(); - - size_t num_motion_steps = 1; - Attribute *motion_keys = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - if (motion_blur && mesh->get_use_motion_blur() && motion_keys) { - num_motion_steps = mesh->get_motion_steps(); - } - - device_vector index_data(this, "optix temp index data", MEM_READ_ONLY); - index_data.alloc(mesh->get_triangles().size()); - memcpy(index_data.data(), - mesh->get_triangles().data(), - mesh->get_triangles().size() * sizeof(int)); - device_vector vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); - vertex_data.alloc(num_verts * num_motion_steps); - - for (size_t step = 0; step < num_motion_steps; ++step) { - const float3 *verts = mesh->get_verts().data(); - - size_t center_step = (num_motion_steps - 1) / 2; - // The center step for motion vertices is not stored in the attribute - if (step != center_step) { - verts = motion_keys->data_float3() + - (step > center_step ? step - 1 : step) * num_verts; - } - - memcpy(vertex_data.data() + num_verts * step, verts, num_verts * sizeof(float3)); - } - - // Upload triangle data to GPU - index_data.copy_to_device(); - vertex_data.copy_to_device(); - - vector vertex_ptrs; - vertex_ptrs.reserve(num_motion_steps); - for (size_t step = 0; step < num_motion_steps; ++step) { - vertex_ptrs.push_back(vertex_data.device_pointer + num_verts * step * sizeof(float3)); - } - - // Force a single any-hit call, so shadow record-all behavior works correctly - unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL; - OptixBuildInput build_input = {}; - build_input.type = OPTIX_BUILD_INPUT_TYPE_TRIANGLES; - build_input.triangleArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data(); - build_input.triangleArray.numVertices = num_verts; - build_input.triangleArray.vertexFormat = OPTIX_VERTEX_FORMAT_FLOAT3; - build_input.triangleArray.vertexStrideInBytes = sizeof(float3); - build_input.triangleArray.indexBuffer = index_data.device_pointer; - build_input.triangleArray.numIndexTriplets = mesh->num_triangles(); - build_input.triangleArray.indexFormat = OPTIX_INDICES_FORMAT_UNSIGNED_INT3; - build_input.triangleArray.indexStrideInBytes = 3 * sizeof(int); - build_input.triangleArray.flags = &build_flags; - // The SBT does not store per primitive data since Cycles already allocates separate - // buffers for that purpose. OptiX does not allow this to be zero though, so just pass in - // one and rely on that having the same meaning in this case. - build_input.triangleArray.numSbtRecords = 1; - build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset; - - if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { - progress.set_error("Failed to build OptiX acceleration structure"); - } - } - } - else { - unsigned int num_instances = 0; - unsigned int max_num_instances = 0xFFFFFFFF; - - bvh_optix->as_data.free(); - bvh_optix->traversable_handle = 0; - bvh_optix->motion_transform_data.free(); - - optixDeviceContextGetProperty(context, - OPTIX_DEVICE_PROPERTY_LIMIT_MAX_INSTANCE_ID, - &max_num_instances, - sizeof(max_num_instances)); - // Do not count first bit, which is used to distinguish instanced and non-instanced objects - max_num_instances >>= 1; - if (bvh->objects.size() > max_num_instances) { - progress.set_error( - "Failed to build OptiX acceleration structure because there are too many instances"); - return; - } - - // Fill instance descriptions -# if OPTIX_ABI_VERSION < 41 - device_vector aabbs(this, "optix tlas aabbs", MEM_READ_ONLY); - aabbs.alloc(bvh->objects.size()); -# endif - device_vector instances(this, "optix tlas instances", MEM_READ_ONLY); - instances.alloc(bvh->objects.size()); - - // Calculate total motion transform size and allocate memory for them - size_t motion_transform_offset = 0; - if (motion_blur) { - size_t total_motion_transform_size = 0; - for (Object *const ob : bvh->objects) { - if (ob->is_traceable() && ob->use_motion()) { - total_motion_transform_size = align_up(total_motion_transform_size, - OPTIX_TRANSFORM_BYTE_ALIGNMENT); - const size_t motion_keys = max(ob->get_motion().size(), 2) - 2; - total_motion_transform_size = total_motion_transform_size + - sizeof(OptixSRTMotionTransform) + - motion_keys * sizeof(OptixSRTData); - } - } - - assert(bvh_optix->motion_transform_data.device == this); - bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size); - } - - for (Object *ob : bvh->objects) { - // Skip non-traceable objects - if (!ob->is_traceable()) - continue; - - BVHOptiX *const blas = static_cast(ob->get_geometry()->bvh); - OptixTraversableHandle handle = blas->traversable_handle; - -# if OPTIX_ABI_VERSION < 41 - OptixAabb &aabb = aabbs[num_instances]; - aabb.minX = ob->bounds.min.x; - aabb.minY = ob->bounds.min.y; - aabb.minZ = ob->bounds.min.z; - aabb.maxX = ob->bounds.max.x; - aabb.maxY = ob->bounds.max.y; - aabb.maxZ = ob->bounds.max.z; -# endif - - OptixInstance &instance = instances[num_instances++]; - memset(&instance, 0, sizeof(instance)); - - // Clear transform to identity matrix - instance.transform[0] = 1.0f; - instance.transform[5] = 1.0f; - instance.transform[10] = 1.0f; - - // Set user instance ID to object index (but leave low bit blank) - instance.instanceId = ob->get_device_index() << 1; - - // Have to have at least one bit in the mask, or else instance would always be culled - instance.visibilityMask = 1; - - if (ob->get_geometry()->has_volume) { - // Volumes have a special bit set in the visibility mask so a trace can mask only volumes - instance.visibilityMask |= 2; - } - - if (ob->get_geometry()->geometry_type == Geometry::HAIR) { - // Same applies to curves (so they can be skipped in local trace calls) - instance.visibilityMask |= 4; - -# if OPTIX_ABI_VERSION >= 36 - if (motion_blur && ob->get_geometry()->has_motion_blur() && - DebugFlags().optix.curves_api && - static_cast(ob->get_geometry())->curve_shape == CURVE_THICK) { - // Select between motion blur and non-motion blur built-in intersection module - instance.sbtOffset = PG_HITD_MOTION - PG_HITD; - } -# endif - } - - // Insert motion traversable if object has motion - if (motion_blur && ob->use_motion()) { - size_t motion_keys = max(ob->get_motion().size(), 2) - 2; - size_t motion_transform_size = sizeof(OptixSRTMotionTransform) + - motion_keys * sizeof(OptixSRTData); - - const CUDAContextScope scope(cuContext); - - motion_transform_offset = align_up(motion_transform_offset, - OPTIX_TRANSFORM_BYTE_ALIGNMENT); - CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer + - motion_transform_offset; - motion_transform_offset += motion_transform_size; - - // Allocate host side memory for motion transform and fill it with transform data - OptixSRTMotionTransform &motion_transform = *reinterpret_cast( - new uint8_t[motion_transform_size]); - motion_transform.child = handle; - motion_transform.motionOptions.numKeys = ob->get_motion().size(); - motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE; - motion_transform.motionOptions.timeBegin = 0.0f; - motion_transform.motionOptions.timeEnd = 1.0f; - - OptixSRTData *const srt_data = motion_transform.srtData; - array decomp(ob->get_motion().size()); - transform_motion_decompose( - decomp.data(), ob->get_motion().data(), ob->get_motion().size()); - - for (size_t i = 0; i < ob->get_motion().size(); ++i) { - // Scale - srt_data[i].sx = decomp[i].y.w; // scale.x.x - srt_data[i].sy = decomp[i].z.w; // scale.y.y - srt_data[i].sz = decomp[i].w.w; // scale.z.z - - // Shear - srt_data[i].a = decomp[i].z.x; // scale.x.y - srt_data[i].b = decomp[i].z.y; // scale.x.z - srt_data[i].c = decomp[i].w.x; // scale.y.z - assert(decomp[i].z.z == 0.0f); // scale.y.x - assert(decomp[i].w.y == 0.0f); // scale.z.x - assert(decomp[i].w.z == 0.0f); // scale.z.y - - // Pivot point - srt_data[i].pvx = 0.0f; - srt_data[i].pvy = 0.0f; - srt_data[i].pvz = 0.0f; - - // Rotation - srt_data[i].qx = decomp[i].x.x; - srt_data[i].qy = decomp[i].x.y; - srt_data[i].qz = decomp[i].x.z; - srt_data[i].qw = decomp[i].x.w; - - // Translation - srt_data[i].tx = decomp[i].y.x; - srt_data[i].ty = decomp[i].y.y; - srt_data[i].tz = decomp[i].y.z; - } - - // Upload motion transform to GPU - cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size); - delete[] reinterpret_cast(&motion_transform); - - // Disable instance transform if object uses motion transform already - instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; - - // Get traversable handle to motion transform - optixConvertPointerToTraversableHandle(context, - motion_transform_gpu, - OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM, - &instance.traversableHandle); - } - else { - instance.traversableHandle = handle; - - if (ob->get_geometry()->is_instanced()) { - // Set transform matrix - memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform)); - } - else { - // Disable instance transform if geometry already has it applied to vertex data - instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; - // Non-instanced objects read ID from 'prim_object', so distinguish - // them from instanced objects with the low bit set - instance.instanceId |= 1; - } - } - } - - // Upload instance descriptions -# if OPTIX_ABI_VERSION < 41 - aabbs.resize(num_instances); - aabbs.copy_to_device(); -# endif - instances.resize(num_instances); - instances.copy_to_device(); - - // Build top-level acceleration structure (TLAS) - OptixBuildInput build_input = {}; - build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES; -# if OPTIX_ABI_VERSION < 41 // Instance AABBs no longer need to be set since OptiX 7.2 - build_input.instanceArray.aabbs = aabbs.device_pointer; - build_input.instanceArray.numAabbs = num_instances; -# endif - build_input.instanceArray.instances = instances.device_pointer; - build_input.instanceArray.numInstances = num_instances; - - if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) { - progress.set_error("Failed to build OptiX acceleration structure"); - } - tlas_handle = bvh_optix->traversable_handle; - } - } - - void release_optix_bvh(BVH *bvh) override - { - thread_scoped_lock lock(delayed_free_bvh_mutex); - /* Do delayed free of BVH memory, since geometry holding BVH might be deleted - * while GPU is still rendering. */ - BVHOptiX *const bvh_optix = static_cast(bvh); - - delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->as_data)); - delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->motion_transform_data)); - bvh_optix->traversable_handle = 0; - } - - void free_bvh_memory_delayed() - { - thread_scoped_lock lock(delayed_free_bvh_mutex); - delayed_free_bvh_memory.free_memory(); - } - - void const_copy_to(const char *name, void *host, size_t size) override - { - // Set constant memory for CUDA module - // TODO(pmours): This is only used for tonemapping (see 'film_convert'). - // Could be removed by moving those functions to filter CUDA module. - CUDADevice::const_copy_to(name, host, size); - - if (strcmp(name, "__data") == 0) { - assert(size <= sizeof(KernelData)); - - // Update traversable handle (since it is different for each device on multi devices) - KernelData *const data = (KernelData *)host; - *(OptixTraversableHandle *)&data->bvh.scene = tlas_handle; - - update_launch_params(offsetof(KernelParams, data), host, size); - return; - } - - // Update data storage pointers in launch parameters -# define KERNEL_TEX(data_type, tex_name) \ - if (strcmp(name, #tex_name) == 0) { \ - update_launch_params(offsetof(KernelParams, tex_name), host, size); \ - return; \ - } -# include "kernel/kernel_textures.h" -# undef KERNEL_TEX - } - - void update_launch_params(size_t offset, void *data, size_t data_size) - { - const CUDAContextScope scope(cuContext); - - for (int i = 0; i < info.cpu_threads; ++i) - check_result_cuda( - cuMemcpyHtoD(launch_params.device_pointer + i * launch_params.data_elements + offset, - data, - data_size)); - } - - void task_add(DeviceTask &task) override - { - // Upload texture information to device if it has changed since last launch - load_texture_info(); - - if (task.type == DeviceTask::FILM_CONVERT) { - // Execute in main thread because of OpenGL access - film_convert(task, task.buffer, task.rgba_byte, task.rgba_half); - return; - } - - if (task.type == DeviceTask::DENOISE_BUFFER) { - // Execute denoising in a single thread (e.g. to avoid race conditions during creation) - task_pool.push([=] { - DeviceTask task_copy = task; - thread_run(task_copy, 0); - }); - return; - } - - // Split task into smaller ones - list tasks; - task.split(tasks, info.cpu_threads); - - // Queue tasks in internal task pool - int task_index = 0; - for (DeviceTask &task : tasks) { - task_pool.push([=] { - // Using task index parameter instead of thread index, since number of CUDA streams may - // differ from number of threads - DeviceTask task_copy = task; - thread_run(task_copy, task_index); - }); - task_index++; - } - } - - void task_wait() override - { - // Wait for all queued tasks to finish - task_pool.wait_work(); - } - - void task_cancel() override - { - // Cancel any remaining tasks in the internal pool - task_pool.cancel(); - } -}; - -bool device_optix_init() -{ - if (g_optixFunctionTable.optixDeviceContextCreate != NULL) - return true; // Already initialized function table - - // Need to initialize CUDA as well - if (!device_cuda_init()) - return false; - - const OptixResult result = optixInit(); - - if (result == OPTIX_ERROR_UNSUPPORTED_ABI_VERSION) { - VLOG(1) << "OptiX initialization failed because the installed NVIDIA driver is too old. " - "Please update to the latest driver first!"; - return false; - } - else if (result != OPTIX_SUCCESS) { - VLOG(1) << "OptiX initialization failed with error code " << (unsigned int)result; - return false; - } - - // Loaded OptiX successfully! - return true; -} - -void device_optix_info(const vector &cuda_devices, vector &devices) -{ - devices.reserve(cuda_devices.size()); - - // Simply add all supported CUDA devices as OptiX devices again - for (DeviceInfo info : cuda_devices) { - assert(info.type == DEVICE_CUDA); - - int major; - cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, info.num); - if (major < 5) { - continue; // Only Maxwell and up are supported by OptiX - } - - info.type = DEVICE_OPTIX; - info.id += "_OptiX"; - info.denoisers |= DENOISER_OPTIX; - info.has_branched_path = false; - - devices.push_back(info); - } -} - -Device *device_optix_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) -{ - return new OptiXDevice(info, stats, profiler, background); -} - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/device_queue.cpp b/intern/cycles/device/device_queue.cpp new file mode 100644 index 00000000000..a89ba68d62c --- /dev/null +++ b/intern/cycles/device/device_queue.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/device_queue.h" + +#include "util/util_algorithm.h" +#include "util/util_logging.h" +#include "util/util_time.h" + +#include + +CCL_NAMESPACE_BEGIN + +DeviceQueue::DeviceQueue(Device *device) + : device(device), last_kernels_enqueued_(0), last_sync_time_(0.0) +{ + DCHECK_NE(device, nullptr); +} + +DeviceQueue::~DeviceQueue() +{ + if (VLOG_IS_ON(3)) { + /* Print kernel execution times sorted by time. */ + vector> stats_sorted; + for (const auto &stat : stats_kernel_time_) { + stats_sorted.push_back(stat); + } + + sort(stats_sorted.begin(), + stats_sorted.end(), + [](const pair &a, const pair &b) { + return a.second > b.second; + }); + + VLOG(3) << "GPU queue stats:"; + for (const auto &[mask, time] : stats_sorted) { + VLOG(3) << " " << std::setfill(' ') << std::setw(10) << std::fixed << std::setprecision(5) + << std::right << time << "s: " << device_kernel_mask_as_string(mask); + } + } +} + +void DeviceQueue::debug_init_execution() +{ + if (VLOG_IS_ON(3)) { + last_sync_time_ = time_dt(); + last_kernels_enqueued_ = 0; + } +} + +void DeviceQueue::debug_enqueue(DeviceKernel kernel, const int work_size) +{ + if (VLOG_IS_ON(3)) { + VLOG(4) << "GPU queue launch " << device_kernel_as_string(kernel) << ", work_size " + << work_size; + last_kernels_enqueued_ |= (uint64_t(1) << (uint64_t)kernel); + } +} + +void DeviceQueue::debug_synchronize() +{ + if (VLOG_IS_ON(3)) { + const double new_time = time_dt(); + const double elapsed_time = new_time - last_sync_time_; + VLOG(4) << "GPU queue synchronize, elapsed " << std::setw(10) << elapsed_time << "s"; + + stats_kernel_time_[last_kernels_enqueued_] += elapsed_time; + + last_sync_time_ = new_time; + last_kernels_enqueued_ = 0; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_queue.h b/intern/cycles/device/device_queue.h new file mode 100644 index 00000000000..edda3e61d51 --- /dev/null +++ b/intern/cycles/device/device_queue.h @@ -0,0 +1,113 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "device/device_kernel.h" + +#include "device/device_graphics_interop.h" +#include "util/util_logging.h" +#include "util/util_map.h" +#include "util/util_unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class device_memory; + +struct KernelWorkTile; + +/* Abstraction of a command queue for a device. + * Provides API to schedule kernel execution in a specific queue with minimal possible overhead + * from driver side. + * + * This class encapsulates all properties needed for commands execution. */ +class DeviceQueue { + public: + virtual ~DeviceQueue(); + + /* Number of concurrent states to process for integrator, + * based on number of cores and/or available memory. */ + virtual int num_concurrent_states(const size_t state_size) const = 0; + + /* Number of states which keeps the device occupied with work without loosing performance. + * The renderer will add more work (when available) when number of active paths falls below this + * value. */ + virtual int num_concurrent_busy_states() const = 0; + + /* Initialize execution of kernels on this queue. + * + * Will, for example, load all data required by the kernels from Device to global or path state. + * + * Use this method after device synchronization has finished before enqueueing any kernels. */ + virtual void init_execution() = 0; + + /* Test if an optional device kernel is available. */ + virtual bool kernel_available(DeviceKernel kernel) const = 0; + + /* Enqueue kernel execution. + * + * Execute the kernel work_size times on the device. + * Supported arguments types: + * - int: pass pointer to the int + * - device memory: pass pointer to device_memory.device_pointer + * Return false if there was an error executing this or a previous kernel. */ + virtual bool enqueue(DeviceKernel kernel, const int work_size, void *args[]) = 0; + + /* Wait unit all enqueued kernels have finished execution. + * Return false if there was an error executing any of the enqueued kernels. */ + virtual bool synchronize() = 0; + + /* Copy memory to/from device as part of the command queue, to ensure + * operations are done in order without having to synchronize. */ + virtual void zero_to_device(device_memory &mem) = 0; + virtual void copy_to_device(device_memory &mem) = 0; + virtual void copy_from_device(device_memory &mem) = 0; + + /* Graphics resources interoperability. + * + * The interoperability comes here by the meaning that the device is capable of computing result + * directly into an OpenGL (or other graphics library) buffer. */ + + /* Create graphics interoperability context which will be taking care of mapping graphics + * resource as a buffer writable by kernels of this device. */ + virtual unique_ptr graphics_interop_create() + { + LOG(FATAL) << "Request of GPU interop of a device which does not support it."; + return nullptr; + } + + /* Device this queue has been created for. */ + Device *device; + + protected: + /* Hide construction so that allocation via `Device` API is enforced. */ + explicit DeviceQueue(Device *device); + + /* Implementations call these from the corresponding methods to generate debugging logs. */ + void debug_init_execution(); + void debug_enqueue(DeviceKernel kernel, const int work_size); + void debug_synchronize(); + + /* Combination of kernels enqueued together sync last synchronize. */ + DeviceKernelMask last_kernels_enqueued_; + /* Time of synchronize call. */ + double last_sync_time_; + /* Accumulated execution time for combinations of kernels launched together. */ + map stats_kernel_time_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_split_kernel.cpp b/intern/cycles/device/device_split_kernel.cpp deleted file mode 100644 index 9889f688aaa..00000000000 --- a/intern/cycles/device/device_split_kernel.cpp +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "device/device_split_kernel.h" - -#include "kernel/kernel_types.h" -#include "kernel/split/kernel_split_data_types.h" - -#include "util/util_logging.h" -#include "util/util_time.h" - -CCL_NAMESPACE_BEGIN - -static const double alpha = 0.1; /* alpha for rolling average */ - -DeviceSplitKernel::DeviceSplitKernel(Device *device) - : device(device), - split_data(device, "split_data"), - ray_state(device, "ray_state", MEM_READ_WRITE), - queue_index(device, "queue_index"), - use_queues_flag(device, "use_queues_flag"), - work_pool_wgs(device, "work_pool_wgs"), - kernel_data_initialized(false) -{ - avg_time_per_sample = 0.0; - - kernel_path_init = NULL; - kernel_scene_intersect = NULL; - kernel_lamp_emission = NULL; - kernel_do_volume = NULL; - kernel_queue_enqueue = NULL; - kernel_indirect_background = NULL; - kernel_shader_setup = NULL; - kernel_shader_sort = NULL; - kernel_shader_eval = NULL; - kernel_holdout_emission_blurring_pathtermination_ao = NULL; - kernel_subsurface_scatter = NULL; - kernel_direct_lighting = NULL; - kernel_shadow_blocked_ao = NULL; - kernel_shadow_blocked_dl = NULL; - kernel_enqueue_inactive = NULL; - kernel_next_iteration_setup = NULL; - kernel_indirect_subsurface = NULL; - kernel_buffer_update = NULL; - kernel_adaptive_stopping = NULL; - kernel_adaptive_filter_x = NULL; - kernel_adaptive_filter_y = NULL; - kernel_adaptive_adjust_samples = NULL; -} - -DeviceSplitKernel::~DeviceSplitKernel() -{ - split_data.free(); - ray_state.free(); - use_queues_flag.free(); - queue_index.free(); - work_pool_wgs.free(); - - delete kernel_path_init; - delete kernel_scene_intersect; - delete kernel_lamp_emission; - delete kernel_do_volume; - delete kernel_queue_enqueue; - delete kernel_indirect_background; - delete kernel_shader_setup; - delete kernel_shader_sort; - delete kernel_shader_eval; - delete kernel_holdout_emission_blurring_pathtermination_ao; - delete kernel_subsurface_scatter; - delete kernel_direct_lighting; - delete kernel_shadow_blocked_ao; - delete kernel_shadow_blocked_dl; - delete kernel_enqueue_inactive; - delete kernel_next_iteration_setup; - delete kernel_indirect_subsurface; - delete kernel_buffer_update; - delete kernel_adaptive_stopping; - delete kernel_adaptive_filter_x; - delete kernel_adaptive_filter_y; - delete kernel_adaptive_adjust_samples; -} - -bool DeviceSplitKernel::load_kernels(const DeviceRequestedFeatures &requested_features) -{ -#define LOAD_KERNEL(name) \ - kernel_##name = get_split_kernel_function(#name, requested_features); \ - if (!kernel_##name) { \ - device->set_error(string("Split kernel error: failed to load kernel_") + #name); \ - return false; \ - } - - LOAD_KERNEL(path_init); - LOAD_KERNEL(scene_intersect); - LOAD_KERNEL(lamp_emission); - if (requested_features.use_volume) { - LOAD_KERNEL(do_volume); - } - LOAD_KERNEL(queue_enqueue); - LOAD_KERNEL(indirect_background); - LOAD_KERNEL(shader_setup); - LOAD_KERNEL(shader_sort); - LOAD_KERNEL(shader_eval); - LOAD_KERNEL(holdout_emission_blurring_pathtermination_ao); - LOAD_KERNEL(subsurface_scatter); - LOAD_KERNEL(direct_lighting); - LOAD_KERNEL(shadow_blocked_ao); - LOAD_KERNEL(shadow_blocked_dl); - LOAD_KERNEL(enqueue_inactive); - LOAD_KERNEL(next_iteration_setup); - LOAD_KERNEL(indirect_subsurface); - LOAD_KERNEL(buffer_update); - LOAD_KERNEL(adaptive_stopping); - LOAD_KERNEL(adaptive_filter_x); - LOAD_KERNEL(adaptive_filter_y); - LOAD_KERNEL(adaptive_adjust_samples); - -#undef LOAD_KERNEL - - /* Re-initialiaze kernel-dependent data when kernels change. */ - kernel_data_initialized = false; - - return true; -} - -size_t DeviceSplitKernel::max_elements_for_max_buffer_size(device_memory &kg, - device_memory &data, - uint64_t max_buffer_size) -{ - uint64_t size_per_element = state_buffer_size(kg, data, 1024) / 1024; - VLOG(1) << "Split state element size: " << string_human_readable_number(size_per_element) - << " bytes. (" << string_human_readable_size(size_per_element) << ")."; - return max_buffer_size / size_per_element; -} - -bool DeviceSplitKernel::path_trace(DeviceTask &task, - RenderTile &tile, - device_memory &kgbuffer, - device_memory &kernel_data) -{ - if (device->have_error()) { - return false; - } - - /* Allocate all required global memory once. */ - if (!kernel_data_initialized) { - kernel_data_initialized = true; - - /* Set local size */ - int2 lsize = split_kernel_local_size(); - local_size[0] = lsize[0]; - local_size[1] = lsize[1]; - - /* Set global size */ - int2 gsize = split_kernel_global_size(kgbuffer, kernel_data, task); - - /* Make sure that set work size is a multiple of local - * work size dimensions. - */ - global_size[0] = round_up(gsize[0], local_size[0]); - global_size[1] = round_up(gsize[1], local_size[1]); - - int num_global_elements = global_size[0] * global_size[1]; - assert(num_global_elements % WORK_POOL_SIZE == 0); - - /* Calculate max groups */ - - /* Denotes the maximum work groups possible w.r.t. current requested tile size. */ - unsigned int work_pool_size = (device->info.type == DEVICE_CPU) ? WORK_POOL_SIZE_CPU : - WORK_POOL_SIZE_GPU; - unsigned int max_work_groups = num_global_elements / work_pool_size + 1; - - /* Allocate work_pool_wgs memory. */ - work_pool_wgs.alloc_to_device(max_work_groups); - queue_index.alloc_to_device(NUM_QUEUES); - use_queues_flag.alloc_to_device(1); - split_data.alloc_to_device(state_buffer_size(kgbuffer, kernel_data, num_global_elements)); - ray_state.alloc(num_global_elements); - } - - /* Number of elements in the global state buffer */ - int num_global_elements = global_size[0] * global_size[1]; - -#define ENQUEUE_SPLIT_KERNEL(name, global_size, local_size) \ - if (device->have_error()) { \ - return false; \ - } \ - if (!kernel_##name->enqueue( \ - KernelDimensions(global_size, local_size), kgbuffer, kernel_data)) { \ - return false; \ - } - - tile.sample = tile.start_sample; - - /* for exponential increase between tile updates */ - int time_multiplier = 1; - - while (tile.sample < tile.start_sample + tile.num_samples) { - /* to keep track of how long it takes to run a number of samples */ - double start_time = time_dt(); - - /* initial guess to start rolling average */ - const int initial_num_samples = 1; - /* approx number of samples per second */ - const int samples_per_second = (avg_time_per_sample > 0.0) ? - int(double(time_multiplier) / avg_time_per_sample) + 1 : - initial_num_samples; - - RenderTile subtile = tile; - subtile.start_sample = tile.sample; - subtile.num_samples = samples_per_second; - - if (task.adaptive_sampling.use) { - subtile.num_samples = task.adaptive_sampling.align_samples(subtile.start_sample, - subtile.num_samples); - } - - /* Don't go beyond requested number of samples. */ - subtile.num_samples = min(subtile.num_samples, - tile.start_sample + tile.num_samples - tile.sample); - - if (device->have_error()) { - return false; - } - - /* reset state memory here as global size for data_init - * kernel might not be large enough to do in kernel - */ - work_pool_wgs.zero_to_device(); - split_data.zero_to_device(); - ray_state.zero_to_device(); - - if (!enqueue_split_kernel_data_init(KernelDimensions(global_size, local_size), - subtile, - num_global_elements, - kgbuffer, - kernel_data, - split_data, - ray_state, - queue_index, - use_queues_flag, - work_pool_wgs)) { - return false; - } - - ENQUEUE_SPLIT_KERNEL(path_init, global_size, local_size); - - bool activeRaysAvailable = true; - double cancel_time = DBL_MAX; - - while (activeRaysAvailable) { - /* Do path-iteration in host [Enqueue Path-iteration kernels. */ - for (int PathIter = 0; PathIter < 16; PathIter++) { - ENQUEUE_SPLIT_KERNEL(scene_intersect, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(lamp_emission, global_size, local_size); - if (kernel_do_volume) { - ENQUEUE_SPLIT_KERNEL(do_volume, global_size, local_size); - } - ENQUEUE_SPLIT_KERNEL(queue_enqueue, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(indirect_background, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(shader_setup, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(shader_sort, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(shader_eval, global_size, local_size); - ENQUEUE_SPLIT_KERNEL( - holdout_emission_blurring_pathtermination_ao, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(subsurface_scatter, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(queue_enqueue, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(direct_lighting, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(shadow_blocked_ao, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(shadow_blocked_dl, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(enqueue_inactive, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(next_iteration_setup, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(indirect_subsurface, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(queue_enqueue, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(buffer_update, global_size, local_size); - - if (task.get_cancel() && cancel_time == DBL_MAX) { - /* Wait up to twice as many seconds for current samples to finish - * to avoid artifacts in render result from ending too soon. - */ - cancel_time = time_dt() + 2.0 * time_multiplier; - } - - if (time_dt() > cancel_time) { - return true; - } - } - - /* Decide if we should exit path-iteration in host. */ - ray_state.copy_from_device(0, global_size[0] * global_size[1], 1); - - activeRaysAvailable = false; - - for (int rayStateIter = 0; rayStateIter < global_size[0] * global_size[1]; ++rayStateIter) { - if (!IS_STATE(ray_state.data(), rayStateIter, RAY_INACTIVE)) { - if (IS_STATE(ray_state.data(), rayStateIter, RAY_INVALID)) { - /* Something went wrong, abort to avoid looping endlessly. */ - device->set_error("Split kernel error: invalid ray state"); - return false; - } - - /* Not all rays are RAY_INACTIVE. */ - activeRaysAvailable = true; - break; - } - } - - if (time_dt() > cancel_time) { - return true; - } - } - - int filter_sample = tile.sample + subtile.num_samples - 1; - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) { - size_t buffer_size[2]; - buffer_size[0] = round_up(tile.w, local_size[0]); - buffer_size[1] = round_up(tile.h, local_size[1]); - kernel_adaptive_stopping->enqueue( - KernelDimensions(buffer_size, local_size), kgbuffer, kernel_data); - buffer_size[0] = round_up(tile.h, local_size[0]); - buffer_size[1] = round_up(1, local_size[1]); - kernel_adaptive_filter_x->enqueue( - KernelDimensions(buffer_size, local_size), kgbuffer, kernel_data); - buffer_size[0] = round_up(tile.w, local_size[0]); - buffer_size[1] = round_up(1, local_size[1]); - kernel_adaptive_filter_y->enqueue( - KernelDimensions(buffer_size, local_size), kgbuffer, kernel_data); - } - - double time_per_sample = ((time_dt() - start_time) / subtile.num_samples); - - if (avg_time_per_sample == 0.0) { - /* start rolling average */ - avg_time_per_sample = time_per_sample; - } - else { - avg_time_per_sample = alpha * time_per_sample + (1.0 - alpha) * avg_time_per_sample; - } - -#undef ENQUEUE_SPLIT_KERNEL - - tile.sample += subtile.num_samples; - task.update_progress(&tile, tile.w * tile.h * subtile.num_samples); - - time_multiplier = min(time_multiplier << 1, 10); - - if (task.get_cancel()) { - return true; - } - } - - if (task.adaptive_sampling.use) { - /* Reset the start samples. */ - RenderTile subtile = tile; - subtile.start_sample = tile.start_sample; - subtile.num_samples = tile.sample - tile.start_sample; - enqueue_split_kernel_data_init(KernelDimensions(global_size, local_size), - subtile, - num_global_elements, - kgbuffer, - kernel_data, - split_data, - ray_state, - queue_index, - use_queues_flag, - work_pool_wgs); - size_t buffer_size[2]; - buffer_size[0] = round_up(tile.w, local_size[0]); - buffer_size[1] = round_up(tile.h, local_size[1]); - kernel_adaptive_adjust_samples->enqueue( - KernelDimensions(buffer_size, local_size), kgbuffer, kernel_data); - } - - return true; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_split_kernel.h b/intern/cycles/device/device_split_kernel.h deleted file mode 100644 index 07a21b10299..00000000000 --- a/intern/cycles/device/device_split_kernel.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEVICE_SPLIT_KERNEL_H__ -#define __DEVICE_SPLIT_KERNEL_H__ - -#include "device/device.h" -#include "render/buffers.h" - -CCL_NAMESPACE_BEGIN - -/* When allocate global memory in chunks. We may not be able to - * allocate exactly "CL_DEVICE_MAX_MEM_ALLOC_SIZE" bytes in chunks; - * Since some bytes may be needed for aligning chunks of memory; - * This is the amount of memory that we dedicate for that purpose. - */ -#define DATA_ALLOCATION_MEM_FACTOR 5000000 // 5MB - -/* Types used for split kernel */ - -class KernelDimensions { - public: - size_t global_size[2]; - size_t local_size[2]; - - KernelDimensions(size_t global_size_[2], size_t local_size_[2]) - { - memcpy(global_size, global_size_, sizeof(global_size)); - memcpy(local_size, local_size_, sizeof(local_size)); - } -}; - -class SplitKernelFunction { - public: - virtual ~SplitKernelFunction() - { - } - - /* enqueue the kernel, returns false if there is an error */ - virtual bool enqueue(const KernelDimensions &dim, device_memory &kg, device_memory &data) = 0; -}; - -class DeviceSplitKernel { - private: - Device *device; - - SplitKernelFunction *kernel_path_init; - SplitKernelFunction *kernel_scene_intersect; - SplitKernelFunction *kernel_lamp_emission; - SplitKernelFunction *kernel_do_volume; - SplitKernelFunction *kernel_queue_enqueue; - SplitKernelFunction *kernel_indirect_background; - SplitKernelFunction *kernel_shader_setup; - SplitKernelFunction *kernel_shader_sort; - SplitKernelFunction *kernel_shader_eval; - SplitKernelFunction *kernel_holdout_emission_blurring_pathtermination_ao; - SplitKernelFunction *kernel_subsurface_scatter; - SplitKernelFunction *kernel_direct_lighting; - SplitKernelFunction *kernel_shadow_blocked_ao; - SplitKernelFunction *kernel_shadow_blocked_dl; - SplitKernelFunction *kernel_enqueue_inactive; - SplitKernelFunction *kernel_next_iteration_setup; - SplitKernelFunction *kernel_indirect_subsurface; - SplitKernelFunction *kernel_buffer_update; - SplitKernelFunction *kernel_adaptive_stopping; - SplitKernelFunction *kernel_adaptive_filter_x; - SplitKernelFunction *kernel_adaptive_filter_y; - SplitKernelFunction *kernel_adaptive_adjust_samples; - - /* Global memory variables [porting]; These memory is used for - * co-operation between different kernels; Data written by one - * kernel will be available to another kernel via this global - * memory. - */ - device_only_memory split_data; - device_vector ray_state; - device_only_memory - queue_index; /* Array of size num_queues that tracks the size of each queue. */ - - /* Flag to make sceneintersect and lampemission kernel use queues. */ - device_only_memory use_queues_flag; - - /* Approximate time it takes to complete one sample */ - double avg_time_per_sample; - - /* Work pool with respect to each work group. */ - device_only_memory work_pool_wgs; - - /* Cached kernel-dependent data, initialized once. */ - bool kernel_data_initialized; - size_t local_size[2]; - size_t global_size[2]; - - public: - explicit DeviceSplitKernel(Device *device); - virtual ~DeviceSplitKernel(); - - bool load_kernels(const DeviceRequestedFeatures &requested_features); - bool path_trace(DeviceTask &task, - RenderTile &rtile, - device_memory &kgbuffer, - device_memory &kernel_data); - - virtual uint64_t state_buffer_size(device_memory &kg, - device_memory &data, - size_t num_threads) = 0; - size_t max_elements_for_max_buffer_size(device_memory &kg, - device_memory &data, - uint64_t max_buffer_size); - - virtual bool enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory &kernel_globals, - device_memory &kernel_data_, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flag, - device_memory &work_pool_wgs) = 0; - - virtual SplitKernelFunction *get_split_kernel_function(const string &kernel_name, - const DeviceRequestedFeatures &) = 0; - virtual int2 split_kernel_local_size() = 0; - virtual int2 split_kernel_global_size(device_memory &kg, - device_memory &data, - DeviceTask &task) = 0; -}; - -CCL_NAMESPACE_END - -#endif /* __DEVICE_SPLIT_KERNEL_H__ */ diff --git a/intern/cycles/device/device_task.cpp b/intern/cycles/device/device_task.cpp deleted file mode 100644 index 55fbaa31e42..00000000000 --- a/intern/cycles/device/device_task.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "device/device_task.h" - -#include "render/buffers.h" - -#include "util/util_algorithm.h" -#include "util/util_time.h" - -CCL_NAMESPACE_BEGIN - -/* Device Task */ - -DeviceTask::DeviceTask(Type type_) - : type(type_), - x(0), - y(0), - w(0), - h(0), - rgba_byte(0), - rgba_half(0), - buffer(0), - sample(0), - num_samples(1), - shader_input(0), - shader_output(0), - shader_eval_type(0), - shader_filter(0), - shader_x(0), - shader_w(0), - buffers(nullptr), - tile_types(0), - denoising_from_render(false), - pass_stride(0), - frame_stride(0), - target_pass_stride(0), - pass_denoising_data(0), - pass_denoising_clean(0), - need_finish_queue(false), - integrator_branched(false) -{ - last_update_time = time_dt(); -} - -int DeviceTask::get_subtask_count(int num, int max_size) const -{ - if (max_size != 0) { - int max_size_num; - - if (type == SHADER) { - max_size_num = (shader_w + max_size - 1) / max_size; - } - else { - max_size = max(1, max_size / w); - max_size_num = (h + max_size - 1) / max_size; - } - - num = max(max_size_num, num); - } - - if (type == SHADER) { - num = min(shader_w, num); - } - else if (type == RENDER) { - } - else { - num = min(h, num); - } - - return num; -} - -void DeviceTask::split(list &tasks, int num, int max_size) const -{ - num = get_subtask_count(num, max_size); - - if (type == SHADER) { - for (int i = 0; i < num; i++) { - int tx = shader_x + (shader_w / num) * i; - int tw = (i == num - 1) ? shader_w - i * (shader_w / num) : shader_w / num; - - DeviceTask task = *this; - - task.shader_x = tx; - task.shader_w = tw; - - tasks.push_back(task); - } - } - else if (type == RENDER) { - for (int i = 0; i < num; i++) - tasks.push_back(*this); - } - else { - for (int i = 0; i < num; i++) { - int ty = y + (h / num) * i; - int th = (i == num - 1) ? h - i * (h / num) : h / num; - - DeviceTask task = *this; - - task.y = ty; - task.h = th; - - tasks.push_back(task); - } - } -} - -void DeviceTask::update_progress(RenderTile *rtile, int pixel_samples) -{ - if (type == FILM_CONVERT) - return; - - if (update_progress_sample) { - if (pixel_samples == -1) { - pixel_samples = shader_w; - } - update_progress_sample(pixel_samples, rtile ? rtile->sample : 0); - } - - if (update_tile_sample) { - double current_time = time_dt(); - - if (current_time - last_update_time >= 1.0) { - update_tile_sample(*rtile); - - last_update_time = current_time; - } - } -} - -/* Adaptive Sampling */ - -AdaptiveSampling::AdaptiveSampling() : use(true), adaptive_step(0), min_samples(0) -{ -} - -/* Render samples in steps that align with the adaptive filtering. */ -int AdaptiveSampling::align_samples(int sample, int num_samples) const -{ - int end_sample = sample + num_samples; - - /* Round down end sample to the nearest sample that needs filtering. */ - end_sample &= ~(adaptive_step - 1); - - if (end_sample <= sample) { - /* In order to reach the next sample that needs filtering, we'd need - * to increase num_samples. We don't do that in this function, so - * just keep it as is and don't filter this time around. */ - return num_samples; - } - return end_sample - sample; -} - -bool AdaptiveSampling::need_filter(int sample) const -{ - if (sample > min_samples) { - return (sample & (adaptive_step - 1)) == (adaptive_step - 1); - } - else { - return false; - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_task.h b/intern/cycles/device/device_task.h deleted file mode 100644 index 3f7cf47b692..00000000000 --- a/intern/cycles/device/device_task.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEVICE_TASK_H__ -#define __DEVICE_TASK_H__ - -#include "device/device_memory.h" - -#include "util/util_function.h" -#include "util/util_list.h" - -CCL_NAMESPACE_BEGIN - -/* Device Task */ - -class Device; -class RenderBuffers; -class RenderTile; -class RenderTileNeighbors; -class Tile; - -enum DenoiserType { - DENOISER_NLM = 1, - DENOISER_OPTIX = 2, - DENOISER_OPENIMAGEDENOISE = 4, - DENOISER_NUM, - - DENOISER_NONE = 0, - DENOISER_ALL = ~0, -}; - -enum DenoiserInput { - DENOISER_INPUT_RGB = 1, - DENOISER_INPUT_RGB_ALBEDO = 2, - DENOISER_INPUT_RGB_ALBEDO_NORMAL = 3, - - DENOISER_INPUT_NUM, -}; - -typedef int DenoiserTypeMask; - -class DenoiseParams { - public: - /* Apply denoiser to image. */ - bool use; - /* Output denoising data passes (possibly without applying the denoiser). */ - bool store_passes; - - /* Denoiser type. */ - DenoiserType type; - - /* Viewport start sample. */ - int start_sample; - - /** Native Denoiser. */ - - /* Pixel radius for neighboring pixels to take into account. */ - int radius; - /* Controls neighbor pixel weighting for the denoising filter. */ - float strength; - /* Preserve more or less detail based on feature passes. */ - float feature_strength; - /* When removing pixels that don't carry information, - * use a relative threshold instead of an absolute one. */ - bool relative_pca; - /* How many frames before and after the current center frame are included. */ - int neighbor_frames; - /* Clamp the input to the range of +-1e8. Should be enough for any legitimate data. */ - bool clamp_input; - - /** OIDN/Optix Denoiser. */ - - /* Passes handed over to the OIDN/OptiX denoiser (default to color + albedo). */ - DenoiserInput input_passes; - - DenoiseParams() - { - use = false; - store_passes = false; - - type = DENOISER_NLM; - - radius = 8; - strength = 0.5f; - feature_strength = 0.5f; - relative_pca = false; - neighbor_frames = 2; - clamp_input = true; - - /* Default to color + albedo only, since normal input does not always have the desired effect - * when denoising with OptiX. */ - input_passes = DENOISER_INPUT_RGB_ALBEDO; - - start_sample = 0; - } - - /* Test if a denoising task needs to run, also to prefilter passes for the native - * denoiser when we are not applying denoising to the combined image. */ - bool need_denoising_task() const - { - return (use || (store_passes && type == DENOISER_NLM)); - } -}; - -class AdaptiveSampling { - public: - AdaptiveSampling(); - - int align_samples(int sample, int num_samples) const; - bool need_filter(int sample) const; - - bool use; - int adaptive_step; - int min_samples; -}; - -class DeviceTask { - public: - typedef enum { RENDER, FILM_CONVERT, SHADER, DENOISE_BUFFER } Type; - Type type; - - int x, y, w, h; - device_ptr rgba_byte; - device_ptr rgba_half; - device_ptr buffer; - int sample; - int num_samples; - int offset, stride; - - device_ptr shader_input; - device_ptr shader_output; - int shader_eval_type; - int shader_filter; - int shader_x, shader_w; - - RenderBuffers *buffers; - - explicit DeviceTask(Type type = RENDER); - - int get_subtask_count(int num, int max_size = 0) const; - void split(list &tasks, int num, int max_size = 0) const; - - void update_progress(RenderTile *rtile, int pixel_samples = -1); - - function acquire_tile; - function update_progress_sample; - function update_tile_sample; - function release_tile; - function get_cancel; - function get_tile_stolen; - function map_neighbor_tiles; - function unmap_neighbor_tiles; - - uint tile_types; - DenoiseParams denoising; - bool denoising_from_render; - vector denoising_frames; - - int pass_stride; - int frame_stride; - int target_pass_stride; - int pass_denoising_data; - int pass_denoising_clean; - - bool need_finish_queue; - bool integrator_branched; - AdaptiveSampling adaptive_sampling; - - protected: - double last_update_time; -}; - -CCL_NAMESPACE_END - -#endif /* __DEVICE_TASK_H__ */ diff --git a/intern/cycles/device/device_dummy.cpp b/intern/cycles/device/dummy/device.cpp similarity index 73% rename from intern/cycles/device/device_dummy.cpp rename to intern/cycles/device/dummy/device.cpp index 5112fc152e5..678276ed025 100644 --- a/intern/cycles/device/device_dummy.cpp +++ b/intern/cycles/device/dummy/device.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ +#include "device/dummy/device.h" + #include "device/device.h" -#include "device/device_intern.h" +#include "device/device_queue.h" CCL_NAMESPACE_BEGIN @@ -23,8 +25,8 @@ CCL_NAMESPACE_BEGIN class DummyDevice : public Device { public: - DummyDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_) - : Device(info_, stats_, profiler_, background_) + DummyDevice(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_) + : Device(info_, stats_, profiler_) { error_msg = info.error_msg; } @@ -61,23 +63,11 @@ class DummyDevice : public Device { virtual void const_copy_to(const char *, void *, size_t) override { } - - virtual void task_add(DeviceTask &) override - { - } - - virtual void task_wait() override - { - } - - virtual void task_cancel() override - { - } }; -Device *device_dummy_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) +Device *device_dummy_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) { - return new DummyDevice(info, stats, profiler, background); + return new DummyDevice(info, stats, profiler); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_do_volume.cl b/intern/cycles/device/dummy/device.h similarity index 64% rename from intern/cycles/kernel/kernels/opencl/kernel_do_volume.cl rename to intern/cycles/device/dummy/device.h index 8afaa686e28..832a9568129 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_do_volume.cl +++ b/intern/cycles/device/dummy/device.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,18 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_do_volume.h" +#pragma once -#define KERNEL_NAME do_volume -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME +#include "util/util_string.h" +#include "util/util_vector.h" +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +Device *device_dummy_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/multi/device.cpp b/intern/cycles/device/multi/device.cpp new file mode 100644 index 00000000000..6dbcce2d9a5 --- /dev/null +++ b/intern/cycles/device/multi/device.cpp @@ -0,0 +1,423 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/multi/device.h" + +#include +#include + +#include "bvh/bvh_multi.h" + +#include "device/device.h" +#include "device/device_queue.h" + +#include "render/buffers.h" +#include "render/geometry.h" + +#include "util/util_foreach.h" +#include "util/util_list.h" +#include "util/util_logging.h" +#include "util/util_map.h" +#include "util/util_time.h" + +CCL_NAMESPACE_BEGIN + +class MultiDevice : public Device { + public: + struct SubDevice { + Stats stats; + Device *device; + map ptr_map; + int peer_island_index = -1; + }; + + list devices; + device_ptr unique_key; + vector> peer_islands; + + MultiDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler) + : Device(info, stats, profiler), unique_key(1) + { + foreach (const DeviceInfo &subinfo, info.multi_devices) { + /* Always add CPU devices at the back since GPU devices can change + * host memory pointers, which CPU uses as device pointer. */ + SubDevice *sub; + if (subinfo.type == DEVICE_CPU) { + devices.emplace_back(); + sub = &devices.back(); + } + else { + devices.emplace_front(); + sub = &devices.front(); + } + + /* The pointer to 'sub->stats' will stay valid even after new devices + * are added, since 'devices' is a linked list. */ + sub->device = Device::create(subinfo, sub->stats, profiler); + } + + /* Build a list of peer islands for the available render devices */ + foreach (SubDevice &sub, devices) { + /* First ensure that every device is in at least once peer island */ + if (sub.peer_island_index < 0) { + peer_islands.emplace_back(); + sub.peer_island_index = (int)peer_islands.size() - 1; + peer_islands[sub.peer_island_index].push_back(&sub); + } + + if (!info.has_peer_memory) { + continue; + } + + /* Second check peer access between devices and fill up the islands accordingly */ + foreach (SubDevice &peer_sub, devices) { + if (peer_sub.peer_island_index < 0 && + peer_sub.device->info.type == sub.device->info.type && + peer_sub.device->check_peer_access(sub.device)) { + peer_sub.peer_island_index = sub.peer_island_index; + peer_islands[sub.peer_island_index].push_back(&peer_sub); + } + } + } + } + + ~MultiDevice() + { + foreach (SubDevice &sub, devices) + delete sub.device; + } + + const string &error_message() override + { + error_msg.clear(); + + foreach (SubDevice &sub, devices) + error_msg += sub.device->error_message(); + + return error_msg; + } + + virtual bool show_samples() const override + { + if (devices.size() > 1) { + return false; + } + return devices.front().device->show_samples(); + } + + virtual BVHLayoutMask get_bvh_layout_mask() const override + { + BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL; + BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE; + foreach (const SubDevice &sub_device, devices) { + BVHLayoutMask device_bvh_layout_mask = sub_device.device->get_bvh_layout_mask(); + bvh_layout_mask &= device_bvh_layout_mask; + bvh_layout_mask_all |= device_bvh_layout_mask; + } + + /* With multiple OptiX devices, every device needs its own acceleration structure */ + if (bvh_layout_mask == BVH_LAYOUT_OPTIX) { + return BVH_LAYOUT_MULTI_OPTIX; + } + + /* When devices do not share a common BVH layout, fall back to creating one for each */ + const BVHLayoutMask BVH_LAYOUT_OPTIX_EMBREE = (BVH_LAYOUT_OPTIX | BVH_LAYOUT_EMBREE); + if ((bvh_layout_mask_all & BVH_LAYOUT_OPTIX_EMBREE) == BVH_LAYOUT_OPTIX_EMBREE) { + return BVH_LAYOUT_MULTI_OPTIX_EMBREE; + } + + return bvh_layout_mask; + } + + bool load_kernels(const uint kernel_features) override + { + foreach (SubDevice &sub, devices) + if (!sub.device->load_kernels(kernel_features)) + return false; + + return true; + } + + void build_bvh(BVH *bvh, Progress &progress, bool refit) override + { + /* Try to build and share a single acceleration structure, if possible */ + if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2 || bvh->params.bvh_layout == BVH_LAYOUT_EMBREE) { + devices.back().device->build_bvh(bvh, progress, refit); + return; + } + + assert(bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX || + bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE); + + BVHMulti *const bvh_multi = static_cast(bvh); + bvh_multi->sub_bvhs.resize(devices.size()); + + vector geom_bvhs; + geom_bvhs.reserve(bvh->geometry.size()); + foreach (Geometry *geom, bvh->geometry) { + geom_bvhs.push_back(static_cast(geom->bvh)); + } + + /* Broadcast acceleration structure build to all render devices */ + size_t i = 0; + foreach (SubDevice &sub, devices) { + /* Change geometry BVH pointers to the sub BVH */ + for (size_t k = 0; k < bvh->geometry.size(); ++k) { + bvh->geometry[k]->bvh = geom_bvhs[k]->sub_bvhs[i]; + } + + if (!bvh_multi->sub_bvhs[i]) { + BVHParams params = bvh->params; + if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX) + params.bvh_layout = BVH_LAYOUT_OPTIX; + else if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) + params.bvh_layout = sub.device->info.type == DEVICE_OPTIX ? BVH_LAYOUT_OPTIX : + BVH_LAYOUT_EMBREE; + + /* Skip building a bottom level acceleration structure for non-instanced geometry on Embree + * (since they are put into the top level directly, see bvh_embree.cpp) */ + if (!params.top_level && params.bvh_layout == BVH_LAYOUT_EMBREE && + !bvh->geometry[0]->is_instanced()) { + i++; + continue; + } + + bvh_multi->sub_bvhs[i] = BVH::create(params, bvh->geometry, bvh->objects, sub.device); + } + + sub.device->build_bvh(bvh_multi->sub_bvhs[i], progress, refit); + i++; + } + + /* Change geometry BVH pointers back to the multi BVH. */ + for (size_t k = 0; k < bvh->geometry.size(); ++k) { + bvh->geometry[k]->bvh = geom_bvhs[k]; + } + } + + virtual void *get_cpu_osl_memory() override + { + if (devices.size() > 1) { + return NULL; + } + return devices.front().device->get_cpu_osl_memory(); + } + + bool is_resident(device_ptr key, Device *sub_device) override + { + foreach (SubDevice &sub, devices) { + if (sub.device == sub_device) { + return find_matching_mem_device(key, sub)->device == sub_device; + } + } + return false; + } + + SubDevice *find_matching_mem_device(device_ptr key, SubDevice &sub) + { + assert(key != 0 && (sub.peer_island_index >= 0 || sub.ptr_map.find(key) != sub.ptr_map.end())); + + /* Get the memory owner of this key (first try current device, then peer devices) */ + SubDevice *owner_sub = ⊂ + if (owner_sub->ptr_map.find(key) == owner_sub->ptr_map.end()) { + foreach (SubDevice *island_sub, peer_islands[sub.peer_island_index]) { + if (island_sub != owner_sub && + island_sub->ptr_map.find(key) != island_sub->ptr_map.end()) { + owner_sub = island_sub; + } + } + } + return owner_sub; + } + + SubDevice *find_suitable_mem_device(device_ptr key, const vector &island) + { + assert(!island.empty()); + + /* Get the memory owner of this key or the device with the lowest memory usage when new */ + SubDevice *owner_sub = island.front(); + foreach (SubDevice *island_sub, island) { + if (key ? (island_sub->ptr_map.find(key) != island_sub->ptr_map.end()) : + (island_sub->device->stats.mem_used < owner_sub->device->stats.mem_used)) { + owner_sub = island_sub; + } + } + return owner_sub; + } + + inline device_ptr find_matching_mem(device_ptr key, SubDevice &sub) + { + return find_matching_mem_device(key, sub)->ptr_map[key]; + } + + void mem_alloc(device_memory &mem) override + { + device_ptr key = unique_key++; + + assert(mem.type == MEM_READ_ONLY || mem.type == MEM_READ_WRITE || mem.type == MEM_DEVICE_ONLY); + /* The remaining memory types can be distributed across devices */ + foreach (const vector &island, peer_islands) { + SubDevice *owner_sub = find_suitable_mem_device(key, island); + mem.device = owner_sub->device; + mem.device_pointer = 0; + mem.device_size = 0; + + owner_sub->device->mem_alloc(mem); + owner_sub->ptr_map[key] = mem.device_pointer; + } + + mem.device = this; + mem.device_pointer = key; + stats.mem_alloc(mem.device_size); + } + + void mem_copy_to(device_memory &mem) override + { + device_ptr existing_key = mem.device_pointer; + device_ptr key = (existing_key) ? existing_key : unique_key++; + size_t existing_size = mem.device_size; + + /* The tile buffers are allocated on each device (see below), so copy to all of them */ + foreach (const vector &island, peer_islands) { + SubDevice *owner_sub = find_suitable_mem_device(existing_key, island); + mem.device = owner_sub->device; + mem.device_pointer = (existing_key) ? owner_sub->ptr_map[existing_key] : 0; + mem.device_size = existing_size; + + owner_sub->device->mem_copy_to(mem); + owner_sub->ptr_map[key] = mem.device_pointer; + + if (mem.type == MEM_GLOBAL || mem.type == MEM_TEXTURE) { + /* Need to create texture objects and update pointer in kernel globals on all devices */ + foreach (SubDevice *island_sub, island) { + if (island_sub != owner_sub) { + island_sub->device->mem_copy_to(mem); + } + } + } + } + + mem.device = this; + mem.device_pointer = key; + stats.mem_alloc(mem.device_size - existing_size); + } + + void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override + { + device_ptr key = mem.device_pointer; + int i = 0, sub_h = h / devices.size(); + + foreach (SubDevice &sub, devices) { + int sy = y + i * sub_h; + int sh = (i == (int)devices.size() - 1) ? h - sub_h * i : sub_h; + + SubDevice *owner_sub = find_matching_mem_device(key, sub); + mem.device = owner_sub->device; + mem.device_pointer = owner_sub->ptr_map[key]; + + owner_sub->device->mem_copy_from(mem, sy, w, sh, elem); + i++; + } + + mem.device = this; + mem.device_pointer = key; + } + + void mem_zero(device_memory &mem) override + { + device_ptr existing_key = mem.device_pointer; + device_ptr key = (existing_key) ? existing_key : unique_key++; + size_t existing_size = mem.device_size; + + foreach (const vector &island, peer_islands) { + SubDevice *owner_sub = find_suitable_mem_device(existing_key, island); + mem.device = owner_sub->device; + mem.device_pointer = (existing_key) ? owner_sub->ptr_map[existing_key] : 0; + mem.device_size = existing_size; + + owner_sub->device->mem_zero(mem); + owner_sub->ptr_map[key] = mem.device_pointer; + } + + mem.device = this; + mem.device_pointer = key; + stats.mem_alloc(mem.device_size - existing_size); + } + + void mem_free(device_memory &mem) override + { + device_ptr key = mem.device_pointer; + size_t existing_size = mem.device_size; + + /* Free memory that was allocated for all devices (see above) on each device */ + foreach (const vector &island, peer_islands) { + SubDevice *owner_sub = find_matching_mem_device(key, *island.front()); + mem.device = owner_sub->device; + mem.device_pointer = owner_sub->ptr_map[key]; + mem.device_size = existing_size; + + owner_sub->device->mem_free(mem); + owner_sub->ptr_map.erase(owner_sub->ptr_map.find(key)); + + if (mem.type == MEM_TEXTURE) { + /* Free texture objects on all devices */ + foreach (SubDevice *island_sub, island) { + if (island_sub != owner_sub) { + island_sub->device->mem_free(mem); + } + } + } + } + + mem.device = this; + mem.device_pointer = 0; + mem.device_size = 0; + stats.mem_free(existing_size); + } + + void const_copy_to(const char *name, void *host, size_t size) override + { + foreach (SubDevice &sub, devices) + sub.device->const_copy_to(name, host, size); + } + + int device_number(Device *sub_device) override + { + int i = 0; + + foreach (SubDevice &sub, devices) { + if (sub.device == sub_device) + return i; + i++; + } + + return -1; + } + + virtual void foreach_device(const function &callback) override + { + foreach (SubDevice &sub, devices) { + sub.device->foreach_device(callback); + } + } +}; + +Device *device_multi_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) +{ + return new MultiDevice(info, stats, profiler); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_indirect_background.cl b/intern/cycles/device/multi/device.h similarity index 64% rename from intern/cycles/kernel/kernels/opencl/kernel_indirect_background.cl rename to intern/cycles/device/multi/device.h index 192d01444ba..6e121014a1f 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_indirect_background.cl +++ b/intern/cycles/device/multi/device.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,18 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_indirect_background.h" +#pragma once -#define KERNEL_NAME indirect_background -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME +#include "util/util_string.h" +#include "util/util_vector.h" +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +Device *device_multi_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/opencl/device_opencl.h b/intern/cycles/device/opencl/device_opencl.h deleted file mode 100644 index a65e764b0d4..00000000000 --- a/intern/cycles/device/opencl/device_opencl.h +++ /dev/null @@ -1,658 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPENCL - -# include "device/device.h" -# include "device/device_denoising.h" -# include "device/device_split_kernel.h" - -# include "util/util_map.h" -# include "util/util_param.h" -# include "util/util_string.h" -# include "util/util_task.h" - -# include "clew.h" - -# include "device/opencl/memory_manager.h" - -CCL_NAMESPACE_BEGIN - -/* Disable workarounds, seems to be working fine on latest drivers. */ -# define CYCLES_DISABLE_DRIVER_WORKAROUNDS - -/* Define CYCLES_DISABLE_DRIVER_WORKAROUNDS to disable workarounds for testing. */ -# ifndef CYCLES_DISABLE_DRIVER_WORKAROUNDS -/* Work around AMD driver hangs by ensuring each command is finished before doing anything else. */ -# undef clEnqueueNDRangeKernel -# define clEnqueueNDRangeKernel(a, b, c, d, e, f, g, h, i) \ - CLEW_GET_FUN(__clewEnqueueNDRangeKernel)(a, b, c, d, e, f, g, h, i); \ - clFinish(a); - -# undef clEnqueueWriteBuffer -# define clEnqueueWriteBuffer(a, b, c, d, e, f, g, h, i) \ - CLEW_GET_FUN(__clewEnqueueWriteBuffer)(a, b, c, d, e, f, g, h, i); \ - clFinish(a); - -# undef clEnqueueReadBuffer -# define clEnqueueReadBuffer(a, b, c, d, e, f, g, h, i) \ - CLEW_GET_FUN(__clewEnqueueReadBuffer)(a, b, c, d, e, f, g, h, i); \ - clFinish(a); -# endif /* CYCLES_DISABLE_DRIVER_WORKAROUNDS */ - -# define CL_MEM_PTR(p) ((cl_mem)(uintptr_t)(p)) - -struct OpenCLPlatformDevice { - OpenCLPlatformDevice(cl_platform_id platform_id, - const string &platform_name, - cl_device_id device_id, - cl_device_type device_type, - const string &device_name, - const string &hardware_id, - const string &device_extensions) - : platform_id(platform_id), - platform_name(platform_name), - device_id(device_id), - device_type(device_type), - device_name(device_name), - hardware_id(hardware_id), - device_extensions(device_extensions) - { - } - cl_platform_id platform_id; - string platform_name; - cl_device_id device_id; - cl_device_type device_type; - string device_name; - string hardware_id; - string device_extensions; -}; - -/* Contains all static OpenCL helper functions. */ -class OpenCLInfo { - public: - static cl_device_type device_type(); - static bool use_debug(); - static bool device_supported(const string &platform_name, const cl_device_id device_id); - static bool platform_version_check(cl_platform_id platform, string *error = NULL); - static bool device_version_check(cl_device_id device, string *error = NULL); - static bool get_device_version(cl_device_id device, - int *r_major, - int *r_minor, - string *error = NULL); - static string get_hardware_id(const string &platform_name, cl_device_id device_id); - static void get_usable_devices(vector *usable_devices); - - /* ** Some handy shortcuts to low level cl*GetInfo() functions. ** */ - - /* Platform information. */ - static bool get_num_platforms(cl_uint *num_platforms, cl_int *error = NULL); - static cl_uint get_num_platforms(); - - static bool get_platforms(vector *platform_ids, cl_int *error = NULL); - static vector get_platforms(); - - static bool get_platform_name(cl_platform_id platform_id, string *platform_name); - static string get_platform_name(cl_platform_id platform_id); - - static bool get_num_platform_devices(cl_platform_id platform_id, - cl_device_type device_type, - cl_uint *num_devices, - cl_int *error = NULL); - static cl_uint get_num_platform_devices(cl_platform_id platform_id, cl_device_type device_type); - - static bool get_platform_devices(cl_platform_id platform_id, - cl_device_type device_type, - vector *device_ids, - cl_int *error = NULL); - static vector get_platform_devices(cl_platform_id platform_id, - cl_device_type device_type); - - /* Device information. */ - static bool get_device_name(cl_device_id device_id, string *device_name, cl_int *error = NULL); - - static string get_device_name(cl_device_id device_id); - - static bool get_device_extensions(cl_device_id device_id, - string *device_extensions, - cl_int *error = NULL); - - static string get_device_extensions(cl_device_id device_id); - - static bool get_device_type(cl_device_id device_id, - cl_device_type *device_type, - cl_int *error = NULL); - static cl_device_type get_device_type(cl_device_id device_id); - - static bool get_driver_version(cl_device_id device_id, - int *major, - int *minor, - cl_int *error = NULL); - - static int mem_sub_ptr_alignment(cl_device_id device_id); - - /* Get somewhat more readable device name. - * Main difference is AMD OpenCL here which only gives code name - * for the regular device name. This will give more sane device - * name using some extensions. - */ - static string get_readable_device_name(cl_device_id device_id); -}; - -/* Thread safe cache for contexts and programs. - */ -class OpenCLCache { - struct Slot { - struct ProgramEntry { - ProgramEntry(); - ProgramEntry(const ProgramEntry &rhs); - ~ProgramEntry(); - cl_program program; - thread_mutex *mutex; - }; - - Slot(); - Slot(const Slot &rhs); - ~Slot(); - - thread_mutex *context_mutex; - cl_context context; - typedef map EntryMap; - EntryMap programs; - }; - - /* key is combination of platform ID and device ID */ - typedef pair PlatformDevicePair; - - /* map of Slot objects */ - typedef map CacheMap; - CacheMap cache; - - /* MD5 hash of the kernel source. */ - string kernel_md5; - - thread_mutex cache_lock; - thread_mutex kernel_md5_lock; - - /* lazy instantiate */ - static OpenCLCache &global_instance(); - - public: - enum ProgramName { - OCL_DEV_BASE_PROGRAM, - OCL_DEV_MEGAKERNEL_PROGRAM, - }; - - /* Lookup context in the cache. If this returns NULL, slot_locker - * will be holding a lock for the cache. slot_locker should refer to a - * default constructed thread_scoped_lock. */ - static cl_context get_context(cl_platform_id platform, - cl_device_id device, - thread_scoped_lock &slot_locker); - /* Same as above. */ - static cl_program get_program(cl_platform_id platform, - cl_device_id device, - ustring key, - thread_scoped_lock &slot_locker); - - /* Store context in the cache. You MUST have tried to get the item before storing to it. */ - static void store_context(cl_platform_id platform, - cl_device_id device, - cl_context context, - thread_scoped_lock &slot_locker); - /* Same as above. */ - static void store_program(cl_platform_id platform, - cl_device_id device, - cl_program program, - ustring key, - thread_scoped_lock &slot_locker); - - static string get_kernel_md5(); -}; - -# define opencl_device_assert(device, stmt) \ - { \ - cl_int err = stmt; \ -\ - if (err != CL_SUCCESS) { \ - string message = string_printf( \ - "OpenCL error: %s in %s (%s:%d)", clewErrorString(err), #stmt, __FILE__, __LINE__); \ - if ((device)->error_message() == "") { \ - (device)->set_error(message); \ - } \ - fprintf(stderr, "%s\n", message.c_str()); \ - } \ - } \ - (void)0 - -# define opencl_assert(stmt) \ - { \ - cl_int err = stmt; \ -\ - if (err != CL_SUCCESS) { \ - string message = string_printf( \ - "OpenCL error: %s in %s (%s:%d)", clewErrorString(err), #stmt, __FILE__, __LINE__); \ - if (error_msg == "") { \ - error_msg = message; \ - } \ - fprintf(stderr, "%s\n", message.c_str()); \ - } \ - } \ - (void)0 - -class OpenCLDevice : public Device { - public: - DedicatedTaskPool task_pool; - - /* Task pool for required kernels (base, AO kernels during foreground rendering) */ - TaskPool load_required_kernel_task_pool; - /* Task pool for optional kernels (feature kernels during foreground rendering) */ - TaskPool load_kernel_task_pool; - std::atomic load_kernel_num_compiling; - - cl_context cxContext; - cl_command_queue cqCommandQueue; - cl_platform_id cpPlatform; - cl_device_id cdDevice; - cl_int ciErr; - int device_num; - - class OpenCLProgram { - public: - OpenCLProgram() : loaded(false), needs_compiling(true), program(NULL), device(NULL) - { - } - OpenCLProgram(OpenCLDevice *device, - const string &program_name, - const string &kernel_name, - const string &kernel_build_options, - bool use_stdout = true); - ~OpenCLProgram(); - - void add_kernel(ustring name); - - /* Try to load the program from device cache or disk */ - bool load(); - /* Compile the kernel (first separate, fail-back to local). */ - void compile(); - /* Create the OpenCL kernels after loading or compiling */ - void create_kernels(); - - bool is_loaded() const - { - return loaded; - } - const string &get_log() const - { - return log; - } - void report_error(); - - /* Wait until this kernel is available to be used - * It will return true when the kernel is available. - * It will return false when the kernel is not available - * or could not be loaded. */ - bool wait_for_availability(); - - cl_kernel operator()(); - cl_kernel operator()(ustring name); - - void release(); - - private: - bool build_kernel(const string *debug_src); - /* Build the program by calling the own process. - * This is required for multithreaded OpenCL compilation, since most Frameworks serialize - * build calls internally if they come from the same process. - * If that is not supported, this function just returns false. - */ - bool compile_separate(const string &clbin); - /* Build the program by calling OpenCL directly. */ - bool compile_kernel(const string *debug_src); - /* Loading and saving the program from/to disk. */ - bool load_binary(const string &clbin, const string *debug_src = NULL); - bool save_binary(const string &clbin); - - void add_log(const string &msg, bool is_debug); - void add_error(const string &msg); - - bool loaded; - bool needs_compiling; - - cl_program program; - OpenCLDevice *device; - - /* Used for the OpenCLCache key. */ - string program_name; - - string kernel_file, kernel_build_options, device_md5; - - bool use_stdout; - string log, error_msg; - string compile_output; - - map kernels; - }; - - /* Container for all types of split programs. */ - class OpenCLSplitPrograms { - public: - OpenCLDevice *device; - OpenCLProgram program_split; - OpenCLProgram program_lamp_emission; - OpenCLProgram program_do_volume; - OpenCLProgram program_indirect_background; - OpenCLProgram program_shader_eval; - OpenCLProgram program_holdout_emission_blurring_pathtermination_ao; - OpenCLProgram program_subsurface_scatter; - OpenCLProgram program_direct_lighting; - OpenCLProgram program_shadow_blocked_ao; - OpenCLProgram program_shadow_blocked_dl; - - OpenCLSplitPrograms(OpenCLDevice *device); - ~OpenCLSplitPrograms(); - - /* Load the kernels and put the created kernels in the given - * `programs` parameter. */ - void load_kernels(vector &programs, - const DeviceRequestedFeatures &requested_features); - }; - - DeviceSplitKernel *split_kernel; - - OpenCLProgram base_program; - OpenCLProgram bake_program; - OpenCLProgram displace_program; - OpenCLProgram background_program; - OpenCLProgram denoising_program; - - OpenCLSplitPrograms kernel_programs; - - typedef map *> ConstMemMap; - typedef map MemMap; - - ConstMemMap const_mem_map; - MemMap mem_map; - - bool device_initialized; - string platform_name; - string device_name; - - bool opencl_error(cl_int err); - void opencl_error(const string &message); - void opencl_assert_err(cl_int err, const char *where); - - OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); - ~OpenCLDevice(); - - static void CL_CALLBACK context_notify_callback(const char *err_info, - const void * /*private_info*/, - size_t /*cb*/, - void *user_data); - - bool opencl_version_check(); - OpenCLSplitPrograms *get_split_programs(); - - string device_md5_hash(string kernel_custom_build_options = ""); - bool load_kernels(const DeviceRequestedFeatures &requested_features); - void load_required_kernels(const DeviceRequestedFeatures &requested_features); - - bool wait_for_availability(const DeviceRequestedFeatures &requested_features); - DeviceKernelStatus get_active_kernel_switch_state(); - - /* Get the name of the opencl program for the given kernel */ - const string get_opencl_program_name(const string &kernel_name); - /* Get the program file name to compile (*.cl) for the given kernel */ - const string get_opencl_program_filename(const string &kernel_name); - string get_build_options(const DeviceRequestedFeatures &requested_features, - const string &opencl_program_name); - /* Enable the default features to reduce recompilation events */ - void enable_default_features(DeviceRequestedFeatures &features); - - void mem_alloc(device_memory &mem); - void mem_copy_to(device_memory &mem); - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem); - void mem_zero(device_memory &mem); - void mem_free(device_memory &mem); - - int mem_sub_ptr_alignment(); - - void const_copy_to(const char *name, void *host, size_t size); - void global_alloc(device_memory &mem); - void global_free(device_memory &mem); - void tex_alloc(device_texture &mem); - void tex_free(device_texture &mem); - - size_t global_size_round_up(int group_size, int global_size); - void enqueue_kernel(cl_kernel kernel, - size_t w, - size_t h, - bool x_workgroups = false, - size_t max_workgroup_size = -1); - void set_kernel_arg_mem(cl_kernel kernel, cl_uint *narg, const char *name); - void set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg); - - void film_convert(DeviceTask &task, - device_ptr buffer, - device_ptr rgba_byte, - device_ptr rgba_half); - void shader(DeviceTask &task); - void update_adaptive(DeviceTask &task, RenderTile &tile, int sample); - void bake(DeviceTask &task, RenderTile &tile); - - void denoise(RenderTile &tile, DenoisingTask &denoising); - - int get_split_task_count(DeviceTask & /*task*/) - { - return 1; - } - - void task_add(DeviceTask &task) - { - task_pool.push([=] { - DeviceTask task_copy = task; - thread_run(task_copy); - }); - } - - void task_wait() - { - task_pool.wait(); - } - - void task_cancel() - { - task_pool.cancel(); - } - - void thread_run(DeviceTask &task); - - virtual BVHLayoutMask get_bvh_layout_mask() const - { - return BVH_LAYOUT_BVH2; - } - - virtual bool show_samples() const - { - return true; - } - - protected: - string kernel_build_options(const string *debug_src = NULL); - - void mem_zero_kernel(device_ptr ptr, size_t size); - - bool denoising_non_local_means(device_ptr image_ptr, - device_ptr guide_ptr, - device_ptr variance_ptr, - device_ptr out_ptr, - DenoisingTask *task); - bool denoising_construct_transform(DenoisingTask *task); - bool denoising_accumulate(device_ptr color_ptr, - device_ptr color_variance_ptr, - device_ptr scale_ptr, - int frame, - DenoisingTask *task); - bool denoising_solve(device_ptr output_ptr, DenoisingTask *task); - bool denoising_combine_halves(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr mean_ptr, - device_ptr variance_ptr, - int r, - int4 rect, - DenoisingTask *task); - bool denoising_divide_shadow(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr sample_variance_ptr, - device_ptr sv_variance_ptr, - device_ptr buffer_variance_ptr, - DenoisingTask *task); - bool denoising_get_feature(int mean_offset, - int variance_offset, - device_ptr mean_ptr, - device_ptr variance_ptr, - float scale, - DenoisingTask *task); - bool denoising_write_feature(int to_offset, - device_ptr from_ptr, - device_ptr buffer_ptr, - DenoisingTask *task); - bool denoising_detect_outliers(device_ptr image_ptr, - device_ptr variance_ptr, - device_ptr depth_ptr, - device_ptr output_ptr, - DenoisingTask *task); - - device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int size); - void mem_free_sub_ptr(device_ptr ptr); - - class ArgumentWrapper { - public: - ArgumentWrapper() : size(0), pointer(NULL) - { - } - - ArgumentWrapper(device_memory &argument) - : size(sizeof(void *)), pointer((void *)(&argument.device_pointer)) - { - } - - template - ArgumentWrapper(device_vector &argument) - : size(sizeof(void *)), pointer((void *)(&argument.device_pointer)) - { - } - - template - ArgumentWrapper(device_only_memory &argument) - : size(sizeof(void *)), pointer((void *)(&argument.device_pointer)) - { - } - template ArgumentWrapper(T &argument) : size(sizeof(argument)), pointer(&argument) - { - } - - ArgumentWrapper(int argument) : size(sizeof(int)), int_value(argument), pointer(&int_value) - { - } - - ArgumentWrapper(float argument) - : size(sizeof(float)), float_value(argument), pointer(&float_value) - { - } - - size_t size; - int int_value; - float float_value; - void *pointer; - }; - - /* TODO(sergey): In the future we can use variadic templates, once - * C++0x is allowed. Should allow to clean this up a bit. - */ - int kernel_set_args(cl_kernel kernel, - int start_argument_index, - const ArgumentWrapper &arg1 = ArgumentWrapper(), - const ArgumentWrapper &arg2 = ArgumentWrapper(), - const ArgumentWrapper &arg3 = ArgumentWrapper(), - const ArgumentWrapper &arg4 = ArgumentWrapper(), - const ArgumentWrapper &arg5 = ArgumentWrapper(), - const ArgumentWrapper &arg6 = ArgumentWrapper(), - const ArgumentWrapper &arg7 = ArgumentWrapper(), - const ArgumentWrapper &arg8 = ArgumentWrapper(), - const ArgumentWrapper &arg9 = ArgumentWrapper(), - const ArgumentWrapper &arg10 = ArgumentWrapper(), - const ArgumentWrapper &arg11 = ArgumentWrapper(), - const ArgumentWrapper &arg12 = ArgumentWrapper(), - const ArgumentWrapper &arg13 = ArgumentWrapper(), - const ArgumentWrapper &arg14 = ArgumentWrapper(), - const ArgumentWrapper &arg15 = ArgumentWrapper(), - const ArgumentWrapper &arg16 = ArgumentWrapper(), - const ArgumentWrapper &arg17 = ArgumentWrapper(), - const ArgumentWrapper &arg18 = ArgumentWrapper(), - const ArgumentWrapper &arg19 = ArgumentWrapper(), - const ArgumentWrapper &arg20 = ArgumentWrapper(), - const ArgumentWrapper &arg21 = ArgumentWrapper(), - const ArgumentWrapper &arg22 = ArgumentWrapper(), - const ArgumentWrapper &arg23 = ArgumentWrapper(), - const ArgumentWrapper &arg24 = ArgumentWrapper(), - const ArgumentWrapper &arg25 = ArgumentWrapper(), - const ArgumentWrapper &arg26 = ArgumentWrapper(), - const ArgumentWrapper &arg27 = ArgumentWrapper(), - const ArgumentWrapper &arg28 = ArgumentWrapper(), - const ArgumentWrapper &arg29 = ArgumentWrapper(), - const ArgumentWrapper &arg30 = ArgumentWrapper(), - const ArgumentWrapper &arg31 = ArgumentWrapper(), - const ArgumentWrapper &arg32 = ArgumentWrapper(), - const ArgumentWrapper &arg33 = ArgumentWrapper()); - - void release_kernel_safe(cl_kernel kernel); - void release_mem_object_safe(cl_mem mem); - void release_program_safe(cl_program program); - - /* ** Those guys are for working around some compiler-specific bugs ** */ - - cl_program load_cached_kernel(ustring key, thread_scoped_lock &cache_locker); - - void store_cached_kernel(cl_program program, ustring key, thread_scoped_lock &cache_locker); - - private: - MemoryManager memory_manager; - friend class MemoryManager; - - static_assert_align(TextureInfo, 16); - device_vector texture_info; - - typedef map TexturesMap; - TexturesMap textures; - - bool textures_need_update; - - protected: - void flush_texture_buffers(); - - friend class OpenCLSplitKernel; - friend class OpenCLSplitKernelFunction; -}; - -Device *opencl_create_split_device(DeviceInfo &info, - Stats &stats, - Profiler &profiler, - bool background); - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp deleted file mode 100644 index 31a2265700c..00000000000 --- a/intern/cycles/device/opencl/device_opencl_impl.cpp +++ /dev/null @@ -1,2113 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPENCL - -# include "device/opencl/device_opencl.h" - -# include "kernel/kernel_types.h" -# include "kernel/split/kernel_split_data_types.h" - -# include "util/util_algorithm.h" -# include "util/util_debug.h" -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_md5.h" -# include "util/util_path.h" -# include "util/util_time.h" - -CCL_NAMESPACE_BEGIN - -struct texture_slot_t { - texture_slot_t(const string &name, int slot) : name(name), slot(slot) - { - } - string name; - int slot; -}; - -static const string NON_SPLIT_KERNELS = - "denoising " - "base " - "background " - "displace "; - -static const string SPLIT_BUNDLE_KERNELS = - "data_init " - "path_init " - "state_buffer_size " - "scene_intersect " - "queue_enqueue " - "shader_setup " - "shader_sort " - "enqueue_inactive " - "next_iteration_setup " - "indirect_subsurface " - "buffer_update " - "adaptive_stopping " - "adaptive_filter_x " - "adaptive_filter_y " - "adaptive_adjust_samples"; - -const string OpenCLDevice::get_opencl_program_name(const string &kernel_name) -{ - if (NON_SPLIT_KERNELS.find(kernel_name) != std::string::npos) { - return kernel_name; - } - else if (SPLIT_BUNDLE_KERNELS.find(kernel_name) != std::string::npos) { - return "split_bundle"; - } - else { - return "split_" + kernel_name; - } -} - -const string OpenCLDevice::get_opencl_program_filename(const string &kernel_name) -{ - if (kernel_name == "denoising") { - return "filter.cl"; - } - else if (SPLIT_BUNDLE_KERNELS.find(kernel_name) != std::string::npos) { - return "kernel_split_bundle.cl"; - } - else { - return "kernel_" + kernel_name + ".cl"; - } -} - -/* Enable features that we always want to compile to reduce recompilation events */ -void OpenCLDevice::enable_default_features(DeviceRequestedFeatures &features) -{ - features.use_transparent = true; - features.use_shadow_tricks = true; - features.use_principled = true; - features.use_denoising = true; - - if (!background) { - features.max_nodes_group = NODE_GROUP_LEVEL_MAX; - features.nodes_features = NODE_FEATURE_ALL; - features.use_hair = true; - features.use_subsurface = true; - features.use_camera_motion = false; - features.use_object_motion = false; - } -} - -string OpenCLDevice::get_build_options(const DeviceRequestedFeatures &requested_features, - const string &opencl_program_name) -{ - /* first check for non-split kernel programs */ - if (opencl_program_name == "base" || opencl_program_name == "denoising") { - return ""; - } - else if (opencl_program_name == "bake") { - /* Note: get_build_options for bake is only requested when baking is enabled. - * displace and background are always requested. - * `__SPLIT_KERNEL__` must not be present in the compile directives for bake */ - DeviceRequestedFeatures features(requested_features); - enable_default_features(features); - features.use_denoising = false; - features.use_object_motion = false; - features.use_camera_motion = false; - features.use_hair = true; - features.use_subsurface = true; - features.max_nodes_group = NODE_GROUP_LEVEL_MAX; - features.nodes_features = NODE_FEATURE_ALL; - features.use_integrator_branched = false; - return features.get_build_options(); - } - else if (opencl_program_name == "displace") { - /* As displacement does not use any nodes from the Shading group (eg BSDF). - * We disable all features that are related to shading. */ - DeviceRequestedFeatures features(requested_features); - enable_default_features(features); - features.use_denoising = false; - features.use_object_motion = false; - features.use_camera_motion = false; - features.use_baking = false; - features.use_transparent = false; - features.use_shadow_tricks = false; - features.use_subsurface = false; - features.use_volume = false; - features.nodes_features &= ~NODE_FEATURE_VOLUME; - features.use_denoising = false; - features.use_principled = false; - features.use_integrator_branched = false; - return features.get_build_options(); - } - else if (opencl_program_name == "background") { - /* Background uses Background shading - * It is save to disable shadow features, subsurface and volumetric. */ - DeviceRequestedFeatures features(requested_features); - enable_default_features(features); - features.use_baking = false; - features.use_object_motion = false; - features.use_camera_motion = false; - features.use_transparent = false; - features.use_shadow_tricks = false; - features.use_denoising = false; - /* NOTE: currently possible to use surface nodes like `Hair Info`, `Bump` node. - * Perhaps we should remove them in UI as it does not make any sense when - * rendering background. */ - features.nodes_features &= ~NODE_FEATURE_VOLUME; - features.use_subsurface = false; - features.use_volume = false; - features.use_shader_raytrace = false; - features.use_patch_evaluation = false; - features.use_integrator_branched = false; - return features.get_build_options(); - } - - string build_options = "-D__SPLIT_KERNEL__ "; - /* Set compute device build option. */ - cl_device_type device_type; - OpenCLInfo::get_device_type(this->cdDevice, &device_type, &this->ciErr); - assert(this->ciErr == CL_SUCCESS); - if (device_type == CL_DEVICE_TYPE_GPU) { - build_options += "-D__COMPUTE_DEVICE_GPU__ "; - } - - DeviceRequestedFeatures nofeatures; - enable_default_features(nofeatures); - - /* Add program specific optimized compile directives */ - if (opencl_program_name == "split_do_volume" && !requested_features.use_volume) { - build_options += nofeatures.get_build_options(); - } - else { - DeviceRequestedFeatures features(requested_features); - enable_default_features(features); - - /* Always turn off baking at this point. Baking is only useful when building the bake kernel. - * this also makes sure that the kernels that are build during baking can be reused - * when not doing any baking. */ - features.use_baking = false; - - /* Do not vary on shaders when program doesn't do any shading. - * We have bundled them in a single program. */ - if (opencl_program_name == "split_bundle") { - features.max_nodes_group = 0; - features.nodes_features = 0; - features.use_shader_raytrace = false; - } - - /* No specific settings, just add the regular ones */ - build_options += features.get_build_options(); - } - - return build_options; -} - -OpenCLDevice::OpenCLSplitPrograms::OpenCLSplitPrograms(OpenCLDevice *device_) -{ - device = device_; -} - -OpenCLDevice::OpenCLSplitPrograms::~OpenCLSplitPrograms() -{ - program_split.release(); - program_lamp_emission.release(); - program_do_volume.release(); - program_indirect_background.release(); - program_shader_eval.release(); - program_holdout_emission_blurring_pathtermination_ao.release(); - program_subsurface_scatter.release(); - program_direct_lighting.release(); - program_shadow_blocked_ao.release(); - program_shadow_blocked_dl.release(); -} - -void OpenCLDevice::OpenCLSplitPrograms::load_kernels( - vector &programs, const DeviceRequestedFeatures &requested_features) -{ - if (!requested_features.use_baking) { -# define ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(kernel_name) \ - program_split.add_kernel(ustring("path_trace_" #kernel_name)); -# define ADD_SPLIT_KERNEL_PROGRAM(kernel_name) \ - const string program_name_##kernel_name = "split_" #kernel_name; \ - program_##kernel_name = OpenCLDevice::OpenCLProgram( \ - device, \ - program_name_##kernel_name, \ - "kernel_" #kernel_name ".cl", \ - device->get_build_options(requested_features, program_name_##kernel_name)); \ - program_##kernel_name.add_kernel(ustring("path_trace_" #kernel_name)); \ - programs.push_back(&program_##kernel_name); - - /* Ordered with most complex kernels first, to reduce overall compile time. */ - ADD_SPLIT_KERNEL_PROGRAM(subsurface_scatter); - ADD_SPLIT_KERNEL_PROGRAM(direct_lighting); - ADD_SPLIT_KERNEL_PROGRAM(indirect_background); - if (requested_features.use_volume) { - ADD_SPLIT_KERNEL_PROGRAM(do_volume); - } - ADD_SPLIT_KERNEL_PROGRAM(shader_eval); - ADD_SPLIT_KERNEL_PROGRAM(lamp_emission); - ADD_SPLIT_KERNEL_PROGRAM(holdout_emission_blurring_pathtermination_ao); - ADD_SPLIT_KERNEL_PROGRAM(shadow_blocked_dl); - ADD_SPLIT_KERNEL_PROGRAM(shadow_blocked_ao); - - /* Quick kernels bundled in a single program to reduce overhead of starting - * Blender processes. */ - program_split = OpenCLDevice::OpenCLProgram( - device, - "split_bundle", - "kernel_split_bundle.cl", - device->get_build_options(requested_features, "split_bundle")); - - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(data_init); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(state_buffer_size); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(path_init); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(scene_intersect); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(queue_enqueue); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(shader_setup); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(shader_sort); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(enqueue_inactive); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(next_iteration_setup); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(indirect_subsurface); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(buffer_update); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(adaptive_stopping); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(adaptive_filter_x); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(adaptive_filter_y); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(adaptive_adjust_samples); - programs.push_back(&program_split); - -# undef ADD_SPLIT_KERNEL_PROGRAM -# undef ADD_SPLIT_KERNEL_BUNDLE_PROGRAM - } -} - -namespace { - -/* Copy dummy KernelGlobals related to OpenCL from kernel_globals.h to - * fetch its size. - */ -typedef struct KernelGlobalsDummy { - ccl_constant KernelData *data; - ccl_global char *buffers[8]; - -# define KERNEL_TEX(type, name) TextureInfo name; -# include "kernel/kernel_textures.h" -# undef KERNEL_TEX - SplitData split_data; - SplitParams split_param_data; -} KernelGlobalsDummy; - -} // namespace - -struct CachedSplitMemory { - int id; - device_memory *split_data; - device_memory *ray_state; - device_memory *queue_index; - device_memory *use_queues_flag; - device_memory *work_pools; - device_ptr *buffer; -}; - -class OpenCLSplitKernelFunction : public SplitKernelFunction { - public: - OpenCLDevice *device; - OpenCLDevice::OpenCLProgram program; - CachedSplitMemory &cached_memory; - int cached_id; - - OpenCLSplitKernelFunction(OpenCLDevice *device, CachedSplitMemory &cached_memory) - : device(device), cached_memory(cached_memory), cached_id(cached_memory.id - 1) - { - } - - ~OpenCLSplitKernelFunction() - { - program.release(); - } - - virtual bool enqueue(const KernelDimensions &dim, device_memory &kg, device_memory &data) - { - if (cached_id != cached_memory.id) { - cl_uint start_arg_index = device->kernel_set_args( - program(), 0, kg, data, *cached_memory.split_data, *cached_memory.ray_state); - - device->set_kernel_arg_buffers(program(), &start_arg_index); - - start_arg_index += device->kernel_set_args(program(), - start_arg_index, - *cached_memory.queue_index, - *cached_memory.use_queues_flag, - *cached_memory.work_pools, - *cached_memory.buffer); - - cached_id = cached_memory.id; - } - - device->ciErr = clEnqueueNDRangeKernel(device->cqCommandQueue, - program(), - 2, - NULL, - dim.global_size, - dim.local_size, - 0, - NULL, - NULL); - - device->opencl_assert_err(device->ciErr, "clEnqueueNDRangeKernel"); - - if (device->ciErr != CL_SUCCESS) { - string message = string_printf("OpenCL error: %s in clEnqueueNDRangeKernel()", - clewErrorString(device->ciErr)); - device->opencl_error(message); - return false; - } - - return true; - } -}; - -class OpenCLSplitKernel : public DeviceSplitKernel { - OpenCLDevice *device; - CachedSplitMemory cached_memory; - - public: - explicit OpenCLSplitKernel(OpenCLDevice *device) : DeviceSplitKernel(device), device(device) - { - } - - virtual SplitKernelFunction *get_split_kernel_function( - const string &kernel_name, const DeviceRequestedFeatures &requested_features) - { - OpenCLSplitKernelFunction *kernel = new OpenCLSplitKernelFunction(device, cached_memory); - - const string program_name = device->get_opencl_program_name(kernel_name); - kernel->program = OpenCLDevice::OpenCLProgram( - device, - program_name, - device->get_opencl_program_filename(kernel_name), - device->get_build_options(requested_features, program_name)); - - kernel->program.add_kernel(ustring("path_trace_" + kernel_name)); - kernel->program.load(); - - if (!kernel->program.is_loaded()) { - delete kernel; - return NULL; - } - - return kernel; - } - - virtual uint64_t state_buffer_size(device_memory &kg, device_memory &data, size_t num_threads) - { - device_vector size_buffer(device, "size_buffer", MEM_READ_WRITE); - size_buffer.alloc(1); - size_buffer.zero_to_device(); - - uint threads = num_threads; - OpenCLDevice::OpenCLSplitPrograms *programs = device->get_split_programs(); - cl_kernel kernel_state_buffer_size = programs->program_split( - ustring("path_trace_state_buffer_size")); - device->kernel_set_args(kernel_state_buffer_size, 0, kg, data, threads, size_buffer); - - size_t global_size = 64; - device->ciErr = clEnqueueNDRangeKernel(device->cqCommandQueue, - kernel_state_buffer_size, - 1, - NULL, - &global_size, - NULL, - 0, - NULL, - NULL); - - device->opencl_assert_err(device->ciErr, "clEnqueueNDRangeKernel"); - - size_buffer.copy_from_device(0, 1, 1); - size_t size = size_buffer[0]; - size_buffer.free(); - - if (device->ciErr != CL_SUCCESS) { - string message = string_printf("OpenCL error: %s in clEnqueueNDRangeKernel()", - clewErrorString(device->ciErr)); - device->opencl_error(message); - return 0; - } - - return size; - } - - virtual bool enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory &kernel_globals, - device_memory &kernel_data, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flag, - device_memory &work_pool_wgs) - { - cl_int dQueue_size = dim.global_size[0] * dim.global_size[1]; - - /* Set the range of samples to be processed for every ray in - * path-regeneration logic. - */ - cl_int start_sample = rtile.start_sample; - cl_int end_sample = rtile.start_sample + rtile.num_samples; - - OpenCLDevice::OpenCLSplitPrograms *programs = device->get_split_programs(); - cl_kernel kernel_data_init = programs->program_split(ustring("path_trace_data_init")); - - cl_uint start_arg_index = device->kernel_set_args(kernel_data_init, - 0, - kernel_globals, - kernel_data, - split_data, - num_global_elements, - ray_state); - - device->set_kernel_arg_buffers(kernel_data_init, &start_arg_index); - - start_arg_index += device->kernel_set_args(kernel_data_init, - start_arg_index, - start_sample, - end_sample, - rtile.x, - rtile.y, - rtile.w, - rtile.h, - rtile.offset, - rtile.stride, - queue_index, - dQueue_size, - use_queues_flag, - work_pool_wgs, - rtile.num_samples, - rtile.buffer); - - /* Enqueue ckPathTraceKernel_data_init kernel. */ - device->ciErr = clEnqueueNDRangeKernel(device->cqCommandQueue, - kernel_data_init, - 2, - NULL, - dim.global_size, - dim.local_size, - 0, - NULL, - NULL); - - device->opencl_assert_err(device->ciErr, "clEnqueueNDRangeKernel"); - - if (device->ciErr != CL_SUCCESS) { - string message = string_printf("OpenCL error: %s in clEnqueueNDRangeKernel()", - clewErrorString(device->ciErr)); - device->opencl_error(message); - return false; - } - - cached_memory.split_data = &split_data; - cached_memory.ray_state = &ray_state; - cached_memory.queue_index = &queue_index; - cached_memory.use_queues_flag = &use_queues_flag; - cached_memory.work_pools = &work_pool_wgs; - cached_memory.buffer = &rtile.buffer; - cached_memory.id++; - - return true; - } - - virtual int2 split_kernel_local_size() - { - return make_int2(64, 1); - } - - virtual int2 split_kernel_global_size(device_memory &kg, - device_memory &data, - DeviceTask & /*task*/) - { - cl_device_type type = OpenCLInfo::get_device_type(device->cdDevice); - /* Use small global size on CPU devices as it seems to be much faster. */ - if (type == CL_DEVICE_TYPE_CPU) { - VLOG(1) << "Global size: (64, 64)."; - return make_int2(64, 64); - } - - cl_ulong max_buffer_size; - clGetDeviceInfo( - device->cdDevice, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_buffer_size, NULL); - - if (DebugFlags().opencl.mem_limit) { - max_buffer_size = min(max_buffer_size, - cl_ulong(DebugFlags().opencl.mem_limit - device->stats.mem_used)); - } - - VLOG(1) << "Maximum device allocation size: " << string_human_readable_number(max_buffer_size) - << " bytes. (" << string_human_readable_size(max_buffer_size) << ")."; - - /* Limit to 2gb, as we shouldn't need more than that and some devices may support much more. */ - max_buffer_size = min(max_buffer_size / 2, (cl_ulong)2l * 1024 * 1024 * 1024); - - size_t num_elements = max_elements_for_max_buffer_size(kg, data, max_buffer_size); - int2 global_size = make_int2(max(round_down((int)sqrt(num_elements), 64), 64), - (int)sqrt(num_elements)); - - if (device->info.description.find("Intel") != string::npos) { - global_size = make_int2(min(512, global_size.x), min(512, global_size.y)); - } - - VLOG(1) << "Global size: " << global_size << "."; - return global_size; - } -}; - -bool OpenCLDevice::opencl_error(cl_int err) -{ - if (err != CL_SUCCESS) { - string message = string_printf("OpenCL error (%d): %s", err, clewErrorString(err)); - if (error_msg == "") - error_msg = message; - fprintf(stderr, "%s\n", message.c_str()); - return true; - } - - return false; -} - -void OpenCLDevice::opencl_error(const string &message) -{ - if (error_msg == "") - error_msg = message; - fprintf(stderr, "%s\n", message.c_str()); -} - -void OpenCLDevice::opencl_assert_err(cl_int err, const char *where) -{ - if (err != CL_SUCCESS) { - string message = string_printf( - "OpenCL error (%d): %s in %s", err, clewErrorString(err), where); - if (error_msg == "") - error_msg = message; - fprintf(stderr, "%s\n", message.c_str()); -# ifndef NDEBUG - abort(); -# endif - } -} - -OpenCLDevice::OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) - : Device(info, stats, profiler, background), - load_kernel_num_compiling(0), - kernel_programs(this), - memory_manager(this), - texture_info(this, "__texture_info", MEM_GLOBAL) -{ - cpPlatform = NULL; - cdDevice = NULL; - cxContext = NULL; - cqCommandQueue = NULL; - device_initialized = false; - textures_need_update = true; - - vector usable_devices; - OpenCLInfo::get_usable_devices(&usable_devices); - if (usable_devices.size() == 0) { - opencl_error("OpenCL: no devices found."); - return; - } - assert(info.num < usable_devices.size()); - OpenCLPlatformDevice &platform_device = usable_devices[info.num]; - device_num = info.num; - cpPlatform = platform_device.platform_id; - cdDevice = platform_device.device_id; - platform_name = platform_device.platform_name; - device_name = platform_device.device_name; - VLOG(2) << "Creating new Cycles device for OpenCL platform " << platform_name << ", device " - << device_name << "."; - - { - /* try to use cached context */ - thread_scoped_lock cache_locker; - cxContext = OpenCLCache::get_context(cpPlatform, cdDevice, cache_locker); - - if (cxContext == NULL) { - /* create context properties array to specify platform */ - const cl_context_properties context_props[] = { - CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 0, 0}; - - /* create context */ - cxContext = clCreateContext( - context_props, 1, &cdDevice, context_notify_callback, cdDevice, &ciErr); - - if (opencl_error(ciErr)) { - opencl_error("OpenCL: clCreateContext failed"); - return; - } - - /* cache it */ - OpenCLCache::store_context(cpPlatform, cdDevice, cxContext, cache_locker); - } - } - - cqCommandQueue = clCreateCommandQueue(cxContext, cdDevice, 0, &ciErr); - if (opencl_error(ciErr)) { - opencl_error("OpenCL: Error creating command queue"); - return; - } - - /* Allocate this right away so that texture_info - * is placed at offset 0 in the device memory buffers. */ - texture_info.resize(1); - memory_manager.alloc("texture_info", texture_info); - - device_initialized = true; - - split_kernel = new OpenCLSplitKernel(this); -} - -OpenCLDevice::~OpenCLDevice() -{ - task_pool.cancel(); - load_required_kernel_task_pool.cancel(); - load_kernel_task_pool.cancel(); - - memory_manager.free(); - - ConstMemMap::iterator mt; - for (mt = const_mem_map.begin(); mt != const_mem_map.end(); mt++) { - delete mt->second; - } - - base_program.release(); - bake_program.release(); - displace_program.release(); - background_program.release(); - denoising_program.release(); - - if (cqCommandQueue) - clReleaseCommandQueue(cqCommandQueue); - if (cxContext) - clReleaseContext(cxContext); - - delete split_kernel; -} - -void CL_CALLBACK OpenCLDevice::context_notify_callback(const char *err_info, - const void * /*private_info*/, - size_t /*cb*/, - void *user_data) -{ - string device_name = OpenCLInfo::get_device_name((cl_device_id)user_data); - fprintf(stderr, "OpenCL error (%s): %s\n", device_name.c_str(), err_info); -} - -bool OpenCLDevice::opencl_version_check() -{ - string error; - if (!OpenCLInfo::platform_version_check(cpPlatform, &error)) { - opencl_error(error); - return false; - } - if (!OpenCLInfo::device_version_check(cdDevice, &error)) { - opencl_error(error); - return false; - } - return true; -} - -string OpenCLDevice::device_md5_hash(string kernel_custom_build_options) -{ - MD5Hash md5; - char version[256], driver[256], name[256], vendor[256]; - - clGetPlatformInfo(cpPlatform, CL_PLATFORM_VENDOR, sizeof(vendor), &vendor, NULL); - clGetDeviceInfo(cdDevice, CL_DEVICE_VERSION, sizeof(version), &version, NULL); - clGetDeviceInfo(cdDevice, CL_DEVICE_NAME, sizeof(name), &name, NULL); - clGetDeviceInfo(cdDevice, CL_DRIVER_VERSION, sizeof(driver), &driver, NULL); - - md5.append((uint8_t *)vendor, strlen(vendor)); - md5.append((uint8_t *)version, strlen(version)); - md5.append((uint8_t *)name, strlen(name)); - md5.append((uint8_t *)driver, strlen(driver)); - - string options = kernel_build_options(); - options += kernel_custom_build_options; - md5.append((uint8_t *)options.c_str(), options.size()); - - return md5.get_hex(); -} - -bool OpenCLDevice::load_kernels(const DeviceRequestedFeatures &requested_features) -{ - VLOG(2) << "Loading kernels for platform " << platform_name << ", device " << device_name << "."; - /* Verify if device was initialized. */ - if (!device_initialized) { - fprintf(stderr, "OpenCL: failed to initialize device.\n"); - return false; - } - - /* Verify we have right opencl version. */ - if (!opencl_version_check()) - return false; - - load_required_kernels(requested_features); - - vector programs; - kernel_programs.load_kernels(programs, requested_features); - - if (!requested_features.use_baking && requested_features.use_denoising) { - denoising_program = OpenCLProgram( - this, "denoising", "filter.cl", get_build_options(requested_features, "denoising")); - denoising_program.add_kernel(ustring("filter_divide_shadow")); - denoising_program.add_kernel(ustring("filter_get_feature")); - denoising_program.add_kernel(ustring("filter_write_feature")); - denoising_program.add_kernel(ustring("filter_detect_outliers")); - denoising_program.add_kernel(ustring("filter_combine_halves")); - denoising_program.add_kernel(ustring("filter_construct_transform")); - denoising_program.add_kernel(ustring("filter_nlm_calc_difference")); - denoising_program.add_kernel(ustring("filter_nlm_blur")); - denoising_program.add_kernel(ustring("filter_nlm_calc_weight")); - denoising_program.add_kernel(ustring("filter_nlm_update_output")); - denoising_program.add_kernel(ustring("filter_nlm_normalize")); - denoising_program.add_kernel(ustring("filter_nlm_construct_gramian")); - denoising_program.add_kernel(ustring("filter_finalize")); - programs.push_back(&denoising_program); - } - - load_required_kernel_task_pool.wait_work(); - - /* Parallel compilation of Cycles kernels, this launches multiple - * processes to workaround OpenCL frameworks serializing the calls - * internally within a single process. */ - foreach (OpenCLProgram *program, programs) { - if (!program->load()) { - load_kernel_num_compiling++; - load_kernel_task_pool.push([=] { - program->compile(); - load_kernel_num_compiling--; - }); - } - } - return true; -} - -void OpenCLDevice::load_required_kernels(const DeviceRequestedFeatures &requested_features) -{ - vector programs; - base_program = OpenCLProgram( - this, "base", "kernel_base.cl", get_build_options(requested_features, "base")); - base_program.add_kernel(ustring("convert_to_byte")); - base_program.add_kernel(ustring("convert_to_half_float")); - base_program.add_kernel(ustring("zero_buffer")); - programs.push_back(&base_program); - - if (requested_features.use_true_displacement) { - displace_program = OpenCLProgram( - this, "displace", "kernel_displace.cl", get_build_options(requested_features, "displace")); - displace_program.add_kernel(ustring("displace")); - programs.push_back(&displace_program); - } - - if (requested_features.use_background_light) { - background_program = OpenCLProgram(this, - "background", - "kernel_background.cl", - get_build_options(requested_features, "background")); - background_program.add_kernel(ustring("background")); - programs.push_back(&background_program); - } - - if (requested_features.use_baking) { - bake_program = OpenCLProgram( - this, "bake", "kernel_bake.cl", get_build_options(requested_features, "bake")); - bake_program.add_kernel(ustring("bake")); - programs.push_back(&bake_program); - } - - foreach (OpenCLProgram *program, programs) { - if (!program->load()) { - load_required_kernel_task_pool.push(function_bind(&OpenCLProgram::compile, program)); - } - } -} - -bool OpenCLDevice::wait_for_availability(const DeviceRequestedFeatures &requested_features) -{ - if (requested_features.use_baking) { - /* For baking, kernels have already been loaded in load_required_kernels(). */ - return true; - } - - load_kernel_task_pool.wait_work(); - return split_kernel->load_kernels(requested_features); -} - -OpenCLDevice::OpenCLSplitPrograms *OpenCLDevice::get_split_programs() -{ - return &kernel_programs; -} - -DeviceKernelStatus OpenCLDevice::get_active_kernel_switch_state() -{ - return DEVICE_KERNEL_USING_FEATURE_KERNEL; -} - -void OpenCLDevice::mem_alloc(device_memory &mem) -{ - if (mem.name) { - VLOG(1) << "Buffer allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - } - - size_t size = mem.memory_size(); - - /* check there is enough memory available for the allocation */ - cl_ulong max_alloc_size = 0; - clGetDeviceInfo(cdDevice, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_alloc_size, NULL); - - if (DebugFlags().opencl.mem_limit) { - max_alloc_size = min(max_alloc_size, cl_ulong(DebugFlags().opencl.mem_limit - stats.mem_used)); - } - - if (size > max_alloc_size) { - string error = "Scene too complex to fit in available memory."; - if (mem.name != NULL) { - error += string_printf(" (allocating buffer %s failed.)", mem.name); - } - set_error(error); - - return; - } - - cl_mem_flags mem_flag; - void *mem_ptr = NULL; - - if (mem.type == MEM_READ_ONLY || mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) - mem_flag = CL_MEM_READ_ONLY; - else - mem_flag = CL_MEM_READ_WRITE; - - /* Zero-size allocation might be invoked by render, but not really - * supported by OpenCL. Using NULL as device pointer also doesn't really - * work for some reason, so for the time being we'll use special case - * will null_mem buffer. - */ - if (size != 0) { - mem.device_pointer = (device_ptr)clCreateBuffer(cxContext, mem_flag, size, mem_ptr, &ciErr); - opencl_assert_err(ciErr, "clCreateBuffer"); - } - else { - mem.device_pointer = 0; - } - - stats.mem_alloc(size); - mem.device_size = size; -} - -void OpenCLDevice::mem_copy_to(device_memory &mem) -{ - if (mem.type == MEM_GLOBAL) { - global_free(mem); - global_alloc(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - tex_alloc((device_texture &)mem); - } - else { - if (!mem.device_pointer) { - mem_alloc(mem); - } - - /* this is blocking */ - size_t size = mem.memory_size(); - if (size != 0) { - opencl_assert(clEnqueueWriteBuffer(cqCommandQueue, - CL_MEM_PTR(mem.device_pointer), - CL_TRUE, - 0, - size, - mem.host_pointer, - 0, - NULL, - NULL)); - } - } -} - -void OpenCLDevice::mem_copy_from(device_memory &mem, int y, int w, int h, int elem) -{ - size_t offset = elem * y * w; - size_t size = elem * w * h; - assert(size != 0); - opencl_assert(clEnqueueReadBuffer(cqCommandQueue, - CL_MEM_PTR(mem.device_pointer), - CL_TRUE, - offset, - size, - (uchar *)mem.host_pointer + offset, - 0, - NULL, - NULL)); -} - -void OpenCLDevice::mem_zero_kernel(device_ptr mem, size_t size) -{ - base_program.wait_for_availability(); - cl_kernel ckZeroBuffer = base_program(ustring("zero_buffer")); - - size_t global_size[] = {1024, 1024}; - size_t num_threads = global_size[0] * global_size[1]; - - cl_mem d_buffer = CL_MEM_PTR(mem); - cl_ulong d_offset = 0; - cl_ulong d_size = 0; - - while (d_offset < size) { - d_size = std::min(num_threads * sizeof(float4), size - d_offset); - - kernel_set_args(ckZeroBuffer, 0, d_buffer, d_size, d_offset); - - ciErr = clEnqueueNDRangeKernel( - cqCommandQueue, ckZeroBuffer, 2, NULL, global_size, NULL, 0, NULL, NULL); - opencl_assert_err(ciErr, "clEnqueueNDRangeKernel"); - - d_offset += d_size; - } -} - -void OpenCLDevice::mem_zero(device_memory &mem) -{ - if (!mem.device_pointer) { - mem_alloc(mem); - } - - if (mem.device_pointer) { - if (base_program.is_loaded()) { - mem_zero_kernel(mem.device_pointer, mem.memory_size()); - } - - if (mem.host_pointer) { - memset(mem.host_pointer, 0, mem.memory_size()); - } - - if (!base_program.is_loaded()) { - void *zero = mem.host_pointer; - - if (!mem.host_pointer) { - zero = util_aligned_malloc(mem.memory_size(), 16); - memset(zero, 0, mem.memory_size()); - } - - opencl_assert(clEnqueueWriteBuffer(cqCommandQueue, - CL_MEM_PTR(mem.device_pointer), - CL_TRUE, - 0, - mem.memory_size(), - zero, - 0, - NULL, - NULL)); - - if (!mem.host_pointer) { - util_aligned_free(zero); - } - } - } -} - -void OpenCLDevice::mem_free(device_memory &mem) -{ - if (mem.type == MEM_GLOBAL) { - global_free(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - } - else { - if (mem.device_pointer) { - if (mem.device_pointer != 0) { - opencl_assert(clReleaseMemObject(CL_MEM_PTR(mem.device_pointer))); - } - mem.device_pointer = 0; - - stats.mem_free(mem.device_size); - mem.device_size = 0; - } - } -} - -int OpenCLDevice::mem_sub_ptr_alignment() -{ - return OpenCLInfo::mem_sub_ptr_alignment(cdDevice); -} - -device_ptr OpenCLDevice::mem_alloc_sub_ptr(device_memory &mem, int offset, int size) -{ - cl_mem_flags mem_flag; - if (mem.type == MEM_READ_ONLY || mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) - mem_flag = CL_MEM_READ_ONLY; - else - mem_flag = CL_MEM_READ_WRITE; - - cl_buffer_region info; - info.origin = mem.memory_elements_size(offset); - info.size = mem.memory_elements_size(size); - - device_ptr sub_buf = (device_ptr)clCreateSubBuffer( - CL_MEM_PTR(mem.device_pointer), mem_flag, CL_BUFFER_CREATE_TYPE_REGION, &info, &ciErr); - opencl_assert_err(ciErr, "clCreateSubBuffer"); - return sub_buf; -} - -void OpenCLDevice::mem_free_sub_ptr(device_ptr device_pointer) -{ - if (device_pointer != 0) { - opencl_assert(clReleaseMemObject(CL_MEM_PTR(device_pointer))); - } -} - -void OpenCLDevice::const_copy_to(const char *name, void *host, size_t size) -{ - ConstMemMap::iterator i = const_mem_map.find(name); - device_vector *data; - - if (i == const_mem_map.end()) { - data = new device_vector(this, name, MEM_READ_ONLY); - data->alloc(size); - const_mem_map.insert(ConstMemMap::value_type(name, data)); - } - else { - data = i->second; - } - - memcpy(data->data(), host, size); - data->copy_to_device(); -} - -void OpenCLDevice::global_alloc(device_memory &mem) -{ - VLOG(1) << "Global memory allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - - memory_manager.alloc(mem.name, mem); - /* Set the pointer to non-null to keep code that inspects its value from thinking its - * unallocated. */ - mem.device_pointer = 1; - textures[mem.name] = &mem; - textures_need_update = true; -} - -void OpenCLDevice::global_free(device_memory &mem) -{ - if (mem.device_pointer) { - mem.device_pointer = 0; - - if (memory_manager.free(mem)) { - textures_need_update = true; - } - - foreach (TexturesMap::value_type &value, textures) { - if (value.second == &mem) { - textures.erase(value.first); - break; - } - } - } -} - -void OpenCLDevice::tex_alloc(device_texture &mem) -{ - VLOG(1) << "Texture allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - - memory_manager.alloc(mem.name, mem); - /* Set the pointer to non-null to keep code that inspects its value from thinking its - * unallocated. */ - mem.device_pointer = 1; - textures[mem.name] = &mem; - textures_need_update = true; -} - -void OpenCLDevice::tex_free(device_texture &mem) -{ - global_free(mem); -} - -size_t OpenCLDevice::global_size_round_up(int group_size, int global_size) -{ - int r = global_size % group_size; - return global_size + ((r == 0) ? 0 : group_size - r); -} - -void OpenCLDevice::enqueue_kernel( - cl_kernel kernel, size_t w, size_t h, bool x_workgroups, size_t max_workgroup_size) -{ - size_t workgroup_size, max_work_items[3]; - - clGetKernelWorkGroupInfo( - kernel, cdDevice, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &workgroup_size, NULL); - clGetDeviceInfo( - cdDevice, CL_DEVICE_MAX_WORK_ITEM_SIZES, sizeof(size_t) * 3, max_work_items, NULL); - - if (max_workgroup_size > 0 && workgroup_size > max_workgroup_size) { - workgroup_size = max_workgroup_size; - } - - /* Try to divide evenly over 2 dimensions. */ - size_t local_size[2]; - if (x_workgroups) { - local_size[0] = workgroup_size; - local_size[1] = 1; - } - else { - size_t sqrt_workgroup_size = max((size_t)sqrt((double)workgroup_size), 1); - local_size[0] = local_size[1] = sqrt_workgroup_size; - } - - /* Some implementations have max size 1 on 2nd dimension. */ - if (local_size[1] > max_work_items[1]) { - local_size[0] = workgroup_size / max_work_items[1]; - local_size[1] = max_work_items[1]; - } - - size_t global_size[2] = {global_size_round_up(local_size[0], w), - global_size_round_up(local_size[1], h)}; - - /* Vertical size of 1 is coming from bake/shade kernels where we should - * not round anything up because otherwise we'll either be doing too - * much work per pixel (if we don't check global ID on Y axis) or will - * be checking for global ID to always have Y of 0. - */ - if (h == 1) { - global_size[h] = 1; - } - - /* run kernel */ - opencl_assert( - clEnqueueNDRangeKernel(cqCommandQueue, kernel, 2, NULL, global_size, NULL, 0, NULL, NULL)); - opencl_assert(clFlush(cqCommandQueue)); -} - -void OpenCLDevice::set_kernel_arg_mem(cl_kernel kernel, cl_uint *narg, const char *name) -{ - cl_mem ptr; - - MemMap::iterator i = mem_map.find(name); - if (i != mem_map.end()) { - ptr = CL_MEM_PTR(i->second); - } - else { - ptr = 0; - } - - opencl_assert(clSetKernelArg(kernel, (*narg)++, sizeof(ptr), (void *)&ptr)); -} - -void OpenCLDevice::set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg) -{ - flush_texture_buffers(); - - memory_manager.set_kernel_arg_buffers(kernel, narg); -} - -void OpenCLDevice::flush_texture_buffers() -{ - if (!textures_need_update) { - return; - } - textures_need_update = false; - - /* Setup slots for textures. */ - int num_slots = 0; - - vector texture_slots; - -# define KERNEL_TEX(type, name) \ - if (textures.find(#name) != textures.end()) { \ - texture_slots.push_back(texture_slot_t(#name, num_slots)); \ - } \ - num_slots++; -# include "kernel/kernel_textures.h" - - int num_data_slots = num_slots; - - foreach (TexturesMap::value_type &tex, textures) { - string name = tex.first; - device_memory *mem = tex.second; - - if (mem->type == MEM_TEXTURE) { - const uint id = ((device_texture *)mem)->slot; - texture_slots.push_back(texture_slot_t(name, num_data_slots + id)); - num_slots = max(num_slots, num_data_slots + id + 1); - } - } - - /* Realloc texture descriptors buffer. */ - memory_manager.free(texture_info); - texture_info.resize(num_slots); - memory_manager.alloc("texture_info", texture_info); - - /* Fill in descriptors */ - foreach (texture_slot_t &slot, texture_slots) { - device_memory *mem = textures[slot.name]; - TextureInfo &info = texture_info[slot.slot]; - - MemoryManager::BufferDescriptor desc = memory_manager.get_descriptor(slot.name); - - if (mem->type == MEM_TEXTURE) { - info = ((device_texture *)mem)->info; - } - else { - memset(&info, 0, sizeof(TextureInfo)); - } - - info.data = desc.offset; - info.cl_buffer = desc.device_buffer; - } - - /* Force write of descriptors. */ - memory_manager.free(texture_info); - memory_manager.alloc("texture_info", texture_info); -} - -void OpenCLDevice::thread_run(DeviceTask &task) -{ - flush_texture_buffers(); - - if (task.type == DeviceTask::RENDER) { - RenderTile tile; - DenoisingTask denoising(this, task); - - /* Allocate buffer for kernel globals */ - device_only_memory kgbuffer(this, "kernel_globals"); - kgbuffer.alloc_to_device(1); - - /* Keep rendering tiles until done. */ - while (task.acquire_tile(this, tile, task.tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) { - assert(tile.task == RenderTile::PATH_TRACE); - scoped_timer timer(&tile.buffers->render_time); - - split_kernel->path_trace(task, tile, kgbuffer, *const_mem_map["__data"]); - - /* Complete kernel execution before release tile. */ - /* This helps in multi-device render; - * The device that reaches the critical-section function - * release_tile waits (stalling other devices from entering - * release_tile) for all kernels to complete. If device1 (a - * slow-render device) reaches release_tile first then it would - * stall device2 (a fast-render device) from proceeding to render - * next tile. - */ - clFinish(cqCommandQueue); - } - else if (tile.task == RenderTile::BAKE) { - bake(task, tile); - } - else if (tile.task == RenderTile::DENOISE) { - tile.sample = tile.start_sample + tile.num_samples; - denoise(tile, denoising); - task.update_progress(&tile, tile.w * tile.h); - } - - task.release_tile(tile); - } - - kgbuffer.free(); - } - else if (task.type == DeviceTask::SHADER) { - shader(task); - } - else if (task.type == DeviceTask::FILM_CONVERT) { - film_convert(task, task.buffer, task.rgba_byte, task.rgba_half); - } - else if (task.type == DeviceTask::DENOISE_BUFFER) { - RenderTile tile; - tile.x = task.x; - tile.y = task.y; - tile.w = task.w; - tile.h = task.h; - tile.buffer = task.buffer; - tile.sample = task.sample + task.num_samples; - tile.num_samples = task.num_samples; - tile.start_sample = task.sample; - tile.offset = task.offset; - tile.stride = task.stride; - tile.buffers = task.buffers; - - DenoisingTask denoising(this, task); - denoise(tile, denoising); - task.update_progress(&tile, tile.w * tile.h); - } -} - -void OpenCLDevice::film_convert(DeviceTask &task, - device_ptr buffer, - device_ptr rgba_byte, - device_ptr rgba_half) -{ - /* cast arguments to cl types */ - cl_mem d_data = CL_MEM_PTR(const_mem_map["__data"]->device_pointer); - cl_mem d_rgba = (rgba_byte) ? CL_MEM_PTR(rgba_byte) : CL_MEM_PTR(rgba_half); - cl_mem d_buffer = CL_MEM_PTR(buffer); - cl_int d_x = task.x; - cl_int d_y = task.y; - cl_int d_w = task.w; - cl_int d_h = task.h; - cl_float d_sample_scale = 1.0f / (task.sample + 1); - cl_int d_offset = task.offset; - cl_int d_stride = task.stride; - - cl_kernel ckFilmConvertKernel = (rgba_byte) ? base_program(ustring("convert_to_byte")) : - base_program(ustring("convert_to_half_float")); - - cl_uint start_arg_index = kernel_set_args(ckFilmConvertKernel, 0, d_data, d_rgba, d_buffer); - - set_kernel_arg_buffers(ckFilmConvertKernel, &start_arg_index); - - start_arg_index += kernel_set_args(ckFilmConvertKernel, - start_arg_index, - d_sample_scale, - d_x, - d_y, - d_w, - d_h, - d_offset, - d_stride); - - enqueue_kernel(ckFilmConvertKernel, d_w, d_h); -} - -bool OpenCLDevice::denoising_non_local_means(device_ptr image_ptr, - device_ptr guide_ptr, - device_ptr variance_ptr, - device_ptr out_ptr, - DenoisingTask *task) -{ - int stride = task->buffer.stride; - int w = task->buffer.width; - int h = task->buffer.h; - int r = task->nlm_state.r; - int f = task->nlm_state.f; - float a = task->nlm_state.a; - float k_2 = task->nlm_state.k_2; - - int pass_stride = task->buffer.pass_stride; - int num_shifts = (2 * r + 1) * (2 * r + 1); - int channel_offset = task->nlm_state.is_color ? task->buffer.pass_stride : 0; - - device_sub_ptr difference(task->buffer.temporary_mem, 0, pass_stride * num_shifts); - device_sub_ptr blurDifference( - task->buffer.temporary_mem, pass_stride * num_shifts, pass_stride * num_shifts); - device_sub_ptr weightAccum( - task->buffer.temporary_mem, 2 * pass_stride * num_shifts, pass_stride); - cl_mem weightAccum_mem = CL_MEM_PTR(*weightAccum); - cl_mem difference_mem = CL_MEM_PTR(*difference); - cl_mem blurDifference_mem = CL_MEM_PTR(*blurDifference); - - cl_mem image_mem = CL_MEM_PTR(image_ptr); - cl_mem guide_mem = CL_MEM_PTR(guide_ptr); - cl_mem variance_mem = CL_MEM_PTR(variance_ptr); - cl_mem out_mem = CL_MEM_PTR(out_ptr); - cl_mem scale_mem = NULL; - - mem_zero_kernel(*weightAccum, sizeof(float) * pass_stride); - mem_zero_kernel(out_ptr, sizeof(float) * pass_stride); - - cl_kernel ckNLMCalcDifference = denoising_program(ustring("filter_nlm_calc_difference")); - cl_kernel ckNLMBlur = denoising_program(ustring("filter_nlm_blur")); - cl_kernel ckNLMCalcWeight = denoising_program(ustring("filter_nlm_calc_weight")); - cl_kernel ckNLMUpdateOutput = denoising_program(ustring("filter_nlm_update_output")); - cl_kernel ckNLMNormalize = denoising_program(ustring("filter_nlm_normalize")); - - kernel_set_args(ckNLMCalcDifference, - 0, - guide_mem, - variance_mem, - scale_mem, - difference_mem, - w, - h, - stride, - pass_stride, - r, - channel_offset, - 0, - a, - k_2); - kernel_set_args( - ckNLMBlur, 0, difference_mem, blurDifference_mem, w, h, stride, pass_stride, r, f); - kernel_set_args( - ckNLMCalcWeight, 0, blurDifference_mem, difference_mem, w, h, stride, pass_stride, r, f); - kernel_set_args(ckNLMUpdateOutput, - 0, - blurDifference_mem, - image_mem, - out_mem, - weightAccum_mem, - w, - h, - stride, - pass_stride, - channel_offset, - r, - f); - - enqueue_kernel(ckNLMCalcDifference, w * h, num_shifts, true); - enqueue_kernel(ckNLMBlur, w * h, num_shifts, true); - enqueue_kernel(ckNLMCalcWeight, w * h, num_shifts, true); - enqueue_kernel(ckNLMBlur, w * h, num_shifts, true); - enqueue_kernel(ckNLMUpdateOutput, w * h, num_shifts, true); - - kernel_set_args(ckNLMNormalize, 0, out_mem, weightAccum_mem, w, h, stride); - enqueue_kernel(ckNLMNormalize, w, h); - - return true; -} - -bool OpenCLDevice::denoising_construct_transform(DenoisingTask *task) -{ - cl_mem buffer_mem = CL_MEM_PTR(task->buffer.mem.device_pointer); - cl_mem transform_mem = CL_MEM_PTR(task->storage.transform.device_pointer); - cl_mem rank_mem = CL_MEM_PTR(task->storage.rank.device_pointer); - cl_mem tile_info_mem = CL_MEM_PTR(task->tile_info_mem.device_pointer); - - char use_time = task->buffer.use_time ? 1 : 0; - - cl_kernel ckFilterConstructTransform = denoising_program(ustring("filter_construct_transform")); - - int arg_ofs = kernel_set_args(ckFilterConstructTransform, 0, buffer_mem, tile_info_mem); - cl_mem buffers[9]; - for (int i = 0; i < 9; i++) { - buffers[i] = CL_MEM_PTR(task->tile_info->buffers[i]); - arg_ofs += kernel_set_args(ckFilterConstructTransform, arg_ofs, buffers[i]); - } - kernel_set_args(ckFilterConstructTransform, - arg_ofs, - transform_mem, - rank_mem, - task->filter_area, - task->rect, - task->buffer.pass_stride, - task->buffer.frame_stride, - use_time, - task->radius, - task->pca_threshold); - - enqueue_kernel(ckFilterConstructTransform, task->storage.w, task->storage.h, 256); - - return true; -} - -bool OpenCLDevice::denoising_accumulate(device_ptr color_ptr, - device_ptr color_variance_ptr, - device_ptr scale_ptr, - int frame, - DenoisingTask *task) -{ - cl_mem color_mem = CL_MEM_PTR(color_ptr); - cl_mem color_variance_mem = CL_MEM_PTR(color_variance_ptr); - cl_mem scale_mem = CL_MEM_PTR(scale_ptr); - - cl_mem buffer_mem = CL_MEM_PTR(task->buffer.mem.device_pointer); - cl_mem transform_mem = CL_MEM_PTR(task->storage.transform.device_pointer); - cl_mem rank_mem = CL_MEM_PTR(task->storage.rank.device_pointer); - cl_mem XtWX_mem = CL_MEM_PTR(task->storage.XtWX.device_pointer); - cl_mem XtWY_mem = CL_MEM_PTR(task->storage.XtWY.device_pointer); - - cl_kernel ckNLMCalcDifference = denoising_program(ustring("filter_nlm_calc_difference")); - cl_kernel ckNLMBlur = denoising_program(ustring("filter_nlm_blur")); - cl_kernel ckNLMCalcWeight = denoising_program(ustring("filter_nlm_calc_weight")); - cl_kernel ckNLMConstructGramian = denoising_program(ustring("filter_nlm_construct_gramian")); - - int w = task->reconstruction_state.source_w; - int h = task->reconstruction_state.source_h; - int stride = task->buffer.stride; - int frame_offset = frame * task->buffer.frame_stride; - int t = task->tile_info->frames[frame]; - char use_time = task->buffer.use_time ? 1 : 0; - - int r = task->radius; - int pass_stride = task->buffer.pass_stride; - int num_shifts = (2 * r + 1) * (2 * r + 1); - - device_sub_ptr difference(task->buffer.temporary_mem, 0, pass_stride * num_shifts); - device_sub_ptr blurDifference( - task->buffer.temporary_mem, pass_stride * num_shifts, pass_stride * num_shifts); - cl_mem difference_mem = CL_MEM_PTR(*difference); - cl_mem blurDifference_mem = CL_MEM_PTR(*blurDifference); - - kernel_set_args(ckNLMCalcDifference, - 0, - color_mem, - color_variance_mem, - scale_mem, - difference_mem, - w, - h, - stride, - pass_stride, - r, - pass_stride, - frame_offset, - 1.0f, - task->nlm_k_2); - kernel_set_args( - ckNLMBlur, 0, difference_mem, blurDifference_mem, w, h, stride, pass_stride, r, 4); - kernel_set_args( - ckNLMCalcWeight, 0, blurDifference_mem, difference_mem, w, h, stride, pass_stride, r, 4); - kernel_set_args(ckNLMConstructGramian, - 0, - t, - blurDifference_mem, - buffer_mem, - transform_mem, - rank_mem, - XtWX_mem, - XtWY_mem, - task->reconstruction_state.filter_window, - w, - h, - stride, - pass_stride, - r, - 4, - frame_offset, - use_time); - - enqueue_kernel(ckNLMCalcDifference, w * h, num_shifts, true); - enqueue_kernel(ckNLMBlur, w * h, num_shifts, true); - enqueue_kernel(ckNLMCalcWeight, w * h, num_shifts, true); - enqueue_kernel(ckNLMBlur, w * h, num_shifts, true); - enqueue_kernel(ckNLMConstructGramian, w * h, num_shifts, true, 256); - - return true; -} - -bool OpenCLDevice::denoising_solve(device_ptr output_ptr, DenoisingTask *task) -{ - cl_kernel ckFinalize = denoising_program(ustring("filter_finalize")); - - cl_mem output_mem = CL_MEM_PTR(output_ptr); - cl_mem rank_mem = CL_MEM_PTR(task->storage.rank.device_pointer); - cl_mem XtWX_mem = CL_MEM_PTR(task->storage.XtWX.device_pointer); - cl_mem XtWY_mem = CL_MEM_PTR(task->storage.XtWY.device_pointer); - - int w = task->reconstruction_state.source_w; - int h = task->reconstruction_state.source_h; - - kernel_set_args(ckFinalize, - 0, - output_mem, - rank_mem, - XtWX_mem, - XtWY_mem, - task->filter_area, - task->reconstruction_state.buffer_params, - task->render_buffer.samples); - enqueue_kernel(ckFinalize, w, h); - - return true; -} - -bool OpenCLDevice::denoising_combine_halves(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr mean_ptr, - device_ptr variance_ptr, - int r, - int4 rect, - DenoisingTask *task) -{ - cl_mem a_mem = CL_MEM_PTR(a_ptr); - cl_mem b_mem = CL_MEM_PTR(b_ptr); - cl_mem mean_mem = CL_MEM_PTR(mean_ptr); - cl_mem variance_mem = CL_MEM_PTR(variance_ptr); - - cl_kernel ckFilterCombineHalves = denoising_program(ustring("filter_combine_halves")); - - kernel_set_args(ckFilterCombineHalves, 0, mean_mem, variance_mem, a_mem, b_mem, rect, r); - enqueue_kernel(ckFilterCombineHalves, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - return true; -} - -bool OpenCLDevice::denoising_divide_shadow(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr sample_variance_ptr, - device_ptr sv_variance_ptr, - device_ptr buffer_variance_ptr, - DenoisingTask *task) -{ - cl_mem a_mem = CL_MEM_PTR(a_ptr); - cl_mem b_mem = CL_MEM_PTR(b_ptr); - cl_mem sample_variance_mem = CL_MEM_PTR(sample_variance_ptr); - cl_mem sv_variance_mem = CL_MEM_PTR(sv_variance_ptr); - cl_mem buffer_variance_mem = CL_MEM_PTR(buffer_variance_ptr); - - cl_mem tile_info_mem = CL_MEM_PTR(task->tile_info_mem.device_pointer); - - cl_kernel ckFilterDivideShadow = denoising_program(ustring("filter_divide_shadow")); - - int arg_ofs = kernel_set_args( - ckFilterDivideShadow, 0, task->render_buffer.samples, tile_info_mem); - cl_mem buffers[9]; - for (int i = 0; i < 9; i++) { - buffers[i] = CL_MEM_PTR(task->tile_info->buffers[i]); - arg_ofs += kernel_set_args(ckFilterDivideShadow, arg_ofs, buffers[i]); - } - kernel_set_args(ckFilterDivideShadow, - arg_ofs, - a_mem, - b_mem, - sample_variance_mem, - sv_variance_mem, - buffer_variance_mem, - task->rect, - task->render_buffer.pass_stride, - task->render_buffer.offset); - enqueue_kernel(ckFilterDivideShadow, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - return true; -} - -bool OpenCLDevice::denoising_get_feature(int mean_offset, - int variance_offset, - device_ptr mean_ptr, - device_ptr variance_ptr, - float scale, - DenoisingTask *task) -{ - cl_mem mean_mem = CL_MEM_PTR(mean_ptr); - cl_mem variance_mem = CL_MEM_PTR(variance_ptr); - - cl_mem tile_info_mem = CL_MEM_PTR(task->tile_info_mem.device_pointer); - - cl_kernel ckFilterGetFeature = denoising_program(ustring("filter_get_feature")); - - int arg_ofs = kernel_set_args(ckFilterGetFeature, 0, task->render_buffer.samples, tile_info_mem); - cl_mem buffers[9]; - for (int i = 0; i < 9; i++) { - buffers[i] = CL_MEM_PTR(task->tile_info->buffers[i]); - arg_ofs += kernel_set_args(ckFilterGetFeature, arg_ofs, buffers[i]); - } - kernel_set_args(ckFilterGetFeature, - arg_ofs, - mean_offset, - variance_offset, - mean_mem, - variance_mem, - scale, - task->rect, - task->render_buffer.pass_stride, - task->render_buffer.offset); - enqueue_kernel(ckFilterGetFeature, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - return true; -} - -bool OpenCLDevice::denoising_write_feature(int out_offset, - device_ptr from_ptr, - device_ptr buffer_ptr, - DenoisingTask *task) -{ - cl_mem from_mem = CL_MEM_PTR(from_ptr); - cl_mem buffer_mem = CL_MEM_PTR(buffer_ptr); - - cl_kernel ckFilterWriteFeature = denoising_program(ustring("filter_write_feature")); - - kernel_set_args(ckFilterWriteFeature, - 0, - task->render_buffer.samples, - task->reconstruction_state.buffer_params, - task->filter_area, - from_mem, - buffer_mem, - out_offset, - task->rect); - enqueue_kernel(ckFilterWriteFeature, task->filter_area.z, task->filter_area.w); - - return true; -} - -bool OpenCLDevice::denoising_detect_outliers(device_ptr image_ptr, - device_ptr variance_ptr, - device_ptr depth_ptr, - device_ptr output_ptr, - DenoisingTask *task) -{ - cl_mem image_mem = CL_MEM_PTR(image_ptr); - cl_mem variance_mem = CL_MEM_PTR(variance_ptr); - cl_mem depth_mem = CL_MEM_PTR(depth_ptr); - cl_mem output_mem = CL_MEM_PTR(output_ptr); - - cl_kernel ckFilterDetectOutliers = denoising_program(ustring("filter_detect_outliers")); - - kernel_set_args(ckFilterDetectOutliers, - 0, - image_mem, - variance_mem, - depth_mem, - output_mem, - task->rect, - task->buffer.pass_stride); - enqueue_kernel(ckFilterDetectOutliers, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - return true; -} - -void OpenCLDevice::denoise(RenderTile &rtile, DenoisingTask &denoising) -{ - denoising.functions.construct_transform = function_bind( - &OpenCLDevice::denoising_construct_transform, this, &denoising); - denoising.functions.accumulate = function_bind( - &OpenCLDevice::denoising_accumulate, this, _1, _2, _3, _4, &denoising); - denoising.functions.solve = function_bind(&OpenCLDevice::denoising_solve, this, _1, &denoising); - denoising.functions.divide_shadow = function_bind( - &OpenCLDevice::denoising_divide_shadow, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.non_local_means = function_bind( - &OpenCLDevice::denoising_non_local_means, this, _1, _2, _3, _4, &denoising); - denoising.functions.combine_halves = function_bind( - &OpenCLDevice::denoising_combine_halves, this, _1, _2, _3, _4, _5, _6, &denoising); - denoising.functions.get_feature = function_bind( - &OpenCLDevice::denoising_get_feature, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.write_feature = function_bind( - &OpenCLDevice::denoising_write_feature, this, _1, _2, _3, &denoising); - denoising.functions.detect_outliers = function_bind( - &OpenCLDevice::denoising_detect_outliers, this, _1, _2, _3, _4, &denoising); - - denoising.filter_area = make_int4(rtile.x, rtile.y, rtile.w, rtile.h); - denoising.render_buffer.samples = rtile.sample; - denoising.buffer.gpu_temporary_mem = true; - - denoising.run_denoising(rtile); -} - -void OpenCLDevice::shader(DeviceTask &task) -{ - /* cast arguments to cl types */ - cl_mem d_data = CL_MEM_PTR(const_mem_map["__data"]->device_pointer); - cl_mem d_input = CL_MEM_PTR(task.shader_input); - cl_mem d_output = CL_MEM_PTR(task.shader_output); - cl_int d_shader_eval_type = task.shader_eval_type; - cl_int d_shader_filter = task.shader_filter; - cl_int d_shader_x = task.shader_x; - cl_int d_shader_w = task.shader_w; - cl_int d_offset = task.offset; - - OpenCLDevice::OpenCLProgram *program = &background_program; - if (task.shader_eval_type == SHADER_EVAL_DISPLACE) { - program = &displace_program; - } - program->wait_for_availability(); - cl_kernel kernel = (*program)(); - - cl_uint start_arg_index = kernel_set_args(kernel, 0, d_data, d_input, d_output); - - set_kernel_arg_buffers(kernel, &start_arg_index); - - start_arg_index += kernel_set_args(kernel, start_arg_index, d_shader_eval_type); - if (task.shader_eval_type >= SHADER_EVAL_BAKE) { - start_arg_index += kernel_set_args(kernel, start_arg_index, d_shader_filter); - } - start_arg_index += kernel_set_args(kernel, start_arg_index, d_shader_x, d_shader_w, d_offset); - - for (int sample = 0; sample < task.num_samples; sample++) { - - if (task.get_cancel()) - break; - - kernel_set_args(kernel, start_arg_index, sample); - - enqueue_kernel(kernel, task.shader_w, 1); - - clFinish(cqCommandQueue); - - task.update_progress(NULL); - } -} - -void OpenCLDevice::bake(DeviceTask &task, RenderTile &rtile) -{ - scoped_timer timer(&rtile.buffers->render_time); - - /* Cast arguments to cl types. */ - cl_mem d_data = CL_MEM_PTR(const_mem_map["__data"]->device_pointer); - cl_mem d_buffer = CL_MEM_PTR(rtile.buffer); - cl_int d_x = rtile.x; - cl_int d_y = rtile.y; - cl_int d_w = rtile.w; - cl_int d_h = rtile.h; - cl_int d_offset = rtile.offset; - cl_int d_stride = rtile.stride; - - bake_program.wait_for_availability(); - cl_kernel kernel = bake_program(); - - cl_uint start_arg_index = kernel_set_args(kernel, 0, d_data, d_buffer); - - set_kernel_arg_buffers(kernel, &start_arg_index); - - start_arg_index += kernel_set_args( - kernel, start_arg_index, d_x, d_y, d_w, d_h, d_offset, d_stride); - - int start_sample = rtile.start_sample; - int end_sample = rtile.start_sample + rtile.num_samples; - - for (int sample = start_sample; sample < end_sample; sample++) { - if (task.get_cancel()) { - if (task.need_finish_queue == false) - break; - } - - kernel_set_args(kernel, start_arg_index, sample); - - enqueue_kernel(kernel, d_w, d_h); - clFinish(cqCommandQueue); - - rtile.sample = sample + 1; - - task.update_progress(&rtile, rtile.w * rtile.h); - } -} - -static bool kernel_build_opencl_2(cl_device_id cdDevice) -{ - /* Build with OpenCL 2.0 if available, this improves performance - * with AMD OpenCL drivers on Windows and Linux (legacy drivers). - * Note that OpenCL selects the highest 1.x version by default, - * only for 2.0 do we need the explicit compiler flag. */ - int version_major, version_minor; - if (OpenCLInfo::get_device_version(cdDevice, &version_major, &version_minor)) { - if (version_major >= 2) { - /* This appears to trigger a driver bug in Radeon RX cards with certain - * driver version, so don't use OpenCL 2.0 for those. */ - string device_name = OpenCLInfo::get_readable_device_name(cdDevice); - if (string_startswith(device_name, "Radeon RX 4") || - string_startswith(device_name, "Radeon (TM) RX 4") || - string_startswith(device_name, "Radeon RX 5") || - string_startswith(device_name, "Radeon (TM) RX 5")) { - char version[256] = ""; - int driver_major, driver_minor; - clGetDeviceInfo(cdDevice, CL_DEVICE_VERSION, sizeof(version), &version, NULL); - if (sscanf(version, "OpenCL 2.0 AMD-APP (%d.%d)", &driver_major, &driver_minor) == 2) { - return !(driver_major == 3075 && driver_minor <= 12); - } - } - - return true; - } - } - - return false; -} - -string OpenCLDevice::kernel_build_options(const string *debug_src) -{ - string build_options = "-cl-no-signed-zeros -cl-mad-enable "; - - if (kernel_build_opencl_2(cdDevice)) { - build_options += "-cl-std=CL2.0 "; - } - - if (platform_name == "NVIDIA CUDA") { - build_options += - "-D__KERNEL_OPENCL_NVIDIA__ " - "-cl-nv-maxrregcount=32 " - "-cl-nv-verbose "; - - uint compute_capability_major, compute_capability_minor; - clGetDeviceInfo(cdDevice, - CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV, - sizeof(cl_uint), - &compute_capability_major, - NULL); - clGetDeviceInfo(cdDevice, - CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV, - sizeof(cl_uint), - &compute_capability_minor, - NULL); - - build_options += string_printf("-D__COMPUTE_CAPABILITY__=%u ", - compute_capability_major * 100 + compute_capability_minor * 10); - } - - else if (platform_name == "Apple") - build_options += "-D__KERNEL_OPENCL_APPLE__ "; - - else if (platform_name == "AMD Accelerated Parallel Processing") - build_options += "-D__KERNEL_OPENCL_AMD__ "; - - else if (platform_name == "Intel(R) OpenCL") { - build_options += "-D__KERNEL_OPENCL_INTEL_CPU__ "; - - /* Options for gdb source level kernel debugging. - * this segfaults on linux currently. - */ - if (OpenCLInfo::use_debug() && debug_src) - build_options += "-g -s \"" + *debug_src + "\" "; - } - - if (info.has_half_images) { - build_options += "-D__KERNEL_CL_KHR_FP16__ "; - } - - if (OpenCLInfo::use_debug()) { - build_options += "-D__KERNEL_OPENCL_DEBUG__ "; - } - -# ifdef WITH_NANOVDB - if (info.has_nanovdb) { - build_options += "-DWITH_NANOVDB "; - } -# endif - - return build_options; -} - -/* TODO(sergey): In the future we can use variadic templates, once - * C++0x is allowed. Should allow to clean this up a bit. - */ -int OpenCLDevice::kernel_set_args(cl_kernel kernel, - int start_argument_index, - const ArgumentWrapper &arg1, - const ArgumentWrapper &arg2, - const ArgumentWrapper &arg3, - const ArgumentWrapper &arg4, - const ArgumentWrapper &arg5, - const ArgumentWrapper &arg6, - const ArgumentWrapper &arg7, - const ArgumentWrapper &arg8, - const ArgumentWrapper &arg9, - const ArgumentWrapper &arg10, - const ArgumentWrapper &arg11, - const ArgumentWrapper &arg12, - const ArgumentWrapper &arg13, - const ArgumentWrapper &arg14, - const ArgumentWrapper &arg15, - const ArgumentWrapper &arg16, - const ArgumentWrapper &arg17, - const ArgumentWrapper &arg18, - const ArgumentWrapper &arg19, - const ArgumentWrapper &arg20, - const ArgumentWrapper &arg21, - const ArgumentWrapper &arg22, - const ArgumentWrapper &arg23, - const ArgumentWrapper &arg24, - const ArgumentWrapper &arg25, - const ArgumentWrapper &arg26, - const ArgumentWrapper &arg27, - const ArgumentWrapper &arg28, - const ArgumentWrapper &arg29, - const ArgumentWrapper &arg30, - const ArgumentWrapper &arg31, - const ArgumentWrapper &arg32, - const ArgumentWrapper &arg33) -{ - int current_arg_index = 0; -# define FAKE_VARARG_HANDLE_ARG(arg) \ - do { \ - if (arg.pointer != NULL) { \ - opencl_assert(clSetKernelArg( \ - kernel, start_argument_index + current_arg_index, arg.size, arg.pointer)); \ - ++current_arg_index; \ - } \ - else { \ - return current_arg_index; \ - } \ - } while (false) - FAKE_VARARG_HANDLE_ARG(arg1); - FAKE_VARARG_HANDLE_ARG(arg2); - FAKE_VARARG_HANDLE_ARG(arg3); - FAKE_VARARG_HANDLE_ARG(arg4); - FAKE_VARARG_HANDLE_ARG(arg5); - FAKE_VARARG_HANDLE_ARG(arg6); - FAKE_VARARG_HANDLE_ARG(arg7); - FAKE_VARARG_HANDLE_ARG(arg8); - FAKE_VARARG_HANDLE_ARG(arg9); - FAKE_VARARG_HANDLE_ARG(arg10); - FAKE_VARARG_HANDLE_ARG(arg11); - FAKE_VARARG_HANDLE_ARG(arg12); - FAKE_VARARG_HANDLE_ARG(arg13); - FAKE_VARARG_HANDLE_ARG(arg14); - FAKE_VARARG_HANDLE_ARG(arg15); - FAKE_VARARG_HANDLE_ARG(arg16); - FAKE_VARARG_HANDLE_ARG(arg17); - FAKE_VARARG_HANDLE_ARG(arg18); - FAKE_VARARG_HANDLE_ARG(arg19); - FAKE_VARARG_HANDLE_ARG(arg20); - FAKE_VARARG_HANDLE_ARG(arg21); - FAKE_VARARG_HANDLE_ARG(arg22); - FAKE_VARARG_HANDLE_ARG(arg23); - FAKE_VARARG_HANDLE_ARG(arg24); - FAKE_VARARG_HANDLE_ARG(arg25); - FAKE_VARARG_HANDLE_ARG(arg26); - FAKE_VARARG_HANDLE_ARG(arg27); - FAKE_VARARG_HANDLE_ARG(arg28); - FAKE_VARARG_HANDLE_ARG(arg29); - FAKE_VARARG_HANDLE_ARG(arg30); - FAKE_VARARG_HANDLE_ARG(arg31); - FAKE_VARARG_HANDLE_ARG(arg32); - FAKE_VARARG_HANDLE_ARG(arg33); -# undef FAKE_VARARG_HANDLE_ARG - return current_arg_index; -} - -void OpenCLDevice::release_kernel_safe(cl_kernel kernel) -{ - if (kernel) { - clReleaseKernel(kernel); - } -} - -void OpenCLDevice::release_mem_object_safe(cl_mem mem) -{ - if (mem != NULL) { - clReleaseMemObject(mem); - } -} - -void OpenCLDevice::release_program_safe(cl_program program) -{ - if (program) { - clReleaseProgram(program); - } -} - -/* ** Those guys are for working around some compiler-specific bugs ** */ - -cl_program OpenCLDevice::load_cached_kernel(ustring key, thread_scoped_lock &cache_locker) -{ - return OpenCLCache::get_program(cpPlatform, cdDevice, key, cache_locker); -} - -void OpenCLDevice::store_cached_kernel(cl_program program, - ustring key, - thread_scoped_lock &cache_locker) -{ - OpenCLCache::store_program(cpPlatform, cdDevice, program, key, cache_locker); -} - -Device *opencl_create_split_device(DeviceInfo &info, - Stats &stats, - Profiler &profiler, - bool background) -{ - return new OpenCLDevice(info, stats, profiler, background); -} - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/opencl/memory_manager.cpp b/intern/cycles/device/opencl/memory_manager.cpp deleted file mode 100644 index 4330e07cb37..00000000000 --- a/intern/cycles/device/opencl/memory_manager.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPENCL - -# include "util/util_foreach.h" - -# include "device/opencl/device_opencl.h" -# include "device/opencl/memory_manager.h" - -CCL_NAMESPACE_BEGIN - -void MemoryManager::DeviceBuffer::add_allocation(Allocation &allocation) -{ - allocations.push_back(&allocation); -} - -void MemoryManager::DeviceBuffer::update_device_memory(OpenCLDevice *device) -{ - bool need_realloc = false; - - /* Calculate total size and remove any freed. */ - size_t total_size = 0; - - for (int i = allocations.size() - 1; i >= 0; i--) { - Allocation *allocation = allocations[i]; - - /* Remove allocations that have been freed. */ - if (!allocation->mem || allocation->mem->memory_size() == 0) { - allocation->device_buffer = NULL; - allocation->size = 0; - - allocations.erase(allocations.begin() + i); - - need_realloc = true; - - continue; - } - - /* Get actual size for allocation. */ - size_t alloc_size = align_up(allocation->mem->memory_size(), 16); - - if (allocation->size != alloc_size) { - /* Allocation is either new or resized. */ - allocation->size = alloc_size; - allocation->needs_copy_to_device = true; - - need_realloc = true; - } - - total_size += alloc_size; - } - - /* Always allocate non-empty buffer, NULL pointers cause problems with some drivers. */ - total_size = std::max(total_size, (size_t)16); - - if (need_realloc) { - cl_ulong max_buffer_size; - clGetDeviceInfo( - device->cdDevice, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_buffer_size, NULL); - - if (total_size > max_buffer_size) { - device->set_error("Scene too complex to fit in available memory."); - return; - } - - device_only_memory *new_buffer = new device_only_memory(device, - "memory manager buffer"); - - new_buffer->alloc_to_device(total_size); - - size_t offset = 0; - - foreach (Allocation *allocation, allocations) { - if (allocation->needs_copy_to_device) { - /* Copy from host to device. */ - opencl_device_assert(device, - clEnqueueWriteBuffer(device->cqCommandQueue, - CL_MEM_PTR(new_buffer->device_pointer), - CL_FALSE, - offset, - allocation->mem->memory_size(), - allocation->mem->host_pointer, - 0, - NULL, - NULL)); - - allocation->needs_copy_to_device = false; - } - else { - /* Fast copy from memory already on device. */ - opencl_device_assert(device, - clEnqueueCopyBuffer(device->cqCommandQueue, - CL_MEM_PTR(buffer->device_pointer), - CL_MEM_PTR(new_buffer->device_pointer), - allocation->desc.offset, - offset, - allocation->mem->memory_size(), - 0, - NULL, - NULL)); - } - - allocation->desc.offset = offset; - offset += allocation->size; - } - - delete buffer; - - buffer = new_buffer; - } - else { - assert(total_size == buffer->data_size); - - size_t offset = 0; - - foreach (Allocation *allocation, allocations) { - if (allocation->needs_copy_to_device) { - /* Copy from host to device. */ - opencl_device_assert(device, - clEnqueueWriteBuffer(device->cqCommandQueue, - CL_MEM_PTR(buffer->device_pointer), - CL_FALSE, - offset, - allocation->mem->memory_size(), - allocation->mem->host_pointer, - 0, - NULL, - NULL)); - - allocation->needs_copy_to_device = false; - } - - offset += allocation->size; - } - } - - /* Not really necessary, but seems to improve responsiveness for some reason. */ - clFinish(device->cqCommandQueue); -} - -void MemoryManager::DeviceBuffer::free(OpenCLDevice *) -{ - buffer->free(); -} - -MemoryManager::DeviceBuffer *MemoryManager::smallest_device_buffer() -{ - DeviceBuffer *smallest = device_buffers; - - foreach (DeviceBuffer &device_buffer, device_buffers) { - if (device_buffer.size < smallest->size) { - smallest = &device_buffer; - } - } - - return smallest; -} - -MemoryManager::MemoryManager(OpenCLDevice *device) : device(device), need_update(false) -{ - foreach (DeviceBuffer &device_buffer, device_buffers) { - device_buffer.buffer = new device_only_memory(device, "memory manager buffer"); - } -} - -void MemoryManager::free() -{ - foreach (DeviceBuffer &device_buffer, device_buffers) { - device_buffer.free(device); - } -} - -void MemoryManager::alloc(const char *name, device_memory &mem) -{ - Allocation &allocation = allocations[name]; - - allocation.mem = &mem; - allocation.needs_copy_to_device = true; - - if (!allocation.device_buffer) { - DeviceBuffer *device_buffer = smallest_device_buffer(); - allocation.device_buffer = device_buffer; - - allocation.desc.device_buffer = device_buffer - device_buffers; - - device_buffer->add_allocation(allocation); - - device_buffer->size += mem.memory_size(); - } - - need_update = true; -} - -bool MemoryManager::free(device_memory &mem) -{ - foreach (AllocationsMap::value_type &value, allocations) { - Allocation &allocation = value.second; - if (allocation.mem == &mem) { - - allocation.device_buffer->size -= mem.memory_size(); - - allocation.mem = NULL; - allocation.needs_copy_to_device = false; - - need_update = true; - return true; - } - } - - return false; -} - -MemoryManager::BufferDescriptor MemoryManager::get_descriptor(string name) -{ - update_device_memory(); - - Allocation &allocation = allocations[name]; - return allocation.desc; -} - -void MemoryManager::update_device_memory() -{ - if (!need_update) { - return; - } - - need_update = false; - - foreach (DeviceBuffer &device_buffer, device_buffers) { - device_buffer.update_device_memory(device); - } -} - -void MemoryManager::set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg) -{ - update_device_memory(); - - foreach (DeviceBuffer &device_buffer, device_buffers) { - if (device_buffer.buffer->device_pointer) { - device->kernel_set_args(kernel, (*narg)++, *device_buffer.buffer); - } - else { - device->kernel_set_args(kernel, (*narg)++); - } - } -} - -CCL_NAMESPACE_END - -#endif /* WITH_OPENCL */ diff --git a/intern/cycles/device/opencl/memory_manager.h b/intern/cycles/device/opencl/memory_manager.h deleted file mode 100644 index 23624f837a6..00000000000 --- a/intern/cycles/device/opencl/memory_manager.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "device/device.h" - -#include "util/util_map.h" -#include "util/util_string.h" -#include "util/util_vector.h" - -#include "clew.h" - -CCL_NAMESPACE_BEGIN - -class OpenCLDevice; - -class MemoryManager { - public: - static const int NUM_DEVICE_BUFFERS = 8; - - struct BufferDescriptor { - uint device_buffer; - cl_ulong offset; - }; - - private: - struct DeviceBuffer; - - struct Allocation { - device_memory *mem; - - DeviceBuffer *device_buffer; - size_t size; /* Size of actual allocation, may be larger than requested. */ - - BufferDescriptor desc; - - bool needs_copy_to_device; - - Allocation() : mem(NULL), device_buffer(NULL), size(0), needs_copy_to_device(false) - { - } - }; - - struct DeviceBuffer { - device_only_memory *buffer; - vector allocations; - size_t size; /* Size of all allocations. */ - - DeviceBuffer() : buffer(NULL), size(0) - { - } - - ~DeviceBuffer() - { - delete buffer; - buffer = NULL; - } - - void add_allocation(Allocation &allocation); - - void update_device_memory(OpenCLDevice *device); - - void free(OpenCLDevice *device); - }; - - OpenCLDevice *device; - - DeviceBuffer device_buffers[NUM_DEVICE_BUFFERS]; - - typedef unordered_map AllocationsMap; - AllocationsMap allocations; - - bool need_update; - - DeviceBuffer *smallest_device_buffer(); - - public: - MemoryManager(OpenCLDevice *device); - - void free(); /* Free all memory. */ - - void alloc(const char *name, device_memory &mem); - bool free(device_memory &mem); - - BufferDescriptor get_descriptor(string name); - - void update_device_memory(); - void set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg); -}; - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/opencl/opencl_util.cpp b/intern/cycles/device/opencl/opencl_util.cpp deleted file mode 100644 index 3929cf77f15..00000000000 --- a/intern/cycles/device/opencl/opencl_util.cpp +++ /dev/null @@ -1,1326 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPENCL - -# include "device/device_intern.h" -# include "device/opencl/device_opencl.h" - -# include "util/util_debug.h" -# include "util/util_logging.h" -# include "util/util_md5.h" -# include "util/util_path.h" -# include "util/util_semaphore.h" -# include "util/util_system.h" -# include "util/util_time.h" - -using std::cerr; -using std::endl; - -CCL_NAMESPACE_BEGIN - -OpenCLCache::Slot::ProgramEntry::ProgramEntry() : program(NULL), mutex(NULL) -{ -} - -OpenCLCache::Slot::ProgramEntry::ProgramEntry(const ProgramEntry &rhs) - : program(rhs.program), mutex(NULL) -{ -} - -OpenCLCache::Slot::ProgramEntry::~ProgramEntry() -{ - delete mutex; -} - -OpenCLCache::Slot::Slot() : context_mutex(NULL), context(NULL) -{ -} - -OpenCLCache::Slot::Slot(const Slot &rhs) - : context_mutex(NULL), context(NULL), programs(rhs.programs) -{ -} - -OpenCLCache::Slot::~Slot() -{ - delete context_mutex; -} - -OpenCLCache &OpenCLCache::global_instance() -{ - static OpenCLCache instance; - return instance; -} - -cl_context OpenCLCache::get_context(cl_platform_id platform, - cl_device_id device, - thread_scoped_lock &slot_locker) -{ - assert(platform != NULL); - - OpenCLCache &self = global_instance(); - - thread_scoped_lock cache_lock(self.cache_lock); - - pair ins = self.cache.insert( - CacheMap::value_type(PlatformDevicePair(platform, device), Slot())); - - Slot &slot = ins.first->second; - - /* create slot lock only while holding cache lock */ - if (!slot.context_mutex) - slot.context_mutex = new thread_mutex; - - /* need to unlock cache before locking slot, to allow store to complete */ - cache_lock.unlock(); - - /* lock the slot */ - slot_locker = thread_scoped_lock(*slot.context_mutex); - - /* If the thing isn't cached */ - if (slot.context == NULL) { - /* return with the caller's lock holder holding the slot lock */ - return NULL; - } - - /* the item was already cached, release the slot lock */ - slot_locker.unlock(); - - cl_int ciErr = clRetainContext(slot.context); - assert(ciErr == CL_SUCCESS); - (void)ciErr; - - return slot.context; -} - -cl_program OpenCLCache::get_program(cl_platform_id platform, - cl_device_id device, - ustring key, - thread_scoped_lock &slot_locker) -{ - assert(platform != NULL); - - OpenCLCache &self = global_instance(); - - thread_scoped_lock cache_lock(self.cache_lock); - - pair ins = self.cache.insert( - CacheMap::value_type(PlatformDevicePair(platform, device), Slot())); - - Slot &slot = ins.first->second; - - pair ins2 = slot.programs.insert( - Slot::EntryMap::value_type(key, Slot::ProgramEntry())); - - Slot::ProgramEntry &entry = ins2.first->second; - - /* create slot lock only while holding cache lock */ - if (!entry.mutex) - entry.mutex = new thread_mutex; - - /* need to unlock cache before locking slot, to allow store to complete */ - cache_lock.unlock(); - - /* lock the slot */ - slot_locker = thread_scoped_lock(*entry.mutex); - - /* If the thing isn't cached */ - if (entry.program == NULL) { - /* return with the caller's lock holder holding the slot lock */ - return NULL; - } - - /* the item was already cached, release the slot lock */ - slot_locker.unlock(); - - cl_int ciErr = clRetainProgram(entry.program); - assert(ciErr == CL_SUCCESS); - (void)ciErr; - - return entry.program; -} - -void OpenCLCache::store_context(cl_platform_id platform, - cl_device_id device, - cl_context context, - thread_scoped_lock &slot_locker) -{ - assert(platform != NULL); - assert(device != NULL); - assert(context != NULL); - - OpenCLCache &self = global_instance(); - - thread_scoped_lock cache_lock(self.cache_lock); - CacheMap::iterator i = self.cache.find(PlatformDevicePair(platform, device)); - cache_lock.unlock(); - - Slot &slot = i->second; - - /* sanity check */ - assert(i != self.cache.end()); - assert(slot.context == NULL); - - slot.context = context; - - /* unlock the slot */ - slot_locker.unlock(); - - /* increment reference count in OpenCL. - * The caller is going to release the object when done with it. */ - cl_int ciErr = clRetainContext(context); - assert(ciErr == CL_SUCCESS); - (void)ciErr; -} - -void OpenCLCache::store_program(cl_platform_id platform, - cl_device_id device, - cl_program program, - ustring key, - thread_scoped_lock &slot_locker) -{ - assert(platform != NULL); - assert(device != NULL); - assert(program != NULL); - - OpenCLCache &self = global_instance(); - - thread_scoped_lock cache_lock(self.cache_lock); - - CacheMap::iterator i = self.cache.find(PlatformDevicePair(platform, device)); - assert(i != self.cache.end()); - Slot &slot = i->second; - - Slot::EntryMap::iterator i2 = slot.programs.find(key); - assert(i2 != slot.programs.end()); - Slot::ProgramEntry &entry = i2->second; - - assert(entry.program == NULL); - - cache_lock.unlock(); - - entry.program = program; - - /* unlock the slot */ - slot_locker.unlock(); - - /* Increment reference count in OpenCL. - * The caller is going to release the object when done with it. - */ - cl_int ciErr = clRetainProgram(program); - assert(ciErr == CL_SUCCESS); - (void)ciErr; -} - -string OpenCLCache::get_kernel_md5() -{ - OpenCLCache &self = global_instance(); - thread_scoped_lock lock(self.kernel_md5_lock); - - if (self.kernel_md5.empty()) { - self.kernel_md5 = path_files_md5_hash(path_get("source")); - } - return self.kernel_md5; -} - -static string get_program_source(const string &kernel_file) -{ - string source = "#include \"kernel/kernels/opencl/" + kernel_file + "\"\n"; - /* We compile kernels consisting of many files. unfortunately OpenCL - * kernel caches do not seem to recognize changes in included files. - * so we force recompile on changes by adding the md5 hash of all files. - */ - source = path_source_replace_includes(source, path_get("source")); - source += "\n// " + util_md5_string(source) + "\n"; - return source; -} - -OpenCLDevice::OpenCLProgram::OpenCLProgram(OpenCLDevice *device, - const string &program_name, - const string &kernel_file, - const string &kernel_build_options, - bool use_stdout) - : device(device), - program_name(program_name), - kernel_file(kernel_file), - kernel_build_options(kernel_build_options), - use_stdout(use_stdout) -{ - loaded = false; - needs_compiling = true; - program = NULL; -} - -OpenCLDevice::OpenCLProgram::~OpenCLProgram() -{ - release(); -} - -void OpenCLDevice::OpenCLProgram::release() -{ - for (map::iterator kernel = kernels.begin(); kernel != kernels.end(); - ++kernel) { - if (kernel->second) { - clReleaseKernel(kernel->second); - kernel->second = NULL; - } - } - if (program) { - clReleaseProgram(program); - program = NULL; - } -} - -void OpenCLDevice::OpenCLProgram::add_log(const string &msg, bool debug) -{ - if (!use_stdout) { - log += msg + "\n"; - } - else if (!debug) { - printf("%s\n", msg.c_str()); - fflush(stdout); - } - else { - VLOG(2) << msg; - } -} - -void OpenCLDevice::OpenCLProgram::add_error(const string &msg) -{ - if (use_stdout) { - fprintf(stderr, "%s\n", msg.c_str()); - } - if (error_msg == "") { - error_msg += "\n"; - } - error_msg += msg; -} - -void OpenCLDevice::OpenCLProgram::add_kernel(ustring name) -{ - if (!kernels.count(name)) { - kernels[name] = NULL; - } -} - -bool OpenCLDevice::OpenCLProgram::build_kernel(const string *debug_src) -{ - string build_options; - build_options = device->kernel_build_options(debug_src) + kernel_build_options; - - VLOG(1) << "Build options passed to clBuildProgram: '" << build_options << "'."; - cl_int ciErr = clBuildProgram(program, 0, NULL, build_options.c_str(), NULL, NULL); - - /* show warnings even if build is successful */ - size_t ret_val_size = 0; - - clGetProgramBuildInfo(program, device->cdDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size); - - if (ciErr != CL_SUCCESS) { - add_error(string("OpenCL build failed with error ") + clewErrorString(ciErr) + - ", errors in console."); - } - - if (ret_val_size > 1) { - vector build_log(ret_val_size + 1); - clGetProgramBuildInfo( - program, device->cdDevice, CL_PROGRAM_BUILD_LOG, ret_val_size, &build_log[0], NULL); - - build_log[ret_val_size] = '\0'; - /* Skip meaningless empty output from the NVidia compiler. */ - if (!(ret_val_size == 2 && build_log[0] == '\n')) { - add_log(string("OpenCL program ") + program_name + " build output: " + string(&build_log[0]), - ciErr == CL_SUCCESS); - } - } - - return (ciErr == CL_SUCCESS); -} - -bool OpenCLDevice::OpenCLProgram::compile_kernel(const string *debug_src) -{ - string source = get_program_source(kernel_file); - - if (debug_src) { - path_write_text(*debug_src, source); - } - - size_t source_len = source.size(); - const char *source_str = source.c_str(); - cl_int ciErr; - - program = clCreateProgramWithSource(device->cxContext, 1, &source_str, &source_len, &ciErr); - - if (ciErr != CL_SUCCESS) { - add_error(string("OpenCL program creation failed: ") + clewErrorString(ciErr)); - return false; - } - - double starttime = time_dt(); - add_log(string("Cycles: compiling OpenCL program ") + program_name + "...", false); - add_log(string("Build flags: ") + kernel_build_options, true); - - if (!build_kernel(debug_src)) - return false; - - double elapsed = time_dt() - starttime; - add_log( - string_printf("Kernel compilation of %s finished in %.2lfs.", program_name.c_str(), elapsed), - false); - - return true; -} - -static void escape_python_string(string &str) -{ - /* Escape string to be passed as a Python raw string with '' quotes'. */ - string_replace(str, "'", "\'"); -} - -static int opencl_compile_process_limit() -{ - /* Limit number of concurrent processes compiling, with a heuristic based - * on total physical RAM and estimate of memory usage needed when compiling - * with all Cycles features enabled. - * - * This is somewhat arbitrary as we don't know the actual available RAM or - * how much the kernel compilation will needed depending on the features, but - * better than not limiting at all. */ - static const int64_t GB = 1024LL * 1024LL * 1024LL; - static const int64_t process_memory = 2 * GB; - static const int64_t base_memory = 2 * GB; - static const int64_t system_memory = system_physical_ram(); - static const int64_t process_limit = (system_memory - base_memory) / process_memory; - - return max((int)process_limit, 1); -} - -bool OpenCLDevice::OpenCLProgram::compile_separate(const string &clbin) -{ - /* Construct arguments. */ - vector args; - args.push_back("--background"); - args.push_back("--factory-startup"); - args.push_back("--python-expr"); - - int device_platform_id = device->device_num; - string device_name = device->device_name; - string platform_name = device->platform_name; - string build_options = device->kernel_build_options(NULL) + kernel_build_options; - string kernel_file_escaped = kernel_file; - string clbin_escaped = clbin; - - escape_python_string(device_name); - escape_python_string(platform_name); - escape_python_string(build_options); - escape_python_string(kernel_file_escaped); - escape_python_string(clbin_escaped); - - args.push_back(string_printf( - "import _cycles; _cycles.opencl_compile(r'%d', r'%s', r'%s', r'%s', r'%s', r'%s')", - device_platform_id, - device_name.c_str(), - platform_name.c_str(), - build_options.c_str(), - kernel_file_escaped.c_str(), - clbin_escaped.c_str())); - - /* Limit number of concurrent processes compiling. */ - static thread_counting_semaphore semaphore(opencl_compile_process_limit()); - semaphore.acquire(); - - /* Compile. */ - const double starttime = time_dt(); - add_log(string("Cycles: compiling OpenCL program ") + program_name + "...", false); - add_log(string("Build flags: ") + kernel_build_options, true); - const bool success = system_call_self(args); - const double elapsed = time_dt() - starttime; - - semaphore.release(); - - if (!success || !path_exists(clbin)) { - return false; - } - - add_log( - string_printf("Kernel compilation of %s finished in %.2lfs.", program_name.c_str(), elapsed), - false); - - return load_binary(clbin); -} - -/* Compile opencl kernel. This method is called from the _cycles Python - * module compile kernels. Parameters must match function above. */ -bool device_opencl_compile_kernel(const vector ¶meters) -{ - int device_platform_id = std::stoi(parameters[0]); - const string &device_name = parameters[1]; - const string &platform_name = parameters[2]; - const string &build_options = parameters[3]; - const string &kernel_file = parameters[4]; - const string &binary_path = parameters[5]; - - if (clewInit() != CLEW_SUCCESS) { - return false; - } - - vector usable_devices; - OpenCLInfo::get_usable_devices(&usable_devices); - if (device_platform_id >= usable_devices.size()) { - return false; - } - - OpenCLPlatformDevice &platform_device = usable_devices[device_platform_id]; - if (platform_device.platform_name != platform_name || - platform_device.device_name != device_name) { - return false; - } - - cl_platform_id platform = platform_device.platform_id; - cl_device_id device = platform_device.device_id; - const cl_context_properties context_props[] = { - CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0, 0}; - - cl_int err; - cl_context context = clCreateContext(context_props, 1, &device, NULL, NULL, &err); - if (err != CL_SUCCESS) { - return false; - } - - string source = get_program_source(kernel_file); - size_t source_len = source.size(); - const char *source_str = source.c_str(); - cl_program program = clCreateProgramWithSource(context, 1, &source_str, &source_len, &err); - bool result = false; - - if (err == CL_SUCCESS) { - err = clBuildProgram(program, 0, NULL, build_options.c_str(), NULL, NULL); - - if (err == CL_SUCCESS) { - size_t size = 0; - clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &size, NULL); - if (size > 0) { - vector binary(size); - uint8_t *bytes = &binary[0]; - clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(uint8_t *), &bytes, NULL); - result = path_write_binary(binary_path, binary); - } - } - clReleaseProgram(program); - } - - clReleaseContext(context); - - return result; -} - -bool OpenCLDevice::OpenCLProgram::load_binary(const string &clbin, const string *debug_src) -{ - /* read binary into memory */ - vector binary; - - if (!path_read_binary(clbin, binary)) { - add_error(string_printf("OpenCL failed to read cached binary %s.", clbin.c_str())); - return false; - } - - /* create program */ - cl_int status, ciErr; - size_t size = binary.size(); - const uint8_t *bytes = &binary[0]; - - program = clCreateProgramWithBinary( - device->cxContext, 1, &device->cdDevice, &size, &bytes, &status, &ciErr); - - if (status != CL_SUCCESS || ciErr != CL_SUCCESS) { - add_error(string("OpenCL failed create program from cached binary ") + clbin + ": " + - clewErrorString(status) + " " + clewErrorString(ciErr)); - return false; - } - - if (!build_kernel(debug_src)) - return false; - - return true; -} - -bool OpenCLDevice::OpenCLProgram::save_binary(const string &clbin) -{ - size_t size = 0; - clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &size, NULL); - - if (!size) - return false; - - vector binary(size); - uint8_t *bytes = &binary[0]; - - clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(uint8_t *), &bytes, NULL); - - return path_write_binary(clbin, binary); -} - -bool OpenCLDevice::OpenCLProgram::load() -{ - loaded = false; - string device_md5 = device->device_md5_hash(kernel_build_options); - - /* Try to use cached kernel. */ - thread_scoped_lock cache_locker; - ustring cache_key(program_name + device_md5); - program = device->load_cached_kernel(cache_key, cache_locker); - if (!program) { - add_log(string("OpenCL program ") + program_name + " not found in cache.", true); - - /* need to create source to get md5 */ - string source = get_program_source(kernel_file); - - string basename = "cycles_kernel_" + program_name + "_" + device_md5 + "_" + - util_md5_string(source); - basename = path_cache_get(path_join("kernels", basename)); - string clbin = basename + ".clbin"; - - /* If binary kernel exists already, try use it. */ - if (path_exists(clbin) && load_binary(clbin)) { - /* Kernel loaded from binary, nothing to do. */ - add_log(string("Loaded program from ") + clbin + ".", true); - - /* Cache the program. */ - device->store_cached_kernel(program, cache_key, cache_locker); - } - else { - add_log(string("OpenCL program ") + program_name + " not found on disk.", true); - cache_locker.unlock(); - } - } - - if (program) { - create_kernels(); - loaded = true; - needs_compiling = false; - } - - return loaded; -} - -void OpenCLDevice::OpenCLProgram::compile() -{ - assert(device); - - string device_md5 = device->device_md5_hash(kernel_build_options); - - /* Try to use cached kernel. */ - thread_scoped_lock cache_locker; - ustring cache_key(program_name + device_md5); - program = device->load_cached_kernel(cache_key, cache_locker); - - if (!program) { - - add_log(string("OpenCL program ") + program_name + " not found in cache.", true); - - /* need to create source to get md5 */ - string source = get_program_source(kernel_file); - - string basename = "cycles_kernel_" + program_name + "_" + device_md5 + "_" + - util_md5_string(source); - basename = path_cache_get(path_join("kernels", basename)); - string clbin = basename + ".clbin"; - - /* path to preprocessed source for debugging */ - string clsrc, *debug_src = NULL; - - if (OpenCLInfo::use_debug()) { - clsrc = basename + ".cl"; - debug_src = &clsrc; - } - - if (DebugFlags().running_inside_blender && compile_separate(clbin)) { - add_log(string("Built and loaded program from ") + clbin + ".", true); - loaded = true; - } - else { - if (DebugFlags().running_inside_blender) { - add_log(string("Separate-process building of ") + clbin + - " failed, will fall back to regular building.", - true); - } - - /* If does not exist or loading binary failed, compile kernel. */ - if (!compile_kernel(debug_src)) { - needs_compiling = false; - return; - } - - /* Save binary for reuse. */ - if (!save_binary(clbin)) { - add_log(string("Saving compiled OpenCL kernel to ") + clbin + " failed!", true); - } - } - - /* Cache the program. */ - device->store_cached_kernel(program, cache_key, cache_locker); - } - - create_kernels(); - needs_compiling = false; - loaded = true; -} - -void OpenCLDevice::OpenCLProgram::create_kernels() -{ - for (map::iterator kernel = kernels.begin(); kernel != kernels.end(); - ++kernel) { - assert(kernel->second == NULL); - cl_int ciErr; - string name = "kernel_ocl_" + kernel->first.string(); - kernel->second = clCreateKernel(program, name.c_str(), &ciErr); - if (device->opencl_error(ciErr)) { - add_error(string("Error getting kernel ") + name + " from program " + program_name + ": " + - clewErrorString(ciErr)); - return; - } - } -} - -bool OpenCLDevice::OpenCLProgram::wait_for_availability() -{ - add_log(string("Waiting for availability of ") + program_name + ".", true); - while (needs_compiling) { - time_sleep(0.1); - } - return loaded; -} - -void OpenCLDevice::OpenCLProgram::report_error() -{ - /* If loaded is true, there was no error. */ - if (loaded) - return; - /* if use_stdout is true, the error was already reported. */ - if (use_stdout) - return; - - cerr << error_msg << endl; - if (!compile_output.empty()) { - cerr << "OpenCL kernel build output for " << program_name << ":" << endl; - cerr << compile_output << endl; - } -} - -cl_kernel OpenCLDevice::OpenCLProgram::operator()() -{ - assert(kernels.size() == 1); - return kernels.begin()->second; -} - -cl_kernel OpenCLDevice::OpenCLProgram::operator()(ustring name) -{ - assert(kernels.count(name)); - return kernels[name]; -} - -cl_device_type OpenCLInfo::device_type() -{ - switch (DebugFlags().opencl.device_type) { - case DebugFlags::OpenCL::DEVICE_NONE: - return 0; - case DebugFlags::OpenCL::DEVICE_ALL: - return CL_DEVICE_TYPE_ALL; - case DebugFlags::OpenCL::DEVICE_DEFAULT: - return CL_DEVICE_TYPE_DEFAULT; - case DebugFlags::OpenCL::DEVICE_CPU: - return CL_DEVICE_TYPE_CPU; - case DebugFlags::OpenCL::DEVICE_GPU: - return CL_DEVICE_TYPE_GPU; - case DebugFlags::OpenCL::DEVICE_ACCELERATOR: - return CL_DEVICE_TYPE_ACCELERATOR; - default: - return CL_DEVICE_TYPE_ALL; - } -} - -bool OpenCLInfo::use_debug() -{ - return DebugFlags().opencl.debug; -} - -bool OpenCLInfo::device_supported(const string &platform_name, const cl_device_id device_id) -{ - cl_device_type device_type; - if (!get_device_type(device_id, &device_type)) { - return false; - } - string device_name; - if (!get_device_name(device_id, &device_name)) { - return false; - } - - int driver_major = 0; - int driver_minor = 0; - if (!get_driver_version(device_id, &driver_major, &driver_minor)) { - return false; - } - VLOG(3) << "OpenCL driver version " << driver_major << "." << driver_minor; - - if (getenv("CYCLES_OPENCL_TEST")) { - return true; - } - - /* Allow Intel GPUs on Intel OpenCL platform. */ - if (platform_name.find("Intel") != string::npos) { - if (device_type != CL_DEVICE_TYPE_GPU) { - /* OpenCL on Intel CPU is not an officially supported configuration. - * Use hybrid CPU+GPU rendering to utilize both GPU and CPU. */ - return false; - } - -# ifdef __APPLE__ - /* Apple uses own framework, which can also put Iris onto AMD frame-work. - * This isn't supported configuration. */ - return false; -# else - if (device_name.find("Iris") != string::npos || device_name.find("Xe") != string::npos) { - return true; - } -# endif - } - - if (platform_name == "AMD Accelerated Parallel Processing" && - device_type == CL_DEVICE_TYPE_GPU) { - if (driver_major < 2236) { - VLOG(1) << "AMD driver version " << driver_major << "." << driver_minor << " not supported."; - return false; - } - const char *blacklist[] = {/* GCN 1 */ - "Tahiti", - "Pitcairn", - "Capeverde", - "Oland", - "Hainan", - NULL}; - for (int i = 0; blacklist[i] != NULL; i++) { - if (device_name == blacklist[i]) { - VLOG(1) << "AMD device " << device_name << " not supported"; - return false; - } - } - return true; - } - if (platform_name == "Apple" && device_type == CL_DEVICE_TYPE_GPU) { - return false; - } - return false; -} - -bool OpenCLInfo::platform_version_check(cl_platform_id platform, string *error) -{ - const int req_major = 1, req_minor = 1; - int major, minor; - char version[256]; - clGetPlatformInfo(platform, CL_PLATFORM_VERSION, sizeof(version), &version, NULL); - if (sscanf(version, "OpenCL %d.%d", &major, &minor) < 2) { - if (error != NULL) { - *error = string_printf("OpenCL: failed to parse platform version string (%s).", version); - } - return false; - } - if (!((major == req_major && minor >= req_minor) || (major > req_major))) { - if (error != NULL) { - *error = string_printf( - "OpenCL: platform version 1.1 or later required, found %d.%d", major, minor); - } - return false; - } - if (error != NULL) { - *error = ""; - } - return true; -} - -bool OpenCLInfo::get_device_version(cl_device_id device, int *r_major, int *r_minor, string *error) -{ - char version[256]; - clGetDeviceInfo(device, CL_DEVICE_OPENCL_C_VERSION, sizeof(version), &version, NULL); - if (sscanf(version, "OpenCL C %d.%d", r_major, r_minor) < 2) { - if (error != NULL) { - *error = string_printf("OpenCL: failed to parse OpenCL C version string (%s).", version); - } - return false; - } - if (error != NULL) { - *error = ""; - } - return true; -} - -bool OpenCLInfo::device_version_check(cl_device_id device, string *error) -{ - const int req_major = 1, req_minor = 1; - int major, minor; - if (!get_device_version(device, &major, &minor, error)) { - return false; - } - - if (!((major == req_major && minor >= req_minor) || (major > req_major))) { - if (error != NULL) { - *error = string_printf("OpenCL: C version 1.1 or later required, found %d.%d", major, minor); - } - return false; - } - if (error != NULL) { - *error = ""; - } - return true; -} - -string OpenCLInfo::get_hardware_id(const string &platform_name, cl_device_id device_id) -{ - if (platform_name == "AMD Accelerated Parallel Processing" || platform_name == "Apple") { - /* Use cl_amd_device_topology extension. */ - cl_char topology[24]; - if (clGetDeviceInfo(device_id, 0x4037, sizeof(topology), topology, NULL) == CL_SUCCESS && - topology[0] == 1) { - return string_printf("%02x:%02x.%01x", - (unsigned int)topology[21], - (unsigned int)topology[22], - (unsigned int)topology[23]); - } - } - else if (platform_name == "NVIDIA CUDA") { - /* Use two undocumented options of the cl_nv_device_attribute_query extension. */ - cl_int bus_id, slot_id; - if (clGetDeviceInfo(device_id, 0x4008, sizeof(cl_int), &bus_id, NULL) == CL_SUCCESS && - clGetDeviceInfo(device_id, 0x4009, sizeof(cl_int), &slot_id, NULL) == CL_SUCCESS) { - return string_printf("%02x:%02x.%01x", - (unsigned int)(bus_id), - (unsigned int)(slot_id >> 3), - (unsigned int)(slot_id & 0x7)); - } - } - /* No general way to get a hardware ID from OpenCL => give up. */ - return ""; -} - -void OpenCLInfo::get_usable_devices(vector *usable_devices) -{ - const cl_device_type device_type = OpenCLInfo::device_type(); - static bool first_time = true; -# define FIRST_VLOG(severity) \ - if (first_time) \ - VLOG(severity) - - usable_devices->clear(); - - if (device_type == 0) { - FIRST_VLOG(2) << "OpenCL devices are forced to be disabled."; - first_time = false; - return; - } - - cl_int error; - vector device_ids; - vector platform_ids; - - /* Get platforms. */ - if (!get_platforms(&platform_ids, &error)) { - FIRST_VLOG(2) << "Error fetching platforms:" << string(clewErrorString(error)); - first_time = false; - return; - } - if (platform_ids.size() == 0) { - FIRST_VLOG(2) << "No OpenCL platforms were found."; - first_time = false; - return; - } - /* Devices are numbered consecutively across platforms. */ - for (int platform = 0; platform < platform_ids.size(); platform++) { - cl_platform_id platform_id = platform_ids[platform]; - string platform_name; - if (!get_platform_name(platform_id, &platform_name)) { - FIRST_VLOG(2) << "Failed to get platform name, ignoring."; - continue; - } - FIRST_VLOG(2) << "Enumerating devices for platform " << platform_name << "."; - if (!platform_version_check(platform_id)) { - FIRST_VLOG(2) << "Ignoring platform " << platform_name - << " due to too old compiler version."; - continue; - } - if (!get_platform_devices(platform_id, device_type, &device_ids, &error)) { - FIRST_VLOG(2) << "Ignoring platform " << platform_name - << ", failed to fetch of devices: " << string(clewErrorString(error)); - continue; - } - if (device_ids.size() == 0) { - FIRST_VLOG(2) << "Ignoring platform " << platform_name << ", it has no devices."; - continue; - } - for (int num = 0; num < device_ids.size(); num++) { - const cl_device_id device_id = device_ids[num]; - string device_name; - if (!get_device_name(device_id, &device_name, &error)) { - FIRST_VLOG(2) << "Failed to fetch device name: " << string(clewErrorString(error)) - << ", ignoring."; - continue; - } - if (!device_version_check(device_id)) { - FIRST_VLOG(2) << "Ignoring device " << device_name << " due to old compiler version."; - continue; - } - if (device_supported(platform_name, device_id)) { - cl_device_type device_type; - if (!get_device_type(device_id, &device_type, &error)) { - FIRST_VLOG(2) << "Ignoring device " << device_name - << ", failed to fetch device type:" << string(clewErrorString(error)); - continue; - } - string readable_device_name = get_readable_device_name(device_id); - if (readable_device_name != device_name) { - FIRST_VLOG(2) << "Using more readable device name: " << readable_device_name; - } - FIRST_VLOG(2) << "Adding new device " << readable_device_name << "."; - string hardware_id = get_hardware_id(platform_name, device_id); - string device_extensions = get_device_extensions(device_id); - usable_devices->push_back(OpenCLPlatformDevice(platform_id, - platform_name, - device_id, - device_type, - readable_device_name, - hardware_id, - device_extensions)); - } - else { - FIRST_VLOG(2) << "Ignoring device " << device_name << ", not officially supported yet."; - } - } - } - first_time = false; -} - -bool OpenCLInfo::get_platforms(vector *platform_ids, cl_int *error) -{ - /* Reset from possible previous state. */ - platform_ids->resize(0); - cl_uint num_platforms; - if (!get_num_platforms(&num_platforms, error)) { - return false; - } - /* Get actual platforms. */ - cl_int err; - platform_ids->resize(num_platforms); - if ((err = clGetPlatformIDs(num_platforms, &platform_ids->at(0), NULL)) != CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - return true; -} - -vector OpenCLInfo::get_platforms() -{ - vector platform_ids; - get_platforms(&platform_ids); - return platform_ids; -} - -bool OpenCLInfo::get_num_platforms(cl_uint *num_platforms, cl_int *error) -{ - cl_int err; - if ((err = clGetPlatformIDs(0, NULL, num_platforms)) != CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *num_platforms = 0; - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - return true; -} - -cl_uint OpenCLInfo::get_num_platforms() -{ - cl_uint num_platforms; - if (!get_num_platforms(&num_platforms)) { - return 0; - } - return num_platforms; -} - -bool OpenCLInfo::get_platform_name(cl_platform_id platform_id, string *platform_name) -{ - char buffer[256]; - if (clGetPlatformInfo(platform_id, CL_PLATFORM_NAME, sizeof(buffer), &buffer, NULL) != - CL_SUCCESS) { - *platform_name = ""; - return false; - } - *platform_name = buffer; - return true; -} - -string OpenCLInfo::get_platform_name(cl_platform_id platform_id) -{ - string platform_name; - if (!get_platform_name(platform_id, &platform_name)) { - return ""; - } - return platform_name; -} - -bool OpenCLInfo::get_num_platform_devices(cl_platform_id platform_id, - cl_device_type device_type, - cl_uint *num_devices, - cl_int *error) -{ - cl_int err; - if ((err = clGetDeviceIDs(platform_id, device_type, 0, NULL, num_devices)) != CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *num_devices = 0; - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - return true; -} - -cl_uint OpenCLInfo::get_num_platform_devices(cl_platform_id platform_id, - cl_device_type device_type) -{ - cl_uint num_devices; - if (!get_num_platform_devices(platform_id, device_type, &num_devices)) { - return 0; - } - return num_devices; -} - -bool OpenCLInfo::get_platform_devices(cl_platform_id platform_id, - cl_device_type device_type, - vector *device_ids, - cl_int *error) -{ - /* Reset from possible previous state. */ - device_ids->resize(0); - /* Get number of devices to pre-allocate memory. */ - cl_uint num_devices; - if (!get_num_platform_devices(platform_id, device_type, &num_devices, error)) { - return false; - } - /* Get actual device list. */ - device_ids->resize(num_devices); - cl_int err; - if ((err = clGetDeviceIDs(platform_id, device_type, num_devices, &device_ids->at(0), NULL)) != - CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - return true; -} - -vector OpenCLInfo::get_platform_devices(cl_platform_id platform_id, - cl_device_type device_type) -{ - vector devices; - get_platform_devices(platform_id, device_type, &devices); - return devices; -} - -bool OpenCLInfo::get_device_name(cl_device_id device_id, string *device_name, cl_int *error) -{ - char buffer[1024]; - cl_int err; - if ((err = clGetDeviceInfo(device_id, CL_DEVICE_NAME, sizeof(buffer), &buffer, NULL)) != - CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *device_name = ""; - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - *device_name = buffer; - return true; -} - -string OpenCLInfo::get_device_name(cl_device_id device_id) -{ - string device_name; - if (!get_device_name(device_id, &device_name)) { - return ""; - } - return device_name; -} - -bool OpenCLInfo::get_device_extensions(cl_device_id device_id, - string *device_extensions, - cl_int *error) -{ - size_t extension_length = 0; - cl_int err; - /* Determine the size of the extension string. */ - if ((err = clGetDeviceInfo(device_id, CL_DEVICE_EXTENSIONS, 0, 0, &extension_length)) != - CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *device_extensions = ""; - return false; - } - vector buffer(extension_length); - if ((err = clGetDeviceInfo( - device_id, CL_DEVICE_EXTENSIONS, extension_length, buffer.data(), NULL)) != - CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *device_extensions = ""; - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - *device_extensions = string(buffer.data()); - return true; -} - -string OpenCLInfo::get_device_extensions(cl_device_id device_id) -{ - string device_extensions; - if (!get_device_extensions(device_id, &device_extensions)) { - return ""; - } - return device_extensions; -} - -bool OpenCLInfo::get_device_type(cl_device_id device_id, - cl_device_type *device_type, - cl_int *error) -{ - cl_int err; - if ((err = clGetDeviceInfo( - device_id, CL_DEVICE_TYPE, sizeof(cl_device_type), device_type, NULL)) != CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *device_type = 0; - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - return true; -} - -cl_device_type OpenCLInfo::get_device_type(cl_device_id device_id) -{ - cl_device_type device_type; - if (!get_device_type(device_id, &device_type)) { - return 0; - } - return device_type; -} - -string OpenCLInfo::get_readable_device_name(cl_device_id device_id) -{ - string name = ""; - char board_name[1024]; - size_t length = 0; - if (clGetDeviceInfo( - device_id, CL_DEVICE_BOARD_NAME_AMD, sizeof(board_name), &board_name, &length) == - CL_SUCCESS) { - if (length != 0 && board_name[0] != '\0') { - name = board_name; - } - } - - /* Fallback to standard device name API. */ - if (name.empty()) { - name = get_device_name(device_id); - } - - /* Special exception for AMD Vega, need to be able to tell - * Vega 56 from 64 apart. - */ - if (name == "Radeon RX Vega") { - cl_int max_compute_units = 0; - if (clGetDeviceInfo(device_id, - CL_DEVICE_MAX_COMPUTE_UNITS, - sizeof(max_compute_units), - &max_compute_units, - NULL) == CL_SUCCESS) { - name += " " + to_string(max_compute_units); - } - } - - /* Distinguish from our native CPU device. */ - if (get_device_type(device_id) & CL_DEVICE_TYPE_CPU) { - name += " (OpenCL)"; - } - - return name; -} - -bool OpenCLInfo::get_driver_version(cl_device_id device_id, int *major, int *minor, cl_int *error) -{ - char buffer[1024]; - cl_int err; - if ((err = clGetDeviceInfo(device_id, CL_DRIVER_VERSION, sizeof(buffer), &buffer, NULL)) != - CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - if (sscanf(buffer, "%d.%d", major, minor) < 2) { - VLOG(1) << string_printf("OpenCL: failed to parse driver version string (%s).", buffer); - return false; - } - return true; -} - -int OpenCLInfo::mem_sub_ptr_alignment(cl_device_id device_id) -{ - int base_align_bits; - if (clGetDeviceInfo( - device_id, CL_DEVICE_MEM_BASE_ADDR_ALIGN, sizeof(int), &base_align_bits, NULL) == - CL_SUCCESS) { - return base_align_bits / 8; - } - return 1; -} - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/optix/device.cpp b/intern/cycles/device/optix/device.cpp new file mode 100644 index 00000000000..13f23bd229a --- /dev/null +++ b/intern/cycles/device/optix/device.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2019, NVIDIA Corporation. + * Copyright 2019, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/optix/device.h" + +#include "device/cuda/device.h" +#include "device/optix/device_impl.h" +#include "util/util_logging.h" + +#ifdef WITH_OPTIX +# include +#endif + +CCL_NAMESPACE_BEGIN + +bool device_optix_init() +{ +#ifdef WITH_OPTIX + if (g_optixFunctionTable.optixDeviceContextCreate != NULL) { + /* Already initialized function table. */ + return true; + } + + /* Need to initialize CUDA as well. */ + if (!device_cuda_init()) { + return false; + } + + const OptixResult result = optixInit(); + + if (result == OPTIX_ERROR_UNSUPPORTED_ABI_VERSION) { + VLOG(1) << "OptiX initialization failed because the installed NVIDIA driver is too old. " + "Please update to the latest driver first!"; + return false; + } + else if (result != OPTIX_SUCCESS) { + VLOG(1) << "OptiX initialization failed with error code " << (unsigned int)result; + return false; + } + + /* Loaded OptiX successfully! */ + return true; +#else + return false; +#endif +} + +void device_optix_info(const vector &cuda_devices, vector &devices) +{ +#ifdef WITH_OPTIX + devices.reserve(cuda_devices.size()); + + /* Simply add all supported CUDA devices as OptiX devices again. */ + for (DeviceInfo info : cuda_devices) { + assert(info.type == DEVICE_CUDA); + + int major; + cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, info.num); + if (major < 5) { + /* Only Maxwell and up are supported by OptiX. */ + continue; + } + + info.type = DEVICE_OPTIX; + info.id += "_OptiX"; + info.denoisers |= DENOISER_OPTIX; + + devices.push_back(info); + } +#else + (void)cuda_devices; + (void)devices; +#endif +} + +Device *device_optix_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) +{ +#ifdef WITH_OPTIX + return new OptiXDevice(info, stats, profiler); +#else + (void)info; + (void)stats; + (void)profiler; + + LOG(FATAL) << "Request to create OptiX device without compiled-in support. Should never happen."; + + return nullptr; +#endif +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/optix/device.h b/intern/cycles/device/optix/device.h new file mode 100644 index 00000000000..29fa729c2e4 --- /dev/null +++ b/intern/cycles/device/optix/device.h @@ -0,0 +1,35 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/util_string.h" +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +bool device_optix_init(); + +Device *device_optix_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +void device_optix_info(const vector &cuda_devices, vector &devices); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp new file mode 100644 index 00000000000..cd16b8c9f01 --- /dev/null +++ b/intern/cycles/device/optix/device_impl.cpp @@ -0,0 +1,1573 @@ +/* + * Copyright 2019, NVIDIA Corporation. + * Copyright 2019, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_OPTIX + +# include "device/optix/device_impl.h" + +# include "bvh/bvh.h" +# include "bvh/bvh_optix.h" +# include "integrator/pass_accessor_gpu.h" +# include "render/buffers.h" +# include "render/hair.h" +# include "render/mesh.h" +# include "render/object.h" +# include "render/pass.h" +# include "render/scene.h" + +# include "util/util_debug.h" +# include "util/util_logging.h" +# include "util/util_md5.h" +# include "util/util_path.h" +# include "util/util_progress.h" +# include "util/util_time.h" + +# undef __KERNEL_CPU__ +# define __KERNEL_OPTIX__ +# include "kernel/device/optix/globals.h" + +CCL_NAMESPACE_BEGIN + +OptiXDevice::Denoiser::Denoiser(OptiXDevice *device) + : device(device), queue(device), state(device, "__denoiser_state") +{ +} + +OptiXDevice::Denoiser::~Denoiser() +{ + const CUDAContextScope scope(device); + if (optix_denoiser != nullptr) { + optixDenoiserDestroy(optix_denoiser); + } +} + +OptiXDevice::OptiXDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler) + : CUDADevice(info, stats, profiler), + sbt_data(this, "__sbt", MEM_READ_ONLY), + launch_params(this, "__params"), + denoiser_(this) +{ + /* Make the CUDA context current. */ + if (!cuContext) { + /* Do not initialize if CUDA context creation failed already. */ + return; + } + const CUDAContextScope scope(this); + + /* Create OptiX context for this device. */ + OptixDeviceContextOptions options = {}; +# ifdef WITH_CYCLES_LOGGING + options.logCallbackLevel = 4; /* Fatal = 1, Error = 2, Warning = 3, Print = 4. */ + options.logCallbackFunction = [](unsigned int level, const char *, const char *message, void *) { + switch (level) { + case 1: + LOG_IF(FATAL, VLOG_IS_ON(1)) << message; + break; + case 2: + LOG_IF(ERROR, VLOG_IS_ON(1)) << message; + break; + case 3: + LOG_IF(WARNING, VLOG_IS_ON(1)) << message; + break; + case 4: + LOG_IF(INFO, VLOG_IS_ON(1)) << message; + break; + } + }; +# endif + if (DebugFlags().optix.use_debug) { + options.validationMode = OPTIX_DEVICE_CONTEXT_VALIDATION_MODE_ALL; + } + optix_assert(optixDeviceContextCreate(cuContext, &options, &context)); +# ifdef WITH_CYCLES_LOGGING + optix_assert(optixDeviceContextSetLogCallback( + context, options.logCallbackFunction, options.logCallbackData, options.logCallbackLevel)); +# endif + + /* Fix weird compiler bug that assigns wrong size. */ + launch_params.data_elements = sizeof(KernelParamsOptiX); + + /* Allocate launch parameter buffer memory on device. */ + launch_params.alloc_to_device(1); +} + +OptiXDevice::~OptiXDevice() +{ + /* Make CUDA context current. */ + const CUDAContextScope scope(this); + + free_bvh_memory_delayed(); + + sbt_data.free(); + texture_info.free(); + launch_params.free(); + + /* Unload modules. */ + if (optix_module != NULL) { + optixModuleDestroy(optix_module); + } + for (unsigned int i = 0; i < 2; ++i) { + if (builtin_modules[i] != NULL) { + optixModuleDestroy(builtin_modules[i]); + } + } + for (unsigned int i = 0; i < NUM_PIPELINES; ++i) { + if (pipelines[i] != NULL) { + optixPipelineDestroy(pipelines[i]); + } + } + + optixDeviceContextDestroy(context); +} + +unique_ptr OptiXDevice::gpu_queue_create() +{ + return make_unique(this); +} + +BVHLayoutMask OptiXDevice::get_bvh_layout_mask() const +{ + /* OptiX has its own internal acceleration structure format. */ + return BVH_LAYOUT_OPTIX; +} + +string OptiXDevice::compile_kernel_get_common_cflags(const uint kernel_features) +{ + string common_cflags = CUDADevice::compile_kernel_get_common_cflags(kernel_features); + + /* Add OptiX SDK include directory to include paths. */ + const char *optix_sdk_path = getenv("OPTIX_ROOT_DIR"); + if (optix_sdk_path) { + common_cflags += string_printf(" -I\"%s/include\"", optix_sdk_path); + } + + /* Specialization for shader raytracing. */ + if (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) { + common_cflags += " --keep-device-functions"; + } + + return common_cflags; +} + +bool OptiXDevice::load_kernels(const uint kernel_features) +{ + if (have_error()) { + /* Abort early if context creation failed already. */ + return false; + } + + /* Load CUDA modules because we need some of the utility kernels. */ + if (!CUDADevice::load_kernels(kernel_features)) { + return false; + } + + /* Skip creating OptiX module if only doing denoising. */ + if (!(kernel_features & (KERNEL_FEATURE_PATH_TRACING | KERNEL_FEATURE_BAKING))) { + return true; + } + + const CUDAContextScope scope(this); + + /* Unload existing OptiX module and pipelines first. */ + if (optix_module != NULL) { + optixModuleDestroy(optix_module); + optix_module = NULL; + } + for (unsigned int i = 0; i < 2; ++i) { + if (builtin_modules[i] != NULL) { + optixModuleDestroy(builtin_modules[i]); + builtin_modules[i] = NULL; + } + } + for (unsigned int i = 0; i < NUM_PIPELINES; ++i) { + if (pipelines[i] != NULL) { + optixPipelineDestroy(pipelines[i]); + pipelines[i] = NULL; + } + } + + OptixModuleCompileOptions module_options = {}; + module_options.maxRegisterCount = 0; /* Do not set an explicit register limit. */ + + if (DebugFlags().optix.use_debug) { + module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0; + module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL; + } + else { + module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_3; + module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; + } + + module_options.boundValues = nullptr; + module_options.numBoundValues = 0; + + OptixPipelineCompileOptions pipeline_options = {}; + /* Default to no motion blur and two-level graph, since it is the fastest option. */ + pipeline_options.usesMotionBlur = false; + pipeline_options.traversableGraphFlags = + OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING; + pipeline_options.numPayloadValues = 6; + pipeline_options.numAttributeValues = 2; /* u, v */ + pipeline_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE; + pipeline_options.pipelineLaunchParamsVariableName = "__params"; /* See globals.h */ + + pipeline_options.usesPrimitiveTypeFlags = OPTIX_PRIMITIVE_TYPE_FLAGS_TRIANGLE; + if (kernel_features & KERNEL_FEATURE_HAIR) { + if (kernel_features & KERNEL_FEATURE_HAIR_THICK) { + pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_ROUND_CUBIC_BSPLINE; + } + else + pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM; + } + + /* Keep track of whether motion blur is enabled, so to enable/disable motion in BVH builds + * This is necessary since objects may be reported to have motion if the Vector pass is + * active, but may still need to be rendered without motion blur if that isn't active as well. */ + motion_blur = (kernel_features & KERNEL_FEATURE_OBJECT_MOTION) != 0; + + if (motion_blur) { + pipeline_options.usesMotionBlur = true; + /* Motion blur can insert motion transforms into the traversal graph. + * It is no longer a two-level graph then, so need to set flags to allow any configuration. */ + pipeline_options.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_ANY; + } + + { /* Load and compile PTX module with OptiX kernels. */ + string ptx_data, ptx_filename = path_get((kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) ? + "lib/kernel_optix_shader_raytrace.ptx" : + "lib/kernel_optix.ptx"); + if (use_adaptive_compilation() || path_file_size(ptx_filename) == -1) { + if (!getenv("OPTIX_ROOT_DIR")) { + set_error( + "Missing OPTIX_ROOT_DIR environment variable (which must be set with the path to " + "the Optix SDK to be able to compile Optix kernels on demand)."); + return false; + } + ptx_filename = compile_kernel( + kernel_features, + (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) ? "kernel_shader_raytrace" : "kernel", + "optix", + true); + } + if (ptx_filename.empty() || !path_read_text(ptx_filename, ptx_data)) { + set_error(string_printf("Failed to load OptiX kernel from '%s'", ptx_filename.c_str())); + return false; + } + + const OptixResult result = optixModuleCreateFromPTX(context, + &module_options, + &pipeline_options, + ptx_data.data(), + ptx_data.size(), + nullptr, + 0, + &optix_module); + if (result != OPTIX_SUCCESS) { + set_error(string_printf("Failed to load OptiX kernel from '%s' (%s)", + ptx_filename.c_str(), + optixGetErrorName(result))); + return false; + } + } + + /* Create program groups. */ + OptixProgramGroup groups[NUM_PROGRAM_GROUPS] = {}; + OptixProgramGroupDesc group_descs[NUM_PROGRAM_GROUPS] = {}; + OptixProgramGroupOptions group_options = {}; /* There are no options currently. */ + group_descs[PG_RGEN_INTERSECT_CLOSEST].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; + group_descs[PG_RGEN_INTERSECT_CLOSEST].raygen.module = optix_module; + group_descs[PG_RGEN_INTERSECT_CLOSEST].raygen.entryFunctionName = + "__raygen__kernel_optix_integrator_intersect_closest"; + group_descs[PG_RGEN_INTERSECT_SHADOW].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; + group_descs[PG_RGEN_INTERSECT_SHADOW].raygen.module = optix_module; + group_descs[PG_RGEN_INTERSECT_SHADOW].raygen.entryFunctionName = + "__raygen__kernel_optix_integrator_intersect_shadow"; + group_descs[PG_RGEN_INTERSECT_SUBSURFACE].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; + group_descs[PG_RGEN_INTERSECT_SUBSURFACE].raygen.module = optix_module; + group_descs[PG_RGEN_INTERSECT_SUBSURFACE].raygen.entryFunctionName = + "__raygen__kernel_optix_integrator_intersect_subsurface"; + group_descs[PG_RGEN_INTERSECT_VOLUME_STACK].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; + group_descs[PG_RGEN_INTERSECT_VOLUME_STACK].raygen.module = optix_module; + group_descs[PG_RGEN_INTERSECT_VOLUME_STACK].raygen.entryFunctionName = + "__raygen__kernel_optix_integrator_intersect_volume_stack"; + group_descs[PG_MISS].kind = OPTIX_PROGRAM_GROUP_KIND_MISS; + group_descs[PG_MISS].miss.module = optix_module; + group_descs[PG_MISS].miss.entryFunctionName = "__miss__kernel_optix_miss"; + group_descs[PG_HITD].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; + group_descs[PG_HITD].hitgroup.moduleCH = optix_module; + group_descs[PG_HITD].hitgroup.entryFunctionNameCH = "__closesthit__kernel_optix_hit"; + group_descs[PG_HITD].hitgroup.moduleAH = optix_module; + group_descs[PG_HITD].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_visibility_test"; + group_descs[PG_HITS].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; + group_descs[PG_HITS].hitgroup.moduleAH = optix_module; + group_descs[PG_HITS].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_shadow_all_hit"; + + if (kernel_features & KERNEL_FEATURE_HAIR) { + if (kernel_features & KERNEL_FEATURE_HAIR_THICK) { + /* Built-in thick curve intersection. */ + OptixBuiltinISOptions builtin_options = {}; + builtin_options.builtinISModuleType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE; + builtin_options.usesMotionBlur = false; + + optix_assert(optixBuiltinISModuleGet( + context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[0])); + + group_descs[PG_HITD].hitgroup.moduleIS = builtin_modules[0]; + group_descs[PG_HITD].hitgroup.entryFunctionNameIS = nullptr; + group_descs[PG_HITS].hitgroup.moduleIS = builtin_modules[0]; + group_descs[PG_HITS].hitgroup.entryFunctionNameIS = nullptr; + + if (motion_blur) { + builtin_options.usesMotionBlur = true; + + optix_assert(optixBuiltinISModuleGet( + context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[1])); + + group_descs[PG_HITD_MOTION] = group_descs[PG_HITD]; + group_descs[PG_HITD_MOTION].hitgroup.moduleIS = builtin_modules[1]; + group_descs[PG_HITS_MOTION] = group_descs[PG_HITS]; + group_descs[PG_HITS_MOTION].hitgroup.moduleIS = builtin_modules[1]; + } + } + else { + /* Custom ribbon intersection. */ + group_descs[PG_HITD].hitgroup.moduleIS = optix_module; + group_descs[PG_HITS].hitgroup.moduleIS = optix_module; + group_descs[PG_HITD].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon"; + group_descs[PG_HITS].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon"; + } + } + + if (kernel_features & (KERNEL_FEATURE_SUBSURFACE | KERNEL_FEATURE_NODE_RAYTRACE)) { + /* Add hit group for local intersections. */ + group_descs[PG_HITL].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; + group_descs[PG_HITL].hitgroup.moduleAH = optix_module; + group_descs[PG_HITL].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_local_hit"; + } + + /* Shader raytracing replaces some functions with direct callables. */ + if (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) { + group_descs[PG_RGEN_SHADE_SURFACE_RAYTRACE].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; + group_descs[PG_RGEN_SHADE_SURFACE_RAYTRACE].raygen.module = optix_module; + group_descs[PG_RGEN_SHADE_SURFACE_RAYTRACE].raygen.entryFunctionName = + "__raygen__kernel_optix_integrator_shade_surface_raytrace"; + group_descs[PG_CALL_SVM_AO].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; + group_descs[PG_CALL_SVM_AO].callables.moduleDC = optix_module; + group_descs[PG_CALL_SVM_AO].callables.entryFunctionNameDC = "__direct_callable__svm_node_ao"; + group_descs[PG_CALL_SVM_BEVEL].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; + group_descs[PG_CALL_SVM_BEVEL].callables.moduleDC = optix_module; + group_descs[PG_CALL_SVM_BEVEL].callables.entryFunctionNameDC = + "__direct_callable__svm_node_bevel"; + group_descs[PG_CALL_AO_PASS].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; + group_descs[PG_CALL_AO_PASS].callables.moduleDC = optix_module; + group_descs[PG_CALL_AO_PASS].callables.entryFunctionNameDC = "__direct_callable__ao_pass"; + } + + optix_assert(optixProgramGroupCreate( + context, group_descs, NUM_PROGRAM_GROUPS, &group_options, nullptr, 0, groups)); + + /* Get program stack sizes. */ + OptixStackSizes stack_size[NUM_PROGRAM_GROUPS] = {}; + /* Set up SBT, which in this case is used only to select between different programs. */ + sbt_data.alloc(NUM_PROGRAM_GROUPS); + memset(sbt_data.host_pointer, 0, sizeof(SbtRecord) * NUM_PROGRAM_GROUPS); + for (unsigned int i = 0; i < NUM_PROGRAM_GROUPS; ++i) { + optix_assert(optixSbtRecordPackHeader(groups[i], &sbt_data[i])); + optix_assert(optixProgramGroupGetStackSize(groups[i], &stack_size[i])); + } + sbt_data.copy_to_device(); /* Upload SBT to device. */ + + /* Calculate maximum trace continuation stack size. */ + unsigned int trace_css = stack_size[PG_HITD].cssCH; + /* This is based on the maximum of closest-hit and any-hit/intersection programs. */ + trace_css = std::max(trace_css, stack_size[PG_HITD].cssIS + stack_size[PG_HITD].cssAH); + trace_css = std::max(trace_css, stack_size[PG_HITS].cssIS + stack_size[PG_HITS].cssAH); + trace_css = std::max(trace_css, stack_size[PG_HITL].cssIS + stack_size[PG_HITL].cssAH); + trace_css = std::max(trace_css, + stack_size[PG_HITD_MOTION].cssIS + stack_size[PG_HITD_MOTION].cssAH); + trace_css = std::max(trace_css, + stack_size[PG_HITS_MOTION].cssIS + stack_size[PG_HITS_MOTION].cssAH); + + OptixPipelineLinkOptions link_options = {}; + link_options.maxTraceDepth = 1; + + if (DebugFlags().optix.use_debug) { + link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL; + } + else { + link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; + } + + if (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) { + /* Create shader raytracing pipeline. */ + vector pipeline_groups; + pipeline_groups.reserve(NUM_PROGRAM_GROUPS); + pipeline_groups.push_back(groups[PG_RGEN_SHADE_SURFACE_RAYTRACE]); + pipeline_groups.push_back(groups[PG_MISS]); + pipeline_groups.push_back(groups[PG_HITD]); + pipeline_groups.push_back(groups[PG_HITS]); + pipeline_groups.push_back(groups[PG_HITL]); + if (motion_blur) { + pipeline_groups.push_back(groups[PG_HITD_MOTION]); + pipeline_groups.push_back(groups[PG_HITS_MOTION]); + } + pipeline_groups.push_back(groups[PG_CALL_SVM_AO]); + pipeline_groups.push_back(groups[PG_CALL_SVM_BEVEL]); + + optix_assert(optixPipelineCreate(context, + &pipeline_options, + &link_options, + pipeline_groups.data(), + pipeline_groups.size(), + nullptr, + 0, + &pipelines[PIP_SHADE_RAYTRACE])); + + /* Combine ray generation and trace continuation stack size. */ + const unsigned int css = stack_size[PG_RGEN_SHADE_SURFACE_RAYTRACE].cssRG + + link_options.maxTraceDepth * trace_css; + const unsigned int dss = std::max(stack_size[PG_CALL_SVM_AO].dssDC, + stack_size[PG_CALL_SVM_BEVEL].dssDC); + + /* Set stack size depending on pipeline options. */ + optix_assert(optixPipelineSetStackSize( + pipelines[PIP_SHADE_RAYTRACE], 0, dss, css, motion_blur ? 3 : 2)); + } + + { /* Create intersection-only pipeline. */ + vector pipeline_groups; + pipeline_groups.reserve(NUM_PROGRAM_GROUPS); + pipeline_groups.push_back(groups[PG_RGEN_INTERSECT_CLOSEST]); + pipeline_groups.push_back(groups[PG_RGEN_INTERSECT_SHADOW]); + pipeline_groups.push_back(groups[PG_RGEN_INTERSECT_SUBSURFACE]); + pipeline_groups.push_back(groups[PG_RGEN_INTERSECT_VOLUME_STACK]); + pipeline_groups.push_back(groups[PG_MISS]); + pipeline_groups.push_back(groups[PG_HITD]); + pipeline_groups.push_back(groups[PG_HITS]); + pipeline_groups.push_back(groups[PG_HITL]); + if (motion_blur) { + pipeline_groups.push_back(groups[PG_HITD_MOTION]); + pipeline_groups.push_back(groups[PG_HITS_MOTION]); + } + + optix_assert(optixPipelineCreate(context, + &pipeline_options, + &link_options, + pipeline_groups.data(), + pipeline_groups.size(), + nullptr, + 0, + &pipelines[PIP_INTERSECT])); + + /* Calculate continuation stack size based on the maximum of all ray generation stack sizes. */ + const unsigned int css = + std::max(stack_size[PG_RGEN_INTERSECT_CLOSEST].cssRG, + std::max(stack_size[PG_RGEN_INTERSECT_SHADOW].cssRG, + std::max(stack_size[PG_RGEN_INTERSECT_SUBSURFACE].cssRG, + stack_size[PG_RGEN_INTERSECT_VOLUME_STACK].cssRG))) + + link_options.maxTraceDepth * trace_css; + + optix_assert( + optixPipelineSetStackSize(pipelines[PIP_INTERSECT], 0, 0, css, motion_blur ? 3 : 2)); + } + + /* Clean up program group objects. */ + for (unsigned int i = 0; i < NUM_PROGRAM_GROUPS; ++i) { + optixProgramGroupDestroy(groups[i]); + } + + return true; +} + +/* -------------------------------------------------------------------- + * Buffer denoising. + */ + +class OptiXDevice::DenoiseContext { + public: + explicit DenoiseContext(OptiXDevice *device, const DeviceDenoiseTask &task) + : denoise_params(task.params), + render_buffers(task.render_buffers), + buffer_params(task.buffer_params), + guiding_buffer(device, "denoiser guiding passes buffer"), + num_samples(task.num_samples) + { + num_input_passes = 1; + if (denoise_params.use_pass_albedo) { + num_input_passes += 1; + use_pass_albedo = true; + pass_denoising_albedo = buffer_params.get_pass_offset(PASS_DENOISING_ALBEDO); + if (denoise_params.use_pass_normal) { + num_input_passes += 1; + use_pass_normal = true; + pass_denoising_normal = buffer_params.get_pass_offset(PASS_DENOISING_NORMAL); + } + } + + const int num_guiding_passes = num_input_passes - 1; + + if (num_guiding_passes) { + if (task.allow_inplace_modification) { + guiding_params.device_pointer = render_buffers->buffer.device_pointer; + + guiding_params.pass_albedo = pass_denoising_albedo; + guiding_params.pass_normal = pass_denoising_normal; + + guiding_params.stride = buffer_params.stride; + guiding_params.pass_stride = buffer_params.pass_stride; + } + else { + guiding_params.pass_stride = 0; + if (use_pass_albedo) { + guiding_params.pass_albedo = guiding_params.pass_stride; + guiding_params.pass_stride += 3; + } + if (use_pass_normal) { + guiding_params.pass_normal = guiding_params.pass_stride; + guiding_params.pass_stride += 3; + } + + guiding_params.stride = buffer_params.width; + + guiding_buffer.alloc_to_device(buffer_params.width * buffer_params.height * + guiding_params.pass_stride); + guiding_params.device_pointer = guiding_buffer.device_pointer; + } + } + + pass_sample_count = buffer_params.get_pass_offset(PASS_SAMPLE_COUNT); + } + + const DenoiseParams &denoise_params; + + RenderBuffers *render_buffers = nullptr; + const BufferParams &buffer_params; + + /* Device-side storage of the guiding passes. */ + device_only_memory guiding_buffer; + + struct { + device_ptr device_pointer = 0; + + /* NOTE: Are only initialized when the corresponding guiding pass is enabled. */ + int pass_albedo = PASS_UNUSED; + int pass_normal = PASS_UNUSED; + + int stride = -1; + int pass_stride = -1; + } guiding_params; + + /* Number of input passes. Including the color and extra auxillary passes. */ + int num_input_passes = 0; + bool use_pass_albedo = false; + bool use_pass_normal = false; + + int num_samples = 0; + + int pass_sample_count = PASS_UNUSED; + + /* NOTE: Are only initialized when the corresponding guiding pass is enabled. */ + int pass_denoising_albedo = PASS_UNUSED; + int pass_denoising_normal = PASS_UNUSED; + + /* For passes which don't need albedo channel for denoising we replace the actual albedo with + * the (0.5, 0.5, 0.5). This flag indicates that the real albedo pass has been replaced with + * the fake values and denoising of passes which do need albedo can no longer happen. */ + bool albedo_replaced_with_fake = false; +}; + +class OptiXDevice::DenoisePass { + public: + DenoisePass(const PassType type, const BufferParams &buffer_params) : type(type) + { + noisy_offset = buffer_params.get_pass_offset(type, PassMode::NOISY); + denoised_offset = buffer_params.get_pass_offset(type, PassMode::DENOISED); + + const PassInfo pass_info = Pass::get_info(type); + num_components = pass_info.num_components; + use_compositing = pass_info.use_compositing; + use_denoising_albedo = pass_info.use_denoising_albedo; + } + + PassType type; + + int noisy_offset; + int denoised_offset; + + int num_components; + bool use_compositing; + bool use_denoising_albedo; +}; + +bool OptiXDevice::denoise_buffer(const DeviceDenoiseTask &task) +{ + const CUDAContextScope scope(this); + + DenoiseContext context(this, task); + + if (!denoise_ensure(context)) { + return false; + } + + if (!denoise_filter_guiding_preprocess(context)) { + LOG(ERROR) << "Error preprocessing guiding passes."; + return false; + } + + /* Passes which will use real albedo when it is available. */ + denoise_pass(context, PASS_COMBINED); + denoise_pass(context, PASS_SHADOW_CATCHER_MATTE); + + /* Passes which do not need albedo and hence if real is present it needs to become fake. */ + denoise_pass(context, PASS_SHADOW_CATCHER); + + return true; +} + +DeviceQueue *OptiXDevice::get_denoise_queue() +{ + return &denoiser_.queue; +} + +bool OptiXDevice::denoise_filter_guiding_preprocess(DenoiseContext &context) +{ + const BufferParams &buffer_params = context.buffer_params; + + const int work_size = buffer_params.width * buffer_params.height; + + void *args[] = {const_cast(&context.guiding_params.device_pointer), + const_cast(&context.guiding_params.pass_stride), + const_cast(&context.guiding_params.pass_albedo), + const_cast(&context.guiding_params.pass_normal), + &context.render_buffers->buffer.device_pointer, + const_cast(&buffer_params.offset), + const_cast(&buffer_params.stride), + const_cast(&buffer_params.pass_stride), + const_cast(&context.pass_sample_count), + const_cast(&context.pass_denoising_albedo), + const_cast(&context.pass_denoising_normal), + const_cast(&buffer_params.full_x), + const_cast(&buffer_params.full_y), + const_cast(&buffer_params.width), + const_cast(&buffer_params.height), + const_cast(&context.num_samples)}; + + return denoiser_.queue.enqueue(DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS, work_size, args); +} + +bool OptiXDevice::denoise_filter_guiding_set_fake_albedo(DenoiseContext &context) +{ + const BufferParams &buffer_params = context.buffer_params; + + const int work_size = buffer_params.width * buffer_params.height; + + void *args[] = {const_cast(&context.guiding_params.device_pointer), + const_cast(&context.guiding_params.pass_stride), + const_cast(&context.guiding_params.pass_albedo), + const_cast(&buffer_params.width), + const_cast(&buffer_params.height)}; + + return denoiser_.queue.enqueue(DEVICE_KERNEL_FILTER_GUIDING_SET_FAKE_ALBEDO, work_size, args); +} + +void OptiXDevice::denoise_pass(DenoiseContext &context, PassType pass_type) +{ + const BufferParams &buffer_params = context.buffer_params; + + const DenoisePass pass(pass_type, buffer_params); + + if (pass.noisy_offset == PASS_UNUSED) { + return; + } + if (pass.denoised_offset == PASS_UNUSED) { + LOG(DFATAL) << "Missing denoised pass " << pass_type_as_string(pass_type); + return; + } + + if (pass.use_denoising_albedo) { + if (context.albedo_replaced_with_fake) { + LOG(ERROR) << "Pass which requires albedo is denoised after fake albedo has been set."; + return; + } + } + else if (!context.albedo_replaced_with_fake) { + context.albedo_replaced_with_fake = true; + if (!denoise_filter_guiding_set_fake_albedo(context)) { + LOG(ERROR) << "Error replacing real albedo with the fake one."; + return; + } + } + + /* Read and preprocess noisy color input pass. */ + denoise_color_read(context, pass); + if (!denoise_filter_color_preprocess(context, pass)) { + LOG(ERROR) << "Error connverting denoising passes to RGB buffer."; + return; + } + + if (!denoise_run(context, pass)) { + LOG(ERROR) << "Error running OptiX denoiser."; + return; + } + + /* Store result in the combined pass of the render buffer. + * + * This will scale the denoiser result up to match the number of, possibly per-pixel, samples. */ + if (!denoise_filter_color_postprocess(context, pass)) { + LOG(ERROR) << "Error copying denoiser result to the denoised pass."; + return; + } + + denoiser_.queue.synchronize(); +} + +void OptiXDevice::denoise_color_read(DenoiseContext &context, const DenoisePass &pass) +{ + PassAccessor::PassAccessInfo pass_access_info; + pass_access_info.type = pass.type; + pass_access_info.mode = PassMode::NOISY; + pass_access_info.offset = pass.noisy_offset; + + /* Denoiser operates on passes which are used to calculate the approximation, and is never used + * on the approximation. The latter is not even possible because OptiX does not support + * denoising of semi-transparent pixels. */ + pass_access_info.use_approximate_shadow_catcher = false; + pass_access_info.use_approximate_shadow_catcher_background = false; + pass_access_info.show_active_pixels = false; + + /* TODO(sergey): Consider adding support of actual exposure, to avoid clamping in extreme cases. + */ + const PassAccessorGPU pass_accessor( + &denoiser_.queue, pass_access_info, 1.0f, context.num_samples); + + PassAccessor::Destination destination(pass_access_info.type); + destination.d_pixels = context.render_buffers->buffer.device_pointer + + pass.denoised_offset * sizeof(float); + destination.num_components = 3; + destination.pixel_stride = context.buffer_params.pass_stride; + + pass_accessor.get_render_tile_pixels(context.render_buffers, context.buffer_params, destination); +} + +bool OptiXDevice::denoise_filter_color_preprocess(DenoiseContext &context, const DenoisePass &pass) +{ + const BufferParams &buffer_params = context.buffer_params; + + const int work_size = buffer_params.width * buffer_params.height; + + void *args[] = {&context.render_buffers->buffer.device_pointer, + const_cast(&buffer_params.full_x), + const_cast(&buffer_params.full_y), + const_cast(&buffer_params.width), + const_cast(&buffer_params.height), + const_cast(&buffer_params.offset), + const_cast(&buffer_params.stride), + const_cast(&buffer_params.pass_stride), + const_cast(&pass.denoised_offset)}; + + return denoiser_.queue.enqueue(DEVICE_KERNEL_FILTER_COLOR_PREPROCESS, work_size, args); +} + +bool OptiXDevice::denoise_filter_color_postprocess(DenoiseContext &context, + const DenoisePass &pass) +{ + const BufferParams &buffer_params = context.buffer_params; + + const int work_size = buffer_params.width * buffer_params.height; + + void *args[] = {&context.render_buffers->buffer.device_pointer, + const_cast(&buffer_params.full_x), + const_cast(&buffer_params.full_y), + const_cast(&buffer_params.width), + const_cast(&buffer_params.height), + const_cast(&buffer_params.offset), + const_cast(&buffer_params.stride), + const_cast(&buffer_params.pass_stride), + const_cast(&context.num_samples), + const_cast(&pass.noisy_offset), + const_cast(&pass.denoised_offset), + const_cast(&context.pass_sample_count), + const_cast(&pass.num_components), + const_cast(&pass.use_compositing)}; + + return denoiser_.queue.enqueue(DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS, work_size, args); +} + +bool OptiXDevice::denoise_ensure(DenoiseContext &context) +{ + if (!denoise_create_if_needed(context)) { + LOG(ERROR) << "OptiX denoiser creation has failed."; + return false; + } + + if (!denoise_configure_if_needed(context)) { + LOG(ERROR) << "OptiX denoiser configuration has failed."; + return false; + } + + return true; +} + +bool OptiXDevice::denoise_create_if_needed(DenoiseContext &context) +{ + const bool recreate_denoiser = (denoiser_.optix_denoiser == nullptr) || + (denoiser_.use_pass_albedo != context.use_pass_albedo) || + (denoiser_.use_pass_normal != context.use_pass_normal); + if (!recreate_denoiser) { + return true; + } + + /* Destroy existing handle before creating new one. */ + if (denoiser_.optix_denoiser) { + optixDenoiserDestroy(denoiser_.optix_denoiser); + } + + /* Create OptiX denoiser handle on demand when it is first used. */ + OptixDenoiserOptions denoiser_options = {}; + denoiser_options.guideAlbedo = context.use_pass_albedo; + denoiser_options.guideNormal = context.use_pass_normal; + const OptixResult result = optixDenoiserCreate( + this->context, OPTIX_DENOISER_MODEL_KIND_HDR, &denoiser_options, &denoiser_.optix_denoiser); + + if (result != OPTIX_SUCCESS) { + set_error("Failed to create OptiX denoiser"); + return false; + } + + /* OptiX denoiser handle was created with the requested number of input passes. */ + denoiser_.use_pass_albedo = context.use_pass_albedo; + denoiser_.use_pass_normal = context.use_pass_normal; + + /* OptiX denoiser has been created, but it needs configuration. */ + denoiser_.is_configured = false; + + return true; +} + +bool OptiXDevice::denoise_configure_if_needed(DenoiseContext &context) +{ + if (denoiser_.is_configured && (denoiser_.configured_size.x == context.buffer_params.width && + denoiser_.configured_size.y == context.buffer_params.height)) { + return true; + } + + const BufferParams &buffer_params = context.buffer_params; + + OptixDenoiserSizes sizes = {}; + optix_assert(optixDenoiserComputeMemoryResources( + denoiser_.optix_denoiser, buffer_params.width, buffer_params.height, &sizes)); + + denoiser_.scratch_size = sizes.withOverlapScratchSizeInBytes; + denoiser_.scratch_offset = sizes.stateSizeInBytes; + + /* Allocate denoiser state if tile size has changed since last setup. */ + denoiser_.state.alloc_to_device(denoiser_.scratch_offset + denoiser_.scratch_size); + + /* Initialize denoiser state for the current tile size. */ + const OptixResult result = optixDenoiserSetup(denoiser_.optix_denoiser, + denoiser_.queue.stream(), + buffer_params.width, + buffer_params.height, + denoiser_.state.device_pointer, + denoiser_.scratch_offset, + denoiser_.state.device_pointer + + denoiser_.scratch_offset, + denoiser_.scratch_size); + if (result != OPTIX_SUCCESS) { + set_error("Failed to set up OptiX denoiser"); + return false; + } + + denoiser_.is_configured = true; + denoiser_.configured_size.x = buffer_params.width; + denoiser_.configured_size.y = buffer_params.height; + + return true; +} + +bool OptiXDevice::denoise_run(DenoiseContext &context, const DenoisePass &pass) +{ + const BufferParams &buffer_params = context.buffer_params; + const int width = buffer_params.width; + const int height = buffer_params.height; + + /* Set up input and output layer information. */ + OptixImage2D color_layer = {0}; + OptixImage2D albedo_layer = {0}; + OptixImage2D normal_layer = {0}; + + OptixImage2D output_layer = {0}; + + /* Color pass. */ + { + const int pass_denoised = pass.denoised_offset; + const int64_t pass_stride_in_bytes = context.buffer_params.pass_stride * sizeof(float); + + color_layer.data = context.render_buffers->buffer.device_pointer + + pass_denoised * sizeof(float); + color_layer.width = width; + color_layer.height = height; + color_layer.rowStrideInBytes = pass_stride_in_bytes * context.buffer_params.stride; + color_layer.pixelStrideInBytes = pass_stride_in_bytes; + color_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3; + } + + device_vector fake_albedo(this, "fake_albedo", MEM_READ_WRITE); + + /* Optional albedo and color passes. */ + if (context.num_input_passes > 1) { + const device_ptr d_guiding_buffer = context.guiding_params.device_pointer; + const int64_t pixel_stride_in_bytes = context.guiding_params.pass_stride * sizeof(float); + const int64_t row_stride_in_bytes = context.guiding_params.stride * pixel_stride_in_bytes; + + if (context.use_pass_albedo) { + albedo_layer.data = d_guiding_buffer + context.guiding_params.pass_albedo * sizeof(float); + albedo_layer.width = width; + albedo_layer.height = height; + albedo_layer.rowStrideInBytes = row_stride_in_bytes; + albedo_layer.pixelStrideInBytes = pixel_stride_in_bytes; + albedo_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3; + } + + if (context.use_pass_normal) { + normal_layer.data = d_guiding_buffer + context.guiding_params.pass_normal * sizeof(float); + normal_layer.width = width; + normal_layer.height = height; + normal_layer.rowStrideInBytes = row_stride_in_bytes; + normal_layer.pixelStrideInBytes = pixel_stride_in_bytes; + normal_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3; + } + } + + /* Denoise in-place of the noisy input in the render buffers. */ + output_layer = color_layer; + + /* Finally run denonising. */ + OptixDenoiserParams params = {}; /* All parameters are disabled/zero. */ + OptixDenoiserLayer image_layers = {}; + image_layers.input = color_layer; + image_layers.output = output_layer; + + OptixDenoiserGuideLayer guide_layers = {}; + guide_layers.albedo = albedo_layer; + guide_layers.normal = normal_layer; + + optix_assert(optixDenoiserInvoke(denoiser_.optix_denoiser, + denoiser_.queue.stream(), + ¶ms, + denoiser_.state.device_pointer, + denoiser_.scratch_offset, + &guide_layers, + &image_layers, + 1, + 0, + 0, + denoiser_.state.device_pointer + denoiser_.scratch_offset, + denoiser_.scratch_size)); + + return true; +} + +bool OptiXDevice::build_optix_bvh(BVHOptiX *bvh, + OptixBuildOperation operation, + const OptixBuildInput &build_input, + uint16_t num_motion_steps) +{ + const CUDAContextScope scope(this); + + const bool use_fast_trace_bvh = (bvh->params.bvh_type == BVH_TYPE_STATIC); + + /* Compute memory usage. */ + OptixAccelBufferSizes sizes = {}; + OptixAccelBuildOptions options = {}; + options.operation = operation; + if (use_fast_trace_bvh) { + VLOG(2) << "Using fast to trace OptiX BVH"; + options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_TRACE | OPTIX_BUILD_FLAG_ALLOW_COMPACTION; + } + else { + VLOG(2) << "Using fast to update OptiX BVH"; + options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_BUILD | OPTIX_BUILD_FLAG_ALLOW_UPDATE; + } + + options.motionOptions.numKeys = num_motion_steps; + options.motionOptions.flags = OPTIX_MOTION_FLAG_START_VANISH | OPTIX_MOTION_FLAG_END_VANISH; + options.motionOptions.timeBegin = 0.0f; + options.motionOptions.timeEnd = 1.0f; + + optix_assert(optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes)); + + /* Allocate required output buffers. */ + device_only_memory temp_mem(this, "optix temp as build mem"); + temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8); + if (!temp_mem.device_pointer) { + /* Make sure temporary memory allocation succeeded. */ + return false; + } + + device_only_memory &out_data = bvh->as_data; + if (operation == OPTIX_BUILD_OPERATION_BUILD) { + assert(out_data.device == this); + out_data.alloc_to_device(sizes.outputSizeInBytes); + if (!out_data.device_pointer) { + return false; + } + } + else { + assert(out_data.device_pointer && out_data.device_size >= sizes.outputSizeInBytes); + } + + /* Finally build the acceleration structure. */ + OptixAccelEmitDesc compacted_size_prop = {}; + compacted_size_prop.type = OPTIX_PROPERTY_TYPE_COMPACTED_SIZE; + /* A tiny space was allocated for this property at the end of the temporary buffer above. + * Make sure this pointer is 8-byte aligned. */ + compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8); + + OptixTraversableHandle out_handle = 0; + optix_assert(optixAccelBuild(context, + NULL, + &options, + &build_input, + 1, + temp_mem.device_pointer, + sizes.tempSizeInBytes, + out_data.device_pointer, + sizes.outputSizeInBytes, + &out_handle, + use_fast_trace_bvh ? &compacted_size_prop : NULL, + use_fast_trace_bvh ? 1 : 0)); + bvh->traversable_handle = static_cast(out_handle); + + /* Wait for all operations to finish. */ + cuda_assert(cuStreamSynchronize(NULL)); + + /* Compact acceleration structure to save memory (do not do this in viewport for faster builds). + */ + if (use_fast_trace_bvh) { + uint64_t compacted_size = sizes.outputSizeInBytes; + cuda_assert(cuMemcpyDtoH(&compacted_size, compacted_size_prop.result, sizeof(compacted_size))); + + /* Temporary memory is no longer needed, so free it now to make space. */ + temp_mem.free(); + + /* There is no point compacting if the size does not change. */ + if (compacted_size < sizes.outputSizeInBytes) { + device_only_memory compacted_data(this, "optix compacted as"); + compacted_data.alloc_to_device(compacted_size); + if (!compacted_data.device_pointer) + /* Do not compact if memory allocation for compacted acceleration structure fails. + * Can just use the uncompacted one then, so succeed here regardless. */ + return !have_error(); + + optix_assert(optixAccelCompact( + context, NULL, out_handle, compacted_data.device_pointer, compacted_size, &out_handle)); + bvh->traversable_handle = static_cast(out_handle); + + /* Wait for compaction to finish. */ + cuda_assert(cuStreamSynchronize(NULL)); + + std::swap(out_data.device_size, compacted_data.device_size); + std::swap(out_data.device_pointer, compacted_data.device_pointer); + } + } + + return !have_error(); +} + +void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) +{ + const bool use_fast_trace_bvh = (bvh->params.bvh_type == BVH_TYPE_STATIC); + + free_bvh_memory_delayed(); + + BVHOptiX *const bvh_optix = static_cast(bvh); + + progress.set_substatus("Building OptiX acceleration structure"); + + if (!bvh->params.top_level) { + assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1); + + /* Refit is only possible in viewport for now (because AS is built with + * OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above). */ + OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD; + if (refit && !use_fast_trace_bvh) { + assert(bvh_optix->traversable_handle != 0); + operation = OPTIX_BUILD_OPERATION_UPDATE; + } + else { + bvh_optix->as_data.free(); + bvh_optix->traversable_handle = 0; + } + + /* Build bottom level acceleration structures (BLAS). */ + Geometry *const geom = bvh->geometry[0]; + if (geom->geometry_type == Geometry::HAIR) { + /* Build BLAS for curve primitives. */ + Hair *const hair = static_cast(geom); + if (hair->num_curves() == 0) { + return; + } + + const size_t num_segments = hair->num_segments(); + + size_t num_motion_steps = 1; + Attribute *motion_keys = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + if (motion_blur && hair->get_use_motion_blur() && motion_keys) { + num_motion_steps = hair->get_motion_steps(); + } + + device_vector aabb_data(this, "optix temp aabb data", MEM_READ_ONLY); + device_vector index_data(this, "optix temp index data", MEM_READ_ONLY); + device_vector vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); + /* Four control points for each curve segment. */ + const size_t num_vertices = num_segments * 4; + if (hair->curve_shape == CURVE_THICK) { + index_data.alloc(num_segments); + vertex_data.alloc(num_vertices * num_motion_steps); + } + else + aabb_data.alloc(num_segments * num_motion_steps); + + /* Get AABBs for each motion step. */ + for (size_t step = 0; step < num_motion_steps; ++step) { + /* The center step for motion vertices is not stored in the attribute. */ + const float3 *keys = hair->get_curve_keys().data(); + size_t center_step = (num_motion_steps - 1) / 2; + if (step != center_step) { + size_t attr_offset = (step > center_step) ? step - 1 : step; + /* Technically this is a float4 array, but sizeof(float3) == sizeof(float4). */ + keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size(); + } + + for (size_t j = 0, i = 0; j < hair->num_curves(); ++j) { + const Hair::Curve curve = hair->get_curve(j); + const array &curve_radius = hair->get_curve_radius(); + + for (int segment = 0; segment < curve.num_segments(); ++segment, ++i) { + if (hair->curve_shape == CURVE_THICK) { + int k0 = curve.first_key + segment; + int k1 = k0 + 1; + int ka = max(k0 - 1, curve.first_key); + int kb = min(k1 + 1, curve.first_key + curve.num_keys - 1); + + const float4 px = make_float4(keys[ka].x, keys[k0].x, keys[k1].x, keys[kb].x); + const float4 py = make_float4(keys[ka].y, keys[k0].y, keys[k1].y, keys[kb].y); + const float4 pz = make_float4(keys[ka].z, keys[k0].z, keys[k1].z, keys[kb].z); + const float4 pw = make_float4( + curve_radius[ka], curve_radius[k0], curve_radius[k1], curve_radius[kb]); + + /* Convert Catmull-Rom data to Bezier spline. */ + static const float4 cr2bsp0 = make_float4(+7, -4, +5, -2) / 6.f; + static const float4 cr2bsp1 = make_float4(-2, 11, -4, +1) / 6.f; + static const float4 cr2bsp2 = make_float4(+1, -4, 11, -2) / 6.f; + static const float4 cr2bsp3 = make_float4(-2, +5, -4, +7) / 6.f; + + index_data[i] = i * 4; + float4 *const v = vertex_data.data() + step * num_vertices + index_data[i]; + v[0] = make_float4( + dot(cr2bsp0, px), dot(cr2bsp0, py), dot(cr2bsp0, pz), dot(cr2bsp0, pw)); + v[1] = make_float4( + dot(cr2bsp1, px), dot(cr2bsp1, py), dot(cr2bsp1, pz), dot(cr2bsp1, pw)); + v[2] = make_float4( + dot(cr2bsp2, px), dot(cr2bsp2, py), dot(cr2bsp2, pz), dot(cr2bsp2, pw)); + v[3] = make_float4( + dot(cr2bsp3, px), dot(cr2bsp3, py), dot(cr2bsp3, pz), dot(cr2bsp3, pw)); + } + else { + BoundBox bounds = BoundBox::empty; + curve.bounds_grow(segment, keys, hair->get_curve_radius().data(), bounds); + + const size_t index = step * num_segments + i; + aabb_data[index].minX = bounds.min.x; + aabb_data[index].minY = bounds.min.y; + aabb_data[index].minZ = bounds.min.z; + aabb_data[index].maxX = bounds.max.x; + aabb_data[index].maxY = bounds.max.y; + aabb_data[index].maxZ = bounds.max.z; + } + } + } + } + + /* Upload AABB data to GPU. */ + aabb_data.copy_to_device(); + index_data.copy_to_device(); + vertex_data.copy_to_device(); + + vector aabb_ptrs; + aabb_ptrs.reserve(num_motion_steps); + vector width_ptrs; + vector vertex_ptrs; + width_ptrs.reserve(num_motion_steps); + vertex_ptrs.reserve(num_motion_steps); + for (size_t step = 0; step < num_motion_steps; ++step) { + aabb_ptrs.push_back(aabb_data.device_pointer + step * num_segments * sizeof(OptixAabb)); + const device_ptr base_ptr = vertex_data.device_pointer + + step * num_vertices * sizeof(float4); + width_ptrs.push_back(base_ptr + 3 * sizeof(float)); /* Offset by vertex size. */ + vertex_ptrs.push_back(base_ptr); + } + + /* Force a single any-hit call, so shadow record-all behavior works correctly. */ + unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL; + OptixBuildInput build_input = {}; + if (hair->curve_shape == CURVE_THICK) { + build_input.type = OPTIX_BUILD_INPUT_TYPE_CURVES; + build_input.curveArray.curveType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE; + build_input.curveArray.numPrimitives = num_segments; + build_input.curveArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data(); + build_input.curveArray.numVertices = num_vertices; + build_input.curveArray.vertexStrideInBytes = sizeof(float4); + build_input.curveArray.widthBuffers = (CUdeviceptr *)width_ptrs.data(); + build_input.curveArray.widthStrideInBytes = sizeof(float4); + build_input.curveArray.indexBuffer = (CUdeviceptr)index_data.device_pointer; + build_input.curveArray.indexStrideInBytes = sizeof(int); + build_input.curveArray.flag = build_flags; + build_input.curveArray.primitiveIndexOffset = hair->optix_prim_offset; + } + else { + /* Disable visibility test any-hit program, since it is already checked during + * intersection. Those trace calls that require anyhit can force it with a ray flag. */ + build_flags |= OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT; + + build_input.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES; + build_input.customPrimitiveArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data(); + build_input.customPrimitiveArray.numPrimitives = num_segments; + build_input.customPrimitiveArray.strideInBytes = sizeof(OptixAabb); + build_input.customPrimitiveArray.flags = &build_flags; + build_input.customPrimitiveArray.numSbtRecords = 1; + build_input.customPrimitiveArray.primitiveIndexOffset = hair->optix_prim_offset; + } + + if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { + progress.set_error("Failed to build OptiX acceleration structure"); + } + } + else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { + /* Build BLAS for triangle primitives. */ + Mesh *const mesh = static_cast(geom); + if (mesh->num_triangles() == 0) { + return; + } + + const size_t num_verts = mesh->get_verts().size(); + + size_t num_motion_steps = 1; + Attribute *motion_keys = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + if (motion_blur && mesh->get_use_motion_blur() && motion_keys) { + num_motion_steps = mesh->get_motion_steps(); + } + + device_vector index_data(this, "optix temp index data", MEM_READ_ONLY); + index_data.alloc(mesh->get_triangles().size()); + memcpy(index_data.data(), + mesh->get_triangles().data(), + mesh->get_triangles().size() * sizeof(int)); + device_vector vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); + vertex_data.alloc(num_verts * num_motion_steps); + + for (size_t step = 0; step < num_motion_steps; ++step) { + const float3 *verts = mesh->get_verts().data(); + + size_t center_step = (num_motion_steps - 1) / 2; + /* The center step for motion vertices is not stored in the attribute. */ + if (step != center_step) { + verts = motion_keys->data_float3() + (step > center_step ? step - 1 : step) * num_verts; + } + + memcpy(vertex_data.data() + num_verts * step, verts, num_verts * sizeof(float3)); + } + + /* Upload triangle data to GPU. */ + index_data.copy_to_device(); + vertex_data.copy_to_device(); + + vector vertex_ptrs; + vertex_ptrs.reserve(num_motion_steps); + for (size_t step = 0; step < num_motion_steps; ++step) { + vertex_ptrs.push_back(vertex_data.device_pointer + num_verts * step * sizeof(float3)); + } + + /* Force a single any-hit call, so shadow record-all behavior works correctly. */ + unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL; + OptixBuildInput build_input = {}; + build_input.type = OPTIX_BUILD_INPUT_TYPE_TRIANGLES; + build_input.triangleArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data(); + build_input.triangleArray.numVertices = num_verts; + build_input.triangleArray.vertexFormat = OPTIX_VERTEX_FORMAT_FLOAT3; + build_input.triangleArray.vertexStrideInBytes = sizeof(float4); + build_input.triangleArray.indexBuffer = index_data.device_pointer; + build_input.triangleArray.numIndexTriplets = mesh->num_triangles(); + build_input.triangleArray.indexFormat = OPTIX_INDICES_FORMAT_UNSIGNED_INT3; + build_input.triangleArray.indexStrideInBytes = 3 * sizeof(int); + build_input.triangleArray.flags = &build_flags; + /* The SBT does not store per primitive data since Cycles already allocates separate + * buffers for that purpose. OptiX does not allow this to be zero though, so just pass in + * one and rely on that having the same meaning in this case. */ + build_input.triangleArray.numSbtRecords = 1; + build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset; + + if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { + progress.set_error("Failed to build OptiX acceleration structure"); + } + } + } + else { + unsigned int num_instances = 0; + unsigned int max_num_instances = 0xFFFFFFFF; + + bvh_optix->as_data.free(); + bvh_optix->traversable_handle = 0; + bvh_optix->motion_transform_data.free(); + + optixDeviceContextGetProperty(context, + OPTIX_DEVICE_PROPERTY_LIMIT_MAX_INSTANCE_ID, + &max_num_instances, + sizeof(max_num_instances)); + /* Do not count first bit, which is used to distinguish instanced and non-instanced objects. */ + max_num_instances >>= 1; + if (bvh->objects.size() > max_num_instances) { + progress.set_error( + "Failed to build OptiX acceleration structure because there are too many instances"); + return; + } + + /* Fill instance descriptions. */ + device_vector instances(this, "optix tlas instances", MEM_READ_ONLY); + instances.alloc(bvh->objects.size()); + + /* Calculate total motion transform size and allocate memory for them. */ + size_t motion_transform_offset = 0; + if (motion_blur) { + size_t total_motion_transform_size = 0; + for (Object *const ob : bvh->objects) { + if (ob->is_traceable() && ob->use_motion()) { + total_motion_transform_size = align_up(total_motion_transform_size, + OPTIX_TRANSFORM_BYTE_ALIGNMENT); + const size_t motion_keys = max(ob->get_motion().size(), 2) - 2; + total_motion_transform_size = total_motion_transform_size + + sizeof(OptixSRTMotionTransform) + + motion_keys * sizeof(OptixSRTData); + } + } + + assert(bvh_optix->motion_transform_data.device == this); + bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size); + } + + for (Object *ob : bvh->objects) { + /* Skip non-traceable objects. */ + if (!ob->is_traceable()) { + continue; + } + + BVHOptiX *const blas = static_cast(ob->get_geometry()->bvh); + OptixTraversableHandle handle = blas->traversable_handle; + + OptixInstance &instance = instances[num_instances++]; + memset(&instance, 0, sizeof(instance)); + + /* Clear transform to identity matrix. */ + instance.transform[0] = 1.0f; + instance.transform[5] = 1.0f; + instance.transform[10] = 1.0f; + + /* Set user instance ID to object index (but leave low bit blank). */ + instance.instanceId = ob->get_device_index() << 1; + + /* Have to have at least one bit in the mask, or else instance would always be culled. */ + instance.visibilityMask = 1; + + if (ob->get_geometry()->has_volume) { + /* Volumes have a special bit set in the visibility mask so a trace can mask only volumes. + */ + instance.visibilityMask |= 2; + } + + if (ob->get_geometry()->geometry_type == Geometry::HAIR) { + /* Same applies to curves (so they can be skipped in local trace calls). */ + instance.visibilityMask |= 4; + + if (motion_blur && ob->get_geometry()->has_motion_blur() && + static_cast(ob->get_geometry())->curve_shape == CURVE_THICK) { + /* Select between motion blur and non-motion blur built-in intersection module. */ + instance.sbtOffset = PG_HITD_MOTION - PG_HITD; + } + } + + /* Insert motion traversable if object has motion. */ + if (motion_blur && ob->use_motion()) { + size_t motion_keys = max(ob->get_motion().size(), 2) - 2; + size_t motion_transform_size = sizeof(OptixSRTMotionTransform) + + motion_keys * sizeof(OptixSRTData); + + const CUDAContextScope scope(this); + + motion_transform_offset = align_up(motion_transform_offset, + OPTIX_TRANSFORM_BYTE_ALIGNMENT); + CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer + + motion_transform_offset; + motion_transform_offset += motion_transform_size; + + /* Allocate host side memory for motion transform and fill it with transform data. */ + OptixSRTMotionTransform &motion_transform = *reinterpret_cast( + new uint8_t[motion_transform_size]); + motion_transform.child = handle; + motion_transform.motionOptions.numKeys = ob->get_motion().size(); + motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE; + motion_transform.motionOptions.timeBegin = 0.0f; + motion_transform.motionOptions.timeEnd = 1.0f; + + OptixSRTData *const srt_data = motion_transform.srtData; + array decomp(ob->get_motion().size()); + transform_motion_decompose( + decomp.data(), ob->get_motion().data(), ob->get_motion().size()); + + for (size_t i = 0; i < ob->get_motion().size(); ++i) { + /* Scale. */ + srt_data[i].sx = decomp[i].y.w; /* scale.x.x */ + srt_data[i].sy = decomp[i].z.w; /* scale.y.y */ + srt_data[i].sz = decomp[i].w.w; /* scale.z.z */ + + /* Shear. */ + srt_data[i].a = decomp[i].z.x; /* scale.x.y */ + srt_data[i].b = decomp[i].z.y; /* scale.x.z */ + srt_data[i].c = decomp[i].w.x; /* scale.y.z */ + assert(decomp[i].z.z == 0.0f); /* scale.y.x */ + assert(decomp[i].w.y == 0.0f); /* scale.z.x */ + assert(decomp[i].w.z == 0.0f); /* scale.z.y */ + + /* Pivot point. */ + srt_data[i].pvx = 0.0f; + srt_data[i].pvy = 0.0f; + srt_data[i].pvz = 0.0f; + + /* Rotation. */ + srt_data[i].qx = decomp[i].x.x; + srt_data[i].qy = decomp[i].x.y; + srt_data[i].qz = decomp[i].x.z; + srt_data[i].qw = decomp[i].x.w; + + /* Translation. */ + srt_data[i].tx = decomp[i].y.x; + srt_data[i].ty = decomp[i].y.y; + srt_data[i].tz = decomp[i].y.z; + } + + /* Upload motion transform to GPU. */ + cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size); + delete[] reinterpret_cast(&motion_transform); + + /* Disable instance transform if object uses motion transform already. */ + instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + + /* Get traversable handle to motion transform. */ + optixConvertPointerToTraversableHandle(context, + motion_transform_gpu, + OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM, + &instance.traversableHandle); + } + else { + instance.traversableHandle = handle; + + if (ob->get_geometry()->is_instanced()) { + /* Set transform matrix. */ + memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform)); + } + else { + /* Disable instance transform if geometry already has it applied to vertex data. */ + instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + /* Non-instanced objects read ID from 'prim_object', so distinguish + * them from instanced objects with the low bit set. */ + instance.instanceId |= 1; + } + } + } + + /* Upload instance descriptions. */ + instances.resize(num_instances); + instances.copy_to_device(); + + /* Build top-level acceleration structure (TLAS) */ + OptixBuildInput build_input = {}; + build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES; + build_input.instanceArray.instances = instances.device_pointer; + build_input.instanceArray.numInstances = num_instances; + + if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) { + progress.set_error("Failed to build OptiX acceleration structure"); + } + tlas_handle = bvh_optix->traversable_handle; + } +} + +void OptiXDevice::release_optix_bvh(BVH *bvh) +{ + thread_scoped_lock lock(delayed_free_bvh_mutex); + /* Do delayed free of BVH memory, since geometry holding BVH might be deleted + * while GPU is still rendering. */ + BVHOptiX *const bvh_optix = static_cast(bvh); + + delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->as_data)); + delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->motion_transform_data)); + bvh_optix->traversable_handle = 0; +} + +void OptiXDevice::free_bvh_memory_delayed() +{ + thread_scoped_lock lock(delayed_free_bvh_mutex); + delayed_free_bvh_memory.free_memory(); +} + +void OptiXDevice::const_copy_to(const char *name, void *host, size_t size) +{ + /* Set constant memory for CUDA module. */ + CUDADevice::const_copy_to(name, host, size); + + if (strcmp(name, "__data") == 0) { + assert(size <= sizeof(KernelData)); + + /* Update traversable handle (since it is different for each device on multi devices). */ + KernelData *const data = (KernelData *)host; + *(OptixTraversableHandle *)&data->bvh.scene = tlas_handle; + + update_launch_params(offsetof(KernelParamsOptiX, data), host, size); + return; + } + + /* Update data storage pointers in launch parameters. */ +# define KERNEL_TEX(data_type, tex_name) \ + if (strcmp(name, #tex_name) == 0) { \ + update_launch_params(offsetof(KernelParamsOptiX, tex_name), host, size); \ + return; \ + } + KERNEL_TEX(IntegratorStateGPU, __integrator_state) +# include "kernel/kernel_textures.h" +# undef KERNEL_TEX +} + +void OptiXDevice::update_launch_params(size_t offset, void *data, size_t data_size) +{ + const CUDAContextScope scope(this); + + cuda_assert(cuMemcpyHtoD(launch_params.device_pointer + offset, data, data_size)); +} + +CCL_NAMESPACE_END + +#endif /* WITH_OPTIX */ diff --git a/intern/cycles/device/optix/device_impl.h b/intern/cycles/device/optix/device_impl.h new file mode 100644 index 00000000000..742ae0f1bab --- /dev/null +++ b/intern/cycles/device/optix/device_impl.h @@ -0,0 +1,186 @@ +/* + * Copyright 2019, NVIDIA Corporation. + * Copyright 2019, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_OPTIX + +# include "device/cuda/device_impl.h" +# include "device/optix/queue.h" +# include "device/optix/util.h" +# include "kernel/kernel_types.h" + +CCL_NAMESPACE_BEGIN + +class BVHOptiX; +struct KernelParamsOptiX; + +/* List of OptiX program groups. */ +enum { + PG_RGEN_INTERSECT_CLOSEST, + PG_RGEN_INTERSECT_SHADOW, + PG_RGEN_INTERSECT_SUBSURFACE, + PG_RGEN_INTERSECT_VOLUME_STACK, + PG_RGEN_SHADE_SURFACE_RAYTRACE, + PG_MISS, + PG_HITD, /* Default hit group. */ + PG_HITS, /* __SHADOW_RECORD_ALL__ hit group. */ + PG_HITL, /* __BVH_LOCAL__ hit group (only used for triangles). */ + PG_HITD_MOTION, + PG_HITS_MOTION, + PG_CALL_SVM_AO, + PG_CALL_SVM_BEVEL, + PG_CALL_AO_PASS, + NUM_PROGRAM_GROUPS +}; + +static const int MISS_PROGRAM_GROUP_OFFSET = PG_MISS; +static const int NUM_MIS_PROGRAM_GROUPS = 1; +static const int HIT_PROGAM_GROUP_OFFSET = PG_HITD; +static const int NUM_HIT_PROGRAM_GROUPS = 5; +static const int CALLABLE_PROGRAM_GROUPS_BASE = PG_CALL_SVM_AO; +static const int NUM_CALLABLE_PROGRAM_GROUPS = 3; + +/* List of OptiX pipelines. */ +enum { PIP_SHADE_RAYTRACE, PIP_INTERSECT, NUM_PIPELINES }; + +/* A single shader binding table entry. */ +struct SbtRecord { + char header[OPTIX_SBT_RECORD_HEADER_SIZE]; +}; + +class OptiXDevice : public CUDADevice { + public: + OptixDeviceContext context = NULL; + + OptixModule optix_module = NULL; /* All necessary OptiX kernels are in one module. */ + OptixModule builtin_modules[2] = {}; + OptixPipeline pipelines[NUM_PIPELINES] = {}; + + bool motion_blur = false; + device_vector sbt_data; + device_only_memory launch_params; + OptixTraversableHandle tlas_handle = 0; + + vector> delayed_free_bvh_memory; + thread_mutex delayed_free_bvh_mutex; + + class Denoiser { + public: + explicit Denoiser(OptiXDevice *device); + ~Denoiser(); + + OptiXDevice *device; + OptiXDeviceQueue queue; + + OptixDenoiser optix_denoiser = nullptr; + + /* Configuration size, as provided to `optixDenoiserSetup`. + * If the `optixDenoiserSetup()` was never used on the current `optix_denoiser` the + * `is_configured` will be false. */ + bool is_configured = false; + int2 configured_size = make_int2(0, 0); + + /* OptiX denoiser state and scratch buffers, stored in a single memory buffer. + * The memory layout goes as following: [denoiser state][scratch buffer]. */ + device_only_memory state; + size_t scratch_offset = 0; + size_t scratch_size = 0; + + bool use_pass_albedo = false; + bool use_pass_normal = false; + }; + Denoiser denoiser_; + + public: + OptiXDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler); + ~OptiXDevice(); + + private: + BVHLayoutMask get_bvh_layout_mask() const override; + + string compile_kernel_get_common_cflags(const uint kernel_features) override; + + bool load_kernels(const uint kernel_features) override; + + bool build_optix_bvh(BVHOptiX *bvh, + OptixBuildOperation operation, + const OptixBuildInput &build_input, + uint16_t num_motion_steps); + + void build_bvh(BVH *bvh, Progress &progress, bool refit) override; + + void release_optix_bvh(BVH *bvh) override; + void free_bvh_memory_delayed(); + + void const_copy_to(const char *name, void *host, size_t size) override; + + void update_launch_params(size_t offset, void *data, size_t data_size); + + virtual unique_ptr gpu_queue_create() override; + + /* -------------------------------------------------------------------- + * Denoising. + */ + + class DenoiseContext; + class DenoisePass; + + virtual bool denoise_buffer(const DeviceDenoiseTask &task) override; + virtual DeviceQueue *get_denoise_queue() override; + + /* Read guiding passes from the render buffers, preprocess them in a way which is expected by + * OptiX and store in the guiding passes memory within the given context. + * + * Pre=-processing of the guiding passes is to only hapopen once per context lifetime. DO not + * preprocess them for every pass which is being denoised. */ + bool denoise_filter_guiding_preprocess(DenoiseContext &context); + + /* Set fake albedo pixels in the albedo guiding pass storage. + * After this point only passes which do not need albedo for denoising can be processed. */ + bool denoise_filter_guiding_set_fake_albedo(DenoiseContext &context); + + void denoise_pass(DenoiseContext &context, PassType pass_type); + + /* Read input color pass from the render buffer into the memory which corresponds to the noisy + * input within the given context. Pixels are scaled to the number of samples, but are not + * preprocessed yet. */ + void denoise_color_read(DenoiseContext &context, const DenoisePass &pass); + + /* Run corresponding filter kernels, preparing data for the denoiser or copying data from the + * denoiser result to the render buffer. */ + bool denoise_filter_color_preprocess(DenoiseContext &context, const DenoisePass &pass); + bool denoise_filter_color_postprocess(DenoiseContext &context, const DenoisePass &pass); + + /* Make sure the OptiX denoiser is created and configured. */ + bool denoise_ensure(DenoiseContext &context); + + /* Create OptiX denoiser descriptor if needed. + * Will do nothing if the current OptiX descriptor is usable for the given parameters. + * If the OptiX denoiser descriptor did re-allocate here it is left unconfigured. */ + bool denoise_create_if_needed(DenoiseContext &context); + + /* Configure existing OptiX denoiser descriptor for the use for the given task. */ + bool denoise_configure_if_needed(DenoiseContext &context); + + /* Run configured denoiser. */ + bool denoise_run(DenoiseContext &context, const DenoisePass &pass); +}; + +CCL_NAMESPACE_END + +#endif /* WITH_OPTIX */ diff --git a/intern/cycles/device/optix/queue.cpp b/intern/cycles/device/optix/queue.cpp new file mode 100644 index 00000000000..458ed70baa8 --- /dev/null +++ b/intern/cycles/device/optix/queue.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_OPTIX + +# include "device/optix/queue.h" +# include "device/optix/device_impl.h" + +# include "util/util_time.h" + +# undef __KERNEL_CPU__ +# define __KERNEL_OPTIX__ +# include "kernel/device/optix/globals.h" + +CCL_NAMESPACE_BEGIN + +/* CUDADeviceQueue */ + +OptiXDeviceQueue::OptiXDeviceQueue(OptiXDevice *device) : CUDADeviceQueue(device) +{ +} + +void OptiXDeviceQueue::init_execution() +{ + CUDADeviceQueue::init_execution(); +} + +static bool is_optix_specific_kernel(DeviceKernel kernel) +{ + return (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE || + kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || + kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || + kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE || + kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); +} + +bool OptiXDeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *args[]) +{ + if (!is_optix_specific_kernel(kernel)) { + return CUDADeviceQueue::enqueue(kernel, work_size, args); + } + + if (cuda_device_->have_error()) { + return false; + } + + debug_enqueue(kernel, work_size); + + const CUDAContextScope scope(cuda_device_); + + OptiXDevice *const optix_device = static_cast(cuda_device_); + + const device_ptr sbt_data_ptr = optix_device->sbt_data.device_pointer; + const device_ptr launch_params_ptr = optix_device->launch_params.device_pointer; + + cuda_device_assert( + cuda_device_, + cuMemcpyHtoDAsync(launch_params_ptr + offsetof(KernelParamsOptiX, path_index_array), + args[0], // &d_path_index + sizeof(device_ptr), + cuda_stream_)); + + if (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) { + cuda_device_assert( + cuda_device_, + cuMemcpyHtoDAsync(launch_params_ptr + offsetof(KernelParamsOptiX, render_buffer), + args[1], // &d_render_buffer + sizeof(device_ptr), + cuda_stream_)); + } + + cuda_device_assert(cuda_device_, cuStreamSynchronize(cuda_stream_)); + + OptixPipeline pipeline = nullptr; + OptixShaderBindingTable sbt_params = {}; + + switch (kernel) { + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE: + pipeline = optix_device->pipelines[PIP_SHADE_RAYTRACE]; + sbt_params.raygenRecord = sbt_data_ptr + PG_RGEN_SHADE_SURFACE_RAYTRACE * sizeof(SbtRecord); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: + pipeline = optix_device->pipelines[PIP_INTERSECT]; + sbt_params.raygenRecord = sbt_data_ptr + PG_RGEN_INTERSECT_CLOSEST * sizeof(SbtRecord); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: + pipeline = optix_device->pipelines[PIP_INTERSECT]; + sbt_params.raygenRecord = sbt_data_ptr + PG_RGEN_INTERSECT_SHADOW * sizeof(SbtRecord); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE: + pipeline = optix_device->pipelines[PIP_INTERSECT]; + sbt_params.raygenRecord = sbt_data_ptr + PG_RGEN_INTERSECT_SUBSURFACE * sizeof(SbtRecord); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: + pipeline = optix_device->pipelines[PIP_INTERSECT]; + sbt_params.raygenRecord = sbt_data_ptr + PG_RGEN_INTERSECT_VOLUME_STACK * sizeof(SbtRecord); + break; + + default: + LOG(ERROR) << "Invalid kernel " << device_kernel_as_string(kernel) + << " is attempted to be enqueued."; + return false; + } + + sbt_params.missRecordBase = sbt_data_ptr + MISS_PROGRAM_GROUP_OFFSET * sizeof(SbtRecord); + sbt_params.missRecordStrideInBytes = sizeof(SbtRecord); + sbt_params.missRecordCount = NUM_MIS_PROGRAM_GROUPS; + sbt_params.hitgroupRecordBase = sbt_data_ptr + HIT_PROGAM_GROUP_OFFSET * sizeof(SbtRecord); + sbt_params.hitgroupRecordStrideInBytes = sizeof(SbtRecord); + sbt_params.hitgroupRecordCount = NUM_HIT_PROGRAM_GROUPS; + sbt_params.callablesRecordBase = sbt_data_ptr + CALLABLE_PROGRAM_GROUPS_BASE * sizeof(SbtRecord); + sbt_params.callablesRecordCount = NUM_CALLABLE_PROGRAM_GROUPS; + sbt_params.callablesRecordStrideInBytes = sizeof(SbtRecord); + + /* Launch the ray generation program. */ + optix_device_assert(optix_device, + optixLaunch(pipeline, + cuda_stream_, + launch_params_ptr, + optix_device->launch_params.data_elements, + &sbt_params, + work_size, + 1, + 1)); + + return !(optix_device->have_error()); +} + +CCL_NAMESPACE_END + +#endif /* WITH_OPTIX */ diff --git a/intern/cycles/device/optix/queue.h b/intern/cycles/device/optix/queue.h new file mode 100644 index 00000000000..0de422ccc71 --- /dev/null +++ b/intern/cycles/device/optix/queue.h @@ -0,0 +1,39 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_OPTIX + +# include "device/cuda/queue.h" + +CCL_NAMESPACE_BEGIN + +class OptiXDevice; + +/* Base class for CUDA queues. */ +class OptiXDeviceQueue : public CUDADeviceQueue { + public: + OptiXDeviceQueue(OptiXDevice *device); + + virtual void init_execution() override; + + virtual bool enqueue(DeviceKernel kernel, const int work_size, void *args[]) override; +}; + +CCL_NAMESPACE_END + +#endif /* WITH_OPTIX */ diff --git a/intern/cycles/device/optix/util.h b/intern/cycles/device/optix/util.h new file mode 100644 index 00000000000..34ae5bb5609 --- /dev/null +++ b/intern/cycles/device/optix/util.h @@ -0,0 +1,45 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_OPTIX + +# include "device/cuda/util.h" + +# ifdef WITH_CUDA_DYNLOAD +# include +// Do not use CUDA SDK headers when using CUEW +# define OPTIX_DONT_INCLUDE_CUDA +# endif + +# include + +/* Utility for checking return values of OptiX function calls. */ +# define optix_device_assert(optix_device, stmt) \ + { \ + OptixResult result = stmt; \ + if (result != OPTIX_SUCCESS) { \ + const char *name = optixGetErrorName(result); \ + optix_device->set_error( \ + string_printf("%s in %s (%s:%d)", name, #stmt, __FILE__, __LINE__)); \ + } \ + } \ + (void)0 + +# define optix_assert(stmt) optix_device_assert(this, stmt) + +#endif /* WITH_OPTIX */ diff --git a/intern/cycles/graph/node.cpp b/intern/cycles/graph/node.cpp index 57f25283f85..8294e716ebe 100644 --- a/intern/cycles/graph/node.cpp +++ b/intern/cycles/graph/node.cpp @@ -814,7 +814,7 @@ bool Node::socket_is_modified(const SocketType &input) const return (socket_modified & input.modified_flag_bit) != 0; } -bool Node::is_modified() +bool Node::is_modified() const { return socket_modified != 0; } diff --git a/intern/cycles/graph/node.h b/intern/cycles/graph/node.h index aa365baeccd..8f27a82d37b 100644 --- a/intern/cycles/graph/node.h +++ b/intern/cycles/graph/node.h @@ -16,6 +16,8 @@ #pragma once +#include + #include "graph/node_type.h" #include "util/util_array.h" @@ -34,7 +36,10 @@ struct Transform; #define NODE_SOCKET_API_BASE_METHODS(type_, name, string_name) \ const SocketType *get_##name##_socket() const \ { \ - static const SocketType *socket = type->find_input(ustring(string_name)); \ + /* Explicitly cast to base class to use `Node::type` even if the derived class defines \ + * `type`. */ \ + const Node *self_node = this; \ + static const SocketType *socket = self_node->type->find_input(ustring(string_name)); \ return socket; \ } \ bool name##_is_modified() const \ @@ -111,6 +116,15 @@ struct Node { void set(const SocketType &input, const Transform &value); void set(const SocketType &input, Node *value); + /* Implicitly cast enums and enum classes to integer, which matches an internal way of how + * enumerator values are stored and accessed in a generic API. */ + template> * = nullptr> + void set(const SocketType &input, const ValueType &value) + { + static_assert(sizeof(ValueType) <= sizeof(int), "Enumerator type should fit int"); + set(input, static_cast(value)); + } + /* set array values. the memory from the input array will taken over * by the node and the input array will be empty after return */ void set(const SocketType &input, array &value); @@ -164,7 +178,7 @@ struct Node { bool socket_is_modified(const SocketType &input) const; - bool is_modified(); + bool is_modified() const; void tag_modified(); void clear_modified(); diff --git a/intern/cycles/integrator/CMakeLists.txt b/intern/cycles/integrator/CMakeLists.txt new file mode 100644 index 00000000000..bfabd35d7c3 --- /dev/null +++ b/intern/cycles/integrator/CMakeLists.txt @@ -0,0 +1,76 @@ +# Copyright 2011-2021 Blender Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(INC + .. +) + +set(SRC + adaptive_sampling.cpp + denoiser.cpp + denoiser_device.cpp + denoiser_oidn.cpp + denoiser_optix.cpp + path_trace.cpp + tile.cpp + pass_accessor.cpp + pass_accessor_cpu.cpp + pass_accessor_gpu.cpp + path_trace_work.cpp + path_trace_work_cpu.cpp + path_trace_work_gpu.cpp + render_scheduler.cpp + shader_eval.cpp + work_balancer.cpp + work_tile_scheduler.cpp +) + +set(SRC_HEADERS + adaptive_sampling.h + denoiser.h + denoiser_device.h + denoiser_oidn.h + denoiser_optix.h + path_trace.h + tile.h + pass_accessor.h + pass_accessor_cpu.h + pass_accessor_gpu.h + path_trace_work.h + path_trace_work_cpu.h + path_trace_work_gpu.h + render_scheduler.h + shader_eval.h + work_balancer.h + work_tile_scheduler.h +) + +set(LIB + # NOTE: Is required for RenderBuffers access. Might consider moving files around a bit to + # avoid such cyclic dependency. + cycles_render + + cycles_util +) + +if(WITH_OPENIMAGEDENOISE) + list(APPEND LIB + ${OPENIMAGEDENOISE_LIBRARIES} + ) +endif() + +include_directories(${INC}) +include_directories(SYSTEM ${INC_SYS}) + +cycles_add_library(cycles_integrator "${LIB}" ${SRC} ${SRC_HEADERS}) diff --git a/intern/cycles/integrator/adaptive_sampling.cpp b/intern/cycles/integrator/adaptive_sampling.cpp new file mode 100644 index 00000000000..23fbcfea5c2 --- /dev/null +++ b/intern/cycles/integrator/adaptive_sampling.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/adaptive_sampling.h" + +#include "util/util_math.h" + +CCL_NAMESPACE_BEGIN + +AdaptiveSampling::AdaptiveSampling() +{ +} + +int AdaptiveSampling::align_samples(int start_sample, int num_samples) const +{ + if (!use) { + return num_samples; + } + + /* + * The naive implementation goes as following: + * + * int count = 1; + * while (!need_filter(start_sample + count - 1) && count < num_samples) { + * ++count; + * } + * return count; + */ + + /* 0-based sample index at which first filtering will happen. */ + const int first_filter_sample = (min_samples + 1) | (adaptive_step - 1); + + /* Allow as many samples as possible until the first filter sample. */ + if (start_sample + num_samples <= first_filter_sample) { + return num_samples; + } + + const int next_filter_sample = max(first_filter_sample, start_sample | (adaptive_step - 1)); + + const int num_samples_until_filter = next_filter_sample - start_sample + 1; + + return min(num_samples_until_filter, num_samples); +} + +bool AdaptiveSampling::need_filter(int sample) const +{ + if (!use) { + return false; + } + + if (sample <= min_samples) { + return false; + } + + return (sample & (adaptive_step - 1)) == (adaptive_step - 1); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/adaptive_sampling.h b/intern/cycles/integrator/adaptive_sampling.h new file mode 100644 index 00000000000..d98edd9894c --- /dev/null +++ b/intern/cycles/integrator/adaptive_sampling.h @@ -0,0 +1,55 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +class AdaptiveSampling { + public: + AdaptiveSampling(); + + /* Align number of samples so that they align with the adaptive filtering. + * + * Returns the new value for the `num_samples` so that after rendering so many samples on top + * of `start_sample` filtering is required. + * + * The alignment happens in a way that allows to render as many samples as possible without + * missing any filtering point. This means that the result is "clamped" by the nearest sample + * at which filtering is needed. This is part of mechanism which ensures that all devices will + * perform same exact filtering and adaptive sampling, regardless of their performance. + * + * `start_sample` is the 0-based index of sample. + * + * NOTE: The start sample is included into the number of samples to render. This means that + * if the number of samples is 1, then the path tracer will render samples [align_samples], + * if the number of samples is 2, then the path tracer will render samples [align_samples, + * align_samples + 1] and so on. */ + int align_samples(int start_sample, int num_samples) const; + + /* Check whether adaptive sampling filter should happen at this sample. + * Returns false if the adaptive sampling is not use. + * + * `sample` is the 0-based index of sample. */ + bool need_filter(int sample) const; + + bool use = false; + int adaptive_step = 0; + int min_samples = 0; + float threshold = 0.0f; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp new file mode 100644 index 00000000000..598bbd497a5 --- /dev/null +++ b/intern/cycles/integrator/denoiser.cpp @@ -0,0 +1,204 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/denoiser.h" + +#include "device/device.h" +#include "integrator/denoiser_oidn.h" +#include "integrator/denoiser_optix.h" +#include "render/buffers.h" +#include "util/util_logging.h" +#include "util/util_progress.h" + +CCL_NAMESPACE_BEGIN + +unique_ptr Denoiser::create(Device *path_trace_device, const DenoiseParams ¶ms) +{ + DCHECK(params.use); + + switch (params.type) { + case DENOISER_OPTIX: + return make_unique(path_trace_device, params); + + case DENOISER_OPENIMAGEDENOISE: + return make_unique(path_trace_device, params); + + case DENOISER_NUM: + case DENOISER_NONE: + case DENOISER_ALL: + /* pass */ + break; + } + + LOG(FATAL) << "Unhandled denoiser type " << params.type << ", should never happen."; + + return nullptr; +} + +Denoiser::Denoiser(Device *path_trace_device, const DenoiseParams ¶ms) + : path_trace_device_(path_trace_device), params_(params) +{ + DCHECK(params.use); +} + +void Denoiser::set_params(const DenoiseParams ¶ms) +{ + DCHECK_EQ(params.type, params_.type); + + if (params.type == params_.type) { + params_ = params; + } + else { + LOG(ERROR) << "Attempt to change denoiser type."; + } +} + +const DenoiseParams &Denoiser::get_params() const +{ + return params_; +} + +bool Denoiser::load_kernels(Progress *progress) +{ + const Device *denoiser_device = ensure_denoiser_device(progress); + + if (!denoiser_device) { + path_trace_device_->set_error("No device available to denoise on"); + return false; + } + + VLOG(3) << "Will denoise on " << denoiser_device->info.description << " (" + << denoiser_device->info.id << ")"; + + return true; +} + +Device *Denoiser::get_denoiser_device() const +{ + return denoiser_device_; +} + +/* Check whether given device is single (not a MultiDevice) and supports requested denoiser. */ +static bool is_single_supported_device(Device *device, DenoiserType type) +{ + if (device->info.type == DEVICE_MULTI) { + /* Assume multi-device is never created with a single sub-device. + * If one requests such configuration it should be checked on the session level. */ + return false; + } + + if (!device->info.multi_devices.empty()) { + /* Some configurations will use multi_devices, but keep the type of an individual device. + * This does simplify checks for homogenous setups, but here we really need a single device. */ + return false; + } + + /* Check the denoiser type is supported. */ + return (device->info.denoisers & type); +} + +/* Find best suitable device to perform denoiser on. Will iterate over possible sub-devices of + * multi-device. + * + * If there is no device available which supports given denoiser type nullptr is returned. */ +static Device *find_best_device(Device *device, DenoiserType type) +{ + Device *best_device = nullptr; + + device->foreach_device([&](Device *sub_device) { + if ((sub_device->info.denoisers & type) == 0) { + return; + } + if (!best_device) { + best_device = sub_device; + } + else { + /* TODO(sergey): Choose fastest device from available ones. Taking into account performance + * of the device and data transfer cost. */ + } + }); + + return best_device; +} + +static unique_ptr create_denoiser_device(Device *path_trace_device, + const uint device_type_mask) +{ + const vector device_infos = Device::available_devices(device_type_mask); + if (device_infos.empty()) { + return nullptr; + } + + /* TODO(sergey): Use one of the already configured devices, so that OptiX denoising can happen on + * a physical CUDA device which is already used for rendering. */ + + /* TODO(sergey): Choose fastest device for denoising. */ + + const DeviceInfo denoiser_device_info = device_infos.front(); + + unique_ptr denoiser_device( + Device::create(denoiser_device_info, path_trace_device->stats, path_trace_device->profiler)); + + if (!denoiser_device) { + return nullptr; + } + + if (denoiser_device->have_error()) { + return nullptr; + } + + /* Only need denoising feature, everything else is unused. */ + if (!denoiser_device->load_kernels(KERNEL_FEATURE_DENOISING)) { + return nullptr; + } + + return denoiser_device; +} + +Device *Denoiser::ensure_denoiser_device(Progress *progress) +{ + /* The best device has been found already, avoid sequential lookups. + * Additionally, avoid device re-creation if it has failed once. */ + if (denoiser_device_ || device_creation_attempted_) { + return denoiser_device_; + } + + /* Simple case: rendering happens on a single device which also supports denoiser. */ + if (is_single_supported_device(path_trace_device_, params_.type)) { + denoiser_device_ = path_trace_device_; + return denoiser_device_; + } + + /* Find best device from the ones which are already used for rendering. */ + denoiser_device_ = find_best_device(path_trace_device_, params_.type); + if (denoiser_device_) { + return denoiser_device_; + } + + if (progress) { + progress->set_status("Loading denoising kernels (may take a few minutes the first time)"); + } + + device_creation_attempted_ = true; + + const uint device_type_mask = get_device_type_mask(); + local_denoiser_device_ = create_denoiser_device(path_trace_device_, device_type_mask); + denoiser_device_ = local_denoiser_device_.get(); + + return denoiser_device_; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser.h b/intern/cycles/integrator/denoiser.h new file mode 100644 index 00000000000..3101b45e31b --- /dev/null +++ b/intern/cycles/integrator/denoiser.h @@ -0,0 +1,135 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +/* TODO(sergey): The integrator folder might not be the best. Is easy to move files around if the + * better place is figured out. */ + +#include "device/device.h" +#include "device/device_denoise.h" +#include "util/util_function.h" +#include "util/util_unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +class BufferParams; +class Device; +class RenderBuffers; +class Progress; + +/* Implementation of a specific denoising algorithm. + * + * This class takes care of breaking down denosiing algorithm into a series of device calls or to + * calls of an external API to denoise given input. + * + * TODO(sergey): Are we better with device or a queue here? */ +class Denoiser { + public: + /* Create denoiser for the given path trace device. + * + * Notes: + * - The denoiser must be configured. This means that `params.use` must be true. + * This is checked in debug builds. + * - The device might be MultiDevice. */ + static unique_ptr create(Device *path_trace_device, const DenoiseParams ¶ms); + + virtual ~Denoiser() = default; + + void set_params(const DenoiseParams ¶ms); + const DenoiseParams &get_params() const; + + /* Create devices and load kernels needed for denoising. + * The progress is used to communicate state when kenrels actually needs to be loaded. + * + * NOTE: The `progress` is an optional argument, can be nullptr. */ + virtual bool load_kernels(Progress *progress); + + /* Denoise the entire buffer. + * + * Buffer parameters denotes an effective parameters used during rendering. It could be + * a lower resolution render into a bigger allocated buffer, which is used in viewport during + * navigation and non-unit pixel size. Use that instead of render_buffers->params. + * + * The buffer might be copming from a "foreign" device from what this denoise is created for. + * This means that in general case the denoiser will make sure the input data is available on + * the denoiser device, perform denoising, and put data back to the device where the buffer + * came from. + * + * The `num_samples` corresponds to the number of samples in the render buffers. It is used + * to scale buffers down to the "final" value in algorithms which don't do automatic exposure, + * or which needs "final" value for data passes. + * + * The `allow_inplace_modification` means that the denoiser is allowed to do in-place + * modification of the input passes (scaling them down i.e.). This will lower the memory + * footprint of the denoiser but will make input passes "invalid" (from path tracer) point of + * view. + * + * Returns true when all passes are denoised. Will return false if there is a denoiser error (for + * example, caused by misconfigured denoiser) or when user requested to cancel rendering. */ + virtual bool denoise_buffer(const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + bool allow_inplace_modification) = 0; + + /* Get a device which is used to perform actual denoising. + * + * Notes: + * + * - The device is lazily initialized via `load_kernels()`, so it will be nullptr until then, + * + * - The device can be different from the path tracing device. This happens, for example, when + * using OptiX denoiser and rendering on CPU. + * + * - No threading safety is ensured in this call. This means, that it is up to caller to ensure + * that there is no threadingconflict between denoising task lazily initializing the device and + * access to this device happen. */ + Device *get_denoiser_device() const; + + function is_cancelled_cb; + + bool is_cancelled() const + { + if (!is_cancelled_cb) { + return false; + } + return is_cancelled_cb(); + } + + protected: + Denoiser(Device *path_trace_device, const DenoiseParams ¶ms); + + /* Make sure denoising device is initialized. */ + virtual Device *ensure_denoiser_device(Progress *progress); + + /* Get device type mask which is used to filter available devices when new device needs to be + * created. */ + virtual uint get_device_type_mask() const = 0; + + Device *path_trace_device_; + DenoiseParams params_; + + /* Cached pointer to the device on which denoising will happen. + * Used to avoid lookup of a device for every denoising request. */ + Device *denoiser_device_ = nullptr; + + /* Denoiser device which was created to perform denoising in the case the none of the rendering + * devices are capable of denoising. */ + unique_ptr local_denoiser_device_; + bool device_creation_attempted_ = false; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_device.cpp b/intern/cycles/integrator/denoiser_device.cpp new file mode 100644 index 00000000000..8088cfd7800 --- /dev/null +++ b/intern/cycles/integrator/denoiser_device.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/denoiser_device.h" + +#include "device/device.h" +#include "device/device_denoise.h" +#include "device/device_memory.h" +#include "device/device_queue.h" +#include "render/buffers.h" +#include "util/util_logging.h" +#include "util/util_progress.h" + +CCL_NAMESPACE_BEGIN + +DeviceDenoiser::DeviceDenoiser(Device *path_trace_device, const DenoiseParams ¶ms) + : Denoiser(path_trace_device, params) +{ +} + +DeviceDenoiser::~DeviceDenoiser() +{ + /* Explicit implementation, to allow forward declaration of Device in the header. */ +} + +bool DeviceDenoiser::denoise_buffer(const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + bool allow_inplace_modification) +{ + Device *denoiser_device = get_denoiser_device(); + if (!denoiser_device) { + return false; + } + + DeviceDenoiseTask task; + task.params = params_; + task.num_samples = num_samples; + task.buffer_params = buffer_params; + task.allow_inplace_modification = allow_inplace_modification; + + RenderBuffers local_render_buffers(denoiser_device); + bool local_buffer_used = false; + + if (denoiser_device == render_buffers->buffer.device) { + /* The device can access an existing buffer pointer. */ + local_buffer_used = false; + task.render_buffers = render_buffers; + } + else { + VLOG(3) << "Creating temporary buffer on denoiser device."; + + DeviceQueue *queue = denoiser_device->get_denoise_queue(); + + /* Create buffer which is available by the device used by denoiser. */ + + /* TODO(sergey): Optimize data transfers. For example, only copy denoising related passes, + * ignoring other light ad data passes. */ + + local_buffer_used = true; + + render_buffers->copy_from_device(); + + local_render_buffers.reset(buffer_params); + + /* NOTE: The local buffer is allocated for an exact size of the effective render size, while + * the input render buffer is allcoated for the lowest resolution divider possible. So it is + * important to only copy actually needed part of the input buffer. */ + memcpy(local_render_buffers.buffer.data(), + render_buffers->buffer.data(), + sizeof(float) * local_render_buffers.buffer.size()); + + queue->copy_to_device(local_render_buffers.buffer); + + task.render_buffers = &local_render_buffers; + task.allow_inplace_modification = true; + } + + const bool denoise_result = denoiser_device->denoise_buffer(task); + + if (local_buffer_used) { + local_render_buffers.copy_from_device(); + + render_buffers_host_copy_denoised( + render_buffers, buffer_params, &local_render_buffers, local_render_buffers.params); + + render_buffers->copy_to_device(); + } + + return denoise_result; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_device.h b/intern/cycles/integrator/denoiser_device.h new file mode 100644 index 00000000000..0fd934dba79 --- /dev/null +++ b/intern/cycles/integrator/denoiser_device.h @@ -0,0 +1,40 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/denoiser.h" +#include "util/util_unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +/* Denoiser which uses device-specific denoising implementation, such as OptiX denoiser which are + * implemented as a part of a driver of specific device. + * + * This implementation makes sure the to-be-denoised buffer is available on the denoising device + * and invoke denoising kernel via device API. */ +class DeviceDenoiser : public Denoiser { + public: + DeviceDenoiser(Device *path_trace_device, const DenoiseParams ¶ms); + ~DeviceDenoiser(); + + virtual bool denoise_buffer(const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + bool allow_inplace_modification) override; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp new file mode 100644 index 00000000000..1b5a012ec87 --- /dev/null +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -0,0 +1,628 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/denoiser_oidn.h" + +#include + +#include "device/device.h" +#include "device/device_queue.h" +#include "integrator/pass_accessor_cpu.h" +#include "render/buffers.h" +#include "util/util_array.h" +#include "util/util_logging.h" +#include "util/util_openimagedenoise.h" + +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/kernel.h" + +CCL_NAMESPACE_BEGIN + +thread_mutex OIDNDenoiser::mutex_; + +OIDNDenoiser::OIDNDenoiser(Device *path_trace_device, const DenoiseParams ¶ms) + : Denoiser(path_trace_device, params) +{ + DCHECK_EQ(params.type, DENOISER_OPENIMAGEDENOISE); + + DCHECK(openimagedenoise_supported()) << "OpenImageDenoiser is not supported on this platform."; +} + +#ifdef WITH_OPENIMAGEDENOISE +static bool oidn_progress_monitor_function(void *user_ptr, double /*n*/) +{ + OIDNDenoiser *oidn_denoiser = reinterpret_cast(user_ptr); + return !oidn_denoiser->is_cancelled(); +} +#endif + +#ifdef WITH_OPENIMAGEDENOISE + +class OIDNPass { + public: + OIDNPass() = default; + + OIDNPass(const BufferParams &buffer_params, + const char *name, + PassType type, + PassMode mode = PassMode::NOISY) + : name(name), type(type), mode(mode) + { + offset = buffer_params.get_pass_offset(type, mode); + need_scale = (type == PASS_DENOISING_ALBEDO || type == PASS_DENOISING_NORMAL); + + const PassInfo pass_info = Pass::get_info(type); + num_components = pass_info.num_components; + use_compositing = pass_info.use_compositing; + use_denoising_albedo = pass_info.use_denoising_albedo; + } + + inline operator bool() const + { + return name[0] != '\0'; + } + + /* Name of an image which will be passed to the OIDN library. + * Should be one of the following: color, albedo, normal, output. + * The albedo and normal images are optional. */ + const char *name = ""; + + PassType type = PASS_NONE; + PassMode mode = PassMode::NOISY; + int num_components = -1; + bool use_compositing = false; + bool use_denoising_albedo = true; + + /* Offset of beginning of this pass in the render buffers. */ + int offset = -1; + + /* Denotes whether the data is to be scaled down with the number of passes. + * Is required for albedo and normal passes. The color pass OIDN will perform auto-exposure, so + * scaling is not needed for the color pass unless adaptive sampling is used. + * + * NOTE: Do not scale the outout pass, as that requires to be a pointer in the original buffer. + * All the scaling on the output needed for integration with adaptive sampling will happen + * outside of generic pass handling. */ + bool need_scale = false; + + /* The content of the pass has been pre-filtered. */ + bool is_filtered = false; + + /* For the scaled passes, the data which holds values of scaled pixels. */ + array scaled_buffer; +}; + +class OIDNDenoiseContext { + public: + OIDNDenoiseContext(OIDNDenoiser *denoiser, + const DenoiseParams &denoise_params, + const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + const bool allow_inplace_modification) + : denoiser_(denoiser), + denoise_params_(denoise_params), + buffer_params_(buffer_params), + render_buffers_(render_buffers), + num_samples_(num_samples), + allow_inplace_modification_(allow_inplace_modification), + pass_sample_count_(buffer_params_.get_pass_offset(PASS_SAMPLE_COUNT)) + { + if (denoise_params_.use_pass_albedo) { + oidn_albedo_pass_ = OIDNPass(buffer_params_, "albedo", PASS_DENOISING_ALBEDO); + } + + if (denoise_params_.use_pass_normal) { + oidn_normal_pass_ = OIDNPass(buffer_params_, "normal", PASS_DENOISING_NORMAL); + } + } + + bool need_denoising() const + { + if (buffer_params_.width == 0 && buffer_params_.height == 0) { + return false; + } + + return true; + } + + /* Make the guiding passes available by a sequential denoising of various passes. */ + void read_guiding_passes() + { + read_guiding_pass(oidn_albedo_pass_); + read_guiding_pass(oidn_normal_pass_); + } + + void denoise_pass(const PassType pass_type) + { + OIDNPass oidn_color_pass(buffer_params_, "color", pass_type); + if (oidn_color_pass.offset == PASS_UNUSED) { + return; + } + + if (oidn_color_pass.use_denoising_albedo) { + if (albedo_replaced_with_fake_) { + LOG(ERROR) << "Pass which requires albedo is denoised after fake albedo has been set."; + return; + } + } + + OIDNPass oidn_output_pass(buffer_params_, "output", pass_type, PassMode::DENOISED); + if (oidn_output_pass.offset == PASS_UNUSED) { + LOG(DFATAL) << "Missing denoised pass " << pass_type_as_string(pass_type); + return; + } + + OIDNPass oidn_color_access_pass = read_input_pass(oidn_color_pass, oidn_output_pass); + + oidn::DeviceRef oidn_device = oidn::newDevice(); + oidn_device.commit(); + + /* Create a filter for denoising a beauty (color) image using prefiltered auxiliary images too. + */ + oidn::FilterRef oidn_filter = oidn_device.newFilter("RT"); + set_input_pass(oidn_filter, oidn_color_access_pass); + set_guiding_passes(oidn_filter, oidn_color_pass); + set_output_pass(oidn_filter, oidn_output_pass); + oidn_filter.setProgressMonitorFunction(oidn_progress_monitor_function, denoiser_); + oidn_filter.set("hdr", true); + oidn_filter.set("srgb", false); + if (denoise_params_.prefilter == DENOISER_PREFILTER_NONE || + denoise_params_.prefilter == DENOISER_PREFILTER_ACCURATE) { + oidn_filter.set("cleanAux", true); + } + oidn_filter.commit(); + + filter_guiding_pass_if_needed(oidn_device, oidn_albedo_pass_); + filter_guiding_pass_if_needed(oidn_device, oidn_normal_pass_); + + /* Filter the beauty image. */ + oidn_filter.execute(); + + /* Check for errors. */ + const char *error_message; + const oidn::Error error = oidn_device.getError(error_message); + if (error != oidn::Error::None && error != oidn::Error::Cancelled) { + LOG(ERROR) << "OpenImageDenoise error: " << error_message; + } + + postprocess_output(oidn_color_pass, oidn_output_pass); + } + + protected: + void filter_guiding_pass_if_needed(oidn::DeviceRef &oidn_device, OIDNPass &oidn_pass) + { + if (denoise_params_.prefilter != DENOISER_PREFILTER_ACCURATE || !oidn_pass || + oidn_pass.is_filtered) { + return; + } + + oidn::FilterRef oidn_filter = oidn_device.newFilter("RT"); + set_pass(oidn_filter, oidn_pass); + set_output_pass(oidn_filter, oidn_pass); + oidn_filter.commit(); + oidn_filter.execute(); + + oidn_pass.is_filtered = true; + } + + /* Make pixels of a guiding pass available by the denoiser. */ + void read_guiding_pass(OIDNPass &oidn_pass) + { + if (!oidn_pass) { + return; + } + + DCHECK(!oidn_pass.use_compositing); + + if (denoise_params_.prefilter != DENOISER_PREFILTER_ACCURATE && + !is_pass_scale_needed(oidn_pass)) { + /* Pass data is available as-is from the render buffers. */ + return; + } + + if (allow_inplace_modification_) { + scale_pass_in_render_buffers(oidn_pass); + return; + } + + read_pass_pixels_into_buffer(oidn_pass); + } + + /* Special reader of the input pass. + * To save memory it will read pixels into the output, and let the denoiser to perform an + * in-place operation. */ + OIDNPass read_input_pass(OIDNPass &oidn_input_pass, const OIDNPass &oidn_output_pass) + { + const bool use_compositing = oidn_input_pass.use_compositing; + + /* Simple case: no compositing is involved, no scaling is needed. + * The pass pixels will be referenced as-is, without extra processing. */ + if (!use_compositing && !is_pass_scale_needed(oidn_input_pass)) { + return oidn_input_pass; + } + + float *buffer_data = render_buffers_->buffer.data(); + float *pass_data = buffer_data + oidn_output_pass.offset; + + PassAccessor::Destination destination(pass_data, 3); + destination.pixel_stride = buffer_params_.pass_stride; + + read_pass_pixels(oidn_input_pass, destination); + + OIDNPass oidn_input_pass_at_output = oidn_input_pass; + oidn_input_pass_at_output.offset = oidn_output_pass.offset; + + return oidn_input_pass_at_output; + } + + /* Read pass pixels using PassAccessor into the given destination. */ + void read_pass_pixels(const OIDNPass &oidn_pass, const PassAccessor::Destination &destination) + { + PassAccessor::PassAccessInfo pass_access_info; + pass_access_info.type = oidn_pass.type; + pass_access_info.mode = oidn_pass.mode; + pass_access_info.offset = oidn_pass.offset; + + /* Denoiser operates on passes which are used to calculate the approximation, and is never used + * on the approximation. The latter is not even possible because OIDN does not support + * denoising of semi-transparent pixels. */ + pass_access_info.use_approximate_shadow_catcher = false; + pass_access_info.use_approximate_shadow_catcher_background = false; + pass_access_info.show_active_pixels = false; + + /* OIDN will perform an auto-exposure, so it is not required to know exact exposure configured + * by users. What is important is to use same exposure for read and write access of the pass + * pixels. */ + const PassAccessorCPU pass_accessor(pass_access_info, 1.0f, num_samples_); + + pass_accessor.get_render_tile_pixels(render_buffers_, buffer_params_, destination); + } + + /* Read pass pixels using PassAccessor into a temporary buffer which is owned by the pass.. */ + void read_pass_pixels_into_buffer(OIDNPass &oidn_pass) + { + VLOG(3) << "Allocating temporary buffer for pass " << oidn_pass.name << " (" + << pass_type_as_string(oidn_pass.type) << ")"; + + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + + array &scaled_buffer = oidn_pass.scaled_buffer; + scaled_buffer.resize(width * height * 3); + + const PassAccessor::Destination destination(scaled_buffer.data(), 3); + + read_pass_pixels(oidn_pass, destination); + } + + /* Set OIDN image to reference pixels from the given render buffer pass. + * No transform to the pixels is done, no additional memory is used. */ + void set_pass_referenced(oidn::FilterRef &oidn_filter, + const char *name, + const OIDNPass &oidn_pass) + { + const int64_t x = buffer_params_.full_x; + const int64_t y = buffer_params_.full_y; + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + const int64_t offset = buffer_params_.offset; + const int64_t stride = buffer_params_.stride; + const int64_t pass_stride = buffer_params_.pass_stride; + + const int64_t pixel_index = offset + x + y * stride; + const int64_t buffer_offset = pixel_index * pass_stride; + + float *buffer_data = render_buffers_->buffer.data(); + + oidn_filter.setImage(name, + buffer_data + buffer_offset + oidn_pass.offset, + oidn::Format::Float3, + width, + height, + 0, + pass_stride * sizeof(float), + stride * pass_stride * sizeof(float)); + } + + void set_pass_from_buffer(oidn::FilterRef &oidn_filter, const char *name, OIDNPass &oidn_pass) + { + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + + oidn_filter.setImage( + name, oidn_pass.scaled_buffer.data(), oidn::Format::Float3, width, height, 0, 0, 0); + } + + void set_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass) + { + set_pass(oidn_filter, oidn_pass.name, oidn_pass); + } + void set_pass(oidn::FilterRef &oidn_filter, const char *name, OIDNPass &oidn_pass) + { + if (oidn_pass.scaled_buffer.empty()) { + set_pass_referenced(oidn_filter, name, oidn_pass); + } + else { + set_pass_from_buffer(oidn_filter, name, oidn_pass); + } + } + + void set_input_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass) + { + set_pass_referenced(oidn_filter, oidn_pass.name, oidn_pass); + } + + void set_guiding_passes(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass) + { + if (oidn_albedo_pass_) { + if (oidn_pass.use_denoising_albedo) { + set_pass(oidn_filter, oidn_albedo_pass_); + } + else { + /* NOTE: OpenImageDenoise library implicitly expects albedo pass when normal pass has been + * provided. */ + set_fake_albedo_pass(oidn_filter); + } + } + + if (oidn_normal_pass_) { + set_pass(oidn_filter, oidn_normal_pass_); + } + } + + void set_fake_albedo_pass(oidn::FilterRef &oidn_filter) + { + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + + if (!albedo_replaced_with_fake_) { + const int64_t num_pixel_components = width * height * 3; + oidn_albedo_pass_.scaled_buffer.resize(num_pixel_components); + + for (int i = 0; i < num_pixel_components; ++i) { + oidn_albedo_pass_.scaled_buffer[i] = 0.5f; + } + + albedo_replaced_with_fake_ = true; + } + + set_pass(oidn_filter, oidn_albedo_pass_); + } + + void set_output_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass) + { + set_pass(oidn_filter, "output", oidn_pass); + } + + /* Scale output pass to match adaptive sampling per-pixel scale, as well as bring alpha channel + * back. */ + void postprocess_output(const OIDNPass &oidn_input_pass, const OIDNPass &oidn_output_pass) + { + kernel_assert(oidn_input_pass.num_components == oidn_output_pass.num_components); + + const int64_t x = buffer_params_.full_x; + const int64_t y = buffer_params_.full_y; + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + const int64_t offset = buffer_params_.offset; + const int64_t stride = buffer_params_.stride; + const int64_t pass_stride = buffer_params_.pass_stride; + const int64_t row_stride = stride * pass_stride; + + const int64_t pixel_offset = offset + x + y * stride; + const int64_t buffer_offset = (pixel_offset * pass_stride); + + float *buffer_data = render_buffers_->buffer.data(); + + const bool has_pass_sample_count = (pass_sample_count_ != PASS_UNUSED); + const bool need_scale = has_pass_sample_count || oidn_input_pass.use_compositing; + + for (int y = 0; y < height; ++y) { + float *buffer_row = buffer_data + buffer_offset + y * row_stride; + for (int x = 0; x < width; ++x) { + float *buffer_pixel = buffer_row + x * pass_stride; + float *denoised_pixel = buffer_pixel + oidn_output_pass.offset; + + if (need_scale) { + const float pixel_scale = has_pass_sample_count ? + __float_as_uint(buffer_pixel[pass_sample_count_]) : + num_samples_; + + denoised_pixel[0] = denoised_pixel[0] * pixel_scale; + denoised_pixel[1] = denoised_pixel[1] * pixel_scale; + denoised_pixel[2] = denoised_pixel[2] * pixel_scale; + } + + if (oidn_output_pass.num_components == 3) { + /* Pass without alpha channel. */ + } + else if (!oidn_input_pass.use_compositing) { + /* Currently compositing passes are either 3-component (derived by dividing light passes) + * or do not have transparency (shadow catcher). Implicitly rely on this logic, as it + * simplifies logic and avoids extra memory allocation. */ + const float *noisy_pixel = buffer_pixel + oidn_input_pass.offset; + denoised_pixel[3] = noisy_pixel[3]; + } + else { + /* Assigning to zero since this is a default alpha value for 3-component passes, and it + * is an opaque pixel for 4 component passes. */ + denoised_pixel[3] = 0; + } + } + } + } + + bool is_pass_scale_needed(OIDNPass &oidn_pass) const + { + if (pass_sample_count_ != PASS_UNUSED) { + /* With adaptive sampling pixels will have different number of samples in them, so need to + * always scale the pass to make pixels uniformly sampled. */ + return true; + } + + if (!oidn_pass.need_scale) { + return false; + } + + if (num_samples_ == 1) { + /* If the avoid scaling if there is only one sample, to save up time (so we dont divide + * buffer by 1). */ + return false; + } + + return true; + } + + void scale_pass_in_render_buffers(OIDNPass &oidn_pass) + { + const int64_t x = buffer_params_.full_x; + const int64_t y = buffer_params_.full_y; + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + const int64_t offset = buffer_params_.offset; + const int64_t stride = buffer_params_.stride; + const int64_t pass_stride = buffer_params_.pass_stride; + const int64_t row_stride = stride * pass_stride; + + const int64_t pixel_offset = offset + x + y * stride; + const int64_t buffer_offset = (pixel_offset * pass_stride); + + float *buffer_data = render_buffers_->buffer.data(); + + const bool has_pass_sample_count = (pass_sample_count_ != PASS_UNUSED); + + for (int y = 0; y < height; ++y) { + float *buffer_row = buffer_data + buffer_offset + y * row_stride; + for (int x = 0; x < width; ++x) { + float *buffer_pixel = buffer_row + x * pass_stride; + float *pass_pixel = buffer_pixel + oidn_pass.offset; + + const float pixel_scale = 1.0f / (has_pass_sample_count ? + __float_as_uint(buffer_pixel[pass_sample_count_]) : + num_samples_); + + pass_pixel[0] = pass_pixel[0] * pixel_scale; + pass_pixel[1] = pass_pixel[1] * pixel_scale; + pass_pixel[2] = pass_pixel[2] * pixel_scale; + } + } + } + + OIDNDenoiser *denoiser_ = nullptr; + + const DenoiseParams &denoise_params_; + const BufferParams &buffer_params_; + RenderBuffers *render_buffers_ = nullptr; + int num_samples_ = 0; + bool allow_inplace_modification_ = false; + int pass_sample_count_ = PASS_UNUSED; + + /* Optional albedo and normal passes, reused by denoising of different pass types. */ + OIDNPass oidn_albedo_pass_; + OIDNPass oidn_normal_pass_; + + /* For passes which don't need albedo channel for denoising we replace the actual albedo with + * the (0.5, 0.5, 0.5). This flag indicates that the real albedo pass has been replaced with + * the fake values and denoising of passes which do need albedo can no longer happen. */ + bool albedo_replaced_with_fake_ = false; +}; +#endif + +static unique_ptr create_device_queue(const RenderBuffers *render_buffers) +{ + Device *device = render_buffers->buffer.device; + if (device->info.has_gpu_queue) { + return device->gpu_queue_create(); + } + return nullptr; +} + +static void copy_render_buffers_from_device(unique_ptr &queue, + RenderBuffers *render_buffers) +{ + if (queue) { + queue->copy_from_device(render_buffers->buffer); + queue->synchronize(); + } + else { + render_buffers->copy_from_device(); + } +} + +static void copy_render_buffers_to_device(unique_ptr &queue, + RenderBuffers *render_buffers) +{ + if (queue) { + queue->copy_to_device(render_buffers->buffer); + queue->synchronize(); + } + else { + render_buffers->copy_to_device(); + } +} + +bool OIDNDenoiser::denoise_buffer(const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + bool allow_inplace_modification) +{ + thread_scoped_lock lock(mutex_); + + /* Make sure the host-side data is available for denoising. */ + unique_ptr queue = create_device_queue(render_buffers); + copy_render_buffers_from_device(queue, render_buffers); + +#ifdef WITH_OPENIMAGEDENOISE + OIDNDenoiseContext context( + this, params_, buffer_params, render_buffers, num_samples, allow_inplace_modification); + + if (context.need_denoising()) { + context.read_guiding_passes(); + + const std::array passes = { + {/* Passes which will use real albedo when it is available. */ + PASS_COMBINED, + PASS_SHADOW_CATCHER_MATTE, + + /* Passes which do not need albedo and hence if real is present it needs to become fake. + */ + PASS_SHADOW_CATCHER}}; + + for (const PassType pass_type : passes) { + context.denoise_pass(pass_type); + if (is_cancelled()) { + return false; + } + } + + /* TODO: It may be possible to avoid this copy, but we have to ensure that when other code + * copies data from the device it doesn't overwrite the denoiser buffers. */ + copy_render_buffers_to_device(queue, render_buffers); + } +#endif + + /* This code is not supposed to run when compiled without OIDN support, so can assume if we made + * it up here all passes are properly denoised. */ + return true; +} + +uint OIDNDenoiser::get_device_type_mask() const +{ + return DEVICE_MASK_CPU; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_oidn.h b/intern/cycles/integrator/denoiser_oidn.h new file mode 100644 index 00000000000..566e761ae79 --- /dev/null +++ b/intern/cycles/integrator/denoiser_oidn.h @@ -0,0 +1,47 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/denoiser.h" +#include "util/util_thread.h" +#include "util/util_unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +/* Implementation of denoising API which uses OpenImageDenoise library. */ +class OIDNDenoiser : public Denoiser { + public: + /* Forwardly declared state which might be using compile-flag specific fields, such as + * OpenImageDenoise device and filter handles. */ + class State; + + OIDNDenoiser(Device *path_trace_device, const DenoiseParams ¶ms); + + virtual bool denoise_buffer(const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + bool allow_inplace_modification) override; + + protected: + virtual uint get_device_type_mask() const override; + + /* We only perform one denoising at a time, since OpenImageDenoise itself is multithreaded. + * Use this mutex whenever images are passed to the OIDN and needs to be denoised. */ + static thread_mutex mutex_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_direct_lighting.cl b/intern/cycles/integrator/denoiser_optix.cpp similarity index 58% rename from intern/cycles/kernel/kernels/opencl/kernel_direct_lighting.cl rename to intern/cycles/integrator/denoiser_optix.cpp index ed64ae01aae..5f9de23bfe6 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_direct_lighting.cl +++ b/intern/cycles/integrator/denoiser_optix.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,21 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_direct_lighting.h" +#include "integrator/denoiser_optix.h" -#define KERNEL_NAME direct_lighting -#define LOCALS_TYPE unsigned int -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE +#include "device/device.h" +#include "device/device_denoise.h" +CCL_NAMESPACE_BEGIN + +OptiXDenoiser::OptiXDenoiser(Device *path_trace_device, const DenoiseParams ¶ms) + : DeviceDenoiser(path_trace_device, params) +{ +} + +uint OptiXDenoiser::get_device_type_mask() const +{ + return DEVICE_MASK_OPTIX; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_lamp_emission.cl b/intern/cycles/integrator/denoiser_optix.h similarity index 62% rename from intern/cycles/kernel/kernels/opencl/kernel_lamp_emission.cl rename to intern/cycles/integrator/denoiser_optix.h index c314dc96c33..a8df770ecf7 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_lamp_emission.cl +++ b/intern/cycles/integrator/denoiser_optix.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,18 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_lamp_emission.h" +#pragma once -#define KERNEL_NAME lamp_emission -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME +#include "integrator/denoiser_device.h" +CCL_NAMESPACE_BEGIN + +class OptiXDenoiser : public DeviceDenoiser { + public: + OptiXDenoiser(Device *path_trace_device, const DenoiseParams ¶ms); + + protected: + virtual uint get_device_type_mask() const override; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor.cpp b/intern/cycles/integrator/pass_accessor.cpp new file mode 100644 index 00000000000..87c048b1fa5 --- /dev/null +++ b/intern/cycles/integrator/pass_accessor.cpp @@ -0,0 +1,318 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/pass_accessor.h" + +#include "render/buffers.h" +#include "util/util_logging.h" + +// clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/kernel_types.h" +// clang-format on + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Pass input information. + */ + +PassAccessor::PassAccessInfo::PassAccessInfo(const BufferPass &pass) + : type(pass.type), mode(pass.mode), include_albedo(pass.include_albedo), offset(pass.offset) +{ +} + +/* -------------------------------------------------------------------- + * Pass destination. + */ + +PassAccessor::Destination::Destination(float *pixels, int num_components) + : pixels(pixels), num_components(num_components) +{ +} + +PassAccessor::Destination::Destination(const PassType pass_type, half4 *pixels) + : Destination(pass_type) +{ + pixels_half_rgba = pixels; +} + +PassAccessor::Destination::Destination(const PassType pass_type) +{ + const PassInfo pass_info = Pass::get_info(pass_type); + num_components = pass_info.num_components; +} + +/* -------------------------------------------------------------------- + * Pass source. + */ + +PassAccessor::Source::Source(const float *pixels, int num_components) + : pixels(pixels), num_components(num_components) +{ +} + +/* -------------------------------------------------------------------- + * Pass accessor. + */ + +PassAccessor::PassAccessor(const PassAccessInfo &pass_access_info, float exposure, int num_samples) + : pass_access_info_(pass_access_info), exposure_(exposure), num_samples_(num_samples) +{ +} + +bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers, + const Destination &destination) const +{ + if (render_buffers == nullptr || render_buffers->buffer.data() == nullptr) { + return false; + } + + return get_render_tile_pixels(render_buffers, render_buffers->params, destination); +} + +static void pad_pixels(const BufferParams &buffer_params, + const PassAccessor::Destination &destination, + const int src_num_components) +{ + /* When requesting a single channel pass as RGBA, or RGB pass as RGBA, + * fill in the additional components for convenience. */ + const int dest_num_components = destination.num_components; + + if (src_num_components >= dest_num_components) { + return; + } + + const size_t size = buffer_params.width * buffer_params.height; + if (destination.pixels) { + float *pixel = destination.pixels; + + for (size_t i = 0; i < size; i++, pixel += dest_num_components) { + if (dest_num_components >= 3 && src_num_components == 1) { + pixel[1] = pixel[0]; + pixel[2] = pixel[0]; + } + if (dest_num_components >= 4) { + pixel[3] = 1.0f; + } + } + } + + if (destination.pixels_half_rgba) { + const half one = float_to_half(1.0f); + half4 *pixel = destination.pixels_half_rgba; + + for (size_t i = 0; i < size; i++, pixel++) { + if (dest_num_components >= 3 && src_num_components == 1) { + pixel[0].y = pixel[0].x; + pixel[0].z = pixel[0].x; + } + if (dest_num_components >= 4) { + pixel[0].w = one; + } + } + } +} + +bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination) const +{ + if (render_buffers == nullptr || render_buffers->buffer.data() == nullptr) { + return false; + } + + if (pass_access_info_.offset == PASS_UNUSED) { + return false; + } + + const PassType type = pass_access_info_.type; + const PassMode mode = pass_access_info_.mode; + const PassInfo pass_info = Pass::get_info(type, pass_access_info_.include_albedo); + + if (pass_info.num_components == 1) { + /* Single channel passes. */ + if (mode == PassMode::DENOISED) { + /* Denoised passes store their final pixels, no need in special calculation. */ + get_pass_float(render_buffers, buffer_params, destination); + } + else if (type == PASS_RENDER_TIME) { + /* TODO(sergey): Needs implementation. */ + } + else if (type == PASS_DEPTH) { + get_pass_depth(render_buffers, buffer_params, destination); + } + else if (type == PASS_MIST) { + get_pass_mist(render_buffers, buffer_params, destination); + } + else if (type == PASS_SAMPLE_COUNT) { + get_pass_sample_count(render_buffers, buffer_params, destination); + } + else { + get_pass_float(render_buffers, buffer_params, destination); + } + } + else if (type == PASS_MOTION) { + /* Motion pass. */ + DCHECK_EQ(destination.num_components, 4) << "Motion pass must have 4 components"; + get_pass_motion(render_buffers, buffer_params, destination); + } + else if (type == PASS_CRYPTOMATTE) { + /* Cryptomatte pass. */ + DCHECK_EQ(destination.num_components, 4) << "Cryptomatte pass must have 4 components"; + get_pass_cryptomatte(render_buffers, buffer_params, destination); + } + else { + /* RGB, RGBA and vector passes. */ + DCHECK(destination.num_components == 3 || destination.num_components == 4) + << pass_type_as_string(type) << " pass must have 3 or 4 components"; + + if (type == PASS_SHADOW_CATCHER_MATTE && pass_access_info_.use_approximate_shadow_catcher) { + /* Denoised matte with shadow needs to do calculation (will use denoised shadow catcher pass + * to approximate shadow with). */ + get_pass_shadow_catcher_matte_with_shadow(render_buffers, buffer_params, destination); + } + else if (type == PASS_SHADOW_CATCHER && mode != PassMode::DENOISED) { + /* Shadow catcher pass. */ + get_pass_shadow_catcher(render_buffers, buffer_params, destination); + } + else if ((pass_info.divide_type != PASS_NONE || pass_info.direct_type != PASS_NONE || + pass_info.indirect_type != PASS_NONE) && + mode != PassMode::DENOISED) { + /* RGB lighting passes that need to divide out color and/or sum direct and indirect. */ + get_pass_light_path(render_buffers, buffer_params, destination); + } + else { + /* Passes that need no special computation, or denoised passes that already + * had the computation done. */ + if (pass_info.num_components == 3) { + get_pass_float3(render_buffers, buffer_params, destination); + } + else if (pass_info.num_components == 4) { + if (destination.num_components == 3) { + /* Special case for denoiser access of RGBA passes ignoring alpha channel. */ + get_pass_float3(render_buffers, buffer_params, destination); + } + else if (type == PASS_COMBINED || type == PASS_SHADOW_CATCHER || + type == PASS_SHADOW_CATCHER_MATTE) { + /* Passes with transparency as 4th component. */ + get_pass_combined(render_buffers, buffer_params, destination); + } + else { + /* Passes with alpha as 4th component. */ + get_pass_float4(render_buffers, buffer_params, destination); + } + } + } + } + + pad_pixels(buffer_params, destination, pass_info.num_components); + + return true; +} + +void PassAccessor::init_kernel_film_convert(KernelFilmConvert *kfilm_convert, + const BufferParams &buffer_params, + const Destination &destination) const +{ + const PassMode mode = pass_access_info_.mode; + const PassInfo &pass_info = Pass::get_info(pass_access_info_.type, + pass_access_info_.include_albedo); + + kfilm_convert->pass_offset = pass_access_info_.offset; + kfilm_convert->pass_stride = buffer_params.pass_stride; + + kfilm_convert->pass_use_exposure = pass_info.use_exposure; + kfilm_convert->pass_use_filter = pass_info.use_filter; + + /* TODO(sergey): Some of the passes needs to become denoised when denoised pass is accessed. */ + if (pass_info.direct_type != PASS_NONE) { + kfilm_convert->pass_offset = buffer_params.get_pass_offset(pass_info.direct_type); + } + kfilm_convert->pass_indirect = buffer_params.get_pass_offset(pass_info.indirect_type); + kfilm_convert->pass_divide = buffer_params.get_pass_offset(pass_info.divide_type); + + kfilm_convert->pass_combined = buffer_params.get_pass_offset(PASS_COMBINED); + kfilm_convert->pass_sample_count = buffer_params.get_pass_offset(PASS_SAMPLE_COUNT); + kfilm_convert->pass_adaptive_aux_buffer = buffer_params.get_pass_offset( + PASS_ADAPTIVE_AUX_BUFFER); + kfilm_convert->pass_motion_weight = buffer_params.get_pass_offset(PASS_MOTION_WEIGHT); + kfilm_convert->pass_shadow_catcher = buffer_params.get_pass_offset(PASS_SHADOW_CATCHER, mode); + kfilm_convert->pass_shadow_catcher_sample_count = buffer_params.get_pass_offset( + PASS_SHADOW_CATCHER_SAMPLE_COUNT); + kfilm_convert->pass_shadow_catcher_matte = buffer_params.get_pass_offset( + PASS_SHADOW_CATCHER_MATTE, mode); + + /* Background is not denoised, so always use noisy pass. */ + kfilm_convert->pass_background = buffer_params.get_pass_offset(PASS_BACKGROUND); + + if (pass_info.use_filter) { + kfilm_convert->scale = num_samples_ != 0 ? 1.0f / num_samples_ : 0.0f; + } + else { + kfilm_convert->scale = 1.0f; + } + + if (pass_info.use_exposure) { + kfilm_convert->exposure = exposure_; + } + else { + kfilm_convert->exposure = 1.0f; + } + + kfilm_convert->scale_exposure = kfilm_convert->scale * kfilm_convert->exposure; + + kfilm_convert->use_approximate_shadow_catcher = pass_access_info_.use_approximate_shadow_catcher; + kfilm_convert->use_approximate_shadow_catcher_background = + pass_access_info_.use_approximate_shadow_catcher_background; + kfilm_convert->show_active_pixels = pass_access_info_.show_active_pixels; + + kfilm_convert->num_components = destination.num_components; + kfilm_convert->pixel_stride = destination.pixel_stride ? destination.pixel_stride : + destination.num_components; + + kfilm_convert->is_denoised = (mode == PassMode::DENOISED); +} + +bool PassAccessor::set_render_tile_pixels(RenderBuffers *render_buffers, const Source &source) +{ + if (render_buffers == nullptr || render_buffers->buffer.data() == nullptr) { + return false; + } + + const PassInfo pass_info = Pass::get_info(pass_access_info_.type, + pass_access_info_.include_albedo); + + const BufferParams &buffer_params = render_buffers->params; + + float *buffer_data = render_buffers->buffer.data(); + const int size = buffer_params.width * buffer_params.height; + + const int out_stride = buffer_params.pass_stride; + const int in_stride = source.num_components; + const int num_components_to_copy = min(source.num_components, pass_info.num_components); + + float *out = buffer_data + pass_access_info_.offset; + const float *in = source.pixels + source.offset * in_stride; + + for (int i = 0; i < size; i++, out += out_stride, in += in_stride) { + memcpy(out, in, sizeof(float) * num_components_to_copy); + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor.h b/intern/cycles/integrator/pass_accessor.h new file mode 100644 index 00000000000..624bf7d0b2c --- /dev/null +++ b/intern/cycles/integrator/pass_accessor.h @@ -0,0 +1,160 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "render/pass.h" +#include "util/util_half.h" +#include "util/util_string.h" +#include "util/util_types.h" + +CCL_NAMESPACE_BEGIN + +class RenderBuffers; +class BufferPass; +class BufferParams; +struct KernelFilmConvert; + +/* Helper class which allows to access pass data. + * Is designed in a way that it is created once when the pass data is known, and then pixels gets + * progressively update from various render buffers. */ +class PassAccessor { + public: + class PassAccessInfo { + public: + PassAccessInfo() = default; + explicit PassAccessInfo(const BufferPass &pass); + + PassType type = PASS_NONE; + PassMode mode = PassMode::NOISY; + bool include_albedo = false; + int offset = -1; + + /* For the shadow catcher matte pass: whether to approximate shadow catcher pass into its + * matte pass, so that both artificial objects and shadows can be alpha-overed onto a backdrop. + */ + bool use_approximate_shadow_catcher = false; + + /* When approximate shadow catcher matte is used alpha-over the result on top of background. */ + bool use_approximate_shadow_catcher_background = false; + + bool show_active_pixels = false; + }; + + class Destination { + public: + Destination() = default; + Destination(float *pixels, int num_components); + Destination(const PassType pass_type, half4 *pixels); + + /* Destination will be initialized with the number of components which is native for the given + * pass type. */ + explicit Destination(const PassType pass_type); + + /* CPU-side pointers. only usable by the `PassAccessorCPU`. */ + float *pixels = nullptr; + half4 *pixels_half_rgba = nullptr; + + /* Device-side pointers. */ + device_ptr d_pixels = 0; + device_ptr d_pixels_half_rgba = 0; + + /* Number of components per pixel in the floating-point destination. + * Is ignored for half4 destination (where number of components is implied to be 4). */ + int num_components = 0; + + /* Offset in pixels from the beginning of pixels storage. + * Allows to get pixels of render buffer into a partial slice of the destination. */ + int offset = 0; + + /* Number of floats per pixel. When zero is the same as `num_components`. + * + * NOTE: Is ignored for half4 destination, as the half4 pixels are always 4-component + * half-floats. */ + int pixel_stride = 0; + + /* Row stride in pixel elements: + * - For the float destination stride is a number of floats per row. + * - For the half4 destination stride is a number of half4 per row. */ + int stride = 0; + }; + + class Source { + public: + Source() = default; + Source(const float *pixels, int num_components); + + /* CPU-side pointers. only usable by the `PassAccessorCPU`. */ + const float *pixels = nullptr; + int num_components = 0; + + /* Offset in pixels from the beginning of pixels storage. + * Allows to get pixels of render buffer into a partial slice of the destination. */ + int offset = 0; + }; + + PassAccessor(const PassAccessInfo &pass_access_info, float exposure, int num_samples); + + virtual ~PassAccessor() = default; + + /* Get pass data from the given render buffers, perform needed filtering, and store result into + * the pixels. + * The result is stored sequentially starting from the very beginning of the pixels memory. */ + bool get_render_tile_pixels(const RenderBuffers *render_buffers, + const Destination &destination) const; + bool get_render_tile_pixels(const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination) const; + /* Set pass data for the given render buffers. Used for baking to read from passes. */ + bool set_render_tile_pixels(RenderBuffers *render_buffers, const Source &source); + + protected: + virtual void init_kernel_film_convert(KernelFilmConvert *kfilm_convert, + const BufferParams &buffer_params, + const Destination &destination) const; + +#define DECLARE_PASS_ACCESSOR(pass) \ + virtual void get_pass_##pass(const RenderBuffers *render_buffers, \ + const BufferParams &buffer_params, \ + const Destination &destination) const = 0; + + /* Float (scalar) passes. */ + DECLARE_PASS_ACCESSOR(depth) + DECLARE_PASS_ACCESSOR(mist) + DECLARE_PASS_ACCESSOR(sample_count) + DECLARE_PASS_ACCESSOR(float) + + /* Float3 passes. */ + DECLARE_PASS_ACCESSOR(light_path) + DECLARE_PASS_ACCESSOR(shadow_catcher) + DECLARE_PASS_ACCESSOR(float3) + + /* Float4 passes. */ + DECLARE_PASS_ACCESSOR(motion) + DECLARE_PASS_ACCESSOR(cryptomatte) + DECLARE_PASS_ACCESSOR(shadow_catcher_matte_with_shadow) + DECLARE_PASS_ACCESSOR(combined) + DECLARE_PASS_ACCESSOR(float4) + +#undef DECLARE_PASS_ACCESSOR + + PassAccessInfo pass_access_info_; + + float exposure_ = 0.0f; + int num_samples_ = 0; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor_cpu.cpp b/intern/cycles/integrator/pass_accessor_cpu.cpp new file mode 100644 index 00000000000..3c6691f6d43 --- /dev/null +++ b/intern/cycles/integrator/pass_accessor_cpu.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/pass_accessor_cpu.h" + +#include "render/buffers.h" +#include "util/util_logging.h" +#include "util/util_tbb.h" + +// clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" +#include "kernel/kernel_types.h" +#include "kernel/kernel_film.h" +// clang-format on + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Kernel processing. + */ + +template +inline void PassAccessorCPU::run_get_pass_kernel_processor(const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const +{ + KernelFilmConvert kfilm_convert; + init_kernel_film_convert(&kfilm_convert, buffer_params, destination); + + if (destination.pixels) { + /* NOTE: No overlays are applied since they are not used for final renders. + * Can be supported via some sort of specialization to avoid code duplication. */ + + run_get_pass_kernel_processor_float( + &kfilm_convert, render_buffers, buffer_params, destination, processor); + } + + if (destination.pixels_half_rgba) { + /* TODO(sergey): Consider adding specialization to avoid per-pixel overlay check. */ + + if (destination.num_components == 1) { + run_get_pass_kernel_processor_half_rgba(&kfilm_convert, + render_buffers, + buffer_params, + destination, + [&processor](const KernelFilmConvert *kfilm_convert, + ccl_global const float *buffer, + float *pixel_rgba) { + float pixel; + processor(kfilm_convert, buffer, &pixel); + + pixel_rgba[0] = pixel; + pixel_rgba[1] = pixel; + pixel_rgba[2] = pixel; + pixel_rgba[3] = 1.0f; + }); + } + else if (destination.num_components == 3) { + run_get_pass_kernel_processor_half_rgba(&kfilm_convert, + render_buffers, + buffer_params, + destination, + [&processor](const KernelFilmConvert *kfilm_convert, + ccl_global const float *buffer, + float *pixel_rgba) { + processor(kfilm_convert, buffer, pixel_rgba); + pixel_rgba[3] = 1.0f; + }); + } + else if (destination.num_components == 4) { + run_get_pass_kernel_processor_half_rgba( + &kfilm_convert, render_buffers, buffer_params, destination, processor); + } + } +} + +template +inline void PassAccessorCPU::run_get_pass_kernel_processor_float( + const KernelFilmConvert *kfilm_convert, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const +{ + DCHECK_EQ(destination.stride, 0) << "Custom stride for float destination is not implemented."; + + const float *buffer_data = render_buffers->buffer.data(); + const int pixel_stride = destination.pixel_stride ? destination.pixel_stride : + destination.num_components; + + tbb::parallel_for(0, buffer_params.height, [&](int64_t y) { + int64_t pixel_index = y * buffer_params.width; + for (int64_t x = 0; x < buffer_params.width; ++x, ++pixel_index) { + const int64_t input_pixel_offset = pixel_index * buffer_params.pass_stride; + const float *buffer = buffer_data + input_pixel_offset; + float *pixel = destination.pixels + (pixel_index + destination.offset) * pixel_stride; + + processor(kfilm_convert, buffer, pixel); + } + }); +} + +template +inline void PassAccessorCPU::run_get_pass_kernel_processor_half_rgba( + const KernelFilmConvert *kfilm_convert, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const +{ + const float *buffer_data = render_buffers->buffer.data(); + + half4 *dst_start = destination.pixels_half_rgba + destination.offset; + const int destination_stride = destination.stride != 0 ? destination.stride : + buffer_params.width; + + tbb::parallel_for(0, buffer_params.height, [&](int64_t y) { + int64_t pixel_index = y * buffer_params.width; + half4 *dst_row_start = dst_start + y * destination_stride; + for (int64_t x = 0; x < buffer_params.width; ++x, ++pixel_index) { + const int64_t input_pixel_offset = pixel_index * buffer_params.pass_stride; + const float *buffer = buffer_data + input_pixel_offset; + + float pixel[4]; + processor(kfilm_convert, buffer, pixel); + + film_apply_pass_pixel_overlays_rgba(kfilm_convert, buffer, pixel); + + half4 *pixel_half_rgba = dst_row_start + x; + float4_store_half(&pixel_half_rgba->x, make_float4(pixel[0], pixel[1], pixel[2], pixel[3])); + } + }); +} + +/* -------------------------------------------------------------------- + * Pass accessors. + */ + +#define DEFINE_PASS_ACCESSOR(pass) \ + void PassAccessorCPU::get_pass_##pass(const RenderBuffers *render_buffers, \ + const BufferParams &buffer_params, \ + const Destination &destination) const \ + { \ + run_get_pass_kernel_processor( \ + render_buffers, buffer_params, destination, film_get_pass_pixel_##pass); \ + } + +/* Float (scalar) passes. */ +DEFINE_PASS_ACCESSOR(depth) +DEFINE_PASS_ACCESSOR(mist) +DEFINE_PASS_ACCESSOR(sample_count) +DEFINE_PASS_ACCESSOR(float) + +/* Float3 passes. */ +DEFINE_PASS_ACCESSOR(light_path) +DEFINE_PASS_ACCESSOR(shadow_catcher) +DEFINE_PASS_ACCESSOR(float3) + +/* Float4 passes. */ +DEFINE_PASS_ACCESSOR(motion) +DEFINE_PASS_ACCESSOR(cryptomatte) +DEFINE_PASS_ACCESSOR(shadow_catcher_matte_with_shadow) +DEFINE_PASS_ACCESSOR(combined) +DEFINE_PASS_ACCESSOR(float4) + +#undef DEFINE_PASS_ACCESSOR + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor_cpu.h b/intern/cycles/integrator/pass_accessor_cpu.h new file mode 100644 index 00000000000..0313dc5bb0d --- /dev/null +++ b/intern/cycles/integrator/pass_accessor_cpu.h @@ -0,0 +1,77 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/pass_accessor.h" + +CCL_NAMESPACE_BEGIN + +struct KernelFilmConvert; + +/* Pass accessor implementation for CPU side. */ +class PassAccessorCPU : public PassAccessor { + public: + using PassAccessor::PassAccessor; + + protected: + template + inline void run_get_pass_kernel_processor(const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const; + + template + inline void run_get_pass_kernel_processor_float(const KernelFilmConvert *kfilm_convert, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const; + + template + inline void run_get_pass_kernel_processor_half_rgba(const KernelFilmConvert *kfilm_convert, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const; + +#define DECLARE_PASS_ACCESSOR(pass) \ + virtual void get_pass_##pass(const RenderBuffers *render_buffers, \ + const BufferParams &buffer_params, \ + const Destination &destination) const override; + + /* Float (scalar) passes. */ + DECLARE_PASS_ACCESSOR(depth) + DECLARE_PASS_ACCESSOR(mist) + DECLARE_PASS_ACCESSOR(sample_count) + DECLARE_PASS_ACCESSOR(float) + + /* Float3 passes. */ + DECLARE_PASS_ACCESSOR(light_path) + DECLARE_PASS_ACCESSOR(shadow_catcher) + DECLARE_PASS_ACCESSOR(float3) + + /* Float4 passes. */ + DECLARE_PASS_ACCESSOR(motion) + DECLARE_PASS_ACCESSOR(cryptomatte) + DECLARE_PASS_ACCESSOR(shadow_catcher_matte_with_shadow) + DECLARE_PASS_ACCESSOR(combined) + DECLARE_PASS_ACCESSOR(float4) + +#undef DECLARE_PASS_ACCESSOR +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor_gpu.cpp b/intern/cycles/integrator/pass_accessor_gpu.cpp new file mode 100644 index 00000000000..eb80ba99655 --- /dev/null +++ b/intern/cycles/integrator/pass_accessor_gpu.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/pass_accessor_gpu.h" + +#include "device/device_queue.h" +#include "render/buffers.h" +#include "util/util_logging.h" + +CCL_NAMESPACE_BEGIN + +PassAccessorGPU::PassAccessorGPU(DeviceQueue *queue, + const PassAccessInfo &pass_access_info, + float exposure, + int num_samples) + : PassAccessor(pass_access_info, exposure, num_samples), queue_(queue) + +{ +} + +/* -------------------------------------------------------------------- + * Kernel execution. + */ + +void PassAccessorGPU::run_film_convert_kernels(DeviceKernel kernel, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination) const +{ + KernelFilmConvert kfilm_convert; + init_kernel_film_convert(&kfilm_convert, buffer_params, destination); + + const int work_size = buffer_params.width * buffer_params.height; + + const int destination_stride = destination.stride != 0 ? destination.stride : + buffer_params.width; + + if (destination.d_pixels) { + DCHECK_EQ(destination.stride, 0) << "Custom stride for float destination is not implemented."; + + void *args[] = {const_cast(&kfilm_convert), + const_cast(&destination.d_pixels), + const_cast(&render_buffers->buffer.device_pointer), + const_cast(&work_size), + const_cast(&buffer_params.width), + const_cast(&buffer_params.offset), + const_cast(&buffer_params.stride), + const_cast(&destination.offset), + const_cast(&destination_stride)}; + + queue_->enqueue(kernel, work_size, args); + } + if (destination.d_pixels_half_rgba) { + const DeviceKernel kernel_half_float = static_cast(kernel + 1); + + void *args[] = {const_cast(&kfilm_convert), + const_cast(&destination.d_pixels_half_rgba), + const_cast(&render_buffers->buffer.device_pointer), + const_cast(&work_size), + const_cast(&buffer_params.width), + const_cast(&buffer_params.offset), + const_cast(&buffer_params.stride), + const_cast(&destination.offset), + const_cast(&destination_stride)}; + + queue_->enqueue(kernel_half_float, work_size, args); + } + + queue_->synchronize(); +} + +/* -------------------------------------------------------------------- + * Pass accessors. + */ + +#define DEFINE_PASS_ACCESSOR(pass, kernel_pass) \ + void PassAccessorGPU::get_pass_##pass(const RenderBuffers *render_buffers, \ + const BufferParams &buffer_params, \ + const Destination &destination) const \ + { \ + run_film_convert_kernels( \ + DEVICE_KERNEL_FILM_CONVERT_##kernel_pass, render_buffers, buffer_params, destination); \ + } + +/* Float (scalar) passes. */ +DEFINE_PASS_ACCESSOR(depth, DEPTH); +DEFINE_PASS_ACCESSOR(mist, MIST); +DEFINE_PASS_ACCESSOR(sample_count, SAMPLE_COUNT); +DEFINE_PASS_ACCESSOR(float, FLOAT); + +/* Float3 passes. */ +DEFINE_PASS_ACCESSOR(light_path, LIGHT_PATH); +DEFINE_PASS_ACCESSOR(float3, FLOAT3); + +/* Float4 passes. */ +DEFINE_PASS_ACCESSOR(motion, MOTION); +DEFINE_PASS_ACCESSOR(cryptomatte, CRYPTOMATTE); +DEFINE_PASS_ACCESSOR(shadow_catcher, SHADOW_CATCHER); +DEFINE_PASS_ACCESSOR(shadow_catcher_matte_with_shadow, SHADOW_CATCHER_MATTE_WITH_SHADOW); +DEFINE_PASS_ACCESSOR(combined, COMBINED); +DEFINE_PASS_ACCESSOR(float4, FLOAT4); + +#undef DEFINE_PASS_ACCESSOR + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor_gpu.h b/intern/cycles/integrator/pass_accessor_gpu.h new file mode 100644 index 00000000000..bc37e4387f3 --- /dev/null +++ b/intern/cycles/integrator/pass_accessor_gpu.h @@ -0,0 +1,68 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/pass_accessor.h" +#include "kernel/kernel_types.h" + +CCL_NAMESPACE_BEGIN + +class DeviceQueue; + +/* Pass accessor implementation for GPU side. */ +class PassAccessorGPU : public PassAccessor { + public: + PassAccessorGPU(DeviceQueue *queue, + const PassAccessInfo &pass_access_info, + float exposure, + int num_samples); + + protected: + void run_film_convert_kernels(DeviceKernel kernel, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination) const; + +#define DECLARE_PASS_ACCESSOR(pass) \ + virtual void get_pass_##pass(const RenderBuffers *render_buffers, \ + const BufferParams &buffer_params, \ + const Destination &destination) const override; + + /* Float (scalar) passes. */ + DECLARE_PASS_ACCESSOR(depth); + DECLARE_PASS_ACCESSOR(mist); + DECLARE_PASS_ACCESSOR(sample_count); + DECLARE_PASS_ACCESSOR(float); + + /* Float3 passes. */ + DECLARE_PASS_ACCESSOR(light_path); + DECLARE_PASS_ACCESSOR(float3); + + /* Float4 passes. */ + DECLARE_PASS_ACCESSOR(motion); + DECLARE_PASS_ACCESSOR(cryptomatte); + DECLARE_PASS_ACCESSOR(shadow_catcher); + DECLARE_PASS_ACCESSOR(shadow_catcher_matte_with_shadow); + DECLARE_PASS_ACCESSOR(combined); + DECLARE_PASS_ACCESSOR(float4); + +#undef DECLARE_PASS_ACCESSOR + + DeviceQueue *queue_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp new file mode 100644 index 00000000000..6c02316ac2b --- /dev/null +++ b/intern/cycles/integrator/path_trace.cpp @@ -0,0 +1,1147 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/path_trace.h" + +#include "device/cpu/device.h" +#include "device/device.h" +#include "integrator/pass_accessor.h" +#include "integrator/render_scheduler.h" +#include "render/gpu_display.h" +#include "render/pass.h" +#include "render/scene.h" +#include "render/tile.h" +#include "util/util_algorithm.h" +#include "util/util_logging.h" +#include "util/util_progress.h" +#include "util/util_tbb.h" +#include "util/util_time.h" + +CCL_NAMESPACE_BEGIN + +PathTrace::PathTrace(Device *device, + Film *film, + DeviceScene *device_scene, + RenderScheduler &render_scheduler, + TileManager &tile_manager) + : device_(device), + device_scene_(device_scene), + render_scheduler_(render_scheduler), + tile_manager_(tile_manager) +{ + DCHECK_NE(device_, nullptr); + + { + vector cpu_devices; + device_cpu_info(cpu_devices); + + cpu_device_.reset(device_cpu_create(cpu_devices[0], device->stats, device->profiler)); + } + + /* Create path tracing work in advance, so that it can be reused by incremental sampling as much + * as possible. */ + device_->foreach_device([&](Device *path_trace_device) { + path_trace_works_.emplace_back(PathTraceWork::create( + path_trace_device, film, device_scene, &render_cancel_.is_requested)); + }); + + work_balance_infos_.resize(path_trace_works_.size()); + work_balance_do_initial(work_balance_infos_); + + render_scheduler.set_need_schedule_rebalance(path_trace_works_.size() > 1); +} + +PathTrace::~PathTrace() +{ + /* Destroy any GPU resource which was used for graphics interop. + * Need to have access to the GPUDisplay as it is the only source of drawing context which is + * used for interop. */ + if (gpu_display_) { + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->destroy_gpu_resources(gpu_display_.get()); + } + } +} + +void PathTrace::load_kernels() +{ + if (denoiser_) { + denoiser_->load_kernels(progress_); + } +} + +void PathTrace::alloc_work_memory() +{ + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->alloc_work_memory(); + } +} + +bool PathTrace::ready_to_reset() +{ + /* The logic here is optimized for the best feedback in the viewport, which implies having a GPU + * display. Of there is no such display, the logic here will break. */ + DCHECK(gpu_display_); + + /* The logic here tries to provide behavior which feels the most interactive feel to artists. + * General idea is to be able to reset as quickly as possible, while still providing interactive + * feel. + * + * If the render result was ever drawn after previous reset, consider that reset is now possible. + * This way camera navigation gives the quickest feedback of rendered pixels, regardless of + * whether CPU or GPU drawing pipeline is used. + * + * Consider reset happening after redraw "slow" enough to not clog anything. This is a bit + * arbitrary, but seems to work very well with viewport navigation in Blender. */ + + if (did_draw_after_reset_) { + return true; + } + + return false; +} + +void PathTrace::reset(const BufferParams &full_params, const BufferParams &big_tile_params) +{ + if (big_tile_params_.modified(big_tile_params)) { + big_tile_params_ = big_tile_params; + render_state_.need_reset_params = true; + } + + full_params_ = full_params; + + /* NOTE: GPU display checks for buffer modification and avoids unnecessary re-allocation. + * It is requires to inform about reset whenever it happens, so that the redraw state tracking is + * properly updated. */ + if (gpu_display_) { + gpu_display_->reset(full_params); + } + + render_state_.has_denoised_result = false; + render_state_.tile_written = false; + + did_draw_after_reset_ = false; +} + +void PathTrace::device_free() +{ + /* Free render buffers used by the path trace work to reduce memory peak. */ + BufferParams empty_params; + empty_params.pass_stride = 0; + empty_params.update_offset_stride(); + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->get_render_buffers()->reset(empty_params); + } + render_state_.need_reset_params = true; +} + +void PathTrace::set_progress(Progress *progress) +{ + progress_ = progress; +} + +void PathTrace::render(const RenderWork &render_work) +{ + /* Indicate that rendering has started and that it can be requested to cancel. */ + { + thread_scoped_lock lock(render_cancel_.mutex); + if (render_cancel_.is_requested) { + return; + } + render_cancel_.is_rendering = true; + } + + render_pipeline(render_work); + + /* Indicate that rendering has finished, making it so thread which requested `cancel()` can carry + * on. */ + { + thread_scoped_lock lock(render_cancel_.mutex); + render_cancel_.is_rendering = false; + render_cancel_.condition.notify_one(); + } +} + +void PathTrace::render_pipeline(RenderWork render_work) +{ + /* NOTE: Only check for "instant" cancel here. Ther user-requested cancel via progress is + * checked in Session and the work in the event of cancel is to be finished here. */ + + render_scheduler_.set_need_schedule_cryptomatte(device_scene_->data.film.cryptomatte_passes != + 0); + + render_init_kernel_execution(); + + render_scheduler_.report_work_begin(render_work); + + init_render_buffers(render_work); + + rebalance(render_work); + + path_trace(render_work); + if (render_cancel_.is_requested) { + return; + } + + adaptive_sample(render_work); + if (render_cancel_.is_requested) { + return; + } + + cryptomatte_postprocess(render_work); + if (render_cancel_.is_requested) { + return; + } + + denoise(render_work); + if (render_cancel_.is_requested) { + return; + } + + write_tile_buffer(render_work); + update_display(render_work); + + progress_update_if_needed(render_work); + + finalize_full_buffer_on_disk(render_work); +} + +void PathTrace::render_init_kernel_execution() +{ + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->init_execution(); + } +} + +/* TODO(sergey): Look into `std::function` rather than using a template. Should not be a + * measurable performance impact at runtime, but will make compilation faster and binary somewhat + * smaller. */ +template +static void foreach_sliced_buffer_params(const vector> &path_trace_works, + const vector &work_balance_infos, + const BufferParams &buffer_params, + const Callback &callback) +{ + const int num_works = path_trace_works.size(); + const int height = buffer_params.height; + + int current_y = 0; + for (int i = 0; i < num_works; ++i) { + const double weight = work_balance_infos[i].weight; + const int slice_height = max(lround(height * weight), 1); + + /* Disallow negative values to deal with situations when there are more compute devices than + * scanlines. */ + const int remaining_height = max(0, height - current_y); + + BufferParams slide_params = buffer_params; + slide_params.full_y = buffer_params.full_y + current_y; + if (i < num_works - 1) { + slide_params.height = min(slice_height, remaining_height); + } + else { + slide_params.height = remaining_height; + } + + slide_params.update_offset_stride(); + + callback(path_trace_works[i].get(), slide_params); + + current_y += slide_params.height; + } +} + +void PathTrace::update_allocated_work_buffer_params() +{ + foreach_sliced_buffer_params(path_trace_works_, + work_balance_infos_, + big_tile_params_, + [](PathTraceWork *path_trace_work, const BufferParams ¶ms) { + RenderBuffers *buffers = path_trace_work->get_render_buffers(); + buffers->reset(params); + }); +} + +static BufferParams scale_buffer_params(const BufferParams ¶ms, int resolution_divider) +{ + BufferParams scaled_params = params; + + scaled_params.width = max(1, params.width / resolution_divider); + scaled_params.height = max(1, params.height / resolution_divider); + scaled_params.full_x = params.full_x / resolution_divider; + scaled_params.full_y = params.full_y / resolution_divider; + scaled_params.full_width = params.full_width / resolution_divider; + scaled_params.full_height = params.full_height / resolution_divider; + + scaled_params.update_offset_stride(); + + return scaled_params; +} + +void PathTrace::update_effective_work_buffer_params(const RenderWork &render_work) +{ + const int resolution_divider = render_work.resolution_divider; + + const BufferParams scaled_full_params = scale_buffer_params(full_params_, resolution_divider); + const BufferParams scaled_big_tile_params = scale_buffer_params(big_tile_params_, + resolution_divider); + + foreach_sliced_buffer_params(path_trace_works_, + work_balance_infos_, + scaled_big_tile_params, + [&](PathTraceWork *path_trace_work, const BufferParams params) { + path_trace_work->set_effective_buffer_params( + scaled_full_params, scaled_big_tile_params, params); + }); + + render_state_.effective_big_tile_params = scaled_big_tile_params; +} + +void PathTrace::update_work_buffer_params_if_needed(const RenderWork &render_work) +{ + if (render_state_.need_reset_params) { + update_allocated_work_buffer_params(); + } + + if (render_state_.need_reset_params || + render_state_.resolution_divider != render_work.resolution_divider) { + update_effective_work_buffer_params(render_work); + } + + render_state_.resolution_divider = render_work.resolution_divider; + render_state_.need_reset_params = false; +} + +void PathTrace::init_render_buffers(const RenderWork &render_work) +{ + update_work_buffer_params_if_needed(render_work); + + /* Handle initialization scheduled by the render scheduler. */ + if (render_work.init_render_buffers) { + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + path_trace_work->zero_render_buffers(); + }); + + tile_buffer_read(); + } +} + +void PathTrace::path_trace(RenderWork &render_work) +{ + if (!render_work.path_trace.num_samples) { + return; + } + + VLOG(3) << "Will path trace " << render_work.path_trace.num_samples + << " samples at the resolution divider " << render_work.resolution_divider; + + const double start_time = time_dt(); + + const int num_works = path_trace_works_.size(); + + tbb::parallel_for(0, num_works, [&](int i) { + const double work_start_time = time_dt(); + const int num_samples = render_work.path_trace.num_samples; + + PathTraceWork *path_trace_work = path_trace_works_[i].get(); + + PathTraceWork::RenderStatistics statistics; + path_trace_work->render_samples(statistics, render_work.path_trace.start_sample, num_samples); + + const double work_time = time_dt() - work_start_time; + work_balance_infos_[i].time_spent += work_time; + work_balance_infos_[i].occupancy = statistics.occupancy; + + VLOG(3) << "Rendered " << num_samples << " samples in " << work_time << " seconds (" + << work_time / num_samples + << " seconds per sample), occupancy: " << statistics.occupancy; + }); + + float occupancy_accum = 0.0f; + for (const WorkBalanceInfo &balance_info : work_balance_infos_) { + occupancy_accum += balance_info.occupancy; + } + const float occupancy = occupancy_accum / num_works; + render_scheduler_.report_path_trace_occupancy(render_work, occupancy); + + render_scheduler_.report_path_trace_time( + render_work, time_dt() - start_time, is_cancel_requested()); +} + +void PathTrace::adaptive_sample(RenderWork &render_work) +{ + if (!render_work.adaptive_sampling.filter) { + return; + } + + bool did_reschedule_on_idle = false; + + while (true) { + VLOG(3) << "Will filter adaptive stopping buffer, threshold " + << render_work.adaptive_sampling.threshold; + if (render_work.adaptive_sampling.reset) { + VLOG(3) << "Will re-calculate convergency flag for currently converged pixels."; + } + + const double start_time = time_dt(); + + uint num_active_pixels = 0; + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + const uint num_active_pixels_in_work = + path_trace_work->adaptive_sampling_converge_filter_count_active( + render_work.adaptive_sampling.threshold, render_work.adaptive_sampling.reset); + if (num_active_pixels_in_work) { + atomic_add_and_fetch_u(&num_active_pixels, num_active_pixels_in_work); + } + }); + + render_scheduler_.report_adaptive_filter_time( + render_work, time_dt() - start_time, is_cancel_requested()); + + if (num_active_pixels == 0) { + VLOG(3) << "All pixels converged."; + if (!render_scheduler_.render_work_reschedule_on_converge(render_work)) { + break; + } + VLOG(3) << "Continuing with lower threshold."; + } + else if (did_reschedule_on_idle) { + break; + } + else if (num_active_pixels < 128 * 128) { + /* NOTE: The hardcoded value of 128^2 is more of an empirical value to keep GPU busy so that + * there is no performance loss from the progressive noise floor feature. + * + * A better heuristic is possible here: for example, use maximum of 128^2 and percentage of + * the final resolution. */ + if (!render_scheduler_.render_work_reschedule_on_idle(render_work)) { + VLOG(3) << "Rescheduling is not possible: final threshold is reached."; + break; + } + VLOG(3) << "Rescheduling lower threshold."; + did_reschedule_on_idle = true; + } + else { + break; + } + } +} + +void PathTrace::set_denoiser_params(const DenoiseParams ¶ms) +{ + render_scheduler_.set_denoiser_params(params); + + if (!params.use) { + denoiser_.reset(); + return; + } + + if (denoiser_) { + const DenoiseParams old_denoiser_params = denoiser_->get_params(); + if (old_denoiser_params.type == params.type) { + denoiser_->set_params(params); + return; + } + } + + denoiser_ = Denoiser::create(device_, params); + denoiser_->is_cancelled_cb = [this]() { return is_cancel_requested(); }; +} + +void PathTrace::set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling) +{ + render_scheduler_.set_adaptive_sampling(adaptive_sampling); +} + +void PathTrace::cryptomatte_postprocess(const RenderWork &render_work) +{ + if (!render_work.cryptomatte.postprocess) { + return; + } + VLOG(3) << "Perform cryptomatte work."; + + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + path_trace_work->cryptomatte_postproces(); + }); +} + +void PathTrace::denoise(const RenderWork &render_work) +{ + if (!render_work.tile.denoise) { + return; + } + + if (!denoiser_) { + /* Denoiser was not configured, so nothing to do here. */ + return; + } + + VLOG(3) << "Perform denoising work."; + + const double start_time = time_dt(); + + RenderBuffers *buffer_to_denoise = nullptr; + + unique_ptr multi_device_buffers; + bool allow_inplace_modification = false; + + if (path_trace_works_.size() == 1) { + buffer_to_denoise = path_trace_works_.front()->get_render_buffers(); + } + else { + Device *denoiser_device = denoiser_->get_denoiser_device(); + if (!denoiser_device) { + return; + } + + multi_device_buffers = make_unique(denoiser_device); + multi_device_buffers->reset(render_state_.effective_big_tile_params); + + buffer_to_denoise = multi_device_buffers.get(); + + copy_to_render_buffers(multi_device_buffers.get()); + + allow_inplace_modification = true; + } + + if (denoiser_->denoise_buffer(render_state_.effective_big_tile_params, + buffer_to_denoise, + get_num_samples_in_buffer(), + allow_inplace_modification)) { + render_state_.has_denoised_result = true; + } + + if (multi_device_buffers) { + multi_device_buffers->copy_from_device(); + tbb::parallel_for_each( + path_trace_works_, [&multi_device_buffers](unique_ptr &path_trace_work) { + path_trace_work->copy_from_denoised_render_buffers(multi_device_buffers.get()); + }); + } + + render_scheduler_.report_denoise_time(render_work, time_dt() - start_time); +} + +void PathTrace::set_gpu_display(unique_ptr gpu_display) +{ + gpu_display_ = move(gpu_display); +} + +void PathTrace::clear_gpu_display() +{ + if (gpu_display_) { + gpu_display_->clear(); + } +} + +void PathTrace::draw() +{ + if (!gpu_display_) { + return; + } + + did_draw_after_reset_ |= gpu_display_->draw(); +} + +void PathTrace::update_display(const RenderWork &render_work) +{ + if (!render_work.display.update) { + return; + } + + if (!gpu_display_ && !tile_buffer_update_cb) { + VLOG(3) << "Ignore display update."; + return; + } + + if (full_params_.width == 0 || full_params_.height == 0) { + VLOG(3) << "Skipping GPUDisplay update due to 0 size of the render buffer."; + return; + } + + const double start_time = time_dt(); + + if (tile_buffer_update_cb) { + VLOG(3) << "Invoke buffer update callback."; + + tile_buffer_update_cb(); + } + + if (gpu_display_) { + VLOG(3) << "Perform copy to GPUDisplay work."; + + const int resolution_divider = render_work.resolution_divider; + const int texture_width = max(1, full_params_.width / resolution_divider); + const int texture_height = max(1, full_params_.height / resolution_divider); + if (!gpu_display_->update_begin(texture_width, texture_height)) { + LOG(ERROR) << "Error beginning GPUDisplay update."; + return; + } + + const PassMode pass_mode = render_work.display.use_denoised_result && + render_state_.has_denoised_result ? + PassMode::DENOISED : + PassMode::NOISY; + + /* TODO(sergey): When using multi-device rendering map the GPUDisplay once and copy data from + * all works in parallel. */ + const int num_samples = get_num_samples_in_buffer(); + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->copy_to_gpu_display(gpu_display_.get(), pass_mode, num_samples); + } + + gpu_display_->update_end(); + } + + render_scheduler_.report_display_update_time(render_work, time_dt() - start_time); +} + +void PathTrace::rebalance(const RenderWork &render_work) +{ + static const int kLogLevel = 3; + + if (!render_work.rebalance) { + return; + } + + const int num_works = path_trace_works_.size(); + + if (num_works == 1) { + VLOG(kLogLevel) << "Ignoring rebalance work due to single device render."; + return; + } + + const double start_time = time_dt(); + + if (VLOG_IS_ON(kLogLevel)) { + VLOG(kLogLevel) << "Perform rebalance work."; + VLOG(kLogLevel) << "Per-device path tracing time (seconds):"; + for (int i = 0; i < num_works; ++i) { + VLOG(kLogLevel) << path_trace_works_[i]->get_device()->info.description << ": " + << work_balance_infos_[i].time_spent; + } + } + + const bool did_rebalance = work_balance_do_rebalance(work_balance_infos_); + + if (VLOG_IS_ON(kLogLevel)) { + VLOG(kLogLevel) << "Calculated per-device weights for works:"; + for (int i = 0; i < num_works; ++i) { + VLOG(kLogLevel) << path_trace_works_[i]->get_device()->info.description << ": " + << work_balance_infos_[i].weight; + } + } + + if (!did_rebalance) { + VLOG(kLogLevel) << "Balance in path trace works did not change."; + render_scheduler_.report_rebalance_time(render_work, time_dt() - start_time, false); + return; + } + + RenderBuffers big_tile_cpu_buffers(cpu_device_.get()); + big_tile_cpu_buffers.reset(render_state_.effective_big_tile_params); + + copy_to_render_buffers(&big_tile_cpu_buffers); + + render_state_.need_reset_params = true; + update_work_buffer_params_if_needed(render_work); + + copy_from_render_buffers(&big_tile_cpu_buffers); + + render_scheduler_.report_rebalance_time(render_work, time_dt() - start_time, true); +} + +void PathTrace::write_tile_buffer(const RenderWork &render_work) +{ + if (!render_work.tile.write) { + return; + } + + VLOG(3) << "Write tile result."; + + render_state_.tile_written = true; + + const bool has_multiple_tiles = tile_manager_.has_multiple_tiles(); + + /* Write render tile result, but only if not using tiled rendering. + * + * Tiles are written to a file during rendering, and written to the software at the end + * of rendering (wither when all tiles are finished, or when rendering was requested to be + * cancelled). + * + * Important thing is: tile should be written to the software via callback only once. */ + if (!has_multiple_tiles) { + VLOG(3) << "Write tile result via buffer write callback."; + tile_buffer_write(); + } + + /* Write tile to disk, so that the render work's render buffer can be re-used for the next tile. + */ + if (has_multiple_tiles) { + VLOG(3) << "Write tile result into ."; + tile_buffer_write_to_disk(); + } +} + +void PathTrace::finalize_full_buffer_on_disk(const RenderWork &render_work) +{ + if (!render_work.full.write) { + return; + } + + VLOG(3) << "Handle full-frame render buffer work."; + + if (!tile_manager_.has_written_tiles()) { + VLOG(3) << "No tiles on disk."; + return; + } + + /* Make sure writing to the file is fully finished. + * This will include writing all possible missing tiles, ensuring validness of the file. */ + tile_manager_.finish_write_tiles(); + + /* NOTE: The rest of full-frame post-processing (such as full-frame denoising) will be done after + * all scenes and layers are rendered by the Session (which happens after freeing Session memory, + * so that we never hold scene and full-frame buffer in memory at the same time). */ +} + +void PathTrace::cancel() +{ + thread_scoped_lock lock(render_cancel_.mutex); + + render_cancel_.is_requested = true; + + while (render_cancel_.is_rendering) { + render_cancel_.condition.wait(lock); + } + + render_cancel_.is_requested = false; +} + +int PathTrace::get_num_samples_in_buffer() +{ + return render_scheduler_.get_num_rendered_samples(); +} + +bool PathTrace::is_cancel_requested() +{ + if (render_cancel_.is_requested) { + return true; + } + + if (progress_ != nullptr) { + if (progress_->get_cancel()) { + return true; + } + } + + return false; +} + +void PathTrace::tile_buffer_write() +{ + if (!tile_buffer_write_cb) { + return; + } + + tile_buffer_write_cb(); +} + +void PathTrace::tile_buffer_read() +{ + if (!tile_buffer_read_cb) { + return; + } + + if (tile_buffer_read_cb()) { + tbb::parallel_for_each(path_trace_works_, [](unique_ptr &path_trace_work) { + path_trace_work->copy_render_buffers_to_device(); + }); + } +} + +void PathTrace::tile_buffer_write_to_disk() +{ + /* Sample count pass is required to support per-tile partial results stored in the file. */ + DCHECK_NE(big_tile_params_.get_pass_offset(PASS_SAMPLE_COUNT), PASS_UNUSED); + + const int num_rendered_samples = render_scheduler_.get_num_rendered_samples(); + + if (num_rendered_samples == 0) { + /* The tile has zero samples, no need to write it. */ + return; + } + + /* Get access to the CPU-side render buffers of the current big tile. */ + RenderBuffers *buffers; + RenderBuffers big_tile_cpu_buffers(cpu_device_.get()); + + if (path_trace_works_.size() == 1) { + path_trace_works_[0]->copy_render_buffers_from_device(); + buffers = path_trace_works_[0]->get_render_buffers(); + } + else { + big_tile_cpu_buffers.reset(render_state_.effective_big_tile_params); + copy_to_render_buffers(&big_tile_cpu_buffers); + + buffers = &big_tile_cpu_buffers; + } + + if (!tile_manager_.write_tile(*buffers)) { + LOG(ERROR) << "Error writing tile to file."; + } +} + +void PathTrace::progress_update_if_needed(const RenderWork &render_work) +{ + if (progress_ != nullptr) { + const int2 tile_size = get_render_tile_size(); + const int num_samples_added = tile_size.x * tile_size.y * render_work.path_trace.num_samples; + const int current_sample = render_work.path_trace.start_sample + + render_work.path_trace.num_samples; + progress_->add_samples(num_samples_added, current_sample); + } + + if (progress_update_cb) { + progress_update_cb(); + } +} + +void PathTrace::progress_set_status(const string &status, const string &substatus) +{ + if (progress_ != nullptr) { + progress_->set_status(status, substatus); + } +} + +void PathTrace::copy_to_render_buffers(RenderBuffers *render_buffers) +{ + tbb::parallel_for_each(path_trace_works_, + [&render_buffers](unique_ptr &path_trace_work) { + path_trace_work->copy_to_render_buffers(render_buffers); + }); + render_buffers->copy_to_device(); +} + +void PathTrace::copy_from_render_buffers(RenderBuffers *render_buffers) +{ + render_buffers->copy_from_device(); + tbb::parallel_for_each(path_trace_works_, + [&render_buffers](unique_ptr &path_trace_work) { + path_trace_work->copy_from_render_buffers(render_buffers); + }); +} + +bool PathTrace::copy_render_tile_from_device() +{ + if (full_frame_state_.render_buffers) { + /* Full-frame buffer is always allocated on CPU. */ + return true; + } + + bool success = true; + + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + if (!success) { + return; + } + if (!path_trace_work->copy_render_buffers_from_device()) { + success = false; + } + }); + + return success; +} + +static string get_layer_view_name(const RenderBuffers &buffers) +{ + string result; + + if (buffers.params.layer.size()) { + result += string(buffers.params.layer); + } + + if (buffers.params.view.size()) { + if (!result.empty()) { + result += ", "; + } + result += string(buffers.params.view); + } + + return result; +} + +void PathTrace::process_full_buffer_from_disk(string_view filename) +{ + VLOG(3) << "Processing full frame buffer file " << filename; + + progress_set_status("Reading full buffer from disk"); + + RenderBuffers full_frame_buffers(cpu_device_.get()); + + DenoiseParams denoise_params; + if (!tile_manager_.read_full_buffer_from_disk(filename, &full_frame_buffers, &denoise_params)) { + LOG(ERROR) << "Error reading tiles from file."; + return; + } + + const string layer_view_name = get_layer_view_name(full_frame_buffers); + + render_state_.has_denoised_result = false; + + if (denoise_params.use) { + progress_set_status(layer_view_name, "Denoising"); + + /* Re-use the denoiser as much as possible, avoiding possible device re-initialization. + * + * It will not conflict with the regular rendering as: + * - Rendering is supposed to be finished here. + * - The next rendering will go via Session's `run_update_for_next_iteration` which will + * ensure proper denoiser is used. */ + set_denoiser_params(denoise_params); + + /* Number of samples doesn't matter too much, since the sampels count pass will be used. */ + denoiser_->denoise_buffer(full_frame_buffers.params, &full_frame_buffers, 0, false); + + render_state_.has_denoised_result = true; + } + + full_frame_state_.render_buffers = &full_frame_buffers; + + progress_set_status(layer_view_name, "Finishing"); + + /* Write the full result pretending that there is a single tile. + * Requires some state change, but allows to use same communication API with the software. */ + tile_buffer_write(); + + full_frame_state_.render_buffers = nullptr; +} + +int PathTrace::get_num_render_tile_samples() const +{ + if (full_frame_state_.render_buffers) { + /* If the full-frame buffer is read from disk the number of samples is not used as there is a + * sample count pass for that in the buffer. Just avoid access to badly defined state of the + * path state. */ + return 0; + } + + return render_scheduler_.get_num_rendered_samples(); +} + +bool PathTrace::get_render_tile_pixels(const PassAccessor &pass_accessor, + const PassAccessor::Destination &destination) +{ + if (full_frame_state_.render_buffers) { + return pass_accessor.get_render_tile_pixels(full_frame_state_.render_buffers, destination); + } + + bool success = true; + + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + if (!success) { + return; + } + if (!path_trace_work->get_render_tile_pixels(pass_accessor, destination)) { + success = false; + } + }); + + return success; +} + +bool PathTrace::set_render_tile_pixels(PassAccessor &pass_accessor, + const PassAccessor::Source &source) +{ + bool success = true; + + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + if (!success) { + return; + } + if (!path_trace_work->set_render_tile_pixels(pass_accessor, source)) { + success = false; + } + }); + + return success; +} + +int2 PathTrace::get_render_tile_size() const +{ + if (full_frame_state_.render_buffers) { + return make_int2(full_frame_state_.render_buffers->params.width, + full_frame_state_.render_buffers->params.height); + } + + const Tile &tile = tile_manager_.get_current_tile(); + return make_int2(tile.width, tile.height); +} + +int2 PathTrace::get_render_tile_offset() const +{ + if (full_frame_state_.render_buffers) { + return make_int2(0, 0); + } + + const Tile &tile = tile_manager_.get_current_tile(); + return make_int2(tile.x, tile.y); +} + +const BufferParams &PathTrace::get_render_tile_params() const +{ + if (full_frame_state_.render_buffers) { + return full_frame_state_.render_buffers->params; + } + + return big_tile_params_; +} + +bool PathTrace::has_denoised_result() const +{ + return render_state_.has_denoised_result; +} + +/* -------------------------------------------------------------------- + * Report generation. + */ + +static const char *device_type_for_description(const DeviceType type) +{ + switch (type) { + case DEVICE_NONE: + return "None"; + + case DEVICE_CPU: + return "CPU"; + case DEVICE_CUDA: + return "CUDA"; + case DEVICE_OPTIX: + return "OptiX"; + case DEVICE_DUMMY: + return "Dummy"; + case DEVICE_MULTI: + return "Multi"; + } + + return "UNKNOWN"; +} + +/* Construct description of the device which will appear in the full report. */ +/* TODO(sergey): Consider making it more reusable utility. */ +static string full_device_info_description(const DeviceInfo &device_info) +{ + string full_description = device_info.description; + + full_description += " (" + string(device_type_for_description(device_info.type)) + ")"; + + if (device_info.display_device) { + full_description += " (display)"; + } + + if (device_info.type == DEVICE_CPU) { + full_description += " (" + to_string(device_info.cpu_threads) + " threads)"; + } + + full_description += " [" + device_info.id + "]"; + + return full_description; +} + +/* Construct string which will contain information about devices, possibly multiple of the devices. + * + * In the simple case the result looks like: + * + * Message: Full Device Description + * + * If there are multiple devices then the result looks like: + * + * Message: Full First Device Description + * Full Second Device Description + * + * Note that the newlines are placed in a way so that the result can be easily concatenated to the + * full report. */ +static string device_info_list_report(const string &message, const DeviceInfo &device_info) +{ + string result = "\n" + message + ": "; + const string pad(message.length() + 2, ' '); + + if (device_info.multi_devices.empty()) { + result += full_device_info_description(device_info) + "\n"; + return result; + } + + bool is_first = true; + for (const DeviceInfo &sub_device_info : device_info.multi_devices) { + if (!is_first) { + result += pad; + } + + result += full_device_info_description(sub_device_info) + "\n"; + + is_first = false; + } + + return result; +} + +static string path_trace_devices_report(const vector> &path_trace_works) +{ + DeviceInfo device_info; + device_info.type = DEVICE_MULTI; + + for (auto &&path_trace_work : path_trace_works) { + device_info.multi_devices.push_back(path_trace_work->get_device()->info); + } + + return device_info_list_report("Path tracing on", device_info); +} + +static string denoiser_device_report(const Denoiser *denoiser) +{ + if (!denoiser) { + return ""; + } + + if (!denoiser->get_params().use) { + return ""; + } + + const Device *denoiser_device = denoiser->get_denoiser_device(); + if (!denoiser_device) { + return ""; + } + + return device_info_list_report("Denoising on", denoiser_device->info); +} + +string PathTrace::full_report() const +{ + string result = "\nFull path tracing report\n"; + + result += path_trace_devices_report(path_trace_works_); + result += denoiser_device_report(denoiser_.get()); + + /* Report from the render scheduler, which includes: + * - Render mode (interactive, offline, headless) + * - Adaptive sampling and denoiser parameters + * - Breakdown of timing. */ + result += render_scheduler_.full_report(); + + return result; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h new file mode 100644 index 00000000000..78ca68c1198 --- /dev/null +++ b/intern/cycles/integrator/path_trace.h @@ -0,0 +1,324 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/denoiser.h" +#include "integrator/pass_accessor.h" +#include "integrator/path_trace_work.h" +#include "integrator/work_balancer.h" +#include "render/buffers.h" +#include "util/util_function.h" +#include "util/util_thread.h" +#include "util/util_unique_ptr.h" +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +class AdaptiveSampling; +class Device; +class DeviceScene; +class Film; +class RenderBuffers; +class RenderScheduler; +class RenderWork; +class Progress; +class GPUDisplay; +class TileManager; + +/* PathTrace class takes care of kernel graph and scheduling on a (multi)device. It takes care of + * all the common steps of path tracing which are not device-specific. The list of tasks includes + * but is not limited to: + * - Kernel graph. + * - Scheduling logic. + * - Queues management. + * - Adaptive stopping. */ +class PathTrace { + public: + /* Render scheduler is used to report timing information and access things like start/finish + * sample. */ + PathTrace(Device *device, + Film *film, + DeviceScene *device_scene, + RenderScheduler &render_scheduler, + TileManager &tile_manager); + ~PathTrace(); + + /* Create devices and load kernels which are created on-demand (for example, denoising devices). + * The progress is reported to the currently configure progress object (via `set_progress`). */ + void load_kernels(); + + /* Allocate working memory. This runs before allocating scene memory so that we can estimate + * more accurately which scene device memory may need to allocated on the host. */ + void alloc_work_memory(); + + /* Check whether now it is a good time to reset rendering. + * Used to avoid very often resets in the viewport, giving it a chance to draw intermediate + * render result. */ + bool ready_to_reset(); + + void reset(const BufferParams &full_params, const BufferParams &big_tile_params); + + void device_free(); + + /* Set progress tracker. + * Used to communicate details about the progress to the outer world, check whether rendering is + * to be canceled. + * + * The path tracer writes to this object, and then at a convenient moment runs + * progress_update_cb() callback. */ + void set_progress(Progress *progress); + + /* NOTE: This is a blocking call. Meaning, it will not return until given number of samples are + * rendered (or until rendering is requested to be cancelled). */ + void render(const RenderWork &render_work); + + /* TODO(sergey): Decide whether denoiser is really a part of path tracer. Currently it is + * convenient to have it here because then its easy to access render buffer. But the downside is + * that this adds too much of entities which can live separately with some clear API. */ + + /* Set denoiser parameters. + * Use this to configure the denoiser before rendering any samples. */ + void set_denoiser_params(const DenoiseParams ¶ms); + + /* Set parameters used for adaptive sampling. + * Use this to configure the adaptive sampler before rendering any samples. */ + void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling); + + /* Set GPU display which takes care of drawing the render result. */ + void set_gpu_display(unique_ptr gpu_display); + + /* Clear the GPU display by filling it in with all zeroes. */ + void clear_gpu_display(); + + /* Perform drawing of the current state of the GPUDisplay. */ + void draw(); + + /* Cancel rendering process as soon as possible, without waiting for full tile to be sampled. + * Used in cases like reset of render session. + * + * This is a blockign call, which returns as soon as there is no running `render_samples()` call. + */ + void cancel(); + + /* Copy an entire render buffer to/from the path trace. */ + + /* Copy happens via CPU side buffer: data will be copied from every device of the path trace, and + * the data will be copied to the device of the given render buffers. */ + void copy_to_render_buffers(RenderBuffers *render_buffers); + + /* Copy happens via CPU side buffer: data will be copied from the device of the given rendetr + * buffers and will be copied to all devices of the path trace. */ + void copy_from_render_buffers(RenderBuffers *render_buffers); + + /* Copy render buffers of the big tile from the device to hsot. + * Return true if all copies are successful. */ + bool copy_render_tile_from_device(); + + /* Read given full-frame file from disk, perform needed processing and write it to the software + * via the write callback. */ + void process_full_buffer_from_disk(string_view filename); + + /* Get number of samples in the current big tile render buffers. */ + int get_num_render_tile_samples() const; + + /* Get pass data of the entire big tile. + * This call puts pass render result from all devices into the final pixels storage. + * + * NOTE: Expects buffers to be copied to the host using `copy_render_tile_from_device()`. + * + * Returns false if any of the accessor's `get_render_tile_pixels()` returned false. */ + bool get_render_tile_pixels(const PassAccessor &pass_accessor, + const PassAccessor::Destination &destination); + + /* Set pass data for baking. */ + bool set_render_tile_pixels(PassAccessor &pass_accessor, const PassAccessor::Source &source); + + /* Check whether denoiser was run and denoised passes are available. */ + bool has_denoised_result() const; + + /* Get size and offset (relative to the buffer's full x/y) of the currently rendering tile. + * In the case of tiled rendering this will return full-frame after all tiles has been rendered. + * + * NOTE: If the full-frame buffer processing is in progress, returns parameters of the full-frame + * instead. */ + int2 get_render_tile_size() const; + int2 get_render_tile_offset() const; + + /* Get buffer parameters of the current tile. + * + * NOTE: If the full-frame buffer processing is in progress, returns parameters of the full-frame + * instead. */ + const BufferParams &get_render_tile_params() const; + + /* Generate full multi-line report of the rendering process, including rendering parameters, + * times, and so on. */ + string full_report() const; + + /* Callback which communicates an updates state of the render buffer of the current big tile. + * Is called during path tracing to communicate work-in-progress state of the final buffer. */ + function tile_buffer_update_cb; + + /* Callback which communicates final rendered buffer. Is called after pathtracing is done. */ + function tile_buffer_write_cb; + + /* Callback which initializes rendered buffer. Is called before pathtracing starts. + * + * This is used for baking. */ + function tile_buffer_read_cb; + + /* Callback which is called to report current rendering progress. + * + * It is supposed to be cheaper than buffer update/write, hence can be called more often. + * Additionally, it might be called form the middle of wavefront (meaning, it is not guaranteed + * that the buffer is "uniformly" sampled at the moment of this callback). */ + function progress_update_cb; + + protected: + /* Actual implementation of the rendering pipeline. + * Calls steps in order, checking for the cancel to be requested inbetween. + * + * Is separate from `render()` to simplify dealing with the early outputs and keeping + * `render_cancel_` in the consistent state. */ + void render_pipeline(RenderWork render_work); + + /* Initialize kernel execution on all integrator queues. */ + void render_init_kernel_execution(); + + /* Make sure both allocated and effective buffer parameters of path tracer works are up to date + * with the current big tile parameters, performance-dependent slicing, and resolution divider. + */ + void update_work_buffer_params_if_needed(const RenderWork &render_work); + void update_allocated_work_buffer_params(); + void update_effective_work_buffer_params(const RenderWork &render_work); + + /* Perform various steps of the render work. + * + * Note that some steps might modify the work, forcing some steps to happen within this iteration + * of rendering. */ + void init_render_buffers(const RenderWork &render_work); + void path_trace(RenderWork &render_work); + void adaptive_sample(RenderWork &render_work); + void denoise(const RenderWork &render_work); + void cryptomatte_postprocess(const RenderWork &render_work); + void update_display(const RenderWork &render_work); + void rebalance(const RenderWork &render_work); + void write_tile_buffer(const RenderWork &render_work); + void finalize_full_buffer_on_disk(const RenderWork &render_work); + + /* Get number of samples in the current state of the render buffers. */ + int get_num_samples_in_buffer(); + + /* Check whether user requested to cancel rendering, so that path tracing is to be finished as + * soon as possible. */ + bool is_cancel_requested(); + + /* Write the big tile render buffer via the write callback. */ + void tile_buffer_write(); + + /* Read the big tile render buffer via the read callback. */ + void tile_buffer_read(); + + /* Write current tile into the file on disk. */ + void tile_buffer_write_to_disk(); + + /* Run the progress_update_cb callback if it is needed. */ + void progress_update_if_needed(const RenderWork &render_work); + + void progress_set_status(const string &status, const string &substatus = ""); + + /* Pointer to a device which is configured to be used for path tracing. If multiple devices + * are configured this is a `MultiDevice`. */ + Device *device_ = nullptr; + + /* CPU device for creating temporary render buffers on the CPU side. */ + unique_ptr cpu_device_; + + DeviceScene *device_scene_; + + RenderScheduler &render_scheduler_; + TileManager &tile_manager_; + + unique_ptr gpu_display_; + + /* Per-compute device descriptors of work which is responsible for path tracing on its configured + * device. */ + vector> path_trace_works_; + + /* Per-path trace work information needed for multi-device balancing. */ + vector work_balance_infos_; + + /* Render buffer parameters of the full frame and current big tile. */ + BufferParams full_params_; + BufferParams big_tile_params_; + + /* Denoiser which takes care of denoising the big tile. */ + unique_ptr denoiser_; + + /* State which is common for all the steps of the render work. + * Is brought up to date in the `render()` call and is accessed from all the steps involved into + * rendering the work. */ + struct { + /* Denotes whether render buffers parameters of path trace works are to be reset for the new + * value of the big tile parameters. */ + bool need_reset_params = false; + + /* Divider of the resolution for faster previews. + * + * Allows to re-use same render buffer, but have less pixels rendered into in it. The way to + * think of render buffer in this case is as an over-allocated array: the resolution divider + * affects both resolution and stride as visible by the integrator kernels. */ + int resolution_divider = 0; + + /* Paramaters of the big tile with the current resolution divider applied. */ + BufferParams effective_big_tile_params; + + /* Denosier was run and there are denoised versions of the passes in the render buffers. */ + bool has_denoised_result = false; + + /* Current tile has been written (to either disk or callback. + * Indicates that no more work will be done on this tile. */ + bool tile_written = false; + } render_state_; + + /* Progress object which is used to communicate sample progress. */ + Progress *progress_; + + /* Fields required for canceling render on demand, as quickly as possible. */ + struct { + /* Indicates whether there is an on-going `render_samples()` call. */ + bool is_rendering = false; + + /* Indicates whether rendering is requested to be canceled by `cancel()`. */ + bool is_requested = false; + + /* Synchronization between thread which does `render_samples()` and thread which does + * `cancel()`. */ + thread_mutex mutex; + thread_condition_variable condition; + } render_cancel_; + + /* Indicates whether a render result was drawn after latest session reset. + * Used by `ready_to_reset()` to implement logic which feels the most interactive. */ + bool did_draw_after_reset_ = true; + + /* State of the full frame processing and writing to the software. */ + struct { + RenderBuffers *render_buffers = nullptr; + } full_frame_state_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp new file mode 100644 index 00000000000..d9634acac10 --- /dev/null +++ b/intern/cycles/integrator/path_trace_work.cpp @@ -0,0 +1,203 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/device.h" + +#include "integrator/path_trace_work.h" +#include "integrator/path_trace_work_cpu.h" +#include "integrator/path_trace_work_gpu.h" +#include "render/buffers.h" +#include "render/film.h" +#include "render/gpu_display.h" +#include "render/scene.h" + +#include "kernel/kernel_types.h" + +CCL_NAMESPACE_BEGIN + +unique_ptr PathTraceWork::create(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag) +{ + if (device->info.type == DEVICE_CPU) { + return make_unique(device, film, device_scene, cancel_requested_flag); + } + + return make_unique(device, film, device_scene, cancel_requested_flag); +} + +PathTraceWork::PathTraceWork(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag) + : device_(device), + film_(film), + device_scene_(device_scene), + buffers_(make_unique(device)), + effective_buffer_params_(buffers_->params), + cancel_requested_flag_(cancel_requested_flag) +{ +} + +PathTraceWork::~PathTraceWork() +{ +} + +RenderBuffers *PathTraceWork::get_render_buffers() +{ + return buffers_.get(); +} + +void PathTraceWork::set_effective_buffer_params(const BufferParams &effective_full_params, + const BufferParams &effective_big_tile_params, + const BufferParams &effective_buffer_params) +{ + effective_full_params_ = effective_full_params; + effective_big_tile_params_ = effective_big_tile_params; + effective_buffer_params_ = effective_buffer_params; +} + +bool PathTraceWork::has_multiple_works() const +{ + /* Assume if there are multiple works working on the same big tile none of the works gets the + * entire big tile to work on. */ + return !(effective_big_tile_params_.width == effective_buffer_params_.width && + effective_big_tile_params_.height == effective_buffer_params_.height && + effective_big_tile_params_.full_x == effective_buffer_params_.full_x && + effective_big_tile_params_.full_y == effective_buffer_params_.full_y); +} + +void PathTraceWork::copy_to_render_buffers(RenderBuffers *render_buffers) +{ + copy_render_buffers_from_device(); + + const int64_t width = effective_buffer_params_.width; + const int64_t height = effective_buffer_params_.height; + const int64_t pass_stride = effective_buffer_params_.pass_stride; + const int64_t row_stride = width * pass_stride; + const int64_t data_size = row_stride * height * sizeof(float); + + const int64_t offset_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y; + const int64_t offset_in_floats = offset_y * row_stride; + + const float *src = buffers_->buffer.data(); + float *dst = render_buffers->buffer.data() + offset_in_floats; + + memcpy(dst, src, data_size); +} + +void PathTraceWork::copy_from_render_buffers(const RenderBuffers *render_buffers) +{ + const int64_t width = effective_buffer_params_.width; + const int64_t height = effective_buffer_params_.height; + const int64_t pass_stride = effective_buffer_params_.pass_stride; + const int64_t row_stride = width * pass_stride; + const int64_t data_size = row_stride * height * sizeof(float); + + const int64_t offset_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y; + const int64_t offset_in_floats = offset_y * row_stride; + + const float *src = render_buffers->buffer.data() + offset_in_floats; + float *dst = buffers_->buffer.data(); + + memcpy(dst, src, data_size); + + copy_render_buffers_to_device(); +} + +void PathTraceWork::copy_from_denoised_render_buffers(const RenderBuffers *render_buffers) +{ + const int64_t width = effective_buffer_params_.width; + const int64_t offset_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y; + const int64_t offset = offset_y * width; + + render_buffers_host_copy_denoised( + buffers_.get(), effective_buffer_params_, render_buffers, effective_buffer_params_, offset); + + copy_render_buffers_to_device(); +} + +bool PathTraceWork::get_render_tile_pixels(const PassAccessor &pass_accessor, + const PassAccessor::Destination &destination) +{ + const int offset_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y; + const int width = effective_buffer_params_.width; + + PassAccessor::Destination slice_destination = destination; + slice_destination.offset += offset_y * width; + + return pass_accessor.get_render_tile_pixels(buffers_.get(), slice_destination); +} + +bool PathTraceWork::set_render_tile_pixels(PassAccessor &pass_accessor, + const PassAccessor::Source &source) +{ + const int offset_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y; + const int width = effective_buffer_params_.width; + + PassAccessor::Source slice_source = source; + slice_source.offset += offset_y * width; + + return pass_accessor.set_render_tile_pixels(buffers_.get(), slice_source); +} + +PassAccessor::PassAccessInfo PathTraceWork::get_display_pass_access_info(PassMode pass_mode) const +{ + const KernelFilm &kfilm = device_scene_->data.film; + const KernelBackground &kbackground = device_scene_->data.background; + + const BufferParams ¶ms = buffers_->params; + + const BufferPass *display_pass = params.get_actual_display_pass(film_->get_display_pass()); + + PassAccessor::PassAccessInfo pass_access_info; + pass_access_info.type = display_pass->type; + pass_access_info.offset = PASS_UNUSED; + + if (pass_mode == PassMode::DENOISED) { + pass_access_info.mode = PassMode::DENOISED; + pass_access_info.offset = params.get_pass_offset(pass_access_info.type, PassMode::DENOISED); + } + + if (pass_access_info.offset == PASS_UNUSED) { + pass_access_info.mode = PassMode::NOISY; + pass_access_info.offset = params.get_pass_offset(pass_access_info.type); + } + + pass_access_info.use_approximate_shadow_catcher = kfilm.use_approximate_shadow_catcher; + pass_access_info.use_approximate_shadow_catcher_background = + kfilm.use_approximate_shadow_catcher && !kbackground.transparent; + + return pass_access_info; +} + +PassAccessor::Destination PathTraceWork::get_gpu_display_destination_template( + const GPUDisplay *gpu_display) const +{ + PassAccessor::Destination destination(film_->get_display_pass()); + + const int2 display_texture_size = gpu_display->get_texture_size(); + const int texture_x = effective_buffer_params_.full_x - effective_full_params_.full_x; + const int texture_y = effective_buffer_params_.full_y - effective_full_params_.full_y; + + destination.offset = texture_y * display_texture_size.x + texture_x; + destination.stride = display_texture_size.x; + + return destination; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work.h b/intern/cycles/integrator/path_trace_work.h new file mode 100644 index 00000000000..97b97f3d888 --- /dev/null +++ b/intern/cycles/integrator/path_trace_work.h @@ -0,0 +1,194 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/pass_accessor.h" +#include "render/buffers.h" +#include "render/pass.h" +#include "util/util_types.h" +#include "util/util_unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +class BufferParams; +class Device; +class DeviceScene; +class Film; +class GPUDisplay; +class RenderBuffers; + +class PathTraceWork { + public: + struct RenderStatistics { + float occupancy = 1.0f; + }; + + /* Create path trace work which fits best the device. + * + * The cancel request flag is used for a cheap check whether cancel is to berformed as soon as + * possible. This could be, for rexample, request to cancel rendering on camera navigation in + * viewport. */ + static unique_ptr create(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag); + + virtual ~PathTraceWork(); + + /* Access the render buffers. + * + * Is only supposed to be used by the PathTrace to update buffer allocation and slicing to + * correspond to the big tile size and relative device performance. */ + RenderBuffers *get_render_buffers(); + + /* Set effective parameters of the big tile and the work itself. */ + void set_effective_buffer_params(const BufferParams &effective_full_params, + const BufferParams &effective_big_tile_params, + const BufferParams &effective_buffer_params); + + /* Check whether the big tile is being worked on by multiple path trace works. */ + bool has_multiple_works() const; + + /* Allocate working memory for execution. Must be called before init_execution(). */ + virtual void alloc_work_memory(){}; + + /* Initialize execution of kernels. + * Will ensure that all device queues are initialized for execution. + * + * This method is to be called after any change in the scene. It is not needed to call it prior + * to an every call of the `render_samples()`. */ + virtual void init_execution() = 0; + + /* Render given number of samples as a synchronous blocking call. + * The samples are added to the render buffer associated with this work. */ + virtual void render_samples(RenderStatistics &statistics, int start_sample, int samples_num) = 0; + + /* Copy render result from this work to the corresponding place of the GPU display. + * + * The `pass_mode` indicates whether to access denoised or noisy version of the display pass. The + * noisy pass mode will be passed here when it is known that the buffer does not have denoised + * passes yet (because denoiser did not run). If the denoised pass is requested and denoiser is + * not used then this function will fall-back to the noisy pass instead. */ + virtual void copy_to_gpu_display(GPUDisplay *gpu_display, + PassMode pass_mode, + int num_samples) = 0; + + virtual void destroy_gpu_resources(GPUDisplay *gpu_display) = 0; + + /* Copy data from/to given render buffers. + * Will copy pixels from a corresponding place (from multi-device point of view) of the render + * buffers, and copy work's render buffers to the corresponding place of the destination. */ + + /* Notes: + * - Copies work's render buffer from the device. + * - Copies CPU-side buffer of the given buffer + * - Does not copy the buffer to its device. */ + void copy_to_render_buffers(RenderBuffers *render_buffers); + + /* Notes: + * - Does not copy given render buffers from the device. + * - Copies work's render buffer to its device. */ + void copy_from_render_buffers(const RenderBuffers *render_buffers); + + /* Special version of the `copy_from_render_buffers()` which only copies denosied passes from the + * given render buffers, leaving rest of the passes. + * + * Same notes about device copying aplies to this call as well. */ + void copy_from_denoised_render_buffers(const RenderBuffers *render_buffers); + + /* Copy render buffers to/from device using an appropriate device queue when needed so that + * things are executed in order with the `render_samples()`. */ + virtual bool copy_render_buffers_from_device() = 0; + virtual bool copy_render_buffers_to_device() = 0; + + /* Zero render buffers to/from device using an appropriate device queue when needed so that + * things are executed in order with the `render_samples()`. */ + virtual bool zero_render_buffers() = 0; + + /* Access pixels rendered by this work and copy them to the coresponding location in the + * destination. + * + * NOTE: Does not perform copy of buffers from the device. Use `copy_render_tile_from_device()` + * to update host-side data. */ + bool get_render_tile_pixels(const PassAccessor &pass_accessor, + const PassAccessor::Destination &destination); + + /* Set pass data for baking. */ + bool set_render_tile_pixels(PassAccessor &pass_accessor, const PassAccessor::Source &source); + + /* Perform convergence test on the render buffer, and filter the convergence mask. + * Returns number of active pixels (the ones which did not converge yet). */ + virtual int adaptive_sampling_converge_filter_count_active(float threshold, bool reset) = 0; + + /* Run cryptomatte pass post-processing kernels. */ + virtual void cryptomatte_postproces() = 0; + + /* Cheap-ish request to see whether rendering is requested and is to be stopped as soon as + * possible, without waiting for any samples to be finished. */ + inline bool is_cancel_requested() const + { + /* NOTE: Rely on the fact that on x86 CPU reading scalar can happen without atomic even in + * threaded environment. */ + return *cancel_requested_flag_; + } + + /* Access to the device which is used to path trace this work on. */ + Device *get_device() const + { + return device_; + } + + protected: + PathTraceWork(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag); + + PassAccessor::PassAccessInfo get_display_pass_access_info(PassMode pass_mode) const; + + /* Get destination which offset and stride are configured so that writing to it will write to a + * proper location of GPU display texture, taking current tile and device slice into account. */ + PassAccessor::Destination get_gpu_display_destination_template( + const GPUDisplay *gpu_display) const; + + /* Device which will be used for path tracing. + * Note that it is an actual render device (and never is a multi-device). */ + Device *device_; + + /* Film is used to access display pass configuration for GPU display update. + * Note that only fields which are not a part of kernel data can be accessed via the Film. */ + Film *film_; + + /* Device side scene storage, that may be used for integrator logic. */ + DeviceScene *device_scene_; + + /* Render buffers where sampling is being accumulated into, allocated for a fraction of the big + * tile which is being rendered by this work. + * It also defines possible subset of a big tile in the case of multi-device rendering. */ + unique_ptr buffers_; + + /* Effective parameters of the full, big tile, and current work render buffer. + * The latter might be different from buffers_->params when there is a resolution divider + * involved. */ + BufferParams effective_full_params_; + BufferParams effective_big_tile_params_; + BufferParams effective_buffer_params_; + + bool *cancel_requested_flag_ = nullptr; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp new file mode 100644 index 00000000000..b9a33b64051 --- /dev/null +++ b/intern/cycles/integrator/path_trace_work_cpu.cpp @@ -0,0 +1,281 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/path_trace_work_cpu.h" + +#include "device/cpu/kernel.h" +#include "device/device.h" + +#include "integrator/pass_accessor_cpu.h" + +#include "render/buffers.h" +#include "render/gpu_display.h" +#include "render/scene.h" + +#include "util/util_atomic.h" +#include "util/util_logging.h" +#include "util/util_tbb.h" + +CCL_NAMESPACE_BEGIN + +/* Create TBB arena for execution of path tracing and rendering tasks. */ +static inline tbb::task_arena local_tbb_arena_create(const Device *device) +{ + /* TODO: limit this to number of threads of CPU device, it may be smaller than + * the system number of threads when we reduce the number of CPU threads in + * CPU + GPU rendering to dedicate some cores to handling the GPU device. */ + return tbb::task_arena(device->info.cpu_threads); +} + +/* Get CPUKernelThreadGlobals for the current thread. */ +static inline CPUKernelThreadGlobals *kernel_thread_globals_get( + vector &kernel_thread_globals) +{ + const int thread_index = tbb::this_task_arena::current_thread_index(); + DCHECK_GE(thread_index, 0); + DCHECK_LE(thread_index, kernel_thread_globals.size()); + + return &kernel_thread_globals[thread_index]; +} + +PathTraceWorkCPU::PathTraceWorkCPU(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag) + : PathTraceWork(device, film, device_scene, cancel_requested_flag), + kernels_(*(device->get_cpu_kernels())) +{ + DCHECK_EQ(device->info.type, DEVICE_CPU); +} + +void PathTraceWorkCPU::init_execution() +{ + /* Cache per-thread kernel globals. */ + device_->get_cpu_kernel_thread_globals(kernel_thread_globals_); +} + +void PathTraceWorkCPU::render_samples(RenderStatistics &statistics, + int start_sample, + int samples_num) +{ + const int64_t image_width = effective_buffer_params_.width; + const int64_t image_height = effective_buffer_params_.height; + const int64_t total_pixels_num = image_width * image_height; + + for (CPUKernelThreadGlobals &kernel_globals : kernel_thread_globals_) { + kernel_globals.start_profiling(); + } + + tbb::task_arena local_arena = local_tbb_arena_create(device_); + local_arena.execute([&]() { + tbb::parallel_for(int64_t(0), total_pixels_num, [&](int64_t work_index) { + if (is_cancel_requested()) { + return; + } + + const int y = work_index / image_width; + const int x = work_index - y * image_width; + + KernelWorkTile work_tile; + work_tile.x = effective_buffer_params_.full_x + x; + work_tile.y = effective_buffer_params_.full_y + y; + work_tile.w = 1; + work_tile.h = 1; + work_tile.start_sample = start_sample; + work_tile.num_samples = 1; + work_tile.offset = effective_buffer_params_.offset; + work_tile.stride = effective_buffer_params_.stride; + + CPUKernelThreadGlobals *kernel_globals = kernel_thread_globals_get(kernel_thread_globals_); + + render_samples_full_pipeline(kernel_globals, work_tile, samples_num); + }); + }); + + for (CPUKernelThreadGlobals &kernel_globals : kernel_thread_globals_) { + kernel_globals.stop_profiling(); + } + + statistics.occupancy = 1.0f; +} + +void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_globals, + const KernelWorkTile &work_tile, + const int samples_num) +{ + const bool has_shadow_catcher = device_scene_->data.integrator.has_shadow_catcher; + const bool has_bake = device_scene_->data.bake.use; + + IntegratorStateCPU integrator_states[2] = {}; + + IntegratorStateCPU *state = &integrator_states[0]; + IntegratorStateCPU *shadow_catcher_state = &integrator_states[1]; + + KernelWorkTile sample_work_tile = work_tile; + float *render_buffer = buffers_->buffer.data(); + + for (int sample = 0; sample < samples_num; ++sample) { + if (is_cancel_requested()) { + break; + } + + if (has_bake) { + if (!kernels_.integrator_init_from_bake( + kernel_globals, state, &sample_work_tile, render_buffer)) { + break; + } + } + else { + if (!kernels_.integrator_init_from_camera( + kernel_globals, state, &sample_work_tile, render_buffer)) { + break; + } + } + + kernels_.integrator_megakernel(kernel_globals, state, render_buffer); + + if (has_shadow_catcher) { + kernels_.integrator_megakernel(kernel_globals, shadow_catcher_state, render_buffer); + } + + ++sample_work_tile.start_sample; + } +} + +void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display, + PassMode pass_mode, + int num_samples) +{ + half4 *rgba_half = gpu_display->map_texture_buffer(); + if (!rgba_half) { + /* TODO(sergey): Look into using copy_to_gpu_display() if mapping failed. Might be needed for + * some implementations of GPUDisplay which can not map memory? */ + return; + } + + const KernelFilm &kfilm = device_scene_->data.film; + + const PassAccessor::PassAccessInfo pass_access_info = get_display_pass_access_info(pass_mode); + + const PassAccessorCPU pass_accessor(pass_access_info, kfilm.exposure, num_samples); + + PassAccessor::Destination destination = get_gpu_display_destination_template(gpu_display); + destination.pixels_half_rgba = rgba_half; + + tbb::task_arena local_arena = local_tbb_arena_create(device_); + local_arena.execute([&]() { + pass_accessor.get_render_tile_pixels(buffers_.get(), effective_buffer_params_, destination); + }); + + gpu_display->unmap_texture_buffer(); +} + +void PathTraceWorkCPU::destroy_gpu_resources(GPUDisplay * /*gpu_display*/) +{ +} + +bool PathTraceWorkCPU::copy_render_buffers_from_device() +{ + return buffers_->copy_from_device(); +} + +bool PathTraceWorkCPU::copy_render_buffers_to_device() +{ + buffers_->buffer.copy_to_device(); + return true; +} + +bool PathTraceWorkCPU::zero_render_buffers() +{ + buffers_->zero(); + return true; +} + +int PathTraceWorkCPU::adaptive_sampling_converge_filter_count_active(float threshold, bool reset) +{ + const int full_x = effective_buffer_params_.full_x; + const int full_y = effective_buffer_params_.full_y; + const int width = effective_buffer_params_.width; + const int height = effective_buffer_params_.height; + const int offset = effective_buffer_params_.offset; + const int stride = effective_buffer_params_.stride; + + float *render_buffer = buffers_->buffer.data(); + + uint num_active_pixels = 0; + + tbb::task_arena local_arena = local_tbb_arena_create(device_); + + /* Check convergency and do x-filter in a single `parallel_for`, to reduce threading overhead. */ + local_arena.execute([&]() { + tbb::parallel_for(full_y, full_y + height, [&](int y) { + CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0]; + + bool row_converged = true; + uint num_row_pixels_active = 0; + for (int x = 0; x < width; ++x) { + if (!kernels_.adaptive_sampling_convergence_check( + kernel_globals, render_buffer, full_x + x, y, threshold, reset, offset, stride)) { + ++num_row_pixels_active; + row_converged = false; + } + } + + atomic_fetch_and_add_uint32(&num_active_pixels, num_row_pixels_active); + + if (!row_converged) { + kernels_.adaptive_sampling_filter_x( + kernel_globals, render_buffer, y, full_x, width, offset, stride); + } + }); + }); + + if (num_active_pixels) { + local_arena.execute([&]() { + tbb::parallel_for(full_x, full_x + width, [&](int x) { + CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0]; + kernels_.adaptive_sampling_filter_y( + kernel_globals, render_buffer, x, full_y, height, offset, stride); + }); + }); + } + + return num_active_pixels; +} + +void PathTraceWorkCPU::cryptomatte_postproces() +{ + const int width = effective_buffer_params_.width; + const int height = effective_buffer_params_.height; + + float *render_buffer = buffers_->buffer.data(); + + tbb::task_arena local_arena = local_tbb_arena_create(device_); + + /* Check convergency and do x-filter in a single `parallel_for`, to reduce threading overhead. */ + local_arena.execute([&]() { + tbb::parallel_for(0, height, [&](int y) { + CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0]; + int pixel_index = y * width; + + for (int x = 0; x < width; ++x, ++pixel_index) { + kernels_.cryptomatte_postprocess(kernel_globals, render_buffer, pixel_index); + } + }); + }); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work_cpu.h b/intern/cycles/integrator/path_trace_work_cpu.h new file mode 100644 index 00000000000..ab729bbf879 --- /dev/null +++ b/intern/cycles/integrator/path_trace_work_cpu.h @@ -0,0 +1,82 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/integrator_state.h" + +#include "device/cpu/kernel_thread_globals.h" +#include "device/device_queue.h" + +#include "integrator/path_trace_work.h" + +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +struct KernelWorkTile; +struct KernelGlobals; + +class CPUKernels; + +/* Implementation of PathTraceWork which schedules work on to queues pixel-by-pixel, + * for CPU devices. + * + * NOTE: For the CPU rendering there are assumptions about TBB arena size and number of concurrent + * queues on the render device which makes this work be only usable on CPU. */ +class PathTraceWorkCPU : public PathTraceWork { + public: + PathTraceWorkCPU(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag); + + virtual void init_execution() override; + + virtual void render_samples(RenderStatistics &statistics, + int start_sample, + int samples_num) override; + + virtual void copy_to_gpu_display(GPUDisplay *gpu_display, + PassMode pass_mode, + int num_samples) override; + virtual void destroy_gpu_resources(GPUDisplay *gpu_display) override; + + virtual bool copy_render_buffers_from_device() override; + virtual bool copy_render_buffers_to_device() override; + virtual bool zero_render_buffers() override; + + virtual int adaptive_sampling_converge_filter_count_active(float threshold, bool reset) override; + virtual void cryptomatte_postproces() override; + + protected: + /* Core path tracing routine. Renders given work time on the given queue. */ + void render_samples_full_pipeline(KernelGlobals *kernel_globals, + const KernelWorkTile &work_tile, + const int samples_num); + + /* CPU kernels. */ + const CPUKernels &kernels_; + + /* Copy of kernel globals which is suitable for concurrent access from multiple threads. + * + * More specifically, the `kernel_globals_` is local to each threads and nobody else is + * accessing it, but some "localization" is required to decouple from kernel globals stored + * on the device level. */ + vector kernel_thread_globals_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp new file mode 100644 index 00000000000..10baf869aa6 --- /dev/null +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -0,0 +1,933 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/path_trace_work_gpu.h" + +#include "device/device.h" + +#include "integrator/pass_accessor_gpu.h" +#include "render/buffers.h" +#include "render/gpu_display.h" +#include "render/scene.h" +#include "util/util_logging.h" +#include "util/util_tbb.h" +#include "util/util_time.h" + +#include "kernel/kernel_types.h" + +CCL_NAMESPACE_BEGIN + +PathTraceWorkGPU::PathTraceWorkGPU(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag) + : PathTraceWork(device, film, device_scene, cancel_requested_flag), + queue_(device->gpu_queue_create()), + integrator_state_soa_kernel_features_(0), + integrator_queue_counter_(device, "integrator_queue_counter", MEM_READ_WRITE), + integrator_shader_sort_counter_(device, "integrator_shader_sort_counter", MEM_READ_WRITE), + integrator_shader_raytrace_sort_counter_( + device, "integrator_shader_raytrace_sort_counter", MEM_READ_WRITE), + integrator_next_shadow_catcher_path_index_( + device, "integrator_next_shadow_catcher_path_index", MEM_READ_WRITE), + queued_paths_(device, "queued_paths", MEM_READ_WRITE), + num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE), + work_tiles_(device, "work_tiles", MEM_READ_WRITE), + gpu_display_rgba_half_(device, "display buffer half", MEM_READ_WRITE), + max_num_paths_(queue_->num_concurrent_states(sizeof(IntegratorStateCPU))), + min_num_active_paths_(queue_->num_concurrent_busy_states()), + max_active_path_index_(0) +{ + memset(&integrator_state_gpu_, 0, sizeof(integrator_state_gpu_)); + + /* Limit number of active paths to the half of the overall state. This is due to the logic in the + * path compaction which relies on the fact that regeneration does not happen sooner than half of + * the states are available again. */ + min_num_active_paths_ = min(min_num_active_paths_, max_num_paths_ / 2); +} + +void PathTraceWorkGPU::alloc_integrator_soa() +{ + /* IntegrateState allocated as structure of arrays. */ + + /* Check if we already allocated memory for the required features. */ + const uint kernel_features = device_scene_->data.kernel_features; + if ((integrator_state_soa_kernel_features_ & kernel_features) == kernel_features) { + return; + } + integrator_state_soa_kernel_features_ = kernel_features; + + /* Allocate a device only memory buffer before for each struct member, and then + * write the pointers into a struct that resides in constant memory. + * + * TODO: store float3 in separate XYZ arrays. */ +#define KERNEL_STRUCT_BEGIN(name) for (int array_index = 0;; array_index++) { +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) \ + if ((kernel_features & feature) && (integrator_state_gpu_.parent_struct.name == nullptr)) { \ + device_only_memory *array = new device_only_memory(device_, \ + "integrator_state_" #name); \ + array->alloc_to_device(max_num_paths_); \ + integrator_state_soa_.emplace_back(array); \ + integrator_state_gpu_.parent_struct.name = (type *)array->device_pointer; \ + } +#define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) \ + if ((kernel_features & feature) && \ + (integrator_state_gpu_.parent_struct[array_index].name == nullptr)) { \ + device_only_memory *array = new device_only_memory(device_, \ + "integrator_state_" #name); \ + array->alloc_to_device(max_num_paths_); \ + integrator_state_soa_.emplace_back(array); \ + integrator_state_gpu_.parent_struct[array_index].name = (type *)array->device_pointer; \ + } +#define KERNEL_STRUCT_END(name) \ + break; \ + } +#define KERNEL_STRUCT_END_ARRAY(name, array_size) \ + if (array_index == array_size - 1) { \ + break; \ + } \ + } +#include "kernel/integrator/integrator_state_template.h" +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_ARRAY_MEMBER +#undef KERNEL_STRUCT_END +#undef KERNEL_STRUCT_END_ARRAY +} + +void PathTraceWorkGPU::alloc_integrator_queue() +{ + if (integrator_queue_counter_.size() == 0) { + integrator_queue_counter_.alloc(1); + integrator_queue_counter_.zero_to_device(); + integrator_queue_counter_.copy_from_device(); + integrator_state_gpu_.queue_counter = (IntegratorQueueCounter *) + integrator_queue_counter_.device_pointer; + } + + /* Allocate data for active path index arrays. */ + if (num_queued_paths_.size() == 0) { + num_queued_paths_.alloc(1); + num_queued_paths_.zero_to_device(); + } + + if (queued_paths_.size() == 0) { + queued_paths_.alloc(max_num_paths_); + /* TODO: this could be skip if we had a function to just allocate on device. */ + queued_paths_.zero_to_device(); + } +} + +void PathTraceWorkGPU::alloc_integrator_sorting() +{ + /* Allocate arrays for shader sorting. */ + const int max_shaders = device_scene_->data.max_shaders; + if (integrator_shader_sort_counter_.size() < max_shaders) { + integrator_shader_sort_counter_.alloc(max_shaders); + integrator_shader_sort_counter_.zero_to_device(); + + integrator_shader_raytrace_sort_counter_.alloc(max_shaders); + integrator_shader_raytrace_sort_counter_.zero_to_device(); + + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE] = + (int *)integrator_shader_sort_counter_.device_pointer; + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE] = + (int *)integrator_shader_raytrace_sort_counter_.device_pointer; + } +} + +void PathTraceWorkGPU::alloc_integrator_path_split() +{ + if (integrator_next_shadow_catcher_path_index_.size() != 0) { + return; + } + + integrator_next_shadow_catcher_path_index_.alloc(1); + /* TODO(sergey): Use queue? */ + integrator_next_shadow_catcher_path_index_.zero_to_device(); + + integrator_state_gpu_.next_shadow_catcher_path_index = + (int *)integrator_next_shadow_catcher_path_index_.device_pointer; +} + +void PathTraceWorkGPU::alloc_work_memory() +{ + alloc_integrator_soa(); + alloc_integrator_queue(); + alloc_integrator_sorting(); + alloc_integrator_path_split(); +} + +void PathTraceWorkGPU::init_execution() +{ + queue_->init_execution(); + + /* Copy to device side struct in constant memory. */ + device_->const_copy_to( + "__integrator_state", &integrator_state_gpu_, sizeof(integrator_state_gpu_)); +} + +void PathTraceWorkGPU::render_samples(RenderStatistics &statistics, + int start_sample, + int samples_num) +{ + /* Limit number of states for the tile and rely on a greedy scheduling of tiles. This allows to + * add more work (because tiles are smaller, so there is higher chance that more paths will + * become busy after adding new tiles). This is especially important for the shadow catcher which + * schedules work in halves of available number of paths. */ + work_tile_scheduler_.set_max_num_path_states(max_num_paths_ / 8); + + work_tile_scheduler_.reset(effective_buffer_params_, start_sample, samples_num); + + enqueue_reset(); + + int num_iterations = 0; + uint64_t num_busy_accum = 0; + + /* TODO: set a hard limit in case of undetected kernel failures? */ + while (true) { + /* Enqueue work from the scheduler, on start or when there are not enough + * paths to keep the device occupied. */ + bool finished; + if (enqueue_work_tiles(finished)) { + /* Copy stats from the device. */ + queue_->copy_from_device(integrator_queue_counter_); + + if (!queue_->synchronize()) { + break; /* Stop on error. */ + } + } + + if (is_cancel_requested()) { + break; + } + + /* Stop if no more work remaining. */ + if (finished) { + break; + } + + /* Enqueue on of the path iteration kernels. */ + if (enqueue_path_iteration()) { + /* Copy stats from the device. */ + queue_->copy_from_device(integrator_queue_counter_); + + if (!queue_->synchronize()) { + break; /* Stop on error. */ + } + } + + if (is_cancel_requested()) { + break; + } + + num_busy_accum += get_num_active_paths(); + ++num_iterations; + } + + statistics.occupancy = static_cast(num_busy_accum) / num_iterations / max_num_paths_; +} + +DeviceKernel PathTraceWorkGPU::get_most_queued_kernel() const +{ + const IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); + + int max_num_queued = 0; + DeviceKernel kernel = DEVICE_KERNEL_NUM; + + for (int i = 0; i < DEVICE_KERNEL_INTEGRATOR_NUM; i++) { + if (queue_counter->num_queued[i] > max_num_queued) { + kernel = (DeviceKernel)i; + max_num_queued = queue_counter->num_queued[i]; + } + } + + return kernel; +} + +void PathTraceWorkGPU::enqueue_reset() +{ + void *args[] = {&max_num_paths_}; + queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_RESET, max_num_paths_, args); + queue_->zero_to_device(integrator_queue_counter_); + queue_->zero_to_device(integrator_shader_sort_counter_); + queue_->zero_to_device(integrator_shader_raytrace_sort_counter_); + + /* Tiles enqueue need to know number of active paths, which is based on this counter. Zero the + * counter on the host side because `zero_to_device()` is not doing it. */ + if (integrator_queue_counter_.host_pointer) { + memset(integrator_queue_counter_.data(), 0, integrator_queue_counter_.memory_size()); + } +} + +bool PathTraceWorkGPU::enqueue_path_iteration() +{ + /* Find kernel to execute, with max number of queued paths. */ + const IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); + + int num_active_paths = 0; + for (int i = 0; i < DEVICE_KERNEL_INTEGRATOR_NUM; i++) { + num_active_paths += queue_counter->num_queued[i]; + } + + if (num_active_paths == 0) { + return false; + } + + /* Find kernel to execute, with max number of queued paths. */ + const DeviceKernel kernel = get_most_queued_kernel(); + if (kernel == DEVICE_KERNEL_NUM) { + return false; + } + + /* Finish shadows before potentially adding more shadow rays. We can only + * store one shadow ray in the integrator state. */ + if (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME) { + if (queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW]) { + enqueue_path_iteration(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + return true; + } + else if (queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW]) { + enqueue_path_iteration(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + return true; + } + } + + /* Schedule kernel with maximum number of queued items. */ + enqueue_path_iteration(kernel); + return true; +} + +void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel) +{ + void *d_path_index = (void *)NULL; + + /* Create array of path indices for which this kernel is queued to be executed. */ + int work_size = max_active_path_index_; + + IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); + int num_queued = queue_counter->num_queued[kernel]; + + if (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) { + /* Compute array of active paths, sorted by shader. */ + work_size = num_queued; + d_path_index = (void *)queued_paths_.device_pointer; + + compute_sorted_queued_paths(DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY, kernel); + } + else if (num_queued < work_size) { + work_size = num_queued; + d_path_index = (void *)queued_paths_.device_pointer; + + if (kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW) { + /* Compute array of active shadow paths for specific kernel. */ + compute_queued_paths(DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY, kernel); + } + else { + /* Compute array of active paths for specific kernel. */ + compute_queued_paths(DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY, kernel); + } + } + + DCHECK_LE(work_size, max_num_paths_); + + switch (kernel) { + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE: + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: { + /* Ray intersection kernels with integrator state. */ + void *args[] = {&d_path_index, const_cast(&work_size)}; + + queue_->enqueue(kernel, work_size, args); + break; + } + case DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND: + case DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT: + case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE: + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE: + case DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME: { + /* Shading kernels with integrator state and render buffer. */ + void *d_render_buffer = (void *)buffers_->buffer.device_pointer; + void *args[] = {&d_path_index, &d_render_buffer, const_cast(&work_size)}; + + queue_->enqueue(kernel, work_size, args); + break; + } + + default: + LOG(FATAL) << "Unhandled kernel " << device_kernel_as_string(kernel) + << " used for path iteration, should never happen."; + break; + } +} + +void PathTraceWorkGPU::compute_sorted_queued_paths(DeviceKernel kernel, DeviceKernel queued_kernel) +{ + int d_queued_kernel = queued_kernel; + void *d_counter = integrator_state_gpu_.sort_key_counter[d_queued_kernel]; + assert(d_counter != nullptr); + + /* Compute prefix sum of number of active paths with each shader. */ + { + const int work_size = 1; + int max_shaders = device_scene_->data.max_shaders; + void *args[] = {&d_counter, &max_shaders}; + queue_->enqueue(DEVICE_KERNEL_PREFIX_SUM, work_size, args); + } + + queue_->zero_to_device(num_queued_paths_); + + /* Launch kernel to fill the active paths arrays. */ + { + /* TODO: this could be smaller for terminated paths based on amount of work we want + * to schedule. */ + const int work_size = max_active_path_index_; + + void *d_queued_paths = (void *)queued_paths_.device_pointer; + void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; + void *args[] = {const_cast(&work_size), + &d_queued_paths, + &d_num_queued_paths, + &d_counter, + &d_queued_kernel}; + + queue_->enqueue(kernel, work_size, args); + } + + if (queued_kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE) { + queue_->zero_to_device(integrator_shader_sort_counter_); + } + else if (queued_kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) { + queue_->zero_to_device(integrator_shader_raytrace_sort_counter_); + } + else { + assert(0); + } +} + +void PathTraceWorkGPU::compute_queued_paths(DeviceKernel kernel, DeviceKernel queued_kernel) +{ + int d_queued_kernel = queued_kernel; + + /* Launch kernel to fill the active paths arrays. */ + const int work_size = max_active_path_index_; + void *d_queued_paths = (void *)queued_paths_.device_pointer; + void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; + void *args[] = { + const_cast(&work_size), &d_queued_paths, &d_num_queued_paths, &d_queued_kernel}; + + queue_->zero_to_device(num_queued_paths_); + queue_->enqueue(kernel, work_size, args); +} + +void PathTraceWorkGPU::compact_states(const int num_active_paths) +{ + if (num_active_paths == 0) { + max_active_path_index_ = 0; + } + + /* Compact fragmented path states into the start of the array, moving any paths + * with index higher than the number of active paths into the gaps. */ + if (max_active_path_index_ == num_active_paths) { + return; + } + + void *d_compact_paths = (void *)queued_paths_.device_pointer; + void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; + + /* Create array with terminated paths that we can write to. */ + { + /* TODO: can the work size be reduced here? */ + int offset = num_active_paths; + int work_size = num_active_paths; + void *args[] = {&work_size, &d_compact_paths, &d_num_queued_paths, &offset}; + queue_->zero_to_device(num_queued_paths_); + queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY, work_size, args); + } + + /* Create array of paths that we need to compact, where the path index is bigger + * than the number of active paths. */ + { + int work_size = max_active_path_index_; + void *args[] = { + &work_size, &d_compact_paths, &d_num_queued_paths, const_cast(&num_active_paths)}; + queue_->zero_to_device(num_queued_paths_); + queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY, work_size, args); + } + + queue_->copy_from_device(num_queued_paths_); + queue_->synchronize(); + + int num_compact_paths = num_queued_paths_.data()[0]; + + /* Move paths into gaps. */ + if (num_compact_paths > 0) { + int work_size = num_compact_paths; + int active_states_offset = 0; + int terminated_states_offset = num_active_paths; + void *args[] = { + &d_compact_paths, &active_states_offset, &terminated_states_offset, &work_size}; + queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES, work_size, args); + } + + queue_->synchronize(); + + /* Adjust max active path index now we know which part of the array is actually used. */ + max_active_path_index_ = num_active_paths; +} + +bool PathTraceWorkGPU::enqueue_work_tiles(bool &finished) +{ + /* If there are existing paths wait them to go to intersect closest kernel, which will align the + * wavefront of the existing and newely added paths. */ + /* TODO: Check whether counting new intersection kernels here will have positive affect on the + * performance. */ + const DeviceKernel kernel = get_most_queued_kernel(); + if (kernel != DEVICE_KERNEL_NUM && kernel != DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST) { + return false; + } + + int num_active_paths = get_num_active_paths(); + + /* Don't schedule more work if cancelling. */ + if (is_cancel_requested()) { + if (num_active_paths == 0) { + finished = true; + } + return false; + } + + finished = false; + + vector work_tiles; + + int max_num_camera_paths = max_num_paths_; + int num_predicted_splits = 0; + + if (has_shadow_catcher()) { + /* When there are shadow catchers in the scene bounce from them will split the state. So we + * make sure there is enough space in the path states array to fit split states. + * + * Basically, when adding N new paths we ensure that there is 2*N available path states, so + * that all the new paths can be split. + * + * Note that it is possible that some of the current states can still split, so need to make + * sure there is enough space for them as well. */ + + /* Number of currently in-flight states which can still split. */ + const int num_scheduled_possible_split = shadow_catcher_count_possible_splits(); + + const int num_available_paths = max_num_paths_ - num_active_paths; + const int num_new_paths = num_available_paths / 2; + max_num_camera_paths = max(num_active_paths, + num_active_paths + num_new_paths - num_scheduled_possible_split); + num_predicted_splits += num_scheduled_possible_split + num_new_paths; + } + + /* Schedule when we're out of paths or there are too few paths to keep the + * device occupied. */ + int num_paths = num_active_paths; + if (num_paths == 0 || num_paths < min_num_active_paths_) { + /* Get work tiles until the maximum number of path is reached. */ + while (num_paths < max_num_camera_paths) { + KernelWorkTile work_tile; + if (work_tile_scheduler_.get_work(&work_tile, max_num_camera_paths - num_paths)) { + work_tiles.push_back(work_tile); + num_paths += work_tile.w * work_tile.h * work_tile.num_samples; + } + else { + break; + } + } + + /* If we couldn't get any more tiles, we're done. */ + if (work_tiles.size() == 0 && num_paths == 0) { + finished = true; + return false; + } + } + + /* Initialize paths from work tiles. */ + if (work_tiles.size() == 0) { + return false; + } + + /* Compact state array when number of paths becomes small relative to the + * known maximum path index, which makes computing active index arrays slow. */ + compact_states(num_active_paths); + + if (has_shadow_catcher()) { + integrator_next_shadow_catcher_path_index_.data()[0] = num_paths; + queue_->copy_to_device(integrator_next_shadow_catcher_path_index_); + } + + enqueue_work_tiles((device_scene_->data.bake.use) ? DEVICE_KERNEL_INTEGRATOR_INIT_FROM_BAKE : + DEVICE_KERNEL_INTEGRATOR_INIT_FROM_CAMERA, + work_tiles.data(), + work_tiles.size(), + num_active_paths, + num_predicted_splits); + + return true; +} + +void PathTraceWorkGPU::enqueue_work_tiles(DeviceKernel kernel, + const KernelWorkTile work_tiles[], + const int num_work_tiles, + const int num_active_paths, + const int num_predicted_splits) +{ + /* Copy work tiles to device. */ + if (work_tiles_.size() < num_work_tiles) { + work_tiles_.alloc(num_work_tiles); + } + + int path_index_offset = num_active_paths; + int max_tile_work_size = 0; + for (int i = 0; i < num_work_tiles; i++) { + KernelWorkTile &work_tile = work_tiles_.data()[i]; + work_tile = work_tiles[i]; + + const int tile_work_size = work_tile.w * work_tile.h * work_tile.num_samples; + + work_tile.path_index_offset = path_index_offset; + work_tile.work_size = tile_work_size; + + path_index_offset += tile_work_size; + + max_tile_work_size = max(max_tile_work_size, tile_work_size); + } + + queue_->copy_to_device(work_tiles_); + + void *d_work_tiles = (void *)work_tiles_.device_pointer; + void *d_render_buffer = (void *)buffers_->buffer.device_pointer; + + /* Launch kernel. */ + void *args[] = {&d_work_tiles, + const_cast(&num_work_tiles), + &d_render_buffer, + const_cast(&max_tile_work_size)}; + + queue_->enqueue(kernel, max_tile_work_size * num_work_tiles, args); + + max_active_path_index_ = path_index_offset + num_predicted_splits; +} + +int PathTraceWorkGPU::get_num_active_paths() +{ + /* TODO: this is wrong, does not account for duplicates with shadow! */ + IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); + + int num_paths = 0; + for (int i = 0; i < DEVICE_KERNEL_INTEGRATOR_NUM; i++) { + DCHECK_GE(queue_counter->num_queued[i], 0) + << "Invalid number of queued states for kernel " + << device_kernel_as_string(static_cast(i)); + num_paths += queue_counter->num_queued[i]; + } + + return num_paths; +} + +bool PathTraceWorkGPU::should_use_graphics_interop() +{ + /* There are few aspects with the graphics interop when using multiple devices caused by the fact + * that the GPUDisplay has a single texture: + * + * CUDA will return `CUDA_ERROR_NOT_SUPPORTED` from `cuGraphicsGLRegisterBuffer()` when + * attempting to register OpenGL PBO which has been mapped. Which makes sense, because + * otherwise one would run into a conflict of where the source of truth is. */ + if (has_multiple_works()) { + return false; + } + + if (!interop_use_checked_) { + Device *device = queue_->device; + interop_use_ = device->should_use_graphics_interop(); + + if (interop_use_) { + VLOG(2) << "Will be using graphics interop GPU display update."; + } + else { + VLOG(2) << "Will be using naive GPU display update."; + } + + interop_use_checked_ = true; + } + + return interop_use_; +} + +void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display, + PassMode pass_mode, + int num_samples) +{ + if (device_->have_error()) { + /* Don't attempt to update GPU display if the device has errors: the error state will make + * wrong decisions to happen about interop, causing more chained bugs. */ + return; + } + + if (!buffers_->buffer.device_pointer) { + LOG(WARNING) << "Request for GPU display update without allocated render buffers."; + return; + } + + if (should_use_graphics_interop()) { + if (copy_to_gpu_display_interop(gpu_display, pass_mode, num_samples)) { + return; + } + + /* If error happens when trying to use graphics interop fallback to the native implementation + * and don't attempt to use interop for the further updates. */ + interop_use_ = false; + } + + copy_to_gpu_display_naive(gpu_display, pass_mode, num_samples); +} + +void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display, + PassMode pass_mode, + int num_samples) +{ + const int full_x = effective_buffer_params_.full_x; + const int full_y = effective_buffer_params_.full_y; + const int width = effective_buffer_params_.width; + const int height = effective_buffer_params_.height; + const int final_width = buffers_->params.width; + const int final_height = buffers_->params.height; + + const int texture_x = full_x - effective_full_params_.full_x; + const int texture_y = full_y - effective_full_params_.full_y; + + /* Re-allocate display memory if needed, and make sure the device pointer is allocated. + * + * NOTE: allocation happens to the final resolution so that no re-allocation happens on every + * change of the resolution divider. However, if the display becomes smaller, shrink the + * allocated memory as well. */ + if (gpu_display_rgba_half_.data_width != final_width || + gpu_display_rgba_half_.data_height != final_height) { + gpu_display_rgba_half_.alloc(final_width, final_height); + /* TODO(sergey): There should be a way to make sure device-side memory is allocated without + * transfering zeroes to the device. */ + queue_->zero_to_device(gpu_display_rgba_half_); + } + + PassAccessor::Destination destination(film_->get_display_pass()); + destination.d_pixels_half_rgba = gpu_display_rgba_half_.device_pointer; + + get_render_tile_film_pixels(destination, pass_mode, num_samples); + + gpu_display_rgba_half_.copy_from_device(); + + gpu_display->copy_pixels_to_texture( + gpu_display_rgba_half_.data(), texture_x, texture_y, width, height); +} + +bool PathTraceWorkGPU::copy_to_gpu_display_interop(GPUDisplay *gpu_display, + PassMode pass_mode, + int num_samples) +{ + if (!device_graphics_interop_) { + device_graphics_interop_ = queue_->graphics_interop_create(); + } + + const DeviceGraphicsInteropDestination graphics_interop_dst = + gpu_display->graphics_interop_get(); + device_graphics_interop_->set_destination(graphics_interop_dst); + + const device_ptr d_rgba_half = device_graphics_interop_->map(); + if (!d_rgba_half) { + return false; + } + + PassAccessor::Destination destination = get_gpu_display_destination_template(gpu_display); + destination.d_pixels_half_rgba = d_rgba_half; + + get_render_tile_film_pixels(destination, pass_mode, num_samples); + + device_graphics_interop_->unmap(); + + return true; +} + +void PathTraceWorkGPU::destroy_gpu_resources(GPUDisplay *gpu_display) +{ + if (!device_graphics_interop_) { + return; + } + gpu_display->graphics_interop_activate(); + device_graphics_interop_ = nullptr; + gpu_display->graphics_interop_deactivate(); +} + +void PathTraceWorkGPU::get_render_tile_film_pixels(const PassAccessor::Destination &destination, + PassMode pass_mode, + int num_samples) +{ + const KernelFilm &kfilm = device_scene_->data.film; + + const PassAccessor::PassAccessInfo pass_access_info = get_display_pass_access_info(pass_mode); + const PassAccessorGPU pass_accessor(queue_.get(), pass_access_info, kfilm.exposure, num_samples); + + pass_accessor.get_render_tile_pixels(buffers_.get(), effective_buffer_params_, destination); +} + +int PathTraceWorkGPU::adaptive_sampling_converge_filter_count_active(float threshold, bool reset) +{ + const int num_active_pixels = adaptive_sampling_convergence_check_count_active(threshold, reset); + + if (num_active_pixels) { + enqueue_adaptive_sampling_filter_x(); + enqueue_adaptive_sampling_filter_y(); + queue_->synchronize(); + } + + return num_active_pixels; +} + +int PathTraceWorkGPU::adaptive_sampling_convergence_check_count_active(float threshold, bool reset) +{ + device_vector num_active_pixels(device_, "num_active_pixels", MEM_READ_WRITE); + num_active_pixels.alloc(1); + + queue_->zero_to_device(num_active_pixels); + + const int work_size = effective_buffer_params_.width * effective_buffer_params_.height; + + void *args[] = {&buffers_->buffer.device_pointer, + const_cast(&effective_buffer_params_.full_x), + const_cast(&effective_buffer_params_.full_y), + const_cast(&effective_buffer_params_.width), + const_cast(&effective_buffer_params_.height), + &threshold, + &reset, + &effective_buffer_params_.offset, + &effective_buffer_params_.stride, + &num_active_pixels.device_pointer}; + + queue_->enqueue(DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_CHECK, work_size, args); + + queue_->copy_from_device(num_active_pixels); + queue_->synchronize(); + + return num_active_pixels.data()[0]; +} + +void PathTraceWorkGPU::enqueue_adaptive_sampling_filter_x() +{ + const int work_size = effective_buffer_params_.height; + + void *args[] = {&buffers_->buffer.device_pointer, + &effective_buffer_params_.full_x, + &effective_buffer_params_.full_y, + &effective_buffer_params_.width, + &effective_buffer_params_.height, + &effective_buffer_params_.offset, + &effective_buffer_params_.stride}; + + queue_->enqueue(DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_X, work_size, args); +} + +void PathTraceWorkGPU::enqueue_adaptive_sampling_filter_y() +{ + const int work_size = effective_buffer_params_.width; + + void *args[] = {&buffers_->buffer.device_pointer, + &effective_buffer_params_.full_x, + &effective_buffer_params_.full_y, + &effective_buffer_params_.width, + &effective_buffer_params_.height, + &effective_buffer_params_.offset, + &effective_buffer_params_.stride}; + + queue_->enqueue(DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_Y, work_size, args); +} + +void PathTraceWorkGPU::cryptomatte_postproces() +{ + const int work_size = effective_buffer_params_.width * effective_buffer_params_.height; + + void *args[] = {&buffers_->buffer.device_pointer, + const_cast(&work_size), + &effective_buffer_params_.offset, + &effective_buffer_params_.stride}; + + queue_->enqueue(DEVICE_KERNEL_CRYPTOMATTE_POSTPROCESS, work_size, args); +} + +bool PathTraceWorkGPU::copy_render_buffers_from_device() +{ + queue_->copy_from_device(buffers_->buffer); + + /* Synchronize so that the CPU-side buffer is available at the exit of this function. */ + return queue_->synchronize(); +} + +bool PathTraceWorkGPU::copy_render_buffers_to_device() +{ + queue_->copy_to_device(buffers_->buffer); + + /* NOTE: The direct device access to the buffers only happens within this path trace work. The + * rest of communication happens via API calls which involves `copy_render_buffers_from_device()` + * which will perform synchronization as needed. */ + + return true; +} + +bool PathTraceWorkGPU::zero_render_buffers() +{ + queue_->zero_to_device(buffers_->buffer); + + return true; +} + +bool PathTraceWorkGPU::has_shadow_catcher() const +{ + return device_scene_->data.integrator.has_shadow_catcher; +} + +int PathTraceWorkGPU::shadow_catcher_count_possible_splits() +{ + if (max_active_path_index_ == 0) { + return 0; + } + + if (!has_shadow_catcher()) { + return 0; + } + + queue_->zero_to_device(num_queued_paths_); + + const int work_size = max_active_path_index_; + void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; + void *args[] = {const_cast(&work_size), &d_num_queued_paths}; + + queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_SHADOW_CATCHER_COUNT_POSSIBLE_SPLITS, work_size, args); + queue_->copy_from_device(num_queued_paths_); + queue_->synchronize(); + + return num_queued_paths_.data()[0]; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h new file mode 100644 index 00000000000..38788122b0d --- /dev/null +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -0,0 +1,165 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/integrator_state.h" + +#include "device/device_graphics_interop.h" +#include "device/device_memory.h" +#include "device/device_queue.h" + +#include "integrator/path_trace_work.h" +#include "integrator/work_tile_scheduler.h" + +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +struct KernelWorkTile; + +/* Implementation of PathTraceWork which schedules work to the device in tiles which are sized + * to match device queue's number of path states. + * This implementation suits best devices which have a lot of integrator states, such as GPU. */ +class PathTraceWorkGPU : public PathTraceWork { + public: + PathTraceWorkGPU(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag); + + virtual void alloc_work_memory() override; + virtual void init_execution() override; + + virtual void render_samples(RenderStatistics &statistics, + int start_sample, + int samples_num) override; + + virtual void copy_to_gpu_display(GPUDisplay *gpu_display, + PassMode pass_mode, + int num_samples) override; + virtual void destroy_gpu_resources(GPUDisplay *gpu_display) override; + + virtual bool copy_render_buffers_from_device() override; + virtual bool copy_render_buffers_to_device() override; + virtual bool zero_render_buffers() override; + + virtual int adaptive_sampling_converge_filter_count_active(float threshold, bool reset) override; + virtual void cryptomatte_postproces() override; + + protected: + void alloc_integrator_soa(); + void alloc_integrator_queue(); + void alloc_integrator_sorting(); + void alloc_integrator_path_split(); + + /* Returns DEVICE_KERNEL_NUM if there are no scheduled kernels. */ + DeviceKernel get_most_queued_kernel() const; + + void enqueue_reset(); + + bool enqueue_work_tiles(bool &finished); + void enqueue_work_tiles(DeviceKernel kernel, + const KernelWorkTile work_tiles[], + const int num_work_tiles, + const int num_active_paths, + const int num_predicted_splits); + + bool enqueue_path_iteration(); + void enqueue_path_iteration(DeviceKernel kernel); + + void compute_queued_paths(DeviceKernel kernel, DeviceKernel queued_kernel); + void compute_sorted_queued_paths(DeviceKernel kernel, DeviceKernel queued_kernel); + + void compact_states(const int num_active_paths); + + int get_num_active_paths(); + + /* Check whether graphics interop can be used for the GPUDisplay update. */ + bool should_use_graphics_interop(); + + /* Naive implementation of the `copy_to_gpu_display()` which performs film conversion on the + * device, then copies pixels to the host and pushes them to the `gpu_display`. */ + void copy_to_gpu_display_naive(GPUDisplay *gpu_display, PassMode pass_mode, int num_samples); + + /* Implementation of `copy_to_gpu_display()` which uses driver's OpenGL/GPU interoperability + * functionality, avoiding copy of pixels to the host. */ + bool copy_to_gpu_display_interop(GPUDisplay *gpu_display, PassMode pass_mode, int num_samples); + + /* Synchronously run film conversion kernel and store display result in the given destination. */ + void get_render_tile_film_pixels(const PassAccessor::Destination &destination, + PassMode pass_mode, + int num_samples); + + int adaptive_sampling_convergence_check_count_active(float threshold, bool reset); + void enqueue_adaptive_sampling_filter_x(); + void enqueue_adaptive_sampling_filter_y(); + + bool has_shadow_catcher() const; + + /* Count how many currently scheduled paths can still split. */ + int shadow_catcher_count_possible_splits(); + + /* Integrator queue. */ + unique_ptr queue_; + + /* Scheduler which gives work to path tracing threads. */ + WorkTileScheduler work_tile_scheduler_; + + /* Integrate state for paths. */ + IntegratorStateGPU integrator_state_gpu_; + /* SoA arrays for integrator state. */ + vector> integrator_state_soa_; + uint integrator_state_soa_kernel_features_; + /* Keep track of number of queued kernels. */ + device_vector integrator_queue_counter_; + /* Shader sorting. */ + device_vector integrator_shader_sort_counter_; + device_vector integrator_shader_raytrace_sort_counter_; + /* Path split. */ + device_vector integrator_next_shadow_catcher_path_index_; + + /* Temporary buffer to get an array of queued path for a particular kernel. */ + device_vector queued_paths_; + device_vector num_queued_paths_; + + /* Temporary buffer for passing work tiles to kernel. */ + device_vector work_tiles_; + + /* Temporary buffer used by the copy_to_gpu_display() whenever graphics interoperability is not + * available. Is allocated on-demand. */ + device_vector gpu_display_rgba_half_; + + unique_ptr device_graphics_interop_; + + /* Cached result of device->should_use_graphics_interop(). */ + bool interop_use_checked_ = false; + bool interop_use_ = false; + + /* Maximum number of concurrent integrator states. */ + int max_num_paths_; + + /* Minimum number of paths which keeps the device bust. If the actual number of paths falls below + * this value more work will be scheduled. */ + int min_num_active_paths_; + + /* Maximum path index, effective number of paths used may be smaller than + * the size of the integrator_state_ buffer so can avoid iterating over the + * full buffer. */ + int max_active_path_index_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp new file mode 100644 index 00000000000..4eb1dd941f9 --- /dev/null +++ b/intern/cycles/integrator/render_scheduler.cpp @@ -0,0 +1,1187 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/render_scheduler.h" + +#include "render/session.h" +#include "render/tile.h" +#include "util/util_logging.h" +#include "util/util_math.h" +#include "util/util_time.h" + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Render scheduler. + */ + +RenderScheduler::RenderScheduler(TileManager &tile_manager, const SessionParams ¶ms) + : headless_(params.headless), + background_(params.background), + pixel_size_(params.pixel_size), + tile_manager_(tile_manager), + default_start_resolution_divider_(pixel_size_ * 8) +{ + use_progressive_noise_floor_ = !background_; +} + +void RenderScheduler::set_need_schedule_cryptomatte(bool need_schedule_cryptomatte) +{ + need_schedule_cryptomatte_ = need_schedule_cryptomatte; +} + +void RenderScheduler::set_need_schedule_rebalance(bool need_schedule_rebalance) +{ + need_schedule_rebalance_works_ = need_schedule_rebalance; +} + +bool RenderScheduler::is_background() const +{ + return background_; +} + +void RenderScheduler::set_denoiser_params(const DenoiseParams ¶ms) +{ + denoiser_params_ = params; +} + +void RenderScheduler::set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling) +{ + adaptive_sampling_ = adaptive_sampling; +} + +bool RenderScheduler::is_adaptive_sampling_used() const +{ + return adaptive_sampling_.use; +} + +void RenderScheduler::set_start_sample(int start_sample) +{ + start_sample_ = start_sample; +} + +int RenderScheduler::get_start_sample() const +{ + return start_sample_; +} + +void RenderScheduler::set_num_samples(int num_samples) +{ + num_samples_ = num_samples; +} + +int RenderScheduler::get_num_samples() const +{ + return num_samples_; +} + +void RenderScheduler::set_time_limit(double time_limit) +{ + time_limit_ = time_limit; +} + +double RenderScheduler::get_time_limit() const +{ + return time_limit_; +} + +int RenderScheduler::get_rendered_sample() const +{ + DCHECK_GT(get_num_rendered_samples(), 0); + + return start_sample_ + get_num_rendered_samples() - 1; +} + +int RenderScheduler::get_num_rendered_samples() const +{ + return state_.num_rendered_samples; +} + +void RenderScheduler::reset(const BufferParams &buffer_params, int num_samples) +{ + buffer_params_ = buffer_params; + + update_start_resolution_divider(); + + set_num_samples(num_samples); + + /* In background mode never do lower resolution render preview, as it is not really supported + * by the software. */ + if (background_) { + state_.resolution_divider = 1; + } + else { + /* NOTE: Divide by 2 because of the way how scheduling works: it advances resolution divider + * first and then initialized render work. */ + state_.resolution_divider = start_resolution_divider_ * 2; + } + + state_.num_rendered_samples = 0; + state_.last_display_update_time = 0.0; + state_.last_display_update_sample = -1; + + state_.last_rebalance_time = 0.0; + state_.num_rebalance_requested = 0; + state_.num_rebalance_changes = 0; + state_.last_rebalance_changed = false; + state_.need_rebalance_at_next_work = false; + + /* TODO(sergey): Choose better initial value. */ + /* NOTE: The adaptive sampling settings might not be available here yet. */ + state_.adaptive_sampling_threshold = 0.4f; + + state_.last_work_tile_was_denoised = false; + state_.tile_result_was_written = false; + state_.postprocess_work_scheduled = false; + state_.full_frame_work_scheduled = false; + state_.full_frame_was_written = false; + + state_.path_trace_finished = false; + + state_.start_render_time = 0.0; + state_.end_render_time = 0.0; + state_.time_limit_reached = false; + + state_.occupancy_num_samples = 0; + state_.occupancy = 1.0f; + + first_render_time_.path_trace_per_sample = 0.0; + first_render_time_.denoise_time = 0.0; + first_render_time_.display_update_time = 0.0; + + path_trace_time_.reset(); + denoise_time_.reset(); + adaptive_filter_time_.reset(); + display_update_time_.reset(); + rebalance_time_.reset(); +} + +void RenderScheduler::reset_for_next_tile() +{ + reset(buffer_params_, num_samples_); +} + +bool RenderScheduler::render_work_reschedule_on_converge(RenderWork &render_work) +{ + /* Move to the next resolution divider. Assume adaptive filtering is not needed during + * navigation. */ + if (state_.resolution_divider != pixel_size_) { + return false; + } + + if (render_work_reschedule_on_idle(render_work)) { + return true; + } + + state_.path_trace_finished = true; + + bool denoiser_delayed, denoiser_ready_to_display; + render_work.tile.denoise = work_need_denoise(denoiser_delayed, denoiser_ready_to_display); + + render_work.display.update = work_need_update_display(denoiser_delayed); + render_work.display.use_denoised_result = denoiser_ready_to_display; + + return false; +} + +bool RenderScheduler::render_work_reschedule_on_idle(RenderWork &render_work) +{ + if (!use_progressive_noise_floor_) { + return false; + } + + /* Move to the next resolution divider. Assume adaptive filtering is not needed during + * navigation. */ + if (state_.resolution_divider != pixel_size_) { + return false; + } + + if (adaptive_sampling_.use) { + if (state_.adaptive_sampling_threshold > adaptive_sampling_.threshold) { + state_.adaptive_sampling_threshold = max(state_.adaptive_sampling_threshold / 2, + adaptive_sampling_.threshold); + + render_work.adaptive_sampling.threshold = state_.adaptive_sampling_threshold; + render_work.adaptive_sampling.reset = true; + + return true; + } + } + + return false; +} + +void RenderScheduler::render_work_reschedule_on_cancel(RenderWork &render_work) +{ + VLOG(3) << "Schedule work for cancel."; + + /* Un-schedule samples: they will not be rendered and should not be counted. */ + state_.num_rendered_samples -= render_work.path_trace.num_samples; + + const bool has_rendered_samples = get_num_rendered_samples() != 0; + + /* Reset all fields of the previous work, canelling things like adaptive sampling filtering and + * denoising. + * However, need to preserve write requests, since those will not be possible to recover and + * writes are only to happen once. */ + const bool tile_write = render_work.tile.write; + const bool full_write = render_work.full.write; + + render_work = RenderWork(); + + render_work.tile.write = tile_write; + render_work.full.write = full_write; + + /* Do not write tile if it has zero samples it it, treat it similarly to all other tiles which + * got cancelled. */ + if (!state_.tile_result_was_written && has_rendered_samples) { + render_work.tile.write = true; + } + + if (!state_.full_frame_was_written) { + render_work.full.write = true; + } + + /* Update current tile, but only if any sample was rendered. + * Allows to have latest state of tile visible while full buffer is being processed. + * + * Note that if there are no samples in the current tile its render buffer might have pixels + * remained from previous state. + * + * If the full result was written, then there is no way any updates were made to the render + * buffers. And the buffers might have been freed from the device, so display update is not + * possible. */ + if (has_rendered_samples && !state_.full_frame_was_written) { + render_work.display.update = true; + } +} + +bool RenderScheduler::done() const +{ + if (state_.resolution_divider != pixel_size_) { + return false; + } + + if (state_.path_trace_finished || state_.time_limit_reached) { + return true; + } + + return get_num_rendered_samples() >= num_samples_; +} + +RenderWork RenderScheduler::get_render_work() +{ + check_time_limit_reached(); + + const double time_now = time_dt(); + + if (done()) { + RenderWork render_work; + render_work.resolution_divider = state_.resolution_divider; + + if (!set_postprocess_render_work(&render_work)) { + set_full_frame_render_work(&render_work); + } + + if (!render_work) { + state_.end_render_time = time_now; + } + + update_state_for_render_work(render_work); + + return render_work; + } + + RenderWork render_work; + + if (state_.resolution_divider != pixel_size_) { + state_.resolution_divider = max(state_.resolution_divider / 2, pixel_size_); + state_.num_rendered_samples = 0; + state_.last_display_update_sample = -1; + } + + render_work.resolution_divider = state_.resolution_divider; + + render_work.path_trace.start_sample = get_start_sample_to_path_trace(); + render_work.path_trace.num_samples = get_num_samples_to_path_trace(); + + render_work.init_render_buffers = (render_work.path_trace.start_sample == get_start_sample()); + + /* NOTE: Rebalance scheduler requires current number of samples to not be advanced forward. */ + render_work.rebalance = work_need_rebalance(); + + /* NOTE: Advance number of samples now, so that filter and denoising check can see that all the + * samples are rendered. */ + state_.num_rendered_samples += render_work.path_trace.num_samples; + + render_work.adaptive_sampling.filter = work_need_adaptive_filter(); + render_work.adaptive_sampling.threshold = work_adaptive_threshold(); + render_work.adaptive_sampling.reset = false; + + bool denoiser_delayed, denoiser_ready_to_display; + render_work.tile.denoise = work_need_denoise(denoiser_delayed, denoiser_ready_to_display); + + render_work.tile.write = done(); + + render_work.display.update = work_need_update_display(denoiser_delayed); + render_work.display.use_denoised_result = denoiser_ready_to_display; + + if (done()) { + set_postprocess_render_work(&render_work); + } + + update_state_for_render_work(render_work); + + return render_work; +} + +void RenderScheduler::update_state_for_render_work(const RenderWork &render_work) +{ + const double time_now = time_dt(); + + if (render_work.rebalance) { + state_.last_rebalance_time = time_now; + ++state_.num_rebalance_requested; + } + + /* A fallback display update time, for the case there is an error of display update, or when + * there is no display at all. */ + if (render_work.display.update) { + state_.last_display_update_time = time_now; + state_.last_display_update_sample = state_.num_rendered_samples; + } + + state_.last_work_tile_was_denoised = render_work.tile.denoise; + state_.tile_result_was_written |= render_work.tile.write; + state_.full_frame_was_written |= render_work.full.write; +} + +bool RenderScheduler::set_postprocess_render_work(RenderWork *render_work) +{ + if (state_.postprocess_work_scheduled) { + return false; + } + state_.postprocess_work_scheduled = true; + + bool any_scheduled = false; + + if (need_schedule_cryptomatte_) { + render_work->cryptomatte.postprocess = true; + any_scheduled = true; + } + + if (denoiser_params_.use && !state_.last_work_tile_was_denoised) { + render_work->tile.denoise = true; + any_scheduled = true; + } + + if (!state_.tile_result_was_written) { + render_work->tile.write = true; + any_scheduled = true; + } + + if (any_scheduled) { + render_work->display.update = true; + } + + return any_scheduled; +} + +void RenderScheduler::set_full_frame_render_work(RenderWork *render_work) +{ + if (state_.full_frame_work_scheduled) { + return; + } + + if (!tile_manager_.has_multiple_tiles()) { + /* There is only single tile, so all work has been performed already. */ + return; + } + + if (!tile_manager_.done()) { + /* There are still tiles to be rendered. */ + return; + } + + if (state_.full_frame_was_written) { + return; + } + + state_.full_frame_work_scheduled = true; + + render_work->full.write = true; +} + +/* Knowing time which it took to complete a task at the current resolution divider approximate how + * long it would have taken to complete it at a final resolution. */ +static double approximate_final_time(const RenderWork &render_work, double time) +{ + if (render_work.resolution_divider == 1) { + return time; + } + + const double resolution_divider_sq = render_work.resolution_divider * + render_work.resolution_divider; + return time * resolution_divider_sq; +} + +void RenderScheduler::report_work_begin(const RenderWork &render_work) +{ + /* Start counting render time when rendering samples at their final resolution. + * + * NOTE: The work might have the path trace part be all zero: this happens when a post-processing + * work is scheduled after the path tracing. Checking for just a start sample doesn't work here + * because it might be wrongly 0. Check for whether path tracing is actually happening as it is + * expected to happen in the first work. */ + if (render_work.resolution_divider == pixel_size_ && render_work.path_trace.num_samples != 0 && + render_work.path_trace.start_sample == get_start_sample()) { + state_.start_render_time = time_dt(); + } +} + +void RenderScheduler::report_path_trace_time(const RenderWork &render_work, + double time, + bool is_cancelled) +{ + path_trace_time_.add_wall(time); + + if (is_cancelled) { + return; + } + + const double final_time_approx = approximate_final_time(render_work, time); + + if (work_is_usable_for_first_render_estimation(render_work)) { + first_render_time_.path_trace_per_sample = final_time_approx / + render_work.path_trace.num_samples; + } + + if (work_report_reset_average(render_work)) { + path_trace_time_.reset_average(); + } + + path_trace_time_.add_average(final_time_approx, render_work.path_trace.num_samples); + + VLOG(4) << "Average path tracing time: " << path_trace_time_.get_average() << " seconds."; +} + +void RenderScheduler::report_path_trace_occupancy(const RenderWork &render_work, float occupancy) +{ + state_.occupancy_num_samples = render_work.path_trace.num_samples; + state_.occupancy = occupancy; + VLOG(4) << "Measured path tracing occupancy: " << occupancy; +} + +void RenderScheduler::report_adaptive_filter_time(const RenderWork &render_work, + double time, + bool is_cancelled) +{ + adaptive_filter_time_.add_wall(time); + + if (is_cancelled) { + return; + } + + const double final_time_approx = approximate_final_time(render_work, time); + + if (work_report_reset_average(render_work)) { + adaptive_filter_time_.reset_average(); + } + + adaptive_filter_time_.add_average(final_time_approx, render_work.path_trace.num_samples); + + VLOG(4) << "Average adaptive sampling filter time: " << adaptive_filter_time_.get_average() + << " seconds."; +} + +void RenderScheduler::report_denoise_time(const RenderWork &render_work, double time) +{ + denoise_time_.add_wall(time); + + const double final_time_approx = approximate_final_time(render_work, time); + + if (work_is_usable_for_first_render_estimation(render_work)) { + first_render_time_.denoise_time = final_time_approx; + } + + if (work_report_reset_average(render_work)) { + denoise_time_.reset_average(); + } + + denoise_time_.add_average(final_time_approx); + + VLOG(4) << "Average denoising time: " << denoise_time_.get_average() << " seconds."; +} + +void RenderScheduler::report_display_update_time(const RenderWork &render_work, double time) +{ + display_update_time_.add_wall(time); + + const double final_time_approx = approximate_final_time(render_work, time); + + if (work_is_usable_for_first_render_estimation(render_work)) { + first_render_time_.display_update_time = final_time_approx; + } + + if (work_report_reset_average(render_work)) { + display_update_time_.reset_average(); + } + + display_update_time_.add_average(final_time_approx); + + VLOG(4) << "Average display update time: " << display_update_time_.get_average() << " seconds."; + + /* Move the display update moment further in time, so that logic which checks when last update + * did happen have more reliable point in time (without path tracing and denoising parts of the + * render work). */ + state_.last_display_update_time = time_dt(); +} + +void RenderScheduler::report_rebalance_time(const RenderWork &render_work, + double time, + bool balance_changed) +{ + rebalance_time_.add_wall(time); + + if (work_report_reset_average(render_work)) { + rebalance_time_.reset_average(); + } + + rebalance_time_.add_average(time); + + if (balance_changed) { + ++state_.num_rebalance_changes; + } + + state_.last_rebalance_changed = balance_changed; + + VLOG(4) << "Average rebalance time: " << rebalance_time_.get_average() << " seconds."; +} + +string RenderScheduler::full_report() const +{ + const double render_wall_time = state_.end_render_time - state_.start_render_time; + const int num_rendered_samples = get_num_rendered_samples(); + + string result = "\nRender Scheduler Summary\n\n"; + + { + string mode; + if (headless_) { + mode = "Headless"; + } + else if (background_) { + mode = "Background"; + } + else { + mode = "Interactive"; + } + result += "Mode: " + mode + "\n"; + } + + result += "Resolution: " + to_string(buffer_params_.width) + "x" + + to_string(buffer_params_.height) + "\n"; + + result += "\nAdaptive sampling:\n"; + result += " Use: " + string_from_bool(adaptive_sampling_.use) + "\n"; + if (adaptive_sampling_.use) { + result += " Step: " + to_string(adaptive_sampling_.adaptive_step) + "\n"; + result += " Min Samples: " + to_string(adaptive_sampling_.min_samples) + "\n"; + result += " Threshold: " + to_string(adaptive_sampling_.threshold) + "\n"; + } + + result += "\nDenoiser:\n"; + result += " Use: " + string_from_bool(denoiser_params_.use) + "\n"; + if (denoiser_params_.use) { + result += " Type: " + string(denoiserTypeToHumanReadable(denoiser_params_.type)) + "\n"; + result += " Start Sample: " + to_string(denoiser_params_.start_sample) + "\n"; + + string passes = "Color"; + if (denoiser_params_.use_pass_albedo) { + passes += ", Albedo"; + } + if (denoiser_params_.use_pass_normal) { + passes += ", Normal"; + } + + result += " Passes: " + passes + "\n"; + } + + if (state_.num_rebalance_requested) { + result += "\nRebalancer:\n"; + result += " Number of requested rebalances: " + to_string(state_.num_rebalance_requested) + + "\n"; + result += " Number of performed rebalances: " + to_string(state_.num_rebalance_changes) + + "\n"; + } + + result += "\nTime (in seconds):\n"; + result += string_printf(" %20s %20s %20s\n", "", "Wall", "Average"); + result += string_printf(" %20s %20f %20f\n", + "Path Tracing", + path_trace_time_.get_wall(), + path_trace_time_.get_average()); + + if (adaptive_sampling_.use) { + result += string_printf(" %20s %20f %20f\n", + "Adaptive Filter", + adaptive_filter_time_.get_wall(), + adaptive_filter_time_.get_average()); + } + + if (denoiser_params_.use) { + result += string_printf( + " %20s %20f %20f\n", "Denoiser", denoise_time_.get_wall(), denoise_time_.get_average()); + } + + result += string_printf(" %20s %20f %20f\n", + "Display Update", + display_update_time_.get_wall(), + display_update_time_.get_average()); + + if (state_.num_rebalance_requested) { + result += string_printf(" %20s %20f %20f\n", + "Rebalance", + rebalance_time_.get_wall(), + rebalance_time_.get_average()); + } + + const double total_time = path_trace_time_.get_wall() + adaptive_filter_time_.get_wall() + + denoise_time_.get_wall() + display_update_time_.get_wall(); + result += "\n Total: " + to_string(total_time) + "\n"; + + result += string_printf( + "\nRendered %d samples in %f seconds\n", num_rendered_samples, render_wall_time); + + /* When adaptive sampling is used the average time becomes meaningless, because different samples + * will likely render different number of pixels. */ + if (!adaptive_sampling_.use) { + result += string_printf("Average time per sample: %f seconds\n", + render_wall_time / num_rendered_samples); + } + + return result; +} + +double RenderScheduler::guess_display_update_interval_in_seconds() const +{ + return guess_display_update_interval_in_seconds_for_num_samples(state_.num_rendered_samples); +} + +double RenderScheduler::guess_display_update_interval_in_seconds_for_num_samples( + int num_rendered_samples) const +{ + double update_interval = guess_display_update_interval_in_seconds_for_num_samples_no_limit( + num_rendered_samples); + + if (time_limit_ != 0.0 && state_.start_render_time != 0.0) { + const double remaining_render_time = max(0.0, + time_limit_ - (time_dt() - state_.start_render_time)); + + update_interval = min(update_interval, remaining_render_time); + } + + return update_interval; +} + +/* TODO(sergey): This is just a quick implementation, exact values might need to be tweaked based + * on a more careful experiments with viewport rendering. */ +double RenderScheduler::guess_display_update_interval_in_seconds_for_num_samples_no_limit( + int num_rendered_samples) const +{ + /* TODO(sergey): Need a decision on whether this should be using number of samples rendered + * within the current render session, or use absolute number of samples with the start sample + * taken into account. It will depend on whether the start sample offset clears the render + * buffer. */ + + if (state_.need_rebalance_at_next_work) { + return 0.1; + } + if (state_.last_rebalance_changed) { + return 0.2; + } + + if (headless_) { + /* In headless mode do rare updates, so that the device occupancy is high, but there are still + * progress messages printed to the logs. */ + return 30.0; + } + + if (background_) { + if (num_rendered_samples < 32) { + return 1.0; + } + return 2.0; + } + + /* Render time and number of samples rendered are used to figure out the display update interval. + * Render time is used to allow for fast display updates in the first few seconds of rendering + * on fast devices. Number of samples rendered is used to allow for potentially quicker display + * updates on slow devices during the first few samples. */ + const double render_time = path_trace_time_.get_wall(); + if (render_time < 1) { + return 0.1; + } + if (render_time < 2) { + return 0.25; + } + if (render_time < 4) { + return 0.5; + } + if (render_time < 8 || num_rendered_samples < 32) { + return 1.0; + } + return 2.0; +} + +int RenderScheduler::calculate_num_samples_per_update() const +{ + const double time_per_sample_average = path_trace_time_.get_average(); + const double num_samples_in_second = pixel_size_ * pixel_size_ / time_per_sample_average; + + const double update_interval_in_seconds = guess_display_update_interval_in_seconds(); + + return max(int(num_samples_in_second * update_interval_in_seconds), 1); +} + +int RenderScheduler::get_start_sample_to_path_trace() const +{ + return start_sample_ + state_.num_rendered_samples; +} + +/* Round number of samples to the closest power of two. + * Rounding might happen to higher or lower value depending on which one is closer. Such behavior + * allows to have number of samples to be power of two without diverging from the planned number of + * samples too much. */ +static inline uint round_num_samples_to_power_of_2(const uint num_samples) +{ + if (num_samples == 1) { + return 1; + } + + if (is_power_of_two(num_samples)) { + return num_samples; + } + + const uint num_samples_up = next_power_of_two(num_samples); + const uint num_samples_down = num_samples_up - (num_samples_up >> 1); + + const uint delta_up = num_samples_up - num_samples; + const uint delta_down = num_samples - num_samples_down; + + if (delta_up <= delta_down) { + return num_samples_up; + } + + return num_samples_down; +} + +int RenderScheduler::get_num_samples_to_path_trace() const +{ + if (state_.resolution_divider != pixel_size_) { + return get_num_samples_during_navigation(state_.resolution_divider); + } + + /* Always start full resolution render with a single sample. Gives more instant feedback to + * artists, and allows to gather information for a subsequent path tracing works. Do it in the + * headless mode as well, to give some estimate of how long samples are taking. */ + if (state_.num_rendered_samples == 0) { + return 1; + } + + const int num_samples_per_update = calculate_num_samples_per_update(); + const int path_trace_start_sample = get_start_sample_to_path_trace(); + + /* Round number of samples to a power of two, so that division of path states into tiles goes in + * a more integer manner. + * This might make it so updates happens more rarely due to rounding up. In the test scenes this + * is not huge deal because it is not seen that more than 8 samples can be rendered between + * updates. If that becomes a problem we can add some extra rules like never allow to round up + * more than N samples. */ + const int num_samples_pot = round_num_samples_to_power_of_2(num_samples_per_update); + + const int max_num_samples_to_render = start_sample_ + num_samples_ - path_trace_start_sample; + + int num_samples_to_render = min(num_samples_pot, max_num_samples_to_render); + + /* When enough statistics is available and doing an offlien rendering prefer to keep device + * occupied. */ + if (state_.occupancy_num_samples && (background_ || headless_)) { + /* Keep occupancy at about 0.5 (this is more of an empirical figure which seems to match scenes + * with good performance without forcing occupancy to be higher). */ + int num_samples_to_occupy = state_.occupancy_num_samples; + if (state_.occupancy < 0.5f) { + num_samples_to_occupy = lround(state_.occupancy_num_samples * 0.7f / state_.occupancy); + } + + num_samples_to_render = max(num_samples_to_render, + min(num_samples_to_occupy, max_num_samples_to_render)); + } + + /* If adaptive sampling is not use, render as many samples per update as possible, keeping the + * device fully occupied, without much overhead of display updates. */ + if (!adaptive_sampling_.use) { + return num_samples_to_render; + } + + /* TODO(sergey): Add extra "clamping" here so that none of the filtering points is missing. This + * is to ensure that the final render is pixel-matched regardless of how many samples per second + * compute device can do. */ + + return adaptive_sampling_.align_samples(path_trace_start_sample, num_samples_to_render); +} + +int RenderScheduler::get_num_samples_during_navigation(int resolution_divider) const +{ + /* Special trick for fast navigation: schedule multiple samples during fast navigation + * (which will prefer to use lower resolution to keep up with refresh rate). This gives more + * usable visual feedback for artists. There are a couple of tricks though. */ + + if (is_denoise_active_during_update()) { + /* When denoising is used during navigation prefer using a higher resolution with less samples + * (scheduling less samples here will make it so the resolution_divider calculation will use a + * lower value for the divider). This is because both OpenImageDenoiser and OptiX denoiser + * give visually better results on a higher resolution image with less samples. */ + return 1; + } + + if (resolution_divider <= pixel_size_) { + /* When resolution divider is at or below pixel size, schedule one sample. This doesn't effect + * the sample count at this resolution division, but instead assists in the calculation of + * the resolution divider. */ + return 1; + } + + if (resolution_divider == pixel_size_ * 2) { + /* When resolution divider is the previous step to the final resolution, schedule two samples. + * This is so that rendering on lower resolution does not exceed time that it takes to render + * first sample at the full resolution. */ + return 2; + } + + /* Always render 4 samples, even if scene is configured for less. + * The idea here is to have enough information on the screen. Resolution divider of 2 allows us + * to have 4 time extra samples, so verall worst case timing is the same as the final resolution + * at one sample. */ + return 4; +} + +bool RenderScheduler::work_need_adaptive_filter() const +{ + return adaptive_sampling_.need_filter(get_rendered_sample()); +} + +float RenderScheduler::work_adaptive_threshold() const +{ + if (!use_progressive_noise_floor_) { + return adaptive_sampling_.threshold; + } + + return max(state_.adaptive_sampling_threshold, adaptive_sampling_.threshold); +} + +bool RenderScheduler::work_need_denoise(bool &delayed, bool &ready_to_display) +{ + delayed = false; + ready_to_display = true; + + if (!denoiser_params_.use) { + /* Denoising is disabled, no need to scheduler work for it. */ + return false; + } + + if (done()) { + /* Always denoise at the last sample. */ + return true; + } + + if (background_) { + /* Background render, only denoise when rendering the last sample. */ + /* TODO(sergey): Follow similar logic to viewport, giving an overview of how final denoised + * image looks like even for the background rendering. */ + return false; + } + + /* Viewport render. */ + + /* Navigation might render multiple samples at a lower resolution. Those are not to be counted as + * final samples. */ + const int num_samples_finished = state_.resolution_divider == pixel_size_ ? + state_.num_rendered_samples : + 1; + + /* Immediately denoise when we reach the start sample or last sample. */ + if (num_samples_finished == denoiser_params_.start_sample || + num_samples_finished == num_samples_) { + return true; + } + + /* Do not denoise until the sample at which denoising should start is reached. */ + if (num_samples_finished < denoiser_params_.start_sample) { + ready_to_display = false; + return false; + } + + /* Avoid excessive denoising in viewport after reaching a certain sample count and render time. + */ + /* TODO(sergey): Consider making time interval and sample configurable. */ + delayed = (path_trace_time_.get_wall() > 4 && num_samples_finished >= 20 && + (time_dt() - state_.last_display_update_time) < 1.0); + + return !delayed; +} + +bool RenderScheduler::work_need_update_display(const bool denoiser_delayed) +{ + if (headless_) { + /* Force disable display update in headless mode. There will be nothing to display the + * in-progress result. */ + return false; + } + + if (denoiser_delayed) { + /* If denoiser has been delayed the display can not be updated as it will not contain + * up-to-date state of the render result. */ + return false; + } + + if (!adaptive_sampling_.use) { + /* When adaptive sampling is not used the work is scheduled in a way that they keep render + * device busy for long enough, so that the display update can happen right after the + * rendering. */ + return true; + } + + if (done() || state_.last_display_update_sample == -1) { + /* Make sure an initial and final results of adaptive sampling is communicated ot the display. + */ + return true; + } + + /* For the development purposes of adaptive sampling it might be very useful to see all updates + * of active pixels after convergence check. However, it would cause a slowdown for regular usage + * users. Possibly, make it a debug panel option to allow rapid update to ease development + * without need to re-compiled. */ + // if (work_need_adaptive_filter()) { + // return true; + // } + + /* When adaptive sampling is used, its possible that only handful of samples of a very simple + * scene will be scheduled to a powerful device (in order to not "miss" any of filtering points). + * We take care of skipping updates here based on when previous display update did happen. */ + const double update_interval = guess_display_update_interval_in_seconds_for_num_samples( + state_.last_display_update_sample); + return (time_dt() - state_.last_display_update_time) > update_interval; +} + +bool RenderScheduler::work_need_rebalance() +{ + /* This is the minimum time, as the rebalancing can not happen more often than the path trace + * work. */ + static const double kRebalanceIntervalInSeconds = 1; + + if (!need_schedule_rebalance_works_) { + return false; + } + + if (state_.resolution_divider != pixel_size_) { + /* Don't rebalance at a non-final resolution divider. Some reasons for this: + * - It will introduce unnecessary during navigation. + * - Per-render device timing information is not very reliable yet. */ + return false; + } + + if (state_.num_rendered_samples == 0) { + state_.need_rebalance_at_next_work = true; + return false; + } + + if (state_.need_rebalance_at_next_work) { + state_.need_rebalance_at_next_work = false; + return true; + } + + if (state_.last_rebalance_changed) { + return true; + } + + return (time_dt() - state_.last_rebalance_time) > kRebalanceIntervalInSeconds; +} + +void RenderScheduler::update_start_resolution_divider() +{ + if (start_resolution_divider_ == 0) { + /* Resolution divider has never been calculated before: use default resolution, so that we have + * somewhat good initial behavior, giving a chance to collect real numbers. */ + start_resolution_divider_ = default_start_resolution_divider_; + VLOG(3) << "Initial resolution divider is " << start_resolution_divider_; + return; + } + + if (first_render_time_.path_trace_per_sample == 0.0) { + /* Not enough information to calculate better resolution, keep the existing one. */ + return; + } + + const double desired_update_interval_in_seconds = + guess_viewport_navigation_update_interval_in_seconds(); + + const double actual_time_per_update = first_render_time_.path_trace_per_sample + + first_render_time_.denoise_time + + first_render_time_.display_update_time; + + /* Allow some percent of tolerance, so that if the render time is close enough to the higher + * resolution we prefer to use it instead of going way lower resolution and time way below the + * desired one. */ + const int resolution_divider_for_update = calculate_resolution_divider_for_time( + desired_update_interval_in_seconds * 1.4, actual_time_per_update); + + /* TODO(sergey): Need to add hysteresis to avoid resolution divider bouncing around when actual + * render time is somewhere on a boundary between two resolutions. */ + + /* Never increase resolution to higher than the pixel size (which is possible if the scene is + * simple and compute device is fast). */ + start_resolution_divider_ = max(resolution_divider_for_update, pixel_size_); + + VLOG(3) << "Calculated resolution divider is " << start_resolution_divider_; +} + +double RenderScheduler::guess_viewport_navigation_update_interval_in_seconds() const +{ + if (is_denoise_active_during_update()) { + /* Use lower value than the non-denoised case to allow having more pixels to reconstruct the + * image from. With the faster updates and extra compute required the resolution becomes too + * low to give usable feedback. */ + /* NOTE: Based on performance of OpenImageDenoiser on CPU. For OptiX denoiser or other denoiser + * on GPU the value might need to become lower for faster navigation. */ + return 1.0 / 12.0; + } + + /* For the best match with the Blender's viewport the refresh ratio should be 60fps. This will + * avoid "jelly" effects. However, on a non-trivial scenes this can only be achieved with high + * values of the resolution divider which does not give very pleasant updates during navigation. + * Choose less frequent updates to allow more noise-free and higher resolution updates. */ + + /* TODO(sergey): Can look into heuristic which will allow to have 60fps if the resolution divider + * is not too high. Alternatively, synchronize Blender's overlays updates to Cycles updates. */ + + return 1.0 / 30.0; +} + +bool RenderScheduler::is_denoise_active_during_update() const +{ + if (!denoiser_params_.use) { + return false; + } + + if (denoiser_params_.start_sample > 1) { + return false; + } + + return true; +} + +bool RenderScheduler::work_is_usable_for_first_render_estimation(const RenderWork &render_work) +{ + return render_work.resolution_divider == pixel_size_ && + render_work.path_trace.start_sample == start_sample_; +} + +bool RenderScheduler::work_report_reset_average(const RenderWork &render_work) +{ + /* When rendering at a non-final resolution divider time average is not very useful because it + * will either bias average down (due to lower render times on the smaller images) or will give + * incorrect result when trying to estimate time which would have spent on the final resolution. + * + * So we only accumulate average for the latest resolution divider which was rendered. */ + return render_work.resolution_divider != pixel_size_; +} + +void RenderScheduler::check_time_limit_reached() +{ + if (time_limit_ == 0.0) { + /* No limit is enforced. */ + return; + } + + if (state_.start_render_time == 0.0) { + /* Rendering did not start yet. */ + return; + } + + const double current_time = time_dt(); + + if (current_time - state_.start_render_time < time_limit_) { + /* Time limit is not reached yet. */ + return; + } + + state_.time_limit_reached = true; + state_.end_render_time = current_time; +} + +/* -------------------------------------------------------------------- + * Utility functions. + */ + +int RenderScheduler::calculate_resolution_divider_for_time(double desired_time, double actual_time) +{ + /* TODO(sergey): There should a non-iterative analytical formula here. */ + + int resolution_divider = 1; + + /* This algorithm iterates through resolution dividers until a divider is found that achieves + * the desired render time. A limit of default_start_resolution_divider_ is put in place as the + * maximum resolution divider to avoid an unreadable viewport due to a low resolution. + * pre_resolution_division_samples and post_resolution_division_samples are used in this + * calculation to better predict the performance impact of changing resolution divisions as + * the sample count can also change between resolution divisions. */ + while (actual_time > desired_time && resolution_divider < default_start_resolution_divider_) { + int pre_resolution_division_samples = get_num_samples_during_navigation(resolution_divider); + resolution_divider = resolution_divider * 2; + int post_resolution_division_samples = get_num_samples_during_navigation(resolution_divider); + actual_time /= 4.0 * pre_resolution_division_samples / post_resolution_division_samples; + } + + return resolution_divider; +} + +int calculate_resolution_divider_for_resolution(int width, int height, int resolution) +{ + if (resolution == INT_MAX) { + return 1; + } + + int resolution_divider = 1; + while (width * height > resolution * resolution) { + width = max(1, width / 2); + height = max(1, height / 2); + + resolution_divider <<= 1; + } + + return resolution_divider; +} + +int calculate_resolution_for_divider(int width, int height, int resolution_divider) +{ + const int pixel_area = width * height; + const int resolution = lround(sqrt(pixel_area)); + + return resolution / resolution_divider; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h new file mode 100644 index 00000000000..9c2d107e46d --- /dev/null +++ b/intern/cycles/integrator/render_scheduler.h @@ -0,0 +1,466 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/adaptive_sampling.h" +#include "integrator/denoiser.h" /* For DenoiseParams. */ +#include "render/buffers.h" +#include "util/util_string.h" + +CCL_NAMESPACE_BEGIN + +class SessionParams; +class TileManager; + +class RenderWork { + public: + int resolution_divider = 1; + + /* Initialize render buffers. + * Includes steps like zero-ing the buffer on the device, and optional reading of pixels from the + * baking target. */ + bool init_render_buffers = false; + + /* Path tracing samples information. */ + struct { + int start_sample = 0; + int num_samples = 0; + } path_trace; + + struct { + /* Check for convergency and filter the mask. */ + bool filter = false; + + float threshold = 0.0f; + + /* Reset convergency flag when filtering, forcing a re-check of whether pixel did converge. */ + bool reset = false; + } adaptive_sampling; + + struct { + bool postprocess = false; + } cryptomatte; + + /* Work related on the current tile. */ + struct { + /* Write render buffers of the current tile. + * + * It is up to the path trace to decide whether writing should happen via user-provided + * callback into the rendering software, or via tile manager into a partial file. */ + bool write = false; + + bool denoise = false; + } tile; + + /* Work related on the full-frame render buffer. */ + struct { + /* Write full render result. + * Implies reading the partial file from disk. */ + bool write = false; + } full; + + /* Display which is used to visualize render result. */ + struct { + /* Display needs to be updated for the new render. */ + bool update = false; + + /* Display can use denoised result if available. */ + bool use_denoised_result = true; + } display; + + /* Re-balance multi-device scheduling after rendering this work. + * Note that the scheduler does not know anything abouce devices, so if there is only a single + * device used, then it is up for the PathTracer to ignore the balancing. */ + bool rebalance = false; + + /* Conversion to bool, to simplify checks about whether there is anything to be done for this + * work. */ + inline operator bool() const + { + return path_trace.num_samples || adaptive_sampling.filter || display.update || tile.denoise || + tile.write || full.write; + } +}; + +class RenderScheduler { + public: + RenderScheduler(TileManager &tile_manager, const SessionParams ¶ms); + + /* Specify whether cryptomatte-related works are to be scheduled. */ + void set_need_schedule_cryptomatte(bool need_schedule_cryptomatte); + + /* Allows to disable work re-balancing works, allowing to schedule as much to a single device + * as possible. */ + void set_need_schedule_rebalance(bool need_schedule_rebalance); + + bool is_background() const; + + void set_denoiser_params(const DenoiseParams ¶ms); + void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling); + + bool is_adaptive_sampling_used() const; + + /* Start sample for path tracing. + * The scheduler will schedule work using this sample as the first one. */ + void set_start_sample(int start_sample); + int get_start_sample() const; + + /* Number of samples to render, starting from start sample. + * The scheduler will schedule work in the range of + * [start_sample, start_sample + num_samples - 1], inclusively. */ + void set_num_samples(int num_samples); + int get_num_samples() const; + + /* Time limit for the path tracing tasks, in minutes. + * Zero disables the limit. */ + void set_time_limit(double time_limit); + double get_time_limit() const; + + /* Get sample up to which rendering has been done. + * This is an absolute 0-based value. + * + * For example, if start sample is 10 and and 5 samples were rendered, then this call will + * return 14. + * + * If there were no samples rendered, then the behavior is undefined. */ + int get_rendered_sample() const; + + /* Get number of samples rendered within the current scheduling session. + * + * For example, if start sample is 10 and and 5 samples were rendered, then this call will + * return 5. + * + * Note that this is based on the scheduling information. In practice this means that if someone + * requested for work to render the scheduler considers the work done. */ + int get_num_rendered_samples() const; + + /* Reset scheduler, indicating that rendering will happen from scratch. + * Resets current rendered state, as well as scheduling information. */ + void reset(const BufferParams &buffer_params, int num_samples); + + /* Reset scheduler upon switching to a next tile. + * Will keep the same number of samples and full-frame render parameters, but will reset progress + * and allow schedule renders works from the beginning of the new tile. */ + void reset_for_next_tile(); + + /* Reschedule adaptive sampling work when all pixels did converge. + * If there is nothing else to be done for the adaptive sampling (pixels did converge to the + * final threshold) then false is returned and the render scheduler will stop scheduling path + * tracing works. Otherwise will modify the work's adaptive sampling settings to continue with + * a lower threshold. */ + bool render_work_reschedule_on_converge(RenderWork &render_work); + + /* Reschedule adaptive sampling work when the device is mostly on idle, but not all pixels yet + * converged. + * If re-scheduling is not possible (adaptive sampling is happening with the final threshold, and + * the path tracer is to finish the current pixels) then false is returned. */ + bool render_work_reschedule_on_idle(RenderWork &render_work); + + /* Reschedule work when rendering has been requested to cancel. + * + * Will skip all work which is not needed anymore because no more samples will be added (for + * example, adaptive sampling filtering and convergence check will be skipped). + * Will enable all work needed to make sure all passes are communicated to the software. + * + * NOTE: Should be used before passing work to `PathTrace::render_samples()`. */ + void render_work_reschedule_on_cancel(RenderWork &render_work); + + RenderWork get_render_work(); + + /* Report that the path tracer started to work, after scene update and loading kernels. */ + void report_work_begin(const RenderWork &render_work); + + /* Report time (in seconds) which corresponding part of work took. */ + void report_path_trace_time(const RenderWork &render_work, double time, bool is_cancelled); + void report_path_trace_occupancy(const RenderWork &render_work, float occupancy); + void report_adaptive_filter_time(const RenderWork &render_work, double time, bool is_cancelled); + void report_denoise_time(const RenderWork &render_work, double time); + void report_display_update_time(const RenderWork &render_work, double time); + void report_rebalance_time(const RenderWork &render_work, double time, bool balance_changed); + + /* Generate full multi-line report of the rendering process, including rendering parameters, + * times, and so on. */ + string full_report() const; + + protected: + /* Check whether all work has been scheduled and time limit was not exceeded. + * + * NOTE: Tricky bit: if the time limit was reached the done() is considered to be true, but some + * extra work needs to be scheduled to denoise and write final result. */ + bool done() const; + + /* Update scheduling state for a newely scheduled work. + * Takes care of things like checking whether work was ever denoised, tile was written and states + * like that. */ + void update_state_for_render_work(const RenderWork &render_work); + + /* Returns true if any work was scheduled. */ + bool set_postprocess_render_work(RenderWork *render_work); + + /* Set work which is to be performed after all tiles has been rendered. */ + void set_full_frame_render_work(RenderWork *render_work); + + /* Update start resolution divider based on the accumulated timing information, preserving nice + * feeling navigation feel. */ + void update_start_resolution_divider(); + + /* Calculate desired update interval in seconds based on the current timings and settings. + * Will give an interval which provides good feeling updates during viewport navigation. */ + double guess_viewport_navigation_update_interval_in_seconds() const; + + /* Check whether denoising is active during interactive update while resolution divider is not + * unit. */ + bool is_denoise_active_during_update() const; + + /* Heuristic which aims to give perceptually pleasant update of display interval in a way that at + * lower samples and near the beginning of rendering, updates happen more often, but with higher + * number of samples and later in the render, updates happen less often but device occupancy + * goes higher. */ + double guess_display_update_interval_in_seconds() const; + double guess_display_update_interval_in_seconds_for_num_samples(int num_rendered_samples) const; + double guess_display_update_interval_in_seconds_for_num_samples_no_limit( + int num_rendered_samples) const; + + /* Calculate number of samples which can be rendered within current desred update interval which + * is calculated by `guess_update_interval_in_seconds()`. */ + int calculate_num_samples_per_update() const; + + /* Get start sample and the number of samples which are to be path traces in the current work. */ + int get_start_sample_to_path_trace() const; + int get_num_samples_to_path_trace() const; + + /* Calculate how many samples there are to be rendered for the very first path trace after reset. + */ + int get_num_samples_during_navigation(int resolution_divier) const; + + /* Whether adaptive sampling convergence check and filter is to happen. */ + bool work_need_adaptive_filter() const; + + /* Calculate thretshold for adaptive sampling. */ + float work_adaptive_threshold() const; + + /* Check whether current work needs denoising. + * Denoising is not needed if the denoiser is not configured, or when denosiing is happening too + * often. + * + * The delayed will be true when the denoiser is configured for use, but it was delayed for a + * later sample, to reduce overhead. + * + * ready_to_display will be false if we may have a denoised result that is outdated due to + * increased samples. */ + bool work_need_denoise(bool &delayed, bool &ready_to_display); + + /* Check whether current work need to update display. + * + * The `denoiser_delayed` is what `work_need_denoise()` returned as delayed denoiser flag. */ + bool work_need_update_display(const bool denoiser_delayed); + + /* Check whether it is time to perform rebalancing for the render work, */ + bool work_need_rebalance(); + + /* Check whether timing of the given work are usable to store timings in the `first_render_time_` + * for the resolution divider calculation. */ + bool work_is_usable_for_first_render_estimation(const RenderWork &render_work); + + /* Check whether timing report about the given work need to reset accumulated average time. */ + bool work_report_reset_average(const RenderWork &render_work); + + /* CHeck whether render time limit has been reached (or exceeded), and if so store related + * information in the state so that rendering is considered finished, and is possible to report + * average render time information. */ + void check_time_limit_reached(); + + /* Helper class to keep track of task timing. + * + * Contains two parts: wall time and average. The wall time is an actual wall time of how long it + * took to complete all tasks of a type. Is always advanced when PathTracer reports time update. + * + * The average time is used for scheduling purposes. It is estimated to be a time of how long it + * takes to perform task on the final resolution. */ + class TimeWithAverage { + public: + inline void reset() + { + total_wall_time_ = 0.0; + + average_time_accumulator_ = 0.0; + num_average_times_ = 0; + } + + inline void add_wall(double time) + { + total_wall_time_ += time; + } + + inline void add_average(double time, int num_measurements = 1) + { + average_time_accumulator_ += time; + num_average_times_ += num_measurements; + } + + inline double get_wall() const + { + return total_wall_time_; + } + + inline double get_average() const + { + if (num_average_times_ == 0) { + return 0; + } + return average_time_accumulator_ / num_average_times_; + } + + inline void reset_average() + { + average_time_accumulator_ = 0.0; + num_average_times_ = 0; + } + + protected: + double total_wall_time_ = 0.0; + + double average_time_accumulator_ = 0.0; + int num_average_times_ = 0; + }; + + struct { + int resolution_divider = 1; + + /* Number of rendered samples on top of the start sample. */ + int num_rendered_samples = 0; + + /* Point in time the latest GPUDisplay work has been scheduled. */ + double last_display_update_time = 0.0; + /* Value of -1 means display was never updated. */ + int last_display_update_sample = -1; + + /* Point in time at which last rebalance has been performed. */ + double last_rebalance_time = 0.0; + + /* Number of rebalance works which has been requested to be performed. + * The path tracer might ignore the work if there is a single device rendering. */ + int num_rebalance_requested = 0; + + /* Number of rebalance works handled which did change balance across devices. */ + int num_rebalance_changes = 0; + + bool need_rebalance_at_next_work = false; + + /* Denotes whether the latest performed rebalance work cause an actual rebalance of work across + * devices. */ + bool last_rebalance_changed = false; + + /* Threshold for adaptive sampling which will be scheduled to work when not using progressive + * noise floor. */ + float adaptive_sampling_threshold = 0.0f; + + bool last_work_tile_was_denoised = false; + bool tile_result_was_written = false; + bool postprocess_work_scheduled = false; + bool full_frame_work_scheduled = false; + bool full_frame_was_written = false; + + bool path_trace_finished = false; + bool time_limit_reached = false; + + /* Time at which rendering started and finished. */ + double start_render_time = 0.0; + double end_render_time = 0.0; + + /* Measured occupancy of the render devices measured normalized to the number of samples. + * + * In a way it is "trailing": when scheduling new work this occupancy is measured when the + * previous work was rendered. */ + int occupancy_num_samples = 0; + float occupancy = 1.0f; + } state_; + + /* Timing of tasks which were performed at the very first render work at 100% of the + * resolution. This timing information is used to estimate resolution divider for fats + * navigation. */ + struct { + double path_trace_per_sample; + double denoise_time; + double display_update_time; + } first_render_time_; + + TimeWithAverage path_trace_time_; + TimeWithAverage adaptive_filter_time_; + TimeWithAverage denoise_time_; + TimeWithAverage display_update_time_; + TimeWithAverage rebalance_time_; + + /* Whether cryptomatte-related work will be scheduled. */ + bool need_schedule_cryptomatte_ = false; + + /* Whether to schedule device load rebalance works. + * Rebalancing requires some special treatment for update intervals and such, so if it's known + * that the rebalance will be ignored (due to single-device rendering i.e.) is better to fully + * ignore rebalancing logic. */ + bool need_schedule_rebalance_works_ = false; + + /* Path tracing work will be scheduled for samples from within + * [start_sample_, start_sample_ + num_samples_ - 1] range, inclusively. */ + int start_sample_ = 0; + int num_samples_ = 0; + + /* Limit in seconds for how long path tracing is allowed to happen. + * Zero means no limit is applied. */ + double time_limit_ = 0.0; + + /* Headless rendering without interface. */ + bool headless_; + + /* Background (offline) rendering. */ + bool background_; + + /* Pixel size is used to force lower resolution render for final pass. Useful for retina or other + * types of hi-dpi displays. */ + int pixel_size_ = 1; + + TileManager &tile_manager_; + + BufferParams buffer_params_; + DenoiseParams denoiser_params_; + + AdaptiveSampling adaptive_sampling_; + + /* Progressively lower adaptive sampling threshold level, keeping the image at a uniform noise + * level. */ + bool use_progressive_noise_floor_ = false; + + /* Default value for the resolution divider which will be used when there is no render time + * information available yet. + * It is also what defines the upper limit of the automatically calculated resolution divider. */ + int default_start_resolution_divider_ = 1; + + /* Initial resolution divider which will be used on render scheduler reset. */ + int start_resolution_divider_ = 0; + + /* Calculate smallest resolution divider which will bring down actual rendering time below the + * desired one. This call assumes linear dependency of render time from number of pixels + * (quadratic dependency from the resolution divider): resolution divider of 2 brings render time + * down by a factor of 4. */ + int calculate_resolution_divider_for_time(double desired_time, double actual_time); +}; + +int calculate_resolution_divider_for_resolution(int width, int height, int resolution); + +int calculate_resolution_for_divider(int width, int height, int resolution_divider); + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp new file mode 100644 index 00000000000..465b4a8d4da --- /dev/null +++ b/intern/cycles/integrator/shader_eval.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/shader_eval.h" + +#include "device/device.h" +#include "device/device_queue.h" + +#include "device/cpu/kernel.h" +#include "device/cpu/kernel_thread_globals.h" + +#include "util/util_logging.h" +#include "util/util_progress.h" +#include "util/util_tbb.h" + +CCL_NAMESPACE_BEGIN + +ShaderEval::ShaderEval(Device *device, Progress &progress) : device_(device), progress_(progress) +{ + DCHECK_NE(device_, nullptr); +} + +bool ShaderEval::eval(const ShaderEvalType type, + const int max_num_points, + const function &)> &fill_input, + const function &)> &read_output) +{ + bool first_device = true; + bool success = true; + + device_->foreach_device([&](Device *device) { + if (!first_device) { + LOG(ERROR) << "Multi-devices are not yet fully implemented, will evaluate shader on a " + "single device."; + return; + } + first_device = false; + + device_vector input(device, "ShaderEval input", MEM_READ_ONLY); + device_vector output(device, "ShaderEval output", MEM_READ_WRITE); + + /* Allocate and copy device buffers. */ + DCHECK_EQ(input.device, device); + DCHECK_EQ(output.device, device); + DCHECK_LE(output.size(), input.size()); + + input.alloc(max_num_points); + int num_points = fill_input(input); + if (num_points == 0) { + return; + } + + input.copy_to_device(); + output.alloc(num_points); + output.zero_to_device(); + + /* Evaluate on CPU or GPU. */ + success = (device->info.type == DEVICE_CPU) ? eval_cpu(device, type, input, output) : + eval_gpu(device, type, input, output); + + /* Copy data back from device if not cancelled. */ + if (success) { + output.copy_from_device(0, 1, output.size()); + read_output(output); + } + + input.free(); + output.free(); + }); + + return success; +} + +bool ShaderEval::eval_cpu(Device *device, + const ShaderEvalType type, + device_vector &input, + device_vector &output) +{ + vector kernel_thread_globals; + device->get_cpu_kernel_thread_globals(kernel_thread_globals); + + /* Find required kernel function. */ + const CPUKernels &kernels = *(device->get_cpu_kernels()); + + /* Simple parallel_for over all work items. */ + const int64_t work_size = output.size(); + KernelShaderEvalInput *input_data = input.data(); + float4 *output_data = output.data(); + bool success = true; + + tbb::task_arena local_arena(device->info.cpu_threads); + local_arena.execute([&]() { + tbb::parallel_for(int64_t(0), work_size, [&](int64_t work_index) { + /* TODO: is this fast enough? */ + if (progress_.get_cancel()) { + success = false; + return; + } + + const int thread_index = tbb::this_task_arena::current_thread_index(); + KernelGlobals *kg = &kernel_thread_globals[thread_index]; + + switch (type) { + case SHADER_EVAL_DISPLACE: + kernels.shader_eval_displace(kg, input_data, output_data, work_index); + break; + case SHADER_EVAL_BACKGROUND: + kernels.shader_eval_background(kg, input_data, output_data, work_index); + break; + } + }); + }); + + return success; +} + +bool ShaderEval::eval_gpu(Device *device, + const ShaderEvalType type, + device_vector &input, + device_vector &output) +{ + /* Find required kernel function. */ + DeviceKernel kernel; + switch (type) { + case SHADER_EVAL_DISPLACE: + kernel = DEVICE_KERNEL_SHADER_EVAL_DISPLACE; + break; + case SHADER_EVAL_BACKGROUND: + kernel = DEVICE_KERNEL_SHADER_EVAL_BACKGROUND; + break; + }; + + /* Create device queue. */ + unique_ptr queue = device->gpu_queue_create(); + queue->init_execution(); + + /* Execute work on GPU in chunk, so we can cancel. + * TODO : query appropriate size from device.*/ + const int chunk_size = 65536; + + const int work_size = output.size(); + void *d_input = (void *)input.device_pointer; + void *d_output = (void *)output.device_pointer; + + for (int d_offset = 0; d_offset < work_size; d_offset += chunk_size) { + int d_work_size = min(chunk_size, work_size - d_offset); + void *args[] = {&d_input, &d_output, &d_offset, &d_work_size}; + + queue->enqueue(kernel, d_work_size, args); + queue->synchronize(); + + if (progress_.get_cancel()) { + return false; + } + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/shader_eval.h b/intern/cycles/integrator/shader_eval.h new file mode 100644 index 00000000000..7dbf334b8d7 --- /dev/null +++ b/intern/cycles/integrator/shader_eval.h @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "device/device_memory.h" + +#include "kernel/kernel_types.h" + +#include "util/util_function.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class Progress; + +enum ShaderEvalType { + SHADER_EVAL_DISPLACE, + SHADER_EVAL_BACKGROUND, +}; + +/* ShaderEval class performs shader evaluation for background light and displacement. */ +class ShaderEval { + public: + ShaderEval(Device *device, Progress &progress); + + /* Evaluate shader at points specified by KernelShaderEvalInput and write out + * RGBA colors to output. */ + bool eval(const ShaderEvalType type, + const int max_num_points, + const function &)> &fill_input, + const function &)> &read_output); + + protected: + bool eval_cpu(Device *device, + const ShaderEvalType type, + device_vector &input, + device_vector &output); + bool eval_gpu(Device *device, + const ShaderEvalType type, + device_vector &input, + device_vector &output); + + Device *device_; + Progress &progress_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/tile.cpp b/intern/cycles/integrator/tile.cpp new file mode 100644 index 00000000000..3387b7bedf1 --- /dev/null +++ b/intern/cycles/integrator/tile.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/tile.h" + +#include "util/util_logging.h" +#include "util/util_math.h" + +CCL_NAMESPACE_BEGIN + +std::ostream &operator<<(std::ostream &os, const TileSize &tile_size) +{ + os << "size: (" << tile_size.width << ", " << tile_size.height << ")"; + os << ", num_samples: " << tile_size.num_samples; + return os; +} + +ccl_device_inline uint round_down_to_power_of_two(uint x) +{ + if (is_power_of_two(x)) { + return x; + } + + return prev_power_of_two(x); +} + +ccl_device_inline uint round_up_to_power_of_two(uint x) +{ + if (is_power_of_two(x)) { + return x; + } + + return next_power_of_two(x); +} + +TileSize tile_calculate_best_size(const int2 &image_size, + const int num_samples, + const int max_num_path_states) +{ + if (max_num_path_states == 1) { + /* Simple case: avoid any calculation, which could cause rounding issues. */ + return TileSize(1, 1, 1); + } + + const int64_t num_pixels = image_size.x * image_size.y; + const int64_t num_pixel_samples = num_pixels * num_samples; + + if (max_num_path_states >= num_pixel_samples) { + /* Image fully fits into the state (could be border render, for example). */ + return TileSize(image_size.x, image_size.y, num_samples); + } + + /* The idea here is to keep number of samples per tile as much as possible to improve coherency + * across threads. + * + * Some general ideas: + * - Prefer smaller tiles with more samples, which improves spatial coherency of paths. + * - Keep values a power of two, for more integer fit into the maximum number of paths. */ + + TileSize tile_size; + + /* Calculate tile size as if it is the most possible one to fit an entire range of samples. + * The idea here is to keep tiles as small as possible, and keep device occupied by scheduling + * multiple tiles with the same coordinates rendering different samples. */ + const int num_path_states_per_sample = max_num_path_states / num_samples; + if (num_path_states_per_sample != 0) { + tile_size.width = round_down_to_power_of_two(lround(sqrt(num_path_states_per_sample))); + tile_size.height = tile_size.width; + } + else { + tile_size.width = tile_size.height = 1; + } + + if (num_samples == 1) { + tile_size.num_samples = 1; + } + else { + /* Heuristic here is to have more uniform division of the sample range: for example prefer + * [32 <38 times>, 8] over [1024, 200]. This allows to greedily add more tiles early on. */ + tile_size.num_samples = min(round_up_to_power_of_two(lround(sqrt(num_samples / 2))), + static_cast(num_samples)); + + const int tile_area = tile_size.width / tile_size.height; + tile_size.num_samples = min(tile_size.num_samples, max_num_path_states / tile_area); + } + + DCHECK_GE(tile_size.width, 1); + DCHECK_GE(tile_size.height, 1); + DCHECK_GE(tile_size.num_samples, 1); + DCHECK_LE(tile_size.width * tile_size.height * tile_size.num_samples, max_num_path_states); + + return tile_size; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/tile.h b/intern/cycles/integrator/tile.h new file mode 100644 index 00000000000..d0824843ddb --- /dev/null +++ b/intern/cycles/integrator/tile.h @@ -0,0 +1,56 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "util/util_types.h" + +CCL_NAMESPACE_BEGIN + +struct TileSize { + TileSize() = default; + + inline TileSize(int width, int height, int num_samples) + : width(width), height(height), num_samples(num_samples) + { + } + + inline bool operator==(const TileSize &other) const + { + return width == other.width && height == other.height && num_samples == other.num_samples; + } + inline bool operator!=(const TileSize &other) const + { + return !(*this == other); + } + + int width = 0, height = 0; + int num_samples = 0; +}; + +std::ostream &operator<<(std::ostream &os, const TileSize &tile_size); + +/* Calculate tile size which is best suitable for rendering image of a given size with given number + * of active path states. + * Will attempt to provide best guess to keep path tracing threads of a device as localized as + * possible, and have as many threads active for every tile as possible. */ +TileSize tile_calculate_best_size(const int2 &image_size, + const int num_samples, + const int max_num_path_states); + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/work_balancer.cpp b/intern/cycles/integrator/work_balancer.cpp new file mode 100644 index 00000000000..9f96fe3632b --- /dev/null +++ b/intern/cycles/integrator/work_balancer.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/work_balancer.h" + +#include "util/util_math.h" + +#include "util/util_logging.h" + +CCL_NAMESPACE_BEGIN + +void work_balance_do_initial(vector &work_balance_infos) +{ + const int num_infos = work_balance_infos.size(); + + if (num_infos == 1) { + work_balance_infos[0].weight = 1.0; + return; + } + + /* There is no statistics available, so start with an equal distribution. */ + const double weight = 1.0 / num_infos; + for (WorkBalanceInfo &balance_info : work_balance_infos) { + balance_info.weight = weight; + } +} + +static double calculate_total_time(const vector &work_balance_infos) +{ + double total_time = 0; + for (const WorkBalanceInfo &info : work_balance_infos) { + total_time += info.time_spent; + } + return total_time; +} + +/* The balance is based on equalizing time which devices spent performing a task. Assume that + * average of the observed times is usable for estimating whether more or less work is to be + * scheduled, and how difference in the work scheduling is needed. */ + +bool work_balance_do_rebalance(vector &work_balance_infos) +{ + const int num_infos = work_balance_infos.size(); + + const double total_time = calculate_total_time(work_balance_infos); + const double time_average = total_time / num_infos; + + double total_weight = 0; + vector new_weights; + new_weights.reserve(num_infos); + + /* Equalize the overall average time. This means that we don't make it so every work will perform + * amount of work based on the current average, but that after the weights changes the time will + * equalize. + * Can think of it that if one of the devices is 10% faster than another, then one device needs + * to do 5% less of the current work, and another needs to do 5% more. */ + const double lerp_weight = 1.0 / num_infos; + + bool has_big_difference = false; + + for (const WorkBalanceInfo &info : work_balance_infos) { + const double time_target = lerp(info.time_spent, time_average, lerp_weight); + const double new_weight = info.weight * time_target / info.time_spent; + new_weights.push_back(new_weight); + total_weight += new_weight; + + if (std::fabs(1.0 - time_target / time_average) > 0.02) { + has_big_difference = true; + } + } + + if (!has_big_difference) { + return false; + } + + const double total_weight_inv = 1.0 / total_weight; + for (int i = 0; i < num_infos; ++i) { + WorkBalanceInfo &info = work_balance_infos[i]; + info.weight = new_weights[i] * total_weight_inv; + info.time_spent = 0; + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/work_balancer.h b/intern/cycles/integrator/work_balancer.h new file mode 100644 index 00000000000..94e20ecf054 --- /dev/null +++ b/intern/cycles/integrator/work_balancer.h @@ -0,0 +1,42 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +struct WorkBalanceInfo { + /* Time spent performing corresponding work. */ + double time_spent = 0; + + /* Average occupancy of the device while performing the work. */ + float occupancy = 1.0f; + + /* Normalized weight, which is ready to be used for work balancing (like calculating fraction of + * the big tile which is to be rendered on the device). */ + double weight = 1.0; +}; + +/* Balance work for an initial render interation, before any statistics is known. */ +void work_balance_do_initial(vector &work_balance_infos); + +/* Rebalance work after statistics has been accumulated. + * Returns true if the balancing did change. */ +bool work_balance_do_rebalance(vector &work_balance_infos); + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/work_tile_scheduler.cpp b/intern/cycles/integrator/work_tile_scheduler.cpp new file mode 100644 index 00000000000..3fc99d5b74d --- /dev/null +++ b/intern/cycles/integrator/work_tile_scheduler.cpp @@ -0,0 +1,138 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/work_tile_scheduler.h" + +#include "device/device_queue.h" +#include "integrator/tile.h" +#include "render/buffers.h" +#include "util/util_atomic.h" +#include "util/util_logging.h" + +CCL_NAMESPACE_BEGIN + +WorkTileScheduler::WorkTileScheduler() +{ +} + +void WorkTileScheduler::set_max_num_path_states(int max_num_path_states) +{ + max_num_path_states_ = max_num_path_states; +} + +void WorkTileScheduler::reset(const BufferParams &buffer_params, int sample_start, int samples_num) +{ + /* Image buffer parameters. */ + image_full_offset_px_.x = buffer_params.full_x; + image_full_offset_px_.y = buffer_params.full_y; + + image_size_px_ = make_int2(buffer_params.width, buffer_params.height); + + offset_ = buffer_params.offset; + stride_ = buffer_params.stride; + + /* Samples parameters. */ + sample_start_ = sample_start; + samples_num_ = samples_num; + + /* Initialize new scheduling. */ + reset_scheduler_state(); +} + +void WorkTileScheduler::reset_scheduler_state() +{ + tile_size_ = tile_calculate_best_size(image_size_px_, samples_num_, max_num_path_states_); + + VLOG(3) << "Will schedule tiles of size " << tile_size_; + + if (VLOG_IS_ON(3)) { + /* The logging is based on multiple tiles scheduled, ignoring overhead of multi-tile scheduling + * and purely focusing on the number of used path states. */ + const int num_path_states_in_tile = tile_size_.width * tile_size_.height * + tile_size_.num_samples; + const int num_tiles = max_num_path_states_ / num_path_states_in_tile; + VLOG(3) << "Number of unused path states: " + << max_num_path_states_ - num_tiles * num_path_states_in_tile; + } + + num_tiles_x_ = divide_up(image_size_px_.x, tile_size_.width); + num_tiles_y_ = divide_up(image_size_px_.y, tile_size_.height); + + total_tiles_num_ = num_tiles_x_ * num_tiles_y_; + num_tiles_per_sample_range_ = divide_up(samples_num_, tile_size_.num_samples); + + next_work_index_ = 0; + total_work_size_ = total_tiles_num_ * num_tiles_per_sample_range_; +} + +bool WorkTileScheduler::get_work(KernelWorkTile *work_tile_, const int max_work_size) +{ + /* Note that the `max_work_size` can be higher than the `max_num_path_states_`: this is because + * the path trace work can decice to use smaller tile sizes and greedily schedule multiple tiles, + * improving overall device occupancy. + * So the `max_num_path_states_` is a "scheduling unit", and the `max_work_size` is a "scheduling + * limit". */ + + DCHECK_NE(max_num_path_states_, 0); + + const int work_index = atomic_fetch_and_add_int32(&next_work_index_, 1); + if (work_index >= total_work_size_) { + return false; + } + + const int sample_range_index = work_index % num_tiles_per_sample_range_; + const int start_sample = sample_range_index * tile_size_.num_samples; + const int tile_index = work_index / num_tiles_per_sample_range_; + const int tile_y = tile_index / num_tiles_x_; + const int tile_x = tile_index - tile_y * num_tiles_x_; + + KernelWorkTile work_tile; + work_tile.x = tile_x * tile_size_.width; + work_tile.y = tile_y * tile_size_.height; + work_tile.w = tile_size_.width; + work_tile.h = tile_size_.height; + work_tile.start_sample = sample_start_ + start_sample; + work_tile.num_samples = min(tile_size_.num_samples, samples_num_ - start_sample); + work_tile.offset = offset_; + work_tile.stride = stride_; + + work_tile.w = min(work_tile.w, image_size_px_.x - work_tile.x); + work_tile.h = min(work_tile.h, image_size_px_.y - work_tile.y); + + work_tile.x += image_full_offset_px_.x; + work_tile.y += image_full_offset_px_.y; + + const int tile_work_size = work_tile.w * work_tile.h * work_tile.num_samples; + + DCHECK_GT(tile_work_size, 0); + + if (max_work_size && tile_work_size > max_work_size) { + /* The work did not fit into the requested limit of the work size. Unschedule the tile, + * allowing others (or ourselves later one) to pick it up. + * + * TODO: Such temporary decrement is not ideal, since it might lead to situation when another + * device sees there is nothing to be done, finishing its work and leaving all work to be + * done by us. */ + atomic_fetch_and_add_int32(&next_work_index_, -1); + return false; + } + + *work_tile_ = work_tile; + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/work_tile_scheduler.h b/intern/cycles/integrator/work_tile_scheduler.h new file mode 100644 index 00000000000..e4c8f701259 --- /dev/null +++ b/intern/cycles/integrator/work_tile_scheduler.h @@ -0,0 +1,98 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/tile.h" +#include "util/util_types.h" + +CCL_NAMESPACE_BEGIN + +class BufferParams; + +struct KernelWorkTile; + +/* Scheduler of device work tiles. + * Takes care of feeding multiple devices running in parallel a work which needs to be done. */ +class WorkTileScheduler { + public: + WorkTileScheduler(); + + /* MAximum path states which are allowed to be used by a single scheduled work tile. + * + * Affects the scheduled work size: the work size will be as big as possible, but will not exceed + * this number of states. */ + void set_max_num_path_states(int max_num_path_states); + + /* Scheduling will happen for pixels within a big tile denotes by its parameters. */ + void reset(const BufferParams &buffer_params, int sample_start, int samples_num); + + /* Get work for a device. + * Returns true if there is still work to be done and initialize the work tile to all + * parameters of this work. If there is nothing remaining to be done, returns false and the + * work tile is kept unchanged. + * + * Optionally pass max_work_size to do nothing if there is no tile small enough. */ + bool get_work(KernelWorkTile *work_tile, const int max_work_size = 0); + + protected: + void reset_scheduler_state(); + + /* Maximum allowed path states to be used. + * + * TODO(sergey): Naming can be improved. The fact that this is a limiting factor based on the + * number of path states is kind of a detail. Is there a more generic term from the scheduler + * point of view? */ + int max_num_path_states_ = 0; + + /* Offset in pixels within a global buffer. */ + int2 image_full_offset_px_ = make_int2(0, 0); + + /* dimensions of the currently rendering image in pixels. */ + int2 image_size_px_ = make_int2(0, 0); + + /* Offset and stride of the buffer within which scheduing is happenning. + * Will be passed over to the KernelWorkTile. */ + int offset_, stride_; + + /* Start sample of index and number of samples which are to be rendered. + * The scheduler will cover samples range of [start, start + num] over the entire image + * (splitting into a smaller work tiles). */ + int sample_start_ = 0; + int samples_num_ = 0; + + /* Tile size which be scheduled for rendering. */ + TileSize tile_size_; + + /* Number of tiles in X and Y axis of the image. */ + int num_tiles_x_, num_tiles_y_; + + /* Total number of tiles on the image. + * Pre-calculated as `num_tiles_x_ * num_tiles_y_` and re-used in the `get_work()`. + * + * TODO(sergey): Is this an over-optimization? Maybe it's unmeasurable to calculate the value + * in the `get_work()`? */ + int total_tiles_num_ = 0; + + /* In the case when the number of sam[les in the `tile_size_` is lower than samples_num_ denotes + * how many tiles are to be "stacked" to cover the entire requested range of samples. */ + int num_tiles_per_sample_range_ = 0; + + int next_work_index_ = 0; + int total_work_size_ = 0; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 0ce33c51778..4196539a9b1 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -22,68 +22,22 @@ set(INC_SYS ) -set(SRC_CPU_KERNELS - kernels/cpu/kernel.cpp - kernels/cpu/kernel_sse2.cpp - kernels/cpu/kernel_sse3.cpp - kernels/cpu/kernel_sse41.cpp - kernels/cpu/kernel_avx.cpp - kernels/cpu/kernel_avx2.cpp - kernels/cpu/kernel_split.cpp - kernels/cpu/kernel_split_sse2.cpp - kernels/cpu/kernel_split_sse3.cpp - kernels/cpu/kernel_split_sse41.cpp - kernels/cpu/kernel_split_avx.cpp - kernels/cpu/kernel_split_avx2.cpp - kernels/cpu/filter.cpp - kernels/cpu/filter_sse2.cpp - kernels/cpu/filter_sse3.cpp - kernels/cpu/filter_sse41.cpp - kernels/cpu/filter_avx.cpp - kernels/cpu/filter_avx2.cpp +set(SRC_DEVICE_CPU + device/cpu/kernel.cpp + device/cpu/kernel_sse2.cpp + device/cpu/kernel_sse3.cpp + device/cpu/kernel_sse41.cpp + device/cpu/kernel_avx.cpp + device/cpu/kernel_avx2.cpp ) -set(SRC_CUDA_KERNELS - kernels/cuda/kernel.cu - kernels/cuda/kernel_split.cu - kernels/cuda/filter.cu +set(SRC_DEVICE_CUDA + device/cuda/kernel.cu ) -set(SRC_OPENCL_KERNELS - kernels/opencl/kernel_adaptive_stopping.cl - kernels/opencl/kernel_adaptive_filter_x.cl - kernels/opencl/kernel_adaptive_filter_y.cl - kernels/opencl/kernel_adaptive_adjust_samples.cl - kernels/opencl/kernel_bake.cl - kernels/opencl/kernel_base.cl - kernels/opencl/kernel_displace.cl - kernels/opencl/kernel_background.cl - kernels/opencl/kernel_state_buffer_size.cl - kernels/opencl/kernel_split_bundle.cl - kernels/opencl/kernel_data_init.cl - kernels/opencl/kernel_path_init.cl - kernels/opencl/kernel_queue_enqueue.cl - kernels/opencl/kernel_scene_intersect.cl - kernels/opencl/kernel_lamp_emission.cl - kernels/opencl/kernel_do_volume.cl - kernels/opencl/kernel_indirect_background.cl - kernels/opencl/kernel_shader_setup.cl - kernels/opencl/kernel_shader_sort.cl - kernels/opencl/kernel_shader_eval.cl - kernels/opencl/kernel_holdout_emission_blurring_pathtermination_ao.cl - kernels/opencl/kernel_subsurface_scatter.cl - kernels/opencl/kernel_direct_lighting.cl - kernels/opencl/kernel_shadow_blocked_ao.cl - kernels/opencl/kernel_shadow_blocked_dl.cl - kernels/opencl/kernel_enqueue_inactive.cl - kernels/opencl/kernel_next_iteration_setup.cl - kernels/opencl/kernel_indirect_subsurface.cl - kernels/opencl/kernel_buffer_update.cl - kernels/opencl/filter.cl -) - -set(SRC_OPTIX_KERNELS - kernels/optix/kernel_optix.cu +set(SRC_DEVICE_OPTIX + device/optix/kernel.cu + device/optix/kernel_shader_raytrace.cu ) set(SRC_BVH_HEADERS @@ -105,63 +59,56 @@ set(SRC_HEADERS kernel_bake.h kernel_camera.h kernel_color.h - kernel_compat_cpu.h - kernel_compat_cuda.h - kernel_compat_optix.h - kernel_compat_opencl.h kernel_differential.h kernel_emission.h kernel_film.h - kernel_globals.h kernel_id_passes.h kernel_jitter.h kernel_light.h kernel_light_background.h kernel_light_common.h + kernel_lookup_table.h kernel_math.h kernel_montecarlo.h kernel_passes.h - kernel_path.h - kernel_path_branched.h - kernel_path_common.h kernel_path_state.h - kernel_path_surface.h - kernel_path_subsurface.h - kernel_path_volume.h kernel_profiling.h kernel_projection.h - kernel_queues.h kernel_random.h kernel_shader.h - kernel_shadow.h - kernel_subsurface.h + kernel_shadow_catcher.h kernel_textures.h kernel_types.h - kernel_volume.h kernel_work_stealing.h kernel_write_passes.h ) -set(SRC_KERNELS_CPU_HEADERS - kernel.h - kernels/cpu/kernel_cpu.h - kernels/cpu/kernel_cpu_impl.h - kernels/cpu/kernel_cpu_image.h - kernels/cpu/filter_cpu.h - kernels/cpu/filter_cpu_impl.h +set(SRC_DEVICE_CPU_HEADERS + device/cpu/compat.h + device/cpu/image.h + device/cpu/globals.h + device/cpu/kernel.h + device/cpu/kernel_arch.h + device/cpu/kernel_arch_impl.h +) +set(SRC_DEVICE_GPU_HEADERS + device/gpu/image.h + device/gpu/kernel.h + device/gpu/parallel_active_index.h + device/gpu/parallel_prefix_sum.h + device/gpu/parallel_reduce.h + device/gpu/parallel_sorted_index.h ) -set(SRC_KERNELS_CUDA_HEADERS - kernels/cuda/kernel_config.h - kernels/cuda/kernel_cuda_image.h +set(SRC_DEVICE_CUDA_HEADERS + device/cuda/compat.h + device/cuda/config.h + device/cuda/globals.h ) -set(SRC_KERNELS_OPTIX_HEADERS -) - -set(SRC_KERNELS_OPENCL_HEADERS - kernels/opencl/kernel_split_function.h - kernels/opencl/kernel_opencl_image.h +set(SRC_DEVICE_OPTIX_HEADERS + device/optix/compat.h + device/optix/globals.h ) set(SRC_CLOSURE_HEADERS @@ -259,25 +206,32 @@ set(SRC_GEOM_HEADERS geom/geom_object.h geom/geom_patch.h geom/geom_primitive.h + geom/geom_shader_data.h geom/geom_subd_triangle.h geom/geom_triangle.h geom/geom_triangle_intersect.h geom/geom_volume.h ) -set(SRC_FILTER_HEADERS - filter/filter.h - filter/filter_defines.h - filter/filter_features.h - filter/filter_features_sse.h - filter/filter_kernel.h - filter/filter_nlm_cpu.h - filter/filter_nlm_gpu.h - filter/filter_prefilter.h - filter/filter_reconstruction.h - filter/filter_transform.h - filter/filter_transform_gpu.h - filter/filter_transform_sse.h +set(SRC_INTEGRATOR_HEADERS + integrator/integrator_init_from_bake.h + integrator/integrator_init_from_camera.h + integrator/integrator_intersect_closest.h + integrator/integrator_intersect_shadow.h + integrator/integrator_intersect_subsurface.h + integrator/integrator_intersect_volume_stack.h + integrator/integrator_megakernel.h + integrator/integrator_shade_background.h + integrator/integrator_shade_light.h + integrator/integrator_shade_shadow.h + integrator/integrator_shade_surface.h + integrator/integrator_shade_volume.h + integrator/integrator_state.h + integrator/integrator_state_flow.h + integrator/integrator_state_template.h + integrator/integrator_state_util.h + integrator/integrator_subsurface.h + integrator/integrator_volume_stack.h ) set(SRC_UTIL_HEADERS @@ -333,36 +287,6 @@ set(SRC_UTIL_HEADERS ../util/util_types_vector3_impl.h ) -set(SRC_SPLIT_HEADERS - split/kernel_adaptive_adjust_samples.h - split/kernel_adaptive_filter_x.h - split/kernel_adaptive_filter_y.h - split/kernel_adaptive_stopping.h - split/kernel_branched.h - split/kernel_buffer_update.h - split/kernel_data_init.h - split/kernel_direct_lighting.h - split/kernel_do_volume.h - split/kernel_enqueue_inactive.h - split/kernel_holdout_emission_blurring_pathtermination_ao.h - split/kernel_indirect_background.h - split/kernel_indirect_subsurface.h - split/kernel_lamp_emission.h - split/kernel_next_iteration_setup.h - split/kernel_path_init.h - split/kernel_queue_enqueue.h - split/kernel_scene_intersect.h - split/kernel_shader_setup.h - split/kernel_shader_sort.h - split/kernel_shader_eval.h - split/kernel_shadow_blocked_ao.h - split/kernel_shadow_blocked_dl.h - split/kernel_split_common.h - split/kernel_split_data.h - split/kernel_split_data_types.h - split/kernel_subsurface_scatter.h -) - set(LIB ) @@ -393,21 +317,17 @@ if(WITH_CYCLES_CUDA_BINARIES) endif() # build for each arch - set(cuda_sources kernels/cuda/kernel.cu kernels/cuda/kernel_split.cu + set(cuda_sources device/cuda/kernel.cu ${SRC_HEADERS} - ${SRC_KERNELS_CUDA_HEADERS} + ${SRC_DEVICE_GPU_HEADERS} + ${SRC_DEVICE_CUDA_HEADERS} ${SRC_BVH_HEADERS} ${SRC_SVM_HEADERS} ${SRC_GEOM_HEADERS} + ${SRC_INTEGRATOR_HEADERS} ${SRC_CLOSURE_HEADERS} ${SRC_UTIL_HEADERS} ) - set(cuda_filter_sources kernels/cuda/filter.cu - ${SRC_HEADERS} - ${SRC_KERNELS_CUDA_HEADERS} - ${SRC_FILTER_HEADERS} - ${SRC_UTIL_HEADERS} - ) set(cuda_cubins) macro(CYCLES_CUDA_KERNEL_ADD arch prev_arch name flags sources experimental) @@ -427,7 +347,7 @@ if(WITH_CYCLES_CUDA_BINARIES) endif() endif() - set(cuda_kernel_src "/kernels/cuda/${name}.cu") + set(cuda_kernel_src "/device/cuda/${name}.cu") set(cuda_flags ${flags} -D CCL_NAMESPACE_BEGIN= @@ -435,7 +355,7 @@ if(WITH_CYCLES_CUDA_BINARIES) -D NVCC -m ${CUDA_BITS} -I ${CMAKE_CURRENT_SOURCE_DIR}/.. - -I ${CMAKE_CURRENT_SOURCE_DIR}/kernels/cuda + -I ${CMAKE_CURRENT_SOURCE_DIR}/device/cuda --use_fast_math -o ${CMAKE_CURRENT_BINARY_DIR}/${cuda_file}) @@ -523,14 +443,8 @@ if(WITH_CYCLES_CUDA_BINARIES) endif() if(DEFINED cuda_nvcc_executable AND DEFINED cuda_toolkit_root_dir) # Compile regular kernel - CYCLES_CUDA_KERNEL_ADD(${arch} ${prev_arch} filter "" "${cuda_filter_sources}" FALSE) CYCLES_CUDA_KERNEL_ADD(${arch} ${prev_arch} kernel "" "${cuda_sources}" FALSE) - if(WITH_CYCLES_CUDA_SPLIT_KERNEL_BINARIES) - # Compile split kernel - CYCLES_CUDA_KERNEL_ADD(${arch} ${prev_arch} kernel_split "-D __SPLIT__" "${cuda_sources}" FALSE) - endif() - if(WITH_CYCLES_CUDA_BUILD_SERIAL) set(prev_arch ${arch}) endif() @@ -547,15 +461,15 @@ endif() # OptiX PTX modules if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) - macro(CYCLES_OPTIX_KERNEL_ADD name flags) - set(input "kernels/optix/kernel_optix.cu") + macro(CYCLES_OPTIX_KERNEL_ADD name input flags) set(output "${CMAKE_CURRENT_BINARY_DIR}/${name}.ptx") set(cuda_flags ${flags} -I "${OPTIX_INCLUDE_DIR}" -I "${CMAKE_CURRENT_SOURCE_DIR}/.." - -I "${CMAKE_CURRENT_SOURCE_DIR}/kernels/cuda" + -I "${CMAKE_CURRENT_SOURCE_DIR}/device/cuda" --use_fast_math + -Wno-deprecated-gpu-targets -o ${output}) if(WITH_NANOVDB) @@ -580,11 +494,13 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) DEPENDS ${input} ${SRC_HEADERS} - ${SRC_KERNELS_CUDA_HEADERS} - ${SRC_KERNELS_OPTIX_HEADERS} + ${SRC_DEVICE_GPU_HEADERS} + ${SRC_DEVICE_CUDA_HEADERS} + ${SRC_DEVICE_OPTIX_HEADERS} ${SRC_BVH_HEADERS} ${SRC_SVM_HEADERS} ${SRC_GEOM_HEADERS} + ${SRC_INTEGRATOR_HEADERS} ${SRC_CLOSURE_HEADERS} ${SRC_UTIL_HEADERS} COMMAND ${CUBIN_CC_ENV} @@ -603,11 +519,13 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) DEPENDS ${input} ${SRC_HEADERS} - ${SRC_KERNELS_CUDA_HEADERS} - ${SRC_KERNELS_OPTIX_HEADERS} + ${SRC_DEVICE_GPU_HEADERS} + ${SRC_DEVICE_CUDA_HEADERS} + ${SRC_DEVICE_OPTIX_HEADERS} ${SRC_BVH_HEADERS} ${SRC_SVM_HEADERS} ${SRC_GEOM_HEADERS} + ${SRC_INTEGRATOR_HEADERS} ${SRC_CLOSURE_HEADERS} ${SRC_UTIL_HEADERS} COMMAND @@ -624,8 +542,14 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${output}" ${CYCLES_INSTALL_PATH}/lib) endmacro() - CYCLES_OPTIX_KERNEL_ADD(kernel_optix "-D __NO_SHADER_RAYTRACE__") - CYCLES_OPTIX_KERNEL_ADD(kernel_optix_shader_raytrace "--keep-device-functions") + CYCLES_OPTIX_KERNEL_ADD( + kernel_optix + "device/optix/kernel.cu" + "") + CYCLES_OPTIX_KERNEL_ADD( + kernel_optix_shader_raytrace + "device/optix/kernel_shader_raytrace.cu" + "--keep-device-functions") add_custom_target(cycles_kernel_optix ALL DEPENDS ${optix_ptx}) cycles_set_solution_folder(cycles_kernel_optix) @@ -659,62 +583,47 @@ if(WITH_COMPILER_ASAN) endif() endif() -set_source_files_properties(kernels/cpu/kernel.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_KERNEL_FLAGS}") -set_source_files_properties(kernels/cpu/kernel_split.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_KERNEL_FLAGS}") -set_source_files_properties(kernels/cpu/filter.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_KERNEL_FLAGS}") +set_source_files_properties(device/cpu/kernel.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_KERNEL_FLAGS}") if(CXX_HAS_SSE) - set_source_files_properties(kernels/cpu/kernel_sse2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE2_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_sse3.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE3_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_sse41.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE41_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_split_sse2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE2_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_split_sse3.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE3_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_split_sse41.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE41_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/filter_sse2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE2_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/filter_sse3.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE3_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/filter_sse41.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE41_KERNEL_FLAGS}") + set_source_files_properties(device/cpu/kernel_sse2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE2_KERNEL_FLAGS}") + set_source_files_properties(device/cpu/kernel_sse3.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE3_KERNEL_FLAGS}") + set_source_files_properties(device/cpu/kernel_sse41.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE41_KERNEL_FLAGS}") endif() if(CXX_HAS_AVX) - set_source_files_properties(kernels/cpu/kernel_avx.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_split_avx.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/filter_avx.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX_KERNEL_FLAGS}") + set_source_files_properties(device/cpu/kernel_avx.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX_KERNEL_FLAGS}") endif() if(CXX_HAS_AVX2) - set_source_files_properties(kernels/cpu/kernel_avx2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_split_avx2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/filter_avx2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}") + set_source_files_properties(device/cpu/kernel_avx2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}") endif() cycles_add_library(cycles_kernel "${LIB}" - ${SRC_CPU_KERNELS} - ${SRC_CUDA_KERNELS} - ${SRC_OPTIX_KERNELS} - ${SRC_OPENCL_KERNELS} + ${SRC_DEVICE_CPU} + ${SRC_DEVICE_CUDA} + ${SRC_DEVICE_OPTIX} ${SRC_HEADERS} - ${SRC_KERNELS_CPU_HEADERS} - ${SRC_KERNELS_CUDA_HEADERS} - ${SRC_KERNELS_OPTIX_HEADERS} - ${SRC_KERNELS_OPENCL_HEADERS} + ${SRC_DEVICE_CPU_HEADERS} + ${SRC_DEVICE_GPU_HEADERS} + ${SRC_DEVICE_CUDA_HEADERS} + ${SRC_DEVICE_OPTIX_HEADERS} ${SRC_BVH_HEADERS} ${SRC_CLOSURE_HEADERS} - ${SRC_FILTER_HEADERS} ${SRC_SVM_HEADERS} ${SRC_GEOM_HEADERS} - ${SRC_SPLIT_HEADERS} + ${SRC_INTEGRATOR_HEADERS} ) source_group("bvh" FILES ${SRC_BVH_HEADERS}) source_group("closure" FILES ${SRC_CLOSURE_HEADERS}) -source_group("filter" FILES ${SRC_FILTER_HEADERS}) source_group("geom" FILES ${SRC_GEOM_HEADERS}) +source_group("integrator" FILES ${SRC_INTEGRATOR_HEADERS}) source_group("kernel" FILES ${SRC_HEADERS}) -source_group("kernel\\split" FILES ${SRC_SPLIT_HEADERS}) -source_group("kernels\\cpu" FILES ${SRC_CPU_KERNELS} ${SRC_KERNELS_CPU_HEADERS}) -source_group("kernels\\cuda" FILES ${SRC_CUDA_KERNELS} ${SRC_KERNELS_CUDA_HEADERS}) -source_group("kernels\\opencl" FILES ${SRC_OPENCL_KERNELS} ${SRC_KERNELS_OPENCL_HEADERS}) -source_group("kernels\\optix" FILES ${SRC_OPTIX_KERNELS} ${SRC_KERNELS_OPTIX_HEADERS}) +source_group("device\\cpu" FILES ${SRC_DEVICE_CPU} ${SRC_DEVICE_CPU_HEADERS}) +source_group("device\\gpu" FILES ${SRC_DEVICE_GPU_HEADERS}) +source_group("device\\cuda" FILES ${SRC_DEVICE_CUDA} ${SRC_DEVICE_CUDA_HEADERS}) +source_group("device\\optix" FILES ${SRC_DEVICE_OPTIX} ${SRC_DEVICE_OPTIX_HEADERS}) source_group("svm" FILES ${SRC_SVM_HEADERS}) if(WITH_CYCLES_CUDA) @@ -724,31 +633,20 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) add_dependencies(cycles_kernel cycles_kernel_optix) endif() -# OpenCL kernel +# Install kernel source for runtime compilation -# set(KERNEL_PREPROCESSED ${CMAKE_CURRENT_BINARY_DIR}/kernel_preprocessed.cl) -# add_custom_command( -# OUTPUT ${KERNEL_PREPROCESSED} -# COMMAND gcc -x c++ -E ${CMAKE_CURRENT_SOURCE_DIR}/kernel.cl -I ${CMAKE_CURRENT_SOURCE_DIR}/../util/ -DCCL_NAMESPACE_BEGIN= -DCCL_NAMESPACE_END= -o ${KERNEL_PREPROCESSED} -# DEPENDS ${SRC_KERNEL} ${SRC_UTIL_HEADERS}) -# add_custom_target(cycles_kernel_preprocess ALL DEPENDS ${KERNEL_PREPROCESSED}) -# delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${KERNEL_PREPROCESSED}" ${CYCLES_INSTALL_PATH}/kernel) - -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_OPENCL_KERNELS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/opencl) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_CUDA_KERNELS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/cuda) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_OPTIX_KERNELS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/optix) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_CUDA}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_OPTIX}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNELS_OPENCL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/opencl) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNELS_CUDA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/cuda) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNELS_OPTIX_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/optix) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_GPU_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/gpu) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_CUDA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_OPTIX_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_BVH_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bvh) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_CLOSURE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/closure) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_FILTER_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/filter) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_SVM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/svm) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_GEOM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/geom) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_INTEGRATOR_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/integrator) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_UTIL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/util) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_SPLIT_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/split) - if(WITH_NANOVDB) set(SRC_NANOVDB_HEADERS diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index acf29cf1baf..539e9fd05fb 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -25,6 +25,8 @@ * the code has been extended and modified to support more primitives and work * with CPU/CUDA/OpenCL. */ +#pragma once + #ifdef __EMBREE__ # include "kernel/bvh/bvh_embree.h" #endif @@ -152,13 +154,11 @@ ccl_device_inline bool scene_intersect_valid(const Ray *ray) return isfinite_safe(ray->P.x) && isfinite_safe(ray->D.x) && len_squared(ray->D) != 0.0f; } -ccl_device_intersect bool scene_intersect(KernelGlobals *kg, +ccl_device_intersect bool scene_intersect(const KernelGlobals *kg, const Ray *ray, const uint visibility, Intersection *isect) { - PROFILING_INIT(kg, PROFILING_INTERSECT); - #ifdef __KERNEL_OPTIX__ uint p0 = 0; uint p1 = 0; @@ -238,15 +238,13 @@ ccl_device_intersect bool scene_intersect(KernelGlobals *kg, } #ifdef __BVH_LOCAL__ -ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg, +ccl_device_intersect bool scene_intersect_local(const KernelGlobals *kg, const Ray *ray, LocalIntersection *local_isect, int local_object, uint *lcg_state, int max_hits) { - PROFILING_INIT(kg, PROFILING_INTERSECT_LOCAL); - # ifdef __KERNEL_OPTIX__ uint p0 = ((uint64_t)lcg_state) & 0xFFFFFFFF; uint p1 = (((uint64_t)lcg_state) >> 32) & 0xFFFFFFFF; @@ -313,8 +311,8 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg, float3 dir = ray->D; float3 idir = ray->D; Transform ob_itfm; - rtc_ray.tfar = bvh_instance_motion_push( - kg, local_object, ray, &P, &dir, &idir, ray->t, &ob_itfm); + rtc_ray.tfar = ray->t * + bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, &ob_itfm); /* bvh_instance_motion_push() returns the inverse transform but * it's not needed here. */ (void)ob_itfm; @@ -353,15 +351,13 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg, #endif #ifdef __SHADOW_RECORD_ALL__ -ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg, +ccl_device_intersect bool scene_intersect_shadow_all(const KernelGlobals *kg, const Ray *ray, Intersection *isect, uint visibility, uint max_hits, uint *num_hits) { - PROFILING_INIT(kg, PROFILING_INTERSECT_SHADOW_ALL); - # ifdef __KERNEL_OPTIX__ uint p0 = ((uint64_t)isect) & 0xFFFFFFFF; uint p1 = (((uint64_t)isect) >> 32) & 0xFFFFFFFF; @@ -401,17 +397,13 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg, CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_SHADOW_ALL); ctx.isect_s = isect; ctx.max_hits = max_hits; - ctx.num_hits = 0; IntersectContext rtc_ctx(&ctx); RTCRay rtc_ray; kernel_embree_setup_ray(*ray, rtc_ray, visibility); rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); - if (ctx.num_hits > max_hits) { - return true; - } *num_hits = ctx.num_hits; - return rtc_ray.tfar == -INFINITY; + return ctx.opaque_hit; } # endif /* __EMBREE__ */ @@ -439,13 +431,11 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg, #endif /* __SHADOW_RECORD_ALL__ */ #ifdef __VOLUME__ -ccl_device_intersect bool scene_intersect_volume(KernelGlobals *kg, +ccl_device_intersect bool scene_intersect_volume(const KernelGlobals *kg, const Ray *ray, Intersection *isect, const uint visibility) { - PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME); - # ifdef __KERNEL_OPTIX__ uint p0 = 0; uint p1 = 0; @@ -498,14 +488,12 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals *kg, #endif /* __VOLUME__ */ #ifdef __VOLUME_RECORD_ALL__ -ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals *kg, +ccl_device_intersect uint scene_intersect_volume_all(const KernelGlobals *kg, const Ray *ray, Intersection *isect, const uint max_hits, const uint visibility) { - PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_ALL); - if (!scene_intersect_valid(ray)) { return false; } diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/bvh_embree.h index 4605c3ea51d..092d770dcac 100644 --- a/intern/cycles/kernel/bvh/bvh_embree.h +++ b/intern/cycles/kernel/bvh/bvh_embree.h @@ -14,14 +14,13 @@ * limitations under the License. */ +#pragma once + #include #include -// clang-format off -#include "kernel/kernel_compat_cpu.h" -#include "kernel/split/kernel_split_data_types.h" -#include "kernel/kernel_globals.h" -// clang-format on +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" #include "util/util_vector.h" @@ -36,25 +35,29 @@ struct CCLIntersectContext { RAY_VOLUME_ALL = 4, } RayType; - KernelGlobals *kg; + const KernelGlobals *kg; RayType type; /* for shadow rays */ Intersection *isect_s; int max_hits; int num_hits; + float max_t; + bool opaque_hit; /* for SSS Rays: */ LocalIntersection *local_isect; int local_object_id; uint *lcg_state; - CCLIntersectContext(KernelGlobals *kg_, RayType type_) + CCLIntersectContext(const KernelGlobals *kg_, RayType type_) { kg = kg_; type = type_; max_hits = 1; num_hits = 0; + max_t = FLT_MAX; + opaque_hit = false; isect_s = NULL; local_isect = NULL; local_object_id = -1; @@ -98,7 +101,7 @@ ccl_device_inline void kernel_embree_setup_rayhit(const Ray &ray, rayhit.hit.primID = RTC_INVALID_GEOMETRY_ID; } -ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg, +ccl_device_inline void kernel_embree_convert_hit(const KernelGlobals *kg, const RTCRay *ray, const RTCHit *hit, Intersection *isect) @@ -123,7 +126,7 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg, isect->type = kernel_tex_fetch(__prim_type, isect->prim); } -ccl_device_inline void kernel_embree_convert_sss_hit(KernelGlobals *kg, +ccl_device_inline void kernel_embree_convert_sss_hit(const KernelGlobals *kg, const RTCRay *ray, const RTCHit *hit, Intersection *isect, diff --git a/intern/cycles/kernel/bvh/bvh_local.h b/intern/cycles/kernel/bvh/bvh_local.h index 4006c9c1632..90b9f410b29 100644 --- a/intern/cycles/kernel/bvh/bvh_local.h +++ b/intern/cycles/kernel/bvh/bvh_local.h @@ -36,7 +36,7 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, + bool BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, const Ray *ray, LocalIntersection *local_isect, int local_object, @@ -74,9 +74,9 @@ ccl_device_inline if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; - isect_t = bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, isect_t, &ob_itfm); + isect_t *= bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, &ob_itfm); #else - isect_t = bvh_instance_push(kg, local_object, ray, &P, &dir, &idir, isect_t); + isect_t *= bvh_instance_push(kg, local_object, ray, &P, &dir, &idir); #endif object = local_object; } @@ -196,7 +196,7 @@ ccl_device_inline return false; } -ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg, +ccl_device_inline bool BVH_FUNCTION_NAME(const KernelGlobals *kg, const Ray *ray, LocalIntersection *local_isect, int local_object, diff --git a/intern/cycles/kernel/bvh/bvh_nodes.h b/intern/cycles/kernel/bvh/bvh_nodes.h index 5367bdb633c..15cd0f22213 100644 --- a/intern/cycles/kernel/bvh/bvh_nodes.h +++ b/intern/cycles/kernel/bvh/bvh_nodes.h @@ -16,7 +16,7 @@ // TODO(sergey): Look into avoid use of full Transform and use 3x3 matrix and // 3-vector which might be faster. -ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals *kg, +ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(const KernelGlobals *kg, int node_addr, int child) { @@ -28,7 +28,7 @@ ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals *k return space; } -ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals *kg, +ccl_device_forceinline int bvh_aligned_node_intersect(const KernelGlobals *kg, const float3 P, const float3 idir, const float t, @@ -76,7 +76,7 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals *kg, #endif } -ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals *kg, +ccl_device_forceinline bool bvh_unaligned_node_intersect_child(const KernelGlobals *kg, const float3 P, const float3 dir, const float t, @@ -102,7 +102,7 @@ ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals *kg return tnear <= tfar; } -ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals *kg, +ccl_device_forceinline int bvh_unaligned_node_intersect(const KernelGlobals *kg, const float3 P, const float3 dir, const float3 idir, @@ -134,7 +134,7 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals *kg, return mask; } -ccl_device_forceinline int bvh_node_intersect(KernelGlobals *kg, +ccl_device_forceinline int bvh_node_intersect(const KernelGlobals *kg, const float3 P, const float3 dir, const float3 idir, diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index 2e94b1d7c37..0ae36fccf9b 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -36,7 +36,7 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, + bool BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, const Ray *ray, Intersection *isect_array, const uint visibility, @@ -68,10 +68,10 @@ ccl_device_inline Transform ob_itfm; #endif - int num_hits_in_instance = 0; + float t_world_to_instance = 1.0f; *num_hits = 0; - isect_array->t = tmax; + Intersection *isect = isect_array; /* traversal loop */ do { @@ -147,13 +147,14 @@ ccl_device_inline switch (p_type) { case PRIMITIVE_TRIANGLE: { - hit = triangle_intersect(kg, isect_array, P, dir, visibility, object, prim_addr); + hit = triangle_intersect( + kg, isect, P, dir, isect_t, visibility, object, prim_addr); break; } #if BVH_FEATURE(BVH_MOTION) case PRIMITIVE_MOTION_TRIANGLE: { hit = motion_triangle_intersect( - kg, isect_array, P, dir, ray->time, visibility, object, prim_addr); + kg, isect, P, dir, isect_t, ray->time, visibility, object, prim_addr); break; } #endif @@ -163,8 +164,16 @@ ccl_device_inline case PRIMITIVE_CURVE_RIBBON: case PRIMITIVE_MOTION_CURVE_RIBBON: { const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); - hit = curve_intersect( - kg, isect_array, P, dir, visibility, object, prim_addr, ray->time, curve_type); + hit = curve_intersect(kg, + isect, + P, + dir, + isect_t, + visibility, + object, + prim_addr, + ray->time, + curve_type); break; } #endif @@ -176,27 +185,49 @@ ccl_device_inline /* shadow ray early termination */ if (hit) { + /* Convert intersection distance to world space. */ + isect->t /= t_world_to_instance; + /* detect if this surface has a shader with transparent shadows */ /* todo: optimize so primitive visibility flag indicates if * the primitive has a transparent shadow shader? */ - const int flags = intersection_get_shader_flags(kg, isect_array); + const int flags = intersection_get_shader_flags(kg, isect); - /* if no transparent shadows, all light is blocked */ - if (!(flags & SD_HAS_TRANSPARENT_SHADOW)) { - return true; - } - /* if maximum number of hits reached, block all light */ - else if (*num_hits == max_hits) { + if (!(flags & SD_HAS_TRANSPARENT_SHADOW) || max_hits == 0) { + /* If no transparent shadows, all light is blocked and we can + * stop immediately. */ return true; } - /* move on to next entry in intersections array */ - isect_array++; + /* Increase the number of hits, possibly beyond max_hits, we will + * simply not record those and only keep the max_hits closest. */ (*num_hits)++; - num_hits_in_instance++; - isect_array->t = isect_t; + if (*num_hits >= max_hits) { + /* If maximum number of hits reached, find the intersection with + * the largest distance to potentially replace when another hit + * is found. */ + const int num_recorded_hits = min(max_hits, *num_hits); + float max_recorded_t = isect_array[0].t; + int max_recorded_hit = 0; + + for (int i = 1; i < num_recorded_hits; i++) { + if (isect_array[i].t > max_recorded_t) { + max_recorded_t = isect_array[i].t; + max_recorded_hit = i; + } + } + + isect = isect_array + max_recorded_hit; + + /* Limit the ray distance and stop counting hits beyond this. */ + isect_t = max_recorded_t * t_world_to_instance; + } + else { + /* Still have space for intersection, use next hit. */ + isect = isect + 1; + } } prim_addr++; @@ -207,13 +238,14 @@ ccl_device_inline object = kernel_tex_fetch(__prim_object, -prim_addr - 1); #if BVH_FEATURE(BVH_MOTION) - isect_t = bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, isect_t, &ob_itfm); + t_world_to_instance = bvh_instance_motion_push( + kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect_t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect_t); + t_world_to_instance = bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif - num_hits_in_instance = 0; - isect_array->t = isect_t; + /* Convert intersection to object space. */ + isect_t *= t_world_to_instance; ++stack_ptr; kernel_assert(stack_ptr < BVH_STACK_SIZE); @@ -228,32 +260,19 @@ ccl_device_inline kernel_assert(object != OBJECT_NONE); /* Instance pop. */ - if (num_hits_in_instance) { - float t_fac; - #if BVH_FEATURE(BVH_MOTION) - bvh_instance_motion_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac, &ob_itfm); + bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX, &ob_itfm); #else - bvh_instance_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac); + bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX); #endif - /* scale isect->t to adjust for instancing */ - for (int i = 0; i < num_hits_in_instance; i++) { - (isect_array - i - 1)->t *= t_fac; - } - } - else { -#if BVH_FEATURE(BVH_MOTION) - bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX, &ob_itfm); -#else - bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX); -#endif - } - - isect_t = tmax; - isect_array->t = isect_t; + /* Restore world space ray length. If max number of hits exceeded this + * distance is reduced to recorded only the closest hits. If not use + * the original ray length. */ + isect_t = (max_hits && *num_hits > max_hits) ? isect->t : tmax; object = OBJECT_NONE; + t_world_to_instance = 1.0f; node_addr = traversal_stack[stack_ptr]; --stack_ptr; } @@ -262,7 +281,7 @@ ccl_device_inline return false; } -ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg, +ccl_device_inline bool BVH_FUNCTION_NAME(const KernelGlobals *kg, const Ray *ray, Intersection *isect_array, const uint visibility, diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/bvh_traversal.h index 89250a8d60a..a26d8c514f3 100644 --- a/intern/cycles/kernel/bvh/bvh_traversal.h +++ b/intern/cycles/kernel/bvh/bvh_traversal.h @@ -31,7 +31,7 @@ * BVH_MOTION: motion blur rendering */ -ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, +ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, const Ray *ray, Intersection *isect, const uint visibility) @@ -136,7 +136,8 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, case PRIMITIVE_TRIANGLE: { for (; prim_addr < prim_addr2; prim_addr++) { kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); - if (triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr)) { + if (triangle_intersect( + kg, isect, P, dir, isect->t, visibility, object, prim_addr)) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) return true; @@ -149,7 +150,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, for (; prim_addr < prim_addr2; prim_addr++) { kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); if (motion_triangle_intersect( - kg, isect, P, dir, ray->time, visibility, object, prim_addr)) { + kg, isect, P, dir, isect->t, ray->time, visibility, object, prim_addr)) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) return true; @@ -166,8 +167,16 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, for (; prim_addr < prim_addr2; prim_addr++) { const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); kernel_assert((curve_type & PRIMITIVE_ALL) == (type & PRIMITIVE_ALL)); - const bool hit = curve_intersect( - kg, isect, P, dir, visibility, object, prim_addr, ray->time, curve_type); + const bool hit = curve_intersect(kg, + isect, + P, + dir, + isect->t, + visibility, + object, + prim_addr, + ray->time, + curve_type); if (hit) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) @@ -184,10 +193,9 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, object = kernel_tex_fetch(__prim_object, -prim_addr - 1); #if BVH_FEATURE(BVH_MOTION) - isect->t = bvh_instance_motion_push( - kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm); + isect->t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect->t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect->t); + isect->t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif ++stack_ptr; @@ -218,7 +226,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, return (isect->prim != PRIM_NONE); } -ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg, +ccl_device_inline bool BVH_FUNCTION_NAME(const KernelGlobals *kg, const Ray *ray, Intersection *isect, const uint visibility) diff --git a/intern/cycles/kernel/bvh/bvh_types.h b/intern/cycles/kernel/bvh/bvh_types.h index 98e6ec25d15..6039e707fc3 100644 --- a/intern/cycles/kernel/bvh/bvh_types.h +++ b/intern/cycles/kernel/bvh/bvh_types.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __BVH_TYPES__ -#define __BVH_TYPES__ +#pragma once CCL_NAMESPACE_BEGIN @@ -43,5 +42,3 @@ CCL_NAMESPACE_BEGIN #define BVH_FEATURE(f) (((BVH_FUNCTION_FEATURES) & (f)) != 0) CCL_NAMESPACE_END - -#endif /* __BVH_TYPES__ */ diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index b1faebce957..21384457b16 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -71,86 +71,6 @@ ccl_device_inline float3 ray_offset(float3 P, float3 Ng) #endif } -/* This function should be used to compute a modified ray start position for - * rays leaving from a surface. The algorithm slightly distorts flat surface - * of a triangle. Surface is lifted by amount h along normal n in the incident - * point. */ - -ccl_device_inline float3 smooth_surface_offset(KernelGlobals *kg, ShaderData *sd, float3 Ng) -{ - float3 V[3], N[3]; - triangle_vertices_and_normals(kg, sd->prim, V, N); - - const float u = sd->u, v = sd->v; - const float w = 1 - u - v; - float3 P = V[0] * u + V[1] * v + V[2] * w; /* Local space */ - float3 n = N[0] * u + N[1] * v + N[2] * w; /* We get away without normalization */ - - object_normal_transform(kg, sd, &n); /* Normal x scale, world space */ - - /* Parabolic approximation */ - float a = dot(N[2] - N[0], V[0] - V[2]); - float b = dot(N[2] - N[1], V[1] - V[2]); - float c = dot(N[1] - N[0], V[1] - V[0]); - float h = a * u * (u - 1) + (a + b + c) * u * v + b * v * (v - 1); - - /* Check flipped normals */ - if (dot(n, Ng) > 0) { - /* Local linear envelope */ - float h0 = max(max(dot(V[1] - V[0], N[0]), dot(V[2] - V[0], N[0])), 0.0f); - float h1 = max(max(dot(V[0] - V[1], N[1]), dot(V[2] - V[1], N[1])), 0.0f); - float h2 = max(max(dot(V[0] - V[2], N[2]), dot(V[1] - V[2], N[2])), 0.0f); - h0 = max(dot(V[0] - P, N[0]) + h0, 0.0f); - h1 = max(dot(V[1] - P, N[1]) + h1, 0.0f); - h2 = max(dot(V[2] - P, N[2]) + h2, 0.0f); - h = max(min(min(h0, h1), h2), h * 0.5f); - } - else { - float h0 = max(max(dot(V[0] - V[1], N[0]), dot(V[0] - V[2], N[0])), 0.0f); - float h1 = max(max(dot(V[1] - V[0], N[1]), dot(V[1] - V[2], N[1])), 0.0f); - float h2 = max(max(dot(V[2] - V[0], N[2]), dot(V[2] - V[1], N[2])), 0.0f); - h0 = max(dot(P - V[0], N[0]) + h0, 0.0f); - h1 = max(dot(P - V[1], N[1]) + h1, 0.0f); - h2 = max(dot(P - V[2], N[2]) + h2, 0.0f); - h = min(-min(min(h0, h1), h2), h * 0.5f); - } - - return n * h; -} - -/* Ray offset to avoid shadow terminator artifact. */ - -ccl_device_inline float3 ray_offset_shadow(KernelGlobals *kg, ShaderData *sd, float3 L) -{ - float NL = dot(sd->N, L); - bool transmit = (NL < 0.0f); - float3 Ng = (transmit ? -sd->Ng : sd->Ng); - float3 P = ray_offset(sd->P, Ng); - - if ((sd->type & PRIMITIVE_ALL_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) { - const float offset_cutoff = - kernel_tex_fetch(__objects, sd->object).shadow_terminator_geometry_offset; - /* Do ray offset (heavy stuff) only for close to be terminated triangles: - * offset_cutoff = 0.1f means that 10-20% of rays will be affected. Also - * make a smooth transition near the threshold. */ - if (offset_cutoff > 0.0f) { - float NgL = dot(Ng, L); - float offset_amount = 0.0f; - if (NL < offset_cutoff) { - offset_amount = clamp(2.0f - (NgL + NL) / offset_cutoff, 0.0f, 1.0f); - } - else { - offset_amount = clamp(1.0f - NgL / offset_cutoff, 0.0f, 1.0f); - } - if (offset_amount > 0.0f) { - P += smooth_surface_offset(kg, sd, Ng) * offset_amount; - } - } - } - - return P; -} - #if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__)) /* ToDo: Move to another file? */ ccl_device int intersections_compare(const void *a, const void *b) @@ -193,10 +113,10 @@ ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits) } #endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */ -/* Utility to quickly get a shader flags from an intersection. */ +/* Utility to quickly get flags from an intersection. */ -ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals *ccl_restrict kg, - const Intersection *isect) +ccl_device_forceinline int intersection_get_shader_flags(const KernelGlobals *ccl_restrict kg, + const Intersection *ccl_restrict isect) { const int prim = kernel_tex_fetch(__prim_index, isect->prim); int shader = 0; @@ -217,14 +137,14 @@ ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals *ccl_rest return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; } -ccl_device_forceinline int intersection_get_shader(KernelGlobals *ccl_restrict kg, - const Intersection *isect) +ccl_device_forceinline int intersection_get_shader_from_isect_prim( + const KernelGlobals *ccl_restrict kg, const int isect_prim) { - const int prim = kernel_tex_fetch(__prim_index, isect->prim); + const int prim = kernel_tex_fetch(__prim_index, isect_prim); int shader = 0; #ifdef __HAIR__ - if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) + if (kernel_tex_fetch(__prim_type, isect_prim) & PRIMITIVE_ALL_TRIANGLE) #endif { shader = kernel_tex_fetch(__tri_shader, prim); @@ -239,7 +159,13 @@ ccl_device_forceinline int intersection_get_shader(KernelGlobals *ccl_restrict k return shader & SHADER_MASK; } -ccl_device_forceinline int intersection_get_object(KernelGlobals *ccl_restrict kg, +ccl_device_forceinline int intersection_get_shader(const KernelGlobals *ccl_restrict kg, + const Intersection *ccl_restrict isect) +{ + return intersection_get_shader_from_isect_prim(kg, isect->prim); +} + +ccl_device_forceinline int intersection_get_object(const KernelGlobals *ccl_restrict kg, const Intersection *ccl_restrict isect) { if (isect->object != OBJECT_NONE) { @@ -249,4 +175,12 @@ ccl_device_forceinline int intersection_get_object(KernelGlobals *ccl_restrict k return kernel_tex_fetch(__prim_object, isect->prim); } +ccl_device_forceinline int intersection_get_object_flags(const KernelGlobals *ccl_restrict kg, + const Intersection *ccl_restrict isect) +{ + const int object = intersection_get_object(kg, isect); + + return kernel_tex_fetch(__object_flag, object); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/bvh/bvh_volume.h b/intern/cycles/kernel/bvh/bvh_volume.h index 1f2ea47269b..0411d9c522d 100644 --- a/intern/cycles/kernel/bvh/bvh_volume.h +++ b/intern/cycles/kernel/bvh/bvh_volume.h @@ -35,7 +35,7 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, + bool BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, const Ray *ray, Intersection *isect, const uint visibility) @@ -147,7 +147,7 @@ ccl_device_inline if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { continue; } - triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr); + triangle_intersect(kg, isect, P, dir, isect->t, visibility, object, prim_addr); } break; } @@ -165,7 +165,7 @@ ccl_device_inline continue; } motion_triangle_intersect( - kg, isect, P, dir, ray->time, visibility, object, prim_addr); + kg, isect, P, dir, isect->t, ray->time, visibility, object, prim_addr); } break; } @@ -181,10 +181,9 @@ ccl_device_inline int object_flag = kernel_tex_fetch(__object_flag, object); if (object_flag & SD_OBJECT_HAS_VOLUME) { #if BVH_FEATURE(BVH_MOTION) - isect->t = bvh_instance_motion_push( - kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm); + isect->t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect->t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect->t); + isect->t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif ++stack_ptr; @@ -222,7 +221,7 @@ ccl_device_inline return (isect->prim != PRIM_NONE); } -ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg, +ccl_device_inline bool BVH_FUNCTION_NAME(const KernelGlobals *kg, const Ray *ray, Intersection *isect, const uint visibility) diff --git a/intern/cycles/kernel/bvh/bvh_volume_all.h b/intern/cycles/kernel/bvh/bvh_volume_all.h index a8664cc4331..4874270f15d 100644 --- a/intern/cycles/kernel/bvh/bvh_volume_all.h +++ b/intern/cycles/kernel/bvh/bvh_volume_all.h @@ -35,7 +35,7 @@ ccl_device #else ccl_device_inline #endif - uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, + uint BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, const Ray *ray, Intersection *isect_array, const uint max_hits, @@ -150,7 +150,8 @@ ccl_device_inline if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { continue; } - hit = triangle_intersect(kg, isect_array, P, dir, visibility, object, prim_addr); + hit = triangle_intersect( + kg, isect_array, P, dir, isect_t, visibility, object, prim_addr); if (hit) { /* Move on to next entry in intersections array. */ isect_array++; @@ -190,7 +191,7 @@ ccl_device_inline continue; } hit = motion_triangle_intersect( - kg, isect_array, P, dir, ray->time, visibility, object, prim_addr); + kg, isect_array, P, dir, isect_t, ray->time, visibility, object, prim_addr); if (hit) { /* Move on to next entry in intersections array. */ isect_array++; @@ -228,10 +229,9 @@ ccl_device_inline int object_flag = kernel_tex_fetch(__object_flag, object); if (object_flag & SD_OBJECT_HAS_VOLUME) { #if BVH_FEATURE(BVH_MOTION) - isect_t = bvh_instance_motion_push( - kg, object, ray, &P, &dir, &idir, isect_t, &ob_itfm); + isect_t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect_t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect_t); + isect_t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif num_hits_in_instance = 0; @@ -289,7 +289,7 @@ ccl_device_inline return num_hits; } -ccl_device_inline uint BVH_FUNCTION_NAME(KernelGlobals *kg, +ccl_device_inline uint BVH_FUNCTION_NAME(const KernelGlobals *kg, const Ray *ray, Intersection *isect_array, const uint max_hits, diff --git a/intern/cycles/kernel/closure/alloc.h b/intern/cycles/kernel/closure/alloc.h index 99a5a675976..72a8c2ba090 100644 --- a/intern/cycles/kernel/closure/alloc.h +++ b/intern/cycles/kernel/closure/alloc.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device ShaderClosure *closure_alloc(ShaderData *sd, int size, ClosureType type, float3 weight) diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index 6f2f2ebb202..4eb8bcae997 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + // clang-format off #include "kernel/closure/bsdf_ashikhmin_velvet.h" #include "kernel/closure/bsdf_diffuse.h" @@ -109,7 +111,7 @@ ccl_device_inline float shift_cos_in(float cos_in, const float frequency_multipl return val; } -ccl_device_inline int bsdf_sample(KernelGlobals *kg, +ccl_device_inline int bsdf_sample(const KernelGlobals *kg, ShaderData *sd, const ShaderClosure *sc, float randu, @@ -428,21 +430,6 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg, pdf); break; # endif /* __PRINCIPLED__ */ -#endif -#ifdef __VOLUME__ - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: - label = volume_henyey_greenstein_sample(sc, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); - break; #endif default: label = LABEL_NONE; @@ -482,15 +469,16 @@ ccl_device ccl_device_inline #endif float3 - bsdf_eval(KernelGlobals *kg, + bsdf_eval(const KernelGlobals *kg, ShaderData *sd, const ShaderClosure *sc, const float3 omega_in, + const bool is_transmission, float *pdf) { - float3 eval; + float3 eval = zero_float3(); - if (dot(sd->N, omega_in) >= 0.0f) { + if (!is_transmission) { switch (sc->type) { case CLOSURE_BSDF_DIFFUSE_ID: case CLOSURE_BSDF_BSSRDF_ID: @@ -569,14 +557,8 @@ ccl_device_inline eval = bsdf_principled_sheen_eval_reflect(sc, sd->I, omega_in, pdf); break; # endif /* __PRINCIPLED__ */ -#endif -#ifdef __VOLUME__ - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: - eval = volume_henyey_greenstein_eval_phase(sc, sd->I, omega_in, pdf); - break; #endif default: - eval = make_float3(0.0f, 0.0f, 0.0f); break; } if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { @@ -662,14 +644,8 @@ ccl_device_inline eval = bsdf_principled_sheen_eval_transmit(sc, sd->I, omega_in, pdf); break; # endif /* __PRINCIPLED__ */ -#endif -#ifdef __VOLUME__ - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: - eval = volume_henyey_greenstein_eval_phase(sc, sd->I, omega_in, pdf); - break; #endif default: - eval = make_float3(0.0f, 0.0f, 0.0f); break; } if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { @@ -682,7 +658,7 @@ ccl_device_inline return eval; } -ccl_device void bsdf_blur(KernelGlobals *kg, ShaderClosure *sc, float roughness) +ccl_device void bsdf_blur(const KernelGlobals *kg, ShaderClosure *sc, float roughness) { /* ToDo: do we want to blur volume closures? */ #ifdef __SVM__ @@ -715,55 +691,4 @@ ccl_device void bsdf_blur(KernelGlobals *kg, ShaderClosure *sc, float roughness) #endif } -ccl_device bool bsdf_merge(ShaderClosure *a, ShaderClosure *b) -{ -#ifdef __SVM__ - switch (a->type) { - case CLOSURE_BSDF_TRANSPARENT_ID: - return true; - case CLOSURE_BSDF_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_ID: - case CLOSURE_BSDF_TRANSLUCENT_ID: - return bsdf_diffuse_merge(a, b); - case CLOSURE_BSDF_OREN_NAYAR_ID: - return bsdf_oren_nayar_merge(a, b); - case CLOSURE_BSDF_REFLECTION_ID: - case CLOSURE_BSDF_REFRACTION_ID: - case CLOSURE_BSDF_MICROFACET_GGX_ID: - case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID: - case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID: - case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: - case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: - case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID: - case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: - case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID: - case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: - case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID: - case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: - return bsdf_microfacet_merge(a, b); - case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID: - return bsdf_ashikhmin_velvet_merge(a, b); - case CLOSURE_BSDF_DIFFUSE_TOON_ID: - case CLOSURE_BSDF_GLOSSY_TOON_ID: - return bsdf_toon_merge(a, b); - case CLOSURE_BSDF_HAIR_REFLECTION_ID: - case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: - return bsdf_hair_merge(a, b); -# ifdef __PRINCIPLED__ - case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: - return bsdf_principled_diffuse_merge(a, b); -# endif -# ifdef __VOLUME__ - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: - return volume_henyey_greenstein_merge(a, b); -# endif - default: - return false; - } -#else - return false; -#endif -} - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h index 9814a7cf5c9..be6383e521a 100644 --- a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h +++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h @@ -14,20 +14,19 @@ * limitations under the License. */ -#ifndef __BSDF_ASHIKHMIN_SHIRLEY_H__ -#define __BSDF_ASHIKHMIN_SHIRLEY_H__ - /* -ASHIKHMIN SHIRLEY BSDF + * ASHIKHMIN SHIRLEY BSDF + * + * Implementation of + * Michael Ashikhmin and Peter Shirley: "An Anisotropic Phong BRDF Model" (2000) + * + * The Fresnel factor is missing to get a separable bsdf (intensity*color), as is + * the case with all other microfacet-based BSDF implementations in Cycles. + * + * Other than that, the implementation directly follows the paper. + */ -Implementation of -Michael Ashikhmin and Peter Shirley: "An Anisotropic Phong BRDF Model" (2000) - -The Fresnel factor is missing to get a separable bsdf (intensity*color), as is -the case with all other microfacet-based BSDF implementations in Cycles. - -Other than that, the implementation directly follows the paper. -*/ +#pragma once CCL_NAMESPACE_BEGIN @@ -240,5 +239,3 @@ ccl_device int bsdf_ashikhmin_shirley_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_ASHIKHMIN_SHIRLEY_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h index 3d3f20edab3..f51027f5701 100644 --- a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h +++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h @@ -30,8 +30,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_ASHIKHMIN_VELVET_H__ -#define __BSDF_ASHIKHMIN_VELVET_H__ +#pragma once + +#include "kernel/kernel_montecarlo.h" CCL_NAMESPACE_BEGIN @@ -54,14 +55,6 @@ ccl_device int bsdf_ashikhmin_velvet_setup(VelvetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_ashikhmin_velvet_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const VelvetBsdf *bsdf_a = (const VelvetBsdf *)a; - const VelvetBsdf *bsdf_b = (const VelvetBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N)) && (bsdf_a->sigma == bsdf_b->sigma); -} - ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, @@ -175,5 +168,3 @@ ccl_device int bsdf_ashikhmin_velvet_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_ASHIKHMIN_VELVET_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_diffuse.h b/intern/cycles/kernel/closure/bsdf_diffuse.h index ea604ed0311..1555aa30304 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_DIFFUSE_H__ -#define __BSDF_DIFFUSE_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -49,14 +48,6 @@ ccl_device int bsdf_diffuse_setup(DiffuseBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_diffuse_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const DiffuseBsdf *bsdf_a = (const DiffuseBsdf *)a; - const DiffuseBsdf *bsdf_b = (const DiffuseBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N)); -} - ccl_device float3 bsdf_diffuse_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, @@ -174,5 +165,3 @@ ccl_device int bsdf_translucent_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_DIFFUSE_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h index aa62c1c7ceb..b06dd196b9e 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_DIFFUSE_RAMP_H__ -#define __BSDF_DIFFUSE_RAMP_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -125,5 +124,3 @@ ccl_device int bsdf_diffuse_ramp_sample(const ShaderClosure *sc, #endif /* __OSL__ */ CCL_NAMESPACE_END - -#endif /* __BSDF_DIFFUSE_RAMP_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_hair.h b/intern/cycles/kernel/closure/bsdf_hair.h index 7ca9424b815..f56f78aa1f0 100644 --- a/intern/cycles/kernel/closure/bsdf_hair.h +++ b/intern/cycles/kernel/closure/bsdf_hair.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_HAIR_H__ -#define __BSDF_HAIR_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -62,15 +61,6 @@ ccl_device int bsdf_hair_transmission_setup(HairBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_hair_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const HairBsdf *bsdf_a = (const HairBsdf *)a; - const HairBsdf *bsdf_b = (const HairBsdf *)b; - - return (isequal_float3(bsdf_a->T, bsdf_b->T)) && (bsdf_a->roughness1 == bsdf_b->roughness1) && - (bsdf_a->roughness2 == bsdf_b->roughness2) && (bsdf_a->offset == bsdf_b->offset); -} - ccl_device float3 bsdf_hair_reflection_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, @@ -309,5 +299,3 @@ ccl_device int bsdf_hair_transmission_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_HAIR_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_hair_principled.h b/intern/cycles/kernel/closure/bsdf_hair_principled.h index f12661b3095..bfe56e5ab0e 100644 --- a/intern/cycles/kernel/closure/bsdf_hair_principled.h +++ b/intern/cycles/kernel/closure/bsdf_hair_principled.h @@ -14,15 +14,14 @@ * limitations under the License. */ +#pragma once + #ifdef __KERNEL_CPU__ # include #endif #include "kernel/kernel_color.h" -#ifndef __BSDF_HAIR_PRINCIPLED_H__ -# define __BSDF_HAIR_PRINCIPLED_H__ - CCL_NAMESPACE_BEGIN typedef ccl_addr_space struct PrincipledHairExtra { @@ -181,12 +180,12 @@ ccl_device_inline float longitudinal_scattering( } /* Combine the three values using their luminances. */ -ccl_device_inline float4 combine_with_energy(KernelGlobals *kg, float3 c) +ccl_device_inline float4 combine_with_energy(const KernelGlobals *kg, float3 c) { return make_float4(c.x, c.y, c.z, linear_rgb_to_gray(kg, c)); } -# ifdef __HAIR__ +#ifdef __HAIR__ /* Set up the hair closure. */ ccl_device int bsdf_principled_hair_setup(ShaderData *sd, PrincipledHairBSDF *bsdf) { @@ -226,10 +225,10 @@ ccl_device int bsdf_principled_hair_setup(ShaderData *sd, PrincipledHairBSDF *bs return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; } -# endif /* __HAIR__ */ +#endif /* __HAIR__ */ /* Given the Fresnel term and transmittance, generate the attenuation terms for each bounce. */ -ccl_device_inline void hair_attenuation(KernelGlobals *kg, float f, float3 T, float4 *Ap) +ccl_device_inline void hair_attenuation(const KernelGlobals *kg, float f, float3 T, float4 *Ap) { /* Primary specular (R). */ Ap[0] = make_float4(f, f, f, f); @@ -278,7 +277,7 @@ ccl_device_inline void hair_alpha_angles(float sin_theta_i, } /* Evaluation function for our shader. */ -ccl_device float3 bsdf_principled_hair_eval(KernelGlobals *kg, +ccl_device float3 bsdf_principled_hair_eval(const KernelGlobals *kg, const ShaderData *sd, const ShaderClosure *sc, const float3 omega_in, @@ -356,7 +355,7 @@ ccl_device float3 bsdf_principled_hair_eval(KernelGlobals *kg, } /* Sampling function for the hair shader. */ -ccl_device int bsdf_principled_hair_sample(KernelGlobals *kg, +ccl_device int bsdf_principled_hair_sample(const KernelGlobals *kg, const ShaderClosure *sc, ShaderData *sd, float randu, @@ -473,11 +472,11 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals *kg, *omega_in = X * sin_theta_i + Y * cos_theta_i * cosf(phi_i) + Z * cos_theta_i * sinf(phi_i); -# ifdef __RAY_DIFFERENTIALS__ +#ifdef __RAY_DIFFERENTIALS__ float3 N = safe_normalize(sd->I + *omega_in); *domega_in_dx = (2 * dot(N, sd->dI.dx)) * N - sd->dI.dx; *domega_in_dy = (2 * dot(N, sd->dI.dy)) * N - sd->dI.dy; -# endif +#endif return LABEL_GLOSSY | ((p == 0) ? LABEL_REFLECT : LABEL_TRANSMIT); } @@ -501,7 +500,7 @@ ccl_device_inline float bsdf_principled_hair_albedo_roughness_scale( return (((((0.245f * x) + 5.574f) * x - 10.73f) * x + 2.532f) * x - 0.215f) * x + 5.969f; } -ccl_device float3 bsdf_principled_hair_albedo(ShaderClosure *sc) +ccl_device float3 bsdf_principled_hair_albedo(const ShaderClosure *sc) { PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)sc; return exp3(-sqrt(bsdf->sigma) * bsdf_principled_hair_albedo_roughness_scale(bsdf->v)); @@ -523,5 +522,3 @@ ccl_device_inline float3 bsdf_principled_hair_sigma_from_concentration(const flo } CCL_NAMESPACE_END - -#endif /* __BSDF_HAIR_PRINCIPLED_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index af03bab39f7..227cb448b47 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -30,8 +30,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_MICROFACET_H__ -#define __BSDF_MICROFACET_H__ +#pragma once + +#include "kernel/kernel_lookup_table.h" +#include "kernel/kernel_random.h" CCL_NAMESPACE_BEGIN @@ -53,7 +55,7 @@ static_assert(sizeof(ShaderClosure) >= sizeof(MicrofacetBsdf), "MicrofacetBsdf i /* Beckmann and GGX microfacet importance sampling. */ -ccl_device_inline void microfacet_beckmann_sample_slopes(KernelGlobals *kg, +ccl_device_inline void microfacet_beckmann_sample_slopes(const KernelGlobals *kg, const float cos_theta_i, const float sin_theta_i, float randu, @@ -193,7 +195,7 @@ ccl_device_inline void microfacet_ggx_sample_slopes(const float cos_theta_i, *slope_y = S * z * safe_sqrtf(1.0f + (*slope_x) * (*slope_x)); } -ccl_device_forceinline float3 microfacet_sample_stretched(KernelGlobals *kg, +ccl_device_forceinline float3 microfacet_sample_stretched(const KernelGlobals *kg, const float3 omega_i, const float alpha_x, const float alpha_y, @@ -352,21 +354,6 @@ ccl_device int bsdf_microfacet_ggx_clearcoat_setup(MicrofacetBsdf *bsdf, const S return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_microfacet_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const MicrofacetBsdf *bsdf_a = (const MicrofacetBsdf *)a; - const MicrofacetBsdf *bsdf_b = (const MicrofacetBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N)) && (bsdf_a->alpha_x == bsdf_b->alpha_x) && - (bsdf_a->alpha_y == bsdf_b->alpha_y) && (isequal_float3(bsdf_a->T, bsdf_b->T)) && - (bsdf_a->ior == bsdf_b->ior) && - ((bsdf_a->extra == NULL && bsdf_b->extra == NULL) || - ((bsdf_a->extra && bsdf_b->extra) && - (isequal_float3(bsdf_a->extra->color, bsdf_b->extra->color)) && - (isequal_float3(bsdf_a->extra->cspec0, bsdf_b->extra->cspec0)) && - (bsdf_a->extra->clearcoat == bsdf_b->extra->clearcoat))); -} - ccl_device int bsdf_microfacet_ggx_refraction_setup(MicrofacetBsdf *bsdf) { bsdf->extra = NULL; @@ -558,7 +545,7 @@ ccl_device float3 bsdf_microfacet_ggx_eval_transmit(const ShaderClosure *sc, return make_float3(out, out, out); } -ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, +ccl_device int bsdf_microfacet_ggx_sample(const KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, @@ -986,7 +973,7 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(const ShaderClosure *sc return make_float3(out, out, out); } -ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg, +ccl_device int bsdf_microfacet_beckmann_sample(const KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, @@ -1175,5 +1162,3 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg, } CCL_NAMESPACE_END - -#endif /* __BSDF_MICROFACET_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index 9795c8da065..68d5071dbce 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Most of the code is based on the supplemental implementations from @@ -466,7 +468,7 @@ ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(const ShaderClosure *sc bsdf->extra->cspec0); } -ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals *kg, +ccl_device int bsdf_microfacet_multi_ggx_sample(const KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, @@ -628,7 +630,7 @@ ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(const ShaderClosu bsdf->extra->cspec0); } -ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals *kg, +ccl_device int bsdf_microfacet_multi_ggx_glass_sample(const KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, diff --git a/intern/cycles/kernel/closure/bsdf_oren_nayar.h b/intern/cycles/kernel/closure/bsdf_oren_nayar.h index 41e5736bf49..be12d47f0ea 100644 --- a/intern/cycles/kernel/closure/bsdf_oren_nayar.h +++ b/intern/cycles/kernel/closure/bsdf_oren_nayar.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __BSDF_OREN_NAYAR_H__ -#define __BSDF_OREN_NAYAR_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -61,14 +60,6 @@ ccl_device int bsdf_oren_nayar_setup(OrenNayarBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_oren_nayar_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const OrenNayarBsdf *bsdf_a = (const OrenNayarBsdf *)a; - const OrenNayarBsdf *bsdf_b = (const OrenNayarBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N)) && (bsdf_a->roughness == bsdf_b->roughness); -} - ccl_device float3 bsdf_oren_nayar_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, @@ -127,5 +118,3 @@ ccl_device int bsdf_oren_nayar_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_OREN_NAYAR_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_phong_ramp.h b/intern/cycles/kernel/closure/bsdf_phong_ramp.h index cf5484383f2..43f8cf71c59 100644 --- a/intern/cycles/kernel/closure/bsdf_phong_ramp.h +++ b/intern/cycles/kernel/closure/bsdf_phong_ramp.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_PHONG_RAMP_H__ -#define __BSDF_PHONG_RAMP_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -153,5 +152,3 @@ ccl_device int bsdf_phong_ramp_sample(const ShaderClosure *sc, #endif /* __OSL__ */ CCL_NAMESPACE_END - -#endif /* __BSDF_PHONG_RAMP_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index d5d012068ff..a72af519482 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -14,14 +14,15 @@ * limitations under the License. */ -#ifndef __BSDF_PRINCIPLED_DIFFUSE_H__ -#define __BSDF_PRINCIPLED_DIFFUSE_H__ +#pragma once /* DISNEY PRINCIPLED DIFFUSE BRDF * * Shading model by Brent Burley (Disney): "Physically Based Shading at Disney" (2012) */ +#include "kernel/closure/bsdf_util.h" + CCL_NAMESPACE_BEGIN typedef ccl_addr_space struct PrincipledDiffuseBsdf { @@ -61,14 +62,6 @@ ccl_device int bsdf_principled_diffuse_setup(PrincipledDiffuseBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_principled_diffuse_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const PrincipledDiffuseBsdf *bsdf_a = (const PrincipledDiffuseBsdf *)a; - const PrincipledDiffuseBsdf *bsdf_b = (const PrincipledDiffuseBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N) && bsdf_a->roughness == bsdf_b->roughness); -} - ccl_device float3 bsdf_principled_diffuse_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, @@ -136,5 +129,3 @@ ccl_device int bsdf_principled_diffuse_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_PRINCIPLED_DIFFUSE_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_principled_sheen.h b/intern/cycles/kernel/closure/bsdf_principled_sheen.h index 3707de29d73..60ce7e4eb75 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_sheen.h +++ b/intern/cycles/kernel/closure/bsdf_principled_sheen.h @@ -14,14 +14,15 @@ * limitations under the License. */ -#ifndef __BSDF_PRINCIPLED_SHEEN_H__ -#define __BSDF_PRINCIPLED_SHEEN_H__ +#pragma once /* DISNEY PRINCIPLED SHEEN BRDF * * Shading model by Brent Burley (Disney): "Physically Based Shading at Disney" (2012) */ +#include "kernel/closure/bsdf_util.h" + CCL_NAMESPACE_BEGIN typedef ccl_addr_space struct PrincipledSheenBsdf { @@ -137,5 +138,3 @@ ccl_device int bsdf_principled_sheen_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_PRINCIPLED_SHEEN_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_reflection.h b/intern/cycles/kernel/closure/bsdf_reflection.h index c24ba170915..31283971d5a 100644 --- a/intern/cycles/kernel/closure/bsdf_reflection.h +++ b/intern/cycles/kernel/closure/bsdf_reflection.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_REFLECTION_H__ -#define __BSDF_REFLECTION_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -93,5 +92,3 @@ ccl_device int bsdf_reflection_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_REFLECTION_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_refraction.h b/intern/cycles/kernel/closure/bsdf_refraction.h index d4fbe86dac0..cfedb5dfe2c 100644 --- a/intern/cycles/kernel/closure/bsdf_refraction.h +++ b/intern/cycles/kernel/closure/bsdf_refraction.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_REFRACTION_H__ -#define __BSDF_REFRACTION_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -111,5 +110,3 @@ ccl_device int bsdf_refraction_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_REFRACTION_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_toon.h b/intern/cycles/kernel/closure/bsdf_toon.h index cc5de21ed0e..acdafe0f735 100644 --- a/intern/cycles/kernel/closure/bsdf_toon.h +++ b/intern/cycles/kernel/closure/bsdf_toon.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_TOON_H__ -#define __BSDF_TOON_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -55,15 +54,6 @@ ccl_device int bsdf_diffuse_toon_setup(ToonBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_toon_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const ToonBsdf *bsdf_a = (const ToonBsdf *)a; - const ToonBsdf *bsdf_b = (const ToonBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N)) && (bsdf_a->size == bsdf_b->size) && - (bsdf_a->smooth == bsdf_b->smooth); -} - ccl_device float3 bsdf_toon_get_intensity(float max_angle, float smooth, float angle) { float is; @@ -248,5 +238,3 @@ ccl_device int bsdf_glossy_toon_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_TOON_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_transparent.h b/intern/cycles/kernel/closure/bsdf_transparent.h index 4e5513499e8..f1dc7efb345 100644 --- a/intern/cycles/kernel/closure/bsdf_transparent.h +++ b/intern/cycles/kernel/closure/bsdf_transparent.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_TRANSPARENT_H__ -#define __BSDF_TRANSPARENT_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -123,5 +122,3 @@ ccl_device int bsdf_transparent_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_TRANSPARENT_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_util.h b/intern/cycles/kernel/closure/bsdf_util.h index a73dee1b045..beec5f768a1 100644 --- a/intern/cycles/kernel/closure/bsdf_util.h +++ b/intern/cycles/kernel/closure/bsdf_util.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_UTIL_H__ -#define __BSDF_UTIL_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -150,5 +149,3 @@ interpolate_fresnel_color(float3 L, float3 H, float ior, float F0, float3 cspec0 } CCL_NAMESPACE_END - -#endif /* __BSDF_UTIL_H__ */ diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index 562daf1286d..0f9278bba89 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __KERNEL_BSSRDF_H__ -#define __KERNEL_BSSRDF_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -24,310 +23,71 @@ typedef ccl_addr_space struct Bssrdf { float3 radius; float3 albedo; - float sharpness; - float texture_blur; float roughness; - float channels; + float anisotropy; } Bssrdf; static_assert(sizeof(ShaderClosure) >= sizeof(Bssrdf), "Bssrdf is too large!"); -/* Planar Truncated Gaussian - * - * Note how this is different from the typical gaussian, this one integrates - * to 1 over the plane (where you get an extra 2*pi*x factor). We are lucky - * that integrating x*exp(-x) gives a nice closed form solution. */ - -/* paper suggests 1/12.46 which is much too small, suspect it's *12.46 */ -#define GAUSS_TRUNCATE 12.46f - -ccl_device float bssrdf_gaussian_eval(const float radius, float r) +ccl_device float bssrdf_dipole_compute_Rd(float alpha_prime, float fourthirdA) { - /* integrate (2*pi*r * exp(-r*r/(2*v)))/(2*pi*v)) from 0 to Rm - * = 1 - exp(-Rm*Rm/(2*v)) */ - const float v = radius * radius * (0.25f * 0.25f); - const float Rm = sqrtf(v * GAUSS_TRUNCATE); + float s = sqrtf(3.0f * (1.0f - alpha_prime)); + return 0.5f * alpha_prime * (1.0f + expf(-fourthirdA * s)) * expf(-s); +} - if (r >= Rm) +ccl_device float bssrdf_dipole_compute_alpha_prime(float rd, float fourthirdA) +{ + /* Little Newton solver. */ + if (rd < 1e-4f) { return 0.0f; - - return expf(-r * r / (2.0f * v)) / (2.0f * M_PI_F * v); -} - -ccl_device float bssrdf_gaussian_pdf(const float radius, float r) -{ - /* 1.0 - expf(-Rm*Rm/(2*v)) simplified */ - const float area_truncated = 1.0f - expf(-0.5f * GAUSS_TRUNCATE); - - return bssrdf_gaussian_eval(radius, r) * (1.0f / (area_truncated)); -} - -ccl_device void bssrdf_gaussian_sample(const float radius, float xi, float *r, float *h) -{ - /* xi = integrate (2*pi*r * exp(-r*r/(2*v)))/(2*pi*v)) = -exp(-r^2/(2*v)) - * r = sqrt(-2*v*logf(xi)) */ - const float v = radius * radius * (0.25f * 0.25f); - const float Rm = sqrtf(v * GAUSS_TRUNCATE); - - /* 1.0 - expf(-Rm*Rm/(2*v)) simplified */ - const float area_truncated = 1.0f - expf(-0.5f * GAUSS_TRUNCATE); - - /* r(xi) */ - const float r_squared = -2.0f * v * logf(1.0f - xi * area_truncated); - *r = sqrtf(r_squared); - - /* h^2 + r^2 = Rm^2 */ - *h = safe_sqrtf(Rm * Rm - r_squared); -} - -/* Planar Cubic BSSRDF falloff - * - * This is basically (Rm - x)^3, with some factors to normalize it. For sampling - * we integrate 2*pi*x * (Rm - x)^3, which gives us a quintic equation that as - * far as I can tell has no closed form solution. So we get an iterative solution - * instead with newton-raphson. */ - -ccl_device float bssrdf_cubic_eval(const float radius, const float sharpness, float r) -{ - if (sharpness == 0.0f) { - const float Rm = radius; - - if (r >= Rm) - return 0.0f; - - /* integrate (2*pi*r * 10*(R - r)^3)/(pi * R^5) from 0 to R = 1 */ - const float Rm5 = (Rm * Rm) * (Rm * Rm) * Rm; - const float f = Rm - r; - const float num = f * f * f; - - return (10.0f * num) / (Rm5 * M_PI_F); } - else { - float Rm = radius * (1.0f + sharpness); + if (rd >= 0.995f) { + return 0.999999f; + } - if (r >= Rm) - return 0.0f; + float x0 = 0.0f; + float x1 = 1.0f; + float xmid, fmid; - /* custom variation with extra sharpness, to match the previous code */ - const float y = 1.0f / (1.0f + sharpness); - float Rmy, ry, ryinv; - - if (sharpness == 1.0f) { - Rmy = sqrtf(Rm); - ry = sqrtf(r); - ryinv = (ry > 0.0f) ? 1.0f / ry : 0.0f; + constexpr const int max_num_iterations = 12; + for (int i = 0; i < max_num_iterations; ++i) { + xmid = 0.5f * (x0 + x1); + fmid = bssrdf_dipole_compute_Rd(xmid, fourthirdA); + if (fmid < rd) { + x0 = xmid; } else { - Rmy = powf(Rm, y); - ry = powf(r, y); - ryinv = (r > 0.0f) ? powf(r, y - 1.0f) : 0.0f; + x1 = xmid; } - - const float Rmy5 = (Rmy * Rmy) * (Rmy * Rmy) * Rmy; - const float f = Rmy - ry; - const float num = f * (f * f) * (y * ryinv); - - return (10.0f * num) / (Rmy5 * M_PI_F); - } -} - -ccl_device float bssrdf_cubic_pdf(const float radius, const float sharpness, float r) -{ - return bssrdf_cubic_eval(radius, sharpness, r); -} - -/* solve 10x^2 - 20x^3 + 15x^4 - 4x^5 - xi == 0 */ -ccl_device_forceinline float bssrdf_cubic_quintic_root_find(float xi) -{ - /* newton-raphson iteration, usually succeeds in 2-4 iterations, except - * outside 0.02 ... 0.98 where it can go up to 10, so overall performance - * should not be too bad */ - const float tolerance = 1e-6f; - const int max_iteration_count = 10; - float x = 0.25f; - int i; - - for (i = 0; i < max_iteration_count; i++) { - float x2 = x * x; - float x3 = x2 * x; - float nx = (1.0f - x); - - float f = 10.0f * x2 - 20.0f * x3 + 15.0f * x2 * x2 - 4.0f * x2 * x3 - xi; - float f_ = 20.0f * (x * nx) * (nx * nx); - - if (fabsf(f) < tolerance || f_ == 0.0f) - break; - - x = saturate(x - f / f_); } - return x; + return xmid; } -ccl_device void bssrdf_cubic_sample( - const float radius, const float sharpness, float xi, float *r, float *h) +ccl_device void bssrdf_setup_radius(Bssrdf *bssrdf, const ClosureType type, const float eta) { - float Rm = radius; - float r_ = bssrdf_cubic_quintic_root_find(xi); - - if (sharpness != 0.0f) { - r_ = powf(r_, 1.0f + sharpness); - Rm *= (1.0f + sharpness); - } - - r_ *= Rm; - *r = r_; - - /* h^2 + r^2 = Rm^2 */ - *h = safe_sqrtf(Rm * Rm - r_ * r_); -} - -/* Approximate Reflectance Profiles - * http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf - */ - -/* This is a bit arbitrary, just need big enough radius so it matches - * the mean free length, but still not too big so sampling is still - * effective. Might need some further tweaks. - */ -#define BURLEY_TRUNCATE 16.0f -#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) - -ccl_device_inline float bssrdf_burley_fitting(float A) -{ - /* Diffuse surface transmission, equation (6). */ - return 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f); -} - -/* Scale mean free path length so it gives similar looking result - * to Cubic and Gaussian models. - */ -ccl_device_inline float3 bssrdf_burley_compatible_mfp(float3 r) -{ - return 0.25f * M_1_PI_F * r; -} - -ccl_device void bssrdf_burley_setup(Bssrdf *bssrdf) -{ - /* Mean free path length. */ - const float3 l = bssrdf_burley_compatible_mfp(bssrdf->radius); - /* Surface albedo. */ - const float3 A = bssrdf->albedo; - const float3 s = make_float3( - bssrdf_burley_fitting(A.x), bssrdf_burley_fitting(A.y), bssrdf_burley_fitting(A.z)); - - bssrdf->radius = l / s; -} - -ccl_device float bssrdf_burley_eval(const float d, float r) -{ - const float Rm = BURLEY_TRUNCATE * d; - - if (r >= Rm) - return 0.0f; - - /* Burley reflectance profile, equation (3). - * - * NOTES: - * - Surface albedo is already included into sc->weight, no need to - * multiply by this term here. - * - This is normalized diffuse model, so the equation is multiplied - * by 2*pi, which also matches cdf(). - */ - float exp_r_3_d = expf(-r / (3.0f * d)); - float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; - return (exp_r_d + exp_r_3_d) / (4.0f * d); -} - -ccl_device float bssrdf_burley_pdf(const float d, float r) -{ - return bssrdf_burley_eval(d, r) * (1.0f / BURLEY_TRUNCATE_CDF); -} - -/* Find the radius for desired CDF value. - * Returns scaled radius, meaning the result is to be scaled up by d. - * Since there's no closed form solution we do Newton-Raphson method to find it. - */ -ccl_device_forceinline float bssrdf_burley_root_find(float xi) -{ - const float tolerance = 1e-6f; - const int max_iteration_count = 10; - /* Do initial guess based on manual curve fitting, this allows us to reduce - * number of iterations to maximum 4 across the [0..1] range. We keep maximum - * number of iteration higher just to be sure we didn't miss root in some - * corner case. - */ - float r; - if (xi <= 0.9f) { - r = expf(xi * xi * 2.4f) - 1.0f; + if (type == CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) { + /* Scale mean free path length so it gives similar looking result to older + * Cubic, Gaussian and Burley models. */ + bssrdf->radius *= 0.25f * M_1_PI_F; } else { - /* TODO(sergey): Some nicer curve fit is possible here. */ - r = 15.0f; + /* Adjust radius based on IOR and albedo. */ + const float inv_eta = 1.0f / eta; + const float F_dr = inv_eta * (-1.440f * inv_eta + 0.710f) + 0.668f + 0.0636f * eta; + const float fourthirdA = (4.0f / 3.0f) * (1.0f + F_dr) / + (1.0f - F_dr); /* From Jensen's Fdr ratio formula. */ + + const float3 alpha_prime = make_float3( + bssrdf_dipole_compute_alpha_prime(bssrdf->albedo.x, fourthirdA), + bssrdf_dipole_compute_alpha_prime(bssrdf->albedo.y, fourthirdA), + bssrdf_dipole_compute_alpha_prime(bssrdf->albedo.z, fourthirdA)); + + bssrdf->radius *= sqrt(3.0f * (one_float3() - alpha_prime)); } - /* Solve against scaled radius. */ - for (int i = 0; i < max_iteration_count; i++) { - float exp_r_3 = expf(-r / 3.0f); - float exp_r = exp_r_3 * exp_r_3 * exp_r_3; - float f = 1.0f - 0.25f * exp_r - 0.75f * exp_r_3 - xi; - float f_ = 0.25f * exp_r + 0.25f * exp_r_3; - - if (fabsf(f) < tolerance || f_ == 0.0f) { - break; - } - - r = r - f / f_; - if (r < 0.0f) { - r = 0.0f; - } - } - return r; } -ccl_device void bssrdf_burley_sample(const float d, float xi, float *r, float *h) -{ - const float Rm = BURLEY_TRUNCATE * d; - const float r_ = bssrdf_burley_root_find(xi * BURLEY_TRUNCATE_CDF) * d; - - *r = r_; - - /* h^2 + r^2 = Rm^2 */ - *h = safe_sqrtf(Rm * Rm - r_ * r_); -} - -/* None BSSRDF falloff - * - * Samples distributed over disk with no falloff, for reference. */ - -ccl_device float bssrdf_none_eval(const float radius, float r) -{ - const float Rm = radius; - return (r < Rm) ? 1.0f : 0.0f; -} - -ccl_device float bssrdf_none_pdf(const float radius, float r) -{ - /* integrate (2*pi*r)/(pi*Rm*Rm) from 0 to Rm = 1 */ - const float Rm = radius; - const float area = (M_PI_F * Rm * Rm); - - return bssrdf_none_eval(radius, r) / area; -} - -ccl_device void bssrdf_none_sample(const float radius, float xi, float *r, float *h) -{ - /* xi = integrate (2*pi*r)/(pi*Rm*Rm) = r^2/Rm^2 - * r = sqrt(xi)*Rm */ - const float Rm = radius; - const float r_ = sqrtf(xi) * Rm; - - *r = r_; - - /* h^2 + r^2 = Rm^2 */ - *h = safe_sqrtf(Rm * Rm - r_ * r_); -} - -/* Generic */ +/* Setup */ ccl_device_inline Bssrdf *bssrdf_alloc(ShaderData *sd, float3 weight) { @@ -342,7 +102,7 @@ ccl_device_inline Bssrdf *bssrdf_alloc(ShaderData *sd, float3 weight) return (sample_weight >= CLOSURE_WEIGHT_CUTOFF) ? bssrdf : NULL; } -ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type) +ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type, const float ior) { int flag = 0; int bssrdf_channels = 3; @@ -371,7 +131,7 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type) if (bssrdf_channels < 3) { /* Add diffuse BSDF if any radius too small. */ #ifdef __PRINCIPLED__ - if (type == CLOSURE_BSSRDF_PRINCIPLED_ID || type == CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) { + if (bssrdf->roughness != FLT_MAX) { float roughness = bssrdf->roughness; float3 N = bssrdf->N; @@ -401,16 +161,9 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type) /* Setup BSSRDF if radius is large enough. */ if (bssrdf_channels > 0) { bssrdf->type = type; - bssrdf->channels = bssrdf_channels; - bssrdf->sample_weight = fabsf(average(bssrdf->weight)) * bssrdf->channels; - bssrdf->texture_blur = saturate(bssrdf->texture_blur); - bssrdf->sharpness = saturate(bssrdf->sharpness); + bssrdf->sample_weight = fabsf(average(bssrdf->weight)) * bssrdf_channels; - if (type == CLOSURE_BSSRDF_BURLEY_ID || type == CLOSURE_BSSRDF_PRINCIPLED_ID || - type == CLOSURE_BSSRDF_RANDOM_WALK_ID || - type == CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) { - bssrdf_burley_setup(bssrdf); - } + bssrdf_setup_radius(bssrdf, type, ior); flag |= SD_BSSRDF; } @@ -422,77 +175,4 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type) return flag; } -ccl_device void bssrdf_sample(const ShaderClosure *sc, float xi, float *r, float *h) -{ - const Bssrdf *bssrdf = (const Bssrdf *)sc; - float radius; - - /* Sample color channel and reuse random number. Only a subset of channels - * may be used if their radius was too small to handle as BSSRDF. */ - xi *= bssrdf->channels; - - if (xi < 1.0f) { - radius = (bssrdf->radius.x > 0.0f) ? bssrdf->radius.x : - (bssrdf->radius.y > 0.0f) ? bssrdf->radius.y : - bssrdf->radius.z; - } - else if (xi < 2.0f) { - xi -= 1.0f; - radius = (bssrdf->radius.x > 0.0f && bssrdf->radius.y > 0.0f) ? bssrdf->radius.y : - bssrdf->radius.z; - } - else { - xi -= 2.0f; - radius = bssrdf->radius.z; - } - - /* Sample BSSRDF. */ - if (bssrdf->type == CLOSURE_BSSRDF_CUBIC_ID) { - bssrdf_cubic_sample(radius, bssrdf->sharpness, xi, r, h); - } - else if (bssrdf->type == CLOSURE_BSSRDF_GAUSSIAN_ID) { - bssrdf_gaussian_sample(radius, xi, r, h); - } - else { /* if (bssrdf->type == CLOSURE_BSSRDF_BURLEY_ID || - * bssrdf->type == CLOSURE_BSSRDF_PRINCIPLED_ID) */ - bssrdf_burley_sample(radius, xi, r, h); - } -} - -ccl_device float bssrdf_channel_pdf(const Bssrdf *bssrdf, float radius, float r) -{ - if (radius == 0.0f) { - return 0.0f; - } - else if (bssrdf->type == CLOSURE_BSSRDF_CUBIC_ID) { - return bssrdf_cubic_pdf(radius, bssrdf->sharpness, r); - } - else if (bssrdf->type == CLOSURE_BSSRDF_GAUSSIAN_ID) { - return bssrdf_gaussian_pdf(radius, r); - } - else { /* if (bssrdf->type == CLOSURE_BSSRDF_BURLEY_ID || - * bssrdf->type == CLOSURE_BSSRDF_PRINCIPLED_ID)*/ - return bssrdf_burley_pdf(radius, r); - } -} - -ccl_device_forceinline float3 bssrdf_eval(const ShaderClosure *sc, float r) -{ - const Bssrdf *bssrdf = (const Bssrdf *)sc; - - return make_float3(bssrdf_channel_pdf(bssrdf, bssrdf->radius.x, r), - bssrdf_channel_pdf(bssrdf, bssrdf->radius.y, r), - bssrdf_channel_pdf(bssrdf, bssrdf->radius.z, r)); -} - -ccl_device_forceinline float bssrdf_pdf(const ShaderClosure *sc, float r) -{ - const Bssrdf *bssrdf = (const Bssrdf *)sc; - float3 pdf = bssrdf_eval(sc, r); - - return (pdf.x + pdf.y + pdf.z) / bssrdf->channels; -} - CCL_NAMESPACE_END - -#endif /* __KERNEL_BSSRDF_H__ */ diff --git a/intern/cycles/kernel/closure/emissive.h b/intern/cycles/kernel/closure/emissive.h index 911382e6865..a2519d97618 100644 --- a/intern/cycles/kernel/closure/emissive.h +++ b/intern/cycles/kernel/closure/emissive.h @@ -30,6 +30,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + CCL_NAMESPACE_BEGIN /* BACKGROUND CLOSURE */ diff --git a/intern/cycles/kernel/closure/volume.h b/intern/cycles/kernel/closure/volume.h index 1430f712701..69959a3f21b 100644 --- a/intern/cycles/kernel/closure/volume.h +++ b/intern/cycles/kernel/closure/volume.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __VOLUME_H__ -#define __VOLUME_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -62,21 +61,12 @@ ccl_device int volume_henyey_greenstein_setup(HenyeyGreensteinVolume *volume) return SD_SCATTER; } -ccl_device bool volume_henyey_greenstein_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const HenyeyGreensteinVolume *volume_a = (const HenyeyGreensteinVolume *)a; - const HenyeyGreensteinVolume *volume_b = (const HenyeyGreensteinVolume *)b; - - return (volume_a->g == volume_b->g); -} - -ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderClosure *sc, +ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderVolumeClosure *svc, const float3 I, float3 omega_in, float *pdf) { - const HenyeyGreensteinVolume *volume = (const HenyeyGreensteinVolume *)sc; - float g = volume->g; + float g = svc->g; /* note that I points towards the viewer */ if (fabsf(g) < 1e-3f) { @@ -122,7 +112,7 @@ henyey_greenstrein_sample(float3 D, float g, float randu, float randv, float *pd return dir; } -ccl_device int volume_henyey_greenstein_sample(const ShaderClosure *sc, +ccl_device int volume_henyey_greenstein_sample(const ShaderVolumeClosure *svc, float3 I, float3 dIdx, float3 dIdy, @@ -134,8 +124,7 @@ ccl_device int volume_henyey_greenstein_sample(const ShaderClosure *sc, float3 *domega_in_dy, float *pdf) { - const HenyeyGreensteinVolume *volume = (const HenyeyGreensteinVolume *)sc; - float g = volume->g; + float g = svc->g; /* note that I points towards the viewer and so is used negated */ *omega_in = henyey_greenstrein_sample(-I, g, randu, randv, pdf); @@ -153,17 +142,15 @@ ccl_device int volume_henyey_greenstein_sample(const ShaderClosure *sc, /* VOLUME CLOSURE */ ccl_device float3 volume_phase_eval(const ShaderData *sd, - const ShaderClosure *sc, + const ShaderVolumeClosure *svc, float3 omega_in, float *pdf) { - kernel_assert(sc->type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID); - - return volume_henyey_greenstein_eval_phase(sc, sd->I, omega_in, pdf); + return volume_henyey_greenstein_eval_phase(svc, sd->I, omega_in, pdf); } ccl_device int volume_phase_sample(const ShaderData *sd, - const ShaderClosure *sc, + const ShaderVolumeClosure *svc, float randu, float randv, float3 *eval, @@ -171,31 +158,65 @@ ccl_device int volume_phase_sample(const ShaderData *sd, differential3 *domega_in, float *pdf) { - int label; + return volume_henyey_greenstein_sample(svc, + sd->I, + sd->dI.dx, + sd->dI.dy, + randu, + randv, + eval, + omega_in, + &domega_in->dx, + &domega_in->dy, + pdf); +} - switch (sc->type) { - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: - label = volume_henyey_greenstein_sample(sc, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); - break; - default: - *eval = make_float3(0.0f, 0.0f, 0.0f); - label = LABEL_NONE; - break; +/* Volume sampling utilities. */ + +/* todo: this value could be tweaked or turned into a probability to avoid + * unnecessary work in volumes and subsurface scattering. */ +#define VOLUME_THROUGHPUT_EPSILON 1e-6f + +ccl_device float3 volume_color_transmittance(float3 sigma, float t) +{ + return exp3(-sigma * t); +} + +ccl_device float volume_channel_get(float3 value, int channel) +{ + return (channel == 0) ? value.x : ((channel == 1) ? value.y : value.z); +} + +ccl_device int volume_sample_channel(float3 albedo, float3 throughput, float rand, float3 *pdf) +{ + /* Sample color channel proportional to throughput and single scattering + * albedo, to significantly reduce noise with many bounce, following: + * + * "Practical and Controllable Subsurface Scattering for Production Path + * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ + float3 weights = fabs(throughput * albedo); + float sum_weights = weights.x + weights.y + weights.z; + float3 weights_pdf; + + if (sum_weights > 0.0f) { + weights_pdf = weights / sum_weights; + } + else { + weights_pdf = make_float3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f); } - return label; + *pdf = weights_pdf; + + /* OpenCL does not support -> on float3, so don't use pdf->x. */ + if (rand < weights_pdf.x) { + return 0; + } + else if (rand < weights_pdf.x + weights_pdf.y) { + return 1; + } + else { + return 2; + } } CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/device/cpu/compat.h similarity index 61% rename from intern/cycles/kernel/kernel_compat_cpu.h rename to intern/cycles/kernel/device/cpu/compat.h index 88f6a264a5a..bfd936c7bbd 100644 --- a/intern/cycles/kernel/kernel_compat_cpu.h +++ b/intern/cycles/kernel/device/cpu/compat.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __KERNEL_COMPAT_CPU_H__ -#define __KERNEL_COMPAT_CPU_H__ +#pragma once #define __KERNEL_CPU__ @@ -27,14 +26,6 @@ # pragma GCC diagnostic ignored "-Wuninitialized" #endif -/* Selective nodes compilation. */ -#ifndef __NODES_MAX_GROUP__ -# define __NODES_MAX_GROUP__ NODE_GROUP_LEVEL_MAX -#endif -#ifndef __NODES_FEATURES__ -# define __NODES_FEATURES__ NODE_FEATURE_ALL -#endif - #include "util/util_half.h" #include "util/util_math.h" #include "util/util_simd.h" @@ -43,15 +34,6 @@ #define ccl_addr_space -#define ccl_local_id(d) 0 -#define ccl_global_id(d) (kg->global_id[d]) - -#define ccl_local_size(d) 1 -#define ccl_global_size(d) (kg->global_size[d]) - -#define ccl_group_id(d) ccl_global_id(d) -#define ccl_num_groups(d) ccl_global_size(d) - /* On x86_64, versions of glibc < 2.16 have an issue where expf is * much slower than the double version. This was fixed in glibc 2.16. */ @@ -72,37 +54,11 @@ CCL_NAMESPACE_BEGIN * simple arrays and after inlining fetch hopefully revert to being a simple * pointer lookup. */ template struct texture { - ccl_always_inline const T &fetch(int index) + ccl_always_inline const T &fetch(int index) const { kernel_assert(index >= 0 && index < width); return data[index]; } -#if defined(__KERNEL_AVX__) || defined(__KERNEL_AVX2__) - /* Reads 256 bytes but indexes in blocks of 128 bytes to maintain - * compatibility with existing indices and data structures. - */ - ccl_always_inline avxf fetch_avxf(const int index) - { - kernel_assert(index >= 0 && (index + 1) < width); - ssef *ssef_data = (ssef *)data; - ssef *ssef_node_data = &ssef_data[index]; - return _mm256_loadu_ps((float *)ssef_node_data); - } -#endif - -#ifdef __KERNEL_SSE2__ - ccl_always_inline ssef fetch_ssef(int index) - { - kernel_assert(index >= 0 && index < width); - return ((ssef *)data)[index]; - } - - ccl_always_inline ssei fetch_ssei(int index) - { - kernel_assert(index >= 0 && index < width); - return ((ssei *)data)[index]; - } -#endif T *data; int width; @@ -110,15 +66,6 @@ template struct texture { /* Macros to handle different memory storage on different devices */ -#define kernel_tex_fetch(tex, index) (kg->tex.fetch(index)) -#define kernel_tex_fetch_avxf(tex, index) (kg->tex.fetch_avxf(index)) -#define kernel_tex_fetch_ssef(tex, index) (kg->tex.fetch_ssef(index)) -#define kernel_tex_fetch_ssei(tex, index) (kg->tex.fetch_ssei(index)) -#define kernel_tex_lookup(tex, t, offset, size) (kg->tex.lookup(t, offset, size)) -#define kernel_tex_array(tex) (kg->tex.data) - -#define kernel_data (kg->__data) - #ifdef __KERNEL_SSE2__ typedef vector3 sse3b; typedef vector3 sse3f; @@ -152,5 +99,3 @@ typedef vector3 avx3f; #endif CCL_NAMESPACE_END - -#endif /* __KERNEL_COMPAT_CPU_H__ */ diff --git a/intern/cycles/kernel/device/cpu/globals.h b/intern/cycles/kernel/device/cpu/globals.h new file mode 100644 index 00000000000..98b036e269d --- /dev/null +++ b/intern/cycles/kernel/device/cpu/globals.h @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Constant Globals */ + +#pragma once + +#include "kernel/kernel_profiling.h" +#include "kernel/kernel_types.h" + +CCL_NAMESPACE_BEGIN + +/* On the CPU, we pass along the struct KernelGlobals to nearly everywhere in + * the kernel, to access constant data. These are all stored as "textures", but + * these are really just standard arrays. We can't use actually globals because + * multiple renders may be running inside the same process. */ + +#ifdef __OSL__ +struct OSLGlobals; +struct OSLThreadData; +struct OSLShadingSystem; +#endif + +typedef struct KernelGlobals { +#define KERNEL_TEX(type, name) texture name; +#include "kernel/kernel_textures.h" + + KernelData __data; + +#ifdef __OSL__ + /* On the CPU, we also have the OSL globals here. Most data structures are shared + * with SVM, the difference is in the shaders and object/mesh attributes. */ + OSLGlobals *osl; + OSLShadingSystem *osl_ss; + OSLThreadData *osl_tdata; +#endif + + /* **** Run-time data **** */ + + ProfilingState profiler; +} KernelGlobals; + +/* Abstraction macros */ +#define kernel_tex_fetch(tex, index) (kg->tex.fetch(index)) +#define kernel_tex_array(tex) (kg->tex.data) +#define kernel_data (kg->__data) + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h b/intern/cycles/kernel/device/cpu/image.h similarity index 98% rename from intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h rename to intern/cycles/kernel/device/cpu/image.h index 59b96c86c50..57e81ab186d 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h +++ b/intern/cycles/kernel/device/cpu/image.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __KERNEL_CPU_IMAGE_H__ -#define __KERNEL_CPU_IMAGE_H__ +#pragma once #ifdef WITH_NANOVDB # define NANOVDB_USE_INTRINSICS @@ -584,7 +583,7 @@ template struct NanoVDBInterpolator { #undef SET_CUBIC_SPLINE_WEIGHTS -ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y) +ccl_device float4 kernel_tex_image_interp(const KernelGlobals *kg, int id, float x, float y) { const TextureInfo &info = kernel_tex_fetch(__texture_info, id); @@ -612,7 +611,7 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, fl } } -ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, +ccl_device float4 kernel_tex_image_interp_3d(const KernelGlobals *kg, int id, float3 P, InterpolationType interp) @@ -656,5 +655,3 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, } /* Namespace. */ CCL_NAMESPACE_END - -#endif // __KERNEL_CPU_IMAGE_H__ diff --git a/intern/cycles/kernel/kernels/cpu/kernel.cpp b/intern/cycles/kernel/device/cpu/kernel.cpp similarity index 96% rename from intern/cycles/kernel/kernels/cpu/kernel.cpp rename to intern/cycles/kernel/device/cpu/kernel.cpp index 8040bfb7b33..ac1cdf5fffe 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel.cpp +++ b/intern/cycles/kernel/device/cpu/kernel.cpp @@ -56,9 +56,9 @@ /* do nothing */ #endif -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/kernel.h b/intern/cycles/kernel/device/cpu/kernel.h similarity index 77% rename from intern/cycles/kernel/kernel.h rename to intern/cycles/kernel/device/cpu/kernel.h index b907c6a2bac..ae2a841835a 100644 --- a/intern/cycles/kernel/kernel.h +++ b/intern/cycles/kernel/device/cpu/kernel.h @@ -14,50 +14,49 @@ * limitations under the License. */ -#ifndef __KERNEL_H__ -#define __KERNEL_H__ +#pragma once /* CPU Kernel Interface */ -#include "kernel/kernel_types.h" #include "util/util_types.h" +#include "kernel/kernel_types.h" + CCL_NAMESPACE_BEGIN #define KERNEL_NAME_JOIN(x, y, z) x##_##y##_##z #define KERNEL_NAME_EVAL(arch, name) KERNEL_NAME_JOIN(kernel, arch, name) #define KERNEL_FUNCTION_FULL_NAME(name) KERNEL_NAME_EVAL(KERNEL_ARCH, name) +struct IntegratorStateCPU; struct KernelGlobals; struct KernelData; KernelGlobals *kernel_globals_create(); void kernel_globals_free(KernelGlobals *kg); -void *kernel_osl_memory(KernelGlobals *kg); -bool kernel_osl_use(KernelGlobals *kg); +void *kernel_osl_memory(const KernelGlobals *kg); +bool kernel_osl_use(const KernelGlobals *kg); void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t size); void kernel_global_memory_copy(KernelGlobals *kg, const char *name, void *mem, size_t size); #define KERNEL_ARCH cpu -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" #define KERNEL_ARCH cpu_sse2 -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" #define KERNEL_ARCH cpu_sse3 -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" #define KERNEL_ARCH cpu_sse41 -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" #define KERNEL_ARCH cpu_avx -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" #define KERNEL_ARCH cpu_avx2 -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" CCL_NAMESPACE_END - -#endif /* __KERNEL_H__ */ diff --git a/intern/cycles/kernel/device/cpu/kernel_arch.h b/intern/cycles/kernel/device/cpu/kernel_arch.h new file mode 100644 index 00000000000..81f328c710b --- /dev/null +++ b/intern/cycles/kernel/device/cpu/kernel_arch.h @@ -0,0 +1,113 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Templated common declaration part of all CPU kernels. */ + +/* -------------------------------------------------------------------- + * Integrator. + */ + +#define KERNEL_INTEGRATOR_FUNCTION(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobals *ccl_restrict kg, \ + IntegratorStateCPU *state) + +#define KERNEL_INTEGRATOR_SHADE_FUNCTION(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobals *ccl_restrict kg, \ + IntegratorStateCPU *state, \ + ccl_global float *render_buffer) + +#define KERNEL_INTEGRATOR_INIT_FUNCTION(name) \ + bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobals *ccl_restrict kg, \ + IntegratorStateCPU *state, \ + KernelWorkTile *tile, \ + ccl_global float *render_buffer) + +KERNEL_INTEGRATOR_INIT_FUNCTION(init_from_camera); +KERNEL_INTEGRATOR_INIT_FUNCTION(init_from_bake); +KERNEL_INTEGRATOR_FUNCTION(intersect_closest); +KERNEL_INTEGRATOR_FUNCTION(intersect_shadow); +KERNEL_INTEGRATOR_FUNCTION(intersect_subsurface); +KERNEL_INTEGRATOR_FUNCTION(intersect_volume_stack); +KERNEL_INTEGRATOR_SHADE_FUNCTION(shade_background); +KERNEL_INTEGRATOR_SHADE_FUNCTION(shade_light); +KERNEL_INTEGRATOR_SHADE_FUNCTION(shade_shadow); +KERNEL_INTEGRATOR_SHADE_FUNCTION(shade_surface); +KERNEL_INTEGRATOR_SHADE_FUNCTION(shade_volume); +KERNEL_INTEGRATOR_SHADE_FUNCTION(megakernel); + +#undef KERNEL_INTEGRATOR_FUNCTION +#undef KERNEL_INTEGRATOR_INIT_FUNCTION +#undef KERNEL_INTEGRATOR_SHADE_FUNCTION + +/* -------------------------------------------------------------------- + * Shader evaluation. + */ + +void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobals *kg, + const KernelShaderEvalInput *input, + float4 *output, + const int offset); +void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobals *kg, + const KernelShaderEvalInput *input, + float4 *output, + const int offset); + +/* -------------------------------------------------------------------- + * Adaptive sampling. + */ + +bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)( + const KernelGlobals *kg, + ccl_global float *render_buffer, + int x, + int y, + float threshold, + bool reset, + int offset, + int stride); + +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const KernelGlobals *kg, + ccl_global float *render_buffer, + int y, + int start_x, + int width, + int offset, + int stride); +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobals *kg, + ccl_global float *render_buffer, + int x, + int start_y, + int height, + int offset, + int stride); + +/* -------------------------------------------------------------------- + * Cryptomatte. + */ + +void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobals *kg, + ccl_global float *render_buffer, + int pixel_index); + +/* -------------------------------------------------------------------- + * Bake. + */ +/* TODO(sergey): Needs to be re-implemented. Or not? Brecht did it already :) */ + +void KERNEL_FUNCTION_FULL_NAME(bake)( + const KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride); + +#undef KERNEL_ARCH diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h new file mode 100644 index 00000000000..1432abfd330 --- /dev/null +++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h @@ -0,0 +1,235 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Templated common implementation part of all CPU kernels. + * + * The idea is that particular .cpp files sets needed optimization flags and + * simply includes this file without worry of copying actual implementation over. + */ + +#pragma once + +// clang-format off +#include "kernel/device/cpu/compat.h" + +#ifndef KERNEL_STUB +# include "kernel/device/cpu/globals.h" +# include "kernel/device/cpu/image.h" + +# include "kernel/integrator/integrator_state.h" +# include "kernel/integrator/integrator_state_flow.h" +# include "kernel/integrator/integrator_state_util.h" + +# include "kernel/integrator/integrator_init_from_camera.h" +# include "kernel/integrator/integrator_init_from_bake.h" +# include "kernel/integrator/integrator_intersect_closest.h" +# include "kernel/integrator/integrator_intersect_shadow.h" +# include "kernel/integrator/integrator_intersect_subsurface.h" +# include "kernel/integrator/integrator_intersect_volume_stack.h" +# include "kernel/integrator/integrator_shade_background.h" +# include "kernel/integrator/integrator_shade_light.h" +# include "kernel/integrator/integrator_shade_shadow.h" +# include "kernel/integrator/integrator_shade_surface.h" +# include "kernel/integrator/integrator_shade_volume.h" +# include "kernel/integrator/integrator_megakernel.h" + +# include "kernel/kernel_film.h" +# include "kernel/kernel_adaptive_sampling.h" +# include "kernel/kernel_bake.h" +# include "kernel/kernel_id_passes.h" + +#else +# define STUB_ASSERT(arch, name) \ + assert(!(#name " kernel stub for architecture " #arch " was called!")) +#endif /* KERNEL_STUB */ +// clang-format on + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Integrator. + */ + +#ifdef KERNEL_STUB +# define KERNEL_INVOKE(name, ...) (STUB_ASSERT(KERNEL_ARCH, name), 0) +#else +# define KERNEL_INVOKE(name, ...) integrator_##name(__VA_ARGS__) +#endif + +#define DEFINE_INTEGRATOR_KERNEL(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobals *kg, \ + IntegratorStateCPU *state) \ + { \ + KERNEL_INVOKE(name, kg, state); \ + } + +#define DEFINE_INTEGRATOR_SHADE_KERNEL(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)( \ + const KernelGlobals *kg, IntegratorStateCPU *state, ccl_global float *render_buffer) \ + { \ + KERNEL_INVOKE(name, kg, state, render_buffer); \ + } + +/* TODO: Either use something like get_work_pixel(), or simplify tile which is passed here, so + * that it does not contain unused fields. */ +#define DEFINE_INTEGRATOR_INIT_KERNEL(name) \ + bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobals *kg, \ + IntegratorStateCPU *state, \ + KernelWorkTile *tile, \ + ccl_global float *render_buffer) \ + { \ + return KERNEL_INVOKE( \ + name, kg, state, tile, render_buffer, tile->x, tile->y, tile->start_sample); \ + } + +DEFINE_INTEGRATOR_INIT_KERNEL(init_from_camera) +DEFINE_INTEGRATOR_INIT_KERNEL(init_from_bake) +DEFINE_INTEGRATOR_KERNEL(intersect_closest) +DEFINE_INTEGRATOR_KERNEL(intersect_shadow) +DEFINE_INTEGRATOR_KERNEL(intersect_subsurface) +DEFINE_INTEGRATOR_KERNEL(intersect_volume_stack) +DEFINE_INTEGRATOR_SHADE_KERNEL(shade_background) +DEFINE_INTEGRATOR_SHADE_KERNEL(shade_light) +DEFINE_INTEGRATOR_SHADE_KERNEL(shade_shadow) +DEFINE_INTEGRATOR_SHADE_KERNEL(shade_surface) +DEFINE_INTEGRATOR_SHADE_KERNEL(shade_volume) +DEFINE_INTEGRATOR_SHADE_KERNEL(megakernel) + +/* -------------------------------------------------------------------- + * Shader evaluation. + */ + +void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobals *kg, + const KernelShaderEvalInput *input, + float4 *output, + const int offset) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, shader_eval_displace); +#else + kernel_displace_evaluate(kg, input, output, offset); +#endif +} + +void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobals *kg, + const KernelShaderEvalInput *input, + float4 *output, + const int offset) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, shader_eval_background); +#else + kernel_background_evaluate(kg, input, output, offset); +#endif +} + +/* -------------------------------------------------------------------- + * Adaptive sampling. + */ + +bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)( + const KernelGlobals *kg, + ccl_global float *render_buffer, + int x, + int y, + float threshold, + bool reset, + int offset, + int stride) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_convergence_check); + return false; +#else + return kernel_adaptive_sampling_convergence_check( + kg, render_buffer, x, y, threshold, reset, offset, stride); +#endif +} + +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const KernelGlobals *kg, + ccl_global float *render_buffer, + int y, + int start_x, + int width, + int offset, + int stride) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_filter_x); +#else + kernel_adaptive_sampling_filter_x(kg, render_buffer, y, start_x, width, offset, stride); +#endif +} + +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobals *kg, + ccl_global float *render_buffer, + int x, + int start_y, + int height, + int offset, + int stride) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_filter_y); +#else + kernel_adaptive_sampling_filter_y(kg, render_buffer, x, start_y, height, offset, stride); +#endif +} + +/* -------------------------------------------------------------------- + * Cryptomatte. + */ + +void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobals *kg, + ccl_global float *render_buffer, + int pixel_index) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, cryptomatte_postprocess); +#else + kernel_cryptomatte_post(kg, render_buffer, pixel_index); +#endif +} + +/* -------------------------------------------------------------------- + * Bake. + */ +/* TODO(sergey): Needs to be re-implemented. Or not? Brecht did it already :) */ + +void KERNEL_FUNCTION_FULL_NAME(bake)( + const KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride) +{ +#if 0 +# ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, bake); +# else +# ifdef __BAKING__ + kernel_bake_evaluate(kg, buffer, sample, x, y, offset, stride); +# endif +# endif /* KERNEL_STUB */ +#endif +} + +#undef KERNEL_INVOKE +#undef DEFINE_INTEGRATOR_KERNEL +#undef DEFINE_INTEGRATOR_SHADE_KERNEL +#undef DEFINE_INTEGRATOR_INIT_KERNEL + +#undef KERNEL_STUB +#undef STUB_ASSERT +#undef KERNEL_ARCH + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/cpu/kernel_avx.cpp b/intern/cycles/kernel/device/cpu/kernel_avx.cpp similarity index 93% rename from intern/cycles/kernel/kernels/cpu/kernel_avx.cpp rename to intern/cycles/kernel/device/cpu/kernel_avx.cpp index 5f6b6800363..220768036ab 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_avx.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_avx.cpp @@ -34,6 +34,6 @@ # endif #endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX */ -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu_avx -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_avx2.cpp b/intern/cycles/kernel/device/cpu/kernel_avx2.cpp similarity index 93% rename from intern/cycles/kernel/kernels/cpu/kernel_avx2.cpp rename to intern/cycles/kernel/device/cpu/kernel_avx2.cpp index 97e8fc25140..90c05113cbe 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_avx2.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_avx2.cpp @@ -35,6 +35,6 @@ # endif #endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 */ -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu_avx2 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_sse2.cpp b/intern/cycles/kernel/device/cpu/kernel_sse2.cpp similarity index 93% rename from intern/cycles/kernel/kernels/cpu/kernel_sse2.cpp rename to intern/cycles/kernel/device/cpu/kernel_sse2.cpp index 26d7fd4de48..fb85ef5b0d0 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_sse2.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_sse2.cpp @@ -29,6 +29,6 @@ # endif #endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 */ -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu_sse2 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_sse3.cpp b/intern/cycles/kernel/device/cpu/kernel_sse3.cpp similarity index 93% rename from intern/cycles/kernel/kernels/cpu/kernel_sse3.cpp rename to intern/cycles/kernel/device/cpu/kernel_sse3.cpp index 3f259aa4480..87baf04258a 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_sse3.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_sse3.cpp @@ -31,6 +31,6 @@ # endif #endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 */ -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu_sse3 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_sse41.cpp b/intern/cycles/kernel/device/cpu/kernel_sse41.cpp similarity index 93% rename from intern/cycles/kernel/kernels/cpu/kernel_sse41.cpp rename to intern/cycles/kernel/device/cpu/kernel_sse41.cpp index 68bae8c07c6..bb421d58815 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_sse41.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_sse41.cpp @@ -32,6 +32,6 @@ # endif #endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 */ -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu_sse41 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" diff --git a/intern/cycles/kernel/kernel_compat_cuda.h b/intern/cycles/kernel/device/cuda/compat.h similarity index 56% rename from intern/cycles/kernel/kernel_compat_cuda.h rename to intern/cycles/kernel/device/cuda/compat.h index ea3b78b7cef..665da43e1a1 100644 --- a/intern/cycles/kernel/kernel_compat_cuda.h +++ b/intern/cycles/kernel/device/cuda/compat.h @@ -14,20 +14,15 @@ * limitations under the License. */ -#ifndef __KERNEL_COMPAT_CUDA_H__ -#define __KERNEL_COMPAT_CUDA_H__ +#pragma once #define __KERNEL_GPU__ #define __KERNEL_CUDA__ #define CCL_NAMESPACE_BEGIN #define CCL_NAMESPACE_END -/* Selective nodes compilation. */ -#ifndef __NODES_MAX_GROUP__ -# define __NODES_MAX_GROUP__ NODE_GROUP_LEVEL_MAX -#endif -#ifndef __NODES_FEATURES__ -# define __NODES_FEATURES__ NODE_FEATURE_ALL +#ifndef ATTR_FALLTHROUGH +# define ATTR_FALLTHROUGH #endif /* Manual definitions so we can compile without CUDA toolkit. */ @@ -38,8 +33,6 @@ typedef unsigned long long uint64_t; #else # include #endif -typedef unsigned short half; -typedef unsigned long long CUtexObject; #ifdef CYCLES_CUBIN_CC # define FLT_MIN 1.175494350822287507969e-38f @@ -47,14 +40,7 @@ typedef unsigned long long CUtexObject; # define FLT_EPSILON 1.192092896e-07F #endif -__device__ half __float2half(const float f) -{ - half val; - asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f)); - return val; -} - -/* Qualifier wrappers for different names on different devices */ +/* Qualifiers */ #define ccl_device __device__ __inline__ #if __CUDA_ARCH__ < 500 @@ -68,104 +54,61 @@ __device__ half __float2half(const float f) #define ccl_device_noinline_cpu ccl_device #define ccl_global #define ccl_static_constant __constant__ +#define ccl_device_constant __constant__ __device__ #define ccl_constant const -#define ccl_local __shared__ -#define ccl_local_param +#define ccl_gpu_shared __shared__ #define ccl_private #define ccl_may_alias #define ccl_addr_space #define ccl_restrict __restrict__ #define ccl_loop_no_unroll -/* TODO(sergey): In theory we might use references with CUDA, however - * performance impact yet to be investigated. - */ -#define ccl_ref #define ccl_align(n) __align__(n) #define ccl_optional_struct_init -#define ATTR_FALLTHROUGH - -#define CCL_MAX_LOCAL_SIZE (CUDA_THREADS_BLOCK_WIDTH * CUDA_THREADS_BLOCK_WIDTH) - /* No assert supported for CUDA */ #define kernel_assert(cond) -/* Types */ +/* GPU thread, block, grid size and index */ -#include "util/util_half.h" -#include "util/util_types.h" +#define ccl_gpu_thread_idx_x (threadIdx.x) +#define ccl_gpu_block_dim_x (blockDim.x) +#define ccl_gpu_block_idx_x (blockIdx.x) +#define ccl_gpu_grid_dim_x (gridDim.x) +#define ccl_gpu_warp_size (warpSize) -/* Work item functions */ +#define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) +#define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) -ccl_device_inline uint ccl_local_id(uint d) +/* GPU warp synchronizaton */ + +#define ccl_gpu_syncthreads() __syncthreads() +#define ccl_gpu_ballot(predicate) __ballot_sync(0xFFFFFFFF, predicate) +#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down_sync(mask, var, detla) +#define ccl_gpu_popc(x) __popc(x) + +/* GPU texture objects */ + +typedef unsigned long long CUtexObject; +typedef CUtexObject ccl_gpu_tex_object; + +template +ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object texobj, + const float x, + const float y) { - switch (d) { - case 0: - return threadIdx.x; - case 1: - return threadIdx.y; - case 2: - return threadIdx.z; - default: - return 0; - } + return tex2D(texobj, x, y); } -#define ccl_global_id(d) (ccl_group_id(d) * ccl_local_size(d) + ccl_local_id(d)) - -ccl_device_inline uint ccl_local_size(uint d) +template +ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object texobj, + const float x, + const float y, + const float z) { - switch (d) { - case 0: - return blockDim.x; - case 1: - return blockDim.y; - case 2: - return blockDim.z; - default: - return 0; - } + return tex3D(texobj, x, y, z); } -#define ccl_global_size(d) (ccl_num_groups(d) * ccl_local_size(d)) - -ccl_device_inline uint ccl_group_id(uint d) -{ - switch (d) { - case 0: - return blockIdx.x; - case 1: - return blockIdx.y; - case 2: - return blockIdx.z; - default: - return 0; - } -} - -ccl_device_inline uint ccl_num_groups(uint d) -{ - switch (d) { - case 0: - return gridDim.x; - case 1: - return gridDim.y; - case 2: - return gridDim.z; - default: - return 0; - } -} - -/* Textures */ - -/* Use arrays for regular data. */ -#define kernel_tex_fetch(t, index) t[(index)] -#define kernel_tex_array(t) (t) - -#define kernel_data __data - /* Use fast math functions */ #define cosf(x) __cosf(((float)(x))) @@ -175,4 +118,18 @@ ccl_device_inline uint ccl_num_groups(uint d) #define logf(x) __logf(((float)(x))) #define expf(x) __expf(((float)(x))) -#endif /* __KERNEL_COMPAT_CUDA_H__ */ +/* Half */ + +typedef unsigned short half; + +__device__ half __float2half(const float f) +{ + half val; + asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f)); + return val; +} + +/* Types */ + +#include "util/util_half.h" +#include "util/util_types.h" diff --git a/intern/cycles/kernel/device/cuda/config.h b/intern/cycles/kernel/device/cuda/config.h new file mode 100644 index 00000000000..46196dcdb51 --- /dev/null +++ b/intern/cycles/kernel/device/cuda/config.h @@ -0,0 +1,114 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Device data taken from CUDA occupancy calculator. + * + * Terminology + * - CUDA GPUs have multiple streaming multiprocessors + * - Each multiprocessor executes multiple thread blocks + * - Each thread block contains a number of threads, also known as the block size + * - Multiprocessors have a fixed number of registers, and the amount of registers + * used by each threads limits the number of threads per block. + */ + +/* 3.0 and 3.5 */ +#if __CUDA_ARCH__ == 300 || __CUDA_ARCH__ == 350 +# define GPU_MULTIPRESSOR_MAX_REGISTERS 65536 +# define GPU_MULTIPROCESSOR_MAX_BLOCKS 16 +# define GPU_BLOCK_MAX_THREADS 1024 +# define GPU_THREAD_MAX_REGISTERS 63 + +/* tunable parameters */ +# define GPU_KERNEL_BLOCK_NUM_THREADS 256 +# define GPU_KERNEL_MAX_REGISTERS 63 + +/* 3.2 */ +#elif __CUDA_ARCH__ == 320 +# define GPU_MULTIPRESSOR_MAX_REGISTERS 32768 +# define GPU_MULTIPROCESSOR_MAX_BLOCKS 16 +# define GPU_BLOCK_MAX_THREADS 1024 +# define GPU_THREAD_MAX_REGISTERS 63 + +/* tunable parameters */ +# define GPU_KERNEL_BLOCK_NUM_THREADS 256 +# define GPU_KERNEL_MAX_REGISTERS 63 + +/* 3.7 */ +#elif __CUDA_ARCH__ == 370 +# define GPU_MULTIPRESSOR_MAX_REGISTERS 65536 +# define GPU_MULTIPROCESSOR_MAX_BLOCKS 16 +# define GPU_BLOCK_MAX_THREADS 1024 +# define GPU_THREAD_MAX_REGISTERS 255 + +/* tunable parameters */ +# define GPU_KERNEL_BLOCK_NUM_THREADS 256 +# define GPU_KERNEL_MAX_REGISTERS 63 + +/* 5.x, 6.x */ +#elif __CUDA_ARCH__ <= 699 +# define GPU_MULTIPRESSOR_MAX_REGISTERS 65536 +# define GPU_MULTIPROCESSOR_MAX_BLOCKS 32 +# define GPU_BLOCK_MAX_THREADS 1024 +# define GPU_THREAD_MAX_REGISTERS 255 + +/* tunable parameters */ +# define GPU_KERNEL_BLOCK_NUM_THREADS 256 +/* CUDA 9.0 seems to cause slowdowns on high-end Pascal cards unless we increase the number of + * registers */ +# if __CUDACC_VER_MAJOR__ >= 9 && __CUDA_ARCH__ >= 600 +# define GPU_KERNEL_MAX_REGISTERS 64 +# else +# define GPU_KERNEL_MAX_REGISTERS 48 +# endif + +/* 7.x, 8.x */ +#elif __CUDA_ARCH__ <= 899 +# define GPU_MULTIPRESSOR_MAX_REGISTERS 65536 +# define GPU_MULTIPROCESSOR_MAX_BLOCKS 32 +# define GPU_BLOCK_MAX_THREADS 1024 +# define GPU_THREAD_MAX_REGISTERS 255 + +/* tunable parameters */ +# define GPU_KERNEL_BLOCK_NUM_THREADS 512 +# define GPU_KERNEL_MAX_REGISTERS 96 + +/* unknown architecture */ +#else +# error "Unknown or unsupported CUDA architecture, can't determine launch bounds" +#endif + +/* Compute number of threads per block and minimum blocks per multiprocessor + * given the maximum number of registers per thread. */ + +#define ccl_gpu_kernel(block_num_threads, thread_num_registers) \ + extern "C" __global__ void __launch_bounds__(block_num_threads, \ + GPU_MULTIPRESSOR_MAX_REGISTERS / \ + (block_num_threads * thread_num_registers)) + +/* sanity checks */ + +#if GPU_KERNEL_BLOCK_NUM_THREADS > GPU_BLOCK_MAX_THREADS +# error "Maximum number of threads per block exceeded" +#endif + +#if GPU_MULTIPRESSOR_MAX_REGISTERS / (GPU_KERNEL_BLOCK_NUM_THREADS * GPU_KERNEL_MAX_REGISTERS) > \ + GPU_MULTIPROCESSOR_MAX_BLOCKS +# error "Maximum number of blocks per multiprocessor exceeded" +#endif + +#if GPU_KERNEL_MAX_REGISTERS > GPU_THREAD_MAX_REGISTERS +# error "Maximum number of registers per thread exceeded" +#endif diff --git a/intern/cycles/kernel/device/cuda/globals.h b/intern/cycles/kernel/device/cuda/globals.h new file mode 100644 index 00000000000..169047175f5 --- /dev/null +++ b/intern/cycles/kernel/device/cuda/globals.h @@ -0,0 +1,48 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Constant Globals */ + +#pragma once + +#include "kernel/kernel_profiling.h" +#include "kernel/kernel_types.h" + +#include "kernel/integrator/integrator_state.h" + +CCL_NAMESPACE_BEGIN + +/* Not actually used, just a NULL pointer that gets passed everywhere, which we + * hope gets optimized out by the compiler. */ +struct KernelGlobals { + int unused[1]; +}; + +/* Global scene data and textures */ +__constant__ KernelData __data; +#define KERNEL_TEX(type, name) const __constant__ __device__ type *name; +#include "kernel/kernel_textures.h" + +/* Integrator state */ +__constant__ IntegratorStateGPU __integrator_state; + +/* Abstraction macros */ +#define kernel_data __data +#define kernel_tex_fetch(t, index) t[(index)] +#define kernel_tex_array(t) (t) +#define kernel_integrator_state __integrator_state + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_indirect_subsurface.cl b/intern/cycles/kernel/device/cuda/kernel.cu similarity index 64% rename from intern/cycles/kernel/kernels/opencl/kernel_indirect_subsurface.cl rename to intern/cycles/kernel/device/cuda/kernel.cu index 84938b889e5..e26fe243642 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_indirect_subsurface.cl +++ b/intern/cycles/kernel/device/cuda/kernel.cu @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Blender Foundation + * Copyright 2011-2013 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,15 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_indirect_subsurface.h" +/* CUDA kernel entry points */ -#define KERNEL_NAME indirect_subsurface -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME +#ifdef __CUDA_ARCH__ +# include "kernel/device/cuda/compat.h" +# include "kernel/device/cuda/config.h" +# include "kernel/device/cuda/globals.h" + +# include "kernel/device/gpu/image.h" +# include "kernel/device/gpu/kernel.h" + +#endif diff --git a/intern/cycles/kernel/kernels/cuda/kernel_cuda_image.h b/intern/cycles/kernel/device/gpu/image.h similarity index 77% rename from intern/cycles/kernel/kernels/cuda/kernel_cuda_image.h rename to intern/cycles/kernel/device/gpu/image.h index 132653fa7ca..b015c78a8f5 100644 --- a/intern/cycles/kernel/kernels/cuda/kernel_cuda_image.h +++ b/intern/cycles/kernel/device/gpu/image.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +CCL_NAMESPACE_BEGIN + #ifdef WITH_NANOVDB # define NDEBUG /* Disable "assert" in device code */ # define NANOVDB_USE_INTRINSICS @@ -61,9 +65,9 @@ ccl_device float cubic_h1(float a) /* Fast bicubic texture lookup using 4 bilinear lookups, adapted from CUDA samples. */ template -ccl_device T kernel_tex_image_interp_bicubic(const TextureInfo &info, float x, float y) +ccl_device_noinline T kernel_tex_image_interp_bicubic(const TextureInfo &info, float x, float y) { - CUtexObject tex = (CUtexObject)info.data; + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; x = (x * info.width) - 0.5f; y = (y * info.height) - 0.5f; @@ -81,15 +85,18 @@ ccl_device T kernel_tex_image_interp_bicubic(const TextureInfo &info, float x, f float y0 = (py + cubic_h0(fy) + 0.5f) / info.height; float y1 = (py + cubic_h1(fy) + 0.5f) / info.height; - return cubic_g0(fy) * (g0x * tex2D(tex, x0, y0) + g1x * tex2D(tex, x1, y0)) + - cubic_g1(fy) * (g0x * tex2D(tex, x0, y1) + g1x * tex2D(tex, x1, y1)); + return cubic_g0(fy) * (g0x * ccl_gpu_tex_object_read_2D(tex, x0, y0) + + g1x * ccl_gpu_tex_object_read_2D(tex, x1, y0)) + + cubic_g1(fy) * (g0x * ccl_gpu_tex_object_read_2D(tex, x0, y1) + + g1x * ccl_gpu_tex_object_read_2D(tex, x1, y1)); } /* Fast tricubic texture lookup using 8 trilinear lookups. */ template -ccl_device T kernel_tex_image_interp_tricubic(const TextureInfo &info, float x, float y, float z) +ccl_device_noinline T +kernel_tex_image_interp_tricubic(const TextureInfo &info, float x, float y, float z) { - CUtexObject tex = (CUtexObject)info.data; + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; x = (x * info.width) - 0.5f; y = (y * info.height) - 0.5f; @@ -117,10 +124,14 @@ ccl_device T kernel_tex_image_interp_tricubic(const TextureInfo &info, float x, float z0 = (pz + cubic_h0(fz) + 0.5f) / info.depth; float z1 = (pz + cubic_h1(fz) + 0.5f) / info.depth; - return g0z * (g0y * (g0x * tex3D(tex, x0, y0, z0) + g1x * tex3D(tex, x1, y0, z0)) + - g1y * (g0x * tex3D(tex, x0, y1, z0) + g1x * tex3D(tex, x1, y1, z0))) + - g1z * (g0y * (g0x * tex3D(tex, x0, y0, z1) + g1x * tex3D(tex, x1, y0, z1)) + - g1y * (g0x * tex3D(tex, x0, y1, z1) + g1x * tex3D(tex, x1, y1, z1))); + return g0z * (g0y * (g0x * ccl_gpu_tex_object_read_3D(tex, x0, y0, z0) + + g1x * ccl_gpu_tex_object_read_3D(tex, x1, y0, z0)) + + g1y * (g0x * ccl_gpu_tex_object_read_3D(tex, x0, y1, z0) + + g1x * ccl_gpu_tex_object_read_3D(tex, x1, y1, z0))) + + g1z * (g0y * (g0x * ccl_gpu_tex_object_read_3D(tex, x0, y0, z1) + + g1x * ccl_gpu_tex_object_read_3D(tex, x1, y0, z1)) + + g1y * (g0x * ccl_gpu_tex_object_read_3D(tex, x0, y1, z1) + + g1x * ccl_gpu_tex_object_read_3D(tex, x1, y1, z1))); } #ifdef WITH_NANOVDB @@ -157,7 +168,7 @@ ccl_device T kernel_tex_image_interp_tricubic_nanovdb(S &s, float x, float y, fl } template -ccl_device_inline T kernel_tex_image_interp_nanovdb( +ccl_device_noinline T kernel_tex_image_interp_nanovdb( const TextureInfo &info, float x, float y, float z, uint interpolation) { using namespace nanovdb; @@ -178,7 +189,7 @@ ccl_device_inline T kernel_tex_image_interp_nanovdb( } #endif -ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y) +ccl_device float4 kernel_tex_image_interp(const KernelGlobals *kg, int id, float x, float y) { const TextureInfo &info = kernel_tex_fetch(__texture_info, id); @@ -190,8 +201,8 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, fl return kernel_tex_image_interp_bicubic(info, x, y); } else { - CUtexObject tex = (CUtexObject)info.data; - return tex2D(tex, x, y); + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; + return ccl_gpu_tex_object_read_2D(tex, x, y); } } /* float, byte and half */ @@ -202,15 +213,15 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, fl f = kernel_tex_image_interp_bicubic(info, x, y); } else { - CUtexObject tex = (CUtexObject)info.data; - f = tex2D(tex, x, y); + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; + f = ccl_gpu_tex_object_read_2D(tex, x, y); } return make_float4(f, f, f, 1.0f); } } -ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, +ccl_device float4 kernel_tex_image_interp_3d(const KernelGlobals *kg, int id, float3 P, InterpolationType interp) @@ -245,8 +256,8 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, return kernel_tex_image_interp_tricubic(info, x, y, z); } else { - CUtexObject tex = (CUtexObject)info.data; - return tex3D(tex, x, y, z); + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; + return ccl_gpu_tex_object_read_3D(tex, x, y, z); } } else { @@ -256,10 +267,12 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, f = kernel_tex_image_interp_tricubic(info, x, y, z); } else { - CUtexObject tex = (CUtexObject)info.data; - f = tex3D(tex, x, y, z); + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; + f = ccl_gpu_tex_object_read_3D(tex, x, y, z); } return make_float4(f, f, f, 1.0f); } } + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h new file mode 100644 index 00000000000..7b79c0aedfa --- /dev/null +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -0,0 +1,843 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Common GPU kernels. */ + +#include "kernel/device/gpu/parallel_active_index.h" +#include "kernel/device/gpu/parallel_prefix_sum.h" +#include "kernel/device/gpu/parallel_sorted_index.h" + +#include "kernel/integrator/integrator_state.h" +#include "kernel/integrator/integrator_state_flow.h" +#include "kernel/integrator/integrator_state_util.h" + +#include "kernel/integrator/integrator_init_from_bake.h" +#include "kernel/integrator/integrator_init_from_camera.h" +#include "kernel/integrator/integrator_intersect_closest.h" +#include "kernel/integrator/integrator_intersect_shadow.h" +#include "kernel/integrator/integrator_intersect_subsurface.h" +#include "kernel/integrator/integrator_intersect_volume_stack.h" +#include "kernel/integrator/integrator_shade_background.h" +#include "kernel/integrator/integrator_shade_light.h" +#include "kernel/integrator/integrator_shade_shadow.h" +#include "kernel/integrator/integrator_shade_surface.h" +#include "kernel/integrator/integrator_shade_volume.h" + +#include "kernel/kernel_adaptive_sampling.h" +#include "kernel/kernel_bake.h" +#include "kernel/kernel_film.h" +#include "kernel/kernel_work_stealing.h" + +/* -------------------------------------------------------------------- + * Integrator. + */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_reset(int num_states) +{ + const int state = ccl_gpu_global_id_x(); + + if (state < num_states) { + INTEGRATOR_STATE_WRITE(path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = 0; + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_init_from_camera(KernelWorkTile *tiles, + const int num_tiles, + float *render_buffer, + const int max_tile_work_size) +{ + const int work_index = ccl_gpu_global_id_x(); + + if (work_index >= max_tile_work_size * num_tiles) { + return; + } + + const int tile_index = work_index / max_tile_work_size; + const int tile_work_index = work_index - tile_index * max_tile_work_size; + + const KernelWorkTile *tile = &tiles[tile_index]; + + if (tile_work_index >= tile->work_size) { + return; + } + + const int state = tile->path_index_offset + tile_work_index; + + uint x, y, sample; + get_work_pixel(tile, tile_work_index, &x, &y, &sample); + + integrator_init_from_camera(nullptr, state, tile, render_buffer, x, y, sample); +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_init_from_bake(KernelWorkTile *tiles, + const int num_tiles, + float *render_buffer, + const int max_tile_work_size) +{ + const int work_index = ccl_gpu_global_id_x(); + + if (work_index >= max_tile_work_size * num_tiles) { + return; + } + + const int tile_index = work_index / max_tile_work_size; + const int tile_work_index = work_index - tile_index * max_tile_work_size; + + const KernelWorkTile *tile = &tiles[tile_index]; + + if (tile_work_index >= tile->work_size) { + return; + } + + const int state = tile->path_index_offset + tile_work_index; + + uint x, y, sample; + get_work_pixel(tile, tile_work_index, &x, &y, &sample); + + integrator_init_from_bake(nullptr, state, tile, render_buffer, x, y, sample); +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_intersect_closest(const int *path_index_array, const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_intersect_closest(NULL, state); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_intersect_shadow(const int *path_index_array, const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_intersect_shadow(NULL, state); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_intersect_subsurface(const int *path_index_array, const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_intersect_subsurface(NULL, state); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_intersect_volume_stack(const int *path_index_array, const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_intersect_volume_stack(NULL, state); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_background(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_background(NULL, state, render_buffer); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_light(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_light(NULL, state, render_buffer); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_shadow(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_shadow(NULL, state, render_buffer); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_surface(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_surface(NULL, state, render_buffer); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_surface_raytrace(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_surface_raytrace(NULL, state, render_buffer); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_volume(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_volume(NULL, state, render_buffer); + } +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_queued_paths_array(int num_states, + int *indices, + int *num_indices, + int kernel) +{ + gpu_parallel_active_index_array( + num_states, indices, num_indices, [kernel](const int state) { + return (INTEGRATOR_STATE(path, queued_kernel) == kernel); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_queued_shadow_paths_array(int num_states, + int *indices, + int *num_indices, + int kernel) +{ + gpu_parallel_active_index_array( + num_states, indices, num_indices, [kernel](const int state) { + return (INTEGRATOR_STATE(shadow_path, queued_kernel) == kernel); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_active_paths_array(int num_states, int *indices, int *num_indices) +{ + gpu_parallel_active_index_array( + num_states, indices, num_indices, [](const int state) { + return (INTEGRATOR_STATE(path, queued_kernel) != 0) || + (INTEGRATOR_STATE(shadow_path, queued_kernel) != 0); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_terminated_paths_array(int num_states, + int *indices, + int *num_indices, + int indices_offset) +{ + gpu_parallel_active_index_array( + num_states, indices + indices_offset, num_indices, [](const int state) { + return (INTEGRATOR_STATE(path, queued_kernel) == 0) && + (INTEGRATOR_STATE(shadow_path, queued_kernel) == 0); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_sorted_paths_array( + int num_states, int *indices, int *num_indices, int *key_prefix_sum, int kernel) +{ + gpu_parallel_sorted_index_array( + num_states, indices, num_indices, key_prefix_sum, [kernel](const int state) { + return (INTEGRATOR_STATE(path, queued_kernel) == kernel) ? + INTEGRATOR_STATE(path, shader_sort_key) : + GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY; + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_compact_paths_array(int num_states, + int *indices, + int *num_indices, + int num_active_paths) +{ + gpu_parallel_active_index_array( + num_states, indices, num_indices, [num_active_paths](const int state) { + return (state >= num_active_paths) && + ((INTEGRATOR_STATE(path, queued_kernel) != 0) || + (INTEGRATOR_STATE(shadow_path, queued_kernel) != 0)); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_compact_states(const int *active_terminated_states, + const int active_states_offset, + const int terminated_states_offset, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int from_state = active_terminated_states[active_states_offset + global_index]; + const int to_state = active_terminated_states[terminated_states_offset + global_index]; + + integrator_state_move(to_state, from_state); + } +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE) + kernel_gpu_prefix_sum(int *values, int num_values) +{ + gpu_parallel_prefix_sum(values, num_values); +} + +/* -------------------------------------------------------------------- + * Adaptive sampling. + */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_adaptive_sampling_convergence_check(float *render_buffer, + int sx, + int sy, + int sw, + int sh, + float threshold, + bool reset, + int offset, + int stride, + uint *num_active_pixels) +{ + const int work_index = ccl_gpu_global_id_x(); + const int y = work_index / sw; + const int x = work_index - y * sw; + + bool converged = true; + + if (x < sw && y < sh) { + converged = kernel_adaptive_sampling_convergence_check( + nullptr, render_buffer, sx + x, sy + y, threshold, reset, offset, stride); + } + + /* NOTE: All threads specified in the mask must execute the intrinsic. */ + const uint num_active_pixels_mask = ccl_gpu_ballot(!converged); + const int lane_id = ccl_gpu_thread_idx_x % ccl_gpu_warp_size; + if (lane_id == 0) { + atomic_fetch_and_add_uint32(num_active_pixels, __popc(num_active_pixels_mask)); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_adaptive_sampling_filter_x( + float *render_buffer, int sx, int sy, int sw, int sh, int offset, int stride) +{ + const int y = ccl_gpu_global_id_x(); + + if (y < sh) { + kernel_adaptive_sampling_filter_x(NULL, render_buffer, sy + y, sx, sw, offset, stride); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_adaptive_sampling_filter_y( + float *render_buffer, int sx, int sy, int sw, int sh, int offset, int stride) +{ + const int x = ccl_gpu_global_id_x(); + + if (x < sw) { + kernel_adaptive_sampling_filter_y(NULL, render_buffer, sx + x, sy, sh, offset, stride); + } +} + +/* -------------------------------------------------------------------- + * Cryptomatte. + */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_cryptomatte_postprocess(float *render_buffer, int num_pixels) +{ + const int pixel_index = ccl_gpu_global_id_x(); + + if (pixel_index < num_pixels) { + kernel_cryptomatte_post(nullptr, render_buffer, pixel_index); + } +} + +/* -------------------------------------------------------------------- + * Film. + */ + +/* Common implementation for float destination. */ +template +ccl_device_inline void kernel_gpu_film_convert_common(const KernelFilmConvert *kfilm_convert, + float *pixels, + float *render_buffer, + int num_pixels, + int width, + int offset, + int stride, + int dst_offset, + int dst_stride, + const Processor &processor) +{ + const int render_pixel_index = ccl_gpu_global_id_x(); + if (render_pixel_index >= num_pixels) { + return; + } + + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kfilm_convert->pass_stride; + ccl_global const float *buffer = render_buffer + render_buffer_offset; + ccl_global float *pixel = pixels + + (render_pixel_index + dst_offset) * kfilm_convert->pixel_stride; + + processor(kfilm_convert, buffer, pixel); +} + +/* Common implementation for half4 destination and 4-channel input pass. */ +template +ccl_device_inline void kernel_gpu_film_convert_half_rgba_common_rgba( + const KernelFilmConvert *kfilm_convert, + uchar4 *rgba, + float *render_buffer, + int num_pixels, + int width, + int offset, + int stride, + int rgba_offset, + int rgba_stride, + const Processor &processor) +{ + const int render_pixel_index = ccl_gpu_global_id_x(); + if (render_pixel_index >= num_pixels) { + return; + } + + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kfilm_convert->pass_stride; + ccl_global const float *buffer = render_buffer + render_buffer_offset; + + float pixel[4]; + processor(kfilm_convert, buffer, pixel); + + film_apply_pass_pixel_overlays_rgba(kfilm_convert, buffer, pixel); + + const int x = render_pixel_index % width; + const int y = render_pixel_index / width; + + ccl_global half4 *out = ((ccl_global half4 *)rgba) + rgba_offset + y * rgba_stride + x; + float4_store_half((ccl_global half *)out, make_float4(pixel[0], pixel[1], pixel[2], pixel[3])); +} + +/* Common implementation for half4 destination and 3-channel input pass. */ +template +ccl_device_inline void kernel_gpu_film_convert_half_rgba_common_rgb( + const KernelFilmConvert *kfilm_convert, + uchar4 *rgba, + float *render_buffer, + int num_pixels, + int width, + int offset, + int stride, + int rgba_offset, + int rgba_stride, + const Processor &processor) +{ + kernel_gpu_film_convert_half_rgba_common_rgba( + kfilm_convert, + rgba, + render_buffer, + num_pixels, + width, + offset, + stride, + rgba_offset, + rgba_stride, + [&processor](const KernelFilmConvert *kfilm_convert, + ccl_global const float *buffer, + float *pixel_rgba) { + processor(kfilm_convert, buffer, pixel_rgba); + pixel_rgba[3] = 1.0f; + }); +} + +/* Common implementation for half4 destination and single channel input pass. */ +template +ccl_device_inline void kernel_gpu_film_convert_half_rgba_common_value( + const KernelFilmConvert *kfilm_convert, + uchar4 *rgba, + float *render_buffer, + int num_pixels, + int width, + int offset, + int stride, + int rgba_offset, + int rgba_stride, + const Processor &processor) +{ + kernel_gpu_film_convert_half_rgba_common_rgba( + kfilm_convert, + rgba, + render_buffer, + num_pixels, + width, + offset, + stride, + rgba_offset, + rgba_stride, + [&processor](const KernelFilmConvert *kfilm_convert, + ccl_global const float *buffer, + float *pixel_rgba) { + float value; + processor(kfilm_convert, buffer, &value); + + pixel_rgba[0] = value; + pixel_rgba[1] = value; + pixel_rgba[2] = value; + pixel_rgba[3] = 1.0f; + }); +} + +#define KERNEL_FILM_CONVERT_PROC(name) \ + ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) name + +#define KERNEL_FILM_CONVERT_DEFINE(variant, channels) \ + KERNEL_FILM_CONVERT_PROC(kernel_gpu_film_convert_##variant) \ + (const KernelFilmConvert kfilm_convert, \ + float *pixels, \ + float *render_buffer, \ + int num_pixels, \ + int width, \ + int offset, \ + int stride, \ + int rgba_offset, \ + int rgba_stride) \ + { \ + kernel_gpu_film_convert_common(&kfilm_convert, \ + pixels, \ + render_buffer, \ + num_pixels, \ + width, \ + offset, \ + stride, \ + rgba_offset, \ + rgba_stride, \ + film_get_pass_pixel_##variant); \ + } \ + KERNEL_FILM_CONVERT_PROC(kernel_gpu_film_convert_##variant##_half_rgba) \ + (const KernelFilmConvert kfilm_convert, \ + uchar4 *rgba, \ + float *render_buffer, \ + int num_pixels, \ + int width, \ + int offset, \ + int stride, \ + int rgba_offset, \ + int rgba_stride) \ + { \ + kernel_gpu_film_convert_half_rgba_common_##channels(&kfilm_convert, \ + rgba, \ + render_buffer, \ + num_pixels, \ + width, \ + offset, \ + stride, \ + rgba_offset, \ + rgba_stride, \ + film_get_pass_pixel_##variant); \ + } + +KERNEL_FILM_CONVERT_DEFINE(depth, value) +KERNEL_FILM_CONVERT_DEFINE(mist, value) +KERNEL_FILM_CONVERT_DEFINE(sample_count, value) +KERNEL_FILM_CONVERT_DEFINE(float, value) + +KERNEL_FILM_CONVERT_DEFINE(light_path, rgb) +KERNEL_FILM_CONVERT_DEFINE(float3, rgb) + +KERNEL_FILM_CONVERT_DEFINE(motion, rgba) +KERNEL_FILM_CONVERT_DEFINE(cryptomatte, rgba) +KERNEL_FILM_CONVERT_DEFINE(shadow_catcher, rgba) +KERNEL_FILM_CONVERT_DEFINE(shadow_catcher_matte_with_shadow, rgba) +KERNEL_FILM_CONVERT_DEFINE(combined, rgba) +KERNEL_FILM_CONVERT_DEFINE(float4, rgba) + +#undef KERNEL_FILM_CONVERT_DEFINE +#undef KERNEL_FILM_CONVERT_HALF_RGBA_DEFINE +#undef KERNEL_FILM_CONVERT_PROC + +/* -------------------------------------------------------------------- + * Shader evaluation. + */ + +/* Displacement */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_shader_eval_displace(KernelShaderEvalInput *input, + float4 *output, + const int offset, + const int work_size) +{ + int i = ccl_gpu_global_id_x(); + if (i < work_size) { + kernel_displace_evaluate(NULL, input, output, offset + i); + } +} + +/* Background Shader Evaluation */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_shader_eval_background(KernelShaderEvalInput *input, + float4 *output, + const int offset, + const int work_size) +{ + int i = ccl_gpu_global_id_x(); + if (i < work_size) { + kernel_background_evaluate(NULL, input, output, offset + i); + } +} + +/* -------------------------------------------------------------------- + * Denoising. + */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_filter_color_preprocess(float *render_buffer, + int full_x, + int full_y, + int width, + int height, + int offset, + int stride, + int pass_stride, + int pass_denoised) +{ + const int work_index = ccl_gpu_global_id_x(); + const int y = work_index / width; + const int x = work_index - y * width; + + if (x >= width || y >= height) { + return; + } + + const uint64_t render_pixel_index = offset + (x + full_x) + (y + full_y) * stride; + float *buffer = render_buffer + render_pixel_index * pass_stride; + + float *color_out = buffer + pass_denoised; + color_out[0] = clamp(color_out[0], 0.0f, 10000.0f); + color_out[1] = clamp(color_out[1], 0.0f, 10000.0f); + color_out[2] = clamp(color_out[2], 0.0f, 10000.0f); +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_filter_guiding_preprocess(float *guiding_buffer, + int guiding_pass_stride, + int guiding_pass_albedo, + int guiding_pass_normal, + const float *render_buffer, + int render_offset, + int render_stride, + int render_pass_stride, + int render_pass_sample_count, + int render_pass_denoising_albedo, + int render_pass_denoising_normal, + int full_x, + int full_y, + int width, + int height, + int num_samples) +{ + const int work_index = ccl_gpu_global_id_x(); + const int y = work_index / width; + const int x = work_index - y * width; + + if (x >= width || y >= height) { + return; + } + + const uint64_t guiding_pixel_index = x + y * width; + float *guiding_pixel = guiding_buffer + guiding_pixel_index * guiding_pass_stride; + + const uint64_t render_pixel_index = render_offset + (x + full_x) + (y + full_y) * render_stride; + const float *buffer = render_buffer + render_pixel_index * render_pass_stride; + + float pixel_scale; + if (render_pass_sample_count == PASS_UNUSED) { + pixel_scale = 1.0f / num_samples; + } + else { + pixel_scale = 1.0f / __float_as_uint(buffer[render_pass_sample_count]); + } + + /* Albedo pass. */ + if (guiding_pass_albedo != PASS_UNUSED) { + kernel_assert(render_pass_denoising_albedo != PASS_UNUSED); + + const float *aledo_in = buffer + render_pass_denoising_albedo; + float *albedo_out = guiding_pixel + guiding_pass_albedo; + + albedo_out[0] = aledo_in[0] * pixel_scale; + albedo_out[1] = aledo_in[1] * pixel_scale; + albedo_out[2] = aledo_in[2] * pixel_scale; + } + + /* Normal pass. */ + if (render_pass_denoising_normal != PASS_UNUSED) { + kernel_assert(render_pass_denoising_normal != PASS_UNUSED); + + const float *normal_in = buffer + render_pass_denoising_normal; + float *normal_out = guiding_pixel + guiding_pass_normal; + + normal_out[0] = normal_in[0] * pixel_scale; + normal_out[1] = normal_in[1] * pixel_scale; + normal_out[2] = normal_in[2] * pixel_scale; + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_filter_guiding_set_fake_albedo(float *guiding_buffer, + int guiding_pass_stride, + int guiding_pass_albedo, + int width, + int height) +{ + kernel_assert(guiding_pass_albedo != PASS_UNUSED); + + const int work_index = ccl_gpu_global_id_x(); + const int y = work_index / width; + const int x = work_index - y * width; + + if (x >= width || y >= height) { + return; + } + + const uint64_t guiding_pixel_index = x + y * width; + float *guiding_pixel = guiding_buffer + guiding_pixel_index * guiding_pass_stride; + + float *albedo_out = guiding_pixel + guiding_pass_albedo; + + albedo_out[0] = 0.5f; + albedo_out[1] = 0.5f; + albedo_out[2] = 0.5f; +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_filter_color_postprocess(float *render_buffer, + int full_x, + int full_y, + int width, + int height, + int offset, + int stride, + int pass_stride, + int num_samples, + int pass_noisy, + int pass_denoised, + int pass_sample_count, + int num_components, + bool use_compositing) +{ + const int work_index = ccl_gpu_global_id_x(); + const int y = work_index / width; + const int x = work_index - y * width; + + if (x >= width || y >= height) { + return; + } + + const uint64_t render_pixel_index = offset + (x + full_x) + (y + full_y) * stride; + float *buffer = render_buffer + render_pixel_index * pass_stride; + + float pixel_scale; + if (pass_sample_count == PASS_UNUSED) { + pixel_scale = num_samples; + } + else { + pixel_scale = __float_as_uint(buffer[pass_sample_count]); + } + + float *denoised_pixel = buffer + pass_denoised; + + denoised_pixel[0] *= pixel_scale; + denoised_pixel[1] *= pixel_scale; + denoised_pixel[2] *= pixel_scale; + + if (num_components == 3) { + /* Pass without alpha channel. */ + } + else if (!use_compositing) { + /* Currently compositing passes are either 3-component (derived by dividing light passes) + * or do not have transparency (shadow catcher). Implicitly rely on this logic, as it + * simplifies logic and avoids extra memory allocation. */ + const float *noisy_pixel = buffer + pass_noisy; + denoised_pixel[3] = noisy_pixel[3]; + } + else { + /* Assigning to zero since this is a default alpha value for 3-component passes, and it + * is an opaque pixel for 4 component passes. */ + + denoised_pixel[3] = 0; + } +} + +/* -------------------------------------------------------------------- + * Shadow catcher. + */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shadow_catcher_count_possible_splits(int num_states, + uint *num_possible_splits) +{ + const int state = ccl_gpu_global_id_x(); + + bool can_split = false; + + if (state < num_states) { + can_split = kernel_shadow_catcher_path_can_split(nullptr, state); + } + + /* NOTE: All threads specified in the mask must execute the intrinsic. */ + const uint can_split_mask = ccl_gpu_ballot(can_split); + const int lane_id = ccl_gpu_thread_idx_x % ccl_gpu_warp_size; + if (lane_id == 0) { + atomic_fetch_and_add_uint32(num_possible_splits, __popc(can_split_mask)); + } +} diff --git a/intern/cycles/kernel/device/gpu/parallel_active_index.h b/intern/cycles/kernel/device/gpu/parallel_active_index.h new file mode 100644 index 00000000000..85500bf4d07 --- /dev/null +++ b/intern/cycles/kernel/device/gpu/parallel_active_index.h @@ -0,0 +1,83 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Given an array of states, build an array of indices for which the states + * are active. + * + * Shared memory requirement is sizeof(int) * (number_of_warps + 1) */ + +#include "util/util_atomic.h" + +#define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 512 + +template +__device__ void gpu_parallel_active_index_array(const uint num_states, + int *indices, + int *num_indices, + IsActiveOp is_active_op) +{ + extern ccl_gpu_shared int warp_offset[]; + + const uint thread_index = ccl_gpu_thread_idx_x; + const uint thread_warp = thread_index % ccl_gpu_warp_size; + + const uint warp_index = thread_index / ccl_gpu_warp_size; + const uint num_warps = blocksize / ccl_gpu_warp_size; + + /* Test if state corresponding to this thread is active. */ + const uint state_index = ccl_gpu_block_idx_x * blocksize + thread_index; + const uint is_active = (state_index < num_states) ? is_active_op(state_index) : 0; + + /* For each thread within a warp compute how many other active states precede it. */ + const uint thread_mask = 0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp); + const uint thread_offset = ccl_gpu_popc(ccl_gpu_ballot(is_active) & thread_mask); + + /* Last thread in warp stores number of active states for each warp. */ + if (thread_warp == ccl_gpu_warp_size - 1) { + warp_offset[warp_index] = thread_offset + is_active; + } + + ccl_gpu_syncthreads(); + + /* Last thread in block converts per-warp sizes to offsets, increments global size of + * index array and gets offset to write to. */ + if (thread_index == blocksize - 1) { + /* TODO: parallelize this. */ + int offset = 0; + for (int i = 0; i < num_warps; i++) { + int num_active = warp_offset[i]; + warp_offset[i] = offset; + offset += num_active; + } + + const uint block_num_active = warp_offset[warp_index] + thread_offset + is_active; + warp_offset[num_warps] = atomic_fetch_and_add_uint32(num_indices, block_num_active); + } + + ccl_gpu_syncthreads(); + + /* Write to index array. */ + if (is_active) { + const uint block_offset = warp_offset[num_warps]; + indices[block_offset + warp_offset[warp_index] + thread_offset] = state_index; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h new file mode 100644 index 00000000000..f609520b8b4 --- /dev/null +++ b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h @@ -0,0 +1,46 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Parallel prefix sum. + * + * TODO: actually make this work in parallel. + * + * This is used for an array the size of the number of shaders in the scene + * which is not usually huge, so might not be a significant bottleneck. */ + +#include "util/util_atomic.h" + +#define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 512 + +template __device__ void gpu_parallel_prefix_sum(int *values, const int num_values) +{ + if (!(ccl_gpu_block_idx_x == 0 && ccl_gpu_thread_idx_x == 0)) { + return; + } + + int offset = 0; + for (int i = 0; i < num_values; i++) { + const int new_offset = offset + values[i]; + values[i] = offset; + offset = new_offset; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/gpu/parallel_reduce.h b/intern/cycles/kernel/device/gpu/parallel_reduce.h new file mode 100644 index 00000000000..65b1990dbb8 --- /dev/null +++ b/intern/cycles/kernel/device/gpu/parallel_reduce.h @@ -0,0 +1,83 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Parallel sum of array input_data with size n into output_sum. + * + * Adapted from "Optimizing Parallel Reduction in GPU", Mark Harris. + * + * This version adds multiple elements per thread sequentially. This reduces + * the overall cost of the algorithm while keeping the work complexity O(n) and + * the step complexity O(log n). (Brent's Theorem optimization) */ + +#define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 512 + +template +__device__ void gpu_parallel_sum( + const InputT *input_data, const uint n, OutputT *output_sum, OutputT zero, ConvertOp convert) +{ + extern ccl_gpu_shared OutputT shared_data[]; + + const uint tid = ccl_gpu_thread_idx_x; + const uint gridsize = blocksize * ccl_gpu_grid_dim_x(); + + OutputT sum = zero; + for (uint i = ccl_gpu_block_idx_x * blocksize + tid; i < n; i += gridsize) { + sum += convert(input_data[i]); + } + shared_data[tid] = sum; + + ccl_gpu_syncthreads(); + + if (blocksize >= 512 && tid < 256) { + shared_data[tid] = sum = sum + shared_data[tid + 256]; + } + + ccl_gpu_syncthreads(); + + if (blocksize >= 256 && tid < 128) { + shared_data[tid] = sum = sum + shared_data[tid + 128]; + } + + ccl_gpu_syncthreads(); + + if (blocksize >= 128 && tid < 64) { + shared_data[tid] = sum = sum + shared_data[tid + 64]; + } + + ccl_gpu_syncthreads(); + + if (blocksize >= 64 && tid < 32) { + shared_data[tid] = sum = sum + shared_data[tid + 32]; + } + + ccl_gpu_syncthreads(); + + if (tid < 32) { + for (int offset = ccl_gpu_warp_size / 2; offset > 0; offset /= 2) { + sum += ccl_shfl_down_sync(0xFFFFFFFF, sum, offset); + } + } + + if (tid == 0) { + output_sum[ccl_gpu_block_idx_x] = sum; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/gpu/parallel_sorted_index.h b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h new file mode 100644 index 00000000000..99b35468517 --- /dev/null +++ b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h @@ -0,0 +1,49 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Given an array of states, build an array of indices for which the states + * are active and sorted by a given key. The prefix sum of the number of active + * states per key must have already been computed. + * + * TODO: there may be ways to optimize this to avoid this many atomic ops? */ + +#include "util/util_atomic.h" + +#define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 512 +#define GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY (~0) + +template +__device__ void gpu_parallel_sorted_index_array(const uint num_states, + int *indices, + int *num_indices, + int *key_prefix_sum, + GetKeyOp get_key_op) +{ + const uint state_index = ccl_gpu_block_idx_x * blocksize + ccl_gpu_thread_idx_x; + const int key = (state_index < num_states) ? get_key_op(state_index) : + GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY; + + if (key != GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY) { + const uint index = atomic_fetch_and_add_uint32(&key_prefix_sum[key], 1); + indices[index] = state_index; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_compat_optix.h b/intern/cycles/kernel/device/optix/compat.h similarity index 54% rename from intern/cycles/kernel/kernel_compat_optix.h rename to intern/cycles/kernel/device/optix/compat.h index 064c99ca100..4e255a135c6 100644 --- a/intern/cycles/kernel/kernel_compat_optix.h +++ b/intern/cycles/kernel/device/optix/compat.h @@ -15,14 +15,13 @@ * limitations under the License. */ -#ifndef __KERNEL_COMPAT_OPTIX_H__ -#define __KERNEL_COMPAT_OPTIX_H__ +#pragma once #define OPTIX_DONT_INCLUDE_CUDA #include #define __KERNEL_GPU__ -#define __KERNEL_CUDA__ // OptiX kernels are implicitly CUDA kernels too +#define __KERNEL_CUDA__ /* OptiX kernels are implicitly CUDA kernels too */ #define __KERNEL_OPTIX__ #define CCL_NAMESPACE_BEGIN #define CCL_NAMESPACE_END @@ -31,14 +30,14 @@ # define ATTR_FALLTHROUGH #endif +/* Manual definitions so we can compile without CUDA toolkit. */ + #ifdef __CUDACC_RTC__ typedef unsigned int uint32_t; typedef unsigned long long uint64_t; #else # include #endif -typedef unsigned short half; -typedef unsigned long long CUtexObject; #ifdef CYCLES_CUBIN_CC # define FLT_MIN 1.175494350822287507969e-38f @@ -46,21 +45,6 @@ typedef unsigned long long CUtexObject; # define FLT_EPSILON 1.192092896e-07F #endif -__device__ half __float2half(const float f) -{ - half val; - asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f)); - return val; -} - -/* Selective nodes compilation. */ -#ifndef __NODES_MAX_GROUP__ -# define __NODES_MAX_GROUP__ NODE_GROUP_LEVEL_MAX -#endif -#ifndef __NODES_FEATURES__ -# define __NODES_FEATURES__ NODE_FEATURE_ALL -#endif - #define ccl_device \ __device__ __forceinline__ // Function calls are bad for OptiX performance, so inline everything #define ccl_device_inline ccl_device @@ -69,29 +53,75 @@ __device__ half __float2half(const float f) #define ccl_device_noinline_cpu ccl_device #define ccl_global #define ccl_static_constant __constant__ +#define ccl_device_constant __constant__ __device__ #define ccl_constant const -#define ccl_local -#define ccl_local_param +#define ccl_gpu_shared __shared__ #define ccl_private #define ccl_may_alias #define ccl_addr_space -#define ccl_loop_no_unroll #define ccl_restrict __restrict__ -#define ccl_ref +#define ccl_loop_no_unroll #define ccl_align(n) __align__(n) -// Zero initialize structs to help the compiler figure out scoping +/* Zero initialize structs to help the compiler figure out scoping */ #define ccl_optional_struct_init = {} -#define kernel_data __params.data // See kernel_globals.h -#define kernel_tex_array(t) __params.t -#define kernel_tex_fetch(t, index) __params.t[(index)] +/* No assert supported for CUDA */ #define kernel_assert(cond) +/* GPU thread, block, grid size and index */ + +#define ccl_gpu_thread_idx_x (threadIdx.x) +#define ccl_gpu_block_dim_x (blockDim.x) +#define ccl_gpu_block_idx_x (blockIdx.x) +#define ccl_gpu_grid_dim_x (gridDim.x) +#define ccl_gpu_warp_size (warpSize) + +#define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) +#define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) + +/* GPU warp synchronizaton */ + +#define ccl_gpu_syncthreads() __syncthreads() +#define ccl_gpu_ballot(predicate) __ballot_sync(0xFFFFFFFF, predicate) +#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down_sync(mask, var, detla) +#define ccl_gpu_popc(x) __popc(x) + +/* GPU texture objects */ + +typedef unsigned long long CUtexObject; +typedef CUtexObject ccl_gpu_tex_object; + +template +ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object texobj, + const float x, + const float y) +{ + return tex2D(texobj, x, y); +} + +template +ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object texobj, + const float x, + const float y, + const float z) +{ + return tex3D(texobj, x, y, z); +} + +/* Half */ + +typedef unsigned short half; + +__device__ half __float2half(const float f) +{ + half val; + asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f)); + return val; +} + /* Types */ #include "util/util_half.h" #include "util/util_types.h" - -#endif /* __KERNEL_COMPAT_OPTIX_H__ */ diff --git a/intern/cycles/kernel/device/optix/globals.h b/intern/cycles/kernel/device/optix/globals.h new file mode 100644 index 00000000000..7d898ed5d91 --- /dev/null +++ b/intern/cycles/kernel/device/optix/globals.h @@ -0,0 +1,59 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Constant Globals */ + +#pragma once + +#include "kernel/kernel_profiling.h" +#include "kernel/kernel_types.h" + +#include "kernel/integrator/integrator_state.h" + +CCL_NAMESPACE_BEGIN + +/* Not actually used, just a NULL pointer that gets passed everywhere, which we + * hope gets optimized out by the compiler. */ +struct KernelGlobals { + int unused[1]; +}; + +/* Launch parameters */ +struct KernelParamsOptiX { + /* Kernel arguments */ + const int *path_index_array; + float *render_buffer; + + /* Global scene data and textures */ + KernelData data; +#define KERNEL_TEX(type, name) const type *name; +#include "kernel/kernel_textures.h" + + /* Integrator state */ + IntegratorStateGPU __integrator_state; +}; + +#ifdef __NVCC__ +extern "C" static __constant__ KernelParamsOptiX __params; +#endif + +/* Abstraction macros */ +#define kernel_data __params.data +#define kernel_tex_array(t) __params.t +#define kernel_tex_fetch(t, index) __params.t[(index)] +#define kernel_integrator_state __params.__integrator_state + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/optix/kernel_optix.cu b/intern/cycles/kernel/device/optix/kernel.cu similarity index 66% rename from intern/cycles/kernel/kernels/optix/kernel_optix.cu rename to intern/cycles/kernel/device/optix/kernel.cu index 7f609eab474..c1e36febfc0 100644 --- a/intern/cycles/kernel/kernels/optix/kernel_optix.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -16,14 +16,20 @@ */ // clang-format off -#include "kernel/kernel_compat_optix.h" -#include "util/util_atomic.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" -#include "../cuda/kernel_cuda_image.h" // Texture lookup uses normal CUDA intrinsics +#include "kernel/device/optix/compat.h" +#include "kernel/device/optix/globals.h" + +#include "kernel/device/gpu/image.h" // Texture lookup uses normal CUDA intrinsics + +#include "kernel/integrator/integrator_state.h" +#include "kernel/integrator/integrator_state_flow.h" +#include "kernel/integrator/integrator_state_util.h" + +#include "kernel/integrator/integrator_intersect_closest.h" +#include "kernel/integrator/integrator_intersect_shadow.h" +#include "kernel/integrator/integrator_intersect_subsurface.h" +#include "kernel/integrator/integrator_intersect_volume_stack.h" -#include "kernel/kernel_path.h" -#include "kernel/kernel_bake.h" // clang-format on template ccl_device_forceinline T *get_payload_ptr_0() @@ -53,52 +59,36 @@ template ccl_device_forceinline uint get_object_id() return OBJECT_NONE; } -extern "C" __global__ void __raygen__kernel_optix_path_trace() +extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_closest() { - KernelGlobals kg; // Allocate stack storage for common data - - const uint3 launch_index = optixGetLaunchIndex(); - // Keep threads for same pixel together to improve occupancy of warps - uint pixel_offset = launch_index.x / __params.tile.num_samples; - uint sample_offset = launch_index.x % __params.tile.num_samples; - - kernel_path_trace(&kg, - __params.tile.buffer, - __params.tile.start_sample + sample_offset, - __params.tile.x + pixel_offset, - __params.tile.y + launch_index.y, - __params.tile.offset, - __params.tile.stride); + const int global_index = optixGetLaunchIndex().x; + const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] : + global_index; + integrator_intersect_closest(nullptr, path_index); } -#ifdef __BAKING__ -extern "C" __global__ void __raygen__kernel_optix_bake() +extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_shadow() { - KernelGlobals kg; - const ShaderParams &p = __params.shader; - kernel_bake_evaluate(&kg, - p.input, - p.output, - (ShaderEvalType)p.type, - p.filter, - p.sx + optixGetLaunchIndex().x, - p.offset, - p.sample); -} -#endif - -extern "C" __global__ void __raygen__kernel_optix_displace() -{ - KernelGlobals kg; - const ShaderParams &p = __params.shader; - kernel_displace_evaluate(&kg, p.input, p.output, p.sx + optixGetLaunchIndex().x); + const int global_index = optixGetLaunchIndex().x; + const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] : + global_index; + integrator_intersect_shadow(nullptr, path_index); } -extern "C" __global__ void __raygen__kernel_optix_background() +extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_subsurface() { - KernelGlobals kg; - const ShaderParams &p = __params.shader; - kernel_background_evaluate(&kg, p.input, p.output, p.sx + optixGetLaunchIndex().x); + const int global_index = optixGetLaunchIndex().x; + const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] : + global_index; + integrator_intersect_subsurface(nullptr, path_index); +} + +extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_volume_stack() +{ + const int global_index = optixGetLaunchIndex().x; + const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] : + global_index; + integrator_intersect_volume_stack(nullptr, path_index); } extern "C" __global__ void __miss__kernel_optix_miss() @@ -179,54 +169,91 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit() extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() { #ifdef __SHADOW_RECORD_ALL__ + bool ignore_intersection = false; + const uint prim = optixGetPrimitiveIndex(); # ifdef __VISIBILITY_FLAG__ const uint visibility = optixGetPayload_4(); if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) { - return optixIgnoreIntersection(); + ignore_intersection = true; } # endif - // Offset into array with num_hits - Intersection *const isect = get_payload_ptr_0() + optixGetPayload_2(); - isect->t = optixGetRayTmax(); - isect->prim = prim; - isect->object = get_object_id(); - isect->type = kernel_tex_fetch(__prim_type, prim); - + float u = 0.0f, v = 0.0f; if (optixIsTriangleHit()) { const float2 barycentrics = optixGetTriangleBarycentrics(); - isect->u = 1.0f - barycentrics.y - barycentrics.x; - isect->v = barycentrics.x; + u = 1.0f - barycentrics.y - barycentrics.x; + v = barycentrics.x; } # ifdef __HAIR__ else { - const float u = __uint_as_float(optixGetAttribute_0()); - isect->u = u; - isect->v = __uint_as_float(optixGetAttribute_1()); + u = __uint_as_float(optixGetAttribute_0()); + v = __uint_as_float(optixGetAttribute_1()); // Filter out curve endcaps if (u == 0.0f || u == 1.0f) { - return optixIgnoreIntersection(); + ignore_intersection = true; } } # endif -# ifdef __TRANSPARENT_SHADOWS__ - // Detect if this surface has a shader with transparent shadows - if (!shader_transparent_shadow(NULL, isect) || optixGetPayload_2() >= optixGetPayload_3()) { -# endif - // This is an opaque hit or the hit limit has been reached, abort traversal - optixSetPayload_5(true); - return optixTerminateRay(); -# ifdef __TRANSPARENT_SHADOWS__ + int num_hits = optixGetPayload_2(); + int record_index = num_hits; + const int max_hits = optixGetPayload_3(); + + if (!ignore_intersection) { + optixSetPayload_2(num_hits + 1); } - optixSetPayload_2(optixGetPayload_2() + 1); // num_hits++ + Intersection *const isect_array = get_payload_ptr_0(); + +# ifdef __TRANSPARENT_SHADOWS__ + if (num_hits >= max_hits) { + /* If maximum number of hits reached, find a hit to replace. */ + const int num_recorded_hits = min(max_hits, num_hits); + float max_recorded_t = isect_array[0].t; + int max_recorded_hit = 0; + + for (int i = 1; i < num_recorded_hits; i++) { + if (isect_array[i].t > max_recorded_t) { + max_recorded_t = isect_array[i].t; + max_recorded_hit = i; + } + } + + if (optixGetRayTmax() >= max_recorded_t) { + /* Accept hit, so that OptiX won't consider any more hits beyond the distance of the current + * hit anymore. */ + return; + } + + record_index = max_recorded_hit; + } +# endif + + if (!ignore_intersection) { + Intersection *const isect = isect_array + record_index; + isect->u = u; + isect->v = v; + isect->t = optixGetRayTmax(); + isect->prim = prim; + isect->object = get_object_id(); + isect->type = kernel_tex_fetch(__prim_type, prim); + +# ifdef __TRANSPARENT_SHADOWS__ + // Detect if this surface has a shader with transparent shadows + if (!shader_transparent_shadow(NULL, isect) || max_hits == 0) { +# endif + // If no transparent shadows, all light is blocked and we can stop immediately + optixSetPayload_5(true); + return optixTerminateRay(); +# ifdef __TRANSPARENT_SHADOWS__ + } +# endif + } // Continue tracing optixIgnoreIntersection(); -# endif #endif } @@ -300,7 +327,7 @@ ccl_device_inline void optix_intersection_curve(const uint prim, const uint type if (isect.t != FLT_MAX) isect.t *= len; - if (curve_intersect(NULL, &isect, P, dir, visibility, object, prim, time, type)) { + if (curve_intersect(NULL, &isect, P, dir, isect.t, visibility, object, prim, time, type)) { optixReportIntersection(isect.t / len, type & PRIMITIVE_ALL, __float_as_int(isect.u), // Attribute_0 @@ -317,11 +344,4 @@ extern "C" __global__ void __intersection__curve_ribbon() optix_intersection_curve(prim, type); } } - -extern "C" __global__ void __intersection__curve_all() -{ - const uint prim = optixGetPrimitiveIndex(); - const uint type = kernel_tex_fetch(__prim_type, prim); - optix_intersection_curve(prim, type); -} #endif diff --git a/intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu b/intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu new file mode 100644 index 00000000000..bf787e29eaa --- /dev/null +++ b/intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu @@ -0,0 +1,29 @@ +/* + * Copyright 2021, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Copy of the regular kernels with additional shader ray-tracing kernel that takes + * much longer to compiler. This is only loaded when needed by the scene. */ + +#include "kernel/device/optix/kernel.cu" +#include "kernel/integrator/integrator_shade_surface.h" + +extern "C" __global__ void __raygen__kernel_optix_integrator_shade_surface_raytrace() +{ + const int global_index = optixGetLaunchIndex().x; + const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] : + global_index; + integrator_shade_surface_raytrace(nullptr, path_index, __params.render_buffer); +} diff --git a/intern/cycles/kernel/filter/filter.h b/intern/cycles/kernel/filter/filter.h deleted file mode 100644 index b067e53a8bf..00000000000 --- a/intern/cycles/kernel/filter/filter.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __FILTER_H__ -#define __FILTER_H__ - -/* CPU Filter Kernel Interface */ - -#include "util/util_types.h" - -#include "kernel/filter/filter_defines.h" - -CCL_NAMESPACE_BEGIN - -#define KERNEL_NAME_JOIN(x, y, z) x##_##y##_##z -#define KERNEL_NAME_EVAL(arch, name) KERNEL_NAME_JOIN(kernel, arch, name) -#define KERNEL_FUNCTION_FULL_NAME(name) KERNEL_NAME_EVAL(KERNEL_ARCH, name) - -#define KERNEL_ARCH cpu -#include "kernel/kernels/cpu/filter_cpu.h" - -#define KERNEL_ARCH cpu_sse2 -#include "kernel/kernels/cpu/filter_cpu.h" - -#define KERNEL_ARCH cpu_sse3 -#include "kernel/kernels/cpu/filter_cpu.h" - -#define KERNEL_ARCH cpu_sse41 -#include "kernel/kernels/cpu/filter_cpu.h" - -#define KERNEL_ARCH cpu_avx -#include "kernel/kernels/cpu/filter_cpu.h" - -#define KERNEL_ARCH cpu_avx2 -#include "kernel/kernels/cpu/filter_cpu.h" - -CCL_NAMESPACE_END - -#endif /* __FILTER_H__ */ diff --git a/intern/cycles/kernel/filter/filter_defines.h b/intern/cycles/kernel/filter/filter_defines.h deleted file mode 100644 index 1c0ac5e2cb7..00000000000 --- a/intern/cycles/kernel/filter/filter_defines.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __FILTER_DEFINES_H__ -#define __FILTER_DEFINES_H__ - -#define DENOISE_FEATURES 11 -#define TRANSFORM_SIZE (DENOISE_FEATURES * DENOISE_FEATURES) -#define XTWX_SIZE (((DENOISE_FEATURES + 1) * (DENOISE_FEATURES + 2)) / 2) -#define XTWY_SIZE (DENOISE_FEATURES + 1) - -#define DENOISE_MAX_FRAMES 16 - -typedef struct TileInfo { - int offsets[9]; - int strides[9]; - int x[4]; - int y[4]; - int from_render; - int frames[DENOISE_MAX_FRAMES]; - int num_frames; - /* TODO(lukas): CUDA doesn't have uint64_t... */ -#ifdef __KERNEL_OPENCL__ - ccl_global float *buffers[9]; -#else - long long int buffers[9]; -#endif -} TileInfo; - -#ifdef __KERNEL_OPENCL__ -# define CCL_FILTER_TILE_INFO \ - ccl_global TileInfo *tile_info, ccl_global float *tile_buffer_1, \ - ccl_global float *tile_buffer_2, ccl_global float *tile_buffer_3, \ - ccl_global float *tile_buffer_4, ccl_global float *tile_buffer_5, \ - ccl_global float *tile_buffer_6, ccl_global float *tile_buffer_7, \ - ccl_global float *tile_buffer_8, ccl_global float *tile_buffer_9 -# define CCL_FILTER_TILE_INFO_ARG \ - tile_info, tile_buffer_1, tile_buffer_2, tile_buffer_3, tile_buffer_4, tile_buffer_5, \ - tile_buffer_6, tile_buffer_7, tile_buffer_8, tile_buffer_9 -# define ccl_get_tile_buffer(id) \ - (id == 0 ? tile_buffer_1 : \ - id == 1 ? tile_buffer_2 : \ - id == 2 ? tile_buffer_3 : \ - id == 3 ? tile_buffer_4 : \ - id == 4 ? tile_buffer_5 : \ - id == 5 ? tile_buffer_6 : \ - id == 6 ? tile_buffer_7 : \ - id == 7 ? tile_buffer_8 : \ - tile_buffer_9) -#else -# ifdef __KERNEL_CUDA__ -# define CCL_FILTER_TILE_INFO ccl_global TileInfo *tile_info -# else -# define CCL_FILTER_TILE_INFO TileInfo *tile_info -# endif -# define ccl_get_tile_buffer(id) (tile_info->buffers[id]) -#endif - -#endif /* __FILTER_DEFINES_H__*/ diff --git a/intern/cycles/kernel/filter/filter_features.h b/intern/cycles/kernel/filter/filter_features.h deleted file mode 100644 index 8a2af957146..00000000000 --- a/intern/cycles/kernel/filter/filter_features.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#define ccl_get_feature(buffer, pass) (buffer)[(pass)*pass_stride] - -/* Loop over the pixels in the range [low.x, high.x) x [low.y, high.y).+ * pixel_buffer always - * points to the current pixel in the first pass. Repeat the loop for every secondary frame if - * there are any. */ -#define FOR_PIXEL_WINDOW \ - for (int frame = 0; frame < tile_info->num_frames; frame++) { \ - pixel.z = tile_info->frames[frame]; \ - pixel_buffer = buffer + (low.y - rect.y) * buffer_w + (low.x - rect.x) + \ - frame * frame_stride; \ - for (pixel.y = low.y; pixel.y < high.y; pixel.y++) { \ - for (pixel.x = low.x; pixel.x < high.x; pixel.x++, pixel_buffer++) { - -#define END_FOR_PIXEL_WINDOW \ - } \ - pixel_buffer += buffer_w - (high.x - low.x); \ - } \ - } - -ccl_device_inline void filter_get_features(int3 pixel, - const ccl_global float *ccl_restrict buffer, - float *features, - bool use_time, - const float *ccl_restrict mean, - int pass_stride) -{ - features[0] = pixel.x; - features[1] = pixel.y; - features[2] = fabsf(ccl_get_feature(buffer, 0)); - features[3] = ccl_get_feature(buffer, 1); - features[4] = ccl_get_feature(buffer, 2); - features[5] = ccl_get_feature(buffer, 3); - features[6] = ccl_get_feature(buffer, 4); - features[7] = ccl_get_feature(buffer, 5); - features[8] = ccl_get_feature(buffer, 6); - features[9] = ccl_get_feature(buffer, 7); - if (use_time) { - features[10] = pixel.z; - } - if (mean) { - for (int i = 0; i < (use_time ? 11 : 10); i++) { - features[i] -= mean[i]; - } - } -} - -ccl_device_inline void filter_get_feature_scales(int3 pixel, - const ccl_global float *ccl_restrict buffer, - float *scales, - bool use_time, - const float *ccl_restrict mean, - int pass_stride) -{ - scales[0] = fabsf(pixel.x - mean[0]); - scales[1] = fabsf(pixel.y - mean[1]); - scales[2] = fabsf(fabsf(ccl_get_feature(buffer, 0)) - mean[2]); - scales[3] = len_squared(make_float3(ccl_get_feature(buffer, 1) - mean[3], - ccl_get_feature(buffer, 2) - mean[4], - ccl_get_feature(buffer, 3) - mean[5])); - scales[4] = fabsf(ccl_get_feature(buffer, 4) - mean[6]); - scales[5] = len_squared(make_float3(ccl_get_feature(buffer, 5) - mean[7], - ccl_get_feature(buffer, 6) - mean[8], - ccl_get_feature(buffer, 7) - mean[9])); - if (use_time) { - scales[6] = fabsf(pixel.z - mean[10]); - } -} - -ccl_device_inline void filter_calculate_scale(float *scale, bool use_time) -{ - scale[0] = 1.0f / max(scale[0], 0.01f); - scale[1] = 1.0f / max(scale[1], 0.01f); - scale[2] = 1.0f / max(scale[2], 0.01f); - if (use_time) { - scale[10] = 1.0f / max(scale[6], 0.01f); - } - scale[6] = 1.0f / max(scale[4], 0.01f); - scale[7] = scale[8] = scale[9] = 1.0f / max(sqrtf(scale[5]), 0.01f); - scale[3] = scale[4] = scale[5] = 1.0f / max(sqrtf(scale[3]), 0.01f); -} - -ccl_device_inline float3 filter_get_color(const ccl_global float *ccl_restrict buffer, - int pass_stride) -{ - return make_float3( - ccl_get_feature(buffer, 8), ccl_get_feature(buffer, 9), ccl_get_feature(buffer, 10)); -} - -ccl_device_inline void design_row_add(float *design_row, - int rank, - const ccl_global float *ccl_restrict transform, - int stride, - int row, - float feature, - int transform_row_stride) -{ - for (int i = 0; i < rank; i++) { - design_row[1 + i] += transform[(row * transform_row_stride + i) * stride] * feature; - } -} - -/* Fill the design row. */ -ccl_device_inline void filter_get_design_row_transform( - int3 p_pixel, - const ccl_global float *ccl_restrict p_buffer, - int3 q_pixel, - const ccl_global float *ccl_restrict q_buffer, - int pass_stride, - int rank, - float *design_row, - const ccl_global float *ccl_restrict transform, - int stride, - bool use_time) -{ - int num_features = use_time ? 11 : 10; - - design_row[0] = 1.0f; - math_vector_zero(design_row + 1, rank); - -#define DESIGN_ROW_ADD(I, F) \ - design_row_add(design_row, rank, transform, stride, I, F, num_features); - DESIGN_ROW_ADD(0, q_pixel.x - p_pixel.x); - DESIGN_ROW_ADD(1, q_pixel.y - p_pixel.y); - DESIGN_ROW_ADD(2, fabsf(ccl_get_feature(q_buffer, 0)) - fabsf(ccl_get_feature(p_buffer, 0))); - DESIGN_ROW_ADD(3, ccl_get_feature(q_buffer, 1) - ccl_get_feature(p_buffer, 1)); - DESIGN_ROW_ADD(4, ccl_get_feature(q_buffer, 2) - ccl_get_feature(p_buffer, 2)); - DESIGN_ROW_ADD(5, ccl_get_feature(q_buffer, 3) - ccl_get_feature(p_buffer, 3)); - DESIGN_ROW_ADD(6, ccl_get_feature(q_buffer, 4) - ccl_get_feature(p_buffer, 4)); - DESIGN_ROW_ADD(7, ccl_get_feature(q_buffer, 5) - ccl_get_feature(p_buffer, 5)); - DESIGN_ROW_ADD(8, ccl_get_feature(q_buffer, 6) - ccl_get_feature(p_buffer, 6)); - DESIGN_ROW_ADD(9, ccl_get_feature(q_buffer, 7) - ccl_get_feature(p_buffer, 7)); - if (use_time) { - DESIGN_ROW_ADD(10, q_pixel.z - p_pixel.z) - } -#undef DESIGN_ROW_ADD -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_features_sse.h b/intern/cycles/kernel/filter/filter_features_sse.h deleted file mode 100644 index 59d4ace2bef..00000000000 --- a/intern/cycles/kernel/filter/filter_features_sse.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#define ccl_get_feature_sse(pass) load_float4(buffer + (pass)*pass_stride) - -/* Loop over the pixels in the range [low.x, high.x) x [low.y, high.y), 4 at a time. - * pixel_buffer always points to the first of the 4 current pixel in the first pass. - * x4 and y4 contain the coordinates of the four pixels, active_pixels contains a mask that's set - * for all pixels within the window. Repeat the loop for every secondary frame if there are any. */ -#define FOR_PIXEL_WINDOW_SSE \ - for (int frame = 0; frame < tile_info->num_frames; frame++) { \ - pixel.z = tile_info->frames[frame]; \ - pixel_buffer = buffer + (low.y - rect.y) * buffer_w + (low.x - rect.x) + \ - frame * frame_stride; \ - float4 t4 = make_float4(pixel.z); \ - for (pixel.y = low.y; pixel.y < high.y; pixel.y++) { \ - float4 y4 = make_float4(pixel.y); \ - for (pixel.x = low.x; pixel.x < high.x; pixel.x += 4, pixel_buffer += 4) { \ - float4 x4 = make_float4(pixel.x) + make_float4(0.0f, 1.0f, 2.0f, 3.0f); \ - int4 active_pixels = x4 < make_float4(high.x); - -#define END_FOR_PIXEL_WINDOW_SSE \ - } \ - pixel_buffer += buffer_w - (high.x - low.x); \ - } \ - } - -ccl_device_inline void filter_get_features_sse(float4 x, - float4 y, - float4 t, - int4 active_pixels, - const float *ccl_restrict buffer, - float4 *features, - bool use_time, - const float4 *ccl_restrict mean, - int pass_stride) -{ - int num_features = use_time ? 11 : 10; - - features[0] = x; - features[1] = y; - features[2] = fabs(ccl_get_feature_sse(0)); - features[3] = ccl_get_feature_sse(1); - features[4] = ccl_get_feature_sse(2); - features[5] = ccl_get_feature_sse(3); - features[6] = ccl_get_feature_sse(4); - features[7] = ccl_get_feature_sse(5); - features[8] = ccl_get_feature_sse(6); - features[9] = ccl_get_feature_sse(7); - if (use_time) { - features[10] = t; - } - - if (mean) { - for (int i = 0; i < num_features; i++) { - features[i] = features[i] - mean[i]; - } - } - for (int i = 0; i < num_features; i++) { - features[i] = mask(active_pixels, features[i]); - } -} - -ccl_device_inline void filter_get_feature_scales_sse(float4 x, - float4 y, - float4 t, - int4 active_pixels, - const float *ccl_restrict buffer, - float4 *scales, - bool use_time, - const float4 *ccl_restrict mean, - int pass_stride) -{ - scales[0] = fabs(x - mean[0]); - scales[1] = fabs(y - mean[1]); - scales[2] = fabs(fabs(ccl_get_feature_sse(0)) - mean[2]); - scales[3] = sqr(ccl_get_feature_sse(1) - mean[3]) + sqr(ccl_get_feature_sse(2) - mean[4]) + - sqr(ccl_get_feature_sse(3) - mean[5]); - scales[4] = fabs(ccl_get_feature_sse(4) - mean[6]); - scales[5] = sqr(ccl_get_feature_sse(5) - mean[7]) + sqr(ccl_get_feature_sse(6) - mean[8]) + - sqr(ccl_get_feature_sse(7) - mean[9]); - if (use_time) { - scales[6] = fabs(t - mean[10]); - } - - for (int i = 0; i < (use_time ? 7 : 6); i++) - scales[i] = mask(active_pixels, scales[i]); -} - -ccl_device_inline void filter_calculate_scale_sse(float4 *scale, bool use_time) -{ - scale[0] = rcp(max(reduce_max(scale[0]), make_float4(0.01f))); - scale[1] = rcp(max(reduce_max(scale[1]), make_float4(0.01f))); - scale[2] = rcp(max(reduce_max(scale[2]), make_float4(0.01f))); - if (use_time) { - scale[10] = rcp(max(reduce_max(scale[6]), make_float4(0.01f))); - } - scale[6] = rcp(max(reduce_max(scale[4]), make_float4(0.01f))); - scale[7] = scale[8] = scale[9] = rcp(max(reduce_max(sqrt(scale[5])), make_float4(0.01f))); - scale[3] = scale[4] = scale[5] = rcp(max(reduce_max(sqrt(scale[3])), make_float4(0.01f))); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_kernel.h b/intern/cycles/kernel/filter/filter_kernel.h deleted file mode 100644 index 2ef03dc0a02..00000000000 --- a/intern/cycles/kernel/filter/filter_kernel.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/util_color.h" -#include "util/util_math.h" -#include "util/util_math_fast.h" -#include "util/util_texture.h" - -#include "util/util_atomic.h" -#include "util/util_math_matrix.h" - -#include "kernel/filter/filter_defines.h" - -#include "kernel/filter/filter_features.h" -#ifdef __KERNEL_SSE3__ -# include "kernel/filter/filter_features_sse.h" -#endif - -#include "kernel/filter/filter_prefilter.h" - -#ifdef __KERNEL_GPU__ -# include "kernel/filter/filter_transform_gpu.h" -#else -# ifdef __KERNEL_SSE3__ -# include "kernel/filter/filter_transform_sse.h" -# else -# include "kernel/filter/filter_transform.h" -# endif -#endif - -#include "kernel/filter/filter_reconstruction.h" - -#ifdef __KERNEL_CPU__ -# include "kernel/filter/filter_nlm_cpu.h" -#else -# include "kernel/filter/filter_nlm_gpu.h" -#endif diff --git a/intern/cycles/kernel/filter/filter_nlm_cpu.h b/intern/cycles/kernel/filter/filter_nlm_cpu.h deleted file mode 100644 index 24200c29203..00000000000 --- a/intern/cycles/kernel/filter/filter_nlm_cpu.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#define load4_a(buf, ofs) (*((float4 *)((buf) + (ofs)))) -#define load4_u(buf, ofs) load_float4((buf) + (ofs)) - -ccl_device_inline void kernel_filter_nlm_calc_difference(int dx, - int dy, - const float *ccl_restrict weight_image, - const float *ccl_restrict variance_image, - const float *ccl_restrict scale_image, - float *difference_image, - int4 rect, - int stride, - int channel_offset, - int frame_offset, - float a, - float k_2) -{ - /* Strides need to be aligned to 16 bytes. */ - kernel_assert((stride % 4) == 0 && (channel_offset % 4) == 0); - - int aligned_lowx = rect.x & (~3); - const int numChannels = (channel_offset > 0) ? 3 : 1; - const float4 channel_fac = make_float4(1.0f / numChannels); - - for (int y = rect.y; y < rect.w; y++) { - int idx_p = y * stride + aligned_lowx; - int idx_q = (y + dy) * stride + aligned_lowx + dx + frame_offset; - for (int x = aligned_lowx; x < rect.z; x += 4, idx_p += 4, idx_q += 4) { - float4 diff = make_float4(0.0f); - float4 scale_fac; - if (scale_image) { - scale_fac = clamp(load4_a(scale_image, idx_p) / load4_u(scale_image, idx_q), - make_float4(0.25f), - make_float4(4.0f)); - } - else { - scale_fac = make_float4(1.0f); - } - for (int c = 0, chan_ofs = 0; c < numChannels; c++, chan_ofs += channel_offset) { - /* idx_p is guaranteed to be aligned, but idx_q isn't. */ - float4 color_p = load4_a(weight_image, idx_p + chan_ofs); - float4 color_q = scale_fac * load4_u(weight_image, idx_q + chan_ofs); - float4 cdiff = color_p - color_q; - float4 var_p = load4_a(variance_image, idx_p + chan_ofs); - float4 var_q = sqr(scale_fac) * load4_u(variance_image, idx_q + chan_ofs); - diff += (cdiff * cdiff - a * (var_p + min(var_p, var_q))) / - (make_float4(1e-8f) + k_2 * (var_p + var_q)); - } - load4_a(difference_image, idx_p) = diff * channel_fac; - } - } -} - -ccl_device_inline void kernel_filter_nlm_blur( - const float *ccl_restrict difference_image, float *out_image, int4 rect, int stride, int f) -{ - int aligned_lowx = round_down(rect.x, 4); - for (int y = rect.y; y < rect.w; y++) { - const int low = max(rect.y, y - f); - const int high = min(rect.w, y + f + 1); - for (int x = aligned_lowx; x < rect.z; x += 4) { - load4_a(out_image, y * stride + x) = make_float4(0.0f); - } - for (int y1 = low; y1 < high; y1++) { - for (int x = aligned_lowx; x < rect.z; x += 4) { - load4_a(out_image, y * stride + x) += load4_a(difference_image, y1 * stride + x); - } - } - float fac = 1.0f / (high - low); - for (int x = aligned_lowx; x < rect.z; x += 4) { - load4_a(out_image, y * stride + x) *= fac; - } - } -} - -ccl_device_inline void nlm_blur_horizontal( - const float *ccl_restrict difference_image, float *out_image, int4 rect, int stride, int f) -{ - int aligned_lowx = round_down(rect.x, 4); - for (int y = rect.y; y < rect.w; y++) { - for (int x = aligned_lowx; x < rect.z; x += 4) { - load4_a(out_image, y * stride + x) = make_float4(0.0f); - } - } - - for (int dx = -f; dx <= f; dx++) { - aligned_lowx = round_down(rect.x - min(0, dx), 4); - int highx = rect.z - max(0, dx); - int4 lowx4 = make_int4(rect.x - min(0, dx)); - int4 highx4 = make_int4(rect.z - max(0, dx)); - for (int y = rect.y; y < rect.w; y++) { - for (int x = aligned_lowx; x < highx; x += 4) { - int4 x4 = make_int4(x) + make_int4(0, 1, 2, 3); - int4 active = (x4 >= lowx4) & (x4 < highx4); - - float4 diff = load4_u(difference_image, y * stride + x + dx); - load4_a(out_image, y * stride + x) += mask(active, diff); - } - } - } - - aligned_lowx = round_down(rect.x, 4); - for (int y = rect.y; y < rect.w; y++) { - for (int x = aligned_lowx; x < rect.z; x += 4) { - float4 x4 = make_float4(x) + make_float4(0.0f, 1.0f, 2.0f, 3.0f); - float4 low = max(make_float4(rect.x), x4 - make_float4(f)); - float4 high = min(make_float4(rect.z), x4 + make_float4(f + 1)); - load4_a(out_image, y * stride + x) *= rcp(high - low); - } - } -} - -ccl_device_inline void kernel_filter_nlm_calc_weight( - const float *ccl_restrict difference_image, float *out_image, int4 rect, int stride, int f) -{ - nlm_blur_horizontal(difference_image, out_image, rect, stride, f); - - int aligned_lowx = round_down(rect.x, 4); - for (int y = rect.y; y < rect.w; y++) { - for (int x = aligned_lowx; x < rect.z; x += 4) { - load4_a(out_image, y * stride + x) = fast_expf4( - -max(load4_a(out_image, y * stride + x), make_float4(0.0f))); - } - } -} - -ccl_device_inline void kernel_filter_nlm_update_output(int dx, - int dy, - const float *ccl_restrict difference_image, - const float *ccl_restrict image, - float *temp_image, - float *out_image, - float *accum_image, - int4 rect, - int channel_offset, - int stride, - int f) -{ - nlm_blur_horizontal(difference_image, temp_image, rect, stride, f); - - int aligned_lowx = round_down(rect.x, 4); - for (int y = rect.y; y < rect.w; y++) { - for (int x = aligned_lowx; x < rect.z; x += 4) { - int4 x4 = make_int4(x) + make_int4(0, 1, 2, 3); - int4 active = (x4 >= make_int4(rect.x)) & (x4 < make_int4(rect.z)); - - int idx_p = y * stride + x, idx_q = (y + dy) * stride + (x + dx); - - float4 weight = load4_a(temp_image, idx_p); - load4_a(accum_image, idx_p) += mask(active, weight); - - float4 val = load4_u(image, idx_q); - if (channel_offset) { - val += load4_u(image, idx_q + channel_offset); - val += load4_u(image, idx_q + 2 * channel_offset); - val *= 1.0f / 3.0f; - } - - load4_a(out_image, idx_p) += mask(active, weight * val); - } - } -} - -ccl_device_inline void kernel_filter_nlm_construct_gramian(int dx, - int dy, - int t, - const float *ccl_restrict - difference_image, - const float *ccl_restrict buffer, - float *transform, - int *rank, - float *XtWX, - float3 *XtWY, - int4 rect, - int4 filter_window, - int stride, - int f, - int pass_stride, - int frame_offset, - bool use_time) -{ - int4 clip_area = rect_clip(rect, filter_window); - /* fy and fy are in filter-window-relative coordinates, - * while x and y are in feature-window-relative coordinates. */ - for (int y = clip_area.y; y < clip_area.w; y++) { - for (int x = clip_area.x; x < clip_area.z; x++) { - const int low = max(rect.x, x - f); - const int high = min(rect.z, x + f + 1); - float sum = 0.0f; - for (int x1 = low; x1 < high; x1++) { - sum += difference_image[y * stride + x1]; - } - float weight = sum * (1.0f / (high - low)); - - int storage_ofs = coord_to_local_index(filter_window, x, y); - float *l_transform = transform + storage_ofs * TRANSFORM_SIZE; - float *l_XtWX = XtWX + storage_ofs * XTWX_SIZE; - float3 *l_XtWY = XtWY + storage_ofs * XTWY_SIZE; - int *l_rank = rank + storage_ofs; - - kernel_filter_construct_gramian(x, - y, - 1, - dx, - dy, - t, - stride, - pass_stride, - frame_offset, - use_time, - buffer, - l_transform, - l_rank, - weight, - l_XtWX, - l_XtWY, - 0); - } - } -} - -ccl_device_inline void kernel_filter_nlm_normalize(float *out_image, - const float *ccl_restrict accum_image, - int4 rect, - int w) -{ - for (int y = rect.y; y < rect.w; y++) { - for (int x = rect.x; x < rect.z; x++) { - out_image[y * w + x] /= accum_image[y * w + x]; - } - } -} - -#undef load4_a -#undef load4_u - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_nlm_gpu.h b/intern/cycles/kernel/filter/filter_nlm_gpu.h deleted file mode 100644 index 650c743f34f..00000000000 --- a/intern/cycles/kernel/filter/filter_nlm_gpu.h +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* Determines pixel coordinates and offset for the current thread. - * Returns whether the thread should do any work. - * - * All coordinates are relative to the denoising buffer! - * - * Window is the rect that should be processed. - * co is filled with (x, y, dx, dy). - */ -ccl_device_inline bool get_nlm_coords_window( - int w, int h, int r, int stride, int4 *rect, int4 *co, int *ofs, int4 window) -{ - /* Determine the pixel offset that this thread should apply. */ - int s = 2 * r + 1; - int si = ccl_global_id(1); - int sx = si % s; - int sy = si / s; - if (sy >= s) { - return false; - } - - /* Pixels still need to lie inside the denoising buffer after applying the offset, - * so determine the area for which this is the case. */ - int dx = sx - r; - int dy = sy - r; - - *rect = make_int4(max(0, -dx), max(0, -dy), w - max(0, dx), h - max(0, dy)); - - /* Find the intersection of the area that we want to process (window) and the area - * that can be processed (rect) to get the final area for this offset. */ - int4 clip_area = rect_clip(window, *rect); - - /* If the radius is larger than one of the sides of the window, - * there will be shifts for which there is no usable pixel at all. */ - if (!rect_is_valid(clip_area)) { - return false; - } - - /* Map the linear thread index to pixels inside the clip area. */ - int x, y; - if (!local_index_to_coord(clip_area, ccl_global_id(0), &x, &y)) { - return false; - } - - *co = make_int4(x, y, dx, dy); - - *ofs = (sy * s + sx) * stride; - - return true; -} - -ccl_device_inline bool get_nlm_coords( - int w, int h, int r, int stride, int4 *rect, int4 *co, int *ofs) -{ - return get_nlm_coords_window(w, h, r, stride, rect, co, ofs, make_int4(0, 0, w, h)); -} - -ccl_device_inline void kernel_filter_nlm_calc_difference( - int x, - int y, - int dx, - int dy, - const ccl_global float *ccl_restrict weight_image, - const ccl_global float *ccl_restrict variance_image, - const ccl_global float *ccl_restrict scale_image, - ccl_global float *difference_image, - int4 rect, - int stride, - int channel_offset, - int frame_offset, - float a, - float k_2) -{ - int idx_p = y * stride + x, idx_q = (y + dy) * stride + (x + dx) + frame_offset; - int numChannels = channel_offset ? 3 : 1; - - float diff = 0.0f; - float scale_fac = 1.0f; - if (scale_image) { - scale_fac = clamp(scale_image[idx_p] / scale_image[idx_q], 0.25f, 4.0f); - } - - for (int c = 0; c < numChannels; c++, idx_p += channel_offset, idx_q += channel_offset) { - float cdiff = weight_image[idx_p] - scale_fac * weight_image[idx_q]; - float pvar = variance_image[idx_p]; - float qvar = sqr(scale_fac) * variance_image[idx_q]; - diff += (cdiff * cdiff - a * (pvar + min(pvar, qvar))) / (1e-8f + k_2 * (pvar + qvar)); - } - if (numChannels > 1) { - diff *= 1.0f / numChannels; - } - difference_image[y * stride + x] = diff; -} - -ccl_device_inline void kernel_filter_nlm_blur(int x, - int y, - const ccl_global float *ccl_restrict - difference_image, - ccl_global float *out_image, - int4 rect, - int stride, - int f) -{ - float sum = 0.0f; - const int low = max(rect.y, y - f); - const int high = min(rect.w, y + f + 1); - for (int y1 = low; y1 < high; y1++) { - sum += difference_image[y1 * stride + x]; - } - sum *= 1.0f / (high - low); - out_image[y * stride + x] = sum; -} - -ccl_device_inline void kernel_filter_nlm_calc_weight(int x, - int y, - const ccl_global float *ccl_restrict - difference_image, - ccl_global float *out_image, - int4 rect, - int stride, - int f) -{ - float sum = 0.0f; - const int low = max(rect.x, x - f); - const int high = min(rect.z, x + f + 1); - for (int x1 = low; x1 < high; x1++) { - sum += difference_image[y * stride + x1]; - } - sum *= 1.0f / (high - low); - out_image[y * stride + x] = fast_expf(-max(sum, 0.0f)); -} - -ccl_device_inline void kernel_filter_nlm_update_output(int x, - int y, - int dx, - int dy, - const ccl_global float *ccl_restrict - difference_image, - const ccl_global float *ccl_restrict image, - ccl_global float *out_image, - ccl_global float *accum_image, - int4 rect, - int channel_offset, - int stride, - int f) -{ - float sum = 0.0f; - const int low = max(rect.x, x - f); - const int high = min(rect.z, x + f + 1); - for (int x1 = low; x1 < high; x1++) { - sum += difference_image[y * stride + x1]; - } - sum *= 1.0f / (high - low); - - int idx_p = y * stride + x, idx_q = (y + dy) * stride + (x + dx); - if (out_image) { - atomic_add_and_fetch_float(accum_image + idx_p, sum); - - float val = image[idx_q]; - if (channel_offset) { - val += image[idx_q + channel_offset]; - val += image[idx_q + 2 * channel_offset]; - val *= 1.0f / 3.0f; - } - atomic_add_and_fetch_float(out_image + idx_p, sum * val); - } - else { - accum_image[idx_p] = sum; - } -} - -ccl_device_inline void kernel_filter_nlm_construct_gramian( - int x, - int y, - int dx, - int dy, - int t, - const ccl_global float *ccl_restrict difference_image, - const ccl_global float *ccl_restrict buffer, - const ccl_global float *ccl_restrict transform, - ccl_global int *rank, - ccl_global float *XtWX, - ccl_global float3 *XtWY, - int4 rect, - int4 filter_window, - int stride, - int f, - int pass_stride, - int frame_offset, - bool use_time, - int localIdx) -{ - const int low = max(rect.x, x - f); - const int high = min(rect.z, x + f + 1); - float sum = 0.0f; - for (int x1 = low; x1 < high; x1++) { - sum += difference_image[y * stride + x1]; - } - float weight = sum * (1.0f / (high - low)); - - /* Reconstruction data is only stored for pixels inside the filter window, - * so compute the pixels's index in there. */ - int storage_ofs = coord_to_local_index(filter_window, x, y); - transform += storage_ofs; - rank += storage_ofs; - XtWX += storage_ofs; - XtWY += storage_ofs; - - kernel_filter_construct_gramian(x, - y, - rect_size(filter_window), - dx, - dy, - t, - stride, - pass_stride, - frame_offset, - use_time, - buffer, - transform, - rank, - weight, - XtWX, - XtWY, - localIdx); -} - -ccl_device_inline void kernel_filter_nlm_normalize(int x, - int y, - ccl_global float *out_image, - const ccl_global float *ccl_restrict - accum_image, - int stride) -{ - out_image[y * stride + x] /= accum_image[y * stride + x]; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_prefilter.h b/intern/cycles/kernel/filter/filter_prefilter.h deleted file mode 100644 index 97cecba190e..00000000000 --- a/intern/cycles/kernel/filter/filter_prefilter.h +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/** - * First step of the shadow prefiltering, performs the shadow division and stores all data - * in a nice and easy rectangular array that can be passed to the NLM filter. - * - * Calculates: - * \param unfiltered: Contains the two half images of the shadow feature pass - * \param sampleVariance: The sample-based variance calculated in the kernel. - * Note: This calculation is biased in general, - * and especially here since the variance of the ratio can only be approximated. - * \param sampleVarianceV: Variance of the sample variance estimation, quite noisy - * (since it's essentially the buffer variance of the two variance halves) - * \param bufferVariance: The buffer-based variance of the shadow feature. - * Unbiased, but quite noisy. - */ -ccl_device void kernel_filter_divide_shadow(int sample, - CCL_FILTER_TILE_INFO, - int x, - int y, - ccl_global float *unfilteredA, - ccl_global float *unfilteredB, - ccl_global float *sampleVariance, - ccl_global float *sampleVarianceV, - ccl_global float *bufferVariance, - int4 rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int xtile = (x < tile_info->x[1]) ? 0 : ((x < tile_info->x[2]) ? 1 : 2); - int ytile = (y < tile_info->y[1]) ? 0 : ((y < tile_info->y[2]) ? 1 : 2); - int tile = ytile * 3 + xtile; - - int offset = tile_info->offsets[tile]; - int stride = tile_info->strides[tile]; - const ccl_global float *ccl_restrict center_buffer = (ccl_global float *)ccl_get_tile_buffer( - tile); - center_buffer += (y * stride + x + offset) * buffer_pass_stride; - center_buffer += buffer_denoising_offset + 14; - - int buffer_w = align_up(rect.z - rect.x, 4); - int idx = (y - rect.y) * buffer_w + (x - rect.x); - unfilteredA[idx] = center_buffer[1] / max(center_buffer[0], 1e-7f); - unfilteredB[idx] = center_buffer[4] / max(center_buffer[3], 1e-7f); - - float varA = center_buffer[2]; - float varB = center_buffer[5]; - int odd_sample = (sample + 1) / 2; - int even_sample = sample / 2; - - /* Approximate variance as E[x^2] - 1/N * (E[x])^2, since online variance - * update does not work efficiently with atomics in the kernel. */ - varA = max(0.0f, varA - unfilteredA[idx] * unfilteredA[idx] * odd_sample); - varB = max(0.0f, varB - unfilteredB[idx] * unfilteredB[idx] * even_sample); - - varA /= max(odd_sample - 1, 1); - varB /= max(even_sample - 1, 1); - - sampleVariance[idx] = 0.5f * (varA + varB) / sample; - sampleVarianceV[idx] = 0.5f * (varA - varB) * (varA - varB) / (sample * sample); - bufferVariance[idx] = 0.5f * (unfilteredA[idx] - unfilteredB[idx]) * - (unfilteredA[idx] - unfilteredB[idx]); -} - -/* Load a regular feature from the render buffers into the denoise buffer. - * Parameters: - * - sample: The sample amount in the buffer, used to normalize the buffer. - * - m_offset, v_offset: Render Buffer Pass offsets of mean and variance of the feature. - * - x, y: Current pixel - * - mean, variance: Target denoise buffers. - * - rect: The prefilter area (lower pixels inclusive, upper pixels exclusive). - */ -ccl_device void kernel_filter_get_feature(int sample, - CCL_FILTER_TILE_INFO, - int m_offset, - int v_offset, - int x, - int y, - ccl_global float *mean, - ccl_global float *variance, - float scale, - int4 rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int xtile = (x < tile_info->x[1]) ? 0 : ((x < tile_info->x[2]) ? 1 : 2); - int ytile = (y < tile_info->y[1]) ? 0 : ((y < tile_info->y[2]) ? 1 : 2); - int tile = ytile * 3 + xtile; - ccl_global float *center_buffer = ((ccl_global float *)ccl_get_tile_buffer(tile)) + - (tile_info->offsets[tile] + y * tile_info->strides[tile] + x) * - buffer_pass_stride + - buffer_denoising_offset; - - int buffer_w = align_up(rect.z - rect.x, 4); - int idx = (y - rect.y) * buffer_w + (x - rect.x); - - float val = scale * center_buffer[m_offset]; - mean[idx] = val; - - if (v_offset >= 0) { - if (sample > 1) { - /* Approximate variance as E[x^2] - 1/N * (E[x])^2, since online variance - * update does not work efficiently with atomics in the kernel. */ - variance[idx] = max( - 0.0f, (center_buffer[v_offset] - val * val * sample) / (sample * (sample - 1))); - } - else { - /* Can't compute variance with single sample, just set it very high. */ - variance[idx] = 1e10f; - } - } -} - -ccl_device void kernel_filter_write_feature(int sample, - int x, - int y, - int4 buffer_params, - ccl_global float *from, - ccl_global float *buffer, - int out_offset, - int4 rect) -{ - ccl_global float *combined_buffer = buffer + (y * buffer_params.y + x + buffer_params.x) * - buffer_params.z; - - int buffer_w = align_up(rect.z - rect.x, 4); - int idx = (y - rect.y) * buffer_w + (x - rect.x); - - combined_buffer[out_offset] = from[idx]; -} - -#define GET_COLOR(image) \ - make_float3(image[idx], image[idx + pass_stride], image[idx + 2 * pass_stride]) -#define SET_COLOR(image, color) \ - image[idx] = color.x; \ - image[idx + pass_stride] = color.y; \ - image[idx + 2 * pass_stride] = color.z - -ccl_device void kernel_filter_detect_outliers(int x, - int y, - ccl_global float *in, - ccl_global float *variance_out, - ccl_global float *depth, - ccl_global float *image_out, - int4 rect, - int pass_stride) -{ - int buffer_w = align_up(rect.z - rect.x, 4); - - ccl_global float *image_in = in; - ccl_global float *variance_in = in + 3 * pass_stride; - - int n = 0; - float values[25]; - float pixel_variance, max_variance = 0.0f; - for (int y1 = max(y - 2, rect.y); y1 < min(y + 3, rect.w); y1++) { - for (int x1 = max(x - 2, rect.x); x1 < min(x + 3, rect.z); x1++) { - int idx = (y1 - rect.y) * buffer_w + (x1 - rect.x); - float3 color = GET_COLOR(image_in); - color = max(color, make_float3(0.0f, 0.0f, 0.0f)); - float L = average(color); - - /* Find the position of L. */ - int i; - for (i = 0; i < n; i++) { - if (values[i] > L) - break; - } - /* Make space for L by shifting all following values to the right. */ - for (int j = n; j > i; j--) { - values[j] = values[j - 1]; - } - /* Insert L. */ - values[i] = L; - n++; - - float3 pixel_var = GET_COLOR(variance_in); - float var = average(pixel_var); - if ((x1 == x) && (y1 == y)) { - pixel_variance = (pixel_var.x < 0.0f || pixel_var.y < 0.0f || pixel_var.z < 0.0f) ? -1.0f : - var; - } - else { - max_variance = max(max_variance, var); - } - } - } - - max_variance += 1e-4f; - - int idx = (y - rect.y) * buffer_w + (x - rect.x); - - float3 color = GET_COLOR(image_in); - float3 variance = GET_COLOR(variance_in); - color = max(color, make_float3(0.0f, 0.0f, 0.0f)); - variance = max(variance, make_float3(0.0f, 0.0f, 0.0f)); - - float L = average(color); - - float ref = 2.0f * values[(int)(n * 0.75f)]; - - /* Slightly offset values to avoid false positives in (almost) black areas. */ - max_variance += 1e-5f; - ref -= 1e-5f; - - if (L > ref) { - /* The pixel appears to be an outlier. - * However, it may just be a legitimate highlight. Therefore, it is checked how likely it is - * that the pixel should actually be at the reference value: If the reference is within the - * 3-sigma interval, the pixel is assumed to be a statistical outlier. Otherwise, it is very - * unlikely that the pixel should be darker, which indicates a legitimate highlight. - */ - - if (pixel_variance < 0.0f || pixel_variance > 9.0f * max_variance) { - depth[idx] = -depth[idx]; - color *= ref / L; - variance = make_float3(max_variance, max_variance, max_variance); - } - else { - float stddev = sqrtf(pixel_variance); - if (L - 3 * stddev < ref) { - /* The pixel is an outlier, so negate the depth value to mark it as one. - * Also, scale its brightness down to the outlier threshold to avoid trouble with the NLM - * weights. */ - depth[idx] = -depth[idx]; - float fac = ref / L; - color *= fac; - variance *= sqr(fac); - } - } - } - - /* Apply log(1+x) transform to compress highlights and avoid halos in the denoised results. - * Variance is transformed accordingly - the derivative of the transform is 1/(1+x), so we - * scale by the square of that (since we have variance instead of standard deviation). */ - color = color_highlight_compress(color, &variance); - - SET_COLOR(image_out, color); - SET_COLOR(variance_out, variance); -} - -#undef GET_COLOR -#undef SET_COLOR - -/* Combine A/B buffers. - * Calculates the combined mean and the buffer variance. */ -ccl_device void kernel_filter_combine_halves(int x, - int y, - ccl_global float *mean, - ccl_global float *variance, - ccl_global float *a, - ccl_global float *b, - int4 rect, - int r) -{ - int buffer_w = align_up(rect.z - rect.x, 4); - int idx = (y - rect.y) * buffer_w + (x - rect.x); - - if (mean) - mean[idx] = 0.5f * (a[idx] + b[idx]); - if (variance) { - if (r == 0) - variance[idx] = 0.25f * (a[idx] - b[idx]) * (a[idx] - b[idx]); - else { - variance[idx] = 0.0f; - float values[25]; - int numValues = 0; - for (int py = max(y - r, rect.y); py < min(y + r + 1, rect.w); py++) { - for (int px = max(x - r, rect.x); px < min(x + r + 1, rect.z); px++) { - int pidx = (py - rect.y) * buffer_w + (px - rect.x); - values[numValues++] = 0.25f * (a[pidx] - b[pidx]) * (a[pidx] - b[pidx]); - } - } - /* Insertion-sort the variances (fast enough for 25 elements). */ - for (int i = 1; i < numValues; i++) { - float v = values[i]; - int j; - for (j = i - 1; j >= 0 && values[j] > v; j--) - values[j + 1] = values[j]; - values[j + 1] = v; - } - variance[idx] = values[(7 * numValues) / 8]; - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_reconstruction.h b/intern/cycles/kernel/filter/filter_reconstruction.h deleted file mode 100644 index 17941689ad5..00000000000 --- a/intern/cycles/kernel/filter/filter_reconstruction.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device_inline void kernel_filter_construct_gramian(int x, - int y, - int storage_stride, - int dx, - int dy, - int t, - int buffer_stride, - int pass_stride, - int frame_offset, - bool use_time, - const ccl_global float *ccl_restrict buffer, - const ccl_global float *ccl_restrict - transform, - ccl_global int *rank, - float weight, - ccl_global float *XtWX, - ccl_global float3 *XtWY, - int localIdx) -{ - if (weight < 1e-3f) { - return; - } - - int p_offset = y * buffer_stride + x; - int q_offset = (y + dy) * buffer_stride + (x + dx) + frame_offset; - -#ifdef __KERNEL_GPU__ - const int stride = storage_stride; -#else - const int stride = 1; - (void)storage_stride; -#endif - -#ifdef __KERNEL_CUDA__ - ccl_local float shared_design_row[(DENOISE_FEATURES + 1) * CCL_MAX_LOCAL_SIZE]; - ccl_local_param float *design_row = shared_design_row + localIdx * (DENOISE_FEATURES + 1); -#else - float design_row[DENOISE_FEATURES + 1]; -#endif - - float3 q_color = filter_get_color(buffer + q_offset, pass_stride); - - /* If the pixel was flagged as an outlier during prefiltering, skip it. */ - if (ccl_get_feature(buffer + q_offset, 0) < 0.0f) { - return; - } - - filter_get_design_row_transform(make_int3(x, y, t), - buffer + p_offset, - make_int3(x + dx, y + dy, t), - buffer + q_offset, - pass_stride, - *rank, - design_row, - transform, - stride, - use_time); - -#ifdef __KERNEL_GPU__ - math_trimatrix_add_gramian_strided(XtWX, (*rank) + 1, design_row, weight, stride); - math_vec3_add_strided(XtWY, (*rank) + 1, design_row, weight * q_color, stride); -#else - math_trimatrix_add_gramian(XtWX, (*rank) + 1, design_row, weight); - math_vec3_add(XtWY, (*rank) + 1, design_row, weight * q_color); -#endif -} - -ccl_device_inline void kernel_filter_finalize(int x, - int y, - ccl_global float *buffer, - ccl_global int *rank, - int storage_stride, - ccl_global float *XtWX, - ccl_global float3 *XtWY, - int4 buffer_params, - int sample) -{ -#ifdef __KERNEL_GPU__ - const int stride = storage_stride; -#else - const int stride = 1; - (void)storage_stride; -#endif - - if (XtWX[0] < 1e-3f) { - /* There is not enough information to determine a denoised result. - * As a fallback, keep the original value of the pixel. */ - return; - } - - /* The weighted average of pixel colors (essentially, the NLM-filtered image). - * In case the solution of the linear model fails due to numerical issues or - * returns nonsensical negative values, fall back to this value. */ - float3 mean_color = XtWY[0] / XtWX[0]; - - math_trimatrix_vec3_solve(XtWX, XtWY, (*rank) + 1, stride); - - float3 final_color = XtWY[0]; - if (!isfinite3_safe(final_color) || - (final_color.x < -0.01f || final_color.y < -0.01f || final_color.z < -0.01f)) { - final_color = mean_color; - } - - /* Clamp pixel value to positive values and reverse the highlight compression transform. */ - final_color = color_highlight_uncompress(max(final_color, make_float3(0.0f, 0.0f, 0.0f))); - - ccl_global float *combined_buffer = buffer + (y * buffer_params.y + x + buffer_params.x) * - buffer_params.z; - if (buffer_params.w >= 0) { - final_color *= sample; - if (buffer_params.w > 0) { - final_color.x += combined_buffer[buffer_params.w + 0]; - final_color.y += combined_buffer[buffer_params.w + 1]; - final_color.z += combined_buffer[buffer_params.w + 2]; - } - } - combined_buffer[0] = final_color.x; - combined_buffer[1] = final_color.y; - combined_buffer[2] = final_color.z; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_transform.h b/intern/cycles/kernel/filter/filter_transform.h deleted file mode 100644 index 880a661214e..00000000000 --- a/intern/cycles/kernel/filter/filter_transform.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_filter_construct_transform(const float *ccl_restrict buffer, - CCL_FILTER_TILE_INFO, - int x, - int y, - int4 rect, - int pass_stride, - int frame_stride, - bool use_time, - float *transform, - int *rank, - int radius, - float pca_threshold) -{ - int buffer_w = align_up(rect.z - rect.x, 4); - - float features[DENOISE_FEATURES]; - - const float *ccl_restrict pixel_buffer; - int3 pixel; - - int num_features = use_time ? 11 : 10; - - /* === Calculate denoising window. === */ - int2 low = make_int2(max(rect.x, x - radius), max(rect.y, y - radius)); - int2 high = make_int2(min(rect.z, x + radius + 1), min(rect.w, y + radius + 1)); - int num_pixels = (high.y - low.y) * (high.x - low.x) * tile_info->num_frames; - - /* === Shift feature passes to have mean 0. === */ - float feature_means[DENOISE_FEATURES]; - math_vector_zero(feature_means, num_features); - FOR_PIXEL_WINDOW - { - filter_get_features(pixel, pixel_buffer, features, use_time, NULL, pass_stride); - math_vector_add(feature_means, features, num_features); - } - END_FOR_PIXEL_WINDOW - - math_vector_scale(feature_means, 1.0f / num_pixels, num_features); - - /* === Scale the shifted feature passes to a range of [-1; 1] === - * Will be baked into the transform later. */ - float feature_scale[DENOISE_FEATURES]; - math_vector_zero(feature_scale, num_features); - - FOR_PIXEL_WINDOW - { - filter_get_feature_scales(pixel, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_max(feature_scale, features, num_features); - } - END_FOR_PIXEL_WINDOW - - filter_calculate_scale(feature_scale, use_time); - - /* === Generate the feature transformation. === - * This transformation maps the num_features-dimensional feature space to a reduced feature - * (r-feature) space which generally has fewer dimensions. - * This mainly helps to prevent over-fitting. */ - float feature_matrix[DENOISE_FEATURES * DENOISE_FEATURES]; - math_matrix_zero(feature_matrix, num_features); - FOR_PIXEL_WINDOW - { - filter_get_features(pixel, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_mul(features, feature_scale, num_features); - math_matrix_add_gramian(feature_matrix, num_features, features, 1.0f); - } - END_FOR_PIXEL_WINDOW - - math_matrix_jacobi_eigendecomposition(feature_matrix, transform, num_features, 1); - *rank = 0; - /* Prevent over-fitting when a small window is used. */ - int max_rank = min(num_features, num_pixels / 3); - if (pca_threshold < 0.0f) { - float threshold_energy = 0.0f; - for (int i = 0; i < num_features; i++) { - threshold_energy += feature_matrix[i * num_features + i]; - } - threshold_energy *= 1.0f - (-pca_threshold); - - float reduced_energy = 0.0f; - for (int i = 0; i < max_rank; i++, (*rank)++) { - if (i >= 2 && reduced_energy >= threshold_energy) - break; - float s = feature_matrix[i * num_features + i]; - reduced_energy += s; - } - } - else { - for (int i = 0; i < max_rank; i++, (*rank)++) { - float s = feature_matrix[i * num_features + i]; - if (i >= 2 && sqrtf(s) < pca_threshold) - break; - } - } - - /* Bake the feature scaling into the transformation matrix. */ - for (int i = 0; i < (*rank); i++) { - math_vector_mul(transform + i * num_features, feature_scale, num_features); - } - math_matrix_transpose(transform, num_features, 1); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_transform_gpu.h b/intern/cycles/kernel/filter/filter_transform_gpu.h deleted file mode 100644 index ec258a5212a..00000000000 --- a/intern/cycles/kernel/filter/filter_transform_gpu.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_filter_construct_transform(const ccl_global float *ccl_restrict buffer, - CCL_FILTER_TILE_INFO, - int x, - int y, - int4 rect, - int pass_stride, - int frame_stride, - bool use_time, - ccl_global float *transform, - ccl_global int *rank, - int radius, - float pca_threshold, - int transform_stride, - int localIdx) -{ - int buffer_w = align_up(rect.z - rect.x, 4); - -#ifdef __KERNEL_CUDA__ - ccl_local float shared_features[DENOISE_FEATURES * CCL_MAX_LOCAL_SIZE]; - ccl_local_param float *features = shared_features + localIdx * DENOISE_FEATURES; -#else - float features[DENOISE_FEATURES]; -#endif - - int num_features = use_time ? 11 : 10; - - /* === Calculate denoising window. === */ - int2 low = make_int2(max(rect.x, x - radius), max(rect.y, y - radius)); - int2 high = make_int2(min(rect.z, x + radius + 1), min(rect.w, y + radius + 1)); - int num_pixels = (high.y - low.y) * (high.x - low.x) * tile_info->num_frames; - const ccl_global float *ccl_restrict pixel_buffer; - int3 pixel; - - /* === Shift feature passes to have mean 0. === */ - float feature_means[DENOISE_FEATURES]; - math_vector_zero(feature_means, num_features); - FOR_PIXEL_WINDOW - { - filter_get_features(pixel, pixel_buffer, features, use_time, NULL, pass_stride); - math_vector_add(feature_means, features, num_features); - } - END_FOR_PIXEL_WINDOW - - math_vector_scale(feature_means, 1.0f / num_pixels, num_features); - - /* === Scale the shifted feature passes to a range of [-1; 1] === - * Will be baked into the transform later. */ - float feature_scale[DENOISE_FEATURES]; - math_vector_zero(feature_scale, num_features); - - FOR_PIXEL_WINDOW - { - filter_get_feature_scales(pixel, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_max(feature_scale, features, num_features); - } - END_FOR_PIXEL_WINDOW - - filter_calculate_scale(feature_scale, use_time); - - /* === Generate the feature transformation. === - * This transformation maps the num_features-dimensional feature space to a reduced feature - * (r-feature) space which generally has fewer dimensions. - * This mainly helps to prevent over-fitting. */ - float feature_matrix[DENOISE_FEATURES * DENOISE_FEATURES]; - math_matrix_zero(feature_matrix, num_features); - FOR_PIXEL_WINDOW - { - filter_get_features(pixel, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_mul(features, feature_scale, num_features); - math_matrix_add_gramian(feature_matrix, num_features, features, 1.0f); - } - END_FOR_PIXEL_WINDOW - - math_matrix_jacobi_eigendecomposition(feature_matrix, transform, num_features, transform_stride); - *rank = 0; - /* Prevent over-fitting when a small window is used. */ - int max_rank = min(num_features, num_pixels / 3); - if (pca_threshold < 0.0f) { - float threshold_energy = 0.0f; - for (int i = 0; i < num_features; i++) { - threshold_energy += feature_matrix[i * num_features + i]; - } - threshold_energy *= 1.0f - (-pca_threshold); - - float reduced_energy = 0.0f; - for (int i = 0; i < max_rank; i++, (*rank)++) { - if (i >= 2 && reduced_energy >= threshold_energy) - break; - float s = feature_matrix[i * num_features + i]; - reduced_energy += s; - } - } - else { - for (int i = 0; i < max_rank; i++, (*rank)++) { - float s = feature_matrix[i * num_features + i]; - if (i >= 2 && sqrtf(s) < pca_threshold) - break; - } - } - - math_matrix_transpose(transform, num_features, transform_stride); - - /* Bake the feature scaling into the transformation matrix. */ - for (int i = 0; i < num_features; i++) { - for (int j = 0; j < (*rank); j++) { - transform[(i * num_features + j) * transform_stride] *= feature_scale[i]; - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_transform_sse.h b/intern/cycles/kernel/filter/filter_transform_sse.h deleted file mode 100644 index 0304d990f9f..00000000000 --- a/intern/cycles/kernel/filter/filter_transform_sse.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_filter_construct_transform(const float *ccl_restrict buffer, - CCL_FILTER_TILE_INFO, - int x, - int y, - int4 rect, - int pass_stride, - int frame_stride, - bool use_time, - float *transform, - int *rank, - int radius, - float pca_threshold) -{ - int buffer_w = align_up(rect.z - rect.x, 4); - - float4 features[DENOISE_FEATURES]; - const float *ccl_restrict pixel_buffer; - int3 pixel; - - int num_features = use_time ? 11 : 10; - - /* === Calculate denoising window. === */ - int2 low = make_int2(max(rect.x, x - radius), max(rect.y, y - radius)); - int2 high = make_int2(min(rect.z, x + radius + 1), min(rect.w, y + radius + 1)); - int num_pixels = (high.y - low.y) * (high.x - low.x) * tile_info->num_frames; - - /* === Shift feature passes to have mean 0. === */ - float4 feature_means[DENOISE_FEATURES]; - math_vector_zero_sse(feature_means, num_features); - FOR_PIXEL_WINDOW_SSE - { - filter_get_features_sse( - x4, y4, t4, active_pixels, pixel_buffer, features, use_time, NULL, pass_stride); - math_vector_add_sse(feature_means, num_features, features); - } - END_FOR_PIXEL_WINDOW_SSE - - float4 pixel_scale = make_float4(1.0f / num_pixels); - for (int i = 0; i < num_features; i++) { - feature_means[i] = reduce_add(feature_means[i]) * pixel_scale; - } - - /* === Scale the shifted feature passes to a range of [-1; 1] === - * Will be baked into the transform later. */ - float4 feature_scale[DENOISE_FEATURES]; - math_vector_zero_sse(feature_scale, num_features); - FOR_PIXEL_WINDOW_SSE - { - filter_get_feature_scales_sse( - x4, y4, t4, active_pixels, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_max_sse(feature_scale, features, num_features); - } - END_FOR_PIXEL_WINDOW_SSE - - filter_calculate_scale_sse(feature_scale, use_time); - - /* === Generate the feature transformation. === - * This transformation maps the num_features-dimensional feature space to a reduced feature - * (r-feature) space which generally has fewer dimensions. - * This mainly helps to prevent over-fitting. */ - float4 feature_matrix_sse[DENOISE_FEATURES * DENOISE_FEATURES]; - math_matrix_zero_sse(feature_matrix_sse, num_features); - FOR_PIXEL_WINDOW_SSE - { - filter_get_features_sse( - x4, y4, t4, active_pixels, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_mul_sse(features, num_features, feature_scale); - math_matrix_add_gramian_sse(feature_matrix_sse, num_features, features, make_float4(1.0f)); - } - END_FOR_PIXEL_WINDOW_SSE - - float feature_matrix[DENOISE_FEATURES * DENOISE_FEATURES]; - math_matrix_hsum(feature_matrix, num_features, feature_matrix_sse); - - math_matrix_jacobi_eigendecomposition(feature_matrix, transform, num_features, 1); - - *rank = 0; - /* Prevent over-fitting when a small window is used. */ - int max_rank = min(num_features, num_pixels / 3); - if (pca_threshold < 0.0f) { - float threshold_energy = 0.0f; - for (int i = 0; i < num_features; i++) { - threshold_energy += feature_matrix[i * num_features + i]; - } - threshold_energy *= 1.0f - (-pca_threshold); - - float reduced_energy = 0.0f; - for (int i = 0; i < max_rank; i++, (*rank)++) { - if (i >= 2 && reduced_energy >= threshold_energy) - break; - float s = feature_matrix[i * num_features + i]; - reduced_energy += s; - } - } - else { - for (int i = 0; i < max_rank; i++, (*rank)++) { - float s = feature_matrix[i * num_features + i]; - if (i >= 2 && sqrtf(s) < pca_threshold) - break; - } - } - - math_matrix_transpose(transform, num_features, 1); - - /* Bake the feature scaling into the transformation matrix. */ - for (int i = 0; i < num_features; i++) { - math_vector_scale(transform + i * num_features, feature_scale[i][0], *rank); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/geom/geom.h b/intern/cycles/kernel/geom/geom.h index 5ff4d5f7053..4de824cc277 100644 --- a/intern/cycles/kernel/geom/geom.h +++ b/intern/cycles/kernel/geom/geom.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + // clang-format off #include "kernel/geom/geom_attribute.h" #include "kernel/geom/geom_object.h" @@ -31,4 +33,5 @@ #include "kernel/geom/geom_curve_intersect.h" #include "kernel/geom/geom_volume.h" #include "kernel/geom/geom_primitive.h" +#include "kernel/geom/geom_shader_data.h" // clang-format on diff --git a/intern/cycles/kernel/geom/geom_attribute.h b/intern/cycles/kernel/geom/geom_attribute.h index b37797ac21b..9532a21fec7 100644 --- a/intern/cycles/kernel/geom/geom_attribute.h +++ b/intern/cycles/kernel/geom/geom_attribute.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Attributes @@ -25,9 +27,9 @@ CCL_NAMESPACE_BEGIN * Lookup of attributes is different between OSL and SVM, as OSL is ustring * based while for SVM we use integer ids. */ -ccl_device_inline uint subd_triangle_patch(KernelGlobals *kg, const ShaderData *sd); +ccl_device_inline uint subd_triangle_patch(const KernelGlobals *kg, const ShaderData *sd); -ccl_device_inline uint attribute_primitive_type(KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline uint attribute_primitive_type(const KernelGlobals *kg, const ShaderData *sd) { if ((sd->type & PRIMITIVE_ALL_TRIANGLE) && subd_triangle_patch(kg, sd) != ~0) { return ATTR_PRIM_SUBD; @@ -46,12 +48,12 @@ ccl_device_inline AttributeDescriptor attribute_not_found() /* Find attribute based on ID */ -ccl_device_inline uint object_attribute_map_offset(KernelGlobals *kg, int object) +ccl_device_inline uint object_attribute_map_offset(const KernelGlobals *kg, int object) { return kernel_tex_fetch(__objects, object).attribute_map_offset; } -ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals *kg, +ccl_device_inline AttributeDescriptor find_attribute(const KernelGlobals *kg, const ShaderData *sd, uint id) { @@ -98,7 +100,7 @@ ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals *kg, /* Transform matrix attribute on meshes */ -ccl_device Transform primitive_attribute_matrix(KernelGlobals *kg, +ccl_device Transform primitive_attribute_matrix(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc) { diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h index b5a62a31ca9..a827a67ce7a 100644 --- a/intern/cycles/kernel/geom/geom_curve.h +++ b/intern/cycles/kernel/geom/geom_curve.h @@ -12,6 +12,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Curve Primitive @@ -25,8 +27,11 @@ CCL_NAMESPACE_BEGIN /* Reading attributes on various curve elements */ -ccl_device float curve_attribute_float( - KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) +ccl_device float curve_attribute_float(const KernelGlobals *kg, + const ShaderData *sd, + const AttributeDescriptor desc, + float *dx, + float *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { float4 curvedata = kernel_tex_fetch(__curves, sd->prim); @@ -64,7 +69,7 @@ ccl_device float curve_attribute_float( } } -ccl_device float2 curve_attribute_float2(KernelGlobals *kg, +ccl_device float2 curve_attribute_float2(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float2 *dx, @@ -110,7 +115,7 @@ ccl_device float2 curve_attribute_float2(KernelGlobals *kg, } } -ccl_device float3 curve_attribute_float3(KernelGlobals *kg, +ccl_device float3 curve_attribute_float3(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, @@ -152,7 +157,7 @@ ccl_device float3 curve_attribute_float3(KernelGlobals *kg, } } -ccl_device float4 curve_attribute_float4(KernelGlobals *kg, +ccl_device float4 curve_attribute_float4(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float4 *dx, @@ -196,7 +201,7 @@ ccl_device float4 curve_attribute_float4(KernelGlobals *kg, /* Curve thickness */ -ccl_device float curve_thickness(KernelGlobals *kg, ShaderData *sd) +ccl_device float curve_thickness(const KernelGlobals *kg, const ShaderData *sd) { float r = 0.0f; @@ -224,7 +229,7 @@ ccl_device float curve_thickness(KernelGlobals *kg, ShaderData *sd) /* Curve location for motion pass, linear interpolation between keys and * ignoring radius because we do the same for the motion keys */ -ccl_device float3 curve_motion_center_location(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 curve_motion_center_location(const KernelGlobals *kg, const ShaderData *sd) { float4 curvedata = kernel_tex_fetch(__curves, sd->prim); int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); @@ -240,7 +245,7 @@ ccl_device float3 curve_motion_center_location(KernelGlobals *kg, ShaderData *sd /* Curve tangent normal */ -ccl_device float3 curve_tangent_normal(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 curve_tangent_normal(const KernelGlobals *kg, const ShaderData *sd) { float3 tgN = make_float3(0.0f, 0.0f, 0.0f); diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h index e25bf5b4660..213f3e62ee0 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/geom_curve_intersect.h @@ -15,6 +15,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Curve primitive intersection functions. @@ -167,6 +169,7 @@ ccl_device_inline float2 half_plane_intersect(const float3 P, const float3 N, co } ccl_device bool curve_intersect_iterative(const float3 ray_dir, + float *ray_tfar, const float dt, const float4 curve[4], float u, @@ -230,7 +233,7 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, if (fabsf(f) < f_err && fabsf(g) < g_err) { t += dt; - if (!(0.0f <= t && t <= isect->t)) { + if (!(0.0f <= t && t <= *ray_tfar)) { return false; /* Rejects NaNs */ } if (!(u >= 0.0f && u <= 1.0f)) { @@ -247,6 +250,7 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, } /* Record intersection. */ + *ray_tfar = t; isect->t = t; isect->u = u; isect->v = 0.0f; @@ -259,6 +263,7 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, ccl_device bool curve_intersect_recursive(const float3 ray_orig, const float3 ray_dir, + float ray_tfar, float4 curve[4], Intersection *isect) { @@ -339,7 +344,7 @@ ccl_device bool curve_intersect_recursive(const float3 ray_orig, } /* Intersect with cap-planes. */ - float2 tp = make_float2(-dt, isect->t - dt); + float2 tp = make_float2(-dt, ray_tfar - dt); tp = make_float2(max(tp.x, tc_outer.x), min(tp.y, tc_outer.y)); const float2 h0 = half_plane_intersect( float4_to_float3(P0), float4_to_float3(dP0du), ray_dir); @@ -402,19 +407,19 @@ ccl_device bool curve_intersect_recursive(const float3 ray_orig, CURVE_NUM_BEZIER_SUBDIVISIONS; if (depth >= termDepth) { found |= curve_intersect_iterative( - ray_dir, dt, curve, u_outer0, tp0.x, use_backfacing, isect); + ray_dir, &ray_tfar, dt, curve, u_outer0, tp0.x, use_backfacing, isect); } else { recurse = true; } } - if (valid1 && (tp1.x + dt <= isect->t)) { + if (valid1 && (tp1.x + dt <= ray_tfar)) { const int termDepth = unstable1 ? CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE : CURVE_NUM_BEZIER_SUBDIVISIONS; if (depth >= termDepth) { found |= curve_intersect_iterative( - ray_dir, dt, curve, u_outer1, tp1.y, use_backfacing, isect); + ray_dir, &ray_tfar, dt, curve, u_outer1, tp1.y, use_backfacing, isect); } else { recurse = true; @@ -542,7 +547,7 @@ ccl_device_inline float4 ribbon_to_ray_space(const float3 ray_space[3], ccl_device_inline bool ribbon_intersect(const float3 ray_org, const float3 ray_dir, - const float ray_tfar, + float ray_tfar, const int N, float4 curve[4], Intersection *isect) @@ -590,7 +595,7 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, /* Intersect quad. */ float vu, vv, vt; - bool valid0 = ribbon_intersect_quad(isect->t, lp0, lp1, up1, up0, &vu, &vv, &vt); + bool valid0 = ribbon_intersect_quad(ray_tfar, lp0, lp1, up1, up0, &vu, &vv, &vt); if (valid0) { /* ignore self intersections */ @@ -604,6 +609,7 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, vv = 2.0f * vv - 1.0f; /* Record intersection. */ + ray_tfar = vt; isect->t = vt; isect->u = u + vu * step_size; isect->v = vv; @@ -619,10 +625,11 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, return false; } -ccl_device_forceinline bool curve_intersect(KernelGlobals *kg, +ccl_device_forceinline bool curve_intersect(const KernelGlobals *kg, Intersection *isect, const float3 P, const float3 dir, + const float tmax, uint visibility, int object, int curveAddr, @@ -672,7 +679,7 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals *kg, if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) { /* todo: adaptive number of subdivisions could help performance here. */ const int subdivisions = kernel_data.bvh.curve_subdivisions; - if (ribbon_intersect(P, dir, isect->t, subdivisions, curve, isect)) { + if (ribbon_intersect(P, dir, tmax, subdivisions, curve, isect)) { isect->prim = curveAddr; isect->object = object; isect->type = type; @@ -682,7 +689,7 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals *kg, return false; } else { - if (curve_intersect_recursive(P, dir, curve, isect)) { + if (curve_intersect_recursive(P, dir, tmax, curve, isect)) { isect->prim = curveAddr; isect->object = object; isect->type = type; @@ -693,28 +700,23 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals *kg, } } -ccl_device_inline void curve_shader_setup(KernelGlobals *kg, +ccl_device_inline void curve_shader_setup(const KernelGlobals *kg, ShaderData *sd, - const Intersection *isect, - const Ray *ray) + float3 P, + float3 D, + float t, + const int isect_object, + const int isect_prim) { - float t = isect->t; - float3 P = ray->P; - float3 D = ray->D; - - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif + if (isect_object != OBJECT_NONE) { + const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); D = transform_direction(&tfm, D * t); D = normalize_len(D, &t); } - int prim = kernel_tex_fetch(__prim_index, isect->prim); + int prim = kernel_tex_fetch(__prim_index, isect_prim); float4 v00 = kernel_tex_fetch(__curves, prim); int k0 = __float_as_int(v00.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); @@ -735,23 +737,20 @@ ccl_device_inline void curve_shader_setup(KernelGlobals *kg, motion_curve_keys(kg, sd->object, sd->prim, sd->time, ka, k0, k1, kb, P_curve); } - sd->u = isect->u; - P = P + D * t; - const float4 dPdu4 = catmull_rom_basis_derivative(P_curve, isect->u); + const float4 dPdu4 = catmull_rom_basis_derivative(P_curve, sd->u); const float3 dPdu = float4_to_float3(dPdu4); if (sd->type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) { /* Rounded smooth normals for ribbons, to approximate thick curve shape. */ const float3 tangent = normalize(dPdu); const float3 bitangent = normalize(cross(tangent, -D)); - const float sine = isect->v; + const float sine = sd->v; const float cosine = safe_sqrtf(1.0f - sine * sine); sd->N = normalize(sine * bitangent - cosine * normalize(cross(tangent, bitangent))); sd->Ng = -D; - sd->v = isect->v; # if 0 /* This approximates the position and geometric normal of a thick curve too, @@ -765,7 +764,7 @@ ccl_device_inline void curve_shader_setup(KernelGlobals *kg, /* Thick curves, compute normal using direction from inside the curve. * This could be optimized by recording the normal in the intersection, * however for Optix this would go beyond the size of the payload. */ - const float3 P_inside = float4_to_float3(catmull_rom_basis_eval(P_curve, isect->u)); + const float3 P_inside = float4_to_float3(catmull_rom_basis_eval(P_curve, sd->u)); const float3 Ng = normalize(P - P_inside); sd->N = Ng; @@ -779,13 +778,8 @@ ccl_device_inline void curve_shader_setup(KernelGlobals *kg, sd->dPdv = cross(dPdu, sd->Ng); # endif - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - + if (isect_object != OBJECT_NONE) { + const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } diff --git a/intern/cycles/kernel/geom/geom_motion_curve.h b/intern/cycles/kernel/geom/geom_motion_curve.h index 0f66f4af755..5294da03145 100644 --- a/intern/cycles/kernel/geom/geom_motion_curve.h +++ b/intern/cycles/kernel/geom/geom_motion_curve.h @@ -12,6 +12,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Motion Curve Primitive @@ -25,7 +27,7 @@ CCL_NAMESPACE_BEGIN #ifdef __HAIR__ -ccl_device_inline int find_attribute_curve_motion(KernelGlobals *kg, +ccl_device_inline int find_attribute_curve_motion(const KernelGlobals *kg, int object, uint id, AttributeElement *elem) @@ -50,7 +52,7 @@ ccl_device_inline int find_attribute_curve_motion(KernelGlobals *kg, return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; } -ccl_device_inline void motion_curve_keys_for_step_linear(KernelGlobals *kg, +ccl_device_inline void motion_curve_keys_for_step_linear(const KernelGlobals *kg, int offset, int numkeys, int numsteps, @@ -78,7 +80,7 @@ ccl_device_inline void motion_curve_keys_for_step_linear(KernelGlobals *kg, /* return 2 curve key locations */ ccl_device_inline void motion_curve_keys_linear( - KernelGlobals *kg, int object, int prim, float time, int k0, int k1, float4 keys[2]) + const KernelGlobals *kg, int object, int prim, float time, int k0, int k1, float4 keys[2]) { /* get motion info */ int numsteps, numkeys; @@ -105,7 +107,7 @@ ccl_device_inline void motion_curve_keys_linear( keys[1] = (1.0f - t) * keys[1] + t * next_keys[1]; } -ccl_device_inline void motion_curve_keys_for_step(KernelGlobals *kg, +ccl_device_inline void motion_curve_keys_for_step(const KernelGlobals *kg, int offset, int numkeys, int numsteps, @@ -138,7 +140,7 @@ ccl_device_inline void motion_curve_keys_for_step(KernelGlobals *kg, } /* return 2 curve key locations */ -ccl_device_inline void motion_curve_keys(KernelGlobals *kg, +ccl_device_inline void motion_curve_keys(const KernelGlobals *kg, int object, int prim, float time, diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/geom_motion_triangle.h index 53d6b92dd7e..eb4a39e062b 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle.h @@ -25,11 +25,13 @@ * and ATTR_STD_MOTION_VERTEX_NORMAL mesh attributes. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Time interpolation of vertex positions and normals */ -ccl_device_inline int find_attribute_motion(KernelGlobals *kg, +ccl_device_inline int find_attribute_motion(const KernelGlobals *kg, int object, uint id, AttributeElement *elem) @@ -49,7 +51,7 @@ ccl_device_inline int find_attribute_motion(KernelGlobals *kg, return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; } -ccl_device_inline void motion_triangle_verts_for_step(KernelGlobals *kg, +ccl_device_inline void motion_triangle_verts_for_step(const KernelGlobals *kg, uint4 tri_vindex, int offset, int numverts, @@ -76,7 +78,7 @@ ccl_device_inline void motion_triangle_verts_for_step(KernelGlobals *kg, } } -ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals *kg, +ccl_device_inline void motion_triangle_normals_for_step(const KernelGlobals *kg, uint4 tri_vindex, int offset, int numverts, @@ -104,7 +106,7 @@ ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals *kg, } ccl_device_inline void motion_triangle_vertices( - KernelGlobals *kg, int object, int prim, float time, float3 verts[3]) + const KernelGlobals *kg, int object, int prim, float time, float3 verts[3]) { /* get motion info */ int numsteps, numverts; @@ -134,7 +136,7 @@ ccl_device_inline void motion_triangle_vertices( } ccl_device_inline float3 motion_triangle_smooth_normal( - KernelGlobals *kg, float3 Ng, int object, int prim, float u, float v, float time) + const KernelGlobals *kg, float3 Ng, int object, int prim, float u, float v, float time) { /* get motion info */ int numsteps, numverts; diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h index 859d919f0bb..ec7e4b07d76 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h @@ -25,6 +25,8 @@ * and ATTR_STD_MOTION_VERTEX_NORMAL mesh attributes. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Refine triangle intersection to more precise hit point. For rays that travel @@ -32,23 +34,21 @@ CCL_NAMESPACE_BEGIN * a closer distance. */ -ccl_device_inline float3 motion_triangle_refine( - KernelGlobals *kg, ShaderData *sd, const Intersection *isect, const Ray *ray, float3 verts[3]) +ccl_device_inline float3 motion_triangle_refine(const KernelGlobals *kg, + ShaderData *sd, + float3 P, + float3 D, + float t, + const int isect_object, + const int isect_prim, + float3 verts[3]) { - float3 P = ray->P; - float3 D = ray->D; - float t = isect->t; - #ifdef __INTERSECTION_REFINE__ - if (isect->object != OBJECT_NONE) { + if (isect_object != OBJECT_NONE) { if (UNLIKELY(t == 0.0f)) { return P; } -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif + const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); D = transform_direction(&tfm, D * t); @@ -70,13 +70,8 @@ ccl_device_inline float3 motion_triangle_refine( /* Compute refined position. */ P = P + D * rt; - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - + if (isect_object != OBJECT_NONE) { + const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } @@ -86,7 +81,7 @@ ccl_device_inline float3 motion_triangle_refine( #endif } -/* Same as above, except that isect->t is assumed to be in object space +/* Same as above, except that t is assumed to be in object space * for instancing. */ @@ -97,27 +92,22 @@ ccl_device_noinline ccl_device_inline # endif float3 - motion_triangle_refine_local(KernelGlobals *kg, + motion_triangle_refine_local(const KernelGlobals *kg, ShaderData *sd, - const Intersection *isect, - const Ray *ray, + float3 P, + float3 D, + float t, + const int isect_object, + const int isect_prim, float3 verts[3]) { # ifdef __KERNEL_OPTIX__ - /* isect->t is always in world space with OptiX. */ - return motion_triangle_refine(kg, sd, isect, ray, verts); + /* t is always in world space with OptiX. */ + return motion_triangle_refine(kg, sd, P, D, t, isect_object, isect_prim, verts); # else - float3 P = ray->P; - float3 D = ray->D; - float t = isect->t; - # ifdef __INTERSECTION_REFINE__ - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif + if (isect_object != OBJECT_NONE) { + const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); D = transform_direction(&tfm, D); @@ -138,13 +128,8 @@ ccl_device_inline P = P + D * rt; - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - + if (isect_object != OBJECT_NONE) { + const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } @@ -160,10 +145,11 @@ ccl_device_inline * time and do a ray intersection with the resulting triangle. */ -ccl_device_inline bool motion_triangle_intersect(KernelGlobals *kg, +ccl_device_inline bool motion_triangle_intersect(const KernelGlobals *kg, Intersection *isect, float3 P, float3 dir, + float tmax, float time, uint visibility, int object, @@ -179,7 +165,7 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals *kg, float t, u, v; if (ray_triangle_intersect(P, dir, - isect->t, + tmax, #if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) (ssef *)verts, #else @@ -215,7 +201,7 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals *kg, * Returns whether traversal should be stopped. */ #ifdef __BVH_LOCAL__ -ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals *kg, +ccl_device_inline bool motion_triangle_intersect_local(const KernelGlobals *kg, LocalIntersection *local_isect, float3 P, float3 dir, diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h b/intern/cycles/kernel/geom/geom_motion_triangle_shader.h index 7a91f8041f7..85c4f0ca522 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle_shader.h @@ -25,6 +25,8 @@ * and ATTR_STD_MOTION_VERTEX_NORMAL mesh attributes. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Setup of motion triangle specific parts of ShaderData, moved into this one @@ -32,8 +34,14 @@ CCL_NAMESPACE_BEGIN * normals */ /* return 3 triangle vertex normals */ -ccl_device_noinline void motion_triangle_shader_setup( - KernelGlobals *kg, ShaderData *sd, const Intersection *isect, const Ray *ray, bool is_local) +ccl_device_noinline void motion_triangle_shader_setup(const KernelGlobals *kg, + ShaderData *sd, + const float3 P, + const float3 D, + const float ray_t, + const int isect_object, + const int isect_prim, + bool is_local) { /* Get shader. */ sd->shader = kernel_tex_fetch(__tri_shader, sd->prim); @@ -63,12 +71,12 @@ ccl_device_noinline void motion_triangle_shader_setup( /* Compute refined position. */ #ifdef __BVH_LOCAL__ if (is_local) { - sd->P = motion_triangle_refine_local(kg, sd, isect, ray, verts); + sd->P = motion_triangle_refine_local(kg, sd, P, D, ray_t, isect_object, isect_prim, verts); } else #endif /* __BVH_LOCAL__*/ { - sd->P = motion_triangle_refine(kg, sd, isect, ray, verts); + sd->P = motion_triangle_refine(kg, sd, P, D, ray_t, isect_object, isect_prim, verts); } /* Compute face normal. */ float3 Ng; diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index fe73335a335..7d6ad7b4fe3 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -22,6 +22,8 @@ * directly primitives in the BVH with world space locations applied, and the object * ID is looked up afterwards. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Object attributes, for now a fixed size and contents */ @@ -35,7 +37,7 @@ enum ObjectVectorTransform { OBJECT_PASS_MOTION_PRE = 0, OBJECT_PASS_MOTION_POST /* Object to world space transformation */ -ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, +ccl_device_inline Transform object_fetch_transform(const KernelGlobals *kg, int object, enum ObjectTransform type) { @@ -49,7 +51,7 @@ ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, /* Lamp to world space transformation */ -ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bool inverse) +ccl_device_inline Transform lamp_fetch_transform(const KernelGlobals *kg, int lamp, bool inverse) { if (inverse) { return kernel_tex_fetch(__lights, lamp).itfm; @@ -61,7 +63,7 @@ ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bo /* Object to world space transformation for motion vectors */ -ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals *kg, +ccl_device_inline Transform object_fetch_motion_pass_transform(const KernelGlobals *kg, int object, enum ObjectVectorTransform type) { @@ -72,7 +74,7 @@ ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals *kg /* Motion blurred object transformations */ #ifdef __OBJECT_MOTION__ -ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, +ccl_device_inline Transform object_fetch_transform_motion(const KernelGlobals *kg, int object, float time) { @@ -86,7 +88,7 @@ ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, return tfm; } -ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals *kg, +ccl_device_inline Transform object_fetch_transform_motion_test(const KernelGlobals *kg, int object, float time, Transform *itfm) @@ -111,45 +113,79 @@ ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals *kg } #endif +/* Get transform matrix for shading point. */ + +ccl_device_inline Transform object_get_transform(const KernelGlobals *kg, const ShaderData *sd) +{ +#ifdef __OBJECT_MOTION__ + return (sd->object_flag & SD_OBJECT_MOTION) ? + sd->ob_tfm_motion : + object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); +#else + return object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); +#endif +} + +ccl_device_inline Transform object_get_inverse_transform(const KernelGlobals *kg, + const ShaderData *sd) +{ +#ifdef __OBJECT_MOTION__ + return (sd->object_flag & SD_OBJECT_MOTION) ? + sd->ob_itfm_motion : + object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); +#else + return object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); +#endif +} /* Transform position from object to world space */ -ccl_device_inline void object_position_transform(KernelGlobals *kg, +ccl_device_inline void object_position_transform(const KernelGlobals *kg, const ShaderData *sd, float3 *P) { #ifdef __OBJECT_MOTION__ - *P = transform_point_auto(&sd->ob_tfm, *P); -#else + if (sd->object_flag & SD_OBJECT_MOTION) { + *P = transform_point_auto(&sd->ob_tfm_motion, *P); + return; + } +#endif + Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); *P = transform_point(&tfm, *P); -#endif } /* Transform position from world to object space */ -ccl_device_inline void object_inverse_position_transform(KernelGlobals *kg, +ccl_device_inline void object_inverse_position_transform(const KernelGlobals *kg, const ShaderData *sd, float3 *P) { #ifdef __OBJECT_MOTION__ - *P = transform_point_auto(&sd->ob_itfm, *P); -#else + if (sd->object_flag & SD_OBJECT_MOTION) { + *P = transform_point_auto(&sd->ob_itfm_motion, *P); + return; + } +#endif + Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); *P = transform_point(&tfm, *P); -#endif } /* Transform normal from world to object space */ -ccl_device_inline void object_inverse_normal_transform(KernelGlobals *kg, +ccl_device_inline void object_inverse_normal_transform(const KernelGlobals *kg, const ShaderData *sd, float3 *N) { #ifdef __OBJECT_MOTION__ - if ((sd->object != OBJECT_NONE) || (sd->type == PRIMITIVE_LAMP)) { - *N = normalize(transform_direction_transposed_auto(&sd->ob_tfm, *N)); + if (sd->object_flag & SD_OBJECT_MOTION) { + if ((sd->object != OBJECT_NONE) || (sd->type == PRIMITIVE_LAMP)) { + *N = normalize(transform_direction_transposed_auto(&sd->ob_tfm_motion, *N)); + } + return; } -#else +#endif + if (sd->object != OBJECT_NONE) { Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); *N = normalize(transform_direction_transposed(&tfm, *N)); @@ -158,65 +194,79 @@ ccl_device_inline void object_inverse_normal_transform(KernelGlobals *kg, Transform tfm = lamp_fetch_transform(kg, sd->lamp, false); *N = normalize(transform_direction_transposed(&tfm, *N)); } -#endif } /* Transform normal from object to world space */ -ccl_device_inline void object_normal_transform(KernelGlobals *kg, const ShaderData *sd, float3 *N) +ccl_device_inline void object_normal_transform(const KernelGlobals *kg, + const ShaderData *sd, + float3 *N) { #ifdef __OBJECT_MOTION__ - *N = normalize(transform_direction_transposed_auto(&sd->ob_itfm, *N)); -#else + if (sd->object_flag & SD_OBJECT_MOTION) { + *N = normalize(transform_direction_transposed_auto(&sd->ob_itfm_motion, *N)); + return; + } +#endif + Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); *N = normalize(transform_direction_transposed(&tfm, *N)); -#endif } /* Transform direction vector from object to world space */ -ccl_device_inline void object_dir_transform(KernelGlobals *kg, const ShaderData *sd, float3 *D) +ccl_device_inline void object_dir_transform(const KernelGlobals *kg, + const ShaderData *sd, + float3 *D) { #ifdef __OBJECT_MOTION__ - *D = transform_direction_auto(&sd->ob_tfm, *D); -#else + if (sd->object_flag & SD_OBJECT_MOTION) { + *D = transform_direction_auto(&sd->ob_tfm_motion, *D); + return; + } +#endif + Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); *D = transform_direction(&tfm, *D); -#endif } /* Transform direction vector from world to object space */ -ccl_device_inline void object_inverse_dir_transform(KernelGlobals *kg, +ccl_device_inline void object_inverse_dir_transform(const KernelGlobals *kg, const ShaderData *sd, float3 *D) { #ifdef __OBJECT_MOTION__ - *D = transform_direction_auto(&sd->ob_itfm, *D); -#else - Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); - *D = transform_direction(&tfm, *D); + if (sd->object_flag & SD_OBJECT_MOTION) { + *D = transform_direction_auto(&sd->ob_itfm_motion, *D); + return; + } #endif + + const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); + *D = transform_direction(&tfm, *D); } /* Object center position */ -ccl_device_inline float3 object_location(KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline float3 object_location(const KernelGlobals *kg, const ShaderData *sd) { if (sd->object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); #ifdef __OBJECT_MOTION__ - return make_float3(sd->ob_tfm.x.w, sd->ob_tfm.y.w, sd->ob_tfm.z.w); -#else + if (sd->object_flag & SD_OBJECT_MOTION) { + return make_float3(sd->ob_tfm_motion.x.w, sd->ob_tfm_motion.y.w, sd->ob_tfm_motion.z.w); + } +#endif + Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); return make_float3(tfm.x.w, tfm.y.w, tfm.z.w); -#endif } /* Color of the object */ -ccl_device_inline float3 object_color(KernelGlobals *kg, int object) +ccl_device_inline float3 object_color(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); @@ -227,7 +277,7 @@ ccl_device_inline float3 object_color(KernelGlobals *kg, int object) /* Pass ID number of object */ -ccl_device_inline float object_pass_id(KernelGlobals *kg, int object) +ccl_device_inline float object_pass_id(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -237,7 +287,7 @@ ccl_device_inline float object_pass_id(KernelGlobals *kg, int object) /* Per lamp random number for shader variation */ -ccl_device_inline float lamp_random_number(KernelGlobals *kg, int lamp) +ccl_device_inline float lamp_random_number(const KernelGlobals *kg, int lamp) { if (lamp == LAMP_NONE) return 0.0f; @@ -247,7 +297,7 @@ ccl_device_inline float lamp_random_number(KernelGlobals *kg, int lamp) /* Per object random number for shader variation */ -ccl_device_inline float object_random_number(KernelGlobals *kg, int object) +ccl_device_inline float object_random_number(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -257,7 +307,7 @@ ccl_device_inline float object_random_number(KernelGlobals *kg, int object) /* Particle ID from which this object was generated */ -ccl_device_inline int object_particle_id(KernelGlobals *kg, int object) +ccl_device_inline int object_particle_id(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0; @@ -267,7 +317,7 @@ ccl_device_inline int object_particle_id(KernelGlobals *kg, int object) /* Generated texture coordinate on surface from where object was instanced */ -ccl_device_inline float3 object_dupli_generated(KernelGlobals *kg, int object) +ccl_device_inline float3 object_dupli_generated(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); @@ -279,7 +329,7 @@ ccl_device_inline float3 object_dupli_generated(KernelGlobals *kg, int object) /* UV texture coordinate on surface from where object was instanced */ -ccl_device_inline float3 object_dupli_uv(KernelGlobals *kg, int object) +ccl_device_inline float3 object_dupli_uv(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); @@ -291,7 +341,7 @@ ccl_device_inline float3 object_dupli_uv(KernelGlobals *kg, int object) /* Information about mesh for motion blurred triangles and curves */ ccl_device_inline void object_motion_info( - KernelGlobals *kg, int object, int *numsteps, int *numverts, int *numkeys) + const KernelGlobals *kg, int object, int *numsteps, int *numverts, int *numkeys) { if (numkeys) { *numkeys = kernel_tex_fetch(__objects, object).numkeys; @@ -305,7 +355,7 @@ ccl_device_inline void object_motion_info( /* Offset to an objects patch map */ -ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object) +ccl_device_inline uint object_patch_map_offset(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0; @@ -315,7 +365,7 @@ ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object) /* Volume step size */ -ccl_device_inline float object_volume_density(KernelGlobals *kg, int object) +ccl_device_inline float object_volume_density(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) { return 1.0f; @@ -324,7 +374,7 @@ ccl_device_inline float object_volume_density(KernelGlobals *kg, int object) return kernel_tex_fetch(__objects, object).volume_density; } -ccl_device_inline float object_volume_step_size(KernelGlobals *kg, int object) +ccl_device_inline float object_volume_step_size(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) { return kernel_data.background.volume_step_size; @@ -335,14 +385,14 @@ ccl_device_inline float object_volume_step_size(KernelGlobals *kg, int object) /* Pass ID for shader */ -ccl_device int shader_pass_id(KernelGlobals *kg, const ShaderData *sd) +ccl_device int shader_pass_id(const KernelGlobals *kg, const ShaderData *sd) { return kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).pass_id; } /* Cryptomatte ID */ -ccl_device_inline float object_cryptomatte_id(KernelGlobals *kg, int object) +ccl_device_inline float object_cryptomatte_id(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -350,7 +400,7 @@ ccl_device_inline float object_cryptomatte_id(KernelGlobals *kg, int object) return kernel_tex_fetch(__objects, object).cryptomatte_object; } -ccl_device_inline float object_cryptomatte_asset_id(KernelGlobals *kg, int object) +ccl_device_inline float object_cryptomatte_asset_id(const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0; @@ -360,42 +410,42 @@ ccl_device_inline float object_cryptomatte_asset_id(KernelGlobals *kg, int objec /* Particle data from which object was instanced */ -ccl_device_inline uint particle_index(KernelGlobals *kg, int particle) +ccl_device_inline uint particle_index(const KernelGlobals *kg, int particle) { return kernel_tex_fetch(__particles, particle).index; } -ccl_device float particle_age(KernelGlobals *kg, int particle) +ccl_device float particle_age(const KernelGlobals *kg, int particle) { return kernel_tex_fetch(__particles, particle).age; } -ccl_device float particle_lifetime(KernelGlobals *kg, int particle) +ccl_device float particle_lifetime(const KernelGlobals *kg, int particle) { return kernel_tex_fetch(__particles, particle).lifetime; } -ccl_device float particle_size(KernelGlobals *kg, int particle) +ccl_device float particle_size(const KernelGlobals *kg, int particle) { return kernel_tex_fetch(__particles, particle).size; } -ccl_device float4 particle_rotation(KernelGlobals *kg, int particle) +ccl_device float4 particle_rotation(const KernelGlobals *kg, int particle) { return kernel_tex_fetch(__particles, particle).rotation; } -ccl_device float3 particle_location(KernelGlobals *kg, int particle) +ccl_device float3 particle_location(const KernelGlobals *kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).location); } -ccl_device float3 particle_velocity(KernelGlobals *kg, int particle) +ccl_device float3 particle_velocity(const KernelGlobals *kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).velocity); } -ccl_device float3 particle_angular_velocity(KernelGlobals *kg, int particle) +ccl_device float3 particle_angular_velocity(const KernelGlobals *kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).angular_velocity); } @@ -418,7 +468,7 @@ ccl_device_inline float3 bvh_inverse_direction(float3 dir) /* Transform ray into object space to enter static object in BVH */ ccl_device_inline float bvh_instance_push( - KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *dir, float3 *idir, float t) + const KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *dir, float3 *idir) { Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); @@ -428,17 +478,18 @@ ccl_device_inline float bvh_instance_push( *dir = bvh_clamp_direction(normalize_len(transform_direction(&tfm, ray->D), &len)); *idir = bvh_inverse_direction(*dir); - if (t != FLT_MAX) { - t *= len; - } - - return t; + return len; } /* Transform ray to exit static object in BVH. */ -ccl_device_inline float bvh_instance_pop( - KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *dir, float3 *idir, float t) +ccl_device_inline float bvh_instance_pop(const KernelGlobals *kg, + int object, + const Ray *ray, + float3 *P, + float3 *dir, + float3 *idir, + float t) { if (t != FLT_MAX) { Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); @@ -454,7 +505,7 @@ ccl_device_inline float bvh_instance_pop( /* Same as above, but returns scale factor to apply to multiple intersection distances */ -ccl_device_inline void bvh_instance_pop_factor(KernelGlobals *kg, +ccl_device_inline void bvh_instance_pop_factor(const KernelGlobals *kg, int object, const Ray *ray, float3 *P, @@ -473,13 +524,12 @@ ccl_device_inline void bvh_instance_pop_factor(KernelGlobals *kg, #ifdef __OBJECT_MOTION__ /* Transform ray into object space to enter motion blurred object in BVH */ -ccl_device_inline float bvh_instance_motion_push(KernelGlobals *kg, +ccl_device_inline float bvh_instance_motion_push(const KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *dir, float3 *idir, - float t, Transform *itfm) { object_fetch_transform_motion_test(kg, object, ray->time, itfm); @@ -490,16 +540,12 @@ ccl_device_inline float bvh_instance_motion_push(KernelGlobals *kg, *dir = bvh_clamp_direction(normalize_len(transform_direction(itfm, ray->D), &len)); *idir = bvh_inverse_direction(*dir); - if (t != FLT_MAX) { - t *= len; - } - - return t; + return len; } /* Transform ray to exit motion blurred object in BVH. */ -ccl_device_inline float bvh_instance_motion_pop(KernelGlobals *kg, +ccl_device_inline float bvh_instance_motion_pop(const KernelGlobals *kg, int object, const Ray *ray, float3 *P, @@ -521,7 +567,7 @@ ccl_device_inline float bvh_instance_motion_pop(KernelGlobals *kg, /* Same as above, but returns scale factor to apply to multiple intersection distances */ -ccl_device_inline void bvh_instance_motion_pop_factor(KernelGlobals *kg, +ccl_device_inline void bvh_instance_motion_pop_factor(const KernelGlobals *kg, int object, const Ray *ray, float3 *P, @@ -538,48 +584,11 @@ ccl_device_inline void bvh_instance_motion_pop_factor(KernelGlobals *kg, #endif -/* TODO(sergey): This is only for until we've got OpenCL 2.0 - * on all devices we consider supported. It'll be replaced with - * generic address space. - */ +/* TODO: This can be removed when we know if no devices will require explicit + * address space qualifiers for this case. */ -#ifdef __KERNEL_OPENCL__ -ccl_device_inline void object_position_transform_addrspace(KernelGlobals *kg, - const ShaderData *sd, - ccl_addr_space float3 *P) -{ - float3 private_P = *P; - object_position_transform(kg, sd, &private_P); - *P = private_P; -} - -ccl_device_inline void object_dir_transform_addrspace(KernelGlobals *kg, - const ShaderData *sd, - ccl_addr_space float3 *D) -{ - float3 private_D = *D; - object_dir_transform(kg, sd, &private_D); - *D = private_D; -} - -ccl_device_inline void object_normal_transform_addrspace(KernelGlobals *kg, - const ShaderData *sd, - ccl_addr_space float3 *N) -{ - float3 private_N = *N; - object_normal_transform(kg, sd, &private_N); - *N = private_N; -} -#endif - -#ifndef __KERNEL_OPENCL__ -# define object_position_transform_auto object_position_transform -# define object_dir_transform_auto object_dir_transform -# define object_normal_transform_auto object_normal_transform -#else -# define object_position_transform_auto object_position_transform_addrspace -# define object_dir_transform_auto object_dir_transform_addrspace -# define object_normal_transform_auto object_normal_transform_addrspace -#endif +#define object_position_transform_auto object_position_transform +#define object_dir_transform_auto object_dir_transform +#define object_normal_transform_auto object_normal_transform CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/geom/geom_patch.h b/intern/cycles/kernel/geom/geom_patch.h index 9c1768f05db..ce0fc15f196 100644 --- a/intern/cycles/kernel/geom/geom_patch.h +++ b/intern/cycles/kernel/geom/geom_patch.h @@ -24,6 +24,8 @@ * language governing permissions and limitations under the Apache License. */ +#pragma once + CCL_NAMESPACE_BEGIN typedef struct PatchHandle { @@ -60,7 +62,7 @@ ccl_device_inline int patch_map_resolve_quadrant(float median, float *u, float * /* retrieve PatchHandle from patch coords */ ccl_device_inline PatchHandle -patch_map_find_patch(KernelGlobals *kg, int object, int patch, float u, float v) +patch_map_find_patch(const KernelGlobals *kg, int object, int patch, float u, float v) { PatchHandle handle; @@ -191,7 +193,7 @@ ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, float *u, fl /* retrieve patch control indices */ -ccl_device_inline int patch_eval_indices(KernelGlobals *kg, +ccl_device_inline int patch_eval_indices(const KernelGlobals *kg, const PatchHandle *handle, int channel, int indices[PATCH_MAX_CONTROL_VERTS]) @@ -208,7 +210,7 @@ ccl_device_inline int patch_eval_indices(KernelGlobals *kg, /* evaluate patch basis functions */ -ccl_device_inline void patch_eval_basis(KernelGlobals *kg, +ccl_device_inline void patch_eval_basis(const KernelGlobals *kg, const PatchHandle *handle, float u, float v, @@ -247,7 +249,7 @@ ccl_device_inline void patch_eval_basis(KernelGlobals *kg, /* generic function for evaluating indices and weights from patch coords */ -ccl_device_inline int patch_eval_control_verts(KernelGlobals *kg, +ccl_device_inline int patch_eval_control_verts(const KernelGlobals *kg, int object, int patch, float u, @@ -269,7 +271,7 @@ ccl_device_inline int patch_eval_control_verts(KernelGlobals *kg, /* functions for evaluating attributes on patches */ -ccl_device float patch_eval_float(KernelGlobals *kg, +ccl_device float patch_eval_float(const KernelGlobals *kg, const ShaderData *sd, int offset, int patch, @@ -306,7 +308,7 @@ ccl_device float patch_eval_float(KernelGlobals *kg, return val; } -ccl_device float2 patch_eval_float2(KernelGlobals *kg, +ccl_device float2 patch_eval_float2(const KernelGlobals *kg, const ShaderData *sd, int offset, int patch, @@ -343,7 +345,7 @@ ccl_device float2 patch_eval_float2(KernelGlobals *kg, return val; } -ccl_device float3 patch_eval_float3(KernelGlobals *kg, +ccl_device float3 patch_eval_float3(const KernelGlobals *kg, const ShaderData *sd, int offset, int patch, @@ -380,7 +382,7 @@ ccl_device float3 patch_eval_float3(KernelGlobals *kg, return val; } -ccl_device float4 patch_eval_float4(KernelGlobals *kg, +ccl_device float4 patch_eval_float4(const KernelGlobals *kg, const ShaderData *sd, int offset, int patch, @@ -417,7 +419,7 @@ ccl_device float4 patch_eval_float4(KernelGlobals *kg, return val; } -ccl_device float4 patch_eval_uchar4(KernelGlobals *kg, +ccl_device float4 patch_eval_uchar4(const KernelGlobals *kg, const ShaderData *sd, int offset, int patch, diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h index aeb044c9ad3..ba31b12e817 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/geom_primitive.h @@ -19,6 +19,10 @@ * Generic functions to look up mesh, curve and volume primitive attributes for * shading and render passes. */ +#pragma once + +#include "kernel/kernel_projection.h" + CCL_NAMESPACE_BEGIN /* Surface Attributes @@ -27,8 +31,11 @@ CCL_NAMESPACE_BEGIN * attributes for performance, mainly for GPU performance to avoid bringing in * heavy volume interpolation code. */ -ccl_device_inline float primitive_surface_attribute_float( - KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) +ccl_device_inline float primitive_surface_attribute_float(const KernelGlobals *kg, + const ShaderData *sd, + const AttributeDescriptor desc, + float *dx, + float *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -50,7 +57,7 @@ ccl_device_inline float primitive_surface_attribute_float( } } -ccl_device_inline float2 primitive_surface_attribute_float2(KernelGlobals *kg, +ccl_device_inline float2 primitive_surface_attribute_float2(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float2 *dx, @@ -76,7 +83,7 @@ ccl_device_inline float2 primitive_surface_attribute_float2(KernelGlobals *kg, } } -ccl_device_inline float3 primitive_surface_attribute_float3(KernelGlobals *kg, +ccl_device_inline float3 primitive_surface_attribute_float3(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, @@ -102,11 +109,11 @@ ccl_device_inline float3 primitive_surface_attribute_float3(KernelGlobals *kg, } } -ccl_device_inline float4 primitive_surface_attribute_float4(KernelGlobals *kg, - const ShaderData *sd, - const AttributeDescriptor desc, - float4 *dx, - float4 *dy) +ccl_device_forceinline float4 primitive_surface_attribute_float4(const KernelGlobals *kg, + const ShaderData *sd, + const AttributeDescriptor desc, + float4 *dx, + float4 *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -141,7 +148,7 @@ ccl_device_inline bool primitive_is_volume_attribute(const ShaderData *sd, return sd->type == PRIMITIVE_VOLUME; } -ccl_device_inline float primitive_volume_attribute_float(KernelGlobals *kg, +ccl_device_inline float primitive_volume_attribute_float(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc) { @@ -153,7 +160,7 @@ ccl_device_inline float primitive_volume_attribute_float(KernelGlobals *kg, } } -ccl_device_inline float3 primitive_volume_attribute_float3(KernelGlobals *kg, +ccl_device_inline float3 primitive_volume_attribute_float3(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc) { @@ -165,7 +172,7 @@ ccl_device_inline float3 primitive_volume_attribute_float3(KernelGlobals *kg, } } -ccl_device_inline float4 primitive_volume_attribute_float4(KernelGlobals *kg, +ccl_device_inline float4 primitive_volume_attribute_float4(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc) { @@ -180,7 +187,7 @@ ccl_device_inline float4 primitive_volume_attribute_float4(KernelGlobals *kg, /* Default UV coordinate */ -ccl_device_inline float3 primitive_uv(KernelGlobals *kg, ShaderData *sd) +ccl_device_inline float3 primitive_uv(const KernelGlobals *kg, const ShaderData *sd) { const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_UV); @@ -193,7 +200,7 @@ ccl_device_inline float3 primitive_uv(KernelGlobals *kg, ShaderData *sd) /* Ptex coordinates */ -ccl_device bool primitive_ptex(KernelGlobals *kg, ShaderData *sd, float2 *uv, int *face_id) +ccl_device bool primitive_ptex(const KernelGlobals *kg, ShaderData *sd, float2 *uv, int *face_id) { /* storing ptex data as attributes is not memory efficient but simple for tests */ const AttributeDescriptor desc_face_id = find_attribute(kg, sd, ATTR_STD_PTEX_FACE_ID); @@ -213,7 +220,7 @@ ccl_device bool primitive_ptex(KernelGlobals *kg, ShaderData *sd, float2 *uv, in /* Surface tangent */ -ccl_device float3 primitive_tangent(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 primitive_tangent(const KernelGlobals *kg, ShaderData *sd) { #ifdef __HAIR__ if (sd->type & PRIMITIVE_ALL_CURVE) @@ -245,7 +252,7 @@ ccl_device float3 primitive_tangent(KernelGlobals *kg, ShaderData *sd) /* Motion vector for motion pass */ -ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData *sd) +ccl_device_inline float4 primitive_motion_vector(const KernelGlobals *kg, const ShaderData *sd) { /* center position */ float3 center; diff --git a/intern/cycles/kernel/geom/geom_shader_data.h b/intern/cycles/kernel/geom/geom_shader_data.h new file mode 100644 index 00000000000..fb2cb5cb1ea --- /dev/null +++ b/intern/cycles/kernel/geom/geom_shader_data.h @@ -0,0 +1,373 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Functions to initialize ShaderData given. + * + * Could be from an incoming ray, intersection or sampled position. */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* ShaderData setup from incoming ray */ + +#ifdef __OBJECT_MOTION__ +ccl_device void shader_setup_object_transforms(const KernelGlobals *ccl_restrict kg, + ShaderData *ccl_restrict sd, + float time) +{ + if (sd->object_flag & SD_OBJECT_MOTION) { + sd->ob_tfm_motion = object_fetch_transform_motion(kg, sd->object, time); + sd->ob_itfm_motion = transform_quick_inverse(sd->ob_tfm_motion); + } +} +#endif + +/* TODO: break this up if it helps reduce register pressure to load data from + * global memory as we write it to shaderdata. */ +ccl_device_inline void shader_setup_from_ray(const KernelGlobals *ccl_restrict kg, + ShaderData *ccl_restrict sd, + const Ray *ccl_restrict ray, + const Intersection *ccl_restrict isect) +{ + /* Read intersection data into shader globals. + * + * TODO: this is redundant, could potentially remove some of this from + * ShaderData but would need to ensure that it also works for shadow + * shader evaluation. */ + sd->u = isect->u; + sd->v = isect->v; + sd->ray_length = isect->t; + sd->type = isect->type; + sd->object = (isect->object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, isect->prim) : + isect->object; + sd->object_flag = kernel_tex_fetch(__object_flag, sd->object); + sd->prim = kernel_tex_fetch(__prim_index, isect->prim); + sd->lamp = LAMP_NONE; + sd->flag = 0; + + /* Read matrices and time. */ + sd->time = ray->time; + +#ifdef __OBJECT_MOTION__ + shader_setup_object_transforms(kg, sd, ray->time); +#endif + + /* Read ray data into shader globals. */ + sd->I = -ray->D; + +#ifdef __HAIR__ + if (sd->type & PRIMITIVE_ALL_CURVE) { + /* curve */ + curve_shader_setup(kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim); + } + else +#endif + if (sd->type & PRIMITIVE_TRIANGLE) { + /* static triangle */ + float3 Ng = triangle_normal(kg, sd); + sd->shader = kernel_tex_fetch(__tri_shader, sd->prim); + + /* vectors */ + sd->P = triangle_refine(kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim); + sd->Ng = Ng; + sd->N = Ng; + + /* smooth normal */ + if (sd->shader & SHADER_SMOOTH_NORMAL) + sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); + +#ifdef __DPDU__ + /* dPdu/dPdv */ + triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); +#endif + } + else { + /* motion triangle */ + motion_triangle_shader_setup( + kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim, false); + } + + sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; + + if (isect->object != OBJECT_NONE) { + /* instance transform */ + object_normal_transform_auto(kg, sd, &sd->N); + object_normal_transform_auto(kg, sd, &sd->Ng); +#ifdef __DPDU__ + object_dir_transform_auto(kg, sd, &sd->dPdu); + object_dir_transform_auto(kg, sd, &sd->dPdv); +#endif + } + + /* backfacing test */ + bool backfacing = (dot(sd->Ng, sd->I) < 0.0f); + + if (backfacing) { + sd->flag |= SD_BACKFACING; + sd->Ng = -sd->Ng; + sd->N = -sd->N; +#ifdef __DPDU__ + sd->dPdu = -sd->dPdu; + sd->dPdv = -sd->dPdv; +#endif + } + +#ifdef __RAY_DIFFERENTIALS__ + /* differentials */ + differential_transfer_compact(&sd->dP, ray->dP, ray->D, ray->dD, sd->Ng, sd->ray_length); + differential_incoming_compact(&sd->dI, ray->D, ray->dD); + differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng); +#endif +} + +/* ShaderData setup from position sampled on mesh */ + +ccl_device_inline void shader_setup_from_sample(const KernelGlobals *ccl_restrict kg, + ShaderData *ccl_restrict sd, + const float3 P, + const float3 Ng, + const float3 I, + int shader, + int object, + int prim, + float u, + float v, + float t, + float time, + bool object_space, + int lamp) +{ + /* vectors */ + sd->P = P; + sd->N = Ng; + sd->Ng = Ng; + sd->I = I; + sd->shader = shader; + if (prim != PRIM_NONE) + sd->type = PRIMITIVE_TRIANGLE; + else if (lamp != LAMP_NONE) + sd->type = PRIMITIVE_LAMP; + else + sd->type = PRIMITIVE_NONE; + + /* primitive */ + sd->object = object; + sd->lamp = LAMP_NONE; + /* Currently no access to bvh prim index for strand sd->prim. */ + sd->prim = prim; + sd->u = u; + sd->v = v; + sd->time = time; + sd->ray_length = t; + + sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; + sd->object_flag = 0; + if (sd->object != OBJECT_NONE) { + sd->object_flag |= kernel_tex_fetch(__object_flag, sd->object); + +#ifdef __OBJECT_MOTION__ + shader_setup_object_transforms(kg, sd, time); +#endif + } + else if (lamp != LAMP_NONE) { + sd->lamp = lamp; + } + + /* transform into world space */ + if (object_space) { + object_position_transform_auto(kg, sd, &sd->P); + object_normal_transform_auto(kg, sd, &sd->Ng); + sd->N = sd->Ng; + object_dir_transform_auto(kg, sd, &sd->I); + } + + if (sd->type & PRIMITIVE_TRIANGLE) { + /* smooth normal */ + if (sd->shader & SHADER_SMOOTH_NORMAL) { + sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); + + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + object_normal_transform_auto(kg, sd, &sd->N); + } + } + + /* dPdu/dPdv */ +#ifdef __DPDU__ + triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); + + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + object_dir_transform_auto(kg, sd, &sd->dPdu); + object_dir_transform_auto(kg, sd, &sd->dPdv); + } +#endif + } + else { +#ifdef __DPDU__ + sd->dPdu = zero_float3(); + sd->dPdv = zero_float3(); +#endif + } + + /* backfacing test */ + if (sd->prim != PRIM_NONE) { + bool backfacing = (dot(sd->Ng, sd->I) < 0.0f); + + if (backfacing) { + sd->flag |= SD_BACKFACING; + sd->Ng = -sd->Ng; + sd->N = -sd->N; +#ifdef __DPDU__ + sd->dPdu = -sd->dPdu; + sd->dPdv = -sd->dPdv; +#endif + } + } + +#ifdef __RAY_DIFFERENTIALS__ + /* no ray differentials here yet */ + sd->dP = differential3_zero(); + sd->dI = differential3_zero(); + sd->du = differential_zero(); + sd->dv = differential_zero(); +#endif +} + +/* ShaderData setup for displacement */ + +ccl_device void shader_setup_from_displace(const KernelGlobals *ccl_restrict kg, + ShaderData *ccl_restrict sd, + int object, + int prim, + float u, + float v) +{ + float3 P, Ng, I = zero_float3(); + int shader; + + triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader); + + /* force smooth shading for displacement */ + shader |= SHADER_SMOOTH_NORMAL; + + shader_setup_from_sample( + kg, + sd, + P, + Ng, + I, + shader, + object, + prim, + u, + v, + 0.0f, + 0.5f, + !(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED), + LAMP_NONE); +} + +/* ShaderData setup from ray into background */ + +ccl_device_inline void shader_setup_from_background(const KernelGlobals *ccl_restrict kg, + ShaderData *ccl_restrict sd, + const float3 ray_P, + const float3 ray_D, + const float ray_time) +{ + /* for NDC coordinates */ + sd->ray_P = ray_P; + + /* vectors */ + sd->P = ray_D; + sd->N = -ray_D; + sd->Ng = -ray_D; + sd->I = -ray_D; + sd->shader = kernel_data.background.surface_shader; + sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; + sd->object_flag = 0; + sd->time = ray_time; + sd->ray_length = 0.0f; + + sd->object = OBJECT_NONE; + sd->lamp = LAMP_NONE; + sd->prim = PRIM_NONE; + sd->u = 0.0f; + sd->v = 0.0f; + +#ifdef __DPDU__ + /* dPdu/dPdv */ + sd->dPdu = zero_float3(); + sd->dPdv = zero_float3(); +#endif + +#ifdef __RAY_DIFFERENTIALS__ + /* differentials */ + sd->dP = differential3_zero(); /* TODO: ray->dP */ + differential_incoming(&sd->dI, sd->dP); + sd->du = differential_zero(); + sd->dv = differential_zero(); +#endif +} + +/* ShaderData setup from point inside volume */ + +#ifdef __VOLUME__ +ccl_device_inline void shader_setup_from_volume(const KernelGlobals *ccl_restrict kg, + ShaderData *ccl_restrict sd, + const Ray *ccl_restrict ray) +{ + + /* vectors */ + sd->P = ray->P; + sd->N = -ray->D; + sd->Ng = -ray->D; + sd->I = -ray->D; + sd->shader = SHADER_NONE; + sd->flag = 0; + sd->object_flag = 0; + sd->time = ray->time; + sd->ray_length = 0.0f; /* todo: can we set this to some useful value? */ + + sd->object = OBJECT_NONE; /* todo: fill this for texture coordinates */ + sd->lamp = LAMP_NONE; + sd->prim = PRIM_NONE; + sd->type = PRIMITIVE_VOLUME; + + sd->u = 0.0f; + sd->v = 0.0f; + +# ifdef __DPDU__ + /* dPdu/dPdv */ + sd->dPdu = zero_float3(); + sd->dPdv = zero_float3(); +# endif + +# ifdef __RAY_DIFFERENTIALS__ + /* differentials */ + sd->dP = differential3_zero(); /* TODO ray->dD */ + differential_incoming(&sd->dI, sd->dP); + sd->du = differential_zero(); + sd->dv = differential_zero(); +# endif + + /* for NDC coordinates */ + sd->ray_P = ray->P; + sd->ray_dP = ray->dP; +} +#endif /* __VOLUME__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/geom/geom_subd_triangle.h b/intern/cycles/kernel/geom/geom_subd_triangle.h index 9eceb996926..877b2ece15b 100644 --- a/intern/cycles/kernel/geom/geom_subd_triangle.h +++ b/intern/cycles/kernel/geom/geom_subd_triangle.h @@ -16,18 +16,20 @@ /* Functions for retrieving attributes on triangles produced from subdivision meshes */ +#pragma once + CCL_NAMESPACE_BEGIN /* Patch index for triangle, -1 if not subdivision triangle */ -ccl_device_inline uint subd_triangle_patch(KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline uint subd_triangle_patch(const KernelGlobals *kg, const ShaderData *sd) { return (sd->prim != PRIM_NONE) ? kernel_tex_fetch(__tri_patch, sd->prim) : ~0; } /* UV coords of triangle within patch */ -ccl_device_inline void subd_triangle_patch_uv(KernelGlobals *kg, +ccl_device_inline void subd_triangle_patch_uv(const KernelGlobals *kg, const ShaderData *sd, float2 uv[3]) { @@ -40,7 +42,7 @@ ccl_device_inline void subd_triangle_patch_uv(KernelGlobals *kg, /* Vertex indices of patch */ -ccl_device_inline uint4 subd_triangle_patch_indices(KernelGlobals *kg, int patch) +ccl_device_inline uint4 subd_triangle_patch_indices(const KernelGlobals *kg, int patch) { uint4 indices; @@ -54,21 +56,23 @@ ccl_device_inline uint4 subd_triangle_patch_indices(KernelGlobals *kg, int patch /* Originating face for patch */ -ccl_device_inline uint subd_triangle_patch_face(KernelGlobals *kg, int patch) +ccl_device_inline uint subd_triangle_patch_face(const KernelGlobals *kg, int patch) { return kernel_tex_fetch(__patches, patch + 4); } /* Number of corners on originating face */ -ccl_device_inline uint subd_triangle_patch_num_corners(KernelGlobals *kg, int patch) +ccl_device_inline uint subd_triangle_patch_num_corners(const KernelGlobals *kg, int patch) { return kernel_tex_fetch(__patches, patch + 5) & 0xffff; } /* Indices of the four corners that are used by the patch */ -ccl_device_inline void subd_triangle_patch_corners(KernelGlobals *kg, int patch, int corners[4]) +ccl_device_inline void subd_triangle_patch_corners(const KernelGlobals *kg, + int patch, + int corners[4]) { uint4 data; @@ -99,8 +103,11 @@ ccl_device_inline void subd_triangle_patch_corners(KernelGlobals *kg, int patch, /* Reading attributes on various subdivision triangle elements */ -ccl_device_noinline float subd_triangle_attribute_float( - KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) +ccl_device_noinline float subd_triangle_attribute_float(const KernelGlobals *kg, + const ShaderData *sd, + const AttributeDescriptor desc, + float *dx, + float *dy) { int patch = subd_triangle_patch(kg, sd); @@ -235,7 +242,7 @@ ccl_device_noinline float subd_triangle_attribute_float( } } -ccl_device_noinline float2 subd_triangle_attribute_float2(KernelGlobals *kg, +ccl_device_noinline float2 subd_triangle_attribute_float2(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float2 *dx, @@ -378,7 +385,7 @@ ccl_device_noinline float2 subd_triangle_attribute_float2(KernelGlobals *kg, } } -ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals *kg, +ccl_device_noinline float3 subd_triangle_attribute_float3(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, @@ -520,7 +527,7 @@ ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals *kg, } } -ccl_device_noinline float4 subd_triangle_attribute_float4(KernelGlobals *kg, +ccl_device_noinline float4 subd_triangle_attribute_float4(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float4 *dx, diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/geom_triangle.h index ff7909ca425..910fb122c6d 100644 --- a/intern/cycles/kernel/geom/geom_triangle.h +++ b/intern/cycles/kernel/geom/geom_triangle.h @@ -20,10 +20,12 @@ * ray intersection we use a precomputed triangle storage to accelerate * intersection at the cost of more memory usage */ +#pragma once + CCL_NAMESPACE_BEGIN /* Normal on triangle. */ -ccl_device_inline float3 triangle_normal(KernelGlobals *kg, ShaderData *sd) +ccl_device_inline float3 triangle_normal(const KernelGlobals *kg, ShaderData *sd) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, sd->prim); @@ -41,8 +43,14 @@ ccl_device_inline float3 triangle_normal(KernelGlobals *kg, ShaderData *sd) } /* Point and normal on triangle. */ -ccl_device_inline void triangle_point_normal( - KernelGlobals *kg, int object, int prim, float u, float v, float3 *P, float3 *Ng, int *shader) +ccl_device_inline void triangle_point_normal(const KernelGlobals *kg, + int object, + int prim, + float u, + float v, + float3 *P, + float3 *Ng, + int *shader) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -67,7 +75,7 @@ ccl_device_inline void triangle_point_normal( /* Triangle vertex locations */ -ccl_device_inline void triangle_vertices(KernelGlobals *kg, int prim, float3 P[3]) +ccl_device_inline void triangle_vertices(const KernelGlobals *kg, int prim, float3 P[3]) { const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); P[0] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); @@ -77,7 +85,7 @@ ccl_device_inline void triangle_vertices(KernelGlobals *kg, int prim, float3 P[3 /* Triangle vertex locations and vertex normals */ -ccl_device_inline void triangle_vertices_and_normals(KernelGlobals *kg, +ccl_device_inline void triangle_vertices_and_normals(const KernelGlobals *kg, int prim, float3 P[3], float3 N[3]) @@ -94,7 +102,7 @@ ccl_device_inline void triangle_vertices_and_normals(KernelGlobals *kg, /* Interpolate smooth vertex normal from vertices */ ccl_device_inline float3 -triangle_smooth_normal(KernelGlobals *kg, float3 Ng, int prim, float u, float v) +triangle_smooth_normal(const KernelGlobals *kg, float3 Ng, int prim, float u, float v) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -108,7 +116,7 @@ triangle_smooth_normal(KernelGlobals *kg, float3 Ng, int prim, float u, float v) } ccl_device_inline float3 triangle_smooth_normal_unnormalized( - KernelGlobals *kg, ShaderData *sd, float3 Ng, int prim, float u, float v) + const KernelGlobals *kg, const ShaderData *sd, float3 Ng, int prim, float u, float v) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -130,7 +138,7 @@ ccl_device_inline float3 triangle_smooth_normal_unnormalized( /* Ray differentials on triangle */ -ccl_device_inline void triangle_dPdudv(KernelGlobals *kg, +ccl_device_inline void triangle_dPdudv(const KernelGlobals *kg, int prim, ccl_addr_space float3 *dPdu, ccl_addr_space float3 *dPdv) @@ -148,8 +156,11 @@ ccl_device_inline void triangle_dPdudv(KernelGlobals *kg, /* Reading attributes on various triangle elements */ -ccl_device float triangle_attribute_float( - KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) +ccl_device float triangle_attribute_float(const KernelGlobals *kg, + const ShaderData *sd, + const AttributeDescriptor desc, + float *dx, + float *dy) { if (desc.element & (ATTR_ELEMENT_VERTEX | ATTR_ELEMENT_VERTEX_MOTION | ATTR_ELEMENT_CORNER)) { float f0, f1, f2; @@ -195,7 +206,7 @@ ccl_device float triangle_attribute_float( } } -ccl_device float2 triangle_attribute_float2(KernelGlobals *kg, +ccl_device float2 triangle_attribute_float2(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float2 *dx, @@ -245,7 +256,7 @@ ccl_device float2 triangle_attribute_float2(KernelGlobals *kg, } } -ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, +ccl_device float3 triangle_attribute_float3(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, @@ -295,7 +306,7 @@ ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, } } -ccl_device float4 triangle_attribute_float4(KernelGlobals *kg, +ccl_device float4 triangle_attribute_float4(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float4 *dx, diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h index b0cce274b94..30b77ebd2eb 100644 --- a/intern/cycles/kernel/geom/geom_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h @@ -20,12 +20,17 @@ * intersection at the cost of more memory usage. */ +#pragma once + +#include "kernel/kernel_random.h" + CCL_NAMESPACE_BEGIN -ccl_device_inline bool triangle_intersect(KernelGlobals *kg, +ccl_device_inline bool triangle_intersect(const KernelGlobals *kg, Intersection *isect, float3 P, float3 dir, + float tmax, uint visibility, int object, int prim_addr) @@ -41,7 +46,7 @@ ccl_device_inline bool triangle_intersect(KernelGlobals *kg, float t, u, v; if (ray_triangle_intersect(P, dir, - isect->t, + tmax, #if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) ssef_verts, #else @@ -78,7 +83,7 @@ ccl_device_inline bool triangle_intersect(KernelGlobals *kg, */ #ifdef __BVH_LOCAL__ -ccl_device_inline bool triangle_intersect_local(KernelGlobals *kg, +ccl_device_inline bool triangle_intersect_local(const KernelGlobals *kg, LocalIntersection *local_isect, float3 P, float3 dir, @@ -192,25 +197,20 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals *kg, * http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf */ -ccl_device_inline float3 triangle_refine(KernelGlobals *kg, +ccl_device_inline float3 triangle_refine(const KernelGlobals *kg, ShaderData *sd, - const Intersection *isect, - const Ray *ray) + float3 P, + float3 D, + float t, + const int isect_object, + const int isect_prim) { - float3 P = ray->P; - float3 D = ray->D; - float t = isect->t; - #ifdef __INTERSECTION_REFINE__ - if (isect->object != OBJECT_NONE) { + if (isect_object != OBJECT_NONE) { if (UNLIKELY(t == 0.0f)) { return P; } -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif + const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); D = transform_direction(&tfm, D * t); @@ -219,7 +219,7 @@ ccl_device_inline float3 triangle_refine(KernelGlobals *kg, P = P + D * t; - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect->prim); + const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect_prim); const float4 tri_a = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0), tri_b = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1), tri_c = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2); @@ -239,13 +239,8 @@ ccl_device_inline float3 triangle_refine(KernelGlobals *kg, P = P + D * rt; } - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - + if (isect_object != OBJECT_NONE) { + const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } @@ -255,28 +250,23 @@ ccl_device_inline float3 triangle_refine(KernelGlobals *kg, #endif } -/* Same as above, except that isect->t is assumed to be in object space for +/* Same as above, except that t is assumed to be in object space for * instancing. */ -ccl_device_inline float3 triangle_refine_local(KernelGlobals *kg, +ccl_device_inline float3 triangle_refine_local(const KernelGlobals *kg, ShaderData *sd, - const Intersection *isect, - const Ray *ray) + float3 P, + float3 D, + float t, + const int isect_object, + const int isect_prim) { #ifdef __KERNEL_OPTIX__ - /* isect->t is always in world space with OptiX. */ - return triangle_refine(kg, sd, isect, ray); + /* t is always in world space with OptiX. */ + return triangle_refine(kg, sd, P, D, t, isect_object, isect_prim); #else - float3 P = ray->P; - float3 D = ray->D; - float t = isect->t; - - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif + if (isect_object != OBJECT_NONE) { + const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); D = transform_direction(&tfm, D); @@ -286,7 +276,7 @@ ccl_device_inline float3 triangle_refine_local(KernelGlobals *kg, P = P + D * t; # ifdef __INTERSECTION_REFINE__ - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect->prim); + const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect_prim); const float4 tri_a = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0), tri_b = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1), tri_c = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2); @@ -307,13 +297,8 @@ ccl_device_inline float3 triangle_refine_local(KernelGlobals *kg, } # endif /* __INTERSECTION_REFINE__ */ - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - + if (isect_object != OBJECT_NONE) { + const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h index 809b76245ba..2bcd7e56b5f 100644 --- a/intern/cycles/kernel/geom/geom_volume.h +++ b/intern/cycles/kernel/geom/geom_volume.h @@ -23,13 +23,15 @@ * 3D voxel textures can be assigned as attributes per mesh, which means the * same shader can be used for volume objects with different densities, etc. */ +#pragma once + CCL_NAMESPACE_BEGIN #ifdef __VOLUME__ /* Return position normalized to 0..1 in mesh bounds */ -ccl_device_inline float3 volume_normalized_position(KernelGlobals *kg, +ccl_device_inline float3 volume_normalized_position(const KernelGlobals *kg, const ShaderData *sd, float3 P) { @@ -68,7 +70,7 @@ ccl_device float3 volume_attribute_value_to_float3(const float4 value) } } -ccl_device float4 volume_attribute_float4(KernelGlobals *kg, +ccl_device float4 volume_attribute_float4(const KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc) { diff --git a/intern/cycles/kernel/integrator/integrator_init_from_bake.h b/intern/cycles/kernel/integrator/integrator_init_from_bake.h new file mode 100644 index 00000000000..4898ff936c6 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_init_from_bake.h @@ -0,0 +1,181 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/kernel_accumulate.h" +#include "kernel/kernel_adaptive_sampling.h" +#include "kernel/kernel_camera.h" +#include "kernel/kernel_path_state.h" +#include "kernel/kernel_random.h" + +#include "kernel/geom/geom.h" + +CCL_NAMESPACE_BEGIN + +/* This helps with AA but it's not the real solution as it does not AA the geometry + * but it's better than nothing, thus committed. */ +ccl_device_inline float bake_clamp_mirror_repeat(float u, float max) +{ + /* use mirror repeat (like opengl texture) so that if the barycentric + * coordinate goes past the end of the triangle it is not always clamped + * to the same value, gives ugly patterns */ + u /= max; + float fu = floorf(u); + u = u - fu; + + return ((((int)fu) & 1) ? 1.0f - u : u) * max; +} + +/* Return false to indicate that this pixel is finished. + * Used by CPU implementation to not attempt to sample pixel for multiple samples once its known + * that the pixel did converge. */ +ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, + const ccl_global KernelWorkTile *ccl_restrict tile, + ccl_global float *render_buffer, + const int x, + const int y, + const int scheduled_sample) +{ + PROFILING_INIT(kg, PROFILING_RAY_SETUP); + + /* Initialize path state to give basic buffer access and allow early outputs. */ + path_state_init(INTEGRATOR_STATE_PASS, tile, x, y); + + /* Check whether the pixel has converged and should not be sampled anymore. */ + if (!kernel_need_sample_pixel(INTEGRATOR_STATE_PASS, render_buffer)) { + return false; + } + + /* Always count the sample, even if the camera sample will reject the ray. */ + const int sample = kernel_accum_sample(INTEGRATOR_STATE_PASS, render_buffer, scheduled_sample); + + /* Setup render buffers. */ + const int index = INTEGRATOR_STATE(path, render_pixel_index); + const int pass_stride = kernel_data.film.pass_stride; + render_buffer += index * pass_stride; + + ccl_global float *primitive = render_buffer + kernel_data.film.pass_bake_primitive; + ccl_global float *differential = render_buffer + kernel_data.film.pass_bake_differential; + + const int seed = __float_as_uint(primitive[0]); + int prim = __float_as_uint(primitive[1]); + if (prim == -1) { + return false; + } + + prim += kernel_data.bake.tri_offset; + + /* Random number generator. */ + const uint rng_hash = hash_uint(seed) ^ kernel_data.integrator.seed; + + float filter_x, filter_y; + if (sample == 0) { + filter_x = filter_y = 0.5f; + } + else { + path_rng_2D(kg, rng_hash, sample, PRNG_FILTER_U, &filter_x, &filter_y); + } + + /* Initialize path state for path integration. */ + path_state_init_integrator(INTEGRATOR_STATE_PASS, sample, rng_hash); + + /* Barycentric UV with sub-pixel offset. */ + float u = primitive[2]; + float v = primitive[3]; + + float dudx = differential[0]; + float dudy = differential[1]; + float dvdx = differential[2]; + float dvdy = differential[3]; + + if (sample > 0) { + u = bake_clamp_mirror_repeat(u + dudx * (filter_x - 0.5f) + dudy * (filter_y - 0.5f), 1.0f); + v = bake_clamp_mirror_repeat(v + dvdx * (filter_x - 0.5f) + dvdy * (filter_y - 0.5f), + 1.0f - u); + } + + /* Position and normal on triangle. */ + float3 P, Ng; + int shader; + triangle_point_normal(kg, kernel_data.bake.object_index, prim, u, v, &P, &Ng, &shader); + shader &= SHADER_MASK; + + if (kernel_data.film.pass_background != PASS_UNUSED) { + /* Environment baking. */ + + /* Setup and write ray. */ + Ray ray ccl_optional_struct_init; + ray.P = zero_float3(); + ray.D = normalize(P); + ray.t = FLT_MAX; + ray.time = 0.5f; + ray.dP = differential_zero_compact(); + ray.dD = differential_zero_compact(); + integrator_state_write_ray(INTEGRATOR_STATE_PASS, &ray); + + /* Setup next kernel to execute. */ + INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + } + else { + /* Surface baking. */ + + /* Setup ray. */ + Ray ray ccl_optional_struct_init; + ray.P = P + Ng; + ray.D = -Ng; + ray.t = FLT_MAX; + ray.time = 0.5f; + + /* Setup differentials. */ + float3 dPdu, dPdv; + triangle_dPdudv(kg, prim, &dPdu, &dPdv); + differential3 dP; + dP.dx = dPdu * dudx + dPdv * dvdx; + dP.dy = dPdu * dudy + dPdv * dvdy; + ray.dP = differential_make_compact(dP); + ray.dD = differential_zero_compact(); + + /* Write ray. */ + integrator_state_write_ray(INTEGRATOR_STATE_PASS, &ray); + + /* Setup and write intersection. */ + Intersection isect ccl_optional_struct_init; + isect.object = kernel_data.bake.object_index; + isect.prim = prim; + isect.u = u; + isect.v = v; + isect.t = 1.0f; + isect.type = PRIMITIVE_TRIANGLE; +#ifdef __EMBREE__ + isect.Ng = Ng; +#endif + integrator_state_write_isect(INTEGRATOR_STATE_PASS, &isect); + + /* Setup next kernel to execute. */ + const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; + if ((shader_flags & SD_HAS_RAYTRACE) || (kernel_data.film.pass_ao != PASS_UNUSED)) { + INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + } + else { + INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + } + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_init_from_camera.h b/intern/cycles/kernel/integrator/integrator_init_from_camera.h new file mode 100644 index 00000000000..58e7bde4c94 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_init_from_camera.h @@ -0,0 +1,120 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/kernel_accumulate.h" +#include "kernel/kernel_adaptive_sampling.h" +#include "kernel/kernel_camera.h" +#include "kernel/kernel_path_state.h" +#include "kernel/kernel_random.h" +#include "kernel/kernel_shadow_catcher.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_inline void integrate_camera_sample(const KernelGlobals *ccl_restrict kg, + const int sample, + const int x, + const int y, + const uint rng_hash, + Ray *ray) +{ + /* Filter sampling. */ + float filter_u, filter_v; + + if (sample == 0) { + filter_u = 0.5f; + filter_v = 0.5f; + } + else { + path_rng_2D(kg, rng_hash, sample, PRNG_FILTER_U, &filter_u, &filter_v); + } + + /* Depth of field sampling. */ + float lens_u = 0.0f, lens_v = 0.0f; + if (kernel_data.cam.aperturesize > 0.0f) { + path_rng_2D(kg, rng_hash, sample, PRNG_LENS_U, &lens_u, &lens_v); + } + + /* Motion blur time sampling. */ + float time = 0.0f; +#ifdef __CAMERA_MOTION__ + if (kernel_data.cam.shuttertime != -1.0f) + time = path_rng_1D(kg, rng_hash, sample, PRNG_TIME); +#endif + + /* Generate camera ray. */ + camera_sample(kg, x, y, filter_u, filter_v, lens_u, lens_v, time, ray); +} + +/* Return false to indicate that this pixel is finished. + * Used by CPU implementation to not attempt to sample pixel for multiple samples once its known + * that the pixel did converge. */ +ccl_device bool integrator_init_from_camera(INTEGRATOR_STATE_ARGS, + const ccl_global KernelWorkTile *ccl_restrict tile, + ccl_global float *render_buffer, + const int x, + const int y, + const int scheduled_sample) +{ + PROFILING_INIT(kg, PROFILING_RAY_SETUP); + + /* Initialize path state to give basic buffer access and allow early outputs. */ + path_state_init(INTEGRATOR_STATE_PASS, tile, x, y); + + /* Check whether the pixel has converged and should not be sampled anymore. */ + if (!kernel_need_sample_pixel(INTEGRATOR_STATE_PASS, render_buffer)) { + return false; + } + + /* Count the sample and get an effective sample for this pixel. + * + * This logic allows to both count actual number of samples per pixel, and to add samples to this + * pixel after it was converged and samples were added somewhere else (in which case the + * `scheduled_sample` will be different from actual number of samples in this pixel). */ + const int sample = kernel_accum_sample(INTEGRATOR_STATE_PASS, render_buffer, scheduled_sample); + + /* Initialize random number seed for path. */ + const uint rng_hash = path_rng_hash_init(kg, sample, x, y); + + { + /* Generate camera ray. */ + Ray ray; + integrate_camera_sample(kg, sample, x, y, rng_hash, &ray); + if (ray.t == 0.0f) { + return true; + } + + /* Write camera ray to state. */ + integrator_state_write_ray(INTEGRATOR_STATE_PASS, &ray); + } + + /* Initialize path state for path integration. */ + path_state_init_integrator(INTEGRATOR_STATE_PASS, sample, rng_hash); + + /* Continue with intersect_closest kernel, optionally initializing volume + * stack before that if the camera may be inside a volume. */ + if (kernel_data.cam.is_inside_volume) { + INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); + } + else { + INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/integrator_intersect_closest.h new file mode 100644 index 00000000000..34ca6814534 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_intersect_closest.h @@ -0,0 +1,248 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/kernel_differential.h" +#include "kernel/kernel_light.h" +#include "kernel/kernel_path_state.h" +#include "kernel/kernel_projection.h" +#include "kernel/kernel_shadow_catcher.h" + +#include "kernel/geom/geom.h" + +#include "kernel/bvh/bvh.h" + +CCL_NAMESPACE_BEGIN + +template +ccl_device_forceinline bool integrator_intersect_terminate(INTEGRATOR_STATE_ARGS, + const int shader_flags) +{ + + /* Optional AO bounce termination. + * We continue evaluating emissive/transparent surfaces and volumes, similar + * to direct lighting. Only if we know there are none can we terminate the + * path immediately. */ + if (path_state_ao_bounce(INTEGRATOR_STATE_PASS)) { + if (shader_flags & (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) { + INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + } + else if (!integrator_state_volume_stack_is_empty(INTEGRATOR_STATE_PASS)) { + INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_TERMINATE_AFTER_VOLUME; + } + else { + return true; + } + } + + /* Load random number state. */ + RNGState rng_state; + path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + + /* We perform path termination in this kernel to avoid launching shade_surface + * and evaluating the shader when not needed. Only for emission and transparent + * surfaces in front of emission do we need to evaluate the shader, since we + * perform MIS as part of indirect rays. */ + const int path_flag = INTEGRATOR_STATE(path, flag); + const float probability = path_state_continuation_probability(INTEGRATOR_STATE_PASS, path_flag); + + if (probability != 1.0f) { + const float terminate = path_state_rng_1D(kg, &rng_state, PRNG_TERMINATE); + + if (probability == 0.0f || terminate >= probability) { + if (shader_flags & SD_HAS_EMISSION) { + /* Mark path to be terminated right after shader evaluation on the surface. */ + INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_TERMINATE_ON_NEXT_SURFACE; + } + else if (!integrator_state_volume_stack_is_empty(INTEGRATOR_STATE_PASS)) { + /* TODO: only do this for emissive volumes. */ + INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_TERMINATE_IN_NEXT_VOLUME; + } + else { + return true; + } + } + } + + return false; +} + +/* Note that current_kernel is a template value since making this a variable + * leads to poor performance with CUDA atomics. */ +template +ccl_device_forceinline void integrator_intersect_shader_next_kernel( + INTEGRATOR_STATE_ARGS, + const Intersection *ccl_restrict isect, + const int shader, + const int shader_flags) +{ + /* Note on scheduling. + * + * When there is no shadow catcher split the scheduling is simple: schedule surface shading with + * or without raytrace support, depending on the shader used. + * + * When there is a shadow catcher split the general idea is to have the following configuration: + * + * - Schedule surface shading kernel (with corresponding raytrace support) for the ray which + * will trace shadow catcher object. + * + * - When no alpha-over of approximate shadow catcher is needed, schedule surface shading for + * the matte ray. + * + * - Otherwise schedule background shading kernel, so that we have a background to alpha-over + * on. The background kernel will then schedule surface shading for the matte ray. + * + * Note that the splitting leaves kernel and sorting counters as-is, so use INIT semantic for + * the matte path. */ + + const bool use_raytrace_kernel = ((shader_flags & SD_HAS_RAYTRACE) || + (kernel_data.film.pass_ao != PASS_UNUSED)); + + if (use_raytrace_kernel) { + INTEGRATOR_PATH_NEXT_SORTED( + current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + } + else { + INTEGRATOR_PATH_NEXT_SORTED(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + } + +#ifdef __SHADOW_CATCHER__ + const int object_flags = intersection_get_object_flags(kg, isect); + if (kernel_shadow_catcher_split(INTEGRATOR_STATE_PASS, object_flags)) { + if (kernel_data.film.use_approximate_shadow_catcher && !kernel_data.background.transparent) { + INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_SHADOW_CATCHER_BACKGROUND; + + if (use_raytrace_kernel) { + INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + } + else { + INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + } + } + else if (use_raytrace_kernel) { + INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + } + else { + INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + } + } +#endif +} + +ccl_device void integrator_intersect_closest(INTEGRATOR_STATE_ARGS) +{ + PROFILING_INIT(kg, PROFILING_INTERSECT_CLOSEST); + + /* Read ray from integrator state into local memory. */ + Ray ray ccl_optional_struct_init; + integrator_state_read_ray(INTEGRATOR_STATE_PASS, &ray); + kernel_assert(ray.t != 0.0f); + + const uint visibility = path_state_ray_visibility(INTEGRATOR_STATE_PASS); + const int last_isect_prim = INTEGRATOR_STATE(isect, prim); + const int last_isect_object = INTEGRATOR_STATE(isect, object); + + /* Trick to use short AO rays to approximate indirect light at the end of the path. */ + if (path_state_ao_bounce(INTEGRATOR_STATE_PASS)) { + ray.t = kernel_data.integrator.ao_bounces_distance; + + const int last_object = last_isect_object != OBJECT_NONE ? + last_isect_object : + kernel_tex_fetch(__prim_object, last_isect_prim); + const float object_ao_distance = kernel_tex_fetch(__objects, last_object).ao_distance; + if (object_ao_distance != 0.0f) { + ray.t = object_ao_distance; + } + } + + /* Scene Intersection. */ + Intersection isect ccl_optional_struct_init; + bool hit = scene_intersect(kg, &ray, visibility, &isect); + + /* TODO: remove this and do it in the various intersection functions instead. */ + if (!hit) { + isect.prim = PRIM_NONE; + } + + /* Light intersection for MIS. */ + if (kernel_data.integrator.use_lamp_mis) { + /* NOTE: if we make lights visible to camera rays, we'll need to initialize + * these in the path_state_init. */ + const int last_type = INTEGRATOR_STATE(isect, type); + const int path_flag = INTEGRATOR_STATE(path, flag); + + hit = lights_intersect( + kg, &ray, &isect, last_isect_prim, last_isect_object, last_type, path_flag) || + hit; + } + + /* Write intersection result into global integrator state memory. */ + integrator_state_write_isect(INTEGRATOR_STATE_PASS, &isect); + +#ifdef __VOLUME__ + if (!integrator_state_volume_stack_is_empty(INTEGRATOR_STATE_PASS)) { + const bool hit_surface = hit && !(isect.type & PRIMITIVE_LAMP); + const int shader = (hit_surface) ? intersection_get_shader(kg, &isect) : SHADER_NONE; + const int flags = (hit_surface) ? kernel_tex_fetch(__shaders, shader).flags : 0; + + if (!integrator_intersect_terminate( + INTEGRATOR_STATE_PASS, flags)) { + /* Continue with volume kernel if we are inside a volume, regardless + * if we hit anything. */ + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST, + DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); + } + else { + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + } + return; + } +#endif + + if (hit) { + /* Hit a surface, continue with light or surface kernel. */ + if (isect.type & PRIMITIVE_LAMP) { + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST, + DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + return; + } + else { + /* Hit a surface, continue with surface kernel unless terminated. */ + const int shader = intersection_get_shader(kg, &isect); + const int flags = kernel_tex_fetch(__shaders, shader).flags; + + if (!integrator_intersect_terminate( + INTEGRATOR_STATE_PASS, flags)) { + integrator_intersect_shader_next_kernel( + INTEGRATOR_STATE_PASS, &isect, shader, flags); + return; + } + else { + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + return; + } + } + } + else { + /* Nothing hit, continue with background kernel. */ + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST, + DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + return; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h new file mode 100644 index 00000000000..5bd9cfda4a4 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h @@ -0,0 +1,144 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Visibility for the shadow ray. */ +ccl_device_forceinline uint integrate_intersect_shadow_visibility(INTEGRATOR_STATE_CONST_ARGS) +{ + uint visibility = PATH_RAY_SHADOW; + +#ifdef __SHADOW_CATCHER__ + const uint32_t path_flag = INTEGRATOR_STATE(shadow_path, flag); + visibility = SHADOW_CATCHER_PATH_VISIBILITY(path_flag, visibility); +#endif + + return visibility; +} + +ccl_device bool integrate_intersect_shadow_opaque(INTEGRATOR_STATE_ARGS, + const Ray *ray, + const uint visibility) +{ + /* Mask which will pick only opaque visibility bits from the `visibility`. + * Calculate the mask at compile time: the visibility will either be a high bits for the shadow + * catcher objects, or lower bits for the regular objects (there is no need to check the path + * state here again). */ + constexpr const uint opaque_mask = SHADOW_CATCHER_VISIBILITY_SHIFT(PATH_RAY_SHADOW_OPAQUE) | + PATH_RAY_SHADOW_OPAQUE; + + Intersection isect; + const bool opaque_hit = scene_intersect(kg, ray, visibility & opaque_mask, &isect); + + if (!opaque_hit) { + INTEGRATOR_STATE_WRITE(shadow_path, num_hits) = 0; + } + + return opaque_hit; +} + +ccl_device_forceinline int integrate_shadow_max_transparent_hits(INTEGRATOR_STATE_CONST_ARGS) +{ + const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce; + const int transparent_bounce = INTEGRATOR_STATE(shadow_path, transparent_bounce); + + return max(transparent_max_bounce - transparent_bounce - 1, 0); +} + +#ifdef __TRANSPARENT_SHADOWS__ +ccl_device bool integrate_intersect_shadow_transparent(INTEGRATOR_STATE_ARGS, + const Ray *ray, + const uint visibility) +{ + Intersection isect[INTEGRATOR_SHADOW_ISECT_SIZE]; + + /* Limit the number hits to the max transparent bounces allowed and the size that we + * have available in the integrator state. */ + const uint max_transparent_hits = integrate_shadow_max_transparent_hits(INTEGRATOR_STATE_PASS); + const uint max_hits = min(max_transparent_hits, (uint)INTEGRATOR_SHADOW_ISECT_SIZE); + uint num_hits = 0; + bool opaque_hit = scene_intersect_shadow_all(kg, ray, isect, visibility, max_hits, &num_hits); + + /* If number of hits exceed the transparent bounces limit, make opaque. */ + if (num_hits > max_transparent_hits) { + opaque_hit = true; + } + + if (!opaque_hit) { + uint num_recorded_hits = min(num_hits, max_hits); + + if (num_recorded_hits > 0) { + sort_intersections(isect, num_recorded_hits); + + /* Write intersection result into global integrator state memory. */ + for (int hit = 0; hit < num_recorded_hits; hit++) { + integrator_state_write_shadow_isect(INTEGRATOR_STATE_PASS, &isect[hit], hit); + } + } + + INTEGRATOR_STATE_WRITE(shadow_path, num_hits) = num_hits; + } + else { + INTEGRATOR_STATE_WRITE(shadow_path, num_hits) = 0; + } + + return opaque_hit; +} +#endif + +ccl_device void integrator_intersect_shadow(INTEGRATOR_STATE_ARGS) +{ + PROFILING_INIT(kg, PROFILING_INTERSECT_SHADOW); + + /* Read ray from integrator state into local memory. */ + Ray ray ccl_optional_struct_init; + integrator_state_read_shadow_ray(INTEGRATOR_STATE_PASS, &ray); + + /* Compute visibility. */ + const uint visibility = integrate_intersect_shadow_visibility(INTEGRATOR_STATE_PASS); + +#ifdef __TRANSPARENT_SHADOWS__ + /* TODO: compile different kernels depending on this? Especially for OptiX + * conditional trace calls are bad. */ + const bool opaque_hit = + (kernel_data.integrator.transparent_shadows) ? + integrate_intersect_shadow_transparent(INTEGRATOR_STATE_PASS, &ray, visibility) : + integrate_intersect_shadow_opaque(INTEGRATOR_STATE_PASS, &ray, visibility); +#else + const bool opaque_hit = integrate_intersect_shadow_opaque( + INTEGRATOR_STATE_PASS, &ray, visibility); +#endif + + if (opaque_hit) { + /* Hit an opaque surface, shadow path ends here. */ + INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + return; + } + else { + /* Hit nothing or transparent surfaces, continue to shadow kernel + * for shading and render buffer output. + * + * TODO: could also write to render buffer directly if no transparent shadows? + * Could save a kernel execution for the common case. */ + INTEGRATOR_SHADOW_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, + DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + return; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_state_buffer_size.cl b/intern/cycles/kernel/integrator/integrator_intersect_subsurface.h similarity index 55% rename from intern/cycles/kernel/kernels/opencl/kernel_state_buffer_size.cl rename to intern/cycles/kernel/integrator/integrator_intersect_subsurface.h index c10ecc426c6..7c090952dc7 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_state_buffer_size.cl +++ b/intern/cycles/kernel/integrator/integrator_intersect_subsurface.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,23 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" +#pragma once -__kernel void kernel_ocl_path_trace_state_buffer_size( - ccl_global char *kg, - ccl_constant KernelData *data, - uint num_threads, - ccl_global uint64_t *size) +#include "kernel/integrator/integrator_subsurface.h" + +CCL_NAMESPACE_BEGIN + +ccl_device void integrator_intersect_subsurface(INTEGRATOR_STATE_ARGS) { - ((KernelGlobals*)kg)->data = data; - *size = split_data_buffer_size((KernelGlobals*)kg, num_threads); + PROFILING_INIT(kg, PROFILING_INTERSECT_SUBSURFACE); + +#ifdef __SUBSURFACE__ + if (subsurface_scatter(INTEGRATOR_STATE_PASS)) { + return; + } +#endif + + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); } +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h new file mode 100644 index 00000000000..60d8a8e3e54 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h @@ -0,0 +1,198 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/bvh/bvh.h" +#include "kernel/geom/geom.h" +#include "kernel/integrator/integrator_volume_stack.h" +#include "kernel/kernel_shader.h" + +CCL_NAMESPACE_BEGIN + +ccl_device void integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_ARGS, + const float3 from_P, + const float3 to_P) +{ + PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_STACK); + + ShaderDataTinyStorage stack_sd_storage; + ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage); + + kernel_assert(kernel_data.integrator.use_volumes); + + Ray volume_ray ccl_optional_struct_init; + volume_ray.P = from_P; + volume_ray.D = normalize_len(to_P - from_P, &volume_ray.t); + +#ifdef __VOLUME_RECORD_ALL__ + Intersection hits[2 * VOLUME_STACK_SIZE + 1]; + uint num_hits = scene_intersect_volume_all( + kg, &volume_ray, hits, 2 * VOLUME_STACK_SIZE, PATH_RAY_ALL_VISIBILITY); + if (num_hits > 0) { + Intersection *isect = hits; + + qsort(hits, num_hits, sizeof(Intersection), intersections_compare); + + for (uint hit = 0; hit < num_hits; ++hit, ++isect) { + shader_setup_from_ray(kg, stack_sd, &volume_ray, isect); + volume_stack_enter_exit(INTEGRATOR_STATE_PASS, stack_sd); + } + } +#else + Intersection isect; + int step = 0; + while (step < 2 * VOLUME_STACK_SIZE && + scene_intersect_volume(kg, &volume_ray, &isect, PATH_RAY_ALL_VISIBILITY)) { + shader_setup_from_ray(kg, stack_sd, &volume_ray, &isect); + volume_stack_enter_exit(INTEGRATOR_STATE_PASS, stack_sd); + + /* Move ray forward. */ + volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); + if (volume_ray.t != FLT_MAX) { + volume_ray.D = normalize_len(to_P - volume_ray.P, &volume_ray.t); + } + ++step; + } +#endif +} + +ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) +{ + PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_STACK); + + ShaderDataTinyStorage stack_sd_storage; + ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage); + + Ray volume_ray ccl_optional_struct_init; + integrator_state_read_ray(INTEGRATOR_STATE_PASS, &volume_ray); + volume_ray.t = FLT_MAX; + + const uint visibility = (INTEGRATOR_STATE(path, flag) & PATH_RAY_ALL_VISIBILITY); + int stack_index = 0, enclosed_index = 0; + + /* Write background shader. */ + if (kernel_data.background.volume_shader != SHADER_NONE) { + const VolumeStack new_entry = {OBJECT_NONE, kernel_data.background.volume_shader}; + integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, stack_index, new_entry); + stack_index++; + } + +#ifdef __VOLUME_RECORD_ALL__ + Intersection hits[2 * VOLUME_STACK_SIZE + 1]; + uint num_hits = scene_intersect_volume_all( + kg, &volume_ray, hits, 2 * VOLUME_STACK_SIZE, visibility); + if (num_hits > 0) { + int enclosed_volumes[VOLUME_STACK_SIZE]; + Intersection *isect = hits; + + qsort(hits, num_hits, sizeof(Intersection), intersections_compare); + + for (uint hit = 0; hit < num_hits; ++hit, ++isect) { + shader_setup_from_ray(kg, stack_sd, &volume_ray, isect); + if (stack_sd->flag & SD_BACKFACING) { + bool need_add = true; + for (int i = 0; i < enclosed_index && need_add; ++i) { + /* If ray exited the volume and never entered to that volume + * it means that camera is inside such a volume. + */ + if (enclosed_volumes[i] == stack_sd->object) { + need_add = false; + } + } + for (int i = 0; i < stack_index && need_add; ++i) { + /* Don't add intersections twice. */ + VolumeStack entry = integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); + if (entry.object == stack_sd->object) { + need_add = false; + break; + } + } + if (need_add && stack_index < VOLUME_STACK_SIZE - 1) { + const VolumeStack new_entry = {stack_sd->object, stack_sd->shader}; + integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, stack_index, new_entry); + ++stack_index; + } + } + else { + /* If ray from camera enters the volume, this volume shouldn't + * be added to the stack on exit. + */ + enclosed_volumes[enclosed_index++] = stack_sd->object; + } + } + } +#else + int enclosed_volumes[VOLUME_STACK_SIZE]; + int step = 0; + + while (stack_index < VOLUME_STACK_SIZE - 1 && enclosed_index < VOLUME_STACK_SIZE - 1 && + step < 2 * VOLUME_STACK_SIZE) { + Intersection isect; + if (!scene_intersect_volume(kg, &volume_ray, &isect, visibility)) { + break; + } + + shader_setup_from_ray(kg, stack_sd, &volume_ray, &isect); + if (stack_sd->flag & SD_BACKFACING) { + /* If ray exited the volume and never entered to that volume + * it means that camera is inside such a volume. + */ + bool need_add = true; + for (int i = 0; i < enclosed_index && need_add; ++i) { + /* If ray exited the volume and never entered to that volume + * it means that camera is inside such a volume. + */ + if (enclosed_volumes[i] == stack_sd->object) { + need_add = false; + } + } + for (int i = 0; i < stack_index && need_add; ++i) { + /* Don't add intersections twice. */ + VolumeStack entry = integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); + if (entry.object == stack_sd->object) { + need_add = false; + break; + } + } + if (need_add) { + const VolumeStack new_entry = {stack_sd->object, stack_sd->shader}; + integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, stack_index, new_entry); + ++stack_index; + } + } + else { + /* If ray from camera enters the volume, this volume shouldn't + * be added to the stack on exit. + */ + enclosed_volumes[enclosed_index++] = stack_sd->object; + } + + /* Move ray forward. */ + volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); + ++step; + } +#endif + + /* Write terminator. */ + const VolumeStack new_entry = {OBJECT_NONE, SHADER_NONE}; + integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, stack_index, new_entry); + + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_megakernel.h b/intern/cycles/kernel/integrator/integrator_megakernel.h new file mode 100644 index 00000000000..91363ea1c7f --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_megakernel.h @@ -0,0 +1,93 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/integrator_init_from_camera.h" +#include "kernel/integrator/integrator_intersect_closest.h" +#include "kernel/integrator/integrator_intersect_shadow.h" +#include "kernel/integrator/integrator_intersect_subsurface.h" +#include "kernel/integrator/integrator_intersect_volume_stack.h" +#include "kernel/integrator/integrator_shade_background.h" +#include "kernel/integrator/integrator_shade_light.h" +#include "kernel/integrator/integrator_shade_shadow.h" +#include "kernel/integrator/integrator_shade_surface.h" +#include "kernel/integrator/integrator_shade_volume.h" + +CCL_NAMESPACE_BEGIN + +ccl_device void integrator_megakernel(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ + /* Each kernel indicates the next kernel to execute, so here we simply + * have to check what that kernel is and execute it. + * + * TODO: investigate if we can use device side enqueue for GPUs to avoid + * having to compile this big kernel. */ + while (true) { + if (INTEGRATOR_STATE(shadow_path, queued_kernel)) { + /* First handle any shadow paths before we potentially create more shadow paths. */ + switch (INTEGRATOR_STATE(shadow_path, queued_kernel)) { + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: + integrator_intersect_shadow(INTEGRATOR_STATE_PASS); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: + integrator_shade_shadow(INTEGRATOR_STATE_PASS, render_buffer); + break; + default: + kernel_assert(0); + break; + } + } + else if (INTEGRATOR_STATE(path, queued_kernel)) { + /* Then handle regular path kernels. */ + switch (INTEGRATOR_STATE(path, queued_kernel)) { + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: + integrator_intersect_closest(INTEGRATOR_STATE_PASS); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND: + integrator_shade_background(INTEGRATOR_STATE_PASS, render_buffer); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE: + integrator_shade_surface(INTEGRATOR_STATE_PASS, render_buffer); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME: + integrator_shade_volume(INTEGRATOR_STATE_PASS, render_buffer); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE: + integrator_shade_surface_raytrace(INTEGRATOR_STATE_PASS, render_buffer); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT: + integrator_shade_light(INTEGRATOR_STATE_PASS, render_buffer); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE: + integrator_intersect_subsurface(INTEGRATOR_STATE_PASS); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: + integrator_intersect_volume_stack(INTEGRATOR_STATE_PASS); + break; + default: + kernel_assert(0); + break; + } + } + else { + break; + } + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_shade_background.h b/intern/cycles/kernel/integrator/integrator_shade_background.h new file mode 100644 index 00000000000..3e4cc837e9b --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_shade_background.h @@ -0,0 +1,215 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/kernel_accumulate.h" +#include "kernel/kernel_emission.h" +#include "kernel/kernel_light.h" +#include "kernel/kernel_shader.h" + +CCL_NAMESPACE_BEGIN + +ccl_device float3 integrator_eval_background_shader(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ +#ifdef __BACKGROUND__ + const int shader = kernel_data.background.surface_shader; + const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + + /* Use visibility flag to skip lights. */ + if (shader & SHADER_EXCLUDE_ANY) { + if (((shader & SHADER_EXCLUDE_DIFFUSE) && (path_flag & PATH_RAY_DIFFUSE)) || + ((shader & SHADER_EXCLUDE_GLOSSY) && ((path_flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == + (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || + ((shader & SHADER_EXCLUDE_TRANSMIT) && (path_flag & PATH_RAY_TRANSMIT)) || + ((shader & SHADER_EXCLUDE_CAMERA) && (path_flag & PATH_RAY_CAMERA)) || + ((shader & SHADER_EXCLUDE_SCATTER) && (path_flag & PATH_RAY_VOLUME_SCATTER))) + return zero_float3(); + } + + /* Use fast constant background color if available. */ + float3 L = zero_float3(); + if (!shader_constant_emission_eval(kg, shader, &L)) { + /* Evaluate background shader. */ + + /* TODO: does aliasing like this break automatic SoA in CUDA? + * Should we instead store closures separate from ShaderData? */ + ShaderDataTinyStorage emission_sd_storage; + ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + + PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_LIGHT_SETUP); + shader_setup_from_background(kg, + emission_sd, + INTEGRATOR_STATE(ray, P), + INTEGRATOR_STATE(ray, D), + INTEGRATOR_STATE(ray, time)); + + PROFILING_SHADER(emission_sd->object, emission_sd->shader); + PROFILING_EVENT(PROFILING_SHADE_LIGHT_EVAL); + shader_eval_surface( + INTEGRATOR_STATE_PASS, emission_sd, render_buffer, path_flag | PATH_RAY_EMISSION); + + L = shader_background_eval(emission_sd); + } + + /* Background MIS weights. */ +# ifdef __BACKGROUND_MIS__ + /* Check if background light exists or if we should skip pdf. */ + if (!(INTEGRATOR_STATE(path, flag) & PATH_RAY_MIS_SKIP) && kernel_data.background.use_mis) { + const float3 ray_P = INTEGRATOR_STATE(ray, P); + const float3 ray_D = INTEGRATOR_STATE(ray, D); + const float mis_ray_pdf = INTEGRATOR_STATE(path, mis_ray_pdf); + const float mis_ray_t = INTEGRATOR_STATE(path, mis_ray_t); + + /* multiple importance sampling, get background light pdf for ray + * direction, and compute weight with respect to BSDF pdf */ + const float pdf = background_light_pdf(kg, ray_P - ray_D * mis_ray_t, ray_D); + const float mis_weight = power_heuristic(mis_ray_pdf, pdf); + + L *= mis_weight; + } +# endif + + return L; +#else + return make_float3(0.8f, 0.8f, 0.8f); +#endif +} + +ccl_device_inline void integrate_background(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ + /* Accumulate transparency for transparent background. We can skip background + * shader evaluation unless a background pass is used. */ + bool eval_background = true; + float transparent = 0.0f; + + const bool is_transparent_background_ray = kernel_data.background.transparent && + (INTEGRATOR_STATE(path, flag) & + PATH_RAY_TRANSPARENT_BACKGROUND); + + if (is_transparent_background_ray) { + transparent = average(INTEGRATOR_STATE(path, throughput)); + +#ifdef __PASSES__ + eval_background = (kernel_data.film.light_pass_flag & PASSMASK(BACKGROUND)); +#else + eval_background = false; +#endif + } + + /* Evaluate background shader. */ + float3 L = (eval_background) ? + integrator_eval_background_shader(INTEGRATOR_STATE_PASS, render_buffer) : + zero_float3(); + + /* When using the ao bounces approximation, adjust background + * shader intensity with ao factor. */ + if (path_state_ao_bounce(INTEGRATOR_STATE_PASS)) { + L *= kernel_data.integrator.ao_bounces_factor; + } + + /* Write to render buffer. */ + kernel_accum_background( + INTEGRATOR_STATE_PASS, L, transparent, is_transparent_background_ray, render_buffer); +} + +ccl_device_inline void integrate_distant_lights(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ + const float3 ray_D = INTEGRATOR_STATE(ray, D); + const float ray_time = INTEGRATOR_STATE(ray, time); + LightSample ls ccl_optional_struct_init; + for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { + if (light_sample_from_distant_ray(kg, ray_D, lamp, &ls)) { + /* Use visibility flag to skip lights. */ +#ifdef __PASSES__ + const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + + if (ls.shader & SHADER_EXCLUDE_ANY) { + if (((ls.shader & SHADER_EXCLUDE_DIFFUSE) && (path_flag & PATH_RAY_DIFFUSE)) || + ((ls.shader & SHADER_EXCLUDE_GLOSSY) && + ((path_flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == + (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || + ((ls.shader & SHADER_EXCLUDE_TRANSMIT) && (path_flag & PATH_RAY_TRANSMIT)) || + ((ls.shader & SHADER_EXCLUDE_CAMERA) && (path_flag & PATH_RAY_CAMERA)) || + ((ls.shader & SHADER_EXCLUDE_SCATTER) && (path_flag & PATH_RAY_VOLUME_SCATTER))) + return; + } +#endif + + /* Evaluate light shader. */ + /* TODO: does aliasing like this break automatic SoA in CUDA? */ + ShaderDataTinyStorage emission_sd_storage; + ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + float3 light_eval = light_sample_shader_eval( + INTEGRATOR_STATE_PASS, emission_sd, &ls, ray_time); + if (is_zero(light_eval)) { + return; + } + + /* MIS weighting. */ + if (!(path_flag & PATH_RAY_MIS_SKIP)) { + /* multiple importance sampling, get regular light pdf, + * and compute weight with respect to BSDF pdf */ + const float mis_ray_pdf = INTEGRATOR_STATE(path, mis_ray_pdf); + const float mis_weight = power_heuristic(mis_ray_pdf, ls.pdf); + light_eval *= mis_weight; + } + + /* Write to render buffer. */ + const float3 throughput = INTEGRATOR_STATE(path, throughput); + kernel_accum_emission(INTEGRATOR_STATE_PASS, throughput, light_eval, render_buffer); + } + } +} + +ccl_device void integrator_shade_background(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ + PROFILING_INIT(kg, PROFILING_SHADE_LIGHT_SETUP); + + /* TODO: unify these in a single loop to only have a single shader evaluation call. */ + integrate_distant_lights(INTEGRATOR_STATE_PASS, render_buffer); + integrate_background(INTEGRATOR_STATE_PASS, render_buffer); + +#ifdef __SHADOW_CATCHER__ + if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { + INTEGRATOR_STATE_WRITE(path, flag) &= ~PATH_RAY_SHADOW_CATCHER_BACKGROUND; + + const int isect_prim = INTEGRATOR_STATE(isect, prim); + const int shader = intersection_get_shader_from_isect_prim(kg, isect_prim); + const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; + + if ((shader_flags & SD_HAS_RAYTRACE) || (kernel_data.film.pass_ao != PASS_UNUSED)) { + INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, + shader); + } + else { + INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, + shader); + } + return; + } +#endif + + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_shade_light.h b/intern/cycles/kernel/integrator/integrator_shade_light.h new file mode 100644 index 00000000000..05b530f9665 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_shade_light.h @@ -0,0 +1,126 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/kernel_accumulate.h" +#include "kernel/kernel_emission.h" +#include "kernel/kernel_light.h" +#include "kernel/kernel_shader.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_inline void integrate_light(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ + /* Setup light sample. */ + Intersection isect ccl_optional_struct_init; + integrator_state_read_isect(INTEGRATOR_STATE_PASS, &isect); + + float3 ray_P = INTEGRATOR_STATE(ray, P); + const float3 ray_D = INTEGRATOR_STATE(ray, D); + const float ray_time = INTEGRATOR_STATE(ray, time); + + /* Advance ray beyond light. */ + /* TODO: can we make this more numerically robust to avoid reintersecting the + * same light in some cases? */ + const float3 new_ray_P = ray_offset(ray_P + ray_D * isect.t, ray_D); + INTEGRATOR_STATE_WRITE(ray, P) = new_ray_P; + INTEGRATOR_STATE_WRITE(ray, t) -= isect.t; + + /* Set position to where the BSDF was sampled, for correct MIS PDF. */ + const float mis_ray_t = INTEGRATOR_STATE(path, mis_ray_t); + ray_P -= ray_D * mis_ray_t; + isect.t += mis_ray_t; + INTEGRATOR_STATE_WRITE(path, mis_ray_t) = mis_ray_t + isect.t; + + LightSample ls ccl_optional_struct_init; + const bool use_light_sample = light_sample_from_intersection(kg, &isect, ray_P, ray_D, &ls); + + if (!use_light_sample) { + return; + } + + /* Use visibility flag to skip lights. */ +#ifdef __PASSES__ + const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + + if (ls.shader & SHADER_EXCLUDE_ANY) { + if (((ls.shader & SHADER_EXCLUDE_DIFFUSE) && (path_flag & PATH_RAY_DIFFUSE)) || + ((ls.shader & SHADER_EXCLUDE_GLOSSY) && + ((path_flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == + (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || + ((ls.shader & SHADER_EXCLUDE_TRANSMIT) && (path_flag & PATH_RAY_TRANSMIT)) || + ((ls.shader & SHADER_EXCLUDE_SCATTER) && (path_flag & PATH_RAY_VOLUME_SCATTER))) + return; + } +#endif + + /* Evaluate light shader. */ + /* TODO: does aliasing like this break automatic SoA in CUDA? */ + ShaderDataTinyStorage emission_sd_storage; + ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + float3 light_eval = light_sample_shader_eval(INTEGRATOR_STATE_PASS, emission_sd, &ls, ray_time); + if (is_zero(light_eval)) { + return; + } + + /* MIS weighting. */ + if (!(path_flag & PATH_RAY_MIS_SKIP)) { + /* multiple importance sampling, get regular light pdf, + * and compute weight with respect to BSDF pdf */ + const float mis_ray_pdf = INTEGRATOR_STATE(path, mis_ray_pdf); + const float mis_weight = power_heuristic(mis_ray_pdf, ls.pdf); + light_eval *= mis_weight; + } + + /* Write to render buffer. */ + const float3 throughput = INTEGRATOR_STATE(path, throughput); + kernel_accum_emission(INTEGRATOR_STATE_PASS, throughput, light_eval, render_buffer); +} + +ccl_device void integrator_shade_light(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ + PROFILING_INIT(kg, PROFILING_SHADE_LIGHT_SETUP); + + integrate_light(INTEGRATOR_STATE_PASS, render_buffer); + + /* TODO: we could get stuck in an infinite loop if there are precision issues + * and the same light is hit again. + * + * As a workaround count this as a transparent bounce. It makes some sense + * to interpret lights as transparent surfaces (and support making them opaque), + * but this needs to be revisited. */ + uint32_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce) + 1; + INTEGRATOR_STATE_WRITE(path, transparent_bounce) = transparent_bounce; + + if (transparent_bounce >= kernel_data.integrator.transparent_max_bounce) { + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + return; + } + else { + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + return; + } + + /* TODO: in some cases we could continue directly to SHADE_BACKGROUND, but + * probably that optimization is probably not practical if we add lights to + * scene geometry. */ +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_shade_shadow.h b/intern/cycles/kernel/integrator/integrator_shade_shadow.h new file mode 100644 index 00000000000..fd3c3ae1653 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_shade_shadow.h @@ -0,0 +1,182 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/integrator_shade_volume.h" +#include "kernel/integrator/integrator_volume_stack.h" + +#include "kernel/kernel_shader.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_inline bool shadow_intersections_has_remaining(const int num_hits) +{ + return num_hits >= INTEGRATOR_SHADOW_ISECT_SIZE; +} + +#ifdef __TRANSPARENT_SHADOWS__ +ccl_device_inline float3 integrate_transparent_surface_shadow(INTEGRATOR_STATE_ARGS, const int hit) +{ + PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SURFACE); + + /* TODO: does aliasing like this break automatic SoA in CUDA? + * Should we instead store closures separate from ShaderData? + * + * TODO: is it better to declare this outside the loop or keep it local + * so the compiler can see there is no dependency between iterations? */ + ShaderDataTinyStorage shadow_sd_storage; + ShaderData *shadow_sd = AS_SHADER_DATA(&shadow_sd_storage); + + /* Setup shader data at surface. */ + Intersection isect ccl_optional_struct_init; + integrator_state_read_shadow_isect(INTEGRATOR_STATE_PASS, &isect, hit); + + Ray ray ccl_optional_struct_init; + integrator_state_read_shadow_ray(INTEGRATOR_STATE_PASS, &ray); + + shader_setup_from_ray(kg, shadow_sd, &ray, &isect); + + /* Evaluate shader. */ + if (!(shadow_sd->flag & SD_HAS_ONLY_VOLUME)) { + shader_eval_surface( + INTEGRATOR_STATE_PASS, shadow_sd, NULL, PATH_RAY_SHADOW); + } + +# ifdef __VOLUME__ + /* Exit/enter volume. */ + shadow_volume_stack_enter_exit(INTEGRATOR_STATE_PASS, shadow_sd); +# endif + + /* Compute transparency from closures. */ + return shader_bsdf_transparency(kg, shadow_sd); +} + +# ifdef __VOLUME__ +ccl_device_inline void integrate_transparent_volume_shadow(INTEGRATOR_STATE_ARGS, + const int hit, + const int num_recorded_hits, + float3 *ccl_restrict throughput) +{ + PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_VOLUME); + + /* TODO: deduplicate with surface, or does it not matter for memory usage? */ + ShaderDataTinyStorage shadow_sd_storage; + ShaderData *shadow_sd = AS_SHADER_DATA(&shadow_sd_storage); + + /* Setup shader data. */ + Ray ray ccl_optional_struct_init; + integrator_state_read_shadow_ray(INTEGRATOR_STATE_PASS, &ray); + + /* Modify ray position and length to match current segment. */ + const float start_t = (hit == 0) ? 0.0f : INTEGRATOR_STATE_ARRAY(shadow_isect, hit - 1, t); + const float end_t = (hit < num_recorded_hits) ? INTEGRATOR_STATE_ARRAY(shadow_isect, hit, t) : + ray.t; + ray.P += start_t * ray.D; + ray.t = end_t - start_t; + + shader_setup_from_volume(kg, shadow_sd, &ray); + + const float step_size = volume_stack_step_size(INTEGRATOR_STATE_PASS, [=](const int i) { + return integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_PASS, i); + }); + + volume_shadow_heterogeneous(INTEGRATOR_STATE_PASS, &ray, shadow_sd, throughput, step_size); +} +# endif + +ccl_device_inline bool integrate_transparent_shadow(INTEGRATOR_STATE_ARGS, const int num_hits) +{ + /* Accumulate shadow for transparent surfaces. */ + const int num_recorded_hits = min(num_hits, INTEGRATOR_SHADOW_ISECT_SIZE); + + for (int hit = 0; hit < num_recorded_hits + 1; hit++) { + /* Volume shaders. */ + if (hit < num_recorded_hits || !shadow_intersections_has_remaining(num_hits)) { +# ifdef __VOLUME__ + if (!integrator_state_shadow_volume_stack_is_empty(INTEGRATOR_STATE_PASS)) { + float3 throughput = INTEGRATOR_STATE(shadow_path, throughput); + integrate_transparent_volume_shadow( + INTEGRATOR_STATE_PASS, hit, num_recorded_hits, &throughput); + if (is_zero(throughput)) { + return true; + } + + INTEGRATOR_STATE_WRITE(shadow_path, throughput) = throughput; + } +# endif + } + + /* Surface shaders. */ + if (hit < num_recorded_hits) { + const float3 shadow = integrate_transparent_surface_shadow(INTEGRATOR_STATE_PASS, hit); + const float3 throughput = INTEGRATOR_STATE(shadow_path, throughput) * shadow; + if (is_zero(throughput)) { + return true; + } + + INTEGRATOR_STATE_WRITE(shadow_path, throughput) = throughput; + INTEGRATOR_STATE_WRITE(shadow_path, transparent_bounce) += 1; + } + + /* Note we do not need to check max_transparent_bounce here, the number + * of intersections is already limited and made opaque in the + * INTERSECT_SHADOW kernel. */ + } + + if (shadow_intersections_has_remaining(num_hits)) { + /* There are more hits that we could not recorded due to memory usage, + * adjust ray to intersect again from the last hit. */ + const float last_hit_t = INTEGRATOR_STATE_ARRAY(shadow_isect, num_recorded_hits - 1, t); + const float3 ray_P = INTEGRATOR_STATE(shadow_ray, P); + const float3 ray_D = INTEGRATOR_STATE(shadow_ray, D); + INTEGRATOR_STATE_WRITE(shadow_ray, P) = ray_offset(ray_P + last_hit_t * ray_D, ray_D); + INTEGRATOR_STATE_WRITE(shadow_ray, t) -= last_hit_t; + } + + return false; +} +#endif /* __TRANSPARENT_SHADOWS__ */ + +ccl_device void integrator_shade_shadow(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ + PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SETUP); + const int num_hits = INTEGRATOR_STATE(shadow_path, num_hits); + +#ifdef __TRANSPARENT_SHADOWS__ + /* Evaluate transparent shadows. */ + const bool opaque = integrate_transparent_shadow(INTEGRATOR_STATE_PASS, num_hits); + if (opaque) { + INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + return; + } +#endif + + if (shadow_intersections_has_remaining(num_hits)) { + /* More intersections to find, continue shadow ray. */ + INTEGRATOR_SHADOW_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + return; + } + else { + kernel_accum_light(INTEGRATOR_STATE_PASS, render_buffer); + INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + return; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h new file mode 100644 index 00000000000..73b7cad32be --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -0,0 +1,502 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/kernel_accumulate.h" +#include "kernel/kernel_emission.h" +#include "kernel/kernel_light.h" +#include "kernel/kernel_passes.h" +#include "kernel/kernel_path_state.h" +#include "kernel/kernel_shader.h" + +#include "kernel/integrator/integrator_subsurface.h" +#include "kernel/integrator/integrator_volume_stack.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_forceinline void integrate_surface_shader_setup(INTEGRATOR_STATE_CONST_ARGS, + ShaderData *sd) +{ + Intersection isect ccl_optional_struct_init; + integrator_state_read_isect(INTEGRATOR_STATE_PASS, &isect); + + Ray ray ccl_optional_struct_init; + integrator_state_read_ray(INTEGRATOR_STATE_PASS, &ray); + + shader_setup_from_ray(kg, sd, &ray, &isect); +} + +#ifdef __HOLDOUT__ +ccl_device_forceinline bool integrate_surface_holdout(INTEGRATOR_STATE_CONST_ARGS, + ShaderData *sd, + ccl_global float *ccl_restrict render_buffer) +{ + /* Write holdout transparency to render buffer and stop if fully holdout. */ + const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + + if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) && + (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { + const float3 holdout_weight = shader_holdout_apply(kg, sd); + if (kernel_data.background.transparent) { + const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float transparent = average(holdout_weight * throughput); + kernel_accum_transparent(INTEGRATOR_STATE_PASS, transparent, render_buffer); + } + if (isequal_float3(holdout_weight, one_float3())) { + return false; + } + } + + return true; +} +#endif /* __HOLDOUT__ */ + +#ifdef __EMISSION__ +ccl_device_forceinline void integrate_surface_emission(INTEGRATOR_STATE_CONST_ARGS, + const ShaderData *sd, + ccl_global float *ccl_restrict + render_buffer) +{ + const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + + /* Evaluate emissive closure. */ + float3 L = shader_emissive_eval(sd); + +# ifdef __HAIR__ + if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS) && + (sd->type & PRIMITIVE_ALL_TRIANGLE)) +# else + if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS)) +# endif + { + const float bsdf_pdf = INTEGRATOR_STATE(path, mis_ray_pdf); + const float t = sd->ray_length + INTEGRATOR_STATE(path, mis_ray_t); + + /* Multiple importance sampling, get triangle light pdf, + * and compute weight with respect to BSDF pdf. */ + float pdf = triangle_light_pdf(kg, sd, t); + float mis_weight = power_heuristic(bsdf_pdf, pdf); + + L *= mis_weight; + } + + const float3 throughput = INTEGRATOR_STATE(path, throughput); + kernel_accum_emission(INTEGRATOR_STATE_PASS, throughput, L, render_buffer); +} +#endif /* __EMISSION__ */ + +#ifdef __EMISSION__ +/* Path tracing: sample point on light and evaluate light shader, then + * queue shadow ray to be traced. */ +ccl_device_forceinline void integrate_surface_direct_light(INTEGRATOR_STATE_ARGS, + ShaderData *sd, + const RNGState *rng_state) +{ + /* Test if there is a light or BSDF that needs direct light. */ + if (!(kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL))) { + return; + } + + /* Sample position on a light. */ + LightSample ls ccl_optional_struct_init; + { + const int path_flag = INTEGRATOR_STATE(path, flag); + const uint bounce = INTEGRATOR_STATE(path, bounce); + float light_u, light_v; + path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); + + if (!light_distribution_sample_from_position( + kg, light_u, light_v, sd->time, sd->P, bounce, path_flag, &ls)) { + return; + } + } + + kernel_assert(ls.pdf != 0.0f); + + /* Evaluate light shader. + * + * TODO: can we reuse sd memory? In theory we can move this after + * integrate_surface_bounce, evaluate the BSDF, and only then evaluate + * the light shader. This could also move to its own kernel, for + * non-constant light sources. */ + ShaderDataTinyStorage emission_sd_storage; + ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + const float3 light_eval = light_sample_shader_eval( + INTEGRATOR_STATE_PASS, emission_sd, &ls, sd->time); + if (is_zero(light_eval)) { + return; + } + + /* Evaluate BSDF. */ + const bool is_transmission = shader_bsdf_is_transmission(sd, ls.D); + + BsdfEval bsdf_eval ccl_optional_struct_init; + const float bsdf_pdf = shader_bsdf_eval(kg, sd, ls.D, is_transmission, &bsdf_eval, ls.shader); + bsdf_eval_mul3(&bsdf_eval, light_eval / ls.pdf); + + if (ls.shader & SHADER_USE_MIS) { + const float mis_weight = power_heuristic(ls.pdf, bsdf_pdf); + bsdf_eval_mul(&bsdf_eval, mis_weight); + } + + /* Path termination. */ + const float terminate = path_state_rng_light_termination(kg, rng_state); + if (light_sample_terminate(kg, &ls, &bsdf_eval, terminate)) { + return; + } + + /* Create shadow ray. */ + Ray ray ccl_optional_struct_init; + light_sample_to_surface_shadow_ray(kg, sd, &ls, &ray); + const bool is_light = light_sample_is_light(&ls); + + /* Copy volume stack and enter/exit volume. */ + integrator_state_copy_volume_stack_to_shadow(INTEGRATOR_STATE_PASS); + + if (is_transmission) { +# ifdef __VOLUME__ + shadow_volume_stack_enter_exit(INTEGRATOR_STATE_PASS, sd); +# endif + } + + /* Write shadow ray and associated state to global memory. */ + integrator_state_write_shadow_ray(INTEGRATOR_STATE_PASS, &ray); + + /* Copy state from main path to shadow path. */ + const uint16_t bounce = INTEGRATOR_STATE(path, bounce); + const uint16_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce); + uint32_t shadow_flag = INTEGRATOR_STATE(path, flag); + shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; + shadow_flag |= (is_transmission) ? PATH_RAY_TRANSMISSION_PASS : PATH_RAY_REFLECT_PASS; + const float3 throughput = INTEGRATOR_STATE(path, throughput) * bsdf_eval_sum(&bsdf_eval); + + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + const float3 diffuse_glossy_ratio = (bounce == 0) ? + bsdf_eval_diffuse_glossy_ratio(&bsdf_eval) : + INTEGRATOR_STATE(path, diffuse_glossy_ratio); + INTEGRATOR_STATE_WRITE(shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; + } + + INTEGRATOR_STATE_WRITE(shadow_path, flag) = shadow_flag; + INTEGRATOR_STATE_WRITE(shadow_path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(shadow_path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_path, throughput) = throughput; + + if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) { + INTEGRATOR_STATE_WRITE(shadow_path, unshadowed_throughput) = throughput; + } + + /* Branch off shadow kernel. */ + INTEGRATOR_SHADOW_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); +} +#endif + +/* Path tracing: bounce off or through surface with new direction. */ +ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(INTEGRATOR_STATE_ARGS, + ShaderData *sd, + const RNGState *rng_state) +{ + /* Sample BSDF or BSSRDF. */ + if (!(sd->flag & (SD_BSDF | SD_BSSRDF))) { + return LABEL_NONE; + } + + float bsdf_u, bsdf_v; + path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); + const ShaderClosure *sc = shader_bsdf_bssrdf_pick(sd, &bsdf_u); + +#ifdef __SUBSURFACE__ + /* BSSRDF closure, we schedule subsurface intersection kernel. */ + if (CLOSURE_IS_BSSRDF(sc->type)) { + return subsurface_bounce(INTEGRATOR_STATE_PASS, sd, sc); + } +#endif + + /* BSDF closure, sample direction. */ + float bsdf_pdf; + BsdfEval bsdf_eval ccl_optional_struct_init; + float3 bsdf_omega_in ccl_optional_struct_init; + differential3 bsdf_domega_in ccl_optional_struct_init; + int label; + + label = shader_bsdf_sample_closure( + kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval, &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf); + + if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) { + return LABEL_NONE; + } + + /* Setup ray. Note that clipping works through transparent bounces. */ + INTEGRATOR_STATE_WRITE(ray, P) = ray_offset(sd->P, (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng); + INTEGRATOR_STATE_WRITE(ray, D) = normalize(bsdf_omega_in); + INTEGRATOR_STATE_WRITE(ray, t) = (label & LABEL_TRANSPARENT) ? + INTEGRATOR_STATE(ray, t) - sd->ray_length : + FLT_MAX; + +#ifdef __RAY_DIFFERENTIALS__ + INTEGRATOR_STATE_WRITE(ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(ray, dD) = differential_make_compact(bsdf_domega_in); +#endif + + /* Update throughput. */ + float3 throughput = INTEGRATOR_STATE(path, throughput); + throughput *= bsdf_eval_sum(&bsdf_eval) / bsdf_pdf; + INTEGRATOR_STATE_WRITE(path, throughput) = throughput; + + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + if (INTEGRATOR_STATE(path, bounce) == 0) { + INTEGRATOR_STATE_WRITE(path, + diffuse_glossy_ratio) = bsdf_eval_diffuse_glossy_ratio(&bsdf_eval); + } + } + + /* Update path state */ + if (label & LABEL_TRANSPARENT) { + INTEGRATOR_STATE_WRITE(path, mis_ray_t) += sd->ray_length; + } + else { + INTEGRATOR_STATE_WRITE(path, mis_ray_pdf) = bsdf_pdf; + INTEGRATOR_STATE_WRITE(path, mis_ray_t) = 0.0f; + INTEGRATOR_STATE_WRITE(path, min_ray_pdf) = fminf(bsdf_pdf, + INTEGRATOR_STATE(path, min_ray_pdf)); + } + + path_state_next(INTEGRATOR_STATE_PASS, label); + return label; +} + +#ifdef __VOLUME__ +ccl_device_forceinline bool integrate_surface_volume_only_bounce(INTEGRATOR_STATE_ARGS, + ShaderData *sd) +{ + if (!path_state_volume_next(INTEGRATOR_STATE_PASS)) { + return LABEL_NONE; + } + + /* Setup ray position, direction stays unchanged. */ + INTEGRATOR_STATE_WRITE(ray, P) = ray_offset(sd->P, -sd->Ng); + + /* Clipping works through transparent. */ + INTEGRATOR_STATE_WRITE(ray, t) -= sd->ray_length; + +# ifdef __RAY_DIFFERENTIALS__ + INTEGRATOR_STATE_WRITE(ray, dP) = differential_make_compact(sd->dP); +# endif + + INTEGRATOR_STATE_WRITE(path, mis_ray_t) += sd->ray_length; + + return LABEL_TRANSMIT | LABEL_TRANSPARENT; +} +#endif + +#if defined(__AO__) && defined(__SHADER_RAYTRACE__) +ccl_device_forceinline void integrate_surface_ao_pass(INTEGRATOR_STATE_CONST_ARGS, + const ShaderData *ccl_restrict sd, + const RNGState *ccl_restrict rng_state, + ccl_global float *ccl_restrict render_buffer) +{ +# ifdef __KERNEL_OPTIX__ + optixDirectCall(2, INTEGRATOR_STATE_PASS, sd, rng_state, render_buffer); +} + +extern "C" __device__ void __direct_callable__ao_pass(INTEGRATOR_STATE_CONST_ARGS, + const ShaderData *ccl_restrict sd, + const RNGState *ccl_restrict rng_state, + ccl_global float *ccl_restrict render_buffer) +{ +# endif /* __KERNEL_OPTIX__ */ + float bsdf_u, bsdf_v; + path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); + + const float3 ao_N = shader_bsdf_ao_normal(kg, sd); + float3 ao_D; + float ao_pdf; + sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); + + if (dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f) { + Ray ray ccl_optional_struct_init; + ray.P = ray_offset(sd->P, sd->Ng); + ray.D = ao_D; + ray.t = kernel_data.integrator.ao_bounces_distance; + ray.time = sd->time; + ray.dP = differential_zero_compact(); + ray.dD = differential_zero_compact(); + + Intersection isect ccl_optional_struct_init; + if (!scene_intersect(kg, &ray, PATH_RAY_SHADOW_OPAQUE, &isect)) { + ccl_global float *buffer = kernel_pass_pixel_render_buffer(INTEGRATOR_STATE_PASS, + render_buffer); + const float3 throughput = INTEGRATOR_STATE(path, throughput); + kernel_write_pass_float3(buffer + kernel_data.film.pass_ao, throughput); + } + } +} +#endif /* defined(__AO__) && defined(__SHADER_RAYTRACE__) */ + +template +ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) + +{ + PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_SURFACE_SETUP); + + /* Setup shader data. */ + ShaderData sd; + integrate_surface_shader_setup(INTEGRATOR_STATE_PASS, &sd); + PROFILING_SHADER(sd.object, sd.shader); + + int continue_path_label = 0; + + /* Skip most work for volume bounding surface. */ +#ifdef __VOLUME__ + if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { +#endif + + { + const int path_flag = INTEGRATOR_STATE(path, flag); +#ifdef __SUBSURFACE__ + /* Can skip shader evaluation for BSSRDF exit point without bump mapping. */ + if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP))) +#endif + { + /* Evaluate shader. */ + PROFILING_EVENT(PROFILING_SHADE_SURFACE_EVAL); + shader_eval_surface( + INTEGRATOR_STATE_PASS, &sd, render_buffer, path_flag); + } + } + +#ifdef __SUBSURFACE__ + if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SUBSURFACE) { + /* When coming from inside subsurface scattering, setup a diffuse + * closure to perform lighting at the exit point. */ + INTEGRATOR_STATE_WRITE(path, flag) &= ~PATH_RAY_SUBSURFACE; + subsurface_shader_data_setup(INTEGRATOR_STATE_PASS, &sd); + } +#endif + + shader_prepare_surface_closures(INTEGRATOR_STATE_PASS, &sd); + +#ifdef __HOLDOUT__ + /* Evaluate holdout. */ + if (!integrate_surface_holdout(INTEGRATOR_STATE_PASS, &sd, render_buffer)) { + return false; + } +#endif + +#ifdef __EMISSION__ + /* Write emission. */ + if (sd.flag & SD_EMISSION) { + integrate_surface_emission(INTEGRATOR_STATE_PASS, &sd, render_buffer); + } +#endif + +#ifdef __PASSES__ + /* Write render passes. */ + PROFILING_EVENT(PROFILING_SHADE_SURFACE_PASSES); + kernel_write_data_passes(INTEGRATOR_STATE_PASS, &sd, render_buffer); +#endif + + /* Load random number state. */ + RNGState rng_state; + path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + + /* Perform path termination. Most paths have already been terminated in + * the intersect_closest kernel, this is just for emission and for dividing + * throughput by the probability at the right moment. */ + const int path_flag = INTEGRATOR_STATE(path, flag); + const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ? + 0.0f : + path_state_continuation_probability(INTEGRATOR_STATE_PASS, + path_flag); + if (probability == 0.0f) { + return false; + } + else if (probability != 1.0f) { + INTEGRATOR_STATE_WRITE(path, throughput) /= probability; + } + +#ifdef __DENOISING_FEATURES__ + kernel_write_denoising_features_surface(INTEGRATOR_STATE_PASS, &sd, render_buffer); +#endif + +#ifdef __SHADOW_CATCHER__ + kernel_write_shadow_catcher_bounce_data(INTEGRATOR_STATE_PASS, &sd, render_buffer); +#endif + + /* Direct light. */ + PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT); + integrate_surface_direct_light(INTEGRATOR_STATE_PASS, &sd, &rng_state); + +#if defined(__AO__) && defined(__SHADER_RAYTRACE__) + /* Ambient occlusion pass. */ + if (node_feature_mask & KERNEL_FEATURE_NODE_RAYTRACE) { + if ((kernel_data.film.pass_ao != PASS_UNUSED) && + (INTEGRATOR_STATE(path, flag) & PATH_RAY_CAMERA)) { + PROFILING_EVENT(PROFILING_SHADE_SURFACE_AO); + integrate_surface_ao_pass(INTEGRATOR_STATE_PASS, &sd, &rng_state, render_buffer); + } + } +#endif + + PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT); + continue_path_label = integrate_surface_bsdf_bssrdf_bounce( + INTEGRATOR_STATE_PASS, &sd, &rng_state); +#ifdef __VOLUME__ + } + else { + PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT); + continue_path_label = integrate_surface_volume_only_bounce(INTEGRATOR_STATE_PASS, &sd); + } + + if (continue_path_label & LABEL_TRANSMIT) { + /* Enter/Exit volume. */ + volume_stack_enter_exit(INTEGRATOR_STATE_PASS, &sd); + } +#endif + + return continue_path_label != 0; +} + +template +ccl_device_forceinline void integrator_shade_surface(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ + if (integrate_surface(INTEGRATOR_STATE_PASS, render_buffer)) { + if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SUBSURFACE) { + INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); + } + else { + kernel_assert(INTEGRATOR_STATE(ray, t) != 0.0f); + INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + } + } + else { + INTEGRATOR_PATH_TERMINATE(current_kernel); + } +} + +ccl_device_forceinline void integrator_shade_surface_raytrace( + INTEGRATOR_STATE_ARGS, ccl_global float *ccl_restrict render_buffer) +{ + integrator_shade_surface(INTEGRATOR_STATE_PASS, + render_buffer); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h new file mode 100644 index 00000000000..4a864b1e6ce --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -0,0 +1,1015 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/kernel_accumulate.h" +#include "kernel/kernel_emission.h" +#include "kernel/kernel_light.h" +#include "kernel/kernel_passes.h" +#include "kernel/kernel_path_state.h" +#include "kernel/kernel_shader.h" + +#include "kernel/integrator/integrator_intersect_closest.h" +#include "kernel/integrator/integrator_volume_stack.h" + +CCL_NAMESPACE_BEGIN + +#ifdef __VOLUME__ + +/* Events for probalistic scattering */ + +typedef enum VolumeIntegrateEvent { + VOLUME_PATH_SCATTERED = 0, + VOLUME_PATH_ATTENUATED = 1, + VOLUME_PATH_MISSED = 2 +} VolumeIntegrateEvent; + +typedef struct VolumeIntegrateResult { + /* Throughput and offset for direct light scattering. */ + bool direct_scatter; + float3 direct_throughput; + float direct_t; + ShaderVolumePhases direct_phases; + + /* Throughput and offset for indirect light scattering. */ + bool indirect_scatter; + float3 indirect_throughput; + float indirect_t; + ShaderVolumePhases indirect_phases; +} VolumeIntegrateResult; + +/* Ignore paths that have volume throughput below this value, to avoid unnecessary work + * and precision issues. + * todo: this value could be tweaked or turned into a probability to avoid unnecessary + * work in volumes and subsurface scattering. */ +# define VOLUME_THROUGHPUT_EPSILON 1e-6f + +/* Volume shader properties + * + * extinction coefficient = absorption coefficient + scattering coefficient + * sigma_t = sigma_a + sigma_s */ + +typedef struct VolumeShaderCoefficients { + float3 sigma_t; + float3 sigma_s; + float3 emission; +} VolumeShaderCoefficients; + +/* Evaluate shader to get extinction coefficient at P. */ +ccl_device_inline bool shadow_volume_shader_sample(INTEGRATOR_STATE_ARGS, + ShaderData *ccl_restrict sd, + float3 *ccl_restrict extinction) +{ + shader_eval_volume(INTEGRATOR_STATE_PASS, sd, PATH_RAY_SHADOW, [=](const int i) { + return integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_PASS, i); + }); + + if (!(sd->flag & SD_EXTINCTION)) { + return false; + } + + const float density = object_volume_density(kg, sd->object); + *extinction = sd->closure_transparent_extinction * density; + return true; +} + +/* Evaluate shader to get absorption, scattering and emission at P. */ +ccl_device_inline bool volume_shader_sample(INTEGRATOR_STATE_ARGS, + ShaderData *ccl_restrict sd, + VolumeShaderCoefficients *coeff) +{ + const int path_flag = INTEGRATOR_STATE(path, flag); + shader_eval_volume(INTEGRATOR_STATE_PASS, sd, path_flag, [=](const int i) { + return integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); + }); + + if (!(sd->flag & (SD_EXTINCTION | SD_SCATTER | SD_EMISSION))) { + return false; + } + + coeff->sigma_s = zero_float3(); + coeff->sigma_t = (sd->flag & SD_EXTINCTION) ? sd->closure_transparent_extinction : zero_float3(); + coeff->emission = (sd->flag & SD_EMISSION) ? sd->closure_emission_background : zero_float3(); + + if (sd->flag & SD_SCATTER) { + for (int i = 0; i < sd->num_closure; i++) { + const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_VOLUME(sc->type)) { + coeff->sigma_s += sc->weight; + } + } + } + + const float density = object_volume_density(kg, sd->object); + coeff->sigma_s *= density; + coeff->sigma_t *= density; + coeff->emission *= density; + + return true; +} + +ccl_device_forceinline void volume_step_init(const KernelGlobals *kg, + const RNGState *rng_state, + const float object_step_size, + float t, + float *step_size, + float *step_shade_offset, + float *steps_offset, + int *max_steps) +{ + if (object_step_size == FLT_MAX) { + /* Homogeneous volume. */ + *step_size = t; + *step_shade_offset = 0.0f; + *steps_offset = 1.0f; + *max_steps = 1; + } + else { + /* Heterogeneous volume. */ + *max_steps = kernel_data.integrator.volume_max_steps; + float step = min(object_step_size, t); + + /* compute exact steps in advance for malloc */ + if (t > *max_steps * step) { + step = t / (float)*max_steps; + } + + *step_size = step; + + /* Perform shading at this offset within a step, to integrate over + * over the entire step segment. */ + *step_shade_offset = path_state_rng_1D_hash(kg, rng_state, 0x1e31d8a4); + + /* Shift starting point of all segment by this random amount to avoid + * banding artifacts from the volume bounding shape. */ + *steps_offset = path_state_rng_1D_hash(kg, rng_state, 0x3d22c7b3); + } +} + +/* Volume Shadows + * + * These functions are used to attenuate shadow rays to lights. Both absorption + * and scattering will block light, represented by the extinction coefficient. */ + +# if 0 +/* homogeneous volume: assume shader evaluation at the starts gives + * the extinction coefficient for the entire line segment */ +ccl_device void volume_shadow_homogeneous(INTEGRATOR_STATE_ARGS, + Ray *ccl_restrict ray, + ShaderData *ccl_restrict sd, + float3 *ccl_restrict throughput) +{ + float3 sigma_t = zero_float3(); + + if (shadow_volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &sigma_t)) { + *throughput *= volume_color_transmittance(sigma_t, ray->t); + } +} +# endif + +/* heterogeneous volume: integrate stepping through the volume until we + * reach the end, get absorbed entirely, or run out of iterations */ +ccl_device void volume_shadow_heterogeneous(INTEGRATOR_STATE_ARGS, + Ray *ccl_restrict ray, + ShaderData *ccl_restrict sd, + float3 *ccl_restrict throughput, + const float object_step_size) +{ + /* Load random number state. */ + RNGState rng_state; + shadow_path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + + float3 tp = *throughput; + + /* Prepare for stepping. + * For shadows we do not offset all segments, since the starting point is + * already a random distance inside the volume. It also appears to create + * banding artifacts for unknown reasons. */ + int max_steps; + float step_size, step_shade_offset, unused; + volume_step_init(kg, + &rng_state, + object_step_size, + ray->t, + &step_size, + &step_shade_offset, + &unused, + &max_steps); + const float steps_offset = 1.0f; + + /* compute extinction at the start */ + float t = 0.0f; + + float3 sum = zero_float3(); + + for (int i = 0; i < max_steps; i++) { + /* advance to new position */ + float new_t = min(ray->t, (i + steps_offset) * step_size); + float dt = new_t - t; + + float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); + float3 sigma_t = zero_float3(); + + /* compute attenuation over segment */ + sd->P = new_P; + if (shadow_volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &sigma_t)) { + /* Compute expf() only for every Nth step, to save some calculations + * because exp(a)*exp(b) = exp(a+b), also do a quick VOLUME_THROUGHPUT_EPSILON + * check then. */ + sum += (-sigma_t * dt); + if ((i & 0x07) == 0) { /* ToDo: Other interval? */ + tp = *throughput * exp3(sum); + + /* stop if nearly all light is blocked */ + if (tp.x < VOLUME_THROUGHPUT_EPSILON && tp.y < VOLUME_THROUGHPUT_EPSILON && + tp.z < VOLUME_THROUGHPUT_EPSILON) + break; + } + } + + /* stop if at the end of the volume */ + t = new_t; + if (t == ray->t) { + /* Update throughput in case we haven't done it above */ + tp = *throughput * exp3(sum); + break; + } + } + + *throughput = tp; +} + +/* Equi-angular sampling as in: + * "Importance Sampling Techniques for Path Tracing in Participating Media" */ + +ccl_device float volume_equiangular_sample(const Ray *ccl_restrict ray, + const float3 light_P, + const float xi, + float *pdf) +{ + const float t = ray->t; + const float delta = dot((light_P - ray->P), ray->D); + const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); + if (UNLIKELY(D == 0.0f)) { + *pdf = 0.0f; + return 0.0f; + } + const float theta_a = -atan2f(delta, D); + const float theta_b = atan2f(t - delta, D); + const float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a); + if (UNLIKELY(theta_b == theta_a)) { + *pdf = 0.0f; + return 0.0f; + } + *pdf = D / ((theta_b - theta_a) * (D * D + t_ * t_)); + + return min(t, delta + t_); /* min is only for float precision errors */ +} + +ccl_device float volume_equiangular_pdf(const Ray *ccl_restrict ray, + const float3 light_P, + const float sample_t) +{ + const float delta = dot((light_P - ray->P), ray->D); + const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); + if (UNLIKELY(D == 0.0f)) { + return 0.0f; + } + + const float t = ray->t; + const float t_ = sample_t - delta; + + const float theta_a = -atan2f(delta, D); + const float theta_b = atan2f(t - delta, D); + if (UNLIKELY(theta_b == theta_a)) { + return 0.0f; + } + + const float pdf = D / ((theta_b - theta_a) * (D * D + t_ * t_)); + + return pdf; +} + +ccl_device float volume_equiangular_cdf(const Ray *ccl_restrict ray, + const float3 light_P, + const float sample_t) +{ + float delta = dot((light_P - ray->P), ray->D); + float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); + if (UNLIKELY(D == 0.0f)) { + return 0.0f; + } + + const float t = ray->t; + const float t_ = sample_t - delta; + + const float theta_a = -atan2f(delta, D); + const float theta_b = atan2f(t - delta, D); + if (UNLIKELY(theta_b == theta_a)) { + return 0.0f; + } + + const float theta_sample = atan2f(t_, D); + const float cdf = (theta_sample - theta_a) / (theta_b - theta_a); + + return cdf; +} + +/* Distance sampling */ + +ccl_device float volume_distance_sample( + float max_t, float3 sigma_t, int channel, float xi, float3 *transmittance, float3 *pdf) +{ + /* xi is [0, 1[ so log(0) should never happen, division by zero is + * avoided because sample_sigma_t > 0 when SD_SCATTER is set */ + float sample_sigma_t = volume_channel_get(sigma_t, channel); + float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); + float sample_transmittance = volume_channel_get(full_transmittance, channel); + + float sample_t = min(max_t, -logf(1.0f - xi * (1.0f - sample_transmittance)) / sample_sigma_t); + + *transmittance = volume_color_transmittance(sigma_t, sample_t); + *pdf = safe_divide_color(sigma_t * *transmittance, one_float3() - full_transmittance); + + /* todo: optimization: when taken together with hit/miss decision, + * the full_transmittance cancels out drops out and xi does not + * need to be remapped */ + + return sample_t; +} + +ccl_device float3 volume_distance_pdf(float max_t, float3 sigma_t, float sample_t) +{ + float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); + float3 transmittance = volume_color_transmittance(sigma_t, sample_t); + + return safe_divide_color(sigma_t * transmittance, one_float3() - full_transmittance); +} + +/* Emission */ + +ccl_device float3 volume_emission_integrate(VolumeShaderCoefficients *coeff, + int closure_flag, + float3 transmittance, + float t) +{ + /* integral E * exp(-sigma_t * t) from 0 to t = E * (1 - exp(-sigma_t * t))/sigma_t + * this goes to E * t as sigma_t goes to zero + * + * todo: we should use an epsilon to avoid precision issues near zero sigma_t */ + float3 emission = coeff->emission; + + if (closure_flag & SD_EXTINCTION) { + float3 sigma_t = coeff->sigma_t; + + emission.x *= (sigma_t.x > 0.0f) ? (1.0f - transmittance.x) / sigma_t.x : t; + emission.y *= (sigma_t.y > 0.0f) ? (1.0f - transmittance.y) / sigma_t.y : t; + emission.z *= (sigma_t.z > 0.0f) ? (1.0f - transmittance.z) / sigma_t.z : t; + } + else + emission *= t; + + return emission; +} + +/* Volume Integration */ + +typedef struct VolumeIntegrateState { + /* Volume segment extents. */ + float start_t; + float end_t; + + /* If volume is absorption-only up to this point, and no probabilistic + * scattering or termination has been used yet. */ + bool absorption_only; + + /* Random numbers for scattering. */ + float rscatter; + float rphase; + + /* Multiple importance sampling. */ + VolumeSampleMethod direct_sample_method; + bool use_mis; + float distance_pdf; + float equiangular_pdf; +} VolumeIntegrateState; + +ccl_device_forceinline void volume_integrate_step_scattering( + const ShaderData *sd, + const Ray *ray, + const float3 equiangular_light_P, + const VolumeShaderCoefficients &ccl_restrict coeff, + const float3 transmittance, + VolumeIntegrateState &ccl_restrict vstate, + VolumeIntegrateResult &ccl_restrict result) +{ + /* Pick random color channel, we use the Veach one-sample + * model with balance heuristic for the channels. */ + const float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); + float3 channel_pdf; + const int channel = volume_sample_channel( + albedo, result.indirect_throughput, vstate.rphase, &channel_pdf); + + /* Equiangular sampling for direct lighting. */ + if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR && !result.direct_scatter) { + if (result.direct_t >= vstate.start_t && result.direct_t <= vstate.end_t) { + const float new_dt = result.direct_t - vstate.start_t; + const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); + + result.direct_scatter = true; + result.direct_throughput *= coeff.sigma_s * new_transmittance / vstate.equiangular_pdf; + shader_copy_volume_phases(&result.direct_phases, sd); + + /* Multiple importance sampling. */ + if (vstate.use_mis) { + const float distance_pdf = vstate.distance_pdf * + dot(channel_pdf, coeff.sigma_t * new_transmittance); + const float mis_weight = 2.0f * power_heuristic(vstate.equiangular_pdf, distance_pdf); + result.direct_throughput *= mis_weight; + } + } + else { + result.direct_throughput *= transmittance; + vstate.distance_pdf *= dot(channel_pdf, transmittance); + } + } + + /* Distance sampling for indirect and optional direct lighting. */ + if (!result.indirect_scatter) { + /* decide if we will scatter or continue */ + const float sample_transmittance = volume_channel_get(transmittance, channel); + + if (1.0f - vstate.rscatter >= sample_transmittance) { + /* compute sampling distance */ + const float sample_sigma_t = volume_channel_get(coeff.sigma_t, channel); + const float new_dt = -logf(1.0f - vstate.rscatter) / sample_sigma_t; + const float new_t = vstate.start_t + new_dt; + + /* transmittance and pdf */ + const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); + const float distance_pdf = dot(channel_pdf, coeff.sigma_t * new_transmittance); + + /* throughput */ + result.indirect_scatter = true; + result.indirect_t = new_t; + result.indirect_throughput *= coeff.sigma_s * new_transmittance / distance_pdf; + shader_copy_volume_phases(&result.indirect_phases, sd); + + if (vstate.direct_sample_method != VOLUME_SAMPLE_EQUIANGULAR) { + /* If using distance sampling for direct light, just copy parameters + * of indirect light since we scatter at the same point then. */ + result.direct_scatter = true; + result.direct_t = result.indirect_t; + result.direct_throughput = result.indirect_throughput; + shader_copy_volume_phases(&result.direct_phases, sd); + + /* Multiple importance sampling. */ + if (vstate.use_mis) { + const float equiangular_pdf = volume_equiangular_pdf(ray, equiangular_light_P, new_t); + const float mis_weight = power_heuristic(vstate.distance_pdf * distance_pdf, + equiangular_pdf); + result.direct_throughput *= 2.0f * mis_weight; + } + } + } + else { + /* throughput */ + const float pdf = dot(channel_pdf, transmittance); + result.indirect_throughput *= transmittance / pdf; + if (vstate.direct_sample_method != VOLUME_SAMPLE_EQUIANGULAR) { + vstate.distance_pdf *= pdf; + } + + /* remap rscatter so we can reuse it and keep thing stratified */ + vstate.rscatter = 1.0f - (1.0f - vstate.rscatter) / sample_transmittance; + } + } +} + +/* heterogeneous volume distance sampling: integrate stepping through the + * volume until we reach the end, get absorbed entirely, or run out of + * iterations. this does probabilistically scatter or get transmitted through + * for path tracing where we don't want to branch. */ +ccl_device_forceinline void volume_integrate_heterogeneous( + INTEGRATOR_STATE_ARGS, + Ray *ccl_restrict ray, + ShaderData *ccl_restrict sd, + const RNGState *rng_state, + ccl_global float *ccl_restrict render_buffer, + const float object_step_size, + const VolumeSampleMethod direct_sample_method, + const float3 equiangular_light_P, + VolumeIntegrateResult &result) +{ + PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INTEGRATE); + + /* Prepare for stepping. + * Using a different step offset for the first step avoids banding artifacts. */ + int max_steps; + float step_size, step_shade_offset, steps_offset; + volume_step_init(kg, + rng_state, + object_step_size, + ray->t, + &step_size, + &step_shade_offset, + &steps_offset, + &max_steps); + + /* Initialize volume integration state. */ + VolumeIntegrateState vstate ccl_optional_struct_init; + vstate.start_t = 0.0f; + vstate.end_t = 0.0f; + vstate.absorption_only = true; + vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_SCATTER_DISTANCE); + vstate.rphase = path_state_rng_1D(kg, rng_state, PRNG_PHASE_CHANNEL); + + /* Multiple importance sampling: pick between equiangular and distance sampling strategy. */ + vstate.direct_sample_method = direct_sample_method; + vstate.use_mis = (direct_sample_method == VOLUME_SAMPLE_MIS); + if (vstate.use_mis) { + if (vstate.rscatter < 0.5f) { + vstate.rscatter *= 2.0f; + vstate.direct_sample_method = VOLUME_SAMPLE_DISTANCE; + } + else { + vstate.rscatter = (vstate.rscatter - 0.5f) * 2.0f; + vstate.direct_sample_method = VOLUME_SAMPLE_EQUIANGULAR; + } + } + vstate.equiangular_pdf = 0.0f; + vstate.distance_pdf = 1.0f; + + /* Initialize volume integration result. */ + const float3 throughput = INTEGRATOR_STATE(path, throughput); + result.direct_throughput = throughput; + result.indirect_throughput = throughput; + + /* Equiangular sampling: compute distance and PDF in advance. */ + if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) { + result.direct_t = volume_equiangular_sample( + ray, equiangular_light_P, vstate.rscatter, &vstate.equiangular_pdf); + } + +# ifdef __DENOISING_FEATURES__ + const bool write_denoising_features = (INTEGRATOR_STATE(path, flag) & + PATH_RAY_DENOISING_FEATURES); + float3 accum_albedo = zero_float3(); +# endif + float3 accum_emission = zero_float3(); + + for (int i = 0; i < max_steps; i++) { + /* Advance to new position */ + vstate.end_t = min(ray->t, (i + steps_offset) * step_size); + const float shade_t = vstate.start_t + (vstate.end_t - vstate.start_t) * step_shade_offset; + sd->P = ray->P + ray->D * shade_t; + + /* compute segment */ + VolumeShaderCoefficients coeff ccl_optional_struct_init; + if (volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &coeff)) { + const int closure_flag = sd->flag; + + /* Evaluate transmittance over segment. */ + const float dt = (vstate.end_t - vstate.start_t); + const float3 transmittance = (closure_flag & SD_EXTINCTION) ? + volume_color_transmittance(coeff.sigma_t, dt) : + one_float3(); + + /* Emission. */ + if (closure_flag & SD_EMISSION) { + /* Only write emission before indirect light scatter position, since we terminate + * stepping at that point if we have already found a direct light scatter position. */ + if (!result.indirect_scatter) { + const float3 emission = volume_emission_integrate( + &coeff, closure_flag, transmittance, dt); + accum_emission += emission; + } + } + + if (closure_flag & SD_EXTINCTION) { + if ((closure_flag & SD_SCATTER) || !vstate.absorption_only) { +# ifdef __DENOISING_FEATURES__ + /* Accumulate albedo for denoising features. */ + if (write_denoising_features && (closure_flag & SD_SCATTER)) { + const float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); + accum_albedo += result.indirect_throughput * albedo * (one_float3() - transmittance); + } +# endif + + /* Scattering and absorption. */ + volume_integrate_step_scattering( + sd, ray, equiangular_light_P, coeff, transmittance, vstate, result); + } + else { + /* Absorption only. */ + result.indirect_throughput *= transmittance; + result.direct_throughput *= transmittance; + } + + /* Stop if nearly all light blocked. */ + if (!result.indirect_scatter) { + if (max3(result.indirect_throughput) < VOLUME_THROUGHPUT_EPSILON) { + result.indirect_throughput = zero_float3(); + break; + } + } + else if (!result.direct_scatter) { + if (max3(result.direct_throughput) < VOLUME_THROUGHPUT_EPSILON) { + break; + } + } + } + + /* If we have scattering data for both direct and indirect, we're done. */ + if (result.direct_scatter && result.indirect_scatter) { + break; + } + } + + /* Stop if at the end of the volume. */ + vstate.start_t = vstate.end_t; + if (vstate.start_t == ray->t) { + break; + } + } + + /* Write accumulated emisison. */ + if (!is_zero(accum_emission)) { + kernel_accum_emission( + INTEGRATOR_STATE_PASS, result.indirect_throughput, accum_emission, render_buffer); + } + +# ifdef __DENOISING_FEATURES__ + /* Write denoising features. */ + if (write_denoising_features) { + kernel_write_denoising_features_volume( + INTEGRATOR_STATE_PASS, accum_albedo, result.indirect_scatter, render_buffer); + } +# endif /* __DENOISING_FEATURES__ */ +} + +# ifdef __EMISSION__ +/* Path tracing: sample point on light and evaluate light shader, then + * queue shadow ray to be traced. */ +ccl_device_forceinline bool integrate_volume_sample_light(INTEGRATOR_STATE_ARGS, + const ShaderData *ccl_restrict sd, + const RNGState *ccl_restrict rng_state, + LightSample *ccl_restrict ls) +{ + /* Test if there is a light or BSDF that needs direct light. */ + if (!kernel_data.integrator.use_direct_light) { + return false; + } + + /* Sample position on a light. */ + const int path_flag = INTEGRATOR_STATE(path, flag); + const uint bounce = INTEGRATOR_STATE(path, bounce); + float light_u, light_v; + path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); + + light_distribution_sample_from_volume_segment( + kg, light_u, light_v, sd->time, sd->P, bounce, path_flag, ls); + + if (ls->shader & SHADER_EXCLUDE_SCATTER) { + return false; + } + + return true; +} + +/* Path tracing: sample point on light and evaluate light shader, then + * queue shadow ray to be traced. */ +ccl_device_forceinline void integrate_volume_direct_light(INTEGRATOR_STATE_ARGS, + const ShaderData *ccl_restrict sd, + const RNGState *ccl_restrict rng_state, + const float3 P, + const ShaderVolumePhases *ccl_restrict + phases, + const float3 throughput, + LightSample *ccl_restrict ls) +{ + PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_DIRECT_LIGHT); + + if (!kernel_data.integrator.use_direct_light) { + return; + } + + /* Sample position on the same light again, now from the shading + * point where we scattered. + * + * TODO: decorrelate random numbers and use light_sample_new_position to + * avoid resampling the CDF. */ + { + const int path_flag = INTEGRATOR_STATE(path, flag); + const uint bounce = INTEGRATOR_STATE(path, bounce); + float light_u, light_v; + path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); + + if (!light_distribution_sample_from_position( + kg, light_u, light_v, sd->time, P, bounce, path_flag, ls)) { + return; + } + } + + /* Evaluate light shader. + * + * TODO: can we reuse sd memory? In theory we can move this after + * integrate_surface_bounce, evaluate the BSDF, and only then evaluate + * the light shader. This could also move to its own kernel, for + * non-constant light sources. */ + ShaderDataTinyStorage emission_sd_storage; + ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + const float3 light_eval = light_sample_shader_eval( + INTEGRATOR_STATE_PASS, emission_sd, ls, sd->time); + if (is_zero(light_eval)) { + return; + } + + /* Evaluate BSDF. */ + BsdfEval phase_eval ccl_optional_struct_init; + const float phase_pdf = shader_volume_phase_eval(kg, sd, phases, ls->D, &phase_eval); + + if (ls->shader & SHADER_USE_MIS) { + float mis_weight = power_heuristic(ls->pdf, phase_pdf); + bsdf_eval_mul(&phase_eval, mis_weight); + } + + bsdf_eval_mul3(&phase_eval, light_eval / ls->pdf); + + /* Path termination. */ + const float terminate = path_state_rng_light_termination(kg, rng_state); + if (light_sample_terminate(kg, ls, &phase_eval, terminate)) { + return; + } + + /* Create shadow ray. */ + Ray ray ccl_optional_struct_init; + light_sample_to_volume_shadow_ray(kg, sd, ls, P, &ray); + const bool is_light = light_sample_is_light(ls); + + /* Write shadow ray and associated state to global memory. */ + integrator_state_write_shadow_ray(INTEGRATOR_STATE_PASS, &ray); + + /* Copy state from main path to shadow path. */ + const uint16_t bounce = INTEGRATOR_STATE(path, bounce); + const uint16_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce); + uint32_t shadow_flag = INTEGRATOR_STATE(path, flag); + shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; + shadow_flag |= PATH_RAY_VOLUME_PASS; + const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval); + + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + const float3 diffuse_glossy_ratio = (bounce == 0) ? + one_float3() : + INTEGRATOR_STATE(path, diffuse_glossy_ratio); + INTEGRATOR_STATE_WRITE(shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; + } + + INTEGRATOR_STATE_WRITE(shadow_path, flag) = shadow_flag; + INTEGRATOR_STATE_WRITE(shadow_path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(shadow_path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_path, throughput) = throughput_phase; + + if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) { + INTEGRATOR_STATE_WRITE(shadow_path, unshadowed_throughput) = throughput; + } + + integrator_state_copy_volume_stack_to_shadow(INTEGRATOR_STATE_PASS); + + /* Branch off shadow kernel. */ + INTEGRATOR_SHADOW_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); +} +# endif + +/* Path tracing: scatter in new direction using phase function */ +ccl_device_forceinline bool integrate_volume_phase_scatter(INTEGRATOR_STATE_ARGS, + ShaderData *sd, + const RNGState *rng_state, + const ShaderVolumePhases *phases) +{ + PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INDIRECT_LIGHT); + + float phase_u, phase_v; + path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &phase_u, &phase_v); + + /* Phase closure, sample direction. */ + float phase_pdf; + BsdfEval phase_eval ccl_optional_struct_init; + float3 phase_omega_in ccl_optional_struct_init; + differential3 phase_domega_in ccl_optional_struct_init; + + const int label = shader_volume_phase_sample(kg, + sd, + phases, + phase_u, + phase_v, + &phase_eval, + &phase_omega_in, + &phase_domega_in, + &phase_pdf); + + if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) { + return false; + } + + /* Setup ray. */ + INTEGRATOR_STATE_WRITE(ray, P) = sd->P; + INTEGRATOR_STATE_WRITE(ray, D) = normalize(phase_omega_in); + INTEGRATOR_STATE_WRITE(ray, t) = FLT_MAX; + +# ifdef __RAY_DIFFERENTIALS__ + INTEGRATOR_STATE_WRITE(ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(ray, dD) = differential_make_compact(phase_domega_in); +# endif + + /* Update throughput. */ + const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval) / phase_pdf; + INTEGRATOR_STATE_WRITE(path, throughput) = throughput_phase; + + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + INTEGRATOR_STATE_WRITE(path, diffuse_glossy_ratio) = one_float3(); + } + + /* Update path state */ + INTEGRATOR_STATE_WRITE(path, mis_ray_pdf) = phase_pdf; + INTEGRATOR_STATE_WRITE(path, mis_ray_t) = 0.0f; + INTEGRATOR_STATE_WRITE(path, min_ray_pdf) = fminf(phase_pdf, + INTEGRATOR_STATE(path, min_ray_pdf)); + + path_state_next(INTEGRATOR_STATE_PASS, label); + return true; +} + +/* get the volume attenuation and emission over line segment defined by + * ray, with the assumption that there are no surfaces blocking light + * between the endpoints. distance sampling is used to decide if we will + * scatter or not. */ +ccl_device VolumeIntegrateEvent volume_integrate(INTEGRATOR_STATE_ARGS, + Ray *ccl_restrict ray, + ccl_global float *ccl_restrict render_buffer) +{ + ShaderData sd; + shader_setup_from_volume(kg, &sd, ray); + + /* Load random number state. */ + RNGState rng_state; + path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + + /* Sample light ahead of volume stepping, for equiangular sampling. */ + /* TODO: distant lights are ignored now, but could instead use even distribution. */ + LightSample ls ccl_optional_struct_init; + const bool need_light_sample = !(INTEGRATOR_STATE(path, flag) & PATH_RAY_TERMINATE); + const bool have_equiangular_sample = need_light_sample && + integrate_volume_sample_light( + INTEGRATOR_STATE_PASS, &sd, &rng_state, &ls) && + (ls.t != FLT_MAX); + + VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ? + volume_stack_sample_method(INTEGRATOR_STATE_PASS) : + VOLUME_SAMPLE_DISTANCE; + + /* Step through volume. */ + const float step_size = volume_stack_step_size(INTEGRATOR_STATE_PASS, [=](const int i) { + return integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); + }); + + /* TODO: expensive to zero closures? */ + VolumeIntegrateResult result = {}; + volume_integrate_heterogeneous(INTEGRATOR_STATE_PASS, + ray, + &sd, + &rng_state, + render_buffer, + step_size, + direct_sample_method, + ls.P, + result); + + /* Perform path termination. The intersect_closest will have already marked this path + * to be terminated. That will shading evaluating to leave out any scattering closures, + * but emission and absorption are still handled for multiple importance sampling. */ + const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + const float probability = (path_flag & PATH_RAY_TERMINATE_IN_NEXT_VOLUME) ? + 0.0f : + path_state_continuation_probability(INTEGRATOR_STATE_PASS, + path_flag); + if (probability == 0.0f) { + return VOLUME_PATH_MISSED; + } + + /* Direct light. */ + if (result.direct_scatter) { + const float3 direct_P = ray->P + result.direct_t * ray->D; + result.direct_throughput /= probability; + integrate_volume_direct_light(INTEGRATOR_STATE_PASS, + &sd, + &rng_state, + direct_P, + &result.direct_phases, + result.direct_throughput, + &ls); + } + + /* Indirect light. + * + * Only divide throughput by probability if we scatter. For the attenuation + * case the next surface will already do this division. */ + if (result.indirect_scatter) { + result.indirect_throughput /= probability; + } + INTEGRATOR_STATE_WRITE(path, throughput) = result.indirect_throughput; + + if (result.indirect_scatter) { + sd.P = ray->P + result.indirect_t * ray->D; + + if (integrate_volume_phase_scatter( + INTEGRATOR_STATE_PASS, &sd, &rng_state, &result.indirect_phases)) { + return VOLUME_PATH_SCATTERED; + } + else { + return VOLUME_PATH_MISSED; + } + } + else { + return VOLUME_PATH_ATTENUATED; + } +} + +#endif + +ccl_device void integrator_shade_volume(INTEGRATOR_STATE_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ + PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_SETUP); + +#ifdef __VOLUME__ + /* Setup shader data. */ + Ray ray ccl_optional_struct_init; + integrator_state_read_ray(INTEGRATOR_STATE_PASS, &ray); + + Intersection isect ccl_optional_struct_init; + integrator_state_read_isect(INTEGRATOR_STATE_PASS, &isect); + + /* Set ray length to current segment. */ + ray.t = (isect.prim != PRIM_NONE) ? isect.t : FLT_MAX; + + /* Clean volume stack for background rays. */ + if (isect.prim == PRIM_NONE) { + volume_stack_clean(INTEGRATOR_STATE_PASS); + } + + VolumeIntegrateEvent event = volume_integrate(INTEGRATOR_STATE_PASS, &ray, render_buffer); + + if (event == VOLUME_PATH_SCATTERED) { + /* Queue intersect_closest kernel. */ + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + return; + } + else if (event == VOLUME_PATH_MISSED) { + /* End path. */ + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); + return; + } + else { + /* Continue to background, light or surface. */ + if (isect.prim == PRIM_NONE) { + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, + DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + return; + } + else if (isect.type & PRIMITIVE_LAMP) { + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, + DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + return; + } + else { + /* Hit a surface, continue with surface kernel unless terminated. */ + const int shader = intersection_get_shader(kg, &isect); + const int flags = kernel_tex_fetch(__shaders, shader).flags; + + integrator_intersect_shader_next_kernel( + INTEGRATOR_STATE_PASS, &isect, shader, flags); + return; + } + } +#endif /* __VOLUME__ */ +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h new file mode 100644 index 00000000000..8cef9cf31e2 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_state.h @@ -0,0 +1,185 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Integrator State + * + * This file defines the data structures that define the state of a path. Any state that is + * preserved and passed between kernel executions is part of this. + * + * The size of this state must be kept as small as possible, to reduce cache misses and keep memory + * usage under control on GPUs that may execute millions of kernels. + * + * Memory may be allocated and passed along in different ways depending on the device. There may + * be a scalar layout, or AoS or SoA layout for batches. The state may be passed along as a pointer + * to every kernel, or the pointer may exist at program scope or in constant memory. To abstract + * these differences between devices and experiment with different layouts, macros are used. + * + * INTEGRATOR_STATE_ARGS: prepend to argument definitions for every function that accesses + * path state. + * INTEGRATOR_STATE_CONST_ARGS: same as INTEGRATOR_STATE_ARGS, when state is read-only + * INTEGRATOR_STATE_PASS: use to pass along state to other functions access it. + * + * INTEGRATOR_STATE(x, y): read nested struct member x.y of IntegratorState + * INTEGRATOR_STATE_WRITE(x, y): write to nested struct member x.y of IntegratorState + * + * INTEGRATOR_STATE_ARRAY(x, index, y): read x[index].y + * INTEGRATOR_STATE_ARRAY_WRITE(x, index, y): write x[index].y + * + * INTEGRATOR_STATE_COPY(to_x, from_x): copy contents of one nested struct to another + * + * INTEGRATOR_STATE_IS_NULL: test if any integrator state is available, for shader evaluation + * INTEGRATOR_STATE_PASS_NULL: use to pass empty state to other functions. + * + * NOTE: if we end up with a device that passes no arguments, the leading comma will be a problem. + * Can solve it with more macros if we encouter it, but rather ugly so postpone for now. + */ + +#include "kernel/kernel_types.h" + +#include "util/util_types.h" + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Constants + * + * TODO: these could be made dynamic depending on the features used in the scene. */ + +#define INTEGRATOR_VOLUME_STACK_SIZE VOLUME_STACK_SIZE +#define INTEGRATOR_SHADOW_ISECT_SIZE 4 + +/* Data structures */ + +/* Integrator State + * + * CPU rendering path state with AoS layout. */ +typedef struct IntegratorStateCPU { +#define KERNEL_STRUCT_BEGIN(name) struct { +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) type name; +#define KERNEL_STRUCT_ARRAY_MEMBER KERNEL_STRUCT_MEMBER +#define KERNEL_STRUCT_END(name) \ + } \ + name; +#define KERNEL_STRUCT_END_ARRAY(name, size) \ + } \ + name[size]; +#include "kernel/integrator/integrator_state_template.h" +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_ARRAY_MEMBER +#undef KERNEL_STRUCT_END +#undef KERNEL_STRUCT_END_ARRAY +} IntegratorStateCPU; + +/* Path Queue + * + * Keep track of which kernels are queued to be executed next in the path + * for GPU rendering. */ +typedef struct IntegratorQueueCounter { + int num_queued[DEVICE_KERNEL_INTEGRATOR_NUM]; +} IntegratorQueueCounter; + +/* Integrator State GPU + * + * GPU rendering path state with SoA layout. */ +typedef struct IntegratorStateGPU { +#define KERNEL_STRUCT_BEGIN(name) struct { +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) type *name; +#define KERNEL_STRUCT_ARRAY_MEMBER KERNEL_STRUCT_MEMBER +#define KERNEL_STRUCT_END(name) \ + } \ + name; +#define KERNEL_STRUCT_END_ARRAY(name, size) \ + } \ + name[size]; +#include "kernel/integrator/integrator_state_template.h" +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_ARRAY_MEMBER +#undef KERNEL_STRUCT_END +#undef KERNEL_STRUCT_END_ARRAY + + /* Count number of queued kernels. */ + IntegratorQueueCounter *queue_counter; + + /* Count number of kernels queued for specific shaders. */ + int *sort_key_counter[DEVICE_KERNEL_INTEGRATOR_NUM]; + + /* Index of path which will be used by a next shadow catcher split. */ + int *next_shadow_catcher_path_index; +} IntegratorStateGPU; + +/* Abstraction + * + * Macros to access data structures on different devices. + * + * Note that there is a special access function for the shadow catcher state. This access is to + * happen from a kernel which operates on a "main" path. Attempt to use shadow catcher accessors + * from a kernel which operates on a shadow catcher state will cause bad memory acces. */ + +#ifdef __KERNEL_CPU__ + +/* Scalar access on CPU. */ + +typedef IntegratorStateCPU *ccl_restrict IntegratorState; + +# define INTEGRATOR_STATE_ARGS \ + ccl_attr_maybe_unused const KernelGlobals *ccl_restrict kg, \ + IntegratorStateCPU *ccl_restrict state +# define INTEGRATOR_STATE_CONST_ARGS \ + ccl_attr_maybe_unused const KernelGlobals *ccl_restrict kg, \ + const IntegratorStateCPU *ccl_restrict state +# define INTEGRATOR_STATE_PASS kg, state + +# define INTEGRATOR_STATE_PASS_NULL kg, NULL +# define INTEGRATOR_STATE_IS_NULL (state == NULL) + +# define INTEGRATOR_STATE(nested_struct, member) \ + (((const IntegratorStateCPU *)state)->nested_struct.member) +# define INTEGRATOR_STATE_WRITE(nested_struct, member) (state->nested_struct.member) + +# define INTEGRATOR_STATE_ARRAY(nested_struct, array_index, member) \ + (((const IntegratorStateCPU *)state)->nested_struct[array_index].member) +# define INTEGRATOR_STATE_ARRAY_WRITE(nested_struct, array_index, member) \ + ((state)->nested_struct[array_index].member) + +#else /* __KERNEL_CPU__ */ + +/* Array access on GPU with Structure-of-Arrays. */ + +typedef int IntegratorState; + +# define INTEGRATOR_STATE_ARGS const KernelGlobals *ccl_restrict kg, const IntegratorState state +# define INTEGRATOR_STATE_CONST_ARGS \ + const KernelGlobals *ccl_restrict kg, const IntegratorState state +# define INTEGRATOR_STATE_PASS kg, state + +# define INTEGRATOR_STATE_PASS_NULL kg, -1 +# define INTEGRATOR_STATE_IS_NULL (state == -1) + +# define INTEGRATOR_STATE(nested_struct, member) \ + kernel_integrator_state.nested_struct.member[state] +# define INTEGRATOR_STATE_WRITE(nested_struct, member) INTEGRATOR_STATE(nested_struct, member) + +# define INTEGRATOR_STATE_ARRAY(nested_struct, array_index, member) \ + kernel_integrator_state.nested_struct[array_index].member[state] +# define INTEGRATOR_STATE_ARRAY_WRITE(nested_struct, array_index, member) \ + INTEGRATOR_STATE_ARRAY(nested_struct, array_index, member) + +#endif /* __KERNEL_CPU__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_state_flow.h b/intern/cycles/kernel/integrator/integrator_state_flow.h new file mode 100644 index 00000000000..8477efd7b66 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_state_flow.h @@ -0,0 +1,144 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/kernel_types.h" +#include "util/util_atomic.h" + +CCL_NAMESPACE_BEGIN + +/* Control Flow + * + * Utilities for control flow between kernels. The implementation may differ per device + * or even be handled on the host side. To abstract such differences, experiment with + * different implementations and for debugging, this is abstracted using macros. + * + * There is a main path for regular path tracing camera for path tracing. Shadows for next + * event estimation branch off from this into their own path, that may be computed in + * parallel while the main path continues. + * + * Each kernel on the main path must call one of these functions. These may not be called + * multiple times from the same kernel. + * + * INTEGRATOR_PATH_INIT(next_kernel) + * INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) + * INTEGRATOR_PATH_TERMINATE(current_kernel) + * + * For the shadow path similar functions are used, and again each shadow kernel must call + * one of them, and only once. + */ + +#define INTEGRATOR_PATH_IS_TERMINATED (INTEGRATOR_STATE(path, queued_kernel) == 0) +#define INTEGRATOR_SHADOW_PATH_IS_TERMINATED (INTEGRATOR_STATE(shadow_path, queued_kernel) == 0) + +#ifdef __KERNEL_GPU__ + +# define INTEGRATOR_PATH_INIT(next_kernel) \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ + 1); \ + INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; +# define INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) \ + atomic_fetch_and_sub_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ + 1); \ + INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; +# define INTEGRATOR_PATH_TERMINATE(current_kernel) \ + atomic_fetch_and_sub_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ + INTEGRATOR_STATE_WRITE(path, queued_kernel) = 0; + +# define INTEGRATOR_SHADOW_PATH_INIT(next_kernel) \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ + 1); \ + INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = next_kernel; +# define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ + atomic_fetch_and_sub_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ + 1); \ + INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = next_kernel; +# define INTEGRATOR_SHADOW_PATH_TERMINATE(current_kernel) \ + atomic_fetch_and_sub_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ + INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = 0; + +# define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ + { \ + const int key_ = key; \ + atomic_fetch_and_add_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ + INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; \ + INTEGRATOR_STATE_WRITE(path, shader_sort_key) = key_; \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], \ + 1); \ + } +# define INTEGRATOR_PATH_NEXT_SORTED(current_kernel, next_kernel, key) \ + { \ + const int key_ = key; \ + atomic_fetch_and_sub_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ + atomic_fetch_and_add_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ + INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; \ + INTEGRATOR_STATE_WRITE(path, shader_sort_key) = key_; \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], \ + 1); \ + } + +#else + +# define INTEGRATOR_PATH_INIT(next_kernel) \ + INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; +# define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ + { \ + INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; \ + (void)key; \ + } +# define INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) \ + { \ + INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; \ + (void)current_kernel; \ + } +# define INTEGRATOR_PATH_TERMINATE(current_kernel) \ + { \ + INTEGRATOR_STATE_WRITE(path, queued_kernel) = 0; \ + (void)current_kernel; \ + } +# define INTEGRATOR_PATH_NEXT_SORTED(current_kernel, next_kernel, key) \ + { \ + INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; \ + (void)key; \ + (void)current_kernel; \ + } + +# define INTEGRATOR_SHADOW_PATH_INIT(next_kernel) \ + INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = next_kernel; +# define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ + { \ + INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = next_kernel; \ + (void)current_kernel; \ + } +# define INTEGRATOR_SHADOW_PATH_TERMINATE(current_kernel) \ + { \ + INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = 0; \ + (void)current_kernel; \ + } + +#endif + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_state_template.h b/intern/cycles/kernel/integrator/integrator_state_template.h new file mode 100644 index 00000000000..41dd1bfcdbf --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_state_template.h @@ -0,0 +1,163 @@ + +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/************************************ Path State *****************************/ + +KERNEL_STRUCT_BEGIN(path) +/* Index of a pixel within the device render buffer where this path will write its result. + * To get an actual offset within the buffer the value needs to be multiplied by the + * `kernel_data.film.pass_stride`. + * + * The multiplication is delayed for later, so that state can use 32bit integer. */ +KERNEL_STRUCT_MEMBER(path, uint32_t, render_pixel_index, KERNEL_FEATURE_PATH_TRACING) +/* Current sample number. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, sample, KERNEL_FEATURE_PATH_TRACING) +/* Current ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current diffuse ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, diffuse_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current glossy ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, glossy_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current transmission ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, transmission_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current volume ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, volume_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current volume bounds ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, volume_bounds_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current transparent ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, transparent_bounce, KERNEL_FEATURE_PATH_TRACING) +/* DeviceKernel bit indicating queued kernels. + * TODO: reduce size? */ +KERNEL_STRUCT_MEMBER(path, uint32_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) +/* Random number generator seed. */ +KERNEL_STRUCT_MEMBER(path, uint32_t, rng_hash, KERNEL_FEATURE_PATH_TRACING) +/* Random number dimension offset. */ +KERNEL_STRUCT_MEMBER(path, uint32_t, rng_offset, KERNEL_FEATURE_PATH_TRACING) +/* enum PathRayFlag */ +KERNEL_STRUCT_MEMBER(path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) +/* Multiple importance sampling + * The PDF of BSDF sampling at the last scatter point, and distance to the + * last scatter point minus the last ray segment. This distance lets us + * compute the complete distance through transparent surfaces and volumes. */ +KERNEL_STRUCT_MEMBER(path, float, mis_ray_pdf, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(path, float, mis_ray_t, KERNEL_FEATURE_PATH_TRACING) +/* Filter glossy. */ +KERNEL_STRUCT_MEMBER(path, float, min_ray_pdf, KERNEL_FEATURE_PATH_TRACING) +/* Throughput. */ +KERNEL_STRUCT_MEMBER(path, float3, throughput, KERNEL_FEATURE_PATH_TRACING) +/* Ratio of throughput to distinguish diffuse and glossy render passes. */ +KERNEL_STRUCT_MEMBER(path, float3, diffuse_glossy_ratio, KERNEL_FEATURE_LIGHT_PASSES) +/* Denoising. */ +KERNEL_STRUCT_MEMBER(path, float3, denoising_feature_throughput, KERNEL_FEATURE_DENOISING) +/* Shader sorting. */ +/* TODO: compress as uint16? or leave out entirely and recompute key in sorting code? */ +KERNEL_STRUCT_MEMBER(path, uint32_t, shader_sort_key, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(path) + +/************************************** Ray ***********************************/ + +KERNEL_STRUCT_BEGIN(ray) +KERNEL_STRUCT_MEMBER(ray, float3, P, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float3, D, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, time, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, dP, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, dD, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(ray) + +/*************************** Intersection result ******************************/ + +/* Result from scene intersection. */ +KERNEL_STRUCT_BEGIN(isect) +KERNEL_STRUCT_MEMBER(isect, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(isect, float, u, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(isect, float, v, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(isect, int, prim, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(isect, int, object, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(isect, int, type, KERNEL_FEATURE_PATH_TRACING) +/* TODO: exclude for GPU. */ +KERNEL_STRUCT_MEMBER(isect, float3, Ng, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(isect) + +/*************** Subsurface closure state for subsurface kernel ***************/ + +KERNEL_STRUCT_BEGIN(subsurface) +KERNEL_STRUCT_MEMBER(subsurface, float3, albedo, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_MEMBER(subsurface, float3, radius, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_MEMBER(subsurface, float, anisotropy, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_MEMBER(subsurface, float, roughness, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_END(subsurface) + +/********************************** Volume Stack ******************************/ + +KERNEL_STRUCT_BEGIN(volume_stack) +KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, object, KERNEL_FEATURE_VOLUME) +KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, shader, KERNEL_FEATURE_VOLUME) +KERNEL_STRUCT_END_ARRAY(volume_stack, INTEGRATOR_VOLUME_STACK_SIZE) + +/********************************* Shadow Path State **************************/ + +KERNEL_STRUCT_BEGIN(shadow_path) +/* Current ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current transparent ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, transparent_bounce, KERNEL_FEATURE_PATH_TRACING) +/* DeviceKernel bit indicating queued kernels. + * TODO: reduce size? */ +KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) +/* enum PathRayFlag */ +KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) +/* Throughput. */ +KERNEL_STRUCT_MEMBER(shadow_path, float3, throughput, KERNEL_FEATURE_PATH_TRACING) +/* Throughput for shadow pass. */ +KERNEL_STRUCT_MEMBER(shadow_path, float3, unshadowed_throughput, KERNEL_FEATURE_SHADOW_PASS) +/* Ratio of throughput to distinguish diffuse and glossy render passes. */ +KERNEL_STRUCT_MEMBER(shadow_path, float3, diffuse_glossy_ratio, KERNEL_FEATURE_LIGHT_PASSES) +/* Number of intersections found by ray-tracing. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, num_hits, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(shadow_path) + +/********************************** Shadow Ray *******************************/ + +KERNEL_STRUCT_BEGIN(shadow_ray) +KERNEL_STRUCT_MEMBER(shadow_ray, float3, P, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float3, D, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(shadow_ray) + +/*********************** Shadow Intersection result **************************/ + +/* Result from scene intersection. */ +KERNEL_STRUCT_BEGIN(shadow_isect) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, u, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, v, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, prim, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, object, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, type, KERNEL_FEATURE_PATH_TRACING) +/* TODO: exclude for GPU. */ +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float3, Ng, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END_ARRAY(shadow_isect, INTEGRATOR_SHADOW_ISECT_SIZE) + +/**************************** Shadow Volume Stack *****************************/ + +KERNEL_STRUCT_BEGIN(shadow_volume_stack) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, object, KERNEL_FEATURE_VOLUME) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, shader, KERNEL_FEATURE_VOLUME) +KERNEL_STRUCT_END_ARRAY(shadow_volume_stack, INTEGRATOR_VOLUME_STACK_SIZE) diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h new file mode 100644 index 00000000000..cdf412fe22f --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -0,0 +1,273 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/integrator_state.h" +#include "kernel/kernel_differential.h" + +CCL_NAMESPACE_BEGIN + +/* Ray */ + +ccl_device_forceinline void integrator_state_write_ray(INTEGRATOR_STATE_ARGS, + const Ray *ccl_restrict ray) +{ + INTEGRATOR_STATE_WRITE(ray, P) = ray->P; + INTEGRATOR_STATE_WRITE(ray, D) = ray->D; + INTEGRATOR_STATE_WRITE(ray, t) = ray->t; + INTEGRATOR_STATE_WRITE(ray, time) = ray->time; + INTEGRATOR_STATE_WRITE(ray, dP) = ray->dP; + INTEGRATOR_STATE_WRITE(ray, dD) = ray->dD; +} + +ccl_device_forceinline void integrator_state_read_ray(INTEGRATOR_STATE_CONST_ARGS, + Ray *ccl_restrict ray) +{ + ray->P = INTEGRATOR_STATE(ray, P); + ray->D = INTEGRATOR_STATE(ray, D); + ray->t = INTEGRATOR_STATE(ray, t); + ray->time = INTEGRATOR_STATE(ray, time); + ray->dP = INTEGRATOR_STATE(ray, dP); + ray->dD = INTEGRATOR_STATE(ray, dD); +} + +/* Shadow Ray */ + +ccl_device_forceinline void integrator_state_write_shadow_ray(INTEGRATOR_STATE_ARGS, + const Ray *ccl_restrict ray) +{ + INTEGRATOR_STATE_WRITE(shadow_ray, P) = ray->P; + INTEGRATOR_STATE_WRITE(shadow_ray, D) = ray->D; + INTEGRATOR_STATE_WRITE(shadow_ray, t) = ray->t; + INTEGRATOR_STATE_WRITE(shadow_ray, time) = ray->time; + INTEGRATOR_STATE_WRITE(shadow_ray, dP) = ray->dP; +} + +ccl_device_forceinline void integrator_state_read_shadow_ray(INTEGRATOR_STATE_CONST_ARGS, + Ray *ccl_restrict ray) +{ + ray->P = INTEGRATOR_STATE(shadow_ray, P); + ray->D = INTEGRATOR_STATE(shadow_ray, D); + ray->t = INTEGRATOR_STATE(shadow_ray, t); + ray->time = INTEGRATOR_STATE(shadow_ray, time); + ray->dP = INTEGRATOR_STATE(shadow_ray, dP); + ray->dD = differential_zero_compact(); +} + +/* Intersection */ + +ccl_device_forceinline void integrator_state_write_isect(INTEGRATOR_STATE_ARGS, + const Intersection *ccl_restrict isect) +{ + INTEGRATOR_STATE_WRITE(isect, t) = isect->t; + INTEGRATOR_STATE_WRITE(isect, u) = isect->u; + INTEGRATOR_STATE_WRITE(isect, v) = isect->v; + INTEGRATOR_STATE_WRITE(isect, object) = isect->object; + INTEGRATOR_STATE_WRITE(isect, prim) = isect->prim; + INTEGRATOR_STATE_WRITE(isect, type) = isect->type; +#ifdef __EMBREE__ + INTEGRATOR_STATE_WRITE(isect, Ng) = isect->Ng; +#endif +} + +ccl_device_forceinline void integrator_state_read_isect(INTEGRATOR_STATE_CONST_ARGS, + Intersection *ccl_restrict isect) +{ + isect->prim = INTEGRATOR_STATE(isect, prim); + isect->object = INTEGRATOR_STATE(isect, object); + isect->type = INTEGRATOR_STATE(isect, type); + isect->u = INTEGRATOR_STATE(isect, u); + isect->v = INTEGRATOR_STATE(isect, v); + isect->t = INTEGRATOR_STATE(isect, t); +#ifdef __EMBREE__ + isect->Ng = INTEGRATOR_STATE(isect, Ng); +#endif +} + +ccl_device_forceinline VolumeStack integrator_state_read_volume_stack(INTEGRATOR_STATE_CONST_ARGS, + int i) +{ + VolumeStack entry = {INTEGRATOR_STATE_ARRAY(volume_stack, i, object), + INTEGRATOR_STATE_ARRAY(volume_stack, i, shader)}; + return entry; +} + +ccl_device_forceinline void integrator_state_write_volume_stack(INTEGRATOR_STATE_ARGS, + int i, + VolumeStack entry) +{ + INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, i, object) = entry.object; + INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, i, shader) = entry.shader; +} + +ccl_device_forceinline bool integrator_state_volume_stack_is_empty(INTEGRATOR_STATE_CONST_ARGS) +{ + return (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) ? + INTEGRATOR_STATE_ARRAY(volume_stack, 0, shader) == SHADER_NONE : + true; +} + +/* Shadow Intersection */ + +ccl_device_forceinline void integrator_state_write_shadow_isect( + INTEGRATOR_STATE_ARGS, const Intersection *ccl_restrict isect, const int index) +{ + INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, t) = isect->t; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, u) = isect->u; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, v) = isect->v; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, object) = isect->object; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, prim) = isect->prim; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, type) = isect->type; +#ifdef __EMBREE__ + INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, Ng) = isect->Ng; +#endif +} + +ccl_device_forceinline void integrator_state_read_shadow_isect(INTEGRATOR_STATE_CONST_ARGS, + Intersection *ccl_restrict isect, + const int index) +{ + isect->prim = INTEGRATOR_STATE_ARRAY(shadow_isect, index, prim); + isect->object = INTEGRATOR_STATE_ARRAY(shadow_isect, index, object); + isect->type = INTEGRATOR_STATE_ARRAY(shadow_isect, index, type); + isect->u = INTEGRATOR_STATE_ARRAY(shadow_isect, index, u); + isect->v = INTEGRATOR_STATE_ARRAY(shadow_isect, index, v); + isect->t = INTEGRATOR_STATE_ARRAY(shadow_isect, index, t); +#ifdef __EMBREE__ + isect->Ng = INTEGRATOR_STATE_ARRAY(shadow_isect, index, Ng); +#endif +} + +ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(INTEGRATOR_STATE_ARGS) +{ + if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { + for (int i = 0; i < INTEGRATOR_VOLUME_STACK_SIZE; i++) { + INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, i, object) = INTEGRATOR_STATE_ARRAY( + volume_stack, i, object); + INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, i, shader) = INTEGRATOR_STATE_ARRAY( + volume_stack, i, shader); + } + } +} + +ccl_device_forceinline VolumeStack +integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_CONST_ARGS, int i) +{ + VolumeStack entry = {INTEGRATOR_STATE_ARRAY(shadow_volume_stack, i, object), + INTEGRATOR_STATE_ARRAY(shadow_volume_stack, i, shader)}; + return entry; +} + +ccl_device_forceinline bool integrator_state_shadow_volume_stack_is_empty( + INTEGRATOR_STATE_CONST_ARGS) +{ + return (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) ? + INTEGRATOR_STATE_ARRAY(shadow_volume_stack, 0, shader) == SHADER_NONE : + true; +} + +ccl_device_forceinline void integrator_state_write_shadow_volume_stack(INTEGRATOR_STATE_ARGS, + int i, + VolumeStack entry) +{ + INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, i, object) = entry.object; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, i, shader) = entry.shader; +} + +#if defined(__KERNEL_GPU__) +ccl_device_inline void integrator_state_copy_only(const IntegratorState to_state, + const IntegratorState state) +{ + int index; + + /* Rely on the compiler to optimize out unused assignments and `while(false)`'s. */ + +# define KERNEL_STRUCT_BEGIN(name) \ + index = 0; \ + do { + +# define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) \ + if (kernel_integrator_state.parent_struct.name != nullptr) { \ + kernel_integrator_state.parent_struct.name[to_state] = \ + kernel_integrator_state.parent_struct.name[state]; \ + } + +# define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) \ + if (kernel_integrator_state.parent_struct[index].name != nullptr) { \ + kernel_integrator_state.parent_struct[index].name[to_state] = \ + kernel_integrator_state.parent_struct[index].name[state]; \ + } + +# define KERNEL_STRUCT_END(name) \ + } \ + while (false) \ + ; + +# define KERNEL_STRUCT_END_ARRAY(name, array_size) \ + ++index; \ + } \ + while (index < array_size) \ + ; + +# include "kernel/integrator/integrator_state_template.h" + +# undef KERNEL_STRUCT_BEGIN +# undef KERNEL_STRUCT_MEMBER +# undef KERNEL_STRUCT_ARRAY_MEMBER +# undef KERNEL_STRUCT_END +# undef KERNEL_STRUCT_END_ARRAY +} + +ccl_device_inline void integrator_state_move(const IntegratorState to_state, + const IntegratorState state) +{ + integrator_state_copy_only(to_state, state); + + INTEGRATOR_STATE_WRITE(path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = 0; +} + +#endif + +/* NOTE: Leaves kernel scheduling information untouched. Use INIT semantic for one of the paths + * after this function. */ +ccl_device_inline void integrator_state_shadow_catcher_split(INTEGRATOR_STATE_ARGS) +{ +#if defined(__KERNEL_GPU__) + const IntegratorState to_state = atomic_fetch_and_add_uint32( + &kernel_integrator_state.next_shadow_catcher_path_index[0], 1); + + integrator_state_copy_only(to_state, state); + + kernel_integrator_state.path.flag[to_state] |= PATH_RAY_SHADOW_CATCHER_PASS; + + /* Sanity check: expect to split in the intersect-closest kernel, where there is no shadow ray + * and no sorting yet. */ + kernel_assert(INTEGRATOR_STATE(shadow_path, queued_kernel) == 0); + kernel_assert(kernel_integrator_state.sort_key_counter[INTEGRATOR_STATE(path, queued_kernel)] == + nullptr); +#else + + IntegratorStateCPU *ccl_restrict split_state = state + 1; + + *split_state = *state; + + split_state->path.flag |= PATH_RAY_SHADOW_CATCHER_PASS; +#endif +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/integrator_subsurface.h new file mode 100644 index 00000000000..9490738404e --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_subsurface.h @@ -0,0 +1,623 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/kernel_path_state.h" +#include "kernel/kernel_projection.h" +#include "kernel/kernel_shader.h" + +#include "kernel/bvh/bvh.h" + +#include "kernel/closure/alloc.h" +#include "kernel/closure/bsdf_diffuse.h" +#include "kernel/closure/bsdf_principled_diffuse.h" +#include "kernel/closure/bssrdf.h" +#include "kernel/closure/volume.h" + +#include "kernel/integrator/integrator_intersect_volume_stack.h" + +CCL_NAMESPACE_BEGIN + +#ifdef __SUBSURFACE__ + +ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, ShaderData *sd, const ShaderClosure *sc) +{ + /* We should never have two consecutive BSSRDF bounces, the second one should + * be converted to a diffuse BSDF to avoid this. */ + kernel_assert(!(INTEGRATOR_STATE(path, flag) & PATH_RAY_DIFFUSE_ANCESTOR)); + + /* Setup path state for intersect_subsurface kernel. */ + const Bssrdf *bssrdf = (const Bssrdf *)sc; + + /* Setup ray into surface. */ + INTEGRATOR_STATE_WRITE(ray, P) = sd->P; + INTEGRATOR_STATE_WRITE(ray, D) = sd->N; + INTEGRATOR_STATE_WRITE(ray, t) = FLT_MAX; + INTEGRATOR_STATE_WRITE(ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(ray, dD) = differential_zero_compact(); + + /* Pass along object info, reusing isect to save memory. */ + INTEGRATOR_STATE_WRITE(isect, Ng) = sd->Ng; + INTEGRATOR_STATE_WRITE(isect, object) = sd->object; + + /* Pass BSSRDF parameters. */ + const uint32_t path_flag = INTEGRATOR_STATE_WRITE(path, flag); + INTEGRATOR_STATE_WRITE(path, flag) = (path_flag & ~PATH_RAY_CAMERA) | PATH_RAY_SUBSURFACE; + INTEGRATOR_STATE_WRITE(path, throughput) *= shader_bssrdf_sample_weight(sd, sc); + + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + if (INTEGRATOR_STATE(path, bounce) == 0) { + INTEGRATOR_STATE_WRITE(path, diffuse_glossy_ratio) = one_float3(); + } + } + + INTEGRATOR_STATE_WRITE(subsurface, albedo) = bssrdf->albedo; + INTEGRATOR_STATE_WRITE(subsurface, radius) = bssrdf->radius; + INTEGRATOR_STATE_WRITE(subsurface, roughness) = bssrdf->roughness; + INTEGRATOR_STATE_WRITE(subsurface, anisotropy) = bssrdf->anisotropy; + + return LABEL_SUBSURFACE_SCATTER; +} + +ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, ShaderData *sd) +{ + /* Get bump mapped normal from shader evaluation at exit point. */ + float3 N = sd->N; + if (sd->flag & SD_HAS_BSSRDF_BUMP) { + N = shader_bssrdf_normal(sd); + } + + /* Setup diffuse BSDF at the exit point. This replaces shader_eval_surface. */ + sd->flag &= ~SD_CLOSURE_FLAGS; + sd->num_closure = 0; + sd->num_closure_left = kernel_data.max_closures; + + const float3 weight = one_float3(); + const float roughness = INTEGRATOR_STATE(subsurface, roughness); + +# ifdef __PRINCIPLED__ + if (roughness != FLT_MAX) { + PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( + sd, sizeof(PrincipledDiffuseBsdf), weight); + + if (bsdf) { + bsdf->N = N; + bsdf->roughness = roughness; + sd->flag |= bsdf_principled_diffuse_setup(bsdf); + + /* replace CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID with this special ID so render passes + * can recognize it as not being a regular Disney principled diffuse closure */ + bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID; + } + } + else +# endif /* __PRINCIPLED__ */ + { + DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight); + + if (bsdf) { + bsdf->N = N; + sd->flag |= bsdf_diffuse_setup(bsdf); + + /* replace CLOSURE_BSDF_DIFFUSE_ID with this special ID so render passes + * can recognize it as not being a regular diffuse closure */ + bsdf->type = CLOSURE_BSDF_BSSRDF_ID; + } + } +} + +/* Random walk subsurface scattering. + * + * "Practical and Controllable Subsurface Scattering for Production Path + * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ + +/* Support for anisotropy from: + * "Path Traced Subsurface Scattering using Anisotropic Phase Functions + * and Non-Exponential Free Flights". + * Magnus Wrenninge, Ryusuke Villemin, Christophe Hery. + * https://graphics.pixar.com/library/PathTracedSubsurface/ */ + +ccl_device void subsurface_random_walk_remap( + const float albedo, const float d, float g, float *sigma_t, float *alpha) +{ + /* Compute attenuation and scattering coefficients from albedo. */ + const float g2 = g * g; + const float g3 = g2 * g; + const float g4 = g3 * g; + const float g5 = g4 * g; + const float g6 = g5 * g; + const float g7 = g6 * g; + + const float A = 1.8260523782f + -1.28451056436f * g + -1.79904629312f * g2 + + 9.19393289202f * g3 + -22.8215585862f * g4 + 32.0234874259f * g5 + + -23.6264803333f * g6 + 7.21067002658f * g7; + const float B = 4.98511194385f + + 0.127355959438f * + expf(31.1491581433f * g + -201.847017512f * g2 + 841.576016723f * g3 + + -2018.09288505f * g4 + 2731.71560286f * g5 + -1935.41424244f * g6 + + 559.009054474f * g7); + const float C = 1.09686102424f + -0.394704063468f * g + 1.05258115941f * g2 + + -8.83963712726f * g3 + 28.8643230661f * g4 + -46.8802913581f * g5 + + 38.5402837518f * g6 + -12.7181042538f * g7; + const float D = 0.496310210422f + 0.360146581622f * g + -2.15139309747f * g2 + + 17.8896899217f * g3 + -55.2984010333f * g4 + 82.065982243f * g5 + + -58.5106008578f * g6 + 15.8478295021f * g7; + const float E = 4.23190299701f + + 0.00310603949088f * + expf(76.7316253952f * g + -594.356773233f * g2 + 2448.8834203f * g3 + + -5576.68528998f * g4 + 7116.60171912f * g5 + -4763.54467887f * g6 + + 1303.5318055f * g7); + const float F = 2.40602999408f + -2.51814844609f * g + 9.18494908356f * g2 + + -79.2191708682f * g3 + 259.082868209f * g4 + -403.613804597f * g5 + + 302.85712436f * g6 + -87.4370473567f * g7; + + const float blend = powf(albedo, 0.25f); + + *alpha = (1.0f - blend) * A * powf(atanf(B * albedo), C) + + blend * D * powf(atanf(E * albedo), F); + *alpha = clamp(*alpha, 0.0f, 0.999999f); // because of numerical precision + + float sigma_t_prime = 1.0f / fmaxf(d, 1e-16f); + *sigma_t = sigma_t_prime / (1.0f - g); +} + +ccl_device void subsurface_random_walk_coefficients(const float3 albedo, + const float3 radius, + const float anisotropy, + float3 *sigma_t, + float3 *alpha, + float3 *throughput) +{ + float sigma_t_x, sigma_t_y, sigma_t_z; + float alpha_x, alpha_y, alpha_z; + + subsurface_random_walk_remap(albedo.x, radius.x, anisotropy, &sigma_t_x, &alpha_x); + subsurface_random_walk_remap(albedo.y, radius.y, anisotropy, &sigma_t_y, &alpha_y); + subsurface_random_walk_remap(albedo.z, radius.z, anisotropy, &sigma_t_z, &alpha_z); + + /* Throughput already contains closure weight at this point, which includes the + * albedo, as well as closure mixing and Fresnel weights. Divide out the albedo + * which will be added through scattering. */ + *throughput = safe_divide_color(*throughput, albedo); + + /* With low albedo values (like 0.025) we get diffusion_length 1.0 and + * infinite phase functions. To avoid a sharp discontinuity as we go from + * such values to 0.0, increase alpha and reduce the throughput to compensate. */ + const float min_alpha = 0.2f; + if (alpha_x < min_alpha) { + (*throughput).x *= alpha_x / min_alpha; + alpha_x = min_alpha; + } + if (alpha_y < min_alpha) { + (*throughput).y *= alpha_y / min_alpha; + alpha_y = min_alpha; + } + if (alpha_z < min_alpha) { + (*throughput).z *= alpha_z / min_alpha; + alpha_z = min_alpha; + } + + *sigma_t = make_float3(sigma_t_x, sigma_t_y, sigma_t_z); + *alpha = make_float3(alpha_x, alpha_y, alpha_z); +} + +/* References for Dwivedi sampling: + * + * [1] "A Zero-variance-based Sampling Scheme for Monte Carlo Subsurface Scattering" + * by Jaroslav Křivánek and Eugene d'Eon (SIGGRAPH 2014) + * https://cgg.mff.cuni.cz/~jaroslav/papers/2014-zerovar/ + * + * [2] "Improving the Dwivedi Sampling Scheme" + * by Johannes Meng, Johannes Hanika, and Carsten Dachsbacher (EGSR 2016) + * https://cg.ivd.kit.edu/1951.php + * + * [3] "Zero-Variance Theory for Efficient Subsurface Scattering" + * by Eugene d'Eon and Jaroslav Křivánek (SIGGRAPH 2020) + * https://iliyan.com/publications/RenderingCourse2020 + */ + +ccl_device_forceinline float eval_phase_dwivedi(float v, float phase_log, float cos_theta) +{ + /* Eq. 9 from [2] using precomputed log((v + 1) / (v - 1)) */ + return 1.0f / ((v - cos_theta) * phase_log); +} + +ccl_device_forceinline float sample_phase_dwivedi(float v, float phase_log, float rand) +{ + /* Based on Eq. 10 from [2]: `v - (v + 1) * pow((v - 1) / (v + 1), rand)` + * Since we're already pre-computing `phase_log = log((v + 1) / (v - 1))` for the evaluation, + * we can implement the power function like this. */ + return v - (v + 1.0f) * expf(-rand * phase_log); +} + +ccl_device_forceinline float diffusion_length_dwivedi(float alpha) +{ + /* Eq. 67 from [3] */ + return 1.0f / sqrtf(1.0f - powf(alpha, 2.44294f - 0.0215813f * alpha + 0.578637f / alpha)); +} + +ccl_device_forceinline float3 direction_from_cosine(float3 D, float cos_theta, float randv) +{ + float sin_theta = safe_sqrtf(1.0f - cos_theta * cos_theta); + float phi = M_2PI_F * randv; + float3 dir = make_float3(sin_theta * cosf(phi), sin_theta * sinf(phi), cos_theta); + + float3 T, B; + make_orthonormals(D, &T, &B); + return dir.x * T + dir.y * B + dir.z * D; +} + +ccl_device_forceinline float3 subsurface_random_walk_pdf(float3 sigma_t, + float t, + bool hit, + float3 *transmittance) +{ + float3 T = volume_color_transmittance(sigma_t, t); + if (transmittance) { + *transmittance = T; + } + return hit ? T : sigma_t * T; +} + +/* Define the below variable to get the similarity code active, + * and the value represents the cutoff level */ +# define SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL 9 + +ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, + RNGState rng_state, + Ray &ray, + LocalIntersection &ss_isect) +{ + float bssrdf_u, bssrdf_v; + path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); + + const float3 P = INTEGRATOR_STATE(ray, P); + const float3 N = INTEGRATOR_STATE(ray, D); + const float ray_dP = INTEGRATOR_STATE(ray, dP); + const float time = INTEGRATOR_STATE(ray, time); + const float3 Ng = INTEGRATOR_STATE(isect, Ng); + const int object = INTEGRATOR_STATE(isect, object); + + /* Sample diffuse surface scatter into the object. */ + float3 D; + float pdf; + sample_cos_hemisphere(-N, bssrdf_u, bssrdf_v, &D, &pdf); + if (dot(-Ng, D) <= 0.0f) { + return false; + } + + /* Setup ray. */ + ray.P = ray_offset(P, -Ng); + ray.D = D; + ray.t = FLT_MAX; + ray.time = time; + ray.dP = ray_dP; + ray.dD = differential_zero_compact(); + +# ifndef __KERNEL_OPTIX__ + /* Compute or fetch object transforms. */ + Transform ob_itfm ccl_optional_struct_init; + Transform ob_tfm = object_fetch_transform_motion_test(kg, object, time, &ob_itfm); +# endif + + /* Convert subsurface to volume coefficients. + * The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */ + const float3 albedo = INTEGRATOR_STATE(subsurface, albedo); + const float3 radius = INTEGRATOR_STATE(subsurface, radius); + const float anisotropy = INTEGRATOR_STATE(subsurface, anisotropy); + + float3 sigma_t, alpha; + float3 throughput = INTEGRATOR_STATE_WRITE(path, throughput); + subsurface_random_walk_coefficients(albedo, radius, anisotropy, &sigma_t, &alpha, &throughput); + float3 sigma_s = sigma_t * alpha; + + /* Theoretically it should be better to use the exact alpha for the channel we're sampling at + * each bounce, but in practice there doesn't seem to be a noticeable difference in exchange + * for making the code significantly more complex and slower (if direction sampling depends on + * the sampled channel, we need to compute its PDF per-channel and consider it for MIS later on). + * + * Since the strength of the guided sampling increases as alpha gets lower, using a value that + * is too low results in fireflies while one that's too high just gives a bit more noise. + * Therefore, the code here uses the highest of the three albedos to be safe. */ + const float diffusion_length = diffusion_length_dwivedi(max3(alpha)); + + if (diffusion_length == 1.0f) { + /* With specific values of alpha the length might become 1, which in asymptotic makes phase to + * be infinite. After first bounce it will cause throughput to be 0. Do early output, avoiding + * numerical issues and extra unneeded work. */ + return false; + } + + /* Precompute term for phase sampling. */ + const float phase_log = logf((diffusion_length + 1.0f) / (diffusion_length - 1.0f)); + + /* Modify state for RNGs, decorrelated from other paths. */ + rng_state.rng_hash = cmj_hash(rng_state.rng_hash + rng_state.rng_offset, 0xdeadbeef); + + /* Random walk until we hit the surface again. */ + bool hit = false; + bool have_opposite_interface = false; + float opposite_distance = 0.0f; + + /* Todo: Disable for alpha>0.999 or so? */ + /* Our heuristic, a compromise between guiding and classic. */ + const float guided_fraction = 1.0f - fmaxf(0.5f, powf(fabsf(anisotropy), 0.125f)); + +# ifdef SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL + float3 sigma_s_star = sigma_s * (1.0f - anisotropy); + float3 sigma_t_star = sigma_t - sigma_s + sigma_s_star; + float3 sigma_t_org = sigma_t; + float3 sigma_s_org = sigma_s; + const float anisotropy_org = anisotropy; + const float guided_fraction_org = guided_fraction; +# endif + + for (int bounce = 0; bounce < BSSRDF_MAX_BOUNCES; bounce++) { + /* Advance random number offset. */ + rng_state.rng_offset += PRNG_BOUNCE_NUM; + +# ifdef SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL + // shadow with local variables according to depth + float anisotropy, guided_fraction; + float3 sigma_s, sigma_t; + if (bounce <= SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL) { + anisotropy = anisotropy_org; + guided_fraction = guided_fraction_org; + sigma_t = sigma_t_org; + sigma_s = sigma_s_org; + } + else { + anisotropy = 0.0f; + guided_fraction = 0.75f; // back to isotropic heuristic from Blender + sigma_t = sigma_t_star; + sigma_s = sigma_s_star; + } +# endif + + /* Sample color channel, use MIS with balance heuristic. */ + float rphase = path_state_rng_1D(kg, &rng_state, PRNG_PHASE_CHANNEL); + float3 channel_pdf; + int channel = volume_sample_channel(alpha, throughput, rphase, &channel_pdf); + float sample_sigma_t = volume_channel_get(sigma_t, channel); + float randt = path_state_rng_1D(kg, &rng_state, PRNG_SCATTER_DISTANCE); + + /* We need the result of the raycast to compute the full guided PDF, so just remember the + * relevant terms to avoid recomputing them later. */ + float backward_fraction = 0.0f; + float forward_pdf_factor = 0.0f; + float forward_stretching = 1.0f; + float backward_pdf_factor = 0.0f; + float backward_stretching = 1.0f; + + /* For the initial ray, we already know the direction, so just do classic distance sampling. */ + if (bounce > 0) { + /* Decide whether we should use guided or classic sampling. */ + bool guided = (path_state_rng_1D(kg, &rng_state, PRNG_LIGHT_TERMINATE) < guided_fraction); + + /* Determine if we want to sample away from the incoming interface. + * This only happens if we found a nearby opposite interface, and the probability for it + * depends on how close we are to it already. + * This probability term comes from the recorded presentation of [3]. */ + bool guide_backward = false; + if (have_opposite_interface) { + /* Compute distance of the random walk between the tangent plane at the starting point + * and the assumed opposite interface (the parallel plane that contains the point we + * found in our ray query for the opposite side). */ + float x = clamp(dot(ray.P - P, -N), 0.0f, opposite_distance); + backward_fraction = 1.0f / + (1.0f + expf((opposite_distance - 2.0f * x) / diffusion_length)); + guide_backward = path_state_rng_1D(kg, &rng_state, PRNG_TERMINATE) < backward_fraction; + } + + /* Sample scattering direction. */ + float scatter_u, scatter_v; + path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &scatter_u, &scatter_v); + float cos_theta; + float hg_pdf; + if (guided) { + cos_theta = sample_phase_dwivedi(diffusion_length, phase_log, scatter_u); + /* The backwards guiding distribution is just mirrored along sd->N, so swapping the + * sign here is enough to sample from that instead. */ + if (guide_backward) { + cos_theta = -cos_theta; + } + float3 newD = direction_from_cosine(N, cos_theta, scatter_v); + hg_pdf = single_peaked_henyey_greenstein(dot(ray.D, newD), anisotropy); + ray.D = newD; + } + else { + float3 newD = henyey_greenstrein_sample(ray.D, anisotropy, scatter_u, scatter_v, &hg_pdf); + cos_theta = dot(newD, N); + ray.D = newD; + } + + /* Compute PDF factor caused by phase sampling (as the ratio of guided / classic). + * Since phase sampling is channel-independent, we can get away with applying a factor + * to the guided PDF, which implicitly means pulling out the classic PDF term and letting + * it cancel with an equivalent term in the numerator of the full estimator. + * For the backward PDF, we again reuse the same probability distribution with a sign swap. + */ + forward_pdf_factor = M_1_2PI_F * eval_phase_dwivedi(diffusion_length, phase_log, cos_theta) / + hg_pdf; + backward_pdf_factor = M_1_2PI_F * + eval_phase_dwivedi(diffusion_length, phase_log, -cos_theta) / hg_pdf; + + /* Prepare distance sampling. + * For the backwards case, this also needs the sign swapped since now directions against + * sd->N (and therefore with negative cos_theta) are preferred. */ + forward_stretching = (1.0f - cos_theta / diffusion_length); + backward_stretching = (1.0f + cos_theta / diffusion_length); + if (guided) { + sample_sigma_t *= guide_backward ? backward_stretching : forward_stretching; + } + } + + /* Sample direction along ray. */ + float t = -logf(1.0f - randt) / sample_sigma_t; + + /* On the first bounce, we use the raycast to check if the opposite side is nearby. + * If yes, we will later use backwards guided sampling in order to have a decent + * chance of connecting to it. + * Todo: Maybe use less than 10 times the mean free path? */ + ray.t = (bounce == 0) ? max(t, 10.0f / (min3(sigma_t))) : t; + scene_intersect_local(kg, &ray, &ss_isect, object, NULL, 1); + hit = (ss_isect.num_hits > 0); + + if (hit) { +# ifdef __KERNEL_OPTIX__ + /* t is always in world space with OptiX. */ + ray.t = ss_isect.hits[0].t; +# else + /* Compute world space distance to surface hit. */ + float3 D = transform_direction(&ob_itfm, ray.D); + D = normalize(D) * ss_isect.hits[0].t; + ray.t = len(transform_direction(&ob_tfm, D)); +# endif + } + + if (bounce == 0) { + /* Check if we hit the opposite side. */ + if (hit) { + have_opposite_interface = true; + opposite_distance = dot(ray.P + ray.t * ray.D - P, -N); + } + /* Apart from the opposite side check, we were supposed to only trace up to distance t, + * so check if there would have been a hit in that case. */ + hit = ray.t < t; + } + + /* Use the distance to the exit point for the throughput update if we found one. */ + if (hit) { + t = ray.t; + } + else if (bounce == 0) { + /* Restore original position if nothing was hit after the first bounce, + * without the ray_offset() that was added to avoid self-intersection. + * Otherwise if that offset is relatively large compared to the scattering + * radius, we never go back up high enough to exit the surface. */ + ray.P = P; + } + + /* Advance to new scatter location. */ + ray.P += t * ray.D; + + float3 transmittance; + float3 pdf = subsurface_random_walk_pdf(sigma_t, t, hit, &transmittance); + if (bounce > 0) { + /* Compute PDF just like we do for classic sampling, but with the stretched sigma_t. */ + float3 guided_pdf = subsurface_random_walk_pdf(forward_stretching * sigma_t, t, hit, NULL); + + if (have_opposite_interface) { + /* First step of MIS: Depending on geometry we might have two methods for guided + * sampling, so perform MIS between them. */ + float3 back_pdf = subsurface_random_walk_pdf(backward_stretching * sigma_t, t, hit, NULL); + guided_pdf = mix( + guided_pdf * forward_pdf_factor, back_pdf * backward_pdf_factor, backward_fraction); + } + else { + /* Just include phase sampling factor otherwise. */ + guided_pdf *= forward_pdf_factor; + } + + /* Now we apply the MIS balance heuristic between the classic and guided sampling. */ + pdf = mix(pdf, guided_pdf, guided_fraction); + } + + /* Finally, we're applying MIS again to combine the three color channels. + * Altogether, the MIS computation combines up to nine different estimators: + * {classic, guided, backward_guided} x {r, g, b} */ + throughput *= (hit ? transmittance : sigma_s * transmittance) / dot(channel_pdf, pdf); + + if (hit) { + /* If we hit the surface, we are done. */ + break; + } + else if (throughput.x < VOLUME_THROUGHPUT_EPSILON && + throughput.y < VOLUME_THROUGHPUT_EPSILON && + throughput.z < VOLUME_THROUGHPUT_EPSILON) { + /* Avoid unnecessary work and precision issue when throughput gets really small. */ + break; + } + } + + if (hit) { + kernel_assert(isfinite3_safe(throughput)); + INTEGRATOR_STATE_WRITE(path, throughput) = throughput; + } + + return hit; +} + +ccl_device_inline bool subsurface_scatter(INTEGRATOR_STATE_ARGS) +{ + RNGState rng_state; + path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + + Ray ray ccl_optional_struct_init; + LocalIntersection ss_isect ccl_optional_struct_init; + + if (!subsurface_random_walk(INTEGRATOR_STATE_PASS, rng_state, ray, ss_isect)) { + return false; + } + +# ifdef __VOLUME__ + /* Update volume stack if needed. */ + if (kernel_data.integrator.use_volumes) { + const int object = intersection_get_object(kg, &ss_isect.hits[0]); + const int object_flag = kernel_tex_fetch(__object_flag, object); + + if (object_flag & SD_OBJECT_INTERSECTS_VOLUME) { + float3 P = INTEGRATOR_STATE(ray, P); + const float3 Ng = INTEGRATOR_STATE(isect, Ng); + const float3 offset_P = ray_offset(P, -Ng); + + integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_PASS, offset_P, ray.P); + } + } +# endif /* __VOLUME__ */ + + /* Pretend ray is coming from the outside towards the exit point. This ensures + * correct front/back facing normals. + * TODO: find a more elegant solution? */ + ray.P += ray.D * ray.t * 2.0f; + ray.D = -ray.D; + + integrator_state_write_isect(INTEGRATOR_STATE_PASS, &ss_isect.hits[0]); + integrator_state_write_ray(INTEGRATOR_STATE_PASS, &ray); + + /* Advanced random number offset for bounce. */ + INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; + + const int shader = intersection_get_shader(kg, &ss_isect.hits[0]); + const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; + if ((shader_flags & SD_HAS_RAYTRACE) || (kernel_data.film.pass_ao != PASS_UNUSED)) { + INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, + shader); + } + else { + INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, + shader); + } + + return true; +} + +#endif /* __SUBSURFACE__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_volume_stack.h b/intern/cycles/kernel/integrator/integrator_volume_stack.h new file mode 100644 index 00000000000..d53070095f0 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_volume_stack.h @@ -0,0 +1,223 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Volume Stack + * + * This is an array of object/shared ID's that the current segment of the path + * is inside of. */ + +template +ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, + const ShaderData *sd, + StackReadOp stack_read, + StackWriteOp stack_write) +{ + /* todo: we should have some way for objects to indicate if they want the + * world shader to work inside them. excluding it by default is problematic + * because non-volume objects can't be assumed to be closed manifolds */ + if (!(sd->flag & SD_HAS_VOLUME)) { + return; + } + + if (sd->flag & SD_BACKFACING) { + /* Exit volume object: remove from stack. */ + for (int i = 0;; i++) { + VolumeStack entry = stack_read(i); + if (entry.shader == SHADER_NONE) { + break; + } + + if (entry.object == sd->object) { + /* Shift back next stack entries. */ + do { + entry = stack_read(i + 1); + stack_write(i, entry); + i++; + } while (entry.shader != SHADER_NONE); + + return; + } + } + } + else { + /* Enter volume object: add to stack. */ + int i; + for (i = 0;; i++) { + VolumeStack entry = stack_read(i); + if (entry.shader == SHADER_NONE) { + break; + } + + /* Already in the stack? then we have nothing to do. */ + if (entry.object == sd->object) { + return; + } + } + + /* If we exceed the stack limit, ignore. */ + if (i >= VOLUME_STACK_SIZE - 1) { + return; + } + + /* Add to the end of the stack. */ + const VolumeStack new_entry = {sd->object, sd->shader}; + const VolumeStack empty_entry = {OBJECT_NONE, SHADER_NONE}; + stack_write(i, new_entry); + stack_write(i + 1, empty_entry); + } +} + +ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, const ShaderData *sd) +{ + volume_stack_enter_exit( + INTEGRATOR_STATE_PASS, + sd, + [=](const int i) { return integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); }, + [=](const int i, const VolumeStack entry) { + integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, i, entry); + }); +} + +ccl_device void shadow_volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, const ShaderData *sd) +{ + volume_stack_enter_exit( + INTEGRATOR_STATE_PASS, + sd, + [=](const int i) { + return integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_PASS, i); + }, + [=](const int i, const VolumeStack entry) { + integrator_state_write_shadow_volume_stack(INTEGRATOR_STATE_PASS, i, entry); + }); +} + +/* Clean stack after the last bounce. + * + * It is expected that all volumes are closed manifolds, so at the time when ray + * hits nothing (for example, it is a last bounce which goes to environment) the + * only expected volume in the stack is the world's one. All the rest volume + * entries should have been exited already. + * + * This isn't always true because of ray intersection precision issues, which + * could lead us to an infinite non-world volume in the stack, causing render + * artifacts. + * + * Use this function after the last bounce to get rid of all volumes apart from + * the world's one after the last bounce to avoid render artifacts. + */ +ccl_device_inline void volume_stack_clean(INTEGRATOR_STATE_ARGS) +{ + if (kernel_data.background.volume_shader != SHADER_NONE) { + /* Keep the world's volume in stack. */ + INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 1, shader) = SHADER_NONE; + } + else { + INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 0, shader) = SHADER_NONE; + } +} + +template +ccl_device float volume_stack_step_size(INTEGRATOR_STATE_ARGS, StackReadOp stack_read) +{ + float step_size = FLT_MAX; + + for (int i = 0;; i++) { + VolumeStack entry = stack_read(i); + if (entry.shader == SHADER_NONE) { + break; + } + + int shader_flag = kernel_tex_fetch(__shaders, (entry.shader & SHADER_MASK)).flags; + + bool heterogeneous = false; + + if (shader_flag & SD_HETEROGENEOUS_VOLUME) { + heterogeneous = true; + } + else if (shader_flag & SD_NEED_VOLUME_ATTRIBUTES) { + /* We want to render world or objects without any volume grids + * as homogeneous, but can only verify this at run-time since other + * heterogeneous volume objects may be using the same shader. */ + int object = entry.object; + if (object != OBJECT_NONE) { + int object_flag = kernel_tex_fetch(__object_flag, object); + if (object_flag & SD_OBJECT_HAS_VOLUME_ATTRIBUTES) { + heterogeneous = true; + } + } + } + + if (heterogeneous) { + float object_step_size = object_volume_step_size(kg, entry.object); + object_step_size *= kernel_data.integrator.volume_step_rate; + step_size = fminf(object_step_size, step_size); + } + } + + return step_size; +} + +typedef enum VolumeSampleMethod { + VOLUME_SAMPLE_NONE = 0, + VOLUME_SAMPLE_DISTANCE = (1 << 0), + VOLUME_SAMPLE_EQUIANGULAR = (1 << 1), + VOLUME_SAMPLE_MIS = (VOLUME_SAMPLE_DISTANCE | VOLUME_SAMPLE_EQUIANGULAR), +} VolumeSampleMethod; + +ccl_device VolumeSampleMethod volume_stack_sample_method(INTEGRATOR_STATE_ARGS) +{ + VolumeSampleMethod method = VOLUME_SAMPLE_NONE; + + for (int i = 0;; i++) { + VolumeStack entry = integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); + if (entry.shader == SHADER_NONE) { + break; + } + + int shader_flag = kernel_tex_fetch(__shaders, (entry.shader & SHADER_MASK)).flags; + + if (shader_flag & SD_VOLUME_MIS) { + /* Multiple importance sampling. */ + return VOLUME_SAMPLE_MIS; + } + else if (shader_flag & SD_VOLUME_EQUIANGULAR) { + /* Distance + equiangular sampling -> multiple importance sampling. */ + if (method == VOLUME_SAMPLE_DISTANCE) { + return VOLUME_SAMPLE_MIS; + } + + /* Only equiangular sampling. */ + method = VOLUME_SAMPLE_EQUIANGULAR; + } + else { + /* Distance + equiangular sampling -> multiple importance sampling. */ + if (method == VOLUME_SAMPLE_EQUIANGULAR) { + return VOLUME_SAMPLE_MIS; + } + + /* Distance sampling only. */ + method = VOLUME_SAMPLE_DISTANCE; + } + } + + return method; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index 61653d328f1..9e12d24dcf4 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -14,751 +14,501 @@ * limitations under the License. */ +#pragma once + +#include "kernel_adaptive_sampling.h" +#include "kernel_random.h" +#include "kernel_shadow_catcher.h" +#include "kernel_write_passes.h" + CCL_NAMESPACE_BEGIN -/* BSDF Eval +/* -------------------------------------------------------------------- + * BSDF Evaluation * - * BSDF evaluation result, split per BSDF type. This is used to accumulate - * render passes separately. */ + * BSDF evaluation result, split between diffuse and glossy. This is used to + * accumulate render passes separately. Note that reflection, transmission + * and volume scattering are written to different render passes, but we assume + * that only one of those can happen at a bounce, and so do not need to accumulate + * them separately. */ -ccl_device float3 shader_bsdf_transparency(KernelGlobals *kg, const ShaderData *sd); - -ccl_device_inline void bsdf_eval_init(BsdfEval *eval, - ClosureType type, - float3 value, - int use_light_pass) +ccl_device_inline void bsdf_eval_init(BsdfEval *eval, const bool is_diffuse, float3 value) { -#ifdef __PASSES__ - eval->use_light_pass = use_light_pass; + eval->diffuse = zero_float3(); + eval->glossy = zero_float3(); - if (eval->use_light_pass) { - eval->diffuse = zero_float3(); - eval->glossy = zero_float3(); - eval->transmission = zero_float3(); - eval->transparent = zero_float3(); - eval->volume = zero_float3(); - - if (type == CLOSURE_BSDF_TRANSPARENT_ID) - eval->transparent = value; - else if (CLOSURE_IS_BSDF_DIFFUSE(type) || CLOSURE_IS_BSDF_BSSRDF(type)) - eval->diffuse = value; - else if (CLOSURE_IS_BSDF_GLOSSY(type)) - eval->glossy = value; - else if (CLOSURE_IS_BSDF_TRANSMISSION(type)) - eval->transmission = value; - else if (CLOSURE_IS_PHASE(type)) - eval->volume = value; - } - else -#endif - { + if (is_diffuse) { eval->diffuse = value; } -#ifdef __SHADOW_TRICKS__ - eval->sum_no_mis = zero_float3(); -#endif + else { + eval->glossy = value; + } } ccl_device_inline void bsdf_eval_accum(BsdfEval *eval, - ClosureType type, + const bool is_diffuse, float3 value, float mis_weight) { -#ifdef __SHADOW_TRICKS__ - eval->sum_no_mis += value; -#endif value *= mis_weight; -#ifdef __PASSES__ - if (eval->use_light_pass) { - if (CLOSURE_IS_BSDF_DIFFUSE(type) || CLOSURE_IS_BSDF_BSSRDF(type)) - eval->diffuse += value; - else if (CLOSURE_IS_BSDF_GLOSSY(type)) - eval->glossy += value; - else if (CLOSURE_IS_BSDF_TRANSMISSION(type)) - eval->transmission += value; - else if (CLOSURE_IS_PHASE(type)) - eval->volume += value; - /* skipping transparent, this function is used by for eval(), will be zero then */ - } - else -#endif - { + if (is_diffuse) { eval->diffuse += value; } + else { + eval->glossy += value; + } } ccl_device_inline bool bsdf_eval_is_zero(BsdfEval *eval) { -#ifdef __PASSES__ - if (eval->use_light_pass) { - return is_zero(eval->diffuse) && is_zero(eval->glossy) && is_zero(eval->transmission) && - is_zero(eval->transparent) && is_zero(eval->volume); - } - else -#endif - { - return is_zero(eval->diffuse); - } -} - -ccl_device_inline void bsdf_eval_mis(BsdfEval *eval, float value) -{ -#ifdef __PASSES__ - if (eval->use_light_pass) { - eval->diffuse *= value; - eval->glossy *= value; - eval->transmission *= value; - eval->volume *= value; - - /* skipping transparent, this function is used by for eval(), will be zero then */ - } - else -#endif - { - eval->diffuse *= value; - } + return is_zero(eval->diffuse) && is_zero(eval->glossy); } ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float value) { -#ifdef __SHADOW_TRICKS__ - eval->sum_no_mis *= value; -#endif - bsdf_eval_mis(eval, value); + eval->diffuse *= value; + eval->glossy *= value; } ccl_device_inline void bsdf_eval_mul3(BsdfEval *eval, float3 value) { -#ifdef __SHADOW_TRICKS__ - eval->sum_no_mis *= value; -#endif -#ifdef __PASSES__ - if (eval->use_light_pass) { - eval->diffuse *= value; - eval->glossy *= value; - eval->transmission *= value; - eval->volume *= value; - - /* skipping transparent, this function is used by for eval(), will be zero then */ - } - else - eval->diffuse *= value; -#else eval->diffuse *= value; -#endif + eval->glossy *= value; } ccl_device_inline float3 bsdf_eval_sum(const BsdfEval *eval) { -#ifdef __PASSES__ - if (eval->use_light_pass) { - return eval->diffuse + eval->glossy + eval->transmission + eval->volume; - } - else -#endif - return eval->diffuse; + return eval->diffuse + eval->glossy; } -/* Path Radiance +ccl_device_inline float3 bsdf_eval_diffuse_glossy_ratio(const BsdfEval *eval) +{ + /* Ratio of diffuse and glossy to recover proportions for writing to render pass. + * We assume reflection, transmission and volume scatter to be exclusive. */ + return safe_divide_float3_float3(eval->diffuse, eval->diffuse + eval->glossy); +} + +/* -------------------------------------------------------------------- + * Clamping * - * We accumulate different render passes separately. After summing at the end - * to get the combined result, it should be identical. We definite directly - * visible as the first non-transparent hit, while indirectly visible are the - * bounces after that. */ + * Clamping is done on a per-contribution basis so that we can write directly + * to render buffers instead of using per-thread memory, and to avoid the + * impact of clamping on other contributions. */ -ccl_device_inline void path_radiance_init(KernelGlobals *kg, PathRadiance *L) +ccl_device_forceinline void kernel_accum_clamp(const KernelGlobals *kg, float3 *L, int bounce) { - /* clear all */ -#ifdef __PASSES__ - L->use_light_pass = kernel_data.film.use_light_pass; - - if (kernel_data.film.use_light_pass) { - L->indirect = zero_float3(); - L->direct_emission = zero_float3(); - - L->color_diffuse = zero_float3(); - L->color_glossy = zero_float3(); - L->color_transmission = zero_float3(); - - L->direct_diffuse = zero_float3(); - L->direct_glossy = zero_float3(); - L->direct_transmission = zero_float3(); - L->direct_volume = zero_float3(); - - L->indirect_diffuse = zero_float3(); - L->indirect_glossy = zero_float3(); - L->indirect_transmission = zero_float3(); - L->indirect_volume = zero_float3(); - - L->transparent = 0.0f; - L->emission = zero_float3(); - L->background = zero_float3(); - L->ao = zero_float3(); - L->shadow = zero_float3(); - L->mist = 0.0f; - - L->state.diffuse = zero_float3(); - L->state.glossy = zero_float3(); - L->state.transmission = zero_float3(); - L->state.volume = zero_float3(); - L->state.direct = zero_float3(); +#ifdef __KERNEL_DEBUG_NAN__ + if (!isfinite3_safe(*L)) { + kernel_assert(!"Cycles sample with non-finite value detected"); } - else #endif - { - L->transparent = 0.0f; - L->emission = zero_float3(); - } - -#ifdef __SHADOW_TRICKS__ - L->path_total = zero_float3(); - L->path_total_shaded = zero_float3(); - L->shadow_background_color = zero_float3(); - L->shadow_throughput = 0.0f; - L->shadow_transparency = 1.0f; - L->has_shadow_catcher = 0; -#endif - -#ifdef __DENOISING_FEATURES__ - L->denoising_normal = zero_float3(); - L->denoising_albedo = zero_float3(); - L->denoising_depth = 0.0f; -#endif -} - -ccl_device_inline void path_radiance_bsdf_bounce(KernelGlobals *kg, - PathRadianceState *L_state, - ccl_addr_space float3 *throughput, - BsdfEval *bsdf_eval, - float bsdf_pdf, - int bounce, - int bsdf_label) -{ - float inverse_pdf = 1.0f / bsdf_pdf; - -#ifdef __PASSES__ - if (kernel_data.film.use_light_pass) { - if (bounce == 0 && !(bsdf_label & LABEL_TRANSPARENT)) { - /* first on directly visible surface */ - float3 value = *throughput * inverse_pdf; - - L_state->diffuse = bsdf_eval->diffuse * value; - L_state->glossy = bsdf_eval->glossy * value; - L_state->transmission = bsdf_eval->transmission * value; - L_state->volume = bsdf_eval->volume * value; - - *throughput = L_state->diffuse + L_state->glossy + L_state->transmission + L_state->volume; - - L_state->direct = *throughput; - } - else { - /* transparent bounce before first hit, or indirectly visible through BSDF */ - float3 sum = (bsdf_eval_sum(bsdf_eval) + bsdf_eval->transparent) * inverse_pdf; - *throughput *= sum; - } - } - else -#endif - { - *throughput *= bsdf_eval->diffuse * inverse_pdf; - } -} + /* Make sure all components are finite, allowing the contribution to be usable by adaptive + * sampling convergence check, but also to make it so render result never causes issues with + * post-processing. */ + *L = ensure_finite3(*L); #ifdef __CLAMP_SAMPLE__ -ccl_device_forceinline void path_radiance_clamp(KernelGlobals *kg, float3 *L, int bounce) -{ float limit = (bounce > 0) ? kernel_data.integrator.sample_clamp_indirect : kernel_data.integrator.sample_clamp_direct; float sum = reduce_add(fabs(*L)); if (sum > limit) { *L *= limit / sum; } +#endif } -ccl_device_forceinline void path_radiance_clamp_throughput(KernelGlobals *kg, - float3 *L, - float3 *throughput, - int bounce) -{ - float limit = (bounce > 0) ? kernel_data.integrator.sample_clamp_indirect : - kernel_data.integrator.sample_clamp_direct; +/* -------------------------------------------------------------------- + * Pass accumulation utilities. + */ - float sum = reduce_add(fabs(*L)); - if (sum > limit) { - float clamp_factor = limit / sum; - *L *= clamp_factor; - *throughput *= clamp_factor; +/* Get pointer to pixel in render buffer. */ +ccl_device_forceinline ccl_global float *kernel_accum_pixel_render_buffer( + INTEGRATOR_STATE_CONST_ARGS, ccl_global float *ccl_restrict render_buffer) +{ + const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + return render_buffer + render_buffer_offset; +} + +/* -------------------------------------------------------------------- + * Adaptive sampling. + */ + +ccl_device_inline int kernel_accum_sample(INTEGRATOR_STATE_CONST_ARGS, + ccl_global float *ccl_restrict render_buffer, + int sample) +{ + if (kernel_data.film.pass_sample_count == PASS_UNUSED) { + return sample; + } + + ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, + render_buffer); + + return atomic_fetch_and_add_uint32((uint *)(buffer) + kernel_data.film.pass_sample_count, 1); +} + +ccl_device void kernel_accum_adaptive_buffer(INTEGRATOR_STATE_CONST_ARGS, + const float3 contribution, + ccl_global float *ccl_restrict buffer) +{ + /* Adaptive Sampling. Fill the additional buffer with the odd samples and calculate our stopping + * criteria. This is the heuristic from "A hierarchical automatic stopping condition for Monte + * Carlo global illumination" except that here it is applied per pixel and not in hierarchical + * tiles. */ + + if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) { + return; + } + + const int sample = INTEGRATOR_STATE(path, sample); + if (sample_is_even(kernel_data.integrator.sampling_pattern, sample)) { + kernel_write_pass_float4( + buffer + kernel_data.film.pass_adaptive_aux_buffer, + make_float4(contribution.x * 2.0f, contribution.y * 2.0f, contribution.z * 2.0f, 0.0f)); } } -#endif +/* -------------------------------------------------------------------- + * Shadow catcher. + */ -ccl_device_inline void path_radiance_accum_emission(KernelGlobals *kg, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - float3 value) +#ifdef __SHADOW_CATCHER__ + +/* Accumulate contribution to the Shadow Catcher pass. + * + * Returns truth if the contribution is fully handled here and is not to be added to the other + * passes (like combined, adaptive sampling). */ + +ccl_device bool kernel_accum_shadow_catcher(INTEGRATOR_STATE_CONST_ARGS, + const float3 contribution, + ccl_global float *ccl_restrict buffer) { -#ifdef __SHADOW_TRICKS__ - if (state->flag & PATH_RAY_SHADOW_CATCHER) { + if (!kernel_data.integrator.has_shadow_catcher) { + return false; + } + + kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED); + kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); + + /* Matte pass. */ + if (kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_PASS)) { + kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution); + /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive + * sampling is based on how noisy the combined pass is as if there were no catchers in the + * scene. */ + } + + /* Shadow catcher pass. */ + if (kernel_shadow_catcher_is_object_pass(INTEGRATOR_STATE_PASS)) { + kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); + return true; + } + + return false; +} + +ccl_device bool kernel_accum_shadow_catcher_transparent(INTEGRATOR_STATE_CONST_ARGS, + const float3 contribution, + const float transparent, + ccl_global float *ccl_restrict buffer) +{ + if (!kernel_data.integrator.has_shadow_catcher) { + return false; + } + + kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED); + kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); + + if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { + return true; + } + + /* Matte pass. */ + if (kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_PASS)) { + kernel_write_pass_float4( + buffer + kernel_data.film.pass_shadow_catcher_matte, + make_float4(contribution.x, contribution.y, contribution.z, transparent)); + /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive + * sampling is based on how noisy the combined pass is as if there were no catchers in the + * scene. */ + } + + /* Shadow catcher pass. */ + if (kernel_shadow_catcher_is_object_pass(INTEGRATOR_STATE_PASS)) { + /* NOTE: The transparency of the shadow catcher pass is ignored. It is not needed for the + * calculation and the alpha channel of the pass contains numbers of samples contributed to a + * pixel of the pass. */ + kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); + return true; + } + + return false; +} + +ccl_device void kernel_accum_shadow_catcher_transparent_only(INTEGRATOR_STATE_CONST_ARGS, + const float transparent, + ccl_global float *ccl_restrict buffer) +{ + if (!kernel_data.integrator.has_shadow_catcher) { + return; + } + + kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); + + /* Matte pass. */ + if (kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_PASS)) { + kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, transparent); + } +} + +#endif /* __SHADOW_CATCHER__ */ + +/* -------------------------------------------------------------------- + * Render passes. + */ + +/* Write combined pass. */ +ccl_device_inline void kernel_accum_combined_pass(INTEGRATOR_STATE_CONST_ARGS, + const float3 contribution, + ccl_global float *ccl_restrict buffer) +{ +#ifdef __SHADOW_CATCHER__ + if (kernel_accum_shadow_catcher(INTEGRATOR_STATE_PASS, contribution, buffer)) { return; } #endif - float3 contribution = throughput * value; -#ifdef __CLAMP_SAMPLE__ - path_radiance_clamp(kg, &contribution, state->bounce - 1); -#endif + if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { + kernel_write_pass_float3(buffer + kernel_data.film.pass_combined, contribution); + } -#ifdef __PASSES__ - if (L->use_light_pass) { - if (state->bounce == 0) - L->emission += contribution; - else if (state->bounce == 1) - L->direct_emission += contribution; - else - L->indirect += contribution; - } - else -#endif - { - L->emission += contribution; - } + kernel_accum_adaptive_buffer(INTEGRATOR_STATE_PASS, contribution, buffer); } -ccl_device_inline void path_radiance_accum_ao(KernelGlobals *kg, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - float3 alpha, - float3 bsdf, - float3 ao) +/* Write combined pass with transparency. */ +ccl_device_inline void kernel_accum_combined_transparent_pass(INTEGRATOR_STATE_CONST_ARGS, + const float3 contribution, + const float transparent, + ccl_global float *ccl_restrict + buffer) { -#ifdef __PASSES__ - /* Store AO pass. */ - if (L->use_light_pass && state->bounce == 0) { - L->ao += alpha * throughput * ao; +#ifdef __SHADOW_CATCHER__ + if (kernel_accum_shadow_catcher_transparent( + INTEGRATOR_STATE_PASS, contribution, transparent, buffer)) { + return; } #endif -#ifdef __SHADOW_TRICKS__ - /* For shadow catcher, accumulate ratio. */ - if (state->flag & PATH_RAY_STORE_SHADOW_INFO) { - float3 light = throughput * bsdf; - L->path_total += light; - L->path_total_shaded += ao * light; - - if (state->flag & PATH_RAY_SHADOW_CATCHER) { - return; - } + if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { + kernel_write_pass_float4( + buffer + kernel_data.film.pass_combined, + make_float4(contribution.x, contribution.y, contribution.z, transparent)); } -#endif - float3 contribution = throughput * bsdf * ao; - -#ifdef __PASSES__ - if (L->use_light_pass) { - if (state->bounce == 0) { - /* Directly visible lighting. */ - L->direct_diffuse += contribution; - } - else { - /* Indirectly visible lighting after BSDF bounce. */ - L->indirect += contribution; - } - } - else -#endif - { - L->emission += contribution; - } + kernel_accum_adaptive_buffer(INTEGRATOR_STATE_PASS, contribution, buffer); } -ccl_device_inline void path_radiance_accum_total_ao(PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - float3 bsdf) +/* Write background or emission to appropriate pass. */ +ccl_device_inline void kernel_accum_emission_or_background_pass(INTEGRATOR_STATE_CONST_ARGS, + float3 contribution, + ccl_global float *ccl_restrict + buffer, + const int pass) { -#ifdef __SHADOW_TRICKS__ - if (state->flag & PATH_RAY_STORE_SHADOW_INFO) { - L->path_total += throughput * bsdf; + if (!(kernel_data.film.light_pass_flag & PASS_ANY)) { + return; } -#else - (void)L; - (void)state; - (void)throughput; - (void)bsdf; -#endif -} - -ccl_device_inline void path_radiance_accum_light(KernelGlobals *kg, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - BsdfEval *bsdf_eval, - float3 shadow, - float shadow_fac, - bool is_lamp) -{ -#ifdef __SHADOW_TRICKS__ - if (state->flag & PATH_RAY_STORE_SHADOW_INFO) { - float3 light = throughput * bsdf_eval->sum_no_mis; - L->path_total += light; - L->path_total_shaded += shadow * light; - - if (state->flag & PATH_RAY_SHADOW_CATCHER) { - return; - } - } -#endif - - float3 shaded_throughput = throughput * shadow; #ifdef __PASSES__ - if (L->use_light_pass) { - /* Compute the clamping based on the total contribution. - * The resulting scale is then be applied to all individual components. */ - float3 full_contribution = shaded_throughput * bsdf_eval_sum(bsdf_eval); -# ifdef __CLAMP_SAMPLE__ - path_radiance_clamp_throughput(kg, &full_contribution, &shaded_throughput, state->bounce); -# endif + const int path_flag = INTEGRATOR_STATE(path, flag); + int pass_offset = PASS_UNUSED; - if (state->bounce == 0) { - /* directly visible lighting */ - L->direct_diffuse += shaded_throughput * bsdf_eval->diffuse; - L->direct_glossy += shaded_throughput * bsdf_eval->glossy; - L->direct_transmission += shaded_throughput * bsdf_eval->transmission; - L->direct_volume += shaded_throughput * bsdf_eval->volume; + /* Denoising albedo. */ +# ifdef __DENOISING_FEATURES__ + if (path_flag & PATH_RAY_DENOISING_FEATURES) { + if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { + const float3 denoising_feature_throughput = INTEGRATOR_STATE(path, + denoising_feature_throughput); + const float3 denoising_albedo = denoising_feature_throughput * contribution; + kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); + } + } +# endif /* __DENOISING_FEATURES__ */ - if (is_lamp) { - L->shadow += shadow * shadow_fac; + if (!(path_flag & PATH_RAY_ANY_PASS)) { + /* Directly visible, write to emission or background pass. */ + pass_offset = pass; + } + else if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { + /* Indirectly visible through reflection. */ + const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ? + ((INTEGRATOR_STATE(path, bounce) == 1) ? + kernel_data.film.pass_glossy_direct : + kernel_data.film.pass_glossy_indirect) : + ((INTEGRATOR_STATE(path, bounce) == 1) ? + kernel_data.film.pass_transmission_direct : + kernel_data.film.pass_transmission_indirect); + + if (glossy_pass_offset != PASS_UNUSED) { + /* Glossy is a subset of the throughput, reconstruct it here using the + * diffuse-glossy ratio. */ + const float3 ratio = INTEGRATOR_STATE(path, diffuse_glossy_ratio); + const float3 glossy_contribution = (one_float3() - ratio) * contribution; + kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution); + } + + /* Reconstruct diffuse subset of throughput. */ + pass_offset = (INTEGRATOR_STATE(path, bounce) == 1) ? kernel_data.film.pass_diffuse_direct : + kernel_data.film.pass_diffuse_indirect; + if (pass_offset != PASS_UNUSED) { + contribution *= INTEGRATOR_STATE(path, diffuse_glossy_ratio); + } + } + else if (path_flag & PATH_RAY_VOLUME_PASS) { + /* Indirectly visible through volume. */ + pass_offset = (INTEGRATOR_STATE(path, bounce) == 1) ? kernel_data.film.pass_volume_direct : + kernel_data.film.pass_volume_indirect; + } + + /* Single write call for GPU coherence. */ + if (pass_offset != PASS_UNUSED) { + kernel_write_pass_float3(buffer + pass_offset, contribution); + } +#endif /* __PASSES__ */ +} + +/* Write light contribution to render buffer. */ +ccl_device_inline void kernel_accum_light(INTEGRATOR_STATE_CONST_ARGS, + ccl_global float *ccl_restrict render_buffer) +{ + /* The throughput for shadow paths already contains the light shader evaluation. */ + float3 contribution = INTEGRATOR_STATE(shadow_path, throughput); + kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(shadow_path, bounce) - 1); + + ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, + render_buffer); + + kernel_accum_combined_pass(INTEGRATOR_STATE_PASS, contribution, buffer); + +#ifdef __PASSES__ + if (kernel_data.film.light_pass_flag & PASS_ANY) { + const int path_flag = INTEGRATOR_STATE(shadow_path, flag); + int pass_offset = PASS_UNUSED; + + if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { + /* Indirectly visible through reflection. */ + const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ? + ((INTEGRATOR_STATE(shadow_path, bounce) == 0) ? + kernel_data.film.pass_glossy_direct : + kernel_data.film.pass_glossy_indirect) : + ((INTEGRATOR_STATE(shadow_path, bounce) == 0) ? + kernel_data.film.pass_transmission_direct : + kernel_data.film.pass_transmission_indirect); + + if (glossy_pass_offset != PASS_UNUSED) { + /* Glossy is a subset of the throughput, reconstruct it here using the + * diffuse-glossy ratio. */ + const float3 ratio = INTEGRATOR_STATE(shadow_path, diffuse_glossy_ratio); + const float3 glossy_contribution = (one_float3() - ratio) * contribution; + kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution); + } + + /* Reconstruct diffuse subset of throughput. */ + pass_offset = (INTEGRATOR_STATE(shadow_path, bounce) == 0) ? + kernel_data.film.pass_diffuse_direct : + kernel_data.film.pass_diffuse_indirect; + if (pass_offset != PASS_UNUSED) { + contribution *= INTEGRATOR_STATE(shadow_path, diffuse_glossy_ratio); } } - else { - /* indirectly visible lighting after BSDF bounce */ - L->indirect += full_contribution; + else if (path_flag & PATH_RAY_VOLUME_PASS) { + /* Indirectly visible through volume. */ + pass_offset = (INTEGRATOR_STATE(shadow_path, bounce) == 0) ? + kernel_data.film.pass_volume_direct : + kernel_data.film.pass_volume_indirect; } - } - else -#endif - { - float3 contribution = shaded_throughput * bsdf_eval->diffuse; - path_radiance_clamp(kg, &contribution, state->bounce); - L->emission += contribution; - } -} -ccl_device_inline void path_radiance_accum_total_light(PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - const BsdfEval *bsdf_eval) -{ -#ifdef __SHADOW_TRICKS__ - if (state->flag & PATH_RAY_STORE_SHADOW_INFO) { - L->path_total += throughput * bsdf_eval->sum_no_mis; - } -#else - (void)L; - (void)state; - (void)throughput; - (void)bsdf_eval; -#endif -} + /* Single write call for GPU coherence. */ + if (pass_offset != PASS_UNUSED) { + kernel_write_pass_float3(buffer + pass_offset, contribution); + } -ccl_device_inline void path_radiance_accum_background(KernelGlobals *kg, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - float3 value) -{ - -#ifdef __SHADOW_TRICKS__ - if (state->flag & PATH_RAY_STORE_SHADOW_INFO) { - L->path_total += throughput * value; - L->path_total_shaded += throughput * value * L->shadow_transparency; - - if (state->flag & PATH_RAY_SHADOW_CATCHER) { - return; + /* Write shadow pass. */ + if (kernel_data.film.pass_shadow != PASS_UNUSED && (path_flag & PATH_RAY_SHADOW_FOR_LIGHT) && + (path_flag & PATH_RAY_CAMERA)) { + const float3 unshadowed_throughput = INTEGRATOR_STATE(shadow_path, unshadowed_throughput); + const float3 shadowed_throughput = INTEGRATOR_STATE(shadow_path, throughput); + const float3 shadow = safe_divide_float3_float3(shadowed_throughput, unshadowed_throughput) * + kernel_data.film.pass_shadow_scale; + kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow, shadow); } } #endif - - float3 contribution = throughput * value; -#ifdef __CLAMP_SAMPLE__ - path_radiance_clamp(kg, &contribution, state->bounce - 1); -#endif - -#ifdef __PASSES__ - if (L->use_light_pass) { - if (state->flag & PATH_RAY_TRANSPARENT_BACKGROUND) - L->background += contribution; - else if (state->bounce == 1) - L->direct_emission += contribution; - else - L->indirect += contribution; - } - else -#endif - { - L->emission += contribution; - } - -#ifdef __DENOISING_FEATURES__ - L->denoising_albedo += state->denoising_feature_weight * state->denoising_feature_throughput * - value; -#endif /* __DENOISING_FEATURES__ */ } -ccl_device_inline void path_radiance_accum_transparent(PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput) +/* Write transparency to render buffer. + * + * Note that we accumulate transparency = 1 - alpha in the render buffer. + * Otherwise we'd have to write alpha on path termination, which happens + * in many places. */ +ccl_device_inline void kernel_accum_transparent(INTEGRATOR_STATE_CONST_ARGS, + const float transparent, + ccl_global float *ccl_restrict render_buffer) { - L->transparent += average(throughput); -} + ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, + render_buffer); -#ifdef __SHADOW_TRICKS__ -ccl_device_inline void path_radiance_accum_shadowcatcher(PathRadiance *L, - float3 throughput, - float3 background) -{ - L->shadow_throughput += average(throughput); - L->shadow_background_color += throughput * background; - L->has_shadow_catcher = 1; -} -#endif - -ccl_device_inline void path_radiance_sum_indirect(PathRadiance *L) -{ -#ifdef __PASSES__ - /* this division is a bit ugly, but means we only have to keep track of - * only a single throughput further along the path, here we recover just - * the indirect path that is not influenced by any particular BSDF type */ - if (L->use_light_pass) { - L->direct_emission = safe_divide_color(L->direct_emission, L->state.direct); - L->direct_diffuse += L->state.diffuse * L->direct_emission; - L->direct_glossy += L->state.glossy * L->direct_emission; - L->direct_transmission += L->state.transmission * L->direct_emission; - L->direct_volume += L->state.volume * L->direct_emission; - - L->indirect = safe_divide_color(L->indirect, L->state.direct); - L->indirect_diffuse += L->state.diffuse * L->indirect; - L->indirect_glossy += L->state.glossy * L->indirect; - L->indirect_transmission += L->state.transmission * L->indirect; - L->indirect_volume += L->state.volume * L->indirect; + if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { + kernel_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent); } -#endif + + kernel_accum_shadow_catcher_transparent_only(INTEGRATOR_STATE_PASS, transparent, buffer); } -ccl_device_inline void path_radiance_reset_indirect(PathRadiance *L) +/* Write background contribution to render buffer. + * + * Includes transparency, matching kernel_accum_transparent. */ +ccl_device_inline void kernel_accum_background(INTEGRATOR_STATE_CONST_ARGS, + const float3 L, + const float transparent, + const bool is_transparent_background_ray, + ccl_global float *ccl_restrict render_buffer) { -#ifdef __PASSES__ - if (L->use_light_pass) { - L->state.diffuse = zero_float3(); - L->state.glossy = zero_float3(); - L->state.transmission = zero_float3(); - L->state.volume = zero_float3(); + float3 contribution = INTEGRATOR_STATE(path, throughput) * L; + kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(path, bounce) - 1); - L->direct_emission = zero_float3(); - L->indirect = zero_float3(); - } -#endif -} + ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, + render_buffer); -ccl_device_inline void path_radiance_copy_indirect(PathRadiance *L, const PathRadiance *L_src) -{ -#ifdef __PASSES__ - if (L->use_light_pass) { - L->state = L_src->state; - - L->direct_emission = L_src->direct_emission; - L->indirect = L_src->indirect; - } -#endif -} - -#ifdef __SHADOW_TRICKS__ -ccl_device_inline void path_radiance_sum_shadowcatcher(KernelGlobals *kg, - PathRadiance *L, - float3 *L_sum, - float *alpha) -{ - /* Calculate current shadow of the path. */ - float path_total = average(L->path_total); - float shadow; - - if (UNLIKELY(!isfinite_safe(path_total))) { -# ifdef __KERNEL_DEBUG_NAN__ - kernel_assert(!"Non-finite total radiance along the path"); -# endif - shadow = 0.0f; - } - else if (path_total == 0.0f) { - shadow = L->shadow_transparency; + if (is_transparent_background_ray) { + kernel_accum_transparent(INTEGRATOR_STATE_PASS, transparent, render_buffer); } else { - float path_total_shaded = average(L->path_total_shaded); - shadow = path_total_shaded / path_total; + kernel_accum_combined_transparent_pass( + INTEGRATOR_STATE_PASS, contribution, transparent, buffer); } - - /* Calculate final light sum and transparency for shadow catcher object. */ - if (kernel_data.background.transparent) { - *alpha -= L->shadow_throughput * shadow; - } - else { - L->shadow_background_color *= shadow; - *L_sum += L->shadow_background_color; - } -} -#endif - -ccl_device_inline float3 path_radiance_clamp_and_sum(KernelGlobals *kg, - PathRadiance *L, - float *alpha) -{ - float3 L_sum; - /* Light Passes are used */ -#ifdef __PASSES__ - float3 L_direct, L_indirect; - if (L->use_light_pass) { - path_radiance_sum_indirect(L); - - L_direct = L->direct_diffuse + L->direct_glossy + L->direct_transmission + L->direct_volume + - L->emission; - L_indirect = L->indirect_diffuse + L->indirect_glossy + L->indirect_transmission + - L->indirect_volume; - - if (!kernel_data.background.transparent) - L_direct += L->background; - - L_sum = L_direct + L_indirect; - float sum = fabsf((L_sum).x) + fabsf((L_sum).y) + fabsf((L_sum).z); - - /* Reject invalid value */ - if (!isfinite_safe(sum)) { -# ifdef __KERNEL_DEBUG_NAN__ - kernel_assert(!"Non-finite sum in path_radiance_clamp_and_sum!"); -# endif - L_sum = zero_float3(); - - L->direct_diffuse = zero_float3(); - L->direct_glossy = zero_float3(); - L->direct_transmission = zero_float3(); - L->direct_volume = zero_float3(); - - L->indirect_diffuse = zero_float3(); - L->indirect_glossy = zero_float3(); - L->indirect_transmission = zero_float3(); - L->indirect_volume = zero_float3(); - - L->emission = zero_float3(); - } - } - - /* No Light Passes */ - else -#endif - { - L_sum = L->emission; - - /* Reject invalid value */ - float sum = fabsf((L_sum).x) + fabsf((L_sum).y) + fabsf((L_sum).z); - if (!isfinite_safe(sum)) { -#ifdef __KERNEL_DEBUG_NAN__ - kernel_assert(!"Non-finite final sum in path_radiance_clamp_and_sum!"); -#endif - L_sum = zero_float3(); - } - } - - /* Compute alpha. */ - *alpha = 1.0f - L->transparent; - - /* Add shadow catcher contributions. */ -#ifdef __SHADOW_TRICKS__ - if (L->has_shadow_catcher) { - path_radiance_sum_shadowcatcher(kg, L, &L_sum, alpha); - } -#endif /* __SHADOW_TRICKS__ */ - - return L_sum; + kernel_accum_emission_or_background_pass( + INTEGRATOR_STATE_PASS, contribution, buffer, kernel_data.film.pass_background); } -ccl_device_inline void path_radiance_split_denoising(KernelGlobals *kg, - PathRadiance *L, - float3 *noisy, - float3 *clean) +/* Write emission to render buffer. */ +ccl_device_inline void kernel_accum_emission(INTEGRATOR_STATE_CONST_ARGS, + const float3 throughput, + const float3 L, + ccl_global float *ccl_restrict render_buffer) { -#ifdef __PASSES__ - kernel_assert(L->use_light_pass); + float3 contribution = throughput * L; + kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(path, bounce) - 1); - *clean = L->emission + L->background; - *noisy = L->direct_volume + L->indirect_volume; + ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, + render_buffer); -# define ADD_COMPONENT(flag, component) \ - if (kernel_data.film.denoising_flags & flag) \ - *clean += component; \ - else \ - *noisy += component; - - ADD_COMPONENT(DENOISING_CLEAN_DIFFUSE_DIR, L->direct_diffuse); - ADD_COMPONENT(DENOISING_CLEAN_DIFFUSE_IND, L->indirect_diffuse); - ADD_COMPONENT(DENOISING_CLEAN_GLOSSY_DIR, L->direct_glossy); - ADD_COMPONENT(DENOISING_CLEAN_GLOSSY_IND, L->indirect_glossy); - ADD_COMPONENT(DENOISING_CLEAN_TRANSMISSION_DIR, L->direct_transmission); - ADD_COMPONENT(DENOISING_CLEAN_TRANSMISSION_IND, L->indirect_transmission); -# undef ADD_COMPONENT -#else - *noisy = L->emission; - *clean = zero_float3(); -#endif - -#ifdef __SHADOW_TRICKS__ - if (L->has_shadow_catcher) { - *noisy += L->shadow_background_color; - } -#endif - - *noisy = ensure_finite3(*noisy); - *clean = ensure_finite3(*clean); -} - -ccl_device_inline void path_radiance_accum_sample(PathRadiance *L, PathRadiance *L_sample) -{ -#ifdef __SPLIT_KERNEL__ -# define safe_float3_add(f, v) \ - do { \ - ccl_global float *p = (ccl_global float *)(&(f)); \ - atomic_add_and_fetch_float(p + 0, (v).x); \ - atomic_add_and_fetch_float(p + 1, (v).y); \ - atomic_add_and_fetch_float(p + 2, (v).z); \ - } while (0) -# define safe_float_add(f, v) atomic_add_and_fetch_float(&(f), (v)) -#else -# define safe_float3_add(f, v) (f) += (v) -# define safe_float_add(f, v) (f) += (v) -#endif /* __SPLIT_KERNEL__ */ - -#ifdef __PASSES__ - safe_float3_add(L->direct_diffuse, L_sample->direct_diffuse); - safe_float3_add(L->direct_glossy, L_sample->direct_glossy); - safe_float3_add(L->direct_transmission, L_sample->direct_transmission); - safe_float3_add(L->direct_volume, L_sample->direct_volume); - - safe_float3_add(L->indirect_diffuse, L_sample->indirect_diffuse); - safe_float3_add(L->indirect_glossy, L_sample->indirect_glossy); - safe_float3_add(L->indirect_transmission, L_sample->indirect_transmission); - safe_float3_add(L->indirect_volume, L_sample->indirect_volume); - - safe_float3_add(L->background, L_sample->background); - safe_float3_add(L->ao, L_sample->ao); - safe_float3_add(L->shadow, L_sample->shadow); - safe_float_add(L->mist, L_sample->mist); -#endif /* __PASSES__ */ - safe_float3_add(L->emission, L_sample->emission); - -#undef safe_float_add -#undef safe_float3_add + kernel_accum_combined_pass(INTEGRATOR_STATE_PASS, contribution, buffer); + kernel_accum_emission_or_background_pass( + INTEGRATOR_STATE_PASS, contribution, buffer, kernel_data.film.pass_emission); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_adaptive_sampling.h b/intern/cycles/kernel/kernel_adaptive_sampling.h index 98b7bf7e7dc..2bee12f0473 100644 --- a/intern/cycles/kernel/kernel_adaptive_sampling.h +++ b/intern/cycles/kernel/kernel_adaptive_sampling.h @@ -14,226 +14,146 @@ * limitations under the License. */ -#ifndef __KERNEL_ADAPTIVE_SAMPLING_H__ -#define __KERNEL_ADAPTIVE_SAMPLING_H__ +#pragma once + +#include "kernel/kernel_write_passes.h" CCL_NAMESPACE_BEGIN -/* Determines whether to continue sampling a given pixel or if it has sufficiently converged. */ +/* Check whether the pixel has converged and should not be sampled anymore. */ -ccl_device void kernel_do_adaptive_stopping(KernelGlobals *kg, - ccl_global float *buffer, - int sample) +ccl_device_forceinline bool kernel_need_sample_pixel(INTEGRATOR_STATE_CONST_ARGS, + ccl_global float *render_buffer) { - /* TODO Stefan: Is this better in linear, sRGB or something else? */ - float4 I = *((ccl_global float4 *)buffer); - float4 A = *(ccl_global float4 *)(buffer + kernel_data.film.pass_adaptive_aux_buffer); - /* The per pixel error as seen in section 2.1 of - * "A hierarchical automatic stopping condition for Monte Carlo global illumination" - * A small epsilon is added to the divisor to prevent division by zero. */ - float error = (fabsf(I.x - A.x) + fabsf(I.y - A.y) + fabsf(I.z - A.z)) / - (sample * 0.0001f + sqrtf(I.x + I.y + I.z)); - if (error < kernel_data.integrator.adaptive_threshold * (float)sample) { - /* Set the fourth component to non-zero value to indicate that this pixel has converged. */ - buffer[kernel_data.film.pass_adaptive_aux_buffer + 3] += 1.0f; + if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) { + return true; } + + const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + ccl_global float *buffer = render_buffer + render_buffer_offset; + + const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3; + return buffer[aux_w_offset] == 0.0f; } -/* Adjust the values of an adaptively sampled pixel. */ +/* Determines whether to continue sampling a given pixel or if it has sufficiently converged. */ -ccl_device void kernel_adaptive_post_adjust(KernelGlobals *kg, - ccl_global float *buffer, - float sample_multiplier) +ccl_device bool kernel_adaptive_sampling_convergence_check(const KernelGlobals *kg, + ccl_global float *render_buffer, + int x, + int y, + float threshold, + bool reset, + int offset, + int stride) { - *(ccl_global float4 *)(buffer) *= sample_multiplier; + kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED); + kernel_assert(kernel_data.film.pass_sample_count != PASS_UNUSED); - /* Scale the aux pass too, this is necessary for progressive rendering to work properly. */ - kernel_assert(kernel_data.film.pass_adaptive_aux_buffer); - *(ccl_global float4 *)(buffer + kernel_data.film.pass_adaptive_aux_buffer) *= sample_multiplier; + const int render_pixel_index = offset + x + y * stride; + ccl_global float *buffer = render_buffer + + (uint64_t)render_pixel_index * kernel_data.film.pass_stride; -#ifdef __PASSES__ - int flag = kernel_data.film.pass_flag; + /* TODO(Stefan): Is this better in linear, sRGB or something else? */ - if (flag & PASSMASK(NORMAL)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_normal) *= sample_multiplier; - - if (flag & PASSMASK(UV)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_uv) *= sample_multiplier; - - if (flag & PASSMASK(MOTION)) { - *(ccl_global float4 *)(buffer + kernel_data.film.pass_motion) *= sample_multiplier; - *(ccl_global float *)(buffer + kernel_data.film.pass_motion_weight) *= sample_multiplier; + const float4 A = kernel_read_pass_float4(buffer + kernel_data.film.pass_adaptive_aux_buffer); + if (!reset && A.w != 0.0f) { + /* If the pixel was considered converged, its state will not change in this kernmel. Early + * output before doing any math. + * + * TODO(sergey): On a GPU it might be better to keep thread alive for better coherency? */ + return true; } - if (kernel_data.film.use_light_pass) { - int light_flag = kernel_data.film.light_pass_flag; + const float4 I = kernel_read_pass_float4(buffer + kernel_data.film.pass_combined); - if (light_flag & PASSMASK(MIST)) - *(ccl_global float *)(buffer + kernel_data.film.pass_mist) *= sample_multiplier; + const float sample = __float_as_uint(buffer[kernel_data.film.pass_sample_count]); + const float inv_sample = 1.0f / sample; - /* Shadow pass omitted on purpose. It has its own scale parameter. */ + /* The per pixel error as seen in section 2.1 of + * "A hierarchical automatic stopping condition for Monte Carlo global illumination" */ + const float error_difference = (fabsf(I.x - A.x) + fabsf(I.y - A.y) + fabsf(I.z - A.z)) * + inv_sample; + const float error_normalize = sqrtf((I.x + I.y + I.z) * inv_sample); + /* A small epsilon is added to the divisor to prevent division by zero. */ + const float error = error_difference / (0.0001f + error_normalize); + const bool did_converge = (error < threshold); - if (light_flag & PASSMASK(DIFFUSE_INDIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_diffuse_indirect) *= sample_multiplier; - if (light_flag & PASSMASK(GLOSSY_INDIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_glossy_indirect) *= sample_multiplier; - if (light_flag & PASSMASK(TRANSMISSION_INDIRECT)) - *(ccl_global float3 *)(buffer + - kernel_data.film.pass_transmission_indirect) *= sample_multiplier; - if (light_flag & PASSMASK(VOLUME_INDIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_volume_indirect) *= sample_multiplier; - if (light_flag & PASSMASK(DIFFUSE_DIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_diffuse_direct) *= sample_multiplier; - if (light_flag & PASSMASK(GLOSSY_DIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_glossy_direct) *= sample_multiplier; - if (light_flag & PASSMASK(TRANSMISSION_DIRECT)) - *(ccl_global float3 *)(buffer + - kernel_data.film.pass_transmission_direct) *= sample_multiplier; - if (light_flag & PASSMASK(VOLUME_DIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_volume_direct) *= sample_multiplier; + const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3; + buffer[aux_w_offset] = did_converge; - if (light_flag & PASSMASK(EMISSION)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_emission) *= sample_multiplier; - if (light_flag & PASSMASK(BACKGROUND)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_background) *= sample_multiplier; - if (light_flag & PASSMASK(AO)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_ao) *= sample_multiplier; - - if (light_flag & PASSMASK(DIFFUSE_COLOR)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_diffuse_color) *= sample_multiplier; - if (light_flag & PASSMASK(GLOSSY_COLOR)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_glossy_color) *= sample_multiplier; - if (light_flag & PASSMASK(TRANSMISSION_COLOR)) - *(ccl_global float3 *)(buffer + - kernel_data.film.pass_transmission_color) *= sample_multiplier; - } -#endif - -#ifdef __DENOISING_FEATURES__ - -# define scale_float3_variance(buffer, offset, scale) \ - *(buffer + offset) *= scale; \ - *(buffer + offset + 1) *= scale; \ - *(buffer + offset + 2) *= scale; \ - *(buffer + offset + 3) *= scale * scale; \ - *(buffer + offset + 4) *= scale * scale; \ - *(buffer + offset + 5) *= scale * scale; - -# define scale_shadow_variance(buffer, offset, scale) \ - *(buffer + offset) *= scale; \ - *(buffer + offset + 1) *= scale; \ - *(buffer + offset + 2) *= scale * scale; - - if (kernel_data.film.pass_denoising_data) { - scale_shadow_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_SHADOW_A, sample_multiplier); - scale_shadow_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_SHADOW_B, sample_multiplier); - if (kernel_data.film.pass_denoising_clean) { - scale_float3_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_COLOR, sample_multiplier); - *(buffer + kernel_data.film.pass_denoising_clean) *= sample_multiplier; - *(buffer + kernel_data.film.pass_denoising_clean + 1) *= sample_multiplier; - *(buffer + kernel_data.film.pass_denoising_clean + 2) *= sample_multiplier; - } - else { - scale_float3_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_COLOR, sample_multiplier); - } - scale_float3_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_NORMAL, sample_multiplier); - scale_float3_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_ALBEDO, sample_multiplier); - *(buffer + kernel_data.film.pass_denoising_data + DENOISING_PASS_DEPTH) *= sample_multiplier; - *(buffer + kernel_data.film.pass_denoising_data + DENOISING_PASS_DEPTH + - 1) *= sample_multiplier * sample_multiplier; - } -#endif /* __DENOISING_FEATURES__ */ - - /* Cryptomatte. */ - if (kernel_data.film.cryptomatte_passes) { - int num_slots = 0; - num_slots += (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) ? 1 : 0; - num_slots += (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) ? 1 : 0; - num_slots += (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) ? 1 : 0; - num_slots = num_slots * 2 * kernel_data.film.cryptomatte_depth; - ccl_global float2 *id_buffer = (ccl_global float2 *)(buffer + - kernel_data.film.pass_cryptomatte); - for (int slot = 0; slot < num_slots; slot++) { - id_buffer[slot].y *= sample_multiplier; - } - } - - /* AOVs. */ - for (int i = 0; i < kernel_data.film.pass_aov_value_num; i++) { - *(buffer + kernel_data.film.pass_aov_value + i) *= sample_multiplier; - } - for (int i = 0; i < kernel_data.film.pass_aov_color_num; i++) { - *((ccl_global float4 *)(buffer + kernel_data.film.pass_aov_color) + i) *= sample_multiplier; - } + return did_converge; } /* This is a simple box filter in two passes. * When a pixel demands more adaptive samples, let its neighboring pixels draw more samples too. */ -ccl_device bool kernel_do_adaptive_filter_x(KernelGlobals *kg, int y, ccl_global WorkTile *tile) +ccl_device void kernel_adaptive_sampling_filter_x(const KernelGlobals *kg, + ccl_global float *render_buffer, + int y, + int start_x, + int width, + int offset, + int stride) { - bool any = false; + kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED); + bool prev = false; - for (int x = tile->x; x < tile->x + tile->w; ++x) { - int index = tile->offset + x + y * tile->stride; - ccl_global float *buffer = tile->buffer + index * kernel_data.film.pass_stride; - ccl_global float4 *aux = (ccl_global float4 *)(buffer + - kernel_data.film.pass_adaptive_aux_buffer); - if ((*aux).w == 0.0f) { - any = true; - if (x > tile->x && !prev) { + for (int x = start_x; x < start_x + width; ++x) { + int index = offset + x + y * stride; + ccl_global float *buffer = render_buffer + index * kernel_data.film.pass_stride; + const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3; + + if (buffer[aux_w_offset] == 0.0f) { + if (x > start_x && !prev) { index = index - 1; - buffer = tile->buffer + index * kernel_data.film.pass_stride; - aux = (ccl_global float4 *)(buffer + kernel_data.film.pass_adaptive_aux_buffer); - (*aux).w = 0.0f; + buffer = render_buffer + index * kernel_data.film.pass_stride; + buffer[aux_w_offset] = 0.0f; } prev = true; } else { if (prev) { - (*aux).w = 0.0f; + buffer[aux_w_offset] = 0.0f; } prev = false; } } - return any; } -ccl_device bool kernel_do_adaptive_filter_y(KernelGlobals *kg, int x, ccl_global WorkTile *tile) +ccl_device void kernel_adaptive_sampling_filter_y(const KernelGlobals *kg, + ccl_global float *render_buffer, + int x, + int start_y, + int height, + int offset, + int stride) { + kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED); + bool prev = false; - bool any = false; - for (int y = tile->y; y < tile->y + tile->h; ++y) { - int index = tile->offset + x + y * tile->stride; - ccl_global float *buffer = tile->buffer + index * kernel_data.film.pass_stride; - ccl_global float4 *aux = (ccl_global float4 *)(buffer + - kernel_data.film.pass_adaptive_aux_buffer); - if ((*aux).w == 0.0f) { - any = true; - if (y > tile->y && !prev) { - index = index - tile->stride; - buffer = tile->buffer + index * kernel_data.film.pass_stride; - aux = (ccl_global float4 *)(buffer + kernel_data.film.pass_adaptive_aux_buffer); - (*aux).w = 0.0f; + for (int y = start_y; y < start_y + height; ++y) { + int index = offset + x + y * stride; + ccl_global float *buffer = render_buffer + index * kernel_data.film.pass_stride; + const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3; + + if (buffer[aux_w_offset] == 0.0f) { + if (y > start_y && !prev) { + index = index - stride; + buffer = render_buffer + index * kernel_data.film.pass_stride; + buffer[aux_w_offset] = 0.0f; } prev = true; } else { if (prev) { - (*aux).w = 0.0f; + buffer[aux_w_offset] = 0.0f; } prev = false; } } - return any; } CCL_NAMESPACE_END - -#endif /* __KERNEL_ADAPTIVE_SAMPLING_H__ */ diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index 7da890b908d..e025bcd6674 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -14,502 +14,62 @@ * limitations under the License. */ +#pragma once + +#include "kernel/kernel_differential.h" +#include "kernel/kernel_projection.h" +#include "kernel/kernel_shader.h" + +#include "kernel/geom/geom.h" + CCL_NAMESPACE_BEGIN -#ifdef __BAKING__ - -ccl_device_noinline void compute_light_pass( - KernelGlobals *kg, ShaderData *sd, PathRadiance *L, uint rng_hash, int pass_filter, int sample) -{ - kernel_assert(kernel_data.film.use_light_pass); - - float3 throughput = one_float3(); - - /* Emission and indirect shader data memory used by various functions. */ - ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - ShaderData indirect_sd; - - /* Init radiance. */ - path_radiance_init(kg, L); - - /* Init path state. */ - PathState state; - path_state_init(kg, emission_sd, &state, rng_hash, sample, NULL); - - /* Evaluate surface shader. */ - shader_eval_surface(kg, sd, &state, NULL, state.flag); - - /* TODO: disable more closures we don't need besides transparent. */ - shader_bsdf_disable_transparency(kg, sd); - - /* Init ray. */ - Ray ray; - ray.P = sd->P + sd->Ng; - ray.D = -sd->Ng; - ray.t = FLT_MAX; -# ifdef __CAMERA_MOTION__ - ray.time = 0.5f; -# endif - -# ifdef __BRANCHED_PATH__ - if (!kernel_data.integrator.branched) { - /* regular path tracer */ -# endif - - /* sample ambient occlusion */ - if (pass_filter & BAKE_FILTER_AO) { - kernel_path_ao(kg, sd, emission_sd, L, &state, throughput, shader_bsdf_alpha(kg, sd)); - } - - /* sample emission */ - if ((pass_filter & BAKE_FILTER_EMISSION) && (sd->flag & SD_EMISSION)) { - float3 emission = indirect_primitive_emission(kg, sd, 0.0f, state.flag, state.ray_pdf); - path_radiance_accum_emission(kg, L, &state, throughput, emission); - } - - bool is_sss_sample = false; - -# ifdef __SUBSURFACE__ - /* sample subsurface scattering */ - if ((pass_filter & BAKE_FILTER_DIFFUSE) && (sd->flag & SD_BSSRDF)) { - /* When mixing BSSRDF and BSDF closures we should skip BSDF lighting - * if scattering was successful. */ - SubsurfaceIndirectRays ss_indirect; - kernel_path_subsurface_init_indirect(&ss_indirect); - if (kernel_path_subsurface_scatter( - kg, sd, emission_sd, L, &state, &ray, &throughput, &ss_indirect)) { - while (ss_indirect.num_rays) { - kernel_path_subsurface_setup_indirect(kg, &ss_indirect, &state, &ray, L, &throughput); - kernel_path_indirect( - kg, &indirect_sd, emission_sd, &ray, throughput, &state, L, sd->object); - } - is_sss_sample = true; - } - } -# endif - - /* sample light and BSDF */ - if (!is_sss_sample && (pass_filter & (BAKE_FILTER_DIRECT | BAKE_FILTER_INDIRECT))) { - kernel_path_surface_connect_light(kg, sd, emission_sd, throughput, &state, L); - - if (kernel_path_surface_bounce(kg, sd, &throughput, &state, &L->state, &ray)) { -# ifdef __LAMP_MIS__ - state.ray_t = 0.0f; -# endif - /* compute indirect light */ - kernel_path_indirect( - kg, &indirect_sd, emission_sd, &ray, throughput, &state, L, sd->object); - - /* sum and reset indirect light pass variables for the next samples */ - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - } - } -# ifdef __BRANCHED_PATH__ - } - else { - /* branched path tracer */ - - /* sample ambient occlusion */ - if (pass_filter & BAKE_FILTER_AO) { - kernel_branched_path_ao(kg, sd, emission_sd, L, &state, throughput); - } - - /* sample emission */ - if ((pass_filter & BAKE_FILTER_EMISSION) && (sd->flag & SD_EMISSION)) { - float3 emission = indirect_primitive_emission(kg, sd, 0.0f, state.flag, state.ray_pdf); - path_radiance_accum_emission(kg, L, &state, throughput, emission); - } - -# ifdef __SUBSURFACE__ - /* sample subsurface scattering */ - if ((pass_filter & BAKE_FILTER_DIFFUSE) && (sd->flag & SD_BSSRDF)) { - /* When mixing BSSRDF and BSDF closures we should skip BSDF lighting - * if scattering was successful. */ - kernel_branched_path_subsurface_scatter( - kg, sd, &indirect_sd, emission_sd, L, &state, &ray, throughput); - } -# endif - - /* sample light and BSDF */ - if (pass_filter & (BAKE_FILTER_DIRECT | BAKE_FILTER_INDIRECT)) { -# if defined(__EMISSION__) - /* direct light */ - if (kernel_data.integrator.use_direct_light) { - int all = kernel_data.integrator.sample_all_lights_direct; - kernel_branched_path_surface_connect_light( - kg, sd, emission_sd, &state, throughput, 1.0f, L, all); - } -# endif - - /* indirect light */ - kernel_branched_path_surface_indirect_light( - kg, sd, &indirect_sd, emission_sd, throughput, 1.0f, &state, L); - } - } -# endif -} - -/* this helps with AA but it's not the real solution as it does not AA the geometry - * but it's better than nothing, thus committed */ -ccl_device_inline float bake_clamp_mirror_repeat(float u, float max) -{ - /* use mirror repeat (like opengl texture) so that if the barycentric - * coordinate goes past the end of the triangle it is not always clamped - * to the same value, gives ugly patterns */ - u /= max; - float fu = floorf(u); - u = u - fu; - - return ((((int)fu) & 1) ? 1.0f - u : u) * max; -} - -ccl_device_inline float3 kernel_bake_shader_bsdf(KernelGlobals *kg, - ShaderData *sd, - const ShaderEvalType type) -{ - switch (type) { - case SHADER_EVAL_DIFFUSE: - return shader_bsdf_diffuse(kg, sd); - case SHADER_EVAL_GLOSSY: - return shader_bsdf_glossy(kg, sd); - case SHADER_EVAL_TRANSMISSION: - return shader_bsdf_transmission(kg, sd); - default: - kernel_assert(!"Unknown bake type passed to BSDF evaluate"); - return zero_float3(); - } -} - -ccl_device float3 kernel_bake_evaluate_direct_indirect(KernelGlobals *kg, - ShaderData *sd, - PathState *state, - float3 direct, - float3 indirect, - const ShaderEvalType type, - const int pass_filter) -{ - float3 color; - const bool is_color = (pass_filter & BAKE_FILTER_COLOR) != 0; - const bool is_direct = (pass_filter & BAKE_FILTER_DIRECT) != 0; - const bool is_indirect = (pass_filter & BAKE_FILTER_INDIRECT) != 0; - float3 out = zero_float3(); - - if (is_color) { - if (is_direct || is_indirect) { - /* Leave direct and diffuse channel colored. */ - color = one_float3(); - } - else { - /* surface color of the pass only */ - shader_eval_surface(kg, sd, state, NULL, 0); - return kernel_bake_shader_bsdf(kg, sd, type); - } - } - else { - shader_eval_surface(kg, sd, state, NULL, 0); - color = kernel_bake_shader_bsdf(kg, sd, type); - } - - if (is_direct) { - out += safe_divide_even_color(direct, color); - } - - if (is_indirect) { - out += safe_divide_even_color(indirect, color); - } - - return out; -} - -ccl_device void kernel_bake_evaluate( - KernelGlobals *kg, ccl_global float *buffer, int sample, int x, int y, int offset, int stride) -{ - /* Setup render buffers. */ - const int index = offset + x + y * stride; - const int pass_stride = kernel_data.film.pass_stride; - buffer += index * pass_stride; - - ccl_global float *primitive = buffer + kernel_data.film.pass_bake_primitive; - ccl_global float *differential = buffer + kernel_data.film.pass_bake_differential; - ccl_global float *output = buffer + kernel_data.film.pass_combined; - - int seed = __float_as_uint(primitive[0]); - int prim = __float_as_uint(primitive[1]); - if (prim == -1) - return; - - prim += kernel_data.bake.tri_offset; - - /* Random number generator. */ - uint rng_hash = hash_uint(seed) ^ kernel_data.integrator.seed; - int num_samples = kernel_data.integrator.aa_samples; - - float filter_x, filter_y; - if (sample == 0) { - filter_x = filter_y = 0.5f; - } - else { - path_rng_2D(kg, rng_hash, sample, num_samples, PRNG_FILTER_U, &filter_x, &filter_y); - } - - /* Barycentric UV with sub-pixel offset. */ - float u = primitive[2]; - float v = primitive[3]; - - float dudx = differential[0]; - float dudy = differential[1]; - float dvdx = differential[2]; - float dvdy = differential[3]; - - if (sample > 0) { - u = bake_clamp_mirror_repeat(u + dudx * (filter_x - 0.5f) + dudy * (filter_y - 0.5f), 1.0f); - v = bake_clamp_mirror_repeat(v + dvdx * (filter_x - 0.5f) + dvdy * (filter_y - 0.5f), - 1.0f - u); - } - - /* Shader data setup. */ - int object = kernel_data.bake.object_index; - int shader; - float3 P, Ng; - - triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader); - - ShaderData sd; - shader_setup_from_sample( - kg, - &sd, - P, - Ng, - Ng, - shader, - object, - prim, - u, - v, - 1.0f, - 0.5f, - !(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED), - LAMP_NONE); - sd.I = sd.N; - - /* Setup differentials. */ - sd.dP.dx = sd.dPdu * dudx + sd.dPdv * dvdx; - sd.dP.dy = sd.dPdu * dudy + sd.dPdv * dvdy; - sd.du.dx = dudx; - sd.du.dy = dudy; - sd.dv.dx = dvdx; - sd.dv.dy = dvdy; - - /* Set RNG state for shaders that use sampling. */ - PathState state = {0}; - state.rng_hash = rng_hash; - state.rng_offset = 0; - state.sample = sample; - state.num_samples = num_samples; - state.min_ray_pdf = FLT_MAX; - - /* Light passes if we need more than color. */ - PathRadiance L; - int pass_filter = kernel_data.bake.pass_filter; - - if (kernel_data.bake.pass_filter & ~BAKE_FILTER_COLOR) - compute_light_pass(kg, &sd, &L, rng_hash, pass_filter, sample); - - float3 out = zero_float3(); - - ShaderEvalType type = (ShaderEvalType)kernel_data.bake.type; - switch (type) { - /* data passes */ - case SHADER_EVAL_NORMAL: - case SHADER_EVAL_ROUGHNESS: - case SHADER_EVAL_EMISSION: { - if (type != SHADER_EVAL_NORMAL || (sd.flag & SD_HAS_BUMP)) { - int path_flag = (type == SHADER_EVAL_EMISSION) ? PATH_RAY_EMISSION : 0; - shader_eval_surface(kg, &sd, &state, NULL, path_flag); - } - - if (type == SHADER_EVAL_NORMAL) { - float3 N = sd.N; - if (sd.flag & SD_HAS_BUMP) { - N = shader_bsdf_average_normal(kg, &sd); - } - - /* encoding: normal = (2 * color) - 1 */ - out = N * 0.5f + make_float3(0.5f, 0.5f, 0.5f); - } - else if (type == SHADER_EVAL_ROUGHNESS) { - float roughness = shader_bsdf_average_roughness(&sd); - out = make_float3(roughness, roughness, roughness); - } - else { - out = shader_emissive_eval(&sd); - } - break; - } - case SHADER_EVAL_UV: { - out = primitive_uv(kg, &sd); - break; - } -# ifdef __PASSES__ - /* light passes */ - case SHADER_EVAL_AO: { - out = L.ao; - break; - } - case SHADER_EVAL_COMBINED: { - if ((pass_filter & BAKE_FILTER_COMBINED) == BAKE_FILTER_COMBINED) { - float alpha; - out = path_radiance_clamp_and_sum(kg, &L, &alpha); - break; - } - - if ((pass_filter & BAKE_FILTER_DIFFUSE_DIRECT) == BAKE_FILTER_DIFFUSE_DIRECT) - out += L.direct_diffuse; - if ((pass_filter & BAKE_FILTER_DIFFUSE_INDIRECT) == BAKE_FILTER_DIFFUSE_INDIRECT) - out += L.indirect_diffuse; - - if ((pass_filter & BAKE_FILTER_GLOSSY_DIRECT) == BAKE_FILTER_GLOSSY_DIRECT) - out += L.direct_glossy; - if ((pass_filter & BAKE_FILTER_GLOSSY_INDIRECT) == BAKE_FILTER_GLOSSY_INDIRECT) - out += L.indirect_glossy; - - if ((pass_filter & BAKE_FILTER_TRANSMISSION_DIRECT) == BAKE_FILTER_TRANSMISSION_DIRECT) - out += L.direct_transmission; - if ((pass_filter & BAKE_FILTER_TRANSMISSION_INDIRECT) == BAKE_FILTER_TRANSMISSION_INDIRECT) - out += L.indirect_transmission; - - if ((pass_filter & BAKE_FILTER_EMISSION) != 0) - out += L.emission; - - break; - } - case SHADER_EVAL_SHADOW: { - out = L.shadow; - break; - } - case SHADER_EVAL_DIFFUSE: { - out = kernel_bake_evaluate_direct_indirect( - kg, &sd, &state, L.direct_diffuse, L.indirect_diffuse, type, pass_filter); - break; - } - case SHADER_EVAL_GLOSSY: { - out = kernel_bake_evaluate_direct_indirect( - kg, &sd, &state, L.direct_glossy, L.indirect_glossy, type, pass_filter); - break; - } - case SHADER_EVAL_TRANSMISSION: { - out = kernel_bake_evaluate_direct_indirect( - kg, &sd, &state, L.direct_transmission, L.indirect_transmission, type, pass_filter); - break; - } -# endif - - /* extra */ - case SHADER_EVAL_ENVIRONMENT: { - /* setup ray */ - Ray ray; - - ray.P = zero_float3(); - ray.D = normalize(P); - ray.t = 0.0f; -# ifdef __CAMERA_MOTION__ - ray.time = 0.5f; -# endif - -# ifdef __RAY_DIFFERENTIALS__ - ray.dD = differential3_zero(); - ray.dP = differential3_zero(); -# endif - - /* setup shader data */ - shader_setup_from_background(kg, &sd, &ray); - - /* evaluate */ - int path_flag = 0; /* we can't know which type of BSDF this is for */ - shader_eval_surface(kg, &sd, &state, NULL, path_flag | PATH_RAY_EMISSION); - out = shader_background_eval(&sd); - break; - } - default: { - /* no real shader, returning the position of the verts for debugging */ - out = normalize(P); - break; - } - } - - /* write output */ - const float4 result = make_float4(out.x, out.y, out.z, 1.0f); - kernel_write_pass_float4(output, result); -} - -#endif /* __BAKING__ */ - -ccl_device void kernel_displace_evaluate(KernelGlobals *kg, - ccl_global uint4 *input, +ccl_device void kernel_displace_evaluate(const KernelGlobals *kg, + ccl_global const KernelShaderEvalInput *input, ccl_global float4 *output, - int i) + const int offset) { + /* Setup shader data. */ + const KernelShaderEvalInput in = input[offset]; + ShaderData sd; - PathState state = {0}; - uint4 in = input[i]; + shader_setup_from_displace(kg, &sd, in.object, in.prim, in.u, in.v); - /* setup shader data */ - int object = in.x; - int prim = in.y; - float u = __uint_as_float(in.z); - float v = __uint_as_float(in.w); - - shader_setup_from_displace(kg, &sd, object, prim, u, v); - - /* evaluate */ - float3 P = sd.P; - shader_eval_displacement(kg, &sd, &state); + /* Evaluate displacement shader. */ + const float3 P = sd.P; + shader_eval_displacement(INTEGRATOR_STATE_PASS_NULL, &sd); float3 D = sd.P - P; object_inverse_dir_transform(kg, &sd, &D); - /* write output */ - output[i] += make_float4(D.x, D.y, D.z, 0.0f); + /* Write output. */ + output[offset] += make_float4(D.x, D.y, D.z, 0.0f); } -ccl_device void kernel_background_evaluate(KernelGlobals *kg, - ccl_global uint4 *input, +ccl_device void kernel_background_evaluate(const KernelGlobals *kg, + ccl_global const KernelShaderEvalInput *input, ccl_global float4 *output, - int i) + const int offset) { + /* Setup ray */ + const KernelShaderEvalInput in = input[offset]; + const float3 ray_P = zero_float3(); + const float3 ray_D = equirectangular_to_direction(in.u, in.v); + const float ray_time = 0.5f; + + /* Setup shader data. */ ShaderData sd; - PathState state = {0}; - uint4 in = input[i]; + shader_setup_from_background(kg, &sd, ray_P, ray_D, ray_time); - /* setup ray */ - Ray ray; - float u = __uint_as_float(in.x); - float v = __uint_as_float(in.y); + /* Evaluate shader. + * This is being evaluated for all BSDFs, so path flag does not contain a specific type. */ + const int path_flag = PATH_RAY_EMISSION; + shader_eval_surface( + INTEGRATOR_STATE_PASS_NULL, &sd, NULL, path_flag); + const float3 color = shader_background_eval(&sd); - ray.P = zero_float3(); - ray.D = equirectangular_to_direction(u, v); - ray.t = 0.0f; -#ifdef __CAMERA_MOTION__ - ray.time = 0.5f; -#endif - -#ifdef __RAY_DIFFERENTIALS__ - ray.dD = differential3_zero(); - ray.dP = differential3_zero(); -#endif - - /* setup shader data */ - shader_setup_from_background(kg, &sd, &ray); - - /* evaluate */ - int path_flag = 0; /* we can't know which type of BSDF this is for */ - shader_eval_surface(kg, &sd, &state, NULL, path_flag | PATH_RAY_EMISSION); - float3 color = shader_background_eval(&sd); - - /* write output */ - output[i] += make_float4(color.x, color.y, color.z, 0.0f); + /* Write output. */ + output[offset] += make_float4(color.x, color.y, color.z, 0.0f); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index 1bfac37158d..7be5da8fe6d 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -14,6 +14,13 @@ * limitations under the License. */ +#pragma once + +#include "kernel_differential.h" +#include "kernel_lookup_table.h" +#include "kernel_montecarlo.h" +#include "kernel_projection.h" + CCL_NAMESPACE_BEGIN /* Perspective Camera */ @@ -39,7 +46,7 @@ ccl_device float2 camera_sample_aperture(ccl_constant KernelCamera *cam, float u return bokeh; } -ccl_device void camera_sample_perspective(KernelGlobals *kg, +ccl_device void camera_sample_perspective(const KernelGlobals *ccl_restrict kg, float raster_x, float raster_y, float lens_u, @@ -113,10 +120,14 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, #ifdef __RAY_DIFFERENTIALS__ float3 Dcenter = transform_direction(&cameratoworld, Pcamera); + float3 Dcenter_normalized = normalize(Dcenter); - ray->dP = differential3_zero(); - ray->dD.dx = normalize(Dcenter + float4_to_float3(kernel_data.cam.dx)) - normalize(Dcenter); - ray->dD.dy = normalize(Dcenter + float4_to_float3(kernel_data.cam.dy)) - normalize(Dcenter); + /* TODO: can this be optimized to give compact differentials directly? */ + ray->dP = differential_zero_compact(); + differential3 dD; + dD.dx = normalize(Dcenter + float4_to_float3(kernel_data.cam.dx)) - Dcenter_normalized; + dD.dy = normalize(Dcenter + float4_to_float3(kernel_data.cam.dy)) - Dcenter_normalized; + ray->dD = differential_make_compact(dD); #endif } else { @@ -143,8 +154,10 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, Dx = normalize(transform_direction(&cameratoworld, Dx)); spherical_stereo_transform(&kernel_data.cam, &Px, &Dx); - ray->dP.dx = Px - Pcenter; - ray->dD.dx = Dx - Dcenter; + differential3 dP, dD; + + dP.dx = Px - Pcenter; + dD.dx = Dx - Dcenter; float3 Py = Pnostereo; float3 Dy = transform_perspective(&rastertocamera, @@ -152,8 +165,10 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, Dy = normalize(transform_direction(&cameratoworld, Dy)); spherical_stereo_transform(&kernel_data.cam, &Py, &Dy); - ray->dP.dy = Py - Pcenter; - ray->dD.dy = Dy - Dcenter; + dP.dy = Py - Pcenter; + dD.dy = Dy - Dcenter; + ray->dD = differential_make_compact(dD); + ray->dP = differential_make_compact(dP); #endif } @@ -162,8 +177,7 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float z_inv = 1.0f / normalize(Pcamera).z; float nearclip = kernel_data.cam.nearclip * z_inv; ray->P += nearclip * ray->D; - ray->dP.dx += nearclip * ray->dD.dx; - ray->dP.dy += nearclip * ray->dD.dy; + ray->dP += nearclip * ray->dD; ray->t = kernel_data.cam.cliplength * z_inv; #else ray->t = FLT_MAX; @@ -171,7 +185,7 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, } /* Orthographic Camera */ -ccl_device void camera_sample_orthographic(KernelGlobals *kg, +ccl_device void camera_sample_orthographic(const KernelGlobals *ccl_restrict kg, float raster_x, float raster_y, float lens_u, @@ -220,10 +234,12 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, #ifdef __RAY_DIFFERENTIALS__ /* ray differential */ - ray->dP.dx = float4_to_float3(kernel_data.cam.dx); - ray->dP.dy = float4_to_float3(kernel_data.cam.dy); + differential3 dP; + dP.dx = float4_to_float3(kernel_data.cam.dx); + dP.dy = float4_to_float3(kernel_data.cam.dx); - ray->dD = differential3_zero(); + ray->dP = differential_make_compact(dP); + ray->dD = differential_zero_compact(); #endif #ifdef __CAMERA_CLIPPING__ @@ -323,8 +339,9 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, spherical_stereo_transform(cam, &Px, &Dx); } - ray->dP.dx = Px - Pcenter; - ray->dD.dx = Dx - Dcenter; + differential3 dP, dD; + dP.dx = Px - Pcenter; + dD.dx = Dx - Dcenter; float3 Py = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y + 1.0f, 0.0f)); float3 Dy = panorama_to_direction(cam, Py.x, Py.y); @@ -334,16 +351,17 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, spherical_stereo_transform(cam, &Py, &Dy); } - ray->dP.dy = Py - Pcenter; - ray->dD.dy = Dy - Dcenter; + dP.dy = Py - Pcenter; + dD.dy = Dy - Dcenter; + ray->dD = differential_make_compact(dD); + ray->dP = differential_make_compact(dP); #endif #ifdef __CAMERA_CLIPPING__ /* clipping */ float nearclip = cam->nearclip; ray->P += nearclip * ray->D; - ray->dP.dx += nearclip * ray->dD.dx; - ray->dP.dy += nearclip * ray->dD.dy; + ray->dP += nearclip * ray->dD; ray->t = cam->cliplength; #else ray->t = FLT_MAX; @@ -352,7 +370,7 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, /* Common */ -ccl_device_inline void camera_sample(KernelGlobals *kg, +ccl_device_inline void camera_sample(const KernelGlobals *ccl_restrict kg, int x, int y, float filter_u, @@ -426,13 +444,13 @@ ccl_device_inline void camera_sample(KernelGlobals *kg, /* Utilities */ -ccl_device_inline float3 camera_position(KernelGlobals *kg) +ccl_device_inline float3 camera_position(const KernelGlobals *kg) { Transform cameratoworld = kernel_data.cam.cameratoworld; return make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); } -ccl_device_inline float camera_distance(KernelGlobals *kg, float3 P) +ccl_device_inline float camera_distance(const KernelGlobals *kg, float3 P) { Transform cameratoworld = kernel_data.cam.cameratoworld; float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); @@ -446,7 +464,7 @@ ccl_device_inline float camera_distance(KernelGlobals *kg, float3 P) } } -ccl_device_inline float camera_z_depth(KernelGlobals *kg, float3 P) +ccl_device_inline float camera_z_depth(const KernelGlobals *kg, float3 P) { if (kernel_data.cam.type != CAMERA_PANORAMA) { Transform worldtocamera = kernel_data.cam.worldtocamera; @@ -459,7 +477,7 @@ ccl_device_inline float camera_z_depth(KernelGlobals *kg, float3 P) } } -ccl_device_inline float3 camera_direction_from_point(KernelGlobals *kg, float3 P) +ccl_device_inline float3 camera_direction_from_point(const KernelGlobals *kg, float3 P) { Transform cameratoworld = kernel_data.cam.cameratoworld; @@ -473,7 +491,7 @@ ccl_device_inline float3 camera_direction_from_point(KernelGlobals *kg, float3 P } } -ccl_device_inline float3 camera_world_to_ndc(KernelGlobals *kg, ShaderData *sd, float3 P) +ccl_device_inline float3 camera_world_to_ndc(const KernelGlobals *kg, ShaderData *sd, float3 P) { if (kernel_data.cam.type != CAMERA_PANORAMA) { /* perspective / ortho */ diff --git a/intern/cycles/kernel/kernel_color.h b/intern/cycles/kernel/kernel_color.h index 5eb1bdad02e..960774e0741 100644 --- a/intern/cycles/kernel/kernel_color.h +++ b/intern/cycles/kernel/kernel_color.h @@ -14,25 +14,22 @@ * limitations under the License. */ -#ifndef __KERNEL_COLOR_H__ -#define __KERNEL_COLOR_H__ +#pragma once #include "util/util_color.h" CCL_NAMESPACE_BEGIN -ccl_device float3 xyz_to_rgb(KernelGlobals *kg, float3 xyz) +ccl_device float3 xyz_to_rgb(const KernelGlobals *kg, float3 xyz) { return make_float3(dot(float4_to_float3(kernel_data.film.xyz_to_r), xyz), dot(float4_to_float3(kernel_data.film.xyz_to_g), xyz), dot(float4_to_float3(kernel_data.film.xyz_to_b), xyz)); } -ccl_device float linear_rgb_to_gray(KernelGlobals *kg, float3 c) +ccl_device float linear_rgb_to_gray(const KernelGlobals *kg, float3 c) { return dot(c, float4_to_float3(kernel_data.film.rgb_to_y)); } CCL_NAMESPACE_END - -#endif /* __KERNEL_COLOR_H__ */ diff --git a/intern/cycles/kernel/kernel_compat_opencl.h b/intern/cycles/kernel/kernel_compat_opencl.h deleted file mode 100644 index 4a9304a134c..00000000000 --- a/intern/cycles/kernel/kernel_compat_opencl.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_COMPAT_OPENCL_H__ -#define __KERNEL_COMPAT_OPENCL_H__ - -#define __KERNEL_GPU__ -#define __KERNEL_OPENCL__ - -/* no namespaces in opencl */ -#define CCL_NAMESPACE_BEGIN -#define CCL_NAMESPACE_END - -#ifdef __CL_NOINLINE__ -# define ccl_noinline __attribute__((noinline)) -#else -# define ccl_noinline -#endif - -/* in opencl all functions are device functions, so leave this empty */ -#define ccl_device -#define ccl_device_inline ccl_device -#define ccl_device_forceinline ccl_device -#define ccl_device_noinline ccl_device ccl_noinline -#define ccl_device_noinline_cpu ccl_device -#define ccl_may_alias -#define ccl_static_constant static __constant -#define ccl_constant __constant -#define ccl_global __global -#define ccl_local __local -#define ccl_local_param __local -#define ccl_private __private -#define ccl_restrict restrict -#define ccl_ref -#define ccl_align(n) __attribute__((aligned(n))) -#define ccl_optional_struct_init - -#if __OPENCL_VERSION__ >= 200 && !defined(__NV_CL_C_VERSION) -# define ccl_loop_no_unroll __attribute__((opencl_unroll_hint(1))) -#else -# define ccl_loop_no_unroll -#endif - -#ifdef __SPLIT_KERNEL__ -# define ccl_addr_space __global -#else -# define ccl_addr_space -#endif - -#define ATTR_FALLTHROUGH - -#define ccl_local_id(d) get_local_id(d) -#define ccl_global_id(d) get_global_id(d) - -#define ccl_local_size(d) get_local_size(d) -#define ccl_global_size(d) get_global_size(d) - -#define ccl_group_id(d) get_group_id(d) -#define ccl_num_groups(d) get_num_groups(d) - -/* Selective nodes compilation. */ -#ifndef __NODES_MAX_GROUP__ -# define __NODES_MAX_GROUP__ NODE_GROUP_LEVEL_MAX -#endif -#ifndef __NODES_FEATURES__ -# define __NODES_FEATURES__ NODE_FEATURE_ALL -#endif - -/* no assert in opencl */ -#define kernel_assert(cond) - -/* make_type definitions with opencl style element initializers */ -#ifdef make_float2 -# undef make_float2 -#endif -#ifdef make_float3 -# undef make_float3 -#endif -#ifdef make_float4 -# undef make_float4 -#endif -#ifdef make_int2 -# undef make_int2 -#endif -#ifdef make_int3 -# undef make_int3 -#endif -#ifdef make_int4 -# undef make_int4 -#endif -#ifdef make_uchar4 -# undef make_uchar4 -#endif - -#define make_float2(x, y) ((float2)(x, y)) -#define make_float3(x, y, z) ((float3)(x, y, z)) -#define make_float4(x, y, z, w) ((float4)(x, y, z, w)) -#define make_int2(x, y) ((int2)(x, y)) -#define make_int3(x, y, z) ((int3)(x, y, z)) -#define make_int4(x, y, z, w) ((int4)(x, y, z, w)) -#define make_uchar4(x, y, z, w) ((uchar4)(x, y, z, w)) - -/* math functions */ -#define __uint_as_float(x) as_float(x) -#define __float_as_uint(x) as_uint(x) -#define __int_as_float(x) as_float(x) -#define __float_as_int(x) as_int(x) -#define powf(x, y) pow(((float)(x)), ((float)(y))) -#define fabsf(x) fabs(((float)(x))) -#define copysignf(x, y) copysign(((float)(x)), ((float)(y))) -#define asinf(x) asin(((float)(x))) -#define acosf(x) acos(((float)(x))) -#define atanf(x) atan(((float)(x))) -#define floorf(x) floor(((float)(x))) -#define ceilf(x) ceil(((float)(x))) -#define hypotf(x, y) hypot(((float)(x)), ((float)(y))) -#define atan2f(x, y) atan2(((float)(x)), ((float)(y))) -#define fmaxf(x, y) fmax(((float)(x)), ((float)(y))) -#define fminf(x, y) fmin(((float)(x)), ((float)(y))) -#define fmodf(x, y) fmod((float)(x), (float)(y)) -#define sinhf(x) sinh(((float)(x))) -#define coshf(x) cosh(((float)(x))) -#define tanhf(x) tanh(((float)(x))) - -/* Use native functions with possibly lower precision for performance, - * no issues found so far. */ -#if 1 -# define sinf(x) native_sin(((float)(x))) -# define cosf(x) native_cos(((float)(x))) -# define tanf(x) native_tan(((float)(x))) -# define expf(x) native_exp(((float)(x))) -# define sqrtf(x) native_sqrt(((float)(x))) -# define logf(x) native_log(((float)(x))) -# define rcp(x) native_recip(x) -#else -# define sinf(x) sin(((float)(x))) -# define cosf(x) cos(((float)(x))) -# define tanf(x) tan(((float)(x))) -# define expf(x) exp(((float)(x))) -# define sqrtf(x) sqrt(((float)(x))) -# define logf(x) log(((float)(x))) -# define rcp(x) recip(x) -#endif - -/* data lookup defines */ -#define kernel_data (*kg->data) -#define kernel_tex_array(tex) \ - ((const ccl_global tex##_t *)(kg->buffers[kg->tex.cl_buffer] + kg->tex.data)) -#define kernel_tex_fetch(tex, index) kernel_tex_array(tex)[(index)] - -/* define NULL */ -#ifndef NULL -# define NULL ((void *)0) -#endif - -/* enable extensions */ -#ifdef __KERNEL_CL_KHR_FP16__ -# pragma OPENCL EXTENSION cl_khr_fp16 : enable -#endif - -#include "util/util_half.h" -#include "util/util_types.h" - -#endif /* __KERNEL_COMPAT_OPENCL_H__ */ diff --git a/intern/cycles/kernel/kernel_differential.h b/intern/cycles/kernel/kernel_differential.h index 3ec0cdbaccc..db4e110bd10 100644 --- a/intern/cycles/kernel/kernel_differential.h +++ b/intern/cycles/kernel/kernel_differential.h @@ -14,26 +14,28 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* See "Tracing Ray Differentials", Homan Igehy, 1999. */ -ccl_device void differential_transfer(ccl_addr_space differential3 *dP_, - const differential3 dP, - float3 D, - const differential3 dD, - float3 Ng, - float t) +ccl_device void differential_transfer(ccl_addr_space differential3 *surface_dP, + const differential3 ray_dP, + float3 ray_D, + const differential3 ray_dD, + float3 surface_Ng, + float ray_t) { /* ray differential transfer through homogeneous medium, to * compute dPdx/dy at a shading point from the incoming ray */ - float3 tmp = D / dot(D, Ng); - float3 tmpx = dP.dx + t * dD.dx; - float3 tmpy = dP.dy + t * dD.dy; + float3 tmp = ray_D / dot(ray_D, surface_Ng); + float3 tmpx = ray_dP.dx + ray_t * ray_dD.dx; + float3 tmpy = ray_dP.dy + ray_t * ray_dD.dy; - dP_->dx = tmpx - dot(tmpx, Ng) * tmp; - dP_->dy = tmpy - dot(tmpy, Ng) * tmp; + surface_dP->dx = tmpx - dot(tmpx, surface_Ng) * tmp; + surface_dP->dy = tmpy - dot(tmpy, surface_Ng) * tmp; } ccl_device void differential_incoming(ccl_addr_space differential3 *dI, const differential3 dD) @@ -112,4 +114,53 @@ ccl_device differential3 differential3_zero() return d; } +/* Compact ray differentials that are just a scale to reduce memory usage and + * access cost in GPU. + * + * See above for more accurate reference implementations. + * + * TODO: also store the more compact version in ShaderData and recompute where + * needed? */ + +ccl_device_forceinline float differential_zero_compact() +{ + return 0.0f; +} + +ccl_device_forceinline float differential_make_compact(const differential3 D) +{ + return 0.5f * (len(D.dx) + len(D.dy)); +} + +ccl_device_forceinline void differential_transfer_compact(ccl_addr_space differential3 *surface_dP, + const float ray_dP, + const float3 /* ray_D */, + const float ray_dD, + const float3 surface_Ng, + const float ray_t) +{ + /* ray differential transfer through homogeneous medium, to + * compute dPdx/dy at a shading point from the incoming ray */ + float scale = ray_dP + ray_t * ray_dD; + + float3 dx, dy; + make_orthonormals(surface_Ng, &dx, &dy); + surface_dP->dx = dx * scale; + surface_dP->dy = dy * scale; +} + +ccl_device_forceinline void differential_incoming_compact(ccl_addr_space differential3 *dI, + const float3 D, + const float dD) +{ + /* compute dIdx/dy at a shading point, we just need to negate the + * differential of the ray direction */ + + float3 dx, dy; + make_orthonormals(D, &dx, &dy); + + dI->dx = dD * dx; + dI->dy = dD * dy; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h index aebf2ec8e28..d62285d173d 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/kernel_emission.h @@ -14,40 +14,36 @@ * limitations under the License. */ +#pragma once + +#include "kernel/kernel_light.h" +#include "kernel/kernel_montecarlo.h" +#include "kernel/kernel_path_state.h" +#include "kernel/kernel_shader.h" + CCL_NAMESPACE_BEGIN -/* Direction Emission */ -ccl_device_noinline_cpu float3 direct_emissive_eval(KernelGlobals *kg, - ShaderData *emission_sd, - LightSample *ls, - ccl_addr_space PathState *state, - float3 I, - differential3 dI, - float t, - float time) +/* Evaluate shader on light. */ +ccl_device_noinline_cpu float3 light_sample_shader_eval(INTEGRATOR_STATE_ARGS, + ShaderData *ccl_restrict emission_sd, + LightSample *ccl_restrict ls, + float time) { /* setup shading at emitter */ float3 eval = zero_float3(); if (shader_constant_emission_eval(kg, ls->shader, &eval)) { - if ((ls->prim != PRIM_NONE) && dot(ls->Ng, I) < 0.0f) { + if ((ls->prim != PRIM_NONE) && dot(ls->Ng, ls->D) > 0.0f) { ls->Ng = -ls->Ng; } } else { /* Setup shader data and call shader_eval_surface once, better * for GPU coherence and compile times. */ + PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_LIGHT_SETUP); #ifdef __BACKGROUND_MIS__ if (ls->type == LIGHT_BACKGROUND) { - Ray ray; - ray.D = ls->D; - ray.P = ls->P; - ray.t = 1.0f; - ray.time = time; - ray.dP = differential3_zero(); - ray.dD = dI; - - shader_setup_from_background(kg, emission_sd, &ray); + shader_setup_from_background(kg, emission_sd, ls->P, ls->D, time); } else #endif @@ -56,13 +52,13 @@ ccl_device_noinline_cpu float3 direct_emissive_eval(KernelGlobals *kg, emission_sd, ls->P, ls->Ng, - I, + -ls->D, ls->shader, ls->object, ls->prim, ls->u, ls->v, - t, + ls->t, time, false, ls->lamp); @@ -70,11 +66,13 @@ ccl_device_noinline_cpu float3 direct_emissive_eval(KernelGlobals *kg, ls->Ng = emission_sd->Ng; } + PROFILING_SHADER(emission_sd->object, emission_sd->shader); + PROFILING_EVENT(PROFILING_SHADE_LIGHT_EVAL); + /* No proper path flag, we're evaluating this for all closures. that's * weak but we'd have to do multiple evaluations otherwise. */ - path_state_modify_bounce(state, true); - shader_eval_surface(kg, emission_sd, state, NULL, PATH_RAY_EMISSION); - path_state_modify_bounce(state, false); + shader_eval_surface( + INTEGRATOR_STATE_PASS, emission_sd, NULL, PATH_RAY_EMISSION); /* Evaluate closures. */ #ifdef __BACKGROUND_MIS__ @@ -98,85 +96,129 @@ ccl_device_noinline_cpu float3 direct_emissive_eval(KernelGlobals *kg, return eval; } -ccl_device_noinline_cpu bool direct_emission(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - LightSample *ls, - ccl_addr_space PathState *state, - Ray *ray, - BsdfEval *eval, - bool *is_lamp, - float rand_terminate) +/* Test if light sample is from a light or emission from geometry. */ +ccl_device_inline bool light_sample_is_light(const LightSample *ccl_restrict ls) { - if (ls->pdf == 0.0f) - return false; + /* return if it's a lamp for shadow pass */ + return (ls->prim == PRIM_NONE && ls->type != LIGHT_BACKGROUND); +} - /* todo: implement */ - differential3 dD = differential3_zero(); - - /* evaluate closure */ - - float3 light_eval = direct_emissive_eval( - kg, emission_sd, ls, state, -ls->D, dD, ls->t, sd->time); - - if (is_zero(light_eval)) - return false; - - /* evaluate BSDF at shading point */ - -#ifdef __VOLUME__ - if (sd->prim != PRIM_NONE) - shader_bsdf_eval(kg, sd, ls->D, eval, ls->pdf, ls->shader & SHADER_USE_MIS); - else { - float bsdf_pdf; - shader_volume_phase_eval(kg, sd, ls->D, eval, &bsdf_pdf); - if (ls->shader & SHADER_USE_MIS) { - /* Multiple importance sampling. */ - float mis_weight = power_heuristic(ls->pdf, bsdf_pdf); - light_eval *= mis_weight; - } +/* Early path termination of shadow rays. */ +ccl_device_inline bool light_sample_terminate(const KernelGlobals *ccl_restrict kg, + const LightSample *ccl_restrict ls, + BsdfEval *ccl_restrict eval, + const float rand_terminate) +{ + if (bsdf_eval_is_zero(eval)) { + return true; } -#else - shader_bsdf_eval(kg, sd, ls->D, eval, ls->pdf, ls->shader & SHADER_USE_MIS); -#endif - bsdf_eval_mul3(eval, light_eval / ls->pdf); - -#ifdef __PASSES__ - /* use visibility flag to skip lights */ - if (ls->shader & SHADER_EXCLUDE_ANY) { - if (ls->shader & SHADER_EXCLUDE_DIFFUSE) - eval->diffuse = zero_float3(); - if (ls->shader & SHADER_EXCLUDE_GLOSSY) - eval->glossy = zero_float3(); - if (ls->shader & SHADER_EXCLUDE_TRANSMIT) - eval->transmission = zero_float3(); - if (ls->shader & SHADER_EXCLUDE_SCATTER) - eval->volume = zero_float3(); - } -#endif - - if (bsdf_eval_is_zero(eval)) - return false; - - if (kernel_data.integrator.light_inv_rr_threshold > 0.0f -#ifdef __SHADOW_TRICKS__ - && (state->flag & PATH_RAY_SHADOW_CATCHER) == 0 -#endif - ) { + if (kernel_data.integrator.light_inv_rr_threshold > 0.0f) { float probability = max3(fabs(bsdf_eval_sum(eval))) * kernel_data.integrator.light_inv_rr_threshold; if (probability < 1.0f) { if (rand_terminate >= probability) { - return false; + return true; } bsdf_eval_mul(eval, 1.0f / probability); } } + return false; +} + +/* This function should be used to compute a modified ray start position for + * rays leaving from a surface. The algorithm slightly distorts flat surface + * of a triangle. Surface is lifted by amount h along normal n in the incident + * point. */ + +ccl_device_inline float3 shadow_ray_smooth_surface_offset(const KernelGlobals *ccl_restrict kg, + const ShaderData *ccl_restrict sd, + float3 Ng) +{ + float3 V[3], N[3]; + triangle_vertices_and_normals(kg, sd->prim, V, N); + + const float u = sd->u, v = sd->v; + const float w = 1 - u - v; + float3 P = V[0] * u + V[1] * v + V[2] * w; /* Local space */ + float3 n = N[0] * u + N[1] * v + N[2] * w; /* We get away without normalization */ + + object_normal_transform(kg, sd, &n); /* Normal x scale, world space */ + + /* Parabolic approximation */ + float a = dot(N[2] - N[0], V[0] - V[2]); + float b = dot(N[2] - N[1], V[1] - V[2]); + float c = dot(N[1] - N[0], V[1] - V[0]); + float h = a * u * (u - 1) + (a + b + c) * u * v + b * v * (v - 1); + + /* Check flipped normals */ + if (dot(n, Ng) > 0) { + /* Local linear envelope */ + float h0 = max(max(dot(V[1] - V[0], N[0]), dot(V[2] - V[0], N[0])), 0.0f); + float h1 = max(max(dot(V[0] - V[1], N[1]), dot(V[2] - V[1], N[1])), 0.0f); + float h2 = max(max(dot(V[0] - V[2], N[2]), dot(V[1] - V[2], N[2])), 0.0f); + h0 = max(dot(V[0] - P, N[0]) + h0, 0.0f); + h1 = max(dot(V[1] - P, N[1]) + h1, 0.0f); + h2 = max(dot(V[2] - P, N[2]) + h2, 0.0f); + h = max(min(min(h0, h1), h2), h * 0.5f); + } + else { + float h0 = max(max(dot(V[0] - V[1], N[0]), dot(V[0] - V[2], N[0])), 0.0f); + float h1 = max(max(dot(V[1] - V[0], N[1]), dot(V[1] - V[2], N[1])), 0.0f); + float h2 = max(max(dot(V[2] - V[0], N[2]), dot(V[2] - V[1], N[2])), 0.0f); + h0 = max(dot(P - V[0], N[0]) + h0, 0.0f); + h1 = max(dot(P - V[1], N[1]) + h1, 0.0f); + h2 = max(dot(P - V[2], N[2]) + h2, 0.0f); + h = min(-min(min(h0, h1), h2), h * 0.5f); + } + + return n * h; +} + +/* Ray offset to avoid shadow terminator artifact. */ + +ccl_device_inline float3 shadow_ray_offset(const KernelGlobals *ccl_restrict kg, + const ShaderData *ccl_restrict sd, + float3 L) +{ + float NL = dot(sd->N, L); + bool transmit = (NL < 0.0f); + float3 Ng = (transmit ? -sd->Ng : sd->Ng); + float3 P = ray_offset(sd->P, Ng); + + if ((sd->type & PRIMITIVE_ALL_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) { + const float offset_cutoff = + kernel_tex_fetch(__objects, sd->object).shadow_terminator_geometry_offset; + /* Do ray offset (heavy stuff) only for close to be terminated triangles: + * offset_cutoff = 0.1f means that 10-20% of rays will be affected. Also + * make a smooth transition near the threshold. */ + if (offset_cutoff > 0.0f) { + float NgL = dot(Ng, L); + float offset_amount = 0.0f; + if (NL < offset_cutoff) { + offset_amount = clamp(2.0f - (NgL + NL) / offset_cutoff, 0.0f, 1.0f); + } + else { + offset_amount = clamp(1.0f - NgL / offset_cutoff, 0.0f, 1.0f); + } + if (offset_amount > 0.0f) { + P += shadow_ray_smooth_surface_offset(kg, sd, Ng) * offset_amount; + } + } + } + + return P; +} + +ccl_device_inline void shadow_ray_setup(const ShaderData *ccl_restrict sd, + const LightSample *ccl_restrict ls, + const float3 P, + Ray *ray) +{ if (ls->shader & SHADER_CAST_SHADOW) { /* setup ray */ - ray->P = ray_offset_shadow(kg, sd, ls->D); + ray->P = P; if (ls->t == FLT_MAX) { /* distant light */ @@ -185,160 +227,40 @@ ccl_device_noinline_cpu bool direct_emission(KernelGlobals *kg, } else { /* other lights, avoid self-intersection */ - ray->D = ray_offset(ls->P, ls->Ng) - ray->P; + ray->D = ray_offset(ls->P, ls->Ng) - P; ray->D = normalize_len(ray->D, &ray->t); } - - ray->dP = sd->dP; - ray->dD = differential3_zero(); } else { /* signal to not cast shadow ray */ + ray->P = zero_float3(); + ray->D = zero_float3(); ray->t = 0.0f; } - /* return if it's a lamp for shadow pass */ - *is_lamp = (ls->prim == PRIM_NONE && ls->type != LIGHT_BACKGROUND); - - return true; + ray->dP = differential_make_compact(sd->dP); + ray->dD = differential_zero_compact(); + ray->time = sd->time; } -/* Indirect Primitive Emission */ - -ccl_device_noinline_cpu float3 indirect_primitive_emission( - KernelGlobals *kg, ShaderData *sd, float t, int path_flag, float bsdf_pdf) +/* Create shadow ray towards light sample. */ +ccl_device_inline void light_sample_to_surface_shadow_ray(const KernelGlobals *ccl_restrict kg, + const ShaderData *ccl_restrict sd, + const LightSample *ccl_restrict ls, + Ray *ray) { - /* evaluate emissive closure */ - float3 L = shader_emissive_eval(sd); - -#ifdef __HAIR__ - if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS) && - (sd->type & PRIMITIVE_ALL_TRIANGLE)) -#else - if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS)) -#endif - { - /* multiple importance sampling, get triangle light pdf, - * and compute weight with respect to BSDF pdf */ - float pdf = triangle_light_pdf(kg, sd, t); - float mis_weight = power_heuristic(bsdf_pdf, pdf); - - return L * mis_weight; - } - - return L; + const float3 P = shadow_ray_offset(kg, sd, ls->D); + shadow_ray_setup(sd, ls, P, ray); } -/* Indirect Lamp Emission */ - -ccl_device_noinline_cpu void indirect_lamp_emission(KernelGlobals *kg, - ShaderData *emission_sd, - ccl_addr_space PathState *state, - PathRadiance *L, - Ray *ray, - float3 throughput) +/* Create shadow ray towards light sample. */ +ccl_device_inline void light_sample_to_volume_shadow_ray(const KernelGlobals *ccl_restrict kg, + const ShaderData *ccl_restrict sd, + const LightSample *ccl_restrict ls, + const float3 P, + Ray *ray) { - for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { - LightSample ls ccl_optional_struct_init; - - if (!lamp_light_eval(kg, lamp, ray->P, ray->D, ray->t, &ls)) - continue; - -#ifdef __PASSES__ - /* use visibility flag to skip lights */ - if (ls.shader & SHADER_EXCLUDE_ANY) { - if (((ls.shader & SHADER_EXCLUDE_DIFFUSE) && (state->flag & PATH_RAY_DIFFUSE)) || - ((ls.shader & SHADER_EXCLUDE_GLOSSY) && - ((state->flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == - (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || - ((ls.shader & SHADER_EXCLUDE_TRANSMIT) && (state->flag & PATH_RAY_TRANSMIT)) || - ((ls.shader & SHADER_EXCLUDE_SCATTER) && (state->flag & PATH_RAY_VOLUME_SCATTER))) - continue; - } -#endif - - float3 lamp_L = direct_emissive_eval( - kg, emission_sd, &ls, state, -ray->D, ray->dD, ls.t, ray->time); - -#ifdef __VOLUME__ - if (state->volume_stack[0].shader != SHADER_NONE) { - /* shadow attenuation */ - Ray volume_ray = *ray; - volume_ray.t = ls.t; - float3 volume_tp = one_float3(); - kernel_volume_shadow(kg, emission_sd, state, &volume_ray, &volume_tp); - lamp_L *= volume_tp; - } -#endif - - if (!(state->flag & PATH_RAY_MIS_SKIP)) { - /* multiple importance sampling, get regular light pdf, - * and compute weight with respect to BSDF pdf */ - float mis_weight = power_heuristic(state->ray_pdf, ls.pdf); - lamp_L *= mis_weight; - } - - path_radiance_accum_emission(kg, L, state, throughput, lamp_L); - } -} - -/* Indirect Background */ - -ccl_device_noinline_cpu float3 indirect_background(KernelGlobals *kg, - ShaderData *emission_sd, - ccl_addr_space PathState *state, - ccl_global float *buffer, - ccl_addr_space Ray *ray) -{ -#ifdef __BACKGROUND__ - int shader = kernel_data.background.surface_shader; - - /* Use visibility flag to skip lights. */ - if (shader & SHADER_EXCLUDE_ANY) { - if (((shader & SHADER_EXCLUDE_DIFFUSE) && (state->flag & PATH_RAY_DIFFUSE)) || - ((shader & SHADER_EXCLUDE_GLOSSY) && - ((state->flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == - (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || - ((shader & SHADER_EXCLUDE_TRANSMIT) && (state->flag & PATH_RAY_TRANSMIT)) || - ((shader & SHADER_EXCLUDE_CAMERA) && (state->flag & PATH_RAY_CAMERA)) || - ((shader & SHADER_EXCLUDE_SCATTER) && (state->flag & PATH_RAY_VOLUME_SCATTER))) - return zero_float3(); - } - - /* Evaluate background shader. */ - float3 L = zero_float3(); - if (!shader_constant_emission_eval(kg, shader, &L)) { -# ifdef __SPLIT_KERNEL__ - Ray priv_ray = *ray; - shader_setup_from_background(kg, emission_sd, &priv_ray); -# else - shader_setup_from_background(kg, emission_sd, ray); -# endif - - path_state_modify_bounce(state, true); - shader_eval_surface(kg, emission_sd, state, buffer, state->flag | PATH_RAY_EMISSION); - path_state_modify_bounce(state, false); - - L = shader_background_eval(emission_sd); - } - - /* Background MIS weights. */ -# ifdef __BACKGROUND_MIS__ - /* Check if background light exists or if we should skip pdf. */ - if (!(state->flag & PATH_RAY_MIS_SKIP) && kernel_data.background.use_mis) { - /* multiple importance sampling, get background light pdf for ray - * direction, and compute weight with respect to BSDF pdf */ - float pdf = background_light_pdf(kg, ray->P, ray->D); - float mis_weight = power_heuristic(state->ray_pdf, pdf); - - return L * mis_weight; - } -# endif - - return L; -#else - return make_float3(0.8f, 0.8f, 0.8f); -#endif + shadow_ray_setup(sd, ls, P, ray); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_film.h b/intern/cycles/kernel/kernel_film.h index a6fd4f1dc7e..fa93f4830d1 100644 --- a/intern/cycles/kernel/kernel_film.h +++ b/intern/cycles/kernel/kernel_film.h @@ -14,119 +14,516 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device float4 film_get_pass_result(KernelGlobals *kg, - ccl_global float *buffer, - float sample_scale, - int index, - bool use_display_sample_scale) +/* -------------------------------------------------------------------- + * Common utilities. + */ + +/* The input buffer contains transparency = 1 - alpha, this converts it to + * alpha. Also clamp since alpha might end up outside of 0..1 due to Russian + * roulette. */ +ccl_device_forceinline float film_transparency_to_alpha(float transparency) { - float4 pass_result; + return saturate(1.0f - transparency); +} - int display_pass_stride = kernel_data.film.display_pass_stride; - int display_pass_components = kernel_data.film.display_pass_components; - - if (display_pass_components == 4) { - float4 in = *(ccl_global float4 *)(buffer + display_pass_stride + - index * kernel_data.film.pass_stride); - float alpha = use_display_sample_scale ? - (kernel_data.film.use_display_pass_alpha ? in.w : 1.0f / sample_scale) : - 1.0f; - - pass_result = make_float4(in.x, in.y, in.z, alpha); - - int display_divide_pass_stride = kernel_data.film.display_divide_pass_stride; - if (display_divide_pass_stride != -1) { - ccl_global float4 *divide_in = (ccl_global float4 *)(buffer + display_divide_pass_stride + - index * kernel_data.film.pass_stride); - float3 divided = safe_divide_even_color(float4_to_float3(pass_result), - float4_to_float3(*divide_in)); - pass_result = make_float4(divided.x, divided.y, divided.z, pass_result.w); - } - - if (kernel_data.film.use_display_exposure) { - float exposure = kernel_data.film.exposure; - pass_result *= make_float4(exposure, exposure, exposure, 1.0f); - } - } - else if (display_pass_components == 1) { - ccl_global float *in = (ccl_global float *)(buffer + display_pass_stride + - index * kernel_data.film.pass_stride); - pass_result = make_float4(*in, *in, *in, 1.0f / sample_scale); +ccl_device_inline float film_get_scale(const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer) +{ + if (kfilm_convert->pass_sample_count == PASS_UNUSED) { + return kfilm_convert->scale; } - return pass_result; + if (kfilm_convert->pass_use_filter) { + const uint sample_count = *((const uint *)(buffer + kfilm_convert->pass_sample_count)); + return 1.0f / sample_count; + } + + return 1.0f; } -ccl_device float4 film_map(KernelGlobals *kg, float4 rgba_in, float scale) +ccl_device_inline float film_get_scale_exposure(const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer) { - float4 result; + if (kfilm_convert->pass_sample_count == PASS_UNUSED) { + return kfilm_convert->scale_exposure; + } - /* Conversion to SRGB. */ - result.x = color_linear_to_srgb(rgba_in.x * scale); - result.y = color_linear_to_srgb(rgba_in.y * scale); - result.z = color_linear_to_srgb(rgba_in.z * scale); + const float scale = film_get_scale(kfilm_convert, buffer); - /* Clamp since alpha might be > 1.0 due to Russian roulette. */ - result.w = saturate(rgba_in.w * scale); + if (kfilm_convert->pass_use_exposure) { + return scale * kfilm_convert->exposure; + } - return result; + return scale; } -ccl_device uchar4 film_float_to_byte(float4 color) +ccl_device_inline bool film_get_scale_and_scale_exposure( + const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict scale, + float *ccl_restrict scale_exposure) { - uchar4 result; + if (kfilm_convert->pass_sample_count == PASS_UNUSED) { + *scale = kfilm_convert->scale; + *scale_exposure = kfilm_convert->scale_exposure; + return true; + } - /* simple float to byte conversion */ - result.x = (uchar)(saturate(color.x) * 255.0f); - result.y = (uchar)(saturate(color.y) * 255.0f); - result.z = (uchar)(saturate(color.z) * 255.0f); - result.w = (uchar)(saturate(color.w) * 255.0f); + const uint sample_count = *((const uint *)(buffer + kfilm_convert->pass_sample_count)); + if (!sample_count) { + *scale = 0.0f; + *scale_exposure = 0.0f; + return false; + } - return result; + if (kfilm_convert->pass_use_filter) { + *scale = 1.0f / sample_count; + } + else { + *scale = 1.0f; + } + + if (kfilm_convert->pass_use_exposure) { + *scale_exposure = *scale * kfilm_convert->exposure; + } + else { + *scale_exposure = *scale; + } + + return true; } -ccl_device void kernel_film_convert_to_byte(KernelGlobals *kg, - ccl_global uchar4 *rgba, - ccl_global float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride) +/* -------------------------------------------------------------------- + * Float (scalar) passes. + */ + +ccl_device_inline void film_get_pass_pixel_depth(const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) { - /* buffer offset */ - int index = offset + x + y * stride; + kernel_assert(kfilm_convert->num_components >= 1); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); - bool use_display_sample_scale = (kernel_data.film.display_divide_pass_stride == -1); - float4 rgba_in = film_get_pass_result(kg, buffer, sample_scale, index, use_display_sample_scale); + const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); - /* map colors */ - float4 float_result = film_map(kg, rgba_in, use_display_sample_scale ? sample_scale : 1.0f); - uchar4 uchar_result = film_float_to_byte(float_result); + const float *in = buffer + kfilm_convert->pass_offset; + const float f = *in; - rgba += index; - *rgba = uchar_result; + pixel[0] = (f == 0.0f) ? 1e10f : f * scale_exposure; } -ccl_device void kernel_film_convert_to_half_float(KernelGlobals *kg, - ccl_global uchar4 *rgba, - ccl_global float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride) +ccl_device_inline void film_get_pass_pixel_mist(const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) { - /* buffer offset */ - int index = offset + x + y * stride; + kernel_assert(kfilm_convert->num_components >= 1); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); - bool use_display_sample_scale = (kernel_data.film.display_divide_pass_stride == -1); - float4 rgba_in = film_get_pass_result(kg, buffer, sample_scale, index, use_display_sample_scale); + const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); - ccl_global half *out = (ccl_global half *)rgba + index * 4; - float4_store_half(out, rgba_in, use_display_sample_scale ? sample_scale : 1.0f); + const float *in = buffer + kfilm_convert->pass_offset; + const float f = *in; + + /* Note that we accumulate 1 - mist in the kernel to avoid having to + * track the mist values in the integrator state. */ + pixel[0] = saturate(1.0f - f * scale_exposure); +} + +ccl_device_inline void film_get_pass_pixel_sample_count( + const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + /* TODO(sergey): Consider normalizing into the [0..1] range, so that it is possible to see + * meaningful value when adaptive sampler stopped rendering image way before the maximum + * number of samples was reached (for examples when number of samples is set to 0 in + * viewport). */ + + kernel_assert(kfilm_convert->num_components >= 1); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + const float *in = buffer + kfilm_convert->pass_offset; + const float f = *in; + + pixel[0] = __float_as_uint(f) * kfilm_convert->scale; +} + +ccl_device_inline void film_get_pass_pixel_float(const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components >= 1); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); + + const float *in = buffer + kfilm_convert->pass_offset; + const float f = *in; + + pixel[0] = f * scale_exposure; +} + +/* -------------------------------------------------------------------- + * Float 3 passes. + */ + +ccl_device_inline void film_get_pass_pixel_light_path(const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components >= 3); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + /* Read light pass. */ + const float *in = buffer + kfilm_convert->pass_offset; + float3 f = make_float3(in[0], in[1], in[2]); + + /* Optionally add indirect light pass. */ + if (kfilm_convert->pass_indirect != PASS_UNUSED) { + const float *in_indirect = buffer + kfilm_convert->pass_indirect; + const float3 f_indirect = make_float3(in_indirect[0], in_indirect[1], in_indirect[2]); + f += f_indirect; + } + + /* Optionally divide out color. */ + if (kfilm_convert->pass_divide != PASS_UNUSED) { + const float *in_divide = buffer + kfilm_convert->pass_divide; + const float3 f_divide = make_float3(in_divide[0], in_divide[1], in_divide[2]); + f = safe_divide_even_color(f, f_divide); + + /* Exposure only, sample scale cancels out. */ + f *= kfilm_convert->exposure; + } + else { + /* Sample scale and exposure. */ + f *= film_get_scale_exposure(kfilm_convert, buffer); + } + + pixel[0] = f.x; + pixel[1] = f.y; + pixel[2] = f.z; +} + +ccl_device_inline void film_get_pass_pixel_float3(const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components >= 3); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); + + const float *in = buffer + kfilm_convert->pass_offset; + + const float3 f = make_float3(in[0], in[1], in[2]) * scale_exposure; + + pixel[0] = f.x; + pixel[1] = f.y; + pixel[2] = f.z; +} + +/* -------------------------------------------------------------------- + * Float4 passes. + */ + +ccl_device_inline void film_get_pass_pixel_motion(const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components == 4); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + kernel_assert(kfilm_convert->pass_motion_weight != PASS_UNUSED); + + const float *in = buffer + kfilm_convert->pass_offset; + const float *in_weight = buffer + kfilm_convert->pass_motion_weight; + + const float weight = in_weight[0]; + const float weight_inv = (weight > 0.0f) ? 1.0f / weight : 0.0f; + + const float4 motion = make_float4(in[0], in[1], in[2], in[3]) * weight_inv; + + pixel[0] = motion.x; + pixel[1] = motion.y; + pixel[2] = motion.z; + pixel[3] = motion.w; +} + +ccl_device_inline void film_get_pass_pixel_cryptomatte(const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components == 4); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + const float scale = film_get_scale(kfilm_convert, buffer); + + const float *in = buffer + kfilm_convert->pass_offset; + + const float4 f = make_float4(in[0], in[1], in[2], in[3]); + + /* x and z contain integer IDs, don't rescale them. + * y and w contain matte weights, they get scaled. */ + pixel[0] = f.x; + pixel[1] = f.y * scale; + pixel[2] = f.z; + pixel[3] = f.w * scale; +} + +ccl_device_inline void film_get_pass_pixel_float4(const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components == 4); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + float scale, scale_exposure; + film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure); + + const float *in = buffer + kfilm_convert->pass_offset; + + const float3 color = make_float3(in[0], in[1], in[2]) * scale_exposure; + const float alpha = in[3] * scale; + + pixel[0] = color.x; + pixel[1] = color.y; + pixel[2] = color.z; + pixel[3] = alpha; +} + +ccl_device_inline void film_get_pass_pixel_combined(const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components == 4); + + /* 3rd channel contains transparency = 1 - alpha for the combined pass. */ + + kernel_assert(kfilm_convert->num_components == 4); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + float scale, scale_exposure; + if (!film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure)) { + pixel[0] = 0.0f; + pixel[1] = 0.0f; + pixel[2] = 0.0f; + pixel[3] = 0.0f; + return; + } + + const float *in = buffer + kfilm_convert->pass_offset; + + const float3 color = make_float3(in[0], in[1], in[2]) * scale_exposure; + const float alpha = in[3] * scale; + + pixel[0] = color.x; + pixel[1] = color.y; + pixel[2] = color.z; + pixel[3] = film_transparency_to_alpha(alpha); +} + +/* -------------------------------------------------------------------- + * Shadow catcher. + */ + +ccl_device_inline float3 +film_calculate_shadow_catcher_denoised(const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer) +{ + kernel_assert(kfilm_convert->pass_shadow_catcher != PASS_UNUSED); + + float scale, scale_exposure; + film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure); + + ccl_global const float *in_catcher = buffer + kfilm_convert->pass_shadow_catcher; + + const float3 pixel = make_float3(in_catcher[0], in_catcher[1], in_catcher[2]) * scale_exposure; + + return pixel; +} + +ccl_device_inline float3 safe_divide_shadow_catcher(float3 a, float3 b) +{ + float x, y, z; + + x = (b.x != 0.0f) ? a.x / b.x : 1.0f; + y = (b.y != 0.0f) ? a.y / b.y : 1.0f; + z = (b.z != 0.0f) ? a.z / b.z : 1.0f; + + return make_float3(x, y, z); +} + +ccl_device_inline float3 +film_calculate_shadow_catcher(const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer) +{ + /* For the shadow catcher pass we divide combined pass by the shadow catcher. + * Note that denoised shadow catcher pass contains value which only needs ot be scaled (but not + * to be calculated as division). */ + + if (kfilm_convert->is_denoised) { + return film_calculate_shadow_catcher_denoised(kfilm_convert, buffer); + } + + kernel_assert(kfilm_convert->pass_shadow_catcher_sample_count != PASS_UNUSED); + + /* If there is no shadow catcher object in this pixel, there is no modification of the light + * needed, so return one. */ + ccl_global const float *in_catcher_sample_count = + buffer + kfilm_convert->pass_shadow_catcher_sample_count; + const float num_samples = in_catcher_sample_count[0]; + if (num_samples == 0.0f) { + return one_float3(); + } + + kernel_assert(kfilm_convert->pass_shadow_catcher != PASS_UNUSED); + ccl_global const float *in_catcher = buffer + kfilm_convert->pass_shadow_catcher; + + /* NOTE: It is possible that the Shadow Catcher pass is requested as an output without actual + * shadow catcher objects in the scene. In this case there will be no auxillary passes required + * for the devision (to save up memory). So delay the asserts to this point so that the number of + * samples check handles such configuration. */ + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + kernel_assert(kfilm_convert->pass_combined != PASS_UNUSED); + kernel_assert(kfilm_convert->pass_shadow_catcher_matte != PASS_UNUSED); + + ccl_global const float *in_combined = buffer + kfilm_convert->pass_combined; + ccl_global const float *in_matte = buffer + kfilm_convert->pass_shadow_catcher_matte; + + /* No scaling needed. The integration works in way that number of samples in the combined and + * shadow catcher passes are the same, and exposure is cancelled during the division. */ + const float3 color_catcher = make_float3(in_catcher[0], in_catcher[1], in_catcher[2]); + const float3 color_combined = make_float3(in_combined[0], in_combined[1], in_combined[2]); + const float3 color_matte = make_float3(in_matte[0], in_matte[1], in_matte[2]); + + /* Need to ignore contribution of the matte object when doing division (otherwise there will be + * artifacts caused by anti-aliasing). Since combined pass is used for adaptive sampling and need + * to contain matte objects, we subtrack matte objects contribution here. This is the same as if + * the matte objects were not accumulated to the combined pass. */ + const float3 combined_no_matte = color_combined - color_matte; + + const float3 shadow_catcher = safe_divide_shadow_catcher(combined_no_matte, color_catcher); + + const float scale = film_get_scale(kfilm_convert, buffer); + const float transparency = in_combined[3] * scale; + const float alpha = film_transparency_to_alpha(transparency); + + /* Alpha-over on white using transparency of the combined pass. This allows to eliminate + * artifacts which are happenning on an edge of a shadow catcher when using transparent film. + * Note that we treat shadow catcher as straight alpha here because alpha got cancelled out + * during the division. */ + const float3 pixel = (1.0f - alpha) * one_float3() + alpha * shadow_catcher; + + return pixel; +} + +ccl_device_inline float4 film_calculate_shadow_catcher_matte_with_shadow( + const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer) +{ + /* The approximation of the shadow is 1 - average(shadow_catcher_pass). A better approximation + * is possible. + * + * The matte is alpha-overed onto the shadow (which is kind of alpha-overing shadow onto footage, + * and then alpha-overing synthetic objects on top). */ + + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + kernel_assert(kfilm_convert->pass_shadow_catcher != PASS_UNUSED); + kernel_assert(kfilm_convert->pass_shadow_catcher_matte != PASS_UNUSED); + + float scale, scale_exposure; + if (!film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure)) { + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + } + + ccl_global const float *in_matte = buffer + kfilm_convert->pass_shadow_catcher_matte; + + const float3 shadow_catcher = film_calculate_shadow_catcher(kfilm_convert, buffer); + const float3 color_matte = make_float3(in_matte[0], in_matte[1], in_matte[2]) * scale_exposure; + + const float transparency = in_matte[3] * scale; + const float alpha = saturate(1.0f - transparency); + + const float alpha_matte = (1.0f - alpha) * (1.0f - average(shadow_catcher)) + alpha; + + if (kfilm_convert->use_approximate_shadow_catcher_background) { + kernel_assert(kfilm_convert->pass_background != PASS_UNUSED); + + ccl_global const float *in_background = buffer + kfilm_convert->pass_background; + const float3 color_background = make_float3( + in_background[0], in_background[1], in_background[2]) * + scale_exposure; + const float3 alpha_over = color_matte + color_background * (1.0f - alpha_matte); + return make_float4(alpha_over.x, alpha_over.y, alpha_over.z, 1.0f); + } + + return make_float4(color_matte.x, color_matte.y, color_matte.z, alpha_matte); +} + +ccl_device_inline void film_get_pass_pixel_shadow_catcher( + const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components >= 3); + + const float3 pixel_value = film_calculate_shadow_catcher(kfilm_convert, buffer); + + pixel[0] = pixel_value.x; + pixel[1] = pixel_value.y; + pixel[2] = pixel_value.z; +} + +ccl_device_inline void film_get_pass_pixel_shadow_catcher_matte_with_shadow( + const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components == 3 || kfilm_convert->num_components == 4); + + const float4 pixel_value = film_calculate_shadow_catcher_matte_with_shadow(kfilm_convert, + buffer); + + pixel[0] = pixel_value.x; + pixel[1] = pixel_value.y; + pixel[2] = pixel_value.z; + if (kfilm_convert->num_components == 4) { + pixel[3] = pixel_value.w; + } +} + +/* -------------------------------------------------------------------- + * Compositing and overlays. + */ + +ccl_device_inline void film_apply_pass_pixel_overlays_rgba( + const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + float *ccl_restrict pixel) +{ + if (kfilm_convert->show_active_pixels && + kfilm_convert->pass_adaptive_aux_buffer != PASS_UNUSED) { + if (buffer[kfilm_convert->pass_adaptive_aux_buffer + 3] == 0.0f) { + const float3 active_rgb = make_float3(1.0f, 0.0f, 0.0f); + const float3 mix_rgb = interp(make_float3(pixel[0], pixel[1], pixel[2]), active_rgb, 0.5f); + pixel[0] = mix_rgb.x; + pixel[1] = mix_rgb.y; + pixel[2] = mix_rgb.z; + } + } } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_globals.h b/intern/cycles/kernel/kernel_globals.h deleted file mode 100644 index 70aed6d54ed..00000000000 --- a/intern/cycles/kernel/kernel_globals.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Constant Globals */ - -#ifndef __KERNEL_GLOBALS_H__ -#define __KERNEL_GLOBALS_H__ - -#include "kernel/kernel_profiling.h" - -#ifdef __KERNEL_CPU__ -# include "util/util_map.h" -# include "util/util_vector.h" -#endif - -#ifdef __KERNEL_OPENCL__ -# include "util/util_atomic.h" -#endif - -CCL_NAMESPACE_BEGIN - -/* On the CPU, we pass along the struct KernelGlobals to nearly everywhere in - * the kernel, to access constant data. These are all stored as "textures", but - * these are really just standard arrays. We can't use actually globals because - * multiple renders may be running inside the same process. */ - -#ifdef __KERNEL_CPU__ - -# ifdef __OSL__ -struct OSLGlobals; -struct OSLThreadData; -struct OSLShadingSystem; -# endif - -typedef unordered_map CoverageMap; - -struct Intersection; -struct VolumeStep; - -typedef struct KernelGlobals { -# define KERNEL_TEX(type, name) texture name; -# include "kernel/kernel_textures.h" - - KernelData __data; - -# ifdef __OSL__ - /* On the CPU, we also have the OSL globals here. Most data structures are shared - * with SVM, the difference is in the shaders and object/mesh attributes. */ - OSLGlobals *osl; - OSLShadingSystem *osl_ss; - OSLThreadData *osl_tdata; -# endif - - /* **** Run-time data **** */ - - /* Heap-allocated storage for transparent shadows intersections. */ - Intersection *transparent_shadow_intersections; - - /* Storage for decoupled volume steps. */ - VolumeStep *decoupled_volume_steps[2]; - int decoupled_volume_steps_index; - - /* A buffer for storing per-pixel coverage for Cryptomatte. */ - CoverageMap *coverage_object; - CoverageMap *coverage_material; - CoverageMap *coverage_asset; - - /* split kernel */ - SplitData split_data; - SplitParams split_param_data; - - int2 global_size; - int2 global_id; - - ProfilingState profiler; -} KernelGlobals; - -#endif /* __KERNEL_CPU__ */ - -#ifdef __KERNEL_OPTIX__ - -typedef struct ShaderParams { - uint4 *input; - float4 *output; - int type; - int filter; - int sx; - int offset; - int sample; -} ShaderParams; - -typedef struct KernelParams { - WorkTile tile; - KernelData data; - ShaderParams shader; -# define KERNEL_TEX(type, name) const type *name; -# include "kernel/kernel_textures.h" -} KernelParams; - -typedef struct KernelGlobals { -# ifdef __VOLUME__ - VolumeState volume_state; -# endif - Intersection hits_stack[64]; -} KernelGlobals; - -extern "C" __constant__ KernelParams __params; - -#else /* __KERNEL_OPTIX__ */ - -/* For CUDA, constant memory textures must be globals, so we can't put them - * into a struct. As a result we don't actually use this struct and use actual - * globals and simply pass along a NULL pointer everywhere, which we hope gets - * optimized out. */ - -# ifdef __KERNEL_CUDA__ - -__constant__ KernelData __data; -typedef struct KernelGlobals { - /* NOTE: Keep the size in sync with SHADOW_STACK_MAX_HITS. */ - Intersection hits_stack[64]; -} KernelGlobals; - -# define KERNEL_TEX(type, name) const __constant__ __device__ type *name; -# include "kernel/kernel_textures.h" - -# endif /* __KERNEL_CUDA__ */ - -#endif /* __KERNEL_OPTIX__ */ - -/* OpenCL */ - -#ifdef __KERNEL_OPENCL__ - -# define KERNEL_TEX(type, name) typedef type name##_t; -# include "kernel/kernel_textures.h" - -typedef ccl_addr_space struct KernelGlobals { - ccl_constant KernelData *data; - ccl_global char *buffers[8]; - -# define KERNEL_TEX(type, name) TextureInfo name; -# include "kernel/kernel_textures.h" - -# ifdef __SPLIT_KERNEL__ - SplitData split_data; - SplitParams split_param_data; -# endif -} KernelGlobals; - -# define KERNEL_BUFFER_PARAMS \ - ccl_global char *buffer0, ccl_global char *buffer1, ccl_global char *buffer2, \ - ccl_global char *buffer3, ccl_global char *buffer4, ccl_global char *buffer5, \ - ccl_global char *buffer6, ccl_global char *buffer7 - -# define KERNEL_BUFFER_ARGS buffer0, buffer1, buffer2, buffer3, buffer4, buffer5, buffer6, buffer7 - -ccl_device_inline void kernel_set_buffer_pointers(KernelGlobals *kg, KERNEL_BUFFER_PARAMS) -{ -# ifdef __SPLIT_KERNEL__ - if (ccl_local_id(0) + ccl_local_id(1) == 0) -# endif - { - kg->buffers[0] = buffer0; - kg->buffers[1] = buffer1; - kg->buffers[2] = buffer2; - kg->buffers[3] = buffer3; - kg->buffers[4] = buffer4; - kg->buffers[5] = buffer5; - kg->buffers[6] = buffer6; - kg->buffers[7] = buffer7; - } - -# ifdef __SPLIT_KERNEL__ - ccl_barrier(CCL_LOCAL_MEM_FENCE); -# endif -} - -ccl_device_inline void kernel_set_buffer_info(KernelGlobals *kg) -{ -# ifdef __SPLIT_KERNEL__ - if (ccl_local_id(0) + ccl_local_id(1) == 0) -# endif - { - ccl_global TextureInfo *info = (ccl_global TextureInfo *)kg->buffers[0]; - -# define KERNEL_TEX(type, name) kg->name = *(info++); -# include "kernel/kernel_textures.h" - } - -# ifdef __SPLIT_KERNEL__ - ccl_barrier(CCL_LOCAL_MEM_FENCE); -# endif -} - -#endif /* __KERNEL_OPENCL__ */ - -/* Interpolated lookup table access */ - -ccl_device float lookup_table_read(KernelGlobals *kg, float x, int offset, int size) -{ - x = saturate(x) * (size - 1); - - int index = min(float_to_int(x), size - 1); - int nindex = min(index + 1, size - 1); - float t = x - index; - - float data0 = kernel_tex_fetch(__lookup_table, index + offset); - if (t == 0.0f) - return data0; - - float data1 = kernel_tex_fetch(__lookup_table, nindex + offset); - return (1.0f - t) * data0 + t * data1; -} - -ccl_device float lookup_table_read_2D( - KernelGlobals *kg, float x, float y, int offset, int xsize, int ysize) -{ - y = saturate(y) * (ysize - 1); - - int index = min(float_to_int(y), ysize - 1); - int nindex = min(index + 1, ysize - 1); - float t = y - index; - - float data0 = lookup_table_read(kg, x, offset + xsize * index, xsize); - if (t == 0.0f) - return data0; - - float data1 = lookup_table_read(kg, x, offset + xsize * nindex, xsize); - return (1.0f - t) * data0 + t * data1; -} - -CCL_NAMESPACE_END - -#endif /* __KERNEL_GLOBALS_H__ */ diff --git a/intern/cycles/kernel/kernel_id_passes.h b/intern/cycles/kernel/kernel_id_passes.h index 1ca42e933d1..ed01f494f98 100644 --- a/intern/cycles/kernel/kernel_id_passes.h +++ b/intern/cycles/kernel/kernel_id_passes.h @@ -14,8 +14,18 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN +/* Element of ID pass stored in the render buffers. + * It is `float2` semantically, but it must be unaligned since the offset of ID passes in the + * render buffers might not meet expected by compiler alignment. */ +typedef struct IDPassBufferElement { + float x; + float y; +} IDPassBufferElement; + ccl_device_inline void kernel_write_id_slots(ccl_global float *buffer, int num_slots, float id, @@ -27,7 +37,7 @@ ccl_device_inline void kernel_write_id_slots(ccl_global float *buffer, } for (int slot = 0; slot < num_slots; slot++) { - ccl_global float2 *id_buffer = (ccl_global float2 *)buffer; + ccl_global IDPassBufferElement *id_buffer = (ccl_global IDPassBufferElement *)buffer; #ifdef __ATOMIC_PASS_WRITE__ /* If the loop reaches an empty slot, the ID isn't in any slot yet - so add it! */ if (id_buffer[slot].x == ID_NONE) { @@ -65,7 +75,7 @@ ccl_device_inline void kernel_write_id_slots(ccl_global float *buffer, ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_slots) { - ccl_global float2 *id_buffer = (ccl_global float2 *)buffer; + ccl_global IDPassBufferElement *id_buffer = (ccl_global IDPassBufferElement *)buffer; for (int slot = 1; slot < num_slots; ++slot) { if (id_buffer[slot].x == ID_NONE) { return; @@ -73,7 +83,7 @@ ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_sl /* Since we're dealing with a tiny number of elements, insertion sort should be fine. */ int i = slot; while (i > 0 && id_buffer[i].y > id_buffer[i - 1].y) { - float2 swap = id_buffer[i]; + const IDPassBufferElement swap = id_buffer[i]; id_buffer[i] = id_buffer[i - 1]; id_buffer[i - 1] = swap; --i; @@ -81,19 +91,16 @@ ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_sl } } -#ifdef __KERNEL_GPU__ /* post-sorting for Cryptomatte */ -ccl_device void kernel_cryptomatte_post( - KernelGlobals *kg, ccl_global float *buffer, uint sample, int x, int y, int offset, int stride) +ccl_device_inline void kernel_cryptomatte_post(const KernelGlobals *kg, + ccl_global float *render_buffer, + int pixel_index) { - if (sample - 1 == kernel_data.integrator.aa_samples) { - int index = offset + x + y * stride; - int pass_stride = kernel_data.film.pass_stride; - ccl_global float *cryptomatte_buffer = buffer + index * pass_stride + - kernel_data.film.pass_cryptomatte; - kernel_sort_id_slots(cryptomatte_buffer, 2 * kernel_data.film.cryptomatte_depth); - } + const int pass_stride = kernel_data.film.pass_stride; + const uint64_t render_buffer_offset = (uint64_t)pixel_index * pass_stride; + ccl_global float *cryptomatte_buffer = render_buffer + render_buffer_offset + + kernel_data.film.pass_cryptomatte; + kernel_sort_id_slots(cryptomatte_buffer, 2 * kernel_data.film.cryptomatte_depth); } -#endif CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_jitter.h b/intern/cycles/kernel/kernel_jitter.h index f4e60a807f7..354e8115538 100644 --- a/intern/cycles/kernel/kernel_jitter.h +++ b/intern/cycles/kernel/kernel_jitter.h @@ -14,93 +14,27 @@ * limitations under the License. */ -/* TODO(sergey): Consider moving portable ctz/clz stuff to util. */ - +#pragma once CCL_NAMESPACE_BEGIN -/* "Correlated Multi-Jittered Sampling" - * Andrew Kensler, Pixar Technical Memo 13-01, 2013 */ - -/* TODO: find good value, suggested 64 gives pattern on cornell box ceiling. */ -#define CMJ_RANDOM_OFFSET_LIMIT 4096 - -ccl_device_inline bool cmj_is_pow2(int i) +ccl_device_inline uint32_t laine_karras_permutation(uint32_t x, uint32_t seed) { - return (i > 1) && ((i & (i - 1)) == 0); + x += seed; + x ^= (x * 0x6c50b47cu); + x ^= x * 0xb82f1e52u; + x ^= x * 0xc7afe638u; + x ^= x * 0x8d22f6e6u; + + return x; } -ccl_device_inline int cmj_fast_mod_pow2(int a, int b) +ccl_device_inline uint32_t nested_uniform_scramble(uint32_t x, uint32_t seed) { - return (a & (b - 1)); -} + x = reverse_integer_bits(x); + x = laine_karras_permutation(x, seed); + x = reverse_integer_bits(x); -/* b must be > 1 */ -ccl_device_inline int cmj_fast_div_pow2(int a, int b) -{ - kernel_assert(b > 1); - return a >> count_trailing_zeros(b); -} - -ccl_device_inline uint cmj_w_mask(uint w) -{ - kernel_assert(w > 1); - return ((1 << (32 - count_leading_zeros(w))) - 1); -} - -ccl_device_inline uint cmj_permute(uint i, uint l, uint p) -{ - uint w = l - 1; - - if ((l & w) == 0) { - /* l is a power of two (fast) */ - i ^= p; - i *= 0xe170893d; - i ^= p >> 16; - i ^= (i & w) >> 4; - i ^= p >> 8; - i *= 0x0929eb3f; - i ^= p >> 23; - i ^= (i & w) >> 1; - i *= 1 | p >> 27; - i *= 0x6935fa69; - i ^= (i & w) >> 11; - i *= 0x74dcb303; - i ^= (i & w) >> 2; - i *= 0x9e501cc3; - i ^= (i & w) >> 2; - i *= 0xc860a3df; - i &= w; - i ^= i >> 5; - - return (i + p) & w; - } - else { - /* l is not a power of two (slow) */ - w = cmj_w_mask(w); - - do { - i ^= p; - i *= 0xe170893d; - i ^= p >> 16; - i ^= (i & w) >> 4; - i ^= p >> 8; - i *= 0x0929eb3f; - i ^= p >> 23; - i ^= (i & w) >> 1; - i *= 1 | p >> 27; - i *= 0x6935fa69; - i ^= (i & w) >> 11; - i *= 0x74dcb303; - i ^= (i & w) >> 2; - i *= 0x9e501cc3; - i ^= (i & w) >> 2; - i *= 0xc860a3df; - i &= w; - i ^= i >> 5; - } while (i >= l); - - return (i + p) % l; - } + return x; } ccl_device_inline uint cmj_hash(uint i, uint p) @@ -133,99 +67,101 @@ ccl_device_inline float cmj_randfloat(uint i, uint p) return cmj_hash(i, p) * (1.0f / 4294967808.0f); } -#ifdef __CMJ__ -ccl_device float cmj_sample_1D(int s, int N, int p) +ccl_device_inline float cmj_randfloat_simple(uint i, uint p) { - kernel_assert(s < N); - - uint x = cmj_permute(s, N, p * 0x68bc21eb); - float jx = cmj_randfloat(s, p * 0x967a889b); - - float invN = 1.0f / N; - return (x + jx) * invN; + return cmj_hash_simple(i, p) * (1.0f / (float)0xFFFFFFFF); } -/* TODO(sergey): Do some extra tests and consider moving to util_math.h. */ -ccl_device_inline int cmj_isqrt(int value) +ccl_device float pmj_sample_1D(const KernelGlobals *kg, uint sample, uint rng_hash, uint dimension) { -# if defined(__KERNEL_CUDA__) - return float_to_int(__fsqrt_ru(value)); -# elif defined(__KERNEL_GPU__) - return float_to_int(sqrtf(value)); -# else - /* This is a work around for fast-math on CPU which might replace sqrtf() - * with am approximated version. - */ - return float_to_int(sqrtf(value) + 1e-6f); -# endif -} + /* The PMJ sample sets contain a sample with (x,y) with NUM_PMJ_SAMPLES so for 1D + * the x part is used as the sample (TODO(@leesonw): Add using both x and y parts + * independently). */ -ccl_device void cmj_sample_2D(int s, int N, int p, float *fx, float *fy) -{ - kernel_assert(s < N); - - int m = cmj_isqrt(N); - int n = (N - 1) / m + 1; - float invN = 1.0f / N; - float invm = 1.0f / m; - float invn = 1.0f / n; - - s = cmj_permute(s, N, p * 0x51633e2d); - - int sdivm, smodm; - - if (cmj_is_pow2(m)) { - sdivm = cmj_fast_div_pow2(s, m); - smodm = cmj_fast_mod_pow2(s, m); - } - else { - /* Doing `s * inmv` gives precision issues here. */ - sdivm = s / m; - smodm = s - sdivm * m; - } - - uint sx = cmj_permute(smodm, m, p * 0x68bc21eb); - uint sy = cmj_permute(sdivm, n, p * 0x02e5be93); - - float jx = cmj_randfloat(s, p * 0x967a889b); - float jy = cmj_randfloat(s, p * 0x368cc8b7); - - *fx = (sx + (sy + jx) * invn) * invm; - *fy = (s + jy) * invN; -} + /* Perform Owen shuffle of the sample number to reorder the samples. */ +#ifdef _SIMPLE_HASH_ + const uint rv = cmj_hash_simple(dimension, rng_hash); +#else /* Use a _REGULAR_HASH_. */ + const uint rv = cmj_hash(dimension, rng_hash); +#endif +#ifdef _XOR_SHUFFLE_ +# warning "Using XOR shuffle." + const uint s = sample ^ rv; +#else /* Use _OWEN_SHUFFLE_ for reordering. */ + const uint s = nested_uniform_scramble(sample, rv); #endif -ccl_device float pmj_sample_1D(KernelGlobals *kg, int sample, int rng_hash, int dimension) -{ - /* Fallback to random */ - if (sample >= NUM_PMJ_SAMPLES) { - const int p = rng_hash + dimension; - return cmj_randfloat(sample, p); - } - else { - const uint mask = cmj_hash_simple(dimension, rng_hash) & 0x007fffff; - const int index = ((dimension % NUM_PMJ_PATTERNS) * NUM_PMJ_SAMPLES + sample) * 2; - return __uint_as_float(kernel_tex_fetch(__sample_pattern_lut, index) ^ mask) - 1.0f; - } + /* Based on the sample number a sample pattern is selected and offset by the dimension. */ + const uint sample_set = s / NUM_PMJ_SAMPLES; + const uint d = (dimension + sample_set); + const uint dim = d % NUM_PMJ_PATTERNS; + int index = 2 * (dim * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)); + + float fx = kernel_tex_fetch(__sample_pattern_lut, index); + +#ifndef _NO_CRANLEY_PATTERSON_ROTATION_ + /* Use Cranley-Patterson rotation to displace the sample pattern. */ +# ifdef _SIMPLE_HASH_ + float dx = cmj_randfloat_simple(d, rng_hash); +# else + /* Only jitter within the grid interval. */ + float dx = cmj_randfloat(d, rng_hash); +# endif + fx = fx + dx * (1.0f / NUM_PMJ_SAMPLES); + fx = fx - floorf(fx); + +#else +# warning "Not using Cranley-Patterson Rotation." +#endif + + return fx; } -ccl_device float2 pmj_sample_2D(KernelGlobals *kg, int sample, int rng_hash, int dimension) +ccl_device void pmj_sample_2D( + const KernelGlobals *kg, uint sample, uint rng_hash, uint dimension, float *x, float *y) { - if (sample >= NUM_PMJ_SAMPLES) { - const int p = rng_hash + dimension; - const float fx = cmj_randfloat(sample, p); - const float fy = cmj_randfloat(sample, p + 1); - return make_float2(fx, fy); - } - else { - const int index = ((dimension % NUM_PMJ_PATTERNS) * NUM_PMJ_SAMPLES + sample) * 2; - const uint maskx = cmj_hash_simple(dimension, rng_hash) & 0x007fffff; - const uint masky = cmj_hash_simple(dimension + 1, rng_hash) & 0x007fffff; - const float fx = __uint_as_float(kernel_tex_fetch(__sample_pattern_lut, index) ^ maskx) - 1.0f; - const float fy = __uint_as_float(kernel_tex_fetch(__sample_pattern_lut, index + 1) ^ masky) - - 1.0f; - return make_float2(fx, fy); - } + /* Perform a shuffle on the sample number to reorder the samples. */ +#ifdef _SIMPLE_HASH_ + const uint rv = cmj_hash_simple(dimension, rng_hash); +#else /* Use a _REGULAR_HASH_. */ + const uint rv = cmj_hash(dimension, rng_hash); +#endif +#ifdef _XOR_SHUFFLE_ +# warning "Using XOR shuffle." + const uint s = sample ^ rv; +#else /* Use _OWEN_SHUFFLE_ for reordering. */ + const uint s = nested_uniform_scramble(sample, rv); +#endif + + /* Based on the sample number a sample pattern is selected and offset by the dimension. */ + const uint sample_set = s / NUM_PMJ_SAMPLES; + const uint d = (dimension + sample_set); + const uint dim = d % NUM_PMJ_PATTERNS; + int index = 2 * (dim * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)); + + float fx = kernel_tex_fetch(__sample_pattern_lut, index); + float fy = kernel_tex_fetch(__sample_pattern_lut, index + 1); + +#ifndef _NO_CRANLEY_PATTERSON_ROTATION_ + /* Use Cranley-Patterson rotation to displace the sample pattern. */ +# ifdef _SIMPLE_HASH_ + float dx = cmj_randfloat_simple(d, rng_hash); + float dy = cmj_randfloat_simple(d + 1, rng_hash); +# else + float dx = cmj_randfloat(d, rng_hash); + float dy = cmj_randfloat(d + 1, rng_hash); +# endif + /* Only jitter within the grid cells. */ + fx = fx + dx * (1.0f / NUM_PMJ_DIVISIONS); + fy = fy + dy * (1.0f / NUM_PMJ_DIVISIONS); + fx = fx - floorf(fx); + fy = fy - floorf(fy); +#else +# warning "Not using Cranley Patterson Rotation." +#endif + + (*x) = fx; + (*y) = fy; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index 42a834d2ce3..52f641634b9 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -14,7 +14,14 @@ * limitations under the License. */ +#pragma once + +#include "geom/geom.h" + #include "kernel_light_background.h" +#include "kernel_montecarlo.h" +#include "kernel_projection.h" +#include "kernel_types.h" CCL_NAMESPACE_BEGIN @@ -37,10 +44,22 @@ typedef struct LightSample { /* Regular Light */ -ccl_device_inline bool lamp_light_sample( - KernelGlobals *kg, int lamp, float randu, float randv, float3 P, LightSample *ls) +template +ccl_device_inline bool light_sample(const KernelGlobals *kg, + const int lamp, + const float randu, + const float randv, + const float3 P, + const int path_flag, + LightSample *ls) { const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) { + if (klight->shader_id & SHADER_EXCLUDE_SHADOW_CATCHER) { + return false; + } + } + LightType type = (LightType)klight->type; ls->type = type; ls->shader = klight->shader_id; @@ -50,6 +69,18 @@ ccl_device_inline bool lamp_light_sample( ls->u = randu; ls->v = randv; + if (in_volume_segment && (type == LIGHT_DISTANT || type == LIGHT_BACKGROUND)) { + /* Distant lights in a volume get a dummy sample, position will not actually + * be used in that case. Only when sampling from a specific scatter position + * do we actually need to evaluate these. */ + ls->P = zero_float3(); + ls->Ng = zero_float3(); + ls->D = zero_float3(); + ls->pdf = true; + ls->t = FLT_MAX; + return true; + } + if (type == LIGHT_DISTANT) { /* distant light */ float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); @@ -123,13 +154,15 @@ ccl_device_inline bool lamp_light_sample( float invarea = fabsf(klight->area.invarea); bool is_round = (klight->area.invarea < 0.0f); - if (dot(ls->P - P, Ng) > 0.0f) { - return false; + if (!in_volume_segment) { + if (dot(ls->P - P, Ng) > 0.0f) { + return false; + } } float3 inplane; - if (is_round) { + if (is_round || in_volume_segment) { inplane = ellipse_sample(axisu * 0.5f, axisv * 0.5f, randu, randv); ls->P += inplane; ls->pdf = invarea; @@ -176,11 +209,137 @@ ccl_device_inline bool lamp_light_sample( return (ls->pdf > 0.0f); } -ccl_device bool lamp_light_eval( - KernelGlobals *kg, int lamp, float3 P, float3 D, float t, LightSample *ls) +ccl_device bool lights_intersect(const KernelGlobals *ccl_restrict kg, + const Ray *ccl_restrict ray, + Intersection *ccl_restrict isect, + const int last_prim, + const int last_object, + const int last_type, + const int path_flag) +{ + for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + + if (path_flag & PATH_RAY_CAMERA) { + if (klight->shader_id & SHADER_EXCLUDE_CAMERA) { + continue; + } + } + else { + if (!(klight->shader_id & SHADER_USE_MIS)) { + continue; + } + } + + if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) { + if (klight->shader_id & SHADER_EXCLUDE_SHADOW_CATCHER) { + continue; + } + } + + LightType type = (LightType)klight->type; + float t = 0.0f, u = 0.0f, v = 0.0f; + + if (type == LIGHT_POINT || type == LIGHT_SPOT) { + /* Sphere light. */ + const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); + const float radius = klight->spot.radius; + if (radius == 0.0f) { + continue; + } + + float3 P; + if (!ray_aligned_disk_intersect(ray->P, ray->D, ray->t, lightP, radius, &P, &t)) { + continue; + } + } + else if (type == LIGHT_AREA) { + /* Area light. */ + const float invarea = fabsf(klight->area.invarea); + const bool is_round = (klight->area.invarea < 0.0f); + if (invarea == 0.0f) { + continue; + } + + const float3 axisu = make_float3( + klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); + const float3 axisv = make_float3( + klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); + const float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); + + /* One sided. */ + if (dot(ray->D, Ng) >= 0.0f) { + continue; + } + + const float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]); + + float3 P; + if (!ray_quad_intersect( + ray->P, ray->D, 0.0f, ray->t, light_P, axisu, axisv, Ng, &P, &t, &u, &v, is_round)) { + continue; + } + } + else { + continue; + } + + if (t < isect->t && + !(last_prim == lamp && last_object == OBJECT_NONE && last_type == PRIMITIVE_LAMP)) { + isect->t = t; + isect->u = u; + isect->v = v; + isect->type = PRIMITIVE_LAMP; + isect->prim = lamp; + isect->object = OBJECT_NONE; + } + } + + return isect->prim != PRIM_NONE; +} + +ccl_device bool light_sample_from_distant_ray(const KernelGlobals *ccl_restrict kg, + const float3 ray_D, + const int lamp, + LightSample *ccl_restrict ls) { const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); - LightType type = (LightType)klight->type; + const int shader = klight->shader_id; + const float radius = klight->distant.radius; + const LightType type = (LightType)klight->type; + + if (type != LIGHT_DISTANT) { + return false; + } + if (!(shader & SHADER_USE_MIS)) { + return false; + } + if (radius == 0.0f) { + return false; + } + + /* a distant light is infinitely far away, but equivalent to a disk + * shaped light exactly 1 unit away from the current shading point. + * + * radius t^2/cos(theta) + * <----------> t = sqrt(1^2 + tan(theta)^2) + * tan(th) area = radius*radius*pi + * <-----> + * \ | (1 + tan(theta)^2)/cos(theta) + * \ | (1 + tan(acos(cos(theta)))^2)/cos(theta) + * t \th| 1 simplifies to + * \-| 1/(cos(theta)^3) + * \| magic! + * P + */ + + float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); + float costheta = dot(-lightD, ray_D); + float cosangle = klight->distant.cosangle; + + if (costheta < cosangle) + return false; + ls->type = type; ls->shader = klight->shader_id; ls->object = PRIM_NONE; @@ -189,66 +348,41 @@ ccl_device bool lamp_light_eval( /* todo: missing texture coordinates */ ls->u = 0.0f; ls->v = 0.0f; + ls->t = FLT_MAX; + ls->P = -ray_D; + ls->Ng = -ray_D; + ls->D = ray_D; - if (!(ls->shader & SHADER_USE_MIS)) - return false; + /* compute pdf */ + float invarea = klight->distant.invarea; + ls->pdf = invarea / (costheta * costheta * costheta); + ls->pdf *= kernel_data.integrator.pdf_lights; + ls->eval_fac = ls->pdf; - if (type == LIGHT_DISTANT) { - /* distant light */ - float radius = klight->distant.radius; + return true; +} - if (radius == 0.0f) - return false; - if (t != FLT_MAX) - return false; +ccl_device bool light_sample_from_intersection(const KernelGlobals *ccl_restrict kg, + const Intersection *ccl_restrict isect, + const float3 ray_P, + const float3 ray_D, + LightSample *ccl_restrict ls) +{ + const int lamp = isect->prim; + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + LightType type = (LightType)klight->type; + ls->type = type; + ls->shader = klight->shader_id; + ls->object = PRIM_NONE; + ls->prim = PRIM_NONE; + ls->lamp = lamp; + /* todo: missing texture coordinates */ + ls->t = isect->t; + ls->P = ray_P + ray_D * ls->t; + ls->D = ray_D; - /* a distant light is infinitely far away, but equivalent to a disk - * shaped light exactly 1 unit away from the current shading point. - * - * radius t^2/cos(theta) - * <----------> t = sqrt(1^2 + tan(theta)^2) - * tan(th) area = radius*radius*pi - * <-----> - * \ | (1 + tan(theta)^2)/cos(theta) - * \ | (1 + tan(acos(cos(theta)))^2)/cos(theta) - * t \th| 1 simplifies to - * \-| 1/(cos(theta)^3) - * \| magic! - * P - */ - - float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); - float costheta = dot(-lightD, D); - float cosangle = klight->distant.cosangle; - - if (costheta < cosangle) - return false; - - ls->P = -D; - ls->Ng = -D; - ls->D = D; - ls->t = FLT_MAX; - - /* compute pdf */ - float invarea = klight->distant.invarea; - ls->pdf = invarea / (costheta * costheta * costheta); - ls->eval_fac = ls->pdf; - } - else if (type == LIGHT_POINT || type == LIGHT_SPOT) { - float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); - - float radius = klight->spot.radius; - - /* sphere light */ - if (radius == 0.0f) - return false; - - if (!ray_aligned_disk_intersect(P, D, t, lightP, radius, &ls->P, &ls->t)) { - return false; - } - - ls->Ng = -D; - ls->D = D; + if (type == LIGHT_POINT || type == LIGHT_SPOT) { + ls->Ng = -ray_D; float invarea = klight->spot.invarea; ls->eval_fac = (0.25f * M_1_PI_F) * invarea; @@ -260,8 +394,9 @@ ccl_device bool lamp_light_eval( ls->eval_fac *= spot_light_attenuation( dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls->Ng); - if (ls->eval_fac == 0.0f) + if (ls->eval_fac == 0.0f) { return false; + } } float2 uv = map_to_sphere(ls->Ng); ls->u = uv.x; @@ -274,31 +409,22 @@ ccl_device bool lamp_light_eval( else if (type == LIGHT_AREA) { /* area light */ float invarea = fabsf(klight->area.invarea); - bool is_round = (klight->area.invarea < 0.0f); - if (invarea == 0.0f) - return false; float3 axisu = make_float3( klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); float3 axisv = make_float3( klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); - - /* one sided */ - if (dot(D, Ng) >= 0.0f) - return false; - float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]); - if (!ray_quad_intersect( - P, D, 0.0f, t, light_P, axisu, axisv, Ng, &ls->P, &ls->t, &ls->u, &ls->v, is_round)) { - return false; - } - - ls->D = D; + ls->u = isect->u; + ls->v = isect->v; + ls->D = ray_D; ls->Ng = Ng; + + const bool is_round = (klight->area.invarea < 0.0f); if (is_round) { - ls->pdf = invarea * lamp_light_pdf(kg, Ng, -D, ls->t); + ls->pdf = invarea * lamp_light_pdf(kg, Ng, -ray_D, ls->t); } else { float3 sample_axisu = axisu; @@ -306,12 +432,12 @@ ccl_device bool lamp_light_eval( if (klight->area.tan_spread > 0.0f) { if (!light_spread_clamp_area_light( - P, Ng, &light_P, &sample_axisu, &sample_axisv, klight->area.tan_spread)) { + ray_P, Ng, &light_P, &sample_axisu, &sample_axisv, klight->area.tan_spread)) { return false; } } - ls->pdf = rect_light_sample(P, &light_P, sample_axisu, sample_axisv, 0, 0, false); + ls->pdf = rect_light_sample(ray_P, &light_P, sample_axisu, sample_axisv, 0, 0, false); } ls->eval_fac = 0.25f * invarea; @@ -325,6 +451,7 @@ ccl_device bool lamp_light_eval( } } else { + kernel_assert(!"Invalid lamp type in light_sample_from_intersection"); return false; } @@ -337,7 +464,7 @@ ccl_device bool lamp_light_eval( /* returns true if the triangle is has motion blur or an instancing transform applied */ ccl_device_inline bool triangle_world_space_vertices( - KernelGlobals *kg, int object, int prim, float time, float3 V[3]) + const KernelGlobals *kg, int object, int prim, float time, float3 V[3]) { bool has_motion = false; const int object_flag = kernel_tex_fetch(__object_flag, object); @@ -365,7 +492,7 @@ ccl_device_inline bool triangle_world_space_vertices( return has_motion; } -ccl_device_inline float triangle_light_pdf_area(KernelGlobals *kg, +ccl_device_inline float triangle_light_pdf_area(const KernelGlobals *kg, const float3 Ng, const float3 I, float t) @@ -379,7 +506,9 @@ ccl_device_inline float triangle_light_pdf_area(KernelGlobals *kg, return t * t * pdf / cos_pi; } -ccl_device_forceinline float triangle_light_pdf(KernelGlobals *kg, ShaderData *sd, float t) +ccl_device_forceinline float triangle_light_pdf(const KernelGlobals *kg, + const ShaderData *sd, + float t) { /* A naive heuristic to decide between costly solid angle sampling * and simple area sampling, comparing the distance to the triangle plane @@ -448,7 +577,8 @@ ccl_device_forceinline float triangle_light_pdf(KernelGlobals *kg, ShaderData *s } } -ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg, +template +ccl_device_forceinline void triangle_light_sample(const KernelGlobals *kg, int prim, int object, float randu, @@ -488,7 +618,7 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg, float distance_to_plane = fabsf(dot(N0, V[0] - P) / dot(N0, N0)); - if (longest_edge_squared > distance_to_plane * distance_to_plane) { + if (!in_volume_segment && (longest_edge_squared > distance_to_plane * distance_to_plane)) { /* see James Arvo, "Stratified Sampling of Spherical Triangles" * http://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf */ @@ -617,7 +747,7 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg, /* Light Distribution */ -ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu) +ccl_device int light_distribution_sample(const KernelGlobals *kg, float *randu) { /* This is basically std::upper_bound as used by PBRT, to find a point light or * triangle to emit from, proportional to area. a good improvement would be to @@ -655,51 +785,93 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu) /* Generic Light */ -ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals *kg, int index, int bounce) +ccl_device_inline bool light_select_reached_max_bounces(const KernelGlobals *kg, + int index, + int bounce) { return (bounce > kernel_tex_fetch(__lights, index).max_bounces); } -ccl_device_noinline bool light_sample(KernelGlobals *kg, - int lamp, - float randu, - float randv, - float time, - float3 P, - int bounce, - LightSample *ls) +template +ccl_device_noinline bool light_distribution_sample(const KernelGlobals *kg, + float randu, + const float randv, + const float time, + const float3 P, + const int bounce, + const int path_flag, + LightSample *ls) { - if (lamp < 0) { - /* sample index */ - int index = light_distribution_sample(kg, &randu); + /* Sample light index from distribution. */ + const int index = light_distribution_sample(kg, &randu); + const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch(__light_distribution, + index); + const int prim = kdistribution->prim; - /* fetch light data */ - const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch( - __light_distribution, index); - int prim = kdistribution->prim; + if (prim >= 0) { + /* Mesh light. */ + const int object = kdistribution->mesh_light.object_id; - if (prim >= 0) { - int object = kdistribution->mesh_light.object_id; - int shader_flag = kdistribution->mesh_light.shader_flag; - - triangle_light_sample(kg, prim, object, randu, randv, time, ls, P); - ls->shader |= shader_flag; - return (ls->pdf > 0.0f); + /* Exclude synthetic meshes from shadow catcher pass. */ + if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) && + !(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) { + return false; } - lamp = -prim - 1; + const int shader_flag = kdistribution->mesh_light.shader_flag; + triangle_light_sample(kg, prim, object, randu, randv, time, ls, P); + ls->shader |= shader_flag; + return (ls->pdf > 0.0f); } + const int lamp = -prim - 1; + if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) { return false; } - return lamp_light_sample(kg, lamp, randu, randv, P, ls); + return light_sample(kg, lamp, randu, randv, P, path_flag, ls); } -ccl_device_inline int light_select_num_samples(KernelGlobals *kg, int index) +ccl_device_inline bool light_distribution_sample_from_volume_segment(const KernelGlobals *kg, + float randu, + const float randv, + const float time, + const float3 P, + const int bounce, + const int path_flag, + LightSample *ls) { - return kernel_tex_fetch(__lights, index).samples; + return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); +} + +ccl_device_inline bool light_distribution_sample_from_position(const KernelGlobals *kg, + float randu, + const float randv, + const float time, + const float3 P, + const int bounce, + const int path_flag, + LightSample *ls) +{ + return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); +} + +ccl_device_inline bool light_distribution_sample_new_position(const KernelGlobals *kg, + const float randu, + const float randv, + const float time, + const float3 P, + LightSample *ls) +{ + /* Sample a new position on the same light, for volume sampling. */ + if (ls->type == LIGHT_TRIANGLE) { + triangle_light_sample(kg, ls->prim, ls->object, randu, randv, time, ls, P); + return (ls->pdf > 0.0f); + } + else { + return light_sample(kg, ls->lamp, randu, randv, P, 0, ls); + } } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_light_background.h b/intern/cycles/kernel/kernel_light_background.h index f0f64ce8704..493ed560bc6 100644 --- a/intern/cycles/kernel/kernel_light_background.h +++ b/intern/cycles/kernel/kernel_light_background.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + #include "kernel_light_common.h" CCL_NAMESPACE_BEGIN @@ -22,7 +24,10 @@ CCL_NAMESPACE_BEGIN #ifdef __BACKGROUND_MIS__ -ccl_device float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float *pdf) +ccl_device float3 background_map_sample(const KernelGlobals *kg, + float randu, + float randv, + float *pdf) { /* for the following, the CDF values are actually a pair of floats, with the * function value as X and the actual CDF as Y. The last entry's function @@ -104,7 +109,7 @@ ccl_device float3 background_map_sample(KernelGlobals *kg, float randu, float ra /* TODO(sergey): Same as above, after the release we should consider using * 'noinline' for all devices. */ -ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction) +ccl_device float background_map_pdf(const KernelGlobals *kg, float3 direction) { float2 uv = direction_to_equirectangular(direction); int res_x = kernel_data.background.map_res_x; @@ -138,7 +143,7 @@ ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction) } ccl_device_inline bool background_portal_data_fetch_and_check_side( - KernelGlobals *kg, float3 P, int index, float3 *lightpos, float3 *dir) + const KernelGlobals *kg, float3 P, int index, float3 *lightpos, float3 *dir) { int portal = kernel_data.background.portal_offset + index; const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); @@ -154,7 +159,7 @@ ccl_device_inline bool background_portal_data_fetch_and_check_side( } ccl_device_inline float background_portal_pdf( - KernelGlobals *kg, float3 P, float3 direction, int ignore_portal, bool *is_possible) + const KernelGlobals *kg, float3 P, float3 direction, int ignore_portal, bool *is_possible) { float portal_pdf = 0.0f; @@ -214,7 +219,7 @@ ccl_device_inline float background_portal_pdf( return (num_possible > 0) ? portal_pdf / num_possible : 0.0f; } -ccl_device int background_num_possible_portals(KernelGlobals *kg, float3 P) +ccl_device int background_num_possible_portals(const KernelGlobals *kg, float3 P) { int num_possible_portals = 0; for (int p = 0; p < kernel_data.background.num_portals; p++) { @@ -225,7 +230,7 @@ ccl_device int background_num_possible_portals(KernelGlobals *kg, float3 P) return num_possible_portals; } -ccl_device float3 background_portal_sample(KernelGlobals *kg, +ccl_device float3 background_portal_sample(const KernelGlobals *kg, float3 P, float randu, float randv, @@ -280,7 +285,7 @@ ccl_device float3 background_portal_sample(KernelGlobals *kg, return zero_float3(); } -ccl_device_inline float3 background_sun_sample(KernelGlobals *kg, +ccl_device_inline float3 background_sun_sample(const KernelGlobals *kg, float randu, float randv, float *pdf) @@ -292,7 +297,7 @@ ccl_device_inline float3 background_sun_sample(KernelGlobals *kg, return D; } -ccl_device_inline float background_sun_pdf(KernelGlobals *kg, float3 D) +ccl_device_inline float background_sun_pdf(const KernelGlobals *kg, float3 D) { const float3 N = float4_to_float3(kernel_data.background.sun); const float angle = kernel_data.background.sun.w; @@ -300,7 +305,7 @@ ccl_device_inline float background_sun_pdf(KernelGlobals *kg, float3 D) } ccl_device_inline float3 -background_light_sample(KernelGlobals *kg, float3 P, float randu, float randv, float *pdf) +background_light_sample(const KernelGlobals *kg, float3 P, float randu, float randv, float *pdf) { float portal_method_pdf = kernel_data.background.portal_weight; float sun_method_pdf = kernel_data.background.sun_weight; @@ -400,7 +405,7 @@ background_light_sample(KernelGlobals *kg, float3 P, float randu, float randv, f return D; } -ccl_device float background_light_pdf(KernelGlobals *kg, float3 P, float3 direction) +ccl_device float background_light_pdf(const KernelGlobals *kg, float3 P, float3 direction) { float portal_method_pdf = kernel_data.background.portal_weight; float sun_method_pdf = kernel_data.background.sun_weight; diff --git a/intern/cycles/kernel/kernel_light_common.h b/intern/cycles/kernel/kernel_light_common.h index 4a683d36226..765d8f5338e 100644 --- a/intern/cycles/kernel/kernel_light_common.h +++ b/intern/cycles/kernel/kernel_light_common.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +#include "kernel_montecarlo.h" + CCL_NAMESPACE_BEGIN /* Area light sampling */ @@ -210,7 +214,7 @@ ccl_device bool light_spread_clamp_area_light(const float3 P, return true; } -ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3 I, float t) +ccl_device float lamp_light_pdf(const KernelGlobals *kg, const float3 Ng, const float3 I, float t) { float cos_pi = dot(Ng, I); diff --git a/intern/cycles/kernel/kernel_lookup_table.h b/intern/cycles/kernel/kernel_lookup_table.h new file mode 100644 index 00000000000..33d9d5ae1f0 --- /dev/null +++ b/intern/cycles/kernel/kernel_lookup_table.h @@ -0,0 +1,56 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Interpolated lookup table access */ + +ccl_device float lookup_table_read(const KernelGlobals *kg, float x, int offset, int size) +{ + x = saturate(x) * (size - 1); + + int index = min(float_to_int(x), size - 1); + int nindex = min(index + 1, size - 1); + float t = x - index; + + float data0 = kernel_tex_fetch(__lookup_table, index + offset); + if (t == 0.0f) + return data0; + + float data1 = kernel_tex_fetch(__lookup_table, nindex + offset); + return (1.0f - t) * data0 + t * data1; +} + +ccl_device float lookup_table_read_2D( + const KernelGlobals *kg, float x, float y, int offset, int xsize, int ysize) +{ + y = saturate(y) * (ysize - 1); + + int index = min(float_to_int(y), ysize - 1); + int nindex = min(index + 1, ysize - 1); + float t = y - index; + + float data0 = lookup_table_read(kg, x, offset + xsize * index, xsize); + if (t == 0.0f) + return data0; + + float data1 = lookup_table_read(kg, x, offset + xsize * nindex, xsize); + return (1.0f - t) * data0 + t * data1; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_math.h b/intern/cycles/kernel/kernel_math.h index 96391db7649..3c5ab95bbc8 100644 --- a/intern/cycles/kernel/kernel_math.h +++ b/intern/cycles/kernel/kernel_math.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __KERNEL_MATH_H__ -#define __KERNEL_MATH_H__ +#pragma once #include "util/util_color.h" #include "util/util_math.h" @@ -24,5 +23,3 @@ #include "util/util_projection.h" #include "util/util_texture.h" #include "util/util_transform.h" - -#endif /* __KERNEL_MATH_H__ */ diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/kernel_montecarlo.h index ce37bd0b15e..b158f4c4fd3 100644 --- a/intern/cycles/kernel/kernel_montecarlo.h +++ b/intern/cycles/kernel/kernel_montecarlo.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __KERNEL_MONTECARLO_CL__ -#define __KERNEL_MONTECARLO_CL__ +#pragma once CCL_NAMESPACE_BEGIN @@ -300,5 +299,3 @@ ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N) } CCL_NAMESPACE_END - -#endif /* __KERNEL_MONTECARLO_CL__ */ diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h index 8f58b8c3079..67466b28170 100644 --- a/intern/cycles/kernel/kernel_passes.h +++ b/intern/cycles/kernel/kernel_passes.h @@ -14,61 +14,52 @@ * limitations under the License. */ +#pragma once + +#include "kernel/geom/geom.h" + #include "kernel/kernel_id_passes.h" +#include "kernel/kernel_write_passes.h" CCL_NAMESPACE_BEGIN -#ifdef __DENOISING_FEATURES__ - -ccl_device_inline void kernel_write_denoising_shadow(KernelGlobals *kg, - ccl_global float *buffer, - int sample, - float path_total, - float path_total_shaded) +/* Get pointer to pixel in render buffer. */ +ccl_device_forceinline ccl_global float *kernel_pass_pixel_render_buffer( + INTEGRATOR_STATE_CONST_ARGS, ccl_global float *ccl_restrict render_buffer) { - if (kernel_data.film.pass_denoising_data == 0) - return; - - buffer += sample_is_even(kernel_data.integrator.sampling_pattern, sample) ? - DENOISING_PASS_SHADOW_B : - DENOISING_PASS_SHADOW_A; - - path_total = ensure_finite(path_total); - path_total_shaded = ensure_finite(path_total_shaded); - - kernel_write_pass_float(buffer, path_total); - kernel_write_pass_float(buffer + 1, path_total_shaded); - - float value = path_total_shaded / max(path_total, 1e-7f); - kernel_write_pass_float(buffer + 2, value * value); + const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + return render_buffer + render_buffer_offset; } -ccl_device_inline void kernel_update_denoising_features(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - PathRadiance *L) +#ifdef __DENOISING_FEATURES__ + +ccl_device_forceinline void kernel_write_denoising_features_surface( + INTEGRATOR_STATE_ARGS, const ShaderData *sd, ccl_global float *ccl_restrict render_buffer) { - if (state->denoising_feature_weight == 0.0f) { + if (!(INTEGRATOR_STATE(path, flag) & PATH_RAY_DENOISING_FEATURES)) { return; } - L->denoising_depth += ensure_finite(state->denoising_feature_weight * sd->ray_length); - /* Skip implicitly transparent surfaces. */ if (sd->flag & SD_HAS_ONLY_VOLUME) { return; } + ccl_global float *buffer = kernel_pass_pixel_render_buffer(INTEGRATOR_STATE_PASS, render_buffer); + float3 normal = zero_float3(); float3 diffuse_albedo = zero_float3(); float3 specular_albedo = zero_float3(); float sum_weight = 0.0f, sum_nonspecular_weight = 0.0f; for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + const ShaderClosure *sc = &sd->closure[i]; - if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) + if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { continue; + } /* All closures contribute to the normal feature, but only diffuse-like ones to the albedo. */ normal += sc->N * sc->sample_weight; @@ -106,140 +97,208 @@ ccl_device_inline void kernel_update_denoising_features(KernelGlobals *kg, normal /= sum_weight; } - /* Transform normal into camera space. */ - const Transform worldtocamera = kernel_data.cam.worldtocamera; - normal = transform_direction(&worldtocamera, normal); + if (kernel_data.film.pass_denoising_normal != PASS_UNUSED) { + /* Transform normal into camera space. */ + const Transform worldtocamera = kernel_data.cam.worldtocamera; + normal = transform_direction(&worldtocamera, normal); - L->denoising_normal += ensure_finite3(state->denoising_feature_weight * normal); - L->denoising_albedo += ensure_finite3(state->denoising_feature_weight * - state->denoising_feature_throughput * diffuse_albedo); + const float3 denoising_normal = ensure_finite3(normal); + kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_normal, denoising_normal); + } - state->denoising_feature_weight = 0.0f; + if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { + const float3 denoising_feature_throughput = INTEGRATOR_STATE(path, + denoising_feature_throughput); + const float3 denoising_albedo = ensure_finite3(denoising_feature_throughput * + diffuse_albedo); + kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); + } + + INTEGRATOR_STATE_WRITE(path, flag) &= ~PATH_RAY_DENOISING_FEATURES; } else { - state->denoising_feature_throughput *= specular_albedo; + INTEGRATOR_STATE_WRITE(path, denoising_feature_throughput) *= specular_albedo; + } +} + +ccl_device_forceinline void kernel_write_denoising_features_volume(INTEGRATOR_STATE_ARGS, + const float3 albedo, + const bool scatter, + ccl_global float *ccl_restrict + render_buffer) +{ + ccl_global float *buffer = kernel_pass_pixel_render_buffer(INTEGRATOR_STATE_PASS, render_buffer); + const float3 denoising_feature_throughput = INTEGRATOR_STATE(path, denoising_feature_throughput); + + if (scatter && kernel_data.film.pass_denoising_normal != PASS_UNUSED) { + /* Assume scatter is sufficiently diffuse to stop writing denoising features. */ + INTEGRATOR_STATE_WRITE(path, flag) &= ~PATH_RAY_DENOISING_FEATURES; + + /* Write view direction as normal. */ + const float3 denoising_normal = make_float3(0.0f, 0.0f, -1.0f); + kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_normal, denoising_normal); + } + + if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { + /* Write albedo. */ + const float3 denoising_albedo = ensure_finite3(denoising_feature_throughput * albedo); + kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); } } #endif /* __DENOISING_FEATURES__ */ -#ifdef __KERNEL_CPU__ -# define WRITE_ID_SLOT(buffer, depth, id, matte_weight, name) \ - kernel_write_id_pass_cpu(buffer, depth * 2, id, matte_weight, kg->coverage_##name) -ccl_device_inline size_t kernel_write_id_pass_cpu( - float *buffer, size_t depth, float id, float matte_weight, CoverageMap *map) +#ifdef __SHADOW_CATCHER__ + +/* Write shadow catcher passes on a bounce from the shadow catcher object. */ +ccl_device_forceinline void kernel_write_shadow_catcher_bounce_data( + INTEGRATOR_STATE_ARGS, const ShaderData *sd, ccl_global float *ccl_restrict render_buffer) { - if (map) { - (*map)[id] += matte_weight; - return 0; + if (!kernel_data.integrator.has_shadow_catcher) { + return; } -#else /* __KERNEL_CPU__ */ -# define WRITE_ID_SLOT(buffer, depth, id, matte_weight, name) \ - kernel_write_id_slots_gpu(buffer, depth * 2, id, matte_weight) -ccl_device_inline size_t kernel_write_id_slots_gpu(ccl_global float *buffer, - size_t depth, - float id, - float matte_weight) -{ -#endif /* __KERNEL_CPU__ */ - kernel_write_id_slots(buffer, depth, id, matte_weight); - return depth * 2; + + kernel_assert(kernel_data.film.pass_shadow_catcher_sample_count != PASS_UNUSED); + kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); + + if (!kernel_shadow_catcher_is_path_split_bounce(INTEGRATOR_STATE_PASS, sd->object_flag)) { + return; + } + + ccl_global float *buffer = kernel_pass_pixel_render_buffer(INTEGRATOR_STATE_PASS, render_buffer); + + /* Count sample for the shadow catcher object. */ + kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_sample_count, 1.0f); + + /* Since the split is done, the sample does not contribute to the matte, so accumulate it as + * transparency to the matte. */ + const float3 throughput = INTEGRATOR_STATE(path, throughput); + kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, + average(throughput)); } -ccl_device_inline void kernel_write_data_passes(KernelGlobals *kg, - ccl_global float *buffer, - PathRadiance *L, - ShaderData *sd, - ccl_addr_space PathState *state, - float3 throughput) +#endif /* __SHADOW_CATCHER__ */ + +ccl_device_inline size_t kernel_write_id_pass(float *ccl_restrict buffer, + size_t depth, + float id, + float matte_weight) +{ + kernel_write_id_slots(buffer, depth * 2, id, matte_weight); + return depth * 4; +} + +ccl_device_inline void kernel_write_data_passes(INTEGRATOR_STATE_ARGS, + const ShaderData *sd, + ccl_global float *ccl_restrict render_buffer) { #ifdef __PASSES__ - int path_flag = state->flag; + const int path_flag = INTEGRATOR_STATE(path, flag); - if (!(path_flag & PATH_RAY_CAMERA)) + if (!(path_flag & PATH_RAY_CAMERA)) { return; + } - int flag = kernel_data.film.pass_flag; - int light_flag = kernel_data.film.light_pass_flag; + const int flag = kernel_data.film.pass_flag; - if (!((flag | light_flag) & PASS_ANY)) + if (!(flag & PASS_ANY)) { return; + } + + ccl_global float *buffer = kernel_pass_pixel_render_buffer(INTEGRATOR_STATE_PASS, render_buffer); if (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)) { if (!(sd->flag & SD_TRANSPARENT) || kernel_data.film.pass_alpha_threshold == 0.0f || average(shader_bsdf_alpha(kg, sd)) >= kernel_data.film.pass_alpha_threshold) { - if (state->sample == 0) { + if (INTEGRATOR_STATE(path, sample) == 0) { if (flag & PASSMASK(DEPTH)) { - float depth = camera_z_depth(kg, sd->P); + const float depth = camera_z_depth(kg, sd->P); kernel_write_pass_float(buffer + kernel_data.film.pass_depth, depth); } if (flag & PASSMASK(OBJECT_ID)) { - float id = object_pass_id(kg, sd->object); + const float id = object_pass_id(kg, sd->object); kernel_write_pass_float(buffer + kernel_data.film.pass_object_id, id); } if (flag & PASSMASK(MATERIAL_ID)) { - float id = shader_pass_id(kg, sd); + const float id = shader_pass_id(kg, sd); kernel_write_pass_float(buffer + kernel_data.film.pass_material_id, id); } } + if (flag & PASSMASK(POSITION)) { + const float3 position = sd->P; + kernel_write_pass_float3(buffer + kernel_data.film.pass_position, position); + } if (flag & PASSMASK(NORMAL)) { - float3 normal = shader_bsdf_average_normal(kg, sd); + const float3 normal = shader_bsdf_average_normal(kg, sd); kernel_write_pass_float3(buffer + kernel_data.film.pass_normal, normal); } + if (flag & PASSMASK(ROUGHNESS)) { + const float roughness = shader_bsdf_average_roughness(sd); + kernel_write_pass_float(buffer + kernel_data.film.pass_roughness, roughness); + } if (flag & PASSMASK(UV)) { - float3 uv = primitive_uv(kg, sd); + const float3 uv = primitive_uv(kg, sd); kernel_write_pass_float3(buffer + kernel_data.film.pass_uv, uv); } if (flag & PASSMASK(MOTION)) { - float4 speed = primitive_motion_vector(kg, sd); + const float4 speed = primitive_motion_vector(kg, sd); kernel_write_pass_float4(buffer + kernel_data.film.pass_motion, speed); kernel_write_pass_float(buffer + kernel_data.film.pass_motion_weight, 1.0f); } - state->flag |= PATH_RAY_SINGLE_PASS_DONE; + INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_SINGLE_PASS_DONE; } } if (kernel_data.film.cryptomatte_passes) { + const float3 throughput = INTEGRATOR_STATE(path, throughput); const float matte_weight = average(throughput) * (1.0f - average(shader_bsdf_transparency(kg, sd))); if (matte_weight > 0.0f) { ccl_global float *cryptomatte_buffer = buffer + kernel_data.film.pass_cryptomatte; if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { - float id = object_cryptomatte_id(kg, sd->object); - cryptomatte_buffer += WRITE_ID_SLOT( - cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight, object); + const float id = object_cryptomatte_id(kg, sd->object); + cryptomatte_buffer += kernel_write_id_pass( + cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); } if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { - float id = shader_cryptomatte_id(kg, sd->shader); - cryptomatte_buffer += WRITE_ID_SLOT( - cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight, material); + const float id = shader_cryptomatte_id(kg, sd->shader); + cryptomatte_buffer += kernel_write_id_pass( + cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); } if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { - float id = object_cryptomatte_asset_id(kg, sd->object); - cryptomatte_buffer += WRITE_ID_SLOT( - cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight, asset); + const float id = object_cryptomatte_asset_id(kg, sd->object); + cryptomatte_buffer += kernel_write_id_pass( + cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); } } } - if (light_flag & PASSMASK_COMPONENT(DIFFUSE)) - L->color_diffuse += shader_bsdf_diffuse(kg, sd) * throughput; - if (light_flag & PASSMASK_COMPONENT(GLOSSY)) - L->color_glossy += shader_bsdf_glossy(kg, sd) * throughput; - if (light_flag & PASSMASK_COMPONENT(TRANSMISSION)) - L->color_transmission += shader_bsdf_transmission(kg, sd) * throughput; + if (flag & PASSMASK(DIFFUSE_COLOR)) { + const float3 throughput = INTEGRATOR_STATE(path, throughput); + kernel_write_pass_float3(buffer + kernel_data.film.pass_diffuse_color, + shader_bsdf_diffuse(kg, sd) * throughput); + } + if (flag & PASSMASK(GLOSSY_COLOR)) { + const float3 throughput = INTEGRATOR_STATE(path, throughput); + kernel_write_pass_float3(buffer + kernel_data.film.pass_glossy_color, + shader_bsdf_glossy(kg, sd) * throughput); + } + if (flag & PASSMASK(TRANSMISSION_COLOR)) { + const float3 throughput = INTEGRATOR_STATE(path, throughput); + kernel_write_pass_float3(buffer + kernel_data.film.pass_transmission_color, + shader_bsdf_transmission(kg, sd) * throughput); + } + if (flag & PASSMASK(MIST)) { + /* Bring depth into 0..1 range. */ + const float mist_start = kernel_data.film.mist_start; + const float mist_inv_depth = kernel_data.film.mist_inv_depth; - if (light_flag & PASSMASK(MIST)) { - /* bring depth into 0..1 range */ - float mist_start = kernel_data.film.mist_start; - float mist_inv_depth = kernel_data.film.mist_inv_depth; - - float depth = camera_distance(kg, sd->P); + const float depth = camera_distance(kg, sd->P); float mist = saturate((depth - mist_start) * mist_inv_depth); - /* falloff */ - float mist_falloff = kernel_data.film.mist_falloff; + /* Falloff */ + const float mist_falloff = kernel_data.film.mist_falloff; if (mist_falloff == 1.0f) ; @@ -250,158 +309,17 @@ ccl_device_inline void kernel_write_data_passes(KernelGlobals *kg, else mist = powf(mist, mist_falloff); - /* modulate by transparency */ - float3 alpha = shader_bsdf_alpha(kg, sd); - L->mist += (1.0f - mist) * average(throughput * alpha); + /* Modulate by transparency */ + const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 alpha = shader_bsdf_alpha(kg, sd); + const float mist_output = (1.0f - mist) * average(throughput * alpha); + + /* Note that the final value in the render buffer we want is 1 - mist_output, + * to avoid having to tracking this in the Integrator state we do the negation + * after rendering. */ + kernel_write_pass_float(buffer + kernel_data.film.pass_mist, mist_output); } #endif } -ccl_device_inline void kernel_write_light_passes(KernelGlobals *kg, - ccl_global float *buffer, - PathRadiance *L) -{ -#ifdef __PASSES__ - int light_flag = kernel_data.film.light_pass_flag; - - if (!kernel_data.film.use_light_pass) - return; - - if (light_flag & PASSMASK(DIFFUSE_INDIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_diffuse_indirect, L->indirect_diffuse); - if (light_flag & PASSMASK(GLOSSY_INDIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_glossy_indirect, L->indirect_glossy); - if (light_flag & PASSMASK(TRANSMISSION_INDIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_transmission_indirect, - L->indirect_transmission); - if (light_flag & PASSMASK(VOLUME_INDIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_volume_indirect, L->indirect_volume); - if (light_flag & PASSMASK(DIFFUSE_DIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_diffuse_direct, L->direct_diffuse); - if (light_flag & PASSMASK(GLOSSY_DIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_glossy_direct, L->direct_glossy); - if (light_flag & PASSMASK(TRANSMISSION_DIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_transmission_direct, - L->direct_transmission); - if (light_flag & PASSMASK(VOLUME_DIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_volume_direct, L->direct_volume); - - if (light_flag & PASSMASK(EMISSION)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_emission, L->emission); - if (light_flag & PASSMASK(BACKGROUND)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_background, L->background); - if (light_flag & PASSMASK(AO)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_ao, L->ao); - - if (light_flag & PASSMASK(DIFFUSE_COLOR)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_diffuse_color, L->color_diffuse); - if (light_flag & PASSMASK(GLOSSY_COLOR)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_glossy_color, L->color_glossy); - if (light_flag & PASSMASK(TRANSMISSION_COLOR)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_transmission_color, - L->color_transmission); - if (light_flag & PASSMASK(SHADOW)) { - float3 shadow = L->shadow; - kernel_write_pass_float4( - buffer + kernel_data.film.pass_shadow, - make_float4(shadow.x, shadow.y, shadow.z, kernel_data.film.pass_shadow_scale)); - } - if (light_flag & PASSMASK(MIST)) - kernel_write_pass_float(buffer + kernel_data.film.pass_mist, 1.0f - L->mist); -#endif -} - -ccl_device_inline void kernel_write_result(KernelGlobals *kg, - ccl_global float *buffer, - int sample, - PathRadiance *L) -{ - PROFILING_INIT(kg, PROFILING_WRITE_RESULT); - PROFILING_OBJECT(PRIM_NONE); - - float alpha; - float3 L_sum = path_radiance_clamp_and_sum(kg, L, &alpha); - - if (kernel_data.film.pass_flag & PASSMASK(COMBINED)) { - kernel_write_pass_float4(buffer, make_float4(L_sum.x, L_sum.y, L_sum.z, alpha)); - } - - kernel_write_light_passes(kg, buffer, L); - -#ifdef __DENOISING_FEATURES__ - if (kernel_data.film.pass_denoising_data) { -# ifdef __SHADOW_TRICKS__ - kernel_write_denoising_shadow(kg, - buffer + kernel_data.film.pass_denoising_data, - sample, - average(L->path_total), - average(L->path_total_shaded)); -# else - kernel_write_denoising_shadow( - kg, buffer + kernel_data.film.pass_denoising_data, sample, 0.0f, 0.0f); -# endif - if (kernel_data.film.pass_denoising_clean) { - float3 noisy, clean; - path_radiance_split_denoising(kg, L, &noisy, &clean); - kernel_write_pass_float3_variance( - buffer + kernel_data.film.pass_denoising_data + DENOISING_PASS_COLOR, noisy); - kernel_write_pass_float3_unaligned(buffer + kernel_data.film.pass_denoising_clean, clean); - } - else { - kernel_write_pass_float3_variance(buffer + kernel_data.film.pass_denoising_data + - DENOISING_PASS_COLOR, - ensure_finite3(L_sum)); - } - - kernel_write_pass_float3_variance(buffer + kernel_data.film.pass_denoising_data + - DENOISING_PASS_NORMAL, - L->denoising_normal); - kernel_write_pass_float3_variance(buffer + kernel_data.film.pass_denoising_data + - DENOISING_PASS_ALBEDO, - L->denoising_albedo); - kernel_write_pass_float_variance( - buffer + kernel_data.film.pass_denoising_data + DENOISING_PASS_DEPTH, L->denoising_depth); - } -#endif /* __DENOISING_FEATURES__ */ - - /* Adaptive Sampling. Fill the additional buffer with the odd samples and calculate our stopping - criteria. This is the heuristic from "A hierarchical automatic stopping condition for Monte - Carlo global illumination" except that here it is applied per pixel and not in hierarchical - tiles. */ - if (kernel_data.film.pass_adaptive_aux_buffer && - kernel_data.integrator.adaptive_threshold > 0.0f) { - if (sample_is_even(kernel_data.integrator.sampling_pattern, sample)) { - kernel_write_pass_float4(buffer + kernel_data.film.pass_adaptive_aux_buffer, - make_float4(L_sum.x * 2.0f, L_sum.y * 2.0f, L_sum.z * 2.0f, 0.0f)); - } -#ifdef __KERNEL_CPU__ - if ((sample > kernel_data.integrator.adaptive_min_samples) && - kernel_data.integrator.adaptive_stop_per_sample) { - const int step = kernel_data.integrator.adaptive_step; - - if ((sample & (step - 1)) == (step - 1)) { - kernel_do_adaptive_stopping(kg, buffer, sample); - } - } -#endif - } - - /* Write the sample count as negative numbers initially to mark the samples as in progress. - * Once the tile has finished rendering, the sign gets flipped and all the pixel values - * are scaled as if they were taken at a uniform sample count. */ - if (kernel_data.film.pass_sample_count) { - /* Make sure it's a negative number. In progressive refine mode, this bit gets flipped between - * passes. */ -#ifdef __ATOMIC_PASS_WRITE__ - atomic_fetch_and_or_uint32((ccl_global uint *)(buffer + kernel_data.film.pass_sample_count), - 0x80000000); -#else - if (buffer[kernel_data.film.pass_sample_count] > 0) { - buffer[kernel_data.film.pass_sample_count] *= -1.0f; - } -#endif - kernel_write_pass_float(buffer + kernel_data.film.pass_sample_count, -1.0f); - } -} - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h deleted file mode 100644 index 92a097de9e1..00000000000 --- a/intern/cycles/kernel/kernel_path.h +++ /dev/null @@ -1,709 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef __OSL__ -# include "kernel/osl/osl_shader.h" -#endif - -// clang-format off -#include "kernel/kernel_random.h" -#include "kernel/kernel_projection.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_differential.h" -#include "kernel/kernel_camera.h" - -#include "kernel/geom/geom.h" -#include "kernel/bvh/bvh.h" - -#include "kernel/kernel_write_passes.h" -#include "kernel/kernel_accumulate.h" -#include "kernel/kernel_shader.h" -#include "kernel/kernel_light.h" -#include "kernel/kernel_adaptive_sampling.h" -#include "kernel/kernel_passes.h" - -#if defined(__VOLUME__) || defined(__SUBSURFACE__) -# include "kernel/kernel_volume.h" -#endif - -#ifdef __SUBSURFACE__ -# include "kernel/kernel_subsurface.h" -#endif - -#include "kernel/kernel_path_state.h" -#include "kernel/kernel_shadow.h" -#include "kernel/kernel_emission.h" -#include "kernel/kernel_path_common.h" -#include "kernel/kernel_path_surface.h" -#include "kernel/kernel_path_volume.h" -#include "kernel/kernel_path_subsurface.h" -// clang-format on - -CCL_NAMESPACE_BEGIN - -ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - Intersection *isect, - PathRadiance *L, - const int last_object) -{ - PROFILING_INIT(kg, PROFILING_SCENE_INTERSECT); - - uint visibility = path_state_ray_visibility(kg, state); - - if (path_state_ao_bounce(kg, state)) { - ray->t = kernel_data.background.ao_distance; - if (last_object != OBJECT_NONE) { - const float object_ao_distance = kernel_tex_fetch(__objects, last_object).ao_distance; - if (object_ao_distance != 0.0f) { - ray->t = object_ao_distance; - } - } - } - - bool hit = scene_intersect(kg, ray, visibility, isect); - - return hit; -} - -ccl_device_forceinline void kernel_path_lamp_emission(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - float3 throughput, - ccl_addr_space Intersection *isect, - ShaderData *emission_sd, - PathRadiance *L) -{ - PROFILING_INIT(kg, PROFILING_INDIRECT_EMISSION); - -#ifdef __LAMP_MIS__ - if (kernel_data.integrator.use_lamp_mis && !(state->flag & PATH_RAY_CAMERA)) { - /* ray starting from previous non-transparent bounce */ - Ray light_ray ccl_optional_struct_init; - - light_ray.P = ray->P - state->ray_t * ray->D; - state->ray_t += isect->t; - light_ray.D = ray->D; - light_ray.t = state->ray_t; - light_ray.time = ray->time; - light_ray.dD = ray->dD; - light_ray.dP = ray->dP; - - /* intersect with lamp */ - indirect_lamp_emission(kg, emission_sd, state, L, &light_ray, throughput); - } -#endif /* __LAMP_MIS__ */ -} - -ccl_device_forceinline void kernel_path_background(KernelGlobals *kg, - ccl_addr_space PathState *state, - ccl_addr_space Ray *ray, - float3 throughput, - ShaderData *sd, - ccl_global float *buffer, - PathRadiance *L) -{ - /* eval background shader if nothing hit */ - if (kernel_data.background.transparent && (state->flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { - L->transparent += average(throughput); - -#ifdef __PASSES__ - if (!(kernel_data.film.light_pass_flag & PASSMASK(BACKGROUND))) -#endif /* __PASSES__ */ - return; - } - - /* When using the ao bounces approximation, adjust background - * shader intensity with ao factor. */ - if (path_state_ao_bounce(kg, state)) { - throughput *= kernel_data.background.ao_bounces_factor; - } - -#ifdef __BACKGROUND__ - /* sample background shader */ - float3 L_background = indirect_background(kg, sd, state, buffer, ray); - path_radiance_accum_background(kg, L, state, throughput, L_background); -#endif /* __BACKGROUND__ */ -} - -#ifndef __SPLIT_KERNEL__ - -# ifdef __VOLUME__ -ccl_device_forceinline VolumeIntegrateResult kernel_path_volume(KernelGlobals *kg, - ShaderData *sd, - PathState *state, - Ray *ray, - float3 *throughput, - ccl_addr_space Intersection *isect, - bool hit, - ShaderData *emission_sd, - PathRadiance *L) -{ - PROFILING_INIT(kg, PROFILING_VOLUME); - - /* Sanitize volume stack. */ - if (!hit) { - kernel_volume_clean_stack(kg, state->volume_stack); - } - - if (state->volume_stack[0].shader == SHADER_NONE) { - return VOLUME_PATH_ATTENUATED; - } - - /* volume attenuation, emission, scatter */ - Ray volume_ray = *ray; - volume_ray.t = (hit) ? isect->t : FLT_MAX; - - float step_size = volume_stack_step_size(kg, state->volume_stack); - -# ifdef __VOLUME_DECOUPLED__ - int sampling_method = volume_stack_sampling_method(kg, state->volume_stack); - bool direct = (state->flag & PATH_RAY_CAMERA) != 0; - bool decoupled = kernel_volume_use_decoupled(kg, step_size, direct, sampling_method); - - if (decoupled) { - /* cache steps along volume for repeated sampling */ - VolumeSegment volume_segment; - - shader_setup_from_volume(kg, sd, &volume_ray); - kernel_volume_decoupled_record(kg, state, &volume_ray, sd, &volume_segment, step_size); - - volume_segment.sampling_method = sampling_method; - - /* emission */ - if (volume_segment.closure_flag & SD_EMISSION) - path_radiance_accum_emission(kg, L, state, *throughput, volume_segment.accum_emission); - - /* scattering */ - VolumeIntegrateResult result = VOLUME_PATH_ATTENUATED; - - if (volume_segment.closure_flag & SD_SCATTER) { - int all = kernel_data.integrator.sample_all_lights_indirect; - - /* direct light sampling */ - kernel_branched_path_volume_connect_light( - kg, sd, emission_sd, *throughput, state, L, all, &volume_ray, &volume_segment); - - /* indirect sample. if we use distance sampling and take just - * one sample for direct and indirect light, we could share - * this computation, but makes code a bit complex */ - float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL); - float rscatter = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE); - - result = kernel_volume_decoupled_scatter( - kg, state, &volume_ray, sd, throughput, rphase, rscatter, &volume_segment, NULL, true); - } - - /* free cached steps */ - kernel_volume_decoupled_free(kg, &volume_segment); - - if (result == VOLUME_PATH_SCATTERED) { - if (kernel_path_volume_bounce(kg, sd, throughput, state, &L->state, ray)) - return VOLUME_PATH_SCATTERED; - else - return VOLUME_PATH_MISSED; - } - else { - *throughput *= volume_segment.accum_transmittance; - } - } - else -# endif /* __VOLUME_DECOUPLED__ */ - { - /* integrate along volume segment with distance sampling */ - VolumeIntegrateResult result = kernel_volume_integrate( - kg, state, sd, &volume_ray, L, throughput, step_size); - -# ifdef __VOLUME_SCATTER__ - if (result == VOLUME_PATH_SCATTERED) { - /* direct lighting */ - kernel_path_volume_connect_light(kg, sd, emission_sd, *throughput, state, L); - - /* indirect light bounce */ - if (kernel_path_volume_bounce(kg, sd, throughput, state, &L->state, ray)) - return VOLUME_PATH_SCATTERED; - else - return VOLUME_PATH_MISSED; - } -# endif /* __VOLUME_SCATTER__ */ - } - - return VOLUME_PATH_ATTENUATED; -} -# endif /* __VOLUME__ */ - -#endif /* __SPLIT_KERNEL__ */ - -ccl_device_forceinline bool kernel_path_shader_apply(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - ccl_addr_space Ray *ray, - float3 throughput, - ShaderData *emission_sd, - PathRadiance *L, - ccl_global float *buffer) -{ - PROFILING_INIT(kg, PROFILING_SHADER_APPLY); - -#ifdef __SHADOW_TRICKS__ - if (sd->object_flag & SD_OBJECT_SHADOW_CATCHER) { - if (state->flag & PATH_RAY_TRANSPARENT_BACKGROUND) { - state->flag |= (PATH_RAY_SHADOW_CATCHER | PATH_RAY_STORE_SHADOW_INFO); - - float3 bg = zero_float3(); - if (!kernel_data.background.transparent) { - bg = indirect_background(kg, emission_sd, state, NULL, ray); - } - path_radiance_accum_shadowcatcher(L, throughput, bg); - } - } - else if (state->flag & PATH_RAY_SHADOW_CATCHER) { - /* Only update transparency after shadow catcher bounce. */ - L->shadow_transparency *= average(shader_bsdf_transparency(kg, sd)); - } -#endif /* __SHADOW_TRICKS__ */ - - /* holdout */ -#ifdef __HOLDOUT__ - if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) && - (state->flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { - const float3 holdout_weight = shader_holdout_apply(kg, sd); - if (kernel_data.background.transparent) { - L->transparent += average(holdout_weight * throughput); - } - if (isequal_float3(holdout_weight, one_float3())) { - return false; - } - } -#endif /* __HOLDOUT__ */ - - /* holdout mask objects do not write data passes */ - kernel_write_data_passes(kg, buffer, L, sd, state, throughput); - - /* blurring of bsdf after bounces, for rays that have a small likelihood - * of following this particular path (diffuse, rough glossy) */ - if (kernel_data.integrator.filter_glossy != FLT_MAX) { - float blur_pdf = kernel_data.integrator.filter_glossy * state->min_ray_pdf; - - if (blur_pdf < 1.0f) { - float blur_roughness = sqrtf(1.0f - blur_pdf) * 0.5f; - shader_bsdf_blur(kg, sd, blur_roughness); - } - } - -#ifdef __EMISSION__ - /* emission */ - if (sd->flag & SD_EMISSION) { - float3 emission = indirect_primitive_emission( - kg, sd, sd->ray_length, state->flag, state->ray_pdf); - path_radiance_accum_emission(kg, L, state, throughput, emission); - } -#endif /* __EMISSION__ */ - - return true; -} - -#ifdef __KERNEL_OPTIX__ -ccl_device_inline /* inline trace calls */ -#else -ccl_device_noinline -#endif - void - kernel_path_ao(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - float3 ao_alpha) -{ - PROFILING_INIT(kg, PROFILING_AO); - - /* todo: solve correlation */ - float bsdf_u, bsdf_v; - - path_state_rng_2D(kg, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - - float ao_factor = kernel_data.background.ao_factor; - float3 ao_N; - float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N); - float3 ao_D; - float ao_pdf; - - sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); - - if (dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f) { - Ray light_ray; - float3 ao_shadow; - - light_ray.P = ray_offset(sd->P, sd->Ng); - light_ray.D = ao_D; - light_ray.t = kernel_data.background.ao_distance; - light_ray.time = sd->time; - light_ray.dP = sd->dP; - light_ray.dD = differential3_zero(); - - if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &ao_shadow)) { - path_radiance_accum_ao(kg, L, state, throughput, ao_alpha, ao_bsdf, ao_shadow); - } - else { - path_radiance_accum_total_ao(L, state, throughput, ao_bsdf); - } - } -} - -#ifndef __SPLIT_KERNEL__ - -# if defined(__BRANCHED_PATH__) || defined(__BAKING__) - -ccl_device void kernel_path_indirect(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - Ray *ray, - float3 throughput, - PathState *state, - PathRadiance *L, - const int last_object) -{ -# ifdef __SUBSURFACE__ - SubsurfaceIndirectRays ss_indirect; - kernel_path_subsurface_init_indirect(&ss_indirect); - - for (;;) { -# endif /* __SUBSURFACE__ */ - - /* path iteration */ - for (;;) { - /* Find intersection with objects in scene. */ - Intersection isect; - bool hit = kernel_path_scene_intersect(kg, state, ray, &isect, L, last_object); - - /* Find intersection with lamps and compute emission for MIS. */ - kernel_path_lamp_emission(kg, state, ray, throughput, &isect, sd, L); - -# ifdef __VOLUME__ - /* Volume integration. */ - VolumeIntegrateResult result = kernel_path_volume( - kg, sd, state, ray, &throughput, &isect, hit, emission_sd, L); - - if (result == VOLUME_PATH_SCATTERED) { - continue; - } - else if (result == VOLUME_PATH_MISSED) { - break; - } -# endif /* __VOLUME__*/ - - /* Shade background. */ - if (!hit) { - kernel_path_background(kg, state, ray, throughput, sd, NULL, L); - break; - } - else if (path_state_ao_bounce(kg, state)) { - if (intersection_get_shader_flags(kg, &isect) & - (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; - } - else { - break; - } - } - - /* Setup shader data. */ - shader_setup_from_ray(kg, sd, &isect, ray); - - /* Skip most work for volume bounding surface. */ -# ifdef __VOLUME__ - if (!(sd->flag & SD_HAS_ONLY_VOLUME)) { -# endif - - /* Evaluate shader. */ - shader_eval_surface(kg, sd, state, NULL, state->flag); - shader_prepare_closures(sd, state); - - /* Apply shadow catcher, holdout, emission. */ - if (!kernel_path_shader_apply(kg, sd, state, ray, throughput, emission_sd, L, NULL)) { - break; - } - - /* path termination. this is a strange place to put the termination, it's - * mainly due to the mixed in MIS that we use. gives too many unneeded - * shader evaluations, only need emission if we are going to terminate */ - float probability = path_state_continuation_probability(kg, state, throughput); - - if (probability == 0.0f) { - break; - } - else if (probability != 1.0f) { - float terminate = path_state_rng_1D(kg, state, PRNG_TERMINATE); - - if (terminate >= probability) - break; - - throughput /= probability; - } - -# ifdef __DENOISING_FEATURES__ - kernel_update_denoising_features(kg, sd, state, L); -# endif - -# ifdef __AO__ - /* ambient occlusion */ - if (kernel_data.integrator.use_ambient_occlusion) { - kernel_path_ao(kg, sd, emission_sd, L, state, throughput, zero_float3()); - } -# endif /* __AO__ */ - -# ifdef __SUBSURFACE__ - /* bssrdf scatter to a different location on the same object, replacing - * the closures with a diffuse BSDF */ - if (sd->flag & SD_BSSRDF) { - if (kernel_path_subsurface_scatter( - kg, sd, emission_sd, L, state, ray, &throughput, &ss_indirect)) { - break; - } - } -# endif /* __SUBSURFACE__ */ - -# if defined(__EMISSION__) - int all = (kernel_data.integrator.sample_all_lights_indirect) || - (state->flag & PATH_RAY_SHADOW_CATCHER); - kernel_branched_path_surface_connect_light( - kg, sd, emission_sd, state, throughput, 1.0f, L, all); -# endif /* defined(__EMISSION__) */ - -# ifdef __VOLUME__ - } -# endif - - if (!kernel_path_surface_bounce(kg, sd, &throughput, state, &L->state, ray)) - break; - } - -# ifdef __SUBSURFACE__ - /* Trace indirect subsurface rays by restarting the loop. this uses less - * stack memory than invoking kernel_path_indirect. - */ - if (ss_indirect.num_rays) { - kernel_path_subsurface_setup_indirect(kg, &ss_indirect, state, ray, L, &throughput); - } - else { - break; - } - } -# endif /* __SUBSURFACE__ */ -} - -# endif /* defined(__BRANCHED_PATH__) || defined(__BAKING__) */ - -ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg, - PathState *state, - float3 throughput, - Ray *ray, - PathRadiance *L, - ccl_global float *buffer, - ShaderData *emission_sd) -{ - PROFILING_INIT(kg, PROFILING_PATH_INTEGRATE); - - /* Shader data memory used for both volumes and surfaces, saves stack space. */ - ShaderData sd; - -# ifdef __SUBSURFACE__ - SubsurfaceIndirectRays ss_indirect; - kernel_path_subsurface_init_indirect(&ss_indirect); - - for (;;) { -# endif /* __SUBSURFACE__ */ - - /* path iteration */ - for (;;) { - /* Find intersection with objects in scene. */ - Intersection isect; - bool hit = kernel_path_scene_intersect(kg, state, ray, &isect, L, sd.object); - - /* Find intersection with lamps and compute emission for MIS. */ - kernel_path_lamp_emission(kg, state, ray, throughput, &isect, &sd, L); - -# ifdef __VOLUME__ - /* Volume integration. */ - VolumeIntegrateResult result = kernel_path_volume( - kg, &sd, state, ray, &throughput, &isect, hit, emission_sd, L); - - if (result == VOLUME_PATH_SCATTERED) { - continue; - } - else if (result == VOLUME_PATH_MISSED) { - break; - } -# endif /* __VOLUME__*/ - - /* Shade background. */ - if (!hit) { - kernel_path_background(kg, state, ray, throughput, &sd, buffer, L); - break; - } - else if (path_state_ao_bounce(kg, state)) { - if (intersection_get_shader_flags(kg, &isect) & - (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; - } - else { - break; - } - } - - /* Setup shader data. */ - shader_setup_from_ray(kg, &sd, &isect, ray); - - /* Skip most work for volume bounding surface. */ -# ifdef __VOLUME__ - if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { -# endif - - /* Evaluate shader. */ - shader_eval_surface(kg, &sd, state, buffer, state->flag); - shader_prepare_closures(&sd, state); - - /* Apply shadow catcher, holdout, emission. */ - if (!kernel_path_shader_apply(kg, &sd, state, ray, throughput, emission_sd, L, buffer)) { - break; - } - - /* path termination. this is a strange place to put the termination, it's - * mainly due to the mixed in MIS that we use. gives too many unneeded - * shader evaluations, only need emission if we are going to terminate */ - float probability = path_state_continuation_probability(kg, state, throughput); - - if (probability == 0.0f) { - break; - } - else if (probability != 1.0f) { - float terminate = path_state_rng_1D(kg, state, PRNG_TERMINATE); - if (terminate >= probability) - break; - - throughput /= probability; - } - -# ifdef __DENOISING_FEATURES__ - kernel_update_denoising_features(kg, &sd, state, L); -# endif - -# ifdef __AO__ - /* ambient occlusion */ - if (kernel_data.integrator.use_ambient_occlusion) { - kernel_path_ao(kg, &sd, emission_sd, L, state, throughput, shader_bsdf_alpha(kg, &sd)); - } -# endif /* __AO__ */ - -# ifdef __SUBSURFACE__ - /* bssrdf scatter to a different location on the same object, replacing - * the closures with a diffuse BSDF */ - if (sd.flag & SD_BSSRDF) { - if (kernel_path_subsurface_scatter( - kg, &sd, emission_sd, L, state, ray, &throughput, &ss_indirect)) { - break; - } - } -# endif /* __SUBSURFACE__ */ - -# ifdef __EMISSION__ - /* direct lighting */ - kernel_path_surface_connect_light(kg, &sd, emission_sd, throughput, state, L); -# endif /* __EMISSION__ */ - -# ifdef __VOLUME__ - } -# endif - - /* compute direct lighting and next bounce */ - if (!kernel_path_surface_bounce(kg, &sd, &throughput, state, &L->state, ray)) - break; - } - -# ifdef __SUBSURFACE__ - /* Trace indirect subsurface rays by restarting the loop. this uses less - * stack memory than invoking kernel_path_indirect. - */ - if (ss_indirect.num_rays) { - kernel_path_subsurface_setup_indirect(kg, &ss_indirect, state, ray, L, &throughput); - } - else { - break; - } - } -# endif /* __SUBSURFACE__ */ -} - -ccl_device void kernel_path_trace( - KernelGlobals *kg, ccl_global float *buffer, int sample, int x, int y, int offset, int stride) -{ - PROFILING_INIT(kg, PROFILING_RAY_SETUP); - - /* buffer offset */ - int index = offset + x + y * stride; - int pass_stride = kernel_data.film.pass_stride; - - buffer += index * pass_stride; - - if (kernel_data.film.pass_adaptive_aux_buffer) { - ccl_global float4 *aux = (ccl_global float4 *)(buffer + - kernel_data.film.pass_adaptive_aux_buffer); - if ((*aux).w > 0.0f) { - return; - } - } - - /* Initialize random numbers and sample ray. */ - uint rng_hash; - Ray ray; - - kernel_path_trace_setup(kg, sample, x, y, &rng_hash, &ray); - - if (ray.t == 0.0f) { - return; - } - - /* Initialize state. */ - float3 throughput = one_float3(); - - PathRadiance L; - path_radiance_init(kg, &L); - - ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - - PathState state; - path_state_init(kg, emission_sd, &state, rng_hash, sample, &ray); - -# ifdef __KERNEL_OPTIX__ - /* Force struct into local memory to avoid costly spilling on trace calls. */ - if (pass_stride < 0) /* This is never executed and just prevents the compiler from doing SROA. */ - for (int i = 0; i < sizeof(L); ++i) - reinterpret_cast(&L)[-pass_stride + i] = 0; -# endif - - /* Integrate. */ - kernel_path_integrate(kg, &state, throughput, &ray, &L, buffer, emission_sd); - - kernel_write_result(kg, buffer, sample, &L); -} - -#endif /* __SPLIT_KERNEL__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_branched.h b/intern/cycles/kernel/kernel_path_branched.h deleted file mode 100644 index a1ee1bc107e..00000000000 --- a/intern/cycles/kernel/kernel_path_branched.h +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __BRANCHED_PATH__ - -ccl_device_inline void kernel_branched_path_ao(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput) -{ - int num_samples = kernel_data.integrator.ao_samples; - float num_samples_inv = 1.0f / num_samples; - float ao_factor = kernel_data.background.ao_factor; - float3 ao_N; - float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N); - float3 ao_alpha = shader_bsdf_alpha(kg, sd); - - for (int j = 0; j < num_samples; j++) { - float bsdf_u, bsdf_v; - path_branched_rng_2D( - kg, state->rng_hash, state, j, num_samples, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - - float3 ao_D; - float ao_pdf; - - sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); - - if (dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f) { - Ray light_ray; - float3 ao_shadow; - - light_ray.P = ray_offset(sd->P, sd->Ng); - light_ray.D = ao_D; - light_ray.t = kernel_data.background.ao_distance; - light_ray.time = sd->time; - light_ray.dP = sd->dP; - light_ray.dD = differential3_zero(); - - if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &ao_shadow)) { - path_radiance_accum_ao( - kg, L, state, throughput * num_samples_inv, ao_alpha, ao_bsdf, ao_shadow); - } - else { - path_radiance_accum_total_ao(L, state, throughput * num_samples_inv, ao_bsdf); - } - } - } -} - -# ifndef __SPLIT_KERNEL__ - -# ifdef __VOLUME__ -ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg, - ShaderData *sd, - PathState *state, - Ray *ray, - float3 *throughput, - ccl_addr_space Intersection *isect, - bool hit, - ShaderData *indirect_sd, - ShaderData *emission_sd, - PathRadiance *L) -{ - /* Sanitize volume stack. */ - if (!hit) { - kernel_volume_clean_stack(kg, state->volume_stack); - } - - if (state->volume_stack[0].shader == SHADER_NONE) { - return; - } - - /* volume attenuation, emission, scatter */ - Ray volume_ray = *ray; - volume_ray.t = (hit) ? isect->t : FLT_MAX; - - float step_size = volume_stack_step_size(kg, state->volume_stack); - const int object = sd->object; - -# ifdef __VOLUME_DECOUPLED__ - /* decoupled ray marching only supported on CPU */ - if (kernel_data.integrator.volume_decoupled) { - /* cache steps along volume for repeated sampling */ - VolumeSegment volume_segment; - - shader_setup_from_volume(kg, sd, &volume_ray); - kernel_volume_decoupled_record(kg, state, &volume_ray, sd, &volume_segment, step_size); - - /* direct light sampling */ - if (volume_segment.closure_flag & SD_SCATTER) { - volume_segment.sampling_method = volume_stack_sampling_method(kg, state->volume_stack); - - int all = kernel_data.integrator.sample_all_lights_direct; - - kernel_branched_path_volume_connect_light( - kg, sd, emission_sd, *throughput, state, L, all, &volume_ray, &volume_segment); - - /* indirect light sampling */ - int num_samples = kernel_data.integrator.volume_samples; - float num_samples_inv = 1.0f / num_samples; - - for (int j = 0; j < num_samples; j++) { - PathState ps = *state; - Ray pray = *ray; - float3 tp = *throughput; - - /* branch RNG state */ - path_state_branch(&ps, j, num_samples); - - /* scatter sample. if we use distance sampling and take just one - * sample for direct and indirect light, we could share this - * computation, but makes code a bit complex */ - float rphase = path_state_rng_1D(kg, &ps, PRNG_PHASE_CHANNEL); - float rscatter = path_state_rng_1D(kg, &ps, PRNG_SCATTER_DISTANCE); - - VolumeIntegrateResult result = kernel_volume_decoupled_scatter( - kg, &ps, &pray, sd, &tp, rphase, rscatter, &volume_segment, NULL, false); - - if (result == VOLUME_PATH_SCATTERED && - kernel_path_volume_bounce(kg, sd, &tp, &ps, &L->state, &pray)) { - kernel_path_indirect( - kg, indirect_sd, emission_sd, &pray, tp * num_samples_inv, &ps, L, object); - - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - } - } - } - - /* emission and transmittance */ - if (volume_segment.closure_flag & SD_EMISSION) - path_radiance_accum_emission(kg, L, state, *throughput, volume_segment.accum_emission); - *throughput *= volume_segment.accum_transmittance; - - /* free cached steps */ - kernel_volume_decoupled_free(kg, &volume_segment); - } - else -# endif /* __VOLUME_DECOUPLED__ */ - { - /* GPU: no decoupled ray marching, scatter probabilistically. */ - int num_samples = kernel_data.integrator.volume_samples; - float num_samples_inv = 1.0f / num_samples; - - /* todo: we should cache the shader evaluations from stepping - * through the volume, for now we redo them multiple times */ - - for (int j = 0; j < num_samples; j++) { - PathState ps = *state; - Ray pray = *ray; - float3 tp = (*throughput) * num_samples_inv; - - /* branch RNG state */ - path_state_branch(&ps, j, num_samples); - - VolumeIntegrateResult result = kernel_volume_integrate( - kg, &ps, sd, &volume_ray, L, &tp, step_size); - -# ifdef __VOLUME_SCATTER__ - if (result == VOLUME_PATH_SCATTERED) { - /* todo: support equiangular, MIS and all light sampling. - * alternatively get decoupled ray marching working on the GPU */ - kernel_path_volume_connect_light(kg, sd, emission_sd, tp, state, L); - - if (kernel_path_volume_bounce(kg, sd, &tp, &ps, &L->state, &pray)) { - kernel_path_indirect(kg, indirect_sd, emission_sd, &pray, tp, &ps, L, object); - - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - } - } -# endif /* __VOLUME_SCATTER__ */ - } - - /* todo: avoid this calculation using decoupled ray marching */ - kernel_volume_shadow(kg, emission_sd, state, &volume_ray, throughput); - } -} -# endif /* __VOLUME__ */ - -/* bounce off surface and integrate indirect light */ -ccl_device_noinline_cpu void kernel_branched_path_surface_indirect_light(KernelGlobals *kg, - ShaderData *sd, - ShaderData *indirect_sd, - ShaderData *emission_sd, - float3 throughput, - float num_samples_adjust, - PathState *state, - PathRadiance *L) -{ - float sum_sample_weight = 0.0f; -# ifdef __DENOISING_FEATURES__ - if (state->denoising_feature_weight > 0.0f) { - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - /* transparency is not handled here, but in outer loop */ - if (!CLOSURE_IS_BSDF(sc->type) || CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { - continue; - } - - sum_sample_weight += sc->sample_weight; - } - } - else { - sum_sample_weight = 1.0f; - } -# endif /* __DENOISING_FEATURES__ */ - - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - /* transparency is not handled here, but in outer loop */ - if (!CLOSURE_IS_BSDF(sc->type) || CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { - continue; - } - - int num_samples; - - if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) - num_samples = kernel_data.integrator.diffuse_samples; - else if (CLOSURE_IS_BSDF_BSSRDF(sc->type)) - num_samples = 1; - else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) - num_samples = kernel_data.integrator.glossy_samples; - else - num_samples = kernel_data.integrator.transmission_samples; - - num_samples = ceil_to_int(num_samples_adjust * num_samples); - - float num_samples_inv = num_samples_adjust / num_samples; - - for (int j = 0; j < num_samples; j++) { - PathState ps = *state; - float3 tp = throughput; - Ray bsdf_ray; -# ifdef __SHADOW_TRICKS__ - float shadow_transparency = L->shadow_transparency; -# endif - - ps.rng_hash = cmj_hash(state->rng_hash, i); - - if (!kernel_branched_path_surface_bounce( - kg, sd, sc, j, num_samples, &tp, &ps, &L->state, &bsdf_ray, sum_sample_weight)) { - continue; - } - - ps.rng_hash = state->rng_hash; - - kernel_path_indirect( - kg, indirect_sd, emission_sd, &bsdf_ray, tp * num_samples_inv, &ps, L, sd->object); - - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - -# ifdef __SHADOW_TRICKS__ - L->shadow_transparency = shadow_transparency; -# endif - } - } -} - -# ifdef __SUBSURFACE__ -ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg, - ShaderData *sd, - ShaderData *indirect_sd, - ShaderData *emission_sd, - PathRadiance *L, - PathState *state, - Ray *ray, - float3 throughput) -{ - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (!CLOSURE_IS_BSSRDF(sc->type)) - continue; - - /* set up random number generator */ - uint lcg_state = lcg_state_init(state, 0x68bc21eb); - int num_samples = kernel_data.integrator.subsurface_samples * 3; - float num_samples_inv = 1.0f / num_samples; - uint bssrdf_rng_hash = cmj_hash(state->rng_hash, i); - - /* do subsurface scatter step with copy of shader data, this will - * replace the BSSRDF with a diffuse BSDF closure */ - for (int j = 0; j < num_samples; j++) { - PathState hit_state = *state; - path_state_branch(&hit_state, j, num_samples); - hit_state.rng_hash = bssrdf_rng_hash; - - LocalIntersection ss_isect; - float bssrdf_u, bssrdf_v; - path_state_rng_2D(kg, &hit_state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); - int num_hits = subsurface_scatter_multi_intersect( - kg, &ss_isect, sd, &hit_state, sc, &lcg_state, bssrdf_u, bssrdf_v, true); - - hit_state.rng_offset += PRNG_BOUNCE_NUM; - -# ifdef __VOLUME__ - Ray volume_ray = *ray; - bool need_update_volume_stack = kernel_data.integrator.use_volumes && - sd->object_flag & SD_OBJECT_INTERSECTS_VOLUME; -# endif /* __VOLUME__ */ - - /* compute lighting with the BSDF closure */ - for (int hit = 0; hit < num_hits; hit++) { - ShaderData bssrdf_sd = *sd; - Bssrdf *bssrdf = (Bssrdf *)sc; - ClosureType bssrdf_type = sc->type; - float bssrdf_roughness = bssrdf->roughness; - subsurface_scatter_multi_setup( - kg, &ss_isect, hit, &bssrdf_sd, &hit_state, bssrdf_type, bssrdf_roughness); - -# ifdef __VOLUME__ - if (need_update_volume_stack) { - /* Setup ray from previous surface point to the new one. */ - float3 P = ray_offset(bssrdf_sd.P, -bssrdf_sd.Ng); - volume_ray.D = normalize_len(P - volume_ray.P, &volume_ray.t); - - for (int k = 0; k < VOLUME_STACK_SIZE; k++) { - hit_state.volume_stack[k] = state->volume_stack[k]; - } - - kernel_volume_stack_update_for_subsurface( - kg, emission_sd, &volume_ray, hit_state.volume_stack); - } -# endif /* __VOLUME__ */ - -# ifdef __EMISSION__ - /* direct light */ - if (kernel_data.integrator.use_direct_light) { - int all = (kernel_data.integrator.sample_all_lights_direct) || - (hit_state.flag & PATH_RAY_SHADOW_CATCHER); - kernel_branched_path_surface_connect_light( - kg, &bssrdf_sd, emission_sd, &hit_state, throughput, num_samples_inv, L, all); - } -# endif /* __EMISSION__ */ - - /* indirect light */ - kernel_branched_path_surface_indirect_light( - kg, &bssrdf_sd, indirect_sd, emission_sd, throughput, num_samples_inv, &hit_state, L); - } - } - } -} -# endif /* __SUBSURFACE__ */ - -ccl_device void kernel_branched_path_integrate(KernelGlobals *kg, - uint rng_hash, - int sample, - Ray ray, - ccl_global float *buffer, - PathRadiance *L) -{ - /* initialize */ - float3 throughput = one_float3(); - - path_radiance_init(kg, L); - - /* shader data memory used for both volumes and surfaces, saves stack space */ - ShaderData sd; - /* shader data used by emission, shadows, volume stacks, indirect path */ - ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - ShaderData indirect_sd; - - PathState state; - path_state_init(kg, emission_sd, &state, rng_hash, sample, &ray); - - /* Main Loop - * Here we only handle transparency intersections from the camera ray. - * Indirect bounces are handled in kernel_branched_path_surface_indirect_light(). - */ - for (;;) { - /* Find intersection with objects in scene. */ - Intersection isect; - bool hit = kernel_path_scene_intersect(kg, &state, &ray, &isect, L, sd.object); - -# ifdef __VOLUME__ - /* Volume integration. */ - kernel_branched_path_volume( - kg, &sd, &state, &ray, &throughput, &isect, hit, &indirect_sd, emission_sd, L); -# endif /* __VOLUME__ */ - - /* Shade background. */ - if (!hit) { - kernel_path_background(kg, &state, &ray, throughput, &sd, buffer, L); - break; - } - - /* Setup and evaluate shader. */ - shader_setup_from_ray(kg, &sd, &isect, &ray); - - /* Skip most work for volume bounding surface. */ -# ifdef __VOLUME__ - if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { -# endif - - shader_eval_surface(kg, &sd, &state, buffer, state.flag); - shader_merge_closures(&sd); - - /* Apply shadow catcher, holdout, emission. */ - if (!kernel_path_shader_apply(kg, &sd, &state, &ray, throughput, emission_sd, L, buffer)) { - break; - } - - /* transparency termination */ - if (state.flag & PATH_RAY_TRANSPARENT) { - /* path termination. this is a strange place to put the termination, it's - * mainly due to the mixed in MIS that we use. gives too many unneeded - * shader evaluations, only need emission if we are going to terminate */ - float probability = path_state_continuation_probability(kg, &state, throughput); - - if (probability == 0.0f) { - break; - } - else if (probability != 1.0f) { - float terminate = path_state_rng_1D(kg, &state, PRNG_TERMINATE); - - if (terminate >= probability) - break; - - throughput /= probability; - } - } - -# ifdef __DENOISING_FEATURES__ - kernel_update_denoising_features(kg, &sd, &state, L); -# endif - -# ifdef __AO__ - /* ambient occlusion */ - if (kernel_data.integrator.use_ambient_occlusion) { - kernel_branched_path_ao(kg, &sd, emission_sd, L, &state, throughput); - } -# endif /* __AO__ */ - -# ifdef __SUBSURFACE__ - /* bssrdf scatter to a different location on the same object */ - if (sd.flag & SD_BSSRDF) { - kernel_branched_path_subsurface_scatter( - kg, &sd, &indirect_sd, emission_sd, L, &state, &ray, throughput); - } -# endif /* __SUBSURFACE__ */ - - PathState hit_state = state; - -# ifdef __EMISSION__ - /* direct light */ - if (kernel_data.integrator.use_direct_light) { - int all = (kernel_data.integrator.sample_all_lights_direct) || - (state.flag & PATH_RAY_SHADOW_CATCHER); - kernel_branched_path_surface_connect_light( - kg, &sd, emission_sd, &hit_state, throughput, 1.0f, L, all); - } -# endif /* __EMISSION__ */ - - /* indirect light */ - kernel_branched_path_surface_indirect_light( - kg, &sd, &indirect_sd, emission_sd, throughput, 1.0f, &hit_state, L); - - /* continue in case of transparency */ - throughput *= shader_bsdf_transparency(kg, &sd); - - if (is_zero(throughput)) - break; - - /* Update Path State */ - path_state_next(kg, &state, LABEL_TRANSPARENT); - -# ifdef __VOLUME__ - } - else { - if (!path_state_volume_next(kg, &state)) { - break; - } - } -# endif - - ray.P = ray_offset(sd.P, -sd.Ng); - ray.t -= sd.ray_length; /* clipping works through transparent */ - -# ifdef __RAY_DIFFERENTIALS__ - ray.dP = sd.dP; - ray.dD.dx = -sd.dI.dx; - ray.dD.dy = -sd.dI.dy; -# endif /* __RAY_DIFFERENTIALS__ */ - -# ifdef __VOLUME__ - /* enter/exit volume */ - kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack); -# endif /* __VOLUME__ */ - } -} - -ccl_device void kernel_branched_path_trace( - KernelGlobals *kg, ccl_global float *buffer, int sample, int x, int y, int offset, int stride) -{ - /* buffer offset */ - int index = offset + x + y * stride; - int pass_stride = kernel_data.film.pass_stride; - - buffer += index * pass_stride; - - if (kernel_data.film.pass_adaptive_aux_buffer) { - ccl_global float4 *aux = (ccl_global float4 *)(buffer + - kernel_data.film.pass_adaptive_aux_buffer); - if ((*aux).w > 0.0f) { - return; - } - } - - /* initialize random numbers and ray */ - uint rng_hash; - Ray ray; - - kernel_path_trace_setup(kg, sample, x, y, &rng_hash, &ray); - - /* integrate */ - PathRadiance L; - - if (ray.t != 0.0f) { - kernel_branched_path_integrate(kg, rng_hash, sample, ray, buffer, &L); - kernel_write_result(kg, buffer, sample, &L); - } -} - -# endif /* __SPLIT_KERNEL__ */ - -#endif /* __BRANCHED_PATH__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_common.h b/intern/cycles/kernel/kernel_path_common.h deleted file mode 100644 index 815767595a9..00000000000 --- a/intern/cycles/kernel/kernel_path_common.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/util_hash.h" - -CCL_NAMESPACE_BEGIN - -ccl_device_inline void kernel_path_trace_setup( - KernelGlobals *kg, int sample, int x, int y, uint *rng_hash, ccl_addr_space Ray *ray) -{ - float filter_u; - float filter_v; - - int num_samples = kernel_data.integrator.aa_samples; - - path_rng_init(kg, sample, num_samples, rng_hash, x, y, &filter_u, &filter_v); - - /* sample camera ray */ - - float lens_u = 0.0f, lens_v = 0.0f; - - if (kernel_data.cam.aperturesize > 0.0f) - path_rng_2D(kg, *rng_hash, sample, num_samples, PRNG_LENS_U, &lens_u, &lens_v); - - float time = 0.0f; - -#ifdef __CAMERA_MOTION__ - if (kernel_data.cam.shuttertime != -1.0f) - time = path_rng_1D(kg, *rng_hash, sample, num_samples, PRNG_TIME); -#endif - - camera_sample(kg, x, y, filter_u, filter_v, lens_u, lens_v, time, ray); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h index bf601580cd0..ebb2c0df4f1 100644 --- a/intern/cycles/kernel/kernel_path_state.h +++ b/intern/cycles/kernel/kernel_path_state.h @@ -14,99 +14,116 @@ * limitations under the License. */ +#pragma once + +#include "kernel_random.h" + CCL_NAMESPACE_BEGIN -ccl_device_inline void path_state_init(KernelGlobals *kg, - ShaderData *stack_sd, - ccl_addr_space PathState *state, - uint rng_hash, - int sample, - ccl_addr_space Ray *ray) +/* Initialize queues, so that the this path is considered terminated. + * Used for early outputs in the camera ray initialization, as well as initialization of split + * states for shadow catcher. */ +ccl_device_inline void path_state_init_queues(INTEGRATOR_STATE_ARGS) { - state->flag = PATH_RAY_CAMERA | PATH_RAY_MIS_SKIP | PATH_RAY_TRANSPARENT_BACKGROUND; + INTEGRATOR_STATE_WRITE(path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = 0; +} - state->rng_hash = rng_hash; - state->rng_offset = PRNG_BASE_NUM; - state->sample = sample; - state->num_samples = kernel_data.integrator.aa_samples; - state->branch_factor = 1.0f; +/* Minimalistic initialization of the path state, which is needed for early outputs in the + * integrator initialization to work. */ +ccl_device_inline void path_state_init(INTEGRATOR_STATE_ARGS, + const ccl_global KernelWorkTile *ccl_restrict tile, + const int x, + const int y) +{ + const uint render_pixel_index = (uint)tile->offset + x + y * tile->stride; - state->bounce = 0; - state->diffuse_bounce = 0; - state->glossy_bounce = 0; - state->transmission_bounce = 0; - state->transparent_bounce = 0; + INTEGRATOR_STATE_WRITE(path, render_pixel_index) = render_pixel_index; + + path_state_init_queues(INTEGRATOR_STATE_PASS); +} + +/* Initialize the rest of the path state needed to continue the path integration. */ +ccl_device_inline void path_state_init_integrator(INTEGRATOR_STATE_ARGS, + const int sample, + const uint rng_hash) +{ + INTEGRATOR_STATE_WRITE(path, sample) = sample; + INTEGRATOR_STATE_WRITE(path, bounce) = 0; + INTEGRATOR_STATE_WRITE(path, diffuse_bounce) = 0; + INTEGRATOR_STATE_WRITE(path, glossy_bounce) = 0; + INTEGRATOR_STATE_WRITE(path, transmission_bounce) = 0; + INTEGRATOR_STATE_WRITE(path, transparent_bounce) = 0; + INTEGRATOR_STATE_WRITE(path, volume_bounce) = 0; + INTEGRATOR_STATE_WRITE(path, volume_bounds_bounce) = 0; + INTEGRATOR_STATE_WRITE(path, rng_hash) = rng_hash; + INTEGRATOR_STATE_WRITE(path, rng_offset) = PRNG_BASE_NUM; + INTEGRATOR_STATE_WRITE(path, flag) = PATH_RAY_CAMERA | PATH_RAY_MIS_SKIP | + PATH_RAY_TRANSPARENT_BACKGROUND; + INTEGRATOR_STATE_WRITE(path, mis_ray_pdf) = 0.0f; + INTEGRATOR_STATE_WRITE(path, mis_ray_t) = 0.0f; + INTEGRATOR_STATE_WRITE(path, min_ray_pdf) = FLT_MAX; + INTEGRATOR_STATE_WRITE(path, throughput) = make_float3(1.0f, 1.0f, 1.0f); + + if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { + INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 0, object) = OBJECT_NONE; + INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 0, shader) = kernel_data.background.volume_shader; + INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 1, object) = OBJECT_NONE; + INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 1, shader) = SHADER_NONE; + } #ifdef __DENOISING_FEATURES__ - if (kernel_data.film.pass_denoising_data) { - state->flag |= PATH_RAY_STORE_SHADOW_INFO; - state->denoising_feature_weight = 1.0f; - state->denoising_feature_throughput = one_float3(); - } - else { - state->denoising_feature_weight = 0.0f; - state->denoising_feature_throughput = zero_float3(); - } -#endif /* __DENOISING_FEATURES__ */ - - state->min_ray_pdf = FLT_MAX; - state->ray_pdf = 0.0f; -#ifdef __LAMP_MIS__ - state->ray_t = 0.0f; -#endif - -#ifdef __VOLUME__ - state->volume_bounce = 0; - state->volume_bounds_bounce = 0; - - if (kernel_data.integrator.use_volumes) { - /* Initialize volume stack with volume we are inside of. */ - kernel_volume_stack_init(kg, stack_sd, state, ray, state->volume_stack); - } - else { - state->volume_stack[0].shader = SHADER_NONE; + if (kernel_data.kernel_features & KERNEL_FEATURE_DENOISING) { + INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_DENOISING_FEATURES; + INTEGRATOR_STATE_WRITE(path, denoising_feature_throughput) = one_float3(); } #endif } -ccl_device_inline void path_state_next(KernelGlobals *kg, - ccl_addr_space PathState *state, - int label) +ccl_device_inline void path_state_next(INTEGRATOR_STATE_ARGS, int label) { + uint32_t flag = INTEGRATOR_STATE(path, flag); + /* ray through transparent keeps same flags from previous ray and is * not counted as a regular bounce, transparent has separate max */ if (label & LABEL_TRANSPARENT) { - state->flag |= PATH_RAY_TRANSPARENT; - state->transparent_bounce++; - if (state->transparent_bounce >= kernel_data.integrator.transparent_max_bounce) { - state->flag |= PATH_RAY_TERMINATE_IMMEDIATE; + uint32_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce) + 1; + + flag |= PATH_RAY_TRANSPARENT; + if (transparent_bounce >= kernel_data.integrator.transparent_max_bounce) { + flag |= PATH_RAY_TERMINATE_ON_NEXT_SURFACE; } if (!kernel_data.integrator.transparent_shadows) - state->flag |= PATH_RAY_MIS_SKIP; - - /* random number generator next bounce */ - state->rng_offset += PRNG_BOUNCE_NUM; + flag |= PATH_RAY_MIS_SKIP; + INTEGRATOR_STATE_WRITE(path, flag) = flag; + INTEGRATOR_STATE_WRITE(path, transparent_bounce) = transparent_bounce; + /* Random number generator next bounce. */ + INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; return; } - state->bounce++; - if (state->bounce >= kernel_data.integrator.max_bounce) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + uint32_t bounce = INTEGRATOR_STATE(path, bounce) + 1; + if (bounce >= kernel_data.integrator.max_bounce) { + flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } - state->flag &= ~(PATH_RAY_ALL_VISIBILITY | PATH_RAY_MIS_SKIP); + flag &= ~(PATH_RAY_ALL_VISIBILITY | PATH_RAY_MIS_SKIP); #ifdef __VOLUME__ if (label & LABEL_VOLUME_SCATTER) { /* volume scatter */ - state->flag |= PATH_RAY_VOLUME_SCATTER; - state->flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; + flag |= PATH_RAY_VOLUME_SCATTER; + flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; + if (bounce == 1) { + flag |= PATH_RAY_VOLUME_PASS; + } - state->volume_bounce++; - if (state->volume_bounce >= kernel_data.integrator.max_volume_bounce) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + const int volume_bounce = INTEGRATOR_STATE(path, volume_bounce) + 1; + INTEGRATOR_STATE_WRITE(path, volume_bounce) = volume_bounce; + if (volume_bounce >= kernel_data.integrator.max_volume_bounce) { + flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } } else @@ -114,163 +131,237 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, { /* surface reflection/transmission */ if (label & LABEL_REFLECT) { - state->flag |= PATH_RAY_REFLECT; - state->flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; + flag |= PATH_RAY_REFLECT; + flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; if (label & LABEL_DIFFUSE) { - state->diffuse_bounce++; - if (state->diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + const int diffuse_bounce = INTEGRATOR_STATE(path, diffuse_bounce) + 1; + INTEGRATOR_STATE_WRITE(path, diffuse_bounce) = diffuse_bounce; + if (diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) { + flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } } else { - state->glossy_bounce++; - if (state->glossy_bounce >= kernel_data.integrator.max_glossy_bounce) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + const int glossy_bounce = INTEGRATOR_STATE(path, glossy_bounce) + 1; + INTEGRATOR_STATE_WRITE(path, glossy_bounce) = glossy_bounce; + if (glossy_bounce >= kernel_data.integrator.max_glossy_bounce) { + flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } } } else { kernel_assert(label & LABEL_TRANSMIT); - state->flag |= PATH_RAY_TRANSMIT; + flag |= PATH_RAY_TRANSMIT; if (!(label & LABEL_TRANSMIT_TRANSPARENT)) { - state->flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; + flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; } - state->transmission_bounce++; - if (state->transmission_bounce >= kernel_data.integrator.max_transmission_bounce) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + const int transmission_bounce = INTEGRATOR_STATE(path, transmission_bounce) + 1; + INTEGRATOR_STATE_WRITE(path, transmission_bounce) = transmission_bounce; + if (transmission_bounce >= kernel_data.integrator.max_transmission_bounce) { + flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } } /* diffuse/glossy/singular */ if (label & LABEL_DIFFUSE) { - state->flag |= PATH_RAY_DIFFUSE | PATH_RAY_DIFFUSE_ANCESTOR; + flag |= PATH_RAY_DIFFUSE | PATH_RAY_DIFFUSE_ANCESTOR; } else if (label & LABEL_GLOSSY) { - state->flag |= PATH_RAY_GLOSSY; + flag |= PATH_RAY_GLOSSY; } else { kernel_assert(label & LABEL_SINGULAR); - state->flag |= PATH_RAY_GLOSSY | PATH_RAY_SINGULAR | PATH_RAY_MIS_SKIP; + flag |= PATH_RAY_GLOSSY | PATH_RAY_SINGULAR | PATH_RAY_MIS_SKIP; + } + + /* Render pass categories. */ + if (bounce == 1) { + flag |= (label & LABEL_TRANSMIT) ? PATH_RAY_TRANSMISSION_PASS : PATH_RAY_REFLECT_PASS; } } - /* random number generator next bounce */ - state->rng_offset += PRNG_BOUNCE_NUM; + INTEGRATOR_STATE_WRITE(path, flag) = flag; + INTEGRATOR_STATE_WRITE(path, bounce) = bounce; -#ifdef __DENOISING_FEATURES__ - if ((state->denoising_feature_weight == 0.0f) && !(state->flag & PATH_RAY_SHADOW_CATCHER)) { - state->flag &= ~PATH_RAY_STORE_SHADOW_INFO; - } -#endif + /* Random number generator next bounce. */ + INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; } #ifdef __VOLUME__ -ccl_device_inline bool path_state_volume_next(KernelGlobals *kg, ccl_addr_space PathState *state) +ccl_device_inline bool path_state_volume_next(INTEGRATOR_STATE_ARGS) { /* For volume bounding meshes we pass through without counting transparent * bounces, only sanity check in case self intersection gets us stuck. */ - state->volume_bounds_bounce++; - if (state->volume_bounds_bounce > VOLUME_BOUNDS_MAX) { + uint32_t volume_bounds_bounce = INTEGRATOR_STATE(path, volume_bounds_bounce) + 1; + INTEGRATOR_STATE_WRITE(path, volume_bounds_bounce) = volume_bounds_bounce; + if (volume_bounds_bounce > VOLUME_BOUNDS_MAX) { return false; } /* Random number generator next bounce. */ - if (state->volume_bounds_bounce > 1) { - state->rng_offset += PRNG_BOUNCE_NUM; + if (volume_bounds_bounce > 1) { + INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; } return true; } #endif -ccl_device_inline uint path_state_ray_visibility(KernelGlobals *kg, - ccl_addr_space PathState *state) +ccl_device_inline uint path_state_ray_visibility(INTEGRATOR_STATE_CONST_ARGS) { - uint flag = state->flag & PATH_RAY_ALL_VISIBILITY; + const uint32_t path_flag = INTEGRATOR_STATE(path, flag); - /* for visibility, diffuse/glossy are for reflection only */ - if (flag & PATH_RAY_TRANSMIT) - flag &= ~(PATH_RAY_DIFFUSE | PATH_RAY_GLOSSY); - /* todo: this is not supported as its own ray visibility yet */ - if (state->flag & PATH_RAY_VOLUME_SCATTER) - flag |= PATH_RAY_DIFFUSE; + uint32_t visibility = path_flag & PATH_RAY_ALL_VISIBILITY; - return flag; + /* For visibility, diffuse/glossy are for reflection only. */ + if (visibility & PATH_RAY_TRANSMIT) { + visibility &= ~(PATH_RAY_DIFFUSE | PATH_RAY_GLOSSY); + } + + /* todo: this is not supported as its own ray visibility yet. */ + if (path_flag & PATH_RAY_VOLUME_SCATTER) { + visibility |= PATH_RAY_DIFFUSE; + } + + visibility = SHADOW_CATCHER_PATH_VISIBILITY(path_flag, visibility); + + return visibility; } -ccl_device_inline float path_state_continuation_probability(KernelGlobals *kg, - ccl_addr_space PathState *state, - const float3 throughput) +ccl_device_inline float path_state_continuation_probability(INTEGRATOR_STATE_CONST_ARGS, + const uint32_t path_flag) { - if (state->flag & PATH_RAY_TERMINATE_IMMEDIATE) { - /* Ray is to be terminated immediately. */ - return 0.0f; - } - else if (state->flag & PATH_RAY_TRANSPARENT) { + if (path_flag & PATH_RAY_TRANSPARENT) { + const uint32_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce); /* Do at least specified number of bounces without RR. */ - if (state->transparent_bounce <= kernel_data.integrator.transparent_min_bounce) { + if (transparent_bounce <= kernel_data.integrator.transparent_min_bounce) { return 1.0f; } -#ifdef __SHADOW_TRICKS__ - /* Exception for shadow catcher not working correctly with RR. */ - else if ((state->flag & PATH_RAY_SHADOW_CATCHER) && (state->transparent_bounce <= 8)) { - return 1.0f; - } -#endif } else { + const uint32_t bounce = INTEGRATOR_STATE(path, bounce); /* Do at least specified number of bounces without RR. */ - if (state->bounce <= kernel_data.integrator.min_bounce) { + if (bounce <= kernel_data.integrator.min_bounce) { return 1.0f; } -#ifdef __SHADOW_TRICKS__ - /* Exception for shadow catcher not working correctly with RR. */ - else if ((state->flag & PATH_RAY_SHADOW_CATCHER) && (state->bounce <= 3)) { - return 1.0f; - } -#endif } /* Probabilistic termination: use sqrt() to roughly match typical view * transform and do path termination a bit later on average. */ - return min(sqrtf(max3(fabs(throughput)) * state->branch_factor), 1.0f); + return min(sqrtf(max3(fabs(INTEGRATOR_STATE(path, throughput)))), 1.0f); } -/* TODO(DingTo): Find more meaningful name for this */ -ccl_device_inline void path_state_modify_bounce(ccl_addr_space PathState *state, bool increase) +ccl_device_inline bool path_state_ao_bounce(INTEGRATOR_STATE_CONST_ARGS) { - /* Modify bounce temporarily for shader eval */ - if (increase) - state->bounce += 1; - else - state->bounce -= 1; -} - -ccl_device_inline bool path_state_ao_bounce(KernelGlobals *kg, ccl_addr_space PathState *state) -{ - if (state->bounce <= kernel_data.integrator.ao_bounces) { + if (!kernel_data.integrator.ao_bounces) { return false; } - int bounce = state->bounce - state->transmission_bounce - (state->glossy_bounce > 0); + const int bounce = INTEGRATOR_STATE(path, bounce) - INTEGRATOR_STATE(path, transmission_bounce) - + (INTEGRATOR_STATE(path, glossy_bounce) > 0) + 1; return (bounce > kernel_data.integrator.ao_bounces); } -ccl_device_inline void path_state_branch(ccl_addr_space PathState *state, - int branch, - int num_branches) +/* Random Number Sampling Utility Functions + * + * For each random number in each step of the path we must have a unique + * dimension to avoid using the same sequence twice. + * + * For branches in the path we must be careful not to reuse the same number + * in a sequence and offset accordingly. + */ + +/* RNG State loaded onto stack. */ +typedef struct RNGState { + uint rng_hash; + uint rng_offset; + int sample; +} RNGState; + +ccl_device_inline void path_state_rng_load(INTEGRATOR_STATE_CONST_ARGS, RNGState *rng_state) { - if (num_branches > 1) { - /* Path is splitting into a branch, adjust so that each branch - * still gets a unique sample from the same sequence. */ - state->sample = state->sample * num_branches + branch; - state->num_samples = state->num_samples * num_branches; - state->branch_factor *= num_branches; + rng_state->rng_hash = INTEGRATOR_STATE(path, rng_hash); + rng_state->rng_offset = INTEGRATOR_STATE(path, rng_offset); + rng_state->sample = INTEGRATOR_STATE(path, sample); +} + +ccl_device_inline void shadow_path_state_rng_load(INTEGRATOR_STATE_CONST_ARGS, RNGState *rng_state) +{ + const uint shadow_bounces = INTEGRATOR_STATE(shadow_path, transparent_bounce) - + INTEGRATOR_STATE(path, transparent_bounce); + + rng_state->rng_hash = INTEGRATOR_STATE(path, rng_hash); + rng_state->rng_offset = INTEGRATOR_STATE(path, rng_offset) + PRNG_BOUNCE_NUM * shadow_bounces; + rng_state->sample = INTEGRATOR_STATE(path, sample); +} + +ccl_device_inline float path_state_rng_1D(const KernelGlobals *kg, + const RNGState *rng_state, + int dimension) +{ + return path_rng_1D( + kg, rng_state->rng_hash, rng_state->sample, rng_state->rng_offset + dimension); +} + +ccl_device_inline void path_state_rng_2D( + const KernelGlobals *kg, const RNGState *rng_state, int dimension, float *fx, float *fy) +{ + path_rng_2D( + kg, rng_state->rng_hash, rng_state->sample, rng_state->rng_offset + dimension, fx, fy); +} + +ccl_device_inline float path_state_rng_1D_hash(const KernelGlobals *kg, + const RNGState *rng_state, + uint hash) +{ + /* Use a hash instead of dimension, this is not great but avoids adding + * more dimensions to each bounce which reduces quality of dimensions we + * are already using. */ + return path_rng_1D( + kg, cmj_hash_simple(rng_state->rng_hash, hash), rng_state->sample, rng_state->rng_offset); +} + +ccl_device_inline float path_branched_rng_1D(const KernelGlobals *kg, + const RNGState *rng_state, + int branch, + int num_branches, + int dimension) +{ + return path_rng_1D(kg, + rng_state->rng_hash, + rng_state->sample * num_branches + branch, + rng_state->rng_offset + dimension); +} + +ccl_device_inline void path_branched_rng_2D(const KernelGlobals *kg, + const RNGState *rng_state, + int branch, + int num_branches, + int dimension, + float *fx, + float *fy) +{ + path_rng_2D(kg, + rng_state->rng_hash, + rng_state->sample * num_branches + branch, + rng_state->rng_offset + dimension, + fx, + fy); +} + +/* Utility functions to get light termination value, + * since it might not be needed in many cases. + */ +ccl_device_inline float path_state_rng_light_termination(const KernelGlobals *kg, + const RNGState *state) +{ + if (kernel_data.integrator.light_inv_rr_threshold > 0.0f) { + return path_state_rng_1D(kg, state, PRNG_LIGHT_TERMINATE); } + return 0.0f; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_subsurface.h b/intern/cycles/kernel/kernel_path_subsurface.h deleted file mode 100644 index 97d3f292ca3..00000000000 --- a/intern/cycles/kernel/kernel_path_subsurface.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __SUBSURFACE__ -# ifndef __KERNEL_CUDA__ -ccl_device -# else -ccl_device_inline -# endif - bool - kernel_path_subsurface_scatter(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - PathRadiance *L, - ccl_addr_space PathState *state, - ccl_addr_space Ray *ray, - ccl_addr_space float3 *throughput, - ccl_addr_space SubsurfaceIndirectRays *ss_indirect) -{ - PROFILING_INIT(kg, PROFILING_SUBSURFACE); - - float bssrdf_u, bssrdf_v; - path_state_rng_2D(kg, state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); - - const ShaderClosure *sc = shader_bssrdf_pick(sd, throughput, &bssrdf_u); - - /* do bssrdf scatter step if we picked a bssrdf closure */ - if (sc) { - /* We should never have two consecutive BSSRDF bounces, - * the second one should be converted to a diffuse BSDF to - * avoid this. - */ - kernel_assert(!(state->flag & PATH_RAY_DIFFUSE_ANCESTOR)); - - uint lcg_state = lcg_state_init_addrspace(state, 0x68bc21eb); - - LocalIntersection ss_isect; - int num_hits = subsurface_scatter_multi_intersect( - kg, &ss_isect, sd, state, sc, &lcg_state, bssrdf_u, bssrdf_v, false); -# ifdef __VOLUME__ - bool need_update_volume_stack = kernel_data.integrator.use_volumes && - sd->object_flag & SD_OBJECT_INTERSECTS_VOLUME; -# endif /* __VOLUME__ */ - - /* Closure memory will be overwritten, so read required variables now. */ - Bssrdf *bssrdf = (Bssrdf *)sc; - ClosureType bssrdf_type = sc->type; - float bssrdf_roughness = bssrdf->roughness; - - /* compute lighting with the BSDF closure */ - for (int hit = 0; hit < num_hits; hit++) { - /* NOTE: We reuse the existing ShaderData, we assume the path - * integration loop stops when this function returns true. - */ - subsurface_scatter_multi_setup(kg, &ss_isect, hit, sd, state, bssrdf_type, bssrdf_roughness); - - kernel_path_surface_connect_light(kg, sd, emission_sd, *throughput, state, L); - - ccl_addr_space PathState *hit_state = &ss_indirect->state[ss_indirect->num_rays]; - ccl_addr_space Ray *hit_ray = &ss_indirect->rays[ss_indirect->num_rays]; - ccl_addr_space float3 *hit_tp = &ss_indirect->throughputs[ss_indirect->num_rays]; - PathRadianceState *hit_L_state = &ss_indirect->L_state[ss_indirect->num_rays]; - - *hit_state = *state; - *hit_ray = *ray; - *hit_tp = *throughput; - *hit_L_state = L->state; - - hit_state->rng_offset += PRNG_BOUNCE_NUM; - - if (kernel_path_surface_bounce(kg, sd, hit_tp, hit_state, hit_L_state, hit_ray)) { -# ifdef __LAMP_MIS__ - hit_state->ray_t = 0.0f; -# endif /* __LAMP_MIS__ */ - -# ifdef __VOLUME__ - if (need_update_volume_stack) { - Ray volume_ray = *ray; - /* Setup ray from previous surface point to the new one. */ - volume_ray.D = normalize_len(hit_ray->P - volume_ray.P, &volume_ray.t); - - kernel_volume_stack_update_for_subsurface( - kg, emission_sd, &volume_ray, hit_state->volume_stack); - } -# endif /* __VOLUME__ */ - ss_indirect->num_rays++; - } - } - return true; - } - return false; -} - -ccl_device_inline void kernel_path_subsurface_init_indirect( - ccl_addr_space SubsurfaceIndirectRays *ss_indirect) -{ - ss_indirect->num_rays = 0; -} - -ccl_device void kernel_path_subsurface_setup_indirect( - KernelGlobals *kg, - ccl_addr_space SubsurfaceIndirectRays *ss_indirect, - ccl_addr_space PathState *state, - ccl_addr_space Ray *ray, - PathRadiance *L, - ccl_addr_space float3 *throughput) -{ - /* Setup state, ray and throughput for indirect SSS rays. */ - ss_indirect->num_rays--; - - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - - *state = ss_indirect->state[ss_indirect->num_rays]; - *ray = ss_indirect->rays[ss_indirect->num_rays]; - L->state = ss_indirect->L_state[ss_indirect->num_rays]; - *throughput = ss_indirect->throughputs[ss_indirect->num_rays]; - - state->rng_offset += ss_indirect->num_rays * PRNG_BOUNCE_NUM; -} - -#endif /* __SUBSURFACE__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_surface.h b/intern/cycles/kernel/kernel_path_surface.h deleted file mode 100644 index ba48c0bdfc4..00000000000 --- a/intern/cycles/kernel/kernel_path_surface.h +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#if defined(__BRANCHED_PATH__) || defined(__SUBSURFACE__) || defined(__SHADOW_TRICKS__) || \ - defined(__BAKING__) -/* branched path tracing: connect path directly to position on one or more lights and add it to L - */ -ccl_device_noinline_cpu void kernel_branched_path_surface_connect_light( - KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - ccl_addr_space PathState *state, - float3 throughput, - float num_samples_adjust, - PathRadiance *L, - int sample_all_lights) -{ -# ifdef __EMISSION__ - /* sample illumination from lights to find path contribution */ - BsdfEval L_light ccl_optional_struct_init; - - int num_lights = 0; - if (kernel_data.integrator.use_direct_light) { - if (sample_all_lights) { - num_lights = kernel_data.integrator.num_all_lights; - if (kernel_data.integrator.pdf_triangles != 0.0f) { - num_lights += 1; - } - } - else { - num_lights = 1; - } - } - - for (int i = 0; i < num_lights; i++) { - /* sample one light at random */ - int num_samples = 1; - int num_all_lights = 1; - uint lamp_rng_hash = state->rng_hash; - bool double_pdf = false; - bool is_mesh_light = false; - bool is_lamp = false; - - if (sample_all_lights) { - /* lamp sampling */ - is_lamp = i < kernel_data.integrator.num_all_lights; - if (is_lamp) { - if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce))) { - continue; - } - num_samples = ceil_to_int(num_samples_adjust * light_select_num_samples(kg, i)); - num_all_lights = kernel_data.integrator.num_all_lights; - lamp_rng_hash = cmj_hash(state->rng_hash, i); - double_pdf = kernel_data.integrator.pdf_triangles != 0.0f; - } - /* mesh light sampling */ - else { - num_samples = ceil_to_int(num_samples_adjust * kernel_data.integrator.mesh_light_samples); - double_pdf = kernel_data.integrator.num_all_lights != 0; - is_mesh_light = true; - } - } - - float num_samples_inv = num_samples_adjust / (num_samples * num_all_lights); - - for (int j = 0; j < num_samples; j++) { - Ray light_ray ccl_optional_struct_init; - light_ray.t = 0.0f; /* reset ray */ -# ifdef __OBJECT_MOTION__ - light_ray.time = sd->time; -# endif - bool has_emission = false; - - if (kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)) { - float light_u, light_v; - path_branched_rng_2D( - kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v); - float terminate = path_branched_rng_light_termination( - kg, lamp_rng_hash, state, j, num_samples); - - /* only sample triangle lights */ - if (is_mesh_light && double_pdf) { - light_u = 0.5f * light_u; - } - - LightSample ls ccl_optional_struct_init; - const int lamp = is_lamp ? i : -1; - if (light_sample(kg, lamp, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - /* The sampling probability returned by lamp_light_sample assumes that all lights were - * sampled. However, this code only samples lamps, so if the scene also had mesh lights, - * the real probability is twice as high. */ - if (double_pdf) { - ls.pdf *= 2.0f; - } - - has_emission = direct_emission( - kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate); - } - } - - /* trace shadow ray */ - float3 shadow; - - const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow); - - if (has_emission) { - if (!blocked) { - /* accumulate */ - path_radiance_accum_light(kg, - L, - state, - throughput * num_samples_inv, - &L_light, - shadow, - num_samples_inv, - is_lamp); - } - else { - path_radiance_accum_total_light(L, state, throughput * num_samples_inv, &L_light); - } - } - } - } -# endif -} - -/* branched path tracing: bounce off or through surface to with new direction stored in ray */ -ccl_device bool kernel_branched_path_surface_bounce(KernelGlobals *kg, - ShaderData *sd, - const ShaderClosure *sc, - int sample, - int num_samples, - ccl_addr_space float3 *throughput, - ccl_addr_space PathState *state, - PathRadianceState *L_state, - ccl_addr_space Ray *ray, - float sum_sample_weight) -{ - /* sample BSDF */ - float bsdf_pdf; - BsdfEval bsdf_eval ccl_optional_struct_init; - float3 bsdf_omega_in ccl_optional_struct_init; - differential3 bsdf_domega_in ccl_optional_struct_init; - float bsdf_u, bsdf_v; - path_branched_rng_2D( - kg, state->rng_hash, state, sample, num_samples, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - int label; - - label = shader_bsdf_sample_closure( - kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval, &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf); - - if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) - return false; - - /* modify throughput */ - path_radiance_bsdf_bounce(kg, L_state, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label); - -# ifdef __DENOISING_FEATURES__ - state->denoising_feature_weight *= sc->sample_weight / (sum_sample_weight * num_samples); -# endif - - /* modify path state */ - path_state_next(kg, state, label); - - /* setup ray */ - ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng); - ray->D = normalize(bsdf_omega_in); - ray->t = FLT_MAX; -# ifdef __RAY_DIFFERENTIALS__ - ray->dP = sd->dP; - ray->dD = bsdf_domega_in; -# endif -# ifdef __OBJECT_MOTION__ - ray->time = sd->time; -# endif - -# ifdef __VOLUME__ - /* enter/exit volume */ - if (label & LABEL_TRANSMIT) - kernel_volume_stack_enter_exit(kg, sd, state->volume_stack); -# endif - - /* branch RNG state */ - path_state_branch(state, sample, num_samples); - - /* set MIS state */ - state->min_ray_pdf = fminf(bsdf_pdf, FLT_MAX); - state->ray_pdf = bsdf_pdf; -# ifdef __LAMP_MIS__ - state->ray_t = 0.0f; -# endif - - return true; -} - -#endif - -/* path tracing: connect path directly to position on a light and add it to L */ -ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - float3 throughput, - ccl_addr_space PathState *state, - PathRadiance *L) -{ - PROFILING_INIT(kg, PROFILING_CONNECT_LIGHT); - -#ifdef __EMISSION__ -# ifdef __SHADOW_TRICKS__ - int all = (state->flag & PATH_RAY_SHADOW_CATCHER); - kernel_branched_path_surface_connect_light(kg, sd, emission_sd, state, throughput, 1.0f, L, all); -# else - /* sample illumination from lights to find path contribution */ - Ray light_ray ccl_optional_struct_init; - BsdfEval L_light ccl_optional_struct_init; - bool is_lamp = false; - bool has_emission = false; - - light_ray.t = 0.0f; -# ifdef __OBJECT_MOTION__ - light_ray.time = sd->time; -# endif - - if (kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)) { - float light_u, light_v; - path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); - - LightSample ls ccl_optional_struct_init; - if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - float terminate = path_state_rng_light_termination(kg, state); - has_emission = direct_emission( - kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate); - } - } - - /* trace shadow ray */ - float3 shadow; - - const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow); - - if (has_emission) { - if (!blocked) { - /* accumulate */ - path_radiance_accum_light(kg, L, state, throughput, &L_light, shadow, 1.0f, is_lamp); - } - else { - path_radiance_accum_total_light(L, state, throughput, &L_light); - } - } -# endif -#endif -} - -/* path tracing: bounce off or through surface to with new direction stored in ray */ -ccl_device bool kernel_path_surface_bounce(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space float3 *throughput, - ccl_addr_space PathState *state, - PathRadianceState *L_state, - ccl_addr_space Ray *ray) -{ - PROFILING_INIT(kg, PROFILING_SURFACE_BOUNCE); - - /* no BSDF? we can stop here */ - if (sd->flag & SD_BSDF) { - /* sample BSDF */ - float bsdf_pdf; - BsdfEval bsdf_eval ccl_optional_struct_init; - float3 bsdf_omega_in ccl_optional_struct_init; - differential3 bsdf_domega_in ccl_optional_struct_init; - float bsdf_u, bsdf_v; - path_state_rng_2D(kg, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - int label; - - label = shader_bsdf_sample( - kg, sd, bsdf_u, bsdf_v, &bsdf_eval, &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf); - - if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) - return false; - - /* modify throughput */ - path_radiance_bsdf_bounce(kg, L_state, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label); - - /* set labels */ - if (!(label & LABEL_TRANSPARENT)) { - state->ray_pdf = bsdf_pdf; -#ifdef __LAMP_MIS__ - state->ray_t = 0.0f; -#endif - state->min_ray_pdf = fminf(bsdf_pdf, state->min_ray_pdf); - } - - /* update path state */ - path_state_next(kg, state, label); - - /* setup ray */ - ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng); - ray->D = normalize(bsdf_omega_in); - - if (state->bounce == 0) - ray->t -= sd->ray_length; /* clipping works through transparent */ - else - ray->t = FLT_MAX; - -#ifdef __RAY_DIFFERENTIALS__ - ray->dP = sd->dP; - ray->dD = bsdf_domega_in; -#endif - -#ifdef __VOLUME__ - /* enter/exit volume */ - if (label & LABEL_TRANSMIT) - kernel_volume_stack_enter_exit(kg, sd, state->volume_stack); -#endif - return true; - } -#ifdef __VOLUME__ - else if (sd->flag & SD_HAS_ONLY_VOLUME) { - if (!path_state_volume_next(kg, state)) { - return false; - } - - if (state->bounce == 0) - ray->t -= sd->ray_length; /* clipping works through transparent */ - else - ray->t = FLT_MAX; - - /* setup ray position, direction stays unchanged */ - ray->P = ray_offset(sd->P, -sd->Ng); -# ifdef __RAY_DIFFERENTIALS__ - ray->dP = sd->dP; -# endif - - /* enter/exit volume */ - kernel_volume_stack_enter_exit(kg, sd, state->volume_stack); - return true; - } -#endif - else { - /* no bsdf or volume? */ - return false; - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_volume.h b/intern/cycles/kernel/kernel_path_volume.h deleted file mode 100644 index a787910e65c..00000000000 --- a/intern/cycles/kernel/kernel_path_volume.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __VOLUME_SCATTER__ - -ccl_device_inline void kernel_path_volume_connect_light(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - float3 throughput, - ccl_addr_space PathState *state, - PathRadiance *L) -{ -# ifdef __EMISSION__ - /* sample illumination from lights to find path contribution */ - Ray light_ray ccl_optional_struct_init; - BsdfEval L_light ccl_optional_struct_init; - bool is_lamp = false; - bool has_emission = false; - - light_ray.t = 0.0f; -# ifdef __OBJECT_MOTION__ - /* connect to light from given point where shader has been evaluated */ - light_ray.time = sd->time; -# endif - - if (kernel_data.integrator.use_direct_light) { - float light_u, light_v; - path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); - - LightSample ls ccl_optional_struct_init; - if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - float terminate = path_state_rng_light_termination(kg, state); - has_emission = direct_emission( - kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate); - } - } - - /* trace shadow ray */ - float3 shadow; - - const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow); - - if (has_emission && !blocked) { - /* accumulate */ - path_radiance_accum_light(kg, L, state, throughput, &L_light, shadow, 1.0f, is_lamp); - } -# endif /* __EMISSION__ */ -} - -ccl_device_noinline_cpu bool kernel_path_volume_bounce(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space float3 *throughput, - ccl_addr_space PathState *state, - PathRadianceState *L_state, - ccl_addr_space Ray *ray) -{ - /* sample phase function */ - float phase_pdf; - BsdfEval phase_eval ccl_optional_struct_init; - float3 phase_omega_in ccl_optional_struct_init; - differential3 phase_domega_in ccl_optional_struct_init; - float phase_u, phase_v; - path_state_rng_2D(kg, state, PRNG_BSDF_U, &phase_u, &phase_v); - int label; - - label = shader_volume_phase_sample( - kg, sd, phase_u, phase_v, &phase_eval, &phase_omega_in, &phase_domega_in, &phase_pdf); - - if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) - return false; - - /* modify throughput */ - path_radiance_bsdf_bounce(kg, L_state, throughput, &phase_eval, phase_pdf, state->bounce, label); - - /* set labels */ - state->ray_pdf = phase_pdf; -# ifdef __LAMP_MIS__ - state->ray_t = 0.0f; -# endif - state->min_ray_pdf = fminf(phase_pdf, state->min_ray_pdf); - - /* update path state */ - path_state_next(kg, state, label); - - /* Russian roulette termination of volume ray scattering. */ - float probability = path_state_continuation_probability(kg, state, *throughput); - - if (probability == 0.0f) { - return false; - } - else if (probability != 1.0f) { - /* Use dimension from the previous bounce, has not been used yet. */ - float terminate = path_state_rng_1D(kg, state, PRNG_TERMINATE - PRNG_BOUNCE_NUM); - - if (terminate >= probability) { - return false; - } - - *throughput /= probability; - } - - /* setup ray */ - ray->P = sd->P; - ray->D = phase_omega_in; - ray->t = FLT_MAX; - -# ifdef __RAY_DIFFERENTIALS__ - ray->dP = sd->dP; - ray->dD = phase_domega_in; -# endif - - return true; -} - -# if !defined(__SPLIT_KERNEL__) && (defined(__BRANCHED_PATH__) || defined(__VOLUME_DECOUPLED__)) -ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - float3 throughput, - ccl_addr_space PathState *state, - PathRadiance *L, - bool sample_all_lights, - Ray *ray, - const VolumeSegment *segment) -{ -# ifdef __EMISSION__ - BsdfEval L_light ccl_optional_struct_init; - - int num_lights = 1; - if (sample_all_lights) { - num_lights = kernel_data.integrator.num_all_lights; - if (kernel_data.integrator.pdf_triangles != 0.0f) { - num_lights += 1; - } - } - - for (int i = 0; i < num_lights; ++i) { - /* sample one light at random */ - int num_samples = 1; - int num_all_lights = 1; - uint lamp_rng_hash = state->rng_hash; - bool double_pdf = false; - bool is_mesh_light = false; - bool is_lamp = false; - - if (sample_all_lights) { - /* lamp sampling */ - is_lamp = i < kernel_data.integrator.num_all_lights; - if (is_lamp) { - if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce))) { - continue; - } - num_samples = light_select_num_samples(kg, i); - num_all_lights = kernel_data.integrator.num_all_lights; - lamp_rng_hash = cmj_hash(state->rng_hash, i); - double_pdf = kernel_data.integrator.pdf_triangles != 0.0f; - } - /* mesh light sampling */ - else { - num_samples = kernel_data.integrator.mesh_light_samples; - double_pdf = kernel_data.integrator.num_all_lights != 0; - is_mesh_light = true; - } - } - - float num_samples_inv = 1.0f / (num_samples * num_all_lights); - - for (int j = 0; j < num_samples; j++) { - Ray light_ray ccl_optional_struct_init; - light_ray.t = 0.0f; /* reset ray */ -# ifdef __OBJECT_MOTION__ - light_ray.time = sd->time; -# endif - bool has_emission = false; - - float3 tp = throughput; - - if (kernel_data.integrator.use_direct_light) { - /* sample random position on random light/triangle */ - float light_u, light_v; - path_branched_rng_2D( - kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v); - - /* only sample triangle lights */ - if (is_mesh_light && double_pdf) { - light_u = 0.5f * light_u; - } - - LightSample ls ccl_optional_struct_init; - const int lamp = is_lamp ? i : -1; - light_sample(kg, lamp, light_u, light_v, sd->time, ray->P, state->bounce, &ls); - - /* sample position on volume segment */ - float rphase = path_branched_rng_1D( - kg, state->rng_hash, state, j, num_samples, PRNG_PHASE_CHANNEL); - float rscatter = path_branched_rng_1D( - kg, state->rng_hash, state, j, num_samples, PRNG_SCATTER_DISTANCE); - - VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg, - state, - ray, - sd, - &tp, - rphase, - rscatter, - segment, - (ls.t != FLT_MAX) ? &ls.P : - NULL, - false); - - if (result == VOLUME_PATH_SCATTERED) { - /* todo: split up light_sample so we don't have to call it again with new position */ - if (light_sample(kg, lamp, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - if (double_pdf) { - ls.pdf *= 2.0f; - } - - /* sample random light */ - float terminate = path_branched_rng_light_termination( - kg, state->rng_hash, state, j, num_samples); - has_emission = direct_emission( - kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate); - } - } - } - - /* trace shadow ray */ - float3 shadow; - - const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow); - - if (has_emission && !blocked) { - /* accumulate */ - path_radiance_accum_light( - kg, L, state, tp * num_samples_inv, &L_light, shadow, num_samples_inv, is_lamp); - } - } - } -# endif /* __EMISSION__ */ -} -# endif /* __SPLIT_KERNEL__ */ - -#endif /* __VOLUME_SCATTER__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_profiling.h b/intern/cycles/kernel/kernel_profiling.h index 780830879d8..db8644005ea 100644 --- a/intern/cycles/kernel/kernel_profiling.h +++ b/intern/cycles/kernel/kernel_profiling.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __KERNEL_PROFILING_H__ -#define __KERNEL_PROFILING_H__ +#pragma once #ifdef __KERNEL_CPU__ # include "util/util_profiling.h" @@ -24,23 +23,18 @@ CCL_NAMESPACE_BEGIN #ifdef __KERNEL_CPU__ -# define PROFILING_INIT(kg, event) ProfilingHelper profiling_helper(&kg->profiler, event) +# define PROFILING_INIT(kg, event) \ + ProfilingHelper profiling_helper((ProfilingState *)&kg->profiler, event) # define PROFILING_EVENT(event) profiling_helper.set_event(event) -# define PROFILING_SHADER(shader) \ - if ((shader) != SHADER_NONE) { \ - profiling_helper.set_shader((shader)&SHADER_MASK); \ - } -# define PROFILING_OBJECT(object) \ - if ((object) != PRIM_NONE) { \ - profiling_helper.set_object(object); \ - } +# define PROFILING_INIT_FOR_SHADER(kg, event) \ + ProfilingWithShaderHelper profiling_helper((ProfilingState *)&kg->profiler, event) +# define PROFILING_SHADER(object, shader) \ + profiling_helper.set_shader(object, (shader)&SHADER_MASK); #else # define PROFILING_INIT(kg, event) # define PROFILING_EVENT(event) -# define PROFILING_SHADER(shader) -# define PROFILING_OBJECT(object) +# define PROFILING_INIT_FOR_SHADER(kg, event) +# define PROFILING_SHADER(object, shader) #endif /* __KERNEL_CPU__ */ CCL_NAMESPACE_END - -#endif /* __KERNEL_PROFILING_H__ */ diff --git a/intern/cycles/kernel/kernel_projection.h b/intern/cycles/kernel/kernel_projection.h index c33d7150b5c..192bf7ca5aa 100644 --- a/intern/cycles/kernel/kernel_projection.h +++ b/intern/cycles/kernel/kernel_projection.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __KERNEL_PROJECTION_CL__ -#define __KERNEL_PROJECTION_CL__ +#pragma once CCL_NAMESPACE_BEGIN @@ -257,5 +256,3 @@ ccl_device_inline void spherical_stereo_transform(ccl_constant KernelCamera *cam } CCL_NAMESPACE_END - -#endif /* __KERNEL_PROJECTION_CL__ */ diff --git a/intern/cycles/kernel/kernel_queues.h b/intern/cycles/kernel/kernel_queues.h deleted file mode 100644 index d8cc08b3e85..00000000000 --- a/intern/cycles/kernel/kernel_queues.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_QUEUE_H__ -#define __KERNEL_QUEUE_H__ - -CCL_NAMESPACE_BEGIN - -/* - * Queue utility functions for split kernel - */ -#ifdef __KERNEL_OPENCL__ -# pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable -# pragma OPENCL EXTENSION cl_khr_local_int32_base_atomics : enable -#endif - -/* - * Enqueue ray index into the queue - */ -ccl_device void enqueue_ray_index( - int ray_index, /* Ray index to be enqueued. */ - int queue_number, /* Queue in which the ray index should be enqueued. */ - ccl_global int *queues, /* Buffer of all queues. */ - int queue_size, /* Size of each queue. */ - ccl_global int *queue_index) /* Array of size num_queues; Used for atomic increment. */ -{ - /* This thread's queue index. */ - int my_queue_index = atomic_fetch_and_inc_uint32((ccl_global uint *)&queue_index[queue_number]) + - (queue_number * queue_size); - queues[my_queue_index] = ray_index; -} - -/* - * Get the ray index for this thread - * Returns a positive ray_index for threads that have to do some work; - * Returns 'QUEUE_EMPTY_SLOT' for threads that don't have any work - * i.e All ray's in the queue has been successfully allocated and there - * is no more ray to allocate to other threads. - */ -ccl_device int get_ray_index( - KernelGlobals *kg, - int thread_index, /* Global thread index. */ - int queue_number, /* Queue to operate on. */ - ccl_global int *queues, /* Buffer of all queues. */ - int queuesize, /* Size of a queue. */ - int empty_queue) /* Empty the queue slot as soon as we fetch the ray index. */ -{ - int ray_index = queues[queue_number * queuesize + thread_index]; - if (empty_queue && ray_index != QUEUE_EMPTY_SLOT) { - queues[queue_number * queuesize + thread_index] = QUEUE_EMPTY_SLOT; - } - return ray_index; -} - -/* The following functions are to realize Local memory variant of enqueue ray index function. */ - -/* All threads should call this function. */ -ccl_device void enqueue_ray_index_local( - int ray_index, /* Ray index to enqueue. */ - int queue_number, /* Queue in which to enqueue ray index. */ - char enqueue_flag, /* True for threads whose ray index has to be enqueued. */ - int queuesize, /* queue size. */ - ccl_local_param unsigned int *local_queue_atomics, /* To do local queue atomics. */ - ccl_global int *Queue_data, /* Queues. */ - ccl_global int *Queue_index) /* To do global queue atomics. */ -{ - int lidx = ccl_local_id(1) * ccl_local_size(0) + ccl_local_id(0); - - /* Get local queue id. */ - unsigned int lqidx; - if (enqueue_flag) { - lqidx = atomic_fetch_and_inc_uint32(local_queue_atomics); - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - /* Get global queue offset. */ - if (lidx == 0) { - *local_queue_atomics = atomic_fetch_and_add_uint32( - (ccl_global uint *)&Queue_index[queue_number], *local_queue_atomics); - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - /* Get global queue index and enqueue ray. */ - if (enqueue_flag) { - unsigned int my_gqidx = queue_number * queuesize + (*local_queue_atomics) + lqidx; - Queue_data[my_gqidx] = ray_index; - } -} - -ccl_device unsigned int get_local_queue_index( - int queue_number, /* Queue in which to enqueue the ray; -1 if no queue */ - ccl_local_param unsigned int *local_queue_atomics) -{ - int my_lqidx = atomic_fetch_and_inc_uint32(&local_queue_atomics[queue_number]); - return my_lqidx; -} - -ccl_device unsigned int get_global_per_queue_offset( - int queue_number, - ccl_local_param unsigned int *local_queue_atomics, - ccl_global int *global_queue_atomics) -{ - unsigned int queue_offset = atomic_fetch_and_add_uint32( - (ccl_global uint *)&global_queue_atomics[queue_number], local_queue_atomics[queue_number]); - return queue_offset; -} - -ccl_device unsigned int get_global_queue_index( - int queue_number, - int queuesize, - unsigned int lqidx, - ccl_local_param unsigned int *global_per_queue_offset) -{ - int my_gqidx = queuesize * queue_number + lqidx + global_per_queue_offset[queue_number]; - return my_gqidx; -} - -ccl_device int dequeue_ray_index(int queue_number, - ccl_global int *queues, - int queue_size, - ccl_global int *queue_index) -{ - int index = atomic_fetch_and_dec_uint32((ccl_global uint *)&queue_index[queue_number]) - 1; - - if (index < 0) { - return QUEUE_EMPTY_SLOT; - } - - return queues[index + queue_number * queue_size]; -} - -CCL_NAMESPACE_END - -#endif // __KERNEL_QUEUE_H__ diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h index 49e5e25c2e0..41b7d76230a 100644 --- a/intern/cycles/kernel/kernel_random.h +++ b/intern/cycles/kernel/kernel_random.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #include "kernel/kernel_jitter.h" #include "util/util_hash.h" @@ -37,38 +38,34 @@ CCL_NAMESPACE_BEGIN */ # define SOBOL_SKIP 64 -ccl_device uint sobol_dimension(KernelGlobals *kg, int index, int dimension) +ccl_device uint sobol_dimension(const KernelGlobals *kg, int index, int dimension) { uint result = 0; uint i = index + SOBOL_SKIP; for (int j = 0, x; (x = find_first_set(i)); i >>= x) { j += x; - result ^= kernel_tex_fetch(__sample_pattern_lut, 32 * dimension + j - 1); + result ^= __float_as_uint(kernel_tex_fetch(__sample_pattern_lut, 32 * dimension + j - 1)); } return result; } #endif /* __SOBOL__ */ -ccl_device_forceinline float path_rng_1D( - KernelGlobals *kg, uint rng_hash, int sample, int num_samples, int dimension) +ccl_device_forceinline float path_rng_1D(const KernelGlobals *kg, + uint rng_hash, + int sample, + int dimension) { #ifdef __DEBUG_CORRELATION__ return (float)drand48(); #endif - if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ) { + +#ifdef __SOBOL__ + if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ) +#endif + { return pmj_sample_1D(kg, sample, rng_hash, dimension); } -#ifdef __CMJ__ -# ifdef __SOBOL__ - if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_CMJ) -# endif - { - /* Correlated multi-jitter. */ - int p = rng_hash + dimension; - return cmj_sample_1D(sample, num_samples, p); - } -#endif #ifdef __SOBOL__ /* Sobol sequence value using direction vectors. */ @@ -88,68 +85,72 @@ ccl_device_forceinline float path_rng_1D( #endif } -ccl_device_forceinline void path_rng_2D(KernelGlobals *kg, - uint rng_hash, - int sample, - int num_samples, - int dimension, - float *fx, - float *fy) +ccl_device_forceinline void path_rng_2D( + const KernelGlobals *kg, uint rng_hash, int sample, int dimension, float *fx, float *fy) { #ifdef __DEBUG_CORRELATION__ *fx = (float)drand48(); *fy = (float)drand48(); return; #endif - if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ) { - const float2 f = pmj_sample_2D(kg, sample, rng_hash, dimension); - *fx = f.x; - *fy = f.y; - return; - } -#ifdef __CMJ__ -# ifdef __SOBOL__ - if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_CMJ) -# endif - { - /* Correlated multi-jitter. */ - int p = rng_hash + dimension; - cmj_sample_2D(sample, num_samples, p, fx, fy); - return; - } + +#ifdef __SOBOL__ + if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ) #endif + { + pmj_sample_2D(kg, sample, rng_hash, dimension, fx, fy); + + return; + } #ifdef __SOBOL__ /* Sobol. */ - *fx = path_rng_1D(kg, rng_hash, sample, num_samples, dimension); - *fy = path_rng_1D(kg, rng_hash, sample, num_samples, dimension + 1); + *fx = path_rng_1D(kg, rng_hash, sample, dimension); + *fy = path_rng_1D(kg, rng_hash, sample, dimension + 1); #endif } -ccl_device_inline void path_rng_init(KernelGlobals *kg, - int sample, - int num_samples, - uint *rng_hash, - int x, - int y, - float *fx, - float *fy) +/** + * 1D hash recomended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 + * See https://www.shadertoy.com/view/4tXyWN and https://www.shadertoy.com/view/XlGcRh + * http://www.jcgt.org/published/0009/03/02/paper.pdf + */ +ccl_device_inline uint hash_iqint1(uint n) { - /* load state */ - *rng_hash = hash_uint2(x, y); - *rng_hash ^= kernel_data.integrator.seed; + n = (n << 13U) ^ n; + n = n * (n * n * 15731U + 789221U) + 1376312589U; + + return n; +} + +/** + * 2D hash recomended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 + * See https://www.shadertoy.com/view/4tXyWN and https://www.shadertoy.com/view/XlGcRh + * http://www.jcgt.org/published/0009/03/02/paper.pdf + */ +ccl_device_inline uint hash_iqnt2d(const uint x, const uint y) +{ + const uint qx = 1103515245U * ((x >> 1U) ^ (y)); + const uint qy = 1103515245U * ((y >> 1U) ^ (x)); + const uint n = 1103515245U * ((qx) ^ (qy >> 3U)); + + return n; +} + +ccl_device_inline uint path_rng_hash_init(const KernelGlobals *ccl_restrict kg, + const int sample, + const int x, + const int y) +{ + const uint rng_hash = hash_iqnt2d(x, y) ^ kernel_data.integrator.seed; #ifdef __DEBUG_CORRELATION__ - srand48(*rng_hash + sample); + srand48(rng_hash + sample); +#else + (void)sample; #endif - if (sample == 0) { - *fx = 0.5f; - *fy = 0.5f; - } - else { - path_rng_2D(kg, *rng_hash, sample, num_samples, PRNG_FILTER_U, fx, fy); - } + return rng_hash; } /* Linear Congruential Generator */ @@ -175,113 +176,12 @@ ccl_device uint lcg_init(uint seed) return rng; } -/* Path Tracing Utility Functions - * - * For each random number in each step of the path we must have a unique - * dimension to avoid using the same sequence twice. - * - * For branches in the path we must be careful not to reuse the same number - * in a sequence and offset accordingly. - */ - -ccl_device_inline float path_state_rng_1D(KernelGlobals *kg, - const ccl_addr_space PathState *state, - int dimension) +ccl_device_inline uint lcg_state_init(const uint rng_hash, + const uint rng_offset, + const uint sample, + const uint scramble) { - return path_rng_1D( - kg, state->rng_hash, state->sample, state->num_samples, state->rng_offset + dimension); -} - -ccl_device_inline void path_state_rng_2D( - KernelGlobals *kg, const ccl_addr_space PathState *state, int dimension, float *fx, float *fy) -{ - path_rng_2D(kg, - state->rng_hash, - state->sample, - state->num_samples, - state->rng_offset + dimension, - fx, - fy); -} - -ccl_device_inline float path_state_rng_1D_hash(KernelGlobals *kg, - const ccl_addr_space PathState *state, - uint hash) -{ - /* Use a hash instead of dimension, this is not great but avoids adding - * more dimensions to each bounce which reduces quality of dimensions we - * are already using. */ - return path_rng_1D(kg, - cmj_hash_simple(state->rng_hash, hash), - state->sample, - state->num_samples, - state->rng_offset); -} - -ccl_device_inline float path_branched_rng_1D(KernelGlobals *kg, - uint rng_hash, - const ccl_addr_space PathState *state, - int branch, - int num_branches, - int dimension) -{ - return path_rng_1D(kg, - rng_hash, - state->sample * num_branches + branch, - state->num_samples * num_branches, - state->rng_offset + dimension); -} - -ccl_device_inline void path_branched_rng_2D(KernelGlobals *kg, - uint rng_hash, - const ccl_addr_space PathState *state, - int branch, - int num_branches, - int dimension, - float *fx, - float *fy) -{ - path_rng_2D(kg, - rng_hash, - state->sample * num_branches + branch, - state->num_samples * num_branches, - state->rng_offset + dimension, - fx, - fy); -} - -/* Utility functions to get light termination value, - * since it might not be needed in many cases. - */ -ccl_device_inline float path_state_rng_light_termination(KernelGlobals *kg, - const ccl_addr_space PathState *state) -{ - if (kernel_data.integrator.light_inv_rr_threshold > 0.0f) { - return path_state_rng_1D(kg, state, PRNG_LIGHT_TERMINATE); - } - return 0.0f; -} - -ccl_device_inline float path_branched_rng_light_termination(KernelGlobals *kg, - uint rng_hash, - const ccl_addr_space PathState *state, - int branch, - int num_branches) -{ - if (kernel_data.integrator.light_inv_rr_threshold > 0.0f) { - return path_branched_rng_1D(kg, rng_hash, state, branch, num_branches, PRNG_LIGHT_TERMINATE); - } - return 0.0f; -} - -ccl_device_inline uint lcg_state_init(PathState *state, uint scramble) -{ - return lcg_init(state->rng_hash + state->rng_offset + state->sample * scramble); -} - -ccl_device_inline uint lcg_state_init_addrspace(ccl_addr_space PathState *state, uint scramble) -{ - return lcg_init(state->rng_hash + state->rng_offset + state->sample * scramble); + return lcg_init(rng_hash + rng_offset + sample * scramble); } ccl_device float lcg_step_float_addrspace(ccl_addr_space uint *rng) @@ -301,8 +201,6 @@ ccl_device_inline bool sample_is_even(int pattern, int sample) return __builtin_popcount(sample & 0xaaaaaaaa) & 1; #elif defined(__NVCC__) return __popc(sample & 0xaaaaaaaa) & 1; -#elif defined(__KERNEL_OPENCL__) - return popcount(sample & 0xaaaaaaaa) & 1; #else /* TODO(Stefan): pop-count intrinsic for Windows with fallback for older CPUs. */ int i = sample & 0xaaaaaaaa; diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 7f02e6fc7b3..3052bb53040 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -14,14 +14,9 @@ * limitations under the License. */ -/* - * ShaderData, used in four steps: - * - * Setup from incoming ray, sampled position and background. - * Execute for surface, volume or displacement. - * Evaluate one or more closures. - * Release. - */ +/* Functions to evaluate shaders and use the resulting shader closures. */ + +#pragma once // clang-format off #include "kernel/closure/alloc.h" @@ -30,479 +25,39 @@ #include "kernel/closure/emissive.h" // clang-format on +#include "kernel/kernel_accumulate.h" #include "kernel/svm/svm.h" +#ifdef __OSL__ +# include "kernel/osl/osl_shader.h" +#endif + CCL_NAMESPACE_BEGIN -/* ShaderData setup from incoming ray */ - -#ifdef __OBJECT_MOTION__ -ccl_device void shader_setup_object_transforms(KernelGlobals *kg, ShaderData *sd, float time) -{ - if (sd->object_flag & SD_OBJECT_MOTION) { - sd->ob_tfm = object_fetch_transform_motion(kg, sd->object, time); - sd->ob_itfm = transform_quick_inverse(sd->ob_tfm); - } - else { - sd->ob_tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); - sd->ob_itfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); - } -} -#endif - -#ifdef __KERNEL_OPTIX__ -ccl_device_inline -#else -ccl_device_noinline -#endif - void - shader_setup_from_ray(KernelGlobals *kg, - ShaderData *sd, - const Intersection *isect, - const Ray *ray) -{ - PROFILING_INIT(kg, PROFILING_SHADER_SETUP); - - sd->object = (isect->object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, isect->prim) : - isect->object; - sd->lamp = LAMP_NONE; - - sd->type = isect->type; - sd->flag = 0; - sd->object_flag = kernel_tex_fetch(__object_flag, sd->object); - - /* matrices and time */ -#ifdef __OBJECT_MOTION__ - shader_setup_object_transforms(kg, sd, ray->time); -#endif - sd->time = ray->time; - - sd->prim = kernel_tex_fetch(__prim_index, isect->prim); - sd->ray_length = isect->t; - - sd->u = isect->u; - sd->v = isect->v; - -#ifdef __HAIR__ - if (sd->type & PRIMITIVE_ALL_CURVE) { - /* curve */ - curve_shader_setup(kg, sd, isect, ray); - } - else -#endif - if (sd->type & PRIMITIVE_TRIANGLE) { - /* static triangle */ - float3 Ng = triangle_normal(kg, sd); - sd->shader = kernel_tex_fetch(__tri_shader, sd->prim); - - /* vectors */ - sd->P = triangle_refine(kg, sd, isect, ray); - sd->Ng = Ng; - sd->N = Ng; - - /* smooth normal */ - if (sd->shader & SHADER_SMOOTH_NORMAL) - sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); - -#ifdef __DPDU__ - /* dPdu/dPdv */ - triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); -#endif - } - else { - /* motion triangle */ - motion_triangle_shader_setup(kg, sd, isect, ray, false); - } - - sd->I = -ray->D; - - sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; - - if (isect->object != OBJECT_NONE) { - /* instance transform */ - object_normal_transform_auto(kg, sd, &sd->N); - object_normal_transform_auto(kg, sd, &sd->Ng); -#ifdef __DPDU__ - object_dir_transform_auto(kg, sd, &sd->dPdu); - object_dir_transform_auto(kg, sd, &sd->dPdv); -#endif - } - - /* backfacing test */ - bool backfacing = (dot(sd->Ng, sd->I) < 0.0f); - - if (backfacing) { - sd->flag |= SD_BACKFACING; - sd->Ng = -sd->Ng; - sd->N = -sd->N; -#ifdef __DPDU__ - sd->dPdu = -sd->dPdu; - sd->dPdv = -sd->dPdv; -#endif - } - -#ifdef __RAY_DIFFERENTIALS__ - /* differentials */ - differential_transfer(&sd->dP, ray->dP, ray->D, ray->dD, sd->Ng, isect->t); - differential_incoming(&sd->dI, ray->dD); - differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng); -#endif - - PROFILING_SHADER(sd->shader); - PROFILING_OBJECT(sd->object); -} - -/* ShaderData setup from BSSRDF scatter */ - -#ifdef __SUBSURFACE__ -# ifndef __KERNEL_CUDA__ -ccl_device -# else -ccl_device_inline -# endif - void - shader_setup_from_subsurface(KernelGlobals *kg, - ShaderData *sd, - const Intersection *isect, - const Ray *ray) -{ - PROFILING_INIT(kg, PROFILING_SHADER_SETUP); - - const bool backfacing = sd->flag & SD_BACKFACING; - - /* object, matrices, time, ray_length stay the same */ - sd->flag = 0; - sd->object_flag = kernel_tex_fetch(__object_flag, sd->object); - sd->prim = kernel_tex_fetch(__prim_index, isect->prim); - sd->type = isect->type; - - sd->u = isect->u; - sd->v = isect->v; - - /* fetch triangle data */ - if (sd->type == PRIMITIVE_TRIANGLE) { - float3 Ng = triangle_normal(kg, sd); - sd->shader = kernel_tex_fetch(__tri_shader, sd->prim); - - /* static triangle */ - sd->P = triangle_refine_local(kg, sd, isect, ray); - sd->Ng = Ng; - sd->N = Ng; - - if (sd->shader & SHADER_SMOOTH_NORMAL) - sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); - -# ifdef __DPDU__ - /* dPdu/dPdv */ - triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); -# endif - } - else { - /* motion triangle */ - motion_triangle_shader_setup(kg, sd, isect, ray, true); - } - - sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; - - if (isect->object != OBJECT_NONE) { - /* instance transform */ - object_normal_transform_auto(kg, sd, &sd->N); - object_normal_transform_auto(kg, sd, &sd->Ng); -# ifdef __DPDU__ - object_dir_transform_auto(kg, sd, &sd->dPdu); - object_dir_transform_auto(kg, sd, &sd->dPdv); -# endif - } - - /* backfacing test */ - if (backfacing) { - sd->flag |= SD_BACKFACING; - sd->Ng = -sd->Ng; - sd->N = -sd->N; -# ifdef __DPDU__ - sd->dPdu = -sd->dPdu; - sd->dPdv = -sd->dPdv; -# endif - } - - /* should not get used in principle as the shading will only use a diffuse - * BSDF, but the shader might still access it */ - sd->I = sd->N; - -# ifdef __RAY_DIFFERENTIALS__ - /* differentials */ - differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng); - /* don't modify dP and dI */ -# endif - - PROFILING_SHADER(sd->shader); -} -#endif - -/* ShaderData setup from position sampled on mesh */ - -ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg, - ShaderData *sd, - const float3 P, - const float3 Ng, - const float3 I, - int shader, - int object, - int prim, - float u, - float v, - float t, - float time, - bool object_space, - int lamp) -{ - PROFILING_INIT(kg, PROFILING_SHADER_SETUP); - - /* vectors */ - sd->P = P; - sd->N = Ng; - sd->Ng = Ng; - sd->I = I; - sd->shader = shader; - if (prim != PRIM_NONE) - sd->type = PRIMITIVE_TRIANGLE; - else if (lamp != LAMP_NONE) - sd->type = PRIMITIVE_LAMP; - else - sd->type = PRIMITIVE_NONE; - - /* primitive */ - sd->object = object; - sd->lamp = LAMP_NONE; - /* Currently no access to bvh prim index for strand sd->prim. */ - sd->prim = prim; - sd->u = u; - sd->v = v; - sd->time = time; - sd->ray_length = t; - - sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; - sd->object_flag = 0; - if (sd->object != OBJECT_NONE) { - sd->object_flag |= kernel_tex_fetch(__object_flag, sd->object); - -#ifdef __OBJECT_MOTION__ - shader_setup_object_transforms(kg, sd, time); - } - else if (lamp != LAMP_NONE) { - sd->ob_tfm = lamp_fetch_transform(kg, lamp, false); - sd->ob_itfm = lamp_fetch_transform(kg, lamp, true); - sd->lamp = lamp; -#else - } - else if (lamp != LAMP_NONE) { - sd->lamp = lamp; -#endif - } - - /* transform into world space */ - if (object_space) { - object_position_transform_auto(kg, sd, &sd->P); - object_normal_transform_auto(kg, sd, &sd->Ng); - sd->N = sd->Ng; - object_dir_transform_auto(kg, sd, &sd->I); - } - - if (sd->type & PRIMITIVE_TRIANGLE) { - /* smooth normal */ - if (sd->shader & SHADER_SMOOTH_NORMAL) { - sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); - - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - object_normal_transform_auto(kg, sd, &sd->N); - } - } - - /* dPdu/dPdv */ -#ifdef __DPDU__ - triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); - - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - object_dir_transform_auto(kg, sd, &sd->dPdu); - object_dir_transform_auto(kg, sd, &sd->dPdv); - } -#endif - } - else { -#ifdef __DPDU__ - sd->dPdu = zero_float3(); - sd->dPdv = zero_float3(); -#endif - } - - /* backfacing test */ - if (sd->prim != PRIM_NONE) { - bool backfacing = (dot(sd->Ng, sd->I) < 0.0f); - - if (backfacing) { - sd->flag |= SD_BACKFACING; - sd->Ng = -sd->Ng; - sd->N = -sd->N; -#ifdef __DPDU__ - sd->dPdu = -sd->dPdu; - sd->dPdv = -sd->dPdv; -#endif - } - } - -#ifdef __RAY_DIFFERENTIALS__ - /* no ray differentials here yet */ - sd->dP = differential3_zero(); - sd->dI = differential3_zero(); - sd->du = differential_zero(); - sd->dv = differential_zero(); -#endif - - PROFILING_SHADER(sd->shader); - PROFILING_OBJECT(sd->object); -} - -/* ShaderData setup for displacement */ - -ccl_device void shader_setup_from_displace( - KernelGlobals *kg, ShaderData *sd, int object, int prim, float u, float v) -{ - float3 P, Ng, I = zero_float3(); - int shader; - - triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader); - - /* force smooth shading for displacement */ - shader |= SHADER_SMOOTH_NORMAL; - - shader_setup_from_sample( - kg, - sd, - P, - Ng, - I, - shader, - object, - prim, - u, - v, - 0.0f, - 0.5f, - !(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED), - LAMP_NONE); -} - -/* ShaderData setup from ray into background */ - -ccl_device_inline void shader_setup_from_background(KernelGlobals *kg, - ShaderData *sd, - const Ray *ray) -{ - PROFILING_INIT(kg, PROFILING_SHADER_SETUP); - - /* vectors */ - sd->P = ray->D; - sd->N = -ray->D; - sd->Ng = -ray->D; - sd->I = -ray->D; - sd->shader = kernel_data.background.surface_shader; - sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; - sd->object_flag = 0; - sd->time = ray->time; - sd->ray_length = 0.0f; - - sd->object = OBJECT_NONE; - sd->lamp = LAMP_NONE; - sd->prim = PRIM_NONE; - sd->u = 0.0f; - sd->v = 0.0f; - -#ifdef __DPDU__ - /* dPdu/dPdv */ - sd->dPdu = zero_float3(); - sd->dPdv = zero_float3(); -#endif - -#ifdef __RAY_DIFFERENTIALS__ - /* differentials */ - sd->dP = ray->dD; - differential_incoming(&sd->dI, sd->dP); - sd->du = differential_zero(); - sd->dv = differential_zero(); -#endif - - /* for NDC coordinates */ - sd->ray_P = ray->P; - - PROFILING_SHADER(sd->shader); - PROFILING_OBJECT(sd->object); -} - -/* ShaderData setup from point inside volume */ - -#ifdef __VOLUME__ -ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *sd, const Ray *ray) -{ - PROFILING_INIT(kg, PROFILING_SHADER_SETUP); - - /* vectors */ - sd->P = ray->P; - sd->N = -ray->D; - sd->Ng = -ray->D; - sd->I = -ray->D; - sd->shader = SHADER_NONE; - sd->flag = 0; - sd->object_flag = 0; - sd->time = ray->time; - sd->ray_length = 0.0f; /* todo: can we set this to some useful value? */ - - sd->object = OBJECT_NONE; /* todo: fill this for texture coordinates */ - sd->lamp = LAMP_NONE; - sd->prim = PRIM_NONE; - sd->type = PRIMITIVE_NONE; - - sd->u = 0.0f; - sd->v = 0.0f; - -# ifdef __DPDU__ - /* dPdu/dPdv */ - sd->dPdu = zero_float3(); - sd->dPdv = zero_float3(); -# endif - -# ifdef __RAY_DIFFERENTIALS__ - /* differentials */ - sd->dP = ray->dD; - differential_incoming(&sd->dI, sd->dP); - sd->du = differential_zero(); - sd->dv = differential_zero(); -# endif - - /* for NDC coordinates */ - sd->ray_P = ray->P; - sd->ray_dP = ray->dP; - - PROFILING_SHADER(sd->shader); - PROFILING_OBJECT(sd->object); -} -#endif /* __VOLUME__ */ - /* Merging */ -#if defined(__BRANCHED_PATH__) || defined(__VOLUME__) -ccl_device_inline void shader_merge_closures(ShaderData *sd) +#if defined(__VOLUME__) +ccl_device_inline void shader_merge_volume_closures(ShaderData *sd) { - /* merge identical closures, better when we sample a single closure at a time */ + /* Merge identical closures to save closure space with stacked volumes. */ for (int i = 0; i < sd->num_closure; i++) { ShaderClosure *sci = &sd->closure[i]; + if (sci->type != CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { + continue; + } + for (int j = i + 1; j < sd->num_closure; j++) { ShaderClosure *scj = &sd->closure[j]; + if (sci->type != scj->type) { + continue; + } - if (sci->type != scj->type) - continue; - if (!bsdf_merge(sci, scj)) + const HenyeyGreensteinVolume *hgi = (const HenyeyGreensteinVolume *)sci; + const HenyeyGreensteinVolume *hgj = (const HenyeyGreensteinVolume *)scj; + if (!(hgi->g == hgj->g)) { continue; + } sci->weight += scj->weight; sci->sample_weight += scj->sample_weight; @@ -520,16 +75,40 @@ ccl_device_inline void shader_merge_closures(ShaderData *sd) } } } -#endif /* __BRANCHED_PATH__ || __VOLUME__ */ -/* Defensive sampling. */ - -ccl_device_inline void shader_prepare_closures(ShaderData *sd, ccl_addr_space PathState *state) +ccl_device_inline void shader_copy_volume_phases(ShaderVolumePhases *ccl_restrict phases, + const ShaderData *ccl_restrict sd) { - /* We can likely also do defensive sampling at deeper bounces, particularly + phases->num_closure = 0; + + for (int i = 0; i < sd->num_closure; i++) { + const ShaderClosure *from_sc = &sd->closure[i]; + const HenyeyGreensteinVolume *from_hg = (const HenyeyGreensteinVolume *)from_sc; + + if (from_sc->type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { + ShaderVolumeClosure *to_sc = &phases->closure[phases->num_closure]; + + to_sc->weight = from_sc->weight; + to_sc->sample_weight = from_sc->sample_weight; + to_sc->g = from_hg->g; + phases->num_closure++; + if (phases->num_closure >= MAX_VOLUME_CLOSURE) { + break; + } + } + } +} +#endif /* __VOLUME__ */ + +ccl_device_inline void shader_prepare_surface_closures(INTEGRATOR_STATE_CONST_ARGS, ShaderData *sd) +{ + /* Defensive sampling. + * + * We can likely also do defensive sampling at deeper bounces, particularly * for cases like a perfect mirror but possibly also others. This will need * a good heuristic. */ - if (state->bounce + state->transparent_bounce == 0 && sd->num_closure > 1) { + if (INTEGRATOR_STATE(path, bounce) + INTEGRATOR_STATE(path, transparent_bounce) == 0 && + sd->num_closure > 1) { float sum = 0.0f; for (int i = 0; i < sd->num_closure; i++) { @@ -546,98 +125,119 @@ ccl_device_inline void shader_prepare_closures(ShaderData *sd, ccl_addr_space Pa } } } + + /* Filter glossy. + * + * Blurring of bsdf after bounces, for rays that have a small likelihood + * of following this particular path (diffuse, rough glossy) */ + if (kernel_data.integrator.filter_glossy != FLT_MAX) { + float blur_pdf = kernel_data.integrator.filter_glossy * INTEGRATOR_STATE(path, min_ray_pdf); + + if (blur_pdf < 1.0f) { + float blur_roughness = sqrtf(1.0f - blur_pdf) * 0.5f; + + for (int i = 0; i < sd->num_closure; i++) { + ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_BSDF(sc->type)) { + bsdf_blur(kg, sc, blur_roughness); + } + } + } + } } /* BSDF */ -ccl_device_inline void _shader_bsdf_multi_eval(KernelGlobals *kg, - ShaderData *sd, - const float3 omega_in, - float *pdf, - const ShaderClosure *skip_sc, - BsdfEval *result_eval, - float sum_pdf, - float sum_sample_weight) +ccl_device_inline bool shader_bsdf_is_transmission(const ShaderData *sd, const float3 omega_in) +{ + return dot(sd->N, omega_in) < 0.0f; +} + +ccl_device_forceinline bool _shader_bsdf_exclude(ClosureType type, uint light_shader_flags) +{ + if (!(light_shader_flags & SHADER_EXCLUDE_ANY)) { + return false; + } + if (light_shader_flags & SHADER_EXCLUDE_DIFFUSE) { + if (CLOSURE_IS_BSDF_DIFFUSE(type) || CLOSURE_IS_BSDF_BSSRDF(type)) { + return true; + } + } + if (light_shader_flags & SHADER_EXCLUDE_GLOSSY) { + if (CLOSURE_IS_BSDF_GLOSSY(type)) { + return true; + } + } + if (light_shader_flags & SHADER_EXCLUDE_TRANSMIT) { + if (CLOSURE_IS_BSDF_TRANSMISSION(type)) { + return true; + } + } + return false; +} + +ccl_device_inline float _shader_bsdf_multi_eval(const KernelGlobals *kg, + ShaderData *sd, + const float3 omega_in, + const bool is_transmission, + const ShaderClosure *skip_sc, + BsdfEval *result_eval, + float sum_pdf, + float sum_sample_weight, + const uint light_shader_flags) { /* this is the veach one-sample model with balance heuristic, some pdf * factors drop out when using balance heuristic weighting */ for (int i = 0; i < sd->num_closure; i++) { const ShaderClosure *sc = &sd->closure[i]; - if (sc != skip_sc && CLOSURE_IS_BSDF(sc->type)) { - float bsdf_pdf = 0.0f; - float3 eval = bsdf_eval(kg, sd, sc, omega_in, &bsdf_pdf); + if (sc == skip_sc) { + continue; + } - if (bsdf_pdf != 0.0f) { - bsdf_eval_accum(result_eval, sc->type, eval * sc->weight, 1.0f); - sum_pdf += bsdf_pdf * sc->sample_weight; + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + if (CLOSURE_IS_BSDF(sc->type) && !_shader_bsdf_exclude(sc->type, light_shader_flags)) { + float bsdf_pdf = 0.0f; + float3 eval = bsdf_eval(kg, sd, sc, omega_in, is_transmission, &bsdf_pdf); + + if (bsdf_pdf != 0.0f) { + const bool is_diffuse = (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || + CLOSURE_IS_BSDF_BSSRDF(sc->type)); + bsdf_eval_accum(result_eval, is_diffuse, eval * sc->weight, 1.0f); + sum_pdf += bsdf_pdf * sc->sample_weight; + } } sum_sample_weight += sc->sample_weight; } } - *pdf = (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; + return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; } -#ifdef __BRANCHED_PATH__ -ccl_device_inline void _shader_bsdf_multi_eval_branched(KernelGlobals *kg, - ShaderData *sd, - const float3 omega_in, - BsdfEval *result_eval, - float light_pdf, - bool use_mis) -{ - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_BSDF(sc->type)) { - float bsdf_pdf = 0.0f; - float3 eval = bsdf_eval(kg, sd, sc, omega_in, &bsdf_pdf); - if (bsdf_pdf != 0.0f) { - float mis_weight = use_mis ? power_heuristic(light_pdf, bsdf_pdf) : 1.0f; - bsdf_eval_accum(result_eval, sc->type, eval * sc->weight, mis_weight); - } - } - } -} -#endif /* __BRANCHED_PATH__ */ - #ifndef __KERNEL_CUDA__ ccl_device #else ccl_device_inline #endif - void - shader_bsdf_eval(KernelGlobals *kg, + float + shader_bsdf_eval(const KernelGlobals *kg, ShaderData *sd, const float3 omega_in, - BsdfEval *eval, - float light_pdf, - bool use_mis) + const bool is_transmission, + BsdfEval *bsdf_eval, + const uint light_shader_flags) { - PROFILING_INIT(kg, PROFILING_CLOSURE_EVAL); + bsdf_eval_init(bsdf_eval, false, zero_float3()); - bsdf_eval_init(eval, NBUILTIN_CLOSURES, zero_float3(), kernel_data.film.use_light_pass); - -#ifdef __BRANCHED_PATH__ - if (kernel_data.integrator.branched) - _shader_bsdf_multi_eval_branched(kg, sd, omega_in, eval, light_pdf, use_mis); - else -#endif - { - float pdf; - _shader_bsdf_multi_eval(kg, sd, omega_in, &pdf, NULL, eval, 0.0f, 0.0f); - if (use_mis) { - float weight = power_heuristic(light_pdf, pdf); - bsdf_eval_mis(eval, weight); - } - } + return _shader_bsdf_multi_eval( + kg, sd, omega_in, is_transmission, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags); } -ccl_device_inline const ShaderClosure *shader_bsdf_pick(ShaderData *sd, float *randu) +/* Randomly sample a BSSRDF or BSDF proportional to ShaderClosure.sample_weight. */ +ccl_device_inline const ShaderClosure *shader_bsdf_bssrdf_pick(const ShaderData *ccl_restrict sd, + float *randu) { - /* Note the sampling here must match shader_bssrdf_pick, - * since we reuse the same random number. */ int sampled = 0; if (sd->num_closure > 1) { @@ -674,84 +274,42 @@ ccl_device_inline const ShaderClosure *shader_bsdf_pick(ShaderData *sd, float *r } } - const ShaderClosure *sc = &sd->closure[sampled]; - return CLOSURE_IS_BSDF(sc->type) ? sc : NULL; + return &sd->closure[sampled]; } -ccl_device_inline const ShaderClosure *shader_bssrdf_pick(ShaderData *sd, - ccl_addr_space float3 *throughput, - float *randu) +/* Return weight for picked BSSRDF. */ +ccl_device_inline float3 shader_bssrdf_sample_weight(const ShaderData *ccl_restrict sd, + const ShaderClosure *ccl_restrict bssrdf_sc) { - /* Note the sampling here must match shader_bsdf_pick, - * since we reuse the same random number. */ - int sampled = 0; + float3 weight = bssrdf_sc->weight; if (sd->num_closure > 1) { - /* Pick a BSDF or BSSRDF or based on sample weights. */ - float sum_bsdf = 0.0f; - float sum_bssrdf = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF(sc->type)) { - sum_bsdf += sc->sample_weight; - } - else if (CLOSURE_IS_BSSRDF(sc->type)) { - sum_bssrdf += sc->sample_weight; - } - } - - float r = (*randu) * (sum_bsdf + sum_bssrdf); - float partial_sum = 0.0f; - + float sum = 0.0f; for (int i = 0; i < sd->num_closure; i++) { const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - float next_sum = partial_sum + sc->sample_weight; - - if (r < next_sum) { - if (CLOSURE_IS_BSDF(sc->type)) { - *throughput *= (sum_bsdf + sum_bssrdf) / sum_bsdf; - return NULL; - } - else { - *throughput *= (sum_bsdf + sum_bssrdf) / sum_bssrdf; - sampled = i; - - /* Rescale to reuse for direction sample, to better preserve stratification. */ - *randu = (r - partial_sum) / sc->sample_weight; - break; - } - } - - partial_sum = next_sum; + sum += sc->sample_weight; } } + weight *= sum / bssrdf_sc->sample_weight; } - const ShaderClosure *sc = &sd->closure[sampled]; - return CLOSURE_IS_BSSRDF(sc->type) ? sc : NULL; + return weight; } -ccl_device_inline int shader_bsdf_sample(KernelGlobals *kg, - ShaderData *sd, - float randu, - float randv, - BsdfEval *bsdf_eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) +/* Sample direction for picked BSDF, and return evaluation and pdf for all + * BSDFs combined using MIS. */ +ccl_device int shader_bsdf_sample_closure(const KernelGlobals *kg, + ShaderData *sd, + const ShaderClosure *sc, + float randu, + float randv, + BsdfEval *bsdf_eval, + float3 *omega_in, + differential3 *domega_in, + float *pdf) { - PROFILING_INIT(kg, PROFILING_CLOSURE_SAMPLE); - - const ShaderClosure *sc = shader_bsdf_pick(sd, &randu); - if (sc == NULL) { - *pdf = 0.0f; - return LABEL_NONE; - } - /* BSSRDF should already have been handled elsewhere. */ kernel_assert(CLOSURE_IS_BSDF(sc->type)); @@ -762,48 +320,28 @@ ccl_device_inline int shader_bsdf_sample(KernelGlobals *kg, label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); if (*pdf != 0.0f) { - bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight, kernel_data.film.use_light_pass); + const bool is_diffuse = (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || + CLOSURE_IS_BSDF_BSSRDF(sc->type)); + bsdf_eval_init(bsdf_eval, is_diffuse, eval * sc->weight); if (sd->num_closure > 1) { + const bool is_transmission = shader_bsdf_is_transmission(sd, *omega_in); float sweight = sc->sample_weight; - _shader_bsdf_multi_eval(kg, sd, *omega_in, pdf, sc, bsdf_eval, *pdf * sweight, sweight); + *pdf = _shader_bsdf_multi_eval( + kg, sd, *omega_in, is_transmission, sc, bsdf_eval, *pdf * sweight, sweight, 0); } } return label; } -ccl_device int shader_bsdf_sample_closure(KernelGlobals *kg, - ShaderData *sd, - const ShaderClosure *sc, - float randu, - float randv, - BsdfEval *bsdf_eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) -{ - PROFILING_INIT(kg, PROFILING_CLOSURE_SAMPLE); - - int label; - float3 eval = zero_float3(); - - *pdf = 0.0f; - label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); - - if (*pdf != 0.0f) - bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight, kernel_data.film.use_light_pass); - - return label; -} - -ccl_device float shader_bsdf_average_roughness(ShaderData *sd) +ccl_device float shader_bsdf_average_roughness(const ShaderData *sd) { float roughness = 0.0f; float sum_weight = 0.0f; for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF(sc->type)) { /* sqrt once to undo the squaring from multiplying roughness on the @@ -817,17 +355,7 @@ ccl_device float shader_bsdf_average_roughness(ShaderData *sd) return (sum_weight > 0.0f) ? roughness / sum_weight : 0.0f; } -ccl_device void shader_bsdf_blur(KernelGlobals *kg, ShaderData *sd, float roughness) -{ - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF(sc->type)) - bsdf_blur(kg, sc, roughness); - } -} - -ccl_device float3 shader_bsdf_transparency(KernelGlobals *kg, const ShaderData *sd) +ccl_device float3 shader_bsdf_transparency(const KernelGlobals *kg, const ShaderData *sd) { if (sd->flag & SD_HAS_ONLY_VOLUME) { return one_float3(); @@ -840,7 +368,7 @@ ccl_device float3 shader_bsdf_transparency(KernelGlobals *kg, const ShaderData * } } -ccl_device void shader_bsdf_disable_transparency(KernelGlobals *kg, ShaderData *sd) +ccl_device void shader_bsdf_disable_transparency(const KernelGlobals *kg, ShaderData *sd) { if (sd->flag & SD_TRANSPARENT) { for (int i = 0; i < sd->num_closure; i++) { @@ -856,7 +384,7 @@ ccl_device void shader_bsdf_disable_transparency(KernelGlobals *kg, ShaderData * } } -ccl_device float3 shader_bsdf_alpha(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 shader_bsdf_alpha(const KernelGlobals *kg, const ShaderData *sd) { float3 alpha = one_float3() - shader_bsdf_transparency(kg, sd); @@ -866,12 +394,12 @@ ccl_device float3 shader_bsdf_alpha(KernelGlobals *kg, ShaderData *sd) return alpha; } -ccl_device float3 shader_bsdf_diffuse(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 shader_bsdf_diffuse(const KernelGlobals *kg, const ShaderData *sd) { float3 eval = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type) || CLOSURE_IS_BSDF_BSSRDF(sc->type)) @@ -881,12 +409,12 @@ ccl_device float3 shader_bsdf_diffuse(KernelGlobals *kg, ShaderData *sd) return eval; } -ccl_device float3 shader_bsdf_glossy(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 shader_bsdf_glossy(const KernelGlobals *kg, const ShaderData *sd) { float3 eval = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) eval += sc->weight; @@ -895,12 +423,12 @@ ccl_device float3 shader_bsdf_glossy(KernelGlobals *kg, ShaderData *sd) return eval; } -ccl_device float3 shader_bsdf_transmission(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 shader_bsdf_transmission(const KernelGlobals *kg, const ShaderData *sd) { float3 eval = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type)) eval += sc->weight; @@ -909,12 +437,12 @@ ccl_device float3 shader_bsdf_transmission(KernelGlobals *kg, ShaderData *sd) return eval; } -ccl_device float3 shader_bsdf_average_normal(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 shader_bsdf_average_normal(const KernelGlobals *kg, const ShaderData *sd) { float3 N = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) N += sc->N * fabsf(average(sc->weight)); } @@ -922,59 +450,44 @@ ccl_device float3 shader_bsdf_average_normal(KernelGlobals *kg, ShaderData *sd) return (is_zero(N)) ? sd->N : normalize(N); } -ccl_device float3 shader_bsdf_ao(KernelGlobals *kg, ShaderData *sd, float ao_factor, float3 *N_) +ccl_device float3 shader_bsdf_ao_normal(const KernelGlobals *kg, const ShaderData *sd) { - float3 eval = zero_float3(); float3 N = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - + const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; - eval += sc->weight * ao_factor; N += bsdf->N * fabsf(average(sc->weight)); } } - *N_ = (is_zero(N)) ? sd->N : normalize(N); - return eval; + return (is_zero(N)) ? sd->N : normalize(N); } #ifdef __SUBSURFACE__ -ccl_device float3 shader_bssrdf_sum(ShaderData *sd, float3 *N_, float *texture_blur_) +ccl_device float3 shader_bssrdf_normal(const ShaderData *sd) { - float3 eval = zero_float3(); float3 N = zero_float3(); - float texture_blur = 0.0f, weight_sum = 0.0f; for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSSRDF(sc->type)) { const Bssrdf *bssrdf = (const Bssrdf *)sc; float avg_weight = fabsf(average(sc->weight)); N += bssrdf->N * avg_weight; - eval += sc->weight; - texture_blur += bssrdf->texture_blur * avg_weight; - weight_sum += avg_weight; } } - if (N_) - *N_ = (is_zero(N)) ? sd->N : normalize(N); - - if (texture_blur_) - *texture_blur_ = safe_divide(texture_blur, weight_sum); - - return eval; + return (is_zero(N)) ? sd->N : normalize(N); } #endif /* __SUBSURFACE__ */ /* Constant emission optimization */ -ccl_device bool shader_constant_emission_eval(KernelGlobals *kg, int shader, float3 *eval) +ccl_device bool shader_constant_emission_eval(const KernelGlobals *kg, int shader, float3 *eval) { int shader_index = shader & SHADER_MASK; int shader_flag = kernel_tex_fetch(__shaders, shader_index).flags; @@ -992,7 +505,7 @@ ccl_device bool shader_constant_emission_eval(KernelGlobals *kg, int shader, flo /* Background */ -ccl_device float3 shader_background_eval(ShaderData *sd) +ccl_device float3 shader_background_eval(const ShaderData *sd) { if (sd->flag & SD_EMISSION) { return sd->closure_emission_background; @@ -1004,7 +517,7 @@ ccl_device float3 shader_background_eval(ShaderData *sd) /* Emission */ -ccl_device float3 shader_emissive_eval(ShaderData *sd) +ccl_device float3 shader_emissive_eval(const ShaderData *sd) { if (sd->flag & SD_EMISSION) { return emissive_simple_eval(sd->Ng, sd->I) * sd->closure_emission_background; @@ -1016,7 +529,7 @@ ccl_device float3 shader_emissive_eval(ShaderData *sd) /* Holdout */ -ccl_device float3 shader_holdout_apply(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 shader_holdout_apply(const KernelGlobals *kg, ShaderData *sd) { float3 weight = zero_float3(); @@ -1041,7 +554,7 @@ ccl_device float3 shader_holdout_apply(KernelGlobals *kg, ShaderData *sd) } else { for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_HOLDOUT(sc->type)) { weight += sc->weight; } @@ -1053,14 +566,12 @@ ccl_device float3 shader_holdout_apply(KernelGlobals *kg, ShaderData *sd) /* Surface Evaluation */ -ccl_device void shader_eval_surface(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - ccl_global float *buffer, +template +ccl_device void shader_eval_surface(INTEGRATOR_STATE_CONST_ARGS, + ShaderData *ccl_restrict sd, + ccl_global float *ccl_restrict buffer, int path_flag) { - PROFILING_INIT(kg, PROFILING_SHADER_EVAL); - /* If path is being terminated, we are tracing a shadow ray or evaluating * emission, then we don't need to store closures. The emission and shadow * shader data also do not have a closure array to save GPU memory. */ @@ -1069,7 +580,7 @@ ccl_device void shader_eval_surface(KernelGlobals *kg, max_closures = 0; } else { - max_closures = kernel_data.integrator.max_closures; + max_closures = kernel_data.max_closures; } sd->num_closure = 0; @@ -1078,17 +589,18 @@ ccl_device void shader_eval_surface(KernelGlobals *kg, #ifdef __OSL__ if (kg->osl) { if (sd->object == OBJECT_NONE && sd->lamp == LAMP_NONE) { - OSLShader::eval_background(kg, sd, state, path_flag); + OSLShader::eval_background(INTEGRATOR_STATE_PASS, sd, path_flag); } else { - OSLShader::eval_surface(kg, sd, state, path_flag); + OSLShader::eval_surface(INTEGRATOR_STATE_PASS, sd, path_flag); } } else #endif { #ifdef __SVM__ - svm_eval_nodes(kg, sd, state, buffer, SHADER_TYPE_SURFACE, path_flag); + svm_eval_nodes( + INTEGRATOR_STATE_PASS, sd, buffer, path_flag); #else if (sd->object == OBJECT_NONE) { sd->closure_emission_background = make_float3(0.8f, 0.8f, 0.8f); @@ -1105,8 +617,11 @@ ccl_device void shader_eval_surface(KernelGlobals *kg, #endif } - if (sd->flag & SD_BSDF_NEEDS_LCG) { - sd->lcg_state = lcg_state_init_addrspace(state, 0xb4bc3953); + if (KERNEL_NODES_FEATURE(BSDF) && (sd->flag & SD_BSDF_NEEDS_LCG)) { + sd->lcg_state = lcg_state_init(INTEGRATOR_STATE(path, rng_hash), + INTEGRATOR_STATE(path, rng_offset), + INTEGRATOR_STATE(path, sample), + 0xb4bc3953); } } @@ -1114,48 +629,47 @@ ccl_device void shader_eval_surface(KernelGlobals *kg, #ifdef __VOLUME__ -ccl_device_inline void _shader_volume_phase_multi_eval(const ShaderData *sd, - const float3 omega_in, - float *pdf, - int skip_phase, - BsdfEval *result_eval, - float sum_pdf, - float sum_sample_weight) +ccl_device_inline float _shader_volume_phase_multi_eval(const ShaderData *sd, + const ShaderVolumePhases *phases, + const float3 omega_in, + int skip_phase, + BsdfEval *result_eval, + float sum_pdf, + float sum_sample_weight) { - for (int i = 0; i < sd->num_closure; i++) { + for (int i = 0; i < phases->num_closure; i++) { if (i == skip_phase) continue; - const ShaderClosure *sc = &sd->closure[i]; + const ShaderVolumeClosure *svc = &phases->closure[i]; + float phase_pdf = 0.0f; + float3 eval = volume_phase_eval(sd, svc, omega_in, &phase_pdf); - if (CLOSURE_IS_PHASE(sc->type)) { - float phase_pdf = 0.0f; - float3 eval = volume_phase_eval(sd, sc, omega_in, &phase_pdf); - - if (phase_pdf != 0.0f) { - bsdf_eval_accum(result_eval, sc->type, eval, 1.0f); - sum_pdf += phase_pdf * sc->sample_weight; - } - - sum_sample_weight += sc->sample_weight; + if (phase_pdf != 0.0f) { + bsdf_eval_accum(result_eval, false, eval, 1.0f); + sum_pdf += phase_pdf * svc->sample_weight; } + + sum_sample_weight += svc->sample_weight; } - *pdf = (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; + return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; } -ccl_device void shader_volume_phase_eval( - KernelGlobals *kg, const ShaderData *sd, const float3 omega_in, BsdfEval *eval, float *pdf) -{ - PROFILING_INIT(kg, PROFILING_CLOSURE_VOLUME_EVAL); - - bsdf_eval_init(eval, NBUILTIN_CLOSURES, zero_float3(), kernel_data.film.use_light_pass); - - _shader_volume_phase_multi_eval(sd, omega_in, pdf, -1, eval, 0.0f, 0.0f); -} - -ccl_device int shader_volume_phase_sample(KernelGlobals *kg, +ccl_device float shader_volume_phase_eval(const KernelGlobals *kg, const ShaderData *sd, + const ShaderVolumePhases *phases, + const float3 omega_in, + BsdfEval *phase_eval) +{ + bsdf_eval_init(phase_eval, false, zero_float3()); + + return _shader_volume_phase_multi_eval(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f); +} + +ccl_device int shader_volume_phase_sample(const KernelGlobals *kg, + const ShaderData *sd, + const ShaderVolumePhases *phases, float randu, float randv, BsdfEval *phase_eval, @@ -1163,41 +677,34 @@ ccl_device int shader_volume_phase_sample(KernelGlobals *kg, differential3 *domega_in, float *pdf) { - PROFILING_INIT(kg, PROFILING_CLOSURE_VOLUME_SAMPLE); - int sampled = 0; - if (sd->num_closure > 1) { + if (phases->num_closure > 1) { /* pick a phase closure based on sample weights */ float sum = 0.0f; - for (sampled = 0; sampled < sd->num_closure; sampled++) { - const ShaderClosure *sc = &sd->closure[sampled]; - - if (CLOSURE_IS_PHASE(sc->type)) - sum += sc->sample_weight; + for (sampled = 0; sampled < phases->num_closure; sampled++) { + const ShaderVolumeClosure *svc = &phases->closure[sampled]; + sum += svc->sample_weight; } float r = randu * sum; float partial_sum = 0.0f; - for (sampled = 0; sampled < sd->num_closure; sampled++) { - const ShaderClosure *sc = &sd->closure[sampled]; + for (sampled = 0; sampled < phases->num_closure; sampled++) { + const ShaderVolumeClosure *svc = &phases->closure[sampled]; + float next_sum = partial_sum + svc->sample_weight; - if (CLOSURE_IS_PHASE(sc->type)) { - float next_sum = partial_sum + sc->sample_weight; - - if (r <= next_sum) { - /* Rescale to reuse for BSDF direction sample. */ - randu = (r - partial_sum) / sc->sample_weight; - break; - } - - partial_sum = next_sum; + if (r <= next_sum) { + /* Rescale to reuse for BSDF direction sample. */ + randu = (r - partial_sum) / svc->sample_weight; + break; } + + partial_sum = next_sum; } - if (sampled == sd->num_closure) { + if (sampled == phases->num_closure) { *pdf = 0.0f; return LABEL_NONE; } @@ -1205,23 +712,23 @@ ccl_device int shader_volume_phase_sample(KernelGlobals *kg, /* todo: this isn't quite correct, we don't weight anisotropy properly * depending on color channels, even if this is perhaps not a common case */ - const ShaderClosure *sc = &sd->closure[sampled]; + const ShaderVolumeClosure *svc = &phases->closure[sampled]; int label; float3 eval = zero_float3(); *pdf = 0.0f; - label = volume_phase_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); + label = volume_phase_sample(sd, svc, randu, randv, &eval, omega_in, domega_in, pdf); if (*pdf != 0.0f) { - bsdf_eval_init(phase_eval, sc->type, eval, kernel_data.film.use_light_pass); + bsdf_eval_init(phase_eval, false, eval); } return label; } -ccl_device int shader_phase_sample_closure(KernelGlobals *kg, +ccl_device int shader_phase_sample_closure(const KernelGlobals *kg, const ShaderData *sd, - const ShaderClosure *sc, + const ShaderVolumeClosure *sc, float randu, float randv, BsdfEval *phase_eval, @@ -1229,8 +736,6 @@ ccl_device int shader_phase_sample_closure(KernelGlobals *kg, differential3 *domega_in, float *pdf) { - PROFILING_INIT(kg, PROFILING_CLOSURE_VOLUME_SAMPLE); - int label; float3 eval = zero_float3(); @@ -1238,18 +743,18 @@ ccl_device int shader_phase_sample_closure(KernelGlobals *kg, label = volume_phase_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); if (*pdf != 0.0f) - bsdf_eval_init(phase_eval, sc->type, eval, kernel_data.film.use_light_pass); + bsdf_eval_init(phase_eval, false, eval); return label; } /* Volume Evaluation */ -ccl_device_inline void shader_eval_volume(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - ccl_addr_space VolumeStack *stack, - int path_flag) +template +ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS, + ShaderData *ccl_restrict sd, + const int path_flag, + StackReadOp stack_read) { /* If path is being terminated, we are tracing a shadow ray or evaluating * emission, then we don't need to store closures. The emission and shadow @@ -1259,7 +764,7 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg, max_closures = 0; } else { - max_closures = kernel_data.integrator.max_closures; + max_closures = kernel_data.max_closures; } /* reset closures once at the start, we will be accumulating the closures @@ -1268,14 +773,18 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg, sd->num_closure_left = max_closures; sd->flag = 0; sd->object_flag = 0; - sd->type = PRIMITIVE_VOLUME; - for (int i = 0; stack[i].shader != SHADER_NONE; i++) { + for (int i = 0;; i++) { + const VolumeStack entry = stack_read(i); + if (entry.shader == SHADER_NONE) { + break; + } + /* setup shaderdata from stack. it's mostly setup already in * shader_setup_from_volume, this switching should be quick */ - sd->object = stack[i].object; + sd->object = entry.object; sd->lamp = LAMP_NONE; - sd->shader = stack[i].shader; + sd->shader = entry.shader; sd->flag &= ~SD_SHADER_FLAGS; sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; @@ -1295,18 +804,19 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg, # ifdef __SVM__ # ifdef __OSL__ if (kg->osl) { - OSLShader::eval_volume(kg, sd, state, path_flag); + OSLShader::eval_volume(INTEGRATOR_STATE_PASS, sd, path_flag); } else # endif { - svm_eval_nodes(kg, sd, state, NULL, SHADER_TYPE_VOLUME, path_flag); + svm_eval_nodes( + INTEGRATOR_STATE_PASS, sd, NULL, path_flag); } # endif - /* merge closures to avoid exceeding number of closures limit */ + /* Merge closures to avoid exceeding number of closures limit. */ if (i > 0) - shader_merge_closures(sd); + shader_merge_volume_closures(sd); } } @@ -1314,9 +824,7 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg, /* Displacement Evaluation */ -ccl_device void shader_eval_displacement(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state) +ccl_device void shader_eval_displacement(INTEGRATOR_STATE_CONST_ARGS, ShaderData *sd) { sd->num_closure = 0; sd->num_closure_left = 0; @@ -1325,11 +833,12 @@ ccl_device void shader_eval_displacement(KernelGlobals *kg, #ifdef __SVM__ # ifdef __OSL__ if (kg->osl) - OSLShader::eval_displacement(kg, sd, state); + OSLShader::eval_displacement(INTEGRATOR_STATE_PASS, sd); else # endif { - svm_eval_nodes(kg, sd, state, NULL, SHADER_TYPE_DISPLACEMENT, 0); + svm_eval_nodes( + INTEGRATOR_STATE_PASS, sd, NULL, 0); } #endif } @@ -1337,29 +846,13 @@ ccl_device void shader_eval_displacement(KernelGlobals *kg, /* Transparent Shadows */ #ifdef __TRANSPARENT_SHADOWS__ -ccl_device bool shader_transparent_shadow(KernelGlobals *kg, Intersection *isect) +ccl_device bool shader_transparent_shadow(const KernelGlobals *kg, Intersection *isect) { - int prim = kernel_tex_fetch(__prim_index, isect->prim); - int shader = 0; - -# ifdef __HAIR__ - if (isect->type & PRIMITIVE_ALL_TRIANGLE) { -# endif - shader = kernel_tex_fetch(__tri_shader, prim); -# ifdef __HAIR__ - } - else { - float4 str = kernel_tex_fetch(__curves, prim); - shader = __float_as_int(str.z); - } -# endif - int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; - - return (flag & SD_HAS_TRANSPARENT_SHADOW) != 0; + return (intersection_get_shader_flags(kg, isect) & SD_HAS_TRANSPARENT_SHADOW) != 0; } #endif /* __TRANSPARENT_SHADOWS__ */ -ccl_device float shader_cryptomatte_id(KernelGlobals *kg, int shader) +ccl_device float shader_cryptomatte_id(const KernelGlobals *kg, int shader) { return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).cryptomatte_id; } diff --git a/intern/cycles/kernel/kernel_shadow.h b/intern/cycles/kernel/kernel_shadow.h deleted file mode 100644 index 3b124122fba..00000000000 --- a/intern/cycles/kernel/kernel_shadow.h +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __VOLUME__ -/* Get PathState ready for use for volume stack evaluation. */ -# ifdef __SPLIT_KERNEL__ -ccl_addr_space -# endif - ccl_device_inline PathState * - shadow_blocked_volume_path_state(KernelGlobals *kg, - VolumeState *volume_state, - ccl_addr_space PathState *state, - ShaderData *sd, - Ray *ray) -{ -# ifdef __SPLIT_KERNEL__ - ccl_addr_space PathState *ps = - &kernel_split_state.state_shadow[ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0)]; -# else - PathState *ps = &volume_state->ps; -# endif - *ps = *state; - /* We are checking for shadow on the "other" side of the surface, so need - * to discard volume we are currently at. - */ - if (dot(sd->Ng, ray->D) < 0.0f) { - kernel_volume_stack_enter_exit(kg, sd, ps->volume_stack); - } - return ps; -} -#endif /* __VOLUME__ */ - -/* Attenuate throughput accordingly to the given intersection event. - * Returns true if the throughput is zero and traversal can be aborted. - */ -ccl_device_forceinline bool shadow_handle_transparent_isect(KernelGlobals *kg, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, -#ifdef __VOLUME__ - ccl_addr_space PathState *volume_state, -#endif - Intersection *isect, - Ray *ray, - float3 *throughput) -{ -#ifdef __VOLUME__ - /* Attenuation between last surface and next surface. */ - if (volume_state->volume_stack[0].shader != SHADER_NONE) { - Ray segment_ray = *ray; - segment_ray.t = isect->t; - kernel_volume_shadow(kg, shadow_sd, volume_state, &segment_ray, throughput); - } -#endif - /* Setup shader data at surface. */ - shader_setup_from_ray(kg, shadow_sd, isect, ray); - /* Attenuation from transparent surface. */ - if (!(shadow_sd->flag & SD_HAS_ONLY_VOLUME)) { - path_state_modify_bounce(state, true); - shader_eval_surface(kg, shadow_sd, state, NULL, PATH_RAY_SHADOW); - path_state_modify_bounce(state, false); - *throughput *= shader_bsdf_transparency(kg, shadow_sd); - } - /* Stop if all light is blocked. */ - if (is_zero(*throughput)) { - return true; - } -#ifdef __VOLUME__ - /* Exit/enter volume. */ - kernel_volume_stack_enter_exit(kg, shadow_sd, volume_state->volume_stack); -#endif - return false; -} - -/* Special version which only handles opaque shadows. */ -ccl_device bool shadow_blocked_opaque(KernelGlobals *kg, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - const uint visibility, - Ray *ray, - Intersection *isect, - float3 *shadow) -{ - const bool blocked = scene_intersect(kg, ray, visibility & PATH_RAY_SHADOW_OPAQUE, isect); -#ifdef __VOLUME__ - if (!blocked && state->volume_stack[0].shader != SHADER_NONE) { - /* Apply attenuation from current volume shader. */ - kernel_volume_shadow(kg, shadow_sd, state, ray, shadow); - } -#endif - return blocked; -} - -#ifdef __TRANSPARENT_SHADOWS__ -# ifdef __SHADOW_RECORD_ALL__ -/* Shadow function to compute how much light is blocked, - * - * We trace a single ray. If it hits any opaque surface, or more than a given - * number of transparent surfaces is hit, then we consider the geometry to be - * entirely blocked. If not, all transparent surfaces will be recorded and we - * will shade them one by one to determine how much light is blocked. This all - * happens in one scene intersection function. - * - * Recording all hits works well in some cases but may be slower in others. If - * we have many semi-transparent hairs, one intersection may be faster because - * you'd be reinteresecting the same hairs a lot with each step otherwise. If - * however there is mostly binary transparency then we may be recording many - * unnecessary intersections when one of the first surfaces blocks all light. - * - * From tests in real scenes it seems the performance loss is either minimal, - * or there is a performance increase anyway due to avoiding the need to send - * two rays with transparent shadows. - * - * On CPU it'll handle all transparent bounces (by allocating storage for - * intersections when they don't fit into the stack storage). - * - * On GPU it'll only handle SHADOW_STACK_MAX_HITS-1 intersections, so this - * is something to be kept an eye on. - */ - -# define SHADOW_STACK_MAX_HITS 64 - -/* Actual logic with traversal loop implementation which is free from device - * specific tweaks. - * - * Note that hits array should be as big as max_hits+1. - */ -ccl_device bool shadow_blocked_transparent_all_loop(KernelGlobals *kg, - ShaderData *sd, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - const uint visibility, - Ray *ray, - Intersection *hits, - uint max_hits, - float3 *shadow) -{ - /* Intersect to find an opaque surface, or record all transparent - * surface hits. - */ - uint num_hits; - const bool blocked = scene_intersect_shadow_all(kg, ray, hits, visibility, max_hits, &num_hits); -# ifdef __VOLUME__ -# ifdef __KERNEL_OPTIX__ - VolumeState &volume_state = kg->volume_state; -# else - VolumeState volume_state; -# endif -# endif - /* If no opaque surface found but we did find transparent hits, - * shade them. - */ - if (!blocked && num_hits > 0) { - float3 throughput = one_float3(); - float3 Pend = ray->P + ray->D * ray->t; - float last_t = 0.0f; - int bounce = state->transparent_bounce; - Intersection *isect = hits; -# ifdef __VOLUME__ -# ifdef __SPLIT_KERNEL__ - ccl_addr_space -# endif - PathState *ps = shadow_blocked_volume_path_state(kg, &volume_state, state, sd, ray); -# endif - sort_intersections(hits, num_hits); - for (int hit = 0; hit < num_hits; hit++, isect++) { - /* Adjust intersection distance for moving ray forward. */ - float new_t = isect->t; - isect->t -= last_t; - /* Skip hit if we did not move forward, step by step raytracing - * would have skipped it as well then. - */ - if (last_t == new_t) { - continue; - } - last_t = new_t; - /* Attenuate the throughput. */ - if (shadow_handle_transparent_isect(kg, - shadow_sd, - state, -# ifdef __VOLUME__ - ps, -# endif - isect, - ray, - &throughput)) { - return true; - } - /* Move ray forward. */ - ray->P = shadow_sd->P; - if (ray->t != FLT_MAX) { - ray->D = normalize_len(Pend - ray->P, &ray->t); - } - bounce++; - } -# ifdef __VOLUME__ - /* Attenuation for last line segment towards light. */ - if (ps->volume_stack[0].shader != SHADER_NONE) { - kernel_volume_shadow(kg, shadow_sd, ps, ray, &throughput); - } -# endif - *shadow = throughput; - return is_zero(throughput); - } -# ifdef __VOLUME__ - if (!blocked && state->volume_stack[0].shader != SHADER_NONE) { - /* Apply attenuation from current volume shader. */ -# ifdef __SPLIT_KERNEL__ - ccl_addr_space -# endif - PathState *ps = shadow_blocked_volume_path_state(kg, &volume_state, state, sd, ray); - kernel_volume_shadow(kg, shadow_sd, ps, ray, shadow); - } -# endif - return blocked; -} - -/* Here we do all device specific trickery before invoking actual traversal - * loop to help readability of the actual logic. - */ -ccl_device bool shadow_blocked_transparent_all(KernelGlobals *kg, - ShaderData *sd, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - const uint visibility, - Ray *ray, - uint max_hits, - float3 *shadow) -{ -# ifdef __SPLIT_KERNEL__ - Intersection hits_[SHADOW_STACK_MAX_HITS]; - Intersection *hits = &hits_[0]; -# elif defined(__KERNEL_CUDA__) - Intersection *hits = kg->hits_stack; -# else - Intersection hits_stack[SHADOW_STACK_MAX_HITS]; - Intersection *hits = hits_stack; -# endif -# ifndef __KERNEL_GPU__ - /* Prefer to use stack but use dynamic allocation if too deep max hits - * we need max_hits + 1 storage space due to the logic in - * scene_intersect_shadow_all which will first store and then check if - * the limit is exceeded. - * - * Ignore this on GPU because of slow/unavailable malloc(). - */ - if (max_hits + 1 > SHADOW_STACK_MAX_HITS) { - if (kg->transparent_shadow_intersections == NULL) { - const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce; - kg->transparent_shadow_intersections = (Intersection *)malloc(sizeof(Intersection) * - (transparent_max_bounce + 1)); - } - hits = kg->transparent_shadow_intersections; - } -# endif /* __KERNEL_GPU__ */ - /* Invoke actual traversal. */ - return shadow_blocked_transparent_all_loop( - kg, sd, shadow_sd, state, visibility, ray, hits, max_hits, shadow); -} -# endif /* __SHADOW_RECORD_ALL__ */ - -# if defined(__KERNEL_GPU__) || !defined(__SHADOW_RECORD_ALL__) -/* Shadow function to compute how much light is blocked, - * - * Here we raytrace from one transparent surface to the next step by step. - * To minimize overhead in cases where we don't need transparent shadows, we - * first trace a regular shadow ray. We check if the hit primitive was - * potentially transparent, and only in that case start marching. this gives - * one extra ray cast for the cases were we do want transparency. - */ - -/* This function is only implementing device-independent traversal logic - * which requires some precalculation done. - */ -ccl_device bool shadow_blocked_transparent_stepped_loop(KernelGlobals *kg, - ShaderData *sd, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - const uint visibility, - Ray *ray, - Intersection *isect, - const bool blocked, - const bool is_transparent_isect, - float3 *shadow) -{ -# ifdef __VOLUME__ -# ifdef __KERNEL_OPTIX__ - VolumeState &volume_state = kg->volume_state; -# else - VolumeState volume_state; -# endif -# endif - if (blocked && is_transparent_isect) { - float3 throughput = one_float3(); - float3 Pend = ray->P + ray->D * ray->t; - int bounce = state->transparent_bounce; -# ifdef __VOLUME__ -# ifdef __SPLIT_KERNEL__ - ccl_addr_space -# endif - PathState *ps = shadow_blocked_volume_path_state(kg, &volume_state, state, sd, ray); -# endif - for (;;) { - if (bounce >= kernel_data.integrator.transparent_max_bounce) { - return true; - } - if (!scene_intersect(kg, ray, visibility & PATH_RAY_SHADOW_TRANSPARENT, isect)) { - break; - } - if (!shader_transparent_shadow(kg, isect)) { - return true; - } - /* Attenuate the throughput. */ - if (shadow_handle_transparent_isect(kg, - shadow_sd, - state, -# ifdef __VOLUME__ - ps, -# endif - isect, - ray, - &throughput)) { - return true; - } - /* Move ray forward. */ - ray->P = ray_offset(shadow_sd->P, -shadow_sd->Ng); - if (ray->t != FLT_MAX) { - ray->D = normalize_len(Pend - ray->P, &ray->t); - } - bounce++; - } -# ifdef __VOLUME__ - /* Attenuation for last line segment towards light. */ - if (ps->volume_stack[0].shader != SHADER_NONE) { - kernel_volume_shadow(kg, shadow_sd, ps, ray, &throughput); - } -# endif - *shadow *= throughput; - return is_zero(throughput); - } -# ifdef __VOLUME__ - if (!blocked && state->volume_stack[0].shader != SHADER_NONE) { - /* Apply attenuation from current volume shader. */ -# ifdef __SPLIT_KERNEL__ - ccl_addr_space -# endif - PathState *ps = shadow_blocked_volume_path_state(kg, &volume_state, state, sd, ray); - kernel_volume_shadow(kg, shadow_sd, ps, ray, shadow); - } -# endif - return blocked; -} - -ccl_device bool shadow_blocked_transparent_stepped(KernelGlobals *kg, - ShaderData *sd, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - const uint visibility, - Ray *ray, - Intersection *isect, - float3 *shadow) -{ - bool blocked = scene_intersect(kg, ray, visibility & PATH_RAY_SHADOW_OPAQUE, isect); - bool is_transparent_isect = blocked ? shader_transparent_shadow(kg, isect) : false; - return shadow_blocked_transparent_stepped_loop( - kg, sd, shadow_sd, state, visibility, ray, isect, blocked, is_transparent_isect, shadow); -} - -# endif /* __KERNEL_GPU__ || !__SHADOW_RECORD_ALL__ */ -#endif /* __TRANSPARENT_SHADOWS__ */ - -ccl_device_inline bool shadow_blocked(KernelGlobals *kg, - ShaderData *sd, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - Ray *ray, - float3 *shadow) -{ - *shadow = one_float3(); -#if !defined(__KERNEL_OPTIX__) - /* Some common early checks. - * Avoid conditional trace call in OptiX though, since those hurt performance there. - */ - if (ray->t == 0.0f) { - return false; - } -#endif -#ifdef __SHADOW_TRICKS__ - const uint visibility = (state->flag & PATH_RAY_SHADOW_CATCHER) ? PATH_RAY_SHADOW_NON_CATCHER : - PATH_RAY_SHADOW; -#else - const uint visibility = PATH_RAY_SHADOW; -#endif - /* Do actual shadow shading. - * First of all, we check if integrator requires transparent shadows. - * if not, we use simplest and fastest ever way to calculate occlusion. - * Do not do this in OptiX to avoid the additional trace call. - */ -#if !defined(__KERNEL_OPTIX__) || !defined(__TRANSPARENT_SHADOWS__) - Intersection isect; -# ifdef __TRANSPARENT_SHADOWS__ - if (!kernel_data.integrator.transparent_shadows) -# endif - { - return shadow_blocked_opaque(kg, shadow_sd, state, visibility, ray, &isect, shadow); - } -#endif -#ifdef __TRANSPARENT_SHADOWS__ -# ifdef __SHADOW_RECORD_ALL__ - /* For the transparent shadows we try to use record-all logic on the - * devices which supports this. - */ - const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce; - /* Check transparent bounces here, for volume scatter which can do - * lighting before surface path termination is checked. - */ - if (state->transparent_bounce >= transparent_max_bounce) { - return true; - } - uint max_hits = transparent_max_bounce - state->transparent_bounce - 1; -# if defined(__KERNEL_OPTIX__) - /* Always use record-all behavior in OptiX, but ensure there are no out of bounds - * accesses to the hit stack. - */ - max_hits = min(max_hits, SHADOW_STACK_MAX_HITS - 1); -# elif defined(__KERNEL_GPU__) - /* On GPU we do tricky with tracing opaque ray first, this avoids speed - * regressions in some files. - * - * TODO(sergey): Check why using record-all behavior causes slowdown in such - * cases. Could that be caused by a higher spill pressure? - */ - const bool blocked = scene_intersect(kg, ray, visibility & PATH_RAY_SHADOW_OPAQUE, &isect); - const bool is_transparent_isect = blocked ? shader_transparent_shadow(kg, &isect) : false; - if (!blocked || !is_transparent_isect || max_hits + 1 >= SHADOW_STACK_MAX_HITS) { - return shadow_blocked_transparent_stepped_loop( - kg, sd, shadow_sd, state, visibility, ray, &isect, blocked, is_transparent_isect, shadow); - } -# endif /* __KERNEL_GPU__ */ - return shadow_blocked_transparent_all( - kg, sd, shadow_sd, state, visibility, ray, max_hits, shadow); -# else /* __SHADOW_RECORD_ALL__ */ - /* Fallback to a slowest version which works on all devices. */ - return shadow_blocked_transparent_stepped( - kg, sd, shadow_sd, state, visibility, ray, &isect, shadow); -# endif /* __SHADOW_RECORD_ALL__ */ -#endif /* __TRANSPARENT_SHADOWS__ */ -} - -#undef SHADOW_STACK_MAX_HITS - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_shadow_catcher.h b/intern/cycles/kernel/kernel_shadow_catcher.h new file mode 100644 index 00000000000..824749818a4 --- /dev/null +++ b/intern/cycles/kernel/kernel_shadow_catcher.h @@ -0,0 +1,116 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/integrator_state_util.h" +#include "kernel/kernel_path_state.h" + +CCL_NAMESPACE_BEGIN + +/* Check whether current surface bounce is where path is to be split for the shadow catcher. */ +ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(INTEGRATOR_STATE_ARGS, + const int object_flag) +{ +#ifdef __SHADOW_CATCHER__ + if (!kernel_data.integrator.has_shadow_catcher) { + return false; + } + + /* Check the flag first, avoiding fetches form global memory. */ + if ((object_flag & SD_OBJECT_SHADOW_CATCHER) == 0) { + return false; + } + if (object_flag & SD_OBJECT_HOLDOUT_MASK) { + return false; + } + + const int path_flag = INTEGRATOR_STATE(path, flag); + + if ((path_flag & PATH_RAY_TRANSPARENT_BACKGROUND) == 0) { + /* Split only on primary rays, secondary bounces are to treat shadow catcher as a regular + * object. */ + return false; + } + + if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) { + return false; + } + + return true; +#else + (void)object_flag; + return false; +#endif +} + +/* Check whether the current path can still split. */ +ccl_device_inline bool kernel_shadow_catcher_path_can_split(INTEGRATOR_STATE_CONST_ARGS) +{ + if (INTEGRATOR_PATH_IS_TERMINATED && INTEGRATOR_SHADOW_PATH_IS_TERMINATED) { + return false; + } + + const int path_flag = INTEGRATOR_STATE(path, flag); + + if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) { + /* Shadow catcher was already hit and the state was split. No further split is allowed. */ + return false; + } + + return (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND) != 0; +} + +/* NOTE: Leaves kernel scheduling information untouched. Use INIT semantic for one of the paths + * after this function. */ +ccl_device_inline bool kernel_shadow_catcher_split(INTEGRATOR_STATE_ARGS, const int object_flags) +{ +#ifdef __SHADOW_CATCHER__ + + if (!kernel_shadow_catcher_is_path_split_bounce(INTEGRATOR_STATE_PASS, object_flags)) { + return false; + } + + /* The split is to be done. Mark the current state as such, so that it stops contributing to the + * shadow catcher matte pass, but keeps contributing to the combined pass. */ + INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_SHADOW_CATCHER_HIT; + + /* Split new state from the current one. This new state will only track contribution of shadow + * catcher objects ignoring non-catcher objects. */ + integrator_state_shadow_catcher_split(INTEGRATOR_STATE_PASS); + + return true; +#else + (void)object_flags; + return false; +#endif +} + +#ifdef __SHADOW_CATCHER__ + +ccl_device_forceinline bool kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_CONST_ARGS) +{ + return (INTEGRATOR_STATE(path, flag) & PATH_RAY_SHADOW_CATCHER_HIT) == 0; +} + +ccl_device_forceinline bool kernel_shadow_catcher_is_object_pass(INTEGRATOR_STATE_CONST_ARGS) +{ + return INTEGRATOR_STATE(path, flag) & PATH_RAY_SHADOW_CATCHER_PASS; +} + +#endif /* __SHADOW_CATCHER__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h deleted file mode 100644 index 677504a4045..00000000000 --- a/intern/cycles/kernel/kernel_subsurface.h +++ /dev/null @@ -1,724 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* BSSRDF using disk based importance sampling. - * - * BSSRDF Importance Sampling, SIGGRAPH 2013 - * http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf - */ - -ccl_device_inline float3 -subsurface_scatter_eval(ShaderData *sd, const ShaderClosure *sc, float disk_r, float r, bool all) -{ - /* This is the Veach one-sample model with balance heuristic, some pdf - * factors drop out when using balance heuristic weighting. For branched - * path tracing (all) we sample all closure and don't use MIS. */ - float3 eval_sum = zero_float3(); - float pdf_sum = 0.0f; - float sample_weight_inv = 0.0f; - - if (!all) { - float sample_weight_sum = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - sc = &sd->closure[i]; - - if (CLOSURE_IS_DISK_BSSRDF(sc->type)) { - sample_weight_sum += sc->sample_weight; - } - } - - sample_weight_inv = 1.0f / sample_weight_sum; - } - - for (int i = 0; i < sd->num_closure; i++) { - sc = &sd->closure[i]; - - if (CLOSURE_IS_DISK_BSSRDF(sc->type)) { - /* in case of branched path integrate we sample all bssrdf's once, - * for path trace we pick one, so adjust pdf for that */ - float sample_weight = (all) ? 1.0f : sc->sample_weight * sample_weight_inv; - - /* compute pdf */ - float3 eval = bssrdf_eval(sc, r); - float pdf = bssrdf_pdf(sc, disk_r); - - eval_sum += sc->weight * eval; - pdf_sum += sample_weight * pdf; - } - } - - return (pdf_sum > 0.0f) ? eval_sum / pdf_sum : zero_float3(); -} - -ccl_device_inline float3 subsurface_scatter_walk_eval(ShaderData *sd, - const ShaderClosure *sc, - float3 throughput, - bool all) -{ - /* This is the Veach one-sample model with balance heuristic, some pdf - * factors drop out when using balance heuristic weighting. For branched - * path tracing (all) we sample all closure and don't use MIS. */ - if (!all) { - float bssrdf_weight = 0.0f; - float weight = sc->sample_weight; - - for (int i = 0; i < sd->num_closure; i++) { - sc = &sd->closure[i]; - - if (CLOSURE_IS_BSSRDF(sc->type)) { - bssrdf_weight += sc->sample_weight; - } - } - throughput *= bssrdf_weight / weight; - } - return throughput; -} - -/* replace closures with a single diffuse bsdf closure after scatter step */ -ccl_device void subsurface_scatter_setup_diffuse_bsdf( - KernelGlobals *kg, ShaderData *sd, ClosureType type, float roughness, float3 weight, float3 N) -{ - sd->flag &= ~SD_CLOSURE_FLAGS; - sd->num_closure = 0; - sd->num_closure_left = kernel_data.integrator.max_closures; - -#ifdef __PRINCIPLED__ - if (type == CLOSURE_BSSRDF_PRINCIPLED_ID || type == CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) { - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( - sd, sizeof(PrincipledDiffuseBsdf), weight); - - if (bsdf) { - bsdf->N = N; - bsdf->roughness = roughness; - sd->flag |= bsdf_principled_diffuse_setup(bsdf); - - /* replace CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID with this special ID so render passes - * can recognize it as not being a regular Disney principled diffuse closure */ - bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID; - } - } - else if (CLOSURE_IS_BSDF_BSSRDF(type) || CLOSURE_IS_BSSRDF(type)) -#endif /* __PRINCIPLED__ */ - { - DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight); - - if (bsdf) { - bsdf->N = N; - sd->flag |= bsdf_diffuse_setup(bsdf); - - /* replace CLOSURE_BSDF_DIFFUSE_ID with this special ID so render passes - * can recognize it as not being a regular diffuse closure */ - bsdf->type = CLOSURE_BSDF_BSSRDF_ID; - } - } -} - -/* optionally do blurring of color and/or bump mapping, at the cost of a shader evaluation */ -ccl_device float3 subsurface_color_pow(float3 color, float exponent) -{ - color = max(color, zero_float3()); - - if (exponent == 1.0f) { - /* nothing to do */ - } - else if (exponent == 0.5f) { - color.x = sqrtf(color.x); - color.y = sqrtf(color.y); - color.z = sqrtf(color.z); - } - else { - color.x = powf(color.x, exponent); - color.y = powf(color.y, exponent); - color.z = powf(color.z, exponent); - } - - return color; -} - -ccl_device void subsurface_color_bump_blur( - KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, float3 *eval, float3 *N) -{ - /* average color and texture blur at outgoing point */ - float texture_blur; - float3 out_color = shader_bssrdf_sum(sd, NULL, &texture_blur); - - /* do we have bump mapping? */ - bool bump = (sd->flag & SD_HAS_BSSRDF_BUMP) != 0; - - if (bump || texture_blur > 0.0f) { - /* average color and normal at incoming point */ - shader_eval_surface(kg, sd, state, NULL, state->flag); - float3 in_color = shader_bssrdf_sum(sd, (bump) ? N : NULL, NULL); - - /* we simply divide out the average color and multiply with the average - * of the other one. we could try to do this per closure but it's quite - * tricky to match closures between shader evaluations, their number and - * order may change, this is simpler */ - if (texture_blur > 0.0f) { - out_color = subsurface_color_pow(out_color, texture_blur); - in_color = subsurface_color_pow(in_color, texture_blur); - - *eval *= safe_divide_color(in_color, out_color); - } - } -} - -/* Subsurface scattering step, from a point on the surface to other - * nearby points on the same object. - */ -ccl_device_inline int subsurface_scatter_disk(KernelGlobals *kg, - LocalIntersection *ss_isect, - ShaderData *sd, - const ShaderClosure *sc, - uint *lcg_state, - float disk_u, - float disk_v, - bool all) -{ - /* pick random axis in local frame and point on disk */ - float3 disk_N, disk_T, disk_B; - float pick_pdf_N, pick_pdf_T, pick_pdf_B; - - disk_N = sd->Ng; - make_orthonormals(disk_N, &disk_T, &disk_B); - - if (disk_v < 0.5f) { - pick_pdf_N = 0.5f; - pick_pdf_T = 0.25f; - pick_pdf_B = 0.25f; - disk_v *= 2.0f; - } - else if (disk_v < 0.75f) { - float3 tmp = disk_N; - disk_N = disk_T; - disk_T = tmp; - pick_pdf_N = 0.25f; - pick_pdf_T = 0.5f; - pick_pdf_B = 0.25f; - disk_v = (disk_v - 0.5f) * 4.0f; - } - else { - float3 tmp = disk_N; - disk_N = disk_B; - disk_B = tmp; - pick_pdf_N = 0.25f; - pick_pdf_T = 0.25f; - pick_pdf_B = 0.5f; - disk_v = (disk_v - 0.75f) * 4.0f; - } - - /* sample point on disk */ - float phi = M_2PI_F * disk_v; - float disk_height, disk_r; - - bssrdf_sample(sc, disk_u, &disk_r, &disk_height); - - float3 disk_P = (disk_r * cosf(phi)) * disk_T + (disk_r * sinf(phi)) * disk_B; - - /* create ray */ -#ifdef __SPLIT_KERNEL__ - Ray ray_object = ss_isect->ray; - Ray *ray = &ray_object; -#else - Ray *ray = &ss_isect->ray; -#endif - ray->P = sd->P + disk_N * disk_height + disk_P; - ray->D = -disk_N; - ray->t = 2.0f * disk_height; - ray->dP = sd->dP; - ray->dD = differential3_zero(); - ray->time = sd->time; - - /* intersect with the same object. if multiple intersections are found it - * will use at most BSSRDF_MAX_HITS hits, a random subset of all hits */ - scene_intersect_local(kg, ray, ss_isect, sd->object, lcg_state, BSSRDF_MAX_HITS); - int num_eval_hits = min(ss_isect->num_hits, BSSRDF_MAX_HITS); - - for (int hit = 0; hit < num_eval_hits; hit++) { - /* Quickly retrieve P and Ng without setting up ShaderData. */ - float3 hit_P; - if (sd->type & PRIMITIVE_TRIANGLE) { - hit_P = triangle_refine_local(kg, sd, &ss_isect->hits[hit], ray); - } -#ifdef __OBJECT_MOTION__ - else if (sd->type & PRIMITIVE_MOTION_TRIANGLE) { - float3 verts[3]; - motion_triangle_vertices(kg, - sd->object, - kernel_tex_fetch(__prim_index, ss_isect->hits[hit].prim), - sd->time, - verts); - hit_P = motion_triangle_refine_local(kg, sd, &ss_isect->hits[hit], ray, verts); - } -#endif /* __OBJECT_MOTION__ */ - else { - ss_isect->weight[hit] = zero_float3(); - continue; - } - - float3 hit_Ng = ss_isect->Ng[hit]; - if (ss_isect->hits[hit].object != OBJECT_NONE) { - object_normal_transform(kg, sd, &hit_Ng); - } - - /* Probability densities for local frame axes. */ - float pdf_N = pick_pdf_N * fabsf(dot(disk_N, hit_Ng)); - float pdf_T = pick_pdf_T * fabsf(dot(disk_T, hit_Ng)); - float pdf_B = pick_pdf_B * fabsf(dot(disk_B, hit_Ng)); - - /* Multiple importance sample between 3 axes, power heuristic - * found to be slightly better than balance heuristic. pdf_N - * in the MIS weight and denominator cancelled out. */ - float w = pdf_N / (sqr(pdf_N) + sqr(pdf_T) + sqr(pdf_B)); - if (ss_isect->num_hits > BSSRDF_MAX_HITS) { - w *= ss_isect->num_hits / (float)BSSRDF_MAX_HITS; - } - - /* Real distance to sampled point. */ - float r = len(hit_P - sd->P); - - /* Evaluate profiles. */ - float3 eval = subsurface_scatter_eval(sd, sc, disk_r, r, all) * w; - - ss_isect->weight[hit] = eval; - } - -#ifdef __SPLIT_KERNEL__ - ss_isect->ray = *ray; -#endif - - return num_eval_hits; -} - -#if defined(__KERNEL_OPTIX__) && defined(__SHADER_RAYTRACE__) -ccl_device_inline void subsurface_scatter_multi_setup(KernelGlobals *kg, - LocalIntersection *ss_isect, - int hit, - ShaderData *sd, - ccl_addr_space PathState *state, - ClosureType type, - float roughness) -{ - optixDirectCall(2, kg, ss_isect, hit, sd, state, type, roughness); -} -extern "C" __device__ void __direct_callable__subsurface_scatter_multi_setup( -#else -ccl_device_noinline void subsurface_scatter_multi_setup( -#endif - KernelGlobals *kg, - LocalIntersection *ss_isect, - int hit, - ShaderData *sd, - ccl_addr_space PathState *state, - ClosureType type, - float roughness) -{ -#ifdef __SPLIT_KERNEL__ - Ray ray_object = ss_isect->ray; - Ray *ray = &ray_object; -#else - Ray *ray = &ss_isect->ray; -#endif - - /* Workaround for AMD GPU OpenCL compiler. Most probably cache bypass issue. */ -#if defined(__SPLIT_KERNEL__) && defined(__KERNEL_OPENCL_AMD__) && defined(__KERNEL_GPU__) - kernel_split_params.dummy_sd_flag = sd->flag; -#endif - - /* Setup new shading point. */ - shader_setup_from_subsurface(kg, sd, &ss_isect->hits[hit], ray); - - /* Optionally blur colors and bump mapping. */ - float3 weight = ss_isect->weight[hit]; - float3 N = sd->N; - subsurface_color_bump_blur(kg, sd, state, &weight, &N); - - /* Setup diffuse BSDF. */ - subsurface_scatter_setup_diffuse_bsdf(kg, sd, type, roughness, weight, N); -} - -/* Random walk subsurface scattering. - * - * "Practical and Controllable Subsurface Scattering for Production Path - * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ - -ccl_device void subsurface_random_walk_remap(const float A, - const float d, - float *sigma_t, - float *alpha) -{ - /* Compute attenuation and scattering coefficients from albedo. */ - *alpha = 1.0f - expf(A * (-5.09406f + A * (2.61188f - A * 4.31805f))); - const float s = 1.9f - A + 3.5f * sqr(A - 0.8f); - - *sigma_t = 1.0f / fmaxf(d * s, 1e-16f); -} - -ccl_device void subsurface_random_walk_coefficients(const ShaderClosure *sc, - float3 *sigma_t, - float3 *alpha, - float3 *weight) -{ - const Bssrdf *bssrdf = (const Bssrdf *)sc; - const float3 A = bssrdf->albedo; - const float3 d = bssrdf->radius; - float sigma_t_x, sigma_t_y, sigma_t_z; - float alpha_x, alpha_y, alpha_z; - - subsurface_random_walk_remap(A.x, d.x, &sigma_t_x, &alpha_x); - subsurface_random_walk_remap(A.y, d.y, &sigma_t_y, &alpha_y); - subsurface_random_walk_remap(A.z, d.z, &sigma_t_z, &alpha_z); - - *sigma_t = make_float3(sigma_t_x, sigma_t_y, sigma_t_z); - *alpha = make_float3(alpha_x, alpha_y, alpha_z); - - /* Closure mixing and Fresnel weights separate from albedo. */ - *weight = safe_divide_color(bssrdf->weight, A); -} - -/* References for Dwivedi sampling: - * - * [1] "A Zero-variance-based Sampling Scheme for Monte Carlo Subsurface Scattering" - * by Jaroslav Křivánek and Eugene d'Eon (SIGGRAPH 2014) - * https://cgg.mff.cuni.cz/~jaroslav/papers/2014-zerovar/ - * - * [2] "Improving the Dwivedi Sampling Scheme" - * by Johannes Meng, Johannes Hanika, and Carsten Dachsbacher (EGSR 2016) - * https://cg.ivd.kit.edu/1951.php - * - * [3] "Zero-Variance Theory for Efficient Subsurface Scattering" - * by Eugene d'Eon and Jaroslav Křivánek (SIGGRAPH 2020) - * https://iliyan.com/publications/RenderingCourse2020 - */ - -ccl_device_forceinline float eval_phase_dwivedi(float v, float phase_log, float cos_theta) -{ - /* Eq. 9 from [2] using precomputed log((v + 1) / (v - 1)) */ - return 1.0f / ((v - cos_theta) * phase_log); -} - -ccl_device_forceinline float sample_phase_dwivedi(float v, float phase_log, float rand) -{ - /* Based on Eq. 10 from [2]: `v - (v + 1) * pow((v - 1) / (v + 1), rand)` - * Since we're already pre-computing `phase_log = log((v + 1) / (v - 1))` for the evaluation, - * we can implement the power function like this. */ - return v - (v + 1) * expf(-rand * phase_log); -} - -ccl_device_forceinline float diffusion_length_dwivedi(float alpha) -{ - /* Eq. 67 from [3] */ - return 1.0f / sqrtf(1.0f - powf(alpha, 2.44294f - 0.0215813f * alpha + 0.578637f / alpha)); -} - -ccl_device_forceinline float3 direction_from_cosine(float3 D, float cos_theta, float randv) -{ - float sin_theta = safe_sqrtf(1.0f - cos_theta * cos_theta); - float phi = M_2PI_F * randv; - float3 dir = make_float3(sin_theta * cosf(phi), sin_theta * sinf(phi), cos_theta); - - float3 T, B; - make_orthonormals(D, &T, &B); - return dir.x * T + dir.y * B + dir.z * D; -} - -ccl_device_forceinline float3 subsurface_random_walk_pdf(float3 sigma_t, - float t, - bool hit, - float3 *transmittance) -{ - float3 T = volume_color_transmittance(sigma_t, t); - if (transmittance) { - *transmittance = T; - } - return hit ? T : sigma_t * T; -} - -#ifdef __KERNEL_OPTIX__ -ccl_device_inline /* inline trace calls */ -#else -ccl_device_noinline -#endif - bool - subsurface_random_walk(KernelGlobals *kg, - LocalIntersection *ss_isect, - ShaderData *sd, - ccl_addr_space PathState *state, - const ShaderClosure *sc, - const float bssrdf_u, - const float bssrdf_v, - bool all) -{ - /* Sample diffuse surface scatter into the object. */ - float3 D; - float pdf; - sample_cos_hemisphere(-sd->N, bssrdf_u, bssrdf_v, &D, &pdf); - if (dot(-sd->Ng, D) <= 0.0f) { - return 0; - } - - /* Convert subsurface to volume coefficients. - * The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */ - float3 sigma_t, alpha; - float3 throughput = one_float3(); - subsurface_random_walk_coefficients(sc, &sigma_t, &alpha, &throughput); - float3 sigma_s = sigma_t * alpha; - - /* Theoretically it should be better to use the exact alpha for the channel we're sampling at - * each bounce, but in practice there doesn't seem to be a noticeable difference in exchange - * for making the code significantly more complex and slower (if direction sampling depends on - * the sampled channel, we need to compute its PDF per-channel and consider it for MIS later on). - * - * Since the strength of the guided sampling increases as alpha gets lower, using a value that - * is too low results in fireflies while one that's too high just gives a bit more noise. - * Therefore, the code here uses the highest of the three albedos to be safe. */ - float diffusion_length = diffusion_length_dwivedi(max3(alpha)); - /* Precompute term for phase sampling. */ - float phase_log = logf((diffusion_length + 1) / (diffusion_length - 1)); - - /* Setup ray. */ -#ifdef __SPLIT_KERNEL__ - Ray ray_object = ss_isect->ray; - Ray *ray = &ray_object; -#else - Ray *ray = &ss_isect->ray; -#endif - ray->P = ray_offset(sd->P, -sd->Ng); - ray->D = D; - ray->t = FLT_MAX; - ray->time = sd->time; - - /* Modify state for RNGs, decorrelated from other paths. */ - uint prev_rng_offset = state->rng_offset; - uint prev_rng_hash = state->rng_hash; - state->rng_hash = cmj_hash(state->rng_hash + state->rng_offset, 0xdeadbeef); - - /* Random walk until we hit the surface again. */ - bool hit = false; - bool have_opposite_interface = false; - float opposite_distance = 0.0f; - - /* Todo: Disable for alpha>0.999 or so? */ - const float guided_fraction = 0.75f; - - for (int bounce = 0; bounce < BSSRDF_MAX_BOUNCES; bounce++) { - /* Advance random number offset. */ - state->rng_offset += PRNG_BOUNCE_NUM; - - /* Sample color channel, use MIS with balance heuristic. */ - float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL); - float3 channel_pdf; - int channel = kernel_volume_sample_channel(alpha, throughput, rphase, &channel_pdf); - float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel); - float randt = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE); - - /* We need the result of the raycast to compute the full guided PDF, so just remember the - * relevant terms to avoid recomputing them later. */ - float backward_fraction = 0.0f; - float forward_pdf_factor = 0.0f; - float forward_stretching = 1.0f; - float backward_pdf_factor = 0.0f; - float backward_stretching = 1.0f; - - /* For the initial ray, we already know the direction, so just do classic distance sampling. */ - if (bounce > 0) { - /* Decide whether we should use guided or classic sampling. */ - bool guided = (path_state_rng_1D(kg, state, PRNG_LIGHT_TERMINATE) < guided_fraction); - - /* Determine if we want to sample away from the incoming interface. - * This only happens if we found a nearby opposite interface, and the probability for it - * depends on how close we are to it already. - * This probability term comes from the recorded presentation of [3]. */ - bool guide_backward = false; - if (have_opposite_interface) { - /* Compute distance of the random walk between the tangent plane at the starting point - * and the assumed opposite interface (the parallel plane that contains the point we - * found in our ray query for the opposite side). */ - float x = clamp(dot(ray->P - sd->P, -sd->N), 0.0f, opposite_distance); - backward_fraction = 1.0f / (1.0f + expf((opposite_distance - 2 * x) / diffusion_length)); - guide_backward = path_state_rng_1D(kg, state, PRNG_TERMINATE) < backward_fraction; - } - - /* Sample scattering direction. */ - float scatter_u, scatter_v; - path_state_rng_2D(kg, state, PRNG_BSDF_U, &scatter_u, &scatter_v); - float cos_theta; - if (guided) { - cos_theta = sample_phase_dwivedi(diffusion_length, phase_log, scatter_u); - /* The backwards guiding distribution is just mirrored along sd->N, so swapping the - * sign here is enough to sample from that instead. */ - if (guide_backward) { - cos_theta = -cos_theta; - } - } - else { - cos_theta = 2.0f * scatter_u - 1.0f; - } - ray->D = direction_from_cosine(sd->N, cos_theta, scatter_v); - - /* Compute PDF factor caused by phase sampling (as the ratio of guided / classic). - * Since phase sampling is channel-independent, we can get away with applying a factor - * to the guided PDF, which implicitly means pulling out the classic PDF term and letting - * it cancel with an equivalent term in the numerator of the full estimator. - * For the backward PDF, we again reuse the same probability distribution with a sign swap. - */ - forward_pdf_factor = 2.0f * eval_phase_dwivedi(diffusion_length, phase_log, cos_theta); - backward_pdf_factor = 2.0f * eval_phase_dwivedi(diffusion_length, phase_log, -cos_theta); - - /* Prepare distance sampling. - * For the backwards case, this also needs the sign swapped since now directions against - * sd->N (and therefore with negative cos_theta) are preferred. */ - forward_stretching = (1.0f - cos_theta / diffusion_length); - backward_stretching = (1.0f + cos_theta / diffusion_length); - if (guided) { - sample_sigma_t *= guide_backward ? backward_stretching : forward_stretching; - } - } - - /* Sample direction along ray. */ - float t = -logf(1.0f - randt) / sample_sigma_t; - - /* On the first bounce, we use the raycast to check if the opposite side is nearby. - * If yes, we will later use backwards guided sampling in order to have a decent - * chance of connecting to it. - * Todo: Maybe use less than 10 times the mean free path? */ - ray->t = (bounce == 0) ? max(t, 10.0f / (min3(sigma_t))) : t; - scene_intersect_local(kg, ray, ss_isect, sd->object, NULL, 1); - hit = (ss_isect->num_hits > 0); - - if (hit) { -#ifdef __KERNEL_OPTIX__ - /* t is always in world space with OptiX. */ - ray->t = ss_isect->hits[0].t; -#else - /* Compute world space distance to surface hit. */ - float3 D = ray->D; - object_inverse_dir_transform(kg, sd, &D); - D = normalize(D) * ss_isect->hits[0].t; - object_dir_transform(kg, sd, &D); - ray->t = len(D); -#endif - } - - if (bounce == 0) { - /* Check if we hit the opposite side. */ - if (hit) { - have_opposite_interface = true; - opposite_distance = dot(ray->P + ray->t * ray->D - sd->P, -sd->N); - } - /* Apart from the opposite side check, we were supposed to only trace up to distance t, - * so check if there would have been a hit in that case. */ - hit = ray->t < t; - } - - /* Use the distance to the exit point for the throughput update if we found one. */ - if (hit) { - t = ray->t; - } - else if (bounce == 0) { - /* Restore original position if nothing was hit after the first bounce, - * without the ray_offset() that was added to avoid self-intersection. - * Otherwise if that offset is relatively large compared to the scattering - * radius, we never go back up high enough to exit the surface. */ - ray->P = sd->P; - } - - /* Advance to new scatter location. */ - ray->P += t * ray->D; - - float3 transmittance; - float3 pdf = subsurface_random_walk_pdf(sigma_t, t, hit, &transmittance); - if (bounce > 0) { - /* Compute PDF just like we do for classic sampling, but with the stretched sigma_t. */ - float3 guided_pdf = subsurface_random_walk_pdf(forward_stretching * sigma_t, t, hit, NULL); - - if (have_opposite_interface) { - /* First step of MIS: Depending on geometry we might have two methods for guided - * sampling, so perform MIS between them. */ - float3 back_pdf = subsurface_random_walk_pdf(backward_stretching * sigma_t, t, hit, NULL); - guided_pdf = mix( - guided_pdf * forward_pdf_factor, back_pdf * backward_pdf_factor, backward_fraction); - } - else { - /* Just include phase sampling factor otherwise. */ - guided_pdf *= forward_pdf_factor; - } - - /* Now we apply the MIS balance heuristic between the classic and guided sampling. */ - pdf = mix(pdf, guided_pdf, guided_fraction); - } - - /* Finally, we're applying MIS again to combine the three color channels. - * Altogether, the MIS computation combines up to nine different estimators: - * {classic, guided, backward_guided} x {r, g, b} */ - throughput *= (hit ? transmittance : sigma_s * transmittance) / dot(channel_pdf, pdf); - - if (hit) { - /* If we hit the surface, we are done. */ - break; - } - else if (throughput.x < VOLUME_THROUGHPUT_EPSILON && - throughput.y < VOLUME_THROUGHPUT_EPSILON && - throughput.z < VOLUME_THROUGHPUT_EPSILON) { - /* Avoid unnecessary work and precision issue when throughput gets really small. */ - break; - } - } - - kernel_assert(isfinite_safe(throughput.x) && isfinite_safe(throughput.y) && - isfinite_safe(throughput.z)); - - state->rng_offset = prev_rng_offset; - state->rng_hash = prev_rng_hash; - - /* Return number of hits in ss_isect. */ - if (!hit) { - return 0; - } - - /* TODO: gain back performance lost from merging with disk BSSRDF. We - * only need to return on hit so this indirect ray push/pop overhead - * is not actually needed, but it does keep the code simpler. */ - ss_isect->weight[0] = subsurface_scatter_walk_eval(sd, sc, throughput, all); -#ifdef __SPLIT_KERNEL__ - ss_isect->ray = *ray; -#endif - - return 1; -} - -ccl_device_inline int subsurface_scatter_multi_intersect(KernelGlobals *kg, - LocalIntersection *ss_isect, - ShaderData *sd, - ccl_addr_space PathState *state, - const ShaderClosure *sc, - uint *lcg_state, - float bssrdf_u, - float bssrdf_v, - bool all) -{ - if (CLOSURE_IS_DISK_BSSRDF(sc->type)) { - return subsurface_scatter_disk(kg, ss_isect, sd, sc, lcg_state, bssrdf_u, bssrdf_v, all); - } - else { - return subsurface_random_walk(kg, ss_isect, sd, state, sc, bssrdf_u, bssrdf_v, all); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index c8e01677d09..bf9b94c1753 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -78,7 +78,7 @@ KERNEL_TEX(KernelShader, __shaders) KERNEL_TEX(float, __lookup_table) /* sobol */ -KERNEL_TEX(uint, __sample_pattern_lut) +KERNEL_TEX(float, __sample_pattern_lut) /* image textures */ KERNEL_TEX(TextureInfo, __texture_info) diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 7cbe18acf28..927e60e8729 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __KERNEL_TYPES_H__ -#define __KERNEL_TYPES_H__ +#pragma once #if !defined(__KERNEL_GPU__) && defined(WITH_EMBREE) # include @@ -60,27 +59,9 @@ CCL_NAMESPACE_BEGIN #define PRIM_NONE (~0) #define LAMP_NONE (~0) #define ID_NONE (0.0f) +#define PASS_UNUSED (~0) -#define VOLUME_STACK_SIZE 32 - -/* Split kernel constants */ -#define WORK_POOL_SIZE_GPU 64 -#define WORK_POOL_SIZE_CPU 1 -#ifdef __KERNEL_GPU__ -# define WORK_POOL_SIZE WORK_POOL_SIZE_GPU -#else -# define WORK_POOL_SIZE WORK_POOL_SIZE_CPU -#endif - -#define SHADER_SORT_BLOCK_SIZE 2048 - -#ifdef __KERNEL_OPENCL__ -# define SHADER_SORT_LOCAL_SIZE 64 -#elif defined(__KERNEL_CUDA__) -# define SHADER_SORT_LOCAL_SIZE 32 -#else -# define SHADER_SORT_LOCAL_SIZE 1 -#endif +#define VOLUME_STACK_SIZE 4 /* Kernel features */ #define __SOBOL__ @@ -93,7 +74,7 @@ CCL_NAMESPACE_BEGIN #define __INTERSECTION_REFINE__ #define __CLAMP_SAMPLE__ #define __PATCH_EVAL__ -#define __SHADOW_TRICKS__ +#define __SHADOW_CATCHER__ #define __DENOISING_FEATURES__ #define __SHADER_RAYTRACE__ #define __AO__ @@ -102,7 +83,6 @@ CCL_NAMESPACE_BEGIN #define __SVM__ #define __EMISSION__ #define __HOLDOUT__ -#define __MULTI_CLOSURE__ #define __TRANSPARENT_SHADOWS__ #define __BACKGROUND_MIS__ #define __LAMP_MIS__ @@ -112,7 +92,6 @@ CCL_NAMESPACE_BEGIN #define __PRINCIPLED__ #define __SUBSURFACE__ #define __VOLUME__ -#define __VOLUME_SCATTER__ #define __CMJ__ #define __SHADOW_RECORD_ALL__ #define __BRANCHED_PATH__ @@ -122,106 +101,60 @@ CCL_NAMESPACE_BEGIN # ifdef WITH_OSL # define __OSL__ # endif -# define __VOLUME_DECOUPLED__ # define __VOLUME_RECORD_ALL__ #endif /* __KERNEL_CPU__ */ -#ifdef __KERNEL_CUDA__ -# ifdef __SPLIT_KERNEL__ -# undef __BRANCHED_PATH__ -# endif -#endif /* __KERNEL_CUDA__ */ - #ifdef __KERNEL_OPTIX__ # undef __BAKING__ -# undef __BRANCHED_PATH__ #endif /* __KERNEL_OPTIX__ */ -#ifdef __KERNEL_OPENCL__ -#endif /* __KERNEL_OPENCL__ */ - /* Scene-based selective features compilation. */ -#ifdef __NO_CAMERA_MOTION__ -# undef __CAMERA_MOTION__ -#endif -#ifdef __NO_OBJECT_MOTION__ -# undef __OBJECT_MOTION__ -#endif -#ifdef __NO_HAIR__ -# undef __HAIR__ -#endif -#ifdef __NO_VOLUME__ -# undef __VOLUME__ -# undef __VOLUME_SCATTER__ -#endif -#ifdef __NO_SUBSURFACE__ -# undef __SUBSURFACE__ -#endif -#ifdef __NO_BAKING__ -# undef __BAKING__ -#endif -#ifdef __NO_BRANCHED_PATH__ -# undef __BRANCHED_PATH__ -#endif -#ifdef __NO_PATCH_EVAL__ -# undef __PATCH_EVAL__ -#endif -#ifdef __NO_TRANSPARENT__ -# undef __TRANSPARENT_SHADOWS__ -#endif -#ifdef __NO_SHADOW_TRICKS__ -# undef __SHADOW_TRICKS__ -#endif -#ifdef __NO_PRINCIPLED__ -# undef __PRINCIPLED__ -#endif -#ifdef __NO_DENOISING__ -# undef __DENOISING_FEATURES__ -#endif -#ifdef __NO_SHADER_RAYTRACE__ -# undef __SHADER_RAYTRACE__ +#ifdef __KERNEL_FEATURES__ +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_CAMERA_MOTION) +# undef __CAMERA_MOTION__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_OBJECT_MOTION) +# undef __OBJECT_MOTION__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_HAIR) +# undef __HAIR__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_VOLUME) +# undef __VOLUME__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_SUBSURFACE) +# undef __SUBSURFACE__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_BAKING) +# undef __BAKING__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_PATCH_EVALUATION) +# undef __PATCH_EVAL__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_TRANSPARENT) +# undef __TRANSPARENT_SHADOWS__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_SHADOW_CATCHER) +# undef __SHADOW_CATCHER__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_PRINCIPLED) +# undef __PRINCIPLED__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_DENOISING) +# undef __DENOISING_FEATURES__ +# endif #endif #ifdef WITH_CYCLES_DEBUG_NAN # define __KERNEL_DEBUG_NAN__ #endif +/* Features that enable others */ + #if defined(__SUBSURFACE__) || defined(__SHADER_RAYTRACE__) # define __BVH_LOCAL__ #endif -/* Shader Evaluation */ - -typedef enum ShaderEvalType { - SHADER_EVAL_DISPLACE, - SHADER_EVAL_BACKGROUND, - /* bake types */ - SHADER_EVAL_BAKE, /* no real shade, it's used in the code to - * differentiate the type of shader eval from the above - */ - /* data passes */ - SHADER_EVAL_NORMAL, - SHADER_EVAL_UV, - SHADER_EVAL_ROUGHNESS, - SHADER_EVAL_DIFFUSE_COLOR, - SHADER_EVAL_GLOSSY_COLOR, - SHADER_EVAL_TRANSMISSION_COLOR, - SHADER_EVAL_EMISSION, - SHADER_EVAL_AOV_COLOR, - SHADER_EVAL_AOV_VALUE, - - /* light passes */ - SHADER_EVAL_AO, - SHADER_EVAL_COMBINED, - SHADER_EVAL_SHADOW, - SHADER_EVAL_DIFFUSE, - SHADER_EVAL_GLOSSY, - SHADER_EVAL_TRANSMISSION, - - /* extra */ - SHADER_EVAL_ENVIRONMENT, -} ShaderEvalType; - /* Path Tracing * note we need to keep the u/v pairs at even values */ @@ -252,8 +185,7 @@ enum PathTraceDimension { enum SamplingPattern { SAMPLING_PATTERN_SOBOL = 0, - SAMPLING_PATTERN_CMJ = 1, - SAMPLING_PATTERN_PMJ = 2, + SAMPLING_PATTERN_PMJ = 1, SAMPLING_NUM_PATTERNS, }; @@ -261,7 +193,12 @@ enum SamplingPattern { /* these flags values correspond to raytypes in osl.cpp, so keep them in sync! */ enum PathRayFlag { - /* Ray visibility. */ + /* -------------------------------------------------------------------- + * Ray visibility. + * + * NOTE: Recalculated after a surface bounce. + */ + PATH_RAY_CAMERA = (1 << 0), PATH_RAY_REFLECT = (1 << 1), PATH_RAY_TRANSMIT = (1 << 2), @@ -269,57 +206,106 @@ enum PathRayFlag { PATH_RAY_GLOSSY = (1 << 4), PATH_RAY_SINGULAR = (1 << 5), PATH_RAY_TRANSPARENT = (1 << 6), + PATH_RAY_VOLUME_SCATTER = (1 << 7), /* Shadow ray visibility. */ - PATH_RAY_SHADOW_OPAQUE_NON_CATCHER = (1 << 7), - PATH_RAY_SHADOW_OPAQUE_CATCHER = (1 << 8), - PATH_RAY_SHADOW_OPAQUE = (PATH_RAY_SHADOW_OPAQUE_NON_CATCHER | PATH_RAY_SHADOW_OPAQUE_CATCHER), - PATH_RAY_SHADOW_TRANSPARENT_NON_CATCHER = (1 << 9), - PATH_RAY_SHADOW_TRANSPARENT_CATCHER = (1 << 10), - PATH_RAY_SHADOW_TRANSPARENT = (PATH_RAY_SHADOW_TRANSPARENT_NON_CATCHER | - PATH_RAY_SHADOW_TRANSPARENT_CATCHER), - PATH_RAY_SHADOW_NON_CATCHER = (PATH_RAY_SHADOW_OPAQUE_NON_CATCHER | - PATH_RAY_SHADOW_TRANSPARENT_NON_CATCHER), + PATH_RAY_SHADOW_OPAQUE = (1 << 8), + PATH_RAY_SHADOW_TRANSPARENT = (1 << 9), PATH_RAY_SHADOW = (PATH_RAY_SHADOW_OPAQUE | PATH_RAY_SHADOW_TRANSPARENT), - /* Unused, free to reuse. */ - PATH_RAY_UNUSED = (1 << 11), + /* Special flag to tag unaligned BVH nodes. + * Only set and used in BVH nodes to distinguish how to interpret bounding box information stored + * in the node (either it should be intersected as AABB or as OBB). */ + PATH_RAY_NODE_UNALIGNED = (1 << 10), - /* Ray visibility for volume scattering. */ - PATH_RAY_VOLUME_SCATTER = (1 << 12), + /* Subset of flags used for ray visibility for intersection. + * + * NOTE: SHADOW_CATCHER macros below assume there are no more than + * 16 visibility bits. */ + PATH_RAY_ALL_VISIBILITY = ((1 << 11) - 1), - /* Special flag to tag unaligned BVH nodes. */ - PATH_RAY_NODE_UNALIGNED = (1 << 13), - - PATH_RAY_ALL_VISIBILITY = ((1 << 14) - 1), + /* -------------------------------------------------------------------- + * Path flags. + */ /* Don't apply multiple importance sampling weights to emission from * lamp or surface hits, because they were not direct light sampled. */ - PATH_RAY_MIS_SKIP = (1 << 14), + PATH_RAY_MIS_SKIP = (1 << 11), + /* Diffuse bounce earlier in the path, skip SSS to improve performance * and avoid branching twice with disk sampling SSS. */ - PATH_RAY_DIFFUSE_ANCESTOR = (1 << 15), + PATH_RAY_DIFFUSE_ANCESTOR = (1 << 12), + /* Single pass has been written. */ - PATH_RAY_SINGLE_PASS_DONE = (1 << 16), - /* Ray is behind a shadow catcher. */ - PATH_RAY_SHADOW_CATCHER = (1 << 17), - /* Store shadow data for shadow catcher or denoising. */ - PATH_RAY_STORE_SHADOW_INFO = (1 << 18), + PATH_RAY_SINGLE_PASS_DONE = (1 << 13), + /* Zero background alpha, for camera or transparent glass rays. */ - PATH_RAY_TRANSPARENT_BACKGROUND = (1 << 19), + PATH_RAY_TRANSPARENT_BACKGROUND = (1 << 14), + /* Terminate ray immediately at next bounce. */ - PATH_RAY_TERMINATE_IMMEDIATE = (1 << 20), + PATH_RAY_TERMINATE_ON_NEXT_SURFACE = (1 << 15), + PATH_RAY_TERMINATE_IN_NEXT_VOLUME = (1 << 16), + /* Ray is to be terminated, but continue with transparent bounces and * emission as long as we encounter them. This is required to make the * MIS between direct and indirect light rays match, as shadow rays go * through transparent surfaces to reach emission too. */ - PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1 << 21), + PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1 << 17), + + /* Terminate ray immediately after volume shading. */ + PATH_RAY_TERMINATE_AFTER_VOLUME = (1 << 18), + /* Ray is to be terminated. */ - PATH_RAY_TERMINATE = (PATH_RAY_TERMINATE_IMMEDIATE | PATH_RAY_TERMINATE_AFTER_TRANSPARENT), + PATH_RAY_TERMINATE = (PATH_RAY_TERMINATE_ON_NEXT_SURFACE | PATH_RAY_TERMINATE_IN_NEXT_VOLUME | + PATH_RAY_TERMINATE_AFTER_TRANSPARENT | PATH_RAY_TERMINATE_AFTER_VOLUME), + /* Path and shader is being evaluated for direct lighting emission. */ - PATH_RAY_EMISSION = (1 << 22) + PATH_RAY_EMISSION = (1 << 19), + + /* Perform subsurface scattering. */ + PATH_RAY_SUBSURFACE = (1 << 20), + + /* Contribute to denoising features. */ + PATH_RAY_DENOISING_FEATURES = (1 << 21), + + /* Render pass categories. */ + PATH_RAY_REFLECT_PASS = (1 << 22), + PATH_RAY_TRANSMISSION_PASS = (1 << 23), + PATH_RAY_VOLUME_PASS = (1 << 24), + PATH_RAY_ANY_PASS = (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS | PATH_RAY_VOLUME_PASS), + + /* Shadow ray is for a light or surface. */ + PATH_RAY_SHADOW_FOR_LIGHT = (1 << 25), + + /* A shadow catcher object was hit and the path was split into two. */ + PATH_RAY_SHADOW_CATCHER_HIT = (1 << 26), + + /* A shadow catcher object was hit and this path traces only shadow catchers, writing them into + * their dedicated pass for later division. + * + * NOTE: Is not covered with `PATH_RAY_ANY_PASS` because shadow catcher does special handling + * which is separate from the light passes. */ + PATH_RAY_SHADOW_CATCHER_PASS = (1 << 27), + + /* Path is evaluating background for an approximate shadow catcher with non-transparent film. */ + PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1 << 28), }; +/* Configure ray visibility bits for rays and objects respectively, + * to make shadow catchers work. + * + * On shadow catcher paths we want to ignore any intersections with non-catchers, + * whereas on regular paths we want to intersect all objects. */ + +#define SHADOW_CATCHER_VISIBILITY_SHIFT(visibility) ((visibility) << 16) + +#define SHADOW_CATCHER_PATH_VISIBILITY(path_flag, visibility) \ + (((path_flag)&PATH_RAY_SHADOW_CATCHER_PASS) ? SHADOW_CATCHER_VISIBILITY_SHIFT(visibility) : \ + (visibility)) + +#define SHADOW_CATCHER_OBJECT_VISIBILITY(is_shadow_catcher, visibility) \ + (((is_shadow_catcher) ? SHADOW_CATCHER_VISIBILITY_SHIFT(visibility) : 0) | (visibility)) + /* Closure Label */ typedef enum ClosureLabel { @@ -332,6 +318,7 @@ typedef enum ClosureLabel { LABEL_TRANSPARENT = 32, LABEL_VOLUME_SCATTER = 64, LABEL_TRANSMIT_TRANSPARENT = 128, + LABEL_SUBSURFACE_SCATTER = 256, } ClosureLabel; /* Render Passes */ @@ -339,17 +326,35 @@ typedef enum ClosureLabel { #define PASS_NAME_JOIN(a, b) a##_##b #define PASSMASK(pass) (1 << ((PASS_NAME_JOIN(PASS, pass)) % 32)) -#define PASSMASK_COMPONENT(comp) \ - (PASSMASK(PASS_NAME_JOIN(comp, DIRECT)) | PASSMASK(PASS_NAME_JOIN(comp, INDIRECT)) | \ - PASSMASK(PASS_NAME_JOIN(comp, COLOR))) - +// NOTE: Keep in sync with `Pass::get_type_enum()`. typedef enum PassType { PASS_NONE = 0, - /* Main passes */ + /* Light Passes */ PASS_COMBINED = 1, - PASS_DEPTH, + PASS_EMISSION, + PASS_BACKGROUND, + PASS_AO, + PASS_SHADOW, + PASS_DIFFUSE, + PASS_DIFFUSE_DIRECT, + PASS_DIFFUSE_INDIRECT, + PASS_GLOSSY, + PASS_GLOSSY_DIRECT, + PASS_GLOSSY_INDIRECT, + PASS_TRANSMISSION, + PASS_TRANSMISSION_DIRECT, + PASS_TRANSMISSION_INDIRECT, + PASS_VOLUME, + PASS_VOLUME_DIRECT, + PASS_VOLUME_INDIRECT, + PASS_CATEGORY_LIGHT_END = 31, + + /* Data passes */ + PASS_DEPTH = 32, + PASS_POSITION, PASS_NORMAL, + PASS_ROUGHNESS, PASS_UV, PASS_OBJECT_ID, PASS_MATERIAL_ID, @@ -361,31 +366,35 @@ typedef enum PassType { PASS_AOV_VALUE, PASS_ADAPTIVE_AUX_BUFFER, PASS_SAMPLE_COUNT, - PASS_CATEGORY_MAIN_END = 31, - - PASS_MIST = 32, - PASS_EMISSION, - PASS_BACKGROUND, - PASS_AO, - PASS_SHADOW, - PASS_LIGHT, /* no real pass, used to force use_light_pass */ - PASS_DIFFUSE_DIRECT, - PASS_DIFFUSE_INDIRECT, PASS_DIFFUSE_COLOR, - PASS_GLOSSY_DIRECT, - PASS_GLOSSY_INDIRECT, PASS_GLOSSY_COLOR, - PASS_TRANSMISSION_DIRECT, - PASS_TRANSMISSION_INDIRECT, PASS_TRANSMISSION_COLOR, - PASS_VOLUME_DIRECT = 50, - PASS_VOLUME_INDIRECT, /* No Scatter color since it's tricky to define what it would even mean. */ - PASS_CATEGORY_LIGHT_END = 63, + PASS_MIST, + PASS_DENOISING_NORMAL, + PASS_DENOISING_ALBEDO, + + /* PASS_SHADOW_CATCHER accumulates contribution of shadow catcher object which is not affected by + * any other object. The pass accessor will divide the combined pass by the shadow catcher. The + * result of this division is then to be multiplied with the backdrop. The alpha channel of this + * pass contains number of samples which contributed to the color components of the pass. + * + * PASS_SHADOW_CATCHER_SAMPLE_COUNT contains number of samples for which the path split + * happenned. + * + * PASS_SHADOW_CATCHER_MATTE contains pass which contains non-catcher objects. This pass is to be + * alpha-overed onto the backdrop (after multiplication). */ + PASS_SHADOW_CATCHER, + PASS_SHADOW_CATCHER_SAMPLE_COUNT, + PASS_SHADOW_CATCHER_MATTE, + + PASS_CATEGORY_DATA_END = 63, PASS_BAKE_PRIMITIVE, PASS_BAKE_DIFFERENTIAL, - PASS_CATEGORY_BAKE_END = 95 + PASS_CATEGORY_BAKE_END = 95, + + PASS_NUM, } PassType; #define PASS_ANY (~0) @@ -398,158 +407,9 @@ typedef enum CryptomatteType { CRYPT_ACCURATE = (1 << 3), } CryptomatteType; -typedef enum DenoisingPassOffsets { - DENOISING_PASS_NORMAL = 0, - DENOISING_PASS_NORMAL_VAR = 3, - DENOISING_PASS_ALBEDO = 6, - DENOISING_PASS_ALBEDO_VAR = 9, - DENOISING_PASS_DEPTH = 12, - DENOISING_PASS_DEPTH_VAR = 13, - DENOISING_PASS_SHADOW_A = 14, - DENOISING_PASS_SHADOW_B = 17, - DENOISING_PASS_COLOR = 20, - DENOISING_PASS_COLOR_VAR = 23, - DENOISING_PASS_CLEAN = 26, - - DENOISING_PASS_PREFILTERED_DEPTH = 0, - DENOISING_PASS_PREFILTERED_NORMAL = 1, - DENOISING_PASS_PREFILTERED_SHADOWING = 4, - DENOISING_PASS_PREFILTERED_ALBEDO = 5, - DENOISING_PASS_PREFILTERED_COLOR = 8, - DENOISING_PASS_PREFILTERED_VARIANCE = 11, - DENOISING_PASS_PREFILTERED_INTENSITY = 14, - - DENOISING_PASS_SIZE_BASE = 26, - DENOISING_PASS_SIZE_CLEAN = 3, - DENOISING_PASS_SIZE_PREFILTERED = 15, -} DenoisingPassOffsets; - -typedef enum eBakePassFilter { - BAKE_FILTER_NONE = 0, - BAKE_FILTER_DIRECT = (1 << 0), - BAKE_FILTER_INDIRECT = (1 << 1), - BAKE_FILTER_COLOR = (1 << 2), - BAKE_FILTER_DIFFUSE = (1 << 3), - BAKE_FILTER_GLOSSY = (1 << 4), - BAKE_FILTER_TRANSMISSION = (1 << 5), - BAKE_FILTER_EMISSION = (1 << 6), - BAKE_FILTER_AO = (1 << 7), -} eBakePassFilter; - -typedef enum BakePassFilterCombos { - BAKE_FILTER_COMBINED = (BAKE_FILTER_DIRECT | BAKE_FILTER_INDIRECT | BAKE_FILTER_DIFFUSE | - BAKE_FILTER_GLOSSY | BAKE_FILTER_TRANSMISSION | BAKE_FILTER_EMISSION | - BAKE_FILTER_AO), - BAKE_FILTER_DIFFUSE_DIRECT = (BAKE_FILTER_DIRECT | BAKE_FILTER_DIFFUSE), - BAKE_FILTER_GLOSSY_DIRECT = (BAKE_FILTER_DIRECT | BAKE_FILTER_GLOSSY), - BAKE_FILTER_TRANSMISSION_DIRECT = (BAKE_FILTER_DIRECT | BAKE_FILTER_TRANSMISSION), - BAKE_FILTER_DIFFUSE_INDIRECT = (BAKE_FILTER_INDIRECT | BAKE_FILTER_DIFFUSE), - BAKE_FILTER_GLOSSY_INDIRECT = (BAKE_FILTER_INDIRECT | BAKE_FILTER_GLOSSY), - BAKE_FILTER_TRANSMISSION_INDIRECT = (BAKE_FILTER_INDIRECT | BAKE_FILTER_TRANSMISSION), -} BakePassFilterCombos; - -typedef enum DenoiseFlag { - DENOISING_CLEAN_DIFFUSE_DIR = (1 << 0), - DENOISING_CLEAN_DIFFUSE_IND = (1 << 1), - DENOISING_CLEAN_GLOSSY_DIR = (1 << 2), - DENOISING_CLEAN_GLOSSY_IND = (1 << 3), - DENOISING_CLEAN_TRANSMISSION_DIR = (1 << 4), - DENOISING_CLEAN_TRANSMISSION_IND = (1 << 5), - DENOISING_CLEAN_ALL_PASSES = (1 << 6) - 1, -} DenoiseFlag; - -typedef ccl_addr_space struct PathRadianceState { -#ifdef __PASSES__ - float3 diffuse; - float3 glossy; - float3 transmission; - float3 volume; - - float3 direct; -#endif -} PathRadianceState; - -typedef ccl_addr_space struct PathRadiance { -#ifdef __PASSES__ - int use_light_pass; -#endif - - float transparent; - float3 emission; -#ifdef __PASSES__ - float3 background; - float3 ao; - - float3 indirect; - float3 direct_emission; - - float3 color_diffuse; - float3 color_glossy; - float3 color_transmission; - - float3 direct_diffuse; - float3 direct_glossy; - float3 direct_transmission; - float3 direct_volume; - - float3 indirect_diffuse; - float3 indirect_glossy; - float3 indirect_transmission; - float3 indirect_volume; - - float3 shadow; - float mist; -#endif - - struct PathRadianceState state; - -#ifdef __SHADOW_TRICKS__ - /* Total light reachable across the path, ignoring shadow blocked queries. */ - float3 path_total; - /* Total light reachable across the path with shadow blocked queries - * applied here. - * - * Dividing this figure by path_total will give estimate of shadow pass. - */ - float3 path_total_shaded; - - /* Color of the background on which shadow is alpha-overed. */ - float3 shadow_background_color; - - /* Path radiance sum and throughput at the moment when ray hits shadow - * catcher object. - */ - float shadow_throughput; - - /* Accumulated transparency along the path after shadow catcher bounce. */ - float shadow_transparency; - - /* Indicate if any shadow catcher data is set. */ - int has_shadow_catcher; -#endif - -#ifdef __DENOISING_FEATURES__ - float3 denoising_normal; - float3 denoising_albedo; - float denoising_depth; -#endif /* __DENOISING_FEATURES__ */ -} PathRadiance; - typedef struct BsdfEval { -#ifdef __PASSES__ - int use_light_pass; -#endif - float3 diffuse; -#ifdef __PASSES__ float3 glossy; - float3 transmission; - float3 transparent; - float3 volume; -#endif -#ifdef __SHADOW_TRICKS__ - float3 sum_no_mis; -#endif } BsdfEval; /* Shader Flag */ @@ -564,8 +424,10 @@ typedef enum ShaderFlag { SHADER_EXCLUDE_TRANSMIT = (1 << 25), SHADER_EXCLUDE_CAMERA = (1 << 24), SHADER_EXCLUDE_SCATTER = (1 << 23), + SHADER_EXCLUDE_SHADOW_CATCHER = (1 << 22), SHADER_EXCLUDE_ANY = (SHADER_EXCLUDE_DIFFUSE | SHADER_EXCLUDE_GLOSSY | SHADER_EXCLUDE_TRANSMIT | - SHADER_EXCLUDE_CAMERA | SHADER_EXCLUDE_SCATTER), + SHADER_EXCLUDE_CAMERA | SHADER_EXCLUDE_SCATTER | + SHADER_EXCLUDE_SHADOW_CATCHER), SHADER_MASK = ~(SHADER_SMOOTH_NORMAL | SHADER_CAST_SHADOW | SHADER_AREA_LIGHT | SHADER_USE_MIS | SHADER_EXCLUDE_ANY) @@ -612,29 +474,14 @@ typedef struct differential { /* Ray */ typedef struct Ray { -/* TODO(sergey): This is only needed because current AMD - * compiler has hard time building the kernel with this - * reshuffle. And at the same time reshuffle will cause - * less optimal CPU code in certain places. - * - * We'll get rid of this nasty exception once AMD compiler - * is fixed. - */ -#ifndef __KERNEL_OPENCL_AMD__ float3 P; /* origin */ float3 D; /* direction */ float t; /* length of the ray */ float time; /* time (for motion blur) */ -#else - float t; /* length of the ray */ - float time; /* time (for motion blur) */ - float3 P; /* origin */ - float3 D; /* direction */ -#endif #ifdef __RAY_DIFFERENTIALS__ - differential3 dP; - differential3 dD; + float dP; + float dD; #endif } Ray; @@ -661,9 +508,6 @@ typedef enum PrimitiveType { PRIMITIVE_CURVE_RIBBON = (1 << 4), PRIMITIVE_MOTION_CURVE_RIBBON = (1 << 5), PRIMITIVE_VOLUME = (1 << 6), - /* Lamp primitive is not included below on purpose, - * since it is no real traceable primitive. - */ PRIMITIVE_LAMP = (1 << 7), PRIMITIVE_ALL_TRIANGLE = (PRIMITIVE_TRIANGLE | PRIMITIVE_MOTION_TRIANGLE), @@ -672,16 +516,14 @@ typedef enum PrimitiveType { PRIMITIVE_ALL_VOLUME = (PRIMITIVE_VOLUME), PRIMITIVE_ALL_MOTION = (PRIMITIVE_MOTION_TRIANGLE | PRIMITIVE_MOTION_CURVE_THICK | PRIMITIVE_MOTION_CURVE_RIBBON), - PRIMITIVE_ALL = (PRIMITIVE_ALL_TRIANGLE | PRIMITIVE_ALL_CURVE | PRIMITIVE_ALL_VOLUME), + PRIMITIVE_ALL = (PRIMITIVE_ALL_TRIANGLE | PRIMITIVE_ALL_CURVE | PRIMITIVE_ALL_VOLUME | + PRIMITIVE_LAMP), - /* Total number of different traceable primitives. - * NOTE: This is an actual value, not a bitflag. - */ - PRIMITIVE_NUM_TOTAL = 7, + PRIMITIVE_NUM = 8, } PrimitiveType; -#define PRIMITIVE_PACK_SEGMENT(type, segment) ((segment << PRIMITIVE_NUM_TOTAL) | (type)) -#define PRIMITIVE_UNPACK_SEGMENT(type) (type >> PRIMITIVE_NUM_TOTAL) +#define PRIMITIVE_PACK_SEGMENT(type, segment) ((segment << PRIMITIVE_NUM) | (type)) +#define PRIMITIVE_UNPACK_SEGMENT(type) (type >> PRIMITIVE_NUM) typedef enum CurveShapeType { CURVE_RIBBON = 0, @@ -760,20 +602,14 @@ typedef struct AttributeDescriptor { /* Closure data */ -#ifdef __MULTI_CLOSURE__ -# ifdef __SPLIT_KERNEL__ -# define MAX_CLOSURE 1 -# else -# ifndef __MAX_CLOSURE__ -# define MAX_CLOSURE 64 -# else -# define MAX_CLOSURE __MAX_CLOSURE__ -# endif -# endif +#ifndef __MAX_CLOSURE__ +# define MAX_CLOSURE 64 #else -# define MAX_CLOSURE 1 +# define MAX_CLOSURE __MAX_CLOSURE__ #endif +#define MAX_VOLUME_CLOSURE 8 + /* This struct is the base class for all closures. The common members are * duplicated in all derived classes since we don't have C++ in the kernel * yet, and because it lets us lay out the members to minimize padding. The @@ -866,11 +702,14 @@ enum ShaderDataFlag { SD_NEED_VOLUME_ATTRIBUTES = (1 << 28), /* Shader has emission */ SD_HAS_EMISSION = (1 << 29), + /* Shader has raytracing */ + SD_HAS_RAYTRACE = (1 << 30), SD_SHADER_FLAGS = (SD_USE_MIS | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | SD_HAS_ONLY_VOLUME | SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | SD_VOLUME_EQUIANGULAR | SD_VOLUME_MIS | SD_VOLUME_CUBIC | SD_HAS_BUMP | SD_HAS_DISPLACEMENT | - SD_HAS_CONSTANT_EMISSION | SD_NEED_VOLUME_ATTRIBUTES) + SD_HAS_CONSTANT_EMISSION | SD_NEED_VOLUME_ATTRIBUTES | SD_HAS_EMISSION | + SD_HAS_RAYTRACE) }; /* Object flags. */ @@ -955,19 +794,19 @@ typedef ccl_addr_space struct ccl_align(16) ShaderData #endif #ifdef __OBJECT_MOTION__ - /* object <-> world space transformations, cached to avoid - * re-interpolating them constantly for shading */ - Transform ob_tfm; - Transform ob_itfm; + /* Object <-> world space transformations for motion blur, cached to avoid + * re-interpolating them constantly for shading. */ + Transform ob_tfm_motion; + Transform ob_itfm_motion; #endif /* ray start position, only set for backgrounds */ float3 ray_P; - differential3 ray_dP; + float ray_dP; #ifdef __OSL__ - struct KernelGlobals *osl_globals; - struct PathState *osl_path_state; + const struct KernelGlobals *osl_globals; + const struct IntegratorStateCPU *osl_path_state; #endif /* LCG state for closures that require additional random numbers. */ @@ -976,7 +815,6 @@ typedef ccl_addr_space struct ccl_align(16) ShaderData /* Closure data, we store a fixed array of closures */ int num_closure; int num_closure_left; - float randb_closure; float3 svm_closure_weight; /* Closure weights summed directly, so we can evaluate @@ -998,7 +836,22 @@ typedef ccl_addr_space struct ccl_align(16) ShaderDataTinyStorage ShaderDataTinyStorage; #define AS_SHADER_DATA(shader_data_tiny_storage) ((ShaderData *)shader_data_tiny_storage) -/* Path State */ +/* Compact volume closures storage. + * + * Used for decoupled direct/indirect light closure storage. */ + +ccl_addr_space struct ShaderVolumeClosure { + float3 weight; + float sample_weight; + float g; +}; + +ccl_addr_space struct ShaderVolumePhases { + ShaderVolumeClosure closure[MAX_VOLUME_CLOSURE]; + int num_closure; +}; + +/* Volume Stack */ #ifdef __VOLUME__ typedef struct VolumeStack { @@ -1007,53 +860,6 @@ typedef struct VolumeStack { } VolumeStack; #endif -typedef struct PathState { - /* see enum PathRayFlag */ - int flag; - - /* random number generator state */ - uint rng_hash; /* per pixel hash */ - int rng_offset; /* dimension offset */ - int sample; /* path sample number */ - int num_samples; /* total number of times this path will be sampled */ - float branch_factor; /* number of branches in indirect paths */ - - /* bounce counting */ - int bounce; - int diffuse_bounce; - int glossy_bounce; - int transmission_bounce; - int transparent_bounce; - -#ifdef __DENOISING_FEATURES__ - float denoising_feature_weight; - float3 denoising_feature_throughput; -#endif /* __DENOISING_FEATURES__ */ - - /* multiple importance sampling */ - float min_ray_pdf; /* smallest bounce pdf over entire path up to now */ - float ray_pdf; /* last bounce pdf */ -#ifdef __LAMP_MIS__ - float ray_t; /* accumulated distance through transparent surfaces */ -#endif - - /* volume rendering */ -#ifdef __VOLUME__ - int volume_bounce; - int volume_bounds_bounce; - VolumeStack volume_stack[VOLUME_STACK_SIZE]; -#endif -} PathState; - -#ifdef __VOLUME__ -typedef struct VolumeState { -# ifdef __SPLIT_KERNEL__ -# else - PathState ps; -# endif -} VolumeState; -#endif - /* Struct to gather multiple nearby intersections. */ typedef struct LocalIntersection { Ray ray; @@ -1064,20 +870,6 @@ typedef struct LocalIntersection { float3 Ng[LOCAL_MAX_HITS]; } LocalIntersection; -/* Subsurface */ - -/* Struct to gather SSS indirect rays and delay tracing them. */ -typedef struct SubsurfaceIndirectRays { - PathState state[BSSRDF_MAX_HITS]; - - int num_rays; - - struct Ray rays[BSSRDF_MAX_HITS]; - float3 throughputs[BSSRDF_MAX_HITS]; - struct PathRadianceState L_state[BSSRDF_MAX_HITS]; -} SubsurfaceIndirectRays; -static_assert(BSSRDF_MAX_HITS <= LOCAL_MAX_HITS, "BSSRDF hits too high."); - /* Constant Kernel Data * * These structs are passed from CPU to various devices, and the struct layout @@ -1128,7 +920,7 @@ typedef struct KernelCamera { /* render size */ float width, height; - int resolution; + int pad1; /* anamorphic lens bokeh */ float inv_aperture_ratio; @@ -1169,11 +961,12 @@ typedef struct KernelFilm { int light_pass_flag; int pass_stride; - int use_light_pass; int pass_combined; int pass_depth; + int pass_position; int pass_normal; + int pass_roughness; int pass_motion; int pass_motion_weight; @@ -1202,7 +995,13 @@ typedef struct KernelFilm { int pass_shadow; float pass_shadow_scale; + + int pass_shadow_catcher; + int pass_shadow_catcher_sample_count; + int pass_shadow_catcher_matte; + int filter_table_offset; + int cryptomatte_passes; int cryptomatte_depth; int pass_cryptomatte; @@ -1215,15 +1014,11 @@ typedef struct KernelFilm { float mist_inv_depth; float mist_falloff; - int pass_denoising_data; - int pass_denoising_clean; - int denoising_flags; + int pass_denoising_normal; + int pass_denoising_albedo; int pass_aov_color; int pass_aov_value; - int pass_aov_color_num; - int pass_aov_value_num; - int pad1, pad2, pad3; /* XYZ to rendering color space transform. float4 instead of float3 to * ensure consistent padding/alignment across devices. */ @@ -1234,19 +1029,54 @@ typedef struct KernelFilm { int pass_bake_primitive; int pass_bake_differential; - int pad; - /* viewport rendering options */ - int display_pass_stride; - int display_pass_components; - int display_divide_pass_stride; - int use_display_exposure; - int use_display_pass_alpha; + int use_approximate_shadow_catcher; - int pad4, pad5, pad6; + int pad1, pad2, pad3; } KernelFilm; static_assert_align(KernelFilm, 16); +typedef struct KernelFilmConvert { + int pass_offset; + int pass_stride; + + int pass_use_exposure; + int pass_use_filter; + + int pass_divide; + int pass_indirect; + + int pass_combined; + int pass_sample_count; + int pass_adaptive_aux_buffer; + int pass_motion_weight; + int pass_shadow_catcher; + int pass_shadow_catcher_sample_count; + int pass_shadow_catcher_matte; + int pass_background; + + float scale; + float exposure; + float scale_exposure; + + int use_approximate_shadow_catcher; + int use_approximate_shadow_catcher_background; + int show_active_pixels; + + /* Number of components to write to. */ + int num_components; + + /* Number of floats per pixel. When zero is the same as `num_components`. + * NOTE: Is ignored for half4 destination. */ + int pixel_stride; + + int is_denoised; + + /* Padding. */ + int pad1; +} KernelFilmConvert; +static_assert_align(KernelFilmConvert, 16); + typedef struct KernelBackground { /* only shader index */ int surface_shader; @@ -1255,11 +1085,6 @@ typedef struct KernelBackground { int transparent; float transparent_roughness_squared_threshold; - /* ambient occlusion */ - float ao_factor; - float ao_distance; - float ao_bounces_factor; - /* portal sampling */ float portal_weight; int num_portals; @@ -1277,13 +1102,15 @@ typedef struct KernelBackground { int map_res_y; int use_mis; + + /* Padding */ + int pad1, pad2, pad3; } KernelBackground; static_assert_align(KernelBackground, 16); typedef struct KernelIntegrator { /* emission */ int use_direct_light; - int use_ambient_occlusion; int num_distribution; int num_all_lights; float pdf_triangles; @@ -1299,7 +1126,10 @@ typedef struct KernelIntegrator { int max_transmission_bounce; int max_volume_bounce; + /* AO bounces */ int ao_bounces; + float ao_bounces_distance; + float ao_bounces_factor; /* transparent */ int transparent_min_bounce; @@ -1318,39 +1148,20 @@ typedef struct KernelIntegrator { float sample_clamp_direct; float sample_clamp_indirect; - /* branched path */ - int branched; - int volume_decoupled; - int diffuse_samples; - int glossy_samples; - int transmission_samples; - int ao_samples; - int mesh_light_samples; - int subsurface_samples; - int sample_all_lights_direct; - int sample_all_lights_indirect; - /* mis */ int use_lamp_mis; /* sampler */ int sampling_pattern; - int aa_samples; - int adaptive_min_samples; - int adaptive_step; - int adaptive_stop_per_sample; - float adaptive_threshold; /* volume render */ int use_volumes; int volume_max_steps; float volume_step_rate; - int volume_samples; - int start_sample; - - int max_closures; + int has_shadow_catcher; + /* padding */ int pad1, pad2; } KernelIntegrator; static_assert_align(KernelIntegrator, 16); @@ -1401,14 +1212,19 @@ typedef struct KernelTables { static_assert_align(KernelTables, 16); typedef struct KernelBake { + int use; int object_index; int tri_offset; - int type; - int pass_filter; + int pad1; } KernelBake; static_assert_align(KernelBake, 16); typedef struct KernelData { + uint kernel_features; + uint max_closures; + uint max_shaders; + uint pad; + KernelCamera cam; KernelFilm film; KernelBackground background; @@ -1485,11 +1301,10 @@ typedef struct KernelLight { int type; float co[3]; int shader_id; - int samples; float max_bounces; float random; float strength[3]; - float pad1; + float pad1, pad2; Transform tfm; Transform itfm; union { @@ -1539,110 +1354,6 @@ typedef struct KernelShader { } KernelShader; static_assert_align(KernelShader, 16); -/* Declarations required for split kernel */ - -/* Macro for queues */ -/* Value marking queue's empty slot */ -#define QUEUE_EMPTY_SLOT -1 - -/* - * Queue 1 - Active rays - * Queue 2 - Background queue - * Queue 3 - Shadow ray cast kernel - AO - * Queue 4 - Shadow ray cast kernel - direct lighting - */ - -/* Queue names */ -enum QueueNumber { - /* All active rays and regenerated rays are enqueued here. */ - QUEUE_ACTIVE_AND_REGENERATED_RAYS = 0, - - /* All - * 1. Background-hit rays, - * 2. Rays that has exited path-iteration but needs to update output buffer - * 3. Rays to be regenerated - * are enqueued here. - */ - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - - /* All rays for which a shadow ray should be cast to determine radiance - * contribution for AO are enqueued here. - */ - QUEUE_SHADOW_RAY_CAST_AO_RAYS, - - /* All rays for which a shadow ray should be cast to determine radiance - * contributing for direct lighting are enqueued here. - */ - QUEUE_SHADOW_RAY_CAST_DL_RAYS, - - /* Rays sorted according to shader->id */ - QUEUE_SHADER_SORTED_RAYS, - -#ifdef __BRANCHED_PATH__ - /* All rays moving to next iteration of the indirect loop for light */ - QUEUE_LIGHT_INDIRECT_ITER, - /* Queue of all inactive rays. These are candidates for sharing work of indirect loops */ - QUEUE_INACTIVE_RAYS, -# ifdef __VOLUME__ - /* All rays moving to next iteration of the indirect loop for volumes */ - QUEUE_VOLUME_INDIRECT_ITER, -# endif -# ifdef __SUBSURFACE__ - /* All rays moving to next iteration of the indirect loop for subsurface */ - QUEUE_SUBSURFACE_INDIRECT_ITER, -# endif -#endif /* __BRANCHED_PATH__ */ - - NUM_QUEUES -}; - -/* We use RAY_STATE_MASK to get ray_state */ -#define RAY_STATE_MASK 0x0F -#define RAY_FLAG_MASK 0xF0 -enum RayState { - RAY_INVALID = 0, - /* Denotes ray is actively involved in path-iteration. */ - RAY_ACTIVE, - /* Denotes ray has completed processing all samples and is inactive. */ - RAY_INACTIVE, - /* Denotes ray has exited path-iteration and needs to update output buffer. */ - RAY_UPDATE_BUFFER, - /* Denotes ray needs to skip most surface shader work. */ - RAY_HAS_ONLY_VOLUME, - /* Denotes ray has hit background */ - RAY_HIT_BACKGROUND, - /* Denotes ray has to be regenerated */ - RAY_TO_REGENERATE, - /* Denotes ray has been regenerated */ - RAY_REGENERATED, - /* Denotes ray is moving to next iteration of the branched indirect loop */ - RAY_LIGHT_INDIRECT_NEXT_ITER, - RAY_VOLUME_INDIRECT_NEXT_ITER, - RAY_SUBSURFACE_INDIRECT_NEXT_ITER, - - /* Ray flags */ - - /* Flags to denote that the ray is currently evaluating the branched indirect loop */ - RAY_BRANCHED_LIGHT_INDIRECT = (1 << 4), - RAY_BRANCHED_VOLUME_INDIRECT = (1 << 5), - RAY_BRANCHED_SUBSURFACE_INDIRECT = (1 << 6), - RAY_BRANCHED_INDIRECT = (RAY_BRANCHED_LIGHT_INDIRECT | RAY_BRANCHED_VOLUME_INDIRECT | - RAY_BRANCHED_SUBSURFACE_INDIRECT), - - /* Ray is evaluating an iteration of an indirect loop for another thread */ - RAY_BRANCHED_INDIRECT_SHARED = (1 << 7), -}; - -#define ASSIGN_RAY_STATE(ray_state, ray_index, state) \ - (ray_state[ray_index] = ((ray_state[ray_index] & RAY_FLAG_MASK) | state)) -#define IS_STATE(ray_state, ray_index, state) \ - ((ray_index) != QUEUE_EMPTY_SLOT && ((ray_state)[(ray_index)] & RAY_STATE_MASK) == (state)) -#define ADD_RAY_FLAG(ray_state, ray_index, flag) \ - (ray_state[ray_index] = (ray_state[ray_index] | flag)) -#define REMOVE_RAY_FLAG(ray_state, ray_index, flag) \ - (ray_state[ray_index] = (ray_state[ray_index] & (~flag))) -#define IS_FLAG(ray_state, ray_index, flag) (ray_state[ray_index] & flag) - /* Patches */ #define PATCH_MAX_CONTROL_VERTS 16 @@ -1655,7 +1366,7 @@ enum RayState { /* Work Tiles */ -typedef struct WorkTile { +typedef struct KernelWorkTile { uint x, y, w, h; uint start_sample; @@ -1664,13 +1375,172 @@ typedef struct WorkTile { int offset; uint stride; - ccl_global float *buffer; -} WorkTile; + /* Precalculated parameters used by init_from_camera kernel on GPU. */ + int path_index_offset; + int work_size; +} KernelWorkTile; + +/* Shader Evaluation. + * + * Position on a primitive on an object at which we want to evaluate the + * shader for e.g. mesh displacement or light importance map. */ + +typedef struct KernelShaderEvalInput { + int object; + int prim; + float u, v; +} KernelShaderEvalInput; +static_assert_align(KernelShaderEvalInput, 16); /* Pre-computed sample table sizes for PMJ02 sampler. */ -#define NUM_PMJ_SAMPLES (64 * 64) -#define NUM_PMJ_PATTERNS 48 +#define NUM_PMJ_DIVISIONS 32 +#define NUM_PMJ_SAMPLES ((NUM_PMJ_DIVISIONS) * (NUM_PMJ_DIVISIONS)) +#define NUM_PMJ_PATTERNS 1 + +/* Device kernels. + * + * Identifier for kernels that can be executed in device queues. + * + * Some implementation details. + * + * If the kernel uses shared CUDA memory, `CUDADeviceQueue::enqueue` is to be modified. + * The path iteration kernels are handled in `PathTraceWorkGPU::enqueue_path_iteration`. */ + +typedef enum DeviceKernel { + DEVICE_KERNEL_INTEGRATOR_INIT_FROM_CAMERA = 0, + DEVICE_KERNEL_INTEGRATOR_INIT_FROM_BAKE, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, + DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND, + DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, + DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, + DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW, + DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL, + + DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES, + DEVICE_KERNEL_INTEGRATOR_RESET, + DEVICE_KERNEL_INTEGRATOR_SHADOW_CATCHER_COUNT_POSSIBLE_SPLITS, + + DEVICE_KERNEL_SHADER_EVAL_DISPLACE, + DEVICE_KERNEL_SHADER_EVAL_BACKGROUND, + +#define DECLARE_FILM_CONVERT_KERNEL(variant) \ + DEVICE_KERNEL_FILM_CONVERT_##variant, DEVICE_KERNEL_FILM_CONVERT_##variant##_HALF_RGBA + + DECLARE_FILM_CONVERT_KERNEL(DEPTH), + DECLARE_FILM_CONVERT_KERNEL(MIST), + DECLARE_FILM_CONVERT_KERNEL(SAMPLE_COUNT), + DECLARE_FILM_CONVERT_KERNEL(FLOAT), + DECLARE_FILM_CONVERT_KERNEL(LIGHT_PATH), + DECLARE_FILM_CONVERT_KERNEL(FLOAT3), + DECLARE_FILM_CONVERT_KERNEL(MOTION), + DECLARE_FILM_CONVERT_KERNEL(CRYPTOMATTE), + DECLARE_FILM_CONVERT_KERNEL(SHADOW_CATCHER), + DECLARE_FILM_CONVERT_KERNEL(SHADOW_CATCHER_MATTE_WITH_SHADOW), + DECLARE_FILM_CONVERT_KERNEL(COMBINED), + DECLARE_FILM_CONVERT_KERNEL(FLOAT4), + +#undef DECLARE_FILM_CONVERT_KERNEL + + DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_CHECK, + DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_X, + DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_Y, + + DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS, + DEVICE_KERNEL_FILTER_GUIDING_SET_FAKE_ALBEDO, + DEVICE_KERNEL_FILTER_COLOR_PREPROCESS, + DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS, + + DEVICE_KERNEL_CRYPTOMATTE_POSTPROCESS, + + DEVICE_KERNEL_PREFIX_SUM, + + DEVICE_KERNEL_NUM, +} DeviceKernel; + +enum { + DEVICE_KERNEL_INTEGRATOR_NUM = DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL + 1, +}; + +/* Kernel Features */ + +enum KernelFeatureFlag : unsigned int { + /* Shader nodes. */ + KERNEL_FEATURE_NODE_BSDF = (1U << 0U), + KERNEL_FEATURE_NODE_EMISSION = (1U << 1U), + KERNEL_FEATURE_NODE_VOLUME = (1U << 2U), + KERNEL_FEATURE_NODE_HAIR = (1U << 3U), + KERNEL_FEATURE_NODE_BUMP = (1U << 4U), + KERNEL_FEATURE_NODE_BUMP_STATE = (1U << 5U), + KERNEL_FEATURE_NODE_VORONOI_EXTRA = (1U << 6U), + KERNEL_FEATURE_NODE_RAYTRACE = (1U << 7U), + + /* Use denoising kernels and output denoising passes. */ + KERNEL_FEATURE_DENOISING = (1U << 8U), + + /* Use path tracing kernels. */ + KERNEL_FEATURE_PATH_TRACING = (1U << 9U), + + /* BVH/sampling kernel features. */ + KERNEL_FEATURE_HAIR = (1U << 10U), + KERNEL_FEATURE_HAIR_THICK = (1U << 11U), + KERNEL_FEATURE_OBJECT_MOTION = (1U << 12U), + KERNEL_FEATURE_CAMERA_MOTION = (1U << 13U), + + /* Denotes whether baking functionality is needed. */ + KERNEL_FEATURE_BAKING = (1U << 14U), + + /* Use subsurface scattering materials. */ + KERNEL_FEATURE_SUBSURFACE = (1U << 15U), + + /* Use volume materials. */ + KERNEL_FEATURE_VOLUME = (1U << 16U), + + /* Use OpenSubdiv patch evaluation */ + KERNEL_FEATURE_PATCH_EVALUATION = (1U << 17U), + + /* Use Transparent shadows */ + KERNEL_FEATURE_TRANSPARENT = (1U << 18U), + + /* Use shadow catcher. */ + KERNEL_FEATURE_SHADOW_CATCHER = (1U << 19U), + + /* Per-uber shader usage flags. */ + KERNEL_FEATURE_PRINCIPLED = (1U << 20U), + + /* Light render passes. */ + KERNEL_FEATURE_LIGHT_PASSES = (1U << 21U), + + /* Shadow render pass. */ + KERNEL_FEATURE_SHADOW_PASS = (1U << 22U), +}; + +/* Shader node feature mask, to specialize shader evaluation for kernels. */ + +#define KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT \ + (KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VORONOI_EXTRA) +#define KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW \ + (KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | \ + KERNEL_FEATURE_NODE_HAIR | KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE | \ + KERNEL_FEATURE_NODE_VORONOI_EXTRA) +#define KERNEL_FEATURE_NODE_MASK_SURFACE \ + (KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW | KERNEL_FEATURE_NODE_RAYTRACE) +#define KERNEL_FEATURE_NODE_MASK_VOLUME \ + (KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | KERNEL_FEATURE_NODE_VORONOI_EXTRA) +#define KERNEL_FEATURE_NODE_MASK_DISPLACEMENT \ + (KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE) +#define KERNEL_FEATURE_NODE_MASK_BUMP KERNEL_FEATURE_NODE_MASK_DISPLACEMENT + +#define KERNEL_NODES_FEATURE(feature) ((node_feature_mask & (KERNEL_FEATURE_NODE_##feature)) != 0U) CCL_NAMESPACE_END - -#endif /* __KERNEL_TYPES_H__ */ diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h deleted file mode 100644 index f6b34be040e..00000000000 --- a/intern/cycles/kernel/kernel_volume.h +++ /dev/null @@ -1,1440 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* Ignore paths that have volume throughput below this value, to avoid unnecessary work - * and precision issues. - * todo: this value could be tweaked or turned into a probability to avoid unnecessary - * work in volumes and subsurface scattering. */ -#define VOLUME_THROUGHPUT_EPSILON 1e-6f - -/* Events for probalistic scattering */ - -typedef enum VolumeIntegrateResult { - VOLUME_PATH_SCATTERED = 0, - VOLUME_PATH_ATTENUATED = 1, - VOLUME_PATH_MISSED = 2 -} VolumeIntegrateResult; - -/* Volume shader properties - * - * extinction coefficient = absorption coefficient + scattering coefficient - * sigma_t = sigma_a + sigma_s */ - -typedef struct VolumeShaderCoefficients { - float3 sigma_t; - float3 sigma_s; - float3 emission; -} VolumeShaderCoefficients; - -#ifdef __VOLUME__ - -/* evaluate shader to get extinction coefficient at P */ -ccl_device_inline bool volume_shader_extinction_sample(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - float3 P, - float3 *extinction) -{ - sd->P = P; - shader_eval_volume(kg, sd, state, state->volume_stack, PATH_RAY_SHADOW); - - if (sd->flag & SD_EXTINCTION) { - const float density = object_volume_density(kg, sd->object); - *extinction = sd->closure_transparent_extinction * density; - return true; - } - else { - return false; - } -} - -/* evaluate shader to get absorption, scattering and emission at P */ -ccl_device_inline bool volume_shader_sample(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - float3 P, - VolumeShaderCoefficients *coeff) -{ - sd->P = P; - shader_eval_volume(kg, sd, state, state->volume_stack, state->flag); - - if (!(sd->flag & (SD_EXTINCTION | SD_SCATTER | SD_EMISSION))) - return false; - - coeff->sigma_s = zero_float3(); - coeff->sigma_t = (sd->flag & SD_EXTINCTION) ? sd->closure_transparent_extinction : zero_float3(); - coeff->emission = (sd->flag & SD_EMISSION) ? sd->closure_emission_background : zero_float3(); - - if (sd->flag & SD_SCATTER) { - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_VOLUME(sc->type)) - coeff->sigma_s += sc->weight; - } - } - - const float density = object_volume_density(kg, sd->object); - coeff->sigma_s *= density; - coeff->sigma_t *= density; - coeff->emission *= density; - - return true; -} - -#endif /* __VOLUME__ */ - -ccl_device float3 volume_color_transmittance(float3 sigma, float t) -{ - return exp3(-sigma * t); -} - -ccl_device float kernel_volume_channel_get(float3 value, int channel) -{ - return (channel == 0) ? value.x : ((channel == 1) ? value.y : value.z); -} - -#ifdef __VOLUME__ - -ccl_device float volume_stack_step_size(KernelGlobals *kg, ccl_addr_space VolumeStack *stack) -{ - float step_size = FLT_MAX; - - for (int i = 0; stack[i].shader != SHADER_NONE; i++) { - int shader_flag = kernel_tex_fetch(__shaders, (stack[i].shader & SHADER_MASK)).flags; - - bool heterogeneous = false; - - if (shader_flag & SD_HETEROGENEOUS_VOLUME) { - heterogeneous = true; - } - else if (shader_flag & SD_NEED_VOLUME_ATTRIBUTES) { - /* We want to render world or objects without any volume grids - * as homogeneous, but can only verify this at run-time since other - * heterogeneous volume objects may be using the same shader. */ - int object = stack[i].object; - if (object != OBJECT_NONE) { - int object_flag = kernel_tex_fetch(__object_flag, object); - if (object_flag & SD_OBJECT_HAS_VOLUME_ATTRIBUTES) { - heterogeneous = true; - } - } - } - - if (heterogeneous) { - float object_step_size = object_volume_step_size(kg, stack[i].object); - object_step_size *= kernel_data.integrator.volume_step_rate; - step_size = fminf(object_step_size, step_size); - } - } - - return step_size; -} - -ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stack) -{ - if (kernel_data.integrator.num_all_lights == 0) - return 0; - - int method = -1; - - for (int i = 0; stack[i].shader != SHADER_NONE; i++) { - int shader_flag = kernel_tex_fetch(__shaders, (stack[i].shader & SHADER_MASK)).flags; - - if (shader_flag & SD_VOLUME_MIS) { - return SD_VOLUME_MIS; - } - else if (shader_flag & SD_VOLUME_EQUIANGULAR) { - if (method == 0) - return SD_VOLUME_MIS; - - method = SD_VOLUME_EQUIANGULAR; - } - else { - if (method == SD_VOLUME_EQUIANGULAR) - return SD_VOLUME_MIS; - - method = 0; - } - } - - return method; -} - -ccl_device_inline void kernel_volume_step_init(KernelGlobals *kg, - ccl_addr_space PathState *state, - const float object_step_size, - float t, - float *step_size, - float *step_shade_offset, - float *steps_offset) -{ - const int max_steps = kernel_data.integrator.volume_max_steps; - float step = min(object_step_size, t); - - /* compute exact steps in advance for malloc */ - if (t > max_steps * step) { - step = t / (float)max_steps; - } - - *step_size = step; - - /* Perform shading at this offset within a step, to integrate over - * over the entire step segment. */ - *step_shade_offset = path_state_rng_1D_hash(kg, state, 0x1e31d8a4); - - /* Shift starting point of all segment by this random amount to avoid - * banding artifacts from the volume bounding shape. */ - *steps_offset = path_state_rng_1D_hash(kg, state, 0x3d22c7b3); -} - -/* Volume Shadows - * - * These functions are used to attenuate shadow rays to lights. Both absorption - * and scattering will block light, represented by the extinction coefficient. */ - -/* homogeneous volume: assume shader evaluation at the starts gives - * the extinction coefficient for the entire line segment */ -ccl_device void kernel_volume_shadow_homogeneous(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - ShaderData *sd, - float3 *throughput) -{ - float3 sigma_t = zero_float3(); - - if (volume_shader_extinction_sample(kg, sd, state, ray->P, &sigma_t)) - *throughput *= volume_color_transmittance(sigma_t, ray->t); -} - -/* heterogeneous volume: integrate stepping through the volume until we - * reach the end, get absorbed entirely, or run out of iterations */ -ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - ShaderData *sd, - float3 *throughput, - const float object_step_size) -{ - float3 tp = *throughput; - - /* Prepare for stepping. - * For shadows we do not offset all segments, since the starting point is - * already a random distance inside the volume. It also appears to create - * banding artifacts for unknown reasons. */ - int max_steps = kernel_data.integrator.volume_max_steps; - float step_size, step_shade_offset, unused; - kernel_volume_step_init( - kg, state, object_step_size, ray->t, &step_size, &step_shade_offset, &unused); - const float steps_offset = 1.0f; - - /* compute extinction at the start */ - float t = 0.0f; - - float3 sum = zero_float3(); - - for (int i = 0; i < max_steps; i++) { - /* advance to new position */ - float new_t = min(ray->t, (i + steps_offset) * step_size); - float dt = new_t - t; - - float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); - float3 sigma_t = zero_float3(); - - /* compute attenuation over segment */ - if (volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) { - /* Compute expf() only for every Nth step, to save some calculations - * because exp(a)*exp(b) = exp(a+b), also do a quick VOLUME_THROUGHPUT_EPSILON - * check then. */ - sum += (-sigma_t * dt); - if ((i & 0x07) == 0) { /* ToDo: Other interval? */ - tp = *throughput * exp3(sum); - - /* stop if nearly all light is blocked */ - if (tp.x < VOLUME_THROUGHPUT_EPSILON && tp.y < VOLUME_THROUGHPUT_EPSILON && - tp.z < VOLUME_THROUGHPUT_EPSILON) - break; - } - } - - /* stop if at the end of the volume */ - t = new_t; - if (t == ray->t) { - /* Update throughput in case we haven't done it above */ - tp = *throughput * exp3(sum); - break; - } - } - - *throughput = tp; -} - -/* get the volume attenuation over line segment defined by ray, with the - * assumption that there are no surfaces blocking light between the endpoints */ -# if defined(__KERNEL_OPTIX__) && defined(__SHADER_RAYTRACE__) -ccl_device_inline void kernel_volume_shadow(KernelGlobals *kg, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - Ray *ray, - float3 *throughput) -{ - optixDirectCall(1, kg, shadow_sd, state, ray, throughput); -} -extern "C" __device__ void __direct_callable__kernel_volume_shadow( -# else -ccl_device_noinline void kernel_volume_shadow( -# endif - KernelGlobals *kg, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - Ray *ray, - float3 *throughput) -{ - shader_setup_from_volume(kg, shadow_sd, ray); - - float step_size = volume_stack_step_size(kg, state->volume_stack); - if (step_size != FLT_MAX) - kernel_volume_shadow_heterogeneous(kg, state, ray, shadow_sd, throughput, step_size); - else - kernel_volume_shadow_homogeneous(kg, state, ray, shadow_sd, throughput); -} - -#endif /* __VOLUME__ */ - -/* Equi-angular sampling as in: - * "Importance Sampling Techniques for Path Tracing in Participating Media" */ - -ccl_device float kernel_volume_equiangular_sample(Ray *ray, float3 light_P, float xi, float *pdf) -{ - float t = ray->t; - - float delta = dot((light_P - ray->P), ray->D); - float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); - if (UNLIKELY(D == 0.0f)) { - *pdf = 0.0f; - return 0.0f; - } - float theta_a = -atan2f(delta, D); - float theta_b = atan2f(t - delta, D); - float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a); - if (UNLIKELY(theta_b == theta_a)) { - *pdf = 0.0f; - return 0.0f; - } - *pdf = D / ((theta_b - theta_a) * (D * D + t_ * t_)); - - return min(t, delta + t_); /* min is only for float precision errors */ -} - -ccl_device float kernel_volume_equiangular_pdf(Ray *ray, float3 light_P, float sample_t) -{ - float delta = dot((light_P - ray->P), ray->D); - float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); - if (UNLIKELY(D == 0.0f)) { - return 0.0f; - } - - float t = ray->t; - float t_ = sample_t - delta; - - float theta_a = -atan2f(delta, D); - float theta_b = atan2f(t - delta, D); - if (UNLIKELY(theta_b == theta_a)) { - return 0.0f; - } - - float pdf = D / ((theta_b - theta_a) * (D * D + t_ * t_)); - - return pdf; -} - -/* Distance sampling */ - -ccl_device float kernel_volume_distance_sample( - float max_t, float3 sigma_t, int channel, float xi, float3 *transmittance, float3 *pdf) -{ - /* xi is [0, 1[ so log(0) should never happen, division by zero is - * avoided because sample_sigma_t > 0 when SD_SCATTER is set */ - float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel); - float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); - float sample_transmittance = kernel_volume_channel_get(full_transmittance, channel); - - float sample_t = min(max_t, -logf(1.0f - xi * (1.0f - sample_transmittance)) / sample_sigma_t); - - *transmittance = volume_color_transmittance(sigma_t, sample_t); - *pdf = safe_divide_color(sigma_t * *transmittance, one_float3() - full_transmittance); - - /* todo: optimization: when taken together with hit/miss decision, - * the full_transmittance cancels out drops out and xi does not - * need to be remapped */ - - return sample_t; -} - -ccl_device float3 kernel_volume_distance_pdf(float max_t, float3 sigma_t, float sample_t) -{ - float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); - float3 transmittance = volume_color_transmittance(sigma_t, sample_t); - - return safe_divide_color(sigma_t * transmittance, one_float3() - full_transmittance); -} - -/* Emission */ - -ccl_device float3 kernel_volume_emission_integrate(VolumeShaderCoefficients *coeff, - int closure_flag, - float3 transmittance, - float t) -{ - /* integral E * exp(-sigma_t * t) from 0 to t = E * (1 - exp(-sigma_t * t))/sigma_t - * this goes to E * t as sigma_t goes to zero - * - * todo: we should use an epsilon to avoid precision issues near zero sigma_t */ - float3 emission = coeff->emission; - - if (closure_flag & SD_EXTINCTION) { - float3 sigma_t = coeff->sigma_t; - - emission.x *= (sigma_t.x > 0.0f) ? (1.0f - transmittance.x) / sigma_t.x : t; - emission.y *= (sigma_t.y > 0.0f) ? (1.0f - transmittance.y) / sigma_t.y : t; - emission.z *= (sigma_t.z > 0.0f) ? (1.0f - transmittance.z) / sigma_t.z : t; - } - else - emission *= t; - - return emission; -} - -/* Volume Path */ - -ccl_device int kernel_volume_sample_channel(float3 albedo, - float3 throughput, - float rand, - float3 *pdf) -{ - /* Sample color channel proportional to throughput and single scattering - * albedo, to significantly reduce noise with many bounce, following: - * - * "Practical and Controllable Subsurface Scattering for Production Path - * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ - float3 weights = fabs(throughput * albedo); - float sum_weights = weights.x + weights.y + weights.z; - float3 weights_pdf; - - if (sum_weights > 0.0f) { - weights_pdf = weights / sum_weights; - } - else { - weights_pdf = make_float3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f); - } - - *pdf = weights_pdf; - - /* OpenCL does not support -> on float3, so don't use pdf->x. */ - if (rand < weights_pdf.x) { - return 0; - } - else if (rand < weights_pdf.x + weights_pdf.y) { - return 1; - } - else { - return 2; - } -} - -#ifdef __VOLUME__ - -/* homogeneous volume: assume shader evaluation at the start gives - * the volume shading coefficient for the entire line segment */ -ccl_device VolumeIntegrateResult -kernel_volume_integrate_homogeneous(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - ShaderData *sd, - PathRadiance *L, - ccl_addr_space float3 *throughput, - bool probalistic_scatter) -{ - VolumeShaderCoefficients coeff ccl_optional_struct_init; - - if (!volume_shader_sample(kg, sd, state, ray->P, &coeff)) - return VOLUME_PATH_MISSED; - - int closure_flag = sd->flag; - float t = ray->t; - float3 new_tp; - -# ifdef __VOLUME_SCATTER__ - /* randomly scatter, and if we do t is shortened */ - if (closure_flag & SD_SCATTER) { - /* Sample channel, use MIS with balance heuristic. */ - float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL); - float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); - float3 channel_pdf; - int channel = kernel_volume_sample_channel(albedo, *throughput, rphase, &channel_pdf); - - /* decide if we will hit or miss */ - bool scatter = true; - float xi = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE); - - if (probalistic_scatter) { - float sample_sigma_t = kernel_volume_channel_get(coeff.sigma_t, channel); - float sample_transmittance = expf(-sample_sigma_t * t); - - if (1.0f - xi >= sample_transmittance) { - scatter = true; - - /* rescale random number so we can reuse it */ - xi = 1.0f - (1.0f - xi - sample_transmittance) / (1.0f - sample_transmittance); - } - else - scatter = false; - } - - if (scatter) { - /* scattering */ - float3 pdf; - float3 transmittance; - float sample_t; - - /* distance sampling */ - sample_t = kernel_volume_distance_sample( - ray->t, coeff.sigma_t, channel, xi, &transmittance, &pdf); - - /* modify pdf for hit/miss decision */ - if (probalistic_scatter) - pdf *= one_float3() - volume_color_transmittance(coeff.sigma_t, t); - - new_tp = *throughput * coeff.sigma_s * transmittance / dot(channel_pdf, pdf); - t = sample_t; - } - else { - /* no scattering */ - float3 transmittance = volume_color_transmittance(coeff.sigma_t, t); - float pdf = dot(channel_pdf, transmittance); - new_tp = *throughput * transmittance / pdf; - } - } - else -# endif - if (closure_flag & SD_EXTINCTION) { - /* absorption only, no sampling needed */ - float3 transmittance = volume_color_transmittance(coeff.sigma_t, t); - new_tp = *throughput * transmittance; - } - else { - new_tp = *throughput; - } - - /* integrate emission attenuated by extinction */ - if (L && (closure_flag & SD_EMISSION)) { - float3 transmittance = volume_color_transmittance(coeff.sigma_t, ray->t); - float3 emission = kernel_volume_emission_integrate( - &coeff, closure_flag, transmittance, ray->t); - path_radiance_accum_emission(kg, L, state, *throughput, emission); - } - - /* modify throughput */ - if (closure_flag & SD_EXTINCTION) { - *throughput = new_tp; - - /* prepare to scatter to new direction */ - if (t < ray->t) { - /* adjust throughput and move to new location */ - sd->P = ray->P + t * ray->D; - - return VOLUME_PATH_SCATTERED; - } - } - - return VOLUME_PATH_ATTENUATED; -} - -/* heterogeneous volume distance sampling: integrate stepping through the - * volume until we reach the end, get absorbed entirely, or run out of - * iterations. this does probabilistically scatter or get transmitted through - * for path tracing where we don't want to branch. */ -ccl_device VolumeIntegrateResult -kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - ShaderData *sd, - PathRadiance *L, - ccl_addr_space float3 *throughput, - const float object_step_size) -{ - float3 tp = *throughput; - - /* Prepare for stepping. - * Using a different step offset for the first step avoids banding artifacts. */ - int max_steps = kernel_data.integrator.volume_max_steps; - float step_size, step_shade_offset, steps_offset; - kernel_volume_step_init( - kg, state, object_step_size, ray->t, &step_size, &step_shade_offset, &steps_offset); - - /* compute coefficients at the start */ - float t = 0.0f; - float3 accum_transmittance = one_float3(); - - /* pick random color channel, we use the Veach one-sample - * model with balance heuristic for the channels */ - float xi = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE); - float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL); - bool has_scatter = false; - - for (int i = 0; i < max_steps; i++) { - /* advance to new position */ - float new_t = min(ray->t, (i + steps_offset) * step_size); - float dt = new_t - t; - - float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); - VolumeShaderCoefficients coeff ccl_optional_struct_init; - - /* compute segment */ - if (volume_shader_sample(kg, sd, state, new_P, &coeff)) { - int closure_flag = sd->flag; - float3 new_tp; - float3 transmittance; - bool scatter = false; - - /* distance sampling */ -# ifdef __VOLUME_SCATTER__ - if ((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_EXTINCTION))) { - has_scatter = true; - - /* Sample channel, use MIS with balance heuristic. */ - float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); - float3 channel_pdf; - int channel = kernel_volume_sample_channel(albedo, tp, rphase, &channel_pdf); - - /* compute transmittance over full step */ - transmittance = volume_color_transmittance(coeff.sigma_t, dt); - - /* decide if we will scatter or continue */ - float sample_transmittance = kernel_volume_channel_get(transmittance, channel); - - if (1.0f - xi >= sample_transmittance) { - /* compute sampling distance */ - float sample_sigma_t = kernel_volume_channel_get(coeff.sigma_t, channel); - float new_dt = -logf(1.0f - xi) / sample_sigma_t; - new_t = t + new_dt; - - /* transmittance and pdf */ - float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); - float3 pdf = coeff.sigma_t * new_transmittance; - - /* throughput */ - new_tp = tp * coeff.sigma_s * new_transmittance / dot(channel_pdf, pdf); - scatter = true; - } - else { - /* throughput */ - float pdf = dot(channel_pdf, transmittance); - new_tp = tp * transmittance / pdf; - - /* remap xi so we can reuse it and keep thing stratified */ - xi = 1.0f - (1.0f - xi) / sample_transmittance; - } - } - else -# endif - if (closure_flag & SD_EXTINCTION) { - /* absorption only, no sampling needed */ - transmittance = volume_color_transmittance(coeff.sigma_t, dt); - new_tp = tp * transmittance; - } - else { - transmittance = zero_float3(); - new_tp = tp; - } - - /* integrate emission attenuated by absorption */ - if (L && (closure_flag & SD_EMISSION)) { - float3 emission = kernel_volume_emission_integrate( - &coeff, closure_flag, transmittance, dt); - path_radiance_accum_emission(kg, L, state, tp, emission); - } - - /* modify throughput */ - if (closure_flag & SD_EXTINCTION) { - tp = new_tp; - - /* stop if nearly all light blocked */ - if (tp.x < VOLUME_THROUGHPUT_EPSILON && tp.y < VOLUME_THROUGHPUT_EPSILON && - tp.z < VOLUME_THROUGHPUT_EPSILON) { - tp = zero_float3(); - break; - } - } - - /* prepare to scatter to new direction */ - if (scatter) { - /* adjust throughput and move to new location */ - sd->P = ray->P + new_t * ray->D; - *throughput = tp; - - return VOLUME_PATH_SCATTERED; - } - else { - /* accumulate transmittance */ - accum_transmittance *= transmittance; - } - } - - /* stop if at the end of the volume */ - t = new_t; - if (t == ray->t) - break; - } - - *throughput = tp; - - return VOLUME_PATH_ATTENUATED; -} - -/* get the volume attenuation and emission over line segment defined by - * ray, with the assumption that there are no surfaces blocking light - * between the endpoints. distance sampling is used to decide if we will - * scatter or not. */ -ccl_device_noinline_cpu VolumeIntegrateResult -kernel_volume_integrate(KernelGlobals *kg, - ccl_addr_space PathState *state, - ShaderData *sd, - Ray *ray, - PathRadiance *L, - ccl_addr_space float3 *throughput, - float step_size) -{ - shader_setup_from_volume(kg, sd, ray); - - if (step_size != FLT_MAX) - return kernel_volume_integrate_heterogeneous_distance( - kg, state, ray, sd, L, throughput, step_size); - else - return kernel_volume_integrate_homogeneous(kg, state, ray, sd, L, throughput, true); -} - -# ifndef __SPLIT_KERNEL__ -/* Decoupled Volume Sampling - * - * VolumeSegment is list of coefficients and transmittance stored at all steps - * through a volume. This can then later be used for decoupled sampling as in: - * "Importance Sampling Techniques for Path Tracing in Participating Media" - * - * On the GPU this is only supported (but currently not enabled) - * for homogeneous volumes (1 step), due to - * no support for malloc/free and too much stack usage with a fix size array. */ - -typedef struct VolumeStep { - float3 sigma_s; /* scatter coefficient */ - float3 sigma_t; /* extinction coefficient */ - float3 accum_transmittance; /* accumulated transmittance including this step */ - float3 cdf_distance; /* cumulative density function for distance sampling */ - float t; /* distance at end of this step */ - float shade_t; /* jittered distance where shading was done in step */ - int closure_flag; /* shader evaluation closure flags */ -} VolumeStep; - -typedef struct VolumeSegment { - VolumeStep stack_step; /* stack storage for homogeneous step, to avoid malloc */ - VolumeStep *steps; /* recorded steps */ - int numsteps; /* number of steps */ - int closure_flag; /* accumulated closure flags from all steps */ - - float3 accum_emission; /* accumulated emission at end of segment */ - float3 accum_transmittance; /* accumulated transmittance at end of segment */ - float3 accum_albedo; /* accumulated average albedo over segment */ - - int sampling_method; /* volume sampling method */ -} VolumeSegment; - -/* record volume steps to the end of the volume. - * - * it would be nice if we could only record up to the point that we need to scatter, - * but the entire segment is needed to do always scattering, rather than probabilistically - * hitting or missing the volume. if we don't know the transmittance at the end of the - * volume we can't generate stratified distance samples up to that transmittance */ -# ifdef __VOLUME_DECOUPLED__ -ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, - PathState *state, - Ray *ray, - ShaderData *sd, - VolumeSegment *segment, - const float object_step_size) -{ - /* prepare for volume stepping */ - int max_steps; - float step_size, step_shade_offset, steps_offset; - - if (object_step_size != FLT_MAX) { - max_steps = kernel_data.integrator.volume_max_steps; - kernel_volume_step_init( - kg, state, object_step_size, ray->t, &step_size, &step_shade_offset, &steps_offset); - -# ifdef __KERNEL_CPU__ - /* NOTE: For the branched path tracing it's possible to have direct - * and indirect light integration both having volume segments allocated. - * We detect this using index in the pre-allocated memory. Currently we - * only support two segments allocated at a time, if more needed some - * modifications to the KernelGlobals will be needed. - * - * This gives us restrictions that decoupled record should only happen - * in the stack manner, meaning if there's subsequent call of decoupled - * record it'll need to free memory before its caller frees memory. - */ - const int index = kg->decoupled_volume_steps_index; - assert(index < sizeof(kg->decoupled_volume_steps) / sizeof(*kg->decoupled_volume_steps)); - if (kg->decoupled_volume_steps[index] == NULL) { - kg->decoupled_volume_steps[index] = (VolumeStep *)malloc(sizeof(VolumeStep) * max_steps); - } - segment->steps = kg->decoupled_volume_steps[index]; - ++kg->decoupled_volume_steps_index; -# else - segment->steps = (VolumeStep *)malloc(sizeof(VolumeStep) * max_steps); -# endif - } - else { - max_steps = 1; - step_size = ray->t; - step_shade_offset = 0.0f; - steps_offset = 1.0f; - segment->steps = &segment->stack_step; - } - - /* init accumulation variables */ - float3 accum_emission = zero_float3(); - float3 accum_transmittance = one_float3(); - float3 accum_albedo = zero_float3(); - float3 cdf_distance = zero_float3(); - float t = 0.0f; - - segment->numsteps = 0; - segment->closure_flag = 0; - bool is_last_step_empty = false; - - VolumeStep *step = segment->steps; - - for (int i = 0; i < max_steps; i++, step++) { - /* advance to new position */ - float new_t = min(ray->t, (i + steps_offset) * step_size); - float dt = new_t - t; - - float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); - VolumeShaderCoefficients coeff ccl_optional_struct_init; - - /* compute segment */ - if (volume_shader_sample(kg, sd, state, new_P, &coeff)) { - int closure_flag = sd->flag; - float3 sigma_t = coeff.sigma_t; - - /* compute average albedo for channel sampling */ - if (closure_flag & SD_SCATTER) { - accum_albedo += (dt / ray->t) * safe_divide_color(coeff.sigma_s, sigma_t); - } - - /* compute accumulated transmittance */ - float3 transmittance = volume_color_transmittance(sigma_t, dt); - - /* compute emission attenuated by absorption */ - if (closure_flag & SD_EMISSION) { - float3 emission = kernel_volume_emission_integrate( - &coeff, closure_flag, transmittance, dt); - accum_emission += accum_transmittance * emission; - } - - accum_transmittance *= transmittance; - - /* compute pdf for distance sampling */ - float3 pdf_distance = dt * accum_transmittance * coeff.sigma_s; - cdf_distance = cdf_distance + pdf_distance; - - /* write step data */ - step->sigma_t = sigma_t; - step->sigma_s = coeff.sigma_s; - step->closure_flag = closure_flag; - - segment->closure_flag |= closure_flag; - - is_last_step_empty = false; - segment->numsteps++; - } - else { - if (is_last_step_empty) { - /* consecutive empty step, merge */ - step--; - } - else { - /* store empty step */ - step->sigma_t = zero_float3(); - step->sigma_s = zero_float3(); - step->closure_flag = 0; - - segment->numsteps++; - is_last_step_empty = true; - } - } - - step->accum_transmittance = accum_transmittance; - step->cdf_distance = cdf_distance; - step->t = new_t; - step->shade_t = t + dt * step_shade_offset; - - /* stop if at the end of the volume */ - t = new_t; - if (t == ray->t) - break; - - /* stop if nearly all light blocked */ - if (accum_transmittance.x < VOLUME_THROUGHPUT_EPSILON && - accum_transmittance.y < VOLUME_THROUGHPUT_EPSILON && - accum_transmittance.z < VOLUME_THROUGHPUT_EPSILON) - break; - } - - /* store total emission and transmittance */ - segment->accum_emission = accum_emission; - segment->accum_transmittance = accum_transmittance; - segment->accum_albedo = accum_albedo; - - /* normalize cumulative density function for distance sampling */ - VolumeStep *last_step = segment->steps + segment->numsteps - 1; - - if (!is_zero(last_step->cdf_distance)) { - VolumeStep *step = &segment->steps[0]; - int numsteps = segment->numsteps; - float3 inv_cdf_distance_sum = safe_invert_color(last_step->cdf_distance); - - for (int i = 0; i < numsteps; i++, step++) - step->cdf_distance *= inv_cdf_distance_sum; - } -} - -ccl_device void kernel_volume_decoupled_free(KernelGlobals *kg, VolumeSegment *segment) -{ - if (segment->steps != &segment->stack_step) { -# ifdef __KERNEL_CPU__ - /* NOTE: We only allow free last allocated segment. - * No random order of alloc/free is supported. - */ - assert(kg->decoupled_volume_steps_index > 0); - assert(segment->steps == kg->decoupled_volume_steps[kg->decoupled_volume_steps_index - 1]); - --kg->decoupled_volume_steps_index; -# else - free(segment->steps); -# endif - } -} -# endif /* __VOLUME_DECOUPLED__ */ - -/* scattering for homogeneous and heterogeneous volumes, using decoupled ray - * marching. - * - * function is expected to return VOLUME_PATH_SCATTERED when probalistic_scatter is false */ -ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter(KernelGlobals *kg, - PathState *state, - Ray *ray, - ShaderData *sd, - float3 *throughput, - float rphase, - float rscatter, - const VolumeSegment *segment, - const float3 *light_P, - bool probalistic_scatter) -{ - kernel_assert(segment->closure_flag & SD_SCATTER); - - /* Sample color channel, use MIS with balance heuristic. */ - float3 channel_pdf; - int channel = kernel_volume_sample_channel( - segment->accum_albedo, *throughput, rphase, &channel_pdf); - - float xi = rscatter; - - /* probabilistic scattering decision based on transmittance */ - if (probalistic_scatter) { - float sample_transmittance = kernel_volume_channel_get(segment->accum_transmittance, channel); - - if (1.0f - xi >= sample_transmittance) { - /* rescale random number so we can reuse it */ - xi = 1.0f - (1.0f - xi - sample_transmittance) / (1.0f - sample_transmittance); - } - else { - *throughput /= sample_transmittance; - return VOLUME_PATH_MISSED; - } - } - - VolumeStep *step; - float3 transmittance; - float pdf, sample_t; - float mis_weight = 1.0f; - bool distance_sample = true; - bool use_mis = false; - - if (segment->sampling_method && light_P) { - if (segment->sampling_method == SD_VOLUME_MIS) { - /* multiple importance sample: randomly pick between - * equiangular and distance sampling strategy */ - if (xi < 0.5f) { - xi *= 2.0f; - } - else { - xi = (xi - 0.5f) * 2.0f; - distance_sample = false; - } - - use_mis = true; - } - else { - /* only equiangular sampling */ - distance_sample = false; - } - } - - /* distance sampling */ - if (distance_sample) { - /* find step in cdf */ - step = segment->steps; - - float prev_t = 0.0f; - float3 step_pdf_distance = one_float3(); - - if (segment->numsteps > 1) { - float prev_cdf = 0.0f; - float step_cdf = 1.0f; - float3 prev_cdf_distance = zero_float3(); - - for (int i = 0;; i++, step++) { - /* todo: optimize using binary search */ - step_cdf = kernel_volume_channel_get(step->cdf_distance, channel); - - if (xi < step_cdf || i == segment->numsteps - 1) - break; - - prev_cdf = step_cdf; - prev_t = step->t; - prev_cdf_distance = step->cdf_distance; - } - - /* remap xi so we can reuse it */ - xi = (xi - prev_cdf) / (step_cdf - prev_cdf); - - /* pdf for picking step */ - step_pdf_distance = step->cdf_distance - prev_cdf_distance; - } - - /* determine range in which we will sample */ - float step_t = step->t - prev_t; - - /* sample distance and compute transmittance */ - float3 distance_pdf; - sample_t = prev_t + kernel_volume_distance_sample( - step_t, step->sigma_t, channel, xi, &transmittance, &distance_pdf); - - /* modify pdf for hit/miss decision */ - if (probalistic_scatter) - distance_pdf *= one_float3() - segment->accum_transmittance; - - pdf = dot(channel_pdf, distance_pdf * step_pdf_distance); - - /* multiple importance sampling */ - if (use_mis) { - float equi_pdf = kernel_volume_equiangular_pdf(ray, *light_P, sample_t); - mis_weight = 2.0f * power_heuristic(pdf, equi_pdf); - } - } - /* equi-angular sampling */ - else { - /* sample distance */ - sample_t = kernel_volume_equiangular_sample(ray, *light_P, xi, &pdf); - - /* find step in which sampled distance is located */ - step = segment->steps; - - float prev_t = 0.0f; - float3 step_pdf_distance = one_float3(); - - if (segment->numsteps > 1) { - float3 prev_cdf_distance = zero_float3(); - - int numsteps = segment->numsteps; - int high = numsteps - 1; - int low = 0; - int mid; - - while (low < high) { - mid = (low + high) >> 1; - - if (sample_t < step[mid].t) - high = mid; - else if (sample_t >= step[mid + 1].t) - low = mid + 1; - else { - /* found our interval in step[mid] .. step[mid+1] */ - prev_t = step[mid].t; - prev_cdf_distance = step[mid].cdf_distance; - step += mid + 1; - break; - } - } - - if (low >= numsteps - 1) { - prev_t = step[numsteps - 1].t; - prev_cdf_distance = step[numsteps - 1].cdf_distance; - step += numsteps - 1; - } - - /* pdf for picking step with distance sampling */ - step_pdf_distance = step->cdf_distance - prev_cdf_distance; - } - - /* determine range in which we will sample */ - float step_t = step->t - prev_t; - float step_sample_t = sample_t - prev_t; - - /* compute transmittance */ - transmittance = volume_color_transmittance(step->sigma_t, step_sample_t); - - /* multiple importance sampling */ - if (use_mis) { - float3 distance_pdf3 = kernel_volume_distance_pdf(step_t, step->sigma_t, step_sample_t); - float distance_pdf = dot(channel_pdf, distance_pdf3 * step_pdf_distance); - mis_weight = 2.0f * power_heuristic(pdf, distance_pdf); - } - } - if (sample_t < 0.0f || pdf == 0.0f) { - return VOLUME_PATH_MISSED; - } - - /* compute transmittance up to this step */ - if (step != segment->steps) - transmittance *= (step - 1)->accum_transmittance; - - /* modify throughput */ - *throughput *= step->sigma_s * transmittance * (mis_weight / pdf); - - /* evaluate shader to create closures at shading point */ - if (segment->numsteps > 1) { - sd->P = ray->P + step->shade_t * ray->D; - - VolumeShaderCoefficients coeff; - volume_shader_sample(kg, sd, state, sd->P, &coeff); - } - - /* move to new position */ - sd->P = ray->P + sample_t * ray->D; - - return VOLUME_PATH_SCATTERED; -} -# endif /* __SPLIT_KERNEL */ - -/* decide if we need to use decoupled or not */ -ccl_device bool kernel_volume_use_decoupled(KernelGlobals *kg, - bool heterogeneous, - bool direct, - int sampling_method) -{ - /* decoupled ray marching for heterogeneous volumes not supported on the GPU, - * which also means equiangular and multiple importance sampling is not - * support for that case */ - if (!kernel_data.integrator.volume_decoupled) - return false; - -# ifdef __KERNEL_GPU__ - if (heterogeneous) - return false; -# endif - - /* equiangular and multiple importance sampling only implemented for decoupled */ - if (sampling_method != 0) - return true; - - /* for all light sampling use decoupled, reusing shader evaluations is - * typically faster in that case */ - if (direct) - return kernel_data.integrator.sample_all_lights_direct; - else - return kernel_data.integrator.sample_all_lights_indirect; -} - -/* Volume Stack - * - * This is an array of object/shared ID's that the current segment of the path - * is inside of. */ - -ccl_device void kernel_volume_stack_init(KernelGlobals *kg, - ShaderData *stack_sd, - ccl_addr_space const PathState *state, - ccl_addr_space const Ray *ray, - ccl_addr_space VolumeStack *stack) -{ - /* NULL ray happens in the baker, does it need proper initialization of - * camera in volume? - */ - if (!kernel_data.cam.is_inside_volume || ray == NULL) { - /* Camera is guaranteed to be in the air, only take background volume - * into account in this case. - */ - if (kernel_data.background.volume_shader != SHADER_NONE) { - stack[0].shader = kernel_data.background.volume_shader; - stack[0].object = PRIM_NONE; - stack[1].shader = SHADER_NONE; - } - else { - stack[0].shader = SHADER_NONE; - } - return; - } - - kernel_assert(state->flag & PATH_RAY_CAMERA); - - Ray volume_ray = *ray; - volume_ray.t = FLT_MAX; - - const uint visibility = (state->flag & PATH_RAY_ALL_VISIBILITY); - int stack_index = 0, enclosed_index = 0; - -# ifdef __VOLUME_RECORD_ALL__ - Intersection hits[2 * VOLUME_STACK_SIZE + 1]; - uint num_hits = scene_intersect_volume_all( - kg, &volume_ray, hits, 2 * VOLUME_STACK_SIZE, visibility); - if (num_hits > 0) { - int enclosed_volumes[VOLUME_STACK_SIZE]; - Intersection *isect = hits; - - qsort(hits, num_hits, sizeof(Intersection), intersections_compare); - - for (uint hit = 0; hit < num_hits; ++hit, ++isect) { - shader_setup_from_ray(kg, stack_sd, isect, &volume_ray); - if (stack_sd->flag & SD_BACKFACING) { - bool need_add = true; - for (int i = 0; i < enclosed_index && need_add; ++i) { - /* If ray exited the volume and never entered to that volume - * it means that camera is inside such a volume. - */ - if (enclosed_volumes[i] == stack_sd->object) { - need_add = false; - } - } - for (int i = 0; i < stack_index && need_add; ++i) { - /* Don't add intersections twice. */ - if (stack[i].object == stack_sd->object) { - need_add = false; - break; - } - } - if (need_add && stack_index < VOLUME_STACK_SIZE - 1) { - stack[stack_index].object = stack_sd->object; - stack[stack_index].shader = stack_sd->shader; - ++stack_index; - } - } - else { - /* If ray from camera enters the volume, this volume shouldn't - * be added to the stack on exit. - */ - enclosed_volumes[enclosed_index++] = stack_sd->object; - } - } - } -# else - int enclosed_volumes[VOLUME_STACK_SIZE]; - int step = 0; - - while (stack_index < VOLUME_STACK_SIZE - 1 && enclosed_index < VOLUME_STACK_SIZE - 1 && - step < 2 * VOLUME_STACK_SIZE) { - Intersection isect; - if (!scene_intersect_volume(kg, &volume_ray, &isect, visibility)) { - break; - } - - shader_setup_from_ray(kg, stack_sd, &isect, &volume_ray); - if (stack_sd->flag & SD_BACKFACING) { - /* If ray exited the volume and never entered to that volume - * it means that camera is inside such a volume. - */ - bool need_add = true; - for (int i = 0; i < enclosed_index && need_add; ++i) { - /* If ray exited the volume and never entered to that volume - * it means that camera is inside such a volume. - */ - if (enclosed_volumes[i] == stack_sd->object) { - need_add = false; - } - } - for (int i = 0; i < stack_index && need_add; ++i) { - /* Don't add intersections twice. */ - if (stack[i].object == stack_sd->object) { - need_add = false; - break; - } - } - if (need_add) { - stack[stack_index].object = stack_sd->object; - stack[stack_index].shader = stack_sd->shader; - ++stack_index; - } - } - else { - /* If ray from camera enters the volume, this volume shouldn't - * be added to the stack on exit. - */ - enclosed_volumes[enclosed_index++] = stack_sd->object; - } - - /* Move ray forward. */ - volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); - ++step; - } -# endif - /* stack_index of 0 means quick checks outside of the kernel gave false - * positive, nothing to worry about, just we've wasted quite a few of - * ticks just to come into conclusion that camera is in the air. - * - * In this case we're doing the same above -- check whether background has - * volume. - */ - if (stack_index == 0 && kernel_data.background.volume_shader == SHADER_NONE) { - stack[0].shader = kernel_data.background.volume_shader; - stack[0].object = OBJECT_NONE; - stack[1].shader = SHADER_NONE; - } - else { - stack[stack_index].shader = SHADER_NONE; - } -} - -ccl_device void kernel_volume_stack_enter_exit(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space VolumeStack *stack) -{ - /* todo: we should have some way for objects to indicate if they want the - * world shader to work inside them. excluding it by default is problematic - * because non-volume objects can't be assumed to be closed manifolds */ - - if (!(sd->flag & SD_HAS_VOLUME)) - return; - - if (sd->flag & SD_BACKFACING) { - /* exit volume object: remove from stack */ - for (int i = 0; stack[i].shader != SHADER_NONE; i++) { - if (stack[i].object == sd->object) { - /* shift back next stack entries */ - do { - stack[i] = stack[i + 1]; - i++; - } while (stack[i].shader != SHADER_NONE); - - return; - } - } - } - else { - /* enter volume object: add to stack */ - int i; - - for (i = 0; stack[i].shader != SHADER_NONE; i++) { - /* already in the stack? then we have nothing to do */ - if (stack[i].object == sd->object) - return; - } - - /* if we exceed the stack limit, ignore */ - if (i >= VOLUME_STACK_SIZE - 1) - return; - - /* add to the end of the stack */ - stack[i].shader = sd->shader; - stack[i].object = sd->object; - stack[i + 1].shader = SHADER_NONE; - } -} - -# ifdef __SUBSURFACE__ -ccl_device void kernel_volume_stack_update_for_subsurface(KernelGlobals *kg, - ShaderData *stack_sd, - Ray *ray, - ccl_addr_space VolumeStack *stack) -{ - kernel_assert(kernel_data.integrator.use_volumes); - - Ray volume_ray = *ray; - -# ifdef __VOLUME_RECORD_ALL__ - Intersection hits[2 * VOLUME_STACK_SIZE + 1]; - uint num_hits = scene_intersect_volume_all( - kg, &volume_ray, hits, 2 * VOLUME_STACK_SIZE, PATH_RAY_ALL_VISIBILITY); - if (num_hits > 0) { - Intersection *isect = hits; - - qsort(hits, num_hits, sizeof(Intersection), intersections_compare); - - for (uint hit = 0; hit < num_hits; ++hit, ++isect) { - shader_setup_from_ray(kg, stack_sd, isect, &volume_ray); - kernel_volume_stack_enter_exit(kg, stack_sd, stack); - } - } -# else - Intersection isect; - int step = 0; - float3 Pend = ray->P + ray->D * ray->t; - while (step < 2 * VOLUME_STACK_SIZE && - scene_intersect_volume(kg, &volume_ray, &isect, PATH_RAY_ALL_VISIBILITY)) { - shader_setup_from_ray(kg, stack_sd, &isect, &volume_ray); - kernel_volume_stack_enter_exit(kg, stack_sd, stack); - - /* Move ray forward. */ - volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); - if (volume_ray.t != FLT_MAX) { - volume_ray.D = normalize_len(Pend - volume_ray.P, &volume_ray.t); - } - ++step; - } -# endif -} -# endif - -/* Clean stack after the last bounce. - * - * It is expected that all volumes are closed manifolds, so at the time when ray - * hits nothing (for example, it is a last bounce which goes to environment) the - * only expected volume in the stack is the world's one. All the rest volume - * entries should have been exited already. - * - * This isn't always true because of ray intersection precision issues, which - * could lead us to an infinite non-world volume in the stack, causing render - * artifacts. - * - * Use this function after the last bounce to get rid of all volumes apart from - * the world's one after the last bounce to avoid render artifacts. - */ -ccl_device_inline void kernel_volume_clean_stack(KernelGlobals *kg, - ccl_addr_space VolumeStack *volume_stack) -{ - if (kernel_data.background.volume_shader != SHADER_NONE) { - /* Keep the world's volume in stack. */ - volume_stack[1].shader = SHADER_NONE; - } - else { - volume_stack[0].shader = SHADER_NONE; - } -} - -#endif /* __VOLUME__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_work_stealing.h b/intern/cycles/kernel/kernel_work_stealing.h index d1602744f1d..fab0915c38e 100644 --- a/intern/cycles/kernel/kernel_work_stealing.h +++ b/intern/cycles/kernel/kernel_work_stealing.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __KERNEL_WORK_STEALING_H__ -#define __KERNEL_WORK_STEALING_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -24,21 +23,24 @@ CCL_NAMESPACE_BEGIN */ /* Map global work index to tile, pixel X/Y and sample. */ -ccl_device_inline void get_work_pixel(ccl_global const WorkTile *tile, +ccl_device_inline void get_work_pixel(ccl_global const KernelWorkTile *tile, uint global_work_index, ccl_private uint *x, ccl_private uint *y, ccl_private uint *sample) { -#ifdef __KERNEL_CUDA__ - /* Keeping threads for the same pixel together improves performance on CUDA. */ - uint sample_offset = global_work_index % tile->num_samples; - uint pixel_offset = global_work_index / tile->num_samples; -#else /* __KERNEL_CUDA__ */ +#if 0 + /* Keep threads for the same sample together. */ uint tile_pixels = tile->w * tile->h; uint sample_offset = global_work_index / tile_pixels; uint pixel_offset = global_work_index - sample_offset * tile_pixels; -#endif /* __KERNEL_CUDA__ */ +#else + /* Keeping threads for the same pixel together. + * Appears to improve performance by a few % on CUDA and OptiX. */ + uint sample_offset = global_work_index % tile->num_samples; + uint pixel_offset = global_work_index / tile->num_samples; +#endif + uint y_offset = pixel_offset / tile->w; uint x_offset = pixel_offset - y_offset * tile->w; @@ -47,71 +49,4 @@ ccl_device_inline void get_work_pixel(ccl_global const WorkTile *tile, *sample = tile->start_sample + sample_offset; } -#ifdef __KERNEL_OPENCL__ -# pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable -#endif - -#ifdef __SPLIT_KERNEL__ -/* Returns true if there is work */ -ccl_device bool get_next_work_item(KernelGlobals *kg, - ccl_global uint *work_pools, - uint total_work_size, - uint ray_index, - ccl_private uint *global_work_index) -{ - /* With a small amount of work there may be more threads than work due to - * rounding up of global size, stop such threads immediately. */ - if (ray_index >= total_work_size) { - return false; - } - - /* Increase atomic work index counter in pool. */ - uint pool = ray_index / WORK_POOL_SIZE; - uint work_index = atomic_fetch_and_inc_uint32(&work_pools[pool]); - - /* Map per-pool work index to a global work index. */ - uint global_size = ccl_global_size(0) * ccl_global_size(1); - kernel_assert(global_size % WORK_POOL_SIZE == 0); - kernel_assert(ray_index < global_size); - - *global_work_index = (work_index / WORK_POOL_SIZE) * global_size + (pool * WORK_POOL_SIZE) + - (work_index % WORK_POOL_SIZE); - - /* Test if all work for this pool is done. */ - return (*global_work_index < total_work_size); -} - -ccl_device bool get_next_work(KernelGlobals *kg, - ccl_global uint *work_pools, - uint total_work_size, - uint ray_index, - ccl_private uint *global_work_index) -{ - bool got_work = false; - if (kernel_data.film.pass_adaptive_aux_buffer) { - do { - got_work = get_next_work_item(kg, work_pools, total_work_size, ray_index, global_work_index); - if (got_work) { - ccl_global WorkTile *tile = &kernel_split_params.tile; - uint x, y, sample; - get_work_pixel(tile, *global_work_index, &x, &y, &sample); - uint buffer_offset = (tile->offset + x + y * tile->stride) * kernel_data.film.pass_stride; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - ccl_global float4 *aux = (ccl_global float4 *)(buffer + - kernel_data.film.pass_adaptive_aux_buffer); - if ((*aux).w == 0.0f) { - break; - } - } - } while (got_work); - } - else { - got_work = get_next_work_item(kg, work_pools, total_work_size, ray_index, global_work_index); - } - return got_work; -} -#endif - CCL_NAMESPACE_END - -#endif /* __KERNEL_WORK_STEALING_H__ */ diff --git a/intern/cycles/kernel/kernel_write_passes.h b/intern/cycles/kernel/kernel_write_passes.h index 410218d91d4..9d379495629 100644 --- a/intern/cycles/kernel/kernel_write_passes.h +++ b/intern/cycles/kernel/kernel_write_passes.h @@ -14,23 +14,25 @@ * limitations under the License. */ -#if defined(__SPLIT_KERNEL__) || defined(__KERNEL_CUDA__) +#pragma once + +#ifdef __KERNEL_GPU__ # define __ATOMIC_PASS_WRITE__ #endif CCL_NAMESPACE_BEGIN -ccl_device_inline void kernel_write_pass_float(ccl_global float *buffer, float value) +ccl_device_inline void kernel_write_pass_float(ccl_global float *ccl_restrict buffer, float value) { - ccl_global float *buf = buffer; #ifdef __ATOMIC_PASS_WRITE__ - atomic_add_and_fetch_float(buf, value); + atomic_add_and_fetch_float(buffer, value); #else - *buf += value; + *buffer += value; #endif } -ccl_device_inline void kernel_write_pass_float3(ccl_global float *buffer, float3 value) +ccl_device_inline void kernel_write_pass_float3(ccl_global float *ccl_restrict buffer, + float3 value) { #ifdef __ATOMIC_PASS_WRITE__ ccl_global float *buf_x = buffer + 0; @@ -41,12 +43,14 @@ ccl_device_inline void kernel_write_pass_float3(ccl_global float *buffer, float3 atomic_add_and_fetch_float(buf_y, value.y); atomic_add_and_fetch_float(buf_z, value.z); #else - ccl_global float3 *buf = (ccl_global float3 *)buffer; - *buf += value; + buffer[0] += value.x; + buffer[1] += value.y; + buffer[2] += value.z; #endif } -ccl_device_inline void kernel_write_pass_float4(ccl_global float *buffer, float4 value) +ccl_device_inline void kernel_write_pass_float4(ccl_global float *ccl_restrict buffer, + float4 value) { #ifdef __ATOMIC_PASS_WRITE__ ccl_global float *buf_x = buffer + 0; @@ -59,37 +63,26 @@ ccl_device_inline void kernel_write_pass_float4(ccl_global float *buffer, float4 atomic_add_and_fetch_float(buf_z, value.z); atomic_add_and_fetch_float(buf_w, value.w); #else - ccl_global float4 *buf = (ccl_global float4 *)buffer; - *buf += value; -#endif -} - -#ifdef __DENOISING_FEATURES__ -ccl_device_inline void kernel_write_pass_float_variance(ccl_global float *buffer, float value) -{ - kernel_write_pass_float(buffer, value); - - /* The online one-pass variance update that's used for the megakernel can't easily be implemented - * with atomics, so for the split kernel the E[x^2] - 1/N * (E[x])^2 fallback is used. */ - kernel_write_pass_float(buffer + 1, value * value); -} - -# ifdef __ATOMIC_PASS_WRITE__ -# define kernel_write_pass_float3_unaligned kernel_write_pass_float3 -# else -ccl_device_inline void kernel_write_pass_float3_unaligned(ccl_global float *buffer, float3 value) -{ buffer[0] += value.x; buffer[1] += value.y; buffer[2] += value.z; + buffer[3] += value.w; +#endif } -# endif -ccl_device_inline void kernel_write_pass_float3_variance(ccl_global float *buffer, float3 value) +ccl_device_inline float kernel_read_pass_float(ccl_global float *ccl_restrict buffer) { - kernel_write_pass_float3_unaligned(buffer, value); - kernel_write_pass_float3_unaligned(buffer + 3, value * value); + return *buffer; +} + +ccl_device_inline float3 kernel_read_pass_float3(ccl_global float *ccl_restrict buffer) +{ + return make_float3(buffer[0], buffer[1], buffer[2]); +} + +ccl_device_inline float4 kernel_read_pass_float4(ccl_global float *ccl_restrict buffer) +{ + return make_float4(buffer[0], buffer[1], buffer[2], buffer[3]); } -#endif /* __DENOISING_FEATURES__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/cpu/filter.cpp b/intern/cycles/kernel/kernels/cpu/filter.cpp deleted file mode 100644 index 145a6b6ac40..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* CPU kernel entry points */ - -/* On x86-64, we can assume SSE2, so avoid the extra kernel and compile this - * one with SSE2 intrinsics. - */ -#if defined(__x86_64__) || defined(_M_X64) -# define __KERNEL_SSE2__ -#endif - -/* When building kernel for native machine detect kernel features from the flags - * set by compiler. - */ -#ifdef WITH_KERNEL_NATIVE -# ifdef __SSE2__ -# ifndef __KERNEL_SSE2__ -# define __KERNEL_SSE2__ -# endif -# endif -# ifdef __SSE3__ -# define __KERNEL_SSE3__ -# endif -# ifdef __SSSE3__ -# define __KERNEL_SSSE3__ -# endif -# ifdef __SSE4_1__ -# define __KERNEL_SSE41__ -# endif -# ifdef __AVX__ -# define __KERNEL_SSE__ -# define __KERNEL_AVX__ -# endif -# ifdef __AVX2__ -# define __KERNEL_SSE__ -# define __KERNEL_AVX2__ -# endif -#endif - -/* quiet unused define warnings */ -#if defined(__KERNEL_SSE2__) -/* do nothing */ -#endif - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/filter_avx.cpp b/intern/cycles/kernel/kernels/cpu/filter_avx.cpp deleted file mode 100644 index 012daba62d8..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_avx.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with AVX - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE__ -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# define __KERNEL_AVX__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX */ - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu_avx -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/filter_avx2.cpp b/intern/cycles/kernel/kernels/cpu/filter_avx2.cpp deleted file mode 100644 index 16351a7f949..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_avx2.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with AVX2 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE__ -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# define __KERNEL_AVX__ -# define __KERNEL_AVX2__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 */ - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu_avx2 -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/filter_cpu.h b/intern/cycles/kernel/kernels/cpu/filter_cpu.h deleted file mode 100644 index 1423b182ab8..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_cpu.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Templated common declaration part of all CPU kernels. */ - -void KERNEL_FUNCTION_FULL_NAME(filter_divide_shadow)(int sample, - TileInfo *tile_info, - int x, - int y, - float *unfilteredA, - float *unfilteredB, - float *sampleV, - float *sampleVV, - float *bufferV, - int *prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset); - -void KERNEL_FUNCTION_FULL_NAME(filter_get_feature)(int sample, - TileInfo *tile_info, - int m_offset, - int v_offset, - int x, - int y, - float *mean, - float *variance, - float scale, - int *prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset); - -void KERNEL_FUNCTION_FULL_NAME(filter_write_feature)(int sample, - int x, - int y, - int *buffer_params, - float *from, - float *buffer, - int out_offset, - int *prefilter_rect); - -void KERNEL_FUNCTION_FULL_NAME(filter_detect_outliers)(int x, - int y, - ccl_global float *image, - ccl_global float *variance, - ccl_global float *depth, - ccl_global float *output, - int *rect, - int pass_stride); - -void KERNEL_FUNCTION_FULL_NAME(filter_combine_halves)( - int x, int y, float *mean, float *variance, float *a, float *b, int *prefilter_rect, int r); - -void KERNEL_FUNCTION_FULL_NAME(filter_construct_transform)(float *buffer, - TileInfo *tiles, - int x, - int y, - int storage_ofs, - float *transform, - int *rank, - int *rect, - int pass_stride, - int frame_stride, - bool use_time, - int radius, - float pca_threshold); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_calc_difference)(int dx, - int dy, - float *weight_image, - float *variance_image, - float *scale_image, - float *difference_image, - int *rect, - int stride, - int channel_offset, - int frame_offset, - float a, - float k_2); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_blur)( - float *difference_image, float *out_image, int *rect, int stride, int f); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_calc_weight)( - float *difference_image, float *out_image, int *rect, int stride, int f); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_update_output)(int dx, - int dy, - float *difference_image, - float *image, - float *temp_image, - float *out_image, - float *accum_image, - int *rect, - int channel_offset, - int stride, - int f); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_construct_gramian)(int dx, - int dy, - int t, - float *difference_image, - float *buffer, - float *transform, - int *rank, - float *XtWX, - float3 *XtWY, - int *rect, - int *filter_window, - int stride, - int f, - int pass_stride, - int frame_offset, - bool use_time); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_normalize)(float *out_image, - float *accum_image, - int *rect, - int stride); - -void KERNEL_FUNCTION_FULL_NAME(filter_finalize)(int x, - int y, - int storage_ofs, - float *buffer, - int *rank, - float *XtWX, - float3 *XtWY, - int *buffer_params, - int sample); - -#undef KERNEL_ARCH diff --git a/intern/cycles/kernel/kernels/cpu/filter_cpu_impl.h b/intern/cycles/kernel/kernels/cpu/filter_cpu_impl.h deleted file mode 100644 index 3d4cb87e104..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_cpu_impl.h +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Templated common implementation part of all CPU kernels. - * - * The idea is that particular .cpp files sets needed optimization flags and - * simply includes this file without worry of copying actual implementation over. - */ - -#include "kernel/kernel_compat_cpu.h" - -#include "kernel/filter/filter_kernel.h" - -#ifdef KERNEL_STUB -# define STUB_ASSERT(arch, name) \ - assert(!(#name " kernel stub for architecture " #arch " was called!")) -#endif - -CCL_NAMESPACE_BEGIN - -/* Denoise filter */ - -void KERNEL_FUNCTION_FULL_NAME(filter_divide_shadow)(int sample, - TileInfo *tile_info, - int x, - int y, - float *unfilteredA, - float *unfilteredB, - float *sampleVariance, - float *sampleVarianceV, - float *bufferVariance, - int *prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_divide_shadow); -#else - kernel_filter_divide_shadow(sample, - tile_info, - x, - y, - unfilteredA, - unfilteredB, - sampleVariance, - sampleVarianceV, - bufferVariance, - load_int4(prefilter_rect), - buffer_pass_stride, - buffer_denoising_offset); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_get_feature)(int sample, - TileInfo *tile_info, - int m_offset, - int v_offset, - int x, - int y, - float *mean, - float *variance, - float scale, - int *prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_get_feature); -#else - kernel_filter_get_feature(sample, - tile_info, - m_offset, - v_offset, - x, - y, - mean, - variance, - scale, - load_int4(prefilter_rect), - buffer_pass_stride, - buffer_denoising_offset); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_write_feature)(int sample, - int x, - int y, - int *buffer_params, - float *from, - float *buffer, - int out_offset, - int *prefilter_rect) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_write_feature); -#else - kernel_filter_write_feature( - sample, x, y, load_int4(buffer_params), from, buffer, out_offset, load_int4(prefilter_rect)); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_detect_outliers)(int x, - int y, - ccl_global float *image, - ccl_global float *variance, - ccl_global float *depth, - ccl_global float *output, - int *rect, - int pass_stride) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_detect_outliers); -#else - kernel_filter_detect_outliers( - x, y, image, variance, depth, output, load_int4(rect), pass_stride); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_combine_halves)( - int x, int y, float *mean, float *variance, float *a, float *b, int *prefilter_rect, int r) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_combine_halves); -#else - kernel_filter_combine_halves(x, y, mean, variance, a, b, load_int4(prefilter_rect), r); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_construct_transform)(float *buffer, - TileInfo *tile_info, - int x, - int y, - int storage_ofs, - float *transform, - int *rank, - int *prefilter_rect, - int pass_stride, - int frame_stride, - bool use_time, - int radius, - float pca_threshold) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_construct_transform); -#else - rank += storage_ofs; - transform += storage_ofs * TRANSFORM_SIZE; - kernel_filter_construct_transform(buffer, - tile_info, - x, - y, - load_int4(prefilter_rect), - pass_stride, - frame_stride, - use_time, - transform, - rank, - radius, - pca_threshold); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_calc_difference)(int dx, - int dy, - float *weight_image, - float *variance_image, - float *scale_image, - float *difference_image, - int *rect, - int stride, - int channel_offset, - int frame_offset, - float a, - float k_2) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_calc_difference); -#else - kernel_filter_nlm_calc_difference(dx, - dy, - weight_image, - variance_image, - scale_image, - difference_image, - load_int4(rect), - stride, - channel_offset, - frame_offset, - a, - k_2); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_blur)( - float *difference_image, float *out_image, int *rect, int stride, int f) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_blur); -#else - kernel_filter_nlm_blur(difference_image, out_image, load_int4(rect), stride, f); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_calc_weight)( - float *difference_image, float *out_image, int *rect, int stride, int f) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_calc_weight); -#else - kernel_filter_nlm_calc_weight(difference_image, out_image, load_int4(rect), stride, f); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_update_output)(int dx, - int dy, - float *difference_image, - float *image, - float *temp_image, - float *out_image, - float *accum_image, - int *rect, - int channel_offset, - int stride, - int f) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_update_output); -#else - kernel_filter_nlm_update_output(dx, - dy, - difference_image, - image, - temp_image, - out_image, - accum_image, - load_int4(rect), - channel_offset, - stride, - f); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_construct_gramian)(int dx, - int dy, - int t, - float *difference_image, - float *buffer, - float *transform, - int *rank, - float *XtWX, - float3 *XtWY, - int *rect, - int *filter_window, - int stride, - int f, - int pass_stride, - int frame_offset, - bool use_time) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_construct_gramian); -#else - kernel_filter_nlm_construct_gramian(dx, - dy, - t, - difference_image, - buffer, - transform, - rank, - XtWX, - XtWY, - load_int4(rect), - load_int4(filter_window), - stride, - f, - pass_stride, - frame_offset, - use_time); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_normalize)(float *out_image, - float *accum_image, - int *rect, - int stride) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_normalize); -#else - kernel_filter_nlm_normalize(out_image, accum_image, load_int4(rect), stride); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_finalize)(int x, - int y, - int storage_ofs, - float *buffer, - int *rank, - float *XtWX, - float3 *XtWY, - int *buffer_params, - int sample) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_finalize); -#else - XtWX += storage_ofs * XTWX_SIZE; - XtWY += storage_ofs * XTWY_SIZE; - rank += storage_ofs; - kernel_filter_finalize(x, y, buffer, rank, 1, XtWX, XtWY, load_int4(buffer_params), sample); -#endif -} - -#undef KERNEL_STUB -#undef STUB_ASSERT -#undef KERNEL_ARCH - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/cpu/filter_sse2.cpp b/intern/cycles/kernel/kernels/cpu/filter_sse2.cpp deleted file mode 100644 index 75833d83648..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_sse2.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE2 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE2__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 */ - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu_sse2 -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/filter_sse3.cpp b/intern/cycles/kernel/kernels/cpu/filter_sse3.cpp deleted file mode 100644 index c998cd54d3a..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_sse3.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE3/SSSE3 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 */ - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu_sse3 -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/filter_sse41.cpp b/intern/cycles/kernel/kernels/cpu/filter_sse41.cpp deleted file mode 100644 index fc4ef1fca5b..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_sse41.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE3/SSSE3 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE__ -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 */ - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu_sse41 -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_cpu.h b/intern/cycles/kernel/kernels/cpu/kernel_cpu.h deleted file mode 100644 index ea3103f12c3..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_cpu.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Templated common declaration part of all CPU kernels. */ - -void KERNEL_FUNCTION_FULL_NAME(path_trace)( - KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride); - -void KERNEL_FUNCTION_FULL_NAME(convert_to_byte)(KernelGlobals *kg, - uchar4 *rgba, - float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride); - -void KERNEL_FUNCTION_FULL_NAME(convert_to_half_float)(KernelGlobals *kg, - uchar4 *rgba, - float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride); - -void KERNEL_FUNCTION_FULL_NAME(shader)(KernelGlobals *kg, - uint4 *input, - float4 *output, - int type, - int filter, - int i, - int offset, - int sample); - -void KERNEL_FUNCTION_FULL_NAME(bake)( - KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride); - -/* Split kernels */ - -void KERNEL_FUNCTION_FULL_NAME(data_init)(KernelGlobals *kg, - ccl_constant KernelData *data, - ccl_global void *split_data_buffer, - int num_elements, - ccl_global char *ray_state, - int start_sample, - int end_sample, - int sx, - int sy, - int sw, - int sh, - int offset, - int stride, - ccl_global int *Queue_index, - int queuesize, - ccl_global char *use_queues_flag, - ccl_global unsigned int *work_pool_wgs, - unsigned int num_samples, - ccl_global float *buffer); - -#define DECLARE_SPLIT_KERNEL_FUNCTION(name) \ - void KERNEL_FUNCTION_FULL_NAME(name)(KernelGlobals * kg, KernelData * data); - -DECLARE_SPLIT_KERNEL_FUNCTION(path_init) -DECLARE_SPLIT_KERNEL_FUNCTION(scene_intersect) -DECLARE_SPLIT_KERNEL_FUNCTION(lamp_emission) -DECLARE_SPLIT_KERNEL_FUNCTION(do_volume) -DECLARE_SPLIT_KERNEL_FUNCTION(queue_enqueue) -DECLARE_SPLIT_KERNEL_FUNCTION(indirect_background) -DECLARE_SPLIT_KERNEL_FUNCTION(shader_setup) -DECLARE_SPLIT_KERNEL_FUNCTION(shader_sort) -DECLARE_SPLIT_KERNEL_FUNCTION(shader_eval) -DECLARE_SPLIT_KERNEL_FUNCTION(holdout_emission_blurring_pathtermination_ao) -DECLARE_SPLIT_KERNEL_FUNCTION(subsurface_scatter) -DECLARE_SPLIT_KERNEL_FUNCTION(direct_lighting) -DECLARE_SPLIT_KERNEL_FUNCTION(shadow_blocked_ao) -DECLARE_SPLIT_KERNEL_FUNCTION(shadow_blocked_dl) -DECLARE_SPLIT_KERNEL_FUNCTION(enqueue_inactive) -DECLARE_SPLIT_KERNEL_FUNCTION(next_iteration_setup) -DECLARE_SPLIT_KERNEL_FUNCTION(indirect_subsurface) -DECLARE_SPLIT_KERNEL_FUNCTION(buffer_update) -DECLARE_SPLIT_KERNEL_FUNCTION(adaptive_stopping) -DECLARE_SPLIT_KERNEL_FUNCTION(adaptive_filter_x) -DECLARE_SPLIT_KERNEL_FUNCTION(adaptive_filter_y) -DECLARE_SPLIT_KERNEL_FUNCTION(adaptive_adjust_samples) - -#undef KERNEL_ARCH diff --git a/intern/cycles/kernel/kernels/cpu/kernel_cpu_impl.h b/intern/cycles/kernel/kernels/cpu/kernel_cpu_impl.h deleted file mode 100644 index 51d6c23f72f..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_cpu_impl.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Templated common implementation part of all CPU kernels. - * - * The idea is that particular .cpp files sets needed optimization flags and - * simply includes this file without worry of copying actual implementation over. - */ - -// clang-format off -#include "kernel/kernel_compat_cpu.h" - -#ifndef KERNEL_STUB -# ifndef __SPLIT_KERNEL__ -# include "kernel/kernel_math.h" -# include "kernel/kernel_types.h" - -# include "kernel/split/kernel_split_data.h" -# include "kernel/kernel_globals.h" - -# include "kernel/kernel_color.h" -# include "kernel/kernels/cpu/kernel_cpu_image.h" -# include "kernel/kernel_film.h" -# include "kernel/kernel_path.h" -# include "kernel/kernel_path_branched.h" -# include "kernel/kernel_bake.h" -# else -# include "kernel/split/kernel_split_common.h" - -# include "kernel/split/kernel_data_init.h" -# include "kernel/split/kernel_path_init.h" -# include "kernel/split/kernel_scene_intersect.h" -# include "kernel/split/kernel_lamp_emission.h" -# include "kernel/split/kernel_do_volume.h" -# include "kernel/split/kernel_queue_enqueue.h" -# include "kernel/split/kernel_indirect_background.h" -# include "kernel/split/kernel_shader_setup.h" -# include "kernel/split/kernel_shader_sort.h" -# include "kernel/split/kernel_shader_eval.h" -# include "kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h" -# include "kernel/split/kernel_subsurface_scatter.h" -# include "kernel/split/kernel_direct_lighting.h" -# include "kernel/split/kernel_shadow_blocked_ao.h" -# include "kernel/split/kernel_shadow_blocked_dl.h" -# include "kernel/split/kernel_enqueue_inactive.h" -# include "kernel/split/kernel_next_iteration_setup.h" -# include "kernel/split/kernel_indirect_subsurface.h" -# include "kernel/split/kernel_buffer_update.h" -# include "kernel/split/kernel_adaptive_stopping.h" -# include "kernel/split/kernel_adaptive_filter_x.h" -# include "kernel/split/kernel_adaptive_filter_y.h" -# include "kernel/split/kernel_adaptive_adjust_samples.h" -# endif /* __SPLIT_KERNEL__ */ -#else -# define STUB_ASSERT(arch, name) \ - assert(!(#name " kernel stub for architecture " #arch " was called!")) - -# ifdef __SPLIT_KERNEL__ -# include "kernel/split/kernel_data_init.h" -# endif /* __SPLIT_KERNEL__ */ -#endif /* KERNEL_STUB */ -// clang-format on - -CCL_NAMESPACE_BEGIN - -#ifndef __SPLIT_KERNEL__ - -/* Path Tracing */ - -void KERNEL_FUNCTION_FULL_NAME(path_trace)( - KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride) -{ -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, path_trace); -# else -# ifdef __BRANCHED_PATH__ - if (kernel_data.integrator.branched) { - kernel_branched_path_trace(kg, buffer, sample, x, y, offset, stride); - } - else -# endif - { - kernel_path_trace(kg, buffer, sample, x, y, offset, stride); - } -# endif /* KERNEL_STUB */ -} - -/* Film */ - -void KERNEL_FUNCTION_FULL_NAME(convert_to_byte)(KernelGlobals *kg, - uchar4 *rgba, - float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride) -{ -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, convert_to_byte); -# else - kernel_film_convert_to_byte(kg, rgba, buffer, sample_scale, x, y, offset, stride); -# endif /* KERNEL_STUB */ -} - -void KERNEL_FUNCTION_FULL_NAME(convert_to_half_float)(KernelGlobals *kg, - uchar4 *rgba, - float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride) -{ -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, convert_to_half_float); -# else - kernel_film_convert_to_half_float(kg, rgba, buffer, sample_scale, x, y, offset, stride); -# endif /* KERNEL_STUB */ -} - -/* Bake */ - -void KERNEL_FUNCTION_FULL_NAME(bake)( - KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride) -{ -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, bake); -# else -# ifdef __BAKING__ - kernel_bake_evaluate(kg, buffer, sample, x, y, offset, stride); -# endif -# endif /* KERNEL_STUB */ -} - -/* Shader Evaluate */ - -void KERNEL_FUNCTION_FULL_NAME(shader)(KernelGlobals *kg, - uint4 *input, - float4 *output, - int type, - int filter, - int i, - int offset, - int sample) -{ -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, shader); -# else - if (type == SHADER_EVAL_DISPLACE) { - kernel_displace_evaluate(kg, input, output, i); - } - else { - kernel_background_evaluate(kg, input, output, i); - } -# endif /* KERNEL_STUB */ -} - -#else /* __SPLIT_KERNEL__ */ - -/* Split Kernel Path Tracing */ - -# ifdef KERNEL_STUB -# define DEFINE_SPLIT_KERNEL_FUNCTION(name) \ - void KERNEL_FUNCTION_FULL_NAME(name)(KernelGlobals * kg, KernelData * /*data*/) \ - { \ - STUB_ASSERT(KERNEL_ARCH, name); \ - } - -# define DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(name, type) \ - void KERNEL_FUNCTION_FULL_NAME(name)(KernelGlobals * kg, KernelData * /*data*/) \ - { \ - STUB_ASSERT(KERNEL_ARCH, name); \ - } -# else -# define DEFINE_SPLIT_KERNEL_FUNCTION(name) \ - void KERNEL_FUNCTION_FULL_NAME(name)(KernelGlobals * kg, KernelData * /*data*/) \ - { \ - kernel_##name(kg); \ - } - -# define DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(name, type) \ - void KERNEL_FUNCTION_FULL_NAME(name)(KernelGlobals * kg, KernelData * /*data*/) \ - { \ - ccl_local type locals; \ - kernel_##name(kg, &locals); \ - } -# endif /* KERNEL_STUB */ - -DEFINE_SPLIT_KERNEL_FUNCTION(path_init) -DEFINE_SPLIT_KERNEL_FUNCTION(scene_intersect) -DEFINE_SPLIT_KERNEL_FUNCTION(lamp_emission) -DEFINE_SPLIT_KERNEL_FUNCTION(do_volume) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(queue_enqueue, QueueEnqueueLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(indirect_background) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(shader_setup, uint) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(shader_sort, ShaderSortLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(shader_eval) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(holdout_emission_blurring_pathtermination_ao, - BackgroundAOLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(subsurface_scatter) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(direct_lighting, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(shadow_blocked_ao) -DEFINE_SPLIT_KERNEL_FUNCTION(shadow_blocked_dl) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(enqueue_inactive, uint) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(next_iteration_setup, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(indirect_subsurface) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(buffer_update, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_stopping) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_filter_x) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_filter_y) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_adjust_samples) -#endif /* __SPLIT_KERNEL__ */ - -#undef KERNEL_STUB -#undef STUB_ASSERT -#undef KERNEL_ARCH - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split.cpp deleted file mode 100644 index 989f5e5aaa8..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* CPU kernel entry points */ - -/* On x86-64, we can assume SSE2, so avoid the extra kernel and compile this - * one with SSE2 intrinsics. - */ -#if defined(__x86_64__) || defined(_M_X64) -# define __KERNEL_SSE2__ -#endif - -#define __SPLIT_KERNEL__ - -/* When building kernel for native machine detect kernel features from the flags - * set by compiler. - */ -#ifdef WITH_KERNEL_NATIVE -# ifdef __SSE2__ -# ifndef __KERNEL_SSE2__ -# define __KERNEL_SSE2__ -# endif -# endif -# ifdef __SSE3__ -# define __KERNEL_SSE3__ -# endif -# ifdef __SSSE3__ -# define __KERNEL_SSSE3__ -# endif -# ifdef __SSE4_1__ -# define __KERNEL_SSE41__ -# endif -# ifdef __AVX__ -# define __KERNEL_AVX__ -# endif -# ifdef __AVX2__ -# define __KERNEL_SSE__ -# define __KERNEL_AVX2__ -# endif -#endif - -/* quiet unused define warnings */ -#if defined(__KERNEL_SSE2__) -/* do nothing */ -#endif - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split_avx.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split_avx.cpp deleted file mode 100644 index 40e485d27c0..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split_avx.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with AVX - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#define __SPLIT_KERNEL__ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE__ -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# define __KERNEL_AVX__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX */ - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu_avx -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split_avx2.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split_avx2.cpp deleted file mode 100644 index 8c44238470e..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split_avx2.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2011-2014 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with AVX2 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#define __SPLIT_KERNEL__ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE__ -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# define __KERNEL_AVX__ -# define __KERNEL_AVX2__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 */ - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu_avx2 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split_sse2.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split_sse2.cpp deleted file mode 100644 index 7a3f218d5fc..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split_sse2.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE2 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#define __SPLIT_KERNEL__ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE2__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 */ - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu_sse2 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split_sse3.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split_sse3.cpp deleted file mode 100644 index 1cab59e0ea0..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split_sse3.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE3/SSSE3 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#define __SPLIT_KERNEL__ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 */ - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu_sse3 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split_sse41.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split_sse41.cpp deleted file mode 100644 index 637126d9d4c..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split_sse41.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE3/SSSE3 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#define __SPLIT_KERNEL__ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 */ - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu_sse41 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cuda/filter.cu b/intern/cycles/kernel/kernels/cuda/filter.cu deleted file mode 100644 index 6c9642d1f03..00000000000 --- a/intern/cycles/kernel/kernels/cuda/filter.cu +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* CUDA kernel entry points */ - -#ifdef __CUDA_ARCH__ - -#include "kernel_config.h" - -#include "kernel/kernel_compat_cuda.h" - -#include "kernel/filter/filter_kernel.h" - -/* kernels */ - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_copy_input(float *buffer, - CCL_FILTER_TILE_INFO, - int4 prefilter_rect, - int buffer_pass_stride) -{ - int x = prefilter_rect.x + blockDim.x*blockIdx.x + threadIdx.x; - int y = prefilter_rect.y + blockDim.y*blockIdx.y + threadIdx.y; - if(x < prefilter_rect.z && y < prefilter_rect.w) { - int xtile = (x < tile_info->x[1]) ? 0 : ((x < tile_info->x[2]) ? 1 : 2); - int ytile = (y < tile_info->y[1]) ? 0 : ((y < tile_info->y[2]) ? 1 : 2); - int itile = ytile * 3 + xtile; - float *const in = ((float *)ccl_get_tile_buffer(itile)) + - (tile_info->offsets[itile] + y * tile_info->strides[itile] + x) * buffer_pass_stride; - buffer += ((y - prefilter_rect.y) * (prefilter_rect.z - prefilter_rect.x) + (x - prefilter_rect.x)) * buffer_pass_stride; - for (int i = 0; i < buffer_pass_stride; ++i) - buffer[i] = in[i]; - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_convert_to_rgb(float *rgb, float *buf, int sw, int sh, int stride, int pass_stride, int3 pass_offset, int num_inputs, int num_samples) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < sw && y < sh) { - if (num_inputs > 0) { - float *in = buf + x * pass_stride + (y * stride + pass_offset.x) / sizeof(float); - float *out = rgb + (x + y * sw) * 3; - out[0] = clamp(in[0] / num_samples, 0.0f, 10000.0f); - out[1] = clamp(in[1] / num_samples, 0.0f, 10000.0f); - out[2] = clamp(in[2] / num_samples, 0.0f, 10000.0f); - } - if (num_inputs > 1) { - float *in = buf + x * pass_stride + (y * stride + pass_offset.y) / sizeof(float); - float *out = rgb + (x + y * sw) * 3 + (sw * sh) * 3; - out[0] = in[0] / num_samples; - out[1] = in[1] / num_samples; - out[2] = in[2] / num_samples; - } - if (num_inputs > 2) { - float *in = buf + x * pass_stride + (y * stride + pass_offset.z) / sizeof(float); - float *out = rgb + (x + y * sw) * 3 + (sw * sh * 2) * 3; - out[0] = in[0] / num_samples; - out[1] = in[1] / num_samples; - out[2] = in[2] / num_samples; - } - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_convert_from_rgb(float *rgb, float *buf, int ix, int iy, int iw, int ih, int sx, int sy, int sw, int sh, int offset, int stride, int pass_stride, int num_samples) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < sw && y < sh) { - float *in = rgb + ((ix + x) + (iy + y) * iw) * 3; - float *out = buf + (offset + (sx + x) + (sy + y) * stride) * pass_stride; - out[0] = in[0] * num_samples; - out[1] = in[1] * num_samples; - out[2] = in[2] * num_samples; - } -} - - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_divide_shadow(int sample, - CCL_FILTER_TILE_INFO, - float *unfilteredA, - float *unfilteredB, - float *sampleVariance, - float *sampleVarianceV, - float *bufferVariance, - int4 prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int x = prefilter_rect.x + blockDim.x*blockIdx.x + threadIdx.x; - int y = prefilter_rect.y + blockDim.y*blockIdx.y + threadIdx.y; - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_divide_shadow(sample, - tile_info, - x, y, - unfilteredA, - unfilteredB, - sampleVariance, - sampleVarianceV, - bufferVariance, - prefilter_rect, - buffer_pass_stride, - buffer_denoising_offset); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_get_feature(int sample, - CCL_FILTER_TILE_INFO, - int m_offset, - int v_offset, - float *mean, - float *variance, - float scale, - int4 prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int x = prefilter_rect.x + blockDim.x*blockIdx.x + threadIdx.x; - int y = prefilter_rect.y + blockDim.y*blockIdx.y + threadIdx.y; - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_get_feature(sample, - tile_info, - m_offset, v_offset, - x, y, - mean, variance, - scale, - prefilter_rect, - buffer_pass_stride, - buffer_denoising_offset); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_write_feature(int sample, - int4 buffer_params, - int4 filter_area, - float *from, - float *buffer, - int out_offset, - int4 prefilter_rect) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < filter_area.z && y < filter_area.w) { - kernel_filter_write_feature(sample, - x + filter_area.x, - y + filter_area.y, - buffer_params, - from, - buffer, - out_offset, - prefilter_rect); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_detect_outliers(float *image, - float *variance, - float *depth, - float *output, - int4 prefilter_rect, - int pass_stride) -{ - int x = prefilter_rect.x + blockDim.x*blockIdx.x + threadIdx.x; - int y = prefilter_rect.y + blockDim.y*blockIdx.y + threadIdx.y; - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_detect_outliers(x, y, image, variance, depth, output, prefilter_rect, pass_stride); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_combine_halves(float *mean, float *variance, float *a, float *b, int4 prefilter_rect, int r) -{ - int x = prefilter_rect.x + blockDim.x*blockIdx.x + threadIdx.x; - int y = prefilter_rect.y + blockDim.y*blockIdx.y + threadIdx.y; - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_combine_halves(x, y, mean, variance, a, b, prefilter_rect, r); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_construct_transform(float const* __restrict__ buffer, - CCL_FILTER_TILE_INFO, - float *transform, int *rank, - int4 filter_area, int4 rect, - int radius, float pca_threshold, - int pass_stride, int frame_stride, - bool use_time) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < filter_area.z && y < filter_area.w) { - int *l_rank = rank + y*filter_area.z + x; - float *l_transform = transform + y*filter_area.z + x; - kernel_filter_construct_transform(buffer, - tile_info, - x + filter_area.x, y + filter_area.y, - rect, - pass_stride, frame_stride, - use_time, - l_transform, l_rank, - radius, pca_threshold, - filter_area.z*filter_area.w, - threadIdx.y*blockDim.x + threadIdx.x); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_calc_difference(const float *ccl_restrict weight_image, - const float *ccl_restrict variance_image, - const float *ccl_restrict scale_image, - float *difference_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int channel_offset, - int frame_offset, - float a, - float k_2) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_calc_difference(co.x, co.y, co.z, co.w, - weight_image, - variance_image, - scale_image, - difference_image + ofs, - rect, stride, - channel_offset, - frame_offset, - a, k_2); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_blur(const float *ccl_restrict difference_image, - float *out_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_blur(co.x, co.y, - difference_image + ofs, - out_image + ofs, - rect, stride, f); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_calc_weight(const float *ccl_restrict difference_image, - float *out_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_calc_weight(co.x, co.y, - difference_image + ofs, - out_image + ofs, - rect, stride, f); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_update_output(const float *ccl_restrict difference_image, - const float *ccl_restrict image, - float *out_image, - float *accum_image, - int w, - int h, - int stride, - int pass_stride, - int channel_offset, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_update_output(co.x, co.y, co.z, co.w, - difference_image + ofs, - image, - out_image, - accum_image, - rect, - channel_offset, - stride, f); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_normalize(float *out_image, - const float *ccl_restrict accum_image, - int w, - int h, - int stride) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < w && y < h) { - kernel_filter_nlm_normalize(x, y, out_image, accum_image, stride); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_construct_gramian(int t, - const float *ccl_restrict difference_image, - const float *ccl_restrict buffer, - float const* __restrict__ transform, - int *rank, - float *XtWX, - float3 *XtWY, - int4 filter_window, - int w, - int h, - int stride, - int pass_stride, - int r, - int f, - int frame_offset, - bool use_time) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords_window(w, h, r, pass_stride, &rect, &co, &ofs, filter_window)) { - kernel_filter_nlm_construct_gramian(co.x, co.y, - co.z, co.w, - t, - difference_image + ofs, - buffer, - transform, rank, - XtWX, XtWY, - rect, filter_window, - stride, f, - pass_stride, - frame_offset, - use_time, - threadIdx.y*blockDim.x + threadIdx.x); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_finalize(float *buffer, - int *rank, - float *XtWX, - float3 *XtWY, - int4 filter_area, - int4 buffer_params, - int sample) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < filter_area.z && y < filter_area.w) { - int storage_ofs = y*filter_area.z+x; - rank += storage_ofs; - XtWX += storage_ofs; - XtWY += storage_ofs; - kernel_filter_finalize(x, y, buffer, rank, - filter_area.z*filter_area.w, - XtWX, XtWY, - buffer_params, sample); - } -} - -#endif - diff --git a/intern/cycles/kernel/kernels/cuda/kernel.cu b/intern/cycles/kernel/kernels/cuda/kernel.cu deleted file mode 100644 index cf62b6e781e..00000000000 --- a/intern/cycles/kernel/kernels/cuda/kernel.cu +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* CUDA kernel entry points */ - -#ifdef __CUDA_ARCH__ - -#include "kernel/kernel_compat_cuda.h" -#include "kernel_config.h" - -#include "util/util_atomic.h" - -#include "kernel/kernel_math.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" -#include "kernel/kernels/cuda/kernel_cuda_image.h" -#include "kernel/kernel_film.h" -#include "kernel/kernel_path.h" -#include "kernel/kernel_path_branched.h" -#include "kernel/kernel_bake.h" -#include "kernel/kernel_work_stealing.h" -#include "kernel/kernel_adaptive_sampling.h" - -/* kernels */ -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_path_trace(WorkTile *tile, uint total_work_size) -{ - int work_index = ccl_global_id(0); - bool thread_is_active = work_index < total_work_size; - uint x, y, sample; - KernelGlobals kg; - if(thread_is_active) { - get_work_pixel(tile, work_index, &x, &y, &sample); - - kernel_path_trace(&kg, tile->buffer, sample, x, y, tile->offset, tile->stride); - } - - if(kernel_data.film.cryptomatte_passes) { - __syncthreads(); - if(thread_is_active) { - kernel_cryptomatte_post(&kg, tile->buffer, sample, x, y, tile->offset, tile->stride); - } - } -} - -#ifdef __BRANCHED_PATH__ -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_BRANCHED_MAX_REGISTERS) -kernel_cuda_branched_path_trace(WorkTile *tile, uint total_work_size) -{ - int work_index = ccl_global_id(0); - bool thread_is_active = work_index < total_work_size; - uint x, y, sample; - KernelGlobals kg; - if(thread_is_active) { - get_work_pixel(tile, work_index, &x, &y, &sample); - - kernel_branched_path_trace(&kg, tile->buffer, sample, x, y, tile->offset, tile->stride); - } - - if(kernel_data.film.cryptomatte_passes) { - __syncthreads(); - if(thread_is_active) { - kernel_cryptomatte_post(&kg, tile->buffer, sample, x, y, tile->offset, tile->stride); - } - } -} -#endif - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_adaptive_stopping(WorkTile *tile, int sample, uint total_work_size) -{ - int work_index = ccl_global_id(0); - bool thread_is_active = work_index < total_work_size; - KernelGlobals kg; - if(thread_is_active && kernel_data.film.pass_adaptive_aux_buffer) { - uint x = tile->x + work_index % tile->w; - uint y = tile->y + work_index / tile->w; - int index = tile->offset + x + y * tile->stride; - ccl_global float *buffer = tile->buffer + index * kernel_data.film.pass_stride; - kernel_do_adaptive_stopping(&kg, buffer, sample); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_adaptive_filter_x(WorkTile *tile, int sample, uint) -{ - KernelGlobals kg; - if(kernel_data.film.pass_adaptive_aux_buffer && sample > kernel_data.integrator.adaptive_min_samples) { - if(ccl_global_id(0) < tile->h) { - int y = tile->y + ccl_global_id(0); - kernel_do_adaptive_filter_x(&kg, y, tile); - } - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_adaptive_filter_y(WorkTile *tile, int sample, uint) -{ - KernelGlobals kg; - if(kernel_data.film.pass_adaptive_aux_buffer && sample > kernel_data.integrator.adaptive_min_samples) { - if(ccl_global_id(0) < tile->w) { - int x = tile->x + ccl_global_id(0); - kernel_do_adaptive_filter_y(&kg, x, tile); - } - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_adaptive_scale_samples(WorkTile *tile, int start_sample, int sample, uint total_work_size) -{ - if(kernel_data.film.pass_adaptive_aux_buffer) { - int work_index = ccl_global_id(0); - bool thread_is_active = work_index < total_work_size; - KernelGlobals kg; - if(thread_is_active) { - uint x = tile->x + work_index % tile->w; - uint y = tile->y + work_index / tile->w; - int index = tile->offset + x + y * tile->stride; - ccl_global float *buffer = tile->buffer + index * kernel_data.film.pass_stride; - if(buffer[kernel_data.film.pass_sample_count] < 0.0f) { - buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count]; - float sample_multiplier = sample / buffer[kernel_data.film.pass_sample_count]; - if(sample_multiplier != 1.0f) { - kernel_adaptive_post_adjust(&kg, buffer, sample_multiplier); - } - } - else { - kernel_adaptive_post_adjust(&kg, buffer, sample / (sample - 1.0f)); - } - } - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_convert_to_byte(uchar4 *rgba, float *buffer, float sample_scale, int sx, int sy, int sw, int sh, int offset, int stride) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - int y = sy + blockDim.y*blockIdx.y + threadIdx.y; - - if(x < sx + sw && y < sy + sh) { - kernel_film_convert_to_byte(NULL, rgba, buffer, sample_scale, x, y, offset, stride); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_convert_to_half_float(uchar4 *rgba, float *buffer, float sample_scale, int sx, int sy, int sw, int sh, int offset, int stride) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - int y = sy + blockDim.y*blockIdx.y + threadIdx.y; - - if(x < sx + sw && y < sy + sh) { - kernel_film_convert_to_half_float(NULL, rgba, buffer, sample_scale, x, y, offset, stride); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_displace(uint4 *input, - float4 *output, - int type, - int sx, - int sw, - int offset, - int sample) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - - if(x < sx + sw) { - KernelGlobals kg; - kernel_displace_evaluate(&kg, input, output, x); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_background(uint4 *input, - float4 *output, - int type, - int sx, - int sw, - int offset, - int sample) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - - if(x < sx + sw) { - KernelGlobals kg; - kernel_background_evaluate(&kg, input, output, x); - } -} - -#ifdef __BAKING__ -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_bake(WorkTile *tile, uint total_work_size) -{ - int work_index = ccl_global_id(0); - - if(work_index < total_work_size) { - uint x, y, sample; - get_work_pixel(tile, work_index, &x, &y, &sample); - - KernelGlobals kg; - kernel_bake_evaluate(&kg, tile->buffer, sample, x, y, tile->offset, tile->stride); - } -} -#endif - -#endif - diff --git a/intern/cycles/kernel/kernels/cuda/kernel_config.h b/intern/cycles/kernel/kernels/cuda/kernel_config.h deleted file mode 100644 index 2e47ce2de6c..00000000000 --- a/intern/cycles/kernel/kernels/cuda/kernel_config.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* device data taken from CUDA occupancy calculator */ - -/* 3.0 and 3.5 */ -#if __CUDA_ARCH__ == 300 || __CUDA_ARCH__ == 350 -# define CUDA_MULTIPRESSOR_MAX_REGISTERS 65536 -# define CUDA_MULTIPROCESSOR_MAX_BLOCKS 16 -# define CUDA_BLOCK_MAX_THREADS 1024 -# define CUDA_THREAD_MAX_REGISTERS 63 - -/* tunable parameters */ -# define CUDA_THREADS_BLOCK_WIDTH 16 -# define CUDA_KERNEL_MAX_REGISTERS 63 -# define CUDA_KERNEL_BRANCHED_MAX_REGISTERS 63 - -/* 3.2 */ -#elif __CUDA_ARCH__ == 320 -# define CUDA_MULTIPRESSOR_MAX_REGISTERS 32768 -# define CUDA_MULTIPROCESSOR_MAX_BLOCKS 16 -# define CUDA_BLOCK_MAX_THREADS 1024 -# define CUDA_THREAD_MAX_REGISTERS 63 - -/* tunable parameters */ -# define CUDA_THREADS_BLOCK_WIDTH 16 -# define CUDA_KERNEL_MAX_REGISTERS 63 -# define CUDA_KERNEL_BRANCHED_MAX_REGISTERS 63 - -/* 3.7 */ -#elif __CUDA_ARCH__ == 370 -# define CUDA_MULTIPRESSOR_MAX_REGISTERS 65536 -# define CUDA_MULTIPROCESSOR_MAX_BLOCKS 16 -# define CUDA_BLOCK_MAX_THREADS 1024 -# define CUDA_THREAD_MAX_REGISTERS 255 - -/* tunable parameters */ -# define CUDA_THREADS_BLOCK_WIDTH 16 -# define CUDA_KERNEL_MAX_REGISTERS 63 -# define CUDA_KERNEL_BRANCHED_MAX_REGISTERS 63 - -/* 5.x, 6.x */ -#elif __CUDA_ARCH__ <= 699 -# define CUDA_MULTIPRESSOR_MAX_REGISTERS 65536 -# define CUDA_MULTIPROCESSOR_MAX_BLOCKS 32 -# define CUDA_BLOCK_MAX_THREADS 1024 -# define CUDA_THREAD_MAX_REGISTERS 255 - -/* tunable parameters */ -# define CUDA_THREADS_BLOCK_WIDTH 16 -/* CUDA 9.0 seems to cause slowdowns on high-end Pascal cards unless we increase the number of - * registers */ -# if __CUDACC_VER_MAJOR__ >= 9 && __CUDA_ARCH__ >= 600 -# define CUDA_KERNEL_MAX_REGISTERS 64 -# else -# define CUDA_KERNEL_MAX_REGISTERS 48 -# endif -# define CUDA_KERNEL_BRANCHED_MAX_REGISTERS 63 - -/* 7.x, 8.x */ -#elif __CUDA_ARCH__ <= 899 -# define CUDA_MULTIPRESSOR_MAX_REGISTERS 65536 -# define CUDA_MULTIPROCESSOR_MAX_BLOCKS 32 -# define CUDA_BLOCK_MAX_THREADS 1024 -# define CUDA_THREAD_MAX_REGISTERS 255 - -/* tunable parameters */ -# define CUDA_THREADS_BLOCK_WIDTH 16 -# define CUDA_KERNEL_MAX_REGISTERS 64 -# define CUDA_KERNEL_BRANCHED_MAX_REGISTERS 72 - -/* unknown architecture */ -#else -# error "Unknown or unsupported CUDA architecture, can't determine launch bounds" -#endif - -/* For split kernel using all registers seems fastest for now, but this - * is unlikely to be optimal once we resolve other bottlenecks. */ - -#define CUDA_KERNEL_SPLIT_MAX_REGISTERS CUDA_THREAD_MAX_REGISTERS - -/* Compute number of threads per block and minimum blocks per multiprocessor - * given the maximum number of registers per thread. */ - -#define CUDA_LAUNCH_BOUNDS(threads_block_width, thread_num_registers) \ - __launch_bounds__(threads_block_width *threads_block_width, \ - CUDA_MULTIPRESSOR_MAX_REGISTERS / \ - (threads_block_width * threads_block_width * thread_num_registers)) - -/* sanity checks */ - -#if CUDA_THREADS_BLOCK_WIDTH * CUDA_THREADS_BLOCK_WIDTH > CUDA_BLOCK_MAX_THREADS -# error "Maximum number of threads per block exceeded" -#endif - -#if CUDA_MULTIPRESSOR_MAX_REGISTERS / \ - (CUDA_THREADS_BLOCK_WIDTH * CUDA_THREADS_BLOCK_WIDTH * CUDA_KERNEL_MAX_REGISTERS) > \ - CUDA_MULTIPROCESSOR_MAX_BLOCKS -# error "Maximum number of blocks per multiprocessor exceeded" -#endif - -#if CUDA_KERNEL_MAX_REGISTERS > CUDA_THREAD_MAX_REGISTERS -# error "Maximum number of registers per thread exceeded" -#endif - -#if CUDA_KERNEL_BRANCHED_MAX_REGISTERS > CUDA_THREAD_MAX_REGISTERS -# error "Maximum number of registers per thread exceeded" -#endif diff --git a/intern/cycles/kernel/kernels/cuda/kernel_split.cu b/intern/cycles/kernel/kernels/cuda/kernel_split.cu deleted file mode 100644 index 95ad7599cf1..00000000000 --- a/intern/cycles/kernel/kernels/cuda/kernel_split.cu +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* CUDA split kernel entry points */ - -#ifdef __CUDA_ARCH__ - -#define __SPLIT_KERNEL__ - -#include "kernel/kernel_compat_cuda.h" -#include "kernel_config.h" - -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_data_init.h" -#include "kernel/split/kernel_path_init.h" -#include "kernel/split/kernel_scene_intersect.h" -#include "kernel/split/kernel_lamp_emission.h" -#include "kernel/split/kernel_do_volume.h" -#include "kernel/split/kernel_queue_enqueue.h" -#include "kernel/split/kernel_indirect_background.h" -#include "kernel/split/kernel_shader_setup.h" -#include "kernel/split/kernel_shader_sort.h" -#include "kernel/split/kernel_shader_eval.h" -#include "kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h" -#include "kernel/split/kernel_subsurface_scatter.h" -#include "kernel/split/kernel_direct_lighting.h" -#include "kernel/split/kernel_shadow_blocked_ao.h" -#include "kernel/split/kernel_shadow_blocked_dl.h" -#include "kernel/split/kernel_enqueue_inactive.h" -#include "kernel/split/kernel_next_iteration_setup.h" -#include "kernel/split/kernel_indirect_subsurface.h" -#include "kernel/split/kernel_buffer_update.h" -#include "kernel/split/kernel_adaptive_stopping.h" -#include "kernel/split/kernel_adaptive_filter_x.h" -#include "kernel/split/kernel_adaptive_filter_y.h" -#include "kernel/split/kernel_adaptive_adjust_samples.h" - -#include "kernel/kernel_film.h" - -/* kernels */ -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_state_buffer_size(uint num_threads, uint64_t *size) -{ - *size = split_data_buffer_size(NULL, num_threads); -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_path_trace_data_init( - ccl_global void *split_data_buffer, - int num_elements, - ccl_global char *ray_state, - int start_sample, - int end_sample, - int sx, int sy, int sw, int sh, int offset, int stride, - ccl_global int *Queue_index, - int queuesize, - ccl_global char *use_queues_flag, - ccl_global unsigned int *work_pool_wgs, - unsigned int num_samples, - ccl_global float *buffer) -{ - kernel_data_init(NULL, - NULL, - split_data_buffer, - num_elements, - ray_state, - start_sample, - end_sample, - sx, sy, sw, sh, offset, stride, - Queue_index, - queuesize, - use_queues_flag, - work_pool_wgs, - num_samples, - buffer); -} - -#define DEFINE_SPLIT_KERNEL_FUNCTION(name) \ - extern "C" __global__ void \ - CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_SPLIT_MAX_REGISTERS) \ - kernel_cuda_##name() \ - { \ - kernel_##name(NULL); \ - } - -#define DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(name, type) \ - extern "C" __global__ void \ - CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_SPLIT_MAX_REGISTERS) \ - kernel_cuda_##name() \ - { \ - ccl_local type locals; \ - kernel_##name(NULL, &locals); \ - } - -DEFINE_SPLIT_KERNEL_FUNCTION(path_init) -DEFINE_SPLIT_KERNEL_FUNCTION(scene_intersect) -DEFINE_SPLIT_KERNEL_FUNCTION(lamp_emission) -DEFINE_SPLIT_KERNEL_FUNCTION(do_volume) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(queue_enqueue, QueueEnqueueLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(indirect_background) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(shader_setup, uint) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(shader_sort, ShaderSortLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(shader_eval) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(holdout_emission_blurring_pathtermination_ao, BackgroundAOLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(subsurface_scatter) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(direct_lighting, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(shadow_blocked_ao) -DEFINE_SPLIT_KERNEL_FUNCTION(shadow_blocked_dl) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(enqueue_inactive, uint) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(next_iteration_setup, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(indirect_subsurface) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(buffer_update, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_stopping) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_filter_x) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_filter_y) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_adjust_samples) - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_convert_to_byte(uchar4 *rgba, float *buffer, float sample_scale, int sx, int sy, int sw, int sh, int offset, int stride) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - int y = sy + blockDim.y*blockIdx.y + threadIdx.y; - - if(x < sx + sw && y < sy + sh) - kernel_film_convert_to_byte(NULL, rgba, buffer, sample_scale, x, y, offset, stride); -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_convert_to_half_float(uchar4 *rgba, float *buffer, float sample_scale, int sx, int sy, int sw, int sh, int offset, int stride) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - int y = sy + blockDim.y*blockIdx.y + threadIdx.y; - - if(x < sx + sw && y < sy + sh) - kernel_film_convert_to_half_float(NULL, rgba, buffer, sample_scale, x, y, offset, stride); -} - -#endif - diff --git a/intern/cycles/kernel/kernels/opencl/filter.cl b/intern/cycles/kernel/kernels/opencl/filter.cl deleted file mode 100644 index 996bc27f71b..00000000000 --- a/intern/cycles/kernel/kernels/opencl/filter.cl +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* OpenCL kernel entry points */ - -#include "kernel/kernel_compat_opencl.h" - -#include "kernel/filter/filter_kernel.h" - -/* kernels */ - -__kernel void kernel_ocl_filter_divide_shadow(int sample, - CCL_FILTER_TILE_INFO, - ccl_global float *unfilteredA, - ccl_global float *unfilteredB, - ccl_global float *sampleVariance, - ccl_global float *sampleVarianceV, - ccl_global float *bufferVariance, - int4 prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int x = prefilter_rect.x + get_global_id(0); - int y = prefilter_rect.y + get_global_id(1); - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_divide_shadow(sample, - CCL_FILTER_TILE_INFO_ARG, - x, y, - unfilteredA, - unfilteredB, - sampleVariance, - sampleVarianceV, - bufferVariance, - prefilter_rect, - buffer_pass_stride, - buffer_denoising_offset); - } -} - -__kernel void kernel_ocl_filter_get_feature(int sample, - CCL_FILTER_TILE_INFO, - int m_offset, - int v_offset, - ccl_global float *mean, - ccl_global float *variance, - float scale, - int4 prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int x = prefilter_rect.x + get_global_id(0); - int y = prefilter_rect.y + get_global_id(1); - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_get_feature(sample, - CCL_FILTER_TILE_INFO_ARG, - m_offset, v_offset, - x, y, - mean, variance, - scale, - prefilter_rect, - buffer_pass_stride, - buffer_denoising_offset); - } -} - -__kernel void kernel_ocl_filter_write_feature(int sample, - int4 buffer_params, - int4 filter_area, - ccl_global float *from, - ccl_global float *buffer, - int out_offset, - int4 prefilter_rect) -{ - int x = get_global_id(0); - int y = get_global_id(1); - if(x < filter_area.z && y < filter_area.w) { - kernel_filter_write_feature(sample, - x + filter_area.x, - y + filter_area.y, - buffer_params, - from, - buffer, - out_offset, - prefilter_rect); - } -} - -__kernel void kernel_ocl_filter_detect_outliers(ccl_global float *image, - ccl_global float *variance, - ccl_global float *depth, - ccl_global float *output, - int4 prefilter_rect, - int pass_stride) -{ - int x = prefilter_rect.x + get_global_id(0); - int y = prefilter_rect.y + get_global_id(1); - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_detect_outliers(x, y, image, variance, depth, output, prefilter_rect, pass_stride); - } -} - -__kernel void kernel_ocl_filter_combine_halves(ccl_global float *mean, - ccl_global float *variance, - ccl_global float *a, - ccl_global float *b, - int4 prefilter_rect, - int r) -{ - int x = prefilter_rect.x + get_global_id(0); - int y = prefilter_rect.y + get_global_id(1); - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_combine_halves(x, y, mean, variance, a, b, prefilter_rect, r); - } -} - -__kernel void kernel_ocl_filter_construct_transform(const ccl_global float *ccl_restrict buffer, - CCL_FILTER_TILE_INFO, - ccl_global float *transform, - ccl_global int *rank, - int4 filter_area, - int4 rect, - int pass_stride, - int frame_stride, - char use_time, - int radius, - float pca_threshold) -{ - int x = get_global_id(0); - int y = get_global_id(1); - if(x < filter_area.z && y < filter_area.w) { - ccl_global int *l_rank = rank + y*filter_area.z + x; - ccl_global float *l_transform = transform + y*filter_area.z + x; - kernel_filter_construct_transform(buffer, - CCL_FILTER_TILE_INFO_ARG, - x + filter_area.x, y + filter_area.y, - rect, - pass_stride, frame_stride, - use_time, - l_transform, l_rank, - radius, pca_threshold, - filter_area.z*filter_area.w, - get_local_id(1)*get_local_size(0) + get_local_id(0)); - } -} - -__kernel void kernel_ocl_filter_nlm_calc_difference(const ccl_global float *ccl_restrict weight_image, - const ccl_global float *ccl_restrict variance_image, - const ccl_global float *ccl_restrict scale_image, - ccl_global float *difference_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int channel_offset, - int frame_offset, - float a, - float k_2) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_calc_difference(co.x, co.y, co.z, co.w, - weight_image, - variance_image, - scale_image, - difference_image + ofs, - rect, stride, - channel_offset, - frame_offset, - a, k_2); - } -} - -__kernel void kernel_ocl_filter_nlm_blur(const ccl_global float *ccl_restrict difference_image, - ccl_global float *out_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_blur(co.x, co.y, - difference_image + ofs, - out_image + ofs, - rect, stride, f); - } -} - -__kernel void kernel_ocl_filter_nlm_calc_weight(const ccl_global float *ccl_restrict difference_image, - ccl_global float *out_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_calc_weight(co.x, co.y, - difference_image + ofs, - out_image + ofs, - rect, stride, f); - } -} - -__kernel void kernel_ocl_filter_nlm_update_output(const ccl_global float *ccl_restrict difference_image, - const ccl_global float *ccl_restrict image, - ccl_global float *out_image, - ccl_global float *accum_image, - int w, - int h, - int stride, - int pass_stride, - int channel_offset, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_update_output(co.x, co.y, co.z, co.w, - difference_image + ofs, - image, - out_image, - accum_image, - rect, - channel_offset, - stride, f); - } -} - -__kernel void kernel_ocl_filter_nlm_normalize(ccl_global float *out_image, - const ccl_global float *ccl_restrict accum_image, - int w, - int h, - int stride) -{ - int x = get_global_id(0); - int y = get_global_id(1); - if(x < w && y < h) { - kernel_filter_nlm_normalize(x, y, out_image, accum_image, stride); - } -} - -__kernel void kernel_ocl_filter_nlm_construct_gramian(int t, - const ccl_global float *ccl_restrict difference_image, - const ccl_global float *ccl_restrict buffer, - const ccl_global float *ccl_restrict transform, - ccl_global int *rank, - ccl_global float *XtWX, - ccl_global float3 *XtWY, - int4 filter_window, - int w, - int h, - int stride, - int pass_stride, - int r, - int f, - int frame_offset, - char use_time) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords_window(w, h, r, pass_stride, &rect, &co, &ofs, filter_window)) { - kernel_filter_nlm_construct_gramian(co.x, co.y, - co.z, co.w, - t, - difference_image + ofs, - buffer, - transform, rank, - XtWX, XtWY, - rect, filter_window, - stride, f, - pass_stride, - frame_offset, - use_time, - get_local_id(1)*get_local_size(0) + get_local_id(0)); - } -} - -__kernel void kernel_ocl_filter_finalize(ccl_global float *buffer, - ccl_global int *rank, - ccl_global float *XtWX, - ccl_global float3 *XtWY, - int4 filter_area, - int4 buffer_params, - int sample) -{ - int x = get_global_id(0); - int y = get_global_id(1); - if(x < filter_area.z && y < filter_area.w) { - int storage_ofs = y*filter_area.z+x; - rank += storage_ofs; - XtWX += storage_ofs; - XtWY += storage_ofs; - kernel_filter_finalize(x, y, buffer, rank, - filter_area.z*filter_area.w, - XtWX, XtWY, - buffer_params, sample); - } -} diff --git a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_adjust_samples.cl b/intern/cycles/kernel/kernels/opencl/kernel_adaptive_adjust_samples.cl deleted file mode 100644 index ebdb99d4730..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_adjust_samples.cl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_adaptive_adjust_samples.h" - -#define KERNEL_NAME adaptive_adjust_samples -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME diff --git a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_x.cl b/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_x.cl deleted file mode 100644 index 76d82d4184e..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_x.cl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_adaptive_filter_x.h" - -#define KERNEL_NAME adaptive_filter_x -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME diff --git a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_y.cl b/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_y.cl deleted file mode 100644 index 1e6d15ba0f2..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_y.cl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_adaptive_filter_y.h" - -#define KERNEL_NAME adaptive_filter_y -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME diff --git a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_stopping.cl b/intern/cycles/kernel/kernels/opencl/kernel_adaptive_stopping.cl deleted file mode 100644 index 51de0059667..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_stopping.cl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_adaptive_stopping.h" - -#define KERNEL_NAME adaptive_stopping -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME diff --git a/intern/cycles/kernel/kernels/opencl/kernel_background.cl b/intern/cycles/kernel/kernels/opencl/kernel_background.cl deleted file mode 100644 index 0e600676e82..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_background.cl +++ /dev/null @@ -1,35 +0,0 @@ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/kernel_math.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" -#include "kernel/kernels/opencl/kernel_opencl_image.h" - -#include "kernel/kernel_path.h" -#include "kernel/kernel_path_branched.h" - -#include "kernel/kernel_bake.h" - -__kernel void kernel_ocl_background( - ccl_constant KernelData *data, - ccl_global uint4 *input, - ccl_global float4 *output, - - KERNEL_BUFFER_PARAMS, - - int type, int sx, int sw, int offset, int sample) -{ - KernelGlobals kglobals, *kg = &kglobals; - - kg->data = data; - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); - - int x = sx + ccl_global_id(0); - - if(x < sx + sw) { - kernel_background_evaluate(kg, input, output, x); - } -} diff --git a/intern/cycles/kernel/kernels/opencl/kernel_bake.cl b/intern/cycles/kernel/kernels/opencl/kernel_bake.cl deleted file mode 100644 index 7b81e387467..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_bake.cl +++ /dev/null @@ -1,36 +0,0 @@ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/kernel_math.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" -#include "kernel/kernels/opencl/kernel_opencl_image.h" - -#include "kernel/kernel_path.h" -#include "kernel/kernel_path_branched.h" - -#include "kernel/kernel_bake.h" - -__kernel void kernel_ocl_bake( - ccl_constant KernelData *data, - ccl_global float *buffer, - - KERNEL_BUFFER_PARAMS, - - int sx, int sy, int sw, int sh, int offset, int stride, int sample) -{ - KernelGlobals kglobals, *kg = &kglobals; - - kg->data = data; - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); - - int x = sx + ccl_global_id(0); - int y = sy + ccl_global_id(1); - - if(x < sx + sw && y < sy + sh) { -#ifndef __NO_BAKING__ - kernel_bake_evaluate(kg, buffer, sample, x, y, offset, stride); -#endif - } -} diff --git a/intern/cycles/kernel/kernels/opencl/kernel_base.cl b/intern/cycles/kernel/kernels/opencl/kernel_base.cl deleted file mode 100644 index 1c2d89e8a92..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_base.cl +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* OpenCL base kernels entry points */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" - -#include "kernel/kernel_film.h" - - -__kernel void kernel_ocl_convert_to_byte( - ccl_constant KernelData *data, - ccl_global uchar4 *rgba, - ccl_global float *buffer, - - KERNEL_BUFFER_PARAMS, - - float sample_scale, - int sx, int sy, int sw, int sh, int offset, int stride) -{ - KernelGlobals kglobals, *kg = &kglobals; - - kg->data = data; - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); - - int x = sx + ccl_global_id(0); - int y = sy + ccl_global_id(1); - - if(x < sx + sw && y < sy + sh) - kernel_film_convert_to_byte(kg, rgba, buffer, sample_scale, x, y, offset, stride); -} - -__kernel void kernel_ocl_convert_to_half_float( - ccl_constant KernelData *data, - ccl_global uchar4 *rgba, - ccl_global float *buffer, - - KERNEL_BUFFER_PARAMS, - - float sample_scale, - int sx, int sy, int sw, int sh, int offset, int stride) -{ - KernelGlobals kglobals, *kg = &kglobals; - - kg->data = data; - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); - - int x = sx + ccl_global_id(0); - int y = sy + ccl_global_id(1); - - if(x < sx + sw && y < sy + sh) - kernel_film_convert_to_half_float(kg, rgba, buffer, sample_scale, x, y, offset, stride); -} - -__kernel void kernel_ocl_zero_buffer(ccl_global float4 *buffer, uint64_t size, uint64_t offset) -{ - size_t i = ccl_global_id(0) + ccl_global_id(1) * ccl_global_size(0); - - if(i < size / sizeof(float4)) { - buffer[i+offset/sizeof(float4)] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } - else if(i == size / sizeof(float4)) { - ccl_global uchar *b = (ccl_global uchar*)&buffer[i+offset/sizeof(float4)]; - - for(i = 0; i < size % sizeof(float4); i++) { - *(b++) = 0; - } - } -} diff --git a/intern/cycles/kernel/kernels/opencl/kernel_data_init.cl b/intern/cycles/kernel/kernels/opencl/kernel_data_init.cl deleted file mode 100644 index 7125348a49f..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_data_init.cl +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_data_init.h" - -__kernel void kernel_ocl_path_trace_data_init( - ccl_global char *kg, - ccl_constant KernelData *data, - ccl_global void *split_data_buffer, - int num_elements, - ccl_global char *ray_state, - KERNEL_BUFFER_PARAMS, - int start_sample, - int end_sample, - int sx, int sy, int sw, int sh, int offset, int stride, - ccl_global int *Queue_index, /* Tracks the number of elements in queues */ - int queuesize, /* size (capacity) of the queue */ - ccl_global char *use_queues_flag, /* flag to decide if scene-intersect kernel should use queues to fetch ray index */ - ccl_global unsigned int *work_pool_wgs, /* Work pool for each work group */ - unsigned int num_samples, /* Total number of samples per pixel */ - ccl_global float *buffer) -{ - kernel_data_init((KernelGlobals*)kg, - data, - split_data_buffer, - num_elements, - ray_state, - KERNEL_BUFFER_ARGS, - start_sample, - end_sample, - sx, sy, sw, sh, offset, stride, - Queue_index, - queuesize, - use_queues_flag, - work_pool_wgs, - num_samples, - buffer); -} diff --git a/intern/cycles/kernel/kernels/opencl/kernel_displace.cl b/intern/cycles/kernel/kernels/opencl/kernel_displace.cl deleted file mode 100644 index 76cc36971f5..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_displace.cl +++ /dev/null @@ -1,36 +0,0 @@ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/kernel_math.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" -#include "kernel/kernels/opencl/kernel_opencl_image.h" - -#include "kernel/kernel_path.h" -#include "kernel/kernel_path_branched.h" - -#include "kernel/kernel_bake.h" - -__kernel void kernel_ocl_displace( - ccl_constant KernelData *data, - ccl_global uint4 *input, - ccl_global float4 *output, - - KERNEL_BUFFER_PARAMS, - - int type, int sx, int sw, int offset, int sample) -{ - KernelGlobals kglobals, *kg = &kglobals; - - kg->data = data; - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); - - int x = sx + ccl_global_id(0); - - if(x < sx + sw) { - kernel_displace_evaluate(kg, input, output, x); - } -} - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_next_iteration_setup.cl b/intern/cycles/kernel/kernels/opencl/kernel_next_iteration_setup.cl deleted file mode 100644 index 8b1332bf013..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_next_iteration_setup.cl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_next_iteration_setup.h" - -#define KERNEL_NAME next_iteration_setup -#define LOCALS_TYPE unsigned int -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h b/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h deleted file mode 100644 index bb6b8a40e8e..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright 2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_NANOVDB -/* Data type to replace `double` used in the NanoVDB headers. Cycles don't need doubles, and is - * safer and more portable to never use double datatype on GPU. - * Use a special structure, so that the following is true: - * - No unnoticed implicit cast or mathematical operations used on scalar 64bit type - * (which rules out trick like using `uint64_t` as a drop-in replacement for double). - * - Padding rules are matching exactly `double` - * (which rules out array of `uint8_t`). */ -typedef struct ccl_vdb_double_t { - uint64_t i; -} ccl_vdb_double_t; - -# define double ccl_vdb_double_t -# include "nanovdb/CNanoVDB.h" -# undef double -#endif - -/* For OpenCL we do manual lookup and interpolation. */ - -ccl_device_inline ccl_global TextureInfo *kernel_tex_info(KernelGlobals *kg, uint id) -{ - const uint tex_offset = id -#define KERNEL_TEX(type, name) +1 -#include "kernel/kernel_textures.h" - ; - - return &((ccl_global TextureInfo *)kg->buffers[0])[tex_offset]; -} - -#define tex_fetch(type, info, index) \ - ((ccl_global type *)(kg->buffers[info->cl_buffer] + info->data))[(index)] - -ccl_device_inline int svm_image_texture_wrap_periodic(int x, int width) -{ - x %= width; - if (x < 0) - x += width; - return x; -} - -ccl_device_inline int svm_image_texture_wrap_clamp(int x, int width) -{ - return clamp(x, 0, width - 1); -} - -ccl_device_inline float4 svm_image_texture_read( - KernelGlobals *kg, const ccl_global TextureInfo *info, void *acc, int x, int y, int z) -{ - const int data_offset = x + info->width * y + info->width * info->height * z; - const int texture_type = info->data_type; - - /* Float4 */ - if (texture_type == IMAGE_DATA_TYPE_FLOAT4) { - return tex_fetch(float4, info, data_offset); - } - /* Byte4 */ - else if (texture_type == IMAGE_DATA_TYPE_BYTE4) { - uchar4 r = tex_fetch(uchar4, info, data_offset); - float f = 1.0f / 255.0f; - return make_float4(r.x * f, r.y * f, r.z * f, r.w * f); - } - /* Ushort4 */ - else if (texture_type == IMAGE_DATA_TYPE_USHORT4) { - ushort4 r = tex_fetch(ushort4, info, data_offset); - float f = 1.0f / 65535.f; - return make_float4(r.x * f, r.y * f, r.z * f, r.w * f); - } - /* Float */ - else if (texture_type == IMAGE_DATA_TYPE_FLOAT) { - float f = tex_fetch(float, info, data_offset); - return make_float4(f, f, f, 1.0f); - } - /* UShort */ - else if (texture_type == IMAGE_DATA_TYPE_USHORT) { - ushort r = tex_fetch(ushort, info, data_offset); - float f = r * (1.0f / 65535.0f); - return make_float4(f, f, f, 1.0f); - } -#ifdef WITH_NANOVDB - /* NanoVDB Float */ - else if (texture_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT) { - cnanovdb_coord coord; - coord.mVec[0] = x; - coord.mVec[1] = y; - coord.mVec[2] = z; - float f = cnanovdb_readaccessor_getValueF((cnanovdb_readaccessor *)acc, &coord); - return make_float4(f, f, f, 1.0f); - } - /* NanoVDB Float3 */ - else if (texture_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { - cnanovdb_coord coord; - coord.mVec[0] = x; - coord.mVec[1] = y; - coord.mVec[2] = z; - cnanovdb_Vec3F f = cnanovdb_readaccessor_getValueF3((cnanovdb_readaccessor *)acc, &coord); - return make_float4(f.mVec[0], f.mVec[1], f.mVec[2], 1.0f); - } -#endif -#ifdef __KERNEL_CL_KHR_FP16__ - /* Half and Half4 are optional in OpenCL */ - else if (texture_type == IMAGE_DATA_TYPE_HALF) { - float f = tex_fetch(half, info, data_offset); - return make_float4(f, f, f, 1.0f); - } - else if (texture_type == IMAGE_DATA_TYPE_HALF4) { - half4 r = tex_fetch(half4, info, data_offset); - return make_float4(r.x, r.y, r.z, r.w); - } -#endif - /* Byte */ - else { - uchar r = tex_fetch(uchar, info, data_offset); - float f = r * (1.0f / 255.0f); - return make_float4(f, f, f, 1.0f); - } -} - -ccl_device_inline float4 -svm_image_texture_read_2d(KernelGlobals *kg, int id, void *acc, int x, int y) -{ - const ccl_global TextureInfo *info = kernel_tex_info(kg, id); - -#ifdef WITH_NANOVDB - if (info->data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && - info->data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { -#endif - /* Wrap */ - if (info->extension == EXTENSION_REPEAT) { - x = svm_image_texture_wrap_periodic(x, info->width); - y = svm_image_texture_wrap_periodic(y, info->height); - } - else { - x = svm_image_texture_wrap_clamp(x, info->width); - y = svm_image_texture_wrap_clamp(y, info->height); - } -#ifdef WITH_NANOVDB - } -#endif - - return svm_image_texture_read(kg, info, acc, x, y, 0); -} - -ccl_device_inline float4 -svm_image_texture_read_3d(KernelGlobals *kg, int id, void *acc, int x, int y, int z) -{ - const ccl_global TextureInfo *info = kernel_tex_info(kg, id); - -#ifdef WITH_NANOVDB - if (info->data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && - info->data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { -#endif - /* Wrap */ - if (info->extension == EXTENSION_REPEAT) { - x = svm_image_texture_wrap_periodic(x, info->width); - y = svm_image_texture_wrap_periodic(y, info->height); - z = svm_image_texture_wrap_periodic(z, info->depth); - } - else { - x = svm_image_texture_wrap_clamp(x, info->width); - y = svm_image_texture_wrap_clamp(y, info->height); - z = svm_image_texture_wrap_clamp(z, info->depth); - } -#ifdef WITH_NANOVDB - } -#endif - - return svm_image_texture_read(kg, info, acc, x, y, z); -} - -ccl_device_inline float svm_image_texture_frac(float x, int *ix) -{ - int i = float_to_int(x) - ((x < 0.0f) ? 1 : 0); - *ix = i; - return x - (float)i; -} - -#define SET_CUBIC_SPLINE_WEIGHTS(u, t) \ - { \ - u[0] = (((-1.0f / 6.0f) * t + 0.5f) * t - 0.5f) * t + (1.0f / 6.0f); \ - u[1] = ((0.5f * t - 1.0f) * t) * t + (2.0f / 3.0f); \ - u[2] = ((-0.5f * t + 0.5f) * t + 0.5f) * t + (1.0f / 6.0f); \ - u[3] = (1.0f / 6.0f) * t * t * t; \ - } \ - (void)0 - -ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y) -{ - const ccl_global TextureInfo *info = kernel_tex_info(kg, id); - - if (info->extension == EXTENSION_CLIP) { - if (x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } - } - - if (info->interpolation == INTERPOLATION_CLOSEST) { - /* Closest interpolation. */ - int ix, iy; - svm_image_texture_frac(x * info->width, &ix); - svm_image_texture_frac(y * info->height, &iy); - - return svm_image_texture_read_2d(kg, id, NULL, ix, iy); - } - else if (info->interpolation == INTERPOLATION_LINEAR) { - /* Bilinear interpolation. */ - int ix, iy; - float tx = svm_image_texture_frac(x * info->width - 0.5f, &ix); - float ty = svm_image_texture_frac(y * info->height - 0.5f, &iy); - - float4 r; - r = (1.0f - ty) * (1.0f - tx) * svm_image_texture_read_2d(kg, id, NULL, ix, iy); - r += (1.0f - ty) * tx * svm_image_texture_read_2d(kg, id, NULL, ix + 1, iy); - r += ty * (1.0f - tx) * svm_image_texture_read_2d(kg, id, NULL, ix, iy + 1); - r += ty * tx * svm_image_texture_read_2d(kg, id, NULL, ix + 1, iy + 1); - return r; - } - else { - /* Bicubic interpolation. */ - int ix, iy; - float tx = svm_image_texture_frac(x * info->width - 0.5f, &ix); - float ty = svm_image_texture_frac(y * info->height - 0.5f, &iy); - - float u[4], v[4]; - SET_CUBIC_SPLINE_WEIGHTS(u, tx); - SET_CUBIC_SPLINE_WEIGHTS(v, ty); - - float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - float weight = u[x] * v[y]; - r += weight * svm_image_texture_read_2d(kg, id, NULL, ix + x - 1, iy + y - 1); - } - } - return r; - } -} - -ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, int id, float3 P, int interp) -{ - const ccl_global TextureInfo *info = kernel_tex_info(kg, id); - - if (info->use_transform_3d) { - Transform tfm = info->transform_3d; - P = transform_point(&tfm, P); - } - - float x = P.x; - float y = P.y; - float z = P.z; - - uint interpolation = (interp == INTERPOLATION_NONE) ? info->interpolation : interp; - -#ifdef WITH_NANOVDB - cnanovdb_readaccessor acc; - if (info->data_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT || - info->data_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { - ccl_global cnanovdb_griddata *grid = - (ccl_global cnanovdb_griddata *)(kg->buffers[info->cl_buffer] + info->data); - cnanovdb_readaccessor_init(&acc, cnanovdb_treedata_rootF(cnanovdb_griddata_tree(grid))); - } - else { - if (info->extension == EXTENSION_CLIP) { - if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } - } - - x *= info->width; - y *= info->height; - z *= info->depth; - } -# define NANOVDB_ACCESS_POINTER &acc -#else -# define NANOVDB_ACCESS_POINTER NULL -#endif - - if (interpolation == INTERPOLATION_CLOSEST) { - /* Closest interpolation. */ - int ix, iy, iz; - svm_image_texture_frac(x, &ix); - svm_image_texture_frac(y, &iy); - svm_image_texture_frac(z, &iz); - - return svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix, iy, iz); - } - else if (interpolation == INTERPOLATION_LINEAR) { - /* Trilinear interpolation. */ - int ix, iy, iz; - float tx = svm_image_texture_frac(x - 0.5f, &ix); - float ty = svm_image_texture_frac(y - 0.5f, &iy); - float tz = svm_image_texture_frac(z - 0.5f, &iz); - - float4 r; - r = (1.0f - tz) * (1.0f - ty) * (1.0f - tx) * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix, iy, iz); - r += (1.0f - tz) * (1.0f - ty) * tx * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix + 1, iy, iz); - r += (1.0f - tz) * ty * (1.0f - tx) * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix, iy + 1, iz); - r += (1.0f - tz) * ty * tx * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix + 1, iy + 1, iz); - - r += tz * (1.0f - ty) * (1.0f - tx) * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix, iy, iz + 1); - r += tz * (1.0f - ty) * tx * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix + 1, iy, iz + 1); - r += tz * ty * (1.0f - tx) * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix, iy + 1, iz + 1); - r += tz * ty * tx * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix + 1, iy + 1, iz + 1); - return r; - } - else { - /* Tricubic interpolation. */ - int ix, iy, iz; - float tx = svm_image_texture_frac(x - 0.5f, &ix); - float ty = svm_image_texture_frac(y - 0.5f, &iy); - float tz = svm_image_texture_frac(z - 0.5f, &iz); - - float u[4], v[4], w[4]; - SET_CUBIC_SPLINE_WEIGHTS(u, tx); - SET_CUBIC_SPLINE_WEIGHTS(v, ty); - SET_CUBIC_SPLINE_WEIGHTS(w, tz); - - float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - - for (int z = 0; z < 4; z++) { - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - float weight = u[x] * v[y] * w[z]; - r += weight * svm_image_texture_read_3d( - kg, id, NANOVDB_ACCESS_POINTER, ix + x - 1, iy + y - 1, iz + z - 1); - } - } - } - return r; - } -#undef NANOVDB_ACCESS_POINTER -} - -#undef SET_CUBIC_SPLINE_WEIGHTS diff --git a/intern/cycles/kernel/kernels/opencl/kernel_queue_enqueue.cl b/intern/cycles/kernel/kernels/opencl/kernel_queue_enqueue.cl deleted file mode 100644 index 68ee6f1d536..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_queue_enqueue.cl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_queue_enqueue.h" - -#define KERNEL_NAME queue_enqueue -#define LOCALS_TYPE QueueEnqueueLocals -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_scene_intersect.cl b/intern/cycles/kernel/kernels/opencl/kernel_scene_intersect.cl deleted file mode 100644 index 10d09377ba9..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_scene_intersect.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_scene_intersect.h" - -#define KERNEL_NAME scene_intersect -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_shader_eval.cl b/intern/cycles/kernel/kernels/opencl/kernel_shader_eval.cl deleted file mode 100644 index 40eaa561863..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_shader_eval.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_shader_eval.h" - -#define KERNEL_NAME shader_eval -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_shader_setup.cl b/intern/cycles/kernel/kernels/opencl/kernel_shader_setup.cl deleted file mode 100644 index 8c36100f762..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_shader_setup.cl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_shader_setup.h" - -#define KERNEL_NAME shader_setup -#define LOCALS_TYPE unsigned int -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_shader_sort.cl b/intern/cycles/kernel/kernels/opencl/kernel_shader_sort.cl deleted file mode 100644 index bcacaa4a054..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_shader_sort.cl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_shader_sort.h" - -__attribute__((reqd_work_group_size(64, 1, 1))) -#define KERNEL_NAME shader_sort -#define LOCALS_TYPE ShaderSortLocals -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_ao.cl b/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_ao.cl deleted file mode 100644 index 8de250a375c..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_ao.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_shadow_blocked_ao.h" - -#define KERNEL_NAME shadow_blocked_ao -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_dl.cl b/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_dl.cl deleted file mode 100644 index 29da77022ed..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_dl.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_shadow_blocked_dl.h" - -#define KERNEL_NAME shadow_blocked_dl -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl b/intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl deleted file mode 100644 index c3b7b09460a..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" // PRECOMPILED -#include "kernel/split/kernel_split_common.h" // PRECOMPILED - -#include "kernel/kernels/opencl/kernel_data_init.cl" -#include "kernel/kernels/opencl/kernel_path_init.cl" -#include "kernel/kernels/opencl/kernel_state_buffer_size.cl" -#include "kernel/kernels/opencl/kernel_scene_intersect.cl" -#include "kernel/kernels/opencl/kernel_queue_enqueue.cl" -#include "kernel/kernels/opencl/kernel_shader_setup.cl" -#include "kernel/kernels/opencl/kernel_shader_sort.cl" -#include "kernel/kernels/opencl/kernel_enqueue_inactive.cl" -#include "kernel/kernels/opencl/kernel_next_iteration_setup.cl" -#include "kernel/kernels/opencl/kernel_indirect_subsurface.cl" -#include "kernel/kernels/opencl/kernel_buffer_update.cl" -#include "kernel/kernels/opencl/kernel_adaptive_stopping.cl" -#include "kernel/kernels/opencl/kernel_adaptive_filter_x.cl" -#include "kernel/kernels/opencl/kernel_adaptive_filter_y.cl" -#include "kernel/kernels/opencl/kernel_adaptive_adjust_samples.cl" diff --git a/intern/cycles/kernel/kernels/opencl/kernel_split_function.h b/intern/cycles/kernel/kernels/opencl/kernel_split_function.h deleted file mode 100644 index e123b4cd6ec..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_split_function.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define KERNEL_NAME_JOIN(a, b) a##_##b -#define KERNEL_NAME_EVAL(a, b) KERNEL_NAME_JOIN(a, b) - -__kernel void KERNEL_NAME_EVAL(kernel_ocl_path_trace, - KERNEL_NAME)(ccl_global char *kg_global, - ccl_constant KernelData *data, - - ccl_global void *split_data_buffer, - ccl_global char *ray_state, - - KERNEL_BUFFER_PARAMS, - - ccl_global int *queue_index, - ccl_global char *use_queues_flag, - ccl_global unsigned int *work_pools, - ccl_global float *buffer) -{ -#ifdef LOCALS_TYPE - ccl_local LOCALS_TYPE locals; -#endif - - KernelGlobals *kg = (KernelGlobals *)kg_global; - - if (ccl_local_id(0) + ccl_local_id(1) == 0) { - kg->data = data; - - kernel_split_params.queue_index = queue_index; - kernel_split_params.use_queues_flag = use_queues_flag; - kernel_split_params.work_pools = work_pools; - kernel_split_params.tile.buffer = buffer; - - split_data_init(kg, - &kernel_split_state, - ccl_global_size(0) * ccl_global_size(1), - split_data_buffer, - ray_state); - } - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - - KERNEL_NAME_EVAL(kernel, KERNEL_NAME) - (kg -#ifdef LOCALS_TYPE - , - &locals -#endif - ); -} - -#undef KERNEL_NAME_JOIN -#undef KERNEL_NAME_EVAL diff --git a/intern/cycles/kernel/kernels/opencl/kernel_subsurface_scatter.cl b/intern/cycles/kernel/kernels/opencl/kernel_subsurface_scatter.cl deleted file mode 100644 index 2b3be38df84..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_subsurface_scatter.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_subsurface_scatter.h" - -#define KERNEL_NAME subsurface_scatter -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/osl/background.cpp b/intern/cycles/kernel/osl/background.cpp index 3f9de5ab33d..8e497986dcc 100644 --- a/intern/cycles/kernel/osl/background.cpp +++ b/intern/cycles/kernel/osl/background.cpp @@ -37,7 +37,7 @@ #include "kernel/osl/osl_closures.h" // clang-format off -#include "kernel/kernel_compat_cpu.h" +#include "kernel/device/cpu/compat.h" #include "kernel/closure/alloc.h" #include "kernel/closure/emissive.h" // clang-format on diff --git a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp index 76a2e41abfa..a2f9d3f759a 100644 --- a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp @@ -34,7 +34,7 @@ #include -#include "kernel/kernel_compat_cpu.h" +#include "kernel/device/cpu/compat.h" #include "kernel/osl/osl_closures.h" // clang-format off diff --git a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp index b78dc8a3a67..812c3b6e71b 100644 --- a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp @@ -34,7 +34,7 @@ #include -#include "kernel/kernel_compat_cpu.h" +#include "kernel/device/cpu/compat.h" #include "kernel/osl/osl_closures.h" // clang-format off diff --git a/intern/cycles/kernel/osl/emissive.cpp b/intern/cycles/kernel/osl/emissive.cpp index d656723bac2..80dfbee879e 100644 --- a/intern/cycles/kernel/osl/emissive.cpp +++ b/intern/cycles/kernel/osl/emissive.cpp @@ -37,7 +37,7 @@ #include "kernel/osl/osl_closures.h" // clang-format off -#include "kernel/kernel_compat_cpu.h" +#include "kernel/device/cpu/compat.h" #include "kernel/kernel_types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/emissive.h" diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/osl_bssrdf.cpp index c5ca8616fbd..5d968ed85e0 100644 --- a/intern/cycles/kernel/osl/osl_bssrdf.cpp +++ b/intern/cycles/kernel/osl/osl_bssrdf.cpp @@ -32,7 +32,7 @@ #include -#include "kernel/kernel_compat_cpu.h" +#include "kernel/device/cpu/compat.h" #include "kernel/osl/osl_closures.h" // clang-format off @@ -50,45 +50,30 @@ CCL_NAMESPACE_BEGIN using namespace OSL; -static ustring u_cubic("cubic"); -static ustring u_gaussian("gaussian"); -static ustring u_burley("burley"); -static ustring u_principled("principled"); +static ustring u_random_walk_fixed_radius("random_walk_fixed_radius"); static ustring u_random_walk("random_walk"); -static ustring u_principled_random_walk("principled_random_walk"); class CBSSRDFClosure : public CClosurePrimitive { public: Bssrdf params; + float ior; ustring method; CBSSRDFClosure() { - params.texture_blur = 0.0f; - params.sharpness = 0.0f; - params.roughness = 0.0f; + params.roughness = FLT_MAX; + params.anisotropy = 1.0f; + ior = 1.4f; } void setup(ShaderData *sd, int path_flag, float3 weight) { - if (method == u_cubic) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_CUBIC_ID); - } - else if (method == u_gaussian) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_GAUSSIAN_ID); - } - else if (method == u_burley) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_BURLEY_ID); - } - else if (method == u_principled) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_PRINCIPLED_ID); + if (method == u_random_walk_fixed_radius) { + alloc(sd, path_flag, weight, CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); } else if (method == u_random_walk) { alloc(sd, path_flag, weight, CLOSURE_BSSRDF_RANDOM_WALK_ID); } - else if (method == u_principled_random_walk) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID); - } } void alloc(ShaderData *sd, int path_flag, float3 weight, ClosureType type) @@ -106,11 +91,10 @@ class CBSSRDFClosure : public CClosurePrimitive { /* create one closure per color channel */ bssrdf->radius = params.radius; bssrdf->albedo = params.albedo; - bssrdf->texture_blur = params.texture_blur; - bssrdf->sharpness = params.sharpness; bssrdf->N = params.N; bssrdf->roughness = params.roughness; - sd->flag |= bssrdf_setup(sd, bssrdf, (ClosureType)type); + bssrdf->anisotropy = clamp(params.anisotropy, 0.0f, 0.9f); + sd->flag |= bssrdf_setup(sd, bssrdf, (ClosureType)type, clamp(ior, 1.01f, 3.8f)); } } }; @@ -122,9 +106,9 @@ ClosureParam *closure_bssrdf_params() CLOSURE_FLOAT3_PARAM(CBSSRDFClosure, params.N), CLOSURE_FLOAT3_PARAM(CBSSRDFClosure, params.radius), CLOSURE_FLOAT3_PARAM(CBSSRDFClosure, params.albedo), - CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, params.texture_blur, "texture_blur"), - CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, params.sharpness, "sharpness"), CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, params.roughness, "roughness"), + CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, ior, "ior"), + CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, params.anisotropy, "anisotropy"), CLOSURE_STRING_KEYPARAM(CBSSRDFClosure, label, "label"), CLOSURE_FINISH_PARAM(CBSSRDFClosure)}; return params; diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp index 7ee467a46dd..e814fcca246 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/osl_closures.cpp @@ -40,10 +40,10 @@ #include "util/util_param.h" // clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + #include "kernel/kernel_types.h" -#include "kernel/kernel_compat_cpu.h" -#include "kernel/split/kernel_split_data_types.h" -#include "kernel/kernel_globals.h" #include "kernel/kernel_montecarlo.h" #include "kernel/kernel_random.h" @@ -500,7 +500,7 @@ bool CBSDFClosure::skip(const ShaderData *sd, int path_flag, int scattering) { /* caustic options */ if ((scattering & LABEL_GLOSSY) && (path_flag & PATH_RAY_DIFFUSE)) { - KernelGlobals *kg = sd->osl_globals; + const KernelGlobals *kg = sd->osl_globals; if ((!kernel_data.integrator.caustics_reflective && (scattering & LABEL_REFLECT)) || (!kernel_data.integrator.caustics_refractive && (scattering & LABEL_TRANSMIT))) { diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 2b7c21d0bc4..396f42080e4 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -40,22 +40,22 @@ #include "util/util_string.h" // clang-format off -#include "kernel/kernel_compat_cpu.h" -#include "kernel/split/kernel_split_data_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" -#include "kernel/kernel_random.h" -#include "kernel/kernel_write_passes.h" -#include "kernel/kernel_projection.h" +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" +#include "kernel/device/cpu/image.h" + #include "kernel/kernel_differential.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_camera.h" -#include "kernel/kernels/cpu/kernel_cpu_image.h" + +#include "kernel/integrator/integrator_state.h" +#include "kernel/integrator/integrator_state_flow.h" + #include "kernel/geom/geom.h" #include "kernel/bvh/bvh.h" +#include "kernel/kernel_color.h" +#include "kernel/kernel_camera.h" +#include "kernel/kernel_path_state.h" #include "kernel/kernel_projection.h" -#include "kernel/kernel_accumulate.h" #include "kernel/kernel_shader.h" // clang-format on @@ -147,7 +147,7 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; - KernelGlobals *kg = sd->osl_globals; + const KernelGlobals *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { @@ -155,18 +155,19 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, Transform tfm; if (time == sd->time) - tfm = sd->ob_tfm; + tfm = object_get_transform(kg, sd); else tfm = object_fetch_transform_motion_test(kg, object, time, NULL); #else - Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); + const Transform tfm = object_get_transform(kg, sd); #endif copy_matrix(result, tfm); return true; } else if (sd->type == PRIMITIVE_LAMP) { - copy_matrix(result, sd->ob_tfm); + const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false); + copy_matrix(result, tfm); return true; } @@ -184,7 +185,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; - KernelGlobals *kg = sd->osl_globals; + const KernelGlobals *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { @@ -192,18 +193,19 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, Transform itfm; if (time == sd->time) - itfm = sd->ob_itfm; + itfm = object_get_inverse_transform(kg, sd); else object_fetch_transform_motion_test(kg, object, time, &itfm); #else - Transform itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); + const Transform itfm = object_get_inverse_transform(kg, sd); #endif copy_matrix(result, itfm); return true; } else if (sd->type == PRIMITIVE_LAMP) { - copy_matrix(result, sd->ob_itfm); + const Transform itfm = lamp_fetch_transform(kg, sd->lamp, true); + copy_matrix(result, itfm); return true; } @@ -218,7 +220,7 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, float time) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; + const KernelGlobals *kg = sd->osl_globals; if (from == u_ndc) { copy_matrix(result, kernel_data.cam.ndctoworld); @@ -250,7 +252,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, float time) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; + const KernelGlobals *kg = sd->osl_globals; if (to == u_ndc) { copy_matrix(result, kernel_data.cam.worldtondc); @@ -284,21 +286,18 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; + const KernelGlobals *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { -#ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -#else - KernelGlobals *kg = sd->osl_globals; - Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); -#endif + const Transform tfm = object_get_transform(kg, sd); copy_matrix(result, tfm); return true; } else if (sd->type == PRIMITIVE_LAMP) { - copy_matrix(result, sd->ob_tfm); + const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false); + copy_matrix(result, tfm); return true; } @@ -315,21 +314,18 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; + const KernelGlobals *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { -#ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -#else - KernelGlobals *kg = sd->osl_globals; - Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); -#endif + const Transform tfm = object_get_inverse_transform(kg, sd); copy_matrix(result, tfm); return true; } else if (sd->type == PRIMITIVE_LAMP) { - copy_matrix(result, sd->ob_itfm); + const Transform itfm = lamp_fetch_transform(kg, sd->lamp, true); + copy_matrix(result, itfm); return true; } @@ -341,7 +337,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result, ustring from) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; + const KernelGlobals *kg = sd->osl_globals; if (from == u_ndc) { copy_matrix(result, kernel_data.cam.ndctoworld); @@ -368,7 +364,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, ustring to) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; + const KernelGlobals *kg = sd->osl_globals; if (to == u_ndc) { copy_matrix(result, kernel_data.cam.worldtondc); @@ -747,7 +743,7 @@ static bool set_attribute_matrix(const Transform &tfm, TypeDesc type, void *val) return false; } -static bool get_primitive_attribute(KernelGlobals *kg, +static bool get_primitive_attribute(const KernelGlobals *kg, const ShaderData *sd, const OSLGlobals::Attribute &attr, const TypeDesc &type, @@ -808,7 +804,7 @@ static bool get_primitive_attribute(KernelGlobals *kg, } } -static bool get_mesh_attribute(KernelGlobals *kg, +static bool get_mesh_attribute(const KernelGlobals *kg, const ShaderData *sd, const OSLGlobals::Attribute &attr, const TypeDesc &type, @@ -857,8 +853,12 @@ static bool get_object_attribute(const OSLGlobals::Attribute &attr, } } -bool OSLRenderServices::get_object_standard_attribute( - KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val) +bool OSLRenderServices::get_object_standard_attribute(const KernelGlobals *kg, + ShaderData *sd, + ustring name, + TypeDesc type, + bool derivatives, + void *val) { /* todo: turn this into hash table? */ @@ -988,8 +988,12 @@ bool OSLRenderServices::get_object_standard_attribute( return false; } -bool OSLRenderServices::get_background_attribute( - KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val) +bool OSLRenderServices::get_background_attribute(const KernelGlobals *kg, + ShaderData *sd, + ustring name, + TypeDesc type, + bool derivatives, + void *val) { if (name == u_path_ray_length) { /* Ray Length */ @@ -998,38 +1002,32 @@ bool OSLRenderServices::get_background_attribute( } else if (name == u_path_ray_depth) { /* Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->bounce; + const IntegratorStateCPU *state = sd->osl_path_state; + int f = state->path.bounce; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_diffuse_depth) { /* Diffuse Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->diffuse_bounce; + const IntegratorStateCPU *state = sd->osl_path_state; + int f = state->path.diffuse_bounce; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_glossy_depth) { /* Glossy Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->glossy_bounce; + const IntegratorStateCPU *state = sd->osl_path_state; + int f = state->path.glossy_bounce; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_transmission_depth) { /* Transmission Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->transmission_bounce; + const IntegratorStateCPU *state = sd->osl_path_state; + int f = state->path.transmission_bounce; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_transparent_depth) { /* Transparent Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->transparent_bounce; - return set_attribute_int(f, type, derivatives, val); - } - else if (name == u_path_transmission_depth) { - /* Transmission Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->transmission_bounce; + const IntegratorStateCPU *state = sd->osl_path_state; + int f = state->path.transparent_bounce; return set_attribute_int(f, type, derivatives, val); } else if (name == u_ndc) { @@ -1043,8 +1041,10 @@ bool OSLRenderServices::get_background_attribute( ndc[0] = camera_world_to_ndc(kg, sd, sd->ray_P); if (derivatives) { - ndc[1] = camera_world_to_ndc(kg, sd, sd->ray_P + sd->ray_dP.dx) - ndc[0]; - ndc[2] = camera_world_to_ndc(kg, sd, sd->ray_P + sd->ray_dP.dy) - ndc[0]; + ndc[1] = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(sd->ray_dP, 0.0f, 0.0f)) - + ndc[0]; + ndc[2] = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(0.0f, sd->ray_dP, 0.0f)) - + ndc[0]; } } else { @@ -1079,7 +1079,7 @@ bool OSLRenderServices::get_attribute(OSL::ShaderGlobals *sg, bool OSLRenderServices::get_attribute( ShaderData *sd, bool derivatives, ustring object_name, TypeDesc type, ustring name, void *val) { - KernelGlobals *kg = sd->osl_globals; + const KernelGlobals *kg = sd->osl_globals; int prim_type = 0; int object; @@ -1208,17 +1208,17 @@ bool OSLRenderServices::texture(ustring filename, OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; OSLTextureHandle::Type texture_type = (handle) ? handle->type : OSLTextureHandle::OIIO; ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kernel_globals = sd->osl_globals; + const KernelGlobals *kernel_globals = sd->osl_globals; bool status = false; switch (texture_type) { case OSLTextureHandle::BEVEL: { /* Bevel shader hack. */ if (nchannels >= 3) { - PathState *state = sd->osl_path_state; + const IntegratorStateCPU *state = sd->osl_path_state; int num_samples = (int)s; float radius = t; - float3 N = svm_bevel(kernel_globals, sd, state, radius, num_samples); + float3 N = svm_bevel(kernel_globals, state, sd, radius, num_samples); result[0] = N.x; result[1] = N.y; result[2] = N.z; @@ -1228,7 +1228,7 @@ bool OSLRenderServices::texture(ustring filename, } case OSLTextureHandle::AO: { /* AO shader hack. */ - PathState *state = sd->osl_path_state; + const IntegratorStateCPU *state = sd->osl_path_state; int num_samples = (int)s; float radius = t; float3 N = make_float3(dsdx, dtdx, dsdy); @@ -1242,7 +1242,7 @@ bool OSLRenderServices::texture(ustring filename, if ((int)options.tblur) { flags |= NODE_AO_GLOBAL_RADIUS; } - result[0] = svm_ao(kernel_globals, sd, N, state, radius, num_samples, flags); + result[0] = svm_ao(kernel_globals, state, sd, N, radius, num_samples, flags); status = true; break; } @@ -1355,7 +1355,7 @@ bool OSLRenderServices::texture3d(ustring filename, case OSLTextureHandle::SVM: { /* Packed texture. */ ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kernel_globals = sd->osl_globals; + const KernelGlobals *kernel_globals = sd->osl_globals; int slot = handle->svm_slot; float3 P_float3 = make_float3(P.x, P.y, P.z); float4 rgba = kernel_tex_image_interp_3d(kernel_globals, slot, P_float3, INTERPOLATION_NONE); @@ -1377,7 +1377,7 @@ bool OSLRenderServices::texture3d(ustring filename, if (handle && handle->oiio_handle) { if (texture_thread_info == NULL) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kernel_globals = sd->osl_globals; + const KernelGlobals *kernel_globals = sd->osl_globals; OSLThreadData *tdata = kernel_globals->osl_tdata; texture_thread_info = tdata->oiio_thread_info; } @@ -1462,7 +1462,7 @@ bool OSLRenderServices::environment(ustring filename, if (handle && handle->oiio_handle) { if (thread_info == NULL) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kernel_globals = sd->osl_globals; + const KernelGlobals *kernel_globals = sd->osl_globals; OSLThreadData *tdata = kernel_globals->osl_tdata; thread_info = tdata->oiio_thread_info; } @@ -1600,10 +1600,14 @@ bool OSLRenderServices::trace(TraceOpt &options, } /* ray differentials */ - ray.dP.dx = TO_FLOAT3(dPdx); - ray.dP.dy = TO_FLOAT3(dPdy); - ray.dD.dx = TO_FLOAT3(dRdx); - ray.dD.dy = TO_FLOAT3(dRdy); + differential3 dP; + dP.dx = TO_FLOAT3(dPdx); + dP.dy = TO_FLOAT3(dPdy); + ray.dP = differential_make_compact(dP); + differential3 dD; + dD.dx = TO_FLOAT3(dRdx); + dD.dy = TO_FLOAT3(dRdy); + ray.dD = differential_make_compact(dD); /* allocate trace data */ OSLTraceData *tracedata = (OSLTraceData *)sg->tracedata; @@ -1613,7 +1617,7 @@ bool OSLRenderServices::trace(TraceOpt &options, tracedata->hit = false; tracedata->sd.osl_globals = sd->osl_globals; - KernelGlobals *kg = sd->osl_globals; + const KernelGlobals *kg = sd->osl_globals; /* Can't raytrace from shaders like displacement, before BVH exists. */ if (kernel_data.bvh.bvh_layout == BVH_LAYOUT_NONE) { @@ -1646,11 +1650,11 @@ bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg, } else { ShaderData *sd = &tracedata->sd; - KernelGlobals *kg = sd->osl_globals; + const KernelGlobals *kg = sd->osl_globals; if (!tracedata->setup) { /* lazy shader data setup */ - shader_setup_from_ray(kg, sd, &tracedata->isect, &tracedata->ray); + shader_setup_from_ray(kg, sd, &tracedata->ray, &tracedata->isect); tracedata->setup = true; } diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/osl_services.h index 891b9172dd4..58accb46e7d 100644 --- a/intern/cycles/kernel/osl/osl_services.h +++ b/intern/cycles/kernel/osl/osl_services.h @@ -250,10 +250,18 @@ class OSLRenderServices : public OSL::RendererServices { void *data) override; #endif - static bool get_background_attribute( - KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val); - static bool get_object_standard_attribute( - KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val); + static bool get_background_attribute(const KernelGlobals *kg, + ShaderData *sd, + ustring name, + TypeDesc type, + bool derivatives, + void *val); + static bool get_object_standard_attribute(const KernelGlobals *kg, + ShaderData *sd, + ustring name, + TypeDesc type, + bool derivatives, + void *val); static ustring u_distance; static ustring u_index; diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp index 389c854c495..880ef635c76 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/osl_shader.cpp @@ -17,14 +17,16 @@ #include // clang-format off -#include "kernel/kernel_compat_cpu.h" +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + #include "kernel/kernel_montecarlo.h" #include "kernel/kernel_types.h" -#include "kernel/split/kernel_split_data_types.h" -#include "kernel/kernel_globals.h" #include "kernel/geom/geom_object.h" +#include "kernel/integrator/integrator_state.h" + #include "kernel/osl/osl_closures.h" #include "kernel/osl/osl_globals.h" #include "kernel/osl/osl_services.h" @@ -39,9 +41,7 @@ CCL_NAMESPACE_BEGIN /* Threads */ -void OSLShader::thread_init(KernelGlobals *kg, - KernelGlobals *kernel_globals, - OSLGlobals *osl_globals) +void OSLShader::thread_init(KernelGlobals *kg, OSLGlobals *osl_globals) { /* no osl used? */ if (!osl_globals->use) { @@ -87,8 +87,11 @@ void OSLShader::thread_free(KernelGlobals *kg) /* Globals */ -static void shaderdata_to_shaderglobals( - KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, OSLThreadData *tdata) +static void shaderdata_to_shaderglobals(const KernelGlobals *kg, + ShaderData *sd, + const IntegratorStateCPU *state, + int path_flag, + OSLThreadData *tdata) { OSL::ShaderGlobals *globals = &tdata->globals; @@ -171,7 +174,10 @@ static void flatten_surface_closure_tree(ShaderData *sd, } } -void OSLShader::eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag) +void OSLShader::eval_surface(const KernelGlobals *kg, + const IntegratorStateCPU *state, + ShaderData *sd, + int path_flag) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; @@ -276,7 +282,10 @@ static void flatten_background_closure_tree(ShaderData *sd, } } -void OSLShader::eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag) +void OSLShader::eval_background(const KernelGlobals *kg, + const IntegratorStateCPU *state, + ShaderData *sd, + int path_flag) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; @@ -331,7 +340,10 @@ static void flatten_volume_closure_tree(ShaderData *sd, } } -void OSLShader::eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag) +void OSLShader::eval_volume(const KernelGlobals *kg, + const IntegratorStateCPU *state, + ShaderData *sd, + int path_flag) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; @@ -354,7 +366,9 @@ void OSLShader::eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, /* Displacement */ -void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd, PathState *state) +void OSLShader::eval_displacement(const KernelGlobals *kg, + const IntegratorStateCPU *state, + ShaderData *sd) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; @@ -377,7 +391,7 @@ void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd, PathState * /* Attributes */ -int OSLShader::find_attribute(KernelGlobals *kg, +int OSLShader::find_attribute(const KernelGlobals *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc) diff --git a/intern/cycles/kernel/osl/osl_shader.h b/intern/cycles/kernel/osl/osl_shader.h index a4fa24d0a90..f1f17b141eb 100644 --- a/intern/cycles/kernel/osl/osl_shader.h +++ b/intern/cycles/kernel/osl/osl_shader.h @@ -37,6 +37,7 @@ class Scene; struct ShaderClosure; struct ShaderData; +struct IntegratorStateCPU; struct differential3; struct KernelGlobals; @@ -49,19 +50,28 @@ class OSLShader { static void register_closures(OSLShadingSystem *ss); /* per thread data */ - static void thread_init(KernelGlobals *kg, - KernelGlobals *kernel_globals, - OSLGlobals *osl_globals); + static void thread_init(KernelGlobals *kg, OSLGlobals *osl_globals); static void thread_free(KernelGlobals *kg); /* eval */ - static void eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag); - static void eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag); - static void eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag); - static void eval_displacement(KernelGlobals *kg, ShaderData *sd, PathState *state); + static void eval_surface(const KernelGlobals *kg, + const IntegratorStateCPU *state, + ShaderData *sd, + int path_flag); + static void eval_background(const KernelGlobals *kg, + const IntegratorStateCPU *state, + ShaderData *sd, + int path_flag); + static void eval_volume(const KernelGlobals *kg, + const IntegratorStateCPU *state, + ShaderData *sd, + int path_flag); + static void eval_displacement(const KernelGlobals *kg, + const IntegratorStateCPU *state, + ShaderData *sd); /* attributes */ - static int find_attribute(KernelGlobals *kg, + static int find_attribute(const KernelGlobals *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc); diff --git a/intern/cycles/kernel/shaders/node_principled_bsdf.osl b/intern/cycles/kernel/shaders/node_principled_bsdf.osl index 23949f406c7..55afb892d36 100644 --- a/intern/cycles/kernel/shaders/node_principled_bsdf.osl +++ b/intern/cycles/kernel/shaders/node_principled_bsdf.osl @@ -18,11 +18,13 @@ #include "stdcycles.h" shader node_principled_bsdf(string distribution = "Multiscatter GGX", - string subsurface_method = "burley", + string subsurface_method = "random_walk", color BaseColor = color(0.8, 0.8, 0.8), float Subsurface = 0.0, vector SubsurfaceRadius = vector(1.0, 1.0, 1.0), color SubsurfaceColor = color(0.7, 0.1, 0.1), + float SubsurfaceIOR = 1.4, + float SubsurfaceAnisotropy = 0.0, float Metallic = 0.0, float Specular = 0.5, float SpecularTint = 0.0, @@ -59,22 +61,17 @@ shader node_principled_bsdf(string distribution = "Multiscatter GGX", if (diffuse_weight > 1e-5) { if (Subsurface > 1e-5) { color mixed_ss_base_color = SubsurfaceColor * Subsurface + BaseColor * (1.0 - Subsurface); - if (subsurface_method == "burley") { - BSDF = mixed_ss_base_color * bssrdf("principled", - Normal, - Subsurface * SubsurfaceRadius, - SubsurfaceColor, - "roughness", - Roughness); - } - else { - BSDF = mixed_ss_base_color * bssrdf("principled_random_walk", - Normal, - Subsurface * SubsurfaceRadius, - mixed_ss_base_color, - "roughness", - Roughness); - } + + BSDF = mixed_ss_base_color * bssrdf(subsurface_method, + Normal, + Subsurface * SubsurfaceRadius, + mixed_ss_base_color, + "roughness", + Roughness, + "ior", + SubsurfaceIOR, + "anisotropy", + SubsurfaceAnisotropy); } else { BSDF = BaseColor * principled_diffuse(Normal, Roughness); diff --git a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl b/intern/cycles/kernel/shaders/node_subsurface_scattering.osl index b1e854150ab..f55e38c54ff 100644 --- a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl +++ b/intern/cycles/kernel/shaders/node_subsurface_scattering.osl @@ -19,27 +19,12 @@ shader node_subsurface_scattering(color Color = 0.8, float Scale = 1.0, vector Radius = vector(0.1, 0.1, 0.1), - float TextureBlur = 0.0, - float Sharpness = 0.0, - string falloff = "cubic", + float IOR = 1.4, + float Anisotropy = 0.0, + string method = "random_walk", normal Normal = N, output closure color BSSRDF = 0) { - if (falloff == "gaussian") - BSSRDF = Color * - bssrdf("gaussian", Normal, Scale * Radius, Color, "texture_blur", TextureBlur); - else if (falloff == "cubic") - BSSRDF = Color * bssrdf("cubic", - Normal, - Scale * Radius, - Color, - "texture_blur", - TextureBlur, - "sharpness", - Sharpness); - else if (falloff == "burley") - BSSRDF = Color * bssrdf("burley", Normal, Scale * Radius, Color, "texture_blur", TextureBlur); - else - BSSRDF = Color * - bssrdf("random_walk", Normal, Scale * Radius, Color, "texture_blur", TextureBlur); + BSSRDF = Color * + bssrdf(method, Normal, Scale * Radius, Color, "ior", IOR, "anisotropy", Anisotropy); } diff --git a/intern/cycles/kernel/split/kernel_adaptive_adjust_samples.h b/intern/cycles/kernel/split/kernel_adaptive_adjust_samples.h deleted file mode 100644 index 437a5c9581b..00000000000 --- a/intern/cycles/kernel/split/kernel_adaptive_adjust_samples.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_adaptive_adjust_samples(KernelGlobals *kg) -{ - int pixel_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (pixel_index < kernel_split_params.tile.w * kernel_split_params.tile.h) { - int x = kernel_split_params.tile.x + pixel_index % kernel_split_params.tile.w; - int y = kernel_split_params.tile.y + pixel_index / kernel_split_params.tile.w; - int buffer_offset = (kernel_split_params.tile.offset + x + - y * kernel_split_params.tile.stride) * - kernel_data.film.pass_stride; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - int sample = kernel_split_params.tile.start_sample + kernel_split_params.tile.num_samples; - if (buffer[kernel_data.film.pass_sample_count] < 0.0f) { - buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count]; - float sample_multiplier = sample / buffer[kernel_data.film.pass_sample_count]; - if (sample_multiplier != 1.0f) { - kernel_adaptive_post_adjust(kg, buffer, sample_multiplier); - } - } - else { - kernel_adaptive_post_adjust(kg, buffer, sample / (sample - 1.0f)); - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_adaptive_filter_x.h b/intern/cycles/kernel/split/kernel_adaptive_filter_x.h deleted file mode 100644 index 93f41f7ced4..00000000000 --- a/intern/cycles/kernel/split/kernel_adaptive_filter_x.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_adaptive_filter_x(KernelGlobals *kg) -{ - int pixel_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (pixel_index < kernel_split_params.tile.h && - kernel_split_params.tile.start_sample + kernel_split_params.tile.num_samples >= - kernel_data.integrator.adaptive_min_samples) { - int y = kernel_split_params.tile.y + pixel_index; - kernel_do_adaptive_filter_x(kg, y, &kernel_split_params.tile); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_adaptive_filter_y.h b/intern/cycles/kernel/split/kernel_adaptive_filter_y.h deleted file mode 100644 index eca53d079ec..00000000000 --- a/intern/cycles/kernel/split/kernel_adaptive_filter_y.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_adaptive_filter_y(KernelGlobals *kg) -{ - int pixel_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (pixel_index < kernel_split_params.tile.w && - kernel_split_params.tile.start_sample + kernel_split_params.tile.num_samples >= - kernel_data.integrator.adaptive_min_samples) { - int x = kernel_split_params.tile.x + pixel_index; - kernel_do_adaptive_filter_y(kg, x, &kernel_split_params.tile); - } -} -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_adaptive_stopping.h b/intern/cycles/kernel/split/kernel_adaptive_stopping.h deleted file mode 100644 index c8eb1ebd705..00000000000 --- a/intern/cycles/kernel/split/kernel_adaptive_stopping.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_adaptive_stopping(KernelGlobals *kg) -{ - int pixel_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (pixel_index < kernel_split_params.tile.w * kernel_split_params.tile.h && - kernel_split_params.tile.start_sample + kernel_split_params.tile.num_samples >= - kernel_data.integrator.adaptive_min_samples) { - int x = kernel_split_params.tile.x + pixel_index % kernel_split_params.tile.w; - int y = kernel_split_params.tile.y + pixel_index / kernel_split_params.tile.w; - int buffer_offset = (kernel_split_params.tile.offset + x + - y * kernel_split_params.tile.stride) * - kernel_data.film.pass_stride; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - kernel_do_adaptive_stopping(kg, - buffer, - kernel_split_params.tile.start_sample + - kernel_split_params.tile.num_samples - 1); - } -} -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_branched.h b/intern/cycles/kernel/split/kernel_branched.h deleted file mode 100644 index 45f5037d321..00000000000 --- a/intern/cycles/kernel/split/kernel_branched.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __BRANCHED_PATH__ - -/* sets up the various state needed to do an indirect loop */ -ccl_device_inline void kernel_split_branched_path_indirect_loop_init(KernelGlobals *kg, - int ray_index) -{ - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - /* save a copy of the state to restore later */ -# define BRANCHED_STORE(name) branched_state->name = kernel_split_state.name[ray_index]; - - BRANCHED_STORE(path_state); - BRANCHED_STORE(throughput); - BRANCHED_STORE(ray); - BRANCHED_STORE(isect); - BRANCHED_STORE(ray_state); - - *kernel_split_sd(branched_state_sd, ray_index) = *kernel_split_sd(sd, ray_index); - for (int i = 0; i < kernel_split_sd(branched_state_sd, ray_index)->num_closure; i++) { - kernel_split_sd(branched_state_sd, ray_index)->closure[i] = - kernel_split_sd(sd, ray_index)->closure[i]; - } - -# undef BRANCHED_STORE - - /* Set loop counters to initial position. */ - branched_state->next_closure = 0; - branched_state->next_sample = 0; -} - -/* ends an indirect loop and restores the previous state */ -ccl_device_inline void kernel_split_branched_path_indirect_loop_end(KernelGlobals *kg, - int ray_index) -{ - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - /* restore state */ -# define BRANCHED_RESTORE(name) kernel_split_state.name[ray_index] = branched_state->name; - - BRANCHED_RESTORE(path_state); - BRANCHED_RESTORE(throughput); - BRANCHED_RESTORE(ray); - BRANCHED_RESTORE(isect); - BRANCHED_RESTORE(ray_state); - - *kernel_split_sd(sd, ray_index) = *kernel_split_sd(branched_state_sd, ray_index); - for (int i = 0; i < kernel_split_sd(branched_state_sd, ray_index)->num_closure; i++) { - kernel_split_sd(sd, ray_index)->closure[i] = - kernel_split_sd(branched_state_sd, ray_index)->closure[i]; - } - -# undef BRANCHED_RESTORE - - /* leave indirect loop */ - REMOVE_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_INDIRECT); -} - -ccl_device_inline bool kernel_split_branched_indirect_start_shared(KernelGlobals *kg, - int ray_index) -{ - ccl_global char *ray_state = kernel_split_state.ray_state; - - int inactive_ray = dequeue_ray_index(QUEUE_INACTIVE_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - kernel_split_params.queue_index); - - if (!IS_STATE(ray_state, inactive_ray, RAY_INACTIVE)) { - return false; - } - -# define SPLIT_DATA_ENTRY(type, name, num) \ - if (num) { \ - kernel_split_state.name[inactive_ray] = kernel_split_state.name[ray_index]; \ - } - SPLIT_DATA_ENTRIES_BRANCHED_SHARED -# undef SPLIT_DATA_ENTRY - - *kernel_split_sd(sd, inactive_ray) = *kernel_split_sd(sd, ray_index); - for (int i = 0; i < kernel_split_sd(sd, ray_index)->num_closure; i++) { - kernel_split_sd(sd, inactive_ray)->closure[i] = kernel_split_sd(sd, ray_index)->closure[i]; - } - - kernel_split_state.branched_state[inactive_ray].shared_sample_count = 0; - kernel_split_state.branched_state[inactive_ray].original_ray = ray_index; - kernel_split_state.branched_state[inactive_ray].waiting_on_shared_samples = false; - - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - PathRadiance *inactive_L = &kernel_split_state.path_radiance[inactive_ray]; - - path_radiance_init(kg, inactive_L); - path_radiance_copy_indirect(inactive_L, L); - - ray_state[inactive_ray] = RAY_REGENERATED; - ADD_RAY_FLAG(ray_state, inactive_ray, RAY_BRANCHED_INDIRECT_SHARED); - ADD_RAY_FLAG(ray_state, inactive_ray, IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)); - - atomic_fetch_and_inc_uint32( - (ccl_global uint *)&kernel_split_state.branched_state[ray_index].shared_sample_count); - - return true; -} - -/* bounce off surface and integrate indirect light */ -ccl_device_noinline bool kernel_split_branched_path_surface_indirect_light_iter( - KernelGlobals *kg, - int ray_index, - float num_samples_adjust, - ShaderData *saved_sd, - bool reset_path_state, - bool wait_for_shared) -{ - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - ShaderData *sd = saved_sd; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - float3 throughput = branched_state->throughput; - ccl_global PathState *ps = &kernel_split_state.path_state[ray_index]; - - float sum_sample_weight = 0.0f; -# ifdef __DENOISING_FEATURES__ - if (ps->denoising_feature_weight > 0.0f) { - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - /* transparency is not handled here, but in outer loop */ - if (!CLOSURE_IS_BSDF(sc->type) || CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { - continue; - } - - sum_sample_weight += sc->sample_weight; - } - } - else { - sum_sample_weight = 1.0f; - } -# endif /* __DENOISING_FEATURES__ */ - - for (int i = branched_state->next_closure; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - if (!CLOSURE_IS_BSDF(sc->type)) - continue; - /* transparency is not handled here, but in outer loop */ - if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) - continue; - - int num_samples; - - if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) - num_samples = kernel_data.integrator.diffuse_samples; - else if (CLOSURE_IS_BSDF_BSSRDF(sc->type)) - num_samples = 1; - else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) - num_samples = kernel_data.integrator.glossy_samples; - else - num_samples = kernel_data.integrator.transmission_samples; - - num_samples = ceil_to_int(num_samples_adjust * num_samples); - - float num_samples_inv = num_samples_adjust / num_samples; - - for (int j = branched_state->next_sample; j < num_samples; j++) { - if (reset_path_state) { - *ps = branched_state->path_state; - } - - ps->rng_hash = cmj_hash(branched_state->path_state.rng_hash, i); - - ccl_global float3 *tp = &kernel_split_state.throughput[ray_index]; - *tp = throughput; - - ccl_global Ray *bsdf_ray = &kernel_split_state.ray[ray_index]; - - if (!kernel_branched_path_surface_bounce( - kg, sd, sc, j, num_samples, tp, ps, &L->state, bsdf_ray, sum_sample_weight)) { - continue; - } - - ps->rng_hash = branched_state->path_state.rng_hash; - - /* update state for next iteration */ - branched_state->next_closure = i; - branched_state->next_sample = j + 1; - - /* start the indirect path */ - *tp *= num_samples_inv; - - if (kernel_split_branched_indirect_start_shared(kg, ray_index)) { - continue; - } - - return true; - } - - branched_state->next_sample = 0; - } - - branched_state->next_closure = sd->num_closure; - - if (wait_for_shared) { - branched_state->waiting_on_shared_samples = (branched_state->shared_sample_count > 0); - if (branched_state->waiting_on_shared_samples) { - return true; - } - } - - return false; -} - -#endif /* __BRANCHED_PATH__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_buffer_update.h b/intern/cycles/kernel/split/kernel_buffer_update.h deleted file mode 100644 index b96feca582f..00000000000 --- a/intern/cycles/kernel/split/kernel_buffer_update.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel takes care of rays that hit the background (sceneintersect - * kernel), and for the rays of state RAY_UPDATE_BUFFER it updates the ray's - * accumulated radiance in the output buffer. This kernel also takes care of - * rays that have been determined to-be-regenerated. - * - * We will empty QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue in this kernel. - * - * Typically all rays that are in state RAY_HIT_BACKGROUND, RAY_UPDATE_BUFFER - * will be eventually set to RAY_TO_REGENERATE state in this kernel. - * Finally all rays of ray_state RAY_TO_REGENERATE will be regenerated and put - * in queue QUEUE_ACTIVE_AND_REGENERATED_RAYS. - * - * State of queues when this kernel is called: - * At entry, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_UPDATE_BUFFER, RAY_HIT_BACKGROUND, RAY_TO_REGENERATE rays. - * At exit, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and - * RAY_REGENERATED rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be empty. - */ -ccl_device void kernel_buffer_update(KernelGlobals *kg, - ccl_local_param unsigned int *local_queue_atomics) -{ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (ray_index == 0) { - /* We will empty this queue in this kernel. */ - kernel_split_params.queue_index[QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS] = 0; - } - char enqueue_flag = 0; - ray_index = get_ray_index(kg, - ray_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - - if (ray_index != QUEUE_EMPTY_SLOT) { - ccl_global char *ray_state = kernel_split_state.ray_state; - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - bool ray_was_updated = false; - - if (IS_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER)) { - ray_was_updated = true; - uint sample = state->sample; - uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - - /* accumulate result in output buffer */ - kernel_write_result(kg, buffer, sample, L); - - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_TO_REGENERATE); - } - - if (kernel_data.film.cryptomatte_passes) { - /* Make sure no thread is writing to the buffers. */ - ccl_barrier(CCL_LOCAL_MEM_FENCE); - if (ray_was_updated && state->sample - 1 == kernel_data.integrator.aa_samples) { - uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - ccl_global float *cryptomatte_buffer = buffer + kernel_data.film.pass_cryptomatte; - kernel_sort_id_slots(cryptomatte_buffer, 2 * kernel_data.film.cryptomatte_depth); - } - } - - if (IS_STATE(ray_state, ray_index, RAY_TO_REGENERATE)) { - /* We have completed current work; So get next work */ - ccl_global uint *work_pools = kernel_split_params.work_pools; - uint total_work_size = kernel_split_params.total_work_size; - uint work_index; - - if (!get_next_work(kg, work_pools, total_work_size, ray_index, &work_index)) { - /* If work is invalid, this means no more work is available and the thread may exit */ - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_INACTIVE); - } - - if (IS_STATE(ray_state, ray_index, RAY_TO_REGENERATE)) { - ccl_global WorkTile *tile = &kernel_split_params.tile; - uint x, y, sample; - get_work_pixel(tile, work_index, &x, &y, &sample); - - /* Store buffer offset for writing to passes. */ - uint buffer_offset = (tile->offset + x + y * tile->stride) * kernel_data.film.pass_stride; - kernel_split_state.buffer_offset[ray_index] = buffer_offset; - - /* Initialize random numbers and ray. */ - uint rng_hash; - kernel_path_trace_setup(kg, sample, x, y, &rng_hash, ray); - - if (ray->t != 0.0f) { - /* Initialize throughput, path radiance, Ray, PathState; - * These rays proceed with path-iteration. - */ - *throughput = make_float3(1.0f, 1.0f, 1.0f); - path_radiance_init(kg, L); - path_state_init(kg, - AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]), - state, - rng_hash, - sample, - ray); -#ifdef __SUBSURFACE__ - kernel_path_subsurface_init_indirect(&kernel_split_state.ss_rays[ray_index]); -#endif - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - enqueue_flag = 1; - } - else { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_TO_REGENERATE); - } - } - } - } - - /* Enqueue RAY_REGENERATED rays into QUEUE_ACTIVE_AND_REGENERATED_RAYS; - * These rays will be made active during next SceneIntersectkernel. - */ - enqueue_ray_index_local(ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - enqueue_flag, - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_data_init.h b/intern/cycles/kernel/split/kernel_data_init.h deleted file mode 100644 index 2f83a10316d..00000000000 --- a/intern/cycles/kernel/split/kernel_data_init.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel Initializes structures needed in path-iteration kernels. - * - * Note on Queues: - * All slots in queues are initialized to queue empty slot; - * The number of elements in the queues is initialized to 0; - */ - -#ifndef __KERNEL_CPU__ -ccl_device void kernel_data_init( -#else -void KERNEL_FUNCTION_FULL_NAME(data_init)( -#endif - KernelGlobals *kg, - ccl_constant KernelData *data, - ccl_global void *split_data_buffer, - int num_elements, - ccl_global char *ray_state, - -#ifdef __KERNEL_OPENCL__ - KERNEL_BUFFER_PARAMS, -#endif - - int start_sample, - int end_sample, - int sx, - int sy, - int sw, - int sh, - int offset, - int stride, - ccl_global int *Queue_index, /* Tracks the number of elements in queues */ - int queuesize, /* size (capacity) of the queue */ - ccl_global char *use_queues_flag, /* flag to decide if scene-intersect kernel should use queues - to fetch ray index */ - ccl_global unsigned int *work_pools, /* Work pool for each work group */ - unsigned int num_samples, - ccl_global float *buffer) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, data_init); -#else - -# ifdef __KERNEL_OPENCL__ - kg->data = data; -# endif - - kernel_split_params.tile.x = sx; - kernel_split_params.tile.y = sy; - kernel_split_params.tile.w = sw; - kernel_split_params.tile.h = sh; - - kernel_split_params.tile.start_sample = start_sample; - kernel_split_params.tile.num_samples = num_samples; - - kernel_split_params.tile.offset = offset; - kernel_split_params.tile.stride = stride; - - kernel_split_params.tile.buffer = buffer; - - kernel_split_params.total_work_size = sw * sh * num_samples; - - kernel_split_params.work_pools = work_pools; - - kernel_split_params.queue_index = Queue_index; - kernel_split_params.queue_size = queuesize; - kernel_split_params.use_queues_flag = use_queues_flag; - - split_data_init(kg, &kernel_split_state, num_elements, split_data_buffer, ray_state); - -# ifdef __KERNEL_OPENCL__ - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); -# endif - - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - - /* Initialize queue data and queue index. */ - if (thread_index < queuesize) { - for (int i = 0; i < NUM_QUEUES; i++) { - kernel_split_state.queue_data[i * queuesize + thread_index] = QUEUE_EMPTY_SLOT; - } - } - - if (thread_index == 0) { - for (int i = 0; i < NUM_QUEUES; i++) { - Queue_index[i] = 0; - } - - /* The scene-intersect kernel should not use the queues very first time. - * since the queue would be empty. - */ - *use_queues_flag = 0; - } -#endif /* KERENL_STUB */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_direct_lighting.h b/intern/cycles/kernel/split/kernel_direct_lighting.h deleted file mode 100644 index 3be2b35812f..00000000000 --- a/intern/cycles/kernel/split/kernel_direct_lighting.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel takes care of direct lighting logic. - * However, the "shadow ray cast" part of direct lighting is handled - * in the next kernel. - * - * This kernels determines the rays for which a shadow_blocked() function - * associated with direct lighting should be executed. Those rays for which - * a shadow_blocked() function for direct-lighting must be executed, are - * marked with flag RAY_SHADOW_RAY_CAST_DL and enqueued into the queue - * QUEUE_SHADOW_RAY_CAST_DL_RAYS - * - * Note on Queues: - * This kernel only reads from the QUEUE_ACTIVE_AND_REGENERATED_RAYS queue - * and processes only the rays of state RAY_ACTIVE; If a ray needs to execute - * the corresponding shadow_blocked part, after direct lighting, the ray is - * marked with RAY_SHADOW_RAY_CAST_DL flag. - * - * State of queues when this kernel is called: - * - State of queues QUEUE_ACTIVE_AND_REGENERATED_RAYS and - * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be same before and after this - * kernel call. - * - QUEUE_SHADOW_RAY_CAST_DL_RAYS queue will be filled with rays for which a - * shadow_blocked function must be executed, after this kernel call - * Before this kernel call the QUEUE_SHADOW_RAY_CAST_DL_RAYS will be empty. - */ -ccl_device void kernel_direct_lighting(KernelGlobals *kg, - ccl_local_param unsigned int *local_queue_atomics) -{ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - char enqueue_flag = 0; - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE)) { - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - - /* direct lighting */ -#ifdef __EMISSION__ - bool flag = (kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)); - -# ifdef __BRANCHED_PATH__ - if (flag && kernel_data.integrator.branched) { - flag = false; - enqueue_flag = 1; - } -# endif /* __BRANCHED_PATH__ */ - -# ifdef __SHADOW_TRICKS__ - if (flag && state->flag & PATH_RAY_SHADOW_CATCHER) { - flag = false; - enqueue_flag = 1; - } -# endif /* __SHADOW_TRICKS__ */ - - if (flag) { - /* Sample illumination from lights to find path contribution. */ - float light_u, light_v; - path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); - float terminate = path_state_rng_light_termination(kg, state); - - LightSample ls; - if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - Ray light_ray; - light_ray.time = sd->time; - - BsdfEval L_light; - bool is_lamp; - if (direct_emission(kg, - sd, - AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]), - &ls, - state, - &light_ray, - &L_light, - &is_lamp, - terminate)) { - /* Write intermediate data to global memory to access from - * the next kernel. - */ - kernel_split_state.light_ray[ray_index] = light_ray; - kernel_split_state.bsdf_eval[ray_index] = L_light; - kernel_split_state.is_lamp[ray_index] = is_lamp; - /* Mark ray state for next shadow kernel. */ - enqueue_flag = 1; - } - } - } -#endif /* __EMISSION__ */ - } - -#ifdef __EMISSION__ - /* Enqueue RAY_SHADOW_RAY_CAST_DL rays. */ - enqueue_ray_index_local(ray_index, - QUEUE_SHADOW_RAY_CAST_DL_RAYS, - enqueue_flag, - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); -#endif - -#ifdef __BRANCHED_PATH__ - /* Enqueue RAY_LIGHT_INDIRECT_NEXT_ITER rays - * this is the last kernel before next_iteration_setup that uses local atomics so we do this here - */ - ccl_barrier(CCL_LOCAL_MEM_FENCE); - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - enqueue_ray_index_local( - ray_index, - QUEUE_LIGHT_INDIRECT_ITER, - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_LIGHT_INDIRECT_NEXT_ITER), - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); - -#endif /* __BRANCHED_PATH__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_do_volume.h b/intern/cycles/kernel/split/kernel_do_volume.h deleted file mode 100644 index 1775e870f07..00000000000 --- a/intern/cycles/kernel/split/kernel_do_volume.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#if defined(__BRANCHED_PATH__) && defined(__VOLUME__) - -ccl_device_inline void kernel_split_branched_path_volume_indirect_light_init(KernelGlobals *kg, - int ray_index) -{ - kernel_split_branched_path_indirect_loop_init(kg, ray_index); - - ADD_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_VOLUME_INDIRECT); -} - -ccl_device_noinline bool kernel_split_branched_path_volume_indirect_light_iter(KernelGlobals *kg, - int ray_index) -{ - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - ShaderData *sd = kernel_split_sd(sd, ray_index); - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - - /* GPU: no decoupled ray marching, scatter probabilistically. */ - int num_samples = kernel_data.integrator.volume_samples; - float num_samples_inv = 1.0f / num_samples; - - Ray volume_ray = branched_state->ray; - volume_ray.t = (!IS_STATE(&branched_state->ray_state, 0, RAY_HIT_BACKGROUND)) ? - branched_state->isect.t : - FLT_MAX; - - float step_size = volume_stack_step_size(kg, branched_state->path_state.volume_stack); - - for (int j = branched_state->next_sample; j < num_samples; j++) { - ccl_global PathState *ps = &kernel_split_state.path_state[ray_index]; - *ps = branched_state->path_state; - - ccl_global Ray *pray = &kernel_split_state.ray[ray_index]; - *pray = branched_state->ray; - - ccl_global float3 *tp = &kernel_split_state.throughput[ray_index]; - *tp = branched_state->throughput * num_samples_inv; - - /* branch RNG state */ - path_state_branch(ps, j, num_samples); - - /* integrate along volume segment with distance sampling */ - VolumeIntegrateResult result = kernel_volume_integrate( - kg, ps, sd, &volume_ray, L, tp, step_size); - -# ifdef __VOLUME_SCATTER__ - if (result == VOLUME_PATH_SCATTERED) { - /* direct lighting */ - kernel_path_volume_connect_light(kg, sd, emission_sd, *tp, &branched_state->path_state, L); - - /* indirect light bounce */ - if (!kernel_path_volume_bounce(kg, sd, tp, ps, &L->state, pray)) { - continue; - } - - /* start the indirect path */ - branched_state->next_closure = 0; - branched_state->next_sample = j + 1; - - /* Attempting to share too many samples is slow for volumes as it causes us to - * loop here more and have many calls to kernel_volume_integrate which evaluates - * shaders. The many expensive shader evaluations cause the work load to become - * unbalanced and many threads to become idle in this kernel. Limiting the - * number of shared samples here helps quite a lot. - */ - if (branched_state->shared_sample_count < 2) { - if (kernel_split_branched_indirect_start_shared(kg, ray_index)) { - continue; - } - } - - return true; - } -# endif - } - - branched_state->next_sample = num_samples; - - branched_state->waiting_on_shared_samples = (branched_state->shared_sample_count > 0); - if (branched_state->waiting_on_shared_samples) { - return true; - } - - kernel_split_branched_path_indirect_loop_end(kg, ray_index); - - /* todo: avoid this calculation using decoupled ray marching */ - float3 throughput = kernel_split_state.throughput[ray_index]; - kernel_volume_shadow( - kg, emission_sd, &kernel_split_state.path_state[ray_index], &volume_ray, &throughput); - kernel_split_state.throughput[ray_index] = throughput; - - return false; -} - -#endif /* __BRANCHED_PATH__ && __VOLUME__ */ - -ccl_device void kernel_do_volume(KernelGlobals *kg) -{ -#ifdef __VOLUME__ - /* We will empty this queue in this kernel. */ - if (ccl_global_id(0) == 0 && ccl_global_id(1) == 0) { - kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS] = 0; -# ifdef __BRANCHED_PATH__ - kernel_split_params.queue_index[QUEUE_VOLUME_INDIRECT_ITER] = 0; -# endif /* __BRANCHED_PATH__ */ - } - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - - if (*kernel_split_params.use_queues_flag) { - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - } - - ccl_global char *ray_state = kernel_split_state.ray_state; - - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE) || - IS_STATE(ray_state, ray_index, RAY_HIT_BACKGROUND)) { - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ccl_global Intersection *isect = &kernel_split_state.isect[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - - bool hit = !IS_STATE(ray_state, ray_index, RAY_HIT_BACKGROUND); - - /* Sanitize volume stack. */ - if (!hit) { - kernel_volume_clean_stack(kg, state->volume_stack); - } - /* volume attenuation, emission, scatter */ - if (state->volume_stack[0].shader != SHADER_NONE) { - Ray volume_ray = *ray; - volume_ray.t = (hit) ? isect->t : FLT_MAX; - -# ifdef __BRANCHED_PATH__ - if (!kernel_data.integrator.branched || - IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)) { -# endif /* __BRANCHED_PATH__ */ - float step_size = volume_stack_step_size(kg, state->volume_stack); - - { - /* integrate along volume segment with distance sampling */ - VolumeIntegrateResult result = kernel_volume_integrate( - kg, state, sd, &volume_ray, L, throughput, step_size); - -# ifdef __VOLUME_SCATTER__ - if (result == VOLUME_PATH_SCATTERED) { - /* direct lighting */ - kernel_path_volume_connect_light(kg, sd, emission_sd, *throughput, state, L); - - /* indirect light bounce */ - if (kernel_path_volume_bounce(kg, sd, throughput, state, &L->state, ray)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - else { - kernel_split_path_end(kg, ray_index); - } - } -# endif /* __VOLUME_SCATTER__ */ - } - -# ifdef __BRANCHED_PATH__ - } - else { - kernel_split_branched_path_volume_indirect_light_init(kg, ray_index); - - if (kernel_split_branched_path_volume_indirect_light_iter(kg, ray_index)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - } -# endif /* __BRANCHED_PATH__ */ - } - } - -# ifdef __BRANCHED_PATH__ - /* iter loop */ - ray_index = get_ray_index(kg, - ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0), - QUEUE_VOLUME_INDIRECT_ITER, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - - if (IS_STATE(ray_state, ray_index, RAY_VOLUME_INDIRECT_NEXT_ITER)) { - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - path_radiance_sum_indirect(&kernel_split_state.path_radiance[ray_index]); - path_radiance_reset_indirect(&kernel_split_state.path_radiance[ray_index]); - - if (kernel_split_branched_path_volume_indirect_light_iter(kg, ray_index)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - } -# endif /* __BRANCHED_PATH__ */ - -#endif /* __VOLUME__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_enqueue_inactive.h b/intern/cycles/kernel/split/kernel_enqueue_inactive.h deleted file mode 100644 index 745313f89f1..00000000000 --- a/intern/cycles/kernel/split/kernel_enqueue_inactive.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_enqueue_inactive(KernelGlobals *kg, - ccl_local_param unsigned int *local_queue_atomics) -{ -#ifdef __BRANCHED_PATH__ - /* Enqueue RAY_INACTIVE rays into QUEUE_INACTIVE_RAYS queue. */ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - - char enqueue_flag = 0; - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_INACTIVE)) { - enqueue_flag = 1; - } - - enqueue_ray_index_local(ray_index, - QUEUE_INACTIVE_RAYS, - enqueue_flag, - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); -#endif /* __BRANCHED_PATH__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h b/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h deleted file mode 100644 index 61722840b0b..00000000000 --- a/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel takes care of the logic to process "material of type holdout", - * indirect primitive emission, bsdf blurring, probabilistic path termination - * and AO. - * - * This kernels determines the rays for which a shadow_blocked() function - * associated with AO should be executed. Those rays for which a - * shadow_blocked() function for AO must be executed are marked with flag - * RAY_SHADOW_RAY_CAST_ao and enqueued into the queue - * QUEUE_SHADOW_RAY_CAST_AO_RAYS - * - * Ray state of rays that are terminated in this kernel are changed to RAY_UPDATE_BUFFER - * - * Note on Queues: - * This kernel fetches rays from the queue QUEUE_ACTIVE_AND_REGENERATED_RAYS - * and processes only the rays of state RAY_ACTIVE. - * There are different points in this kernel where a ray may terminate and - * reach RAY_UPDATE_BUFFER state. These rays are enqueued into - * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. These rays will still be present - * in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue, but since their ray-state has - * been changed to RAY_UPDATE_BUFFER, there is no problem. - * - * State of queues when this kernel is called: - * At entry, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and - * RAY_REGENERATED rays - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_TO_REGENERATE rays. - * - QUEUE_SHADOW_RAY_CAST_AO_RAYS will be empty. - * At exit, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, - * RAY_REGENERATED and RAY_UPDATE_BUFFER rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays. - * - QUEUE_SHADOW_RAY_CAST_AO_RAYS will be filled with rays marked with - * flag RAY_SHADOW_RAY_CAST_AO - */ - -ccl_device void kernel_holdout_emission_blurring_pathtermination_ao( - KernelGlobals *kg, ccl_local_param BackgroundAOLocals *locals) -{ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - locals->queue_atomics_bg = 0; - locals->queue_atomics_ao = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - -#ifdef __AO__ - char enqueue_flag = 0; -#endif - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (ray_index != QUEUE_EMPTY_SLOT) { - ccl_global PathState *state = 0x0; - float3 throughput; - - ccl_global char *ray_state = kernel_split_state.ray_state; - ShaderData *sd = kernel_split_sd(sd, ray_index); - - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - - throughput = kernel_split_state.throughput[ray_index]; - state = &kernel_split_state.path_state[ray_index]; - - if (!kernel_path_shader_apply(kg, sd, state, ray, throughput, emission_sd, L, buffer)) { - kernel_split_path_end(kg, ray_index); - } - } - - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - /* Path termination. this is a strange place to put the termination, it's - * mainly due to the mixed in MIS that we use. gives too many unneeded - * shader evaluations, only need emission if we are going to terminate. - */ - float probability = path_state_continuation_probability(kg, state, throughput); - - if (probability == 0.0f) { - kernel_split_path_end(kg, ray_index); - } - else if (probability < 1.0f) { - float terminate = path_state_rng_1D(kg, state, PRNG_TERMINATE); - if (terminate >= probability) { - kernel_split_path_end(kg, ray_index); - } - else { - kernel_split_state.throughput[ray_index] = throughput / probability; - } - } - -#ifdef __DENOISING_FEATURES__ - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - kernel_update_denoising_features(kg, sd, state, L); - } -#endif - } - -#ifdef __AO__ - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - /* ambient occlusion */ - if (kernel_data.integrator.use_ambient_occlusion) { - enqueue_flag = 1; - } - } -#endif /* __AO__ */ - } - -#ifdef __AO__ - /* Enqueue to-shadow-ray-cast rays. */ - enqueue_ray_index_local(ray_index, - QUEUE_SHADOW_RAY_CAST_AO_RAYS, - enqueue_flag, - kernel_split_params.queue_size, - &locals->queue_atomics_ao, - kernel_split_state.queue_data, - kernel_split_params.queue_index); -#endif -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_indirect_background.h b/intern/cycles/kernel/split/kernel_indirect_background.h deleted file mode 100644 index 6d500650cc0..00000000000 --- a/intern/cycles/kernel/split/kernel_indirect_background.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_indirect_background(KernelGlobals *kg) -{ - ccl_global char *ray_state = kernel_split_state.ray_state; - - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - int ray_index; - - if (kernel_data.integrator.ao_bounces != INT_MAX) { - ray_index = get_ray_index(kg, - thread_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (ray_index != QUEUE_EMPTY_SLOT) { - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - if (path_state_ao_bounce(kg, state)) { - kernel_split_path_end(kg, ray_index); - } - } - } - } - - ray_index = get_ray_index(kg, - thread_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - - if (IS_STATE(ray_state, ray_index, RAY_HIT_BACKGROUND)) { - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - float3 throughput = kernel_split_state.throughput[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - - kernel_path_background(kg, state, ray, throughput, sd, buffer, L); - kernel_split_path_end(kg, ray_index); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_indirect_subsurface.h b/intern/cycles/kernel/split/kernel_indirect_subsurface.h deleted file mode 100644 index 3f48f8d6f56..00000000000 --- a/intern/cycles/kernel/split/kernel_indirect_subsurface.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_indirect_subsurface(KernelGlobals *kg) -{ - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (thread_index == 0) { - /* We will empty both queues in this kernel. */ - kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS] = 0; - kernel_split_params.queue_index[QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS] = 0; - } - - int ray_index; - get_ray_index(kg, - thread_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - ray_index = get_ray_index(kg, - thread_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - -#ifdef __SUBSURFACE__ - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - - ccl_global char *ray_state = kernel_split_state.ray_state; - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - - if (IS_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER)) { - ccl_addr_space SubsurfaceIndirectRays *ss_indirect = &kernel_split_state.ss_rays[ray_index]; - - /* Trace indirect subsurface rays by restarting the loop. this uses less - * stack memory than invoking kernel_path_indirect. - */ - if (ss_indirect->num_rays) { - kernel_path_subsurface_setup_indirect(kg, ss_indirect, state, ray, L, throughput); - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - } -#endif /* __SUBSURFACE__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_lamp_emission.h b/intern/cycles/kernel/split/kernel_lamp_emission.h deleted file mode 100644 index 7ecb099208d..00000000000 --- a/intern/cycles/kernel/split/kernel_lamp_emission.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel operates on QUEUE_ACTIVE_AND_REGENERATED_RAYS. - * It processes rays of state RAY_ACTIVE and RAY_HIT_BACKGROUND. - * We will empty QUEUE_ACTIVE_AND_REGENERATED_RAYS queue in this kernel. - */ -ccl_device void kernel_lamp_emission(KernelGlobals *kg) -{ -#ifndef __VOLUME__ - /* We will empty this queue in this kernel. */ - if (ccl_global_id(0) == 0 && ccl_global_id(1) == 0) { - kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS] = 0; - } -#endif - /* Fetch use_queues_flag. */ - char local_use_queues_flag = *kernel_split_params.use_queues_flag; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (local_use_queues_flag) { - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, -#ifndef __VOLUME__ - 1 -#else - 0 -#endif - ); - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - } - - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE) || - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_HIT_BACKGROUND)) { - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - - float3 throughput = kernel_split_state.throughput[ray_index]; - Ray ray = kernel_split_state.ray[ray_index]; - ccl_global Intersection *isect = &kernel_split_state.isect[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - - kernel_path_lamp_emission(kg, state, &ray, throughput, isect, sd, L); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_next_iteration_setup.h b/intern/cycles/kernel/split/kernel_next_iteration_setup.h deleted file mode 100644 index 320f6a414bf..00000000000 --- a/intern/cycles/kernel/split/kernel_next_iteration_setup.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/*This kernel takes care of setting up ray for the next iteration of - * path-iteration and accumulating radiance corresponding to AO and - * direct-lighting - * - * Ray state of rays that are terminated in this kernel are changed - * to RAY_UPDATE_BUFFER. - * - * Note on queues: - * This kernel fetches rays from the queue QUEUE_ACTIVE_AND_REGENERATED_RAYS - * and processes only the rays of state RAY_ACTIVE. - * There are different points in this kernel where a ray may terminate and - * reach RAY_UPDATE_BUFF state. These rays are enqueued into - * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. These rays will still be present - * in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue, but since their ray-state has - * been changed to RAY_UPDATE_BUFF, there is no problem. - * - * State of queues when this kernel is called: - * At entry, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, - * RAY_REGENERATED, RAY_UPDATE_BUFFER rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays. - * At exit, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, - * RAY_REGENERATED and more RAY_UPDATE_BUFFER rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_TO_REGENERATE and more RAY_UPDATE_BUFFER rays. - */ - -#ifdef __BRANCHED_PATH__ -ccl_device_inline void kernel_split_branched_indirect_light_init(KernelGlobals *kg, int ray_index) -{ - kernel_split_branched_path_indirect_loop_init(kg, ray_index); - - ADD_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_LIGHT_INDIRECT); -} - -ccl_device void kernel_split_branched_transparent_bounce(KernelGlobals *kg, int ray_index) -{ - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - -# ifdef __VOLUME__ - if (!(sd->flag & SD_HAS_ONLY_VOLUME)) { -# endif - /* continue in case of transparency */ - *throughput *= shader_bsdf_transparency(kg, sd); - - if (is_zero(*throughput)) { - kernel_split_path_end(kg, ray_index); - return; - } - - /* Update Path State */ - path_state_next(kg, state, LABEL_TRANSPARENT); -# ifdef __VOLUME__ - } - else { - if (!path_state_volume_next(kg, state)) { - kernel_split_path_end(kg, ray_index); - return; - } - } -# endif - - ray->P = ray_offset(sd->P, -sd->Ng); - ray->t -= sd->ray_length; /* clipping works through transparent */ - -# ifdef __RAY_DIFFERENTIALS__ - ray->dP = sd->dP; - ray->dD.dx = -sd->dI.dx; - ray->dD.dy = -sd->dI.dy; -# endif /* __RAY_DIFFERENTIALS__ */ - -# ifdef __VOLUME__ - /* enter/exit volume */ - kernel_volume_stack_enter_exit(kg, sd, state->volume_stack); -# endif /* __VOLUME__ */ -} -#endif /* __BRANCHED_PATH__ */ - -ccl_device void kernel_next_iteration_setup(KernelGlobals *kg, - ccl_local_param unsigned int *local_queue_atomics) -{ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - if (ccl_global_id(0) == 0 && ccl_global_id(1) == 0) { - /* If we are here, then it means that scene-intersect kernel - * has already been executed at least once. From the next time, - * scene-intersect kernel may operate on queues to fetch ray index - */ - *kernel_split_params.use_queues_flag = 1; - - /* Mark queue indices of QUEUE_SHADOW_RAY_CAST_AO_RAYS and - * QUEUE_SHADOW_RAY_CAST_DL_RAYS queues that were made empty during the - * previous kernel. - */ - kernel_split_params.queue_index[QUEUE_SHADOW_RAY_CAST_AO_RAYS] = 0; - kernel_split_params.queue_index[QUEUE_SHADOW_RAY_CAST_DL_RAYS] = 0; - } - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - ccl_global char *ray_state = kernel_split_state.ray_state; - -#ifdef __VOLUME__ - /* Reactivate only volume rays here, most surface work was skipped. */ - if (IS_STATE(ray_state, ray_index, RAY_HAS_ONLY_VOLUME)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_ACTIVE); - } -#endif - - bool active = IS_STATE(ray_state, ray_index, RAY_ACTIVE); - if (active) { - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - -#ifdef __BRANCHED_PATH__ - if (!kernel_data.integrator.branched || IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)) { -#endif - /* Compute direct lighting and next bounce. */ - if (!kernel_path_surface_bounce(kg, sd, throughput, state, &L->state, ray)) { - kernel_split_path_end(kg, ray_index); - } -#ifdef __BRANCHED_PATH__ - } - else if (sd->flag & SD_HAS_ONLY_VOLUME) { - kernel_split_branched_transparent_bounce(kg, ray_index); - } - else { - kernel_split_branched_indirect_light_init(kg, ray_index); - - if (kernel_split_branched_path_surface_indirect_light_iter( - kg, ray_index, 1.0f, kernel_split_sd(branched_state_sd, ray_index), true, true)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - else { - kernel_split_branched_path_indirect_loop_end(kg, ray_index); - kernel_split_branched_transparent_bounce(kg, ray_index); - } - } -#endif /* __BRANCHED_PATH__ */ - } - - /* Enqueue RAY_UPDATE_BUFFER rays. */ - enqueue_ray_index_local(ray_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - IS_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER) && active, - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); - -#ifdef __BRANCHED_PATH__ - /* iter loop */ - if (ccl_global_id(0) == 0 && ccl_global_id(1) == 0) { - kernel_split_params.queue_index[QUEUE_LIGHT_INDIRECT_ITER] = 0; - } - - ray_index = get_ray_index(kg, - ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0), - QUEUE_LIGHT_INDIRECT_ITER, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - - if (IS_STATE(ray_state, ray_index, RAY_LIGHT_INDIRECT_NEXT_ITER)) { - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - - if (kernel_split_branched_path_surface_indirect_light_iter( - kg, ray_index, 1.0f, kernel_split_sd(branched_state_sd, ray_index), true, true)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - else { - kernel_split_branched_path_indirect_loop_end(kg, ray_index); - kernel_split_branched_transparent_bounce(kg, ray_index); - } - } - -# ifdef __VOLUME__ - /* Enqueue RAY_VOLUME_INDIRECT_NEXT_ITER rays */ - ccl_barrier(CCL_LOCAL_MEM_FENCE); - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - enqueue_ray_index_local( - ray_index, - QUEUE_VOLUME_INDIRECT_ITER, - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_VOLUME_INDIRECT_NEXT_ITER), - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); - -# endif /* __VOLUME__ */ - -# ifdef __SUBSURFACE__ - /* Enqueue RAY_SUBSURFACE_INDIRECT_NEXT_ITER rays */ - ccl_barrier(CCL_LOCAL_MEM_FENCE); - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - enqueue_ray_index_local( - ray_index, - QUEUE_SUBSURFACE_INDIRECT_ITER, - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_SUBSURFACE_INDIRECT_NEXT_ITER), - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); -# endif /* __SUBSURFACE__ */ -#endif /* __BRANCHED_PATH__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_path_init.h b/intern/cycles/kernel/split/kernel_path_init.h deleted file mode 100644 index c686f46a0cd..00000000000 --- a/intern/cycles/kernel/split/kernel_path_init.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel initializes structures needed in path-iteration kernels. - * This is the first kernel in ray-tracing logic. - * - * Ray state of rays outside the tile-boundary will be marked RAY_INACTIVE - */ -ccl_device void kernel_path_init(KernelGlobals *kg) -{ - int ray_index = ccl_global_id(0) + ccl_global_id(1) * ccl_global_size(0); - - /* This is the first assignment to ray_state; - * So we don't use ASSIGN_RAY_STATE macro. - */ - kernel_split_state.ray_state[ray_index] = RAY_ACTIVE; - - /* Get work. */ - ccl_global uint *work_pools = kernel_split_params.work_pools; - uint total_work_size = kernel_split_params.total_work_size; - uint work_index; - - if (!get_next_work(kg, work_pools, total_work_size, ray_index, &work_index)) { - /* No more work, mark ray as inactive */ - kernel_split_state.ray_state[ray_index] = RAY_INACTIVE; - - return; - } - - ccl_global WorkTile *tile = &kernel_split_params.tile; - uint x, y, sample; - get_work_pixel(tile, work_index, &x, &y, &sample); - - /* Store buffer offset for writing to passes. */ - uint buffer_offset = (tile->offset + x + y * tile->stride) * kernel_data.film.pass_stride; - kernel_split_state.buffer_offset[ray_index] = buffer_offset; - - /* Initialize random numbers and ray. */ - uint rng_hash; - kernel_path_trace_setup(kg, sample, x, y, &rng_hash, &kernel_split_state.ray[ray_index]); - - if (kernel_split_state.ray[ray_index].t != 0.0f) { - /* Initialize throughput, path radiance, Ray, PathState; - * These rays proceed with path-iteration. - */ - kernel_split_state.throughput[ray_index] = make_float3(1.0f, 1.0f, 1.0f); - path_radiance_init(kg, &kernel_split_state.path_radiance[ray_index]); - path_state_init(kg, - AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]), - &kernel_split_state.path_state[ray_index], - rng_hash, - sample, - &kernel_split_state.ray[ray_index]); -#ifdef __SUBSURFACE__ - kernel_path_subsurface_init_indirect(&kernel_split_state.ss_rays[ray_index]); -#endif - } - else { - ASSIGN_RAY_STATE(kernel_split_state.ray_state, ray_index, RAY_TO_REGENERATE); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_queue_enqueue.h b/intern/cycles/kernel/split/kernel_queue_enqueue.h deleted file mode 100644 index 2db87f7a671..00000000000 --- a/intern/cycles/kernel/split/kernel_queue_enqueue.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel enqueues rays of different ray state into their - * appropriate queues: - * - * 1. Rays that have been determined to hit the background from the - * "kernel_scene_intersect" kernel are enqueued in - * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS; - * 2. Rays that have been determined to be actively participating in pat - * -iteration will be enqueued into QUEUE_ACTIVE_AND_REGENERATED_RAYS. - * - * State of queue during other times this kernel is called: - * At entry, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be empty. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will contain RAY_TO_REGENERATE - * and RAY_UPDATE_BUFFER rays. - * At exit, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_TO_REGENERATE, RAY_UPDATE_BUFFER, RAY_HIT_BACKGROUND rays. - */ -ccl_device void kernel_queue_enqueue(KernelGlobals *kg, ccl_local_param QueueEnqueueLocals *locals) -{ - /* We have only 2 cases (Hit/Not-Hit) */ - int lidx = ccl_local_id(1) * ccl_local_size(0) + ccl_local_id(0); - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - - if (lidx == 0) { - locals->queue_atomics[0] = 0; - locals->queue_atomics[1] = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int queue_number = -1; - - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_HIT_BACKGROUND) || - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_UPDATE_BUFFER) || - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_TO_REGENERATE)) { - queue_number = QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS; - } - else if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE) || - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_HAS_ONLY_VOLUME) || - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_REGENERATED)) { - queue_number = QUEUE_ACTIVE_AND_REGENERATED_RAYS; - } - - unsigned int my_lqidx; - if (queue_number != -1) { - my_lqidx = get_local_queue_index(queue_number, locals->queue_atomics); - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - if (lidx == 0) { - locals->queue_atomics[QUEUE_ACTIVE_AND_REGENERATED_RAYS] = get_global_per_queue_offset( - QUEUE_ACTIVE_AND_REGENERATED_RAYS, locals->queue_atomics, kernel_split_params.queue_index); - locals->queue_atomics[QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS] = get_global_per_queue_offset( - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - locals->queue_atomics, - kernel_split_params.queue_index); - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - unsigned int my_gqidx; - if (queue_number != -1) { - my_gqidx = get_global_queue_index( - queue_number, kernel_split_params.queue_size, my_lqidx, locals->queue_atomics); - kernel_split_state.queue_data[my_gqidx] = ray_index; - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_scene_intersect.h b/intern/cycles/kernel/split/kernel_scene_intersect.h deleted file mode 100644 index 9ac95aafd2f..00000000000 --- a/intern/cycles/kernel/split/kernel_scene_intersect.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel takes care of scene_intersect function. - * - * This kernel changes the ray_state of RAY_REGENERATED rays to RAY_ACTIVE. - * This kernel processes rays of ray state RAY_ACTIVE - * This kernel determines the rays that have hit the background and changes - * their ray state to RAY_HIT_BACKGROUND. - */ -ccl_device void kernel_scene_intersect(KernelGlobals *kg) -{ - /* Fetch use_queues_flag */ - char local_use_queues_flag = *kernel_split_params.use_queues_flag; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (local_use_queues_flag) { - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - } - - /* All regenerated rays become active here */ - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_REGENERATED)) { -#ifdef __BRANCHED_PATH__ - if (kernel_split_state.branched_state[ray_index].waiting_on_shared_samples) { - kernel_split_path_end(kg, ray_index); - } - else -#endif /* __BRANCHED_PATH__ */ - { - ASSIGN_RAY_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE); - } - } - - if (!IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE)) { - return; - } - - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - Ray ray = kernel_split_state.ray[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - - Intersection isect; - const int last_object = state->bounce > 0 ? - intersection_get_object(kg, &kernel_split_state.isect[ray_index]) : - OBJECT_NONE; - bool hit = kernel_path_scene_intersect(kg, state, &ray, &isect, L, last_object); - kernel_split_state.isect[ray_index] = isect; - - if (!hit) { - /* Change the state of rays that hit the background; - * These rays undergo special processing in the - * background_bufferUpdate kernel. - */ - ASSIGN_RAY_STATE(kernel_split_state.ray_state, ray_index, RAY_HIT_BACKGROUND); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_shader_eval.h b/intern/cycles/kernel/split/kernel_shader_eval.h deleted file mode 100644 index c760a2b2049..00000000000 --- a/intern/cycles/kernel/split/kernel_shader_eval.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel evaluates ShaderData structure from the values computed - * by the previous kernels. - */ -ccl_device void kernel_shader_eval(KernelGlobals *kg) -{ - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - /* Sorting on cuda split is not implemented */ -#ifdef __KERNEL_CUDA__ - int queue_index = kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS]; -#else - int queue_index = kernel_split_params.queue_index[QUEUE_SHADER_SORTED_RAYS]; -#endif - if (ray_index >= queue_index) { - return; - } - ray_index = get_ray_index(kg, - ray_index, -#ifdef __KERNEL_CUDA__ - QUEUE_ACTIVE_AND_REGENERATED_RAYS, -#else - QUEUE_SHADER_SORTED_RAYS, -#endif - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - - ccl_global char *ray_state = kernel_split_state.ray_state; - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - - shader_eval_surface(kg, kernel_split_sd(sd, ray_index), state, buffer, state->flag); -#ifdef __BRANCHED_PATH__ - if (kernel_data.integrator.branched) { - shader_merge_closures(kernel_split_sd(sd, ray_index)); - } - else -#endif - { - shader_prepare_closures(kernel_split_sd(sd, ray_index), state); - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_shader_setup.h b/intern/cycles/kernel/split/kernel_shader_setup.h deleted file mode 100644 index 551836d1653..00000000000 --- a/intern/cycles/kernel/split/kernel_shader_setup.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel sets up the ShaderData structure from the values computed - * by the previous kernels. - * - * It also identifies the rays of state RAY_TO_REGENERATE and enqueues them - * in QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. - */ -ccl_device void kernel_shader_setup(KernelGlobals *kg, - ccl_local_param unsigned int *local_queue_atomics) -{ - /* Enqueue RAY_TO_REGENERATE rays into QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. */ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - int queue_index = kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS]; - if (ray_index < queue_index) { - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - } - else { - ray_index = QUEUE_EMPTY_SLOT; - } - - char enqueue_flag = (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_TO_REGENERATE)) ? 1 : - 0; - enqueue_ray_index_local(ray_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - enqueue_flag, - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); - - /* Continue on with shader evaluation. */ - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE)) { - Intersection isect = kernel_split_state.isect[ray_index]; - Ray ray = kernel_split_state.ray[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - - shader_setup_from_ray(kg, sd, &isect, &ray); - -#ifdef __VOLUME__ - if (sd->flag & SD_HAS_ONLY_VOLUME) { - ASSIGN_RAY_STATE(kernel_split_state.ray_state, ray_index, RAY_HAS_ONLY_VOLUME); - } -#endif - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_shader_sort.h b/intern/cycles/kernel/split/kernel_shader_sort.h deleted file mode 100644 index 95d33a42014..00000000000 --- a/intern/cycles/kernel/split/kernel_shader_sort.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_shader_sort(KernelGlobals *kg, ccl_local_param ShaderSortLocals *locals) -{ -#ifndef __KERNEL_CUDA__ - int tid = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - uint qsize = kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS]; - if (tid == 0) { - kernel_split_params.queue_index[QUEUE_SHADER_SORTED_RAYS] = qsize; - } - - uint offset = (tid / SHADER_SORT_LOCAL_SIZE) * SHADER_SORT_BLOCK_SIZE; - if (offset >= qsize) { - return; - } - - int lid = ccl_local_id(1) * ccl_local_size(0) + ccl_local_id(0); - uint input = QUEUE_ACTIVE_AND_REGENERATED_RAYS * (kernel_split_params.queue_size); - uint output = QUEUE_SHADER_SORTED_RAYS * (kernel_split_params.queue_size); - ccl_local uint *local_value = &locals->local_value[0]; - ccl_local ushort *local_index = &locals->local_index[0]; - - /* copy to local memory */ - for (uint i = 0; i < SHADER_SORT_BLOCK_SIZE; i += SHADER_SORT_LOCAL_SIZE) { - uint idx = offset + i + lid; - uint add = input + idx; - uint value = (~0); - if (idx < qsize) { - int ray_index = kernel_split_state.queue_data[add]; - bool valid = (ray_index != QUEUE_EMPTY_SLOT) && - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE); - if (valid) { - value = kernel_split_sd(sd, ray_index)->shader & SHADER_MASK; - } - } - local_value[i + lid] = value; - local_index[i + lid] = i + lid; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - /* skip sorting for cpu split kernel */ -# ifdef __KERNEL_OPENCL__ - - /* bitonic sort */ - for (uint length = 1; length < SHADER_SORT_BLOCK_SIZE; length <<= 1) { - for (uint inc = length; inc > 0; inc >>= 1) { - for (uint ii = 0; ii < SHADER_SORT_BLOCK_SIZE; ii += SHADER_SORT_LOCAL_SIZE) { - uint i = lid + ii; - bool direction = ((i & (length << 1)) != 0); - uint j = i ^ inc; - ushort ioff = local_index[i]; - ushort joff = local_index[j]; - uint iKey = local_value[ioff]; - uint jKey = local_value[joff]; - bool smaller = (jKey < iKey) || (jKey == iKey && j < i); - bool swap = smaller ^ (j < i) ^ direction; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - local_index[i] = (swap) ? joff : ioff; - local_index[j] = (swap) ? ioff : joff; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - } - } - } -# endif /* __KERNEL_OPENCL__ */ - - /* copy to destination */ - for (uint i = 0; i < SHADER_SORT_BLOCK_SIZE; i += SHADER_SORT_LOCAL_SIZE) { - uint idx = offset + i + lid; - uint lidx = local_index[i + lid]; - uint outi = output + idx; - uint ini = input + offset + lidx; - uint value = local_value[lidx]; - if (idx < qsize) { - kernel_split_state.queue_data[outi] = (value == (~0)) ? QUEUE_EMPTY_SLOT : - kernel_split_state.queue_data[ini]; - } - } -#endif /* __KERNEL_CUDA__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_shadow_blocked_ao.h b/intern/cycles/kernel/split/kernel_shadow_blocked_ao.h deleted file mode 100644 index 5d772fc597b..00000000000 --- a/intern/cycles/kernel/split/kernel_shadow_blocked_ao.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* Shadow ray cast for AO. */ -ccl_device void kernel_shadow_blocked_ao(KernelGlobals *kg) -{ - unsigned int ao_queue_length = kernel_split_params.queue_index[QUEUE_SHADOW_RAY_CAST_AO_RAYS]; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = QUEUE_EMPTY_SLOT; - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (thread_index < ao_queue_length) { - ray_index = get_ray_index(kg, - thread_index, - QUEUE_SHADOW_RAY_CAST_AO_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - } - - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - - ShaderData *sd = kernel_split_sd(sd, ray_index); - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - float3 throughput = kernel_split_state.throughput[ray_index]; - -#ifdef __BRANCHED_PATH__ - if (!kernel_data.integrator.branched || - IS_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_INDIRECT)) { -#endif - kernel_path_ao(kg, sd, emission_sd, L, state, throughput, shader_bsdf_alpha(kg, sd)); -#ifdef __BRANCHED_PATH__ - } - else { - kernel_branched_path_ao(kg, sd, emission_sd, L, state, throughput); - } -#endif -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_shadow_blocked_dl.h b/intern/cycles/kernel/split/kernel_shadow_blocked_dl.h deleted file mode 100644 index 5e46d300bca..00000000000 --- a/intern/cycles/kernel/split/kernel_shadow_blocked_dl.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* Shadow ray cast for direct visible light. */ -ccl_device void kernel_shadow_blocked_dl(KernelGlobals *kg) -{ - unsigned int dl_queue_length = kernel_split_params.queue_index[QUEUE_SHADOW_RAY_CAST_DL_RAYS]; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = QUEUE_EMPTY_SLOT; - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (thread_index < dl_queue_length) { - ray_index = get_ray_index(kg, - thread_index, - QUEUE_SHADOW_RAY_CAST_DL_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - } - -#ifdef __BRANCHED_PATH__ - /* TODO(mai): move this somewhere else? */ - if (thread_index == 0) { - /* Clear QUEUE_INACTIVE_RAYS before next kernel. */ - kernel_split_params.queue_index[QUEUE_INACTIVE_RAYS] = 0; - } -#endif /* __BRANCHED_PATH__ */ - - if (ray_index == QUEUE_EMPTY_SLOT) - return; - - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - Ray ray = kernel_split_state.light_ray[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - float3 throughput = kernel_split_state.throughput[ray_index]; - - BsdfEval L_light = kernel_split_state.bsdf_eval[ray_index]; - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - bool is_lamp = kernel_split_state.is_lamp[ray_index]; - -#if defined(__BRANCHED_PATH__) || defined(__SHADOW_TRICKS__) - bool use_branched = false; - int all = 0; - - if (state->flag & PATH_RAY_SHADOW_CATCHER) { - use_branched = true; - all = 1; - } -# if defined(__BRANCHED_PATH__) - else if (kernel_data.integrator.branched) { - use_branched = true; - - if (IS_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_INDIRECT)) { - all = (kernel_data.integrator.sample_all_lights_indirect); - } - else { - all = (kernel_data.integrator.sample_all_lights_direct); - } - } -# endif /* __BRANCHED_PATH__ */ - - if (use_branched) { - kernel_branched_path_surface_connect_light( - kg, sd, emission_sd, state, throughput, 1.0f, L, all); - } - else -#endif /* defined(__BRANCHED_PATH__) || defined(__SHADOW_TRICKS__)*/ - { - /* trace shadow ray */ - float3 shadow; - - if (!shadow_blocked(kg, sd, emission_sd, state, &ray, &shadow)) { - /* accumulate */ - path_radiance_accum_light(kg, L, state, throughput, &L_light, shadow, 1.0f, is_lamp); - } - else { - path_radiance_accum_total_light(L, state, throughput, &L_light); - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_split_common.h b/intern/cycles/kernel/split/kernel_split_common.h deleted file mode 100644 index 5114f2b03e5..00000000000 --- a/intern/cycles/kernel/split/kernel_split_common.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_SPLIT_H__ -#define __KERNEL_SPLIT_H__ - -// clang-format off -#include "kernel/kernel_math.h" -#include "kernel/kernel_types.h" - -#include "kernel/split/kernel_split_data.h" - -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" - -#ifdef __OSL__ -# include "kernel/osl/osl_shader.h" -#endif - -#ifdef __KERNEL_OPENCL__ -# include "kernel/kernels/opencl/kernel_opencl_image.h" -#endif -#ifdef __KERNEL_CUDA__ -# include "kernel/kernels/cuda/kernel_cuda_image.h" -#endif -#ifdef __KERNEL_CPU__ -# include "kernel/kernels/cpu/kernel_cpu_image.h" -#endif - -#include "util/util_atomic.h" - -#include "kernel/kernel_path.h" -#ifdef __BRANCHED_PATH__ -# include "kernel/kernel_path_branched.h" -#endif - -#include "kernel/kernel_queues.h" -#include "kernel/kernel_work_stealing.h" - -#ifdef __BRANCHED_PATH__ -# include "kernel/split/kernel_branched.h" -#endif -// clang-format on - -CCL_NAMESPACE_BEGIN - -ccl_device_inline void kernel_split_path_end(KernelGlobals *kg, int ray_index) -{ - ccl_global char *ray_state = kernel_split_state.ray_state; - -#ifdef __BRANCHED_PATH__ -# ifdef __SUBSURFACE__ - ccl_addr_space SubsurfaceIndirectRays *ss_indirect = &kernel_split_state.ss_rays[ray_index]; - - if (ss_indirect->num_rays) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER); - } - else -# endif /* __SUBSURFACE__ */ - if (IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT_SHARED)) { - int orig_ray = kernel_split_state.branched_state[ray_index].original_ray; - - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - PathRadiance *orig_ray_L = &kernel_split_state.path_radiance[orig_ray]; - - path_radiance_sum_indirect(L); - path_radiance_accum_sample(orig_ray_L, L); - - atomic_fetch_and_dec_uint32( - (ccl_global uint *)&kernel_split_state.branched_state[orig_ray].shared_sample_count); - - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_INACTIVE); - } - else if (IS_FLAG(ray_state, ray_index, RAY_BRANCHED_LIGHT_INDIRECT)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_LIGHT_INDIRECT_NEXT_ITER); - } - else if (IS_FLAG(ray_state, ray_index, RAY_BRANCHED_VOLUME_INDIRECT)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_VOLUME_INDIRECT_NEXT_ITER); - } - else if (IS_FLAG(ray_state, ray_index, RAY_BRANCHED_SUBSURFACE_INDIRECT)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_SUBSURFACE_INDIRECT_NEXT_ITER); - } - else { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER); - } -#else - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER); -#endif -} - -CCL_NAMESPACE_END - -#endif /* __KERNEL_SPLIT_H__ */ diff --git a/intern/cycles/kernel/split/kernel_split_data.h b/intern/cycles/kernel/split/kernel_split_data.h deleted file mode 100644 index decc537b39b..00000000000 --- a/intern/cycles/kernel/split/kernel_split_data.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_SPLIT_DATA_H__ -#define __KERNEL_SPLIT_DATA_H__ - -#include "kernel/split/kernel_split_data_types.h" - -#include "kernel/kernel_globals.h" - -CCL_NAMESPACE_BEGIN - -ccl_device_inline uint64_t split_data_buffer_size(KernelGlobals *kg, size_t num_elements) -{ - (void)kg; /* Unused on CPU. */ - - uint64_t size = 0; -#define SPLIT_DATA_ENTRY(type, name, num) +align_up(num_elements *num * sizeof(type), 16) - size = size SPLIT_DATA_ENTRIES; -#undef SPLIT_DATA_ENTRY - - uint64_t closure_size = sizeof(ShaderClosure) * (kernel_data.integrator.max_closures - 1); - -#ifdef __BRANCHED_PATH__ - size += align_up(num_elements * (sizeof(ShaderData) + closure_size), 16); -#endif - - size += align_up(num_elements * (sizeof(ShaderData) + closure_size), 16); - - return size; -} - -ccl_device_inline void split_data_init(KernelGlobals *kg, - ccl_global SplitData *split_data, - size_t num_elements, - ccl_global void *data, - ccl_global char *ray_state) -{ - (void)kg; /* Unused on CPU. */ - - ccl_global char *p = (ccl_global char *)data; - -#define SPLIT_DATA_ENTRY(type, name, num) \ - split_data->name = (type *)p; \ - p += align_up(num_elements * num * sizeof(type), 16); - SPLIT_DATA_ENTRIES; -#undef SPLIT_DATA_ENTRY - - uint64_t closure_size = sizeof(ShaderClosure) * (kernel_data.integrator.max_closures - 1); - -#ifdef __BRANCHED_PATH__ - split_data->_branched_state_sd = (ShaderData *)p; - p += align_up(num_elements * (sizeof(ShaderData) + closure_size), 16); -#endif - - split_data->_sd = (ShaderData *)p; - p += align_up(num_elements * (sizeof(ShaderData) + closure_size), 16); - - split_data->ray_state = ray_state; -} - -CCL_NAMESPACE_END - -#endif /* __KERNEL_SPLIT_DATA_H__ */ diff --git a/intern/cycles/kernel/split/kernel_split_data_types.h b/intern/cycles/kernel/split/kernel_split_data_types.h deleted file mode 100644 index 06bdce9947d..00000000000 --- a/intern/cycles/kernel/split/kernel_split_data_types.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_SPLIT_DATA_TYPES_H__ -#define __KERNEL_SPLIT_DATA_TYPES_H__ - -CCL_NAMESPACE_BEGIN - -/* parameters used by the split kernels, we use a single struct to avoid passing these to each - * kernel */ - -typedef struct SplitParams { - WorkTile tile; - uint total_work_size; - - ccl_global unsigned int *work_pools; - - ccl_global int *queue_index; - int queue_size; - ccl_global char *use_queues_flag; - - /* Place for storing sd->flag. AMD GPU OpenCL compiler workaround */ - int dummy_sd_flag; -} SplitParams; - -/* Global memory variables [porting]; These memory is used for - * co-operation between different kernels; Data written by one - * kernel will be available to another kernel via this global - * memory. - */ - -/* SPLIT_DATA_ENTRY(type, name, num) */ - -#ifdef __BRANCHED_PATH__ - -typedef ccl_global struct SplitBranchedState { - /* various state that must be kept and restored after an indirect loop */ - PathState path_state; - float3 throughput; - Ray ray; - - Intersection isect; - - char ray_state; - - /* indirect loop state */ - int next_closure; - int next_sample; - -# ifdef __SUBSURFACE__ - int ss_next_closure; - int ss_next_sample; - int next_hit; - int num_hits; - - uint lcg_state; - LocalIntersection ss_isect; -# endif /* __SUBSURFACE__ */ - - int shared_sample_count; /* number of branched samples shared with other threads */ - int original_ray; /* index of original ray when sharing branched samples */ - bool waiting_on_shared_samples; -} SplitBranchedState; - -# define SPLIT_DATA_BRANCHED_ENTRIES \ - SPLIT_DATA_ENTRY(SplitBranchedState, branched_state, 1) \ - SPLIT_DATA_ENTRY(ShaderData, _branched_state_sd, 0) -#else -# define SPLIT_DATA_BRANCHED_ENTRIES -#endif /* __BRANCHED_PATH__ */ - -#ifdef __SUBSURFACE__ -# define SPLIT_DATA_SUBSURFACE_ENTRIES \ - SPLIT_DATA_ENTRY(ccl_global SubsurfaceIndirectRays, ss_rays, 1) -#else -# define SPLIT_DATA_SUBSURFACE_ENTRIES -#endif /* __SUBSURFACE__ */ - -#ifdef __VOLUME__ -# define SPLIT_DATA_VOLUME_ENTRIES SPLIT_DATA_ENTRY(ccl_global PathState, state_shadow, 1) -#else -# define SPLIT_DATA_VOLUME_ENTRIES -#endif /* __VOLUME__ */ - -#define SPLIT_DATA_ENTRIES \ - SPLIT_DATA_ENTRY(ccl_global float3, throughput, 1) \ - SPLIT_DATA_ENTRY(PathRadiance, path_radiance, 1) \ - SPLIT_DATA_ENTRY(ccl_global Ray, ray, 1) \ - SPLIT_DATA_ENTRY(ccl_global PathState, path_state, 1) \ - SPLIT_DATA_ENTRY(ccl_global Intersection, isect, 1) \ - SPLIT_DATA_ENTRY(ccl_global BsdfEval, bsdf_eval, 1) \ - SPLIT_DATA_ENTRY(ccl_global int, is_lamp, 1) \ - SPLIT_DATA_ENTRY(ccl_global Ray, light_ray, 1) \ - SPLIT_DATA_ENTRY( \ - ccl_global int, queue_data, (NUM_QUEUES * 2)) /* TODO(mai): this is too large? */ \ - SPLIT_DATA_ENTRY(ccl_global uint, buffer_offset, 1) \ - SPLIT_DATA_ENTRY(ShaderDataTinyStorage, sd_DL_shadow, 1) \ - SPLIT_DATA_SUBSURFACE_ENTRIES \ - SPLIT_DATA_VOLUME_ENTRIES \ - SPLIT_DATA_BRANCHED_ENTRIES \ - SPLIT_DATA_ENTRY(ShaderData, _sd, 0) - -/* Entries to be copied to inactive rays when sharing branched samples - * (TODO: which are actually needed?) */ -#define SPLIT_DATA_ENTRIES_BRANCHED_SHARED \ - SPLIT_DATA_ENTRY(ccl_global float3, throughput, 1) \ - SPLIT_DATA_ENTRY(PathRadiance, path_radiance, 1) \ - SPLIT_DATA_ENTRY(ccl_global Ray, ray, 1) \ - SPLIT_DATA_ENTRY(ccl_global PathState, path_state, 1) \ - SPLIT_DATA_ENTRY(ccl_global Intersection, isect, 1) \ - SPLIT_DATA_ENTRY(ccl_global BsdfEval, bsdf_eval, 1) \ - SPLIT_DATA_ENTRY(ccl_global int, is_lamp, 1) \ - SPLIT_DATA_ENTRY(ccl_global Ray, light_ray, 1) \ - SPLIT_DATA_ENTRY(ShaderDataTinyStorage, sd_DL_shadow, 1) \ - SPLIT_DATA_SUBSURFACE_ENTRIES \ - SPLIT_DATA_VOLUME_ENTRIES \ - SPLIT_DATA_BRANCHED_ENTRIES \ - SPLIT_DATA_ENTRY(ShaderData, _sd, 0) - -/* struct that holds pointers to data in the shared state buffer */ -typedef struct SplitData { -#define SPLIT_DATA_ENTRY(type, name, num) type *name; - SPLIT_DATA_ENTRIES -#undef SPLIT_DATA_ENTRY - - /* this is actually in a separate buffer from the rest of the split state data (so it can be read - * back from the host easily) but is still used the same as the other data so we have it here in - * this struct as well - */ - ccl_global char *ray_state; -} SplitData; - -#ifndef __KERNEL_CUDA__ -# define kernel_split_state (kg->split_data) -# define kernel_split_params (kg->split_param_data) -#else -__device__ SplitData __split_data; -# define kernel_split_state (__split_data) -__device__ SplitParams __split_param_data; -# define kernel_split_params (__split_param_data) -#endif /* __KERNEL_CUDA__ */ - -#define kernel_split_sd(sd, ray_index) \ - ((ShaderData *)(((ccl_global char *)kernel_split_state._##sd) + \ - (sizeof(ShaderData) + \ - sizeof(ShaderClosure) * (kernel_data.integrator.max_closures - 1)) * \ - (ray_index))) - -/* Local storage for queue_enqueue kernel. */ -typedef struct QueueEnqueueLocals { - uint queue_atomics[2]; -} QueueEnqueueLocals; - -/* Local storage for holdout_emission_blurring_pathtermination_ao kernel. */ -typedef struct BackgroundAOLocals { - uint queue_atomics_bg; - uint queue_atomics_ao; -} BackgroundAOLocals; - -typedef struct ShaderSortLocals { - uint local_value[SHADER_SORT_BLOCK_SIZE]; - ushort local_index[SHADER_SORT_BLOCK_SIZE]; -} ShaderSortLocals; - -CCL_NAMESPACE_END - -#endif /* __KERNEL_SPLIT_DATA_TYPES_H__ */ diff --git a/intern/cycles/kernel/split/kernel_subsurface_scatter.h b/intern/cycles/kernel/split/kernel_subsurface_scatter.h deleted file mode 100644 index ba06ae3bc53..00000000000 --- a/intern/cycles/kernel/split/kernel_subsurface_scatter.h +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#if defined(__BRANCHED_PATH__) && defined(__SUBSURFACE__) - -ccl_device_inline void kernel_split_branched_path_subsurface_indirect_light_init(KernelGlobals *kg, - int ray_index) -{ - kernel_split_branched_path_indirect_loop_init(kg, ray_index); - - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - branched_state->ss_next_closure = 0; - branched_state->ss_next_sample = 0; - - branched_state->num_hits = 0; - branched_state->next_hit = 0; - - ADD_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_SUBSURFACE_INDIRECT); -} - -ccl_device_noinline bool kernel_split_branched_path_subsurface_indirect_light_iter( - KernelGlobals *kg, int ray_index) -{ - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - ShaderData *sd = kernel_split_sd(branched_state_sd, ray_index); - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - - for (int i = branched_state->ss_next_closure; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (!CLOSURE_IS_BSSRDF(sc->type)) - continue; - - /* Closure memory will be overwritten, so read required variables now. */ - Bssrdf *bssrdf = (Bssrdf *)sc; - ClosureType bssrdf_type = sc->type; - float bssrdf_roughness = bssrdf->roughness; - - /* set up random number generator */ - if (branched_state->ss_next_sample == 0 && branched_state->next_hit == 0 && - branched_state->next_closure == 0 && branched_state->next_sample == 0) { - branched_state->lcg_state = lcg_state_init_addrspace(&branched_state->path_state, - 0x68bc21eb); - } - int num_samples = kernel_data.integrator.subsurface_samples * 3; - float num_samples_inv = 1.0f / num_samples; - uint bssrdf_rng_hash = cmj_hash(branched_state->path_state.rng_hash, i); - - /* do subsurface scatter step with copy of shader data, this will - * replace the BSSRDF with a diffuse BSDF closure */ - for (int j = branched_state->ss_next_sample; j < num_samples; j++) { - ccl_global PathState *hit_state = &kernel_split_state.path_state[ray_index]; - *hit_state = branched_state->path_state; - hit_state->rng_hash = bssrdf_rng_hash; - path_state_branch(hit_state, j, num_samples); - - ccl_global LocalIntersection *ss_isect = &branched_state->ss_isect; - float bssrdf_u, bssrdf_v; - path_branched_rng_2D( - kg, bssrdf_rng_hash, hit_state, j, num_samples, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); - - /* intersection is expensive so avoid doing multiple times for the same input */ - if (branched_state->next_hit == 0 && branched_state->next_closure == 0 && - branched_state->next_sample == 0) { - uint lcg_state = branched_state->lcg_state; - LocalIntersection ss_isect_private; - - branched_state->num_hits = subsurface_scatter_multi_intersect( - kg, &ss_isect_private, sd, hit_state, sc, &lcg_state, bssrdf_u, bssrdf_v, true); - - branched_state->lcg_state = lcg_state; - *ss_isect = ss_isect_private; - } - - hit_state->rng_offset += PRNG_BOUNCE_NUM; - -# ifdef __VOLUME__ - Ray volume_ray = branched_state->ray; - bool need_update_volume_stack = kernel_data.integrator.use_volumes && - sd->object_flag & SD_OBJECT_INTERSECTS_VOLUME; -# endif /* __VOLUME__ */ - - /* compute lighting with the BSDF closure */ - for (int hit = branched_state->next_hit; hit < branched_state->num_hits; hit++) { - ShaderData *bssrdf_sd = kernel_split_sd(sd, ray_index); - *bssrdf_sd = *sd; /* note: copy happens each iteration of inner loop, this is - * important as the indirect path will write into bssrdf_sd */ - - LocalIntersection ss_isect_private = *ss_isect; - subsurface_scatter_multi_setup( - kg, &ss_isect_private, hit, bssrdf_sd, hit_state, bssrdf_type, bssrdf_roughness); - *ss_isect = ss_isect_private; - -# ifdef __VOLUME__ - if (need_update_volume_stack) { - /* Setup ray from previous surface point to the new one. */ - float3 P = ray_offset(bssrdf_sd->P, -bssrdf_sd->Ng); - volume_ray.D = normalize_len(P - volume_ray.P, &volume_ray.t); - - for (int k = 0; k < VOLUME_STACK_SIZE; k++) { - hit_state->volume_stack[k] = branched_state->path_state.volume_stack[k]; - } - - kernel_volume_stack_update_for_subsurface( - kg, emission_sd, &volume_ray, hit_state->volume_stack); - } -# endif /* __VOLUME__ */ - -# ifdef __EMISSION__ - if (branched_state->next_closure == 0 && branched_state->next_sample == 0) { - /* direct light */ - if (kernel_data.integrator.use_direct_light) { - int all = (kernel_data.integrator.sample_all_lights_direct) || - (hit_state->flag & PATH_RAY_SHADOW_CATCHER); - kernel_branched_path_surface_connect_light(kg, - bssrdf_sd, - emission_sd, - hit_state, - branched_state->throughput, - num_samples_inv, - L, - all); - } - } -# endif /* __EMISSION__ */ - - /* indirect light */ - if (kernel_split_branched_path_surface_indirect_light_iter( - kg, ray_index, num_samples_inv, bssrdf_sd, false, false)) { - branched_state->ss_next_closure = i; - branched_state->ss_next_sample = j; - branched_state->next_hit = hit; - - return true; - } - - branched_state->next_closure = 0; - } - - branched_state->next_hit = 0; - } - - branched_state->ss_next_sample = 0; - } - - branched_state->ss_next_closure = sd->num_closure; - - branched_state->waiting_on_shared_samples = (branched_state->shared_sample_count > 0); - if (branched_state->waiting_on_shared_samples) { - return true; - } - - kernel_split_branched_path_indirect_loop_end(kg, ray_index); - - return false; -} - -#endif /* __BRANCHED_PATH__ && __SUBSURFACE__ */ - -ccl_device void kernel_subsurface_scatter(KernelGlobals *kg) -{ - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (thread_index == 0) { - /* We will empty both queues in this kernel. */ - kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS] = 0; - kernel_split_params.queue_index[QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS] = 0; - } - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - get_ray_index(kg, - thread_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - -#ifdef __SUBSURFACE__ - ccl_global char *ray_state = kernel_split_state.ray_state; - - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - ccl_global SubsurfaceIndirectRays *ss_indirect = &kernel_split_state.ss_rays[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - - if (sd->flag & SD_BSSRDF) { - -# ifdef __BRANCHED_PATH__ - if (!kernel_data.integrator.branched || - IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)) { -# endif - if (kernel_path_subsurface_scatter( - kg, sd, emission_sd, L, state, ray, throughput, ss_indirect)) { - kernel_split_path_end(kg, ray_index); - } -# ifdef __BRANCHED_PATH__ - } - else { - kernel_split_branched_path_subsurface_indirect_light_init(kg, ray_index); - - if (kernel_split_branched_path_subsurface_indirect_light_iter(kg, ray_index)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - } -# endif - } - } - -# ifdef __BRANCHED_PATH__ - if (ccl_global_id(0) == 0 && ccl_global_id(1) == 0) { - kernel_split_params.queue_index[QUEUE_SUBSURFACE_INDIRECT_ITER] = 0; - } - - /* iter loop */ - ray_index = get_ray_index(kg, - ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0), - QUEUE_SUBSURFACE_INDIRECT_ITER, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - - if (IS_STATE(ray_state, ray_index, RAY_SUBSURFACE_INDIRECT_NEXT_ITER)) { - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - path_radiance_sum_indirect(&kernel_split_state.path_radiance[ray_index]); - path_radiance_reset_indirect(&kernel_split_state.path_radiance[ray_index]); - - if (kernel_split_branched_path_subsurface_indirect_light_iter(kg, ray_index)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - } -# endif /* __BRANCHED_PATH__ */ - -#endif /* __SUBSURFACE__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 000da1fa615..4aee1ef11b3 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -48,16 +48,18 @@ ccl_device_inline float3 stack_load_float3(float *stack, uint a) { kernel_assert(a + 2 < SVM_STACK_SIZE); - return make_float3(stack[a + 0], stack[a + 1], stack[a + 2]); + float *stack_a = stack + a; + return make_float3(stack_a[0], stack_a[1], stack_a[2]); } ccl_device_inline void stack_store_float3(float *stack, uint a, float3 f) { kernel_assert(a + 2 < SVM_STACK_SIZE); - stack[a + 0] = f.x; - stack[a + 1] = f.y; - stack[a + 2] = f.z; + float *stack_a = stack + a; + stack_a[0] = f.x; + stack_a[1] = f.y; + stack_a[2] = f.z; } ccl_device_inline float stack_load_float(float *stack, uint a) @@ -105,14 +107,14 @@ ccl_device_inline bool stack_valid(uint a) /* Reading Nodes */ -ccl_device_inline uint4 read_node(KernelGlobals *kg, int *offset) +ccl_device_inline uint4 read_node(const KernelGlobals *kg, int *offset) { uint4 node = kernel_tex_fetch(__svm_nodes, *offset); (*offset)++; return node; } -ccl_device_inline float4 read_node_float(KernelGlobals *kg, int *offset) +ccl_device_inline float4 read_node_float(const KernelGlobals *kg, int *offset) { uint4 node = kernel_tex_fetch(__svm_nodes, *offset); float4 f = make_float4(__uint_as_float(node.x), @@ -123,7 +125,7 @@ ccl_device_inline float4 read_node_float(KernelGlobals *kg, int *offset) return f; } -ccl_device_inline float4 fetch_node_float(KernelGlobals *kg, int offset) +ccl_device_inline float4 fetch_node_float(const KernelGlobals *kg, int offset) { uint4 node = kernel_tex_fetch(__svm_nodes, offset); return make_float4(__uint_as_float(node.x), @@ -217,26 +219,11 @@ CCL_NAMESPACE_END CCL_NAMESPACE_BEGIN /* Main Interpreter Loop */ -#if defined(__KERNEL_OPTIX__) && defined(__SHADER_RAYTRACE__) -ccl_device_inline void svm_eval_nodes(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - ccl_global float *buffer, - ShaderType type, - int path_flag) -{ - optixDirectCall(0, kg, sd, state, buffer, type, path_flag); -} -extern "C" __device__ void __direct_callable__svm_eval_nodes( -#else -ccl_device_noinline void svm_eval_nodes( -#endif - KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - ccl_global float *buffer, - ShaderType type, - int path_flag) +template +ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, + ShaderData *sd, + ccl_global float *render_buffer, + int path_flag) { float stack[SVM_STACK_SIZE]; int offset = sd->shader & SHADER_MASK; @@ -247,7 +234,6 @@ ccl_device_noinline void svm_eval_nodes( switch (node.x) { case NODE_END: return; -#if NODES_GROUP(NODE_GROUP_LEVEL_0) case NODE_SHADER_JUMP: { if (type == SHADER_TYPE_SURFACE) offset = node.y; @@ -260,13 +246,18 @@ ccl_device_noinline void svm_eval_nodes( break; } case NODE_CLOSURE_BSDF: - svm_node_closure_bsdf(kg, sd, stack, node, type, path_flag, &offset); + offset = svm_node_closure_bsdf( + kg, sd, stack, node, path_flag, offset); break; case NODE_CLOSURE_EMISSION: - svm_node_closure_emission(sd, stack, node); + if (KERNEL_NODES_FEATURE(EMISSION)) { + svm_node_closure_emission(sd, stack, node); + } break; case NODE_CLOSURE_BACKGROUND: - svm_node_closure_background(sd, stack, node); + if (KERNEL_NODES_FEATURE(EMISSION)) { + svm_node_closure_background(sd, stack, node); + } break; case NODE_CLOSURE_SET_WEIGHT: svm_node_closure_set_weight(sd, node.y, node.z, node.w); @@ -275,7 +266,9 @@ ccl_device_noinline void svm_eval_nodes( svm_node_closure_weight(sd, stack, node.y); break; case NODE_EMISSION_WEIGHT: - svm_node_emission_weight(kg, sd, stack, node); + if (KERNEL_NODES_FEATURE(EMISSION)) { + svm_node_emission_weight(kg, sd, stack, node); + } break; case NODE_MIX_CLOSURE: svm_node_mix_closure(sd, stack, node); @@ -295,86 +288,108 @@ ccl_device_noinline void svm_eval_nodes( svm_node_convert(kg, sd, stack, node.y, node.z, node.w); break; case NODE_TEX_COORD: - svm_node_tex_coord(kg, sd, path_flag, stack, node, &offset); + offset = svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); break; case NODE_VALUE_F: svm_node_value_f(kg, sd, stack, node.y, node.z); break; case NODE_VALUE_V: - svm_node_value_v(kg, sd, stack, node.y, &offset); + offset = svm_node_value_v(kg, sd, stack, node.y, offset); break; case NODE_ATTR: - svm_node_attr(kg, sd, stack, node); + svm_node_attr(kg, sd, stack, node); break; case NODE_VERTEX_COLOR: svm_node_vertex_color(kg, sd, stack, node.y, node.z, node.w); break; -# if NODES_FEATURE(NODE_FEATURE_BUMP) case NODE_GEOMETRY_BUMP_DX: - svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z); + if (KERNEL_NODES_FEATURE(BUMP)) { + svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z); + } break; case NODE_GEOMETRY_BUMP_DY: - svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z); + if (KERNEL_NODES_FEATURE(BUMP)) { + svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z); + } break; case NODE_SET_DISPLACEMENT: - svm_node_set_displacement(kg, sd, stack, node.y); + if (KERNEL_NODES_FEATURE(BUMP)) { + svm_node_set_displacement(kg, sd, stack, node.y); + } break; case NODE_DISPLACEMENT: - svm_node_displacement(kg, sd, stack, node); + if (KERNEL_NODES_FEATURE(BUMP)) { + svm_node_displacement(kg, sd, stack, node); + } break; case NODE_VECTOR_DISPLACEMENT: - svm_node_vector_displacement(kg, sd, stack, node, &offset); + if (KERNEL_NODES_FEATURE(BUMP)) { + offset = svm_node_vector_displacement(kg, sd, stack, node, offset); + } break; -# endif /* NODES_FEATURE(NODE_FEATURE_BUMP) */ case NODE_TEX_IMAGE: - svm_node_tex_image(kg, sd, stack, node, &offset); + offset = svm_node_tex_image(kg, sd, stack, node, offset); break; case NODE_TEX_IMAGE_BOX: svm_node_tex_image_box(kg, sd, stack, node); break; case NODE_TEX_NOISE: - svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, offset); break; -# if NODES_FEATURE(NODE_FEATURE_BUMP) case NODE_SET_BUMP: - svm_node_set_bump(kg, sd, stack, node); + if (KERNEL_NODES_FEATURE(BUMP)) { + svm_node_set_bump(kg, sd, stack, node); + } break; case NODE_ATTR_BUMP_DX: - svm_node_attr_bump_dx(kg, sd, stack, node); + if (KERNEL_NODES_FEATURE(BUMP)) { + svm_node_attr_bump_dx(kg, sd, stack, node); + } break; case NODE_ATTR_BUMP_DY: - svm_node_attr_bump_dy(kg, sd, stack, node); + if (KERNEL_NODES_FEATURE(BUMP)) { + svm_node_attr_bump_dy(kg, sd, stack, node); + } break; case NODE_VERTEX_COLOR_BUMP_DX: - svm_node_vertex_color_bump_dx(kg, sd, stack, node.y, node.z, node.w); + if (KERNEL_NODES_FEATURE(BUMP)) { + svm_node_vertex_color_bump_dx(kg, sd, stack, node.y, node.z, node.w); + } break; case NODE_VERTEX_COLOR_BUMP_DY: - svm_node_vertex_color_bump_dy(kg, sd, stack, node.y, node.z, node.w); + if (KERNEL_NODES_FEATURE(BUMP)) { + svm_node_vertex_color_bump_dy(kg, sd, stack, node.y, node.z, node.w); + } break; case NODE_TEX_COORD_BUMP_DX: - svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, &offset); + if (KERNEL_NODES_FEATURE(BUMP)) { + offset = svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, offset); + } break; case NODE_TEX_COORD_BUMP_DY: - svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, &offset); + if (KERNEL_NODES_FEATURE(BUMP)) { + offset = svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, offset); + } break; case NODE_CLOSURE_SET_NORMAL: - svm_node_set_normal(kg, sd, stack, node.y, node.z); + if (KERNEL_NODES_FEATURE(BUMP)) { + svm_node_set_normal(kg, sd, stack, node.y, node.z); + } break; -# if NODES_FEATURE(NODE_FEATURE_BUMP_STATE) case NODE_ENTER_BUMP_EVAL: - svm_node_enter_bump_eval(kg, sd, stack, node.y); + if (KERNEL_NODES_FEATURE(BUMP_STATE)) { + svm_node_enter_bump_eval(kg, sd, stack, node.y); + } break; case NODE_LEAVE_BUMP_EVAL: - svm_node_leave_bump_eval(kg, sd, stack, node.y); + if (KERNEL_NODES_FEATURE(BUMP_STATE)) { + svm_node_leave_bump_eval(kg, sd, stack, node.y); + } break; -# endif /* NODES_FEATURE(NODE_FEATURE_BUMP_STATE) */ -# endif /* NODES_FEATURE(NODE_FEATURE_BUMP) */ case NODE_HSV: - svm_node_hsv(kg, sd, stack, node, &offset); + svm_node_hsv(kg, sd, stack, node); break; -#endif /* NODES_GROUP(NODE_GROUP_LEVEL_0) */ -#if NODES_GROUP(NODE_GROUP_LEVEL_1) case NODE_CLOSURE_HOLDOUT: svm_node_closure_holdout(sd, stack, node); break; @@ -384,22 +399,24 @@ ccl_device_noinline void svm_eval_nodes( case NODE_LAYER_WEIGHT: svm_node_layer_weight(sd, stack, node); break; -# if NODES_FEATURE(NODE_FEATURE_VOLUME) case NODE_CLOSURE_VOLUME: - svm_node_closure_volume(kg, sd, stack, node, type); + if (KERNEL_NODES_FEATURE(VOLUME)) { + svm_node_closure_volume(kg, sd, stack, node); + } break; case NODE_PRINCIPLED_VOLUME: - svm_node_principled_volume(kg, sd, stack, node, type, path_flag, &offset); + if (KERNEL_NODES_FEATURE(VOLUME)) { + offset = svm_node_principled_volume(kg, sd, stack, node, path_flag, offset); + } break; -# endif /* NODES_FEATURE(NODE_FEATURE_VOLUME) */ case NODE_MATH: - svm_node_math(kg, sd, stack, node.y, node.z, node.w, &offset); + svm_node_math(kg, sd, stack, node.y, node.z, node.w); break; case NODE_VECTOR_MATH: - svm_node_vector_math(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_vector_math(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_RGB_RAMP: - svm_node_rgb_ramp(kg, sd, stack, node, &offset); + offset = svm_node_rgb_ramp(kg, sd, stack, node, offset); break; case NODE_GAMMA: svm_node_gamma(sd, stack, node.y, node.z, node.w); @@ -408,7 +425,7 @@ ccl_device_noinline void svm_eval_nodes( svm_node_brightness(sd, stack, node.y, node.z, node.w); break; case NODE_LIGHT_PATH: - svm_node_light_path(sd, state, stack, node.y, node.z, path_flag); + svm_node_light_path(INTEGRATOR_STATE_PASS, sd, stack, node.y, node.z, path_flag); break; case NODE_OBJECT_INFO: svm_node_object_info(kg, sd, stack, node.y, node.z); @@ -416,22 +433,22 @@ ccl_device_noinline void svm_eval_nodes( case NODE_PARTICLE_INFO: svm_node_particle_info(kg, sd, stack, node.y, node.z); break; -# if defined(__HAIR__) && NODES_FEATURE(NODE_FEATURE_HAIR) +#if defined(__HAIR__) case NODE_HAIR_INFO: - svm_node_hair_info(kg, sd, stack, node.y, node.z); + if (KERNEL_NODES_FEATURE(HAIR)) { + svm_node_hair_info(kg, sd, stack, node.y, node.z); + } break; -# endif /* NODES_FEATURE(NODE_FEATURE_HAIR) */ -#endif /* NODES_GROUP(NODE_GROUP_LEVEL_1) */ +#endif -#if NODES_GROUP(NODE_GROUP_LEVEL_2) case NODE_TEXTURE_MAPPING: - svm_node_texture_mapping(kg, sd, stack, node.y, node.z, &offset); + offset = svm_node_texture_mapping(kg, sd, stack, node.y, node.z, offset); break; case NODE_MAPPING: - svm_node_mapping(kg, sd, stack, node.y, node.z, node.w, &offset); + svm_node_mapping(kg, sd, stack, node.y, node.z, node.w); break; case NODE_MIN_MAX: - svm_node_min_max(kg, sd, stack, node.y, node.z, &offset); + offset = svm_node_min_max(kg, sd, stack, node.y, node.z, offset); break; case NODE_CAMERA: svm_node_camera(kg, sd, stack, node.y, node.z, node.w); @@ -440,47 +457,46 @@ ccl_device_noinline void svm_eval_nodes( svm_node_tex_environment(kg, sd, stack, node); break; case NODE_TEX_SKY: - svm_node_tex_sky(kg, sd, stack, node, &offset); + offset = svm_node_tex_sky(kg, sd, stack, node, offset); break; case NODE_TEX_GRADIENT: svm_node_tex_gradient(sd, stack, node); break; case NODE_TEX_VORONOI: - svm_node_tex_voronoi(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_tex_voronoi( + kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_TEX_MUSGRAVE: - svm_node_tex_musgrave(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_tex_musgrave(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_TEX_WAVE: - svm_node_tex_wave(kg, sd, stack, node, &offset); + offset = svm_node_tex_wave(kg, sd, stack, node, offset); break; case NODE_TEX_MAGIC: - svm_node_tex_magic(kg, sd, stack, node, &offset); + offset = svm_node_tex_magic(kg, sd, stack, node, offset); break; case NODE_TEX_CHECKER: svm_node_tex_checker(kg, sd, stack, node); break; case NODE_TEX_BRICK: - svm_node_tex_brick(kg, sd, stack, node, &offset); + offset = svm_node_tex_brick(kg, sd, stack, node, offset); break; case NODE_TEX_WHITE_NOISE: - svm_node_tex_white_noise(kg, sd, stack, node.y, node.z, node.w, &offset); + svm_node_tex_white_noise(kg, sd, stack, node.y, node.z, node.w); break; case NODE_NORMAL: - svm_node_normal(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_normal(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_LIGHT_FALLOFF: svm_node_light_falloff(sd, stack, node); break; case NODE_IES: - svm_node_ies(kg, sd, stack, node, &offset); + svm_node_ies(kg, sd, stack, node); break; -#endif /* NODES_GROUP(NODE_GROUP_LEVEL_2) */ -#if NODES_GROUP(NODE_GROUP_LEVEL_3) case NODE_RGB_CURVES: case NODE_VECTOR_CURVES: - svm_node_curves(kg, sd, stack, node, &offset); + offset = svm_node_curves(kg, sd, stack, node, offset); break; case NODE_TANGENT: svm_node_tangent(kg, sd, stack, node); @@ -492,7 +508,7 @@ ccl_device_noinline void svm_eval_nodes( svm_node_invert(sd, stack, node.y, node.z, node.w); break; case NODE_MIX: - svm_node_mix(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_mix(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_SEPARATE_VECTOR: svm_node_separate_vector(sd, stack, node.y, node.z, node.w); @@ -501,10 +517,10 @@ ccl_device_noinline void svm_eval_nodes( svm_node_combine_vector(sd, stack, node.y, node.z, node.w); break; case NODE_SEPARATE_HSV: - svm_node_separate_hsv(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_separate_hsv(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_COMBINE_HSV: - svm_node_combine_hsv(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_combine_hsv(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_VECTOR_ROTATE: svm_node_vector_rotate(sd, stack, node.y, node.z, node.w); @@ -522,39 +538,36 @@ ccl_device_noinline void svm_eval_nodes( svm_node_blackbody(kg, sd, stack, node.y, node.z); break; case NODE_MAP_RANGE: - svm_node_map_range(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_map_range(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_CLAMP: - svm_node_clamp(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_clamp(kg, sd, stack, node.y, node.z, node.w, offset); break; -# ifdef __SHADER_RAYTRACE__ +#ifdef __SHADER_RAYTRACE__ case NODE_BEVEL: - svm_node_bevel(kg, sd, state, stack, node); + svm_node_bevel(INTEGRATOR_STATE_PASS, sd, stack, node); break; case NODE_AMBIENT_OCCLUSION: - svm_node_ao(kg, sd, state, stack, node); + svm_node_ao(INTEGRATOR_STATE_PASS, sd, stack, node); break; -# endif /* __SHADER_RAYTRACE__ */ -#endif /* NODES_GROUP(NODE_GROUP_LEVEL_3) */ +#endif -#if NODES_GROUP(NODE_GROUP_LEVEL_4) -# if NODES_FEATURE(NODE_FEATURE_VOLUME) case NODE_TEX_VOXEL: - svm_node_tex_voxel(kg, sd, stack, node, &offset); + if (KERNEL_NODES_FEATURE(VOLUME)) { + offset = svm_node_tex_voxel(kg, sd, stack, node, offset); + } break; -# endif /* NODES_FEATURE(NODE_FEATURE_VOLUME) */ case NODE_AOV_START: - if (!svm_node_aov_check(state, buffer)) { + if (!svm_node_aov_check(path_flag, render_buffer)) { return; } break; case NODE_AOV_COLOR: - svm_node_aov_color(kg, sd, stack, node, buffer); + svm_node_aov_color(INTEGRATOR_STATE_PASS, sd, stack, node, render_buffer); break; case NODE_AOV_VALUE: - svm_node_aov_value(kg, sd, stack, node, buffer); + svm_node_aov_value(INTEGRATOR_STATE_PASS, sd, stack, node, render_buffer); break; -#endif /* NODES_GROUP(NODE_GROUP_LEVEL_4) */ default: kernel_assert(!"Unknown node type was passed to the SVM machine"); return; diff --git a/intern/cycles/kernel/svm/svm_ao.h b/intern/cycles/kernel/svm/svm_ao.h index 4cb986b897a..34ac2cb8fbf 100644 --- a/intern/cycles/kernel/svm/svm_ao.h +++ b/intern/cycles/kernel/svm/svm_ao.h @@ -14,20 +14,25 @@ * limitations under the License. */ +#include "kernel/bvh/bvh.h" + CCL_NAMESPACE_BEGIN #ifdef __SHADER_RAYTRACE__ -ccl_device_noinline float svm_ao(KernelGlobals *kg, - ShaderData *sd, - float3 N, - ccl_addr_space PathState *state, - float max_dist, - int num_samples, - int flags) +# ifdef __KERNEL_OPTIX__ +extern "C" __device__ float __direct_callable__svm_node_ao(INTEGRATOR_STATE_CONST_ARGS, +# else +ccl_device float svm_ao(INTEGRATOR_STATE_CONST_ARGS, +# endif + ShaderData *sd, + float3 N, + float max_dist, + int num_samples, + int flags) { if (flags & NODE_AO_GLOBAL_RADIUS) { - max_dist = kernel_data.background.ao_distance; + max_dist = kernel_data.integrator.ao_bounces_distance; } /* Early out if no sampling needed. */ @@ -47,11 +52,14 @@ ccl_device_noinline float svm_ao(KernelGlobals *kg, float3 T, B; make_orthonormals(N, &T, &B); + /* TODO: support ray-tracing in shadow shader evaluation? */ + RNGState rng_state; + path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + int unoccluded = 0; for (int sample = 0; sample < num_samples; sample++) { float disk_u, disk_v; - path_branched_rng_2D( - kg, state->rng_hash, state, sample, num_samples, PRNG_BEVEL_U, &disk_u, &disk_v); + path_branched_rng_2D(kg, &rng_state, sample, num_samples, PRNG_BEVEL_U, &disk_u, &disk_v); float2 d = concentric_sample_disk(disk_u, disk_v); float3 D = make_float3(d.x, d.y, safe_sqrtf(1.0f - dot(d, d))); @@ -62,8 +70,8 @@ ccl_device_noinline float svm_ao(KernelGlobals *kg, ray.D = D.x * T + D.y * B + D.z * N; ray.t = max_dist; ray.time = sd->time; - ray.dP = sd->dP; - ray.dD = differential3_zero(); + ray.dP = differential_zero_compact(); + ray.dD = differential_zero_compact(); if (flags & NODE_AO_ONLY_LOCAL) { if (!scene_intersect_local(kg, &ray, NULL, sd->object, NULL, 0)) { @@ -81,8 +89,14 @@ ccl_device_noinline float svm_ao(KernelGlobals *kg, return ((float)unoccluded) / num_samples; } -ccl_device void svm_node_ao( - KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, float *stack, uint4 node) +template +# if defined(__KERNEL_OPTIX__) +ccl_device_inline +# else +ccl_device_noinline +# endif + void + svm_node_ao(INTEGRATOR_STATE_CONST_ARGS, ShaderData *sd, float *stack, uint4 node) { uint flags, dist_offset, normal_offset, out_ao_offset; svm_unpack_node_uchar4(node.y, &flags, &dist_offset, &normal_offset, &out_ao_offset); @@ -92,7 +106,16 @@ ccl_device void svm_node_ao( float dist = stack_load_float_default(stack, dist_offset, node.w); float3 normal = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N; - float ao = svm_ao(kg, sd, normal, state, dist, samples, flags); + + float ao = 1.0f; + + if (KERNEL_NODES_FEATURE(RAYTRACE)) { +# ifdef __KERNEL_OPTIX__ + ao = optixDirectCall(0, INTEGRATOR_STATE_PASS, sd, normal, dist, samples, flags); +# else + ao = svm_ao(INTEGRATOR_STATE_PASS, sd, normal, dist, samples, flags); +# endif + } if (stack_valid(out_ao_offset)) { stack_store_float(stack, out_ao_offset, ao); diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h index 899e466d099..26dec9717b3 100644 --- a/intern/cycles/kernel/svm/svm_aov.h +++ b/intern/cycles/kernel/svm/svm_aov.h @@ -14,36 +14,50 @@ * limitations under the License. */ +#include "kernel/kernel_write_passes.h" + CCL_NAMESPACE_BEGIN -ccl_device_inline bool svm_node_aov_check(ccl_addr_space PathState *state, - ccl_global float *buffer) +ccl_device_inline bool svm_node_aov_check(const int path_flag, ccl_global float *render_buffer) { - int path_flag = state->flag; - bool is_primary = (path_flag & PATH_RAY_CAMERA) && (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)); - return ((buffer != NULL) && is_primary); + return ((render_buffer != NULL) && is_primary); } -ccl_device void svm_node_aov_color( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, ccl_global float *buffer) +ccl_device void svm_node_aov_color(INTEGRATOR_STATE_CONST_ARGS, + ShaderData *sd, + float *stack, + uint4 node, + ccl_global float *render_buffer) { float3 val = stack_load_float3(stack, node.y); - if (buffer) { - kernel_write_pass_float4(buffer + kernel_data.film.pass_aov_color + 4 * node.z, - make_float4(val.x, val.y, val.z, 1.0f)); + if (render_buffer && !INTEGRATOR_STATE_IS_NULL) { + const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + ccl_global float *buffer = render_buffer + render_buffer_offset + + (kernel_data.film.pass_aov_color + node.z); + kernel_write_pass_float3(buffer, make_float3(val.x, val.y, val.z)); } } -ccl_device void svm_node_aov_value( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, ccl_global float *buffer) +ccl_device void svm_node_aov_value(INTEGRATOR_STATE_CONST_ARGS, + ShaderData *sd, + float *stack, + uint4 node, + ccl_global float *render_buffer) { float val = stack_load_float(stack, node.y); - if (buffer) { - kernel_write_pass_float(buffer + kernel_data.film.pass_aov_value + node.z, val); + if (render_buffer && !INTEGRATOR_STATE_IS_NULL) { + const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + ccl_global float *buffer = render_buffer + render_buffer_offset + + (kernel_data.film.pass_aov_value + node.z); + kernel_write_pass_float(buffer, val); } } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_attribute.h b/intern/cycles/kernel/svm/svm_attribute.h index 62740824ad1..5f94b20af73 100644 --- a/intern/cycles/kernel/svm/svm_attribute.h +++ b/intern/cycles/kernel/svm/svm_attribute.h @@ -18,8 +18,11 @@ CCL_NAMESPACE_BEGIN /* Attribute Node */ -ccl_device AttributeDescriptor svm_node_attr_init( - KernelGlobals *kg, ShaderData *sd, uint4 node, NodeAttributeOutputType *type, uint *out_offset) +ccl_device AttributeDescriptor svm_node_attr_init(const KernelGlobals *kg, + ShaderData *sd, + uint4 node, + NodeAttributeOutputType *type, + uint *out_offset) { *out_offset = node.z; *type = (NodeAttributeOutputType)node.w; @@ -44,31 +47,37 @@ ccl_device AttributeDescriptor svm_node_attr_init( return desc; } -ccl_device void svm_node_attr(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +template +ccl_device_noinline void svm_node_attr(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT; uint out_offset = 0; AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type, &out_offset); #ifdef __VOLUME__ - /* Volumes - * NOTE: moving this into its own node type might help improve performance. */ - if (primitive_is_volume_attribute(sd, desc)) { - const float4 value = volume_attribute_float4(kg, sd, desc); + if (KERNEL_NODES_FEATURE(VOLUME)) { + /* Volumes + * NOTE: moving this into its own node type might help improve performance. */ + if (primitive_is_volume_attribute(sd, desc)) { + const float4 value = volume_attribute_float4(kg, sd, desc); - if (type == NODE_ATTR_OUTPUT_FLOAT) { - const float f = volume_attribute_value_to_float(value); - stack_store_float(stack, out_offset, f); + if (type == NODE_ATTR_OUTPUT_FLOAT) { + const float f = volume_attribute_value_to_float(value); + stack_store_float(stack, out_offset, f); + } + else if (type == NODE_ATTR_OUTPUT_FLOAT3) { + const float3 f = volume_attribute_value_to_float3(value); + stack_store_float3(stack, out_offset, f); + } + else { + const float f = volume_attribute_value_to_alpha(value); + stack_store_float(stack, out_offset, f); + } + return; } - else if (type == NODE_ATTR_OUTPUT_FLOAT3) { - const float3 f = volume_attribute_value_to_float3(value); - stack_store_float3(stack, out_offset, f); - } - else { - const float f = volume_attribute_value_to_alpha(value); - stack_store_float(stack, out_offset, f); - } - return; } #endif @@ -139,7 +148,10 @@ ccl_device void svm_node_attr(KernelGlobals *kg, ShaderData *sd, float *stack, u } } -ccl_device void svm_node_attr_bump_dx(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_attr_bump_dx(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT; uint out_offset = 0; @@ -232,7 +244,10 @@ ccl_device void svm_node_attr_bump_dx(KernelGlobals *kg, ShaderData *sd, float * } } -ccl_device void svm_node_attr_bump_dy(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_attr_bump_dy(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT; uint out_offset = 0; diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/svm_bevel.h index bf5957ec9e4..aab089d19ea 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/svm_bevel.h @@ -14,21 +14,95 @@ * limitations under the License. */ +#include "kernel/bvh/bvh.h" +#include "kernel/kernel_montecarlo.h" +#include "kernel/kernel_random.h" + CCL_NAMESPACE_BEGIN #ifdef __SHADER_RAYTRACE__ +/* Planar Cubic BSSRDF falloff, reused for bevel. + * + * This is basically (Rm - x)^3, with some factors to normalize it. For sampling + * we integrate 2*pi*x * (Rm - x)^3, which gives us a quintic equation that as + * far as I can tell has no closed form solution. So we get an iterative solution + * instead with newton-raphson. */ + +ccl_device float svm_bevel_cubic_eval(const float radius, float r) +{ + const float Rm = radius; + + if (r >= Rm) + return 0.0f; + + /* integrate (2*pi*r * 10*(R - r)^3)/(pi * R^5) from 0 to R = 1 */ + const float Rm5 = (Rm * Rm) * (Rm * Rm) * Rm; + const float f = Rm - r; + const float num = f * f * f; + + return (10.0f * num) / (Rm5 * M_PI_F); +} + +ccl_device float svm_bevel_cubic_pdf(const float radius, float r) +{ + return svm_bevel_cubic_eval(radius, r); +} + +/* solve 10x^2 - 20x^3 + 15x^4 - 4x^5 - xi == 0 */ +ccl_device_forceinline float svm_bevel_cubic_quintic_root_find(float xi) +{ + /* newton-raphson iteration, usually succeeds in 2-4 iterations, except + * outside 0.02 ... 0.98 where it can go up to 10, so overall performance + * should not be too bad */ + const float tolerance = 1e-6f; + const int max_iteration_count = 10; + float x = 0.25f; + int i; + + for (i = 0; i < max_iteration_count; i++) { + float x2 = x * x; + float x3 = x2 * x; + float nx = (1.0f - x); + + float f = 10.0f * x2 - 20.0f * x3 + 15.0f * x2 * x2 - 4.0f * x2 * x3 - xi; + float f_ = 20.0f * (x * nx) * (nx * nx); + + if (fabsf(f) < tolerance || f_ == 0.0f) + break; + + x = saturate(x - f / f_); + } + + return x; +} + +ccl_device void svm_bevel_cubic_sample(const float radius, float xi, float *r, float *h) +{ + float Rm = radius; + float r_ = svm_bevel_cubic_quintic_root_find(xi); + + r_ *= Rm; + *r = r_; + + /* h^2 + r^2 = Rm^2 */ + *h = safe_sqrtf(Rm * Rm - r_ * r_); +} + /* Bevel shader averaging normals from nearby surfaces. * * Sampling strategy from: BSSRDF Importance Sampling, SIGGRAPH 2013 * http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf */ -ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - float radius, - int num_samples) +# ifdef __KERNEL_OPTIX__ +extern "C" __device__ float3 __direct_callable__svm_node_bevel(INTEGRATOR_STATE_CONST_ARGS, +# else +ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, +# endif + ShaderData *sd, + float radius, + int num_samples) { /* Early out if no sampling needed. */ if (radius <= 0.0f || num_samples < 1 || sd->object == OBJECT_NONE) { @@ -41,21 +115,27 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, } /* Don't bevel for blurry indirect rays. */ - if (state->min_ray_pdf < 8.0f) { + if (INTEGRATOR_STATE(path, min_ray_pdf) < 8.0f) { return sd->N; } /* Setup for multi intersection. */ LocalIntersection isect; - uint lcg_state = lcg_state_init_addrspace(state, 0x64c6a40e); + uint lcg_state = lcg_state_init(INTEGRATOR_STATE(path, rng_hash), + INTEGRATOR_STATE(path, rng_offset), + INTEGRATOR_STATE(path, sample), + 0x64c6a40e); /* Sample normals from surrounding points on surface. */ float3 sum_N = make_float3(0.0f, 0.0f, 0.0f); + /* TODO: support ray-tracing in shadow shader evaluation? */ + RNGState rng_state; + path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + for (int sample = 0; sample < num_samples; sample++) { float disk_u, disk_v; - path_branched_rng_2D( - kg, state->rng_hash, state, sample, num_samples, PRNG_BEVEL_U, &disk_u, &disk_v); + path_branched_rng_2D(kg, &rng_state, sample, num_samples, PRNG_BEVEL_U, &disk_u, &disk_v); /* Pick random axis in local frame and point on disk. */ float3 disk_N, disk_T, disk_B; @@ -97,7 +177,7 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, float disk_height; /* Perhaps find something better than Cubic BSSRDF, but happens to work well. */ - bssrdf_cubic_sample(radius, 0.0f, disk_r, &disk_r, &disk_height); + svm_bevel_cubic_sample(radius, disk_r, &disk_r, &disk_height); float3 disk_P = (disk_r * cosf(phi)) * disk_T + (disk_r * sinf(phi)) * disk_B; @@ -106,8 +186,8 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, ray->P = sd->P + disk_N * disk_height + disk_P; ray->D = -disk_N; ray->t = 2.0f * disk_height; - ray->dP = sd->dP; - ray->dD = differential3_zero(); + ray->dP = differential_zero_compact(); + ray->dD = differential_zero_compact(); ray->time = sd->time; /* Intersect with the same object. if multiple intersections are found it @@ -120,14 +200,16 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, /* Quickly retrieve P and Ng without setting up ShaderData. */ float3 hit_P; if (sd->type & PRIMITIVE_TRIANGLE) { - hit_P = triangle_refine_local(kg, sd, &isect.hits[hit], ray); + hit_P = triangle_refine_local( + kg, sd, ray->P, ray->D, ray->t, isect.hits[hit].object, isect.hits[hit].prim); } # ifdef __OBJECT_MOTION__ else if (sd->type & PRIMITIVE_MOTION_TRIANGLE) { float3 verts[3]; motion_triangle_vertices( kg, sd->object, kernel_tex_fetch(__prim_index, isect.hits[hit].prim), sd->time, verts); - hit_P = motion_triangle_refine_local(kg, sd, &isect.hits[hit], ray, verts); + hit_P = motion_triangle_refine_local( + kg, sd, ray->P, ray->D, ray->t, isect.hits[hit].object, isect.hits[hit].prim, verts); } # endif /* __OBJECT_MOTION__ */ @@ -183,8 +265,8 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, float r = len(hit_P - sd->P); /* Compute weight. */ - float pdf = bssrdf_cubic_pdf(radius, 0.0f, r); - float disk_pdf = bssrdf_cubic_pdf(radius, 0.0f, disk_r); + float pdf = svm_bevel_cubic_pdf(radius, r); + float disk_pdf = svm_bevel_cubic_pdf(radius, disk_r); w *= pdf / disk_pdf; @@ -198,19 +280,34 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, return is_zero(N) ? sd->N : (sd->flag & SD_BACKFACING) ? -N : N; } -ccl_device void svm_node_bevel( - KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, float *stack, uint4 node) +template +# if defined(__KERNEL_OPTIX__) +ccl_device_inline +# else +ccl_device_noinline +# endif + void + svm_node_bevel(INTEGRATOR_STATE_CONST_ARGS, ShaderData *sd, float *stack, uint4 node) { uint num_samples, radius_offset, normal_offset, out_offset; svm_unpack_node_uchar4(node.y, &num_samples, &radius_offset, &normal_offset, &out_offset); float radius = stack_load_float(stack, radius_offset); - float3 bevel_N = svm_bevel(kg, sd, state, radius, num_samples); - if (stack_valid(normal_offset)) { - /* Preserve input normal. */ - float3 ref_N = stack_load_float3(stack, normal_offset); - bevel_N = normalize(ref_N + (bevel_N - sd->N)); + float3 bevel_N = sd->N; + + if (KERNEL_NODES_FEATURE(RAYTRACE)) { +# ifdef __KERNEL_OPTIX__ + bevel_N = optixDirectCall(1, INTEGRATOR_STATE_PASS, sd, radius, num_samples); +# else + bevel_N = svm_bevel(INTEGRATOR_STATE_PASS, sd, radius, num_samples); +# endif + + if (stack_valid(normal_offset)) { + /* Preserve input normal. */ + float3 ref_N = stack_load_float3(stack, normal_offset); + bevel_N = normalize(ref_N + (bevel_N - sd->N)); + } } stack_store_float3(stack, out_offset, bevel_N); diff --git a/intern/cycles/kernel/svm/svm_blackbody.h b/intern/cycles/kernel/svm/svm_blackbody.h index adfc50d961e..96b3703b954 100644 --- a/intern/cycles/kernel/svm/svm_blackbody.h +++ b/intern/cycles/kernel/svm/svm_blackbody.h @@ -34,8 +34,11 @@ CCL_NAMESPACE_BEGIN /* Blackbody Node */ -ccl_device void svm_node_blackbody( - KernelGlobals *kg, ShaderData *sd, float *stack, uint temperature_offset, uint col_offset) +ccl_device_noinline void svm_node_blackbody(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint temperature_offset, + uint col_offset) { /* Input */ float temperature = stack_load_float(stack, temperature_offset); diff --git a/intern/cycles/kernel/svm/svm_brick.h b/intern/cycles/kernel/svm/svm_brick.h index 6984afa30a5..dca1b220dd5 100644 --- a/intern/cycles/kernel/svm/svm_brick.h +++ b/intern/cycles/kernel/svm/svm_brick.h @@ -72,12 +72,12 @@ ccl_device_noinline_cpu float2 svm_brick(float3 p, return make_float2(tint, mortar); } -ccl_device void svm_node_tex_brick( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_brick( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) { - uint4 node2 = read_node(kg, offset); - uint4 node3 = read_node(kg, offset); - uint4 node4 = read_node(kg, offset); + uint4 node2 = read_node(kg, &offset); + uint4 node3 = read_node(kg, &offset); + uint4 node4 = read_node(kg, &offset); /* Input and Output Sockets */ uint co_offset, color1_offset, color2_offset, mortar_offset, scale_offset; @@ -133,6 +133,7 @@ ccl_device void svm_node_tex_brick( stack_store_float3(stack, color_offset, color1 * (1.0f - f) + mortar * f); if (stack_valid(fac_offset)) stack_store_float(stack, fac_offset, f); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_brightness.h b/intern/cycles/kernel/svm/svm_brightness.h index 9554b5946fb..2ed812acd71 100644 --- a/intern/cycles/kernel/svm/svm_brightness.h +++ b/intern/cycles/kernel/svm/svm_brightness.h @@ -16,7 +16,7 @@ CCL_NAMESPACE_BEGIN -ccl_device void svm_node_brightness( +ccl_device_noinline void svm_node_brightness( ShaderData *sd, float *stack, uint in_color, uint out_color, uint node) { uint bright_offset, contrast_offset; diff --git a/intern/cycles/kernel/svm/svm_bump.h b/intern/cycles/kernel/svm/svm_bump.h index c9d430a2bba..8672839dbab 100644 --- a/intern/cycles/kernel/svm/svm_bump.h +++ b/intern/cycles/kernel/svm/svm_bump.h @@ -18,10 +18,10 @@ CCL_NAMESPACE_BEGIN /* Bump Eval Nodes */ -ccl_device void svm_node_enter_bump_eval(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint offset) +ccl_device_noinline void svm_node_enter_bump_eval(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint offset) { /* save state */ stack_store_float3(stack, offset + 0, sd->P); @@ -45,10 +45,10 @@ ccl_device void svm_node_enter_bump_eval(KernelGlobals *kg, } } -ccl_device void svm_node_leave_bump_eval(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint offset) +ccl_device_noinline void svm_node_leave_bump_eval(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint offset) { /* restore state */ sd->P = stack_load_float3(stack, offset + 0); diff --git a/intern/cycles/kernel/svm/svm_camera.h b/intern/cycles/kernel/svm/svm_camera.h index 21a17acf5f1..40c0edcdad0 100644 --- a/intern/cycles/kernel/svm/svm_camera.h +++ b/intern/cycles/kernel/svm/svm_camera.h @@ -16,12 +16,12 @@ CCL_NAMESPACE_BEGIN -ccl_device void svm_node_camera(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint out_vector, - uint out_zdepth, - uint out_distance) +ccl_device_noinline void svm_node_camera(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint out_vector, + uint out_zdepth, + uint out_distance) { float distance; float zdepth; diff --git a/intern/cycles/kernel/svm/svm_checker.h b/intern/cycles/kernel/svm/svm_checker.h index d54cb73df91..a9919c9ddc9 100644 --- a/intern/cycles/kernel/svm/svm_checker.h +++ b/intern/cycles/kernel/svm/svm_checker.h @@ -32,7 +32,10 @@ ccl_device float svm_checker(float3 p) return ((xi % 2 == yi % 2) == (zi % 2)) ? 1.0f : 0.0f; } -ccl_device void svm_node_tex_checker(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_tex_checker(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { uint co_offset, color1_offset, color2_offset, scale_offset; uint color_offset, fac_offset; diff --git a/intern/cycles/kernel/svm/svm_clamp.h b/intern/cycles/kernel/svm/svm_clamp.h index a85fd82754e..656bd31c085 100644 --- a/intern/cycles/kernel/svm/svm_clamp.h +++ b/intern/cycles/kernel/svm/svm_clamp.h @@ -18,18 +18,18 @@ CCL_NAMESPACE_BEGIN /* Clamp Node */ -ccl_device void svm_node_clamp(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint value_stack_offset, - uint parameters_stack_offsets, - uint result_stack_offset, - int *offset) +ccl_device_noinline int svm_node_clamp(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint value_stack_offset, + uint parameters_stack_offsets, + uint result_stack_offset, + int offset) { uint min_stack_offset, max_stack_offset, type; svm_unpack_node_uchar3(parameters_stack_offsets, &min_stack_offset, &max_stack_offset, &type); - uint4 defaults = read_node(kg, offset); + uint4 defaults = read_node(kg, &offset); float value = stack_load_float(stack, value_stack_offset); float min = stack_load_float_default(stack, min_stack_offset, defaults.x); @@ -41,6 +41,7 @@ ccl_device void svm_node_clamp(KernelGlobals *kg, else { stack_store_float(stack, result_stack_offset, clamp(value, min, max)); } + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h index bbe8d72edf0..e2f6dde4ace 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/svm_closure.h @@ -57,13 +57,9 @@ ccl_device void svm_node_glass_setup( } } -ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint4 node, - ShaderType shader_type, - int path_flag, - int *offset) +template +ccl_device_noinline int svm_node_closure_bsdf( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int path_flag, int offset) { uint type, param1_offset, param2_offset; @@ -73,19 +69,19 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, 1.0f); /* note we read this extra node before weight check, so offset is added */ - uint4 data_node = read_node(kg, offset); + uint4 data_node = read_node(kg, &offset); /* Only compute BSDF for surfaces, transparent variable is shared with volume extinction. */ - if (mix_weight == 0.0f || shader_type != SHADER_TYPE_SURFACE) { + if ((!KERNEL_NODES_FEATURE(BSDF) || shader_type != SHADER_TYPE_SURFACE) || mix_weight == 0.0f) { if (type == CLOSURE_BSDF_PRINCIPLED_ID) { /* Read all principled BSDF extra data to get the right offset. */ - read_node(kg, offset); - read_node(kg, offset); - read_node(kg, offset); - read_node(kg, offset); + read_node(kg, &offset); + read_node(kg, &offset); + read_node(kg, &offset); + read_node(kg, &offset); } - return; + return offset; } float3 N = stack_valid(data_node.x) ? stack_load_float3(stack, data_node.x) : sd->N; @@ -102,7 +98,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, sheen_offset, sheen_tint_offset, clearcoat_offset, clearcoat_roughness_offset, eta_offset, transmission_offset, anisotropic_rotation_offset, transmission_roughness_offset; - uint4 data_node2 = read_node(kg, offset); + uint4 data_node2 = read_node(kg, &offset); float3 T = stack_load_float3(stack, data_node.y); svm_unpack_node_uchar4(data_node.z, @@ -158,7 +154,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, float specular_weight = (1.0f - final_transmission); // get the base color - uint4 data_base_color = read_node(kg, offset); + uint4 data_base_color = read_node(kg, &offset); float3 base_color = stack_valid(data_base_color.x) ? stack_load_float3(stack, data_base_color.x) : make_float3(__uint_as_float(data_base_color.y), @@ -166,16 +162,21 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, __uint_as_float(data_base_color.w)); // get the additional clearcoat normal and subsurface scattering radius - uint4 data_cn_ssr = read_node(kg, offset); + uint4 data_cn_ssr = read_node(kg, &offset); float3 clearcoat_normal = stack_valid(data_cn_ssr.x) ? stack_load_float3(stack, data_cn_ssr.x) : sd->N; float3 subsurface_radius = stack_valid(data_cn_ssr.y) ? stack_load_float3(stack, data_cn_ssr.y) : make_float3(1.0f, 1.0f, 1.0f); + float subsurface_ior = stack_valid(data_cn_ssr.z) ? stack_load_float(stack, data_cn_ssr.z) : + 1.4f; + float subsurface_anisotropy = stack_valid(data_cn_ssr.w) ? + stack_load_float(stack, data_cn_ssr.w) : + 0.0f; // get the subsurface color - uint4 data_subsurface_color = read_node(kg, offset); + uint4 data_subsurface_color = read_node(kg, &offset); float3 subsurface_color = stack_valid(data_subsurface_color.x) ? stack_load_float3(stack, data_subsurface_color.x) : make_float3(__uint_as_float(data_subsurface_color.y), @@ -222,16 +223,16 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, if (bssrdf) { bssrdf->radius = subsurface_radius * subsurface; - bssrdf->albedo = (subsurface_method == CLOSURE_BSSRDF_PRINCIPLED_ID) ? - subsurface_color : - mixed_ss_base_color; - bssrdf->texture_blur = 0.0f; - bssrdf->sharpness = 0.0f; + bssrdf->albedo = mixed_ss_base_color; bssrdf->N = N; bssrdf->roughness = roughness; + /* Clamps protecting against bad/extreme and non physical values. */ + subsurface_ior = clamp(subsurface_ior, 1.01f, 3.8f); + bssrdf->anisotropy = clamp(subsurface_anisotropy, 0.0f, 0.9f); + /* setup bsdf */ - sd->flag |= bssrdf_setup(sd, bssrdf, subsurface_method); + sd->flag |= bssrdf_setup(sd, bssrdf, subsurface_method, subsurface_ior); } } } @@ -733,9 +734,9 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, } #ifdef __HAIR__ case CLOSURE_BSDF_HAIR_PRINCIPLED_ID: { - uint4 data_node2 = read_node(kg, offset); - uint4 data_node3 = read_node(kg, offset); - uint4 data_node4 = read_node(kg, offset); + uint4 data_node2 = read_node(kg, &offset); + uint4 data_node3 = read_node(kg, &offset); + uint4 data_node4 = read_node(kg, &offset); float3 weight = sd->svm_closure_weight * mix_weight; @@ -878,10 +879,8 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, #endif /* __HAIR__ */ #ifdef __SUBSURFACE__ - case CLOSURE_BSSRDF_CUBIC_ID: - case CLOSURE_BSSRDF_GAUSSIAN_ID: - case CLOSURE_BSSRDF_BURLEY_ID: - case CLOSURE_BSSRDF_RANDOM_WALK_ID: { + case CLOSURE_BSSRDF_RANDOM_WALK_ID: + case CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID: { float3 weight = sd->svm_closure_weight * mix_weight; Bssrdf *bssrdf = bssrdf_alloc(sd, weight); @@ -894,11 +893,14 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, bssrdf->radius = stack_load_float3(stack, data_node.z) * param1; bssrdf->albedo = sd->svm_closure_weight; - bssrdf->texture_blur = param2; - bssrdf->sharpness = stack_load_float(stack, data_node.w); bssrdf->N = N; - bssrdf->roughness = 0.0f; - sd->flag |= bssrdf_setup(sd, bssrdf, (ClosureType)type); + bssrdf->roughness = FLT_MAX; + + const float subsurface_ior = clamp(param2, 1.01f, 3.8f); + const float subsurface_anisotropy = stack_load_float(stack, data_node.w); + bssrdf->anisotropy = clamp(subsurface_anisotropy, 0.0f, 0.9f); + + sd->flag |= bssrdf_setup(sd, bssrdf, (ClosureType)type, subsurface_ior); } break; @@ -907,10 +909,15 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, default: break; } + + return offset; } -ccl_device void svm_node_closure_volume( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, ShaderType shader_type) +template +ccl_device_noinline void svm_node_closure_volume(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { #ifdef __VOLUME__ /* Only sum extinction for volumes, variable is shared with surface transparency. */ @@ -961,21 +968,17 @@ ccl_device void svm_node_closure_volume( #endif } -ccl_device void svm_node_principled_volume(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint4 node, - ShaderType shader_type, - int path_flag, - int *offset) +template +ccl_device_noinline int svm_node_principled_volume( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int path_flag, int offset) { #ifdef __VOLUME__ - uint4 value_node = read_node(kg, offset); - uint4 attr_node = read_node(kg, offset); + uint4 value_node = read_node(kg, &offset); + uint4 attr_node = read_node(kg, &offset); /* Only sum extinction for volumes, variable is shared with surface transparency. */ if (shader_type != SHADER_TYPE_VOLUME) { - return; + return offset; } uint density_offset, anisotropy_offset, absorption_color_offset, mix_weight_offset; @@ -985,7 +988,7 @@ ccl_device void svm_node_principled_volume(KernelGlobals *kg, 1.0f); if (mix_weight == 0.0f) { - return; + return offset; } /* Compute density. */ @@ -1034,7 +1037,7 @@ ccl_device void svm_node_principled_volume(KernelGlobals *kg, /* Compute emission. */ if (path_flag & PATH_RAY_SHADOW) { /* Don't need emission for shadows. */ - return; + return offset; } uint emission_offset, emission_color_offset, blackbody_offset, temperature_offset; @@ -1074,9 +1077,10 @@ ccl_device void svm_node_principled_volume(KernelGlobals *kg, } } #endif + return offset; } -ccl_device void svm_node_closure_emission(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_closure_emission(ShaderData *sd, float *stack, uint4 node) { uint mix_weight_offset = node.y; float3 weight = sd->svm_closure_weight; @@ -1093,7 +1097,7 @@ ccl_device void svm_node_closure_emission(ShaderData *sd, float *stack, uint4 no emission_setup(sd, weight); } -ccl_device void svm_node_closure_background(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_closure_background(ShaderData *sd, float *stack, uint4 node) { uint mix_weight_offset = node.y; float3 weight = sd->svm_closure_weight; @@ -1110,7 +1114,7 @@ ccl_device void svm_node_closure_background(ShaderData *sd, float *stack, uint4 background_setup(sd, weight); } -ccl_device void svm_node_closure_holdout(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_closure_holdout(ShaderData *sd, float *stack, uint4 node) { uint mix_weight_offset = node.y; @@ -1145,14 +1149,13 @@ ccl_device void svm_node_closure_set_weight(ShaderData *sd, uint r, uint g, uint ccl_device void svm_node_closure_weight(ShaderData *sd, float *stack, uint weight_offset) { float3 weight = stack_load_float3(stack, weight_offset); - svm_node_closure_store_weight(sd, weight); } -ccl_device void svm_node_emission_weight(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint4 node) +ccl_device_noinline void svm_node_emission_weight(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { uint color_offset = node.y; uint strength_offset = node.z; @@ -1163,7 +1166,7 @@ ccl_device void svm_node_emission_weight(KernelGlobals *kg, svm_node_closure_store_weight(sd, weight); } -ccl_device void svm_node_mix_closure(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_mix_closure(ShaderData *sd, float *stack, uint4 node) { /* fetch weight from blend input, previous mix closures, * and write to stack to be used by closure nodes later */ @@ -1186,7 +1189,7 @@ ccl_device void svm_node_mix_closure(ShaderData *sd, float *stack, uint4 node) /* (Bump) normal */ ccl_device void svm_node_set_normal( - KernelGlobals *kg, ShaderData *sd, float *stack, uint in_direction, uint out_normal) + const KernelGlobals *kg, ShaderData *sd, float *stack, uint in_direction, uint out_normal) { float3 normal = stack_load_float3(stack, in_direction); sd->N = normal; diff --git a/intern/cycles/kernel/svm/svm_convert.h b/intern/cycles/kernel/svm/svm_convert.h index 5df6c9fb755..37d40167ccc 100644 --- a/intern/cycles/kernel/svm/svm_convert.h +++ b/intern/cycles/kernel/svm/svm_convert.h @@ -18,8 +18,8 @@ CCL_NAMESPACE_BEGIN /* Conversion Nodes */ -ccl_device void svm_node_convert( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint from, uint to) +ccl_device_noinline void svm_node_convert( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint from, uint to) { switch (type) { case NODE_CONVERT_FI: { diff --git a/intern/cycles/kernel/svm/svm_displace.h b/intern/cycles/kernel/svm/svm_displace.h index 250fac6bcb8..a1d952173d8 100644 --- a/intern/cycles/kernel/svm/svm_displace.h +++ b/intern/cycles/kernel/svm/svm_displace.h @@ -14,11 +14,16 @@ * limitations under the License. */ +#include "kernel/kernel_montecarlo.h" + CCL_NAMESPACE_BEGIN /* Bump Node */ -ccl_device void svm_node_set_bump(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_set_bump(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { #ifdef __RAY_DIFFERENTIALS__ /* get normal input */ @@ -83,7 +88,7 @@ ccl_device void svm_node_set_bump(KernelGlobals *kg, ShaderData *sd, float *stac /* Displacement Node */ -ccl_device void svm_node_set_displacement(KernelGlobals *kg, +ccl_device void svm_node_set_displacement(const KernelGlobals *kg, ShaderData *sd, float *stack, uint fac_offset) @@ -92,7 +97,10 @@ ccl_device void svm_node_set_displacement(KernelGlobals *kg, sd->P += dP; } -ccl_device void svm_node_displacement(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_displacement(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { uint height_offset, midlevel_offset, scale_offset, normal_offset; svm_unpack_node_uchar4(node.y, &height_offset, &midlevel_offset, &scale_offset, &normal_offset); @@ -119,10 +127,10 @@ ccl_device void svm_node_displacement(KernelGlobals *kg, ShaderData *sd, float * stack_store_float3(stack, node.z, dP); } -ccl_device void svm_node_vector_displacement( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_vector_displacement( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) { - uint4 data_node = read_node(kg, offset); + uint4 data_node = read_node(kg, &offset); uint space = data_node.x; uint vector_offset, midlevel_offset, scale_offset, displacement_offset; @@ -164,6 +172,7 @@ ccl_device void svm_node_vector_displacement( } stack_store_float3(stack, displacement_offset, dP); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_fresnel.h b/intern/cycles/kernel/svm/svm_fresnel.h index 96d602e35bf..b5ecdbe2abf 100644 --- a/intern/cycles/kernel/svm/svm_fresnel.h +++ b/intern/cycles/kernel/svm/svm_fresnel.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Fresnel Node */ -ccl_device void svm_node_fresnel( +ccl_device_noinline void svm_node_fresnel( ShaderData *sd, float *stack, uint ior_offset, uint ior_value, uint node) { uint normal_offset, out_offset; @@ -37,7 +37,7 @@ ccl_device void svm_node_fresnel( /* Layer Weight Node */ -ccl_device void svm_node_layer_weight(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_layer_weight(ShaderData *sd, float *stack, uint4 node) { uint blend_offset = node.y; uint blend_value = node.z; diff --git a/intern/cycles/kernel/svm/svm_gamma.h b/intern/cycles/kernel/svm/svm_gamma.h index 65eb08eb0eb..f6fafdee941 100644 --- a/intern/cycles/kernel/svm/svm_gamma.h +++ b/intern/cycles/kernel/svm/svm_gamma.h @@ -16,7 +16,7 @@ CCL_NAMESPACE_BEGIN -ccl_device void svm_node_gamma( +ccl_device_noinline void svm_node_gamma( ShaderData *sd, float *stack, uint in_gamma, uint in_color, uint out_color) { float3 color = stack_load_float3(stack, in_color); diff --git a/intern/cycles/kernel/svm/svm_geometry.h b/intern/cycles/kernel/svm/svm_geometry.h index e48e96dcfa4..10e9f291d0e 100644 --- a/intern/cycles/kernel/svm/svm_geometry.h +++ b/intern/cycles/kernel/svm/svm_geometry.h @@ -18,8 +18,8 @@ CCL_NAMESPACE_BEGIN /* Geometry Node */ -ccl_device_inline void svm_node_geometry( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_geometry( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) { float3 data; @@ -51,8 +51,8 @@ ccl_device_inline void svm_node_geometry( stack_store_float3(stack, out_offset, data); } -ccl_device void svm_node_geometry_bump_dx( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_geometry_bump_dx( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -75,8 +75,8 @@ ccl_device void svm_node_geometry_bump_dx( #endif } -ccl_device void svm_node_geometry_bump_dy( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_geometry_bump_dy( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -101,8 +101,8 @@ ccl_device void svm_node_geometry_bump_dy( /* Object Info */ -ccl_device void svm_node_object_info( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_object_info( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) { float data; @@ -140,8 +140,8 @@ ccl_device void svm_node_object_info( /* Particle Info */ -ccl_device void svm_node_particle_info( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_particle_info( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) { switch (type) { case NODE_INFO_PAR_INDEX: { @@ -199,8 +199,8 @@ ccl_device void svm_node_particle_info( /* Hair Info */ -ccl_device void svm_node_hair_info( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_hair_info( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) { float data; float3 data3; diff --git a/intern/cycles/kernel/svm/svm_gradient.h b/intern/cycles/kernel/svm/svm_gradient.h index 08304bc47e8..cd15f7097e7 100644 --- a/intern/cycles/kernel/svm/svm_gradient.h +++ b/intern/cycles/kernel/svm/svm_gradient.h @@ -60,7 +60,7 @@ ccl_device float svm_gradient(float3 p, NodeGradientType type) return 0.0f; } -ccl_device void svm_node_tex_gradient(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_tex_gradient(ShaderData *sd, float *stack, uint4 node) { uint type, co_offset, color_offset, fac_offset; diff --git a/intern/cycles/kernel/svm/svm_hsv.h b/intern/cycles/kernel/svm/svm_hsv.h index c299cf58c7f..6f49a8385aa 100644 --- a/intern/cycles/kernel/svm/svm_hsv.h +++ b/intern/cycles/kernel/svm/svm_hsv.h @@ -19,8 +19,10 @@ CCL_NAMESPACE_BEGIN -ccl_device void svm_node_hsv( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline void svm_node_hsv(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { uint in_color_offset, fac_offset, out_color_offset; uint hue_offset, sat_offset, val_offset; diff --git a/intern/cycles/kernel/svm/svm_ies.h b/intern/cycles/kernel/svm/svm_ies.h index 56c804b44d0..9c13734ecf0 100644 --- a/intern/cycles/kernel/svm/svm_ies.h +++ b/intern/cycles/kernel/svm/svm_ies.h @@ -19,7 +19,7 @@ CCL_NAMESPACE_BEGIN /* IES Light */ ccl_device_inline float interpolate_ies_vertical( - KernelGlobals *kg, int ofs, int v, int v_num, float v_frac, int h) + const KernelGlobals *kg, int ofs, int v, int v_num, float v_frac, int h) { /* Since lookups are performed in spherical coordinates, clamping the coordinates at the low end * of v (corresponding to the north pole) would result in artifacts. The proper way of dealing @@ -39,7 +39,7 @@ ccl_device_inline float interpolate_ies_vertical( return cubic_interp(a, b, c, d, v_frac); } -ccl_device_inline float kernel_ies_interp(KernelGlobals *kg, +ccl_device_inline float kernel_ies_interp(const KernelGlobals *kg, int slot, float h_angle, float v_angle) @@ -98,8 +98,10 @@ ccl_device_inline float kernel_ies_interp(KernelGlobals *kg, return max(cubic_interp(a, b, c, d, h_frac), 0.0f); } -ccl_device void svm_node_ies( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline void svm_node_ies(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { uint vector_offset, strength_offset, fac_offset, slot = node.z; svm_unpack_node_uchar3(node.y, &strength_offset, &vector_offset, &fac_offset); diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index 9348ddabde5..a344f36977a 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -16,7 +16,7 @@ CCL_NAMESPACE_BEGIN -ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint flags) +ccl_device float4 svm_image_texture(const KernelGlobals *kg, int id, float x, float y, uint flags) { if (id == -1) { return make_float4( @@ -44,8 +44,8 @@ ccl_device_inline float3 texco_remap_square(float3 co) return (co - make_float3(0.5f, 0.5f, 0.5f)) * 2.0f; } -ccl_device void svm_node_tex_image( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_image( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) { uint co_offset, out_offset, alpha_offset, flags; @@ -71,7 +71,7 @@ ccl_device void svm_node_tex_image( int num_nodes = (int)node.y; if (num_nodes > 0) { /* Remember the offset of the node following the tile nodes. */ - int next_offset = (*offset) + num_nodes; + int next_offset = offset + num_nodes; /* Find the tile that the UV lies in. */ int tx = (int)tex_co.x; @@ -83,7 +83,7 @@ ccl_device void svm_node_tex_image( /* Find the index of the tile. */ for (int i = 0; i < num_nodes; i++) { - uint4 tile_node = read_node(kg, offset); + uint4 tile_node = read_node(kg, &offset); if (tile_node.x == tile) { id = tile_node.y; break; @@ -102,7 +102,7 @@ ccl_device void svm_node_tex_image( } /* Skip over the remaining nodes. */ - *offset = next_offset; + offset = next_offset; } else { id = -num_nodes; @@ -114,9 +114,13 @@ ccl_device void svm_node_tex_image( stack_store_float3(stack, out_offset, make_float3(f.x, f.y, f.z)); if (stack_valid(alpha_offset)) stack_store_float(stack, alpha_offset, f.w); + return offset; } -ccl_device void svm_node_tex_image_box(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_tex_image_box(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { /* get object space normal */ float3 N = sd->N; @@ -215,10 +219,10 @@ ccl_device void svm_node_tex_image_box(KernelGlobals *kg, ShaderData *sd, float stack_store_float(stack, alpha_offset, f.w); } -ccl_device void svm_node_tex_environment(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint4 node) +ccl_device_noinline void svm_node_tex_environment(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { uint id = node.y; uint co_offset, out_offset, alpha_offset, flags; diff --git a/intern/cycles/kernel/svm/svm_invert.h b/intern/cycles/kernel/svm/svm_invert.h index 02024742b13..27cdaaff473 100644 --- a/intern/cycles/kernel/svm/svm_invert.h +++ b/intern/cycles/kernel/svm/svm_invert.h @@ -21,7 +21,7 @@ ccl_device float invert(float color, float factor) return factor * (1.0f - color) + (1.0f - factor) * color; } -ccl_device void svm_node_invert( +ccl_device_noinline void svm_node_invert( ShaderData *sd, float *stack, uint in_fac, uint in_color, uint out_color) { float factor = stack_load_float(stack, in_fac); diff --git a/intern/cycles/kernel/svm/svm_light_path.h b/intern/cycles/kernel/svm/svm_light_path.h index 768c65918cd..49fabad1cc5 100644 --- a/intern/cycles/kernel/svm/svm_light_path.h +++ b/intern/cycles/kernel/svm/svm_light_path.h @@ -18,12 +18,12 @@ CCL_NAMESPACE_BEGIN /* Light Path Node */ -ccl_device void svm_node_light_path(ShaderData *sd, - ccl_addr_space PathState *state, - float *stack, - uint type, - uint out_offset, - int path_flag) +ccl_device_noinline void svm_node_light_path(INTEGRATOR_STATE_CONST_ARGS, + const ShaderData *sd, + float *stack, + uint type, + uint out_offset, + int path_flag) { float info = 0.0f; @@ -58,21 +58,47 @@ ccl_device void svm_node_light_path(ShaderData *sd, case NODE_LP_ray_length: info = sd->ray_length; break; - case NODE_LP_ray_depth: - info = (float)state->bounce; + case NODE_LP_ray_depth: { + /* Read bounce from difference location depending if this is a shadow + * path. It's a bit dubious to have integrate state details leak into + * this function but hard to avoid currently. */ + int bounce = (INTEGRATOR_STATE_IS_NULL) ? 0 : + (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(shadow_path, bounce) : + INTEGRATOR_STATE(path, bounce); + + /* For background, light emission and shadow evaluation we from a + * surface or volume we are effective one bounce further. */ + if (path_flag & (PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { + bounce++; + } + + info = (float)bounce; break; + } + /* TODO */ + case NODE_LP_ray_transparent: { + const int bounce = (INTEGRATOR_STATE_IS_NULL) ? + 0 : + (path_flag & PATH_RAY_SHADOW) ? + INTEGRATOR_STATE(shadow_path, transparent_bounce) : + INTEGRATOR_STATE(path, transparent_bounce); + + info = (float)bounce; + break; + } +#if 0 case NODE_LP_ray_diffuse: info = (float)state->diffuse_bounce; break; case NODE_LP_ray_glossy: info = (float)state->glossy_bounce; break; - case NODE_LP_ray_transparent: - info = (float)state->transparent_bounce; - break; +#endif +#if 0 case NODE_LP_ray_transmission: info = (float)state->transmission_bounce; break; +#endif } stack_store_float(stack, out_offset, info); @@ -80,7 +106,7 @@ ccl_device void svm_node_light_path(ShaderData *sd, /* Light Falloff Node */ -ccl_device void svm_node_light_falloff(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_light_falloff(ShaderData *sd, float *stack, uint4 node) { uint strength_offset, out_offset, smooth_offset; diff --git a/intern/cycles/kernel/svm/svm_magic.h b/intern/cycles/kernel/svm/svm_magic.h index 9c160e6d8cc..8784c760860 100644 --- a/intern/cycles/kernel/svm/svm_magic.h +++ b/intern/cycles/kernel/svm/svm_magic.h @@ -87,8 +87,8 @@ ccl_device_noinline_cpu float3 svm_magic(float3 p, int n, float distortion) return make_float3(0.5f - x, 0.5f - y, 0.5f - z); } -ccl_device void svm_node_tex_magic( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_magic( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) { uint depth; uint scale_offset, distortion_offset, co_offset, fac_offset, color_offset; @@ -96,7 +96,7 @@ ccl_device void svm_node_tex_magic( svm_unpack_node_uchar3(node.y, &depth, &color_offset, &fac_offset); svm_unpack_node_uchar3(node.z, &co_offset, &scale_offset, &distortion_offset); - uint4 node2 = read_node(kg, offset); + uint4 node2 = read_node(kg, &offset); float3 co = stack_load_float3(stack, co_offset); float scale = stack_load_float_default(stack, scale_offset, node2.x); float distortion = stack_load_float_default(stack, distortion_offset, node2.y); @@ -107,6 +107,7 @@ ccl_device void svm_node_tex_magic( stack_store_float(stack, fac_offset, average(color)); if (stack_valid(color_offset)) stack_store_float3(stack, color_offset, color); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_map_range.h b/intern/cycles/kernel/svm/svm_map_range.h index 533a631c837..c8684981e31 100644 --- a/intern/cycles/kernel/svm/svm_map_range.h +++ b/intern/cycles/kernel/svm/svm_map_range.h @@ -24,13 +24,13 @@ ccl_device_inline float smootherstep(float edge0, float edge1, float x) return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); } -ccl_device void svm_node_map_range(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint value_stack_offset, - uint parameters_stack_offsets, - uint results_stack_offsets, - int *offset) +ccl_device_noinline int svm_node_map_range(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint value_stack_offset, + uint parameters_stack_offsets, + uint results_stack_offsets, + int offset) { uint from_min_stack_offset, from_max_stack_offset, to_min_stack_offset, to_max_stack_offset; uint type_stack_offset, steps_stack_offset, result_stack_offset; @@ -42,8 +42,8 @@ ccl_device void svm_node_map_range(KernelGlobals *kg, svm_unpack_node_uchar3( results_stack_offsets, &type_stack_offset, &steps_stack_offset, &result_stack_offset); - uint4 defaults = read_node(kg, offset); - uint4 defaults2 = read_node(kg, offset); + uint4 defaults = read_node(kg, &offset); + uint4 defaults2 = read_node(kg, &offset); float value = stack_load_float(stack, value_stack_offset); float from_min = stack_load_float_default(stack, from_min_stack_offset, defaults.x); @@ -83,6 +83,7 @@ ccl_device void svm_node_map_range(KernelGlobals *kg, result = 0.0f; } stack_store_float(stack, result_stack_offset, result); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_mapping.h b/intern/cycles/kernel/svm/svm_mapping.h index 6e19c859e19..fcc724405f5 100644 --- a/intern/cycles/kernel/svm/svm_mapping.h +++ b/intern/cycles/kernel/svm/svm_mapping.h @@ -18,13 +18,12 @@ CCL_NAMESPACE_BEGIN /* Mapping Node */ -ccl_device void svm_node_mapping(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint type, - uint inputs_stack_offsets, - uint result_stack_offset, - int *offset) +ccl_device_noinline void svm_node_mapping(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint type, + uint inputs_stack_offsets, + uint result_stack_offset) { uint vector_stack_offset, location_stack_offset, rotation_stack_offset, scale_stack_offset; svm_unpack_node_uchar4(inputs_stack_offsets, @@ -44,30 +43,40 @@ ccl_device void svm_node_mapping(KernelGlobals *kg, /* Texture Mapping */ -ccl_device void svm_node_texture_mapping( - KernelGlobals *kg, ShaderData *sd, float *stack, uint vec_offset, uint out_offset, int *offset) +ccl_device_noinline int svm_node_texture_mapping(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint vec_offset, + uint out_offset, + int offset) { float3 v = stack_load_float3(stack, vec_offset); Transform tfm; - tfm.x = read_node_float(kg, offset); - tfm.y = read_node_float(kg, offset); - tfm.z = read_node_float(kg, offset); + tfm.x = read_node_float(kg, &offset); + tfm.y = read_node_float(kg, &offset); + tfm.z = read_node_float(kg, &offset); float3 r = transform_point(&tfm, v); stack_store_float3(stack, out_offset, r); + return offset; } -ccl_device void svm_node_min_max( - KernelGlobals *kg, ShaderData *sd, float *stack, uint vec_offset, uint out_offset, int *offset) +ccl_device_noinline int svm_node_min_max(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint vec_offset, + uint out_offset, + int offset) { float3 v = stack_load_float3(stack, vec_offset); - float3 mn = float4_to_float3(read_node_float(kg, offset)); - float3 mx = float4_to_float3(read_node_float(kg, offset)); + float3 mn = float4_to_float3(read_node_float(kg, &offset)); + float3 mx = float4_to_float3(read_node_float(kg, &offset)); float3 r = min(max(mn, v), mx); stack_store_float3(stack, out_offset, r); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_math.h b/intern/cycles/kernel/svm/svm_math.h index 733ea28f9e5..99e7a8f2bda 100644 --- a/intern/cycles/kernel/svm/svm_math.h +++ b/intern/cycles/kernel/svm/svm_math.h @@ -16,13 +16,12 @@ CCL_NAMESPACE_BEGIN -ccl_device void svm_node_math(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint type, - uint inputs_stack_offsets, - uint result_stack_offset, - int *offset) +ccl_device_noinline void svm_node_math(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint type, + uint inputs_stack_offsets, + uint result_stack_offset) { uint a_stack_offset, b_stack_offset, c_stack_offset; svm_unpack_node_uchar3(inputs_stack_offsets, &a_stack_offset, &b_stack_offset, &c_stack_offset); @@ -35,13 +34,13 @@ ccl_device void svm_node_math(KernelGlobals *kg, stack_store_float(stack, result_stack_offset, result); } -ccl_device void svm_node_vector_math(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint type, - uint inputs_stack_offsets, - uint outputs_stack_offsets, - int *offset) +ccl_device_noinline int svm_node_vector_math(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint type, + uint inputs_stack_offsets, + uint outputs_stack_offsets, + int offset) { uint value_stack_offset, vector_stack_offset; uint a_stack_offset, b_stack_offset, param1_stack_offset; @@ -60,7 +59,7 @@ ccl_device void svm_node_vector_math(KernelGlobals *kg, /* 3 Vector Operators */ if (type == NODE_VECTOR_MATH_WRAP || type == NODE_VECTOR_MATH_FACEFORWARD || type == NODE_VECTOR_MATH_MULTIPLY_ADD) { - uint4 extra_node = read_node(kg, offset); + uint4 extra_node = read_node(kg, &offset); c = stack_load_float3(stack, extra_node.x); } @@ -70,6 +69,7 @@ ccl_device void svm_node_vector_math(KernelGlobals *kg, stack_store_float(stack, value_stack_offset, value); if (stack_valid(vector_stack_offset)) stack_store_float3(stack, vector_stack_offset, vector); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_mix.h b/intern/cycles/kernel/svm/svm_mix.h index 15114bfd5e4..3e38080977f 100644 --- a/intern/cycles/kernel/svm/svm_mix.h +++ b/intern/cycles/kernel/svm/svm_mix.h @@ -18,16 +18,16 @@ CCL_NAMESPACE_BEGIN /* Node */ -ccl_device void svm_node_mix(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint fac_offset, - uint c1_offset, - uint c2_offset, - int *offset) +ccl_device_noinline int svm_node_mix(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint fac_offset, + uint c1_offset, + uint c2_offset, + int offset) { /* read extra data */ - uint4 node1 = read_node(kg, offset); + uint4 node1 = read_node(kg, &offset); float fac = stack_load_float(stack, fac_offset); float3 c1 = stack_load_float3(stack, c1_offset); @@ -35,6 +35,7 @@ ccl_device void svm_node_mix(KernelGlobals *kg, float3 result = svm_mix((NodeMix)node1.y, fac, c1, c2); stack_store_float3(stack, node1.z, result); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_musgrave.h b/intern/cycles/kernel/svm/svm_musgrave.h index 571f62fe27f..03a8b68b3ef 100644 --- a/intern/cycles/kernel/svm/svm_musgrave.h +++ b/intern/cycles/kernel/svm/svm_musgrave.h @@ -700,13 +700,13 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_4d( return value; } -ccl_device void svm_node_tex_musgrave(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint offsets1, - uint offsets2, - uint offsets3, - int *offset) +ccl_device_noinline int svm_node_tex_musgrave(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint offsets1, + uint offsets2, + uint offsets3, + int offset) { uint type, dimensions, co_stack_offset, w_stack_offset; uint scale_stack_offset, detail_stack_offset, dimension_stack_offset, lacunarity_stack_offset; @@ -720,8 +720,8 @@ ccl_device void svm_node_tex_musgrave(KernelGlobals *kg, &lacunarity_stack_offset); svm_unpack_node_uchar3(offsets3, &offset_stack_offset, &gain_stack_offset, &fac_stack_offset); - uint4 defaults1 = read_node(kg, offset); - uint4 defaults2 = read_node(kg, offset); + uint4 defaults1 = read_node(kg, &offset); + uint4 defaults2 = read_node(kg, &offset); float3 co = stack_load_float3(stack, co_stack_offset); float w = stack_load_float_default(stack, w_stack_offset, defaults1.x); @@ -844,6 +844,7 @@ ccl_device void svm_node_tex_musgrave(KernelGlobals *kg, } stack_store_float(stack, fac_stack_offset, fac); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_noise.h b/intern/cycles/kernel/svm/svm_noise.h index 94d8bfde555..ecb4df6afdf 100644 --- a/intern/cycles/kernel/svm/svm_noise.h +++ b/intern/cycles/kernel/svm/svm_noise.h @@ -330,7 +330,7 @@ ccl_device_inline ssef grad(const ssei &hash, const ssef &x, const ssef &y) * |__________________________| * */ -ccl_device_noinline float perlin_2d(float x, float y) +ccl_device_noinline_cpu float perlin_2d(float x, float y) { ssei XY; ssef fxy = floorfrac(ssef(x, y, 0.0f, 0.0f), &XY); @@ -447,7 +447,7 @@ ccl_device_inline ssef quad_mix(ssef p, ssef q, ssef r, ssef s, ssef f) * v7 (1, 1, 1) * */ -ccl_device_noinline float perlin_3d(float x, float y, float z) +ccl_device_noinline_cpu float perlin_3d(float x, float y, float z) { ssei XYZ; ssef fxyz = floorfrac(ssef(x, y, z, 0.0f), &XYZ); @@ -501,7 +501,7 @@ ccl_device_noinline float perlin_3d(float x, float y, float z) * v15 (1, 1, 1, 1) * */ -ccl_device_noinline float perlin_4d(float x, float y, float z, float w) +ccl_device_noinline_cpu float perlin_4d(float x, float y, float z, float w) { ssei XYZW; ssef fxyzw = floorfrac(ssef(x, y, z, w), &XYZW); @@ -585,7 +585,7 @@ ccl_device_inline ssef quad_mix(avxf p, avxf q, ssef f) * |__________________________| * */ -ccl_device_noinline float perlin_3d(float x, float y, float z) +ccl_device_noinline_cpu float perlin_3d(float x, float y, float z) { ssei XYZ; ssef fxyz = floorfrac(ssef(x, y, z, 0.0f), &XYZ); @@ -637,7 +637,7 @@ ccl_device_noinline float perlin_3d(float x, float y, float z) * v15 (1, 1, 1, 1) * */ -ccl_device_noinline float perlin_4d(float x, float y, float z, float w) +ccl_device_noinline_cpu float perlin_4d(float x, float y, float z, float w) { ssei XYZW; ssef fxyzw = floorfrac(ssef(x, y, z, w), &XYZW); diff --git a/intern/cycles/kernel/svm/svm_noisetex.h b/intern/cycles/kernel/svm/svm_noisetex.h index 61fd9553802..29b262ac06e 100644 --- a/intern/cycles/kernel/svm/svm_noisetex.h +++ b/intern/cycles/kernel/svm/svm_noisetex.h @@ -140,13 +140,13 @@ ccl_device void noise_texture_4d(float4 co, } } -ccl_device void svm_node_tex_noise(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint dimensions, - uint offsets1, - uint offsets2, - int *offset) +ccl_device_noinline int svm_node_tex_noise(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint dimensions, + uint offsets1, + uint offsets2, + int offset) { uint vector_stack_offset, w_stack_offset, scale_stack_offset; uint detail_stack_offset, roughness_stack_offset, distortion_stack_offset; @@ -160,8 +160,8 @@ ccl_device void svm_node_tex_noise(KernelGlobals *kg, &value_stack_offset, &color_stack_offset); - uint4 defaults1 = read_node(kg, offset); - uint4 defaults2 = read_node(kg, offset); + uint4 defaults1 = read_node(kg, &offset); + uint4 defaults2 = read_node(kg, &offset); float3 vector = stack_load_float3(stack, vector_stack_offset); float w = stack_load_float_default(stack, w_stack_offset, defaults1.x); @@ -212,6 +212,7 @@ ccl_device void svm_node_tex_noise(KernelGlobals *kg, if (stack_valid(color_stack_offset)) { stack_store_float3(stack, color_stack_offset, color); } + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_normal.h b/intern/cycles/kernel/svm/svm_normal.h index 4cd3eab0ed2..724b5f281f9 100644 --- a/intern/cycles/kernel/svm/svm_normal.h +++ b/intern/cycles/kernel/svm/svm_normal.h @@ -16,16 +16,16 @@ CCL_NAMESPACE_BEGIN -ccl_device void svm_node_normal(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint in_normal_offset, - uint out_normal_offset, - uint out_dot_offset, - int *offset) +ccl_device_noinline int svm_node_normal(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint in_normal_offset, + uint out_normal_offset, + uint out_dot_offset, + int offset) { /* read extra data */ - uint4 node1 = read_node(kg, offset); + uint4 node1 = read_node(kg, &offset); float3 normal = stack_load_float3(stack, in_normal_offset); float3 direction; @@ -39,6 +39,7 @@ ccl_device void svm_node_normal(KernelGlobals *kg, if (stack_valid(out_dot_offset)) stack_store_float(stack, out_dot_offset, dot(direction, normalize(normal))); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_ramp.h b/intern/cycles/kernel/svm/svm_ramp.h index 85ccf39144b..e92df3c093c 100644 --- a/intern/cycles/kernel/svm/svm_ramp.h +++ b/intern/cycles/kernel/svm/svm_ramp.h @@ -21,8 +21,12 @@ CCL_NAMESPACE_BEGIN /* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */ -ccl_device_inline float4 rgb_ramp_lookup( - KernelGlobals *kg, int offset, float f, bool interpolate, bool extrapolate, int table_size) +ccl_device_inline float4 rgb_ramp_lookup(const KernelGlobals *kg, + int offset, + float f, + bool interpolate, + bool extrapolate, + int table_size) { if ((f < 0.0f || f > 1.0f) && extrapolate) { float4 t0, dy; @@ -53,34 +57,35 @@ ccl_device_inline float4 rgb_ramp_lookup( return a; } -ccl_device void svm_node_rgb_ramp( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_rgb_ramp( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) { uint fac_offset, color_offset, alpha_offset; uint interpolate = node.z; svm_unpack_node_uchar3(node.y, &fac_offset, &color_offset, &alpha_offset); - uint table_size = read_node(kg, offset).x; + uint table_size = read_node(kg, &offset).x; float fac = stack_load_float(stack, fac_offset); - float4 color = rgb_ramp_lookup(kg, *offset, fac, interpolate, false, table_size); + float4 color = rgb_ramp_lookup(kg, offset, fac, interpolate, false, table_size); if (stack_valid(color_offset)) stack_store_float3(stack, color_offset, float4_to_float3(color)); if (stack_valid(alpha_offset)) stack_store_float(stack, alpha_offset, color.w); - *offset += table_size; + offset += table_size; + return offset; } -ccl_device void svm_node_curves( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_curves( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) { uint fac_offset, color_offset, out_offset; svm_unpack_node_uchar3(node.y, &fac_offset, &color_offset, &out_offset); - uint table_size = read_node(kg, offset).x; + uint table_size = read_node(kg, &offset).x; float fac = stack_load_float(stack, fac_offset); float3 color = stack_load_float3(stack, color_offset); @@ -89,14 +94,15 @@ ccl_device void svm_node_curves( const float range_x = max_x - min_x; const float3 relpos = (color - make_float3(min_x, min_x, min_x)) / range_x; - float r = rgb_ramp_lookup(kg, *offset, relpos.x, true, true, table_size).x; - float g = rgb_ramp_lookup(kg, *offset, relpos.y, true, true, table_size).y; - float b = rgb_ramp_lookup(kg, *offset, relpos.z, true, true, table_size).z; + float r = rgb_ramp_lookup(kg, offset, relpos.x, true, true, table_size).x; + float g = rgb_ramp_lookup(kg, offset, relpos.y, true, true, table_size).y; + float b = rgb_ramp_lookup(kg, offset, relpos.z, true, true, table_size).z; color = (1.0f - fac) * color + fac * make_float3(r, g, b); stack_store_float3(stack, out_offset, color); - *offset += table_size; + offset += table_size; + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_sepcomb_hsv.h b/intern/cycles/kernel/svm/svm_sepcomb_hsv.h index f501252062e..8d52845ea3d 100644 --- a/intern/cycles/kernel/svm/svm_sepcomb_hsv.h +++ b/intern/cycles/kernel/svm/svm_sepcomb_hsv.h @@ -16,15 +16,15 @@ CCL_NAMESPACE_BEGIN -ccl_device void svm_node_combine_hsv(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint hue_in, - uint saturation_in, - uint value_in, - int *offset) +ccl_device_noinline int svm_node_combine_hsv(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint hue_in, + uint saturation_in, + uint value_in, + int offset) { - uint4 node1 = read_node(kg, offset); + uint4 node1 = read_node(kg, &offset); uint color_out = node1.y; float hue = stack_load_float(stack, hue_in); @@ -36,17 +36,18 @@ ccl_device void svm_node_combine_hsv(KernelGlobals *kg, if (stack_valid(color_out)) stack_store_float3(stack, color_out, color); + return offset; } -ccl_device void svm_node_separate_hsv(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint color_in, - uint hue_out, - uint saturation_out, - int *offset) +ccl_device_noinline int svm_node_separate_hsv(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint color_in, + uint hue_out, + uint saturation_out, + int offset) { - uint4 node1 = read_node(kg, offset); + uint4 node1 = read_node(kg, &offset); uint value_out = node1.y; float3 color = stack_load_float3(stack, color_in); @@ -60,6 +61,7 @@ ccl_device void svm_node_separate_hsv(KernelGlobals *kg, stack_store_float(stack, saturation_out, color.y); if (stack_valid(value_out)) stack_store_float(stack, value_out, color.z); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_sky.h b/intern/cycles/kernel/svm/svm_sky.h index b908732f026..b77c4311e72 100644 --- a/intern/cycles/kernel/svm/svm_sky.h +++ b/intern/cycles/kernel/svm/svm_sky.h @@ -37,7 +37,7 @@ ccl_device float sky_perez_function(float *lam, float theta, float gamma) (1.0f + lam[2] * expf(lam[3] * gamma) + lam[4] * cgamma * cgamma); } -ccl_device float3 sky_radiance_preetham(KernelGlobals *kg, +ccl_device float3 sky_radiance_preetham(const KernelGlobals *kg, float3 dir, float sunphi, float suntheta, @@ -90,7 +90,7 @@ ccl_device float sky_radiance_internal(float *configuration, float theta, float configuration[6] * mieM + configuration[7] * zenith); } -ccl_device float3 sky_radiance_hosek(KernelGlobals *kg, +ccl_device float3 sky_radiance_hosek(const KernelGlobals *kg, float3 dir, float sunphi, float suntheta, @@ -127,7 +127,7 @@ ccl_device float3 geographical_to_direction(float lat, float lon) return make_float3(cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat)); } -ccl_device float3 sky_radiance_nishita(KernelGlobals *kg, +ccl_device float3 sky_radiance_nishita(const KernelGlobals *kg, float3 dir, float *nishita_data, uint texture_id) @@ -209,8 +209,8 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg, return xyz_to_rgb(kg, xyz); } -ccl_device void svm_node_tex_sky( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_sky( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) { /* Load data */ uint dir_offset = node.y; @@ -226,49 +226,49 @@ ccl_device void svm_node_tex_sky( float sunphi, suntheta, radiance_x, radiance_y, radiance_z; float config_x[9], config_y[9], config_z[9]; - float4 data = read_node_float(kg, offset); + float4 data = read_node_float(kg, &offset); sunphi = data.x; suntheta = data.y; radiance_x = data.z; radiance_y = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); radiance_z = data.x; config_x[0] = data.y; config_x[1] = data.z; config_x[2] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_x[3] = data.x; config_x[4] = data.y; config_x[5] = data.z; config_x[6] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_x[7] = data.x; config_x[8] = data.y; config_y[0] = data.z; config_y[1] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_y[2] = data.x; config_y[3] = data.y; config_y[4] = data.z; config_y[5] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_y[6] = data.x; config_y[7] = data.y; config_y[8] = data.z; config_z[0] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_z[1] = data.x; config_z[2] = data.y; config_z[3] = data.z; config_z[4] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_z[5] = data.x; config_z[6] = data.y; config_z[7] = data.z; @@ -305,19 +305,19 @@ ccl_device void svm_node_tex_sky( /* Define variables */ float nishita_data[10]; - float4 data = read_node_float(kg, offset); + float4 data = read_node_float(kg, &offset); nishita_data[0] = data.x; nishita_data[1] = data.y; nishita_data[2] = data.z; nishita_data[3] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); nishita_data[4] = data.x; nishita_data[5] = data.y; nishita_data[6] = data.z; nishita_data[7] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); nishita_data[8] = data.x; nishita_data[9] = data.y; uint texture_id = __float_as_uint(data.z); @@ -327,6 +327,7 @@ ccl_device void svm_node_tex_sky( } stack_store_float3(stack, out_offset, f); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index 46600551cc4..a35253080da 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -14,12 +14,16 @@ * limitations under the License. */ +#include "kernel/geom/geom.h" +#include "kernel/kernel_camera.h" +#include "kernel/kernel_montecarlo.h" + CCL_NAMESPACE_BEGIN /* Texture Coordinate Node */ -ccl_device void svm_node_tex_coord( - KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_coord( + const KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int offset) { float3 data; uint type = node.y; @@ -35,9 +39,9 @@ ccl_device void svm_node_tex_coord( } else { Transform tfm; - tfm.x = read_node_float(kg, offset); - tfm.y = read_node_float(kg, offset); - tfm.z = read_node_float(kg, offset); + tfm.x = read_node_float(kg, &offset); + tfm.y = read_node_float(kg, &offset); + tfm.z = read_node_float(kg, &offset); data = transform_point(&tfm, data); } break; @@ -92,10 +96,11 @@ ccl_device void svm_node_tex_coord( } stack_store_float3(stack, out_offset, data); + return offset; } -ccl_device void svm_node_tex_coord_bump_dx( - KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_coord_bump_dx( + const KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -112,9 +117,9 @@ ccl_device void svm_node_tex_coord_bump_dx( } else { Transform tfm; - tfm.x = read_node_float(kg, offset); - tfm.y = read_node_float(kg, offset); - tfm.z = read_node_float(kg, offset); + tfm.x = read_node_float(kg, &offset); + tfm.y = read_node_float(kg, &offset); + tfm.z = read_node_float(kg, &offset); data = transform_point(&tfm, data); } break; @@ -136,7 +141,7 @@ ccl_device void svm_node_tex_coord_bump_dx( case NODE_TEXCO_WINDOW: { if ((path_flag & PATH_RAY_CAMERA) && sd->object == OBJECT_NONE && kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) - data = camera_world_to_ndc(kg, sd, sd->ray_P + sd->ray_dP.dx); + data = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(sd->ray_dP, 0.0f, 0.0f)); else data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dx); data.z = 0.0f; @@ -169,13 +174,14 @@ ccl_device void svm_node_tex_coord_bump_dx( } stack_store_float3(stack, out_offset, data); + return offset; #else - svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); + return svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); #endif } -ccl_device void svm_node_tex_coord_bump_dy( - KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_coord_bump_dy( + const KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -192,9 +198,9 @@ ccl_device void svm_node_tex_coord_bump_dy( } else { Transform tfm; - tfm.x = read_node_float(kg, offset); - tfm.y = read_node_float(kg, offset); - tfm.z = read_node_float(kg, offset); + tfm.x = read_node_float(kg, &offset); + tfm.y = read_node_float(kg, &offset); + tfm.z = read_node_float(kg, &offset); data = transform_point(&tfm, data); } break; @@ -216,7 +222,7 @@ ccl_device void svm_node_tex_coord_bump_dy( case NODE_TEXCO_WINDOW: { if ((path_flag & PATH_RAY_CAMERA) && sd->object == OBJECT_NONE && kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) - data = camera_world_to_ndc(kg, sd, sd->ray_P + sd->ray_dP.dy); + data = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(0.0f, sd->ray_dP, 0.0f)); else data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dy); data.z = 0.0f; @@ -249,12 +255,16 @@ ccl_device void svm_node_tex_coord_bump_dy( } stack_store_float3(stack, out_offset, data); + return offset; #else - svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); + return svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); #endif } -ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_normal_map(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { uint color_offset, strength_offset, normal_offset, space; svm_unpack_node_uchar4(node.y, &color_offset, &strength_offset, &normal_offset, &space); @@ -346,7 +356,10 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st stack_store_float3(stack, normal_offset, N); } -ccl_device void svm_node_tangent(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_tangent(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { uint tangent_offset, direction_type, axis; svm_unpack_node_uchar3(node.y, &tangent_offset, &direction_type, &axis); diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 062afcfa5ac..c053be96c51 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -30,37 +30,6 @@ CCL_NAMESPACE_BEGIN /* Nodes */ -/* Known frequencies of used nodes, used for selective nodes compilation - * in the kernel. Currently only affects split OpenCL kernel. - * - * Keep as defines so it's easy to check which nodes are to be compiled - * from preprocessor. - * - * Lower the number of group more often the node is used. - */ -#define NODE_GROUP_LEVEL_0 0 -#define NODE_GROUP_LEVEL_1 1 -#define NODE_GROUP_LEVEL_2 2 -#define NODE_GROUP_LEVEL_3 3 -#define NODE_GROUP_LEVEL_4 4 -#define NODE_GROUP_LEVEL_MAX NODE_GROUP_LEVEL_4 - -#define NODE_FEATURE_VOLUME (1 << 0) -#define NODE_FEATURE_HAIR (1 << 1) -#define NODE_FEATURE_BUMP (1 << 2) -#define NODE_FEATURE_BUMP_STATE (1 << 3) -#define NODE_FEATURE_VORONOI_EXTRA (1 << 4) -/* TODO(sergey): Consider using something like ((uint)(-1)). - * Need to check carefully operand types around usage of this - * define first. - */ -#define NODE_FEATURE_ALL \ - (NODE_FEATURE_VOLUME | NODE_FEATURE_HAIR | NODE_FEATURE_BUMP | NODE_FEATURE_BUMP_STATE | \ - NODE_FEATURE_VORONOI_EXTRA) - -#define NODES_GROUP(group) ((group) <= __NODES_MAX_GROUP__) -#define NODES_FEATURE(feature) ((__NODES_FEATURES__ & (feature)) != 0) - typedef enum ShaderNodeType { NODE_END = 0, NODE_SHADER_JUMP, @@ -572,12 +541,8 @@ typedef enum ClosureType { CLOSURE_BSDF_TRANSPARENT_ID, /* BSSRDF */ - CLOSURE_BSSRDF_CUBIC_ID, - CLOSURE_BSSRDF_GAUSSIAN_ID, - CLOSURE_BSSRDF_PRINCIPLED_ID, - CLOSURE_BSSRDF_BURLEY_ID, CLOSURE_BSSRDF_RANDOM_WALK_ID, - CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID, + CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID, /* Other */ CLOSURE_HOLDOUT_ID, @@ -620,11 +585,9 @@ typedef enum ClosureType { type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID || \ type == CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID || \ type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) -#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) +#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) #define CLOSURE_IS_BSSRDF(type) \ - (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) -#define CLOSURE_IS_DISK_BSSRDF(type) \ - (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_BURLEY_ID) + (type >= CLOSURE_BSSRDF_RANDOM_WALK_ID && type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) #define CLOSURE_IS_VOLUME(type) \ (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) #define CLOSURE_IS_VOLUME_SCATTER(type) (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) diff --git a/intern/cycles/kernel/svm/svm_value.h b/intern/cycles/kernel/svm/svm_value.h index 5b76f2c8832..d0478660094 100644 --- a/intern/cycles/kernel/svm/svm_value.h +++ b/intern/cycles/kernel/svm/svm_value.h @@ -19,20 +19,21 @@ CCL_NAMESPACE_BEGIN /* Value Nodes */ ccl_device void svm_node_value_f( - KernelGlobals *kg, ShaderData *sd, float *stack, uint ivalue, uint out_offset) + const KernelGlobals *kg, ShaderData *sd, float *stack, uint ivalue, uint out_offset) { stack_store_float(stack, out_offset, __uint_as_float(ivalue)); } -ccl_device void svm_node_value_v( - KernelGlobals *kg, ShaderData *sd, float *stack, uint out_offset, int *offset) +ccl_device int svm_node_value_v( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint out_offset, int offset) { /* read extra data */ - uint4 node1 = read_node(kg, offset); + uint4 node1 = read_node(kg, &offset); float3 p = make_float3( __uint_as_float(node1.y), __uint_as_float(node1.z), __uint_as_float(node1.w)); stack_store_float3(stack, out_offset, p); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_vector_rotate.h b/intern/cycles/kernel/svm/svm_vector_rotate.h index 50045752484..55e1bce0158 100644 --- a/intern/cycles/kernel/svm/svm_vector_rotate.h +++ b/intern/cycles/kernel/svm/svm_vector_rotate.h @@ -18,11 +18,11 @@ CCL_NAMESPACE_BEGIN /* Vector Rotate */ -ccl_device void svm_node_vector_rotate(ShaderData *sd, - float *stack, - uint input_stack_offsets, - uint axis_stack_offsets, - uint result_stack_offset) +ccl_device_noinline void svm_node_vector_rotate(ShaderData *sd, + float *stack, + uint input_stack_offsets, + uint axis_stack_offsets, + uint result_stack_offset) { uint type, vector_stack_offset, rotation_stack_offset, center_stack_offset, axis_stack_offset, angle_stack_offset, invert; diff --git a/intern/cycles/kernel/svm/svm_vector_transform.h b/intern/cycles/kernel/svm/svm_vector_transform.h index 1e95492cf1b..8aedb7e0f54 100644 --- a/intern/cycles/kernel/svm/svm_vector_transform.h +++ b/intern/cycles/kernel/svm/svm_vector_transform.h @@ -18,10 +18,10 @@ CCL_NAMESPACE_BEGIN /* Vector Transform */ -ccl_device void svm_node_vector_transform(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint4 node) +ccl_device_noinline void svm_node_vector_transform(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { uint itype, ifrom, ito; uint vector_in, vector_out; diff --git a/intern/cycles/kernel/svm/svm_vertex_color.h b/intern/cycles/kernel/svm/svm_vertex_color.h index 0aa45835522..986ea244f3a 100644 --- a/intern/cycles/kernel/svm/svm_vertex_color.h +++ b/intern/cycles/kernel/svm/svm_vertex_color.h @@ -16,12 +16,12 @@ CCL_NAMESPACE_BEGIN -ccl_device void svm_node_vertex_color(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint layer_id, - uint color_offset, - uint alpha_offset) +ccl_device_noinline void svm_node_vertex_color(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint layer_id, + uint color_offset, + uint alpha_offset) { AttributeDescriptor descriptor = find_attribute(kg, sd, layer_id); if (descriptor.offset != ATTR_STD_NOT_FOUND) { @@ -35,18 +35,12 @@ ccl_device void svm_node_vertex_color(KernelGlobals *kg, } } -#ifndef __KERNEL_CUDA__ -ccl_device -#else -ccl_device_noinline -#endif - void - svm_node_vertex_color_bump_dx(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint layer_id, - uint color_offset, - uint alpha_offset) +ccl_device_noinline void svm_node_vertex_color_bump_dx(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint layer_id, + uint color_offset, + uint alpha_offset) { AttributeDescriptor descriptor = find_attribute(kg, sd, layer_id); if (descriptor.offset != ATTR_STD_NOT_FOUND) { @@ -62,18 +56,12 @@ ccl_device_noinline } } -#ifndef __KERNEL_CUDA__ -ccl_device -#else -ccl_device_noinline -#endif - void - svm_node_vertex_color_bump_dy(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint layer_id, - uint color_offset, - uint alpha_offset) +ccl_device_noinline void svm_node_vertex_color_bump_dy(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint layer_id, + uint color_offset, + uint alpha_offset) { AttributeDescriptor descriptor = find_attribute(kg, sd, layer_id); if (descriptor.offset != ATTR_STD_NOT_FOUND) { diff --git a/intern/cycles/kernel/svm/svm_voronoi.h b/intern/cycles/kernel/svm/svm_voronoi.h index d0e7db35fab..b1d2eff7f37 100644 --- a/intern/cycles/kernel/svm/svm_voronoi.h +++ b/intern/cycles/kernel/svm/svm_voronoi.h @@ -902,16 +902,17 @@ ccl_device void voronoi_n_sphere_radius_4d(float4 coord, float randomness, float *outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0f; } -ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint dimensions, - uint feature, - uint metric, - int *offset) +template +ccl_device_noinline int svm_node_tex_voronoi(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint dimensions, + uint feature, + uint metric, + int offset) { - uint4 stack_offsets = read_node(kg, offset); - uint4 defaults = read_node(kg, offset); + uint4 stack_offsets = read_node(kg, &offset); + uint4 defaults = read_node(kg, &offset); uint coord_stack_offset, w_stack_offset, scale_stack_offset, smoothness_stack_offset; uint exponent_stack_offset, randomness_stack_offset, distance_out_stack_offset, @@ -997,18 +998,18 @@ ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, &color_out, &position_out_2d); break; -#if NODES_FEATURE(NODE_FEATURE_VORONOI_EXTRA) case NODE_VORONOI_SMOOTH_F1: - voronoi_smooth_f1_2d(coord_2d, - smoothness, - exponent, - randomness, - voronoi_metric, - &distance_out, - &color_out, - &position_out_2d); + if (KERNEL_NODES_FEATURE(VORONOI_EXTRA)) { + voronoi_smooth_f1_2d(coord_2d, + smoothness, + exponent, + randomness, + voronoi_metric, + &distance_out, + &color_out, + &position_out_2d); + } break; -#endif case NODE_VORONOI_F2: voronoi_f2_2d(coord_2d, exponent, @@ -1042,18 +1043,18 @@ ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, &color_out, &position_out); break; -#if NODES_FEATURE(NODE_FEATURE_VORONOI_EXTRA) case NODE_VORONOI_SMOOTH_F1: - voronoi_smooth_f1_3d(coord, - smoothness, - exponent, - randomness, - voronoi_metric, - &distance_out, - &color_out, - &position_out); + if (KERNEL_NODES_FEATURE(VORONOI_EXTRA)) { + voronoi_smooth_f1_3d(coord, + smoothness, + exponent, + randomness, + voronoi_metric, + &distance_out, + &color_out, + &position_out); + } break; -#endif case NODE_VORONOI_F2: voronoi_f2_3d(coord, exponent, @@ -1076,54 +1077,54 @@ ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, break; } -#if NODES_FEATURE(NODE_FEATURE_VORONOI_EXTRA) case 4: { - float4 coord_4d = make_float4(coord.x, coord.y, coord.z, w); - float4 position_out_4d; - switch (voronoi_feature) { - case NODE_VORONOI_F1: - voronoi_f1_4d(coord_4d, - exponent, - randomness, - voronoi_metric, - &distance_out, - &color_out, - &position_out_4d); - break; - case NODE_VORONOI_SMOOTH_F1: - voronoi_smooth_f1_4d(coord_4d, - smoothness, - exponent, - randomness, - voronoi_metric, - &distance_out, - &color_out, - &position_out_4d); - break; - case NODE_VORONOI_F2: - voronoi_f2_4d(coord_4d, - exponent, - randomness, - voronoi_metric, - &distance_out, - &color_out, - &position_out_4d); - break; - case NODE_VORONOI_DISTANCE_TO_EDGE: - voronoi_distance_to_edge_4d(coord_4d, randomness, &distance_out); - break; - case NODE_VORONOI_N_SPHERE_RADIUS: - voronoi_n_sphere_radius_4d(coord_4d, randomness, &radius_out); - break; - default: - kernel_assert(0); + if (KERNEL_NODES_FEATURE(VORONOI_EXTRA)) { + float4 coord_4d = make_float4(coord.x, coord.y, coord.z, w); + float4 position_out_4d; + switch (voronoi_feature) { + case NODE_VORONOI_F1: + voronoi_f1_4d(coord_4d, + exponent, + randomness, + voronoi_metric, + &distance_out, + &color_out, + &position_out_4d); + break; + case NODE_VORONOI_SMOOTH_F1: + voronoi_smooth_f1_4d(coord_4d, + smoothness, + exponent, + randomness, + voronoi_metric, + &distance_out, + &color_out, + &position_out_4d); + break; + case NODE_VORONOI_F2: + voronoi_f2_4d(coord_4d, + exponent, + randomness, + voronoi_metric, + &distance_out, + &color_out, + &position_out_4d); + break; + case NODE_VORONOI_DISTANCE_TO_EDGE: + voronoi_distance_to_edge_4d(coord_4d, randomness, &distance_out); + break; + case NODE_VORONOI_N_SPHERE_RADIUS: + voronoi_n_sphere_radius_4d(coord_4d, randomness, &radius_out); + break; + default: + kernel_assert(0); + } + position_out_4d = safe_divide_float4_float(position_out_4d, scale); + position_out = make_float3(position_out_4d.x, position_out_4d.y, position_out_4d.z); + w_out = position_out_4d.w; } - position_out_4d = safe_divide_float4_float(position_out_4d, scale); - position_out = make_float3(position_out_4d.x, position_out_4d.y, position_out_4d.z); - w_out = position_out_4d.w; break; } -#endif default: kernel_assert(0); } @@ -1138,6 +1139,7 @@ ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, stack_store_float(stack, w_out_stack_offset, w_out); if (stack_valid(radius_out_stack_offset)) stack_store_float(stack, radius_out_stack_offset, radius_out); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_voxel.h b/intern/cycles/kernel/svm/svm_voxel.h index 4bc14f82382..78b75405356 100644 --- a/intern/cycles/kernel/svm/svm_voxel.h +++ b/intern/cycles/kernel/svm/svm_voxel.h @@ -19,8 +19,8 @@ CCL_NAMESPACE_BEGIN /* TODO(sergey): Think of making it more generic volume-type attribute * sampler. */ -ccl_device void svm_node_tex_voxel( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_voxel( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) { uint co_offset, density_out_offset, color_out_offset, space; svm_unpack_node_uchar4(node.z, &co_offset, &density_out_offset, &color_out_offset, &space); @@ -33,9 +33,9 @@ ccl_device void svm_node_tex_voxel( else { kernel_assert(space == NODE_TEX_VOXEL_SPACE_WORLD); Transform tfm; - tfm.x = read_node_float(kg, offset); - tfm.y = read_node_float(kg, offset); - tfm.z = read_node_float(kg, offset); + tfm.x = read_node_float(kg, &offset); + tfm.y = read_node_float(kg, &offset); + tfm.z = read_node_float(kg, &offset); co = transform_point(&tfm, co); } @@ -47,6 +47,7 @@ ccl_device void svm_node_tex_voxel( stack_store_float(stack, density_out_offset, r.w); if (stack_valid(color_out_offset)) stack_store_float3(stack, color_out_offset, make_float3(r.x, r.y, r.z)); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_wave.h b/intern/cycles/kernel/svm/svm_wave.h index c4763475b47..00f980c16df 100644 --- a/intern/cycles/kernel/svm/svm_wave.h +++ b/intern/cycles/kernel/svm/svm_wave.h @@ -82,11 +82,11 @@ ccl_device_noinline_cpu float svm_wave(NodeWaveType type, } } -ccl_device void svm_node_tex_wave( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_wave( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) { - uint4 node2 = read_node(kg, offset); - uint4 node3 = read_node(kg, offset); + uint4 node2 = read_node(kg, &offset); + uint4 node3 = read_node(kg, &offset); /* RNA properties */ uint type_offset, bands_dir_offset, rings_dir_offset, profile_offset; @@ -125,6 +125,7 @@ ccl_device void svm_node_tex_wave( stack_store_float(stack, fac_offset, f); if (stack_valid(color_offset)) stack_store_float3(stack, color_offset, make_float3(f, f, f)); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_wavelength.h b/intern/cycles/kernel/svm/svm_wavelength.h index d6144802559..fba8aa63d31 100644 --- a/intern/cycles/kernel/svm/svm_wavelength.h +++ b/intern/cycles/kernel/svm/svm_wavelength.h @@ -69,8 +69,8 @@ ccl_static_constant float cie_colour_match[81][3] = { {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}}; -ccl_device void svm_node_wavelength( - KernelGlobals *kg, ShaderData *sd, float *stack, uint wavelength, uint color_out) +ccl_device_noinline void svm_node_wavelength( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint wavelength, uint color_out) { float lambda_nm = stack_load_float(stack, wavelength); float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); // scaled 0..80 diff --git a/intern/cycles/kernel/svm/svm_white_noise.h b/intern/cycles/kernel/svm/svm_white_noise.h index b30d85acaec..0306d2e7b9c 100644 --- a/intern/cycles/kernel/svm/svm_white_noise.h +++ b/intern/cycles/kernel/svm/svm_white_noise.h @@ -16,13 +16,12 @@ CCL_NAMESPACE_BEGIN -ccl_device void svm_node_tex_white_noise(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint dimensions, - uint inputs_stack_offsets, - uint ouptuts_stack_offsets, - int *offset) +ccl_device_noinline void svm_node_tex_white_noise(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint dimensions, + uint inputs_stack_offsets, + uint ouptuts_stack_offsets) { uint vector_stack_offset, w_stack_offset, value_stack_offset, color_stack_offset; svm_unpack_node_uchar2(inputs_stack_offsets, &vector_stack_offset, &w_stack_offset); diff --git a/intern/cycles/kernel/svm/svm_wireframe.h b/intern/cycles/kernel/svm/svm_wireframe.h index 49158bd86d5..7ec913789d2 100644 --- a/intern/cycles/kernel/svm/svm_wireframe.h +++ b/intern/cycles/kernel/svm/svm_wireframe.h @@ -35,7 +35,7 @@ CCL_NAMESPACE_BEGIN /* Wireframe Node */ ccl_device_inline float wireframe( - KernelGlobals *kg, ShaderData *sd, float size, int pixel_size, float3 *P) + const KernelGlobals *kg, ShaderData *sd, float size, int pixel_size, float3 *P) { #ifdef __HAIR__ if (sd->prim != PRIM_NONE && sd->type & PRIMITIVE_ALL_TRIANGLE) @@ -88,7 +88,10 @@ ccl_device_inline float wireframe( return 0.0f; } -ccl_device void svm_node_wireframe(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_wireframe(const KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node) { uint in_size = node.y; uint out_fac = node.z; @@ -100,18 +103,7 @@ ccl_device void svm_node_wireframe(KernelGlobals *kg, ShaderData *sd, float *sta int pixel_size = (int)use_pixel_size; /* Calculate wireframe */ -#ifdef __SPLIT_KERNEL__ - /* TODO(sergey): This is because sd is actually a global space, - * which makes it difficult to re-use same wireframe() function. - * - * With OpenCL 2.0 it's possible to avoid this change, but for until - * then we'll be living with such an exception. - */ - float3 P = sd->P; - float f = wireframe(kg, sd, size, pixel_size, &P); -#else float f = wireframe(kg, sd, size, pixel_size, &sd->P); -#endif /* TODO(sergey): Think of faster way to calculate derivatives. */ if (bump_offset == NODE_BUMP_OFFSET_DX) { diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index feead27c5ca..6edb5261b32 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -32,10 +32,10 @@ set(SRC camera.cpp colorspace.cpp constant_fold.cpp - coverage.cpp denoising.cpp film.cpp geometry.cpp + gpu_display.cpp graph.cpp hair.cpp image.cpp @@ -54,6 +54,7 @@ set(SRC object.cpp osl.cpp particles.cpp + pass.cpp curves.cpp scene.cpp session.cpp @@ -76,10 +77,10 @@ set(SRC_HEADERS camera.h colorspace.h constant_fold.h - coverage.h denoising.h film.h geometry.h + gpu_display.h graph.h hair.h image.h @@ -95,6 +96,7 @@ set(SRC_HEADERS object.h osl.h particles.h + pass.h procedural.h curves.h scene.h @@ -111,6 +113,7 @@ set(SRC_HEADERS set(LIB cycles_bvh cycles_device + cycles_integrator cycles_subd cycles_util ) diff --git a/intern/cycles/render/background.cpp b/intern/cycles/render/background.cpp index b925e755434..ae6290ac27b 100644 --- a/intern/cycles/render/background.cpp +++ b/intern/cycles/render/background.cpp @@ -34,11 +34,7 @@ NODE_DEFINE(Background) { NodeType *type = NodeType::add("background", create); - SOCKET_FLOAT(ao_factor, "AO Factor", 0.0f); - SOCKET_FLOAT(ao_distance, "AO Distance", FLT_MAX); - SOCKET_BOOLEAN(use_shader, "Use Shader", true); - SOCKET_BOOLEAN(use_ao, "Use AO", false); SOCKET_UINT(visibility, "Visibility", PATH_RAY_ALL_VISIBILITY); SOCKET_BOOLEAN(transparent, "Transparent", false); @@ -80,10 +76,6 @@ void Background::device_update(Device *device, DeviceScene *dscene, Scene *scene /* set shader index and transparent option */ KernelBackground *kbackground = &dscene->data.background; - kbackground->ao_factor = (use_ao) ? ao_factor : 0.0f; - kbackground->ao_bounces_factor = ao_factor; - kbackground->ao_distance = ao_distance; - kbackground->transparent = transparent; kbackground->surface_shader = scene->shader_manager->get_shader_id(bg_shader); @@ -138,10 +130,6 @@ void Background::tag_update(Scene *scene) * and to avoid doing unnecessary updates anywhere else. */ tag_use_shader_modified(); } - - if (ao_factor_is_modified() || use_ao_is_modified()) { - scene->integrator->tag_update(scene, Integrator::BACKGROUND_AO_MODIFIED); - } } Shader *Background::get_shader(const Scene *scene) diff --git a/intern/cycles/render/background.h b/intern/cycles/render/background.h index e89ffbc2445..2f7ef0f7737 100644 --- a/intern/cycles/render/background.h +++ b/intern/cycles/render/background.h @@ -32,11 +32,7 @@ class Background : public Node { public: NODE_DECLARE - NODE_SOCKET_API(float, ao_factor) - NODE_SOCKET_API(float, ao_distance) - NODE_SOCKET_API(bool, use_shader) - NODE_SOCKET_API(bool, use_ao) NODE_SOCKET_API(uint, visibility) NODE_SOCKET_API(Shader *, shader) diff --git a/intern/cycles/render/bake.cpp b/intern/cycles/render/bake.cpp index 317a3937cab..54e496caed6 100644 --- a/intern/cycles/render/bake.cpp +++ b/intern/cycles/render/bake.cpp @@ -26,58 +26,8 @@ CCL_NAMESPACE_BEGIN -static int aa_samples(Scene *scene, Object *object, ShaderEvalType type) -{ - if (type == SHADER_EVAL_UV || type == SHADER_EVAL_ROUGHNESS) { - return 1; - } - else if (type == SHADER_EVAL_NORMAL) { - /* Only antialias normal if mesh has bump mapping. */ - if (object->get_geometry()) { - foreach (Node *node, object->get_geometry()->get_used_shaders()) { - Shader *shader = static_cast(node); - if (shader->has_bump) { - return scene->integrator->get_aa_samples(); - } - } - } - - return 1; - } - else { - return scene->integrator->get_aa_samples(); - } -} - -/* Keep it synced with kernel_bake.h logic */ -static int shader_type_to_pass_filter(ShaderEvalType type, int pass_filter) -{ - const int component_flags = pass_filter & - (BAKE_FILTER_DIRECT | BAKE_FILTER_INDIRECT | BAKE_FILTER_COLOR); - - switch (type) { - case SHADER_EVAL_AO: - return BAKE_FILTER_AO; - case SHADER_EVAL_SHADOW: - return BAKE_FILTER_DIRECT; - case SHADER_EVAL_DIFFUSE: - return BAKE_FILTER_DIFFUSE | component_flags; - case SHADER_EVAL_GLOSSY: - return BAKE_FILTER_GLOSSY | component_flags; - case SHADER_EVAL_TRANSMISSION: - return BAKE_FILTER_TRANSMISSION | component_flags; - case SHADER_EVAL_COMBINED: - return pass_filter; - default: - return 0; - } -} - BakeManager::BakeManager() { - type = SHADER_EVAL_BAKE; - pass_filter = 0; - need_update_ = true; } @@ -85,32 +35,14 @@ BakeManager::~BakeManager() { } -bool BakeManager::get_baking() +bool BakeManager::get_baking() const { return !object_name.empty(); } -void BakeManager::set(Scene *scene, - const std::string &object_name_, - ShaderEvalType type_, - int pass_filter_) +void BakeManager::set(Scene *scene, const std::string &object_name_) { object_name = object_name_; - type = type_; - pass_filter = shader_type_to_pass_filter(type_, pass_filter_); - - Pass::add(PASS_BAKE_PRIMITIVE, scene->passes); - Pass::add(PASS_BAKE_DIFFERENTIAL, scene->passes); - - if (type == SHADER_EVAL_UV) { - /* force UV to be available */ - Pass::add(PASS_UV, scene->passes); - } - - /* force use_light_pass to be true if we bake more than just colors */ - if (pass_filter & ~BAKE_FILTER_COLOR) { - Pass::add(PASS_LIGHT, scene->passes); - } /* create device and update scene */ scene->film->tag_modified(); @@ -127,29 +59,29 @@ void BakeManager::device_update(Device * /*device*/, if (!need_update()) return; - scoped_callback_timer timer([scene](double time) { - if (scene->update_stats) { - scene->update_stats->bake.times.add_entry({"device_update", time}); - } - }); - - KernelIntegrator *kintegrator = &dscene->data.integrator; KernelBake *kbake = &dscene->data.bake; + memset(kbake, 0, sizeof(*kbake)); - kbake->type = type; - kbake->pass_filter = pass_filter; + if (!object_name.empty()) { + scoped_callback_timer timer([scene](double time) { + if (scene->update_stats) { + scene->update_stats->bake.times.add_entry({"device_update", time}); + } + }); - int object_index = 0; - foreach (Object *object, scene->objects) { - const Geometry *geom = object->get_geometry(); - if (object->name == object_name && geom->geometry_type == Geometry::MESH) { - kbake->object_index = object_index; - kbake->tri_offset = geom->prim_offset; - kintegrator->aa_samples = aa_samples(scene, object, type); - break; + kbake->use = true; + + int object_index = 0; + foreach (Object *object, scene->objects) { + const Geometry *geom = object->get_geometry(); + if (object->name == object_name && geom->geometry_type == Geometry::MESH) { + kbake->object_index = object_index; + kbake->tri_offset = geom->prim_offset; + break; + } + + object_index++; } - - object_index++; } need_update_ = false; diff --git a/intern/cycles/render/bake.h b/intern/cycles/render/bake.h index 655b9b1cf7e..39e504490c2 100644 --- a/intern/cycles/render/bake.h +++ b/intern/cycles/render/bake.h @@ -30,8 +30,8 @@ class BakeManager { BakeManager(); ~BakeManager(); - void set(Scene *scene, const std::string &object_name, ShaderEvalType type, int pass_filter); - bool get_baking(); + void set(Scene *scene, const std::string &object_name); + bool get_baking() const; void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); void device_free(Device *device, DeviceScene *dscene); @@ -42,8 +42,6 @@ class BakeManager { private: bool need_update_; - ShaderEvalType type; - int pass_filter; std::string object_name; }; diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index fcfad58995e..1cdae3af7f5 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -28,112 +28,229 @@ CCL_NAMESPACE_BEGIN -/* Buffer Params */ +/* -------------------------------------------------------------------- + * Convert part information to an index of `BufferParams::pass_offset_`. + */ -BufferParams::BufferParams() +static int pass_type_mode_to_index(PassType pass_type, PassMode mode) { - width = 0; - height = 0; + int index = static_cast(pass_type) * 2; - full_x = 0; - full_y = 0; - full_width = 0; - full_height = 0; + if (mode == PassMode::DENOISED) { + ++index; + } - denoising_data_pass = false; - denoising_clean_pass = false; - denoising_prefiltered_pass = false; - - Pass::add(PASS_COMBINED, passes); + return index; } -void BufferParams::get_offset_stride(int &offset, int &stride) +static int pass_to_index(const BufferPass &pass) +{ + return pass_type_mode_to_index(pass.type, pass.mode); +} + +/* -------------------------------------------------------------------- + * Buffer pass. + */ + +NODE_DEFINE(BufferPass) +{ + NodeType *type = NodeType::add("buffer_pass", create); + + const NodeEnum *pass_type_enum = Pass::get_type_enum(); + const NodeEnum *pass_mode_enum = Pass::get_mode_enum(); + + SOCKET_ENUM(type, "Type", *pass_type_enum, PASS_COMBINED); + SOCKET_ENUM(mode, "Mode", *pass_mode_enum, static_cast(PassMode::DENOISED)); + SOCKET_STRING(name, "Name", ustring()); + SOCKET_BOOLEAN(include_albedo, "Include Albedo", false); + + SOCKET_INT(offset, "Offset", -1); + + return type; +} + +BufferPass::BufferPass() : Node(get_node_type()) +{ +} + +BufferPass::BufferPass(const Pass *scene_pass) + : Node(get_node_type()), + type(scene_pass->get_type()), + mode(scene_pass->get_mode()), + name(scene_pass->get_name()), + include_albedo(scene_pass->get_include_albedo()) +{ +} + +PassInfo BufferPass::get_info() const +{ + return Pass::get_info(type, include_albedo); +} + +/* -------------------------------------------------------------------- + * Buffer Params. + */ + +NODE_DEFINE(BufferParams) +{ + NodeType *type = NodeType::add("buffer_params", create); + + SOCKET_INT(width, "Width", 0); + SOCKET_INT(height, "Height", 0); + + SOCKET_INT(full_x, "Full X", 0); + SOCKET_INT(full_y, "Full Y", 0); + SOCKET_INT(full_width, "Full Width", 0); + SOCKET_INT(full_height, "Full Height", 0); + + SOCKET_STRING(layer, "Layer", ustring()); + SOCKET_STRING(view, "View", ustring()); + SOCKET_FLOAT(exposure, "Exposure", 1.0f); + SOCKET_BOOLEAN(use_approximate_shadow_catcher, "Use Approximate Shadow Catcher", false); + SOCKET_BOOLEAN(use_transparent_background, "Transparent Background", false); + + /* Notes: + * - Skip passes since they do not follow typical container socket definition. + * Might look into covering those as a socket in the future. + * + * - Skip offset, stride, and pass stride since those can be delivered from the passes and + * rest of the sockets. */ + + return type; +} + +BufferParams::BufferParams() : Node(get_node_type()) +{ + reset_pass_offset(); +} + +void BufferParams::update_passes() +{ + update_offset_stride(); + reset_pass_offset(); + + pass_stride = 0; + for (const BufferPass &pass : passes) { + if (pass.offset != PASS_UNUSED) { + const int index = pass_to_index(pass); + if (pass_offset_[index] == PASS_UNUSED) { + pass_offset_[index] = pass_stride; + } + + pass_stride += pass.get_info().num_components; + } + } +} + +void BufferParams::update_passes(const vector &scene_passes) +{ + passes.clear(); + + pass_stride = 0; + for (const Pass *scene_pass : scene_passes) { + BufferPass buffer_pass(scene_pass); + + if (scene_pass->is_written()) { + buffer_pass.offset = pass_stride; + pass_stride += scene_pass->get_info().num_components; + } + else { + buffer_pass.offset = PASS_UNUSED; + } + + passes.emplace_back(std::move(buffer_pass)); + } + + update_passes(); +} + +void BufferParams::reset_pass_offset() +{ + for (int i = 0; i < kNumPassOffsets; ++i) { + pass_offset_[i] = PASS_UNUSED; + } +} + +int BufferParams::get_pass_offset(PassType pass_type, PassMode mode) const +{ + if (pass_type == PASS_NONE || pass_type == PASS_UNUSED) { + return PASS_UNUSED; + } + + const int index = pass_type_mode_to_index(pass_type, mode); + return pass_offset_[index]; +} + +const BufferPass *BufferParams::find_pass(string_view name) const +{ + for (const BufferPass &pass : passes) { + if (pass.name == name) { + return &pass; + } + } + + return nullptr; +} + +const BufferPass *BufferParams::find_pass(PassType type, PassMode mode) const +{ + for (const BufferPass &pass : passes) { + if (pass.type == type && pass.mode == mode) { + return &pass; + } + } + + return nullptr; +} + +const BufferPass *BufferParams::get_actual_display_pass(PassType type, PassMode mode) const +{ + const BufferPass *pass = find_pass(type, mode); + return get_actual_display_pass(pass); +} + +const BufferPass *BufferParams::get_actual_display_pass(const BufferPass *pass) const +{ + if (!pass) { + return nullptr; + } + + if (pass->type == PASS_COMBINED) { + const BufferPass *shadow_catcher_matte_pass = find_pass(PASS_SHADOW_CATCHER_MATTE, pass->mode); + if (shadow_catcher_matte_pass) { + pass = shadow_catcher_matte_pass; + } + } + + return pass; +} + +void BufferParams::update_offset_stride() { offset = -(full_x + full_y * width); stride = width; } -bool BufferParams::modified(const BufferParams ¶ms) +bool BufferParams::modified(const BufferParams &other) const { - return !(full_x == params.full_x && full_y == params.full_y && width == params.width && - height == params.height && full_width == params.full_width && - full_height == params.full_height && Pass::equals(passes, params.passes) && - denoising_data_pass == params.denoising_data_pass && - denoising_clean_pass == params.denoising_clean_pass && - denoising_prefiltered_pass == params.denoising_prefiltered_pass); -} - -int BufferParams::get_passes_size() -{ - int size = 0; - - for (size_t i = 0; i < passes.size(); i++) - size += passes[i].components; - - if (denoising_data_pass) { - size += DENOISING_PASS_SIZE_BASE; - if (denoising_clean_pass) - size += DENOISING_PASS_SIZE_CLEAN; - if (denoising_prefiltered_pass) - size += DENOISING_PASS_SIZE_PREFILTERED; + if (!(width == other.width && height == other.height && full_x == other.full_x && + full_y == other.full_y && full_width == other.full_width && + full_height == other.full_height && offset == other.offset && stride == other.stride && + pass_stride == other.pass_stride && layer == other.layer && view == other.view && + exposure == other.exposure && + use_approximate_shadow_catcher == other.use_approximate_shadow_catcher && + use_transparent_background == other.use_transparent_background)) { + return true; } - return align_up(size, 4); + return !(passes == other.passes); } -int BufferParams::get_denoising_offset() -{ - int offset = 0; +/* -------------------------------------------------------------------- + * Render Buffers. + */ - for (size_t i = 0; i < passes.size(); i++) - offset += passes[i].components; - - return offset; -} - -int BufferParams::get_denoising_prefiltered_offset() -{ - assert(denoising_prefiltered_pass); - - int offset = get_denoising_offset(); - - offset += DENOISING_PASS_SIZE_BASE; - if (denoising_clean_pass) { - offset += DENOISING_PASS_SIZE_CLEAN; - } - - return offset; -} - -/* Render Buffer Task */ - -RenderTile::RenderTile() -{ - x = 0; - y = 0; - w = 0; - h = 0; - - sample = 0; - start_sample = 0; - num_samples = 0; - resolution = 0; - - offset = 0; - stride = 0; - - buffer = 0; - - buffers = NULL; - stealing_state = NO_STEALING; -} - -/* Render Buffers */ - -RenderBuffers::RenderBuffers(Device *device) - : buffer(device, "RenderBuffers", MEM_READ_WRITE), - map_neighbor_copied(false), - render_time(0.0f) +RenderBuffers::RenderBuffers(Device *device) : buffer(device, "RenderBuffers", MEM_READ_WRITE) { } @@ -142,13 +259,14 @@ RenderBuffers::~RenderBuffers() buffer.free(); } -void RenderBuffers::reset(BufferParams ¶ms_) +void RenderBuffers::reset(const BufferParams ¶ms_) { + DCHECK(params_.pass_stride != -1); + params = params_; /* re-allocate buffer */ - buffer.alloc(params.width * params.get_passes_size(), params.height); - buffer.zero_to_device(); + buffer.alloc(params.width * params.pass_stride, params.height); } void RenderBuffers::zero() @@ -158,407 +276,86 @@ void RenderBuffers::zero() bool RenderBuffers::copy_from_device() { + DCHECK(params.pass_stride != -1); + if (!buffer.device_pointer) return false; - buffer.copy_from_device(0, params.width * params.get_passes_size(), params.height); + buffer.copy_from_device(0, params.width * params.pass_stride, params.height); return true; } -bool RenderBuffers::get_denoising_pass_rect( - int type, float exposure, int sample, int components, float *pixels) +void RenderBuffers::copy_to_device() { - if (buffer.data() == NULL) { - return false; - } - - float scale = 1.0f; - float alpha_scale = 1.0f / sample; - if (type == DENOISING_PASS_PREFILTERED_COLOR || type == DENOISING_PASS_CLEAN || - type == DENOISING_PASS_PREFILTERED_INTENSITY) { - scale *= exposure; - } - else if (type == DENOISING_PASS_PREFILTERED_VARIANCE) { - scale *= exposure * exposure * (sample - 1); - } - - int offset; - if (type == DENOISING_PASS_CLEAN) { - /* The clean pass isn't changed by prefiltering, so we use the original one there. */ - offset = type + params.get_denoising_offset(); - scale /= sample; - } - else if (params.denoising_prefiltered_pass) { - offset = type + params.get_denoising_prefiltered_offset(); - } - else { - switch (type) { - case DENOISING_PASS_PREFILTERED_DEPTH: - offset = params.get_denoising_offset() + DENOISING_PASS_DEPTH; - break; - case DENOISING_PASS_PREFILTERED_NORMAL: - offset = params.get_denoising_offset() + DENOISING_PASS_NORMAL; - break; - case DENOISING_PASS_PREFILTERED_ALBEDO: - offset = params.get_denoising_offset() + DENOISING_PASS_ALBEDO; - break; - case DENOISING_PASS_PREFILTERED_COLOR: - /* If we're not saving the prefiltering result, return the original noisy pass. */ - offset = params.get_denoising_offset() + DENOISING_PASS_COLOR; - break; - default: - return false; - } - scale /= sample; - } - - int pass_stride = params.get_passes_size(); - int size = params.width * params.height; - - float *in = buffer.data() + offset; - - if (components == 1) { - for (int i = 0; i < size; i++, in += pass_stride, pixels++) { - pixels[0] = in[0] * scale; - } - } - else if (components == 3) { - for (int i = 0; i < size; i++, in += pass_stride, pixels += 3) { - pixels[0] = in[0] * scale; - pixels[1] = in[1] * scale; - pixels[2] = in[2] * scale; - } - } - else if (components == 4) { - /* Since the alpha channel is not involved in denoising, output the Combined alpha channel. */ - assert(params.passes[0].type == PASS_COMBINED); - float *in_combined = buffer.data(); - - for (int i = 0; i < size; i++, in += pass_stride, in_combined += pass_stride, pixels += 4) { - float3 val = make_float3(in[0], in[1], in[2]); - if (type == DENOISING_PASS_PREFILTERED_COLOR && params.denoising_prefiltered_pass) { - /* Remove highlight compression from the image. */ - val = color_highlight_uncompress(val); - } - pixels[0] = val.x * scale; - pixels[1] = val.y * scale; - pixels[2] = val.z * scale; - pixels[3] = saturate(in_combined[3] * alpha_scale); - } - } - else { - return false; - } - - return true; + buffer.copy_to_device(); } -bool RenderBuffers::get_pass_rect( - const string &name, float exposure, int sample, int components, float *pixels) +void render_buffers_host_copy_denoised(RenderBuffers *dst, + const BufferParams &dst_params, + const RenderBuffers *src, + const BufferParams &src_params, + const size_t src_offset) { - if (buffer.data() == NULL) { - return false; - } + DCHECK_EQ(dst_params.width, src_params.width); + /* TODO(sergey): More sanity checks to avoid buffer overrun. */ - float *sample_count = NULL; - if (name == "Combined") { - int sample_offset = 0; - for (size_t j = 0; j < params.passes.size(); j++) { - Pass &pass = params.passes[j]; - if (pass.type != PASS_SAMPLE_COUNT) { - sample_offset += pass.components; - continue; - } - else { - sample_count = buffer.data() + sample_offset; - break; - } - } - } + /* Create a map of pass ofsets to be copied. + * Assume offsets are different to allow copying passes between buffers with different set of + * passes. */ - int pass_offset = 0; + struct { + int dst_offset; + int src_offset; + } pass_offsets[PASS_NUM]; - for (size_t j = 0; j < params.passes.size(); j++) { - Pass &pass = params.passes[j]; + int num_passes = 0; - /* Pass is identified by both type and name, multiple of the same type - * may exist with a different name. */ - if (pass.name != name) { - pass_offset += pass.components; + for (int i = 0; i < PASS_NUM; ++i) { + const PassType pass_type = static_cast(i); + + const int dst_pass_offset = dst_params.get_pass_offset(pass_type, PassMode::DENOISED); + if (dst_pass_offset == PASS_UNUSED) { continue; } - PassType type = pass.type; - - float *in = buffer.data() + pass_offset; - int pass_stride = params.get_passes_size(); - - float scale = (pass.filter) ? 1.0f / (float)sample : 1.0f; - float scale_exposure = (pass.exposure) ? scale * exposure : scale; - - int size = params.width * params.height; - - if (components == 1 && type == PASS_RENDER_TIME) { - /* Render time is not stored by kernel, but measured per tile. */ - float val = (float)(1000.0 * render_time / (params.width * params.height * sample)); - for (int i = 0; i < size; i++, pixels++) { - pixels[0] = val; - } - } - else if (components == 1) { - assert(pass.components == components); - - /* Scalar */ - if (type == PASS_DEPTH) { - for (int i = 0; i < size; i++, in += pass_stride, pixels++) { - float f = *in; - pixels[0] = (f == 0.0f) ? 1e10f : f * scale_exposure; - } - } - else if (type == PASS_MIST) { - for (int i = 0; i < size; i++, in += pass_stride, pixels++) { - float f = *in; - pixels[0] = saturate(f * scale_exposure); - } - } - else { - for (int i = 0; i < size; i++, in += pass_stride, pixels++) { - float f = *in; - pixels[0] = f * scale_exposure; - } - } - } - else if (components == 3) { - assert(pass.components == 4); - - /* RGBA */ - if (type == PASS_SHADOW) { - for (int i = 0; i < size; i++, in += pass_stride, pixels += 3) { - float4 f = make_float4(in[0], in[1], in[2], in[3]); - float invw = (f.w > 0.0f) ? 1.0f / f.w : 1.0f; - - pixels[0] = f.x * invw; - pixels[1] = f.y * invw; - pixels[2] = f.z * invw; - } - } - else if (pass.divide_type != PASS_NONE) { - /* RGB lighting passes that need to divide out color */ - pass_offset = 0; - for (size_t k = 0; k < params.passes.size(); k++) { - Pass &color_pass = params.passes[k]; - if (color_pass.type == pass.divide_type) - break; - pass_offset += color_pass.components; - } - - float *in_divide = buffer.data() + pass_offset; - - for (int i = 0; i < size; i++, in += pass_stride, in_divide += pass_stride, pixels += 3) { - float3 f = make_float3(in[0], in[1], in[2]); - float3 f_divide = make_float3(in_divide[0], in_divide[1], in_divide[2]); - - f = safe_divide_even_color(f * exposure, f_divide); - - pixels[0] = f.x; - pixels[1] = f.y; - pixels[2] = f.z; - } - } - else { - /* RGB/vector */ - for (int i = 0; i < size; i++, in += pass_stride, pixels += 3) { - float3 f = make_float3(in[0], in[1], in[2]); - - pixels[0] = f.x * scale_exposure; - pixels[1] = f.y * scale_exposure; - pixels[2] = f.z * scale_exposure; - } - } - } - else if (components == 4) { - assert(pass.components == components); - - /* RGBA */ - if (type == PASS_SHADOW) { - for (int i = 0; i < size; i++, in += pass_stride, pixels += 4) { - float4 f = make_float4(in[0], in[1], in[2], in[3]); - float invw = (f.w > 0.0f) ? 1.0f / f.w : 1.0f; - - pixels[0] = f.x * invw; - pixels[1] = f.y * invw; - pixels[2] = f.z * invw; - pixels[3] = 1.0f; - } - } - else if (type == PASS_MOTION) { - /* need to normalize by number of samples accumulated for motion */ - pass_offset = 0; - for (size_t k = 0; k < params.passes.size(); k++) { - Pass &color_pass = params.passes[k]; - if (color_pass.type == PASS_MOTION_WEIGHT) - break; - pass_offset += color_pass.components; - } - - float *in_weight = buffer.data() + pass_offset; - - for (int i = 0; i < size; i++, in += pass_stride, in_weight += pass_stride, pixels += 4) { - float4 f = make_float4(in[0], in[1], in[2], in[3]); - float w = in_weight[0]; - float invw = (w > 0.0f) ? 1.0f / w : 0.0f; - - pixels[0] = f.x * invw; - pixels[1] = f.y * invw; - pixels[2] = f.z * invw; - pixels[3] = f.w * invw; - } - } - else if (type == PASS_CRYPTOMATTE) { - for (int i = 0; i < size; i++, in += pass_stride, pixels += 4) { - float4 f = make_float4(in[0], in[1], in[2], in[3]); - /* x and z contain integer IDs, don't rescale them. - y and w contain matte weights, they get scaled. */ - pixels[0] = f.x; - pixels[1] = f.y * scale; - pixels[2] = f.z; - pixels[3] = f.w * scale; - } - } - else { - for (int i = 0; i < size; i++, in += pass_stride, pixels += 4) { - if (sample_count && sample_count[i * pass_stride] < 0.0f) { - scale = (pass.filter) ? -1.0f / (sample_count[i * pass_stride]) : 1.0f; - scale_exposure = (pass.exposure) ? scale * exposure : scale; - } - - float4 f = make_float4(in[0], in[1], in[2], in[3]); - - pixels[0] = f.x * scale_exposure; - pixels[1] = f.y * scale_exposure; - pixels[2] = f.z * scale_exposure; - - /* Clamp since alpha might be > 1.0 due to Russian roulette. */ - pixels[3] = saturate(f.w * scale); - } - } - } - - return true; - } - - return false; -} - -bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels, int samples) -{ - if (buffer.data() == NULL) { - return false; - } - - int pass_offset = 0; - - for (size_t j = 0; j < params.passes.size(); j++) { - Pass &pass = params.passes[j]; - - if (pass.type != type) { - pass_offset += pass.components; + const int src_pass_offset = src_params.get_pass_offset(pass_type, PassMode::DENOISED); + if (src_pass_offset == PASS_UNUSED) { continue; } - float *out = buffer.data() + pass_offset; - int pass_stride = params.get_passes_size(); - int size = params.width * params.height; + pass_offsets[num_passes].dst_offset = dst_pass_offset; + pass_offsets[num_passes].src_offset = src_pass_offset; + ++num_passes; + } - assert(pass.components == components); + /* Copy passes. */ + /* TODO(sergey): Make it more reusable, allowing implement copy of noisy passes. */ - for (int i = 0; i < size; i++, out += pass_stride, pixels += components) { - if (pass.filter) { - /* Scale by the number of samples, inverse of what we do in get_pass_rect. - * A better solution would be to remove the need for set_pass_rect entirely, - * and change baking to bake multiple objects in a tile at once. */ - for (int j = 0; j < components; j++) { - out[j] = pixels[j] * samples; - } - } - else { - /* For non-filtered passes just straight copy, these may contain non-float data. */ - memcpy(out, pixels, sizeof(float) * components); - } + const int64_t dst_width = dst_params.width; + const int64_t dst_height = dst_params.height; + const int64_t dst_pass_stride = dst_params.pass_stride; + const int64_t dst_num_pixels = dst_width * dst_height; + + const int64_t src_pass_stride = src_params.pass_stride; + const int64_t src_offset_in_floats = src_offset * src_pass_stride; + + const float *src_pixel = src->buffer.data() + src_offset_in_floats; + float *dst_pixel = dst->buffer.data(); + + for (int i = 0; i < dst_num_pixels; + ++i, src_pixel += src_pass_stride, dst_pixel += dst_pass_stride) { + for (int pass_offset_idx = 0; pass_offset_idx < num_passes; ++pass_offset_idx) { + const int dst_pass_offset = pass_offsets[pass_offset_idx].dst_offset; + const int src_pass_offset = pass_offsets[pass_offset_idx].src_offset; + + /* TODO(sergey): Support non-RGBA passes. */ + dst_pixel[dst_pass_offset + 0] = src_pixel[src_pass_offset + 0]; + dst_pixel[dst_pass_offset + 1] = src_pixel[src_pass_offset + 1]; + dst_pixel[dst_pass_offset + 2] = src_pixel[src_pass_offset + 2]; + dst_pixel[dst_pass_offset + 3] = src_pixel[src_pass_offset + 3]; } - - return true; } - - return false; -} - -/* Display Buffer */ - -DisplayBuffer::DisplayBuffer(Device *device, bool linear) - : draw_width(0), - draw_height(0), - transparent(true), /* todo: determine from background */ - half_float(linear), - rgba_byte(device, "display buffer byte"), - rgba_half(device, "display buffer half") -{ -} - -DisplayBuffer::~DisplayBuffer() -{ - rgba_byte.free(); - rgba_half.free(); -} - -void DisplayBuffer::reset(BufferParams ¶ms_) -{ - draw_width = 0; - draw_height = 0; - - params = params_; - - /* allocate display pixels */ - if (half_float) { - rgba_half.alloc_to_device(params.width, params.height); - } - else { - rgba_byte.alloc_to_device(params.width, params.height); - } -} - -void DisplayBuffer::draw_set(int width, int height) -{ - assert(width <= params.width && height <= params.height); - - draw_width = width; - draw_height = height; -} - -void DisplayBuffer::draw(Device *device, const DeviceDrawParams &draw_params) -{ - if (draw_width != 0 && draw_height != 0) { - device_memory &rgba = (half_float) ? (device_memory &)rgba_half : (device_memory &)rgba_byte; - - device->draw_pixels(rgba, - 0, - draw_width, - draw_height, - params.width, - params.height, - params.full_x, - params.full_y, - params.full_width, - params.full_height, - transparent, - draw_params); - } -} - -bool DisplayBuffer::draw_ready() -{ - return (draw_width != 0 && draw_height != 0); } CCL_NAMESPACE_END diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h index 4ffc628bb52..c048234167d 100644 --- a/intern/cycles/render/buffers.h +++ b/intern/cycles/render/buffers.h @@ -18,8 +18,8 @@ #define __BUFFERS_H__ #include "device/device_memory.h" - -#include "render/film.h" +#include "graph/node.h" +#include "render/pass.h" #include "kernel/kernel_types.h" @@ -34,39 +34,119 @@ class Device; struct DeviceDrawParams; struct float4; +/* NOTE: Is not a real scene node. Using Node API for ease of (de)serialization. */ +class BufferPass : public Node { + public: + NODE_DECLARE + + PassType type = PASS_NONE; + PassMode mode = PassMode::NOISY; + ustring name; + bool include_albedo = false; + + int offset = -1; + + BufferPass(); + explicit BufferPass(const Pass *scene_pass); + + BufferPass(BufferPass &&other) noexcept = default; + BufferPass(const BufferPass &other) = default; + + BufferPass &operator=(BufferPass &&other) = default; + BufferPass &operator=(const BufferPass &other) = default; + + ~BufferPass() = default; + + PassInfo get_info() const; + + inline bool operator==(const BufferPass &other) const + { + return type == other.type && mode == other.mode && name == other.name && + include_albedo == other.include_albedo && offset == other.offset; + } + inline bool operator!=(const BufferPass &other) const + { + return !(*this == other); + } +}; + /* Buffer Parameters * Size of render buffer and how it fits in the full image (border render). */ -class BufferParams { +/* NOTE: Is not a real scene node. Using Node API for ease of (de)serialization. */ +class BufferParams : public Node { public: - /* width/height of the physical buffer */ - int width; - int height; + NODE_DECLARE - /* offset into and width/height of the full buffer */ - int full_x; - int full_y; - int full_width; - int full_height; + /* Width/height of the physical buffer. */ + int width = 0; + int height = 0; - /* passes */ - vector passes; - bool denoising_data_pass; - /* If only some light path types should be target, an additional pass is needed. */ - bool denoising_clean_pass; - /* When we're prefiltering the passes during rendering, we need to keep both the - * original and the prefiltered data around because neighboring tiles might still - * need the original data. */ - bool denoising_prefiltered_pass; + /* Offset into and width/height of the full buffer. */ + int full_x = 0; + int full_y = 0; + int full_width = 0; + int full_height = 0; + + /* Runtime fields, only valid after `update_passes()` or `update_offset_stride()`. */ + int offset = -1, stride = -1; + + /* Runtime fields, only valid after `update_passes()`. */ + int pass_stride = -1; + + /* Properties which are used for accessing buffer pixels outside of scene graph. */ + vector passes; + ustring layer; + ustring view; + float exposure = 1.0f; + bool use_approximate_shadow_catcher = false; + bool use_transparent_background = false; - /* functions */ BufferParams(); - void get_offset_stride(int &offset, int &stride); - bool modified(const BufferParams ¶ms); - int get_passes_size(); - int get_denoising_offset(); - int get_denoising_prefiltered_offset(); + BufferParams(BufferParams &&other) noexcept = default; + BufferParams(const BufferParams &other) = default; + + BufferParams &operator=(BufferParams &&other) = default; + BufferParams &operator=(const BufferParams &other) = default; + + ~BufferParams() = default; + + /* Pre-calculate all fields which depends on the passes. + * + * When the scene passes are given, the buffer passes will be created from them and stored in + * this params, and then params are updated for those passes. + * The `update_passes()` without parameters updates offsets and stries which are stored outside + * of the passes. */ + void update_passes(); + void update_passes(const vector &scene_passes); + + /* Returns PASS_UNUSED if there is no such pass in the buffer. */ + int get_pass_offset(PassType type, PassMode mode = PassMode::NOISY) const; + + /* Returns nullptr if pass with given name does not exist. */ + const BufferPass *find_pass(string_view name) const; + const BufferPass *find_pass(PassType type, PassMode mode = PassMode::NOISY) const; + + /* Get display pass from its name. + * Will do special logic to replace combined pass with shadow catcher matte. */ + const BufferPass *get_actual_display_pass(PassType type, PassMode mode = PassMode::NOISY) const; + const BufferPass *get_actual_display_pass(const BufferPass *pass) const; + + void update_offset_stride(); + + bool modified(const BufferParams &other) const; + + protected: + void reset_pass_offset(); + + /* Multipled by 2 to be able to store noisy and denoised pass types. */ + static constexpr int kNumPassOffsets = PASS_NUM * 2; + + /* Indexed by an index derived from pass type and mode, indicates offset of the corresponding + * pass in the buffer. + * If there are multiple passes with same type and mode contains lowest offset of all of them. */ + int pass_offset_[kNumPassOffsets]; }; /* Render Buffers */ @@ -78,125 +158,31 @@ class RenderBuffers { /* float buffer */ device_vector buffer; - bool map_neighbor_copied; - double render_time; explicit RenderBuffers(Device *device); ~RenderBuffers(); - void reset(BufferParams ¶ms); + void reset(const BufferParams ¶ms); void zero(); bool copy_from_device(); - bool get_pass_rect( - const string &name, float exposure, int sample, int components, float *pixels); - bool get_denoising_pass_rect( - int offset, float exposure, int sample, int components, float *pixels); - bool set_pass_rect(PassType type, int components, float *pixels, int samples); + void copy_to_device(); }; -/* Display Buffer +/* Copy denoised passes form source to destination. * - * The buffer used for drawing during render, filled by converting the render - * buffers to byte of half float storage */ - -class DisplayBuffer { - public: - /* buffer parameters */ - BufferParams params; - /* dimensions for how much of the buffer is actually ready for display. - * with progressive render we can be using only a subset of the buffer. - * if these are zero, it means nothing can be drawn yet */ - int draw_width, draw_height; - /* draw alpha channel? */ - bool transparent; - /* use half float? */ - bool half_float; - /* byte buffer for converted result */ - device_pixels rgba_byte; - device_pixels rgba_half; - - DisplayBuffer(Device *device, bool linear = false); - ~DisplayBuffer(); - - void reset(BufferParams ¶ms); - - void draw_set(int width, int height); - void draw(Device *device, const DeviceDrawParams &draw_params); - bool draw_ready(); -}; - -/* Render Tile - * Rendering task on a buffer */ - -class RenderTile { - public: - typedef enum { PATH_TRACE = (1 << 0), BAKE = (1 << 1), DENOISE = (1 << 2) } Task; - - Task task; - int x, y, w, h; - int start_sample; - int num_samples; - int sample; - int resolution; - int offset; - int stride; - int tile_index; - - device_ptr buffer; - int device_size; - - typedef enum { NO_STEALING = 0, CAN_BE_STOLEN = 1, WAS_STOLEN = 2 } StealingState; - StealingState stealing_state; - - RenderBuffers *buffers; - - RenderTile(); - - int4 bounds() const - { - return make_int4(x, /* xmin */ - y, /* ymin */ - x + w, /* xmax */ - y + h); /* ymax */ - } -}; - -/* Render Tile Neighbors - * Set of neighboring tiles used for denoising. Tile order: - * 0 1 2 - * 3 4 5 - * 6 7 8 */ - -class RenderTileNeighbors { - public: - static const int SIZE = 9; - static const int CENTER = 4; - - RenderTile tiles[SIZE]; - RenderTile target; - - RenderTileNeighbors(const RenderTile ¢er) - { - tiles[CENTER] = center; - } - - int4 bounds() const - { - return make_int4(tiles[3].x, /* xmin */ - tiles[1].y, /* ymin */ - tiles[5].x + tiles[5].w, /* xmax */ - tiles[7].y + tiles[7].h); /* ymax */ - } - - void set_bounds_from_center() - { - tiles[3].x = tiles[CENTER].x; - tiles[1].y = tiles[CENTER].y; - tiles[5].x = tiles[CENTER].x + tiles[CENTER].w; - tiles[7].y = tiles[CENTER].y + tiles[CENTER].h; - } -}; + * Buffer parameters are provided explicitly, allowing to copy pixelks between render buffers which + * content corresponds to a render result at a non-unit resolution divider. + * + * `src_offset` allows to offset source pixel index which is used when a fraction of the source + * buffer is to be copied. + * + * Copy happens of the number of pixels in the destination. */ +void render_buffers_host_copy_denoised(RenderBuffers *dst, + const BufferParams &dst_params, + const RenderBuffers *src, + const BufferParams &src_params, + const size_t src_offset = 0); CCL_NAMESPACE_END diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp index 327f166f9d8..8b69c971991 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/render/camera.cpp @@ -33,9 +33,9 @@ /* needed for calculating differentials */ // clang-format off -#include "kernel/kernel_compat_cpu.h" -#include "kernel/split/kernel_split_data.h" -#include "kernel/kernel_globals.h" +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + #include "kernel/kernel_projection.h" #include "kernel/kernel_differential.h" #include "kernel/kernel_montecarlo.h" @@ -169,7 +169,6 @@ Camera::Camera() : Node(get_node_type()) width = 1024; height = 512; - resolution = 1; use_perspective_motion = false; @@ -455,7 +454,6 @@ void Camera::update(Scene *scene) /* render size */ kcam->width = width; kcam->height = height; - kcam->resolution = resolution; /* store differentials */ kcam->dx = float3_to_float4(dx); @@ -776,9 +774,11 @@ float Camera::world_to_raster_size(float3 P) &ray); #endif - differential_transfer(&ray.dP, ray.dP, ray.D, ray.dD, ray.D, dist); + /* TODO: would it help to use more accurate differentials here? */ + differential3 dP; + differential_transfer_compact(&dP, ray.dP, ray.D, ray.dD, ray.D, dist); - return max(len(ray.dP.dx), len(ray.dP.dy)); + return max(len(dP.dx), len(dP.dy)); } return res; @@ -789,12 +789,11 @@ bool Camera::use_motion() const return motion.size() > 1; } -void Camera::set_screen_size_and_resolution(int width_, int height_, int resolution_) +void Camera::set_screen_size(int width_, int height_) { - if (width_ != width || height_ != height || resolution_ != resolution) { + if (width_ != width || height_ != height) { width = width_; height = height_; - resolution = resolution_; tag_modified(); } } diff --git a/intern/cycles/render/camera.h b/intern/cycles/render/camera.h index 5abb4750764..cb8ecac1a7e 100644 --- a/intern/cycles/render/camera.h +++ b/intern/cycles/render/camera.h @@ -199,7 +199,6 @@ class Camera : public Node { private: int width; int height; - int resolution; public: /* functions */ @@ -225,7 +224,7 @@ class Camera : public Node { int motion_step(float time) const; bool use_motion() const; - void set_screen_size_and_resolution(int width_, int height_, int resolution_); + void set_screen_size(int width_, int height_); private: /* Private utility functions. */ diff --git a/intern/cycles/render/coverage.cpp b/intern/cycles/render/coverage.cpp deleted file mode 100644 index 99d4daa6961..00000000000 --- a/intern/cycles/render/coverage.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2018 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "render/coverage.h" -#include "render/buffers.h" - -#include "kernel/kernel_compat_cpu.h" -#include "kernel/kernel_types.h" -#include "kernel/split/kernel_split_data.h" - -#include "kernel/kernel_globals.h" -#include "kernel/kernel_id_passes.h" - -#include "util/util_map.h" - -CCL_NAMESPACE_BEGIN - -static bool crypomatte_comp(const pair &i, const pair j) -{ - return i.first > j.first; -} - -void Coverage::finalize() -{ - int pass_offset = 0; - if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { - finalize_buffer(coverage_object, pass_offset); - pass_offset += kernel_data.film.cryptomatte_depth * 4; - } - if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { - finalize_buffer(coverage_material, pass_offset); - pass_offset += kernel_data.film.cryptomatte_depth * 4; - } - if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { - finalize_buffer(coverage_asset, pass_offset); - } -} - -void Coverage::init_path_trace() -{ - kg->coverage_object = kg->coverage_material = kg->coverage_asset = NULL; - - if (kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE) { - if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { - coverage_object.clear(); - coverage_object.resize(tile.w * tile.h); - } - if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { - coverage_material.clear(); - coverage_material.resize(tile.w * tile.h); - } - if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { - coverage_asset.clear(); - coverage_asset.resize(tile.w * tile.h); - } - } -} - -void Coverage::init_pixel(int x, int y) -{ - if (kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE) { - const int pixel_index = tile.w * (y - tile.y) + x - tile.x; - if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { - kg->coverage_object = &coverage_object[pixel_index]; - } - if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { - kg->coverage_material = &coverage_material[pixel_index]; - } - if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { - kg->coverage_asset = &coverage_asset[pixel_index]; - } - } -} - -void Coverage::finalize_buffer(vector &coverage, const int pass_offset) -{ - if (kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE) { - flatten_buffer(coverage, pass_offset); - } - else { - sort_buffer(pass_offset); - } -} - -void Coverage::flatten_buffer(vector &coverage, const int pass_offset) -{ - /* Sort the coverage map and write it to the output */ - int pixel_index = 0; - int pass_stride = tile.buffers->params.get_passes_size(); - for (int y = 0; y < tile.h; ++y) { - for (int x = 0; x < tile.w; ++x) { - const CoverageMap &pixel = coverage[pixel_index]; - if (!pixel.empty()) { - /* buffer offset */ - int index = x + y * tile.stride; - float *buffer = (float *)tile.buffer + index * pass_stride; - - /* sort the cryptomatte pixel */ - vector> sorted_pixel; - for (CoverageMap::const_iterator it = pixel.begin(); it != pixel.end(); ++it) { - sorted_pixel.push_back(std::make_pair(it->second, it->first)); - } - sort(sorted_pixel.begin(), sorted_pixel.end(), crypomatte_comp); - int num_slots = 2 * (kernel_data.film.cryptomatte_depth); - if (sorted_pixel.size() > num_slots) { - float leftover = 0.0f; - for (vector>::iterator it = sorted_pixel.begin() + num_slots; - it != sorted_pixel.end(); - ++it) { - leftover += it->first; - } - sorted_pixel[num_slots - 1].first += leftover; - } - int limit = min(num_slots, sorted_pixel.size()); - for (int i = 0; i < limit; ++i) { - kernel_write_id_slots(buffer + kernel_data.film.pass_cryptomatte + pass_offset, - 2 * (kernel_data.film.cryptomatte_depth), - sorted_pixel[i].second, - sorted_pixel[i].first); - } - } - ++pixel_index; - } - } -} - -void Coverage::sort_buffer(const int pass_offset) -{ - /* Sort the coverage map and write it to the output */ - int pass_stride = tile.buffers->params.get_passes_size(); - for (int y = 0; y < tile.h; ++y) { - for (int x = 0; x < tile.w; ++x) { - /* buffer offset */ - int index = x + y * tile.stride; - float *buffer = (float *)tile.buffer + index * pass_stride; - kernel_sort_id_slots(buffer + kernel_data.film.pass_cryptomatte + pass_offset, - 2 * (kernel_data.film.cryptomatte_depth)); - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/render/coverage.h b/intern/cycles/render/coverage.h deleted file mode 100644 index 12182c614da..00000000000 --- a/intern/cycles/render/coverage.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __COVERAGE_H__ -#define __COVERAGE_H__ - -#include "util/util_map.h" -#include "util/util_vector.h" - -CCL_NAMESPACE_BEGIN - -struct KernelGlobals; -class RenderTile; - -typedef unordered_map CoverageMap; - -class Coverage { - public: - Coverage(KernelGlobals *kg_, RenderTile &tile_) : kg(kg_), tile(tile_) - { - } - void init_path_trace(); - void init_pixel(int x, int y); - void finalize(); - - private: - vector coverage_object; - vector coverage_material; - vector coverage_asset; - KernelGlobals *kg; - RenderTile &tile; - void finalize_buffer(vector &coverage, const int pass_offset); - void flatten_buffer(vector &coverage, const int pass_offset); - void sort_buffer(const int pass_offset); -}; - -CCL_NAMESPACE_END - -#endif /* __COVERAGE_H__ */ diff --git a/intern/cycles/render/denoising.cpp b/intern/cycles/render/denoising.cpp index ddbe7484800..bcf8d3fa204 100644 --- a/intern/cycles/render/denoising.cpp +++ b/intern/cycles/render/denoising.cpp @@ -16,15 +16,17 @@ #include "render/denoising.h" -#include "kernel/filter/filter_defines.h" +#if 0 -#include "util/util_foreach.h" -#include "util/util_map.h" -#include "util/util_system.h" -#include "util/util_task.h" -#include "util/util_time.h" +# include "kernel/filter/filter_defines.h" -#include +# include "util/util_foreach.h" +# include "util/util_map.h" +# include "util/util_system.h" +# include "util/util_task.h" +# include "util/util_time.h" + +# include CCL_NAMESPACE_BEGIN @@ -225,7 +227,7 @@ bool DenoiseImageLayer::match_channels(int neighbor, /* Denoise Task */ DenoiseTask::DenoiseTask(Device *device, - Denoiser *denoiser, + DenoiserPipeline *denoiser, int frame, const vector &neighbor_frames) : denoiser(denoiser), @@ -386,7 +388,6 @@ void DenoiseTask::create_task(DeviceTask &task) task.denoising = denoiser->params; task.denoising.type = DENOISER_NLM; task.denoising.use = true; - task.denoising.store_passes = false; task.denoising_from_render = false; task.denoising_frames.resize(neighbor_frames.size()); @@ -863,7 +864,7 @@ bool DenoiseImage::save_output(const string &out_filepath, string &error) /* File pattern handling and outer loop over frames */ -Denoiser::Denoiser(DeviceInfo &device_info) +DenoiserPipeline::DenoiserPipeline(DeviceInfo &device_info) { samples_override = 0; tile_size = make_int2(64, 64); @@ -876,18 +877,16 @@ Denoiser::Denoiser(DeviceInfo &device_info) /* Initialize device. */ device = Device::create(device_info, stats, profiler, true); - DeviceRequestedFeatures req; - req.use_denoising = true; - device->load_kernels(req); + device->load_kernels(KERNEL_FEATURE_DENOISING); } -Denoiser::~Denoiser() +DenoiserPipeline::~DenoiserPipeline() { delete device; TaskScheduler::exit(); } -bool Denoiser::run() +bool DenoiserPipeline::run() { assert(input.size() == output.size()); @@ -931,3 +930,5 @@ bool Denoiser::run() } CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/render/denoising.h b/intern/cycles/render/denoising.h index c1b4d0a5596..097cc570d06 100644 --- a/intern/cycles/render/denoising.h +++ b/intern/cycles/render/denoising.h @@ -17,27 +17,31 @@ #ifndef __DENOISING_H__ #define __DENOISING_H__ -#include "device/device.h" -#include "device/device_denoising.h" +#if 0 -#include "render/buffers.h" +/* TODO(sergey): Make it explicit and clear when something is a denoiser, its pipeline or + * parameters. Currently it is an annoying mixture of terms used interchangeably. */ -#include "util/util_string.h" -#include "util/util_unique_ptr.h" -#include "util/util_vector.h" +# include "device/device.h" -#include +# include "render/buffers.h" + +# include "util/util_string.h" +# include "util/util_unique_ptr.h" +# include "util/util_vector.h" + +# include OIIO_NAMESPACE_USING CCL_NAMESPACE_BEGIN -/* Denoiser */ +/* Denoiser pipeline */ -class Denoiser { +class DenoiserPipeline { public: - Denoiser(DeviceInfo &device_info); - ~Denoiser(); + DenoiserPipeline(DeviceInfo &device_info); + ~DenoiserPipeline(); bool run(); @@ -155,7 +159,10 @@ class DenoiseImage { class DenoiseTask { public: - DenoiseTask(Device *device, Denoiser *denoiser, int frame, const vector &neighbor_frames); + DenoiseTask(Device *device, + DenoiserPipeline *denoiser, + int frame, + const vector &neighbor_frames); ~DenoiseTask(); /* Task stages */ @@ -168,7 +175,7 @@ class DenoiseTask { protected: /* Denoiser parameters and device */ - Denoiser *denoiser; + DenoiserPipeline *denoiser; Device *device; /* Frame number to be denoised */ @@ -204,4 +211,6 @@ class DenoiseTask { CCL_NAMESPACE_END +#endif + #endif /* __DENOISING_H__ */ diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 5df396394c4..8e14b338bd3 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -16,9 +16,12 @@ #include "render/film.h" #include "device/device.h" +#include "render/background.h" +#include "render/bake.h" #include "render/camera.h" #include "render/integrator.h" #include "render/mesh.h" +#include "render/object.h" #include "render/scene.h" #include "render/stats.h" #include "render/tables.h" @@ -31,261 +34,6 @@ CCL_NAMESPACE_BEGIN -/* Pass */ - -static bool compare_pass_order(const Pass &a, const Pass &b) -{ - if (a.components == b.components) - return (a.type < b.type); - return (a.components > b.components); -} - -static NodeEnum *get_pass_type_enum() -{ - static NodeEnum pass_type_enum; - pass_type_enum.insert("combined", PASS_COMBINED); - pass_type_enum.insert("depth", PASS_DEPTH); - pass_type_enum.insert("normal", PASS_NORMAL); - pass_type_enum.insert("uv", PASS_UV); - pass_type_enum.insert("object_id", PASS_OBJECT_ID); - pass_type_enum.insert("material_id", PASS_MATERIAL_ID); - pass_type_enum.insert("motion", PASS_MOTION); - pass_type_enum.insert("motion_weight", PASS_MOTION_WEIGHT); - pass_type_enum.insert("render_time", PASS_RENDER_TIME); - pass_type_enum.insert("cryptomatte", PASS_CRYPTOMATTE); - pass_type_enum.insert("aov_color", PASS_AOV_COLOR); - pass_type_enum.insert("aov_value", PASS_AOV_VALUE); - pass_type_enum.insert("adaptive_aux_buffer", PASS_ADAPTIVE_AUX_BUFFER); - pass_type_enum.insert("sample_count", PASS_SAMPLE_COUNT); - pass_type_enum.insert("mist", PASS_MIST); - pass_type_enum.insert("emission", PASS_EMISSION); - pass_type_enum.insert("background", PASS_BACKGROUND); - pass_type_enum.insert("ambient_occlusion", PASS_AO); - pass_type_enum.insert("shadow", PASS_SHADOW); - pass_type_enum.insert("diffuse_direct", PASS_DIFFUSE_DIRECT); - pass_type_enum.insert("diffuse_indirect", PASS_DIFFUSE_INDIRECT); - pass_type_enum.insert("diffuse_color", PASS_DIFFUSE_COLOR); - pass_type_enum.insert("glossy_direct", PASS_GLOSSY_DIRECT); - pass_type_enum.insert("glossy_indirect", PASS_GLOSSY_INDIRECT); - pass_type_enum.insert("glossy_color", PASS_GLOSSY_COLOR); - pass_type_enum.insert("transmission_direct", PASS_TRANSMISSION_DIRECT); - pass_type_enum.insert("transmission_indirect", PASS_TRANSMISSION_INDIRECT); - pass_type_enum.insert("transmission_color", PASS_TRANSMISSION_COLOR); - pass_type_enum.insert("volume_direct", PASS_VOLUME_DIRECT); - pass_type_enum.insert("volume_indirect", PASS_VOLUME_INDIRECT); - pass_type_enum.insert("bake_primitive", PASS_BAKE_PRIMITIVE); - pass_type_enum.insert("bake_differential", PASS_BAKE_DIFFERENTIAL); - - return &pass_type_enum; -} - -NODE_DEFINE(Pass) -{ - NodeType *type = NodeType::add("pass", create); - - NodeEnum *pass_type_enum = get_pass_type_enum(); - SOCKET_ENUM(type, "Type", *pass_type_enum, PASS_COMBINED); - SOCKET_STRING(name, "Name", ustring()); - - return type; -} - -Pass::Pass() : Node(get_node_type()) -{ -} - -void Pass::add(PassType type, vector &passes, const char *name) -{ - for (size_t i = 0; i < passes.size(); i++) { - if (passes[i].type != type) { - continue; - } - - /* An empty name is used as a placeholder to signal that any pass of - * that type is fine (because the content always is the same). - * This is important to support divide_type: If the pass that has a - * divide_type is added first, a pass for divide_type with an empty - * name will be added. Then, if a matching pass with a name is later - * requested, the existing placeholder will be renamed to that. - * If the divide_type is explicitly allocated with a name first and - * then again as part of another pass, the second one will just be - * skipped because that type already exists. */ - - /* If no name is specified, any pass of the correct type will match. */ - if (name == NULL) { - return; - } - - /* If we already have a placeholder pass, rename that one. */ - if (passes[i].name.empty()) { - passes[i].name = name; - return; - } - - /* If neither existing nor requested pass have placeholder name, they - * must match. */ - if (name == passes[i].name) { - return; - } - } - - Pass pass; - - pass.type = type; - pass.filter = true; - pass.exposure = false; - pass.divide_type = PASS_NONE; - if (name) { - pass.name = name; - } - - switch (type) { - case PASS_NONE: - pass.components = 0; - break; - case PASS_COMBINED: - pass.components = 4; - pass.exposure = true; - break; - case PASS_DEPTH: - pass.components = 1; - pass.filter = false; - break; - case PASS_MIST: - pass.components = 1; - break; - case PASS_NORMAL: - pass.components = 4; - break; - case PASS_UV: - pass.components = 4; - break; - case PASS_MOTION: - pass.components = 4; - pass.divide_type = PASS_MOTION_WEIGHT; - break; - case PASS_MOTION_WEIGHT: - pass.components = 1; - break; - case PASS_OBJECT_ID: - case PASS_MATERIAL_ID: - pass.components = 1; - pass.filter = false; - break; - - case PASS_EMISSION: - case PASS_BACKGROUND: - pass.components = 4; - pass.exposure = true; - break; - case PASS_AO: - pass.components = 4; - break; - case PASS_SHADOW: - pass.components = 4; - pass.exposure = false; - break; - case PASS_LIGHT: - /* This isn't a real pass, used by baking to see whether - * light data is needed or not. - * - * Set components to 0 so pass sort below happens in a - * determined way. - */ - pass.components = 0; - break; - case PASS_RENDER_TIME: - /* This pass is handled entirely on the host side. */ - pass.components = 0; - break; - - case PASS_DIFFUSE_COLOR: - case PASS_GLOSSY_COLOR: - case PASS_TRANSMISSION_COLOR: - pass.components = 4; - break; - case PASS_DIFFUSE_DIRECT: - case PASS_DIFFUSE_INDIRECT: - pass.components = 4; - pass.exposure = true; - pass.divide_type = PASS_DIFFUSE_COLOR; - break; - case PASS_GLOSSY_DIRECT: - case PASS_GLOSSY_INDIRECT: - pass.components = 4; - pass.exposure = true; - pass.divide_type = PASS_GLOSSY_COLOR; - break; - case PASS_TRANSMISSION_DIRECT: - case PASS_TRANSMISSION_INDIRECT: - pass.components = 4; - pass.exposure = true; - pass.divide_type = PASS_TRANSMISSION_COLOR; - break; - case PASS_VOLUME_DIRECT: - case PASS_VOLUME_INDIRECT: - pass.components = 4; - pass.exposure = true; - break; - case PASS_CRYPTOMATTE: - pass.components = 4; - break; - case PASS_ADAPTIVE_AUX_BUFFER: - pass.components = 4; - break; - case PASS_SAMPLE_COUNT: - pass.components = 1; - pass.exposure = false; - break; - case PASS_AOV_COLOR: - pass.components = 4; - break; - case PASS_AOV_VALUE: - pass.components = 1; - break; - case PASS_BAKE_PRIMITIVE: - case PASS_BAKE_DIFFERENTIAL: - pass.components = 4; - pass.exposure = false; - pass.filter = false; - break; - default: - assert(false); - break; - } - - passes.push_back(pass); - - /* Order from by components, to ensure alignment so passes with size 4 - * come first and then passes with size 1. Note this must use stable sort - * so cryptomatte passes remain in the right order. */ - stable_sort(&passes[0], &passes[0] + passes.size(), compare_pass_order); - - if (pass.divide_type != PASS_NONE) - Pass::add(pass.divide_type, passes); -} - -bool Pass::equals(const vector &A, const vector &B) -{ - if (A.size() != B.size()) - return false; - - for (int i = 0; i < A.size(); i++) - if (A[i].type != B[i].type || A[i].name != B[i].name) - return false; - - return true; -} - -bool Pass::contains(const vector &passes, PassType type) -{ - for (size_t i = 0; i < passes.size(); i++) - if (passes[i].type == type) - return true; - - return false; -} - /* Pixel Filter */ static float filter_func_box(float /*v*/, float /*width*/) @@ -368,17 +116,11 @@ NODE_DEFINE(Film) SOCKET_FLOAT(mist_depth, "Mist Depth", 100.0f); SOCKET_FLOAT(mist_falloff, "Mist Falloff", 1.0f); - SOCKET_BOOLEAN(denoising_data_pass, "Generate Denoising Data Pass", false); - SOCKET_BOOLEAN(denoising_clean_pass, "Generate Denoising Clean Pass", false); - SOCKET_BOOLEAN(denoising_prefiltered_pass, "Generate Denoising Prefiltered Pass", false); - SOCKET_INT(denoising_flags, "Denoising Flags", 0); - SOCKET_BOOLEAN(use_adaptive_sampling, "Use Adaptive Sampling", false); - - SOCKET_BOOLEAN(use_light_visibility, "Use Light Visibility", false); - - NodeEnum *pass_type_enum = get_pass_type_enum(); + const NodeEnum *pass_type_enum = Pass::get_type_enum(); SOCKET_ENUM(display_pass, "Display Pass", *pass_type_enum, PASS_COMBINED); + SOCKET_BOOLEAN(show_active_pixels, "Show Active Pixels", false); + static NodeEnum cryptomatte_passes_enum; cryptomatte_passes_enum.insert("none", CRYPT_NONE); cryptomatte_passes_enum.insert("object", CRYPT_OBJECT); @@ -389,15 +131,13 @@ NODE_DEFINE(Film) SOCKET_INT(cryptomatte_depth, "Cryptomatte Depth", 0); + SOCKET_BOOLEAN(use_approximate_shadow_catcher, "Use Approximate Shadow Catcher", false); + return type; } -Film::Film() : Node(get_node_type()) +Film::Film() : Node(get_node_type()), filter_table_offset_(TABLE_OFFSET_INVALID) { - use_light_visibility = false; - filter_table_offset = TABLE_OFFSET_INVALID; - cryptomatte_passes = CRYPT_NONE; - display_pass = PASS_COMBINED; } Film::~Film() @@ -406,7 +146,8 @@ Film::~Film() void Film::add_default(Scene *scene) { - Pass::add(PASS_COMBINED, scene->passes); + Pass *pass = scene->create_node(); + pass->set_type(PASS_COMBINED); } void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) @@ -426,50 +167,77 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) /* update __data */ kfilm->exposure = exposure; + kfilm->pass_alpha_threshold = pass_alpha_threshold; kfilm->pass_flag = 0; - kfilm->display_pass_stride = -1; - kfilm->display_pass_components = 0; - kfilm->display_divide_pass_stride = -1; - kfilm->use_display_exposure = false; - kfilm->use_display_pass_alpha = (display_pass == PASS_COMBINED); + kfilm->use_approximate_shadow_catcher = get_use_approximate_shadow_catcher(); kfilm->light_pass_flag = 0; kfilm->pass_stride = 0; - kfilm->use_light_pass = use_light_visibility; - kfilm->pass_aov_value_num = 0; - kfilm->pass_aov_color_num = 0; + + /* Mark with PASS_UNUSED to avoid mask test in the kernel. */ + kfilm->pass_background = PASS_UNUSED; + kfilm->pass_emission = PASS_UNUSED; + kfilm->pass_ao = PASS_UNUSED; + kfilm->pass_diffuse_direct = PASS_UNUSED; + kfilm->pass_diffuse_indirect = PASS_UNUSED; + kfilm->pass_glossy_direct = PASS_UNUSED; + kfilm->pass_glossy_indirect = PASS_UNUSED; + kfilm->pass_transmission_direct = PASS_UNUSED; + kfilm->pass_transmission_indirect = PASS_UNUSED; + kfilm->pass_volume_direct = PASS_UNUSED; + kfilm->pass_volume_indirect = PASS_UNUSED; + kfilm->pass_volume_direct = PASS_UNUSED; + kfilm->pass_volume_indirect = PASS_UNUSED; + kfilm->pass_shadow = PASS_UNUSED; + + /* Mark passes as unused so that the kernel knows the pass is inaccessible. */ + kfilm->pass_denoising_normal = PASS_UNUSED; + kfilm->pass_denoising_albedo = PASS_UNUSED; + kfilm->pass_sample_count = PASS_UNUSED; + kfilm->pass_adaptive_aux_buffer = PASS_UNUSED; + kfilm->pass_shadow_catcher = PASS_UNUSED; + kfilm->pass_shadow_catcher_sample_count = PASS_UNUSED; + kfilm->pass_shadow_catcher_matte = PASS_UNUSED; bool have_cryptomatte = false; + bool have_aov_color = false; + bool have_aov_value = false; for (size_t i = 0; i < scene->passes.size(); i++) { - Pass &pass = scene->passes[i]; + const Pass *pass = scene->passes[i]; - if (pass.type == PASS_NONE) { + if (pass->get_type() == PASS_NONE || !pass->is_written()) { + continue; + } + + if (pass->get_mode() == PassMode::DENOISED) { + /* Generally we only storing offsets of the noisy passes. The display pass is an exception + * since it is a read operation and not a write. */ + kfilm->pass_stride += pass->get_info().num_components; continue; } /* Can't do motion pass if no motion vectors are available. */ - if (pass.type == PASS_MOTION || pass.type == PASS_MOTION_WEIGHT) { + if (pass->get_type() == PASS_MOTION || pass->get_type() == PASS_MOTION_WEIGHT) { if (scene->need_motion() != Scene::MOTION_PASS) { - kfilm->pass_stride += pass.components; + kfilm->pass_stride += pass->get_info().num_components; continue; } } - int pass_flag = (1 << (pass.type % 32)); - if (pass.type <= PASS_CATEGORY_MAIN_END) { - kfilm->pass_flag |= pass_flag; - } - else if (pass.type <= PASS_CATEGORY_LIGHT_END) { - kfilm->use_light_pass = 1; + const int pass_flag = (1 << (pass->get_type() % 32)); + if (pass->get_type() <= PASS_CATEGORY_LIGHT_END) { kfilm->light_pass_flag |= pass_flag; } + else if (pass->get_type() <= PASS_CATEGORY_DATA_END) { + kfilm->pass_flag |= pass_flag; + } else { - assert(pass.type <= PASS_CATEGORY_BAKE_END); + assert(pass->get_type() <= PASS_CATEGORY_BAKE_END); } - switch (pass.type) { + switch (pass->get_type()) { case PASS_COMBINED: kfilm->pass_combined = kfilm->pass_stride; break; @@ -479,6 +247,12 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) case PASS_NORMAL: kfilm->pass_normal = kfilm->pass_stride; break; + case PASS_POSITION: + kfilm->pass_position = kfilm->pass_stride; + break; + case PASS_ROUGHNESS: + kfilm->pass_roughness = kfilm->pass_stride; + break; case PASS_UV: kfilm->pass_uv = kfilm->pass_stride; break; @@ -511,9 +285,6 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) kfilm->pass_shadow = kfilm->pass_stride; break; - case PASS_LIGHT: - break; - case PASS_DIFFUSE_COLOR: kfilm->pass_diffuse_color = kfilm->pass_stride; break; @@ -563,78 +334,56 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) kfilm->pass_stride; have_cryptomatte = true; break; + + case PASS_DENOISING_NORMAL: + kfilm->pass_denoising_normal = kfilm->pass_stride; + break; + case PASS_DENOISING_ALBEDO: + kfilm->pass_denoising_albedo = kfilm->pass_stride; + break; + + case PASS_SHADOW_CATCHER: + kfilm->pass_shadow_catcher = kfilm->pass_stride; + break; + case PASS_SHADOW_CATCHER_SAMPLE_COUNT: + kfilm->pass_shadow_catcher_sample_count = kfilm->pass_stride; + break; + case PASS_SHADOW_CATCHER_MATTE: + kfilm->pass_shadow_catcher_matte = kfilm->pass_stride; + break; + case PASS_ADAPTIVE_AUX_BUFFER: kfilm->pass_adaptive_aux_buffer = kfilm->pass_stride; break; case PASS_SAMPLE_COUNT: kfilm->pass_sample_count = kfilm->pass_stride; break; + case PASS_AOV_COLOR: - if (kfilm->pass_aov_color_num == 0) { + if (!have_aov_color) { kfilm->pass_aov_color = kfilm->pass_stride; + have_aov_color = true; } - kfilm->pass_aov_color_num++; break; case PASS_AOV_VALUE: - if (kfilm->pass_aov_value_num == 0) { + if (!have_aov_value) { kfilm->pass_aov_value = kfilm->pass_stride; + have_aov_value = true; } - kfilm->pass_aov_value_num++; break; default: assert(false); break; } - if (pass.type == display_pass) { - kfilm->display_pass_stride = kfilm->pass_stride; - kfilm->display_pass_components = pass.components; - kfilm->use_display_exposure = pass.exposure && (kfilm->exposure != 1.0f); - } - else if (pass.type == PASS_DIFFUSE_COLOR || pass.type == PASS_TRANSMISSION_COLOR || - pass.type == PASS_GLOSSY_COLOR) { - kfilm->display_divide_pass_stride = kfilm->pass_stride; - } - - kfilm->pass_stride += pass.components; - } - - kfilm->pass_denoising_data = 0; - kfilm->pass_denoising_clean = 0; - kfilm->denoising_flags = 0; - if (denoising_data_pass) { - kfilm->pass_denoising_data = kfilm->pass_stride; - kfilm->pass_stride += DENOISING_PASS_SIZE_BASE; - kfilm->denoising_flags = denoising_flags; - if (denoising_clean_pass) { - kfilm->pass_denoising_clean = kfilm->pass_stride; - kfilm->pass_stride += DENOISING_PASS_SIZE_CLEAN; - kfilm->use_light_pass = 1; - } - if (denoising_prefiltered_pass) { - kfilm->pass_stride += DENOISING_PASS_SIZE_PREFILTERED; - } - } - - kfilm->pass_stride = align_up(kfilm->pass_stride, 4); - - /* When displaying the normal/uv pass in the viewport we need to disable - * transparency. - * - * We also don't need to perform light accumulations. Later we want to optimize this to suppress - * light calculations. */ - if (display_pass == PASS_NORMAL || display_pass == PASS_UV) { - kfilm->use_light_pass = 0; - } - else { - kfilm->pass_alpha_threshold = pass_alpha_threshold; + kfilm->pass_stride += pass->get_info().num_components; } /* update filter table */ vector table = filter_table(filter_type, filter_width); - scene->lookup_tables->remove_table(&filter_table_offset); - filter_table_offset = scene->lookup_tables->add_table(dscene, table); - kfilm->filter_table_offset = (int)filter_table_offset; + scene->lookup_tables->remove_table(&filter_table_offset_); + filter_table_offset_ = scene->lookup_tables->add_table(dscene, table); + kfilm->filter_table_offset = (int)filter_table_offset_; /* mist pass parameters */ kfilm->mist_start = mist_start; @@ -644,79 +393,298 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) kfilm->cryptomatte_passes = cryptomatte_passes; kfilm->cryptomatte_depth = cryptomatte_depth; - pass_stride = kfilm->pass_stride; - denoising_data_offset = kfilm->pass_denoising_data; - denoising_clean_offset = kfilm->pass_denoising_clean; - clear_modified(); } void Film::device_free(Device * /*device*/, DeviceScene * /*dscene*/, Scene *scene) { - scene->lookup_tables->remove_table(&filter_table_offset); -} - -void Film::tag_passes_update(Scene *scene, const vector &passes_, bool update_passes) -{ - if (Pass::contains(scene->passes, PASS_UV) != Pass::contains(passes_, PASS_UV)) { - scene->geometry_manager->tag_update(scene, GeometryManager::UV_PASS_NEEDED); - - foreach (Shader *shader, scene->shaders) - shader->need_update_uvs = true; - } - else if (Pass::contains(scene->passes, PASS_MOTION) != Pass::contains(passes_, PASS_MOTION)) { - scene->geometry_manager->tag_update(scene, GeometryManager::MOTION_PASS_NEEDED); - } - else if (Pass::contains(scene->passes, PASS_AO) != Pass::contains(passes_, PASS_AO)) { - scene->integrator->tag_update(scene, Integrator::AO_PASS_MODIFIED); - } - - if (update_passes) { - scene->passes = passes_; - } + scene->lookup_tables->remove_table(&filter_table_offset_); } int Film::get_aov_offset(Scene *scene, string name, bool &is_color) { - int num_color = 0, num_value = 0; - foreach (const Pass &pass, scene->passes) { - if (pass.type == PASS_AOV_COLOR) { - num_color++; - } - else if (pass.type == PASS_AOV_VALUE) { - num_value++; - } - else { - continue; + int offset_color = 0, offset_value = 0; + foreach (const Pass *pass, scene->passes) { + if (pass->get_name() == name) { + if (pass->get_type() == PASS_AOV_VALUE) { + is_color = false; + return offset_value; + } + else if (pass->get_type() == PASS_AOV_COLOR) { + is_color = true; + return offset_color; + } } - if (pass.name == name) { - is_color = (pass.type == PASS_AOV_COLOR); - return (is_color ? num_color : num_value) - 1; + if (pass->get_type() == PASS_AOV_VALUE) { + offset_value += pass->get_info().num_components; + } + else if (pass->get_type() == PASS_AOV_COLOR) { + offset_color += pass->get_info().num_components; } } return -1; } -int Film::get_pass_stride() const +void Film::update_passes(Scene *scene, bool add_sample_count_pass) { - return pass_stride; + const Background *background = scene->background; + const BakeManager *bake_manager = scene->bake_manager; + const ObjectManager *object_manager = scene->object_manager; + Integrator *integrator = scene->integrator; + + if (!is_modified() && !object_manager->need_update() && !integrator->is_modified()) { + return; + } + + /* Remove auto generated passes and recreate them. */ + remove_auto_passes(scene); + + /* Display pass for viewport. */ + const PassType display_pass = get_display_pass(); + add_auto_pass(scene, display_pass); + + /* Assumption is that a combined pass always exists for now, for example + * adaptive sampling is always based on a combined pass. But we should + * try to lift this limitation in the future for faster rendering of + * individual passes. */ + if (display_pass != PASS_COMBINED) { + add_auto_pass(scene, PASS_COMBINED); + } + + /* Create passes needed for adaptive sampling. */ + const AdaptiveSampling adaptive_sampling = integrator->get_adaptive_sampling(); + if (adaptive_sampling.use) { + add_auto_pass(scene, PASS_SAMPLE_COUNT); + add_auto_pass(scene, PASS_ADAPTIVE_AUX_BUFFER); + } + + /* Create passes needed for denoising. */ + const bool use_denoise = integrator->get_use_denoise(); + if (use_denoise) { + if (integrator->get_use_denoise_pass_normal()) { + add_auto_pass(scene, PASS_DENOISING_NORMAL); + } + if (integrator->get_use_denoise_pass_albedo()) { + add_auto_pass(scene, PASS_DENOISING_ALBEDO); + } + } + + /* Create passes for shadow catcher. */ + if (scene->has_shadow_catcher()) { + const bool need_background = get_use_approximate_shadow_catcher() && + !background->get_transparent(); + + add_auto_pass(scene, PASS_SHADOW_CATCHER); + add_auto_pass(scene, PASS_SHADOW_CATCHER_SAMPLE_COUNT); + add_auto_pass(scene, PASS_SHADOW_CATCHER_MATTE); + + if (need_background) { + add_auto_pass(scene, PASS_BACKGROUND); + } + } + else if (Pass::contains(scene->passes, PASS_SHADOW_CATCHER)) { + add_auto_pass(scene, PASS_SHADOW_CATCHER); + add_auto_pass(scene, PASS_SHADOW_CATCHER_SAMPLE_COUNT); + } + + const vector passes_immutable = scene->passes; + for (const Pass *pass : passes_immutable) { + const PassInfo info = pass->get_info(); + /* Add utility passes needed to generate some light passes. */ + if (info.divide_type != PASS_NONE) { + add_auto_pass(scene, info.divide_type); + } + if (info.direct_type != PASS_NONE) { + add_auto_pass(scene, info.direct_type); + } + if (info.indirect_type != PASS_NONE) { + add_auto_pass(scene, info.indirect_type); + } + + /* NOTE: Enable all denoised passes when storage is requested. + * This way it is possible to tweak denoiser parameters later on. */ + if (info.support_denoise && use_denoise) { + add_auto_pass(scene, pass->get_type(), PassMode::DENOISED); + } + } + + if (bake_manager->get_baking()) { + add_auto_pass(scene, PASS_BAKE_PRIMITIVE, "BakePrimitive"); + add_auto_pass(scene, PASS_BAKE_DIFFERENTIAL, "BakeDifferential"); + } + + if (add_sample_count_pass) { + if (!Pass::contains(scene->passes, PASS_SAMPLE_COUNT)) { + add_auto_pass(scene, PASS_SAMPLE_COUNT); + } + } + + /* Remove duplicates and initialize internal pass info. */ + finalize_passes(scene, use_denoise); + + /* Flush scene updates. */ + const bool have_uv_pass = Pass::contains(scene->passes, PASS_UV); + const bool have_motion_pass = Pass::contains(scene->passes, PASS_MOTION); + const bool have_ao_pass = Pass::contains(scene->passes, PASS_AO); + + if (have_uv_pass != prev_have_uv_pass) { + scene->geometry_manager->tag_update(scene, GeometryManager::UV_PASS_NEEDED); + foreach (Shader *shader, scene->shaders) + shader->need_update_uvs = true; + } + if (have_motion_pass != prev_have_motion_pass) { + scene->geometry_manager->tag_update(scene, GeometryManager::MOTION_PASS_NEEDED); + } + if (have_ao_pass != prev_have_ao_pass) { + scene->integrator->tag_update(scene, Integrator::AO_PASS_MODIFIED); + } + + prev_have_uv_pass = have_uv_pass; + prev_have_motion_pass = have_motion_pass; + prev_have_ao_pass = have_ao_pass; + + tag_modified(); + + /* Debug logging. */ + if (VLOG_IS_ON(2)) { + VLOG(2) << "Effective scene passes:"; + for (const Pass *pass : scene->passes) { + VLOG(2) << "- " << *pass; + } + } } -int Film::get_denoising_data_offset() const +void Film::add_auto_pass(Scene *scene, PassType type, const char *name) { - return denoising_data_offset; + add_auto_pass(scene, type, PassMode::NOISY, name); } -int Film::get_denoising_clean_offset() const +void Film::add_auto_pass(Scene *scene, PassType type, PassMode mode, const char *name) { - return denoising_clean_offset; + Pass *pass = new Pass(); + pass->set_type(type); + pass->set_mode(mode); + pass->set_name(ustring((name) ? name : "")); + pass->is_auto_ = true; + + pass->set_owner(scene); + scene->passes.push_back(pass); } -size_t Film::get_filter_table_offset() const +void Film::remove_auto_passes(Scene *scene) { - return filter_table_offset; + /* Remove all passes which were automatically created. */ + vector new_passes; + + for (Pass *pass : scene->passes) { + if (!pass->is_auto_) { + new_passes.push_back(pass); + } + else { + delete pass; + } + } + + scene->passes = new_passes; +} + +static bool compare_pass_order(const Pass *a, const Pass *b) +{ + const int num_components_a = a->get_info().num_components; + const int num_components_b = b->get_info().num_components; + + if (num_components_a == num_components_b) { + return (a->get_type() < b->get_type()); + } + + return num_components_a > num_components_b; +} + +void Film::finalize_passes(Scene *scene, const bool use_denoise) +{ + /* Remove duplicate passes. */ + vector new_passes; + + for (Pass *pass : scene->passes) { + /* Disable denoising on passes if denoising is disabled, or if the + * pass does not support it. */ + pass->set_mode((use_denoise && pass->get_info().support_denoise) ? pass->get_mode() : + PassMode::NOISY); + + /* Merge duplicate passes. */ + bool duplicate_found = false; + for (Pass *new_pass : new_passes) { + /* If different type or denoising, don't merge. */ + if (new_pass->get_type() != pass->get_type() || new_pass->get_mode() != pass->get_mode()) { + continue; + } + + /* If both passes have a name and the names are different, don't merge. + * If either pass has a name, we'll use that name. */ + if (!pass->get_name().empty() && !new_pass->get_name().empty() && + pass->get_name() != new_pass->get_name()) { + continue; + } + + if (!pass->get_name().empty() && new_pass->get_name().empty()) { + new_pass->set_name(pass->get_name()); + } + + new_pass->is_auto_ &= pass->is_auto_; + duplicate_found = true; + break; + } + + if (!duplicate_found) { + new_passes.push_back(pass); + } + else { + delete pass; + } + } + + /* Order from by components and type, This is required to for AOVs and cryptomatte passes, + * which the kernel assumes to be in order. Note this must use stable sort so cryptomatte + * passes remain in the right order. */ + stable_sort(new_passes.begin(), new_passes.end(), compare_pass_order); + + scene->passes = new_passes; +} + +uint Film::get_kernel_features(const Scene *scene) const +{ + uint kernel_features = 0; + + for (const Pass *pass : scene->passes) { + if (!pass->is_written()) { + continue; + } + + const PassType pass_type = pass->get_type(); + const PassMode pass_mode = pass->get_mode(); + + if (pass_mode == PassMode::DENOISED || pass_type == PASS_DENOISING_NORMAL || + pass_type == PASS_DENOISING_ALBEDO) { + kernel_features |= KERNEL_FEATURE_DENOISING; + } + + if (pass_type != PASS_NONE && pass_type != PASS_COMBINED && + pass_type <= PASS_CATEGORY_LIGHT_END) { + kernel_features |= KERNEL_FEATURE_LIGHT_PASSES; + + if (pass_type == PASS_SHADOW) { + kernel_features |= KERNEL_FEATURE_SHADOW_PASS; + } + } + + if (pass_type == PASS_AO) { + kernel_features |= KERNEL_FEATURE_NODE_RAYTRACE; + } + } + + return kernel_features; } CCL_NAMESPACE_END diff --git a/intern/cycles/render/film.h b/intern/cycles/render/film.h index 462a7275491..5d327353361 100644 --- a/intern/cycles/render/film.h +++ b/intern/cycles/render/film.h @@ -17,6 +17,7 @@ #ifndef __FILM_H__ #define __FILM_H__ +#include "render/pass.h" #include "util/util_string.h" #include "util/util_vector.h" @@ -38,36 +39,15 @@ typedef enum FilterType { FILTER_NUM_TYPES, } FilterType; -class Pass : public Node { - public: - NODE_DECLARE - - Pass(); - - PassType type; - int components; - bool filter; - bool exposure; - PassType divide_type; - ustring name; - - static void add(PassType type, vector &passes, const char *name = NULL); - static bool equals(const vector &A, const vector &B); - static bool contains(const vector &passes, PassType); -}; - class Film : public Node { public: NODE_DECLARE NODE_SOCKET_API(float, exposure) - NODE_SOCKET_API(bool, denoising_data_pass) - NODE_SOCKET_API(bool, denoising_clean_pass) - NODE_SOCKET_API(bool, denoising_prefiltered_pass) - NODE_SOCKET_API(int, denoising_flags) NODE_SOCKET_API(float, pass_alpha_threshold) NODE_SOCKET_API(PassType, display_pass) + NODE_SOCKET_API(bool, show_active_pixels) NODE_SOCKET_API(FilterType, filter_type) NODE_SOCKET_API(float, filter_width) @@ -76,17 +56,18 @@ class Film : public Node { NODE_SOCKET_API(float, mist_depth) NODE_SOCKET_API(float, mist_falloff) - NODE_SOCKET_API(bool, use_light_visibility) NODE_SOCKET_API(CryptomatteType, cryptomatte_passes) NODE_SOCKET_API(int, cryptomatte_depth) - NODE_SOCKET_API(bool, use_adaptive_sampling) + /* Approximate shadow catcher pass into its matte pass, so that both artificial objects and + * shadows can be alpha-overed onto a backdrop. */ + NODE_SOCKET_API(bool, use_approximate_shadow_catcher) private: - int pass_stride; - int denoising_data_offset; - int denoising_clean_offset; - size_t filter_table_offset; + size_t filter_table_offset_; + bool prev_have_uv_pass = false; + bool prev_have_motion_pass = false; + bool prev_have_ao_pass = false; public: Film(); @@ -98,14 +79,20 @@ class Film : public Node { void device_update(Device *device, DeviceScene *dscene, Scene *scene); void device_free(Device *device, DeviceScene *dscene, Scene *scene); - void tag_passes_update(Scene *scene, const vector &passes_, bool update_passes = true); - int get_aov_offset(Scene *scene, string name, bool &is_color); - int get_pass_stride() const; - int get_denoising_data_offset() const; - int get_denoising_clean_offset() const; - size_t get_filter_table_offset() const; + /* Update passes so that they contain all passes required for the configured functionality. + * + * If `add_sample_count_pass` is true then the SAMPLE_COUNT pass is ensured to be added. */ + void update_passes(Scene *scene, bool add_sample_count_pass); + + uint get_kernel_features(const Scene *scene) const; + + private: + void add_auto_pass(Scene *scene, PassType type, const char *name = nullptr); + void add_auto_pass(Scene *scene, PassType type, PassMode mode, const char *name = nullptr); + void remove_auto_passes(Scene *scene); + void finalize_passes(Scene *scene, const bool use_denoise); }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 7ec1d2d9abb..6804a006fe6 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -215,6 +215,12 @@ void Geometry::compute_bvh( msg += string_printf("%s %u/%u", name.c_str(), (uint)(n + 1), (uint)total); Object object; + + /* Ensure all visibility bits are set at the geometry level BVH. In + * the object level BVH is where actual visibility is tested. */ + object.set_is_shadow_catcher(true); + object.set_visibility(~0); + object.set_geometry(this); vector geometry; @@ -315,7 +321,7 @@ void GeometryManager::update_osl_attributes(Device *device, { #ifdef WITH_OSL /* for OSL, a hash map is used to lookup the attribute by name. */ - OSLGlobals *og = (OSLGlobals *)device->osl_memory(); + OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); og->object_name_map.clear(); og->attribute_map.clear(); @@ -1855,8 +1861,8 @@ void GeometryManager::device_update(Device *device, }); Camera *dicing_camera = scene->dicing_camera; - dicing_camera->set_screen_size_and_resolution( - dicing_camera->get_full_width(), dicing_camera->get_full_height(), 1); + dicing_camera->set_screen_size(dicing_camera->get_full_width(), + dicing_camera->get_full_height()); dicing_camera->update(scene); size_t i = 0; @@ -2157,7 +2163,7 @@ void GeometryManager::device_free(Device *device, DeviceScene *dscene, bool forc dscene->data.bvh.bvh_layout = BVH_LAYOUT_NONE; #ifdef WITH_OSL - OSLGlobals *og = (OSLGlobals *)device->osl_memory(); + OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); if (og) { og->object_name_map.clear(); diff --git a/intern/cycles/render/gpu_display.cpp b/intern/cycles/render/gpu_display.cpp new file mode 100644 index 00000000000..a8f0cc50583 --- /dev/null +++ b/intern/cycles/render/gpu_display.cpp @@ -0,0 +1,227 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "render/gpu_display.h" + +#include "render/buffers.h" +#include "util/util_logging.h" + +CCL_NAMESPACE_BEGIN + +void GPUDisplay::reset(const BufferParams &buffer_params) +{ + thread_scoped_lock lock(mutex_); + + const GPUDisplayParams old_params = params_; + + params_.offset = make_int2(buffer_params.full_x, buffer_params.full_y); + params_.full_size = make_int2(buffer_params.full_width, buffer_params.full_height); + params_.size = make_int2(buffer_params.width, buffer_params.height); + + /* If the parameters did change tag texture as unusable. This avoids drawing old texture content + * in an updated configuration of the viewport. For example, avoids drawing old frame when render + * border did change. + * If the parameters did not change, allow drawing the current state of the texture, which will + * not count as an up-to-date redraw. This will avoid flickering when doping camera navigation by + * showing a previously rendered frame for until the new one is ready. */ + if (old_params.modified(params_)) { + texture_state_.is_usable = false; + } + + texture_state_.is_outdated = true; +} + +void GPUDisplay::mark_texture_updated() +{ + texture_state_.is_outdated = false; + texture_state_.is_usable = true; +} + +/* -------------------------------------------------------------------- + * Update procedure. + */ + +bool GPUDisplay::update_begin(int texture_width, int texture_height) +{ + DCHECK(!update_state_.is_active); + + if (update_state_.is_active) { + LOG(ERROR) << "Attempt to re-activate update process."; + return false; + } + + /* Get parameters within a mutex lock, to avoid reset() modifying them at the same time. + * The update itself is non-blocking however, for better performance and to avoid + * potential deadlocks due to locks held by the subclass. */ + GPUDisplayParams params; + { + thread_scoped_lock lock(mutex_); + params = params_; + texture_state_.size = make_int2(texture_width, texture_height); + } + + if (!do_update_begin(params, texture_width, texture_height)) { + LOG(ERROR) << "GPUDisplay implementation could not begin update."; + return false; + } + + update_state_.is_active = true; + + return true; +} + +void GPUDisplay::update_end() +{ + DCHECK(update_state_.is_active); + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to deactivate inactive update process."; + return; + } + + do_update_end(); + + update_state_.is_active = false; +} + +int2 GPUDisplay::get_texture_size() const +{ + return texture_state_.size; +} + +/* -------------------------------------------------------------------- + * Texture update from CPU buffer. + */ + +void GPUDisplay::copy_pixels_to_texture( + const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height) +{ + DCHECK(update_state_.is_active); + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to copy pixels data outside of GPUDisplay update."; + return; + } + + mark_texture_updated(); + do_copy_pixels_to_texture(rgba_pixels, texture_x, texture_y, pixels_width, pixels_height); +} + +/* -------------------------------------------------------------------- + * Texture buffer mapping. + */ + +half4 *GPUDisplay::map_texture_buffer() +{ + DCHECK(!texture_buffer_state_.is_mapped); + DCHECK(update_state_.is_active); + + if (texture_buffer_state_.is_mapped) { + LOG(ERROR) << "Attempt to re-map an already mapped texture buffer."; + return nullptr; + } + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to copy pixels data outside of GPUDisplay update."; + return nullptr; + } + + half4 *mapped_rgba_pixels = do_map_texture_buffer(); + + if (mapped_rgba_pixels) { + texture_buffer_state_.is_mapped = true; + } + + return mapped_rgba_pixels; +} + +void GPUDisplay::unmap_texture_buffer() +{ + DCHECK(texture_buffer_state_.is_mapped); + + if (!texture_buffer_state_.is_mapped) { + LOG(ERROR) << "Attempt to unmap non-mapped texture buffer."; + return; + } + + texture_buffer_state_.is_mapped = false; + + mark_texture_updated(); + do_unmap_texture_buffer(); +} + +/* -------------------------------------------------------------------- + * Graphics interoperability. + */ + +DeviceGraphicsInteropDestination GPUDisplay::graphics_interop_get() +{ + DCHECK(!texture_buffer_state_.is_mapped); + DCHECK(update_state_.is_active); + + if (texture_buffer_state_.is_mapped) { + LOG(ERROR) + << "Attempt to use graphics interoperability mode while the texture buffer is mapped."; + return DeviceGraphicsInteropDestination(); + } + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to use graphics interoperability outside of GPUDisplay update."; + return DeviceGraphicsInteropDestination(); + } + + /* Assume that interop will write new values to the texture. */ + mark_texture_updated(); + + return do_graphics_interop_get(); +} + +void GPUDisplay::graphics_interop_activate() +{ +} + +void GPUDisplay::graphics_interop_deactivate() +{ +} + +/* -------------------------------------------------------------------- + * Drawing. + */ + +bool GPUDisplay::draw() +{ + /* Get parameters within a mutex lock, to avoid reset() modifying them at the same time. + * The drawing itself is non-blocking however, for better performance and to avoid + * potential deadlocks due to locks held by the subclass. */ + GPUDisplayParams params; + bool is_usable; + bool is_outdated; + + { + thread_scoped_lock lock(mutex_); + params = params_; + is_usable = texture_state_.is_usable; + is_outdated = texture_state_.is_outdated; + } + + if (is_usable) { + do_draw(params); + } + + return !is_outdated; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/gpu_display.h b/intern/cycles/render/gpu_display.h new file mode 100644 index 00000000000..cbe347895a1 --- /dev/null +++ b/intern/cycles/render/gpu_display.h @@ -0,0 +1,247 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "device/device_graphics_interop.h" +#include "util/util_half.h" +#include "util/util_thread.h" +#include "util/util_types.h" + +CCL_NAMESPACE_BEGIN + +class BufferParams; + +/* GPUDisplay class takes care of drawing render result in a viewport. The render result is stored + * in a GPU-side texture, which is updated from a path tracer and drawn by an application. + * + * The base GPUDisplay does some special texture state tracking, which allows render Session to + * make decisions on whether reset for an updated state is possible or not. This state should only + * be tracked in a base class and a particular implementation should not worry about it. + * + * The subclasses should only implement the pure virtual methods, which allows them to not worry + * about parent method calls, which helps them to be as small and reliable as possible. */ + +class GPUDisplayParams { + public: + /* Offset of the display within a viewport. + * For example, set to a lower-bottom corner of border render in Blender's viewport. */ + int2 offset = make_int2(0, 0); + + /* Full viewport size. + * + * NOTE: Is not affected by the resolution divider. */ + int2 full_size = make_int2(0, 0); + + /* Effective vieport size. + * In the case of border render, size of the border rectangle. + * + * NOTE: Is not affected by the resolution divider. */ + int2 size = make_int2(0, 0); + + bool modified(const GPUDisplayParams &other) const + { + return !(offset == other.offset && full_size == other.full_size && size == other.size); + } +}; + +class GPUDisplay { + public: + GPUDisplay() = default; + virtual ~GPUDisplay() = default; + + /* Reset the display for the new state of render session. Is called whenever session is reset, + * which happens on changes like viewport navigation or viewport dimension change. + * + * This call will configure parameters for a changed buffer and reset the texture state. */ + void reset(const BufferParams &buffer_params); + + const GPUDisplayParams &get_params() const + { + return params_; + } + + /* -------------------------------------------------------------------- + * Update procedure. + * + * These calls indicates a desire of the caller to update content of the displayed texture. */ + + /* Returns true when update is ready. Update should be finished with update_end(). + * + * If false is returned then no update is possible, and no update_end() call is needed. + * + * The texture width and height denotes an actual resolution of the underlying render result. */ + bool update_begin(int texture_width, int texture_height); + + void update_end(); + + /* Get currently configured texture size of the display (as configured by `update_begin()`. */ + int2 get_texture_size() const; + + /* -------------------------------------------------------------------- + * Texture update from CPU buffer. + * + * NOTE: The GPUDisplay should be marked for an update being in process with `update_begin()`. + * + * Most portable implementation, which must be supported by all platforms. Might not be the most + * efficient one. + */ + + /* Copy buffer of rendered pixels of a given size into a given position of the texture. + * + * This function does not acquire a lock. The reason for this is is to allow use of this function + * for partial updates from different devices. In this case the caller will acquire the lock + * once, update all the slices and release + * the lock once. This will ensure that draw() will never use partially updated texture. */ + void copy_pixels_to_texture( + const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height); + + /* -------------------------------------------------------------------- + * Texture buffer mapping. + * + * This functionality is used to update GPU-side texture content without need to maintain CPU + * side buffer on the caller. + * + * NOTE: The GPUDisplay should be marked for an update being in process with `update_begin()`. + * + * NOTE: Texture buffer can not be mapped while graphics interopeability is active. This means + * that `map_texture_buffer()` is not allowed between `graphics_interop_begin()` and + * `graphics_interop_end()` calls. + */ + + /* Map pixels memory form texture to a buffer available for write from CPU. Width and height will + * define a requested size of the texture to write to. + * Upon success a non-null pointer is returned and the texture buffer is to be unmapped. + * If an error happens during mapping, or if mapoping is not supported by this GPU display a + * null pointer is returned and the buffer is NOT to be unmapped. + * + * NOTE: Usually the implementation will rely on a GPU context of some sort, and the GPU context + * is often can not be bound to two threads simultaneously, and can not be released from a + * different thread. This means that the mapping API should be used from the single thread only, + */ + half4 *map_texture_buffer(); + void unmap_texture_buffer(); + + /* -------------------------------------------------------------------- + * Graphics interoperability. + * + * A special code path which allows to update texture content directly from the GPU compute + * device. Complementary part of DeviceGraphicsInterop. + * + * NOTE: Graphics interoperability can not be used while the texture buffer is mapped. This means + * that `graphics_interop_get()` is not allowed between `map_texture_buffer()` and + * `unmap_texture_buffer()` calls. */ + + /* Get GPUDisplay graphics interoperability information which acts as a destination for the + * device API. */ + DeviceGraphicsInteropDestination graphics_interop_get(); + + /* (De)activate GPU display for graphics interoperability outside of regular display udpate + * routines. */ + virtual void graphics_interop_activate(); + virtual void graphics_interop_deactivate(); + + /* -------------------------------------------------------------------- + * Drawing. + */ + + /* Clear the texture by filling it with all zeroes. + * + * This call might happen in parallel with draw, but can never happen in parallel with the + * update. + * + * The actual zero-ing can be deferred to a later moment. What is important is that after clear + * and before pixels update the drawing texture will be fully empty, and that partial update + * after clear will write new pixel values for an updating area, leaving everything else zeroed. + * + * If the GPU display supports graphics interoperability then the zeroing the display is to be + * delegated to the device via the `DeviceGraphicsInteropDestination`. */ + virtual void clear() = 0; + + /* Draw the current state of the texture. + * + * Returns true if this call did draw an updated state of the texture. */ + bool draw(); + + protected: + /* Implementation-specific calls which subclasses are to implement. + * These `do_foo()` method corresponds to their `foo()` calls, but they are purely virtual to + * simplify their particular implementation. */ + virtual bool do_update_begin(const GPUDisplayParams ¶ms, + int texture_width, + int texture_height) = 0; + virtual void do_update_end() = 0; + + virtual void do_copy_pixels_to_texture(const half4 *rgba_pixels, + int texture_x, + int texture_y, + int pixels_width, + int pixels_height) = 0; + + virtual half4 *do_map_texture_buffer() = 0; + virtual void do_unmap_texture_buffer() = 0; + + /* Note that this might be called in parallel to do_update_begin() and do_update_end(), + * the subclass is responsible for appropriate mutex locks to avoid multiple threads + * editing and drawing the texture at the same time. */ + virtual void do_draw(const GPUDisplayParams ¶ms) = 0; + + virtual DeviceGraphicsInteropDestination do_graphics_interop_get() = 0; + + private: + thread_mutex mutex_; + GPUDisplayParams params_; + + /* Mark texture as its content has been updated. + * Used from places which knows that the texture content has been brough up-to-date, so that the + * drawing knows whether it can be performed, and whether drawing happenned with an up-to-date + * texture state. */ + void mark_texture_updated(); + + /* State of the update process. */ + struct { + /* True when update is in process, indicated by `update_begin()` / `update_end()`. */ + bool is_active = false; + } update_state_; + + /* State of the texture, which is needed for an integration with render session and interactive + * updates and navigation. */ + struct { + /* Denotes whether possibly existing state of GPU side texture is still usable. + * It will not be usable in cases like render border did change (in this case we don't want + * previous texture to be rendered at all). + * + * However, if only navigation or object in scene did change, then the outdated state of the + * texture is still usable for draw, preventing display viewport flickering on navigation and + * object modifications. */ + bool is_usable = false; + + /* Texture is considered outdated after `reset()` until the next call of + * `copy_pixels_to_texture()`. */ + bool is_outdated = true; + + /* Texture size in pixels. */ + int2 size = make_int2(0, 0); + } texture_state_; + + /* State of the texture buffer. Is tracked to perform sanity checks. */ + struct { + /* True when the texture buffer is mapped with `map_texture_buffer()`. */ + bool is_mapped = false; + } texture_buffer_state_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h index 5102b182593..3584754fad1 100644 --- a/intern/cycles/render/graph.h +++ b/intern/cycles/render/graph.h @@ -224,10 +224,6 @@ class ShaderNode : public Node { { return false; } - virtual bool has_raytrace() - { - return false; - } vector inputs; vector outputs; @@ -242,22 +238,13 @@ class ShaderNode : public Node { * that those functions are for selective compilation only? */ - /* Nodes are split into several groups, group of level 0 contains - * nodes which are most commonly used, further levels are extension - * of previous one and includes less commonly used nodes. - */ - virtual int get_group() - { - return NODE_GROUP_LEVEL_0; - } - /* Node feature are used to disable huge nodes inside the group, * so it's possible to disable huge nodes inside of the required * nodes group. */ virtual int get_feature() { - return bump == SHADER_BUMP_NONE ? 0 : NODE_FEATURE_BUMP; + return bump == SHADER_BUMP_NONE ? 0 : KERNEL_FEATURE_NODE_BUMP; } /* Get closure ID to which the node compiles into. */ diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp index d8749cec9fa..d74d14242bb 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/render/integrator.cpp @@ -53,6 +53,8 @@ NODE_DEFINE(Integrator) SOCKET_INT(transparent_max_bounce, "Transparent Max Bounce", 7); SOCKET_INT(ao_bounces, "AO Bounces", 0); + SOCKET_FLOAT(ao_factor, "AO Factor", 0.0f); + SOCKET_FLOAT(ao_distance, "AO Distance", FLT_MAX); SOCKET_INT(volume_max_steps, "Volume Max Steps", 1024); SOCKET_FLOAT(volume_step_rate, "Volume Step Rate", 1.0f); @@ -66,33 +68,39 @@ NODE_DEFINE(Integrator) SOCKET_BOOLEAN(motion_blur, "Motion Blur", false); SOCKET_INT(aa_samples, "AA Samples", 0); - SOCKET_INT(diffuse_samples, "Diffuse Samples", 1); - SOCKET_INT(glossy_samples, "Glossy Samples", 1); - SOCKET_INT(transmission_samples, "Transmission Samples", 1); - SOCKET_INT(ao_samples, "AO Samples", 1); - SOCKET_INT(mesh_light_samples, "Mesh Light Samples", 1); - SOCKET_INT(subsurface_samples, "Subsurface Samples", 1); - SOCKET_INT(volume_samples, "Volume Samples", 1); SOCKET_INT(start_sample, "Start Sample", 0); + SOCKET_BOOLEAN(use_adaptive_sampling, "Use Adaptive Sampling", false); SOCKET_FLOAT(adaptive_threshold, "Adaptive Threshold", 0.0f); SOCKET_INT(adaptive_min_samples, "Adaptive Min Samples", 0); - SOCKET_BOOLEAN(sample_all_lights_direct, "Sample All Lights Direct", true); - SOCKET_BOOLEAN(sample_all_lights_indirect, "Sample All Lights Indirect", true); SOCKET_FLOAT(light_sampling_threshold, "Light Sampling Threshold", 0.05f); - static NodeEnum method_enum; - method_enum.insert("path", PATH); - method_enum.insert("branched_path", BRANCHED_PATH); - SOCKET_ENUM(method, "Method", method_enum, PATH); - static NodeEnum sampling_pattern_enum; sampling_pattern_enum.insert("sobol", SAMPLING_PATTERN_SOBOL); - sampling_pattern_enum.insert("cmj", SAMPLING_PATTERN_CMJ); sampling_pattern_enum.insert("pmj", SAMPLING_PATTERN_PMJ); SOCKET_ENUM(sampling_pattern, "Sampling Pattern", sampling_pattern_enum, SAMPLING_PATTERN_SOBOL); + static NodeEnum denoiser_type_enum; + denoiser_type_enum.insert("optix", DENOISER_OPTIX); + denoiser_type_enum.insert("openimagedenoise", DENOISER_OPENIMAGEDENOISE); + + static NodeEnum denoiser_prefilter_enum; + denoiser_prefilter_enum.insert("none", DENOISER_PREFILTER_NONE); + denoiser_prefilter_enum.insert("fast", DENOISER_PREFILTER_FAST); + denoiser_prefilter_enum.insert("accurate", DENOISER_PREFILTER_ACCURATE); + + /* Default to accurate denoising with OpenImageDenoise. For interactive viewport + * it's best use OptiX and disable the normal pass since it does not always have + * the desired effect for that denoiser. */ + SOCKET_BOOLEAN(use_denoise, "Use Denoiser", false); + SOCKET_ENUM(denoiser_type, "Denoiser Type", denoiser_type_enum, DENOISER_OPENIMAGEDENOISE); + SOCKET_INT(denoise_start_sample, "Start Sample to Denoise", 0); + SOCKET_BOOLEAN(use_denoise_pass_albedo, "Use Albedo Pass for Denoiser", true); + SOCKET_BOOLEAN(use_denoise_pass_normal, "Use Normal Pass for Denoiser", true); + SOCKET_ENUM( + denoiser_prefilter, "Denoiser Type", denoiser_prefilter_enum, DENOISER_PREFILTER_ACCURATE); + return type; } @@ -115,13 +123,20 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene } }); - const bool need_update_lut = ao_samples_is_modified() || diffuse_samples_is_modified() || - glossy_samples_is_modified() || max_bounce_is_modified() || - max_transmission_bounce_is_modified() || - mesh_light_samples_is_modified() || method_is_modified() || - sampling_pattern_is_modified() || - subsurface_samples_is_modified() || - transmission_samples_is_modified() || volume_samples_is_modified(); + KernelIntegrator *kintegrator = &dscene->data.integrator; + + /* Adaptive sampling requires PMJ samples. + * + * This also makes detection of sampling pattern a bit more involved: can not rely on the changed + * state of socket, since its value might be different from the effective value used here. So + * instead compare with previous value in the KernelIntegrator. Only do it if the device was + * updated once (in which case the `sample_pattern_lut` will be allocated to a non-zero size). */ + const SamplingPattern new_sampling_pattern = (use_adaptive_sampling) ? SAMPLING_PATTERN_PMJ : + sampling_pattern; + + const bool need_update_lut = max_bounce_is_modified() || max_transmission_bounce_is_modified() || + dscene->sample_pattern_lut.size() == 0 || + kintegrator->sampling_pattern != new_sampling_pattern; if (need_update_lut) { dscene->sample_pattern_lut.tag_realloc(); @@ -129,8 +144,6 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene device_free(device, dscene); - KernelIntegrator *kintegrator = &dscene->data.integrator; - /* integrator parameters */ kintegrator->min_bounce = min_bounce + 1; kintegrator->max_bounce = max_bounce + 1; @@ -143,12 +156,9 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene kintegrator->transparent_min_bounce = transparent_min_bounce + 1; kintegrator->transparent_max_bounce = transparent_max_bounce + 1; - if (ao_bounces == 0) { - kintegrator->ao_bounces = INT_MAX; - } - else { - kintegrator->ao_bounces = ao_bounces - 1; - } + kintegrator->ao_bounces = ao_bounces; + kintegrator->ao_bounces_distance = ao_distance; + kintegrator->ao_bounces_factor = ao_factor; /* Transparent Shadows * We only need to enable transparent shadows, if we actually have @@ -171,10 +181,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene kintegrator->caustics_refractive = caustics_refractive; kintegrator->filter_glossy = (filter_glossy == 0.0f) ? FLT_MAX : 1.0f / filter_glossy; - kintegrator->seed = hash_uint2(seed, 0); - - kintegrator->use_ambient_occlusion = ((Pass::contains(scene->passes, PASS_AO)) || - dscene->data.background.ao_factor != 0.0f); + kintegrator->seed = seed; kintegrator->sample_clamp_direct = (sample_clamp_direct == 0.0f) ? FLT_MAX : sample_clamp_direct * 3.0f; @@ -182,51 +189,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene FLT_MAX : sample_clamp_indirect * 3.0f; - kintegrator->branched = (method == BRANCHED_PATH) && device->info.has_branched_path; - kintegrator->volume_decoupled = device->info.has_volume_decoupled; - kintegrator->diffuse_samples = diffuse_samples; - kintegrator->glossy_samples = glossy_samples; - kintegrator->transmission_samples = transmission_samples; - kintegrator->ao_samples = ao_samples; - kintegrator->mesh_light_samples = mesh_light_samples; - kintegrator->subsurface_samples = subsurface_samples; - kintegrator->volume_samples = volume_samples; - kintegrator->start_sample = start_sample; - - if (kintegrator->branched) { - kintegrator->sample_all_lights_direct = sample_all_lights_direct; - kintegrator->sample_all_lights_indirect = sample_all_lights_indirect; - } - else { - kintegrator->sample_all_lights_direct = false; - kintegrator->sample_all_lights_indirect = false; - } - - kintegrator->sampling_pattern = sampling_pattern; - kintegrator->aa_samples = aa_samples; - if (aa_samples > 0 && adaptive_min_samples == 0) { - kintegrator->adaptive_min_samples = max(4, (int)sqrtf(aa_samples)); - VLOG(1) << "Cycles adaptive sampling: automatic min samples = " - << kintegrator->adaptive_min_samples; - } - else { - kintegrator->adaptive_min_samples = max(4, adaptive_min_samples); - } - - kintegrator->adaptive_step = 4; - kintegrator->adaptive_stop_per_sample = device->info.has_adaptive_stop_per_sample; - - /* Adaptive step must be a power of two for bitwise operations to work. */ - assert((kintegrator->adaptive_step & (kintegrator->adaptive_step - 1)) == 0); - - if (aa_samples > 0 && adaptive_threshold == 0.0f) { - kintegrator->adaptive_threshold = max(0.001f, 1.0f / (float)aa_samples); - VLOG(1) << "Cycles adaptive sampling: automatic threshold = " - << kintegrator->adaptive_threshold; - } - else { - kintegrator->adaptive_threshold = adaptive_threshold; - } + kintegrator->sampling_pattern = new_sampling_pattern; if (light_sampling_threshold > 0.0f) { kintegrator->light_inv_rr_threshold = 1.0f / light_sampling_threshold; @@ -236,29 +199,15 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene } /* sobol directions table */ - int max_samples = 1; - - if (kintegrator->branched) { - foreach (Light *light, scene->lights) - max_samples = max(max_samples, light->get_samples()); - - max_samples = max(max_samples, - max(diffuse_samples, max(glossy_samples, transmission_samples))); - max_samples = max(max_samples, max(ao_samples, max(mesh_light_samples, subsurface_samples))); - max_samples = max(max_samples, volume_samples); - } - - uint total_bounces = max_bounce + transparent_max_bounce + 3 + VOLUME_BOUNDS_MAX + - max(BSSRDF_MAX_HITS, BSSRDF_MAX_BOUNCES); - - max_samples *= total_bounces; + int max_samples = max_bounce + transparent_max_bounce + 3 + VOLUME_BOUNDS_MAX + + max(BSSRDF_MAX_HITS, BSSRDF_MAX_BOUNCES); int dimensions = PRNG_BASE_NUM + max_samples * PRNG_BOUNCE_NUM; dimensions = min(dimensions, SOBOL_MAX_DIMENSIONS); if (need_update_lut) { - if (sampling_pattern == SAMPLING_PATTERN_SOBOL) { - uint *directions = dscene->sample_pattern_lut.alloc(SOBOL_BITS * dimensions); + if (kintegrator->sampling_pattern == SAMPLING_PATTERN_SOBOL) { + uint *directions = (uint *)dscene->sample_pattern_lut.alloc(SOBOL_BITS * dimensions); sobol_generate_direction_vectors((uint(*)[SOBOL_BITS])directions, dimensions); @@ -276,10 +225,13 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene function_bind(&progressive_multi_jitter_02_generate_2D, sequence, sequence_size, j)); } pool.wait_work(); + dscene->sample_pattern_lut.copy_to_device(); } } + kintegrator->has_shadow_catcher = scene->has_shadow_catcher(); + dscene->sample_pattern_lut.clear_modified(); clear_modified(); } @@ -295,17 +247,12 @@ void Integrator::tag_update(Scene *scene, uint32_t flag) tag_modified(); } - if (flag & (AO_PASS_MODIFIED | BACKGROUND_AO_MODIFIED)) { + if (flag & AO_PASS_MODIFIED) { /* tag only the ao_bounces socket as modified so we avoid updating sample_pattern_lut * unnecessarily */ tag_ao_bounces_modified(); } - if ((flag & LIGHT_SAMPLES_MODIFIED) && (method == BRANCHED_PATH)) { - /* the number of light samples may affect the size of the sample_pattern_lut */ - tag_sampling_pattern_modified(); - } - if (filter_glossy_is_modified()) { foreach (Shader *shader, scene->shaders) { if (shader->has_integrator_dependency) { @@ -321,4 +268,65 @@ void Integrator::tag_update(Scene *scene, uint32_t flag) } } +AdaptiveSampling Integrator::get_adaptive_sampling() const +{ + AdaptiveSampling adaptive_sampling; + + adaptive_sampling.use = use_adaptive_sampling; + + if (!adaptive_sampling.use) { + return adaptive_sampling; + } + + if (aa_samples > 0 && adaptive_threshold == 0.0f) { + adaptive_sampling.threshold = max(0.001f, 1.0f / (float)aa_samples); + VLOG(1) << "Cycles adaptive sampling: automatic threshold = " << adaptive_sampling.threshold; + } + else { + adaptive_sampling.threshold = adaptive_threshold; + } + + if (adaptive_sampling.threshold > 0 && adaptive_min_samples == 0) { + /* Threshold 0.1 -> 32, 0.01 -> 64, 0.001 -> 128. + * This is highly scene dependent, we make a guess that seemed to work well + * in various test scenes. */ + const int min_samples = (int)ceilf(16.0f / powf(adaptive_sampling.threshold, 0.3f)); + adaptive_sampling.min_samples = max(4, min_samples); + VLOG(1) << "Cycles adaptive sampling: automatic min samples = " + << adaptive_sampling.min_samples; + } + else { + adaptive_sampling.min_samples = max(4, adaptive_min_samples); + } + + /* Arbitrary factor that makes the threshold more similar to what is was before, + * and gives arguably more intuitive values. */ + adaptive_sampling.threshold *= 5.0f; + + adaptive_sampling.adaptive_step = 16; + + DCHECK(is_power_of_two(adaptive_sampling.adaptive_step)) + << "Adaptive step must be a power of two for bitwise operations to work"; + + return adaptive_sampling; +} + +DenoiseParams Integrator::get_denoise_params() const +{ + DenoiseParams denoise_params; + + denoise_params.use = use_denoise; + + denoise_params.type = denoiser_type; + + denoise_params.start_sample = denoise_start_sample; + + denoise_params.use_pass_albedo = use_denoise_pass_albedo; + denoise_params.use_pass_normal = use_denoise_pass_normal; + + denoise_params.prefilter = denoiser_prefilter; + + return denoise_params; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h index 4eeeda92d41..32e108d62ca 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -19,7 +19,9 @@ #include "kernel/kernel_types.h" +#include "device/device_denoise.h" /* For the paramaters and type enum. */ #include "graph/node.h" +#include "integrator/adaptive_sampling.h" CCL_NAMESPACE_BEGIN @@ -43,6 +45,8 @@ class Integrator : public Node { NODE_SOCKET_API(int, transparent_max_bounce) NODE_SOCKET_API(int, ao_bounces) + NODE_SOCKET_API(float, ao_factor) + NODE_SOCKET_API(float, ao_distance) NODE_SOCKET_API(int, volume_max_steps) NODE_SOCKET_API(float, volume_step_rate) @@ -62,37 +66,26 @@ class Integrator : public Node { static const int MAX_SAMPLES = (1 << 24); NODE_SOCKET_API(int, aa_samples) - NODE_SOCKET_API(int, diffuse_samples) - NODE_SOCKET_API(int, glossy_samples) - NODE_SOCKET_API(int, transmission_samples) - NODE_SOCKET_API(int, ao_samples) - NODE_SOCKET_API(int, mesh_light_samples) - NODE_SOCKET_API(int, subsurface_samples) - NODE_SOCKET_API(int, volume_samples) NODE_SOCKET_API(int, start_sample) - NODE_SOCKET_API(bool, sample_all_lights_direct) - NODE_SOCKET_API(bool, sample_all_lights_indirect) NODE_SOCKET_API(float, light_sampling_threshold) + NODE_SOCKET_API(bool, use_adaptive_sampling) NODE_SOCKET_API(int, adaptive_min_samples) NODE_SOCKET_API(float, adaptive_threshold) - enum Method { - BRANCHED_PATH = 0, - PATH = 1, - - NUM_METHODS, - }; - - NODE_SOCKET_API(Method, method) - NODE_SOCKET_API(SamplingPattern, sampling_pattern) + NODE_SOCKET_API(bool, use_denoise); + NODE_SOCKET_API(DenoiserType, denoiser_type); + NODE_SOCKET_API(int, denoise_start_sample); + NODE_SOCKET_API(bool, use_denoise_pass_albedo); + NODE_SOCKET_API(bool, use_denoise_pass_normal); + NODE_SOCKET_API(DenoiserPrefilter, denoiser_prefilter); + enum : uint32_t { AO_PASS_MODIFIED = (1 << 0), - BACKGROUND_AO_MODIFIED = (1 << 1), - LIGHT_SAMPLES_MODIFIED = (1 << 2), + OBJECT_MANAGER = (1 << 1), /* tag everything in the manager for an update */ UPDATE_ALL = ~0u, @@ -107,6 +100,9 @@ class Integrator : public Node { void device_free(Device *device, DeviceScene *dscene, bool force_free = false); void tag_update(Scene *scene, uint32_t flag); + + AdaptiveSampling get_adaptive_sampling() const; + DenoiseParams get_denoise_params() const; }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/jitter.cpp b/intern/cycles/render/jitter.cpp index fc47b0e8f0a..e31f8abd446 100644 --- a/intern/cycles/render/jitter.cpp +++ b/intern/cycles/render/jitter.cpp @@ -242,12 +242,6 @@ class PMJ02_Generator : public PMJ_Generator { static void shuffle(float2 points[], int size, int rng_seed) { - /* Offset samples by 1.0 for faster scrambling in kernel_random.h */ - for (int i = 0; i < size; ++i) { - points[i].x += 1.0f; - points[i].y += 1.0f; - } - if (rng_seed == 0) { return; } diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index 15aa4e047b5..ae1150fc07b 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ -#include "render/light.h" #include "device/device.h" + #include "render/background.h" #include "render/film.h" #include "render/graph.h" #include "render/integrator.h" +#include "render/light.h" #include "render/mesh.h" #include "render/nodes.h" #include "render/object.h" @@ -27,6 +28,8 @@ #include "render/shader.h" #include "render/stats.h" +#include "integrator/shader_eval.h" + #include "util/util_foreach.h" #include "util/util_hash.h" #include "util/util_logging.h" @@ -43,63 +46,49 @@ static void shade_background_pixels(Device *device, vector &pixels, Progress &progress) { - /* create input */ - device_vector d_input(device, "background_input", MEM_READ_ONLY); - device_vector d_output(device, "background_output", MEM_READ_WRITE); - - uint4 *d_input_data = d_input.alloc(width * height); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - float u = (x + 0.5f) / width; - float v = (y + 0.5f) / height; - - uint4 in = make_uint4(__float_as_int(u), __float_as_int(v), 0, 0); - d_input_data[x + y * width] = in; - } - } - - /* compute on device */ - d_output.alloc(width * height); - d_output.zero_to_device(); - d_input.copy_to_device(); - + /* Needs to be up to data for attribute access. */ device->const_copy_to("__data", &dscene->data, sizeof(dscene->data)); - DeviceTask main_task(DeviceTask::SHADER); - main_task.shader_input = d_input.device_pointer; - main_task.shader_output = d_output.device_pointer; - main_task.shader_eval_type = SHADER_EVAL_BACKGROUND; - main_task.shader_x = 0; - main_task.shader_w = width * height; - main_task.num_samples = 1; - main_task.get_cancel = function_bind(&Progress::get_cancel, &progress); + const int size = width * height; + pixels.resize(size); - /* disabled splitting for now, there's an issue with multi-GPU mem_copy_from */ - list split_tasks; - main_task.split(split_tasks, 1, 128 * 128); + /* Evaluate shader on device. */ + ShaderEval shader_eval(device, progress); + shader_eval.eval( + SHADER_EVAL_BACKGROUND, + size, + [&](device_vector &d_input) { + /* Fill coordinates for shading. */ + KernelShaderEvalInput *d_input_data = d_input.data(); - foreach (DeviceTask &task, split_tasks) { - device->task_add(task); - device->task_wait(); - d_output.copy_from_device(task.shader_x, 1, task.shader_w); - } + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + float u = (x + 0.5f) / width; + float v = (y + 0.5f) / height; - d_input.free(); + KernelShaderEvalInput in; + in.object = OBJECT_NONE; + in.prim = PRIM_NONE; + in.u = u; + in.v = v; + d_input_data[x + y * width] = in; + } + } - float4 *d_output_data = d_output.data(); + return size; + }, + [&](device_vector &d_output) { + /* Copy output to pixel buffer. */ + float4 *d_output_data = d_output.data(); - pixels.resize(width * height); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - pixels[y * width + x].x = d_output_data[y * width + x].x; - pixels[y * width + x].y = d_output_data[y * width + x].y; - pixels[y * width + x].z = d_output_data[y * width + x].z; - } - } - - d_output.free(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + pixels[y * width + x].x = d_output_data[y * width + x].x; + pixels[y * width + x].y = d_output_data[y * width + x].y; + pixels[y * width + x].z = d_output_data[y * width + x].z; + } + } + }); } /* Light */ @@ -140,15 +129,16 @@ NODE_DEFINE(Light) SOCKET_BOOLEAN(cast_shadow, "Cast Shadow", true); SOCKET_BOOLEAN(use_mis, "Use Mis", false); + SOCKET_BOOLEAN(use_camera, "Use Camera", true); SOCKET_BOOLEAN(use_diffuse, "Use Diffuse", true); SOCKET_BOOLEAN(use_glossy, "Use Glossy", true); SOCKET_BOOLEAN(use_transmission, "Use Transmission", true); SOCKET_BOOLEAN(use_scatter, "Use Scatter", true); - SOCKET_INT(samples, "Samples", 1); SOCKET_INT(max_bounces, "Max Bounces", 1024); SOCKET_UINT(random_id, "Random ID", 0); + SOCKET_BOOLEAN(is_shadow_catcher, "Shadow Catcher", true); SOCKET_BOOLEAN(is_portal, "Is Portal", false); SOCKET_BOOLEAN(is_enabled, "Is Enabled", true); @@ -166,10 +156,6 @@ void Light::tag_update(Scene *scene) { if (is_modified()) { scene->light_manager->tag_update(scene, LightManager::LIGHT_MODIFIED); - - if (samples_is_modified()) { - scene->integrator->tag_update(scene, Integrator::LIGHT_SAMPLES_MODIFIED); - } } } @@ -193,7 +179,6 @@ LightManager::LightManager() { update_flags = UPDATE_ALL; need_update_background = true; - use_light_visibility = false; last_background_enabled = false; last_background_resolution = 0; } @@ -357,21 +342,23 @@ void LightManager::device_update_distribution(Device *, int object_id = j; int shader_flag = 0; + if (!(object->get_visibility() & PATH_RAY_CAMERA)) { + shader_flag |= SHADER_EXCLUDE_CAMERA; + } if (!(object->get_visibility() & PATH_RAY_DIFFUSE)) { shader_flag |= SHADER_EXCLUDE_DIFFUSE; - use_light_visibility = true; } if (!(object->get_visibility() & PATH_RAY_GLOSSY)) { shader_flag |= SHADER_EXCLUDE_GLOSSY; - use_light_visibility = true; } if (!(object->get_visibility() & PATH_RAY_TRANSMIT)) { shader_flag |= SHADER_EXCLUDE_TRANSMIT; - use_light_visibility = true; } if (!(object->get_visibility() & PATH_RAY_VOLUME_SCATTER)) { shader_flag |= SHADER_EXCLUDE_SCATTER; - use_light_visibility = true; + } + if (!(object->get_is_shadow_catcher())) { + shader_flag |= SHADER_EXCLUDE_SHADOW_CATCHER; } size_t mesh_num_triangles = mesh->num_triangles(); @@ -496,10 +483,10 @@ void LightManager::device_update_distribution(Device *, kfilm->pass_shadow_scale = 1.0f; if (kintegrator->pdf_triangles != 0.0f) - kfilm->pass_shadow_scale *= 0.5f; + kfilm->pass_shadow_scale /= 0.5f; if (num_background_lights < num_lights) - kfilm->pass_shadow_scale *= (float)(num_lights - num_background_lights) / (float)num_lights; + kfilm->pass_shadow_scale /= (float)(num_lights - num_background_lights) / (float)num_lights; /* CDF */ dscene->light_distribution.copy_to_device(); @@ -766,25 +753,26 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc if (!light->cast_shadow) shader_id &= ~SHADER_CAST_SHADOW; + if (!light->use_camera) { + shader_id |= SHADER_EXCLUDE_CAMERA; + } if (!light->use_diffuse) { shader_id |= SHADER_EXCLUDE_DIFFUSE; - use_light_visibility = true; } if (!light->use_glossy) { shader_id |= SHADER_EXCLUDE_GLOSSY; - use_light_visibility = true; } if (!light->use_transmission) { shader_id |= SHADER_EXCLUDE_TRANSMIT; - use_light_visibility = true; } if (!light->use_scatter) { shader_id |= SHADER_EXCLUDE_SCATTER; - use_light_visibility = true; + } + if (!light->is_shadow_catcher) { + shader_id |= SHADER_EXCLUDE_SHADOW_CATCHER; } klights[light_index].type = light->light_type; - klights[light_index].samples = light->samples; klights[light_index].strength[0] = light->strength.x; klights[light_index].strength[1] = light->strength.y; klights[light_index].strength[2] = light->strength.z; @@ -836,19 +824,15 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc if (!(visibility & PATH_RAY_DIFFUSE)) { shader_id |= SHADER_EXCLUDE_DIFFUSE; - use_light_visibility = true; } if (!(visibility & PATH_RAY_GLOSSY)) { shader_id |= SHADER_EXCLUDE_GLOSSY; - use_light_visibility = true; } if (!(visibility & PATH_RAY_TRANSMIT)) { shader_id |= SHADER_EXCLUDE_TRANSMIT; - use_light_visibility = true; } if (!(visibility & PATH_RAY_VOLUME_SCATTER)) { shader_id |= SHADER_EXCLUDE_SCATTER; - use_light_visibility = true; } } else if (light->light_type == LIGHT_AREA) { @@ -998,8 +982,6 @@ void LightManager::device_update(Device *device, device_free(device, dscene, need_update_background); - use_light_visibility = false; - device_update_points(device, dscene, scene); if (progress.get_cancel()) return; @@ -1018,8 +1000,6 @@ void LightManager::device_update(Device *device, if (progress.get_cancel()) return; - scene->film->set_use_light_visibility(use_light_visibility); - update_flags = UPDATE_NONE; need_update_background = false; } diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h index fbd709125ff..7f86237c8b3 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/render/light.h @@ -69,16 +69,17 @@ class Light : public Node { NODE_SOCKET_API(bool, cast_shadow) NODE_SOCKET_API(bool, use_mis) + NODE_SOCKET_API(bool, use_camera) NODE_SOCKET_API(bool, use_diffuse) NODE_SOCKET_API(bool, use_glossy) NODE_SOCKET_API(bool, use_transmission) NODE_SOCKET_API(bool, use_scatter) + NODE_SOCKET_API(bool, is_shadow_catcher) NODE_SOCKET_API(bool, is_portal) NODE_SOCKET_API(bool, is_enabled) NODE_SOCKET_API(Shader *, shader) - NODE_SOCKET_API(int, samples) NODE_SOCKET_API(int, max_bounces) NODE_SOCKET_API(uint, random_id) @@ -108,8 +109,6 @@ class LightManager { UPDATE_NONE = 0u, }; - bool use_light_visibility; - /* Need to update background (including multiple importance map) */ bool need_update_background; diff --git a/intern/cycles/render/mesh_displace.cpp b/intern/cycles/render/mesh_displace.cpp index b39d81023d9..c00c4c24211 100644 --- a/intern/cycles/render/mesh_displace.cpp +++ b/intern/cycles/render/mesh_displace.cpp @@ -16,6 +16,8 @@ #include "device/device.h" +#include "integrator/shader_eval.h" + #include "render/mesh.h" #include "render/object.h" #include "render/scene.h" @@ -43,40 +45,28 @@ static float3 compute_face_normal(const Mesh::Triangle &t, float3 *verts) return norm / normlen; } -bool GeometryManager::displace( - Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress &progress) +/* Fill in coordinates for mesh displacement shader evaluation on device. */ +static int fill_shader_input(const Scene *scene, + const Mesh *mesh, + const int object_index, + device_vector &d_input) { - /* verify if we have a displacement shader */ - if (!mesh->has_true_displacement()) { - return false; - } + int d_input_size = 0; + KernelShaderEvalInput *d_input_data = d_input.data(); - string msg = string_printf("Computing Displacement %s", mesh->name.c_str()); - progress.set_status("Updating Mesh", msg); + const array &mesh_shaders = mesh->get_shader(); + const array &mesh_used_shaders = mesh->get_used_shaders(); + const array &mesh_verts = mesh->get_verts(); - /* find object index. todo: is arbitrary */ - size_t object_index = OBJECT_NONE; - - for (size_t i = 0; i < scene->objects.size(); i++) { - if (scene->objects[i]->get_geometry() == mesh) { - object_index = i; - break; - } - } - - /* setup input for device task */ - const size_t num_verts = mesh->verts.size(); + const int num_verts = mesh_verts.size(); vector done(num_verts, false); - device_vector d_input(device, "displace_input", MEM_READ_ONLY); - uint4 *d_input_data = d_input.alloc(num_verts); - size_t d_input_size = 0; - size_t num_triangles = mesh->num_triangles(); - for (size_t i = 0; i < num_triangles; i++) { + int num_triangles = mesh->num_triangles(); + for (int i = 0; i < num_triangles; i++) { Mesh::Triangle t = mesh->get_triangle(i); - int shader_index = mesh->shader[i]; - Shader *shader = (shader_index < mesh->used_shaders.size()) ? - static_cast(mesh->used_shaders[shader_index]) : + int shader_index = mesh_shaders[i]; + Shader *shader = (shader_index < mesh_used_shaders.size()) ? + static_cast(mesh_used_shaders[shader_index]) : scene->default_surface; if (!shader->has_displacement || shader->get_displacement_method() == DISPLACE_BUMP) { @@ -110,57 +100,41 @@ bool GeometryManager::displace( } /* back */ - uint4 in = make_uint4(object, prim, __float_as_int(u), __float_as_int(v)); + KernelShaderEvalInput in; + in.object = object; + in.prim = prim; + in.u = u; + in.v = v; d_input_data[d_input_size++] = in; } } - if (d_input_size == 0) - return false; + return d_input_size; +} - /* run device task */ - device_vector d_output(device, "displace_output", MEM_READ_WRITE); - d_output.alloc(d_input_size); - d_output.zero_to_device(); - d_input.copy_to_device(); +/* Read back mesh displacement shader output. */ +static void read_shader_output(const Scene *scene, + Mesh *mesh, + const device_vector &d_output) +{ + const array &mesh_shaders = mesh->get_shader(); + const array &mesh_used_shaders = mesh->get_used_shaders(); + array &mesh_verts = mesh->get_verts(); - /* needs to be up to data for attribute access */ - device->const_copy_to("__data", &dscene->data, sizeof(dscene->data)); + const int num_verts = mesh_verts.size(); + const int num_motion_steps = mesh->get_motion_steps(); + vector done(num_verts, false); - DeviceTask task(DeviceTask::SHADER); - task.shader_input = d_input.device_pointer; - task.shader_output = d_output.device_pointer; - task.shader_eval_type = SHADER_EVAL_DISPLACE; - task.shader_x = 0; - task.shader_w = d_output.size(); - task.num_samples = 1; - task.get_cancel = function_bind(&Progress::get_cancel, &progress); - - device->task_add(task); - device->task_wait(); - - if (progress.get_cancel()) { - d_input.free(); - d_output.free(); - return false; - } - - d_output.copy_from_device(0, 1, d_output.size()); - d_input.free(); - - /* read result */ - done.clear(); - done.resize(num_verts, false); - int k = 0; - - float4 *offset = d_output.data(); + const float4 *d_output_data = d_output.data(); + int d_output_index = 0; Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - for (size_t i = 0; i < num_triangles; i++) { + int num_triangles = mesh->num_triangles(); + for (int i = 0; i < num_triangles; i++) { Mesh::Triangle t = mesh->get_triangle(i); - int shader_index = mesh->shader[i]; - Shader *shader = (shader_index < mesh->used_shaders.size()) ? - static_cast(mesh->used_shaders[shader_index]) : + int shader_index = mesh_shaders[i]; + Shader *shader = (shader_index < mesh_used_shaders.size()) ? + static_cast(mesh_used_shaders[shader_index]) : scene->default_surface; if (!shader->has_displacement || shader->get_displacement_method() == DISPLACE_BUMP) { @@ -170,12 +144,12 @@ bool GeometryManager::displace( for (int j = 0; j < 3; j++) { if (!done[t.v[j]]) { done[t.v[j]] = true; - float3 off = float4_to_float3(offset[k++]); + float3 off = float4_to_float3(d_output_data[d_output_index++]); /* Avoid illegal vertex coordinates. */ off = ensure_finite3(off); - mesh->verts[t.v[j]] += off; + mesh_verts[t.v[j]] += off; if (attr_mP != NULL) { - for (int step = 0; step < mesh->motion_steps - 1; step++) { + for (int step = 0; step < num_motion_steps - 1; step++) { float3 *mP = attr_mP->data_float3() + step * num_verts; mP[t.v[j]] += off; } @@ -183,8 +157,47 @@ bool GeometryManager::displace( } } } +} - d_output.free(); +bool GeometryManager::displace( + Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress &progress) +{ + /* verify if we have a displacement shader */ + if (!mesh->has_true_displacement()) { + return false; + } + + const size_t num_verts = mesh->verts.size(); + const size_t num_triangles = mesh->num_triangles(); + + if (num_triangles == 0) { + return false; + } + + string msg = string_printf("Computing Displacement %s", mesh->name.c_str()); + progress.set_status("Updating Mesh", msg); + + /* find object index. todo: is arbitrary */ + size_t object_index = OBJECT_NONE; + + for (size_t i = 0; i < scene->objects.size(); i++) { + if (scene->objects[i]->get_geometry() == mesh) { + object_index = i; + break; + } + } + + /* Needs to be up to data for attribute access. */ + device->const_copy_to("__data", &dscene->data, sizeof(dscene->data)); + + /* Evaluate shader on device. */ + ShaderEval shader_eval(device, progress); + if (!shader_eval.eval(SHADER_EVAL_DISPLACE, + num_verts, + function_bind(&fill_shader_input, scene, mesh, object_index, _1), + function_bind(&read_shader_output, scene, mesh, _1))) { + return false; + } /* stitch */ unordered_set stitch_keys; @@ -297,8 +310,7 @@ bool GeometryManager::displace( } /* normalize vertex normals */ - done.clear(); - done.resize(num_verts, false); + vector done(num_verts, false); for (size_t i = 0; i < num_triangles; i++) { if (tri_has_true_disp[i]) { @@ -368,8 +380,7 @@ bool GeometryManager::displace( } /* normalize vertex normals */ - done.clear(); - done.resize(num_verts, false); + vector done(num_verts, false); for (size_t i = 0; i < num_triangles; i++) { if (tri_has_true_disp[i]) { diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 795166bcf4c..5303d55242e 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -2736,18 +2736,21 @@ NODE_DEFINE(PrincipledBsdfNode) distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); static NodeEnum subsurface_method_enum; - subsurface_method_enum.insert("burley", CLOSURE_BSSRDF_PRINCIPLED_ID); - subsurface_method_enum.insert("random_walk", CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID); + subsurface_method_enum.insert("random_walk_fixed_radius", + CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); + subsurface_method_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID); SOCKET_ENUM(subsurface_method, "Subsurface Method", subsurface_method_enum, - CLOSURE_BSSRDF_PRINCIPLED_ID); + CLOSURE_BSSRDF_RANDOM_WALK_ID); SOCKET_IN_COLOR(base_color, "Base Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_COLOR(subsurface_color, "Subsurface Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(metallic, "Metallic", 0.0f); SOCKET_IN_FLOAT(subsurface, "Subsurface", 0.0f); SOCKET_IN_VECTOR(subsurface_radius, "Subsurface Radius", make_float3(0.1f, 0.1f, 0.1f)); + SOCKET_IN_FLOAT(subsurface_ior, "Subsurface IOR", 1.4f); + SOCKET_IN_FLOAT(subsurface_anisotropy, "Subsurface Anisotropy", 0.0f); SOCKET_IN_FLOAT(specular, "Specular", 0.0f); SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f); SOCKET_IN_FLOAT(specular_tint, "Specular Tint", 0.0f); @@ -2857,6 +2860,8 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler, ShaderInput *p_metallic, ShaderInput *p_subsurface, ShaderInput *p_subsurface_radius, + ShaderInput *p_subsurface_ior, + ShaderInput *p_subsurface_anisotropy, ShaderInput *p_specular, ShaderInput *p_roughness, ShaderInput *p_specular_tint, @@ -2896,6 +2901,8 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler, int transmission_roughness_offset = compiler.stack_assign(p_transmission_roughness); int anisotropic_rotation_offset = compiler.stack_assign(p_anisotropic_rotation); int subsurface_radius_offset = compiler.stack_assign(p_subsurface_radius); + int subsurface_ior_offset = compiler.stack_assign(p_subsurface_ior); + int subsurface_anisotropy_offset = compiler.stack_assign(p_subsurface_anisotropy); compiler.add_node(NODE_CLOSURE_BSDF, compiler.encode_uchar4(closure, @@ -2929,8 +2936,10 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler, __float_as_int(bc_default.y), __float_as_int(bc_default.z)); - compiler.add_node( - clearcoat_normal_offset, subsurface_radius_offset, SVM_STACK_INVALID, SVM_STACK_INVALID); + compiler.add_node(clearcoat_normal_offset, + subsurface_radius_offset, + subsurface_ior_offset, + subsurface_anisotropy_offset); float3 ss_default = get_float3(subsurface_color_in->socket_type); @@ -2953,6 +2962,8 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler) input("Metallic"), input("Subsurface"), input("Subsurface Radius"), + input("Subsurface IOR"), + input("Subsurface Anisotropy"), input("Specular"), input("Roughness"), input("Specular Tint"), @@ -3048,16 +3059,16 @@ NODE_DEFINE(SubsurfaceScatteringNode) SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); - static NodeEnum falloff_enum; - falloff_enum.insert("cubic", CLOSURE_BSSRDF_CUBIC_ID); - falloff_enum.insert("gaussian", CLOSURE_BSSRDF_GAUSSIAN_ID); - falloff_enum.insert("burley", CLOSURE_BSSRDF_BURLEY_ID); - falloff_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID); - SOCKET_ENUM(falloff, "Falloff", falloff_enum, CLOSURE_BSSRDF_BURLEY_ID); + static NodeEnum method_enum; + method_enum.insert("random_walk_fixed_radius", CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); + method_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID); + SOCKET_ENUM(method, "Method", method_enum, CLOSURE_BSSRDF_RANDOM_WALK_ID); + SOCKET_IN_FLOAT(scale, "Scale", 0.01f); SOCKET_IN_VECTOR(radius, "Radius", make_float3(0.1f, 0.1f, 0.1f)); - SOCKET_IN_FLOAT(sharpness, "Sharpness", 0.0f); - SOCKET_IN_FLOAT(texture_blur, "Texture Blur", 1.0f); + + SOCKET_IN_FLOAT(subsurface_ior, "IOR", 1.4f); + SOCKET_IN_FLOAT(subsurface_anisotropy, "Anisotropy", 0.0f); SOCKET_OUT_CLOSURE(BSSRDF, "BSSRDF"); @@ -3066,20 +3077,19 @@ NODE_DEFINE(SubsurfaceScatteringNode) SubsurfaceScatteringNode::SubsurfaceScatteringNode() : BsdfNode(get_node_type()) { - closure = falloff; + closure = method; } void SubsurfaceScatteringNode::compile(SVMCompiler &compiler) { - closure = falloff; - BsdfNode::compile( - compiler, input("Scale"), input("Texture Blur"), input("Radius"), input("Sharpness")); + closure = method; + BsdfNode::compile(compiler, input("Scale"), input("IOR"), input("Radius"), input("Anisotropy")); } void SubsurfaceScatteringNode::compile(OSLCompiler &compiler) { - closure = falloff; - compiler.parameter(this, "falloff"); + closure = method; + compiler.parameter(this, "method"); compiler.add(this, "node_subsurface_scattering"); } @@ -3786,20 +3796,6 @@ void GeometryNode::compile(OSLCompiler &compiler) compiler.add(this, "node_geometry"); } -int GeometryNode::get_group() -{ - ShaderOutput *out; - int result = ShaderNode::get_group(); - - /* Backfacing uses NODE_LIGHT_PATH */ - out = output("Backfacing"); - if (!out->links.empty()) { - result = max(result, NODE_GROUP_LEVEL_1); - } - - return result; -} - /* TextureCoordinate */ NODE_DEFINE(TextureCoordinateNode) @@ -5926,33 +5922,33 @@ NODE_DEFINE(OutputAOVNode) OutputAOVNode::OutputAOVNode() : ShaderNode(get_node_type()) { special_type = SHADER_SPECIAL_TYPE_OUTPUT_AOV; - slot = -1; + offset = -1; } void OutputAOVNode::simplify_settings(Scene *scene) { - slot = scene->film->get_aov_offset(scene, name.string(), is_color); - if (slot == -1) { - slot = scene->film->get_aov_offset(scene, name.string(), is_color); + offset = scene->film->get_aov_offset(scene, name.string(), is_color); + if (offset == -1) { + offset = scene->film->get_aov_offset(scene, name.string(), is_color); } - if (slot == -1 || is_color) { + if (offset == -1 || is_color) { input("Value")->disconnect(); } - if (slot == -1 || !is_color) { + if (offset == -1 || !is_color) { input("Color")->disconnect(); } } void OutputAOVNode::compile(SVMCompiler &compiler) { - assert(slot >= 0); + assert(offset >= 0); if (is_color) { - compiler.add_node(NODE_AOV_COLOR, compiler.stack_assign(input("Color")), slot); + compiler.add_node(NODE_AOV_COLOR, compiler.stack_assign(input("Color")), offset); } else { - compiler.add_node(NODE_AOV_VALUE, compiler.stack_assign(input("Value")), slot); + compiler.add_node(NODE_AOV_VALUE, compiler.stack_assign(input("Value")), offset); } } diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 3013e9b1866..22bdb06b059 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -143,10 +143,6 @@ class EnvironmentTextureNode : public ImageSlotTextureNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } virtual bool equals(const ShaderNode &other) { @@ -170,11 +166,6 @@ class SkyTextureNode : public TextureNode { public: SHADER_NODE_CLASS(SkyTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - NODE_SOCKET_API(NodeSkyType, sky_type) NODE_SOCKET_API(float3, sun_direction) NODE_SOCKET_API(float, turbidity) @@ -224,18 +215,13 @@ class OutputAOVNode : public ShaderNode { NODE_SOCKET_API(ustring, name) - virtual int get_group() - { - return NODE_GROUP_LEVEL_4; - } - /* Don't allow output node de-duplication. */ virtual bool equals(const ShaderNode & /*other*/) { return false; } - int slot; + int offset; bool is_color; }; @@ -243,11 +229,6 @@ class GradientTextureNode : public TextureNode { public: SHADER_NODE_CLASS(GradientTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - NODE_SOCKET_API(NodeGradientType, gradient_type) NODE_SOCKET_API(float3, vector) }; @@ -269,19 +250,14 @@ class VoronoiTextureNode : public TextureNode { public: SHADER_NODE_CLASS(VoronoiTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - virtual int get_feature() { int result = ShaderNode::get_feature(); if (dimensions == 4) { - result |= NODE_FEATURE_VORONOI_EXTRA; + result |= KERNEL_FEATURE_NODE_VORONOI_EXTRA; } else if (dimensions >= 2 && feature == NODE_VORONOI_SMOOTH_F1) { - result |= NODE_FEATURE_VORONOI_EXTRA; + result |= KERNEL_FEATURE_NODE_VORONOI_EXTRA; } return result; } @@ -301,11 +277,6 @@ class MusgraveTextureNode : public TextureNode { public: SHADER_NODE_CLASS(MusgraveTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - NODE_SOCKET_API(int, dimensions) NODE_SOCKET_API(NodeMusgraveType, musgrave_type) NODE_SOCKET_API(float, w) @@ -322,11 +293,6 @@ class WaveTextureNode : public TextureNode { public: SHADER_NODE_CLASS(WaveTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - NODE_SOCKET_API(NodeWaveType, wave_type) NODE_SOCKET_API(NodeWaveBandsDirection, bands_direction) NODE_SOCKET_API(NodeWaveRingsDirection, rings_direction) @@ -345,11 +311,6 @@ class MagicTextureNode : public TextureNode { public: SHADER_NODE_CLASS(MagicTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - NODE_SOCKET_API(int, depth) NODE_SOCKET_API(float3, vector) NODE_SOCKET_API(float, scale) @@ -364,11 +325,6 @@ class CheckerTextureNode : public TextureNode { NODE_SOCKET_API(float3, color1) NODE_SOCKET_API(float3, color2) NODE_SOCKET_API(float, scale) - - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } }; class BrickTextureNode : public TextureNode { @@ -390,20 +346,11 @@ class BrickTextureNode : public TextureNode { NODE_SOCKET_API(float, brick_width) NODE_SOCKET_API(float, row_height) NODE_SOCKET_API(float3, vector) - - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } }; class PointDensityTextureNode : public ShaderNode { public: SHADER_NODE_NO_CLONE_CLASS(PointDensityTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_4; - } ~PointDensityTextureNode(); ShaderNode *clone(ShaderGraph *graph) const; @@ -443,10 +390,6 @@ class IESLightNode : public TextureNode { ~IESLightNode(); ShaderNode *clone(ShaderGraph *graph) const; - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } NODE_SOCKET_API(ustring, filename) NODE_SOCKET_API(ustring, ies) @@ -464,10 +407,6 @@ class IESLightNode : public TextureNode { class WhiteNoiseTextureNode : public ShaderNode { public: SHADER_NODE_CLASS(WhiteNoiseTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } NODE_SOCKET_API(int, dimensions) NODE_SOCKET_API(float3, vector) @@ -477,10 +416,6 @@ class WhiteNoiseTextureNode : public ShaderNode { class MappingNode : public ShaderNode { public: SHADER_NODE_CLASS(MappingNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } void constant_fold(const ConstantFolder &folder); NODE_SOCKET_API(float3, vector) @@ -546,6 +481,11 @@ class BsdfBaseNode : public ShaderNode { return false; } + virtual int get_feature() + { + return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_BSDF; + } + protected: ClosureType closure; }; @@ -606,6 +546,8 @@ class PrincipledBsdfNode : public BsdfBaseNode { ShaderInput *metallic, ShaderInput *subsurface, ShaderInput *subsurface_radius, + ShaderInput *subsurface_ior, + ShaderInput *subsurface_anisotropy, ShaderInput *specular, ShaderInput *roughness, ShaderInput *specular_tint, @@ -622,6 +564,8 @@ class PrincipledBsdfNode : public BsdfBaseNode { NODE_SOCKET_API(float3, base_color) NODE_SOCKET_API(float3, subsurface_color) NODE_SOCKET_API(float3, subsurface_radius) + NODE_SOCKET_API(float, subsurface_ior) + NODE_SOCKET_API(float, subsurface_anisotropy) NODE_SOCKET_API(float, metallic) NODE_SOCKET_API(float, subsurface) NODE_SOCKET_API(float, specular) @@ -758,14 +702,14 @@ class SubsurfaceScatteringNode : public BsdfNode { bool has_bssrdf_bump(); ClosureType get_closure_type() { - return falloff; + return method; } NODE_SOCKET_API(float, scale) NODE_SOCKET_API(float3, radius) - NODE_SOCKET_API(float, sharpness) - NODE_SOCKET_API(float, texture_blur) - NODE_SOCKET_API(ClosureType, falloff) + NODE_SOCKET_API(float, subsurface_ior) + NODE_SOCKET_API(float, subsurface_anisotropy) + NODE_SOCKET_API(ClosureType, method) }; class EmissionNode : public ShaderNode { @@ -782,6 +726,11 @@ class EmissionNode : public ShaderNode { return true; } + virtual int get_feature() + { + return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_EMISSION; + } + NODE_SOCKET_API(float3, color) NODE_SOCKET_API(float, strength) NODE_SOCKET_API(float, surface_mix_weight) @@ -792,6 +741,11 @@ class BackgroundNode : public ShaderNode { SHADER_NODE_CLASS(BackgroundNode) void constant_fold(const ConstantFolder &folder); + virtual int get_feature() + { + return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_EMISSION; + } + NODE_SOCKET_API(float3, color) NODE_SOCKET_API(float, strength) NODE_SOCKET_API(float, surface_mix_weight) @@ -800,10 +754,6 @@ class BackgroundNode : public ShaderNode { class HoldoutNode : public ShaderNode { public: SHADER_NODE_CLASS(HoldoutNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } virtual ClosureType get_closure_type() { return CLOSURE_HOLDOUT_ID; @@ -821,13 +771,9 @@ class AmbientOcclusionNode : public ShaderNode { { return true; } - virtual int get_group() + virtual int get_feature() { - return NODE_GROUP_LEVEL_3; - } - virtual bool has_raytrace() - { - return true; + return KERNEL_FEATURE_NODE_RAYTRACE; } NODE_SOCKET_API(float3, color) @@ -845,13 +791,9 @@ class VolumeNode : public ShaderNode { SHADER_NODE_BASE_CLASS(VolumeNode) void compile(SVMCompiler &compiler, ShaderInput *param1, ShaderInput *param2); - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } virtual int get_feature() { - return ShaderNode::get_feature() | NODE_FEATURE_VOLUME; + return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_VOLUME; } virtual ClosureType get_closure_type() { @@ -1013,10 +955,6 @@ class UVMapNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API(ustring, attribute) NODE_SOCKET_API(bool, from_dupli) @@ -1025,10 +963,6 @@ class UVMapNode : public ShaderNode { class LightPathNode : public ShaderNode { public: SHADER_NODE_CLASS(LightPathNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } }; class LightFalloffNode : public ShaderNode { @@ -1038,10 +972,6 @@ class LightFalloffNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } NODE_SOCKET_API(float, strength) NODE_SOCKET_API(float, smooth) @@ -1050,10 +980,6 @@ class LightFalloffNode : public ShaderNode { class ObjectInfoNode : public ShaderNode { public: SHADER_NODE_CLASS(ObjectInfoNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } }; class ParticleInfoNode : public ShaderNode { @@ -1064,10 +990,6 @@ class ParticleInfoNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } }; class HairInfoNode : public ShaderNode { @@ -1083,13 +1005,9 @@ class HairInfoNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } virtual int get_feature() { - return ShaderNode::get_feature() | NODE_FEATURE_HAIR; + return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_HAIR; } }; @@ -1168,10 +1086,6 @@ class InvertNode : public ShaderNode { public: SHADER_NODE_CLASS(InvertNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, fac) NODE_SOCKET_API(float3, color) @@ -1182,11 +1096,6 @@ class MixNode : public ShaderNode { SHADER_NODE_CLASS(MixNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } - NODE_SOCKET_API(NodeMix, mix_type) NODE_SOCKET_API(bool, use_clamp) NODE_SOCKET_API(float3, color1) @@ -1198,10 +1107,6 @@ class CombineRGBNode : public ShaderNode { public: SHADER_NODE_CLASS(CombineRGBNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, r) NODE_SOCKET_API(float, g) @@ -1212,10 +1117,6 @@ class CombineHSVNode : public ShaderNode { public: SHADER_NODE_CLASS(CombineHSVNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, h) NODE_SOCKET_API(float, s) @@ -1226,10 +1127,6 @@ class CombineXYZNode : public ShaderNode { public: SHADER_NODE_CLASS(CombineXYZNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, x) NODE_SOCKET_API(float, y) @@ -1240,10 +1137,6 @@ class GammaNode : public ShaderNode { public: SHADER_NODE_CLASS(GammaNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API(float3, color) NODE_SOCKET_API(float, gamma) @@ -1253,10 +1146,6 @@ class BrightContrastNode : public ShaderNode { public: SHADER_NODE_CLASS(BrightContrastNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API(float3, color) NODE_SOCKET_API(float, bright) @@ -1267,10 +1156,6 @@ class SeparateRGBNode : public ShaderNode { public: SHADER_NODE_CLASS(SeparateRGBNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float3, color) }; @@ -1279,10 +1164,6 @@ class SeparateHSVNode : public ShaderNode { public: SHADER_NODE_CLASS(SeparateHSVNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float3, color) }; @@ -1291,10 +1172,6 @@ class SeparateXYZNode : public ShaderNode { public: SHADER_NODE_CLASS(SeparateXYZNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float3, vector) }; @@ -1333,10 +1210,6 @@ class CameraNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } }; class FresnelNode : public ShaderNode { @@ -1346,10 +1219,6 @@ class FresnelNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API(float3, normal) NODE_SOCKET_API(float, IOR) @@ -1362,10 +1231,6 @@ class LayerWeightNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API(float3, normal) NODE_SOCKET_API(float, blend) @@ -1378,10 +1243,6 @@ class WireframeNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, size) NODE_SOCKET_API(bool, use_pixel_size) @@ -1390,10 +1251,6 @@ class WireframeNode : public ShaderNode { class WavelengthNode : public ShaderNode { public: SHADER_NODE_CLASS(WavelengthNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, wavelength) }; @@ -1402,10 +1259,6 @@ class BlackbodyNode : public ShaderNode { public: SHADER_NODE_CLASS(BlackbodyNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, temperature) }; @@ -1413,10 +1266,6 @@ class BlackbodyNode : public ShaderNode { class MapRangeNode : public ShaderNode { public: SHADER_NODE_CLASS(MapRangeNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } void expand(ShaderGraph *graph); NODE_SOCKET_API(float, value) @@ -1433,10 +1282,6 @@ class ClampNode : public ShaderNode { public: SHADER_NODE_CLASS(ClampNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, value) NODE_SOCKET_API(float, min) NODE_SOCKET_API(float, max) @@ -1446,10 +1291,6 @@ class ClampNode : public ShaderNode { class MathNode : public ShaderNode { public: SHADER_NODE_CLASS(MathNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } void expand(ShaderGraph *graph); void constant_fold(const ConstantFolder &folder); @@ -1463,10 +1304,6 @@ class MathNode : public ShaderNode { class NormalNode : public ShaderNode { public: SHADER_NODE_CLASS(NormalNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } NODE_SOCKET_API(float3, direction) NODE_SOCKET_API(float3, normal) @@ -1475,10 +1312,6 @@ class NormalNode : public ShaderNode { class VectorMathNode : public ShaderNode { public: SHADER_NODE_CLASS(VectorMathNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } void constant_fold(const ConstantFolder &folder); NODE_SOCKET_API(float3, vector1) @@ -1492,10 +1325,6 @@ class VectorRotateNode : public ShaderNode { public: SHADER_NODE_CLASS(VectorRotateNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(NodeVectorRotateType, rotate_type) NODE_SOCKET_API(bool, invert) NODE_SOCKET_API(float3, vector) @@ -1509,11 +1338,6 @@ class VectorTransformNode : public ShaderNode { public: SHADER_NODE_CLASS(VectorTransformNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } - NODE_SOCKET_API(NodeVectorTransformType, transform_type) NODE_SOCKET_API(NodeVectorTransformConvertSpace, convert_from) NODE_SOCKET_API(NodeVectorTransformConvertSpace, convert_to) @@ -1530,7 +1354,7 @@ class BumpNode : public ShaderNode { } virtual int get_feature() { - return NODE_FEATURE_BUMP; + return KERNEL_FEATURE_NODE_BUMP; } NODE_SOCKET_API(bool, invert) @@ -1549,11 +1373,6 @@ class CurvesNode : public ShaderNode { explicit CurvesNode(const NodeType *node_type); SHADER_NODE_BASE_CLASS(CurvesNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } - NODE_SOCKET_API_ARRAY(array, curves) NODE_SOCKET_API(float, min_x) NODE_SOCKET_API(float, max_x) @@ -1583,10 +1402,6 @@ class RGBRampNode : public ShaderNode { public: SHADER_NODE_CLASS(RGBRampNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API_ARRAY(array, ramp) NODE_SOCKET_API_ARRAY(array, ramp_alpha) @@ -1656,10 +1471,6 @@ class NormalMapNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(NodeNormalMapSpace, space) NODE_SOCKET_API(ustring, attribute) @@ -1680,10 +1491,6 @@ class TangentNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(NodeTangentDirectionType, direction_type) NODE_SOCKET_API(NodeTangentAxis, axis) @@ -1698,13 +1505,9 @@ class BevelNode : public ShaderNode { { return true; } - virtual int get_group() + virtual int get_feature() { - return NODE_GROUP_LEVEL_3; - } - virtual bool has_raytrace() - { - return true; + return KERNEL_FEATURE_NODE_RAYTRACE; } NODE_SOCKET_API(float, radius) @@ -1718,7 +1521,7 @@ class DisplacementNode : public ShaderNode { void constant_fold(const ConstantFolder &folder); virtual int get_feature() { - return NODE_FEATURE_BUMP; + return KERNEL_FEATURE_NODE_BUMP; } NODE_SOCKET_API(NodeNormalMapSpace, space) @@ -1739,7 +1542,7 @@ class VectorDisplacementNode : public ShaderNode { void constant_fold(const ConstantFolder &folder); virtual int get_feature() { - return NODE_FEATURE_BUMP; + return KERNEL_FEATURE_NODE_BUMP; } NODE_SOCKET_API(NodeNormalMapSpace, space) diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index c88d94fe4c2..4637f8fe989 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -216,6 +216,10 @@ void Object::tag_update(Scene *scene) if (use_holdout_is_modified()) { flag |= ObjectManager::HOLDOUT_MODIFIED; } + + if (is_shadow_catcher_is_modified()) { + scene->tag_shadow_catcher_modified(); + } } if (geometry) { @@ -273,14 +277,7 @@ bool Object::is_traceable() const uint Object::visibility_for_tracing() const { - uint trace_visibility = visibility; - if (is_shadow_catcher) { - trace_visibility &= ~PATH_RAY_SHADOW_NON_CATCHER; - } - else { - trace_visibility &= ~PATH_RAY_SHADOW_CATCHER; - } - return trace_visibility; + return SHADOW_CATCHER_OBJECT_VISIBILITY(is_shadow_catcher, visibility & PATH_RAY_ALL_VISIBILITY); } float Object::compute_volume_step_size() const @@ -680,7 +677,7 @@ void ObjectManager::device_update(Device *device, /* prepare for static BVH building */ /* todo: do before to support getting object level coords? */ - if (scene->params.bvh_type == SceneParams::BVH_STATIC) { + if (scene->params.bvh_type == BVH_TYPE_STATIC) { scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { scene->update_stats->object.times.add_entry( @@ -932,6 +929,11 @@ void ObjectManager::tag_update(Scene *scene, uint32_t flag) } scene->light_manager->tag_update(scene, LightManager::OBJECT_MANAGER); + + /* Integrator's shadow catcher settings depends on object visibility settings. */ + if (flag & (OBJECT_ADDED | OBJECT_REMOVED | OBJECT_MODIFIED)) { + scene->integrator->tag_update(scene, Integrator::OBJECT_MANAGER); + } } bool ObjectManager::need_update() const diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 7dc79f48145..d28b222c10e 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -113,7 +113,7 @@ void OSLShaderManager::device_update_specific(Device *device, scene->image_manager->set_osl_texture_system((void *)ts); /* create shaders */ - OSLGlobals *og = (OSLGlobals *)device->osl_memory(); + OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); Shader *background_shader = scene->background->get_shader(scene); foreach (Shader *shader, scene->shaders) { @@ -174,7 +174,7 @@ void OSLShaderManager::device_update_specific(Device *device, void OSLShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *scene) { - OSLGlobals *og = (OSLGlobals *)device->osl_memory(); + OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); device_free_common(device, dscene, scene); @@ -257,25 +257,36 @@ void OSLShaderManager::shading_system_init() /* our own ray types */ static const char *raytypes[] = { - "camera", /* PATH_RAY_CAMERA */ - "reflection", /* PATH_RAY_REFLECT */ - "refraction", /* PATH_RAY_TRANSMIT */ - "diffuse", /* PATH_RAY_DIFFUSE */ - "glossy", /* PATH_RAY_GLOSSY */ - "singular", /* PATH_RAY_SINGULAR */ - "transparent", /* PATH_RAY_TRANSPARENT */ + "camera", /* PATH_RAY_CAMERA */ + "reflection", /* PATH_RAY_REFLECT */ + "refraction", /* PATH_RAY_TRANSMIT */ + "diffuse", /* PATH_RAY_DIFFUSE */ + "glossy", /* PATH_RAY_GLOSSY */ + "singular", /* PATH_RAY_SINGULAR */ + "transparent", /* PATH_RAY_TRANSPARENT */ + "volume_scatter", /* PATH_RAY_VOLUME_SCATTER */ - "shadow", /* PATH_RAY_SHADOW_OPAQUE_NON_CATCHER */ - "shadow", /* PATH_RAY_SHADOW_OPAQUE_CATCHER */ - "shadow", /* PATH_RAY_SHADOW_TRANSPARENT_NON_CATCHER */ - "shadow", /* PATH_RAY_SHADOW_TRANSPARENT_CATCHER */ + "shadow", /* PATH_RAY_SHADOW_OPAQUE */ + "shadow", /* PATH_RAY_SHADOW_TRANSPARENT */ - "__unused__", "volume_scatter", /* PATH_RAY_VOLUME_SCATTER */ - "__unused__", + "__unused__", /* PATH_RAY_NODE_UNALIGNED */ + "__unused__", /* PATH_RAY_MIS_SKIP */ - "__unused__", "diffuse_ancestor", /* PATH_RAY_DIFFUSE_ANCESTOR */ - "__unused__", "__unused__", "__unused__", "__unused__", - "__unused__", "__unused__", "__unused__", + "diffuse_ancestor", /* PATH_RAY_DIFFUSE_ANCESTOR */ + + "__unused__", /* PATH_RAY_SINGLE_PASS_DONE */ + "__unused__", /* PATH_RAY_TRANSPARENT_BACKGROUND */ + "__unused__", /* PATH_RAY_TERMINATE_IMMEDIATE */ + "__unused__", /* PATH_RAY_TERMINATE_AFTER_TRANSPARENT */ + "__unused__", /* PATH_RAY_EMISSION */ + "__unused__", /* PATH_RAY_SUBSURFACE */ + "__unused__", /* PATH_RAY_DENOISING_FEATURES */ + "__unused__", /* PATH_RAY_REFLECT_PASS */ + "__unused__", /* PATH_RAY_TRANSMISSION_PASS */ + "__unused__", /* PATH_RAY_VOLUME_PASS */ + "__unused__", /* PATH_RAY_SHADOW_FOR_LIGHT */ + "__unused__", /* PATH_RAY_SHADOW_CATCHER_HIT */ + "__unused__", /* PATH_RAY_SHADOW_CATCHER_PASS */ }; const int nraytypes = sizeof(raytypes) / sizeof(raytypes[0]); @@ -758,7 +769,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath) current_shader->has_surface_bssrdf = true; current_shader->has_bssrdf_bump = true; /* can't detect yet */ } - current_shader->has_bump = true; /* can't detect yet */ + current_shader->has_bump = true; /* can't detect yet */ + current_shader->has_surface_raytrace = true; /* can't detect yet */ } if (node->has_spatial_varying()) { @@ -1054,6 +1066,8 @@ void OSLCompiler::generate_nodes(const ShaderNodeSet &nodes) current_shader->has_surface_emission = true; if (node->has_surface_transparent()) current_shader->has_surface_transparent = true; + if (node->get_feature() & KERNEL_FEATURE_NODE_RAYTRACE) + current_shader->has_surface_raytrace = true; if (node->has_spatial_varying()) current_shader->has_surface_spatial_varying = true; if (node->has_surface_bssrdf()) { diff --git a/intern/cycles/render/pass.cpp b/intern/cycles/render/pass.cpp new file mode 100644 index 00000000000..27ad7c0db97 --- /dev/null +++ b/intern/cycles/render/pass.cpp @@ -0,0 +1,427 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "render/pass.h" + +#include "util/util_algorithm.h" +#include "util/util_logging.h" + +CCL_NAMESPACE_BEGIN + +const char *pass_type_as_string(const PassType type) +{ + const int type_int = static_cast(type); + + const NodeEnum *type_enum = Pass::get_type_enum(); + + if (!type_enum->exists(type_int)) { + LOG(DFATAL) << "Unhandled pass type " << static_cast(type) << ", not supposed to happen."; + return "UNKNOWN"; + } + + return (*type_enum)[type_int].c_str(); +} + +const char *pass_mode_as_string(PassMode mode) +{ + switch (mode) { + case PassMode::NOISY: + return "NOISY"; + case PassMode::DENOISED: + return "DENOISED"; + } + + LOG(DFATAL) << "Unhandled pass mode " << static_cast(mode) << ", should never happen."; + return "UNKNOWN"; +} + +std::ostream &operator<<(std::ostream &os, PassMode mode) +{ + os << pass_mode_as_string(mode); + return os; +} + +const NodeEnum *Pass::get_type_enum() +{ + static NodeEnum pass_type_enum; + + if (pass_type_enum.empty()) { + + /* Light Passes. */ + pass_type_enum.insert("combined", PASS_COMBINED); + pass_type_enum.insert("emission", PASS_EMISSION); + pass_type_enum.insert("background", PASS_BACKGROUND); + pass_type_enum.insert("ao", PASS_AO); + pass_type_enum.insert("shadow", PASS_SHADOW); + pass_type_enum.insert("diffuse", PASS_DIFFUSE); + pass_type_enum.insert("diffuse_direct", PASS_DIFFUSE_DIRECT); + pass_type_enum.insert("diffuse_indirect", PASS_DIFFUSE_INDIRECT); + pass_type_enum.insert("glossy", PASS_GLOSSY); + pass_type_enum.insert("glossy_direct", PASS_GLOSSY_DIRECT); + pass_type_enum.insert("glossy_indirect", PASS_GLOSSY_INDIRECT); + pass_type_enum.insert("transmission", PASS_TRANSMISSION); + pass_type_enum.insert("transmission_direct", PASS_TRANSMISSION_DIRECT); + pass_type_enum.insert("transmission_indirect", PASS_TRANSMISSION_INDIRECT); + pass_type_enum.insert("volume", PASS_VOLUME); + pass_type_enum.insert("volume_direct", PASS_VOLUME_DIRECT); + pass_type_enum.insert("volume_indirect", PASS_VOLUME_INDIRECT); + + /* Data passes. */ + pass_type_enum.insert("depth", PASS_DEPTH); + pass_type_enum.insert("position", PASS_POSITION); + pass_type_enum.insert("normal", PASS_NORMAL); + pass_type_enum.insert("roughness", PASS_ROUGHNESS); + pass_type_enum.insert("uv", PASS_UV); + pass_type_enum.insert("object_id", PASS_OBJECT_ID); + pass_type_enum.insert("material_id", PASS_MATERIAL_ID); + pass_type_enum.insert("motion", PASS_MOTION); + pass_type_enum.insert("motion_weight", PASS_MOTION_WEIGHT); + pass_type_enum.insert("render_time", PASS_RENDER_TIME); + pass_type_enum.insert("cryptomatte", PASS_CRYPTOMATTE); + pass_type_enum.insert("aov_color", PASS_AOV_COLOR); + pass_type_enum.insert("aov_value", PASS_AOV_VALUE); + pass_type_enum.insert("adaptive_aux_buffer", PASS_ADAPTIVE_AUX_BUFFER); + pass_type_enum.insert("sample_count", PASS_SAMPLE_COUNT); + pass_type_enum.insert("diffuse_color", PASS_DIFFUSE_COLOR); + pass_type_enum.insert("glossy_color", PASS_GLOSSY_COLOR); + pass_type_enum.insert("transmission_color", PASS_TRANSMISSION_COLOR); + pass_type_enum.insert("mist", PASS_MIST); + pass_type_enum.insert("denoising_normal", PASS_DENOISING_NORMAL); + pass_type_enum.insert("denoising_albedo", PASS_DENOISING_ALBEDO); + + pass_type_enum.insert("shadow_catcher", PASS_SHADOW_CATCHER); + pass_type_enum.insert("shadow_catcher_sample_count", PASS_SHADOW_CATCHER_SAMPLE_COUNT); + pass_type_enum.insert("shadow_catcher_matte", PASS_SHADOW_CATCHER_MATTE); + + pass_type_enum.insert("bake_primitive", PASS_BAKE_PRIMITIVE); + pass_type_enum.insert("bake_differential", PASS_BAKE_DIFFERENTIAL); + } + + return &pass_type_enum; +} + +const NodeEnum *Pass::get_mode_enum() +{ + static NodeEnum pass_mode_enum; + + if (pass_mode_enum.empty()) { + pass_mode_enum.insert("noisy", static_cast(PassMode::NOISY)); + pass_mode_enum.insert("denoised", static_cast(PassMode::DENOISED)); + } + + return &pass_mode_enum; +} + +NODE_DEFINE(Pass) +{ + NodeType *type = NodeType::add("pass", create); + + const NodeEnum *pass_type_enum = get_type_enum(); + const NodeEnum *pass_mode_enum = get_mode_enum(); + + SOCKET_ENUM(type, "Type", *pass_type_enum, PASS_COMBINED); + SOCKET_ENUM(mode, "Mode", *pass_mode_enum, static_cast(PassMode::DENOISED)); + SOCKET_STRING(name, "Name", ustring()); + SOCKET_BOOLEAN(include_albedo, "Include Albedo", false); + + return type; +} + +Pass::Pass() : Node(get_node_type()), is_auto_(false) +{ +} + +PassInfo Pass::get_info() const +{ + return get_info(type, include_albedo); +} + +bool Pass::is_written() const +{ + return get_info().is_written; +} + +PassInfo Pass::get_info(const PassType type, const bool include_albedo) +{ + PassInfo pass_info; + + pass_info.use_filter = true; + pass_info.use_exposure = false; + pass_info.divide_type = PASS_NONE; + pass_info.use_compositing = false; + pass_info.use_denoising_albedo = true; + + switch (type) { + case PASS_NONE: + pass_info.num_components = 0; + break; + case PASS_COMBINED: + pass_info.num_components = 4; + pass_info.use_exposure = true; + pass_info.support_denoise = true; + break; + case PASS_DEPTH: + pass_info.num_components = 1; + pass_info.use_filter = false; + break; + case PASS_MIST: + pass_info.num_components = 1; + break; + case PASS_POSITION: + pass_info.num_components = 3; + break; + case PASS_NORMAL: + pass_info.num_components = 3; + break; + case PASS_ROUGHNESS: + pass_info.num_components = 1; + break; + case PASS_UV: + pass_info.num_components = 3; + break; + case PASS_MOTION: + pass_info.num_components = 4; + pass_info.divide_type = PASS_MOTION_WEIGHT; + break; + case PASS_MOTION_WEIGHT: + pass_info.num_components = 1; + break; + case PASS_OBJECT_ID: + case PASS_MATERIAL_ID: + pass_info.num_components = 1; + pass_info.use_filter = false; + break; + + case PASS_EMISSION: + case PASS_BACKGROUND: + pass_info.num_components = 3; + pass_info.use_exposure = true; + break; + case PASS_AO: + pass_info.num_components = 3; + break; + case PASS_SHADOW: + pass_info.num_components = 3; + pass_info.use_exposure = false; + break; + case PASS_RENDER_TIME: + /* This pass is handled entirely on the host side. */ + pass_info.num_components = 0; + break; + + case PASS_DIFFUSE_COLOR: + case PASS_GLOSSY_COLOR: + case PASS_TRANSMISSION_COLOR: + pass_info.num_components = 3; + break; + case PASS_DIFFUSE: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.direct_type = PASS_DIFFUSE_DIRECT; + pass_info.indirect_type = PASS_DIFFUSE_INDIRECT; + pass_info.divide_type = (!include_albedo) ? PASS_DIFFUSE_COLOR : PASS_NONE; + pass_info.use_compositing = true; + pass_info.is_written = false; + break; + case PASS_DIFFUSE_DIRECT: + case PASS_DIFFUSE_INDIRECT: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.divide_type = (!include_albedo) ? PASS_DIFFUSE_COLOR : PASS_NONE; + pass_info.use_compositing = true; + break; + case PASS_GLOSSY: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.direct_type = PASS_GLOSSY_DIRECT; + pass_info.indirect_type = PASS_GLOSSY_INDIRECT; + pass_info.divide_type = (!include_albedo) ? PASS_GLOSSY_COLOR : PASS_NONE; + pass_info.use_compositing = true; + pass_info.is_written = false; + break; + case PASS_GLOSSY_DIRECT: + case PASS_GLOSSY_INDIRECT: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.divide_type = (!include_albedo) ? PASS_GLOSSY_COLOR : PASS_NONE; + pass_info.use_compositing = true; + break; + case PASS_TRANSMISSION: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.direct_type = PASS_TRANSMISSION_DIRECT; + pass_info.indirect_type = PASS_TRANSMISSION_INDIRECT; + pass_info.divide_type = (!include_albedo) ? PASS_TRANSMISSION_COLOR : PASS_NONE; + pass_info.use_compositing = true; + pass_info.is_written = false; + break; + case PASS_TRANSMISSION_DIRECT: + case PASS_TRANSMISSION_INDIRECT: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.divide_type = (!include_albedo) ? PASS_TRANSMISSION_COLOR : PASS_NONE; + pass_info.use_compositing = true; + break; + case PASS_VOLUME: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.direct_type = PASS_VOLUME_DIRECT; + pass_info.indirect_type = PASS_VOLUME_INDIRECT; + pass_info.use_compositing = true; + pass_info.is_written = false; + break; + case PASS_VOLUME_DIRECT: + case PASS_VOLUME_INDIRECT: + pass_info.num_components = 3; + pass_info.use_exposure = true; + break; + + case PASS_CRYPTOMATTE: + pass_info.num_components = 4; + break; + + case PASS_DENOISING_NORMAL: + pass_info.num_components = 3; + break; + case PASS_DENOISING_ALBEDO: + pass_info.num_components = 3; + break; + + case PASS_SHADOW_CATCHER: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.use_compositing = true; + pass_info.use_denoising_albedo = false; + pass_info.support_denoise = true; + break; + case PASS_SHADOW_CATCHER_SAMPLE_COUNT: + pass_info.num_components = 1; + break; + case PASS_SHADOW_CATCHER_MATTE: + pass_info.num_components = 4; + pass_info.use_exposure = true; + pass_info.support_denoise = true; + /* Without shadow catcher approximation compositing is not needed. + * Since we don't know here whether approximation is used or not, leave the decision up to + * the caller which will know that. */ + break; + + case PASS_ADAPTIVE_AUX_BUFFER: + pass_info.num_components = 4; + break; + case PASS_SAMPLE_COUNT: + pass_info.num_components = 1; + pass_info.use_exposure = false; + break; + + case PASS_AOV_COLOR: + pass_info.num_components = 3; + break; + case PASS_AOV_VALUE: + pass_info.num_components = 1; + break; + + case PASS_BAKE_PRIMITIVE: + case PASS_BAKE_DIFFERENTIAL: + pass_info.num_components = 4; + pass_info.use_exposure = false; + pass_info.use_filter = false; + break; + + case PASS_CATEGORY_LIGHT_END: + case PASS_CATEGORY_DATA_END: + case PASS_CATEGORY_BAKE_END: + case PASS_NUM: + LOG(DFATAL) << "Unexpected pass type is used " << type; + pass_info.num_components = 0; + break; + } + + return pass_info; +} + +bool Pass::contains(const vector &passes, PassType type) +{ + for (const Pass *pass : passes) { + if (pass->get_type() != type) { + continue; + } + + return true; + } + + return false; +} + +const Pass *Pass::find(const vector &passes, const string &name) +{ + for (const Pass *pass : passes) { + if (pass->get_name() == name) { + return pass; + } + } + + return nullptr; +} + +const Pass *Pass::find(const vector &passes, PassType type, PassMode mode) +{ + for (const Pass *pass : passes) { + if (pass->get_type() != type || pass->get_mode() != mode) { + continue; + } + + return pass; + } + + return nullptr; +} + +int Pass::get_offset(const vector &passes, const Pass *pass) +{ + int pass_offset = 0; + + for (const Pass *current_pass : passes) { + /* Note that pass name is allowed to be empty. This is why we check for type and mode. */ + if (current_pass->get_type() == pass->get_type() && + current_pass->get_mode() == pass->get_mode() && + current_pass->get_name() == pass->get_name()) { + if (current_pass->is_written()) { + return pass_offset; + } + else { + return PASS_UNUSED; + } + } + if (current_pass->is_written()) { + pass_offset += current_pass->get_info().num_components; + } + } + + return PASS_UNUSED; +} + +std::ostream &operator<<(std::ostream &os, const Pass &pass) +{ + os << "type: " << pass_type_as_string(pass.get_type()); + os << ", name: \"" << pass.get_name() << "\""; + os << ", mode: " << pass.get_mode(); + os << ", is_written: " << string_from_bool(pass.is_written()); + + return os; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/pass.h b/intern/cycles/render/pass.h new file mode 100644 index 00000000000..82230c62cb0 --- /dev/null +++ b/intern/cycles/render/pass.h @@ -0,0 +1,106 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include // NOLINT + +#include "util/util_string.h" +#include "util/util_vector.h" + +#include "kernel/kernel_types.h" + +#include "graph/node.h" + +CCL_NAMESPACE_BEGIN + +const char *pass_type_as_string(const PassType type); + +enum class PassMode { + NOISY, + DENOISED, +}; +const char *pass_mode_as_string(PassMode mode); +std::ostream &operator<<(std::ostream &os, PassMode mode); + +struct PassInfo { + int num_components = -1; + bool use_filter = false; + bool use_exposure = false; + bool is_written = true; + PassType divide_type = PASS_NONE; + PassType direct_type = PASS_NONE; + PassType indirect_type = PASS_NONE; + + /* Pass access for read can not happen directly and needs some sort of compositing (for example, + * light passes due to divide_type, or shadow catcher pass. */ + bool use_compositing = false; + + /* Used to disable albedo pass for denoising. + * Light and shadow catcher passes should not have discontinuity in the denoised result based on + * the underlying albedo. */ + bool use_denoising_albedo = true; + + /* Pass supports denoising. */ + bool support_denoise = false; +}; + +class Pass : public Node { + public: + NODE_DECLARE + + NODE_SOCKET_API(PassType, type) + NODE_SOCKET_API(PassMode, mode) + NODE_SOCKET_API(ustring, name) + NODE_SOCKET_API(bool, include_albedo) + + Pass(); + + PassInfo get_info() const; + + /* The pass is written by the render pipeline (kernel or denoiser). If the pass is written it + * will have pixels allocated in a RenderBuffer. Passes which are not written do not have their + * pixels allocated to save memory. */ + bool is_written() const; + + protected: + /* The has been created automatically as a requirement to various rendering functionality (such + * as adaptive sampling). */ + bool is_auto_; + + public: + static const NodeEnum *get_type_enum(); + static const NodeEnum *get_mode_enum(); + + static PassInfo get_info(PassType type, const bool include_albedo = false); + + static bool contains(const vector &passes, PassType type); + + /* Returns nullptr if there is no pass with the given name or type+mode. */ + static const Pass *find(const vector &passes, const string &name); + static const Pass *find(const vector &passes, + PassType type, + PassMode mode = PassMode::NOISY); + + /* Returns PASS_UNUSED if there is no corresponding pass. */ + static int get_offset(const vector &passes, const Pass *pass); + + friend class Film; +}; + +std::ostream &operator<<(std::ostream &os, const Pass &pass); + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index c4e7d2c79d6..a4b030190dc 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -163,12 +163,15 @@ void Scene::free_memory(bool final) delete p; foreach (Light *l, lights) delete l; + foreach (Pass *p, passes) + delete p; geometry.clear(); objects.clear(); lights.clear(); particle_systems.clear(); procedurals.clear(); + passes.clear(); if (device) { camera->device_free(device, &dscene, this); @@ -253,7 +256,6 @@ void Scene::device_update(Device *device_, Progress &progress) * - Camera may be used for adaptive subdivision. * - Displacement shader must have all shader data available. * - Light manager needs lookup tables and final mesh data to compute emission CDF. - * - Film needs light manager to run for use_light_visibility * - Lookup tables are done a second time to handle film tables */ @@ -469,88 +471,110 @@ void Scene::enable_update_stats() } } -DeviceRequestedFeatures Scene::get_requested_device_features() +void Scene::update_kernel_features() { - DeviceRequestedFeatures requested_features; + if (!need_update()) { + return; + } - shader_manager->get_requested_features(this, &requested_features); + /* These features are not being tweaked as often as shaders, + * so could be done selective magic for the viewport as well. */ + uint kernel_features = shader_manager->get_kernel_features(this); - /* This features are not being tweaked as often as shaders, - * so could be done selective magic for the viewport as well. - */ bool use_motion = need_motion() == Scene::MotionType::MOTION_BLUR; - requested_features.use_hair = false; - requested_features.use_hair_thick = (params.hair_shape == CURVE_THICK); - requested_features.use_object_motion = false; - requested_features.use_camera_motion = use_motion && camera->use_motion(); + kernel_features |= KERNEL_FEATURE_PATH_TRACING; + if (params.hair_shape == CURVE_THICK) { + kernel_features |= KERNEL_FEATURE_HAIR_THICK; + } + if (use_motion && camera->use_motion()) { + kernel_features |= KERNEL_FEATURE_CAMERA_MOTION; + } foreach (Object *object, objects) { Geometry *geom = object->get_geometry(); if (use_motion) { - requested_features.use_object_motion |= object->use_motion() | geom->get_use_motion_blur(); - requested_features.use_camera_motion |= geom->get_use_motion_blur(); + if (object->use_motion() || geom->get_use_motion_blur()) { + kernel_features |= KERNEL_FEATURE_OBJECT_MOTION; + } + if (geom->get_use_motion_blur()) { + kernel_features |= KERNEL_FEATURE_CAMERA_MOTION; + } } if (object->get_is_shadow_catcher()) { - requested_features.use_shadow_tricks = true; + kernel_features |= KERNEL_FEATURE_SHADOW_CATCHER; } if (geom->is_mesh()) { Mesh *mesh = static_cast(geom); #ifdef WITH_OPENSUBDIV if (mesh->get_subdivision_type() != Mesh::SUBDIVISION_NONE) { - requested_features.use_patch_evaluation = true; + kernel_features |= KERNEL_FEATURE_PATCH_EVALUATION; } #endif - requested_features.use_true_displacement |= mesh->has_true_displacement(); } else if (geom->is_hair()) { - requested_features.use_hair = true; + kernel_features |= KERNEL_FEATURE_HAIR; } } - requested_features.use_background_light = light_manager->has_background_light(this); - - requested_features.use_baking = bake_manager->get_baking(); - requested_features.use_integrator_branched = (integrator->get_method() == - Integrator::BRANCHED_PATH); - if (film->get_denoising_data_pass()) { - requested_features.use_denoising = true; - requested_features.use_shadow_tricks = true; + if (bake_manager->get_baking()) { + kernel_features |= KERNEL_FEATURE_BAKING; } - return requested_features; + kernel_features |= film->get_kernel_features(this); + + dscene.data.kernel_features = kernel_features; + + /* Currently viewport render is faster with higher max_closures, needs investigating. */ + const uint max_closures = (params.background) ? get_max_closure_count() : MAX_CLOSURE; + dscene.data.max_closures = max_closures; + dscene.data.max_shaders = shaders.size(); } -bool Scene::update(Progress &progress, bool &kernel_switch_needed) +bool Scene::update(Progress &progress) { - /* update scene */ - if (need_update()) { - /* Update max_closures. */ - KernelIntegrator *kintegrator = &dscene.data.integrator; - if (params.background) { - kintegrator->max_closures = get_max_closure_count(); - } - else { - /* Currently viewport render is faster with higher max_closures, needs investigating. */ - kintegrator->max_closures = MAX_CLOSURE; - } - - /* Load render kernels, before device update where we upload data to the GPU. */ - bool new_kernels_needed = load_kernels(progress, false); - - progress.set_status("Updating Scene"); - MEM_GUARDED_CALL(&progress, device_update, device, progress); - - DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state(); - kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE || - kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID; - if (new_kernels_needed || kernel_switch_needed) { - progress.set_kernel_status("Compiling render kernels"); - device->wait_for_availability(loaded_kernel_features); - progress.set_kernel_status(""); - } - - return true; + if (!need_update()) { + return false; } - return false; + + /* Load render kernels, before device update where we upload data to the GPU. */ + load_kernels(progress, false); + + /* Upload scene data to the GPU. */ + progress.set_status("Updating Scene"); + MEM_GUARDED_CALL(&progress, device_update, device, progress); + + return true; +} + +static void log_kernel_features(const uint features) +{ + VLOG(2) << "Requested features:\n"; + VLOG(2) << "Use BSDF " << string_from_bool(features & KERNEL_FEATURE_NODE_BSDF) << "\n"; + VLOG(2) << "Use Principled BSDF " << string_from_bool(features & KERNEL_FEATURE_PRINCIPLED) + << "\n"; + VLOG(2) << "Use Emission " << string_from_bool(features & KERNEL_FEATURE_NODE_EMISSION) << "\n"; + VLOG(2) << "Use Volume " << string_from_bool(features & KERNEL_FEATURE_NODE_VOLUME) << "\n"; + VLOG(2) << "Use Hair " << string_from_bool(features & KERNEL_FEATURE_NODE_HAIR) << "\n"; + VLOG(2) << "Use Bump " << string_from_bool(features & KERNEL_FEATURE_NODE_BUMP) << "\n"; + VLOG(2) << "Use Voronoi " << string_from_bool(features & KERNEL_FEATURE_NODE_VORONOI_EXTRA) + << "\n"; + VLOG(2) << "Use Shader Raytrace " << string_from_bool(features & KERNEL_FEATURE_NODE_RAYTRACE) + << "\n"; + VLOG(2) << "Use Transparent " << string_from_bool(features & KERNEL_FEATURE_TRANSPARENT) << "\n"; + VLOG(2) << "Use Denoising " << string_from_bool(features & KERNEL_FEATURE_DENOISING) << "\n"; + VLOG(2) << "Use Path Tracing " << string_from_bool(features & KERNEL_FEATURE_PATH_TRACING) + << "\n"; + VLOG(2) << "Use Hair " << string_from_bool(features & KERNEL_FEATURE_HAIR) << "\n"; + VLOG(2) << "Use Object Motion " << string_from_bool(features & KERNEL_FEATURE_OBJECT_MOTION) + << "\n"; + VLOG(2) << "Use Camera Motion " << string_from_bool(features & KERNEL_FEATURE_CAMERA_MOTION) + << "\n"; + VLOG(2) << "Use Baking " << string_from_bool(features & KERNEL_FEATURE_BAKING) << "\n"; + VLOG(2) << "Use Subsurface " << string_from_bool(features & KERNEL_FEATURE_SUBSURFACE) << "\n"; + VLOG(2) << "Use Volume " << string_from_bool(features & KERNEL_FEATURE_VOLUME) << "\n"; + VLOG(2) << "Use Patch Evaluation " + << string_from_bool(features & KERNEL_FEATURE_PATCH_EVALUATION) << "\n"; + VLOG(2) << "Use Shadow Catcher " << string_from_bool(features & KERNEL_FEATURE_SHADOW_CATCHER) + << "\n"; } bool Scene::load_kernels(Progress &progress, bool lock_scene) @@ -560,15 +584,15 @@ bool Scene::load_kernels(Progress &progress, bool lock_scene) scene_lock = thread_scoped_lock(mutex); } - DeviceRequestedFeatures requested_features = get_requested_device_features(); + const uint kernel_features = dscene.data.kernel_features; - if (!kernels_loaded || loaded_kernel_features.modified(requested_features)) { + if (!kernels_loaded || loaded_kernel_features != kernel_features) { progress.set_status("Loading render kernels (may take a few minutes the first time)"); scoped_timer timer; - VLOG(2) << "Requested features:\n" << requested_features; - if (!device->load_kernels(requested_features)) { + log_kernel_features(kernel_features); + if (!device->load_kernels(kernel_features)) { string message = device->error_message(); if (message.empty()) message = "Failed loading render kernel, see console for errors"; @@ -580,7 +604,7 @@ bool Scene::load_kernels(Progress &progress, bool lock_scene) } kernels_loaded = true; - loaded_kernel_features = requested_features; + loaded_kernel_features = kernel_features; return true; } return false; @@ -618,6 +642,28 @@ int Scene::get_max_closure_count() return max_closure_global; } +bool Scene::has_shadow_catcher() +{ + if (shadow_catcher_modified_) { + has_shadow_catcher_ = false; + for (Object *object : objects) { + if (object->get_is_shadow_catcher()) { + has_shadow_catcher_ = true; + break; + } + } + + shadow_catcher_modified_ = false; + } + + return has_shadow_catcher_; +} + +void Scene::tag_shadow_catcher_modified() +{ + shadow_catcher_modified_ = true; +} + template<> Light *Scene::create_node() { Light *node = new Light(); @@ -694,6 +740,15 @@ template<> AlembicProcedural *Scene::create_node() #endif } +template<> Pass *Scene::create_node() +{ + Pass *node = new Pass(); + node->set_owner(this); + passes.push_back(node); + film->tag_modified(); + return node; +} + template void delete_node_from_array(vector &nodes, T node) { for (size_t i = 0; i < nodes.size(); ++i) { @@ -779,6 +834,12 @@ template<> void Scene::delete_node_impl(AlembicProcedural *node) #endif } +template<> void Scene::delete_node_impl(Pass *node) +{ + delete_node_from_array(passes, node); + film->tag_modified(); +} + template static void remove_nodes_in_set(const set &nodes_set, vector &nodes_array, @@ -842,4 +903,10 @@ template<> void Scene::delete_nodes(const set &nodes, const NodeOw procedural_manager->tag_update(); } +template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner) +{ + remove_nodes_in_set(nodes, passes, owner); + film->tag_modified(); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 7d8a6774381..cf4a3ba6b12 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -128,7 +128,7 @@ class DeviceScene { device_vector lookup_table; /* integrator */ - device_vector sample_pattern_lut; + device_vector sample_pattern_lut; /* ies lights */ device_vector ies_lights; @@ -142,27 +142,6 @@ class DeviceScene { class SceneParams { public: - /* Type of BVH, in terms whether it is supported dynamic updates of meshes - * or whether modifying geometry requires full BVH rebuild. - */ - enum BVHType { - /* BVH supports dynamic updates of geometry. - * - * Faster for updating BVH tree when doing modifications in viewport, - * but slower for rendering. - */ - BVH_DYNAMIC = 0, - /* BVH tree is calculated for specific scene, updates in geometry - * requires full tree rebuild. - * - * Slower to update BVH tree when modifying objects in viewport, also - * slower to build final BVH tree but gives best possible render speed. - */ - BVH_STATIC = 1, - - BVH_NUM_TYPES, - }; - ShadingSystem shadingsystem; /* Requested BVH layout. @@ -186,7 +165,7 @@ class SceneParams { { shadingsystem = SHADINGSYSTEM_SVM; bvh_layout = BVH_LAYOUT_BVH2; - bvh_type = BVH_DYNAMIC; + bvh_type = BVH_TYPE_DYNAMIC; use_bvh_spatial_split = false; use_bvh_unaligned_nodes = true; num_bvh_time_steps = 0; @@ -196,7 +175,7 @@ class SceneParams { background = true; } - bool modified(const SceneParams ¶ms) + bool modified(const SceneParams ¶ms) const { return !(shadingsystem == params.shadingsystem && bvh_layout == params.bvh_layout && bvh_type == params.bvh_type && @@ -236,7 +215,7 @@ class Scene : public NodeOwner { vector shaders; vector lights; vector particle_systems; - vector passes; + vector passes; vector procedurals; /* data managers */ @@ -291,7 +270,11 @@ class Scene : public NodeOwner { void enable_update_stats(); - bool update(Progress &progress, bool &kernel_switch_needed); + void update_kernel_features(); + bool update(Progress &progress); + + bool has_shadow_catcher(); + void tag_shadow_catcher_modified(); /* This function is used to create a node of a specified type instead of * calling 'new', and sets the scene as the owner of the node. @@ -348,13 +331,12 @@ class Scene : public NodeOwner { void free_memory(bool final); bool kernels_loaded; - DeviceRequestedFeatures loaded_kernel_features; + uint loaded_kernel_features; bool load_kernels(Progress &progress, bool lock_scene = true); - /* ** Split kernel routines ** */ - - DeviceRequestedFeatures get_requested_device_features(); + bool has_shadow_catcher_ = false; + bool shadow_catcher_modified_ = true; /* Maximum number of closure during session lifetime. */ int max_closure_global; @@ -384,6 +366,8 @@ template<> Shader *Scene::create_node(); template<> AlembicProcedural *Scene::create_node(); +template<> Pass *Scene::create_node(); + template<> void Scene::delete_node_impl(Light *node); template<> void Scene::delete_node_impl(Mesh *node); @@ -404,6 +388,8 @@ template<> void Scene::delete_node_impl(Procedural *node); template<> void Scene::delete_node_impl(AlembicProcedural *node); +template<> void Scene::delete_node_impl(Pass *node); + template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); @@ -416,6 +402,8 @@ template<> void Scene::delete_nodes(const set &nodes, const NodeOwner template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); +template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); + CCL_NAMESPACE_END #endif /* __SCENE_H__ */ diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 1b91c49f0ea..84407f8e6dd 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -17,10 +17,15 @@ #include #include +#include "device/cpu/device.h" #include "device/device.h" +#include "integrator/pass_accessor_cpu.h" +#include "integrator/path_trace.h" +#include "render/background.h" #include "render/bake.h" #include "render/buffers.h" #include "render/camera.h" +#include "render/gpu_display.h" #include "render/graph.h" #include "render/integrator.h" #include "render/light.h" @@ -39,70 +44,63 @@ CCL_NAMESPACE_BEGIN -/* Note about preserve_tile_device option for tile manager: - * progressive refine and viewport rendering does requires tiles to - * always be allocated for the same device - */ -Session::Session(const SessionParams ¶ms_) - : params(params_), - tile_manager(params.progressive, - params.samples, - params.tile_size, - params.start_resolution, - params.background == false || params.progressive_refine, - params.background, - params.tile_order, - max(params.device.multi_devices.size(), 1), - params.pixel_size), - stats(), - profiler() +Session::Session(const SessionParams ¶ms_, const SceneParams &scene_params) + : params(params_), render_scheduler_(tile_manager_, params) { - device_use_gl_ = ((params.device.type != DEVICE_CPU) && !params.background); - TaskScheduler::init(params.threads); - session_thread_ = NULL; - scene = NULL; - - reset_time_ = 0.0; - last_update_time_ = 0.0; + session_thread_ = nullptr; delayed_reset_.do_reset = false; - delayed_reset_.samples = 0; - - display_outdated_ = false; - gpu_draw_ready_ = false; - gpu_need_display_buffer_update_ = false; pause_ = false; cancel_ = false; new_work_added_ = false; - buffers = NULL; - display = NULL; + device = Device::create(params.device, stats, profiler); - /* Validate denoising parameters. */ - set_denoising(params.denoising); + scene = new Scene(scene_params, device); - /* Create CPU/GPU devices. */ - device = Device::create(params.device, stats, profiler, params.background); + /* Configure path tracer. */ + path_trace_ = make_unique( + device, scene->film, &scene->dscene, render_scheduler_, tile_manager_); + path_trace_->set_progress(&progress); + path_trace_->tile_buffer_update_cb = [&]() { + if (!update_render_tile_cb) { + return; + } + update_render_tile_cb(); + }; + path_trace_->tile_buffer_write_cb = [&]() { + if (!write_render_tile_cb) { + return; + } + write_render_tile_cb(); + }; + path_trace_->tile_buffer_read_cb = [&]() -> bool { + if (!read_render_tile_cb) { + return false; + } + read_render_tile_cb(); + return true; + }; + path_trace_->progress_update_cb = [&]() { update_status_time(); }; - if (!device->error_message().empty()) { - progress.set_error(device->error_message()); - return; - } - - /* Create buffers for interactive rendering. */ - if (!(params.background && !params.write_render_cb)) { - buffers = new RenderBuffers(device); - display = new DisplayBuffer(device, params.display_buffer_linear); - } + tile_manager_.full_buffer_written_cb = [&](string_view filename) { + if (!full_buffer_written_cb) { + return; + } + full_buffer_written_cb(filename); + }; } Session::~Session() { cancel(); + /* TODO(sergey): Bring the passes in viewport back. + * It is unclear why there is such an exception needed though. */ +#if 0 if (buffers && params.write_render_cb) { /* Copy to display buffer and write out image if requested */ delete display; @@ -116,12 +114,14 @@ Session::~Session() uchar4 *pixels = display->rgba_byte.copy_from_device(0, w, h); params.write_render_cb((uchar *)pixels, w, h, 4); } +#endif - /* clean up */ - tile_manager.device_free(); + /* Make sure path tracer is destroyed before the deviec. This is needed because destruction might + * need to access device for device memory free. */ + /* TODO(sergey): Convert device to be unique_ptr, and rely on C++ to destruct objects in the + * pre-defined order. */ + path_trace_.reset(); - delete buffers; - delete display; delete scene; delete device; @@ -135,15 +135,16 @@ void Session::start() } } -void Session::cancel() +void Session::cancel(bool quick) { + if (quick && path_trace_) { + path_trace_->cancel(); + } + if (session_thread_) { /* wait for session thread to end */ progress.set_cancel("Exiting"); - gpu_need_display_buffer_update_ = false; - gpu_need_display_buffer_update_cond_.notify_all(); - { thread_scoped_lock pause_lock(pause_mutex_); pause_ = false; @@ -157,622 +158,71 @@ void Session::cancel() bool Session::ready_to_reset() { - double dt = time_dt() - reset_time_; - - if (!display_outdated_) - return (dt > params.reset_timeout); - else - return (dt > params.cancel_timeout); + return path_trace_->ready_to_reset(); } -/* GPU Session */ - -void Session::reset_gpu(BufferParams &buffer_params, int samples) +void Session::run_main_render_loop() { - thread_scoped_lock pause_lock(pause_mutex_); + path_trace_->clear_gpu_display(); - /* block for buffer access and reset immediately. we can't do this - * in the thread, because we need to allocate an OpenGL buffer, and - * that only works in the main thread */ - thread_scoped_lock display_lock(display_mutex_); - thread_scoped_lock buffers_lock(buffers_mutex_); + while (true) { + RenderWork render_work = run_update_for_next_iteration(); - display_outdated_ = true; - reset_time_ = time_dt(); - - reset_(buffer_params, samples); - - gpu_need_display_buffer_update_ = false; - gpu_need_display_buffer_update_cond_.notify_all(); - - new_work_added_ = true; - - pause_cond_.notify_all(); -} - -bool Session::draw_gpu(BufferParams &buffer_params, DeviceDrawParams &draw_params) -{ - /* block for buffer access */ - thread_scoped_lock display_lock(display_mutex_); - - /* first check we already rendered something */ - if (gpu_draw_ready_) { - /* then verify the buffers have the expected size, so we don't - * draw previous results in a resized window */ - if (buffer_params.width == display->params.width && - buffer_params.height == display->params.height) { - /* for CUDA we need to do tone-mapping still, since we can - * only access GL buffers from the main thread. */ - if (gpu_need_display_buffer_update_) { - thread_scoped_lock buffers_lock(buffers_mutex_); - copy_to_display_buffer(tile_manager.state.sample); - gpu_need_display_buffer_update_ = false; - gpu_need_display_buffer_update_cond_.notify_all(); + if (!render_work) { + if (VLOG_IS_ON(2)) { + double total_time, render_time; + progress.get_time(total_time, render_time); + VLOG(2) << "Rendering in main loop is done in " << render_time << " seconds."; + VLOG(2) << path_trace_->full_report(); } - display->draw(device, draw_params); - - if (display_outdated_ && (time_dt() - reset_time_) > params.text_timeout) - return false; - - return true; - } - } - - return false; -} - -void Session::run_gpu() -{ - bool tiles_written = false; - - reset_time_ = time_dt(); - last_update_time_ = time_dt(); - last_display_time_ = last_update_time_; - - progress.set_render_start_time(); - - while (!progress.get_cancel()) { - const bool no_tiles = !run_update_for_next_iteration(); - - if (no_tiles) { if (params.background) { - /* if no work left and in background mode, we can stop immediately */ + /* if no work left and in background mode, we can stop immediately. */ progress.set_status("Finished"); break; } } - if (run_wait_for_work(no_tiles)) { + const bool did_cancel = progress.get_cancel(); + if (did_cancel) { + render_scheduler_.render_work_reschedule_on_cancel(render_work); + if (!render_work) { + break; + } + } + else if (run_wait_for_work(render_work)) { continue; } - if (progress.get_cancel()) { - break; - } - - if (!no_tiles) { - if (!device->error_message().empty()) - progress.set_error(device->error_message()); - - if (progress.get_cancel()) - break; - - /* buffers mutex is locked entirely while rendering each - * sample, and released/reacquired on each iteration to allow - * reset and draw in between */ - thread_scoped_lock buffers_lock(buffers_mutex_); - - /* update status and timing */ - update_status_time(); - - /* render */ - bool delayed_denoise = false; - const bool need_denoise = render_need_denoise(delayed_denoise); - render(need_denoise); - - device->task_wait(); - - if (!device->error_message().empty()) - progress.set_cancel(device->error_message()); - - /* update status and timing */ - update_status_time(); - - gpu_need_display_buffer_update_ = !delayed_denoise; - gpu_draw_ready_ = true; - progress.set_update(); - - /* wait for until display buffer is updated */ - if (!params.background) { - while (gpu_need_display_buffer_update_) { - if (progress.get_cancel()) - break; - - gpu_need_display_buffer_update_cond_.wait(buffers_lock); - } - } - - if (!device->error_message().empty()) - progress.set_error(device->error_message()); - - tiles_written = update_progressive_refine(progress.get_cancel()); - - if (progress.get_cancel()) - break; - } - } - - if (!tiles_written) - update_progressive_refine(true); -} - -/* CPU Session */ - -void Session::reset_cpu(BufferParams &buffer_params, int samples) -{ - thread_scoped_lock reset_lock(delayed_reset_.mutex); - thread_scoped_lock pause_lock(pause_mutex_); - - display_outdated_ = true; - reset_time_ = time_dt(); - - delayed_reset_.params = buffer_params; - delayed_reset_.samples = samples; - delayed_reset_.do_reset = true; - device->task_cancel(); - - pause_cond_.notify_all(); -} - -bool Session::draw_cpu(BufferParams &buffer_params, DeviceDrawParams &draw_params) -{ - thread_scoped_lock display_lock(display_mutex_); - - /* first check we already rendered something */ - if (display->draw_ready()) { - /* then verify the buffers have the expected size, so we don't - * draw previous results in a resized window */ - if (buffer_params.width == display->params.width && - buffer_params.height == display->params.height) { - display->draw(device, draw_params); - - if (display_outdated_ && (time_dt() - reset_time_) > params.text_timeout) - return false; - - return true; - } - } - - return false; -} - -bool Session::steal_tile(RenderTile &rtile, Device *tile_device, thread_scoped_lock &tile_lock) -{ - /* Devices that can get their tiles stolen don't steal tiles themselves. - * Additionally, if there are no stealable tiles in flight, give up here. */ - if (tile_device->info.type == DEVICE_CPU || stealable_tiles_ == 0) { - return false; - } - - /* Wait until no other thread is trying to steal a tile. */ - while (tile_stealing_state_ != NOT_STEALING && stealable_tiles_ > 0) { - /* Someone else is currently trying to get a tile. - * Wait on the condition variable and try later. */ - tile_steal_cond_.wait(tile_lock); - } - /* If another thread stole the last stealable tile in the meantime, give up. */ - if (stealable_tiles_ == 0) { - return false; - } - - /* There are stealable tiles in flight, so signal that one should be released. */ - tile_stealing_state_ = WAITING_FOR_TILE; - - /* Wait until a device notices the signal and releases its tile. */ - while (tile_stealing_state_ != GOT_TILE && stealable_tiles_ > 0) { - tile_steal_cond_.wait(tile_lock); - } - /* If the last stealable tile finished on its own, give up. */ - if (tile_stealing_state_ != GOT_TILE) { - tile_stealing_state_ = NOT_STEALING; - return false; - } - - /* Successfully stole a tile, now move it to the new device. */ - rtile = stolen_tile_; - rtile.buffers->buffer.move_device(tile_device); - rtile.buffer = rtile.buffers->buffer.device_pointer; - rtile.stealing_state = RenderTile::NO_STEALING; - rtile.num_samples -= (rtile.sample - rtile.start_sample); - rtile.start_sample = rtile.sample; - - tile_stealing_state_ = NOT_STEALING; - - /* Poke any threads which might be waiting for NOT_STEALING above. */ - tile_steal_cond_.notify_one(); - - return true; -} - -bool Session::get_tile_stolen() -{ - /* If tile_stealing_state is WAITING_FOR_TILE, atomically set it to RELEASING_TILE - * and return true. */ - TileStealingState expected = WAITING_FOR_TILE; - return tile_stealing_state_.compare_exchange_weak(expected, RELEASING_TILE); -} - -bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_types) -{ - if (progress.get_cancel()) { - if (params.progressive_refine == false) { - /* for progressive refine current sample should be finished for all tiles */ - return false; - } - } - - thread_scoped_lock tile_lock(tile_mutex_); - - /* get next tile from manager */ - Tile *tile; - int device_num = device->device_number(tile_device); - - while (!tile_manager.next_tile(tile, device_num, tile_types)) { - /* Can only steal tiles on devices that support rendering - * This is because denoising tiles cannot be stolen (see below) - */ - if ((tile_types & (RenderTile::PATH_TRACE | RenderTile::BAKE)) && - steal_tile(rtile, tile_device, tile_lock)) { - return true; - } - - /* Wait for denoising tiles to become available */ - if ((tile_types & RenderTile::DENOISE) && !progress.get_cancel() && tile_manager.has_tiles()) { - denoising_cond_.wait(tile_lock); - continue; - } - - return false; - } - - /* fill render tile */ - rtile.x = tile_manager.state.buffer.full_x + tile->x; - rtile.y = tile_manager.state.buffer.full_y + tile->y; - rtile.w = tile->w; - rtile.h = tile->h; - rtile.start_sample = tile_manager.state.sample; - rtile.num_samples = tile_manager.state.num_samples; - rtile.resolution = tile_manager.state.resolution_divider; - rtile.tile_index = tile->index; - rtile.stealing_state = RenderTile::NO_STEALING; - - if (tile->state == Tile::DENOISE) { - rtile.task = RenderTile::DENOISE; - } - else { - if (tile_device->info.type == DEVICE_CPU) { - stealable_tiles_++; - rtile.stealing_state = RenderTile::CAN_BE_STOLEN; - } - - if (read_bake_tile_cb) { - rtile.task = RenderTile::BAKE; - } - else { - rtile.task = RenderTile::PATH_TRACE; - } - } - - tile_lock.unlock(); - - /* in case of a permanent buffer, return it, otherwise we will allocate - * a new temporary buffer */ - if (buffers) { - tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride); - - rtile.buffer = buffers->buffer.device_pointer; - rtile.buffers = buffers; - - device->map_tile(tile_device, rtile); - - /* Reset copy state, since buffer contents change after the tile was acquired */ - buffers->map_neighbor_copied = false; - - /* This hack ensures that the copy in 'MultiDevice::map_neighbor_tiles' accounts - * for the buffer resolution divider. */ - buffers->buffer.data_width = (buffers->params.width * buffers->params.get_passes_size()) / - tile_manager.state.resolution_divider; - buffers->buffer.data_height = buffers->params.height / tile_manager.state.resolution_divider; - - return true; - } - - if (tile->buffers == NULL) { - /* fill buffer parameters */ - BufferParams buffer_params = tile_manager.params; - buffer_params.full_x = rtile.x; - buffer_params.full_y = rtile.y; - buffer_params.width = rtile.w; - buffer_params.height = rtile.h; - - /* allocate buffers */ - tile->buffers = new RenderBuffers(tile_device); - tile->buffers->reset(buffer_params); - } - else if (tile->buffers->buffer.device != tile_device) { - /* Move buffer to current tile device again in case it was stolen before. - * Not needed for denoising since that already handles mapping of tiles and - * neighbors to its own device. */ - if (rtile.task != RenderTile::DENOISE) { - tile->buffers->buffer.move_device(tile_device); - } - } - - tile->buffers->map_neighbor_copied = false; - - tile->buffers->params.get_offset_stride(rtile.offset, rtile.stride); - - rtile.buffer = tile->buffers->buffer.device_pointer; - rtile.buffers = tile->buffers; - rtile.sample = tile_manager.state.sample; - - if (read_bake_tile_cb) { - /* This will read any passes needed as input for baking. */ - if (tile_manager.state.sample == tile_manager.range_start_sample) { - { - thread_scoped_lock tile_lock(tile_mutex_); - read_bake_tile_cb(rtile); - } - rtile.buffers->buffer.copy_to_device(); - } - } - else { - /* This will tag tile as IN PROGRESS in blender-side render pipeline, - * which is needed to highlight currently rendering tile before first - * sample was processed for it. */ - update_tile_sample(rtile); - } - - return true; -} - -void Session::update_tile_sample(RenderTile &rtile) -{ - thread_scoped_lock tile_lock(tile_mutex_); - - if (update_render_tile_cb) { - if (params.progressive_refine == false) { - /* todo: optimize this by making it thread safe and removing lock */ - - update_render_tile_cb(rtile, true); - } - } - - update_status_time(); -} - -void Session::release_tile(RenderTile &rtile, const bool need_denoise) -{ - thread_scoped_lock tile_lock(tile_mutex_); - - if (rtile.stealing_state != RenderTile::NO_STEALING) { - stealable_tiles_--; - if (rtile.stealing_state == RenderTile::WAS_STOLEN) { - /* If the tile is being stolen, don't release it here - the new device will pick up where - * the old one left off. */ - - assert(tile_stealing_state_ == RELEASING_TILE); - assert(rtile.sample < rtile.start_sample + rtile.num_samples); - - tile_stealing_state_ = GOT_TILE; - stolen_tile_ = rtile; - tile_steal_cond_.notify_all(); - return; - } - else if (stealable_tiles_ == 0) { - /* If this was the last stealable tile, wake up any threads still waiting for one. */ - tile_steal_cond_.notify_all(); - } - } - - progress.add_finished_tile(rtile.task == RenderTile::DENOISE); - - bool delete_tile; - - if (tile_manager.finish_tile(rtile.tile_index, need_denoise, delete_tile)) { - /* Finished tile pixels write. */ - if (write_render_tile_cb && params.progressive_refine == false) { - write_render_tile_cb(rtile); - } - - if (delete_tile) { - delete rtile.buffers; - tile_manager.state.tiles[rtile.tile_index].buffers = NULL; - } - } - else { - /* In progress tile pixels update. */ - if (update_render_tile_cb && params.progressive_refine == false) { - update_render_tile_cb(rtile, false); - } - } - - update_status_time(); - - /* Notify denoising thread that a tile was finished. */ - denoising_cond_.notify_all(); -} - -void Session::map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device) -{ - thread_scoped_lock tile_lock(tile_mutex_); - - const int4 image_region = make_int4( - tile_manager.state.buffer.full_x, - tile_manager.state.buffer.full_y, - tile_manager.state.buffer.full_x + tile_manager.state.buffer.width, - tile_manager.state.buffer.full_y + tile_manager.state.buffer.height); - - RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; - - if (!tile_manager.schedule_denoising) { - /* Fix up tile slices with overlap. */ - if (tile_manager.slice_overlap != 0) { - int y = max(center_tile.y - tile_manager.slice_overlap, image_region.y); - center_tile.h = min(center_tile.y + center_tile.h + tile_manager.slice_overlap, - image_region.w) - - y; - center_tile.y = y; - } - - /* Tiles are not being denoised individually, which means the entire image is processed. */ - neighbors.set_bounds_from_center(); - } - else { - int center_idx = center_tile.tile_index; - assert(tile_manager.state.tiles[center_idx].state == Tile::DENOISE); - - for (int dy = -1, i = 0; dy <= 1; dy++) { - for (int dx = -1; dx <= 1; dx++, i++) { - RenderTile &rtile = neighbors.tiles[i]; - int nindex = tile_manager.get_neighbor_index(center_idx, i); - if (nindex >= 0) { - Tile *tile = &tile_manager.state.tiles[nindex]; - - rtile.x = image_region.x + tile->x; - rtile.y = image_region.y + tile->y; - rtile.w = tile->w; - rtile.h = tile->h; - - if (buffers) { - tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride); - - rtile.buffer = buffers->buffer.device_pointer; - rtile.buffers = buffers; - } - else { - assert(tile->buffers); - tile->buffers->params.get_offset_stride(rtile.offset, rtile.stride); - - rtile.buffer = tile->buffers->buffer.device_pointer; - rtile.buffers = tile->buffers; - } - } - else { - int px = center_tile.x + dx * params.tile_size.x; - int py = center_tile.y + dy * params.tile_size.y; - - rtile.x = clamp(px, image_region.x, image_region.z); - rtile.y = clamp(py, image_region.y, image_region.w); - rtile.w = rtile.h = 0; - - rtile.buffer = (device_ptr)NULL; - rtile.buffers = NULL; - } - } - } - } - - assert(center_tile.buffers); - device->map_neighbor_tiles(tile_device, neighbors); - - /* The denoised result is written back to the original tile. */ - neighbors.target = center_tile; -} - -void Session::unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device) -{ - thread_scoped_lock tile_lock(tile_mutex_); - device->unmap_neighbor_tiles(tile_device, neighbors); -} - -void Session::run_cpu() -{ - bool tiles_written = false; - - last_update_time_ = time_dt(); - last_display_time_ = last_update_time_; - - while (!progress.get_cancel()) { - const bool no_tiles = !run_update_for_next_iteration(); - bool need_copy_to_display_buffer = false; - - if (no_tiles) { - if (params.background) { - /* if no work left and in background mode, we can stop immediately */ - progress.set_status("Finished"); - break; - } - } - - if (run_wait_for_work(no_tiles)) { - continue; - } - - if (progress.get_cancel()) { - break; - } - - if (!no_tiles) { - if (!device->error_message().empty()) - progress.set_error(device->error_message()); - - if (progress.get_cancel()) - break; - - /* buffers mutex is locked entirely while rendering each - * sample, and released/reacquired on each iteration to allow - * reset and draw in between */ - thread_scoped_lock buffers_lock(buffers_mutex_); - - /* update status and timing */ - update_status_time(); - - /* render */ - bool delayed_denoise = false; - const bool need_denoise = render_need_denoise(delayed_denoise); - render(need_denoise); - - /* update status and timing */ - update_status_time(); - - if (!params.background) - need_copy_to_display_buffer = !delayed_denoise; - - if (!device->error_message().empty()) - progress.set_error(device->error_message()); - } - - device->task_wait(); - { - thread_scoped_lock reset_lock(delayed_reset_.mutex); + /* buffers mutex is locked entirely while rendering each + * sample, and released/reacquired on each iteration to allow + * reset and draw in between */ thread_scoped_lock buffers_lock(buffers_mutex_); - thread_scoped_lock display_lock(display_mutex_); - if (delayed_reset_.do_reset) { - /* reset rendering if request from main thread */ - delayed_reset_.do_reset = false; - reset_(delayed_reset_.params, delayed_reset_.samples); + /* update status and timing */ + update_status_time(); + + /* render */ + path_trace_->render(render_work); + + /* update status and timing */ + update_status_time(); + + if (device->have_error()) { + const string &error_message = device->error_message(); + progress.set_error(error_message); + progress.set_cancel(error_message); + break; } - else if (need_copy_to_display_buffer) { - /* Only copy to display_buffer if we do not reset, we don't - * want to show the result of an incomplete sample */ - copy_to_display_buffer(tile_manager.state.sample); - } - - if (!device->error_message().empty()) - progress.set_error(device->error_message()); - - tiles_written = update_progressive_refine(progress.get_cancel()); } progress.set_update(); - } - if (!tiles_written) - update_progressive_refine(true); + if (did_cancel) { + break; + } + } } void Session::run() @@ -789,10 +239,7 @@ void Session::run() /* reset number of rendered samples */ progress.reset_sample(); - if (device_use_gl_) - run_gpu(); - else - run_cpu(); + run_main_render_loop(); } profiler.stop(); @@ -804,31 +251,92 @@ void Session::run() progress.set_update(); } -bool Session::run_update_for_next_iteration() +RenderWork Session::run_update_for_next_iteration() { + RenderWork render_work; + thread_scoped_lock scene_lock(scene->mutex); thread_scoped_lock reset_lock(delayed_reset_.mutex); + bool have_tiles = true; + bool switched_to_new_tile = false; + if (delayed_reset_.do_reset) { thread_scoped_lock buffers_lock(buffers_mutex_); - reset_(delayed_reset_.params, delayed_reset_.samples); - delayed_reset_.do_reset = false; + do_delayed_reset(); + + /* After reset make sure the tile manager is at the first big tile. */ + have_tiles = tile_manager_.next(); + switched_to_new_tile = true; } - const bool have_tiles = tile_manager.next(); + /* Update number of samples in the integrator. + * Ideally this would need to happen once in `Session::set_samples()`, but the issue there is + * the initial configuration when Session is created where the `set_samples()` is not used. */ + scene->integrator->set_aa_samples(params.samples); - if (have_tiles) { + /* Update denoiser settings. */ + { + const DenoiseParams denoise_params = scene->integrator->get_denoise_params(); + path_trace_->set_denoiser_params(denoise_params); + } + + /* Update adaptive sampling. */ + { + const AdaptiveSampling adaptive_sampling = scene->integrator->get_adaptive_sampling(); + path_trace_->set_adaptive_sampling(adaptive_sampling); + } + + render_scheduler_.set_num_samples(params.samples); + render_scheduler_.set_time_limit(params.time_limit); + + while (have_tiles) { + render_work = render_scheduler_.get_render_work(); + if (render_work) { + break; + } + + progress.add_finished_tile(false); + + have_tiles = tile_manager_.next(); + if (have_tiles) { + render_scheduler_.reset_for_next_tile(); + switched_to_new_tile = true; + } + } + + if (render_work) { scoped_timer update_timer; - if (update_scene()) { + + if (switched_to_new_tile) { + BufferParams tile_params = buffer_params_; + + const Tile &tile = tile_manager_.get_current_tile(); + tile_params.width = tile.width; + tile_params.height = tile.height; + tile_params.full_x = tile.x + buffer_params_.full_x; + tile_params.full_y = tile.y + buffer_params_.full_y; + tile_params.full_width = buffer_params_.full_width; + tile_params.full_height = buffer_params_.full_height; + tile_params.update_offset_stride(); + + path_trace_->reset(buffer_params_, tile_params); + } + + const int resolution = render_work.resolution_divider; + const int width = max(1, buffer_params_.full_width / resolution); + const int height = max(1, buffer_params_.full_height / resolution); + + if (update_scene(width, height)) { profiler.reset(scene->shaders.size(), scene->objects.size()); } progress.add_skip_time(update_timer, params.background); } - return have_tiles; + return render_work; } -bool Session::run_wait_for_work(bool no_tiles) +bool Session::run_wait_for_work(const RenderWork &render_work) { /* In an offline rendering there is no pause, and no tiles will mean the job is fully done. */ if (params.background) { @@ -837,19 +345,20 @@ bool Session::run_wait_for_work(bool no_tiles) thread_scoped_lock pause_lock(pause_mutex_); - if (!pause_ && !no_tiles) { + if (!pause_ && render_work) { /* Rendering is not paused and there is work to be done. No need to wait for anything. */ return false; } - update_status_time(pause_, no_tiles); + const bool no_work = !render_work; + update_status_time(pause_, no_work); /* Only leave the loop when rendering is not paused. But even if the current render is un-paused * but there is nothing to render keep waiting until new work is added. */ while (!cancel_) { scoped_timer pause_timer; - if (!pause_ && (!no_tiles || new_work_added_ || delayed_reset_.do_reset)) { + if (!pause_ && (render_work || new_work_added_ || delayed_reset_.do_reset)) { break; } @@ -860,52 +369,88 @@ bool Session::run_wait_for_work(bool no_tiles) progress.add_skip_time(pause_timer, params.background); } - update_status_time(pause_, no_tiles); + update_status_time(pause_, no_work); progress.set_update(); } new_work_added_ = false; - return no_tiles; + return no_work; } -bool Session::draw(BufferParams &buffer_params, DeviceDrawParams &draw_params) +void Session::draw() { - if (device_use_gl_) - return draw_gpu(buffer_params, draw_params); - else - return draw_cpu(buffer_params, draw_params); + path_trace_->draw(); } -void Session::reset_(BufferParams &buffer_params, int samples) +int2 Session::get_effective_tile_size() const { - if (buffers && buffer_params.modified(tile_manager.params)) { - gpu_draw_ready_ = false; - buffers->reset(buffer_params); - if (display) { - display->reset(buffer_params); - } + /* No support yet for baking with tiles. */ + if (!params.use_auto_tile || scene->bake_manager->get_baking()) { + return make_int2(buffer_params_.width, buffer_params_.height); } - tile_manager.reset(buffer_params, samples); - stealable_tiles_ = 0; - tile_stealing_state_ = NOT_STEALING; + /* TODO(sergey): Take available memory into account, and if there is enough memory do not tile + * and prefer optimal performance. */ + + return make_int2(params.tile_size, params.tile_size); +} + +void Session::do_delayed_reset() +{ + if (!delayed_reset_.do_reset) { + return; + } + delayed_reset_.do_reset = false; + + params = delayed_reset_.session_params; + buffer_params_ = delayed_reset_.buffer_params; + + /* Store parameters used for buffers access outside of scene graph. */ + buffer_params_.exposure = scene->film->get_exposure(); + buffer_params_.use_approximate_shadow_catcher = + scene->film->get_use_approximate_shadow_catcher(); + buffer_params_.use_transparent_background = scene->background->get_transparent(); + + /* Tile and work scheduling. */ + tile_manager_.reset_scheduling(buffer_params_, get_effective_tile_size()); + render_scheduler_.reset(buffer_params_, params.samples); + + /* Passes. */ + /* When multiple tiles are used SAMPLE_COUNT pass is used to keep track of possible partial + * tile results. It is safe to use generic update function here which checks for changes since + * changes in tile settings re-creates session, which ensures film is fully updated on tile + * changes. */ + scene->film->update_passes(scene, tile_manager_.has_multiple_tiles()); + + /* Update for new state of scene and passes. */ + buffer_params_.update_passes(scene->passes); + tile_manager_.update(buffer_params_, scene); + + /* Progress. */ progress.reset_sample(); + progress.set_total_pixel_samples(buffer_params_.width * buffer_params_.height * params.samples); - bool show_progress = params.background || tile_manager.get_num_effective_samples() != INT_MAX; - progress.set_total_pixel_samples(show_progress ? tile_manager.state.total_pixel_samples : 0); - - if (!params.background) + if (!params.background) { progress.set_start_time(); + } progress.set_render_start_time(); } -void Session::reset(BufferParams &buffer_params, int samples) +void Session::reset(const SessionParams &session_params, const BufferParams &buffer_params) { - if (device_use_gl_) - reset_gpu(buffer_params, samples); - else - reset_cpu(buffer_params, samples); + { + thread_scoped_lock reset_lock(delayed_reset_.mutex); + thread_scoped_lock pause_lock(pause_mutex_); + + delayed_reset_.do_reset = true; + delayed_reset_.session_params = session_params; + delayed_reset_.buffer_params = buffer_params; + + path_trace_->cancel(); + } + + pause_cond_.notify_all(); } void Session::set_samples(int samples) @@ -915,7 +460,22 @@ void Session::set_samples(int samples) } params.samples = samples; - tile_manager.set_samples(samples); + + { + thread_scoped_lock pause_lock(pause_mutex_); + new_work_added_ = true; + } + + pause_cond_.notify_all(); +} + +void Session::set_time_limit(double time_limit) +{ + if (time_limit == params.time_limit) { + return; + } + + params.time_limit = time_limit; { thread_scoped_lock pause_lock(pause_mutex_); @@ -948,38 +508,9 @@ void Session::set_pause(bool pause) } } -void Session::set_denoising(const DenoiseParams &denoising) +void Session::set_gpu_display(unique_ptr gpu_display) { - bool need_denoise = denoising.need_denoising_task(); - - /* Lock buffers so no denoising operation is triggered while the settings are changed here. */ - thread_scoped_lock buffers_lock(buffers_mutex_); - params.denoising = denoising; - - if (!(params.device.denoisers & denoising.type)) { - if (need_denoise) { - progress.set_error("Denoiser type not supported by compute device"); - } - - params.denoising.use = false; - need_denoise = false; - } - - // TODO(pmours): Query the required overlap value for denoising from the device? - tile_manager.slice_overlap = need_denoise && !params.background ? 64 : 0; - - /* Schedule per tile denoising for final renders if we are either denoising or - * need prefiltered passes for the native denoiser. */ - tile_manager.schedule_denoising = need_denoise && !buffers; -} - -void Session::set_denoising_start_sample(int sample) -{ - if (sample != params.denoising.start_sample) { - params.denoising.start_sample = sample; - - pause_cond_.notify_all(); - } + path_trace_->set_gpu_display(move(gpu_display)); } void Session::wait() @@ -989,81 +520,67 @@ void Session::wait() delete session_thread_; } - session_thread_ = NULL; + session_thread_ = nullptr; } -bool Session::update_scene() +bool Session::update_scene(int width, int height) { - /* update camera if dimensions changed for progressive render. the camera + /* Update camera if dimensions changed for progressive render. the camera * knows nothing about progressive or cropped rendering, it just gets the - * image dimensions passed in */ + * image dimensions passed in. */ Camera *cam = scene->camera; - int width = tile_manager.state.buffer.full_width; - int height = tile_manager.state.buffer.full_height; - int resolution = tile_manager.state.resolution_divider; + cam->set_screen_size(width, height); - cam->set_screen_size_and_resolution(width, height, resolution); + /* First detect which kernel features are used and allocate working memory. + * This helps estimate how may device memory is available for the scene and + * how much we need to allocate on the host instead. */ + scene->update_kernel_features(); - /* number of samples is needed by multi jittered - * sampling pattern and by baking */ - Integrator *integrator = scene->integrator; - BakeManager *bake_manager = scene->bake_manager; + path_trace_->load_kernels(); + path_trace_->alloc_work_memory(); - if (integrator->get_sampling_pattern() != SAMPLING_PATTERN_SOBOL || bake_manager->get_baking()) { - integrator->set_aa_samples(tile_manager.num_samples); - } - - bool kernel_switch_needed = false; - if (scene->update(progress, kernel_switch_needed)) { - if (kernel_switch_needed) { - reset(tile_manager.params, params.samples); - } + if (scene->update(progress)) { return true; } + return false; } +static string status_append(const string &status, const string &suffix) +{ + string prefix = status; + if (!prefix.empty()) { + prefix += ", "; + } + return prefix + suffix; +} + void Session::update_status_time(bool show_pause, bool show_done) { - int progressive_sample = tile_manager.state.sample; - int num_samples = tile_manager.get_num_effective_samples(); - - int tile = progress.get_rendered_tiles(); - int num_tiles = tile_manager.state.num_tiles; - - /* update status */ string status, substatus; - if (!params.progressive) { - const bool is_cpu = params.device.type == DEVICE_CPU; - const bool rendering_finished = (tile == num_tiles); - const bool is_last_tile = (tile + 1) == num_tiles; + const int current_tile = progress.get_rendered_tiles(); + const int num_tiles = tile_manager_.get_num_tiles(); - substatus = string_printf("Rendered %d/%d Tiles", tile, num_tiles); + const int current_sample = progress.get_current_sample(); + const int num_samples = render_scheduler_.get_num_samples(); - if (!rendering_finished && (device->show_samples() || (is_cpu && is_last_tile))) { - /* Some devices automatically support showing the sample number: - * - CUDADevice - * - OpenCLDevice when using the megakernel (the split kernel renders multiple - * samples at the same time, so the current sample isn't really defined) - * - CPUDevice when using one thread - * For these devices, the current sample is always shown. - * - * The other option is when the last tile is currently being rendered by the CPU. - */ - substatus += string_printf(", Sample %d/%d", progress.get_current_sample(), num_samples); - } - if (params.denoising.use && params.denoising.type != DENOISER_OPENIMAGEDENOISE) { - substatus += string_printf(", Denoised %d tiles", progress.get_denoised_tiles()); - } - else if (params.denoising.store_passes && params.denoising.type == DENOISER_NLM) { - substatus += string_printf(", Prefiltered %d tiles", progress.get_denoised_tiles()); - } + /* TIle. */ + if (tile_manager_.has_multiple_tiles()) { + substatus = status_append(substatus, + string_printf("Rendered %d/%d Tiles", current_tile, num_tiles)); } - else if (tile_manager.num_samples == Integrator::MAX_SAMPLES) - substatus = string_printf("Path Tracing Sample %d", progressive_sample + 1); - else - substatus = string_printf("Path Tracing Sample %d/%d", progressive_sample + 1, num_samples); + + /* Sample. */ + if (num_samples == Integrator::MAX_SAMPLES) { + substatus = status_append(substatus, string_printf("Sample %d", current_sample)); + } + else { + substatus = status_append(substatus, + string_printf("Sample %d/%d", current_sample, num_samples)); + } + + /* TODO(sergey): Denoising status from the path trace. */ if (show_pause) { status = "Rendering Paused"; @@ -1080,202 +597,10 @@ void Session::update_status_time(bool show_pause, bool show_done) progress.set_status(status, substatus); } -bool Session::render_need_denoise(bool &delayed) -{ - delayed = false; - - /* Not supported yet for baking. */ - if (read_bake_tile_cb) { - return false; - } - - /* Denoising enabled? */ - if (!params.denoising.need_denoising_task()) { - return false; - } - - if (params.background) { - /* Background render, only denoise when rendering the last sample. */ - return tile_manager.done(); - } - - /* Viewport render. */ - - /* It can happen that denoising was already enabled, but the scene still needs an update. */ - if (scene->film->is_modified() || !scene->film->get_denoising_data_offset()) { - return false; - } - - /* Immediately denoise when we reach the start sample or last sample. */ - const int num_samples_finished = tile_manager.state.sample + 1; - if (num_samples_finished == params.denoising.start_sample || - num_samples_finished == params.samples) { - return true; - } - - /* Do not denoise until the sample at which denoising should start is reached. */ - if (num_samples_finished < params.denoising.start_sample) { - return false; - } - - /* Avoid excessive denoising in viewport after reaching a certain amount of samples. */ - delayed = (tile_manager.state.sample >= 20 && - (time_dt() - last_display_time_) < params.progressive_update_timeout); - return !delayed; -} - -void Session::render(bool need_denoise) -{ - if (buffers && tile_manager.state.sample == tile_manager.range_start_sample) { - /* Clear buffers. */ - buffers->zero(); - } - - if (tile_manager.state.buffer.width == 0 || tile_manager.state.buffer.height == 0) { - return; /* Avoid empty launches. */ - } - - /* Add path trace task. */ - DeviceTask task(DeviceTask::RENDER); - - task.acquire_tile = function_bind(&Session::acquire_tile, this, _2, _1, _3); - task.release_tile = function_bind(&Session::release_tile, this, _1, need_denoise); - task.map_neighbor_tiles = function_bind(&Session::map_neighbor_tiles, this, _1, _2); - task.unmap_neighbor_tiles = function_bind(&Session::unmap_neighbor_tiles, this, _1, _2); - task.get_cancel = function_bind(&Progress::get_cancel, &this->progress); - task.update_tile_sample = function_bind(&Session::update_tile_sample, this, _1); - task.update_progress_sample = function_bind(&Progress::add_samples, &this->progress, _1, _2); - task.get_tile_stolen = function_bind(&Session::get_tile_stolen, this); - task.need_finish_queue = params.progressive_refine; - task.integrator_branched = scene->integrator->get_method() == Integrator::BRANCHED_PATH; - - task.adaptive_sampling.use = (scene->integrator->get_sampling_pattern() == - SAMPLING_PATTERN_PMJ) && - scene->dscene.data.film.pass_adaptive_aux_buffer; - task.adaptive_sampling.min_samples = scene->dscene.data.integrator.adaptive_min_samples; - task.adaptive_sampling.adaptive_step = scene->dscene.data.integrator.adaptive_step; - - /* Acquire render tiles by default. */ - task.tile_types = RenderTile::PATH_TRACE; - - if (need_denoise) { - task.denoising = params.denoising; - - task.pass_stride = scene->film->get_pass_stride(); - task.target_pass_stride = task.pass_stride; - task.pass_denoising_data = scene->film->get_denoising_data_offset(); - task.pass_denoising_clean = scene->film->get_denoising_clean_offset(); - - task.denoising_from_render = true; - - if (tile_manager.schedule_denoising) { - /* Acquire denoising tiles during rendering. */ - task.tile_types |= RenderTile::DENOISE; - } - else { - assert(buffers); - - /* Schedule rendering and wait for it to finish. */ - device->task_add(task); - device->task_wait(); - - /* Then run denoising on the whole image at once. */ - task.type = DeviceTask::DENOISE_BUFFER; - task.x = tile_manager.state.buffer.full_x; - task.y = tile_manager.state.buffer.full_y; - task.w = tile_manager.state.buffer.width; - task.h = tile_manager.state.buffer.height; - task.buffer = buffers->buffer.device_pointer; - task.sample = tile_manager.state.sample; - task.num_samples = tile_manager.state.num_samples; - tile_manager.state.buffer.get_offset_stride(task.offset, task.stride); - task.buffers = buffers; - } - } - - device->task_add(task); -} - -void Session::copy_to_display_buffer(int sample) -{ - /* add film conversion task */ - DeviceTask task(DeviceTask::FILM_CONVERT); - - task.x = tile_manager.state.buffer.full_x; - task.y = tile_manager.state.buffer.full_y; - task.w = tile_manager.state.buffer.width; - task.h = tile_manager.state.buffer.height; - task.rgba_byte = display->rgba_byte.device_pointer; - task.rgba_half = display->rgba_half.device_pointer; - task.buffer = buffers->buffer.device_pointer; - task.sample = sample; - tile_manager.state.buffer.get_offset_stride(task.offset, task.stride); - - if (task.w > 0 && task.h > 0) { - device->task_add(task); - device->task_wait(); - - /* set display to new size */ - display->draw_set(task.w, task.h); - - last_display_time_ = time_dt(); - } - - display_outdated_ = false; -} - -bool Session::update_progressive_refine(bool cancel) -{ - int sample = tile_manager.state.sample + 1; - bool write = sample == tile_manager.num_samples || cancel; - - double current_time = time_dt(); - - if (current_time - last_update_time_ < params.progressive_update_timeout) { - /* If last sample was processed, we need to write buffers anyway. */ - if (!write && sample != 1) - return false; - } - - if (params.progressive_refine) { - foreach (Tile &tile, tile_manager.state.tiles) { - if (!tile.buffers) { - continue; - } - - RenderTile rtile; - rtile.x = tile_manager.state.buffer.full_x + tile.x; - rtile.y = tile_manager.state.buffer.full_y + tile.y; - rtile.w = tile.w; - rtile.h = tile.h; - rtile.sample = sample; - rtile.buffers = tile.buffers; - - if (write) { - if (write_render_tile_cb) - write_render_tile_cb(rtile); - } - else { - if (update_render_tile_cb) - update_render_tile_cb(rtile, true); - } - } - } - - last_update_time_ = current_time; - - return write; -} - void Session::device_free() { scene->device_free(); - - tile_manager.device_free(); - - /* used from background render only, so no need to - * re-create render/display buffers here - */ + path_trace_->device_free(); } void Session::collect_statistics(RenderStats *render_stats) @@ -1286,4 +611,108 @@ void Session::collect_statistics(RenderStats *render_stats) } } +/* -------------------------------------------------------------------- + * Tile and tile pixels aceess. + */ + +bool Session::has_multiple_render_tiles() const +{ + return tile_manager_.has_multiple_tiles(); +} + +int2 Session::get_render_tile_size() const +{ + return path_trace_->get_render_tile_size(); +} + +int2 Session::get_render_tile_offset() const +{ + return path_trace_->get_render_tile_offset(); +} + +string_view Session::get_render_tile_layer() const +{ + const BufferParams &buffer_params = path_trace_->get_render_tile_params(); + return buffer_params.layer; +} + +string_view Session::get_render_tile_view() const +{ + const BufferParams &buffer_params = path_trace_->get_render_tile_params(); + return buffer_params.view; +} + +bool Session::copy_render_tile_from_device() +{ + return path_trace_->copy_render_tile_from_device(); +} + +bool Session::get_render_tile_pixels(const string &pass_name, int num_components, float *pixels) +{ + /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification + * is happenning while this function runs. */ + + const BufferParams &buffer_params = path_trace_->get_render_tile_params(); + + const BufferPass *pass = buffer_params.find_pass(pass_name); + if (pass == nullptr) { + return false; + } + + const bool has_denoised_result = path_trace_->has_denoised_result(); + if (pass->mode == PassMode::DENOISED && !has_denoised_result) { + pass = buffer_params.find_pass(pass->type); + if (pass == nullptr) { + /* Happens when denoised result pass is requested but is never written by the kernel. */ + return false; + } + } + + pass = buffer_params.get_actual_display_pass(pass); + + const float exposure = buffer_params.exposure; + const int num_samples = path_trace_->get_num_render_tile_samples(); + + PassAccessor::PassAccessInfo pass_access_info(*pass); + pass_access_info.use_approximate_shadow_catcher = buffer_params.use_approximate_shadow_catcher; + pass_access_info.use_approximate_shadow_catcher_background = + pass_access_info.use_approximate_shadow_catcher && !buffer_params.use_transparent_background; + + const PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); + const PassAccessor::Destination destination(pixels, num_components); + + return path_trace_->get_render_tile_pixels(pass_accessor, destination); +} + +bool Session::set_render_tile_pixels(const string &pass_name, + int num_components, + const float *pixels) +{ + /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification + * is happenning while this function runs. */ + + const BufferPass *pass = buffer_params_.find_pass(pass_name); + if (!pass) { + return false; + } + + const float exposure = scene->film->get_exposure(); + const int num_samples = render_scheduler_.get_num_rendered_samples(); + + const PassAccessor::PassAccessInfo pass_access_info(*pass); + PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); + PassAccessor::Source source(pixels, num_components); + + return path_trace_->set_render_tile_pixels(pass_accessor, source); +} + +/* -------------------------------------------------------------------- + * Full-frame on-disk storage. + */ + +void Session::process_full_buffer_from_disk(string_view filename) +{ + path_trace_->process_full_buffer_from_disk(filename); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index 05025c10f9c..492cfdd1c09 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -18,6 +18,7 @@ #define __SESSION_H__ #include "device/device.h" +#include "integrator/render_scheduler.h" #include "render/buffers.h" #include "render/shader.h" #include "render/stats.h" @@ -26,6 +27,7 @@ #include "util/util_progress.h" #include "util/util_stats.h" #include "util/util_thread.h" +#include "util/util_unique_ptr.h" #include "util/util_vector.h" CCL_NAMESPACE_BEGIN @@ -33,41 +35,35 @@ CCL_NAMESPACE_BEGIN class BufferParams; class Device; class DeviceScene; -class DeviceRequestedFeatures; -class DisplayBuffer; +class PathTrace; class Progress; +class GPUDisplay; class RenderBuffers; class Scene; +class SceneParams; /* Session Parameters */ class SessionParams { public: DeviceInfo device; - bool background; - bool progressive_refine; - bool progressive; + bool headless; + bool background; + bool experimental; int samples; - int2 tile_size; - TileOrder tile_order; - int start_resolution; - int denoising_start_sample; int pixel_size; int threads; - bool adaptive_sampling; + + /* Limit in seconds for how long path tracing is allowed to happen. + * Zero means no limit is applied. */ + double time_limit; bool use_profiling; - bool display_buffer_linear; - - DenoiseParams denoising; - - double cancel_timeout; - double reset_timeout; - double text_timeout; - double progressive_update_timeout; + bool use_auto_tile; + int tile_size; ShadingSystem shadingsystem; @@ -75,50 +71,32 @@ class SessionParams { SessionParams() { + headless = false; background = false; - progressive_refine = false; - progressive = false; experimental = false; samples = 1024; - tile_size = make_int2(64, 64); - start_resolution = INT_MAX; - denoising_start_sample = 0; pixel_size = 1; threads = 0; - adaptive_sampling = false; + time_limit = 0.0; use_profiling = false; - display_buffer_linear = false; - - cancel_timeout = 0.1; - reset_timeout = 0.1; - text_timeout = 1.0; - progressive_update_timeout = 1.0; + use_auto_tile = true; + tile_size = 2048; shadingsystem = SHADINGSYSTEM_SVM; - tile_order = TILE_CENTER; } - bool modified(const SessionParams ¶ms) + bool modified(const SessionParams ¶ms) const { /* Modified means we have to recreate the session, any parameter changes * that can be handled by an existing Session are omitted. */ - return !(device == params.device && background == params.background && - progressive_refine == params.progressive_refine && - progressive == params.progressive && experimental == params.experimental && - tile_size == params.tile_size && start_resolution == params.start_resolution && + return !(device == params.device && headless == params.headless && + background == params.background && experimental == params.experimental && pixel_size == params.pixel_size && threads == params.threads && - adaptive_sampling == params.adaptive_sampling && - use_profiling == params.use_profiling && - display_buffer_linear == params.display_buffer_linear && - cancel_timeout == params.cancel_timeout && reset_timeout == params.reset_timeout && - text_timeout == params.text_timeout && - progressive_update_timeout == params.progressive_update_timeout && - tile_order == params.tile_order && shadingsystem == params.shadingsystem && - denoising.type == params.denoising.type && - (denoising.use == params.denoising.use || (device.denoisers & denoising.type))); + use_profiling == params.use_profiling && shadingsystem == params.shadingsystem && + use_auto_tile == params.use_auto_tile && tile_size == params.tile_size); } }; @@ -131,34 +109,41 @@ class Session { public: Device *device; Scene *scene; - RenderBuffers *buffers; - DisplayBuffer *display; Progress progress; SessionParams params; - TileManager tile_manager; Stats stats; Profiler profiler; - function write_render_tile_cb; - function update_render_tile_cb; - function read_bake_tile_cb; + function write_render_tile_cb; + function update_render_tile_cb; + function read_render_tile_cb; - explicit Session(const SessionParams ¶ms); + /* Callback is invoked by tile manager whenever on-dist tiles storage file is closed after + * writing. Allows an engine integration to keep track of those files without worry about + * transfering the information when it needs to re-create session during rendering. */ + function full_buffer_written_cb; + + explicit Session(const SessionParams ¶ms, const SceneParams &scene_params); ~Session(); void start(); - void cancel(); - bool draw(BufferParams ¶ms, DeviceDrawParams &draw_params); + + /* When quick cancel is requested path tracing is cancelles as soon as possible, without waiting + * for the buffer to be uniformly sampled. */ + void cancel(bool quick = false); + + void draw(); void wait(); bool ready_to_reset(); - void reset(BufferParams ¶ms, int samples); - void set_pause(bool pause); - void set_samples(int samples); - void set_denoising(const DenoiseParams &denoising); - void set_denoising_start_sample(int sample); + void reset(const SessionParams &session_params, const BufferParams &buffer_params); - bool update_scene(); + void set_pause(bool pause); + + void set_samples(int samples); + void set_time_limit(double time_limit); + + void set_gpu_display(unique_ptr gpu_display); void device_free(); @@ -168,83 +153,95 @@ class Session { void collect_statistics(RenderStats *stats); + /* -------------------------------------------------------------------- + * Tile and tile pixels aceess. + */ + + bool has_multiple_render_tiles() const; + + /* Get size and offset (relative to the buffer's full x/y) of the currently rendering tile. */ + int2 get_render_tile_size() const; + int2 get_render_tile_offset() const; + + string_view get_render_tile_layer() const; + string_view get_render_tile_view() const; + + bool copy_render_tile_from_device(); + + bool get_render_tile_pixels(const string &pass_name, int num_components, float *pixels); + bool set_render_tile_pixels(const string &pass_name, int num_components, const float *pixels); + + /* -------------------------------------------------------------------- + * Full-frame on-disk storage. + */ + + /* Read given full-frame file from disk, perform needed processing and write it to the software + * via the write callback. */ + void process_full_buffer_from_disk(string_view filename); + protected: struct DelayedReset { thread_mutex mutex; bool do_reset; - BufferParams params; - int samples; + SessionParams session_params; + BufferParams buffer_params; } delayed_reset_; void run(); - bool run_update_for_next_iteration(); - bool run_wait_for_work(bool no_tiles); + /* Update for the new iteration of the main loop in run implementation (run_cpu and run_gpu). + * + * Will take care of the following things: + * - Delayed reset + * - Scene update + * - Tile manager advance + * - Render scheduler work request + * + * The updates are done in a proper order with proper locking around them, which guarantees + * that the device side of scene and render buffers are always in a consistent state. + * + * Returns render work which is to be rendered next. */ + RenderWork run_update_for_next_iteration(); + + /* Wait for rendering to be unpaused, or for new tiles for render to arrive. + * Returns true if new main render loop iteration is required after this function call. + * + * The `render_work` is the work which was scheduled by the render scheduler right before + * checking the pause. */ + bool run_wait_for_work(const RenderWork &render_work); + + void run_main_render_loop(); + + bool update_scene(int width, int height); void update_status_time(bool show_pause = false, bool show_done = false); - void render(bool use_denoise); - void copy_to_display_buffer(int sample); + void do_delayed_reset(); - void reset_(BufferParams ¶ms, int samples); - - void run_cpu(); - bool draw_cpu(BufferParams ¶ms, DeviceDrawParams &draw_params); - void reset_cpu(BufferParams ¶ms, int samples); - - void run_gpu(); - bool draw_gpu(BufferParams ¶ms, DeviceDrawParams &draw_params); - void reset_gpu(BufferParams ¶ms, int samples); - - bool render_need_denoise(bool &delayed); - - bool steal_tile(RenderTile &tile, Device *tile_device, thread_scoped_lock &tile_lock); - bool get_tile_stolen(); - bool acquire_tile(RenderTile &tile, Device *tile_device, uint tile_types); - void update_tile_sample(RenderTile &tile); - void release_tile(RenderTile &tile, const bool need_denoise); - - void map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device); - void unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device); - - bool device_use_gl_; + int2 get_effective_tile_size() const; thread *session_thread_; - volatile bool display_outdated_; - - volatile bool gpu_draw_ready_; - volatile bool gpu_need_display_buffer_update_; - thread_condition_variable gpu_need_display_buffer_update_cond_; - - bool pause_; - bool cancel_; - bool new_work_added_; + bool pause_ = false; + bool cancel_ = false; + bool new_work_added_ = false; thread_condition_variable pause_cond_; thread_mutex pause_mutex_; thread_mutex tile_mutex_; thread_mutex buffers_mutex_; - thread_mutex display_mutex_; - thread_condition_variable denoising_cond_; - thread_condition_variable tile_steal_cond_; - double reset_time_; - double last_update_time_; - double last_display_time_; + TileManager tile_manager_; + BufferParams buffer_params_; - RenderTile stolen_tile_; - typedef enum { - NOT_STEALING, /* There currently is no tile stealing in progress. */ - WAITING_FOR_TILE, /* A device is waiting for another device to release a tile. */ - RELEASING_TILE, /* A device has releasing a stealable tile. */ - GOT_TILE /* A device has released a stealable tile, which is now stored in stolen_tile. */ - } TileStealingState; - std::atomic tile_stealing_state_; - int stealable_tiles_; + /* Render scheduler is used to get work to be rendered with the current big tile. */ + RenderScheduler render_scheduler_; - /* progressive refine */ - bool update_progressive_refine(bool cancel); + /* Path tracer object. + * + * Is a single full-frame path tracer for interactive viewport rendering. + * A path tracer for the current big-tile for an offline rendering. */ + unique_ptr path_trace_; }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index 59b60904746..f6b23606e58 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -203,6 +203,7 @@ Shader::Shader() : Node(get_node_type()) has_surface = false; has_surface_transparent = false; has_surface_emission = false; + has_surface_raytrace = false; has_surface_bssrdf = false; has_volume = false; has_displacement = false; @@ -485,7 +486,7 @@ void ShaderManager::device_update(Device *device, device_update_specific(device, dscene, scene, progress); } -void ShaderManager::device_update_common(Device *device, +void ShaderManager::device_update_common(Device * /*device*/, DeviceScene *dscene, Scene *scene, Progress & /*progress*/) @@ -508,6 +509,8 @@ void ShaderManager::device_update_common(Device *device, flag |= SD_HAS_EMISSION; if (shader->has_surface_transparent && shader->get_use_transparent_shadow()) flag |= SD_HAS_TRANSPARENT_SHADOW; + if (shader->has_surface_raytrace) + flag |= SD_HAS_RAYTRACE; if (shader->has_volume) { flag |= SD_HAS_VOLUME; has_volumes = true; @@ -528,12 +531,10 @@ void ShaderManager::device_update_common(Device *device, flag |= SD_NEED_VOLUME_ATTRIBUTES; if (shader->has_bssrdf_bump) flag |= SD_HAS_BSSRDF_BUMP; - if (device->info.has_volume_decoupled) { - if (shader->get_volume_sampling_method() == VOLUME_SAMPLING_EQUIANGULAR) - flag |= SD_VOLUME_EQUIANGULAR; - if (shader->get_volume_sampling_method() == VOLUME_SAMPLING_MULTIPLE_IMPORTANCE) - flag |= SD_VOLUME_MIS; - } + if (shader->get_volume_sampling_method() == VOLUME_SAMPLING_EQUIANGULAR) + flag |= SD_VOLUME_EQUIANGULAR; + if (shader->get_volume_sampling_method() == VOLUME_SAMPLING_MULTIPLE_IMPORTANCE) + flag |= SD_VOLUME_MIS; if (shader->get_volume_interpolation_method() == VOLUME_INTERPOLATION_CUBIC) flag |= SD_VOLUME_CUBIC; if (shader->has_bump) @@ -682,39 +683,35 @@ void ShaderManager::add_default(Scene *scene) } } -void ShaderManager::get_requested_graph_features(ShaderGraph *graph, - DeviceRequestedFeatures *requested_features) +uint ShaderManager::get_graph_kernel_features(ShaderGraph *graph) { + uint kernel_features = 0; + foreach (ShaderNode *node, graph->nodes) { - requested_features->max_nodes_group = max(requested_features->max_nodes_group, - node->get_group()); - requested_features->nodes_features |= node->get_feature(); + kernel_features |= node->get_feature(); if (node->special_type == SHADER_SPECIAL_TYPE_CLOSURE) { BsdfBaseNode *bsdf_node = static_cast(node); if (CLOSURE_IS_VOLUME(bsdf_node->get_closure_type())) { - requested_features->nodes_features |= NODE_FEATURE_VOLUME; + kernel_features |= KERNEL_FEATURE_NODE_VOLUME; } else if (CLOSURE_IS_PRINCIPLED(bsdf_node->get_closure_type())) { - requested_features->use_principled = true; + kernel_features |= KERNEL_FEATURE_PRINCIPLED; } } if (node->has_surface_bssrdf()) { - requested_features->use_subsurface = true; + kernel_features |= KERNEL_FEATURE_SUBSURFACE; } if (node->has_surface_transparent()) { - requested_features->use_transparent = true; - } - if (node->has_raytrace()) { - requested_features->use_shader_raytrace = true; + kernel_features |= KERNEL_FEATURE_TRANSPARENT; } } + + return kernel_features; } -void ShaderManager::get_requested_features(Scene *scene, - DeviceRequestedFeatures *requested_features) +uint ShaderManager::get_kernel_features(Scene *scene) { - requested_features->max_nodes_group = NODE_GROUP_LEVEL_0; - requested_features->nodes_features = 0; + uint kernel_features = KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION; for (int i = 0; i < scene->shaders.size(); i++) { Shader *shader = scene->shaders[i]; if (!shader->reference_count()) { @@ -722,21 +719,22 @@ void ShaderManager::get_requested_features(Scene *scene, } /* Gather requested features from all the nodes from the graph nodes. */ - get_requested_graph_features(shader->graph, requested_features); + kernel_features |= get_graph_kernel_features(shader->graph); ShaderNode *output_node = shader->graph->output(); if (output_node->input("Displacement")->link != NULL) { - requested_features->nodes_features |= NODE_FEATURE_BUMP; + kernel_features |= KERNEL_FEATURE_NODE_BUMP; if (shader->get_displacement_method() == DISPLACE_BOTH) { - requested_features->nodes_features |= NODE_FEATURE_BUMP_STATE; - requested_features->max_nodes_group = max(requested_features->max_nodes_group, - NODE_GROUP_LEVEL_1); + kernel_features |= KERNEL_FEATURE_NODE_BUMP_STATE; } } /* On top of volume nodes, also check if we need volume sampling because - * e.g. an Emission node would slip through the NODE_FEATURE_VOLUME check */ - if (shader->has_volume) - requested_features->use_volume |= true; + * e.g. an Emission node would slip through the KERNEL_FEATURE_NODE_VOLUME check */ + if (shader->has_volume) { + kernel_features |= KERNEL_FEATURE_VOLUME; + } } + + return kernel_features; } void ShaderManager::free_memory() diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index c65cac351a4..5f9adea3949 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -38,7 +38,6 @@ CCL_NAMESPACE_BEGIN class Device; class DeviceScene; -class DeviceRequestedFeatures; class Mesh; class Progress; class Scene; @@ -117,6 +116,7 @@ class Shader : public Node { bool has_surface; bool has_surface_emission; bool has_surface_transparent; + bool has_surface_raytrace; bool has_volume; bool has_displacement; bool has_surface_bssrdf; @@ -216,7 +216,7 @@ class ShaderManager { static void add_default(Scene *scene); /* Selective nodes compilation. */ - void get_requested_features(Scene *scene, DeviceRequestedFeatures *requested_features); + uint get_kernel_features(Scene *scene); static void free_memory(); @@ -244,8 +244,7 @@ class ShaderManager { size_t beckmann_table_offset; - void get_requested_graph_features(ShaderGraph *graph, - DeviceRequestedFeatures *requested_features); + uint get_graph_kernel_features(ShaderGraph *graph); thread_spin_lock attribute_lock_; diff --git a/intern/cycles/render/stats.cpp b/intern/cycles/render/stats.cpp index 2c6273842e2..73eb7e21ff9 100644 --- a/intern/cycles/render/stats.cpp +++ b/intern/cycles/render/stats.cpp @@ -264,53 +264,34 @@ void RenderStats::collect_profiling(Scene *scene, Profiler &prof) has_profiling = true; kernel = NamedNestedSampleStats("Total render time", prof.get_event(PROFILING_UNKNOWN)); - kernel.add_entry("Ray setup", prof.get_event(PROFILING_RAY_SETUP)); - kernel.add_entry("Result writing", prof.get_event(PROFILING_WRITE_RESULT)); + kernel.add_entry("Intersect Closest", prof.get_event(PROFILING_INTERSECT_CLOSEST)); + kernel.add_entry("Intersect Shadow", prof.get_event(PROFILING_INTERSECT_SHADOW)); + kernel.add_entry("Intersect Subsurface", prof.get_event(PROFILING_INTERSECT_SUBSURFACE)); + kernel.add_entry("Intersect Volume Stack", prof.get_event(PROFILING_INTERSECT_VOLUME_STACK)); - NamedNestedSampleStats &integrator = kernel.add_entry("Path integration", - prof.get_event(PROFILING_PATH_INTEGRATE)); - integrator.add_entry("Scene intersection", prof.get_event(PROFILING_SCENE_INTERSECT)); - integrator.add_entry("Indirect emission", prof.get_event(PROFILING_INDIRECT_EMISSION)); - integrator.add_entry("Volumes", prof.get_event(PROFILING_VOLUME)); + NamedNestedSampleStats &surface = kernel.add_entry("Shade Surface", 0); + surface.add_entry("Setup", prof.get_event(PROFILING_SHADE_SURFACE_SETUP)); + surface.add_entry("Shader Evaluation", prof.get_event(PROFILING_SHADE_SURFACE_EVAL)); + surface.add_entry("Render Passes", prof.get_event(PROFILING_SHADE_SURFACE_PASSES)); + surface.add_entry("Direct Light", prof.get_event(PROFILING_SHADE_SURFACE_DIRECT_LIGHT)); + surface.add_entry("Indirect Light", prof.get_event(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT)); + surface.add_entry("Ambient Occlusion", prof.get_event(PROFILING_SHADE_SURFACE_AO)); - NamedNestedSampleStats &shading = integrator.add_entry("Shading", 0); - shading.add_entry("Shader Setup", prof.get_event(PROFILING_SHADER_SETUP)); - shading.add_entry("Shader Eval", prof.get_event(PROFILING_SHADER_EVAL)); - shading.add_entry("Shader Apply", prof.get_event(PROFILING_SHADER_APPLY)); - shading.add_entry("Ambient Occlusion", prof.get_event(PROFILING_AO)); - shading.add_entry("Subsurface", prof.get_event(PROFILING_SUBSURFACE)); + NamedNestedSampleStats &volume = kernel.add_entry("Shade Volume", 0); + volume.add_entry("Setup", prof.get_event(PROFILING_SHADE_VOLUME_SETUP)); + volume.add_entry("Integrate", prof.get_event(PROFILING_SHADE_VOLUME_INTEGRATE)); + volume.add_entry("Direct Light", prof.get_event(PROFILING_SHADE_VOLUME_DIRECT_LIGHT)); + volume.add_entry("Indirect Light", prof.get_event(PROFILING_SHADE_VOLUME_INDIRECT_LIGHT)); - integrator.add_entry("Connect Light", prof.get_event(PROFILING_CONNECT_LIGHT)); - integrator.add_entry("Surface Bounce", prof.get_event(PROFILING_SURFACE_BOUNCE)); + NamedNestedSampleStats &shadow = kernel.add_entry("Shade Shadow", 0); + shadow.add_entry("Setup", prof.get_event(PROFILING_SHADE_SHADOW_SETUP)); + shadow.add_entry("Surface", prof.get_event(PROFILING_SHADE_SHADOW_SURFACE)); + shadow.add_entry("Volume", prof.get_event(PROFILING_SHADE_SHADOW_VOLUME)); - NamedNestedSampleStats &intersection = kernel.add_entry("Intersection", 0); - intersection.add_entry("Full Intersection", prof.get_event(PROFILING_INTERSECT)); - intersection.add_entry("Local Intersection", prof.get_event(PROFILING_INTERSECT_LOCAL)); - intersection.add_entry("Shadow All Intersection", - prof.get_event(PROFILING_INTERSECT_SHADOW_ALL)); - intersection.add_entry("Volume Intersection", prof.get_event(PROFILING_INTERSECT_VOLUME)); - intersection.add_entry("Volume All Intersection", - prof.get_event(PROFILING_INTERSECT_VOLUME_ALL)); - - NamedNestedSampleStats &closure = kernel.add_entry("Closures", 0); - closure.add_entry("Surface Closure Evaluation", prof.get_event(PROFILING_CLOSURE_EVAL)); - closure.add_entry("Surface Closure Sampling", prof.get_event(PROFILING_CLOSURE_SAMPLE)); - closure.add_entry("Volume Closure Evaluation", prof.get_event(PROFILING_CLOSURE_VOLUME_EVAL)); - closure.add_entry("Volume Closure Sampling", prof.get_event(PROFILING_CLOSURE_VOLUME_SAMPLE)); - - NamedNestedSampleStats &denoising = kernel.add_entry("Denoising", - prof.get_event(PROFILING_DENOISING)); - denoising.add_entry("Construct Transform", - prof.get_event(PROFILING_DENOISING_CONSTRUCT_TRANSFORM)); - denoising.add_entry("Reconstruct", prof.get_event(PROFILING_DENOISING_RECONSTRUCT)); - - NamedNestedSampleStats &prefilter = denoising.add_entry("Prefiltering", 0); - prefilter.add_entry("Divide Shadow", prof.get_event(PROFILING_DENOISING_DIVIDE_SHADOW)); - prefilter.add_entry("Non-Local means", prof.get_event(PROFILING_DENOISING_NON_LOCAL_MEANS)); - prefilter.add_entry("Get Feature", prof.get_event(PROFILING_DENOISING_GET_FEATURE)); - prefilter.add_entry("Detect Outliers", prof.get_event(PROFILING_DENOISING_DETECT_OUTLIERS)); - prefilter.add_entry("Combine Halves", prof.get_event(PROFILING_DENOISING_COMBINE_HALVES)); + NamedNestedSampleStats &light = kernel.add_entry("Shade Light", 0); + light.add_entry("Setup", prof.get_event(PROFILING_SHADE_LIGHT_SETUP)); + light.add_entry("Shader Evaluation", prof.get_event(PROFILING_SHADE_LIGHT_EVAL)); shaders.entries.clear(); foreach (Shader *shader, scene->shaders) { diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index dcb3976e15c..2379eb775a0 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -446,6 +446,8 @@ void SVMCompiler::generate_node(ShaderNode *node, ShaderNodeSet &done) if (current_type == SHADER_TYPE_SURFACE) { if (node->has_spatial_varying()) current_shader->has_surface_spatial_varying = true; + if (node->get_feature() & KERNEL_FEATURE_NODE_RAYTRACE) + current_shader->has_surface_raytrace = true; } else if (current_type == SHADER_TYPE_VOLUME) { if (node->has_spatial_varying()) @@ -492,6 +494,13 @@ void SVMCompiler::generate_svm_nodes(const ShaderNodeSet &nodes, CompilerState * void SVMCompiler::generate_closure_node(ShaderNode *node, CompilerState *state) { + /* Skip generating closure that are not supported or needed for a particular + * type of shader. For example a BSDF in a volume shader. */ + const int node_feature = node->get_feature(); + if ((state->node_feature_mask & node_feature) != node_feature) { + return; + } + /* execute dependencies for closure */ foreach (ShaderInput *in, node->inputs) { if (in->link != NULL) { @@ -555,7 +564,7 @@ void SVMCompiler::find_aov_nodes_and_dependencies(ShaderNodeSet &aov_nodes, foreach (ShaderNode *node, graph->nodes) { if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) { OutputAOVNode *aov_node = static_cast(node); - if (aov_node->slot >= 0) { + if (aov_node->offset >= 0) { aov_nodes.insert(aov_node); foreach (ShaderInput *in, node->inputs) { if (in->link != NULL) { @@ -785,17 +794,21 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty case SHADER_TYPE_SURFACE: /* generate surface shader */ generate = true; shader->has_surface = true; + state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_SURFACE; break; case SHADER_TYPE_VOLUME: /* generate volume shader */ generate = true; shader->has_volume = true; + state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_VOLUME; break; case SHADER_TYPE_DISPLACEMENT: /* generate displacement shader */ generate = true; shader->has_displacement = true; + state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_DISPLACEMENT; break; case SHADER_TYPE_BUMP: /* generate bump shader */ generate = true; + state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_BUMP; break; default: break; @@ -867,6 +880,7 @@ void SVMCompiler::compile(Shader *shader, array &svm_nodes, int index, Sum shader->has_surface = false; shader->has_surface_emission = false; shader->has_surface_transparent = false; + shader->has_surface_raytrace = false; shader->has_surface_bssrdf = false; shader->has_bump = has_bump; shader->has_bssrdf_bump = has_bump; @@ -964,6 +978,7 @@ SVMCompiler::CompilerState::CompilerState(ShaderGraph *graph) max_id = max(node->id, max_id); } nodes_done_flag.resize(max_id + 1, false); + node_feature_mask = 0; } CCL_NAMESPACE_END diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h index d23ff3e2a47..0353c393ae4 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/render/svm.h @@ -192,6 +192,9 @@ class SVMCompiler { * all areas to use this flags array. */ vector nodes_done_flag; + + /* Node features that can be compiled. */ + uint node_feature_mask; }; void stack_clear_temporary(ShaderNode *node); diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 375c9fd8e09..eed75cc2372 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -16,601 +16,559 @@ #include "render/tile.h" +#include + +#include "graph/node.h" +#include "render/background.h" +#include "render/film.h" +#include "render/integrator.h" +#include "render/scene.h" #include "util/util_algorithm.h" #include "util/util_foreach.h" +#include "util/util_logging.h" +#include "util/util_path.h" +#include "util/util_string.h" +#include "util/util_system.h" #include "util/util_types.h" CCL_NAMESPACE_BEGIN -namespace { +/* -------------------------------------------------------------------- + * Internal functions. + */ -class TileComparator { - public: - TileComparator(TileOrder order_, int2 center_, Tile *tiles_) - : order(order_), center(center_), tiles(tiles_) - { - } +static const char *ATTR_PASSES_COUNT = "cycles.passes.count"; +static const char *ATTR_PASS_SOCKET_PREFIX_FORMAT = "cycles.passes.%d."; +static const char *ATTR_BUFFER_SOCKET_PREFIX = "cycles.buffer."; +static const char *ATTR_DENOISE_SOCKET_PREFIX = "cycles.denoise."; - bool operator()(int a, int b) - { - switch (order) { - case TILE_CENTER: { - float2 dist_a = make_float2(center.x - (tiles[a].x + tiles[a].w / 2), - center.y - (tiles[a].y + tiles[a].h / 2)); - float2 dist_b = make_float2(center.x - (tiles[b].x + tiles[b].w / 2), - center.y - (tiles[b].y + tiles[b].h / 2)); - return dot(dist_a, dist_a) < dot(dist_b, dist_b); - } - case TILE_LEFT_TO_RIGHT: - return (tiles[a].x == tiles[b].x) ? (tiles[a].y < tiles[b].y) : (tiles[a].x < tiles[b].x); - case TILE_RIGHT_TO_LEFT: - return (tiles[a].x == tiles[b].x) ? (tiles[a].y < tiles[b].y) : (tiles[a].x > tiles[b].x); - case TILE_TOP_TO_BOTTOM: - return (tiles[a].y == tiles[b].y) ? (tiles[a].x < tiles[b].x) : (tiles[a].y > tiles[b].y); - case TILE_BOTTOM_TO_TOP: - default: - return (tiles[a].y == tiles[b].y) ? (tiles[a].x < tiles[b].x) : (tiles[a].y < tiles[b].y); - } - } +/* Global counter of ToleManager object instances. */ +static std::atomic g_instance_index = 0; - protected: - TileOrder order; - int2 center; - Tile *tiles; -}; - -inline int2 hilbert_index_to_pos(int n, int d) +/* Construct names of EXR channels which will ensure order of all channels to match exact offsets + * in render buffers corresponding to the given passes. + * + * Returns `std` datatypes so that it can be assigned directly to the OIIO's `ImageSpec`. */ +static std::vector exr_channel_names_for_passes(const BufferParams &buffer_params) { - int2 r, xy = make_int2(0, 0); - for (int s = 1; s < n; s *= 2) { - r.x = (d >> 1) & 1; - r.y = (d ^ r.x) & 1; - if (!r.y) { - if (r.x) { - xy = make_int2(s - 1, s - 1) - xy; - } - swap(xy.x, xy.y); + static const char *component_suffixes[] = {"R", "G", "B", "A"}; + + int pass_index = 0; + int num_channels = 0; + std::vector channel_names; + for (const BufferPass &pass : buffer_params.passes) { + if (pass.offset == PASS_UNUSED) { + continue; } - xy += r * make_int2(s, s); - d >>= 2; + + const PassInfo pass_info = pass.get_info(); + num_channels += pass_info.num_components; + + /* EXR canonically expects first part of channel names to be sorted alphabetically, which is + * not guaranteed to be the case with passes names. Assign a prefix based on the pass index + * with a fixed width to ensure ordering. This makes it possible to dump existing render + * buffers memory to disk and read it back without doing extra mapping. */ + const string prefix = string_printf("%08d", pass_index); + + const string channel_name_prefix = prefix + string(pass.name) + "."; + + for (int i = 0; i < pass_info.num_components; ++i) { + channel_names.push_back(channel_name_prefix + component_suffixes[i]); + } + + ++pass_index; } - return xy; + + return channel_names; } -enum SpiralDirection { - DIRECTION_UP, - DIRECTION_LEFT, - DIRECTION_DOWN, - DIRECTION_RIGHT, -}; - -} /* namespace */ - -TileManager::TileManager(bool progressive_, - int num_samples_, - int2 tile_size_, - int start_resolution_, - bool preserve_tile_device_, - bool background_, - TileOrder tile_order_, - int num_devices_, - int pixel_size_) +inline string node_socket_attribute_name(const SocketType &socket, const string &attr_name_prefix) { - progressive = progressive_; - tile_size = tile_size_; - tile_order = tile_order_; - start_resolution = start_resolution_; - pixel_size = pixel_size_; - slice_overlap = 0; - num_samples = num_samples_; - num_devices = num_devices_; - preserve_tile_device = preserve_tile_device_; - background = background_; - schedule_denoising = false; + return attr_name_prefix + string(socket.name); +} - range_start_sample = 0; - range_num_samples = -1; +template +static bool node_socket_generic_to_image_spec_atttributes( + ImageSpec *image_spec, + const Node *node, + const SocketType &socket, + const string &attr_name_prefix, + const ValidateValueFunc &validate_value_func, + const GetValueFunc &get_value_func) +{ + if (!validate_value_func(node, socket)) { + return false; + } - BufferParams buffer_params; - reset(buffer_params, 0); + image_spec->attribute(node_socket_attribute_name(socket, attr_name_prefix), + get_value_func(node, socket)); + + return true; +} + +static bool node_socket_to_image_spec_atttributes(ImageSpec *image_spec, + const Node *node, + const SocketType &socket, + const string &attr_name_prefix) +{ + const string attr_name = node_socket_attribute_name(socket, attr_name_prefix); + + switch (socket.type) { + case SocketType::ENUM: { + const ustring value = node->get_string(socket); + + /* Validate that the node is consistent with the node type definition. */ + const NodeEnum &enum_values = *socket.enum_values; + if (!enum_values.exists(value)) { + LOG(DFATAL) << "Node enum contains invalid value " << value; + return false; + } + + image_spec->attribute(attr_name, value); + + return true; + } + + case SocketType::STRING: + image_spec->attribute(attr_name, node->get_string(socket)); + return true; + + case SocketType::INT: + image_spec->attribute(attr_name, node->get_int(socket)); + return true; + + case SocketType::FLOAT: + image_spec->attribute(attr_name, node->get_float(socket)); + return true; + + case SocketType::BOOLEAN: + image_spec->attribute(attr_name, node->get_bool(socket)); + return true; + + default: + LOG(DFATAL) << "Unhandled socket type " << socket.type << ", should never happen."; + return false; + } +} + +static bool node_socket_from_image_spec_atttributes(Node *node, + const SocketType &socket, + const ImageSpec &image_spec, + const string &attr_name_prefix) +{ + const string attr_name = node_socket_attribute_name(socket, attr_name_prefix); + + switch (socket.type) { + case SocketType::ENUM: { + /* TODO(sergey): Avoid construction of `ustring` by using `string_view` in the Node API. */ + const ustring value(image_spec.get_string_attribute(attr_name, "")); + + /* Validate that the node is consistent with the node type definition. */ + const NodeEnum &enum_values = *socket.enum_values; + if (!enum_values.exists(value)) { + LOG(ERROR) << "Invalid enumerator value " << value; + return false; + } + + node->set(socket, enum_values[value]); + + return true; + } + + case SocketType::STRING: + /* TODO(sergey): Avoid construction of `ustring` by using `string_view` in the Node API. */ + node->set(socket, ustring(image_spec.get_string_attribute(attr_name, ""))); + return true; + + case SocketType::INT: + node->set(socket, image_spec.get_int_attribute(attr_name, 0)); + return true; + + case SocketType::FLOAT: + node->set(socket, image_spec.get_float_attribute(attr_name, 0)); + return true; + + case SocketType::BOOLEAN: + node->set(socket, static_cast(image_spec.get_int_attribute(attr_name, 0))); + return true; + + default: + LOG(DFATAL) << "Unhandled socket type " << socket.type << ", should never happen."; + return false; + } +} + +static bool node_to_image_spec_atttributes(ImageSpec *image_spec, + const Node *node, + const string &attr_name_prefix) +{ + for (const SocketType &socket : node->type->inputs) { + if (!node_socket_to_image_spec_atttributes(image_spec, node, socket, attr_name_prefix)) { + return false; + } + } + + return true; +} + +static bool node_from_image_spec_atttributes(Node *node, + const ImageSpec &image_spec, + const string &attr_name_prefix) +{ + for (const SocketType &socket : node->type->inputs) { + if (!node_socket_from_image_spec_atttributes(node, socket, image_spec, attr_name_prefix)) { + return false; + } + } + + return true; +} + +static bool buffer_params_to_image_spec_atttributes(ImageSpec *image_spec, + const BufferParams &buffer_params) +{ + if (!node_to_image_spec_atttributes(image_spec, &buffer_params, ATTR_BUFFER_SOCKET_PREFIX)) { + return false; + } + + /* Passes storage is not covered by the node socket. so "expand" the loop manually. */ + + const int num_passes = buffer_params.passes.size(); + image_spec->attribute(ATTR_PASSES_COUNT, num_passes); + + for (int pass_index = 0; pass_index < num_passes; ++pass_index) { + const string attr_name_prefix = string_printf(ATTR_PASS_SOCKET_PREFIX_FORMAT, pass_index); + + const BufferPass *pass = &buffer_params.passes[pass_index]; + if (!node_to_image_spec_atttributes(image_spec, pass, attr_name_prefix)) { + return false; + } + } + + return true; +} + +static bool buffer_params_from_image_spec_atttributes(BufferParams *buffer_params, + const ImageSpec &image_spec) +{ + if (!node_from_image_spec_atttributes(buffer_params, image_spec, ATTR_BUFFER_SOCKET_PREFIX)) { + return false; + } + + /* Passes storage is not covered by the node socket. so "expand" the loop manually. */ + + const int num_passes = image_spec.get_int_attribute(ATTR_PASSES_COUNT, 0); + if (num_passes == 0) { + LOG(ERROR) << "Missing passes count attribute."; + return false; + } + + for (int pass_index = 0; pass_index < num_passes; ++pass_index) { + const string attr_name_prefix = string_printf(ATTR_PASS_SOCKET_PREFIX_FORMAT, pass_index); + + BufferPass pass; + + if (!node_from_image_spec_atttributes(&pass, image_spec, attr_name_prefix)) { + return false; + } + + buffer_params->passes.emplace_back(std::move(pass)); + } + + buffer_params->update_passes(); + + return true; +} + +/* Configure image specification for the given buffer parameters and passes. + * + * Image channels will ber strictly ordered to match content of corresponding buffer, and the + * metadata will be set so that the render buffers and passes can be reconstructed from it. + * + * If the tile size different from (0, 0) the image specification will be configured to use the + * given tile size for tiled IO. */ +static bool configure_image_spec_from_buffer(ImageSpec *image_spec, + const BufferParams &buffer_params, + const int2 tile_size = make_int2(0, 0)) +{ + const std::vector channel_names = exr_channel_names_for_passes(buffer_params); + const int num_channels = channel_names.size(); + + *image_spec = ImageSpec( + buffer_params.width, buffer_params.height, num_channels, TypeDesc::FLOAT); + + image_spec->channelnames = move(channel_names); + + if (!buffer_params_to_image_spec_atttributes(image_spec, buffer_params)) { + return false; + } + + if (tile_size.x != 0 || tile_size.y != 0) { + DCHECK_GT(tile_size.x, 0); + DCHECK_GT(tile_size.y, 0); + + image_spec->tile_width = tile_size.x; + image_spec->tile_height = tile_size.y; + } + + return true; +} + +/* -------------------------------------------------------------------- + * Tile Manager. + */ + +TileManager::TileManager() +{ + /* Use process ID to separate different processes. + * To ensure uniqueness from within a process use combination of object address and instance + * index. This solves problem of possible object re-allocation at the same time, and solves + * possible conflict when the counter overflows while there are still active instances of the + * class. */ + const int tile_manager_id = g_instance_index.fetch_add(1, std::memory_order_relaxed); + tile_file_unique_part_ = to_string(system_self_process_id()) + "-" + + to_string(reinterpret_cast(this)) + "-" + + to_string(tile_manager_id); } TileManager::~TileManager() { } -void TileManager::device_free() +void TileManager::reset_scheduling(const BufferParams ¶ms, int2 tile_size) { - if (schedule_denoising || progressive) { - for (int i = 0; i < state.tiles.size(); i++) { - delete state.tiles[i].buffers; - state.tiles[i].buffers = NULL; - } - } + VLOG(3) << "Using tile size of " << tile_size; - state.tiles.clear(); + close_tile_output(); + + tile_size_ = tile_size; + + tile_state_.num_tiles_x = divide_up(params.width, tile_size_.x); + tile_state_.num_tiles_y = divide_up(params.height, tile_size_.y); + tile_state_.num_tiles = tile_state_.num_tiles_x * tile_state_.num_tiles_y; + + tile_state_.next_tile_index = 0; + + tile_state_.current_tile = Tile(); } -static int get_divider(int w, int h, int start_resolution) +void TileManager::update(const BufferParams ¶ms, const Scene *scene) { - int divider = 1; - if (start_resolution != INT_MAX) { - while (w * h > start_resolution * start_resolution) { - w = max(1, w / 2); - h = max(1, h / 2); + DCHECK_NE(params.pass_stride, -1); - divider <<= 1; - } - } - return divider; -} + buffer_params_ = params; -void TileManager::reset(BufferParams ¶ms_, int num_samples_) -{ - params = params_; + /* TODO(sergey): Proper Error handling, so that if configuration has failed we dont' attempt to + * write to a partially configured file. */ + configure_image_spec_from_buffer(&write_state_.image_spec, buffer_params_, tile_size_); - set_samples(num_samples_); - - state.buffer = BufferParams(); - state.sample = range_start_sample - 1; - state.num_tiles = 0; - state.num_samples = 0; - state.resolution_divider = get_divider(params.width, params.height, start_resolution); - state.render_tiles.clear(); - state.denoising_tiles.clear(); - device_free(); -} - -void TileManager::set_samples(int num_samples_) -{ - num_samples = num_samples_; - - /* No real progress indication is possible when using unlimited samples. */ - if (num_samples == INT_MAX) { - state.total_pixel_samples = 0; - } - else { - uint64_t pixel_samples = 0; - /* While rendering in the viewport, the initial preview resolution is increased to the native - * resolution before the actual rendering begins. Therefore, additional pixel samples will be - * rendered. */ - int divider = max(get_divider(params.width, params.height, start_resolution) / 2, pixel_size); - while (divider > pixel_size) { - int image_w = max(1, params.width / divider); - int image_h = max(1, params.height / divider); - pixel_samples += image_w * image_h; - divider >>= 1; - } - - int image_w = max(1, params.width / divider); - int image_h = max(1, params.height / divider); - state.total_pixel_samples = pixel_samples + - (uint64_t)get_num_effective_samples() * image_w * image_h; - if (schedule_denoising) { - state.total_pixel_samples += params.width * params.height; - } - } -} - -/* If sliced is false, splits image into tiles and assigns equal amount of tiles to every render - * device. If sliced is true, slice image into as much pieces as how many devices are rendering - * this image. */ -int TileManager::gen_tiles(bool sliced) -{ - int resolution = state.resolution_divider; - int image_w = max(1, params.width / resolution); - int image_h = max(1, params.height / resolution); - int2 center = make_int2(image_w / 2, image_h / 2); - - int num = preserve_tile_device || sliced ? min(image_h, num_devices) : 1; - int slice_num = sliced ? num : 1; - int tile_w = (tile_size.x >= image_w) ? 1 : divide_up(image_w, tile_size.x); - - device_free(); - state.render_tiles.clear(); - state.denoising_tiles.clear(); - state.render_tiles.resize(num); - state.denoising_tiles.resize(num); - state.tile_stride = tile_w; - vector>::iterator tile_list; - tile_list = state.render_tiles.begin(); - - if (tile_order == TILE_HILBERT_SPIRAL) { - assert(!sliced && slice_overlap == 0); - - int tile_h = (tile_size.y >= image_h) ? 1 : divide_up(image_h, tile_size.y); - state.tiles.resize(tile_w * tile_h); - - /* Size of blocks in tiles, must be a power of 2 */ - const int hilbert_size = (max(tile_size.x, tile_size.y) <= 12) ? 8 : 4; - - int tiles_per_device = divide_up(tile_w * tile_h, num); - int cur_device = 0, cur_tiles = 0; - - int2 block_size = tile_size * make_int2(hilbert_size, hilbert_size); - /* Number of blocks to fill the image */ - int blocks_x = (block_size.x >= image_w) ? 1 : divide_up(image_w, block_size.x); - int blocks_y = (block_size.y >= image_h) ? 1 : divide_up(image_h, block_size.y); - int n = max(blocks_x, blocks_y) | 0x1; /* Side length of the spiral (must be odd) */ - /* Offset of spiral (to keep it centered) */ - int2 offset = make_int2((image_w - n * block_size.x) / 2, (image_h - n * block_size.y) / 2); - offset = (offset / tile_size) * tile_size; /* Round to tile border. */ - - int2 block = make_int2(0, 0); /* Current block */ - SpiralDirection prev_dir = DIRECTION_UP, dir = DIRECTION_UP; - for (int i = 0;;) { - /* Generate the tiles in the current block. */ - for (int hilbert_index = 0; hilbert_index < hilbert_size * hilbert_size; hilbert_index++) { - int2 tile, hilbert_pos = hilbert_index_to_pos(hilbert_size, hilbert_index); - /* Rotate block according to spiral direction. */ - if (prev_dir == DIRECTION_UP && dir == DIRECTION_UP) { - tile = make_int2(hilbert_pos.y, hilbert_pos.x); - } - else if (dir == DIRECTION_LEFT || prev_dir == DIRECTION_LEFT) { - tile = hilbert_pos; - } - else if (dir == DIRECTION_DOWN) { - tile = make_int2(hilbert_size - 1 - hilbert_pos.y, hilbert_size - 1 - hilbert_pos.x); - } - else { - tile = make_int2(hilbert_size - 1 - hilbert_pos.x, hilbert_size - 1 - hilbert_pos.y); - } - - int2 pos = block * block_size + tile * tile_size + offset; - /* Only add tiles which are in the image (tiles outside of the image can be generated since - * the spiral is always square). */ - if (pos.x >= 0 && pos.y >= 0 && pos.x < image_w && pos.y < image_h) { - int w = min(tile_size.x, image_w - pos.x); - int h = min(tile_size.y, image_h - pos.y); - int2 ipos = pos / tile_size; - int idx = ipos.y * tile_w + ipos.x; - state.tiles[idx] = Tile(idx, pos.x, pos.y, w, h, cur_device, Tile::RENDER); - tile_list->push_front(idx); - cur_tiles++; - - if (cur_tiles == tiles_per_device) { - tile_list++; - cur_tiles = 0; - cur_device++; - } - } - } - - /* Stop as soon as the spiral has reached the center block. */ - if (block.x == (n - 1) / 2 && block.y == (n - 1) / 2) - break; - - /* Advance to next block. */ - prev_dir = dir; - switch (dir) { - case DIRECTION_UP: - block.y++; - if (block.y == (n - i - 1)) { - dir = DIRECTION_LEFT; - } - break; - case DIRECTION_LEFT: - block.x++; - if (block.x == (n - i - 1)) { - dir = DIRECTION_DOWN; - } - break; - case DIRECTION_DOWN: - block.y--; - if (block.y == i) { - dir = DIRECTION_RIGHT; - } - break; - case DIRECTION_RIGHT: - block.x--; - if (block.x == i + 1) { - dir = DIRECTION_UP; - i++; - } - break; - } - } - return tile_w * tile_h; - } - - int idx = 0; - for (int slice = 0; slice < slice_num; slice++) { - int slice_y = (image_h / slice_num) * slice; - int slice_h = (slice == slice_num - 1) ? image_h - slice * (image_h / slice_num) : - image_h / slice_num; - - if (slice_overlap != 0) { - int slice_y_offset = max(slice_y - slice_overlap, 0); - slice_h = min(slice_y + slice_h + slice_overlap, image_h) - slice_y_offset; - slice_y = slice_y_offset; - } - - int tile_h = (tile_size.y >= slice_h) ? 1 : divide_up(slice_h, tile_size.y); - - int tiles_per_device = divide_up(tile_w * tile_h, num); - int cur_device = 0, cur_tiles = 0; - - for (int tile_y = 0; tile_y < tile_h; tile_y++) { - for (int tile_x = 0; tile_x < tile_w; tile_x++, idx++) { - int x = tile_x * tile_size.x; - int y = tile_y * tile_size.y; - int w = (tile_x == tile_w - 1) ? image_w - x : tile_size.x; - int h = (tile_y == tile_h - 1) ? slice_h - y : tile_size.y; - - state.tiles.push_back( - Tile(idx, x, y + slice_y, w, h, sliced ? slice : cur_device, Tile::RENDER)); - tile_list->push_back(idx); - - if (!sliced) { - cur_tiles++; - - if (cur_tiles == tiles_per_device) { - /* Tiles are already generated in Bottom-to-Top order, so no sort is necessary in that - * case. */ - if (tile_order != TILE_BOTTOM_TO_TOP) { - tile_list->sort(TileComparator(tile_order, center, &state.tiles[0])); - } - tile_list++; - cur_tiles = 0; - cur_device++; - } - } - } - } - if (sliced) { - tile_list++; - } - } - - return idx; -} - -void TileManager::gen_render_tiles() -{ - /* Regenerate just the render tiles for progressive render. */ - foreach (Tile &tile, state.tiles) { - tile.state = Tile::RENDER; - state.render_tiles[tile.device].push_back(tile.index); - } -} - -void TileManager::set_tiles() -{ - int resolution = state.resolution_divider; - int image_w = max(1, params.width / resolution); - int image_h = max(1, params.height / resolution); - - state.num_tiles = gen_tiles(!background); - - state.buffer.width = image_w; - state.buffer.height = image_h; - - state.buffer.full_x = params.full_x / resolution; - state.buffer.full_y = params.full_y / resolution; - state.buffer.full_width = max(1, params.full_width / resolution); - state.buffer.full_height = max(1, params.full_height / resolution); -} - -int TileManager::get_neighbor_index(int index, int neighbor) -{ - /* Neighbor indices: - * 0 1 2 - * 3 4 5 - * 6 7 8 - */ - static const int dx[] = {-1, 0, 1, -1, 0, 1, -1, 0, 1}; - static const int dy[] = {-1, -1, -1, 0, 0, 0, 1, 1, 1}; - - int resolution = state.resolution_divider; - int image_w = max(1, params.width / resolution); - int image_h = max(1, params.height / resolution); - - int num = min(image_h, num_devices); - int slice_num = !background ? num : 1; - int slice_h = image_h / slice_num; - - int tile_w = (tile_size.x >= image_w) ? 1 : divide_up(image_w, tile_size.x); - int tile_h = (tile_size.y >= slice_h) ? 1 : divide_up(slice_h, tile_size.y); - - /* Tiles in the state tile list are always indexed from left to right, top to bottom. */ - int nx = (index % tile_w) + dx[neighbor]; - int ny = (index / tile_w) + dy[neighbor]; - if (nx < 0 || ny < 0 || nx >= tile_w || ny >= tile_h * slice_num) - return -1; - - return ny * state.tile_stride + nx; -} - -/* Checks whether all neighbors of a tile (as well as the tile itself) are at least at state - * min_state. */ -bool TileManager::check_neighbor_state(int index, Tile::State min_state) -{ - if (index < 0 || state.tiles[index].state < min_state) { - return false; - } - for (int neighbor = 0; neighbor < 9; neighbor++) { - int nindex = get_neighbor_index(index, neighbor); - /* Out-of-bounds tiles don't matter. */ - if (nindex >= 0 && state.tiles[nindex].state < min_state) { - return false; - } - } - - return true; -} - -/* Returns whether the tile should be written (and freed if no denoising is used) instead of - * updating. */ -bool TileManager::finish_tile(const int index, const bool need_denoise, bool &delete_tile) -{ - delete_tile = false; - - switch (state.tiles[index].state) { - case Tile::RENDER: { - if (!(schedule_denoising && need_denoise)) { - state.tiles[index].state = Tile::DONE; - delete_tile = !progressive; - return true; - } - state.tiles[index].state = Tile::RENDERED; - /* For each neighbor and the tile itself, check whether all of its neighbors have been - * rendered. If yes, it can be denoised. */ - for (int neighbor = 0; neighbor < 9; neighbor++) { - int nindex = get_neighbor_index(index, neighbor); - if (check_neighbor_state(nindex, Tile::RENDERED)) { - state.tiles[nindex].state = Tile::DENOISE; - state.denoising_tiles[state.tiles[nindex].device].push_back(nindex); - } - } - return false; - } - case Tile::DENOISE: { - state.tiles[index].state = Tile::DENOISED; - /* For each neighbor and the tile itself, check whether all of its neighbors have been - * denoised. If yes, it can be freed. */ - for (int neighbor = 0; neighbor < 9; neighbor++) { - int nindex = get_neighbor_index(index, neighbor); - if (check_neighbor_state(nindex, Tile::DENOISED)) { - state.tiles[nindex].state = Tile::DONE; - /* Do not delete finished tiles in progressive mode. */ - if (!progressive) { - /* It can happen that the tile just finished denoising and already can be freed here. - * However, in that case it still has to be written before deleting, so we can't delete - * it yet. */ - if (neighbor == 4) { - delete_tile = true; - } - else { - delete state.tiles[nindex].buffers; - state.tiles[nindex].buffers = NULL; - } - } - } - } - return true; - } - default: - assert(false); - return true; - } -} - -bool TileManager::next_tile(Tile *&tile, int device, uint tile_types) -{ - /* Preserve device if requested, unless this is a separate denoising device that just wants to - * grab any available tile. */ - const bool preserve_device = preserve_tile_device && device < num_devices; - - if (tile_types & RenderTile::DENOISE) { - int tile_index = -1; - int logical_device = preserve_device ? device : 0; - - while (logical_device < state.denoising_tiles.size()) { - if (state.denoising_tiles[logical_device].empty()) { - if (preserve_device) { - break; - } - else { - logical_device++; - continue; - } - } - - tile_index = state.denoising_tiles[logical_device].front(); - state.denoising_tiles[logical_device].pop_front(); - break; - } - - if (tile_index >= 0) { - tile = &state.tiles[tile_index]; - return true; - } - } - - if (tile_types & RenderTile::PATH_TRACE) { - int tile_index = -1; - int logical_device = preserve_device ? device : 0; - - while (logical_device < state.render_tiles.size()) { - if (state.render_tiles[logical_device].empty()) { - if (preserve_device) { - break; - } - else { - logical_device++; - continue; - } - } - - tile_index = state.render_tiles[logical_device].front(); - state.render_tiles[logical_device].pop_front(); - break; - } - - if (tile_index >= 0) { - tile = &state.tiles[tile_index]; - return true; - } - } - - return false; + const DenoiseParams denoise_params = scene->integrator->get_denoise_params(); + node_to_image_spec_atttributes( + &write_state_.image_spec, &denoise_params, ATTR_DENOISE_SOCKET_PREFIX); } bool TileManager::done() { - int end_sample = (range_num_samples == -1) ? num_samples : - range_start_sample + range_num_samples; - return (state.resolution_divider == pixel_size) && - (state.sample + state.num_samples >= end_sample); -} - -bool TileManager::has_tiles() -{ - foreach (Tile &tile, state.tiles) { - if (tile.state != Tile::DONE) { - return true; - } - } - return false; + return tile_state_.next_tile_index == tile_state_.num_tiles; } bool TileManager::next() { - if (done()) + if (done()) { return false; - - if (progressive && state.resolution_divider > pixel_size) { - state.sample = 0; - state.resolution_divider = max(state.resolution_divider / 2, pixel_size); - state.num_samples = 1; - set_tiles(); } - else { - state.sample++; - if (progressive) - state.num_samples = 1; - else if (range_num_samples == -1) - state.num_samples = num_samples; - else - state.num_samples = range_num_samples; + tile_state_.current_tile = get_tile_for_index(tile_state_.next_tile_index); - state.resolution_divider = pixel_size; + ++tile_state_.next_tile_index; - if (state.sample == range_start_sample) { - set_tiles(); + return true; +} + +Tile TileManager::get_tile_for_index(int index) const +{ + /* TODO(sergey): Consider using hilbert spiral, or. maybe, even configurable. Not sure this + * brings a lot of value since this is only applicable to BIG tiles. */ + + const int tile_y = index / tile_state_.num_tiles_x; + const int tile_x = index - tile_y * tile_state_.num_tiles_x; + + Tile tile; + + tile.x = tile_x * tile_size_.x; + tile.y = tile_y * tile_size_.y; + tile.width = tile_size_.x; + tile.height = tile_size_.y; + + tile.width = min(tile.width, buffer_params_.width - tile.x); + tile.height = min(tile.height, buffer_params_.height - tile.y); + + return tile; +} + +const Tile &TileManager::get_current_tile() const +{ + return tile_state_.current_tile; +} + +bool TileManager::open_tile_output() +{ + write_state_.filename = path_temp_get("cycles-tile-buffer-" + tile_file_unique_part_ + "-" + + to_string(write_state_.tile_file_index) + ".exr"); + + write_state_.tile_out = ImageOutput::create(write_state_.filename); + if (!write_state_.tile_out) { + LOG(ERROR) << "Error creating image output for " << write_state_.filename; + return false; + } + + if (!write_state_.tile_out->supports("tiles")) { + LOG(ERROR) << "Progress tile file format does not support tiling."; + return false; + } + + write_state_.tile_out->open(write_state_.filename, write_state_.image_spec); + write_state_.num_tiles_written = 0; + + VLOG(3) << "Opened tile file " << write_state_.filename; + + return true; +} + +bool TileManager::close_tile_output() +{ + if (!write_state_.tile_out) { + return true; + } + + const bool success = write_state_.tile_out->close(); + write_state_.tile_out = nullptr; + + if (!success) { + LOG(ERROR) << "Error closing tile file."; + return false; + } + + VLOG(3) << "Tile output is closed."; + + return true; +} + +bool TileManager::write_tile(const RenderBuffers &tile_buffers) +{ + if (!write_state_.tile_out) { + if (!open_tile_output()) { + return false; } - else { - gen_render_tiles(); + } + + DCHECK_EQ(tile_buffers.params.pass_stride, buffer_params_.pass_stride); + + const BufferParams &tile_params = tile_buffers.params; + + vector pixel_storage; + const float *pixels = tile_buffers.buffer.data(); + + /* Tiled writing expects pixels to contain data for an entire tile. Pad the render buffers with + * empty pixels for tiles which are on the image boundary. */ + if (tile_params.width != tile_size_.x || tile_params.height != tile_size_.y) { + const int64_t pass_stride = tile_params.pass_stride; + const int64_t src_row_stride = tile_params.width * pass_stride; + + const int64_t dst_row_stride = tile_size_.x * pass_stride; + pixel_storage.resize(dst_row_stride * tile_size_.y); + + const float *src = tile_buffers.buffer.data(); + float *dst = pixel_storage.data(); + pixels = dst; + + for (int y = 0; y < tile_params.height; ++y, src += src_row_stride, dst += dst_row_stride) { + memcpy(dst, src, src_row_stride * sizeof(float)); } } + const int tile_x = tile_params.full_x - buffer_params_.full_x; + const int tile_y = tile_params.full_y - buffer_params_.full_y; + + VLOG(3) << "Write tile at " << tile_x << ", " << tile_y; + if (!write_state_.tile_out->write_tile(tile_x, tile_y, 0, TypeDesc::FLOAT, pixels)) { + LOG(ERROR) << "Error writing tile " << write_state_.tile_out->geterror(); + } + + ++write_state_.num_tiles_written; + + return true; +} + +void TileManager::finish_write_tiles() +{ + if (!write_state_.tile_out) { + /* None of the tiles were written hence the file was not created. + * Avoid creation of fully empty file since it is redundant. */ + return; + } + + /* EXR expects all tiles to present in file. So explicitly write missing tiles as all-zero. */ + if (write_state_.num_tiles_written < tile_state_.num_tiles) { + vector pixel_storage(tile_size_.x * tile_size_.y * buffer_params_.pass_stride); + + for (int tile_index = write_state_.num_tiles_written; tile_index < tile_state_.num_tiles; + ++tile_index) { + const Tile tile = get_tile_for_index(tile_index); + + VLOG(3) << "Write dummy tile at " << tile.x << ", " << tile.y; + + write_state_.tile_out->write_tile(tile.x, tile.y, 0, TypeDesc::FLOAT, pixel_storage.data()); + } + } + + close_tile_output(); + + if (full_buffer_written_cb) { + full_buffer_written_cb(write_state_.filename); + } + + /* Advance the counter upon explicit finish of the file. + * Makes it possible to re-use tile manager for another scene, and avoids unnecessary increments + * of the tile-file-within-session index. */ + ++write_state_.tile_file_index; + + write_state_.filename = ""; +} + +bool TileManager::read_full_buffer_from_disk(const string_view filename, + RenderBuffers *buffers, + DenoiseParams *denoise_params) +{ + unique_ptr in(ImageInput::open(filename)); + if (!in) { + LOG(ERROR) << "Error opening tile file " << filename; + return false; + } + + const ImageSpec &image_spec = in->spec(); + + BufferParams buffer_params; + if (!buffer_params_from_image_spec_atttributes(&buffer_params, image_spec)) { + return false; + } + buffers->reset(buffer_params); + + if (!node_from_image_spec_atttributes(denoise_params, image_spec, ATTR_DENOISE_SOCKET_PREFIX)) { + return false; + } + + if (!in->read_image(TypeDesc::FLOAT, buffers->buffer.data())) { + LOG(ERROR) << "Error reading pixels from the tile file " << in->geterror(); + return false; + } + + if (!in->close()) { + LOG(ERROR) << "Error closing tile file " << in->geterror(); + return false; + } + return true; } -int TileManager::get_num_effective_samples() -{ - return (range_num_samples == -1) ? num_samples : range_num_samples; -} - CCL_NAMESPACE_END diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h index 790a56f9445..124d0b3652c 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/render/tile.h @@ -14,159 +14,151 @@ * limitations under the License. */ -#ifndef __TILE_H__ -#define __TILE_H__ - -#include +#pragma once #include "render/buffers.h" -#include "util/util_list.h" +#include "util/util_image.h" +#include "util/util_string.h" +#include "util/util_unique_ptr.h" CCL_NAMESPACE_BEGIN -/* Tile */ +class DenoiseParams; +class Scene; + +/* -------------------------------------------------------------------- + * Tile. + */ class Tile { public: - int index; - int x, y, w, h; - int device; - /* RENDER: The tile has to be rendered. - * RENDERED: The tile has been rendered, but can't be denoised yet (waiting for neighbors). - * DENOISE: The tile can be denoised now. - * DENOISED: The tile has been denoised, but can't be freed yet (waiting for neighbors). - * DONE: The tile is finished and has been freed. */ - typedef enum { RENDER = 0, RENDERED, DENOISE, DENOISED, DONE } State; - State state; - RenderBuffers *buffers; + int x = 0, y = 0; + int width = 0, height = 0; Tile() { } - - Tile(int index_, int x_, int y_, int w_, int h_, int device_, State state_ = RENDER) - : index(index_), x(x_), y(y_), w(w_), h(h_), device(device_), state(state_), buffers(NULL) - { - } }; -/* Tile order */ - -/* Note: this should match enum_tile_order in properties.py */ -enum TileOrder { - TILE_CENTER = 0, - TILE_RIGHT_TO_LEFT = 1, - TILE_LEFT_TO_RIGHT = 2, - TILE_TOP_TO_BOTTOM = 3, - TILE_BOTTOM_TO_TOP = 4, - TILE_HILBERT_SPIRAL = 5, -}; - -/* Tile Manager */ +/* -------------------------------------------------------------------- + * Tile Manager. + */ class TileManager { public: - BufferParams params; + /* This callback is invoked by whenever on-dist tiles storage file is closed after writing. */ + function full_buffer_written_cb; - struct State { - vector tiles; - int tile_stride; - BufferParams buffer; - int sample; - int num_samples; - int resolution_divider; - int num_tiles; - - /* Total samples over all pixels: Generally num_samples*num_pixels, - * but can be higher due to the initial resolution division for previews. */ - uint64_t total_pixel_samples; - - /* These lists contain the indices of the tiles to be rendered/denoised and are used - * when acquiring a new tile for the device. - * Each list in each vector is for one logical device. */ - vector> render_tiles; - vector> denoising_tiles; - } state; - - int num_samples; - int slice_overlap; - - TileManager(bool progressive, - int num_samples, - int2 tile_size, - int start_resolution, - bool preserve_tile_device, - bool background, - TileOrder tile_order, - int num_devices = 1, - int pixel_size = 1); + TileManager(); ~TileManager(); - void device_free(); - void reset(BufferParams ¶ms, int num_samples); - void set_samples(int num_samples); - bool next(); - bool next_tile(Tile *&tile, int device, uint tile_types); - bool finish_tile(const int index, const bool need_denoise, bool &delete_tile); - bool done(); - bool has_tiles(); + TileManager(const TileManager &other) = delete; + TileManager(TileManager &&other) noexcept = delete; + TileManager &operator=(const TileManager &other) = delete; + TileManager &operator=(TileManager &&other) = delete; - void set_tile_order(TileOrder tile_order_) + /* Reset current progress and start new rendering of the full-frame parameters in tiles of the + * given size. + * Only touches scheduling-related state of the tile manager. */ + /* TODO(sergey): Consider using tile area instead of exact size to help dealing with extreme + * cases of stretched renders. */ + void reset_scheduling(const BufferParams ¶ms, int2 tile_size); + + /* Update for the known buffer passes and scene parameters. + * Will store all parameters needed for buffers access outside of the scene graph. */ + void update(const BufferParams ¶ms, const Scene *scene); + + inline int get_num_tiles() const { - tile_order = tile_order_; + return tile_state_.num_tiles; } - int get_neighbor_index(int index, int neighbor); - bool check_neighbor_state(int index, Tile::State state); + inline bool has_multiple_tiles() const + { + return tile_state_.num_tiles > 1; + } - /* ** Sample range rendering. ** */ + bool next(); + bool done(); - /* Start sample in the range. */ - int range_start_sample; + const Tile &get_current_tile() const; - /* Number to samples in the rendering range. */ - int range_num_samples; + /* Write render buffer of a tile to a file on disk. + * + * Opens file for write when first tile is written. + * + * Returns true on success. */ + bool write_tile(const RenderBuffers &tile_buffers); - /* Get number of actual samples to render. */ - int get_num_effective_samples(); + /* Inform the tile manager that no more tiles will be written to disk. + * The file will be considered final, all handles to it will be closed. */ + void finish_write_tiles(); - /* Schedule tiles for denoising after they've been rendered. */ - bool schedule_denoising; + /* Check whether any tile ahs been written to disk. */ + inline bool has_written_tiles() const + { + return write_state_.num_tiles_written != 0; + } + + /* Read full frame render buffer from tiles file on disk. + * + * Returns true on success. */ + bool read_full_buffer_from_disk(string_view filename, + RenderBuffers *buffers, + DenoiseParams *denoise_params); protected: - void set_tiles(); + /* Get tile configuration for its index. + * The tile index must be within [0, state_.tile_state_). */ + Tile get_tile_for_index(int index) const; - bool progressive; - int2 tile_size; - TileOrder tile_order; - int start_resolution; - int pixel_size; - int num_devices; + bool open_tile_output(); + bool close_tile_output(); - /* in some cases it is important that the same tile will be returned for the same - * device it was originally generated for (i.e. viewport rendering when buffer is - * allocating once for tile and then always used by it) - * - * in other cases any tile could be handled by any device (i.e. final rendering - * without progressive refine) - */ - bool preserve_tile_device; + /* Part of an on-disk tile file name which avoids conflicts between several Cycles instances or + * several sessions. */ + string tile_file_unique_part_; - /* for background render tiles should exactly match render parts generated from - * blender side, which means image first gets split into tiles and then tiles are - * assigning to render devices - * - * however viewport rendering expects tiles to be allocated in a special way, - * meaning image is being sliced horizontally first and every device handles - * its own slice - */ - bool background; + int2 tile_size_ = make_int2(0, 0); - /* Generate tile list, return number of tiles. */ - int gen_tiles(bool sliced); - void gen_render_tiles(); + BufferParams buffer_params_; + + /* Tile scheduling state. */ + struct { + int num_tiles_x = 0; + int num_tiles_y = 0; + int num_tiles = 0; + + int next_tile_index; + + Tile current_tile; + } tile_state_; + + /* State of tiles writing to a file on disk. */ + struct { + /* Index of a tile file used during the current session. + * This number is used for the file name construction, making it possible to render several + * scenes throughout duration of the session and keep all results available for later read + * access. */ + int tile_file_index = 0; + + string filename; + + /* Specification of the tile image which corresponds to the buffer parameters. + * Contains channels configured according to the passes configuration in the path traces. + * + * Output images are saved using this specification, input images are expected to have matched + * specification. */ + ImageSpec image_spec; + + /* Output handle for the tile file. + * + * This file can not be closed until all tiles has been provided, so the handle is stored in + * the state and is created whenever writing is requested. */ + unique_ptr tile_out; + + int num_tiles_written = 0; + } write_state_; }; CCL_NAMESPACE_END - -#endif /* __TILE_H__ */ diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt index 65a692acd03..0f6b435813f 100644 --- a/intern/cycles/test/CMakeLists.txt +++ b/intern/cycles/test/CMakeLists.txt @@ -32,6 +32,7 @@ set(INC set(ALL_CYCLES_LIBRARIES cycles_device cycles_kernel + cycles_integrator cycles_render cycles_bvh cycles_graph @@ -45,8 +46,12 @@ include_directories(${INC}) cycles_link_directories() set(SRC + integrator_adaptive_sampling_test.cpp + integrator_render_scheduler_test.cpp + integrator_tile_test.cpp render_graph_finalize_test.cpp util_aligned_malloc_test.cpp + util_math_test.cpp util_path_test.cpp util_string_test.cpp util_task_test.cpp diff --git a/intern/cycles/test/integrator_adaptive_sampling_test.cpp b/intern/cycles/test/integrator_adaptive_sampling_test.cpp new file mode 100644 index 00000000000..3ed6a23125d --- /dev/null +++ b/intern/cycles/test/integrator_adaptive_sampling_test.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "testing/testing.h" + +#include "integrator/adaptive_sampling.h" +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +TEST(AdaptiveSampling, schedule_samples) +{ + AdaptiveSampling adaptive_sampling; + adaptive_sampling.use = true; + adaptive_sampling.min_samples = 0; + adaptive_sampling.adaptive_step = 4; + + for (int sample = 2; sample < 32; ++sample) { + for (int num_samples = 8; num_samples < 32; ++num_samples) { + const int num_samples_aligned = adaptive_sampling.align_samples(sample, num_samples); + /* NOTE: `sample + num_samples_aligned` is the number of samples after rendering, so need + * to convert this to the 0-based index of the last sample. */ + EXPECT_TRUE(adaptive_sampling.need_filter(sample + num_samples_aligned - 1)); + } + } +} + +TEST(AdaptiveSampling, align_samples) +{ + AdaptiveSampling adaptive_sampling; + adaptive_sampling.use = true; + adaptive_sampling.min_samples = 11 /* rounded of sqrt(128) */; + adaptive_sampling.adaptive_step = 4; + + /* Filtering will happen at the following samples: + * 15, 19, 23, 27, 31, 35, 39, 43 */ + + /* Requested sample and number of samples will result in number of samples lower than + * `min_samples`. */ + EXPECT_EQ(adaptive_sampling.align_samples(0, 4), 4); + EXPECT_EQ(adaptive_sampling.align_samples(0, 7), 7); + + /* Request number of samples higher than the minimum samples before filter, but prior to the + * first sample at which filtering will happen. */ + EXPECT_EQ(adaptive_sampling.align_samples(0, 15), 15); + + /* When rendering many samples from the very beginning, limit number of samples by the first + * sample at which filtering is to happen. */ + EXPECT_EQ(adaptive_sampling.align_samples(0, 16), 16); + EXPECT_EQ(adaptive_sampling.align_samples(0, 17), 16); + EXPECT_EQ(adaptive_sampling.align_samples(0, 20), 16); + EXPECT_EQ(adaptive_sampling.align_samples(0, 60), 16); + + /* Similar to above, but start sample is not 0. */ + EXPECT_EQ(adaptive_sampling.align_samples(9, 8), 7); + EXPECT_EQ(adaptive_sampling.align_samples(9, 20), 7); + EXPECT_EQ(adaptive_sampling.align_samples(9, 60), 7); + + /* Start sample is past the minimum required samples, but prior to the first filter sample. */ + EXPECT_EQ(adaptive_sampling.align_samples(12, 6), 4); + EXPECT_EQ(adaptive_sampling.align_samples(12, 20), 4); + EXPECT_EQ(adaptive_sampling.align_samples(12, 60), 4); + + /* Start sample is the sample which is to be filtered. */ + EXPECT_EQ(adaptive_sampling.align_samples(15, 4), 1); + EXPECT_EQ(adaptive_sampling.align_samples(15, 6), 1); + EXPECT_EQ(adaptive_sampling.align_samples(15, 10), 1); + EXPECT_EQ(adaptive_sampling.align_samples(58, 2), 2); + + /* Start sample is past the sample which is to be filtered. */ + EXPECT_EQ(adaptive_sampling.align_samples(16, 3), 3); + EXPECT_EQ(adaptive_sampling.align_samples(16, 4), 4); + EXPECT_EQ(adaptive_sampling.align_samples(16, 5), 4); + EXPECT_EQ(adaptive_sampling.align_samples(16, 10), 4); + + /* Should never exceed requested number of samples. */ + EXPECT_EQ(adaptive_sampling.align_samples(15, 2), 1); + EXPECT_EQ(adaptive_sampling.align_samples(16, 2), 2); + EXPECT_EQ(adaptive_sampling.align_samples(17, 2), 2); + EXPECT_EQ(adaptive_sampling.align_samples(18, 2), 2); +} + +TEST(AdaptiveSampling, need_filter) +{ + AdaptiveSampling adaptive_sampling; + adaptive_sampling.use = true; + adaptive_sampling.min_samples = 11 /* rounded of sqrt(128) */; + adaptive_sampling.adaptive_step = 4; + + const vector expected_samples_to_filter = { + {15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59}}; + + vector actual_samples_to_filter; + for (int sample = 0; sample < 60; ++sample) { + if (adaptive_sampling.need_filter(sample)) { + actual_samples_to_filter.push_back(sample); + } + } + + EXPECT_EQ(actual_samples_to_filter, expected_samples_to_filter); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/test/integrator_render_scheduler_test.cpp b/intern/cycles/test/integrator_render_scheduler_test.cpp new file mode 100644 index 00000000000..b4efbc2d1a7 --- /dev/null +++ b/intern/cycles/test/integrator_render_scheduler_test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "testing/testing.h" + +#include "integrator/render_scheduler.h" + +CCL_NAMESPACE_BEGIN + +TEST(IntegratorRenderScheduler, calculate_resolution_divider_for_resolution) +{ + EXPECT_EQ(calculate_resolution_divider_for_resolution(1920, 1080, 1920), 1); + EXPECT_EQ(calculate_resolution_divider_for_resolution(1920, 1080, 960), 2); + EXPECT_EQ(calculate_resolution_divider_for_resolution(1920, 1080, 480), 4); +} + +TEST(IntegratorRenderScheduler, calculate_resolution_for_divider) +{ + EXPECT_EQ(calculate_resolution_for_divider(1920, 1080, 1), 1440); + EXPECT_EQ(calculate_resolution_for_divider(1920, 1080, 2), 720); + EXPECT_EQ(calculate_resolution_for_divider(1920, 1080, 4), 360); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/test/integrator_tile_test.cpp b/intern/cycles/test/integrator_tile_test.cpp new file mode 100644 index 00000000000..5bb57b48c3c --- /dev/null +++ b/intern/cycles/test/integrator_tile_test.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "testing/testing.h" + +#include "integrator/tile.h" +#include "util/util_math.h" + +CCL_NAMESPACE_BEGIN + +TEST(tile_calculate_best_size, Basic) +{ + /* Make sure CPU-like case is handled properly. */ + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 1, 1), TileSize(1, 1, 1)); + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 100, 1), TileSize(1, 1, 1)); + + /* Enough path states to fit an entire image with all samples. */ + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 1, 1920 * 1080), + TileSize(1920, 1080, 1)); + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 100, 1920 * 1080 * 100), + TileSize(1920, 1080, 100)); +} + +TEST(tile_calculate_best_size, Extreme) +{ + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 262144, 131072), TileSize(1, 1, 512)); + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 1048576, 131072), TileSize(1, 1, 1024)); + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 10485760, 131072), TileSize(1, 1, 4096)); + + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 8192 * 8192 * 2, 1024), + TileSize(1, 1, 1024)); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index da9b29314a7..19c211fe5f7 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -181,7 +181,7 @@ class RenderGraph : public testing::Test { util_logging_start(); util_logging_verbosity_set(1); - device_cpu = Device::create(device_info, stats, profiler, true); + device_cpu = Device::create(device_info, stats, profiler); scene = new Scene(scene_params, device_cpu); } diff --git a/intern/cycles/test/util_math_test.cpp b/intern/cycles/test/util_math_test.cpp new file mode 100644 index 00000000000..b6ce3ef0cf3 --- /dev/null +++ b/intern/cycles/test/util_math_test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "testing/testing.h" + +#include "util/util_math.h" + +CCL_NAMESPACE_BEGIN + +TEST(math, next_power_of_two) +{ + EXPECT_EQ(next_power_of_two(0), 1); + EXPECT_EQ(next_power_of_two(1), 2); + EXPECT_EQ(next_power_of_two(2), 4); + EXPECT_EQ(next_power_of_two(3), 4); + EXPECT_EQ(next_power_of_two(4), 8); +} + +TEST(math, prev_power_of_two) +{ + EXPECT_EQ(prev_power_of_two(0), 0); + + EXPECT_EQ(prev_power_of_two(1), 1); + EXPECT_EQ(prev_power_of_two(2), 1); + + EXPECT_EQ(prev_power_of_two(3), 2); + EXPECT_EQ(prev_power_of_two(4), 2); + + EXPECT_EQ(prev_power_of_two(5), 4); + EXPECT_EQ(prev_power_of_two(6), 4); + EXPECT_EQ(prev_power_of_two(7), 4); + EXPECT_EQ(prev_power_of_two(8), 4); +} + +TEST(math, reverse_integer_bits) +{ + EXPECT_EQ(reverse_integer_bits(0xFFFFFFFF), 0xFFFFFFFF); + EXPECT_EQ(reverse_integer_bits(0x00000000), 0x00000000); + EXPECT_EQ(reverse_integer_bits(0x1), 0x80000000); + EXPECT_EQ(reverse_integer_bits(0x80000000), 0x1); + EXPECT_EQ(reverse_integer_bits(0xFFFF0000), 0x0000FFFF); + EXPECT_EQ(reverse_integer_bits(0x0000FFFF), 0xFFFF0000); + EXPECT_EQ(reverse_integer_bits(0x00FF0000), 0x0000FF00); + EXPECT_EQ(reverse_integer_bits(0x0000FF00), 0x00FF0000); + EXPECT_EQ(reverse_integer_bits(0xAAAAAAAA), 0x55555555); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/test/util_string_test.cpp b/intern/cycles/test/util_string_test.cpp index 97f8daa65de..c9022d1b132 100644 --- a/intern/cycles/test/util_string_test.cpp +++ b/intern/cycles/test/util_string_test.cpp @@ -281,4 +281,40 @@ TEST(util_string_remove_trademark, r_space_middle) EXPECT_EQ(str, "foo bar baz"); } +/* ******** Tests for string_startswith() ******** */ + +TEST(string_startswith, basic) +{ + EXPECT_TRUE(string_startswith("", "")); + + EXPECT_FALSE(string_startswith("", "World")); + EXPECT_TRUE(string_startswith("Hello", "")); + + EXPECT_FALSE(string_startswith("Hello", "World")); + + EXPECT_TRUE(string_startswith("Hello", "Hello")); + EXPECT_TRUE(string_startswith("Hello", "He")); + EXPECT_TRUE(string_startswith("Hello", "H")); + + EXPECT_FALSE(string_startswith("Hello", "e")); + EXPECT_FALSE(string_startswith("Hello", "HelloWorld")); +} + +TEST(string_endswith, basic) +{ + EXPECT_TRUE(string_endswith("", "")); + + EXPECT_FALSE(string_endswith("", "World")); + EXPECT_TRUE(string_endswith("Hello", "")); + + EXPECT_FALSE(string_endswith("Hello", "World")); + + EXPECT_TRUE(string_endswith("Hello", "Hello")); + EXPECT_TRUE(string_endswith("Hello", "lo")); + EXPECT_TRUE(string_endswith("Hello", "o")); + + EXPECT_FALSE(string_endswith("Hello", "e")); + EXPECT_FALSE(string_endswith("Hello", "WorldHello")); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_atomic.h b/intern/cycles/util/util_atomic.h index 13d177d2b25..de17efafcf2 100644 --- a/intern/cycles/util/util_atomic.h +++ b/intern/cycles/util/util_atomic.h @@ -34,56 +34,6 @@ #else /* __KERNEL_GPU__ */ -# ifdef __KERNEL_OPENCL__ - -/* Float atomics implementation credits: - * http://suhorukov.blogspot.in/2011/12/opencl-11-atomic-operations-on-floating.html - */ -ccl_device_inline float atomic_add_and_fetch_float(volatile ccl_global float *source, - const float operand) -{ - union { - unsigned int int_value; - float float_value; - } new_value; - union { - unsigned int int_value; - float float_value; - } prev_value; - do { - prev_value.float_value = *source; - new_value.float_value = prev_value.float_value + operand; - } while (atomic_cmpxchg((volatile ccl_global unsigned int *)source, - prev_value.int_value, - new_value.int_value) != prev_value.int_value); - return new_value.float_value; -} - -ccl_device_inline float atomic_compare_and_swap_float(volatile ccl_global float *dest, - const float old_val, - const float new_val) -{ - union { - unsigned int int_value; - float float_value; - } new_value, prev_value, result; - prev_value.float_value = old_val; - new_value.float_value = new_val; - result.int_value = atomic_cmpxchg( - (volatile ccl_global unsigned int *)dest, prev_value.int_value, new_value.int_value); - return result.float_value; -} - -# define atomic_fetch_and_add_uint32(p, x) atomic_add((p), (x)) -# define atomic_fetch_and_inc_uint32(p) atomic_inc((p)) -# define atomic_fetch_and_dec_uint32(p) atomic_dec((p)) -# define atomic_fetch_and_or_uint32(p, x) atomic_or((p), (x)) - -# define CCL_LOCAL_MEM_FENCE CLK_LOCAL_MEM_FENCE -# define ccl_barrier(flags) barrier(flags) - -# endif /* __KERNEL_OPENCL__ */ - # ifdef __KERNEL_CUDA__ # define atomic_add_and_fetch_float(p, x) (atomicAdd((float *)(p), (float)(x)) + (float)(x)) diff --git a/intern/cycles/util/util_debug.cpp b/intern/cycles/util/util_debug.cpp index 74ecefa1917..1d598725c84 100644 --- a/intern/cycles/util/util_debug.cpp +++ b/intern/cycles/util/util_debug.cpp @@ -26,13 +26,7 @@ CCL_NAMESPACE_BEGIN DebugFlags::CPU::CPU() - : avx2(true), - avx(true), - sse41(true), - sse3(true), - sse2(true), - bvh_layout(BVH_LAYOUT_AUTO), - split_kernel(false) + : avx2(true), avx(true), sse41(true), sse3(true), sse2(true), bvh_layout(BVH_LAYOUT_AUTO) { reset(); } @@ -58,11 +52,9 @@ void DebugFlags::CPU::reset() #undef CHECK_CPU_FLAGS bvh_layout = BVH_LAYOUT_AUTO; - - split_kernel = false; } -DebugFlags::CUDA::CUDA() : adaptive_compile(false), split_kernel(false) +DebugFlags::CUDA::CUDA() : adaptive_compile(false) { reset(); } @@ -71,8 +63,6 @@ void DebugFlags::CUDA::reset() { if (getenv("CYCLES_CUDA_ADAPTIVE_COMPILE") != NULL) adaptive_compile = true; - - split_kernel = false; } DebugFlags::OptiX::OptiX() @@ -82,42 +72,7 @@ DebugFlags::OptiX::OptiX() void DebugFlags::OptiX::reset() { - cuda_streams = 1; - curves_api = false; -} - -DebugFlags::OpenCL::OpenCL() : device_type(DebugFlags::OpenCL::DEVICE_ALL), debug(false) -{ - reset(); -} - -void DebugFlags::OpenCL::reset() -{ - /* Initialize device type from environment variables. */ - device_type = DebugFlags::OpenCL::DEVICE_ALL; - char *device = getenv("CYCLES_OPENCL_TEST"); - if (device) { - if (strcmp(device, "NONE") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_NONE; - } - else if (strcmp(device, "ALL") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_ALL; - } - else if (strcmp(device, "DEFAULT") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_DEFAULT; - } - else if (strcmp(device, "CPU") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_CPU; - } - else if (strcmp(device, "GPU") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_GPU; - } - else if (strcmp(device, "ACCELERATOR") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_ACCELERATOR; - } - } - /* Initialize other flags from environment variables. */ - debug = (getenv("CYCLES_OPENCL_DEBUG") != NULL); + use_debug = false; } DebugFlags::DebugFlags() : viewport_static_bvh(false), running_inside_blender(false) @@ -131,7 +86,6 @@ void DebugFlags::reset() cpu.reset(); cuda.reset(); optix.reset(); - opencl.reset(); } std::ostream &operator<<(std::ostream &os, DebugFlagsConstRef debug_flags) @@ -142,40 +96,13 @@ std::ostream &operator<<(std::ostream &os, DebugFlagsConstRef debug_flags) << " SSE4.1 : " << string_from_bool(debug_flags.cpu.sse41) << "\n" << " SSE3 : " << string_from_bool(debug_flags.cpu.sse3) << "\n" << " SSE2 : " << string_from_bool(debug_flags.cpu.sse2) << "\n" - << " BVH layout : " << bvh_layout_name(debug_flags.cpu.bvh_layout) << "\n" - << " Split : " << string_from_bool(debug_flags.cpu.split_kernel) << "\n"; + << " BVH layout : " << bvh_layout_name(debug_flags.cpu.bvh_layout) << "\n"; os << "CUDA flags:\n" << " Adaptive Compile : " << string_from_bool(debug_flags.cuda.adaptive_compile) << "\n"; os << "OptiX flags:\n" - << " CUDA streams : " << debug_flags.optix.cuda_streams << "\n"; - - const char *opencl_device_type; - switch (debug_flags.opencl.device_type) { - case DebugFlags::OpenCL::DEVICE_NONE: - opencl_device_type = "NONE"; - break; - case DebugFlags::OpenCL::DEVICE_ALL: - opencl_device_type = "ALL"; - break; - case DebugFlags::OpenCL::DEVICE_DEFAULT: - opencl_device_type = "DEFAULT"; - break; - case DebugFlags::OpenCL::DEVICE_CPU: - opencl_device_type = "CPU"; - break; - case DebugFlags::OpenCL::DEVICE_GPU: - opencl_device_type = "GPU"; - break; - case DebugFlags::OpenCL::DEVICE_ACCELERATOR: - opencl_device_type = "ACCELERATOR"; - break; - } - os << "OpenCL flags:\n" - << " Device type : " << opencl_device_type << "\n" - << " Debug : " << string_from_bool(debug_flags.opencl.debug) << "\n" - << " Memory limit : " << string_human_readable_size(debug_flags.opencl.mem_limit) << "\n"; + << " Debug : " << string_from_bool(debug_flags.optix.use_debug) << "\n"; return os; } diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/util_debug.h index f7e53f90f74..99e2723180c 100644 --- a/intern/cycles/util/util_debug.h +++ b/intern/cycles/util/util_debug.h @@ -79,9 +79,6 @@ class DebugFlags { * CPUs and GPUs can be selected here instead. */ BVHLayout bvh_layout; - - /* Whether split kernel is used */ - bool split_kernel; }; /* Descriptor of CUDA feature-set to be used. */ @@ -94,9 +91,6 @@ class DebugFlags { /* Whether adaptive feature based runtime compile is enabled or not. * Requires the CUDA Toolkit and only works on Linux atm. */ bool adaptive_compile; - - /* Whether split kernel is used */ - bool split_kernel; }; /* Descriptor of OptiX feature-set to be used. */ @@ -106,61 +100,9 @@ class DebugFlags { /* Reset flags to their defaults. */ void reset(); - /* Number of CUDA streams to launch kernels concurrently from. */ - int cuda_streams; - - /* Use OptiX curves API for hair instead of custom implementation. */ - bool curves_api; - }; - - /* Descriptor of OpenCL feature-set to be used. */ - struct OpenCL { - OpenCL(); - - /* Reset flags to their defaults. */ - void reset(); - - /* Available device types. - * Only gives a hint which devices to let user to choose from, does not - * try to use any sort of optimal device or so. - */ - enum DeviceType { - /* None of OpenCL devices will be used. */ - DEVICE_NONE, - /* All OpenCL devices will be used. */ - DEVICE_ALL, - /* Default system OpenCL device will be used. */ - DEVICE_DEFAULT, - /* Host processor will be used. */ - DEVICE_CPU, - /* GPU devices will be used. */ - DEVICE_GPU, - /* Dedicated OpenCL accelerator device will be used. */ - DEVICE_ACCELERATOR, - }; - - /* Available kernel types. */ - enum KernelType { - /* Do automated guess which kernel to use, based on the officially - * supported GPUs and such. - */ - KERNEL_DEFAULT, - /* Force mega kernel to be used. */ - KERNEL_MEGA, - /* Force split kernel to be used. */ - KERNEL_SPLIT, - }; - - /* Requested device type. */ - DeviceType device_type; - - /* Use debug version of the kernel. */ - bool debug; - - /* TODO(mai): Currently this is only for OpenCL, but we should have it implemented for all - * devices. */ - /* Artificial memory limit in bytes (0 if disabled). */ - size_t mem_limit; + /* Load OptiX module with debug capabilities. Will lower logging verbosity level, enable + * validations, and lower optimization level. */ + bool use_debug; }; /* Get instance of debug flags registry. */ @@ -182,9 +124,6 @@ class DebugFlags { /* Requested OptiX flags. */ OptiX optix; - /* Requested OpenCL flags. */ - OpenCL opencl; - private: DebugFlags(); diff --git a/intern/cycles/util/util_defines.h b/intern/cycles/util/util_defines.h index 0a239a944a5..9b1698d461a 100644 --- a/intern/cycles/util/util_defines.h +++ b/intern/cycles/util/util_defines.h @@ -43,9 +43,9 @@ # define ccl_local_param # define ccl_private # define ccl_restrict __restrict -# define ccl_ref & # define ccl_optional_struct_init # define ccl_loop_no_unroll +# define ccl_attr_maybe_unused [[maybe_unused]] # define __KERNEL_WITH_SSE_ALIGN__ # if defined(_WIN32) && !defined(FREE_WINDOWS) @@ -62,7 +62,6 @@ # define ccl_may_alias # define ccl_always_inline __forceinline # define ccl_never_inline __declspec(noinline) -# define ccl_maybe_unused # else /* _WIN32 && !FREE_WINDOWS */ # define ccl_device_inline static inline __attribute__((always_inline)) # define ccl_device_forceinline static inline __attribute__((always_inline)) @@ -74,7 +73,6 @@ # define ccl_may_alias __attribute__((__may_alias__)) # define ccl_always_inline __attribute__((always_inline)) # define ccl_never_inline __attribute__((noinline)) -# define ccl_maybe_unused __attribute__((used)) # endif /* _WIN32 && !FREE_WINDOWS */ /* Use to suppress '-Wimplicit-fallthrough' (in place of 'break'). */ diff --git a/intern/cycles/util/util_half.h b/intern/cycles/util/util_half.h index a8d4ee75e20..d9edfec5da3 100644 --- a/intern/cycles/util/util_half.h +++ b/intern/cycles/util/util_half.h @@ -28,14 +28,8 @@ CCL_NAMESPACE_BEGIN /* Half Floats */ -#ifdef __KERNEL_OPENCL__ - -# define float4_store_half(h, f, scale) vstore_half4(f *(scale), 0, h); - -#else - /* CUDA has its own half data type, no need to define then */ -# ifndef __KERNEL_CUDA__ +#ifndef __KERNEL_CUDA__ /* Implementing this as a class rather than a typedef so that the compiler can tell it apart from * unsigned shorts. */ class half { @@ -59,27 +53,27 @@ class half { private: unsigned short v; }; -# endif +#endif struct half4 { half x, y, z, w; }; -# ifdef __KERNEL_CUDA__ +#ifdef __KERNEL_CUDA__ -ccl_device_inline void float4_store_half(half *h, float4 f, float scale) +ccl_device_inline void float4_store_half(half *h, float4 f) { - h[0] = __float2half(f.x * scale); - h[1] = __float2half(f.y * scale); - h[2] = __float2half(f.z * scale); - h[3] = __float2half(f.w * scale); + h[0] = __float2half(f.x); + h[1] = __float2half(f.y); + h[2] = __float2half(f.z); + h[3] = __float2half(f.w); } -# else +#else -ccl_device_inline void float4_store_half(half *h, float4 f, float scale) +ccl_device_inline void float4_store_half(half *h, float4 f) { -# ifndef __KERNEL_SSE2__ +# ifndef __KERNEL_SSE2__ for (int i = 0; i < 4; i++) { /* optimized float to half for pixels: * assumes no negative, no nan, no inf, and sets denormal to 0 */ @@ -87,8 +81,7 @@ ccl_device_inline void float4_store_half(half *h, float4 f, float scale) uint i; float f; } in; - float fscale = f[i] * scale; - in.f = (fscale > 0.0f) ? ((fscale < 65504.0f) ? fscale : 65504.0f) : 0.0f; + in.f = (f[i] > 0.0f) ? ((f[i] < 65504.0f) ? f[i] : 65504.0f) : 0.0f; int x = in.i; int absolute = x & 0x7FFFFFFF; @@ -98,23 +91,22 @@ ccl_device_inline void float4_store_half(half *h, float4 f, float scale) h[i] = (rshift & 0x7FFF); } -# else +# else /* same as above with SSE */ - ssef fscale = load4f(f) * scale; - ssef x = min(max(fscale, 0.0f), 65504.0f); + ssef x = min(max(load4f(f), 0.0f), 65504.0f); -# ifdef __KERNEL_AVX2__ +# ifdef __KERNEL_AVX2__ ssei rpack = _mm_cvtps_ph(x, 0); -# else +# else ssei absolute = cast(x) & 0x7FFFFFFF; ssei Z = absolute + 0xC8000000; ssei result = andnot(absolute < 0x38800000, Z); ssei rshift = (result >> 13) & 0x7FFF; ssei rpack = _mm_packs_epi32(rshift, rshift); -# endif +# endif _mm_storel_pi((__m64 *)h, _mm_castsi128_ps(rpack)); -# endif +# endif } ccl_device_inline float half_to_float(half h) @@ -160,8 +152,6 @@ ccl_device_inline half float_to_half(float f) return (value_bits | sign_bit); } -# endif - #endif CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_logging.h b/intern/cycles/util/util_logging.h index c161299acd0..35c2d436d09 100644 --- a/intern/cycles/util/util_logging.h +++ b/intern/cycles/util/util_logging.h @@ -49,6 +49,7 @@ class LogMessageVoidify { # define LOG(severity) LOG_SUPPRESS() # define VLOG(severity) LOG_SUPPRESS() # define VLOG_IF(severity, condition) LOG_SUPPRESS() +# define VLOG_IS_ON(severity) false # define CHECK(expression) LOG_SUPPRESS() diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index c5996ebfcb6..6d728dde679 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -26,11 +26,9 @@ # include #endif -#ifndef __KERNEL_OPENCL__ -# include -# include -# include -#endif /* __KERNEL_OPENCL__ */ +#include +#include +#include #include "util/util_types.h" @@ -86,7 +84,6 @@ CCL_NAMESPACE_BEGIN /* Scalar */ #ifdef _WIN32 -# ifndef __KERNEL_OPENCL__ ccl_device_inline float fmaxf(float a, float b) { return (a > b) ? a : b; @@ -96,8 +93,7 @@ ccl_device_inline float fminf(float a, float b) { return (a < b) ? a : b; } -# endif /* !__KERNEL_OPENCL__ */ -#endif /* _WIN32 */ +#endif /* _WIN32 */ #ifndef __KERNEL_GPU__ using std::isfinite; @@ -119,6 +115,11 @@ ccl_device_inline int min(int a, int b) return (a < b) ? a : b; } +ccl_device_inline uint min(uint a, uint b) +{ + return (a < b) ? a : b; +} + ccl_device_inline float max(float a, float b) { return (a > b) ? a : b; @@ -166,7 +167,6 @@ ccl_device_inline float max4(float a, float b, float c, float d) return max(max(a, b), max(c, d)); } -#ifndef __KERNEL_OPENCL__ /* Int/Float conversion */ ccl_device_inline int as_int(uint i) @@ -241,24 +241,23 @@ ccl_device_inline float __uint_as_float(uint i) ccl_device_inline int4 __float4_as_int4(float4 f) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int4(_mm_castps_si128(f.m128)); -# else +#else return make_int4( __float_as_int(f.x), __float_as_int(f.y), __float_as_int(f.z), __float_as_int(f.w)); -# endif +#endif } ccl_device_inline float4 __int4_as_float4(int4 i) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_castsi128_ps(i.m128)); -# else +#else return make_float4( __int_as_float(i.x), __int_as_float(i.y), __int_as_float(i.z), __int_as_float(i.w)); -# endif +#endif } -#endif /* __KERNEL_OPENCL__ */ /* Versions of functions which are safe for fast math. */ ccl_device_inline bool isnan_safe(float f) @@ -279,7 +278,6 @@ ccl_device_inline float ensure_finite(float v) return isfinite_safe(v) ? v : 0.0f; } -#ifndef __KERNEL_OPENCL__ ccl_device_inline int clamp(int a, int mn, int mx) { return min(max(a, mn), mx); @@ -309,8 +307,6 @@ ccl_device_inline float smoothstep(float edge0, float edge1, float x) return result; } -#endif /* __KERNEL_OPENCL__ */ - #ifndef __KERNEL_CUDA__ ccl_device_inline float saturate(float a) { @@ -451,7 +447,6 @@ CCL_NAMESPACE_END CCL_NAMESPACE_BEGIN -#ifndef __KERNEL_OPENCL__ /* Interpolation */ template A lerp(const A &a, const A &b, const B &t) @@ -459,15 +454,9 @@ template A lerp(const A &a, const A &b, const B &t) return (A)(a * ((B)1 - t) + b * t); } -#endif /* __KERNEL_OPENCL__ */ - /* Triangle */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline float triangle_area(const float3 &v1, const float3 &v2, const float3 &v3) -#else -ccl_device_inline float triangle_area(const float3 v1, const float3 v2, const float3 v3) -#endif { return len(cross(v3 - v2, v1 - v2)) * 0.5f; } @@ -665,11 +654,7 @@ ccl_device_inline float pow22(float a) ccl_device_inline float beta(float x, float y) { -#ifndef __KERNEL_OPENCL__ return expf(lgammaf(x) + lgammaf(y) - lgammaf(x + y)); -#else - return expf(lgamma(x) + lgamma(y) - lgamma(x + y)); -#endif } ccl_device_inline float xor_signmask(float x, int y) @@ -686,8 +671,6 @@ ccl_device_inline uint count_leading_zeros(uint x) { #if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) return __clz(x); -#elif defined(__KERNEL_OPENCL__) - return clz(x); #else assert(x != 0); # ifdef _MSC_VER @@ -704,8 +687,6 @@ ccl_device_inline uint count_trailing_zeros(uint x) { #if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) return (__ffs(x) - 1); -#elif defined(__KERNEL_OPENCL__) - return (31 - count_leading_zeros(x & -x)); #else assert(x != 0); # ifdef _MSC_VER @@ -722,8 +703,6 @@ ccl_device_inline uint find_first_set(uint x) { #if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) return __ffs(x); -#elif defined(__KERNEL_OPENCL__) - return (x != 0) ? (32 - count_leading_zeros(x & (-x))) : 0; #else # ifdef _MSC_VER return (x != 0) ? (32 - count_leading_zeros(x & (-x))) : 0; @@ -797,6 +776,52 @@ ccl_device_inline float precise_angle(float3 a, float3 b) return 2.0f * atan2f(len(a - b), len(a + b)); } +/* Return value which is greater than the given one and is a power of two. */ +ccl_device_inline uint next_power_of_two(uint x) +{ + return x == 0 ? 1 : 1 << (32 - count_leading_zeros(x)); +} + +/* Return value which is lower than the given one and is a power of two. */ +ccl_device_inline uint prev_power_of_two(uint x) +{ + return x < 2 ? x : 1 << (31 - count_leading_zeros(x - 1)); +} + +#ifndef __has_builtin +# define __has_builtin(v) 0 +#endif + +/* Reverses the bits of a 32 bit integer. */ +ccl_device_inline uint32_t reverse_integer_bits(uint32_t x) +{ + /* Use a native instruction if it exists. */ +#if defined(__arm__) || defined(__aarch64__) + __asm__("rbit %w0, %w1" : "=r"(x) : "r"(x)); + return x; +#elif defined(__KERNEL_CUDA__) + return __brev(x); +#elif __has_builtin(__builtin_bitreverse32) + return __builtin_bitreverse32(x); +#else + /* Flip pairwise. */ + x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1); + /* Flip pairs. */ + x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2); + /* Flip nibbles. */ + x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4); + /* Flip bytes. CPUs have an instruction for that, pretty fast one. */ +# ifdef _MSC_VER + return _byteswap_ulong(x); +# elif defined(__INTEL_COMPILER) + return (uint32_t)_bswap((int)x); +# else + /* Assuming gcc or clang. */ + return __builtin_bswap32(x); +# endif +#endif +} + CCL_NAMESPACE_END #endif /* __UTIL_MATH_H__ */ diff --git a/intern/cycles/util/util_math_float2.h b/intern/cycles/util/util_math_float2.h index 17f6f3c9382..70b80c33544 100644 --- a/intern/cycles/util/util_math_float2.h +++ b/intern/cycles/util/util_math_float2.h @@ -27,7 +27,6 @@ CCL_NAMESPACE_BEGIN * Declaration. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline float2 operator-(const float2 &a); ccl_device_inline float2 operator*(const float2 &a, const float2 &b); ccl_device_inline float2 operator*(const float2 &a, float f); @@ -64,7 +63,6 @@ ccl_device_inline float2 fabs(const float2 &a); ccl_device_inline float2 as_float2(const float4 &a); ccl_device_inline float2 interp(const float2 &a, const float2 &b, float t); ccl_device_inline float2 floor(const float2 &a); -#endif /* !__KERNEL_OPENCL__ */ ccl_device_inline float2 safe_divide_float2_float(const float2 a, const float b); @@ -82,7 +80,6 @@ ccl_device_inline float2 one_float2() return make_float2(1.0f, 1.0f); } -#ifndef __KERNEL_OPENCL__ ccl_device_inline float2 operator-(const float2 &a) { return make_float2(-a.x, -a.y); @@ -262,8 +259,6 @@ ccl_device_inline float2 floor(const float2 &a) return make_float2(floorf(a.x), floorf(a.y)); } -#endif /* !__KERNEL_OPENCL__ */ - ccl_device_inline float2 safe_divide_float2_float(const float2 a, const float b) { return (b != 0.0f) ? a / b : zero_float2(); diff --git a/intern/cycles/util/util_math_float3.h b/intern/cycles/util/util_math_float3.h index 9673c043189..30a1b4c3f77 100644 --- a/intern/cycles/util/util_math_float3.h +++ b/intern/cycles/util/util_math_float3.h @@ -27,7 +27,6 @@ CCL_NAMESPACE_BEGIN * Declaration. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline float3 operator-(const float3 &a); ccl_device_inline float3 operator*(const float3 &a, const float3 &b); ccl_device_inline float3 operator*(const float3 &a, const float f); @@ -63,7 +62,6 @@ ccl_device_inline float3 rcp(const float3 &a); ccl_device_inline float3 sqrt(const float3 &a); ccl_device_inline float3 floor(const float3 &a); ccl_device_inline float3 ceil(const float3 &a); -#endif /* !__KERNEL_OPENCL__ */ ccl_device_inline float min3(float3 a); ccl_device_inline float max3(float3 a); @@ -105,50 +103,49 @@ ccl_device_inline float3 one_float3() return make_float3(1.0f, 1.0f, 1.0f); } -#ifndef __KERNEL_OPENCL__ ccl_device_inline float3 operator-(const float3 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_xor_ps(a.m128, _mm_castsi128_ps(_mm_set1_epi32(0x80000000)))); -# else +#else return make_float3(-a.x, -a.y, -a.z); -# endif +#endif } ccl_device_inline float3 operator*(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_mul_ps(a.m128, b.m128)); -# else +#else return make_float3(a.x * b.x, a.y * b.y, a.z * b.z); -# endif +#endif } ccl_device_inline float3 operator*(const float3 &a, const float f) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_mul_ps(a.m128, _mm_set1_ps(f))); -# else +#else return make_float3(a.x * f, a.y * f, a.z * f); -# endif +#endif } ccl_device_inline float3 operator*(const float f, const float3 &a) { -# if defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE__) return float3(_mm_mul_ps(_mm_set1_ps(f), a.m128)); -# else +#else return make_float3(a.x * f, a.y * f, a.z * f); -# endif +#endif } ccl_device_inline float3 operator/(const float f, const float3 &a) { -# if defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE__) return float3(_mm_div_ps(_mm_set1_ps(f), a.m128)); -# else +#else return make_float3(f / a.x, f / a.y, f / a.z); -# endif +#endif } ccl_device_inline float3 operator/(const float3 &a, const float f) @@ -159,11 +156,11 @@ ccl_device_inline float3 operator/(const float3 &a, const float f) ccl_device_inline float3 operator/(const float3 &a, const float3 &b) { -# if defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE__) return float3(_mm_div_ps(a.m128, b.m128)); -# else +#else return make_float3(a.x / b.x, a.y / b.y, a.z / b.z); -# endif +#endif } ccl_device_inline float3 operator+(const float3 &a, const float f) @@ -173,11 +170,11 @@ ccl_device_inline float3 operator+(const float3 &a, const float f) ccl_device_inline float3 operator+(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_add_ps(a.m128, b.m128)); -# else +#else return make_float3(a.x + b.x, a.y + b.y, a.z + b.z); -# endif +#endif } ccl_device_inline float3 operator-(const float3 &a, const float f) @@ -187,11 +184,11 @@ ccl_device_inline float3 operator-(const float3 &a, const float f) ccl_device_inline float3 operator-(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_sub_ps(a.m128, b.m128)); -# else +#else return make_float3(a.x - b.x, a.y - b.y, a.z - b.z); -# endif +#endif } ccl_device_inline float3 operator+=(float3 &a, const float3 &b) @@ -227,11 +224,11 @@ ccl_device_inline float3 operator/=(float3 &a, float f) ccl_device_inline bool operator==(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return (_mm_movemask_ps(_mm_cmpeq_ps(a.m128, b.m128)) & 7) == 7; -# else +#else return (a.x == b.x && a.y == b.y && a.z == b.z); -# endif +#endif } ccl_device_inline bool operator!=(const float3 &a, const float3 &b) @@ -246,20 +243,20 @@ ccl_device_inline float distance(const float3 &a, const float3 &b) ccl_device_inline float dot(const float3 &a, const float3 &b) { -# if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) return _mm_cvtss_f32(_mm_dp_ps(a, b, 0x7F)); -# else +#else return a.x * b.x + a.y * b.y + a.z * b.z; -# endif +#endif } ccl_device_inline float dot_xy(const float3 &a, const float3 &b) { -# if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) return _mm_cvtss_f32(_mm_hadd_ps(_mm_mul_ps(a, b), b)); -# else +#else return a.x * b.x + a.y * b.y; -# endif +#endif } ccl_device_inline float3 cross(const float3 &a, const float3 &b) @@ -270,30 +267,30 @@ ccl_device_inline float3 cross(const float3 &a, const float3 &b) ccl_device_inline float3 normalize(const float3 &a) { -# if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) __m128 norm = _mm_sqrt_ps(_mm_dp_ps(a.m128, a.m128, 0x7F)); return float3(_mm_div_ps(a.m128, norm)); -# else +#else return a / len(a); -# endif +#endif } ccl_device_inline float3 min(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_min_ps(a.m128, b.m128)); -# else +#else return make_float3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); -# endif +#endif } ccl_device_inline float3 max(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_max_ps(a.m128, b.m128)); -# else +#else return make_float3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); -# endif +#endif } ccl_device_inline float3 clamp(const float3 &a, const float3 &mn, const float3 &mx) @@ -303,43 +300,43 @@ ccl_device_inline float3 clamp(const float3 &a, const float3 &mn, const float3 & ccl_device_inline float3 fabs(const float3 &a) { -# ifdef __KERNEL_SSE__ -# ifdef __KERNEL_NEON__ +#ifdef __KERNEL_SSE__ +# ifdef __KERNEL_NEON__ return float3(vabsq_f32(a.m128)); -# else +# else __m128 mask = _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)); return float3(_mm_and_ps(a.m128, mask)); -# endif -# else - return make_float3(fabsf(a.x), fabsf(a.y), fabsf(a.z)); # endif +#else + return make_float3(fabsf(a.x), fabsf(a.y), fabsf(a.z)); +#endif } ccl_device_inline float3 sqrt(const float3 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_sqrt_ps(a)); -# else +#else return make_float3(sqrtf(a.x), sqrtf(a.y), sqrtf(a.z)); -# endif +#endif } ccl_device_inline float3 floor(const float3 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_floor_ps(a)); -# else +#else return make_float3(floorf(a.x), floorf(a.y), floorf(a.z)); -# endif +#endif } ccl_device_inline float3 ceil(const float3 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_ceil_ps(a)); -# else +#else return make_float3(ceilf(a.x), ceilf(a.y), ceilf(a.z)); -# endif +#endif } ccl_device_inline float3 mix(const float3 &a, const float3 &b, float t) @@ -349,14 +346,13 @@ ccl_device_inline float3 mix(const float3 &a, const float3 &b, float t) ccl_device_inline float3 rcp(const float3 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ /* Don't use _mm_rcp_ps due to poor precision. */ return float3(_mm_div_ps(_mm_set_ps1(1.0f), a.m128)); -# else +#else return make_float3(1.0f / a.x, 1.0f / a.y, 1.0f / a.z); -# endif +#endif } -#endif /* !__KERNEL_OPENCL__ */ ccl_device_inline float min3(float3 a) { @@ -483,11 +479,7 @@ ccl_device_inline float average(const float3 a) ccl_device_inline bool isequal_float3(const float3 a, const float3 b) { -#ifdef __KERNEL_OPENCL__ - return all(a == b); -#else return a == b; -#endif } ccl_device_inline float3 pow3(float3 v, float e) diff --git a/intern/cycles/util/util_math_float4.h b/intern/cycles/util/util_math_float4.h index 0ba2bafa2f0..19af5c8c638 100644 --- a/intern/cycles/util/util_math_float4.h +++ b/intern/cycles/util/util_math_float4.h @@ -27,7 +27,6 @@ CCL_NAMESPACE_BEGIN * Declaration. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline float4 operator-(const float4 &a); ccl_device_inline float4 operator*(const float4 &a, const float4 &b); ccl_device_inline float4 operator*(const float4 &a, float f); @@ -66,7 +65,6 @@ ccl_device_inline float4 clamp(const float4 &a, const float4 &mn, const float4 & ccl_device_inline float4 fabs(const float4 &a); ccl_device_inline float4 floor(const float4 &a); ccl_device_inline float4 mix(const float4 &a, const float4 &b, float t); -#endif /* !__KERNEL_OPENCL__*/ ccl_device_inline float4 safe_divide_float4_float(const float4 a, const float b); @@ -112,33 +110,32 @@ ccl_device_inline float4 one_float4() return make_float4(1.0f, 1.0f, 1.0f, 1.0f); } -#ifndef __KERNEL_OPENCL__ ccl_device_inline float4 operator-(const float4 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ __m128 mask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000)); return float4(_mm_xor_ps(a.m128, mask)); -# else +#else return make_float4(-a.x, -a.y, -a.z, -a.w); -# endif +#endif } ccl_device_inline float4 operator*(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_mul_ps(a.m128, b.m128)); -# else +#else return make_float4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); -# endif +#endif } ccl_device_inline float4 operator*(const float4 &a, float f) { -# if defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE__) return a * make_float4(f); -# else +#else return make_float4(a.x * f, a.y * f, a.z * f, a.w * f); -# endif +#endif } ccl_device_inline float4 operator*(float f, const float4 &a) @@ -153,11 +150,11 @@ ccl_device_inline float4 operator/(const float4 &a, float f) ccl_device_inline float4 operator/(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_div_ps(a.m128, b.m128)); -# else +#else return make_float4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); -# endif +#endif } ccl_device_inline float4 operator+(const float4 &a, const float f) @@ -167,11 +164,11 @@ ccl_device_inline float4 operator+(const float4 &a, const float f) ccl_device_inline float4 operator+(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_add_ps(a.m128, b.m128)); -# else +#else return make_float4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); -# endif +#endif } ccl_device_inline float4 operator-(const float4 &a, const float f) @@ -181,11 +178,11 @@ ccl_device_inline float4 operator-(const float4 &a, const float f) ccl_device_inline float4 operator-(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_sub_ps(a.m128, b.m128)); -# else +#else return make_float4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); -# endif +#endif } ccl_device_inline float4 operator+=(float4 &a, const float4 &b) @@ -215,38 +212,38 @@ ccl_device_inline float4 operator/=(float4 &a, float f) ccl_device_inline int4 operator<(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int4(_mm_castps_si128(_mm_cmplt_ps(a.m128, b.m128))); -# else +#else return make_int4(a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w); -# endif +#endif } ccl_device_inline int4 operator>=(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int4(_mm_castps_si128(_mm_cmpge_ps(a.m128, b.m128))); -# else +#else return make_int4(a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w); -# endif +#endif } ccl_device_inline int4 operator<=(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int4(_mm_castps_si128(_mm_cmple_ps(a.m128, b.m128))); -# else +#else return make_int4(a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w); -# endif +#endif } ccl_device_inline bool operator==(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return (_mm_movemask_ps(_mm_cmpeq_ps(a.m128, b.m128)) & 15) == 15; -# else +#else return (a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w); -# endif +#endif } ccl_device_inline float distance(const float4 &a, const float4 &b) @@ -256,16 +253,16 @@ ccl_device_inline float distance(const float4 &a, const float4 &b) ccl_device_inline float dot(const float4 &a, const float4 &b) { -# if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) -# if defined(__KERNEL_NEON__) +#if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) +# if defined(__KERNEL_NEON__) __m128 t = vmulq_f32(a, b); return vaddvq_f32(t); -# else - return _mm_cvtss_f32(_mm_dp_ps(a, b, 0xFF)); -# endif # else - return (a.x * b.x + a.y * b.y) + (a.z * b.z + a.w * b.w); + return _mm_cvtss_f32(_mm_dp_ps(a, b, 0xFF)); # endif +#else + return (a.x * b.x + a.y * b.y) + (a.z * b.z + a.w * b.w); +#endif } ccl_device_inline float len_squared(const float4 &a) @@ -275,21 +272,21 @@ ccl_device_inline float len_squared(const float4 &a) ccl_device_inline float4 rcp(const float4 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ /* Don't use _mm_rcp_ps due to poor precision. */ return float4(_mm_div_ps(_mm_set_ps1(1.0f), a.m128)); -# else +#else return make_float4(1.0f / a.x, 1.0f / a.y, 1.0f / a.z, 1.0f / a.w); -# endif +#endif } ccl_device_inline float4 sqrt(const float4 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_sqrt_ps(a.m128)); -# else +#else return make_float4(sqrtf(a.x), sqrtf(a.y), sqrtf(a.z), sqrtf(a.w)); -# endif +#endif } ccl_device_inline float4 sqr(const float4 &a) @@ -299,39 +296,39 @@ ccl_device_inline float4 sqr(const float4 &a) ccl_device_inline float4 cross(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return (shuffle<1, 2, 0, 0>(a) * shuffle<2, 0, 1, 0>(b)) - (shuffle<2, 0, 1, 0>(a) * shuffle<1, 2, 0, 0>(b)); -# else +#else return make_float4(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x, 0.0f); -# endif +#endif } ccl_device_inline bool is_zero(const float4 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return a == make_float4(0.0f); -# else +#else return (a.x == 0.0f && a.y == 0.0f && a.z == 0.0f && a.w == 0.0f); -# endif +#endif } ccl_device_inline float4 reduce_add(const float4 &a) { -# if defined(__KERNEL_SSE__) -# if defined(__KERNEL_NEON__) +#if defined(__KERNEL_SSE__) +# if defined(__KERNEL_NEON__) return float4(vdupq_n_f32(vaddvq_f32(a))); -# elif defined(__KERNEL_SSE3__) +# elif defined(__KERNEL_SSE3__) float4 h(_mm_hadd_ps(a.m128, a.m128)); return float4(_mm_hadd_ps(h.m128, h.m128)); -# else +# else float4 h(shuffle<1, 0, 3, 2>(a) + a); return shuffle<2, 3, 0, 1>(h) + h; -# endif -# else +# endif +#else float sum = (a.x + a.y) + (a.z + a.w); return make_float4(sum, sum, sum, sum); -# endif +#endif } ccl_device_inline float average(const float4 &a) @@ -357,20 +354,20 @@ ccl_device_inline float4 safe_normalize(const float4 &a) ccl_device_inline float4 min(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_min_ps(a.m128, b.m128)); -# else +#else return make_float4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w)); -# endif +#endif } ccl_device_inline float4 max(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_max_ps(a.m128, b.m128)); -# else +#else return make_float4(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w)); -# endif +#endif } ccl_device_inline float4 clamp(const float4 &a, const float4 &mn, const float4 &mx) @@ -380,24 +377,24 @@ ccl_device_inline float4 clamp(const float4 &a, const float4 &mn, const float4 & ccl_device_inline float4 fabs(const float4 &a) { -# if defined(__KERNEL_SSE__) -# if defined(__KERNEL_NEON__) +#if defined(__KERNEL_SSE__) +# if defined(__KERNEL_NEON__) return float4(vabsq_f32(a)); -# else - return float4(_mm_and_ps(a.m128, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)))); -# endif # else - return make_float4(fabsf(a.x), fabsf(a.y), fabsf(a.z), fabsf(a.w)); + return float4(_mm_and_ps(a.m128, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)))); # endif +#else + return make_float4(fabsf(a.x), fabsf(a.y), fabsf(a.z), fabsf(a.w)); +#endif } ccl_device_inline float4 floor(const float4 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_floor_ps(a)); -# else +#else return make_float4(floorf(a.x), floorf(a.y), floorf(a.z), floorf(a.w)); -# endif +#endif } ccl_device_inline float4 mix(const float4 &a, const float4 &b, float t) @@ -405,8 +402,6 @@ ccl_device_inline float4 mix(const float4 &a, const float4 &b, float t) return a + t * (b - a); } -#endif /* !__KERNEL_OPENCL__*/ - #ifdef __KERNEL_SSE__ template __forceinline const float4 shuffle(const float4 &b) diff --git a/intern/cycles/util/util_math_int2.h b/intern/cycles/util/util_math_int2.h index 0295cd51f7e..5782b878801 100644 --- a/intern/cycles/util/util_math_int2.h +++ b/intern/cycles/util/util_math_int2.h @@ -27,20 +27,17 @@ CCL_NAMESPACE_BEGIN * Declaration. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline bool operator==(const int2 a, const int2 b); ccl_device_inline int2 operator+(const int2 &a, const int2 &b); ccl_device_inline int2 operator+=(int2 &a, const int2 &b); ccl_device_inline int2 operator-(const int2 &a, const int2 &b); ccl_device_inline int2 operator*(const int2 &a, const int2 &b); ccl_device_inline int2 operator/(const int2 &a, const int2 &b); -#endif /* !__KERNEL_OPENCL__ */ /******************************************************************************* * Definition. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline bool operator==(const int2 a, const int2 b) { return (a.x == b.x && a.y == b.y); @@ -70,7 +67,6 @@ ccl_device_inline int2 operator/(const int2 &a, const int2 &b) { return make_int2(a.x / b.x, a.y / b.y); } -#endif /* !__KERNEL_OPENCL__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_math_int3.h b/intern/cycles/util/util_math_int3.h index d92ed895dc2..e0dfae7c015 100644 --- a/intern/cycles/util/util_math_int3.h +++ b/intern/cycles/util/util_math_int3.h @@ -27,52 +27,49 @@ CCL_NAMESPACE_BEGIN * Declaration. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline int3 min(int3 a, int3 b); ccl_device_inline int3 max(int3 a, int3 b); ccl_device_inline int3 clamp(const int3 &a, int mn, int mx); ccl_device_inline int3 clamp(const int3 &a, int3 &mn, int mx); -#endif /* !__KERNEL_OPENCL__ */ /******************************************************************************* * Definition. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline int3 min(int3 a, int3 b) { -# if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE41__) +#if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE41__) return int3(_mm_min_epi32(a.m128, b.m128)); -# else +#else return make_int3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); -# endif +#endif } ccl_device_inline int3 max(int3 a, int3 b) { -# if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE41__) +#if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE41__) return int3(_mm_max_epi32(a.m128, b.m128)); -# else +#else return make_int3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); -# endif +#endif } ccl_device_inline int3 clamp(const int3 &a, int mn, int mx) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return min(max(a, make_int3(mn)), make_int3(mx)); -# else +#else return make_int3(clamp(a.x, mn, mx), clamp(a.y, mn, mx), clamp(a.z, mn, mx)); -# endif +#endif } ccl_device_inline int3 clamp(const int3 &a, int3 &mn, int mx) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return min(max(a, mn), make_int3(mx)); -# else +#else return make_int3(clamp(a.x, mn.x, mx), clamp(a.y, mn.y, mx), clamp(a.z, mn.z, mx)); -# endif +#endif } ccl_device_inline bool operator==(const int3 &a, const int3 &b) @@ -92,22 +89,21 @@ ccl_device_inline bool operator<(const int3 &a, const int3 &b) ccl_device_inline int3 operator+(const int3 &a, const int3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int3(_mm_add_epi32(a.m128, b.m128)); -# else +#else return make_int3(a.x + b.x, a.y + b.y, a.z + b.z); -# endif +#endif } ccl_device_inline int3 operator-(const int3 &a, const int3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int3(_mm_sub_epi32(a.m128, b.m128)); -# else +#else return make_int3(a.x - b.x, a.y - b.y, a.z - b.z); -# endif +#endif } -#endif /* !__KERNEL_OPENCL__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_path.cpp b/intern/cycles/util/util_path.cpp index 8905c8bc7f0..c78f4615013 100644 --- a/intern/cycles/util/util_path.cpp +++ b/intern/cycles/util/util_path.cpp @@ -66,6 +66,7 @@ typedef struct stat path_stat_t; static string cached_path = ""; static string cached_user_path = ""; +static string cached_temp_path = ""; static string cached_xdg_cache_path = ""; namespace { @@ -335,10 +336,11 @@ static string path_xdg_cache_get() } #endif -void path_init(const string &path, const string &user_path) +void path_init(const string &path, const string &user_path, const string &temp_path) { cached_path = path; cached_user_path = user_path; + cached_temp_path = temp_path; #ifdef _MSC_VER // workaround for https://svn.boost.org/trac/boost/ticket/6320 @@ -382,6 +384,15 @@ string path_cache_get(const string &sub) #endif } +string path_temp_get(const string &sub) +{ + if (cached_temp_path == "") { + cached_temp_path = Filesystem::temp_directory_path(); + } + + return path_join(cached_temp_path, sub); +} + #if defined(__linux__) || defined(__APPLE__) string path_xdg_home_get(const string &sub = ""); #endif @@ -739,177 +750,6 @@ bool path_remove(const string &path) return remove(path.c_str()) == 0; } -struct SourceReplaceState { - typedef map ProcessedMapping; - /* Base director for all relative include headers. */ - string base; - /* Result of processed files. */ - ProcessedMapping processed_files; - /* Set of files which are considered "precompiled" and which are replaced - * with and empty string on a subsequent occurrence in include statement. - */ - set precompiled_headers; -}; - -static string path_source_replace_includes_recursive(const string &source, - const string &source_filepath, - SourceReplaceState *state); - -static string line_directive(const SourceReplaceState &state, const string &path, const int line) -{ - string unescaped_path = path; - /* First we make path relative. */ - if (string_startswith(unescaped_path, state.base.c_str())) { - const string base_file = path_filename(state.base); - const size_t base_len = state.base.length(); - unescaped_path = base_file + - unescaped_path.substr(base_len, unescaped_path.length() - base_len); - } - /* Second, we replace all unsafe characters. */ - const size_t length = unescaped_path.length(); - string escaped_path = ""; - for (size_t i = 0; i < length; ++i) { - const char ch = unescaped_path[i]; - if (strchr("\"\'\?\\", ch) != NULL) { - escaped_path += "\\"; - } - escaped_path += ch; - } - /* TODO(sergey): Check whether using std::to_string combined with several - * concatenation operations is any faster. - */ - return string_printf("#line %d \"%s\"", line, escaped_path.c_str()); -} - -static string path_source_handle_preprocessor(const string &preprocessor_line, - const string &source_filepath, - const size_t line_number, - SourceReplaceState *state) -{ - string result = preprocessor_line; - string token = string_strip(preprocessor_line.substr(1, preprocessor_line.size() - 1)); - if (string_startswith(token, "include")) { - token = string_strip(token.substr(7, token.size() - 7)); - if (token[0] == '"') { - const size_t n_start = 1; - const size_t n_end = token.find("\"", n_start); - const string filename = token.substr(n_start, n_end - n_start); - const bool is_precompiled = string_endswith(token, "// PRECOMPILED"); - string filepath = path_join(state->base, filename); - if (!path_exists(filepath)) { - filepath = path_join(path_dirname(source_filepath), filename); - } - if (is_precompiled) { - state->precompiled_headers.insert(filepath); - } - string text; - if (path_read_text(filepath, text)) { - text = path_source_replace_includes_recursive(text, filepath, state); - /* Use line directives for better error messages. */ - result = line_directive(*state, filepath, 1) + "\n" + text + "\n" + - line_directive(*state, source_filepath, line_number + 1); - } - } - } - return result; -} - -/* Our own little c preprocessor that replaces #includes with the file - * contents, to work around issue of OpenCL drivers not supporting - * include paths with spaces in them. - */ -static string path_source_replace_includes_recursive(const string &source, - const string &source_filepath, - SourceReplaceState *state) -{ - /* Try to re-use processed file without spending time on replacing all - * include directives again. - */ - SourceReplaceState::ProcessedMapping::iterator replaced_file = state->processed_files.find( - source_filepath); - if (replaced_file != state->processed_files.end()) { - if (state->precompiled_headers.find(source_filepath) != state->precompiled_headers.end()) { - return ""; - } - return replaced_file->second; - } - /* Perform full file processing. */ - string result = ""; - const size_t source_length = source.length(); - size_t index = 0; - /* Information about where we are in the source. */ - size_t line_number = 0, column_number = 1; - /* Currently gathered non-preprocessor token. - * Store as start/length rather than token itself to avoid overhead of - * memory re-allocations on each character concatenation. - */ - size_t token_start = 0, token_length = 0; - /* Denotes whether we're inside of preprocessor line, together with - * preprocessor line itself. - * - * TODO(sergey): Investigate whether using token start/end position - * gives measurable speedup. - */ - bool inside_preprocessor = false; - string preprocessor_line = ""; - /* Actual loop over the whole source. */ - while (index < source_length) { - const char ch = source[index]; - if (ch == '\n') { - if (inside_preprocessor) { - result += path_source_handle_preprocessor( - preprocessor_line, source_filepath, line_number, state); - /* Start gathering net part of the token. */ - token_start = index; - token_length = 0; - } - inside_preprocessor = false; - preprocessor_line = ""; - column_number = 0; - ++line_number; - } - else if (ch == '#' && column_number == 1 && !inside_preprocessor) { - /* Append all possible non-preprocessor token to the result. */ - if (token_length != 0) { - result.append(source, token_start, token_length); - token_start = index; - token_length = 0; - } - inside_preprocessor = true; - } - if (inside_preprocessor) { - preprocessor_line += ch; - } - else { - ++token_length; - } - ++index; - ++column_number; - } - /* Append possible tokens which happened before special events handled - * above. - */ - if (token_length != 0) { - result.append(source, token_start, token_length); - } - if (inside_preprocessor) { - result += path_source_handle_preprocessor( - preprocessor_line, source_filepath, line_number, state); - } - /* Store result for further reuse. */ - state->processed_files[source_filepath] = result; - return result; -} - -string path_source_replace_includes(const string &source, - const string &path, - const string &source_filename) -{ - SourceReplaceState state; - state.base = path; - return path_source_replace_includes_recursive(source, path_join(path, source_filename), &state); -} - FILE *path_fopen(const string &path, const string &mode) { #ifdef _WIN32 diff --git a/intern/cycles/util/util_path.h b/intern/cycles/util/util_path.h index 7a83c2135a4..f899bc2e01c 100644 --- a/intern/cycles/util/util_path.h +++ b/intern/cycles/util/util_path.h @@ -32,9 +32,10 @@ CCL_NAMESPACE_BEGIN /* program paths */ -void path_init(const string &path = "", const string &user_path = ""); +void path_init(const string &path = "", const string &user_path = "", const string &tmp_path = ""); string path_get(const string &sub = ""); string path_user_get(const string &sub = ""); +string path_temp_get(const string &sub = ""); string path_cache_get(const string &sub = ""); /* path string manipulation */ @@ -65,11 +66,6 @@ bool path_read_text(const string &path, string &text); /* File manipulation. */ bool path_remove(const string &path); -/* source code utility */ -string path_source_replace_includes(const string &source, - const string &path, - const string &source_filename = ""); - /* cache utility */ void path_cache_clear_except(const string &name, const set &except); diff --git a/intern/cycles/util/util_profiling.cpp b/intern/cycles/util/util_profiling.cpp index 073b09f719f..5343f076e22 100644 --- a/intern/cycles/util/util_profiling.cpp +++ b/intern/cycles/util/util_profiling.cpp @@ -48,13 +48,7 @@ void Profiler::run() } if (cur_shader >= 0 && cur_shader < shader_samples.size()) { - /* Only consider the active shader during events whose runtime significantly depends on it. - */ - if (((cur_event >= PROFILING_SHADER_EVAL) && (cur_event <= PROFILING_SUBSURFACE)) || - ((cur_event >= PROFILING_CLOSURE_EVAL) && - (cur_event <= PROFILING_CLOSURE_VOLUME_SAMPLE))) { - shader_samples[cur_shader]++; - } + shader_samples[cur_shader]++; } if (cur_object >= 0 && cur_object < object_samples.size()) { diff --git a/intern/cycles/util/util_profiling.h b/intern/cycles/util/util_profiling.h index ceec08ed894..96bb682c50e 100644 --- a/intern/cycles/util/util_profiling.h +++ b/intern/cycles/util/util_profiling.h @@ -28,38 +28,30 @@ CCL_NAMESPACE_BEGIN enum ProfilingEvent : uint32_t { PROFILING_UNKNOWN, PROFILING_RAY_SETUP, - PROFILING_PATH_INTEGRATE, - PROFILING_SCENE_INTERSECT, - PROFILING_INDIRECT_EMISSION, - PROFILING_VOLUME, - PROFILING_SHADER_SETUP, - PROFILING_SHADER_EVAL, - PROFILING_SHADER_APPLY, - PROFILING_AO, - PROFILING_SUBSURFACE, - PROFILING_CONNECT_LIGHT, - PROFILING_SURFACE_BOUNCE, - PROFILING_WRITE_RESULT, - PROFILING_INTERSECT, - PROFILING_INTERSECT_LOCAL, - PROFILING_INTERSECT_SHADOW_ALL, - PROFILING_INTERSECT_VOLUME, - PROFILING_INTERSECT_VOLUME_ALL, + PROFILING_INTERSECT_CLOSEST, + PROFILING_INTERSECT_SUBSURFACE, + PROFILING_INTERSECT_SHADOW, + PROFILING_INTERSECT_VOLUME_STACK, - PROFILING_CLOSURE_EVAL, - PROFILING_CLOSURE_SAMPLE, - PROFILING_CLOSURE_VOLUME_EVAL, - PROFILING_CLOSURE_VOLUME_SAMPLE, + PROFILING_SHADE_SURFACE_SETUP, + PROFILING_SHADE_SURFACE_EVAL, + PROFILING_SHADE_SURFACE_DIRECT_LIGHT, + PROFILING_SHADE_SURFACE_INDIRECT_LIGHT, + PROFILING_SHADE_SURFACE_AO, + PROFILING_SHADE_SURFACE_PASSES, - PROFILING_DENOISING, - PROFILING_DENOISING_CONSTRUCT_TRANSFORM, - PROFILING_DENOISING_RECONSTRUCT, - PROFILING_DENOISING_DIVIDE_SHADOW, - PROFILING_DENOISING_NON_LOCAL_MEANS, - PROFILING_DENOISING_COMBINE_HALVES, - PROFILING_DENOISING_GET_FEATURE, - PROFILING_DENOISING_DETECT_OUTLIERS, + PROFILING_SHADE_VOLUME_SETUP, + PROFILING_SHADE_VOLUME_INTEGRATE, + PROFILING_SHADE_VOLUME_DIRECT_LIGHT, + PROFILING_SHADE_VOLUME_INDIRECT_LIGHT, + + PROFILING_SHADE_SHADOW_SETUP, + PROFILING_SHADE_SHADOW_SURFACE, + PROFILING_SHADE_SHADOW_VOLUME, + + PROFILING_SHADE_LIGHT_SETUP, + PROFILING_SHADE_LIGHT_EVAL, PROFILING_NUM_EVENTS, }; @@ -136,39 +128,53 @@ class ProfilingHelper { state->event = event; } - inline void set_event(ProfilingEvent event) - { - state->event = event; - } - - inline void set_shader(int shader) - { - state->shader = shader; - if (state->active) { - assert(shader < state->shader_hits.size()); - state->shader_hits[shader]++; - } - } - - inline void set_object(int object) - { - state->object = object; - if (state->active) { - assert(object < state->object_hits.size()); - state->object_hits[object]++; - } - } - ~ProfilingHelper() { state->event = previous_event; } - private: + inline void set_event(ProfilingEvent event) + { + state->event = event; + } + + protected: ProfilingState *state; uint32_t previous_event; }; +class ProfilingWithShaderHelper : public ProfilingHelper { + public: + ProfilingWithShaderHelper(ProfilingState *state, ProfilingEvent event) + : ProfilingHelper(state, event) + { + } + + ~ProfilingWithShaderHelper() + { + state->object = -1; + state->shader = -1; + } + + inline void set_shader(int object, int shader) + { + if (state->active) { + state->shader = shader; + state->object = object; + + if (shader >= 0) { + assert(shader < state->shader_hits.size()); + state->shader_hits[shader]++; + } + + if (object >= 0) { + assert(object < state->object_hits.size()); + state->object_hits[object]++; + } + } + } +}; + CCL_NAMESPACE_END #endif /* __UTIL_PROFILING_H__ */ diff --git a/intern/cycles/util/util_progress.h b/intern/cycles/util/util_progress.h index 26534a29dfe..dca8d3d0ab5 100644 --- a/intern/cycles/util/util_progress.h +++ b/intern/cycles/util/util_progress.h @@ -46,7 +46,6 @@ class Progress { substatus = ""; sync_status = ""; sync_substatus = ""; - kernel_status = ""; update_cb = function_null; cancel = false; cancel_message = ""; @@ -87,7 +86,6 @@ class Progress { substatus = ""; sync_status = ""; sync_substatus = ""; - kernel_status = ""; cancel = false; cancel_message = ""; error = false; @@ -316,24 +314,6 @@ class Progress { } } - /* kernel status */ - - void set_kernel_status(const string &kernel_status_) - { - { - thread_scoped_lock lock(progress_mutex); - kernel_status = kernel_status_; - } - - set_update(); - } - - void get_kernel_status(string &kernel_status_) - { - thread_scoped_lock lock(progress_mutex); - kernel_status_ = kernel_status; - } - /* callback */ void set_update() @@ -378,8 +358,6 @@ class Progress { string sync_status; string sync_substatus; - string kernel_status; - volatile bool cancel; string cancel_message; diff --git a/intern/cycles/util/util_simd.h b/intern/cycles/util/util_simd.h index 8e8caa98a1b..b4a153c329f 100644 --- a/intern/cycles/util/util_simd.h +++ b/intern/cycles/util/util_simd.h @@ -61,14 +61,14 @@ static struct TrueTy { { return true; } -} True ccl_maybe_unused; +} True ccl_attr_maybe_unused; static struct FalseTy { __forceinline operator bool() const { return false; } -} False ccl_maybe_unused; +} False ccl_attr_maybe_unused; static struct ZeroTy { __forceinline operator float() const @@ -79,7 +79,7 @@ static struct ZeroTy { { return 0; } -} zero ccl_maybe_unused; +} zero ccl_attr_maybe_unused; static struct OneTy { __forceinline operator float() const @@ -90,7 +90,7 @@ static struct OneTy { { return 1; } -} one ccl_maybe_unused; +} one ccl_attr_maybe_unused; static struct NegInfTy { __forceinline operator float() const @@ -101,7 +101,7 @@ static struct NegInfTy { { return std::numeric_limits::min(); } -} neg_inf ccl_maybe_unused; +} neg_inf ccl_attr_maybe_unused; static struct PosInfTy { __forceinline operator float() const @@ -112,10 +112,10 @@ static struct PosInfTy { { return std::numeric_limits::max(); } -} inf ccl_maybe_unused, pos_inf ccl_maybe_unused; +} inf ccl_attr_maybe_unused, pos_inf ccl_attr_maybe_unused; static struct StepTy { -} step ccl_maybe_unused; +} step ccl_attr_maybe_unused; #endif diff --git a/intern/cycles/util/util_static_assert.h b/intern/cycles/util/util_static_assert.h index d809f2e06d7..7df52d462b7 100644 --- a/intern/cycles/util/util_static_assert.h +++ b/intern/cycles/util/util_static_assert.h @@ -24,9 +24,9 @@ CCL_NAMESPACE_BEGIN -#if defined(__KERNEL_OPENCL__) || defined(CYCLES_CUBIN_CC) +#if defined(CYCLES_CUBIN_CC) # define static_assert(statement, message) -#endif /* __KERNEL_OPENCL__ */ +#endif #define static_assert_align(st, align) \ static_assert((sizeof(st) % (align) == 0), "Structure must be strictly aligned") // NOLINT diff --git a/intern/cycles/util/util_string.cpp b/intern/cycles/util/util_string.cpp index 4dfebf14923..9c0b2ca50bb 100644 --- a/intern/cycles/util/util_string.cpp +++ b/intern/cycles/util/util_string.cpp @@ -17,6 +17,9 @@ #include #include +#include +#include + #include "util/util_foreach.h" #include "util/util_string.h" #include "util/util_windows.h" @@ -107,24 +110,26 @@ void string_split(vector &tokens, } } -bool string_startswith(const string &s, const char *start) +bool string_startswith(const string_view s, const string_view start) { - size_t len = strlen(start); + const size_t len = start.size(); - if (len > s.size()) - return 0; - else - return strncmp(s.c_str(), start, len) == 0; + if (len > s.size()) { + return false; + } + + return strncmp(s.c_str(), start.data(), len) == 0; } -bool string_endswith(const string &s, const string &end) +bool string_endswith(const string_view s, const string_view end) { - size_t len = end.length(); + const size_t len = end.size(); - if (len > s.size()) - return 0; - else - return s.compare(s.length() - len, len, end) == 0; + if (len > s.size()) { + return false; + } + + return strncmp(s.c_str() + s.size() - len, end.data(), len) == 0; } string string_strip(const string &s) @@ -172,6 +177,13 @@ string to_string(const char *str) return string(str); } +string string_to_lower(const string &s) +{ + string r = s; + std::transform(r.begin(), r.end(), r.begin(), [](char c) { return std::tolower(c); }); + return r; +} + /* Wide char strings helpers for Windows. */ #ifdef _WIN32 diff --git a/intern/cycles/util/util_string.h b/intern/cycles/util/util_string.h index f2272819b2f..55462cfd8b8 100644 --- a/intern/cycles/util/util_string.h +++ b/intern/cycles/util/util_string.h @@ -21,6 +21,11 @@ #include #include +/* Use string view implementation from OIIO. + * Ideally, need to switch to `std::string_view`, but this first requires getting rid of using + * namespace OIIO as it causes symbol collision. */ +#include + #include "util/util_vector.h" CCL_NAMESPACE_BEGIN @@ -31,6 +36,8 @@ using std::string; using std::stringstream; using std::to_string; +using OIIO::string_view; + #ifdef __GNUC__ # define PRINTF_ATTRIBUTE __attribute__((format(printf, 1, 2))) #else @@ -45,12 +52,13 @@ void string_split(vector &tokens, const string &separators = "\t ", bool skip_empty_tokens = true); void string_replace(string &haystack, const string &needle, const string &other); -bool string_startswith(const string &s, const char *start); -bool string_endswith(const string &s, const string &end); +bool string_startswith(string_view s, string_view start); +bool string_endswith(string_view s, string_view end); string string_strip(const string &s); string string_remove_trademark(const string &s); string string_from_bool(const bool var); string to_string(const char *str); +string string_to_lower(const string &s); /* Wide char strings are only used on Windows to deal with non-ASCII * characters in file names and such. No reason to use such strings diff --git a/intern/cycles/util/util_system.cpp b/intern/cycles/util/util_system.cpp index b010881058b..be8c2fb505a 100644 --- a/intern/cycles/util/util_system.cpp +++ b/intern/cycles/util/util_system.cpp @@ -403,4 +403,13 @@ size_t system_physical_ram() #endif } +uint64_t system_self_process_id() +{ +#ifdef _WIN32 + return GetCurrentProcessId(); +#else + return getpid(); +#endif +} + CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_system.h b/intern/cycles/util/util_system.h index c4db8b74339..a1797e6ca44 100644 --- a/intern/cycles/util/util_system.h +++ b/intern/cycles/util/util_system.h @@ -65,6 +65,9 @@ size_t system_physical_ram(); /* Start a new process of the current application with the given arguments. */ bool system_call_self(const vector &args); +/* Get identifier of the currently running process. */ +uint64_t system_self_process_id(); + CCL_NAMESPACE_END #endif /* __UTIL_SYSTEM_H__ */ diff --git a/intern/cycles/util/util_tbb.h b/intern/cycles/util/util_tbb.h index 73e0f92d19c..8f84377ac8c 100644 --- a/intern/cycles/util/util_tbb.h +++ b/intern/cycles/util/util_tbb.h @@ -23,6 +23,7 @@ #include #include +#include #include #include diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h index 71bf9c65911..4de66bf5f46 100644 --- a/intern/cycles/util/util_texture.h +++ b/intern/cycles/util/util_texture.h @@ -85,8 +85,6 @@ typedef struct TextureInfo { uint64_t data; /* Data Type */ uint data_type; - /* Buffer number for OpenCL. */ - uint cl_buffer; /* Interpolation and extension type. */ uint interpolation, extension; /* Dimensions. */ diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index f79eac4cbcf..e9cd3b0b483 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -498,36 +498,12 @@ Transform transform_from_viewplane(BoundBox2D &viewplane); #endif -/* TODO(sergey): This is only for until we've got OpenCL 2.0 - * on all devices we consider supported. It'll be replaced with - * generic address space. - */ +/* TODO: This can be removed when we know if no devices will require explicit + * address space qualifiers for this case. */ -#ifdef __KERNEL_OPENCL__ - -# define OPENCL_TRANSFORM_ADDRSPACE_GLUE(a, b) a##b -# define OPENCL_TRANSFORM_ADDRSPACE_DECLARE(function) \ - ccl_device_inline float3 OPENCL_TRANSFORM_ADDRSPACE_GLUE(function, _addrspace)( \ - ccl_addr_space const Transform *t, const float3 a) \ - { \ - Transform private_tfm = *t; \ - return function(&private_tfm, a); \ - } - -OPENCL_TRANSFORM_ADDRSPACE_DECLARE(transform_point) -OPENCL_TRANSFORM_ADDRSPACE_DECLARE(transform_direction) -OPENCL_TRANSFORM_ADDRSPACE_DECLARE(transform_direction_transposed) - -# undef OPENCL_TRANSFORM_ADDRSPACE_DECLARE -# undef OPENCL_TRANSFORM_ADDRSPACE_GLUE -# define transform_point_auto transform_point_addrspace -# define transform_direction_auto transform_direction_addrspace -# define transform_direction_transposed_auto transform_direction_transposed_addrspace -#else -# define transform_point_auto transform_point -# define transform_direction_auto transform_direction -# define transform_direction_transposed_auto transform_direction_transposed -#endif +#define transform_point_auto transform_point +#define transform_direction_auto transform_direction +#define transform_direction_transposed_auto transform_direction_transposed CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_types.h b/intern/cycles/util/util_types.h index 87358877e3c..442c32b3a3d 100644 --- a/intern/cycles/util/util_types.h +++ b/intern/cycles/util/util_types.h @@ -17,9 +17,7 @@ #ifndef __UTIL_TYPES_H__ #define __UTIL_TYPES_H__ -#ifndef __KERNEL_OPENCL__ -# include -#endif +#include /* Standard Integer Types */ @@ -44,18 +42,12 @@ CCL_NAMESPACE_BEGIN /* Shorter Unsigned Names */ -#ifndef __KERNEL_OPENCL__ typedef unsigned char uchar; typedef unsigned int uint; typedef unsigned short ushort; -#endif /* Fixed Bits Types */ -#ifdef __KERNEL_OPENCL__ -typedef unsigned long uint64_t; -#endif - #ifndef __KERNEL_GPU__ /* Generic Memory Pointer */ diff --git a/intern/cycles/util/util_unique_ptr.h b/intern/cycles/util/util_unique_ptr.h index 3aaaf083eff..3181eafd43d 100644 --- a/intern/cycles/util/util_unique_ptr.h +++ b/intern/cycles/util/util_unique_ptr.h @@ -21,6 +21,7 @@ CCL_NAMESPACE_BEGIN +using std::make_unique; using std::unique_ptr; CCL_NAMESPACE_END diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 0e3cb7e3cab..40f59307bec 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -209,7 +209,6 @@ url_manual_mapping = ( ("bpy.types.toolsettings.use_proportional_connected*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-connected"), ("bpy.types.toolsettings.use_proportional_projected*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-projected"), ("bpy.types.view3doverlay.vertex_paint_mode_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-vertex-paint-mode-opacity"), - ("bpy.types.viewlayer.use_pass_cryptomatte_accurate*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-accurate"), ("bpy.types.viewlayer.use_pass_cryptomatte_material*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-material"), ("bpy.ops.gpencil.vertex_color_brightness_contrast*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-brightness-contrast"), ("bpy.ops.view3d.edit_mesh_extrude_individual_move*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-view3d-edit-mesh-extrude-individual-move"), @@ -573,7 +572,6 @@ url_manual_mapping = ( ("bpy.types.rendersettings.film_transparent*", "render/cycles/render_settings/film.html#bpy-types-rendersettings-film-transparent"), ("bpy.types.rendersettings.simplify_volumes*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-volumes"), ("bpy.types.rendersettings.use_render_cache*", "render/output/properties/output.html#bpy-types-rendersettings-use-render-cache"), - ("bpy.types.rendersettings.use_save_buffers*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-save-buffers"), ("bpy.types.rendersettings.use_single_layer*", "render/layers/view_layer.html#bpy-types-rendersettings-use-single-layer"), ("bpy.types.sceneeevee.use_taa_reprojection*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-use-taa-reprojection"), ("bpy.types.sequenceeditor.use_overlay_lock*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-overlay-lock"), diff --git a/release/scripts/presets/cycles/sampling/Final.py b/release/scripts/presets/cycles/sampling/Final.py index f1222d927c1..f3626c4b778 100644 --- a/release/scripts/presets/cycles/sampling/Final.py +++ b/release/scripts/presets/cycles/sampling/Final.py @@ -1,18 +1,12 @@ import bpy cycles = bpy.context.scene.cycles -# Path Trace -cycles.samples = 512 -cycles.preview_samples = 128 - -# Branched Path Trace -cycles.aa_samples = 128 -cycles.preview_aa_samples = 32 - -cycles.diffuse_samples = 4 -cycles.glossy_samples = 4 -cycles.transmission_samples = 4 -cycles.ao_samples = 1 -cycles.mesh_light_samples = 4 -cycles.subsurface_samples = 4 -cycles.volume_samples = 4 +cycles.use_adaptive_sampling = True +cycles.adaptive_threshold = 0.01 +cycles.samples = 4096 +cycles.adaptive_min_samples = 0 +cycles.time_limit = 0.0 +cycles.use_denoising = True +cycles.denoiser = 'OPENIMAGEDENOISE' +cycles.denoising_input_passes = 'RGB_ALBEDO_NORMAL' +cycles.denoising_prefilter = 'ACCURATE' diff --git a/release/scripts/presets/cycles/sampling/Preview.py b/release/scripts/presets/cycles/sampling/Preview.py index c16449e2c8f..66aa9339063 100644 --- a/release/scripts/presets/cycles/sampling/Preview.py +++ b/release/scripts/presets/cycles/sampling/Preview.py @@ -1,18 +1,12 @@ import bpy cycles = bpy.context.scene.cycles -# Path Trace -cycles.samples = 128 -cycles.preview_samples = 32 - -# Branched Path Trace -cycles.aa_samples = 32 -cycles.preview_aa_samples = 4 - -cycles.diffuse_samples = 4 -cycles.glossy_samples = 4 -cycles.transmission_samples = 4 -cycles.ao_samples = 1 -cycles.mesh_light_samples = 4 -cycles.subsurface_samples = 4 -cycles.volume_samples = 4 +cycles.use_adaptive_sampling = True +cycles.adaptive_threshold = 0.1 +cycles.samples = 1024 +cycles.adaptive_min_samples = 0 +cycles.time_limit = 0.0 +cycles.use_denoising = True +cycles.denoiser = 'OPENIMAGEDENOISE' +cycles.denoising_input_passes = 'RGB_ALBEDO_NORMAL' +cycles.denoising_prefilter = 'ACCURATE' diff --git a/release/scripts/presets/cycles/viewport_sampling/Final.py b/release/scripts/presets/cycles/viewport_sampling/Final.py new file mode 100644 index 00000000000..b2cb6bfe90a --- /dev/null +++ b/release/scripts/presets/cycles/viewport_sampling/Final.py @@ -0,0 +1,11 @@ +import bpy +cycles = bpy.context.scene.cycles + +cycles.use_preview_adaptive_sampling = True +cycles.preview_adaptive_threshold = 0.01 +cycles.preview_samples = 4096 +cycles.preview_adaptive_min_samples = 0 +cycles.use_preview_denoising = True +cycles.preview_denoiser = 'OPENIMAGEDENOISE' +cycles.preview_denoising_input_passes = 'RGB_ALBEDO_NORMAL' +cycles.preview_denoising_prefilter = 'ACCURATE' diff --git a/release/scripts/presets/cycles/viewport_sampling/Preview.py b/release/scripts/presets/cycles/viewport_sampling/Preview.py new file mode 100644 index 00000000000..f8319b70d4a --- /dev/null +++ b/release/scripts/presets/cycles/viewport_sampling/Preview.py @@ -0,0 +1,11 @@ +import bpy +cycles = bpy.context.scene.cycles + +cycles.use_preview_adaptive_sampling = True +cycles.preview_adaptive_threshold = 0.1 +cycles.preview_samples = 1024 +cycles.preview_adaptive_min_samples = 0 +cycles.use_preview_denoising = False +cycles.preview_denoiser = 'AUTO' +cycles.preview_denoising_input_passes = 'RGB_ALBEDO' +cycles.preview_denoising_prefilter = 'FAST' diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py index ad7d6008238..6b130d7353d 100644 --- a/release/scripts/startup/bl_ui/properties_view_layer.py +++ b/release/scripts/startup/bl_ui/properties_view_layer.py @@ -192,8 +192,6 @@ class ViewLayerCryptomattePanel(ViewLayerButtonsPanel, Panel): view_layer.use_pass_cryptomatte_material, view_layer.use_pass_cryptomatte_asset)) col.prop(view_layer, "pass_cryptomatte_depth", text="Levels") - col.prop(view_layer, "use_pass_cryptomatte_accurate", - text="Accurate Mode") class VIEWLAYER_PT_layer_passes_cryptomatte(ViewLayerCryptomattePanel, Panel): diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index b3ee2f411d7..1f25106404a 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 24 +#define BLENDER_FILE_SUBVERSION 25 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index b489675cd74..434a2296d95 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -183,7 +183,6 @@ static ViewLayer *view_layer_add(const char *name) view_layer->passflag = SCE_PASS_COMBINED; view_layer->pass_alpha_threshold = 0.5f; view_layer->cryptomatte_levels = 6; - view_layer->cryptomatte_flag = VIEW_LAYER_CRYPTOMATTE_ACCURATE; BKE_freestyle_config_init(&view_layer->freestyle_config); return view_layer; diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index fa15e541e43..54d1efab7dd 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -651,13 +651,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) mat->line_col[3] = mat->alpha; } } - - if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "int", "preview_start_resolution")) { - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - scene->r.preview_start_resolution = 64; - } - } } if (!MAIN_VERSION_ATLEAST(bmain, 271, 3)) { @@ -698,15 +691,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - if (!MAIN_VERSION_ATLEAST(bmain, 272, 0)) { - if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "int", "preview_start_resolution")) { - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - scene->r.preview_start_resolution = 64; - } - } - } - if (!MAIN_VERSION_ATLEAST(bmain, 272, 1)) { Brush *br; for (br = bmain->brushes.first; br; br = br->id.next) { diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 9f2c090c242..69b67460a5d 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3718,7 +3718,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) STRNCPY(node->idname, "ShaderNodeOutputLight"); } if (node->type == SH_NODE_BSDF_PRINCIPLED && node->custom2 == 0) { - node->custom2 = SHD_SUBSURFACE_BURLEY; + node->custom2 = SHD_SUBSURFACE_DIFFUSION; } } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index bafba486c88..be8c4b735be 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1461,7 +1461,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { view_layer->cryptomatte_levels = 6; - view_layer->cryptomatte_flag = VIEW_LAYER_CRYPTOMATTE_ACCURATE; } } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 1a19bbbee5c..4dc6a0ecea6 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -784,6 +784,20 @@ static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data)) return true; } +static void do_version_subsurface_methods(bNode *node) +{ + if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { + if (node->custom1 != SHD_SUBSURFACE_RANDOM_WALK) { + node->custom1 = SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS; + } + } + else if (node->type == SH_NODE_BSDF_PRINCIPLED) { + if (node->custom2 != SHD_SUBSURFACE_RANDOM_WALK) { + node->custom2 = SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS; + } + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -1336,6 +1350,25 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 25)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + do_version_subsurface_methods(node); + } + } + } + FOREACH_NODETREE_END; + + enum { + R_EXR_TILE_FILE = (1 << 10), + R_FULL_SAMPLE = (1 << 15), + }; + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->r.scemode &= ~(R_EXR_TILE_FILE | R_FULL_SAMPLE); + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index 90e6b43f02e..da57f27af4e 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -182,8 +182,8 @@ static void displacement_principled_nodes(bNode *node) } } else if (node->type == SH_NODE_BSDF_PRINCIPLED) { - if (node->custom2 != SHD_SUBSURFACE_RANDOM_WALK) { - node->custom2 = SHD_SUBSURFACE_BURLEY; + if (node->custom2 != SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS) { + node->custom2 = SHD_SUBSURFACE_DIFFUSION; } } } @@ -1373,6 +1373,11 @@ void blo_do_versions_cycles(FileData *UNUSED(fd), Library *UNUSED(lib), Main *bm void do_versions_after_linking_cycles(Main *bmain) { + const int DENOISER_AUTO = 0; + const int DENOISER_NLM = 1; + const int DENOISER_OPTIX = 2; + const int DENOISER_OPENIMAGEDENOISE = 4; + if (!MAIN_VERSION_ATLEAST(bmain, 280, 66)) { /* Shader node tree changes. After lib linking so we have all the typeinfo * pointers and updated sockets and we can use the high level node API to @@ -1578,10 +1583,6 @@ void do_versions_after_linking_cycles(Main *bmain) } if (cscene) { - const int DENOISER_AUTO = 0; - const int DENOISER_NLM = 1; - const int DENOISER_OPTIX = 2; - /* Enable denoiser if it was enabled for one view layer before. */ cycles_property_int_set(cscene, "denoiser", (use_optix) ? DENOISER_OPTIX : DENOISER_NLM); cycles_property_boolean_set(cscene, "use_denoising", use_denoising); @@ -1637,4 +1638,17 @@ void do_versions_after_linking_cycles(Main *bmain) object->visibility_flag |= flag; } } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 25)) { + /* Removal of NLM denoiser. */ + for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + IDProperty *cscene = cycles_properties_from_ID(&scene->id); + + if (cscene) { + if (cycles_property_int(cscene, "denoiser", DENOISER_NLM) == DENOISER_NLM) { + cycles_property_int_set(cscene, "denoiser", DENOISER_OPENIMAGEDENOISE); + } + } + } + } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index f2d5896be03..152ef79a38f 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -54,6 +54,7 @@ #include "BKE_curveprofile.h" #include "BKE_customdata.h" #include "BKE_gpencil.h" +#include "BKE_idprop.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -356,6 +357,12 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) if (ts->custom_bevel_profile_preset == NULL) { ts->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE); } + + /* Clear ID properties so Cycles gets defaults. */ + IDProperty *idprop = IDP_GetProperties(&scene->id, false); + if (idprop) { + IDP_ClearProperty(idprop); + } } /** @@ -582,6 +589,10 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) bNodeSocket *roughness_socket = nodeFindSocket(node, SOCK_IN, "Roughness"); bNodeSocketValueFloat *roughness_data = roughness_socket->default_value; roughness_data->value = 0.4f; + node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; + } + else if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { + node->custom1 = SHD_SUBSURFACE_RANDOM_WALK; } } } diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.cc b/source/blender/compositor/nodes/COM_IDMaskNode.cc index b51e79f2dea..761cb8b98cf 100644 --- a/source/blender/compositor/nodes/COM_IDMaskNode.cc +++ b/source/blender/compositor/nodes/COM_IDMaskNode.cc @@ -28,7 +28,7 @@ IDMaskNode::IDMaskNode(bNode *editorNode) : Node(editorNode) /* pass */ } void IDMaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const + const CompositorContext & /*context*/) const { bNode *bnode = this->getbNode(); @@ -38,7 +38,7 @@ void IDMaskNode::convertToOperations(NodeConverter &converter, converter.addOperation(operation); converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - if (bnode->custom2 == 0 || context.getRenderData()->scemode & R_FULL_SAMPLE) { + if (bnode->custom2 == 0) { converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); } else { diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.cc b/source/blender/compositor/nodes/COM_ZCombineNode.cc index ddf66740578..e29748dc317 100644 --- a/source/blender/compositor/nodes/COM_ZCombineNode.cc +++ b/source/blender/compositor/nodes/COM_ZCombineNode.cc @@ -31,9 +31,9 @@ namespace blender::compositor { void ZCombineNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const + const CompositorContext & /*context*/) const { - if ((context.getRenderData()->scemode & R_FULL_SAMPLE) || this->getbNode()->custom2) { + if (this->getbNode()->custom2) { ZCombineOperation *operation = nullptr; if (this->getbNode()->custom1) { operation = new ZCombineAlphaOperation(); diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index a125a13eaf9..2e25211ea62 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -176,6 +176,9 @@ void DRW_deferred_shader_remove(struct GPUMaterial *mat); struct DrawDataList *DRW_drawdatalist_from_id(struct ID *id); void DRW_drawdata_free(struct ID *id); +bool DRW_opengl_context_release(void); +void DRW_opengl_context_activate(bool test); + #ifdef __cplusplus } #endif diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 76a1b561972..49780abc6f4 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -139,8 +139,6 @@ void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata) g_data->cryptomatte_session = session; g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE | EEVEE_RENDER_PASS_VOLUME_LIGHT; - g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag & - VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0; } } @@ -405,7 +403,6 @@ void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EE { EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; EEVEE_EffectsInfo *effects = stl->effects; EEVEE_PassList *psl = vedata->psl; const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -413,10 +410,9 @@ void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EE const int cryptomatte_levels = view_layer->cryptomatte_levels; const int current_sample = effects->taa_current_sample; - /* In accurate mode all render samples are evaluated. In inaccurate mode this is limited to the - * number of cryptomatte levels. This will reduce the overhead of downloading the GPU buffer and - * integrating it into the accum buffer. */ - if (g_data->cryptomatte_accurate_mode || current_sample < cryptomatte_levels) { + /* Render samples used by cryptomatte are limited to the number of cryptomatte levels. This will + * reduce the overhead of downloading the GPU buffer and integrating it into the accum buffer. */ + if (current_sample < cryptomatte_levels) { static float clear_color[4] = {0.0}; GPU_framebuffer_bind(fbl->cryptomatte_fb); GPU_framebuffer_clear_color(fbl->cryptomatte_fb, clear_color); diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 6a66e8b1a58..f8e1cc9c923 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -648,6 +648,8 @@ RenderEngineType DRW_engine_viewport_eevee_type = { NULL, NULL, NULL, + NULL, + NULL, &EEVEE_render_update_passes, &draw_engine_eevee_type, {NULL, NULL, NULL}, diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index f51b4fa0127..eae5d161cc3 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -1042,7 +1042,6 @@ typedef struct EEVEE_PrivateData { int aov_hash; int num_aovs_used; struct CryptomatteSession *cryptomatte_session; - bool cryptomatte_accurate_mode; EEVEE_CryptomatteSample *cryptomatte_accum_buffer; float *cryptomatte_download_buffer; diff --git a/source/blender/draw/engines/external/external_engine.c b/source/blender/draw/engines/external/external_engine.c index 89ee3f1b293..cc548a53a8e 100644 --- a/source/blender/draw/engines/external/external_engine.c +++ b/source/blender/draw/engines/external/external_engine.c @@ -32,13 +32,19 @@ #include "BKE_object.h" #include "BKE_particle.h" +#include "ED_image.h" #include "ED_screen.h" +#include "GPU_batch.h" +#include "GPU_debug.h" #include "GPU_matrix.h" #include "GPU_shader.h" #include "GPU_state.h" #include "GPU_viewport.h" +#include "RE_engine.h" +#include "RE_pipeline.h" + #include "external_engine.h" /* own include */ /* Shaders */ @@ -137,6 +143,22 @@ static void external_engine_init(void *vedata) } } +/* Add shading group call which will take care of writing to the depth buffer, so that the + * alpha-under overlay will happen for the render buffer. */ +static void external_cache_image_add(DRWShadingGroup *grp) +{ + float obmat[4][4]; + unit_m4(obmat); + scale_m4_fl(obmat, 0.5f); + + /* NOTE: Use the same Z-depth value as in the regular image drawing engine. */ + translate_m4(obmat, 1.0f, 1.0f, 0.75f); + + GPUBatch *geom = DRW_cache_quad_get(); + + DRW_shgroup_call_obmat(grp, geom, obmat); +} + static void external_cache_init(void *vedata) { EXTERNAL_PassList *psl = ((EXTERNAL_Data *)vedata)->psl; @@ -162,14 +184,33 @@ static void external_cache_init(void *vedata) stl->g_data->depth_shgrp = DRW_shgroup_create(e_data.depth_sh, psl->depth_pass); } - /* Do not draw depth pass when overlays are turned off. */ - stl->g_data->need_depth = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0; + if (v3d != NULL) { + /* Do not draw depth pass when overlays are turned off. */ + stl->g_data->need_depth = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0; + } + else if (draw_ctx->space_data != NULL) { + const eSpace_Type space_type = draw_ctx->space_data->spacetype; + if (space_type == SPACE_IMAGE) { + external_cache_image_add(stl->g_data->depth_shgrp); + + stl->g_data->need_depth = true; + stl->g_data->update_depth = true; + } + } } static void external_cache_populate(void *vedata, Object *ob) { + const DRWContextState *draw_ctx = DRW_context_state_get(); EXTERNAL_StorageList *stl = ((EXTERNAL_Data *)vedata)->stl; + if (draw_ctx->space_data != NULL) { + const eSpace_Type space_type = draw_ctx->space_data->spacetype; + if (space_type == SPACE_IMAGE) { + return; + } + } + if (!(DRW_object_is_renderable(ob) && DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) { return; @@ -210,13 +251,11 @@ static void external_cache_finish(void *UNUSED(vedata)) { } -static void external_draw_scene_do(void *vedata) +static void external_draw_scene_do_v3d(void *vedata) { const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; RegionView3D *rv3d = draw_ctx->rv3d; ARegion *region = draw_ctx->region; - const RenderEngineType *type; DRW_state_reset_ex(DRW_STATE_DEFAULT & ~DRW_STATE_DEPTH_LESS_EQUAL); @@ -229,8 +268,6 @@ static void external_draw_scene_do(void *vedata) } RenderEngine *engine = RE_engine_create(engine_type); - engine->tile_x = scene->r.tilex; - engine->tile_y = scene->r.tiley; engine_type->view_update(engine, draw_ctx->evil_C, draw_ctx->depsgraph); rv3d->render_engine = engine; } @@ -241,7 +278,7 @@ static void external_draw_scene_do(void *vedata) ED_region_pixelspace(region); /* Render result draw. */ - type = rv3d->render_engine->type; + const RenderEngineType *type = rv3d->render_engine->type; type->view_draw(rv3d->render_engine, draw_ctx->evil_C, draw_ctx->depsgraph); GPU_bgl_end(); @@ -259,6 +296,116 @@ static void external_draw_scene_do(void *vedata) } } +/* Configure current matrix stack so that the external engine can use the same drawing code for + * both viewport and image editor drawing. + * + * The engine draws result in the pixel space, and is applying render offset. For image editor we + * need to switch from normalized space to pixel space, and "un-apply" offset. */ +static void external_image_space_matrix_set(const RenderEngine *engine) +{ + BLI_assert(engine != NULL); + + const DRWContextState *draw_ctx = DRW_context_state_get(); + const DRWView *view = DRW_view_get_active(); + struct SpaceImage *space_image = (struct SpaceImage *)draw_ctx->space_data; + + /* Apply current view as transformation matrix. + * This will configure drawing for normalized space with current zoom and pan applied. */ + + float view_matrix[4][4]; + DRW_view_viewmat_get(view, view_matrix, false); + + float projection_matrix[4][4]; + DRW_view_winmat_get(view, projection_matrix, false); + + GPU_matrix_projection_set(projection_matrix); + GPU_matrix_set(view_matrix); + + /* Switch from normalized space to pixel space. */ + { + int width, height; + ED_space_image_get_size(space_image, &width, &height); + + const float width_inv = width ? 1.0f / width : 0.0f; + const float height_inv = height ? 1.0f / height : 0.0f; + GPU_matrix_scale_2f(width_inv, height_inv); + } + + /* Un-apply render offset. */ + { + Render *render = engine->re; + rctf view_rect; + rcti render_rect; + RE_GetViewPlane(render, &view_rect, &render_rect); + + GPU_matrix_translate_2f(-render_rect.xmin, -render_rect.ymin); + } +} + +static void external_draw_scene_do_image(void *UNUSED(vedata)) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + Render *re = RE_GetSceneRender(scene); + RenderEngine *engine = RE_engine_get(re); + + /* Is tested before enabling the drawing engine. */ + BLI_assert(re != NULL); + BLI_assert(engine != NULL); + + const DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + /* Clear the depth buffer to the value used by the background overlay so that the overlay is not + * happening outside of the drawn image. + * + * NOTE: The external engine only draws color. The depth is taken care of using the depth pass + * which initialized the depth to the values expected by the background overlay. */ + GPU_framebuffer_clear_depth(dfbl->default_fb, 1.0f); + + GPU_matrix_push_projection(); + GPU_matrix_push(); + + external_image_space_matrix_set(engine); + + GPU_debug_group_begin("External Engine"); + + const RenderEngineType *engine_type = engine->type; + BLI_assert(engine_type != NULL); + BLI_assert(engine_type->draw != NULL); + + engine_type->draw(engine, draw_ctx->evil_C, draw_ctx->depsgraph); + + GPU_debug_group_end(); + + GPU_matrix_pop(); + GPU_matrix_pop_projection(); + + DRW_state_reset(); + GPU_bgl_end(); + + RE_engine_draw_release(re); +} + +static void external_draw_scene_do(void *vedata) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + + if (draw_ctx->v3d != NULL) { + external_draw_scene_do_v3d(vedata); + return; + } + + if (draw_ctx->space_data == NULL) { + return; + } + + const eSpace_Type space_type = draw_ctx->space_data->spacetype; + if (space_type == SPACE_IMAGE) { + external_draw_scene_do_image(vedata); + return; + } +} + static void external_draw_scene(void *vedata) { const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -297,7 +444,7 @@ static void external_engine_free(void) static const DrawEngineDataSize external_data_size = DRW_VIEWPORT_DATA_SIZE(EXTERNAL_Data); -static DrawEngineType draw_engine_external_type = { +DrawEngineType draw_engine_external_type = { NULL, NULL, N_("External"), @@ -330,8 +477,45 @@ RenderEngineType DRW_engine_viewport_external_type = { NULL, NULL, NULL, + NULL, + NULL, &draw_engine_external_type, {NULL, NULL, NULL}, }; +bool DRW_engine_external_acquire_for_image_editor(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const SpaceLink *space_data = draw_ctx->space_data; + Scene *scene = draw_ctx->scene; + + if (space_data == NULL) { + return false; + } + + const eSpace_Type space_type = draw_ctx->space_data->spacetype; + if (space_type != SPACE_IMAGE) { + return false; + } + + struct SpaceImage *space_image = (struct SpaceImage *)space_data; + const Image *image = ED_space_image(space_image); + if (image == NULL || image->type != IMA_TYPE_R_RESULT) { + return false; + } + + if (image->render_slot != image->last_render_slot) { + return false; + } + + /* Render is allocated on main thread, so it is safe to access it from here. */ + Render *re = RE_GetSceneRender(scene); + + if (re == NULL) { + return false; + } + + return RE_engine_draw_acquire(re); +} + #undef EXTERNAL_ENGINE diff --git a/source/blender/draw/engines/external/external_engine.h b/source/blender/draw/engines/external/external_engine.h index c645fb99e0e..14ec4e2d3c5 100644 --- a/source/blender/draw/engines/external/external_engine.h +++ b/source/blender/draw/engines/external/external_engine.h @@ -22,4 +22,12 @@ #pragma once +extern DrawEngineType draw_engine_external_type; extern RenderEngineType DRW_engine_viewport_external_type; + +/* Check whether an external engine is to be used to draw content of an image editor. + * If the drawing is possible, the render engine is "acquired" so that it is not freed by the + * render engine for until drawing is finished. + * + * NOTE: Released by the draw engine when it is done drawing. */ +bool DRW_engine_external_acquire_for_image_editor(void); diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index 96ab8a28e09..20edd78597b 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -388,6 +388,8 @@ RenderEngineType DRW_engine_viewport_select_type = { NULL, NULL, NULL, + NULL, + NULL, &draw_engine_select_type, {NULL, NULL, NULL}, }; diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index f09c019ef8d..635aa7cef25 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -651,6 +651,8 @@ RenderEngineType DRW_engine_viewport_workbench_type = { NULL, NULL, NULL, + NULL, + NULL, &workbench_render_update_passes, &draw_engine_workbench, {NULL, NULL, NULL}, diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 660a4adaf51..fb8b8536897 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -623,6 +623,7 @@ const DRWView *DRW_view_default_get(void); void DRW_view_default_set(DRWView *view); void DRW_view_reset(void); void DRW_view_set_active(DRWView *view); +const DRWView *DRW_view_get_active(void); void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len); void DRW_view_camtexco_set(DRWView *view, float texco[4]); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 47adc0acc60..e65fdce5f2e 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1197,6 +1197,18 @@ static void drw_engines_enable_basic(void) use_drw_engine(&draw_engine_basic_type); } +static void drw_engine_enable_image_editor(void) +{ + if (DRW_engine_external_acquire_for_image_editor()) { + use_drw_engine(&draw_engine_external_type); + } + else { + use_drw_engine(&draw_engine_image_type); + } + + use_drw_engine(&draw_engine_overlay_type); +} + static void drw_engines_enable_editors(void) { SpaceLink *space_data = DST.draw_ctx.space_data; @@ -1205,8 +1217,7 @@ static void drw_engines_enable_editors(void) } if (space_data->spacetype == SPACE_IMAGE) { - use_drw_engine(&draw_engine_image_type); - use_drw_engine(&draw_engine_overlay_type); + drw_engine_enable_image_editor(); } else if (space_data->spacetype == SPACE_NODE) { /* Only enable when drawing the space image backdrop. */ @@ -3188,3 +3199,66 @@ void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg) #endif /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw manager context release/activation + * + * These functions are used in cases when an OpenGL context creation is needed during the draw. + * This happens, for example, when an external engine needs to create its own OpenGL context from + * the engine initialization. + * + * Example of context creation: + * + * const bool drw_state = DRW_opengl_context_release(); + * gl_context = WM_opengl_context_create(); + * DRW_opengl_context_activate(drw_state); + * + * Example of context destruction: + * + * const bool drw_state = DRW_opengl_context_release(); + * WM_opengl_context_activate(gl_context); + * WM_opengl_context_dispose(gl_context); + * DRW_opengl_context_activate(drw_state); + * + * + * NOTE: Will only perform context modification when on main thread. This way these functions can + * be used in an engine without check on whether it is a draw manager which manages OpenGL context + * on the current thread. The downside of this is that if the engine performs OpenGL creation from + * a non-main thread, that thread is supposed to not have OpenGL context ever bound by Blender. + * + * \{ */ + +bool DRW_opengl_context_release(void) +{ + if (!BLI_thread_is_main()) { + return false; + } + + if (GPU_context_active_get() != DST.gpu_context) { + /* Context release is requested from the outside of the draw manager main draw loop, indicate + * this to the `DRW_opengl_context_activate()` so that it restores drawable of the window. */ + return false; + } + + GPU_context_active_set(NULL); + WM_opengl_context_release(DST.gl_context); + + return true; +} + +void DRW_opengl_context_activate(bool drw_state) +{ + if (!BLI_thread_is_main()) { + return; + } + + if (drw_state) { + WM_opengl_context_activate(DST.gl_context); + GPU_context_active_set(DST.gpu_context); + } + else { + wm_window_reset_drawable(); + } +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 22356a3c57b..aa01ca7a262 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -367,6 +367,11 @@ void DRW_view_set_active(DRWView *view) DST.view_active = (view) ? view : DST.view_default; } +const DRWView *DRW_view_get_active(void) +{ + return DST.view_active; +} + /* Return True if the given BoundSphere intersect the current view frustum */ static bool draw_culling_sphere_test(const BoundSphere *frustum_bsphere, const float (*frustum_planes)[4], diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 0a2df655395..26f5b21a311 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -412,6 +412,7 @@ static bool is_noncolor_pass(eScenePassType pass_type) { return ELEM(pass_type, SCE_PASS_Z, + SCE_PASS_POSITION, SCE_PASS_NORMAL, SCE_PASS_VECTOR, SCE_PASS_INDEXOB, @@ -554,19 +555,10 @@ static bool bake_pass_filter_check(eScenePassType pass_type, return true; } - if ((pass_filter & R_BAKE_PASS_FILTER_AO) != 0) { - BKE_report( - reports, - RPT_ERROR, - "Combined bake pass Ambient Occlusion contribution requires an enabled light pass " - "(bake the Ambient Occlusion pass type instead)"); - } - else { - BKE_report(reports, - RPT_ERROR, - "Combined bake pass requires Emit, or a light pass with " - "Direct or Indirect contributions enabled"); - } + BKE_report(reports, + RPT_ERROR, + "Combined bake pass requires Emit, or a light pass with " + "Direct or Indirect contributions enabled"); return false; } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 95351de45f0..81aecfdf788 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -479,15 +479,6 @@ static Scene *preview_prepare_scene( BKE_color_managed_view_settings_free(&sce->view_settings); BKE_color_managed_view_settings_copy(&sce->view_settings, &scene->view_settings); - /* prevent overhead for small renders and icons (32) */ - if (id && sp->sizex < 40) { - sce->r.tilex = sce->r.tiley = 64; - } - else { - sce->r.tilex = sce->r.xsch / 4; - sce->r.tiley = sce->r.ysch / 4; - } - if ((id && sp->pr_method == PR_ICON_RENDER) && id_type != ID_WO) { sce->r.alphamode = R_ALPHAPREMUL; } diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 937a10f26b1..0a82c237256 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -94,17 +94,15 @@ BlenderStrokeRenderer::BlenderStrokeRenderer(Render *re, int render_count) freestyle_scene = BKE_scene_add(freestyle_bmain, name); freestyle_scene->r.cfra = old_scene->r.cfra; freestyle_scene->r.mode = old_scene->r.mode & ~(R_EDGE_FRS | R_BORDER); - freestyle_scene->r.xsch = re->rectx; // old_scene->r.xsch - freestyle_scene->r.ysch = re->recty; // old_scene->r.ysch - freestyle_scene->r.xasp = 1.0f; // old_scene->r.xasp; - freestyle_scene->r.yasp = 1.0f; // old_scene->r.yasp; - freestyle_scene->r.tilex = old_scene->r.tilex; - freestyle_scene->r.tiley = old_scene->r.tiley; + freestyle_scene->r.xsch = re->rectx; // old_scene->r.xsch + freestyle_scene->r.ysch = re->recty; // old_scene->r.ysch + freestyle_scene->r.xasp = 1.0f; // old_scene->r.xasp; + freestyle_scene->r.yasp = 1.0f; // old_scene->r.yasp; freestyle_scene->r.size = 100; // old_scene->r.size freestyle_scene->r.color_mgt_flag = 0; // old_scene->r.color_mgt_flag; freestyle_scene->r.scemode = (old_scene->r.scemode & ~(R_SINGLE_LAYER | R_NO_FRAME_UPDATE | R_MULTIVIEW)) & - (re->r.scemode | ~R_FULL_SAMPLE); + (re->r.scemode); freestyle_scene->r.flag = old_scene->r.flag; freestyle_scene->r.threads = old_scene->r.threads; freestyle_scene->r.border.xmin = old_scene->r.border.xmin; diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 312da491a36..e64521768f9 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -175,10 +175,7 @@ GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat, void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash); -void GPU_material_sss_profile_create(GPUMaterial *material, - float radii[3], - const short *falloff_type, - const float *sharpness); +void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]); struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, int sample_len, struct GPUTexture **tex_profile); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 56e72fbeca9..6872a08e854 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -96,8 +96,6 @@ struct GPUMaterial { float sss_enabled; float sss_radii[3]; int sss_samples; - short int sss_falloff; - float sss_sharpness; bool sss_dirty; GPUTexture *coba_tex; /* 1D Texture array containing all color bands. */ @@ -266,18 +264,6 @@ static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponen } } -#define GAUSS_TRUNCATE 12.46f -static float gaussian_profile(float r, float radius) -{ - const float v = radius * radius * (0.25f * 0.25f); - const float Rm = sqrtf(v * GAUSS_TRUNCATE); - - if (r >= Rm) { - return 0.0f; - } - return expf(-r * r / (2.0f * v)) / (2.0f * M_PI * v); -} - #define BURLEY_TRUNCATE 16.0f #define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) static float burley_profile(float r, float d) @@ -287,45 +273,15 @@ static float burley_profile(float r, float d) return (exp_r_d + exp_r_3_d) / (4.0f * d); } -static float cubic_profile(float r, float radius, float sharpness) -{ - float Rm = radius * (1.0f + sharpness); - - if (r >= Rm) { - return 0.0f; - } - /* custom variation with extra sharpness, to match the previous code */ - const float y = 1.0f / (1.0f + sharpness); - float Rmy, ry, ryinv; - - Rmy = powf(Rm, y); - ry = powf(r, y); - ryinv = (r > 0.0f) ? powf(r, y - 1.0f) : 0.0f; - - const float Rmy5 = (Rmy * Rmy) * (Rmy * Rmy) * Rmy; - const float f = Rmy - ry; - const float num = f * (f * f) * (y * ryinv); - - return (10.0f * num) / (Rmy5 * M_PI); -} - -static float eval_profile(float r, short falloff_type, float sharpness, float param) +static float eval_profile(float r, float param) { r = fabsf(r); - - if (ELEM(falloff_type, SHD_SUBSURFACE_BURLEY, SHD_SUBSURFACE_RANDOM_WALK)) { - return burley_profile(r, param) / BURLEY_TRUNCATE_CDF; - } - if (falloff_type == SHD_SUBSURFACE_CUBIC) { - return cubic_profile(r, param, sharpness); - } - - return gaussian_profile(r, param); + return burley_profile(r, param) / BURLEY_TRUNCATE_CDF; } /* Resolution for each sample of the precomputed kernel profile */ #define INTEGRAL_RESOLUTION 32 -static float eval_integral(float x0, float x1, short falloff_type, float sharpness, float param) +static float eval_integral(float x0, float x1, float param) { const float range = x1 - x0; const float step = range / INTEGRAL_RESOLUTION; @@ -333,7 +289,7 @@ static float eval_integral(float x0, float x1, short falloff_type, float sharpne for (int i = 0; i < INTEGRAL_RESOLUTION; i++) { float x = x0 + range * ((float)i + 0.5f) / (float)INTEGRAL_RESOLUTION; - float y = eval_profile(x, falloff_type, sharpness, param); + float y = eval_profile(x, param); integral += y * step; } @@ -341,8 +297,7 @@ static float eval_integral(float x0, float x1, short falloff_type, float sharpne } #undef INTEGRAL_RESOLUTION -static void compute_sss_kernel( - GPUSssKernelData *kd, const float radii[3], int sample_len, int falloff_type, float sharpness) +static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int sample_len) { float rad[3]; /* Minimum radius */ @@ -353,27 +308,15 @@ static void compute_sss_kernel( /* Christensen-Burley fitting */ float l[3], d[3]; - if (ELEM(falloff_type, SHD_SUBSURFACE_BURLEY, SHD_SUBSURFACE_RANDOM_WALK)) { - mul_v3_v3fl(l, rad, 0.25f * M_1_PI); - const float A = 1.0f; - const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f); - /* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */ - mul_v3_v3fl(d, l, 0.6f / s); - mul_v3_v3fl(rad, d, BURLEY_TRUNCATE); - kd->max_radius = MAX3(rad[0], rad[1], rad[2]); + mul_v3_v3fl(l, rad, 0.25f * M_1_PI); + const float A = 1.0f; + const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f); + /* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */ + mul_v3_v3fl(d, l, 0.6f / s); + mul_v3_v3fl(rad, d, BURLEY_TRUNCATE); + kd->max_radius = MAX3(rad[0], rad[1], rad[2]); - copy_v3_v3(kd->param, d); - } - else if (falloff_type == SHD_SUBSURFACE_CUBIC) { - copy_v3_v3(kd->param, rad); - mul_v3_fl(rad, 1.0f + sharpness); - kd->max_radius = MAX3(rad[0], rad[1], rad[2]); - } - else { - kd->max_radius = MAX3(rad[0], rad[1], rad[2]); - - copy_v3_v3(kd->param, rad); - } + copy_v3_v3(kd->param, d); /* Compute samples locations on the 1d kernel [-1..1] */ sss_calculate_offsets(kd, sample_len, SSS_EXPONENT); @@ -403,9 +346,9 @@ static void compute_sss_kernel( x0 *= kd->max_radius; x1 *= kd->max_radius; - kd->kernel[i][0] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[0]); - kd->kernel[i][1] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[1]); - kd->kernel[i][2] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[2]); + kd->kernel[i][0] = eval_integral(x0, x1, kd->param[0]); + kd->kernel[i][1] = eval_integral(x0, x1, kd->param[1]); + kd->kernel[i][2] = eval_integral(x0, x1, kd->param[2]); sum[0] += kd->kernel[i][0]; sum[1] += kd->kernel[i][1]; @@ -439,8 +382,6 @@ static void compute_sss_kernel( #define INTEGRAL_RESOLUTION 512 static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, int resolution, - short falloff_type, - float sharpness, float **output) { float(*texels)[4]; @@ -463,9 +404,9 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, float dist = hypotf(r + r_step * 0.5f, d); float profile[3]; - profile[0] = eval_profile(dist, falloff_type, sharpness, kd->param[0]); - profile[1] = eval_profile(dist, falloff_type, sharpness, kd->param[1]); - profile[2] = eval_profile(dist, falloff_type, sharpness, kd->param[2]); + profile[0] = eval_profile(dist, kd->param[0]); + profile[1] = eval_profile(dist, kd->param[1]); + profile[2] = eval_profile(dist, kd->param[2]); /* Since the profile and configuration are radially symmetrical we * can just evaluate it once and weight it accordingly */ @@ -499,14 +440,9 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, } #undef INTEGRAL_RESOLUTION -void GPU_material_sss_profile_create(GPUMaterial *material, - float radii[3], - const short *falloff_type, - const float *sharpness) +void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]) { copy_v3_v3(material->sss_radii, radii); - material->sss_falloff = (falloff_type) ? *falloff_type : 0.0; - material->sss_sharpness = (sharpness) ? *sharpness : 0.0; material->sss_dirty = true; material->sss_enabled = true; @@ -527,20 +463,14 @@ struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, if (material->sss_dirty || (material->sss_samples != sample_len)) { GPUSssKernelData kd; - float sharpness = material->sss_sharpness; - - /* XXX Black magic but it seems to fit. Maybe because we integrate -1..1 */ - sharpness *= 0.5f; - - compute_sss_kernel(&kd, material->sss_radii, sample_len, material->sss_falloff, sharpness); + compute_sss_kernel(&kd, material->sss_radii, sample_len); /* Update / Create UBO */ GPU_uniformbuf_update(material->sss_profile, &kd); /* Update / Create Tex */ float *translucence_profile; - compute_sss_translucence_kernel( - &kd, 64, material->sss_falloff, sharpness, &translucence_profile); + compute_sss_translucence_kernel(&kd, 64, &translucence_profile); if (material->sss_tex_profile != NULL) { GPU_texture_free(material->sss_tex_profile); diff --git a/source/blender/gpu/intern/gpu_material_library.h b/source/blender/gpu/intern/gpu_material_library.h index 782d89d6f2a..d3b12d3a2b7 100644 --- a/source/blender/gpu/intern/gpu_material_library.h +++ b/source/blender/gpu/intern/gpu_material_library.h @@ -27,7 +27,7 @@ #include "GPU_material.h" #define MAX_FUNCTION_NAME 64 -#define MAX_PARAMETER 32 +#define MAX_PARAMETER 36 struct GSet; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index d77259638fd..bba84c2be52 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -19,6 +19,8 @@ void node_bsdf_principled(vec4 base_color, float subsurface, vec3 subsurface_radius, vec4 subsurface_color, + float subsurface_ior, + float subsurface_anisotropy, float metallic, float specular, float specular_tint, @@ -201,6 +203,6 @@ void node_bsdf_principled(vec4 base_color, #else /* clang-format off */ /* Stub principled because it is not compatible with volumetrics. */ -# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, cc, dd, result) (result = CLOSURE_DEFAULT) +# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, cc, dd, ee, ff, result) (result = CLOSURE_DEFAULT) /* clang-format on */ #endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl index 5129bf71903..d0c159cdf37 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl @@ -5,8 +5,8 @@ CLOSURE_EVAL_FUNCTION_DECLARE_1(node_subsurface_scattering, Diffuse) void node_subsurface_scattering(vec4 color, float scale, vec3 radius, - float sharpen, - float texture_blur, + float ior, + float anisotropy, vec3 N, float sss_id, out Closure result) @@ -20,15 +20,7 @@ void node_subsurface_scattering(vec4 color, result = CLOSURE_DEFAULT; - /* Not perfect for texture_blur values between 0.0 and 1.0. - * Interpolate between separated color and color applied on irradiance. */ - float one_minus_texture_blur = 1.0 - texture_blur; - vec3 sss_albedo = color.rgb * one_minus_texture_blur + texture_blur; - vec3 radiance_tint = color.rgb * texture_blur + one_minus_texture_blur; - /* Consider output radiance as irradiance. */ - out_Diffuse_0.radiance *= radiance_tint; - - closure_load_sss_data(scale, out_Diffuse_0.radiance, sss_albedo, int(sss_id), result); + closure_load_sss_data(scale, out_Diffuse_0.radiance, color.rgb, int(sss_id), result); /* TODO(fclem) Try to not use this. */ closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result); diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index 63e4597150c..520f989452c 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -68,7 +68,7 @@ typedef enum eViewLayerCryptomatteFlags { VIEW_LAYER_CRYPTOMATTE_OBJECT = (1 << 0), VIEW_LAYER_CRYPTOMATTE_MATERIAL = (1 << 1), VIEW_LAYER_CRYPTOMATTE_ASSET = (1 << 2), - VIEW_LAYER_CRYPTOMATTE_ACCURATE = (1 << 3), + /* VIEW_LAYER_CRYPTOMATTE_ACCURATE = (1 << 3), */ /* DEPRECATED */ } eViewLayerCryptomatteFlags; #define VIEW_LAYER_CRYPTOMATTE_ALL \ (VIEW_LAYER_CRYPTOMATTE_OBJECT | VIEW_LAYER_CRYPTOMATTE_MATERIAL | VIEW_LAYER_CRYPTOMATTE_ASSET) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 49083542fd7..cf159a1e28d 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1032,6 +1032,11 @@ typedef struct NodeShaderTexPointDensity { char _pad2[4]; } NodeShaderTexPointDensity; +typedef struct NodeShaderPrincipled { + char use_subsurface_auto_radius; + char _pad[3]; +} NodeShaderPrincipled; + /* TEX_output */ typedef struct TexNodeOutput { char name[64]; @@ -1803,11 +1808,12 @@ enum { enum { #ifdef DNA_DEPRECATED_ALLOW SHD_SUBSURFACE_COMPATIBLE = 0, /* Deprecated */ -#endif SHD_SUBSURFACE_CUBIC = 1, SHD_SUBSURFACE_GAUSSIAN = 2, - SHD_SUBSURFACE_BURLEY = 3, - SHD_SUBSURFACE_RANDOM_WALK = 4, +#endif + SHD_SUBSURFACE_DIFFUSION = 3, + SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS = 4, + SHD_SUBSURFACE_RANDOM_WALK = 5, }; /* blur node */ diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 61707964191..9ecf94ebd6e 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -134,8 +134,6 @@ .border.ymin = 0.0f, \ .border.xmax = 1.0f, \ .border.ymax = 1.0f, \ - \ - .preview_start_resolution = 64, \ \ .line_thickness_mode = R_LINE_THICKNESS_ABSOLUTE, \ .unit_line_thickness = 1.0f, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index f2244b4ae61..b28c3ac2b85 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -261,7 +261,7 @@ typedef enum eScenePassType { SCE_PASS_UNUSED_3 = (1 << 4), /* SPEC */ SCE_PASS_SHADOW = (1 << 5), SCE_PASS_AO = (1 << 6), - SCE_PASS_UNUSED_4 = (1 << 7), /* REFLECT */ + SCE_PASS_POSITION = (1 << 7), SCE_PASS_NORMAL = (1 << 8), SCE_PASS_VECTOR = (1 << 9), SCE_PASS_UNUSED_5 = (1 << 10), /* REFRACT */ @@ -293,6 +293,7 @@ typedef enum eScenePassType { #define RE_PASSNAME_COMBINED "Combined" #define RE_PASSNAME_Z "Depth" #define RE_PASSNAME_VECTOR "Vector" +#define RE_PASSNAME_POSITION "Position" #define RE_PASSNAME_NORMAL "Normal" #define RE_PASSNAME_UV "UV" #define RE_PASSNAME_EMIT "Emit" @@ -592,7 +593,7 @@ typedef enum eBakeSaveMode { /** #BakeData.pass_filter */ typedef enum eBakePassFilter { R_BAKE_PASS_FILTER_NONE = 0, - R_BAKE_PASS_FILTER_AO = (1 << 0), + R_BAKE_PASS_FILTER_UNUSED = (1 << 0), R_BAKE_PASS_FILTER_EMIT = (1 << 1), R_BAKE_PASS_FILTER_DIFFUSE = (1 << 2), R_BAKE_PASS_FILTER_GLOSSY = (1 << 3), @@ -653,7 +654,8 @@ typedef struct RenderData { /** * render tile dimensions */ - int tilex, tiley; + int tilex DNA_DEPRECATED; + int tiley DNA_DEPRECATED; short planes DNA_DEPRECATED; short imtype DNA_DEPRECATED; @@ -764,13 +766,10 @@ typedef struct RenderData { /* Cycles baking */ struct BakeData bake; - int preview_start_resolution; + int _pad8; short preview_pixel_size; - /* Type of the debug pass to use. - * Only used when built with debug passes support. - */ - short debug_pass_type; + short _pad4; /* MultiView */ /** SceneRenderView. */ @@ -1887,12 +1886,12 @@ enum { #define R_COMP_CROP (1 << 7) #define R_SCEMODE_UNUSED_8 (1 << 8) /* cleared */ #define R_SINGLE_LAYER (1 << 9) -#define R_EXR_TILE_FILE (1 << 10) +#define R_SCEMODE_UNUSED_10 (1 << 10) /* cleared */ #define R_SCEMODE_UNUSED_11 (1 << 11) /* cleared */ #define R_NO_IMAGE_LOAD (1 << 12) #define R_SCEMODE_UNUSED_13 (1 << 13) /* cleared */ #define R_NO_FRAME_UPDATE (1 << 14) -#define R_FULL_SAMPLE (1 << 15) +#define R_SCEMODE_UNUSED_15 (1 << 15) /* cleared */ #define R_SCEMODE_UNUSED_16 (1 << 16) /* cleared */ #define R_SCEMODE_UNUSED_17 (1 << 17) /* cleared */ #define R_TEXNODE_PREVIEW (1 << 18) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index d0bf60d5d02..ec53f35df4c 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4665,16 +4665,18 @@ static const EnumPropertyItem node_principled_distribution_items[] = { }; static const EnumPropertyItem node_subsurface_method_items[] = { - {SHD_SUBSURFACE_BURLEY, - "BURLEY", + {SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS, + "RANDOM_WALK_FIXED_RADIUS", 0, - "Christensen-Burley", - "Approximation to physically based volume scattering"}, + "Random Walk (Fixed Radius)", + "Volumetric approximation to physically based volume scattering, using the scattering radius " + "as specified"}, {SHD_SUBSURFACE_RANDOM_WALK, "RANDOM_WALK", 0, "Random Walk", - "Volumetric approximation to physically based volume scattering"}, + "Volumetric approximation to physically based volume scattering, with scattering radius " + "automatically adjusted to match color textures"}, {0, NULL, 0, NULL, NULL}}; /* -- Common nodes ---------------------------------------------------------- */ @@ -6144,35 +6146,12 @@ static void def_sh_ambient_occlusion(StructRNA *srna) static void def_sh_subsurface(StructRNA *srna) { - static const EnumPropertyItem prop_subsurface_falloff_items[] = { - {SHD_SUBSURFACE_CUBIC, "CUBIC", 0, "Cubic", "Simple cubic falloff function"}, - {SHD_SUBSURFACE_GAUSSIAN, - "GAUSSIAN", - 0, - "Gaussian", - "Normal distribution, multiple can be combined to fit more complex profiles"}, - {SHD_SUBSURFACE_BURLEY, - "BURLEY", - 0, - "Christensen-Burley", - "Approximation to physically based volume scattering"}, - {SHD_SUBSURFACE_RANDOM_WALK, - "RANDOM_WALK", - 0, - "Random Walk", - "Volumetric approximation to physically based volume scattering"}, - {0, NULL, 0, NULL, NULL}, - }; - PropertyRNA *prop; prop = RNA_def_property(srna, "falloff", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom1"); - RNA_def_property_enum_items(prop, prop_subsurface_falloff_items); - RNA_def_property_ui_text(prop, - "Falloff", - "Function to determine how much light nearby points contribute based " - "on their distance to the shading point"); + RNA_def_property_enum_items(prop, node_subsurface_method_items); + RNA_def_property_ui_text(prop, "Method", "Method for rendering subsurface scattering"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update"); } diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 4400d198b4a..fcb46904e8d 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -52,6 +52,7 @@ const EnumPropertyItem rna_enum_render_pass_type_items[] = { {SCE_PASS_Z, "Z", 0, "Z", ""}, {SCE_PASS_SHADOW, "SHADOW", 0, "Shadow", ""}, {SCE_PASS_AO, "AO", 0, "Ambient Occlusion", ""}, + {SCE_PASS_POSITION, "POSITION", 0, "Position", ""}, {SCE_PASS_NORMAL, "NORMAL", 0, "Normal", ""}, {SCE_PASS_VECTOR, "VECTOR", 0, "Vector", ""}, {SCE_PASS_INDEXOB, "OBJECT_INDEX", 0, "Object Index", ""}, @@ -79,6 +80,7 @@ const EnumPropertyItem rna_enum_bake_pass_type_items[] = { {SCE_PASS_COMBINED, "COMBINED", 0, "Combined", ""}, {SCE_PASS_AO, "AO", 0, "Ambient Occlusion", ""}, {SCE_PASS_SHADOW, "SHADOW", 0, "Shadow", ""}, + {SCE_PASS_POSITION, "POSITION", 0, "Position", ""}, {SCE_PASS_NORMAL, "NORMAL", 0, "Normal", ""}, {SCE_PASS_UV, "UV", 0, "UV", ""}, {SCE_PASS_ROUGHNESS, "ROUGHNESS", 0, "ROUGHNESS", ""}, @@ -177,6 +179,40 @@ static void engine_render(RenderEngine *engine, Depsgraph *depsgraph) RNA_parameter_list_free(&list); } +static void engine_render_frame_finish(RenderEngine *engine) +{ + extern FunctionRNA rna_RenderEngine_render_frame_finish_func; + PointerRNA ptr; + ParameterList list; + FunctionRNA *func; + + RNA_pointer_create(NULL, engine->type->rna_ext.srna, engine, &ptr); + func = &rna_RenderEngine_render_frame_finish_func; + + RNA_parameter_list_create(&list, &ptr, func); + engine->type->rna_ext.call(NULL, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + +static void engine_draw(RenderEngine *engine, const struct bContext *context, Depsgraph *depsgraph) +{ + extern FunctionRNA rna_RenderEngine_draw_func; + PointerRNA ptr; + ParameterList list; + FunctionRNA *func; + + RNA_pointer_create(NULL, engine->type->rna_ext.srna, engine, &ptr); + func = &rna_RenderEngine_draw_func; + + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "context", &context); + RNA_parameter_set_lookup(&list, "depsgraph", &depsgraph); + engine->type->rna_ext.call(NULL, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + static void engine_bake(RenderEngine *engine, struct Depsgraph *depsgraph, struct Object *object, @@ -315,7 +351,7 @@ static StructRNA *rna_RenderEngine_register(Main *bmain, RenderEngineType *et, dummyet = {NULL}; RenderEngine dummyengine = {NULL}; PointerRNA dummyptr; - int have_function[8]; + int have_function[9]; /* setup dummy engine & engine type to store static properties in */ dummyengine.type = &dummyet; @@ -358,11 +394,13 @@ static StructRNA *rna_RenderEngine_register(Main *bmain, et->update = (have_function[0]) ? engine_update : NULL; et->render = (have_function[1]) ? engine_render : NULL; - et->bake = (have_function[2]) ? engine_bake : NULL; - et->view_update = (have_function[3]) ? engine_view_update : NULL; - et->view_draw = (have_function[4]) ? engine_view_draw : NULL; - et->update_script_node = (have_function[5]) ? engine_update_script_node : NULL; - et->update_render_passes = (have_function[6]) ? engine_update_render_passes : NULL; + et->render_frame_finish = (have_function[2]) ? engine_render_frame_finish : NULL; + et->draw = (have_function[3]) ? engine_draw : NULL; + et->bake = (have_function[4]) ? engine_bake : NULL; + et->view_update = (have_function[5]) ? engine_view_update : NULL; + et->view_draw = (have_function[6]) ? engine_view_draw : NULL; + et->update_script_node = (have_function[7]) ? engine_update_script_node : NULL; + et->update_render_passes = (have_function[8]) ? engine_update_render_passes : NULL; RE_engines_register(et); @@ -519,6 +557,19 @@ static void rna_def_render_engine(BlenderRNA *brna) parm = RNA_def_pointer(func, "depsgraph", "Depsgraph", "", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + func = RNA_def_function(srna, "render_frame_finish", NULL); + RNA_def_function_ui_description( + func, "Perform finishing operations after all view layers in a frame were rendered"); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); + + func = RNA_def_function(srna, "draw", NULL); + RNA_def_function_ui_description(func, "Draw render image"); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "depsgraph", "Depsgraph", "", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + func = RNA_def_function(srna, "bake", NULL); RNA_def_function_ui_description(func, "Bake passes"); RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); @@ -641,6 +692,14 @@ static void rna_def_render_engine(BlenderRNA *brna) parm = RNA_def_boolean(func, "do_break", 0, "Break", ""); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "pass_by_index_get", "RE_engine_pass_by_index_get"); + parm = RNA_def_string(func, "layer", NULL, 0, "Layer", "Name of render layer to get pass for"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_int(func, "index", 0, 0, INT_MAX, "Index", "Index of pass to get", 0, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "render_pass", "RenderPass", "Index", "Index of pass to get"); + RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "active_view_get", "RE_engine_active_view_get"); parm = RNA_def_string(func, "view", NULL, 0, "View", "Single view active"); RNA_def_function_return(func, parm); @@ -761,6 +820,22 @@ static void rna_def_render_engine(BlenderRNA *brna) func = RNA_def_function(srna, "free_blender_memory", "RE_engine_free_blender_memory"); RNA_def_function_ui_description(func, "Free Blender side memory of render engine"); + func = RNA_def_function(srna, "tile_highlight_set", "RE_engine_tile_highlight_set"); + RNA_def_function_ui_description(func, "Set highlighted state of the given tile"); + parm = RNA_def_int(func, "x", 0, 0, INT_MAX, "X", "", 0, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_int(func, "y", 0, 0, INT_MAX, "Y", "", 0, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_int(func, "width", 0, 0, INT_MAX, "Width", "", 0, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_int(func, "height", 0, 0, INT_MAX, "Height", "", 0, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, "highlight", 0, "Highlight", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + + func = RNA_def_function(srna, "tile_highlight_clear_all", "RE_engine_tile_highlight_clear_all"); + RNA_def_function_ui_description(func, "Clear highlight from all tiles"); + RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "is_animation", PROP_BOOLEAN, PROP_NONE); @@ -777,11 +852,6 @@ static void rna_def_render_engine(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "layer_override", 1); RNA_def_property_array(prop, 20); - prop = RNA_def_property(srna, "tile_x", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_sdna(prop, NULL, "tile_x"); - prop = RNA_def_property(srna, "tile_y", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_sdna(prop, NULL, "tile_y"); - prop = RNA_def_property(srna, "resolution_x", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "resolution_x"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -880,12 +950,6 @@ static void rna_def_render_engine(BlenderRNA *brna) "Don't expose Cycles and Eevee shading nodes in the node editor user " "interface, so own nodes can be used instead"); - prop = RNA_def_property(srna, "bl_use_save_buffers", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_SAVE_BUFFERS); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - RNA_def_property_ui_text( - prop, "Use Save Buffers", "Support render to an on disk buffer during rendering"); - prop = RNA_def_property(srna, "bl_use_spherical_stereo", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_SPHERICAL_STEREO); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 1762b964f8d..e45d39a1ddc 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -532,7 +532,6 @@ const EnumPropertyItem rna_enum_stereo3d_interlace_type_items[] = { const EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { {R_BAKE_PASS_FILTER_NONE, "NONE", 0, "None", ""}, - {R_BAKE_PASS_FILTER_AO, "AO", 0, "Ambient Occlusion", ""}, {R_BAKE_PASS_FILTER_EMIT, "EMIT", 0, "Emit", ""}, {R_BAKE_PASS_FILTER_DIRECT, "DIRECT", 0, "Direct", ""}, {R_BAKE_PASS_FILTER_INDIRECT, "INDIRECT", 0, "Indirect", ""}, @@ -4151,13 +4150,6 @@ void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool sce prop, "Cryptomatte Levels", "Sets how many unique objects can be distinguished per pixel"); RNA_def_property_ui_range(prop, 2.0, 16.0, 2.0, 0.0); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update"); - - prop = RNA_def_property(srna, "use_pass_cryptomatte_accurate", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "cryptomatte_flag", VIEW_LAYER_CRYPTOMATTE_ACCURATE); - RNA_def_property_boolean_default(prop, true); - RNA_def_property_ui_text( - prop, "Cryptomatte Accurate", "Generate a more accurate cryptomatte pass"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update"); } prop = RNA_def_property(srna, "use_solid", PROP_BOOLEAN, PROP_NONE); @@ -4251,6 +4243,16 @@ void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool sce RNA_def_property_clear_flag(prop, PROP_EDITABLE); } + prop = RNA_def_property(srna, "use_pass_position", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "passflag", SCE_PASS_POSITION); + RNA_def_property_ui_text(prop, "Position", "Deliver position pass"); + if (scene) { + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update"); + } + else { + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + } + prop = RNA_def_property(srna, "use_pass_normal", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "passflag", SCE_PASS_NORMAL); RNA_def_property_ui_text(prop, "Normal", "Deliver normal pass"); @@ -5122,10 +5124,6 @@ static void rna_def_bake_data(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); /* custom passes flags */ - prop = RNA_def_property(srna, "use_pass_ambient_occlusion", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "pass_filter", R_BAKE_PASS_FILTER_AO); - RNA_def_property_ui_text(prop, "Ambient Occlusion", "Add ambient occlusion contribution"); - prop = RNA_def_property(srna, "use_pass_emit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "pass_filter", R_BAKE_PASS_FILTER_EMIT); RNA_def_property_ui_text(prop, "Emit", "Add emission contribution"); @@ -5934,29 +5932,6 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Resolution %", "Percentage scale for render resolution"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_SceneSequencer_update"); - prop = RNA_def_property(srna, "tile_x", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "tilex"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_range(prop, 8, 65536); - RNA_def_property_ui_text(prop, "Tile X", "Horizontal tile size to use while rendering"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - - prop = RNA_def_property(srna, "tile_y", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "tiley"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_range(prop, 8, 65536); - RNA_def_property_ui_text(prop, "Tile Y", "Vertical tile size to use while rendering"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - - prop = RNA_def_property(srna, "preview_start_resolution", PROP_INT, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_range(prop, 8, 16384); - RNA_def_property_ui_text(prop, - "Start Resolution", - "Resolution to start rendering preview at, " - "progressively increasing it to the full viewport size"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - prop = RNA_def_property(srna, "preview_pixel_size", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "preview_pixel_size"); RNA_def_property_enum_items(prop, pixel_size_items); @@ -6213,24 +6188,6 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Movie Format", "When true the format is a movie"); - prop = RNA_def_property(srna, "use_save_buffers", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "scemode", R_EXR_TILE_FILE); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text( - prop, - "Save Buffers", - "Save tiles for all RenderLayers and SceneNodes to files in the temp directory " - "(saves memory, required for Full Sample)"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - - prop = RNA_def_property(srna, "use_full_sample", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "scemode", R_FULL_SAMPLE); - RNA_def_property_ui_text(prop, - "Full Sample", - "Save for every anti-aliasing sample the entire RenderLayer results " - "(this solves anti-aliasing issues with compositing)"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - prop = RNA_def_property(srna, "use_lock_interface", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_lock_interface", 1); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c index 243300b0a44..a56dfea9dbf 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.c +++ b/source/blender/nodes/composite/nodes/node_composite_image.c @@ -45,7 +45,7 @@ static bNodeSocketTemplate cmp_node_rlayers_out[] = { {SOCK_VECTOR, N_(RE_PASSNAME_NORMAL), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_VECTOR, N_(RE_PASSNAME_UV), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_VECTOR, N_(RE_PASSNAME_VECTOR), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + {SOCK_VECTOR, N_(RE_PASSNAME_POSITION), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_RGBA, N_(RE_PASSNAME_SHADOW), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, @@ -72,7 +72,7 @@ static bNodeSocketTemplate cmp_node_rlayers_out[] = { {SOCK_RGBA, N_(RE_PASSNAME_SUBSURFACE_COLOR), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {-1, ""}, }; -#define MAX_LEGACY_SOCKET_INDEX 30 +#define NUM_LEGACY_SOCKETS (ARRAY_SIZE(cmp_node_rlayers_out) - 1) static void cmp_node_image_add_pass_output(bNodeTree *ntree, bNode *node, @@ -382,7 +382,7 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl break; } } - if (!link && (!rlayer || sock_index > MAX_LEGACY_SOCKET_INDEX)) { + if (!link && (!rlayer || sock_index >= NUM_LEGACY_SOCKETS)) { MEM_freeN(sock->storage); nodeRemoveSocket(ntree, node, sock); } @@ -468,43 +468,12 @@ void node_cmp_rlayers_outputs(bNodeTree *ntree, bNode *node) const char *node_cmp_rlayers_sock_to_pass(int sock_index) { - const char *sock_to_passname[] = { - RE_PASSNAME_COMBINED, - RE_PASSNAME_COMBINED, - RE_PASSNAME_Z, - RE_PASSNAME_NORMAL, - RE_PASSNAME_UV, - RE_PASSNAME_VECTOR, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_SHADOW, - RE_PASSNAME_AO, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_INDEXOB, - RE_PASSNAME_INDEXMA, - RE_PASSNAME_MIST, - RE_PASSNAME_EMIT, - RE_PASSNAME_ENVIRONMENT, - RE_PASSNAME_DIFFUSE_DIRECT, - RE_PASSNAME_DIFFUSE_INDIRECT, - RE_PASSNAME_DIFFUSE_COLOR, - RE_PASSNAME_GLOSSY_DIRECT, - RE_PASSNAME_GLOSSY_INDIRECT, - RE_PASSNAME_GLOSSY_COLOR, - RE_PASSNAME_TRANSM_DIRECT, - RE_PASSNAME_TRANSM_INDIRECT, - RE_PASSNAME_TRANSM_COLOR, - RE_PASSNAME_SUBSURFACE_DIRECT, - RE_PASSNAME_SUBSURFACE_INDIRECT, - RE_PASSNAME_SUBSURFACE_COLOR, - }; - if (sock_index > MAX_LEGACY_SOCKET_INDEX) { + if (sock_index >= NUM_LEGACY_SOCKETS) { return NULL; } - return sock_to_passname[sock_index]; + const char *name = cmp_node_rlayers_out[sock_index].name; + /* Exception for alpha, which is derived from Combined. */ + return (STREQ(name, "Alpha")) ? RE_PASSNAME_COMBINED : name; } static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr) diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c index f601f3e9fd0..06f4d1f1b79 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c @@ -35,6 +35,8 @@ static bNodeSocketTemplate sh_node_bsdf_principled_in[] = { PROP_NONE, SOCK_COMPACT}, {SOCK_RGBA, N_("Subsurface Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, + {SOCK_FLOAT, N_("Subsurface IOR"), 1.4f, 0.0f, 0.0f, 0.0f, 1.01f, 3.8f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Subsurface Anisotropy"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_FLOAT, N_("Metallic"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_FLOAT, N_("Specular"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_FLOAT, N_("Specular Tint"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, @@ -74,7 +76,7 @@ static bNodeSocketTemplate sh_node_bsdf_principled_out[] = { static void node_shader_init_principled(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = SHD_GLOSSY_GGX; - node->custom2 = SHD_SUBSURFACE_BURLEY; + node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; } #define socket_not_zero(sock) (in[sock].link || (clamp_f(in[sock].vec[0], 0.0f, 1.0f) > 1e-5f)) @@ -90,41 +92,40 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, GPUNodeLink *sss_scale; /* Normals */ - if (!in[20].link) { - GPU_link(mat, "world_normals_get", &in[20].link); + if (!in[22].link) { + GPU_link(mat, "world_normals_get", &in[22].link); } /* Clearcoat Normals */ - if (!in[21].link) { - GPU_link(mat, "world_normals_get", &in[21].link); + if (!in[23].link) { + GPU_link(mat, "world_normals_get", &in[23].link); } #if 0 /* Not used at the moment. */ /* Tangents */ - if (!in[22].link) { + if (!in[24].link) { GPUNodeLink *orco = GPU_attribute(CD_ORCO, ""); - GPU_link(mat, "tangent_orco_z", orco, &in[22].link); + GPU_link(mat, "tangent_orco_z", orco, &in[24].link); GPU_link(mat, "node_tangent", GPU_builtin(GPU_WORLD_NORMAL), - in[22].link, + in[24].link, GPU_builtin(GPU_OBJECT_MATRIX), - &in[22].link); + &in[24].link); } #endif - bool use_diffuse = socket_not_one(4) && socket_not_one(15); + bool use_diffuse = socket_not_one(6) && socket_not_one(17); bool use_subsurf = socket_not_zero(1) && use_diffuse && node->sss_id > 0; - bool use_refract = socket_not_one(4) && socket_not_zero(15); - bool use_clear = socket_not_zero(12); + bool use_refract = socket_not_one(6) && socket_not_zero(17); + bool use_clear = socket_not_zero(14); /* SSS Profile */ if (use_subsurf) { - static short profile = SHD_SUBSURFACE_BURLEY; bNodeSocket *socket = BLI_findlink(&node->original->inputs, 2); bNodeSocketValueRGBA *socket_data = socket->default_value; /* For some reason it seems that the socket value is in ARGB format. */ - GPU_material_sss_profile_create(mat, &socket_data->value[1], &profile, NULL); + GPU_material_sss_profile_create(mat, &socket_data->value[1]); } if (in[2].link) { diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c index 4b91bcbd11c..e917858e0f2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c +++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c @@ -25,8 +25,8 @@ static bNodeSocketTemplate sh_node_subsurface_scattering_in[] = { {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, {SOCK_FLOAT, N_("Scale"), 1.0, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, {SOCK_VECTOR, N_("Radius"), 1.0f, 0.2f, 0.1f, 0.0f, 0.0f, 100.0f, PROP_NONE, SOCK_COMPACT}, - {SOCK_FLOAT, N_("Sharpness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Texture Blur"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("IOR"), 1.4f, 0.0f, 0.0f, 0.0f, 1.01f, 3.8f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Anisotropy"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, {-1, ""}, }; @@ -38,7 +38,8 @@ static bNodeSocketTemplate sh_node_subsurface_scattering_out[] = { static void node_shader_init_subsurface_scattering(bNodeTree *UNUSED(ntree), bNode *node) { - node->custom1 = SHD_SUBSURFACE_BURLEY; + node->custom1 = SHD_SUBSURFACE_RANDOM_WALK; + node->custom2 = true; } static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, @@ -54,11 +55,8 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, if (node->sss_id > 0) { bNodeSocket *socket = BLI_findlink(&node->original->inputs, 2); bNodeSocketValueRGBA *socket_data = socket->default_value; - bNodeSocket *socket_sharp = BLI_findlink(&node->original->inputs, 3); - bNodeSocketValueFloat *socket_data_sharp = socket_sharp->default_value; /* For some reason it seems that the socket value is in ARGB format. */ - GPU_material_sss_profile_create( - mat, &socket_data->value[1], &node->original->custom1, &socket_data_sharp->value); + GPU_material_sss_profile_create(mat, &socket_data->value[1]); /* sss_id is 0 only the node is not connected to any output. * In this case flagging the material would trigger a bug (see T68736). */ @@ -69,23 +67,6 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, mat, node, "node_subsurface_scattering", in, out, GPU_constant(&node->sss_id)); } -static void node_shader_update_subsurface_scattering(bNodeTree *UNUSED(ntree), bNode *node) -{ - bNodeSocket *sock; - int falloff = node->custom1; - - for (sock = node->inputs.first; sock; sock = sock->next) { - if (STREQ(sock->name, "Sharpness")) { - if (falloff == SHD_SUBSURFACE_CUBIC) { - sock->flag &= ~SOCK_UNAVAIL; - } - else { - sock->flag |= SOCK_UNAVAIL; - } - } - } -} - /* node type definition */ void register_node_type_sh_subsurface_scattering(void) { @@ -99,7 +80,6 @@ void register_node_type_sh_subsurface_scattering(void) node_type_init(&ntype, node_shader_init_subsurface_scattering); node_type_storage(&ntype, "", NULL, NULL); node_type_gpu(&ntype, node_shader_gpu_subsurface_scattering); - node_type_update(&ntype, node_shader_update_subsurface_scattering); nodeRegisterType(&ntype); } diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt index 0046474d064..494415a4077 100644 --- a/source/blender/render/CMakeLists.txt +++ b/source/blender/render/CMakeLists.txt @@ -59,7 +59,6 @@ set(SRC RE_pipeline.h RE_texture.h - intern/initrender.h intern/pipeline.h intern/render_result.h intern/render_types.h diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h index dfc0d5d0e9f..2a3a5964262 100644 --- a/source/blender/render/RE_engine.h +++ b/source/blender/render/RE_engine.h @@ -40,6 +40,7 @@ struct RenderData; struct RenderEngine; struct RenderEngineType; struct RenderLayer; +struct RenderPass; struct RenderResult; struct ReportList; struct Scene; @@ -59,7 +60,7 @@ extern "C" { #define RE_USE_PREVIEW 4 #define RE_USE_POSTPROCESS 8 #define RE_USE_EEVEE_VIEWPORT 16 -#define RE_USE_SAVE_BUFFERS 32 +/* #define RE_USE_SAVE_BUFFERS_DEPRECATED 32 */ #define RE_USE_SHADING_NODES_CUSTOM 64 #define RE_USE_SPHERICAL_STEREO 128 #define RE_USE_STEREO_VIEWPORT 256 @@ -75,6 +76,7 @@ extern "C" { #define RE_ENGINE_DO_UPDATE 8 #define RE_ENGINE_RENDERING 16 #define RE_ENGINE_HIGHLIGHT_TILES 32 +#define RE_ENGINE_CAN_DRAW 64 extern ListBase R_engines; @@ -87,7 +89,20 @@ typedef struct RenderEngineType { int flag; void (*update)(struct RenderEngine *engine, struct Main *bmain, struct Depsgraph *depsgraph); + void (*render)(struct RenderEngine *engine, struct Depsgraph *depsgraph); + + /* Offline rendering is finished - no more view layers will be rendered. + * + * All the pending data is to be communicated from the engine back to Blender. In a possibly + * most memory-efficient manner (engine might free its database before making Blender to allocate + * full-frame render result). */ + void (*render_frame_finish)(struct RenderEngine *engine); + + void (*draw)(struct RenderEngine *engine, + const struct bContext *context, + struct Depsgraph *depsgraph); + void (*bake)(struct RenderEngine *engine, struct Depsgraph *depsgraph, struct Object *object, @@ -132,9 +147,6 @@ typedef struct RenderEngine { struct Object *camera_override; unsigned int layer_override; - int tile_x; - int tile_y; - struct Render *re; ListBase fullresult; char text[512]; /* IMA_MAX_RENDER_TEXT */ @@ -189,6 +201,10 @@ void RE_engine_end_result(RenderEngine *engine, bool merge_results); struct RenderResult *RE_engine_get_result(struct RenderEngine *engine); +struct RenderPass *RE_engine_pass_by_index_get(struct RenderEngine *engine, + const char *layer_name, + int index); + const char *RE_engine_active_view_get(RenderEngine *engine); void RE_engine_active_view_set(RenderEngine *engine, const char *viewname); float RE_engine_get_camera_shift_x(RenderEngine *engine, @@ -228,6 +244,24 @@ void RE_engine_register_pass(struct RenderEngine *engine, bool RE_engine_use_persistent_data(struct RenderEngine *engine); +struct RenderEngine *RE_engine_get(const struct Render *re); + +/* Acquire render engine for drawing via its `draw()` callback. + * + * If drawing is not possible false is returned. If drawing is possible then the engine is + * "acquired" so that it can not be freed by the render pipeline. + * + * Drawing is possible if the engine has the `draw()` callback and it is in its `render()` + * callback. */ +bool RE_engine_draw_acquire(struct Render *re); +void RE_engine_draw_release(struct Render *re); + +/* NOTE: Only used for Cycles's BLenderGPUDisplay integration with the draw manager. A subject + * for re-consideration. Do not use this functionality. */ +bool RE_engine_has_render_context(struct RenderEngine *engine); +void RE_engine_render_context_enable(struct RenderEngine *engine); +void RE_engine_render_context_disable(struct RenderEngine *engine); + /* Engine Types */ void RE_engines_init(void); @@ -252,6 +286,10 @@ void RE_bake_engine_set_engine_parameters(struct Render *re, void RE_engine_free_blender_memory(struct RenderEngine *engine); +void RE_engine_tile_highlight_set( + struct RenderEngine *engine, int x, int y, int width, int height, bool highlight); +void RE_engine_tile_highlight_clear_all(struct RenderEngine *engine); + #ifdef __cplusplus } #endif diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index cd839385bfb..3237772dd80 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -141,9 +141,6 @@ typedef struct RenderResult { volatile rcti renrect; volatile RenderLayer *renlay; - /* optional saved endresult on disk */ - int do_exr_tile; - /* for render results in Image, verify validity for sequences */ int framenr; diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 76839651b5d..0f893ce8cd5 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -774,18 +774,6 @@ void RE_bake_pixels_populate(Mesh *me, /* ******************** NORMALS ************************ */ -/** - * convert a normalized normal to the -1.0 1.0 range - * the input is expected to be POS_X, POS_Y, POS_Z - */ -static void normal_uncompress(float out[3], const float in[3]) -{ - int i; - for (i = 0; i < 3; i++) { - out[i] = 2.0f * in[i] - 1.0f; - } -} - static void normal_compress(float out[3], const float in[3], const eBakeNormalSwizzle normal_swizzle[3]) @@ -934,7 +922,7 @@ void RE_bake_normal_world_to_tangent(const BakePixel pixel_array[], copy_v3_v3(tsm[2], normal); /* texture values */ - normal_uncompress(nor, &result[offset]); + copy_v3_v3(nor, &result[offset]); /* converts from world space to local space */ mul_transposed_mat3_m4_v3(mat, nor); @@ -976,7 +964,7 @@ void RE_bake_normal_world_to_object(const BakePixel pixel_array[], } offset = i * depth; - normal_uncompress(nor, &result[offset]); + copy_v3_v3(nor, &result[offset]); /* rotates only without translation */ mul_mat3_m4_v3(iobmat, nor); @@ -1004,7 +992,7 @@ void RE_bake_normal_world_to_world(const BakePixel pixel_array[], } offset = i * depth; - normal_uncompress(nor, &result[offset]); + copy_v3_v3(nor, &result[offset]); /* save back the values */ normal_compress(&result[offset], nor, normal_swizzle); @@ -1053,6 +1041,7 @@ int RE_pass_depth(const eScenePassType pass_type) } case SCE_PASS_COMBINED: case SCE_PASS_SHADOW: + case SCE_PASS_POSITION: case SCE_PASS_NORMAL: case SCE_PASS_VECTOR: case SCE_PASS_INDEXOB: /* XXX double check */ diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 5728b784714..389b821ca35 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -62,7 +62,6 @@ #include "DRW_engine.h" -#include "initrender.h" #include "pipeline.h" #include "render_result.h" #include "render_types.h" @@ -283,14 +282,6 @@ static void render_result_to_bake(RenderEngine *engine, RenderResult *rr) /* Render Results */ -static RenderPart *get_part_from_result(Render *re, RenderResult *result) -{ - rcti key = result->tilerect; - BLI_rcti_translate(&key, re->disprect.xmin, re->disprect.ymin); - - return BLI_ghash_lookup(re->parts, &key); -} - static HighlightedTile highlighted_tile_from_result_get(Render *re, RenderResult *result) { HighlightedTile tile; @@ -300,6 +291,37 @@ static HighlightedTile highlighted_tile_from_result_get(Render *re, RenderResult return tile; } +static void engine_tile_highlight_set(RenderEngine *engine, + const HighlightedTile *tile, + bool highlight) +{ + if ((engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) { + return; + } + + Render *re = engine->re; + + BLI_mutex_lock(&re->highlighted_tiles_mutex); + + if (re->highlighted_tiles == NULL) { + re->highlighted_tiles = BLI_gset_new( + BLI_ghashutil_inthash_v4_p, BLI_ghashutil_inthash_v4_cmp, "highlighted tiles"); + } + + if (highlight) { + HighlightedTile **tile_in_set; + if (!BLI_gset_ensure_p_ex(re->highlighted_tiles, tile, (void ***)&tile_in_set)) { + *tile_in_set = MEM_mallocN(sizeof(HighlightedTile), __func__); + **tile_in_set = *tile; + } + } + else { + BLI_gset_remove(re->highlighted_tiles, tile, MEM_freeN); + } + + BLI_mutex_unlock(&re->highlighted_tiles_mutex); +} + RenderResult *RE_engine_begin_result( RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) { @@ -332,7 +354,7 @@ RenderResult *RE_engine_begin_result( disprect.ymin = y; disprect.ymax = y + h; - result = render_result_new(re, &disprect, RR_USE_MEM, layername, viewname); + result = render_result_new(re, &disprect, layername, viewname); /* TODO: make this thread safe. */ @@ -341,25 +363,12 @@ RenderResult *RE_engine_begin_result( render_result_clone_passes(re, result, viewname); render_result_passes_allocated_ensure(result); - RenderPart *pa; - - /* Copy EXR tile settings, so pipeline knows whether this is a result - * for Save Buffers enabled rendering. - */ - result->do_exr_tile = re->result->do_exr_tile; - BLI_addtail(&engine->fullresult, result); result->tilerect.xmin += re->disprect.xmin; result->tilerect.xmax += re->disprect.xmin; result->tilerect.ymin += re->disprect.ymin; result->tilerect.ymax += re->disprect.ymin; - - pa = get_part_from_result(re, result); - - if (pa) { - pa->status = PART_STATUS_IN_PROGRESS; - } } return result; @@ -426,53 +435,14 @@ void RE_engine_end_result( re_ensure_passes_allocated_thread_safe(re); - /* merge. on break, don't merge in result for preview renders, looks nicer */ - if (!highlight) { - /* for exr tile render, detect tiles that are done */ - RenderPart *pa = get_part_from_result(re, result); - - if (pa) { - pa->status = (!cancel && merge_results) ? PART_STATUS_MERGED : PART_STATUS_RENDERED; - } - else if (re->result->do_exr_tile) { - /* If written result does not match any tile and we are using save - * buffers, we are going to get OpenEXR save errors. */ - fprintf(stderr, "RenderEngine.end_result: dimensions do not match any OpenEXR tile.\n"); - } - } - if (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES)) { - BLI_mutex_lock(&re->highlighted_tiles_mutex); + const HighlightedTile tile = highlighted_tile_from_result_get(re, result); - if (re->highlighted_tiles == NULL) { - re->highlighted_tiles = BLI_gset_new( - BLI_ghashutil_inthash_v4_p, BLI_ghashutil_inthash_v4_cmp, "highlighted tiles"); - } - - HighlightedTile tile = highlighted_tile_from_result_get(re, result); - if (highlight) { - void **tile_in_set; - if (!BLI_gset_ensure_p_ex(re->highlighted_tiles, &tile, &tile_in_set)) { - *tile_in_set = MEM_mallocN(sizeof(HighlightedTile), __func__); - memcpy(*tile_in_set, &tile, sizeof(tile)); - } - BLI_gset_add(re->highlighted_tiles, &tile); - } - else { - BLI_gset_remove(re->highlighted_tiles, &tile, MEM_freeN); - } - - BLI_mutex_unlock(&re->highlighted_tiles_mutex); + engine_tile_highlight_set(engine, &tile, highlight); } if (!cancel || merge_results) { - if (re->result->do_exr_tile) { - if (!cancel && merge_results) { - render_result_exr_file_merge(re->result, result, re->viewname); - render_result_merge(re->result, result); - } - } - else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) { + if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) { render_result_merge(re->result, result); } @@ -582,6 +552,27 @@ void RE_engine_set_error_message(RenderEngine *engine, const char *msg) } } +RenderPass *RE_engine_pass_by_index_get(RenderEngine *engine, const char *layer_name, int index) +{ + Render *re = engine->re; + if (re == NULL) { + return NULL; + } + + RenderPass *pass = NULL; + + RenderResult *rr = RE_AcquireResultRead(re); + if (rr != NULL) { + const RenderLayer *layer = RE_GetRenderLayer(rr, layer_name); + if (layer != NULL) { + pass = BLI_findlink(&layer->passes, index); + } + } + RE_ReleaseResult(re); + + return pass; +} + const char *RE_engine_active_view_get(RenderEngine *engine) { Render *re = engine->re; @@ -837,12 +828,6 @@ bool RE_bake_engine(Render *re, engine->resolution_x = re->winx; engine->resolution_y = re->winy; - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - RE_parts_init(re); - engine->tile_x = re->r.tilex; - engine->tile_y = re->r.tiley; - BLI_rw_mutex_unlock(&re->partsmutex); - if (type->bake) { engine->depsgraph = depsgraph; @@ -870,21 +855,13 @@ bool RE_bake_engine(Render *re, engine->depsgraph = NULL; } - engine->tile_x = 0; - engine->tile_y = 0; engine->flag &= ~RE_ENGINE_RENDERING; - /* Free depsgraph outside of parts mutex lock, since this locks OpenGL context - * while the UI drawing might also lock the OpenGL context and parts mutex. */ engine_depsgraph_free(engine); - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); RE_engine_free(engine); re->engine = NULL; - RE_parts_free(re); - BLI_rw_mutex_unlock(&re->partsmutex); - if (BKE_reports_contain(re->reports, RPT_ERROR)) { G.is_break = true; } @@ -928,15 +905,23 @@ static void engine_render_view_layer(Render *re, DRW_render_context_enable(engine->re); } + BLI_mutex_lock(&engine->re->engine_draw_mutex); + re->engine->flag |= RE_ENGINE_CAN_DRAW; + BLI_mutex_unlock(&engine->re->engine_draw_mutex); + engine->type->render(engine, engine->depsgraph); + BLI_mutex_lock(&engine->re->engine_draw_mutex); + re->engine->flag &= ~RE_ENGINE_CAN_DRAW; + BLI_mutex_unlock(&engine->re->engine_draw_mutex); + if (use_gpu_context) { DRW_render_context_disable(engine->re); } } /* Optionally composite grease pencil over render result. */ - if (engine->has_grease_pencil && use_grease_pencil && !re->result->do_exr_tile) { + if (engine->has_grease_pencil && use_grease_pencil) { /* NOTE: External engine might have been requested to free its * dependency graph, which is only allowed if there is no grease * pencil (pipeline is taking care of that). */ @@ -981,16 +966,11 @@ bool RE_engine_render(Render *re, bool do_all) /* create render result */ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); if (re->result == NULL || !(re->r.scemode & R_BUTS_PREVIEW)) { - int savebuffers = RR_USE_MEM; - if (re->result) { render_result_free(re->result); } - if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) { - savebuffers = RR_USE_EXR; - } - re->result = render_result_new(re, &re->disprect, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); } BLI_rw_mutex_unlock(&re->resultmutex); @@ -1035,32 +1015,15 @@ bool RE_engine_render(Render *re, bool do_all) engine->resolution_x = re->winx; engine->resolution_y = re->winy; - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - RE_parts_init(re); - engine->tile_x = re->partx; - engine->tile_y = re->party; - BLI_rw_mutex_unlock(&re->partsmutex); - - if (re->result->do_exr_tile) { - render_result_exr_file_begin(re, engine); - } - /* Clear UI drawing locks. */ if (re->draw_lock) { re->draw_lock(re->dlh, false); } - /* Render view layers. */ - bool delay_grease_pencil = false; - if (type->render) { FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer_iter) { engine_render_view_layer(re, engine, view_layer_iter, true, true); - /* With save buffers there is no render buffer in memory for compositing, delay - * grease pencil in that case. */ - delay_grease_pencil = engine->has_grease_pencil && re->result->do_exr_tile; - if (RE_engine_test_break(engine)) { break; } @@ -1068,42 +1031,18 @@ bool RE_engine_render(Render *re, bool do_all) FOREACH_VIEW_LAYER_TO_RENDER_END; } + if (type->render_frame_finish) { + type->render_frame_finish(engine); + } + /* Clear tile data */ - engine->tile_x = 0; - engine->tile_y = 0; engine->flag &= ~RE_ENGINE_RENDERING; render_result_free_list(&engine->fullresult, engine->fullresult.first); - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - - /* For save buffers, read back from disk. */ - if (re->result->do_exr_tile) { - render_result_exr_file_end(re, engine); - } - - /* Perform delayed grease pencil rendering. */ - if (delay_grease_pencil) { - BLI_rw_mutex_unlock(&re->partsmutex); - - FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer_iter) { - engine_render_view_layer(re, engine, view_layer_iter, false, true); - if (RE_engine_test_break(engine)) { - break; - } - } - FOREACH_VIEW_LAYER_TO_RENDER_END; - - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - } - /* re->engine becomes zero if user changed active render engine during render */ if (!engine_keep_depsgraph(engine) || !re->engine) { - /* Free depsgraph outside of parts mutex lock, since this locks OpenGL context - * while the UI drawing might also lock the OpenGL context and parts mutex. */ - BLI_rw_mutex_unlock(&re->partsmutex); engine_depsgraph_free(engine); - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); RE_engine_free(engine); re->engine = NULL; @@ -1115,9 +1054,6 @@ bool RE_engine_render(Render *re, bool do_all) BLI_rw_mutex_unlock(&re->resultmutex); } - RE_parts_free(re); - BLI_rw_mutex_unlock(&re->partsmutex); - if (BKE_reports_contain(re->reports, RPT_ERROR)) { G.is_break = true; } @@ -1179,3 +1115,81 @@ void RE_engine_free_blender_memory(RenderEngine *engine) } engine_depsgraph_free(engine); } + +struct RenderEngine *RE_engine_get(const Render *re) +{ + return re->engine; +} + +bool RE_engine_draw_acquire(Render *re) +{ + BLI_mutex_lock(&re->engine_draw_mutex); + + RenderEngine *engine = re->engine; + + if (engine == NULL || engine->type->draw == NULL || (engine->flag & RE_ENGINE_CAN_DRAW) == 0) { + BLI_mutex_unlock(&re->engine_draw_mutex); + return false; + } + + return true; +} + +void RE_engine_draw_release(Render *re) +{ + BLI_mutex_unlock(&re->engine_draw_mutex); +} + +void RE_engine_tile_highlight_set( + RenderEngine *engine, int x, int y, int width, int height, bool highlight) +{ + HighlightedTile tile; + BLI_rcti_init(&tile.rect, x, x + width, y, y + height); + + engine_tile_highlight_set(engine, &tile, highlight); +} + +void RE_engine_tile_highlight_clear_all(RenderEngine *engine) +{ + if ((engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) { + return; + } + + Render *re = engine->re; + + BLI_mutex_lock(&re->highlighted_tiles_mutex); + + if (re->highlighted_tiles != NULL) { + BLI_gset_clear(re->highlighted_tiles, MEM_freeN); + } + + BLI_mutex_unlock(&re->highlighted_tiles_mutex); +} + +/* -------------------------------------------------------------------- */ +/** \name OpenGL context manipulation. + * + * NOTE: Only used for Cycles's BLenderGPUDisplay integration with the draw manager. A subject + * for re-consideration. Do not use this functionality. + * \{ */ + +bool RE_engine_has_render_context(RenderEngine *engine) +{ + if (engine->re == NULL) { + return false; + } + + return RE_gl_context_get(engine->re) != NULL; +} + +void RE_engine_render_context_enable(RenderEngine *engine) +{ + DRW_render_context_enable(engine->re); +} + +void RE_engine_render_context_disable(RenderEngine *engine) +{ + DRW_render_context_disable(engine->re); +} + +/** \} */ diff --git a/source/blender/render/intern/initrender.c b/source/blender/render/intern/initrender.c index 3148625c866..2370d8e893b 100644 --- a/source/blender/render/intern/initrender.c +++ b/source/blender/render/intern/initrender.c @@ -43,9 +43,6 @@ #include "pipeline.h" #include "render_types.h" -/* Own includes */ -#include "initrender.h" - /* ****************** MASKS and LUTS **************** */ static float filt_quadratic(float x) @@ -244,91 +241,3 @@ void RE_GetViewPlane(Render *re, rctf *r_viewplane, rcti *r_disprect) BLI_rcti_init(r_disprect, 0, 0, 0, 0); } } - -/* ~~~~~~~~~~~~~~~~ part (tile) calculus ~~~~~~~~~~~~~~~~~~~~~~ */ - -void RE_parts_free(Render *re) -{ - if (re->parts) { - BLI_ghash_free(re->parts, NULL, MEM_freeN); - re->parts = NULL; - } -} - -void RE_parts_clamp(Render *re) -{ - /* part size */ - re->partx = max_ii(1, min_ii(re->r.tilex, re->rectx)); - re->party = max_ii(1, min_ii(re->r.tiley, re->recty)); -} - -void RE_parts_init(Render *re) -{ - int nr, xd, yd, partx, party, xparts, yparts; - int xminb, xmaxb, yminb, ymaxb; - - RE_parts_free(re); - - re->parts = BLI_ghash_new( - BLI_ghashutil_inthash_v4_p, BLI_ghashutil_inthash_v4_cmp, "render parts"); - - /* Just for readable code. */ - xminb = re->disprect.xmin; - yminb = re->disprect.ymin; - xmaxb = re->disprect.xmax; - ymaxb = re->disprect.ymax; - - RE_parts_clamp(re); - - partx = re->partx; - party = re->party; - /* part count */ - xparts = (re->rectx + partx - 1) / partx; - yparts = (re->recty + party - 1) / party; - - for (nr = 0; nr < xparts * yparts; nr++) { - rcti disprect; - int rectx, recty; - - xd = (nr % xparts); - yd = (nr - xd) / xparts; - - disprect.xmin = xminb + xd * partx; - disprect.ymin = yminb + yd * party; - - /* ensure we cover the entire picture, so last parts go to end */ - if (xd < xparts - 1) { - disprect.xmax = disprect.xmin + partx; - if (disprect.xmax > xmaxb) { - disprect.xmax = xmaxb; - } - } - else { - disprect.xmax = xmaxb; - } - - if (yd < yparts - 1) { - disprect.ymax = disprect.ymin + party; - if (disprect.ymax > ymaxb) { - disprect.ymax = ymaxb; - } - } - else { - disprect.ymax = ymaxb; - } - - rectx = BLI_rcti_size_x(&disprect); - recty = BLI_rcti_size_y(&disprect); - - /* so, now can we add this part? */ - if (rectx > 0 && recty > 0) { - RenderPart *pa = MEM_callocN(sizeof(RenderPart), "new part"); - - pa->disprect = disprect; - pa->rectx = rectx; - pa->recty = recty; - - BLI_ghash_insert(re->parts, &pa->disprect, pa); - } - } -} diff --git a/source/blender/render/intern/initrender.h b/source/blender/render/intern/initrender.h deleted file mode 100644 index f5ac352752f..00000000000 --- a/source/blender/render/intern/initrender.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup render - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -/* Functions */ - -void RE_parts_init(Render *re); -void RE_parts_free(Render *re); -void RE_parts_clamp(Render *re); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 5418f4035b1..72ff920561d 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -102,7 +102,6 @@ #include "DEG_depsgraph.h" /* internal */ -#include "initrender.h" #include "pipeline.h" #include "render_result.h" #include "render_types.h" @@ -568,7 +567,7 @@ Render *RE_NewRender(const char *name) BLI_addtail(&RenderGlobal.renderlist, re); BLI_strncpy(re->name, name, RE_MAXNAME); BLI_rw_mutex_init(&re->resultmutex); - BLI_rw_mutex_init(&re->partsmutex); + BLI_mutex_init(&re->engine_draw_mutex); BLI_mutex_init(&re->highlighted_tiles_mutex); } @@ -633,7 +632,7 @@ void RE_FreeRender(Render *re) } BLI_rw_mutex_end(&re->resultmutex); - BLI_rw_mutex_end(&re->partsmutex); + BLI_mutex_end(&re->engine_draw_mutex); BLI_mutex_end(&re->highlighted_tiles_mutex); BLI_freelistN(&re->view_layers); @@ -722,26 +721,6 @@ void RE_FreePersistentData(const Scene *scene) /* ********* initialize state ******** */ -/* clear full sample and tile flags if needed */ -static int check_mode_full_sample(RenderData *rd) -{ - int scemode = rd->scemode; - - /* not supported by any current renderer */ - scemode &= ~R_FULL_SAMPLE; - -#ifdef WITH_OPENEXR - if (scemode & R_FULL_SAMPLE) { - scemode |= R_EXR_TILE_FILE; /* enable automatic */ - } -#else - /* can't do this without openexr support */ - scemode &= ~(R_EXR_TILE_FILE | R_FULL_SAMPLE); -#endif - - return scemode; -} - static void re_init_resolution(Render *re, Render *source, int winx, int winy, rcti *disprect) { re->winx = winx; @@ -839,8 +818,6 @@ void RE_InitState(Render *re, return; } - re->r.scemode = check_mode_full_sample(&re->r); - if (single_layer) { int index = BLI_findindex(render_layers, single_layer); if (index != -1) { @@ -890,9 +867,6 @@ void RE_InitState(Render *re, render_result_view_new(re->result, ""); } - /* ensure renderdatabase can use part settings correct */ - RE_parts_clamp(re); - BLI_rw_mutex_unlock(&re->resultmutex); RE_init_threadcount(re); @@ -1040,7 +1014,7 @@ static void render_result_uncrop(Render *re) /* weak is: it chances disprect from border */ render_result_disprect_to_full_resolution(re); - rres = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + rres = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); render_result_passes_allocated_ensure(rres); rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data); @@ -1227,7 +1201,7 @@ static void do_render_compositor(Render *re) if ((re->r.mode & R_CROP) == 0) { render_result_disprect_to_full_resolution(re); } - re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); BLI_rw_mutex_unlock(&re->resultmutex); @@ -1647,7 +1621,7 @@ bool RE_is_rendering_allowed(Scene *scene, Object *camera_override, ReportList *reports) { - int scemode = check_mode_full_sample(&scene->r); + const int scemode = scene->r.scemode; if (scene->r.mode & R_BORDER) { if (scene->r.border.xmax <= scene->r.border.xmin || @@ -1657,17 +1631,6 @@ bool RE_is_rendering_allowed(Scene *scene, } } - if (scemode & (R_EXR_TILE_FILE | R_FULL_SAMPLE)) { - char str[FILE_MAX]; - - render_result_exr_file_path(scene, "", 0, str); - - if (!BLI_file_is_writable(str)) { - BKE_report(reports, RPT_ERROR, "Cannot save render buffers, check the temp default path"); - return 0; - } - } - if (RE_seq_render_active(scene, &scene->r)) { /* Sequencer */ if (scene->r.mode & R_BORDER) { @@ -1686,13 +1649,6 @@ bool RE_is_rendering_allowed(Scene *scene, BKE_report(reports, RPT_ERROR, "No render output node in scene"); return 0; } - - if (scemode & R_FULL_SAMPLE) { - if (compositor_needs_render(scene, 0) == 0) { - BKE_report(reports, RPT_ERROR, "Full sample AA not supported without 3D rendering"); - return 0; - } - } } else { /* Regular Render */ @@ -1710,14 +1666,6 @@ bool RE_is_rendering_allowed(Scene *scene, return 1; } -static void validate_render_settings(Render *re) -{ - if (RE_engine_is_external(re)) { - /* not supported yet */ - re->r.scemode &= ~R_FULL_SAMPLE; - } -} - static void update_physics_cache(Render *re, Scene *scene, ViewLayer *view_layer, @@ -1820,8 +1768,6 @@ static int render_init_from_main(Render *re, /* initstate makes new result, have to send changed tags around */ ntreeCompositTagRender(re->scene); - validate_render_settings(re); - re->display_init(re->dih, re->result); re->display_clear(re->dch, re->result); diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index 6cb6aabe885..c308147fc5b 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -260,8 +260,10 @@ RenderPass *render_layer_add_pass(RenderResult *rr, /* will read info from Render *re to define layers */ /* called in threads */ /* re->winx,winy is coordinate space of entire image, partrct the part within */ -RenderResult *render_result_new( - Render *re, rcti *partrct, int savebuffers, const char *layername, const char *viewname) +RenderResult *render_result_new(Render *re, + rcti *partrct, + const char *layername, + const char *viewname) { RenderResult *rr; RenderLayer *rl; @@ -287,10 +289,6 @@ RenderResult *render_result_new( rr->tilerect.ymin = partrct->ymin - re->disprect.ymin; rr->tilerect.ymax = partrct->ymax - re->disprect.ymin; - if (savebuffers) { - rr->do_exr_tile = true; - } - rr->passes_allocated = false; render_result_views_new(rr, &re->r); @@ -314,10 +312,6 @@ RenderResult *render_result_new( rl->rectx = rectx; rl->recty = recty; - if (rr->do_exr_tile) { - rl->exrhandle = IMB_exr_get_handle(); - } - for (rv = rr->views.first; rv; rv = rv->next) { const char *view = rv->name; @@ -327,10 +321,6 @@ RenderResult *render_result_new( } } - if (rr->do_exr_tile) { - IMB_exr_add_view(rl->exrhandle, view); - } - #define RENDER_LAYER_ADD_PASS_SAFE(rr, rl, channels, name, viewname, chan_id) \ do { \ if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id) == NULL) { \ @@ -351,6 +341,9 @@ RenderResult *render_result_new( if (view_layer->passflag & SCE_PASS_NORMAL) { RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_NORMAL, view, "XYZ"); } + if (view_layer->passflag & SCE_PASS_POSITION) { + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_POSITION, view, "XYZ"); + } if (view_layer->passflag & SCE_PASS_UV) { RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_UV, view, "UVA"); } @@ -424,11 +417,6 @@ RenderResult *render_result_new( rl->rectx = rectx; rl->recty = recty; - /* duplicate code... */ - if (rr->do_exr_tile) { - rl->exrhandle = IMB_exr_get_handle(); - } - for (rv = rr->views.first; rv; rv = rv->next) { const char *view = rv->name; @@ -438,10 +426,6 @@ RenderResult *render_result_new( } } - if (rr->do_exr_tile) { - IMB_exr_add_view(rl->exrhandle, view); - } - /* a renderlayer should always have a Combined pass */ render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, view, "RGBA"); } @@ -1089,227 +1073,6 @@ void render_result_single_layer_end(Render *re) re->pushedresult = NULL; } -/************************* EXR Tile File Rendering ***************************/ - -static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, const char *viewname) -{ - RenderLayer *rlp, *rl; - RenderPass *rpassp; - int partx, party; - - BLI_thread_lock(LOCK_IMAGE); - - for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) { - rl = RE_GetRenderLayer(rr, rlp->name); - - /* should never happen but prevents crash if it does */ - BLI_assert(rl); - if (UNLIKELY(rl == NULL)) { - continue; - } - - /* passes are allocated in sync */ - for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) { - const int xstride = rpassp->channels; - int a; - char fullname[EXR_PASS_MAXNAME]; - - for (a = 0; a < xstride; a++) { - set_pass_full_name(fullname, rpassp->name, a, viewname, rpassp->chan_id); - - IMB_exr_set_channel(rl->exrhandle, - rlp->name, - fullname, - xstride, - xstride * rrpart->rectx, - rpassp->rect + a); - } - } - } - - party = rrpart->tilerect.ymin; - partx = rrpart->tilerect.xmin; - - for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) { - rl = RE_GetRenderLayer(rr, rlp->name); - - /* should never happen but prevents crash if it does */ - BLI_assert(rl); - if (UNLIKELY(rl == NULL)) { - continue; - } - - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, viewname, false); - } - - BLI_thread_unlock(LOCK_IMAGE); -} - -void render_result_save_empty_result_tiles(Render *re) -{ - RenderResult *rr; - RenderLayer *rl; - - for (rr = re->result; rr; rr = rr->next) { - for (rl = rr->layers.first; rl; rl = rl->next) { - GHashIterator pa_iter; - GHASH_ITER (pa_iter, re->parts) { - RenderPart *pa = BLI_ghashIterator_getValue(&pa_iter); - if (pa->status != PART_STATUS_MERGED) { - int party = pa->disprect.ymin - re->disprect.ymin; - int partx = pa->disprect.xmin - re->disprect.xmin; - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, re->viewname, true); - } - } - } - } -} - -/* Compute list of passes needed by render engine. */ -static void templates_register_pass_cb(void *userdata, - Scene *UNUSED(scene), - ViewLayer *UNUSED(view_layer), - const char *name, - int channels, - const char *chan_id, - eNodeSocketDatatype UNUSED(type)) -{ - ListBase *templates = userdata; - RenderPass *pass = MEM_callocN(sizeof(RenderPass), "RenderPassTemplate"); - - pass->channels = channels; - BLI_strncpy(pass->name, name, sizeof(pass->name)); - BLI_strncpy(pass->chan_id, chan_id, sizeof(pass->chan_id)); - - BLI_addtail(templates, pass); -} - -static void render_result_get_pass_templates(RenderEngine *engine, - Render *re, - RenderLayer *rl, - ListBase *templates) -{ - BLI_listbase_clear(templates); - - if (engine && engine->type->update_render_passes) { - ViewLayer *view_layer = BLI_findstring(&re->view_layers, rl->name, offsetof(ViewLayer, name)); - if (view_layer) { - RE_engine_update_render_passes( - engine, re->scene, view_layer, templates_register_pass_cb, templates); - } - } -} - -/* begin write of exr tile file */ -void render_result_exr_file_begin(Render *re, RenderEngine *engine) -{ - char str[FILE_MAX]; - - for (RenderResult *rr = re->result; rr; rr = rr->next) { - LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) { - /* Get passes needed by engine. Normally we would wait for the - * engine to create them, but for EXR file we need to know in - * advance. */ - ListBase templates; - render_result_get_pass_templates(engine, re, rl, &templates); - - /* Create render passes requested by engine. Only this part is - * mutex locked to avoid deadlock with Python GIL. */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - LISTBASE_FOREACH (RenderPass *, pass, &templates) { - RE_create_render_pass( - re->result, pass->name, pass->channels, pass->chan_id, rl->name, NULL); - } - BLI_rw_mutex_unlock(&re->resultmutex); - - BLI_freelistN(&templates); - - /* Open EXR file for writing. */ - render_result_exr_file_path(re->scene, rl->name, rr->sample_nr, str); - printf("write exr tmp file, %dx%d, %s\n", rr->rectx, rr->recty, str); - IMB_exrtile_begin_write(rl->exrhandle, str, 0, rr->rectx, rr->recty, re->partx, re->party); - } - } -} - -/* end write of exr tile file, read back first sample */ -void render_result_exr_file_end(Render *re, RenderEngine *engine) -{ - /* Preserve stamp data. */ - struct StampData *stamp_data = re->result->stamp_data; - re->result->stamp_data = NULL; - - /* Close EXR files. */ - for (RenderResult *rr = re->result; rr; rr = rr->next) { - LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) { - IMB_exr_close(rl->exrhandle); - rl->exrhandle = NULL; - } - - rr->do_exr_tile = false; - } - - /* Create new render result in memory instead of on disk. */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_free_list(&re->fullresult, re->result); - re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); - re->result->stamp_data = stamp_data; - render_result_passes_allocated_ensure(re->result); - BLI_rw_mutex_unlock(&re->resultmutex); - - LISTBASE_FOREACH (RenderLayer *, rl, &re->result->layers) { - /* Get passes needed by engine. */ - ListBase templates; - render_result_get_pass_templates(engine, re, rl, &templates); - - /* Create render passes requested by engine. Only this part is - * mutex locked to avoid deadlock with Python GIL. */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - LISTBASE_FOREACH (RenderPass *, pass, &templates) { - RE_create_render_pass(re->result, pass->name, pass->channels, pass->chan_id, rl->name, NULL); - } - - BLI_freelistN(&templates); - - /* Render passes contents from file. */ - char str[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; - render_result_exr_file_path(re->scene, rl->name, 0, str); - printf("read exr tmp file: %s\n", str); - - if (!render_result_exr_file_read_path(re->result, rl, str)) { - printf("cannot read: %s\n", str); - } - BLI_rw_mutex_unlock(&re->resultmutex); - } -} - -/* save part into exr file */ -void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart, const char *viewname) -{ - for (; rr && rrpart; rr = rr->next, rrpart = rrpart->next) { - save_render_result_tile(rr, rrpart, viewname); - } -} - -/* path to temporary exr file */ -void render_result_exr_file_path(Scene *scene, const char *layname, int sample, char *filepath) -{ - char name[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100]; - const char *fi = BLI_path_basename(BKE_main_blendfile_path_from_global()); - - if (sample == 0) { - BLI_snprintf(name, sizeof(name), "%s_%s_%s.exr", fi, scene->id.name + 2, layname); - } - else { - BLI_snprintf(name, sizeof(name), "%s_%s_%s%d.exr", fi, scene->id.name + 2, layname, sample); - } - - /* Make name safe for paths, see T43275. */ - BLI_filename_make_safe(name); - - BLI_join_dirfile(filepath, FILE_MAX, BKE_tempdir_session(), name); -} - /* called for reading temp files, and for external engines */ int render_result_exr_file_read_path(RenderResult *rr, RenderLayer *rl_single, @@ -1416,7 +1179,7 @@ bool render_result_exr_file_cache_read(Render *re) char *root = U.render_cachedir; RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); /* First try cache. */ render_result_exr_file_cache_path(re->scene, root, str); diff --git a/source/blender/render/intern/render_result.h b/source/blender/render/intern/render_result.h index 1fc64a4ea97..4145bb3b8ab 100644 --- a/source/blender/render/intern/render_result.h +++ b/source/blender/render/intern/render_result.h @@ -25,9 +25,6 @@ #define PASS_VECTOR_MAX 10000.0f -#define RR_USE_MEM 0 -#define RR_USE_EXR 1 - #define RR_ALL_LAYERS NULL #define RR_ALL_VIEWS NULL @@ -51,7 +48,6 @@ extern "C" { struct RenderResult *render_result_new(struct Render *re, struct rcti *partrct, - int savebuffers, const char *layername, const char *viewname); @@ -81,12 +77,6 @@ void render_result_free_list(struct ListBase *lb, struct RenderResult *rr); void render_result_single_layer_begin(struct Render *re); void render_result_single_layer_end(struct Render *re); -/* EXR Tile File Render */ - -void render_result_save_empty_result_tiles(struct Render *re); -void render_result_exr_file_begin(struct Render *re, struct RenderEngine *engine); -void render_result_exr_file_end(struct Render *re, struct RenderEngine *engine); - /* render pass wrapper for gpencil */ struct RenderPass *render_layer_add_pass(struct RenderResult *rr, struct RenderLayer *rl, @@ -95,14 +85,6 @@ struct RenderPass *render_layer_add_pass(struct RenderResult *rr, const char *viewname, const char *chan_id); -void render_result_exr_file_merge(struct RenderResult *rr, - struct RenderResult *rrpart, - const char *viewname); - -void render_result_exr_file_path(struct Scene *scene, - const char *layname, - int sample, - char *filepath); int render_result_exr_file_read_path(struct RenderResult *rr, struct RenderLayer *rl_single, const char *filepath); diff --git a/source/blender/render/intern/render_types.h b/source/blender/render/intern/render_types.h index d2d2b499495..ca4f72350e1 100644 --- a/source/blender/render/intern/render_types.h +++ b/source/blender/render/intern/render_types.h @@ -47,30 +47,10 @@ struct ReportList; extern "C" { #endif -/* this is handed over to threaded hiding/passes/shading engine */ -typedef struct RenderPart { - struct RenderPart *next, *prev; - - RenderResult *result; /* result of part rendering */ - ListBase fullresult; /* optional full sample buffers */ - - rcti disprect; /* part coordinates within total picture */ - int rectx, recty; /* the size */ - int nr; /* nr is partnr */ - short status; -} RenderPart; - typedef struct HighlightedTile { rcti rect; } HighlightedTile; -enum { - /* PART_STATUS_NONE = 0, */ /* UNUSED */ - PART_STATUS_IN_PROGRESS = 1, - PART_STATUS_RENDERED = 2, - PART_STATUS_MERGED = 3, -}; - /* controls state of render, everything that's read-only during render stage */ struct Render { struct Render *next, *prev; @@ -91,6 +71,9 @@ struct Render { * to not conflict with writes, so no lock used for that */ ThreadRWMutex resultmutex; + /* Guard for drawing render result using engine's `draw()` callback. */ + ThreadMutex engine_draw_mutex; + /** Window size, display rect, viewplane. * \note Buffer width and height with percentage applied * without border & crop. convert to long before multiplying together to avoid overflow. */ @@ -101,10 +84,6 @@ struct Render { /* final picture width and height (within disprect) */ int rectx, recty; - /* real maximum size of parts after correction for minimum - * partx*xparts can be larger than rectx, in that case last part is smaller */ - int partx, party; - /* Camera transform, only used by Freestyle. */ float winmat[4][4]; @@ -120,9 +99,6 @@ struct Render { int active_view_layer; struct Object *camera_override; - ThreadRWMutex partsmutex; - struct GHash *parts; - ThreadMutex highlighted_tiles_mutex; struct GSet *highlighted_tiles; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 887aed7ffc7..8baf4a0e013 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -2426,10 +2426,15 @@ void wm_window_IME_end(wmWindow *win) void *WM_opengl_context_create(void) { - /* On Windows there is a problem creating contexts that share lists - * from one context that is current in another thread. - * So we should call this function only on the main thread. - */ + /* On Windows there is a problem creating contexts that share resources (almost any object, + * including legacy display lists, but also textures) with a context which is current in another + * thread. This is a documented and behavior of both `::wglCreateContextAttribsARB()` and + * `::wglShareLists()`. + * + * Other platforms might successfully share resources from context which is active somewhere + * else, but to keep our code behave the same on all platform we expect contexts to only be + * created from the main thread. */ + BLI_assert(BLI_thread_is_main()); BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); diff --git a/tests/performance/tests/cycles.py b/tests/performance/tests/cycles.py index bac6b8a7ceb..e702fa445d2 100644 --- a/tests/performance/tests/cycles.py +++ b/tests/performance/tests/cycles.py @@ -17,6 +17,16 @@ def _run(args): scene.render.image_settings.file_format = 'PNG' scene.cycles.device = 'CPU' if device_type == 'CPU' else 'GPU' + if scene.cycles.use_adaptive_sampling: + # Render samples specified in file, no other way to measure + # adaptive sampling performance reliably. + scene.cycles.time_limit = 0.0 + else: + # Render for fixed amount of time so it's adaptive to the + # machine and devices. + scene.cycles.samples = 16384 + scene.cycles.time_limit = 10.0 + if scene.cycles.device == 'GPU': # Enable specified GPU in preferences. prefs = bpy.context.preferences @@ -62,12 +72,14 @@ class CyclesTest(api.Test): 'device_index': device_index, 'render_filepath': str(env.log_file.parent / (env.log_file.stem + '.png'))} - _, lines = env.run_in_blender(_run, args, ['--debug-cycles', '--verbose', '1', self.filepath]) + _, lines = env.run_in_blender(_run, args, ['--debug-cycles', '--verbose', '2', self.filepath]) # Parse render time from output prefix_time = "Render time (without synchronization): " prefix_memory = "Peak: " + prefix_time_per_sample = "Average time per sample: " time = None + time_per_sample = None memory = None for line in lines: line = line.strip() @@ -75,12 +87,20 @@ class CyclesTest(api.Test): if offset != -1: time = line[offset + len(prefix_time):] time = float(time) + offset = line.find(prefix_time_per_sample) + if offset != -1: + time_per_sample = line[offset + len(prefix_time_per_sample):] + time_per_sample = time_per_sample.split()[0] + time_per_sample = float(time_per_sample) offset = line.find(prefix_memory) if offset != -1: memory = line[offset + len(prefix_memory):] memory = memory.split()[0].replace(',', '') memory = float(memory) + if time_per_sample: + time = time_per_sample + if not (time and memory): raise Exception("Error parsing render time output") @@ -88,5 +108,5 @@ class CyclesTest(api.Test): def generate(env): - filepaths = env.find_blend_files('cycles-x/*') + filepaths = env.find_blend_files('cycles/*') return [CyclesTest(filepath) for filepath in filepaths] diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index a1b94abc317..75f00c3c5cc 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -637,7 +637,6 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS) set(render_tests bsdf denoise - denoise_animation displacement hair image_colorspace From 96027b2d15b73d2b5086899425021ea4c903fa00 Mon Sep 17 00:00:00 2001 From: Urko Date: Tue, 21 Sep 2021 15:37:58 +0200 Subject: [PATCH 0053/1500] Make knife drawing anti aliased (Monkey work based on D11333) Knife tool. Make the drawing anti aliased and consistent by using 3D_POLYLINE/3D_POINT shaders, and making sure alpha blending is on. Monkey work based on [[ https://developer.blender.org/D11333 | D11333 ]] done by [[ https://developer.blender.org/p/krash/ | Anthony Edlin (krash)]] Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12287 --- source/blender/editors/mesh/editmesh_knife.c | 48 ++++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 3e3593d18fd..090754a2353 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -29,6 +29,8 @@ #include "MEM_guardedalloc.h" +#include "DNA_userdef_types.h" + #include "BLI_alloca.h" #include "BLI_array.h" #include "BLI_linklist.h" @@ -323,10 +325,13 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) } uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + float viewport[4]; + GPU_viewport_size_get_f(viewport); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformThemeColor3(TH_TRANSFORM); - GPU_line_width(2.0); + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + immUniform2fv("viewportSize", &viewport[2]); + immUniform1f("lineWidth", 2 * U.pixelsize); immBegin(GPU_PRIM_LINES, 2); immVertex3fv(pos, v1); @@ -341,6 +346,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v { const KnifeTool_OpData *kcd = arg; GPU_depth_test(GPU_DEPTH_NONE); + GPU_blend(GPU_BLEND_ALPHA); GPU_matrix_push_projection(); GPU_polygon_offset(1.0f, 1.0f); @@ -355,55 +361,70 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + float viewport[4]; + GPU_viewport_size_get_f(viewport); if (kcd->mode == MODE_DRAGGING) { + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + immUniform2fv("viewportSize", &viewport[2]); + immUniform1f("lineWidth", 2 * U.pixelsize); + immUniformColor3ubv(kcd->colors.line); - GPU_line_width(2.0); immBegin(GPU_PRIM_LINES, 2); immVertex3fv(pos, kcd->prev.cage); immVertex3fv(pos, kcd->curr.cage); immEnd(); + immUnbindProgram(); } if (kcd->prev.vert) { + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3ubv(kcd->colors.point); GPU_point_size(11 * UI_DPI_FAC); immBegin(GPU_PRIM_POINTS, 1); immVertex3fv(pos, kcd->prev.cage); immEnd(); + immUnbindProgram(); } if (kcd->prev.bmface || kcd->prev.edge) { + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3ubv(kcd->colors.curpoint); GPU_point_size(9 * UI_DPI_FAC); immBegin(GPU_PRIM_POINTS, 1); immVertex3fv(pos, kcd->prev.cage); immEnd(); + immUnbindProgram(); } if (kcd->curr.vert) { + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3ubv(kcd->colors.point); GPU_point_size(11 * UI_DPI_FAC); immBegin(GPU_PRIM_POINTS, 1); immVertex3fv(pos, kcd->curr.cage); immEnd(); + immUnbindProgram(); } else if (kcd->curr.edge) { + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + immUniform2fv("viewportSize", &viewport[2]); + immUniform1f("lineWidth", 2 * U.pixelsize); immUniformColor3ubv(kcd->colors.edge); - GPU_line_width(2.0); immBegin(GPU_PRIM_LINES, 2); immVertex3fv(pos, kcd->curr.edge->v1->cageco); immVertex3fv(pos, kcd->curr.edge->v2->cageco); immEnd(); + immUnbindProgram(); } if (kcd->curr.bmface || kcd->curr.edge) { + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3ubv(kcd->colors.curpoint); GPU_point_size(9 * UI_DPI_FAC); @@ -417,6 +438,8 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v int i, snapped_verts_count, other_verts_count; float fcol[4]; + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformThemeColor3(TH_TRANSFORM); GPU_blend(GPU_BLEND_ALPHA); GPUVertBuf *vert = GPU_vertbuf_create_with_format(format); @@ -454,14 +477,19 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPU_batch_discard(batch); GPU_blend(GPU_BLEND_NONE); + + immEnd(); + immUnbindProgram(); } if (kcd->totkedge > 0) { BLI_mempool_iter iter; KnifeEdge *kfe; + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + immUniform2fv("viewportSize", &viewport[2]); + immUniform1f("lineWidth", U.pixelsize); immUniformColor3ubv(kcd->colors.line); - GPU_line_width(1.0); GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_LINES, BLI_mempool_len(kcd->kedges) * 2); @@ -476,6 +504,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v } immEnd(); + immUnbindProgram(); GPU_batch_draw(batch); GPU_batch_discard(batch); @@ -485,6 +514,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v BLI_mempool_iter iter; KnifeVert *kfv; + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3ubv(kcd->colors.point); GPU_point_size(5.0 * UI_DPI_FAC); @@ -500,18 +530,18 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v } immEnd(); + immUnbindProgram(); GPU_batch_draw(batch); GPU_batch_discard(batch); } - immUnbindProgram(); - GPU_matrix_pop(); GPU_matrix_pop_projection(); /* Reset default */ GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + GPU_blend(GPU_BLEND_NONE); } /** \} */ From a25cd206c6b2b84373a4dad218641b8a2da42b78 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 21 Sep 2021 15:56:05 +0200 Subject: [PATCH 0054/1500] Cleanup ClangTidy warning. Different parameter name in function declaration and implementation. --- source/blender/draw/DRW_engine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 2e25211ea62..5e7b812c37b 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -177,7 +177,7 @@ struct DrawDataList *DRW_drawdatalist_from_id(struct ID *id); void DRW_drawdata_free(struct ID *id); bool DRW_opengl_context_release(void); -void DRW_opengl_context_activate(bool test); +void DRW_opengl_context_activate(bool drw_state); #ifdef __cplusplus } From 33955231f326c972dfd96486beb66f58831b59a3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 Sep 2021 00:03:51 +1000 Subject: [PATCH 0055/1500] Fix T91458: Displaying dope-sheet with sequencer keyframes crashes Error in e6194e735791b42feb51e810a4910a41d999d3bf. --- source/blender/editors/animation/anim_deps.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index 97679723d84..088de80bb65 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -217,8 +217,6 @@ static void animchan_sync_fcurve_scene(bAnimListElem *ale) /* Check if this strip is selected. */ Editing *ed = SEQ_editing_get(scene); seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false); - MEM_freeN(seq_name); - if (seq == NULL) { return; } From 84f98e28e384f1245c11a81da57e74fa6130d286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 21 Sep 2021 16:38:25 +0200 Subject: [PATCH 0056/1500] Fix T87801: Eevee ambient occlusion is incorrect on M1 macMini The issue was caused by `textureSize()` returning the size of the level 0 even when the min texture level is higher than 0. Using a uniform to pass the correct size fixes the issue. This issue also affected the downsampling of radiance for reflections and refractions. This does not affect anything other than the recusive downsampling shaders. --- .../draw/engines/eevee/eevee_effects.c | 24 +++++++++++++++---- .../eevee/shaders/effect_downsample_frag.glsl | 13 ++++++---- .../eevee/shaders/effect_minmaxz_frag.glsl | 19 +++++++++------ 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index 3a38edecec6..d5960ea57d5 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -38,7 +38,8 @@ static struct { struct GPUTexture *color_src; int depth_src_layer; - float cube_texel_size; + /* Size can be vec3. But we only use 2 components in the shader. */ + float texel_size[2]; } e_data = {NULL}; /* Engine data */ #define SETUP_BUFFER(tex, fb, fb_color) \ @@ -259,6 +260,7 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_PASS_CREATE(psl->color_downsample_ps, DRW_STATE_WRITE_COLOR); grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_sh_get(), psl->color_downsample_ps); DRW_shgroup_uniform_texture_ex(grp, "source", txl->filtered_radiance, GPU_SAMPLER_FILTER); + DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } @@ -267,7 +269,7 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_cube_sh_get(), psl->color_downsample_cube_ps); DRW_shgroup_uniform_texture_ref(grp, "source", &e_data.color_src); - DRW_shgroup_uniform_float(grp, "texelSize", &e_data.cube_texel_size, 1); + DRW_shgroup_uniform_float(grp, "texelSize", e_data.texel_size, 1); DRW_shgroup_uniform_int_copy(grp, "Layer", 0); DRW_shgroup_call_instances(grp, NULL, quad, 6); } @@ -277,6 +279,7 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_PASS_CREATE(psl->maxz_downlevel_ps, downsample_write); grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_downlevel_sh_get(), psl->maxz_downlevel_ps); DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &txl->maxzbuffer, GPU_SAMPLER_DEFAULT); + DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1); DRW_shgroup_call(grp, quad, NULL); /* Copy depth buffer to top level of HiZ */ @@ -345,16 +348,22 @@ static void min_downsample_cb(void *vedata, int UNUSED(level)) } #endif -static void max_downsample_cb(void *vedata, int UNUSED(level)) +static void max_downsample_cb(void *vedata, int level) { EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; + EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl; + int texture_size[3]; + GPU_texture_get_mipmap_size(txl->maxzbuffer, level - 1, texture_size); + e_data.texel_size[0] = 1.0f / texture_size[0]; + e_data.texel_size[1] = 1.0f / texture_size[1]; DRW_draw_pass(psl->maxz_downlevel_ps); } static void simple_downsample_cube_cb(void *vedata, int level) { EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - e_data.cube_texel_size = (float)(1 << level) / (float)GPU_texture_width(e_data.color_src); + e_data.texel_size[0] = (float)(1 << level) / (float)GPU_texture_width(e_data.color_src); + e_data.texel_size[1] = e_data.texel_size[0]; DRW_draw_pass(psl->color_downsample_cube_ps); } @@ -390,9 +399,14 @@ void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, GPUTexture *depth_src, int l } } -static void downsample_radiance_cb(void *vedata, int UNUSED(level)) +static void downsample_radiance_cb(void *vedata, int level) { EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; + EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl; + int texture_size[3]; + GPU_texture_get_mipmap_size(txl->filtered_radiance, level - 1, texture_size); + e_data.texel_size[0] = 1.0f / texture_size[0]; + e_data.texel_size[1] = 1.0f / texture_size[1]; DRW_draw_pass(psl->color_downsample_ps); } diff --git a/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl index d1cb25af82f..9fc258da185 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl @@ -9,14 +9,16 @@ uniform sampler2D source; uniform float fireflyFactor; +#ifndef COPY_SRC +uniform vec2 texelSize; +#endif + out vec4 FragColor; void main() { - vec2 texel_size = 1.0 / vec2(textureSize(source, 0)); - vec2 uvs = gl_FragCoord.xy * texel_size; - #ifdef COPY_SRC + vec2 uvs = gl_FragCoord.xy / vec2(textureSize(source, 0)); FragColor = textureLod(source, uvs, 0.0); FragColor = safe_color(FragColor); @@ -25,7 +27,10 @@ void main() FragColor *= 1.0 - max(0.0, luma - fireflyFactor) / luma; #else - vec4 ofs = texel_size.xyxy * vec4(0.75, 0.75, -0.75, -0.75); + /* NOTE(@fclem): textureSize() does not work the same on all implementations + * when changing the min and max texture levels. Use uniform instead (see T87801). */ + vec2 uvs = gl_FragCoord.xy * texelSize; + vec4 ofs = texelSize.xyxy * vec4(0.75, 0.75, -0.75, -0.75); uvs *= 2.0; FragColor = textureLod(source, uvs + ofs.xy, 0.0); diff --git a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl index ccb65d2e5a6..8ef39a55921 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl @@ -14,6 +14,10 @@ uniform int depthLayer; uniform sampler2D depthBuffer; #endif +#ifndef COPY_DEPTH +uniform vec2 texelSize; +#endif + #ifdef LAYERED # define sampleLowerMip(t) texture(depthBuffer, vec3(t, depthLayer)).r # define gatherLowerMip(t) textureGather(depthBuffer, vec3(t, depthLayer)) @@ -41,23 +45,24 @@ out vec4 fragColor; void main() { vec2 texel = gl_FragCoord.xy; - vec2 texel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); #ifdef COPY_DEPTH - vec2 uv = texel * texel_size; + vec2 uv = texel / vec2(textureSize(depthBuffer, 0).xy); float val = sampleLowerMip(uv); #else - vec2 uv = texel * 2.0 * texel_size; + /* NOTE(@fclem): textureSize() does not work the same on all implementations + * when changing the min and max texture levels. Use uniform instead (see T87801). */ + vec2 uv = texel * 2.0 * texelSize; vec4 samp; # ifdef GPU_ARB_texture_gather samp = gatherLowerMip(uv); # else - samp.x = sampleLowerMip(uv + vec2(-0.5, -0.5) * texel_size); - samp.y = sampleLowerMip(uv + vec2(-0.5, 0.5) * texel_size); - samp.z = sampleLowerMip(uv + vec2(0.5, -0.5) * texel_size); - samp.w = sampleLowerMip(uv + vec2(0.5, 0.5) * texel_size); + samp.x = sampleLowerMip(uv + vec2(-0.5, -0.5) * texelSize); + samp.y = sampleLowerMip(uv + vec2(-0.5, 0.5) * texelSize); + samp.z = sampleLowerMip(uv + vec2(0.5, -0.5) * texelSize); + samp.w = sampleLowerMip(uv + vec2(0.5, 0.5) * texelSize); # endif float val = minmax4(samp.x, samp.y, samp.z, samp.w); From 2e6a7353852f432f626269b3e7d9246701c690e2 Mon Sep 17 00:00:00 2001 From: "Henrik Dick (weasel)" <> Date: Tue, 21 Sep 2021 23:00:49 +0800 Subject: [PATCH 0057/1500] GPencil: Curvature support for length modifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the stroke following an approximated circluar/helical curve. This can be used as an effect for lineart or on its own as helix generator. Reviewed By: Sebastian Parborg (zeddb), Hans Goudey (HooglyBoogly), YimingWu (NicksBest), Antonio Vazquez (antoniov), Henrik Dick (weasel) Differential Revision: https://developer.blender.org/D11668 # 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交 # 说明将会终止提交。 # # 位于分支 master # 您的分支与上游分支 'origin/master' 一致。 # # 要提交的变更: # 修改: source/blender/blenkernel/BKE_gpencil_geom.h # 修改: source/blender/blenkernel/intern/gpencil_geom.cc # 修改: source/blender/gpencil_modifiers/intern/MOD_gpencillength.c # 修改: source/blender/makesdna/DNA_gpencil_modifier_defaults.h # 修改: source/blender/makesdna/DNA_gpencil_modifier_types.h # 修改: source/blender/makesrna/intern/rna_gpencil_modifier.c # --- source/blender/blenkernel/BKE_gpencil_geom.h | 7 +- .../blender/blenkernel/intern/gpencil_geom.cc | 262 +++++++++++++++--- .../intern/MOD_gpencillength.c | 136 +++++++-- .../makesdna/DNA_gpencil_modifier_defaults.h | 6 +- .../makesdna/DNA_gpencil_modifier_types.h | 7 +- .../makesrna/intern/rna_gpencil_modifier.c | 75 ++++- 6 files changed, 417 insertions(+), 76 deletions(-) diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index d472fd6f02b..a9cd553a8fe 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -114,7 +114,12 @@ void BKE_gpencil_dissolve_points(struct bGPdata *gpd, bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float overshoot_fac, - const short mode); + const short mode, + const bool follow_curvature, + const int extra_point_count, + const float segment_influence, + const float max_angle, + const bool invert_curvature); bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps, const int index_from, const int index_to); diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 8ff026231f5..d7c906be18e 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -540,65 +540,242 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, return true; } +/** + * Give extra stroke points before and after the original tip points. + * \param gps: Target stroke + * \param count_before: how many extra points to be added before a stroke + * \param count_after: how many extra points to be added after a stroke + */ +static bool BKE_gpencil_stroke_extra_points(bGPDstroke *gps, + const int count_before, + const int count_after) +{ + bGPDspoint *pts = gps->points; + + BLI_assert(count_before >= 0); + BLI_assert(count_after >= 0); + if (!count_before && !count_after) { + return false; + } + + const int new_count = count_before + count_after + gps->totpoints; + + bGPDspoint *new_pts = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, __func__); + + for (int i = 0; i < count_before; i++) { + memcpy(&new_pts[i], &pts[0], sizeof(bGPDspoint)); + } + memcpy(&new_pts[count_before], pts, sizeof(bGPDspoint) * gps->totpoints); + for (int i = new_count - count_after; i < new_count; i++) { + memcpy(&new_pts[i], &pts[gps->totpoints - 1], sizeof(bGPDspoint)); + } + + if (gps->dvert) { + MDeformVert *new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count, __func__); + + for (int i = 0; i < new_count; i++) { + MDeformVert *dv = &gps->dvert[CLAMPIS(i - count_before, 0, gps->totpoints - 1)]; + int inew = i; + new_dv[inew].flag = dv->flag; + new_dv[inew].totweight = dv->totweight; + new_dv[inew].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, + __func__); + memcpy(new_dv[inew].dw, dv->dw, sizeof(MDeformWeight) * dv->totweight); + } + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + gps->dvert = new_dv; + } + + MEM_freeN(gps->points); + gps->points = new_pts; + gps->totpoints = new_count; + + return true; +} + /** * Backbone stretch similar to Freestyle. * \param gps: Stroke to sample. - * \param dist: Distance of one segment. - * \param overshoot_fac: How exact is the follow curve algorithm. + * \param dist: Length of the added section. + * \param overshoot_fac: Relative length of the curve which is used to determine the extension. * \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End) + * \param follow_curvature: True for appproximating curvature of given overshoot. + * \param extra_point_count: When follow_curvature is true, use this amount of extra points */ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float overshoot_fac, - const short mode) + const short mode, + const bool follow_curvature, + const int extra_point_count, + const float segment_influence, + const float max_angle, + const bool invert_curvature) { #define BOTH 0 #define START 1 #define END 2 - bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt; - int i; - float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac); + const bool do_start = ELEM(mode, BOTH, START); + const bool do_end = ELEM(mode, BOTH, END); + float used_percent_length = overshoot_fac; + CLAMP(used_percent_length, 1e-4f, 1.0f); + if (!isfinite(used_percent_length)) { + /* #used_percent_length must always be finite, otherwise a segfault occurs. + * Since this function should never segfault, set #used_percent_length to a safe fallback. */ + /* NOTE: This fallback is used if gps->totpoints == 2, see MOD_gpencillength.c */ + used_percent_length = 0.1f; + } - if (gps->totpoints < 2 || dist < FLT_EPSILON) { + if (gps->totpoints <= 1 || dist < FLT_EPSILON || extra_point_count <= 0) { return false; } - last_pt = &pt[gps->totpoints - 1]; - second_last = &pt[gps->totpoints - 2]; - next_pt = &pt[1]; + /* NOTE: When it's just a straight line, we don't need to do the curvature stuff. */ + if (!follow_curvature || gps->totpoints <= 2) { + /* Not following curvature, just straight line. */ + /* NOTE: #overshoot_point_param can not be zero. */ + float overshoot_point_param = used_percent_length * (gps->totpoints - 1); + float result[3]; - if (mode == BOTH || mode == START) { - float len1 = 0.0f; - i = 1; - while (len1 < threshold && gps->totpoints > i) { - next_pt = &pt[i]; - len1 = len_v3v3(&next_pt->x, &pt->x); - i++; - } - float extend1 = (len1 + dist) / len1; - float result1[3]; - - interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1); - copy_v3_v3(&pt->x, result1); - } - - if (mode == BOTH || mode == END) { - float len2 = 0.0f; - i = 2; - while (len2 < threshold && gps->totpoints >= i) { - second_last = &pt[gps->totpoints - i]; - len2 = len_v3v3(&last_pt->x, &second_last->x); - i++; + if (do_start) { + int index1 = floor(overshoot_point_param); + int index2 = ceil(overshoot_point_param); + interp_v3_v3v3(result, + &gps->points[index1].x, + &gps->points[index2].x, + fmodf(overshoot_point_param, 1.0f)); + sub_v3_v3(result, &gps->points[0].x); + if (UNLIKELY(is_zero_v3(result))) { + sub_v3_v3v3(result, &gps->points[1].x, &gps->points[0].x); + } + madd_v3_v3fl(&gps->points[0].x, result, -dist / len_v3(result)); } - float extend2 = (len2 + dist) / len2; - float result2[3]; - interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2); - - copy_v3_v3(&last_pt->x, result2); + if (do_end) { + int index1 = gps->totpoints - 1 - floor(overshoot_point_param); + int index2 = gps->totpoints - 1 - ceil(overshoot_point_param); + interp_v3_v3v3(result, + &gps->points[index1].x, + &gps->points[index2].x, + fmodf(overshoot_point_param, 1.0f)); + sub_v3_v3(result, &gps->points[gps->totpoints - 1].x); + if (UNLIKELY(is_zero_v3(result))) { + sub_v3_v3v3( + result, &gps->points[gps->totpoints - 2].x, &gps->points[gps->totpoints - 1].x); + } + madd_v3_v3fl(&gps->points[gps->totpoints - 1].x, result, -dist / len_v3(result)); + } + return true; } + /* Curvature calculation. */ + + /* First allocate the new stroke size. */ + const int first_old_index = do_start ? extra_point_count : 0; + const int last_old_index = gps->totpoints - 1 + first_old_index; + const int orig_totpoints = gps->totpoints; + BKE_gpencil_stroke_extra_points(gps, first_old_index, do_end ? extra_point_count : 0); + + /* The fractional amount of points to query when calculating the average curvature of the + * strokes. */ + const float overshoot_parameter = used_percent_length * (orig_totpoints - 2); + int overshoot_pointcount = ceil(overshoot_parameter); + CLAMP(overshoot_pointcount, 1, orig_totpoints - 2); + + /* Do for both sides without code duplication. */ + float no[3], vec1[3], vec2[3], total_angle[3]; + for (int k = 0; k < 2; k++) { + if ((k == 0 && !do_start) || (k == 1 && !do_end)) { + continue; + } + + const int start_i = k == 0 ? first_old_index : + last_old_index; // first_old_index, last_old_index + const int dir_i = 1 - k * 2; // 1, -1 + + sub_v3_v3v3(vec1, &gps->points[start_i + dir_i].x, &gps->points[start_i].x); + zero_v3(total_angle); + float segment_length = normalize_v3(vec1); + float overshoot_length = 0.0f; + + /* Accumulate rotation angle and length. */ + int j = 0; + for (int i = start_i; j < overshoot_pointcount; i += dir_i, j++) { + /* Don't fully add last segment to get continuity in overshoot_fac. */ + float fac = fmin(overshoot_parameter - j, 1.0f); + + /* Read segments. */ + copy_v3_v3(vec2, vec1); + sub_v3_v3v3(vec1, &gps->points[i + dir_i * 2].x, &gps->points[i + dir_i].x); + const float len = normalize_v3(vec1); + float angle = angle_normalized_v3v3(vec1, vec2) * fac; + + /* Add half of both adjacent legs of the current angle. */ + const float added_len = (segment_length + len) * 0.5f * fac; + overshoot_length += added_len; + segment_length = len; + + if (angle > max_angle) { + continue; + } + if (angle > M_PI * 0.995f) { + continue; + } + + angle *= powf(added_len, segment_influence); + + cross_v3_v3v3(no, vec1, vec2); + normalize_v3_length(no, angle); + add_v3_v3(total_angle, no); + } + + if (UNLIKELY(overshoot_length == 0.0f)) { + /* Don't do a proper extension if the used points are all in the same position. */ + continue; + } + + sub_v3_v3v3(vec1, &gps->points[start_i].x, &gps->points[start_i + dir_i].x); + /* In general curvature = 1/radius. For the case without the + * weights introduced by #segment_influence, the calculation is + * curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length */ + float curvature = normalize_v3(total_angle) / overshoot_length; + /* Compensate for the weights powf(added_len, segment_influence). */ + curvature /= powf(overshoot_length / fminf(overshoot_parameter, (float)j), segment_influence); + if (invert_curvature) { + curvature = -curvature; + } + const float angle_step = curvature * dist / extra_point_count; + float step_length = dist / extra_point_count; + if (fabsf(angle_step) > FLT_EPSILON) { + /* Make a direct step length from the assigned arc step length. */ + step_length *= sin(angle_step * 0.5f) / (angle_step * 0.5f); + } + else { + zero_v3(total_angle); + } + const float prev_length = normalize_v3_length(vec1, step_length); + + /* Build rotation matrix here to get best performance. */ + float rot[3][3]; + float q[4]; + axis_angle_to_quat(q, total_angle, angle_step); + quat_to_mat3(rot, q); + + /* Rotate the starting direction to account for change in edge lengths. */ + axis_angle_to_quat(q, + total_angle, + fmaxf(0.0f, 1.0f - fabs(segment_influence)) * + (curvature * prev_length - angle_step) / 2.0f); + mul_qt_v3(q, vec1); + + /* Now iteratively accumulate the segments with a rotating added direction. */ + for (int i = start_i - dir_i, j = 0; j < extra_point_count; i -= dir_i, j++) { + mul_v3_m3v3(vec1, rot, vec1); + add_v3_v3v3(&gps->points[i].x, vec1, &gps->points[i + dir_i].x); + } + } return true; } @@ -749,6 +926,7 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo second_last = &pt[gps->totpoints - 2]; + float len; float len1, cut_len1; float len2, cut_len2; len1 = len2 = cut_len1 = cut_len2 = 0.0f; @@ -759,11 +937,13 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo i = 0; index_end = gps->totpoints - 1; while (len1 < dist && gps->totpoints > i + 1) { - len1 += len_v3v3(&pt[i].x, &pt[i + 1].x); + len = len_v3v3(&pt[i].x, &pt[i + 1].x); + len1 += len; cut_len1 = len1 - dist; i++; } index_start = i - 1; + interp_v3_v3v3(&pt[index_start].x, &pt[index_start + 1].x, &pt[index_start].x, cut_len1 / len); } if (mode == END) { @@ -771,18 +951,20 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo i = 2; while (len2 < dist && gps->totpoints >= i) { second_last = &pt[gps->totpoints - i]; - len2 += len_v3v3(&second_last[1].x, &second_last->x); + len = len_v3v3(&second_last[1].x, &second_last->x); + len2 += len; cut_len2 = len2 - dist; i++; } index_end = gps->totpoints - i + 2; + interp_v3_v3v3(&pt[index_end].x, &pt[index_end - 1].x, &pt[index_end].x, cut_len2 / len); } if (index_end <= index_start) { index_start = index_end = 0; /* empty stroke */ } - if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) { + if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < 0)) { index_start = index_end = 0; /* no length left to cut */ } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index 6aa0e6c152e..e4a48925bf0 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -72,9 +72,14 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target) } static bool gpencil_modify_stroke(bGPDstroke *gps, - float length, + const float length, const float overshoot_fac, - const short len_mode) + const short len_mode, + const bool use_curvature, + const int extra_point_count, + const float segment_influence, + const float max_angle, + const bool invert_curvature) { bool changed = false; if (length == 0.0f) { @@ -82,10 +87,18 @@ static bool gpencil_modify_stroke(bGPDstroke *gps, } if (length > 0.0f) { - BKE_gpencil_stroke_stretch(gps, length, overshoot_fac, len_mode); + changed = BKE_gpencil_stroke_stretch(gps, + length, + overshoot_fac, + len_mode, + use_curvature, + extra_point_count, + segment_influence, + max_angle, + invert_curvature); } else { - changed |= BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode); + changed = BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode); } return changed; @@ -96,12 +109,51 @@ static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke bool changed = false; const float len = (lmd->mode == GP_LENGTH_ABSOLUTE) ? 1.0f : BKE_gpencil_stroke_length(gps, true); + const int totpoints = gps->totpoints; if (len < FLT_EPSILON) { return; } - changed |= gpencil_modify_stroke(gps, len * lmd->start_fac, lmd->overshoot_fac, 1); - changed |= gpencil_modify_stroke(gps, len * lmd->end_fac, lmd->overshoot_fac, 2); + /* Always do the stretching first since it might depend on points which could be deleted by the + * shrink. */ + float first_fac = lmd->start_fac; + int first_mode = 1; + float second_fac = lmd->end_fac; + int second_mode = 2; + if (first_fac < 0) { + SWAP(float, first_fac, second_fac); + SWAP(int, first_mode, second_mode); + } + + const int first_extra_point_count = ceil(first_fac * lmd->point_density); + const int second_extra_point_count = ceil(second_fac * lmd->point_density); + + changed |= gpencil_modify_stroke(gps, + len * first_fac, + lmd->overshoot_fac, + first_mode, + lmd->flag & GP_LENGTH_USE_CURVATURE, + first_extra_point_count, + lmd->segment_influence, + lmd->max_angle, + lmd->flag & GP_LENGTH_INVERT_CURVATURE); + /* HACK: The second #overshoot_fac needs to be adjusted because it is not + * done in the same stretch call, because it can have a different length. + * The adjustment needs to be stable when + * ceil(overshoot_fac*(gps->totpoints - 2)) is used in stretch and never + * produce a result highter than totpoints - 2. */ + const float second_overshoot_fac = lmd->overshoot_fac * (totpoints - 2) / + ((float)gps->totpoints - 2) * + (1.0f - 0.1f / (totpoints - 1.0f)); + changed |= gpencil_modify_stroke(gps, + len * second_fac, + second_overshoot_fac, + second_mode, + lmd->flag & GP_LENGTH_USE_CURVATURE, + second_extra_point_count, + lmd->segment_influence, + lmd->max_angle, + lmd->flag & GP_LENGTH_INVERT_CURVATURE); if (changed) { BKE_gpencil_stroke_geometry_update(gpd, gps); @@ -117,20 +169,25 @@ static void deformStroke(GpencilModifierData *md, { bGPdata *gpd = ob->data; LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md; - if (is_stroke_affected_by_modifier(ob, - lmd->layername, - lmd->material, - lmd->pass_index, - lmd->layer_pass, - 1, - gpl, - gps, - lmd->flag & GP_LENGTH_INVERT_LAYER, - lmd->flag & GP_LENGTH_INVERT_PASS, - lmd->flag & GP_LENGTH_INVERT_LAYERPASS, - lmd->flag & GP_LENGTH_INVERT_MATERIAL)) { - applyLength(lmd, gpd, gps); + if (!is_stroke_affected_by_modifier(ob, + lmd->layername, + lmd->material, + lmd->pass_index, + lmd->layer_pass, + 1, + gpl, + gps, + lmd->flag & GP_LENGTH_INVERT_LAYER, + lmd->flag & GP_LENGTH_INVERT_PASS, + lmd->flag & GP_LENGTH_INVERT_LAYERPASS, + lmd->flag & GP_LENGTH_INVERT_MATERIAL)) { + return; } + if ((gps->flag & GP_STROKE_CYCLIC) != 0) { + /* Don't affect cyclic strokes as they have no start/end. */ + return; + } + applyLength(lmd, gpd, gps); } static void bakeModifier(Main *UNUSED(bmain), @@ -168,10 +225,16 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE); - uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE); + if (RNA_enum_get(ptr, "mode") == GP_LENGTH_RELATIVE) { + uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE); + uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE); + } + else { + uiItemR(col, ptr, "start_length", 0, IFACE_("Start"), ICON_NONE); + uiItemR(col, ptr, "end_length", 0, IFACE_("End"), ICON_NONE); + } - uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Overshoot"), ICON_NONE); + uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Used Length"), ICON_NONE); gpencil_modifier_panel_end(layout, ptr); } @@ -181,10 +244,39 @@ static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) gpencil_modifier_masking_panel_draw(panel, true, false); } +static void curvature_header_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiItemR(layout, ptr, "use_curvature", 0, IFACE_("Curvature"), ICON_NONE); +} + +static void curvature_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayoutSetPropSep(layout, true); + + uiLayout *col = uiLayoutColumn(layout, false); + + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_curvature")); + + uiItemR(col, ptr, "point_density", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "segment_influence", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "max_angle", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "invert_curvature", 0, IFACE_("Invert"), ICON_NONE); +} + static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Length, panel_draw); + gpencil_modifier_subpanel_register( + region_type, "curvature", "", curvature_header_draw, curvature_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); } diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 450527c7443..3a100c00999 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -314,9 +314,13 @@ { \ .start_fac = 0.1f,\ .end_fac = 0.1f,\ - .overshoot_fac = 0.01f,\ + .overshoot_fac = 0.1f,\ .pass_index = 0,\ .material = NULL,\ + .flag = GP_LENGTH_USE_CURVATURE,\ + .point_density = 30.0f,\ + .segment_influence = 0.0f,\ + .max_angle = DEG2RAD(170.0f),\ } #define _DNA_DEFAULT_DashGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index d3429329ef6..43469fbb46b 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -493,7 +493,10 @@ typedef struct LengthGpencilModifierData { float overshoot_fac; /** Modifier mode. */ int mode; - char _pad[4]; + /* Curvature parameters. */ + float point_density; + float segment_influence; + float max_angle; } LengthGpencilModifierData; typedef enum eLengthGpencil_Flag { @@ -501,6 +504,8 @@ typedef enum eLengthGpencil_Flag { GP_LENGTH_INVERT_PASS = (1 << 1), GP_LENGTH_INVERT_LAYERPASS = (1 << 2), GP_LENGTH_INVERT_MATERIAL = (1 << 3), + GP_LENGTH_USE_CURVATURE = (1 << 4), + GP_LENGTH_INVERT_CURVATURE = (1 << 5), } eLengthGpencil_Flag; typedef enum eLengthGpencil_Type { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 4fa33424994..d5ef7cf2651 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -74,6 +74,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_DASH, "Dot Dash", "Generate dot-dash styled strokes"}, + {eGpencilModifierType_Length, + "GP_LENGTH", + ICON_MOD_LENGTH, + "Length", + "Extend or shrink strokes"}, {eGpencilModifierType_Lineart, "GP_LINEART", ICON_MOD_LINEART, @@ -120,11 +125,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_LATTICE, "Lattice", "Deform strokes using lattice"}, - {eGpencilModifierType_Length, - "GP_LENGTH", - ICON_MOD_LENGTH, - "Length", - "Extend or shrink strokes"}, {eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"}, {eGpencilModifierType_Offset, "GP_OFFSET", @@ -3278,14 +3278,29 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) prop = RNA_def_property(srna, "start_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "start_fac"); - RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1); - RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment"); + RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2); + RNA_def_property_ui_text( + prop, "Start Factor", "Added length to the start of each stroke relative to its length"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "end_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "end_fac"); - RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1); - RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment"); + RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2); + RNA_def_property_ui_text( + prop, "End Factor", "Added length to the end of each stroke relative to its length"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "start_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "start_fac"); + RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3); + RNA_def_property_ui_text( + prop, "Start Factor", "Absolute added length to the start of each stroke"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "end_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "end_fac"); + RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3); + RNA_def_property_ui_text(prop, "End Factor", "Absolute added length to the end of each stroke"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "overshoot_factor", PROP_FLOAT, PROP_FACTOR); @@ -3293,8 +3308,8 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text( prop, - "Overshoot Factor", - "Defines how precise must follow the stroke trajectory for the overshoot extremes"); + "Used Length", + "Defines what portion of the stroke is used for the calculation of the extension"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); @@ -3303,6 +3318,44 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", "Mode to define length"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_curvature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_USE_CURVATURE); + RNA_def_property_ui_text(prop, "Use Curvature", "Follow the curvature of the stroke"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_curvature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_CURVATURE); + RNA_def_property_ui_text( + prop, "Invert Curvature", "Invert the curvature of the stroke's extension"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "point_density", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.1f, 1000.0f); + RNA_def_property_ui_range(prop, 0.1f, 1000.0f, 1.0f, 1); + RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC); + RNA_def_property_ui_text( + prop, "Point Density", "Multiplied by Start/End for the total added point count"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "segment_influence", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, -2.0f, 3.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 2); + RNA_def_property_ui_text(prop, + "Segment Influence", + "Factor to determine how much the length of the individual segments " + "should influence the final computed curvature. Higher factors makes " + "small segments influence the overall curvature less"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "max_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_ui_text(prop, + "Filter Angle", + "Ignore points on the stroke that deviate from their neighbors by more " + "than this angle when determining the extrapolation shape"); + RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f)); + RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(179.5f), 10.0f, 1); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "layername"); RNA_def_property_ui_text(prop, "Layer", "Layer name"); From 49ab38bf54d4da95f0bb67a348c9305527d95983 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Tue, 21 Sep 2021 17:29:19 +0200 Subject: [PATCH 0058/1500] Fix T91534: GPencil interpolate Sequence fails if stroke has 0 points In some cases the stroke has 0 points and this must be skipped in the interpolation. --- source/blender/editors/gpencil/gpencil_interpolate.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index fdd9f44605e..d7cab85abad 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -316,6 +316,9 @@ static void gpencil_stroke_pair_table(bContext *C, if (ELEM(NULL, gps_from, gps_to)) { continue; } + if ((gps_from->totpoints == 0) || (gps_to->totpoints == 0)) { + continue; + } /* Insert the pair entry in the hash table and the list of strokes to keep order. */ BLI_addtail(&tgpil->selected_strokes, BLI_genericNodeN(gps_from)); BLI_ghash_insert(tgpil->pair_strokes, gps_from, gps_to); @@ -1333,6 +1336,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) if (ELEM(NULL, gps_from, gps_to)) { continue; } + if ((gps_from->totpoints == 0) || (gps_to->totpoints == 0)) { + continue; + } /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { From 2df7d1ebce862a7c7471f7a6ba3d7d14d392b3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 21 Sep 2021 17:49:36 +0200 Subject: [PATCH 0059/1500] Cleanup: mention `UUID_STRING_LEN` in comment Mention that `UUID_STRING_LEN` exists in the documentation of `BLI_uuid_format()`. No functional changes. --- source/blender/blenlib/BLI_uuid.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 1ce294ed723..0c6fb3ae3e7 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -51,6 +51,7 @@ bool BLI_uuid_equal(UUID uuid1, UUID uuid2); /** * Format UUID as string. * The buffer must be at least 37 bytes (36 bytes for the UUID + terminating 0). + * Use `UUID_STRING_LEN` from DNA_uuid_types.h if you want to use a constant for this. */ void BLI_uuid_format(char *buffer, UUID uuid) ATTR_NONNULL(); From 3c1e75a2301c352dac72b1742cdd8efa2b335d6e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 21 Sep 2021 17:50:53 +0200 Subject: [PATCH 0060/1500] Functions: make asserts more correct It is valid to e.g. copy construct an integer in the same place, because it is a trivial type. It does not work for types like std::string. This fixes a crash reported in D12584 where it would copy a buffer into itself. We should probably also avoid doing this copy alltogether but that can be done separately. --- source/blender/functions/FN_cpp_type.hh | 11 +++++------ source/blender/functions/FN_cpp_type_make.hh | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index 7277bf99c12..643b2fc1f28 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -96,6 +96,7 @@ class CPPType : NonCopyable, NonMovable { int64_t size_ = 0; int64_t alignment_ = 0; uintptr_t alignment_mask_ = 0; + bool is_trivial_ = false; bool is_trivially_destructible_ = false; bool has_special_member_functions_ = false; @@ -340,7 +341,6 @@ class CPPType : NonCopyable, NonMovable { */ void copy_assign(const void *src, void *dst) const { - BLI_assert(src != dst); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); @@ -371,7 +371,7 @@ class CPPType : NonCopyable, NonMovable { */ void copy_construct(const void *src, void *dst) const { - BLI_assert(src != dst); + BLI_assert(src != dst || is_trivial_); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); @@ -402,7 +402,6 @@ class CPPType : NonCopyable, NonMovable { */ void move_assign(void *src, void *dst) const { - BLI_assert(src != dst); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); @@ -433,7 +432,7 @@ class CPPType : NonCopyable, NonMovable { */ void move_construct(void *src, void *dst) const { - BLI_assert(src != dst); + BLI_assert(src != dst || is_trivial_); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); @@ -464,7 +463,7 @@ class CPPType : NonCopyable, NonMovable { */ void relocate_assign(void *src, void *dst) const { - BLI_assert(src != dst); + BLI_assert(src != dst || is_trivial_); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); @@ -495,7 +494,7 @@ class CPPType : NonCopyable, NonMovable { */ void relocate_construct(void *src, void *dst) const { - BLI_assert(src != dst); + BLI_assert(src != dst || is_trivial_); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); diff --git a/source/blender/functions/FN_cpp_type_make.hh b/source/blender/functions/FN_cpp_type_make.hh index 088f6b469f4..74dbcabf81a 100644 --- a/source/blender/functions/FN_cpp_type_make.hh +++ b/source/blender/functions/FN_cpp_type_make.hh @@ -195,6 +195,7 @@ CPPType::CPPType(CPPTypeParam /* unused */, StringRef debug_name) debug_name_ = debug_name; size_ = (int64_t)sizeof(T); alignment_ = (int64_t)alignof(T); + is_trivial_ = std::is_trivial_v; is_trivially_destructible_ = std::is_trivially_destructible_v; if constexpr (std::is_default_constructible_v) { default_construct_ = default_construct_cb; From 66dbe6e55a1d3e412ad6ce4fd77a545a03e767d7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 21 Sep 2021 17:55:39 +0200 Subject: [PATCH 0061/1500] Cleanup: else-after-return --- source/blender/editors/space_sequencer/sequencer_draw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index e9e29ae3677..a6a83b0c3bc 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1328,13 +1328,13 @@ static bool check_seq_need_thumbnails(Sequence *seq, rctf *view_area) if (min_ii(seq->startdisp, seq->start) > view_area->xmax) { return false; } - else if (max_ii(seq->enddisp, seq->start + seq->len) < view_area->xmin) { + if (max_ii(seq->enddisp, seq->start + seq->len) < view_area->xmin) { return false; } - else if (seq->machine + 1.0f < view_area->ymin) { + if (seq->machine + 1.0f < view_area->ymin) { return false; } - else if (seq->machine > view_area->ymax) { + if (seq->machine > view_area->ymax) { return false; } From fde9c3bc74261aecdebe94dab9cfccf8cce66083 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 21 Sep 2021 11:20:34 -0500 Subject: [PATCH 0062/1500] Fix: Incorrect versioning for float IDProperty default array The versioning has to have different behavior for when the old default value had a float type rather than a double type. Also use memcpy rather than dupalloc, since it's a bit clearer. --- .../blender/blenloader/intern/versioning_300.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 4dc6a0ecea6..791b5d52d97 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -110,7 +110,8 @@ static void version_idproperty_move_data_int(IDPropertyUIDataInt *ui_data, if (default_value != NULL) { if (default_value->type == IDP_ARRAY) { if (default_value->subtype == IDP_INT) { - ui_data->default_array = MEM_dupallocN(IDP_Array(default_value)); + ui_data->default_array = MEM_malloc_arrayN(default_value->len, sizeof(int), __func__); + memcpy(ui_data->default_array, IDP_Array(default_value), sizeof(int) * default_value->len); ui_data->default_array_len = default_value->len; } } @@ -152,9 +153,18 @@ static void version_idproperty_move_data_float(IDPropertyUIDataFloat *ui_data, IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default"); if (default_value != NULL) { if (default_value->type == IDP_ARRAY) { - if (ELEM(default_value->subtype, IDP_FLOAT, IDP_DOUBLE)) { - ui_data->default_array = MEM_dupallocN(IDP_Array(default_value)); - ui_data->default_array_len = default_value->len; + const int size = default_value->len; + ui_data->default_array_len = size; + if (default_value->subtype == IDP_FLOAT) { + ui_data->default_array = MEM_malloc_arrayN(size, sizeof(double), __func__); + const float *old_default_array = IDP_Array(default_value); + for (int i = 0; i < ui_data->default_array_len; i++) { + ui_data->default_array[i] = (double)old_default_array[i]; + } + } + else if (default_value->subtype == IDP_DOUBLE) { + ui_data->default_array = MEM_malloc_arrayN(size, sizeof(double), __func__); + memcpy(ui_data->default_array, IDP_Array(default_value), sizeof(double) * size); } } else if (ELEM(default_value->type, IDP_DOUBLE, IDP_FLOAT)) { From 3642e17428448e4e9760ca5a8900d02e0abe2df7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 21 Sep 2021 11:56:54 -0500 Subject: [PATCH 0063/1500] Cleanup: Move curve to mesh node implementation to blenkernel I plan to use this for curve object data conversion to mesh in D12533, and possibly for the implicit curve to mesh conversion in the curve and text object modifier stack in the future. Differential Revision: https://developer.blender.org/D12585 --- .../blender/blenkernel/BKE_curve_to_mesh.hh | 31 + source/blender/blenkernel/CMakeLists.txt | 2 + .../intern/curve_to_mesh_convert.cc | 739 ++++++++++++++++++ .../geometry/nodes/node_geo_curve_to_mesh.cc | 710 +---------------- 4 files changed, 782 insertions(+), 700 deletions(-) create mode 100644 source/blender/blenkernel/BKE_curve_to_mesh.hh create mode 100644 source/blender/blenkernel/intern/curve_to_mesh_convert.cc diff --git a/source/blender/blenkernel/BKE_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh new file mode 100644 index 00000000000..cc1ef08908d --- /dev/null +++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh @@ -0,0 +1,31 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +struct Mesh; +struct CurveEval; + +/** \file + * \ingroup geo + */ + +namespace blender::bke { + +Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile); +Mesh *curve_to_wire_mesh(const CurveEval &curve); + +} // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 0b082bf1c5a..de7864ef36a 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -116,6 +116,7 @@ set(SRC intern/curve_decimate.c intern/curve_deform.c intern/curve_eval.cc + intern/curve_to_mesh_convert.cc intern/curveprofile.c intern/customdata.c intern/customdata_file.c @@ -332,6 +333,7 @@ set(SRC BKE_cryptomatte.h BKE_cryptomatte.hh BKE_curve.h + BKE_curve_to_mesh.hh BKE_curveprofile.h BKE_customdata.h BKE_customdata_file.h diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc new file mode 100644 index 00000000000..5f2f945192c --- /dev/null +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -0,0 +1,739 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_set.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_access.hh" +#include "BKE_attribute_math.hh" +#include "BKE_geometry_set.hh" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_spline.hh" + +#include "BKE_curve_to_mesh.hh" + +using blender::fn::GMutableSpan; +using blender::fn::GSpan; +using blender::fn::GVArray_Typed; +using blender::fn::GVArrayPtr; + +namespace blender::bke { + +/** Information about the creation of one curve spline and profile spline combination. */ +struct ResultInfo { + const Spline &spline; + const Spline &profile; + int vert_offset; + int edge_offset; + int loop_offset; + int poly_offset; + int spline_vert_len; + int spline_edge_len; + int profile_vert_len; + int profile_edge_len; +}; + +static void vert_extrude_to_mesh_data(const Spline &spline, + const float3 profile_vert, + MutableSpan r_verts, + MutableSpan r_edges, + const int vert_offset, + const int edge_offset) +{ + Span positions = spline.evaluated_positions(); + + for (const int i : IndexRange(positions.size() - 1)) { + MEdge &edge = r_edges[edge_offset + i]; + edge.v1 = vert_offset + i; + edge.v2 = vert_offset + i + 1; + edge.flag = ME_LOOSEEDGE; + } + + if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) { + MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1]; + edge.v1 = vert_offset; + edge.v2 = vert_offset + positions.size() - 1; + edge.flag = ME_LOOSEEDGE; + } + + for (const int i : positions.index_range()) { + MVert &vert = r_verts[vert_offset + i]; + copy_v3_v3(vert.co, positions[i] + profile_vert); + } +} + +static void mark_edges_sharp(MutableSpan edges) +{ + for (MEdge &edge : edges) { + edge.flag |= ME_SHARP; + } +} + +static void spline_extrude_to_mesh_data(const ResultInfo &info, + MutableSpan r_verts, + MutableSpan r_edges, + MutableSpan r_loops, + MutableSpan r_polys) +{ + const Spline &spline = info.spline; + const Spline &profile = info.profile; + if (info.profile_vert_len == 1) { + vert_extrude_to_mesh_data(spline, + profile.evaluated_positions()[0], + r_verts, + r_edges, + info.vert_offset, + info.edge_offset); + return; + } + + /* Add the edges running along the length of the curve, starting at each profile vertex. */ + const int spline_edges_start = info.edge_offset; + for (const int i_profile : IndexRange(info.profile_vert_len)) { + const int profile_edge_offset = spline_edges_start + i_profile * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1; + + const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; + const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; + + MEdge &edge = r_edges[profile_edge_offset + i_ring]; + edge.v1 = ring_vert_offset + i_profile; + edge.v2 = next_ring_vert_offset + i_profile; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Add the edges running along each profile ring. */ + const int profile_edges_start = spline_edges_start + + info.profile_vert_len * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; + + const int ring_edge_offset = profile_edges_start + i_ring * info.profile_edge_len; + for (const int i_profile : IndexRange(info.profile_edge_len)) { + const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; + + MEdge &edge = r_edges[ring_edge_offset + i_profile]; + edge.v1 = ring_vert_offset + i_profile; + edge.v2 = ring_vert_offset + i_next_profile; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Calculate poly and corner indices. */ + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1; + + const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; + const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; + + const int ring_edge_start = profile_edges_start + info.profile_edge_len * i_ring; + const int next_ring_edge_offset = profile_edges_start + info.profile_edge_len * i_next_ring; + + const int ring_poly_offset = info.poly_offset + i_ring * info.profile_edge_len; + const int ring_loop_offset = info.loop_offset + i_ring * info.profile_edge_len * 4; + + for (const int i_profile : IndexRange(info.profile_edge_len)) { + const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4; + const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; + + const int spline_edge_start = spline_edges_start + info.spline_edge_len * i_profile; + const int next_spline_edge_start = spline_edges_start + + info.spline_edge_len * i_next_profile; + + MPoly &poly = r_polys[ring_poly_offset + i_profile]; + poly.loopstart = ring_segment_loop_offset; + poly.totloop = 4; + poly.flag = ME_SMOOTH; + + MLoop &loop_a = r_loops[ring_segment_loop_offset]; + loop_a.v = ring_vert_offset + i_profile; + loop_a.e = ring_edge_start + i_profile; + MLoop &loop_b = r_loops[ring_segment_loop_offset + 1]; + loop_b.v = ring_vert_offset + i_next_profile; + loop_b.e = next_spline_edge_start + i_ring; + MLoop &loop_c = r_loops[ring_segment_loop_offset + 2]; + loop_c.v = next_ring_vert_offset + i_next_profile; + loop_c.e = next_ring_edge_offset + i_profile; + MLoop &loop_d = r_loops[ring_segment_loop_offset + 3]; + loop_d.v = next_ring_vert_offset + i_profile; + loop_d.e = spline_edge_start + i_ring; + } + } + + /* Calculate the positions of each profile ring profile along the spline. */ + Span positions = spline.evaluated_positions(); + Span tangents = spline.evaluated_tangents(); + Span normals = spline.evaluated_normals(); + Span profile_positions = profile.evaluated_positions(); + + GVArray_Typed radii = spline.interpolate_to_evaluated(spline.radii()); + for (const int i_ring : IndexRange(info.spline_vert_len)) { + float4x4 point_matrix = float4x4::from_normalized_axis_data( + positions[i_ring], normals[i_ring], tangents[i_ring]); + point_matrix.apply_scale(radii[i_ring]); + + const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + for (const int i_profile : IndexRange(info.profile_vert_len)) { + MVert &vert = r_verts[ring_vert_start + i_profile]; + copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]); + } + } + + /* Mark edge loops from sharp vector control points sharp. */ + if (profile.type() == Spline::Type::Bezier) { + const BezierSpline &bezier_spline = static_cast(profile); + Span control_point_offsets = bezier_spline.control_point_offsets(); + for (const int i : IndexRange(bezier_spline.size())) { + if (bezier_spline.point_is_sharp(i)) { + mark_edges_sharp( + r_edges.slice(spline_edges_start + info.spline_edge_len * control_point_offsets[i], + info.spline_edge_len)); + } + } + } +} + +static inline int spline_extrude_vert_size(const Spline &curve, const Spline &profile) +{ + return curve.evaluated_points_size() * profile.evaluated_points_size(); +} + +static inline int spline_extrude_edge_size(const Spline &curve, const Spline &profile) +{ + /* Add the ring edges, with one ring for every curve vertex, and the edge loops + * that run along the length of the curve, starting on the first profile. */ + return curve.evaluated_points_size() * profile.evaluated_edges_size() + + curve.evaluated_edges_size() * profile.evaluated_points_size(); +} + +static inline int spline_extrude_loop_size(const Spline &curve, const Spline &profile) +{ + return curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; +} + +static inline int spline_extrude_poly_size(const Spline &curve, const Spline &profile) +{ + return curve.evaluated_edges_size() * profile.evaluated_edges_size(); +} + +struct ResultOffsets { + Array vert; + Array edge; + Array loop; + Array poly; +}; +static ResultOffsets calculate_result_offsets(Span profiles, Span curves) +{ + const int total = profiles.size() * curves.size(); + Array vert(total + 1); + Array edge(total + 1); + Array loop(total + 1); + Array poly(total + 1); + + int mesh_index = 0; + int vert_offset = 0; + int edge_offset = 0; + int loop_offset = 0; + int poly_offset = 0; + for (const int i_spline : curves.index_range()) { + for (const int i_profile : profiles.index_range()) { + vert[mesh_index] = vert_offset; + edge[mesh_index] = edge_offset; + loop[mesh_index] = loop_offset; + poly[mesh_index] = poly_offset; + vert_offset += spline_extrude_vert_size(*curves[i_spline], *profiles[i_profile]); + edge_offset += spline_extrude_edge_size(*curves[i_spline], *profiles[i_profile]); + loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile]); + poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile]); + mesh_index++; + } + } + vert.last() = vert_offset; + edge.last() = edge_offset; + loop.last() = loop_offset; + poly.last() = poly_offset; + + return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)}; +} + +static AttributeDomain get_result_attribute_domain(const MeshComponent &component, + const AttributeIDRef &attribute_id) +{ + /* Only use a different domain if it is builtin and must only exist on one domain. */ + if (!component.attribute_is_builtin(attribute_id)) { + return ATTR_DOMAIN_POINT; + } + + std::optional meta_data = component.attribute_get_meta_data(attribute_id); + if (!meta_data) { + /* This function has to return something in this case, but it shouldn't be used, + * so return an output that will assert later if the code attempts to handle it. */ + return ATTR_DOMAIN_AUTO; + } + + return meta_data->domain; +} + +/** + * The data stored in the attribute and its domain from #OutputAttribute, to avoid calling + * `as_span()` for every single profile and curve spline combination, and for readability. + */ +struct ResultAttributeData { + GMutableSpan data; + AttributeDomain domain; +}; + +static std::optional create_attribute_and_get_span( + MeshComponent &component, + const AttributeIDRef &attribute_id, + AttributeMetaData meta_data, + Vector &r_attributes) +{ + const AttributeDomain domain = get_result_attribute_domain(component, attribute_id); + OutputAttribute attribute = component.attribute_try_get_for_output_only( + attribute_id, domain, meta_data.data_type); + if (!attribute) { + return std::nullopt; + } + + GMutableSpan span = attribute.as_span(); + r_attributes.append(std::move(attribute)); + return std::make_optional({span, domain}); +} + +/** + * Store the references to the attribute data from the curve and profile inputs. Here we rely on + * the invariants of the storage of curve attributes, that the order will be consistent between + * splines, and all splines will have the same attributes. + */ +struct ResultAttributes { + /** + * Result attributes on the mesh corresponding to each attribute on the curve input, in the same + * order. The data is optional only in case the attribute does not exist on the mesh for some + * reason, like "shade_smooth" when the result has no faces. + */ + Vector> curve_point_attributes; + Vector> curve_spline_attributes; + + /** + * Result attributes corresponding the attributes on the profile input, in the same order. The + * attributes are optional in case the attribute names correspond to a names used by the curve + * input, in which case the curve input attributes take precedence. + */ + Vector> profile_point_attributes; + Vector> profile_spline_attributes; + + /** + * Because some builtin attributes are not stored contiguously, and the curve inputs might have + * attributes with those names, it's necessary to keep OutputAttributes around to give access to + * the result data in a contiguous array. + */ + Vector attributes; +}; +static ResultAttributes create_result_attributes(const CurveEval &curve, + const CurveEval &profile, + Mesh &mesh) +{ + MeshComponent mesh_component; + mesh_component.replace(&mesh, GeometryOwnershipType::Editable); + Set curve_attributes; + + /* In order to prefer attributes on the main curve input when there are name collisions, first + * check the attributes on the curve, then add attributes on the profile that are not also on the + * main curve input. */ + ResultAttributes result; + curve.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + curve_attributes.add_new(id); + result.curve_point_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + return true; + }, + ATTR_DOMAIN_POINT); + curve.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + curve_attributes.add_new(id); + result.curve_spline_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + return true; + }, + ATTR_DOMAIN_CURVE); + profile.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (curve_attributes.contains(id)) { + result.profile_point_attributes.append({}); + } + else { + result.profile_point_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + } + return true; + }, + ATTR_DOMAIN_POINT); + profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (curve_attributes.contains(id)) { + result.profile_spline_attributes.append({}); + } + else { + result.profile_spline_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + } + return true; + }, + ATTR_DOMAIN_CURVE); + + return result; +} + +template +static void copy_curve_point_data_to_mesh_verts(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + dst.slice(ring_vert_start, info.profile_vert_len).fill(src[i_ring]); + } +} + +template +static void copy_curve_point_data_to_mesh_edges(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + const int edges_start = info.edge_offset + info.profile_vert_len * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_edge_start = edges_start + info.profile_edge_len * i_ring; + dst.slice(ring_edge_start, info.profile_edge_len).fill(src[i_ring]); + } +} + +template +static void copy_curve_point_data_to_mesh_faces(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int ring_face_start = info.poly_offset + info.profile_edge_len * i_ring; + dst.slice(ring_face_start, info.profile_edge_len).fill(src[i_ring]); + } +} + +static void copy_curve_point_attribute_to_mesh(const GSpan src, + const ResultInfo &info, + ResultAttributeData &dst) +{ + GVArrayPtr interpolated_gvarray = info.spline.interpolate_to_evaluated(src); + GSpan interpolated = interpolated_gvarray->get_internal_span(); + + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst.domain) { + case ATTR_DOMAIN_POINT: + copy_curve_point_data_to_mesh_verts(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_EDGE: + copy_curve_point_data_to_mesh_edges(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_FACE: + copy_curve_point_data_to_mesh_faces(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_CORNER: + /* Unsupported for now, since there are no builtin attributes to convert into. */ + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +template +static void copy_profile_point_data_to_mesh_verts(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int profile_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + for (const int i_profile : IndexRange(info.profile_vert_len)) { + dst[profile_vert_start + i_profile] = src[i_profile]; + } + } +} + +template +static void copy_profile_point_data_to_mesh_edges(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_profile : IndexRange(info.profile_vert_len)) { + const int profile_edge_offset = info.edge_offset + i_profile * info.spline_edge_len; + dst.slice(profile_edge_offset, info.spline_edge_len).fill(src[i_profile]); + } +} + +template +static void copy_profile_point_data_to_mesh_faces(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int profile_face_start = info.poly_offset + i_ring * info.profile_edge_len; + for (const int i_profile : IndexRange(info.profile_edge_len)) { + dst[profile_face_start + i_profile] = src[i_profile]; + } + } +} + +static void copy_profile_point_attribute_to_mesh(const GSpan src, + const ResultInfo &info, + ResultAttributeData &dst) +{ + GVArrayPtr interpolated_gvarray = info.profile.interpolate_to_evaluated(src); + GSpan interpolated = interpolated_gvarray->get_internal_span(); + + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst.domain) { + case ATTR_DOMAIN_POINT: + copy_profile_point_data_to_mesh_verts(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_EDGE: + copy_profile_point_data_to_mesh_edges(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_FACE: + copy_profile_point_data_to_mesh_faces(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_CORNER: + /* Unsupported for now, since there are no builtin attributes to convert into. */ + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +static void copy_point_domain_attributes_to_mesh(const ResultInfo &info, + ResultAttributes &attributes) +{ + if (!attributes.curve_point_attributes.is_empty()) { + int i = 0; + info.spline.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.curve_point_attributes[i]) { + copy_curve_point_attribute_to_mesh(*info.spline.attributes.get_for_read(id), + info, + *attributes.curve_point_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_POINT); + } + if (!attributes.profile_point_attributes.is_empty()) { + int i = 0; + info.profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.profile_point_attributes[i]) { + copy_profile_point_attribute_to_mesh(*info.profile.attributes.get_for_read(id), + info, + *attributes.profile_point_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_POINT); + } +} + +template +static void copy_spline_data_to_mesh(Span src, Span offsets, MutableSpan dst) +{ + for (const int i : IndexRange(src.size())) { + dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); + } +} + +/** + * Since the offsets for each combination of curve and profile spline are stored for every mesh + * domain, and this just needs to fill the chunks corresponding to each combination, we can use + * the same function for all mesh domains. + */ +static void copy_spline_attribute_to_mesh(const GSpan src, + const ResultOffsets &offsets, + ResultAttributeData &dst_attribute) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst_attribute.domain) { + case ATTR_DOMAIN_POINT: + copy_spline_data_to_mesh(src.typed(), offsets.vert, dst_attribute.data.typed()); + break; + case ATTR_DOMAIN_EDGE: + copy_spline_data_to_mesh(src.typed(), offsets.edge, dst_attribute.data.typed()); + break; + case ATTR_DOMAIN_FACE: + copy_spline_data_to_mesh(src.typed(), offsets.poly, dst_attribute.data.typed()); + break; + case ATTR_DOMAIN_CORNER: + copy_spline_data_to_mesh(src.typed(), offsets.loop, dst_attribute.data.typed()); + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +static void copy_spline_domain_attributes_to_mesh(const CurveEval &curve, + const CurveEval &profile, + const ResultOffsets &offsets, + ResultAttributes &attributes) +{ + if (!attributes.curve_spline_attributes.is_empty()) { + int i = 0; + curve.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.curve_spline_attributes[i]) { + copy_spline_attribute_to_mesh(*curve.attributes.get_for_read(id), + offsets, + *attributes.curve_spline_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_CURVE); + } + if (!attributes.profile_spline_attributes.is_empty()) { + int i = 0; + profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.profile_spline_attributes[i]) { + copy_spline_attribute_to_mesh(*profile.attributes.get_for_read(id), + offsets, + *attributes.profile_spline_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_CURVE); + } +} + +/** + * Extrude all splines in the profile curve along the path of every spline in the curve input. + * Transfer curve attributes to the mesh. + * + * \note Normal calculation is by far the slowest part of calculations relating to the result mesh. + * Although it would be a sensible decision to use the better topology information available while + * generating the mesh to also generate the normals, that work may wasted if the output mesh is + * changed anyway in a way that affects the normals. So currently this code uses the safer / + * simpler solution of deferring normal calculation to the rest of Blender. + */ +Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile) +{ + Span profiles = profile.splines(); + Span curves = curve.splines(); + + const ResultOffsets offsets = calculate_result_offsets(profiles, curves); + if (offsets.vert.last() == 0) { + return nullptr; + } + + Mesh *mesh = BKE_mesh_new_nomain( + offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), offsets.poly.last()); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + mesh->flag |= ME_AUTOSMOOTH; + mesh->smoothresh = DEG2RADF(180.0f); + BKE_mesh_normals_tag_dirty(mesh); + + ResultAttributes attributes = create_result_attributes(curve, profile, *mesh); + + threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) { + for (const int i_spline : curves_range) { + const Spline &spline = *curves[i_spline]; + if (spline.evaluated_points_size() == 0) { + continue; + } + const int spline_start_index = i_spline * profiles.size(); + threading::parallel_for(profiles.index_range(), 128, [&](IndexRange profiles_range) { + for (const int i_profile : profiles_range) { + const Spline &profile = *profiles[i_profile]; + const int i_mesh = spline_start_index + i_profile; + ResultInfo info{ + spline, + profile, + offsets.vert[i_mesh], + offsets.edge[i_mesh], + offsets.loop[i_mesh], + offsets.poly[i_mesh], + spline.evaluated_points_size(), + spline.evaluated_edges_size(), + profile.evaluated_points_size(), + profile.evaluated_edges_size(), + }; + + spline_extrude_to_mesh_data(info, + {mesh->mvert, mesh->totvert}, + {mesh->medge, mesh->totedge}, + {mesh->mloop, mesh->totloop}, + {mesh->mpoly, mesh->totpoly}); + + copy_point_domain_attributes_to_mesh(info, attributes); + } + }); + } + }); + + copy_spline_domain_attributes_to_mesh(curve, profile, offsets, attributes); + + for (OutputAttribute &output_attribute : attributes.attributes) { + output_attribute.save(); + } + + return mesh; +} + +static CurveEval get_curve_single_vert() +{ + CurveEval curve; + std::unique_ptr spline = std::make_unique(); + spline->add_point(float3(0), 0, 0.0f); + curve.add_spline(std::move(spline)); + + return curve; +} + +/** + * Create a loose-edge mesh based on the evaluated path of the curve's splines. + * Transfer curve attributes to the mesh. + */ +Mesh *curve_to_wire_mesh(const CurveEval &curve) +{ + static const CurveEval vert_curve = get_curve_single_vert(); + return curve_to_mesh_sweep(curve, vert_curve); +} + +} // namespace blender::bke diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index b8bdb3d71d6..89ba635ff4b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -14,17 +14,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BLI_array.hh" -#include "BLI_float4x4.hh" -#include "BLI_task.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_material.h" -#include "BKE_mesh.h" #include "BKE_spline.hh" +#include "BKE_curve_to_mesh.hh" + #include "UI_interface.h" #include "UI_resources.h" @@ -39,692 +32,6 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) b.add_output("Mesh"); } -/** Information about the creation of one curve spline and profile spline combination. */ -struct ResultInfo { - const Spline &spline; - const Spline &profile; - int vert_offset; - int edge_offset; - int loop_offset; - int poly_offset; - int spline_vert_len; - int spline_edge_len; - int profile_vert_len; - int profile_edge_len; -}; - -static void vert_extrude_to_mesh_data(const Spline &spline, - const float3 profile_vert, - MutableSpan r_verts, - MutableSpan r_edges, - const int vert_offset, - const int edge_offset) -{ - Span positions = spline.evaluated_positions(); - - for (const int i : IndexRange(positions.size() - 1)) { - MEdge &edge = r_edges[edge_offset + i]; - edge.v1 = vert_offset + i; - edge.v2 = vert_offset + i + 1; - edge.flag = ME_LOOSEEDGE; - } - - if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) { - MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1]; - edge.v1 = vert_offset; - edge.v2 = vert_offset + positions.size() - 1; - edge.flag = ME_LOOSEEDGE; - } - - for (const int i : positions.index_range()) { - MVert &vert = r_verts[vert_offset + i]; - copy_v3_v3(vert.co, positions[i] + profile_vert); - } -} - -static void mark_edges_sharp(MutableSpan edges) -{ - for (MEdge &edge : edges) { - edge.flag |= ME_SHARP; - } -} - -static void spline_extrude_to_mesh_data(const ResultInfo &info, - MutableSpan r_verts, - MutableSpan r_edges, - MutableSpan r_loops, - MutableSpan r_polys) -{ - const Spline &spline = info.spline; - const Spline &profile = info.profile; - if (info.profile_vert_len == 1) { - vert_extrude_to_mesh_data(spline, - profile.evaluated_positions()[0], - r_verts, - r_edges, - info.vert_offset, - info.edge_offset); - return; - } - - /* Add the edges running along the length of the curve, starting at each profile vertex. */ - const int spline_edges_start = info.edge_offset; - for (const int i_profile : IndexRange(info.profile_vert_len)) { - const int profile_edge_offset = spline_edges_start + i_profile * info.spline_edge_len; - for (const int i_ring : IndexRange(info.spline_edge_len)) { - const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1; - - const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; - const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; - - MEdge &edge = r_edges[profile_edge_offset + i_ring]; - edge.v1 = ring_vert_offset + i_profile; - edge.v2 = next_ring_vert_offset + i_profile; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - } - - /* Add the edges running along each profile ring. */ - const int profile_edges_start = spline_edges_start + - info.profile_vert_len * info.spline_edge_len; - for (const int i_ring : IndexRange(info.spline_vert_len)) { - const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; - - const int ring_edge_offset = profile_edges_start + i_ring * info.profile_edge_len; - for (const int i_profile : IndexRange(info.profile_edge_len)) { - const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; - - MEdge &edge = r_edges[ring_edge_offset + i_profile]; - edge.v1 = ring_vert_offset + i_profile; - edge.v2 = ring_vert_offset + i_next_profile; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - } - - /* Calculate poly and corner indices. */ - for (const int i_ring : IndexRange(info.spline_edge_len)) { - const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1; - - const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; - const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; - - const int ring_edge_start = profile_edges_start + info.profile_edge_len * i_ring; - const int next_ring_edge_offset = profile_edges_start + info.profile_edge_len * i_next_ring; - - const int ring_poly_offset = info.poly_offset + i_ring * info.profile_edge_len; - const int ring_loop_offset = info.loop_offset + i_ring * info.profile_edge_len * 4; - - for (const int i_profile : IndexRange(info.profile_edge_len)) { - const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4; - const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; - - const int spline_edge_start = spline_edges_start + info.spline_edge_len * i_profile; - const int next_spline_edge_start = spline_edges_start + - info.spline_edge_len * i_next_profile; - - MPoly &poly = r_polys[ring_poly_offset + i_profile]; - poly.loopstart = ring_segment_loop_offset; - poly.totloop = 4; - poly.flag = ME_SMOOTH; - - MLoop &loop_a = r_loops[ring_segment_loop_offset]; - loop_a.v = ring_vert_offset + i_profile; - loop_a.e = ring_edge_start + i_profile; - MLoop &loop_b = r_loops[ring_segment_loop_offset + 1]; - loop_b.v = ring_vert_offset + i_next_profile; - loop_b.e = next_spline_edge_start + i_ring; - MLoop &loop_c = r_loops[ring_segment_loop_offset + 2]; - loop_c.v = next_ring_vert_offset + i_next_profile; - loop_c.e = next_ring_edge_offset + i_profile; - MLoop &loop_d = r_loops[ring_segment_loop_offset + 3]; - loop_d.v = next_ring_vert_offset + i_profile; - loop_d.e = spline_edge_start + i_ring; - } - } - - /* Calculate the positions of each profile ring profile along the spline. */ - Span positions = spline.evaluated_positions(); - Span tangents = spline.evaluated_tangents(); - Span normals = spline.evaluated_normals(); - Span profile_positions = profile.evaluated_positions(); - - GVArray_Typed radii = spline.interpolate_to_evaluated(spline.radii()); - for (const int i_ring : IndexRange(info.spline_vert_len)) { - float4x4 point_matrix = float4x4::from_normalized_axis_data( - positions[i_ring], normals[i_ring], tangents[i_ring]); - point_matrix.apply_scale(radii[i_ring]); - - const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; - for (const int i_profile : IndexRange(info.profile_vert_len)) { - MVert &vert = r_verts[ring_vert_start + i_profile]; - copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]); - } - } - - /* Mark edge loops from sharp vector control points sharp. */ - if (profile.type() == Spline::Type::Bezier) { - const BezierSpline &bezier_spline = static_cast(profile); - Span control_point_offsets = bezier_spline.control_point_offsets(); - for (const int i : IndexRange(bezier_spline.size())) { - if (bezier_spline.point_is_sharp(i)) { - mark_edges_sharp( - r_edges.slice(spline_edges_start + info.spline_edge_len * control_point_offsets[i], - info.spline_edge_len)); - } - } - } -} - -static inline int spline_extrude_vert_size(const Spline &curve, const Spline &profile) -{ - return curve.evaluated_points_size() * profile.evaluated_points_size(); -} - -static inline int spline_extrude_edge_size(const Spline &curve, const Spline &profile) -{ - /* Add the ring edges, with one ring for every curve vertex, and the edge loops - * that run along the length of the curve, starting on the first profile. */ - return curve.evaluated_points_size() * profile.evaluated_edges_size() + - curve.evaluated_edges_size() * profile.evaluated_points_size(); -} - -static inline int spline_extrude_loop_size(const Spline &curve, const Spline &profile) -{ - return curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; -} - -static inline int spline_extrude_poly_size(const Spline &curve, const Spline &profile) -{ - return curve.evaluated_edges_size() * profile.evaluated_edges_size(); -} - -struct ResultOffsets { - Array vert; - Array edge; - Array loop; - Array poly; -}; -static ResultOffsets calculate_result_offsets(Span profiles, Span curves) -{ - const int total = profiles.size() * curves.size(); - Array vert(total + 1); - Array edge(total + 1); - Array loop(total + 1); - Array poly(total + 1); - - int mesh_index = 0; - int vert_offset = 0; - int edge_offset = 0; - int loop_offset = 0; - int poly_offset = 0; - for (const int i_spline : curves.index_range()) { - for (const int i_profile : profiles.index_range()) { - vert[mesh_index] = vert_offset; - edge[mesh_index] = edge_offset; - loop[mesh_index] = loop_offset; - poly[mesh_index] = poly_offset; - vert_offset += spline_extrude_vert_size(*curves[i_spline], *profiles[i_profile]); - edge_offset += spline_extrude_edge_size(*curves[i_spline], *profiles[i_profile]); - loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile]); - poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile]); - mesh_index++; - } - } - vert.last() = vert_offset; - edge.last() = edge_offset; - loop.last() = loop_offset; - poly.last() = poly_offset; - - return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)}; -} - -static AttributeDomain get_result_attribute_domain(const MeshComponent &component, - const AttributeIDRef &attribute_id) -{ - /* Only use a different domain if it is builtin and must only exist on one domain. */ - if (!component.attribute_is_builtin(attribute_id)) { - return ATTR_DOMAIN_POINT; - } - - std::optional meta_data = component.attribute_get_meta_data(attribute_id); - if (!meta_data) { - /* This function has to return something in this case, but it shouldn't be used, - * so return an output that will assert later if the code attempts to handle it. */ - return ATTR_DOMAIN_AUTO; - } - - return meta_data->domain; -} - -/** - * The data stored in the attribute and its domain from #OutputAttribute, to avoid calling - * `as_span()` for every single profile and curve spline combination, and for readability. - */ -struct ResultAttributeData { - GMutableSpan data; - AttributeDomain domain; -}; - -static std::optional create_attribute_and_get_span( - MeshComponent &component, - const AttributeIDRef &attribute_id, - AttributeMetaData meta_data, - Vector &r_attributes) -{ - const AttributeDomain domain = get_result_attribute_domain(component, attribute_id); - OutputAttribute attribute = component.attribute_try_get_for_output_only( - attribute_id, domain, meta_data.data_type); - if (!attribute) { - return std::nullopt; - } - - GMutableSpan span = attribute.as_span(); - r_attributes.append(std::move(attribute)); - return std::make_optional({span, domain}); -} - -/** - * Store the references to the attribute data from the curve and profile inputs. Here we rely on - * the invariants of the storage of curve attributes, that the order will be consistent between - * splines, and all splines will have the same attributes. - */ -struct ResultAttributes { - /** - * Result attributes on the mesh corresponding to each attribute on the curve input, in the same - * order. The data is optional only in case the attribute does not exist on the mesh for some - * reason, like "shade_smooth" when the result has no faces. - */ - Vector> curve_point_attributes; - Vector> curve_spline_attributes; - - /** - * Result attributes corresponding the attributes on the profile input, in the same order. The - * attributes are optional in case the attribute names correspond to a names used by the curve - * input, in which case the curve input attributes take precedence. - */ - Vector> profile_point_attributes; - Vector> profile_spline_attributes; - - /** - * Because some builtin attributes are not stored contiguously, and the curve inputs might have - * attributes with those names, it's necessary to keep OutputAttributes around to give access to - * the result data in a contiguous array. - */ - Vector attributes; -}; -static ResultAttributes create_result_attributes(const CurveEval &curve, - const CurveEval &profile, - Mesh &mesh) -{ - MeshComponent mesh_component; - mesh_component.replace(&mesh, GeometryOwnershipType::Editable); - Set curve_attributes; - - /* In order to prefer attributes on the main curve input when there are name collisions, first - * check the attributes on the curve, then add attributes on the profile that are not also on the - * main curve input. */ - ResultAttributes result; - curve.splines().first()->attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - curve_attributes.add_new(id); - result.curve_point_attributes.append( - create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); - return true; - }, - ATTR_DOMAIN_POINT); - curve.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - curve_attributes.add_new(id); - result.curve_spline_attributes.append( - create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); - return true; - }, - ATTR_DOMAIN_CURVE); - profile.splines().first()->attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - if (curve_attributes.contains(id)) { - result.profile_point_attributes.append({}); - } - else { - result.profile_point_attributes.append( - create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); - } - return true; - }, - ATTR_DOMAIN_POINT); - profile.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - if (curve_attributes.contains(id)) { - result.profile_spline_attributes.append({}); - } - else { - result.profile_spline_attributes.append( - create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); - } - return true; - }, - ATTR_DOMAIN_CURVE); - - return result; -} - -template -static void copy_curve_point_data_to_mesh_verts(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - for (const int i_ring : IndexRange(info.spline_vert_len)) { - const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; - dst.slice(ring_vert_start, info.profile_vert_len).fill(src[i_ring]); - } -} - -template -static void copy_curve_point_data_to_mesh_edges(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - const int edges_start = info.edge_offset + info.profile_vert_len * info.spline_edge_len; - for (const int i_ring : IndexRange(info.spline_vert_len)) { - const int ring_edge_start = edges_start + info.profile_edge_len * i_ring; - dst.slice(ring_edge_start, info.profile_edge_len).fill(src[i_ring]); - } -} - -template -static void copy_curve_point_data_to_mesh_faces(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - for (const int i_ring : IndexRange(info.spline_edge_len)) { - const int ring_face_start = info.poly_offset + info.profile_edge_len * i_ring; - dst.slice(ring_face_start, info.profile_edge_len).fill(src[i_ring]); - } -} - -static void copy_curve_point_attribute_to_mesh(const GSpan src, - const ResultInfo &info, - ResultAttributeData &dst) -{ - GVArrayPtr interpolated_gvarray = info.spline.interpolate_to_evaluated(src); - GSpan interpolated = interpolated_gvarray->get_internal_span(); - - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - switch (dst.domain) { - case ATTR_DOMAIN_POINT: - copy_curve_point_data_to_mesh_verts(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_EDGE: - copy_curve_point_data_to_mesh_edges(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_FACE: - copy_curve_point_data_to_mesh_faces(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_CORNER: - /* Unsupported for now, since there are no builtin attributes to convert into. */ - break; - default: - BLI_assert_unreachable(); - break; - } - }); -} - -template -static void copy_profile_point_data_to_mesh_verts(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - for (const int i_ring : IndexRange(info.spline_vert_len)) { - const int profile_vert_start = info.vert_offset + i_ring * info.profile_vert_len; - for (const int i_profile : IndexRange(info.profile_vert_len)) { - dst[profile_vert_start + i_profile] = src[i_profile]; - } - } -} - -template -static void copy_profile_point_data_to_mesh_edges(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - for (const int i_profile : IndexRange(info.profile_vert_len)) { - const int profile_edge_offset = info.edge_offset + i_profile * info.spline_edge_len; - dst.slice(profile_edge_offset, info.spline_edge_len).fill(src[i_profile]); - } -} - -template -static void copy_profile_point_data_to_mesh_faces(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - for (const int i_ring : IndexRange(info.spline_edge_len)) { - const int profile_face_start = info.poly_offset + i_ring * info.profile_edge_len; - for (const int i_profile : IndexRange(info.profile_edge_len)) { - dst[profile_face_start + i_profile] = src[i_profile]; - } - } -} - -static void copy_profile_point_attribute_to_mesh(const GSpan src, - const ResultInfo &info, - ResultAttributeData &dst) -{ - GVArrayPtr interpolated_gvarray = info.profile.interpolate_to_evaluated(src); - GSpan interpolated = interpolated_gvarray->get_internal_span(); - - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - switch (dst.domain) { - case ATTR_DOMAIN_POINT: - copy_profile_point_data_to_mesh_verts(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_EDGE: - copy_profile_point_data_to_mesh_edges(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_FACE: - copy_profile_point_data_to_mesh_faces(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_CORNER: - /* Unsupported for now, since there are no builtin attributes to convert into. */ - break; - default: - BLI_assert_unreachable(); - break; - } - }); -} - -static void copy_point_domain_attributes_to_mesh(const ResultInfo &info, - ResultAttributes &attributes) -{ - if (!attributes.curve_point_attributes.is_empty()) { - int i = 0; - info.spline.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { - if (attributes.curve_point_attributes[i]) { - copy_curve_point_attribute_to_mesh(*info.spline.attributes.get_for_read(id), - info, - *attributes.curve_point_attributes[i]); - } - i++; - return true; - }, - ATTR_DOMAIN_POINT); - } - if (!attributes.profile_point_attributes.is_empty()) { - int i = 0; - info.profile.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { - if (attributes.profile_point_attributes[i]) { - copy_profile_point_attribute_to_mesh(*info.profile.attributes.get_for_read(id), - info, - *attributes.profile_point_attributes[i]); - } - i++; - return true; - }, - ATTR_DOMAIN_POINT); - } -} - -template -static void copy_spline_data_to_mesh(Span src, Span offsets, MutableSpan dst) -{ - for (const int i : IndexRange(src.size())) { - dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); - } -} - -/** - * Since the offsets for each combination of curve and profile spline are stored for every mesh - * domain, and this just needs to fill the chunks corresponding to each combination, we can use - * the same function for all mesh domains. - */ -static void copy_spline_attribute_to_mesh(const GSpan src, - const ResultOffsets &offsets, - ResultAttributeData &dst_attribute) -{ - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - switch (dst_attribute.domain) { - case ATTR_DOMAIN_POINT: - copy_spline_data_to_mesh(src.typed(), offsets.vert, dst_attribute.data.typed()); - break; - case ATTR_DOMAIN_EDGE: - copy_spline_data_to_mesh(src.typed(), offsets.edge, dst_attribute.data.typed()); - break; - case ATTR_DOMAIN_FACE: - copy_spline_data_to_mesh(src.typed(), offsets.poly, dst_attribute.data.typed()); - break; - case ATTR_DOMAIN_CORNER: - copy_spline_data_to_mesh(src.typed(), offsets.loop, dst_attribute.data.typed()); - break; - default: - BLI_assert_unreachable(); - break; - } - }); -} - -static void copy_spline_domain_attributes_to_mesh(const CurveEval &curve, - const CurveEval &profile, - const ResultOffsets &offsets, - ResultAttributes &attributes) -{ - if (!attributes.curve_spline_attributes.is_empty()) { - int i = 0; - curve.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { - if (attributes.curve_spline_attributes[i]) { - copy_spline_attribute_to_mesh(*curve.attributes.get_for_read(id), - offsets, - *attributes.curve_spline_attributes[i]); - } - i++; - return true; - }, - ATTR_DOMAIN_CURVE); - } - if (!attributes.profile_spline_attributes.is_empty()) { - int i = 0; - profile.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { - if (attributes.profile_spline_attributes[i]) { - copy_spline_attribute_to_mesh(*profile.attributes.get_for_read(id), - offsets, - *attributes.profile_spline_attributes[i]); - } - i++; - return true; - }, - ATTR_DOMAIN_CURVE); - } -} - -/** - * \note Normal calculation is by far the slowest part of calculations relating to the result mesh. - * Although it would be a sensible decision to use the better topology information available while - * generating the mesh to also generate the normals, that work may wasted if the output mesh is - * changed anyway in a way that affects the normals. So currently this code uses the safer / - * simpler solution of deferring normal calculation to the rest of Blender. - */ -static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile) -{ - Span profiles = profile.splines(); - Span curves = curve.splines(); - - const ResultOffsets offsets = calculate_result_offsets(profiles, curves); - if (offsets.vert.last() == 0) { - return nullptr; - } - - Mesh *mesh = BKE_mesh_new_nomain( - offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), offsets.poly.last()); - BKE_id_material_eval_ensure_default_slot(&mesh->id); - mesh->flag |= ME_AUTOSMOOTH; - mesh->smoothresh = DEG2RADF(180.0f); - BKE_mesh_normals_tag_dirty(mesh); - - ResultAttributes attributes = create_result_attributes(curve, profile, *mesh); - - threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) { - for (const int i_spline : curves_range) { - const Spline &spline = *curves[i_spline]; - if (spline.evaluated_points_size() == 0) { - continue; - } - const int spline_start_index = i_spline * profiles.size(); - threading::parallel_for(profiles.index_range(), 128, [&](IndexRange profiles_range) { - for (const int i_profile : profiles_range) { - const Spline &profile = *profiles[i_profile]; - const int i_mesh = spline_start_index + i_profile; - ResultInfo info{ - spline, - profile, - offsets.vert[i_mesh], - offsets.edge[i_mesh], - offsets.loop[i_mesh], - offsets.poly[i_mesh], - spline.evaluated_points_size(), - spline.evaluated_edges_size(), - profile.evaluated_points_size(), - profile.evaluated_edges_size(), - }; - - spline_extrude_to_mesh_data(info, - {mesh->mvert, mesh->totvert}, - {mesh->medge, mesh->totedge}, - {mesh->mloop, mesh->totloop}, - {mesh->mpoly, mesh->totpoly}); - - copy_point_domain_attributes_to_mesh(info, attributes); - } - }); - } - }); - - copy_spline_domain_attributes_to_mesh(curve, profile, offsets, attributes); - - for (OutputAttribute &output_attribute : attributes.attributes) { - output_attribute.save(); - } - - return mesh; -} - -static CurveEval get_curve_single_vert() -{ - CurveEval curve; - std::unique_ptr spline = std::make_unique(); - spline->add_point(float3(0), 0, 0.0f); - curve.add_spline(std::move(spline)); - - return curve; -} - static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input("Curve"); @@ -750,11 +57,14 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) const CurveEval *profile_curve = profile_set.get_curve_for_read(); - static const CurveEval vert_curve = get_curve_single_vert(); - - Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(), - (profile_curve == nullptr) ? vert_curve : *profile_curve); - params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); + if (profile_curve == nullptr) { + Mesh *mesh = bke::curve_to_wire_mesh(*curve_set.get_curve_for_read()); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); + } + else { + Mesh *mesh = bke::curve_to_mesh_sweep(*curve_set.get_curve_for_read(), *profile_curve); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); + } } } // namespace blender::nodes From b37d36a60f9f60d8beb34142b4768f7bcd7bf987 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 21 Sep 2021 12:42:47 -0500 Subject: [PATCH 0064/1500] Fix: Add versioning for geometry nodes attribute input toggle rB8e21d528cab98 neglected to add versioning to add the new "use_attribute" and "attribute_name" properties to the modifier input list. Though they are added if the modifier's interface is updated, that doesn't happen when the file is loaded, so patch adds them manually. Another solution would be calling `MOD_nodes_update_interface`, but that would require including the modifiers module. Differential Revision: https://developer.blender.org/D12535 --- .../blenloader/intern/versioning_300.c | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 791b5d52d97..58265bca238 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -808,6 +808,47 @@ static void do_version_subsurface_methods(bNode *node) } } +static void version_geometry_nodes_add_attribute_input_settings(NodesModifierData *nmd) +{ + /* Before versioning the properties, make sure it hasn't been done already. */ + LISTBASE_FOREACH (const IDProperty *, property, &nmd->settings.properties->data.group) { + if (strstr(property->name, "_use_attribute") || strstr(property->name, "_attribute_name")) { + return; + } + } + + LISTBASE_FOREACH_MUTABLE (IDProperty *, property, &nmd->settings.properties->data.group) { + if (!ELEM(property->type, IDP_FLOAT, IDP_INT, IDP_ARRAY)) { + continue; + } + + if (strstr(property->name, "_use_attribute") || strstr(property->name, "_attribute_name")) { + continue; + } + + char use_attribute_prop_name[MAX_IDPROP_NAME]; + BLI_snprintf(use_attribute_prop_name, + sizeof(use_attribute_prop_name), + "%s%s", + property->name, + "_use_attribute"); + + IDPropertyTemplate idprop = {0}; + IDProperty *use_attribute_prop = IDP_New(IDP_INT, &idprop, use_attribute_prop_name); + IDP_AddToGroup(nmd->settings.properties, use_attribute_prop); + + char attribute_name_prop_name[MAX_IDPROP_NAME]; + BLI_snprintf(attribute_name_prop_name, + sizeof(attribute_name_prop_name), + "%s%s", + property->name, + "_attribute_name"); + + IDProperty *attribute_prop = IDP_New(IDP_STRING, &idprop, attribute_name_prop_name); + IDP_AddToGroup(nmd->settings.properties, attribute_prop); + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -1390,5 +1431,12 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Nodes) { + version_geometry_nodes_add_attribute_input_settings((NodesModifierData *)md); + } + } + } } } From 499dbb626acb816de1dcbca10fdb2d8ca27dbcc9 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Tue, 21 Sep 2021 18:33:14 +0200 Subject: [PATCH 0065/1500] UI: Style drag-drop indicators as tooltips Currently, the drop indicator colors are hardcoded to white text on semi-transparent black background. This patch makes the drop indicator use the tooltip theme settings, as they serve a similar purpose. {F10530482, size=full} All built-in themes seem to work well and got improved readability. Reviewed By: HooglyBoogly Differential Revision: https://developer.blender.org/D12588 --- .../blender/editors/interface/interface_eyedropper.c | 11 +++++++++-- source/blender/editors/interface/interface_style.c | 7 ++----- source/blender/windowmanager/intern/wm_dragdrop.c | 12 ++++++++++-- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index 2e7b0ce532c..58a9f362488 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -24,6 +24,8 @@ #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "BLI_math_color.h" + #include "BKE_context.h" #include "BKE_screen.h" @@ -107,8 +109,13 @@ static void eyedropper_draw_cursor_text_ex(const int x, const int y, const char { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + /* Use the theme settings from tooltips. */ + const bTheme *btheme = UI_GetTheme(); + const uiWidgetColors *wcol = &btheme->tui.wcol_tooltip; + + float col_fg[4], col_bg[4]; + rgba_uchar_to_float(col_fg, wcol->text); + rgba_uchar_to_float(col_bg, wcol->inner); UI_fontstyle_draw_simple_backdrop(fstyle, x, y + U.widget_unit, name, col_fg, col_bg); } diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 804156ba48c..6b1ff92a855 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -312,11 +312,8 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs, const float decent = BLF_descender(fs->uifont_id); const float margin = height / 4.0f; - /* backdrop */ - const float color[4] = {col_bg[0], col_bg[1], col_bg[2], 0.5f}; - UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa( + UI_draw_roundbox_4fv( &(const rctf){ .xmin = x - margin, .xmax = x + width + margin, @@ -325,7 +322,7 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs, }, true, margin, - color); + col_bg); } BLF_position(fs->uifont_id, x, y, 0.0f); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 76bb93b681c..b6a04251ffb 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -34,6 +34,7 @@ #include "BLT_translation.h" #include "BLI_blenlib.h" +#include "BLI_math_color.h" #include "BIF_glutil.h" @@ -50,6 +51,7 @@ #include "UI_interface.h" #include "UI_interface_icons.h" +#include "UI_resources.h" #include "RNA_access.h" @@ -463,8 +465,14 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * static void wm_drop_operator_draw(const char *name, int x, int y) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + + /* Use the theme settings from tooltips. */ + const bTheme *btheme = UI_GetTheme(); + const uiWidgetColors *wcol = &btheme->tui.wcol_tooltip; + + float col_fg[4], col_bg[4]; + rgba_uchar_to_float(col_fg, wcol->text); + rgba_uchar_to_float(col_bg, wcol->inner); UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg); } From 29b13fa3cae61bff3707d6a3d844188f21dda06c Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 21 Sep 2021 20:01:16 +0200 Subject: [PATCH 0066/1500] Cleanup: Clang-tidy warnings --- .../editors/space_sequencer/sequencer_draw.c | 26 +++++++++++-------- source/blender/sequencer/SEQ_render.h | 4 +-- source/blender/sequencer/intern/render.c | 4 +-- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index a6a83b0c3bc..6edf2413e87 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1393,7 +1393,7 @@ static float seq_thumbnail_get_start_frame(Sequence *seq, float frame_step, rctf return ((no_invisible_thumbs - 1) * frame_step) + seq->start; } -static void thumbnail_start_job(void *data, short *stop, short *do_update, float *progress) +static void thumbnail_start_job(void *data, const short *stop, const short *do_update, const float *progress) { ThumbnailDrawJob *tj = data; float start_frame, frame_step; @@ -1627,24 +1627,28 @@ static ImBuf *sequencer_thumbnail_closest_from_memory(const SeqRenderData *conte int frame_guaranteed = sequencer_thumbnail_closest_guaranteed_frame_get(seq, timeline_frame); ImBuf *ibuf_guaranteed = SEQ_get_thumbnail(context, seq, frame_guaranteed, crop, clipped); - if (ibuf_previous && ibuf_guaranteed && - abs(frame_previous - timeline_frame) < abs(frame_guaranteed - timeline_frame)) { + ImBuf *closest_in_memory = NULL; - IMB_freeImBuf(ibuf_guaranteed); - return ibuf_previous; - } - else { - IMB_freeImBuf(ibuf_previous); - return ibuf_guaranteed; + if (ibuf_previous && ibuf_guaranteed) { + if (abs(frame_previous - timeline_frame) < abs(frame_guaranteed - timeline_frame)) { + IMB_freeImBuf(ibuf_guaranteed); + closest_in_memory = ibuf_previous; + } + else { + IMB_freeImBuf(ibuf_previous); + closest_in_memory = ibuf_guaranteed; + } } if (ibuf_previous == NULL) { - return ibuf_guaranteed; + closest_in_memory = ibuf_guaranteed; } if (ibuf_guaranteed == NULL) { - return ibuf_previous; + closest_in_memory = ibuf_previous; } + + return closest_in_memory; } static void draw_seq_strip_thumbnail(View2D *v2d, diff --git a/source/blender/sequencer/SEQ_render.h b/source/blender/sequencer/SEQ_render.h index 50280db55ff..e99dc6d344f 100644 --- a/source/blender/sequencer/SEQ_render.h +++ b/source/blender/sequencer/SEQ_render.h @@ -75,7 +75,7 @@ void SEQ_render_thumbnails(const struct SeqRenderData *context, float start_frame, float frame_step, rctf *view_area, - short *stop); + const short *stop); struct ImBuf *SEQ_get_thumbnail(const struct SeqRenderData *context, struct Sequence *seq, float timeline_frame, @@ -86,7 +86,7 @@ void SEQ_render_thumbnails_base_set(const struct SeqRenderData *context, struct Sequence *seq, struct Sequence *seq_orig, rctf *view_area, - short *stop); + const short *stop); void SEQ_render_init_colorspace(struct Sequence *seq); void SEQ_render_new_render_data(struct Main *bmain, diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index b2642228c91..2578a6d4223 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1990,7 +1990,7 @@ void SEQ_render_thumbnails(const SeqRenderData *context, float start_frame, float frame_step, rctf *view_area, - short *stop) + const short *stop) { SeqRenderState state; seq_render_state_init(&state); @@ -2044,7 +2044,7 @@ int SEQ_render_thumbnails_guaranteed_set_frame_step_get(const Sequence *seq) /* Render set of evenly spaced thumbnails that are drawn when zooming. */ void SEQ_render_thumbnails_base_set( - const SeqRenderData *context, Sequence *seq, Sequence *seq_orig, rctf *view_area, short *stop) + const SeqRenderData *context, Sequence *seq, Sequence *seq_orig, rctf *view_area, const short *stop) { SeqRenderState state; seq_render_state_init(&state); From 4d51af68adb273a9e63cba196a71a404b07be95d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 21 Sep 2021 13:11:39 -0500 Subject: [PATCH 0067/1500] Geometry Nodes: Curve tangent node This node outputs the direction vector, or tangent of a curve at every control point. For poly splines this is simply the evaluated tangents, so it all works very simply. For Bezier splines it uses the tangent at the evaluated point corresponding to each control point, and NURBS are interpereted as poly splines built from their control points. Internally the node is called "Input Tangent" to simplify using it for mesh tangents as well in the future like the "Normal" node. Differential Revision: https://developer.blender.org/D12581 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../geometry/nodes/node_geo_input_tangent.cc | 174 ++++++++++++++++++ 7 files changed, 180 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index b8bb4e551d2..ea9305caff3 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -527,6 +527,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveFill"), NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), + NodeItem("GeometryNodeInputTangent", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeCurveSample", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index e4e9a6eff3a..28ac27c09c6 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1496,6 +1496,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_REALIZE_INSTANCES 1083 #define GEO_NODE_ATTRIBUTE_STATISTIC 1084 #define GEO_NODE_CURVE_SAMPLE 1085 +#define GEO_NODE_INPUT_TANGENT 1086 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index e2891f00119..cfcd50dc482 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5208,6 +5208,7 @@ static void registerGeometryNodes() register_node_type_geo_input_material(); register_node_type_geo_input_normal(); register_node_type_geo_input_position(); + register_node_type_geo_input_tangent(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); register_node_type_geo_material_assign(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 842c76935d1..c60e68e08f1 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -195,6 +195,7 @@ set(SRC geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_normal.cc geometry/nodes/node_geo_input_position.cc + geometry/nodes/node_geo_input_tangent.cc geometry/nodes/node_geo_input_index.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 63330b7df62..013ec638b82 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -81,6 +81,7 @@ void register_node_type_geo_input_index(void); void register_node_type_geo_input_material(void); void register_node_type_geo_input_normal(void); void register_node_type_geo_input_position(void); +void register_node_type_geo_input_tangent(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); void register_node_type_geo_material_assign(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 918c82dec1c..832e73e1ff1 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -332,6 +332,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") +DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc new file mode 100644 index 00000000000..68788709f1e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -0,0 +1,174 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Tangent"); +} + +static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan tangents) +{ + Span offsets = spline.control_point_offsets(); + Span evaluated_tangents = spline.evaluated_tangents(); + for (const int i : IndexRange(spline.size())) { + tangents[i] = evaluated_tangents[offsets[i]]; + } +} + +static void calculate_poly_tangents(const PolySpline &spline, MutableSpan tangents) +{ + tangents.copy_from(spline.evaluated_tangents()); +} + +/** + * Because NURBS control points are not necessarily on the path, the tangent at the control points + * is not well defined, so create a temporary poly spline to find the tangents. This requires extra + * copying currently, but may be more efficient in the future if attributes have some form of CoW. + */ +static void calculate_nurbs_tangents(const NURBSpline &spline, MutableSpan tangents) +{ + PolySpline poly_spline; + poly_spline.resize(spline.size()); + poly_spline.positions().copy_from(spline.positions()); + tangents.copy_from(poly_spline.evaluated_tangents()); +} + +static Array curve_tangent_point_domain(const CurveEval &curve) +{ + Span splines = curve.splines(); + Array offsets = curve.control_point_offsets(); + const int total_size = offsets.last(); + Array tangents(total_size); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + MutableSpan spline_tangents{tangents.as_mutable_span().slice(offsets[i], spline.size())}; + switch (splines[i]->type()) { + case Spline::Type::Bezier: { + calculate_bezier_tangents(static_cast(spline), spline_tangents); + break; + } + case Spline::Type::Poly: { + calculate_poly_tangents(static_cast(spline), spline_tangents); + break; + } + case Spline::Type::NURBS: { + calculate_nurbs_tangents(static_cast(spline), spline_tangents); + break; + } + } + } + }); + return tangents; +} + +static const GVArray *construct_curve_tangent_gvarray(const CurveComponent &component, + const AttributeDomain domain, + ResourceScope &scope) +{ + const CurveEval *curve = component.get_for_read(); + if (curve == nullptr) { + return nullptr; + } + + if (domain == ATTR_DOMAIN_POINT) { + const Span splines = curve->splines(); + + /* Use a reference to evaluated tangents if possible to avoid an allocation and a copy. + * This is only possible when there is only one poly spline. */ + if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { + const PolySpline &spline = static_cast(*splines.first()); + return &scope.construct>(spline.evaluated_tangents()); + } + + Array tangents = curve_tangent_point_domain(*curve); + return &scope.construct>>(std::move(tangents)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + Array point_tangents = curve_tangent_point_domain(*curve); + GVArrayPtr gvarray = std::make_unique>>( + std::move(point_tangents)); + GVArrayPtr spline_tangents = component.attribute_try_adapt_domain( + std::move(gvarray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + return scope.add_value(std::move(spline_tangents)).get(); + } + + return nullptr; +} + +class TangentFieldInput final : public fn::FieldInput { + public: + TangentFieldInput() : fn::FieldInput(CPPType::get(), "Tangent") + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask UNUSED(mask), + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast(component); + return construct_curve_tangent_gvarray(curve_component, domain, scope); + } + } + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 91827364589; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast(&other) != nullptr; + } +}; + +static void geo_node_input_tangent_exec(GeoNodeExecParams params) +{ + Field tangent_field{std::make_shared()}; + params.set_output("Tangent", std::move(tangent_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_tangent() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_TANGENT, "Curve Tangent", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_tangent_exec; + ntype.declare = blender::nodes::geo_node_input_tangent_declare; + nodeRegisterType(&ntype); +} From 05ce5276db7be4f742b6558d723a989eae4963a3 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 21 Sep 2021 13:39:46 -0500 Subject: [PATCH 0068/1500] Geometry nodes: Output curve normals from the normal node The code is basically the same as rB4d51af68adb273. --- .../geometry/nodes/node_geo_input_normal.cc | 96 ++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc index 07818f2a3ad..f92086acdf0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -14,10 +14,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_task.hh" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BKE_mesh.h" +#include "BKE_spline.hh" #include "node_geometry_util.hh" @@ -147,6 +150,95 @@ static const GVArray *construct_mesh_normals_gvarray(const MeshComponent &mesh_c } } +static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan normals) +{ + Span offsets = spline.control_point_offsets(); + Span evaluated_normals = spline.evaluated_normals(); + for (const int i : IndexRange(spline.size())) { + normals[i] = evaluated_normals[offsets[i]]; + } +} + +static void calculate_poly_normals(const PolySpline &spline, MutableSpan normals) +{ + normals.copy_from(spline.evaluated_normals()); +} + +/** + * Because NURBS control points are not necessarily on the path, the normal at the control points + * is not well defined, so create a temporary poly spline to find the normals. This requires extra + * copying currently, but may be more efficient in the future if attributes have some form of CoW. + */ +static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan normals) +{ + PolySpline poly_spline; + poly_spline.resize(spline.size()); + poly_spline.positions().copy_from(spline.positions()); + normals.copy_from(poly_spline.evaluated_normals()); +} + +static Array curve_normal_point_domain(const CurveEval &curve) +{ + Span splines = curve.splines(); + Array offsets = curve.control_point_offsets(); + const int total_size = offsets.last(); + Array normals(total_size); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())}; + switch (splines[i]->type()) { + case Spline::Type::Bezier: + calculate_bezier_normals(static_cast(spline), spline_normals); + break; + case Spline::Type::Poly: + calculate_poly_normals(static_cast(spline), spline_normals); + break; + case Spline::Type::NURBS: + calculate_nurbs_normals(static_cast(spline), spline_normals); + break; + } + } + }); + return normals; +} + +static const GVArray *construct_curve_normal_gvarray(const CurveComponent &component, + const AttributeDomain domain, + ResourceScope &scope) +{ + const CurveEval *curve = component.get_for_read(); + if (curve == nullptr) { + return nullptr; + } + + if (domain == ATTR_DOMAIN_POINT) { + const Span splines = curve->splines(); + + /* Use a reference to evaluated normals if possible to avoid an allocation and a copy. + * This is only possible when there is only one poly spline. */ + if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { + const PolySpline &spline = static_cast(*splines.first()); + return &scope.construct>(spline.evaluated_normals()); + } + + Array normals = curve_normal_point_domain(*curve); + return &scope.construct>>(std::move(normals)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + Array point_normals = curve_normal_point_domain(*curve); + GVArrayPtr gvarray = std::make_unique>>( + std::move(point_normals)); + GVArrayPtr spline_normals = component.attribute_try_adapt_domain( + std::move(gvarray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + return scope.add_value(std::move(spline_normals)).get(); + } + + return nullptr; +} + class NormalFieldInput final : public fn::FieldInput { public: NormalFieldInput() : fn::FieldInput(CPPType::get(), "Normal") @@ -173,8 +265,8 @@ class NormalFieldInput final : public fn::FieldInput { return construct_mesh_normals_gvarray(mesh_component, *mesh, mask, domain, scope); } if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - /* TODO: Add curve normals support. */ - return nullptr; + const CurveComponent &curve_component = static_cast(component); + return construct_curve_normal_gvarray(curve_component, domain, scope); } } return nullptr; From 29e3545194804428676b0adf881f418a96a40a9a Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Tue, 21 Sep 2021 14:11:32 -0500 Subject: [PATCH 0069/1500] Geometry Nodes: String manipulation nodes This patch adds four new nodes to a new "Text" category: - String Length: Outputs length of a string - String Substring: Outputs part of a string - Value to String: Converts a value to a string - String Join: Concatenates multiple strings with a delimiter The initial use case of these nodes is the upcoming string to curve node. However, they could also be used to calculate dynamic attribute names, or with string attributes in the future. Differential Revision: https://developer.blender.org/D12532 --- release/scripts/startup/nodeitems_builtins.py | 6 +++ source/blender/blenkernel/BKE_node.h | 4 ++ source/blender/blenkernel/intern/node.cc | 4 ++ source/blender/nodes/CMakeLists.txt | 4 ++ source/blender/nodes/NOD_function.h | 3 ++ source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 6 ++- .../function/nodes/node_fn_string_length.cc | 49 +++++++++++++++++ .../nodes/node_fn_string_substring.cc | 54 +++++++++++++++++++ .../function/nodes/node_fn_value_to_string.cc | 51 ++++++++++++++++++ .../geometry/nodes/node_geo_string_join.cc | 53 ++++++++++++++++++ 11 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 source/blender/nodes/function/nodes/node_fn_string_length.cc create mode 100644 source/blender/nodes/function/nodes/node_fn_string_substring.cc create mode 100644 source/blender/nodes/function/nodes/node_fn_value_to_string.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_string_join.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index ea9305caff3..5ec1db0baf8 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -598,6 +598,12 @@ geometry_node_categories = [ NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_fields_legacy_poll), NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_fields_legacy_poll), ]), + GeometryNodeCategory("GEO_TEXT", "Text", items=[ + NodeItem("FunctionNodeStringLength"), + NodeItem("FunctionNodeStringSubstring"), + NodeItem("FunctionNodeValueToString"), + NodeItem("GeometryNodeStringJoin"), + ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), NodeItem("ShaderNodeClamp"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 28ac27c09c6..e242aed879f 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1497,6 +1497,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ATTRIBUTE_STATISTIC 1084 #define GEO_NODE_CURVE_SAMPLE 1085 #define GEO_NODE_INPUT_TANGENT 1086 +#define GEO_NODE_STRING_JOIN 1087 /** \} */ @@ -1510,6 +1511,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_INPUT_VECTOR 1207 #define FN_NODE_INPUT_STRING 1208 #define FN_NODE_FLOAT_TO_INT 1209 +#define FN_NODE_VALUE_TO_STRING 1210 +#define FN_NODE_STRING_LENGTH 1211 +#define FN_NODE_STRING_SUBSTRING 1212 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index cfcd50dc482..7f423e53161 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5235,6 +5235,7 @@ static void registerGeometryNodes() register_node_type_geo_realize_instances(); register_node_type_geo_sample_texture(); register_node_type_geo_select_by_handle_type(); + register_node_type_geo_string_join(); register_node_type_geo_material_selection(); register_node_type_geo_separate_components(); register_node_type_geo_set_position(); @@ -5254,6 +5255,9 @@ static void registerFunctionNodes() register_node_type_fn_input_string(); register_node_type_fn_input_vector(); register_node_type_fn_random_float(); + register_node_type_fn_string_length(); + register_node_type_fn_string_substring(); + register_node_type_fn_value_to_string(); } void BKE_node_system_init(void) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index c60e68e08f1..1f6f1f333f0 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -139,6 +139,9 @@ set(SRC function/nodes/node_fn_input_string.cc function/nodes/node_fn_input_vector.cc function/nodes/node_fn_random_float.cc + function/nodes/node_fn_string_length.cc + function/nodes/node_fn_string_substring.cc + function/nodes/node_fn_value_to_string.cc function/node_function_util.cc geometry/nodes/legacy/node_geo_material_assign.cc @@ -224,6 +227,7 @@ set(SRC geometry/nodes/node_geo_realize_instances.cc geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_set_position.cc + geometry/nodes/node_geo_string_join.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc geometry/nodes/node_geo_transform.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 29f1a465491..a67458418f2 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -26,6 +26,9 @@ void register_node_type_fn_float_to_int(void); void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); void register_node_type_fn_random_float(void); +void register_node_type_fn_string_length(void); +void register_node_type_fn_string_substring(void); +void register_node_type_fn_value_to_string(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 013ec638b82..91d79d9e7b6 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -111,6 +111,7 @@ void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_separate_components(void); void register_node_type_geo_set_position(void); +void register_node_type_geo_string_join(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); void register_node_type_geo_transform(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 832e73e1ff1..908940be93a 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -267,7 +267,10 @@ DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") -DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") +DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") +DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") +DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") +DefNode(FunctionNode, FN_NODE_STRING_SUBSTRING, 0, "STRING_SUBSTRING", StringSubstring, "String Substring", "") DefNode(GeometryNode, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") @@ -351,6 +354,7 @@ DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") +DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "String Join", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc new file mode 100644 index 00000000000..a0f85dfd2bf --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_string_utf8.h" + +#include + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_string_length_declare(NodeDeclarationBuilder &b) +{ + b.add_input("String"); + b.add_output("Length"); +}; + +} // namespace blender::nodes + +static void fn_node_string_length_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + static blender::fn::CustomMF_SI_SO str_len_fn{ + "String Length", [](const std::string &a) { return BLI_strlen_utf8(a.c_str()); }}; + builder.set_matching_fn(&str_len_fn); +} + +void register_node_type_fn_string_length() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_STRING_LENGTH, "String Length", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_string_length_declare; + ntype.build_multi_function = fn_node_string_length_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_string_substring.cc b/source/blender/nodes/function/nodes/node_fn_string_substring.cc new file mode 100644 index 00000000000..55a01093ae9 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_string_substring.cc @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_string_utf8.h" + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_string_substring_declare(NodeDeclarationBuilder &b) +{ + b.add_input("String"); + b.add_input("Position"); + b.add_input("Length").min(0); + b.add_output("String"); +}; + +} // namespace blender::nodes + +static void fn_node_string_substring_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + static blender::fn::CustomMF_SI_SI_SI_SO substring_fn{ + "Substring", [](const std::string &str, int a, int b) { + const int len = BLI_strlen_utf8(str.c_str()); + const int start = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a, 0, len)); + const int end = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a + b, 0, len)); + return str.substr(start, std::max(end - start, 0)); + }}; + builder.set_matching_fn(&substring_fn); +} + +void register_node_type_fn_string_substring() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_STRING_SUBSTRING, "String Substring", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_string_substring_declare; + ntype.build_multi_function = fn_node_string_substring_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc new file mode 100644 index 00000000000..c1e6373cb6d --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc @@ -0,0 +1,51 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" +#include + +namespace blender::nodes { + +static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Value"); + b.add_input("Decimals").min(0); + b.add_output("String"); +}; + +} // namespace blender::nodes + +static void fn_node_value_to_string_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + static blender::fn::CustomMF_SI_SI_SO to_str_fn{ + "Value To String", [](float a, int b) { + std::stringstream stream; + stream << std::fixed << std::setprecision(std::max(0, b)) << a; + return stream.str(); + }}; + builder.set_matching_fn(&to_str_fn); +} + +void register_node_type_fn_value_to_string() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_VALUE_TO_STRING, "Value to String", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_value_to_string_declare; + ntype.build_multi_function = fn_node_value_to_string_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc new file mode 100644 index 00000000000..1e4a4d1f68b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc @@ -0,0 +1,53 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_string_join_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Delimiter"); + b.add_input("Strings").multi_input().hide_value(); + b.add_output("String"); +}; + +static void geo_node_string_join_exec(GeoNodeExecParams params) +{ + Vector strings = params.extract_multi_input("Strings"); + const std::string delim = params.extract_input("Delimiter"); + + std::string output; + for (const int i : strings.index_range()) { + output += strings[i]; + if (i < (strings.size() - 1)) { + output += delim; + } + } + params.set_output("String", std::move(output)); +} + +} // namespace blender::nodes + +void register_node_type_geo_string_join() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_STRING_JOIN, "String Join", NODE_CLASS_CONVERTER, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_string_join_exec; + ntype.declare = blender::nodes::geo_node_string_join_declare; + nodeRegisterType(&ntype); +} From 6d162d35e2c85ea4fb990f0c459ec36064cf0550 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 21 Sep 2021 14:20:54 -0500 Subject: [PATCH 0070/1500] Geometry Nodes: Fill instances separately in the curve fill node With this commit, each referenced instance data will be converted to a geometry instances and processed separately. This should result in a large speedup when the instances component has many insances referring to the same data. This change can act as a blueprint for other nodes that need to implement similar behavior. It adds some helper functions on the instances component to make that easier. Thanks to Erik Abrahamsson for a proof of concept patch. Differential Revision: https://developer.blender.org/D12572 --- source/blender/blenkernel/BKE_geometry_set.hh | 4 ++ .../intern/geometry_component_instances.cc | 53 +++++++++++++++++++ .../geometry/nodes/node_geo_curve_fill.cc | 44 ++++++++++----- 3 files changed, 88 insertions(+), 13 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 98f5de43f84..317d513dae6 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -580,6 +580,9 @@ class InstancesComponent : public GeometryComponent { blender::Span references() const; + void ensure_geometry_instances(); + GeometrySet &geometry_set_from_reference(const int reference_index); + blender::Span instance_reference_handles() const; blender::MutableSpan instance_reference_handles(); blender::MutableSpan instance_transforms(); @@ -588,6 +591,7 @@ class InstancesComponent : public GeometryComponent { blender::Span instance_ids() const; int instances_amount() const; + int references_amount() const; blender::Span almost_unique_ids() const; diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index c4e1fe2f8e9..4c10f5398b7 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -24,6 +24,7 @@ #include "DNA_collection_types.h" #include "BKE_geometry_set.hh" +#include "BKE_geometry_set_instances.hh" #include "attribute_access_intern.hh" @@ -32,6 +33,7 @@ using blender::Map; using blender::MutableSpan; using blender::Set; using blender::Span; +using blender::VectorSet; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -119,6 +121,52 @@ blender::Span InstancesComponent::instance_ids() const return instance_ids_; } +/** + * If references have a collection or object type, convert them into geometry instances. This + * will join geometry components from nested instances if necessary. After that, the geometry + * sets can be edited. + */ +void InstancesComponent::ensure_geometry_instances() +{ + VectorSet new_references; + new_references.reserve(references_.size()); + for (const InstanceReference &reference : references_) { + if (reference.type() == InstanceReference::Type::Object) { + GeometrySet geometry_set; + InstancesComponent &instances = geometry_set.get_component_for_write(); + const int handle = instances.add_reference(reference.object()); + instances.add_instance(handle, float4x4::identity()); + new_references.add_new(geometry_set); + } + else if (reference.type() == InstanceReference::Type::Collection) { + GeometrySet geometry_set; + InstancesComponent &instances = geometry_set.get_component_for_write(); + const int handle = instances.add_reference(reference.collection()); + instances.add_instance(handle, float4x4::identity()); + new_references.add_new(geometry_set); + } + else { + new_references.add_new(reference); + } + } + references_ = std::move(new_references); +} + +/** + * With write access to the instances component, the data in the instanced geometry sets can be + * changed. This is a function on the component rather than each reference to ensure const + * correct-ness for that reason. + */ +GeometrySet &InstancesComponent::geometry_set_from_reference(const int reference_index) +{ + /* If this assert fails, it means #ensure_geometry_instances must be called first. */ + BLI_assert(references_[reference_index].type() == InstanceReference::Type::GeometrySet); + + /* The const cast is okay because the instance's hash in the set + * is not changed by adjusting the data inside the geometry set. */ + return const_cast(references_[reference_index].geometry_set()); +} + /** * Returns a handle for the given reference. * If the reference exists already, the handle of the existing reference is returned. @@ -139,6 +187,11 @@ int InstancesComponent::instances_amount() const return instance_transforms_.size(); } +int InstancesComponent::references_amount() const +{ + return references_.size(); +} + bool InstancesComponent::is_empty() const { return this->instance_reference_handles_.size() == 0; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index d8f40b0a0df..8de2975f9b0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -124,37 +124,55 @@ static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result &resul return mesh; } -static Mesh *curve_fill_calculate(GeoNodeExecParams ¶ms, const CurveComponent &component) +static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCurveFillMode mode) { - const CurveEval &curve = *component.get_for_read(); - if (curve.splines().size() == 0) { - return nullptr; + if (!geometry_set.has_curve()) { + return; } - const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage; - const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode; + const CurveEval &curve = *geometry_set.get_curve_for_read(); + if (curve.splines().is_empty()) { + geometry_set.replace_curve(nullptr); + return; + } const CDT_output_type output_type = (mode == GEO_NODE_CURVE_FILL_MODE_NGONS) ? CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES : CDT_INSIDE_WITH_HOLES; const blender::meshintersect::CDT_result results = do_cdt(curve, output_type); - return cdt_to_mesh(results); + Mesh *mesh = cdt_to_mesh(results); + + geometry_set.replace_mesh(mesh); + geometry_set.replace_curve(nullptr); } static void geo_node_curve_fill_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); - if (!geometry_set.has_curve()) { - params.set_output("Mesh", GeometrySet()); + const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage; + const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode; + + if (geometry_set.has_instances()) { + InstancesComponent &instances = geometry_set.get_component_for_write(); + instances.ensure_geometry_instances(); + + threading::parallel_for(IndexRange(instances.references_amount()), 16, [&](IndexRange range) { + for (int i : range) { + GeometrySet &geometry_set = instances.geometry_set_from_reference(i); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + curve_fill_calculate(geometry_set, mode); + } + }); + + params.set_output("Mesh", std::move(geometry_set)); return; } - Mesh *mesh = curve_fill_calculate(params, - *geometry_set.get_component_for_read()); - params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); + curve_fill_calculate(geometry_set, mode); + + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes From af95542df3ab6b5a9ddce4c933ef6b2230d833eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 21 Sep 2021 21:54:53 +0200 Subject: [PATCH 0071/1500] UUIDs: rename UUID to bUUID Rename the `UUID` struct to `bUUID`. This avoids a symbol clash on Windows, which also defines `UUID`. This only pops up when a `UUID` field is used in the DNA code, which is why it wasn't a problem before (it will be once D12589 lands). No functional changes. --- source/blender/blenlib/BLI_uuid.h | 14 +++++----- source/blender/blenlib/intern/uuid.cc | 20 ++++++------- source/blender/blenlib/tests/BLI_uuid_test.cc | 28 +++++++++---------- source/blender/makesdna/DNA_uuid_types.h | 6 ++-- 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 0c6fb3ae3e7..9b85f8e65bc 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -35,25 +35,25 @@ extern "C" { /** * UUID generator for random (version 4) UUIDs. See RFC4122 section 4.4. * This function is not thread-safe. */ -UUID BLI_uuid_generate_random(void); +bUUID BLI_uuid_generate_random(void); /** * Return the UUID nil value, consisting of all-zero fields. */ -UUID BLI_uuid_nil(void); +bUUID BLI_uuid_nil(void); /** Return true only if this is the nil UUID. */ -bool BLI_uuid_is_nil(UUID uuid); +bool BLI_uuid_is_nil(bUUID uuid); /** Compare two UUIDs, return true only if they are equal. */ -bool BLI_uuid_equal(UUID uuid1, UUID uuid2); +bool BLI_uuid_equal(bUUID uuid1, bUUID uuid2); /** * Format UUID as string. * The buffer must be at least 37 bytes (36 bytes for the UUID + terminating 0). * Use `UUID_STRING_LEN` from DNA_uuid_types.h if you want to use a constant for this. */ -void BLI_uuid_format(char *buffer, UUID uuid) ATTR_NONNULL(); +void BLI_uuid_format(char *buffer, bUUID uuid) ATTR_NONNULL(); /** * Parse a string as UUID. @@ -63,7 +63,7 @@ void BLI_uuid_format(char *buffer, UUID uuid) ATTR_NONNULL(); * Return true if the string could be parsed, and false otherwise. In the latter case, the UUID may * have been partially updated. */ -bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) ATTR_NONNULL(); +bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) ATTR_NONNULL(); #ifdef __cplusplus } @@ -71,6 +71,6 @@ bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) ATTR_NONNULL(); # include /** Output the UUID as formatted ASCII string, see #BLI_uuid_format(). */ -std::ostream &operator<<(std::ostream &stream, UUID uuid); +std::ostream &operator<<(std::ostream &stream, bUUID uuid); #endif diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index f5edb356acc..ae34bcb3d32 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -27,9 +27,9 @@ #include /* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */ -static_assert(sizeof(UUID) == 16, "expect UUIDs to be 128 bit exactly"); +static_assert(sizeof(bUUID) == 16, "expect UUIDs to be 128 bit exactly"); -UUID BLI_uuid_generate_random() +bUUID BLI_uuid_generate_random() { static std::mt19937_64 rng = []() { std::mt19937_64 rng; @@ -57,7 +57,7 @@ UUID BLI_uuid_generate_random() return rng; }(); - UUID uuid; + bUUID uuid; /* RFC4122 suggests setting certain bits to a fixed value, and then randomizing the remaining * bits. The opposite is easier to implement, though, so that's what's done here. */ @@ -78,23 +78,23 @@ UUID BLI_uuid_generate_random() return uuid; } -UUID BLI_uuid_nil(void) +bUUID BLI_uuid_nil(void) { - const UUID nil = {0, 0, 0, 0, 0, 0}; + const bUUID nil = {0, 0, 0, 0, 0, 0}; return nil; } -bool BLI_uuid_is_nil(UUID uuid) +bool BLI_uuid_is_nil(bUUID uuid) { return BLI_uuid_equal(BLI_uuid_nil(), uuid); } -bool BLI_uuid_equal(const UUID uuid1, const UUID uuid2) +bool BLI_uuid_equal(const bUUID uuid1, const bUUID uuid2) { return std::memcmp(&uuid1, &uuid2, sizeof(uuid1)) == 0; } -void BLI_uuid_format(char *buffer, const UUID uuid) +void BLI_uuid_format(char *buffer, const bUUID uuid) { std::sprintf(buffer, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", @@ -111,7 +111,7 @@ void BLI_uuid_format(char *buffer, const UUID uuid) uuid.node[5]); } -bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) +bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) { const int num_fields_parsed = std::sscanf( buffer, @@ -130,7 +130,7 @@ bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) return num_fields_parsed == 11; } -std::ostream &operator<<(std::ostream &stream, UUID uuid) +std::ostream &operator<<(std::ostream &stream, bUUID uuid) { std::string buffer(36, '\0'); BLI_uuid_format(buffer.data(), uuid); diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc index 31c69002c1c..731489c6c9e 100644 --- a/source/blender/blenlib/tests/BLI_uuid_test.cc +++ b/source/blender/blenlib/tests/BLI_uuid_test.cc @@ -20,7 +20,7 @@ TEST(BLI_uuid, generate_random) { - const UUID uuid = BLI_uuid_generate_random(); + const bUUID uuid = BLI_uuid_generate_random(); // The 4 MSbits represent the "version" of the UUID. const uint16_t version = uuid.time_hi_and_version >> 12; @@ -33,11 +33,11 @@ TEST(BLI_uuid, generate_random) TEST(BLI_uuid, generate_many_random) { - const UUID first_uuid = BLI_uuid_generate_random(); + const bUUID first_uuid = BLI_uuid_generate_random(); /* Generate lots of UUIDs to get some indication that the randomness is okay. */ for (int i = 0; i < 1000000; ++i) { - const UUID uuid = BLI_uuid_generate_random(); + const bUUID uuid = BLI_uuid_generate_random(); EXPECT_FALSE(BLI_uuid_equal(first_uuid, uuid)); // Check that the non-random bits are set according to RFC4122. @@ -50,8 +50,8 @@ TEST(BLI_uuid, generate_many_random) TEST(BLI_uuid, nil_value) { - const UUID nil_uuid = BLI_uuid_nil(); - const UUID zeroes_uuid = {0, 0, 0, 0, 0, 0}; + const bUUID nil_uuid = BLI_uuid_nil(); + const bUUID zeroes_uuid = {0, 0, 0, 0, 0, 0}; EXPECT_TRUE(BLI_uuid_equal(nil_uuid, zeroes_uuid)); EXPECT_TRUE(BLI_uuid_is_nil(nil_uuid)); @@ -63,8 +63,8 @@ TEST(BLI_uuid, nil_value) TEST(BLI_uuid, equality) { - const UUID uuid1 = BLI_uuid_generate_random(); - const UUID uuid2 = BLI_uuid_generate_random(); + const bUUID uuid1 = BLI_uuid_generate_random(); + const bUUID uuid2 = BLI_uuid_generate_random(); EXPECT_TRUE(BLI_uuid_equal(uuid1, uuid1)); EXPECT_FALSE(BLI_uuid_equal(uuid1, uuid2)); @@ -72,7 +72,7 @@ TEST(BLI_uuid, equality) TEST(BLI_uuid, string_formatting) { - UUID uuid; + bUUID uuid; std::string buffer(36, '\0'); memset(&uuid, 0, sizeof(uuid)); @@ -91,12 +91,12 @@ TEST(BLI_uuid, string_formatting) EXPECT_EQ("00000001-0002-0003-0405-060000000007", buffer); /* Somewhat more complex bit patterns. This is a version 1 UUID generated from Python. */ - const UUID uuid1 = {3540651616, 5282, 4588, 139, 153, {0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}}; + const bUUID uuid1 = {3540651616, 5282, 4588, 139, 153, {0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}}; BLI_uuid_format(buffer.data(), uuid1); EXPECT_EQ("d30a0e60-14a2-11ec-8b99-f7736944db8b", buffer); /* Namespace UUID, example listed in RFC4211. */ - const UUID namespace_dns = { + const bUUID namespace_dns = { 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, {0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}}; BLI_uuid_format(buffer.data(), namespace_dns); EXPECT_EQ("6ba7b810-9dad-11d1-80b4-00c04fd430c8", buffer); @@ -104,7 +104,7 @@ TEST(BLI_uuid, string_formatting) TEST(BLI_uuid, string_parsing_ok) { - UUID uuid; + bUUID uuid; std::string buffer(36, '\0'); const bool parsed_ok = BLI_uuid_parse_string(&uuid, "d30a0e60-14a2-11ec-8b99-f7736944db8b"); @@ -115,7 +115,7 @@ TEST(BLI_uuid, string_parsing_ok) TEST(BLI_uuid, string_parsing_capitalisation) { - UUID uuid; + bUUID uuid; std::string buffer(36, '\0'); /* RFC4122 demands acceptance of upper-case hex digits. */ @@ -129,7 +129,7 @@ TEST(BLI_uuid, string_parsing_capitalisation) TEST(BLI_uuid, string_parsing_fail) { - UUID uuid; + bUUID uuid; std::string buffer(36, '\0'); const bool parsed_ok = BLI_uuid_parse_string(&uuid, "d30a0e60!14a2-11ec-8b99-f7736944db8b"); @@ -139,7 +139,7 @@ TEST(BLI_uuid, string_parsing_fail) TEST(BLI_uuid, stream_operator) { std::stringstream ss; - const UUID uuid = {3540651616, 5282, 4588, 139, 153, {0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}}; + const bUUID uuid = {3540651616, 5282, 4588, 139, 153, {0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}}; ss << uuid; EXPECT_EQ(ss.str(), "d30a0e60-14a2-11ec-8b99-f7736944db8b"); } diff --git a/source/blender/makesdna/DNA_uuid_types.h b/source/blender/makesdna/DNA_uuid_types.h index 30c8beaa628..fa0a78f074b 100644 --- a/source/blender/makesdna/DNA_uuid_types.h +++ b/source/blender/makesdna/DNA_uuid_types.h @@ -28,15 +28,17 @@ extern "C" { /** * \brief Universally Unique Identifier according to RFC4122. + * + * Cannot be named simply `UUID`, because Windows already defines that type. */ -typedef struct UUID { +typedef struct bUUID { uint32_t time_low; uint16_t time_mid; uint16_t time_hi_and_version; uint8_t clock_seq_hi_and_reserved; uint8_t clock_seq_low; uint8_t node[6]; -} UUID; +} bUUID; #ifdef __cplusplus } From 8324ac84577cd96fe578dd905cc1eced823e2fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20M=C3=BCller?= Date: Sun, 19 Sep 2021 22:37:13 +0200 Subject: [PATCH 0072/1500] Audaspace: porting upstream pulseaudio fixes. Fixes T89045 and T91057. --- extern/audaspace/CMakeLists.txt | 2 + extern/audaspace/include/util/RingBuffer.h | 97 ++++++++++ .../plugins/pulseaudio/PulseAudioDevice.cpp | 179 ++++++++++++------ .../plugins/pulseaudio/PulseAudioDevice.h | 76 ++++++-- .../plugins/pulseaudio/PulseAudioSymbols.h | 11 ++ extern/audaspace/src/util/RingBuffer.cpp | 137 ++++++++++++++ 6 files changed, 424 insertions(+), 78 deletions(-) create mode 100644 extern/audaspace/include/util/RingBuffer.h create mode 100644 extern/audaspace/src/util/RingBuffer.cpp diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt index 552ff749512..8493fe3e67d 100644 --- a/extern/audaspace/CMakeLists.txt +++ b/extern/audaspace/CMakeLists.txt @@ -129,6 +129,7 @@ set(SRC src/util/Barrier.cpp src/util/Buffer.cpp src/util/BufferReader.cpp + src/util/RingBuffer.cpp src/util/StreamBuffer.cpp src/util/ThreadPool.cpp ) @@ -245,6 +246,7 @@ set(PUBLIC_HDR include/util/BufferReader.h include/util/ILockable.h include/util/Math3D.h + include/util/RingBuffer.h include/util/StreamBuffer.h include/util/ThreadPool.h ) diff --git a/extern/audaspace/include/util/RingBuffer.h b/extern/audaspace/include/util/RingBuffer.h new file mode 100644 index 00000000000..67bd1cc8640 --- /dev/null +++ b/extern/audaspace/include/util/RingBuffer.h @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright 2009-2021 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#pragma once + +/** + * @file RingBuffer.h + * @ingroup util + * The RingBuffer class. + */ + +#include "Audaspace.h" +#include "Buffer.h" + +#include + +AUD_NAMESPACE_BEGIN + +/** + * This class is a simple ring buffer in RAM which is 32 Byte aligned and provides + * functionality for concurrent reading and writting without locks. + */ +class AUD_API RingBuffer +{ +private: + /// The buffer storing the actual data. + Buffer m_buffer; + + /// The reading pointer. + volatile size_t m_read; + + /// The writing pointer. + volatile size_t m_write; + + // delete copy constructor and operator= + RingBuffer(const RingBuffer&) = delete; + RingBuffer& operator=(const RingBuffer&) = delete; + +public: + /** + * Creates a new ring buffer. + * \param size The size of the buffer in bytes. + */ + RingBuffer(int size = 0); + + /** + * Returns the pointer to the ring buffer in memory. + */ + sample_t* getBuffer() const; + + /** + * Returns the size of the ring buffer in bytes. + */ + int getSize() const; + + size_t getReadSize() const; + + size_t getWriteSize() const; + + size_t read(data_t* target, size_t size); + + size_t write(data_t* source, size_t size); + + /** + * Resets the ring buffer to a state where nothing has been written or read. + */ + void reset(); + + /** + * Resizes the ring buffer. + * \param size The new size of the ring buffer, measured in bytes. + */ + void resize(int size); + + /** + * Makes sure the ring buffer has a minimum size. + * If size is >= current size, nothing will happen. + * Otherwise the ring buffer is resized with keep as parameter. + * \param size The new minimum size of the ring buffer, measured in bytes. + */ + void assureSize(int size); +}; + +AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp index bf3fad82620..cddc411cfc6 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp @@ -23,95 +23,121 @@ AUD_NAMESPACE_BEGIN +PulseAudioDevice::PulseAudioSynchronizer::PulseAudioSynchronizer(PulseAudioDevice *device) : + m_device(device) +{ +} + +double PulseAudioDevice::PulseAudioSynchronizer::getPosition(std::shared_ptr handle) +{ + pa_usec_t latency; + int negative; + AUD_pa_stream_get_latency(m_device->m_stream, &latency, &negative); + + double delay = m_device->m_ring_buffer.getReadSize() / (AUD_SAMPLE_SIZE(m_device->m_specs) * m_device->m_specs.rate) + latency * 1.0e-6; + + return handle->getPosition() - delay; +} + +void PulseAudioDevice::updateRingBuffer() +{ + unsigned int samplesize = AUD_SAMPLE_SIZE(m_specs); + + std::unique_lock lock(m_mixingLock); + + Buffer buffer; + + while(m_valid) + { + size_t size = m_ring_buffer.getWriteSize(); + + size_t sample_count = size / samplesize; + + if(sample_count > 0) + { + size = sample_count * samplesize; + + buffer.assureSize(size); + + mix(reinterpret_cast(buffer.getBuffer()), sample_count); + + m_ring_buffer.write(reinterpret_cast(buffer.getBuffer()), size); + } + + m_mixingCondition.wait(lock); + } +} + void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data) { PulseAudioDevice* device = (PulseAudioDevice*)data; - std::lock_guard lock(*device); - device->m_state = AUD_pa_context_get_state(context); + + AUD_pa_threaded_mainloop_signal(device->m_mainloop, 0); } void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t total_bytes, void *data) { PulseAudioDevice* device = (PulseAudioDevice*)data; - void* buffer; + data_t* buffer; + + size_t sample_size = AUD_DEVICE_SAMPLE_SIZE(device->m_specs); while(total_bytes > 0) { size_t num_bytes = total_bytes; - AUD_pa_stream_begin_write(stream, &buffer, &num_bytes); + AUD_pa_stream_begin_write(stream, reinterpret_cast(&buffer), &num_bytes); - device->mix((data_t*)buffer, num_bytes / AUD_DEVICE_SAMPLE_SIZE(device->m_specs)); + size_t readsamples = device->m_ring_buffer.getReadSize(); - AUD_pa_stream_write(stream, buffer, num_bytes, nullptr, 0, PA_SEEK_RELATIVE); + readsamples = std::min(readsamples, size_t(num_bytes)) / sample_size; + + device->m_ring_buffer.read(buffer, readsamples * sample_size); + + if(readsamples * sample_size < num_bytes) + std::memset(buffer + readsamples * sample_size, 0, num_bytes - readsamples * sample_size); + + if(device->m_mixingLock.try_lock()) + { + device->m_mixingCondition.notify_all(); + device->m_mixingLock.unlock(); + } + + AUD_pa_stream_write(stream, reinterpret_cast(buffer), num_bytes, nullptr, 0, PA_SEEK_RELATIVE); total_bytes -= num_bytes; } } -void PulseAudioDevice::PulseAudio_underflow(pa_stream *stream, void *data) +void PulseAudioDevice::playing(bool playing) { - PulseAudioDevice* device = (PulseAudioDevice*)data; + m_playback = playing; - DeviceSpecs specs = device->getSpecs(); - - if(++device->m_underflows > 4 && device->m_buffersize < AUD_DEVICE_SAMPLE_SIZE(specs) * specs.rate * 2) - { - device->m_buffersize <<= 1; - device->m_underflows = 0; - - pa_buffer_attr buffer_attr; - - buffer_attr.fragsize = -1U; - buffer_attr.maxlength = -1U; - buffer_attr.minreq = -1U; - buffer_attr.prebuf = -1U; - buffer_attr.tlength = device->m_buffersize; - - AUD_pa_stream_set_buffer_attr(stream, &buffer_attr, nullptr, nullptr); - } -} - -void PulseAudioDevice::runMixingThread() -{ - for(;;) - { - { - std::lock_guard lock(*this); - - if(shouldStop()) - { - AUD_pa_stream_cork(m_stream, 1, nullptr, nullptr); - AUD_pa_stream_flush(m_stream, nullptr, nullptr); - doStop(); - return; - } - } - - if(AUD_pa_stream_is_corked(m_stream)) - AUD_pa_stream_cork(m_stream, 0, nullptr, nullptr); - - // similar to AUD_pa_mainloop_iterate(m_mainloop, false, nullptr); except with a longer timeout - AUD_pa_mainloop_prepare(m_mainloop, 1 << 14); - AUD_pa_mainloop_poll(m_mainloop); - AUD_pa_mainloop_dispatch(m_mainloop); - } + AUD_pa_threaded_mainloop_lock(m_mainloop); + AUD_pa_stream_cork(m_stream, playing ? 0 : 1, nullptr, nullptr); + AUD_pa_threaded_mainloop_unlock(m_mainloop); } PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize) : + m_synchronizer(this), + m_playback(false), m_state(PA_CONTEXT_UNCONNECTED), + m_valid(true), m_underflows(0) { - m_mainloop = AUD_pa_mainloop_new(); + m_mainloop = AUD_pa_threaded_mainloop_new(); - m_context = AUD_pa_context_new(AUD_pa_mainloop_get_api(m_mainloop), name.c_str()); + AUD_pa_threaded_mainloop_lock(m_mainloop); + + m_context = AUD_pa_context_new(AUD_pa_threaded_mainloop_get_api(m_mainloop), name.c_str()); if(!m_context) { - AUD_pa_mainloop_free(m_mainloop); + AUD_pa_threaded_mainloop_unlock(m_mainloop); + AUD_pa_threaded_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect to PulseAudio."); } @@ -120,21 +146,26 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff AUD_pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr); + AUD_pa_threaded_mainloop_start(m_mainloop); + while(m_state != PA_CONTEXT_READY) { switch(m_state) { case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: + AUD_pa_threaded_mainloop_unlock(m_mainloop); + AUD_pa_threaded_mainloop_stop(m_mainloop); + AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_mainloop_free(m_mainloop); + AUD_pa_threaded_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect to PulseAudio."); break; default: - AUD_pa_mainloop_iterate(m_mainloop, true, nullptr); + AUD_pa_threaded_mainloop_wait(m_mainloop); break; } } @@ -182,16 +213,18 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff if(!m_stream) { + AUD_pa_threaded_mainloop_unlock(m_mainloop); + AUD_pa_threaded_mainloop_stop(m_mainloop); + AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_mainloop_free(m_mainloop); + AUD_pa_threaded_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not create PulseAudio stream."); } AUD_pa_stream_set_write_callback(m_stream, PulseAudio_request, this); - AUD_pa_stream_set_underflow_callback(m_stream, PulseAudio_underflow, this); buffersize *= AUD_DEVICE_SAMPLE_SIZE(m_specs); m_buffersize = buffersize; @@ -204,31 +237,53 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff buffer_attr.prebuf = -1U; buffer_attr.tlength = buffersize; - if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast(PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0) + m_ring_buffer.resize(buffersize); + + if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0) { + AUD_pa_threaded_mainloop_unlock(m_mainloop); + AUD_pa_threaded_mainloop_stop(m_mainloop); + AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_mainloop_free(m_mainloop); + AUD_pa_threaded_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect PulseAudio stream."); } + AUD_pa_threaded_mainloop_unlock(m_mainloop); + create(); + + m_mixingThread = std::thread(&PulseAudioDevice::updateRingBuffer, this); } PulseAudioDevice::~PulseAudioDevice() { - stopMixingThread(); + m_valid = false; + + m_mixingLock.lock(); + m_mixingCondition.notify_all(); + m_mixingLock.unlock(); + + m_mixingThread.join(); + + AUD_pa_threaded_mainloop_stop(m_mainloop); AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_mainloop_free(m_mainloop); + AUD_pa_threaded_mainloop_free(m_mainloop); destroy(); } +ISynchronizer *PulseAudioDevice::getSynchronizer() +{ + return &m_synchronizer; +} + class PulseAudioDeviceFactory : public IDeviceFactory { private: diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h index 45b813a5755..57359110633 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h @@ -26,7 +26,11 @@ * The PulseAudioDevice class. */ -#include "devices/ThreadedDevice.h" +#include "devices/SoftwareDevice.h" +#include "util/RingBuffer.h" + +#include +#include #include @@ -35,17 +39,65 @@ AUD_NAMESPACE_BEGIN /** * This device plays back through PulseAudio, the simple direct media layer. */ -class AUD_PLUGIN_API PulseAudioDevice : public ThreadedDevice +class AUD_PLUGIN_API PulseAudioDevice : public SoftwareDevice { private: - pa_mainloop* m_mainloop; + class PulseAudioSynchronizer : public DefaultSynchronizer + { + PulseAudioDevice* m_device; + + public: + PulseAudioSynchronizer(PulseAudioDevice* device); + + virtual double getPosition(std::shared_ptr handle); + }; + + /// Synchronizer. + PulseAudioSynchronizer m_synchronizer; + + /** + * Whether there is currently playback. + */ + volatile bool m_playback; + + pa_threaded_mainloop* m_mainloop; pa_context* m_context; pa_stream* m_stream; pa_context_state_t m_state; + /** + * The mixing ring buffer. + */ + RingBuffer m_ring_buffer; + + /** + * Whether the device is valid. + */ + bool m_valid; + int m_buffersize; uint32_t m_underflows; + /** + * The mixing thread. + */ + std::thread m_mixingThread; + + /** + * Mutex for mixing. + */ + std::mutex m_mixingLock; + + /** + * Condition for mixing. + */ + std::condition_variable m_mixingCondition; + + /** + * Updates the ring buffer. + */ + AUD_LOCAL void updateRingBuffer(); + /** * Reports the state of the PulseAudio server connection. * \param context The PulseAudio context. @@ -61,23 +113,13 @@ private: */ AUD_LOCAL static void PulseAudio_request(pa_stream* stream, size_t total_bytes, void* data); - /** - * Reports an underflow from the PulseAudio server. - * Automatically adjusts the latency if this happens too often. - * @param stream The PulseAudio stream. - * \param data The PulseAudio device. - */ - AUD_LOCAL static void PulseAudio_underflow(pa_stream* stream, void* data); - - /** - * Streaming thread main function. - */ - AUD_LOCAL void runMixingThread(); - // delete copy constructor and operator= PulseAudioDevice(const PulseAudioDevice&) = delete; PulseAudioDevice& operator=(const PulseAudioDevice&) = delete; +protected: + virtual void playing(bool playing); + public: /** * Opens the PulseAudio audio device for playback. @@ -93,6 +135,8 @@ public: */ virtual ~PulseAudioDevice(); + virtual ISynchronizer* getSynchronizer(); + /** * Registers this plugin. */ diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h index 361aa518087..a33135b6e25 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h @@ -25,6 +25,7 @@ PULSEAUDIO_SYMBOL(pa_stream_begin_write); PULSEAUDIO_SYMBOL(pa_stream_connect_playback); PULSEAUDIO_SYMBOL(pa_stream_cork); PULSEAUDIO_SYMBOL(pa_stream_flush); +PULSEAUDIO_SYMBOL(pa_stream_get_latency); PULSEAUDIO_SYMBOL(pa_stream_is_corked); PULSEAUDIO_SYMBOL(pa_stream_new); PULSEAUDIO_SYMBOL(pa_stream_set_buffer_attr); @@ -39,3 +40,13 @@ PULSEAUDIO_SYMBOL(pa_mainloop_iterate); PULSEAUDIO_SYMBOL(pa_mainloop_prepare); PULSEAUDIO_SYMBOL(pa_mainloop_poll); PULSEAUDIO_SYMBOL(pa_mainloop_dispatch); + +PULSEAUDIO_SYMBOL(pa_threaded_mainloop_free); +PULSEAUDIO_SYMBOL(pa_threaded_mainloop_get_api); +PULSEAUDIO_SYMBOL(pa_threaded_mainloop_lock); +PULSEAUDIO_SYMBOL(pa_threaded_mainloop_new); +PULSEAUDIO_SYMBOL(pa_threaded_mainloop_signal); +PULSEAUDIO_SYMBOL(pa_threaded_mainloop_start); +PULSEAUDIO_SYMBOL(pa_threaded_mainloop_stop); +PULSEAUDIO_SYMBOL(pa_threaded_mainloop_unlock); +PULSEAUDIO_SYMBOL(pa_threaded_mainloop_wait); diff --git a/extern/audaspace/src/util/RingBuffer.cpp b/extern/audaspace/src/util/RingBuffer.cpp new file mode 100644 index 00000000000..3796684aa88 --- /dev/null +++ b/extern/audaspace/src/util/RingBuffer.cpp @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright 2009-2021 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "util/RingBuffer.h" + +#include +#include +#include + +#define ALIGNMENT 32 +#define ALIGN(a) (a + ALIGNMENT - ((long long)a & (ALIGNMENT-1))) + +AUD_NAMESPACE_BEGIN + +RingBuffer::RingBuffer(int size) : + m_buffer(size), + m_read(0), + m_write(0) +{ +} + +sample_t* RingBuffer::getBuffer() const +{ + return m_buffer.getBuffer(); +} + +int RingBuffer::getSize() const +{ + return m_buffer.getSize(); +} + +size_t RingBuffer::getReadSize() const +{ + size_t read = m_read; + size_t write = m_write; + + if(read > write) + return write + getSize() - read; + else + return write - read; +} + +size_t RingBuffer::getWriteSize() const +{ + size_t read = m_read; + size_t write = m_write; + + if(read > write) + return read - write - 1; + else + return read + getSize() - write - 1; +} + +size_t RingBuffer::read(data_t* target, size_t size) +{ + size = std::min(size, getReadSize()); + + data_t* buffer = reinterpret_cast(m_buffer.getBuffer()); + + if(m_read + size > m_buffer.getSize()) + { + size_t read_first = m_buffer.getSize() - m_read; + size_t read_second = size - read_first; + + std::memcpy(target, buffer + m_read, read_first); + std::memcpy(target + read_first, buffer, read_second); + + m_read = read_second; + } + else + { + std::memcpy(target, buffer + m_read, size); + + m_read += size; + } + + return size; +} + +size_t RingBuffer::write(data_t* source, size_t size) +{ + size = std::min(size, getWriteSize()); + + data_t* buffer = reinterpret_cast(m_buffer.getBuffer()); + + if(m_write + size > m_buffer.getSize()) + { + size_t write_first = m_buffer.getSize() - m_write; + size_t write_second = size - write_first; + + std::memcpy(buffer + m_write, source, write_first); + std::memcpy(buffer, source + write_first, write_second); + + m_write = write_second; + } + else + { + std::memcpy(buffer + m_write, source, size); + + m_write += size; + } + + return size; +} + +void RingBuffer::reset() +{ + m_read = 0; + m_write = 0; +} + +void RingBuffer::resize(int size) +{ + m_buffer.resize(size); + reset(); +} + +void RingBuffer::assureSize(int size) +{ + m_buffer.assureSize(size); + reset(); +} + +AUD_NAMESPACE_END From 41e3bf8a8e79c8c42f72d49dea64b634aa243ff7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 21 Sep 2021 17:17:40 -0500 Subject: [PATCH 0073/1500] Geometry Nodes: Add legacy warning and "View Legacy" operator This commit adds warning messages to "legacy" nodes that will be removed in the future. The warning is shown in the node header, but it is not printed in the terminal or displayed in the modifier. It is also not propogated to node groups, but that is a more general task. If the modifier's node tree has executed a deprecated node, it will display a warning and a "Search" button that will select the nodes and pan to them in the node editor. This doesn't open child node trees and select nodes in there, because I want to keep this operator simple and avoid wasting a lot of time perfecting this behavior. Differential Revision: https://developer.blender.org/D12454 --- .../blender/editors/space_node/node_draw.cc | 4 + .../blender/editors/space_node/node_intern.h | 1 + source/blender/editors/space_node/node_ops.c | 1 + .../blender/editors/space_node/node_view.cc | 90 +++++++++++++++++++ source/blender/modifiers/intern/MOD_nodes.cc | 18 +++- .../modifiers/intern/MOD_nodes_evaluator.cc | 8 ++ .../nodes/NOD_geometry_nodes_eval_log.hh | 1 + 7 files changed, 121 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index aa241071425..10a3285be8b 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -1446,6 +1446,8 @@ static int node_error_type_to_icon(const geo_log::NodeWarningType type) return ICON_ERROR; case geo_log::NodeWarningType::Info: return ICON_INFO; + case geo_log::NodeWarningType::Legacy: + return ICON_ERROR; } BLI_assert(false); @@ -1456,6 +1458,8 @@ static uint8_t node_error_type_priority(const geo_log::NodeWarningType type) { switch (type) { case geo_log::NodeWarningType::Error: + return 4; + case geo_log::NodeWarningType::Legacy: return 3; case geo_log::NodeWarningType::Warning: return 2; diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index d35fd729131..f069038cc09 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -175,6 +175,7 @@ int space_node_view_flag(struct bContext *C, void NODE_OT_view_all(struct wmOperatorType *ot); void NODE_OT_view_selected(struct wmOperatorType *ot); +void NODE_OT_geometry_node_view_legacy(struct wmOperatorType *ot); void NODE_OT_backimage_move(struct wmOperatorType *ot); void NODE_OT_backimage_zoom(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index 610c2889e7a..df4f63af20b 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -51,6 +51,7 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_view_all); WM_operatortype_append(NODE_OT_view_selected); + WM_operatortype_append(NODE_OT_geometry_node_view_legacy); WM_operatortype_append(NODE_OT_mute_toggle); WM_operatortype_append(NODE_OT_hide_toggle); diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc index f0db0539c4f..762b4b36a39 100644 --- a/source/blender/editors/space_node/node_view.cc +++ b/source/blender/editors/space_node/node_view.cc @@ -23,8 +23,10 @@ #include "DNA_node_types.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" +#include "BLI_string_ref.hh" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -54,6 +56,8 @@ #include "node_intern.h" /* own include */ +using blender::StringRef; + /* -------------------------------------------------------------------- */ /** \name View All Operator * \{ */ @@ -700,3 +704,89 @@ void NODE_OT_backimage_sample(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Geometry Nodes Legacy Operator + * + * This operator should be removed when the 2.93 legacy nodes are removed. + * \{ */ + +static int space_node_view_geometry_nodes_legacy(bContext *C, SpaceNode *snode, wmOperator *op) +{ + ARegion *region = CTX_wm_region(C); + + /* Only use the node editor's active node tree. Otherwise this will be too complicated. */ + bNodeTree *node_tree = snode->nodetree; + if (node_tree == nullptr || node_tree->type != NTREE_GEOMETRY) { + return OPERATOR_CANCELLED; + } + + bool found_legacy_node = false; + LISTBASE_FOREACH_BACKWARD (bNode *, node, &node_tree->nodes) { + StringRef idname{node->idname}; + if (idname.find("Legacy") == StringRef::not_found) { + node->flag &= ~NODE_SELECT; + } + else { + found_legacy_node = true; + node->flag |= NODE_SELECT; + } + } + + if (!found_legacy_node) { + WM_report(RPT_INFO, "Legacy node not found, may be in nested node group"); + } + + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + if (space_node_view_flag(C, snode, region, NODE_SELECT, smooth_viewtx)) { + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static int geometry_node_view_legacy_exec(bContext *C, wmOperator *op) +{ + /* Allow running this operator directly in a specific node editor. */ + if (SpaceNode *snode = CTX_wm_space_node(C)) { + return space_node_view_geometry_nodes_legacy(C, snode, op); + } + + /* Since the operator is meant to be called from a button in the modifier panel, the node tree + * must be found from the screen, using the largest node editor if there is more than one. */ + if (ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_NODE, 0)) { + if (SpaceNode *snode = static_cast(area->spacedata.first)) { + ScrArea *old_area = CTX_wm_area(C); + ARegion *old_region = CTX_wm_region(C); + + /* Override the context since it is used by the View2D panning code. */ + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, static_cast(area->regionbase.last)); + const int result = space_node_view_geometry_nodes_legacy(C, snode, op); + CTX_wm_area_set(C, old_area); + CTX_wm_region_set(C, old_region); + return result; + } + } + + return OPERATOR_CANCELLED; +} + +static bool geometry_node_view_legacy_poll(bContext *C) +{ + /* Allow direct execution in a node editor, but also affecting any visible node editor. */ + return ED_operator_node_active(C) || BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_NODE, 0); +} + +void NODE_OT_geometry_node_view_legacy(wmOperatorType *ot) +{ + ot->name = "View Deprecated Geometry Nodes"; + ot->idname = "NODE_OT_geometry_node_view_legacy"; + ot->description = "Select and view legacy geometry nodes in the node editor"; + + ot->exec = geometry_node_view_legacy_exec; + ot->poll = geometry_node_view_legacy_poll; + + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 6b976b016e1..8c02c83d479 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -68,6 +68,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "BLT_translation.h" + #include "WM_types.h" #include "RNA_access.h" @@ -1090,17 +1092,29 @@ static void panel_draw(const bContext *C, Panel *panel) } /* Draw node warnings. */ + bool has_legacy_node = false; if (nmd->runtime_eval_log != nullptr) { const geo_log::ModifierLog &log = *static_cast(nmd->runtime_eval_log); - log.foreach_node_log([layout](const geo_log::NodeLog &node_log) { + log.foreach_node_log([&](const geo_log::NodeLog &node_log) { for (const geo_log::NodeWarning &warning : node_log.warnings()) { - if (warning.type != geo_log::NodeWarningType::Info) { + if (warning.type == geo_log::NodeWarningType::Legacy) { + has_legacy_node = true; + } + else if (warning.type != geo_log::NodeWarningType::Info) { uiItemL(layout, warning.message.c_str(), ICON_ERROR); } } }); } + if (USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields) && has_legacy_node) { + uiLayout *row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Node tree has legacy node"), ICON_ERROR); + uiLayout *sub = uiLayoutRow(row, false); + uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT); + uiItemO(sub, "", ICON_VIEWZOOM, "NODE_OT_geometry_node_view_legacy"); + } + modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 56de0f87ed8..e50c07ce6f2 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -26,6 +26,8 @@ #include "FN_generic_value_map.hh" #include "FN_multi_function.hh" +#include "BLT_translation.h" + #include "BLI_enumerable_thread_specific.hh" #include "BLI_stack.hh" #include "BLI_task.h" @@ -868,6 +870,12 @@ class GeometryNodesEvaluator { NodeParamsProvider params_provider{*this, node, node_state}; GeoNodeExecParams params{params_provider}; + if (USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) { + if (node->idname().find("Legacy") != StringRef::not_found) { + params.error_message_add(geo_log::NodeWarningType::Legacy, + TIP_("Legacy node will be removed before Blender 4.0")); + } + } bnode.typeinfo->geometry_node_execute(params); } diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 00d97b24646..ff8e137e341 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -131,6 +131,7 @@ enum class NodeWarningType { Error, Warning, Info, + Legacy, }; struct NodeWarning { From 4d881d9dad764dec22c057f434d3f91c26f0b0ef Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 21 Sep 2021 17:37:16 -0500 Subject: [PATCH 0074/1500] Geometry Nodes: Curve Parameter Node This commit adds a field input node that outputs the fraction of the total length of the spline on each control point. This is useful for anything that involves varying a value depending on how far along the spline it is. It also works when evaluated on the spline domain, where it outputs the fraction of the total length of all of the splines at the start. The operation isn't as well defined for NURB splines for the reasons noted in the code comment. That can be said explicitly in the docs. Differential Revision: https://developer.blender.org/D12548 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_geo_curve_parameter.cc | 206 ++++++++++++++++++ 7 files changed, 212 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 5ec1db0baf8..77ffb609dd2 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -527,6 +527,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveFill"), NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), + NodeItem("GeometryNodeCurveParameter", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeInputTangent", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeCurveSample", poll=geometry_nodes_fields_poll), ]), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index e242aed879f..42e2cda8de3 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1498,6 +1498,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_SAMPLE 1085 #define GEO_NODE_INPUT_TANGENT 1086 #define GEO_NODE_STRING_JOIN 1087 +#define GEO_NODE_CURVE_PARAMETER 1088 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 7f423e53161..2d0239740f8 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5187,6 +5187,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_endpoints(); register_node_type_geo_curve_fill(); register_node_type_geo_curve_length(); + register_node_type_geo_curve_parameter(); register_node_type_geo_curve_primitive_bezier_segment(); register_node_type_geo_curve_primitive_circle(); register_node_type_geo_curve_primitive_line(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 1f6f1f333f0..a8795649ede 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -177,6 +177,7 @@ set(SRC geometry/nodes/node_geo_curve_endpoints.cc geometry/nodes/node_geo_curve_fill.cc geometry/nodes/node_geo_curve_length.cc + geometry/nodes/node_geo_curve_parameter.cc geometry/nodes/node_geo_curve_primitive_bezier_segment.cc geometry/nodes/node_geo_curve_primitive_circle.cc geometry/nodes/node_geo_curve_primitive_line.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 91d79d9e7b6..24f60263d8a 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -59,6 +59,7 @@ void register_node_type_geo_convex_hull(void); void register_node_type_geo_curve_endpoints(void); void register_node_type_geo_curve_fill(void); void register_node_type_geo_curve_length(void); +void register_node_type_geo_curve_parameter(void); void register_node_type_geo_curve_sample(void); void register_node_type_geo_curve_primitive_bezier_segment(void); void register_node_type_geo_curve_primitive_circle(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 908940be93a..8fb18e839a7 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -319,6 +319,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "") DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Curve Fill", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") +DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc new file mode 100644 index 00000000000..2cde198e679 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc @@ -0,0 +1,206 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Factor"); +} + +/** + * A basic interpolation from the point domain to the spline domain would be useless, since the + * average parameter for each spline would just be 0.5, or close to it. Instead, the parameter for + * each spline is the portion of the total length at the start of the spline. + */ +static Array curve_parameter_spline_domain(const CurveEval &curve, const IndexMask mask) +{ + Span splines = curve.splines(); + float length = 0.0f; + Array parameters(splines.size()); + for (const int i : splines.index_range()) { + parameters[i] = length; + length += splines[i]->length(); + } + const float total_length_inverse = length == 0.0f ? 0.0f : 1.0f / length; + mask.foreach_index([&](const int64_t i) { parameters[i] *= total_length_inverse; }); + + return parameters; +} + +/** + * The parameter at each control point is the factor at the corresponding evaluated point. + */ +static void calculate_bezier_parameters(const BezierSpline &spline, MutableSpan parameters) +{ + Span offsets = spline.control_point_offsets(); + Span lengths = spline.evaluated_lengths(); + const float total_length = spline.length(); + const float total_length_inverse = total_length == 0.0f ? 0.0f : 1.0f / total_length; + + for (const int i : IndexRange(1, spline.size() - 1)) { + parameters[i] = lengths[offsets[i] - 1] * total_length_inverse; + } +} + +/** + * The parameter for poly splines is simply the evaluated lengths divided by the total length. + */ +static void calculate_poly_parameters(const PolySpline &spline, MutableSpan parameters) +{ + Span lengths = spline.evaluated_lengths(); + const float total_length = spline.length(); + const float total_length_inverse = total_length == 0.0f ? 0.0f : 1.0f / total_length; + + for (const int i : IndexRange(1, spline.size() - 1)) { + parameters[i] = lengths[i - 1] * total_length_inverse; + } +} + +/** + * Since NURBS control points do not necessarily coincide with the evaluated curve's path, and + * each control point doesn't correspond well to a specific evaluated point, the parameter at + * each point is not well defined. So instead, treat the control points as if they were a poly + * spline. + */ +static void calculate_nurbs_parameters(const NURBSpline &spline, MutableSpan parameters) +{ + Span positions = spline.positions(); + Array control_point_lengths(spline.size()); + + float length = 0.0f; + for (const int i : IndexRange(positions.size() - 1)) { + parameters[i] = length; + length += float3::distance(positions[i], positions[i + 1]); + } + + const float total_length_inverse = length == 0.0f ? 0.0f : 1.0f / length; + for (float ¶meter : parameters) { + parameter *= total_length_inverse; + } +} + +static Array curve_parameter_point_domain(const CurveEval &curve) +{ + Span splines = curve.splines(); + Array offsets = curve.control_point_offsets(); + const int total_size = offsets.last(); + Array parameters(total_size); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + MutableSpan spline_factors{parameters.as_mutable_span().slice(offsets[i], spline.size())}; + spline_factors.first() = 0.0f; + switch (splines[i]->type()) { + case Spline::Type::Bezier: { + calculate_bezier_parameters(static_cast(spline), spline_factors); + break; + } + case Spline::Type::Poly: { + calculate_poly_parameters(static_cast(spline), spline_factors); + break; + } + case Spline::Type::NURBS: { + calculate_nurbs_parameters(static_cast(spline), spline_factors); + break; + } + } + } + }); + return parameters; +} + +static const GVArray *construct_curve_parameter_gvarray(const CurveEval &curve, + const IndexMask mask, + const AttributeDomain domain, + ResourceScope &scope) +{ + if (domain == ATTR_DOMAIN_POINT) { + Array parameters = curve_parameter_point_domain(curve); + return &scope.construct>>(std::move(parameters)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + Array parameters = curve_parameter_spline_domain(curve, mask); + return &scope.construct>>(std::move(parameters)); + } + + return nullptr; +} + +class CurveParameterFieldInput final : public fn::FieldInput { + public: + CurveParameterFieldInput() : fn::FieldInput(CPPType::get(), "Curve Parameter") + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve) { + return construct_curve_parameter_gvarray(*curve, mask, domain, scope); + } + } + } + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 29837456298; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast(&other) != nullptr; + } +}; + +static void geo_node_curve_parameter_exec(GeoNodeExecParams params) +{ + Field parameter_field{std::make_shared()}; + params.set_output("Factor", std::move(parameter_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_parameter() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_PARAMETER, "Curve Parameter", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_parameter_exec; + ntype.declare = blender::nodes::geo_node_curve_parameter_declare; + nodeRegisterType(&ntype); +} From 77061a5621015dfd0c9f89fd21cb23d706d0cec8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 Sep 2021 14:46:32 +1000 Subject: [PATCH 0075/1500] Cleanup: incompatible-pointer-types warning --- source/blender/editors/space_sequencer/sequencer_draw.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 6edf2413e87..6f10a0f6c9e 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1393,7 +1393,10 @@ static float seq_thumbnail_get_start_frame(Sequence *seq, float frame_step, rctf return ((no_invisible_thumbs - 1) * frame_step) + seq->start; } -static void thumbnail_start_job(void *data, const short *stop, const short *do_update, const float *progress) +static void thumbnail_start_job(void *data, + short *stop, + short *UNUSED(do_update), + float *UNUSED(progress)) { ThumbnailDrawJob *tj = data; float start_frame, frame_step; @@ -1414,7 +1417,6 @@ static void thumbnail_start_job(void *data, const short *stop, const short *do_u } BLI_ghashIterator_step(&gh_iter); } - UNUSED_VARS(do_update, progress); } static SeqRenderData sequencer_thumbnail_context_init(const bContext *C) From 4d66cbd140b1648b79df0df695046cb718797b70 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 Sep 2021 14:48:01 +1000 Subject: [PATCH 0076/1500] Cleanup: spelling in comments --- intern/cycles/blender/blender_gpu_display.cpp | 6 +++--- intern/cycles/blender/blender_gpu_display.h | 6 +++--- intern/cycles/device/cpu/kernel.cpp | 2 +- intern/cycles/device/cuda/kernel.cpp | 2 +- intern/cycles/device/device.h | 2 +- intern/cycles/device/device_denoise.h | 2 +- intern/cycles/device/optix/device_impl.cpp | 4 ++-- intern/cycles/device/optix/device_impl.h | 2 +- intern/cycles/integrator/denoiser.h | 10 +++++----- intern/cycles/integrator/denoiser_device.cpp | 2 +- intern/cycles/integrator/denoiser_oidn.cpp | 4 ++-- intern/cycles/integrator/path_trace.cpp | 6 +++--- intern/cycles/integrator/path_trace.h | 16 ++++++++-------- intern/cycles/integrator/path_trace_work.h | 10 +++++----- intern/cycles/integrator/path_trace_work_gpu.cpp | 6 +++--- intern/cycles/integrator/render_scheduler.cpp | 8 ++++---- intern/cycles/integrator/render_scheduler.h | 10 +++++----- intern/cycles/integrator/shader_eval.cpp | 2 +- intern/cycles/integrator/work_balancer.h | 2 +- intern/cycles/integrator/work_tile_scheduler.cpp | 2 +- intern/cycles/integrator/work_tile_scheduler.h | 4 ++-- intern/cycles/kernel/closure/bssrdf.h | 2 +- intern/cycles/kernel/device/cuda/compat.h | 2 +- intern/cycles/kernel/device/optix/compat.h | 2 +- .../kernel/integrator/integrator_shade_volume.h | 8 ++++---- .../cycles/kernel/integrator/integrator_state.h | 4 ++-- intern/cycles/kernel/kernel_adaptive_sampling.h | 2 +- intern/cycles/kernel/kernel_film.h | 10 +++++----- intern/cycles/kernel/kernel_random.h | 4 ++-- intern/cycles/kernel/kernel_types.h | 2 +- intern/cycles/kernel/svm/svm_bevel.h | 2 +- intern/cycles/render/buffers.cpp | 2 +- intern/cycles/render/buffers.h | 6 +++--- intern/cycles/render/gpu_display.h | 10 +++++----- intern/cycles/render/session.cpp | 8 ++++---- intern/cycles/render/session.h | 6 +++--- intern/cycles/render/tile.cpp | 4 ++-- intern/cycles/render/tile.h | 2 +- .../intern/geometry_component_instances.cc | 4 ++-- source/blender/blenkernel/intern/gpencil_geom.cc | 2 +- source/blender/blenkernel/intern/mesh_convert.cc | 2 +- source/blender/editors/space_file/filesel.c | 2 +- .../editors/space_sequencer/sequencer_draw.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencillength.c | 4 ++-- .../geometry/nodes/node_geo_curve_sample.cc | 2 +- .../blender/windowmanager/intern/wm_dragdrop.c | 2 +- 46 files changed, 102 insertions(+), 102 deletions(-) diff --git a/intern/cycles/blender/blender_gpu_display.cpp b/intern/cycles/blender/blender_gpu_display.cpp index a79232af71f..aa2a9c17a8a 100644 --- a/intern/cycles/blender/blender_gpu_display.cpp +++ b/intern/cycles/blender/blender_gpu_display.cpp @@ -338,9 +338,9 @@ bool BlenderGPUDisplay::do_update_begin(const GPUDisplayParams ¶ms, * NOTE: Allocate the PBO for the the size which will fit the final render resolution (as in, * at a resolution divider 1. This was we don't need to recreate graphics interoperability * objects which are costly and which are tied to the specific underlying buffer size. - * The downside of this approach is that when graphics interopeability is not used we are sending - * too much data to GPU when resolution divider is not 1. */ - /* TODO(sergey): Investigate whether keeping the PBO exact size of the texute makes non-interop + * The downside of this approach is that when graphics interoperability is not used we are + * sending too much data to GPU when resolution divider is not 1. */ + /* TODO(sergey): Investigate whether keeping the PBO exact size of the texture makes non-interop * mode faster. */ const int buffer_width = params.full_size.x; const int buffer_height = params.full_size.y; diff --git a/intern/cycles/blender/blender_gpu_display.h b/intern/cycles/blender/blender_gpu_display.h index b7eddf0afa7..1014c96cee4 100644 --- a/intern/cycles/blender/blender_gpu_display.h +++ b/intern/cycles/blender/blender_gpu_display.h @@ -134,7 +134,7 @@ class BlenderGPUDisplay : public GPUDisplay { /* Make sure texture is allocated and its initial configuration is performed. */ bool gl_texture_resources_ensure(); - /* Ensure all runtime GPU resources needefd for drawing are allocated. + /* Ensure all runtime GPU resources needed for drawing are allocated. * Returns true if all resources needed for drawing are available. */ bool gl_draw_resources_ensure(); @@ -146,7 +146,7 @@ class BlenderGPUDisplay : public GPUDisplay { * NOTE: The texture needs to be bound. */ void texture_update_if_needed(); - /* Update vetrex buffer with new coordinates of vertex positions and texture coordinates. + /* Update vertex buffer with new coordinates of vertex positions and texture coordinates. * This buffer is used to render texture in the viewport. * * NOTE: The buffer needs to be bound. */ @@ -200,7 +200,7 @@ class BlenderGPUDisplay : public GPUDisplay { bool gl_draw_resource_creation_attempted_ = false; bool gl_draw_resources_created_ = false; - /* Vertex buffer which hold vertrices of a triangle fan which is textures with the texture + /* Vertex buffer which hold vertices of a triangle fan which is textures with the texture * holding the render result. */ uint vertex_buffer_ = 0; diff --git a/intern/cycles/device/cpu/kernel.cpp b/intern/cycles/device/cpu/kernel.cpp index 0ab58ff8600..91282390e27 100644 --- a/intern/cycles/device/cpu/kernel.cpp +++ b/intern/cycles/device/cpu/kernel.cpp @@ -44,7 +44,7 @@ CPUKernels::CPUKernels() /* Shader evaluation. */ REGISTER_KERNEL(shader_eval_displace), REGISTER_KERNEL(shader_eval_background), - /* Adaptive campling. */ + /* Adaptive sampling. */ REGISTER_KERNEL(adaptive_sampling_convergence_check), REGISTER_KERNEL(adaptive_sampling_filter_x), REGISTER_KERNEL(adaptive_sampling_filter_y), diff --git a/intern/cycles/device/cuda/kernel.cpp b/intern/cycles/device/cuda/kernel.cpp index 0ed20ddf8e6..a4a7bfabce0 100644 --- a/intern/cycles/device/cuda/kernel.cpp +++ b/intern/cycles/device/cuda/kernel.cpp @@ -28,7 +28,7 @@ void CUDADeviceKernels::load(CUDADevice *device) for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) { CUDADeviceKernel &kernel = kernels_[i]; - /* No megakernel used for GPU. */ + /* No mega-kernel used for GPU. */ if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { continue; } diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index 02b6edb56d0..399d5eb91df 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -213,7 +213,7 @@ class Device { * The interoperability comes here by the meaning that the device is capable of computing result * directly into an OpenGL (or other graphics library) buffer. */ - /* Check display si to be updated using graphics interoperability. + /* Check display is to be updated using graphics interoperability. * The interoperability can not be used is it is not supported by the device. But the device * might also force disable the interoperability if it detects that it will be slower than * copying pixels from the render buffer. */ diff --git a/intern/cycles/device/device_denoise.h b/intern/cycles/device/device_denoise.h index 02ee63fb0ad..dfdc7cc87b3 100644 --- a/intern/cycles/device/device_denoise.h +++ b/intern/cycles/device/device_denoise.h @@ -68,7 +68,7 @@ class DenoiseParams : public Node { /* Viewport start sample. */ int start_sample = 0; - /* Auxiliry passes. */ + /* Auxiliary passes. */ bool use_pass_albedo = true; bool use_pass_normal = true; diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index cd16b8c9f01..b54d423a183 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -571,7 +571,7 @@ class OptiXDevice::DenoiseContext { int pass_stride = -1; } guiding_params; - /* Number of input passes. Including the color and extra auxillary passes. */ + /* Number of input passes. Including the color and extra auxiliary passes. */ int num_input_passes = 0; bool use_pass_albedo = false; bool use_pass_normal = false; @@ -956,7 +956,7 @@ bool OptiXDevice::denoise_run(DenoiseContext &context, const DenoisePass &pass) /* Denoise in-place of the noisy input in the render buffers. */ output_layer = color_layer; - /* Finally run denonising. */ + /* Finally run denoising. */ OptixDenoiserParams params = {}; /* All parameters are disabled/zero. */ OptixDenoiserLayer image_layers = {}; image_layers.input = color_layer; diff --git a/intern/cycles/device/optix/device_impl.h b/intern/cycles/device/optix/device_impl.h index 742ae0f1bab..91ef52e0a5a 100644 --- a/intern/cycles/device/optix/device_impl.h +++ b/intern/cycles/device/optix/device_impl.h @@ -146,7 +146,7 @@ class OptiXDevice : public CUDADevice { /* Read guiding passes from the render buffers, preprocess them in a way which is expected by * OptiX and store in the guiding passes memory within the given context. * - * Pre=-processing of the guiding passes is to only hapopen once per context lifetime. DO not + * Pre=-processing of the guiding passes is to only happen once per context lifetime. DO not * preprocess them for every pass which is being denoised. */ bool denoise_filter_guiding_preprocess(DenoiseContext &context); diff --git a/intern/cycles/integrator/denoiser.h b/intern/cycles/integrator/denoiser.h index 3101b45e31b..b02bcbeb046 100644 --- a/intern/cycles/integrator/denoiser.h +++ b/intern/cycles/integrator/denoiser.h @@ -33,7 +33,7 @@ class Progress; /* Implementation of a specific denoising algorithm. * - * This class takes care of breaking down denosiing algorithm into a series of device calls or to + * This class takes care of breaking down denoising algorithm into a series of device calls or to * calls of an external API to denoise given input. * * TODO(sergey): Are we better with device or a queue here? */ @@ -53,7 +53,7 @@ class Denoiser { const DenoiseParams &get_params() const; /* Create devices and load kernels needed for denoising. - * The progress is used to communicate state when kenrels actually needs to be loaded. + * The progress is used to communicate state when kernels actually needs to be loaded. * * NOTE: The `progress` is an optional argument, can be nullptr. */ virtual bool load_kernels(Progress *progress); @@ -64,7 +64,7 @@ class Denoiser { * a lower resolution render into a bigger allocated buffer, which is used in viewport during * navigation and non-unit pixel size. Use that instead of render_buffers->params. * - * The buffer might be copming from a "foreign" device from what this denoise is created for. + * The buffer might be coming from a "foreign" device from what this denoise is created for. * This means that in general case the denoiser will make sure the input data is available on * the denoiser device, perform denoising, and put data back to the device where the buffer * came from. @@ -95,8 +95,8 @@ class Denoiser { * using OptiX denoiser and rendering on CPU. * * - No threading safety is ensured in this call. This means, that it is up to caller to ensure - * that there is no threadingconflict between denoising task lazily initializing the device and - * access to this device happen. */ + * that there is no threading-conflict between denoising task lazily initializing the device + * and access to this device happen. */ Device *get_denoiser_device() const; function is_cancelled_cb; diff --git a/intern/cycles/integrator/denoiser_device.cpp b/intern/cycles/integrator/denoiser_device.cpp index 8088cfd7800..e8361c50f2f 100644 --- a/intern/cycles/integrator/denoiser_device.cpp +++ b/intern/cycles/integrator/denoiser_device.cpp @@ -77,7 +77,7 @@ bool DeviceDenoiser::denoise_buffer(const BufferParams &buffer_params, local_render_buffers.reset(buffer_params); /* NOTE: The local buffer is allocated for an exact size of the effective render size, while - * the input render buffer is allcoated for the lowest resolution divider possible. So it is + * the input render buffer is allocated for the lowest resolution divider possible. So it is * important to only copy actually needed part of the input buffer. */ memcpy(local_render_buffers.buffer.data(), render_buffers->buffer.data(), diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp index 1b5a012ec87..7fc2b2b1892 100644 --- a/intern/cycles/integrator/denoiser_oidn.cpp +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -93,7 +93,7 @@ class OIDNPass { * Is required for albedo and normal passes. The color pass OIDN will perform auto-exposure, so * scaling is not needed for the color pass unless adaptive sampling is used. * - * NOTE: Do not scale the outout pass, as that requires to be a pointer in the original buffer. + * NOTE: Do not scale the output pass, as that requires to be a pointer in the original buffer. * All the scaling on the output needed for integration with adaptive sampling will happen * outside of generic pass handling. */ bool need_scale = false; @@ -479,7 +479,7 @@ class OIDNDenoiseContext { } if (num_samples_ == 1) { - /* If the avoid scaling if there is only one sample, to save up time (so we dont divide + /* If the avoid scaling if there is only one sample, to save up time (so we don't divide * buffer by 1). */ return false; } diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 6c02316ac2b..bc43747718d 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -177,7 +177,7 @@ void PathTrace::render(const RenderWork &render_work) void PathTrace::render_pipeline(RenderWork render_work) { - /* NOTE: Only check for "instant" cancel here. Ther user-requested cancel via progress is + /* NOTE: Only check for "instant" cancel here. The user-requested cancel via progress is * checked in Session and the work in the event of cancel is to be finished here. */ render_scheduler_.set_need_schedule_cryptomatte(device_scene_->data.film.cryptomatte_passes != @@ -680,7 +680,7 @@ void PathTrace::write_tile_buffer(const RenderWork &render_work) * * Tiles are written to a file during rendering, and written to the software at the end * of rendering (wither when all tiles are finished, or when rendering was requested to be - * cancelled). + * canceled). * * Important thing is: tile should be written to the software via callback only once. */ if (!has_multiple_tiles) { @@ -913,7 +913,7 @@ void PathTrace::process_full_buffer_from_disk(string_view filename) * ensure proper denoiser is used. */ set_denoiser_params(denoise_params); - /* Number of samples doesn't matter too much, since the sampels count pass will be used. */ + /* Number of samples doesn't matter too much, since the samples count pass will be used. */ denoiser_->denoise_buffer(full_frame_buffers.params, &full_frame_buffers, 0, false); render_state_.has_denoised_result = true; diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h index 78ca68c1198..fc7713e6df9 100644 --- a/intern/cycles/integrator/path_trace.h +++ b/intern/cycles/integrator/path_trace.h @@ -83,7 +83,7 @@ class PathTrace { void set_progress(Progress *progress); /* NOTE: This is a blocking call. Meaning, it will not return until given number of samples are - * rendered (or until rendering is requested to be cancelled). */ + * rendered (or until rendering is requested to be canceled). */ void render(const RenderWork &render_work); /* TODO(sergey): Decide whether denoiser is really a part of path tracer. Currently it is @@ -110,7 +110,7 @@ class PathTrace { /* Cancel rendering process as soon as possible, without waiting for full tile to be sampled. * Used in cases like reset of render session. * - * This is a blockign call, which returns as soon as there is no running `render_samples()` call. + * This is a blocking call, which returns as soon as there is no running `render_samples()` call. */ void cancel(); @@ -120,11 +120,11 @@ class PathTrace { * the data will be copied to the device of the given render buffers. */ void copy_to_render_buffers(RenderBuffers *render_buffers); - /* Copy happens via CPU side buffer: data will be copied from the device of the given rendetr + /* Copy happens via CPU side buffer: data will be copied from the device of the given render * buffers and will be copied to all devices of the path trace. */ void copy_from_render_buffers(RenderBuffers *render_buffers); - /* Copy render buffers of the big tile from the device to hsot. + /* Copy render buffers of the big tile from the device to host. * Return true if all copies are successful. */ bool copy_render_tile_from_device(); @@ -172,10 +172,10 @@ class PathTrace { * Is called during path tracing to communicate work-in-progress state of the final buffer. */ function tile_buffer_update_cb; - /* Callback which communicates final rendered buffer. Is called after pathtracing is done. */ + /* Callback which communicates final rendered buffer. Is called after path-tracing is done. */ function tile_buffer_write_cb; - /* Callback which initializes rendered buffer. Is called before pathtracing starts. + /* Callback which initializes rendered buffer. Is called before path-tracing starts. * * This is used for baking. */ function tile_buffer_read_cb; @@ -189,7 +189,7 @@ class PathTrace { protected: /* Actual implementation of the rendering pipeline. - * Calls steps in order, checking for the cancel to be requested inbetween. + * Calls steps in order, checking for the cancel to be requested in between. * * Is separate from `render()` to simplify dealing with the early outputs and keeping * `render_cancel_` in the consistent state. */ @@ -283,7 +283,7 @@ class PathTrace { * affects both resolution and stride as visible by the integrator kernels. */ int resolution_divider = 0; - /* Paramaters of the big tile with the current resolution divider applied. */ + /* Parameters of the big tile with the current resolution divider applied. */ BufferParams effective_big_tile_params; /* Denosier was run and there are denoised versions of the passes in the render buffers. */ diff --git a/intern/cycles/integrator/path_trace_work.h b/intern/cycles/integrator/path_trace_work.h index 97b97f3d888..8c9c8811199 100644 --- a/intern/cycles/integrator/path_trace_work.h +++ b/intern/cycles/integrator/path_trace_work.h @@ -39,8 +39,8 @@ class PathTraceWork { /* Create path trace work which fits best the device. * - * The cancel request flag is used for a cheap check whether cancel is to berformed as soon as - * possible. This could be, for rexample, request to cancel rendering on camera navigation in + * The cancel request flag is used for a cheap check whether cancel is to be performed as soon as + * possible. This could be, for example, request to cancel rendering on camera navigation in * viewport. */ static unique_ptr create(Device *device, Film *film, @@ -107,7 +107,7 @@ class PathTraceWork { /* Special version of the `copy_from_render_buffers()` which only copies denosied passes from the * given render buffers, leaving rest of the passes. * - * Same notes about device copying aplies to this call as well. */ + * Same notes about device copying applies to this call as well. */ void copy_from_denoised_render_buffers(const RenderBuffers *render_buffers); /* Copy render buffers to/from device using an appropriate device queue when needed so that @@ -119,7 +119,7 @@ class PathTraceWork { * things are executed in order with the `render_samples()`. */ virtual bool zero_render_buffers() = 0; - /* Access pixels rendered by this work and copy them to the coresponding location in the + /* Access pixels rendered by this work and copy them to the corresponding location in the * destination. * * NOTE: Does not perform copy of buffers from the device. Use `copy_render_tile_from_device()` @@ -182,7 +182,7 @@ class PathTraceWork { unique_ptr buffers_; /* Effective parameters of the full, big tile, and current work render buffer. - * The latter might be different from buffers_->params when there is a resolution divider + * The latter might be different from `buffers_->params` when there is a resolution divider * involved. */ BufferParams effective_full_params_; BufferParams effective_big_tile_params_; diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 10baf869aa6..135466becc6 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -498,7 +498,7 @@ void PathTraceWorkGPU::compact_states(const int num_active_paths) bool PathTraceWorkGPU::enqueue_work_tiles(bool &finished) { /* If there are existing paths wait them to go to intersect closest kernel, which will align the - * wavefront of the existing and newely added paths. */ + * wavefront of the existing and newly added paths. */ /* TODO: Check whether counting new intersection kernels here will have positive affect on the * performance. */ const DeviceKernel kernel = get_most_queued_kernel(); @@ -508,7 +508,7 @@ bool PathTraceWorkGPU::enqueue_work_tiles(bool &finished) int num_active_paths = get_num_active_paths(); - /* Don't schedule more work if cancelling. */ + /* Don't schedule more work if canceling. */ if (is_cancel_requested()) { if (num_active_paths == 0) { finished = true; @@ -729,7 +729,7 @@ void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display, gpu_display_rgba_half_.data_height != final_height) { gpu_display_rgba_half_.alloc(final_width, final_height); /* TODO(sergey): There should be a way to make sure device-side memory is allocated without - * transfering zeroes to the device. */ + * transferring zeroes to the device. */ queue_->zero_to_device(gpu_display_rgba_half_); } diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp index 4eb1dd941f9..3e5b3417a6a 100644 --- a/intern/cycles/integrator/render_scheduler.cpp +++ b/intern/cycles/integrator/render_scheduler.cpp @@ -233,7 +233,7 @@ void RenderScheduler::render_work_reschedule_on_cancel(RenderWork &render_work) const bool has_rendered_samples = get_num_rendered_samples() != 0; - /* Reset all fields of the previous work, canelling things like adaptive sampling filtering and + /* Reset all fields of the previous work, canceling things like adaptive sampling filtering and * denoising. * However, need to preserve write requests, since those will not be possible to recover and * writes are only to happen once. */ @@ -246,7 +246,7 @@ void RenderScheduler::render_work_reschedule_on_cancel(RenderWork &render_work) render_work.full.write = full_write; /* Do not write tile if it has zero samples it it, treat it similarly to all other tiles which - * got cancelled. */ + * got canceled. */ if (!state_.tile_result_was_written && has_rendered_samples) { render_work.tile.write = true; } @@ -817,7 +817,7 @@ int RenderScheduler::get_num_samples_to_path_trace() const int num_samples_to_render = min(num_samples_pot, max_num_samples_to_render); - /* When enough statistics is available and doing an offlien rendering prefer to keep device + /* When enough statistics is available and doing an offline rendering prefer to keep device * occupied. */ if (state_.occupancy_num_samples && (background_ || headless_)) { /* Keep occupancy at about 0.5 (this is more of an empirical figure which seems to match scenes @@ -874,7 +874,7 @@ int RenderScheduler::get_num_samples_during_navigation(int resolution_divider) c /* Always render 4 samples, even if scene is configured for less. * The idea here is to have enough information on the screen. Resolution divider of 2 allows us - * to have 4 time extra samples, so verall worst case timing is the same as the final resolution + * to have 4 time extra samples, so overall worst case timing is the same as the final resolution * at one sample. */ return 4; } diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h index 9c2d107e46d..b7b598fb10c 100644 --- a/intern/cycles/integrator/render_scheduler.h +++ b/intern/cycles/integrator/render_scheduler.h @@ -83,7 +83,7 @@ class RenderWork { } display; /* Re-balance multi-device scheduling after rendering this work. - * Note that the scheduler does not know anything abouce devices, so if there is only a single + * Note that the scheduler does not know anything about devices, so if there is only a single * device used, then it is up for the PathTracer to ignore the balancing. */ bool rebalance = false; @@ -203,7 +203,7 @@ class RenderScheduler { * extra work needs to be scheduled to denoise and write final result. */ bool done() const; - /* Update scheduling state for a newely scheduled work. + /* Update scheduling state for a newly scheduled work. * Takes care of things like checking whether work was ever denoised, tile was written and states * like that. */ void update_state_for_render_work(const RenderWork &render_work); @@ -235,7 +235,7 @@ class RenderScheduler { double guess_display_update_interval_in_seconds_for_num_samples_no_limit( int num_rendered_samples) const; - /* Calculate number of samples which can be rendered within current desred update interval which + /* Calculate number of samples which can be rendered within current desired update interval which * is calculated by `guess_update_interval_in_seconds()`. */ int calculate_num_samples_per_update() const; @@ -250,11 +250,11 @@ class RenderScheduler { /* Whether adaptive sampling convergence check and filter is to happen. */ bool work_need_adaptive_filter() const; - /* Calculate thretshold for adaptive sampling. */ + /* Calculate threshold for adaptive sampling. */ float work_adaptive_threshold() const; /* Check whether current work needs denoising. - * Denoising is not needed if the denoiser is not configured, or when denosiing is happening too + * Denoising is not needed if the denoiser is not configured, or when denoising is happening too * often. * * The delayed will be true when the denoiser is configured for use, but it was delayed for a diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp index 465b4a8d4da..d35ff4cd03f 100644 --- a/intern/cycles/integrator/shader_eval.cpp +++ b/intern/cycles/integrator/shader_eval.cpp @@ -71,7 +71,7 @@ bool ShaderEval::eval(const ShaderEvalType type, success = (device->info.type == DEVICE_CPU) ? eval_cpu(device, type, input, output) : eval_gpu(device, type, input, output); - /* Copy data back from device if not cancelled. */ + /* Copy data back from device if not canceled. */ if (success) { output.copy_from_device(0, 1, output.size()); read_output(output); diff --git a/intern/cycles/integrator/work_balancer.h b/intern/cycles/integrator/work_balancer.h index 94e20ecf054..fc5e561845e 100644 --- a/intern/cycles/integrator/work_balancer.h +++ b/intern/cycles/integrator/work_balancer.h @@ -32,7 +32,7 @@ struct WorkBalanceInfo { double weight = 1.0; }; -/* Balance work for an initial render interation, before any statistics is known. */ +/* Balance work for an initial render integration, before any statistics is known. */ void work_balance_do_initial(vector &work_balance_infos); /* Rebalance work after statistics has been accumulated. diff --git a/intern/cycles/integrator/work_tile_scheduler.cpp b/intern/cycles/integrator/work_tile_scheduler.cpp index 3fc99d5b74d..e6ada2f46ee 100644 --- a/intern/cycles/integrator/work_tile_scheduler.cpp +++ b/intern/cycles/integrator/work_tile_scheduler.cpp @@ -81,7 +81,7 @@ void WorkTileScheduler::reset_scheduler_state() bool WorkTileScheduler::get_work(KernelWorkTile *work_tile_, const int max_work_size) { /* Note that the `max_work_size` can be higher than the `max_num_path_states_`: this is because - * the path trace work can decice to use smaller tile sizes and greedily schedule multiple tiles, + * the path trace work can decide to use smaller tile sizes and greedily schedule multiple tiles, * improving overall device occupancy. * So the `max_num_path_states_` is a "scheduling unit", and the `max_work_size` is a "scheduling * limit". */ diff --git a/intern/cycles/integrator/work_tile_scheduler.h b/intern/cycles/integrator/work_tile_scheduler.h index e4c8f701259..85f11b601c7 100644 --- a/intern/cycles/integrator/work_tile_scheduler.h +++ b/intern/cycles/integrator/work_tile_scheduler.h @@ -64,7 +64,7 @@ class WorkTileScheduler { /* dimensions of the currently rendering image in pixels. */ int2 image_size_px_ = make_int2(0, 0); - /* Offset and stride of the buffer within which scheduing is happenning. + /* Offset and stride of the buffer within which scheduling is happening. * Will be passed over to the KernelWorkTile. */ int offset_, stride_; @@ -87,7 +87,7 @@ class WorkTileScheduler { * in the `get_work()`? */ int total_tiles_num_ = 0; - /* In the case when the number of sam[les in the `tile_size_` is lower than samples_num_ denotes + /* In the case when the number of samples in the `tile_size_` is lower than samples_num_ denotes * how many tiles are to be "stacked" to cover the entire requested range of samples. */ int num_tiles_per_sample_range_ = 0; diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index 0f9278bba89..e095314678a 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -76,7 +76,7 @@ ccl_device void bssrdf_setup_radius(Bssrdf *bssrdf, const ClosureType type, cons const float inv_eta = 1.0f / eta; const float F_dr = inv_eta * (-1.440f * inv_eta + 0.710f) + 0.668f + 0.0636f * eta; const float fourthirdA = (4.0f / 3.0f) * (1.0f + F_dr) / - (1.0f - F_dr); /* From Jensen's Fdr ratio formula. */ + (1.0f - F_dr); /* From Jensen's `Fdr` ratio formula. */ const float3 alpha_prime = make_float3( bssrdf_dipole_compute_alpha_prime(bssrdf->albedo.x, fourthirdA), diff --git a/intern/cycles/kernel/device/cuda/compat.h b/intern/cycles/kernel/device/cuda/compat.h index 665da43e1a1..3c85a8e7bd2 100644 --- a/intern/cycles/kernel/device/cuda/compat.h +++ b/intern/cycles/kernel/device/cuda/compat.h @@ -80,7 +80,7 @@ typedef unsigned long long uint64_t; #define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) #define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) -/* GPU warp synchronizaton */ +/* GPU warp synchronization. */ #define ccl_gpu_syncthreads() __syncthreads() #define ccl_gpu_ballot(predicate) __ballot_sync(0xFFFFFFFF, predicate) diff --git a/intern/cycles/kernel/device/optix/compat.h b/intern/cycles/kernel/device/optix/compat.h index 4e255a135c6..fb9e094b535 100644 --- a/intern/cycles/kernel/device/optix/compat.h +++ b/intern/cycles/kernel/device/optix/compat.h @@ -81,7 +81,7 @@ typedef unsigned long long uint64_t; #define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) #define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) -/* GPU warp synchronizaton */ +/* GPU warp synchronization. */ #define ccl_gpu_syncthreads() __syncthreads() #define ccl_gpu_ballot(predicate) __ballot_sync(0xFFFFFFFF, predicate) diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index 4a864b1e6ce..d44890f800e 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -30,7 +30,7 @@ CCL_NAMESPACE_BEGIN #ifdef __VOLUME__ -/* Events for probalistic scattering */ +/* Events for probabilistic scattering. */ typedef enum VolumeIntegrateEvent { VOLUME_PATH_SCATTERED = 0, @@ -228,8 +228,8 @@ ccl_device void volume_shadow_heterogeneous(INTEGRATOR_STATE_ARGS, /* compute attenuation over segment */ sd->P = new_P; if (shadow_volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &sigma_t)) { - /* Compute expf() only for every Nth step, to save some calculations - * because exp(a)*exp(b) = exp(a+b), also do a quick VOLUME_THROUGHPUT_EPSILON + /* Compute `expf()` only for every Nth step, to save some calculations + * because `exp(a)*exp(b) = exp(a+b)`, also do a quick #VOLUME_THROUGHPUT_EPSILON * check then. */ sum += (-sigma_t * dt); if ((i & 0x07) == 0) { /* ToDo: Other interval? */ @@ -648,7 +648,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous( } } - /* Write accumulated emisison. */ + /* Write accumulated emission. */ if (!is_zero(accum_emission)) { kernel_accum_emission( INTEGRATOR_STATE_PASS, result.indirect_throughput, accum_emission, render_buffer); diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h index 8cef9cf31e2..094446be02c 100644 --- a/intern/cycles/kernel/integrator/integrator_state.h +++ b/intern/cycles/kernel/integrator/integrator_state.h @@ -44,7 +44,7 @@ * INTEGRATOR_STATE_PASS_NULL: use to pass empty state to other functions. * * NOTE: if we end up with a device that passes no arguments, the leading comma will be a problem. - * Can solve it with more macros if we encouter it, but rather ugly so postpone for now. + * Can solve it with more macros if we encounter it, but rather ugly so postpone for now. */ #include "kernel/kernel_types.h" @@ -129,7 +129,7 @@ typedef struct IntegratorStateGPU { * * Note that there is a special access function for the shadow catcher state. This access is to * happen from a kernel which operates on a "main" path. Attempt to use shadow catcher accessors - * from a kernel which operates on a shadow catcher state will cause bad memory acces. */ + * from a kernel which operates on a shadow catcher state will cause bad memory access. */ #ifdef __KERNEL_CPU__ diff --git a/intern/cycles/kernel/kernel_adaptive_sampling.h b/intern/cycles/kernel/kernel_adaptive_sampling.h index 2bee12f0473..7d71907effe 100644 --- a/intern/cycles/kernel/kernel_adaptive_sampling.h +++ b/intern/cycles/kernel/kernel_adaptive_sampling.h @@ -60,7 +60,7 @@ ccl_device bool kernel_adaptive_sampling_convergence_check(const KernelGlobals * const float4 A = kernel_read_pass_float4(buffer + kernel_data.film.pass_adaptive_aux_buffer); if (!reset && A.w != 0.0f) { - /* If the pixel was considered converged, its state will not change in this kernmel. Early + /* If the pixel was considered converged, its state will not change in this kernel. Early * output before doing any math. * * TODO(sergey): On a GPU it might be better to keep thread alive for better coherency? */ diff --git a/intern/cycles/kernel/kernel_film.h b/intern/cycles/kernel/kernel_film.h index fa93f4830d1..715d764fb31 100644 --- a/intern/cycles/kernel/kernel_film.h +++ b/intern/cycles/kernel/kernel_film.h @@ -393,7 +393,7 @@ film_calculate_shadow_catcher(const KernelFilmConvert *ccl_restrict kfilm_conver ccl_global const float *in_catcher = buffer + kfilm_convert->pass_shadow_catcher; /* NOTE: It is possible that the Shadow Catcher pass is requested as an output without actual - * shadow catcher objects in the scene. In this case there will be no auxillary passes required + * shadow catcher objects in the scene. In this case there will be no auxiliary passes required * for the devision (to save up memory). So delay the asserts to this point so that the number of * samples check handles such configuration. */ kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); @@ -404,14 +404,14 @@ film_calculate_shadow_catcher(const KernelFilmConvert *ccl_restrict kfilm_conver ccl_global const float *in_matte = buffer + kfilm_convert->pass_shadow_catcher_matte; /* No scaling needed. The integration works in way that number of samples in the combined and - * shadow catcher passes are the same, and exposure is cancelled during the division. */ + * shadow catcher passes are the same, and exposure is canceled during the division. */ const float3 color_catcher = make_float3(in_catcher[0], in_catcher[1], in_catcher[2]); const float3 color_combined = make_float3(in_combined[0], in_combined[1], in_combined[2]); const float3 color_matte = make_float3(in_matte[0], in_matte[1], in_matte[2]); /* Need to ignore contribution of the matte object when doing division (otherwise there will be * artifacts caused by anti-aliasing). Since combined pass is used for adaptive sampling and need - * to contain matte objects, we subtrack matte objects contribution here. This is the same as if + * to contain matte objects, we subtract matte objects contribution here. This is the same as if * the matte objects were not accumulated to the combined pass. */ const float3 combined_no_matte = color_combined - color_matte; @@ -422,8 +422,8 @@ film_calculate_shadow_catcher(const KernelFilmConvert *ccl_restrict kfilm_conver const float alpha = film_transparency_to_alpha(transparency); /* Alpha-over on white using transparency of the combined pass. This allows to eliminate - * artifacts which are happenning on an edge of a shadow catcher when using transparent film. - * Note that we treat shadow catcher as straight alpha here because alpha got cancelled out + * artifacts which are happening on an edge of a shadow catcher when using transparent film. + * Note that we treat shadow catcher as straight alpha here because alpha got canceled out * during the division. */ const float3 pixel = (1.0f - alpha) * one_float3() + alpha * shadow_catcher; diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h index 41b7d76230a..240c92bf9d0 100644 --- a/intern/cycles/kernel/kernel_random.h +++ b/intern/cycles/kernel/kernel_random.h @@ -111,7 +111,7 @@ ccl_device_forceinline void path_rng_2D( } /** - * 1D hash recomended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 + * 1D hash recommended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 * See https://www.shadertoy.com/view/4tXyWN and https://www.shadertoy.com/view/XlGcRh * http://www.jcgt.org/published/0009/03/02/paper.pdf */ @@ -124,7 +124,7 @@ ccl_device_inline uint hash_iqint1(uint n) } /** - * 2D hash recomended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 + * 2D hash recommended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 * See https://www.shadertoy.com/view/4tXyWN and https://www.shadertoy.com/view/XlGcRh * http://www.jcgt.org/published/0009/03/02/paper.pdf */ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 927e60e8729..66b7310ab65 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -380,7 +380,7 @@ typedef enum PassType { * pass contains number of samples which contributed to the color components of the pass. * * PASS_SHADOW_CATCHER_SAMPLE_COUNT contains number of samples for which the path split - * happenned. + * happened. * * PASS_SHADOW_CATCHER_MATTE contains pass which contains non-catcher objects. This pass is to be * alpha-overed onto the backdrop (after multiplication). */ diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/svm_bevel.h index aab089d19ea..9d7ce202d49 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/svm_bevel.h @@ -255,7 +255,7 @@ ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, /* Multiple importance sample between 3 axes, power heuristic * found to be slightly better than balance heuristic. pdf_N - * in the MIS weight and denominator cancelled out. */ + * in the MIS weight and denominator canceled out. */ float w = pdf_N / (sqr(pdf_N) + sqr(pdf_T) + sqr(pdf_B)); if (isect.num_hits > LOCAL_MAX_HITS) { w *= isect.num_hits / (float)LOCAL_MAX_HITS; diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 1cdae3af7f5..186699596ac 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -300,7 +300,7 @@ void render_buffers_host_copy_denoised(RenderBuffers *dst, DCHECK_EQ(dst_params.width, src_params.width); /* TODO(sergey): More sanity checks to avoid buffer overrun. */ - /* Create a map of pass ofsets to be copied. + /* Create a map of pass offsets to be copied. * Assume offsets are different to allow copying passes between buffers with different set of * passes. */ diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h index c048234167d..a07e7289566 100644 --- a/intern/cycles/render/buffers.h +++ b/intern/cycles/render/buffers.h @@ -116,7 +116,7 @@ class BufferParams : public Node { * * When the scene passes are given, the buffer passes will be created from them and stored in * this params, and then params are updated for those passes. - * The `update_passes()` without parameters updates offsets and stries which are stored outside + * The `update_passes()` without parameters updates offsets and strides which are stored outside * of the passes. */ void update_passes(); void update_passes(const vector &scene_passes); @@ -140,7 +140,7 @@ class BufferParams : public Node { protected: void reset_pass_offset(); - /* Multipled by 2 to be able to store noisy and denoised pass types. */ + /* Multiplied by 2 to be able to store noisy and denoised pass types. */ static constexpr int kNumPassOffsets = PASS_NUM * 2; /* Indexed by an index derived from pass type and mode, indicates offset of the corresponding @@ -171,7 +171,7 @@ class RenderBuffers { /* Copy denoised passes form source to destination. * - * Buffer parameters are provided explicitly, allowing to copy pixelks between render buffers which + * Buffer parameters are provided explicitly, allowing to copy pixels between render buffers which * content corresponds to a render result at a non-unit resolution divider. * * `src_offset` allows to offset source pixel index which is used when a fraction of the source diff --git a/intern/cycles/render/gpu_display.h b/intern/cycles/render/gpu_display.h index cbe347895a1..a01348d28d5 100644 --- a/intern/cycles/render/gpu_display.h +++ b/intern/cycles/render/gpu_display.h @@ -117,7 +117,7 @@ class GPUDisplay { * * NOTE: The GPUDisplay should be marked for an update being in process with `update_begin()`. * - * NOTE: Texture buffer can not be mapped while graphics interopeability is active. This means + * NOTE: Texture buffer can not be mapped while graphics interoperability is active. This means * that `map_texture_buffer()` is not allowed between `graphics_interop_begin()` and * `graphics_interop_end()` calls. */ @@ -125,7 +125,7 @@ class GPUDisplay { /* Map pixels memory form texture to a buffer available for write from CPU. Width and height will * define a requested size of the texture to write to. * Upon success a non-null pointer is returned and the texture buffer is to be unmapped. - * If an error happens during mapping, or if mapoping is not supported by this GPU display a + * If an error happens during mapping, or if mapping is not supported by this GPU display a * null pointer is returned and the buffer is NOT to be unmapped. * * NOTE: Usually the implementation will rely on a GPU context of some sort, and the GPU context @@ -149,7 +149,7 @@ class GPUDisplay { * device API. */ DeviceGraphicsInteropDestination graphics_interop_get(); - /* (De)activate GPU display for graphics interoperability outside of regular display udpate + /* (De)activate GPU display for graphics interoperability outside of regular display update * routines. */ virtual void graphics_interop_activate(); virtual void graphics_interop_deactivate(); @@ -206,8 +206,8 @@ class GPUDisplay { GPUDisplayParams params_; /* Mark texture as its content has been updated. - * Used from places which knows that the texture content has been brough up-to-date, so that the - * drawing knows whether it can be performed, and whether drawing happenned with an up-to-date + * Used from places which knows that the texture content has been brought up-to-date, so that the + * drawing knows whether it can be performed, and whether drawing happened with an up-to-date * texture state. */ void mark_texture_updated(); diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 84407f8e6dd..c39232be2b0 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -116,7 +116,7 @@ Session::~Session() } #endif - /* Make sure path tracer is destroyed before the deviec. This is needed because destruction might + /* Make sure path tracer is destroyed before the device. This is needed because destruction might * need to access device for device memory free. */ /* TODO(sergey): Convert device to be unique_ptr, and rely on C++ to destruct objects in the * pre-defined order. */ @@ -612,7 +612,7 @@ void Session::collect_statistics(RenderStats *render_stats) } /* -------------------------------------------------------------------- - * Tile and tile pixels aceess. + * Tile and tile pixels access. */ bool Session::has_multiple_render_tiles() const @@ -650,7 +650,7 @@ bool Session::copy_render_tile_from_device() bool Session::get_render_tile_pixels(const string &pass_name, int num_components, float *pixels) { /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification - * is happenning while this function runs. */ + * is happening while this function runs. */ const BufferParams &buffer_params = path_trace_->get_render_tile_params(); @@ -689,7 +689,7 @@ bool Session::set_render_tile_pixels(const string &pass_name, const float *pixels) { /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification - * is happenning while this function runs. */ + * is happening while this function runs. */ const BufferPass *pass = buffer_params_.find_pass(pass_name); if (!pass) { diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index 492cfdd1c09..5623604bfe8 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -120,7 +120,7 @@ class Session { /* Callback is invoked by tile manager whenever on-dist tiles storage file is closed after * writing. Allows an engine integration to keep track of those files without worry about - * transfering the information when it needs to re-create session during rendering. */ + * transferring the information when it needs to re-create session during rendering. */ function full_buffer_written_cb; explicit Session(const SessionParams ¶ms, const SceneParams &scene_params); @@ -128,7 +128,7 @@ class Session { void start(); - /* When quick cancel is requested path tracing is cancelles as soon as possible, without waiting + /* When quick cancel is requested path tracing is cancels as soon as possible, without waiting * for the buffer to be uniformly sampled. */ void cancel(bool quick = false); @@ -154,7 +154,7 @@ class Session { void collect_statistics(RenderStats *stats); /* -------------------------------------------------------------------- - * Tile and tile pixels aceess. + * Tile and tile pixels access. */ bool has_multiple_render_tiles() const; diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index eed75cc2372..28910bffa7b 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -282,7 +282,7 @@ static bool buffer_params_from_image_spec_atttributes(BufferParams *buffer_param /* Configure image specification for the given buffer parameters and passes. * - * Image channels will ber strictly ordered to match content of corresponding buffer, and the + * Image channels will be strictly ordered to match content of corresponding buffer, and the * metadata will be set so that the render buffers and passes can be reconstructed from it. * * If the tile size different from (0, 0) the image specification will be configured to use the @@ -358,7 +358,7 @@ void TileManager::update(const BufferParams ¶ms, const Scene *scene) buffer_params_ = params; - /* TODO(sergey): Proper Error handling, so that if configuration has failed we dont' attempt to + /* TODO(sergey): Proper Error handling, so that if configuration has failed we don't attempt to * write to a partially configured file. */ configure_image_spec_from_buffer(&write_state_.image_spec, buffer_params_, tile_size_); diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h index 124d0b3652c..71b9e966278 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/render/tile.h @@ -94,7 +94,7 @@ class TileManager { * The file will be considered final, all handles to it will be closed. */ void finish_write_tiles(); - /* Check whether any tile ahs been written to disk. */ + /* Check whether any tile has been written to disk. */ inline bool has_written_tiles() const { return write_state_.num_tiles_written != 0; diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 4c10f5398b7..9479d012cb8 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -154,8 +154,8 @@ void InstancesComponent::ensure_geometry_instances() /** * With write access to the instances component, the data in the instanced geometry sets can be - * changed. This is a function on the component rather than each reference to ensure const - * correct-ness for that reason. + * changed. This is a function on the component rather than each reference to ensure `const` + * correctness for that reason. */ GeometrySet &InstancesComponent::geometry_set_from_reference(const int reference_index) { diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index d7c906be18e..976b26a1f3a 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -600,7 +600,7 @@ static bool BKE_gpencil_stroke_extra_points(bGPDstroke *gps, * \param dist: Length of the added section. * \param overshoot_fac: Relative length of the curve which is used to determine the extension. * \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End) - * \param follow_curvature: True for appproximating curvature of given overshoot. + * \param follow_curvature: True for approximating curvature of given overshoot. * \param extra_point_count: When follow_curvature is true, use this amount of extra points */ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 07dc6db05aa..467f7d4543e 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -1118,7 +1118,7 @@ static Mesh *mesh_new_from_mball_object(Object *object) * balls and all evaluated child meta balls (since polygonization is only stored in the mother * ball). * - * We create empty mesh so scripters don't run into None objects. */ + * Create empty mesh so script-authors don't run into None objects. */ if (!DEG_is_evaluated_object(object) || object->runtime.curve_cache == nullptr || BLI_listbase_is_empty(&object->runtime.curve_cache->disp)) { return (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 11b06d2b414..f7bdb4326a5 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -1271,7 +1271,7 @@ void file_params_rename_end(wmWindowManager *wm, /* Ensure smooth-scroll timer is active, even if not needed, because that way rename state is * handled properly. */ file_params_invoke_rename_postscroll(wm, win, sfile); - /* Also always activate the rename file, even if renaming was cancelled. */ + /* Also always activate the rename file, even if renaming was canceled. */ file_params_renamefile_activate(sfile, params); } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 6f10a0f6c9e..53f1c35776c 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1757,7 +1757,7 @@ static void draw_seq_strip_thumbnail(View2D *v2d, } /* Store recently rendered frames, so they can be reused when zooming. */ else if (!sequencer_thumbnail_v2d_is_navigating(C)) { - /* Clear images in frame range occupied bynew thumbnail. */ + /* Clear images in frame range occupied by new thumbnail. */ last_displayed_thumbnails_list_cleanup( last_displayed_thumbnails, thumb_x_start, thumb_x_end); /* Insert new thumbnail frame to list. */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index e4a48925bf0..80b60547e92 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -140,8 +140,8 @@ static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke /* HACK: The second #overshoot_fac needs to be adjusted because it is not * done in the same stretch call, because it can have a different length. * The adjustment needs to be stable when - * ceil(overshoot_fac*(gps->totpoints - 2)) is used in stretch and never - * produce a result highter than totpoints - 2. */ + * `ceil(overshoot_fac*(gps->totpoints - 2))` is used in stretch and never + * produce a result higher than `totpoints - 2`. */ const float second_overshoot_fac = lmd->overshoot_fac * (totpoints - 2) / ((float)gps->totpoints - 2) * (1.0f - 0.1f / (totpoints - 1.0f)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 245394d3057..ac0cd510ffa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -155,7 +155,7 @@ class SampleCurveFunction : public fn::MultiFunction { spline_indices[i] = std::max(index, 0); } - /* Storing lookups in an array is unecessary but will simplify custom attribute transfer. */ + /* Storing lookups in an array is unnecessary but will simplify custom attribute transfer. */ Array lookups(mask.min_array_size()); for (const int i : mask) { const float length_in_spline = lengths[i] - spline_lengths_[spline_indices[i]]; diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index b6a04251ffb..6585349c83c 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -428,7 +428,7 @@ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) } /** - * \brief Free asset ID imported for cancelled drop. + * \brief Free asset ID imported for canceled drop. * * If the asset was imported (linked/appended) using #WM_drag_get_local_ID_or_import_from_asset()` * (typically via a #wmDropBox.copy() callback), we want the ID to be removed again if the drop From 3fa64263927f2d8ac595ba411091321c47409ea4 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 21 Sep 2021 22:59:10 -0700 Subject: [PATCH 0077/1500] Extern: Add TinyGLTF to load XR controller model The XR_MSFT_controller_model OpenXR extension provides a glTF controller model that can be displayed to users during a VR session. There are plans to support this in D10948, which will greatly improve VR immersion when using a compatible OpenXR runtime. TinyGLTF (https://github.com/syoyo/tinygltf) was agreed upon as a simple and sufficient solution for loading this glTF controller model, which will be performed at the GHOST abstraction layer. Although by default it has two additional dependencies, stb and json, stb can be excluded by defining TINYGLTF_NO_STB_IMAGE and TINYGLTF_NO_STB_IMAGE_WRITE whereas json will be added as a separate extern lib in D12567. Reviewed By: Severin Differential Revision: https://developer.blender.org/D12344 --- extern/tinygltf/README.blender | 5 + extern/tinygltf/tiny_gltf.h | 7760 ++++++++++++++++++++++++++++++++ 2 files changed, 7765 insertions(+) create mode 100644 extern/tinygltf/README.blender create mode 100644 extern/tinygltf/tiny_gltf.h diff --git a/extern/tinygltf/README.blender b/extern/tinygltf/README.blender new file mode 100644 index 00000000000..fe23d320b77 --- /dev/null +++ b/extern/tinygltf/README.blender @@ -0,0 +1,5 @@ +Project: TinyGLTF +URL: https://github.com/syoyo/tinygltf +License: MIT +Upstream version: 2.5.0, 19a41d20ec0 +Local modifications: None diff --git a/extern/tinygltf/tiny_gltf.h b/extern/tinygltf/tiny_gltf.h new file mode 100644 index 00000000000..185bb0daa98 --- /dev/null +++ b/extern/tinygltf/tiny_gltf.h @@ -0,0 +1,7760 @@ +// +// Header-only tiny glTF 2.0 loader and serializer. +// +// +// The MIT License (MIT) +// +// Copyright (c) 2015 - Present Syoyo Fujita, Aurélien Chatelain and many +// contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Version: +// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is. +// - v2.4.3 Fix null object output when when material has all default +// parameters. +// - v2.4.2 Decode percent-encoded URI. +// - v2.4.1 Fix some glTF object class does not have `extensions` and/or +// `extras` property. +// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone). +// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1. +// - v2.3.0 Modified Material representation according to glTF 2.0 schema +// (and introduced TextureInfo class) +// Change the behavior of `Value::IsNumber`. It return true either the +// value is int or real. +// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks +// to @Ybalrid) +// - v2.1.0 Add draco compression. +// - v2.0.1 Add comparsion feature(Thanks to @Selmar). +// - v2.0.0 glTF 2.0!. +// +// Tiny glTF loader is using following third party libraries: +// +// - jsonhpp: C++ JSON library. +// - base64: base64 decode/encode library. +// - stb_image: Image loading library. +// +#ifndef TINY_GLTF_H_ +#define TINY_GLTF_H_ + +#include +#include +#include // std::fabs +#include +#include +#include +#include +#include +#include +#include + +#ifndef TINYGLTF_USE_CPP14 +#include +#endif + +#ifdef __ANDROID__ +#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS +#include +#endif +#endif + +#ifdef __GNUC__ +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8)) +#define TINYGLTF_NOEXCEPT +#else +#define TINYGLTF_NOEXCEPT noexcept +#endif +#else +#define TINYGLTF_NOEXCEPT noexcept +#endif + +#define DEFAULT_METHODS(x) \ + ~x() = default; \ + x(const x &) = default; \ + x(x &&) TINYGLTF_NOEXCEPT = default; \ + x &operator=(const x &) = default; \ + x &operator=(x &&) TINYGLTF_NOEXCEPT = default; + +namespace tinygltf { + +#define TINYGLTF_MODE_POINTS (0) +#define TINYGLTF_MODE_LINE (1) +#define TINYGLTF_MODE_LINE_LOOP (2) +#define TINYGLTF_MODE_LINE_STRIP (3) +#define TINYGLTF_MODE_TRIANGLES (4) +#define TINYGLTF_MODE_TRIANGLE_STRIP (5) +#define TINYGLTF_MODE_TRIANGLE_FAN (6) + +#define TINYGLTF_COMPONENT_TYPE_BYTE (5120) +#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121) +#define TINYGLTF_COMPONENT_TYPE_SHORT (5122) +#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123) +#define TINYGLTF_COMPONENT_TYPE_INT (5124) +#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125) +#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126) +#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130) + +#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728) +#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729) +#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984) +#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985) +#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986) +#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987) + +#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497) +#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071) +#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648) + +// Redeclarations of the above for technique.parameters. +#define TINYGLTF_PARAMETER_TYPE_BYTE (5120) +#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121) +#define TINYGLTF_PARAMETER_TYPE_SHORT (5122) +#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123) +#define TINYGLTF_PARAMETER_TYPE_INT (5124) +#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125) +#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126) + +#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664) +#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665) +#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666) + +#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667) +#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668) +#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669) + +#define TINYGLTF_PARAMETER_TYPE_BOOL (35670) +#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671) +#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672) +#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673) + +#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674) +#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675) +#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676) + +#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678) + +// End parameter types + +#define TINYGLTF_TYPE_VEC2 (2) +#define TINYGLTF_TYPE_VEC3 (3) +#define TINYGLTF_TYPE_VEC4 (4) +#define TINYGLTF_TYPE_MAT2 (32 + 2) +#define TINYGLTF_TYPE_MAT3 (32 + 3) +#define TINYGLTF_TYPE_MAT4 (32 + 4) +#define TINYGLTF_TYPE_SCALAR (64 + 1) +#define TINYGLTF_TYPE_VECTOR (64 + 4) +#define TINYGLTF_TYPE_MATRIX (64 + 16) + +#define TINYGLTF_IMAGE_FORMAT_JPEG (0) +#define TINYGLTF_IMAGE_FORMAT_PNG (1) +#define TINYGLTF_IMAGE_FORMAT_BMP (2) +#define TINYGLTF_IMAGE_FORMAT_GIF (3) + +#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406) +#define TINYGLTF_TEXTURE_FORMAT_RGB (6407) +#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408) +#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409) +#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410) + +#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553) +#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121) + +#define TINYGLTF_TARGET_ARRAY_BUFFER (34962) +#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963) + +#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633) +#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632) + +#define TINYGLTF_DOUBLE_EPS (1.e-12) +#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS) + +#ifdef __ANDROID__ +#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS +AAssetManager *asset_manager = nullptr; +#endif +#endif + +typedef enum { + NULL_TYPE, + REAL_TYPE, + INT_TYPE, + BOOL_TYPE, + STRING_TYPE, + ARRAY_TYPE, + BINARY_TYPE, + OBJECT_TYPE +} Type; + +static inline int32_t GetComponentSizeInBytes(uint32_t componentType) { + if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) { + return 1; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { + return 1; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) { + return 2; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { + return 2; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) { + return 4; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) { + return 4; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) { + return 4; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) { + return 8; + } else { + // Unknown componenty type + return -1; + } +} + +static inline int32_t GetNumComponentsInType(uint32_t ty) { + if (ty == TINYGLTF_TYPE_SCALAR) { + return 1; + } else if (ty == TINYGLTF_TYPE_VEC2) { + return 2; + } else if (ty == TINYGLTF_TYPE_VEC3) { + return 3; + } else if (ty == TINYGLTF_TYPE_VEC4) { + return 4; + } else if (ty == TINYGLTF_TYPE_MAT2) { + return 4; + } else if (ty == TINYGLTF_TYPE_MAT3) { + return 9; + } else if (ty == TINYGLTF_TYPE_MAT4) { + return 16; + } else { + // Unknown componenty type + return -1; + } +} + +// TODO(syoyo): Move these functions to TinyGLTF class +bool IsDataURI(const std::string &in); +bool DecodeDataURI(std::vector *out, std::string &mime_type, + const std::string &in, size_t reqBytes, bool checkSize); + +#ifdef __clang__ +#pragma clang diagnostic push +// Suppress warning for : static Value null_value +// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// Simple class to represent JSON object +class Value { + public: + typedef std::vector Array; + typedef std::map Object; + + Value() + : type_(NULL_TYPE), + int_value_(0), + real_value_(0.0), + boolean_value_(false) {} + + explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; } + explicit Value(int i) : type_(INT_TYPE) { + int_value_ = i; + real_value_ = i; + } + explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; } + explicit Value(const std::string &s) : type_(STRING_TYPE) { + string_value_ = s; + } + explicit Value(std::string &&s) + : type_(STRING_TYPE), string_value_(std::move(s)) {} + explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) { + binary_value_.resize(n); + memcpy(binary_value_.data(), p, n); + } + explicit Value(std::vector &&v) noexcept + : type_(BINARY_TYPE), + binary_value_(std::move(v)) {} + explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; } + explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE), + array_value_(std::move(a)) {} + + explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; } + explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE), + object_value_(std::move(o)) {} + + DEFAULT_METHODS(Value) + + char Type() const { return static_cast(type_); } + + bool IsBool() const { return (type_ == BOOL_TYPE); } + + bool IsInt() const { return (type_ == INT_TYPE); } + + bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); } + + bool IsReal() const { return (type_ == REAL_TYPE); } + + bool IsString() const { return (type_ == STRING_TYPE); } + + bool IsBinary() const { return (type_ == BINARY_TYPE); } + + bool IsArray() const { return (type_ == ARRAY_TYPE); } + + bool IsObject() const { return (type_ == OBJECT_TYPE); } + + // Use this function if you want to have number value as double. + double GetNumberAsDouble() const { + if (type_ == INT_TYPE) { + return double(int_value_); + } else { + return real_value_; + } + } + + // Use this function if you want to have number value as int. + // TODO(syoyo): Support int value larger than 32 bits + int GetNumberAsInt() const { + if (type_ == REAL_TYPE) { + return int(real_value_); + } else { + return int_value_; + } + } + + // Accessor + template + const T &Get() const; + template + T &Get(); + + // Lookup value from an array + const Value &Get(int idx) const { + static Value null_value; + assert(IsArray()); + assert(idx >= 0); + return (static_cast(idx) < array_value_.size()) + ? array_value_[static_cast(idx)] + : null_value; + } + + // Lookup value from a key-value pair + const Value &Get(const std::string &key) const { + static Value null_value; + assert(IsObject()); + Object::const_iterator it = object_value_.find(key); + return (it != object_value_.end()) ? it->second : null_value; + } + + size_t ArrayLen() const { + if (!IsArray()) return 0; + return array_value_.size(); + } + + // Valid only for object type. + bool Has(const std::string &key) const { + if (!IsObject()) return false; + Object::const_iterator it = object_value_.find(key); + return (it != object_value_.end()) ? true : false; + } + + // List keys + std::vector Keys() const { + std::vector keys; + if (!IsObject()) return keys; // empty + + for (Object::const_iterator it = object_value_.begin(); + it != object_value_.end(); ++it) { + keys.push_back(it->first); + } + + return keys; + } + + size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); } + + bool operator==(const tinygltf::Value &other) const; + + protected: + int type_ = NULL_TYPE; + + int int_value_ = 0; + double real_value_ = 0.0; + std::string string_value_; + std::vector binary_value_; + Array array_value_; + Object object_value_; + bool boolean_value_ = false; +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#define TINYGLTF_VALUE_GET(ctype, var) \ + template <> \ + inline const ctype &Value::Get() const { \ + return var; \ + } \ + template <> \ + inline ctype &Value::Get() { \ + return var; \ + } +TINYGLTF_VALUE_GET(bool, boolean_value_) +TINYGLTF_VALUE_GET(double, real_value_) +TINYGLTF_VALUE_GET(int, int_value_) +TINYGLTF_VALUE_GET(std::string, string_value_) +TINYGLTF_VALUE_GET(std::vector, binary_value_) +TINYGLTF_VALUE_GET(Value::Array, array_value_) +TINYGLTF_VALUE_GET(Value::Object, object_value_) +#undef TINYGLTF_VALUE_GET + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat" +#pragma clang diagnostic ignored "-Wpadded" +#endif + +/// Agregate object for representing a color +using ColorValue = std::array; + +// === legacy interface ==== +// TODO(syoyo): Deprecate `Parameter` class. +struct Parameter { + bool bool_value = false; + bool has_number_value = false; + std::string string_value; + std::vector number_array; + std::map json_double_value; + double number_value = 0.0; + + // context sensitive methods. depending the type of the Parameter you are + // accessing, these are either valid or not + // If this parameter represent a texture map in a material, will return the + // texture index + + /// Return the index of a texture if this Parameter is a texture map. + /// Returned value is only valid if the parameter represent a texture from a + /// material + int TextureIndex() const { + const auto it = json_double_value.find("index"); + if (it != std::end(json_double_value)) { + return int(it->second); + } + return -1; + } + + /// Return the index of a texture coordinate set if this Parameter is a + /// texture map. Returned value is only valid if the parameter represent a + /// texture from a material + int TextureTexCoord() const { + const auto it = json_double_value.find("texCoord"); + if (it != std::end(json_double_value)) { + return int(it->second); + } + // As per the spec, if texCoord is ommited, this parameter is 0 + return 0; + } + + /// Return the scale of a texture if this Parameter is a normal texture map. + /// Returned value is only valid if the parameter represent a normal texture + /// from a material + double TextureScale() const { + const auto it = json_double_value.find("scale"); + if (it != std::end(json_double_value)) { + return it->second; + } + // As per the spec, if scale is ommited, this paramter is 1 + return 1; + } + + /// Return the strength of a texture if this Parameter is a an occlusion map. + /// Returned value is only valid if the parameter represent an occlusion map + /// from a material + double TextureStrength() const { + const auto it = json_double_value.find("strength"); + if (it != std::end(json_double_value)) { + return it->second; + } + // As per the spec, if strenghth is ommited, this parameter is 1 + return 1; + } + + /// Material factor, like the roughness or metalness of a material + /// Returned value is only valid if the parameter represent a texture from a + /// material + double Factor() const { return number_value; } + + /// Return the color of a material + /// Returned value is only valid if the parameter represent a texture from a + /// material + ColorValue ColorFactor() const { + return { + {// this agregate intialize the std::array object, and uses C++11 RVO. + number_array[0], number_array[1], number_array[2], + (number_array.size() > 3 ? number_array[3] : 1.0)}}; + } + + Parameter() = default; + DEFAULT_METHODS(Parameter) + bool operator==(const Parameter &) const; +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +typedef std::map ParameterMap; +typedef std::map ExtensionMap; + +struct AnimationChannel { + int sampler; // required + int target_node; // required (index of the node to target) + std::string target_path; // required in ["translation", "rotation", "scale", + // "weights"] + Value extras; + ExtensionMap extensions; + ExtensionMap target_extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + std::string target_extensions_json_string; + + AnimationChannel() : sampler(-1), target_node(-1) {} + DEFAULT_METHODS(AnimationChannel) + bool operator==(const AnimationChannel &) const; +}; + +struct AnimationSampler { + int input; // required + int output; // required + std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined + // string. default "LINEAR" + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {} + DEFAULT_METHODS(AnimationSampler) + bool operator==(const AnimationSampler &) const; +}; + +struct Animation { + std::string name; + std::vector channels; + std::vector samplers; + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Animation() = default; + DEFAULT_METHODS(Animation) + bool operator==(const Animation &) const; +}; + +struct Skin { + std::string name; + int inverseBindMatrices; // required here but not in the spec + int skeleton; // The index of the node used as a skeleton root + std::vector joints; // Indices of skeleton nodes + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Skin() { + inverseBindMatrices = -1; + skeleton = -1; + } + DEFAULT_METHODS(Skin) + bool operator==(const Skin &) const; +}; + +struct Sampler { + std::string name; + // glTF 2.0 spec does not define default value for `minFilter` and + // `magFilter`. Set -1 in TinyGLTF(issue #186) + int minFilter = + -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR", + // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST", + // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"] + int magFilter = + -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"] + int wrapS = + TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", + // "REPEAT"], default "REPEAT" + int wrapT = + TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", + // "REPEAT"], default "REPEAT" + //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used. + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Sampler() + : minFilter(-1), + magFilter(-1), + wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT), + wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {} + DEFAULT_METHODS(Sampler) + bool operator==(const Sampler &) const; +}; + +struct Image { + std::string name; + int width; + int height; + int component; + int bits; // bit depth per channel. 8(byte), 16 or 32. + int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually + // UBYTE(bits = 8) or USHORT(bits = 16) + std::vector image; + int bufferView; // (required if no uri) + std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", + // "image/bmp", "image/gif"] + std::string uri; // (required if no mimeType) uri is not decoded(e.g. + // whitespace may be represented as %20) + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg + // compressed for "image/jpeg" mime) This feature is good if you use custom + // image loader function. (e.g. delayed decoding of images for faster glTF + // parsing) Default parser for Image does not provide as-is loading feature at + // the moment. (You can manipulate this by providing your own LoadImageData + // function) + bool as_is; + + Image() : as_is(false) { + bufferView = -1; + width = -1; + height = -1; + component = -1; + bits = -1; + pixel_type = -1; + } + DEFAULT_METHODS(Image) + + bool operator==(const Image &) const; +}; + +struct Texture { + std::string name; + + int sampler; + int source; + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Texture() : sampler(-1), source(-1) {} + DEFAULT_METHODS(Texture) + + bool operator==(const Texture &) const; +}; + +struct TextureInfo { + int index = -1; // required. + int texCoord; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + TextureInfo() : index(-1), texCoord(0) {} + DEFAULT_METHODS(TextureInfo) + bool operator==(const TextureInfo &) const; +}; + +struct NormalTextureInfo { + int index = -1; // required + int texCoord; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. + double scale; // scaledNormal = normalize(( + // * 2.0 - 1.0) * vec3(, , 1.0)) + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {} + DEFAULT_METHODS(NormalTextureInfo) + bool operator==(const NormalTextureInfo &) const; +}; + +struct OcclusionTextureInfo { + int index = -1; // required + int texCoord; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. + double strength; // occludedColor = lerp(color, color * , ) + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {} + DEFAULT_METHODS(OcclusionTextureInfo) + bool operator==(const OcclusionTextureInfo &) const; +}; + +// pbrMetallicRoughness class defined in glTF 2.0 spec. +struct PbrMetallicRoughness { + std::vector baseColorFactor; // len = 4. default [1,1,1,1] + TextureInfo baseColorTexture; + double metallicFactor; // default 1 + double roughnessFactor; // default 1 + TextureInfo metallicRoughnessTexture; + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + PbrMetallicRoughness() + : baseColorFactor(std::vector{1.0, 1.0, 1.0, 1.0}), + metallicFactor(1.0), + roughnessFactor(1.0) {} + DEFAULT_METHODS(PbrMetallicRoughness) + bool operator==(const PbrMetallicRoughness &) const; +}; + +// Each extension should be stored in a ParameterMap. +// members not in the values could be included in the ParameterMap +// to keep a single material model +struct Material { + std::string name; + + std::vector emissiveFactor; // length 3. default [0, 0, 0] + std::string alphaMode; // default "OPAQUE" + double alphaCutoff; // default 0.5 + bool doubleSided; // default false; + + PbrMetallicRoughness pbrMetallicRoughness; + + NormalTextureInfo normalTexture; + OcclusionTextureInfo occlusionTexture; + TextureInfo emissiveTexture; + + // For backward compatibility + // TODO(syoyo): Remove `values` and `additionalValues` in the next release. + ParameterMap values; + ParameterMap additionalValues; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {} + DEFAULT_METHODS(Material) + + bool operator==(const Material &) const; +}; + +struct BufferView { + std::string name; + int buffer{-1}; // Required + size_t byteOffset{0}; // minimum 0, default 0 + size_t byteLength{0}; // required, minimum 1. 0 = invalid + size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 = + // understood to be tightly packed + int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices + // or atttribs. Could be 0 for other data + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + bool dracoDecoded{false}; // Flag indicating this has been draco decoded + + BufferView() + : buffer(-1), + byteOffset(0), + byteLength(0), + byteStride(0), + target(0), + dracoDecoded(false) {} + DEFAULT_METHODS(BufferView) + bool operator==(const BufferView &) const; +}; + +struct Accessor { + int bufferView; // optional in spec but required here since sparse accessor + // are not supported + std::string name; + size_t byteOffset; + bool normalized; // optional. + int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_*** + size_t count; // required + int type; // (required) One of TINYGLTF_TYPE_*** .. + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + std::vector + minValues; // optional. integer value is promoted to double + std::vector + maxValues; // optional. integer value is promoted to double + + struct { + int count; + bool isSparse; + struct { + int byteOffset; + int bufferView; + int componentType; // a TINYGLTF_COMPONENT_TYPE_ value + } indices; + struct { + int bufferView; + int byteOffset; + } values; + } sparse; + + /// + /// Utility function to compute byteStride for a given bufferView object. + /// Returns -1 upon invalid glTF value or parameter configuration. + /// + int ByteStride(const BufferView &bufferViewObject) const { + if (bufferViewObject.byteStride == 0) { + // Assume data is tightly packed. + int componentSizeInBytes = + GetComponentSizeInBytes(static_cast(componentType)); + if (componentSizeInBytes <= 0) { + return -1; + } + + int numComponents = GetNumComponentsInType(static_cast(type)); + if (numComponents <= 0) { + return -1; + } + + return componentSizeInBytes * numComponents; + } else { + // Check if byteStride is a mulple of the size of the accessor's component + // type. + int componentSizeInBytes = + GetComponentSizeInBytes(static_cast(componentType)); + if (componentSizeInBytes <= 0) { + return -1; + } + + if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) { + return -1; + } + return static_cast(bufferViewObject.byteStride); + } + + // unreachable return 0; + } + + Accessor() + : bufferView(-1), + byteOffset(0), + normalized(false), + componentType(-1), + count(0), + type(-1) { + sparse.isSparse = false; + } + DEFAULT_METHODS(Accessor) + bool operator==(const tinygltf::Accessor &) const; +}; + +struct PerspectiveCamera { + double aspectRatio; // min > 0 + double yfov; // required. min > 0 + double zfar; // min > 0 + double znear; // required. min > 0 + + PerspectiveCamera() + : aspectRatio(0.0), + yfov(0.0), + zfar(0.0) // 0 = use infinite projecton matrix + , + znear(0.0) {} + DEFAULT_METHODS(PerspectiveCamera) + bool operator==(const PerspectiveCamera &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct OrthographicCamera { + double xmag; // required. must not be zero. + double ymag; // required. must not be zero. + double zfar; // required. `zfar` must be greater than `znear`. + double znear; // required + + OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {} + DEFAULT_METHODS(OrthographicCamera) + bool operator==(const OrthographicCamera &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct Camera { + std::string type; // required. "perspective" or "orthographic" + std::string name; + + PerspectiveCamera perspective; + OrthographicCamera orthographic; + + Camera() {} + DEFAULT_METHODS(Camera) + bool operator==(const Camera &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct Primitive { + std::map attributes; // (required) A dictionary object of + // integer, where each integer + // is the index of the accessor + // containing an attribute. + int material; // The index of the material to apply to this primitive + // when rendering. + int indices; // The index of the accessor that contains the indices. + int mode; // one of TINYGLTF_MODE_*** + std::vector > targets; // array of morph targets, + // where each target is a dict with attribues in ["POSITION, "NORMAL", + // "TANGENT"] pointing + // to their corresponding accessors + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Primitive() { + material = -1; + indices = -1; + mode = -1; + } + DEFAULT_METHODS(Primitive) + bool operator==(const Primitive &) const; +}; + +struct Mesh { + std::string name; + std::vector primitives; + std::vector weights; // weights to be applied to the Morph Targets + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Mesh() = default; + DEFAULT_METHODS(Mesh) + bool operator==(const Mesh &) const; +}; + +class Node { + public: + Node() : camera(-1), skin(-1), mesh(-1) {} + + DEFAULT_METHODS(Node) + + bool operator==(const Node &) const; + + int camera; // the index of the camera referenced by this node + + std::string name; + int skin; + int mesh; + std::vector children; + std::vector rotation; // length must be 0 or 4 + std::vector scale; // length must be 0 or 3 + std::vector translation; // length must be 0 or 3 + std::vector matrix; // length must be 0 or 16 + std::vector weights; // The weights of the instantiated Morph Target + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct Buffer { + std::string name; + std::vector data; + std::string + uri; // considered as required here but not in the spec (need to clarify) + // uri is not decoded(e.g. whitespace may be represented as %20) + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Buffer() = default; + DEFAULT_METHODS(Buffer) + bool operator==(const Buffer &) const; +}; + +struct Asset { + std::string version = "2.0"; // required + std::string generator; + std::string minVersion; + std::string copyright; + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Asset() = default; + DEFAULT_METHODS(Asset) + bool operator==(const Asset &) const; +}; + +struct Scene { + std::string name; + std::vector nodes; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Scene() = default; + DEFAULT_METHODS(Scene) + bool operator==(const Scene &) const; +}; + +struct SpotLight { + double innerConeAngle; + double outerConeAngle; + + SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {} + DEFAULT_METHODS(SpotLight) + bool operator==(const SpotLight &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct Light { + std::string name; + std::vector color; + double intensity{1.0}; + std::string type; + double range{0.0}; // 0.0 = inifinite + SpotLight spot; + + Light() : intensity(1.0), range(0.0) {} + DEFAULT_METHODS(Light) + + bool operator==(const Light &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +class Model { + public: + Model() = default; + DEFAULT_METHODS(Model) + + bool operator==(const Model &) const; + + std::vector accessors; + std::vector animations; + std::vector buffers; + std::vector bufferViews; + std::vector materials; + std::vector meshes; + std::vector nodes; + std::vector textures; + std::vector images; + std::vector skins; + std::vector samplers; + std::vector cameras; + std::vector scenes; + std::vector lights; + + int defaultScene = -1; + std::vector extensionsUsed; + std::vector extensionsRequired; + + Asset asset; + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +enum SectionCheck { + NO_REQUIRE = 0x00, + REQUIRE_VERSION = 0x01, + REQUIRE_SCENE = 0x02, + REQUIRE_SCENES = 0x04, + REQUIRE_NODES = 0x08, + REQUIRE_ACCESSORS = 0x10, + REQUIRE_BUFFERS = 0x20, + REQUIRE_BUFFER_VIEWS = 0x40, + REQUIRE_ALL = 0x7f +}; + +/// +/// LoadImageDataFunction type. Signature for custom image loading callbacks. +/// +typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, + std::string *, int, int, + const unsigned char *, int, + void *user_pointer); + +/// +/// WriteImageDataFunction type. Signature for custom image writing callbacks. +/// +typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *, + Image *, bool, void *); + +#ifndef TINYGLTF_NO_STB_IMAGE +// Declaration of default image loader callback +bool LoadImageData(Image *image, const int image_idx, std::string *err, + std::string *warn, int req_width, int req_height, + const unsigned char *bytes, int size, void *); +#endif + +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE +// Declaration of default image writer callback +bool WriteImageData(const std::string *basepath, const std::string *filename, + Image *image, bool embedImages, void *); +#endif + +/// +/// FilExistsFunction type. Signature for custom filesystem callbacks. +/// +typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *); + +/// +/// ExpandFilePathFunction type. Signature for custom filesystem callbacks. +/// +typedef std::string (*ExpandFilePathFunction)(const std::string &, void *); + +/// +/// ReadWholeFileFunction type. Signature for custom filesystem callbacks. +/// +typedef bool (*ReadWholeFileFunction)(std::vector *, + std::string *, const std::string &, + void *); + +/// +/// WriteWholeFileFunction type. Signature for custom filesystem callbacks. +/// +typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &, + const std::vector &, + void *); + +/// +/// A structure containing all required filesystem callbacks and a pointer to +/// their user data. +/// +struct FsCallbacks { + FileExistsFunction FileExists; + ExpandFilePathFunction ExpandFilePath; + ReadWholeFileFunction ReadWholeFile; + WriteWholeFileFunction WriteWholeFile; + + void *user_data; // An argument that is passed to all fs callbacks +}; + +#ifndef TINYGLTF_NO_FS +// Declaration of default filesystem callbacks + +bool FileExists(const std::string &abs_filename, void *); + +/// +/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to +/// `C:\\Users\\tinygltf\\AppData`) +/// +/// @param[in] filepath File path string. Assume UTF-8 +/// @param[in] userdata User data. Set to `nullptr` if you don't need it. +/// +std::string ExpandFilePath(const std::string &filepath, void *userdata); + +bool ReadWholeFile(std::vector *out, std::string *err, + const std::string &filepath, void *); + +bool WriteWholeFile(std::string *err, const std::string &filepath, + const std::vector &contents, void *); +#endif + +/// +/// glTF Parser/Serialier context. +/// +class TinyGLTF { + public: +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat" +#endif + + TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + ~TinyGLTF() {} + + /// + /// Loads glTF ASCII asset from a file. + /// Set warning message to `warn` for example it fails to load asserts. + /// Returns false and set error string to `err` if there's an error. + /// + bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn, + const std::string &filename, + unsigned int check_sections = REQUIRE_VERSION); + + /// + /// Loads glTF ASCII asset from string(memory). + /// `length` = strlen(str); + /// Set warning message to `warn` for example it fails to load asserts. + /// Returns false and set error string to `err` if there's an error. + /// + bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn, + const char *str, const unsigned int length, + const std::string &base_dir, + unsigned int check_sections = REQUIRE_VERSION); + + /// + /// Loads glTF binary asset from a file. + /// Set warning message to `warn` for example it fails to load asserts. + /// Returns false and set error string to `err` if there's an error. + /// + bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn, + const std::string &filename, + unsigned int check_sections = REQUIRE_VERSION); + + /// + /// Loads glTF binary asset from memory. + /// `length` = strlen(str); + /// Set warning message to `warn` for example it fails to load asserts. + /// Returns false and set error string to `err` if there's an error. + /// + bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn, + const unsigned char *bytes, + const unsigned int length, + const std::string &base_dir = "", + unsigned int check_sections = REQUIRE_VERSION); + + /// + /// Write glTF to stream, buffers and images will be embeded + /// + bool WriteGltfSceneToStream(Model *model, std::ostream &stream, + bool prettyPrint, bool writeBinary); + + /// + /// Write glTF to file. + /// + bool WriteGltfSceneToFile(Model *model, const std::string &filename, + bool embedImages, bool embedBuffers, + bool prettyPrint, bool writeBinary); + + /// + /// Set callback to use for loading image data + /// + void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data); + + /// + /// Unset(remove) callback of loading image data + /// + void RemoveImageLoader(); + + /// + /// Set callback to use for writing image data + /// + void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data); + + /// + /// Set callbacks to use for filesystem (fs) access and their user data + /// + void SetFsCallbacks(FsCallbacks callbacks); + + /// + /// Set serializing default values(default = false). + /// When true, default values are force serialized to .glTF. + /// This may be helpfull if you want to serialize a full description of glTF + /// data. + /// + /// TODO(LTE): Supply parsing option as function arguments to + /// `LoadASCIIFromFile()` and others, not by a class method + /// + void SetSerializeDefaultValues(const bool enabled) { + serialize_default_values_ = enabled; + } + + bool GetSerializeDefaultValues() const { return serialize_default_values_; } + + /// + /// Store original JSON string for `extras` and `extensions`. + /// This feature will be useful when the user want to reconstruct custom data + /// structure from JSON string. + /// + void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) { + store_original_json_for_extras_and_extensions_ = enabled; + } + + bool GetStoreOriginalJSONForExtrasAndExtensions() const { + return store_original_json_for_extras_and_extensions_; + } + + /// + /// Specify whether preserve image channales when loading images or not. + /// (Not effective when the user suppy their own LoadImageData callbacks) + /// + void SetPreserveImageChannels(bool onoff) { + preserve_image_channels_ = onoff; + } + + bool GetPreserveImageChannels() const { return preserve_image_channels_; } + + private: + /// + /// Loads glTF asset from string(memory). + /// `length` = strlen(str); + /// Set warning message to `warn` for example it fails to load asserts + /// Returns false and set error string to `err` if there's an error. + /// + bool LoadFromString(Model *model, std::string *err, std::string *warn, + const char *str, const unsigned int length, + const std::string &base_dir, unsigned int check_sections); + + const unsigned char *bin_data_ = nullptr; + size_t bin_size_ = 0; + bool is_binary_ = false; + + bool serialize_default_values_ = false; ///< Serialize default values? + + bool store_original_json_for_extras_and_extensions_ = false; + + bool preserve_image_channels_ = false; /// Default false(expand channels to + /// RGBA) for backward compatibility. + + FsCallbacks fs = { +#ifndef TINYGLTF_NO_FS + &tinygltf::FileExists, &tinygltf::ExpandFilePath, + &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile, + + nullptr // Fs callback user data +#else + nullptr, nullptr, nullptr, nullptr, + + nullptr // Fs callback user data +#endif + }; + + LoadImageDataFunction LoadImageData = +#ifndef TINYGLTF_NO_STB_IMAGE + &tinygltf::LoadImageData; +#else + nullptr; +#endif + void *load_image_user_data_{nullptr}; + bool user_image_loader_{false}; + + WriteImageDataFunction WriteImageData = +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE + &tinygltf::WriteImageData; +#else + nullptr; +#endif + void *write_image_user_data_{nullptr}; +}; + +#ifdef __clang__ +#pragma clang diagnostic pop // -Wpadded +#endif + +} // namespace tinygltf + +#endif // TINY_GLTF_H_ + +#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__) +#include +//#include +#ifndef TINYGLTF_NO_FS +#include +#include +#endif +#include + +#ifdef __clang__ +// Disable some warnings for external files. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfloat-equal" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wc++98-compat" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" +#endif +#if __has_warning("-Wcomma") +#pragma clang diagnostic ignored "-Wcomma" +#endif +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif +#if __has_warning("-Wcast-qual") +#pragma clang diagnostic ignored "-Wcast-qual" +#endif +#if __has_warning("-Wmissing-variable-declarations") +#pragma clang diagnostic ignored "-Wmissing-variable-declarations" +#endif +#if __has_warning("-Wmissing-prototypes") +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#endif +#if __has_warning("-Wcast-align") +#pragma clang diagnostic ignored "-Wcast-align" +#endif +#if __has_warning("-Wnewline-eof") +#pragma clang diagnostic ignored "-Wnewline-eof" +#endif +#if __has_warning("-Wunused-parameter") +#pragma clang diagnostic ignored "-Wunused-parameter" +#endif +#if __has_warning("-Wmismatched-tags") +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +#if __has_warning("-Wextra-semi-stmt") +#pragma clang diagnostic ignored "-Wextra-semi-stmt" +#endif +#endif + +// Disable GCC warnigs +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" +#endif // __GNUC__ + +#ifndef TINYGLTF_NO_INCLUDE_JSON +#ifndef TINYGLTF_USE_RAPIDJSON +#include "json.hpp" +#else +#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON +#include "document.h" +#include "prettywriter.h" +#include "rapidjson.h" +#include "stringbuffer.h" +#include "writer.h" +#endif +#endif +#endif + +#ifdef TINYGLTF_ENABLE_DRACO +#include "draco/compression/decode.h" +#include "draco/core/decoder_buffer.h" +#endif + +#ifndef TINYGLTF_NO_STB_IMAGE +#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE +#include "stb_image.h" +#endif +#endif + +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE +#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE +#include "stb_image_write.h" +#endif +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef _WIN32 + +// issue 143. +// Define NOMINMAX to avoid min/max defines, +// but undef it after included windows.h +#ifndef NOMINMAX +#define TINYGLTF_INTERNAL_NOMINMAX +#define NOMINMAX +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN +#endif +#include // include API for expanding a file path + +#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN +#undef WIN32_LEAN_AND_MEAN +#endif + +#if defined(TINYGLTF_INTERNAL_NOMINMAX) +#undef NOMINMAX +#endif + +#if defined(__GLIBCXX__) // mingw + +#include // _O_RDONLY + +#include // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf) + +#endif + +#elif !defined(__ANDROID__) && !defined(__OpenBSD__) +#include +#endif + +#if defined(__sparcv9) +// Big endian +#else +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +#define TINYGLTF_LITTLE_ENDIAN 1 +#endif +#endif + +namespace { +#ifdef TINYGLTF_USE_RAPIDJSON + +#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR +// This uses the RapidJSON CRTAllocator. It is thread safe and multiple +// documents may be active at once. +using json = + rapidjson::GenericValue, rapidjson::CrtAllocator>; +using json_const_iterator = json::ConstMemberIterator; +using json_const_array_iterator = json const *; +using JsonDocument = + rapidjson::GenericDocument, rapidjson::CrtAllocator>; +rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe +rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; } +#else +// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but +// not thread safe. Only a single JsonDocument may be active at any one time, +// meaning only a single gltf load/save can be active any one time. +using json = rapidjson::Value; +using json_const_iterator = json::ConstMemberIterator; +using json_const_array_iterator = json const *; +rapidjson::Document *s_pActiveDocument = nullptr; +rapidjson::Document::AllocatorType &GetAllocator() { + assert(s_pActiveDocument); // Root json node must be JsonDocument type + return s_pActiveDocument->GetAllocator(); +} + +#ifdef __clang__ +#pragma clang diagnostic push +// Suppress JsonDocument(JsonDocument &&rhs) noexcept +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif + +struct JsonDocument : public rapidjson::Document { + JsonDocument() { + assert(s_pActiveDocument == + nullptr); // When using default allocator, only one document can be + // active at a time, if you need multiple active at once, + // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR + s_pActiveDocument = this; + } + JsonDocument(const JsonDocument &) = delete; + JsonDocument(JsonDocument &&rhs) noexcept + : rapidjson::Document(std::move(rhs)) { + s_pActiveDocument = this; + rhs.isNil = true; + } + ~JsonDocument() { + if (!isNil) { + s_pActiveDocument = nullptr; + } + } + + private: + bool isNil = false; +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR + +#else +using nlohmann::json; +using json_const_iterator = json::const_iterator; +using json_const_array_iterator = json_const_iterator; +using JsonDocument = json; +#endif + +void JsonParse(JsonDocument &doc, const char *str, size_t length, + bool throwExc = false) { +#ifdef TINYGLTF_USE_RAPIDJSON + (void)throwExc; + doc.Parse(str, length); +#else + doc = json::parse(str, str + length, nullptr, throwExc); +#endif +} +} // namespace + +#ifdef __APPLE__ +#include "TargetConditionals.h" +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat" +#endif + +namespace tinygltf { + +/// +/// Internal LoadImageDataOption struct. +/// This struct is passed through `user_pointer` in LoadImageData. +/// The struct is not passed when the user supply their own LoadImageData +/// callbacks. +/// +struct LoadImageDataOption { + // true: preserve image channels(e.g. load as RGB image if the image has RGB + // channels) default `false`(channels are expanded to RGBA for backward + // compatiblity). + bool preserve_channels{false}; +}; + +// Equals function for Value, for recursivity +static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) { + if (one.Type() != other.Type()) return false; + + switch (one.Type()) { + case NULL_TYPE: + return true; + case BOOL_TYPE: + return one.Get() == other.Get(); + case REAL_TYPE: + return TINYGLTF_DOUBLE_EQUAL(one.Get(), other.Get()); + case INT_TYPE: + return one.Get() == other.Get(); + case OBJECT_TYPE: { + auto oneObj = one.Get(); + auto otherObj = other.Get(); + if (oneObj.size() != otherObj.size()) return false; + for (auto &it : oneObj) { + auto otherIt = otherObj.find(it.first); + if (otherIt == otherObj.end()) return false; + + if (!Equals(it.second, otherIt->second)) return false; + } + return true; + } + case ARRAY_TYPE: { + if (one.Size() != other.Size()) return false; + for (int i = 0; i < int(one.Size()); ++i) + if (!Equals(one.Get(i), other.Get(i))) return false; + return true; + } + case STRING_TYPE: + return one.Get() == other.Get(); + case BINARY_TYPE: + return one.Get >() == + other.Get >(); + default: { + // unhandled type + return false; + } + } +} + +// Equals function for std::vector using TINYGLTF_DOUBLE_EPSILON +static bool Equals(const std::vector &one, + const std::vector &other) { + if (one.size() != other.size()) return false; + for (int i = 0; i < int(one.size()); ++i) { + if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false; + } + return true; +} + +bool Accessor::operator==(const Accessor &other) const { + return this->bufferView == other.bufferView && + this->byteOffset == other.byteOffset && + this->componentType == other.componentType && + this->count == other.count && this->extensions == other.extensions && + this->extras == other.extras && + Equals(this->maxValues, other.maxValues) && + Equals(this->minValues, other.minValues) && this->name == other.name && + this->normalized == other.normalized && this->type == other.type; +} +bool Animation::operator==(const Animation &other) const { + return this->channels == other.channels && + this->extensions == other.extensions && this->extras == other.extras && + this->name == other.name && this->samplers == other.samplers; +} +bool AnimationChannel::operator==(const AnimationChannel &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->target_node == other.target_node && + this->target_path == other.target_path && + this->sampler == other.sampler; +} +bool AnimationSampler::operator==(const AnimationSampler &other) const { + return this->extras == other.extras && this->extensions == other.extensions && + this->input == other.input && + this->interpolation == other.interpolation && + this->output == other.output; +} +bool Asset::operator==(const Asset &other) const { + return this->copyright == other.copyright && + this->extensions == other.extensions && this->extras == other.extras && + this->generator == other.generator && + this->minVersion == other.minVersion && this->version == other.version; +} +bool Buffer::operator==(const Buffer &other) const { + return this->data == other.data && this->extensions == other.extensions && + this->extras == other.extras && this->name == other.name && + this->uri == other.uri; +} +bool BufferView::operator==(const BufferView &other) const { + return this->buffer == other.buffer && this->byteLength == other.byteLength && + this->byteOffset == other.byteOffset && + this->byteStride == other.byteStride && this->name == other.name && + this->target == other.target && this->extensions == other.extensions && + this->extras == other.extras && + this->dracoDecoded == other.dracoDecoded; +} +bool Camera::operator==(const Camera &other) const { + return this->name == other.name && this->extensions == other.extensions && + this->extras == other.extras && + this->orthographic == other.orthographic && + this->perspective == other.perspective && this->type == other.type; +} +bool Image::operator==(const Image &other) const { + return this->bufferView == other.bufferView && + this->component == other.component && + this->extensions == other.extensions && this->extras == other.extras && + this->height == other.height && this->image == other.image && + this->mimeType == other.mimeType && this->name == other.name && + this->uri == other.uri && this->width == other.width; +} +bool Light::operator==(const Light &other) const { + return Equals(this->color, other.color) && this->name == other.name && + this->type == other.type; +} +bool Material::operator==(const Material &other) const { + return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) && + (this->normalTexture == other.normalTexture) && + (this->occlusionTexture == other.occlusionTexture) && + (this->emissiveTexture == other.emissiveTexture) && + Equals(this->emissiveFactor, other.emissiveFactor) && + (this->alphaMode == other.alphaMode) && + TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) && + (this->doubleSided == other.doubleSided) && + (this->extensions == other.extensions) && + (this->extras == other.extras) && (this->values == other.values) && + (this->additionalValues == other.additionalValues) && + (this->name == other.name); +} +bool Mesh::operator==(const Mesh &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->name == other.name && Equals(this->weights, other.weights) && + this->primitives == other.primitives; +} +bool Model::operator==(const Model &other) const { + return this->accessors == other.accessors && + this->animations == other.animations && this->asset == other.asset && + this->buffers == other.buffers && + this->bufferViews == other.bufferViews && + this->cameras == other.cameras && + this->defaultScene == other.defaultScene && + this->extensions == other.extensions && + this->extensionsRequired == other.extensionsRequired && + this->extensionsUsed == other.extensionsUsed && + this->extras == other.extras && this->images == other.images && + this->lights == other.lights && this->materials == other.materials && + this->meshes == other.meshes && this->nodes == other.nodes && + this->samplers == other.samplers && this->scenes == other.scenes && + this->skins == other.skins && this->textures == other.textures; +} +bool Node::operator==(const Node &other) const { + return this->camera == other.camera && this->children == other.children && + this->extensions == other.extensions && this->extras == other.extras && + Equals(this->matrix, other.matrix) && this->mesh == other.mesh && + this->name == other.name && Equals(this->rotation, other.rotation) && + Equals(this->scale, other.scale) && this->skin == other.skin && + Equals(this->translation, other.translation) && + Equals(this->weights, other.weights); +} +bool SpotLight::operator==(const SpotLight &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) && + TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle); +} +bool OrthographicCamera::operator==(const OrthographicCamera &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) && + TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) && + TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) && + TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); +} +bool Parameter::operator==(const Parameter &other) const { + if (this->bool_value != other.bool_value || + this->has_number_value != other.has_number_value) + return false; + + if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value)) + return false; + + if (this->json_double_value.size() != other.json_double_value.size()) + return false; + for (auto &it : this->json_double_value) { + auto otherIt = other.json_double_value.find(it.first); + if (otherIt == other.json_double_value.end()) return false; + + if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false; + } + + if (!Equals(this->number_array, other.number_array)) return false; + + if (this->string_value != other.string_value) return false; + + return true; +} +bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const { + return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) && + this->extensions == other.extensions && this->extras == other.extras && + TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) && + TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) && + TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); +} +bool Primitive::operator==(const Primitive &other) const { + return this->attributes == other.attributes && this->extras == other.extras && + this->indices == other.indices && this->material == other.material && + this->mode == other.mode && this->targets == other.targets; +} +bool Sampler::operator==(const Sampler &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->magFilter == other.magFilter && + this->minFilter == other.minFilter && this->name == other.name && + this->wrapT == other.wrapT; + + //this->wrapR == other.wrapR && this->wrapS == other.wrapS && +} +bool Scene::operator==(const Scene &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->name == other.name && this->nodes == other.nodes; +} +bool Skin::operator==(const Skin &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->inverseBindMatrices == other.inverseBindMatrices && + this->joints == other.joints && this->name == other.name && + this->skeleton == other.skeleton; +} +bool Texture::operator==(const Texture &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->name == other.name && this->sampler == other.sampler && + this->source == other.source; +} +bool TextureInfo::operator==(const TextureInfo &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->index == other.index && this->texCoord == other.texCoord; +} +bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->index == other.index && this->texCoord == other.texCoord && + TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale); +} +bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->index == other.index && this->texCoord == other.texCoord && + TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength); +} +bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + (this->baseColorTexture == other.baseColorTexture) && + (this->metallicRoughnessTexture == other.metallicRoughnessTexture) && + Equals(this->baseColorFactor, other.baseColorFactor) && + TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) && + TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor); +} +bool Value::operator==(const Value &other) const { + return Equals(*this, other); +} + +static void swap4(unsigned int *val) { +#ifdef TINYGLTF_LITTLE_ENDIAN + (void)val; +#else + unsigned int tmp = *val; + unsigned char *dst = reinterpret_cast(val); + unsigned char *src = reinterpret_cast(&tmp); + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +#endif +} + +static std::string JoinPath(const std::string &path0, + const std::string &path1) { + if (path0.empty()) { + return path1; + } else { + // check '/' + char lastChar = *path0.rbegin(); + if (lastChar != '/') { + return path0 + std::string("/") + path1; + } else { + return path0 + path1; + } + } +} + +static std::string FindFile(const std::vector &paths, + const std::string &filepath, FsCallbacks *fs) { + if (fs == nullptr || fs->ExpandFilePath == nullptr || + fs->FileExists == nullptr) { + // Error, fs callback[s] missing + return std::string(); + } + + for (size_t i = 0; i < paths.size(); i++) { + std::string absPath = + fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data); + if (fs->FileExists(absPath, fs->user_data)) { + return absPath; + } + } + + return std::string(); +} + +static std::string GetFilePathExtension(const std::string &FileName) { + if (FileName.find_last_of(".") != std::string::npos) + return FileName.substr(FileName.find_last_of(".") + 1); + return ""; +} + +static std::string GetBaseDir(const std::string &filepath) { + if (filepath.find_last_of("/\\") != std::string::npos) + return filepath.substr(0, filepath.find_last_of("/\\")); + return ""; +} + +// https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path +static std::string GetBaseFilename(const std::string &filepath) { + return filepath.substr(filepath.find_last_of("/\\") + 1); +} + +std::string base64_encode(unsigned char const *, unsigned int len); +std::string base64_decode(std::string const &s); + +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wconversion" +#endif + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_encode(unsigned char const *bytes_to_encode, + unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + const char *base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = + ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = + ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) { + for (j = i; j < 3; j++) char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = + ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = + ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + + for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; + + while ((i++ < 3)) ret += '='; + } + + return ret; +} + +std::string base64_decode(std::string const &encoded_string) { + int in_len = static_cast(encoded_string.size()); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + while (in_len-- && (encoded_string[in_] != '=') && + is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; + in_++; + if (i == 4) { + for (i = 0; i < 4; i++) + char_array_4[i] = + static_cast(base64_chars.find(char_array_4[i])); + + char_array_3[0] = + (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = + ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j < 4; j++) char_array_4[j] = 0; + + for (j = 0; j < 4; j++) + char_array_4[j] = + static_cast(base64_chars.find(char_array_4[j])); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = + ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// https://github.com/syoyo/tinygltf/issues/228 +// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri +// decoding? +// +// https://stackoverflow.com/questions/18307429/encode-decode-url-in-c +// http://dlib.net/dlib/server/server_http.cpp.html + +// --- dlib beign ------------------------------------------------------------ +// Copyright (C) 2003 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. + +namespace dlib { + +#if 0 + inline unsigned char to_hex( unsigned char x ) + { + return x + (x > 9 ? ('A'-10) : '0'); + } + + const std::string urlencode( const std::string& s ) + { + std::ostringstream os; + + for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci ) + { + if ( (*ci >= 'a' && *ci <= 'z') || + (*ci >= 'A' && *ci <= 'Z') || + (*ci >= '0' && *ci <= '9') ) + { // allowed + os << *ci; + } + else if ( *ci == ' ') + { + os << '+'; + } + else + { + os << '%' << to_hex(static_cast(*ci >> 4)) << to_hex(static_cast(*ci % 16)); + } + } + + return os.str(); + } +#endif + +inline unsigned char from_hex(unsigned char ch) { + if (ch <= '9' && ch >= '0') + ch -= '0'; + else if (ch <= 'f' && ch >= 'a') + ch -= 'a' - 10; + else if (ch <= 'F' && ch >= 'A') + ch -= 'A' - 10; + else + ch = 0; + return ch; +} + +static const std::string urldecode(const std::string &str) { + using namespace std; + string result; + string::size_type i; + for (i = 0; i < str.size(); ++i) { + if (str[i] == '+') { + result += ' '; + } else if (str[i] == '%' && str.size() > i + 2) { + const unsigned char ch1 = + from_hex(static_cast(str[i + 1])); + const unsigned char ch2 = + from_hex(static_cast(str[i + 2])); + const unsigned char ch = static_cast((ch1 << 4) | ch2); + result += static_cast(ch); + i += 2; + } else { + result += str[i]; + } + } + return result; +} + +} // namespace dlib +// --- dlib end -------------------------------------------------------------- + +static bool LoadExternalFile(std::vector *out, std::string *err, + std::string *warn, const std::string &filename, + const std::string &basedir, bool required, + size_t reqBytes, bool checkSize, FsCallbacks *fs) { + if (fs == nullptr || fs->FileExists == nullptr || + fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) { + // This is a developer error, assert() ? + if (err) { + (*err) += "FS callback[s] not set\n"; + } + return false; + } + + std::string *failMsgOut = required ? err : warn; + + out->clear(); + + std::vector paths; + paths.push_back(basedir); + paths.push_back("."); + + std::string filepath = FindFile(paths, filename, fs); + if (filepath.empty() || filename.empty()) { + if (failMsgOut) { + (*failMsgOut) += "File not found : " + filename + "\n"; + } + return false; + } + + std::vector buf; + std::string fileReadErr; + bool fileRead = + fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data); + if (!fileRead) { + if (failMsgOut) { + (*failMsgOut) += + "File read error : " + filepath + " : " + fileReadErr + "\n"; + } + return false; + } + + size_t sz = buf.size(); + if (sz == 0) { + if (failMsgOut) { + (*failMsgOut) += "File is empty : " + filepath + "\n"; + } + return false; + } + + if (checkSize) { + if (reqBytes == sz) { + out->swap(buf); + return true; + } else { + std::stringstream ss; + ss << "File size mismatch : " << filepath << ", requestedBytes " + << reqBytes << ", but got " << sz << std::endl; + if (failMsgOut) { + (*failMsgOut) += ss.str(); + } + return false; + } + } + + out->swap(buf); + return true; +} + +void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) { + LoadImageData = func; + load_image_user_data_ = user_data; + user_image_loader_ = true; +} + +void TinyGLTF::RemoveImageLoader() { + LoadImageData = +#ifndef TINYGLTF_NO_STB_IMAGE + &tinygltf::LoadImageData; +#else + nullptr; +#endif + + load_image_user_data_ = nullptr; + user_image_loader_ = false; +} + +#ifndef TINYGLTF_NO_STB_IMAGE +bool LoadImageData(Image *image, const int image_idx, std::string *err, + std::string *warn, int req_width, int req_height, + const unsigned char *bytes, int size, void *user_data) { + (void)warn; + + LoadImageDataOption option; + if (user_data) { + option = *reinterpret_cast(user_data); + } + + int w = 0, h = 0, comp = 0, req_comp = 0; + + unsigned char *data = nullptr; + + // preserve_channels true: Use channels stored in the image file. + // false: force 32-bit textures for common Vulkan compatibility. It appears + // that some GPU drivers do not support 24-bit images for Vulkan + req_comp = option.preserve_channels ? 0 : 4; + int bits = 8; + int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + + // It is possible that the image we want to load is a 16bit per channel image + // We are going to attempt to load it as 16bit per channel, and if it worked, + // set the image data accodingly. We are casting the returned pointer into + // unsigned char, because we are representing "bytes". But we are updating + // the Image metadata to signal that this image uses 2 bytes (16bits) per + // channel: + if (stbi_is_16_bit_from_memory(bytes, size)) { + data = reinterpret_cast( + stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp)); + if (data) { + bits = 16; + pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + } + } + + // at this point, if data is still NULL, it means that the image wasn't + // 16bit per channel, we are going to load it as a normal 8bit per channel + // mage as we used to do: + // if image cannot be decoded, ignore parsing and keep it by its path + // don't break in this case + // FIXME we should only enter this function if the image is embedded. If + // image->uri references + // an image file, it should be left as it is. Image loading should not be + // mandatory (to support other formats) + if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); + if (!data) { + // NOTE: you can use `warn` instead of `err` + if (err) { + (*err) += + "Unknown image format. STB cannot decode image data for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; + } + return false; + } + + if ((w < 1) || (h < 1)) { + stbi_image_free(data); + if (err) { + (*err) += "Invalid image data for image[" + std::to_string(image_idx) + + "] name = \"" + image->name + "\"\n"; + } + return false; + } + + if (req_width > 0) { + if (req_width != w) { + stbi_image_free(data); + if (err) { + (*err) += "Image width mismatch for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + + "\"\n"; + } + return false; + } + } + + if (req_height > 0) { + if (req_height != h) { + stbi_image_free(data); + if (err) { + (*err) += "Image height mismatch. for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + + "\"\n"; + } + return false; + } + } + + if (req_comp != 0) { + // loaded data has `req_comp` channels(components) + comp = req_comp; + } + + image->width = w; + image->height = h; + image->component = comp; + image->bits = bits; + image->pixel_type = pixel_type; + image->image.resize(static_cast(w * h * comp) * size_t(bits / 8)); + std::copy(data, data + w * h * comp * (bits / 8), image->image.begin()); + stbi_image_free(data); + + return true; +} +#endif + +void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { + WriteImageData = func; + write_image_user_data_ = user_data; +} + +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE +static void WriteToMemory_stbi(void *context, void *data, int size) { + std::vector *buffer = + reinterpret_cast *>(context); + + unsigned char *pData = reinterpret_cast(data); + + buffer->insert(buffer->end(), pData, pData + size); +} + +bool WriteImageData(const std::string *basepath, const std::string *filename, + Image *image, bool embedImages, void *fsPtr) { + const std::string ext = GetFilePathExtension(*filename); + + // Write image to temporary buffer + std::string header; + std::vector data; + + if (ext == "png") { + if ((image->bits != 8) || + (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { + // Unsupported pixel format + return false; + } + + if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width, + image->height, image->component, + &image->image[0], 0)) { + return false; + } + header = "data:image/png;base64,"; + } else if (ext == "jpg") { + if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width, + image->height, image->component, + &image->image[0], 100)) { + return false; + } + header = "data:image/jpeg;base64,"; + } else if (ext == "bmp") { + if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width, + image->height, image->component, + &image->image[0])) { + return false; + } + header = "data:image/bmp;base64,"; + } else if (!embedImages) { + // Error: can't output requested format to file + return false; + } + + if (embedImages) { + // Embed base64-encoded image into URI + if (data.size()) { + image->uri = + header + + base64_encode(&data[0], static_cast(data.size())); + } else { + // Throw error? + } + } else { + // Write image to disc + FsCallbacks *fs = reinterpret_cast(fsPtr); + if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) { + const std::string imagefilepath = JoinPath(*basepath, *filename); + std::string writeError; + if (!fs->WriteWholeFile(&writeError, imagefilepath, data, + fs->user_data)) { + // Could not write image file to disc; Throw error ? + return false; + } + } else { + // Throw error? + } + image->uri = *filename; + } + + return true; +} +#endif + +void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; } + +#ifdef _WIN32 +static inline std::wstring UTF8ToWchar(const std::string &str) { + int wstr_size = + MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0); + std::wstring wstr(wstr_size, 0); + MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], + (int)wstr.size()); + return wstr; +} + +static inline std::string WcharToUTF8(const std::wstring &wstr) { + int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), + nullptr, 0, NULL, NULL); + std::string str(str_size, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0], + (int)str.size(), NULL, NULL); + return str; +} +#endif + +#ifndef TINYGLTF_NO_FS +// Default implementations of filesystem functions + +bool FileExists(const std::string &abs_filename, void *) { + bool ret; +#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS + if (asset_manager) { + AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(), + AASSET_MODE_STREAMING); + if (!asset) { + return false; + } + AAsset_close(asset); + ret = true; + } else { + return false; + } +#else +#ifdef _WIN32 +#if defined(_MSC_VER) || defined(__GLIBCXX__) + FILE *fp = nullptr; + errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb"); + if (err != 0) { + return false; + } +#else + FILE *fp = nullptr; + errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb"); + if (err != 0) { + return false; + } +#endif + +#else + FILE *fp = fopen(abs_filename.c_str(), "rb"); +#endif + if (fp) { + ret = true; + fclose(fp); + } else { + ret = false; + } +#endif + + return ret; +} + +std::string ExpandFilePath(const std::string &filepath, void *) { +#ifdef _WIN32 + // Assume input `filepath` is encoded in UTF-8 + std::wstring wfilepath = UTF8ToWchar(filepath); + DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0); + wchar_t *wstr = new wchar_t[wlen]; + ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen); + + std::wstring ws(wstr); + delete[] wstr; + return WcharToUTF8(ws); + +#else + +#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \ + defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__) + // no expansion + std::string s = filepath; +#else + std::string s; + wordexp_t p; + + if (filepath.empty()) { + return ""; + } + + // Quote the string to keep any spaces in filepath intact. + std::string quoted_path = "\"" + filepath + "\""; + // char** w; + int ret = wordexp(quoted_path.c_str(), &p, 0); + if (ret) { + // err + s = filepath; + return s; + } + + // Use first element only. + if (p.we_wordv) { + s = std::string(p.we_wordv[0]); + wordfree(&p); + } else { + s = filepath; + } + +#endif + + return s; +#endif +} + +bool ReadWholeFile(std::vector *out, std::string *err, + const std::string &filepath, void *) { +#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS + if (asset_manager) { + AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(), + AASSET_MODE_STREAMING); + if (!asset) { + if (err) { + (*err) += "File open error : " + filepath + "\n"; + } + return false; + } + size_t size = AAsset_getLength(asset); + if (size == 0) { + if (err) { + (*err) += "Invalid file size : " + filepath + + " (does the path point to a directory?)"; + } + return false; + } + out->resize(size); + AAsset_read(asset, reinterpret_cast(&out->at(0)), size); + AAsset_close(asset); + return true; + } else { + if (err) { + (*err) += "No asset manager specified : " + filepath + "\n"; + } + return false; + } +#else +#ifdef _WIN32 +#if defined(__GLIBCXX__) // mingw + int file_descriptor = + _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf(file_descriptor, std::ios_base::in); + std::istream f(&wfile_buf); +#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION) + // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept + // `wchar_t *` + std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary); +#else + // Unknown compiler/runtime + std::ifstream f(filepath.c_str(), std::ifstream::binary); +#endif +#else + std::ifstream f(filepath.c_str(), std::ifstream::binary); +#endif + if (!f) { + if (err) { + (*err) += "File open error : " + filepath + "\n"; + } + return false; + } + + f.seekg(0, f.end); + size_t sz = static_cast(f.tellg()); + f.seekg(0, f.beg); + + if (int64_t(sz) < 0) { + if (err) { + (*err) += "Invalid file size : " + filepath + + " (does the path point to a directory?)"; + } + return false; + } else if (sz == 0) { + if (err) { + (*err) += "File is empty : " + filepath + "\n"; + } + return false; + } + + out->resize(sz); + f.read(reinterpret_cast(&out->at(0)), + static_cast(sz)); + + return true; +#endif +} + +bool WriteWholeFile(std::string *err, const std::string &filepath, + const std::vector &contents, void *) { +#ifdef _WIN32 +#if defined(__GLIBCXX__) // mingw + int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); + std::ostream f(&wfile_buf); +#elif defined(_MSC_VER) + std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary); +#else // clang? + std::ofstream f(filepath.c_str(), std::ofstream::binary); +#endif +#else + std::ofstream f(filepath.c_str(), std::ofstream::binary); +#endif + if (!f) { + if (err) { + (*err) += "File open error for writing : " + filepath + "\n"; + } + return false; + } + + f.write(reinterpret_cast(&contents.at(0)), + static_cast(contents.size())); + if (!f) { + if (err) { + (*err) += "File write error: " + filepath + "\n"; + } + return false; + } + + return true; +} + +#endif // TINYGLTF_NO_FS + +static std::string MimeToExt(const std::string &mimeType) { + if (mimeType == "image/jpeg") { + return "jpg"; + } else if (mimeType == "image/png") { + return "png"; + } else if (mimeType == "image/bmp") { + return "bmp"; + } else if (mimeType == "image/gif") { + return "gif"; + } + + return ""; +} + +static void UpdateImageObject(Image &image, std::string &baseDir, int index, + bool embedImages, + WriteImageDataFunction *WriteImageData = nullptr, + void *user_data = nullptr) { + std::string filename; + std::string ext; + // If image has uri, use it it as a filename + if (image.uri.size()) { + filename = GetBaseFilename(image.uri); + ext = GetFilePathExtension(filename); + } else if (image.bufferView != -1) { + // If there's no URI and the data exists in a buffer, + // don't change properties or write images + } else if (image.name.size()) { + ext = MimeToExt(image.mimeType); + // Otherwise use name as filename + filename = image.name + "." + ext; + } else { + ext = MimeToExt(image.mimeType); + // Fallback to index of image as filename + filename = std::to_string(index) + "." + ext; + } + + // If callback is set, modify image data object + if (*WriteImageData != nullptr && !filename.empty()) { + std::string uri; + (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data); + } +} + +bool IsDataURI(const std::string &in) { + std::string header = "data:application/octet-stream;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:image/jpeg;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:image/png;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:image/bmp;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:image/gif;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:text/plain;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:application/gltf-buffer;base64,"; + if (in.find(header) == 0) { + return true; + } + + return false; +} + +bool DecodeDataURI(std::vector *out, std::string &mime_type, + const std::string &in, size_t reqBytes, bool checkSize) { + std::string header = "data:application/octet-stream;base64,"; + std::string data; + if (in.find(header) == 0) { + data = base64_decode(in.substr(header.size())); // cut mime string. + } + + if (data.empty()) { + header = "data:image/jpeg;base64,"; + if (in.find(header) == 0) { + mime_type = "image/jpeg"; + data = base64_decode(in.substr(header.size())); // cut mime string. + } + } + + if (data.empty()) { + header = "data:image/png;base64,"; + if (in.find(header) == 0) { + mime_type = "image/png"; + data = base64_decode(in.substr(header.size())); // cut mime string. + } + } + + if (data.empty()) { + header = "data:image/bmp;base64,"; + if (in.find(header) == 0) { + mime_type = "image/bmp"; + data = base64_decode(in.substr(header.size())); // cut mime string. + } + } + + if (data.empty()) { + header = "data:image/gif;base64,"; + if (in.find(header) == 0) { + mime_type = "image/gif"; + data = base64_decode(in.substr(header.size())); // cut mime string. + } + } + + if (data.empty()) { + header = "data:text/plain;base64,"; + if (in.find(header) == 0) { + mime_type = "text/plain"; + data = base64_decode(in.substr(header.size())); + } + } + + if (data.empty()) { + header = "data:application/gltf-buffer;base64,"; + if (in.find(header) == 0) { + data = base64_decode(in.substr(header.size())); + } + } + + // TODO(syoyo): Allow empty buffer? #229 + if (data.empty()) { + return false; + } + + if (checkSize) { + if (data.size() != reqBytes) { + return false; + } + out->resize(reqBytes); + } else { + out->resize(data.size()); + } + std::copy(data.begin(), data.end(), out->begin()); + return true; +} + +namespace { +bool GetInt(const json &o, int &val) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (!o.IsDouble()) { + if (o.IsInt()) { + val = o.GetInt(); + return true; + } else if (o.IsUint()) { + val = static_cast(o.GetUint()); + return true; + } else if (o.IsInt64()) { + val = static_cast(o.GetInt64()); + return true; + } else if (o.IsUint64()) { + val = static_cast(o.GetUint64()); + return true; + } + } + + return false; +#else + auto type = o.type(); + + if ((type == json::value_t::number_integer) || + (type == json::value_t::number_unsigned)) { + val = static_cast(o.get()); + return true; + } + + return false; +#endif +} + +#ifdef TINYGLTF_USE_RAPIDJSON +bool GetDouble(const json &o, double &val) { + if (o.IsDouble()) { + val = o.GetDouble(); + return true; + } + + return false; +} +#endif + +bool GetNumber(const json &o, double &val) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (o.IsNumber()) { + val = o.GetDouble(); + return true; + } + + return false; +#else + if (o.is_number()) { + val = o.get(); + return true; + } + + return false; +#endif +} + +bool GetString(const json &o, std::string &val) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (o.IsString()) { + val = o.GetString(); + return true; + } + + return false; +#else + if (o.type() == json::value_t::string) { + val = o.get(); + return true; + } + + return false; +#endif +} + +bool IsArray(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.IsArray(); +#else + return o.is_array(); +#endif +} + +json_const_array_iterator ArrayBegin(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.Begin(); +#else + return o.begin(); +#endif +} + +json_const_array_iterator ArrayEnd(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.End(); +#else + return o.end(); +#endif +} + +bool IsObject(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.IsObject(); +#else + return o.is_object(); +#endif +} + +json_const_iterator ObjectBegin(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.MemberBegin(); +#else + return o.begin(); +#endif +} + +json_const_iterator ObjectEnd(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.MemberEnd(); +#else + return o.end(); +#endif +} + +// Making this a const char* results in a pointer to a temporary when +// TINYGLTF_USE_RAPIDJSON is off. +std::string GetKey(json_const_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + return it->name.GetString(); +#else + return it.key().c_str(); +#endif +} + +bool FindMember(const json &o, const char *member, json_const_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (!o.IsObject()) { + return false; + } + it = o.FindMember(member); + return it != o.MemberEnd(); +#else + it = o.find(member); + return it != o.end(); +#endif +} + +const json &GetValue(json_const_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + return it->value; +#else + return it.value(); +#endif +} + +std::string JsonToString(const json &o, int spacing = -1) { +#ifdef TINYGLTF_USE_RAPIDJSON + using namespace rapidjson; + StringBuffer buffer; + if (spacing == -1) { + Writer writer(buffer); + o.Accept(writer); + } else { + PrettyWriter writer(buffer); + writer.SetIndent(' ', uint32_t(spacing)); + o.Accept(writer); + } + return buffer.GetString(); +#else + return o.dump(spacing); +#endif +} + +} // namespace + +static bool ParseJsonAsValue(Value *ret, const json &o) { + Value val{}; +#ifdef TINYGLTF_USE_RAPIDJSON + using rapidjson::Type; + switch (o.GetType()) { + case Type::kObjectType: { + Value::Object value_object; + for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) { + Value entry; + ParseJsonAsValue(&entry, it->value); + if (entry.Type() != NULL_TYPE) + value_object.emplace(GetKey(it), std::move(entry)); + } + if (value_object.size() > 0) val = Value(std::move(value_object)); + } break; + case Type::kArrayType: { + Value::Array value_array; + value_array.reserve(o.Size()); + for (auto it = o.Begin(); it != o.End(); ++it) { + Value entry; + ParseJsonAsValue(&entry, *it); + if (entry.Type() != NULL_TYPE) + value_array.emplace_back(std::move(entry)); + } + if (value_array.size() > 0) val = Value(std::move(value_array)); + } break; + case Type::kStringType: + val = Value(std::string(o.GetString())); + break; + case Type::kFalseType: + case Type::kTrueType: + val = Value(o.GetBool()); + break; + case Type::kNumberType: + if (!o.IsDouble()) { + int i = 0; + GetInt(o, i); + val = Value(i); + } else { + double d = 0.0; + GetDouble(o, d); + val = Value(d); + } + break; + case Type::kNullType: + break; + // all types are covered, so no `case default` + } +#else + switch (o.type()) { + case json::value_t::object: { + Value::Object value_object; + for (auto it = o.begin(); it != o.end(); it++) { + Value entry; + ParseJsonAsValue(&entry, it.value()); + if (entry.Type() != NULL_TYPE) + value_object.emplace(it.key(), std::move(entry)); + } + if (value_object.size() > 0) val = Value(std::move(value_object)); + } break; + case json::value_t::array: { + Value::Array value_array; + value_array.reserve(o.size()); + for (auto it = o.begin(); it != o.end(); it++) { + Value entry; + ParseJsonAsValue(&entry, it.value()); + if (entry.Type() != NULL_TYPE) + value_array.emplace_back(std::move(entry)); + } + if (value_array.size() > 0) val = Value(std::move(value_array)); + } break; + case json::value_t::string: + val = Value(o.get()); + break; + case json::value_t::boolean: + val = Value(o.get()); + break; + case json::value_t::number_integer: + case json::value_t::number_unsigned: + val = Value(static_cast(o.get())); + break; + case json::value_t::number_float: + val = Value(o.get()); + break; + case json::value_t::null: + case json::value_t::discarded: + // default: + break; + } +#endif + if (ret) *ret = std::move(val); + + return val.Type() != NULL_TYPE; +} + +static bool ParseExtrasProperty(Value *ret, const json &o) { + json_const_iterator it; + if (!FindMember(o, "extras", it)) { + return false; + } + + return ParseJsonAsValue(ret, GetValue(it)); +} + +static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o, + const std::string &property, + const bool required, + const std::string &parent_node = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + auto &value = GetValue(it); + + bool isBoolean; + bool boolValue = false; +#ifdef TINYGLTF_USE_RAPIDJSON + isBoolean = value.IsBool(); + if (isBoolean) { + boolValue = value.GetBool(); + } +#else + isBoolean = value.is_boolean(); + if (isBoolean) { + boolValue = value.get(); + } +#endif + if (!isBoolean) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a bool type.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = boolValue; + } + + return true; +} + +static bool ParseIntegerProperty(int *ret, std::string *err, const json &o, + const std::string &property, + const bool required, + const std::string &parent_node = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + int intValue; + bool isInt = GetInt(GetValue(it), intValue); + if (!isInt) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an integer type.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = intValue; + } + + return true; +} + +static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o, + const std::string &property, + const bool required, + const std::string &parent_node = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + auto &value = GetValue(it); + + size_t uValue = 0; + bool isUValue; +#ifdef TINYGLTF_USE_RAPIDJSON + isUValue = false; + if (value.IsUint()) { + uValue = value.GetUint(); + isUValue = true; + } else if (value.IsUint64()) { + uValue = value.GetUint64(); + isUValue = true; + } +#else + isUValue = value.is_number_unsigned(); + if (isUValue) { + uValue = value.get(); + } +#endif + if (!isUValue) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a positive integer.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = uValue; + } + + return true; +} + +static bool ParseNumberProperty(double *ret, std::string *err, const json &o, + const std::string &property, + const bool required, + const std::string &parent_node = "") { + json_const_iterator it; + + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + double numberValue; + bool isNumber = GetNumber(GetValue(it), numberValue); + + if (!isNumber) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a number type.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = numberValue; + } + + return true; +} + +static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, + const json &o, const std::string &property, + bool required, + const std::string &parent_node = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + if (!IsArray(GetValue(it))) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an array"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + ret->clear(); + auto end = ArrayEnd(GetValue(it)); + for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) { + double numberValue; + const bool isNumber = GetNumber(*i, numberValue); + if (!isNumber) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a number.\n"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + ret->push_back(numberValue); + } + + return true; +} + +static bool ParseIntegerArrayProperty(std::vector *ret, std::string *err, + const json &o, + const std::string &property, + bool required, + const std::string &parent_node = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + if (!IsArray(GetValue(it))) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an array"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + ret->clear(); + auto end = ArrayEnd(GetValue(it)); + for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) { + int numberValue; + bool isNumber = GetInt(*i, numberValue); + if (!isNumber) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an integer type.\n"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + ret->push_back(numberValue); + } + + return true; +} + +static bool ParseStringProperty( + std::string *ret, std::string *err, const json &o, + const std::string &property, bool required, + const std::string &parent_node = std::string()) { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (parent_node.empty()) { + (*err) += ".\n"; + } else { + (*err) += " in `" + parent_node + "'.\n"; + } + } + } + return false; + } + + std::string strValue; + if (!GetString(GetValue(it), strValue)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a string type.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = std::move(strValue); + } + + return true; +} + +static bool ParseStringIntegerProperty(std::map *ret, + std::string *err, const json &o, + const std::string &property, + bool required, + const std::string &parent = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + if (!parent.empty()) { + (*err) += + "'" + property + "' property is missing in " + parent + ".\n"; + } else { + (*err) += "'" + property + "' property is missing.\n"; + } + } + } + return false; + } + + const json &dict = GetValue(it); + + // Make sure we are dealing with an object / dictionary. + if (!IsObject(dict)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an object.\n"; + } + } + return false; + } + + ret->clear(); + + json_const_iterator dictIt(ObjectBegin(dict)); + json_const_iterator dictItEnd(ObjectEnd(dict)); + + for (; dictIt != dictItEnd; ++dictIt) { + int intVal; + if (!GetInt(GetValue(dictIt), intVal)) { + if (required) { + if (err) { + (*err) += "'" + property + "' value is not an integer type.\n"; + } + } + return false; + } + + // Insert into the list. + (*ret)[GetKey(dictIt)] = intVal; + } + return true; +} + +static bool ParseJSONProperty(std::map *ret, + std::string *err, const json &o, + const std::string &property, bool required) { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing. \n'"; + } + } + return false; + } + + const json &obj = GetValue(it); + + if (!IsObject(obj)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a JSON object.\n"; + } + } + return false; + } + + ret->clear(); + + json_const_iterator it2(ObjectBegin(obj)); + json_const_iterator itEnd(ObjectEnd(obj)); + for (; it2 != itEnd; ++it2) { + double numVal; + if (GetNumber(GetValue(it2), numVal)) + ret->emplace(std::string(GetKey(it2)), numVal); + } + + return true; +} + +static bool ParseParameterProperty(Parameter *param, std::string *err, + const json &o, const std::string &prop, + bool required) { + // A parameter value can either be a string or an array of either a boolean or + // a number. Booleans of any kind aren't supported here. Granted, it + // complicates the Parameter structure and breaks it semantically in the sense + // that the client probably works off the assumption that if the string is + // empty the vector is used, etc. Would a tagged union work? + if (ParseStringProperty(¶m->string_value, err, o, prop, false)) { + // Found string property. + return true; + } else if (ParseNumberArrayProperty(¶m->number_array, err, o, prop, + false)) { + // Found a number array. + return true; + } else if (ParseNumberProperty(¶m->number_value, err, o, prop, false)) { + return param->has_number_value = true; + } else if (ParseJSONProperty(¶m->json_double_value, err, o, prop, + false)) { + return true; + } else if (ParseBooleanProperty(¶m->bool_value, err, o, prop, false)) { + return true; + } else { + if (required) { + if (err) { + (*err) += "parameter must be a string or number / number array.\n"; + } + } + return false; + } +} + +static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, + const json &o) { + (void)err; + + json_const_iterator it; + if (!FindMember(o, "extensions", it)) { + return false; + } + + auto &obj = GetValue(it); + if (!IsObject(obj)) { + return false; + } + ExtensionMap extensions; + json_const_iterator extIt = ObjectBegin(obj); // it.value().begin(); + json_const_iterator extEnd = ObjectEnd(obj); + for (; extIt != extEnd; ++extIt) { + auto &itObj = GetValue(extIt); + if (!IsObject(itObj)) continue; + std::string key(GetKey(extIt)); + if (!ParseJsonAsValue(&extensions[key], itObj)) { + if (!key.empty()) { + // create empty object so that an extension object is still of type + // object + extensions[key] = Value{Value::Object{}}; + } + } + } + if (ret) { + (*ret) = std::move(extensions); + } + return true; +} + +static bool ParseAsset(Asset *asset, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&asset->version, err, o, "version", true, "Asset"); + ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset"); + ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset"); + ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset"); + + ParseExtensionsProperty(&asset->extensions, err, o); + + // Unity exporter version is added as extra here + ParseExtrasProperty(&(asset->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + asset->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + asset->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseImage(Image *image, const int image_idx, std::string *err, + std::string *warn, const json &o, + bool store_original_json_for_extras_and_extensions, + const std::string &basedir, FsCallbacks *fs, + LoadImageDataFunction *LoadImageData = nullptr, + void *load_image_user_data = nullptr) { + // A glTF image must either reference a bufferView or an image uri + + // schema says oneOf [`bufferView`, `uri`] + // TODO(syoyo): Check the type of each parameters. + json_const_iterator it; + bool hasBufferView = FindMember(o, "bufferView", it); + bool hasURI = FindMember(o, "uri", it); + + ParseStringProperty(&image->name, err, o, "name", false); + + if (hasBufferView && hasURI) { + // Should not both defined. + if (err) { + (*err) += + "Only one of `bufferView` or `uri` should be defined, but both are " + "defined for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n"; + } + return false; + } + + if (!hasBufferView && !hasURI) { + if (err) { + (*err) += "Neither required `bufferView` nor `uri` defined for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + + "\"\n"; + } + return false; + } + + ParseExtensionsProperty(&image->extensions, err, o); + ParseExtrasProperty(&image->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator eit; + if (FindMember(o, "extensions", eit)) { + image->extensions_json_string = JsonToString(GetValue(eit)); + } + } + { + json_const_iterator eit; + if (FindMember(o, "extras", eit)) { + image->extras_json_string = JsonToString(GetValue(eit)); + } + } + } + + if (hasBufferView) { + int bufferView = -1; + if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) { + if (err) { + (*err) += "Failed to parse `bufferView` for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + + "\"\n"; + } + return false; + } + + std::string mime_type; + ParseStringProperty(&mime_type, err, o, "mimeType", false); + + int width = 0; + ParseIntegerProperty(&width, err, o, "width", false); + + int height = 0; + ParseIntegerProperty(&height, err, o, "height", false); + + // Just only save some information here. Loading actual image data from + // bufferView is done after this `ParseImage` function. + image->bufferView = bufferView; + image->mimeType = mime_type; + image->width = width; + image->height = height; + + return true; + } + + // Parse URI & Load image data. + + std::string uri; + std::string tmp_err; + if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) { + if (err) { + (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) + + "] name = \"" + image->name + "\".\n"; + } + return false; + } + + std::vector img; + + if (IsDataURI(uri)) { + if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) { + if (err) { + (*err) += "Failed to decode 'uri' for image[" + + std::to_string(image_idx) + "] name = [" + image->name + + "]\n"; + } + return false; + } + } else { + // Assume external file + // Keep texture path (for textures that cannot be decoded) + image->uri = uri; +#ifdef TINYGLTF_NO_EXTERNAL_IMAGE + return true; +#endif + std::string decoded_uri = dlib::urldecode(uri); + if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir, + /* required */ false, /* required bytes */ 0, + /* checksize */ false, fs)) { + if (warn) { + (*warn) += "Failed to load external 'uri' for image[" + + std::to_string(image_idx) + "] name = [" + image->name + + "]\n"; + } + // If the image cannot be loaded, keep uri as image->uri. + return true; + } + + if (img.empty()) { + if (warn) { + (*warn) += "Image data is empty for image[" + + std::to_string(image_idx) + "] name = [" + image->name + + "] \n"; + } + return false; + } + } + + if (*LoadImageData == nullptr) { + if (err) { + (*err) += "No LoadImageData callback specified.\n"; + } + return false; + } + return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0), + static_cast(img.size()), load_image_user_data); +} + +static bool ParseTexture(Texture *texture, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions, + const std::string &basedir) { + (void)basedir; + int sampler = -1; + int source = -1; + ParseIntegerProperty(&sampler, err, o, "sampler", false); + + ParseIntegerProperty(&source, err, o, "source", false); + + texture->sampler = sampler; + texture->source = source; + + ParseExtensionsProperty(&texture->extensions, err, o); + ParseExtrasProperty(&texture->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + texture->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + texture->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + ParseStringProperty(&texture->name, err, o, "name", false); + + return true; +} + +static bool ParseTextureInfo( + TextureInfo *texinfo, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (texinfo == nullptr) { + return false; + } + + if (!ParseIntegerProperty(&texinfo->index, err, o, "index", + /* required */ true, "TextureInfo")) { + return false; + } + + ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); + + ParseExtensionsProperty(&texinfo->extensions, err, o); + ParseExtrasProperty(&texinfo->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + texinfo->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + texinfo->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseNormalTextureInfo( + NormalTextureInfo *texinfo, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (texinfo == nullptr) { + return false; + } + + if (!ParseIntegerProperty(&texinfo->index, err, o, "index", + /* required */ true, "NormalTextureInfo")) { + return false; + } + + ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); + ParseNumberProperty(&texinfo->scale, err, o, "scale", false); + + ParseExtensionsProperty(&texinfo->extensions, err, o); + ParseExtrasProperty(&texinfo->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + texinfo->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + texinfo->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseOcclusionTextureInfo( + OcclusionTextureInfo *texinfo, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (texinfo == nullptr) { + return false; + } + + if (!ParseIntegerProperty(&texinfo->index, err, o, "index", + /* required */ true, "NormalTextureInfo")) { + return false; + } + + ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); + ParseNumberProperty(&texinfo->strength, err, o, "strength", false); + + ParseExtensionsProperty(&texinfo->extensions, err, o); + ParseExtrasProperty(&texinfo->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + texinfo->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + texinfo->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions, + FsCallbacks *fs, const std::string &basedir, + bool is_binary = false, + const unsigned char *bin_data = nullptr, + size_t bin_size = 0) { + size_t byteLength; + if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true, + "Buffer")) { + return false; + } + + // In glTF 2.0, uri is not mandatory anymore + buffer->uri.clear(); + ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer"); + + // having an empty uri for a non embedded image should not be valid + if (!is_binary && buffer->uri.empty()) { + if (err) { + (*err) += "'uri' is missing from non binary glTF file buffer.\n"; + } + } + + json_const_iterator type; + if (FindMember(o, "type", type)) { + std::string typeStr; + if (GetString(GetValue(type), typeStr)) { + if (typeStr.compare("arraybuffer") == 0) { + // buffer.type = "arraybuffer"; + } + } + } + + if (is_binary) { + // Still binary glTF accepts external dataURI. + if (!buffer->uri.empty()) { + // First try embedded data URI. + if (IsDataURI(buffer->uri)) { + std::string mime_type; + if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength, + true)) { + if (err) { + (*err) += + "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n"; + } + return false; + } + } else { + // External .bin file. + std::string decoded_uri = dlib::urldecode(buffer->uri); + if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, + decoded_uri, basedir, /* required */ true, + byteLength, /* checkSize */ true, fs)) { + return false; + } + } + } else { + // load data from (embedded) binary data + + if ((bin_size == 0) || (bin_data == nullptr)) { + if (err) { + (*err) += "Invalid binary data in `Buffer'.\n"; + } + return false; + } + + if (byteLength > bin_size) { + if (err) { + std::stringstream ss; + ss << "Invalid `byteLength'. Must be equal or less than binary size: " + "`byteLength' = " + << byteLength << ", binary size = " << bin_size << std::endl; + (*err) += ss.str(); + } + return false; + } + + // Read buffer data + buffer->data.resize(static_cast(byteLength)); + memcpy(&(buffer->data.at(0)), bin_data, static_cast(byteLength)); + } + + } else { + if (IsDataURI(buffer->uri)) { + std::string mime_type; + if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength, + true)) { + if (err) { + (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n"; + } + return false; + } + } else { + // Assume external .bin file. + std::string decoded_uri = dlib::urldecode(buffer->uri); + if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri, + basedir, /* required */ true, byteLength, + /* checkSize */ true, fs)) { + return false; + } + } + } + + ParseStringProperty(&buffer->name, err, o, "name", false); + + ParseExtensionsProperty(&buffer->extensions, err, o); + ParseExtrasProperty(&buffer->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + buffer->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + buffer->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseBufferView( + BufferView *bufferView, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + int buffer = -1; + if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) { + return false; + } + + size_t byteOffset = 0; + ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false); + + size_t byteLength = 1; + if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true, + "BufferView")) { + return false; + } + + size_t byteStride = 0; + if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) { + // Spec says: When byteStride of referenced bufferView is not defined, it + // means that accessor elements are tightly packed, i.e., effective stride + // equals the size of the element. + // We cannot determine the actual byteStride until Accessor are parsed, thus + // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner) + byteStride = 0; + } + + if ((byteStride > 252) || ((byteStride % 4) != 0)) { + if (err) { + std::stringstream ss; + ss << "Invalid `byteStride' value. `byteStride' must be the multiple of " + "4 : " + << byteStride << std::endl; + + (*err) += ss.str(); + } + return false; + } + + int target = 0; + ParseIntegerProperty(&target, err, o, "target", false); + if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) || + (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) { + // OK + } else { + target = 0; + } + bufferView->target = target; + + ParseStringProperty(&bufferView->name, err, o, "name", false); + + ParseExtensionsProperty(&bufferView->extensions, err, o); + ParseExtrasProperty(&bufferView->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + bufferView->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + bufferView->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + bufferView->buffer = buffer; + bufferView->byteOffset = byteOffset; + bufferView->byteLength = byteLength; + bufferView->byteStride = byteStride; + return true; +} + +static bool ParseSparseAccessor(Accessor *accessor, std::string *err, + const json &o) { + accessor->sparse.isSparse = true; + + int count = 0; + ParseIntegerProperty(&count, err, o, "count", true); + + json_const_iterator indices_iterator; + json_const_iterator values_iterator; + if (!FindMember(o, "indices", indices_iterator)) { + (*err) = "the sparse object of this accessor doesn't have indices"; + return false; + } + + if (!FindMember(o, "values", values_iterator)) { + (*err) = "the sparse object ob ths accessor doesn't have values"; + return false; + } + + const json &indices_obj = GetValue(indices_iterator); + const json &values_obj = GetValue(values_iterator); + + int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0; + ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView", + true); + ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset", + true); + ParseIntegerProperty(&component_type, err, indices_obj, "componentType", + true); + + int values_buffer_view = 0, values_byte_offset = 0; + ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView", + true); + ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset", + true); + + accessor->sparse.count = count; + accessor->sparse.indices.bufferView = indices_buffer_view; + accessor->sparse.indices.byteOffset = indices_byte_offset; + accessor->sparse.indices.componentType = component_type; + accessor->sparse.values.bufferView = values_buffer_view; + accessor->sparse.values.byteOffset = values_byte_offset; + + // todo check theses values + + return true; +} + +static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + int bufferView = -1; + ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor"); + + size_t byteOffset = 0; + ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor"); + + bool normalized = false; + ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor"); + + size_t componentType = 0; + if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true, + "Accessor")) { + return false; + } + + size_t count = 0; + if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) { + return false; + } + + std::string type; + if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) { + return false; + } + + if (type.compare("SCALAR") == 0) { + accessor->type = TINYGLTF_TYPE_SCALAR; + } else if (type.compare("VEC2") == 0) { + accessor->type = TINYGLTF_TYPE_VEC2; + } else if (type.compare("VEC3") == 0) { + accessor->type = TINYGLTF_TYPE_VEC3; + } else if (type.compare("VEC4") == 0) { + accessor->type = TINYGLTF_TYPE_VEC4; + } else if (type.compare("MAT2") == 0) { + accessor->type = TINYGLTF_TYPE_MAT2; + } else if (type.compare("MAT3") == 0) { + accessor->type = TINYGLTF_TYPE_MAT3; + } else if (type.compare("MAT4") == 0) { + accessor->type = TINYGLTF_TYPE_MAT4; + } else { + std::stringstream ss; + ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n"; + if (err) { + (*err) += ss.str(); + } + return false; + } + + ParseStringProperty(&accessor->name, err, o, "name", false); + + accessor->minValues.clear(); + accessor->maxValues.clear(); + ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false, + "Accessor"); + + ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false, + "Accessor"); + + accessor->count = count; + accessor->bufferView = bufferView; + accessor->byteOffset = byteOffset; + accessor->normalized = normalized; + { + if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE && + componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) { + // OK + accessor->componentType = int(componentType); + } else { + std::stringstream ss; + ss << "Invalid `componentType` in accessor. Got " << componentType + << "\n"; + if (err) { + (*err) += ss.str(); + } + return false; + } + } + + ParseExtensionsProperty(&(accessor->extensions), err, o); + ParseExtrasProperty(&(accessor->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + accessor->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + accessor->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + // check if accessor has a "sparse" object: + json_const_iterator iterator; + if (FindMember(o, "sparse", iterator)) { + // here this accessor has a "sparse" subobject + return ParseSparseAccessor(accessor, err, GetValue(iterator)); + } + + return true; +} + +#ifdef TINYGLTF_ENABLE_DRACO + +static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize, + std::vector &outBuffer) { + if (componentSize == 4) { + assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize); + memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0], + outBuffer.size()); + } else { + size_t faceStride = componentSize * 3; + for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) { + const draco::Mesh::Face &face = mesh->face(f); + if (componentSize == 2) { + uint16_t indices[3] = {(uint16_t)face[0].value(), + (uint16_t)face[1].value(), + (uint16_t)face[2].value()}; + memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], + faceStride); + } else { + uint8_t indices[3] = {(uint8_t)face[0].value(), + (uint8_t)face[1].value(), + (uint8_t)face[2].value()}; + memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], + faceStride); + } + } + } +} + +template +static bool GetAttributeForAllPoints(draco::Mesh *mesh, + const draco::PointAttribute *pAttribute, + std::vector &outBuffer) { + size_t byteOffset = 0; + T values[4] = {0, 0, 0, 0}; + for (draco::PointIndex i(0); i < mesh->num_points(); ++i) { + const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i); + if (!pAttribute->ConvertValue(val_index, pAttribute->num_components(), + values)) + return false; + + memcpy(outBuffer.data() + byteOffset, &values[0], + sizeof(T) * pAttribute->num_components()); + byteOffset += sizeof(T) * pAttribute->num_components(); + } + + return true; +} + +static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh, + const draco::PointAttribute *pAttribute, + std::vector &outBuffer) { + bool decodeResult = false; + switch (componentType) { + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_BYTE: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_SHORT: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_INT: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_FLOAT: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_DOUBLE: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + default: + return false; + } + + return decodeResult; +} + +static bool ParseDracoExtension(Primitive *primitive, Model *model, + std::string *err, + const Value &dracoExtensionValue) { + (void)err; + auto bufferViewValue = dracoExtensionValue.Get("bufferView"); + if (!bufferViewValue.IsInt()) return false; + auto attributesValue = dracoExtensionValue.Get("attributes"); + if (!attributesValue.IsObject()) return false; + + auto attributesObject = attributesValue.Get(); + int bufferView = bufferViewValue.Get(); + + BufferView &view = model->bufferViews[bufferView]; + Buffer &buffer = model->buffers[view.buffer]; + // BufferView has already been decoded + if (view.dracoDecoded) return true; + view.dracoDecoded = true; + + const char *bufferViewData = + reinterpret_cast(buffer.data.data() + view.byteOffset); + size_t bufferViewSize = view.byteLength; + + // decode draco + draco::DecoderBuffer decoderBuffer; + decoderBuffer.Init(bufferViewData, bufferViewSize); + draco::Decoder decoder; + auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer); + if (!decodeResult.ok()) { + return false; + } + const std::unique_ptr &mesh = decodeResult.value(); + + // create new bufferView for indices + if (primitive->indices >= 0) { + int32_t componentSize = GetComponentSizeInBytes( + model->accessors[primitive->indices].componentType); + Buffer decodedIndexBuffer; + decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize); + + DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data); + + model->buffers.emplace_back(std::move(decodedIndexBuffer)); + + BufferView decodedIndexBufferView; + decodedIndexBufferView.buffer = int(model->buffers.size() - 1); + decodedIndexBufferView.byteLength = + int(mesh->num_faces() * 3 * componentSize); + decodedIndexBufferView.byteOffset = 0; + decodedIndexBufferView.byteStride = 0; + decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER; + model->bufferViews.emplace_back(std::move(decodedIndexBufferView)); + + model->accessors[primitive->indices].bufferView = + int(model->bufferViews.size() - 1); + model->accessors[primitive->indices].count = int(mesh->num_faces() * 3); + } + + for (const auto &attribute : attributesObject) { + if (!attribute.second.IsInt()) return false; + auto primitiveAttribute = primitive->attributes.find(attribute.first); + if (primitiveAttribute == primitive->attributes.end()) return false; + + int dracoAttributeIndex = attribute.second.Get(); + const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex); + const auto componentType = + model->accessors[primitiveAttribute->second].componentType; + + // Create a new buffer for this decoded buffer + Buffer decodedBuffer; + size_t bufferSize = mesh->num_points() * pAttribute->num_components() * + GetComponentSizeInBytes(componentType); + decodedBuffer.data.resize(bufferSize); + + if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute, + decodedBuffer.data)) + return false; + + model->buffers.emplace_back(std::move(decodedBuffer)); + + BufferView decodedBufferView; + decodedBufferView.buffer = int(model->buffers.size() - 1); + decodedBufferView.byteLength = bufferSize; + decodedBufferView.byteOffset = pAttribute->byte_offset(); + decodedBufferView.byteStride = pAttribute->byte_stride(); + decodedBufferView.target = primitive->indices >= 0 + ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER + : TINYGLTF_TARGET_ARRAY_BUFFER; + model->bufferViews.emplace_back(std::move(decodedBufferView)); + + model->accessors[primitiveAttribute->second].bufferView = + int(model->bufferViews.size() - 1); + model->accessors[primitiveAttribute->second].count = + int(mesh->num_points()); + } + + return true; +} +#endif + +static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, + const json &o, + bool store_original_json_for_extras_and_extensions) { + int material = -1; + ParseIntegerProperty(&material, err, o, "material", false); + primitive->material = material; + + int mode = TINYGLTF_MODE_TRIANGLES; + ParseIntegerProperty(&mode, err, o, "mode", false); + primitive->mode = mode; // Why only triangled were supported ? + + int indices = -1; + ParseIntegerProperty(&indices, err, o, "indices", false); + primitive->indices = indices; + if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes", + true, "Primitive")) { + return false; + } + + // Look for morph targets + json_const_iterator targetsObject; + if (FindMember(o, "targets", targetsObject) && + IsArray(GetValue(targetsObject))) { + auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject)); + for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject)); + i != targetsObjectEnd; ++i) { + std::map targetAttribues; + + const json &dict = *i; + if (IsObject(dict)) { + json_const_iterator dictIt(ObjectBegin(dict)); + json_const_iterator dictItEnd(ObjectEnd(dict)); + + for (; dictIt != dictItEnd; ++dictIt) { + int iVal; + if (GetInt(GetValue(dictIt), iVal)) + targetAttribues[GetKey(dictIt)] = iVal; + } + primitive->targets.emplace_back(std::move(targetAttribues)); + } + } + } + + ParseExtrasProperty(&(primitive->extras), o); + ParseExtensionsProperty(&primitive->extensions, err, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + primitive->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + primitive->extras_json_string = JsonToString(GetValue(it)); + } + } + } + +#ifdef TINYGLTF_ENABLE_DRACO + auto dracoExtension = + primitive->extensions.find("KHR_draco_mesh_compression"); + if (dracoExtension != primitive->extensions.end()) { + ParseDracoExtension(primitive, model, err, dracoExtension->second); + } +#else + (void)model; +#endif + + return true; +} + +static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&mesh->name, err, o, "name", false); + + mesh->primitives.clear(); + json_const_iterator primObject; + if (FindMember(o, "primitives", primObject) && + IsArray(GetValue(primObject))) { + json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject)); + for (json_const_array_iterator i = ArrayBegin(GetValue(primObject)); + i != primEnd; ++i) { + Primitive primitive; + if (ParsePrimitive(&primitive, model, err, *i, + store_original_json_for_extras_and_extensions)) { + // Only add the primitive if the parsing succeeds. + mesh->primitives.emplace_back(std::move(primitive)); + } + } + } + + // Should probably check if has targets and if dimensions fit + ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false); + + ParseExtensionsProperty(&mesh->extensions, err, o); + ParseExtrasProperty(&(mesh->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + mesh->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + mesh->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseNode(Node *node, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&node->name, err, o, "name", false); + + int skin = -1; + ParseIntegerProperty(&skin, err, o, "skin", false); + node->skin = skin; + + // Matrix and T/R/S are exclusive + if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) { + ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false); + ParseNumberArrayProperty(&node->scale, err, o, "scale", false); + ParseNumberArrayProperty(&node->translation, err, o, "translation", false); + } + + int camera = -1; + ParseIntegerProperty(&camera, err, o, "camera", false); + node->camera = camera; + + int mesh = -1; + ParseIntegerProperty(&mesh, err, o, "mesh", false); + node->mesh = mesh; + + node->children.clear(); + ParseIntegerArrayProperty(&node->children, err, o, "children", false); + + ParseNumberArrayProperty(&node->weights, err, o, "weights", false); + + ParseExtensionsProperty(&node->extensions, err, o); + ParseExtrasProperty(&(node->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + node->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + node->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParsePbrMetallicRoughness( + PbrMetallicRoughness *pbr, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (pbr == nullptr) { + return false; + } + + std::vector baseColorFactor; + if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor", + /* required */ false)) { + if (baseColorFactor.size() != 4) { + if (err) { + (*err) += + "Array length of `baseColorFactor` parameter in " + "pbrMetallicRoughness must be 4, but got " + + std::to_string(baseColorFactor.size()) + "\n"; + } + return false; + } + pbr->baseColorFactor = baseColorFactor; + } + + { + json_const_iterator it; + if (FindMember(o, "baseColorTexture", it)) { + ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + { + json_const_iterator it; + if (FindMember(o, "metallicRoughnessTexture", it)) { + ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false); + ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false); + + ParseExtensionsProperty(&pbr->extensions, err, o); + ParseExtrasProperty(&pbr->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + pbr->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + pbr->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseMaterial(Material *material, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&material->name, err, o, "name", /* required */ false); + + if (ParseNumberArrayProperty(&material->emissiveFactor, err, o, + "emissiveFactor", + /* required */ false)) { + if (material->emissiveFactor.size() != 3) { + if (err) { + (*err) += + "Array length of `emissiveFactor` parameter in " + "material must be 3, but got " + + std::to_string(material->emissiveFactor.size()) + "\n"; + } + return false; + } + } else { + // fill with default values + material->emissiveFactor = {0.0, 0.0, 0.0}; + } + + ParseStringProperty(&material->alphaMode, err, o, "alphaMode", + /* required */ false); + ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff", + /* required */ false); + ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided", + /* required */ false); + + { + json_const_iterator it; + if (FindMember(o, "pbrMetallicRoughness", it)) { + ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err, + GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + { + json_const_iterator it; + if (FindMember(o, "normalTexture", it)) { + ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + { + json_const_iterator it; + if (FindMember(o, "occlusionTexture", it)) { + ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + { + json_const_iterator it; + if (FindMember(o, "emissiveTexture", it)) { + ParseTextureInfo(&material->emissiveTexture, err, GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + // Old code path. For backward compatibility, we still store material values + // as Parameter. This will create duplicated information for + // example(pbrMetallicRoughness), but should be neglible in terms of memory + // consumption. + // TODO(syoyo): Remove in the next major release. + material->values.clear(); + material->additionalValues.clear(); + + json_const_iterator it(ObjectBegin(o)); + json_const_iterator itEnd(ObjectEnd(o)); + + for (; it != itEnd; ++it) { + std::string key(GetKey(it)); + if (key == "pbrMetallicRoughness") { + if (IsObject(GetValue(it))) { + const json &values_object = GetValue(it); + + json_const_iterator itVal(ObjectBegin(values_object)); + json_const_iterator itValEnd(ObjectEnd(values_object)); + + for (; itVal != itValEnd; ++itVal) { + Parameter param; + if (ParseParameterProperty(¶m, err, values_object, GetKey(itVal), + false)) { + material->values.emplace(GetKey(itVal), std::move(param)); + } + } + } + } else if (key == "extensions" || key == "extras") { + // done later, skip, otherwise poorly parsed contents will be saved in the + // parametermap and serialized again later + } else { + Parameter param; + if (ParseParameterProperty(¶m, err, o, key, false)) { + // names of materials have already been parsed. Putting it in this map + // doesn't correctly reflext the glTF specification + if (key != "name") + material->additionalValues.emplace(std::move(key), std::move(param)); + } + } + } + + material->extensions.clear(); + ParseExtensionsProperty(&material->extensions, err, o); + ParseExtrasProperty(&(material->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator eit; + if (FindMember(o, "extensions", eit)) { + material->extensions_json_string = JsonToString(GetValue(eit)); + } + } + { + json_const_iterator eit; + if (FindMember(o, "extras", eit)) { + material->extras_json_string = JsonToString(GetValue(eit)); + } + } + } + + return true; +} + +static bool ParseAnimationChannel( + AnimationChannel *channel, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + int samplerIndex = -1; + int targetIndex = -1; + if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true, + "AnimationChannel")) { + if (err) { + (*err) += "`sampler` field is missing in animation channels\n"; + } + return false; + } + + json_const_iterator targetIt; + if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) { + const json &target_object = GetValue(targetIt); + + if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) { + if (err) { + (*err) += "`node` field is missing in animation.channels.target\n"; + } + return false; + } + + if (!ParseStringProperty(&channel->target_path, err, target_object, "path", + true)) { + if (err) { + (*err) += "`path` field is missing in animation.channels.target\n"; + } + return false; + } + ParseExtensionsProperty(&channel->target_extensions, err, target_object); + if (store_original_json_for_extras_and_extensions) { + json_const_iterator it; + if (FindMember(target_object, "extensions", it)) { + channel->target_extensions_json_string = JsonToString(GetValue(it)); + } + } + } + + channel->sampler = samplerIndex; + channel->target_node = targetIndex; + + ParseExtensionsProperty(&channel->extensions, err, o); + ParseExtrasProperty(&(channel->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + channel->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + channel->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseAnimation(Animation *animation, std::string *err, + const json &o, + bool store_original_json_for_extras_and_extensions) { + { + json_const_iterator channelsIt; + if (FindMember(o, "channels", channelsIt) && + IsArray(GetValue(channelsIt))) { + json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt)); + for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt)); + i != channelEnd; ++i) { + AnimationChannel channel; + if (ParseAnimationChannel( + &channel, err, *i, + store_original_json_for_extras_and_extensions)) { + // Only add the channel if the parsing succeeds. + animation->channels.emplace_back(std::move(channel)); + } + } + } + } + + { + json_const_iterator samplerIt; + if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) { + const json &sampler_array = GetValue(samplerIt); + + json_const_array_iterator it = ArrayBegin(sampler_array); + json_const_array_iterator itEnd = ArrayEnd(sampler_array); + + for (; it != itEnd; ++it) { + const json &s = *it; + + AnimationSampler sampler; + int inputIndex = -1; + int outputIndex = -1; + if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) { + if (err) { + (*err) += "`input` field is missing in animation.sampler\n"; + } + return false; + } + ParseStringProperty(&sampler.interpolation, err, s, "interpolation", + false); + if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) { + if (err) { + (*err) += "`output` field is missing in animation.sampler\n"; + } + return false; + } + sampler.input = inputIndex; + sampler.output = outputIndex; + ParseExtensionsProperty(&(sampler.extensions), err, o); + ParseExtrasProperty(&(sampler.extras), s); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator eit; + if (FindMember(o, "extensions", eit)) { + sampler.extensions_json_string = JsonToString(GetValue(eit)); + } + } + { + json_const_iterator eit; + if (FindMember(o, "extras", eit)) { + sampler.extras_json_string = JsonToString(GetValue(eit)); + } + } + } + + animation->samplers.emplace_back(std::move(sampler)); + } + } + } + + ParseStringProperty(&animation->name, err, o, "name", false); + + ParseExtensionsProperty(&animation->extensions, err, o); + ParseExtrasProperty(&(animation->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + animation->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + animation->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseSampler(Sampler *sampler, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&sampler->name, err, o, "name", false); + + int minFilter = -1; + int magFilter = -1; + int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT; + int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT; + //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; + ParseIntegerProperty(&minFilter, err, o, "minFilter", false); + ParseIntegerProperty(&magFilter, err, o, "magFilter", false); + ParseIntegerProperty(&wrapS, err, o, "wrapS", false); + ParseIntegerProperty(&wrapT, err, o, "wrapT", false); + //ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension + + // TODO(syoyo): Check the value is alloed one. + // (e.g. we allow 9728(NEAREST), but don't allow 9727) + + sampler->minFilter = minFilter; + sampler->magFilter = magFilter; + sampler->wrapS = wrapS; + sampler->wrapT = wrapT; + //sampler->wrapR = wrapR; + + ParseExtensionsProperty(&(sampler->extensions), err, o); + ParseExtrasProperty(&(sampler->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + sampler->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + sampler->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseSkin(Skin *skin, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&skin->name, err, o, "name", false, "Skin"); + + std::vector joints; + if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) { + return false; + } + skin->joints = std::move(joints); + + int skeleton = -1; + ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin"); + skin->skeleton = skeleton; + + int invBind = -1; + ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin"); + skin->inverseBindMatrices = invBind; + + ParseExtensionsProperty(&(skin->extensions), err, o); + ParseExtrasProperty(&(skin->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + skin->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + skin->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParsePerspectiveCamera( + PerspectiveCamera *camera, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + double yfov = 0.0; + if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) { + return false; + } + + double znear = 0.0; + if (!ParseNumberProperty(&znear, err, o, "znear", true, + "PerspectiveCamera")) { + return false; + } + + double aspectRatio = 0.0; // = invalid + ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false, + "PerspectiveCamera"); + + double zfar = 0.0; // = invalid + ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera"); + + camera->aspectRatio = aspectRatio; + camera->zfar = zfar; + camera->yfov = yfov; + camera->znear = znear; + + ParseExtensionsProperty(&camera->extensions, err, o); + ParseExtrasProperty(&(camera->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + camera->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + camera->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + // TODO(syoyo): Validate parameter values. + + return true; +} + +static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false); + ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false); + + ParseExtensionsProperty(&light->extensions, err, o); + ParseExtrasProperty(&light->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + light->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + light->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + // TODO(syoyo): Validate parameter values. + + return true; +} + +static bool ParseOrthographicCamera( + OrthographicCamera *camera, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + double xmag = 0.0; + if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) { + return false; + } + + double ymag = 0.0; + if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) { + return false; + } + + double zfar = 0.0; + if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) { + return false; + } + + double znear = 0.0; + if (!ParseNumberProperty(&znear, err, o, "znear", true, + "OrthographicCamera")) { + return false; + } + + ParseExtensionsProperty(&camera->extensions, err, o); + ParseExtrasProperty(&(camera->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + camera->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + camera->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + camera->xmag = xmag; + camera->ymag = ymag; + camera->zfar = zfar; + camera->znear = znear; + + // TODO(syoyo): Validate parameter values. + + return true; +} + +static bool ParseCamera(Camera *camera, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) { + return false; + } + + if (camera->type.compare("orthographic") == 0) { + json_const_iterator orthoIt; + if (!FindMember(o, "orthographic", orthoIt)) { + if (err) { + std::stringstream ss; + ss << "Orhographic camera description not found." << std::endl; + (*err) += ss.str(); + } + return false; + } + + const json &v = GetValue(orthoIt); + if (!IsObject(v)) { + if (err) { + std::stringstream ss; + ss << "\"orthographic\" is not a JSON object." << std::endl; + (*err) += ss.str(); + } + return false; + } + + if (!ParseOrthographicCamera( + &camera->orthographic, err, v, + store_original_json_for_extras_and_extensions)) { + return false; + } + } else if (camera->type.compare("perspective") == 0) { + json_const_iterator perspIt; + if (!FindMember(o, "perspective", perspIt)) { + if (err) { + std::stringstream ss; + ss << "Perspective camera description not found." << std::endl; + (*err) += ss.str(); + } + return false; + } + + const json &v = GetValue(perspIt); + if (!IsObject(v)) { + if (err) { + std::stringstream ss; + ss << "\"perspective\" is not a JSON object." << std::endl; + (*err) += ss.str(); + } + return false; + } + + if (!ParsePerspectiveCamera( + &camera->perspective, err, v, + store_original_json_for_extras_and_extensions)) { + return false; + } + } else { + if (err) { + std::stringstream ss; + ss << "Invalid camera type: \"" << camera->type + << "\". Must be \"perspective\" or \"orthographic\"" << std::endl; + (*err) += ss.str(); + } + return false; + } + + ParseStringProperty(&camera->name, err, o, "name", false); + + ParseExtensionsProperty(&camera->extensions, err, o); + ParseExtrasProperty(&(camera->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + camera->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + camera->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseLight(Light *light, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (!ParseStringProperty(&light->type, err, o, "type", true)) { + return false; + } + + if (light->type == "spot") { + json_const_iterator spotIt; + if (!FindMember(o, "spot", spotIt)) { + if (err) { + std::stringstream ss; + ss << "Spot light description not found." << std::endl; + (*err) += ss.str(); + } + return false; + } + + const json &v = GetValue(spotIt); + if (!IsObject(v)) { + if (err) { + std::stringstream ss; + ss << "\"spot\" is not a JSON object." << std::endl; + (*err) += ss.str(); + } + return false; + } + + if (!ParseSpotLight(&light->spot, err, v, + store_original_json_for_extras_and_extensions)) { + return false; + } + } + + ParseStringProperty(&light->name, err, o, "name", false); + ParseNumberArrayProperty(&light->color, err, o, "color", false); + ParseNumberProperty(&light->range, err, o, "range", false); + ParseNumberProperty(&light->intensity, err, o, "intensity", false); + ParseExtensionsProperty(&light->extensions, err, o); + ParseExtrasProperty(&(light->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + light->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + light->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, + const char *json_str, + unsigned int json_str_length, + const std::string &base_dir, + unsigned int check_sections) { + if (json_str_length < 4) { + if (err) { + (*err) = "JSON string too short.\n"; + } + return false; + } + + JsonDocument v; + +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \ + defined(_CPPUNWIND)) && \ + !defined(TINYGLTF_NOEXCEPTION) + try { + JsonParse(v, json_str, json_str_length, true); + + } catch (const std::exception &e) { + if (err) { + (*err) = e.what(); + } + return false; + } +#else + { + JsonParse(v, json_str, json_str_length); + + if (!IsObject(v)) { + // Assume parsing was failed. + if (err) { + (*err) = "Failed to parse JSON object\n"; + } + return false; + } + } +#endif + + if (!IsObject(v)) { + // root is not an object. + if (err) { + (*err) = "Root element is not a JSON object\n"; + } + return false; + } + + { + bool version_found = false; + json_const_iterator it; + if (FindMember(v, "asset", it) && IsObject(GetValue(it))) { + auto &itObj = GetValue(it); + json_const_iterator version_it; + std::string versionStr; + if (FindMember(itObj, "version", version_it) && + GetString(GetValue(version_it), versionStr)) { + version_found = true; + } + } + if (version_found) { + // OK + } else if (check_sections & REQUIRE_VERSION) { + if (err) { + (*err) += "\"asset\" object not found in .gltf or not an object type\n"; + } + return false; + } + } + + // scene is not mandatory. + // FIXME Maybe a better way to handle it than removing the code + + auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool { + json_const_iterator it; + return FindMember(_v, name, it) && IsArray(GetValue(it)); + }; + + { + if ((check_sections & REQUIRE_SCENES) && + !IsArrayMemberPresent(v, "scenes")) { + if (err) { + (*err) += "\"scenes\" object not found in .gltf or not an array type\n"; + } + return false; + } + } + + { + if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) { + if (err) { + (*err) += "\"nodes\" object not found in .gltf\n"; + } + return false; + } + } + + { + if ((check_sections & REQUIRE_ACCESSORS) && + !IsArrayMemberPresent(v, "accessors")) { + if (err) { + (*err) += "\"accessors\" object not found in .gltf\n"; + } + return false; + } + } + + { + if ((check_sections & REQUIRE_BUFFERS) && + !IsArrayMemberPresent(v, "buffers")) { + if (err) { + (*err) += "\"buffers\" object not found in .gltf\n"; + } + return false; + } + } + + { + if ((check_sections & REQUIRE_BUFFER_VIEWS) && + !IsArrayMemberPresent(v, "bufferViews")) { + if (err) { + (*err) += "\"bufferViews\" object not found in .gltf\n"; + } + return false; + } + } + + model->buffers.clear(); + model->bufferViews.clear(); + model->accessors.clear(); + model->meshes.clear(); + model->cameras.clear(); + model->nodes.clear(); + model->extensionsUsed.clear(); + model->extensionsRequired.clear(); + model->extensions.clear(); + model->defaultScene = -1; + + // 1. Parse Asset + { + json_const_iterator it; + if (FindMember(v, "asset", it) && IsObject(GetValue(it))) { + const json &root = GetValue(it); + + ParseAsset(&model->asset, err, root, + store_original_json_for_extras_and_extensions_); + } + } + +#ifdef TINYGLTF_USE_CPP14 + auto ForEachInArray = [](const json &_v, const char *member, + const auto &cb) -> bool +#else + // The std::function<> implementation can be less efficient because it will + // allocate heap when the size of the captured lambda is above 16 bytes with + // clang and gcc, but it does not require C++14. + auto ForEachInArray = [](const json &_v, const char *member, + const std::function &cb) -> bool +#endif + { + json_const_iterator itm; + if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) { + const json &root = GetValue(itm); + auto it = ArrayBegin(root); + auto end = ArrayEnd(root); + for (; it != end; ++it) { + if (!cb(*it)) return false; + } + } + return true; + }; + + // 2. Parse extensionUsed + { + ForEachInArray(v, "extensionsUsed", [&](const json &o) { + std::string str; + GetString(o, str); + model->extensionsUsed.emplace_back(std::move(str)); + return true; + }); + } + + { + ForEachInArray(v, "extensionsRequired", [&](const json &o) { + std::string str; + GetString(o, str); + model->extensionsRequired.emplace_back(std::move(str)); + return true; + }); + } + + // 3. Parse Buffer + { + bool success = ForEachInArray(v, "buffers", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`buffers' does not contain an JSON object."; + } + return false; + } + Buffer buffer; + if (!ParseBuffer(&buffer, err, o, + store_original_json_for_extras_and_extensions_, &fs, + base_dir, is_binary_, bin_data_, bin_size_)) { + return false; + } + + model->buffers.emplace_back(std::move(buffer)); + return true; + }); + + if (!success) { + return false; + } + } + // 4. Parse BufferView + { + bool success = ForEachInArray(v, "bufferViews", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`bufferViews' does not contain an JSON object."; + } + return false; + } + BufferView bufferView; + if (!ParseBufferView(&bufferView, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->bufferViews.emplace_back(std::move(bufferView)); + return true; + }); + + if (!success) { + return false; + } + } + + // 5. Parse Accessor + { + bool success = ForEachInArray(v, "accessors", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`accessors' does not contain an JSON object."; + } + return false; + } + Accessor accessor; + if (!ParseAccessor(&accessor, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->accessors.emplace_back(std::move(accessor)); + return true; + }); + + if (!success) { + return false; + } + } + + // 6. Parse Mesh + { + bool success = ForEachInArray(v, "meshes", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`meshes' does not contain an JSON object."; + } + return false; + } + Mesh mesh; + if (!ParseMesh(&mesh, model, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->meshes.emplace_back(std::move(mesh)); + return true; + }); + + if (!success) { + return false; + } + } + + // Assign missing bufferView target types + // - Look for missing Mesh indices + // - Look for missing Mesh attributes + for (auto &mesh : model->meshes) { + for (auto &primitive : mesh.primitives) { + if (primitive.indices > + -1) // has indices from parsing step, must be Element Array Buffer + { + if (size_t(primitive.indices) >= model->accessors.size()) { + if (err) { + (*err) += "primitive indices accessor out of bounds"; + } + return false; + } + + auto bufferView = + model->accessors[size_t(primitive.indices)].bufferView; + if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) { + if (err) { + (*err) += "accessor[" + std::to_string(primitive.indices) + + "] invalid bufferView"; + } + return false; + } + + model->bufferViews[size_t(bufferView)].target = + TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; + // we could optionally check if acessors' bufferView type is Scalar, as + // it should be + } + + for (auto &attribute : primitive.attributes) { + model + ->bufferViews[size_t( + model->accessors[size_t(attribute.second)].bufferView)] + .target = TINYGLTF_TARGET_ARRAY_BUFFER; + } + + for (auto &target : primitive.targets) { + for (auto &attribute : target) { + auto bufferView = + model->accessors[size_t(attribute.second)].bufferView; + // bufferView could be null(-1) for sparse morph target + if (bufferView >= 0) { + model->bufferViews[size_t(bufferView)].target = + TINYGLTF_TARGET_ARRAY_BUFFER; + } + } + } + } + } + + // 7. Parse Node + { + bool success = ForEachInArray(v, "nodes", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`nodes' does not contain an JSON object."; + } + return false; + } + Node node; + if (!ParseNode(&node, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->nodes.emplace_back(std::move(node)); + return true; + }); + + if (!success) { + return false; + } + } + + // 8. Parse scenes. + { + bool success = ForEachInArray(v, "scenes", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`scenes' does not contain an JSON object."; + } + return false; + } + std::vector nodes; + ParseIntegerArrayProperty(&nodes, err, o, "nodes", false); + + Scene scene; + scene.nodes = std::move(nodes); + + ParseStringProperty(&scene.name, err, o, "name", false); + + ParseExtensionsProperty(&scene.extensions, err, o); + ParseExtrasProperty(&scene.extras, o); + + if (store_original_json_for_extras_and_extensions_) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + scene.extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + scene.extras_json_string = JsonToString(GetValue(it)); + } + } + } + + model->scenes.emplace_back(std::move(scene)); + return true; + }); + + if (!success) { + return false; + } + } + + // 9. Parse default scenes. + { + json_const_iterator rootIt; + int iVal; + if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) { + model->defaultScene = iVal; + } + } + + // 10. Parse Material + { + bool success = ForEachInArray(v, "materials", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`materials' does not contain an JSON object."; + } + return false; + } + Material material; + ParseStringProperty(&material.name, err, o, "name", false); + + if (!ParseMaterial(&material, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->materials.emplace_back(std::move(material)); + return true; + }); + + if (!success) { + return false; + } + } + + // 11. Parse Image + void *load_image_user_data{nullptr}; + + LoadImageDataOption load_image_option; + + if (user_image_loader_) { + // Use user supplied pointer + load_image_user_data = load_image_user_data_; + } else { + load_image_option.preserve_channels = preserve_image_channels_; + load_image_user_data = reinterpret_cast(&load_image_option); + } + + { + int idx = 0; + bool success = ForEachInArray(v, "images", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "image[" + std::to_string(idx) + "] is not a JSON object."; + } + return false; + } + Image image; + if (!ParseImage(&image, idx, err, warn, o, + store_original_json_for_extras_and_extensions_, base_dir, + &fs, &this->LoadImageData, load_image_user_data)) { + return false; + } + + if (image.bufferView != -1) { + // Load image from the buffer view. + if (size_t(image.bufferView) >= model->bufferViews.size()) { + if (err) { + std::stringstream ss; + ss << "image[" << idx << "] bufferView \"" << image.bufferView + << "\" not found in the scene." << std::endl; + (*err) += ss.str(); + } + return false; + } + + const BufferView &bufferView = + model->bufferViews[size_t(image.bufferView)]; + if (size_t(bufferView.buffer) >= model->buffers.size()) { + if (err) { + std::stringstream ss; + ss << "image[" << idx << "] buffer \"" << bufferView.buffer + << "\" not found in the scene." << std::endl; + (*err) += ss.str(); + } + return false; + } + const Buffer &buffer = model->buffers[size_t(bufferView.buffer)]; + + if (*LoadImageData == nullptr) { + if (err) { + (*err) += "No LoadImageData callback specified.\n"; + } + return false; + } + bool ret = LoadImageData( + &image, idx, err, warn, image.width, image.height, + &buffer.data[bufferView.byteOffset], + static_cast(bufferView.byteLength), load_image_user_data); + if (!ret) { + return false; + } + } + + model->images.emplace_back(std::move(image)); + ++idx; + return true; + }); + + if (!success) { + return false; + } + } + + // 12. Parse Texture + { + bool success = ForEachInArray(v, "textures", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`textures' does not contain an JSON object."; + } + return false; + } + Texture texture; + if (!ParseTexture(&texture, err, o, + store_original_json_for_extras_and_extensions_, + base_dir)) { + return false; + } + + model->textures.emplace_back(std::move(texture)); + return true; + }); + + if (!success) { + return false; + } + } + + // 13. Parse Animation + { + bool success = ForEachInArray(v, "animations", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`animations' does not contain an JSON object."; + } + return false; + } + Animation animation; + if (!ParseAnimation(&animation, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->animations.emplace_back(std::move(animation)); + return true; + }); + + if (!success) { + return false; + } + } + + // 14. Parse Skin + { + bool success = ForEachInArray(v, "skins", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`skins' does not contain an JSON object."; + } + return false; + } + Skin skin; + if (!ParseSkin(&skin, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->skins.emplace_back(std::move(skin)); + return true; + }); + + if (!success) { + return false; + } + } + + // 15. Parse Sampler + { + bool success = ForEachInArray(v, "samplers", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`samplers' does not contain an JSON object."; + } + return false; + } + Sampler sampler; + if (!ParseSampler(&sampler, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->samplers.emplace_back(std::move(sampler)); + return true; + }); + + if (!success) { + return false; + } + } + + // 16. Parse Camera + { + bool success = ForEachInArray(v, "cameras", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`cameras' does not contain an JSON object."; + } + return false; + } + Camera camera; + if (!ParseCamera(&camera, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->cameras.emplace_back(std::move(camera)); + return true; + }); + + if (!success) { + return false; + } + } + + // 17. Parse Extensions + ParseExtensionsProperty(&model->extensions, err, v); + + // 18. Specific extension implementations + { + json_const_iterator rootIt; + if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) { + const json &root = GetValue(rootIt); + + json_const_iterator it(ObjectBegin(root)); + json_const_iterator itEnd(ObjectEnd(root)); + for (; it != itEnd; ++it) { + // parse KHR_lights_punctual extension + std::string key(GetKey(it)); + if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) { + const json &object = GetValue(it); + json_const_iterator itLight; + if (FindMember(object, "lights", itLight)) { + const json &lights = GetValue(itLight); + if (!IsArray(lights)) { + continue; + } + + auto arrayIt(ArrayBegin(lights)); + auto arrayItEnd(ArrayEnd(lights)); + for (; arrayIt != arrayItEnd; ++arrayIt) { + Light light; + if (!ParseLight(&light, err, *arrayIt, + store_original_json_for_extras_and_extensions_)) { + return false; + } + model->lights.emplace_back(std::move(light)); + } + } + } + } + } + } + + // 19. Parse Extras + ParseExtrasProperty(&model->extras, v); + + if (store_original_json_for_extras_and_extensions_) { + model->extras_json_string = JsonToString(v["extras"]); + model->extensions_json_string = JsonToString(v["extensions"]); + } + + return true; +} + +bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err, + std::string *warn, const char *str, + unsigned int length, + const std::string &base_dir, + unsigned int check_sections) { + is_binary_ = false; + bin_data_ = nullptr; + bin_size_ = 0; + + return LoadFromString(model, err, warn, str, length, base_dir, + check_sections); +} + +bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err, + std::string *warn, const std::string &filename, + unsigned int check_sections) { + std::stringstream ss; + + if (fs.ReadWholeFile == nullptr) { + // Programmer error, assert() ? + ss << "Failed to read file: " << filename + << ": one or more FS callback not set" << std::endl; + if (err) { + (*err) = ss.str(); + } + return false; + } + + std::vector data; + std::string fileerr; + bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data); + if (!fileread) { + ss << "Failed to read file: " << filename << ": " << fileerr << std::endl; + if (err) { + (*err) = ss.str(); + } + return false; + } + + size_t sz = data.size(); + if (sz == 0) { + if (err) { + (*err) = "Empty file."; + } + return false; + } + + std::string basedir = GetBaseDir(filename); + + bool ret = LoadASCIIFromString( + model, err, warn, reinterpret_cast(&data.at(0)), + static_cast(data.size()), basedir, check_sections); + + return ret; +} + +bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, + std::string *warn, + const unsigned char *bytes, + unsigned int size, + const std::string &base_dir, + unsigned int check_sections) { + if (size < 20) { + if (err) { + (*err) = "Too short data size for glTF Binary."; + } + return false; + } + + if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' && + bytes[3] == 'F') { + // ok + } else { + if (err) { + (*err) = "Invalid magic."; + } + return false; + } + + unsigned int version; // 4 bytes + unsigned int length; // 4 bytes + unsigned int model_length; // 4 bytes + unsigned int model_format; // 4 bytes; + + // @todo { Endian swap for big endian machine. } + memcpy(&version, bytes + 4, 4); + swap4(&version); + memcpy(&length, bytes + 8, 4); + swap4(&length); + memcpy(&model_length, bytes + 12, 4); + swap4(&model_length); + memcpy(&model_format, bytes + 16, 4); + swap4(&model_format); + + // In case the Bin buffer is not present, the size is exactly 20 + size of + // JSON contents, + // so use "greater than" operator. + if ((20 + model_length > size) || (model_length < 1) || (length > size) || + (20 + model_length > length) || + (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format. + if (err) { + (*err) = "Invalid glTF binary."; + } + return false; + } + + // Extract JSON string. + std::string jsonString(reinterpret_cast(&bytes[20]), + model_length); + + is_binary_ = true; + bin_data_ = bytes + 20 + model_length + + 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format) + bin_size_ = + length - (20 + model_length); // extract header + JSON scene data. + + bool ret = LoadFromString(model, err, warn, + reinterpret_cast(&bytes[20]), + model_length, base_dir, check_sections); + if (!ret) { + return ret; + } + + return true; +} + +bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err, + std::string *warn, + const std::string &filename, + unsigned int check_sections) { + std::stringstream ss; + + if (fs.ReadWholeFile == nullptr) { + // Programmer error, assert() ? + ss << "Failed to read file: " << filename + << ": one or more FS callback not set" << std::endl; + if (err) { + (*err) = ss.str(); + } + return false; + } + + std::vector data; + std::string fileerr; + bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data); + if (!fileread) { + ss << "Failed to read file: " << filename << ": " << fileerr << std::endl; + if (err) { + (*err) = ss.str(); + } + return false; + } + + std::string basedir = GetBaseDir(filename); + + bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0), + static_cast(data.size()), + basedir, check_sections); + + return ret; +} + +/////////////////////// +// GLTF Serialization +/////////////////////// +namespace { +json JsonFromString(const char *s) { +#ifdef TINYGLTF_USE_RAPIDJSON + return json(s, GetAllocator()); +#else + return json(s); +#endif +} + +void JsonAssign(json &dest, const json &src) { +#ifdef TINYGLTF_USE_RAPIDJSON + dest.CopyFrom(src, GetAllocator()); +#else + dest = src; +#endif +} + +void JsonAddMember(json &o, const char *key, json &&value) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (!o.IsObject()) { + o.SetObject(); + } + o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator()); +#else + o[key] = std::move(value); +#endif +} + +void JsonPushBack(json &o, json &&value) { +#ifdef TINYGLTF_USE_RAPIDJSON + o.PushBack(std::move(value), GetAllocator()); +#else + o.push_back(std::move(value)); +#endif +} + +bool JsonIsNull(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.IsNull(); +#else + return o.is_null(); +#endif +} + +void JsonSetObject(json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + o.SetObject(); +#else + o = o.object({}); +#endif +} + +void JsonReserveArray(json &o, size_t s) { +#ifdef TINYGLTF_USE_RAPIDJSON + o.SetArray(); + o.Reserve(static_cast(s), GetAllocator()); +#endif + (void)(o); + (void)(s); +} +} // namespace + +// typedef std::pair json_object_pair; + +template +static void SerializeNumberProperty(const std::string &key, T number, + json &obj) { + // obj.insert( + // json_object_pair(key, json(static_cast(number)))); + // obj[key] = static_cast(number); + JsonAddMember(obj, key.c_str(), json(number)); +} + +#ifdef TINYGLTF_USE_RAPIDJSON +template <> +void SerializeNumberProperty(const std::string &key, size_t number, json &obj) { + JsonAddMember(obj, key.c_str(), json(static_cast(number))); +} +#endif + +template +static void SerializeNumberArrayProperty(const std::string &key, + const std::vector &value, + json &obj) { + if (value.empty()) return; + + json ary; + JsonReserveArray(ary, value.size()); + for (const auto &s : value) { + JsonPushBack(ary, json(s)); + } + JsonAddMember(obj, key.c_str(), std::move(ary)); +} + +static void SerializeStringProperty(const std::string &key, + const std::string &value, json &obj) { + JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str())); +} + +static void SerializeStringArrayProperty(const std::string &key, + const std::vector &value, + json &obj) { + json ary; + JsonReserveArray(ary, value.size()); + for (auto &s : value) { + JsonPushBack(ary, JsonFromString(s.c_str())); + } + JsonAddMember(obj, key.c_str(), std::move(ary)); +} + +static bool ValueToJson(const Value &value, json *ret) { + json obj; +#ifdef TINYGLTF_USE_RAPIDJSON + switch (value.Type()) { + case REAL_TYPE: + obj.SetDouble(value.Get()); + break; + case INT_TYPE: + obj.SetInt(value.Get()); + break; + case BOOL_TYPE: + obj.SetBool(value.Get()); + break; + case STRING_TYPE: + obj.SetString(value.Get().c_str(), GetAllocator()); + break; + case ARRAY_TYPE: { + obj.SetArray(); + obj.Reserve(static_cast(value.ArrayLen()), + GetAllocator()); + for (unsigned int i = 0; i < value.ArrayLen(); ++i) { + Value elementValue = value.Get(int(i)); + json elementJson; + if (ValueToJson(value.Get(int(i)), &elementJson)) + obj.PushBack(std::move(elementJson), GetAllocator()); + } + break; + } + case BINARY_TYPE: + // TODO + // obj = json(value.Get>()); + return false; + break; + case OBJECT_TYPE: { + obj.SetObject(); + Value::Object objMap = value.Get(); + for (auto &it : objMap) { + json elementJson; + if (ValueToJson(it.second, &elementJson)) { + obj.AddMember(json(it.first.c_str(), GetAllocator()), + std::move(elementJson), GetAllocator()); + } + } + break; + } + case NULL_TYPE: + default: + return false; + } +#else + switch (value.Type()) { + case REAL_TYPE: + obj = json(value.Get()); + break; + case INT_TYPE: + obj = json(value.Get()); + break; + case BOOL_TYPE: + obj = json(value.Get()); + break; + case STRING_TYPE: + obj = json(value.Get()); + break; + case ARRAY_TYPE: { + for (unsigned int i = 0; i < value.ArrayLen(); ++i) { + Value elementValue = value.Get(int(i)); + json elementJson; + if (ValueToJson(value.Get(int(i)), &elementJson)) + obj.push_back(elementJson); + } + break; + } + case BINARY_TYPE: + // TODO + // obj = json(value.Get>()); + return false; + break; + case OBJECT_TYPE: { + Value::Object objMap = value.Get(); + for (auto &it : objMap) { + json elementJson; + if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson; + } + break; + } + case NULL_TYPE: + default: + return false; + } +#endif + if (ret) *ret = std::move(obj); + return true; +} + +static void SerializeValue(const std::string &key, const Value &value, + json &obj) { + json ret; + if (ValueToJson(value, &ret)) { + JsonAddMember(obj, key.c_str(), std::move(ret)); + } +} + +static void SerializeGltfBufferData(const std::vector &data, + json &o) { + std::string header = "data:application/octet-stream;base64,"; + if (data.size() > 0) { + std::string encodedData = + base64_encode(&data[0], static_cast(data.size())); + SerializeStringProperty("uri", header + encodedData, o); + } else { + // Issue #229 + // size 0 is allowd. Just emit mime header. + SerializeStringProperty("uri", header, o); + } +} + +static bool SerializeGltfBufferData(const std::vector &data, + const std::string &binFilename) { +#ifdef _WIN32 +#if defined(__GLIBCXX__) // mingw + int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); + std::ostream output(&wfile_buf); + if (!wfile_buf.is_open()) return false; +#elif defined(_MSC_VER) + std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary); + if (!output.is_open()) return false; +#else + std::ofstream output(binFilename.c_str(), std::ofstream::binary); + if (!output.is_open()) return false; +#endif +#else + std::ofstream output(binFilename.c_str(), std::ofstream::binary); + if (!output.is_open()) return false; +#endif + if (data.size() > 0) { + output.write(reinterpret_cast(&data[0]), + std::streamsize(data.size())); + } else { + // Issue #229 + // size 0 will be still valid buffer data. + // write empty file. + } + return true; +} + +#if 0 // FIXME(syoyo): not used. will be removed in the future release. +static void SerializeParameterMap(ParameterMap ¶m, json &o) { + for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end(); + ++paramIt) { + if (paramIt->second.number_array.size()) { + SerializeNumberArrayProperty(paramIt->first, + paramIt->second.number_array, o); + } else if (paramIt->second.json_double_value.size()) { + json json_double_value; + for (std::map::iterator it = + paramIt->second.json_double_value.begin(); + it != paramIt->second.json_double_value.end(); ++it) { + if (it->first == "index") { + json_double_value[it->first] = paramIt->second.TextureIndex(); + } else { + json_double_value[it->first] = it->second; + } + } + + o[paramIt->first] = json_double_value; + } else if (!paramIt->second.string_value.empty()) { + SerializeStringProperty(paramIt->first, paramIt->second.string_value, o); + } else if (paramIt->second.has_number_value) { + o[paramIt->first] = paramIt->second.number_value; + } else { + o[paramIt->first] = paramIt->second.bool_value; + } + } +} +#endif + +static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) { + if (!extensions.size()) return; + + json extMap; + for (ExtensionMap::const_iterator extIt = extensions.begin(); + extIt != extensions.end(); ++extIt) { + // Allow an empty object for extension(#97) + json ret; + bool isNull = true; + if (ValueToJson(extIt->second, &ret)) { + isNull = JsonIsNull(ret); + JsonAddMember(extMap, extIt->first.c_str(), std::move(ret)); + } + if (isNull) { + if (!(extIt->first.empty())) { // name should not be empty, but for sure + // create empty object so that an extension name is still included in + // json. + json empty; + JsonSetObject(empty); + JsonAddMember(extMap, extIt->first.c_str(), std::move(empty)); + } + } + } + JsonAddMember(o, "extensions", std::move(extMap)); +} + +static void SerializeGltfAccessor(Accessor &accessor, json &o) { + if (accessor.bufferView >= 0) + SerializeNumberProperty("bufferView", accessor.bufferView, o); + + if (accessor.byteOffset != 0) + SerializeNumberProperty("byteOffset", int(accessor.byteOffset), o); + + SerializeNumberProperty("componentType", accessor.componentType, o); + SerializeNumberProperty("count", accessor.count, o); + + if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) || + (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) { + SerializeNumberArrayProperty("min", accessor.minValues, o); + SerializeNumberArrayProperty("max", accessor.maxValues, o); + } else { + // Issue #301. Serialize as integer. + // Assume int value is within [-2**31-1, 2**31-1] + { + std::vector values; + std::transform(accessor.minValues.begin(), accessor.minValues.end(), + std::back_inserter(values), + [](double v) { return static_cast(v); }); + + SerializeNumberArrayProperty("min", values, o); + } + + { + std::vector values; + std::transform(accessor.maxValues.begin(), accessor.maxValues.end(), + std::back_inserter(values), + [](double v) { return static_cast(v); }); + + SerializeNumberArrayProperty("max", values, o); + } + } + + if (accessor.normalized) + SerializeValue("normalized", Value(accessor.normalized), o); + std::string type; + switch (accessor.type) { + case TINYGLTF_TYPE_SCALAR: + type = "SCALAR"; + break; + case TINYGLTF_TYPE_VEC2: + type = "VEC2"; + break; + case TINYGLTF_TYPE_VEC3: + type = "VEC3"; + break; + case TINYGLTF_TYPE_VEC4: + type = "VEC4"; + break; + case TINYGLTF_TYPE_MAT2: + type = "MAT2"; + break; + case TINYGLTF_TYPE_MAT3: + type = "MAT3"; + break; + case TINYGLTF_TYPE_MAT4: + type = "MAT4"; + break; + } + + SerializeStringProperty("type", type, o); + if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o); + + if (accessor.extras.Type() != NULL_TYPE) { + SerializeValue("extras", accessor.extras, o); + } +} + +static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { + SerializeNumberProperty("sampler", channel.sampler, o); + { + json target; + SerializeNumberProperty("node", channel.target_node, target); + SerializeStringProperty("path", channel.target_path, target); + + SerializeExtensionMap(channel.target_extensions, target); + + JsonAddMember(o, "target", std::move(target)); + } + + if (channel.extras.Type() != NULL_TYPE) { + SerializeValue("extras", channel.extras, o); + } + + SerializeExtensionMap(channel.extensions, o); +} + +static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) { + SerializeNumberProperty("input", sampler.input, o); + SerializeNumberProperty("output", sampler.output, o); + SerializeStringProperty("interpolation", sampler.interpolation, o); + + if (sampler.extras.Type() != NULL_TYPE) { + SerializeValue("extras", sampler.extras, o); + } +} + +static void SerializeGltfAnimation(Animation &animation, json &o) { + if (!animation.name.empty()) + SerializeStringProperty("name", animation.name, o); + + { + json channels; + JsonReserveArray(channels, animation.channels.size()); + for (unsigned int i = 0; i < animation.channels.size(); ++i) { + json channel; + AnimationChannel gltfChannel = animation.channels[i]; + SerializeGltfAnimationChannel(gltfChannel, channel); + JsonPushBack(channels, std::move(channel)); + } + + JsonAddMember(o, "channels", std::move(channels)); + } + + { + json samplers; + JsonReserveArray(samplers, animation.samplers.size()); + for (unsigned int i = 0; i < animation.samplers.size(); ++i) { + json sampler; + AnimationSampler gltfSampler = animation.samplers[i]; + SerializeGltfAnimationSampler(gltfSampler, sampler); + JsonPushBack(samplers, std::move(sampler)); + } + JsonAddMember(o, "samplers", std::move(samplers)); + } + + if (animation.extras.Type() != NULL_TYPE) { + SerializeValue("extras", animation.extras, o); + } + + SerializeExtensionMap(animation.extensions, o); +} + +static void SerializeGltfAsset(Asset &asset, json &o) { + if (!asset.generator.empty()) { + SerializeStringProperty("generator", asset.generator, o); + } + + if (!asset.copyright.empty()) { + SerializeStringProperty("copyright", asset.copyright, o); + } + + if (asset.version.empty()) { + // Just in case + // `version` must be defined + asset.version = "2.0"; + } + + // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0? + SerializeStringProperty("version", asset.version, o); + + if (asset.extras.Keys().size()) { + SerializeValue("extras", asset.extras, o); + } + + SerializeExtensionMap(asset.extensions, o); +} + +static void SerializeGltfBufferBin(Buffer &buffer, json &o, + std::vector &binBuffer) { + SerializeNumberProperty("byteLength", buffer.data.size(), o); + binBuffer = buffer.data; + + if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); + + if (buffer.extras.Type() != NULL_TYPE) { + SerializeValue("extras", buffer.extras, o); + } +} + +static void SerializeGltfBuffer(Buffer &buffer, json &o) { + SerializeNumberProperty("byteLength", buffer.data.size(), o); + SerializeGltfBufferData(buffer.data, o); + + if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); + + if (buffer.extras.Type() != NULL_TYPE) { + SerializeValue("extras", buffer.extras, o); + } +} + +static bool SerializeGltfBuffer(Buffer &buffer, json &o, + const std::string &binFilename, + const std::string &binBaseFilename) { + if (!SerializeGltfBufferData(buffer.data, binFilename)) return false; + SerializeNumberProperty("byteLength", buffer.data.size(), o); + SerializeStringProperty("uri", binBaseFilename, o); + + if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); + + if (buffer.extras.Type() != NULL_TYPE) { + SerializeValue("extras", buffer.extras, o); + } + return true; +} + +static void SerializeGltfBufferView(BufferView &bufferView, json &o) { + SerializeNumberProperty("buffer", bufferView.buffer, o); + SerializeNumberProperty("byteLength", bufferView.byteLength, o); + + // byteStride is optional, minimum allowed is 4 + if (bufferView.byteStride >= 4) { + SerializeNumberProperty("byteStride", bufferView.byteStride, o); + } + // byteOffset is optional, default is 0 + if (bufferView.byteOffset > 0) { + SerializeNumberProperty("byteOffset", bufferView.byteOffset, o); + } + // Target is optional, check if it contains a valid value + if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER || + bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) { + SerializeNumberProperty("target", bufferView.target, o); + } + if (bufferView.name.size()) { + SerializeStringProperty("name", bufferView.name, o); + } + + if (bufferView.extras.Type() != NULL_TYPE) { + SerializeValue("extras", bufferView.extras, o); + } +} + +static void SerializeGltfImage(Image &image, json &o) { + // if uri empty, the mimeType and bufferview should be set + if (image.uri.empty()) { + SerializeStringProperty("mimeType", image.mimeType, o); + SerializeNumberProperty("bufferView", image.bufferView, o); + } else { + // TODO(syoyo): dlib::urilencode? + SerializeStringProperty("uri", image.uri, o); + } + + if (image.name.size()) { + SerializeStringProperty("name", image.name, o); + } + + if (image.extras.Type() != NULL_TYPE) { + SerializeValue("extras", image.extras, o); + } + + SerializeExtensionMap(image.extensions, o); +} + +static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) { + SerializeNumberProperty("index", texinfo.index, o); + + if (texinfo.texCoord != 0) { + SerializeNumberProperty("texCoord", texinfo.texCoord, o); + } + + if (texinfo.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texinfo.extras, o); + } + + SerializeExtensionMap(texinfo.extensions, o); +} + +static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo, + json &o) { + SerializeNumberProperty("index", texinfo.index, o); + + if (texinfo.texCoord != 0) { + SerializeNumberProperty("texCoord", texinfo.texCoord, o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) { + SerializeNumberProperty("scale", texinfo.scale, o); + } + + if (texinfo.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texinfo.extras, o); + } + + SerializeExtensionMap(texinfo.extensions, o); +} + +static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo, + json &o) { + SerializeNumberProperty("index", texinfo.index, o); + + if (texinfo.texCoord != 0) { + SerializeNumberProperty("texCoord", texinfo.texCoord, o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) { + SerializeNumberProperty("strength", texinfo.strength, o); + } + + if (texinfo.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texinfo.extras, o); + } + + SerializeExtensionMap(texinfo.extensions, o); +} + +static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr, + json &o) { + std::vector default_baseColorFactor = {1.0, 1.0, 1.0, 1.0}; + if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) { + SerializeNumberArrayProperty("baseColorFactor", pbr.baseColorFactor, + o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) { + SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) { + SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o); + } + + if (pbr.baseColorTexture.index > -1) { + json texinfo; + SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo); + JsonAddMember(o, "baseColorTexture", std::move(texinfo)); + } + + if (pbr.metallicRoughnessTexture.index > -1) { + json texinfo; + SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo); + JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo)); + } + + SerializeExtensionMap(pbr.extensions, o); + + if (pbr.extras.Type() != NULL_TYPE) { + SerializeValue("extras", pbr.extras, o); + } +} + +static void SerializeGltfMaterial(Material &material, json &o) { + if (material.name.size()) { + SerializeStringProperty("name", material.name, o); + } + + // QUESTION(syoyo): Write material parameters regardless of its default value? + + if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) { + SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o); + } + + if (material.alphaMode.compare("OPAQUE") != 0) { + SerializeStringProperty("alphaMode", material.alphaMode, o); + } + + if (material.doubleSided != false) + JsonAddMember(o, "doubleSided", json(material.doubleSided)); + + if (material.normalTexture.index > -1) { + json texinfo; + SerializeGltfNormalTextureInfo(material.normalTexture, texinfo); + JsonAddMember(o, "normalTexture", std::move(texinfo)); + } + + if (material.occlusionTexture.index > -1) { + json texinfo; + SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo); + JsonAddMember(o, "occlusionTexture", std::move(texinfo)); + } + + if (material.emissiveTexture.index > -1) { + json texinfo; + SerializeGltfTextureInfo(material.emissiveTexture, texinfo); + JsonAddMember(o, "emissiveTexture", std::move(texinfo)); + } + + std::vector default_emissiveFactor = {0.0, 0.0, 0.0}; + if (!Equals(material.emissiveFactor, default_emissiveFactor)) { + SerializeNumberArrayProperty("emissiveFactor", + material.emissiveFactor, o); + } + + { + json pbrMetallicRoughness; + SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness, + pbrMetallicRoughness); + // Issue 204 + // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all + // default values(json is null). Otherwise it will serialize to + // `pbrMetallicRoughness : null`, which cannot be read by other glTF + // importers(and validators). + // + if (!JsonIsNull(pbrMetallicRoughness)) { + JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); + } + } + +#if 0 // legacy way. just for the record. + if (material.values.size()) { + json pbrMetallicRoughness; + SerializeParameterMap(material.values, pbrMetallicRoughness); + JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); + } + + SerializeParameterMap(material.additionalValues, o); +#else + +#endif + + SerializeExtensionMap(material.extensions, o); + + if (material.extras.Type() != NULL_TYPE) { + SerializeValue("extras", material.extras, o); + } +} + +static void SerializeGltfMesh(Mesh &mesh, json &o) { + json primitives; + JsonReserveArray(primitives, mesh.primitives.size()); + for (unsigned int i = 0; i < mesh.primitives.size(); ++i) { + json primitive; + const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy + { + json attributes; + for (auto attrIt = gltfPrimitive.attributes.begin(); + attrIt != gltfPrimitive.attributes.end(); ++attrIt) { + SerializeNumberProperty(attrIt->first, attrIt->second, attributes); + } + + JsonAddMember(primitive, "attributes", std::move(attributes)); + } + + // Indicies is optional + if (gltfPrimitive.indices > -1) { + SerializeNumberProperty("indices", gltfPrimitive.indices, primitive); + } + // Material is optional + if (gltfPrimitive.material > -1) { + SerializeNumberProperty("material", gltfPrimitive.material, + primitive); + } + SerializeNumberProperty("mode", gltfPrimitive.mode, primitive); + + // Morph targets + if (gltfPrimitive.targets.size()) { + json targets; + JsonReserveArray(targets, gltfPrimitive.targets.size()); + for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) { + json targetAttributes; + std::map targetData = gltfPrimitive.targets[k]; + for (std::map::iterator attrIt = targetData.begin(); + attrIt != targetData.end(); ++attrIt) { + SerializeNumberProperty(attrIt->first, attrIt->second, + targetAttributes); + } + JsonPushBack(targets, std::move(targetAttributes)); + } + JsonAddMember(primitive, "targets", std::move(targets)); + } + + SerializeExtensionMap(gltfPrimitive.extensions, primitive); + + if (gltfPrimitive.extras.Type() != NULL_TYPE) { + SerializeValue("extras", gltfPrimitive.extras, primitive); + } + + JsonPushBack(primitives, std::move(primitive)); + } + + JsonAddMember(o, "primitives", std::move(primitives)); + + if (mesh.weights.size()) { + SerializeNumberArrayProperty("weights", mesh.weights, o); + } + + if (mesh.name.size()) { + SerializeStringProperty("name", mesh.name, o); + } + + SerializeExtensionMap(mesh.extensions, o); + if (mesh.extras.Type() != NULL_TYPE) { + SerializeValue("extras", mesh.extras, o); + } +} + +static void SerializeSpotLight(SpotLight &spot, json &o) { + SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o); + SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o); + SerializeExtensionMap(spot.extensions, o); + if (spot.extras.Type() != NULL_TYPE) { + SerializeValue("extras", spot.extras, o); + } +} + +static void SerializeGltfLight(Light &light, json &o) { + if (!light.name.empty()) SerializeStringProperty("name", light.name, o); + SerializeNumberProperty("intensity", light.intensity, o); + if (light.range > 0.0) { + SerializeNumberProperty("range", light.range, o); + } + SerializeNumberArrayProperty("color", light.color, o); + SerializeStringProperty("type", light.type, o); + if (light.type == "spot") { + json spot; + SerializeSpotLight(light.spot, spot); + JsonAddMember(o, "spot", std::move(spot)); + } + SerializeExtensionMap(light.extensions, o); + if (light.extras.Type() != NULL_TYPE) { + SerializeValue("extras", light.extras, o); + } +} + +static void SerializeGltfNode(Node &node, json &o) { + if (node.translation.size() > 0) { + SerializeNumberArrayProperty("translation", node.translation, o); + } + if (node.rotation.size() > 0) { + SerializeNumberArrayProperty("rotation", node.rotation, o); + } + if (node.scale.size() > 0) { + SerializeNumberArrayProperty("scale", node.scale, o); + } + if (node.matrix.size() > 0) { + SerializeNumberArrayProperty("matrix", node.matrix, o); + } + if (node.mesh != -1) { + SerializeNumberProperty("mesh", node.mesh, o); + } + + if (node.skin != -1) { + SerializeNumberProperty("skin", node.skin, o); + } + + if (node.camera != -1) { + SerializeNumberProperty("camera", node.camera, o); + } + + if (node.weights.size() > 0) { + SerializeNumberArrayProperty("weights", node.weights, o); + } + + if (node.extras.Type() != NULL_TYPE) { + SerializeValue("extras", node.extras, o); + } + + SerializeExtensionMap(node.extensions, o); + if (!node.name.empty()) SerializeStringProperty("name", node.name, o); + SerializeNumberArrayProperty("children", node.children, o); +} + +static void SerializeGltfSampler(Sampler &sampler, json &o) { + if (sampler.magFilter != -1) { + SerializeNumberProperty("magFilter", sampler.magFilter, o); + } + if (sampler.minFilter != -1) { + SerializeNumberProperty("minFilter", sampler.minFilter, o); + } + //SerializeNumberProperty("wrapR", sampler.wrapR, o); + SerializeNumberProperty("wrapS", sampler.wrapS, o); + SerializeNumberProperty("wrapT", sampler.wrapT, o); + + if (sampler.extras.Type() != NULL_TYPE) { + SerializeValue("extras", sampler.extras, o); + } +} + +static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera, + json &o) { + SerializeNumberProperty("zfar", camera.zfar, o); + SerializeNumberProperty("znear", camera.znear, o); + SerializeNumberProperty("xmag", camera.xmag, o); + SerializeNumberProperty("ymag", camera.ymag, o); + + if (camera.extras.Type() != NULL_TYPE) { + SerializeValue("extras", camera.extras, o); + } +} + +static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera, + json &o) { + SerializeNumberProperty("zfar", camera.zfar, o); + SerializeNumberProperty("znear", camera.znear, o); + if (camera.aspectRatio > 0) { + SerializeNumberProperty("aspectRatio", camera.aspectRatio, o); + } + + if (camera.yfov > 0) { + SerializeNumberProperty("yfov", camera.yfov, o); + } + + if (camera.extras.Type() != NULL_TYPE) { + SerializeValue("extras", camera.extras, o); + } +} + +static void SerializeGltfCamera(const Camera &camera, json &o) { + SerializeStringProperty("type", camera.type, o); + if (!camera.name.empty()) { + SerializeStringProperty("name", camera.name, o); + } + + if (camera.type.compare("orthographic") == 0) { + json orthographic; + SerializeGltfOrthographicCamera(camera.orthographic, orthographic); + JsonAddMember(o, "orthographic", std::move(orthographic)); + } else if (camera.type.compare("perspective") == 0) { + json perspective; + SerializeGltfPerspectiveCamera(camera.perspective, perspective); + JsonAddMember(o, "perspective", std::move(perspective)); + } else { + // ??? + } + + if (camera.extras.Type() != NULL_TYPE) { + SerializeValue("extras", camera.extras, o); + } + SerializeExtensionMap(camera.extensions, o); +} + +static void SerializeGltfScene(Scene &scene, json &o) { + SerializeNumberArrayProperty("nodes", scene.nodes, o); + + if (scene.name.size()) { + SerializeStringProperty("name", scene.name, o); + } + if (scene.extras.Type() != NULL_TYPE) { + SerializeValue("extras", scene.extras, o); + } + SerializeExtensionMap(scene.extensions, o); +} + +static void SerializeGltfSkin(Skin &skin, json &o) { + // required + SerializeNumberArrayProperty("joints", skin.joints, o); + + if (skin.inverseBindMatrices >= 0) { + SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o); + } + + if (skin.skeleton >= 0) { + SerializeNumberProperty("skeleton", skin.skeleton, o); + } + + if (skin.name.size()) { + SerializeStringProperty("name", skin.name, o); + } +} + +static void SerializeGltfTexture(Texture &texture, json &o) { + if (texture.sampler > -1) { + SerializeNumberProperty("sampler", texture.sampler, o); + } + if (texture.source > -1) { + SerializeNumberProperty("source", texture.source, o); + } + if (texture.name.size()) { + SerializeStringProperty("name", texture.name, o); + } + if (texture.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texture.extras, o); + } + SerializeExtensionMap(texture.extensions, o); +} + +/// +/// Serialize all properties except buffers and images. +/// +static void SerializeGltfModel(Model *model, json &o) { + // ACCESSORS + if (model->accessors.size()) { + json accessors; + JsonReserveArray(accessors, model->accessors.size()); + for (unsigned int i = 0; i < model->accessors.size(); ++i) { + json accessor; + SerializeGltfAccessor(model->accessors[i], accessor); + JsonPushBack(accessors, std::move(accessor)); + } + JsonAddMember(o, "accessors", std::move(accessors)); + } + + // ANIMATIONS + if (model->animations.size()) { + json animations; + JsonReserveArray(animations, model->animations.size()); + for (unsigned int i = 0; i < model->animations.size(); ++i) { + if (model->animations[i].channels.size()) { + json animation; + SerializeGltfAnimation(model->animations[i], animation); + JsonPushBack(animations, std::move(animation)); + } + } + + JsonAddMember(o, "animations", std::move(animations)); + } + + // ASSET + json asset; + SerializeGltfAsset(model->asset, asset); + JsonAddMember(o, "asset", std::move(asset)); + + // BUFFERVIEWS + if (model->bufferViews.size()) { + json bufferViews; + JsonReserveArray(bufferViews, model->bufferViews.size()); + for (unsigned int i = 0; i < model->bufferViews.size(); ++i) { + json bufferView; + SerializeGltfBufferView(model->bufferViews[i], bufferView); + JsonPushBack(bufferViews, std::move(bufferView)); + } + JsonAddMember(o, "bufferViews", std::move(bufferViews)); + } + + // Extensions required + if (model->extensionsRequired.size()) { + SerializeStringArrayProperty("extensionsRequired", + model->extensionsRequired, o); + } + + // MATERIALS + if (model->materials.size()) { + json materials; + JsonReserveArray(materials, model->materials.size()); + for (unsigned int i = 0; i < model->materials.size(); ++i) { + json material; + SerializeGltfMaterial(model->materials[i], material); + + if (JsonIsNull(material)) { + // Issue 294. + // `material` does not have any required parameters + // so the result may be null(unmodified) when all material parameters + // have default value. + // + // null is not allowed thus we create an empty JSON object. + JsonSetObject(material); + } + JsonPushBack(materials, std::move(material)); + } + JsonAddMember(o, "materials", std::move(materials)); + } + + // MESHES + if (model->meshes.size()) { + json meshes; + JsonReserveArray(meshes, model->meshes.size()); + for (unsigned int i = 0; i < model->meshes.size(); ++i) { + json mesh; + SerializeGltfMesh(model->meshes[i], mesh); + JsonPushBack(meshes, std::move(mesh)); + } + JsonAddMember(o, "meshes", std::move(meshes)); + } + + // NODES + if (model->nodes.size()) { + json nodes; + JsonReserveArray(nodes, model->nodes.size()); + for (unsigned int i = 0; i < model->nodes.size(); ++i) { + json node; + SerializeGltfNode(model->nodes[i], node); + JsonPushBack(nodes, std::move(node)); + } + JsonAddMember(o, "nodes", std::move(nodes)); + } + + // SCENE + if (model->defaultScene > -1) { + SerializeNumberProperty("scene", model->defaultScene, o); + } + + // SCENES + if (model->scenes.size()) { + json scenes; + JsonReserveArray(scenes, model->scenes.size()); + for (unsigned int i = 0; i < model->scenes.size(); ++i) { + json currentScene; + SerializeGltfScene(model->scenes[i], currentScene); + JsonPushBack(scenes, std::move(currentScene)); + } + JsonAddMember(o, "scenes", std::move(scenes)); + } + + // SKINS + if (model->skins.size()) { + json skins; + JsonReserveArray(skins, model->skins.size()); + for (unsigned int i = 0; i < model->skins.size(); ++i) { + json skin; + SerializeGltfSkin(model->skins[i], skin); + JsonPushBack(skins, std::move(skin)); + } + JsonAddMember(o, "skins", std::move(skins)); + } + + // TEXTURES + if (model->textures.size()) { + json textures; + JsonReserveArray(textures, model->textures.size()); + for (unsigned int i = 0; i < model->textures.size(); ++i) { + json texture; + SerializeGltfTexture(model->textures[i], texture); + JsonPushBack(textures, std::move(texture)); + } + JsonAddMember(o, "textures", std::move(textures)); + } + + // SAMPLERS + if (model->samplers.size()) { + json samplers; + JsonReserveArray(samplers, model->samplers.size()); + for (unsigned int i = 0; i < model->samplers.size(); ++i) { + json sampler; + SerializeGltfSampler(model->samplers[i], sampler); + JsonPushBack(samplers, std::move(sampler)); + } + JsonAddMember(o, "samplers", std::move(samplers)); + } + + // CAMERAS + if (model->cameras.size()) { + json cameras; + JsonReserveArray(cameras, model->cameras.size()); + for (unsigned int i = 0; i < model->cameras.size(); ++i) { + json camera; + SerializeGltfCamera(model->cameras[i], camera); + JsonPushBack(cameras, std::move(camera)); + } + JsonAddMember(o, "cameras", std::move(cameras)); + } + + // EXTENSIONS + SerializeExtensionMap(model->extensions, o); + + auto extensionsUsed = model->extensionsUsed; + + // LIGHTS as KHR_lights_punctual + if (model->lights.size()) { + json lights; + JsonReserveArray(lights, model->lights.size()); + for (unsigned int i = 0; i < model->lights.size(); ++i) { + json light; + SerializeGltfLight(model->lights[i], light); + JsonPushBack(lights, std::move(light)); + } + json khr_lights_cmn; + JsonAddMember(khr_lights_cmn, "lights", std::move(lights)); + json ext_j; + + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + JsonAssign(ext_j, GetValue(it)); + } + } + + JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn)); + + JsonAddMember(o, "extensions", std::move(ext_j)); + + // Also add "KHR_lights_punctual" to `extensionsUsed` + { + auto has_khr_lights_punctual = + std::find_if(extensionsUsed.begin(), extensionsUsed.end(), + [](const std::string &s) { + return (s.compare("KHR_lights_punctual") == 0); + }); + + if (has_khr_lights_punctual == extensionsUsed.end()) { + extensionsUsed.push_back("KHR_lights_punctual"); + } + } + } + + // Extensions used + if (extensionsUsed.size()) { + SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o); + } + + // EXTRAS + if (model->extras.Type() != NULL_TYPE) { + SerializeValue("extras", model->extras, o); + } +} + +static bool WriteGltfStream(std::ostream &stream, const std::string &content) { + stream << content << std::endl; + return true; +} + +static bool WriteGltfFile(const std::string &output, + const std::string &content) { +#ifdef _WIN32 +#if defined(_MSC_VER) + std::ofstream gltfFile(UTF8ToWchar(output).c_str()); +#elif defined(__GLIBCXX__) + int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); + std::ostream gltfFile(&wfile_buf); + if (!wfile_buf.is_open()) return false; +#else + std::ofstream gltfFile(output.c_str()); + if (!gltfFile.is_open()) return false; +#endif +#else + std::ofstream gltfFile(output.c_str()); + if (!gltfFile.is_open()) return false; +#endif + return WriteGltfStream(gltfFile, content); +} + +static void WriteBinaryGltfStream(std::ostream &stream, + const std::string &content, + const std::vector &binBuffer) { + const std::string header = "glTF"; + const int version = 2; + + // https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number + auto roundUp = [](uint32_t numToRound, uint32_t multiple) { + if (multiple == 0) return numToRound; + + uint32_t remainder = numToRound % multiple; + if (remainder == 0) return numToRound; + + return numToRound + multiple - remainder; + }; + + const uint32_t padding_size = + roundUp(uint32_t(content.size()), 4) - uint32_t(content.size()); + + // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info. + // Chunk data must be located at 4-byte boundary. + const uint32_t length = + 12 + 8 + roundUp(uint32_t(content.size()), 4) + + (binBuffer.size() ? (8 + roundUp(uint32_t(binBuffer.size()), 4)) : 0); + + stream.write(header.c_str(), std::streamsize(header.size())); + stream.write(reinterpret_cast(&version), sizeof(version)); + stream.write(reinterpret_cast(&length), sizeof(length)); + + // JSON chunk info, then JSON data + const uint32_t model_length = uint32_t(content.size()) + padding_size; + const uint32_t model_format = 0x4E4F534A; + stream.write(reinterpret_cast(&model_length), + sizeof(model_length)); + stream.write(reinterpret_cast(&model_format), + sizeof(model_format)); + stream.write(content.c_str(), std::streamsize(content.size())); + + // Chunk must be multiplies of 4, so pad with spaces + if (padding_size > 0) { + const std::string padding = std::string(size_t(padding_size), ' '); + stream.write(padding.c_str(), std::streamsize(padding.size())); + } + if (binBuffer.size() > 0) { + const uint32_t bin_padding_size = + roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size()); + // BIN chunk info, then BIN data + const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size; + const uint32_t bin_format = 0x004e4942; + stream.write(reinterpret_cast(&bin_length), + sizeof(bin_length)); + stream.write(reinterpret_cast(&bin_format), + sizeof(bin_format)); + stream.write(reinterpret_cast(binBuffer.data()), + std::streamsize(binBuffer.size())); + // Chunksize must be multiplies of 4, so pad with zeroes + if (bin_padding_size > 0) { + const std::vector padding = + std::vector(size_t(bin_padding_size), 0); + stream.write(reinterpret_cast(padding.data()), + std::streamsize(padding.size())); + } + } +} + +static void WriteBinaryGltfFile(const std::string &output, + const std::string &content, + const std::vector &binBuffer) { +#ifdef _WIN32 +#if defined(_MSC_VER) + std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary); +#elif defined(__GLIBCXX__) + int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); + std::ostream gltfFile(&wfile_buf); +#else + std::ofstream gltfFile(output.c_str(), std::ios::binary); +#endif +#else + std::ofstream gltfFile(output.c_str(), std::ios::binary); +#endif + WriteBinaryGltfStream(gltfFile, content, binBuffer); +} + +bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, + bool prettyPrint = true, + bool writeBinary = false) { + JsonDocument output; + + /// Serialize all properties except buffers and images. + SerializeGltfModel(model, output); + + // BUFFERS + std::vector binBuffer; + if (model->buffers.size()) { + json buffers; + JsonReserveArray(buffers, model->buffers.size()); + for (unsigned int i = 0; i < model->buffers.size(); ++i) { + json buffer; + if (writeBinary && i == 0 && model->buffers[i].uri.empty()) { + SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer); + } else { + SerializeGltfBuffer(model->buffers[i], buffer); + } + JsonPushBack(buffers, std::move(buffer)); + } + JsonAddMember(output, "buffers", std::move(buffers)); + } + + // IMAGES + if (model->images.size()) { + json images; + JsonReserveArray(images, model->images.size()); + for (unsigned int i = 0; i < model->images.size(); ++i) { + json image; + + std::string dummystring = ""; + // UpdateImageObject need baseDir but only uses it if embeddedImages is + // enabled, since we won't write separate images when writing to a stream + // we + UpdateImageObject(model->images[i], dummystring, int(i), false, + &this->WriteImageData, this->write_image_user_data_); + SerializeGltfImage(model->images[i], image); + JsonPushBack(images, std::move(image)); + } + JsonAddMember(output, "images", std::move(images)); + } + + if (writeBinary) { + WriteBinaryGltfStream(stream, JsonToString(output), binBuffer); + } else { + WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1)); + } + + return true; +} + +bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, + bool embedImages = false, + bool embedBuffers = false, + bool prettyPrint = true, + bool writeBinary = false) { + JsonDocument output; + std::string defaultBinFilename = GetBaseFilename(filename); + std::string defaultBinFileExt = ".bin"; + std::string::size_type pos = + defaultBinFilename.rfind('.', defaultBinFilename.length()); + + if (pos != std::string::npos) { + defaultBinFilename = defaultBinFilename.substr(0, pos); + } + std::string baseDir = GetBaseDir(filename); + if (baseDir.empty()) { + baseDir = "./"; + } + /// Serialize all properties except buffers and images. + SerializeGltfModel(model, output); + + // BUFFERS + std::vector usedUris; + std::vector binBuffer; + if (model->buffers.size()) { + json buffers; + JsonReserveArray(buffers, model->buffers.size()); + for (unsigned int i = 0; i < model->buffers.size(); ++i) { + json buffer; + if (writeBinary && i == 0 && model->buffers[i].uri.empty()) { + SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer); + } else if (embedBuffers) { + SerializeGltfBuffer(model->buffers[i], buffer); + } else { + std::string binSavePath; + std::string binUri; + if (!model->buffers[i].uri.empty() && + !IsDataURI(model->buffers[i].uri)) { + binUri = model->buffers[i].uri; + } else { + binUri = defaultBinFilename + defaultBinFileExt; + bool inUse = true; + int numUsed = 0; + while (inUse) { + inUse = false; + for (const std::string &usedName : usedUris) { + if (binUri.compare(usedName) != 0) continue; + inUse = true; + binUri = defaultBinFilename + std::to_string(numUsed++) + + defaultBinFileExt; + break; + } + } + } + usedUris.push_back(binUri); + binSavePath = JoinPath(baseDir, binUri); + if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath, + binUri)) { + return false; + } + } + JsonPushBack(buffers, std::move(buffer)); + } + JsonAddMember(output, "buffers", std::move(buffers)); + } + + // IMAGES + if (model->images.size()) { + json images; + JsonReserveArray(images, model->images.size()); + for (unsigned int i = 0; i < model->images.size(); ++i) { + json image; + + UpdateImageObject(model->images[i], baseDir, int(i), embedImages, + &this->WriteImageData, this->write_image_user_data_); + SerializeGltfImage(model->images[i], image); + JsonPushBack(images, std::move(image)); + } + JsonAddMember(output, "images", std::move(images)); + } + + if (writeBinary) { + WriteBinaryGltfFile(filename, JsonToString(output), binBuffer); + } else { + WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1))); + } + + return true; +} + +} // namespace tinygltf + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // TINYGLTF_IMPLEMENTATION From 4c156cba21f7ada1057ca91360bc0a6d48962fb5 Mon Sep 17 00:00:00 2001 From: Jake Date: Wed, 22 Sep 2021 08:22:09 +0200 Subject: [PATCH 0078/1500] EEVEE fix gloss low roughness error Up lower clamp on spec_angle to prevent NaN from being generated on intel GPUs at low roughness. Fixes T88754 Reviewed By: fclem Maniphest Tasks: T88754 Differential Revision: https://developer.blender.org/D12508 --- .../draw/engines/eevee/shaders/ambient_occlusion_lib.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl index d4e3b879426..93641443cac 100644 --- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl @@ -379,7 +379,7 @@ float specular_occlusion( /* Visibility to cone angle (eq. 18). */ float vis_angle = fast_acos(sqrt(1 - visibility)); /* Roughness to cone angle (eq. 26). */ - float spec_angle = max(0.001, fast_acos(cone_cosine(roughness))); + float spec_angle = max(0.00990998744964599609375, fast_acos(cone_cosine(roughness))); /* Angle between cone axes. */ float cone_cone_dist = fast_acos(saturate(dot(visibility_dir, specular_dir))); float cone_nor_dist = fast_acos(saturate(dot(N, specular_dir))); From 0c59386110e75b6a5ede8694556e799a2a8e73af Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 22 Sep 2021 09:06:56 +0200 Subject: [PATCH 0079/1500] Blender Libraries: Add JSON Library. Several areas within blender can benefit a JSON reader/writer library. Areas like the asset browser, XR and grease pencil. After looking at the available options we selected nlohmann's JSON for modern C++ library. It is actively maintained for over 10 years and flexible. This patch only adds the header only implementation of the library so it can be used by different areas. The asset browser project is planning to add a small abstraction layer so it will be easier to switch between several different serialization formats. This is currently in development in D12544. In cases the abstraction layer can be an overhead and undesired to be used. In this case the header file can be directly included. Reviewed By: Severin Maniphest Tasks: T91430 Differential Revision: https://developer.blender.org/D12567 --- extern/json/README.blender | 5 + extern/json/include/json.hpp | 26640 +++++++++++++++++++++++++++++++++ 2 files changed, 26645 insertions(+) create mode 100644 extern/json/README.blender create mode 100644 extern/json/include/json.hpp diff --git a/extern/json/README.blender b/extern/json/README.blender new file mode 100644 index 00000000000..b9d8b02d87e --- /dev/null +++ b/extern/json/README.blender @@ -0,0 +1,5 @@ +Project: JSON +URL: https://github.com/nlohmann/json/ +License: MIT License +Upstream version: 3.10.2 +Local modifications: None diff --git a/extern/json/include/json.hpp b/extern/json/include/json.hpp new file mode 100644 index 00000000000..8959265daea --- /dev/null +++ b/extern/json/include/json.hpp @@ -0,0 +1,26640 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.10.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 10 +#define NLOHMANN_JSON_VERSION_PATCH 2 + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include + + +#include +#include + +// #include + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include + + +#include // exception +#include // runtime_error +#include // to_string +#include // vector + +// #include + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +namespace nlohmann +{ +namespace detail +{ +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +} +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +// #include + + +#include // pair +// #include + + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow to override assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +inline void replace_substring(std::string& s, const std::string& f, + const std::string& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +inline std::string escape(std::string s) +{ + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +static void unescape(std::string& s) +{ + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. + +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors + +@internal +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. +@endinternal + +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + template + static std::string diagnostics(const BasicJsonType& leaf_element) + { +#if JSON_DIAGNOSTICS + std::vector tokens; + for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (¤t->m_parent->m_value.array->operator[](i) == current) + { + tokens.emplace_back(std::to_string(i)); + break; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (&element.second == current) + { + tokens.emplace_back(element.first.c_str()); + break; + } + } + break; + } + + case value_t::null: // LCOV_EXCL_LINE + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }) + ") "; +#else + static_cast(leaf_element); + return ""; +#endif + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/*! +@brief exception indicating a parse error + +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +Exceptions have ids 1xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). +json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed. + +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). + +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + template + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + exception::diagnostics(context) + what_arg; + return parse_error(id_, pos.chars_read_total, w.c_str()); + } + + template + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + exception::diagnostics(context) + what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/*! +@brief exception indicating errors with iterators + +This exception is thrown if iterators passed to a library function do not match +the expected semantics. + +Exceptions have ids 2xx. + +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + template + static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. + +Exceptions have ids 3xx. + +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | +json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | + +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + template + static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating access out of the defined range + +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. + +Exceptions have ids 4xx. + +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | +json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | + +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + template + static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating other library errors + +This exception is thrown in case of errors that cannot be classified with the +other exception types. + +Exceptions have ids 5xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range + +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + template + static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct identity_tag {}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple + +// #include + + +#include // random_access_iterator_tag + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include + +// #include + + +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; +} // namespace detail +} // namespace nlohmann + +// #include +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +class basic_json; + +/*! +@brief JSON Pointer + +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. + +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + +@since version 2.0.0 +*/ +template +class json_pointer; + +/*! +@brief default JSON class + +This type is the default specialization of the @ref basic_json class which +uses the standard template types. + +@since version 1.0.0 +*/ +using json = basic_json<>; + +template +struct ordered_map; + +/*! +@brief ordered JSON class + +This type preserves the insertion order of object keys. + +@since version 3.9.0 +*/ +using ordered_json = basic_json; + +} // namespace nlohmann + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +namespace nlohmann +{ +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using iterator_t = typename T::iterator; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B1 { }; +template +struct conjunction +: std::conditional, B1>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type_impl : std::false_type {}; + +template +struct is_compatible_string_type_impl < + BasicJsonType, CompatibleStringType, + enable_if_t::value >> +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_compatible_string_type + : is_compatible_string_type_impl {}; + +template +struct is_constructible_string_type_impl : std::false_type {}; + +template +struct is_constructible_string_type_impl < + BasicJsonType, ConstructibleStringType, + enable_if_t::value >> +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type + : is_constructible_string_type_impl {}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < is_detected::value&& + is_detected::value&& +// This is needed because json_reverse_iterator has a ::iterator type... +// Therefore it is detected as a CompatibleArrayType. +// The real fix would be to have an Iterable concept. + !is_iterator_traits < + iterator_traits>::value >> +{ + static constexpr bool value = + is_constructible::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_detected::value&& +is_complete_type < +detected_t>::value >> +{ + static constexpr bool value = + // This is needed because json_reverse_iterator has a ::iterator type, + // furthermore, std::back_insert_iterator (and other iterators) have a + // base class `iterator`... Therefore it is detected as a + // ConstructibleArrayType. The real fix would be to have an Iterable + // concept. + !is_iterator_traits>::value && + + (std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, typename ConstructibleArrayType::value_type >::value); +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +// a naive helper to check if a type is an ordered_map (exploits the fact that +// ordered_map inherits capacity() from std::vector) +template +struct is_ordered_map +{ + using one = char; + + struct two + { + char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + }; + + template static one test( decltype(&C::capacity) ) ; + template static two test(...); + + enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) +}; + +// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) +template < typename T, typename U, enable_if_t < !std::is_same::value, int > = 0 > +T conditional_static_cast(U value) +{ + return static_cast(value); +} + +template::value, int> = 0> +T conditional_static_cast(U value) +{ + return value; +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); + } + n = nullptr; +} + +// overloads for basic_json template parameters +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic::value&& + !std::is_same::value, + int > = 0 > +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::binary: + case value_t::discarded: + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + s = *j.template get_ptr(); +} + +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + l.resize(j.size()); + std::transform(j.begin(), j.end(), std::begin(l), + [](const BasicJsonType & elem) + { + return elem.template get(); + }); +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} + +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template::value, + int> = 0> +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template::value, + int> = 0> +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; + + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template < typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t < + is_constructible_array_type::value&& + !is_constructible_object_type::value&& + !is_constructible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + from_json_array_impl(j, arr, priority_tag<3> {}); +} + +template < typename BasicJsonType, typename T, std::size_t... Idx > +std::array from_json_inplace_array_impl(BasicJsonType&& j, + identity_tag> /*unused*/, index_sequence /*unused*/) +{ + return { { std::forward(j).at(Idx).template get()... } }; +} + +template < typename BasicJsonType, typename T, std::size_t N > +auto from_json(BasicJsonType&& j, identity_tag> tag) +-> decltype(from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); + } + + bin = *j.template get_ptr(); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); + } + + ConstructibleObjectType ret; + const auto* inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < + std::is_arithmetic::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::binary: + case value_t::discarded: + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + } +} + +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get()...); +} + +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +{ + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; +} + +template +void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) +{ + p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); +} + +template +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) +{ + return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) +{ + t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +auto from_json(BasicJsonType&& j, TupleRelated&& t) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); +} + +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T&& val) const + noexcept(noexcept(from_json(j, std::forward(val)))) + -> decltype(from_json(j, std::forward(val))) + { + return from_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +{ +constexpr const auto& from_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include + + +#include // copy +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +// #include + + +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element +#include // move + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + // For ADL + using std::to_string; + target = to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str{}; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept + : anchor(std::move(it)) + {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + JSON_ASSERT(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann + +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; + +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +/* + * Note all external_constructor<>::construct functions need to call + * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an + * allocated value (e.g., a string). See bug issue + * https://github.com/nlohmann/json/issues/2865 for more information. + */ + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleStringType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(b); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(std::move(b)); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = arr; + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.set_parents(); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + j.set_parent(j.m_value.array->back()); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.set_parents(); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = obj; + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.set_parents(); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < !std::is_same::value, int > = 0 > + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.set_parents(); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < is_compatible_array_type::value&& + !is_compatible_object_type::value&& + !is_compatible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor::construct(j, bin); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_constructible::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + int > = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + external_constructor::construct(j, arr); +} + +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} + +// for https://github.com/nlohmann/json/pull/1134 +template>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} + +template::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) +{ + to_json_tuple_impl(j, t, make_index_sequence::value> {}); +} + +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `to_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +{ +constexpr const auto& to_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include + +// #include + + +namespace nlohmann +{ + +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @note This function is chosen for default-constructible value types. + + @param[in] j JSON value to read from + @param[in,out] val value to write to + */ + template + static auto from_json(BasicJsonType && j, TargetType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @note This function is chosen for value types which are not default-constructible. + + @param[in] j JSON value to read from + + @return copy of the JSON value, converted to @a ValueType + */ + template + static auto from_json(BasicJsonType && j) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); + } + + /*! + @brief convert any value type to a JSON value + + This function is usually called by the constructors of the @ref basic_json + class. + + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static auto to_json(BasicJsonType& j, TargetType && val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; +} // namespace nlohmann + +// #include + + +#include // uint8_t, uint64_t +#include // tie +#include // move + +namespace nlohmann +{ + +/*! +@brief an internal type for a backed binary type + +This type extends the template parameter @a BinaryType provided to `basic_json` +with a subtype used by BSON and MessagePack. This type exists so that the user +does not have to specify a type themselves with a specific naming scheme in +order to override the binary type. + +@tparam BinaryType container to store bytes (`std::vector` by + default) + +@since version 3.8.0; changed type of subtypes to std::uint64_t in 3.10.0. +*/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + /// the type of the underlying container + using container_type = BinaryType; + /// the type of the subtype + using subtype_type = std::uint64_t; + + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + + /*! + @brief sets the binary subtype + + Sets the binary subtype of the value, also flags a binary JSON value as + having a subtype, which has implications for serialization. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa see @ref subtype() -- return the binary subtype + @sa see @ref clear_subtype() -- clears the binary subtype + @sa see @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void set_subtype(subtype_type subtype_) noexcept + { + m_subtype = subtype_; + m_has_subtype = true; + } + + /*! + @brief return the binary subtype + + Returns the numerical subtype of the value if it has a subtype. If it does + not have a subtype, this function will return subtype_type(-1) as a sentinel + value. + + @return the numerical subtype of the binary value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa see @ref set_subtype() -- sets the binary subtype + @sa see @ref clear_subtype() -- clears the binary subtype + @sa see @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0; fixed return value to properly return + subtype_type(-1) as documented in version 3.10.0 + */ + constexpr subtype_type subtype() const noexcept + { + return m_has_subtype ? m_subtype : subtype_type(-1); + } + + /*! + @brief return whether the value has a subtype + + @return whether the value has a subtype + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa see @ref subtype() -- return the binary subtype + @sa see @ref set_subtype() -- sets the binary subtype + @sa see @ref clear_subtype() -- clears the binary subtype + + @since version 3.8.0 + */ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /*! + @brief clears the binary subtype + + Clears the binary subtype and flags the value as not having a subtype, which + has implications for serialization; for instance MessagePack will prefer the + bin family over the ext family. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa see @ref subtype() -- return the binary subtype + @sa see @ref set_subtype() -- sets the binary subtype + @sa see @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + subtype_type m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + + +#include // uint8_t +#include // size_t +#include // hash + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +// boost::hash_combine +inline std::size_t combine(std::size_t seed, std::size_t h) noexcept +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; +} + +/*! +@brief hash a JSON value + +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. + +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ +template +std::size_t hash(const BasicJsonType& j) +{ + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + + const auto type = static_cast(j.type()); + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::value_t::discarded: + { + return combine(type, 0); + } + + case BasicJsonType::value_t::object: + { + auto seed = combine(type, j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(type, j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } + + case BasicJsonType::value_t::string: + { + const auto h = std::hash {}(j.template get_ref()); + return combine(type, h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_unsigned: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_float: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::binary: + { + auto seed = combine(type, j.get_binary().size()); + const auto h = std::hash {}(j.get_binary().has_subtype()); + seed = combine(seed, h); + seed = combine(seed, static_cast(j.get_binary().subtype())); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash {}(byte)); + } + return seed; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return 0; // LCOV_EXCL_LINE + } +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // generate_n +#include // array +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move +#include // vector + +// #include + +// #include + + +#include // array +#include // size_t +#include // strlen +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +#ifndef JSON_NO_IO + #include // FILE * + #include // istream +#endif // JSON_NO_IO + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; + +//////////////////// +// input adapters // +//////////////////// + +#ifndef JSON_NO_IO +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter +{ + public: + using char_type = char; + + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) noexcept = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = delete; + ~file_input_adapter() = default; + + std::char_traits::int_type get_character() noexcept + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; +}; + + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter +{ + public: + using char_type = char; + + ~input_stream_adapter() + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + if (is != nullptr) + { + is->clear(is->rdstate() & std::ios::eofbit); + } + } + + explicit input_stream_adapter(std::istream& i) + : is(&i), sb(i.rdbuf()) + {} + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&&) = delete; + + input_stream_adapter(input_stream_adapter&& rhs) noexcept + : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, eg. 0xFFFFFFFF. + std::char_traits::int_type get_character() + { + auto res = sb->sbumpc(); + // set eof manually, as we don't use the istream interface. + if (JSON_HEDLEY_UNLIKELY(res == std::char_traits::eof())) + { + is->clear(is->rdstate() | std::ios::eofbit); + } + return res; + } + + private: + /// the associated input stream + std::istream* is = nullptr; + std::streambuf* sb = nullptr; +}; +#endif // JSON_NO_IO + +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template +class iterator_input_adapter +{ + public: + using char_type = typename std::iterator_traits::value_type; + + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) + {} + + typename std::char_traits::int_type get_character() + { + if (JSON_HEDLEY_LIKELY(current != end)) + { + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + + return std::char_traits::eof(); + } + + private: + IteratorType current; + IteratorType end; + + template + friend struct wide_string_input_helper; + + bool empty() const + { + return current == end; + } +}; + + +template +struct wide_string_input_helper; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } +}; + +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc || wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (JSON_HEDLEY_UNLIKELY(!input.empty())) + { + const auto wc2 = static_cast(input.get_character()); + const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +// Wraps another input apdater to convert wide character types into individual bytes. +template +class wide_string_input_adapter +{ + public: + using char_type = char; + + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} + + typename std::char_traits::int_type get_character() noexcept + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); + + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); + } + + // use buffer + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + BaseInputAdapter base_adapter; + + template + void fill_buffer() + { + wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } + + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; + + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + + +template +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } +}; + +template +struct is_iterator_of_multibyte +{ + using value_type = typename std::iterator_traits::value_type; + enum + { + value = sizeof(value_type) > 1 + }; +}; + +template +struct iterator_input_adapter_factory::value>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } +}; + +// General purpose iterator-based input +template +typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); +} + +// Convenience shorthand from container to iterator +// Enables ADL on begin(container) and end(container) +// Encloses the using declarations in namespace for not to leak them to outside scope + +namespace container_input_adapter_factory_impl +{ + +using std::begin; +using std::end; + +template +struct container_input_adapter_factory {}; + +template +struct container_input_adapter_factory< ContainerType, + void_t()), end(std::declval()))>> + { + using adapter_type = decltype(input_adapter(begin(std::declval()), end(std::declval()))); + + static adapter_type create(const ContainerType& container) +{ + return input_adapter(begin(container), end(container)); +} + }; + +} // namespace container_input_adapter_factory_impl + +template +typename container_input_adapter_factory_impl::container_input_adapter_factory::adapter_type input_adapter(const ContainerType& container) +{ + return container_input_adapter_factory_impl::container_input_adapter_factory::create(container); +} + +#ifndef JSON_NO_IO +// Special cases with fast paths +inline file_input_adapter input_adapter(std::FILE* file) +{ + return file_input_adapter(file); +} + +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} + +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} +#endif // JSON_NO_IO + +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + !std::is_array::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) +{ + auto length = std::strlen(reinterpret_cast(b)); + const auto* ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); +} + +template +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + return input_adapter(array, array + N); +} + +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitely casted +// to the correct adapter. +class span_input_adapter +{ + public: + template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} + + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} + + contiguous_bytes_input_adapter&& get() + { + return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) + } + + private: + contiguous_bytes_input_adapter ia; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +#include // string +#include // move +#include // vector + +// #include + +// #include + + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief an floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief a binary string was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary. + */ + virtual bool binary(binary_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + json_sax() = default; + json_sax(const json_sax&) = default; + json_sax(json_sax&&) noexcept = default; + json_sax& operator=(const json_sax&) = default; + json_sax& operator=(json_sax&&) noexcept = default; + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @param[in,out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } + + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep && ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back()) + { + if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + else + { + ref_stack.back()->set_parents(); + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (keep) + { + ref_stack.back()->set_parents(); + } + else + { + // discard array + *ref_stack.back() = discarded; + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + JSON_ASSERT(!keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (!keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (!keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (!ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + JSON_ASSERT(ref_stack.back()->is_object()); + // check if we should store an element for the current key + JSON_ASSERT(!key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (!store_element) + { + return {false, nullptr}; + } + + JSON_ASSERT(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool binary(binary_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann + +// #include + + +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +template +class lexer_base +{ + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } +}; +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer : public lexer_base +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + using token_type = typename lexer_base::token_type; + + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~lexer() = default; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept + { + const auto* loc = localeconv(); + JSON_ASSERT(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + JSON_ASSERT(current == 'u'); + int codepoint = 0; + + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' && current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' && current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' && current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } + } + + JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 8259. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + JSON_ASSERT(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(static_cast(codepoint)); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits::eof(): + case '\0': + return true; + + default: + break; + } + } + } + + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case std::char_traits::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } + + case '*': + { + switch (get()) + { + case '/': + return true; + + default: + { + unget(); + continue; + } + } + } + + default: + continue; + } + } + } + + // unexpected character after reading '/' + default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 8259. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 8259. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char_type* literal_text, const std::size_t length, + token_type return_type) + { + JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + char_int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; + + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia.get_character(); + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; + } + + /*! + @brief unget current character (read it again on next get) + + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + JSON_ASSERT(!token_string.empty()); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(char_int_type c) + { + token_buffer.push_back(static_cast(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if (static_cast(c) <= '\x1F') + { + // escape control characters + std::array cs{{}}; + (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + result += cs.data(); + } + else + { + // add character as is + result.push_back(static_cast(c)); + } + } + + return result; + } + + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB && get() == 0xBF; + } + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + } + + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 && !skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } + + // read next character and ignore whitespace + skip_whitespace(); + + // ignore comments + while (ignore_comments && current == '/') + { + if (!scan_comment()) + { + return token_type::parse_error; + } + + // skip following whitespace + skip_whitespace(); + } + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + { + std::array true_literal = {{char_type('t'), char_type('r'), char_type('u'), char_type('e')}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } + case 'f': + { + std::array false_literal = {{char_type('f'), char_type('a'), char_type('l'), char_type('s'), char_type('e')}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); + } + case 'n': + { + std::array null_literal = {{char_type('n'), char_type('u'), char_type('l'), char_type('l')}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + InputAdapterType ia; + + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// whether the next get() call should just return current + bool next_unget = false; + + /// the start position of the current token + position_t position {}; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char_int_type decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // declval +#include // string + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +using null_function_t = decltype(std::declval().null()); + +template +using boolean_function_t = + decltype(std::declval().boolean(std::declval())); + +template +using number_integer_function_t = + decltype(std::declval().number_integer(std::declval())); + +template +using number_unsigned_function_t = + decltype(std::declval().number_unsigned(std::declval())); + +template +using number_float_function_t = decltype(std::declval().number_float( + std::declval(), std::declval())); + +template +using string_function_t = + decltype(std::declval().string(std::declval())); + +template +using binary_function_t = + decltype(std::declval().binary(std::declval())); + +template +using start_object_function_t = + decltype(std::declval().start_object(std::declval())); + +template +using key_function_t = + decltype(std::declval().key(std::declval())); + +template +using end_object_function_t = decltype(std::declval().end_object()); + +template +using start_array_function_t = + decltype(std::declval().start_array(std::declval())); + +template +using end_array_function_t = decltype(std::declval().end_array()); + +template +using parse_error_function_t = decltype(std::declval().parse_error( + std::declval(), std::declval(), + std::declval())); + +template +struct is_sax +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static constexpr bool value = + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value; +}; + +template +struct is_sax_static_asserts +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static_assert(is_detected_exact::value, + "Missing/invalid function: bool null()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_integer(number_integer_t)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool string(string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool binary(binary_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_object(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool key(string_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_object()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_array(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_array()"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool parse_error(std::size_t, const " + "std::string&, const exception&)"); +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/// how to treat CBOR tags +enum class cbor_tag_handler_t +{ + error, ///< throw a parse_error exception in case of a tag + ignore, ///< ignore tags + store ///< store tags as binary type +}; + +/*! +@brief determine system byte order + +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianess(int num = 1) noexcept +{ + return *reinterpret_cast(&num) == 1; +} + + +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter)) + { + (void)detail::is_sax_static_asserts {}; + } + + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~binary_reader() = default; + + /*! + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + @param[in] tag_handler how to treat CBOR tags + + @return whether parsing was successful + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + sax = sax_; + bool result = false; + + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; + + case input_format_t::cbor: + result = parse_cbor_internal(true, tag_handler); + break; + + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; + + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + // strict mode: next byte must be EOF + if (result && strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType())); + } + } + + return result; + } + + private: + ////////// + // BSON // + ////////// + + /*! + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser + */ + bool parse_bson_internal() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); + } + + /*! + @brief Parses a C-style string from the BSON input. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType())); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); + } + + /*! + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in,out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed + */ + template + bool get_bson_binary(const NumberType len, binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType())); + } + + // All BSON binary values have a subtype + std::uint8_t subtype{}; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); + + return get_binary(input_format_t::bson, len, result); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const char_int_type element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number{}; + return get_number(input_format_t::bson, number) && sax->number_float(static_cast(number), ""); + } + + case 0x02: // string + { + std::int32_t len{}; + string_t value; + return get_number(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x05: // binary + { + std::int32_t len{}; + binary_t value; + return get_number(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType())); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + + while (auto element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) + { + return false; + } + + if (!is_array && !sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true) or whether the last read character should + be considered instead (false) + @param[in] tag_handler how CBOR tags should be treated + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char, + const cbor_tag_handler_t tag_handler) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) + - static_cast(number)); + } + + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) && sax->binary(b); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) && sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast(len), tag_handler); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(std::size_t(-1), tag_handler); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast(len), tag_handler); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(std::size_t(-1), tag_handler); + + case 0xC6: // tagged item + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD8: // tagged item (1 bytes follow) + case 0xD9: // tagged item (2 bytes follow) + case 0xDA: // tagged item (4 bytes follow) + case 0xDB: // tagged item (8 bytes follow) + { + switch (tag_handler) + { + case cbor_tag_handler_t::error: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + + case cbor_tag_handler_t::ignore: + { + // ignore binary subtype + switch (current) + { + case 0xD8: + { + std::uint8_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xD9: + { + std::uint16_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDA: + { + std::uint32_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDB: + { + std::uint64_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + default: + break; + } + return parse_cbor_internal(true, tag_handler); + } + + case cbor_tag_handler_t::store: + { + binary_t b; + // use binary subtype and store in binary container + switch (current) + { + case 0xD8: + { + std::uint8_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xD9: + { + std::uint16_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDA: + { + std::uint32_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDB: + { + std::uint64_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + default: + return parse_cbor_internal(true, tag_handler); + } + get(); + return get_cbor_binary(b) && sax->binary(b); + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (!get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + { + return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x58: // Binary data (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x59: // Binary data (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5A: // Binary data (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (!get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType())); + } + } + } + + /*! + @param[in] len the length of the array or std::size_t(-1) for an + array of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or std::size_t(-1) for an + object of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + if (len != 0) + { + string_t key; + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) && sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) && sax->binary(b); + } + + case 0xCA: // float 32 + { + float number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a MessagePack byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC5: // bin 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC6: // bin 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC7: // ext 8 + { + std::uint8_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC8: // ext 16 + { + std::uint16_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC9: // ext 32 + { + std::uint32_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xD4: // fixext 1 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 1, result) && + assign_and_return_true(subtype); + } + + case 0xD5: // fixext 2 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 2, result) && + assign_and_return_true(subtype); + } + + case 0xD6: // fixext 4 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 4, result) && + assign_and_return_true(subtype); + } + + case 0xD7: // fixext 8 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 8, result) && + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 16, result) && + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'i': + { + std::int8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'I': + { + std::int16_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'l': + { + std::int32_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'L': + { + std::int64_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + default: + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) + { + case 'U': + { + std::uint8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char + return true; + } + + case 'I': + { + std::int16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType())); + } + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType())); + } + + return get_ubjson_size_value(result.first); + } + + if (current == '#') + { + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const char_int_type prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'U': + { + std::uint8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'I': + { + std::int16_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'l': + { + std::int32_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'L': + { + std::int64_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'd': + { + float number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'H': + { + return get_ubjson_high_precision_number(); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType())); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) && sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + // Note, no reader for UBJSON binary types is implemented because they do + // not exist + + bool get_ubjson_high_precision_number() + { + // get size of following number string + std::size_t size{}; + auto res = get_ubjson_size_value(size); + if (JSON_HEDLEY_UNLIKELY(!res)) + { + return res; + } + + // get number string + std::vector number_vector; + for (std::size_t i = 0; i < size; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) + { + return false; + } + number_vector.push_back(static_cast(current)); + } + + // parse number string + using ia_type = decltype(detail::input_adapter(number_vector)); + auto number_lexer = detail::lexer(detail::input_adapter(number_vector), false); + const auto result_number = number_lexer.scan(); + const auto number_string = number_lexer.get_token_string(); + const auto result_remainder = number_lexer.scan(); + + using token_type = typename detail::lexer_base::token_type; + + if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) + { + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + + switch (result_number) + { + case token_type::value_integer: + return sax->number_integer(number_lexer.get_number_integer()); + case token_type::value_unsigned: + return sax->number_unsigned(number_lexer.get_number_unsigned()); + case token_type::value_float: + return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); + case token_type::uninitialized: + case token_type::literal_true: + case token_type::literal_false: + case token_type::literal_null: + case token_type::value_string: + case token_type::begin_array: + case token_type::begin_object: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::parse_error: + case token_type::end_of_input: + case token_type::literal_or_value: + default: + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + char_int_type get() + { + ++chars_read; + return current = ia.get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + char_int_type get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianess, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec{}; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. + */ + template + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType())); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; + } + + private: + /// input adapter + InputAdapterType ia; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +enum class parse_event_t : std::uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + +/*! +@brief syntax analysis + +This class implements a recursive descent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + /// a parser reading from an input adapter + explicit parser(InputAdapterType&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + + result.assert_invariant(); + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result && strict && (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (!skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(!sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType())); + } + + case token_type::uninitialized: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::end_of_input: + case token_type::literal_or_value: + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType())); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType())); + } + + // states.back() is false -> object + + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (!context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +// #include + + +#include // ptrdiff_t +#include // numeric_limits + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + JSON_PRIVATE_UNLESS_TESTED: + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl +{ + /// the iterator with BasicJsonType of different const-ness + using other_iter_impl = iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + /// allow basic_json to access private members + friend other_iter_impl; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + iter_impl() = default; + ~iter_impl() = default; + iter_impl(iter_impl&&) noexcept = default; + iter_impl& operator=(iter_impl&&) noexcept = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + if (&other != this) + { + m_object = other.m_object; + m_it = other.m_it; + } + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept // NOLINT(cert-oop54-cpp) + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator==(const IterImpl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator!=(const IterImpl& other) const + { + return !operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object)); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return !other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return !operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return !operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object)); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + JSON_ASSERT(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object)); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + JSON_PRIVATE_UNLESS_TESTED: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // all_of +#include // isdigit +#include // max +#include // accumulate +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the empty + string is assumed which references the whole JSON value + + @throw parse_error.107 if the given JSON pointer @a s is nonempty and does + not begin with a slash (`/`); see example below + + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is + not followed by `0` (representing `~`) or `1` (representing `/`); see + example below + + @liveexample{The example shows the construction several valid JSON pointers + as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + /*! + @brief append another JSON pointer at the end of this JSON pointer + + @param[in] ptr JSON pointer to append + @return JSON pointer with @a ptr appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Linear in the length of @a ptr. + + @sa see @ref operator/=(std::string) to append a reference token + @sa see @ref operator/=(std::size_t) to append an array index + @sa see @ref operator/(const json_pointer&, const json_pointer&) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /*! + @brief append an unescaped reference token at the end of this JSON pointer + + @param[in] token reference token to append + @return JSON pointer with @a token appended without escaping @a token + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa see @ref operator/=(const json_pointer&) to append a JSON pointer + @sa see @ref operator/=(std::size_t) to append an array index + @sa see @ref operator/(const json_pointer&, std::size_t) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /*! + @brief append an array index at the end of this JSON pointer + + @param[in] array_idx array index to append + @return JSON pointer with @a array_idx appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa see @ref operator/=(const json_pointer&) to append a JSON pointer + @sa see @ref operator/=(std::string) to append a reference token + @sa see @ref operator/(const json_pointer&, std::string) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::size_t array_idx) + { + return *this /= std::to_string(array_idx); + } + + /*! + @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + + @param[in] lhs JSON pointer + @param[in] rhs JSON pointer + @return a new JSON pointer with @a rhs appended to @a lhs + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a lhs and @a rhs. + + @sa see @ref operator/=(const json_pointer&) to append a JSON pointer + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /*! + @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] token reference token + @return a new JSON pointer with unescaped @a token appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa see @ref operator/=(std::string) to append a reference token + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::string token) // NOLINT(performance-unnecessary-value-param) + { + return json_pointer(ptr) /= std::move(token); + } + + /*! + @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] array_idx array index + @return a new JSON pointer with @a array_idx appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa see @ref operator/=(std::size_t) to append an array index + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx) + { + return json_pointer(ptr) /= array_idx; + } + + /*! + @brief returns the parent of this JSON pointer + + @return parent of this JSON pointer; in case this JSON pointer is the root, + the root itself is returned + + @complexity Linear in the length of the JSON pointer. + + @liveexample{The example shows the result of `parent_pointer` for different + JSON Pointers.,json_pointer__parent_pointer} + + @since version 3.6.0 + */ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /*! + @brief remove last reference token + + @pre not `empty()` + + @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + reference_tokens.pop_back(); + } + + /*! + @brief return last reference token + + @pre not `empty()` + @return last reference token + + @liveexample{The example shows the usage of `back`.,json_pointer__back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + return reference_tokens.back(); + } + + /*! + @brief append an unescaped token at the end of the reference pointer + + @param[in] token token to add + + @complexity Amortized constant. + + @liveexample{The example shows the result of `push_back` for different + JSON Pointers.,json_pointer__push_back} + + @since version 3.6.0 + */ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @copydoc push_back(const std::string&) + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /*! + @brief return whether pointer points to the root document + + @return true iff the JSON pointer points to the root document + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example shows the result of `empty` for different JSON + Pointers.,json_pointer__empty} + + @since version 3.6.0 + */ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit + @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type + */ + static typename BasicJsonType::size_type array_index(const std::string& s) + { + using size_type = typename BasicJsonType::size_type; + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType())); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType())); + } + + std::size_t processed_chars = 0; + unsigned long long res = 0; // NOLINT(runtime/int) + JSON_TRY + { + res = std::stoull(s, &processed_chars); + } + JSON_CATCH(std::out_of_range&) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE + } + + return static_cast(res); + } + + JSON_PRIVATE_UNLESS_TESTED: + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + private: + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + auto* result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + result = &result->operator[](array_index(reference_token)); + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j)); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums || reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); + } + + // use unchecked array access + ptr = &ptr->operator[](array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (!ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) + { + // invalid char + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) + { + if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) + { + // first char should be between '1' and '9' + return false; + } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } + + const auto idx = array_index(reference_token); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType())); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + JSON_ASSERT(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || + (reference_token[pos + 1] != '0' && + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType())); + } + } + + // finally, store the reference token + detail::unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + detail::escape(element.first), element.second, result); + } + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(!value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value)); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second)); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return !(lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann + +// #include + + +#include +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)) + {} + + json_ref(const value_type& value) + : value_ref(&value) + {} + + json_ref(std::initializer_list init) + : owned_value(init) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...) + {} + + // class should be movable only + json_ref(json_ref&&) noexcept = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (value_ref == nullptr) + { + return std::move(owned_value); + } + return *value_ref; + } + + value_type const& operator*() const + { + return value_ref ? *value_ref : owned_value; + } + + value_type const* operator->() const + { + return &** this; + } + + private: + mutable value_type owned_value = nullptr; + value_type const* value_ref = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + + +#include // reverse +#include // array +#include // isnan, isinf +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy +#include // numeric_limits +#include // string +#include // move + +// #include + +// #include + +// #include + + +#include // copy +#include // size_t +#include // back_inserter +#include // shared_ptr, make_shared +#include // basic_string +#include // vector + +#ifndef JSON_NO_IO + #include // streamsize + #include // basic_ostream +#endif // JSON_NO_IO + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// abstract output adapter interface +template struct output_adapter_protocol +{ + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, std::size_t length) = 0; + virtual ~output_adapter_protocol() = default; + + output_adapter_protocol() = default; + output_adapter_protocol(const output_adapter_protocol&) = default; + output_adapter_protocol(output_adapter_protocol&&) noexcept = default; + output_adapter_protocol& operator=(const output_adapter_protocol&) = default; + output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default; +}; + +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; + +/// output adapter for byte vectors +template +class output_vector_adapter : public output_adapter_protocol +{ + public: + explicit output_vector_adapter(std::vector& vec) noexcept + : v(vec) + {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } + + private: + std::vector& v; +}; + +#ifndef JSON_NO_IO +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter_protocol +{ + public: + explicit output_stream_adapter(std::basic_ostream& s) noexcept + : stream(s) + {} + + void write_character(CharType c) override + { + stream.put(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream& stream; +}; +#endif // JSON_NO_IO + +/// output adapter for basic_string +template> +class output_string_adapter : public output_adapter_protocol +{ + public: + explicit output_string_adapter(StringType& s) noexcept + : str(s) + {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + str.append(s, length); + } + + private: + StringType& str; +}; + +template> +class output_adapter +{ + public: + output_adapter(std::vector& vec) + : oa(std::make_shared>(vec)) {} + +#ifndef JSON_NO_IO + output_adapter(std::basic_ostream& s) + : oa(std::make_shared>(s)) {} +#endif // JSON_NO_IO + + output_adapter(StringType& s) + : oa(std::make_shared>(s)) {} + + operator output_adapter_t() + { + return oa; + } + + private: + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary writer // +/////////////////// + +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; + + public: + /*! + @brief create a binary writer + + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) : oa(std::move(adapter)) + { + JSON_ASSERT(oa); + } + + /*! + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + void write_bson(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + { + write_bson_object(*j.m_value.object); + break; + } + + case value_t::null: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j)); + } + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(to_char_type(0xF6)); + break; + } + + case value_t::boolean: + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xF5) + : to_char_type(0xF4)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x38)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x39)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x3A)); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(to_char_type(0x3B)); + write_number(static_cast(positive_number)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_unsigned)); + } + break; + } + + case value_t::number_float: + { + if (std::isnan(j.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + else + { + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); + } + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x78)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x79)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x98)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x99)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_cbor(el); + } + break; + } + + case value_t::binary: + { + if (j.m_value.binary->has_subtype()) + { + if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd8)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd9)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xda)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xdb)); + write_number(static_cast(j.m_value.binary->subtype())); + } + } + + // step 1: write control byte and the binary array size + const auto N = j.m_value.binary->size(); + if (N <= 0x17) + { + write_number(static_cast(0x40 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x58)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x59)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xA0 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB8)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBA)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBB)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: // nil + { + oa->write_character(to_char_type(0xC0)); + break; + } + + case value_t::boolean: // true and false + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xC3) + : to_char_type(0xC2)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(to_char_type(0xD0)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(to_char_type(0xD1)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(to_char_type(0xD2)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(to_char_type(0xD3)); + write_number(static_cast(j.m_value.number_integer)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + break; + } + + case value_t::number_float: + { + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xA0 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 8 + oa->write_character(to_char_type(0xD9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 16 + oa->write_character(to_char_type(0xDA)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 32 + oa->write_character(to_char_type(0xDB)); + write_number(static_cast(N)); + } + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + write_number(static_cast(0x90 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 16 + oa->write_character(to_char_type(0xDC)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 32 + oa->write_character(to_char_type(0xDD)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_msgpack(el); + } + break; + } + + case value_t::binary: + { + // step 0: determine if the binary type has a set subtype to + // determine whether or not to use the ext or fixext types + const bool use_ext = j.m_value.binary->has_subtype(); + + // step 1: write control byte and the byte string length + const auto N = j.m_value.binary->size(); + if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type{}; + bool fixed = true; + if (use_ext) + { + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } + + } + else + { + output_type = 0xC4; // bin 8 + fixed = false; + } + + oa->write_character(to_char_type(output_type)); + if (!fixed) + { + write_number(static_cast(N)); + } + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + + // step 1.5: if this is an ext type, write the subtype + if (use_ext) + { + write_number(static_cast(j.m_value.binary->subtype())); + } + + // step 2: write the byte string + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xF))); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 16 + oa->write_character(to_char_type(0xDE)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 32 + oa->write_character(to_char_type(0xDF)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + @param[in] use_count whether to use '#' prefixes (optimized format) + @param[in] use_type whether to use '$' prefixes (optimized format) + @param[in] add_prefix whether prefixes need to be used for this value + */ + void write_ubjson(const BasicJsonType& j, const bool use_count, + const bool use_type, const bool add_prefix = true) + { + switch (j.type()) + { + case value_t::null: + { + if (add_prefix) + { + oa->write_character(to_char_type('Z')); + } + break; + } + + case value_t::boolean: + { + if (add_prefix) + { + oa->write_character(j.m_value.boolean + ? to_char_type('T') + : to_char_type('F')); + } + break; + } + + case value_t::number_integer: + { + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); + break; + } + + case value_t::number_unsigned: + { + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); + break; + } + + case value_t::number_float: + { + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); + break; + } + + case value_t::string: + { + if (add_prefix) + { + oa->write_character(to_char_type('S')); + } + write_number_with_ubjson_prefix(j.m_value.string->size(), true); + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.array->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.array->size(), true); + } + + for (const auto& el : *j.m_value.array) + { + write_ubjson(el, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::binary: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + if (use_type && !j.m_value.binary->empty()) + { + JSON_ASSERT(use_count); + oa->write_character(to_char_type('$')); + oa->write_character('U'); + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + } + + if (use_type) + { + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + j.m_value.binary->size()); + } + else + { + for (size_t i = 0; i < j.m_value.binary->size(); ++i) + { + oa->write_character(to_char_type('U')); + oa->write_character(j.m_value.binary->data()[i]); + } + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::object: + { + if (add_prefix) + { + oa->write_character(to_char_type('{')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.object->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin(), j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.object->size(), true); + } + + for (const auto& el : *j.m_value.object) + { + write_number_with_ubjson_prefix(el.first.size(), true); + oa->write_characters( + reinterpret_cast(el.first.c_str()), + el.first.size()); + write_ubjson(el.second, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type('}')); + } + + break; + } + + case value_t::discarded: + default: + break; + } + } + + private: + ////////// + // BSON // + ////////// + + /*! + @return The size of a BSON document entry header, including the id marker + and the entry name size (and its null-terminator). + */ + static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) + { + const auto it = name.find(static_cast(0)); + if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) + { + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j)); + static_cast(j); + } + + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; + } + + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const string_t& name, + const std::uint8_t element_type) + { + oa->write_character(to_char_type(element_type)); // boolean + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + } + + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const string_t& name, + const bool value) + { + write_bson_entry_header(name, 0x08); + oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const string_t& name, + const double value) + { + write_bson_entry_header(name, 0x01); + write_number(value); + } + + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const string_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const string_t& name, + const string_t& value) + { + write_bson_entry_header(name, 0x02); + + write_number(static_cast(value.size() + 1ul)); + oa->write_characters( + reinterpret_cast(value.c_str()), + value.size() + 1); + } + + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const string_t& name) + { + write_bson_entry_header(name, 0x0A); + } + + /*! + @return The size of the BSON-encoded integer @a value + */ + static std::size_t calc_bson_integer_size(const std::int64_t value) + { + return (std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)() + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const string_t& name, + const std::int64_t value) + { + if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) + { + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); + } + else + { + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); + } + } + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept + { + return (value <= static_cast((std::numeric_limits::max)())) + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const string_t& name, + const BasicJsonType& j) + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x10 /* int32 */); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x12 /* int64 */); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j)); + } + } + + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const string_t& name, + const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } + + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t array_index = 0ul; + + const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) + { + return result + calc_bson_element_size(std::to_string(array_index++), el); + }); + + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } + + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const string_t& name, + const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number(static_cast(calc_bson_array_size(value))); + + std::size_t array_index = 0ul; + + for (const auto& el : value) + { + write_bson_element(std::to_string(array_index++), el); + } + + oa->write_character(to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, + const binary_t& value) + { + write_bson_entry_header(name, 0x05); + + write_number(static_cast(value.size())); + write_number(value.has_subtype() ? static_cast(value.subtype()) : std::uint8_t(0x00)); + + oa->write_characters(reinterpret_cast(value.data()), value.size()); + } + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const string_t& name, + const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name, j); + switch (j.type()) + { + case value_t::object: + return header_size + calc_bson_object_size(*j.m_value.object); + + case value_t::array: + return header_size + calc_bson_array_size(*j.m_value.array); + + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_value.binary); + + case value_t::boolean: + return header_size + 1ul; + + case value_t::number_float: + return header_size + 8ul; + + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_value.number_integer); + + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); + + case value_t::string: + return header_size + calc_bson_string_size(*j.m_value.string); + + case value_t::null: + return header_size + 0ul; + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return 0ul; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the + key @a name. + @param name The name to associate with the JSON entity @a j within the + current BSON document + */ + void write_bson_element(const string_t& name, + const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + return write_bson_object_entry(name, *j.m_value.object); + + case value_t::array: + return write_bson_array(name, *j.m_value.array); + + case value_t::binary: + return write_bson_binary(name, *j.m_value.binary); + + case value_t::boolean: + return write_bson_boolean(name, j.m_value.boolean); + + case value_t::number_float: + return write_bson_double(name, j.m_value.number_float); + + case value_t::number_integer: + return write_bson_integer(name, j.m_value.number_integer); + + case value_t::number_unsigned: + return write_bson_unsigned(name, j); + + case value_t::string: + return write_bson_string(name, *j.m_value.string); + + case value_t::null: + return write_bson_null(name); + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0), + [](size_t result, const typename BasicJsonType::object_t::value_type & el) + { + return result += calc_bson_element_size(el.first, el.second); + }); + + return sizeof(std::int32_t) + document_size + 1ul; + } + + /*! + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + void write_bson_object(const typename BasicJsonType::object_t& value) + { + write_number(static_cast(calc_bson_object_size(value))); + + for (const auto& el : value) + { + write_bson_element(el.first, el.second); + } + + oa->write_character(to_char_type(0x00)); + } + + ////////// + // CBOR // + ////////// + + static constexpr CharType get_cbor_float_prefix(float /*unused*/) + { + return to_char_type(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double /*unused*/) + { + return to_char_type(0xFB); // Double-Precision Float + } + + ///////////// + // MsgPack // + ///////////// + + static constexpr CharType get_msgpack_float_prefix(float /*unused*/) + { + return to_char_type(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double /*unused*/) + { + return to_char_type(0xCB); // float 64 + } + + //////////// + // UBJSON // + //////////// + + // UBJSON: write number (floating point) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (add_prefix) + { + oa->write_character(get_ubjson_float_prefix(n)); + } + write_number(n); + } + + // UBJSON: write number (unsigned integer) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + } + + // UBJSON: write number (signed integer) + template < typename NumberType, typename std::enable_if < + std::is_signed::value&& + !std::is_floating_point::value, int >::type = 0 > + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + // LCOV_EXCL_START + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + // LCOV_EXCL_STOP + } + + /*! + @brief determine the type prefix of container values + */ + CharType ubjson_prefix(const BasicJsonType& j) const noexcept + { + switch (j.type()) + { + case value_t::null: + return 'Z'; + + case value_t::boolean: + return j.m_value.boolean ? 'T' : 'F'; + + case value_t::number_integer: + { + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'i'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'U'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'I'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'l'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'i'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'U'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'I'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'l'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_float: + return get_ubjson_float_prefix(j.m_value.number_float); + + case value_t::string: + return 'S'; + + case value_t::array: // fallthrough + case value_t::binary: + return '['; + + case value_t::object: + return '{'; + + case value_t::discarded: + default: // discarded values + return 'N'; + } + } + + static constexpr CharType get_ubjson_float_prefix(float /*unused*/) + { + return 'd'; // float 32 + } + + static constexpr CharType get_ubjson_float_prefix(double /*unused*/) + { + return 'D'; // float 64 + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /* + @brief write a number to output input + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number + @tparam OutputIsLittleEndian Set to true if output data is + required to be little endian + + @note This function needs to respect the system's endianess, because bytes + in CBOR, MessagePack, and UBJSON are stored in network order (big + endian) and therefore need reordering on little endian systems. + */ + template + void write_number(const NumberType n) + { + // step 1: write number to array of length NumberType + std::array vec{}; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write array to output (with possible reordering) + if (is_little_endian != OutputIsLittleEndian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + void write_compact_float(const number_float_t n, detail::input_format_t format) + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) && + static_cast(n) <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + public: + // The following to_char_type functions are implement the conversion + // between uint8_t and CharType. In case CharType is not unsigned, + // such a conversion is required to allow values greater than 128. + // See for a discussion. + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_signed::value > * = nullptr > + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return *reinterpret_cast(&x); + } + + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > + static CharType to_char_type(std::uint8_t x) noexcept + { + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + static_assert(std::is_trivial::value, "CharType must be trivial"); + CharType result; + std::memcpy(&result, &x, sizeof(x)); + return result; + } + + template::value>* = nullptr> + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return x; + } + + template < typename InputCharType, typename C = CharType, + enable_if_t < + std::is_signed::value && + std::is_signed::value && + std::is_same::type>::value + > * = nullptr > + static constexpr CharType to_char_type(InputCharType x) noexcept + { + return x; + } + + private: + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the output + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // reverse, remove, fill, find, none_of +#include // array +#include // localeconv, lconv +#include // labs, isfinite, isnan, signbit +#include // size_t, ptrdiff_t +#include // uint8_t +#include // snprintf +#include // numeric_limits +#include // string, char_traits +#include // is_same +#include // move + +// #include + + +#include // array +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + JSON_ASSERT(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const auto bits = static_cast(reinterpret_bits(value)); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 && E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + if (n >= 100000) + { + pow10 = 100000; + return 6; + } + if (n >= 10000) + { + pow10 = 10000; + return 5; + } + if (n >= 1000) + { + pow10 = 1000; + return 4; + } + if (n >= 100) + { + pow10 = 100; + return 3; + } + if (n >= 10) + { + pow10 = 10; + return 2; + } + + pow10 = 1; + return 1; +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + && delta - rest >= ten_k + && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) + { + JSON_ASSERT(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + JSON_ASSERT(p1 > 0); + + std::uint32_t pow10{}; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + JSON_ASSERT(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n && n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (static_cast(n) + 2); + } + + if (0 < n && n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + JSON_ASSERT(k > n); + + std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); + buf[n] = '.'; + return buf + (static_cast(k) + 1U); + } + + if (min_exp < n && n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2U + static_cast(-n) + static_cast(k)); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); + buf[1] = '.'; + buf += 1 + static_cast(k); + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + JSON_ASSERT(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + JSON_ASSERT(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// serialization // +/////////////////// + +/// how to treat decoding errors +enum class error_handler_t +{ + strict, ///< throw a type_error exception in case of invalid UTF-8 + replace, ///< replace invalid UTF-8 sequences with U+FFFD + ignore ///< ignore invalid UTF-8 sequences +}; + +template +class serializer +{ + using string_t = typename BasicJsonType::string_t; + using number_float_t = typename BasicJsonType::number_float_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; + static constexpr std::uint8_t UTF8_ACCEPT = 0; + static constexpr std::uint8_t UTF8_REJECT = 1; + + public: + /*! + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use + @param[in] error_handler_ how to react on decoding errors + */ + serializer(output_adapter_t s, const char ichar, + error_handler_t error_handler_ = error_handler_t::strict) + : o(std::move(s)) + , loc(std::localeconv()) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) + , indent_char(ichar) + , indent_string(512, indent_char) + , error_handler(error_handler_) + {} + + // delete because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; + serializer(serializer&&) = delete; + serializer& operator=(serializer&&) = delete; + ~serializer() = default; + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + - binary values are serialized as objects containing the subtype and the + byte array + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const BasicJsonType& val, + const bool pretty_print, + const bool ensure_ascii, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_type) + { + case value_t::object: + { + if (val.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_character('{'); + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + + o->write_character('}'); + } + + return; + } + + case value_t::array: + { + if (val.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } + + if (pretty_print) + { + o->write_characters("[\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + dump(*i, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + + o->write_character(']'); + } + + return; + } + + case value_t::string: + { + o->write_character('\"'); + dump_escaped(*val.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::binary: + { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"bytes\":[", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } + } + return; + } + + case value_t::boolean: + { + if (val.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: + { + dump_integer(val.m_value.number_integer); + return; + } + + case value_t::number_unsigned: + { + dump_integer(val.m_value.number_unsigned); + return; + } + + case value_t::number_float: + { + dump_float(val.m_value.number_float); + return; + } + + case value_t::discarded: + { + o->write_characters("", 11); + return; + } + + case value_t::null: + { + o->write_characters("null", 4); + return; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief dump escaped string + + Escape a string by replacing certain special characters by a sequence of an + escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. + + @param[in] s the string to escape + @param[in] ensure_ascii whether to escape non-ASCII characters with + \uXXXX sequences + + @complexity Linear in the length of string @a s. + */ + void dump_escaped(const string_t& s, const bool ensure_ascii) + { + std::uint32_t codepoint{}; + std::uint8_t state = UTF8_ACCEPT; + std::size_t bytes = 0; // number of bytes written to string_buffer + + // number of bytes written at the point of the last valid byte + std::size_t bytes_after_last_accept = 0; + std::size_t undumped_chars = 0; + + for (std::size_t i = 0; i < s.size(); ++i) + { + const auto byte = static_cast(s[i]); + + switch (decode(state, codepoint, byte)) + { + case UTF8_ACCEPT: // decode found a new code point + { + switch (codepoint) + { + case 0x08: // backspace + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'b'; + break; + } + + case 0x09: // horizontal tab + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 't'; + break; + } + + case 0x0A: // newline + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'n'; + break; + } + + case 0x0C: // formfeed + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'f'; + break; + } + + case 0x0D: // carriage return + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'r'; + break; + } + + case 0x22: // quotation mark + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\"'; + break; + } + + case 0x5C: // reverse solidus + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\\'; + break; + } + + default: + { + // escape control characters (0x00..0x1F) or, if + // ensure_ascii parameter is used, non-ASCII characters + if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) + { + if (codepoint <= 0xFFFF) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + (std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x", + static_cast(codepoint)); + bytes += 6; + } + else + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + (std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", + static_cast(0xD7C0u + (codepoint >> 10u)), + static_cast(0xDC00u + (codepoint & 0x3FFu))); + bytes += 12; + } + } + else + { + // copy byte to buffer (all previous bytes + // been copied have in default case above) + string_buffer[bytes++] = s[i]; + } + break; + } + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + // remember the byte position of this accept + bytes_after_last_accept = bytes; + undumped_chars = 0; + break; + } + + case UTF8_REJECT: // decode found invalid UTF-8 byte + { + switch (error_handler) + { + case error_handler_t::strict: + { + std::string sn(9, '\0'); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType())); + } + + case error_handler_t::ignore: + case error_handler_t::replace: + { + // in case we saw this character the first time, we + // would like to read it again, because the byte + // may be OK for itself, but just not OK for the + // previous sequence + if (undumped_chars > 0) + { + --i; + } + + // reset length buffer to the last accepted index; + // thus removing/ignoring the invalid characters + bytes = bytes_after_last_accept; + + if (error_handler == error_handler_t::replace) + { + // add a replacement character + if (ensure_ascii) + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'u'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'd'; + } + else + { + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xEF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBD'); + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + bytes_after_last_accept = bytes; + } + + undumped_chars = 0; + + // continue processing the string + state = UTF8_ACCEPT; + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + break; + } + + default: // decode found yet incomplete multi-byte code point + { + if (!ensure_ascii) + { + // code point will not be escaped - copy byte to buffer + string_buffer[bytes++] = s[i]; + } + ++undumped_chars; + break; + } + } + } + + // we finished processing the string + if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) + { + // write buffer + if (bytes > 0) + { + o->write_characters(string_buffer.data(), bytes); + } + } + else + { + // we finish reading, but do not accept: string was incomplete + switch (error_handler) + { + case error_handler_t::strict: + { + std::string sn(9, '\0'); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType())); + } + + case error_handler_t::ignore: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + break; + } + + case error_handler_t::replace: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + // add a replacement character + if (ensure_ascii) + { + o->write_characters("\\ufffd", 6); + } + else + { + o->write_characters("\xEF\xBF\xBD", 3); + } + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + } + + private: + /*! + @brief count digits + + Count the number of decimal (base 10) digits for an input unsigned integer. + + @param[in] x unsigned integer number to count its digits + @return number of decimal digits + */ + inline unsigned int count_digits(number_unsigned_t x) noexcept + { + unsigned int n_digits = 1; + for (;;) + { + if (x < 10) + { + return n_digits; + } + if (x < 100) + { + return n_digits + 1; + } + if (x < 1000) + { + return n_digits + 2; + } + if (x < 10000) + { + return n_digits + 3; + } + x = x / 10000u; + n_digits += 4; + } + } + + /*! + @brief dump an integer + + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. + + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t + */ + template < typename NumberType, detail::enable_if_t < + std::is_integral::value || + std::is_same::value || + std::is_same::value || + std::is_same::value, + int > = 0 > + void dump_integer(NumberType x) + { + static constexpr std::array, 100> digits_to_99 + { + { + {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}}, + {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}}, + {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}}, + {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}}, + {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}}, + {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}}, + {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}}, + {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}}, + {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}}, + {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}}, + } + }; + + // special case for "0" + if (x == 0) + { + o->write_character('0'); + return; + } + + // use a pointer to fill the buffer + auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg) + + const bool is_negative = std::is_signed::value && !(x >= 0); // see issue #755 + number_unsigned_t abs_value; + + unsigned int n_chars{}; + + if (is_negative) + { + *buffer_ptr = '-'; + abs_value = remove_sign(static_cast(x)); + + // account one more byte for the minus sign + n_chars = 1 + count_digits(abs_value); + } + else + { + abs_value = static_cast(x); + n_chars = count_digits(abs_value); + } + + // spare 1 byte for '\0' + JSON_ASSERT(n_chars < number_buffer.size() - 1); + + // jump to the end to generate the string from backward + // so we later avoid reversing the result + buffer_ptr += n_chars; + + // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu + // See: https://www.youtube.com/watch?v=o4-CwDo2zpg + while (abs_value >= 100) + { + const auto digits_index = static_cast((abs_value % 100)); + abs_value /= 100; + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + + if (abs_value >= 10) + { + const auto digits_index = static_cast(abs_value); + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + else + { + *(--buffer_ptr) = static_cast('0' + abs_value); + } + + o->write_characters(number_buffer.data(), n_chars); + } + + /*! + @brief dump a floating-point number + + Dump a given floating-point number to output stream @a o. Works internally + with @a number_buffer. + + @param[in] x floating-point number to dump + */ + void dump_float(number_float_t x) + { + // NaN / inf + if (!std::isfinite(x)) + { + o->write_characters("null", 4); + return; + } + + // If number_float_t is an IEEE-754 single or double precision number, + // use the Grisu2 algorithm to produce short numbers which are + // guaranteed to round-trip, using strtof and strtod, resp. + // + // NB: The test below works if == . + static constexpr bool is_ieee_single_or_double + = (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) || + (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024); + + dump_float(x, std::integral_constant()); + } + + void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + { + auto* begin = number_buffer.data(); + auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + + o->write_characters(begin, static_cast(end - begin)); + } + + void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + { + // get number of digits for a float -> text -> float round-trip + static constexpr auto d = std::numeric_limits::max_digits10; + + // the actual conversion + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + + // negative value indicates an error + JSON_ASSERT(len > 0); + // check if buffer was large enough + JSON_ASSERT(static_cast(len) < number_buffer.size()); + + // erase thousands separator + if (thousands_sep != '\0') + { + auto* const end = std::remove(number_buffer.begin(), + number_buffer.begin() + len, thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + JSON_ASSERT((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } + + // convert decimal point to '.' + if (decimal_point != '\0' && decimal_point != '.') + { + auto* const dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); + if (dec_pos != number_buffer.end()) + { + *dec_pos = '.'; + } + } + + o->write_characters(number_buffer.data(), static_cast(len)); + + // determine if need to append ".0" + const bool value_is_int_like = + std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, + [](char c) + { + return c == '.' || c == 'e'; + }); + + if (value_is_int_like) + { + o->write_characters(".0", 2); + } + } + + /*! + @brief check whether a string is UTF-8 encoded + + The function checks each byte of a string whether it is UTF-8 encoded. The + result of the check is stored in the @a state parameter. The function must + be called initially with state 0 (accept). State 1 means the string must + be rejected, because the current byte is not allowed. If the string is + completely processed, but the state is non-zero, the string ended + prematurely; that is, the last byte indicated more bytes should have + followed. + + @param[in,out] state the state of the decoding + @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) + @param[in] byte next byte to decode + @return new state + + @note The function has been edited: a std::array is used. + + @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann + @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + */ + static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept + { + static const std::array utf8d = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF + 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF + 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 + } + }; + + JSON_ASSERT(byte < utf8d.size()); + const std::uint8_t type = utf8d[byte]; + + codep = (state != UTF8_ACCEPT) + ? (byte & 0x3fu) | (codep << 6u) + : (0xFFu >> type) & (byte); + + std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + JSON_ASSERT(index < 400); + state = utf8d[index]; + return state; + } + + /* + * Overload to make the compiler happy while it is instantiating + * dump_integer for number_unsigned_t. + * Must never be called. + */ + number_unsigned_t remove_sign(number_unsigned_t x) + { + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return x; // LCOV_EXCL_LINE + } + + /* + * Helper function for dump_integer + * + * This function takes a negative signed integer and returns its absolute + * value as unsigned integer. The plus/minus shuffling is necessary as we can + * not directly remove the sign of an arbitrary signed integer as the + * absolute values of INT_MIN and INT_MAX are usually not the same. See + * #1708 for details. + */ + inline number_unsigned_t remove_sign(number_integer_t x) noexcept + { + JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); // NOLINT(misc-redundant-expression) + return static_cast(-(x + 1)) + 1; + } + + private: + /// the output of the serializer + output_adapter_t o = nullptr; + + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; + + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; + + /// string buffer + std::array string_buffer{{}}; + + /// the indentation character + const char indent_char; + /// the indentation string + string_t indent_string; + + /// error_handler how to react on decoding errors + const error_handler_t error_handler; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // less +#include // initializer_list +#include // input_iterator_tag, iterator_traits +#include // allocator +#include // for out_of_range +#include // enable_if, is_convertible +#include // pair +#include // vector + +// #include + + +namespace nlohmann +{ + +/// ordered_map: a minimal map-like container that preserves insertion order +/// for use within nlohmann::basic_json +template , + class Allocator = std::allocator>> + struct ordered_map : std::vector, Allocator> +{ + using key_type = Key; + using mapped_type = T; + using Container = std::vector, Allocator>; + using typename Container::iterator; + using typename Container::const_iterator; + using typename Container::size_type; + using typename Container::value_type; + + // Explicit constructors instead of `using Container::Container` + // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) + ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + template + ordered_map(It first, It last, const Allocator& alloc = Allocator()) + : Container{first, last, alloc} {} + ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) + : Container{init, alloc} {} + + std::pair emplace(const key_type& key, T&& t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return {it, false}; + } + } + Container::emplace_back(key, t); + return {--this->end(), true}; + } + + T& operator[](const Key& key) + { + return emplace(key, T{}).first->second; + } + + const T& operator[](const Key& key) const + { + return at(key); + } + + T& at(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + const T& at(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + size_type erase(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + iterator erase(iterator pos) + { + auto it = pos; + + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return pos; + } + + size_type count(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return 1; + } + } + return 0; + } + + iterator find(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + const_iterator find(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + std::pair insert( value_type&& value ) + { + return emplace(value.first, std::move(value.second)); + } + + std::pair insert( const value_type& value ) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == value.first) + { + return {it, false}; + } + } + Container::push_back(value); + return {--this->end(), true}; + } + + template + using require_input_iter = typename std::enable_if::iterator_category, + std::input_iterator_tag>::value>::type; + + template> + void insert(InputIt first, InputIt last) + { + for (auto it = first; it != last; ++it) + { + insert(*it); + } + } +}; + +} // namespace nlohmann + + +#if defined(JSON_HAS_CPP_17) + #include +#endif + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ + +/*! +@brief a class to store JSON values + +@tparam ObjectType type for JSON objects (`std::map` by default; will be used +in @ref object_t) +@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used +in @ref array_t) +@tparam StringType type for JSON strings and object keys (`std::string` by +default; will be used in @ref string_t) +@tparam BooleanType type for JSON booleans (`bool` by default; will be used +in @ref boolean_t) +@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by +default; will be used in @ref number_integer_t) +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) +@tparam NumberFloatType type for JSON floating-point numbers (`double` by +default; will be used in @ref number_float_t) +@tparam BinaryType type for packed binary data for compatibility with binary +serialization formats (`std::vector` by default; will be used in +@ref binary_t) +@tparam AllocatorType type of the allocator to use (`std::allocator` by +default) +@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` +and `from_json()` (@ref adl_serializer by default) + +@requirement The class satisfies the following concept requirements: +- Basic + - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): + JSON values can be default constructed. The result will be a JSON null + value. + - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible): + A JSON value can be constructed from an rvalue argument. + - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): + A JSON value can be copy-constructed from an lvalue expression. + - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): + A JSON value van be assigned from an rvalue argument. + - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): + A JSON value can be copy-assigned from an lvalue expression. + - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible): + JSON values can be destructed. +- Layout + - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType): + JSON values have + [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. +- Library-wide + - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable): + JSON values can be compared with `==`, see @ref + operator==(const_reference,const_reference). + - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): + JSON values can be compared with `<`, see @ref + operator<(const_reference,const_reference). + - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable): + Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of + other compatible types, using unqualified function call @ref swap(). + - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer): + JSON values can be compared against `std::nullptr_t` objects which are used + to model the `null` value. +- Container + - [Container](https://en.cppreference.com/w/cpp/named_req/Container): + JSON values can be used like STL containers and provide iterator access. + - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer); + JSON values can be used like STL containers and provide reverse iterator + access. + +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@internal +@note ObjectType trick from https://stackoverflow.com/a/9860911 +@endinternal + +@see [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange +Format](https://tools.ietf.org/html/rfc8259) + +@since version 1.0.0 + +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) +{ + private: + template friend struct detail::external_constructor; + friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + template + friend class ::nlohmann::detail::iter_impl; + template + friend class ::nlohmann::detail::binary_writer; + template + friend class ::nlohmann::detail::binary_reader; + template + friend class ::nlohmann::detail::json_sax_dom_parser; + template + friend class ::nlohmann::detail::json_sax_dom_callback_parser; + friend class ::nlohmann::detail::exception; + + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + + JSON_PRIVATE_UNLESS_TESTED: + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false + ) + { + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); + } + + private: + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + template + using binary_reader = ::nlohmann::detail::binary_reader; + template using binary_writer = ::nlohmann::detail::binary_writer; + + JSON_PRIVATE_UNLESS_TESTED: + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + /// JSON Pointer, see @ref nlohmann::json_pointer + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + using input_format_t = detail::input_format_t; + /// SAX interface type, see @ref nlohmann::json_sax + using json_sax_t = json_sax; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + /// @copydoc detail::exception + using exception = detail::exception; + /// @copydoc detail::parse_error + using parse_error = detail::parse_error; + /// @copydoc detail::invalid_iterator + using invalid_iterator = detail::invalid_iterator; + /// @copydoc detail::type_error + using type_error = detail::type_error; + /// @copydoc detail::out_of_range + using out_of_range = detail::out_of_range; + /// @copydoc detail::other_error + using other_error = detail::other_error; + + /// @} + + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + + /*! + @brief returns the allocator associated with the container + */ + static allocator_type get_allocator() + { + return allocator_type(); + } + + /*! + @brief returns version information on the library + + This function returns a JSON object with information about the library, + including the version number and information on the platform and compiler. + + @return JSON object holding version information + key | description + ----------- | --------------- + `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). + `copyright` | The copyright line for the library as string. + `name` | The name of the library as string. + `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. + `url` | The URL of the project as string. + `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). + + @liveexample{The following code shows an example output of the `meta()` + function.,meta} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @complexity Constant. + + @since 2.1.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2021 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"]["string"] = + std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_PATCH); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + +#if defined(JSON_HAS_CPP_14) + // Use transparent comparator if possible, combined with perfect forwarding + // on find() and count() calls prevents unnecessary string construction. + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + /*! + @brief a type for an object + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON objects as follows: + > An object is an unordered collection of zero or more name/value pairs, + > where a name is a string and a value is a string, number, boolean, null, + > object, or array. + + To store objects in C++, a type is defined by the template parameters + described below. + + @tparam ObjectType the container to store objects (e.g., `std::map` or + `std::unordered_map`) + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements + inside the container. + @tparam AllocatorType the allocator to use for objects (e.g., + `std::allocator`) + + #### Default type + + With the default values for @a ObjectType (`std::map`), @a StringType + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: + + @code {.cpp} + std::map< + std::string, // key_type + basic_json, // value_type + std::less, // key_compare + std::allocator> // allocator_type + > + @endcode + + #### Behavior + + The choice of @a object_t influences the behavior of the JSON class. With + the default type, objects have the following behavior: + + - When all names are unique, objects will be interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. + - When the names within an object are not unique, it is unspecified which + one of the values for a given key will be chosen. For instance, + `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or + `{"key": 2}`. + - Internally, name/value pairs are stored in lexicographical order of the + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. + - When comparing objects, the order of the name/value pairs is irrelevant. + This makes objects interoperable in the sense that they will not be + affected by these differences. For instance, `{"b": 1, "a": 2}` and + `{"a": 2, "b": 1}` will be treated as equal. + + #### Limits + + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the object's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. + + #### Storage + + Objects are stored as pointers in a @ref basic_json type. That is, for any + access to object values, a pointer of type `object_t*` must be + dereferenced. + + @sa see @ref array_t -- type for an array value + + @since version 1.0.0 + + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC + 8259](https://tools.ietf.org/html/rfc8259), because any order implements the + specified "unordered" nature of JSON objects. + */ + using object_t = ObjectType>>; + + /*! + @brief a type for an array + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON arrays as follows: + > An array is an ordered sequence of zero or more values. + + To store objects in C++, a type is defined by the template parameters + explained below. + + @tparam ArrayType container type to store arrays (e.g., `std::vector` or + `std::list`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + + #### Default type + + With the default values for @a ArrayType (`std::vector`) and @a + AllocatorType (`std::allocator`), the default value for @a array_t is: + + @code {.cpp} + std::vector< + basic_json, // value_type + std::allocator // allocator_type + > + @endcode + + #### Limits + + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the array's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. + + #### Storage + + Arrays are stored as pointers in a @ref basic_json type. That is, for any + access to array values, a pointer of type `array_t*` must be dereferenced. + + @sa see @ref object_t -- type for an object value + + @since version 1.0.0 + */ + using array_t = ArrayType>; + + /*! + @brief a type for a string + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows: + > A string is a sequence of zero or more Unicode characters. + + To store objects in C++, a type is defined by the template parameter + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. + + @tparam StringType the container to store strings (e.g., `std::string`). + Note this container is used for keys/names in objects, see @ref object_t. + + #### Default type + + With the default values for @a StringType (`std::string`), the default + value for @a string_t is: + + @code {.cpp} + std::string + @endcode + + #### Encoding + + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. + + #### String comparison + + [RFC 8259](https://tools.ietf.org/html/rfc8259) states: + > Software implementations are typically required to test names of object + > members for equality. Implementations that transform the textual + > representation into sequences of Unicode code units and then perform the + > comparison numerically, code unit by code unit, are interoperable in the + > sense that implementations will agree in all cases on equality or + > inequality of two strings. For example, implementations that compare + > strings with escaped characters unconverted may incorrectly find that + > `"a\\b"` and `"a\u005Cb"` are not equal. + + This implementation is interoperable as it does compare strings code unit + by code unit. + + #### Storage + + String values are stored as pointers in a @ref basic_json type. That is, + for any access to string values, a pointer of type `string_t*` must be + dereferenced. + + @since version 1.0.0 + */ + using string_t = StringType; + + /*! + @brief a type for a boolean + + [RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a + type which differentiates the two literals `true` and `false`. + + To store objects in C++, a type is defined by the template parameter @a + BooleanType which chooses the type to use. + + #### Default type + + With the default values for @a BooleanType (`bool`), the default value for + @a boolean_t is: + + @code {.cpp} + bool + @endcode + + #### Storage + + Boolean values are stored directly inside a @ref basic_json type. + + @since version 1.0.0 + */ + using boolean_t = BooleanType; + + /*! + @brief a type for a number (integer) + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store integer numbers in C++, a type is defined by the template + parameter @a NumberIntegerType which chooses the type to use. + + #### Default type + + With the default values for @a NumberIntegerType (`int64_t`), the default + value for @a number_integer_t is: + + @code {.cpp} + int64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `9223372036854775807` (INT64_MAX) and the minimal integer number + that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. + + [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange of the exactly supported range [INT64_MIN, + INT64_MAX], this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa see @ref number_float_t -- type for number values (floating-point) + + @sa see @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_integer_t = NumberIntegerType; + + /*! + @brief a type for a number (unsigned) + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. + + [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa see @ref number_float_t -- type for number values (floating-point) + @sa see @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + + /*! + @brief a type for a number (floating-point) + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store floating-point numbers in C++, a type is defined by the template + parameter @a NumberFloatType which chooses the type to use. + + #### Default type + + With the default values for @a NumberFloatType (`double`), the default + value for @a number_float_t is: + + @code {.cpp} + double + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ + floating-point literal `01.2` will be serialized to `1.2`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 8259](https://tools.ietf.org/html/rfc8259) states: + > This specification allows implementations to set limits on the range and + > precision of numbers accepted. Since software that implements IEEE + > 754-2008 binary64 (double precision) numbers is generally available and + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected + > precision. + + This implementation does exactly follow this approach, as it uses double + precision floating-point numbers. Note values smaller than + `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` + will be stored as NaN internally and be serialized to `null`. + + #### Storage + + Floating-point number values are stored directly inside a @ref basic_json + type. + + @sa see @ref number_integer_t -- type for number values (integer) + + @sa see @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_float_t = NumberFloatType; + + /*! + @brief a type for a packed binary type + + This type is a type designed to carry binary data that appears in various + serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and + BSON's generic binary subtype. This type is NOT a part of standard JSON and + exists solely for compatibility with these binary types. As such, it is + simply defined as an ordered sequence of zero or more byte values. + + Additionally, as an implementation detail, the subtype of the binary data is + carried around as a `std::uint8_t`, which is compatible with both of the + binary data formats that use binary subtyping, (though the specific + numbering is incompatible with each other, and it is up to the user to + translate between them). + + [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type + as: + > Major type 2: a byte string. The string's length in bytes is represented + > following the rules for positive integers (major type 0). + + [MessagePack's documentation on the bin type + family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) + describes this type as: + > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes + > in addition to the size of the byte array. + + [BSON's specifications](http://bsonspec.org/spec.html) describe several + binary types; however, this type is intended to represent the generic binary + type which has the description: + > Generic binary subtype - This is the most commonly used binary subtype and + > should be the 'default' for drivers and tools. + + None of these impose any limitations on the internal representation other + than the basic unit of storage be some type of array whose parts are + decomposable into bytes. + + The default representation of this binary format is a + `std::vector`, which is a very common way to represent a byte + array in modern C++. + + #### Default type + + The default values for @a BinaryType is `std::vector` + + #### Storage + + Binary Arrays are stored as pointers in a @ref basic_json type. That is, + for any access to array values, a pointer of the type `binary_t*` must be + dereferenced. + + #### Notes on subtypes + + - CBOR + - Binary values are represented as byte strings. Subtypes are serialized + as tagged values. + - MessagePack + - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, + or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) + is used. For other sizes, the ext family (ext8, ext16, ext32) is used. + The subtype is then added as singed 8-bit integer. + - If no subtype is given, the bin family (bin8, bin16, bin32) is used. + - BSON + - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If no subtype is given, the generic binary subtype 0x00 is used. + + @sa see @ref binary -- create a binary array + + @since version 3.8.0 + */ + using binary_t = nlohmann::byte_container_with_subtype; + /// @} + + private: + + /// helper for exception-safe object creation + template + JSON_HEDLEY_RETURNS_NON_NULL + static T* create(Args&& ... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T * obj) + { + AllocatorTraits::deallocate(alloc, obj, 1); + }; + std::unique_ptr obj(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, obj.get(), std::forward(args)...); + JSON_ASSERT(obj != nullptr); + return obj.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::binary: + { + binary = create(); + break; + } + + case value_t::boolean: + { + boolean = boolean_t(false); + break; + } + + case value_t::number_integer: + { + number_integer = number_integer_t(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } + + case value_t::number_float: + { + number_float = number_float_t(0.0); + break; + } + + case value_t::null: + { + object = nullptr; // silence warning, see #821 + break; + } + + case value_t::discarded: + default: + { + object = nullptr; // silence warning, see #821 + if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.2", basic_json())); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) + { + string = create(value); + } + + /// constructor for rvalue strings + json_value(string_t&& value) + { + string = create(std::move(value)); + } + + /// constructor for objects + json_value(const object_t& value) + { + object = create(value); + } + + /// constructor for rvalue objects + json_value(object_t&& value) + { + object = create(std::move(value)); + } + + /// constructor for arrays + json_value(const array_t& value) + { + array = create(value); + } + + /// constructor for rvalue arrays + json_value(array_t&& value) + { + array = create(std::move(value)); + } + + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) + { + binary = create(std::move(value)); + } + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) + { + binary = create(std::move(value)); + } + + void destroy(value_t t) + { + if (t == value_t::array || t == value_t::object) + { + // flatten the current json_value to a heap-allocated stack + std::vector stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } + } + + while (!stack.empty()) + { + // move the last item to local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack)); + + current_item.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_value.object->clear(); + } + + // it's now safe that current_item get destructed + // since it doesn't have any children + } + } + + switch (t) + { + case value_t::object: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + case value_t::binary: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + + case value_t::null: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::discarded: + default: + { + break; + } + } + } + }; + + private: + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to false + during destruction of objects when the invariant does not + need to hold. + */ + void assert_invariant(bool check_parents = true) const noexcept + { + JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_TRY + { + // cppcheck-suppress assertWithSideEffect + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); + } + JSON_CATCH(...) {} // LCOV_EXCL_LINE +#endif + static_cast(check_parents); + } + + void set_parents() + { +#if JSON_DIAGNOSTICS + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + break; + } +#endif + } + + iterator set_parents(iterator it, typename iterator::difference_type count) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast(count); +#endif + return it; + } + + reference set_parent(reference j, std::size_t old_capacity = std::size_t(-1)) + { +#if JSON_DIAGNOSTICS + if (old_capacity != std::size_t(-1)) + { + // see https://github.com/nlohmann/json/issues/2838 + JSON_ASSERT(type() == value_t::array); + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + return j; + } + } + + // ordered_json uses a vector internally, so pointers could have + // been invalidated; see https://github.com/nlohmann/json/issues/2962 +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(push ) +#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr +#endif + if (detail::is_ordered_map::value) + { + set_parents(); + return j; + } +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning( pop ) +#endif + + j.m_parent = this; +#else + static_cast(j); + static_cast(old_capacity); +#endif + return j; + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /*! + @brief parser event types + + The parser callback distinguishes the following events: + - `object_start`: the parser read `{` and started to process a JSON object + - `key`: the parser read a key of a value in an object + - `object_end`: the parser read `}` and finished processing a JSON object + - `array_start`: the parser read `[` and started to process a JSON array + - `array_end`: the parser read `]` and finished processing a JSON array + - `value`: the parser finished reading a JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + @sa see @ref parser_callback_t for more information and examples + */ + using parse_event_t = detail::parse_event_t; + + /*! + @brief per-element parser callback type + + With a parser callback function, the result of parsing a JSON text can be + influenced. When passed to @ref parse, it is called on certain events + (passed as @ref parse_event_t via parameter @a event) with a set recursion + depth @a depth and context JSON value @a parsed. The return value of the + callback function is a boolean indicating whether the element that emitted + the callback shall be kept or not. + + We distinguish six scenarios (determined by the event type) in which the + callback function can be called. The following table describes the values + of the parameters @a depth, @a event, and @a parsed. + + parameter @a event | description | parameter @a depth | parameter @a parsed + ------------------ | ----------- | ------------------ | ------------------- + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded + parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key + parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded + parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array + parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + Discarding a value (i.e., returning `false`) has different effects + depending on the context in which function was called: + + - Discarded values in structured types are skipped. That is, the parser + will behave as if the discarded value was never read. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. + + @param[in] depth the depth of the recursion during parsing + + @param[in] event an event of type parse_event_t indicating the context in + the callback function has been called + + @param[in,out] parsed the current intermediate parse result; note that + writing to this value has no effect for parse_event_t::key events + + @return Whether the JSON value which called the function during parsing + should be kept (`true`) or not (`false`). In the latter case, it is either + skipped completely or replaced by an empty discarded object. + + @sa see @ref parse for examples + + @since version 1.0.0 + */ + using parser_callback_t = detail::parser_callback_t; + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /*! + @brief create an empty value with a given type + + Create an empty JSON value with a given type. The value will be default + initialized with an empty value which depends on the type: + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + binary | empty array + + @param[in] v the type of the value to create + + @complexity Constant. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows the constructor for different @ref + value_t values,basic_json__value_t} + + @sa see @ref clear() -- restores the postcondition of this constructor + + @since version 1.0.0 + */ + basic_json(const value_t v) + : m_type(v), m_value(v) + { + assert_invariant(); + } + + /*! + @brief create a null object + + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} + + @since version 1.0.0 + */ + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) + { + assert_invariant(); + } + + /*! + @brief create a JSON value + + This is a "catch all" constructor for all compatible JSON types; that is, + types for which a `to_json()` method exists. The constructor forwards the + parameter @a val to that method (to `json_serializer::to_json` method + with `U = uncvref_t`, to be exact). + + Template type @a CompatibleType includes, but is not limited to, the + following types: + - **arrays**: @ref array_t and all kinds of compatible containers such as + `std::vector`, `std::deque`, `std::list`, `std::forward_list`, + `std::array`, `std::valarray`, `std::set`, `std::unordered_set`, + `std::multiset`, and `std::unordered_multiset` with a `value_type` from + which a @ref basic_json value can be constructed. + - **objects**: @ref object_t and all kinds of compatible associative + containers such as `std::map`, `std::unordered_map`, `std::multimap`, + and `std::unordered_multimap` with a `key_type` compatible to + @ref string_t and a `value_type` from which a @ref basic_json value can + be constructed. + - **strings**: @ref string_t, string literals, and all compatible string + containers can be used. + - **numbers**: @ref number_integer_t, @ref number_unsigned_t, + @ref number_float_t, and all convertible number types such as `int`, + `size_t`, `int64_t`, `float` or `double` can be used. + - **boolean**: @ref boolean_t / `bool` can be used. + - **binary**: @ref binary_t / `std::vector` may be used, + unfortunately because string literals cannot be distinguished from binary + character arrays by the C++ type system, all types compatible with `const + char*` will be directed to the string constructor instead. This is both + for backwards compatibility, and due to the fact that a binary type is not + a standard JSON type. + + See the examples below. + + @tparam CompatibleType a type such that: + - @a CompatibleType is not derived from `std::istream`, + - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move + constructors), + - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments) + - @a CompatibleType is not a @ref basic_json nested type (e.g., + @ref json_pointer, @ref iterator, etc ...) + - `json_serializer` has a `to_json(basic_json_t&, CompatibleType&&)` method + + @tparam U = `uncvref_t` + + @param[in] val the value to be forwarded to the respective constructor + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @liveexample{The following code shows the constructor with several + compatible types.,basic_json__CompatibleType} + + @since version 2.1.0 + */ + template < typename CompatibleType, + typename U = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > + basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) + JSONSerializer::to_json(std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + set_parents(); + assert_invariant(); + } + + /*! + @brief create a JSON value from an existing one + + This is a constructor for existing @ref basic_json types. + It does not hijack copy/move constructors, since the parameter has different + template arguments than the current ones. + + The constructor tries to convert the internal @ref m_value of the parameter. + + @tparam BasicJsonType a type such that: + - @a BasicJsonType is a @ref basic_json type. + - @a BasicJsonType has different template arguments than @ref basic_json_t. + + @param[in] val the @ref basic_json value to be converted. + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value&& !std::is_same::value, int > = 0 > + basic_json(const BasicJsonType& val) + { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using other_string_t = typename BasicJsonType::string_t; + using other_object_t = typename BasicJsonType::object_t; + using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; + + switch (val.type()) + { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::binary: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_type = value_t::discarded; + break; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + set_parents(); + assert_invariant(); + } + + /*! + @brief create a container (array or object) from an initializer list + + Creates a JSON value of type array or object from the passed initializer + list @a init. In case @a type_deduction is `true` (default), the type of + the JSON value to be created is deducted from the initializer list @a init + according to the following rules: + + 1. If the list is empty, an empty JSON object value `{}` is created. + 2. If the list consists of pairs whose first element is a string, a JSON + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. + 3. In all other cases, an array is created. + + The rules aim to create the best fit between a C++ initializer list and + JSON values. The rationale is as follows: + + 1. The empty initializer list is written as `{}` which is exactly an empty + JSON object. + 2. C++ has no way of describing mapped types other than to list a list of + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. + 3. In all other cases, the initializer list could not be interpreted as + JSON object type, so interpreting it as JSON array type is safe. + + With the rules described above, the following JSON values cannot be + expressed by an initializer list: + + - the empty array (`[]`): use @ref array(initializer_list_t) + with an empty initializer list in this case + - arrays whose elements satisfy rule 2: use @ref + array(initializer_list_t) with the same initializer list + in this case + + @note When used without parentheses around an empty initializer list, @ref + basic_json() is called instead of this function, yielding the JSON null + value. + + @param[in] init initializer list with JSON values + + @param[in] type_deduction internal parameter; when set to `true`, the type + of the JSON value is deducted from the initializer list @a init; when set + to `false`, the type provided via @a manual_type is forced. This mode is + used by the functions @ref array(initializer_list_t) and + @ref object(initializer_list_t). + + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref + value_t::array and @ref value_t::object are valid); when @a type_deduction + is set to `true`, this parameter has no effect + + @throw type_error.301 if @a type_deduction is `false`, @a manual_type is + `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string. In this case, the constructor could not + create an object. If @a type_deduction would have be `true`, an array + would have been created. See @ref object(initializer_list_t) + for an example. + + @complexity Linear in the size of the initializer list @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows how JSON values are created from + initializer lists.,basic_json__list_init_t} + + @sa see @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + @sa see @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref& element_ref) + { + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); + }); + + // adjust type if type deduction is not wanted + if (!type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json())); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + for (auto& element_ref : init) + { + auto element = element_ref.moved_or_copied(); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + } + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init.begin(), init.end()); + } + + set_parents(); + assert_invariant(); + } + + /*! + @brief explicitly create a binary array (without subtype) + + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. + + @note Note, this function exists because of the difficulty in correctly + specifying the correct template overload in the standard value ctor, as both + JSON arrays and JSON binary arrays are backed with some form of a + `std::vector`. Because JSON binary arrays are a non-standard extension it + was decided that it would be best to prevent automatic initialization of a + binary array type, for backwards compatibility and so it does not happen on + accident. + + @param[in] init container containing bytes to use as binary type + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = init; + return res; + } + + /*! + @brief explicitly create a binary array (with subtype) + + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. + + @note Note, this function exists because of the difficulty in correctly + specifying the correct template overload in the standard value ctor, as both + JSON arrays and JSON binary arrays are backed with some form of a + `std::vector`. Because JSON binary arrays are a non-standard extension it + was decided that it would be best to prevent automatic initialization of a + binary array type, for backwards compatibility and so it does not happen on + accident. + + @param[in] init container containing bytes to use as binary type + @param[in] subtype subtype to use in MessagePack and BSON + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = std::move(init); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&, typename binary_t::subtype_type) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } + + /*! + @brief explicitly create an array from an initializer list + + Creates a JSON array value from a given initializer list. That is, given a + list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the + initializer list is empty, the empty array `[]` is created. + + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref + basic_json(initializer_list_t, bool, value_t)). These cases + are: + 1. creating an array whose elements are all pairs whose first element is a + string -- in this case, the initializer list constructor would create an + object, taking the first elements as keys + 2. creating an empty array -- passing the empty initializer list to the + initializer list constructor yields an empty object + + @param[in] init initializer list with JSON values to create an array from + (optional) + + @return JSON array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `array` + function.,array} + + @sa see @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa see @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /*! + @brief explicitly create an object from an initializer list + + Creates a JSON object value from a given initializer list. The initializer + lists elements must be pairs, and their first elements must be strings. If + the initializer list is empty, the empty object `{}` is created. + + @note This function is only added for symmetry reasons. In contrast to the + related function @ref array(initializer_list_t), there are + no cases which can only be expressed by this function. That is, any + initializer list @a init can also be passed to the initializer list + constructor @ref basic_json(initializer_list_t, bool, value_t). + + @param[in] init initializer list to create an object from (optional) + + @return JSON object value + + @throw type_error.301 if @a init is not a list of pairs whose first + elements are strings. In this case, no object can be created. When such a + value is passed to @ref basic_json(initializer_list_t, bool, value_t), + an array would have been created from the passed initializer list @a init. + See example below. + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `object` + function.,object} + + @sa see @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa see @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + + @since version 1.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /*! + @brief construct an array with count copies of given value + + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. + + @param[in] cnt the number of JSON copies of @a val to create + @param[in] val the JSON value to copy + + @post `std::distance(begin(),end()) == cnt` holds. + + @complexity Linear in @a cnt. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows examples for the @ref + basic_json(size_type\, const basic_json&) + constructor.,basic_json__size_type_basic_json} + + @since version 1.0.0 + */ + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + set_parents(); + assert_invariant(); + } + + /*! + @brief construct a JSON container given an iterator range + + Constructs the JSON value with the contents of the range `[first, last)`. + The semantics depends on the different types a JSON value can have: + - In case of a null type, invalid_iterator.206 is thrown. + - In case of other primitive types (number, boolean, or string), @a first + must be `begin()` and @a last must be `end()`. In this case, the value is + copied. Otherwise, invalid_iterator.204 is thrown. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector` or `std::map`; that is, a JSON array + or object is constructed from the values in the range. + + @tparam InputIT an input iterator type (@ref iterator or @ref + const_iterator) + + @param[in] first begin of the range to copy from (included) + @param[in] last end of the range to copy from (excluded) + + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion (see warning).** If + assertions are switched off, a violation of this precondition yields + undefined behavior. + + @pre Range `[first, last)` is valid. Usually, this precondition cannot be + checked efficiently. Only certain edge cases are detected; see the + description of the exceptions below. A violation of this precondition + yields undefined behavior. + + @warning A precondition is enforced with a runtime assertion that will + result in calling `std::abort` if this precondition is not met. + Assertions can be disabled by defining `NDEBUG` at compile time. + See https://en.cppreference.com/w/cpp/error/assert for more + information. + + @throw invalid_iterator.201 if iterators @a first and @a last are not + compatible (i.e., do not belong to the same JSON value). In this case, + the range `[first, last)` is undefined. + @throw invalid_iterator.204 if iterators @a first and @a last belong to a + primitive type (number, boolean, or string), but @a first does not point + to the first element any more. In this case, the range `[first, last)` is + undefined. See example code below. + @throw invalid_iterator.206 if iterators @a first and @a last belong to a + null value. In this case, the range `[first, last)` is undefined. + + @complexity Linear in distance between @a first and @a last. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows several ways to create JSON values by + specifying a subrange with iterators.,basic_json__InputIt_InputIt} + + @since version 1.0.0 + */ + template < class InputIT, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type = 0 > + basic_json(InputIT first, InputIT last) + { + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); + + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json())); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object)); + } + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::binary: + case value_t::discarded: + default: + break; + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::binary: + { + m_value = *first.m_object->m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object)); + } + + set_parents(); + assert_invariant(); + } + + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + template, + std::is_same>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} + + /*! + @brief copy constructor + + Creates a copy of a given JSON value. + + @param[in] other the JSON value to copy + + @post `*this == other` + + @complexity Linear in the size of @a other. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + - As postcondition, it holds: `other == basic_json(other)`. + + @liveexample{The following code shows an example for the copy + constructor.,basic_json__basic_json} + + @since version 1.0.0 + */ + basic_json(const basic_json& other) + : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + case value_t::binary: + { + m_value = *other.m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + + set_parents(); + assert_invariant(); + } + + /*! + @brief move constructor + + Move constructor. Constructs a JSON value with the contents of the given + value @a other using move semantics. It "steals" the resources from @a + other and leaves it as JSON null value. + + @param[in,out] other value to move to this object + + @post `*this` has the same value as @a other before the call. + @post @a other is a JSON null value. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @requirement This function helps `basic_json` satisfying the + [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible) + requirements. + + @liveexample{The code below shows the move constructor explicitly called + via std::move.,basic_json__moveconstructor} + + @since version 1.0.0 + */ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(false); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + set_parents(); + assert_invariant(); + } + + /*! + @brief copy assignment + + Copy assignment operator. Copies a JSON value via the "copy and swap" + strategy: It is expressed in terms of the copy constructor, destructor, + and the `swap()` member function. + + @param[in] other value to copy from + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + + @liveexample{The code below shows and example for the copy assignment. It + creates a copy of value `a` which is then swapped with `b`. Finally\, the + copy of `a` (which is the null value after the swap) is + destroyed.,basic_json__copyassignment} + + @since version 1.0.0 + */ + basic_json& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + set_parents(); + assert_invariant(); + return *this; + } + + /*! + @brief destructor + + Destroys the JSON value and frees all allocated memory. + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory is freed. + + @since version 1.0.0 + */ + ~basic_json() noexcept + { + assert_invariant(false); + m_value.destroy(m_type); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /*! + @brief serialization + + Serialization function for JSON values. The function tries to mimic + Python's `json.dumps()` function, and currently supports its @a indent + and @a ensure_ascii parameters. + + @param[in] indent If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. + @param[in] indent_char The character to use for indentation if @a indent is + greater than `0`. The default is ` ` (space). + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] error_handler how to react on decoding errors; there are three + possible values: `strict` (throws and exception in case a decoding error + occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), + and `ignore` (ignore invalid UTF-8 sequences during serialization; all + bytes are copied to the output unchanged). + + @return string containing the serialization of the JSON value + + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded and @a error_handler is set to strict + + @note Binary values are serialized as object containing two keys: + - "bytes": an array of bytes as integers + - "subtype": the subtype as integer or "null" if the binary has no subtype + + @complexity Linear. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @liveexample{The following example shows the effect of different @a indent\, + @a indent_char\, and @a ensure_ascii parameters to the result of the + serialization.,dump} + + @see https://docs.python.org/2/library/json.html#json.dump + + @since version 1.0.0; indentation character @a indent_char, option + @a ensure_ascii and exceptions added in version 3.0.0; error + handlers added in version 3.4.0; serialization of binary values added + in version 3.8.0. + */ + string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const + { + string_t result; + serializer s(detail::output_adapter(result), indent_char, error_handler); + + if (indent >= 0) + { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /*! + @brief return the type of the JSON value (explicit) + + Return the type of the JSON value as a value from the @ref value_t + enumeration. + + @return the type of the JSON value + Value type | return value + ------------------------- | ------------------------- + null | value_t::null + boolean | value_t::boolean + string | value_t::string + number (integer) | value_t::number_integer + number (unsigned integer) | value_t::number_unsigned + number (floating-point) | value_t::number_float + object | value_t::object + array | value_t::array + binary | value_t::binary + discarded | value_t::discarded + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON + types.,type} + + @sa see @ref operator value_t() -- return the type of the JSON value (implicit) + @sa see @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr value_t type() const noexcept + { + return m_type; + } + + /*! + @brief return whether type is primitive + + This function returns true if and only if the JSON type is primitive + (string, number, boolean, or null). + + @return `true` if type is primitive (string, number, boolean, or null), + `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_primitive()` for all JSON + types.,is_primitive} + + @sa see @ref is_structured() -- returns whether JSON value is structured + @sa see @ref is_null() -- returns whether JSON value is `null` + @sa see @ref is_string() -- returns whether JSON value is a string + @sa see @ref is_boolean() -- returns whether JSON value is a boolean + @sa see @ref is_number() -- returns whether JSON value is a number + @sa see @ref is_binary() -- returns whether JSON value is a binary array + + @since version 1.0.0 + */ + constexpr bool is_primitive() const noexcept + { + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); + } + + /*! + @brief return whether type is structured + + This function returns true if and only if the JSON type is structured + (array or object). + + @return `true` if type is structured (array or object), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_structured()` for all JSON + types.,is_structured} + + @sa see @ref is_primitive() -- returns whether value is primitive + @sa see @ref is_array() -- returns whether value is an array + @sa see @ref is_object() -- returns whether value is an object + + @since version 1.0.0 + */ + constexpr bool is_structured() const noexcept + { + return is_array() || is_object(); + } + + /*! + @brief return whether value is null + + This function returns true if and only if the JSON value is null. + + @return `true` if type is null, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_null()` for all JSON + types.,is_null} + + @since version 1.0.0 + */ + constexpr bool is_null() const noexcept + { + return m_type == value_t::null; + } + + /*! + @brief return whether value is a boolean + + This function returns true if and only if the JSON value is a boolean. + + @return `true` if type is boolean, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON + types.,is_boolean} + + @since version 1.0.0 + */ + constexpr bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + /*! + @brief return whether value is a number + + This function returns true if and only if the JSON value is a number. This + includes both integer (signed and unsigned) and floating-point values. + + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number()` for all JSON + types.,is_number} + + @sa see @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa see @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa see @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number() const noexcept + { + return is_number_integer() || is_number_float(); + } + + /*! + @brief return whether value is an integer number + + This function returns true if and only if the JSON value is a signed or + unsigned integer number. This excludes floating-point values. + + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_integer()` for all + JSON types.,is_number_integer} + + @sa see @ref is_number() -- check if value is a number + @sa see @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa see @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer || m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is an unsigned integer number + + This function returns true if and only if the JSON value is an unsigned + integer number. This excludes floating-point and signed integer values. + + @return `true` if type is an unsigned integer number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} + + @sa see @ref is_number() -- check if value is a number + @sa see @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa see @ref is_number_float() -- check if value is a floating-point number + + @since version 2.0.0 + */ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is a floating-point number + + This function returns true if and only if the JSON value is a + floating-point number. This excludes signed and unsigned integer values. + + @return `true` if type is a floating-point number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_float()` for all + JSON types.,is_number_float} + + @sa see @ref is_number() -- check if value is number + @sa see @ref is_number_integer() -- check if value is an integer number + @sa see @ref is_number_unsigned() -- check if value is an unsigned integer + number + + @since version 1.0.0 + */ + constexpr bool is_number_float() const noexcept + { + return m_type == value_t::number_float; + } + + /*! + @brief return whether value is an object + + This function returns true if and only if the JSON value is an object. + + @return `true` if type is object, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON + types.,is_object} + + @since version 1.0.0 + */ + constexpr bool is_object() const noexcept + { + return m_type == value_t::object; + } + + /*! + @brief return whether value is an array + + This function returns true if and only if the JSON value is an array. + + @return `true` if type is array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_array()` for all JSON + types.,is_array} + + @since version 1.0.0 + */ + constexpr bool is_array() const noexcept + { + return m_type == value_t::array; + } + + /*! + @brief return whether value is a string + + This function returns true if and only if the JSON value is a string. + + @return `true` if type is string, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON + types.,is_string} + + @since version 1.0.0 + */ + constexpr bool is_string() const noexcept + { + return m_type == value_t::string; + } + + /*! + @brief return whether value is a binary array + + This function returns true if and only if the JSON value is a binary array. + + @return `true` if type is binary array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_binary()` for all JSON + types.,is_binary} + + @since version 3.8.0 + */ + constexpr bool is_binary() const noexcept + { + return m_type == value_t::binary; + } + + /*! + @brief return whether value is discarded + + This function returns true if and only if the JSON value was discarded + during parsing with a callback function (see @ref parser_callback_t). + + @note This function will always be `false` for JSON values after parsing. + That is, discarded values can only occur during parsing, but will be + removed when inside a structured value or replaced by null in other cases. + + @return `true` if type is discarded, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_discarded()` for all JSON + types.,is_discarded} + + @since version 1.0.0 + */ + constexpr bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } + + /*! + @brief return the type of the JSON value (implicit) + + Implicitly return the type of the JSON value as a value from the @ref + value_t enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} + + @sa see @ref type() -- return the type of the JSON value (explicit) + @sa see @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr operator value_t() const noexcept + { + return m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (JSON_HEDLEY_LIKELY(is_boolean())) + { + return m_value.boolean; + } + + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this)); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto* ptr = obj.template get_ptr::type>(); + + if (JSON_HEDLEY_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj)); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /*! + @brief get a pointer value (implicit) + + Implicit pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + private: + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::is_default_constructible::value&& + detail::has_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + ValueType ret{}; + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + return JSONSerializer::from_json(*this); + } + + /*! + @brief get special-case overload + + This overloads converts the current @ref basic_json in a different + @ref basic_json type + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this, converted into @a BasicJsonType + + @complexity Depending on the implementation of the called `from_json()` + method. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value, + int > = 0 > + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const + { + return *this; + } + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, + int> = 0> + basic_json get_impl(detail::priority_tag<3> /*unused*/) const + { + return *this; + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, + int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + public: + /*! + @brief get a (pointer) value (explicit) + + Performs explicit type conversion between the JSON value and a compatible value if required. + + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. + + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. + + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @tparam ValueType if necessary + + @throw what @ref json_serializer `from_json()` method throws if conversion is required + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> +#if defined(JSON_HAS_CPP_14) + constexpr +#endif + auto get() const noexcept( + noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) + -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa see @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + The value is filled into the input parameter by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType v; + JSONSerializer::from_json(*this, v); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + + @tparam ValueType the input parameter type. + + @return the input parameter, allowing chaining calls. + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get_to} + + @since version 3.3.0 + */ + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + /*! + @brief get a reference value (implicit) + + Implicit reference access to the internally stored JSON value. No copies + are made. + + @warning Writing data to the referee of the result yields an undefined + state. + + @tparam ReferenceType reference type; must be a reference to @ref array_t, + @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or + @ref number_float_t. Enforced by static assertion. + + @return reference to the internally stored JSON value if the requested + reference type @a ReferenceType fits to the JSON value; throws + type_error.303 otherwise + + @throw type_error.303 in case passed type @a ReferenceType is incompatible + with the stored JSON value; see example below + + @complexity Constant. + + @liveexample{The example shows several calls to `get_ref()`.,get_ref} + + @since version 1.1.0 + */ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template < typename ReferenceType, typename std::enable_if < + std::is_reference::value&& + std::is_const::type>::value, int >::type = 0 > + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + detail::conjunction < + detail::negation>, + detail::negation>>, + detail::negation>, + detail::negation>, + detail::negation>>, + +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + detail::negation>, +#endif + detail::is_detected_lazy + >::value, int >::type = 0 > + JSON_EXPLICIT operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /*! + @return reference to the binary value + + @throw type_error.302 if the value is not binary + + @sa see @ref is_binary() to check if the value is binary + + @since version 3.8.0 + */ + binary_t& get_binary() + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr(); + } + + /// @copydoc get_binary() + const binary_t& get_binary() const + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr(); + } + + /// @} + + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /*! + @brief access specified array element with bounds checking + + Returns a reference to the element at specified location @a idx, with + bounds checking. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__size_type} + */ + reference at(size_type idx) + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return set_parent(m_value.array->at(idx)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief access specified array element with bounds checking + + Returns a const reference to the element at specified location @a idx, + with bounds checking. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__size_type_const} + */ + const_reference at(size_type idx) const + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a reference to the element at with specified key @a key, with + bounds checking. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa see @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa see @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__object_t_key_type} + */ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return set_parent(m_value.object->at(key)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a const reference to the element at with specified key @a key, + with bounds checking. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa see @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa see @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__object_t_key_type_const} + */ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief access specified array element + + Returns a reference to the element at specified location @a idx. + + @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), + then the array is silently filled up with `null` values to make `idx` a + valid reference to the last stored element. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array or null; in that + cases, using the [] operator with an index makes no sense. + + @complexity Constant if @a idx is in the range of the array. Otherwise + linear in `idx - size()`. + + @liveexample{The example below shows how array elements can be read and + written using `[]` operator. Note the addition of `null` + values.,operatorarray__size_type} + + @since version 1.0.0 + */ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { +#if JSON_DIAGNOSTICS + // remember array size before resizing + const auto previous_size = m_value.array->size(); +#endif + m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + // set parent for values added above + set_parents(begin() + static_cast(previous_size), static_cast(idx + 1 - previous_size)); +#endif + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + } + + /*! + @brief access specified array element + + Returns a const reference to the element at specified location @a idx. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array; in that case, + using the [] operator with an index makes no sense. + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read using + the `[]` operator.,operatorarray__size_type_const} + + @since version 1.0.0 + */ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref value() for access by value with a default value + + @since version 1.0.0 + */ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return set_parent(m_value.object->operator[](key)); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref value() for access by value with a default value + + @since version 1.0.0 + */ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return set_parent(m_value.object->operator[](key)); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + const_reference operator[](T* key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /*! + @brief access specified object element with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(key); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const typename object_t::key_type&), this function + does not throw if the given key @a key was not found. + + @note Unlike @ref operator[](const typename object_t::key_type& key), this + function does not implicitly add an element to the position defined by @a + key. This function is furthermore also applicable to const objects. + + @param[in] key key of the element to access + @param[in] default_value the value to return if @a key is not found + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.302 if @a default_value does not match the type of the + value at @a key + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value} + + @sa see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + + @since version 1.0.0 + */ + // using std::is_convertible in a std::enable_if will fail when using explicit conversions + template < class ValueType, typename std::enable_if < + detail::is_getable::value + && !std::is_same::value, int >::type = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const + */ + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /*! + @brief access specified object element via JSON Pointer with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. + + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.302 if @a default_value does not match the type of the + value at @a ptr + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa see @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 + */ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return default_value; + } + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + JSON_HEDLEY_NON_NULL(3) + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /*! + @brief access the first element + + Returns a reference to the first element in the container. For a JSON + container `c`, the expression `c.front()` is equivalent to `*c.begin()`. + + @return In case of a structured type (array or object), a reference to the + first element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on `null` value + + @liveexample{The following code shows an example for `front()`.,front} + + @sa see @ref back() -- access the last element + + @since version 1.0.0 + */ + reference front() + { + return *begin(); + } + + /*! + @copydoc basic_json::front() + */ + const_reference front() const + { + return *cbegin(); + } + + /*! + @brief access the last element + + Returns a reference to the last element in the container. For a JSON + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode + + @return In case of a structured type (array or object), a reference to the + last element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on a `null` value. See example + below. + + @liveexample{The following code shows an example for `back()`.,back} + + @sa see @ref front() -- access the first element + + @since version 1.0.0 + */ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /*! + @copydoc basic_json::back() + */ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /*! + @brief remove element given an iterator + + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] pos iterator to the element to remove + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.202 if called on an iterator which does not belong + to the current JSON value; example: `"iterator does not fit current + value"` + @throw invalid_iterator.205 if called on a primitive type with invalid + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` + + @complexity The complexity depends on the type: + - objects: amortized constant + - arrays: linear in distance between @a pos and the end of the container + - strings and binary: linear in the length of the member + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType} + + @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa see @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa see @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + return result; + } + + /*! + @brief remove elements given an iterator range + + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] first iterator to the beginning of the range to remove + @param[in] last iterator past the end of the range to remove + @return Iterator following the last removed element. If the iterator @a + second refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.203 if called on iterators which does not belong + to the current JSON value; example: `"iterators do not fit current value"` + @throw invalid_iterator.204 if called on a primitive type with invalid + iterators (i.e., if `first != begin()` and `last != end()`); example: + `"iterators out of range"` + + @complexity The complexity depends on the type: + - objects: `log(size()) + std::distance(first, last)` + - arrays: linear in the distance between @a first and @a last, plus linear + in the distance between @a last and end of the container + - strings and binary: linear in the length of the member + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType_IteratorType} + + @sa see @ref erase(IteratorType) -- removes the element at a given position + @sa see @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa see @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) + { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this)); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + return result; + } + + /*! + @brief remove element from a JSON object given a key + + Removes elements from a JSON object with the key value @a key. + + @param[in] key value of the elements to remove + + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). + + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + + @complexity `log(size()) + count(key)` + + @liveexample{The example shows the effect of `erase()`.,erase__key_type} + + @sa see @ref erase(IteratorType) -- removes the element at a given position + @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa see @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->erase(key); + } + + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + /*! + @brief remove element from a JSON array given an index + + Removes element from a JSON array at the index @a idx. + + @param[in] idx index of the element to remove + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 + is out of range"` + + @complexity Linear in distance between @a idx and the end of the container. + + @liveexample{The example shows the effect of `erase()`.,erase__size_type} + + @sa see @ref erase(IteratorType) -- removes the element at a given position + @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa see @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + + @since version 1.0.0 + */ + void erase(const size_type idx) + { + // this erase only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + if (JSON_HEDLEY_UNLIKELY(idx >= size())) + { + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + } + + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /*! + @brief find an element in a JSON object + + Finds an element in a JSON object with key equivalent to @a key. If the + element is not found or the JSON value is not an object, end() is + returned. + + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + + @param[in] key key value of the element to search for. + + @return Iterator to an element with key equivalent to @a key. If no such + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `find()` is used.,find__key_type} + + @sa see @ref contains(KeyT&&) const -- checks whether a key exists + + @since version 1.0.0 + */ + template + iterator find(KeyT&& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief find an element in a JSON object + @copydoc find(KeyT&&) + */ + template + const_iterator find(KeyT&& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief returns the number of occurrences of a key in a JSON object + + Returns the number of elements with key @a key. If ObjectType is the + default `std::map` type, the return value will always be `0` (@a key was + not found) or `1` (@a key was found). + + @note This method always returns `0` when executed on a JSON type that is + not an object. + + @param[in] key key value of the element to count + + @return Number of elements with key @a key. If the JSON value is not an + object, the return value will be `0`. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `count()` is used.,count} + + @since version 1.0.0 + */ + template + size_type count(KeyT&& key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /*! + @brief check the existence of an element in a JSON object + + Check whether an element exists in a JSON object with key equivalent to + @a key. If the element is not found or the JSON value is not an object, + false is returned. + + @note This method always returns false when executed on a JSON type + that is not an object. + + @param[in] key key value to check its existence. + + @return true if an element with specified @a key exists. If no such + element with such key is found or the JSON value is not an object, + false is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The following code shows an example for `contains()`.,contains} + + @sa see @ref find(KeyT&&) -- returns an iterator to an object element + @sa see @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer + + @since version 3.6.0 + */ + template < typename KeyT, typename std::enable_if < + !std::is_same::type, json_pointer>::value, int >::type = 0 > + bool contains(KeyT && key) const + { + return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + } + + /*! + @brief check the existence of an element in a JSON object given a JSON pointer + + Check whether the given JSON pointer @a ptr can be resolved in the current + JSON value. + + @note This method can be executed on any JSON value type. + + @param[in] ptr JSON pointer to check its existence. + + @return true if the JSON pointer can be resolved to a stored value, false + otherwise. + + @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The following code shows an example for `contains()`.,contains_json_pointer} + + @sa see @ref contains(KeyT &&) const -- checks the existence of a key + + @since version 3.7.0 + */ + bool contains(const json_pointer& ptr) const + { + return ptr.contains(this); + } + + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /*! + @brief returns an iterator to the first element + + Returns an iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa see @ref cbegin() -- returns a const iterator to the beginning + @sa see @ref end() -- returns an iterator to the end + @sa see @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /*! + @copydoc basic_json::cbegin() + */ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /*! + @brief returns a const iterator to the first element + + Returns a const iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).begin()`. + + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa see @ref begin() -- returns an iterator to the beginning + @sa see @ref end() -- returns an iterator to the end + @sa see @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /*! + @brief returns an iterator to one past the last element + + Returns an iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `end()`.,end} + + @sa see @ref cend() -- returns a const iterator to the end + @sa see @ref begin() -- returns an iterator to the beginning + @sa see @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /*! + @copydoc basic_json::cend() + */ + const_iterator end() const noexcept + { + return cend(); + } + + /*! + @brief returns a const iterator to one past the last element + + Returns a const iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).end()`. + + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa see @ref end() -- returns an iterator to the end + @sa see @ref begin() -- returns an iterator to the beginning + @sa see @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /*! + @brief returns an iterator to the reverse-beginning + + Returns an iterator to the reverse-beginning; that is, the last element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(end())`. + + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa see @ref crbegin() -- returns a const reverse iterator to the beginning + @sa see @ref rend() -- returns a reverse iterator to the end + @sa see @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /*! + @copydoc basic_json::crbegin() + */ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /*! + @brief returns an iterator to the reverse-end + + Returns an iterator to the reverse-end; that is, one before the first + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(begin())`. + + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa see @ref crend() -- returns a const reverse iterator to the end + @sa see @ref rbegin() -- returns a reverse iterator to the beginning + @sa see @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /*! + @copydoc basic_json::crend() + */ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /*! + @brief returns a const reverse iterator to the last element + + Returns a const iterator to the reverse-beginning; that is, the last + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rbegin()`. + + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa see @ref rbegin() -- returns a reverse iterator to the beginning + @sa see @ref rend() -- returns a reverse iterator to the end + @sa see @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /*! + @brief returns a const reverse iterator to one before the first + + Returns a const reverse iterator to the reverse-end; that is, one before + the first element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rend()`. + + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa see @ref rend() -- returns a reverse iterator to the end + @sa see @ref rbegin() -- returns a reverse iterator to the beginning + @sa see @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + public: + /*! + @brief wrapper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without iterator_wrapper: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without iterator proxy: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with iterator proxy: + + @code{cpp} + for (auto it : json::iterator_wrapper(j_object)) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). + + @param[in] ref reference to a JSON value + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @note The name of this function is not yet final and may change in the + future. + + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use @ref items() instead; + that is, replace `json::iterator_wrapper(j)` with `j.items()`. + */ + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /*! + @copydoc iterator_wrapper(reference) + */ + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /*! + @brief helper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without `items()` function: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without `items()` function: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with `items()` function: + + @code{cpp} + for (auto& el : j_object.items()) + { + std::cout << "key: " << el.key() << ", value:" << el.value() << '\n'; + } + @endcode + + The `items()` function also allows to use + [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding) + (C++17): + + @code{cpp} + for (auto& [key, val] : j_object.items()) + { + std::cout << "key: " << key << ", value:" << val << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). For primitive types (e.g., numbers), + `key()` returns an empty string. + + @warning Using `items()` on temporary objects is dangerous. Make sure the + object's lifetime exeeds the iteration. See + for more + information. + + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the function is used.,items} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 3.1.0, structured bindings support since 3.5.0. + */ + iteration_proxy items() noexcept + { + return iteration_proxy(*this); + } + + /*! + @copydoc items() + */ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /*! + @brief checks whether the container is empty. + + Checks if a JSON value has no elements (i.e. whether its @ref size is `0`). + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `true` + boolean | `false` + string | `false` + number | `false` + binary | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` + + @liveexample{The following code uses `empty()` to check if a JSON + object contains any elements.,empty} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `begin() == end()`. + + @sa see @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types are nonempty + return false; + } + } + } + + /*! + @brief returns the number of elements + + Returns the number of elements in a JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` + boolean | `1` + string | `1` + number | `1` + binary | `1` + object | result of function object_t::size() + array | result of function array_t::size() + + @liveexample{The following code calls `size()` on the different value + types.,size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `std::distance(begin(), end())`. + + @sa see @ref empty() -- checks whether the container is empty + @sa see @ref max_size() -- returns the maximal number of elements + + @since version 1.0.0 + */ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have size 1 + return 1; + } + } + } + + /*! + @brief returns the maximum possible number of elements + + Returns the maximum number of elements a JSON value is able to hold due to + system or library implementation limitations, i.e. `std::distance(begin(), + end())` for the JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + binary | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` + + @liveexample{The following code calls `max_size()` on the different value + types. Note the output is implementation specific.,max_size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of returning `b.size()` where `b` is the largest + possible JSON value. + + @sa see @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /*! + @brief clears the contents + + Clears the content of a JSON value and resets it to the default value as + if @ref basic_json(value_t) would have been called with the current value + type from @ref type(): + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + binary | An empty byte vector + object | `{}` + array | `[]` + + @post Has the same effect as calling + @code {.cpp} + *this = basic_json(type()); + @endcode + + @liveexample{The example below shows the effect of `clear()` to different + JSON types.,clear} + + @complexity Linear in the size of the JSON value. + + @iterators All iterators, pointers and references related to this container + are invalidated. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @sa see @ref basic_json(value_t) -- constructor that creates an object with the + same value than calling `clear()` + + @since version 1.0.0 + */ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_value.boolean = false; + break; + } + + case value_t::string: + { + m_value.string->clear(); + break; + } + + case value_t::binary: + { + m_value.binary->clear(); + break; + } + + case value_t::array: + { + m_value.array->clear(); + break; + } + + case value_t::object: + { + m_value.object->clear(); + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + } + + /*! + @brief add an object to an array + + Appends the given element @a val to the end of the JSON value. If the + function is called on a JSON null value, an empty array is created before + appending @a val. + + @param[in] val the value to add to the JSON array + + @throw type_error.308 when called on a type other than JSON array or + null; example: `"cannot use push_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} + + @since version 1.0.0 + */ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(std::move(val)); + set_parent(m_value.array->back(), old_capacity); + // if val is moved from, basic_json move constructor marks it null so we do not call the destructor + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(val); + set_parent(m_value.array->back(), old_capacity); + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + Inserts the given element @a val to the JSON object. If the function is + called on a JSON null value, an empty object is created before inserting + @a val. + + @param[in] val the value to add to the JSON object + + @throw type_error.308 when called on a type other than JSON object or + null; example: `"cannot use push_back() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} + + @since version 1.0.0 + */ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to object + auto res = m_value.object->insert(val); + set_parent(res.first->second); + } + + /*! + @brief add an object to an object + @copydoc push_back(const typename object_t::value_type&) + */ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + This function allows to use `push_back` with an initializer list. In case + + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, + + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). + + @param[in] init an initializer list + + @complexity Linear in the size of the initializer list @a init. + + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. + + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(initializer_list_t init) + { + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /*! + @brief add an object to an object + @copydoc push_back(initializer_list_t) + */ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return reference to the inserted element + + @throw type_error.311 when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8, returns reference since 3.7.0 + */ + template + reference emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + const auto old_capacity = m_value.array->capacity(); + m_value.array->emplace_back(std::forward(args)...); + return set_parent(m_value.array->back(), old_capacity); + } + + /*! + @brief add an object to an object if key does not exist + + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. + + @throw type_error.311 when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} + + @since version 2.0.8 + */ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this)); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + set_parent(res.first->second); + + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /// Helper for insertion of an iterator + /// @note: This uses std::distance to support GCC 4.8, + /// see https://github.com/nlohmann/json/pull/1257 + template + iterator insert_iterator(const_iterator pos, Args&& ... args) + { + iterator result(this); + JSON_ASSERT(m_value.array != nullptr); + + auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); + m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); + result.m_it.array_iterator = m_value.array->begin() + insert_pos; + + // This could have been written as: + // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + + set_parents(); + return result; + } + + /*! + @brief inserts element + + Inserts element @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. + + @throw type_error.309 if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Constant plus linear in the distance between @a pos and end of + the container. + + @liveexample{The example shows how `insert()` is used.,insert} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /*! + @brief inserts elements + + Inserts @a cnt copies of @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] cnt number of copies of @a val to insert + @param[in] val element to insert + @return iterator pointing to the first element inserted, or @a pos if + `cnt==0` + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Linear in @a cnt plus linear in the distance between @a pos + and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__count} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, cnt, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + @throw invalid_iterator.211 if @a first or @a last are iterators into + container for which insert is called; example: `"passed iterators may not + belong to container"` + + @return iterator pointing to the first element inserted, or @a pos if + `first==last` + + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__range} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) + { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + } + + /*! + @brief inserts elements + + Inserts elements from initializer list @a ilist before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] ilist initializer list to insert the values from + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @return iterator pointing to the first element inserted, or @a pos if + `ilist` is empty + + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__ilist} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, initializer_list_t ilist) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, ilist.begin(), ilist.end()); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)`. + + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than objects; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + + @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number + of elements to insert. + + @liveexample{The example shows how `insert()` is used.,insert__range_object} + + @since version 3.0.0 + */ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); + } + + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + /*! + @brief updates a JSON object from another object, overwriting existing keys + + Inserts all values from JSON object @a j and overwrites existing keys. + + @param[in] j JSON object to read values from + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 + */ + void update(const_reference j) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); + } + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()), *this)); + } + + for (auto it = j.cbegin(); it != j.cend(); ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } + } + + /*! + @brief updates a JSON object from another object, overwriting existing keys + + Inserts all values from from range `[first, last)` and overwrites existing + keys. + + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used__range.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 + */ + void update(const_iterator first, const_iterator last) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() + || !last.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); + } + + for (auto it = first; it != last; ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + + set_parents(); + other.set_parents(); + assert_invariant(); + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value from @a left with those of @a right. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. implemented as a friend function callable via ADL. + + @param[in,out] left JSON value to exchange the contents with + @param[in,out] right JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON array with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other array to exchange the contents with + + @throw type_error.310 when JSON value is not an array; example: `"cannot + use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} + + @since version 1.0.0 + */ + void swap(array_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + std::swap(*(m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON object with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other object to exchange the contents with + + @throw type_error.310 when JSON value is not an object; example: + `"cannot use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} + + @since version 1.0.0 + */ + void swap(object_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + std::swap(*(m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other string to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} + + @since version 1.0.0 + */ + void swap(string_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_string())) + { + std::swap(*(m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other binary to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__binary_t} + + @since version 3.8.0 + */ + void swap(binary_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @copydoc swap(binary_t&) + void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @} + + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + /*! + @brief comparison: equal + + Compares two JSON values for equality according to the following rules: + - Two JSON values are equal if (1) they are from the same type and (2) + their stored values are the same according to their respective + `operator==`. + - Integer and floating-point numbers are automatically converted before + comparison. Note that two NaN values are always treated as unequal. + - Two JSON null values are equal. + + @note Floating-point inside JSON values numbers are compared with + `json::number_float_t::operator==` which is `double::operator==` by + default. To compare floating-point while respecting an epsilon, an alternative + [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39) + could be used, for instance + @code {.cpp} + template::value, T>::type> + inline bool is_same(T a, T b, T epsilon = std::numeric_limits::epsilon()) noexcept + { + return std::abs(a - b) <= epsilon; + } + @endcode + Or you can self-defined operator equal function like this: + @code {.cpp} + bool my_equal(const_reference lhs, const_reference rhs) { + const auto lhs_type lhs.type(); + const auto rhs_type rhs.type(); + if (lhs_type == rhs_type) { + switch(lhs_type) + // self_defined case + case value_t::number_float: + return std::abs(lhs - rhs) <= std::numeric_limits::epsilon(); + // other cases remain the same with the original + ... + } + ... + } + @endcode + + @note NaN values never compare equal to themselves or to other NaN values. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are equal + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__equal} + + @since version 1.0.0 + */ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + return *lhs.m_value.array == *rhs.m_value.array; + + case value_t::object: + return *lhs.m_value.object == *rhs.m_value.object; + + case value_t::null: + return true; + + case value_t::string: + return *lhs.m_value.string == *rhs.m_value.string; + + case value_t::boolean: + return lhs.m_value.boolean == rhs.m_value.boolean; + + case value_t::number_integer: + return lhs.m_value.number_integer == rhs.m_value.number_integer; + + case value_t::number_unsigned: + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + + case value_t::number_float: + return lhs.m_value.number_float == rhs.m_value.number_float; + + case value_t::binary: + return *lhs.m_value.binary == *rhs.m_value.binary; + + case value_t::discarded: + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + + return false; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, ScalarType rhs) noexcept + { + return lhs == basic_json(rhs); + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) == rhs; + } + + /*! + @brief comparison: not equal + + Compares two JSON values for inequality by calculating `not (lhs == rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are not equal + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__notequal} + + @since version 1.0.0 + */ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs == rhs); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs != basic_json(rhs); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) != rhs; + } + + /*! + @brief comparison: less than + + Compares whether one JSON value @a lhs is less than another JSON value @a + rhs according to the following rules: + - If @a lhs and @a rhs have the same type, the values are compared using + the default `<` operator. + - Integer and floating-point numbers are automatically converted before + comparison + - In case @a lhs and @a rhs have different types, the values are ignored + and the order of the types is considered, see + @ref operator<(const value_t, const value_t). + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__less} + + @since version 1.0.0 + */ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + // note parentheses are necessary, see + // https://github.com/nlohmann/json/issues/1530 + return (*lhs.m_value.array) < (*rhs.m_value.array); + + case value_t::object: + return (*lhs.m_value.object) < (*rhs.m_value.object); + + case value_t::null: + return false; + + case value_t::string: + return (*lhs.m_value.string) < (*rhs.m_value.string); + + case value_t::boolean: + return (lhs.m_value.boolean) < (rhs.m_value.boolean); + + case value_t::number_integer: + return (lhs.m_value.number_integer) < (rhs.m_value.number_integer); + + case value_t::number_unsigned: + return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned); + + case value_t::number_float: + return (lhs.m_value.number_float) < (rhs.m_value.number_float); + + case value_t::binary: + return (*lhs.m_value.binary) < (*rhs.m_value.binary); + + case value_t::discarded: + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } + + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, ScalarType rhs) noexcept + { + return lhs < basic_json(rhs); + } + + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) < rhs; + } + + /*! + @brief comparison: less than or equal + + Compares whether one JSON value @a lhs is less than or equal to another + JSON value by calculating `not (rhs < lhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than or equal to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greater} + + @since version 1.0.0 + */ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return !(rhs < lhs); + } + + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs <= basic_json(rhs); + } + + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) <= rhs; + } + + /*! + @brief comparison: greater than + + Compares whether one JSON value @a lhs is greater than another + JSON value by calculating `not (lhs <= rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__lessequal} + + @since version 1.0.0 + */ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs <= rhs); + } + + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, ScalarType rhs) noexcept + { + return lhs > basic_json(rhs); + } + + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) > rhs; + } + + /*! + @brief comparison: greater than or equal + + Compares whether one JSON value @a lhs is greater than or equal to another + JSON value by calculating `not (lhs < rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than or equal to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greaterequal} + + @since version 1.0.0 + */ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs < rhs); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs >= basic_json(rhs); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) >= rhs; + } + + /// @} + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ +#ifndef JSON_NO_IO + /*! + @brief serialize to stream + + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. + + - The indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. + + - The indentation character can be controlled with the member variable + `fill` of the output stream @a o. For instance, the manipulator + `std::setfill('\\t')` sets indentation to use a tab character rather than + the default space character. + + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize + + @return the stream @a o + + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded + + @complexity Linear. + + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} + + @since version 1.0.0; indentation character added in version 3.0.0 + */ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + /*! + @brief serialize to stream + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use + @ref operator<<(std::ostream&, const basic_json&) + instead; that is, replace calls like `j >> o;` with `o << j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } +#endif // JSON_NO_IO + /// @} + + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /*! + @brief deserialize from a compatible input + + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb or reading from the input @a i has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to + ignore comments. + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /*! + @brief deserialize from a pair of character iterators + + The value_type of the iterator must be a integral type with size of 1, 2 or + 4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32. + + @param[in] first iterator to start of character range + @param[in] last iterator to end of character range + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /*! + @brief check if the input is valid JSON + + Unlike the @ref parse(InputType&&, const parser_callback_t,const bool) + function, this function neither throws an exception in case of invalid JSON + input (i.e., a parse error) nor creates diagnostic information. + + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) + + @return Whether the input read from @a i is valid JSON. + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `accept()` function reading + from a string.,accept__string} + */ + template + static bool accept(InputType&& i, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); + } + + template + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) + { + return parser(i.get(), nullptr, false, ignore_comments).accept(true); + } + + /*! + @brief generate SAX events + + The SAX event lister must follow the interface of @ref json_sax. + + This function reads from a compatible input. Examples are: + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in,out] sax SAX event listener + @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) + @param[in] strict whether the input has to be consumed completely + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default); only applies to the JSON file format. + + @return return value of the last processed SAX event + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the SAX consumer @a sax has + a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `sax_parse()` function + reading from string and processing the events with a user-defined SAX + event consumer.,sax_parse} + + @since version 3.2.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(InputType&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + template + JSON_HEDLEY_NON_NULL(3) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + template + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = i.get(); + return format == input_format_t::json + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } +#ifndef JSON_NO_IO + /*! + @brief deserialize from stream + @deprecated This stream operator is deprecated and will be removed in + version 4.0.0 of the library. Please use + @ref operator>>(std::istream&, basic_json&) + instead; that is, replace calls like `j << i;` with `i >> j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + return operator>>(i, j); + } + + /*! + @brief deserialize from stream + + Deserializes an input stream to a JSON value. + + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} + + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing + + @since version 1.0.0 + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } +#endif // JSON_NO_IO + /// @} + + /////////////////////////// + // convenience functions // + /////////////////////////// + + /*! + @brief return the type as string + + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. + + @return a string representation of a the @a m_type member: + Value type | return value + ----------- | ------------- + null | `"null"` + boolean | `"boolean"` + string | `"string"` + number | `"number"` (for all number types) + object | `"object"` + array | `"array"` + binary | `"binary"` + discarded | `"discarded"` + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @complexity Constant. + + @liveexample{The following code exemplifies `type_name()` for all JSON + types.,type_name} + + @sa see @ref type() -- return the type of the JSON value + @sa see @ref operator value_t() -- return the type of the JSON value (implicit) + + @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept` + since 3.0.0 + */ + JSON_HEDLEY_RETURNS_NON_NULL + const char* type_name() const noexcept + { + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + default: + return "number"; + } + } + } + + + JSON_PRIVATE_UNLESS_TESTED: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + +#if JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + public: + /*! + @brief create a CBOR serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. + + The library uses the following mapping from JSON values types to + CBOR types according to the CBOR specification (RFC 7049): + + JSON value type | value/range | CBOR type | first byte + --------------- | ------------------------------------------ | ---------------------------------- | --------------- + null | `null` | Null | 0xF6 + boolean | `true` | True | 0xF5 + boolean | `false` | False | 0xF4 + number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B + number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A + number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 + number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 + number_integer | -24..-1 | Negative integer | 0x20..0x37 + number_integer | 0..23 | Integer | 0x00..0x17 + number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A + number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B + number_unsigned | 0..23 | Integer | 0x00..0x17 + number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A + number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B + number_float | *any value representable by a float* | Single-Precision Float | 0xFA + number_float | *any value NOT representable by a float* | Double-Precision Float | 0xFB + string | *length*: 0..23 | UTF-8 string | 0x60..0x77 + string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 + string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 + string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A + string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B + array | *size*: 0..23 | array | 0x80..0x97 + array | *size*: 23..255 | array (1 byte follow) | 0x98 + array | *size*: 256..65535 | array (2 bytes follow) | 0x99 + array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A + array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B + object | *size*: 0..23 | map | 0xA0..0xB7 + object | *size*: 23..255 | map (1 byte follow) | 0xB8 + object | *size*: 256..65535 | map (2 bytes follow) | 0xB9 + object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA + object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB + binary | *size*: 0..23 | byte string | 0x40..0x57 + binary | *size*: 23..255 | byte string (1 byte follow) | 0x58 + binary | *size*: 256..65535 | byte string (2 bytes follow) | 0x59 + binary | *size*: 65536..4294967295 | byte string (4 bytes follow) | 0x5A + binary | *size*: 4294967296..18446744073709551615 | byte string (8 bytes follow) | 0x5B + + Binary values with subtype are mapped to tagged values (0xD8..0xDB) + depending on the subtype, followed by a byte string, see "binary" cells + in the table above. + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a CBOR value. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The following CBOR types are not used in the conversion: + - UTF-8 strings terminated by "break" (0x7F) + - arrays terminated by "break" (0x9F) + - maps terminated by "break" (0xBF) + - byte strings terminated by "break" (0x5F) + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + - half-precision floats (0xF9) + - break (0xFF) + + @param[in] j JSON value to serialize + @return CBOR serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} + + @sa http://cbor.io + @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the + analogous deserialization + @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format + @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9; compact representation of floating-point numbers + since version 3.8.0 + */ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor(j, result); + return result; + } + + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. + + The library uses the following mapping from JSON values types to + MessagePack types according to the MessagePack specification: + + JSON value type | value/range | MessagePack type | first byte + --------------- | --------------------------------- | ---------------- | ---------- + null | `null` | nil | 0xC0 + boolean | `true` | true | 0xC3 + boolean | `false` | false | 0xC2 + number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3 + number_integer | -2147483648..-32769 | int32 | 0xD2 + number_integer | -32768..-129 | int16 | 0xD1 + number_integer | -128..-33 | int8 | 0xD0 + number_integer | -32..-1 | negative fixint | 0xE0..0xFF + number_integer | 0..127 | positive fixint | 0x00..0x7F + number_integer | 128..255 | uint 8 | 0xCC + number_integer | 256..65535 | uint 16 | 0xCD + number_integer | 65536..4294967295 | uint 32 | 0xCE + number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_unsigned | 0..127 | positive fixint | 0x00..0x7F + number_unsigned | 128..255 | uint 8 | 0xCC + number_unsigned | 256..65535 | uint 16 | 0xCD + number_unsigned | 65536..4294967295 | uint 32 | 0xCE + number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_float | *any value representable by a float* | float 32 | 0xCA + number_float | *any value NOT representable by a float* | float 64 | 0xCB + string | *length*: 0..31 | fixstr | 0xA0..0xBF + string | *length*: 32..255 | str 8 | 0xD9 + string | *length*: 256..65535 | str 16 | 0xDA + string | *length*: 65536..4294967295 | str 32 | 0xDB + array | *size*: 0..15 | fixarray | 0x90..0x9F + array | *size*: 16..65535 | array 16 | 0xDC + array | *size*: 65536..4294967295 | array 32 | 0xDD + object | *size*: 0..15 | fix map | 0x80..0x8F + object | *size*: 16..65535 | map 16 | 0xDE + object | *size*: 65536..4294967295 | map 32 | 0xDF + binary | *size*: 0..255 | bin 8 | 0xC4 + binary | *size*: 256..65535 | bin 16 | 0xC5 + binary | *size*: 65536..4294967295 | bin 32 | 0xC6 + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a MessagePack value. + + @note The following values can **not** be converted to a MessagePack value: + - strings with more than 4294967295 bytes + - byte strings with more than 4294967295 bytes + - arrays with more than 4294967295 elements + - objects with more than 4294967295 elements + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa see @ref from_msgpack for the analogous deserialization + @sa see @ref to_cbor(const basic_json& for the related CBOR format + @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9 + */ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } + + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /*! + @brief create a UBJSON serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the UBJSON + (Universal Binary JSON) serialization format. UBJSON aims to be more compact + than JSON itself, yet more efficient to parse. + + The library uses the following mapping from JSON values types to + UBJSON types according to the UBJSON specification: + + JSON value type | value/range | UBJSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | `Z` + boolean | `true` | true | `T` + boolean | `false` | false | `F` + number_integer | -9223372036854775808..-2147483649 | int64 | `L` + number_integer | -2147483648..-32769 | int32 | `l` + number_integer | -32768..-129 | int16 | `I` + number_integer | -128..127 | int8 | `i` + number_integer | 128..255 | uint8 | `U` + number_integer | 256..32767 | int16 | `I` + number_integer | 32768..2147483647 | int32 | `l` + number_integer | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 0..127 | int8 | `i` + number_unsigned | 128..255 | uint8 | `U` + number_unsigned | 256..32767 | int16 | `I` + number_unsigned | 32768..2147483647 | int32 | `l` + number_unsigned | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 2147483649..18446744073709551615 | high-precision | `H` + number_float | *any value* | float64 | `D` + string | *with shortest length indicator* | string | `S` + array | *see notes on optimized format* | array | `[` + object | *see notes on optimized format* | map | `{` + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a UBJSON value. + + @note The following values can **not** be converted to a UBJSON value: + - strings with more than 9223372036854775807 bytes (theoretical) + + @note The following markers are not used in the conversion: + - `Z`: no-op values are not created. + - `C`: single-byte strings are serialized with `S` markers. + + @note Any UBJSON output created @ref to_ubjson can be successfully parsed + by @ref from_ubjson. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The optimized formats for containers are supported: Parameter + @a use_size adds size information to the beginning of a container and + removes the closing marker. Parameter @a use_type further checks + whether all elements of a container have the same type and adds the + type marker to the beginning of the container. The @a use_type + parameter must only be used together with @a use_size = true. Note + that @a use_size = true alone may result in larger representations - + the benefit of this parameter is that the receiving side is + immediately informed on the number of elements of the container. + + @note If the JSON data contains the binary type, the value stored is a list + of integers, as suggested by the UBJSON documentation. In particular, + this means that serialization and the deserialization of a JSON + containing binary values into UBJSON and back will result in a + different JSON object. + + @param[in] j JSON value to serialize + @param[in] use_size whether to add size annotations to container types + @param[in] use_type whether to add type annotations to container types + (must be combined with @a use_size = true) + @return UBJSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in UBJSON format.,to_ubjson} + + @sa http://ubjson.org + @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the + analogous deserialization + @sa see @ref to_cbor(const basic_json& for the related CBOR format + @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format + + @since version 3.1.0 + */ + static std::vector to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } + + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + + /*! + @brief Serializes the given JSON object `j` to BSON and returns a vector + containing the corresponding BSON-representation. + + BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are + stored as a single entity (a so-called document). + + The library uses the following mapping from JSON values types to BSON types: + + JSON value type | value/range | BSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | 0x0A + boolean | `true`, `false` | boolean | 0x08 + number_integer | -9223372036854775808..-2147483649 | int64 | 0x12 + number_integer | -2147483648..2147483647 | int32 | 0x10 + number_integer | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 0..2147483647 | int32 | 0x10 + number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 9223372036854775808..18446744073709551615| -- | -- + number_float | *any value* | double | 0x01 + string | *any value* | string | 0x02 + array | *any value* | document | 0x04 + object | *any value* | document | 0x03 + binary | *any value* | binary | 0x05 + + @warning The mapping is **incomplete**, since only JSON-objects (and things + contained therein) can be serialized to BSON. + Also, integers larger than 9223372036854775807 cannot be serialized to BSON, + and the keys may not contain U+0000, since they are serialized a + zero-terminated c-strings. + + @throw out_of_range.407 if `j.is_number_unsigned() && j.get() > 9223372036854775807` + @throw out_of_range.409 if a key in `j` contains a NULL (U+0000) + @throw type_error.317 if `!j.is_object()` + + @pre The input `j` is required to be an object: `j.is_object() == true`. + + @note Any BSON output created via @ref to_bson can be successfully parsed + by @ref from_bson. + + @param[in] j JSON value to serialize + @return BSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in BSON format.,to_bson} + + @sa http://bsonspec.org/spec.html + @sa see @ref from_bson(detail::input_adapter&&, const bool strict) for the + analogous deserialization + @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + @sa see @ref to_cbor(const basic_json&) for the related CBOR format + @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format + */ + static std::vector to_bson(const basic_json& j) + { + std::vector result; + to_bson(j, result); + return result; + } + + /*! + @brief Serializes the given JSON object `j` to BSON and forwards the + corresponding BSON-representation to the given output_adapter `o`. + @param j The JSON object to convert to BSON. + @param o The output adapter that receives the binary BSON representation. + @pre The input `j` shall be an object: `j.is_object() == true` + @sa see @ref to_bson(const basic_json&) + */ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /*! + @copydoc to_bson(const basic_json&, detail::output_adapter) + */ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + + /*! + @brief create a JSON value from an input in CBOR format + + Deserializes a given input @a i to a JSON value using the CBOR (Concise + Binary Object Representation) serialization format. + + The library maps CBOR types to JSON value types as follows: + + CBOR type | JSON value type | first byte + ---------------------- | --------------- | ---------- + Integer | number_unsigned | 0x00..0x17 + Unsigned integer | number_unsigned | 0x18 + Unsigned integer | number_unsigned | 0x19 + Unsigned integer | number_unsigned | 0x1A + Unsigned integer | number_unsigned | 0x1B + Negative integer | number_integer | 0x20..0x37 + Negative integer | number_integer | 0x38 + Negative integer | number_integer | 0x39 + Negative integer | number_integer | 0x3A + Negative integer | number_integer | 0x3B + Byte string | binary | 0x40..0x57 + Byte string | binary | 0x58 + Byte string | binary | 0x59 + Byte string | binary | 0x5A + Byte string | binary | 0x5B + UTF-8 string | string | 0x60..0x77 + UTF-8 string | string | 0x78 + UTF-8 string | string | 0x79 + UTF-8 string | string | 0x7A + UTF-8 string | string | 0x7B + UTF-8 string | string | 0x7F + array | array | 0x80..0x97 + array | array | 0x98 + array | array | 0x99 + array | array | 0x9A + array | array | 0x9B + array | array | 0x9F + map | object | 0xA0..0xB7 + map | object | 0xB8 + map | object | 0xB9 + map | object | 0xBA + map | object | 0xBB + map | object | 0xBF + False | `false` | 0xF4 + True | `true` | 0xF5 + Null | `null` | 0xF6 + Half-Precision Float | number_float | 0xF9 + Single-Precision Float | number_float | 0xFA + Double-Precision Float | number_float | 0xFB + + @warning The mapping is **incomplete** in the sense that not all CBOR + types can be converted to a JSON value. The following CBOR types + are not supported and will yield parse errors (parse_error.112): + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + + @warning CBOR allows map keys of any type, whereas JSON only allows + strings as keys in object values. Therefore, CBOR maps with keys + other than UTF-8 strings are rejected (parse_error.113). + + @note Any CBOR output created @ref to_cbor can be successfully parsed by + @ref from_cbor. + + @param[in] i an input in CBOR format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] tag_handler how to treat CBOR tags (optional, error by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from CBOR were + used in the given input @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} + + @sa http://cbor.io + @sa see @ref to_cbor(const basic_json&) for the analogous serialization + @sa see @ref from_msgpack(InputType&&, const bool, const bool) for the + related MessagePack format + @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0; added @a allow_exceptions parameter + since 3.2.0; added @a tag_handler parameter since 3.9.0. + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @brief create a JSON value from an input in MessagePack format + + Deserializes a given input @a i to a JSON value using the MessagePack + serialization format. + + The library maps MessagePack types to JSON value types as follows: + + MessagePack type | JSON value type | first byte + ---------------- | --------------- | ---------- + positive fixint | number_unsigned | 0x00..0x7F + fixmap | object | 0x80..0x8F + fixarray | array | 0x90..0x9F + fixstr | string | 0xA0..0xBF + nil | `null` | 0xC0 + false | `false` | 0xC2 + true | `true` | 0xC3 + float 32 | number_float | 0xCA + float 64 | number_float | 0xCB + uint 8 | number_unsigned | 0xCC + uint 16 | number_unsigned | 0xCD + uint 32 | number_unsigned | 0xCE + uint 64 | number_unsigned | 0xCF + int 8 | number_integer | 0xD0 + int 16 | number_integer | 0xD1 + int 32 | number_integer | 0xD2 + int 64 | number_integer | 0xD3 + str 8 | string | 0xD9 + str 16 | string | 0xDA + str 32 | string | 0xDB + array 16 | array | 0xDC + array 32 | array | 0xDD + map 16 | object | 0xDE + map 32 | object | 0xDF + bin 8 | binary | 0xC4 + bin 16 | binary | 0xC5 + bin 32 | binary | 0xC6 + ext 8 | binary | 0xC7 + ext 16 | binary | 0xC8 + ext 32 | binary | 0xC9 + fixext 1 | binary | 0xD4 + fixext 2 | binary | 0xD5 + fixext 4 | binary | 0xD6 + fixext 8 | binary | 0xD7 + fixext 16 | binary | 0xD8 + negative fixint | number_integer | 0xE0-0xFF + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + + @param[in] i an input in MessagePack format convertible to an input + adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from MessagePack were + used in the given input @a i or if the input is not valid MessagePack + @throw parse_error.113 if a string was expected as map key, but not found + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} + + @sa http://msgpack.org + @sa see @ref to_msgpack(const basic_json&) for the analogous serialization + @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa see @ref from_ubjson(InputType&&, const bool, const bool) for + the related UBJSON format + @sa see @ref from_bson(InputType&&, const bool, const bool) for + the related BSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0; added @a allow_exceptions parameter + since 3.2.0 + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_msgpack(InputType&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + /*! + @brief create a JSON value from an input in UBJSON format + + Deserializes a given input @a i to a JSON value using the UBJSON (Universal + Binary JSON) serialization format. + + The library maps UBJSON types to JSON value types as follows: + + UBJSON type | JSON value type | marker + ----------- | --------------------------------------- | ------ + no-op | *no value, next value is read* | `N` + null | `null` | `Z` + false | `false` | `F` + true | `true` | `T` + float32 | number_float | `d` + float64 | number_float | `D` + uint8 | number_unsigned | `U` + int8 | number_integer | `i` + int16 | number_integer | `I` + int32 | number_integer | `l` + int64 | number_integer | `L` + high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H' + string | string | `S` + char | string | `C` + array | array (optimized values are supported) | `[` + object | object (optimized values are supported) | `{` + + @note The mapping is **complete** in the sense that any UBJSON value can + be converted to a JSON value. + + @param[in] i an input in UBJSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if a parse error occurs + @throw parse_error.113 if a string could not be parsed successfully + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + UBJSON format to a JSON value.,from_ubjson} + + @sa http://ubjson.org + @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the + analogous serialization + @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa see @ref from_msgpack(InputType&&, const bool, const bool) for + the related MessagePack format + @sa see @ref from_bson(InputType&&, const bool, const bool) for + the related BSON format + + @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0 + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_ubjson(InputType&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + /*! + @brief Create a JSON value from an input in BSON format + + Deserializes a given input @a i to a JSON value using the BSON (Binary JSON) + serialization format. + + The library maps BSON record types to JSON value types as follows: + + BSON type | BSON marker byte | JSON value type + --------------- | ---------------- | --------------------------- + double | 0x01 | number_float + string | 0x02 | string + document | 0x03 | object + array | 0x04 | array + binary | 0x05 | binary + undefined | 0x06 | still unsupported + ObjectId | 0x07 | still unsupported + boolean | 0x08 | boolean + UTC Date-Time | 0x09 | still unsupported + null | 0x0A | null + Regular Expr. | 0x0B | still unsupported + DB Pointer | 0x0C | still unsupported + JavaScript Code | 0x0D | still unsupported + Symbol | 0x0E | still unsupported + JavaScript Code | 0x0F | still unsupported + int32 | 0x10 | number_integer + Timestamp | 0x11 | still unsupported + 128-bit decimal float | 0x13 | still unsupported + Max Key | 0x7F | still unsupported + Min Key | 0xFF | still unsupported + + @warning The mapping is **incomplete**. The unsupported mappings + are indicated in the table above. + + @param[in] i an input in BSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.114 if an unsupported BSON record type is encountered + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + BSON format to a JSON value.,from_bson} + + @sa http://bsonspec.org/spec.html + @sa see @ref to_bson(const basic_json&) for the analogous serialization + @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa see @ref from_msgpack(InputType&&, const bool, const bool) for + the related MessagePack format + @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the + related UBJSON format + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_bson(InputType&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + /// @} + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. Similar to @ref operator[](const typename + object_t::key_type&), `null` values are created in arrays and objects if + necessary. + + In particular: + - If the JSON pointer points to an object key that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. + - If the JSON pointer points to an array index that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. All indices between the current maximum and the given + index are also filled with `null`. + - The special value `-` is treated as a synonym for the index past the + end. + + @param[in] ptr a JSON pointer + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + + @liveexample{The behavior is shown in the example.,operatorjson_pointer} + + @since version 2.0.0 + */ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. The function does not change the JSON + value; no `null` values are created. In particular, the special value + `-` yields an exception. + + @param[in] ptr JSON pointer to the desired element + + @return const reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + + @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} + + @since version 2.0.0 + */ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a reference to the element at with specified JSON pointer @a ptr, + with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 2.0.0 + + @liveexample{The behavior is shown in the example.,at_json_pointer} + */ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a const reference to the element at with specified JSON pointer @a + ptr, with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 2.0.0 + + @liveexample{The behavior is shown in the example.,at_json_pointer_const} + */ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /*! + @brief return flattened JSON value + + The function creates a JSON object whose keys are JSON pointers (see [RFC + 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all + primitive. The original JSON value can be restored using the @ref + unflatten() function. + + @return an object that maps JSON pointers to primitive values + + @note Empty objects and arrays are flattened to `null` and will not be + reconstructed correctly by the @ref unflatten() function. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a JSON object is flattened to an + object whose keys consist of JSON pointers.,flatten} + + @sa see @ref unflatten() for the reverse function + + @since version 2.0.0 + */ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /*! + @brief unflatten a previously flattened JSON value + + The function restores the arbitrary nesting of a JSON value that has been + flattened before using the @ref flatten() function. The JSON value must + meet certain constraints: + 1. The value must be an object. + 2. The keys must be JSON pointers (see + [RFC 6901](https://tools.ietf.org/html/rfc6901)) + 3. The mapped values must be primitive JSON types. + + @return the original JSON from a flattened version + + @note Empty objects and arrays are flattened by @ref flatten() to `null` + values and can not unflattened to their original type. Apart from + this example, for a JSON value `j`, the following is always true: + `j == j.flatten().unflatten()`. + + @complexity Linear in the size the JSON value. + + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + + @liveexample{The following code shows how a flattened JSON object is + unflattened into the original nested JSON object.,unflatten} + + @sa see @ref flatten() for the reverse function + + @since version 2.0.0 + */ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /*! + @brief applies a JSON patch + + [JSON Patch](http://jsonpatch.com) defines a JSON document structure for + expressing a sequence of operations to apply to a JSON) document. With + this function, a JSON Patch is applied to the current JSON value by + executing all operations from the patch. + + @param[in] json_patch JSON patch document + @return patched document + + @note The application of a patch is atomic: Either all operations succeed + and the patched document is returned or an exception is thrown. In + any case, the original value is not changed: the patch is applied + to a copy of the value. + + @throw parse_error.104 if the JSON patch does not consist of an array of + objects + + @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory + attributes are missing); example: `"operation add must have member path"` + + @throw out_of_range.401 if an array index is out of range. + + @throw out_of_range.403 if a JSON pointer inside the patch could not be + resolved successfully in the current JSON value; example: `"key baz not + found"` + + @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", + "move") + + @throw other_error.501 if "test" operation was unsuccessful + + @complexity Linear in the size of the JSON value and the length of the + JSON patch. As usually only a fraction of the JSON value is affected by + the patch, the complexity can usually be neglected. + + @liveexample{The following code shows how a JSON patch is applied to a + value.,patch} + + @sa see @ref diff -- create a JSON patch by comparing two JSON values + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const std::string & op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.empty()) + { + result = val; + return; + } + + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = json_pointer::array_index(last_path); + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) + { + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent)); + } + + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + break; + } + + // if there exists a parent it cannot be primitive + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [this, &result](json_pointer & ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (JSON_HEDLEY_LIKELY(it != parent.end())) + { + parent.erase(it); + } + else + { + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this)); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(json_pointer::array_index(last_path)); + } + }; + + // type check: top level value must be an array + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch)); + } + + // iterate and apply the operations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val)); + } + + // check if result is of type string + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val)); + } + + // no error: return value + return it->second; + }; + + // type check: every element of the array must be an object + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val)); + } + + // collect mandatory members + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const auto from_path = get_value("move", "from", true).template get(); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const auto from_path = get_value("copy", "from", true).template get(); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); + break; + } + + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (JSON_HEDLEY_UNLIKELY(!success)) + { + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val)); + } + + break; + } + + case patch_operations::invalid: + default: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val)); + } + } + } + + return result; + } + + /*! + @brief creates a diff as a JSON patch + + Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can + be changed into the value @a target by calling @ref patch function. + + @invariant For two JSON values @a source and @a target, the following code + yields always `true`: + @code {.cpp} + source.patch(diff(source, target)) == target; + @endcode + + @note Currently, only `remove`, `add`, and `replace` operations are + generated. + + @param[in] source JSON value to compare from + @param[in] target JSON value to compare against + @param[in] path helper value to create JSON pointers + + @return a JSON patch to convert the @a source to @a target + + @complexity Linear in the lengths of @a source and @a target. + + @liveexample{The following code shows how a JSON patch is created as a + diff for two JSON values.,diff} + + @sa see @ref patch -- apply a JSON patch + @sa see @ref merge_patch -- apply a JSON Merge Patch + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + + @since version 2.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json diff(const basic_json& source, const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + return result; + } + + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // i now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/-"}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto path_key = path + "/" + detail::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path_key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, {"path", path_key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto path_key = path + "/" + detail::escape(it.key()); + result.push_back( + { + {"op", "add"}, {"path", path_key}, + {"value", it.value()} + }); + } + } + + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; + } + } + + return result; + } + + /// @} + + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// + + /// @name JSON Merge Patch functions + /// @{ + + /*! + @brief applies a JSON Merge Patch + + The merge patch format is primarily intended for use with the HTTP PATCH + method as a means of describing a set of modifications to a target + resource's content. This function applies a merge patch to the current + JSON value. + + The function implements the following algorithm from Section 2 of + [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396): + + ``` + define MergePatch(Target, Patch): + if Patch is an Object: + if Target is not an Object: + Target = {} // Ignore the contents and set it to an empty Object + for each Name/Value pair in Patch: + if Value is null: + if Name exists in Target: + remove the Name/Value pair from Target + else: + Target[Name] = MergePatch(Target[Name], Value) + return Target + else: + return Patch + ``` + + Thereby, `Target` is the current object; that is, the patch is applied to + the current value. + + @param[in] apply_patch the patch to apply + + @complexity Linear in the lengths of @a patch. + + @liveexample{The following code shows how a JSON Merge Patch is applied to + a JSON document.,merge_patch} + + @sa see @ref patch -- apply a JSON patch + @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396) + + @since version 3.0.0 + */ + void merge_patch(const basic_json& apply_patch) + { + if (apply_patch.is_object()) + { + if (!is_object()) + { + *this = object(); + } + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) + { + if (it.value().is_null()) + { + erase(it.key()); + } + else + { + operator[](it.key()).merge_patch(it.value()); + } + } + } + else + { + *this = apply_patch; + } + } + + /// @} +}; + +/*! +@brief user-defined to_string function for JSON values + +This function implements a user-defined to_string for JSON objects. + +@param[in] j a JSON object +@return a std::string object +*/ + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) +{ + return j.dump(); +} +} // namespace nlohmann + +/////////////////////// +// nonmember support // +/////////////////////// + +// specialization of std::swap, and std::hash +namespace std +{ + +/// hash value for JSON objects +template<> +struct hash +{ + /*! + @brief return a hash value for a JSON object + + @since version 1.0.0 + */ + std::size_t operator()(const nlohmann::json& j) const + { + return nlohmann::detail::hash(j); + } +}; + +/// specialization for std::less +/// @note: do not remove the space after '<', +/// see https://github.com/nlohmann/json/pull/679 +template<> +struct less<::nlohmann::detail::value_t> +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(nlohmann::detail::value_t lhs, + nlohmann::detail::value_t rhs) const noexcept + { + return nlohmann::detail::operator<(lhs, rhs); + } +}; + +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + +/*! +@brief exchanges the values of two JSON objects + +@since version 1.0.0 +*/ +template<> +inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) + is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression) + is_nothrow_move_assignable::value + ) +{ + j1.swap(j2); +} + +#endif + +} // namespace std + +/*! +@brief user-defined string literal for JSON values + +This operator implements a user-defined string literal for JSON objects. It +can be used by adding `"_json"` to a string literal and returns a JSON object +if no parse error occurred. + +@param[in] s a string representation of a JSON object +@param[in] n the length of string @a s +@return a JSON object + +@since version 1.0.0 +*/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +/*! +@brief user-defined string literal for JSON pointer + +This operator implements a user-defined string literal for JSON Pointers. It +can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer +object if no parse error occurred. + +@param[in] s a string representation of a JSON Pointer +@param[in] n the length of string @a s +@return a JSON pointer object + +@since version 2.0.0 +*/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +// #include + + +// restore clang diagnostic settings +#if defined(__clang__) + #pragma clang diagnostic pop +#endif + +// clean up +#undef JSON_ASSERT +#undef JSON_INTERNAL_CATCH +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_PRIVATE_UNLESS_TESTED +#undef JSON_HAS_CPP_11 +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef JSON_HAS_CPP_20 +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL +#undef JSON_EXPLICIT + +// #include + + +#undef JSON_HEDLEY_ALWAYS_INLINE +#undef JSON_HEDLEY_ARM_VERSION +#undef JSON_HEDLEY_ARM_VERSION_CHECK +#undef JSON_HEDLEY_ARRAY_PARAM +#undef JSON_HEDLEY_ASSUME +#undef JSON_HEDLEY_BEGIN_C_DECLS +#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#undef JSON_HEDLEY_CLANG_HAS_FEATURE +#undef JSON_HEDLEY_CLANG_HAS_WARNING +#undef JSON_HEDLEY_COMPCERT_VERSION +#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#undef JSON_HEDLEY_CONCAT +#undef JSON_HEDLEY_CONCAT3 +#undef JSON_HEDLEY_CONCAT3_EX +#undef JSON_HEDLEY_CONCAT_EX +#undef JSON_HEDLEY_CONST +#undef JSON_HEDLEY_CONSTEXPR +#undef JSON_HEDLEY_CONST_CAST +#undef JSON_HEDLEY_CPP_CAST +#undef JSON_HEDLEY_CRAY_VERSION +#undef JSON_HEDLEY_CRAY_VERSION_CHECK +#undef JSON_HEDLEY_C_DECL +#undef JSON_HEDLEY_DEPRECATED +#undef JSON_HEDLEY_DEPRECATED_FOR +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#undef JSON_HEDLEY_DIAGNOSTIC_POP +#undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#undef JSON_HEDLEY_DMC_VERSION +#undef JSON_HEDLEY_DMC_VERSION_CHECK +#undef JSON_HEDLEY_EMPTY_BASES +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#undef JSON_HEDLEY_END_C_DECLS +#undef JSON_HEDLEY_FLAGS +#undef JSON_HEDLEY_FLAGS_CAST +#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_BUILTIN +#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_EXTENSION +#undef JSON_HEDLEY_GCC_HAS_FEATURE +#undef JSON_HEDLEY_GCC_HAS_WARNING +#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#undef JSON_HEDLEY_GCC_VERSION +#undef JSON_HEDLEY_GCC_VERSION_CHECK +#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#undef JSON_HEDLEY_GNUC_HAS_FEATURE +#undef JSON_HEDLEY_GNUC_HAS_WARNING +#undef JSON_HEDLEY_GNUC_VERSION +#undef JSON_HEDLEY_GNUC_VERSION_CHECK +#undef JSON_HEDLEY_HAS_ATTRIBUTE +#undef JSON_HEDLEY_HAS_BUILTIN +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_HAS_EXTENSION +#undef JSON_HEDLEY_HAS_FEATURE +#undef JSON_HEDLEY_HAS_WARNING +#undef JSON_HEDLEY_IAR_VERSION +#undef JSON_HEDLEY_IAR_VERSION_CHECK +#undef JSON_HEDLEY_IBM_VERSION +#undef JSON_HEDLEY_IBM_VERSION_CHECK +#undef JSON_HEDLEY_IMPORT +#undef JSON_HEDLEY_INLINE +#undef JSON_HEDLEY_INTEL_CL_VERSION +#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#undef JSON_HEDLEY_INTEL_VERSION +#undef JSON_HEDLEY_INTEL_VERSION_CHECK +#undef JSON_HEDLEY_IS_CONSTANT +#undef JSON_HEDLEY_IS_CONSTEXPR_ +#undef JSON_HEDLEY_LIKELY +#undef JSON_HEDLEY_MALLOC +#undef JSON_HEDLEY_MCST_LCC_VERSION +#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#undef JSON_HEDLEY_MESSAGE +#undef JSON_HEDLEY_MSVC_VERSION +#undef JSON_HEDLEY_MSVC_VERSION_CHECK +#undef JSON_HEDLEY_NEVER_INLINE +#undef JSON_HEDLEY_NON_NULL +#undef JSON_HEDLEY_NO_ESCAPE +#undef JSON_HEDLEY_NO_RETURN +#undef JSON_HEDLEY_NO_THROW +#undef JSON_HEDLEY_NULL +#undef JSON_HEDLEY_PELLES_VERSION +#undef JSON_HEDLEY_PELLES_VERSION_CHECK +#undef JSON_HEDLEY_PGI_VERSION +#undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PRINTF_FORMAT +#undef JSON_HEDLEY_PRIVATE +#undef JSON_HEDLEY_PUBLIC +#undef JSON_HEDLEY_PURE +#undef JSON_HEDLEY_REINTERPRET_CAST +#undef JSON_HEDLEY_REQUIRE +#undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#undef JSON_HEDLEY_REQUIRE_MSG +#undef JSON_HEDLEY_RESTRICT +#undef JSON_HEDLEY_RETURNS_NON_NULL +#undef JSON_HEDLEY_SENTINEL +#undef JSON_HEDLEY_STATIC_ASSERT +#undef JSON_HEDLEY_STATIC_CAST +#undef JSON_HEDLEY_STRINGIFY +#undef JSON_HEDLEY_STRINGIFY_EX +#undef JSON_HEDLEY_SUNPRO_VERSION +#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#undef JSON_HEDLEY_TINYC_VERSION +#undef JSON_HEDLEY_TINYC_VERSION_CHECK +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL2000_VERSION +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL430_VERSION +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL6X_VERSION +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL7X_VERSION +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#undef JSON_HEDLEY_TI_VERSION +#undef JSON_HEDLEY_TI_VERSION_CHECK +#undef JSON_HEDLEY_UNAVAILABLE +#undef JSON_HEDLEY_UNLIKELY +#undef JSON_HEDLEY_UNPREDICTABLE +#undef JSON_HEDLEY_UNREACHABLE +#undef JSON_HEDLEY_UNREACHABLE_RETURN +#undef JSON_HEDLEY_VERSION +#undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#undef JSON_HEDLEY_VERSION_DECODE_MINOR +#undef JSON_HEDLEY_VERSION_DECODE_REVISION +#undef JSON_HEDLEY_VERSION_ENCODE +#undef JSON_HEDLEY_WARNING +#undef JSON_HEDLEY_WARN_UNUSED_RESULT +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef JSON_HEDLEY_FALL_THROUGH + + + +#endif // INCLUDE_NLOHMANN_JSON_HPP_ From c75e6bcea27dcaba04ec1a502e3d40e6f98c1536 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 Sep 2021 17:16:09 +1000 Subject: [PATCH 0080/1500] Keymap: add fallback keymap for sequencer tools This quiets error messages, fall-back tool preference needs to be further developed. --- .../keyconfig/keymap_data/blender_default.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index a2edf5e541d..3b596d6cad1 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -7277,9 +7277,10 @@ def km_3d_view_tool_sculpt_gpencil_select_lasso(params): ) -def km_sequencer_editor_tool_select(params): +def km_sequencer_editor_tool_select(params, *, fallback): return ( - "Sequencer Tool: Select", + # TODO, fall-back tool support. + _fallback_id("Sequencer Tool: Select", fallback), {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, None), @@ -7288,9 +7289,10 @@ def km_sequencer_editor_tool_select(params): ) -def km_sequencer_editor_tool_select_box(params): +def km_sequencer_editor_tool_select_box(params, *, fallback): return ( - "Sequencer Tool: Select Box", + # TODO, fall-back tool support. + _fallback_id("Sequencer Tool: Select Box", fallback), {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ *_template_items_tool_select_actions_simple( @@ -7607,8 +7609,8 @@ def generate_keymaps(params=None): km_3d_view_tool_sculpt_gpencil_select_box(params), km_3d_view_tool_sculpt_gpencil_select_circle(params), km_3d_view_tool_sculpt_gpencil_select_lasso(params), - km_sequencer_editor_tool_select(params), - km_sequencer_editor_tool_select_box(params), + *(km_sequencer_editor_tool_select(params, fallback=fallback) for fallback in (False, True)), + *(km_sequencer_editor_tool_select_box(params, fallback=fallback) for fallback in (False, True)), km_sequencer_editor_tool_blade(params), km_sequencer_editor_tool_generic_sample(params), km_sequencer_editor_tool_scale(params), From c0f55400eb5c7d20a7337333f3dde011ba1f58e8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 Sep 2021 17:16:11 +1000 Subject: [PATCH 0081/1500] Keymap: support use_key_activate_tools for selection tools Also support sequencer transform tools. --- .../keyconfig/keymap_data/blender_default.py | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 3b596d6cad1..c26abf07baf 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -955,11 +955,15 @@ def km_uv_editor(params): {"type": params.select_mouse, "value": params.select_mouse_value_fallback, "ctrl": True, "shift": True}, {"properties": [("use_fill", True)]}), ("uv.select_split", {"type": 'Y', "value": 'PRESS'}, None), - ("uv.select_box", {"type": 'B', "value": 'PRESS'}, - {"properties": [("pinned", False)]}), + op_tool_optional( + ("uv.select_box", {"type": 'B', "value": 'PRESS'}, + {"properties": [("pinned", False)]}), + (op_tool, "builtin.select_box"), params), ("uv.select_box", {"type": 'B', "value": 'PRESS', "ctrl": True}, {"properties": [("pinned", True)]}), - ("uv.select_circle", {"type": 'C', "value": 'PRESS'}, None), + op_tool_optional( + ("uv.select_circle", {"type": 'C', "value": 'PRESS'}, None), + (op_tool, "builtin.select_circle"), params), ("uv.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, {"properties": [("mode", 'ADD')]}), ("uv.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True}, @@ -1264,12 +1268,16 @@ def km_view3d(params): value=params.select_mouse_value_fallback, legacy=params.legacy, ), - ("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None), + op_tool_optional( + ("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None), + (op_tool, "builtin.select_box"), params), ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, {"properties": [("mode", 'ADD')]}), ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True}, {"properties": [("mode", 'SUB')]}), - ("view3d.select_circle", {"type": 'C', "value": 'PRESS'}, None), + op_tool_optional( + ("view3d.select_circle", {"type": 'C', "value": 'PRESS'}, None), + (op_tool, "builtin.select_circle"), params), # Borders. ("view3d.clip_border", {"type": 'B', "value": 'PRESS', "alt": True}, None), ("view3d.zoom_border", {"type": 'B', "value": 'PRESS', "shift": True}, None), @@ -1896,7 +1904,13 @@ def km_node_editor(params): {"properties": [("mode", 'ADD')]}), ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, {"properties": [("mode", 'SUB')]}), - ("node.select_circle", {"type": 'C', "value": 'PRESS'}, None), + op_tool_optional( + ("node.select_box", {"type": 'B', "value": 'PRESS'}, + {"properties": [("tweak", False)]}), + (op_tool, "builtin.select_box"), params), + op_tool_optional( + ("node.select_circle", {"type": 'C', "value": 'PRESS'}, None), + (op_tool, "builtin.select_circle"), params), ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, {"properties": [("detach", False)]}), ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, @@ -1930,8 +1944,6 @@ def km_node_editor(params): ("node.view_all", {"type": 'HOME', "value": 'PRESS'}, None), ("node.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None), ("node.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None), - ("node.select_box", {"type": 'B', "value": 'PRESS'}, - {"properties": [("tweak", False)]}), ("node.delete", {"type": 'X', "value": 'PRESS'}, None), ("node.delete", {"type": 'DEL', "value": 'PRESS'}, None), ("node.delete_reconnect", {"type": 'X', "value": 'PRESS', "ctrl": True}, None), @@ -2749,9 +2761,15 @@ def km_sequencerpreview(params): ("sequencer.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS'}, {"properties": [("ratio", 0.125)]}), ("sequencer.sample", {"type": params.action_mouse, "value": 'PRESS'}, None), - ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), - ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), - ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), + op_tool_optional( + ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.move"), params), + op_tool_optional( + ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.rotate"), params), + op_tool_optional( + ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), + (op_tool_cycle, "builtin.scale"), params), ("sequencer.strip_transform_clear", {"type": 'G', "alt": True, "value": 'PRESS'}, {"properties": [("property", 'POSITION')]}), ("sequencer.strip_transform_clear", {"type": 'S', "alt": True, "value": 'PRESS'}, @@ -3281,9 +3299,13 @@ def _grease_pencil_selection(params, use_select_mouse=True): # Select all *_template_items_select_actions(params, "gpencil.select_all"), # Circle select - ("gpencil.select_circle", {"type": 'C', "value": 'PRESS'}, None), + op_tool_optional( + ("gpencil.select_circle", {"type": 'C', "value": 'PRESS'}, None), + (op_tool, "builtin.select_circle"), params), # Box select - ("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None), + op_tool_optional( + ("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None), + (op_tool, "builtin.select_box"), params), # Lasso select ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, {"properties": [("mode", 'ADD')]}), From 719451f840a1bf7d152aa828a3c68e8c41d8fb3b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 Sep 2021 17:16:13 +1000 Subject: [PATCH 0082/1500] Cleanup: use template for hide/reveal keymap items --- .../keyconfig/keymap_data/blender_default.py | 94 +++++-------------- 1 file changed, 24 insertions(+), 70 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index c26abf07baf..0ea3c1882c9 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -282,6 +282,14 @@ def _template_items_select_actions(params, operator): ] +def _template_items_hide_reveal_actions(op_hide, op_reveal): + return [ + (op_reveal, {"type": 'H', "value": 'PRESS', "alt": True}, None), + (op_hide, {"type": 'H', "value": 'PRESS'}, {"properties": [("unselected", False)]}), + (op_hide, {"type": 'H', "value": 'PRESS', "shift": True}, {"properties": [("unselected", True)]}), + ] + + def _template_items_object_subdivision_set(): return [ ("object.subdivision_set", @@ -976,6 +984,7 @@ def km_uv_editor(params): ("uv.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("uv.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), *_template_items_select_actions(params, "uv.select_all"), + *_template_items_hide_reveal_actions("uv.hide", "uv.reveal"), ("uv.select_pinned", {"type": 'P', "value": 'PRESS', "shift": True}, None), op_menu("IMAGE_MT_uvs_merge", {"type": 'M', "value": 'PRESS'}), op_menu("IMAGE_MT_uvs_split", {"type": 'M', "value": 'PRESS', "alt": True}), @@ -987,11 +996,6 @@ def km_uv_editor(params): ("uv.pin", {"type": 'P', "value": 'PRESS', "alt": True}, {"properties": [("clear", True)]}), op_menu("IMAGE_MT_uvs_unwrap", {"type": 'U', "value": 'PRESS'}), - ("uv.hide", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("uv.hide", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), - ("uv.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), ( op_menu_pie("IMAGE_MT_uvs_snap_pie", {"type": 'S', "value": 'PRESS', "shift": True}) if not params.legacy else @@ -1452,11 +1456,7 @@ def km_mask_editing(params): {"properties": [("mode", 'SUB')]}), ("mask.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("mask.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), - ("mask.hide_view_clear", {"type": 'H', "value": 'PRESS', "alt": True}, None), - ("mask.hide_view_set", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("mask.hide_view_set", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), + *_template_items_hide_reveal_actions("mask.hide_view_set", "mask.hide_view_clear"), ("clip.select", {"type": params.select_mouse, "value": 'PRESS', "ctrl": True}, None), ("mask.cyclic_toggle", {"type": 'C', "value": 'PRESS', "alt": True}, None), ("mask.slide_point", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), @@ -1573,11 +1573,7 @@ def km_graph_editor_generic(_params): ), ("graph.extrapolation_type", {"type": 'E', "value": 'PRESS', "shift": True}, None), ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), - ("graph.hide", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("graph.hide", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), - ("graph.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), + *_template_items_hide_reveal_actions("graph.hide", "graph.reveal"), ("wm.context_set_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, {"properties": [("data_path", 'area.type'), ("value", 'DOPESHEET_EDITOR')]}), ]) @@ -2958,11 +2954,7 @@ def km_clip_editor(params): {"properties": [("action", 'LOCK')]}), ("clip.lock_tracks", {"type": 'L', "value": 'PRESS', "alt": True}, {"properties": [("action", 'UNLOCK')]}), - ("clip.hide_tracks", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("clip.hide_tracks", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), - ("clip.hide_tracks_clear", {"type": 'H', "value": 'PRESS', "alt": True}, None), + *_template_items_hide_reveal_actions("clip.hide_tracks", "clip.hide_tracks_clear"), ("clip.slide_plane_marker", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("clip.keyframe_insert", {"type": 'I', "value": 'PRESS'}, None), ("clip.keyframe_delete", {"type": 'I', "value": 'PRESS', "alt": True}, None), @@ -3399,11 +3391,7 @@ def km_grease_pencil_stroke_edit_mode(params): op_menu("GPENCIL_MT_snap", {"type": 'S', "value": 'PRESS', "shift": True}) ), # Show/hide - ("gpencil.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), - ("gpencil.hide", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("gpencil.hide", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), + *_template_items_hide_reveal_actions("gpencil.hide", "gpencil.reveal"), ("gpencil.selection_opacity_toggle", {"type": 'H', "value": 'PRESS', "ctrl": True}, None), # Display *_grease_pencil_display(), @@ -3520,11 +3508,7 @@ def km_grease_pencil_stroke_paint_mode(params): (op_tool_cycle, "builtin.interpolate"), params), ("gpencil.interpolate_sequence", {"type": 'E', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Show/hide - ("gpencil.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), - ("gpencil.hide", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("gpencil.hide", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), + *_template_items_hide_reveal_actions("gpencil.hide", "gpencil.reveal"), # Active layer op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), # Merge Layer @@ -4116,11 +4100,7 @@ def km_face_mask(params): items.extend([ *_template_items_select_actions(params, "paint.face_select_all"), - ("paint.face_select_hide", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("paint.face_select_hide", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), - ("paint.face_select_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), + *_template_items_hide_reveal_actions("paint.face_select_hide", "paint.face_select_reveal"), ("paint.face_select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), ("paint.face_select_linked_pick", {"type": 'L', "value": 'PRESS'}, {"properties": [("deselect", False)]}), @@ -4162,11 +4142,7 @@ def km_pose(params): items.extend([ ("object.parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None), - ("pose.hide", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("pose.hide", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), - ("pose.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), + *_template_items_hide_reveal_actions("pose.hide", "pose.reveal"), op_menu("VIEW3D_MT_pose_apply", {"type": 'A', "value": 'PRESS', "ctrl": True}), ("pose.rot_clear", {"type": 'R', "value": 'PRESS', "alt": True}, None), ("pose.loc_clear", {"type": 'G', "value": 'PRESS', "alt": True}, None), @@ -4287,11 +4263,7 @@ def km_object_mode(params): *_template_items_object_subdivision_set(), ("object.move_to_collection", {"type": 'M', "value": 'PRESS'}, None), ("object.link_to_collection", {"type": 'M', "value": 'PRESS', "shift": True}, None), - ("object.hide_view_clear", {"type": 'H', "value": 'PRESS', "alt": True}, None), - ("object.hide_view_set", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("object.hide_view_set", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), + *_template_items_hide_reveal_actions("object.hide_view_set", "object.hide_view_clear"), ("object.hide_collection", {"type": 'H', "value": 'PRESS', "ctrl": True}, None), *( (("object.hide_collection", @@ -4398,11 +4370,7 @@ def km_curve(params): (op_tool_cycle, "builtin.tilt"), params), ("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True}, {"properties": [("mode", 'CURVE_SHRINKFATTEN')]}), - ("curve.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), - ("curve.hide", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("curve.hide", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), + *_template_items_hide_reveal_actions("curve.hide", "curve.reveal"), ("curve.normals_make_consistent", {"type": 'N', "value": 'PRESS', "ctrl" if params.legacy else "shift": True}, None), ("object.vertex_parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None), op_menu("VIEW3D_MT_hook", {"type": 'H', "value": 'PRESS', "ctrl": True}), @@ -4715,12 +4683,14 @@ def km_sculpt(params): ("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True, "alt": True}, {"properties": [("target", "FACE_SETS"), ("falloff_type", "BOUNDARY_FACE_SET"),("invert", False), ("use_modify_active", True)]}), # Partial Visibility Show/hide + # Match keys from: `_template_items_hide_reveal_actions`, cannot use because arguments aren't compatible. ("sculpt.face_set_change_visibility", {"type": 'H', "value": 'PRESS'}, {"properties": [("mode", 'TOGGLE')]}), ("sculpt.face_set_change_visibility", {"type": 'H', "value": 'PRESS', "shift": True}, {"properties": [("mode", 'HIDE_ACTIVE')]}), ("sculpt.face_set_change_visibility", {"type": 'H', "value": 'PRESS', "alt": True}, {"properties": [("mode", 'SHOW_ALL')]}), + ("sculpt.face_set_edit", {"type": 'W', "value": 'PRESS', "ctrl": True}, {"properties": [("mode", 'GROW')]}), ("sculpt.face_set_edit", {"type": 'W', "value": 'PRESS', "ctrl": True, "alt": True}, @@ -4868,11 +4838,7 @@ def km_mesh(params): ("mesh.select_mirror", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), op_menu("VIEW3D_MT_edit_mesh_select_similar", {"type": 'G', "value": 'PRESS', "shift": True}), # Hide/reveal. - ("mesh.hide", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("mesh.hide", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), - ("mesh.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), + *_template_items_hide_reveal_actions("mesh.hide", "mesh.reveal"), # Tools. ("mesh.normals_make_consistent", {"type": 'N', "value": 'PRESS', "ctrl" if params.legacy else "shift": True}, {"properties": [("inside", False)]}), @@ -4975,11 +4941,7 @@ def km_armature(params): items.extend([ # Hide/reveal. - ("armature.hide", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("armature.hide", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), - ("armature.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), + *_template_items_hide_reveal_actions("armature.hide", "armature.reveal"), # Align & roll. ("armature.align", {"type": 'A', "value": 'PRESS', "ctrl": True, "alt": True}, None), ("armature.calculate_roll", {"type": 'N', "value": 'PRESS', "ctrl" if params.legacy else "shift": True}, None), @@ -5064,11 +5026,7 @@ def km_metaball(params): items.extend([ ("object.metaball_add", {"type": 'A', "value": 'PRESS', "shift": True}, None), - ("mball.reveal_metaelems", {"type": 'H', "value": 'PRESS', "alt": True}, None), - ("mball.hide_metaelems", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("mball.hide_metaelems", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), + *_template_items_hide_reveal_actions("mball.hide_metaelems", "mball.reveal_metaelems"), ("mball.delete_metaelems", {"type": 'X', "value": 'PRESS'}, None), ("mball.delete_metaelems", {"type": 'DEL', "value": 'PRESS'}, None), ("mball.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), @@ -5126,11 +5084,7 @@ def km_particle(params): ("particle.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), ("particle.delete", {"type": 'X', "value": 'PRESS'}, None), ("particle.delete", {"type": 'DEL', "value": 'PRESS'}, None), - ("particle.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), - ("particle.hide", {"type": 'H', "value": 'PRESS'}, - {"properties": [("unselected", False)]}), - ("particle.hide", {"type": 'H', "value": 'PRESS', "shift": True}, - {"properties": [("unselected", True)]}), + *_template_items_hide_reveal_actions("particle.hide", "particle.reveal"), ("particle.brush_edit", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), ("particle.brush_edit", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, None), ("wm.radial_control", {"type": 'F', "value": 'PRESS'}, From ba313f8a74fb3323a1a295090c8d890f78173a39 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 Sep 2021 17:37:10 +1000 Subject: [PATCH 0083/1500] Fix crash duplicating sequencer area Error in 997b5fe45dab8bd0e2976c8b673e56266134fc80. --- source/blender/editors/space_sequencer/space_sequencer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 0f23c61a2ca..99b75f82922 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -344,6 +344,7 @@ static SpaceLink *sequencer_duplicate(SpaceLink *sl) /* XXX sseq->gpd = gpencil_data_duplicate(sseq->gpd, false); */ memset(&sseqn->scopes, 0, sizeof(sseqn->scopes)); + memset(&sseqn->runtime, 0, sizeof(sseqn->runtime)); return (SpaceLink *)sseqn; } From 90b485845caf5ea9177be50cdd6d7117153f844e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 Sep 2021 17:47:52 +1000 Subject: [PATCH 0084/1500] Keymap: set the default filepath exporting keymaps Use the key-config name for the file name. --- release/scripts/startup/bl_operators/userpref.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 623bf583a74..67a02f6e1f4 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -268,7 +268,7 @@ class PREFERENCES_OT_keyconfig_export(Operator): ) filepath: StringProperty( subtype='FILE_PATH', - default="keymap.py", + default="", ) filter_folder: BoolProperty( name="Filter folders", @@ -307,7 +307,13 @@ class PREFERENCES_OT_keyconfig_export(Operator): return {'FINISHED'} def invoke(self, context, _event): + import os wm = context.window_manager + if not self.filepath: + self.filepath = os.path.join( + os.path.expanduser("~"), + bpy.path.display_name_to_filepath(wm.keyconfigs.active.name) + ".py", + ) wm.fileselect_add(self) return {'RUNNING_MODAL'} From 2884ae971ab56301fbddb053802ff947f934bf7c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 22 Sep 2021 13:12:48 +0300 Subject: [PATCH 0085/1500] Custom Properties: fix the tooltip field not initialized in edit dialog. Initializing the description property was completely forgotten. It also seems it may be missing sometimes, so use `get`. Also, clean values when there is no data, and correctly use the return value of `get_value_eval` in one instance. --- release/scripts/startup/bl_operators/wm.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 6af86e75b8a..a386df5c428 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1556,11 +1556,18 @@ class WM_OT_properties_edit(Operator): self.max != self.soft_max ) self.default = str(rna_data["default"]) - if prop_type == str and not is_array and not value_failed: # String arrays do not support UI data. + self.description = rna_data.get("description", "") + elif prop_type == str and not is_array and not value_failed: # String arrays do not support UI data. ui_data = item.id_properties_ui(prop) rna_data = ui_data.as_dict() self.subtype = rna_data["subtype"] self.default = str(rna_data["default"]) + self.description = rna_data.get("description", "") + else: + self.min = self.soft_min = 0 + self.max = self.soft_max = 1 + self.use_soft_limits = False + self.description = "" self._init_subtype(prop_type, is_array, self.subtype) @@ -1611,7 +1618,7 @@ class WM_OT_properties_edit(Operator): layout.prop(self, "property") layout.prop(self, "value") - value = self.get_value_eval() + value, value_failed = self.get_value_eval() proptype, is_array = rna_idprop_value_item_type(value) row = layout.row() From 7633548d60ccba95005efee41e836f90d0379f79 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 22 Sep 2021 13:50:05 +0200 Subject: [PATCH 0086/1500] Revert "Make knife drawing anti aliased (Monkey work based on D11333)" This reverts commit 96027b2d15b73d2b5086899425021ea4c903fa00. The patch asserts on different occasions and needs more work. --- source/blender/editors/mesh/editmesh_knife.c | 48 ++++---------------- 1 file changed, 9 insertions(+), 39 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 090754a2353..3e3593d18fd 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -29,8 +29,6 @@ #include "MEM_guardedalloc.h" -#include "DNA_userdef_types.h" - #include "BLI_alloca.h" #include "BLI_array.h" #include "BLI_linklist.h" @@ -325,13 +323,10 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) } uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - float viewport[4]; - GPU_viewport_size_get_f(viewport); - immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); - immUniform2fv("viewportSize", &viewport[2]); - immUniform1f("lineWidth", 2 * U.pixelsize); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformThemeColor3(TH_TRANSFORM); + GPU_line_width(2.0); immBegin(GPU_PRIM_LINES, 2); immVertex3fv(pos, v1); @@ -346,7 +341,6 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v { const KnifeTool_OpData *kcd = arg; GPU_depth_test(GPU_DEPTH_NONE); - GPU_blend(GPU_BLEND_ALPHA); GPU_matrix_push_projection(); GPU_polygon_offset(1.0f, 1.0f); @@ -361,70 +355,55 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - float viewport[4]; - GPU_viewport_size_get_f(viewport); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); if (kcd->mode == MODE_DRAGGING) { - immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); - immUniform2fv("viewportSize", &viewport[2]); - immUniform1f("lineWidth", 2 * U.pixelsize); - immUniformColor3ubv(kcd->colors.line); + GPU_line_width(2.0); immBegin(GPU_PRIM_LINES, 2); immVertex3fv(pos, kcd->prev.cage); immVertex3fv(pos, kcd->curr.cage); immEnd(); - immUnbindProgram(); } if (kcd->prev.vert) { - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3ubv(kcd->colors.point); GPU_point_size(11 * UI_DPI_FAC); immBegin(GPU_PRIM_POINTS, 1); immVertex3fv(pos, kcd->prev.cage); immEnd(); - immUnbindProgram(); } if (kcd->prev.bmface || kcd->prev.edge) { - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3ubv(kcd->colors.curpoint); GPU_point_size(9 * UI_DPI_FAC); immBegin(GPU_PRIM_POINTS, 1); immVertex3fv(pos, kcd->prev.cage); immEnd(); - immUnbindProgram(); } if (kcd->curr.vert) { - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3ubv(kcd->colors.point); GPU_point_size(11 * UI_DPI_FAC); immBegin(GPU_PRIM_POINTS, 1); immVertex3fv(pos, kcd->curr.cage); immEnd(); - immUnbindProgram(); } else if (kcd->curr.edge) { - immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); - immUniform2fv("viewportSize", &viewport[2]); - immUniform1f("lineWidth", 2 * U.pixelsize); immUniformColor3ubv(kcd->colors.edge); + GPU_line_width(2.0); immBegin(GPU_PRIM_LINES, 2); immVertex3fv(pos, kcd->curr.edge->v1->cageco); immVertex3fv(pos, kcd->curr.edge->v2->cageco); immEnd(); - immUnbindProgram(); } if (kcd->curr.bmface || kcd->curr.edge) { - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3ubv(kcd->colors.curpoint); GPU_point_size(9 * UI_DPI_FAC); @@ -438,8 +417,6 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v int i, snapped_verts_count, other_verts_count; float fcol[4]; - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformThemeColor3(TH_TRANSFORM); GPU_blend(GPU_BLEND_ALPHA); GPUVertBuf *vert = GPU_vertbuf_create_with_format(format); @@ -477,19 +454,14 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPU_batch_discard(batch); GPU_blend(GPU_BLEND_NONE); - - immEnd(); - immUnbindProgram(); } if (kcd->totkedge > 0) { BLI_mempool_iter iter; KnifeEdge *kfe; - immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); - immUniform2fv("viewportSize", &viewport[2]); - immUniform1f("lineWidth", U.pixelsize); immUniformColor3ubv(kcd->colors.line); + GPU_line_width(1.0); GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_LINES, BLI_mempool_len(kcd->kedges) * 2); @@ -504,7 +476,6 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v } immEnd(); - immUnbindProgram(); GPU_batch_draw(batch); GPU_batch_discard(batch); @@ -514,7 +485,6 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v BLI_mempool_iter iter; KnifeVert *kfv; - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor3ubv(kcd->colors.point); GPU_point_size(5.0 * UI_DPI_FAC); @@ -530,18 +500,18 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v } immEnd(); - immUnbindProgram(); GPU_batch_draw(batch); GPU_batch_discard(batch); } + immUnbindProgram(); + GPU_matrix_pop(); GPU_matrix_pop_projection(); /* Reset default */ GPU_depth_test(GPU_DEPTH_LESS_EQUAL); - GPU_blend(GPU_BLEND_NONE); } /** \} */ From 2526a355bcae7292d6945903def69f982f88990d Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 22 Sep 2021 10:59:49 +0200 Subject: [PATCH 0087/1500] Fix bad image drawing during rendering on certain GPUs Reported by Thomas DInges: the default cube render in Cycles has jagged edges during rendering. Happens on AMD Radeon RX 5500 XT. Force linear interpolation at zoom level 1 and less. Reviewed by @fclem --- intern/cycles/blender/blender_gpu_display.cpp | 26 +++++++++++++++++++ intern/cycles/blender/blender_gpu_display.h | 4 +++ intern/cycles/blender/blender_session.cpp | 7 ++++- intern/cycles/blender/blender_session.h | 4 +++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/intern/cycles/blender/blender_gpu_display.cpp b/intern/cycles/blender/blender_gpu_display.cpp index aa2a9c17a8a..c5c3a2bd155 100644 --- a/intern/cycles/blender/blender_gpu_display.cpp +++ b/intern/cycles/blender/blender_gpu_display.cpp @@ -475,6 +475,11 @@ void BlenderGPUDisplay::clear() texture_.need_clear = true; } +void BlenderGPUDisplay::set_zoom(float zoom_x, float zoom_y) +{ + zoom_ = make_float2(zoom_x, zoom_y); +} + void BlenderGPUDisplay::do_draw(const GPUDisplayParams ¶ms) { /* See do_update_begin() for why no locking is required here. */ @@ -508,6 +513,27 @@ void BlenderGPUDisplay::do_draw(const GPUDisplayParams ¶ms) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + /* Trick to keep sharp rendering without jagged edges on all GPUs. + * + * The idea here is to enforce driver to use linear interpolation when the image is not zoomed + * in. + * For the render result with a resolution divider in effect we always use nearest interpolation. + * + * Use explicit MIN assignment to make sure the driver does not have an undefined behavior at + * the zoom level 1. The MAG filter is always NEAREST. */ + const float zoomed_width = params.size.x * zoom_.x; + const float zoomed_height = params.size.y * zoom_.y; + if (texture_.width != params.size.x || texture_.height != params.size.y) { + /* Resolution divider is different from 1, force enarest interpolation. */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + else if (zoomed_width - params.size.x > 0.5f || zoomed_height - params.size.y > 0.5f) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); texture_update_if_needed(); diff --git a/intern/cycles/blender/blender_gpu_display.h b/intern/cycles/blender/blender_gpu_display.h index 1014c96cee4..89420567037 100644 --- a/intern/cycles/blender/blender_gpu_display.h +++ b/intern/cycles/blender/blender_gpu_display.h @@ -107,6 +107,8 @@ class BlenderGPUDisplay : public GPUDisplay { virtual void clear() override; + void set_zoom(float zoom_x, float zoom_y); + protected: virtual bool do_update_begin(const GPUDisplayParams ¶ms, int texture_width, @@ -206,6 +208,8 @@ class BlenderGPUDisplay : public GPUDisplay { void *gl_render_sync_ = nullptr; void *gl_upload_sync_ = nullptr; + + float2 zoom_ = make_float2(1.0f, 1.0f); }; CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 5aafa605526..d65d89a7ddd 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -158,7 +158,9 @@ void BlenderSession::create_session() /* Create GPU display. */ if (!b_engine.is_preview() && !headless) { - session->set_gpu_display(make_unique(b_engine, b_scene)); + unique_ptr gpu_display = make_unique(b_engine, b_scene); + gpu_display_ = gpu_display.get(); + session->set_gpu_display(move(gpu_display)); } /* Viewport and preview (as in, material preview) does not do tiled rendering, so can inform @@ -878,6 +880,9 @@ void BlenderSession::draw(BL::SpaceImageEditor &space_image) draw_state_.last_pass_index = pass_index; } + BL::Array zoom = space_image.zoom(); + gpu_display_->set_zoom(zoom[0], zoom[1]); + session->draw(); } diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index cf52359ea5d..11e2657a325 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -29,6 +29,7 @@ CCL_NAMESPACE_BEGIN +class BlenderGPUDisplay; class BlenderSync; class ImageMetaData; class Scene; @@ -159,6 +160,9 @@ class BlenderSession { int last_pass_index = -1; } draw_state_; + /* NOTE: The BlenderSession references the GPU display. */ + BlenderGPUDisplay *gpu_display_ = nullptr; + vector full_buffer_files_; }; From 9add7c35cc6818b28842dd1bcdd3081d35d7623b Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 22 Sep 2021 14:34:59 +0200 Subject: [PATCH 0088/1500] Fix (unreported) crash in outliner after recent changes to ID management core code. Outliner tree building code abuse the `ID.newid` pointer to store non-ID data. While this is bad and should be fixed at some point, for the time being at the very least do not use ID BKE API to deal with this pointer in that specific case, this needs its own proper code. --- .../blender/editors/space_outliner/outliner_tree.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index c5ec656080a..5427ae31ac3 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1864,6 +1864,15 @@ static void outliner_filter_tree(SpaceOutliner *space_outliner, ViewLayer *view_ space_outliner, view_layer, &space_outliner->tree, search_string, exclude_filter); } +static void outliner_clear_newid_from_main(Main *bmain) +{ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + id_iter->newid = NULL; + } + FOREACH_MAIN_ID_END; +} + /* ======================================================= */ /* Main Tree Building API */ @@ -1926,5 +1935,7 @@ void outliner_build_tree(Main *mainvar, outliner_filter_tree(space_outliner, view_layer); outliner_restore_scrolling_position(space_outliner, region, &focus); - BKE_main_id_newptr_and_tag_clear(mainvar); + /* `ID.newid` pointer is abused when building tree, DO NOT call #BKE_main_id_newptr_and_tag_clear + * as this expects valid IDs in this pointer, not random unknown data. */ + outliner_clear_newid_from_main(mainvar); } From f7f5024c95ca72d9fee864ffda032723f343c605 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 Sep 2021 21:46:20 +1000 Subject: [PATCH 0089/1500] Keymap: support use_key_activate_tools for annotate --- .../keyconfig/keymap_data/blender_default.py | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 0ea3c1882c9..9dde1e024fd 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -3258,7 +3258,7 @@ def km_animation_channels(params): # Modes -def km_grease_pencil(_params): +def km_grease_pencil(params): items = [] keymap = ( "Grease Pencil", @@ -3266,22 +3266,27 @@ def km_grease_pencil(_params): {"items": items}, ) - items.extend([ - # Draw - ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "key_modifier": 'D'}, - {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}), - ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "key_modifier": 'D', "shift": True}, - {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}), - # Draw - straight lines - ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True, "key_modifier": 'D'}, - {"properties": [("mode", 'DRAW_STRAIGHT'), ("wait_for_input", False)]}), - # Draw - poly lines - ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "alt": True, "key_modifier": 'D'}, - {"properties": [("mode", 'DRAW_POLY'), ("wait_for_input", False)]}), - # Erase - ("gpencil.annotate", {"type": 'RIGHTMOUSE', "value": 'PRESS', "key_modifier": 'D'}, - {"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}), - ]) + if params.use_key_activate_tools: + items.extend([ + op_tool_cycle("builtin.annotate", {"type": 'D', "value": 'PRESS'}), + ]) + else: + items.extend([ + # Draw + ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "key_modifier": 'D'}, + {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}), + ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "key_modifier": 'D', "shift": True}, + {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}), + # Draw - straight lines + ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True, "key_modifier": 'D'}, + {"properties": [("mode", 'DRAW_STRAIGHT'), ("wait_for_input", False)]}), + # Draw - poly lines + ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "alt": True, "key_modifier": 'D'}, + {"properties": [("mode", 'DRAW_POLY'), ("wait_for_input", False)]}), + # Erase + ("gpencil.annotate", {"type": 'RIGHTMOUSE', "value": 'PRESS', "key_modifier": 'D'}, + {"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}), + ]) return keymap From a6f6c421ef1d4b09ad4985a220be9aa2551af6e7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 Sep 2021 22:50:16 +1000 Subject: [PATCH 0090/1500] Cleanup: pep8 for the default keymap Mainly line lengths & indentation. --- .../keyconfig/keymap_data/blender_default.py | 240 +++++++++++++----- 1 file changed, 174 insertions(+), 66 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 9dde1e024fd..15d6d44d240 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -147,7 +147,6 @@ class Params: else: self.tool_modifier = {} - self.use_mouse_emulate_3_button = use_mouse_emulate_3_button # User preferences @@ -824,10 +823,14 @@ def km_property_editor(_params): ("object.modifier_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None), ("object.modifier_apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}), # Grease pencil modifier panels - ("object.gpencil_modifier_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}), - ("object.gpencil_modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), - ("object.gpencil_modifier_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None), - ("object.gpencil_modifier_apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}), + ("object.gpencil_modifier_remove", + {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.gpencil_modifier_remove", + {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.gpencil_modifier_copy", + {"type": 'D', "value": 'PRESS', "shift": True}, None), + ("object.gpencil_modifier_apply", + {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}), # ShaderFX panels ("object.shaderfx_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}), ("object.shaderfx_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), @@ -948,8 +951,10 @@ def km_uv_editor(params): legacy=params.legacy, ), ("uv.mark_seam", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), - ("uv.select_loop", {"type": params.select_mouse, "value": params.select_mouse_value, "alt": True}, None), - ("uv.select_loop", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "alt": True}, + ("uv.select_loop", + {"type": params.select_mouse, "value": params.select_mouse_value, "alt": True}, None), + ("uv.select_loop", + {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "alt": True}, {"properties": [("extend", True)]}), ("uv.select_edge_ring", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "alt": True}, None), @@ -1317,10 +1322,14 @@ def km_view3d(params): # Snapping. ("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.use_snap')]}), - op_panel("VIEW3D_PT_snapping", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, [("keep_open", True)]), + op_panel( + "VIEW3D_PT_snapping", + {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, + [("keep_open", True)], + ), ( op_menu_pie("VIEW3D_MT_snap_pie", {"type": 'S', "value": 'PRESS', "shift": True}) - if not params.legacy else + if not params.legacy else op_menu("VIEW3D_MT_snap", {"type": 'S', "value": 'PRESS', "shift": True}) ), ]) @@ -1397,11 +1406,17 @@ def km_view3d(params): {"properties": [("data_path", 'tool_settings.transform_pivot_point'), ("value", 'ACTIVE_ELEMENT')]}), # Old shading. ("wm.context_toggle_enum", {"type": 'Z', "value": 'PRESS'}, - {"properties": [("data_path", 'space_data.shading.type'), ("value_1", 'WIREFRAME'), ("value_2", 'SOLID')]}), + {"properties": [ + ("data_path", 'space_data.shading.type'), ("value_1", 'WIREFRAME'), ("value_2", 'SOLID'), + ]}), ("wm.context_toggle_enum", {"type": 'Z', "value": 'PRESS', "shift": True}, - {"properties": [("data_path", 'space_data.shading.type'), ("value_1", 'RENDERED'), ("value_2", 'SOLID')]}), + {"properties": [ + ("data_path", 'space_data.shading.type'), ("value_1", 'RENDERED'), ("value_2", 'SOLID'), + ]}), ("wm.context_toggle_enum", {"type": 'Z', "value": 'PRESS', "alt": True}, - {"properties": [("data_path", 'space_data.shading.type'), ("value_1", 'MATERIAL'), ("value_2", 'SOLID')]}), + {"properties": [ + ("data_path", 'space_data.shading.type'), ("value_1", 'MATERIAL'), ("value_2", 'SOLID'), + ]}), ]) if params.select_mouse == 'LEFTMOUSE' and not params.legacy: @@ -1462,7 +1477,8 @@ def km_mask_editing(params): ("mask.slide_point", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), ("mask.slide_spline_curvature", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), ("mask.handle_type_set", {"type": 'V', "value": 'PRESS'}, None), - ("mask.normals_make_consistent", {"type": 'N', "value": 'PRESS', "ctrl" if params.legacy else "shift": True}, None), + ("mask.normals_make_consistent", + {"type": 'N', "value": 'PRESS', "ctrl" if params.legacy else "shift": True}, None), ("mask.parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None), ("mask.parent_clear", {"type": 'P', "value": 'PRESS', "alt": True}, None), ("mask.shape_key_insert", {"type": 'I', "value": 'PRESS'}, None), @@ -1912,8 +1928,10 @@ def km_node_editor(params): ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, {"properties": [("detach", True)]}), ("node.resize", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("node.add_reroute", {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "shift": True}, None), - ("node.links_cut", {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True}, None), + ("node.add_reroute", + {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "shift": True}, None), + ("node.links_cut", + {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True}, None), ("node.links_mute", {"type": 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True, "alt": True}, None), ("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None), ("node.backimage_move", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None), @@ -1968,9 +1986,15 @@ def km_node_editor(params): ("node.clipboard_paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), ("node.viewer_border", {"type": 'B', "value": 'PRESS', "ctrl": True}, None), ("node.clear_viewer_border", {"type": 'B', "value": 'PRESS', "ctrl": True, "alt": True}, None), - ("node.translate_attach", {"type": 'G', "value": 'PRESS'}, {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), - ("node.translate_attach", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), - ("node.translate_attach", {"type": params.select_tweak, "value": 'ANY'}, {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), + ("node.translate_attach", + {"type": 'G', "value": 'PRESS'}, + {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), + ("node.translate_attach", + {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), + ("node.translate_attach", + {"type": params.select_tweak, "value": 'ANY'}, + {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), ("transform.translate", {"type": 'G', "value": 'PRESS'}, {"properties": [("view2d_edge_pan", True)]}), ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, {"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}), @@ -1978,9 +2002,15 @@ def km_node_editor(params): {"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}), ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), - ("node.move_detach_links", {"type": 'D', "value": 'PRESS', "alt": True}, {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), - ("node.move_detach_links_release", {"type": params.action_tweak, "value": 'ANY', "alt": True}, {"properties": [("NODE_OT_translate_attach", [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])])]}), - ("node.move_detach_links", {"type": params.select_tweak, "value": 'ANY', "alt": True}, {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), + ("node.move_detach_links", + {"type": 'D', "value": 'PRESS', "alt": True}, + {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), + ("node.move_detach_links_release", + {"type": params.action_tweak, "value": 'ANY', "alt": True}, + {"properties": [("NODE_OT_translate_attach", [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])])]}), + ("node.move_detach_links", + {"type": params.select_tweak, "value": 'ANY', "alt": True}, + {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), ("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.use_snap')]}), ("wm.context_menu_enum", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, @@ -2030,7 +2060,7 @@ def km_file_browser(params): toolbar_key={"type": 'T', "value": 'PRESS'}, ), ("wm.context_toggle", {"type": 'N', "value": 'PRESS'}, - {"properties": [("data_path", 'space_data.show_region_tool_props')]}), + {"properties": [("data_path", 'space_data.show_region_tool_props')]}), ("file.parent", {"type": 'UP_ARROW', "value": 'PRESS', "alt": True}, None), ("file.previous", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True}, None), ("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True}, None), @@ -2063,7 +2093,10 @@ def km_file_browser(params): # Select file under cursor before spawning the context menu. ("file.select", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, - {"properties": [("open", False), ("only_activate_if_selected", params.select_mouse == 'LEFTMOUSE'), ("pass_through", True)]}), + {"properties": [ + ("open", False), + ("only_activate_if_selected", params.select_mouse == 'LEFTMOUSE'), ("pass_through", True), + ]}), *_template_items_context_menu("FILEBROWSER_MT_context_menu", params.context_menu_event), *_template_items_context_menu("ASSETBROWSER_MT_context_menu", params.context_menu_event), ]) @@ -2189,17 +2222,23 @@ def km_dopesheet(params): ) items.extend([ - ("action.clickselect", {"type": params.select_mouse, "value": 'PRESS'}, + ("action.clickselect", + {"type": params.select_mouse, "value": 'PRESS'}, {"properties": [("deselect_all", not params.legacy)]}), - ("action.clickselect", {"type": params.select_mouse, "value": 'PRESS', "alt": True}, + ("action.clickselect", + {"type": params.select_mouse, "value": 'PRESS', "alt": True}, {"properties": [("column", True)]}), - ("action.clickselect", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, + ("action.clickselect", + {"type": params.select_mouse, "value": 'PRESS', "shift": True}, {"properties": [("extend", True)]}), - ("action.clickselect", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "alt": True}, + ("action.clickselect", + {"type": params.select_mouse, "value": 'PRESS', "shift": True, "alt": True}, {"properties": [("extend", True), ("column", True)]}), - ("action.clickselect", {"type": params.select_mouse, "value": 'PRESS', "ctrl": True, "alt": True}, + ("action.clickselect", + {"type": params.select_mouse, "value": 'PRESS', "ctrl": True, "alt": True}, {"properties": [("channel", True)]}), - ("action.clickselect", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, + ("action.clickselect", + {"type": params.select_mouse, "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, {"properties": [("extend", True), ("channel", True)]}), ("action.select_leftright", {"type": params.select_mouse, "value": 'PRESS' if params.legacy else 'CLICK', "ctrl": True}, @@ -3070,6 +3109,7 @@ def km_clip_dopesheet_editor(_params): return keymap + def km_spreadsheet_generic(_params): items = [] keymap = ( @@ -3273,18 +3313,23 @@ def km_grease_pencil(params): else: items.extend([ # Draw - ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "key_modifier": 'D'}, + ("gpencil.annotate", + {"type": 'LEFTMOUSE', "value": 'PRESS', "key_modifier": 'D'}, {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}), - ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "key_modifier": 'D', "shift": True}, + ("gpencil.annotate", + {"type": 'LEFTMOUSE', "value": 'PRESS', "key_modifier": 'D', "shift": True}, {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}), # Draw - straight lines - ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True, "key_modifier": 'D'}, + ("gpencil.annotate", + {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True, "key_modifier": 'D'}, {"properties": [("mode", 'DRAW_STRAIGHT'), ("wait_for_input", False)]}), # Draw - poly lines - ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "alt": True, "key_modifier": 'D'}, + ("gpencil.annotate", + {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "alt": True, "key_modifier": 'D'}, {"properties": [("mode", 'DRAW_POLY'), ("wait_for_input", False)]}), # Erase - ("gpencil.annotate", {"type": 'RIGHTMOUSE', "value": 'PRESS', "key_modifier": 'D'}, + ("gpencil.annotate", + {"type": 'RIGHTMOUSE', "value": 'PRESS', "key_modifier": 'D'}, {"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}), ]) @@ -3313,9 +3358,11 @@ def _grease_pencil_selection(params, use_select_mouse=True): # There probably isn't too much harm adding this for other editors too # as part of standard GP editing keymap. This hotkey combo doesn't seem # to see much use under standard scenarios? - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, + ("gpencil.select_lasso", + {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, {"properties": [("mode", 'ADD')]}), - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, + ("gpencil.select_lasso", + {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, {"properties": [("mode", 'SUB')]}), *_template_view3d_gpencil_select( type=params.select_mouse, @@ -3466,6 +3513,7 @@ def km_grease_pencil_stroke_edit_mode(params): return keymap + def km_grease_pencil_stroke_curve_edit_mode(_params): items = [] keymap = ( @@ -3481,6 +3529,7 @@ def km_grease_pencil_stroke_curve_edit_mode(_params): return keymap + def km_grease_pencil_stroke_paint_mode(params): items = [] keymap = ( @@ -3622,10 +3671,21 @@ def km_grease_pencil_stroke_paint_fill(_params): {"properties": [("on_back", False)]}), # If press alternate key, the brush now it's for drawing areas ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, - {"properties": [("mode", 'DRAW'), ("wait_for_input", False), ("disable_straight", True), ("disable_stabilizer", True)]}), + {"properties": [ + ("mode", 'DRAW'), + ("wait_for_input", False), + ("disable_straight", True), + ("disable_stabilizer", True), + ]}), # If press alternative key, the brush now it's for drawing lines ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, - {"properties": [("mode", 'DRAW'), ("wait_for_input", False), ("disable_straight", True), ("disable_stabilizer", True), ("disable_fill", True)]}), + {"properties": [ + ("mode", 'DRAW'), + ("wait_for_input", False), + ("disable_straight", True), + ("disable_stabilizer", True), + ("disable_fill", True), + ]}), ]) return keymap @@ -3949,7 +4009,9 @@ def km_grease_pencil_stroke_vertex_mode(params): *_grease_pencil_selection(params, use_select_mouse=False), # Brush strength ("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True}, - {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength')]}), + {"properties": [ + ("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength'), + ]}), # Brush size ("wm.radial_control", {"type": 'F', "value": 'PRESS'}, {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.size')]}), @@ -3995,7 +4057,9 @@ def km_grease_pencil_stroke_vertex_draw(_params): {"properties": [("wait_for_input", False)]}), # Brush strength ("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True}, - {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength')]}), + {"properties": [ + ("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength'), + ]}), # Brush size ("wm.radial_control", {"type": 'F', "value": 'PRESS'}, {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.size')]}), @@ -4018,7 +4082,9 @@ def km_grease_pencil_stroke_vertex_blur(_params): {"properties": [("wait_for_input", False)]}), # Brush strength ("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True}, - {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength')]}), + {"properties": [ + ("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength'), + ]}), # Brush size ("wm.radial_control", {"type": 'F', "value": 'PRESS'}, {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.size')]}), @@ -4043,7 +4109,9 @@ def km_grease_pencil_stroke_vertex_average(_params): {"properties": [("wait_for_input", False)]}), # Brush strength ("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True}, - {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength')]}), + {"properties": [ + ("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength')], + }), # Brush size ("wm.radial_control", {"type": 'F', "value": 'PRESS'}, {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.size')]}), @@ -4066,7 +4134,9 @@ def km_grease_pencil_stroke_vertex_smear(_params): {"properties": [("wait_for_input", False)]}), # Brush strength ("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True}, - {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength')]}), + {"properties": [ + ("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.gpencil_settings.pen_strength'), + ]}), # Brush size ("wm.radial_control", {"type": 'F', "value": 'PRESS'}, {"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.size')]}), @@ -4262,8 +4332,10 @@ def km_object_mode(params): ("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), ("collection.create", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), ("collection.objects_remove", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None), - ("collection.objects_remove_all", {"type": 'G', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), - ("collection.objects_add_active", {"type": 'G', "value": 'PRESS', "shift": True, "ctrl": True}, None), + ("collection.objects_remove_all", + {"type": 'G', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), + ("collection.objects_add_active", + {"type": 'G', "value": 'PRESS', "shift": True, "ctrl": True}, None), ("collection.objects_remove_active", {"type": 'G', "value": 'PRESS', "shift": True, "alt": True}, None), *_template_items_object_subdivision_set(), ("object.move_to_collection", {"type": 'M', "value": 'PRESS'}, None), @@ -4376,7 +4448,8 @@ def km_curve(params): ("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True}, {"properties": [("mode", 'CURVE_SHRINKFATTEN')]}), *_template_items_hide_reveal_actions("curve.hide", "curve.reveal"), - ("curve.normals_make_consistent", {"type": 'N', "value": 'PRESS', "ctrl" if params.legacy else "shift": True}, None), + ("curve.normals_make_consistent", + {"type": 'N', "value": 'PRESS', "ctrl" if params.legacy else "shift": True}, None), ("object.vertex_parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None), op_menu("VIEW3D_MT_hook", {"type": 'H', "value": 'PRESS', "ctrl": True}), *_template_items_proportional_editing( @@ -4417,9 +4490,11 @@ def _template_paint_radial_control(paint, rotation=False, secondary_rotation=Fal items.extend([ ("wm.radial_control", {"type": 'F', "value": 'PRESS'}, - radial_control_properties(paint, 'size', 'use_unified_size', secondary_rotation=secondary_rotation, color=color, zoom=zoom)), + radial_control_properties( + paint, 'size', 'use_unified_size', secondary_rotation=secondary_rotation, color=color, zoom=zoom)), ("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True}, - radial_control_properties(paint, 'strength', 'use_unified_strength', secondary_rotation=secondary_rotation, color=color)), + radial_control_properties( + paint, 'strength', 'use_unified_strength', secondary_rotation=secondary_rotation, color=color)), ]) if rotation: @@ -4431,7 +4506,8 @@ def _template_paint_radial_control(paint, rotation=False, secondary_rotation=Fal if secondary_rotation: items.extend([ ("wm.radial_control", {"type": 'F', "value": 'PRESS', "ctrl": True, "alt": True}, - radial_control_properties(paint, 'mask_texture_slot.angle', None, secondary_rotation=secondary_rotation, color=color)), + radial_control_properties( + paint, 'mask_texture_slot.angle', None, secondary_rotation=secondary_rotation, color=color)), ]) return items @@ -4684,9 +4760,18 @@ def km_sculpt(params): ("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True, "alt": True}, {"properties": [("target", "MASK"), ("falloff_type", "NORMALS"), ("invert", False)]}), ("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True}, - {"properties": [("target", "FACE_SETS"), ("falloff_type", "GEODESIC"), ("invert", False), ("use_modify_active", False)]}), + {"properties": [ + ("target", "FACE_SETS"), + ("falloff_type", "GEODESIC"), + ("invert", False), + ("use_modify_active", False)]}), ("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True, "alt": True}, - {"properties": [("target", "FACE_SETS"), ("falloff_type", "BOUNDARY_FACE_SET"),("invert", False), ("use_modify_active", True)]}), + {"properties": [ + ("target", "FACE_SETS"), + ("falloff_type", "BOUNDARY_FACE_SET"), + ("invert", False), + ("use_modify_active", True), + ]}), # Partial Visibility Show/hide # Match keys from: `_template_items_hide_reveal_actions`, cannot use because arguments aren't compatible. ("sculpt.face_set_change_visibility", {"type": 'H', "value": 'PRESS'}, @@ -4775,7 +4860,7 @@ def km_sculpt(params): {"properties": [("data_path", 'tool_settings.sculpt.brush.use_smooth_stroke')]}), op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}), op_menu_pie("VIEW3D_MT_sculpt_mask_edit_pie", {"type": 'A', "value": 'PRESS'}), - op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True,"value": 'PRESS'}), + op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True, "value": 'PRESS'}), op_menu_pie("VIEW3D_MT_sculpt_face_sets_edit_pie", {"type": 'W', "value": 'PRESS'}), *_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", params.context_menu_event), ]) @@ -4817,12 +4902,16 @@ def km_mesh(params): # Selection modes. *_template_items_editmode_mesh_select_mode(params), # Loop Select with alt. Double click in case MMB emulation is on (below). - ("mesh.loop_select", {"type": params.select_mouse, "value": params.select_mouse_value, "alt": True}, None), - ("mesh.loop_select", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "alt": True}, + ("mesh.loop_select", + {"type": params.select_mouse, "value": params.select_mouse_value, "alt": True}, None), + ("mesh.loop_select", + {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "alt": True}, {"properties": [("toggle", True)]}), # Selection - ("mesh.edgering_select", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "alt": True}, None), - ("mesh.edgering_select", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "ctrl": True, "alt": True}, + ("mesh.edgering_select", + {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "alt": True}, None), + ("mesh.edgering_select", + {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "ctrl": True, "alt": True}, {"properties": [("toggle", True)]}), ("mesh.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value_fallback, "ctrl": True}, @@ -4833,8 +4922,10 @@ def km_mesh(params): *_template_items_select_actions(params, "mesh.select_all"), ("mesh.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("mesh.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), - ("mesh.select_next_item", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "shift": True, "ctrl": True, "repeat": True}, None), - ("mesh.select_prev_item", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "shift": True, "ctrl": True, "repeat": True}, None), + ("mesh.select_next_item", + {"type": 'NUMPAD_PLUS', "value": 'PRESS', "shift": True, "ctrl": True, "repeat": True}, None), + ("mesh.select_prev_item", + {"type": 'NUMPAD_MINUS', "value": 'PRESS', "shift": True, "ctrl": True, "repeat": True}, None), ("mesh.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), ("mesh.select_linked_pick", {"type": 'L', "value": 'PRESS'}, {"properties": [("deselect", False)]}), @@ -4917,16 +5008,20 @@ def km_mesh(params): {"properties": [("extend", True)]}), ("mesh.loop_select", {"type": params.select_mouse, "value": 'DOUBLE_CLICK', "alt": True}, {"properties": [("deselect", True)]}), - ("mesh.edgering_select", {"type": params.select_mouse, "value": 'DOUBLE_CLICK', "ctrl": True}, None), - ("mesh.edgering_select", {"type": params.select_mouse, "value": 'DOUBLE_CLICK', "shift": True, "ctrl": True}, + ("mesh.edgering_select", + {"type": params.select_mouse, "value": 'DOUBLE_CLICK', "ctrl": True}, None), + ("mesh.edgering_select", + {"type": params.select_mouse, "value": 'DOUBLE_CLICK', "shift": True, "ctrl": True}, {"properties": [("toggle", True)]}), ]) if params.legacy: items.extend([ ("mesh.poke", {"type": 'P', "value": 'PRESS', "alt": True}, None), - ("mesh.select_non_manifold", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), - ("mesh.faces_select_linked_flat", {"type": 'F', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), + ("mesh.select_non_manifold", + {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), + ("mesh.faces_select_linked_flat", + {"type": 'F', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), ("mesh.spin", {"type": 'R', "value": 'PRESS', "alt": True}, None), ("mesh.beautify_fill", {"type": 'F', "value": 'PRESS', "shift": True, "alt": True}, None), *_template_items_object_subdivision_set(), @@ -5795,6 +5890,7 @@ def km_paint_stroke_modal(_params): return keymap + def km_sculpt_expand_modal(_params): items = [] keymap = ( @@ -7072,12 +7168,16 @@ def km_3d_view_tool_paint_gpencil_eyedropper(params): "3D View Tool: Paint Gpencil, Eyedropper", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("ui.eyedropper_gpencil_color", {"type": params.tool_mouse, "value": 'PRESS'}, None), - ("ui.eyedropper_gpencil_color", {"type": params.tool_mouse, "value": 'PRESS', "shift": True}, None), - ("ui.eyedropper_gpencil_color", {"type": params.tool_mouse, "value": 'PRESS', "shift": True, "ctrl": True}, None), + ("ui.eyedropper_gpencil_color", + {"type": params.tool_mouse, "value": 'PRESS'}, None), + ("ui.eyedropper_gpencil_color", + {"type": params.tool_mouse, "value": 'PRESS', "shift": True}, None), + ("ui.eyedropper_gpencil_color", + {"type": params.tool_mouse, "value": 'PRESS', "shift": True, "ctrl": True}, None), ]}, ) + def km_3d_view_tool_paint_gpencil_interpolate(params): return ( "3D View Tool: Paint Gpencil, Interpolate", @@ -7088,6 +7188,7 @@ def km_3d_view_tool_paint_gpencil_interpolate(params): ]}, ) + def km_3d_view_tool_edit_gpencil_select(params, *, fallback): return ( _fallback_id("3D View Tool: Edit Gpencil, Tweak", fallback), @@ -7100,6 +7201,7 @@ def km_3d_view_tool_edit_gpencil_select(params, *, fallback): ]}, ) + def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback): return ( _fallback_id("3D View Tool: Edit Gpencil, Select Box", fallback), @@ -7111,6 +7213,7 @@ def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback): ]}, ) + def km_3d_view_tool_edit_gpencil_select_circle(params, *, fallback): return ( _fallback_id("3D View Tool: Edit Gpencil, Select Circle", fallback), @@ -7303,7 +7406,12 @@ def km_sequencer_editor_tool_blade(_params): {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ ("sequencer.split", {"type": 'LEFTMOUSE', "value": 'PRESS'}, - {"properties": [("type", 'SOFT'), ("side", 'NO_CHANGE'), ("use_cursor_position", True), ("ignore_selection", True)]}), + {"properties": [ + ("type", 'SOFT'), + ("side", 'NO_CHANGE'), + ("use_cursor_position", True), + ("ignore_selection", True), + ]}), ]}, ) From f7a492d460543fd42386cb0c941d247ea902f290 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Wed, 22 Sep 2021 14:02:19 +0100 Subject: [PATCH 0091/1500] Animation: Pose Slide Operator - Blend to Neighbour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new operator to the pose slider tools that blends the current pose with the neighbouring poses in the timeline. The operator can be called in pose mode with Shift+Alt+E or from the "pose" menu under "In betweens/Blend to Neighbour" Reviewed by: Sybren A. Stüvel Differential Revision: https://developer.blender.org/D9137#inline-105214 Ref: D9137 --- .../keyconfig/keymap_data/blender_default.py | 1 + release/scripts/startup/bl_ui/space_view3d.py | 2 + .../editors/armature/armature_intern.h | 1 + .../blender/editors/armature/armature_ops.c | 1 + source/blender/editors/armature/pose_slide.c | 102 +++++++++++++++++- 5 files changed, 105 insertions(+), 2 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 15d6d44d240..1b0da23aa4a 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4265,6 +4265,7 @@ def km_pose(params): ("pose.push", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), ("pose.relax", {"type": 'E', "value": 'PRESS', "alt": True}, None), ("pose.breakdown", {"type": 'E', "value": 'PRESS', "shift": True}, None), + ("pose.blend_to_neighbour", {"type": 'E', "value": 'PRESS', "shift": True, "alt": True}, None), op_menu("VIEW3D_MT_pose_propagate", {"type": 'P', "value": 'PRESS', "alt": True}), *( (("object.hide_collection", diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index a332295715c..3879f7de250 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -3453,6 +3453,7 @@ class VIEW3D_MT_pose_slide(Menu): layout.operator("pose.push") layout.operator("pose.relax") layout.operator("pose.breakdown") + layout.operator("pose.blend_to_neighbour") class VIEW3D_MT_pose_propagate(Menu): @@ -3605,6 +3606,7 @@ class VIEW3D_MT_pose_context_menu(Menu): layout.operator("pose.push") layout.operator("pose.relax") layout.operator("pose.breakdown") + layout.operator("pose.blend_to_neighbour") layout.separator() diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index f9950d27e97..696355324e6 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -216,6 +216,7 @@ void POSE_OT_relax(struct wmOperatorType *ot); void POSE_OT_push_rest(struct wmOperatorType *ot); void POSE_OT_relax_rest(struct wmOperatorType *ot); void POSE_OT_breakdown(struct wmOperatorType *ot); +void POSE_OT_blend_to_neighbours(struct wmOperatorType *ot); void POSE_OT_propagate(struct wmOperatorType *ot); diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index fbd89106de5..a1070a8823a 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -150,6 +150,7 @@ void ED_operatortypes_armature(void) WM_operatortype_append(POSE_OT_push_rest); WM_operatortype_append(POSE_OT_relax_rest); WM_operatortype_append(POSE_OT_breakdown); + WM_operatortype_append(POSE_OT_blend_to_neighbours); } void ED_operatormacros_armature(void) diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index f23376867af..b273d3aac76 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -117,6 +117,7 @@ typedef enum ePoseSlide_Modes { POSESLIDE_BREAKDOWN, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST, + POSESLIDE_BLEND, } ePoseSlide_Modes; /** Transforms/Channels to Affect. */ @@ -423,6 +424,25 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo (*val) = ((sVal * w2) + (eVal * w1)); break; } + case POSESLIDE_BLEND: /* Blend the current pose with the previous (<50%) or next key (>50%). */ + { + /* FCurve value on current frame. */ + const float cVal = evaluate_fcurve(fcu, cframe); + const float factor = ED_slider_factor_get(pso->slider); + /* Convert factor to absolute 0-1 range. */ + const float blend_factor = fabs((factor - 0.5f) * 2); + + if (factor < 0.5) { + /* Blend to previous key. */ + (*val) = (cVal * (1 - blend_factor)) + (sVal * blend_factor); + } + else { + /* Blend to next key. */ + (*val) = (cVal * (1 - blend_factor)) + (eVal * blend_factor); + } + + break; + } /* Those are handled in pose_slide_rest_pose_apply. */ case POSESLIDE_PUSH_REST: case POSESLIDE_RELAX_REST: { @@ -614,8 +634,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) interp_qt_qtqt(quat_final, quat_prev, quat_next, ED_slider_factor_get(pso->slider)); } - else { - /* POSESLIDE_PUSH and POSESLIDE_RELAX. */ + else if (pso->mode == POSESLIDE_PUSH || pso->mode == POSESLIDE_RELAX) { float quat_breakdown[4]; float quat_curr[4]; @@ -638,6 +657,32 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, ED_slider_factor_get(pso->slider)); } } + else if (pso->mode == POSESLIDE_BLEND) { + float quat_blend[4]; + float quat_curr[4]; + + copy_qt_qt(quat_curr, pchan->quat); + + if (ED_slider_factor_get(pso->slider) < 0.5) { + quat_blend[0] = evaluate_fcurve(fcu_w, prevFrameF); + quat_blend[1] = evaluate_fcurve(fcu_x, prevFrameF); + quat_blend[2] = evaluate_fcurve(fcu_y, prevFrameF); + quat_blend[3] = evaluate_fcurve(fcu_z, prevFrameF); + } + else { + quat_blend[0] = evaluate_fcurve(fcu_w, nextFrameF); + quat_blend[1] = evaluate_fcurve(fcu_x, nextFrameF); + quat_blend[2] = evaluate_fcurve(fcu_y, nextFrameF); + quat_blend[3] = evaluate_fcurve(fcu_z, nextFrameF); + } + + normalize_qt(quat_blend); + normalize_qt(quat_curr); + + const float blend_factor = fabs((ED_slider_factor_get(pso->slider) - 0.5f) * 2); + + interp_qt_qtqt(quat_final, quat_curr, quat_blend, blend_factor); + } /* Apply final to the pose bone, keeping compatible for similar keyframe positions. */ quat_to_compatible_quat(pchan->quat, quat_final, pchan->quat); @@ -868,6 +913,9 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) case POSESLIDE_BREAKDOWN: strcpy(mode_str, TIP_("Breakdown")); break; + case POSESLIDE_BLEND: + strcpy(mode_str, TIP_("Blend To Neighbour")); + break; default: /* Unknown. */ @@ -1660,6 +1708,56 @@ void POSE_OT_breakdown(wmOperatorType *ot) pose_slide_opdef_properties(ot); } +/* ........................ */ +static int pose_slide_blend_to_neighbours_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + /* Initialize data. */ + if (pose_slide_init(C, op, POSESLIDE_BLEND) == 0) { + pose_slide_exit(C, op); + return OPERATOR_CANCELLED; + } + + /* Do common setup work. */ + return pose_slide_invoke_common(C, op, event); +} + +static int pose_slide_blend_to_neighbours_exec(bContext *C, wmOperator *op) +{ + tPoseSlideOp *pso; + + /* Initialize data (from RNA-props). */ + if (pose_slide_init(C, op, POSESLIDE_BLEND) == 0) { + pose_slide_exit(C, op); + return OPERATOR_CANCELLED; + } + + pso = op->customdata; + + /* Do common exec work. */ + return pose_slide_exec_common(C, op, pso); +} + +void POSE_OT_blend_to_neighbours(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Blend To Neighbour"; + ot->idname = "POSE_OT_blend_to_neighbour"; + ot->description = "Blend from current position to previous or next keyframe"; + + /* Callbacks. */ + ot->exec = pose_slide_blend_to_neighbours_exec; + ot->invoke = pose_slide_blend_to_neighbours_invoke; + ot->modal = pose_slide_modal; + ot->cancel = pose_slide_cancel; + ot->poll = ED_operator_posemode; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; + + /* Properties. */ + pose_slide_opdef_properties(ot); +} + /* **************************************************** */ /* B) Pose Propagate */ From 9f6313498a0af386f08ed17c83bf33b8c2c3b5b3 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 22 Sep 2021 13:05:09 +0200 Subject: [PATCH 0092/1500] Fix missing samples count pass when using tiles Samples count pass is normalized to the overall number of samples. This means that we need to store actual value of the samples in the tile buffer file. A bit annoying to pull all those settings to BufferParams and need to find a more generic solution, but for now this is easiest and a quickest solution. Differential Revision: https://developer.blender.org/D12597 --- intern/cycles/integrator/path_trace.cpp | 5 +---- intern/cycles/render/buffers.cpp | 1 + intern/cycles/render/buffers.h | 1 + intern/cycles/render/session.cpp | 1 + 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index bc43747718d..b62a06aea43 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -933,10 +933,7 @@ void PathTrace::process_full_buffer_from_disk(string_view filename) int PathTrace::get_num_render_tile_samples() const { if (full_frame_state_.render_buffers) { - /* If the full-frame buffer is read from disk the number of samples is not used as there is a - * sample count pass for that in the buffer. Just avoid access to badly defined state of the - * path state. */ - return 0; + return full_frame_state_.render_buffers->params.samples; } return render_scheduler_.get_num_rendered_samples(); diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 186699596ac..1882510cd70 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -105,6 +105,7 @@ NODE_DEFINE(BufferParams) SOCKET_STRING(layer, "Layer", ustring()); SOCKET_STRING(view, "View", ustring()); + SOCKET_INT(samples, "Samples", 0); SOCKET_FLOAT(exposure, "Exposure", 1.0f); SOCKET_BOOLEAN(use_approximate_shadow_catcher, "Use Approximate Shadow Catcher", false); SOCKET_BOOLEAN(use_transparent_background, "Transparent Background", false); diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h index a07e7289566..184ac7197af 100644 --- a/intern/cycles/render/buffers.h +++ b/intern/cycles/render/buffers.h @@ -98,6 +98,7 @@ class BufferParams : public Node { vector passes; ustring layer; ustring view; + int samples = 0; float exposure = 1.0f; bool use_approximate_shadow_catcher = false; bool use_transparent_background = false; diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index c39232be2b0..47eeffd97fe 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -407,6 +407,7 @@ void Session::do_delayed_reset() buffer_params_ = delayed_reset_.buffer_params; /* Store parameters used for buffers access outside of scene graph. */ + buffer_params_.samples = params.samples; buffer_params_.exposure = scene->film->get_exposure(); buffer_params_.use_approximate_shadow_catcher = scene->film->get_use_approximate_shadow_catcher(); From 368b56c9a132f7a37c638d16fc4c263d211bc4ed Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 22 Sep 2021 15:50:38 +0200 Subject: [PATCH 0093/1500] GPencil: Split Weight modifier in two to make more consistent The old modifier had two modes, but it is better to keep separated as meshes. The UI has changed to be more consistent, including a new column type of modifiers. Note: The logic has not changed with the previous version of the modifier, just is a split on two modifiers.. Reviewed By: mendio, pablovazquez Differential Revision: https://developer.blender.org/D12586 --- .../editors/space_outliner/outliner_draw.c | 5 +- .../blender/gpencil_modifiers/CMakeLists.txt | 3 +- .../MOD_gpencil_modifiertypes.h | 3 +- .../intern/MOD_gpencil_util.c | 3 +- .../intern/MOD_gpencilweight_angle.c | 260 ++++++++++++++++++ ...weight.c => MOD_gpencilweight_proximity.c} | 122 +++----- .../makesdna/DNA_gpencil_modifier_defaults.h | 17 +- .../makesdna/DNA_gpencil_modifier_types.h | 49 ++-- source/blender/makesdna/intern/dna_defaults.c | 6 +- .../makesrna/intern/rna_gpencil_modifier.c | 234 +++++++++++----- 10 files changed, 519 insertions(+), 183 deletions(-) create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c rename source/blender/gpencil_modifiers/intern/{MOD_gpencilweight.c => MOD_gpencilweight_proximity.c} (66%) diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index c06a1010168..7cdfb553da5 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -2358,7 +2358,10 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case eGpencilModifierType_Texture: data.icon = ICON_TEXTURE; break; - case eGpencilModifierType_Weight: + case eGpencilModifierType_WeightProximity: + data.icon = ICON_MOD_VERTEX_WEIGHT; + break; + case eGpencilModifierType_WeightAngle: data.icon = ICON_MOD_VERTEX_WEIGHT; break; diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index adf68e534bb..eb1f61b1862 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -69,7 +69,8 @@ set(SRC intern/MOD_gpencilthick.c intern/MOD_gpenciltime.c intern/MOD_gpenciltint.c - intern/MOD_gpencilweight.c + intern/MOD_gpencilweight_proximity.c + intern/MOD_gpencilweight_angle.c MOD_gpencil_lineart.h MOD_gpencil_modifiertypes.h diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h index 043186155b7..d9285f44a37 100644 --- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h +++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h @@ -44,7 +44,8 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Armature; extern GpencilModifierTypeInfo modifierType_Gpencil_Time; extern GpencilModifierTypeInfo modifierType_Gpencil_Multiply; extern GpencilModifierTypeInfo modifierType_Gpencil_Texture; -extern GpencilModifierTypeInfo modifierType_Gpencil_Weight; +extern GpencilModifierTypeInfo modifierType_Gpencil_WeightProximity; +extern GpencilModifierTypeInfo modifierType_Gpencil_WeightAngle; extern GpencilModifierTypeInfo modifierType_Gpencil_Lineart; extern GpencilModifierTypeInfo modifierType_Gpencil_Dash; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index 5eb1eeab780..df78ac8110e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -63,7 +63,8 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) INIT_GP_TYPE(Time); INIT_GP_TYPE(Multiply); INIT_GP_TYPE(Texture); - INIT_GP_TYPE(Weight); + INIT_GP_TYPE(WeightAngle); + INIT_GP_TYPE(WeightProximity); INIT_GP_TYPE(Lineart); INIT_GP_TYPE(Dash); #undef INIT_GP_TYPE diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c new file mode 100644 index 00000000000..2c0f3d2d8ad --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c @@ -0,0 +1,260 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021, Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup modifiers + */ + +#include + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_defaults.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_lib_query.h" +#include "BKE_modifier.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" + +#include "MOD_gpencil_modifiertypes.h" +#include "MOD_gpencil_ui_common.h" +#include "MOD_gpencil_util.h" + +static void initData(GpencilModifierData *md) +{ + WeightAngleGpencilModifierData *gpmd = (WeightAngleGpencilModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier)); + + MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(WeightAngleGpencilModifierData), modifier); +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copydata_generic(md, target); +} + +/* change stroke thickness */ +static void deformStroke(GpencilModifierData *md, + Depsgraph *UNUSED(depsgraph), + Object *ob, + bGPDlayer *gpl, + bGPDframe *UNUSED(gpf), + bGPDstroke *gps) +{ + WeightAngleGpencilModifierData *mmd = (WeightAngleGpencilModifierData *)md; + const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, + mmd->material, + mmd->pass_index, + mmd->layer_pass, + 1, + gpl, + gps, + mmd->flag & GP_WEIGHT_INVERT_LAYER, + mmd->flag & GP_WEIGHT_INVERT_PASS, + mmd->flag & GP_WEIGHT_INVERT_LAYERPASS, + mmd->flag & GP_WEIGHT_INVERT_MATERIAL)) { + return; + } + + const int target_def_nr = BKE_object_defgroup_name_index(ob, mmd->target_vgname); + + if (target_def_nr == -1) { + return; + } + + /* Use default Z up. */ + float vec_axis[3] = {0.0f, 0.0f, 1.0f}; + float axis[3] = {0.0f, 0.0f, 0.0f}; + axis[mmd->axis] = 1.0f; + float vec_ref[3]; + /* Apply modifier rotation (sub 90 degrees for Y axis due Z-Up vector). */ + float rot_angle = mmd->angle - ((mmd->axis == 1) ? M_PI_2 : 0.0f); + rotate_normalized_v3_v3v3fl(vec_ref, vec_axis, axis, rot_angle); + + /* Apply the rotation of the object. */ + if (mmd->space == GP_SPACE_LOCAL) { + mul_mat3_m4_v3(ob->obmat, vec_ref); + } + + /* Ensure there is a vertex group. */ + BKE_gpencil_dvert_ensure(gps); + + float weight_pt = 1.0f; + for (int i = 0; i < gps->totpoints; i++) { + MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; + /* Verify point is part of vertex group. */ + float weight = get_modifier_point_weight( + dvert, (mmd->flag & GP_WEIGHT_INVERT_VGROUP) != 0, def_nr); + if (weight < 0.0f) { + continue; + } + + /* Special case for single points. */ + if (gps->totpoints == 1) { + weight_pt = 1.0f; + break; + } + + bGPDspoint *pt1 = (i > 0) ? &gps->points[i] : &gps->points[i + 1]; + bGPDspoint *pt2 = (i > 0) ? &gps->points[i - 1] : &gps->points[i]; + float fpt1[3], fpt2[3]; + mul_v3_m4v3(fpt1, ob->obmat, &pt1->x); + mul_v3_m4v3(fpt2, ob->obmat, &pt2->x); + + float vec[3]; + sub_v3_v3v3(vec, fpt1, fpt2); + float angle = angle_on_axis_v3v3_v3(vec_ref, vec, axis); + /* Use sin to get a value between 0 and 1. */ + weight_pt = 1.0f - sin(angle); + + /* Invert weight if required. */ + if (mmd->flag & GP_WEIGHT_INVERT_OUTPUT) { + weight_pt = 1.0f - weight_pt; + } + /* Assign weight. */ + dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; + if (dvert != NULL) { + MDeformWeight *dw = BKE_defvert_ensure_index(dvert, target_def_nr); + if (dw) { + dw->weight = (mmd->flag & GP_WEIGHT_MULTIPLY_DATA) ? dw->weight * weight_pt : weight_pt; + CLAMP(dw->weight, mmd->min_weight, 1.0f); + } + } + } +} + +static void bakeModifier(struct Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) +{ + bGPdata *gpd = ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + deformStroke(md, depsgraph, ob, gpl, gpf, gps); + } + } + } +} + +static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + WeightAngleGpencilModifierData *mmd = (WeightAngleGpencilModifierData *)md; + + walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); +} + +static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) +{ + WeightAngleGpencilModifierData *mmd = (WeightAngleGpencilModifierData *)md; + + return (mmd->target_vgname[0] == '\0'); +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *row, *sub; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + row = uiLayoutRow(layout, true); + uiItemPointerR(row, ptr, "target_vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE); + sub = uiLayoutRow(row, true); + bool has_output = RNA_string_length(ptr, "target_vertex_group") != 0; + uiLayoutSetPropDecorate(sub, false); + uiLayoutSetActive(sub, has_output); + uiItemR(sub, ptr, "use_invert_output", 0, "", ICON_ARROW_LEFTRIGHT); + + uiItemR(layout, ptr, "angle", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "axis", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "space", 0, NULL, ICON_NONE); + + uiItemR(layout, ptr, "minimum_weight", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_multiply", 0, NULL, ICON_NONE); + + + gpencil_modifier_panel_end(layout, ptr); +} + +static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + gpencil_modifier_masking_panel_draw(panel, true, true); +} + +static void panelRegister(ARegionType *region_type) +{ + PanelType *panel_type = gpencil_modifier_panel_register( + region_type, eGpencilModifierType_WeightAngle, panel_draw); + + gpencil_modifier_subpanel_register( + region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); +} + +GpencilModifierTypeInfo modifierType_Gpencil_WeightAngle = { + /* name */ "Vertex Weight Angle", + /* structName */ "WeightAngleGpencilModifierData", + /* structSize */ sizeof(WeightAngleGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ 0, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + /* remapTime */ NULL, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ NULL, + /* panelRegister */ panelRegister, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c similarity index 66% rename from source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c rename to source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c index 686023a36d4..0885828a3a0 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c @@ -58,11 +58,11 @@ static void initData(GpencilModifierData *md) { - WeightGpencilModifierData *gpmd = (WeightGpencilModifierData *)md; + WeightProxGpencilModifierData *gpmd = (WeightProxGpencilModifierData *)md; BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier)); - MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(WeightGpencilModifierData), modifier); + MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(WeightProxGpencilModifierData), modifier); } static void copyData(const GpencilModifierData *md, GpencilModifierData *target) @@ -72,7 +72,7 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target) /* Calc distance between point and target object. */ static float calc_point_weight_by_distance(Object *ob, - WeightGpencilModifierData *mmd, + WeightProxGpencilModifierData *mmd, const float dist_max, const float dist_min, bGPDspoint *pt) @@ -103,9 +103,8 @@ static void deformStroke(GpencilModifierData *md, bGPDframe *UNUSED(gpf), bGPDstroke *gps) { - WeightGpencilModifierData *mmd = (WeightGpencilModifierData *)md; + WeightProxGpencilModifierData *mmd = (WeightProxGpencilModifierData *)md; const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); - const eWeightGpencilModifierMode mode = mmd->mode; if (!is_stroke_affected_by_modifier(ob, mmd->layername, @@ -130,20 +129,6 @@ static void deformStroke(GpencilModifierData *md, return; } - /* Use default Z up. */ - float vec_axis[3] = {0.0f, 0.0f, 1.0f}; - float axis[3] = {0.0f, 0.0f, 0.0f}; - axis[mmd->axis] = 1.0f; - float vec_ref[3]; - /* Apply modifier rotation (sub 90 degrees for Y axis due Z-Up vector). */ - float rot_angle = mmd->angle - ((mmd->axis == 1) ? M_PI_2 : 0.0f); - rotate_normalized_v3_v3v3fl(vec_ref, vec_axis, axis, rot_angle); - - /* Apply the rotation of the object. */ - if (mmd->space == GP_SPACE_LOCAL) { - mul_mat3_m4_v3(ob->obmat, vec_ref); - } - /* Ensure there is a vertex group. */ BKE_gpencil_dvert_ensure(gps); @@ -157,36 +142,9 @@ static void deformStroke(GpencilModifierData *md, continue; } - switch (mode) { - case GP_WEIGHT_MODE_DISTANCE: { - if (mmd->object) { - bGPDspoint *pt = &gps->points[i]; - weight_pt = calc_point_weight_by_distance(ob, mmd, dist_max, dist_min, pt); - } - break; - } - case GP_WEIGHT_MODE_ANGLE: { - /* Special case for single points. */ - if (gps->totpoints == 1) { - weight_pt = 1.0f; - break; - } - - bGPDspoint *pt1 = (i > 0) ? &gps->points[i] : &gps->points[i + 1]; - bGPDspoint *pt2 = (i > 0) ? &gps->points[i - 1] : &gps->points[i]; - float fpt1[3], fpt2[3]; - mul_v3_m4v3(fpt1, ob->obmat, &pt1->x); - mul_v3_m4v3(fpt2, ob->obmat, &pt2->x); - - float vec[3]; - sub_v3_v3v3(vec, fpt1, fpt2); - float angle = angle_on_axis_v3v3_v3(vec_ref, vec, axis); - /* Use sin to get a value between 0 and 1. */ - weight_pt = 1.0f - sin(angle); - break; - } - default: - break; + if (mmd->object) { + bGPDspoint *pt = &gps->points[i]; + weight_pt = calc_point_weight_by_distance(ob, mmd, dist_max, dist_min, pt); } /* Invert weight if required. */ @@ -198,7 +156,7 @@ static void deformStroke(GpencilModifierData *md, if (dvert != NULL) { MDeformWeight *dw = BKE_defvert_ensure_index(dvert, target_def_nr); if (dw) { - dw->weight = (mmd->flag & GP_WEIGHT_BLEND_DATA) ? dw->weight * weight_pt : weight_pt; + dw->weight = (mmd->flag & GP_WEIGHT_MULTIPLY_DATA) ? dw->weight * weight_pt : weight_pt; CLAMP(dw->weight, mmd->min_weight, 1.0f); } } @@ -223,7 +181,7 @@ static void bakeModifier(struct Main *UNUSED(bmain), static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) { - WeightGpencilModifierData *mmd = (WeightGpencilModifierData *)md; + WeightProxGpencilModifierData *mmd = (WeightProxGpencilModifierData *)md; walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP); @@ -233,7 +191,7 @@ static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx, const int UNUSED(mode)) { - WeightGpencilModifierData *mmd = (WeightGpencilModifierData *)md; + WeightProxGpencilModifierData *mmd = (WeightProxGpencilModifierData *)md; if (mmd->object != NULL) { DEG_add_object_relation( ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "GPencil Weight Modifier"); @@ -244,54 +202,36 @@ static void updateDepsgraph(GpencilModifierData *md, static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) { - WeightGpencilModifierData *mmd = (WeightGpencilModifierData *)md; + WeightProxGpencilModifierData *mmd = (WeightProxGpencilModifierData *)md; - return (mmd->target_vgname[0] == '\0'); + return ((mmd->target_vgname[0] == '\0') || (mmd->object == NULL)); } -static void distance_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); - - uiLayout *layout = panel->layout; - uiLayoutSetPropSep(layout, true); - - uiItemR(layout, ptr, "object", 0, NULL, ICON_CUBE); - uiLayout *sub = uiLayoutColumn(layout, true); - uiItemR(sub, ptr, "distance_start", 0, NULL, ICON_NONE); - uiItemR(sub, ptr, "distance_end", 0, "End", ICON_NONE); -} - -static void panel_draw(const bContext *C, Panel *panel) +static void panel_draw(const bContext *UNUSED(C), Panel *panel) { + uiLayout *row, *sub; uiLayout *layout = panel->layout; PointerRNA ob_ptr; PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); uiLayoutSetPropSep(layout, true); - uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); + row = uiLayoutRow(layout, true); + uiItemPointerR(row, ptr, "target_vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE); + sub = uiLayoutRow(row, true); + bool has_output = RNA_string_length(ptr, "target_vertex_group") != 0; + uiLayoutSetPropDecorate(sub, false); + uiLayoutSetActive(sub, has_output); + uiItemR(sub, ptr, "use_invert_output", 0, "", ICON_ARROW_LEFTRIGHT); - const eWeightGpencilModifierMode mode = RNA_enum_get(ptr, "mode"); + uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE); - uiItemPointerR(layout, ptr, "target_vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE); + sub = uiLayoutColumn(layout, true); + uiItemR(sub, ptr, "distance_start", 0, NULL, ICON_NONE); + uiItemR(sub, ptr, "distance_end", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "minimum_weight", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_invert_output", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_blend", 0, NULL, ICON_NONE); - - switch (mode) { - case GP_WEIGHT_MODE_DISTANCE: - distance_panel_draw(C, panel); - break; - case GP_WEIGHT_MODE_ANGLE: - uiItemR(layout, ptr, "angle", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "axis", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "space", 0, NULL, ICON_NONE); - break; - default: - break; - } + uiItemR(layout, ptr, "use_multiply", 0, NULL, ICON_NONE); gpencil_modifier_panel_end(layout, ptr); } @@ -304,16 +244,16 @@ static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( - region_type, eGpencilModifierType_Weight, panel_draw); + region_type, eGpencilModifierType_WeightProximity, panel_draw); gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); } -GpencilModifierTypeInfo modifierType_Gpencil_Weight = { - /* name */ "Vertex Weight", - /* structName */ "WeightGpencilModifierData", - /* structSize */ sizeof(WeightGpencilModifierData), +GpencilModifierTypeInfo modifierType_Gpencil_WeightProximity = { + /* name */ "Vertex Weight Proximity", + /* structName */ "WeightProxGpencilModifierData", + /* structSize */ sizeof(WeightProxGpencilModifierData), /* type */ eGpencilModifierTypeType_Gpencil, /* flags */ 0, diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 3a100c00999..2a3c6f4e3db 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -283,7 +283,20 @@ .colorband = NULL, \ } -#define _DNA_DEFAULT_WeightGpencilModifierData \ +#define _DNA_DEFAULT_WeightProxGpencilModifierData \ + { \ + .target_vgname = "", \ + .material = NULL, \ + .layername = "", \ + .vgname = "", \ + .pass_index = 0, \ + .flag = 0, \ + .layer_pass = 0, \ + .dist_start = 0.0f, \ + .dist_end = 20.0f, \ + } + +#define _DNA_DEFAULT_WeightAngleGpencilModifierData \ { \ .target_vgname = "", \ .material = NULL, \ @@ -293,8 +306,6 @@ .flag = 0, \ .axis = 1, \ .layer_pass = 0, \ - .dist_start = 0.0f, \ - .dist_end = 20.0f, \ } #define _DNA_DEFAULT_LineartGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 43469fbb46b..8d967a38808 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -55,8 +55,9 @@ typedef enum GpencilModifierType { eGpencilModifierType_Texture = 18, eGpencilModifierType_Lineart = 19, eGpencilModifierType_Length = 20, - eGpencilModifierType_Weight = 21, + eGpencilModifierType_WeightProximity = 21, eGpencilModifierType_Dash = 22, + eGpencilModifierType_WeightAngle = 23, /* Keep last. */ NUM_GREASEPENCIL_MODIFIER_TYPES, } GpencilModifierType; @@ -896,7 +897,7 @@ typedef enum eTextureGpencil_Mode { STROKE_AND_FILL = 2, } eTextureGpencil_Mode; -typedef struct WeightGpencilModifierData { +typedef struct WeightProxGpencilModifierData { GpencilModifierData modifier; /** Target vertexgroup name, MAX_VGROUP_NAME. */ char target_vgname[64]; @@ -914,22 +915,39 @@ typedef struct WeightGpencilModifierData { float min_weight; /** Custom index for passes. */ int layer_pass; - /** Calculation Mode. */ - short mode; - /** Axis. */ - short axis; - /** Angle */ - float angle; /** Start/end distances. */ float dist_start; float dist_end; - /** Space (Local/World). */ - short space; - char _pad[6]; /** Reference object */ struct Object *object; -} WeightGpencilModifierData; +} WeightProxGpencilModifierData; + +typedef struct WeightAngleGpencilModifierData { + GpencilModifierData modifier; + /** Target vertexgroup name, MAX_VGROUP_NAME. */ + char target_vgname[64]; + /** Material for filtering. */ + struct Material *material; + /** Layer name. */ + char layername[64]; + /** Optional vertexgroup filter name, MAX_VGROUP_NAME. */ + char vgname[64]; + /** Custom index for passes. */ + int pass_index; + /** Flags. */ + int flag; + /** Minimum valid weight (clamp value). */ + float min_weight; + /** Custom index for passes. */ + int layer_pass; + /** Axis. */ + short axis; + /** Space (Local/World). */ + short space; + /** Angle */ + float angle; +} WeightAngleGpencilModifierData; typedef enum eWeightGpencil_Flag { GP_WEIGHT_INVERT_LAYER = (1 << 0), @@ -937,15 +955,10 @@ typedef enum eWeightGpencil_Flag { GP_WEIGHT_INVERT_VGROUP = (1 << 2), GP_WEIGHT_INVERT_LAYERPASS = (1 << 3), GP_WEIGHT_INVERT_MATERIAL = (1 << 4), - GP_WEIGHT_BLEND_DATA = (1 << 5), + GP_WEIGHT_MULTIPLY_DATA = (1 << 5), GP_WEIGHT_INVERT_OUTPUT = (1 << 6), } eWeightGpencil_Flag; -typedef enum eWeightGpencilModifierMode { - GP_WEIGHT_MODE_DISTANCE = 0, - GP_WEIGHT_MODE_ANGLE = 1, -} eWeightGpencilModifierMode; - typedef enum eGpencilModifierSpace { GP_SPACE_LOCAL = 0, GP_SPACE_WORLD = 1, diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 4cb8610f6ac..2dbbb35c3ca 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -318,7 +318,8 @@ SDNA_DEFAULT_DECL_STRUCT(TextureGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(ThickGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(TimeGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(TintGpencilModifierData); -SDNA_DEFAULT_DECL_STRUCT(WeightGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(WeightProxGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(WeightAngleGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(LineartGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(LengthGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierData); @@ -548,7 +549,8 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(ThickGpencilModifierData), SDNA_DEFAULT_DECL(TimeGpencilModifierData), SDNA_DEFAULT_DECL(TintGpencilModifierData), - SDNA_DEFAULT_DECL(WeightGpencilModifierData), + SDNA_DEFAULT_DECL(WeightAngleGpencilModifierData), + SDNA_DEFAULT_DECL(WeightProxGpencilModifierData), SDNA_DEFAULT_DECL(LineartGpencilModifierData), SDNA_DEFAULT_DECL(LengthGpencilModifierData), SDNA_DEFAULT_DECL(DashGpencilModifierData), diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index d5ef7cf2651..a2d5b134056 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -58,6 +58,17 @@ #include "WM_types.h" const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { + {0, "", 0, N_("Modify"), ""}, + {eGpencilModifierType_WeightAngle, + "GP_WEIGHT_ANGLE", + ICON_MOD_VERTEX_WEIGHT, + "Vertex Weight Angle", + "Generate Vertex Weights base on stroke angle"}, + {eGpencilModifierType_WeightProximity, + "GP_WEIGHT_PROXIMITY", + ICON_MOD_VERTEX_WEIGHT, + "Vertex Weight Proximity", + "Generate Vertex Weights base on distance to object"}, {0, "", 0, N_("Generate"), ""}, {eGpencilModifierType_Array, "GP_ARRAY", @@ -104,11 +115,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_SUBSURF, "Subdivide", "Subdivide stroke adding more control points"}, - {eGpencilModifierType_Weight, - "GP_WEIGHT", - ICON_MOD_VERTEX_WEIGHT, - "Vertex Weight", - "Generate Vertex Weights"}, {0, "", 0, N_("Deform"), ""}, {eGpencilModifierType_Armature, "GP_ARMATURE", @@ -244,8 +250,10 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr) return &RNA_TintGpencilModifier; case eGpencilModifierType_Time: return &RNA_TimeGpencilModifier; - case eGpencilModifierType_Weight: - return &RNA_WeightGpencilModifier; + case eGpencilModifierType_WeightProximity: + return &RNA_WeightProxGpencilModifier; + case eGpencilModifierType_WeightAngle: + return &RNA_WeightAngleGpencilModifier; case eGpencilModifierType_Color: return &RNA_ColorGpencilModifier; case eGpencilModifierType_Array: @@ -346,8 +354,10 @@ RNA_GP_MOD_VGROUP_NAME_SET(Offset, vgname); RNA_GP_MOD_VGROUP_NAME_SET(Armature, vgname); RNA_GP_MOD_VGROUP_NAME_SET(Texture, vgname); RNA_GP_MOD_VGROUP_NAME_SET(Tint, vgname); -RNA_GP_MOD_VGROUP_NAME_SET(Weight, target_vgname); -RNA_GP_MOD_VGROUP_NAME_SET(Weight, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(WeightProx, target_vgname); +RNA_GP_MOD_VGROUP_NAME_SET(WeightProx, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(WeightAngle, target_vgname); +RNA_GP_MOD_VGROUP_NAME_SET(WeightAngle, vgname); RNA_GP_MOD_VGROUP_NAME_SET(Lineart, vgname); # undef RNA_GP_MOD_VGROUP_NAME_SET @@ -380,7 +390,7 @@ static void greasepencil_modifier_object_set(Object *self, RNA_GP_MOD_OBJECT_SET(Armature, object, OB_ARMATURE); RNA_GP_MOD_OBJECT_SET(Lattice, object, OB_LATTICE); RNA_GP_MOD_OBJECT_SET(Mirror, object, OB_EMPTY); -RNA_GP_MOD_OBJECT_SET(Weight, object, OB_EMPTY); +RNA_GP_MOD_OBJECT_SET(WeightProx, object, OB_EMPTY); # undef RNA_GP_MOD_OBJECT_SET @@ -554,11 +564,21 @@ static void rna_ThickGpencilModifier_material_set(PointerRNA *ptr, rna_GpencilModifier_material_set(ptr, value, ma_target, reports); } -static void rna_WeightGpencilModifier_material_set(PointerRNA *ptr, - PointerRNA value, - struct ReportList *reports) +static void rna_WeightProxGpencilModifier_material_set(PointerRNA *ptr, + PointerRNA value, + struct ReportList *reports) { - WeightGpencilModifierData *tmd = (WeightGpencilModifierData *)ptr->data; + WeightProxGpencilModifierData *tmd = (WeightProxGpencilModifierData *)ptr->data; + Material **ma_target = &tmd->material; + + rna_GpencilModifier_material_set(ptr, value, ma_target, reports); +} + +static void rna_WeightAngleGpencilModifier_material_set(PointerRNA *ptr, + PointerRNA value, + struct ReportList *reports) +{ + WeightAngleGpencilModifierData *tmd = (WeightAngleGpencilModifierData *)ptr->data; Material **ma_target = &tmd->material; rna_GpencilModifier_material_set(ptr, value, ma_target, reports); @@ -2783,24 +2803,129 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna) RNA_define_lib_overridable(false); } -static void rna_def_modifier_gpencilweight(BlenderRNA *brna) +static void rna_def_modifier_gpencilweight_proximity(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "WeightProxGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Weight Modifier Proximity", "Calculate Vertex Weight dynamically"); + RNA_def_struct_sdna(srna, "WeightProxGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_VERTEX_WEIGHT); + + RNA_define_lib_overridable(true); + + prop = RNA_def_property(srna, "target_vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "target_vgname"); + RNA_def_property_ui_text(prop, "Vertex Group", "Output Vertex group"); + RNA_def_property_string_funcs( + prop, NULL, NULL, "rna_WeightProxGpencilModifier_target_vgname_set"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_multiply", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_MULTIPLY_DATA); + RNA_def_property_ui_text( + prop, + "Multiply Weights", + "Multiply the calculated weights with the existing values in the vertex group"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_invert_output", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_OUTPUT); + RNA_def_property_ui_text(prop, "Invert", "Invert output weight values"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "layername"); + RNA_def_property_ui_text(prop, "Layer", "Layer name"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, + NULL, + "rna_WeightProxGpencilModifier_material_set", + NULL, + "rna_GpencilModifier_material_poll"); + RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "vgname"); + RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modulating the deform"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_WeightProxGpencilModifier_vgname_set"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Distance reference object */ + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Target Object", "Object used as distance reference"); + RNA_def_property_pointer_funcs( + prop, NULL, "rna_WeightProxGpencilModifier_object_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + + prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "dist_start"); + RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Lowest", "Start value for distance calculation"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "minimum_weight", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "min_weight"); + RNA_def_property_ui_text(prop, "Minimum", "Minimum value for vertex weight"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "dist_end"); + RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Highest", "Max value for distance calculation"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "pass_index"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_LAYER); + RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_MATERIAL); + RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_vertex", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "layer_pass"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Layer pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_LAYERPASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + RNA_define_lib_overridable(false); +} + +static void rna_def_modifier_gpencilweight_angle(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem mode_items[] = { - {GP_WEIGHT_MODE_DISTANCE, - "DISTANCE", - 0, - "Distance", - "Calculate weights depending on the distance to the target object"}, - {GP_WEIGHT_MODE_ANGLE, - "ANGLE", - 0, - "Angle", - "Calculate weights depending on the stroke orientation"}, - {0, NULL, 0, NULL, NULL}, - }; static const EnumPropertyItem axis_items[] = { {0, "X", 0, "X", ""}, {1, "Y", 0, "Y", ""}, @@ -2814,34 +2939,31 @@ static void rna_def_modifier_gpencilweight(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - srna = RNA_def_struct(brna, "WeightGpencilModifier", "GpencilModifier"); - RNA_def_struct_ui_text(srna, "Weight Modifier", "Calculate Vertex Weight dynamically"); - RNA_def_struct_sdna(srna, "WeightGpencilModifierData"); + srna = RNA_def_struct(brna, "WeightAngleGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Weight Modifier Amgle", "Calculate Vertex Weight dynamically"); + RNA_def_struct_sdna(srna, "WeightAngleGpencilModifierData"); RNA_def_struct_ui_icon(srna, ICON_MOD_VERTEX_WEIGHT); RNA_define_lib_overridable(true); - prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "mode"); - RNA_def_property_enum_items(prop, mode_items); - RNA_def_property_ui_text(prop, "Mode", ""); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "target_vertex_group", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "target_vgname"); - RNA_def_property_ui_text(prop, "Output", "Output Vertex group"); - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_WeightGpencilModifier_target_vgname_set"); + RNA_def_property_ui_text(prop, "Vertex Group", "Output Vertex group"); + RNA_def_property_string_funcs( + prop, NULL, NULL, "rna_WeightProxGpencilModifier_target_vgname_set"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "use_blend", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_BLEND_DATA); + prop = RNA_def_property(srna, "use_multiply", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_MULTIPLY_DATA); RNA_def_property_ui_text( - prop, "Blend", "Blend results with existing weights in output weight group"); + prop, + "Multiply Weights", + "Multiply the calculated weights with the existing values in the vertex group"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_invert_output", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_OUTPUT); - RNA_def_property_ui_text(prop, "Invert", "Invert weight values"); + RNA_def_property_ui_text(prop, "Invert", "Invert output weight values"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE); @@ -2871,7 +2993,7 @@ static void rna_def_modifier_gpencilweight(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_pointer_funcs(prop, NULL, - "rna_WeightGpencilModifier_material_set", + "rna_WeightAngleGpencilModifier_material_set", NULL, "rna_GpencilModifier_material_poll"); RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect"); @@ -2880,20 +3002,7 @@ static void rna_def_modifier_gpencilweight(BlenderRNA *brna) prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "vgname"); RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modulating the deform"); - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_WeightGpencilModifier_vgname_set"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - /* Distance reference object */ - prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); - RNA_def_property_ui_text(prop, "Object", "Object used as distance reference"); - RNA_def_property_pointer_funcs(prop, NULL, "rna_WeightGpencilModifier_object_set", NULL, NULL); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); - - prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "dist_start"); - RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); - RNA_def_property_ui_text(prop, "Distance Start", "Start value for distance calculation"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_WeightAngleGpencilModifier_vgname_set"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "minimum_weight", PROP_FLOAT, PROP_FACTOR); @@ -2901,12 +3010,6 @@ static void rna_def_modifier_gpencilweight(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Minimum", "Minimum value for vertex weight"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "dist_end"); - RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); - RNA_def_property_ui_text(prop, "Distance End", "End value for distance calculation"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "pass_index"); RNA_def_property_range(prop, 0, 100); @@ -3605,7 +3708,8 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna) rna_def_modifier_gpencilarmature(brna); rna_def_modifier_gpencilmultiply(brna); rna_def_modifier_gpenciltexture(brna); - rna_def_modifier_gpencilweight(brna); + rna_def_modifier_gpencilweight_angle(brna); + rna_def_modifier_gpencilweight_proximity(brna); rna_def_modifier_gpencillineart(brna); rna_def_modifier_gpencillength(brna); rna_def_modifier_gpencildash(brna); From 53e7c64be7f1c7aebf65aa6082ec51146da387f5 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 22 Sep 2021 15:37:06 +0200 Subject: [PATCH 0094/1500] Fix T91597: Cycles volume scatter visibility on lights not working --- intern/cycles/kernel/integrator/integrator_shade_volume.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index d44890f800e..095a28ac505 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -726,6 +726,10 @@ ccl_device_forceinline void integrate_volume_direct_light(INTEGRATOR_STATE_ARGS, } } + if (ls->shader & SHADER_EXCLUDE_SCATTER) { + return; + } + /* Evaluate light shader. * * TODO: can we reuse sd memory? In theory we can move this after From e57ce464c21f405edacbfb806114e3acfdcc1410 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 22 Sep 2021 15:53:04 +0200 Subject: [PATCH 0095/1500] Fix T91600: Cycles viewport not updaing on metaball changes --- intern/cycles/blender/blender_geometry.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp index b1de37dac10..fca8cb9eda3 100644 --- a/intern/cycles/blender/blender_geometry.cpp +++ b/intern/cycles/blender/blender_geometry.cpp @@ -80,7 +80,9 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, { /* Test if we can instance or if the object is modified. */ Geometry::Type geom_type = determine_geom_type(b_ob_info, use_particle_hair); - GeometryKey key(b_ob_info.object_data, geom_type); + BL::ID b_key_id = (BKE_object_is_modified(b_ob_info.real_object)) ? b_ob_info.real_object : + b_ob_info.object_data; + GeometryKey key(b_key_id.ptr.data, geom_type); /* Find shader indices. */ array used_shaders = find_used_shaders(b_ob_info.iter_object); @@ -110,7 +112,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, } else { /* Test if we need to update existing geometry. */ - sync = geometry_map.update(geom, b_ob_info.object_data); + sync = geometry_map.update(geom, b_key_id); } if (!sync) { From 204b01a254ac2445fea217e5211b2ed6aef631ca Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 22 Sep 2021 16:01:58 +0200 Subject: [PATCH 0096/1500] Fix T91590: Cycles specular baking not using smooth normal --- .../kernel/integrator/integrator_init_from_bake.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/intern/cycles/kernel/integrator/integrator_init_from_bake.h b/intern/cycles/kernel/integrator/integrator_init_from_bake.h index 4898ff936c6..96db606cee1 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_bake.h +++ b/intern/cycles/kernel/integrator/integrator_init_from_bake.h @@ -112,8 +112,6 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, float3 P, Ng; int shader; triangle_point_normal(kg, kernel_data.bake.object_index, prim, u, v, &P, &Ng, &shader); - shader &= SHADER_MASK; - if (kernel_data.film.pass_background != PASS_UNUSED) { /* Environment baking. */ @@ -132,11 +130,13 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, } else { /* Surface baking. */ + const float3 N = (shader & SHADER_SMOOTH_NORMAL) ? triangle_smooth_normal(kg, Ng, prim, u, v) : + Ng; /* Setup ray. */ Ray ray ccl_optional_struct_init; - ray.P = P + Ng; - ray.D = -Ng; + ray.P = P + N; + ray.D = -N; ray.t = FLT_MAX; ray.time = 0.5f; @@ -166,12 +166,13 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, integrator_state_write_isect(INTEGRATOR_STATE_PASS, &isect); /* Setup next kernel to execute. */ - const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; + const int shader_index = shader & SHADER_MASK; + const int shader_flags = kernel_tex_fetch(__shaders, shader_index).flags; if ((shader_flags & SD_HAS_RAYTRACE) || (kernel_data.film.pass_ao != PASS_UNUSED)) { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader_index); } else { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader_index); } } From 0d350e0193f1af82274ba78f5f93702c14646a86 Mon Sep 17 00:00:00 2001 From: dilithjay Date: Wed, 22 Sep 2021 20:11:12 +0530 Subject: [PATCH 0097/1500] Geometry Nodes: Curve Fillet Node This node can be used to fillet splines at control points to create a circular arc. The implementation roughly follows T89227's design. The node works in two main modes: Bezier and Poly * Bezier: Creates a circular arc at vertices by changing handle lengths (applicable only for Bezier splines). * Poly: Creates a circular arc by creating vertices (as many as defined by the Count fields input) along the arc (applicable for all spline types). In both modes, the radius of the created arc is defined by the Radius fields input. The Limit Radius attribute can be enabled to prevent overlapping when the defined radius exceeds the maximum possible radius for a given point. Reviewed By: Hans Goudey Differential Revision: https://developer.blender.org/D12115 --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesdna/DNA_node_types.h | 10 + source/blender/makesrna/intern/rna_nodetree.c | 26 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../geometry/nodes/node_geo_curve_fillet.cc | 629 ++++++++++++++++++ source/tools | 2 +- 13 files changed, 675 insertions(+), 4 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc diff --git a/release/datafiles/locale b/release/datafiles/locale index 62e82958a76..94c39b5832b 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 62e82958a760dad775d9b3387d7fb535fd6de4c6 +Subproject commit 94c39b5832b9ef3b56ed94ce4011412e3d776eb2 diff --git a/release/scripts/addons b/release/scripts/addons index 4475cbd11a6..ecf30de46c3 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 4475cbd11a636382d57571e0f5dfeff1f90bd6b7 +Subproject commit ecf30de46c368ffddad259c125402a38e6093382 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 788441f2930..42da56aa737 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 788441f2930465bbfba8f0797b12dcef1d46694d +Subproject commit 42da56aa73726710107031787af5eea186797984 diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 77ffb609dd2..9ad162da7dc 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -530,6 +530,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveParameter", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeInputTangent", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeCurveSample", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeCurveFillet", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ NodeItem("GeometryNodeCurvePrimitiveLine"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 42e2cda8de3..21ca65baf00 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1499,6 +1499,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_INPUT_TANGENT 1086 #define GEO_NODE_STRING_JOIN 1087 #define GEO_NODE_CURVE_PARAMETER 1088 +#define GEO_NODE_CURVE_FILLET 1089 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 2d0239740f8..3c54d88c93a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5200,6 +5200,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_set_handles(); register_node_type_geo_curve_spline_type(); register_node_type_geo_curve_subdivide(); + register_node_type_geo_curve_fillet(); register_node_type_geo_curve_to_mesh(); register_node_type_geo_curve_to_points(); register_node_type_geo_curve_trim(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index cf159a1e28d..18545666796 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1447,6 +1447,11 @@ typedef struct NodeGeometryCurveSubdivide { uint8_t cuts_type; } NodeGeometryCurveSubdivide; +typedef struct NodeGeometryCurveFillet { + /* GeometryNodeCurveFilletMode. */ + uint8_t mode; +} NodeGeometryCurveFillet; + typedef struct NodeGeometryCurveTrim { /* GeometryNodeCurveSampleMode. */ uint8_t mode; @@ -2060,6 +2065,11 @@ typedef enum GeometryNodeCurveSampleMode { GEO_NODE_CURVE_SAMPLE_LENGTH = 1, } GeometryNodeCurveSampleMode; +typedef enum GeometryNodeCurveFilletMode { + GEO_NODE_CURVE_FILLET_BEZIER = 0, + GEO_NODE_CURVE_FILLET_POLY = 1, +} GeometryNodeCurveFilletMode; + typedef enum GeometryNodeAttributeTransferMapMode { GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index ec53f35df4c..b631e76c094 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10193,6 +10193,32 @@ static void def_geo_curve_subdivide(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_fillet(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_FILLET_BEZIER, + "BEZIER", + 0, + "Bezier", + "Align Bezier handles to create circular arcs at each control point"}, + {GEO_NODE_CURVE_FILLET_POLY, + "POLY", + 0, + "Poly", + "Add control points along a circular arc (handle type is vector if Bezier Spline)"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveFillet", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "How to choose number of vertices on fillet"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_to_points(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index a8795649ede..e6af3ecafbc 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -191,6 +191,7 @@ set(SRC geometry/nodes/node_geo_curve_set_handles.cc geometry/nodes/node_geo_curve_spline_type.cc geometry/nodes/node_geo_curve_subdivide.cc + geometry/nodes/node_geo_curve_fillet.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_to_points.cc geometry/nodes/node_geo_curve_trim.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 24f60263d8a..47bc54132eb 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -73,6 +73,7 @@ void register_node_type_geo_curve_reverse(void); void register_node_type_geo_curve_set_handles(void); void register_node_type_geo_curve_spline_type(void); void register_node_type_geo_curve_subdivide(void); +void register_node_type_geo_curve_fillet(void); void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 8fb18e839a7..ab673d814bb 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -328,6 +328,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_prim DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Curve Fillet", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc new file mode 100644 index 00000000000..830cfcc8331 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -0,0 +1,629 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "DNA_node_types.h" + +#include "node_geometry_util.hh" + +#include "BKE_spline.hh" + +namespace blender::nodes { + +static void geo_node_curve_fillet_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Curve"); + b.add_input("Count").default_value(1).min(1).max(1000); + b.add_input("Radius") + .min(0.0f) + .max(FLT_MAX) + .subtype(PropertySubType::PROP_DISTANCE) + .default_value(0.25f); + b.add_input("Limit Radius"); + b.add_output("Curve"); +} + +static void geo_node_curve_fillet_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_curve_fillet_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveFillet *data = (NodeGeometryCurveFillet *)MEM_callocN( + sizeof(NodeGeometryCurveFillet), __func__); + + data->mode = GEO_NODE_CURVE_FILLET_BEZIER; + node->storage = data; +} + +struct FilletParam { + GeometryNodeCurveFilletMode mode; + + /* Number of points to be added. */ + const VArray *counts; + + /* Radii for fillet arc at all vertices. */ + const VArray *radii; + + /* Whether or not fillets are allowed to overlap. */ + bool limit_radius; +}; + +/* A data structure used to store fillet data about all vertices to be filleted. */ +struct FilletData { + Span positions; + Array directions, axes; + Array radii, angles; + Array counts; +}; + +static void geo_node_curve_fillet_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)node->storage; + const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode; + + bNodeSocket *poly_socket = ((bNodeSocket *)node->inputs.first)->next; + + nodeSetSocketAvailability(poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY); +} + +/* Function to get the center of a fillet. */ +static float3 get_center(const float3 vec_pos2prev, + const float3 pos, + const float3 axis, + const float angle) +{ + float3 vec_pos2center; + rotate_normalized_v3_v3v3fl(vec_pos2center, vec_pos2prev, axis, M_PI_2 - angle / 2.0f); + vec_pos2center *= 1.0f / sinf(angle / 2.0f); + + return vec_pos2center + pos; +} + +/* Function to get the center of the fillet using fillet data */ +static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const int index) +{ + const float angle = fd.angles[index]; + const float3 axis = fd.axes[index]; + const float3 pos = fd.positions[index]; + + return get_center(vec_pos2prev, pos, axis, angle); +} + +/* Calculate the direction vectors from each vertex to their previous vertex. */ +static Array calculate_directions(const Span positions) +{ + const int size = positions.size(); + Array directions(size); + + for (const int i : IndexRange(size - 1)) { + directions[i] = (positions[i + 1] - positions[i]).normalized(); + } + directions[size - 1] = (positions[0] - positions[size - 1]).normalized(); + + return directions; +} + +/* Calculate the axes around which the fillet is built. */ +static Array calculate_axes(const Span directions) +{ + const int size = directions.size(); + Array axes(size); + + axes[0] = float3::cross(-directions[size - 1], directions[0]).normalized(); + for (const int i : IndexRange(1, size - 1)) { + axes[i] = float3::cross(-directions[i - 1], directions[i]).normalized(); + } + + return axes; +} + +/* Calculate the angle of the arc formed by the fillet. */ +static Array calculate_angles(const Span directions) +{ + const int size = directions.size(); + Array angles(size); + + angles[0] = M_PI - angle_v3v3(-directions[size - 1], directions[0]); + for (const int i : IndexRange(1, size - 1)) { + angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]); + } + + return angles; +} + +/* Calculate the segment count in each filleted arc. */ +static Array calculate_counts(const FilletParam &fillet_param, + const int size, + const int spline_offset, + const bool cyclic) +{ + Array counts(size, 1); + if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { + for (const int i : IndexRange(size)) { + counts[i] = (*fillet_param.counts)[spline_offset + i]; + } + } + if (!cyclic) { + counts[0] = counts[size - 1] = 0; + } + + return counts; +} + +/* Calculate the radii for the vertices to be filleted. */ +static Array calculate_radii(const FilletParam &fillet_param, + const int size, + const int spline_offset) +{ + Array radii(size, 0.0f); + if (fillet_param.limit_radius) { + for (const int i : IndexRange(size)) { + radii[i] = std::max((*fillet_param.radii)[spline_offset + i], 0.0f); + } + } + else { + for (const int i : IndexRange(size)) { + radii[i] = (*fillet_param.radii)[spline_offset + i]; + } + } + + return radii; +} + +/* Calculate the number of vertices added per vertex on the source spline. */ +static int calculate_point_counts(MutableSpan point_counts, + const Span radii, + const Span counts) +{ + int added_count = 0; + for (const int i : IndexRange(point_counts.size())) { + /* Calculate number of points to be added for the vertex. */ + if (radii[i] != 0.0f) { + added_count += counts[i]; + point_counts[i] = counts[i] + 1; + } + } + + return added_count; +} + +static FilletData calculate_fillet_data(const Spline &spline, + const FilletParam &fillet_param, + int &added_count, + MutableSpan point_counts, + const int spline_offset) +{ + const int size = spline.size(); + + FilletData fd; + fd.directions = calculate_directions(spline.positions()); + fd.positions = spline.positions(); + fd.axes = calculate_axes(fd.directions); + fd.angles = calculate_angles(fd.directions); + fd.counts = calculate_counts(fillet_param, size, spline_offset, spline.is_cyclic()); + fd.radii = calculate_radii(fillet_param, size, spline_offset); + + added_count = calculate_point_counts(point_counts, fd.radii, fd.counts); + + return fd; +} + +/* Limit the radius based on angle and radii to prevent overlapping. */ +static void limit_radii(FilletData &fd, const bool cyclic) +{ + MutableSpan radii(fd.radii); + Span angles(fd.angles); + Span positions(fd.positions); + + const int size = radii.size(); + const int fillet_count = cyclic ? size : size - 2; + const int start = cyclic ? 0 : 1; + Array max_radii(size, FLT_MAX); + + if (cyclic) { + /* Calculate lengths between adjacent control points. */ + const float len_prev = float3::distance(positions[0], positions[size - 1]); + const float len_next = float3::distance(positions[0], positions[1]); + + /* Calculate tangent lengths of fillets in control points. */ + const float tan_len = radii[0] * tan(angles[0] / 2.0f); + const float tan_len_prev = radii[size - 1] * tan(angles[size - 1] / 2.0f); + const float tan_len_next = radii[1] * tan(angles[1] / 2.0f); + + float factor_prev = 1.0f, factor_next = 1.0f; + if (tan_len + tan_len_prev > len_prev) { + factor_prev = len_prev / (tan_len + tan_len_prev); + } + if (tan_len + tan_len_next > len_next) { + factor_next = len_next / (tan_len + tan_len_next); + } + + /* Scale max radii by calculated factors. */ + max_radii[0] = radii[0] * std::min(factor_next, factor_prev); + max_radii[1] = radii[1] * factor_next; + max_radii[size - 1] = radii[size - 1] * factor_prev; + } + + /* Initialize max_radii to largest possible radii. */ + float prev_dist = float3::distance(positions[1], positions[0]); + for (const int i : IndexRange(1, size - 2)) { + const float temp_dist = float3::distance(positions[i], positions[i + 1]); + max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f); + prev_dist = temp_dist; + } + + /* Max radii calculations for each index. */ + for (const int i : IndexRange(start, fillet_count - 1)) { + const float len_next = float3::distance(positions[i], positions[i + 1]); + const float tan_len = radii[i] * tan(angles[i] / 2.0f); + const float tan_len_next = radii[i + 1] * tan(angles[i + 1] / 2.0f); + + /* Scale down radii if too large for segment. */ + float factor = 1.0f; + if (tan_len + tan_len_next > len_next) { + factor = len_next / (tan_len + tan_len_next); + } + max_radii[i] = std::min(max_radii[i], radii[i] * factor); + max_radii[i + 1] = std::min(max_radii[i + 1], radii[i + 1] * factor); + } + + /* Assign the max_radii to the fillet data's radii. */ + for (const int i : IndexRange(size)) { + radii[i] = max_radii[i]; + } +} + +/* + * Create a mapping from each vertex in the destination spline to that of the source spline. + * Used for copying the data from the source spline. + */ +static Array create_dst_to_src_map(const Span point_counts, const int total_points) +{ + Array map(total_points); + MutableSpan map_span{map}; + int index = 0; + + for (const int i : point_counts.index_range()) { + map_span.slice(index, point_counts[i]).fill(i); + index += point_counts[i]; + } + + BLI_assert(index == total_points); + + return map; +} + +template +static void copy_attribute_by_mapping(const Span src, + MutableSpan dst, + const Span mapping) +{ + for (const int i : dst.index_range()) { + dst[i] = src[mapping[i]]; + } +} + +/* Copy radii and tilts from source spline to destination. Positions are handled later in update + * positions methods. */ +static void copy_common_attributes_by_mapping(const Spline &src, + Spline &dst, + const Span mapping) +{ + copy_attribute_by_mapping(src.radii(), dst.radii(), mapping); + copy_attribute_by_mapping(src.tilts(), dst.tilts(), mapping); + + dst.attributes.reallocate(1); + src.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src_attribute = src.attributes.get_for_read(attribute_id); + if (dst.attributes.create(attribute_id, meta_data.data_type)) { + std::optional dst_attribute = dst.attributes.get_for_write(attribute_id); + if (dst_attribute) { + src_attribute->type().copy_assign(src_attribute->data(), dst_attribute->data()); + return true; + } + } + BLI_assert_unreachable(); + return false; + }, + ATTR_DOMAIN_POINT); +} + +/* Update the vertex positions and handle positions of a Bezier spline based on fillet data. */ +static void update_bezier_positions(const FilletData &fd, + BezierSpline &dst_spline, + const BezierSpline &src_spline, + const Span point_counts) +{ + Span radii(fd.radii); + Span angles(fd.angles); + Span axes(fd.axes); + Span positions(fd.positions); + Span directions(fd.directions); + + const int size = radii.size(); + + int i_dst = 0; + for (const int i_src : IndexRange(size)) { + const int count = point_counts[i_src]; + + /* Skip if the point count for the vertex is 1. */ + if (count == 1) { + dst_spline.positions()[i_dst] = src_spline.positions()[i_src]; + dst_spline.handle_types_left()[i_dst] = src_spline.handle_types_left()[i_src]; + dst_spline.handle_types_right()[i_dst] = src_spline.handle_types_right()[i_src]; + dst_spline.handle_positions_left()[i_dst] = src_spline.handle_positions_left()[i_src]; + dst_spline.handle_positions_right()[i_dst] = src_spline.handle_positions_right()[i_src]; + i_dst++; + continue; + } + + /* Calculate the angle to be formed between any 2 adjacent vertices within the fillet. */ + const float segment_angle = angles[i_src] / (count - 1); + /* Calculate the handle length for each added vertex. Equation: L = 4R/3 * tan(A/4) */ + const float handle_length = 4.0f * radii[i_src] / 3.0f * tan(segment_angle / 4.0f); + /* Calculate the distance by which each vertex should be displaced from their initial position. + */ + const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f); + + /* Position the end points of the arc and their handles. */ + const int end_i = i_dst + count - 1; + const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 next_dir = directions[i_src]; + dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; + dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; + dst_spline.handle_positions_right()[i_dst] = dst_spline.positions()[i_dst] - + handle_length * prev_dir; + dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] - + handle_length * next_dir; + dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] = + BezierSpline::HandleType::Align; + dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] = + BezierSpline::HandleType::Vector; + dst_spline.mark_cache_invalid(); + + /* Calculate the center of the radius to be formed. */ + const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src); + /* Calculate the vector of the radius formed by the first vertex. */ + float3 radius_vec = dst_spline.positions()[i_dst] - center; + const float radius = radius_vec.normalize_and_get_length(); + + dst_spline.handle_types_right().slice(1, count - 2).fill(BezierSpline::HandleType::Align); + dst_spline.handle_types_left().slice(1, count - 2).fill(BezierSpline::HandleType::Align); + + /* For each of the vertices in between the end points. */ + for (const int j : IndexRange(1, count - 2)) { + int index = i_dst + j; + /* Rotate the radius by the segment angle and determine its tangent (used for getting handle + * directions). */ + float3 new_radius_vec, tangent_vec; + rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle); + rotate_normalized_v3_v3v3fl(tangent_vec, new_radius_vec, axes[i_src], M_PI_2); + radius_vec = new_radius_vec; + tangent_vec *= handle_length; + + /* Adjust the positions of the respective vertex and its handles. */ + dst_spline.positions()[index] = center + new_radius_vec * radius; + dst_spline.handle_positions_left()[index] = dst_spline.positions()[index] + tangent_vec; + dst_spline.handle_positions_right()[index] = dst_spline.positions()[index] - tangent_vec; + } + + i_dst += count; + } +} + +/* Update the vertex positions of a Poly spline based on fillet data. */ +static void update_poly_positions(const FilletData &fd, + Spline &dst_spline, + const Spline &src_spline, + const Span point_counts) +{ + Span radii(fd.radii); + Span angles(fd.angles); + Span axes(fd.axes); + Span positions(fd.positions); + Span directions(fd.directions); + + const int size = radii.size(); + + int i_dst = 0; + for (const int i_src : IndexRange(size)) { + const int count = point_counts[i_src]; + + /* Skip if the point count for the vertex is 1. */ + if (count == 1) { + dst_spline.positions()[i_dst] = src_spline.positions()[i_src]; + i_dst++; + continue; + } + + const float segment_angle = angles[i_src] / (count - 1); + const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f); + + /* Position the end points of the arc. */ + const int end_i = i_dst + count - 1; + const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 next_dir = directions[i_src]; + dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; + dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; + + /* Calculate the center of the radius to be formed. */ + const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src); + /* Calculate the vector of the radius formed by the first vertex. */ + float3 radius_vec = dst_spline.positions()[i_dst] - center; + + for (const int j : IndexRange(1, count - 2)) { + /* Rotate the radius by the segment angle */ + float3 new_radius_vec; + rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle); + radius_vec = new_radius_vec; + + dst_spline.positions()[i_dst + j] = center + new_radius_vec; + } + + i_dst += count; + } +} + +static SplinePtr fillet_spline(const Spline &spline, + const FilletParam &fillet_param, + const int spline_offset) +{ + const int size = spline.size(); + const bool cyclic = spline.is_cyclic(); + + if (size < 3) { + return spline.copy(); + } + + /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */ + Array point_counts(size, 1); + + int added_count = 0; + /* Update point_counts array and added_count. */ + FilletData fd = calculate_fillet_data( + spline, fillet_param, added_count, point_counts, spline_offset); + if (fillet_param.limit_radius) { + limit_radii(fd, cyclic); + } + + const int total_points = added_count + size; + const Array dst_to_src = create_dst_to_src_map(point_counts, total_points); + SplinePtr dst_spline_ptr = spline.copy_only_settings(); + (*dst_spline_ptr).resize(total_points); + copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src); + + switch (spline.type()) { + case Spline::Type::Bezier: { + const BezierSpline &src_spline = static_cast(spline); + BezierSpline &dst_spline = static_cast(*dst_spline_ptr); + if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { + dst_spline.handle_types_left().fill(BezierSpline::HandleType::Vector); + dst_spline.handle_types_right().fill(BezierSpline::HandleType::Vector); + update_poly_positions(fd, dst_spline, src_spline, point_counts); + } + else { + update_bezier_positions(fd, dst_spline, src_spline, point_counts); + } + break; + } + case Spline::Type::Poly: { + update_poly_positions(fd, *dst_spline_ptr, spline, point_counts); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src_spline = static_cast(spline); + NURBSpline &dst_spline = static_cast(*dst_spline_ptr); + copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src); + update_poly_positions(fd, dst_spline, src_spline, point_counts); + break; + } + } + + return dst_spline_ptr; +} + +static std::unique_ptr fillet_curve(const CurveEval &input_curve, + const FilletParam &fillet_param) +{ + Span input_splines = input_curve.splines(); + + std::unique_ptr output_curve = std::make_unique(); + const int num_splines = input_splines.size(); + output_curve->resize(num_splines); + MutableSpan output_splines = output_curve->splines(); + Array spline_offsets = input_curve.control_point_offsets(); + + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + output_splines[i] = fillet_spline(*input_splines[i], fillet_param, spline_offsets[i]); + } + }); + output_curve->attributes = input_curve.attributes; + + return output_curve; +} + +static void geo_node_fillet_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Curve"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_curve()) { + params.set_output("Curve", geometry_set); + return; + } + + NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage; + const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode; + FilletParam fillet_param; + fillet_param.mode = mode; + + CurveComponent &component = geometry_set.get_component_for_write(); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + fn::FieldEvaluator field_evaluator{field_context, domain_size}; + + Field radius_field = params.extract_input>("Radius"); + field_evaluator.add(std::move(radius_field)); + + if (mode == GEO_NODE_CURVE_FILLET_POLY) { + Field count_field = params.extract_input>("Count"); + field_evaluator.add(std::move(count_field)); + } + + field_evaluator.evaluate(); + + fillet_param.radii = &field_evaluator.get_evaluated(0); + if (fillet_param.radii->is_single() && fillet_param.radii->get_internal_single() < 0.0f) { + params.set_output("Geometry", geometry_set); + return; + } + + if (mode == GEO_NODE_CURVE_FILLET_POLY) { + fillet_param.counts = &field_evaluator.get_evaluated(1); + } + + fillet_param.limit_radius = params.extract_input("Limit Radius"); + + const CurveEval &input_curve = *geometry_set.get_curve_for_read(); + std::unique_ptr output_curve = fillet_curve(input_curve, fillet_param); + + params.set_output("Curve", GeometrySet::create_with_curve(output_curve.release())); +} +} // namespace blender::nodes + +void register_node_type_geo_curve_fillet() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_FILLET, "Curve Fillet", NODE_CLASS_GEOMETRY, 0); + ntype.draw_buttons = blender::nodes::geo_node_curve_fillet_layout; + node_type_storage( + &ntype, "NodeGeometryCurveFillet", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_curve_fillet_declare; + node_type_init(&ntype, blender::nodes::geo_node_curve_fillet_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_fillet_update); + ntype.geometry_node_execute = blender::nodes::geo_node_fillet_exec; + nodeRegisterType(&ntype); +} diff --git a/source/tools b/source/tools index c8579c5cf43..723b24841df 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit c8579c5cf43229843df505da9644b5b0b7201974 +Subproject commit 723b24841df1ed8478949bca20c73878fab00dca From 707bcd5693aedc0c1a461bbb0ce88680e32de561 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 22 Sep 2021 16:55:09 +0200 Subject: [PATCH 0098/1500] Cleanup: make format --- source/blender/blenkernel/intern/screen.c | 1 - source/blender/blenkernel/intern/sound.c | 5 ++++- .../editors/transform/transform_convert_sequencer_image.c | 4 +++- .../gpencil_modifiers/intern/MOD_gpencilweight_angle.c | 1 - source/blender/makesrna/intern/rna_scene.c | 2 +- source/blender/sequencer/intern/render.c | 7 +++++-- source/blender/sequencer/intern/strip_add.c | 7 ++----- 7 files changed, 15 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 4c38536b662..0474c2b81cb 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -1680,7 +1680,6 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) sseq->scopes.vector_ibuf = NULL; sseq->scopes.histogram_ibuf = NULL; memset(&sseq->runtime, 0x0, sizeof(sseq->runtime)); - } else if (sl->spacetype == SPACE_PROPERTIES) { SpaceProperties *sbuts = (SpaceProperties *)sl; diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index ccb10f080e3..093e4f42d4a 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -1230,7 +1230,10 @@ bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *soun return result; } -bool BKE_sound_stream_info_get(struct Main *main, const char *filepath, int stream, SoundStreamInfo *sound_info) +bool BKE_sound_stream_info_get(struct Main *main, + const char *filepath, + int stream, + SoundStreamInfo *sound_info) { const char *path; char str[FILE_MAX]; diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index 465f8b9a694..5db9a2e092f 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -102,7 +102,9 @@ static TransData *SeqToTransData(const Scene *scene, return td; } -static void freeSeqData(TransInfo *UNUSED(t), TransDataContainer *tc, TransCustomData *UNUSED(custom_data)) +static void freeSeqData(TransInfo *UNUSED(t), + TransDataContainer *tc, + TransCustomData *UNUSED(custom_data)) { TransData *td = (TransData *)tc->data; MEM_freeN(td->extra); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c index 2c0f3d2d8ad..a833be080a9 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c @@ -217,7 +217,6 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "minimum_weight", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "use_multiply", 0, NULL, ICON_NONE); - gpencil_modifier_panel_end(layout, ptr); } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index e45d39a1ddc..80fc13faab4 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3524,7 +3524,7 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem pivot_points[] = { + static const EnumPropertyItem pivot_points[] = { {V3D_AROUND_CENTER_MEDIAN, "MEDIAN", ICON_PIVOT_MEDIAN, "Median Point", ""}, {V3D_AROUND_LOCAL_ORIGINS, "INDIVIDUAL_ORIGINS", diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 2578a6d4223..cf3f9d5cba5 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -2043,8 +2043,11 @@ int SEQ_render_thumbnails_guaranteed_set_frame_step_get(const Sequence *seq) } /* Render set of evenly spaced thumbnails that are drawn when zooming. */ -void SEQ_render_thumbnails_base_set( - const SeqRenderData *context, Sequence *seq, Sequence *seq_orig, rctf *view_area, const short *stop) +void SEQ_render_thumbnails_base_set(const SeqRenderData *context, + Sequence *seq, + Sequence *seq_orig, + rctf *view_area, + const short *stop) { SeqRenderState state; seq_render_state_init(&state); diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 3cf7a4ebf4d..aa3f7c92dd8 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -486,11 +486,8 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_ * \param load_data: SeqLoadData with information necessary to create strip * \return created strip */ -Sequence *SEQ_add_movie_strip(Main *bmain, - Scene *scene, - ListBase *seqbase, - SeqLoadData *load_data, - double *r_start_offset) +Sequence *SEQ_add_movie_strip( + Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data, double *r_start_offset) { char path[sizeof(load_data->path)]; BLI_strncpy(path, load_data->path, sizeof(path)); From 794c2828af60af02a38381c2a9a81f9046381074 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 17 Sep 2021 16:22:29 +0200 Subject: [PATCH 0099/1500] Initial implementation of local ID re-use when appending. This commit adds to ID struct a new optional 'weak reference' to a linked ID (in the form of a blend file library path and full ID name). This can then be used on next append to try to find a matching local ID instead of re-making the linked data local again. Ref. T90545 NOTE: ID re-use will be disabled for regular append for the time being (3.0 release), and only used for assets. Therefore, this commit should not change anything user-wise. Differential Revision: https://developer.blender.org/D12545 --- source/blender/blenkernel/BKE_idtype.h | 6 +- source/blender/blenkernel/BKE_main.h | 26 +++ source/blender/blenkernel/intern/armature.c | 2 +- source/blender/blenkernel/intern/bpath.c | 5 + source/blender/blenkernel/intern/cachefile.c | 2 +- source/blender/blenkernel/intern/camera.c | 2 +- source/blender/blenkernel/intern/curve.c | 2 +- source/blender/blenkernel/intern/font.c | 2 +- source/blender/blenkernel/intern/hair.c | 2 +- source/blender/blenkernel/intern/idtype.c | 18 ++ source/blender/blenkernel/intern/image.c | 2 +- source/blender/blenkernel/intern/lattice.c | 2 +- source/blender/blenkernel/intern/lib_id.c | 7 + .../blender/blenkernel/intern/lib_id_delete.c | 4 + source/blender/blenkernel/intern/light.c | 2 +- source/blender/blenkernel/intern/lightprobe.c | 2 +- source/blender/blenkernel/intern/linestyle.c | 2 +- source/blender/blenkernel/intern/main.c | 202 ++++++++++++++++++ source/blender/blenkernel/intern/mask.c | 2 +- source/blender/blenkernel/intern/material.c | 2 +- source/blender/blenkernel/intern/mball.c | 2 +- source/blender/blenkernel/intern/mesh.c | 2 +- source/blender/blenkernel/intern/movieclip.c | 2 +- source/blender/blenkernel/intern/node.cc | 2 +- .../blender/blenkernel/intern/pointcloud.cc | 2 +- .../blender/blenkernel/intern/simulation.cc | 2 +- source/blender/blenkernel/intern/sound.c | 2 +- source/blender/blenkernel/intern/speaker.c | 2 +- source/blender/blenkernel/intern/text.c | 2 +- source/blender/blenkernel/intern/texture.c | 2 +- source/blender/blenkernel/intern/volume.cc | 2 +- source/blender/blenkernel/intern/world.c | 2 +- source/blender/blenloader/intern/readfile.c | 7 + source/blender/makesdna/DNA_ID.h | 29 ++- source/blender/makesrna/intern/rna_ID.c | 35 +++ .../windowmanager/intern/wm_files_link.c | 52 ++++- 36 files changed, 412 insertions(+), 29 deletions(-) diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index 7136a3fd7af..cd656d94fce 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -49,8 +49,11 @@ enum { * appended. * NOTE: Mutually exclusive with `IDTYPE_FLAGS_NO_LIBLINKING`. */ IDTYPE_FLAGS_ONLY_APPEND = 1 << 2, + /** Allow to re-use an existing local ID with matching weak library reference instead of creating + * a new copy of it, when appending. See also #LibraryWeakReference in `DNA_ID.h`. */ + IDTYPE_FLAGS_APPEND_IS_REUSABLE = 1 << 3, /** Indicates that the given IDType does not have animation data. */ - IDTYPE_FLAGS_NO_ANIMDATA = 1 << 3, + IDTYPE_FLAGS_NO_ANIMDATA = 1 << 4, }; typedef struct IDCacheKey { @@ -290,6 +293,7 @@ bool BKE_idtype_idcode_is_valid(const short idcode); bool BKE_idtype_idcode_is_linkable(const short idcode); bool BKE_idtype_idcode_is_only_appendable(const short idcode); +bool BKE_idtype_idcode_append_is_reusable(const short idcode); /* Macro currently, since any linkable IDtype should be localizable. */ #define BKE_idtype_idcode_is_localizable BKE_idtype_idcode_is_linkable diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index ae60a5563b5..93d5b5c5aa6 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -212,6 +212,32 @@ void BKE_main_relations_tag_set(struct Main *bmain, struct GSet *BKE_main_gset_create(struct Main *bmain, struct GSet *gset); +/* + * Temporary runtime API to allow re-using local (already appended) IDs instead of appending a new + * copy again. + */ + +struct GHash *BKE_main_library_weak_reference_create(struct Main *bmain) ATTR_NONNULL(); +void BKE_main_library_weak_reference_destroy(struct GHash *library_weak_reference_mapping) + ATTR_NONNULL(); +struct ID *BKE_main_library_weak_reference_search_item( + struct GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name) ATTR_NONNULL(); +void BKE_main_library_weak_reference_add_item(struct GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + struct ID *new_id) ATTR_NONNULL(); +void BKE_main_library_weak_reference_update_item(struct GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + struct ID *old_id, + struct ID *new_id) ATTR_NONNULL(); +void BKE_main_library_weak_reference_remove_item(struct GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + struct ID *old_id) ATTR_NONNULL(); + /* *** Generic utils to loop over whole Main database. *** */ #define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id) \ diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 87320c88b1b..a86f436185e 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -315,7 +315,7 @@ IDTypeInfo IDType_ID_AR = { .name = "Armature", .name_plural = "armatures", .translation_context = BLT_I18NCONTEXT_ID_ARMATURE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = armature_init_data, .copy_data = armature_copy_data, diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 1684e22dece..371ec14876b 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -586,6 +586,11 @@ void BKE_bpath_traverse_id( return; } + if (id->library_weak_reference != NULL) { + rewrite_path_fixed( + id->library_weak_reference->library_filepath, visit_cb, absbase, bpath_user_data); + } + switch (GS(id->name)) { case ID_IM: { Image *ima; diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 87b1584d422..e642bbc9e06 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -133,7 +133,7 @@ IDTypeInfo IDType_ID_CF = { .name = "CacheFile", .name_plural = "cache_files", .translation_context = BLT_I18NCONTEXT_ID_CACHEFILE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = cache_file_init_data, .copy_data = cache_file_copy_data, diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index b77855f8f95..ed1f6fcb40a 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -182,7 +182,7 @@ IDTypeInfo IDType_ID_CA = { .name = "Camera", .name_plural = "cameras", .translation_context = BLT_I18NCONTEXT_ID_CAMERA, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = camera_init_data, .copy_data = camera_copy_data, diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index f22c3b13efc..b0d196b2bb0 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -311,7 +311,7 @@ IDTypeInfo IDType_ID_CU = { .name = "Curve", .name_plural = "curves", .translation_context = BLT_I18NCONTEXT_ID_CURVE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = curve_init_data, .copy_data = curve_copy_data, diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 842a701f525..aa13f86523a 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -160,7 +160,7 @@ IDTypeInfo IDType_ID_VF = { .name = "Font", .name_plural = "fonts", .translation_context = BLT_I18NCONTEXT_ID_VFONT, - .flags = IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = vfont_init_data, .copy_data = vfont_copy_data, diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index af7cc0acb57..cf346e9cac7 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -181,7 +181,7 @@ IDTypeInfo IDType_ID_HA = { .name = "Hair", .name_plural = "hairs", .translation_context = BLT_I18NCONTEXT_ID_HAIR, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = hair_init_data, .copy_data = hair_copy_data, diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index b2efccc53c4..d9dc68b1a4f 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -254,6 +254,24 @@ bool BKE_idtype_idcode_is_only_appendable(const short idcode) return false; } +/** + * Check if an ID type can try to reuse and existing matching local one when being appended again. + * + * \param idcode: The IDType code to check. + * \return Boolean, false when it cannot be re-used, true otherwise. + */ +bool BKE_idtype_idcode_append_is_reusable(const short idcode) +{ + const IDTypeInfo *id_type = BKE_idtype_get_info_from_idcode(idcode); + BLI_assert(id_type != NULL); + if (id_type != NULL && (id_type->flags & IDTYPE_FLAGS_APPEND_IS_REUSABLE) != 0) { + /* All appendable ID types should also always be linkable. */ + BLI_assert((id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0); + return true; + } + return false; +} + /** * Convert an \a idcode into an \a idfilter (e.g. ID_OB -> FILTER_ID_OB). */ diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 33f007c6dee..b993d743044 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -345,7 +345,7 @@ IDTypeInfo IDType_ID_IM = { .name = "Image", .name_plural = "images", .translation_context = BLT_I18NCONTEXT_ID_IMAGE, - .flags = IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = image_init_data, .copy_data = image_copy_data, diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index e804f32e5a6..9bca8172e64 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -196,7 +196,7 @@ IDTypeInfo IDType_ID_LT = { .name = "Lattice", .name_plural = "lattices", .translation_context = BLT_I18NCONTEXT_ID_LATTICE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = lattice_init_data, .copy_data = lattice_copy_data, diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 60b6d7ad66d..18824e73ee5 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1301,6 +1301,9 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag); } + /* This is never duplicated, only one existing ID should have a given weak ref to library/ID. */ + new_id->library_weak_reference = NULL; + if ((orig_flag & LIB_ID_COPY_NO_LIB_OVERRIDE) == 0) { if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { /* We do not want to copy existing override rules here, as they would break the proper @@ -2440,6 +2443,10 @@ void BKE_id_blend_write(BlendWriter *writer, ID *id) BKE_asset_metadata_write(writer, id->asset_data); } + if (id->library_weak_reference != NULL) { + BLO_write_struct(writer, LibraryWeakReference, id->library_weak_reference); + } + /* ID_WM's id->properties are considered runtime only, and never written in .blend file. */ if (id->properties && !ELEM(GS(id->name), ID_WM)) { IDP_BlendWrite(writer, id->properties); diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index 79717fe5f48..502a1197616 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -69,6 +69,10 @@ void BKE_libblock_free_data(ID *id, const bool do_id_user) BKE_asset_metadata_free(&id->asset_data); } + if (id->library_weak_reference != NULL) { + MEM_freeN(id->library_weak_reference); + } + BKE_animdata_free(id, do_id_user); } diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index c2b71b85973..a6150028f46 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -193,7 +193,7 @@ IDTypeInfo IDType_ID_LA = { .name = "Light", .name_plural = "lights", .translation_context = BLT_I18NCONTEXT_ID_LIGHT, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = light_init_data, .copy_data = light_copy_data, diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 15733af8ef0..1f4abf36426 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -91,7 +91,7 @@ IDTypeInfo IDType_ID_LP = { .name = "LightProbe", .name_plural = "lightprobes", .translation_context = BLT_I18NCONTEXT_ID_LIGHTPROBE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = lightprobe_init_data, .copy_data = NULL, diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index 19030fca38b..f4e4dd9f1ab 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -751,7 +751,7 @@ IDTypeInfo IDType_ID_LS = { .name = "FreestyleLineStyle", .name_plural = "linestyles", .translation_context = BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = linestyle_init_data, .copy_data = linestyle_copy_data, diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 981f1d4a623..26dcadcc77b 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -35,6 +35,7 @@ #include "DNA_ID.h" #include "BKE_global.h" +#include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -358,6 +359,207 @@ GSet *BKE_main_gset_create(Main *bmain, GSet *gset) return gset; } +/* Utils for ID's library weak reference API. */ +typedef struct LibWeakRefKey { + char filepath[FILE_MAX]; + char id_name[MAX_ID_NAME]; +} LibWeakRefKey; + +static LibWeakRefKey *lib_weak_key_create(LibWeakRefKey *key, + const char *lib_path, + const char *id_name) +{ + if (key == NULL) { + key = MEM_mallocN(sizeof(*key), __func__); + } + BLI_strncpy(key->filepath, lib_path, sizeof(key->filepath)); + BLI_strncpy(key->id_name, id_name, sizeof(key->id_name)); + return key; +} + +static uint lib_weak_key_hash(const void *ptr) +{ + const LibWeakRefKey *string_pair = ptr; + uint hash = BLI_ghashutil_strhash_p_murmur(string_pair->filepath); + return hash ^ BLI_ghashutil_strhash_p_murmur(string_pair->id_name); +} + +static bool lib_weak_key_cmp(const void *a, const void *b) +{ + const LibWeakRefKey *string_pair_a = a; + const LibWeakRefKey *string_pair_b = b; + + return !(STREQ(string_pair_a->filepath, string_pair_b->filepath) && + STREQ(string_pair_a->id_name, string_pair_b->id_name)); +} + +/** + * Generate a mapping between 'library path' of an ID (as a pair (relative blend file path, id + * name)), and a current local ID, if any. + * + * This uses the information stored in `ID.library_weak_reference`. + */ +GHash *BKE_main_library_weak_reference_create(Main *bmain) +{ + GHash *library_weak_reference_mapping = BLI_ghash_new( + lib_weak_key_hash, lib_weak_key_cmp, __func__); + + ListBase *lb; + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + ID *id_iter = lb->first; + if (id_iter == NULL) { + continue; + } + if (!BKE_idtype_idcode_append_is_reusable(GS(id_iter->name))) { + continue; + } + BLI_assert(BKE_idtype_idcode_is_linkable(GS(id_iter->name))); + + FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_iter) { + if (id_iter->library_weak_reference == NULL) { + continue; + } + LibWeakRefKey *key = lib_weak_key_create(NULL, + id_iter->library_weak_reference->library_filepath, + id_iter->library_weak_reference->library_id_name); + BLI_ghash_insert(library_weak_reference_mapping, key, id_iter); + } + FOREACH_MAIN_LISTBASE_ID_END; + } + FOREACH_MAIN_LISTBASE_END; + + return library_weak_reference_mapping; +} + +/** + * Destroy the data generated by #BKE_main_library_weak_reference_create. + */ +void BKE_main_library_weak_reference_destroy(GHash *library_weak_reference_mapping) +{ + BLI_ghash_free(library_weak_reference_mapping, MEM_freeN, NULL); +} + +/** + * Search for a local ID matching the given linked ID reference. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID + * type. + */ +ID *BKE_main_library_weak_reference_search_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name) +{ + LibWeakRefKey key; + lib_weak_key_create(&key, library_filepath, library_id_name); + return (ID *)BLI_ghash_lookup(library_weak_reference_mapping, &key); +} + +/** + * Add the given ID weak library reference to given local ID and the runtime mapping. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID type. + * \param new_id: New local ID matching given weak reference. + */ +void BKE_main_library_weak_reference_add_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + ID *new_id) +{ + BLI_assert(GS(library_id_name) == GS(new_id->name)); + BLI_assert(new_id->library_weak_reference == NULL); + BLI_assert(BKE_idtype_idcode_append_is_reusable(GS(new_id->name))); + + new_id->library_weak_reference = MEM_mallocN(sizeof(*(new_id->library_weak_reference)), + __func__); + + LibWeakRefKey *key = lib_weak_key_create(NULL, library_filepath, library_id_name); + void **id_p; + const bool already_exist_in_mapping = BLI_ghash_ensure_p( + library_weak_reference_mapping, key, &id_p); + BLI_assert(!already_exist_in_mapping); + + BLI_strncpy(new_id->library_weak_reference->library_filepath, + library_filepath, + sizeof(new_id->library_weak_reference->library_filepath)); + BLI_strncpy(new_id->library_weak_reference->library_id_name, + library_id_name, + sizeof(new_id->library_weak_reference->library_id_name)); + *id_p = new_id; +} + +/** + * Update the status of the given ID weak library reference in current local IDs and the runtime + * mapping. + * + * This effectively transfers the 'ownership' of the given weak reference from `old_id` to + * `new_id`. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID type. + * \param old_id: Existing local ID matching given weak reference. + * \param new_id: New local ID matching given weak reference. + */ +void BKE_main_library_weak_reference_update_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + ID *old_id, + ID *new_id) +{ + BLI_assert(GS(library_id_name) == GS(old_id->name)); + BLI_assert(GS(library_id_name) == GS(new_id->name)); + BLI_assert(old_id->library_weak_reference != NULL); + BLI_assert(new_id->library_weak_reference == NULL); + BLI_assert(STREQ(old_id->library_weak_reference->library_filepath, library_filepath)); + BLI_assert(STREQ(old_id->library_weak_reference->library_id_name, library_id_name)); + + LibWeakRefKey key; + lib_weak_key_create(&key, library_filepath, library_id_name); + void **id_p = BLI_ghash_lookup_p(library_weak_reference_mapping, &key); + BLI_assert(id_p != NULL && *id_p == old_id); + + new_id->library_weak_reference = old_id->library_weak_reference; + old_id->library_weak_reference = NULL; + *id_p = new_id; +} + +/** + * Remove the given ID weak library reference from the given local ID and the runtime mapping. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID type. + * \param old_id: Existing local ID matching given weak reference. + */ +void BKE_main_library_weak_reference_remove_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + ID *old_id) +{ + BLI_assert(GS(library_id_name) == GS(old_id->name)); + BLI_assert(old_id->library_weak_reference != NULL); + + LibWeakRefKey key; + lib_weak_key_create(&key, library_filepath, library_id_name); + + BLI_assert(BLI_ghash_lookup(library_weak_reference_mapping, &key) == old_id); + BLI_ghash_remove(library_weak_reference_mapping, &key, MEM_freeN, NULL); + + MEM_SAFE_FREE(old_id->library_weak_reference); +} + /** * Generates a raw .blend file thumbnail data from given image. * diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index a93fcb6e8e0..1d3ebaac303 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -254,7 +254,7 @@ IDTypeInfo IDType_ID_MSK = { .name = "Mask", .name_plural = "masks", .translation_context = BLT_I18NCONTEXT_ID_MASK, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = NULL, .copy_data = mask_copy_data, diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 13b5bca5638..5f53d5e1ae8 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -259,7 +259,7 @@ IDTypeInfo IDType_ID_MA = { .name = "Material", .name_plural = "materials", .translation_context = BLT_I18NCONTEXT_ID_MATERIAL, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = material_init_data, .copy_data = material_copy_data, diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 45cf0f17840..6c8664aefed 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -188,7 +188,7 @@ IDTypeInfo IDType_ID_MB = { .name = "Metaball", .name_plural = "metaballs", .translation_context = BLT_I18NCONTEXT_ID_METABALL, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = metaball_init_data, .copy_data = metaball_copy_data, diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index d631993c4e8..ed3766ad6a3 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -360,7 +360,7 @@ IDTypeInfo IDType_ID_ME = { .name = "Mesh", .name_plural = "meshes", .translation_context = BLT_I18NCONTEXT_ID_MESH, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = mesh_init_data, .copy_data = mesh_copy_data, diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index e507252307b..0c2ac841b87 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -346,7 +346,7 @@ IDTypeInfo IDType_ID_MC = { .name = "MovieClip", .name_plural = "movieclips", .translation_context = BLT_I18NCONTEXT_ID_MOVIECLIP, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = movie_clip_init_data, .copy_data = movie_clip_copy_data, diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3c54d88c93a..d56a7bf8fb4 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -992,7 +992,7 @@ IDTypeInfo IDType_ID_NT = { /* name */ "NodeTree", /* name_plural */ "node_groups", /* translation_context */ BLT_I18NCONTEXT_ID_NODETREE, - /* flags */ 0, + /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* init_data */ ntree_init_data, /* copy_data */ ntree_copy_data, diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 837a772607f..1db14dc3dc8 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -174,7 +174,7 @@ IDTypeInfo IDType_ID_PT = { /* name */ "PointCloud", /* name_plural */ "pointclouds", /* translation_context */ BLT_I18NCONTEXT_ID_POINTCLOUD, - /* flags */ 0, + /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* init_data */ pointcloud_init_data, /* copy_data */ pointcloud_copy_data, diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 4c97ccdf8b1..1d297b3ced9 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -152,7 +152,7 @@ IDTypeInfo IDType_ID_SIM = { /* name */ "Simulation", /* name_plural */ "simulations", /* translation_context */ BLT_I18NCONTEXT_ID_SIMULATION, - /* flags */ 0, + /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* init_data */ simulation_init_data, /* copy_data */ simulation_copy_data, diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 093e4f42d4a..8feda76cc5b 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -204,7 +204,7 @@ IDTypeInfo IDType_ID_SO = { .name = "Sound", .name_plural = "sounds", .translation_context = BLT_I18NCONTEXT_ID_SOUND, - .flags = IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* A fuzzy case, think NULLified content is OK here... */ .init_data = NULL, diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index 4b10522c375..b361f31cc30 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -98,7 +98,7 @@ IDTypeInfo IDType_ID_SPK = { .name = "Speaker", .name_plural = "speakers", .translation_context = BLT_I18NCONTEXT_ID_SPEAKER, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = speaker_init_data, .copy_data = NULL, diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index f67bf68010d..5eb40b6624a 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -241,7 +241,7 @@ IDTypeInfo IDType_ID_TXT = { .name = "Text", .name_plural = "texts", .translation_context = BLT_I18NCONTEXT_ID_TEXT, - .flags = IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = text_init_data, .copy_data = text_copy_data, diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 228e6fffdf7..d5f7647f07a 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -210,7 +210,7 @@ IDTypeInfo IDType_ID_TE = { .name = "Texture", .name_plural = "textures", .translation_context = BLT_I18NCONTEXT_ID_TEXTURE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = texture_init_data, .copy_data = texture_copy_data, diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 69452d6896f..0b9ef5c537d 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -642,7 +642,7 @@ IDTypeInfo IDType_ID_VO = { /* name */ "Volume", /* name_plural */ "volumes", /* translation_context */ BLT_I18NCONTEXT_ID_VOLUME, - /* flags */ 0, + /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* init_data */ volume_init_data, /* copy_data */ volume_copy_data, diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 4abe1ff0f20..fe03c5b817a 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -190,7 +190,7 @@ IDTypeInfo IDType_ID_WO = { .name = "World", .name_plural = "worlds", .translation_context = BLT_I18NCONTEXT_ID_WORLD, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = world_init_data, .copy_data = world_copy_data, diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 15653264211..3cda1e613f8 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2195,6 +2195,13 @@ static void direct_link_id_common( /* Initialize with provided tag. */ id->tag = tag; + if (ID_IS_LINKED(id)) { + id->library_weak_reference = NULL; + } + else { + BLO_read_data_address(reader, &id->library_weak_reference); + } + if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) { /* For placeholder we only need to set the tag and properly initialize generic ID fields above, * no further data to read. */ diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 5b7c99f2545..d829d707a71 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -392,7 +392,14 @@ typedef struct ID { * that references this ID (the bones of an armature or the modifiers of an object for e.g.). */ void *py_instance; - void *_pad1; + + /** + * Weak reference to an ID in a given library file, used to allow re-using already appended data + * in some cases, instead of appending it again. + * + * May be NULL. + */ + struct LibraryWeakReference *library_weak_reference; } ID; /** @@ -426,6 +433,26 @@ typedef struct Library { short versionfile, subversionfile; } Library; +/** + * A weak library/ID reference for local data that has been appended, to allow re-using that local + * data instead of creating a new copy of it in future appends. + * + * NOTE: This is by design a week reference, in other words code should be totally fine and perform + * a regular append if it cannot find a valid matching local ID. + * + * NOTE: There should always be only one single ID in current Main matching a given linked + * reference. + */ +typedef struct LibraryWeakReference { + /** Expected to match a `Library.filepath`. */ + char library_filepath[1024]; + + /** MAX_ID_NAME. May be different from the current local ID name. */ + char library_id_name[66]; + + char _pad[2]; +} LibraryWeakReference; + /* for PreviewImage->flag */ enum ePreviewImage_Flag { PRV_CHANGED = (1 << 0), diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index eb887e1881b..1113ac0eb45 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -1936,6 +1936,15 @@ static void rna_def_ID(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text(prop, "Library", "Library file the data-block is linked from"); + prop = RNA_def_pointer(srna, + "library_weak_reference", + "LibraryWeakReference", + "Library Weak Reference", + "Weak reference to a data-block in another library .blend file (used to " + "re-use already appended data instead of appending new copies)"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); + prop = RNA_def_property(srna, "asset_data", PROP_POINTER, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); @@ -2143,6 +2152,31 @@ static void rna_def_library(BlenderRNA *brna) RNA_def_function_ui_description(func, "Reload this library and all its linked data-blocks"); } +static void rna_def_library_weak_reference(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "LibraryWeakReference", NULL); + RNA_def_struct_ui_text( + srna, + "LibraryWeakReference", + "Read-only external reference to a linked data-block and its library file"); + + prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_sdna(prop, NULL, "library_filepath"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "File Path", "Path to the library .blend file"); + + prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_sdna(prop, NULL, "library_id_name"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, + "ID name", + "Full ID name in the library .blend file (including the two leading 'id type' chars)"); +} + /** * \attention This is separate from the above. It allows for RNA functions to * return an IDProperty *. See MovieClip.metadata for a usage example. @@ -2175,6 +2209,7 @@ void RNA_def_ID(BlenderRNA *brna) rna_def_ID_properties(brna); rna_def_ID_materials(brna); rna_def_library(brna); + rna_def_library_weak_reference(brna); rna_def_idproperty_wrap_ptr(brna); } diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index d0117d9d57a..09567eca17f 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -190,6 +190,9 @@ typedef struct WMLinkAppendData { /** Allows to easily find an existing items from an ID pointer. Used by append code. */ GHash *new_id_to_item; + /** Runtime info used by append code to manage re-use of already appended matching IDs. */ + GHash *library_weak_reference_mapping; + /* Internal 'private' data */ MemArena *memarena; } WMLinkAppendData; @@ -230,6 +233,8 @@ static void wm_link_append_data_free(WMLinkAppendData *lapp_data) BLI_ghash_free(lapp_data->new_id_to_item, NULL, NULL); } + BLI_assert(lapp_data->library_weak_reference_mapping == NULL); + BLI_memarena_free(lapp_data->memarena); } @@ -637,6 +642,9 @@ static void wm_append_do(WMLinkAppendData *lapp_data, BLI_ghash_insert(lapp_data->new_id_to_item, id, item); } + const bool do_reuse_existing_id = false; + lapp_data->library_weak_reference_mapping = BKE_main_library_weak_reference_create(bmain); + /* NOTE: Since we append items for IDs not already listed (i.e. implicitly linked indirect * dependencies), this list will grow and we will process those IDs later, leading to a flatten * recursive processing of all the linked dependencies. */ @@ -650,7 +658,14 @@ static void wm_append_do(WMLinkAppendData *lapp_data, /* Clear tag previously used to mark IDs needing post-processing (instantiation of loose * objects etc.). */ - id->tag &= ~LIB_TAG_DOIT; + BLI_assert((id->tag & LIB_TAG_DOIT) == 0); + + ID *existing_local_id = BKE_idtype_idcode_append_is_reusable(GS(id->name)) ? + BKE_main_library_weak_reference_search_item( + lapp_data->library_weak_reference_mapping, + id->lib->filepath, + id->name) : + NULL; if (item->append_action != WM_APPEND_ACT_UNSET) { /* Already set, pass. */ @@ -659,6 +674,14 @@ static void wm_append_do(WMLinkAppendData *lapp_data, CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name); item->append_action = WM_APPEND_ACT_KEEP_LINKED; } + /* Only re-use existing local ID for indirectly linked data, the ID explicitely selected by the + * user we always fully append. */ + else if (do_reuse_existing_id && existing_local_id != NULL && + (item->append_tag & WM_APPEND_TAG_INDIRECT) != 0) { + CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name); + item->append_action = WM_APPEND_ACT_REUSE_LOCAL; + item->customdata = existing_local_id; + } else if (id->tag & LIB_TAG_PRE_EXISTING) { CLOG_INFO(&LOG, 3, "Appended ID '%s' was already linked, need to copy it...", id->name); item->append_action = WM_APPEND_ACT_COPY_LOCAL; @@ -678,6 +701,16 @@ static void wm_append_do(WMLinkAppendData *lapp_data, BKE_library_foreach_ID_link( bmain, id, foreach_libblock_append_callback, &cb_data, IDWALK_NOP); } + + /* If we found a matching existing local id but are not re-using it, we need to properly clear + * its weak reference to linked data. */ + if (existing_local_id != NULL && + !ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) { + BKE_main_library_weak_reference_remove_item(lapp_data->library_weak_reference_mapping, + id->lib->filepath, + id->name, + existing_local_id); + } } /* Effectively perform required operation on every linked ID. */ @@ -689,6 +722,11 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } ID *local_appended_new_id = NULL; + char lib_filepath[FILE_MAX]; + BLI_strncpy(lib_filepath, id->lib->filepath, sizeof(lib_filepath)); + char lib_id_name[MAX_ID_NAME]; + BLI_strncpy(lib_id_name, id->name, sizeof(lib_id_name)); + switch (item->append_action) { case WM_APPEND_ACT_COPY_LOCAL: { BKE_lib_id_make_local( @@ -721,6 +759,13 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } if (local_appended_new_id != NULL) { + if (BKE_idtype_idcode_append_is_reusable(GS(local_appended_new_id->name))) { + BKE_main_library_weak_reference_add_item(lapp_data->library_weak_reference_mapping, + lib_filepath, + lib_id_name, + local_appended_new_id); + } + if (GS(local_appended_new_id->name) == ID_OB) { BKE_rigidbody_ensure_local_object(bmain, (Object *)local_appended_new_id); } @@ -733,6 +778,9 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } } + BKE_main_library_weak_reference_destroy(lapp_data->library_weak_reference_mapping); + lapp_data->library_weak_reference_mapping = NULL; + /* Remap IDs as needed. */ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { WMLinkAppendDataItem *item = itemlink->link; @@ -745,7 +793,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, if (id == NULL) { continue; } - if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) { + if (ELEM(item->append_action, WM_APPEND_ACT_COPY_LOCAL, WM_APPEND_ACT_REUSE_LOCAL)) { BLI_assert(ID_IS_LINKED(id)); id = id->newid; if (id == NULL) { From 4762a9b09f98523093e5778e52b1ed8abf9fa331 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 22 Sep 2021 17:05:04 +0200 Subject: [PATCH 0100/1500] GPencil: Fix unreported missing material panel for new Scene When a new scene is created, the paint pointers are not available before using them, so the python panel exits because the pointer was None. Now, the pointer is checked in order to display the materials panel as expected. --- .../scripts/startup/bl_ui/properties_grease_pencil_common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index f01e75dbab8..c31881fa194 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -525,7 +525,7 @@ class GreasePencilMaterialsPanel: is_view3d = (self.bl_space_type == 'VIEW_3D') tool_settings = context.scene.tool_settings gpencil_paint = tool_settings.gpencil_paint - brush = gpencil_paint.brush + brush = gpencil_paint.brush if gpencil_paint else None ob = context.object row = layout.row() @@ -587,6 +587,9 @@ class GreasePencilMaterialsPanel: ma = ob.material_slots[ob.active_material_index].material else: ma = gp_settings.material + else: + if len(ob.material_slots) > 0 and ob.active_material_index >= 0: + ma = ob.material_slots[ob.active_material_index].material if ma is not None and ma.grease_pencil is not None: gpcolor = ma.grease_pencil From 1eba32c3e95effe3d3caef3bb7b6f508786c958f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 22 Sep 2021 16:57:31 +0200 Subject: [PATCH 0101/1500] Fix T91576: Cycles attribute node with name N for smooth normal no longer working There was an optimization to remove duplicate storage of normals as attributes when using normal maps. However for named attributes like this we still need to store the attribute. Don't request normal attribute from the normal map node now, instead of skipping it in the geometry code. --- intern/cycles/render/geometry.cpp | 20 -------------------- intern/cycles/render/nodes.cpp | 4 ---- 2 files changed, 24 deletions(-) diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 6804a006fe6..4de458de271 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -794,11 +794,6 @@ void GeometryManager::device_update_attributes(Device *device, foreach (AttributeRequest &req, attributes.requests) { Attribute *attr = geom->attributes.find(req); - /* Vertex normals are stored in DeviceScene.tri_vnormal. */ - if (attr && attr->std == ATTR_STD_VERTEX_NORMAL) { - continue; - } - update_attribute_element_size(geom, attr, ATTR_PRIM_GEOMETRY, @@ -811,11 +806,6 @@ void GeometryManager::device_update_attributes(Device *device, Mesh *mesh = static_cast(geom); Attribute *subd_attr = mesh->subd_attributes.find(req); - /* Vertex normals are stored in DeviceScene.tri_vnormal. */ - if (subd_attr && subd_attr->std == ATTR_STD_VERTEX_NORMAL) { - continue; - } - update_attribute_element_size(mesh, subd_attr, ATTR_PRIM_SUBD, @@ -870,11 +860,6 @@ void GeometryManager::device_update_attributes(Device *device, Attribute *attr = geom->attributes.find(req); if (attr) { - /* Vertex normals are stored in DeviceScene.tri_vnormal. */ - if (attr->std == ATTR_STD_VERTEX_NORMAL) { - continue; - } - /* force a copy if we need to reallocate all the data */ attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; } @@ -898,11 +883,6 @@ void GeometryManager::device_update_attributes(Device *device, Attribute *subd_attr = mesh->subd_attributes.find(req); if (subd_attr) { - /* Vertex normals are stored in DeviceScene.tri_vnormal. */ - if (subd_attr->std == ATTR_STD_VERTEX_NORMAL) { - continue; - } - /* force a copy if we need to reallocate all the data */ subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)]; } diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 5303d55242e..03b79d7de3e 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -6749,8 +6749,6 @@ void NormalMapNode::attributes(Shader *shader, AttributeRequestSet *attributes) attributes->add(ustring((string(attribute.c_str()) + ".tangent").c_str())); attributes->add(ustring((string(attribute.c_str()) + ".tangent_sign").c_str())); } - - attributes->add(ATTR_STD_VERTEX_NORMAL); } ShaderNode::attributes(shader, attributes); @@ -7026,8 +7024,6 @@ void VectorDisplacementNode::attributes(Shader *shader, AttributeRequestSet *att attributes->add(ustring((string(attribute.c_str()) + ".tangent").c_str())); attributes->add(ustring((string(attribute.c_str()) + ".tangent_sign").c_str())); } - - attributes->add(ATTR_STD_VERTEX_NORMAL); } ShaderNode::attributes(shader, attributes); From bc1e675bb905baec73f2758b009826530678e436 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 22 Sep 2021 17:05:44 +0200 Subject: [PATCH 0102/1500] Fix T91603: Cycles crash when volume becomes visible Making object which uses volume shader invisible will mark the shader as not having a volume, forcing re-compilation of the shader to bring it back to a consistent state. The compilation is happening as part of scene update, which needs to know kernel features. So there is a feedback loop. Use more relaxed way of knowing whether there is a volume in the shader for the kernel features, which doesn't require shader to be compiled first. Solves issues from the report, but potentially causes extra memory allocated if the volume part of graph is fully optimized out. This downside is solvable, but would need to split scene update into two steps (the one which requires on kernel, and the one which does not). It will be an interesting project to tackle, but for a bug fix is better to use simpler solution. --- intern/cycles/render/shader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index f6b23606e58..23786a3390f 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -729,7 +729,7 @@ uint ShaderManager::get_kernel_features(Scene *scene) } /* On top of volume nodes, also check if we need volume sampling because * e.g. an Emission node would slip through the KERNEL_FEATURE_NODE_VOLUME check */ - if (shader->has_volume) { + if (shader->has_volume_connected) { kernel_features |= KERNEL_FEATURE_VOLUME; } } From ac68b08c5b785ce08637fac14c20dc3b52cfed47 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 22 Sep 2021 17:30:00 +0200 Subject: [PATCH 0103/1500] Fix T91592: Negative Cycles remaining render time For the default startup was showing -14:-08.-48 as a remaining time. Was an integer overflow when specifying total number of pixel-samples. --- intern/cycles/render/session.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 47eeffd97fe..0539e02a8d5 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -430,7 +430,8 @@ void Session::do_delayed_reset() /* Progress. */ progress.reset_sample(); - progress.set_total_pixel_samples(buffer_params_.width * buffer_params_.height * params.samples); + progress.set_total_pixel_samples(static_cast(buffer_params_.width) * + buffer_params_.height * params.samples); if (!params.background) { progress.set_start_time(); From 5033310e8aca2cbbb5d55f306d8b98663469867f Mon Sep 17 00:00:00 2001 From: Chandrapal Singh Date: Wed, 22 Sep 2021 11:35:00 -0400 Subject: [PATCH 0104/1500] UI: Add description for Batch rename Added description for Batch rename which pop-ups when hovering the mouse over "Batch rename" inside the edit menu. Fixes T91390 Reviewed By: Blendify Maniphest Tasks: T91390 Differential Revision: https://developer.blender.org/D12594 --- release/scripts/startup/bl_operators/wm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index a386df5c428..ebf80ca9ee4 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -2194,6 +2194,7 @@ class WM_OT_batch_rename(Operator): bl_idname = "wm.batch_rename" bl_label = "Batch Rename" + bl_description = "Rename multiple items at once" bl_options = {'UNDO'} data_type: EnumProperty( From 4068b6b5a7112647f4efe4f7df0ee3c6399a1b2d Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 22 Sep 2021 18:00:39 +0200 Subject: [PATCH 0105/1500] Fix T91598: Decreasing sample count causes viewport to reset Differential Revision: https://developer.blender.org/D12601 --- intern/cycles/render/session.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 0539e02a8d5..823c34ed519 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -261,6 +261,7 @@ RenderWork Session::run_update_for_next_iteration() bool have_tiles = true; bool switched_to_new_tile = false; + const bool did_reset = delayed_reset_.do_reset; if (delayed_reset_.do_reset) { thread_scoped_lock buffers_lock(buffers_mutex_); do_delayed_reset(); @@ -272,8 +273,12 @@ RenderWork Session::run_update_for_next_iteration() /* Update number of samples in the integrator. * Ideally this would need to happen once in `Session::set_samples()`, but the issue there is - * the initial configuration when Session is created where the `set_samples()` is not used. */ - scene->integrator->set_aa_samples(params.samples); + * the initial configuration when Session is created where the `set_samples()` is not used. + * + * NOTE: Unless reset was requested only allow increasing number of samples. */ + if (did_reset || scene->integrator->get_aa_samples() < params.samples) { + scene->integrator->set_aa_samples(params.samples); + } /* Update denoiser settings. */ { From e1a0983b3c49f79660e907bd78fb18be54bc05ce Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 22 Sep 2021 18:11:15 +0200 Subject: [PATCH 0106/1500] Fix T91608: Cycles crash with tile size 0 --- intern/cycles/blender/addon/properties.py | 2 +- intern/cycles/blender/blender_sync.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index c2570e71efd..5fb0eeed925 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -746,7 +746,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): name="Tile Size", default=2048, description="", - min=0, max=16384, + min=8, max=16384, ) # Various fine-tuning debug flags diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index d6fc7ee1723..717f301b03e 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -855,7 +855,7 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, if (background) { params.use_auto_tile = RNA_boolean_get(&cscene, "use_auto_tile"); - params.tile_size = get_int(cscene, "tile_size"); + params.tile_size = max(get_int(cscene, "tile_size"), 8); } else { params.use_auto_tile = false; From 3180c6b4a70596a1c04d0abf7e035ce12e3fe599 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 22 Sep 2021 12:32:53 -0400 Subject: [PATCH 0107/1500] Cleanup: Themes: Remove invalid theme option `font_kerning_style` was removed in rBa1e91fbef3dc9a5d5c8456cd9a887aac1bdb652c --- release/scripts/presets/interface_theme/Blender_Light.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/release/scripts/presets/interface_theme/Blender_Light.xml b/release/scripts/presets/interface_theme/Blender_Light.xml index e75d4334582..132295316eb 100644 --- a/release/scripts/presets/interface_theme/Blender_Light.xml +++ b/release/scripts/presets/interface_theme/Blender_Light.xml @@ -1533,7 +1533,6 @@ Date: Wed, 22 Sep 2021 18:44:35 +0200 Subject: [PATCH 0108/1500] Geometry Nodes: fix evaluating field to span --- source/blender/functions/intern/field.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 6a4518ad4a6..599e4d4595a 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -468,7 +468,8 @@ Vector evaluate_fields(ResourceScope &scope, /* Still have to copy over the data in the destination provided by the caller. */ if (output_varray->is_span()) { /* Materialize into a span. */ - computed_varray->materialize_to_uninitialized(output_varray->get_internal_span().data()); + computed_varray->materialize_to_uninitialized(mask, + output_varray->get_internal_span().data()); } else { /* Slower materialize into a different structure. */ From 188de4bc31427c4883b87beea83126b42a628798 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 22 Sep 2021 19:44:58 +0200 Subject: [PATCH 0109/1500] BLI: initialize MutableSpan in default constructor --- source/blender/blenlib/BLI_span.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index 5adb47ba0b0..29098fd79ce 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -478,8 +478,8 @@ template class MutableSpan { using size_type = int64_t; protected: - T *data_; - int64_t size_; + T *data_ = nullptr; + int64_t size_ = 0; public: constexpr MutableSpan() = default; From a28ec920884c63fbef72cf5af75cd446744009ca Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 22 Sep 2021 19:45:18 +0200 Subject: [PATCH 0110/1500] BLI: avoid warning when copying empty StringRef --- source/blender/blenlib/BLI_string_ref.hh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index 257a0ba143e..dcf66bbf5ad 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -134,7 +134,9 @@ class StringRefBase { */ void unsafe_copy(char *dst) const { - memcpy(dst, data_, static_cast(size_)); + if (size_ > 0) { + memcpy(dst, data_, static_cast(size_)); + } dst[size_] = '\0'; } From 02bde2c1d5bbf1ea44209a618eb1947ea1d6cd25 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 22 Sep 2021 19:45:47 +0200 Subject: [PATCH 0111/1500] Cleanup: add using declarations --- source/blender/nodes/NOD_geometry_exec.hh | 2 ++ source/blender/nodes/geometry/nodes/node_geo_input_position.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index dbb5f8b240d..6ce3d0f2ab5 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -33,6 +33,8 @@ struct ModifierData; namespace blender::nodes { +using bke::AnonymousAttributeFieldInput; +using bke::AttributeFieldInput; using bke::AttributeIDRef; using bke::geometry_set_realize_instances; using bke::GeometryComponentFieldContext; diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc index c6365bf6809..3f3457a3acb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc @@ -26,7 +26,7 @@ static void geo_node_input_position_declare(NodeDeclarationBuilder &b) static void geo_node_input_position_exec(GeoNodeExecParams params) { Field position_field{ - std::make_shared("position", CPPType::get())}; + std::make_shared("position", CPPType::get())}; params.set_output("Position", std::move(position_field)); } From f893dea5863934e5c09cff673ff6cccd4106e069 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 22 Sep 2021 19:55:44 +0200 Subject: [PATCH 0112/1500] BLI: support initializing empty FunctionRef with nullptr This may sometimes be desired because it is more explicitely shows that the `FunctionRef` will be empty. --- source/blender/blenlib/BLI_function_ref.hh | 4 ++++ source/blender/blenlib/tests/BLI_function_ref_test.cc | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/source/blender/blenlib/BLI_function_ref.hh b/source/blender/blenlib/BLI_function_ref.hh index 70a064adc5d..71be7d7f029 100644 --- a/source/blender/blenlib/BLI_function_ref.hh +++ b/source/blender/blenlib/BLI_function_ref.hh @@ -110,6 +110,10 @@ template class FunctionRef { public: FunctionRef() = default; + FunctionRef(std::nullptr_t) + { + } + /** * A `FunctionRef` itself is a callable as well. However, we don't want that this * constructor is called when `Callable` is a `FunctionRef`. If we would allow this, it diff --git a/source/blender/blenlib/tests/BLI_function_ref_test.cc b/source/blender/blenlib/tests/BLI_function_ref_test.cc index 74f5014142c..20c9ee5885e 100644 --- a/source/blender/blenlib/tests/BLI_function_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_function_ref_test.cc @@ -124,4 +124,10 @@ TEST(function_ref, CallSafeVoid) EXPECT_EQ(value, 1); } +TEST(function_ref, InitializeWithNull) +{ + FunctionRef f{nullptr}; + EXPECT_FALSE(f); +} + } // namespace blender::tests From 6611f2cb740057b7019652524058069d01effa37 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 22 Sep 2021 20:07:20 +0200 Subject: [PATCH 0113/1500] Fix T91607: GPencil Tint modifier "apply" removes the effect --- source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c index 7ce731c0dd6..a055dff9449 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -281,7 +281,7 @@ static void bakeModifier(Main *UNUSED(bmain), bGPdata *gpd = ob->data; int oldframe = (int)DEG_get_ctime(depsgraph); - if (mmd->object == NULL) { + if ((mmd->type == GP_TINT_GRADIENT) && (mmd->object == NULL)) { return; } From ee49991999c9c36cf90bbed513c1a38a688f269b Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Wed, 22 Sep 2021 12:00:21 -0700 Subject: [PATCH 0114/1500] Cleanup: Silence missing switch case warning --- extern/tinygltf/README.blender | 3 ++- extern/tinygltf/tiny_gltf.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/extern/tinygltf/README.blender b/extern/tinygltf/README.blender index fe23d320b77..2aba84dea80 100644 --- a/extern/tinygltf/README.blender +++ b/extern/tinygltf/README.blender @@ -2,4 +2,5 @@ Project: TinyGLTF URL: https://github.com/syoyo/tinygltf License: MIT Upstream version: 2.5.0, 19a41d20ec0 -Local modifications: None +Local modifications: +* Silence "enum value not handled in switch" warnings due to JSON dependency. diff --git a/extern/tinygltf/tiny_gltf.h b/extern/tinygltf/tiny_gltf.h index 185bb0daa98..099e0c76d92 100644 --- a/extern/tinygltf/tiny_gltf.h +++ b/extern/tinygltf/tiny_gltf.h @@ -3201,6 +3201,7 @@ static bool ParseJsonAsValue(Value *ret, const json &o) { val = Value(o.get()); break; case json::value_t::null: + case json::value_t::binary: case json::value_t::discarded: // default: break; From bd01f4761cf983e411b9c3fd52dd83a3fd9103f8 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 22 Sep 2021 21:12:11 +0200 Subject: [PATCH 0115/1500] GPencil: Fix compiler warnings --- source/blender/makesrna/intern/rna_gpencil_modifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index a2d5b134056..6480d16ccd2 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -2950,7 +2950,7 @@ static void rna_def_modifier_gpencilweight_angle(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "target_vgname"); RNA_def_property_ui_text(prop, "Vertex Group", "Output Vertex group"); RNA_def_property_string_funcs( - prop, NULL, NULL, "rna_WeightProxGpencilModifier_target_vgname_set"); + prop, NULL, NULL, "rna_WeightAngleGpencilModifier_target_vgname_set"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_multiply", PROP_BOOLEAN, PROP_NONE); From c99cb814520480379276192f044c673ef857447b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 22 Sep 2021 14:39:09 -0500 Subject: [PATCH 0116/1500] Cleanup: Use new node socket declaration API for some shader nodes This converts socket declarations for most shader nodes that had already been converted to C++, but skips those that needed a special flag. --- .../blender/nodes/shader/node_shader_util.h | 1 + .../nodes/shader/nodes/node_shader_clamp.cc | 22 +++++----- .../nodes/shader/nodes/node_shader_curves.cc | 38 ++++++++-------- .../shader/nodes/node_shader_map_range.cc | 28 ++++++------ .../nodes/shader/nodes/node_shader_math.cc | 19 +++++--- .../nodes/shader/nodes/node_shader_mixRgb.cc | 22 +++++----- .../shader/nodes/node_shader_sepcombRGB.cc | 44 +++++++++---------- .../shader/nodes/node_shader_sepcombXYZ.cc | 44 +++++++++---------- .../nodes/node_shader_tex_white_noise.cc | 21 +++++---- .../shader/nodes/node_shader_valToRgb.cc | 36 ++++++++------- .../nodes/shader/nodes/node_shader_value.cc | 13 +++--- .../shader/nodes/node_shader_vector_math.cc | 23 +++++----- .../shader/nodes/node_shader_vector_rotate.cc | 24 +++++----- 13 files changed, 175 insertions(+), 160 deletions(-) diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h index a75354d3381..c647b86a19a 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.h @@ -73,6 +73,7 @@ # include "FN_multi_function_builder.hh" # include "NOD_multi_function.hh" +# include "NOD_socket_declarations.hh" # include "BLI_color.hh" # include "BLI_float3.hh" diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index b90397e4892..31d8f8ef15c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -23,18 +23,18 @@ #include "node_shader_util.h" -/* **************** Clamp ******************** */ -static bNodeSocketTemplate sh_node_clamp_in[] = { - {SOCK_FLOAT, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_clamp_out[] = { - {SOCK_FLOAT, N_("Result")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_clamp_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Value").min(0.0f).max(1.0f).default_value(1.0f); + b.add_input("Min").default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_output("Result"); }; +} // namespace blender::nodes + static void node_shader_init_clamp(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = NODE_CLAMP_MINMAX; /* clamp type */ @@ -79,7 +79,7 @@ void register_node_type_sh_clamp(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_CLAMP, "Clamp", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out); + ntype.declare = blender::nodes::sh_node_clamp_declare; node_type_init(&ntype, node_shader_init_clamp); node_type_gpu(&ntype, gpu_shader_clamp); ntype.build_multi_function = sh_node_clamp_build_multi_function; diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index df075d6e973..e4ada06133e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -23,17 +23,16 @@ #include "node_shader_util.h" -/* **************** CURVE VEC ******************** */ -static bNodeSocketTemplate sh_node_curve_vec_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input("Vector").min(-1.0f).max(1.0f); + b.add_output("Vector"); }; -static bNodeSocketTemplate sh_node_curve_vec_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_exec_curve_vec(void *UNUSED(data), int UNUSED(thread), @@ -157,7 +156,7 @@ void register_node_type_sh_curve_vec(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_curve_vec_in, sh_node_curve_vec_out); + ntype.declare = blender::nodes::sh_node_curve_vec_declare; node_type_init(&ntype, node_shader_init_curve_vec); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); @@ -169,16 +168,17 @@ void register_node_type_sh_curve_vec(void) } /* **************** CURVE RGB ******************** */ -static bNodeSocketTemplate sh_node_curve_rgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, + +namespace blender::nodes { + +static void sh_node_curve_rgb_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Color"); }; -static bNodeSocketTemplate sh_node_curve_rgb_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_exec_curve_rgb(void *UNUSED(data), int UNUSED(thread), @@ -332,7 +332,7 @@ void register_node_type_sh_curve_rgb(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, sh_node_curve_rgb_in, sh_node_curve_rgb_out); + ntype.declare = blender::nodes::sh_node_curve_rgb_declare; node_type_init(&ntype, node_shader_init_curve_rgb); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index f48e824ccb5..a5f9a24a728 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -25,21 +25,21 @@ #include "BLI_math_base_safe.h" -/* **************** Map Range ******************** */ -static bNodeSocketTemplate sh_node_map_range_in[] = { - {SOCK_FLOAT, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("From Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("From Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("To Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("To Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Steps"), 4.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_map_range_out[] = { - {SOCK_FLOAT, N_("Result")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_map_range_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Value").min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input("From Min").min(-10000.0f).max(10000.0f); + b.add_input("From Max").min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input("To Min").min(-10000.0f).max(10000.0f); + b.add_input("To Max").min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input("Steps").min(-10000.0f).max(10000.0f).default_value(4.0f); + b.add_output("Result"); }; +} // namespace blender::nodes + static void node_shader_update_map_range(bNodeTree *UNUSED(ntree), bNode *node) { bNodeSocket *sockSteps = nodeFindSocket(node, SOCK_IN, "Steps"); @@ -311,7 +311,7 @@ void register_node_type_sh_map_range(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_map_range_in, sh_node_map_range_out); + ntype.declare = blender::nodes::sh_node_map_range_declare; node_type_init(&ntype, node_shader_init_map_range); node_type_update(&ntype, node_shader_update_map_range); node_type_gpu(&ntype, gpu_shader_map_range); diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 66c50b6de46..80a27b8e6a1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -26,13 +26,18 @@ #include "NOD_math_functions.hh" /* **************** SCALAR MATH ******************** */ -static bNodeSocketTemplate sh_node_math_in[] = { - {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}}; -static bNodeSocketTemplate sh_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; +namespace blender::nodes { + +static void sh_node_math_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Value").default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_input("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_input("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_output("Value"); +}; + +} // namespace blender::nodes static const char *gpu_shader_get_name(int mode) { @@ -153,7 +158,7 @@ void register_node_type_sh_math(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_math_in, sh_node_math_out); + ntype.declare = blender::nodes::sh_node_math_declare; node_type_label(&ntype, node_math_label); node_type_gpu(&ntype, gpu_shader_math); node_type_update(&ntype, node_math_update); diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index ade35a40366..860cc260d5d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -23,18 +23,18 @@ #include "node_shader_util.h" -/* **************** MIX RGB ******************** */ -static bNodeSocketTemplate sh_node_mix_rgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Color1"), 0.5f, 0.5f, 0.5f, 1.0f}, - {SOCK_RGBA, N_("Color2"), 0.5f, 0.5f, 0.5f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_mix_rgb_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_mix_rgb_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Color1").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input("Color2").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output("Color"); }; +} // namespace blender::nodes + static void node_shader_exec_mix_rgb(void *UNUSED(data), int UNUSED(thread), bNode *node, @@ -187,7 +187,7 @@ void register_node_type_sh_mix_rgb(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, sh_node_mix_rgb_in, sh_node_mix_rgb_out); + ntype.declare = blender::nodes::sh_node_mix_rgb_declare; node_type_label(&ntype, node_blend_label); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb); node_type_gpu(&ntype, gpu_shader_mix_rgb); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index d9cbee33c0f..38f66547379 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -23,18 +23,18 @@ #include "node_shader_util.h" -/* **************** SEPARATE RGBA ******************** */ -static bNodeSocketTemplate sh_node_seprgb_in[] = { - {SOCK_RGBA, N_("Image"), 0.8f, 0.8f, 0.8f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_seprgb_out[] = { - {SOCK_FLOAT, N_("R")}, - {SOCK_FLOAT, N_("G")}, - {SOCK_FLOAT, N_("B")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_seprgb_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output("X"); + b.add_output("Y"); + b.add_output("Z"); }; +} // namespace blender::nodes + static void node_shader_exec_seprgb(void *UNUSED(data), int UNUSED(thread), bNode *UNUSED(node), @@ -107,7 +107,7 @@ void register_node_type_sh_seprgb(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_seprgb_in, sh_node_seprgb_out); + ntype.declare = blender::nodes::sh_node_seprgb_declare; node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_seprgb); node_type_gpu(&ntype, gpu_shader_seprgb); ntype.build_multi_function = sh_node_seprgb_build_multi_function; @@ -115,18 +115,18 @@ void register_node_type_sh_seprgb(void) nodeRegisterType(&ntype); } -/* **************** COMBINE RGB ******************** */ -static bNodeSocketTemplate sh_node_combrgb_in[] = { - {SOCK_FLOAT, N_("R"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED}, - {SOCK_FLOAT, N_("G"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED}, - {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_combrgb_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_combrgb_declare(NodeDeclarationBuilder &b) +{ + b.add_input("R").min(0.0f).max(1.0f); + b.add_input("G").min(0.0f).max(1.0f); + b.add_input("B").min(0.0f).max(1.0f); + b.add_output("Image"); }; +} // namespace blender::nodes + static void node_shader_exec_combrgb(void *UNUSED(data), int UNUSED(thread), bNode *UNUSED(node), @@ -166,7 +166,7 @@ void register_node_type_sh_combrgb(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_combrgb_in, sh_node_combrgb_out); + ntype.declare = blender::nodes::sh_node_combrgb_declare; node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_combrgb); node_type_gpu(&ntype, gpu_shader_combrgb); ntype.build_multi_function = sh_node_combrgb_build_multi_function; diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index 3048ed1e9fc..b4b3c48482f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -23,18 +23,18 @@ #include "node_shader_util.h" -/* **************** SEPARATE XYZ ******************** */ -static bNodeSocketTemplate sh_node_sepxyz_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_sepxyz_out[] = { - {SOCK_FLOAT, N_("X")}, - {SOCK_FLOAT, N_("Y")}, - {SOCK_FLOAT, N_("Z")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Vector").min(-10000.0f).max(10000.0f); + b.add_output("X"); + b.add_output("Y"); + b.add_output("Z"); }; +} // namespace blender::nodes + static int gpu_shader_sepxyz(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -92,25 +92,25 @@ void register_node_type_sh_sepxyz(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out); + ntype.declare = blender::nodes::sh_node_sepxyz_declare; node_type_gpu(&ntype, gpu_shader_sepxyz); ntype.build_multi_function = sh_node_sepxyz_build_multi_function; nodeRegisterType(&ntype); } -/* **************** COMBINE XYZ ******************** */ -static bNodeSocketTemplate sh_node_combxyz_in[] = { - {SOCK_FLOAT, N_("X"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f}, - {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f}, - {SOCK_FLOAT, N_("Z"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_combxyz_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_combxyz_declare(NodeDeclarationBuilder &b) +{ + b.add_input("X").min(-10000.0f).max(10000.0f); + b.add_input("Y").min(-10000.0f).max(10000.0f); + b.add_input("Z").min(-10000.0f).max(10000.0f); + b.add_output("Vector"); }; +} // namespace blender::nodes + static int gpu_shader_combxyz(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -132,7 +132,7 @@ void register_node_type_sh_combxyz(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out); + ntype.declare = blender::nodes::sh_node_combxyz_declare; node_type_gpu(&ntype, gpu_shader_combxyz); ntype.build_multi_function = sh_node_combxyz_build_multi_function; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index 6e973189065..bae16e10120 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -19,19 +19,18 @@ #include "../node_shader_util.h" -/* **************** WHITE NOISE **************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_white_noise_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}}; - -static bNodeSocketTemplate sh_node_tex_white_noise_out[] = { - {SOCK_FLOAT, N_("Value")}, - {SOCK_RGBA, N_("Color")}, - {-1, ""}, +static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Vector").min(-10000.0f).max(10000.0f); + b.add_input("W").min(-10000.0f).max(10000.0f); + b.add_output("Value"); + b.add_output("Color"); }; +} // namespace blender::nodes + static void node_shader_init_tex_white_noise(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = 3; @@ -70,7 +69,7 @@ void register_node_type_sh_tex_white_noise(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_TEX_WHITE_NOISE, "White Noise Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_white_noise_in, sh_node_tex_white_noise_out); + ntype.declare = blender::nodes::sh_node_tex_white_noise_declare; node_type_init(&ntype, node_shader_init_tex_white_noise); node_type_gpu(&ntype, gpu_shader_tex_white_noise); node_type_update(&ntype, node_shader_update_tex_white_noise); diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 62f6b38c79f..1870caffbb1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -29,17 +29,17 @@ #include "node_shader_util.h" -/* **************** VALTORGB ******************** */ -static bNodeSocketTemplate sh_node_valtorgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_valtorgb_out[] = { - {SOCK_RGBA, N_("Color")}, - {SOCK_FLOAT, N_("Alpha")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_valtorgb_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output("Color"); + b.add_output("Alpha"); }; +} // namespace blender::nodes + static void node_shader_exec_valtorgb(void *UNUSED(data), int UNUSED(thread), bNode *node, @@ -176,7 +176,7 @@ void register_node_type_sh_valtorgb(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_valtorgb_in, sh_node_valtorgb_out); + ntype.declare = blender::nodes::sh_node_valtorgb_declare; node_type_init(&ntype, node_shader_init_valtorgb); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); @@ -187,11 +187,15 @@ void register_node_type_sh_valtorgb(void) nodeRegisterType(&ntype); } -/* **************** RGBTOBW ******************** */ -static bNodeSocketTemplate sh_node_rgbtobw_in[] = { - {SOCK_RGBA, N_("Color"), 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f}, {-1, ""}}; -static bNodeSocketTemplate sh_node_rgbtobw_out[] = { - {SOCK_FLOAT, N_("Val"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, {-1, ""}}; +namespace blender::nodes { + +static void sh_node_rgbtobw_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Color").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output("Val"); +}; + +} // namespace blender::nodes static void node_shader_exec_rgbtobw(void *UNUSED(data), int UNUSED(thread), @@ -222,7 +226,7 @@ void register_node_type_sh_rgbtobw(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_rgbtobw_in, sh_node_rgbtobw_out); + ntype.declare = blender::nodes::sh_node_rgbtobw_declare; node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_rgbtobw); node_type_gpu(&ntype, gpu_shader_rgbtobw); diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 602d5a1cf56..74197ff8218 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -23,12 +23,15 @@ #include "node_shader_util.h" -/* **************** VALUE ******************** */ -static bNodeSocketTemplate sh_node_value_out[] = { - {SOCK_FLOAT, N_("Value"), 0.5f, 0, 0, 0, -FLT_MAX, FLT_MAX, PROP_NONE}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_value_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Vector"); }; +} // namespace blender::nodes + static int gpu_shader_value(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -51,7 +54,7 @@ void register_node_type_sh_value(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, sh_node_value_out); + ntype.declare = blender::nodes::sh_node_value_declare; node_type_gpu(&ntype, gpu_shader_value); ntype.build_multi_function = sh_node_value_build_multi_function; diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 4424db6aed1..5b24e8bb72d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -25,16 +25,19 @@ #include "NOD_math_functions.hh" -/* **************** VECTOR MATH ******************** */ -static bNodeSocketTemplate sh_node_vector_math_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}}; +namespace blender::nodes { -static bNodeSocketTemplate sh_node_vector_math_out[] = { - {SOCK_VECTOR, N_("Vector")}, {SOCK_FLOAT, N_("Value")}, {-1, ""}}; +static void sh_node_vector_math_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Vector").min(-10000.0f).max(10000.0f); + b.add_input("Vector", "Vector_001").min(-10000.0f).max(10000.0f); + b.add_input("Vector", "Vector_002").min(-10000.0f).max(10000.0f); + b.add_input("Scale").default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_output("Vector"); + b.add_output("Value"); +}; + +} // namespace blender::nodes static const char *gpu_shader_get_name(int mode) { @@ -274,7 +277,7 @@ void register_node_type_sh_vect_math(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_VECTOR_MATH, "Vector Math", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_vector_math_in, sh_node_vector_math_out); + ntype.declare = blender::nodes::sh_node_vector_math_declare; node_type_label(&ntype, node_vector_math_label); node_type_gpu(&ntype, gpu_shader_vector_math); node_type_update(&ntype, node_shader_update_vector_math); diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index bc51b7e29ea..e9fd6c4f31e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -23,20 +23,20 @@ #include "../node_shader_util.h" -/* **************** Vector Rotate ******************** */ -static bNodeSocketTemplate sh_node_vector_rotate_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_VECTOR, N_("Center"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_NONE}, - {SOCK_VECTOR, N_("Axis"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_NONE, PROP_NONE}, - {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE, PROP_NONE}, - {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, - {-1, ""}}; +namespace blender::nodes { -static bNodeSocketTemplate sh_node_vector_rotate_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, +static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Vector").min(0.0f).max(1.0f).hide_value(); + b.add_input("Vector"); + b.add_input("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); + b.add_input("Angle").subtype(PROP_ANGLE); + b.add_input("Rotation").subtype(PROP_EULER); + b.add_output("Value"); }; +} // namespace blender::nodes + static const char *gpu_shader_get_name(int mode) { switch (mode) { @@ -207,7 +207,7 @@ void register_node_type_sh_vector_rotate(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_VECTOR_ROTATE, "Vector Rotate", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_vector_rotate_in, sh_node_vector_rotate_out); + ntype.declare = blender::nodes::sh_node_vector_rotate_declare; node_type_gpu(&ntype, gpu_shader_vector_rotate); node_type_update(&ntype, node_shader_update_vector_rotate); ntype.build_multi_function = sh_node_vector_rotate_build_multi_function; From 6fb4c8f040f87bed4d6c69e3a718114eef91b7e1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 22 Sep 2021 16:58:29 -0500 Subject: [PATCH 0117/1500] Fix: Incorrect socket names after previous commit --- source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc | 6 +++--- source/blender/nodes/shader/nodes/node_shader_value.cc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index 38f66547379..63be399366f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -28,9 +28,9 @@ namespace blender::nodes { static void sh_node_seprgb_declare(NodeDeclarationBuilder &b) { b.add_input("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_output("X"); - b.add_output("Y"); - b.add_output("Z"); + b.add_output("R"); + b.add_output("G"); + b.add_output("B"); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 74197ff8218..1344ce5c5d9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -27,7 +27,7 @@ namespace blender::nodes { static void sh_node_value_declare(NodeDeclarationBuilder &b) { - b.add_output("Vector"); + b.add_output("Value"); }; } // namespace blender::nodes From 79bcc19240258fe697b583376f59902c3235691c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 22 Sep 2021 17:34:09 -0500 Subject: [PATCH 0118/1500] Cleanup: Move more shader nodes to socket declaration API I had to add `no_muted_links` to the declaration API. The name could change there, but I think it's more obvious than "internal" --- source/blender/nodes/NOD_node_declaration.hh | 7 ++ .../blender/nodes/intern/node_declaration.cc | 4 ++ .../shader/nodes/node_shader_tex_gradient.cc | 27 +++----- .../shader/nodes/node_shader_tex_musgrave.cc | 39 ++++------- .../shader/nodes/node_shader_tex_noise.cc | 41 +++++------ .../shader/nodes/node_shader_tex_voronoi.cc | 69 +++++++------------ 6 files changed, 74 insertions(+), 113 deletions(-) diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index d64b76ccbb9..8e9e72bf4c8 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -37,6 +37,7 @@ class SocketDeclaration { bool hide_label_ = false; bool hide_value_ = false; bool is_multi_input_ = false; + bool no_mute_links_ = false; friend NodeDeclarationBuilder; template friend class SocketDeclarationBuilder; @@ -93,6 +94,12 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { decl_->is_multi_input_ = value; return *(Self *)this; } + + Self &no_muted_links(bool value = true) + { + decl_->no_mute_links_ = value; + return *(Self *)this; + } }; using SocketDeclarationPtr = std::unique_ptr; diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index f6b6cc49b2e..8a38b68ec59 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -69,6 +69,7 @@ void SocketDeclaration::set_common_flags(bNodeSocket &socket) const SET_FLAG_FROM_TEST(socket.flag, hide_value_, SOCK_HIDE_VALUE); SET_FLAG_FROM_TEST(socket.flag, hide_label_, SOCK_HIDE_LABEL); SET_FLAG_FROM_TEST(socket.flag, is_multi_input_, SOCK_MULTI_INPUT); + SET_FLAG_FROM_TEST(socket.flag, no_mute_links_, SOCK_NO_INTERNAL_LINK); } bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const @@ -88,6 +89,9 @@ bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const if (((socket.flag & SOCK_MULTI_INPUT) != 0) != is_multi_input_) { return false; } + if (((socket.flag & SOCK_NO_INTERNAL_LINK) != 0) != no_mute_links_) { + return false; + } return true; } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc index 0c0d75179a9..e0520ee49d3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc @@ -19,27 +19,16 @@ #include "../node_shader_util.h" -/* **************** BLEND ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_gradient_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, +static void sh_node_tex_gradient_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Vector").hide_value(); + b.add_output("Color").no_muted_links(); + b.add_output("Fac").no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_gradient_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_gradient(bNodeTree *UNUSED(ntree), bNode *node) { @@ -72,7 +61,7 @@ void register_node_type_sh_tex_gradient(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_TEX_GRADIENT, "Gradient Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_gradient_in, sh_node_tex_gradient_out); + ntype.declare = blender::nodes::sh_node_tex_gradient_declare; node_type_init(&ntype, node_shader_init_tex_gradient); node_type_storage( &ntype, "NodeTexGradient", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index f5e9aef3aad..d33d92f25fd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -19,33 +19,22 @@ #include "../node_shader_util.h" -/* **************** MUSGRAVE ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_musgrave_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, - {SOCK_FLOAT, N_("Dimension"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Lacunarity"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Gain"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {-1, ""}, +static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Vector").hide_value(); + b.add_input("W").min(-1000.0f).max(1000.0f); + b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input("Detail").min(0.0f).max(16.0f).default_value(2.0f); + b.add_input("Dimension").min(0.0f).max(1000.0f).default_value(2.0f); + b.add_input("Lacunarity").min(0.0f).max(1000.0f).default_value(2.0f); + b.add_input("Offset").min(-1000.0f).max(1000.0f); + b.add_input("Gain").min(0.0f).max(1000.0f).default_value(1.0f); + b.add_output("Fac").no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_musgrave_out[] = { - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) { @@ -139,7 +128,7 @@ void register_node_type_sh_tex_musgrave(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_TEX_MUSGRAVE, "Musgrave Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_musgrave_in, sh_node_tex_musgrave_out); + ntype.declare = blender::nodes::sh_node_tex_musgrave_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, node_shader_init_tex_musgrave); node_type_storage( diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index c0deb232b2d..1ae6b3a616c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -21,32 +21,25 @@ #include "BLI_noise.hh" -/* **************** NOISE ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_noise_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, - {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {-1, ""}, +static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Vector").hide_value(); + b.add_input("W").min(-1000.0f).max(1000.0f); + b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input("Detail").min(0.0f).max(16.0f).default_value(2.0f); + b.add_input("Roughness") + .min(0.0f) + .max(1.0f) + .default_value(0.5f) + .subtype(PROP_FACTOR); + b.add_input("Distortion").min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_output("Fac").no_muted_links(); + b.add_output("Color").no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_noise_out[] = { - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) { @@ -252,7 +245,7 @@ void register_node_type_sh_tex_noise(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out); + ntype.declare = blender::nodes::sh_node_tex_noise_declare; node_type_init(&ntype, node_shader_init_tex_noise); node_type_storage( &ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index 1cc715d99ea..cea7af247c1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -19,53 +19,32 @@ #include "../node_shader_util.h" -/* **************** VORONOI ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_voronoi_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Smoothness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Exponent"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 32.0f}, - {SOCK_FLOAT, N_("Randomness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, +static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Vector").hide_value(); + b.add_input("W").min(-1000.0f).max(1000.0f); + b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input("Smoothness") + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR); + b.add_input("Exponent").min(0.0f).max(32.0f).default_value(0.5f); + b.add_input("Randomness") + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR); + b.add_output("Distance").no_muted_links(); + b.add_output("Color").no_muted_links(); + b.add_output("Position").no_muted_links(); + b.add_output("W").no_muted_links(); + b.add_output("Radius").no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_voronoi_out[] = { - {SOCK_FLOAT, - N_("Distance"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_VECTOR, - N_("Position"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Radius"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) { @@ -183,7 +162,7 @@ void register_node_type_sh_tex_voronoi(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_voronoi_in, sh_node_tex_voronoi_out); + ntype.declare = blender::nodes::sh_node_tex_voronoi_declare; node_type_init(&ntype, node_shader_init_tex_voronoi); node_type_storage( &ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage); From a78d3c5261b55ba2a63d550aafada518a0f88fbe Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Wed, 22 Sep 2021 17:29:23 -0600 Subject: [PATCH 0119/1500] Fix: T91602 ffmpeg crash Issue caused by our patch in rB1af722b81912 we replaced an array with a memory allocation but we forgot to update the assert which now used an invalid method to calculate the array size. SVN libs will have to be updated before T91602 will be fixed for end users. --- build_files/build_environment/patches/ffmpeg.diff | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/build_files/build_environment/patches/ffmpeg.diff b/build_files/build_environment/patches/ffmpeg.diff index 5a50a3f8756..c255f7c37a3 100644 --- a/build_files/build_environment/patches/ffmpeg.diff +++ b/build_files/build_environment/patches/ffmpeg.diff @@ -70,16 +70,18 @@ } --- a/libavcodec/rl.c +++ b/libavcodec/rl.c -@@ -71,7 +71,7 @@ av_cold void ff_rl_init(RLTable *rl, +@@ -71,17 +71,19 @@ av_cold void ff_rl_init_vlc(RLTable *rl, unsigned static_size) { int i, q; - VLC_TYPE table[1500][2] = {{0}}; + VLC_TYPE (*table)[2] = av_calloc(sizeof(VLC_TYPE), 1500 * 2); VLC vlc = { .table = table, .table_allocated = static_size }; - av_assert0(static_size <= FF_ARRAY_ELEMS(table)); +- av_assert0(static_size <= FF_ARRAY_ELEMS(table)); ++ av_assert0(static_size < 1500); init_vlc(&vlc, 9, rl->n + 1, &rl->table_vlc[0][1], 4, 2, &rl->table_vlc[0][0], 4, 2, INIT_VLC_USE_NEW_STATIC); -@@ -80,8 +80,10 @@ av_cold void ff_rl_init_vlc(RLTable *rl, unsigned static_size) + + for (q = 0; q < 32; q++) { int qmul = q * 2; int qadd = (q - 1) | 1; @@ -91,7 +93,7 @@ if (q == 0) { qmul = 1; -@@ -113,4 +115,5 @@ av_cold void ff_rl_init_vlc(RLTable *rl, unsigned static_size) +@@ -113,4 +115,5 @@ rl->rl_vlc[q][i].run = run; } } From 6e77afe6ec7b6a73f218f1fef264758abcbc778a Mon Sep 17 00:00:00 2001 From: Cian Jinks Date: Wed, 22 Sep 2021 21:23:44 -0400 Subject: [PATCH 0120/1500] Applying patch D12600, GSOC Knife Tools branch This adds constrained angle mode improvements, snapping to global and local orientation, visible distance and angle measurements, undo capability, x-ray mode, multi-object edit mode. See https://developer.blender.org/D12600 for more details. Note: this project moved some of the default keymappings around a bit, as discussed with users in the thread https://devtalk.blender.org/t/gsoc-2021-knife-tool-improvements-feedback/19047 We'll change the manual documentation in the next couple of days. --- .../keyconfig/keymap_data/blender_default.py | 27 +- .../keymap_data/industry_compatible_data.py | 11 +- .../startup/bl_ui/space_toolsystem_toolbar.py | 26 +- source/blender/editors/mesh/CMakeLists.txt | 1 + source/blender/editors/mesh/editmesh_knife.c | 2785 +++++++++++++---- .../editors/mesh/editmesh_knife_project.c | 2 +- source/blender/editors/mesh/mesh_intern.h | 3 +- 7 files changed, 2291 insertions(+), 564 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 1b0da23aa4a..5ecbe7715e3 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -5626,21 +5626,24 @@ def km_knife_tool_modal_map(_params): ("PANNING", {"type": 'MIDDLEMOUSE', "value": 'ANY', "any": True}, None), ("ADD_CUT_CLOSED", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "any": True}, None), ("ADD_CUT", {"type": 'LEFTMOUSE', "value": 'ANY', "any": True}, None), - ("CANCEL", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}, None), + ("UNDO", {"type": 'Z', "value": 'PRESS', "ctrl": True}, None), ("CONFIRM", {"type": 'RET', "value": 'PRESS', "any": True}, None), ("CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "any": True}, None), ("CONFIRM", {"type": 'SPACE', "value": 'PRESS', "any": True}, None), - ("NEW_CUT", {"type": 'E', "value": 'PRESS'}, None), - ("SNAP_MIDPOINTS_ON", {"type": 'LEFT_CTRL', "value": 'PRESS', "any": True}, None), - ("SNAP_MIDPOINTS_OFF", {"type": 'LEFT_CTRL', "value": 'RELEASE', "any": True}, None), - ("SNAP_MIDPOINTS_ON", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None), - ("SNAP_MIDPOINTS_OFF", {"type": 'RIGHT_CTRL', "value": 'RELEASE', "any": True}, None), - ("IGNORE_SNAP_ON", {"type": 'LEFT_SHIFT', "value": 'PRESS', "any": True}, None), - ("IGNORE_SNAP_OFF", {"type": 'LEFT_SHIFT', "value": 'RELEASE', "any": True}, None), - ("IGNORE_SNAP_ON", {"type": 'RIGHT_SHIFT', "value": 'PRESS', "any": True}, None), - ("IGNORE_SNAP_OFF", {"type": 'RIGHT_SHIFT', "value": 'RELEASE', "any": True}, None), - ("ANGLE_SNAP_TOGGLE", {"type": 'C', "value": 'PRESS'}, None), - ("CUT_THROUGH_TOGGLE", {"type": 'Z', "value": 'PRESS'}, None), + ("NEW_CUT", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None), + ("SNAP_MIDPOINTS_ON", {"type": 'LEFT_SHIFT', "value": 'PRESS', "any": True}, None), + ("SNAP_MIDPOINTS_OFF", {"type": 'LEFT_SHIFT', "value": 'RELEASE', "any": True}, None), + ("SNAP_MIDPOINTS_ON", {"type": 'RIGHT_SHIFT', "value": 'PRESS', "any": True}, None), + ("SNAP_MIDPOINTS_OFF", {"type": 'RIGHT_SHIFT', "value": 'RELEASE', "any": True}, None), + ("IGNORE_SNAP_ON", {"type": 'LEFT_CTRL', "value": 'PRESS', "any": True}, None), + ("IGNORE_SNAP_OFF", {"type": 'LEFT_CTRL', "value": 'RELEASE', "any": True}, None), + ("IGNORE_SNAP_ON", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None), + ("IGNORE_SNAP_OFF", {"type": 'RIGHT_CTRL', "value": 'RELEASE', "any": True}, None), + ("ANGLE_SNAP_TOGGLE", {"type": 'A', "value": 'PRESS'}, None), + ("CYCLE_ANGLE_SNAP_EDGE", {"type": 'R', "value": 'PRESS'}, None), + ("CUT_THROUGH_TOGGLE", {"type": 'C', "value": 'PRESS'}, None), + ("SHOW_DISTANCE_ANGLE_TOGGLE", {"type": 'S', "value": 'PRESS'}, None), + ("DEPTH_TEST_TOGGLE", {"type": 'V', "value": 'PRESS'}, None), ]) return keymap diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index dbe351eb10c..6a24f072ed0 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -3866,7 +3866,8 @@ def km_knife_tool_modal_map(_params): ("CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "any": True}, None), ("ADD_CUT_CLOSED", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "any": True}, None), ("ADD_CUT", {"type": 'LEFTMOUSE', "value": 'ANY', "any": True}, None), - ("NEW_CUT", {"type": 'E', "value": 'PRESS'}, None), + ("UNDO", {"type": 'Z', "value": 'PRESS', "ctrl": True}, None), + ("NEW_CUT", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None), ("SNAP_MIDPOINTS_ON", {"type": 'LEFT_CTRL', "value": 'PRESS'}, None), ("SNAP_MIDPOINTS_OFF", {"type": 'LEFT_CTRL', "value": 'RELEASE'}, None), ("SNAP_MIDPOINTS_ON", {"type": 'RIGHT_CTRL', "value": 'PRESS'}, None), @@ -3875,11 +3876,13 @@ def km_knife_tool_modal_map(_params): ("IGNORE_SNAP_OFF", {"type": 'LEFT_SHIFT', "value": 'RELEASE', "any": True}, None), ("IGNORE_SNAP_ON", {"type": 'RIGHT_SHIFT', "value": 'PRESS', "any": True}, None), ("IGNORE_SNAP_OFF", {"type": 'RIGHT_SHIFT', "value": 'RELEASE', "any": True}, None), - ("ANGLE_SNAP_TOGGLE", {"type": 'C', "value": 'PRESS'}, None), - ("CUT_THROUGH_TOGGLE", {"type": 'X', "value": 'PRESS'}, None), + ("ANGLE_SNAP_TOGGLE", {"type": 'A', "value": 'PRESS'}, None), + ("CYCLE_ANGLE_SNAP_EDGE", {"type": 'R', "value": 'PRESS'}, None), + ("CUT_THROUGH_TOGGLE", {"type": 'C', "value": 'PRESS'}, None), ("PANNING", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None), ("PANNING", {"type": 'RIGHTMOUSE', "value": 'PRESS', "alt": True}, None), - ("CONFIRM", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None), + ("SHOW_DISTANCE_ANGLE_TOGGLE", {"type": 'D', "value": 'PRESS'}, None), + ("DEPTH_TEST_TOGGLE", {"type": 'V', "value": 'PRESS'}, None), ]) return keymap diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 77a6ff79598..a4a51cb9910 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1087,11 +1087,29 @@ class _defs_edit_mesh: @ToolDef.from_fn def knife(): - def draw_settings(_context, layout, tool): + def draw_settings(_context, layout, tool, *, extra=False): + show_extra = False props = tool.operator_properties("mesh.knife_tool") - layout.prop(props, "use_occlude_geometry") - layout.prop(props, "only_selected") - + if not extra: + row = layout.row() + layout.prop(props, "use_occlude_geometry") + row = layout.row() + layout.prop(props, "only_selected") + row = layout.row() + layout.prop(props, "xray") + region_is_header = bpy.context.region.type == 'TOOL_HEADER' + if region_is_header: + show_extra = True + else: + extra = True + if extra: + layout.use_property_split = True + layout.prop(props, "visible_measurements") + layout.prop(props, "angle_snapping") + layout.label(text="Angle Snapping Increment") + layout.row().prop(props, "angle_snapping_increment", text="", expand=True) + if show_extra: + layout.popover("TOPBAR_PT_tool_settings_extra", text="...") return dict( idname="builtin.knife", label="Knife", diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index 35bf295a678..4ad2e57d266 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -18,6 +18,7 @@ set(INC ../include ../uvedit + ../../blenfont ../../blenkernel ../../blenlib ../../blentranslation diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 3e3593d18fd..eee4aec7459 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -29,6 +29,8 @@ #include "MEM_guardedalloc.h" +#include "BLF_api.h" + #include "BLI_alloca.h" #include "BLI_array.h" #include "BLI_linklist.h" @@ -36,6 +38,7 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_smallhash.h" +#include "BLI_stack.h" #include "BLI_string.h" #include "BLT_translation.h" @@ -44,15 +47,20 @@ #include "BKE_context.h" #include "BKE_editmesh.h" #include "BKE_editmesh_bvh.h" +#include "BKE_layer.h" #include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_unit.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_state.h" #include "ED_mesh.h" +#include "ED_numinput.h" #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_transform.h" #include "ED_view3d.h" #include "WM_api.h" @@ -69,15 +77,15 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" -#include "mesh_intern.h" /* own include */ +#include "mesh_intern.h" /* Own include. */ -/* detect isolated holes and fill them */ +/* Detect isolated holes and fill them. */ #define USE_NET_ISLAND_CONNECT -#define KMAXDIST (10 * U.dpi_fac) /* max mouse distance from edge before not detecting it */ +#define KMAXDIST (10 * U.dpi_fac) /* Max mouse distance from edge before not detecting it. */ -/* WARNING: knife float precision is fragile: - * be careful before making changes here see: (T43229, T42864, T42459, T41164). +/* WARNING: Knife float precision is fragile: + * Be careful before making changes here see: (T43229, T42864, T42459, T41164). */ #define KNIFE_FLT_EPS 0.00001f #define KNIFE_FLT_EPS_SQUARED (KNIFE_FLT_EPS * KNIFE_FLT_EPS) @@ -87,24 +95,37 @@ #define KNIFE_FLT_EPS_PX_EDGE 0.05f #define KNIFE_FLT_EPS_PX_FACE 0.05f +#define KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT 30.0f +#define KNIFE_MIN_ANGLE_SNAPPING_INCREMENT 0.0f +#define KNIFE_MAX_ANGLE_SNAPPING_INCREMENT 90.0f + typedef struct KnifeColors { uchar line[3]; uchar edge[3]; + uchar edge_extra[3]; uchar curpoint[3]; uchar curpoint_a[4]; uchar point[3]; uchar point_a[4]; + uchar xaxis[3]; + uchar yaxis[3]; + uchar zaxis[3]; + uchar axis_extra[3]; } KnifeColors; -/* knifetool operator */ +/* Knifetool Operator. */ typedef struct KnifeVert { - BMVert *v; /* non-NULL if this is an original vert */ + Object *ob; + uint base_index; + BMVert *v; /* Non-NULL if this is an original vert. */ ListBase edges; ListBase faces; float co[3], cageco[3]; bool is_face, in_space; - bool is_cut; /* along a cut created by user input (will draw too) */ + bool is_cut; /* Along a cut created by user input (will draw too). */ + bool is_invalid; + bool is_splitting; /* Created when an edge was split. */ } KnifeVert; typedef struct Ref { @@ -114,19 +135,21 @@ typedef struct Ref { typedef struct KnifeEdge { KnifeVert *v1, *v2; - BMFace *basef; /* face to restrict face fill to */ + BMFace *basef; /* Face to restrict face fill to. */ ListBase faces; - BMEdge *e /* , *e_old */; /* non-NULL if this is an original edge */ - bool is_cut; /* along a cut created by user input (will draw too) */ + BMEdge *e; /* Non-NULL if this is an original edge. */ + bool is_cut; /* Along a cut created by user input (will draw too). */ + bool is_invalid; + int splits; /* Number of times this edge has been split. */ } KnifeEdge; typedef struct KnifeLineHit { float hit[3], cagehit[3]; - float schit[2]; /* screen coordinates for cagehit */ - float l; /* lambda along cut line */ - float perc; /* lambda along hit line */ - float m; /* depth front-to-back */ + float schit[2]; /* Screen coordinates for cagehit. */ + float l; /* Lambda along cut line. */ + float perc; /* Lambda along hit line. */ + float m; /* Depth front-to-back. */ /* Exactly one of kfe, v, or f should be non-NULL, * saying whether cut line crosses and edge, @@ -134,6 +157,8 @@ typedef struct KnifeLineHit { KnifeEdge *kfe; KnifeVert *v; BMFace *f; + Object *ob; + uint base_index; } KnifeLineHit; typedef struct KnifePosData { @@ -146,30 +171,61 @@ typedef struct KnifePosData { KnifeVert *vert; KnifeEdge *edge; BMFace *bmface; + Object *ob; /* Object of the vert, edge or bmface. */ + uint base_index; - /** When true, the cursor isn't over a face. */ + /* When true, the cursor isn't over a face. */ bool is_space; - float mval[2]; /* mouse screen position (may be non-integral if snapped to something) */ + float mval[2]; /* Mouse screen position (may be non-integral if snapped to something). */ } KnifePosData; +typedef struct KnifeMeasureData { + float cage[3]; + float mval[2]; + bool is_stored; + float corr_prev_cage[3]; /* "knife_start_cut" updates prev.cage breaking angle calculations, + * store correct version. */ +} KnifeMeasureData; + +typedef struct KnifeUndoFrame { + int cuts; /* Line hits cause multiple edges/cuts to be created at once. */ + int splits; /* Number of edges split. */ + KnifePosData pos; /* Store previous KnifePosData. */ + KnifeMeasureData mdata; + +} KnifeUndoFrame; + +typedef struct KnifeBVH { + BVHTree *tree; /* Knife Custom BVH Tree. */ + BMLoop *(*looptris)[3]; /* Used by #knife_bvh_raycast_cb to store the intersecting looptri. */ + float uv[2]; /* Used by #knife_bvh_raycast_cb to store the intersecting uv. */ + uint base_index; + + /* Use #bm_ray_cast_cb_elem_not_in_face_check. */ + bool (*filter_cb)(BMFace *f, void *userdata); + void *filter_data; + +} KnifeBVH; + /* struct for properties used while drawing */ typedef struct KnifeTool_OpData { - ARegion *region; /* region that knifetool was activated in */ - void *draw_handle; /* for drawing preview loop */ - ViewContext vc; /* NOTE: _don't_ use 'mval', instead use the one we define below. */ - float mval[2]; /* mouse value with snapping applied */ - // bContext *C; + ARegion *region; /* Region that knifetool was activated in. */ + void *draw_handle; /* For drawing preview loop. */ + ViewContext vc; /* Note: _don't_ use 'mval', instead use the one we define below. */ + float mval[2]; /* Mouse value with snapping applied. */ Scene *scene; - Object *ob; - BMEditMesh *em; + + /* Used for swapping current object when in multi-object edit mode. */ + Object **objects; + uint objects_len; MemArena *arena; - /* reused for edge-net filling */ + /* Reused for edge-net filling. */ struct { - /* cleared each use */ + /* Cleared each use. */ GSet *edge_visit; #ifdef USE_NET_ISLAND_CONNECT MemArena *arena; @@ -181,50 +237,43 @@ typedef struct KnifeTool_OpData { GHash *kedgefacemap; GHash *facetrimap; - BMBVHTree *bmbvh; + KnifeBVH bvh; + const float (**cagecos)[3]; BLI_mempool *kverts; BLI_mempool *kedges; + BLI_Stack *undostack; + BLI_Stack *splitstack; /* Store edge splits by #knife_split_edge. */ + float vthresh; float ethresh; - /* used for drag-cutting */ + /* Used for drag-cutting. */ KnifeLineHit *linehits; int totlinehit; - /* Data for mouse-position-derived data */ - KnifePosData curr; /* current point under the cursor */ - KnifePosData prev; /* last added cut (a line draws from the cursor to this) */ - KnifePosData init; /* the first point in the cut-list, used for closing the loop */ + /* Data for mouse-position-derived data. */ + KnifePosData curr; /* Current point under the cursor. */ + KnifePosData prev; /* Last added cut (a line draws from the cursor to this). */ + KnifePosData init; /* The first point in the cut-list, used for closing the loop. */ - /** Number of knife edges `kedges`. */ + /* Number of knife edges `kedges`. */ int totkedge; - /** Number of knife vertices, `kverts`. */ + /* Number of knife vertices, `kverts`. */ int totkvert; BLI_mempool *refs; - /** - * Use this instead of #Object.imat since it's calculated using #invert_m4_m4_safe_ortho - * to support objects with zero scale on a single axis. - */ - float ob_imat[4][4]; - - float projmat[4][4]; - float projmat_inv[4][4]; - /* vector along view z axis (object space, normalized) */ - float proj_zaxis[3]; - KnifeColors colors; - /* run by the UI or not */ + /* Run by the UI or not. */ bool is_interactive; /* Operator options. */ - bool cut_through; /* preference, can be modified at runtime (that feature may go) */ - bool only_select; /* set on initialization */ - bool select_result; /* set on initialization */ + bool cut_through; /* Preference, can be modified at runtime (that feature may go). */ + bool only_select; /* Set on initialization. */ + bool select_result; /* Set on initialization. */ bool is_ortho; float ortho_extent; @@ -240,17 +289,38 @@ typedef struct KnifeTool_OpData { bool ignore_edge_snapping; bool ignore_vert_snapping; - /* use to check if we're currently dragging an angle snapped line */ + NumInput num; + float angle_snapping_increment; /* Degrees */ + + /* Use to check if we're currently dragging an angle snapped line. */ + short angle_snapping_mode; bool is_angle_snapping; bool angle_snapping; float angle; + /* Relative angle snapping reference edge. */ + KnifeEdge *snap_ref_edge; + int snap_ref_edges_count; + int snap_edge; /* Used by #KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE to choose an edge for snapping. */ - const float (*cagecos)[3]; + short constrain_axis; + short constrain_axis_mode; + bool axis_constrained; + char axis_string[2]; + + short dist_angle_mode; + bool show_dist_angle; + KnifeMeasureData mdata; /* Data for distance and angle drawing calculations. */ + + KnifeUndoFrame *undo; /* Current undo frame. */ + bool is_drag_undo; + + bool depth_test; } KnifeTool_OpData; enum { KNF_MODAL_CANCEL = 1, KNF_MODAL_CONFIRM, + KNF_MODAL_UNDO, KNF_MODAL_MIDPOINT_ON, KNF_MODAL_MIDPOINT_OFF, KNF_MODAL_NEW_CUT, @@ -258,45 +328,72 @@ enum { KNF_MODAL_IGNORE_SNAP_OFF, KNF_MODAL_ADD_CUT, KNF_MODAL_ANGLE_SNAP_TOGGLE, + KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE, KNF_MODAL_CUT_THROUGH_TOGGLE, + KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE, + KNF_MODAL_DEPTH_TEST_TOGGLE, KNF_MODAL_PANNING, KNF_MODAL_ADD_CUT_CLOSED, }; +enum { + KNF_CONSTRAIN_ANGLE_MODE_NONE = 0, + KNF_CONSTRAIN_ANGLE_MODE_SCREEN = 1, + KNF_CONSTRAIN_ANGLE_MODE_RELATIVE = 2 +}; + +enum { + KNF_CONSTRAIN_AXIS_NONE = 0, + KNF_CONSTRAIN_AXIS_X = 1, + KNF_CONSTRAIN_AXIS_Y = 2, + KNF_CONSTRAIN_AXIS_Z = 3 +}; + +enum { + KNF_CONSTRAIN_AXIS_MODE_NONE = 0, + KNF_CONSTRAIN_AXIS_MODE_GLOBAL = 1, + KNF_CONSTRAIN_AXIS_MODE_LOCAL = 2 +}; + +enum { + KNF_MEASUREMENT_NONE = 0, + KNF_MEASUREMENT_BOTH = 1, + KNF_MEASUREMENT_DISTANCE = 2, + KNF_MEASUREMENT_ANGLE = 3 +}; + /* -------------------------------------------------------------------- */ /** \name Drawing * \{ */ -static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) +#if 1 +static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], float r_v2[3]) { - float v1[3], v2[3]; float planes[4][4]; + planes_from_projmat( + kcd->vc.rv3d->persmat, planes[2], planes[0], planes[1], planes[3], NULL, NULL); - planes_from_projmat(kcd->projmat, planes[2], planes[0], planes[1], planes[3], NULL, NULL); - - /* ray-cast all planes */ + /* Ray-cast all planes. */ { float ray_dir[3]; float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}}; float lambda_best[2] = {-FLT_MAX, FLT_MAX}; int i; - /* we (sometimes) need the lines to be at the same depth before projecting */ -#if 0 + /* We (sometimes) need the lines to be at the same depth before projecting. */ +# if 0 sub_v3_v3v3(ray_dir, kcd->curr.cage, kcd->prev.cage); -#else +# else { float curr_cage_adjust[3]; float co_depth[3]; copy_v3_v3(co_depth, kcd->prev.cage); - mul_m4_v3(kcd->ob->obmat, co_depth); ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust); - mul_m4_v3(kcd->ob_imat, curr_cage_adjust); sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage); } -#endif +# endif for (i = 0; i < 4; i++) { float ray_hit[3]; @@ -318,9 +415,16 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) } } - copy_v3_v3(v1, ray_hit_best[0]); - copy_v3_v3(v2, ray_hit_best[1]); + copy_v3_v3(r_v1, ray_hit_best[0]); + copy_v3_v3(r_v2, ray_hit_best[1]); } +} + +static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) +{ + float v1[3], v2[3]; + + knifetool_raycast_planes(kcd, v1, v2); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -336,7 +440,454 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) immUnbindProgram(); } -/* modal loop selection drawing callback */ +static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd) +{ + if (!compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) { + float v1[3], v2[3]; + + /* This is causing buggyness when prev.cage and curr.cage are too close together. */ + knifetool_raycast_planes(kcd, v1, v2); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + switch (kcd->constrain_axis) { + case KNF_CONSTRAIN_AXIS_X: { + immUniformColor3ubv(kcd->colors.xaxis); + break; + } + case KNF_CONSTRAIN_AXIS_Y: { + immUniformColor3ubv(kcd->colors.yaxis); + break; + } + case KNF_CONSTRAIN_AXIS_Z: { + immUniformColor3ubv(kcd->colors.zaxis); + break; + } + default: { + immUniformColor3ubv(kcd->colors.axis_extra); + break; + } + } + + GPU_line_width(2.0); + + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + immEnd(); + + immUnbindProgram(); + } +} +#endif + +static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd) +{ + GPU_matrix_push_projection(); + GPU_matrix_push(); + GPU_matrix_identity_set(); + wmOrtho2_region_pixelspace(kcd->region); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + char numstr[256]; + float numstr_size[2]; + float posit[2]; + const float bg_margin = 4.0f * U.dpi_fac; + const int font_size = 14.0f * U.pixelsize; + const int distance_precision = 4; + + /* Calculate distance and convert to string. */ + const float cut_len = len_v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage); + + UnitSettings *unit = &kcd->scene->unit; + if (unit->system == USER_UNIT_NONE) { + BLI_snprintf(numstr, sizeof(numstr), "%.*f", distance_precision, cut_len); + } + else { + BKE_unit_value_as_string(numstr, + sizeof(numstr), + (double)(cut_len * unit->scale_length), + distance_precision, + B_UNIT_LENGTH, + unit, + false); + } + + BLF_enable(blf_mono_font, BLF_ROTATION); + BLF_size(blf_mono_font, font_size, U.dpi); + BLF_rotation(blf_mono_font, 0.0f); + BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]); + + /* Center text. */ + mid_v2_v2v2(posit, kcd->prev.mval, kcd->curr.mval); + posit[0] -= numstr_size[0] / 2.0f; + posit[1] -= numstr_size[1] / 2.0f; + + /* Draw text background. */ + float color_back[4] = {0.0f, 0.0f, 0.0f, 0.5f}; /* TODO: Replace with theme color. */ + immUniformColor4fv(color_back); + + GPU_blend(GPU_BLEND_ALPHA); + immRectf(pos, + posit[0] - bg_margin, + posit[1] - bg_margin, + posit[0] + bg_margin + numstr_size[0], + posit[1] + bg_margin + numstr_size[1]); + GPU_blend(GPU_BLEND_NONE); + immUnbindProgram(); + + /* Draw text. */ + uchar color_text[3]; + UI_GetThemeColor3ubv(TH_TEXT, color_text); + + BLF_color3ubv(blf_mono_font, color_text); + BLF_position(blf_mono_font, posit[0], posit[1], 0.0f); + BLF_draw(blf_mono_font, numstr, sizeof(numstr)); + BLF_disable(blf_mono_font, BLF_ROTATION); + + GPU_matrix_pop(); + GPU_matrix_pop_projection(); +} + +static void knifetool_draw_angle(const KnifeTool_OpData *kcd, + const float start[3], + const float mid[3], + const float end[3], + const float start_ss[2], + const float mid_ss[2], + const float end_ss[2], + const float angle) +{ + const RegionView3D *rv3d = kcd->region->regiondata; + const int arc_steps = 24; + const float arc_size = 64.0f * U.dpi_fac; + const float bg_margin = 4.0f * U.dpi_fac; + const float cap_size = 4.0f * U.dpi_fac; + const int font_size = 14 * U.pixelsize; + const int angle_precision = 3; + + /* Angle arc in 3d space. */ + GPU_blend(GPU_BLEND_ALPHA); + + const uint pos_3d = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + { + float dir_tmp[3]; + float ar_coord[3]; + + float dir_a[3]; + float dir_b[3]; + float quat[4]; + float axis[3]; + float arc_angle; + + const float inverse_average_scale = 1 / + (kcd->curr.ob->obmat[0][0] + kcd->curr.ob->obmat[1][1] + + kcd->curr.ob->obmat[2][2]); + + const float px_scale = + 3.0f * inverse_average_scale * + (ED_view3d_pixel_size_no_ui_scale(rv3d, mid) * + min_fff(arc_size, len_v2v2(start_ss, mid_ss) / 2.0f, len_v2v2(end_ss, mid_ss) / 2.0f)); + + sub_v3_v3v3(dir_a, start, mid); + sub_v3_v3v3(dir_b, end, mid); + normalize_v3(dir_a); + normalize_v3(dir_b); + + cross_v3_v3v3(axis, dir_a, dir_b); + arc_angle = angle_normalized_v3v3(dir_a, dir_b); + + axis_angle_to_quat(quat, axis, arc_angle / arc_steps); + + copy_v3_v3(dir_tmp, dir_a); + + immUniformThemeColor3(TH_WIRE); + GPU_line_width(1.0); + + immBegin(GPU_PRIM_LINE_STRIP, arc_steps + 1); + for (int j = 0; j <= arc_steps; j++) { + madd_v3_v3v3fl(ar_coord, mid, dir_tmp, px_scale); + mul_qt_v3(quat, dir_tmp); + + immVertex3fv(pos_3d, ar_coord); + } + immEnd(); + } + + immUnbindProgram(); + + /* Angle text and background in 2d space. */ + GPU_matrix_push_projection(); + GPU_matrix_push(); + GPU_matrix_identity_set(); + wmOrtho2_region_pixelspace(kcd->region); + + uint pos_2d = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + /* Angle as string. */ + char numstr[256]; + float numstr_size[2]; + float posit[2]; + + UnitSettings *unit = &kcd->scene->unit; + if (unit->system == USER_UNIT_NONE) { + BLI_snprintf(numstr, sizeof(numstr), "%.*f°", angle_precision, RAD2DEGF(angle)); + } + else { + BKE_unit_value_as_string( + numstr, sizeof(numstr), (double)angle, angle_precision, B_UNIT_ROTATION, unit, false); + } + + BLF_enable(blf_mono_font, BLF_ROTATION); + BLF_size(blf_mono_font, font_size, U.dpi); + BLF_rotation(blf_mono_font, 0.0f); + BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]); + + posit[0] = mid_ss[0] + (cap_size * 2.0f); + posit[1] = mid_ss[1] - (numstr_size[1] / 2.0f); + + /* Draw text background. */ + float color_back[4] = {0.0f, 0.0f, 0.0f, 0.5f}; /* TODO: Replace with theme color. */ + immUniformColor4fv(color_back); + + GPU_blend(GPU_BLEND_ALPHA); + immRectf(pos_2d, + posit[0] - bg_margin, + posit[1] - bg_margin, + posit[0] + bg_margin + numstr_size[0], + posit[1] + bg_margin + numstr_size[1]); + GPU_blend(GPU_BLEND_NONE); + immUnbindProgram(); + + /* Draw text. */ + uchar color_text[3]; + UI_GetThemeColor3ubv(TH_TEXT, color_text); + + BLF_color3ubv(blf_mono_font, color_text); + BLF_position(blf_mono_font, posit[0], posit[1], 0.0f); + BLF_rotation(blf_mono_font, 0.0f); + BLF_draw(blf_mono_font, numstr, sizeof(numstr)); + BLF_disable(blf_mono_font, BLF_ROTATION); + + GPU_matrix_pop(); + GPU_matrix_pop_projection(); + + GPU_blend(GPU_BLEND_NONE); +} + +static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) +{ + Ref *ref; + KnifeVert *kfv; + KnifeVert *tempkfv; + KnifeEdge *kfe; + KnifeEdge *tempkfe; + + if (kcd->curr.vert) { + kfv = kcd->curr.vert; + + float min_angle = FLT_MAX; + float angle = 0.0f; + float *end; + + kfe = ((Ref *)kfv->edges.first)->ref; + for (ref = kfv->edges.first; ref; ref = ref->next) { + tempkfe = ref->ref; + if (tempkfe->v1 != kfv) { + tempkfv = tempkfe->v1; + } + else { + tempkfv = tempkfe->v2; + } + angle = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, tempkfv->cageco); + if (angle < min_angle) { + min_angle = angle; + kfe = tempkfe; + end = tempkfv->cageco; + } + } + + if (min_angle > KNIFE_FLT_EPSBIG) { + /* Last vertex in screen space. */ + float end_ss[2]; + ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); + + knifetool_draw_angle(kcd, + kcd->mdata.corr_prev_cage, + kcd->curr.cage, + end, + kcd->prev.mval, + kcd->curr.mval, + end_ss, + min_angle); + } + } + else if (kcd->curr.edge) { + kfe = kcd->curr.edge; + + /* Check for most recent cut (if cage is part of previous cut). */ + if (!compare_v3v3(kfe->v1->cageco, kcd->mdata.corr_prev_cage, KNIFE_FLT_EPSBIG) && + !compare_v3v3(kfe->v2->cageco, kcd->mdata.corr_prev_cage, KNIFE_FLT_EPSBIG)) { + /* Determine acute angle. */ + float angle1 = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, kfe->v1->cageco); + float angle2 = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, kfe->v2->cageco); + + float angle; + float *end; + if (angle1 < angle2) { + angle = angle1; + end = kfe->v1->cageco; + } + else { + angle = angle2; + end = kfe->v2->cageco; + } + + /* Last vertex in screen space. */ + float end_ss[2]; + ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); + + knifetool_draw_angle(kcd, + kcd->mdata.corr_prev_cage, + kcd->curr.cage, + end, + kcd->prev.mval, + kcd->curr.mval, + end_ss, + angle); + } + } + + if (kcd->prev.vert) { + kfv = kcd->prev.vert; + float min_angle = FLT_MAX; + float angle = 0.0f; + float *end; + + /* If using relative angle snapping, always draw angle to reference edge. */ + if (kcd->is_angle_snapping && kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) { + kfe = kcd->snap_ref_edge; + if (kfe->v1 != kfv) { + tempkfv = kfe->v1; + } + else { + tempkfv = kfe->v2; + } + min_angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, tempkfv->cageco); + end = tempkfv->cageco; + } + else { + /* Choose minimum angle edge. */ + kfe = ((Ref *)kfv->edges.first)->ref; + for (ref = kfv->edges.first; ref; ref = ref->next) { + tempkfe = ref->ref; + if (tempkfe->v1 != kfv) { + tempkfv = tempkfe->v1; + } + else { + tempkfv = tempkfe->v2; + } + angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, tempkfv->cageco); + if (angle < min_angle) { + min_angle = angle; + kfe = tempkfe; + end = tempkfv->cageco; + } + } + } + + if (min_angle > KNIFE_FLT_EPSBIG) { + /* Last vertex in screen space. */ + float end_ss[2]; + ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); + + knifetool_draw_angle(kcd, + kcd->curr.cage, + kcd->prev.cage, + end, + kcd->curr.mval, + kcd->prev.mval, + end_ss, + min_angle); + } + } + else if (kcd->prev.edge) { + /* Determine acute angle. */ + kfe = kcd->prev.edge; + float angle1 = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kfe->v1->cageco); + float angle2 = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kfe->v2->cageco); + + float angle; + float *end; + /* kcd->prev.edge can have one vertex part of cut and one part of mesh? */ + /* This never seems to happen for kcd->curr.edge. */ + if ((!kcd->prev.vert || kcd->prev.vert->v == kfe->v1->v) || kfe->v1->is_cut) { + angle = angle2; + end = kfe->v2->cageco; + } + else if ((!kcd->prev.vert || kcd->prev.vert->v == kfe->v2->v) || kfe->v2->is_cut) { + angle = angle1; + end = kfe->v1->cageco; + } + else { + if (angle1 < angle2) { + angle = angle1; + end = kfe->v1->cageco; + } + else { + angle = angle2; + end = kfe->v2->cageco; + } + } + + /* Last vertex in screen space. */ + float end_ss[2]; + ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); + + knifetool_draw_angle( + kcd, kcd->curr.cage, kcd->prev.cage, end, kcd->curr.mval, kcd->prev.mval, end_ss, angle); + } + else if (kcd->mdata.is_stored && !kcd->prev.is_space) { + float angle = angle_v3v3v3(kcd->curr.cage, kcd->mdata.corr_prev_cage, kcd->mdata.cage); + knifetool_draw_angle(kcd, + kcd->curr.cage, + kcd->mdata.corr_prev_cage, + kcd->mdata.cage, + kcd->curr.mval, + kcd->prev.mval, + kcd->mdata.mval, + angle); + } +} + +static void knifetool_draw_dist_angle(const KnifeTool_OpData *kcd) +{ + switch (kcd->dist_angle_mode) { + case KNF_MEASUREMENT_BOTH: { + knifetool_draw_visible_distances(kcd); + knifetool_draw_visible_angles(kcd); + break; + } + case KNF_MEASUREMENT_DISTANCE: { + knifetool_draw_visible_distances(kcd); + break; + } + case KNF_MEASUREMENT_ANGLE: { + knifetool_draw_visible_angles(kcd); + break; + } + } +} + +/* Modal loop selection drawing callback. */ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg) { const KnifeTool_OpData *kcd = arg; @@ -345,13 +896,6 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPU_matrix_push_projection(); GPU_polygon_offset(1.0f, 1.0f); - GPU_matrix_push(); - GPU_matrix_mul(kcd->ob->obmat); - - if (kcd->mode == MODE_DRAGGING && kcd->is_angle_snapping) { - knifetool_draw_angle_snapping(kcd); - } - GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -412,48 +956,8 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v immEnd(); } - if (kcd->totlinehit > 0) { - KnifeLineHit *lh; - int i, snapped_verts_count, other_verts_count; - float fcol[4]; - - GPU_blend(GPU_BLEND_ALPHA); - - GPUVertBuf *vert = GPU_vertbuf_create_with_format(format); - GPU_vertbuf_data_alloc(vert, kcd->totlinehit); - - lh = kcd->linehits; - for (i = 0, snapped_verts_count = 0, other_verts_count = 0; i < kcd->totlinehit; i++, lh++) { - if (lh->v) { - GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, lh->cagehit); - } - else { - GPU_vertbuf_attr_set(vert, pos, kcd->totlinehit - 1 - other_verts_count++, lh->cagehit); - } - } - - GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO); - GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); - - /* draw any snapped verts first */ - rgba_uchar_to_float(fcol, kcd->colors.point_a); - GPU_batch_uniform_4fv(batch, "color", fcol); - GPU_point_size(11 * UI_DPI_FAC); - if (snapped_verts_count > 0) { - GPU_batch_draw_range(batch, 0, snapped_verts_count); - } - - /* now draw the rest */ - rgba_uchar_to_float(fcol, kcd->colors.curpoint_a); - GPU_batch_uniform_4fv(batch, "color", fcol); - GPU_point_size(7 * UI_DPI_FAC); - if (other_verts_count > 0) { - GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count); - } - - GPU_batch_discard(batch); - - GPU_blend(GPU_BLEND_NONE); + if (kcd->depth_test) { + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } if (kcd->totkedge > 0) { @@ -467,7 +971,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v BLI_mempool_iternew(kcd->kedges, &iter); for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) { - if (!kfe->is_cut) { + if (!kfe->is_cut || kfe->is_invalid) { continue; } @@ -492,7 +996,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v BLI_mempool_iternew(kcd->kverts, &iter); for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) { - if (!kfv->is_cut) { + if (!kfv->is_cut || kfv->is_invalid) { continue; } @@ -505,12 +1009,81 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPU_batch_discard(batch); } + /* Draw relative angle snapping reference edge. */ + if (kcd->is_angle_snapping && kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) { + immUniformColor3ubv(kcd->colors.edge_extra); + GPU_line_width(2.0); + + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, kcd->snap_ref_edge->v1->cageco); + immVertex3fv(pos, kcd->snap_ref_edge->v2->cageco); + immEnd(); + } + + if (kcd->totlinehit > 0) { + KnifeLineHit *lh; + int i, snapped_verts_count, other_verts_count; + float fcol[4]; + + GPU_blend(GPU_BLEND_ALPHA); + + GPUVertBuf *vert = GPU_vertbuf_create_with_format(format); + GPU_vertbuf_data_alloc(vert, kcd->totlinehit); + + lh = kcd->linehits; + for (i = 0, snapped_verts_count = 0, other_verts_count = 0; i < kcd->totlinehit; i++, lh++) { + if (lh->v) { + GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, lh->cagehit); + } + else { + GPU_vertbuf_attr_set(vert, pos, kcd->totlinehit - 1 - other_verts_count++, lh->cagehit); + } + } + + GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO); + GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); + + /* Draw any snapped verts first. */ + rgba_uchar_to_float(fcol, kcd->colors.point_a); + GPU_batch_uniform_4fv(batch, "color", fcol); + GPU_point_size(11 * UI_DPI_FAC); + if (snapped_verts_count > 0) { + GPU_batch_draw_range(batch, 0, snapped_verts_count); + } + + /* Now draw the rest. */ + rgba_uchar_to_float(fcol, kcd->colors.curpoint_a); + GPU_batch_uniform_4fv(batch, "color", fcol); + GPU_point_size(7 * UI_DPI_FAC); + if (other_verts_count > 0) { + GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count); + } + + GPU_batch_discard(batch); + + GPU_blend(GPU_BLEND_NONE); + } + immUnbindProgram(); - GPU_matrix_pop(); + GPU_depth_test(GPU_DEPTH_NONE); + + if (kcd->mode == MODE_DRAGGING) { + if (kcd->is_angle_snapping) { + knifetool_draw_angle_snapping(kcd); + } + else if (kcd->axis_constrained) { + knifetool_draw_orientation_locking(kcd); + } + + if (kcd->show_dist_angle) { + knifetool_draw_dist_angle(kcd); + } + } + GPU_matrix_pop_projection(); - /* Reset default */ + /* Reset default. */ GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } @@ -532,27 +1105,50 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k WM_modalkeymap_operator_items_to_string_buf( \ op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p) - BLI_snprintf(header, - sizeof(header), - TIP_("%s: confirm, %s: cancel, " - "%s: start/define cut, %s: close cut, %s: new cut, " - "%s: midpoint snap (%s), %s: ignore snap (%s), " - "%s: angle constraint (%s), %s: cut through (%s), " - "%s: panning"), - WM_MODALKEY(KNF_MODAL_CONFIRM), - WM_MODALKEY(KNF_MODAL_CANCEL), - WM_MODALKEY(KNF_MODAL_ADD_CUT), - WM_MODALKEY(KNF_MODAL_ADD_CUT_CLOSED), - WM_MODALKEY(KNF_MODAL_NEW_CUT), - WM_MODALKEY(KNF_MODAL_MIDPOINT_ON), - WM_bool_as_string(kcd->snap_midpoints), - WM_MODALKEY(KNF_MODAL_IGNORE_SNAP_ON), - WM_bool_as_string(kcd->ignore_edge_snapping), - WM_MODALKEY(KNF_MODAL_ANGLE_SNAP_TOGGLE), - WM_bool_as_string(kcd->angle_snapping), - WM_MODALKEY(KNF_MODAL_CUT_THROUGH_TOGGLE), - WM_bool_as_string(kcd->cut_through), - WM_MODALKEY(KNF_MODAL_PANNING)); + BLI_snprintf( + header, + sizeof(header), + TIP_("%s: confirm, %s: cancel, %s: undo, " + "%s: start/define cut, %s: close cut, %s: new cut, " + "%s: midpoint snap (%s), %s: ignore snap (%s), " + "%s: angle constraint %.2f(%.2f) (%s%s%s%s), %s: cut through (%s), " + "%s: panning, XYZ: orientation lock (%s), " + "%s: distance/angle measurements (%s), " + "%s: x-ray (%s)"), + WM_MODALKEY(KNF_MODAL_CONFIRM), + WM_MODALKEY(KNF_MODAL_CANCEL), + WM_MODALKEY(KNF_MODAL_UNDO), + WM_MODALKEY(KNF_MODAL_ADD_CUT), + WM_MODALKEY(KNF_MODAL_ADD_CUT_CLOSED), + WM_MODALKEY(KNF_MODAL_NEW_CUT), + WM_MODALKEY(KNF_MODAL_MIDPOINT_ON), + WM_bool_as_string(kcd->snap_midpoints), + WM_MODALKEY(KNF_MODAL_IGNORE_SNAP_ON), + WM_bool_as_string(kcd->ignore_edge_snapping), + WM_MODALKEY(KNF_MODAL_ANGLE_SNAP_TOGGLE), + (kcd->angle >= 0.0f) ? RAD2DEGF(kcd->angle) : 360.0f + RAD2DEGF(kcd->angle), + (kcd->angle_snapping_increment > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + kcd->angle_snapping_increment < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) ? + kcd->angle_snapping_increment : + KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT, + kcd->angle_snapping ? + ((kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_SCREEN) ? "Screen" : "Relative") : + "OFF", + /* TODO: Can this be simplified? */ + (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) ? " - " : "", + (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) ? + WM_MODALKEY(KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE) : + "", + (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) ? ": cycle edge" : "", + /**/ + WM_MODALKEY(KNF_MODAL_CUT_THROUGH_TOGGLE), + WM_bool_as_string(kcd->cut_through), + WM_MODALKEY(KNF_MODAL_PANNING), + (kcd->axis_constrained ? kcd->axis_string : WM_bool_as_string(kcd->axis_constrained)), + WM_MODALKEY(KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE), + WM_bool_as_string(kcd->show_dist_angle), + WM_MODALKEY(KNF_MODAL_DEPTH_TEST_TOGGLE), + WM_bool_as_string(!kcd->depth_test)); #undef WM_MODALKEY @@ -561,28 +1157,331 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Knife BVH Utils + * \{ */ + +static bool knife_bm_face_is_select(BMFace *f) +{ + return (BM_elem_flag_test(f, BM_ELEM_SELECT) != 0); +} + +static bool knife_bm_face_is_not_hidden(BMFace *f) +{ + return (BM_elem_flag_test(f, BM_ELEM_HIDDEN) == 0); +} + +static void knife_bvh_init(KnifeTool_OpData *kcd) +{ + Object *ob; + BMEditMesh *em; + + /* Test Function. */ + bool (*test_fn)(BMFace *); + if ((kcd->only_select && kcd->cut_through)) { + test_fn = knife_bm_face_is_select; + } + else { + test_fn = knife_bm_face_is_not_hidden; + } + + /* Construct BVH Tree. */ + float cos[3][3]; + const float epsilon = FLT_EPSILON * 2.0f; + int tottri = 0; + int ob_tottri = 0; + BMLoop *(*looptris)[3]; + BMFace *f_test = NULL, *f_test_prev = NULL; + bool test_fn_ret = false; + + /* Calculate tottri. */ + for (uint b = 0; b < kcd->objects_len; b++) { + ob_tottri = 0; + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + + for (int i = 0; i < em->tottri; i++) { + f_test = em->looptris[i][0]->f; + if (f_test != f_test_prev) { + test_fn_ret = test_fn(f_test); + f_test_prev = f_test; + } + + if (test_fn_ret) { + ob_tottri++; + } + } + + tottri += ob_tottri; + } + + kcd->bvh.tree = BLI_bvhtree_new(tottri, epsilon, 8, 8); + + f_test_prev = NULL; + test_fn_ret = false; + + /* Add tri's for each object. + * TODO: + * test_fn can leave large gaps between bvh tree indices. + * Compacting bvh tree indices may be possible. + * Don't forget to update #knife_bvh_intersect_plane! + */ + tottri = 0; + for (uint b = 0; b < kcd->objects_len; b++) { + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + looptris = em->looptris; + + for (int i = 0; i < em->tottri; i++) { + + f_test = looptris[i][0]->f; + if (f_test != f_test_prev) { + test_fn_ret = test_fn(f_test); + f_test_prev = f_test; + } + + if (!test_fn_ret) { + continue; + } + + copy_v3_v3(cos[0], kcd->cagecos[b][BM_elem_index_get(looptris[i][0]->v)]); + copy_v3_v3(cos[1], kcd->cagecos[b][BM_elem_index_get(looptris[i][1]->v)]); + copy_v3_v3(cos[2], kcd->cagecos[b][BM_elem_index_get(looptris[i][2]->v)]); + + /* Convert to world-space. */ + mul_m4_v3(ob->obmat, cos[0]); + mul_m4_v3(ob->obmat, cos[1]); + mul_m4_v3(ob->obmat, cos[2]); + + BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, (float *)cos, 3); + } + + tottri += em->tottri; + } + + BLI_bvhtree_balance(kcd->bvh.tree); +} + +/* Wrapper for #BLI_bvhtree_free. */ +static void knife_bvh_free(KnifeTool_OpData *kcd) +{ + if (kcd->bvh.tree) { + BLI_bvhtree_free(kcd->bvh.tree); + kcd->bvh.tree = NULL; + } +} + +static void knife_bvh_raycast_cb(void *userdata, + int index, + const BVHTreeRay *ray, + BVHTreeRayHit *hit) +{ + KnifeTool_OpData *kcd = userdata; + BMLoop **ltri; + Object *ob; + BMEditMesh *em; + + float dist, uv[2]; + bool isect; + int tottri; + float tri_cos[3][3]; + + if (index != -1) { + tottri = 0; + uint b = 0; + for (; b < kcd->objects_len; b++) { + index -= tottri; + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + tottri = em->tottri; + if (index < tottri) { + ltri = em->looptris[index]; + break; + } + } + + if (kcd->bvh.filter_cb) { + if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) { + return; + } + } + + copy_v3_v3(tri_cos[0], kcd->cagecos[b][BM_elem_index_get(ltri[0]->v)]); + copy_v3_v3(tri_cos[1], kcd->cagecos[b][BM_elem_index_get(ltri[1]->v)]); + copy_v3_v3(tri_cos[2], kcd->cagecos[b][BM_elem_index_get(ltri[2]->v)]); + mul_m4_v3(ob->obmat, tri_cos[0]); + mul_m4_v3(ob->obmat, tri_cos[1]); + mul_m4_v3(ob->obmat, tri_cos[2]); + + isect = + (ray->radius > 0.0f ? + isect_ray_tri_epsilon_v3(ray->origin, + ray->direction, + tri_cos[0], + tri_cos[1], + tri_cos[2], + &dist, + uv, + ray->radius) : +#ifdef USE_KDOPBVH_WATERTIGHT + isect_ray_tri_watertight_v3( + ray->origin, ray->isect_precalc, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); +#else + isect_ray_tri_v3( + ray->origin, ray->direction, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); +#endif + + if (isect && dist < hit->dist) { + hit->dist = dist; + hit->index = index; + + copy_v3_v3(hit->no, ltri[0]->f->no); + + madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); + + kcd->bvh.looptris = em->looptris; + copy_v2_v2(kcd->bvh.uv, uv); + kcd->bvh.base_index = b; + } + } +} + +/* `co` is expected to be in world space. */ +static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd, + const float co[3], + const float dir[3], + const float radius, + float *r_dist, + float r_hitout[3], + float r_cagehit[3], + uint *r_base_index) +{ + BMFace *face; + BMLoop **ltri; + BVHTreeRayHit hit; + const float dist = r_dist ? *r_dist : FLT_MAX; + hit.dist = dist; + hit.index = -1; + + BLI_bvhtree_ray_cast(kcd->bvh.tree, co, dir, radius, &hit, knife_bvh_raycast_cb, kcd); + + /* Handle Hit */ + if (hit.index != -1 && hit.dist != dist) { + face = kcd->bvh.looptris[hit.index][0]->f; + + /* Hits returned in world space. */ + if (r_hitout) { + ltri = kcd->bvh.looptris[hit.index]; + interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, kcd->bvh.uv); + + if (r_cagehit) { + copy_v3_v3(r_cagehit, hit.co); + } + } + + if (r_dist) { + *r_dist = hit.dist; + } + + if (r_base_index) { + *r_base_index = kcd->bvh.base_index; + } + + return face; + } + return NULL; +} + +/* `co` is expected to be in world space. */ +static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd, + const float co[3], + const float dir[3], + const float radius, + float *r_dist, + float r_hitout[3], + float r_cagehit[3], + uint *r_base_index, + bool (*filter_cb)(BMFace *f, void *userdata), + void *filter_userdata) +{ + kcd->bvh.filter_cb = filter_cb; + kcd->bvh.filter_data = filter_userdata; + + BMFace *face; + BMLoop **ltri; + BVHTreeRayHit hit; + const float dist = r_dist ? *r_dist : FLT_MAX; + hit.dist = dist; + hit.index = -1; + + BLI_bvhtree_ray_cast(kcd->bvh.tree, co, dir, radius, &hit, knife_bvh_raycast_cb, kcd); + + kcd->bvh.filter_cb = NULL; + kcd->bvh.filter_data = NULL; + + /* Handle Hit */ + if (hit.index != -1 && hit.dist != dist) { + face = kcd->bvh.looptris[hit.index][0]->f; + + /* Hits returned in world space. */ + if (r_hitout) { + ltri = kcd->bvh.looptris[hit.index]; + interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, kcd->bvh.uv); + + if (r_cagehit) { + copy_v3_v3(r_cagehit, hit.co); + } + } + + if (r_dist) { + *r_dist = hit.dist; + } + + if (r_base_index) { + *r_base_index = kcd->bvh.base_index; + } + + return face; + } + return NULL; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Geometry Utils * \{ */ static void knife_project_v2(const KnifeTool_OpData *kcd, const float co[3], float sco[2]) { - ED_view3d_project_float_v2_m4(kcd->region, co, sco, (float(*)[4])kcd->projmat); + ED_view3d_project_float_global(kcd->region, co, sco, V3D_PROJ_TEST_NOP); } +/* Ray is returned in world space. */ static void knife_input_ray_segment(KnifeTool_OpData *kcd, const float mval[2], const float ofs, float r_origin[3], float r_origin_ofs[3]) { - /* unproject to find view ray */ + /* Unproject to find view ray. */ ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin); ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); +} - /* transform into object space */ - mul_m4_v3(kcd->ob_imat, r_origin); - mul_m4_v3(kcd->ob_imat, r_origin_ofs); +static void knifetool_recast_cageco(KnifeTool_OpData *kcd, float mval[3], float r_cage[3]) +{ + float origin[3]; + float origin_ofs[3]; + float ray[3], ray_normal[3]; + float co[3]; /* Unused. */ + + knife_input_ray_segment(kcd, mval, 1.0f, origin, origin_ofs); + + sub_v3_v3v3(ray, origin_ofs, origin); + normalize_v3_v3(ray_normal, ray); + + knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, NULL, co, r_cage, NULL); } static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) @@ -599,11 +1498,11 @@ static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) l2 = v2->v ? BM_face_vert_share_loop(f, v2->v) : NULL; if ((l1 && l2) && BM_loop_is_adjacent(l1, l2)) { - /* boundary-case, always false to avoid edge-in-face checks below */ + /* Boundary-case, always false to avoid edge-in-face checks below. */ return false; } - /* find out if v1 and v2, if set, are part of the face */ + /* Find out if v1 and v2, if set, are part of the face. */ v1_inface = (l1 != NULL); v2_inface = (l2 != NULL); @@ -619,22 +1518,15 @@ static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) /* Can have case where v1 and v2 are on shared chain between two faces. * BM_face_splits_check_legal does visibility and self-intersection tests, * but it is expensive and maybe a bit buggy, so use a simple - * "is the midpoint in the face" test */ + * "is the midpoint in the face" test. */ mid_v3_v3v3(mid, v1->co, v2->co); return BM_face_point_inside_test(f, mid); } return false; } -static void knife_recalc_projmat(KnifeTool_OpData *kcd) +static void knife_recalc_ortho(KnifeTool_OpData *kcd) { - ED_view3d_ob_project_mat_get(kcd->region->regiondata, kcd->ob, kcd->projmat); - invert_m4_m4(kcd->projmat_inv, kcd->projmat); - - invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat); - mul_v3_mat3_m4v3(kcd->proj_zaxis, kcd->ob_imat, kcd->vc.rv3d->viewinv[2]); - normalize_v3(kcd->proj_zaxis); - kcd->is_ortho = ED_view3d_clip_range_get( kcd->vc.depsgraph, kcd->vc.v3d, kcd->vc.rv3d, &kcd->clipsta, &kcd->clipend, true); } @@ -762,7 +1654,7 @@ static void knife_add_edge_faces_to_vert(KnifeTool_OpData *kcd, KnifeVert *kfv, } /* Find a face in common in the two faces lists. - * If more than one, return the first; if none, return NULL */ + * If more than one, return the first; if none, return NULL. */ static BMFace *knife_find_common_face(ListBase *faces1, ListBase *faces2) { Ref *ref1, *ref2; @@ -797,12 +1689,13 @@ static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const static KnifeEdge *new_knife_edge(KnifeTool_OpData *kcd) { + KnifeEdge *kfe = BLI_mempool_calloc(kcd->kedges); kcd->totkedge++; - return BLI_mempool_calloc(kcd->kedges); + return kfe; } -/* get a KnifeVert wrapper for an existing BMVert */ -static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v) +/* Get a KnifeVert wrapper for an existing BMVert. */ +static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob, uint base_index) { KnifeVert *kfv = BLI_ghash_lookup(kcd->origvertmap, v); const float *cageco; @@ -812,13 +1705,20 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v) BMFace *f; if (BM_elem_index_get(v) >= 0) { - cageco = kcd->cagecos[BM_elem_index_get(v)]; + cageco = kcd->cagecos[base_index][BM_elem_index_get(v)]; } else { cageco = v->co; } - kfv = new_knife_vert(kcd, v->co, cageco); + + float cageco_ws[3]; + mul_v3_m4v3(cageco_ws, ob->obmat, cageco); + + kfv = new_knife_vert(kcd, v->co, cageco_ws); kfv->v = v; + kfv->ob = ob; + kfv->base_index = base_index; + BLI_ghash_insert(kcd->origvertmap, v, kfv); BM_ITER_ELEM (f, &bmiter, v, BM_FACES_OF_VERT) { knife_append_list(kcd, &kfv->faces, f); @@ -828,8 +1728,8 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v) return kfv; } -/* get a KnifeEdge wrapper for an existing BMEdge */ -static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e) +/* Get a KnifeEdge wrapper for an existing BMEdge. */ +static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e, Object *ob, uint base_index) { KnifeEdge *kfe = BLI_ghash_lookup(kcd->origedgemap, e); if (!kfe) { @@ -838,8 +1738,8 @@ static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e) kfe = new_knife_edge(kcd); kfe->e = e; - kfe->v1 = get_bm_knife_vert(kcd, e->v1); - kfe->v2 = get_bm_knife_vert(kcd, e->v2); + kfe->v1 = get_bm_knife_vert(kcd, e->v1, ob, base_index); + kfe->v2 = get_bm_knife_vert(kcd, e->v2, ob, base_index); knife_add_to_vert_edges(kcd, kfe); @@ -853,7 +1753,10 @@ static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e) return kfe; } -static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f) +static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, + Object *ob, + uint base_index, + BMFace *f) { ListBase *list = BLI_ghash_lookup(kcd->kedgefacemap, f); @@ -864,7 +1767,7 @@ static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f) list = knife_empty_list(kcd); BM_ITER_ELEM (e, &bmiter, f, BM_EDGES_OF_FACE) { - knife_append_list(kcd, list, get_bm_knife_edge(kcd, e)); + knife_append_list(kcd, list, get_bm_knife_edge(kcd, e, ob, base_index)); } BLI_ghash_insert(kcd->kedgefacemap, f, list); @@ -875,7 +1778,7 @@ static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f) static void knife_edge_append_face(KnifeTool_OpData *kcd, KnifeEdge *kfe, BMFace *f) { - knife_append_list(kcd, knife_get_face_kedges(kcd, f), kfe); + knife_append_list(kcd, knife_get_face_kedges(kcd, kfe->v1->ob, kfe->v1->base_index, f), kfe); knife_append_list(kcd, &kfe->faces, f); } @@ -891,6 +1794,8 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, newkfe->v1 = kfe->v1; newkfe->v2 = new_knife_vert(kcd, co, cageco); + newkfe->v2->ob = kfe->v1->ob; + newkfe->v2->base_index = kfe->v1->base_index; newkfe->v2->is_cut = true; if (kfe->e) { knife_add_edge_faces_to_vert(kcd, newkfe->v2, kfe->e); @@ -898,7 +1803,7 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, else { /* kfe cuts across an existing face. * If v1 and v2 are in multiple faces together (e.g., if they - * are in doubled polys) then this arbitrarily chooses one of them */ + * are in doubled polys) then this arbitrarily chooses one of them. */ f = knife_find_common_face(&kfe->v1->faces, &kfe->v2->faces); if (f) { knife_append_list(kcd, &newkfe->v2->faces, f); @@ -910,6 +1815,7 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, BLI_remlink(&kfe->v1->edges, ref); kfe->v1 = newkfe->v2; + kfe->v1->is_splitting = true; BLI_addtail(&kfe->v1->edges, ref); for (ref = kfe->faces.first; ref; ref = ref->next) { @@ -921,11 +1827,32 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, newkfe->is_cut = kfe->is_cut; newkfe->e = kfe->e; + newkfe->splits++; + kfe->splits++; + + kcd->undo->splits++; + + BLI_stack_push(kcd->splitstack, (void *)&kfe); + BLI_stack_push(kcd->splitstack, (void *)&newkfe); + *r_kfe = newkfe; return newkfe->v2; } +/* Rejoin two edges split by #knife_split_edge. */ +static void knife_join_edge(KnifeEdge *newkfe, KnifeEdge *kfe) +{ + newkfe->is_invalid = true; + newkfe->v2->is_invalid = true; + + kfe->v1 = newkfe->v1; + + kfe->splits--; + kfe->v1->is_splitting = false; + kfe->v2->is_splitting = false; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -937,18 +1864,19 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, static void knife_start_cut(KnifeTool_OpData *kcd) { kcd->prev = kcd->curr; - kcd->curr.is_space = 0; /* TODO: why do we do this? */ + kcd->curr.is_space = 0; /* TODO: Why do we do this? */ + kcd->mdata.is_stored = false; if (kcd->prev.vert == NULL && kcd->prev.edge == NULL) { float origin[3], origin_ofs[3]; float ofs_local[3]; negate_v3_v3(ofs_local, kcd->vc.rv3d->ofs); - mul_m4_v3(kcd->ob_imat, ofs_local); knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs); - if (!isect_line_plane_v3(kcd->prev.cage, origin, origin_ofs, ofs_local, kcd->proj_zaxis)) { + if (!isect_line_plane_v3( + kcd->prev.cage, origin, origin_ofs, ofs_local, kcd->vc.rv3d->viewinv[2])) { zero_v3(kcd->prev.cage); } @@ -968,9 +1896,9 @@ static void linehit_to_knifepos(KnifePosData *kpos, KnifeLineHit *lh) copy_v2_v2(kpos->mval, lh->schit); } -/* primary key: lambda along cut - * secondary key: lambda along depth - * tertiary key: pointer comparisons of verts if both snapped to verts +/* Primary key: lambda along cut + * Secondary key: lambda along depth + * Tertiary key: pointer comparisons of verts if both snapped to verts */ static int linehit_compare(const void *vlh1, const void *vlh2) { @@ -1049,19 +1977,19 @@ static void prepare_linehits_for_cut(KnifeTool_OpData *kcd) } if (is_double) { - /* delete-in-place loop: copying from pos j to pos i+1 */ + /* Delete-in-place loop: copying from pos j to pos i+1. */ int i = 0; int j = 1; while (j < n) { KnifeLineHit *lhi = &linehits[i]; KnifeLineHit *lhj = &linehits[j]; if (lhj->l == -1.0f) { - j++; /* skip copying this one */ + j++; /* Skip copying this one. */ } else { - /* copy unless a no-op */ + /* Copy unless a no-op. */ if (lhi->l == -1.0f) { - /* could happen if linehits[0] is being deleted */ + /* Could happen if linehits[0] is being deleted. */ memcpy(&linehits[i], &linehits[j], sizeof(KnifeLineHit)); } else { @@ -1077,7 +2005,7 @@ static void prepare_linehits_for_cut(KnifeTool_OpData *kcd) } } -/* Add hit to list of hits in facehits[f], where facehits is a map, if not already there */ +/* Add hit to list of hits in facehits[f], where facehits is a map, if not already there. */ static void add_hit_to_facehits(KnifeTool_OpData *kcd, GHash *facehits, BMFace *f, @@ -1093,8 +2021,8 @@ static void add_hit_to_facehits(KnifeTool_OpData *kcd, } /** - * special purpose function, if the linehit is connected to a real edge/vert - * return true if \a co is outside the face. + * Special purpose function, if the linehit is connected to a real edge/vert. + * Return true if \a co is outside the face. */ static bool knife_add_single_cut__is_linehit_outside_face(BMFace *f, const KnifeLineHit *lh, @@ -1131,12 +2059,9 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, return; } - /* if the cut is on an edge, just tag that its a cut and return */ + /* If the cut is on an edge. */ if ((lh1->v && lh2->v) && (lh1->v->v && lh2->v && lh2->v->v) && (e_base = BM_edge_exists(lh1->v->v, lh2->v->v))) { - kfe = get_bm_knife_edge(kcd, e_base); - kfe->is_cut = true; - kfe->e = e_base; return; } if (knife_add_single_cut__is_linehit_outside_face(f, lh1, lh2->hit) || @@ -1144,7 +2069,7 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, return; } - /* Check if edge actually lies within face (might not, if this face is concave) */ + /* Check if edge actually lies within face (might not, if this face is concave). */ if ((lh1->v && !lh1->kfe) && (lh2->v && !lh2->kfe)) { if (!knife_verts_edge_in_face(lh1->v, lh2->v, f)) { return; @@ -1165,6 +2090,8 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, else { BLI_assert(lh1->f); kfe->v1 = new_knife_vert(kcd, lh1->hit, lh1->cagehit); + kfe->v1->ob = lh1->ob; + kfe->v1->base_index = lh1->base_index; kfe->v1->is_cut = true; kfe->v1->is_face = true; knife_append_list(kcd, &kfe->v1->faces, lh1->f); @@ -1176,23 +2103,27 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, } else if (lh2->kfe) { kfe->v2 = knife_split_edge(kcd, lh2->kfe, lh2->hit, lh2->cagehit, &kfe2); - lh2->v = kfe->v2; /* future uses of lh2 won't split again */ + lh2->v = kfe->v2; /* Future uses of lh2 won't split again. */ } else { BLI_assert(lh2->f); kfe->v2 = new_knife_vert(kcd, lh2->hit, lh2->cagehit); + kfe->v2->ob = lh2->ob; + kfe->v2->base_index = lh2->base_index; kfe->v2->is_cut = true; kfe->v2->is_face = true; knife_append_list(kcd, &kfe->v2->faces, lh2->f); - lh2->v = kfe->v2; /* record the KnifeVert for this hit */ + lh2->v = kfe->v2; /* Record the KnifeVert for this hit. */ } knife_add_to_vert_edges(kcd, kfe); - /* TODO: check if this is ever needed */ if (kfe->basef && !find_ref(&kfe->faces, kfe->basef)) { knife_edge_append_face(kcd, kfe, kfe->basef); } + + /* Update current undo frame cut count. */ + kcd->undo->cuts++; } /* Given a list of KnifeLineHits for one face, sorted by l @@ -1212,9 +2143,8 @@ static void knife_cut_face(KnifeTool_OpData *kcd, BMFace *f, ListBase *hits) } } -static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfedges) +static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMesh *bm, BMFace *f, ListBase *kfedges) { - BMesh *bm = kcd->em->bm; KnifeEdge *kfe; Ref *ref; int edge_array_len = BLI_listbase_count(kfedges); @@ -1222,7 +2152,7 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe BMEdge **edge_array = BLI_array_alloca(edge_array, edge_array_len); - /* point to knife edges we've created edges in, edge_array aligned */ + /* Point to knife edges we've created edges in, edge_array aligned. */ KnifeEdge **kfe_array = BLI_array_alloca(kfe_array, edge_array_len); BLI_assert(BLI_gset_len(kcd->edgenet.edge_visit) == 0); @@ -1232,6 +2162,10 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe bool is_new_edge = false; kfe = ref->ref; + if (kfe->is_invalid) { + continue; + } + if (kfe->e == NULL) { if (kfe->v1->v && kfe->v2->v) { kfe->e = BM_edge_exists(kfe->v1->v, kfe->v2->v); @@ -1240,7 +2174,7 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe if (kfe->e) { if (BM_edge_in_face(kfe->e, f)) { - /* shouldn't happen, but in this case - just ignore */ + /* Shouldn't happen, but in this case just ignore. */ continue; } } @@ -1292,7 +2226,7 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe } edge_array_len = edge_array_holes_len; - edge_array = edge_array_holes; /* owned by the arena */ + edge_array = edge_array_holes; /* Owned by the arena. */ } #endif @@ -1307,7 +2241,7 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe } } - /* remove dangling edges, not essential - but nice for users */ + /* Remove dangling edges, not essential - but nice for users. */ for (i = 0; i < edge_array_len_orig; i++) { if (kfe_array[i]) { if (BM_edge_is_wire(kfe_array[i]->e)) { @@ -1342,10 +2276,11 @@ static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cu return 0; } -/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges */ -static void knife_make_cuts(KnifeTool_OpData *kcd) +/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges. */ +static void knife_make_cuts(KnifeTool_OpData *kcd, Object *ob) { - BMesh *bm = kcd->em->bm; + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMesh *bm = em->bm; KnifeEdge *kfe; KnifeVert *kfv; BMFace *f; @@ -1361,11 +2296,14 @@ static void knife_make_cuts(KnifeTool_OpData *kcd) BLI_smallhash_init(fhash); BLI_smallhash_init(ehash); - /* put list of cutting edges for a face into fhash, keyed by face */ + /* Put list of cutting edges for a face into fhash, keyed by face. */ BLI_mempool_iternew(kcd->kedges, &iter); for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) { + if (kfe->is_invalid || kfe->v1->ob != ob) { + continue; + } - /* select edges that lie directly on the cut */ + /* Select edges that lie directly on the cut. */ if (kcd->select_result) { if (kfe->e && kfe->is_cut) { BM_edge_select_set(bm, kfe->e, true); @@ -1384,11 +2322,11 @@ static void knife_make_cuts(KnifeTool_OpData *kcd) knife_append_list(kcd, list, kfe); } - /* put list of splitting vertices for an edge into ehash, keyed by edge */ + /* Put list of splitting vertices for an edge into ehash, keyed by edge. */ BLI_mempool_iternew(kcd->kverts, &iter); for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) { - if (kfv->v) { - continue; /* already have a BMVert */ + if (kfv->v || kfv->is_invalid || kfv->ob != ob) { + continue; /* Already have a BMVert. */ } for (ref = kfv->edges.first; ref; ref = ref->next) { kfe = ref->ref; @@ -1401,14 +2339,14 @@ static void knife_make_cuts(KnifeTool_OpData *kcd) list = knife_empty_list(kcd); BLI_smallhash_insert(ehash, (uintptr_t)e, list); } - /* there can be more than one kfe in kfv's list with same e */ + /* There can be more than one kfe in kfv's list with same e. */ if (!find_ref(list, kfv)) { knife_append_list(kcd, list, kfv); } } } - /* split bmesh edges where needed */ + /* Split bmesh edges where needed. */ for (list = BLI_smallhash_iternew(ehash, &hiter, (uintptr_t *)&e); list; list = BLI_smallhash_iternext(&hiter, (uintptr_t *)&e)) { BLI_listbase_sort_r(list, sort_verts_by_dist_cb, e->v1->co); @@ -1421,13 +2359,13 @@ static void knife_make_cuts(KnifeTool_OpData *kcd) } if (kcd->only_select) { - EDBM_flag_disable_all(kcd->em, BM_ELEM_SELECT); + EDBM_flag_disable_all(em, BM_ELEM_SELECT); } - /* do cuts for each face */ + /* Do cuts for each face. */ for (list = BLI_smallhash_iternew(fhash, &hiter, (uintptr_t *)&f); list; list = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) { - knife_make_face_cuts(kcd, f, list); + knife_make_face_cuts(kcd, bm, f, list); } BLI_smallhash_release(fhash); @@ -1448,6 +2386,21 @@ static void knife_add_cut(KnifeTool_OpData *kcd) GHashIterator giter; ListBase *list; + /* Allocate new undo frame on stack, unless cut is being dragged. */ + if (!kcd->is_drag_undo) { + kcd->undo = BLI_stack_push_r(kcd->undostack); + kcd->undo->pos = kcd->prev; + kcd->undo->cuts = 0; + kcd->undo->splits = 0; + kcd->undo->mdata = kcd->mdata; + kcd->is_drag_undo = true; + } + + /* Save values for angle drawing calculations. */ + copy_v3_v3(kcd->mdata.cage, kcd->mdata.corr_prev_cage); + copy_v2_v2(kcd->mdata.mval, kcd->prev.mval); + kcd->mdata.is_stored = true; + prepare_linehits_for_cut(kcd); if (kcd->totlinehit == 0) { if (kcd->is_drag_hold == false) { @@ -1456,7 +2409,12 @@ static void knife_add_cut(KnifeTool_OpData *kcd) return; } - /* make facehits: map face -> list of linehits touching it */ + /* Consider most recent linehit in angle drawing calculations. */ + if (kcd->totlinehit >= 2) { + copy_v3_v3(kcd->mdata.cage, kcd->linehits[kcd->totlinehit - 2].cagehit); + } + + /* Make facehits: map face -> list of linehits touching it. */ facehits = BLI_ghash_ptr_new("knife facehits"); for (i = 0; i < kcd->totlinehit; i++) { KnifeLineHit *lh = &kcd->linehits[i]; @@ -1485,11 +2443,11 @@ static void knife_add_cut(KnifeTool_OpData *kcd) knife_cut_face(kcd, f, list); } - /* set up for next cut */ + /* Set up for next cut. */ kcd->prev = kcd->curr; if (kcd->prev.bmface) { - /* was "in face" but now we have a KnifeVert it is snapped to */ + /* Was "in face" but now we have a KnifeVert it is snapped to. */ KnifeLineHit *lh = &kcd->linehits[kcd->totlinehit - 1]; kcd->prev.vert = lh->v; kcd->prev.bmface = NULL; @@ -1529,7 +2487,7 @@ static void knife_finish_cut(KnifeTool_OpData *kcd) * to hash lookup routine; will reverse this in the get routine. * Doing this lazily rather than all at once for all faces. */ -static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f, int index) +static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMEditMesh *em, BMFace *f, int index) { int i; @@ -1537,10 +2495,10 @@ static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f, int index) return; } - BLI_assert(index >= 0 && index < kcd->em->tottri); - BLI_assert(kcd->em->looptris[index][0]->f == f); + BLI_assert(index >= 0 && index < em->tottri); + BLI_assert(em->looptris[index][0]->f == f); for (i = index - 1; i >= 0; i--) { - if (kcd->em->looptris[i][0]->f != f) { + if (em->looptris[i][0]->f != f) { i++; break; } @@ -1552,7 +2510,8 @@ static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f, int index) BLI_ghash_insert(kcd->facetrimap, f, POINTER_FROM_INT(i + 1)); } -/* This should only be called for faces that have had a lowest face tri set by previous function */ +/* This should only be called for faces that have had a lowest face tri set by previous function. + */ static int get_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f) { int ans; @@ -1574,11 +2533,15 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, const float s[2], const float v1[3], const float v2[3], + Object *ob, + uint base_index, BMFace *f, const float face_tol_sq, float hit_co[3], float hit_cageco[3]) { + BMEditMesh *em = BKE_editmesh_from_object(ob); + int tottri, tri_i; float raydir[3]; float tri_norm[3], tri_plane[4]; @@ -1592,37 +2555,45 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, sub_v3_v3v3(raydir, v2, v1); normalize_v3(raydir); tri_i = get_lowest_face_tri(kcd, f); - tottri = kcd->em->tottri; + tottri = em->tottri; BLI_assert(tri_i >= 0 && tri_i < tottri); for (; tri_i < tottri; tri_i++) { - const float *lv1, *lv2, *lv3; + float lv[3][3]; float ray_tri_uv[2]; - tri = kcd->em->looptris[tri_i]; + tri = em->looptris[tri_i]; if (tri[0]->f != f) { break; } - lv1 = kcd->cagecos[BM_elem_index_get(tri[0]->v)]; - lv2 = kcd->cagecos[BM_elem_index_get(tri[1]->v)]; - lv3 = kcd->cagecos[BM_elem_index_get(tri[2]->v)]; - /* using epsilon test in case ray is directly through an internal + copy_v3_v3(lv[0], kcd->cagecos[base_index][BM_elem_index_get(tri[0]->v)]); + copy_v3_v3(lv[1], kcd->cagecos[base_index][BM_elem_index_get(tri[1]->v)]); + copy_v3_v3(lv[2], kcd->cagecos[base_index][BM_elem_index_get(tri[2]->v)]); + mul_m4_v3(ob->obmat, lv[0]); + mul_m4_v3(ob->obmat, lv[1]); + mul_m4_v3(ob->obmat, lv[2]); + + /* Using epsilon test in case ray is directly through an internal * tessellation edge and might not hit either tessellation tri with * an exact test; - * we will exclude hits near real edges by a later test */ - if (isect_ray_tri_epsilon_v3(v1, raydir, lv1, lv2, lv3, &lambda, ray_tri_uv, KNIFE_FLT_EPS)) { - /* check if line coplanar with tri */ - normal_tri_v3(tri_norm, lv1, lv2, lv3); - plane_from_point_normal_v3(tri_plane, lv1, tri_norm); + * We will exclude hits near real edges by a later test. */ + if (isect_ray_tri_epsilon_v3( + v1, raydir, lv[0], lv[1], lv[2], &lambda, ray_tri_uv, KNIFE_FLT_EPS)) { + /* Check if line coplanar with tri. */ + normal_tri_v3(tri_norm, lv[0], lv[1], lv[2]); + plane_from_point_normal_v3(tri_plane, lv[0], tri_norm); if ((dist_squared_to_plane_v3(v1, tri_plane) < KNIFE_FLT_EPS) && (dist_squared_to_plane_v3(v2, tri_plane) < KNIFE_FLT_EPS)) { return false; } - interp_v3_v3v3v3_uv(hit_cageco, lv1, lv2, lv3, ray_tri_uv); - /* Now check that far enough away from verts and edges */ - list = knife_get_face_kedges(kcd, f); + interp_v3_v3v3v3_uv(hit_cageco, lv[0], lv[1], lv[2], ray_tri_uv); + /* Now check that far enough away from verts and edges. */ + list = knife_get_face_kedges(kcd, ob, base_index, f); for (ref = list->first; ref; ref = ref->next) { kfe = ref->ref; + if (kfe->is_invalid) { + continue; + } knife_project_v2(kcd, kfe->v1->cageco, se1); knife_project_v2(kcd, kfe->v2->cageco, se2); d = dist_squared_to_line_segment_v2(s, se1, se2); @@ -1642,19 +2613,24 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, */ static void calc_ortho_extent(KnifeTool_OpData *kcd) { + Object *ob; + BMEditMesh *em; BMIter iter; BMVert *v; - BMesh *bm = kcd->em->bm; float min[3], max[3]; - INIT_MINMAX(min, max); - if (kcd->cagecos) { - minmax_v3v3_v3_array(min, max, kcd->cagecos, bm->totvert); - } - else { - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - minmax_v3v3_v3(min, max, v->co); + for (uint b = 0; b < kcd->objects_len; b++) { + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + + if (kcd->cagecos[b]) { + minmax_v3v3_v3_array(min, max, kcd->cagecos[b], em->bm->totvert); + } + else { + BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + minmax_v3v3_v3(min, max, v->co); + } } } @@ -1732,28 +2708,26 @@ static bool point_is_visible(KnifeTool_OpData *kcd, { BMFace *f_hit; - /* If box clipping on, make sure p is not clipped */ + /* If box clipping on, make sure p is not clipped. */ if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) && - ED_view3d_clipping_test(kcd->vc.rv3d, p, true)) { + ED_view3d_clipping_test(kcd->vc.rv3d, p, false)) { return false; } - /* If not cutting through, make sure no face is in front of p */ + /* If not cutting through, make sure no face is in front of p. */ if (!kcd->cut_through) { float dist; float view[3], p_ofs[3]; - /* TODO: I think there's a simpler way to get the required raycast ray */ + /* TODO: I think there's a simpler way to get the required raycast ray. */ ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view); - mul_m4_v3(kcd->ob_imat, view); - - /* make p_ofs a little towards view, so ray doesn't hit p's face. */ + /* Make p_ofs a little towards view, so ray doesn't hit p's face. */ sub_v3_v3(view, p); dist = normalize_v3(view); copy_v3_v3(p_ofs, p); - /* avoid projecting behind the viewpoint */ + /* Avoid projecting behind the viewpoint. */ if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) { dist = kcd->vc.v3d->clip_end * 2.0f; } @@ -1774,20 +2748,21 @@ static bool point_is_visible(KnifeTool_OpData *kcd, } } - /* see if there's a face hit between p1 and the view */ + /* See if there's a face hit between p1 and the view. */ if (ele_test) { - f_hit = BKE_bmbvh_ray_cast_filter(kcd->bmbvh, - p_ofs, - view, - KNIFE_FLT_EPS, - &dist, - NULL, - NULL, - bm_ray_cast_cb_elem_not_in_face_check, - ele_test); + f_hit = knife_bvh_raycast_filter(kcd, + p_ofs, + view, + KNIFE_FLT_EPS, + &dist, + NULL, + NULL, + NULL, + bm_ray_cast_cb_elem_not_in_face_check, + ele_test); } else { - f_hit = BKE_bmbvh_ray_cast(kcd->bmbvh, p_ofs, view, KNIFE_FLT_EPS, &dist, NULL, NULL); + f_hit = knife_bvh_raycast(kcd, p_ofs, view, KNIFE_FLT_EPS, &dist, NULL, NULL, NULL); } if (f_hit) { @@ -1799,7 +2774,7 @@ static bool point_is_visible(KnifeTool_OpData *kcd, } /* Clip the line (v1, v2) to planes perpendicular to it and distances d from - * the closest point on the line to the origin */ + * the closest point on the line to the origin. */ static void clip_to_ortho_planes(float v1[3], float v2[3], const float center[3], const float d) { float closest[3], dir[3]; @@ -1821,12 +2796,12 @@ static void set_linehit_depth(KnifeTool_OpData *kcd, KnifeLineHit *lh) lh->m = dot_m4_v3_row_z(kcd->vc.rv3d->persmatob, lh->cagehit); } -/* Finds visible (or all, if cutting through) edges that intersects the current screen drag line */ +/* Finds visible (or all, if cutting through) edges that intersects the current screen drag line. + */ static void knife_find_line_hits(KnifeTool_OpData *kcd) { - SmallHash faces, kfes, kfvs; + SmallHash faces, kfes, kfvs, fobs; float v1[3], v2[3], v3[3], v4[3], s1[2], s2[2]; - BVHTree *tree; int *results, *result; BMLoop **ls; BMFace *f; @@ -1858,7 +2833,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) copy_v3_v3(v1, kcd->prev.cage); copy_v3_v3(v2, kcd->curr.cage); - /* project screen line's 3d coordinates back into 2d */ + /* Project screen line's 3d coordinates back into 2d. */ knife_project_v2(kcd, v1, s1); knife_project_v2(kcd, v2, s2); @@ -1873,22 +2848,17 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } } - /* unproject screen line */ + /* Unproject screen line. */ ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s1, v1, v3, true); ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s2, v2, v4, true); - mul_m4_v3(kcd->ob_imat, v1); - mul_m4_v3(kcd->ob_imat, v2); - mul_m4_v3(kcd->ob_imat, v3); - mul_m4_v3(kcd->ob_imat, v4); - /* Numeric error, 'v1' -> 'v2', 'v2' -> 'v4' * can end up being ~2000 units apart with an orthogonal perspective. * * (from ED_view3d_win_to_segment_clipped() above) - * this gives precision error; rather than solving properly + * This gives precision error; rather than solving properly * (which may involve using doubles everywhere!), - * limit the distance between these points */ + * limit the distance between these points. */ if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) { if (kcd->ortho_extent == 0.0f) { calc_ortho_extent(kcd); @@ -1910,8 +2880,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) * intersect the cut plane with rays v1-v3 and v2-v4. * This de-duplicates the candidates before doing more expensive intersection tests. */ - tree = BKE_bmbvh_tree_get(kcd->bmbvh); - results = BLI_bvhtree_intersect_plane(tree, plane, &tot); + results = BLI_bvhtree_intersect_plane(kcd->bvh.tree, plane, &tot); if (!results) { return; } @@ -1919,26 +2888,44 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) BLI_smallhash_init(&faces); BLI_smallhash_init(&kfes); BLI_smallhash_init(&kfvs); + BLI_smallhash_init(&fobs); + + Object *ob; + BMEditMesh *em; + uint b = 0; for (i = 0, result = results; i < tot; i++, result++) { - ls = (BMLoop **)kcd->em->looptris[*result]; - f = ls[0]->f; - set_lowest_face_tri(kcd, f, *result); + for (b = 0; b < kcd->objects_len; b++) { + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + if (*result >= 0 && *result < em->tottri) { + ls = (BMLoop **)em->looptris[*result]; + break; + } + *result -= em->tottri; + } - /* occlude but never cut unselected faces (when only_select is used) */ + f = ls[0]->f; + set_lowest_face_tri(kcd, em, f, *result); + + /* Occlude but never cut unselected faces (when only_select is used). */ if (kcd->only_select && !BM_elem_flag_test(f, BM_ELEM_SELECT)) { continue; } - /* for faces, store index of lowest hit looptri in hash */ + /* For faces, store index of lowest hit looptri in hash. */ if (BLI_smallhash_haskey(&faces, (uintptr_t)f)) { continue; } - /* don't care what the value is except that it is non-NULL, for iterator */ + /* Don't care what the value is except that it is non-NULL, for iterator. */ BLI_smallhash_insert(&faces, (uintptr_t)f, f); + BLI_smallhash_insert(&fobs, (uintptr_t)f, (void *)(uintptr_t)b); - list = knife_get_face_kedges(kcd, f); + list = knife_get_face_kedges(kcd, ob, b, f); for (ref = list->first; ref; ref = ref->next) { kfe = ref->ref; + if (kfe->is_invalid) { + continue; + } if (BLI_smallhash_haskey(&kfes, (uintptr_t)kfe)) { continue; } @@ -1950,7 +2937,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } } - /* Now go through the candidates and find intersections */ + /* Now go through the candidates and find intersections. */ /* These tolerances, in screen space, are for intermediate hits, * as ends are already snapped to screen. */ @@ -1971,9 +2958,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) line_tol_sq = line_tol * line_tol; face_tol_sq = face_tol * face_tol; - /* Assume these tolerances swamp floating point rounding errors in calculations below */ + /* Assume these tolerances swamp floating point rounding errors in calculations below. */ - /* first look for vertex hits */ + /* First look for vertex hits. */ for (val_p = BLI_smallhash_iternew_p(&kfvs, &hiter, (uintptr_t *)&v); val_p; val_p = BLI_smallhash_iternext_p(&hiter, (uintptr_t *)&v)) { KnifeEdge *kfe_hit = NULL; @@ -2001,12 +2988,14 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) hit.v = v; /* If this isn't from an existing BMVert, it may have been added to a BMEdge originally. - * knowing if the hit comes from an edge is important for edge-in-face checks later on - * see: #knife_add_single_cut -> #knife_verts_edge_in_face, T42611 */ + * Knowing if the hit comes from an edge is important for edge-in-face checks later on. + * See: #knife_add_single_cut -> #knife_verts_edge_in_face, T42611. */ if (kfe_hit) { hit.kfe = kfe_hit; } + hit.ob = v->ob; + hit.base_index = v->base_index; copy_v3_v3(hit.hit, v->co); copy_v3_v3(hit.cagehit, v->cageco); copy_v2_v2(hit.schit, s); @@ -2021,16 +3010,10 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } } - /* now edge hits; don't add if a vertex at end of edge should have hit */ + /* Now edge hits; don't add if a vertex at end of edge should have hit. */ for (val = BLI_smallhash_iternew(&kfes, &hiter, (uintptr_t *)&kfe); val; val = BLI_smallhash_iternext(&hiter, (uintptr_t *)&kfe)) { - /* If we intersect any of the vertices, don't attempt to intersect the edge. */ - if (BLI_smallhash_lookup(&kfvs, (intptr_t)kfe->v1) || - BLI_smallhash_lookup(&kfvs, (intptr_t)kfe->v2)) { - continue; - } - knife_project_v2(kcd, kfe->v1->cageco, se1); knife_project_v2(kcd, kfe->v2->cageco, se2); int isect_kind = 1; @@ -2045,7 +3028,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) else { isect_kind = isect_seg_seg_v2_point_ex(s1, s2, se1, se2, 0.0f, sint); if (isect_kind == -1) { - /* isect_seg_seg_v2_point doesn't do tolerance test around ends of s1-s2 */ + /* isect_seg_seg_v2_point doesn't do tolerance test around ends of s1-s2. */ closest_to_line_segment_v2(sint, s1, se1, se2); if (len_squared_v2v2(sint, s1) <= line_tol_sq) { isect_kind = 1; @@ -2066,14 +3049,14 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) lambda = d1 / d2; /* Can't just interpolate between ends of kfe because * that doesn't work with perspective transformation. - * Need to find 3d intersection of ray through sint */ + * Need to find 3d intersection of ray through sint. */ knife_input_ray_segment(kcd, sint, 1.0f, r1, r2); isect_kind = isect_line_line_v3( kfe->v1->cageco, kfe->v2->cageco, r1, r2, p_cage, p_cage_tmp); if (isect_kind >= 1 && point_is_visible(kcd, p_cage, sint, bm_elem_from_knife_edge(kfe))) { memset(&hit, 0, sizeof(hit)); if (kcd->snap_midpoints) { - /* choose intermediate point snap too */ + /* Choose intermediate point snap too. */ mid_v3_v3v3(p_cage, kfe->v1->cageco, kfe->v2->cageco); mid_v2_v2v2(sint, se1, se2); lambda = 0.5f; @@ -2081,6 +3064,8 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) hit.kfe = kfe; transform_point_by_seg_v3( hit.hit, p_cage, kfe->v1->co, kfe->v2->co, kfe->v1->cageco, kfe->v2->cageco); + hit.ob = kfe->v1->ob; + hit.base_index = kfe->v1->base_index; copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, sint); hit.perc = lambda; @@ -2091,7 +3076,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } } - /* now face hits; don't add if a vertex or edge in face should have hit */ + /* Now face hits; don't add if a vertex or edge in face should have hit. */ const bool use_hit_prev = (kcd->prev.vert == NULL) && (kcd->prev.edge == NULL); const bool use_hit_curr = (kcd->curr.vert == NULL) && (kcd->curr.edge == NULL) && !kcd->is_drag_hold; @@ -2100,10 +3085,16 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) val = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) { float p[3], p_cage[3]; - if (use_hit_prev && knife_ray_intersect_face(kcd, s1, v1, v3, f, face_tol_sq, p, p_cage)) { + uint base_index = (uint)(uintptr_t)BLI_smallhash_lookup(&fobs, (uintptr_t)f); + ob = kcd->objects[base_index]; + + if (use_hit_prev && + knife_ray_intersect_face(kcd, s1, v1, v3, ob, base_index, f, face_tol_sq, p, p_cage)) { if (point_is_visible(kcd, p_cage, s1, (BMElem *)f)) { memset(&hit, 0, sizeof(hit)); hit.f = f; + hit.ob = ob; + hit.base_index = base_index; copy_v3_v3(hit.hit, p); copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, s1); @@ -2112,10 +3103,13 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } } - if (use_hit_curr && knife_ray_intersect_face(kcd, s2, v2, v4, f, face_tol_sq, p, p_cage)) { + if (use_hit_curr && + knife_ray_intersect_face(kcd, s2, v2, v4, ob, base_index, f, face_tol_sq, p, p_cage)) { if (point_is_visible(kcd, p_cage, s2, (BMElem *)f)) { memset(&hit, 0, sizeof(hit)); hit.f = f; + hit.ob = ob; + hit.base_index = base_index; copy_v3_v3(hit.hit, p); copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, s2); @@ -2129,7 +3123,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) kcd->linehits = linehits; kcd->totlinehit = BLI_array_len(linehits); - /* find position along screen line, used for sorting */ + /* Find position along screen line, used for sorting. */ for (i = 0; i < kcd->totlinehit; i++) { KnifeLineHit *lh = kcd->linehits + i; @@ -2139,6 +3133,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) BLI_smallhash_release(&faces); BLI_smallhash_release(&kfes); BLI_smallhash_release(&kfvs); + BLI_smallhash_release(&fobs); MEM_freeN(results); } @@ -2165,9 +3160,11 @@ static void knife_pos_data_clear(KnifePosData *kpd) * \{ */ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, - float co[3], - float cageco[3], - bool *is_space) + Object **r_ob, + uint *r_base_index, + bool *is_space, + float r_co[3], + float r_cageco[3]) { BMFace *f; float dist = KMAXDIST; @@ -2175,12 +3172,12 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, float origin_ofs[3]; float ray[3], ray_normal[3]; - /* unproject to find view ray */ + /* Unproject to find view ray. */ knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs); sub_v3_v3v3(ray, origin_ofs, origin); normalize_v3_v3(ray_normal, ray); - f = BKE_bmbvh_ray_cast(kcd->bmbvh, origin, ray_normal, 0.0f, NULL, co, cageco); + f = knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, NULL, r_co, r_cageco, r_base_index); if (f && kcd->only_select && BM_elem_flag_test(f, BM_ELEM_SELECT) == 0) { f = NULL; @@ -2190,7 +3187,10 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, *is_space = !f; } - if (!f) { + if (f) { + *r_ob = kcd->objects[*r_base_index]; + } + else { if (kcd->is_interactive) { /* Try to use back-buffer selection method if ray casting failed. * @@ -2202,12 +3202,12 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, f = EDBM_face_find_nearest(&vc, &dist); - /* cheat for now; just put in the origin instead + /* Cheat for now; just put in the origin instead * of a true coordinate on the face. * This just puts a point 1.0f in front of the view. */ - add_v3_v3v3(co, origin, ray); + add_v3_v3v3(r_co, origin, ray); /* Use this value for the cage location too as it's used to find near edges/vertices. */ - copy_v3_v3(cageco, co); + copy_v3_v3(r_cageco, r_co); } } @@ -2222,6 +3222,8 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, */ static int knife_sample_screen_density_from_closest_face(KnifeTool_OpData *kcd, const float radius, + Object *ob, + uint base_index, BMFace *f, const float cageco[3]) { @@ -2234,21 +3236,29 @@ static int knife_sample_screen_density_from_closest_face(KnifeTool_OpData *kcd, knife_project_v2(kcd, cageco, sco); - list = knife_get_face_kedges(kcd, f); + list = knife_get_face_kedges(kcd, ob, base_index, f); for (ref = list->first; ref; ref = ref->next) { KnifeEdge *kfe = ref->ref; int i; + if (kfe->is_invalid) { + continue; + } + for (i = 0; i < 2; i++) { KnifeVert *kfv = i ? kfe->v2 : kfe->v1; float kfv_sco[2]; + if (kfv->is_invalid) { + continue; + } + knife_project_v2(kcd, kfv->cageco, kfv_sco); dis_sq = len_squared_v2v2(kfv_sco, sco); if (dis_sq < radius_sq) { if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) { - if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) { + if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false) == 0) { c++; } } @@ -2275,27 +3285,27 @@ static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize) if (!kcd->curr.is_space) { density = (float)knife_sample_screen_density_from_closest_face( - kcd, maxsize * 2.0f, kcd->curr.bmface, kcd->curr.cage); + kcd, maxsize * 2.0f, kcd->curr.ob, kcd->curr.base_index, kcd->curr.bmface, kcd->curr.cage); } return density ? min_ff(maxsize / ((float)density * 0.5f), maxsize) : maxsize; } -/* Snap to edge in a specified angle. +/* Snap to edge when in a constrained mode. * Returns 'lambda' calculated (in screen-space). */ -static bool knife_snap_edge_in_angle(KnifeTool_OpData *kcd, - const float sco[3], - const float kfv1_sco[2], - const float kfv2_sco[2], - float *r_dist_sq, - float *r_lambda) +static bool knife_snap_edge_constrained(KnifeTool_OpData *kcd, + const float sco[3], + const float kfv1_sco[2], + const float kfv2_sco[2], + float *r_dist_sq, + float *r_lambda) { - /* if snapping, check we're in bounds */ + /* If snapping, check we're in bounds. */ float sco_snap[2]; isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap); float lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco); - /* be strict about angle-snapping within edge */ + /* Be strict when constrained within edge. */ if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) { return false; } @@ -2309,7 +3319,7 @@ static bool knife_snap_edge_in_angle(KnifeTool_OpData *kcd, return false; } -/* use when lambda is in screen-space */ +/* Use when lambda is in screen-space. */ static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd, float r_co[3], const float v1[3], @@ -2320,23 +3330,21 @@ static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd, interp_v3_v3v3(r_co, v1, v2, lambda_ss); } else { - /* transform into screen-space, interp, then transform back */ + /* Transform into screen-space, interp, then transform back. */ float v1_ss[3], v2_ss[3]; - mul_v3_project_m4_v3(v1_ss, (float(*)[4])kcd->projmat, v1); - mul_v3_project_m4_v3(v2_ss, (float(*)[4])kcd->projmat, v2); + mul_v3_project_m4_v3(v1_ss, (float(*)[4])kcd->vc.rv3d->persmat, v1); + mul_v3_project_m4_v3(v2_ss, (float(*)[4])kcd->vc.rv3d->persmat, v2); interp_v3_v3v3(r_co, v1_ss, v2_ss, lambda_ss); - mul_project_m4_v3((float(*)[4])kcd->projmat_inv, r_co); + mul_project_m4_v3((float(*)[4])kcd->vc.rv3d->persinv, r_co); } } -/* p is closest point on edge to the mouse cursor */ -static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, - BMFace *f, - float p[3], - float cagep[3]) +/* p is closest point on edge to the mouse cursor. */ +static KnifeEdge *knife_find_closest_edge_of_face( + KnifeTool_OpData *kcd, Object *ob, uint base_index, BMFace *f, float p[3], float cagep[3]) { float sco[2]; float maxdist; @@ -2361,21 +3369,27 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, knife_project_v2(kcd, cagep, sco); - /* look through all edges associated with this face */ - list = knife_get_face_kedges(kcd, f); + /* Look through all edges associated with this face. */ + list = knife_get_face_kedges(kcd, ob, base_index, f); for (ref = list->first; ref; ref = ref->next) { KnifeEdge *kfe = ref->ref; float kfv1_sco[2], kfv2_sco[2], test_cagep[3]; float lambda; - /* project edge vertices into screen space */ + if (kfe->is_invalid) { + continue; + } + + /* Project edge vertices into screen space. */ knife_project_v2(kcd, kfe->v1->cageco, kfv1_sco); knife_project_v2(kcd, kfe->v2->cageco, kfv2_sco); - /* check if we're close enough and calculate 'lambda' */ - if (kcd->is_angle_snapping) { + /* Check if we're close enough and calculate 'lambda'. */ + /* In constrained mode calculate lambda differently, unless constrained along kcd->prev.edge */ + if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kfe != kcd->prev.edge) && + (kcd->mode == MODE_DRAGGING)) { dis_sq = curdis_sq; - if (!knife_snap_edge_in_angle(kcd, sco, kfv1_sco, kfv2_sco, &dis_sq, &lambda)) { + if (!knife_snap_edge_constrained(kcd, sco, kfv1_sco, kfv2_sco, &dis_sq, &lambda)) { continue; } } @@ -2389,12 +3403,12 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, } } - /* now we have 'lambda' calculated (in screen-space) */ + /* Now we have 'lambda' calculated (in screen-space). */ knife_interp_v3_v3v3(kcd, test_cagep, kfe->v1->cageco, kfe->v2->cageco, lambda); if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) { - /* check we're in the view */ - if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, true)) { + /* Check we're in the view */ + if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, false)) { continue; } } @@ -2417,16 +3431,18 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda); } - /* update mouse coordinates to the snapped-to edge's screen coordinates - * this is important for angle snap, which uses the previous mouse position */ + /* Update mouse coordinates to the snapped-to edge's screen coordinates + * this is important for angle snap, which uses the previous mouse position. */ edgesnap = new_knife_vert(kcd, p, cagep); + edgesnap->ob = ob; + edgesnap->base_index = base_index; knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval); } return cure; } -/* find a vertex near the mouse cursor, if it exists */ +/* Find a vertex near the mouse cursor, if it exists. */ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd, KnifeEdge *kfe, float p[3], @@ -2458,9 +3474,9 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd, knife_project_v2(kcd, kfv->cageco, kfv_sco); - /* be strict about angle snapping, the vertex needs to be very close to the angle, - * or we ignore */ - if (kcd->is_angle_snapping) { + /* Be strict when in a constrained mode, the vertex needs to be very close to the cut line, + * or we ignore. */ + if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kcd->mode == MODE_DRAGGING)) { if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) > KNIFE_FLT_EPSBIG) { continue; @@ -2470,7 +3486,7 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd, dis_sq = len_squared_v2v2(kfv_sco, sco); if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) || - !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true)) { + !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false)) { curv = kfv; curdis_sq = dis_sq; copy_v2_v2(cur_kfv_sco, kfv_sco); @@ -2482,8 +3498,8 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd, copy_v3_v3(p, curv->co); copy_v3_v3(cagep, curv->cageco); - /* update mouse coordinates to the snapped-to vertex's screen coordinates - * this is important for angle snap, which uses the previous mouse position */ + /* Update mouse coordinates to the snapped-to vertex's screen coordinates + * this is important for angle snap, which uses the previous mouse position. */ copy_v2_v2(kcd->curr.mval, cur_kfv_sco); } @@ -2510,12 +3526,21 @@ static float snap_v2_angle(float r[2], const float v[2], const float v_ref[2], f return angle + angle_delta; } -/* update both kcd->curr.mval and kcd->mval to snap to required angle */ -static bool knife_snap_angle(KnifeTool_OpData *kcd) +/* Update both kcd->curr.mval and kcd->mval to snap to required angle. */ +static bool knife_snap_angle_screen(KnifeTool_OpData *kcd) { - const float dvec_ref[2] = {0.0f, 1.0f}; + const float dvec_ref[2] = {1.0f, 0.0f}; float dvec[2], dvec_snap[2]; - float snap_step = DEG2RADF(45); + + float snap_step; + /* Currently user can input any float between 0 and 90. */ + if (kcd->angle_snapping_increment > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + kcd->angle_snapping_increment < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + snap_step = DEG2RADF(kcd->angle_snapping_increment); + } + else { + snap_step = DEG2RADF(KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT); + } sub_v2_v2v2(dvec, kcd->curr.mval, kcd->prev.mval); if (is_zero_v2(dvec)) { @@ -2531,6 +3556,273 @@ static bool knife_snap_angle(KnifeTool_OpData *kcd) return true; } +/** + * Snaps a 3d vector to an angle, relative to \a v_ref, along the plane with normal \a plane_no. + */ +static float snap_v3_angle_plane( + float r[3], const float v[3], const float v_ref[3], const float plane_no[3], float snap_step) +{ + /* Calculate angle between current cut vector and reference vector. */ + float angle, angle_delta; + angle = angle_signed_on_axis_v3v3_v3(v, v_ref, plane_no); + /* Use this to calculate the angle to rotate by based on snap_step. */ + angle_delta = (roundf(angle / snap_step) * snap_step) - angle; + + /* Snap to angle. */ + rotate_v3_v3v3fl(r, v, plane_no, angle_delta); + return angle + angle_delta; +} + +/* Snap to required angle along the plane of the face nearest to kcd->prev. */ +static bool knife_snap_angle_relative(KnifeTool_OpData *kcd) +{ + Ref *ref; + KnifeEdge *kfe; + KnifeVert *kfv; + BMFace *f; + float refv[3]; + + /* Ray for kcd->curr. */ + float curr_origin[3]; + float curr_origin_ofs[3]; + float curr_ray[3], curr_ray_normal[3]; + float curr_co[3], curr_cage[3]; /* Unused. */ + + float plane[4]; + float ray_hit[3]; + float lambda; + + knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, curr_origin, curr_origin_ofs); + sub_v3_v3v3(curr_ray, curr_origin_ofs, curr_origin); + normalize_v3_v3(curr_ray_normal, curr_ray); + + BMFace *fcurr = knife_bvh_raycast( + kcd, curr_origin, curr_ray_normal, 0.0f, NULL, curr_co, curr_cage, NULL); + + if (!fcurr) { + return false; + } + + /* Calculate a reference vector using previous cut segment, edge or vertex. + * If none exists then exit. */ + if (kcd->prev.vert) { + int count = 0; + for (ref = kcd->prev.vert->edges.first; ref; ref = ref->next) { + kfe = ((KnifeEdge *)(ref->ref)); + if (kfe->is_invalid) { + continue; + } + if (kfe->e) { + if (!BM_edge_in_face(kfe->e, fcurr)) { + continue; + } + } + if (count == kcd->snap_edge) { + kfv = compare_v3v3(kfe->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) ? kfe->v2 : kfe->v1; + sub_v3_v3v3(refv, kfv->cageco, kcd->prev.cage); + kcd->snap_ref_edge = kfe; + break; + } + count++; + } + } + else if (kcd->prev.edge) { + kfv = compare_v3v3(kcd->prev.edge->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) ? + kcd->prev.edge->v2 : + kcd->prev.edge->v1; + sub_v3_v3v3(refv, kfv->cageco, kcd->prev.cage); + kcd->snap_ref_edge = kcd->prev.edge; + } + else { + return false; + } + + /* Choose best face for plane. */ + BMFace *fprev = NULL; + if (kcd->prev.vert && kcd->prev.vert->v) { + for (ref = kcd->prev.vert->faces.first; ref; ref = ref->next) { + f = ((BMFace *)(ref->ref)); + if (f == fcurr) { + fprev = f; + } + } + } + else if (kcd->prev.edge) { + for (ref = kcd->prev.edge->faces.first; ref; ref = ref->next) { + f = ((BMFace *)(ref->ref)); + if (f == fcurr) { + fprev = f; + } + } + } + else { + /* Cut segment was started in a face. */ + float prev_origin[3]; + float prev_origin_ofs[3]; + float prev_ray[3], prev_ray_normal[3]; + float prev_co[3], prev_cage[3]; /* Unused. */ + + knife_input_ray_segment(kcd, kcd->prev.mval, 1.0f, prev_origin, prev_origin_ofs); + + sub_v3_v3v3(prev_ray, prev_origin_ofs, prev_origin); + normalize_v3_v3(prev_ray_normal, prev_ray); + + /* kcd->prev.face is usually not set. */ + fprev = knife_bvh_raycast( + kcd, prev_origin, prev_ray_normal, 0.0f, NULL, prev_co, prev_cage, NULL); + } + + if (!fprev || fprev != fcurr) { + return false; + } + + /* Re-calculate current ray in object space. */ + knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, curr_origin, curr_origin_ofs); + sub_v3_v3v3(curr_ray, curr_origin_ofs, curr_origin); + normalize_v3_v3(curr_ray_normal, curr_ray); + + plane_from_point_normal_v3(plane, kcd->prev.cage, fprev->no); + + if (isect_ray_plane_v3(curr_origin, curr_ray_normal, plane, &lambda, false)) { + madd_v3_v3v3fl(ray_hit, curr_origin, curr_ray_normal, lambda); + + /* Calculate snap step. */ + float snap_step; + if (kcd->angle_snapping_increment > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + kcd->angle_snapping_increment < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + snap_step = DEG2RADF(kcd->angle_snapping_increment); + } + else { + snap_step = DEG2RADF(KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT); + } + + float v1[3]; + float v2[3]; + float rotated_vec[3]; + /* Maybe check for vectors being zero here? */ + sub_v3_v3v3(v1, ray_hit, kcd->prev.cage); + copy_v3_v3(v2, refv); + kcd->angle = snap_v3_angle_plane(rotated_vec, v1, v2, fprev->no, snap_step); + add_v3_v3(rotated_vec, kcd->prev.cage); + + knife_project_v2(kcd, rotated_vec, kcd->curr.mval); + copy_v2_v2(kcd->mval, kcd->curr.mval); + return true; + } + return false; +} + +static int knife_calculate_snap_ref_edges(KnifeTool_OpData *kcd) +{ + Ref *ref; + KnifeEdge *kfe; + + /* Ray for kcd->curr. */ + float curr_origin[3]; + float curr_origin_ofs[3]; + float curr_ray[3], curr_ray_normal[3]; + float curr_co[3], curr_cage[3]; /* Unused. */ + + knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, curr_origin, curr_origin_ofs); + sub_v3_v3v3(curr_ray, curr_origin_ofs, curr_origin); + normalize_v3_v3(curr_ray_normal, curr_ray); + + BMFace *fcurr = knife_bvh_raycast( + kcd, curr_origin, curr_ray_normal, 0.0f, NULL, curr_co, curr_cage, NULL); + + int count = 0; + + if (!fcurr) { + return count; + } + + if (kcd->prev.vert) { + for (ref = kcd->prev.vert->edges.first; ref; ref = ref->next) { + kfe = ((KnifeEdge *)(ref->ref)); + if (kfe->is_invalid) { + continue; + } + if (kfe->e) { + if (!BM_edge_in_face(kfe->e, fcurr)) { + continue; + } + } + count++; + } + } + else if (kcd->prev.edge) { + return 1; + } + return count; +} + +/* Reset the snapping angle num input. */ +static void knife_reset_snap_angle_input(KnifeTool_OpData *kcd) +{ + kcd->num.val[0] = 0; + while (kcd->num.str_cur > 0) { + kcd->num.str[kcd->num.str_cur - 1] = '\0'; + kcd->num.str_cur--; + } +} +/** + * Constrains the current cut to an axis. + * If scene orientation is set to anything other than global it takes priority. + * Otherwise kcd->constrain_axis_mode is used. + */ +static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd) +{ + /* Obtain current mouse position in world space. */ + float curr_cage_adjusted[3]; + ED_view3d_win_to_3d( + kcd->vc.v3d, kcd->region, kcd->prev.cage, kcd->curr.mval, curr_cage_adjusted); + + /* Constrain axes. */ + Scene *scene = kcd->scene; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obedit = (kcd->prev.ob) ? kcd->prev.ob : kcd->vc.obedit; + RegionView3D *rv3d = kcd->region->regiondata; + const short scene_orientation = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); + /* Scene orientation takes priority. */ + const short orientation_type = scene_orientation ? scene_orientation : + kcd->constrain_axis_mode - 1; + const int pivot_point = scene->toolsettings->transform_pivot_point; + float mat[3][3]; + ED_transform_calc_orientation_from_type_ex( + scene, view_layer, kcd->vc.v3d, rv3d, obedit, obedit, orientation_type, pivot_point, mat); + + /* Apply orientation matrix (can be simplified?). */ + float co[3][3]; + copy_v3_v3(co[0], kcd->prev.cage); + copy_v3_v3(co[2], curr_cage_adjusted); + invert_m3(mat); + mul_m3_m3_pre(co, mat); + for (int i = 0; i <= 2; i++) { + if ((kcd->constrain_axis - 1) != i) { + /* kcd->curr_cage_adjusted[i] = prev_cage_adjusted[i]; */ + co[2][i] = co[0][i]; + } + } + invert_m3(mat); + mul_m3_m3_pre(co, mat); + copy_v3_v3(kcd->prev.cage, co[0]); + copy_v3_v3(curr_cage_adjusted, co[2]); + + /* Set mval to closest point on constrained line in screen space. */ + float curr_screenspace[2]; + float prev_screenspace[2]; + knife_project_v2(kcd, curr_cage_adjusted, curr_screenspace); + knife_project_v2(kcd, kcd->prev.cage, prev_screenspace); + float intersection[2]; + if (closest_to_line_v2(intersection, kcd->curr.mval, prev_screenspace, curr_screenspace)) { + copy_v2_v2(kcd->curr.mval, intersection); + } + else { + copy_v2_v2(kcd->curr.mval, curr_screenspace); + } + copy_v2_v2(kcd->mval, kcd->curr.mval); +} + /** * \return true when `kcd->curr.co` & `kcd->curr.cage` are set. * @@ -2539,7 +3831,7 @@ static bool knife_snap_angle(KnifeTool_OpData *kcd) * In this case the selection-buffer is used to select the face, * then the closest `vert` or `edge` is set, and those will enable `is_co_set`. */ -static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2]) +static bool knife_snap_update_from_mval(bContext *C, KnifeTool_OpData *kcd, const float mval[2]) { knife_pos_data_clear(&kcd->curr); copy_v2_v2(kcd->curr.mval, mval); @@ -2547,20 +3839,37 @@ static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[ /* view matrix may have changed, reproject */ knife_project_v2(kcd, kcd->prev.cage, kcd->prev.mval); - if (kcd->angle_snapping && (kcd->mode == MODE_DRAGGING)) { - kcd->is_angle_snapping = knife_snap_angle(kcd); - } - else { - kcd->is_angle_snapping = false; + kcd->is_angle_snapping = false; + if (kcd->mode == MODE_DRAGGING) { + if (kcd->angle_snapping) { + if (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_SCREEN) { + kcd->is_angle_snapping = knife_snap_angle_screen(kcd); + } + else if (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) { + kcd->is_angle_snapping = knife_snap_angle_relative(kcd); + if (kcd->is_angle_snapping) { + kcd->snap_ref_edges_count = knife_calculate_snap_ref_edges(kcd); + } + } + } + + if (kcd->axis_constrained) { + knife_constrain_axis(C, kcd); + } } { - kcd->curr.bmface = knife_find_closest_face( - kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.is_space); + kcd->curr.ob = kcd->vc.obedit; + kcd->curr.bmface = knife_find_closest_face(kcd, + &kcd->curr.ob, + &kcd->curr.base_index, + &kcd->curr.is_space, + kcd->curr.co, + kcd->curr.cage); if (kcd->curr.bmface) { kcd->curr.edge = knife_find_closest_edge_of_face( - kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage); + kcd, kcd->curr.ob, kcd->curr.base_index, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage); } if (kcd->curr.edge) { @@ -2576,86 +3885,182 @@ static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[ return kcd->curr.vert || kcd->curr.edge || (kcd->curr.bmface && !kcd->curr.is_space); } +/** + * TODO: Undo currently assumes that the most recent cut segment added is + * the last valid KnifeEdge in the kcd->kedges mempool. This could break in + * the future so it may be better to store the KnifeEdges for each KnifeUndoFrame + * on a stack. This stack could then be used instead of iterating over the mempool. + */ +static void knifetool_undo(KnifeTool_OpData *kcd) +{ + Ref *ref; + KnifeEdge *kfe, *newkfe; + KnifeEdge *lastkfe = NULL; + KnifeVert *v1, *v2; + KnifeUndoFrame *undo; + BLI_mempool_iter iterkfe; + + if (!BLI_stack_is_empty(kcd->undostack)) { + undo = BLI_stack_peek(kcd->undostack); + + /* Undo edge splitting. */ + for (int i = 0; i < undo->splits; i++) { + BLI_stack_pop(kcd->splitstack, &newkfe); + BLI_stack_pop(kcd->splitstack, &kfe); + knife_join_edge(newkfe, kfe); + } + + for (int i = 0; i < undo->cuts; i++) { + + BLI_mempool_iternew(kcd->kedges, &iterkfe); + for (kfe = BLI_mempool_iterstep(&iterkfe); kfe; kfe = BLI_mempool_iterstep(&iterkfe)) { + if (!kfe->is_cut || kfe->is_invalid || kfe->splits) { + continue; + } + lastkfe = kfe; + } + + if (lastkfe) { + lastkfe->is_invalid = true; + + /* TODO: Are they always guaranteed to be in this order? */ + v1 = lastkfe->v1; + v2 = lastkfe->v2; + + /* Only remove first vertex if it is the start segment of the cut. */ + if (!v1->is_invalid && !v1->is_splitting) { + v1->is_invalid = true; + /* If the first vertex is touching any other cut edges don't remove it. */ + for (ref = v1->edges.first; ref; ref = ref->next) { + kfe = ref->ref; + if (kfe->is_cut && !kfe->is_invalid) { + v1->is_invalid = false; + break; + } + } + } + + /* Only remove second vertex if it is the end segment of the cut. */ + if (!v2->is_invalid && !v2->is_splitting) { + v2->is_invalid = true; + /* If the second vertex is touching any other cut edges don't remove it. */ + for (ref = v2->edges.first; ref; ref = ref->next) { + kfe = ref->ref; + if (kfe->is_cut && !kfe->is_invalid) { + v2->is_invalid = false; + break; + } + } + } + } + } + + if (kcd->mode == MODE_DRAGGING) { + /* Restore kcd->prev. */ + kcd->prev = undo->pos; + } + + /* Restore data for distance and angle measurements. */ + kcd->mdata = undo->mdata; + + BLI_stack_discard(kcd->undostack); + } +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name #KnifeTool_OpData (#op->customdata) Init and Free * \{ */ -static void knifetool_init_bmbvh(KnifeTool_OpData *kcd) +static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_index) { - BM_mesh_elem_index_ensure(kcd->em->bm, BM_VERT); Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id); - Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->ob->id); + Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &ob->id); BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval); - kcd->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc( - kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL); + BM_mesh_elem_index_ensure(em_eval->bm, BM_VERT); - kcd->bmbvh = BKE_bmbvh_new_from_editmesh( - kcd->em, - BMBVH_RETURN_ORIG | - ((kcd->only_select && kcd->cut_through) ? BMBVH_RESPECT_SELECT : BMBVH_RESPECT_HIDDEN), - kcd->cagecos, - false); + kcd->cagecos[base_index] = (const float(*)[3])BKE_editmesh_vert_coords_alloc( + kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL); } -static void knifetool_free_bmbvh(KnifeTool_OpData *kcd) +static void knifetool_free_cagecos(KnifeTool_OpData *kcd, uint base_index) { - if (kcd->bmbvh) { - BKE_bmbvh_free(kcd->bmbvh); - kcd->bmbvh = NULL; - } - - if (kcd->cagecos) { - MEM_freeN((void *)kcd->cagecos); - kcd->cagecos = NULL; + if (kcd->cagecos[base_index]) { + MEM_freeN((void *)kcd->cagecos[base_index]); + kcd->cagecos[base_index] = NULL; } } static void knife_init_colors(KnifeColors *colors) { - /* possible BMESH_TODO: add explicit themes or calculate these by + /* Possible BMESH_TODO: add explicit themes or calculate these by * figuring out contrasting colors with grid / edges / verts - * a la UI_make_axis_color */ + * a la UI_make_axis_color. */ UI_GetThemeColorType3ubv(TH_NURB_VLINE, SPACE_VIEW3D, colors->line); UI_GetThemeColorType3ubv(TH_NURB_ULINE, SPACE_VIEW3D, colors->edge); + UI_GetThemeColorType3ubv(TH_NURB_SEL_ULINE, SPACE_VIEW3D, colors->edge_extra); UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint); UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint_a); colors->curpoint_a[3] = 102; UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point); UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point_a); colors->point_a[3] = 102; + + UI_GetThemeColorType3ubv(TH_AXIS_X, SPACE_VIEW3D, colors->xaxis); + UI_GetThemeColorType3ubv(TH_AXIS_Y, SPACE_VIEW3D, colors->yaxis); + UI_GetThemeColorType3ubv(TH_AXIS_Z, SPACE_VIEW3D, colors->zaxis); + UI_GetThemeColorType3ubv(TH_TRANSFORM, SPACE_VIEW3D, colors->axis_extra); } /* called when modal loop selection gets set up... */ -static void knifetool_init(ViewContext *vc, +static void knifetool_init(bContext *C, + ViewContext *vc, KnifeTool_OpData *kcd, const bool only_select, const bool cut_through, + const bool xray, + const int visible_measurements, + const int angle_snapping, + const float angle_snapping_increment, const bool is_interactive) { kcd->vc = *vc; Scene *scene = vc->scene; - Object *obedit = vc->obedit; - /* assign the drawing handle for drawing preview line... */ + /* Assign the drawing handle for drawing preview line... */ kcd->scene = scene; - kcd->ob = obedit; kcd->region = vc->region; - invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat); + kcd->objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + CTX_data_view_layer(C), CTX_wm_view3d(C), &kcd->objects_len); - kcd->em = BKE_editmesh_from_object(kcd->ob); + Object *ob; + BMEditMesh *em; + kcd->cagecos = MEM_callocN(sizeof(*kcd->cagecos) * kcd->objects_len, "knife cagecos"); + for (uint b = 0; b < kcd->objects_len; b++) { + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + knifetool_init_cagecos(kcd, ob, b); - /* cut all the way through the mesh if use_occlude_geometry button not pushed */ + /* Can't usefully select resulting edges in face mode. */ + kcd->select_result = (em->selectmode != SCE_SELECT_FACE); + } + knife_bvh_init(kcd); + + /* Cut all the way through the mesh if use_occlude_geometry button not pushed. */ kcd->is_interactive = is_interactive; kcd->cut_through = cut_through; kcd->only_select = only_select; - - knifetool_init_bmbvh(kcd); + kcd->depth_test = xray; + kcd->dist_angle_mode = visible_measurements; + kcd->show_dist_angle = (kcd->dist_angle_mode != KNF_MEASUREMENT_NONE); + kcd->angle_snapping_mode = angle_snapping; + kcd->angle_snapping = (kcd->angle_snapping_mode != KNF_CONSTRAIN_ANGLE_MODE_NONE); + kcd->angle_snapping_increment = angle_snapping_increment; kcd->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), "knife"); #ifdef USE_NET_ISLAND_CONNECT @@ -2666,7 +4071,7 @@ static void knifetool_init(ViewContext *vc, kcd->vthresh = KMAXDIST - 1; kcd->ethresh = KMAXDIST; - knife_recalc_projmat(kcd); + knife_recalc_ortho(kcd); ED_region_tag_redraw(kcd->region); @@ -2674,14 +4079,14 @@ static void knifetool_init(ViewContext *vc, kcd->kverts = BLI_mempool_create(sizeof(KnifeVert), 0, 512, BLI_MEMPOOL_ALLOW_ITER); kcd->kedges = BLI_mempool_create(sizeof(KnifeEdge), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + kcd->undostack = BLI_stack_new(sizeof(KnifeUndoFrame), "knife undostack"); + kcd->splitstack = BLI_stack_new(sizeof(KnifeEdge *), "knife splitstack"); + kcd->origedgemap = BLI_ghash_ptr_new("knife origedgemap"); kcd->origvertmap = BLI_ghash_ptr_new("knife origvertmap"); kcd->kedgefacemap = BLI_ghash_ptr_new("knife kedgefacemap"); kcd->facetrimap = BLI_ghash_ptr_new("knife facetrimap"); - /* can't usefully select resulting edges in face mode */ - kcd->select_result = (kcd->em->selectmode != SCE_SELECT_FACE); - knife_pos_data_clear(&kcd->curr); knife_pos_data_clear(&kcd->prev); @@ -2691,6 +4096,16 @@ static void knifetool_init(ViewContext *vc, knife_init_colors(&kcd->colors); } + + kcd->axis_string[0] = ' '; + kcd->axis_string[1] = '\0'; + + /* Initialise num input handling for angle snapping. */ + initNumInput(&kcd->num); + kcd->num.idx_max = 0; + kcd->num.val_flag[0] |= NUM_NO_NEGATIVE; + kcd->num.unit_sys = scene->unit.system; + kcd->num.unit_type[0] = B_UNIT_NONE; } /* called when modal loop selection is done... */ @@ -2703,15 +4118,18 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd) if (kcd->is_interactive) { WM_cursor_modal_restore(kcd->vc.win); - /* deactivate the extra drawing stuff in 3D-View */ + /* Deactivate the extra drawing stuff in 3D-View. */ ED_region_draw_cb_exit(kcd->region->type, kcd->draw_handle); } - /* free the custom data */ + /* Free the custom data. */ BLI_mempool_destroy(kcd->refs); BLI_mempool_destroy(kcd->kverts); BLI_mempool_destroy(kcd->kedges); + BLI_stack_free(kcd->undostack); + BLI_stack_free(kcd->splitstack); + BLI_ghash_free(kcd->origedgemap, NULL, NULL); BLI_ghash_free(kcd->origvertmap, NULL, NULL); BLI_ghash_free(kcd->kedgefacemap, NULL, NULL); @@ -2723,18 +4141,28 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd) #endif BLI_gset_free(kcd->edgenet.edge_visit, NULL); - /* tag for redraw */ + /* Tag for redraw. */ ED_region_tag_redraw(kcd->region); - knifetool_free_bmbvh(kcd); + /* Knife BVH cleanup. */ + for (int i = 0; i < kcd->objects_len; i++) { + knifetool_free_cagecos(kcd, i); + } + MEM_freeN(kcd->cagecos); + knife_bvh_free(kcd); + /* Linehits cleanup. */ if (kcd->linehits) { MEM_freeN(kcd->linehits); } - /* destroy kcd itself */ + /* Free object bases. */ + MEM_freeN(kcd->objects); + + /* Destroy kcd itself. */ MEM_freeN(kcd); } + static void knifetool_exit(wmOperator *op) { KnifeTool_OpData *kcd = op->customdata; @@ -2748,24 +4176,24 @@ static void knifetool_exit(wmOperator *op) /** \name Mouse-Moving Event Updates * \{ */ -/* update active knife edge/vert pointers */ -static int knife_update_active(KnifeTool_OpData *kcd) +/* Update active knife edge/vert pointers. */ +static int knife_update_active(bContext *C, KnifeTool_OpData *kcd) { - /* if no hits are found this would normally default to (0, 0, 0) so instead + /* If no hits are found this would normally default to (0, 0, 0) so instead * get a point at the mouse ray closest to the previous point. * Note that drawing lines in `free-space` isn't properly supported * but there's no guarantee (0, 0, 0) has any geometry either - campbell */ - if (!knife_snap_update_from_mval(kcd, kcd->mval)) { + if (!knife_snap_update_from_mval(C, kcd, kcd->mval)) { float origin[3]; float origin_ofs[3]; knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs); if (!isect_line_plane_v3( - kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->proj_zaxis)) { + kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->vc.rv3d->viewinv[2])) { copy_v3_v3(kcd->curr.cage, kcd->prev.cage); - /* should never fail! */ + /* Should never fail! */ BLI_assert(0); } } @@ -2776,20 +4204,20 @@ static int knife_update_active(KnifeTool_OpData *kcd) return 1; } -static void knifetool_update_mval(KnifeTool_OpData *kcd, const float mval[2]) +static void knifetool_update_mval(bContext *C, KnifeTool_OpData *kcd, const float mval[2]) { - knife_recalc_projmat(kcd); + knife_recalc_ortho(kcd); copy_v2_v2(kcd->mval, mval); - if (knife_update_active(kcd)) { + if (knife_update_active(C, kcd)) { ED_region_tag_redraw(kcd->region); } } -static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2]) +static void knifetool_update_mval_i(bContext *C, KnifeTool_OpData *kcd, const int mval_i[2]) { const float mval[2] = {UNPACK2(mval_i)}; - knifetool_update_mval(kcd, mval); + knifetool_update_mval(C, kcd, mval); } /** \} */ @@ -2798,21 +4226,40 @@ static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2]) /** \name Finalization * \{ */ -/* called on tool confirmation */ +/* Called on tool confirmation. */ static void knifetool_finish_ex(KnifeTool_OpData *kcd) { - knife_make_cuts(kcd); + Object *ob; + BMEditMesh *em; + for (uint b = 0; b < kcd->objects_len; b++) { + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); - EDBM_selectmode_flush(kcd->em); - EDBM_update(kcd->ob->data, + knife_make_cuts(kcd, ob); + + EDBM_selectmode_flush(em); + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); + } +} + +static void knifetool_finish_single_ex(KnifeTool_OpData *kcd, Object *ob, uint UNUSED(base_index)) +{ + knife_make_cuts(kcd, ob); + + BMEditMesh *em = BKE_editmesh_from_object(ob); + + EDBM_selectmode_flush(em); + EDBM_update(ob->data, &(const struct EDBMUpdate_Params){ .calc_looptri = true, .calc_normals = true, .is_destructive = true, }); - - /* Re-tessellating makes this invalid, don't use again by accident. */ - knifetool_free_bmbvh(kcd); } static void knifetool_finish(wmOperator *op) @@ -2838,12 +4285,24 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf) static const EnumPropertyItem modal_items[] = { {KNF_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, {KNF_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + {KNF_MODAL_UNDO, "UNDO", 0, "Undo", ""}, {KNF_MODAL_MIDPOINT_ON, "SNAP_MIDPOINTS_ON", 0, "Snap to Midpoints On", ""}, {KNF_MODAL_MIDPOINT_OFF, "SNAP_MIDPOINTS_OFF", 0, "Snap to Midpoints Off", ""}, {KNF_MODAL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""}, {KNF_MODAL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""}, {KNF_MODAL_ANGLE_SNAP_TOGGLE, "ANGLE_SNAP_TOGGLE", 0, "Toggle Angle Snapping", ""}, + {KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE, + "CYCLE_ANGLE_SNAP_EDGE", + 0, + "Cycle Angle Snapping Relative Edge", + ""}, {KNF_MODAL_CUT_THROUGH_TOGGLE, "CUT_THROUGH_TOGGLE", 0, "Toggle Cut Through", ""}, + {KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE, + "SHOW_DISTANCE_ANGLE_TOGGLE", + 0, + "Toggle Distance and Angle Measurements", + ""}, + {KNF_MODAL_DEPTH_TEST_TOGGLE, "DEPTH_TEST_TOGGLE", 0, "Toggle Depth Testing", ""}, {KNF_MODAL_NEW_CUT, "NEW_CUT", 0, "End Current Cut", ""}, {KNF_MODAL_ADD_CUT, "ADD_CUT", 0, "Add Cut", ""}, {KNF_MODAL_ADD_CUT_CLOSED, "ADD_CUT_CLOSED", 0, "Add Cut Closed", ""}, @@ -2853,7 +4312,7 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf) wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Knife Tool Modal Map"); - /* this function is called for each spacetype, only needs to add map once */ + /* This function is called for each spacetype, only needs to add map once. */ if (keymap && keymap->modal_items) { return NULL; } @@ -2865,28 +4324,66 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf) return keymap; } +/* Turn off angle snapping. */ +static void knifetool_disable_angle_snapping(KnifeTool_OpData *kcd) +{ + kcd->angle_snapping_mode = KNF_CONSTRAIN_ANGLE_MODE_NONE; + kcd->angle_snapping = false; + kcd->is_angle_snapping = false; +} + +/* Turn off orientation locking. */ +static void knifetool_disable_orientation_locking(KnifeTool_OpData *kcd) +{ + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_MODE_NONE; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_NONE; + kcd->axis_constrained = false; +} + static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) { - Object *obedit = CTX_data_edit_object(C); KnifeTool_OpData *kcd = op->customdata; bool do_refresh = false; - if (!obedit || obedit->type != OB_MESH || BKE_editmesh_from_object(obedit) != kcd->em) { + if (!kcd->curr.ob || kcd->curr.ob->type != OB_MESH) { knifetool_exit(op); ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; } - em_setup_viewcontext(C, &kcd->vc); kcd->region = kcd->vc.region; - ED_view3d_init_mats_rv3d(obedit, kcd->vc.rv3d); /* needed to initialize clipping */ + ED_view3d_init_mats_rv3d(kcd->curr.ob, kcd->vc.rv3d); /* Needed to initialize clipping. */ if (kcd->mode == MODE_PANNING) { kcd->mode = kcd->prevmode; } - /* handle modal keymap */ + bool handled = false; + float snapping_increment_temp; + + if (kcd->angle_snapping) { + if (kcd->num.str_cur >= 2) { + knife_reset_snap_angle_input(kcd); + } + knife_update_header(C, op, kcd); /* Update the angle multiple. */ + /* Modal numinput active, try to handle numeric inputs first... */ + if (event->val == KM_PRESS && hasNumInput(&kcd->num) && handleNumInput(C, &kcd->num, event)) { + handled = true; + applyNumInput(&kcd->num, &snapping_increment_temp); + /* Restrict number key input to 0 - 90 degree range. */ + if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + snapping_increment_temp < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + kcd->angle_snapping_increment = snapping_increment_temp; + } + knife_update_active(C, kcd); + knife_update_header(C, op, kcd); + ED_region_tag_redraw(kcd->region); + return OPERATOR_RUNNING_MODAL; + } + } + + /* Handle modal keymap. */ if (event->type == EVT_MODAL_MAP) { switch (event->val) { case KNF_MODAL_CANCEL: @@ -2906,55 +4403,111 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; + case KNF_MODAL_UNDO: + knifetool_undo(kcd); + knife_update_active(C, kcd); + ED_region_tag_redraw(kcd->region); + handled = true; + break; case KNF_MODAL_MIDPOINT_ON: kcd->snap_midpoints = true; - knife_recalc_projmat(kcd); - knife_update_active(kcd); + knife_recalc_ortho(kcd); + knife_update_active(C, kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); do_refresh = true; + handled = true; break; case KNF_MODAL_MIDPOINT_OFF: kcd->snap_midpoints = false; - knife_recalc_projmat(kcd); - knife_update_active(kcd); + knife_recalc_ortho(kcd); + knife_update_active(C, kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); do_refresh = true; + handled = true; break; case KNF_MODAL_IGNORE_SNAP_ON: ED_region_tag_redraw(kcd->region); kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = true; knife_update_header(C, op, kcd); do_refresh = true; + handled = true; break; case KNF_MODAL_IGNORE_SNAP_OFF: ED_region_tag_redraw(kcd->region); kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = false; knife_update_header(C, op, kcd); do_refresh = true; + handled = true; break; case KNF_MODAL_ANGLE_SNAP_TOGGLE: - kcd->angle_snapping = !kcd->angle_snapping; + if (kcd->angle_snapping_mode != KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) { + kcd->angle_snapping_mode++; + kcd->snap_ref_edges_count = 0; + kcd->snap_edge = 0; + } + else { + kcd->angle_snapping_mode = KNF_CONSTRAIN_ANGLE_MODE_NONE; + } + kcd->angle_snapping = (kcd->angle_snapping_mode != KNF_CONSTRAIN_ANGLE_MODE_NONE); + kcd->angle_snapping_increment = RAD2DEGF( + RNA_float_get(op->ptr, "angle_snapping_increment")); + knifetool_disable_orientation_locking(kcd); + knife_reset_snap_angle_input(kcd); + knife_update_active(C, kcd); knife_update_header(C, op, kcd); + ED_region_tag_redraw(kcd->region); do_refresh = true; + handled = true; + break; + case KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE: + if (kcd->angle_snapping && kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) { + if (kcd->snap_ref_edges_count) { + kcd->snap_edge++; + kcd->snap_edge %= kcd->snap_ref_edges_count; + } + } + do_refresh = true; + handled = true; break; case KNF_MODAL_CUT_THROUGH_TOGGLE: kcd->cut_through = !kcd->cut_through; knife_update_header(C, op, kcd); do_refresh = true; + handled = true; + break; + case KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE: + if (kcd->dist_angle_mode != KNF_MEASUREMENT_ANGLE) { + kcd->dist_angle_mode++; + } + else { + kcd->dist_angle_mode = KNF_MEASUREMENT_NONE; + } + kcd->show_dist_angle = (kcd->dist_angle_mode != KNF_MEASUREMENT_NONE); + knife_update_header(C, op, kcd); + do_refresh = true; + handled = true; + break; + case KNF_MODAL_DEPTH_TEST_TOGGLE: + kcd->depth_test = !kcd->depth_test; + ED_region_tag_redraw(kcd->region); + knife_update_header(C, op, kcd); + do_refresh = true; + handled = true; break; case KNF_MODAL_NEW_CUT: ED_region_tag_redraw(kcd->region); knife_finish_cut(kcd); kcd->mode = MODE_IDLE; + handled = true; break; case KNF_MODAL_ADD_CUT: - knife_recalc_projmat(kcd); + knife_recalc_ortho(kcd); - /* get the value of the event which triggered this one */ + /* Get the value of the event which triggered this one. */ if (event->prevval != KM_RELEASE) { if (kcd->mode == MODE_DRAGGING) { knife_add_cut(kcd); @@ -2965,7 +4518,17 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->init = kcd->curr; } - /* freehand drawing is incompatible with cut-through */ + /* Preserve correct prev.cage for angle drawing calculations. */ + if (kcd->prev.edge == NULL && kcd->prev.vert == NULL) { + /* "knife_start_cut" moves prev.cage so needs to be recalculated. */ + /* Only occurs if prev was started on a face. */ + knifetool_recast_cageco(kcd, kcd->prev.mval, kcd->mdata.corr_prev_cage); + } + else { + copy_v3_v3(kcd->mdata.corr_prev_cage, kcd->prev.cage); + } + + /* Freehand drawing is incompatible with cut-through. */ if (kcd->cut_through == false) { kcd->is_drag_hold = true; /* No edge snapping while dragging (edges are too sticky when cuts are immediate). */ @@ -2975,12 +4538,14 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) else { kcd->is_drag_hold = false; kcd->ignore_edge_snapping = false; + kcd->is_drag_undo = false; - /* needed because the last face 'hit' is ignored when dragging */ - knifetool_update_mval(kcd, kcd->curr.mval); + /* Needed because the last face 'hit' is ignored when dragging. */ + knifetool_update_mval(C, kcd, kcd->curr.mval); } ED_region_tag_redraw(kcd->region); + handled = true; break; case KNF_MODAL_ADD_CUT_CLOSED: if (kcd->mode == MODE_DRAGGING) { @@ -2988,14 +4553,15 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Shouldn't be possible with default key-layout, just in case. */ if (kcd->is_drag_hold) { kcd->is_drag_hold = false; - knifetool_update_mval(kcd, kcd->curr.mval); + kcd->is_drag_undo = false; + knifetool_update_mval(C, kcd, kcd->curr.mval); } kcd->prev = kcd->curr; kcd->curr = kcd->init; knife_project_v2(kcd, kcd->curr.cage, kcd->curr.mval); - knifetool_update_mval(kcd, kcd->curr.mval); + knifetool_update_mval(C, kcd, kcd->curr.mval); knife_add_cut(kcd); @@ -3003,6 +4569,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) knife_finish_cut(kcd); kcd->mode = MODE_IDLE; } + handled = true; break; case KNF_MODAL_PANNING: if (event->val != KM_RELEASE) { @@ -3028,9 +4595,9 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) case WHEELDOWNMOUSE: case NDOF_MOTION: return OPERATOR_PASS_THROUGH; - case MOUSEMOVE: /* mouse moved somewhere to select another loop */ + case MOUSEMOVE: /* Mouse moved somewhere to select another loop. */ if (kcd->mode != MODE_PANNING) { - knifetool_update_mval_i(kcd, event->mval); + knifetool_update_mval_i(C, kcd, event->mval); if (kcd->is_drag_hold) { if (kcd->totlinehit >= 2) { @@ -3043,6 +4610,58 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) } } + if (kcd->angle_snapping) { + if (kcd->num.str_cur >= 2) { + knife_reset_snap_angle_input(kcd); + } + /* Modal numinput inactive, try to handle numeric inputs last... */ + if (!handled && event->val == KM_PRESS && handleNumInput(C, &kcd->num, event)) { + applyNumInput(&kcd->num, &snapping_increment_temp); + /* Restrict number key input to 0 - 90 degree range. */ + if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + snapping_increment_temp < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + kcd->angle_snapping_increment = snapping_increment_temp; + } + knife_update_active(C, kcd); + knife_update_header(C, op, kcd); + ED_region_tag_redraw(kcd->region); + return OPERATOR_RUNNING_MODAL; + } + } + + /* Constrain axes with X,Y,Z keys. */ + if (event->val == KM_PRESS && ELEM(event->type, EVT_XKEY, EVT_YKEY, EVT_ZKEY)) { + if (event->type == EVT_XKEY && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_X; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; + kcd->axis_string[0] = 'X'; + } + else if (event->type == EVT_YKEY && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Y; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; + kcd->axis_string[0] = 'Y'; + } + else if (event->type == EVT_ZKEY && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Z; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; + kcd->axis_string[0] = 'Z'; + } + else { + /* Cycle through modes with repeated key presses. */ + if (kcd->constrain_axis_mode != KNF_CONSTRAIN_AXIS_MODE_LOCAL) { + kcd->constrain_axis_mode++; + kcd->axis_string[0] += 32; /* Lower case. */ + } + else { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_NONE; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_NONE; + } + } + kcd->axis_constrained = (kcd->constrain_axis != KNF_CONSTRAIN_AXIS_NONE); + knifetool_disable_angle_snapping(kcd); + knife_update_header(C, op, kcd); + } + if (kcd->mode == MODE_DRAGGING) { op->flag &= ~OP_IS_MODAL_CURSOR_REGION; } @@ -3051,12 +4670,12 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (do_refresh) { - /* we don't really need to update mval, - * but this happens to be the best way to refresh at the moment */ - knifetool_update_mval_i(kcd, event->mval); + /* We don't really need to update mval, + * but this happens to be the best way to refresh at the moment. */ + knifetool_update_mval_i(C, kcd, event->mval); } - /* keep going until the user confirms */ + /* Keep going until the user confirms. */ return OPERATOR_RUNNING_MODAL; } @@ -3064,34 +4683,59 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) { const bool only_select = RNA_boolean_get(op->ptr, "only_selected"); const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry"); + const bool xray = !RNA_boolean_get(op->ptr, "xray"); + const int visible_measurements = RNA_enum_get(op->ptr, "visible_measurements"); + const int angle_snapping = RNA_enum_get(op->ptr, "angle_snapping"); const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input"); + const float angle_snapping_increment = RAD2DEGF( + RNA_float_get(op->ptr, "angle_snapping_increment")); ViewContext vc; KnifeTool_OpData *kcd; em_setup_viewcontext(C, &vc); + /* alloc new customdata */ + kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__); + + knifetool_init(C, + &vc, + kcd, + only_select, + cut_through, + xray, + visible_measurements, + angle_snapping, + angle_snapping_increment, + true); + if (only_select) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (em->bm->totfacesel == 0) { + Object *obedit; + BMEditMesh *em; + bool faces_selected = false; + + for (uint b = 0; b < kcd->objects_len; b++) { + obedit = kcd->objects[b]; + em = BKE_editmesh_from_object(obedit); + if (em->bm->totfacesel != 0) { + faces_selected = true; + } + } + + if (!faces_selected) { BKE_report(op->reports, RPT_ERROR, "Selected faces required"); + knifetool_cancel(C, op); return OPERATOR_CANCELLED; } } - /* alloc new customdata */ - kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - - knifetool_init(&vc, kcd, only_select, cut_through, true); - op->flag |= OP_IS_MODAL_CURSOR_REGION; - /* add a modal handler for this operator - handles loop selection */ + /* Add a modal handler for this operator - handles loop selection. */ WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE); WM_event_add_modal_handler(C, op); - knifetool_update_mval_i(kcd, event->mval); + knifetool_update_mval_i(C, kcd, event->mval); if (wait_for_input == false) { /* Avoid copy-paste logic. */ @@ -3112,28 +4756,72 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) void MESH_OT_knife_tool(wmOperatorType *ot) { - /* description */ + /* Description. */ ot->name = "Knife Topology Tool"; ot->idname = "MESH_OT_knife_tool"; ot->description = "Cut new topology"; - /* callbacks */ + /* Callbacks. */ ot->invoke = knifetool_invoke; ot->modal = knifetool_modal; ot->cancel = knifetool_cancel; ot->poll = ED_operator_editmesh_view3d; - /* flags */ + /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - /* properties */ + /* Properties. */ PropertyRNA *prop; + static const EnumPropertyItem visible_measurements_items[] = { + {KNF_MEASUREMENT_NONE, "NONE", 0, "None", "Show no measurements"}, + {KNF_MEASUREMENT_BOTH, "BOTH", 0, "Both", "Show both distances and angles"}, + {KNF_MEASUREMENT_DISTANCE, "DISTANCE", 0, "Distance", "Show just distance measurements"}, + {KNF_MEASUREMENT_ANGLE, "ANGLE", 0, "Angle", "Show just angle measurements"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem angle_snapping_items[] = { + {KNF_CONSTRAIN_ANGLE_MODE_NONE, "NONE", 0, "None", "No angle snapping"}, + {KNF_CONSTRAIN_ANGLE_MODE_SCREEN, "SCREEN", 0, "Screen", "Screen space angle snapping"}, + {KNF_CONSTRAIN_ANGLE_MODE_RELATIVE, + "RELATIVE", + 0, + "Relative", + "Angle snapping relative to the previous cut edge"}, + {0, NULL, 0, NULL, NULL}, + }; + RNA_def_boolean(ot->srna, "use_occlude_geometry", true, "Occlude Geometry", "Only cut the front most geometry"); RNA_def_boolean(ot->srna, "only_selected", false, "Only Selected", "Only cut selected geometry"); + RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts through geometry"); + + RNA_def_enum(ot->srna, + "visible_measurements", + visible_measurements_items, + KNF_MEASUREMENT_NONE, + "Measurements", + "Visible distance and angle measurements"); + RNA_def_enum(ot->srna, + "angle_snapping", + angle_snapping_items, + KNF_CONSTRAIN_ANGLE_MODE_NONE, + "Angle Snapping", + "Angle snapping mode"); + + prop = RNA_def_float(ot->srna, + "angle_snapping_increment", + DEG2RADF(KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT), + DEG2RADF(KNIFE_MIN_ANGLE_SNAPPING_INCREMENT), + DEG2RADF(KNIFE_MAX_ANGLE_SNAPPING_INCREMENT), + "Angle Snap Increment", + "The angle snap increment used when in constrained angle mode", + DEG2RADF(KNIFE_MIN_ANGLE_SNAPPING_INCREMENT), + DEG2RADF(KNIFE_MAX_ANGLE_SNAPPING_INCREMENT)); + RNA_def_property_subtype(prop, PROP_ANGLE); prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); @@ -3168,32 +4856,41 @@ static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2]) /** * \param use_tag: When set, tag all faces inside the polylines. */ -void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) +void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) { KnifeTool_OpData *kcd; - /* init */ + /* Init. */ { const bool only_select = false; - const bool is_interactive = false; /* can enable for testing */ + const bool is_interactive = false; /* Can enable for testing. */ + const bool xray = false; + const int visible_measurements = KNF_MEASUREMENT_NONE; + const int angle_snapping = KNF_CONSTRAIN_ANGLE_MODE_NONE; + const float angle_snapping_increment = KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT; kcd = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - knifetool_init(vc, kcd, only_select, cut_through, is_interactive); + knifetool_init(C, + vc, + kcd, + only_select, + cut_through, + xray, + visible_measurements, + angle_snapping, + angle_snapping_increment, + is_interactive); kcd->ignore_edge_snapping = true; kcd->ignore_vert_snapping = true; - - if (use_tag) { - BM_mesh_elem_hflag_enable_all(kcd->em->bm, BM_EDGE, BM_ELEM_TAG, false); - } } - /* execute */ + /* Execute. */ { LinkNode *p = polys; - knife_recalc_projmat(kcd); + knife_recalc_ortho(kcd); while (p) { const float(*mval_fl)[2] = p->link; @@ -3201,7 +4898,7 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th int i; for (i = 0; i < mval_tot; i++) { - knifetool_update_mval(kcd, mval_fl[i]); + knifetool_update_mval(C, kcd, mval_fl[i]); if (i == 0) { knife_start_cut(kcd); kcd->mode = MODE_DRAGGING; @@ -3216,104 +4913,108 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th } } - /* finish */ + /* Finish. */ { - knifetool_finish_ex(kcd); + Object *ob; + BMEditMesh *em; + for (uint b = 0; b < kcd->objects_len; b++) { - /* tag faces inside! */ - if (use_tag) { - BMesh *bm = kcd->em->bm; - float projmat[4][4]; + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); - BMEdge *e; - BMIter iter; - - bool keep_search; - - /* freed on knifetool_finish_ex, but we need again to check if points are visible */ - if (kcd->cut_through == false) { - knifetool_init_bmbvh(kcd); + if (use_tag) { + BM_mesh_elem_hflag_enable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false); } - ED_view3d_ob_project_mat_get(kcd->region->regiondata, kcd->ob, projmat); + knifetool_finish_single_ex(kcd, ob, b); - /* use face-loop tag to store if we have intersected */ + /* Tag faces inside! */ + if (use_tag) { + BMesh *bm = em->bm; + BMEdge *e; + BMIter iter; + bool keep_search; + + /* Use face-loop tag to store if we have intersected. */ #define F_ISECT_IS_UNKNOWN(f) BM_elem_flag_test(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG) #define F_ISECT_SET_UNKNOWN(f) BM_elem_flag_enable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG) #define F_ISECT_SET_OUTSIDE(f) BM_elem_flag_disable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG) - { - BMFace *f; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - F_ISECT_SET_UNKNOWN(f); - BM_elem_flag_disable(f, BM_ELEM_TAG); - } - } - - /* tag all faces linked to cut edges */ - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - /* check are we tagged?, then we are an original face */ - if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) { + { BMFace *f; - BMIter fiter; - BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { - float cent[3], cent_ss[2]; - BM_face_calc_point_in_face(f, cent); - knife_project_v2(kcd, cent, cent_ss); - if (edbm_mesh_knife_point_isect(polys, cent_ss)) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - } + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + F_ISECT_SET_UNKNOWN(f); + BM_elem_flag_disable(f, BM_ELEM_TAG); } } - } - /* expand tags for faces which are not cut, but are inside the polys */ - do { - BMFace *f; - keep_search = false; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_TAG) == false && (F_ISECT_IS_UNKNOWN(f))) { - /* am I connected to a tagged face via an un-tagged edge - * (ie, not across a cut) */ - BMLoop *l_first = BM_FACE_FIRST_LOOP(f); - BMLoop *l_iter = l_first; - bool found = false; - - do { - if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { - /* now check if the adjacent faces is tagged */ - BMLoop *l_radial_iter = l_iter->radial_next; - if (l_radial_iter != l_iter) { - do { - if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { - found = true; - } - } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && - (found == false)); - } - } - } while ((l_iter = l_iter->next) != l_first && (found == false)); - - if (found) { + /* Tag all faces linked to cut edges. */ + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + /* Check are we tagged?, then we are an original face. */ + if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) { + BMFace *f; + BMIter fiter; + BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { float cent[3], cent_ss[2]; BM_face_calc_point_in_face(f, cent); + mul_m4_v3(ob->obmat, cent); knife_project_v2(kcd, cent, cent_ss); - if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) && - edbm_mesh_knife_point_isect(polys, cent_ss)) { + if (edbm_mesh_knife_point_isect(polys, cent_ss)) { BM_elem_flag_enable(f, BM_ELEM_TAG); - keep_search = true; - } - else { - /* don't lose time on this face again, set it as outside */ - F_ISECT_SET_OUTSIDE(f); } } } } - } while (keep_search); + + /* Expand tags for faces which are not cut, but are inside the polys. */ + do { + BMFace *f; + keep_search = false; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_TAG) == false && (F_ISECT_IS_UNKNOWN(f))) { + /* Am I connected to a tagged face via an un-tagged edge + * (ie, not across a cut)? */ + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + bool found = false; + + do { + if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { + /* Now check if the adjacent faces is tagged. */ + BMLoop *l_radial_iter = l_iter->radial_next; + if (l_radial_iter != l_iter) { + do { + if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { + found = true; + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && + (found == false)); + } + } + } while ((l_iter = l_iter->next) != l_first && (found == false)); + + if (found) { + float cent[3], cent_ss[2]; + BM_face_calc_point_in_face(f, cent); + mul_m4_v3(ob->obmat, cent); + knife_project_v2(kcd, cent, cent_ss); + if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) && + edbm_mesh_knife_point_isect(polys, cent_ss)) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + keep_search = true; + } + else { + /* Don't lose time on this face again, set it as outside. */ + F_ISECT_SET_OUTSIDE(f); + } + } + } + } + } while (keep_search); #undef F_ISECT_IS_UNKNOWN #undef F_ISECT_SET_UNKNOWN #undef F_ISECT_SET_OUTSIDE + } } knifetool_exit_ex(kcd); diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 669a09b3fd3..3129fb047ab 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -158,7 +158,7 @@ static int knifeproject_exec(bContext *C, wmOperator *op) ED_view3d_viewcontext_init_object(&vc, obedit); BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_mesh_knife(&vc, polys, true, cut_through); + EDBM_mesh_knife(C, &vc, polys, true, cut_through); /* select only tagged faces */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 03c99e40d1e..abff3c70e67 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -150,7 +150,8 @@ void MESH_OT_face_split_by_edges(struct wmOperatorType *ot); /* *** editmesh_knife.c *** */ void MESH_OT_knife_tool(struct wmOperatorType *ot); void MESH_OT_knife_project(struct wmOperatorType *ot); -void EDBM_mesh_knife(struct ViewContext *vc, +void EDBM_mesh_knife(struct bContext *C, + struct ViewContext *vc, struct LinkNode *polys, bool use_tag, bool cut_through); From bf948b2cef3ba340a6bba5e7bd7f4911c9a9275a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 22 Sep 2021 21:56:54 -0500 Subject: [PATCH 0121/1500] Custom Properties: Rewrite edit operator, improve UX This commit changes the custom property edit operator to make editing different properties types more obvious and expose more of the data, made more easily possible by the recent UI data refactor. Previously, the operator guessed the type you wanted based on what you wrote in a text box. That was problematic, you couldn't make a string property with a value of `1234`, and you had to know about the Python syntax for lists in order to create an array property. It was also slow and error prone; it was too easy to make a typo. Improvements compared to the old operator: - A type drop-down to choose between the property types. - Step and precision values are exposed. - Buttons that have the correct type based on the property. - String properties no longer display min, max, etc. buttons. - Generally works in more cases. The old operator tended to break. - Choose array length with a slider. - Easy to choose to use python evaluation when necessary. - Code is commented, split up, and much easier to understand. The custom property's value is purposefully not exposed, since the Edit operator is for changing the property's metadata now, rather than the value itself. Though in the "Python" mode the value is still available. More improvements are possible in the future, like exposing different subtypes, and improving the UI of the custom properties panel. Differential Revision: https://developer.blender.org/D12435 --- release/scripts/modules/rna_prop_ui.py | 5 +- release/scripts/startup/bl_operators/wm.py | 711 +++++++++++++-------- 2 files changed, 456 insertions(+), 260 deletions(-) diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 26a2f9ad89b..6d92c94a85c 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -21,9 +21,10 @@ import bpy from mathutils import Vector +from bpy.types import bpy_prop_array from idprop.types import IDPropertyArray, IDPropertyGroup -ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector) +ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector, bpy_prop_array) # Maximum length of an array property for which a multi-line # edit field will be displayed in the Custom Properties panel. @@ -136,7 +137,7 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): def assign_props(prop, val, key): prop.data_path = context_member - prop.property = key + prop.property_name = key try: prop.value = str(val) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index ebf80ca9ee4..6bf45cc5a15 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -23,6 +23,7 @@ import bpy from bpy.types import ( Menu, Operator, + bpy_prop_array, ) from bpy.props import ( BoolProperty, @@ -31,6 +32,8 @@ from bpy.props import ( FloatProperty, IntProperty, StringProperty, + IntVectorProperty, + FloatVectorProperty, ) from bpy.app.translations import pgettext_iface as iface_ @@ -1266,48 +1269,20 @@ rna_path = StringProperty( options={'HIDDEN'}, ) -rna_value = StringProperty( - name="Property Value", - description="Property value edit", - maxlen=1024, -) - -rna_default = StringProperty( - name="Default Value", - description="Default value of the property. Important for NLA mixing", - maxlen=1024, -) - -rna_custom_property = StringProperty( +rna_custom_property_name = StringProperty( name="Property Name", description="Property name edit", # Match `MAX_IDPROP_NAME - 1` in Blender's source. maxlen=63, ) -rna_min = FloatProperty( - name="Min", - description="Minimum value of the property", - default=-10000.0, - precision=3, -) - -rna_max = FloatProperty( - name="Max", - description="Maximum value of the property", - default=10000.0, - precision=3, -) - -rna_use_soft_limits = BoolProperty( - name="Use Soft Limits", - description="Limits the Property Value slider to a range, values outside the range must be inputted numerically", -) - -rna_is_overridable_library = BoolProperty( - name="Is Library Overridable", - description="Allow the property to be overridden when the data-block is linked", - default=False, +rna_custom_property_type_items = ( + ('FLOAT', "Float", "A single floating-point value"), + ('FLOAT_ARRAY', "Float Array", "An array of floating-point values"), + ('INT', "Integer", "A single integer"), + ('INT_ARRAY', "Integer Array", "An array of integers"), + ('STRING', "String", "A string value"), + ('PYTHON', "Python", "Edit a python value directly, for unsupported property types"), ) # Most useful entries of rna_enum_property_subtype_items for number arrays: @@ -1319,35 +1294,190 @@ rna_vector_subtype_items = ( ('QUATERNION', "Quaternion Rotation", "Quaternion rotation (affects NLA blending)"), ) - class WM_OT_properties_edit(Operator): - """Edit the attributes of the property""" + """Change a custom property's type, or adjust how it is displayed in the interface""" bl_idname = "wm.properties_edit" bl_label = "Edit Property" # register only because invoke_props_popup requires. bl_options = {'REGISTER', 'INTERNAL'} + # Common settings used for all property types. Generally, separate properties are used for each + # type to improve the experience when choosing UI data values. + data_path: rna_path - property: rna_custom_property - value: rna_value - default: rna_default - min: rna_min - max: rna_max - use_soft_limits: rna_use_soft_limits - is_overridable_library: rna_is_overridable_library - soft_min: rna_min - soft_max: rna_max + property_name: rna_custom_property_name + property_type: EnumProperty( + name="Type", + items=lambda self, _context: WM_OT_properties_edit.type_items, + ) + is_overridable_library: BoolProperty( + name="Is Library Overridable", + description="Allow the property to be overridden when the data-block is linked", + default=False, + ) description: StringProperty( - name="Tooltip", + name="Description", + ) + + # Shared for integer and string properties. + + use_soft_limits: BoolProperty( + name="Use Soft Limits", + description="Limits the Property Value slider to a range, values outside the range must be inputted numerically", + ) + array_length: IntProperty( + name="Array Length", + default=3, + min=1, + max=32, # 32 is the maximum size for RNA array properties. + ) + + # Integer properties. + + # This property stores values for both array and non-array properties. + default_int: IntVectorProperty( + name="Default Value", + size=32, + ) + min_int: IntProperty( + name="Min", + default=-10000, + ) + max_int: IntProperty( + name="Max", + default=10000, + ) + soft_min_int: IntProperty( + name="Soft Min", + default=-10000, + ) + soft_max_int: IntProperty( + name="Soft Max", + default=10000, + ) + step_int: IntProperty( + name="Step", + min=1, + default=1, + ) + + # Float properties. + + # This property stores values for both array and non-array properties. + default_float: FloatVectorProperty( + name="Default Value", + size=32, + ) + min_float: FloatProperty( + name="Min", + default=-10000.0, + ) + max_float: FloatProperty( + name="Max", + default=-10000.0, + ) + soft_min_float: FloatProperty( + name="Soft Min", + default=-10000.0, + ) + soft_max_float: FloatProperty( + name="Soft Max", + default=-10000.0, + ) + precision: IntProperty( + name="Precision", + default=3, + min=0, + max=8, + ) + step_float: FloatProperty( + name="Step", + default=0.1, + min=0.001, ) subtype: EnumProperty( name="Subtype", items=lambda self, _context: WM_OT_properties_edit.subtype_items, ) + # String properties. + + default_string: StringProperty( + name="Default Value", + maxlen=1024, + ) + + # Store the value converted to a string as a fallback for otherwise unsupported types. + eval_string: StringProperty( + name="Value", + description="Python value for unsupported custom property types" + ) + + type_items = rna_custom_property_type_items subtype_items = rna_vector_subtype_items - def _init_subtype(self, prop_type, is_array, subtype): + # Helper method to avoid repetative code to retrieve a single value from sequences and non-sequences. + @staticmethod + def _convert_new_value_single(old_value, new_type): + if hasattr(old_value, "__len__"): + return new_type(old_value[0]) + return new_type(old_value) + + # Helper method to create a list of a given value and type, using a sequence or non-sequence old value. + @staticmethod + def _convert_new_value_array(old_value, new_type, new_len): + if hasattr(old_value, "__len__"): + new_array = [new_type()] * new_len + for i in range(min(len(old_value), new_len)): + new_array[i] = new_type(old_value[i]) + return new_array + return [new_type(old_value)] * new_len + + # Convert an old property for a string, avoiding unhelpful string representations for custom list types. + @staticmethod + def _convert_old_property_to_string(item, name): + # The IDProperty group view API currently doesn't have a "lookup" method. + for key, value in item.items(): + if key == name: + old_value = value + break + + # In order to get a better string conversion, convert the property to a builtin sequence type first. + to_dict = getattr(old_value, "to_dict", None) + to_list = getattr(old_value, "to_list", None) + if to_dict: + old_value = to_dict() + elif to_list: + old_value = to_list() + + return str(old_value) + + # Retrieve the current type of the custom property on the RNA struct. Some properties like group properties + # can be created in the UI, but editing their meta-data isn't supported. In that case, return 'PYTHON'. + def _get_property_type(self, item, property_name): + from rna_prop_ui import ( + rna_idprop_value_item_type, + ) + + prop_value = item[property_name] + + prop_type, is_array = rna_idprop_value_item_type(prop_value) + if prop_type == int: + if is_array: + return 'INT_ARRAY' + return 'INT' + elif prop_type == float: + if is_array: + return 'FLOAT_ARRAY' + return 'FLOAT' + elif prop_type == str: + if is_array: + return 'PYTHON' + return 'STRING' + + return 'PYTHON' + + def _init_subtype(self, subtype): subtype = subtype or 'NONE' subtype_items = rna_vector_subtype_items @@ -1358,121 +1488,139 @@ class WM_OT_properties_edit(Operator): WM_OT_properties_edit.subtype_items = subtype_items self.subtype = subtype - def _cmp_props_get(self): - # Changing these properties will refresh the UI - return { - "use_soft_limits": self.use_soft_limits, - "soft_range": (self.soft_min, self.soft_max), - "hard_range": (self.min, self.max), - } + # Fill the operator's properties with the UI data properties from the existing custom property. + # Note that if the UI data doesn't exist yet, the access will create it and use those default values. + def _fill_old_ui_data(self, item, name): + ui_data = item.id_properties_ui(name) + rna_data = ui_data.as_dict() - def get_value_eval(self): - failed = False - try: - value_eval = eval(self.value) - # assert else None -> None, not "None", see T33431. - assert(type(value_eval) in {str, float, int, bool, tuple, list}) - except: - failed = True - value_eval = self.value + if self.property_type in {'FLOAT', 'FLOAT_ARRAY'}: + self.min_float = rna_data["min"] + self.max_float = rna_data["max"] + self.soft_min_float = rna_data["soft_min"] + self.soft_max_float = rna_data["soft_max"] + self.precision = rna_data["precision"] + self.step_float = rna_data["step"] + self.subtype = rna_data["subtype"] + self.use_soft_limits = ( + self.min_float != self.soft_min_float or + self.max_float != self.soft_max_float + ) + default = self._convert_new_value_array(rna_data["default"], float, 32) + self.default_float = default if isinstance(default, list) else [default] * 32 + elif self.property_type in {'INT', 'INT_ARRAY'}: + self.min_int = rna_data["min"] + self.max_int = rna_data["max"] + self.soft_min_int = rna_data["soft_min"] + self.soft_max_int = rna_data["soft_max"] + self.step_int = rna_data["step"] + self.use_soft_limits = ( + self.min_int != self.soft_min_int or + self.max_int != self.soft_max_int + ) + self.default_int = self._convert_new_value_array(rna_data["default"], int, 32) + elif self.property_type == 'STRING': + self.default_string = rna_data["default"] - return value_eval, failed + if self.property_type in { 'FLOAT_ARRAY', 'INT_ARRAY'}: + self.array_length = len(item[name]) - def get_default_eval(self): - failed = False - try: - default_eval = eval(self.default) - # assert else None -> None, not "None", see T33431. - assert(type(default_eval) in {str, float, int, bool, tuple, list}) - except: - failed = True - default_eval = self.default + # The dictionary does not contain the description if it was empty. + self.description = rna_data.get("description", "") - return default_eval, failed + self._init_subtype(self.subtype) + escaped_name = bpy.utils.escape_identifier(name) + self.is_overridable_library = bool(item.is_property_overridable_library('["%s"]' % escaped_name)) - def execute(self, context): + # When the operator chooses a different type than the original property, + # attempt to convert the old value to the new type for continuity and speed. + def _get_converted_value(self, item, name_old, prop_type_new): + if prop_type_new == 'INT': + return self._convert_new_value_single(item[name_old], int) + + if prop_type_new == 'FLOAT': + return self._convert_new_value_single(item[name_old], float) + + if prop_type_new == 'INT_ARRAY': + prop_type_old = self._get_property_type(item, name_old) + if prop_type_old in {'INT', 'FLOAT', 'INT_ARRAY', 'FLOAT_ARRAY'}: + return self._convert_new_value_array(item[name_old], int, self.array_length) + + if prop_type_new == 'FLOAT_ARRAY': + prop_type_old = self._get_property_type(item, name_old) + if prop_type_old in {'INT', 'FLOAT', 'FLOAT_ARRAY', 'INT_ARRAY'}: + return self._convert_new_value_array(item[name_old], float, self.array_length) + + if prop_type_new == 'STRING': + return self._convert_old_property_to_string(item, name_old) + + # If all else fails, create an empty string property. That should avoid errors later on anyway. + return "" + + # Any time the target type is changed in the dialog, it's helpful to convert the UI data values + # to the new type as well, when possible, currently this only applies for floats and ints. + def _convert_old_ui_data_to_new_type(self, prop_type_old, prop_type_new): + if prop_type_new in {'INT', 'INT_ARRAY'} and prop_type_old in {'FLOAT', 'FLOAT_ARRAY'}: + self.min_int = int(self.min_float) + self.max_int = int(self.max_float) + self.soft_min_int = int(self.soft_min_float) + self.soft_max_int = int(self.soft_max_float) + self.default_int = self._convert_new_value_array(self.default_float, int, 32) + elif prop_type_new in {'FLOAT', 'FLOAT_ARRAY'} and prop_type_old in {'INT', 'INT_ARRAY'}: + self.min_float = float(self.min_int) + self.max_float = float(self.max_int) + self.soft_min_float = float(self.soft_min_int) + self.soft_max_float = float(self.soft_max_int) + self.default_float = self._convert_new_value_array(self.default_int, float, 32) + # Don't convert between string and float/int defaults here, it's not expected like the other conversions. + + # Fill the property's UI data with the values chosen in the operator. + def _create_ui_data_for_new_prop(self, item, name, prop_type_new): + if prop_type_new in {'INT', 'INT_ARRAY'}: + ui_data = item.id_properties_ui(name) + ui_data.update( + min=self.min_int, + max=self.max_int, + soft_min=self.soft_min_int if self.use_soft_limits else self.min_int, + soft_max=self.soft_max_int if self.use_soft_limits else self.min_int, + step=self.step_int, + default=self.default_int[0] if prop_type_new == 'INT' else self.default_int[:self.array_length], + description=self.description, + ) + elif prop_type_new in {'FLOAT', 'FLOAT_ARRAY'}: + ui_data = item.id_properties_ui(name) + ui_data.update( + min=self.min_float, + max=self.max_float, + soft_min=self.soft_min_float if self.use_soft_limits else self.min_float, + soft_max=self.soft_max_float if self.use_soft_limits else self.max_float, + step=self.step_float, + precision=self.precision, + default=self.default_float[0] if prop_type_new == 'FLOAT' else self.default_float[:self.array_length], + description=self.description, + subtype=self.subtype, + ) + elif prop_type_new == 'STRING': + ui_data = item.id_properties_ui(name) + ui_data.update( + default=self.default_string, + description=self.description, + ) + + escaped_name = bpy.utils.escape_identifier(name) + item.property_overridable_library_set('["%s"]' % escaped_name, self.is_overridable_library) + + def _update_blender_for_prop_change(self, context, item, name, prop_type_old, prop_type_new): from rna_prop_ui import ( rna_idprop_ui_prop_update, - rna_idprop_value_item_type, ) - data_path = self.data_path - prop = self.property - prop_escape = bpy.utils.escape_identifier(prop) - - prop_old = getattr(self, "_last_prop", [None])[0] - - if prop_old is None: - self.report({'ERROR'}, "Direct execution not supported") - return {'CANCELLED'} - - value_eval, value_failed = self.get_value_eval() - default_eval, default_failed = self.get_default_eval() - - # First remove - item = eval("context.%s" % data_path) - - if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference): - self.report({'ERROR'}, "Cannot edit properties from override data") - return {'CANCELLED'} - - prop_type_old = type(item[prop_old]) - - # Deleting the property will also remove the UI data. - del item[prop_old] - - # Reassign - item[prop] = value_eval - item.property_overridable_library_set('["%s"]' % prop_escape, self.is_overridable_library) - rna_idprop_ui_prop_update(item, prop) - - self._last_prop[:] = [prop] - - prop_value = item[prop] - prop_type_new = type(prop_value) - prop_type, is_array = rna_idprop_value_item_type(prop_value) - - if prop_type == int: - ui_data = item.id_properties_ui(prop) - if type(default_eval) == str: - self.report({'WARNING'}, "Could not evaluate number from default value") - default_eval = None - elif hasattr(default_eval, "__len__"): - default_eval = [int(round(value)) for value in default_eval] - ui_data.update( - min=int(round(self.min)), - max=int(round(self.max)), - soft_min=int(round(self.soft_min)), - soft_max=int(round(self.soft_max)), - default=default_eval, - subtype=self.subtype, - description=self.description - ) - elif prop_type == float: - ui_data = item.id_properties_ui(prop) - if type(default_eval) == str: - self.report({'WARNING'}, "Could not evaluate number from default value") - default_eval = None - ui_data.update( - min=self.min, - max=self.max, - soft_min=self.soft_min, - soft_max=self.soft_max, - default=default_eval, - subtype=self.subtype, - description=self.description - ) - elif prop_type == str and not is_array and not default_failed: # String arrays do not support UI data. - ui_data = item.id_properties_ui(prop) - ui_data.update( - default=self.default, - subtype=self.subtype, - description=self.description - ) + rna_idprop_ui_prop_update(item, name) # If we have changed the type of the property, update its potential anim curves! if prop_type_old != prop_type_new: - data_path = '["%s"]' % prop_escape + escaped_name = bpy.utils.escape_identifier(name) + data_path = '["%s"]' % escaped_name done = set() def _update(fcurves): @@ -1498,149 +1646,196 @@ class WM_OT_properties_edit(Operator): for nt in adt.nla_tracks: _update_strips(nt.strips) - # Otherwise existing buttons which reference freed - # memory may crash Blender T26510. - # context.area.tag_redraw() + # Otherwise existing buttons which reference freed memory may crash Blender (T26510). for win in context.window_manager.windows: for area in win.screen.areas: area.tag_redraw() - return {'FINISHED'} - - def invoke(self, context, _event): - from rna_prop_ui import ( - rna_idprop_value_to_python, - rna_idprop_value_item_type - ) - - prop = self.property - prop_escape = bpy.utils.escape_identifier(prop) - - data_path = self.data_path - - if not data_path: - self.report({'ERROR'}, "Data path not set") + def execute(self, context): + name_old = getattr(self, "_old_prop_name", [None])[0] + if name_old is None: + self.report({'ERROR'}, "Direct execution not supported") return {'CANCELLED'} - self._last_prop = [prop] + data_path = self.data_path + name = self.property_name item = eval("context.%s" % data_path) - if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference): self.report({'ERROR'}, "Cannot edit properties from override data") return {'CANCELLED'} - # retrieve overridable static - is_overridable = item.is_property_overridable_library('["%s"]' % prop_escape) - self.is_overridable_library = bool(is_overridable) + prop_type_old = self._get_property_type(item, name_old) + prop_type_new = self.property_type + self._old_prop_name[:] = [name] - # default default value - value, value_failed = self.get_value_eval() - prop_type, is_array = rna_idprop_value_item_type(value) - if prop_type in {int, float}: - self.default = str(prop_type(0)) + if prop_type_new == 'PYTHON': + try: + new_value = eval(self.eval_string) + except Exception as ex: + self.report({'WARNING'}, "Python evaluation failed: " + str(ex)) + return {'CANCELLED'} + try: + item[name] = new_value + except Exception as ex: + self.report({'ERROR'}, "Failed to assign value: " + str(ex)) + return {'CANCELLED'} + if name_old != name: + del item[name_old] else: - self.default = "" + new_value = self._get_converted_value(item, name_old, prop_type_new) + del item[name_old] + item[name] = new_value - # setup defaults - if prop_type in {int, float}: - ui_data = item.id_properties_ui(prop) - rna_data = ui_data.as_dict() - self.subtype = rna_data["subtype"] - self.min = rna_data["min"] - self.max = rna_data["max"] - self.soft_min = rna_data["soft_min"] - self.soft_max = rna_data["soft_max"] - self.use_soft_limits = ( - self.min != self.soft_min or - self.max != self.soft_max - ) - self.default = str(rna_data["default"]) - self.description = rna_data.get("description", "") - elif prop_type == str and not is_array and not value_failed: # String arrays do not support UI data. - ui_data = item.id_properties_ui(prop) - rna_data = ui_data.as_dict() - self.subtype = rna_data["subtype"] - self.default = str(rna_data["default"]) - self.description = rna_data.get("description", "") - else: - self.min = self.soft_min = 0 - self.max = self.soft_max = 1 - self.use_soft_limits = False - self.description = "" + self._create_ui_data_for_new_prop(item, name, prop_type_new) - self._init_subtype(prop_type, is_array, self.subtype) + self._update_blender_for_prop_change(context, item, name, prop_type_old, prop_type_new) - # store for comparison - self._cmp_props = self._cmp_props_get() + return {'FINISHED'} + + def invoke(self, context, _event): + data_path = self.data_path + if not data_path: + self.report({'ERROR'}, "Data path not set") + return {'CANCELLED'} + + name = self.property_name + + self._old_prop_name = [name] + self.last_property_type = self.property_type + + item = eval("context.%s" % data_path) + if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference): + self.report({'ERROR'}, "Properties from override data can not be edited") + return {'CANCELLED'} + + # Set operator's property type with the type of the existing property, to display the right settings. + old_type = self._get_property_type(item, name) + self.property_type = old_type + + # So that the operator can do something for unsupported properties, change the property into + # a string, just for editing in the dialog. When the operator executes, it will be converted back + # into a python value. Always do this conversion, in case the Python property edit type is selected. + self.eval_string = self._convert_old_property_to_string(item, name) + + if old_type != 'PYTHON': + self._fill_old_ui_data(item, name) wm = context.window_manager return wm.invoke_props_dialog(self) - def check(self, _context): - cmp_props = self._cmp_props_get() + def check(self, context): changed = False - if self._cmp_props != cmp_props: - if cmp_props["use_soft_limits"]: - if cmp_props["soft_range"] != self._cmp_props["soft_range"]: - self.min = min(self.min, self.soft_min) - self.max = max(self.max, self.soft_max) + + # In order to convert UI data between types for type changes before the operator has actually executed, + # compare against the type the last time the check method was called (the last time a value was edited). + if self.property_type != self.last_property_type: + self._convert_old_ui_data_to_new_type(self.last_property_type, self.property_type) + changed = True + + # Make sure that min is less than max, soft range is inside hard range, etc. + if self.property_type in {'FLOAT', 'FLOAT_ARRAY'}: + if self.min_float > self.max_float: + self.min_float, self.max_float = self.max_float, self.min_float + changed = True + if self.soft_min_float > self.soft_max_float: + self.soft_min_float, self.soft_max_float = self.soft_max_float, self.soft_min_float + changed = True + if self.use_soft_limits: + if self.soft_max_float > self.max_float: + self.soft_max_float = self.max_float changed = True - if cmp_props["hard_range"] != self._cmp_props["hard_range"]: - self.soft_min = max(self.min, self.soft_min) - self.soft_max = min(self.max, self.soft_max) + if self.soft_min_float < self.min_float: + self.soft_min_float = self.min_float changed = True - else: - if cmp_props["soft_range"] != cmp_props["hard_range"]: - self.soft_min = self.min - self.soft_max = self.max + elif self.property_type in {'INT', 'INT_ARRAY'}: + if self.min_int > self.max_int: + self.min_int, self.max_int = self.max_int, self.min_int + changed = True + if self.soft_min_int > self.soft_max_int: + self.soft_min_int, self.soft_max_int = self.soft_max_int, self.soft_min_int + changed = True + if self.use_soft_limits: + if self.soft_max_int > self.max_int: + self.soft_max_int = self.max_int + changed = True + if self.soft_min_int < self.min_int: + self.soft_min_int = self.min_int changed = True - changed |= (cmp_props["use_soft_limits"] != self._cmp_props["use_soft_limits"]) - - if changed: - cmp_props = self._cmp_props_get() - - self._cmp_props = cmp_props + self.last_property_type = self.property_type return changed def draw(self, _context): - from rna_prop_ui import ( - rna_idprop_value_item_type, - ) - layout = self.layout layout.use_property_split = True layout.use_property_decorate = False - layout.prop(self, "property") - layout.prop(self, "value") + layout.prop(self, "property_type") + layout.prop(self, "property_name") - value, value_failed = self.get_value_eval() - proptype, is_array = rna_idprop_value_item_type(value) + if self.property_type in {'FLOAT', 'FLOAT_ARRAY'}: + if self.property_type == 'FLOAT_ARRAY': + layout.prop(self, "array_length") + col = layout.column(align=True) + col.prop(self, "default_float", index=0, text="Default") + for i in range(1, self.array_length): + col.prop(self, "default_float", index=i, text=" ") + else: + layout.prop(self, "default_float", index=0) - row = layout.row() - row.enabled = proptype in {int, float, str} - row.prop(self, "default") + col = layout.column(align=True) + col.prop(self, "min_float") + col.prop(self, "max_float") - col = layout.column(align=True) - col.prop(self, "min") - col.prop(self, "max") + col = layout.column() + col.prop(self, "is_overridable_library") + col.prop(self, "use_soft_limits") - col = layout.column() - col.prop(self, "is_overridable_library") - col.prop(self, "use_soft_limits") + col = layout.column(align=True) + col.enabled = self.use_soft_limits + col.prop(self, "soft_min_float", text="Soft Min") + col.prop(self, "soft_max_float", text="Max") - col = layout.column(align=True) - col.enabled = self.use_soft_limits - col.prop(self, "soft_min", text="Soft Min") - col.prop(self, "soft_max", text="Max") - layout.prop(self, "description") + layout.prop(self, "step_float") + layout.prop(self, "precision") - if is_array and proptype == float: - layout.prop(self, "subtype") + # Subtype is only supported for float properties currently. + if self.property_type != 'FLOAT': + layout.prop(self, "subtype") + elif self.property_type in {'INT', 'INT_ARRAY'}: + if self.property_type == 'INT_ARRAY': + layout.prop(self, "array_length") + col = layout.column(align=True) + col.prop(self, "default_int", index=0, text="Default") + for i in range(1, self.array_length): + col.prop(self, "default_int", index=i, text=" ") + else: + layout.prop(self, "default_int", index=0) + + col = layout.column(align=True) + col.prop(self, "min_int") + col.prop(self, "max_int") + + col = layout.column() + col.prop(self, "is_overridable_library") + col.prop(self, "use_soft_limits") + + col = layout.column(align=True) + col.enabled = self.use_soft_limits + col.prop(self, "soft_min_int", text="Soft Min") + col.prop(self, "soft_max_int", text="Max") + + layout.prop(self, "step_int") + elif self.property_type == 'STRING': + layout.prop(self, "default_string") + + if self.property_type == 'PYTHON': + layout.prop(self, "eval_string") + else: + layout.prop(self, "description") class WM_OT_properties_add(Operator): @@ -1706,7 +1901,7 @@ class WM_OT_properties_remove(Operator): bl_options = {'UNDO', 'INTERNAL'} data_path: rna_path - property: rna_custom_property + property_name: rna_custom_property_name def execute(self, context): from rna_prop_ui import ( @@ -1719,9 +1914,9 @@ class WM_OT_properties_remove(Operator): self.report({'ERROR'}, "Cannot remove properties from override data") return {'CANCELLED'} - prop = self.property - rna_idprop_ui_prop_update(item, prop) - del item[prop] + name = self.property_name + rna_idprop_ui_prop_update(item, name) + del item[name] return {'FINISHED'} From 754d56dcc19373358d264f04b3630eeca614d0b9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 23 Sep 2021 15:02:10 +1000 Subject: [PATCH 0122/1500] Cleanup: spelling in comments --- intern/cycles/blender/blender_gpu_display.cpp | 2 +- intern/cycles/integrator/path_trace.cpp | 2 +- intern/cycles/integrator/path_trace_work.h | 2 +- intern/cycles/integrator/render_scheduler.h | 2 +- intern/cycles/render/gpu_display.h | 2 +- source/blender/blenkernel/intern/gpencil_geom.cc | 4 ++-- source/blender/blenkernel/intern/mesh_convert.cc | 2 +- source/blender/editors/mesh/editmesh_knife.c | 8 ++++---- source/blender/editors/space_view3d/view3d_select.c | 2 +- source/blender/gpu/intern/gpu_texture.cc | 2 +- source/blender/windowmanager/intern/wm_files_link.c | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/intern/cycles/blender/blender_gpu_display.cpp b/intern/cycles/blender/blender_gpu_display.cpp index c5c3a2bd155..456ca676cce 100644 --- a/intern/cycles/blender/blender_gpu_display.cpp +++ b/intern/cycles/blender/blender_gpu_display.cpp @@ -524,7 +524,7 @@ void BlenderGPUDisplay::do_draw(const GPUDisplayParams ¶ms) const float zoomed_width = params.size.x * zoom_.x; const float zoomed_height = params.size.y * zoom_.y; if (texture_.width != params.size.x || texture_.height != params.size.y) { - /* Resolution divider is different from 1, force enarest interpolation. */ + /* Resolution divider is different from 1, force nearest interpolation. */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else if (zoomed_width - params.size.x > 0.5f || zoomed_height - params.size.y > 0.5f) { diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index b62a06aea43..e785c0d1b19 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -244,7 +244,7 @@ static void foreach_sliced_buffer_params(const vector> const int slice_height = max(lround(height * weight), 1); /* Disallow negative values to deal with situations when there are more compute devices than - * scanlines. */ + * scan-lines. */ const int remaining_height = max(0, height - current_y); BufferParams slide_params = buffer_params; diff --git a/intern/cycles/integrator/path_trace_work.h b/intern/cycles/integrator/path_trace_work.h index 8c9c8811199..e1be1655edd 100644 --- a/intern/cycles/integrator/path_trace_work.h +++ b/intern/cycles/integrator/path_trace_work.h @@ -104,7 +104,7 @@ class PathTraceWork { * - Copies work's render buffer to its device. */ void copy_from_render_buffers(const RenderBuffers *render_buffers); - /* Special version of the `copy_from_render_buffers()` which only copies denosied passes from the + /* Special version of the `copy_from_render_buffers()` which only copies denoised passes from the * given render buffers, leaving rest of the passes. * * Same notes about device copying applies to this call as well. */ diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h index b7b598fb10c..6ed368a2dc8 100644 --- a/intern/cycles/integrator/render_scheduler.h +++ b/intern/cycles/integrator/render_scheduler.h @@ -31,7 +31,7 @@ class RenderWork { int resolution_divider = 1; /* Initialize render buffers. - * Includes steps like zero-ing the buffer on the device, and optional reading of pixels from the + * Includes steps like zeroing the buffer on the device, and optional reading of pixels from the * baking target. */ bool init_render_buffers = false; diff --git a/intern/cycles/render/gpu_display.h b/intern/cycles/render/gpu_display.h index a01348d28d5..0340e0b7e45 100644 --- a/intern/cycles/render/gpu_display.h +++ b/intern/cycles/render/gpu_display.h @@ -46,7 +46,7 @@ class GPUDisplayParams { * NOTE: Is not affected by the resolution divider. */ int2 full_size = make_int2(0, 0); - /* Effective vieport size. + /* Effective viewport size. * In the case of border render, size of the border rectangle. * * NOTE: Is not affected by the resolution divider. */ diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 976b26a1f3a..debdf44b0bb 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -738,8 +738,8 @@ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, sub_v3_v3v3(vec1, &gps->points[start_i].x, &gps->points[start_i + dir_i].x); /* In general curvature = 1/radius. For the case without the - * weights introduced by #segment_influence, the calculation is - * curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length */ + * weights introduced by #segment_influence, the calculation is: + * `curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length` */ float curvature = normalize_v3(total_angle) / overshoot_length; /* Compensate for the weights powf(added_len, segment_influence). */ curvature /= powf(overshoot_length / fminf(overshoot_parameter, (float)j), segment_influence); diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 467f7d4543e..72ccfffbc3c 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -237,7 +237,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0; int p1, p2, p3, p4, *index; const bool conv_polys = ( - /* 2d polys are filled with DL_INDEX3 displists */ + /* 2D polys are filled with #DispList.type == #DL_INDEX3. */ (CU_DO_2DFILL(cu) == false) || /* surf polys are never filled */ BKE_curve_type_get(cu) == OB_SURF); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index eee4aec7459..e76a9641811 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -113,7 +113,7 @@ typedef struct KnifeColors { uchar axis_extra[3]; } KnifeColors; -/* Knifetool Operator. */ +/* Knife-tool Operator. */ typedef struct KnifeVert { Object *ob; uint base_index; @@ -445,7 +445,7 @@ static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd) if (!compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) { float v1[3], v2[3]; - /* This is causing buggyness when prev.cage and curr.cage are too close together. */ + /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */ knifetool_raycast_planes(kcd, v1, v2); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -4100,7 +4100,7 @@ static void knifetool_init(bContext *C, kcd->axis_string[0] = ' '; kcd->axis_string[1] = '\0'; - /* Initialise num input handling for angle snapping. */ + /* Initialize number input handling for angle snapping. */ initNumInput(&kcd->num); kcd->num.idx_max = 0; kcd->num.val_flag[0] |= NUM_NO_NEGATIVE; @@ -4151,7 +4151,7 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd) MEM_freeN(kcd->cagecos); knife_bvh_free(kcd); - /* Linehits cleanup. */ + /* Line-hits cleanup. */ if (kcd->linehits) { MEM_freeN(kcd->linehits); } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 39aed131ea1..07f1f8a753c 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2082,7 +2082,7 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, /** * \param has_bones: When true, skip non-bone hits, also allow bases to be used * that are visible but not select-able, - * since you may be in pose mode with an unselect-able object. + * since you may be in pose mode with an un-selectable object. * * \return the active base or NULL. */ diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index d5d13ea269f..2744c0c5e17 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -461,7 +461,7 @@ void GPU_texture_generate_mipmap(GPUTexture *tex) reinterpret_cast(tex)->generate_mipmap(); } -/* Copy a texture content to a similar texture. Only Mip 0 is copied. */ +/* Copy a texture content to a similar texture. Only MIP 0 is copied. */ void GPU_texture_copy(GPUTexture *dst_, GPUTexture *src_) { Texture *src = reinterpret_cast(src_); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 09567eca17f..321c7da3765 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -674,7 +674,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name); item->append_action = WM_APPEND_ACT_KEEP_LINKED; } - /* Only re-use existing local ID for indirectly linked data, the ID explicitely selected by the + /* Only re-use existing local ID for indirectly linked data, the ID explicitly selected by the * user we always fully append. */ else if (do_reuse_existing_id && existing_local_id != NULL && (item->append_tag & WM_APPEND_TAG_INDIRECT) != 0) { From a351023bd50c19f09e9785245d029f6e57810aa9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 23 Sep 2021 15:54:32 +1000 Subject: [PATCH 0123/1500] Fix default surface resolution U/V mis-match The resolution for surfaces was 12 for U, 4 for V, where both should have been set to 4. Regression in 9a076dd95a01135ea50f9ccc675668db9f2155f4 --- source/blender/blenkernel/intern/curve.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index b0d196b2bb0..0dcfea78ca5 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -404,6 +404,7 @@ void BKE_curve_init(Curve *cu, const short curve_type) } else if (cu->type == OB_SURF) { cu->flag |= CU_3D; + cu->resolu = 4; cu->resolv = 4; } cu->bevel_profile = NULL; From 12924ed573c9c4faf025a7cd2f61c53732667ad5 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 23 Sep 2021 10:41:06 +0200 Subject: [PATCH 0124/1500] Fix last Cycles tile highlighted while file from disk is being processed --- intern/cycles/blender/blender_session.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index d65d89a7ddd..33092129ddf 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -557,6 +557,11 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) /* free result without merging */ b_engine.end_result(b_rr, true, false, false); + /* When tiled rendering is used there will be no "write" done for the tile. Forcefully clear + * highlighted tiles now, so that the highlight will be removed while processing full frame from + * file. */ + b_engine.tile_highlight_clear_all(); + double total_time, render_time; session->progress.get_time(total_time, render_time); VLOG(1) << "Total render time: " << total_time; From 3042994c91667f9c8a1ecadc11e69c012c33d581 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 23 Sep 2021 10:43:31 +0200 Subject: [PATCH 0125/1500] Link/Append: Refactor flags. Flags controlling link/append code are split between two enums, one in `DNA_space_types.h` and one in `BLO_readfile.h`. This commit: - Moves flags exclusively used in WM and BLO code to `eBLOLibLinkFlags` in `BLO_readfile.h`. Flags in `eFileSel_Params_Flag` from `DNA_space_types.h` are now only the ones effectively used by the file browser editor code too. - Fixes some internal utils in `readfile.c` still taking `short` flag parameter instead of proper `int` one. NOTE: there are a few other flags that could probably be moved to `eBLOLibLinkFlags` (at the very least `FILE_LINK`, probably also `FILE_AUTOSELECT` and `FILE_ACTIVE_COLLECTION`), since those are not effectively used by the file browser, and control linking/appending behavior, not filebrowser behavior. However for now think it's safer to not touch that. --- source/blender/blenloader/BLO_readfile.h | 8 +++++ source/blender/blenloader/intern/readfile.c | 12 ++++---- .../blenloader/intern/versioning_280.c | 4 +-- .../blenloader/intern/versioning_300.c | 19 ++++++++++++ source/blender/makesdna/DNA_space_types.h | 8 ++--- .../windowmanager/intern/wm_files_link.c | 29 +++++++++---------- 6 files changed, 53 insertions(+), 27 deletions(-) diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 4b7f29dd7dc..1f0203b45e9 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -209,6 +209,14 @@ typedef enum eBLOLibLinkFlags { * don't need to remember to set this flag. */ BLO_LIBLINK_NEEDS_ID_TAG_DOIT = 1 << 18, + /** Set fake user on appended IDs. */ + BLO_LIBLINK_APPEND_SET_FAKEUSER = 1 << 19, + /** Append (make local) also indirect dependencies of appendeds IDs. */ + BLO_LIBLINK_APPEND_RECURSIVE = 1 << 20, + /** Instantiate object data IDs (i.e. create objects for them if needed). */ + BLO_LIBLINK_OBDATA_INSTANCE = 1 << 21, + /** Instantiate collections as empties, instead of linking them into current view layer. */ + BLO_LIBLINK_COLLECTION_INSTANCE = 1 << 22, } eBLOLibLinkFlags; /** diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 3cda1e613f8..cdae043d01c 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4496,7 +4496,7 @@ static void add_loose_objects_to_scene(Main *mainvar, ViewLayer *view_layer, const View3D *v3d, Library *lib, - const short flag) + const int flag) { Collection *active_collection = NULL; const bool do_append = (flag & FILE_LINK) == 0; @@ -4556,9 +4556,9 @@ static void add_loose_object_data_to_scene(Main *mainvar, Scene *scene, ViewLayer *view_layer, const View3D *v3d, - const short flag) + const int flag) { - if ((flag & FILE_OBDATA_INSTANCE) == 0) { + if ((flag & BLO_LIBLINK_OBDATA_INSTANCE) == 0) { return; } @@ -4617,7 +4617,7 @@ static void add_collections_to_scene(Main *mainvar, ViewLayer *view_layer, const View3D *v3d, Library *lib, - const short flag) + const int flag) { Collection *active_collection = scene->master_collection; if (flag & FILE_ACTIVE_COLLECTION) { @@ -4627,7 +4627,7 @@ static void add_collections_to_scene(Main *mainvar, /* Give all objects which are tagged a base. */ LISTBASE_FOREACH (Collection *, collection, &mainvar->collections) { - if ((flag & FILE_COLLECTION_INSTANCE) && (collection->id.tag & LIB_TAG_DOIT)) { + if ((flag & BLO_LIBLINK_COLLECTION_INSTANCE) && (collection->id.tag & LIB_TAG_DOIT)) { /* Any indirect collection should not have been tagged. */ BLI_assert((collection->id.tag & LIB_TAG_INDIRECT) == 0); @@ -4830,7 +4830,7 @@ static bool library_link_idcode_needs_tag_check(const short idcode, const int fl if (ELEM(idcode, ID_OB, ID_GR)) { return true; } - if (flag & FILE_OBDATA_INSTANCE) { + if (flag & BLO_LIBLINK_OBDATA_INSTANCE) { if (OB_DATA_SUPPORT_ID(idcode)) { return true; } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 69b67460a5d..292ca726b6f 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3416,8 +3416,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) case SPACE_FILE: { SpaceFile *sfile = (SpaceFile *)sl; if (sfile->params) { - sfile->params->flag &= ~(FILE_APPEND_SET_FAKEUSER | FILE_APPEND_RECURSIVE | - FILE_OBDATA_INSTANCE); + sfile->params->flag &= ~(FILE_PARAMS_FLAG_UNUSED_1 | FILE_PARAMS_FLAG_UNUSED_2 | + FILE_PARAMS_FLAG_UNUSED_3); } break; } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 58265bca238..9908e231452 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1438,5 +1438,24 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + switch (sl->spacetype) { + case SPACE_FILE: { + SpaceFile *sfile = (SpaceFile *)sl; + if (sfile->params) { + sfile->params->flag &= ~(FILE_PARAMS_FLAG_UNUSED_1 | FILE_PARAMS_FLAG_UNUSED_2 | + FILE_PARAMS_FLAG_UNUSED_3 | FILE_PARAMS_FLAG_UNUSED_4); + } + break; + } + default: + break; + } + } + } + } } } diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index e849039fa93..cebe8fc61fe 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -984,17 +984,17 @@ typedef enum eFileSel_Action { * (WM and BLO code area, see #eBLOLibLinkFlags in BLO_readfile.h). */ typedef enum eFileSel_Params_Flag { - FILE_APPEND_SET_FAKEUSER = (1 << 0), + FILE_PARAMS_FLAG_UNUSED_1 = (1 << 0), FILE_RELPATH = (1 << 1), FILE_LINK = (1 << 2), FILE_HIDE_DOT = (1 << 3), FILE_AUTOSELECT = (1 << 4), FILE_ACTIVE_COLLECTION = (1 << 5), - FILE_APPEND_RECURSIVE = (1 << 6), + FILE_PARAMS_FLAG_UNUSED_2 = (1 << 6), FILE_DIRSEL_ONLY = (1 << 7), FILE_FILTER = (1 << 8), - FILE_OBDATA_INSTANCE = (1 << 9), - FILE_COLLECTION_INSTANCE = (1 << 10), + FILE_PARAMS_FLAG_UNUSED_3 = (1 << 9), + FILE_PARAMS_FLAG_UNUSED_4 = (1 << 10), FILE_SORT_INVERT = (1 << 11), FILE_HIDE_TOOL_PROPS = (1 << 12), FILE_CHECK_EXISTING = (1 << 13), diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 321c7da3765..820a5990479 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -127,10 +127,10 @@ static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNU return OPERATOR_RUNNING_MODAL; } -static short wm_link_append_flag(wmOperator *op) +static int wm_link_append_flag(wmOperator *op) { PropertyRNA *prop; - short flag = 0; + int flag = 0; if (RNA_boolean_get(op->ptr, "autoselect")) { flag |= FILE_AUTOSELECT; @@ -147,17 +147,17 @@ static short wm_link_append_flag(wmOperator *op) } else { if (RNA_boolean_get(op->ptr, "use_recursive")) { - flag |= FILE_APPEND_RECURSIVE; + flag |= BLO_LIBLINK_APPEND_RECURSIVE; } if (RNA_boolean_get(op->ptr, "set_fake")) { - flag |= FILE_APPEND_SET_FAKEUSER; + flag |= BLO_LIBLINK_APPEND_SET_FAKEUSER; } } if (RNA_boolean_get(op->ptr, "instance_collections")) { - flag |= FILE_COLLECTION_INSTANCE; + flag |= BLO_LIBLINK_COLLECTION_INSTANCE; } if (RNA_boolean_get(op->ptr, "instance_object_data")) { - flag |= FILE_OBDATA_INSTANCE; + flag |= BLO_LIBLINK_OBDATA_INSTANCE; } return flag; @@ -396,7 +396,7 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, LinkNode *itemlink; Collection *active_collection = NULL; - const bool do_obdata = (lapp_data->flag & FILE_OBDATA_INSTANCE) != 0; + const bool do_obdata = (lapp_data->flag & BLO_LIBLINK_OBDATA_INSTANCE) != 0; const bool object_set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0; /* Do NOT make base active here! screws up GUI stuff, @@ -472,7 +472,7 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, /* In case user requested instantiation of collections as empties, we do so for the one they * explicitly selected (originally directly linked IDs). */ - if ((lapp_data->flag & FILE_COLLECTION_INSTANCE) != 0 && + if ((lapp_data->flag & BLO_LIBLINK_COLLECTION_INSTANCE) != 0 && (item->append_tag & WM_APPEND_TAG_INDIRECT) == 0) { /* BKE_object_add(...) messes with the selection. */ Object *ob = BKE_object_add_only_object(bmain, OB_EMPTY, collection->id.name + 2); @@ -626,8 +626,8 @@ static void wm_append_do(WMLinkAppendData *lapp_data, { BLI_assert((lapp_data->flag & FILE_LINK) == 0); - const bool do_recursive = (lapp_data->flag & FILE_APPEND_RECURSIVE) != 0; - const bool set_fakeuser = (lapp_data->flag & FILE_APPEND_SET_FAKEUSER) != 0; + const bool do_recursive = (lapp_data->flag & BLO_LIBLINK_APPEND_RECURSIVE) != 0; + const bool set_fakeuser = (lapp_data->flag & BLO_LIBLINK_APPEND_SET_FAKEUSER) != 0; LinkNode *itemlink; @@ -1057,7 +1057,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - short flag = wm_link_append_flag(op); + int flag = wm_link_append_flag(op); const bool do_append = (flag & FILE_LINK) == 0; /* sanity checks for flag */ @@ -1066,12 +1066,10 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) RPT_WARNING, "Scene '%s' is linked, instantiation of objects is disabled", scene->id.name + 2); - flag &= ~(FILE_COLLECTION_INSTANCE | FILE_OBDATA_INSTANCE); + flag &= ~(BLO_LIBLINK_COLLECTION_INSTANCE | BLO_LIBLINK_OBDATA_INSTANCE); scene = NULL; } - /* We need to add nothing from #eBLOLibLinkFlags to flag here. */ - /* from here down, no error returns */ if (view_layer && RNA_boolean_get(op->ptr, "autoselect")) { @@ -1305,7 +1303,8 @@ static ID *wm_file_link_append_datablock_ex(Main *bmain, BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); /* Define working data, with just the one item we want to link. */ - WMLinkAppendData *lapp_data = wm_link_append_data_new(do_append ? FILE_APPEND_RECURSIVE : 0); + WMLinkAppendData *lapp_data = wm_link_append_data_new(do_append ? BLO_LIBLINK_APPEND_RECURSIVE : + 0); wm_link_append_data_library_add(lapp_data, filepath); WMLinkAppendDataItem *item = wm_link_append_data_item_add(lapp_data, id_name, id_code, NULL); From b801e86f8b6719cd902707a95dbf0dd1576e061b Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 23 Sep 2021 11:15:58 +0200 Subject: [PATCH 0126/1500] Append: Reuse local data: remove limitation on directly selected data. in asset context, when user drag & drop a material several time, they would still expect to re-use existing one instead of getting new copies of it, even if this material is directly appended (and not an indirect dependency of an object e.g.). --- source/blender/windowmanager/intern/wm_files_link.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 820a5990479..aa23f3523d3 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -674,10 +674,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name); item->append_action = WM_APPEND_ACT_KEEP_LINKED; } - /* Only re-use existing local ID for indirectly linked data, the ID explicitly selected by the - * user we always fully append. */ - else if (do_reuse_existing_id && existing_local_id != NULL && - (item->append_tag & WM_APPEND_TAG_INDIRECT) != 0) { + else if (do_reuse_existing_id && existing_local_id != NULL) { CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name); item->append_action = WM_APPEND_ACT_REUSE_LOCAL; item->customdata = existing_local_id; From b63f777950239518f6bbeeaec3d01d6cb03068f1 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 23 Sep 2021 11:28:39 +0200 Subject: [PATCH 0127/1500] Add missing GPencil `IDTYPE_FLAGS_APPEND_IS_REUSABLE` flag. Forgot that one in rB794c2828af60. Noted by Antonio Vazquez (@antoniov), thanks. --- source/blender/blenkernel/intern/gpencil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 82a44afbbb1..ed84694a919 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -319,7 +319,7 @@ IDTypeInfo IDType_ID_GD = { .name = "GPencil", .name_plural = "grease_pencils", .translation_context = BLT_I18NCONTEXT_ID_GPENCIL, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = NULL, .copy_data = greasepencil_copy_data, From 3558ae3b6c0859acef806f05222fb6b577db7855 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 23 Sep 2021 12:30:07 +0200 Subject: [PATCH 0128/1500] Cleanup: Silence unused var warning in release builds. --- source/blender/blenkernel/intern/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 26dcadcc77b..9c3291edbcc 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -485,6 +485,7 @@ void BKE_main_library_weak_reference_add_item(GHash *library_weak_reference_mapp const bool already_exist_in_mapping = BLI_ghash_ensure_p( library_weak_reference_mapping, key, &id_p); BLI_assert(!already_exist_in_mapping); + UNUSED_VARS_NDEBUG(already_exist_in_mapping); BLI_strncpy(new_id->library_weak_reference->library_filepath, library_filepath, From ce96a75c2cb17c255226c8b56f85231ce0578811 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 23 Sep 2021 12:45:40 +0200 Subject: [PATCH 0129/1500] Cleanup: eBLOLibLinkFlags: Add 'space' between `APPEND` flags and `INSTANCE` ones. --- source/blender/blenloader/BLO_readfile.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 1f0203b45e9..c3a57f17e8b 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -214,9 +214,9 @@ typedef enum eBLOLibLinkFlags { /** Append (make local) also indirect dependencies of appendeds IDs. */ BLO_LIBLINK_APPEND_RECURSIVE = 1 << 20, /** Instantiate object data IDs (i.e. create objects for them if needed). */ - BLO_LIBLINK_OBDATA_INSTANCE = 1 << 21, + BLO_LIBLINK_OBDATA_INSTANCE = 1 << 24, /** Instantiate collections as empties, instead of linking them into current view layer. */ - BLO_LIBLINK_COLLECTION_INSTANCE = 1 << 22, + BLO_LIBLINK_COLLECTION_INSTANCE = 1 << 25, } eBLOLibLinkFlags; /** From 6f53988e7afbedddc2c442b49622860ebee41bcc Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 23 Sep 2021 12:46:51 +0200 Subject: [PATCH 0130/1500] Cleanup: proper handling of `LIB_TAG_DOIT` in append code. --- source/blender/windowmanager/intern/wm_files_link.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index aa23f3523d3..817947f2161 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -573,6 +573,8 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, ob, object_set_selected, object_set_active, view_layer, v3d); copy_v3_v3(ob->loc, scene->cursor.location); + + id->tag &= ~LIB_TAG_DOIT; } } @@ -656,8 +658,8 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } BLI_assert(item->customdata == NULL); - /* Clear tag previously used to mark IDs needing post-processing (instantiation of loose - * objects etc.). */ + /* In Append case linked IDs should never be marked as needing post-processing (instantiation + * of loose objects etc.). */ BLI_assert((id->tag & LIB_TAG_DOIT) == 0); ID *existing_local_id = BKE_idtype_idcode_append_is_reusable(GS(id->name)) ? From cb173d05dc3e17f0407ebdeb7d092344242ea4ad Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 23 Sep 2021 12:54:23 +0200 Subject: [PATCH 0131/1500] LibLink Append: Fix 'reused ID' case keeping linked data around. When we re-use a local ID, we need to delete the matching linked data. --- .../windowmanager/intern/wm_files_link.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 817947f2161..b61337ec8e4 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -805,6 +805,27 @@ static void wm_append_do(WMLinkAppendData *lapp_data, BKE_libblock_relink_to_newid_new(bmain, id); } + /* Remove linked IDs when a local existing data has been reused instead. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + + if (item->append_action != WM_APPEND_ACT_REUSE_LOCAL) { + continue; + } + + ID *id = item->new_id; + if (id == NULL) { + continue; + } + BLI_assert(ID_IS_LINKED(id)); + BLI_assert(id->newid != NULL); + BLI_assert((id->tag & LIB_TAG_DOIT) == 0); + + id->tag |= LIB_TAG_DOIT; + item->new_id = id->newid; + } + BKE_id_multi_tagged_delete(bmain); + /* Instantiate newly created (duplicated) IDs as needed. */ wm_append_loose_data_instantiate(lapp_data, bmain, scene, view_layer, v3d); From f48a4aa0f9157c1338a190d5d1b907cfc7d3da10 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 23 Sep 2021 12:56:05 +0200 Subject: [PATCH 0132/1500] LibLink Append: Expose 'reuse ID' through new BLO flag, and add basic tests. Option is now available to append operator, alsthough hidden and disabled by default. --- source/blender/blenloader/BLO_readfile.h | 2 + .../windowmanager/intern/wm_files_link.c | 18 ++++- tests/python/bl_blendfile_liblink.py | 76 +++++++++++++++++-- 3 files changed, 88 insertions(+), 8 deletions(-) diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index c3a57f17e8b..3f3e61734ec 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -213,6 +213,8 @@ typedef enum eBLOLibLinkFlags { BLO_LIBLINK_APPEND_SET_FAKEUSER = 1 << 19, /** Append (make local) also indirect dependencies of appendeds IDs. */ BLO_LIBLINK_APPEND_RECURSIVE = 1 << 20, + /** Try to re-use previously appended matching ID on new append. */ + BLO_LIBLINK_APPEND_LOCAL_ID_REUSE = 1 << 21, /** Instantiate object data IDs (i.e. create objects for them if needed). */ BLO_LIBLINK_OBDATA_INSTANCE = 1 << 24, /** Instantiate collections as empties, instead of linking them into current view layer. */ diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index b61337ec8e4..2f34ee3db3c 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -152,6 +152,9 @@ static int wm_link_append_flag(wmOperator *op) if (RNA_boolean_get(op->ptr, "set_fake")) { flag |= BLO_LIBLINK_APPEND_SET_FAKEUSER; } + if (RNA_boolean_get(op->ptr, "do_reuse_local_id")) { + flag |= BLO_LIBLINK_APPEND_LOCAL_ID_REUSE; + } } if (RNA_boolean_get(op->ptr, "instance_collections")) { flag |= BLO_LIBLINK_COLLECTION_INSTANCE; @@ -630,6 +633,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, const bool do_recursive = (lapp_data->flag & BLO_LIBLINK_APPEND_RECURSIVE) != 0; const bool set_fakeuser = (lapp_data->flag & BLO_LIBLINK_APPEND_SET_FAKEUSER) != 0; + const bool do_reuse_local_id = (lapp_data->flag & BLO_LIBLINK_APPEND_LOCAL_ID_REUSE) != 0; LinkNode *itemlink; @@ -644,7 +648,6 @@ static void wm_append_do(WMLinkAppendData *lapp_data, BLI_ghash_insert(lapp_data->new_id_to_item, id, item); } - const bool do_reuse_existing_id = false; lapp_data->library_weak_reference_mapping = BKE_main_library_weak_reference_create(bmain); /* NOTE: Since we append items for IDs not already listed (i.e. implicitly linked indirect @@ -676,7 +679,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name); item->append_action = WM_APPEND_ACT_KEEP_LINKED; } - else if (do_reuse_existing_id && existing_local_id != NULL) { + else if (do_reuse_local_id && existing_local_id != NULL) { CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name); item->append_action = WM_APPEND_ACT_REUSE_LOCAL; item->customdata = existing_local_id; @@ -1219,14 +1222,25 @@ static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link) prop = RNA_def_boolean( ot->srna, "link", is_link, "Link", "Link the objects or data-blocks rather than appending"); RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + + prop = RNA_def_boolean( + ot->srna, + "do_reuse_local_id", + false, + "Re-Use Local Data", + "Try to re-use previously matching appended data-blocks instead of appending a new copy"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "autoselect", true, "Select", "Select new objects"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "active_collection", true, "Active Collection", "Put new objects on the active collection"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean( ot->srna, "instance_collections", diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index 992bf6b89d9..4186ba58817 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -165,7 +165,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Mesh") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=False, set_fake=False, use_recursive=False) + instance_object_data=False, set_fake=False, use_recursive=False, do_reuse_local_id=False) assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) @@ -179,7 +179,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Mesh") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=True, set_fake=False, use_recursive=False) + instance_object_data=True, set_fake=False, use_recursive=False, do_reuse_local_id=False) assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) @@ -194,7 +194,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Mesh") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=False, set_fake=True, use_recursive=False) + instance_object_data=False, set_fake=True, use_recursive=False, do_reuse_local_id=False) assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) @@ -208,7 +208,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Object") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=False, set_fake=False, use_recursive=False) + instance_object_data=False, set_fake=False, use_recursive=False, do_reuse_local_id=False) assert(len(bpy.data.meshes) == 1) # This one fails currently, for unclear reasons. @@ -224,7 +224,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Object") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=False, set_fake=False, use_recursive=True) + instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False) assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) @@ -239,7 +239,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Collection") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=False, set_fake=False, use_recursive=True) + instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False) assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].users == 1) @@ -251,9 +251,73 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert(bpy.data.collections[0].users == 1) +class TestBlendLibAppendReuseID(TestBlendLibLinkHelper): + + def __init__(self, args): + self.args = args + + def test_append(self): + output_dir = self.args.output_dir + output_lib_path = self.init_lib_data_basic() + + # Append of a single Object, and then append it again. + self.reset_blender() + + link_dir = os.path.join(output_lib_path, "Object") + bpy.ops.wm.append(directory=link_dir, filename="LibMesh", + instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False) + + assert(len(bpy.data.meshes) == 1) + assert(bpy.data.meshes[0].library is None) + assert(bpy.data.meshes[0].use_fake_user is False) + assert(bpy.data.meshes[0].users == 1) + assert(bpy.data.meshes[0].library_weak_reference is not None) + assert(bpy.data.meshes[0].library_weak_reference.filepath == output_lib_path) + assert(bpy.data.meshes[0].library_weak_reference.id_name == "MELibMesh") + assert(len(bpy.data.objects) == 1) + for ob in bpy.data.objects: + assert(ob.library is None) + assert(ob.library_weak_reference is None) + assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here + + bpy.ops.wm.append(directory=link_dir, filename="LibMesh", + instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=True) + + assert(len(bpy.data.meshes) == 1) + assert(bpy.data.meshes[0].library is None) + assert(bpy.data.meshes[0].use_fake_user is False) + assert(bpy.data.meshes[0].users == 2) + assert(bpy.data.meshes[0].library_weak_reference is not None) + assert(bpy.data.meshes[0].library_weak_reference.filepath == output_lib_path) + assert(bpy.data.meshes[0].library_weak_reference.id_name == "MELibMesh") + assert(len(bpy.data.objects) == 2) + for ob in bpy.data.objects: + assert(ob.library is None) + assert(ob.library_weak_reference is None) + assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here + + bpy.ops.wm.append(directory=link_dir, filename="LibMesh", + instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False) + + assert(len(bpy.data.meshes) == 2) + assert(bpy.data.meshes[0].library_weak_reference is None) + assert(bpy.data.meshes[1].library is None) + assert(bpy.data.meshes[1].use_fake_user is False) + assert(bpy.data.meshes[1].users == 1) + assert(bpy.data.meshes[1].library_weak_reference is not None) + assert(bpy.data.meshes[1].library_weak_reference.filepath == output_lib_path) + assert(bpy.data.meshes[1].library_weak_reference.id_name == "MELibMesh") + assert(len(bpy.data.objects) == 3) + for ob in bpy.data.objects: + assert(ob.library is None) + assert(ob.library_weak_reference is None) + assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here + + TESTS = ( TestBlendLibLinkSaveLoadBasic, TestBlendLibAppendBasic, + TestBlendLibAppendReuseID, ) From e86cf55667a74c469c325f1bae1868ac963fb87d Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 23 Sep 2021 13:03:01 +0200 Subject: [PATCH 0133/1500] Fix T91629: Crash in "Open Cached Render" function --- source/blender/render/intern/render_result.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index c308147fc5b..6bd6521d803 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -1180,6 +1180,7 @@ bool render_result_exr_file_cache_read(Render *re) RE_FreeRenderResult(re->result); re->result = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); + render_result_passes_allocated_ensure(re->result); /* First try cache. */ render_result_exr_file_cache_path(re->scene, root, str); From aadb7ef0718af9c54fa97d0ce10e3967a80cea2e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 23 Sep 2021 13:46:13 +0200 Subject: [PATCH 0134/1500] Geometry Nodes: add utility to print an attribute id. --- source/blender/blenkernel/BKE_attribute_access.hh | 2 ++ .../blender/blenkernel/intern/attribute_access.cc | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index cf54e7efa0d..77db479f525 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -101,6 +101,8 @@ class AttributeIDRef { BLI_assert(this->is_anonymous()); return *anonymous_id_; } + + friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id); }; } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 8c4f87be91f..c2837b522c4 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -55,6 +55,21 @@ using blender::fn::GVArray_For_SingleValue; namespace blender::bke { +std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id) +{ + if (attribute_id.is_named()) { + stream << attribute_id.name(); + } + else if (attribute_id.is_anonymous()) { + const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); + stream << "<" << BKE_anonymous_attribute_id_debug_name(&anonymous_id) << ">"; + } + else { + stream << ""; + } + return stream; +} + const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) { switch (type) { From 4d2ca33a8a7d08897a273031099da9b7bcaa11ff Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 23 Sep 2021 13:46:55 +0200 Subject: [PATCH 0135/1500] LibLink: Modify WM API to link/append one ID to take flag parameter. There is no reason to lock behavior into a specific configuration in those calls, make them properly configurable like the rest of the link/append code. This also enable users of those functions to activate 'ID reuse' behavior. --- source/blender/editors/screen/workspace_edit.c | 9 ++++++++- source/blender/windowmanager/WM_api.h | 6 ++++-- .../blender/windowmanager/intern/wm_dragdrop.c | 7 +++++-- .../windowmanager/intern/wm_files_link.c | 18 +++++++++++------- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index b99cb831bee..4b81e713080 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -310,7 +310,14 @@ static int workspace_append_activate_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "filepath", filepath); WorkSpace *appended_workspace = (WorkSpace *)WM_file_append_datablock( - bmain, CTX_data_scene(C), CTX_data_view_layer(C), CTX_wm_view3d(C), filepath, ID_WS, idname); + bmain, + CTX_data_scene(C), + CTX_data_view_layer(C), + CTX_wm_view3d(C), + filepath, + ID_WS, + idname, + BLO_LIBLINK_APPEND_RECURSIVE); if (appended_workspace) { /* Set defaults. */ diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 6794b1f4091..c5482a729c3 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -212,14 +212,16 @@ struct ID *WM_file_link_datablock(struct Main *bmain, struct View3D *v3d, const char *filepath, const short id_code, - const char *id_name); + const char *id_name, + int flag); struct ID *WM_file_append_datablock(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct View3D *v3d, const char *filepath, const short id_code, - const char *id_name); + const char *id_name, + int flag); void WM_lib_reload(struct Library *lib, struct bContext *C, struct ReportList *reports); /* mouse cursors */ diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 6585349c83c..f3a57b72095 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -43,6 +43,8 @@ #include "BKE_idtype.h" #include "BKE_lib_id.h" +#include "BLO_readfile.h" + #include "GPU_shader.h" #include "GPU_state.h" #include "GPU_viewport.h" @@ -392,9 +394,10 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) switch ((eFileAssetImportType)asset_drag->import_type) { case FILE_ASSET_IMPORT_LINK: - return WM_file_link_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name); + return WM_file_link_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name, 0); case FILE_ASSET_IMPORT_APPEND: - return WM_file_append_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name); + return WM_file_append_datablock( + G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name, BLO_LIBLINK_APPEND_RECURSIVE); } BLI_assert_unreachable(); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 2f34ee3db3c..92335f28d94 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -1331,14 +1331,14 @@ static ID *wm_file_link_append_datablock_ex(Main *bmain, const char *filepath, const short id_code, const char *id_name, - const bool do_append) + const int flag) { + const bool do_append = (flag & FILE_LINK) == 0; /* Tag everything so we can make local only the new datablock. */ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); /* Define working data, with just the one item we want to link. */ - WMLinkAppendData *lapp_data = wm_link_append_data_new(do_append ? BLO_LIBLINK_APPEND_RECURSIVE : - 0); + WMLinkAppendData *lapp_data = wm_link_append_data_new(flag); wm_link_append_data_library_add(lapp_data, filepath); WMLinkAppendDataItem *item = wm_link_append_data_item_add(lapp_data, id_name, id_code, NULL); @@ -1371,10 +1371,12 @@ ID *WM_file_link_datablock(Main *bmain, View3D *v3d, const char *filepath, const short id_code, - const char *id_name) + const char *id_name, + int flag) { + flag |= FILE_LINK; return wm_file_link_append_datablock_ex( - bmain, scene, view_layer, v3d, filepath, id_code, id_name, false); + bmain, scene, view_layer, v3d, filepath, id_code, id_name, flag); } /* @@ -1387,10 +1389,12 @@ ID *WM_file_append_datablock(Main *bmain, View3D *v3d, const char *filepath, const short id_code, - const char *id_name) + const char *id_name, + int flag) { + BLI_assert((flag & FILE_LINK) == 0); ID *id = wm_file_link_append_datablock_ex( - bmain, scene, view_layer, v3d, filepath, id_code, id_name, true); + bmain, scene, view_layer, v3d, filepath, id_code, id_name, flag); return id; } From d431b91995dafc1eb2560fbd4a2eec3b2a92204b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 23 Sep 2021 22:06:47 +1000 Subject: [PATCH 0136/1500] Cleanup: assign variable for LMB select --- release/scripts/presets/keyconfig/Blender.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index 15cc6097979..8a937b4d4b1 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -250,6 +250,8 @@ def load(): kc = context.window_manager.keyconfigs.new(IDNAME) kc_prefs = kc.preferences + is_select_left = (kc_prefs.select_mouse == 'LEFT') + keyconfig_data = blender_default.generate_keymaps( blender_default.Params( select_mouse=kc_prefs.select_mouse, @@ -265,12 +267,9 @@ def load(): use_select_all_toggle=kc_prefs.use_select_all_toggle, use_v3d_tab_menu=kc_prefs.use_v3d_tab_menu, use_v3d_shade_ex_pie=kc_prefs.use_v3d_shade_ex_pie, - use_gizmo_drag=( - kc_prefs.select_mouse == 'LEFT' and - kc_prefs.gizmo_action == 'DRAG' - ), - use_fallback_tool=(True if (kc_prefs.select_mouse == 'LEFT') else (kc_prefs.rmb_action == 'FALLBACK_TOOL')), - use_alt_tool=(kc_prefs.use_alt_tool and kc_prefs.select_mouse == 'LEFT'), + use_gizmo_drag=(is_select_left and kc_prefs.gizmo_action == 'DRAG'), + use_fallback_tool=(True if is_select_left else (kc_prefs.rmb_action == 'FALLBACK_TOOL')), + use_alt_tool=(kc_prefs.use_alt_tool and is_select_left), use_alt_click_leader=kc_prefs.use_alt_click_leader, use_pie_click_drag=kc_prefs.use_pie_click_drag, ), From b659d1a560410425b3454016eeead8dbae7a0898 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 23 Sep 2021 22:06:49 +1000 Subject: [PATCH 0137/1500] Cleanup: spelling in comments --- intern/cycles/integrator/path_trace.h | 2 +- intern/cycles/kernel/device/gpu/parallel_active_index.h | 2 +- intern/cycles/kernel/kernel_film.h | 2 +- intern/cycles/render/integrator.h | 2 +- intern/cycles/util/util_math_intersect.h | 2 +- source/blender/blenkernel/intern/gpencil_modifier.c | 4 ++-- source/blender/blenkernel/intern/modifier.c | 2 +- source/blender/blenlib/tests/BLI_mesh_intersect_test.cc | 6 +++--- source/blender/blenloader/BLO_readfile.h | 2 +- source/blender/imbuf/intern/IMB_indexer.h | 2 +- source/blender/imbuf/intern/radiance_hdr.c | 4 ++-- source/blender/imbuf/intern/targa.c | 8 ++++---- source/blender/python/intern/bpy_interface.c | 2 +- source/blender/python/mathutils/mathutils_geometry.c | 2 +- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h index fc7713e6df9..f507c2d7e0a 100644 --- a/intern/cycles/integrator/path_trace.h +++ b/intern/cycles/integrator/path_trace.h @@ -286,7 +286,7 @@ class PathTrace { /* Parameters of the big tile with the current resolution divider applied. */ BufferParams effective_big_tile_params; - /* Denosier was run and there are denoised versions of the passes in the render buffers. */ + /* Denoiser was run and there are denoised versions of the passes in the render buffers. */ bool has_denoised_result = false; /* Current tile has been written (to either disk or callback. diff --git a/intern/cycles/kernel/device/gpu/parallel_active_index.h b/intern/cycles/kernel/device/gpu/parallel_active_index.h index 85500bf4d07..a68d1d80c7d 100644 --- a/intern/cycles/kernel/device/gpu/parallel_active_index.h +++ b/intern/cycles/kernel/device/gpu/parallel_active_index.h @@ -21,7 +21,7 @@ CCL_NAMESPACE_BEGIN /* Given an array of states, build an array of indices for which the states * are active. * - * Shared memory requirement is sizeof(int) * (number_of_warps + 1) */ + * Shared memory requirement is `sizeof(int) * (number_of_warps + 1)`. */ #include "util/util_atomic.h" diff --git a/intern/cycles/kernel/kernel_film.h b/intern/cycles/kernel/kernel_film.h index 715d764fb31..e8f4a21878e 100644 --- a/intern/cycles/kernel/kernel_film.h +++ b/intern/cycles/kernel/kernel_film.h @@ -394,7 +394,7 @@ film_calculate_shadow_catcher(const KernelFilmConvert *ccl_restrict kfilm_conver /* NOTE: It is possible that the Shadow Catcher pass is requested as an output without actual * shadow catcher objects in the scene. In this case there will be no auxiliary passes required - * for the devision (to save up memory). So delay the asserts to this point so that the number of + * for the decision (to save up memory). So delay the asserts to this point so that the number of * samples check handles such configuration. */ kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); kernel_assert(kfilm_convert->pass_combined != PASS_UNUSED); diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h index 32e108d62ca..5ad419e02ca 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -19,7 +19,7 @@ #include "kernel/kernel_types.h" -#include "device/device_denoise.h" /* For the paramaters and type enum. */ +#include "device/device_denoise.h" /* For the parameters and type enum. */ #include "graph/node.h" #include "integrator/adaptive_sampling.h" diff --git a/intern/cycles/util/util_math_intersect.h b/intern/cycles/util/util_math_intersect.h index fa3a541eea9..fd0c9124345 100644 --- a/intern/cycles/util/util_math_intersect.h +++ b/intern/cycles/util/util_math_intersect.h @@ -40,7 +40,7 @@ ccl_device bool ray_sphere_intersect(float3 ray_P, /* Ray points away from sphere. */ return false; } - const float dsq = tsq - tp * tp; /* pythagoras */ + const float dsq = tsq - tp * tp; /* Pythagoras. */ if (dsq > radiussq) { /* Closest point on ray outside sphere. */ return false; diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 6be03bffb3c..de459c2e414 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -329,7 +329,7 @@ void BKE_gpencil_modifier_init(void) #if 0 /* Note that GPencil actually does not support these atm, but might do in the future. */ - /* Initialize global cmmon storage used for virtual modifier list */ + /* Initialize global common storage used for virtual modifier list. */ GpencilModifierData *md; md = BKE_gpencil_modifier_new(eGpencilModifierType_Armature); virtualModifierCommonData.amd = *((ArmatureGpencilModifierData *)md); @@ -518,7 +518,7 @@ static void gpencil_modifier_copy_data_id_us_cb(void *UNUSED(userData), * Copy grease pencil modifier data. * \param md: Source modifier data * \param target: Target modifier data - * \parm flag: Flags + * \param flag: Flags */ void BKE_gpencil_modifier_copydata_ex(GpencilModifierData *md, GpencilModifierData *target, diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index b55b02c7bf2..6f6cf12f023 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -100,7 +100,7 @@ void BKE_modifier_init(void) /* Initialize modifier types */ modifier_type_init(modifier_types); /* MOD_utils.c */ - /* Initialize global cmmon storage used for virtual modifier list */ + /* Initialize global common storage used for virtual modifier list. */ md = BKE_modifier_new(eModifierType_Armature); virtualModifierCommonData.amd = *((ArmatureModifierData *)md); BKE_modifier_free(md); diff --git a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc index 0329fc156c0..68111fb8eb1 100644 --- a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc +++ b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc @@ -457,8 +457,8 @@ TEST(mesh_intersect, TwoTris) {4, 11, 6, 4}, /* 9: T11 edge (-1,1,1)(0,1/2,1/2) inside T4 edge. */ {4, 12, 6, 2}, /* 10: parallel planes, not intersecting. */ {4, 13, 6, 2}, /* 11: non-parallel planes, not intersecting, all one side. */ - {0, 14, 6, 2}, /* 12: non-paralel planes, not intersecting, alternate sides. */ - /* Following are all coplanar cases. */ + {0, 14, 6, 2}, /* 12: non-parallel planes, not intersecting, alternate sides. */ + /* Following are all co-planar cases. */ {15, 16, 6, 8}, /* 13: T16 inside T15. NOTE: dup'd tri is expected. */ {15, 17, 8, 8}, /* 14: T17 intersects one edge of T15 at (1,1,0)(3,3,0). */ {15, 18, 10, 12}, /* 15: T18 intersects T15 at (1,1,0)(3,3,0)(3,15/4,1/2)(0,3,2). */ @@ -970,7 +970,7 @@ static void fill_sphere_data(int nrings, static void spheresphere_test(int nrings, double y_offset, bool use_self) { - /* Make two uvspheres with nrings rings ad 2*nrings segments. */ + /* Make two UV-spheres with nrings rings ad 2*nrings segments. */ if (nrings < 2) { return; } diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 3f3e61734ec..c8615545df9 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -211,7 +211,7 @@ typedef enum eBLOLibLinkFlags { BLO_LIBLINK_NEEDS_ID_TAG_DOIT = 1 << 18, /** Set fake user on appended IDs. */ BLO_LIBLINK_APPEND_SET_FAKEUSER = 1 << 19, - /** Append (make local) also indirect dependencies of appendeds IDs. */ + /** Append (make local) also indirect dependencies of appended IDs. */ BLO_LIBLINK_APPEND_RECURSIVE = 1 << 20, /** Try to re-use previously appended matching ID on new append. */ BLO_LIBLINK_APPEND_LOCAL_ID_REUSE = 1 << 21, diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h index 37309ccc13a..6c66b11df4f 100644 --- a/source/blender/imbuf/intern/IMB_indexer.h +++ b/source/blender/imbuf/intern/IMB_indexer.h @@ -33,7 +33,7 @@ * a) different time-codes within one file (like DTS/PTS, Time-code-Track, * "implicit" time-codes within DV-files and HDV-files etc.) * b) seeking difficulties within FFMPEG for files with timestamp holes - * c) broken files that miss several frames / have varying framerates + * c) broken files that miss several frames / have varying frame-rates * d) use proxies accordingly * * ... we need index files, that provide us with diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c index 94b2a62aa26..7f4e4dd31df 100644 --- a/source/blender/imbuf/intern/radiance_hdr.c +++ b/source/blender/imbuf/intern/radiance_hdr.c @@ -294,7 +294,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem, break; } for (x = 0; x < width; x++) { - /* convert to ldr */ + /* Convert to LDR. */ RGBE2FLOAT(sline[x], fcol); *rect_float++ = fcol[RED]; *rect_float++ = fcol[GRN]; @@ -328,7 +328,7 @@ static int fwritecolrs( rgbe_scan = (RGBE *)MEM_mallocN(sizeof(RGBE) * width, "radhdr_write_tmpscan"); - /* convert scanline */ + /* Convert scan-line. */ for (size_t i = 0, j = 0; i < width; i++) { if (fpscan) { fcol[RED] = fpscan[j]; diff --git a/source/blender/imbuf/intern/targa.c b/source/blender/imbuf/intern/targa.c index 8ed0b8b535c..333e29e6d97 100644 --- a/source/blender/imbuf/intern/targa.c +++ b/source/blender/imbuf/intern/targa.c @@ -192,7 +192,7 @@ static bool makebody_tga(ImBuf *ibuf, FILE *file, int (*out)(unsigned int, FILE else { while (*rect++ == this) { /* seek for first different byte */ if (--bytes == 0) { - break; /* oor end of line */ + break; /* Or end of line. */ } } rect--; @@ -470,7 +470,7 @@ static void decodetarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem if (psize & 2) { if (psize & 1) { - /* order = bgra */ + /* Order = BGRA. */ cp[0] = mem[3]; cp[1] = mem[0]; cp[2] = mem[1]; @@ -512,7 +512,7 @@ static void decodetarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem while (count > 0) { if (psize & 2) { if (psize & 1) { - /* order = bgra */ + /* Order = BGRA. */ cp[0] = mem[3]; cp[1] = mem[0]; cp[2] = mem[1]; @@ -589,7 +589,7 @@ static void ldtarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem_siz if (psize & 2) { if (psize & 1) { - /* order = bgra */ + /* Order = BGRA. */ cp[0] = mem[3]; cp[1] = mem[0]; cp[2] = mem[1]; diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 7a93a076621..95affa9dba9 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -246,7 +246,7 @@ void BPY_modules_update(void) #if 0 /* slow, this runs all the time poll, draw etc 100's of time a sec. */ PyObject *mod = PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0); PyModule_AddObject(mod, "data", BPY_rna_module()); - PyModule_AddObject(mod, "types", BPY_rna_types()); /* atm this does not need updating */ + PyModule_AddObject(mod, "types", BPY_rna_types()); /* This does not need updating. */ #endif /* refreshes the main struct */ diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 5868c76b28f..d47b59d0c76 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -1210,7 +1210,7 @@ PyDoc_STRVAR(M_Geometry_tessellate_polygon_doc, "\n" " :arg veclist_list: list of polylines\n" " :rtype: list\n"); -/* PolyFill function, uses Blenders scanfill to fill multiple poly lines */ +/* PolyFill function, uses Blenders scan-fill to fill multiple poly lines. */ static PyObject *M_Geometry_tessellate_polygon(PyObject *UNUSED(self), PyObject *polyLineSeq) { PyObject *tri_list; /* Return this list of tri's */ From 88692baacef170d90e55f6ab89392237a55b1bf3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 23 Sep 2021 22:06:50 +1000 Subject: [PATCH 0138/1500] WM: gestures now activate immediately on mouse press Some gestures were activating immediately on tweak events, extend this to mouse-press and click-drag. Without this change, box-select for example wouldn't be automatically activated on mouse-press. --- source/blender/windowmanager/WM_api.h | 1 + source/blender/windowmanager/intern/wm_event_query.c | 6 ++++++ source/blender/windowmanager/intern/wm_gesture_ops.c | 8 +++++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index c5482a729c3..577561017c4 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -906,6 +906,7 @@ int WM_event_modifier_flag(const struct wmEvent *event); bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); bool WM_event_is_last_mousemove(const struct wmEvent *event); bool WM_event_is_mouse_drag(const struct wmEvent *event); +bool WM_event_is_mouse_drag_or_press(const wmEvent *event); int WM_event_drag_threshold(const struct wmEvent *event); bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index e22285214f0..7b5691b99a0 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -270,6 +270,12 @@ bool WM_event_is_mouse_drag(const wmEvent *event) return ISTWEAK(event->type) || (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG)); } +bool WM_event_is_mouse_drag_or_press(const wmEvent *event) +{ + return WM_event_is_mouse_drag(event) || + (ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS)); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index cc376d8f201..578699dda60 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -179,7 +179,8 @@ int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - const bool wait_for_input = !ISTWEAK(event->type) && RNA_boolean_get(op->ptr, "wait_for_input"); + const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) && + RNA_boolean_get(op->ptr, "wait_for_input"); if (wait_for_input) { op->customdata = WM_gesture_new(win, region, event, WM_GESTURE_CROSS_RECT); @@ -300,7 +301,8 @@ static void gesture_circle_apply(bContext *C, wmOperator *op); int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); - const bool wait_for_input = !ISTWEAK(event->type) && RNA_boolean_get(op->ptr, "wait_for_input"); + const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) && + RNA_boolean_get(op->ptr, "wait_for_input"); op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_CIRCLE); wmGesture *gesture = op->customdata; @@ -871,7 +873,7 @@ int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *e op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_STRAIGHTLINE); - if (ISTWEAK(event->type)) { + if (WM_event_is_mouse_drag_or_press(event)) { wmGesture *gesture = op->customdata; gesture->is_active = true; } From 83975965a797642eb0aece30c6a887061b34978d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 23 Sep 2021 22:06:51 +1000 Subject: [PATCH 0139/1500] Keymap: RMB select option to place cursor on Alt-LMB This allows most tools to activate on press, removing a small but noticeable distance between the initial press and detection of a drag event. Gizmo tweak no longer actives when Alt is held, to avoid conflicting with cursor placement. This has the advantage that the gizmo doesn't get in the way when Alt is used for activating tools or placing the cursor. --- release/scripts/presets/keyconfig/Blender.py | 16 +- .../keyconfig/keymap_data/blender_default.py | 253 ++++++++++-------- 2 files changed, 154 insertions(+), 115 deletions(-) diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index 8a937b4d4b1..eb395dbc3b4 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -85,6 +85,8 @@ class Prefs(bpy.types.KeyConfigPreferences): default=False, update=update_fn, ) + # NOTE: expose `use_alt_tool` and `use_alt_cursor` as two options in the UI + # as the tool-tips and titles are different enough depending on RMB/LMB select. use_alt_tool: BoolProperty( name="Alt Tool Access", description=( @@ -93,6 +95,16 @@ class Prefs(bpy.types.KeyConfigPreferences): default=False, update=update_fn, ) + use_alt_cursor: BoolProperty( + name="Alt Cursor Access", + description=( + "Hold Alt-LMB to place the Cursor (instead of LMB), allows tools to activate on press instead of drag" + ), + default=False, + update=update_fn, + ) + # end note. + use_select_all_toggle: BoolProperty( name="Select All Toggles", description=( @@ -219,6 +231,8 @@ class Prefs(bpy.types.KeyConfigPreferences): row.prop(self, "use_alt_click_leader") if is_select_left: row.prop(self, "use_alt_tool") + else: + row.prop(self, "use_alt_cursor") row = sub.row() row.prop(self, "use_select_all_toggle") row.prop(self, "use_key_activate_tools", text="Key Activates Tools") @@ -269,7 +283,7 @@ def load(): use_v3d_shade_ex_pie=kc_prefs.use_v3d_shade_ex_pie, use_gizmo_drag=(is_select_left and kc_prefs.gizmo_action == 'DRAG'), use_fallback_tool=(True if is_select_left else (kc_prefs.rmb_action == 'FALLBACK_TOOL')), - use_alt_tool=(kc_prefs.use_alt_tool and is_select_left), + use_alt_tool_or_cursor=kc_prefs.use_alt_tool if is_select_left else kc_prefs.use_alt_cursor, use_alt_click_leader=kc_prefs.use_alt_click_leader, use_pie_click_drag=kc_prefs.use_pie_click_drag, ), diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 5ecbe7715e3..4b15cdc25d3 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -31,6 +31,8 @@ class Params: "action_tweak", "tool_mouse", "tool_tweak", + "tool_maybe_tweak", + "tool_maybe_tweak_value", "context_menu_event", "cursor_set_event", "cursor_tweak_event", @@ -74,6 +76,8 @@ class Params: "use_fallback_tool_rmb", # Convenience for: `'CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value`. "select_mouse_value_fallback", + # Convenience for: `{"type": params.tool_maybe_tweak, "value": params.tool_maybe_tweak_value}`. + "tool_maybe_tweak_event", ) def __init__( @@ -92,7 +96,7 @@ class Params: use_v3d_tab_menu=False, use_v3d_shade_ex_pie=False, use_v3d_mmb_pan=False, - use_alt_tool=False, + use_alt_tool_or_cursor=False, use_alt_click_leader=False, use_pie_click_drag=False, v3d_tilde_action='VIEW', @@ -111,8 +115,21 @@ class Params: self.action_tweak = 'EVT_TWEAK_L' self.tool_mouse = 'LEFTMOUSE' self.tool_tweak = 'EVT_TWEAK_L' + if use_alt_tool_or_cursor: + self.tool_maybe_tweak = 'LEFTMOUSE' + self.tool_maybe_tweak_value = 'PRESS' + else: + self.tool_maybe_tweak = 'EVT_TWEAK_L' + self.tool_maybe_tweak_value = 'ANY' + self.context_menu_event = {"type": 'W', "value": 'PRESS'} - self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'} + + # Use the "cursor" functionality for RMB select. + if use_alt_tool_or_cursor: + self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True} + else: + self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'} + self.cursor_tweak_event = None self.use_fallback_tool = use_fallback_tool self.use_fallback_tool_rmb = use_fallback_tool @@ -129,6 +146,8 @@ class Params: self.action_tweak = 'EVT_TWEAK_R' self.tool_mouse = 'LEFTMOUSE' self.tool_tweak = 'EVT_TWEAK_L' + self.tool_maybe_tweak = 'EVT_TWEAK_L' + self.tool_maybe_tweak_value = 'ANY' if self.legacy: self.context_menu_event = {"type": 'W', "value": 'PRESS'} @@ -141,7 +160,8 @@ class Params: self.use_fallback_tool_rmb = False self.select_mouse_value_fallback = self.select_mouse_value - if use_alt_tool: + # Use the "tool" functionality for LMB select. + if use_alt_tool_or_cursor: # Allow `Alt` to be pressed or not. self.tool_modifier = {"alt": -1} else: @@ -168,6 +188,9 @@ class Params: else: self.pie_value = 'CLICK_DRAG' + # Convenience variables. + self.tool_maybe_tweak_event = {"type": self.tool_maybe_tweak, "value": self.tool_maybe_tweak_value} + # ------------------------------------------------------------------------------ # Constants @@ -188,6 +211,13 @@ def _fallback_id(text, fallback): return text +def any_except(*args): + mod = {"ctrl": -1, "alt": -1, "shift": -1, "oskey": -1} + for arg in args: + del mod[arg] + return mod + + # ------------------------------------------------------------------------------ # Keymap Item Wrappers @@ -300,20 +330,23 @@ def _template_items_object_subdivision_set(): def _template_items_gizmo_tweak_value(): return [ - ("gizmogroup.gizmo_tweak", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None), + ("gizmogroup.gizmo_tweak", + {"type": 'LEFTMOUSE', "value": 'PRESS', **any_except("alt")}, None), ] def _template_items_gizmo_tweak_value_click_drag(): return [ - ("gizmogroup.gizmo_tweak", {"type": 'LEFTMOUSE', "value": 'CLICK', "any": True}, None), - ("gizmogroup.gizmo_tweak", {"type": 'EVT_TWEAK_L', "value": 'ANY', "any": True}, None), + ("gizmogroup.gizmo_tweak", + {"type": 'LEFTMOUSE', "value": 'CLICK', **any_except("alt")}, None), + ("gizmogroup.gizmo_tweak", + {"type": 'EVT_TWEAK_L', "value": 'ANY', **any_except("alt")}, None), ] def _template_items_gizmo_tweak_value_drag(): return [ - ("gizmogroup.gizmo_tweak", {"type": 'EVT_TWEAK_L', "value": 'ANY', "any": True}, None), + ("gizmogroup.gizmo_tweak", {"type": 'EVT_TWEAK_L', "value": 'ANY', **any_except("alt")}, None), ] @@ -6046,7 +6079,7 @@ def km_generic_tool_annotate_line(params): "Generic Tool: Annotate Line", {"space_type": 'EMPTY', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.annotate", {"type": params.tool_tweak, "value": 'ANY'}, + ("gpencil.annotate", params.tool_maybe_tweak_event, {"properties": [("mode", 'DRAW_STRAIGHT'), ("wait_for_input", False)]}), ("gpencil.annotate", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True}, {"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}), @@ -6095,8 +6128,12 @@ def km_image_editor_tool_uv_cursor(params): "Image Editor Tool: Uv, Cursor", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("uv.cursor_set", {"type": params.tool_mouse, "value": 'PRESS'}, None), - ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'}, + ("uv.cursor_set", + {"type": params.tool_mouse, "value": 'PRESS'}, + None), + # Don't use `tool_maybe_tweak_event` since it conflicts with `PRESS` that places the cursor. + ("transform.translate", + {"type": params.tool_tweak, "value": 'ANY'}, {"properties": [("release_confirm", True), ("cursor_transform", True)]}), ]}, ) @@ -6121,8 +6158,7 @@ def km_image_editor_tool_uv_select_box(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_box", - type=params.select_tweak if fallback else params.tool_tweak, - value='ANY')), + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), *_template_uv_select_for_fallback(params, fallback), ]}, ) @@ -6151,9 +6187,7 @@ def km_image_editor_tool_uv_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_lasso", - type=params.select_tweak if fallback else params.tool_tweak, - value='ANY') - ), + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), *_template_uv_select_for_fallback(params, fallback), ]}, ) @@ -6164,7 +6198,7 @@ def km_image_editor_tool_uv_rip_region(params): "Image Editor Tool: Uv, Rip Region", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("uv.rip_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("uv.rip_move", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6194,7 +6228,7 @@ def km_image_editor_tool_uv_move(params): "Image Editor Tool: Uv, Move", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.translate", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.translate", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6205,7 +6239,7 @@ def km_image_editor_tool_uv_rotate(params): "Image Editor Tool: Uv, Rotate", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.rotate", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.rotate", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6216,7 +6250,7 @@ def km_image_editor_tool_uv_scale(params): "Image Editor Tool: Uv, Scale", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.resize", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.resize", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6241,7 +6275,8 @@ def km_node_editor_tool_select_box(params, *, fallback): {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( - "node.select_box", type=params.tool_tweak, value='ANY', + "node.select_box", + type=params.tool_maybe_tweak, value=params.tool_maybe_tweak_value, properties=[("tweak", True)], )), ]}, @@ -6288,6 +6323,7 @@ def km_3d_view_tool_cursor(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ ("view3d.cursor3d", {"type": params.tool_mouse, "value": 'PRESS'}, None), + # Don't use `tool_maybe_tweak_event` since it conflicts with `PRESS` that places the cursor. ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'}, {"properties": [("release_confirm", True), ("cursor_transform", True)]}), ]}, @@ -6314,8 +6350,7 @@ def km_3d_view_tool_select_box(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "view3d.select_box", - type=params.select_tweak if fallback else params.tool_tweak, - value='ANY')), + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), *_template_view3d_select_for_fallback(params, fallback), ]}, ) @@ -6345,8 +6380,7 @@ def km_3d_view_tool_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "view3d.select_lasso", - type=params.select_tweak if fallback else params.tool_tweak, - value='ANY')), + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), *_template_view3d_select_for_fallback(params, fallback), ]} ) @@ -6357,8 +6391,7 @@ def km_3d_view_tool_transform(params): "3D View Tool: Transform", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.from_gizmo", - {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), + ("transform.from_gizmo", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None), ]}, ) @@ -6368,8 +6401,7 @@ def km_3d_view_tool_move(params): "3D View Tool: Move", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.translate", - {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.translate", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6380,8 +6412,7 @@ def km_3d_view_tool_rotate(params): "3D View Tool: Rotate", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.rotate", - {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.rotate", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6392,14 +6423,14 @@ def km_3d_view_tool_scale(params): "3D View Tool: Scale", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.resize", - {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.resize", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) def km_3d_view_tool_shear(params): + # Don't use 'tool_maybe_tweak_value' since we would loose tweak direction support. return ( "3D View Tool: Shear", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, @@ -6424,7 +6455,7 @@ def km_3d_view_tool_measure(params): "3D View Tool: Measure", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("view3d.ruler_add", {"type": params.tool_tweak, "value": 'ANY'}, None), + ("view3d.ruler_add", params.tool_maybe_tweak_event, None), ("view3d.ruler_remove", {"type": 'X', "value": 'PRESS'}, None), ("view3d.ruler_remove", {"type": 'DEL', "value": 'PRESS'}, None), ]}, @@ -6436,7 +6467,7 @@ def km_3d_view_tool_pose_breakdowner(params): "3D View Tool: Pose, Breakdowner", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("pose.breakdown", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), + ("pose.breakdown", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None), ]}, ) @@ -6446,8 +6477,7 @@ def km_3d_view_tool_pose_push(params): "3D View Tool: Pose, Push", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("pose.push", - {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), + ("pose.push", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None), ]}, ) @@ -6457,8 +6487,7 @@ def km_3d_view_tool_pose_relax(params): "3D View Tool: Pose, Relax", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("pose.relax", - {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), + ("pose.relax", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None), ]}, ) @@ -6468,8 +6497,7 @@ def km_3d_view_tool_edit_armature_roll(params): "3D View Tool: Edit Armature, Roll", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.transform", - {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.transform", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True), ("mode", 'BONE_ROLL')]}), ]}, ) @@ -6480,7 +6508,7 @@ def km_3d_view_tool_edit_armature_bone_size(params): "3D View Tool: Edit Armature, Bone Size", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.transform", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.transform", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True), ("mode", 'BONE_ENVELOPE')]}), ]}, ) @@ -6492,7 +6520,7 @@ def km_3d_view_tool_edit_armature_bone_envelope(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.bbone_resize", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.bbone_resize", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6503,7 +6531,7 @@ def km_3d_view_tool_edit_armature_extrude(params): "3D View Tool: Edit Armature, Extrude", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("armature.extrude_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("armature.extrude_move", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6524,7 +6552,12 @@ def km_3d_view_tool_interactive_add(params): "3D View Tool: Object, Add Primitive", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("view3d.interactive_add", {"type": params.tool_tweak, "value": 'ANY', "any": True}, + ("view3d.interactive_add", + {**params.tool_maybe_tweak_event, + # While "Alt" isn't an important shortcut to support, + # when the preferences to activate tools when "Alt" is held is used, + # it's illogical not to support holding "Alt", even though it is not required. + **({"any": True} if "alt" in params.tool_modifier else any_except("alt"))}, {"properties": [("wait_for_input", False)]}), ]}, ) @@ -6535,7 +6568,7 @@ def km_3d_view_tool_edit_mesh_extrude_region(params): "3D View Tool: Edit Mesh, Extrude Region", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.extrude_context_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("mesh.extrude_context_move", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6546,7 +6579,7 @@ def km_3d_view_tool_edit_mesh_extrude_manifold(params): "3D View Tool: Edit Mesh, Extrude Manifold", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.extrude_manifold", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("mesh.extrude_manifold", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [ ("MESH_OT_extrude_region", [("use_dissolve_ortho_edges", True)]), ("TRANSFORM_OT_translate", [ @@ -6565,7 +6598,7 @@ def km_3d_view_tool_edit_mesh_extrude_along_normals(params): "3D View Tool: Edit Mesh, Extrude Along Normals", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.extrude_region_shrink_fatten", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("mesh.extrude_region_shrink_fatten", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("TRANSFORM_OT_shrink_fatten", [("release_confirm", True)])]}), ]}, ) @@ -6576,7 +6609,7 @@ def km_3d_view_tool_edit_mesh_extrude_individual(params): "3D View Tool: Edit Mesh, Extrude Individual", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.extrude_faces_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("mesh.extrude_faces_move", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("TRANSFORM_OT_shrink_fatten", [("release_confirm", True)])]}), ]}, ) @@ -6598,7 +6631,7 @@ def km_3d_view_tool_edit_mesh_inset_faces(params): "3D View Tool: Edit Mesh, Inset Faces", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.inset", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("mesh.inset", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6609,7 +6642,7 @@ def km_3d_view_tool_edit_mesh_bevel(params): "3D View Tool: Edit Mesh, Bevel", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.bevel", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("mesh.bevel", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6656,7 +6689,7 @@ def km_3d_view_tool_edit_mesh_bisect(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ # No need for `tool_modifier` since this takes all input. - ("mesh.bisect", {"type": params.tool_tweak, "value": 'ANY'}, None), + ("mesh.bisect", params.tool_maybe_tweak_event, None), ]}, ) @@ -6681,7 +6714,7 @@ def km_3d_view_tool_edit_mesh_spin(params): "3D View Tool: Edit Mesh, Spin", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.spin", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), + ("mesh.spin", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None), ]}, ) @@ -6691,7 +6724,7 @@ def km_3d_view_tool_edit_mesh_spin_duplicate(params): "3D View Tool: Edit Mesh, Spin Duplicates", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.spin", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("mesh.spin", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("dupli", True)]}), ]}, ) @@ -6702,7 +6735,7 @@ def km_3d_view_tool_edit_mesh_smooth(params): "3D View Tool: Edit Mesh, Smooth", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.vertices_smooth", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("mesh.vertices_smooth", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("wait_for_input", False)]}), ]}, ) @@ -6713,7 +6746,7 @@ def km_3d_view_tool_edit_mesh_randomize(params): "3D View Tool: Edit Mesh, Randomize", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.vertex_random", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.vertex_random", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("wait_for_input", False)]}), ]}, ) @@ -6724,7 +6757,7 @@ def km_3d_view_tool_edit_mesh_edge_slide(params): "3D View Tool: Edit Mesh, Edge Slide", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.edge_slide", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.edge_slide", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6735,7 +6768,7 @@ def km_3d_view_tool_edit_mesh_vertex_slide(params): "3D View Tool: Edit Mesh, Vertex Slide", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.vert_slide", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.vert_slide", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6746,7 +6779,7 @@ def km_3d_view_tool_edit_mesh_shrink_fatten(params): "3D View Tool: Edit Mesh, Shrink/Fatten", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.shrink_fatten", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.shrink_fatten", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6757,7 +6790,7 @@ def km_3d_view_tool_edit_mesh_push_pull(params): "3D View Tool: Edit Mesh, Push/Pull", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.push_pull", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.push_pull", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6768,7 +6801,7 @@ def km_3d_view_tool_edit_mesh_to_sphere(params): "3D View Tool: Edit Mesh, To Sphere", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.tosphere", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.tosphere", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6779,7 +6812,7 @@ def km_3d_view_tool_edit_mesh_rip_region(params): "3D View Tool: Edit Mesh, Rip Region", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.rip_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("mesh.rip_move", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6790,7 +6823,7 @@ def km_3d_view_tool_edit_mesh_rip_edge(params): "3D View Tool: Edit Mesh, Rip Edge", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.rip_edge_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("mesh.rip_edge_move", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6813,7 +6846,7 @@ def km_3d_view_tool_edit_curve_tilt(params): "3D View Tool: Edit Curve, Tilt", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.tilt", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.tilt", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -6824,7 +6857,7 @@ def km_3d_view_tool_edit_curve_radius(params): "3D View Tool: Edit Curve, Radius", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.transform", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.transform", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("mode", 'CURVE_SHRINKFATTEN'), ("release_confirm", True)]}), ]}, ) @@ -6835,7 +6868,7 @@ def km_3d_view_tool_edit_curve_randomize(params): "3D View Tool: Edit Curve, Randomize", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("transform.vertex_random", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("transform.vertex_random", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("wait_for_input", False)]}), ]}, ) @@ -6846,7 +6879,7 @@ def km_3d_view_tool_edit_curve_extrude(params): "3D View Tool: Edit Curve, Extrude", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("curve.extrude_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + ("curve.extrude_move", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), ]}, ) @@ -6868,9 +6901,9 @@ def km_3d_view_tool_sculpt_box_hide(params): "3D View Tool: Sculpt, Box Hide", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("paint.hide_show", {"type": params.tool_tweak, "value": 'ANY'}, + ("paint.hide_show", params.tool_maybe_tweak_event, {"properties": [("action", 'HIDE')]}), - ("paint.hide_show", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True}, + ("paint.hide_show", {**params.tool_maybe_tweak_event, "ctrl": True}, {"properties": [("action", 'SHOW')]}), ("paint.hide_show", {"type": params.select_mouse, "value": params.select_mouse_value}, {"properties": [("action", 'SHOW'), ("area", 'ALL')]}), @@ -6883,9 +6916,9 @@ def km_3d_view_tool_sculpt_box_mask(params): "3D View Tool: Sculpt, Box Mask", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("paint.mask_box_gesture", {"type": params.tool_tweak, "value": 'ANY'}, + ("paint.mask_box_gesture", params.tool_maybe_tweak_event, {"properties": [("value", 1.0)]}), - ("paint.mask_box_gesture", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True}, + ("paint.mask_box_gesture", {**params.tool_maybe_tweak_event, "ctrl": True}, {"properties": [("value", 0.0)]}), ]}, ) @@ -6896,9 +6929,9 @@ def km_3d_view_tool_sculpt_lasso_mask(params): "3D View Tool: Sculpt, Lasso Mask", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("paint.mask_lasso_gesture", {"type": params.tool_tweak, "value": 'ANY'}, + ("paint.mask_lasso_gesture", params.tool_maybe_tweak_event, {"properties": [("value", 1.0)]}), - ("paint.mask_lasso_gesture", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True}, + ("paint.mask_lasso_gesture", {**params.tool_maybe_tweak_event, "ctrl": True}, {"properties": [("value", 0.0)]}), ]}, ) @@ -6909,8 +6942,7 @@ def km_3d_view_tool_sculpt_box_face_set(params): "3D View Tool: Sculpt, Box Face Set", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.face_set_box_gesture", {"type": params.tool_tweak, "value": 'ANY'}, - None), + ("sculpt.face_set_box_gesture", params.tool_maybe_tweak_event, None), ]}, ) @@ -6920,8 +6952,7 @@ def km_3d_view_tool_sculpt_lasso_face_set(params): "3D View Tool: Sculpt, Lasso Face Set", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.face_set_lasso_gesture", {"type": params.tool_tweak, "value": 'ANY'}, - None), + ("sculpt.face_set_lasso_gesture", params.tool_maybe_tweak_event, None), ]}, ) @@ -6931,8 +6962,7 @@ def km_3d_view_tool_sculpt_box_trim(params): "3D View Tool: Sculpt, Box Trim", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.trim_box_gesture", {"type": params.tool_tweak, "value": 'ANY'}, - None), + ("sculpt.trim_box_gesture", params.tool_maybe_tweak_event, None), ]}, ) @@ -6942,8 +6972,7 @@ def km_3d_view_tool_sculpt_lasso_trim(params): "3D View Tool: Sculpt, Lasso Trim", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.trim_lasso_gesture", {"type": params.tool_tweak, "value": 'ANY'}, - None), + ("sculpt.trim_lasso_gesture", params.tool_maybe_tweak_event, None), ]}, ) @@ -6953,9 +6982,9 @@ def km_3d_view_tool_sculpt_line_mask(params): "3D View Tool: Sculpt, Line Mask", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("paint.mask_line_gesture", {"type": params.tool_tweak, "value": 'ANY'}, + ("paint.mask_line_gesture", params.tool_maybe_tweak_event, {"properties": [("value", 1.0)]}), - ("paint.mask_line_gesture", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True}, + ("paint.mask_line_gesture", {**params.tool_maybe_tweak_event, "ctrl": True}, {"properties": [("value", 0.0)]}), ]}, ) @@ -6966,8 +6995,7 @@ def km_3d_view_tool_sculpt_line_project(params): "3D View Tool: Sculpt, Line Project", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.project_line_gesture", {"type": params.tool_tweak, "value": 'ANY'}, - None), + ("sculpt.project_line_gesture", params.tool_maybe_tweak_event, None), ]}, ) @@ -6977,8 +7005,7 @@ def km_3d_view_tool_sculpt_mesh_filter(params): "3D View Tool: Sculpt, Mesh Filter", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.mesh_filter", {"type": params.tool_tweak, "value": 'ANY'}, - None) + ("sculpt.mesh_filter", params.tool_maybe_tweak_event, None) ]}, ) @@ -6988,8 +7015,7 @@ def km_3d_view_tool_sculpt_cloth_filter(params): "3D View Tool: Sculpt, Cloth Filter", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.cloth_filter", {"type": params.tool_tweak, "value": 'ANY'}, - None) + ("sculpt.cloth_filter", params.tool_maybe_tweak_event, None) ]}, ) @@ -6999,8 +7025,7 @@ def km_3d_view_tool_sculpt_color_filter(params): "3D View Tool: Sculpt, Color Filter", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.color_filter", {"type": params.tool_tweak, "value": 'ANY'}, - None) + ("sculpt.color_filter", params.tool_maybe_tweak_event, None) ]}, ) @@ -7054,7 +7079,7 @@ def km_3d_view_tool_paint_weight_gradient(params): "3D View Tool: Paint Weight, Gradient", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("paint.weight_gradient", {"type": params.tool_tweak, "value": 'ANY'}, None), + ("paint.weight_gradient", params.tool_maybe_tweak_event, None), ]}, ) @@ -7064,7 +7089,7 @@ def km_3d_view_tool_paint_gpencil_line(params): "3D View Tool: Paint Gpencil, Line", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.primitive_line", {"type": params.tool_tweak, "value": 'ANY'}, + ("gpencil.primitive_line", params.tool_maybe_tweak_event, {"properties": [("wait_for_input", False)]}), ("gpencil.primitive_line", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, {"properties": [("wait_for_input", False)]}), @@ -7081,7 +7106,7 @@ def km_3d_view_tool_paint_gpencil_polyline(params): "3D View Tool: Paint Gpencil, Polyline", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.primitive_polyline", {"type": params.tool_tweak, "value": 'ANY'}, + ("gpencil.primitive_polyline", params.tool_maybe_tweak_event, {"properties": [("wait_for_input", False)]}), ("gpencil.primitive_polyline", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, {"properties": [("wait_for_input", False)]}), @@ -7096,7 +7121,7 @@ def km_3d_view_tool_paint_gpencil_box(params): "3D View Tool: Paint Gpencil, Box", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.primitive_box", {"type": params.tool_tweak, "value": 'ANY'}, + ("gpencil.primitive_box", params.tool_maybe_tweak_event, {"properties": [("wait_for_input", False)]}), ("gpencil.primitive_box", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, {"properties": [("wait_for_input", False)]}), @@ -7113,7 +7138,7 @@ def km_3d_view_tool_paint_gpencil_circle(params): "3D View Tool: Paint Gpencil, Circle", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.primitive_circle", {"type": params.tool_tweak, "value": 'ANY'}, + ("gpencil.primitive_circle", params.tool_maybe_tweak_event, {"properties": [("wait_for_input", False)]}), ("gpencil.primitive_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, {"properties": [("wait_for_input", False)]}), @@ -7130,7 +7155,7 @@ def km_3d_view_tool_paint_gpencil_arc(params): "3D View Tool: Paint Gpencil, Arc", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.primitive_curve", {"type": params.tool_tweak, "value": 'ANY'}, + ("gpencil.primitive_curve", params.tool_maybe_tweak_event, {"properties": [("type", 'ARC'), ("wait_for_input", False)]}), ("gpencil.primitive_curve", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, {"properties": [("type", 'ARC'), ("wait_for_input", False)]}), @@ -7147,7 +7172,7 @@ def km_3d_view_tool_paint_gpencil_curve(params): "3D View Tool: Paint Gpencil, Curve", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.primitive_curve", {"type": params.tool_tweak, "value": 'ANY'}, + ("gpencil.primitive_curve", params.tool_maybe_tweak_event, {"properties": [("type", 'CURVE'), ("wait_for_input", False)]}), # Lasso select ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), @@ -7187,7 +7212,7 @@ def km_3d_view_tool_paint_gpencil_interpolate(params): "3D View Tool: Paint Gpencil, Interpolate", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.interpolate", {"type": params.tool_tweak, "value": 'ANY'}, + ("gpencil.interpolate", params.tool_maybe_tweak_event, {"properties": [("release_confirm", True)]}), ]}, ) @@ -7212,7 +7237,8 @@ def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( - "gpencil.select_box", type=params.select_tweak if fallback else params.tool_tweak, value='ANY')), + "gpencil.select_box", + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), *_template_view3d_gpencil_select_for_fallback(params, fallback), ]}, ) @@ -7242,8 +7268,7 @@ def km_3d_view_tool_edit_gpencil_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "gpencil.select_lasso", - type=params.select_tweak if fallback else params.tool_tweak, - value='ANY')), + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), *_template_view3d_gpencil_select_for_fallback(params, fallback), ]} ) @@ -7254,7 +7279,7 @@ def km_3d_view_tool_edit_gpencil_extrude(params): "3D View Tool: Edit Gpencil, Extrude", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.extrude_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None), + ("gpencil.extrude_move", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None), ]}, ) @@ -7265,7 +7290,7 @@ def km_3d_view_tool_edit_gpencil_radius(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ # No need for `tool_modifier` since this takes all input. - ("transform.transform", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.transform", params.tool_maybe_tweak_event, {"properties": [("mode", 'GPENCIL_SHRINKFATTEN'), ("release_confirm", True)]}), ]}, ) @@ -7277,7 +7302,7 @@ def km_3d_view_tool_edit_gpencil_bend(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ # No need for `tool_modifier` since this takes all input. - ("transform.bend", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.bend", params.tool_maybe_tweak_event, {"properties": [("release_confirm", True)]}), ]}, ) @@ -7289,7 +7314,7 @@ def km_3d_view_tool_edit_gpencil_shear(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ # No need for `tool_modifier` since this takes all input. - ("transform.shear", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.shear", params.tool_maybe_tweak_event, {"properties": [("release_confirm", True)]}), ]}, ) @@ -7301,7 +7326,7 @@ def km_3d_view_tool_edit_gpencil_to_sphere(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ # No need for `tool_modifier` since this takes all input. - ("transform.tosphere", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.tosphere", params.tool_maybe_tweak_event, {"properties": [("release_confirm", True)]}), ]}, ) @@ -7313,7 +7338,7 @@ def km_3d_view_tool_edit_gpencil_transform_fill(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ # No need for `tool_modifier` since this takes all input. - ("gpencil.transform_fill", {"type": params.tool_tweak, "value": 'ANY'}, + ("gpencil.transform_fill", params.tool_maybe_tweak_event, {"properties": [("release_confirm", True)]}), ]}, ) @@ -7324,7 +7349,7 @@ def km_3d_view_tool_edit_gpencil_interpolate(params): "3D View Tool: Edit Gpencil, Interpolate", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("gpencil.interpolate", {"type": params.tool_tweak, "value": 'ANY'}, + ("gpencil.interpolate", params.tool_maybe_tweak_event, {"properties": [("release_confirm", True)]}), ]}, ) @@ -7342,7 +7367,7 @@ def km_3d_view_tool_sculpt_gpencil_select_box(params): return ( "3D View Tool: Sculpt Gpencil, Select Box", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions("gpencil.select_box", type=params.tool_tweak, value='ANY')}, + {"items": _template_items_tool_select_actions("gpencil.select_box", **params.tool_maybe_tweak_event)}, ) @@ -7361,7 +7386,7 @@ def km_3d_view_tool_sculpt_gpencil_select_lasso(params): return ( "3D View Tool: Sculpt Gpencil, Select Lasso", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions("gpencil.select_lasso", type=params.tool_tweak, value='ANY')}, + {"items": _template_items_tool_select_actions("gpencil.select_lasso", **params.tool_maybe_tweak_event)}, ) @@ -7384,7 +7409,7 @@ def km_sequencer_editor_tool_select_box(params, *, fallback): {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ *_template_items_tool_select_actions_simple( - "sequencer.select_box", type=params.tool_tweak, value='ANY', + "sequencer.select_box", **params.tool_maybe_tweak_event, properties=[("tweak", params.select_mouse == 'LEFTMOUSE')], ), # RMB select can already set the frame, match the tweak tool. @@ -7425,7 +7450,7 @@ def km_sequencer_editor_tool_move(params): "Sequencer Tool: Move", {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.translate", params.tool_maybe_tweak_event, {"properties": [("release_confirm", True)]}), ]}, ) @@ -7436,7 +7461,7 @@ def km_sequencer_editor_tool_rotate(params): "Sequencer Tool: Rotate", {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.rotate", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.rotate", params.tool_maybe_tweak_event, {"properties": [("release_confirm", True)]}), ]}, ) @@ -7447,7 +7472,7 @@ def km_sequencer_editor_tool_scale(params): "Sequencer Tool: Scale", {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.resize", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.resize", params.tool_maybe_tweak_event, {"properties": [("release_confirm", True)]}), ]}, ) From 059d01d42e049a5d4a42f81c8b860540127efeb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 23 Sep 2021 14:41:06 +0200 Subject: [PATCH 0140/1500] Fileops: call `BLI_path_slash_native()` in `BLI_dir_create_recursive()` Make the Windows version of `BLI_dir_create_recursive()` call `BLI_path_slash_native()` before it tries to handle the path. This should make it possible to call it with non-native path separators. This change was provided by our Windows platform maintainer @LazyDodo in P2414, so I assume he agrees with this change. --- source/blender/blenlib/intern/fileops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 532a29b8147..88fb67c5502 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -570,6 +570,7 @@ bool BLI_dir_create_recursive(const char *dirname) * blah1/blah2 (without slash) */ BLI_strncpy(tmp, dirname, sizeof(tmp)); + BLI_path_slash_native(tmp); BLI_path_slash_rstrip(tmp); /* check special case "c:\foo", don't try create "c:", harmless but prints an error below */ From 222fd1abf09ae65b7082f58bf2ac43422c77162c Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 23 Sep 2021 14:43:21 +0200 Subject: [PATCH 0141/1500] Asset Browser: Disable metadata editing for external asset libraries Buttons to edit asset metadata are now disabled for assets from an external library (i.e. assets not stored in the current .blend file). Their tooltips explain why they are disabled. Had to do some RNA trickery to disable the metadata properties at RNA level, not at UI script level. The basic idea is: * Local data-block assets set the data-block as owning ID for the asset metadata RNA pointer now. * That way we can use the owner ID to see where the metadata belongs to and decide if it's editable that way. * Additionaly, some Python operators needed better polling so they show as grayed out, and don't just fail. One important thing: Custom properties of the metadata can still be edited. The edits won't be saved however. Would be nice to disable that, but it's currently not supported on BPY/IDProperty/RNA level. Addresses T82943. Differential Revision: https://developer.blender.org/D12127 --- .../scripts/startup/bl_operators/assets.py | 42 +++++++----- source/blender/makesrna/intern/rna_asset.c | 68 ++++++++++++++++--- source/blender/makesrna/intern/rna_internal.h | 1 + source/blender/makesrna/intern/rna_space.c | 43 ++++++++++-- 4 files changed, 123 insertions(+), 31 deletions(-) diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py index b241e537c38..de46e5c85fb 100644 --- a/release/scripts/startup/bl_operators/assets.py +++ b/release/scripts/startup/bl_operators/assets.py @@ -27,17 +27,28 @@ from bpy_extras.asset_utils import ( ) -class ASSET_OT_tag_add(Operator): +class AssetBrowserMetadataOperator: + @classmethod + def poll(cls, context): + if not SpaceAssetInfo.is_asset_browser_poll(context) or not context.asset_file_handle: + return False + + if not context.asset_file_handle.local_id: + Operator.poll_message_set( + "Asset metadata from external asset libraries can't be " + "edited, only assets stored in the current file can" + ) + return False + return True + + +class ASSET_OT_tag_add(AssetBrowserMetadataOperator, Operator): """Add a new keyword tag to the active asset""" bl_idname = "asset.tag_add" bl_label = "Add Asset Tag" bl_options = {'REGISTER', 'UNDO'} - @classmethod - def poll(cls, context): - return SpaceAssetInfo.is_asset_browser_poll(context) and SpaceAssetInfo.get_active_asset(context) - def execute(self, context): active_asset = SpaceAssetInfo.get_active_asset(context) active_asset.tags.new("Unnamed Tag") @@ -45,7 +56,7 @@ class ASSET_OT_tag_add(Operator): return {'FINISHED'} -class ASSET_OT_tag_remove(Operator): +class ASSET_OT_tag_remove(AssetBrowserMetadataOperator, Operator): """Remove an existing keyword tag from the active asset""" bl_idname = "asset.tag_remove" @@ -54,21 +65,20 @@ class ASSET_OT_tag_remove(Operator): @classmethod def poll(cls, context): - if not SpaceAssetInfo.is_asset_browser_poll(context): + if not super().poll(context): return False - active_asset = SpaceAssetInfo.get_active_asset(context) - if not active_asset: - return False - - return active_asset.active_tag in range(len(active_asset.tags)) + active_asset_file = context.asset_file_handle + asset_metadata = active_asset_file.asset_data + return asset_metadata.active_tag in range(len(asset_metadata.tags)) def execute(self, context): - active_asset = SpaceAssetInfo.get_active_asset(context) - tag = active_asset.tags[active_asset.active_tag] + active_asset_file = context.asset_file_handle + asset_metadata = active_asset_file.asset_data + tag = asset_metadata.tags[asset_metadata.active_tag] - active_asset.tags.remove(tag) - active_asset.active_tag -= 1 + asset_metadata.tags.remove(tag) + asset_metadata.active_tag -= 1 return {'FINISHED'} diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 1e583f4ca52..dcef88d2e79 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -40,11 +40,54 @@ # include "RNA_access.h" -static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data, - ReportList *reports, - const char *name, - bool skip_if_exists) +static bool rna_AssetMetaData_editable_from_owner_id(const ID *owner_id, + const AssetMetaData *asset_data, + const char **r_info) { + if (owner_id && asset_data && (owner_id->asset_data == asset_data)) { + return true; + } + + if (r_info) { + *r_info = + "Asset metadata from external asset libraries can't be edited, only assets stored in the " + "current file can"; + } + return false; +} + +int rna_AssetMetaData_editable(PointerRNA *ptr, const char **r_info) +{ + AssetMetaData *asset_data = ptr->data; + + return rna_AssetMetaData_editable_from_owner_id(ptr->owner_id, asset_data, r_info) ? + PROP_EDITABLE : + 0; +} + +static int rna_AssetTag_editable(PointerRNA *ptr, const char **r_info) +{ + AssetTag *asset_tag = ptr->data; + ID *owner_id = ptr->owner_id; + if (owner_id && owner_id->asset_data) { + BLI_assert_msg(BLI_findindex(&owner_id->asset_data->tags, asset_tag) != -1, + "The owner of the asset tag pointer is not the asset ID containing the tag"); + } + + return rna_AssetMetaData_editable_from_owner_id(ptr->owner_id, owner_id->asset_data, r_info) ? + PROP_EDITABLE : + 0; +} + +static AssetTag *rna_AssetMetaData_tag_new( + ID *id, AssetMetaData *asset_data, ReportList *reports, const char *name, bool skip_if_exists) +{ + const char *disabled_info = NULL; + if (!rna_AssetMetaData_editable_from_owner_id(id, asset_data, &disabled_info)) { + BKE_report(reports, RPT_WARNING, disabled_info); + return NULL; + } + AssetTag *tag = NULL; if (skip_if_exists) { @@ -64,10 +107,17 @@ static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data, return tag; } -static void rna_AssetMetaData_tag_remove(AssetMetaData *asset_data, +static void rna_AssetMetaData_tag_remove(ID *id, + AssetMetaData *asset_data, ReportList *reports, PointerRNA *tag_ptr) { + const char *disabled_info = NULL; + if (!rna_AssetMetaData_editable_from_owner_id(id, asset_data, &disabled_info)) { + BKE_report(reports, RPT_WARNING, disabled_info); + return; + } + AssetTag *tag = tag_ptr->data; if (BLI_findindex(&asset_data->tags, tag) == -1) { BKE_reportf(reports, RPT_ERROR, "Tag '%s' not found in given asset", tag->name); @@ -185,6 +235,7 @@ static void rna_def_asset_tag(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Asset Tag", "User defined tag (name token)"); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_editable_func(prop, "rna_AssetTag_editable"); RNA_def_property_string_maxlength(prop, MAX_NAME); RNA_def_property_ui_text(prop, "Name", "The identifier that makes up this tag"); RNA_def_struct_name_property(srna, prop); @@ -205,7 +256,7 @@ static void rna_def_asset_tags_api(BlenderRNA *brna, PropertyRNA *cprop) /* Tag collection */ func = RNA_def_function(srna, "new", "rna_AssetMetaData_tag_new"); RNA_def_function_ui_description(func, "Add a new tag to this asset"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS); parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_boolean(func, @@ -219,7 +270,7 @@ static void rna_def_asset_tags_api(BlenderRNA *brna, PropertyRNA *cprop) func = RNA_def_function(srna, "remove", "rna_AssetMetaData_tag_remove"); RNA_def_function_ui_description(func, "Remove an existing tag from this asset"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS); /* tag to remove */ parm = RNA_def_pointer(func, "tag", "AssetTag", "", "Removed tag"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); @@ -239,6 +290,7 @@ static void rna_def_asset_data(BlenderRNA *brna) RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */ prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); + RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); RNA_def_property_string_funcs(prop, "rna_AssetMetaData_description_get", "rna_AssetMetaData_description_length", @@ -248,7 +300,7 @@ static void rna_def_asset_data(BlenderRNA *brna) prop = RNA_def_property(srna, "tags", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "AssetTag"); - RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); RNA_def_property_ui_text(prop, "Tags", "Custom tags (name tokens) for the asset, used for filtering and " diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 0bb76fd933a..fd6664ff0de 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -267,6 +267,7 @@ void rna_def_mtex_common(struct BlenderRNA *brna, void rna_def_texpaint_slots(struct BlenderRNA *brna, struct StructRNA *srna); void rna_def_view_layer_common(struct BlenderRNA *brna, struct StructRNA *srna, const bool scene); +int rna_AssetMetaData_editable(struct PointerRNA *ptr, const char **r_info); PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna, const char *get, const char *set); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index a05cef7a1cd..7b57c0fd6a5 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2616,6 +2616,40 @@ static uint64_t rna_FileAssetSelectParams_asset_category_get(PointerRNA *ptr) return params->filter_id; } +static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr) +{ + const FileDirEntry *entry = ptr->data; + + /* Note that the owning ID of the RNA pointer (`ptr->owner_id`) has to be set carefully: + * Local IDs (`entry->id`) own their asset metadata themselves. Asset metadata from other blend + * files are owned by the file browser (`entry`). Only if this is set correctly, we can tell from + * the metadata RNA pointer if the metadata is stored locally and can thus be edited or not. */ + + if (entry->id) { + PointerRNA id_ptr; + RNA_id_pointer_create(entry->id, &id_ptr); + return rna_pointer_inherit_refine(&id_ptr, &RNA_AssetMetaData, entry->asset_data); + } + + return rna_pointer_inherit_refine(ptr, &RNA_AssetMetaData, entry->asset_data); +} + +static int rna_FileBrowser_FileSelectEntry_name_editable(PointerRNA *ptr, const char **r_info) +{ + const FileDirEntry *entry = ptr->data; + + /* This actually always returns 0 (the name is never editable) but we want to get a disabled + * message returned to `r_info` in some cases. */ + + if (entry->asset_data) { + PointerRNA asset_data_ptr = rna_FileBrowser_FileSelectEntry_asset_data_get(ptr); + /* Get disabled hint from asset metadata polling. */ + rna_AssetMetaData_editable(&asset_data_ptr, r_info); + } + + return 0; +} + static void rna_FileBrowser_FileSelectEntry_name_get(PointerRNA *ptr, char *value) { const FileDirEntry *entry = ptr->data; @@ -2672,12 +2706,6 @@ static int rna_FileBrowser_FileSelectEntry_preview_icon_id_get(PointerRNA *ptr) return ED_file_icon(entry); } -static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr) -{ - const FileDirEntry *entry = ptr->data; - return rna_pointer_inherit_refine(ptr, &RNA_AssetMetaData, entry->asset_data); -} - static StructRNA *rna_FileBrowser_params_typef(PointerRNA *ptr) { SpaceFile *sfile = ptr->data; @@ -6260,12 +6288,13 @@ static void rna_def_fileselect_entry(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "File Select Entry", "A file viewable in the File Browser"); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_editable_func(prop, "rna_FileBrowser_FileSelectEntry_name_editable"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_string_funcs(prop, "rna_FileBrowser_FileSelectEntry_name_get", "rna_FileBrowser_FileSelectEntry_name_length", NULL); RNA_def_property_ui_text(prop, "Name", ""); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_struct_name_property(srna, prop); prop = RNA_def_property(srna, "relative_path", PROP_STRING, PROP_NONE); From 9b12b23d0bace4056ed14ff3e3e8415eb4ff75af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 23 Sep 2021 14:56:45 +0200 Subject: [PATCH 0142/1500] Assets: add Asset Catalog system Catalogs work like directories on disk (without hard-/symlinks), in that an asset is only contained in one catalog. See T90066 for design considerations. #### Known Limitations Only a single catalog definition file (CDF), is supported, at `${ASSET_LIBRARY_ROOT}/blender_assets.cats.txt`. In the future this is to be expanded to support arbitrary CDFs (like one per blend file, one per subdirectory, etc.). The current implementation is based on the asset browser, which in practice means that the asset browser owns the `AssetCatalogService` instance for the selected asset library. In the future these instances will be accessible via a less UI-bound asset system. The UI is still very rudimentary, only showing the catalog ID for the currently selected asset. Most notably, the loaded catalogs are not shown yet. The UI is being implemented and will be merged soon. #### Catalog Identifiers Catalogs are internally identified by UUID. In older designs this was a human-readable name, which has the problem that it has to be kept in sync with its semantics (so when renaming a catalog from X to Y, the UUID can be kept the same). Since UUIDs don't communicate any human-readable information, the mapping from catalog UUID to its path (stored in the Catalog Definition File, CDF) is critical for understanding which asset is stored in which human-readable catalog. To make this less critical, and to allow manual data reconstruction after a CDF is lost/corrupted, each catalog also has a "simple name" that's stored along with the UUID. This is also stored on each asset, next to the catalog UUID. #### Writing to Disk Before saving asset catalogs to disk, the to-be-overwritten file gets inspected. Any new catalogs that are found thre are loaded to memory before writing the catalogs back to disk: - Changed catalog path: in-memory data wins - Catalogs deleted on disk: they are recreated based on in-memory data - Catalogs deleted in memory: deleted on disk as well - New catalogs on disk: are loaded and thus survive the overwriting #### Tree Design This implements the initial tree structure to load catalogs into. See T90608, and the basic design in T90066. Reviewed By: Severin Maniphest Tasks: T91552 Differential Revision: https://developer.blender.org/D12589 --- .../startup/bl_ui/space_filebrowser.py | 13 + source/blender/blenkernel/BKE_asset.h | 8 + .../blender/blenkernel/BKE_asset_catalog.hh | 252 ++++++++ source/blender/blenkernel/BKE_asset_library.h | 36 ++ .../blender/blenkernel/BKE_asset_library.hh | 41 ++ source/blender/blenkernel/CMakeLists.txt | 8 + source/blender/blenkernel/intern/asset.cc | 25 + .../blenkernel/intern/asset_catalog.cc | 573 ++++++++++++++++++ .../blenkernel/intern/asset_catalog_test.cc | 443 ++++++++++++++ .../blenkernel/intern/asset_library.cc | 53 ++ .../blenkernel/intern/asset_library_test.cc | 82 +++ .../blender/blenkernel/intern/asset_test.cc | 70 +++ source/blender/blenlib/BLI_uuid.h | 15 + source/blender/blenlib/intern/uuid.cc | 32 + .../blender/editors/space_file/CMakeLists.txt | 1 + source/blender/editors/space_file/filelist.c | 27 + source/blender/makesdna/DNA_asset_types.h | 14 + source/blender/makesdna/DNA_uuid_types.h | 6 + source/blender/makesrna/intern/rna_asset.c | 56 ++ 19 files changed, 1755 insertions(+) create mode 100644 source/blender/blenkernel/BKE_asset_catalog.hh create mode 100644 source/blender/blenkernel/BKE_asset_library.h create mode 100644 source/blender/blenkernel/BKE_asset_library.hh create mode 100644 source/blender/blenkernel/intern/asset_catalog.cc create mode 100644 source/blender/blenkernel/intern/asset_catalog_test.cc create mode 100644 source/blender/blenkernel/intern/asset_library.cc create mode 100644 source/blender/blenkernel/intern/asset_library_test.cc create mode 100644 source/blender/blenkernel/intern/asset_test.cc diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index e52136fc416..ee9a8cc3346 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -691,10 +691,23 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): if asset_file_handle.local_id: # If the active file is an ID, use its name directly so renaming is possible from right here. layout.prop(asset_file_handle.local_id, "name", text="") + + col = layout.column(align=True) + col.label(text="Asset Catalog:") + col.prop(asset_file_handle.local_id.asset_data, "catalog_id", text="UUID") + col.prop(asset_file_handle.local_id.asset_data, "catalog_simple_name", text="Simple Name") + row = layout.row() row.label(text="Source: Current File") else: layout.prop(asset_file_handle, "name", text="") + + col = layout.column(align=True) + col.enabled = False + col.label(text="Asset Catalog:") + col.prop(asset_file_handle.asset_data, "catalog_id", text="UUID") + col.prop(asset_file_handle.asset_data, "catalog_simple_name", text="Simple Name") + col = layout.column(align=True) # Just to reduce margin. col.label(text="Source:") row = col.row() diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h index 50eb2859279..7ae5378272d 100644 --- a/source/blender/blenkernel/BKE_asset.h +++ b/source/blender/blenkernel/BKE_asset.h @@ -22,6 +22,8 @@ #include "BLI_utildefines.h" +#include "DNA_asset_types.h" + #ifdef __cplusplus extern "C" { #endif @@ -46,6 +48,12 @@ struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(struct AssetMetaData * const char *name); void BKE_asset_metadata_tag_remove(struct AssetMetaData *asset_data, struct AssetTag *tag); +/** Clean up the catalog ID (whitespaces removed, length reduced, etc.) and assign it. */ +void BKE_asset_metadata_catalog_id_clear(struct AssetMetaData *asset_data); +void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data, + bUUID catalog_id, + const char *catalog_simple_name); + void BKE_asset_library_reference_init_default(struct AssetLibraryReference *library_ref); struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data, diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh new file mode 100644 index 00000000000..2bbaa4b4222 --- /dev/null +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -0,0 +1,252 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#pragma once + +#ifndef __cplusplus +# error This is a C++ header. The C interface is yet to be implemented/designed. +#endif + +#include "BLI_function_ref.hh" +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_uuid.h" +#include "BLI_vector.hh" + +#include +#include +#include + +namespace blender::bke { + +using CatalogID = bUUID; +using CatalogPath = std::string; +using CatalogPathComponent = std::string; +/* Would be nice to be able to use `std::filesystem::path` for this, but it's currently not + * available on the minimum macOS target version. */ +using CatalogFilePath = std::string; + +class AssetCatalog; +class AssetCatalogDefinitionFile; +class AssetCatalogTree; + +/* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single + * directory hierarchy). */ +class AssetCatalogService { + public: + static const char PATH_SEPARATOR; + static const CatalogFilePath DEFAULT_CATALOG_FILENAME; + + public: + AssetCatalogService() = default; + explicit AssetCatalogService(const CatalogFilePath &asset_library_root); + + /** Load asset catalog definitions from the files found in the asset library. */ + void load_from_disk(); + /** Load asset catalog definitions from the given file or directory. */ + void load_from_disk(const CatalogFilePath &file_or_directory_path); + + /** + * Write the catalog definitions to disk. + * The provided directory path is only used when there is no CDF loaded from disk yet but assets + * still have to be saved. + * + * Return true on success, which either means there were no in-memory categories to save, or the + * save was succesfful. */ + bool write_to_disk(const CatalogFilePath &directory_for_new_files); + + /** + * Merge on-disk changes into the in-memory asset catalogs. + * This should be called before writing the asset catalogs to disk. + * + * - New on-disk catalogs are loaded into memory. + * - Already-known on-disk catalogs are ignored (so will be overwritten with our in-memory + * data). This includes in-memory marked-as-deleted catalogs. + */ + void merge_from_disk_before_writing(); + + /** Return catalog with the given ID. Return nullptr if not found. */ + AssetCatalog *find_catalog(CatalogID catalog_id); + + /** Create a catalog with some sensible auto-generated catalog ID. + * The catalog will be saved to the default catalog file.*/ + AssetCatalog *create_catalog(const CatalogPath &catalog_path); + + /** + * Soft-delete the catalog, ensuring it actually gets deleted when the catalog definition file is + * written. */ + void delete_catalog(CatalogID catalog_id); + + AssetCatalogTree *get_catalog_tree(); + + /** Return true iff there are no catalogs known. */ + bool is_empty() const; + + protected: + /* These pointers are owned by this AssetCatalogService. */ + Map> catalogs_; + Map> deleted_catalogs_; + std::unique_ptr catalog_definition_file_; + std::unique_ptr catalog_tree_; + CatalogFilePath asset_library_root_; + + void load_directory_recursive(const CatalogFilePath &directory_path); + void load_single_file(const CatalogFilePath &catalog_definition_file_path); + + std::unique_ptr parse_catalog_file( + const CatalogFilePath &catalog_definition_file_path); + + /** + * Construct an in-memory catalog definition file (CDF) from the currently known catalogs. + * This object can then be processed further before saving to disk. */ + std::unique_ptr construct_cdf_in_memory( + const CatalogFilePath &file_path); + + std::unique_ptr read_into_tree(); + void rebuild_tree(); +}; + +class AssetCatalogTreeItem { + friend class AssetCatalogService; + + public: + using ChildMap = std::map; + using ItemIterFn = FunctionRef; + + AssetCatalogTreeItem(StringRef name, const AssetCatalogTreeItem *parent = nullptr); + + StringRef get_name() const; + /** Return the full catalog path, defined as the name of this catalog prefixed by the full + * catalog path of its parent and a separator. */ + CatalogPath catalog_path() const; + int count_parents() const; + + static void foreach_item_recursive(const ChildMap &children_, const ItemIterFn callback); + + protected: + /** Child tree items, ordered by their names. */ + ChildMap children_; + /** The user visible name of this component. */ + CatalogPathComponent name_; + + /** Pointer back to the parent item. Used to reconstruct the hierarchy from an item (e.g. to + * build a path). */ + const AssetCatalogTreeItem *parent_ = nullptr; +}; + +/** + * A representation of the catalog paths as tree structure. Each component of the catalog tree is + * represented by a #AssetCatalogTreeItem. + * There is no single root tree element, the #AssetCatalogTree instance itself represents the root. + */ +class AssetCatalogTree { + friend class AssetCatalogService; + + public: + void foreach_item(const AssetCatalogTreeItem::ItemIterFn callback) const; + + protected: + /** Child tree items, ordered by their names. */ + AssetCatalogTreeItem::ChildMap children_; +}; + +/** Keeps track of which catalogs are defined in a certain file on disk. + * Only contains non-owning pointers to the #AssetCatalog instances, so ensure the lifetime of this + * class is shorter than that of the #`AssetCatalog`s themselves. */ +class AssetCatalogDefinitionFile { + public: + CatalogFilePath file_path; + + AssetCatalogDefinitionFile() = default; + + /** + * Write the catalog definitions to the same file they were read from. + * Return true when the file was written correctly, false when there was a problem. + */ + bool write_to_disk() const; + /** + * Write the catalog definitions to an arbitrary file path. + * + * Any existing file is backed up to "filename~". Any previously existing backup is overwritten. + * + * Return true when the file was written correctly, false when there was a problem. + */ + bool write_to_disk(const CatalogFilePath &dest_file_path) const; + + bool contains(CatalogID catalog_id) const; + /* Add a new catalog. Undefined behaviour if a catalog with the same ID was already added. */ + void add_new(AssetCatalog *catalog); + + using AssetCatalogParsedFn = FunctionRef)>; + void parse_catalog_file(const CatalogFilePath &catalog_definition_file_path, + AssetCatalogParsedFn callback); + + protected: + /* Catalogs stored in this file. They are mapped by ID to make it possible to query whether a + * catalog is already known, without having to find the corresponding `AssetCatalog*`. */ + Map catalogs_; + + std::unique_ptr parse_catalog_line(StringRef line); + + /** + * Write the catalog definitions to the given file path. + * Return true when the file was written correctly, false when there was a problem. + */ + bool write_to_disk_unsafe(const CatalogFilePath &dest_file_path) const; + bool ensure_directory_exists(const CatalogFilePath directory_path) const; +}; + +/** Asset Catalog definition, containing a symbolic ID and a path that points to a node in the + * catalog hierarchy. */ +class AssetCatalog { + public: + AssetCatalog() = default; + AssetCatalog(CatalogID catalog_id, const CatalogPath &path, const std::string &simple_name); + + CatalogID catalog_id; + CatalogPath path; + /** + * Simple, human-readable name for the asset catalog. This is stored on assets alongside the + * catalog ID; the catalog ID is a UUID that is not human-readable, so to avoid complete dataloss + * when the catalog definition file gets lost, we also store a human-readable simple name for the + * catalog. */ + std::string simple_name; + + struct Flags { + /* Treat this catalog as deleted. Keeping deleted catalogs around is necessary to support + * merging of on-disk changes with in-memory changes. */ + bool is_deleted = false; + } flags; + + /** + * Create a new Catalog with the given path, auto-generating a sensible catalog simplename. + * + * NOTE: the given path will be cleaned up (trailing spaces removed, etc.), so the returned + * `AssetCatalog`'s path differ from the given one. + */ + static std::unique_ptr from_path(const CatalogPath &path); + static CatalogPath cleanup_path(const CatalogPath &path); + + protected: + /** Generate a sensible catalog ID for the given path. */ + static std::string sensible_simple_name_for_path(const CatalogPath &path); +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_asset_library.h b/source/blender/blenkernel/BKE_asset_library.h new file mode 100644 index 00000000000..709b915f9ff --- /dev/null +++ b/source/blender/blenkernel/BKE_asset_library.h @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** Forward declaration, defined in intern/asset_library.hh */ +typedef struct AssetLibrary AssetLibrary; + +/** TODO(@sybren): properly have a think/discussion about the API for this. */ +struct AssetLibrary *BKE_asset_library_load(const char *library_path); +void BKE_asset_library_free(struct AssetLibrary *asset_library); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh new file mode 100644 index 00000000000..68f7481574e --- /dev/null +++ b/source/blender/blenkernel/BKE_asset_library.hh @@ -0,0 +1,41 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#pragma once + +#ifndef __cplusplus +# error This is a C++-only header file. Use BKE_asset_library.h instead. +#endif + +#include "BKE_asset_library.h" + +#include "BKE_asset_catalog.hh" + +#include + +namespace blender::bke { + +struct AssetLibrary { + std::unique_ptr catalog_service; + + void load(StringRefNull library_root_directory); +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index de7864ef36a..d04d4558fed 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -83,6 +83,8 @@ set(SRC intern/armature_pose.cc intern/armature_selection.cc intern/armature_update.c + intern/asset_catalog.cc + intern/asset_library.cc intern/asset.cc intern/attribute.c intern/attribute_access.cc @@ -302,6 +304,9 @@ set(SRC BKE_appdir.h BKE_armature.h BKE_armature.hh + BKE_asset_catalog.hh + BKE_asset_library.h + BKE_asset_library.hh BKE_asset.h BKE_attribute.h BKE_attribute_access.hh @@ -783,6 +788,9 @@ if(WITH_GTESTS) set(TEST_SRC intern/action_test.cc intern/armature_test.cc + intern/asset_catalog_test.cc + intern/asset_library_test.cc + intern/asset_test.cc intern/cryptomatte_test.cc intern/fcurve_test.cc intern/lattice_deform_test.cc diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc index f74018b20c5..ac9bcccc8bd 100644 --- a/source/blender/blenkernel/intern/asset.cc +++ b/source/blender/blenkernel/intern/asset.cc @@ -26,8 +26,10 @@ #include "BLI_listbase.h" #include "BLI_string.h" +#include "BLI_string_ref.hh" #include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "BLI_uuid.h" #include "BKE_asset.h" #include "BKE_icons.h" @@ -37,6 +39,8 @@ #include "MEM_guardedalloc.h" +using namespace blender; + AssetMetaData *BKE_asset_metadata_create(void) { AssetMetaData *asset_data = (AssetMetaData *)MEM_callocN(sizeof(*asset_data), __func__); @@ -115,6 +119,27 @@ void BKE_asset_library_reference_init_default(AssetLibraryReference *library_ref memcpy(library_ref, DNA_struct_default_get(AssetLibraryReference), sizeof(*library_ref)); } +void BKE_asset_metadata_catalog_id_clear(struct AssetMetaData *asset_data) +{ + asset_data->catalog_id = BLI_uuid_nil(); + asset_data->catalog_simple_name[0] = '\0'; +} + +void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data, + const bUUID catalog_id, + const char *catalog_simple_name) +{ + asset_data->catalog_id = catalog_id; + + constexpr size_t max_simple_name_length = sizeof(asset_data->catalog_simple_name); + + /* The substr() call is necessary to make copy() copy the first N characters (instead of refusing + * to copy and producing an empty string). */ + StringRef trimmed_id = + StringRef(catalog_simple_name).trim().substr(0, max_simple_name_length - 1); + trimmed_id.copy(asset_data->catalog_simple_name, max_simple_name_length); +} + /* Queries -------------------------------------------- */ PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data), diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc new file mode 100644 index 00000000000..0c64a0b085c --- /dev/null +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -0,0 +1,573 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#include "BKE_asset_catalog.hh" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" +#include "BLI_string_ref.hh" + +/* For S_ISREG() and S_ISDIR() on Windows. */ +#ifdef WIN32 +# include "BLI_winstuff.h" +#endif + +#include + +namespace blender::bke { + +const char AssetCatalogService::PATH_SEPARATOR = '/'; +const CatalogFilePath AssetCatalogService::DEFAULT_CATALOG_FILENAME = "blender_assets.cats.txt"; + +AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_root) + : asset_library_root_(asset_library_root) +{ +} + +bool AssetCatalogService::is_empty() const +{ + return catalogs_.is_empty(); +} + +AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) +{ + std::unique_ptr *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id); + if (catalog_uptr_ptr == nullptr) { + return nullptr; + } + return catalog_uptr_ptr->get(); +} + +void AssetCatalogService::delete_catalog(CatalogID catalog_id) +{ + std::unique_ptr *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id); + if (catalog_uptr_ptr == nullptr) { + /* Catalog cannot be found, which is fine. */ + return; + } + + /* Mark the catalog as deleted. */ + AssetCatalog *catalog = catalog_uptr_ptr->get(); + catalog->flags.is_deleted = true; + + /* Move ownership from this->catalogs_ to this->deleted_catalogs_. */ + this->deleted_catalogs_.add(catalog_id, std::move(*catalog_uptr_ptr)); + + /* The catalog can now be removed from the map without freeing the actual AssetCatalog. */ + this->catalogs_.remove(catalog_id); + + this->rebuild_tree(); +} + +AssetCatalog *AssetCatalogService::create_catalog(const CatalogPath &catalog_path) +{ + std::unique_ptr catalog = AssetCatalog::from_path(catalog_path); + + /* So we can std::move(catalog) and still use the non-owning pointer: */ + AssetCatalog *const catalog_ptr = catalog.get(); + + /* TODO(@sybren): move the `AssetCatalog::from_path()` function to another place, that can reuse + * catalogs when a catalog with the given path is already known, and avoid duplicate catalog IDs. + */ + BLI_assert_msg(!catalogs_.contains(catalog->catalog_id), "duplicate catalog ID not supported"); + catalogs_.add_new(catalog->catalog_id, std::move(catalog)); + + if (catalog_definition_file_) { + /* Ensure the new catalog gets written to disk at some point. If there is no CDF in memory yet, + * it's enough to have the catalog known to the service as it'll be saved to a new file. */ + catalog_definition_file_->add_new(catalog_ptr); + } + + return catalog_ptr; +} + +static std::string asset_definition_default_file_path_from_dir(StringRef asset_library_root) +{ + char file_path[PATH_MAX]; + BLI_join_dirfile(file_path, + sizeof(file_path), + asset_library_root.data(), + AssetCatalogService::DEFAULT_CATALOG_FILENAME.data()); + return file_path; +} + +void AssetCatalogService::load_from_disk() +{ + load_from_disk(asset_library_root_); +} + +void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_directory_path) +{ + BLI_stat_t status; + if (BLI_stat(file_or_directory_path.data(), &status) == -1) { + // TODO(@sybren): throw an appropriate exception. + return; + } + + if (S_ISREG(status.st_mode)) { + load_single_file(file_or_directory_path); + } + else if (S_ISDIR(status.st_mode)) { + load_directory_recursive(file_or_directory_path); + } + else { + // TODO(@sybren): throw an appropriate exception. + } + + /* TODO: Should there be a sanitize step? E.g. to remove catalogs with identical paths? */ + + catalog_tree_ = read_into_tree(); +} + +void AssetCatalogService::load_directory_recursive(const CatalogFilePath &directory_path) +{ + // TODO(@sybren): implement proper multi-file support. For now, just load + // the default file if it is there. + CatalogFilePath file_path = asset_definition_default_file_path_from_dir(directory_path); + + if (!BLI_exists(file_path.data())) { + /* No file to be loaded is perfectly fine. */ + return; + } + + this->load_single_file(file_path); +} + +void AssetCatalogService::load_single_file(const CatalogFilePath &catalog_definition_file_path) +{ + /* TODO(@sybren): check that #catalog_definition_file_path is contained in #asset_library_root_, + * otherwise some assumptions may fail. */ + std::unique_ptr cdf = parse_catalog_file( + catalog_definition_file_path); + + BLI_assert_msg(!this->catalog_definition_file_, + "Only loading of a single catalog definition file is supported."); + this->catalog_definition_file_ = std::move(cdf); +} + +std::unique_ptr AssetCatalogService::parse_catalog_file( + const CatalogFilePath &catalog_definition_file_path) +{ + auto cdf = std::make_unique(); + cdf->file_path = catalog_definition_file_path; + + auto catalog_parsed_callback = [this, catalog_definition_file_path]( + std::unique_ptr catalog) { + if (this->catalogs_.contains(catalog->catalog_id)) { + // TODO(@sybren): apparently another CDF was already loaded. This is not supported yet. + std::cerr << catalog_definition_file_path << ": multiple definitions of catalog " + << catalog->catalog_id << " in multiple files, ignoring this one." << std::endl; + /* Don't store 'catalog'; unique_ptr will free its memory. */ + return false; + } + + /* The AssetCatalog pointer is now owned by the AssetCatalogService. */ + this->catalogs_.add_new(catalog->catalog_id, std::move(catalog)); + return true; + }; + + cdf->parse_catalog_file(cdf->file_path, catalog_parsed_callback); + + return cdf; +} + +void AssetCatalogService::merge_from_disk_before_writing() +{ + /* TODO(Sybren): expand to support multiple CDFs. */ + + if (!catalog_definition_file_ || catalog_definition_file_->file_path.empty() || + !BLI_is_file(catalog_definition_file_->file_path.c_str())) { + return; + } + + auto catalog_parsed_callback = [this](std::unique_ptr catalog) { + const bUUID catalog_id = catalog->catalog_id; + + /* The following two conditions could be or'ed together. Keeping them separated helps when + * adding debug prints, breakpoints, etc. */ + if (this->catalogs_.contains(catalog_id)) { + /* This catalog was already seen, so just ignore it. */ + return false; + } + if (this->deleted_catalogs_.contains(catalog_id)) { + /* This catalog was already seen and subsequently deleted, so just ignore it. */ + return false; + } + + /* This is a new catalog, so let's keep it around. */ + this->catalogs_.add_new(catalog_id, std::move(catalog)); + return true; + }; + + catalog_definition_file_->parse_catalog_file(catalog_definition_file_->file_path, + catalog_parsed_callback); +} + +bool AssetCatalogService::write_to_disk(const CatalogFilePath &directory_for_new_files) +{ + /* TODO(Sybren): expand to support multiple CDFs. */ + + if (!catalog_definition_file_) { + if (catalogs_.is_empty() && deleted_catalogs_.is_empty()) { + /* Avoid saving anything, when there is nothing to save. */ + return true; /* Writing nothing when there is nothing to write is still a success. */ + } + + /* A CDF has to be created to contain all current in-memory catalogs. */ + const CatalogFilePath cdf_path = asset_definition_default_file_path_from_dir( + directory_for_new_files); + catalog_definition_file_ = construct_cdf_in_memory(cdf_path); + } + + merge_from_disk_before_writing(); + return catalog_definition_file_->write_to_disk(); +} + +std::unique_ptr AssetCatalogService::construct_cdf_in_memory( + const CatalogFilePath &file_path) +{ + auto cdf = std::make_unique(); + cdf->file_path = file_path; + + for (auto &catalog : catalogs_.values()) { + cdf->add_new(catalog.get()); + } + + return cdf; +} + +std::unique_ptr AssetCatalogService::read_into_tree() +{ + auto tree = std::make_unique(); + + /* Go through the catalogs, insert each path component into the tree where needed. */ + for (auto &catalog : catalogs_.values()) { + const AssetCatalogTreeItem *parent = nullptr; + AssetCatalogTreeItem::ChildMap *insert_to_map = &tree->children_; + + BLI_assert_msg(!ELEM(catalog->path[0], '/', '\\'), + "Malformed catalog path: Path should be formatted like a relative path"); + + const char *next_slash_ptr; + /* Looks more complicated than it is, this just iterates over path components. E.g. + * "just/some/path" iterates over "just", then "some" then "path". */ + for (const char *name_begin = catalog->path.data(); name_begin && name_begin[0]; + /* Jump to one after the next slash if there is any. */ + name_begin = next_slash_ptr ? next_slash_ptr + 1 : nullptr) { + next_slash_ptr = BLI_path_slash_find(name_begin); + + /* Note that this won't be null terminated. */ + StringRef component_name = next_slash_ptr ? + StringRef(name_begin, next_slash_ptr - name_begin) : + /* Last component in the path. */ + name_begin; + + /* Insert new tree element - if no matching one is there yet! */ + auto [item, was_inserted] = insert_to_map->emplace( + component_name, AssetCatalogTreeItem(component_name, parent)); + + /* Walk further into the path (no matter if a new item was created or not). */ + parent = &item->second; + insert_to_map = &item->second.children_; + } + } + + return tree; +} + +void AssetCatalogService::rebuild_tree() +{ + this->catalog_tree_ = read_into_tree(); +} + +AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name, const AssetCatalogTreeItem *parent) + : name_(name), parent_(parent) +{ +} + +StringRef AssetCatalogTreeItem::get_name() const +{ + return name_; +} + +CatalogPath AssetCatalogTreeItem::catalog_path() const +{ + std::string current_path = name_; + for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) { + current_path = parent->name_ + AssetCatalogService::PATH_SEPARATOR + current_path; + } + return current_path; +} + +int AssetCatalogTreeItem::count_parents() const +{ + int i = 0; + for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) { + i++; + } + return i; +} + +void AssetCatalogTree::foreach_item(const AssetCatalogTreeItem::ItemIterFn callback) const +{ + AssetCatalogTreeItem::foreach_item_recursive(children_, callback); +} + +void AssetCatalogTreeItem::foreach_item_recursive(const AssetCatalogTreeItem::ChildMap &children, + const ItemIterFn callback) +{ + for (const auto &[key, item] : children) { + callback(item); + foreach_item_recursive(item.children_, callback); + } +} + +AssetCatalogTree *AssetCatalogService::get_catalog_tree() +{ + return catalog_tree_.get(); +} + +bool AssetCatalogDefinitionFile::contains(const CatalogID catalog_id) const +{ + return catalogs_.contains(catalog_id); +} + +void AssetCatalogDefinitionFile::add_new(AssetCatalog *catalog) +{ + catalogs_.add_new(catalog->catalog_id, catalog); +} + +void AssetCatalogDefinitionFile::parse_catalog_file( + const CatalogFilePath &catalog_definition_file_path, + AssetCatalogParsedFn catalog_loaded_callback) +{ + std::fstream infile(catalog_definition_file_path); + std::string line; + while (std::getline(infile, line)) { + const StringRef trimmed_line = StringRef(line).trim(); + if (trimmed_line.is_empty() || trimmed_line[0] == '#') { + continue; + } + + std::unique_ptr catalog = this->parse_catalog_line(trimmed_line); + if (!catalog) { + continue; + } + + AssetCatalog *non_owning_ptr = catalog.get(); + const bool keep_catalog = catalog_loaded_callback(std::move(catalog)); + if (!keep_catalog) { + continue; + } + + if (this->contains(non_owning_ptr->catalog_id)) { + std::cerr << catalog_definition_file_path << ": multiple definitions of catalog " + << non_owning_ptr->catalog_id << " in the same file, using first occurrence." + << std::endl; + /* Don't store 'catalog'; unique_ptr will free its memory. */ + continue; + } + + /* The AssetDefinitionFile should include this catalog when writing it back to disk. */ + this->add_new(non_owning_ptr); + } +} + +std::unique_ptr AssetCatalogDefinitionFile::parse_catalog_line(const StringRef line) +{ + const char delim = ':'; + const int64_t first_delim = line.find_first_of(delim); + if (first_delim == StringRef::not_found) { + std::cerr << "Invalid line in " << this->file_path << ": " << line << std::endl; + return std::unique_ptr(nullptr); + } + + /* Parse the catalog ID. */ + const std::string id_as_string = line.substr(0, first_delim).trim(); + bUUID catalog_id; + const bool uuid_parsed_ok = BLI_uuid_parse_string(&catalog_id, id_as_string.c_str()); + if (!uuid_parsed_ok) { + std::cerr << "Invalid UUID in " << this->file_path << ": " << line << std::endl; + return std::unique_ptr(nullptr); + } + + /* Parse the path and simple name. */ + const StringRef path_and_simple_name = line.substr(first_delim + 1); + const int64_t second_delim = path_and_simple_name.find_first_of(delim); + + CatalogPath catalog_path; + std::string simple_name; + if (second_delim == 0) { + /* Delimiter as first character means there is no path. These lines are to be ignored. */ + return std::unique_ptr(nullptr); + } + + if (second_delim == StringRef::not_found) { + /* No delimiter means no simple name, just treat it as all "path". */ + catalog_path = path_and_simple_name; + simple_name = ""; + } + else { + catalog_path = path_and_simple_name.substr(0, second_delim); + simple_name = path_and_simple_name.substr(second_delim + 1).trim(); + } + + catalog_path = AssetCatalog::cleanup_path(catalog_path); + return std::make_unique(catalog_id, catalog_path, simple_name); +} + +bool AssetCatalogDefinitionFile::write_to_disk() const +{ + BLI_assert_msg(!this->file_path.empty(), "Writing to CDF requires its file path to be known"); + return this->write_to_disk(this->file_path); +} + +bool AssetCatalogDefinitionFile::write_to_disk(const CatalogFilePath &dest_file_path) const +{ + const CatalogFilePath writable_path = dest_file_path + ".writing"; + const CatalogFilePath backup_path = dest_file_path + "~"; + + if (!this->write_to_disk_unsafe(writable_path)) { + /* TODO: communicate what went wrong. */ + return false; + } + if (BLI_exists(dest_file_path.c_str())) { + if (BLI_rename(dest_file_path.c_str(), backup_path.c_str())) { + /* TODO: communicate what went wrong. */ + return false; + } + } + if (BLI_rename(writable_path.c_str(), dest_file_path.c_str())) { + /* TODO: communicate what went wrong. */ + return false; + } + + return true; +} + +bool AssetCatalogDefinitionFile::write_to_disk_unsafe(const CatalogFilePath &dest_file_path) const +{ + char directory[PATH_MAX]; + BLI_split_dir_part(dest_file_path.c_str(), directory, sizeof(directory)); + if (!ensure_directory_exists(directory)) { + /* TODO(Sybren): pass errors to the UI somehow. */ + return false; + } + + std::ofstream output(dest_file_path); + + // TODO(@sybren): remember the line ending style that was originally read, then use that to write + // the file again. + + // Write the header. + // TODO(@sybren): move the header definition to some other place. + output << "# This is an Asset Catalog Definition file for Blender." << std::endl; + output << "#" << std::endl; + output << "# Empty lines and lines starting with `#` will be ignored." << std::endl; + output << "# Other lines are of the format \"UUID:catalog/path/for/assets:simple catalog name\"" + << std::endl; + output << "" << std::endl; + + // Write the catalogs. + // TODO(@sybren): order them by Catalog ID or Catalog Path. + for (const auto &catalog : catalogs_.values()) { + if (catalog->flags.is_deleted) { + continue; + } + output << catalog->catalog_id << ":" << catalog->path << ":" << catalog->simple_name + << std::endl; + } + output.close(); + return !output.bad(); +} + +bool AssetCatalogDefinitionFile::ensure_directory_exists( + const CatalogFilePath directory_path) const +{ + /* TODO(@sybren): design a way to get such errors presented to users (or ensure that they never + * occur). */ + if (directory_path.empty()) { + std::cerr + << "AssetCatalogService: no asset library root configured, unable to ensure it exists." + << std::endl; + return false; + } + + if (BLI_exists(directory_path.data())) { + if (!BLI_is_dir(directory_path.data())) { + std::cerr << "AssetCatalogService: " << directory_path + << " exists but is not a directory, this is not a supported situation." + << std::endl; + return false; + } + + /* Root directory exists, work is done. */ + return true; + } + + /* Ensure the root directory exists. */ + std::error_code err_code; + if (!BLI_dir_create_recursive(directory_path.data())) { + std::cerr << "AssetCatalogService: error creating directory " << directory_path << ": " + << err_code << std::endl; + return false; + } + + /* Root directory has been created, work is done. */ + return true; +} + +AssetCatalog::AssetCatalog(const CatalogID catalog_id, + const CatalogPath &path, + const std::string &simple_name) + : catalog_id(catalog_id), path(path), simple_name(simple_name) +{ +} + +std::unique_ptr AssetCatalog::from_path(const CatalogPath &path) +{ + const CatalogPath clean_path = cleanup_path(path); + const CatalogID cat_id = BLI_uuid_generate_random(); + const std::string simple_name = sensible_simple_name_for_path(clean_path); + auto catalog = std::make_unique(cat_id, clean_path, simple_name); + return catalog; +} + +std::string AssetCatalog::sensible_simple_name_for_path(const CatalogPath &path) +{ + std::string name = path; + std::replace(name.begin(), name.end(), AssetCatalogService::PATH_SEPARATOR, '-'); + if (name.length() < MAX_NAME - 1) { + return name; + } + + /* Trim off the start of the path, as that's the most generic part and thus contains the least + * information. */ + return "..." + name.substr(name.length() - 60); +} + +CatalogPath AssetCatalog::cleanup_path(const CatalogPath &path) +{ + /* TODO(@sybren): maybe go over each element of the path, and trim those? */ + CatalogPath clean_path = StringRef(path).trim().trim(AssetCatalogService::PATH_SEPARATOR).trim(); + return clean_path; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc new file mode 100644 index 00000000000..0f389999d6d --- /dev/null +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -0,0 +1,443 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation + * All rights reserved. + */ + +#include "BKE_appdir.h" +#include "BKE_asset_catalog.hh" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" + +#include "testing/testing.h" + +namespace blender::bke::tests { + +/* UUIDs from lib/tests/asset_library/blender_assets.cats.txt */ +const bUUID UUID_ID_WITHOUT_PATH("e34dd2c5-5d2e-4668-9794-1db5de2a4f71"); +const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); +const bUUID UUID_POSES_ELLIE_WHITESPACE("b06132f6-5687-4751-a6dd-392740eb3c46"); +const bUUID UUID_POSES_ELLIE_TRAILING_SLASH("3376b94b-a28d-4d05-86c1-bf30b937130d"); +const bUUID UUID_POSES_RUZENA("79a4f887-ab60-4bd4-94da-d572e27d6aed"); +const bUUID UUID_POSES_RUZENA_HAND("81811c31-1a88-4bd7-bb34-c6fc2607a12e"); +const bUUID UUID_POSES_RUZENA_FACE("82162c1f-06cc-4d91-a9bf-4f72c104e348"); +const bUUID UUID_WITHOUT_SIMPLENAME("d7916a31-6ca9-4909-955f-182ca2b81fa3"); + +/* UUIDs from lib/tests/asset_library/modified_assets.cats.txt */ +const bUUID UUID_AGENT_47("c5744ba5-43f5-4f73-8e52-010ad4a61b34"); + +/* Subclass that adds accessors such that protected fields can be used in tests. */ +class TestableAssetCatalogService : public AssetCatalogService { + public: + explicit TestableAssetCatalogService(const CatalogFilePath &asset_library_root) + : AssetCatalogService(asset_library_root) + { + } + + AssetCatalogDefinitionFile *get_catalog_definition_file() + { + return catalog_definition_file_.get(); + } +}; + +class AssetCatalogTest : public testing::Test { + protected: + CatalogFilePath asset_library_root_; + CatalogFilePath temp_library_path_; + + void SetUp() override + { + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + + asset_library_root_ = test_files_dir + "/" + "asset_library"; + temp_library_path_ = ""; + } + + /* Register a temporary path, which will be removed at the end of the test. + * The returned path ends in a slash. */ + CatalogFilePath use_temp_path() + { + BKE_tempdir_init(""); + const CatalogFilePath tempdir = BKE_tempdir_session(); + temp_library_path_ = tempdir + "test-temporary-path/"; + return temp_library_path_; + } + + CatalogFilePath create_temp_path() + { + CatalogFilePath path = use_temp_path(); + BLI_dir_create_recursive(path.c_str()); + return path; + } + + struct CatalogPathInfo { + StringRef name; + int parent_count; + }; + + void assert_expected_tree_items(AssetCatalogTree *tree, + const std::vector &expected_paths) + { + int i = 0; + tree->foreach_item([&](const AssetCatalogTreeItem &actual_item) { + ASSERT_LT(i, expected_paths.size()) + << "More catalogs in tree than expected; did not expect " << actual_item.catalog_path(); + + char expected_filename[FILE_MAXFILE]; + /* Is the catalog name as expected? "character", "Ellie", ... */ + BLI_split_file_part( + expected_paths[i].name.data(), expected_filename, sizeof(expected_filename)); + EXPECT_EQ(expected_filename, actual_item.get_name()); + /* Does the computed number of parents match? */ + EXPECT_EQ(expected_paths[i].parent_count, actual_item.count_parents()); + EXPECT_EQ(expected_paths[i].name, actual_item.catalog_path()); + + i++; + }); + } + + void TearDown() override + { + if (!temp_library_path_.empty()) { + BLI_delete(temp_library_path_.c_str(), true, true); + temp_library_path_ = ""; + } + } +}; + +TEST_F(AssetCatalogTest, load_single_file) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + // Test getting a non-existant catalog ID. + EXPECT_EQ(nullptr, service.find_catalog(BLI_uuid_generate_random())); + + // Test getting an invalid catalog (without path definition). + AssetCatalog *cat_without_path = service.find_catalog(UUID_ID_WITHOUT_PATH); + ASSERT_EQ(nullptr, cat_without_path); + + // Test getting a regular catalog. + AssetCatalog *poses_ellie = service.find_catalog(UUID_POSES_ELLIE); + ASSERT_NE(nullptr, poses_ellie); + EXPECT_EQ(UUID_POSES_ELLIE, poses_ellie->catalog_id); + EXPECT_EQ("character/Ellie/poselib", poses_ellie->path); + EXPECT_EQ("POSES_ELLIE", poses_ellie->simple_name); + + // Test whitespace stripping and support in the path. + AssetCatalog *poses_whitespace = service.find_catalog(UUID_POSES_ELLIE_WHITESPACE); + ASSERT_NE(nullptr, poses_whitespace); + EXPECT_EQ(UUID_POSES_ELLIE_WHITESPACE, poses_whitespace->catalog_id); + EXPECT_EQ("character/Ellie/poselib/white space", poses_whitespace->path); + EXPECT_EQ("POSES_ELLIE WHITESPACE", poses_whitespace->simple_name); + + // Test getting a UTF-8 catalog ID. + AssetCatalog *poses_ruzena = service.find_catalog(UUID_POSES_RUZENA); + ASSERT_NE(nullptr, poses_ruzena); + EXPECT_EQ(UUID_POSES_RUZENA, poses_ruzena->catalog_id); + EXPECT_EQ("character/Ružena/poselib", poses_ruzena->path); + EXPECT_EQ("POSES_RUŽENA", poses_ruzena->simple_name); +} + +TEST_F(AssetCatalogTest, load_single_file_into_tree) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + /* Contains not only paths from the CDF but also the missing parents (implicitly defined + * catalogs). */ + std::vector expected_paths{ + {"character", 0}, + {"character/Ellie", 1}, + {"character/Ellie/poselib", 2}, + {"character/Ellie/poselib/white space", 3}, + {"character/Ružena", 1}, + {"character/Ružena/poselib", 2}, + {"character/Ružena/poselib/face", 3}, + {"character/Ružena/poselib/hand", 3}, + {"path", 0}, // Implicit. + {"path/without", 1}, // Implicit. + {"path/without/simplename", 2}, // From CDF. + }; + + AssetCatalogTree *tree = service.get_catalog_tree(); + assert_expected_tree_items(tree, expected_paths); +} + +TEST_F(AssetCatalogTest, write_single_file) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + const CatalogFilePath save_to_path = use_temp_path() + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + cdf->write_to_disk(save_to_path); + + AssetCatalogService loaded_service(save_to_path); + loaded_service.load_from_disk(); + + // Test that the expected catalogs are there. + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); + + // Test that the invalid catalog definition wasn't copied. + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_ID_WITHOUT_PATH)); + + // TODO(@sybren): test ordering of catalogs in the file. +} + +TEST_F(AssetCatalogTest, no_writing_empty_files) +{ + const CatalogFilePath temp_lib_root = create_temp_path(); + AssetCatalogService service(temp_lib_root); + service.write_to_disk(temp_lib_root); + + const CatalogFilePath default_cdf_path = temp_lib_root + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + EXPECT_FALSE(BLI_exists(default_cdf_path.c_str())); +} + +TEST_F(AssetCatalogTest, create_first_catalog_from_scratch) +{ + /* Even from scratch a root directory should be known. */ + const CatalogFilePath temp_lib_root = use_temp_path(); + AssetCatalogService service; + + /* Just creating the service should NOT create the path. */ + EXPECT_FALSE(BLI_exists(temp_lib_root.c_str())); + + AssetCatalog *cat = service.create_catalog("some/catalog/path"); + ASSERT_NE(nullptr, cat); + EXPECT_EQ(cat->path, "some/catalog/path"); + EXPECT_EQ(cat->simple_name, "some-catalog-path"); + + /* Creating a new catalog should not save anything to disk yet. */ + EXPECT_FALSE(BLI_exists(temp_lib_root.c_str())); + + /* Writing to disk should create the directory + the default file. */ + service.write_to_disk(temp_lib_root); + EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str())); + + const CatalogFilePath definition_file_path = temp_lib_root + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + EXPECT_TRUE(BLI_is_file(definition_file_path.c_str())); + + AssetCatalogService loaded_service(temp_lib_root); + loaded_service.load_from_disk(); + + // Test that the expected catalog is there. + AssetCatalog *written_cat = loaded_service.find_catalog(cat->catalog_id); + ASSERT_NE(nullptr, written_cat); + EXPECT_EQ(written_cat->catalog_id, cat->catalog_id); + EXPECT_EQ(written_cat->path, cat->path); +} + +TEST_F(AssetCatalogTest, create_catalog_after_loading_file) +{ + const CatalogFilePath temp_lib_root = create_temp_path(); + + /* Copy the asset catalog definition files to a separate location, so that we can test without + * overwriting the test file in SVN. */ + const CatalogFilePath default_catalog_path = asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + const CatalogFilePath writable_catalog_path = temp_lib_root + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + BLI_copy(default_catalog_path.c_str(), writable_catalog_path.c_str()); + EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str())); + EXPECT_TRUE(BLI_is_file(writable_catalog_path.c_str())); + + TestableAssetCatalogService service(temp_lib_root); + service.load_from_disk(); + EXPECT_EQ(writable_catalog_path, service.get_catalog_definition_file()->file_path); + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_ELLIE)) << "expected catalogs to be loaded"; + + /* This should create a new catalog but not write to disk. */ + const AssetCatalog *new_catalog = service.create_catalog("new/catalog"); + const bUUID new_catalog_id = new_catalog->catalog_id; + + /* Reload the on-disk catalog file. */ + TestableAssetCatalogService loaded_service(temp_lib_root); + loaded_service.load_from_disk(); + EXPECT_EQ(writable_catalog_path, loaded_service.get_catalog_definition_file()->file_path); + + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)) + << "expected pre-existing catalogs to be kept in the file"; + EXPECT_EQ(nullptr, loaded_service.find_catalog(new_catalog_id)) + << "expecting newly added catalog to not yet be saved to " << temp_lib_root; + + /* Write and reload the catalog file. */ + service.write_to_disk(temp_lib_root.c_str()); + AssetCatalogService reloaded_service(temp_lib_root); + reloaded_service.load_from_disk(); + EXPECT_NE(nullptr, reloaded_service.find_catalog(UUID_POSES_ELLIE)) + << "expected pre-existing catalogs to be kept in the file"; + EXPECT_NE(nullptr, reloaded_service.find_catalog(new_catalog_id)) + << "expecting newly added catalog to exist in the file"; +} + +TEST_F(AssetCatalogTest, create_catalog_path_cleanup) +{ + const CatalogFilePath temp_lib_root = use_temp_path(); + AssetCatalogService service(temp_lib_root); + AssetCatalog *cat = service.create_catalog(" /some/path / "); + + EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id)); + EXPECT_EQ("some/path", cat->path); + EXPECT_EQ("some-path", cat->simple_name); +} + +TEST_F(AssetCatalogTest, create_catalog_simple_name) +{ + const CatalogFilePath temp_lib_root = use_temp_path(); + AssetCatalogService service(temp_lib_root); + AssetCatalog *cat = service.create_catalog( + "production/Spite Fright/Characters/Victora/Pose Library/Approved/Body Parts/Hands"); + + EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id)); + EXPECT_EQ("production/Spite Fright/Characters/Victora/Pose Library/Approved/Body Parts/Hands", + cat->path); + EXPECT_EQ("...ht-Characters-Victora-Pose Library-Approved-Body Parts-Hands", cat->simple_name); +} + +TEST_F(AssetCatalogTest, delete_catalog_leaf) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + /* Delete a leaf catalog, i.e. one that is not a parent of another catalog. + * This keeps this particular test easy. */ + service.delete_catalog(UUID_POSES_RUZENA_HAND); + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_HAND)); + + /* Contains not only paths from the CDF but also the missing parents (implicitly defined + * catalogs). This is why a leaf catalog was deleted. */ + std::vector expected_paths{ + {"character", 0}, + {"character/Ellie", 1}, + {"character/Ellie/poselib", 2}, + {"character/Ellie/poselib/white space", 3}, + {"character/Ružena", 1}, + {"character/Ružena/poselib", 2}, + {"character/Ružena/poselib/face", 3}, + // {"character/Ružena/poselib/hand", 3}, // this is the deleted one + {"path", 0}, + {"path/without", 1}, + {"path/without/simplename", 2}, + }; + + AssetCatalogTree *tree = service.get_catalog_tree(); + assert_expected_tree_items(tree, expected_paths); +} + +TEST_F(AssetCatalogTest, delete_catalog_write_to_disk) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + service.delete_catalog(UUID_POSES_ELLIE); + + const CatalogFilePath save_to_path = use_temp_path(); + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + cdf->write_to_disk(save_to_path + "/" + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + AssetCatalogService loaded_service(save_to_path); + loaded_service.load_from_disk(); + + // Test that the expected catalogs are there, except the deleted one. + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); +} + +TEST_F(AssetCatalogTest, merge_catalog_files) +{ + const CatalogFilePath cdf_dir = create_temp_path(); + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath modified_cdf_file = asset_library_root_ + "/modified_assets.cats.txt"; + const CatalogFilePath temp_cdf_file = cdf_dir + "blender_assets.cats.txt"; + BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str()); + + // Load the unmodified, original CDF. + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(cdf_dir); + + // Copy a modified file, to mimick a situation where someone changed the CDF after we loaded it. + BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str()); + + // Overwrite the modified file. This should merge the on-disk file with our catalogs. + service.write_to_disk(cdf_dir); + + AssetCatalogService loaded_service(cdf_dir); + loaded_service.load_from_disk(); + + // Test that the expected catalogs are there. + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_AGENT_47)); // New in the modified file. + + // When there are overlaps, the in-memory (i.e. last-saved) paths should win. + const AssetCatalog *ruzena_face = loaded_service.find_catalog(UUID_POSES_RUZENA_FACE); + EXPECT_EQ("character/Ružena/poselib/face", ruzena_face->path); +} + +TEST_F(AssetCatalogTest, backups) +{ + const CatalogFilePath cdf_dir = create_temp_path(); + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath writable_cdf_file = cdf_dir + "/blender_assets.cats.txt"; + BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str()); + + /* Read a CDF, modify, and write it. */ + AssetCatalogService service(cdf_dir); + service.load_from_disk(); + service.delete_catalog(UUID_POSES_ELLIE); + service.write_to_disk(cdf_dir); + + const CatalogFilePath backup_path = writable_cdf_file + "~"; + ASSERT_TRUE(BLI_is_file(backup_path.c_str())); + + AssetCatalogService loaded_service; + loaded_service.load_from_disk(backup_path); + + // Test that the expected catalogs are there, including the deleted one. + // This is the backup, after all. + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc new file mode 100644 index 00000000000..1153e7b29f5 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -0,0 +1,53 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#include "BKE_asset_library.hh" + +#include "MEM_guardedalloc.h" + +#include + +/** + * Loading an asset library at this point only means loading the catalogs. Later on this should + * invoke reading of asset representations too. + */ +struct AssetLibrary *BKE_asset_library_load(const char *library_path) +{ + blender::bke::AssetLibrary *lib = new blender::bke::AssetLibrary(); + lib->load(library_path); + return reinterpret_cast(lib); +} + +void BKE_asset_library_free(struct AssetLibrary *asset_library) +{ + blender::bke::AssetLibrary *lib = reinterpret_cast(asset_library); + delete lib; +} + +namespace blender::bke { + +void AssetLibrary::load(StringRefNull library_root_directory) +{ + auto catalog_service = std::make_unique(library_root_directory); + catalog_service->load_from_disk(); + this->catalog_service = std::move(catalog_service); +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc new file mode 100644 index 00000000000..37686175aed --- /dev/null +++ b/source/blender/blenkernel/intern/asset_library_test.cc @@ -0,0 +1,82 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation + * All rights reserved. + */ + +#include "BKE_appdir.h" +#include "BKE_asset_catalog.hh" +#include "BKE_asset_library.hh" + +#include "testing/testing.h" + +namespace blender::bke::tests { + +TEST(AssetLibraryTest, load_and_free_c_functions) +{ + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + + /* Load the asset library. */ + const std::string library_path = test_files_dir + "/" + "asset_library"; + ::AssetLibrary *library_c_ptr = BKE_asset_library_load(library_path.data()); + ASSERT_NE(nullptr, library_c_ptr); + + /* Check that it can be cast to the C++ type and has a Catalog Service. */ + blender::bke::AssetLibrary *library_cpp_ptr = reinterpret_cast( + library_c_ptr); + AssetCatalogService *service = library_cpp_ptr->catalog_service.get(); + ASSERT_NE(nullptr, service); + + /* Check that the catalogs defined in the library are actually loaded. This just tests one single + * catalog, as that indicates the file has been loaded. Testing that that loading went OK is for + * the asset catalog service tests. */ + const bUUID uuid_poses_ellie("df60e1f6-2259-475b-93d9-69a1b4a8db78"); + AssetCatalog *poses_ellie = service->find_catalog(uuid_poses_ellie); + ASSERT_NE(nullptr, poses_ellie) << "unable to find POSES_ELLIE catalog"; + EXPECT_EQ("character/Ellie/poselib", poses_ellie->path); + + BKE_asset_library_free(library_c_ptr); +} + +TEST(AssetLibraryTest, load_nonexistent_directory) +{ + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + + /* Load the asset library. */ + const std::string library_path = test_files_dir + "/" + + "asset_library/this/subdir/does/not/exist"; + ::AssetLibrary *library_c_ptr = BKE_asset_library_load(library_path.data()); + ASSERT_NE(nullptr, library_c_ptr); + + /* Check that it can be cast to the C++ type and has a Catalog Service. */ + blender::bke::AssetLibrary *library_cpp_ptr = reinterpret_cast( + library_c_ptr); + AssetCatalogService *service = library_cpp_ptr->catalog_service.get(); + ASSERT_NE(nullptr, service); + + /* Check that the catalog service doesn't have any catalogs. */ + EXPECT_TRUE(service->is_empty()); + + BKE_asset_library_free(library_c_ptr); +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_test.cc b/source/blender/blenkernel/intern/asset_test.cc new file mode 100644 index 00000000000..f1bf21a4f83 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_test.cc @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation + * All rights reserved. + */ + +#include "BKE_asset.h" + +#include "BLI_uuid.h" + +#include "DNA_asset_types.h" + +#include "testing/testing.h" + +namespace blender::bke::tests { + +TEST(AssetMetadataTest, set_catalog_id) +{ + AssetMetaData meta; + const bUUID uuid = BLI_uuid_generate_random(); + + /* Test trivial values. */ + BKE_asset_metadata_catalog_id_clear(&meta); + EXPECT_TRUE(BLI_uuid_is_nil(meta.catalog_id)); + EXPECT_STREQ("", meta.catalog_simple_name); + + /* Test simple situation where the given short name is used as-is. */ + BKE_asset_metadata_catalog_id_set(&meta, uuid, "simple"); + EXPECT_TRUE(BLI_uuid_equal(uuid, meta.catalog_id)); + EXPECT_STREQ("simple", meta.catalog_simple_name); + + /* Test whitespace trimming. */ + BKE_asset_metadata_catalog_id_set(&meta, uuid, " Govoriš angleško? "); + EXPECT_STREQ("Govoriš angleško?", meta.catalog_simple_name); + + /* Test length trimming to 63 chars + terminating zero. */ + constexpr char len66[] = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; + constexpr char len63[] = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1"; + BKE_asset_metadata_catalog_id_set(&meta, uuid, len66); + EXPECT_STREQ(len63, meta.catalog_simple_name); + + /* Test length trimming happens after whitespace trimming. */ + constexpr char len68[] = + " \ + 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 "; + BKE_asset_metadata_catalog_id_set(&meta, uuid, len68); + EXPECT_STREQ(len63, meta.catalog_simple_name); + + /* Test length trimming to 63 bytes, and not 63 characters. ✓ in UTF-8 is three bytes long. */ + constexpr char with_utf8[] = + "00010203040506✓0708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; + BKE_asset_metadata_catalog_id_set(&meta, uuid, with_utf8); + EXPECT_STREQ("00010203040506✓0708090a0b0c0d0e0f101112131415161718191a1b1c1d", + meta.catalog_simple_name); +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 9b85f8e65bc..592ac3d4607 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -73,4 +73,19 @@ bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) ATTR_NONNULL(); /** Output the UUID as formatted ASCII string, see #BLI_uuid_format(). */ std::ostream &operator<<(std::ostream &stream, bUUID uuid); +namespace blender::bke { + +class bUUID : public ::bUUID { + public: + bUUID() = default; + bUUID(const ::bUUID &struct_uuid); + explicit bUUID(const std::string &string_formatted_uuid); + + uint64_t hash() const; +}; + +bool operator==(bUUID uuid1, bUUID uuid2); + +} // namespace blender::bke + #endif diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index ae34bcb3d32..c1f7f8dfecd 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include /* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */ @@ -137,3 +138,34 @@ std::ostream &operator<<(std::ostream &stream, bUUID uuid) stream << buffer; return stream; } + +namespace blender::bke { + +bUUID::bUUID(const std::string &string_formatted_uuid) +{ + const bool parsed_ok = BLI_uuid_parse_string(this, string_formatted_uuid.c_str()); + if (!parsed_ok) { + std::stringstream ss; + ss << "invalid UUID string " << string_formatted_uuid; + throw std::runtime_error(ss.str()); + } +} + +bUUID::bUUID(const ::bUUID &struct_uuid) +{ + *(static_cast<::bUUID *>(this)) = struct_uuid; +} + +uint64_t bUUID::hash() const +{ + /* Convert the struct into two 64-bit numbers, and XOR them to get the hash. */ + const uint64_t *uuid_as_int64 = reinterpret_cast(this); + return uuid_as_int64[0] ^ uuid_as_int64[1]; +} + +bool operator==(bUUID uuid1, bUUID uuid2) +{ + return BLI_uuid_equal(uuid1, uuid2); +} + +} // namespace blender::bke diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt index 993a52b9084..b60f9df82f6 100644 --- a/source/blender/editors/space_file/CMakeLists.txt +++ b/source/blender/editors/space_file/CMakeLists.txt @@ -49,6 +49,7 @@ set(SRC ) set(LIB + bf_blenkernel ) if(WITH_HEADLESS) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 511b5b255e9..4f881184990 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -56,6 +56,7 @@ #endif #include "BKE_asset.h" +#include "BKE_asset_library.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_icons.h" @@ -386,6 +387,7 @@ typedef struct FileList { eFileSelectType type; /* The library this list was created for. Stored here so we know when to re-read. */ AssetLibraryReference *asset_library_ref; + struct AssetLibrary *asset_library; short flags; @@ -1758,6 +1760,13 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const boo if (do_selection && filelist->selection_state) { BLI_ghash_clear(filelist->selection_state, NULL, NULL); } + + if (filelist->asset_library != NULL) { + /* There is no way to refresh the catalogs stored by the AssetLibrary struct, so instead of + * "clearing" it, the entire struct is freed. It will be reallocated when needed. */ + BKE_asset_library_free(filelist->asset_library); + filelist->asset_library = NULL; + } } void filelist_clear(struct FileList *filelist) @@ -3136,6 +3145,9 @@ typedef struct FileListReadJob { * The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist * into #filelist in a thread-safe way. * + * #tmp_filelist also keeps an `AssetLibrary *` so that it can be loaded in the same thread, and + * moved to #filelist once all categories are loaded. + * * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be set * to NULL to avoid double-freeing them. */ struct FileList *tmp_filelist; @@ -3266,6 +3278,13 @@ static void filelist_readjob_do(const bool do_lib, BLI_stack_discard(todo_dirs); } BLI_stack_free(todo_dirs); + + /* Check whether assets catalogs need to be loaded. */ + if (job_params->filelist->asset_library_ref != NULL) { + /* Load asset catalogs, into the temp filelist for thread-safety. + * #filelist_readjob_endjob() will move it into the real filelist. */ + job_params->tmp_filelist->asset_library = BKE_asset_library_load(filelist->filelist.root); + } } static void filelist_readjob_dir(FileListReadJob *job_params, @@ -3355,6 +3374,8 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update BLI_mutex_lock(&flrj->lock); BLI_assert((flrj->tmp_filelist == NULL) && flrj->filelist); + BLI_assert_msg(flrj->filelist->asset_library == NULL, + "Asset library should not yet be assigned at start of read job"); flrj->tmp_filelist = MEM_dupallocN(flrj->filelist); @@ -3415,6 +3436,12 @@ static void filelist_readjob_endjob(void *flrjv) /* In case there would be some dangling update... */ filelist_readjob_update(flrjv); + /* Move ownership of the asset library from the temporary list to the true filelist. */ + BLI_assert_msg(flrj->filelist->asset_library == NULL, + "asset library should not already have been allocated"); + flrj->filelist->asset_library = flrj->tmp_filelist->asset_library; + flrj->tmp_filelist->asset_library = NULL; /* MUST be NULL to avoid double-free. */ + flrj->filelist->flags &= ~FL_IS_PENDING; flrj->filelist->flags |= FL_IS_READY; } diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index 2975915eccd..f5bdad3e79e 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -22,6 +22,7 @@ #include "DNA_defs.h" #include "DNA_listBase.h" +#include "DNA_uuid_types.h" #ifdef __cplusplus extern "C" { @@ -58,6 +59,19 @@ typedef struct AssetMetaData { /** Custom asset meta-data. Cannot store pointers to IDs (#STRUCT_NO_DATABLOCK_IDPROPERTIES)! */ struct IDProperty *properties; + /** + * Asset Catalog identifier. Should not contain spaces. + * Mapped to a path in the asset catalog hierarchy by an #AssetCatalogService. + * Use #BKE_asset_metadata_catalog_id_set() to ensure a valid ID is set. + */ + struct bUUID catalog_id; + /** + * Short name of the asset's catalog. This is for debugging purposes only, to allow (partial) + * reconstruction of asset catalogs in the unfortunate case that the mapping from catalog UUID to + * catalog path is lost. The catalog's simple name is copied to #catalog_simple_name whenever + * #catalog_id is updated. */ + char catalog_simple_name[64]; /* MAX_NAME */ + /** Optional description of this asset for display in the UI. Dynamic length. */ char *description; /** User defined tags for this asset. The asset manager uses these for filtering, but how they diff --git a/source/blender/makesdna/DNA_uuid_types.h b/source/blender/makesdna/DNA_uuid_types.h index fa0a78f074b..dcebfed6be7 100644 --- a/source/blender/makesdna/DNA_uuid_types.h +++ b/source/blender/makesdna/DNA_uuid_types.h @@ -40,6 +40,12 @@ typedef struct bUUID { uint8_t node[6]; } bUUID; +/** + * Memory required for a string representation of a UUID according to RFC4122. + * This is 36 characters for the string + a trailing zero byte. + */ +#define UUID_STRING_LEN 37 + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index dcef88d2e79..5df32150cc4 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -32,11 +32,15 @@ #ifdef RNA_RUNTIME # include "BKE_asset.h" +# include "BKE_asset_library.h" +# include "BKE_context.h" # include "BKE_idprop.h" # include "BLI_listbase.h" +# include "BLI_uuid.h" # include "ED_asset.h" +# include "ED_fileselect.h" # include "RNA_access.h" @@ -176,6 +180,40 @@ static void rna_AssetMetaData_active_tag_range( *max = *softmax = MAX2(asset_data->tot_tags - 1, 0); } +static void rna_AssetMetaData_catalog_id_get(PointerRNA *ptr, char *value) +{ + const AssetMetaData *asset_data = ptr->data; + BLI_uuid_format(value, asset_data->catalog_id); +} + +static int rna_AssetMetaData_catalog_id_length(PointerRNA *UNUSED(ptr)) +{ + return UUID_STRING_LEN - 1; +} + +static void rna_AssetMetaData_catalog_id_set(PointerRNA *ptr, const char *value) +{ + AssetMetaData *asset_data = ptr->data; + bUUID new_uuid; + + if (value[0] == '\0') { + BKE_asset_metadata_catalog_id_clear(asset_data); + return; + } + + if (!BLI_uuid_parse_string(&new_uuid, value)) { + // TODO(Sybren): raise ValueError exception once that's possible from an RNA setter. + printf("UUID %s not formatted correctly, ignoring new value\n", value); + return; + } + + /* This just sets the new UUID and clears the catalog simple name. The actual + * catalog simple name will be updated by some update function, as it + * needs the asset library from the context. */ + /* TODO(Sybren): write that update function. */ + BKE_asset_metadata_catalog_id_set(asset_data, new_uuid, ""); +} + static PointerRNA rna_AssetHandle_file_data_get(PointerRNA *ptr) { AssetHandle *asset_handle = ptr->data; @@ -310,6 +348,24 @@ static void rna_def_asset_data(BlenderRNA *brna) prop = RNA_def_property(srna, "active_tag", PROP_INT, PROP_NONE); RNA_def_property_int_funcs(prop, NULL, NULL, "rna_AssetMetaData_active_tag_range"); RNA_def_property_ui_text(prop, "Active Tag", "Index of the tag set for editing"); + + prop = RNA_def_property(srna, "catalog_id", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_AssetMetaData_catalog_id_get", + "rna_AssetMetaData_catalog_id_length", + "rna_AssetMetaData_catalog_id_set"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_ui_text(prop, + "Catalog UUID", + "Identifier for the asset's catalog, used by Blender to look up the " + "asset's catalog path. Must be a UUID according to RFC4122"); + + prop = RNA_def_property(srna, "catalog_simple_name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Catalog Simple Name", + "Simple name of the asset's catalog, for debugging and " + "data recovery purposes"); } static void rna_def_asset_handle_api(StructRNA *srna) From aa2493e2e7e6be645d352f53d30a1e018b8a677a Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 23 Sep 2021 15:04:00 +0200 Subject: [PATCH 0143/1500] Fix unused variable warning in release builds The variable is only used in debug builds. --- source/blender/makesrna/intern/rna_asset.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 5df32150cc4..80824df1bc8 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -76,6 +76,7 @@ static int rna_AssetTag_editable(PointerRNA *ptr, const char **r_info) if (owner_id && owner_id->asset_data) { BLI_assert_msg(BLI_findindex(&owner_id->asset_data->tags, asset_tag) != -1, "The owner of the asset tag pointer is not the asset ID containing the tag"); + UNUSED_VARS_NDEBUG(asset_tag); } return rna_AssetMetaData_editable_from_owner_id(ptr->owner_id, owner_id->asset_data, r_info) ? From 490425d56e7bc48c6ebf8440e3463ff6a3a5a954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 23 Sep 2021 16:06:25 +0200 Subject: [PATCH 0144/1500] Asset Catalogs: explicit version number in catalog definition files Declare the current format used for asset catalog definition files as version 1, and write that to the files. Files without that version number will be rejected. This makes it much easier to move to different versions later, with the opportunity to do versioning on file load. The version is not associated with any version of Blender, but a separate integer that's simply incremented when a non-backward-compatible change happens. --- .../blender/blenkernel/BKE_asset_catalog.hh | 6 +++ .../blenkernel/intern/asset_catalog.cc | 40 ++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 2bbaa4b4222..0401fae7126 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -172,6 +172,11 @@ class AssetCatalogTree { * class is shorter than that of the #`AssetCatalog`s themselves. */ class AssetCatalogDefinitionFile { public: + /* For now this is the only version of the catalog definition files that is supported. + * Later versioning code may be added to handle older files. */ + const static int SUPPORTED_VERSION; + const static std::string VERSION_MARKER; + CatalogFilePath file_path; AssetCatalogDefinitionFile() = default; @@ -203,6 +208,7 @@ class AssetCatalogDefinitionFile { * catalog is already known, without having to find the corresponding `AssetCatalog*`. */ Map catalogs_; + bool parse_version_line(StringRef line); std::unique_ptr parse_catalog_line(StringRef line); /** diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 0c64a0b085c..2e56ca1392d 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -36,6 +36,14 @@ namespace blender::bke { const char AssetCatalogService::PATH_SEPARATOR = '/'; const CatalogFilePath AssetCatalogService::DEFAULT_CATALOG_FILENAME = "blender_assets.cats.txt"; +/* For now this is the only version of the catalog definition files that is supported. + * Later versioning code may be added to handle older files. */ +const int AssetCatalogDefinitionFile::SUPPORTED_VERSION = 1; +/* String that's matched in the catalog definition file to know that the line is the version + * declaration. It has to start with a space to ensure it won't match any hypothetical future field + * that starts with "VERSION". */ +const std::string AssetCatalogDefinitionFile::VERSION_MARKER = "VERSION "; + AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_root) : asset_library_root_(asset_library_root) { @@ -359,6 +367,8 @@ void AssetCatalogDefinitionFile::parse_catalog_file( AssetCatalogParsedFn catalog_loaded_callback) { std::fstream infile(catalog_definition_file_path); + + bool seen_version_number = false; std::string line; while (std::getline(infile, line)) { const StringRef trimmed_line = StringRef(line).trim(); @@ -366,6 +376,18 @@ void AssetCatalogDefinitionFile::parse_catalog_file( continue; } + if (!seen_version_number) { + /* The very first non-ignored line should be the version declaration. */ + const bool is_valid_version = this->parse_version_line(trimmed_line); + if (!is_valid_version) { + std::cerr << catalog_definition_file_path + << ": first line should be version declaration; ignoring file." << std::endl; + break; + } + seen_version_number = true; + continue; + } + std::unique_ptr catalog = this->parse_catalog_line(trimmed_line); if (!catalog) { continue; @@ -390,12 +412,25 @@ void AssetCatalogDefinitionFile::parse_catalog_file( } } +bool AssetCatalogDefinitionFile::parse_version_line(const StringRef line) +{ + if (!line.startswith(VERSION_MARKER)) { + return false; + } + + const std::string version_string = line.substr(VERSION_MARKER.length()); + const int file_version = std::atoi(version_string.c_str()); + + /* No versioning, just a blunt check whether it's the right one. */ + return file_version == SUPPORTED_VERSION; +} + std::unique_ptr AssetCatalogDefinitionFile::parse_catalog_line(const StringRef line) { const char delim = ':'; const int64_t first_delim = line.find_first_of(delim); if (first_delim == StringRef::not_found) { - std::cerr << "Invalid line in " << this->file_path << ": " << line << std::endl; + std::cerr << "Invalid catalog line in " << this->file_path << ": " << line << std::endl; return std::unique_ptr(nullptr); } @@ -481,9 +516,12 @@ bool AssetCatalogDefinitionFile::write_to_disk_unsafe(const CatalogFilePath &des output << "# This is an Asset Catalog Definition file for Blender." << std::endl; output << "#" << std::endl; output << "# Empty lines and lines starting with `#` will be ignored." << std::endl; + output << "# The first non-ignored line should be the version indicator." << std::endl; output << "# Other lines are of the format \"UUID:catalog/path/for/assets:simple catalog name\"" << std::endl; output << "" << std::endl; + output << VERSION_MARKER << SUPPORTED_VERSION << std::endl; + output << "" << std::endl; // Write the catalogs. // TODO(@sybren): order them by Catalog ID or Catalog Path. From f11bcb5a80eb106560678c8c60be32ad997d6641 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 21 Sep 2021 13:11:08 +0200 Subject: [PATCH 0145/1500] Fix T91557: Texture Paint Stencil doesnt use assigned UV Layer Choosing a UV layer would actually affect the overlay in the viewport and also painting with the mask brush was in that UV space, but the resulting stencil mask was always applied with the active UV (not the explicitly selected stencil UV -- the one one is looking at in the viewport!) to painting. This has been like that as far as I have checked back (at least 2.79b), I am surprised this has not come up before, but it does not seem to make sense at all... Now use the UV specified for the stencil layer when applying the mask for painting, so it corresponds to the stencil mask one is looking at in the viewport. Maniphest Tasks: T91557 Differential Revision: https://developer.blender.org/D12583 --- source/blender/editors/sculpt_paint/paint_image_proj.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index a58b1947b0c..0176b4a1b13 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1659,7 +1659,9 @@ static float project_paint_uvpixel_mask(const ProjPaintState *ps, if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index]; - const float *lt_other_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt_other)}; + const float *lt_other_tri_uv[3] = {ps->mloopuv_stencil_eval[lt_other->tri[0]].uv, + ps->mloopuv_stencil_eval[lt_other->tri[1]].uv, + ps->mloopuv_stencil_eval[lt_other->tri[2]].uv}; /* #BKE_image_acquire_ibuf - TODO: this may be slow. */ uchar rgba_ub[4]; From d0493796a6f40db8dcdd4a9058b98a69282d4759 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Thu, 23 Sep 2021 15:53:38 +0200 Subject: [PATCH 0146/1500] Cleanup: Remove hardcoded values and rename keyframe shape shaders No functional change. The shader is complicated by itself, having hardcoded values makes it even more cryptic. I also renamed the shader because the shader is not for the keyfarme diamond only, but for all the keyframe shapes. Differential Revision: https://developer.blender.org/D12615 --- .../editors/animation/keyframes_draw.c | 18 +++++------ .../editors/include/ED_keyframes_draw.h | 2 +- .../editors/interface/interface_icons.c | 2 +- .../editors/space_clip/clip_dopesheet_draw.c | 2 +- source/blender/editors/space_nla/nla_draw.c | 2 +- .../blender/editors/space_node/node_draw.cc | 18 ++++------- source/blender/gpu/CMakeLists.txt | 4 +-- source/blender/gpu/GPU_shader.h | 15 ++++++++- .../blender/gpu/intern/gpu_shader_builtin.c | 12 +++---- ...sl => gpu_shader_keyframe_shape_frag.glsl} | 31 +++++++++++++------ ...sl => gpu_shader_keyframe_shape_vert.glsl} | 17 ++++++++-- .../gpu/tests/gpu_shader_builtin_test.cc | 2 +- 12 files changed, 79 insertions(+), 46 deletions(-) rename source/blender/gpu/shaders/{gpu_shader_keyframe_diamond_frag.glsl => gpu_shader_keyframe_shape_frag.glsl} (63%) rename source/blender/gpu/shaders/{gpu_shader_keyframe_diamond_vert.glsl => gpu_shader_keyframe_shape_vert.glsl} (72%) diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index ac7db9f4f46..e3ea8f0ab21 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -146,31 +146,31 @@ void draw_keyframe_shape(float x, /* Handle type to outline shape. */ switch (handle_type) { case KEYFRAME_HANDLE_AUTO_CLAMP: - flags = 0x2; + flags = GPU_KEYFRAME_SHAPE_CIRCLE; break; /* circle */ case KEYFRAME_HANDLE_AUTO: - flags = 0x12; + flags = GPU_KEYFRAME_SHAPE_CIRCLE | GPU_KEYFRAME_SHAPE_INNER_DOT; break; /* circle with dot */ case KEYFRAME_HANDLE_VECTOR: - flags = 0xC; + flags = GPU_KEYFRAME_SHAPE_SQUARE; break; /* square */ case KEYFRAME_HANDLE_ALIGNED: - flags = 0x5; + flags = GPU_KEYFRAME_SHAPE_DIAMOND | GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL; break; /* clipped diamond */ case KEYFRAME_HANDLE_FREE: default: - flags = 1; /* diamond */ + flags = GPU_KEYFRAME_SHAPE_DIAMOND; /* diamond */ } /* Extreme type to arrow-like shading. */ if (extreme_type & KEYFRAME_EXTREME_MAX) { - flags |= 0x100; + flags |= GPU_KEYFRAME_SHAPE_ARROW_END_MAX; } if (extreme_type & KEYFRAME_EXTREME_MIN) { - flags |= 0x200; + flags |= GPU_KEYFRAME_SHAPE_ARROW_END_MIN; } - if (extreme_type & KEYFRAME_EXTREME_MIXED) { + if (extreme_type & GPU_KEYFRAME_SHAPE_ARROW_END_MIXED) { flags |= 0x400; } } @@ -584,7 +584,7 @@ static void ED_keylist_draw_list_draw_keys(AnimKeylistDrawList *draw_list, View2 sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); immBegin(GPU_PRIM_POINTS, visible_key_len); diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index 61e37f20b1b..6a7037c3eed 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -41,7 +41,7 @@ struct bDopeSheet; struct bGPDlayer; /* draw simple diamond-shape keyframe */ -/* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_DIAMOND, +/* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_SHAPE, * immBegin(GPU_PRIM_POINTS, n), then call this n times */ typedef struct KeyframeShaderBindings { uint pos_id; diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index f739830cfdb..7f1a8ee99e0 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -309,7 +309,7 @@ static void vicon_keytype_draw_wrapper( sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", -1.0f, -1.0f); immBegin(GPU_PRIM_POINTS, 1); diff --git a/source/blender/editors/space_clip/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c index 8aaf3faffec..42fda3ef464 100644 --- a/source/blender/editors/space_clip/clip_dopesheet_draw.c +++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c @@ -224,7 +224,7 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *region, Scene *scene) uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); immUniform1f("outline_scale", 1.0f); immUniform2f( "ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index c1b308d213f..4694d8652f6 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -152,7 +152,7 @@ static void nla_action_draw_keyframes( format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); immBegin(GPU_PRIM_POINTS, key_len); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 10a3285be8b..b57696cb676 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -733,12 +733,6 @@ static void node_draw_mute_line(const View2D *v2d, const SpaceNode *snode, const GPU_blend(GPU_BLEND_NONE); } -/* Flags used in gpu_shader_keyframe_diamond_frag.glsl. */ -#define MARKER_SHAPE_DIAMOND 0x1 -#define MARKER_SHAPE_SQUARE 0xC -#define MARKER_SHAPE_CIRCLE 0x2 -#define MARKER_SHAPE_INNER_DOT 0x10 - static void node_socket_draw(const bNodeSocket *sock, const float color[4], const float color_outline[4], @@ -757,16 +751,16 @@ static void node_socket_draw(const bNodeSocket *sock, switch (sock->display_shape) { case SOCK_DISPLAY_SHAPE_DIAMOND: case SOCK_DISPLAY_SHAPE_DIAMOND_DOT: - flags = MARKER_SHAPE_DIAMOND; + flags = GPU_KEYFRAME_SHAPE_DIAMOND; break; case SOCK_DISPLAY_SHAPE_SQUARE: case SOCK_DISPLAY_SHAPE_SQUARE_DOT: - flags = MARKER_SHAPE_SQUARE; + flags = GPU_KEYFRAME_SHAPE_SQUARE; break; default: case SOCK_DISPLAY_SHAPE_CIRCLE: case SOCK_DISPLAY_SHAPE_CIRCLE_DOT: - flags = MARKER_SHAPE_CIRCLE; + flags = GPU_KEYFRAME_SHAPE_CIRCLE; break; } @@ -774,7 +768,7 @@ static void node_socket_draw(const bNodeSocket *sock, SOCK_DISPLAY_SHAPE_DIAMOND_DOT, SOCK_DISPLAY_SHAPE_SQUARE_DOT, SOCK_DISPLAY_SHAPE_CIRCLE_DOT)) { - flags |= MARKER_SHAPE_INNER_DOT; + flags |= GPU_KEYFRAME_SHAPE_INNER_DOT; } immAttr4fv(col_id, color); @@ -1132,7 +1126,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); immUniform1f("outline_scale", 0.7f); immUniform2f("ViewportSize", -1.0f, -1.0f); @@ -1277,7 +1271,7 @@ void node_draw_sockets(const View2D *v2d, GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); immUniform1f("outline_scale", 0.7f); immUniform2f("ViewportSize", -1.0f, -1.0f); diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index b7dc3210c41..df370c7079b 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -268,8 +268,8 @@ data_to_c_simple(shaders/gpu_shader_2D_edituvs_stretch_vert.glsl SRC) data_to_c_simple(shaders/gpu_shader_text_vert.glsl SRC) data_to_c_simple(shaders/gpu_shader_text_frag.glsl SRC) -data_to_c_simple(shaders/gpu_shader_keyframe_diamond_vert.glsl SRC) -data_to_c_simple(shaders/gpu_shader_keyframe_diamond_frag.glsl SRC) +data_to_c_simple(shaders/gpu_shader_keyframe_shape_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_keyframe_shape_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_codegen_lib.glsl SRC) diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 62b748b7edf..c6cfac79699 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -169,7 +169,7 @@ void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear); typedef enum eGPUBuiltinShader { /* specialized drawing */ GPU_SHADER_TEXT, - GPU_SHADER_KEYFRAME_DIAMOND, + GPU_SHADER_KEYFRAME_SHAPE, GPU_SHADER_SIMPLE_LIGHTING, /* for simple 2D drawing */ /** @@ -423,6 +423,19 @@ void GPU_shader_free_builtin_shaders(void); /* Determined by the maximum uniform buffer size divided by chunk size. */ #define GPU_MAX_UNIFORM_ATTR 8 +typedef enum eGPUKeyframeShapes { + GPU_KEYFRAME_SHAPE_DIAMOND = (1 << 0), + GPU_KEYFRAME_SHAPE_CIRCLE = (1 << 1), + GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL = (1 << 2), + GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL = (1 << 3), + GPU_KEYFRAME_SHAPE_INNER_DOT = (1 << 4), + GPU_KEYFRAME_SHAPE_ARROW_END_MAX = (1 << 8), + GPU_KEYFRAME_SHAPE_ARROW_END_MIN = (1 << 9), + GPU_KEYFRAME_SHAPE_ARROW_END_MIXED = (1 << 10), +} eGPUKeyframeShapes; +#define GPU_KEYFRAME_SHAPE_SQUARE \ + (GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL) + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index c5122b76001..9ea46788f44 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -121,8 +121,8 @@ extern char datatoc_gpu_shader_3D_line_dashed_uniform_color_vert_glsl[]; extern char datatoc_gpu_shader_text_vert_glsl[]; extern char datatoc_gpu_shader_text_frag_glsl[]; -extern char datatoc_gpu_shader_keyframe_diamond_vert_glsl[]; -extern char datatoc_gpu_shader_keyframe_diamond_frag_glsl[]; +extern char datatoc_gpu_shader_keyframe_shape_vert_glsl[]; +extern char datatoc_gpu_shader_keyframe_shape_frag_glsl[]; extern char datatoc_gpu_shader_gpencil_stroke_vert_glsl[]; extern char datatoc_gpu_shader_gpencil_stroke_frag_glsl[]; @@ -166,11 +166,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { .vert = datatoc_gpu_shader_text_vert_glsl, .frag = datatoc_gpu_shader_text_frag_glsl, }, - [GPU_SHADER_KEYFRAME_DIAMOND] = + [GPU_SHADER_KEYFRAME_SHAPE] = { - .name = "GPU_SHADER_KEYFRAME_DIAMOND", - .vert = datatoc_gpu_shader_keyframe_diamond_vert_glsl, - .frag = datatoc_gpu_shader_keyframe_diamond_frag_glsl, + .name = "GPU_SHADER_KEYFRAME_SHAPE", + .vert = datatoc_gpu_shader_keyframe_shape_vert_glsl, + .frag = datatoc_gpu_shader_keyframe_shape_frag_glsl, }, [GPU_SHADER_SIMPLE_LIGHTING] = { diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl similarity index 63% rename from source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl rename to source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl index 1c4039bc590..f0ff70f7690 100644 --- a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl @@ -1,3 +1,16 @@ + +/* Values in GPU_shader.h. */ +#define GPU_KEYFRAME_SHAPE_DIAMOND (1 << 0) +#define GPU_KEYFRAME_SHAPE_CIRCLE (1 << 1) +#define GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL (1 << 2) +#define GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL (1 << 3) +#define GPU_KEYFRAME_SHAPE_INNER_DOT (1 << 4) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MAX (1 << 8) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MIN (1 << 9) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MIXED (1 << 10) +#define GPU_KEYFRAME_SHAPE_SQUARE \ + (GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL) + flat in vec4 radii; flat in vec4 thresholds; @@ -27,24 +40,24 @@ void main() float outline_dist = -1.0; /* Diamond outline */ - if (test(0x1)) { + if (test(GPU_KEYFRAME_SHAPE_DIAMOND)) { outline_dist = max(outline_dist, radius - radii[0]); } /* Circle outline */ - if (test(0x2)) { + if (test(GPU_KEYFRAME_SHAPE_CIRCLE)) { radius = length(absPos); outline_dist = max(outline_dist, radius - radii[1]); } /* Top & Bottom clamp */ - if (test(0x4)) { + if (test(GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL)) { outline_dist = max(outline_dist, absPos.y - radii[2]); } /* Left & Right clamp */ - if (test(0x8)) { + if (test(GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL)) { outline_dist = max(outline_dist, absPos.x - radii[2]); } @@ -53,20 +66,20 @@ void main() /* Inside the outline. */ if (outline_dist < 0) { /* Middle dot */ - if (test(0x10)) { + if (test(GPU_KEYFRAME_SHAPE_INNER_DOT)) { alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], radius)); } /* Up and down arrow-like shading. */ - if (test(0x300)) { + if (test(GPU_KEYFRAME_SHAPE_ARROW_END_MAX | GPU_KEYFRAME_SHAPE_ARROW_END_MIN)) { float ypos = -1.0; /* Up arrow (maximum) */ - if (test(0x100)) { + if (test(GPU_KEYFRAME_SHAPE_ARROW_END_MAX)) { ypos = max(ypos, pos.y); } /* Down arrow (minimum) */ - if (test(0x200)) { + if (test(GPU_KEYFRAME_SHAPE_ARROW_END_MIN)) { ypos = max(ypos, -pos.y); } @@ -75,7 +88,7 @@ void main() float minmax_step = smoothstep(thresholds[0], thresholds[1], minmax_dist * minmax_scale); /* Reduced alpha for uncertain extremes. */ - float minmax_alpha = test(0x400) ? 0.55 : 0.85; + float minmax_alpha = test(GPU_KEYFRAME_SHAPE_ARROW_END_MIXED) ? 0.55 : 0.85; alpha = max(alpha, minmax_step * minmax_alpha); } diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl similarity index 72% rename from source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl rename to source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl index 2ba89230d80..18e8b76ba23 100644 --- a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl @@ -1,4 +1,16 @@ +/* Values in GPU_shader.h. */ +#define GPU_KEYFRAME_SHAPE_DIAMOND (1 << 0) +#define GPU_KEYFRAME_SHAPE_CIRCLE (1 << 1) +#define GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL (1 << 2) +#define GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL (1 << 3) +#define GPU_KEYFRAME_SHAPE_INNER_DOT (1 << 4) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MAX (1 << 8) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MIN (1 << 9) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MIXED (1 << 10) +#define GPU_KEYFRAME_SHAPE_SQUARE \ + (GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL) + uniform mat4 ModelViewProjectionMatrix; uniform vec2 ViewportSize = vec2(-1, -1); uniform float outline_scale = 1.0; @@ -49,8 +61,9 @@ void main() finalOutlineColor = outlineColor; finalFlags = flags; - if (!test(0xF)) { - finalFlags |= 1; + if (!test(GPU_KEYFRAME_SHAPE_DIAMOND | GPU_KEYFRAME_SHAPE_CIRCLE | + GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL)) { + finalFlags |= GPU_KEYFRAME_SHAPE_DIAMOND; } /* Size-dependent line thickness. */ diff --git a/source/blender/gpu/tests/gpu_shader_builtin_test.cc b/source/blender/gpu/tests/gpu_shader_builtin_test.cc index f0061a6bf5c..523a7e5b881 100644 --- a/source/blender/gpu/tests/gpu_shader_builtin_test.cc +++ b/source/blender/gpu/tests/gpu_shader_builtin_test.cc @@ -32,7 +32,7 @@ static void test_shader_builtin() test_compile_builtin_shader(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA); test_compile_builtin_shader(GPU_SHADER_TEXT, GPU_SHADER_CFG_DEFAULT); - test_compile_builtin_shader(GPU_SHADER_KEYFRAME_DIAMOND, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_KEYFRAME_SHAPE, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_SIMPLE_LIGHTING, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT); From 6279efbb78a3c701547b8e25cf90efd712c377d0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 23 Sep 2021 17:28:20 +0200 Subject: [PATCH 0147/1500] Fix Cycles compiler warning on GCC 11 For shadow rays there are no closures, leave out the closure merging code there to avoid warnings about accessing closure memory that does not exist. --- .../cycles/kernel/integrator/integrator_shade_volume.h | 4 ++-- intern/cycles/kernel/kernel_shader.h | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index 095a28ac505..dac3efb3996 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -74,7 +74,7 @@ ccl_device_inline bool shadow_volume_shader_sample(INTEGRATOR_STATE_ARGS, ShaderData *ccl_restrict sd, float3 *ccl_restrict extinction) { - shader_eval_volume(INTEGRATOR_STATE_PASS, sd, PATH_RAY_SHADOW, [=](const int i) { + shader_eval_volume(INTEGRATOR_STATE_PASS, sd, PATH_RAY_SHADOW, [=](const int i) { return integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_PASS, i); }); @@ -93,7 +93,7 @@ ccl_device_inline bool volume_shader_sample(INTEGRATOR_STATE_ARGS, VolumeShaderCoefficients *coeff) { const int path_flag = INTEGRATOR_STATE(path, flag); - shader_eval_volume(INTEGRATOR_STATE_PASS, sd, path_flag, [=](const int i) { + shader_eval_volume(INTEGRATOR_STATE_PASS, sd, path_flag, [=](const int i) { return integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); }); diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 3052bb53040..8bad9c34d74 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -750,7 +750,7 @@ ccl_device int shader_phase_sample_closure(const KernelGlobals *kg, /* Volume Evaluation */ -template +template ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS, ShaderData *ccl_restrict sd, const int path_flag, @@ -815,8 +815,11 @@ ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS, # endif /* Merge closures to avoid exceeding number of closures limit. */ - if (i > 0) - shader_merge_volume_closures(sd); + if (!shadow) { + if (i > 0) { + shader_merge_volume_closures(sd); + } + } } } From d7f803f522237be41c6ede50dde38b3d6795b161 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 23 Sep 2021 17:38:56 +0200 Subject: [PATCH 0148/1500] Fix T91641: crash rendering with 16k environment map in Cycles Protect against integer overflow. --- intern/cycles/device/cpu/device_impl.cpp | 4 +- intern/cycles/device/cpu/device_impl.h | 7 +- intern/cycles/device/cuda/device_impl.cpp | 4 +- intern/cycles/device/cuda/device_impl.h | 4 +- intern/cycles/device/device.h | 4 +- intern/cycles/device/device_memory.cpp | 4 +- intern/cycles/device/device_memory.h | 94 +++++++++++------------ intern/cycles/device/dummy/device.cpp | 2 +- intern/cycles/device/multi/device.cpp | 8 +- intern/cycles/integrator/shader_eval.cpp | 8 +- 10 files changed, 71 insertions(+), 68 deletions(-) diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp index 3b0db6bdd0e..bd0a32e610b 100644 --- a/intern/cycles/device/cpu/device_impl.cpp +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -170,7 +170,7 @@ void CPUDevice::mem_copy_to(device_memory &mem) } void CPUDevice::mem_copy_from( - device_memory & /*mem*/, int /*y*/, int /*w*/, int /*h*/, int /*elem*/) + device_memory & /*mem*/, size_t /*y*/, size_t /*w*/, size_t /*h*/, size_t /*elem*/) { /* no-op */ } @@ -204,7 +204,7 @@ void CPUDevice::mem_free(device_memory &mem) } } -device_ptr CPUDevice::mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) +device_ptr CPUDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) { return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); } diff --git a/intern/cycles/device/cpu/device_impl.h b/intern/cycles/device/cpu/device_impl.h index 7d222808652..371d2258104 100644 --- a/intern/cycles/device/cpu/device_impl.h +++ b/intern/cycles/device/cpu/device_impl.h @@ -72,10 +72,13 @@ class CPUDevice : public Device { virtual void mem_alloc(device_memory &mem) override; virtual void mem_copy_to(device_memory &mem) override; - virtual void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override; + virtual void mem_copy_from( + device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override; virtual void mem_zero(device_memory &mem) override; virtual void mem_free(device_memory &mem) override; - virtual device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override; + virtual device_ptr mem_alloc_sub_ptr(device_memory &mem, + size_t offset, + size_t /*size*/) override; virtual void const_copy_to(const char *name, void *host, size_t size) override; diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp index 37fab8f8293..0f10119e24f 100644 --- a/intern/cycles/device/cuda/device_impl.cpp +++ b/intern/cycles/device/cuda/device_impl.cpp @@ -837,7 +837,7 @@ void CUDADevice::mem_copy_to(device_memory &mem) } } -void CUDADevice::mem_copy_from(device_memory &mem, int y, int w, int h, int elem) +void CUDADevice::mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) { if (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) { assert(!"mem_copy_from not supported for textures."); @@ -891,7 +891,7 @@ void CUDADevice::mem_free(device_memory &mem) } } -device_ptr CUDADevice::mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) +device_ptr CUDADevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) { return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); } diff --git a/intern/cycles/device/cuda/device_impl.h b/intern/cycles/device/cuda/device_impl.h index 6b27db54ab4..76c09d84651 100644 --- a/intern/cycles/device/cuda/device_impl.h +++ b/intern/cycles/device/cuda/device_impl.h @@ -120,13 +120,13 @@ class CUDADevice : public Device { void mem_copy_to(device_memory &mem) override; - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override; + void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override; void mem_zero(device_memory &mem) override; void mem_free(device_memory &mem) override; - device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override; + device_ptr mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) override; virtual void const_copy_to(const char *name, void *host, size_t size) override; diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index 399d5eb91df..3bbad179f52 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -119,7 +119,7 @@ class Device { string error_msg; - virtual device_ptr mem_alloc_sub_ptr(device_memory & /*mem*/, int /*offset*/, int /*size*/) + virtual device_ptr mem_alloc_sub_ptr(device_memory & /*mem*/, size_t /*offset*/, size_t /*size*/) { /* Only required for devices that implement denoising. */ assert(false); @@ -273,7 +273,7 @@ class Device { virtual void mem_alloc(device_memory &mem) = 0; virtual void mem_copy_to(device_memory &mem) = 0; - virtual void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) = 0; + virtual void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) = 0; virtual void mem_zero(device_memory &mem) = 0; virtual void mem_free(device_memory &mem) = 0; diff --git a/intern/cycles/device/device_memory.cpp b/intern/cycles/device/device_memory.cpp index c4d45829b83..c0ab2e17cae 100644 --- a/intern/cycles/device/device_memory.cpp +++ b/intern/cycles/device/device_memory.cpp @@ -136,7 +136,7 @@ void device_memory::device_copy_to() } } -void device_memory::device_copy_from(int y, int w, int h, int elem) +void device_memory::device_copy_from(size_t y, size_t w, size_t h, size_t elem) { assert(type != MEM_TEXTURE && type != MEM_READ_ONLY && type != MEM_GLOBAL); device->mem_copy_from(*this, y, w, h, elem); @@ -181,7 +181,7 @@ bool device_memory::is_resident(Device *sub_device) const /* Device Sub Ptr */ -device_sub_ptr::device_sub_ptr(device_memory &mem, int offset, int size) : device(mem.device) +device_sub_ptr::device_sub_ptr(device_memory &mem, size_t offset, size_t size) : device(mem.device) { ptr = device->mem_alloc_sub_ptr(mem, offset, size); } diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/device_memory.h index c51594b8580..a854cc9b693 100644 --- a/intern/cycles/device/device_memory.h +++ b/intern/cycles/device/device_memory.h @@ -81,154 +81,154 @@ static constexpr size_t datatype_size(DataType datatype) template struct device_type_traits { static const DataType data_type = TYPE_UNKNOWN; - static const int num_elements_cpu = sizeof(T); - static const int num_elements_gpu = sizeof(T); + static const size_t num_elements_cpu = sizeof(T); + static const size_t num_elements_gpu = sizeof(T); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements_cpu = 1; - static const int num_elements_gpu = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; static_assert(sizeof(uchar) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements_cpu = 2; - static const int num_elements_gpu = 2; + static const size_t num_elements_cpu = 2; + static const size_t num_elements_gpu = 2; static_assert(sizeof(uchar2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements_cpu = 3; - static const int num_elements_gpu = 3; + static const size_t num_elements_cpu = 3; + static const size_t num_elements_gpu = 3; static_assert(sizeof(uchar3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements_cpu = 4; - static const int num_elements_gpu = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; static_assert(sizeof(uchar4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements_cpu = 1; - static const int num_elements_gpu = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; static_assert(sizeof(uint) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements_cpu = 2; - static const int num_elements_gpu = 2; + static const size_t num_elements_cpu = 2; + static const size_t num_elements_gpu = 2; static_assert(sizeof(uint2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements_cpu = 3; - static const int num_elements_gpu = 3; + static const size_t num_elements_cpu = 3; + static const size_t num_elements_gpu = 3; static_assert(sizeof(uint3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements_cpu = 4; - static const int num_elements_gpu = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; static_assert(sizeof(uint4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements_cpu = 1; - static const int num_elements_gpu = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; static_assert(sizeof(int) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements_cpu = 2; - static const int num_elements_gpu = 2; + static const size_t num_elements_cpu = 2; + static const size_t num_elements_gpu = 2; static_assert(sizeof(int2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements_cpu = 4; - static const int num_elements_gpu = 3; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 3; static_assert(sizeof(int3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements_cpu = 4; - static const int num_elements_gpu = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; static_assert(sizeof(int4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements_cpu = 1; - static const int num_elements_gpu = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; static_assert(sizeof(float) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements_cpu = 2; - static const int num_elements_gpu = 2; + static const size_t num_elements_cpu = 2; + static const size_t num_elements_gpu = 2; static_assert(sizeof(float2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements_cpu = 4; - static const int num_elements_gpu = 3; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 3; static_assert(sizeof(float3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements_cpu = 4; - static const int num_elements_gpu = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; static_assert(sizeof(float4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_HALF; - static const int num_elements_cpu = 1; - static const int num_elements_gpu = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; static_assert(sizeof(half) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT16; - static const int num_elements_cpu = 4; - static const int num_elements_gpu = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; static_assert(sizeof(ushort4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT16; - static const int num_elements_cpu = 1; - static const int num_elements_gpu = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; static_assert(sizeof(uint16_t) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_HALF; - static const int num_elements_cpu = 4; - static const int num_elements_gpu = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; static_assert(sizeof(half4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT64; - static const int num_elements_cpu = 1; - static const int num_elements_gpu = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; static_assert(sizeof(uint64_t) == num_elements_cpu * datatype_size(data_type)); }; @@ -296,7 +296,7 @@ class device_memory { void device_alloc(); void device_free(); void device_copy_to(); - void device_copy_from(int y, int w, int h, int elem); + void device_copy_from(size_t y, size_t w, size_t h, size_t elem); void device_zero(); bool device_is_cpu(); @@ -565,7 +565,7 @@ template class device_vector : public device_memory { device_copy_from(0, data_width, (data_height == 0) ? 1 : data_height, sizeof(T)); } - void copy_from_device(int y, int w, int h) + void copy_from_device(size_t y, size_t w, size_t h) { device_copy_from(y, w, h, sizeof(T)); } @@ -601,7 +601,7 @@ template class device_vector : public device_memory { class device_sub_ptr { public: - device_sub_ptr(device_memory &mem, int offset, int size); + device_sub_ptr(device_memory &mem, size_t offset, size_t size); ~device_sub_ptr(); device_ptr operator*() const diff --git a/intern/cycles/device/dummy/device.cpp b/intern/cycles/device/dummy/device.cpp index 678276ed025..e3cea272300 100644 --- a/intern/cycles/device/dummy/device.cpp +++ b/intern/cycles/device/dummy/device.cpp @@ -48,7 +48,7 @@ class DummyDevice : public Device { { } - virtual void mem_copy_from(device_memory &, int, int, int, int) override + virtual void mem_copy_from(device_memory &, size_t, size_t, size_t, size_t) override { } diff --git a/intern/cycles/device/multi/device.cpp b/intern/cycles/device/multi/device.cpp index 6dbcce2d9a5..4f995abf2c4 100644 --- a/intern/cycles/device/multi/device.cpp +++ b/intern/cycles/device/multi/device.cpp @@ -315,14 +315,14 @@ class MultiDevice : public Device { stats.mem_alloc(mem.device_size - existing_size); } - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override + void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override { device_ptr key = mem.device_pointer; - int i = 0, sub_h = h / devices.size(); + size_t i = 0, sub_h = h / devices.size(); foreach (SubDevice &sub, devices) { - int sy = y + i * sub_h; - int sh = (i == (int)devices.size() - 1) ? h - sub_h * i : sub_h; + size_t sy = y + i * sub_h; + size_t sh = (i == (size_t)devices.size() - 1) ? h - sub_h * i : sub_h; SubDevice *owner_sub = find_matching_mem_device(key, sub); mem.device = owner_sub->device; diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp index d35ff4cd03f..a14e41ec5be 100644 --- a/intern/cycles/integrator/shader_eval.cpp +++ b/intern/cycles/integrator/shader_eval.cpp @@ -149,14 +149,14 @@ bool ShaderEval::eval_gpu(Device *device, /* Execute work on GPU in chunk, so we can cancel. * TODO : query appropriate size from device.*/ - const int chunk_size = 65536; + const int64_t chunk_size = 65536; - const int work_size = output.size(); + const int64_t work_size = output.size(); void *d_input = (void *)input.device_pointer; void *d_output = (void *)output.device_pointer; - for (int d_offset = 0; d_offset < work_size; d_offset += chunk_size) { - int d_work_size = min(chunk_size, work_size - d_offset); + for (int64_t d_offset = 0; d_offset < work_size; d_offset += chunk_size) { + int64_t d_work_size = std::min(chunk_size, work_size - d_offset); void *args[] = {&d_input, &d_output, &d_offset, &d_work_size}; queue->enqueue(kernel, d_work_size, args); From 93997f9d0a44a83105537e130abc3b80fd5b9fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 23 Sep 2021 17:31:09 +0200 Subject: [PATCH 0149/1500] Cleanup: asset catalogs, correct assertion message There is no such thing as a "relative path" when it comes to asset catalog paths (they're always absolute). No functional changes. --- source/blender/blenkernel/intern/asset_catalog.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 2e56ca1392d..20f2dc0a571 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -271,7 +271,7 @@ std::unique_ptr AssetCatalogService::read_into_tree() AssetCatalogTreeItem::ChildMap *insert_to_map = &tree->children_; BLI_assert_msg(!ELEM(catalog->path[0], '/', '\\'), - "Malformed catalog path: Path should be formatted like a relative path"); + "Malformed catalog path; should not start with a separator"); const char *next_slash_ptr; /* Looks more complicated than it is, this just iterates over path components. E.g. From 942fc9f467c104150a474eb61a82957b5c9715f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 23 Sep 2021 17:52:53 +0200 Subject: [PATCH 0150/1500] Cleanup: bUUID, document the constructors No functional changes. --- source/blender/blenlib/BLI_uuid.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 592ac3d4607..72d95b41329 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -78,7 +78,10 @@ namespace blender::bke { class bUUID : public ::bUUID { public: bUUID() = default; + /** Initialise from the bUUID DNA struct. */ bUUID(const ::bUUID &struct_uuid); + + /** Initialise by parsing the string; undefined behaviour when the string is invalid. */ explicit bUUID(const std::string &string_formatted_uuid); uint64_t hash() const; From bd63944a739b4dcd49e8a65294ffb1b8a0a7b20b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 23 Sep 2021 17:55:43 +0200 Subject: [PATCH 0151/1500] UUID: place C++ code in correct namespace Put the `bUUID` class in the `blender` namespace, instead of the `blender::bke` namespace. As a result, some C++ code now correctly uses the C++ class, where previously it would use the C struct and use implicit casting where necessary. As a result, support for initializer lists had to be explicitly coded and in another place an explicit `::bUUID` was necessary to avoid ambiguity. --- source/blender/blenkernel/intern/asset.cc | 2 +- source/blender/blenlib/BLI_uuid.h | 9 ++++++-- source/blender/blenlib/intern/uuid.cc | 22 ++++++++++++++++--- source/blender/blenlib/tests/BLI_uuid_test.cc | 12 ++++++---- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc index ac9bcccc8bd..ae9ded3c754 100644 --- a/source/blender/blenkernel/intern/asset.cc +++ b/source/blender/blenkernel/intern/asset.cc @@ -126,7 +126,7 @@ void BKE_asset_metadata_catalog_id_clear(struct AssetMetaData *asset_data) } void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data, - const bUUID catalog_id, + const ::bUUID catalog_id, const char *catalog_simple_name) { asset_data->catalog_id = catalog_id; diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 72d95b41329..d21ccd450cc 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -68,19 +68,24 @@ bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) ATTR_NONNULL(); #ifdef __cplusplus } +# include # include /** Output the UUID as formatted ASCII string, see #BLI_uuid_format(). */ std::ostream &operator<<(std::ostream &stream, bUUID uuid); -namespace blender::bke { +namespace blender { class bUUID : public ::bUUID { public: bUUID() = default; + /** Initialise from the bUUID DNA struct. */ bUUID(const ::bUUID &struct_uuid); + /** Initialise from 11 integers, 5 for the regular fields and 6 for the `node` array. */ + bUUID(std::initializer_list field_values); + /** Initialise by parsing the string; undefined behaviour when the string is invalid. */ explicit bUUID(const std::string &string_formatted_uuid); @@ -89,6 +94,6 @@ class bUUID : public ::bUUID { bool operator==(bUUID uuid1, bUUID uuid2); -} // namespace blender::bke +} // namespace blender #endif diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index c1f7f8dfecd..cdb430c046a 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -18,6 +18,7 @@ * \ingroup bli */ +#include "BLI_assert.h" #include "BLI_uuid.h" #include @@ -139,7 +140,22 @@ std::ostream &operator<<(std::ostream &stream, bUUID uuid) return stream; } -namespace blender::bke { +namespace blender { + +bUUID::bUUID(const std::initializer_list field_values) +{ + BLI_assert_msg(field_values.size() == 11, "bUUID requires 5 regular fields + 6 `node` values"); + + auto field_iter = field_values.begin(); + + this->time_low = static_cast(*field_iter++); + this->time_mid = static_cast(*field_iter++); + this->time_hi_and_version = static_cast(*field_iter++); + this->clock_seq_hi_and_reserved = static_cast(*field_iter++); + this->clock_seq_low = static_cast(*field_iter++); + + std::copy(field_iter, field_values.end(), this->node); +} bUUID::bUUID(const std::string &string_formatted_uuid) { @@ -163,9 +179,9 @@ uint64_t bUUID::hash() const return uuid_as_int64[0] ^ uuid_as_int64[1]; } -bool operator==(bUUID uuid1, bUUID uuid2) +bool operator==(const bUUID uuid1, const bUUID uuid2) { return BLI_uuid_equal(uuid1, uuid2); } -} // namespace blender::bke +} // namespace blender diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc index 731489c6c9e..42aa618963f 100644 --- a/source/blender/blenlib/tests/BLI_uuid_test.cc +++ b/source/blender/blenlib/tests/BLI_uuid_test.cc @@ -18,6 +18,8 @@ #include "BLI_uuid.h" +namespace blender::tests { + TEST(BLI_uuid, generate_random) { const bUUID uuid = BLI_uuid_generate_random(); @@ -51,7 +53,7 @@ TEST(BLI_uuid, generate_many_random) TEST(BLI_uuid, nil_value) { const bUUID nil_uuid = BLI_uuid_nil(); - const bUUID zeroes_uuid = {0, 0, 0, 0, 0, 0}; + const bUUID zeroes_uuid{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; EXPECT_TRUE(BLI_uuid_equal(nil_uuid, zeroes_uuid)); EXPECT_TRUE(BLI_uuid_is_nil(nil_uuid)); @@ -91,13 +93,13 @@ TEST(BLI_uuid, string_formatting) EXPECT_EQ("00000001-0002-0003-0405-060000000007", buffer); /* Somewhat more complex bit patterns. This is a version 1 UUID generated from Python. */ - const bUUID uuid1 = {3540651616, 5282, 4588, 139, 153, {0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}}; + const bUUID uuid1 = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}; BLI_uuid_format(buffer.data(), uuid1); EXPECT_EQ("d30a0e60-14a2-11ec-8b99-f7736944db8b", buffer); /* Namespace UUID, example listed in RFC4211. */ const bUUID namespace_dns = { - 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, {0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}}; + 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}; BLI_uuid_format(buffer.data(), namespace_dns); EXPECT_EQ("6ba7b810-9dad-11d1-80b4-00c04fd430c8", buffer); } @@ -139,7 +141,9 @@ TEST(BLI_uuid, string_parsing_fail) TEST(BLI_uuid, stream_operator) { std::stringstream ss; - const bUUID uuid = {3540651616, 5282, 4588, 139, 153, {0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}}; + const bUUID uuid = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}; ss << uuid; EXPECT_EQ(ss.str(), "d30a0e60-14a2-11ec-8b99-f7736944db8b"); } + +} // namespace blender::tests From 105115da9f601c53d87cdc038f795f00f56a3495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 23 Sep 2021 17:22:17 +0200 Subject: [PATCH 0152/1500] UUID: add `!=` operator for comparing UUIDs Make it possible to unit test with `EXPECT_NE(uuid1, uuid2)`. --- source/blender/blenlib/BLI_uuid.h | 1 + source/blender/blenlib/intern/uuid.cc | 5 +++++ source/blender/blenlib/tests/BLI_uuid_test.cc | 8 ++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index d21ccd450cc..111a3d1eac3 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -93,6 +93,7 @@ class bUUID : public ::bUUID { }; bool operator==(bUUID uuid1, bUUID uuid2); +bool operator!=(bUUID uuid1, bUUID uuid2); } // namespace blender diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index cdb430c046a..bc8f67f939c 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -184,4 +184,9 @@ bool operator==(const bUUID uuid1, const bUUID uuid2) return BLI_uuid_equal(uuid1, uuid2); } +bool operator!=(const bUUID uuid1, const bUUID uuid2) +{ + return !(uuid1 == uuid2); +} + } // namespace blender diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc index 42aa618963f..b5d636ed1c3 100644 --- a/source/blender/blenlib/tests/BLI_uuid_test.cc +++ b/source/blender/blenlib/tests/BLI_uuid_test.cc @@ -40,7 +40,7 @@ TEST(BLI_uuid, generate_many_random) /* Generate lots of UUIDs to get some indication that the randomness is okay. */ for (int i = 0; i < 1000000; ++i) { const bUUID uuid = BLI_uuid_generate_random(); - EXPECT_FALSE(BLI_uuid_equal(first_uuid, uuid)); + EXPECT_NE(first_uuid, uuid); // Check that the non-random bits are set according to RFC4122. const uint16_t version = uuid.time_hi_and_version >> 12; @@ -55,7 +55,7 @@ TEST(BLI_uuid, nil_value) const bUUID nil_uuid = BLI_uuid_nil(); const bUUID zeroes_uuid{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - EXPECT_TRUE(BLI_uuid_equal(nil_uuid, zeroes_uuid)); + EXPECT_EQ(nil_uuid, zeroes_uuid); EXPECT_TRUE(BLI_uuid_is_nil(nil_uuid)); std::string buffer(36, '\0'); @@ -68,8 +68,8 @@ TEST(BLI_uuid, equality) const bUUID uuid1 = BLI_uuid_generate_random(); const bUUID uuid2 = BLI_uuid_generate_random(); - EXPECT_TRUE(BLI_uuid_equal(uuid1, uuid1)); - EXPECT_FALSE(BLI_uuid_equal(uuid1, uuid2)); + EXPECT_EQ(uuid1, uuid1); + EXPECT_NE(uuid1, uuid2); } TEST(BLI_uuid, string_formatting) From 38af29df5c307517dfdd0803b7e00d979be7185d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 23 Sep 2021 17:50:33 +0200 Subject: [PATCH 0153/1500] Geometry Nodes: simplify looping over attributes in geometry set This adds three new methods: * `InstancesComponent::foreach_reference_as_geometry(...)` * `GeometrySet::attribute_foreach(...)` * `GeometrySet::gather_attributes_for_propagation(...)` The goal is that these iteration primitives can be used in places where we use more specialized iterators currently. Differential Revision: https://developer.blender.org/D12613 --- .../blenkernel/BKE_attribute_access.hh | 5 ++ source/blender/blenkernel/BKE_geometry_set.hh | 19 +++++ .../blenkernel/BKE_geometry_set_instances.hh | 5 -- .../blender/blenkernel/intern/geometry_set.cc | 70 +++++++++++++++++++ .../intern/geometry_set_instances.cc | 35 ++++++++++ .../nodes/node_geo_point_distribute.cc | 1 - 6 files changed, 129 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 77db479f525..da3de2f08bd 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -122,6 +122,11 @@ struct AttributeMetaData { } }; +struct AttributeKind { + AttributeDomain domain; + CustomDataType data_type; +}; + /** * Base class for the attribute initializer types described below. */ diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 317d513dae6..a6c77c74b9e 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -25,6 +25,7 @@ #include "BLI_float3.hh" #include "BLI_float4x4.hh" +#include "BLI_function_ref.hh" #include "BLI_hash.hh" #include "BLI_map.hh" #include "BLI_set.hh" @@ -293,6 +294,21 @@ struct GeometrySet { bool owns_direct_data() const; void ensure_owns_direct_data(); + using AttributeForeachCallback = + blender::FunctionRef; + + void attribute_foreach(blender::Span component_types, + bool include_instances, + AttributeForeachCallback callback) const; + + void gather_attributes_for_propagation( + blender::Span component_types, + GeometryComponentType dst_component_type, + bool include_instances, + blender::Map &r_attributes) const; + /* Utility methods for creation. */ static GeometrySet create_with_mesh( Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); @@ -597,6 +613,9 @@ class InstancesComponent : public GeometryComponent { int attribute_domain_size(const AttributeDomain domain) const final; + void foreach_referenced_geometry( + blender::FunctionRef callback) const; + bool is_empty() const final; bool owns_direct_data() const override; diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh index 44a0ee30c4c..9d68d726c3a 100644 --- a/source/blender/blenkernel/BKE_geometry_set_instances.hh +++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh @@ -49,11 +49,6 @@ void geometry_set_gather_instances(const GeometrySet &geometry_set, GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set); GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set); -struct AttributeKind { - CustomDataType data_type; - AttributeDomain domain; -}; - /** * Add information about all the attributes on every component of the type. The resulting info * will contain the highest complexity data type and the highest priority domain among every diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index e717d289894..54e9fadf8ed 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -375,6 +375,76 @@ CurveEval *GeometrySet::get_curve_for_write() return component.get_for_write(); } +void GeometrySet::attribute_foreach(const Span component_types, + const bool include_instances, + const AttributeForeachCallback callback) const +{ + using namespace blender; + using namespace blender::bke; + for (const GeometryComponentType component_type : component_types) { + if (!this->has(component_type)) { + continue; + } + const GeometryComponent &component = *this->get_component_for_read(component_type); + component.attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + callback(attribute_id, meta_data, component); + return true; + }); + } + if (include_instances && this->has_instances()) { + const InstancesComponent &instances = *this->get_component_for_read(); + instances.foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { + instance_geometry_set.attribute_foreach(component_types, include_instances, callback); + }); + } +} + +void GeometrySet::gather_attributes_for_propagation( + const Span component_types, + const GeometryComponentType dst_component_type, + bool include_instances, + blender::Map &r_attributes) const +{ + using namespace blender; + using namespace blender::bke; + /* Only needed right now to check if an attribute is built-in on this component type. + * TODO: Get rid of the dummy component. */ + const GeometryComponent *dummy_component = GeometryComponent::create(dst_component_type); + this->attribute_foreach( + component_types, + include_instances, + [&](const AttributeIDRef &attribute_id, + const AttributeMetaData &meta_data, + const GeometryComponent &component) { + if (component.attribute_is_builtin(attribute_id)) { + if (!dummy_component->attribute_is_builtin(attribute_id)) { + /* Don't propagate built-in attributes that are not built-in on the destination + * component. */ + return; + } + } + if (attribute_id.is_anonymous()) { + if (!BKE_anonymous_attribute_id_has_strong_references(&attribute_id.anonymous_id())) { + /* Don't propagate anonymous attributes that are not used anymore. */ + return; + } + } + auto add_info = [&](AttributeKind *attribute_kind) { + attribute_kind->domain = meta_data.domain; + attribute_kind->data_type = meta_data.data_type; + }; + auto modify_info = [&](AttributeKind *attribute_kind) { + attribute_kind->domain = bke::attribute_domain_highest_priority( + {attribute_kind->domain, meta_data.domain}); + attribute_kind->data_type = bke::attribute_data_type_highest_complexity( + {attribute_kind->data_type, meta_data.data_type}); + }; + r_attributes.add_or_modify(attribute_id, add_info, modify_info); + }); + delete dummy_component; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 9dca2c2907e..10e698c8f8a 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -14,6 +14,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_collection.h" #include "BKE_geometry_set_instances.hh" #include "BKE_material.h" #include "BKE_mesh.h" @@ -23,6 +24,7 @@ #include "BKE_spline.hh" #include "DNA_collection_types.h" +#include "DNA_layer_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -745,3 +747,36 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) } } // namespace blender::bke + +void InstancesComponent::foreach_referenced_geometry( + blender::FunctionRef callback) const +{ + using namespace blender::bke; + for (const InstanceReference &reference : references_) { + switch (reference.type()) { + case InstanceReference::Type::Object: { + const Object &object = reference.object(); + const GeometrySet object_geometry_set = object_get_geometry_set_for_read(object); + callback(object_geometry_set); + break; + } + case InstanceReference::Type::Collection: { + Collection &collection = reference.collection(); + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) { + const GeometrySet object_geometry_set = object_get_geometry_set_for_read(*object); + callback(object_geometry_set); + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + break; + } + case InstanceReference::Type::GeometrySet: { + const GeometrySet &instance_geometry_set = reference.geometry_set(); + callback(instance_geometry_set); + break; + } + case InstanceReference::Type::None: { + break; + } + } + } +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index 04b4003daed..f95b0da86ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -36,7 +36,6 @@ #include "node_geometry_util.hh" -using blender::bke::AttributeKind; using blender::bke::GeometryInstanceGroup; namespace blender::nodes { From 502543e46b0c3ccc92db7161b899442f6d7a10f9 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 23 Sep 2021 17:59:14 +0200 Subject: [PATCH 0154/1500] Geometry Nodes: remove old method to iterate over attributes The previous commit added a new method to the same in a better way. --- .../blenkernel/BKE_geometry_set_instances.hh | 4 - .../intern/geometry_set_instances.cc | 128 ------------------ .../nodes/intern/geometry_nodes_eval_log.cc | 19 ++- 3 files changed, 13 insertions(+), 138 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh index 9d68d726c3a..653450c7d8e 100644 --- a/source/blender/blenkernel/BKE_geometry_set_instances.hh +++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh @@ -39,10 +39,6 @@ struct GeometryInstanceGroup { Vector transforms; }; -void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set, - const AttributeForeachCallback callback, - const int limit); - void geometry_set_gather_instances(const GeometrySet &geometry_set, Vector &r_instance_groups); diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 10e698c8f8a..162f91a4a47 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -189,134 +189,6 @@ void geometry_set_gather_instances(const GeometrySet &geometry_set, geometry_set_collect_recursive(geometry_set, unit_transform, r_instance_groups); } -static bool collection_instance_attribute_foreach(const Collection &collection, - const AttributeForeachCallback callback, - const int limit, - int &count); - -static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set, - const AttributeForeachCallback callback, - const int limit, - int &count); - -static bool object_instance_attribute_foreach(const Object &object, - const AttributeForeachCallback callback, - const int limit, - int &count) -{ - GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object); - if (!instances_attribute_foreach_recursive(instance_geometry_set, callback, limit, count)) { - return false; - } - - if (object.type == OB_EMPTY) { - const Collection *collection_instance = object.instance_collection; - if (collection_instance != nullptr) { - if (!collection_instance_attribute_foreach(*collection_instance, callback, limit, count)) { - return false; - } - } - } - return true; -} - -static bool collection_instance_attribute_foreach(const Collection &collection, - const AttributeForeachCallback callback, - const int limit, - int &count) -{ - LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) { - BLI_assert(collection_object->ob != nullptr); - const Object &object = *collection_object->ob; - if (!object_instance_attribute_foreach(object, callback, limit, count)) { - return false; - } - } - LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) { - BLI_assert(collection_child->collection != nullptr); - const Collection &collection = *collection_child->collection; - if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { - return false; - } - } - return true; -} - -/** - * \return True if the recursive iteration should continue, false if the limit is reached or the - * callback has returned false indicating it should stop. - */ -static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set, - const AttributeForeachCallback callback, - const int limit, - int &count) -{ - for (const GeometryComponent *component : geometry_set.get_components_for_read()) { - if (!component->attribute_foreach(callback)) { - return false; - } - } - - /* Now that this geometry set is visited, increase the count and check with the limit. */ - if (limit > 0 && count++ > limit) { - return false; - } - - const InstancesComponent *instances_component = - geometry_set.get_component_for_read(); - if (instances_component == nullptr) { - return true; - } - - for (const InstanceReference &reference : instances_component->references()) { - switch (reference.type()) { - case InstanceReference::Type::Object: { - const Object &object = reference.object(); - if (!object_instance_attribute_foreach(object, callback, limit, count)) { - return false; - } - break; - } - case InstanceReference::Type::Collection: { - const Collection &collection = reference.collection(); - if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { - return false; - } - break; - } - case InstanceReference::Type::GeometrySet: { - const GeometrySet &geometry_set = reference.geometry_set(); - if (!instances_attribute_foreach_recursive(geometry_set, callback, limit, count)) { - return false; - } - break; - } - case InstanceReference::Type::None: { - break; - } - } - } - - return true; -} - -/** - * Call the callback on all of this geometry set's components, including geometry sets from - * instances and recursive instances. This is necessary to access available attributes without - * making all of the set's geometry real. - * - * \param limit: The total number of geometry sets to visit before returning early. This is used - * to avoid looking through too many geometry sets recursively, as an explicit tradeoff in favor - * of performance at the cost of visiting every unique attribute. - */ -void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set, - const AttributeForeachCallback callback, - const int limit) -{ - int count = 0; - instances_attribute_foreach_recursive(geometry_set, callback, limit, count); -} - void geometry_set_gather_instances_attribute_info(Span set_groups, Span component_types, const Set &ignored_attributes, diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 3b3b643d0ae..fa9bf09d8d9 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -159,15 +159,22 @@ const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry) { - bke::geometry_set_instances_attribute_foreach( - geometry_set, - [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES, + GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_VOLUME}; + geometry_set.attribute_foreach( + all_component_types, + true, + [&](const bke::AttributeIDRef &attribute_id, + const AttributeMetaData &meta_data, + const GeometryComponent &UNUSED(component)) { if (attribute_id.is_named()) { this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); } - return true; - }, - 8); + }); + for (const GeometryComponent *component : geometry_set.get_components_for_read()) { component_types_.append(component->type()); switch (component->type()) { From 18a4dc869d16fca315df32a790843a0068c72a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 23 Sep 2021 18:22:47 +0200 Subject: [PATCH 0155/1500] Cleanup: UUID, fix clang-tidy warnings Use explicit `uint32_t` instead of `uint`, add a missing end-of-namespace comment, and change `auto` to `const auto *`. No functional changes. --- source/blender/blenlib/BLI_uuid.h | 4 ++-- source/blender/blenlib/intern/uuid.cc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 111a3d1eac3..eef37841d15 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -84,13 +84,13 @@ class bUUID : public ::bUUID { bUUID(const ::bUUID &struct_uuid); /** Initialise from 11 integers, 5 for the regular fields and 6 for the `node` array. */ - bUUID(std::initializer_list field_values); + bUUID(std::initializer_list field_values); /** Initialise by parsing the string; undefined behaviour when the string is invalid. */ explicit bUUID(const std::string &string_formatted_uuid); uint64_t hash() const; -}; +}; // namespace blender bool operator==(bUUID uuid1, bUUID uuid2); bool operator!=(bUUID uuid1, bUUID uuid2); diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index bc8f67f939c..fe237b8aae6 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -142,13 +142,13 @@ std::ostream &operator<<(std::ostream &stream, bUUID uuid) namespace blender { -bUUID::bUUID(const std::initializer_list field_values) +bUUID::bUUID(const std::initializer_list field_values) { BLI_assert_msg(field_values.size() == 11, "bUUID requires 5 regular fields + 6 `node` values"); - auto field_iter = field_values.begin(); + const auto *field_iter = field_values.begin(); - this->time_low = static_cast(*field_iter++); + this->time_low = *field_iter++; this->time_mid = static_cast(*field_iter++); this->time_hi_and_version = static_cast(*field_iter++); this->clock_seq_hi_and_reserved = static_cast(*field_iter++); From 0a8a726014fb4135414eedbe98170790fe09c2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 23 Sep 2021 18:27:15 +0200 Subject: [PATCH 0156/1500] bUUID: make it explicit the default constructor produces the nil value The implicit default constructor zeroes all plain data fields, and now this behaviour is explicit & tested for in a unit test. --- source/blender/blenlib/BLI_uuid.h | 3 +++ source/blender/blenlib/tests/BLI_uuid_test.cc | 3 +++ 2 files changed, 6 insertions(+) diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index eef37841d15..7ac676b7617 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -78,6 +78,9 @@ namespace blender { class bUUID : public ::bUUID { public: + /** + * Default constructor, used with `bUUID value{};`, will initialise to the nil UUID. + */ bUUID() = default; /** Initialise from the bUUID DNA struct. */ diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc index b5d636ed1c3..f2160f614ba 100644 --- a/source/blender/blenlib/tests/BLI_uuid_test.cc +++ b/source/blender/blenlib/tests/BLI_uuid_test.cc @@ -54,9 +54,12 @@ TEST(BLI_uuid, nil_value) { const bUUID nil_uuid = BLI_uuid_nil(); const bUUID zeroes_uuid{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + const bUUID default_constructed{}; EXPECT_EQ(nil_uuid, zeroes_uuid); EXPECT_TRUE(BLI_uuid_is_nil(nil_uuid)); + EXPECT_TRUE(BLI_uuid_is_nil(default_constructed)) + << "Default constructor should produce the nil value."; std::string buffer(36, '\0'); BLI_uuid_format(buffer.data(), nil_uuid); From 354c3eee40219a8f65bed7f3a81ada367a67c6a5 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 23 Sep 2021 18:24:34 +0200 Subject: [PATCH 0157/1500] Cycles: improve Auto Tile option description Ref T91645 --- intern/cycles/blender/addon/properties.py | 2 +- intern/cycles/blender/addon/ui.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 5fb0eeed925..e2b671848d0 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -739,7 +739,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): use_auto_tile: BoolProperty( name="Auto Tiles", - description="Automatically split image into tiles", + description="Automatically render high resolution images in tiles to reduce memory usage, using the specified tile size. Tiles are cached to disk while rendering to save memory", default=True, ) tile_size: IntProperty( diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index d02627b9936..ac96ddf5e10 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -613,8 +613,8 @@ class CYCLES_RENDER_PT_performance_threads(CyclesButtonsPanel, Panel): sub.prop(rd, "threads") -class CYCLES_RENDER_PT_performance_tiles(CyclesButtonsPanel, Panel): - bl_label = "Tiles" +class CYCLES_RENDER_PT_performance_memory(CyclesButtonsPanel, Panel): + bl_label = "Memory" bl_parent_id = "CYCLES_RENDER_PT_performance" def draw(self, context): @@ -2107,7 +2107,7 @@ classes = ( CYCLES_RENDER_PT_film_transparency, CYCLES_RENDER_PT_performance, CYCLES_RENDER_PT_performance_threads, - CYCLES_RENDER_PT_performance_tiles, + CYCLES_RENDER_PT_performance_memory, CYCLES_RENDER_PT_performance_acceleration_structure, CYCLES_RENDER_PT_performance_final_render, CYCLES_RENDER_PT_performance_viewport, From ed541de29dd01339736e04d95302e6e20495de54 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 23 Sep 2021 18:26:22 +0200 Subject: [PATCH 0158/1500] Fix T91626: Cycles sss behind fully transparent object renders differently --- .../integrator/integrator_shade_surface.h | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index 73b7cad32be..a24473addcc 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -365,19 +365,16 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, #ifdef __VOLUME__ if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { #endif + const int path_flag = INTEGRATOR_STATE(path, flag); - { - const int path_flag = INTEGRATOR_STATE(path, flag); #ifdef __SUBSURFACE__ - /* Can skip shader evaluation for BSSRDF exit point without bump mapping. */ - if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP))) + /* Can skip shader evaluation for BSSRDF exit point without bump mapping. */ + if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP))) #endif - { - /* Evaluate shader. */ - PROFILING_EVENT(PROFILING_SHADE_SURFACE_EVAL); - shader_eval_surface( - INTEGRATOR_STATE_PASS, &sd, render_buffer, path_flag); - } + { + /* Evaluate shader. */ + PROFILING_EVENT(PROFILING_SHADE_SURFACE_EVAL); + shader_eval_surface(INTEGRATOR_STATE_PASS, &sd, render_buffer, path_flag); } #ifdef __SUBSURFACE__ @@ -417,17 +414,20 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, /* Perform path termination. Most paths have already been terminated in * the intersect_closest kernel, this is just for emission and for dividing - * throughput by the probability at the right moment. */ - const int path_flag = INTEGRATOR_STATE(path, flag); - const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ? - 0.0f : - path_state_continuation_probability(INTEGRATOR_STATE_PASS, - path_flag); - if (probability == 0.0f) { - return false; - } - else if (probability != 1.0f) { - INTEGRATOR_STATE_WRITE(path, throughput) /= probability; + * throughput by the probability at the right moment. + * + * Also ensure we don't do it twice for SSS at both the entry and exit point. */ + if (!(path_flag & PATH_RAY_SUBSURFACE)) { + const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ? + 0.0f : + path_state_continuation_probability(INTEGRATOR_STATE_PASS, + path_flag); + if (probability == 0.0f) { + return false; + } + else if (probability != 1.0f) { + INTEGRATOR_STATE_WRITE(path, throughput) /= probability; + } } #ifdef __DENOISING_FEATURES__ From eb0eb54d9644c5139ef139fee1e14da35c4fab7e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 23 Sep 2021 11:41:46 -0500 Subject: [PATCH 0159/1500] Fix D12533: Simplify curve object to mesh conversion This patch simplifies the curve object to mesh conversion used by the object convert operator and exporters. The existing code had a convoluted model of ownership, and did quite a bit of unnecessary work. It also assumed that curve objects always evaluated to a mesh, which is not the case anymore. Now the code checks if the object it receives is evaluated. If so, it can simply return a copy of the evaluated mesh (or convert the evaluated curve wire edges to a mesh if there was no evaluated mesh). If the object isn't evaluated, it uses a temporary copy of the object with modifiers removed to create the mesh in the same way. This follows up on the recent changes to curve evaluation, namely that the result is always either a mesh or a wire curve. Differential Revision: https://developer.blender.org/D12533 --- source/blender/blenkernel/BKE_displist.h | 5 - source/blender/blenkernel/intern/displist.cc | 17 -- .../blender/blenkernel/intern/mesh_convert.cc | 267 ++++++------------ 3 files changed, 83 insertions(+), 206 deletions(-) diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h index db5663fcc94..8fb596a8096 100644 --- a/source/blender/blenkernel/BKE_displist.h +++ b/source/blender/blenkernel/BKE_displist.h @@ -87,11 +87,6 @@ void BKE_displist_make_curveTypes(struct Depsgraph *depsgraph, const struct Scene *scene, struct Object *ob, const bool for_render); -void BKE_displist_make_curveTypes_forRender(struct Depsgraph *depsgraph, - const struct Scene *scene, - struct Object *ob, - struct ListBase *dispbase, - struct Mesh **r_final); void BKE_displist_make_mball(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); void BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph, diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index e756daa1156..79e913d266f 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -1540,23 +1540,6 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, boundbox_displist_object(ob); } -void BKE_displist_make_curveTypes_forRender( - Depsgraph *depsgraph, const Scene *scene, Object *ob, ListBase *r_dispbase, Mesh **r_final) -{ - if (ob->runtime.curve_cache == nullptr) { - ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), __func__); - } - - if (ob->type == OB_SURF) { - evaluate_surface_object(depsgraph, scene, ob, true, r_dispbase, r_final); - } - else { - GeometrySet geometry_set = evaluate_curve_type_object(depsgraph, scene, ob, true, r_dispbase); - MeshComponent &mesh_component = geometry_set.get_component_for_write(); - *r_final = mesh_component.release(); - } -} - void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3]) { bool doit = false; diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 72ccfffbc3c..51acbeb8f5b 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -41,6 +41,7 @@ #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_editmesh.h" +#include "BKE_geometry_set.hh" #include "BKE_key.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" @@ -51,6 +52,7 @@ #include "BKE_mesh_runtime.h" #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" +#include "BKE_spline.hh" /* these 2 are only used by conversion functions */ #include "BKE_curve.h" /* -- */ @@ -58,6 +60,8 @@ /* -- */ #include "BKE_pointcloud.h" +#include "BKE_curve_to_mesh.hh" + #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -573,90 +577,6 @@ Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob) return BKE_mesh_new_nomain_from_curve_displist(ob, &disp); } -static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char *obdata_name) -{ - if (ob->runtime.data_eval && GS(((ID *)ob->runtime.data_eval)->name) != ID_ME) { - return; - } - - Mesh *me_eval = (Mesh *)ob->runtime.data_eval; - Mesh *me; - MVert *allvert = nullptr; - MEdge *alledge = nullptr; - MLoop *allloop = nullptr; - MLoopUV *alluv = nullptr; - MPoly *allpoly = nullptr; - int totvert, totedge, totloop, totpoly; - - Curve *cu = (Curve *)ob->data; - - if (me_eval == nullptr) { - if (mesh_nurbs_displist_to_mdata(cu, - dispbase, - &allvert, - &totvert, - &alledge, - &totedge, - &allloop, - &allpoly, - &alluv, - &totloop, - &totpoly) != 0) { - /* Error initializing */ - return; - } - - /* make mesh */ - me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name); - - me->totvert = totvert; - me->totedge = totedge; - me->totloop = totloop; - me->totpoly = totpoly; - - me->mvert = (MVert *)CustomData_add_layer( - &me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert); - me->medge = (MEdge *)CustomData_add_layer( - &me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge); - me->mloop = (MLoop *)CustomData_add_layer( - &me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop); - me->mpoly = (MPoly *)CustomData_add_layer( - &me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly); - - if (alluv) { - const char *uvname = "UVMap"; - me->mloopuv = (MLoopUV *)CustomData_add_layer_named( - &me->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, me->totloop, uvname); - } - - BKE_mesh_calc_normals(me); - } - else { - me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name); - - ob->runtime.data_eval = nullptr; - BKE_mesh_nomain_to_mesh(me_eval, me, ob, &CD_MASK_MESH, true); - } - - me->totcol = cu->totcol; - me->mat = cu->mat; - - mesh_copy_texture_space_from_curve_type(cu, me); - - cu->mat = nullptr; - cu->totcol = 0; - - /* Do not decrement ob->data usercount here, - * it's done at end of func with BKE_id_free_us() call. */ - ob->data = me; - ob->type = OB_MESH; - - /* For temporary objects in BKE_mesh_new_from_object don't remap - * the entire scene with associated depsgraph updates, which are - * problematic for renderers exporting data. */ - BKE_id_free(nullptr, cu); -} - struct EdgeLink { struct EdgeLink *next, *prev; void *edge; @@ -948,47 +868,25 @@ void BKE_pointcloud_to_mesh(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(sce BKE_object_free_derived_caches(ob); } -/* Create a temporary object to be used for nurbs-to-mesh conversion. - * - * This is more complex that it should be because #mesh_from_nurbs_displist will do more than - * simply conversion and will attempt to take over ownership of evaluated result and will also - * modify the input object. */ -static Object *object_for_curve_to_mesh_create(Object *object) +/* Create a temporary object to be used for nurbs-to-mesh conversion. */ +static Object *object_for_curve_to_mesh_create(const Object *object) { - Curve *curve = (Curve *)object->data; + const Curve *curve = (const Curve *)object->data; - /* Create object itself. */ + /* reate a temporary object which can be evaluated and modified by generic + * curve evaluation (hence the LIB_ID_COPY_SET_COPIED_ON_WRITE flag). */ Object *temp_object = (Object *)BKE_id_copy_ex( - nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE); + nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_SET_COPIED_ON_WRITE); /* Remove all modifiers, since we don't want them to be applied. */ BKE_object_free_modifiers(temp_object, LIB_ID_CREATE_NO_USER_REFCOUNT); - /* Copy relevant evaluated fields of curve cache. - * - * Note that there are extra fields in there like bevel and path, but those are not needed during - * conversion, so they are not copied to save unnecessary allocations. */ - if (temp_object->runtime.curve_cache == nullptr) { - temp_object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), - "CurveCache for curve types"); - } - - if (object->runtime.curve_cache != nullptr) { - BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp); - } - - /* Constructive modifiers will use mesh to store result. */ - if (object->runtime.data_eval != nullptr) { - BKE_id_copy_ex( - nullptr, object->runtime.data_eval, &temp_object->runtime.data_eval, LIB_ID_COPY_LOCALIZE); - } - - /* Need to create copy of curve itself as well, it will be freed by underlying conversion - * functions. - * - * NOTE: Copies the data, but not the shapekeys. */ - BKE_id_copy_ex( - nullptr, (const ID *)object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE); + /* Need to create copy of curve itself as well, since it will be changed by the curve evaluation + * process. NOTE: Copies the data, but not the shapekeys. */ + temp_object->data = BKE_id_copy_ex(nullptr, + (const ID *)object->data, + nullptr, + LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_SET_COPIED_ON_WRITE); Curve *temp_curve = (Curve *)temp_object->data; /* Make sure texture space is calculated for a copy of curve, it will be used for the final @@ -1006,23 +904,10 @@ static Object *object_for_curve_to_mesh_create(Object *object) /** * Populate `object->runtime.curve_cache` which is then used to create the mesh. */ -static void curve_to_mesh_eval_ensure(Object *object) +static void curve_to_mesh_eval_ensure(Object &object) { - Curve *curve = (Curve *)object->data; - Curve remapped_curve = *curve; - Object remapped_object = *object; - BKE_object_runtime_reset(&remapped_object); - - remapped_object.data = &remapped_curve; - - if (object->runtime.curve_cache == nullptr) { - object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), - "CurveCache for Curve"); - } - - /* Temporarily share the curve-cache with the temporary object, owned by `object`. */ - remapped_object.runtime.curve_cache = object->runtime.curve_cache; - + BLI_assert(GS(static_cast(object.data)->name) == ID_CU); + Curve &curve = *static_cast(object.data); /* Clear all modifiers for the bevel object. * * This is because they can not be reliably evaluated for an original object (at least because @@ -1031,83 +916,97 @@ static void curve_to_mesh_eval_ensure(Object *object) * So we create temporary copy of the object which will use same data as the original bevel, but * will have no modifiers. */ Object bevel_object = {{nullptr}}; - if (remapped_curve.bevobj != nullptr) { - bevel_object = *remapped_curve.bevobj; + if (curve.bevobj != nullptr) { + bevel_object = *curve.bevobj; BLI_listbase_clear(&bevel_object.modifiers); BKE_object_runtime_reset(&bevel_object); - remapped_curve.bevobj = &bevel_object; + curve.bevobj = &bevel_object; } /* Same thing for taper. */ Object taper_object = {{nullptr}}; - if (remapped_curve.taperobj != nullptr) { - taper_object = *remapped_curve.taperobj; + if (curve.taperobj != nullptr) { + taper_object = *curve.taperobj; BLI_listbase_clear(&taper_object.modifiers); BKE_object_runtime_reset(&taper_object); - remapped_curve.taperobj = &taper_object; + curve.taperobj = &taper_object; } /* NOTE: We don't have dependency graph or scene here, so we pass nullptr. This is all fine since * they are only used for modifier stack, which we have explicitly disabled for all objects. * * TODO(sergey): This is a very fragile logic, but proper solution requires re-writing quite a - * bit of internal functions (#mesh_from_nurbs_displist, BKE_mesh_nomain_to_mesh) and also - * Mesh From Curve operator. + * bit of internal functions (#BKE_mesh_nomain_to_mesh) and also Mesh From Curve operator. * Brecht says hold off with that. */ - Mesh *mesh_eval = nullptr; - BKE_displist_make_curveTypes_forRender( - nullptr, nullptr, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval); + BKE_displist_make_curveTypes(nullptr, nullptr, &object, true); - /* NOTE: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a - * real issue currently, code here is broken in more than one way, fix(es) will be done - * separately. */ - if (mesh_eval != nullptr) { - BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true); - } - - /* Owned by `object` & needed by the caller to create the mesh. */ - remapped_object.runtime.curve_cache = nullptr; - - BKE_object_runtime_free_data(&remapped_object); - BKE_object_runtime_free_data(&taper_object); + BKE_object_runtime_free_data(&bevel_object); BKE_object_runtime_free_data(&taper_object); } -static Mesh *mesh_new_from_curve_type_object(Object *object) +/* Necessary because #BKE_object_get_evaluated_mesh doesn't look in the geometry set yet. */ +static const Mesh *get_evaluated_mesh_from_object(const Object *object) { - Curve *curve = (Curve *)object->data; + const Mesh *mesh = BKE_object_get_evaluated_mesh(object); + if (mesh) { + return mesh; + } + GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval; + if (geometry_set_eval) { + return geometry_set_eval->get_mesh_for_read(); + } + return nullptr; +} + +static const CurveEval *get_evaluated_curve_from_object(const Object *object) +{ + GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval; + if (geometry_set_eval) { + return geometry_set_eval->get_curve_for_read(); + } + return nullptr; +} + +static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_object) +{ + const Mesh *mesh = get_evaluated_mesh_from_object(evaluated_object); + if (mesh) { + return BKE_mesh_copy_for_eval(mesh, false); + } + const CurveEval *curve = get_evaluated_curve_from_object(evaluated_object); + if (curve) { + return blender::bke::curve_to_wire_mesh(*curve); + } + return nullptr; +} + +static Mesh *mesh_new_from_curve_type_object(const Object *object) +{ + /* If the object is evaluated, it should either have an evaluated mesh or curve data already. + * The mesh can be duplicated, or the curve converted to wire mesh edges. */ + if (DEG_is_evaluated_object(object)) { + return mesh_new_from_evaluated_curve_type_object(object); + } + + /* Otherwise, create a temporary "fake" evaluated object and try again. This might have + * different results, since in order to avoid having adverse affects to other original objects, + * modifiers are cleared. An alternative would be to create a temporary depsgraph only for this + * object and its dependencies. */ Object *temp_object = object_for_curve_to_mesh_create(object); - Curve *temp_curve = (Curve *)temp_object->data; + ID *temp_data = static_cast(temp_object->data); + curve_to_mesh_eval_ensure(*temp_object); - /* When input object is an original one, we don't have evaluated curve cache yet, so need to - * create it in the temporary object. */ - if (!DEG_is_evaluated_object(object)) { - curve_to_mesh_eval_ensure(temp_object); + /* If evaluating the curve replaced object data with different data, free the original data. */ + if (temp_data != temp_object->data) { + BKE_id_free(nullptr, temp_data); } - /* Reset pointers before conversion. */ - temp_curve->editfont = nullptr; - temp_curve->editnurb = nullptr; - - /* Convert to mesh. */ - mesh_from_nurbs_displist( - temp_object, &temp_object->runtime.curve_cache->disp, curve->id.name + 2); - - /* #mesh_from_nurbs_displist changes the type to a mesh, check it worked. If it didn't - * the curve did not have any segments or otherwise would have generated an empty mesh. */ - if (temp_object->type != OB_MESH) { - BKE_id_free(nullptr, temp_object->data); - BKE_id_free(nullptr, temp_object); - return nullptr; - } - - Mesh *mesh_result = (Mesh *)temp_object->data; + Mesh *mesh = mesh_new_from_evaluated_curve_type_object(temp_object); + BKE_id_free(nullptr, temp_object->data); BKE_id_free(nullptr, temp_object); - /* NOTE: Materials are copied in #mesh_from_nurbs_displist(). */ - - return mesh_result; + return mesh; } static Mesh *mesh_new_from_mball_object(Object *object) From 323fd80aada46105e23985f5646f9252b5e6f193 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 23 Sep 2021 18:56:29 +0200 Subject: [PATCH 0160/1500] UI: Tree-View API for easy creation of tree UIs This follows three main targets: * Make creation of new tree UIs easy. * Groundwork to generalize tree UIs (so e.g. Outliner, animation channels, asset catalogs and spreadsheet data-sets don't have to re-implement basic tree UI code) or even other data-view UIs. * Better separate data and UI state. E.g. with this, tree-item selection or the open/collapsed state can be stored on the UI level, rather than in data. (Asset Catalogs need this, storing UI state info in them is not an option.) In addition, the design should be well testable and could even be exposed to Python. Note that things will likely change in master still. E.g. the actually resulting UI isn't very nice visually yet. The design is documented here: https://wiki.blender.org/wiki/Source/Interface/Views Differential Revision: https://developer.blender.org/D12573 --- source/blender/editors/include/UI_interface.h | 9 + .../blender/editors/include/UI_interface.hh | 35 ++ .../blender/editors/include/UI_tree_view.hh | 238 +++++++++++++ .../blender/editors/interface/CMakeLists.txt | 2 + source/blender/editors/interface/interface.c | 43 ++- .../editors/interface/interface_handlers.c | 20 +- .../editors/interface/interface_intern.h | 18 + .../editors/interface/interface_query.c | 3 +- .../editors/interface/interface_view.cc | 116 +++++++ .../editors/interface/interface_widgets.c | 37 +- source/blender/editors/interface/tree_view.cc | 316 ++++++++++++++++++ source/blender/editors/util/CMakeLists.txt | 2 + 12 files changed, 826 insertions(+), 13 deletions(-) create mode 100644 source/blender/editors/include/UI_interface.hh create mode 100644 source/blender/editors/include/UI_tree_view.hh create mode 100644 source/blender/editors/interface/interface_view.cc create mode 100644 source/blender/editors/interface/tree_view.cc diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 916105b0f8e..f7842270746 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -84,6 +84,10 @@ typedef struct uiBlock uiBlock; typedef struct uiBut uiBut; typedef struct uiLayout uiLayout; typedef struct uiPopupBlockHandle uiPopupBlockHandle; +/* C handle for C++ #ui::AbstractTreeView type. */ +typedef struct uiTreeViewHandle uiTreeViewHandle; +/* C handle for C++ #ui::AbstractTreeViewItem type. */ +typedef struct uiTreeViewItemHandle uiTreeViewItemHandle; /* Defines */ @@ -389,6 +393,8 @@ typedef enum { UI_BTYPE_GRIP = 57 << 9, UI_BTYPE_DECORATOR = 58 << 9, UI_BTYPE_DATASETROW = 59 << 9, + /* An item in a tree view. Parent items may be collapsible. */ + UI_BTYPE_TREEROW = 60 << 9, } eButType; #define BUTTYPE (63 << 9) @@ -1672,6 +1678,7 @@ void UI_but_datasetrow_component_set(uiBut *but, uint8_t geometry_component_type void UI_but_datasetrow_domain_set(uiBut *but, uint8_t attribute_domain); uint8_t UI_but_datasetrow_component_get(uiBut *but); uint8_t UI_but_datasetrow_domain_get(uiBut *but); +void UI_but_treerow_indentation_set(uiBut *but, int indentation); void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4]); @@ -2754,6 +2761,8 @@ void UI_interface_tag_script_reload(void); /* Support click-drag motion which presses the button and closes a popover (like a menu). */ #define USE_UI_POPOVER_ONCE +bool UI_tree_view_item_is_active(uiTreeViewItemHandle *item_); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh new file mode 100644 index 00000000000..4a583d0225e --- /dev/null +++ b/source/blender/editors/include/UI_interface.hh @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup editorui + */ + +#pragma once + +#include + +#include "BLI_string_ref.hh" + +struct uiBlock; +namespace blender::ui { +class AbstractTreeView; +} + +blender::ui::AbstractTreeView *UI_block_add_view( + uiBlock &block, + blender::StringRef idname, + std::unique_ptr tree_view); diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh new file mode 100644 index 00000000000..51b8e65521a --- /dev/null +++ b/source/blender/editors/include/UI_tree_view.hh @@ -0,0 +1,238 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup editorui + */ + +#pragma once + +#include +#include + +#include "BLI_function_ref.hh" +#include "BLI_vector.hh" + +#include "UI_resources.h" + +struct PointerRNA; +struct uiBlock; +struct uiBut; +struct uiButTreeRow; +struct uiLayout; + +namespace blender::ui { + +class AbstractTreeView; +class AbstractTreeViewItem; + +/* ---------------------------------------------------------------------- */ +/** \name Tree-View Item Container + * \{ */ + +/** + * Helper base class to expose common child-item data and functionality to both #AbstractTreeView + * and #AbstractTreeViewItem. + * + * That means this type can be used whenever either a #AbstractTreeView or a + * #AbstractTreeViewItem is needed. + */ +class TreeViewItemContainer { + friend class AbstractTreeView; + friend class AbstractTreeViewItem; + + /* Private constructor, so only the friends above can create this! */ + TreeViewItemContainer() = default; + + protected: + Vector> children_; + /** Adding the first item to the root will set this, then it's passed on to all children. */ + TreeViewItemContainer *root_ = nullptr; + /** Pointer back to the owning item. */ + AbstractTreeViewItem *parent_ = nullptr; + + public: + enum class IterOptions { + None = 0, + SkipCollapsed = 1 << 0, + + /* Keep ENUM_OPERATORS() below updated! */ + }; + using ItemIterFn = FunctionRef; + + /** + * Convenience wrapper taking the arguments needed to construct an item of type \a ItemT. Calls + * the version just below. + */ + template ItemT &add_tree_item(Args &&...args) + { + static_assert(std::is_base_of::value, + "Type must derive from and implement the AbstractTreeViewItem interface"); + + return dynamic_cast( + add_tree_item(std::make_unique(std::forward(args)...))); + } + + AbstractTreeViewItem &add_tree_item(std::unique_ptr item); + + protected: + void foreach_item_recursive(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; +}; + +ENUM_OPERATORS(TreeViewItemContainer::IterOptions, + TreeViewItemContainer::IterOptions::SkipCollapsed); + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Tree-View Builders + * \{ */ + +class TreeViewBuilder { + uiBlock &block_; + + public: + TreeViewBuilder(uiBlock &block); + + void build_tree_view(AbstractTreeView &tree_view); +}; + +class TreeViewLayoutBuilder { + uiBlock &block_; + + friend TreeViewBuilder; + + public: + void build_row(AbstractTreeViewItem &item) const; + uiBlock &block() const; + uiLayout *current_layout() const; + + private: + /* Created through #TreeViewBuilder. */ + TreeViewLayoutBuilder(uiBlock &block); +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Tree-View Base Class + * \{ */ + +class AbstractTreeView : public TreeViewItemContainer { + friend TreeViewBuilder; + friend TreeViewLayoutBuilder; + + public: + virtual ~AbstractTreeView() = default; + + void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; + + protected: + virtual void build_tree() = 0; + + private: + /** Match the tree-view against an earlier version of itself (if any) and copy the old UI state + * (e.g. collapsed, active, selected) to the new one. See + * #AbstractTreeViewItem.update_from_old(). */ + void update_from_old(uiBlock &new_block); + static void update_children_from_old_recursive(const TreeViewItemContainer &new_items, + const TreeViewItemContainer &old_items); + static AbstractTreeViewItem *find_matching_child(const AbstractTreeViewItem &lookup_item, + const TreeViewItemContainer &items); + void build_layout_from_tree(const TreeViewLayoutBuilder &builder); +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Tree-View Item Type + * \{ */ + +/** \brief Abstract base class for defining a customizable tree-view item. + * + * The tree-view item defines how to build its data into a tree-row. There are implementations for + * common layouts, e.g. #BasicTreeViewItem. + * It also stores state information that needs to be persistent over redraws, like the collapsed + * state. + */ +class AbstractTreeViewItem : public TreeViewItemContainer { + friend class AbstractTreeView; + + bool is_open_ = false; + bool is_active_ = false; + + protected: + /** This label is used for identifying an item (together with its parent's labels). */ + std::string label_{}; + + public: + virtual ~AbstractTreeViewItem() = default; + + virtual void build_row(uiLayout &row) = 0; + + virtual void on_activate(); + + /** Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of the + * last redraw to this item. If sub-classes introduce more advanced state they should override + * this and make it update their state accordingly. */ + virtual void update_from_old(AbstractTreeViewItem &old); + + const AbstractTreeView &get_tree_view() const; + int count_parents() const; + void set_active(bool value = true); + bool is_active() const; + void toggle_collapsed(); + bool is_collapsed() const; + void set_collapsed(bool collapsed); + bool is_collapsible() const; +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Predefined Tree-View Item Types + * + * Common, Basic Tree-View Item Types. + * \{ */ + +/** + * The most basic type, just a label with an icon. + */ +class BasicTreeViewItem : public AbstractTreeViewItem { + public: + using ActivateFn = std::function; + BIFIconID icon; + + BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE, ActivateFn activate_fn = nullptr); + + void build_row(uiLayout &row) override; + void on_activate() override; + + protected: + /** Created in the #build() function. */ + uiButTreeRow *tree_row_but_ = nullptr; + /** Optionally passed to the #BasicTreeViewItem constructor. Called when activating this tree + * view item. This way users don't have to sub-class #BasicTreeViewItem, just to implement + * custom activation behavior (a common thing to do). */ + ActivateFn activate_fn_; + + uiBut *button(); + BIFIconID get_draw_icon() const; +}; + +/** \} */ + +} // namespace blender::ui diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 39dd6143eb9..79e08f46292 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -73,8 +73,10 @@ set(SRC interface_templates.c interface_undo.c interface_utils.c + interface_view.cc interface_widgets.c resources.c + tree_view.cc view2d.c view2d_draw.c view2d_edge_pan.c diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index fd75be5b847..beee622673c 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -856,10 +856,21 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) oldbut->hardmax = but->hardmax; } - if (oldbut->type == UI_BTYPE_PROGRESS_BAR) { - uiButProgressbar *progress_oldbut = (uiButProgressbar *)oldbut; - uiButProgressbar *progress_but = (uiButProgressbar *)but; - progress_oldbut->progress = progress_but->progress; + switch (oldbut->type) { + case UI_BTYPE_PROGRESS_BAR: { + uiButProgressbar *progress_oldbut = (uiButProgressbar *)oldbut; + uiButProgressbar *progress_but = (uiButProgressbar *)but; + progress_oldbut->progress = progress_but->progress; + break; + } + case UI_BTYPE_TREEROW: { + uiButTreeRow *treerow_oldbut = (uiButTreeRow *)oldbut; + uiButTreeRow *treerow_newbut = (uiButTreeRow *)but; + SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item); + break; + } + default: + break; } /* move/copy string from the new button to the old */ @@ -2203,6 +2214,15 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) } } break; + case UI_BTYPE_TREEROW: { + uiButTreeRow *tree_row_but = (uiButTreeRow *)but; + + is_push = -1; + if (tree_row_but->tree_item) { + is_push = UI_tree_view_item_is_active(tree_row_but->tree_item); + } + break; + } default: is_push = -1; break; @@ -3447,6 +3467,7 @@ void UI_block_free(const bContext *C, uiBlock *block) BLI_freelistN(&block->color_pickers.list); ui_block_free_button_groups(block); + ui_block_free_views(block); MEM_freeN(block); } @@ -3942,6 +3963,10 @@ static void ui_but_alloc_info(const eButType type, alloc_size = sizeof(uiButDatasetRow); alloc_str = "uiButDatasetRow"; break; + case UI_BTYPE_TREEROW: + alloc_size = sizeof(uiButTreeRow); + alloc_str = "uiButTreeRow"; + break; default: alloc_size = sizeof(uiBut); alloc_str = "uiBut"; @@ -4141,6 +4166,7 @@ static uiBut *ui_def_but(uiBlock *block, UI_BTYPE_BUT_MENU, UI_BTYPE_SEARCH_MENU, UI_BTYPE_DATASETROW, + UI_BTYPE_TREEROW, UI_BTYPE_POPOVER)) { but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT); } @@ -6878,6 +6904,15 @@ void UI_but_datasetrow_indentation_set(uiBut *but, int indentation) BLI_assert(indentation >= 0); } +void UI_but_treerow_indentation_set(uiBut *but, int indentation) +{ + uiButTreeRow *but_row = (uiButTreeRow *)but; + BLI_assert(but->type == UI_BTYPE_TREEROW); + + but_row->indentation = indentation; + BLI_assert(indentation >= 0); +} + /** * Adds a hint to the button which draws right aligned, grayed out and never clipped. */ diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 77ae16d7cc7..aee66ec3a93 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -384,6 +384,8 @@ typedef struct uiHandleButtonData { /* booleans (could be made into flags) */ bool cancel, escapecancel; bool applied, applied_interactive; + /* Button is being applied through an extra icon. */ + bool apply_through_extra_icon; bool changed_cursor; wmTimer *flashtimer; @@ -1164,6 +1166,16 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu data->applied = true; } +static void ui_apply_but_TREEROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data) +{ + if (data->apply_through_extra_icon) { + /* Don't apply this, it would cause unintended tree-row toggling when clicking on extra icons. + */ + return; + } + ui_apply_but_ROW(C, block, but, data); +} + /** * \note Ownership of \a properties is moved here. The #uiAfterFunc owns it now. * @@ -2307,6 +2319,9 @@ static void ui_apply_but( case UI_BTYPE_ROW: ui_apply_but_ROW(C, block, but, data); break; + case UI_BTYPE_TREEROW: + ui_apply_but_TREEROW(C, block, but, data); + break; case UI_BTYPE_LISTROW: ui_apply_but_LISTROW(C, block, but, data); break; @@ -4194,6 +4209,8 @@ static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleBu static void ui_but_extra_operator_icon_apply(bContext *C, uiBut *but, uiButExtraOpIcon *op_icon) { + but->active->apply_through_extra_icon = true; + if (but->active->interactive) { ui_apply_but(C, but->block, but, but->active, true); } @@ -4737,7 +4754,7 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons /* Behave like other menu items. */ do_activate = (event->val == KM_RELEASE); } - else { + else if (!ui_do_but_extra_operator_icon(C, but, data, event)) { /* Also use double-clicks to prevent fast clicks to leak to other handlers (T76481). */ do_activate = ELEM(event->val, KM_PRESS, KM_DBL_CLICK); } @@ -7966,6 +7983,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_CHECKBOX: case UI_BTYPE_CHECKBOX_N: case UI_BTYPE_ROW: + case UI_BTYPE_TREEROW: case UI_BTYPE_DATASETROW: retval = ui_do_but_TOG(C, but, data, event); break; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index d61104f094e..95e6791b359 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -360,6 +360,14 @@ typedef struct uiButDatasetRow { int indentation; } uiButDatasetRow; +/** Derived struct for #UI_BTYPE_TREEROW. */ +typedef struct uiButTreeRow { + uiBut but; + + uiTreeViewItemHandle *tree_item; + int indentation; +} uiButTreeRow; + /** Derived struct for #UI_BTYPE_HSVCUBE. */ typedef struct uiButHSVCube { uiBut but; @@ -488,6 +496,11 @@ struct uiBlock { ListBase contexts; + /** A block can store "views" on data-sets. Currently tree-views (#AbstractTreeView) only. + * Others are imaginable, e.g. table-views, grid-views, etc. These are stored here to support + * state that is persistent over redraws (e.g. collapsed tree-view items). */ + ListBase views; + char name[UI_MAX_NAME_STR]; float winmat[4][4]; @@ -1274,6 +1287,11 @@ bool ui_jump_to_target_button_poll(struct bContext *C); /* interface_queries.c */ void ui_interface_tag_script_reload_queries(void); +/* interface_view.cc */ +void ui_block_free_views(struct uiBlock *block); +uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, + const uiTreeViewHandle *new_view); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 09429bb6df5..15d1d2f2eec 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -69,7 +69,8 @@ bool ui_but_is_toggle(const uiBut *but) UI_BTYPE_CHECKBOX, UI_BTYPE_CHECKBOX_N, UI_BTYPE_ROW, - UI_BTYPE_DATASETROW); + UI_BTYPE_DATASETROW, + UI_BTYPE_TREEROW); } /** diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc new file mode 100644 index 00000000000..7419f21cbc6 --- /dev/null +++ b/source/blender/editors/interface/interface_view.cc @@ -0,0 +1,116 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edinterface + * + * This part of the UI-View API is mostly needed to support persistent state of items within the + * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we + * can compare the old view items with the new view items and keep state persistent for matching + * ones. + */ + +#include +#include + +#include "BLI_listbase.h" + +#include "interface_intern.h" + +#include "UI_interface.hh" +#include "UI_tree_view.hh" + +using namespace blender; +using namespace blender::ui; + +/** + * Wrapper to store views in a #ListBase. There's no `uiView` base class, we just store views as a + * #std::variant. + */ +struct ViewLink : public Link { + using TreeViewPtr = std::unique_ptr; + + std::string idname; + /* Note: Can't use std::get() on this until minimum macOS deployment target is 10.14. */ + std::variant view; +}; + +template T *get_view_from_link(ViewLink &link) +{ + auto *t_uptr = std::get_if>(&link.view); + return t_uptr ? t_uptr->get() : nullptr; +} + +/** + * Override this for all available tree types. + */ +AbstractTreeView *UI_block_add_view(uiBlock &block, + StringRef idname, + std::unique_ptr tree_view) +{ + ViewLink *view_link = OBJECT_GUARDED_NEW(ViewLink); + BLI_addtail(&block.views, view_link); + + view_link->view = std::move(tree_view); + view_link->idname = idname; + + return get_view_from_link(*view_link); +} + +void ui_block_free_views(uiBlock *block) +{ + LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) { + OBJECT_GUARDED_DELETE(link, ViewLink); + } +} + +static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view) +{ + /* First get the idname the of the view we're looking for. */ + LISTBASE_FOREACH (ViewLink *, view_link, &block.views) { + if (get_view_from_link(*view_link) == &view) { + return view_link->idname; + } + } + + return {}; +} + +uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, + const uiTreeViewHandle *new_view_handle) +{ + const AbstractTreeView &needle_view = reinterpret_cast( + *new_view_handle); + + uiBlock *old_block = new_block->oldblock; + if (!old_block) { + return nullptr; + } + + StringRef idname = ui_block_view_find_idname(*new_block, needle_view); + if (idname.is_empty()) { + return nullptr; + } + + LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) { + if (old_view_link->idname == idname) { + return reinterpret_cast( + get_view_from_link(*old_view_link)); + } + } + + return nullptr; +} diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 0dc7c2d3f9a..375206cab44 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -115,6 +115,7 @@ typedef enum { UI_WTYPE_PROGRESSBAR, UI_WTYPE_NODESOCKET, UI_WTYPE_DATASETROW, + UI_WTYPE_TREEROW, } uiWidgetTypeEnum; /* Button state argument shares bits with 'uiBut.flag'. @@ -3679,10 +3680,9 @@ static void widget_progressbar( widgetbase_draw(&wtb_bar, wcol); } -static void widget_datasetrow( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int UNUSED(roundboxalign)) +static void widget_treerow_exec( + uiWidgetColors *wcol, rcti *rect, int state, int UNUSED(roundboxalign), int indentation) { - uiButDatasetRow *but_componentrow = (uiButDatasetRow *)but; uiWidgetBase wtb; widget_init(&wtb); @@ -3695,10 +3695,24 @@ static void widget_datasetrow( widgetbase_draw(&wtb, wcol); } - BLI_rcti_resize(rect, - BLI_rcti_size_x(rect) - UI_UNIT_X * but_componentrow->indentation, - BLI_rcti_size_y(rect)); - BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * but_componentrow->indentation, 0); + BLI_rcti_resize(rect, BLI_rcti_size_x(rect) - UI_UNIT_X * indentation, BLI_rcti_size_y(rect)); + BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0); +} + +static void widget_treerow( + uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign) +{ + uiButTreeRow *tree_row = (uiButTreeRow *)but; + BLI_assert(but->type == UI_BTYPE_TREEROW); + widget_treerow_exec(wcol, rect, state, roundboxalign, tree_row->indentation); +} + +static void widget_datasetrow( + uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign) +{ + uiButDatasetRow *dataset_row = (uiButDatasetRow *)but; + BLI_assert(but->type == UI_BTYPE_DATASETROW); + widget_treerow_exec(wcol, rect, state, roundboxalign, dataset_row->indentation); } static void widget_nodesocket( @@ -4492,6 +4506,10 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.custom = widget_datasetrow; break; + case UI_WTYPE_TREEROW: + wt.custom = widget_treerow; + break; + case UI_WTYPE_NODESOCKET: wt.custom = widget_nodesocket; break; @@ -4824,6 +4842,11 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu fstyle = &style->widgetlabel; break; + case UI_BTYPE_TREEROW: + wt = widget_type(UI_WTYPE_TREEROW); + fstyle = &style->widgetlabel; + break; + case UI_BTYPE_SCROLL: wt = widget_type(UI_WTYPE_SCROLL); break; diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc new file mode 100644 index 00000000000..c5770e808fa --- /dev/null +++ b/source/blender/editors/interface/tree_view.cc @@ -0,0 +1,316 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edinterface + */ + +#include "DNA_userdef_types.h" + +#include "interface_intern.h" + +#include "UI_interface.h" + +#include "UI_tree_view.hh" + +namespace blender::ui { + +/* ---------------------------------------------------------------------- */ + +/** + * Add a tree-item to the container. This is the only place where items should be added, it handles + * important invariants! + */ +AbstractTreeViewItem &TreeViewItemContainer::add_tree_item( + std::unique_ptr item) +{ + children_.append(std::move(item)); + + /* The first item that will be added to the root sets this. */ + if (root_ == nullptr) { + root_ = this; + } + + AbstractTreeViewItem &added_item = *children_.last(); + added_item.root_ = root_; + if (root_ != this) { + /* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely + * nice to static_cast this, but well... */ + added_item.parent_ = static_cast(this); + } + + return added_item; +} + +void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptions options) const +{ + for (auto &child : children_) { + iter_fn(*child); + if (bool(options & IterOptions::SkipCollapsed) && child->is_collapsed()) { + continue; + } + + child->foreach_item_recursive(iter_fn, options); + } +} + +/* ---------------------------------------------------------------------- */ + +void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) const +{ + foreach_item_recursive(iter_fn, options); +} + +void AbstractTreeView::build_layout_from_tree(const TreeViewLayoutBuilder &builder) +{ + uiLayout *prev_layout = builder.current_layout(); + + uiLayoutColumn(prev_layout, true); + + foreach_item([&builder](AbstractTreeViewItem &item) { builder.build_row(item); }, + IterOptions::SkipCollapsed); + + UI_block_layout_set_current(&builder.block(), prev_layout); +} + +void AbstractTreeView::update_from_old(uiBlock &new_block) +{ + uiBlock *old_block = new_block.oldblock; + if (!old_block) { + return; + } + + uiTreeViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block( + &new_block, reinterpret_cast(this)); + if (!old_view_handle) { + return; + } + + AbstractTreeView &old_view = reinterpret_cast(*old_view_handle); + update_children_from_old_recursive(*this, old_view); +} + +void AbstractTreeView::update_children_from_old_recursive(const TreeViewItemContainer &new_items, + const TreeViewItemContainer &old_items) +{ + for (const auto &new_item : new_items.children_) { + AbstractTreeViewItem *matching_old_item = find_matching_child(*new_item, old_items); + if (!matching_old_item) { + continue; + } + + new_item->update_from_old(*matching_old_item); + + /* Recurse into children of the matched item. */ + update_children_from_old_recursive(*new_item, *matching_old_item); + } +} + +AbstractTreeViewItem *AbstractTreeView::find_matching_child( + const AbstractTreeViewItem &lookup_item, const TreeViewItemContainer &items) +{ + for (const auto &iter_item : items.children_) { + if (lookup_item.label_ == iter_item->label_) { + /* We have a matching item! */ + return iter_item.get(); + } + } + + return nullptr; +} + +/* ---------------------------------------------------------------------- */ + +void AbstractTreeViewItem::on_activate() +{ + /* Do nothing by default. */ +} + +void AbstractTreeViewItem::update_from_old(AbstractTreeViewItem &old) +{ + is_open_ = old.is_open_; + is_active_ = old.is_active_; +} + +const AbstractTreeView &AbstractTreeViewItem::get_tree_view() const +{ + return static_cast(*root_); +} + +int AbstractTreeViewItem::count_parents() const +{ + int i = 0; + for (TreeViewItemContainer *parent = parent_; parent; parent = parent->parent_) { + i++; + } + return i; +} + +void AbstractTreeViewItem::set_active(bool value) +{ + if (value && !is_active()) { + /* Deactivate other items in the tree. */ + get_tree_view().foreach_item([](auto &item) { item.set_active(false); }); + on_activate(); + } + is_active_ = value; +} + +bool AbstractTreeViewItem::is_active() const +{ + return is_active_; +} + +bool AbstractTreeViewItem::is_collapsed() const +{ + return is_collapsible() && !is_open_; +} + +void AbstractTreeViewItem::toggle_collapsed() +{ + is_open_ = !is_open_; +} + +void AbstractTreeViewItem::set_collapsed(bool collapsed) +{ + is_open_ = !collapsed; +} + +bool AbstractTreeViewItem::is_collapsible() const +{ + return !children_.is_empty(); +} + +/* ---------------------------------------------------------------------- */ + +TreeViewBuilder::TreeViewBuilder(uiBlock &block) : block_(block) +{ +} + +void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view) +{ + tree_view.build_tree(); + tree_view.update_from_old(block_); + tree_view.build_layout_from_tree(TreeViewLayoutBuilder(block_)); +} + +/* ---------------------------------------------------------------------- */ + +TreeViewLayoutBuilder::TreeViewLayoutBuilder(uiBlock &block) : block_(block) +{ +} + +void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const +{ + uiLayout *prev_layout = current_layout(); + uiLayout *row = uiLayoutRow(prev_layout, false); + + item.build_row(*row); + + UI_block_layout_set_current(&block(), prev_layout); +} + +uiBlock &TreeViewLayoutBuilder::block() const +{ + return block_; +} + +uiLayout *TreeViewLayoutBuilder::current_layout() const +{ + return block().curlayout; +} + +/* ---------------------------------------------------------------------- */ + +BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_, ActivateFn activate_fn) + : icon(icon_), activate_fn_(activate_fn) +{ + label_ = label; +} + +static void tree_row_click_fn(struct bContext *UNUSED(C), void *but_arg1, void *UNUSED(arg2)) +{ + uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1; + AbstractTreeViewItem &tree_item = reinterpret_cast( + *tree_row_but->tree_item); + + /* Let a click on an opened item activate it, a second click will close it then. + * TODO Should this be for asset catalogs only? */ + if (tree_item.is_collapsed() || tree_item.is_active()) { + tree_item.toggle_collapsed(); + } + tree_item.set_active(); +} + +void BasicTreeViewItem::build_row(uiLayout &row) +{ + uiBlock *block = uiLayoutGetBlock(&row); + tree_row_but_ = (uiButTreeRow *)uiDefIconTextBut(block, + UI_BTYPE_TREEROW, + 0, + /* TODO allow icon besides the chevron icon? */ + get_draw_icon(), + label_.data(), + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + + tree_row_but_->tree_item = reinterpret_cast(this); + UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr); + UI_but_treerow_indentation_set(&tree_row_but_->but, count_parents()); +} + +void BasicTreeViewItem::on_activate() +{ + if (activate_fn_) { + activate_fn_(*this); + } +} + +BIFIconID BasicTreeViewItem::get_draw_icon() const +{ + if (icon) { + return icon; + } + + if (is_collapsible()) { + return is_collapsed() ? ICON_TRIA_RIGHT : ICON_TRIA_DOWN; + } + + return ICON_NONE; +} + +uiBut *BasicTreeViewItem::button() +{ + return &tree_row_but_->but; +} + +} // namespace blender::ui + +using namespace blender::ui; + +bool UI_tree_view_item_is_active(uiTreeViewItemHandle *item_) +{ + AbstractTreeViewItem &item = reinterpret_cast(*item_); + return item.is_active(); +} diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index b396e348845..b339bfbdc47 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -103,8 +103,10 @@ set(SRC ../include/ED_view3d_offscreen.h ../include/UI_icons.h ../include/UI_interface.h + ../include/UI_interface.hh ../include/UI_interface_icons.h ../include/UI_resources.h + ../include/UI_tree_view.hh ../include/UI_view2d.h ) From b8a30c7664a1871fb3dae8805c21b7f24ca413d3 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 23 Sep 2021 19:39:26 +0200 Subject: [PATCH 0161/1500] Cleanup: Use const in previously committed function --- source/blender/editors/include/UI_tree_view.hh | 2 +- source/blender/editors/interface/tree_view.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 51b8e65521a..dd1a0af2dc0 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -188,7 +188,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer { /** Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of the * last redraw to this item. If sub-classes introduce more advanced state they should override * this and make it update their state accordingly. */ - virtual void update_from_old(AbstractTreeViewItem &old); + virtual void update_from_old(const AbstractTreeViewItem &old); const AbstractTreeView &get_tree_view() const; int count_parents() const; diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index c5770e808fa..c2ecab08ace 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -139,7 +139,7 @@ void AbstractTreeViewItem::on_activate() /* Do nothing by default. */ } -void AbstractTreeViewItem::update_from_old(AbstractTreeViewItem &old) +void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) { is_open_ = old.is_open_; is_active_ = old.is_active_; From fc2255135e31679d51edf0652caca1462f75c3d4 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 22 Sep 2021 17:19:42 +0200 Subject: [PATCH 0162/1500] Paint: prevent RenderResults and Viewers where unappropriate Using a RenderResult (or a Viewer) was never really working (think you cant get a real ImBuff from these) -- cannot use it as a clone, stencil or canvas [Single Image paint texture slot]. In the case of using it as a 2D paint clone image this would also crash [due to the Image Editor drawing refactor in 2.91]. Now [in the spirit of T73182 / D11179], prevent using these where unappropriate by using rna pointer polling functions. Also add a security check for the 2D paint clone image crash in case a stencil ImBuff cannot be provided for some reason, but generally old files are now patched in do_versions_after_linking_300 (thx @brecht!). Fixes T91625. Maniphest Tasks: T91625 Differential Revision: https://developer.blender.org/D12609 --- .../blenloader/intern/versioning_300.c | 22 ++++++++ .../draw/engines/overlay/overlay_edit_uv.c | 54 +++++++++++-------- source/blender/makesrna/intern/rna_brush.c | 7 +++ .../makesrna/intern/rna_sculpt_paint.c | 9 ++++ 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 9908e231452..0d333ac2edc 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -518,6 +518,28 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) { /* Keep this block, even when empty. */ do_versions_idproperty_ui_data(bmain); + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *tool_settings = scene->toolsettings; + ImagePaintSettings *imapaint = &tool_settings->imapaint; + if (imapaint->canvas != NULL && + ELEM(imapaint->canvas->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + imapaint->canvas = NULL; + } + if (imapaint->stencil != NULL && + ELEM(imapaint->stencil->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + imapaint->stencil = NULL; + } + if (imapaint->clone != NULL && + ELEM(imapaint->clone->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + imapaint->clone = NULL; + } + } + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->clone.image != NULL && ELEM(brush->clone.image->type, IMA_TYPE_R_RESULT, + IMA_TYPE_COMPOSITE)) { brush->clone.image = NULL; + } + } } } diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.c b/source/blender/draw/engines/overlay/overlay_edit_uv.c index 985f8a6785c..983df1ceac8 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_uv.c +++ b/source/blender/draw/engines/overlay/overlay_edit_uv.c @@ -340,34 +340,42 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata) if (pd->edit_uv.do_stencil_overlay) { const Brush *brush = BKE_paint_brush(&ts->imapaint.paint); - - DRW_PASS_CREATE(psl->edit_uv_stencil_ps, - DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL); - GPUShader *sh = OVERLAY_shader_edit_uv_stencil_image(); - GPUBatch *geom = DRW_cache_quad_get(); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->edit_uv_stencil_ps); Image *stencil_image = brush->clone.image; ImBuf *stencil_ibuf = BKE_image_acquire_ibuf(stencil_image, NULL, &pd->edit_uv.stencil_lock); - pd->edit_uv.stencil_ibuf = stencil_ibuf; - pd->edit_uv.stencil_image = stencil_image; - GPUTexture *stencil_texture = BKE_image_get_gpu_texture(stencil_image, NULL, stencil_ibuf); - DRW_shgroup_uniform_texture(grp, "imgTexture", stencil_texture); - DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", true); - DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", true); - DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){1.0f, 1.0f, 1.0f, brush->clone.alpha}); - float size_image[2]; - BKE_image_get_size_fl(image, NULL, size_image); - float size_stencil_image[2] = {stencil_ibuf->x, stencil_ibuf->y}; + if (stencil_ibuf == NULL) { + pd->edit_uv.stencil_ibuf = NULL; + pd->edit_uv.stencil_image = NULL; + } + else { + DRW_PASS_CREATE(psl->edit_uv_stencil_ps, + DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | + DRW_STATE_BLEND_ALPHA_PREMUL); + GPUShader *sh = OVERLAY_shader_edit_uv_stencil_image(); + GPUBatch *geom = DRW_cache_quad_get(); + DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->edit_uv_stencil_ps); + pd->edit_uv.stencil_ibuf = stencil_ibuf; + pd->edit_uv.stencil_image = stencil_image; + GPUTexture *stencil_texture = BKE_image_get_gpu_texture(stencil_image, NULL, stencil_ibuf); + DRW_shgroup_uniform_texture(grp, "imgTexture", stencil_texture); + DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", true); + DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", true); + DRW_shgroup_uniform_vec4_copy( + grp, "color", (float[4]){1.0f, 1.0f, 1.0f, brush->clone.alpha}); - float obmat[4][4]; - unit_m4(obmat); - obmat[3][1] = brush->clone.offset[1]; - obmat[3][0] = brush->clone.offset[0]; - obmat[0][0] = size_stencil_image[0] / size_image[0]; - obmat[1][1] = size_stencil_image[1] / size_image[1]; + float size_image[2]; + BKE_image_get_size_fl(image, NULL, size_image); + float size_stencil_image[2] = {stencil_ibuf->x, stencil_ibuf->y}; - DRW_shgroup_call_obmat(grp, geom, obmat); + float obmat[4][4]; + unit_m4(obmat); + obmat[3][1] = brush->clone.offset[1]; + obmat[3][0] = brush->clone.offset[0]; + obmat[0][0] = size_stencil_image[0] / size_image[0]; + obmat[1][1] = size_stencil_image[1] / size_image[1]; + + DRW_shgroup_call_obmat(grp, geom, obmat); + } } else { pd->edit_uv.stencil_ibuf = NULL; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 25caa411979..1d3b8cd9f9c 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -734,6 +734,12 @@ static void rna_Brush_icon_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poi WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); } +static bool rna_Brush_imagetype_poll(PointerRNA *UNUSED(ptr), PointerRNA value) +{ + Image *image = (Image *)value.owner_id; + return image->type != IMA_TYPE_R_RESULT && image->type != IMA_TYPE_COMPOSITE; +} + static void rna_TextureSlot_brush_angle_update(bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); @@ -3434,6 +3440,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Clone Image", "Image for clone tool"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, "rna_Brush_update"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Brush_imagetype_poll"); prop = RNA_def_property(srna, "clone_alpha", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "clone.alpha"); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 1da08448c5b..45e85d14865 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -501,6 +501,12 @@ static void rna_ImaPaint_stencil_update(bContext *C, PointerRNA *UNUSED(ptr)) } } +static bool rna_ImaPaint_imagetype_poll(PointerRNA *UNUSED(ptr), PointerRNA value) +{ + Image *image = (Image *)value.owner_id; + return image->type != IMA_TYPE_R_RESULT && image->type != IMA_TYPE_COMPOSITE; +} + static void rna_ImaPaint_canvas_update(bContext *C, PointerRNA *UNUSED(ptr)) { Main *bmain = CTX_data_main(C); @@ -1033,17 +1039,20 @@ static void rna_def_image_paint(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE); RNA_def_property_ui_text(prop, "Stencil Image", "Image used as stencil"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_stencil_update"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_ImaPaint_imagetype_poll"); prop = RNA_def_property(srna, "canvas", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE); RNA_def_property_ui_text(prop, "Canvas", "Image used as canvas"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_canvas_update"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_ImaPaint_imagetype_poll"); prop = RNA_def_property(srna, "clone_image", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "clone"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Clone Image", "Image used as clone source"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_ImaPaint_imagetype_poll"); prop = RNA_def_property(srna, "stencil_color", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_range(prop, 0.0, 1.0); From 7fb2b50e5dace1eeaa777965c445f85b708eaae0 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Thu, 23 Sep 2021 12:33:27 -0600 Subject: [PATCH 0163/1500] Fix: Build issue with MSVC header for std::function was not included reported/fixed by Charlie on chat --- source/blender/editors/include/UI_tree_view.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index dd1a0af2dc0..fac880a0a67 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -20,6 +20,7 @@ #pragma once +#include #include #include From 1bdaf0ebec5bafd6c0b945de49cc74515b93fe1c Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 23 Sep 2021 19:22:15 +0200 Subject: [PATCH 0164/1500] Fix T91638: image editor Open Cached Render not loading some passes Previously this was only loading built-in render passes. Now instead of trying to load the scene render passes, load whatever passes exist in the cache file. --- .../COM_OutputFileMultiViewOperation.cc | 7 +- .../imbuf/intern/openexr/openexr_api.cpp | 165 ++++++++++-------- .../imbuf/intern/openexr/openexr_multi.h | 15 +- .../imbuf/intern/openexr/openexr_stub.cpp | 19 +- source/blender/render/intern/render_result.c | 31 ++-- 5 files changed, 135 insertions(+), 102 deletions(-) diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index 5b6f650d40e..d436b00a6e3 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -86,8 +86,8 @@ void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filenam /* prepare the file with all the channels */ - if (IMB_exr_begin_write( - exrhandle, filename, width, height, this->m_format->exr_codec, nullptr) == 0) { + if (!IMB_exr_begin_write( + exrhandle, filename, width, height, this->m_format->exr_codec, nullptr)) { printf("Error Writing Singlelayer Multiview Openexr\n"); IMB_exr_close(exrhandle); } @@ -200,8 +200,7 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename /* prepare the file with all the channels for the header */ StampData *stamp_data = createStampData(); - if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data) == - 0) { + if (!IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data)) { printf("Error Writing Multilayer Multiview Openexr\n"); IMB_exr_close(exrhandle); BKE_stamp_data_free(stamp_data); diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index cd323e72003..ec0a085700a 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -681,6 +681,8 @@ struct ExrLayer { ListBase passes; }; +static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data); + /* ********************** */ void *IMB_exr_get_handle(void) @@ -839,12 +841,12 @@ void IMB_exr_add_channel(void *handle, } /* used for output files (from RenderResult) (single and multilayer, single and multiview) */ -int IMB_exr_begin_write(void *handle, - const char *filename, - int width, - int height, - int compress, - const StampData *stamp) +bool IMB_exr_begin_write(void *handle, + const char *filename, + int width, + int height, + int compress, + const StampData *stamp) { ExrHandle *data = (ExrHandle *)handle; Header header(width, height); @@ -962,51 +964,64 @@ void IMB_exrtile_begin_write( } /* read from file */ -int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height) +bool IMB_exr_begin_read( + void *handle, const char *filename, int *width, int *height, const bool parse_channels) { ExrHandle *data = (ExrHandle *)handle; ExrChannel *echan; /* 32 is arbitrary, but zero length files crashes exr. */ - if (BLI_exists(filename) && BLI_file_size(filename) > 32) { - /* avoid crash/abort when we don't have permission to write here */ - try { - data->ifile_stream = new IFileStream(filename); - data->ifile = new MultiPartInputFile(*(data->ifile_stream)); - } - catch (const std::exception &) { - delete data->ifile; - delete data->ifile_stream; + if (!(BLI_exists(filename) && BLI_file_size(filename) > 32)) { + return false; + } - data->ifile = nullptr; - data->ifile_stream = nullptr; - } + /* avoid crash/abort when we don't have permission to write here */ + try { + data->ifile_stream = new IFileStream(filename); + data->ifile = new MultiPartInputFile(*(data->ifile_stream)); + } + catch (const std::exception &) { + delete data->ifile; + delete data->ifile_stream; - if (data->ifile) { - Box2i dw = data->ifile->header(0).dataWindow(); - data->width = *width = dw.max.x - dw.min.x + 1; - data->height = *height = dw.max.y - dw.min.y + 1; + data->ifile = nullptr; + data->ifile_stream = nullptr; + } - imb_exr_get_views(*data->ifile, *data->multiView); + if (!data->ifile) { + return false; + } - std::vector channels; - GetChannelsInMultiPartFile(*data->ifile, channels); + Box2i dw = data->ifile->header(0).dataWindow(); + data->width = *width = dw.max.x - dw.min.x + 1; + data->height = *height = dw.max.y - dw.min.y + 1; - for (const MultiViewChannelName &channel : channels) { - IMB_exr_add_channel( - data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false); - - echan = (ExrChannel *)data->channels.last; - echan->m->name = channel.name; - echan->m->view = channel.view; - echan->m->part_number = channel.part_number; - echan->m->internal_name = channel.internal_name; - } - - return 1; + if (parse_channels) { + /* Parse channels into view/layer/pass. */ + if (!imb_exr_multilayer_parse_channels_from_file(data)) { + return false; } } - return 0; + else { + /* Read view and channels without parsing. */ + imb_exr_get_views(*data->ifile, *data->multiView); + + std::vector channels; + GetChannelsInMultiPartFile(*data->ifile, channels); + + for (const MultiViewChannelName &channel : channels) { + IMB_exr_add_channel( + data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false); + + echan = (ExrChannel *)data->channels.last; + echan->m->name = channel.name; + echan->m->view = channel.view; + echan->m->part_number = channel.part_number; + echan->m->internal_name = channel.internal_name; + } + } + + return true; } /* still clumsy name handling, layers/channels can be ordered as list in list later */ @@ -1524,25 +1539,8 @@ static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname) return pass; } -/* creates channels, makes a hierarchy and assigns memory to channels */ -static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, - MultiPartInputFile &file, - int width, - int height) +static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data) { - ExrLayer *lay; - ExrPass *pass; - ExrChannel *echan; - ExrHandle *data = (ExrHandle *)IMB_exr_get_handle(); - int a; - char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME]; - - data->ifile_stream = &file_stream; - data->ifile = &file; - - data->width = width; - data->height = height; - std::vector channels; GetChannelsInMultiPartFile(*data->ifile, channels); @@ -1552,7 +1550,7 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, IMB_exr_add_channel( data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false); - echan = (ExrChannel *)data->channels.last; + ExrChannel *echan = (ExrChannel *)data->channels.last; echan->m->name = channel.name; echan->m->view = channel.view; echan->m->part_number = channel.part_number; @@ -1561,7 +1559,9 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, /* now try to sort out how to assign memory to the channels */ /* first build hierarchical layer list */ - for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + ExrChannel *echan = (ExrChannel *)data->channels.first; + for (; echan; echan = echan->next) { + char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME]; if (imb_exr_split_channel_name(echan, layname, passname)) { const char *view = echan->m->view.c_str(); @@ -1591,21 +1591,20 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, } if (echan) { printf("error, too many channels in one pass: %s\n", echan->m->name.c_str()); - IMB_exr_close(data); - return nullptr; + return false; } /* with some heuristics, try to merge the channels in buffers */ - for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) { - for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) { + for (ExrLayer *lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) { + for (ExrPass *pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) { if (pass->totchan) { - pass->rect = (float *)MEM_callocN(width * height * pass->totchan * sizeof(float), - "pass rect"); + pass->rect = (float *)MEM_callocN( + data->width * data->height * pass->totchan * sizeof(float), "pass rect"); if (pass->totchan == 1) { - echan = pass->chan[0]; + ExrChannel *echan = pass->chan[0]; echan->rect = pass->rect; echan->xstride = 1; - echan->ystride = width; + echan->ystride = data->width; pass->chan_id[0] = echan->chan_id; } else { @@ -1634,20 +1633,20 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, lookup[(unsigned int)'V'] = 1; lookup[(unsigned int)'A'] = 2; } - for (a = 0; a < pass->totchan; a++) { + for (int a = 0; a < pass->totchan; a++) { echan = pass->chan[a]; echan->rect = pass->rect + lookup[(unsigned int)echan->chan_id]; echan->xstride = pass->totchan; - echan->ystride = width * pass->totchan; + echan->ystride = data->width * pass->totchan; pass->chan_id[(unsigned int)lookup[(unsigned int)echan->chan_id]] = echan->chan_id; } } else { /* unknown */ - for (a = 0; a < pass->totchan; a++) { - echan = pass->chan[a]; + for (int a = 0; a < pass->totchan; a++) { + ExrChannel *echan = pass->chan[a]; echan->rect = pass->rect + a; echan->xstride = pass->totchan; - echan->ystride = width * pass->totchan; + echan->ystride = data->width * pass->totchan; pass->chan_id[a] = echan->chan_id; } } @@ -1656,6 +1655,28 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, } } + return true; +} + +/* creates channels, makes a hierarchy and assigns memory to channels */ +static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, + MultiPartInputFile &file, + int width, + int height) +{ + ExrHandle *data = (ExrHandle *)IMB_exr_get_handle(); + + data->ifile_stream = &file_stream; + data->ifile = &file; + + data->width = width; + data->height = height; + + if (!imb_exr_multilayer_parse_channels_from_file(data)) { + IMB_exr_close(data); + return nullptr; + } + return data; } diff --git a/source/blender/imbuf/intern/openexr/openexr_multi.h b/source/blender/imbuf/intern/openexr/openexr_multi.h index 556717ad618..82a5d161ded 100644 --- a/source/blender/imbuf/intern/openexr/openexr_multi.h +++ b/source/blender/imbuf/intern/openexr/openexr_multi.h @@ -50,13 +50,14 @@ void IMB_exr_add_channel(void *handle, float *rect, bool use_half_float); -int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height); -int IMB_exr_begin_write(void *handle, - const char *filename, - int width, - int height, - int compress, - const struct StampData *stamp); +bool IMB_exr_begin_read( + void *handle, const char *filename, int *width, int *height, const bool parse_channels); +bool IMB_exr_begin_write(void *handle, + const char *filename, + int width, + int height, + int compress, + const struct StampData *stamp); void IMB_exrtile_begin_write( void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley); diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index 51bc2094053..c8bc7c57e3a 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -43,21 +43,22 @@ void IMB_exr_add_channel(void * /*handle*/, { } -int IMB_exr_begin_read(void * /*handle*/, +bool IMB_exr_begin_read(void * /*handle*/, const char * /*filename*/, int * /*width*/, - int * /*height*/) + int * /*height*/, + const bool /*add_channels*/)) { return 0; } -int IMB_exr_begin_write(void * /*handle*/, - const char * /*filename*/, - int /*width*/, - int /*height*/, - int /*compress*/, - const struct StampData * /*stamp*/) +bool IMB_exr_begin_write(void * /*handle*/, + const char * /*filename*/, + int /*width*/, + int /*height*/, + int /*compress*/, + const struct StampData * /*stamp*/) { - return 0; + return false; } void IMB_exrtile_begin_write(void * /*handle*/, const char * /*filename*/, diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index 6bd6521d803..db14f7a2982 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -1083,7 +1083,7 @@ int render_result_exr_file_read_path(RenderResult *rr, void *exrhandle = IMB_exr_get_handle(); int rectx, recty; - if (IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty) == 0) { + if (!IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty, false)) { printf("failed being read %s\n", filepath); IMB_exr_close(exrhandle); return 0; @@ -1175,21 +1175,32 @@ void render_result_exr_file_cache_write(Render *re) /* For cache, makes exact copy of render result */ bool render_result_exr_file_cache_read(Render *re) { - char str[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; + /* File path to cache. */ + char filepath[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; char *root = U.render_cachedir; + render_result_exr_file_cache_path(re->scene, root, filepath); - RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); - render_result_passes_allocated_ensure(re->result); + printf("read exr cache file: %s\n", filepath); - /* First try cache. */ - render_result_exr_file_cache_path(re->scene, root, str); + /* Try opening the file. */ + void *exrhandle = IMB_exr_get_handle(); + int rectx, recty; - printf("read exr cache file: %s\n", str); - if (!render_result_exr_file_read_path(re->result, NULL, str)) { - printf("cannot read: %s\n", str); + if (!IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty, true)) { + printf("cannot read: %s\n", filepath); + IMB_exr_close(exrhandle); return false; } + + /* Read file contents into render result. */ + const char *colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + RE_FreeRenderResult(re->result); + + IMB_exr_read_channels(exrhandle); + re->result = render_result_new_from_exr(exrhandle, colorspace, false, rectx, recty); + + IMB_exr_close(exrhandle); + return true; } From c1b925f7fff5f8b26daa05b3105cbb10dfc175e7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 23 Sep 2021 15:20:57 -0500 Subject: [PATCH 0165/1500] Fix build error caused by typo --- source/blender/imbuf/intern/openexr/openexr_stub.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index c8bc7c57e3a..639100ac6fe 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -44,10 +44,10 @@ void IMB_exr_add_channel(void * /*handle*/, } bool IMB_exr_begin_read(void * /*handle*/, - const char * /*filename*/, - int * /*width*/, - int * /*height*/, - const bool /*add_channels*/)) + const char * /*filename*/, + int * /*width*/, + int * /*height*/, + const bool /*add_channels*/) { return 0; } From 61f3d4eb7c7db711f9339d05e68b8f9eac3ce167 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 23 Sep 2021 15:21:31 -0500 Subject: [PATCH 0166/1500] Geometry Nodes: Initial socket visualization for fields. This implements the update logic for the vizualization of which sockets pass data or constants directly, and which pass functions. The socket shapes may still have to be updated. That should be done separately, because it might be a bit more involved, because socket shapes are currently linked to keyframe shapes. Currently the circle and diamond shapes are used with the following meanings: - Input Sockets: - Circle: Required to be a single value. - Diamond: This input supports fields. - Output Sockets: - Circle: This output is a single value. - Diamond: This output may be a field. Connecting a field to a circle input socket is an error, since a field cannot be converted to a single value. If the socket shape is a diamond with a dot in the middle, it means it is currently a single value, but could be a field. In addition to socket shapes, the intention is to draw node links differently based on the field status. However, the exact method for conveying that isn't decided yet. Differential Revision: https://developer.blender.org/D12584 --- source/blender/blenkernel/BKE_node.h | 2 +- source/blender/blenkernel/intern/lib_remap.c | 2 +- source/blender/blenkernel/intern/node.cc | 559 +++++++++++++++++- source/blender/editors/space_node/drawnode.cc | 7 + .../editors/space_node/node_relationships.cc | 5 + source/blender/makesdna/DNA_node_types.h | 8 + source/blender/nodes/NOD_node_declaration.hh | 151 +++++ source/blender/nodes/NOD_node_tree_ref.hh | 26 + .../function/nodes/node_fn_boolean_math.cc | 1 + .../function/nodes/node_fn_float_compare.cc | 1 + .../function/nodes/node_fn_float_to_int.cc | 1 + .../function/nodes/node_fn_input_string.cc | 1 + .../function/nodes/node_fn_input_vector.cc | 1 + .../function/nodes/node_fn_random_float.cc | 1 + .../function/nodes/node_fn_string_length.cc | 1 + .../nodes/node_fn_string_substring.cc | 1 + .../function/nodes/node_fn_value_to_string.cc | 1 + .../nodes/node_geo_attribute_capture.cc | 20 +- .../nodes/node_geo_attribute_statistic.cc | 4 +- .../nodes/node_geo_curve_parameter.cc | 2 +- .../geometry/nodes/node_geo_curve_sample.cc | 10 +- .../geometry/nodes/node_geo_input_index.cc | 2 +- .../geometry/nodes/node_geo_input_normal.cc | 2 +- .../geometry/nodes/node_geo_input_position.cc | 2 +- .../geometry/nodes/node_geo_input_tangent.cc | 2 +- .../nodes/node_geo_material_assign.cc | 2 +- .../nodes/node_geo_material_selection.cc | 2 +- .../geometry/nodes/node_geo_set_position.cc | 4 +- source/blender/nodes/intern/node_tree_ref.cc | 103 ++++ .../nodes/shader/nodes/node_shader_clamp.cc | 1 + .../nodes/shader/nodes/node_shader_curves.cc | 1 + .../shader/nodes/node_shader_map_range.cc | 1 + .../nodes/shader/nodes/node_shader_math.cc | 1 + .../nodes/shader/nodes/node_shader_mixRgb.cc | 1 + .../shader/nodes/node_shader_sepcombRGB.cc | 1 + .../shader/nodes/node_shader_sepcombXYZ.cc | 1 + .../shader/nodes/node_shader_tex_musgrave.cc | 1 + .../shader/nodes/node_shader_tex_noise.cc | 3 +- .../shader/nodes/node_shader_tex_voronoi.cc | 1 + .../nodes/node_shader_tex_white_noise.cc | 1 + .../shader/nodes/node_shader_valToRgb.cc | 1 + .../shader/nodes/node_shader_vector_math.cc | 1 + .../shader/nodes/node_shader_vector_rotate.cc | 1 + 43 files changed, 908 insertions(+), 32 deletions(-) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 21ca65baf00..2e843e82a9f 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -480,7 +480,7 @@ bool ntreeHasType(const struct bNodeTree *ntree, int type); bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup); void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree); void ntreeUpdateAllNew(struct Main *main); -void ntreeUpdateAllUsers(struct Main *main, struct ID *id); +void ntreeUpdateAllUsers(struct Main *main, struct ID *id, int tree_update_flag); void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***r_deplist, diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 250b8d4d515..48396c5e6d9 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -345,7 +345,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id) { /* Update all group nodes using a node group. */ - ntreeUpdateAllUsers(bmain, new_id); + ntreeUpdateAllUsers(bmain, new_id, 0); } /** diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index d56a7bf8fb4..f223ed28301 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -52,9 +52,12 @@ #include "BLI_map.hh" #include "BLI_math.h" #include "BLI_path_util.h" +#include "BLI_set.hh" +#include "BLI_stack.hh" #include "BLI_string.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "BLI_vector_set.hh" #include "BLT_translation.h" @@ -80,6 +83,7 @@ #include "NOD_function.h" #include "NOD_geometry.h" #include "NOD_node_declaration.hh" +#include "NOD_node_tree_ref.hh" #include "NOD_shader.h" #include "NOD_socket.h" #include "NOD_texture.h" @@ -93,6 +97,20 @@ #define NODE_DEFAULT_MAX_WIDTH 700 +using blender::Array; +using blender::MutableSpan; +using blender::Set; +using blender::Span; +using blender::Stack; +using blender::Vector; +using blender::VectorSet; +using blender::nodes::InputSocketFieldType; +using blender::nodes::NodeDeclaration; +using blender::nodes::OutputFieldDependency; +using blender::nodes::OutputSocketFieldType; +using blender::nodes::SocketDeclaration; +using namespace blender::nodes::node_tree_ref_types; + /* Fallback types for undefined tree, nodes, sockets */ static bNodeTreeType NodeTreeTypeUndefined; bNodeType NodeTypeUndefined; @@ -110,6 +128,10 @@ static void node_socket_interface_free(bNodeTree *UNUSED(ntree), static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree, struct bNode *node, const bool mute); +static FieldInferencingInterface *node_field_inferencing_interface_copy( + const FieldInferencingInterface &field_inferencing_interface); +static void node_field_inferencing_interface_free( + const FieldInferencingInterface *field_inferencing_interface); static void ntree_init_data(ID *id) { @@ -220,6 +242,11 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c /* node tree will generate its own interface type */ ntree_dst->interface_type = nullptr; + + if (ntree_src->field_inferencing_interface) { + ntree_dst->field_inferencing_interface = node_field_inferencing_interface_copy( + *ntree_src->field_inferencing_interface); + } } static void ntree_free_data(ID *id) @@ -265,6 +292,8 @@ static void ntree_free_data(ID *id) MEM_freeN(sock); } + node_field_inferencing_interface_free(ntree->field_inferencing_interface); + /* free preview hash */ if (ntree->previews) { BKE_node_instance_hash_free(ntree->previews, (bNodeInstanceValueFP)BKE_node_preview_free); @@ -647,6 +676,8 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) ntree->progress = nullptr; ntree->execdata = nullptr; + ntree->field_inferencing_interface = nullptr; + BLO_read_data_address(reader, &ntree->adt); BKE_animdata_blend_read_data(reader, ntree->adt); @@ -4425,7 +4456,518 @@ void ntreeUpdateAllNew(Main *main) FOREACH_NODETREE_END; } -void ntreeUpdateAllUsers(Main *main, ID *id) +/** + * Information about how a node interacts with fields. + */ +struct FieldInferencingInterface { + Vector inputs; + Vector outputs; + + friend bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) + { + return a.inputs == b.inputs && a.outputs == b.outputs; + } + + friend bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b) + { + return !(a == b); + } +}; + +static FieldInferencingInterface *node_field_inferencing_interface_copy( + const FieldInferencingInterface &field_inferencing_interface) +{ + return new FieldInferencingInterface(field_inferencing_interface); +} + +static void node_field_inferencing_interface_free( + const FieldInferencingInterface *field_inferencing_interface) +{ + delete field_inferencing_interface; +} + +namespace blender::bke::node_field_inferencing { + +static bool is_field_socket_type(eNodeSocketDatatype type) +{ + return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA); +} + +static bool is_field_socket_type(const SocketRef &socket) +{ + return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo()->type); +} + +static bool update_field_inferencing(bNodeTree &btree); + +static InputSocketFieldType get_interface_input_field_type(const NodeRef &node, + const InputSocketRef &socket) +{ + if (!is_field_socket_type(socket)) { + return InputSocketFieldType::None; + } + if (node.is_reroute_node()) { + return InputSocketFieldType::IsSupported; + } + if (node.is_group_output_node()) { + /* Outputs always support fields when the data type is correct. */ + return InputSocketFieldType::IsSupported; + } + + const NodeDeclaration *node_decl = node.declaration(); + + /* Node declarations should be implemented for nodes involved here. */ + BLI_assert(node_decl != nullptr); + + if (node_decl->is_function_node()) { + /* In a function node, every socket supports fields. */ + return InputSocketFieldType::IsSupported; + } + /* Get the field type from the declaration. */ + const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()]; + return socket_decl.input_field_type(); +} + +static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node, + const OutputSocketRef &socket) +{ + if (!is_field_socket_type(socket)) { + /* Non-field sockets always output data. */ + return OutputFieldDependency::ForDataSource(); + } + if (node.is_reroute_node()) { + /* The reroute just forwards what is passed in. */ + return OutputFieldDependency::ForDependentField(); + } + if (node.is_group_input_node()) { + /* Input nodes get special treatment in #determine_group_input_states. */ + return OutputFieldDependency::ForDependentField(); + } + + const NodeDeclaration *node_decl = node.declaration(); + + /* Node declarations should be implemented for nodes involved here. */ + BLI_assert(node_decl != nullptr); + + if (node_decl->is_function_node()) { + /* In a generic function node, all outputs depend on all inputs. */ + return OutputFieldDependency::ForDependentField(); + } + + /* Use the socket declaration. */ + const SocketDeclaration &socket_decl = *node_decl->outputs()[socket.index()]; + return socket_decl.output_field_dependency(); +} + +/** + * Retrieves information about how the node interacts with fields. + * In the future, this information can be stored in the node declaration. This would allow this + * function to return a reference, making it more efficient. + */ +static FieldInferencingInterface get_node_field_inferencing_interface(const NodeRef &node) +{ + /* Node groups already reference all required information, so just return that. */ + if (node.is_group_node()) { + bNodeTree *group = (bNodeTree *)node.bnode()->id; + if (group == nullptr) { + return FieldInferencingInterface(); + } + if (group->field_inferencing_interface == nullptr) { + /* Update group recursively. */ + update_field_inferencing(*group); + } + return *group->field_inferencing_interface; + } + + FieldInferencingInterface inferencing_interface; + for (const InputSocketRef *input_socket : node.inputs()) { + inferencing_interface.inputs.append(get_interface_input_field_type(node, *input_socket)); + } + + for (const OutputSocketRef *output_socket : node.outputs()) { + inferencing_interface.outputs.append( + get_interface_output_field_dependency(node, *output_socket)); + } + return inferencing_interface; +} + +/** + * This struct contains information for every socket. The values are propagated through the + * network. + */ +struct SocketFieldState { + /* This socket is currently a single value. It could become a field though. */ + bool is_single = true; + /* This socket is required to be a single value. It must not be a field. */ + bool requires_single = false; + /* This socket starts a new field. */ + bool is_field_source = false; +}; + +static Vector gather_input_socket_dependencies( + const OutputFieldDependency &field_dependency, const NodeRef &node) +{ + const OutputSocketFieldType type = field_dependency.field_type(); + Vector input_sockets; + switch (type) { + case OutputSocketFieldType::FieldSource: + case OutputSocketFieldType::None: { + break; + } + case OutputSocketFieldType::DependentField: { + /* This output depends on all inputs. */ + input_sockets.extend(node.inputs()); + break; + } + case OutputSocketFieldType::PartiallyDependent: { + /* This output depends only on a few inputs. */ + for (const int i : field_dependency.linked_input_indices()) { + input_sockets.append(&node.input(i)); + } + break; + } + } + return input_sockets; +} + +/** + * Check what the group output socket depends on. Potentially traverses the node tree + * to figure out if it is always a field or if it depends on any group inputs. + */ +static OutputFieldDependency find_group_output_dependencies( + const InputSocketRef &group_output_socket, + const Span field_state_by_socket_id) +{ + if (!is_field_socket_type(group_output_socket)) { + return OutputFieldDependency::ForDataSource(); + } + + /* Use a Set here instead of an array indexed by socket id, because we my only need to look at + * very few sockets. */ + Set handled_sockets; + Stack sockets_to_check; + + handled_sockets.add(&group_output_socket); + sockets_to_check.push(&group_output_socket); + + /* Keeps track of group input indices that are (indirectly) connected to the output. */ + Vector linked_input_indices; + + while (!sockets_to_check.is_empty()) { + const InputSocketRef *input_socket = sockets_to_check.pop(); + + for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) { + const NodeRef &origin_node = origin_socket->node(); + const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; + + if (origin_state.is_field_source) { + if (origin_node.is_group_input_node()) { + /* Found a group input that the group output depends on. */ + linked_input_indices.append_non_duplicates(origin_socket->index()); + } + else { + /* Found a field source that is not the group input. So the output is always a field. */ + return OutputFieldDependency::ForFieldSource(); + } + } + else if (!origin_state.is_single) { + const FieldInferencingInterface inferencing_interface = + get_node_field_inferencing_interface(origin_node); + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[origin_socket->index()]; + + /* Propagate search further to the left. */ + for (const InputSocketRef *origin_input_socket : + gather_input_socket_dependencies(field_dependency, origin_node)) { + if (!field_state_by_socket_id[origin_input_socket->id()].is_single) { + if (handled_sockets.add(origin_input_socket)) { + sockets_to_check.push(origin_input_socket); + } + } + } + } + } + } + return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices)); +} + +static void propagate_data_requirements_from_right_to_left( + const NodeTreeRef &tree, const MutableSpan field_state_by_socket_id) +{ + const Vector sorted_nodes = tree.toposort( + NodeTreeRef::ToposortDirection::RightToLeft); + + for (const NodeRef *node : sorted_nodes) { + const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( + *node); + + for (const OutputSocketRef *output_socket : node->outputs()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[output_socket->index()]; + + if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) { + continue; + } + if (field_dependency.field_type() == OutputSocketFieldType::None) { + state.requires_single = true; + continue; + } + + /* The output is required to be a single value when it is connected to any input that does + * not support fields. */ + for (const InputSocketRef *target_socket : output_socket->directly_linked_sockets()) { + state.requires_single |= field_state_by_socket_id[target_socket->id()].requires_single; + } + + if (state.requires_single) { + bool any_input_is_field_implicitly = false; + const Vector connected_inputs = gather_input_socket_dependencies( + field_dependency, *node); + for (const InputSocketRef *input_socket : connected_inputs) { + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { + if (!input_socket->is_logically_linked()) { + any_input_is_field_implicitly = true; + break; + } + } + } + if (any_input_is_field_implicitly) { + /* This output isn't a single value actually. */ + state.requires_single = false; + } + else { + /* If the output is required to be a single value, the connected inputs in the same node + * must not be fields as well. */ + for (const InputSocketRef *input_socket : connected_inputs) { + field_state_by_socket_id[input_socket->id()].requires_single = true; + } + } + } + } + + /* Some inputs do not require fields independent of what the outputs are connected to. */ + for (const InputSocketRef *input_socket : node->inputs()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; + if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) { + state.requires_single = true; + } + } + } +} + +static void determine_group_input_states( + const NodeTreeRef &tree, + FieldInferencingInterface &new_inferencing_interface, + const MutableSpan field_state_by_socket_id) +{ + { + /* Non-field inputs never support fields. */ + int index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.btree()->inputs, index) { + if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) { + new_inferencing_interface.inputs[index] = InputSocketFieldType::None; + } + } + } + /* Check if group inputs are required to be single values, because they are (indirectly) + * connected to some socket that does not support fields. */ + for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { + for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + if (state.requires_single) { + new_inferencing_interface.inputs[output_socket->index()] = InputSocketFieldType::None; + } + } + } + /* If an input does not support fields, this should be reflected in all Group Input nodes. */ + for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { + for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + const bool supports_field = new_inferencing_interface.inputs[output_socket->index()] != + InputSocketFieldType::None; + if (supports_field) { + state.is_single = false; + state.is_field_source = true; + } + else { + state.requires_single = true; + } + } + SocketFieldState &dummy_socket_state = field_state_by_socket_id[node->outputs().last()->id()]; + dummy_socket_state.requires_single = true; + } +} + +static void propagate_field_status_from_left_to_right( + const NodeTreeRef &tree, const MutableSpan field_state_by_socket_id) +{ + Vector sorted_nodes = tree.toposort( + NodeTreeRef::ToposortDirection::LeftToRight); + + for (const NodeRef *node : sorted_nodes) { + if (node->is_group_input_node()) { + continue; + } + + const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( + *node); + + /* Update field state of input sockets, also taking into account linked origin sockets. */ + for (const InputSocketRef *input_socket : node->inputs()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; + if (state.requires_single) { + state.is_single = true; + continue; + } + state.is_single = true; + if (input_socket->logically_linked_sockets().is_empty()) { + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { + state.is_single = false; + } + } + else { + for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) { + if (!field_state_by_socket_id[origin_socket->id()].is_single) { + state.is_single = false; + break; + } + } + } + } + + /* Update field state of output sockets, also taking into account input sockets. */ + for (const OutputSocketRef *output_socket : node->outputs()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[output_socket->index()]; + + switch (field_dependency.field_type()) { + case OutputSocketFieldType::None: { + state.is_single = true; + break; + } + case OutputSocketFieldType::FieldSource: { + state.is_single = false; + state.is_field_source = true; + break; + } + case OutputSocketFieldType::PartiallyDependent: + case OutputSocketFieldType::DependentField: { + for (const InputSocketRef *input_socket : + gather_input_socket_dependencies(field_dependency, *node)) { + if (!field_state_by_socket_id[input_socket->id()].is_single) { + state.is_single = false; + break; + } + } + break; + } + } + } + } +} + +static void determine_group_output_states(const NodeTreeRef &tree, + FieldInferencingInterface &new_inferencing_interface, + const Span field_state_by_socket_id) +{ + for (const NodeRef *group_output_node : tree.nodes_by_type("NodeGroupOutput")) { + /* Ignore inactive group output nodes. */ + if (!(group_output_node->bnode()->flag & NODE_DO_OUTPUT)) { + continue; + } + /* Determine dependencies of all group outputs. */ + for (const InputSocketRef *group_output_socket : group_output_node->inputs().drop_back(1)) { + OutputFieldDependency field_dependency = find_group_output_dependencies( + *group_output_socket, field_state_by_socket_id); + new_inferencing_interface.outputs[group_output_socket->index()] = std::move( + field_dependency); + } + break; + } +} + +static void update_socket_shapes(const NodeTreeRef &tree, + const Span field_state_by_socket_id) +{ + const eNodeSocketDisplayShape requires_data_shape = SOCK_DISPLAY_SHAPE_CIRCLE; + const eNodeSocketDisplayShape data_but_can_be_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT; + const eNodeSocketDisplayShape is_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND; + + for (const InputSocketRef *socket : tree.input_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + const SocketFieldState &state = field_state_by_socket_id[socket->id()]; + if (state.requires_single) { + bsocket->display_shape = requires_data_shape; + } + else if (state.is_single) { + bsocket->display_shape = data_but_can_be_field_shape; + } + else { + bsocket->display_shape = is_field_shape; + } + } + for (const OutputSocketRef *socket : tree.output_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + const SocketFieldState &state = field_state_by_socket_id[socket->id()]; + if (state.requires_single) { + bsocket->display_shape = requires_data_shape; + } + else if (state.is_single) { + bsocket->display_shape = data_but_can_be_field_shape; + } + else { + bsocket->display_shape = is_field_shape; + } + } +} + +static bool update_field_inferencing(bNodeTree &btree) +{ + using namespace blender::nodes; + if (btree.type != NTREE_GEOMETRY) { + return false; + } + + /* Create new inferencing interface for this node group. */ + FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface(); + new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs), + InputSocketFieldType::IsSupported); + new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs), + OutputFieldDependency::ForDataSource()); + + /* Create #NodeTreeRef to accelerate various queries on the node tree (e.g. linked sockets). */ + const NodeTreeRef tree{&btree}; + + /* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */ + Array field_state_by_socket_id(tree.sockets().size()); + + propagate_data_requirements_from_right_to_left(tree, field_state_by_socket_id); + determine_group_input_states(tree, *new_inferencing_interface, field_state_by_socket_id); + propagate_field_status_from_left_to_right(tree, field_state_by_socket_id); + determine_group_output_states(tree, *new_inferencing_interface, field_state_by_socket_id); + update_socket_shapes(tree, field_state_by_socket_id); + + /* Update the previous group interface. */ + const bool group_interface_changed = btree.field_inferencing_interface == nullptr || + *btree.field_inferencing_interface != + *new_inferencing_interface; + delete btree.field_inferencing_interface; + btree.field_inferencing_interface = new_inferencing_interface; + + return group_interface_changed; +} + +} // namespace blender::bke::node_field_inferencing + +/** + * \param tree_update_flag: #eNodeTreeUpdate enum. + */ +void ntreeUpdateAllUsers(Main *main, ID *id, const int tree_update_flag) { if (id == nullptr) { return; @@ -4446,7 +4988,8 @@ void ntreeUpdateAllUsers(Main *main, ID *id) } if (need_update) { - ntreeUpdateTree(nullptr, ntree); + ntree->update |= tree_update_flag; + ntreeUpdateTree(tree_update_flag ? main : nullptr, ntree); } } FOREACH_NODETREE_END; @@ -4508,8 +5051,18 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) ntreeInterfaceTypeUpdate(ntree); } + int tree_user_update_flag = 0; + + if (ntree->update & NTREE_UPDATE) { + /* If the field interface of this node tree has changed, all node trees using + * this group will need to recalculate their interface as well. */ + if (blender::bke::node_field_inferencing::update_field_inferencing(*ntree)) { + tree_user_update_flag |= NTREE_UPDATE_FIELD_INFERENCING; + } + } + if (bmain) { - ntreeUpdateAllUsers(bmain, &ntree->id); + ntreeUpdateAllUsers(bmain, &ntree->id, tree_user_update_flag); } if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) { diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 62f40152416..b5746dc772a 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -4282,6 +4282,13 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) // th_col3 = -1; /* no shadow */ } } + /* Links from field to non-field sockets are not allowed. */ + if (snode->edittree->type == NTREE_GEOMETRY && !(link->flag & NODE_LINK_DRAGGED)) { + if ((link->fromsock && link->fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) && + (link->tosock && link->tosock->display_shape == SOCK_DISPLAY_SHAPE_CIRCLE)) { + th_col1 = th_col2 = th_col3 = TH_REDALERT; + } + } node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3); } diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 7d95659e403..b69e7e98bca 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -220,6 +220,7 @@ static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bN if (node_connected_to_output(bmain, snode->edittree, node)) { oplink->flag |= NODE_LINK_TEST; } + oplink->flag |= NODE_LINK_DRAGGED; return linkdata; } @@ -894,6 +895,8 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) */ do_tag_update |= (link->flag & NODE_LINK_TEST) != 0; + link->flag &= ~NODE_LINK_DRAGGED; + if (apply_links && link->tosock && link->fromsock) { /* before actually adding the link, * let nodes perform special link insertion handling @@ -1097,6 +1100,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor *oplink = *link; oplink->next = oplink->prev = nullptr; oplink->flag |= NODE_LINK_VALID; + oplink->flag |= NODE_LINK_DRAGGED; /* The link could be disconnected and in that case we * wouldn't be able to check whether tag update is @@ -1150,6 +1154,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor *oplink = *link_to_pick; oplink->next = oplink->prev = nullptr; oplink->flag |= NODE_LINK_VALID; + oplink->flag |= NODE_LINK_DRAGGED; oplink->flag &= ~NODE_LINK_TEST; if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) { oplink->flag |= NODE_LINK_TEST; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 18545666796..a52816ecf57 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -445,6 +445,7 @@ typedef struct bNodeLink { #define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */ #define NODE_LINK_TEMP_HIGHLIGHT (1 << 3) /* Link is highlighted for picking. */ #define NODE_LINK_MUTED (1 << 4) /* Link is muted. */ +#define NODE_LINK_DRAGGED (1 << 5) /* Node link is being dragged by the user. */ /* tree->edit_quality/tree->render_quality */ #define NTREE_QUALITY_HIGH 0 @@ -459,6 +460,8 @@ typedef struct bNodeLink { #define NTREE_CHUNKSIZE_512 512 #define NTREE_CHUNKSIZE_1024 1024 +struct FieldInferencingInterface; + /* the basis for a Node tree, all links and nodes reside internal here */ /* only re-usable node trees are in the library though, * materials and textures allocate own tree struct */ @@ -481,6 +484,8 @@ typedef struct bNodeTree { float view_center[2]; ListBase nodes, links; + /** Information about how inputs and outputs of the node group interact with fields. */ + struct FieldInferencingInterface *field_inferencing_interface; /** Set init on fileread. */ int type, init; @@ -575,6 +580,9 @@ typedef enum eNodeTreeUpdate { NTREE_UPDATE_NODES = (1 << 1), /* nodes or sockets have been added or removed */ NTREE_UPDATE_GROUP_IN = (1 << 4), /* group inputs have changed */ NTREE_UPDATE_GROUP_OUT = (1 << 5), /* group outputs have changed */ + /* The field interface has changed. So e.g. an output that was always a field before is not + * anymore. This implies that the field type inferencing has to be done again. */ + NTREE_UPDATE_FIELD_INFERENCING = (1 << 6), /* group has changed (generic flag including all other group flags) */ NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT), } eNodeTreeUpdate; diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 8e9e72bf4c8..32e63ffb2df 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -27,6 +27,91 @@ namespace blender::nodes { class NodeDeclarationBuilder; +enum class InputSocketFieldType { + /** The input is required to be a single value. */ + None, + /** The input can be a field. */ + IsSupported, + /** The input can be a field and is a field implicitly if nothing is connected. */ + Implicit, +}; + +enum class OutputSocketFieldType { + /** The output is always a single value. */ + None, + /** The output is always a field, independent of the inputs. */ + FieldSource, + /** If any input is a field, this output will be a field as well. */ + DependentField, + /** If any of a subset of inputs is a field, this out will be a field as well. + * The subset is defined by the vector of indices. */ + PartiallyDependent, +}; + +/** + * Contains information about how a node output's field state depends on inputs of the same node. + */ +class OutputFieldDependency { + private: + OutputSocketFieldType type_ = OutputSocketFieldType::None; + Vector linked_input_indices_; + + public: + static OutputFieldDependency ForFieldSource() + { + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::FieldSource; + return field_dependency; + } + + static OutputFieldDependency ForDataSource() + { + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::None; + return field_dependency; + } + + static OutputFieldDependency ForPartiallyDependentField(Vector indices) + { + OutputFieldDependency field_dependency; + if (indices.is_empty()) { + field_dependency.type_ = OutputSocketFieldType::None; + } + else { + field_dependency.type_ = OutputSocketFieldType::PartiallyDependent; + field_dependency.linked_input_indices_ = std::move(indices); + } + return field_dependency; + } + + static OutputFieldDependency ForDependentField() + { + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::DependentField; + return field_dependency; + } + + OutputSocketFieldType field_type() const + { + return type_; + } + + Span linked_input_indices() const + { + return linked_input_indices_; + } + + friend bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b) + { + return a.type_ == b.type_ && a.linked_input_indices_ == b.linked_input_indices_; + } + + friend bool operator!=(const OutputFieldDependency &a, const OutputFieldDependency &b) + { + return !(a == b); + } +}; + /** * Describes a single input or output socket. This is subclassed for different socket types. */ @@ -39,6 +124,9 @@ class SocketDeclaration { bool is_multi_input_ = false; bool no_mute_links_ = false; + InputSocketFieldType input_field_type_ = InputSocketFieldType::None; + OutputFieldDependency output_field_dependency_; + friend NodeDeclarationBuilder; template friend class SocketDeclarationBuilder; @@ -52,6 +140,9 @@ class SocketDeclaration { StringRefNull name() const; StringRefNull identifier() const; + InputSocketFieldType input_field_type() const; + const OutputFieldDependency &output_field_dependency() const; + protected: void set_common_flags(bNodeSocket &socket) const; bool matches_common_data(const bNodeSocket &socket) const; @@ -100,6 +191,41 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { decl_->no_mute_links_ = value; return *(Self *)this; } + + /** The input socket allows passing in a field. */ + Self &supports_field() + { + decl_->input_field_type_ = InputSocketFieldType::IsSupported; + return *(Self *)this; + } + + /** The input supports a field and is a field by default when nothing is connected. */ + Self &implicit_field() + { + decl_->input_field_type_ = InputSocketFieldType::Implicit; + return *(Self *)this; + } + + /** The output is always a field, regardless of any inputs. */ + Self &field_source() + { + decl_->output_field_dependency_ = OutputFieldDependency::ForFieldSource(); + return *(Self *)this; + } + + /** The output is a field if any of the inputs is a field. */ + Self &dependent_field() + { + decl_->output_field_dependency_ = OutputFieldDependency::ForDependentField(); + return *(Self *)this; + } + + /** The output is a field if any of the inputs with indices in the given list is a field. */ + Self &dependent_field(Vector input_dependencies) + { + decl_->output_field_dependency_ = OutputFieldDependency::ForPartiallyDependentField( + std::move(input_dependencies)); + } }; using SocketDeclarationPtr = std::unique_ptr; @@ -108,6 +234,7 @@ class NodeDeclaration { private: Vector inputs_; Vector outputs_; + bool is_function_node_ = false; friend NodeDeclarationBuilder; @@ -118,6 +245,11 @@ class NodeDeclaration { Span inputs() const; Span outputs() const; + bool is_function_node() const + { + return is_function_node_; + } + MEM_CXX_CLASS_ALLOC_FUNCS("NodeDeclaration") }; @@ -129,6 +261,15 @@ class NodeDeclarationBuilder { public: NodeDeclarationBuilder(NodeDeclaration &declaration); + /** + * All inputs support fields, and all outputs are fields if any of the inputs is a field. + * Calling field status definitions on each socket is unnecessary. + */ + void is_function_node(bool value = true) + { + declaration_.is_function_node_ = value; + } + template typename DeclType::Builder &add_input(StringRef name, StringRef identifier = ""); template @@ -155,6 +296,16 @@ inline StringRefNull SocketDeclaration::identifier() const return identifier_; } +inline InputSocketFieldType SocketDeclaration::input_field_type() const +{ + return input_field_type_; +} + +inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() const +{ + return output_field_dependency_; +} + /* -------------------------------------------------------------------- * NodeDeclarationBuilder inline methods. */ diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index 4f2565cbbaf..1da42fb6425 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -182,6 +182,7 @@ class NodeRef : NonCopyable, NonMovable { Span inputs() const; Span outputs() const; Span internal_links() const; + Span sockets(eNodeSocketInOut in_out) const; const InputSocketRef &input(int index) const; const OutputSocketRef &output(int index) const; @@ -189,6 +190,10 @@ class NodeRef : NonCopyable, NonMovable { const InputSocketRef &input_by_identifier(StringRef identifier) const; const OutputSocketRef &output_by_identifier(StringRef identifier) const; + bool any_input_is_directly_linked() const; + bool any_output_is_directly_linked() const; + bool any_socket_is_directly_linked(eNodeSocketInOut in_out) const; + bNode *bnode() const; bNodeTree *btree() const; @@ -196,6 +201,7 @@ class NodeRef : NonCopyable, NonMovable { StringRefNull idname() const; StringRefNull name() const; bNodeType *typeinfo() const; + const NodeDeclaration *declaration() const; int id() const; @@ -272,6 +278,13 @@ class NodeTreeRef : NonCopyable, NonMovable { bool has_link_cycles() const; bool has_undefined_nodes_or_sockets() const; + enum class ToposortDirection { + LeftToRight, + RightToLeft, + }; + + Vector toposort(ToposortDirection direction) const; + bNodeTree *btree() const; StringRefNull name() const; @@ -496,6 +509,12 @@ inline Span NodeRef::outputs() const return outputs_; } +inline Span NodeRef::sockets(const eNodeSocketInOut in_out) const +{ + return in_out == SOCK_IN ? inputs_.as_span().cast() : + outputs_.as_span().cast(); +} + inline Span NodeRef::internal_links() const { return internal_links_; @@ -553,6 +572,13 @@ inline bNodeType *NodeRef::typeinfo() const return bnode_->typeinfo; } +/* Returns a pointer because not all nodes have declarations currently. */ +inline const NodeDeclaration *NodeRef::declaration() const +{ + nodeDeclarationEnsure(this->tree().btree(), bnode_); + return bnode_->declaration; +} + inline int NodeRef::id() const { return id_; diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index b71ee092de6..d10490bb2ee 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -28,6 +28,7 @@ namespace blender::nodes { static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Boolean", "Boolean"); b.add_input("Boolean", "Boolean_001"); b.add_output("Boolean"); diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 4f4830afabc..9736c52e895 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -30,6 +30,7 @@ namespace blender::nodes { static void fn_node_float_compare_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("A").min(-10000.0f).max(10000.0f); b.add_input("B").min(-10000.0f).max(10000.0f); b.add_input("Epsilon").default_value(0.001f).min(-10000.0f).max(10000.0f); diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index e59c78d2c04..8bb5dafff8a 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -29,6 +29,7 @@ namespace blender::nodes { static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Float"); b.add_output("Integer"); }; diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index 4a8e898fb9b..704ae9d900c 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -23,6 +23,7 @@ namespace blender::nodes { static void fn_node_input_string_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_output("String"); }; diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 9548df7b423..387689b3d1e 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -25,6 +25,7 @@ namespace blender::nodes { static void fn_node_input_vector_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_output("Vector"); }; diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc index 1bd39aacdca..20f5fa87685 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -22,6 +22,7 @@ namespace blender::nodes { static void fn_node_random_float_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Min").min(-10000.0f).max(10000.0f); b.add_input("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); b.add_input("Seed").min(-10000).max(10000); diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc index a0f85dfd2bf..89038629c3c 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_length.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc @@ -24,6 +24,7 @@ namespace blender::nodes { static void fn_node_string_length_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("String"); b.add_output("Length"); }; diff --git a/source/blender/nodes/function/nodes/node_fn_string_substring.cc b/source/blender/nodes/function/nodes/node_fn_string_substring.cc index 55a01093ae9..b91171923d6 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_substring.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_substring.cc @@ -22,6 +22,7 @@ namespace blender::nodes { static void fn_node_string_substring_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("String"); b.add_input("Position"); b.add_input("Length").min(0); diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc index c1e6373cb6d..56206af2eb2 100644 --- a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc @@ -21,6 +21,7 @@ namespace blender::nodes { static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Value"); b.add_input("Decimals").min(0); b.add_output("String"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index c8a33205de4..81629a0f8b4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -26,18 +26,18 @@ namespace blender::nodes { static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Value"); - b.add_input("Value", "Value_001"); - b.add_input("Value", "Value_002"); - b.add_input("Value", "Value_003"); - b.add_input("Value", "Value_004"); + b.add_input("Value").supports_field(); + b.add_input("Value", "Value_001").supports_field(); + b.add_input("Value", "Value_002").supports_field(); + b.add_input("Value", "Value_003").supports_field(); + b.add_input("Value", "Value_004").supports_field(); b.add_output("Geometry"); - b.add_output("Attribute"); - b.add_output("Attribute", "Attribute_001"); - b.add_output("Attribute", "Attribute_002"); - b.add_output("Attribute", "Attribute_003"); - b.add_output("Attribute", "Attribute_004"); + b.add_output("Attribute").field_source(); + b.add_output("Attribute", "Attribute_001").field_source(); + b.add_output("Attribute", "Attribute_002").field_source(); + b.add_output("Attribute", "Attribute_003").field_source(); + b.add_output("Attribute", "Attribute_004").field_source(); } static void geo_node_attribute_capture_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 5001034518c..1b7d2fe28a1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -29,8 +29,8 @@ namespace blender::nodes { static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Attribute").hide_value(); - b.add_input("Attribute", "Attribute_001").hide_value(); + b.add_input("Attribute").hide_value().supports_field(); + b.add_input("Attribute", "Attribute_001").hide_value().supports_field(); b.add_output("Mean"); b.add_output("Median"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc index 2cde198e679..90853387ec7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b) { - b.add_output("Factor"); + b.add_output("Factor").field_source(); } /** diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index ac0cd510ffa..1266f525861 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -28,12 +28,12 @@ namespace blender::nodes { static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b) { b.add_input("Curve"); - b.add_input("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Length").min(0.0f).subtype(PROP_DISTANCE); + b.add_input("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); + b.add_input("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field(); - b.add_output("Position"); - b.add_output("Tangent"); - b.add_output("Normal"); + b.add_output("Position").dependent_field(); + b.add_output("Tangent").dependent_field(); + b.add_output("Normal").dependent_field(); } static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc index c52ff3d448e..f18b0ab090a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_index_declare(NodeDeclarationBuilder &b) { - b.add_output("Index"); + b.add_output("Index").field_source(); } class IndexFieldInput final : public fn::FieldInput { diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc index f92086acdf0..5a2495afb9e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -28,7 +28,7 @@ namespace blender::nodes { static void geo_node_input_normal_declare(NodeDeclarationBuilder &b) { - b.add_output("Normal"); + b.add_output("Normal").field_source(); } static GVArrayPtr mesh_face_normals(const Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc index 3f3457a3acb..f439fb32d31 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_position_declare(NodeDeclarationBuilder &b) { - b.add_output("Position"); + b.add_output("Position").field_source(); } static void geo_node_input_position_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index 68788709f1e..d690642373a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b) { - b.add_output("Tangent"); + b.add_output("Tangent").field_source(); } static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan tangents) diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc index 43818947272..2a4481d5404 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -30,7 +30,7 @@ static void geo_node_material_assign_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_input("Material").hide_label(); - b.add_input("Selection").default_value(true).hide_value(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index 22c24e34314..337bd88c6e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -31,7 +31,7 @@ namespace blender::nodes { static void geo_node_material_selection_declare(NodeDeclarationBuilder &b) { b.add_input("Material").hide_label(true); - b.add_output("Selection"); + b.add_output("Selection").field_source(); } static void select_mesh_by_material(const Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index c5e10b788ac..78bdac1b01b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -23,8 +23,8 @@ namespace blender::nodes { static void geo_node_set_position_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Position").hide_value(); - b.add_input("Selection").default_value(true).hide_value(); + b.add_input("Position").hide_value().implicit_field(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 641d02af902..d299f062fab 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -19,6 +19,7 @@ #include "NOD_node_tree_ref.hh" #include "BLI_dot_export.hh" +#include "BLI_stack.hh" namespace blender::nodes { @@ -473,6 +474,108 @@ bool NodeTreeRef::has_undefined_nodes_or_sockets() const return false; } +bool NodeRef::any_input_is_directly_linked() const +{ + for (const SocketRef *socket : inputs_) { + if (!socket->directly_linked_sockets().is_empty()) { + return true; + } + } + return false; +} + +bool NodeRef::any_output_is_directly_linked() const +{ + for (const SocketRef *socket : outputs_) { + if (!socket->directly_linked_sockets().is_empty()) { + return true; + } + } + return false; +} + +bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const +{ + if (in_out == SOCK_IN) { + return this->any_input_is_directly_linked(); + } + return this->any_output_is_directly_linked(); +} + +/** + * Sort nodes topologically from left to right or right to left. + * In the future the result if this could be cached on #NodeTreeRef. + */ +Vector NodeTreeRef::toposort(const ToposortDirection direction) const +{ + struct Item { + const NodeRef *node; + /* Index of the next socket that is checked in the depth-first search. */ + int socket_index = 0; + /* Link index in the next socket that is checked in the depth-first search. */ + int link_index = 0; + }; + + Vector toposort; + toposort.reserve(nodes_by_id_.size()); + Array node_is_done_by_id(nodes_by_id_.size(), false); + Stack nodes_to_check; + + for (const NodeRef *start_node : nodes_by_id_) { + if (node_is_done_by_id[start_node->id()]) { + /* Ignore nodes that are done already. */ + continue; + } + if (start_node->any_socket_is_directly_linked( + direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) { + /* Ignore non-start nodes. */ + continue; + } + + /* Do a depth-first search to sort nodes topologically. */ + nodes_to_check.push({start_node}); + while (!nodes_to_check.is_empty()) { + Item &item = nodes_to_check.peek(); + const NodeRef &node = *item.node; + const Span sockets = node.sockets( + direction == ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT); + + while (true) { + if (item.socket_index == sockets.size()) { + /* All sockets have already been visited. */ + break; + } + const SocketRef &socket = *sockets[item.socket_index]; + const Span linked_sockets = socket.directly_linked_sockets(); + if (item.link_index == linked_sockets.size()) { + /* All linkes connected to this socket have already been visited. */ + item.socket_index++; + item.link_index = 0; + continue; + } + const SocketRef &linked_socket = *linked_sockets[item.link_index]; + const NodeRef &linked_node = linked_socket.node(); + if (node_is_done_by_id[linked_node.id()]) { + /* The linked node has already been visited. */ + item.link_index++; + continue; + } + nodes_to_check.push({&linked_node}); + break; + } + + /* If no other element has been pushed, the current node can be pushed to the sorted list. */ + if (&item == &nodes_to_check.peek()) { + node_is_done_by_id[node.id()] = true; + toposort.append(&node); + nodes_to_check.pop(); + } + } + } + + return toposort; +} + std::string NodeTreeRef::to_dot() const { dot::DirectedGraph digraph; diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 31d8f8ef15c..e8d4239937f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_clamp_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Value").min(0.0f).max(1.0f).default_value(1.0f); b.add_input("Min").default_value(0.0f).min(-10000.0f).max(10000.0f); b.add_input("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index e4ada06133e..887cc84bb76 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); b.add_input("Vector").min(-1.0f).max(1.0f); b.add_output("Vector"); diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index a5f9a24a728..5ea194ddc83 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -29,6 +29,7 @@ namespace blender::nodes { static void sh_node_map_range_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Value").min(-10000.0f).max(10000.0f).default_value(1.0f); b.add_input("From Min").min(-10000.0f).max(10000.0f); b.add_input("From Max").min(-10000.0f).max(10000.0f).default_value(1.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 80a27b8e6a1..96d1be49c04 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -31,6 +31,7 @@ namespace blender::nodes { static void sh_node_math_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Value").default_value(0.5f).min(-10000.0f).max(10000.0f); b.add_input("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f); b.add_input("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index 860cc260d5d..d4d02e80ada 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_mix_rgb_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); b.add_input("Color1").default_value({0.5f, 0.5f, 0.5f, 1.0f}); b.add_input("Color2").default_value({0.5f, 0.5f, 0.5f, 1.0f}); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index 63be399366f..eac81a077bc 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_seprgb_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f}); b.add_output("R"); b.add_output("G"); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index b4b3c48482f..5a1cb3ecd52 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").min(-10000.0f).max(10000.0f); b.add_output("X"); b.add_output("Y"); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index d33d92f25fd..23f150d8135 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -23,6 +23,7 @@ namespace blender::nodes { static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").hide_value(); b.add_input("W").min(-1000.0f).max(1000.0f); b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index 1ae6b3a616c..5bf5e0f9876 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -25,7 +25,8 @@ namespace blender::nodes { static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b) { - b.add_input("Vector").hide_value(); + b.is_function_node(); + b.add_input("Vector").hide_value().implicit_field(); b.add_input("W").min(-1000.0f).max(1000.0f); b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); b.add_input("Detail").min(0.0f).max(16.0f).default_value(2.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index cea7af247c1..e12e5724e8e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -23,6 +23,7 @@ namespace blender::nodes { static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").hide_value(); b.add_input("W").min(-1000.0f).max(1000.0f); b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index bae16e10120..03543e5f7fe 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -23,6 +23,7 @@ namespace blender::nodes { static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").min(-10000.0f).max(10000.0f); b.add_input("W").min(-10000.0f).max(10000.0f); b.add_output("Value"); diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 1870caffbb1..d4d08be5d49 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -33,6 +33,7 @@ namespace blender::nodes { static void sh_node_valtorgb_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); b.add_output("Color"); b.add_output("Alpha"); diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 5b24e8bb72d..f49ff06cef1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -29,6 +29,7 @@ namespace blender::nodes { static void sh_node_vector_math_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").min(-10000.0f).max(10000.0f); b.add_input("Vector", "Vector_001").min(-10000.0f).max(10000.0f); b.add_input("Vector", "Vector_002").min(-10000.0f).max(10000.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index e9fd6c4f31e..fecd203e80a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").min(0.0f).max(1.0f).hide_value(); b.add_input("Vector"); b.add_input("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); From f7608276e35d03d5ad2d38f2189c6d6f5279c432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20M=C3=BCller?= Date: Thu, 23 Sep 2021 23:27:49 +0200 Subject: [PATCH 0167/1500] Audaspace: port bugfix from upstream. Fixes T91615: Instant crash when dragging video file into video editor sequencer --- extern/audaspace/bindings/C/AUD_Sound.cpp | 38 +++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/extern/audaspace/bindings/C/AUD_Sound.cpp b/extern/audaspace/bindings/C/AUD_Sound.cpp index aa246b9a047..8a3c9d1bbc9 100644 --- a/extern/audaspace/bindings/C/AUD_Sound.cpp +++ b/extern/audaspace/bindings/C/AUD_Sound.cpp @@ -102,26 +102,30 @@ AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo **stream_i if(file) { - auto streams = file->queryStreams(); - - size_t size = sizeof(AUD_StreamInfo) * streams.size(); - - if(!size) + try + { + auto streams = file->queryStreams(); + + size_t size = sizeof(AUD_StreamInfo) * streams.size(); + + if(!size) + { + *stream_infos = nullptr; + return 0; + } + + *stream_infos = reinterpret_cast(std::malloc(size)); + std::memcpy(*stream_infos, streams.data(), size); + + return streams.size(); + } + catch(Exception&) { - *stream_infos = nullptr; - return 0; } - - *stream_infos = reinterpret_cast(std::malloc(size)); - std::memcpy(*stream_infos, streams.data(), size); - - return streams.size(); - } - else - { - *stream_infos = nullptr; - return 0; } + + *stream_infos = nullptr; + return 0; } AUD_API sample_t* AUD_Sound_data(AUD_Sound* sound, int* length, AUD_Specs* specs) From 26141664f0c4db83749e9cbbf992e0f254d379ae Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 23 Sep 2021 17:45:41 -0500 Subject: [PATCH 0168/1500] Fix: Curve fill node doesn't fill real geometry with instances A misplaced return in the middle of the function made it so the node didn't fill real geometry. --- source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 8de2975f9b0..fcafdf93197 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -165,9 +165,6 @@ static void geo_node_curve_fill_exec(GeoNodeExecParams params) curve_fill_calculate(geometry_set, mode); } }); - - params.set_output("Mesh", std::move(geometry_set)); - return; } curve_fill_calculate(geometry_set, mode); From bffda4185dc7eee88e49818b72fa8c34dc2778e6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 24 Sep 2021 10:51:59 +1000 Subject: [PATCH 0169/1500] Cleanup: group convenience assignments in the keymap --- .../keyconfig/keymap_data/blender_default.py | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 4b15cdc25d3..3da434ac9d9 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -37,10 +37,8 @@ class Params: "cursor_set_event", "cursor_tweak_event", "use_mouse_emulate_3_button", - # Experimental option. - "pie_value", - # User preferences. + # User preferences: # # Swap 'Space/Shift-Space'. "spacebar_action", @@ -72,11 +70,13 @@ class Params: # (derived from other settings). # # This case needs to be checked often, - # convenience for: `params.use_fallback_tool if params.select_mouse == 'RIGHT' else False`. + # Shorthand for: `(params.use_fallback_tool if params.select_mouse == 'RIGHT' else False)`. "use_fallback_tool_rmb", - # Convenience for: `'CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value`. + # Shorthand for: `('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value)`. "select_mouse_value_fallback", - # Convenience for: `{"type": params.tool_maybe_tweak, "value": params.tool_maybe_tweak_value}`. + # Shorthand for: `('CLICK_DRAG' if params.use_pie_click_drag else 'PRESS')` + "pie_value", + # Shorthand for: `{"type": params.tool_maybe_tweak, "value": params.tool_maybe_tweak_value}`. "tool_maybe_tweak_event", ) @@ -106,6 +106,9 @@ class Params: self.apple = (platform == 'darwin') self.legacy = legacy + if use_mouse_emulate_3_button: + assert(use_alt_tool_or_cursor is False) + if select_mouse == 'RIGHT': # Right mouse select. self.select_mouse = 'RIGHTMOUSE' @@ -132,8 +135,6 @@ class Params: self.cursor_tweak_event = None self.use_fallback_tool = use_fallback_tool - self.use_fallback_tool_rmb = use_fallback_tool - self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value self.tool_modifier = {} else: # Left mouse select uses Click event for selection. This is a little @@ -157,8 +158,6 @@ class Params: self.cursor_set_event = {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True} self.cursor_tweak_event = {"type": 'EVT_TWEAK_R', "value": 'ANY', "shift": True} self.use_fallback_tool = True - self.use_fallback_tool_rmb = False - self.select_mouse_value_fallback = self.select_mouse_value # Use the "tool" functionality for LMB select. if use_alt_tool_or_cursor: @@ -169,7 +168,7 @@ class Params: self.use_mouse_emulate_3_button = use_mouse_emulate_3_button - # User preferences + # User preferences: self.spacebar_action = spacebar_action self.use_key_activate_tools = use_key_activate_tools @@ -183,12 +182,11 @@ class Params: self.use_alt_click_leader = use_alt_click_leader self.use_pie_click_drag = use_pie_click_drag - if not use_pie_click_drag: - self.pie_value = 'PRESS' - else: - self.pie_value = 'CLICK_DRAG' - # Convenience variables. + # Convenience variables: + self.use_fallback_tool_rmb = self.use_fallback_tool if self.select_mouse == 'RIGHT' else False + self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value + self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS' self.tool_maybe_tweak_event = {"type": self.tool_maybe_tweak, "value": self.tool_maybe_tweak_value} From 599d96e8f96f2fd40c51c9949091559b5f162869 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 24 Sep 2021 10:53:28 +1000 Subject: [PATCH 0170/1500] UI: keymap preference tweaks - Expose option to use shortcuts to activate tools as an enum. - "Emulate 3 Button Mouse" now disables preferences that depend on Alt-LMB. --- release/scripts/presets/keyconfig/Blender.py | 72 +++++++++++++------- 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index eb395dbc3b4..b165eaddcf5 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -8,6 +8,7 @@ from bpy.props import ( DIRNAME, FILENAME = os.path.split(__file__) IDNAME = os.path.splitext(FILENAME)[0] + def update_fn(_self, _context): load() @@ -54,12 +55,18 @@ class Prefs(bpy.types.KeyConfigPreferences): default='PLAY', update=update_fn, ) - use_key_activate_tools: BoolProperty( - name="Keys Activate Tools", + tool_key_mode: EnumProperty( + name="Tool Keys:", description=( - "Key shortcuts such as G, R, and S activate the tool instead of running it immediately" + "The method of keys to activate tools such as move, rotate & scale (G, R, S)" ), - default=False, + items=( + ('IMMEDIATE', "Immediate", + "Activate actions immediately"), + ('TOOL', "Active Tool", + "Activate the tool for editors that support tools"), + ), + default='IMMEDIATE', update=update_fn, ) @@ -90,7 +97,8 @@ class Prefs(bpy.types.KeyConfigPreferences): use_alt_tool: BoolProperty( name="Alt Tool Access", description=( - "Hold Alt to use the active tool when the gizmo would normally be required" + "Hold Alt to use the active tool when the gizmo would normally be required\n" + "Incompatible with the input preference \"Emulate 3 Button Mouse\" when the \"Alt\" key is used" ), default=False, update=update_fn, @@ -98,7 +106,8 @@ class Prefs(bpy.types.KeyConfigPreferences): use_alt_cursor: BoolProperty( name="Alt Cursor Access", description=( - "Hold Alt-LMB to place the Cursor (instead of LMB), allows tools to activate on press instead of drag" + "Hold Alt-LMB to place the Cursor (instead of LMB), allows tools to activate on press instead of drag.\n" + "Incompatible with the input preference \"Emulate 3 Button Mouse\" when the \"Alt\" key is used" ), default=False, update=update_fn, @@ -209,40 +218,53 @@ class Prefs(bpy.types.KeyConfigPreferences): ) def draw(self, layout): + from bpy import context + layout.use_property_split = True layout.use_property_decorate = False + prefs = context.preferences + is_select_left = (self.select_mouse == 'LEFT') + use_mouse_emulate_3_button = ( + prefs.inputs.use_mouse_emulate_3_button and + prefs.inputs.mouse_emulate_3_button_modifier == 'ALT' + ) # General settings. col = layout.column() - col.row().prop(self, "select_mouse", text="Select with Mouse Button", expand=True) - col.row().prop(self, "spacebar_action", text="Spacebar Action", expand=True) + col.row().prop(self, "select_mouse", text="Select with Mouse Button:", expand=True) + col.row().prop(self, "spacebar_action", text="Spacebar Action:", expand=True) if is_select_left: - col.row().prop(self, "gizmo_action", text="Activate Gizmo Event", expand=True) + col.row().prop(self, "gizmo_action", text="Activate Gizmo Event:", expand=True) else: - col.row().prop(self, "rmb_action", text="Right Mouse Select Action", expand=True) + col.row().prop(self, "rmb_action", text="Right Mouse Select Action:", expand=True) - # Checkboxes sub-layout. + col.row().prop(self, "tool_key_mode", expand=True) + + # Check-box sub-layout. col = layout.column() sub = col.column(align=True) row = sub.row() row.prop(self, "use_alt_click_leader") + + rowsub = row.row() if is_select_left: - row.prop(self, "use_alt_tool") + rowsub.prop(self, "use_alt_tool") else: - row.prop(self, "use_alt_cursor") + rowsub.prop(self, "use_alt_cursor") + rowsub.active = not use_mouse_emulate_3_button + row = sub.row() row.prop(self, "use_select_all_toggle") - row.prop(self, "use_key_activate_tools", text="Key Activates Tools") # 3DView settings. col = layout.column() col.label(text="3D View") - col.row().prop(self, "v3d_tilde_action", text="Grave Accent / Tilde Action", expand=True) - col.row().prop(self, "v3d_mmb_action", text="Middle Mouse Action", expand=True) - col.row().prop(self, "v3d_alt_mmb_drag_action", text="Alt Middle Mouse Drag Action", expand=True) + col.row().prop(self, "v3d_tilde_action", text="Grave Accent / Tilde Action:", expand=True) + col.row().prop(self, "v3d_mmb_action", text="Middle Mouse Action:", expand=True) + col.row().prop(self, "v3d_alt_mmb_drag_action", text="Alt Middle Mouse Drag Action:", expand=True) # Checkboxes sub-layout. col = layout.column() @@ -265,16 +287,17 @@ def load(): kc_prefs = kc.preferences is_select_left = (kc_prefs.select_mouse == 'LEFT') + use_mouse_emulate_3_button = ( + prefs.inputs.use_mouse_emulate_3_button and + prefs.inputs.mouse_emulate_3_button_modifier == 'ALT' + ) keyconfig_data = blender_default.generate_keymaps( blender_default.Params( select_mouse=kc_prefs.select_mouse, - use_mouse_emulate_3_button=( - prefs.inputs.use_mouse_emulate_3_button and - prefs.inputs.mouse_emulate_3_button_modifier == 'ALT' - ), + use_mouse_emulate_3_button=use_mouse_emulate_3_button, spacebar_action=kc_prefs.spacebar_action, - use_key_activate_tools=kc_prefs.use_key_activate_tools, + use_key_activate_tools=(kc_prefs.tool_key_mode == 'TOOL'), v3d_tilde_action=kc_prefs.v3d_tilde_action, use_v3d_mmb_pan=(kc_prefs.v3d_mmb_action == 'PAN'), v3d_alt_mmb_drag_action=kc_prefs.v3d_alt_mmb_drag_action, @@ -283,7 +306,10 @@ def load(): use_v3d_shade_ex_pie=kc_prefs.use_v3d_shade_ex_pie, use_gizmo_drag=(is_select_left and kc_prefs.gizmo_action == 'DRAG'), use_fallback_tool=(True if is_select_left else (kc_prefs.rmb_action == 'FALLBACK_TOOL')), - use_alt_tool_or_cursor=kc_prefs.use_alt_tool if is_select_left else kc_prefs.use_alt_cursor, + use_alt_tool_or_cursor=( + (not use_mouse_emulate_3_button) and + (kc_prefs.use_alt_tool if is_select_left else kc_prefs.use_alt_cursor) + ), use_alt_click_leader=kc_prefs.use_alt_click_leader, use_pie_click_drag=kc_prefs.use_pie_click_drag, ), From bc65c7d0e579c19169baf8be609afa066712c2cc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 24 Sep 2021 11:31:23 +1000 Subject: [PATCH 0171/1500] Cleanup: spelling in comments --- intern/cycles/render/gpu_display.h | 2 +- intern/cycles/render/osl.cpp | 4 ++-- intern/cycles/util/util_debug.h | 2 +- intern/ghost/intern/GHOST_SystemSDL.cpp | 4 ++-- intern/ghost/intern/GHOST_SystemWin32.cpp | 4 ++-- intern/ghost/intern/GHOST_SystemX11.cpp | 4 ++-- source/blender/blenkernel/BKE_asset.h | 2 +- source/blender/blenkernel/BKE_asset_catalog.hh | 14 +++++++------- source/blender/blenkernel/intern/asset_test.cc | 4 ++-- source/blender/blenkernel/intern/displist.cc | 2 +- source/blender/blenkernel/intern/dynamicpaint.c | 2 +- .../blender/blenkernel/intern/gpencil_modifier.c | 8 +++++--- source/blender/blenkernel/intern/ipo.c | 3 ++- source/blender/blenkernel/intern/material.c | 2 +- .../blender/blenkernel/intern/mball_tessellate.c | 2 +- source/blender/blenkernel/intern/mesh_convert.cc | 10 +++++----- source/blender/blenkernel/intern/node.cc | 2 +- source/blender/blenkernel/intern/scene.c | 4 ++-- source/blender/blenlib/BLI_uuid.h | 8 ++++---- source/blender/bmesh/tools/bmesh_bevel.c | 4 ++-- source/blender/editors/animation/keyingsets.c | 2 +- source/blender/editors/gpencil/annotate_paint.c | 2 +- source/blender/editors/screen/area.c | 2 +- .../editors/space_clip/tracking_ops_track.c | 4 ++-- .../blender/imbuf/intern/openexr/openexr_api.cpp | 10 +++++----- source/blender/makesdna/DNA_cloth_types.h | 2 +- source/blender/makesdna/DNA_constraint_types.h | 4 ++-- source/blender/makesdna/DNA_modifier_types.h | 2 +- source/blender/makesrna/intern/rna_material_api.c | 4 ++-- source/blender/makesrna/intern/rna_object_force.c | 4 ++-- source/blender/nodes/intern/node_tree_ref.cc | 2 +- 31 files changed, 64 insertions(+), 61 deletions(-) diff --git a/intern/cycles/render/gpu_display.h b/intern/cycles/render/gpu_display.h index 0340e0b7e45..3c3cfaea513 100644 --- a/intern/cycles/render/gpu_display.h +++ b/intern/cycles/render/gpu_display.h @@ -163,7 +163,7 @@ class GPUDisplay { * This call might happen in parallel with draw, but can never happen in parallel with the * update. * - * The actual zero-ing can be deferred to a later moment. What is important is that after clear + * The actual zeroing can be deferred to a later moment. What is important is that after clear * and before pixels update the drawing texture will be fully empty, and that partial update * after clear will write new pixel values for an updating area, leaving everything else zeroed. * diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index d28b222c10e..5a43b641872 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -727,8 +727,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath) } } - /* create shader of the appropriate type. OSL only distinguishes between "surface" - * and "displacement" atm */ + /* Create shader of the appropriate type. OSL only distinguishes between "surface" + * and "displacement" at the moment. */ if (current_type == SHADER_TYPE_SURFACE) ss->Shader("surface", name, id(node).c_str()); else if (current_type == SHADER_TYPE_VOLUME) diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/util_debug.h index 99e2723180c..a2acaea5675 100644 --- a/intern/cycles/util/util_debug.h +++ b/intern/cycles/util/util_debug.h @@ -89,7 +89,7 @@ class DebugFlags { void reset(); /* Whether adaptive feature based runtime compile is enabled or not. - * Requires the CUDA Toolkit and only works on Linux atm. */ + * Requires the CUDA Toolkit and only works on Linux at the moment. */ bool adaptive_compile; }; diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 35c7a7ef463..5370d4df857 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -374,8 +374,8 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) window->getClientBounds(bounds); - /* Could also clamp to screen bounds wrap with a window outside the view will fail atm. - * Use offset of 8 in case the window is at screen bounds. */ + /* Could also clamp to screen bounds wrap with a window outside the view will + * fail at the moment. Use offset of 8 in case the window is at screen bounds. */ bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis()); window->getCursorGrabAccum(x_accum, y_accum); diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index f44107ee000..482f20f5cd1 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1100,8 +1100,8 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind window->getClientBounds(bounds); } - /* Could also clamp to screen bounds wrap with a window outside the view will fail atm. - * Use inset in case the window is at screen bounds. */ + /* Could also clamp to screen bounds wrap with a window outside the view will + * fail at the moment. Use inset in case the window is at screen bounds. */ bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis()); window->getCursorGrabAccum(x_accum, y_accum); diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 10ccb00cc15..c87b745cf40 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -973,8 +973,8 @@ void GHOST_SystemX11::processEvent(XEvent *xe) if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) window->getClientBounds(bounds); - /* Could also clamp to screen bounds wrap with a window outside the view will fail atm. - * Use offset of 8 in case the window is at screen bounds. */ + /* Could also clamp to screen bounds wrap with a window outside the view will + * fail at the moment. Use offset of 8 in case the window is at screen bounds. */ bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis()); window->getCursorGrabAccum(x_accum, y_accum); diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h index 7ae5378272d..42eea41b7a7 100644 --- a/source/blender/blenkernel/BKE_asset.h +++ b/source/blender/blenkernel/BKE_asset.h @@ -48,7 +48,7 @@ struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(struct AssetMetaData * const char *name); void BKE_asset_metadata_tag_remove(struct AssetMetaData *asset_data, struct AssetTag *tag); -/** Clean up the catalog ID (whitespaces removed, length reduced, etc.) and assign it. */ +/** Clean up the catalog ID (white-spaces removed, length reduced, etc.) and assign it. */ void BKE_asset_metadata_catalog_id_clear(struct AssetMetaData *asset_data); void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data, bUUID catalog_id, diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 0401fae7126..5fd68e33f4a 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -69,7 +69,7 @@ class AssetCatalogService { * still have to be saved. * * Return true on success, which either means there were no in-memory categories to save, or the - * save was succesfful. */ + * save was successful. */ bool write_to_disk(const CatalogFilePath &directory_for_new_files); /** @@ -96,7 +96,7 @@ class AssetCatalogService { AssetCatalogTree *get_catalog_tree(); - /** Return true iff there are no catalogs known. */ + /** Return true only if there are no catalogs known. */ bool is_empty() const; protected: @@ -196,7 +196,7 @@ class AssetCatalogDefinitionFile { bool write_to_disk(const CatalogFilePath &dest_file_path) const; bool contains(CatalogID catalog_id) const; - /* Add a new catalog. Undefined behaviour if a catalog with the same ID was already added. */ + /* Add a new catalog. Undefined behavior if a catalog with the same ID was already added. */ void add_new(AssetCatalog *catalog); using AssetCatalogParsedFn = FunctionRef)>; @@ -230,9 +230,9 @@ class AssetCatalog { CatalogPath path; /** * Simple, human-readable name for the asset catalog. This is stored on assets alongside the - * catalog ID; the catalog ID is a UUID that is not human-readable, so to avoid complete dataloss - * when the catalog definition file gets lost, we also store a human-readable simple name for the - * catalog. */ + * catalog ID; the catalog ID is a UUID that is not human-readable, + * so to avoid complete data-loss when the catalog definition file gets lost, + * we also store a human-readable simple name for the catalog. */ std::string simple_name; struct Flags { @@ -242,7 +242,7 @@ class AssetCatalog { } flags; /** - * Create a new Catalog with the given path, auto-generating a sensible catalog simplename. + * Create a new Catalog with the given path, auto-generating a sensible catalog simple-name. * * NOTE: the given path will be cleaned up (trailing spaces removed, etc.), so the returned * `AssetCatalog`'s path differ from the given one. diff --git a/source/blender/blenkernel/intern/asset_test.cc b/source/blender/blenkernel/intern/asset_test.cc index f1bf21a4f83..77b98a8ac0a 100644 --- a/source/blender/blenkernel/intern/asset_test.cc +++ b/source/blender/blenkernel/intern/asset_test.cc @@ -42,7 +42,7 @@ TEST(AssetMetadataTest, set_catalog_id) EXPECT_TRUE(BLI_uuid_equal(uuid, meta.catalog_id)); EXPECT_STREQ("simple", meta.catalog_simple_name); - /* Test whitespace trimming. */ + /* Test white-space trimming. */ BKE_asset_metadata_catalog_id_set(&meta, uuid, " Govoriš angleško? "); EXPECT_STREQ("Govoriš angleško?", meta.catalog_simple_name); @@ -52,7 +52,7 @@ TEST(AssetMetadataTest, set_catalog_id) BKE_asset_metadata_catalog_id_set(&meta, uuid, len66); EXPECT_STREQ(len63, meta.catalog_simple_name); - /* Test length trimming happens after whitespace trimming. */ + /* Test length trimming happens after white-space trimming. */ constexpr char len68[] = " \ 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 "; diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 79e913d266f..0776f3b9a68 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -261,7 +261,7 @@ bool BKE_displist_surfindex_get( return true; } -/* ****************** make displists ********************* */ +/* ****************** Make #DispList ********************* */ #ifdef __INTEL_COMPILER /* ICC with the optimization -02 causes crashes. */ # pragma intel optimization_level 1 diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index d75b3259148..2bad47f2ed1 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -317,7 +317,7 @@ static bool setError(DynamicPaintCanvasSettings *canvas, const char *string) static int dynamicPaint_surfaceNumOfPoints(DynamicPaintSurface *surface) { if (surface->format == MOD_DPAINT_SURFACE_F_PTEX) { - return 0; /* not supported atm */ + return 0; /* Not supported at the moment. */ } if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { const Mesh *canvas_mesh = dynamicPaint_canvas_mesh_get(surface->canvas); diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index de459c2e414..a6164340477 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -65,7 +65,7 @@ static CLG_LogRef LOG = {"bke.gpencil_modifier"}; static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = {NULL}; #if 0 -/* Note that GPencil actually does not support these atm, but might do in the future. */ +/* Note that GPencil actually does not support these at the moment, but might do in the future. */ static GpencilVirtualModifierData virtualModifierCommonData; #endif @@ -129,7 +129,8 @@ GpencilModifierData *BKE_gpencil_modifiers_get_virtual_modifierlist( GpencilModifierData *md = ob->greasepencil_modifiers.first; #if 0 - /* Note that GPencil actually does not support these atm, but might do in the future. */ + /* Note that GPencil actually does not support these at the moment, + * but might do in the future. */ *virtualModifierData = virtualModifierCommonData; if (ob->parent) { if (ob->parent->type == OB_ARMATURE && ob->partype == PARSKEL) { @@ -328,7 +329,8 @@ void BKE_gpencil_modifier_init(void) gpencil_modifier_type_init(modifier_gpencil_types); /* MOD_gpencil_util.c */ #if 0 - /* Note that GPencil actually does not support these atm, but might do in the future. */ + /* Note that GPencil actually does not support these at the moment, + * but might do in the future. */ /* Initialize global common storage used for virtual modifier list. */ GpencilModifierData *md; md = BKE_gpencil_modifier_new(eGpencilModifierType_Armature); diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 9b72a2d1a72..26a1240080f 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -2013,7 +2013,8 @@ static void nlastrips_to_animdata(ID *id, ListBase *strips) } } - /* try to add this strip to the current NLA-Track (i.e. the 'last' one on the stack atm) */ + /* Try to add this strip to the current NLA-Track + * (i.e. the 'last' one on the stack at the moment). */ if (BKE_nlatrack_add_strip(nlt, strip, false) == 0) { /* trying to add to the current failed (no space), * so add a new track to the stack, and add to that... diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 5f53d5e1ae8..84e1f6c3c5f 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -473,7 +473,7 @@ bool BKE_object_material_slot_used(ID *id, short actcol) case ID_CU: return BKE_curve_material_index_used((Curve *)id, actcol - 1); case ID_MB: - /* meta-elems don't have materials atm */ + /* Meta-elements don't support materials at the moment. */ return false; case ID_GD: return BKE_gpencil_material_index_used((bGPdata *)id, actcol - 1); diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c index 9dd583b4c6b..a2590171abd 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.c +++ b/source/blender/blenkernel/intern/mball_tessellate.c @@ -454,7 +454,7 @@ static void make_face(PROCESS *process, int i1, int i2, int i3, int i4) cur = process->indices[process->curindex++]; - /* displists now support array drawing, we treat tri's as fake quad */ + /* #DispList supports array drawing, treat tri's as fake quad. */ cur[0] = i1; cur[1] = i2; diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 51acbeb8f5b..59cdb6a2b27 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -873,8 +873,8 @@ static Object *object_for_curve_to_mesh_create(const Object *object) { const Curve *curve = (const Curve *)object->data; - /* reate a temporary object which can be evaluated and modified by generic - * curve evaluation (hence the LIB_ID_COPY_SET_COPIED_ON_WRITE flag). */ + /* Create a temporary object which can be evaluated and modified by generic + * curve evaluation (hence the #LIB_ID_COPY_SET_COPIED_ON_WRITE flag). */ Object *temp_object = (Object *)BKE_id_copy_ex( nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_SET_COPIED_ON_WRITE); @@ -882,7 +882,7 @@ static Object *object_for_curve_to_mesh_create(const Object *object) BKE_object_free_modifiers(temp_object, LIB_ID_CREATE_NO_USER_REFCOUNT); /* Need to create copy of curve itself as well, since it will be changed by the curve evaluation - * process. NOTE: Copies the data, but not the shapekeys. */ + * process. NOTE: Copies the data, but not the shape-keys. */ temp_object->data = BKE_id_copy_ex(nullptr, (const ID *)object->data, nullptr, @@ -893,7 +893,7 @@ static Object *object_for_curve_to_mesh_create(const Object *object) * result. */ BKE_curve_texspace_calc(temp_curve); - /* Temporarily set edit so we get updates from edit mode, but also because for text datablocks + /* Temporarily set edit so we get updates from edit mode, but also because for text data-blocks * copying it while in edit mode gives invalid data structures. */ temp_curve->editfont = curve->editfont; temp_curve->editnurb = curve->editnurb; @@ -1189,7 +1189,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, return mesh_in_bmain; } - /* Make sure mesh only points original datablocks, also increase users of materials and other + /* Make sure mesh only points original data-blocks, also increase users of materials and other * possibly referenced data-blocks. * * Going to original data-blocks is required to have bmain in a consistent state, where diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index f223ed28301..3f4e6281ca7 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -1123,7 +1123,7 @@ static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node) RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr); /* XXX Warning: context can be nullptr in case nodes are added in do_versions. - * Delayed init is not supported for nodes with context-based initfunc_api atm. + * Delayed init is not supported for nodes with context-based `initfunc_api` at the moment. */ BLI_assert(C != nullptr); ntype->initfunc_api(C, &ptr); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 6b5c94a2786..ccd3e0bfd6c 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -2465,7 +2465,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on // DEG_debug_graph_relations_validate(depsgraph, bmain, scene); /* Flush editing data if needed. */ prepare_mesh_for_viewport_render(bmain, view_layer); - /* Update all objects: drivers, matrices, displists, etc. flags set + /* Update all objects: drivers, matrices, #DispList, etc. flags set * by depsgraph or manual, no layer check here, gets correct flushed. */ DEG_evaluate_on_refresh(depsgraph); /* Update sound system. */ @@ -2541,7 +2541,7 @@ void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool cle BKE_image_editors_update_frame(bmain, scene->r.cfra); BKE_sound_set_cfra(scene->r.cfra); DEG_graph_relations_update(depsgraph); - /* Update all objects: drivers, matrices, displists, etc. flags set + /* Update all objects: drivers, matrices, #DispList, etc. flags set * by depgraph or manual, no layer check here, gets correct flushed. * * NOTE: Only update for new frame on first iteration. Second iteration is for ensuring user diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 7ac676b7617..98a600a5de8 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -79,17 +79,17 @@ namespace blender { class bUUID : public ::bUUID { public: /** - * Default constructor, used with `bUUID value{};`, will initialise to the nil UUID. + * Default constructor, used with `bUUID value{};`, will initialize to the nil UUID. */ bUUID() = default; - /** Initialise from the bUUID DNA struct. */ + /** Initialize from the bUUID DNA struct. */ bUUID(const ::bUUID &struct_uuid); - /** Initialise from 11 integers, 5 for the regular fields and 6 for the `node` array. */ + /** Initialize from 11 integers, 5 for the regular fields and 6 for the `node` array. */ bUUID(std::initializer_list field_values); - /** Initialise by parsing the string; undefined behaviour when the string is invalid. */ + /** Initialize by parsing the string; undefined behavior when the string is invalid. */ explicit bUUID(const std::string &string_formatted_uuid); uint64_t hash() const; diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index e306fe47770..0fe687da44e 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -3582,7 +3582,7 @@ static void adjust_the_cycle_or_chain(BoundVert *vstart, bool iscycle) } /* Residue np + 2*i (if cycle) else np - 1 + 2*i: - * right offset for parm i matches its spec; weighted. */ + * right offset for parameter i matches its spec; weighted. */ int row = iscycle ? np + 2 * i : np - 1 + 2 * i; EIG_linear_solver_matrix_add(solver, row, i, weight); EIG_linear_solver_right_hand_side_add(solver, 0, row, weight * eright->offset_r); @@ -3595,7 +3595,7 @@ static void adjust_the_cycle_or_chain(BoundVert *vstart, bool iscycle) #endif /* Residue np + 2*i + 1 (if cycle) else np - 1 + 2*i + 1: - * left offset for parm i matches its spec; weighted. */ + * left offset for parameter i matches its spec; weighted. */ row = row + 1; EIG_linear_solver_matrix_add( solver, row, (i == np - 1) ? 0 : i + 1, weight * v->adjchain->sinratio); diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index 0206aabd359..e1fd3b07f46 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -610,7 +610,7 @@ void ANIM_keyingset_info_unregister(Main *bmain, KeyingSetInfo *ksi) KeyingSet *ks, *ksn; /* find relevant builtin KeyingSets which use this, and remove them */ - /* TODO: this isn't done now, since unregister is really only used atm when we + /* TODO: this isn't done now, since unregister is really only used at the moment when we * reload the scripts, which kindof defeats the purpose of "builtin"? */ for (ks = builtin_keyingsets.first; ks; ks = ksn) { ksn = ks->next; diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 68e2aece6e2..59ea105fbbb 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -2104,7 +2104,7 @@ static void annotation_draw_apply_event( p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; } } - /* We are using the temporal stabilizer flag atm, + /* We are using the temporal stabilizer flag at the moment, * but shift is not pressed as well as the permanent flag is not used, * so we don't need the cursor anymore. */ else if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index c71e68df2fd..d3cbeb4e1d1 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -3000,7 +3000,7 @@ void ED_region_panels_layout_ex(const bContext *C, /* before setting the view */ if (region_layout_based) { - /* XXX, only single panel support atm. + /* XXX, only single panel support at the moment. * Can't use x/y values calculated above because they're not using the real height of panels, * instead they calculate offsets for the next panel to start drawing. */ Panel *panel = region->panels.last; diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c index 0a99d1020f6..0e78a3e9a1e 100644 --- a/source/blender/editors/space_clip/tracking_ops_track.c +++ b/source/blender/editors/space_clip/tracking_ops_track.c @@ -196,8 +196,8 @@ static bool track_markers_initjob(bContext *C, TrackMarkersJob *tmj, bool backwa /* XXX: silly to store this, but this data is needed to update scene and * movie-clip numbers when tracking is finished. This introduces * better feedback for artists. - * Maybe there's another way to solve this problem, but can't think - * better way atm. + * Maybe there's another way to solve this problem, + * but can't think better way at the moment. * Anyway, this way isn't more unstable as animation rendering * animation which uses the same approach (except storing screen). */ diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index ec0a085700a..adf09f8dda8 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -555,7 +555,7 @@ static bool imb_save_openexr_float(ImBuf *ibuf, const char *name, const int flag int xstride = sizeof(float) * channels; int ystride = -xstride * width; - /* last scanline, stride negative */ + /* Last scan-line, stride negative. */ float *rect[4] = {nullptr, nullptr, nullptr, nullptr}; rect[0] = ibuf->rect_float + channels * (height - 1) * width; rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0]; @@ -654,7 +654,7 @@ struct ExrChannel { char name[EXR_TOT_MAXNAME + 1]; /* full name with everything */ struct MultiViewChannelName *m; /* struct to store all multipart channel info */ - int xstride, ystride; /* step to next pixel, to next scanline */ + int xstride, ystride; /* step to next pixel, to next scan-line. */ float *rect; /* first pointer to write in */ char chan_id; /* quick lookup of channel char */ int view_id; /* quick lookup of channel view */ @@ -1127,7 +1127,7 @@ void IMB_exr_write_channels(void *handle) } for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { - /* Writing starts from last scanline, stride negative. */ + /* Writing starts from last scan-line, stride negative. */ if (echan->use_half_float) { float *rect = echan->rect; half *cur = current_rect_half; @@ -1269,7 +1269,7 @@ void IMB_exr_read_channels(void *handle) if (!flip) { /* Inverse correct first pixel for data-window coordinates. */ rect -= echan->xstride * (dw.min.x - dw.min.y * data->width); - /* move to last scanline to flip to Blender convention */ + /* Move to last scan-line to flip to Blender convention. */ rect += echan->xstride * (data->height - 1) * data->width; ystride = -ystride; } @@ -2009,7 +2009,7 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem, /* Inverse correct first pixel for data-window * coordinates (- dw.min.y because of y flip). */ first = ibuf->rect_float - 4 * (dw.min.x - dw.min.y * width); - /* but, since we read y-flipped (negative y stride) we move to last scanline */ + /* But, since we read y-flipped (negative y stride) we move to last scan-line. */ first += 4 * (height - 1) * width; if (num_rgb_channels > 0) { diff --git a/source/blender/makesdna/DNA_cloth_types.h b/source/blender/makesdna/DNA_cloth_types.h index e2eea5e5422..49b2862032f 100644 --- a/source/blender/makesdna/DNA_cloth_types.h +++ b/source/blender/makesdna/DNA_cloth_types.h @@ -42,7 +42,7 @@ extern "C" { */ typedef struct ClothSimSettings { - /** UNUSED atm. */ + /** UNUSED. */ struct LinkNode *cache; /** See SB. */ float mingoal; diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 4b4c24a7a4e..28756395f7d 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -81,8 +81,8 @@ typedef struct bConstraint { /** Local influence ipo or driver */ struct Ipo *ipo DNA_DEPRECATED; - /* below are readonly fields that are set at runtime - * by the solver for use in the GE (only IK atm) */ + /* Below are read-only fields that are set at runtime + * by the solver for use in the GE (only IK at the moment). */ /** Residual error on constraint expressed in blender unit. */ float lin_error; /** Residual error on constraint expressed in radiant. */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 31daa778b03..631db64ddd3 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -848,7 +848,7 @@ typedef struct CollisionModifierData { struct MVert *x; /** Position at the end of the frame. */ struct MVert *xnew; - /** Unused atm, but was discussed during sprint. */ + /** Unused at the moment, but was discussed during sprint. */ struct MVert *xold; /** New position at the actual inter-frame step. */ struct MVert *current_xnew; diff --git a/source/blender/makesrna/intern/rna_material_api.c b/source/blender/makesrna/intern/rna_material_api.c index f5b13fdbc81..5f89664458c 100644 --- a/source/blender/makesrna/intern/rna_material_api.c +++ b/source/blender/makesrna/intern/rna_material_api.c @@ -38,8 +38,8 @@ void RNA_api_material(StructRNA *UNUSED(srna)) { - /* FunctionRNA *func; */ - /* PropertyRNA *parm; */ + // FunctionRNA *func; + // PropertyRNA *parm; } #endif diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 98d59bf3a1a..7f997109920 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -1056,8 +1056,8 @@ static void rna_def_ptcache_point_caches(BlenderRNA *brna, PropertyRNA *cprop) StructRNA *srna; PropertyRNA *prop; - /* FunctionRNA *func; */ - /* PropertyRNA *parm; */ + // FunctionRNA *func; + // PropertyRNA *parm; RNA_def_property_srna(cprop, "PointCaches"); srna = RNA_def_struct(brna, "PointCaches", NULL); diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index d299f062fab..43c7fbd2599 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -548,7 +548,7 @@ Vector NodeTreeRef::toposort(const ToposortDirection direction) const SocketRef &socket = *sockets[item.socket_index]; const Span linked_sockets = socket.directly_linked_sockets(); if (item.link_index == linked_sockets.size()) { - /* All linkes connected to this socket have already been visited. */ + /* All links connected to this socket have already been visited. */ item.socket_index++; item.link_index = 0; continue; From 0e039749e3d79cd6ab5ee94429298d732de39ab8 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 23 Sep 2021 22:23:14 -0500 Subject: [PATCH 0172/1500] Fix: Incorrect field visualization for some shader nodes These need to be tagged as function nodes in their declaration. --- source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc | 1 + source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index eac81a077bc..24c5dcf7ba3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -120,6 +120,7 @@ namespace blender::nodes { static void sh_node_combrgb_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("R").min(0.0f).max(1.0f); b.add_input("G").min(0.0f).max(1.0f); b.add_input("B").min(0.0f).max(1.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index 5a1cb3ecd52..8ca8fc19521 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -104,6 +104,7 @@ namespace blender::nodes { static void sh_node_combxyz_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("X").min(-10000.0f).max(10000.0f); b.add_input("Y").min(-10000.0f).max(10000.0f); b.add_input("Z").min(-10000.0f).max(10000.0f); From 0f764ade1a2fd8aa9c462dd5850e8be52aa203f5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 23 Sep 2021 23:56:15 -0500 Subject: [PATCH 0173/1500] Fix T91661: Vector rotate output socket diconnects on file load An incorrect name was used in the socket declaration refactor. --- source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index fecd203e80a..75c72754af6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -33,7 +33,7 @@ static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b) b.add_input("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); b.add_input("Angle").subtype(PROP_ANGLE); b.add_input("Rotation").subtype(PROP_EULER); - b.add_output("Value"); + b.add_output("Vector"); }; } // namespace blender::nodes From 6a88f83d679f281d7adb3798ab4770069a63c2da Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 24 Sep 2021 07:42:36 +0200 Subject: [PATCH 0174/1500] Hair Info Length Attribute Goal is to add the length attribute to the Hair Info node, for better control over color gradients or similar along the hair. Reviewed By: #eevee_viewport, brecht Differential Revision: https://developer.blender.org/D10481 --- intern/cycles/blender/blender_curves.cpp | 18 ++++ intern/cycles/kernel/kernel_types.h | 1 + intern/cycles/kernel/osl/osl_services.cpp | 1 + intern/cycles/kernel/osl/osl_services.h | 1 + .../cycles/kernel/shaders/node_hair_info.osl | 2 + intern/cycles/kernel/svm/svm_geometry.h | 2 + intern/cycles/kernel/svm/svm_types.h | 1 + intern/cycles/render/attribute.cpp | 5 ++ intern/cycles/render/nodes.cpp | 10 +++ source/blender/blenkernel/intern/customdata.c | 3 + .../draw/engines/eevee/eevee_cryptomatte.c | 2 +- .../draw/engines/eevee/eevee_materials.c | 6 +- .../draw/engines/eevee/eevee_motion_blur.c | 2 +- .../draw/engines/workbench/workbench_engine.c | 2 +- .../draw/intern/draw_cache_impl_curve.cc | 3 + .../draw/intern/draw_cache_impl_hair.c | 67 +++++++++----- .../draw/intern/draw_cache_impl_particles.c | 89 +++++++++++++------ source/blender/draw/intern/draw_common.h | 4 +- source/blender/draw/intern/draw_hair.c | 16 ++-- .../blender/draw/intern/draw_hair_private.h | 6 ++ .../draw/intern/shaders/common_hair_lib.glsl | 6 ++ source/blender/gpu/intern/gpu_codegen.c | 18 +++- .../gpu/shaders/gpu_shader_codegen_lib.glsl | 10 +++ .../gpu_shader_material_hair_info.glsl | 5 +- .../blender/makesdna/DNA_customdata_types.h | 9 +- .../shader/nodes/node_shader_hair_info.c | 7 +- 26 files changed, 228 insertions(+), 68 deletions(-) diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp index 6fe5ea41fff..c7851e40543 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/blender_curves.cpp @@ -283,10 +283,13 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa return; Attribute *attr_intercept = NULL; + Attribute *attr_length = NULL; Attribute *attr_random = NULL; if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT); + if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) + attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH); if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM); @@ -336,6 +339,11 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa num_curve_keys++; } + if (attr_length != NULL) + { + attr_length->add(CData->curve_length[curve]); + } + if (attr_random != NULL) { attr_random->add(hash_uint2_to_float(num_curves, 0)); } @@ -657,11 +665,16 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) /* Add requested attributes. */ Attribute *attr_intercept = NULL; + Attribute *attr_length = NULL; Attribute *attr_random = NULL; if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) { attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT); } + if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) + { + attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH); + } if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) { attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM); } @@ -714,6 +727,11 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) } } + if (attr_length) + { + attr_length->add(length); + } + /* Random number per curve. */ if (attr_random != NULL) { attr_random->add(hash_uint2_to_float(b_curve.index(), 0)); diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 66b7310ab65..3cc42bf7a85 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -572,6 +572,7 @@ typedef enum AttributeStandard { ATTR_STD_MOTION_VERTEX_NORMAL, ATTR_STD_PARTICLE, ATTR_STD_CURVE_INTERCEPT, + ATTR_STD_CURVE_LENGTH, ATTR_STD_CURVE_RANDOM, ATTR_STD_PTEX_FACE_ID, ATTR_STD_PTEX_UV, diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 396f42080e4..4fc46a255a8 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -107,6 +107,7 @@ ustring OSLRenderServices::u_geom_undisplaced("geom:undisplaced"); ustring OSLRenderServices::u_is_smooth("geom:is_smooth"); ustring OSLRenderServices::u_is_curve("geom:is_curve"); ustring OSLRenderServices::u_curve_thickness("geom:curve_thickness"); +ustring OSLRenderServices::u_curve_length("geom:curve_length"); ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal"); ustring OSLRenderServices::u_curve_random("geom:curve_random"); ustring OSLRenderServices::u_path_ray_length("path:ray_length"); diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/osl_services.h index 58accb46e7d..2a5400282b3 100644 --- a/intern/cycles/kernel/osl/osl_services.h +++ b/intern/cycles/kernel/osl/osl_services.h @@ -294,6 +294,7 @@ class OSLRenderServices : public OSL::RendererServices { static ustring u_is_smooth; static ustring u_is_curve; static ustring u_curve_thickness; + static ustring u_curve_length; static ustring u_curve_tangent_normal; static ustring u_curve_random; static ustring u_path_ray_length; diff --git a/intern/cycles/kernel/shaders/node_hair_info.osl b/intern/cycles/kernel/shaders/node_hair_info.osl index ee08ea57e68..ddc2e28b83a 100644 --- a/intern/cycles/kernel/shaders/node_hair_info.osl +++ b/intern/cycles/kernel/shaders/node_hair_info.osl @@ -18,12 +18,14 @@ shader node_hair_info(output float IsStrand = 0.0, output float Intercept = 0.0, + output float Length = 0.0, output float Thickness = 0.0, output normal TangentNormal = N, output float Random = 0) { getattribute("geom:is_curve", IsStrand); getattribute("geom:curve_intercept", Intercept); + getattribute("geom:curve_length", Length); getattribute("geom:curve_thickness", Thickness); getattribute("geom:curve_tangent_normal", TangentNormal); getattribute("geom:curve_random", Random); diff --git a/intern/cycles/kernel/svm/svm_geometry.h b/intern/cycles/kernel/svm/svm_geometry.h index 10e9f291d0e..432529eb061 100644 --- a/intern/cycles/kernel/svm/svm_geometry.h +++ b/intern/cycles/kernel/svm/svm_geometry.h @@ -213,6 +213,8 @@ ccl_device_noinline void svm_node_hair_info( } case NODE_INFO_CURVE_INTERCEPT: break; /* handled as attribute */ + case NODE_INFO_CURVE_LENGTH: + break; /* handled as attribute */ case NODE_INFO_CURVE_RANDOM: break; /* handled as attribute */ case NODE_INFO_CURVE_THICKNESS: { diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index c053be96c51..313bc3235b9 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -173,6 +173,7 @@ typedef enum NodeParticleInfo { typedef enum NodeHairInfo { NODE_INFO_CURVE_IS_STRAND, NODE_INFO_CURVE_INTERCEPT, + NODE_INFO_CURVE_LENGTH, NODE_INFO_CURVE_THICKNESS, /* Fade for minimum hair width transiency. */ // NODE_INFO_CURVE_FADE, diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp index ea5a5f50f2d..aaf21ad9fd2 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/render/attribute.cpp @@ -342,6 +342,8 @@ const char *Attribute::standard_name(AttributeStandard std) return "particle"; case ATTR_STD_CURVE_INTERCEPT: return "curve_intercept"; + case ATTR_STD_CURVE_LENGTH: + return "curve_length"; case ATTR_STD_CURVE_RANDOM: return "curve_random"; case ATTR_STD_PTEX_FACE_ID: @@ -586,6 +588,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name) case ATTR_STD_CURVE_INTERCEPT: attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE_KEY); break; + case ATTR_STD_CURVE_LENGTH: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE); + break; case ATTR_STD_CURVE_RANDOM: attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE); break; diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 03b79d7de3e..e5071c25730 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -4368,6 +4368,7 @@ NODE_DEFINE(HairInfoNode) SOCKET_OUT_FLOAT(is_strand, "Is Strand"); SOCKET_OUT_FLOAT(intercept, "Intercept"); + SOCKET_OUT_FLOAT(size, "Length"); SOCKET_OUT_FLOAT(thickness, "Thickness"); SOCKET_OUT_NORMAL(tangent_normal, "Tangent Normal"); #if 0 /* Output for minimum hair width transparency - deactivated. */ @@ -4390,6 +4391,9 @@ void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) if (!intercept_out->links.empty()) attributes->add(ATTR_STD_CURVE_INTERCEPT); + if (!output("Length")->links.empty()) + attributes->add(ATTR_STD_CURVE_LENGTH); + if (!output("Random")->links.empty()) attributes->add(ATTR_STD_CURVE_RANDOM); } @@ -4412,6 +4416,12 @@ void HairInfoNode::compile(SVMCompiler &compiler) compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT); } + out = output("Length"); + if (!out->links.empty()) { + int attr = compiler.attribute(ATTR_STD_CURVE_LENGTH); + compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT); + } + out = output("Thickness"); if (!out->links.empty()) { compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_THICKNESS, compiler.stack_assign(out)); diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index ad2d5d267d5..3bb02e1856b 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1856,6 +1856,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { NULL, NULL, NULL}, + /* 51: CD_HAIRLENGTH */ + {sizeof(float), "float", 1, NULL, NULL, NULL, NULL, NULL, NULL}, }; static const char *LAYERTYPENAMES[CD_NUMTYPES] = { @@ -1912,6 +1914,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { "CDPropFloat3", "CDPropFloat2", "CDPropBoolean", + "CDHairLength", }; const CustomData_MeshMasks CD_MASK_BAREMESH = { diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 49780abc6f4..ea60dd0753e 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -255,7 +255,7 @@ static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata, { DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( vedata, sldata, ob, material, true); - DRW_shgroup_hair_create_sub(ob, psys, md, grp); + DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL); } void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata, diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 9ecb737192e..5f45e2184aa 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -769,15 +769,15 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata, EeveeMaterialCache matcache = eevee_material_cache_get(vedata, sldata, ob, matnr - 1, true); if (matcache.depth_grp) { - *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp); + *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp, NULL); DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat); } if (matcache.shading_grp) { - *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp); + *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp, matcache.shading_gpumat); DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat); } if (matcache.shadow_grp) { - *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp); + *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp, NULL); DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat); *cast_shadow = true; } diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index 2e200c8e053..e8c2514d908 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -269,7 +269,7 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV]; GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT]; - grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp); + grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL); DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]); DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]); DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]); diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 635aa7cef25..a5281427fa8 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -238,7 +238,7 @@ static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd, workbench_image_hair_setup(wpd, ob, matnr, ima, NULL, state) : workbench_material_hair_setup(wpd, ob, matnr, color_type); - DRW_shgroup_hair_create_sub(ob, psys, md, grp); + DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL); } /** diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 0804745fab5..dc8f382b7f8 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -342,6 +342,9 @@ static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers, case CD_ORCO: *cd_layers |= CD_MASK_ORCO; break; + case CD_HAIRLENGTH: + *cd_layers |= CD_MASK_HAIRLENGTH; + break; } } } diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c index 6424b21666d..82af32c4c9f 100644 --- a/source/blender/draw/intern/draw_cache_impl_hair.c +++ b/source/blender/draw/intern/draw_cache_impl_hair.c @@ -30,6 +30,7 @@ #include "BLI_math_base.h" #include "BLI_math_vector.h" #include "BLI_utildefines.h" +#include "BLI_listbase.h" #include "DNA_hair_types.h" #include "DNA_object_types.h" @@ -38,6 +39,7 @@ #include "GPU_batch.h" #include "GPU_texture.h" +#include "GPU_material.h" #include "draw_cache_impl.h" /* own include */ #include "draw_hair_private.h" /* own include */ @@ -141,7 +143,7 @@ static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache) } } -static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *attr_step) +static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *attr_step, GPUVertBufRaw *length_step) { /* TODO: use hair radius layer if available. */ HairCurve *curve = hair->curves; @@ -162,6 +164,8 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a seg_data[3] = total_len; co_prev = curve_co[j]; } + /* Assign length value*/ + *(float *)GPU_vertbuf_raw_step(length_step) = total_len; if (total_len > 0.0f) { /* Divide by total length to have a [0-1] number. */ for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) { @@ -171,28 +175,48 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a } } -static void hair_batch_cache_ensure_procedural_pos(Hair *hair, ParticleHairCache *cache) +static void hair_batch_cache_ensure_procedural_pos(Hair *hair, + ParticleHairCache *cache, + GPUMaterial *gpu_material) { - if (cache->proc_point_buf != NULL) { - return; + if (cache->proc_point_buf == NULL) { + /* initialize vertex format */ + GPUVertFormat format = {0}; + uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + cache->proc_point_buf = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len); + + GPUVertBufRaw point_step; + GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &point_step); + + GPUVertFormat length_format = {0}; + uint length_id = GPU_vertformat_attr_add( + &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + + cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format); + GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len); + + GPUVertBufRaw length_step; + GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step); + + hair_batch_cache_fill_segments_proc_pos(hair, &point_step, &length_step); + + /* Create vbo immediately to bind to texture buffer. */ + GPU_vertbuf_use(cache->proc_point_buf); + cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf); } - /* initialize vertex format */ - GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - cache->proc_point_buf = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len); - - GPUVertBufRaw pos_step; - GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step); - - hair_batch_cache_fill_segments_proc_pos(hair, &pos_step); - - /* Create vbo immediately to bind to texture buffer. */ - GPU_vertbuf_use(cache->proc_point_buf); - - cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf); + if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex) { + ListBase gpu_attrs = GPU_material_attributes(gpu_material); + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) { + if (attr->type == CD_HAIRLENGTH) { + GPU_vertbuf_use(cache->proc_length_buf); + cache->length_tex = GPU_texture_create_from_vertbuf("hair_length", cache->proc_length_buf); + break; + } + } + } } static void hair_batch_cache_fill_strands_data(Hair *hair, @@ -310,6 +334,7 @@ static void hair_batch_cache_ensure_procedural_indices(Hair *hair, /* Ensure all textures and buffers needed for GPU accelerated drawing. */ bool hair_ensure_procedural_data(Object *object, ParticleHairCache **r_hair_cache, + GPUMaterial *gpu_material, int subdiv, int thickness_res) { @@ -325,7 +350,7 @@ bool hair_ensure_procedural_data(Object *object, /* Refreshed on combing and simulation. */ if ((*r_hair_cache)->proc_point_buf == NULL) { ensure_seg_pt_count(hair, &cache->hair); - hair_batch_cache_ensure_procedural_pos(hair, &cache->hair); + hair_batch_cache_ensure_procedural_pos(hair, &cache->hair, gpu_material); need_ft_update = true; } diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index 5c51f24a435..387c43741f7 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -46,6 +46,7 @@ #include "ED_particle.h" #include "GPU_batch.h" +#include "GPU_material.h" #include "DEG_depsgraph_query.h" @@ -183,7 +184,9 @@ void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache) { /* TODO: more granular update tagging. */ GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf); + GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_length_buf); DRW_TEXTURE_FREE_SAFE(hair_cache->point_tex); + DRW_TEXTURE_FREE_SAFE(hair_cache->length_tex); GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_buf); GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_seg_buf); @@ -609,7 +612,8 @@ static int particle_batch_cache_fill_segments(ParticleSystem *psys, static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_cache, const int num_path_keys, - GPUVertBufRaw *attr_step) + GPUVertBufRaw *attr_step, + GPUVertBufRaw *length_step) { for (int i = 0; i < num_path_keys; i++) { ParticleCacheKey *path = path_cache[i]; @@ -630,6 +634,8 @@ static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_ seg_data[3] = total_len; co_prev = path[j].co; } + /* Assign length value*/ + *(float *)GPU_vertbuf_raw_step(length_step) = total_len; if (total_len > 0.0f) { /* Divide by total length to have a [0-1] number. */ for (int j = 0; j <= path->segments; j++, seg_data_first += 4) { @@ -1079,40 +1085,64 @@ static void particle_batch_cache_ensure_procedural_indices(PTCacheEdit *edit, static void particle_batch_cache_ensure_procedural_pos(PTCacheEdit *edit, ParticleSystem *psys, - ParticleHairCache *cache) + ParticleHairCache *cache, + GPUMaterial *gpu_material) { - if (cache->proc_point_buf != NULL) { - return; - } + if (cache->proc_point_buf == NULL) { + /* initialize vertex format */ + GPUVertFormat pos_format = {0}; + uint pos_id = GPU_vertformat_attr_add( + &pos_format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - /* initialize vertex format */ - GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + cache->proc_point_buf = GPU_vertbuf_create_with_format(&pos_format); + GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len); - cache->proc_point_buf = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len); + GPUVertBufRaw pos_step; + GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step); - GPUVertBufRaw pos_step; - GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step); + GPUVertFormat length_format = {0}; + uint length_id = GPU_vertformat_attr_add( + &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - if (edit != NULL && edit->pathcache != NULL) { - particle_batch_cache_fill_segments_proc_pos(edit->pathcache, edit->totcached, &pos_step); - } - else { - if ((psys->pathcache != NULL) && - (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) { - particle_batch_cache_fill_segments_proc_pos(psys->pathcache, psys->totpart, &pos_step); + cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format); + GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len); + + GPUVertBufRaw length_step; + GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step); + + if (edit != NULL && edit->pathcache != NULL) { + particle_batch_cache_fill_segments_proc_pos( + edit->pathcache, edit->totcached, &pos_step, &length_step); } - if (psys->childcache) { - const int child_count = psys->totchild * psys->part->disp / 100; - particle_batch_cache_fill_segments_proc_pos(psys->childcache, child_count, &pos_step); + else { + if ((psys->pathcache != NULL) && + (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) { + particle_batch_cache_fill_segments_proc_pos( + psys->pathcache, psys->totpart, &pos_step, &length_step); + } + if (psys->childcache) { + const int child_count = psys->totchild * psys->part->disp / 100; + particle_batch_cache_fill_segments_proc_pos( + psys->childcache, child_count, &pos_step, &length_step); + } + } + + /* Create vbo immediately to bind to texture buffer. */ + GPU_vertbuf_use(cache->proc_point_buf); + cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf); + } + + /* Checking hair length seperatly, only allocating gpu memory when needed */ + if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex == NULL) { + ListBase gpu_attrs = GPU_material_attributes(gpu_material); + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) { + if (attr->type == CD_HAIRLENGTH) { + GPU_vertbuf_use(cache->proc_length_buf); + cache->length_tex = GPU_texture_create_from_vertbuf("hair_length", cache->proc_length_buf); + break; + } } } - - /* Create vbo immediately to bind to texture buffer. */ - GPU_vertbuf_use(cache->proc_point_buf); - - cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf); } static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, @@ -1649,6 +1679,7 @@ bool particles_ensure_procedural_data(Object *object, ParticleSystem *psys, ModifierData *md, ParticleHairCache **r_hair_cache, + GPUMaterial *gpu_material, int subdiv, int thickness_res) { @@ -1666,9 +1697,9 @@ bool particles_ensure_procedural_data(Object *object, (*r_hair_cache)->final[subdiv].strands_res = 1 << (part->draw_step + subdiv); /* Refreshed on combing and simulation. */ - if ((*r_hair_cache)->proc_point_buf == NULL) { + if ((*r_hair_cache)->proc_point_buf == NULL || (gpu_material && (*r_hair_cache)->length_tex == NULL)) { ensure_seg_pt_count(source.edit, source.psys, &cache->hair); - particle_batch_cache_ensure_procedural_pos(source.edit, source.psys, &cache->hair); + particle_batch_cache_ensure_procedural_pos(source.edit, source.psys, &cache->hair, gpu_material); need_ft_update = true; } diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 1eaf2bee236..2913877c9c1 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -29,6 +29,7 @@ struct Object; struct ParticleSystem; struct RegionView3D; struct ViewLayer; +struct GPUMaterial; #define UBO_FIRST_COLOR colorWire #define UBO_LAST_COLOR colorUVShadow @@ -175,7 +176,8 @@ bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis); struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md, - struct DRWShadingGroup *shgrp); + struct DRWShadingGroup *shgrp, + struct GPUMaterial *gpu_material); struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md); diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index c2e25389091..75bef12285b 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -41,6 +41,7 @@ #include "GPU_shader.h" #include "GPU_texture.h" #include "GPU_vertex_buffer.h" +#include "GPU_material.h" #include "draw_hair_private.h" #include "draw_shader.h" @@ -173,17 +174,17 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache } static ParticleHairCache *drw_hair_particle_cache_get( - Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res) + Object *object, ParticleSystem *psys, ModifierData *md, GPUMaterial* gpu_material, int subdiv, int thickness_res) { bool update; ParticleHairCache *cache; if (psys) { /* Old particle hair. */ - update = particles_ensure_procedural_data(object, psys, md, &cache, subdiv, thickness_res); + update = particles_ensure_procedural_data(object, psys, md, &cache, gpu_material, subdiv, thickness_res); } else { /* New hair object. */ - update = hair_ensure_procedural_data(object, &cache, subdiv, thickness_res); + update = hair_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res); } if (update) { @@ -206,7 +207,7 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi int subdiv = scene->r.hair_subdiv; int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; - ParticleHairCache *cache = drw_hair_particle_cache_get(object, psys, md, subdiv, thickness_res); + ParticleHairCache *cache = drw_hair_particle_cache_get(object, psys, md, NULL, subdiv, thickness_res); return cache->final[subdiv].proc_buf; } @@ -248,7 +249,8 @@ void DRW_hair_duplimat_get(Object *object, DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, ParticleSystem *psys, ModifierData *md, - DRWShadingGroup *shgrp_parent) + DRWShadingGroup *shgrp_parent, + GPUMaterial* gpu_material) { const DRWContextState *draw_ctx = DRW_context_state_get(); Scene *scene = draw_ctx->scene; @@ -258,7 +260,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; ParticleHairCache *hair_cache = drw_hair_particle_cache_get( - object, psys, md, subdiv, thickness_res); + object, psys, md, gpu_material, subdiv, thickness_res); DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent); @@ -308,6 +310,8 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, } DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex); + if (hair_cache->length_tex) + DRW_shgroup_uniform_texture(shgrp, "hairLen", hair_cache->length_tex); DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1); DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res); DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape); diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h index 1f58d8d0ead..289a1690fc6 100644 --- a/source/blender/draw/intern/draw_hair_private.h +++ b/source/blender/draw/intern/draw_hair_private.h @@ -66,6 +66,10 @@ typedef struct ParticleHairCache { GPUVertBuf *proc_strand_buf; GPUTexture *strand_tex; + /* Hair Length */ + GPUVertBuf *proc_length_buf; + GPUTexture *length_tex; + GPUVertBuf *proc_strand_seg_buf; GPUTexture *strand_seg_tex; @@ -93,11 +97,13 @@ bool particles_ensure_procedural_data(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md, struct ParticleHairCache **r_hair_cache, + struct GPUMaterial *gpu_material, int subdiv, int thickness_res); bool hair_ensure_procedural_data(struct Object *object, struct ParticleHairCache **r_hair_cache, + struct GPUMaterial *gpu_material, int subdiv, int thickness_res); diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index 02c335ddae2..6cc7f09a852 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -210,6 +210,12 @@ void hair_get_pos_tan_binor_time(bool is_persp, } } +float hair_get_customdata_float(const samplerBuffer cd_buf) +{ + int id = hair_get_strand_id(); + return texelFetch(cd_buf, id).r; +} + vec2 hair_get_customdata_vec2(const samplerBuffer cd_buf) { int id = hair_get_strand_id(); diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index bb1ebc0e85d..bbe939b546b 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -656,6 +656,8 @@ static const char *attr_prefix_get(CustomDataType type) return "c"; case CD_AUTO_FROM_NAME: return "a"; + case CD_HAIRLENGTH: + return "hl"; default: BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!"); return ""; @@ -675,7 +677,12 @@ static char *code_generate_interface(GPUNodeGraph *graph, int builtins) BLI_dynstr_append(ds, "\n"); LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); + if (attr->type == CD_HAIRLENGTH) { + BLI_dynstr_appendf(ds, "float var%d;\n", attr->id); + } + else { + BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); + } } if (builtins & GPU_BARYCENTRIC_TEXCO) { BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n"); @@ -711,6 +718,11 @@ static char *code_generate_vertex(GPUNodeGraph *graph, BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); } + if (attr->type == CD_HAIRLENGTH) + { + BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); + BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n"); + } else if (attr->name[0] == '\0') { BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix); BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix); @@ -755,6 +767,10 @@ static char *code_generate_vertex(GPUNodeGraph *graph, BLI_dynstr_appendf( ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id); } + else if (attr->type == CD_HAIRLENGTH) { + BLI_dynstr_appendf( + ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id); + } else { const char *type_str = gpu_data_type_to_string(attr->gputype); BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id); diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl index f7bf3d33361..193a4190cbf 100644 --- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -42,6 +42,11 @@ vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], const sampler return orco_madd[0].xyz + orco * orco_madd[1].xyz; } +float hair_len_get(int id, const samplerBuffer len) +{ + return texelFetch(len, id).x; +} + vec4 tangent_get(const samplerBuffer attr, mat3 normalmat) { /* Unsupported */ @@ -71,6 +76,11 @@ vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], vec4 orco) } } +float hair_len_get(int id, const float len) +{ + return len; +} + vec4 tangent_get(vec4 attr, mat3 normalmat) { vec4 tangent; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index 6330daa4391..6ffa6b59572 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -10,12 +10,15 @@ float wang_hash_noise(uint s) return fract(float(s) / 4294967296.0); } -void node_hair_info(out float is_strand, +void node_hair_info(float hair_length, + out float is_strand, out float intercept, + out float length, out float thickness, out vec3 tangent, out float random) { + length = hair_length; #ifdef HAIR_SHADER is_strand = 1.0; intercept = hairTime; diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 6acea8da15f..36bdd4915ff 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -84,7 +84,8 @@ typedef struct CustomData { * MUST be >= CD_NUMTYPES, but we can't use a define here. * Correct size is ensured in CustomData_update_typemap assert(). */ - int typemap[51]; + int typemap[52]; + char _pad[4]; /** Number of layers, size of layers array. */ int totlayer, maxlayer; /** In editmode, total size of all data layers. */ @@ -166,7 +167,9 @@ typedef enum CustomDataType { CD_PROP_BOOL = 50, - CD_NUMTYPES = 51, + CD_HAIRLENGTH = 51, + + CD_NUMTYPES = 52, } CustomDataType; /* Bits for CustomDataMask */ @@ -220,6 +223,8 @@ typedef enum CustomDataType { #define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2) #define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL) +#define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH) + /** Multires loop data. */ #define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK) diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.c b/source/blender/nodes/shader/nodes/node_shader_hair_info.c index 843185befb6..559c5cf7ae8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_hair_info.c +++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.c @@ -22,6 +22,7 @@ static bNodeSocketTemplate outputs[] = { {SOCK_FLOAT, N_("Is Strand"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_FLOAT, N_("Intercept"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + {SOCK_FLOAT, N_("Length"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_FLOAT, N_("Thickness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_VECTOR, N_("Tangent Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, // { SOCK_FLOAT, 0, N_("Fade"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, @@ -35,7 +36,11 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - return GPU_stack_link(mat, node, "node_hair_info", in, out); + /* Length: don't request length if not needed. */ + const static float zero = 0; + GPUNodeLink *length_link = (!out[2].hasoutput) ? GPU_constant(&zero) : + GPU_attribute(mat, CD_HAIRLENGTH, ""); + return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link); } /* node type definition */ From fc7beac8d6f45d9eca344ec4ae8879c2e73f0731 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 24 Sep 2021 08:39:58 +0200 Subject: [PATCH 0175/1500] FileBrowser: Reduce Overhead Browsing Libraries. When Browsing libraries the asset files were opened multiple times. once to determine the needed groups to query and once for each group to query the items in the group. For file browsing this makes sense but for asset browsing this can be reduced. This patch will load the asset files recursively and only opens them once. Another change is that only the assets are requested and not filtered out later in the process. This patch is needed to simplify the library indexing. Where we need access to the full library content. ## The numbers ## Benchmarked by adding scenes of the spring open movie to the default asset library. Refreshing the asset library would recursively load all the files there. | **8bc27c508a** | Processed 317 'directories/libraries' | 7.573986s | | **Patch** | Processed 42 'directories/libraries' | 0.821013s | {F10442811} Reviewed By: mont29, Severin Maniphest Tasks: T91406 Differential Revision: https://developer.blender.org/D12499 --- source/blender/blenloader/BLO_readfile.h | 5 +- .../blender/blenloader/intern/readblenentry.c | 33 ++- source/blender/editors/space_file/filelist.c | 256 ++++++++++++------ 3 files changed, 196 insertions(+), 98 deletions(-) diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index c8615545df9..4b4311d32bf 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -168,9 +168,8 @@ struct LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, const bool use_assets_only, int *r_tot_names); -struct LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, - int ofblocktype, - int *r_tot_info_items); +struct LinkNode * /*BLODataBlockInfo */ BLO_blendhandle_get_datablock_info( + BlendHandle *bh, int ofblocktype, const bool use_assets_only, int *r_tot_info_items); struct LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *r_tot_prev); struct PreviewImage *BLO_blendhandle_get_preview_for_id(BlendHandle *bh, int ofblocktype, diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index f88b470809c..3306eb9e454 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -170,17 +170,19 @@ LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, } /** - * Gets the names and asset-data (if ID is an asset) of all the data-blocks in a file of a certain - * type (e.g. all the scene names in a file). + * Gets the names and asset-data (if ID is an asset) of data-blocks in a file of a certain type. + * The data-blocks can be limited to assets. * * \param bh: The blendhandle to access. * \param ofblocktype: The type of names to get. + * \param use_assets_only: Limit the result to assets only. * \param tot_info_items: The length of the returned list. * \return A BLI_linklist of BLODataBlockInfo *. The links and #BLODataBlockInfo.asset_data should * be freed with MEM_freeN. */ LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, int ofblocktype, + const bool use_assets_only, int *r_tot_info_items) { FileData *fd = (FileData *)bh; @@ -189,27 +191,34 @@ LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, int tot = 0; for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == ENDB) { + break; + } if (bhead->code == ofblocktype) { - struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__); const char *name = blo_bhead_id_name(fd, bhead) + 2; + AssetMetaData *asset_meta_data = blo_bhead_id_asset_data_address(fd, bhead); - STRNCPY(info->name, name); + const bool is_asset = asset_meta_data != NULL; + const bool skip_datablock = use_assets_only && !is_asset; + if (skip_datablock) { + continue; + } + struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__); /* Lastly, read asset data from the following blocks. */ - info->asset_data = blo_bhead_id_asset_data_address(fd, bhead); - if (info->asset_data) { - bhead = blo_read_asset_data_block(fd, bhead, &info->asset_data); - /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the next - * non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */ + if (asset_meta_data) { + bhead = blo_read_asset_data_block(fd, bhead, &asset_meta_data); + /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the + * next non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */ bhead = blo_bhead_prev(fd, bhead); } + STRNCPY(info->name, name); + info->asset_data = asset_meta_data; + BLI_linklist_prepend(&infos, info); tot++; } - else if (bhead->code == ENDB) { - break; - } } *r_tot_info_items = tot; diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 4f881184990..5c0976e18f2 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -2888,76 +2888,129 @@ static int filelist_readjob_list_dir(const char *root, return nbr_entries; } -static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar) +typedef enum ListLibOptions { + /* Will read both the groups + actual ids from the library. Reduces the amount of times that + * a library needs to be opened. */ + LIST_LIB_RECURSIVE = (1 << 0), + + /* Will only list assets. */ + LIST_LIB_ASSETS_ONLY = (1 << 1), + + /* Add given root as result. */ + LIST_LIB_ADD_PARENT = (1 << 2), +} ListLibOptions; + +static FileListInternEntry *filelist_readjob_list_lib_group_create(const int idcode, + const char *group_name) { - FileListInternEntry *entry; - LinkNode *ln, *names = NULL, *datablock_infos = NULL; - int i, nitems, idcode = 0, nbr_entries = 0; - char dir[FILE_MAX_LIBEXTRA], *group; - bool ok; + FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__); + entry->relpath = BLI_strdup(group_name); + entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR; + entry->blentype = idcode; + return entry; +} - struct BlendHandle *libfiledata = NULL; - - /* name test */ - ok = BLO_library_path_explode(root, dir, &group, NULL); - if (!ok) { - return nbr_entries; - } - - /* there we go */ - BlendFileReadReport bf_reports = {.reports = NULL}; - libfiledata = BLO_blendhandle_from_file(dir, &bf_reports); - if (libfiledata == NULL) { - return nbr_entries; - } - - /* memory for strings is passed into filelist[i].entry->relpath - * and freed in filelist_entry_free. */ - if (group) { - idcode = groupname_to_code(group); - datablock_infos = BLO_blendhandle_get_datablock_info(libfiledata, idcode, &nitems); - } - else { - names = BLO_blendhandle_get_linkable_groups(libfiledata); - nitems = BLI_linklist_count(names); - } - - BLO_blendhandle_close(libfiledata); - - if (!skip_currpar) { - entry = MEM_callocN(sizeof(*entry), __func__); - entry->relpath = BLI_strdup(FILENAME_PARENT); - entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR); - BLI_addtail(entries, entry); - nbr_entries++; - } - - for (i = 0, ln = (datablock_infos ? datablock_infos : names); i < nitems; i++, ln = ln->next) { - struct BLODataBlockInfo *info = datablock_infos ? ln->link : NULL; - const char *blockname = info ? info->name : ln->link; - - entry = MEM_callocN(sizeof(*entry), __func__); - entry->relpath = BLI_strdup(blockname); +static void filelist_readjob_list_lib_add_datablocks(ListBase *entries, + LinkNode *datablock_infos, + const bool prefix_relpath_with_group_name, + const int idcode, + const char *group_name) +{ + for (LinkNode *ln = datablock_infos; ln; ln = ln->next) { + struct BLODataBlockInfo *info = ln->link; + FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__); + if (prefix_relpath_with_group_name) { + entry->relpath = BLI_sprintfN("%s/%s", group_name, info->name); + } + else { + entry->relpath = BLI_strdup(info->name); + } entry->typeflag |= FILE_TYPE_BLENDERLIB; if (info && info->asset_data) { entry->typeflag |= FILE_TYPE_ASSET; /* Moves ownership! */ entry->imported_asset_data = info->asset_data; } - if (!(group && idcode)) { - entry->typeflag |= FILE_TYPE_DIR; - entry->blentype = groupname_to_code(blockname); - } - else { - entry->blentype = idcode; - } + entry->blentype = idcode; BLI_addtail(entries, entry); - nbr_entries++; + } +} + +static int filelist_readjob_list_lib(const char *root, + ListBase *entries, + const ListLibOptions options) +{ + char dir[FILE_MAX_LIBEXTRA], *group; + + struct BlendHandle *libfiledata = NULL; + + /* Check if the given root is actually a library. All folders are passed to + * `filelist_readjob_list_lib` and based on the number of found entries `filelist_readjob_do` + * will do a dir listing only when this function does not return any entries. */ + /* TODO: We should consider introducing its own function to detect if it is a lib and + * call it directly from `filelist_readjob_do` to increase readability. */ + const bool is_lib = BLO_library_path_explode(root, dir, &group, NULL); + if (!is_lib) { + return 0; } - BLI_linklist_freeN(datablock_infos ? datablock_infos : names); + /* Open the library file. */ + BlendFileReadReport bf_reports = {.reports = NULL}; + libfiledata = BLO_blendhandle_from_file(dir, &bf_reports); + if (libfiledata == NULL) { + return 0; + } - return nbr_entries; + /* Add current parent when requested. */ + int parent_len = 0; + if (options & LIST_LIB_ADD_PARENT) { + FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__); + entry->relpath = BLI_strdup(FILENAME_PARENT); + entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR); + BLI_addtail(entries, entry); + parent_len = 1; + } + + int group_len = 0; + int datablock_len = 0; + const bool group_came_from_path = group != NULL; + if (group_came_from_path) { + const int idcode = groupname_to_code(group); + LinkNode *datablock_infos = BLO_blendhandle_get_datablock_info( + libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &datablock_len); + filelist_readjob_list_lib_add_datablocks(entries, datablock_infos, false, idcode, group); + BLI_linklist_freeN(datablock_infos); + } + else { + LinkNode *groups = BLO_blendhandle_get_linkable_groups(libfiledata); + group_len = BLI_linklist_count(groups); + + for (LinkNode *ln = groups; ln; ln = ln->next) { + const char *group_name = ln->link; + const int idcode = groupname_to_code(group_name); + FileListInternEntry *group_entry = filelist_readjob_list_lib_group_create(idcode, + group_name); + BLI_addtail(entries, group_entry); + + if (options & LIST_LIB_RECURSIVE) { + int group_datablock_len; + LinkNode *group_datablock_infos = BLO_blendhandle_get_datablock_info( + libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &group_datablock_len); + filelist_readjob_list_lib_add_datablocks( + entries, group_datablock_infos, true, idcode, group_name); + BLI_linklist_freeN(group_datablock_infos); + datablock_len += group_datablock_len; + } + } + + BLI_linklist_freeN(groups); + } + + BLO_blendhandle_close(libfiledata); + + /* Return the number of items added to entries. */ + int added_entries_len = group_len + datablock_len + parent_len; + return added_entries_len; } #if 0 @@ -3153,6 +3206,35 @@ typedef struct FileListReadJob { struct FileList *tmp_filelist; } FileListReadJob; +static bool filelist_readjob_should_recurse_into_entry(const int max_recursion, + const int current_recursion_level, + FileListInternEntry *entry) +{ + if (max_recursion == 0) { + /* Recursive loading is disabled. */ + return false; + } + if (current_recursion_level >= max_recursion) { + /* No more levels of recursion left. */ + return false; + } + if (entry->typeflag & FILE_TYPE_BLENDERLIB) { + /* Libraries are already loaded recursively when recursive loaded is used. No need to add + * them another time. This loading is done with the `LIST_LIB_RECURSIVE` option. */ + return false; + } + if (!(entry->typeflag & FILE_TYPE_DIR)) { + /* Cannot recurse into regular file entries. */ + return false; + } + if (FILENAME_IS_CURRPAR(entry->relpath)) { + /* Don't schedule go to parent entry, (`..`) */ + return false; + } + + return true; +} + static void filelist_readjob_do(const bool do_lib, FileListReadJob *job_params, const short *stop, @@ -3189,7 +3271,6 @@ static void filelist_readjob_do(const bool do_lib, while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) { FileListInternEntry *entry; int nbr_entries = 0; - bool is_lib = do_lib; char *subdir; char rel_subdir[FILE_MAX_LIBEXTRA]; @@ -3212,45 +3293,54 @@ static void filelist_readjob_do(const bool do_lib, BLI_path_normalize_dir(root, rel_subdir); BLI_path_rel(rel_subdir, root); + bool is_lib = false; if (do_lib) { - nbr_entries = filelist_readjob_list_lib(subdir, &entries, skip_currpar); + ListLibOptions list_lib_options = 0; + if (!skip_currpar) { + list_lib_options |= LIST_LIB_ADD_PARENT; + } + + /* Libraries are loaded recursively when max_recursion is set. It doesn't check if there is + * still a recursion level over. */ + if (max_recursion > 0) { + list_lib_options |= LIST_LIB_RECURSIVE; + } + /* Only load assets when browsing an asset library. For normal file browsing we return all + * entries. `FLF_ASSETS_ONLY` filter can be enabled/disabled by the user.*/ + if (filelist->asset_library_ref) { + list_lib_options |= LIST_LIB_ASSETS_ONLY; + } + nbr_entries = filelist_readjob_list_lib(subdir, &entries, list_lib_options); + if (nbr_entries > 0) { + is_lib = true; + } } - if (!nbr_entries) { - is_lib = false; + + if (!is_lib) { nbr_entries = filelist_readjob_list_dir( subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar); } for (entry = entries.first; entry; entry = entry->next) { - BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath); - entry->uid = filelist_uid_generate(filelist); - /* Only thing we change in direntry here, so we need to free it first. */ + /* When loading entries recursive, the rel_path should be relative from the root dir. + * we combine the relative path to the subdir with the relative path of the entry. */ + BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath); MEM_freeN(entry->relpath); entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//' * added by BLI_path_rel to rel_subdir. */ entry->name = fileentry_uiname(root, entry->relpath, entry->typeflag, dir); entry->free_name = true; - /* Here we decide whether current filedirentry is to be listed too, or not. */ - if (max_recursion && (is_lib || (recursion_level <= max_recursion))) { - if (((entry->typeflag & FILE_TYPE_DIR) == 0) || FILENAME_IS_CURRPAR(entry->relpath)) { - /* Skip... */ - } - else if (!is_lib && (recursion_level >= max_recursion) && - ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) { - /* Do not recurse in real directories in this case, only in .blend libs. */ - } - else { - /* We have a directory we want to list, add it to todo list! */ - BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath); - BLI_path_normalize_dir(job_params->main_name, dir); - td_dir = BLI_stack_push_r(todo_dirs); - td_dir->level = recursion_level + 1; - td_dir->dir = BLI_strdup(dir); - nbr_todo_dirs++; - } + if (filelist_readjob_should_recurse_into_entry(max_recursion, recursion_level, entry)) { + /* We have a directory we want to list, add it to todo list! */ + BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath); + BLI_path_normalize_dir(job_params->main_name, dir); + td_dir = BLI_stack_push_r(todo_dirs); + td_dir->level = recursion_level + 1; + td_dir->dir = BLI_strdup(dir); + nbr_todo_dirs++; } } From e161f39660fb50cf824bf8f674ef5ac9cc635ca6 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Sep 2021 10:25:16 +0200 Subject: [PATCH 0176/1500] Cleanup: clang-tidy --- source/blender/editors/interface/tree_view.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index c2ecab08ace..ee50126f974 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -57,7 +57,7 @@ AbstractTreeViewItem &TreeViewItemContainer::add_tree_item( void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptions options) const { - for (auto &child : children_) { + for (const auto &child : children_) { iter_fn(*child); if (bool(options & IterOptions::SkipCollapsed) && child->is_collapsed()) { continue; From 9a45a4c52597fbdc1cc5871329fdbc4db3b4357f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 24 Sep 2021 10:20:59 +0200 Subject: [PATCH 0177/1500] Cleanup: asset catalogs, fix clang-tidy warning Remove unnecessary call to `std::string::c_str()`. No functional changes. --- source/blender/blenkernel/intern/asset_catalog_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 0f389999d6d..9110b2cb0ba 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -289,7 +289,7 @@ TEST_F(AssetCatalogTest, create_catalog_after_loading_file) << "expecting newly added catalog to not yet be saved to " << temp_lib_root; /* Write and reload the catalog file. */ - service.write_to_disk(temp_lib_root.c_str()); + service.write_to_disk(temp_lib_root); AssetCatalogService reloaded_service(temp_lib_root); reloaded_service.load_from_disk(); EXPECT_NE(nullptr, reloaded_service.find_catalog(UUID_POSES_ELLIE)) From 3ac342dc6d8024024de095dadb7b2a1e7b53583d Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 24 Sep 2021 10:45:36 +0200 Subject: [PATCH 0178/1500] Cleanup: clang format --- intern/cycles/blender/blender_curves.cpp | 9 +++------ intern/cycles/render/nodes.cpp | 2 +- .../blender/blenloader/intern/versioning_300.c | 5 +++-- .../draw/engines/eevee/eevee_materials.c | 3 ++- .../blender/draw/intern/draw_cache_impl_hair.c | 10 ++++++---- .../draw/intern/draw_cache_impl_particles.c | 6 ++++-- source/blender/draw/intern/draw_hair.c | 18 ++++++++++++------ source/blender/gpu/intern/gpu_codegen.c | 6 ++---- 8 files changed, 33 insertions(+), 26 deletions(-) diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp index c7851e40543..b6b4f206620 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/blender_curves.cpp @@ -339,8 +339,7 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa num_curve_keys++; } - if (attr_length != NULL) - { + if (attr_length != NULL) { attr_length->add(CData->curve_length[curve]); } @@ -671,8 +670,7 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) { attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT); } - if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) - { + if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) { attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH); } if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) { @@ -727,8 +725,7 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) } } - if (attr_length) - { + if (attr_length) { attr_length->add(length); } diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index e5071c25730..90f70cf19ec 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -4391,7 +4391,7 @@ void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) if (!intercept_out->links.empty()) attributes->add(ATTR_STD_CURVE_INTERCEPT); - if (!output("Length")->links.empty()) + if (!output("Length")->links.empty()) attributes->add(ATTR_STD_CURVE_LENGTH); if (!output("Random")->links.empty()) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 0d333ac2edc..88df4f73d45 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -536,8 +536,9 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { - if (brush->clone.image != NULL && ELEM(brush->clone.image->type, IMA_TYPE_R_RESULT, - IMA_TYPE_COMPOSITE)) { brush->clone.image = NULL; + if (brush->clone.image != NULL && + ELEM(brush->clone.image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + brush->clone.image = NULL; } } } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 5f45e2184aa..a627bcd9488 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -773,7 +773,8 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata, DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat); } if (matcache.shading_grp) { - *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp, matcache.shading_gpumat); + *matcache.shading_grp_p = DRW_shgroup_hair_create_sub( + ob, psys, md, matcache.shading_grp, matcache.shading_gpumat); DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat); } if (matcache.shadow_grp) { diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c index 82af32c4c9f..41a0cca8a8f 100644 --- a/source/blender/draw/intern/draw_cache_impl_hair.c +++ b/source/blender/draw/intern/draw_cache_impl_hair.c @@ -27,10 +27,10 @@ #include "MEM_guardedalloc.h" +#include "BLI_listbase.h" #include "BLI_math_base.h" #include "BLI_math_vector.h" #include "BLI_utildefines.h" -#include "BLI_listbase.h" #include "DNA_hair_types.h" #include "DNA_object_types.h" @@ -38,8 +38,8 @@ #include "BKE_hair.h" #include "GPU_batch.h" -#include "GPU_texture.h" #include "GPU_material.h" +#include "GPU_texture.h" #include "draw_cache_impl.h" /* own include */ #include "draw_hair_private.h" /* own include */ @@ -143,7 +143,9 @@ static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache) } } -static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *attr_step, GPUVertBufRaw *length_step) +static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, + GPUVertBufRaw *attr_step, + GPUVertBufRaw *length_step) { /* TODO: use hair radius layer if available. */ HairCurve *curve = hair->curves; @@ -165,7 +167,7 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a co_prev = curve_co[j]; } /* Assign length value*/ - *(float *)GPU_vertbuf_raw_step(length_step) = total_len; + *(float *)GPU_vertbuf_raw_step(length_step) = total_len; if (total_len > 0.0f) { /* Divide by total length to have a [0-1] number. */ for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) { diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index 387c43741f7..087d88f4b1f 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -1697,9 +1697,11 @@ bool particles_ensure_procedural_data(Object *object, (*r_hair_cache)->final[subdiv].strands_res = 1 << (part->draw_step + subdiv); /* Refreshed on combing and simulation. */ - if ((*r_hair_cache)->proc_point_buf == NULL || (gpu_material && (*r_hair_cache)->length_tex == NULL)) { + if ((*r_hair_cache)->proc_point_buf == NULL || + (gpu_material && (*r_hair_cache)->length_tex == NULL)) { ensure_seg_pt_count(source.edit, source.psys, &cache->hair); - particle_batch_cache_ensure_procedural_pos(source.edit, source.psys, &cache->hair, gpu_material); + particle_batch_cache_ensure_procedural_pos( + source.edit, source.psys, &cache->hair, gpu_material); need_ft_update = true; } diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index 75bef12285b..6a23eb8695e 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -38,10 +38,10 @@ #include "GPU_batch.h" #include "GPU_capabilities.h" #include "GPU_compute.h" +#include "GPU_material.h" #include "GPU_shader.h" #include "GPU_texture.h" #include "GPU_vertex_buffer.h" -#include "GPU_material.h" #include "draw_hair_private.h" #include "draw_shader.h" @@ -173,14 +173,19 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache } } -static ParticleHairCache *drw_hair_particle_cache_get( - Object *object, ParticleSystem *psys, ModifierData *md, GPUMaterial* gpu_material, int subdiv, int thickness_res) +static ParticleHairCache *drw_hair_particle_cache_get(Object *object, + ParticleSystem *psys, + ModifierData *md, + GPUMaterial *gpu_material, + int subdiv, + int thickness_res) { bool update; ParticleHairCache *cache; if (psys) { /* Old particle hair. */ - update = particles_ensure_procedural_data(object, psys, md, &cache, gpu_material, subdiv, thickness_res); + update = particles_ensure_procedural_data( + object, psys, md, &cache, gpu_material, subdiv, thickness_res); } else { /* New hair object. */ @@ -207,7 +212,8 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi int subdiv = scene->r.hair_subdiv; int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; - ParticleHairCache *cache = drw_hair_particle_cache_get(object, psys, md, NULL, subdiv, thickness_res); + ParticleHairCache *cache = drw_hair_particle_cache_get( + object, psys, md, NULL, subdiv, thickness_res); return cache->final[subdiv].proc_buf; } @@ -250,7 +256,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, ParticleSystem *psys, ModifierData *md, DRWShadingGroup *shgrp_parent, - GPUMaterial* gpu_material) + GPUMaterial *gpu_material) { const DRWContextState *draw_ctx = DRW_context_state_get(); Scene *scene = draw_ctx->scene; diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index bbe939b546b..6cc8d7d9d05 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -718,8 +718,7 @@ static char *code_generate_vertex(GPUNodeGraph *graph, BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); } - if (attr->type == CD_HAIRLENGTH) - { + if (attr->type == CD_HAIRLENGTH) { BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n"); } @@ -768,8 +767,7 @@ static char *code_generate_vertex(GPUNodeGraph *graph, ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id); } else if (attr->type == CD_HAIRLENGTH) { - BLI_dynstr_appendf( - ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id); + BLI_dynstr_appendf(ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id); } else { const char *type_str = gpu_data_type_to_string(attr->gputype); From f8a0e102cf5e33f92447fe6cd6db7838f6051aab Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 23 Sep 2021 15:48:32 +0200 Subject: [PATCH 0179/1500] Weightpaint Gradient tool: expose falloff to the UI By default, we'll always get a falloff like this from the tool: {F10559413} But in the context of using vertexgroups in modifiers/modeling, a choice on how the gradient falloff of the Weightpaint Gradient tool is shaped would be desirable: "real" linear: {F10559416} Custom: {F10559421} {F10559428} The way the Weightpaint gradient tool works is a bit outside the usual tools that use brushes [even though it creates a brush on the fly in `WPGradient_userData`]. However, it does not have an entry in `eBrushWeightPaintTool` and adding one there does not play nice for the same reasons (not "really" being integrated in the brush-based tools). So in order to expose the brush curve in the UI, we would have to do one of the following: - [1] try to use `VIEW3D_PT_tools_brush_falloff`, for this to work: -- make all kinds of exception in python super classes [`FalloffPanel`, `BrushPanel`, `UnifiedPaintPanel`, ... -- including making real entries in `eBrushWeightPaintTool`] to get a proper tool mode and... -- .. to also make sure Falloff Shape and Front-Face Falloff are not available [which the tool seems to just not support in its current form] - [2] just have a simple, contained panel for this tool alone This patch implements [2] and adds it as part of the ToolDef (could also be done in `VIEW3D_HT_tool_header`, but again, I think this is nice to keep separate from the usual tools) {F10559482} {F10559485} Testfile: {F10559442} Fixes T91636 Maniphest Tasks: T91636 Differential Revision: https://developer.blender.org/D12614 --- .../startup/bl_ui/space_toolsystem_toolbar.py | 1 + .../startup/bl_ui/space_view3d_toolbar.py | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index a4a51cb9910..7bdc3ab160f 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1691,6 +1691,7 @@ class _defs_weight_paint: props = tool.operator_properties("paint.weight_gradient") layout.prop(props, "type", expand=True) + layout.popover("VIEW3D_PT_tools_weight_gradient") return dict( idname="builtin.gradient", diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 16b5ed33f3f..acc3d933b85 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -688,6 +688,39 @@ class VIEW3D_PT_tools_brush_stroke_smooth_stroke(Panel, View3DPaintPanel, Smooth bl_options = {'DEFAULT_CLOSED'} +class VIEW3D_PT_tools_weight_gradient(Panel, View3DPaintPanel): + bl_context = ".weightpaint" # dot on purpose (access from topbar) + bl_label = "Falloff" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + settings = context.tool_settings.weight_paint + brush = settings.brush + return brush is not None + + def draw(self, context): + layout = self.layout + settings = context.tool_settings.weight_paint + brush = settings.brush + + col = layout.column(align=True) + row = col.row(align=True) + row.prop(brush, "curve_preset", text="") + + if brush.curve_preset == 'CUSTOM': + layout.template_curve_mapping(brush, "curve", brush=True) + + col = layout.column(align=True) + row = col.row(align=True) + row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' + row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' + row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' + row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' + row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' + row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' + + # TODO, move to space_view3d.py class VIEW3D_PT_tools_brush_falloff(Panel, View3DPaintPanel, FalloffPanel): bl_context = ".paint_common" # dot on purpose (access from topbar) @@ -2219,6 +2252,7 @@ classes = ( VIEW3D_PT_tools_brush_falloff_frontface, VIEW3D_PT_tools_brush_falloff_normal, VIEW3D_PT_tools_brush_display, + VIEW3D_PT_tools_weight_gradient, VIEW3D_PT_sculpt_dyntopo, VIEW3D_PT_sculpt_voxel_remesh, From 7ca48a38147fa3b8bf816316df4cc1034b099b19 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Sep 2021 10:30:13 +0200 Subject: [PATCH 0180/1500] Fix: incorrect socket shape for noise texture input --- source/blender/blenkernel/intern/node.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3f4e6281ca7..7fd681960cf 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4519,13 +4519,17 @@ static InputSocketFieldType get_interface_input_field_type(const NodeRef &node, /* Node declarations should be implemented for nodes involved here. */ BLI_assert(node_decl != nullptr); + /* Get the field type from the declaration. */ + const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()]; + const InputSocketFieldType field_type = socket_decl.input_field_type(); + if (field_type == InputSocketFieldType::Implicit) { + return field_type; + } if (node_decl->is_function_node()) { /* In a function node, every socket supports fields. */ return InputSocketFieldType::IsSupported; } - /* Get the field type from the declaration. */ - const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()]; - return socket_decl.input_field_type(); + return field_type; } static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node, From bc27bafa542d5a5f85b45b83676fe1ad21a3f926 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Sep 2021 10:54:11 +0200 Subject: [PATCH 0181/1500] BLI: make noise hash functions available in header --- source/blender/blenlib/BLI_noise.hh | 7 +++++++ source/blender/blenlib/intern/noise.cc | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh index 760ff082d06..b5a06b2b020 100644 --- a/source/blender/blenlib/BLI_noise.hh +++ b/source/blender/blenlib/BLI_noise.hh @@ -22,6 +22,13 @@ namespace blender::noise { +/* Create a randomized hash from the given inputs. Contrary to hash functions in `BLI_hash.hh` + * these functions produce better randomness but are more expensive to compute. */ +uint32_t hash(uint32_t kx); +uint32_t hash(uint32_t kx, uint32_t ky); +uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz); +uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw); + /* Perlin noise in the range [-1, 1]. */ float perlin_signed(float position); diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index c057c12e543..0f80fa0f294 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -109,7 +109,7 @@ BLI_INLINE void hash_bit_final(uint32_t &a, uint32_t &b, uint32_t &c) c -= hash_bit_rotate(b, 24); } -BLI_INLINE uint32_t hash(uint32_t kx) +uint32_t hash(uint32_t kx) { uint32_t a, b, c; a = b = c = 0xdeadbeef + (1 << 2) + 13; @@ -120,7 +120,7 @@ BLI_INLINE uint32_t hash(uint32_t kx) return c; } -BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky) +uint32_t hash(uint32_t kx, uint32_t ky) { uint32_t a, b, c; a = b = c = 0xdeadbeef + (2 << 2) + 13; @@ -132,7 +132,7 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky) return c; } -BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz) +uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz) { uint32_t a, b, c; a = b = c = 0xdeadbeef + (3 << 2) + 13; @@ -145,7 +145,7 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz) return c; } -BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) +uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) { uint32_t a, b, c; a = b = c = 0xdeadbeef + (4 << 2) + 13; From d8a5b768f0763dab725401460e229787d080476a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Sep 2021 11:24:15 +0200 Subject: [PATCH 0182/1500] BLI: expose more noise hash functions in header This is a follow up of the previous commit. These functions are useful for other areas of Blender as well. --- source/blender/blenlib/BLI_noise.hh | 37 +++++++-- source/blender/blenlib/intern/noise.cc | 110 +++++++++++++++---------- 2 files changed, 97 insertions(+), 50 deletions(-) diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh index b5a06b2b020..7e1655f7864 100644 --- a/source/blender/blenlib/BLI_noise.hh +++ b/source/blender/blenlib/BLI_noise.hh @@ -22,43 +22,66 @@ namespace blender::noise { -/* Create a randomized hash from the given inputs. Contrary to hash functions in `BLI_hash.hh` - * these functions produce better randomness but are more expensive to compute. */ +/* -------------------------------------------------------------------- + * Hash functions. + + * Create a randomized hash from the given inputs. Contrary to hash functions in `BLI_hash.hh` + * these functions produce better randomness but are more expensive to compute. + */ + +/* Hash integers to `uint32_t`. */ uint32_t hash(uint32_t kx); uint32_t hash(uint32_t kx, uint32_t ky); uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz); uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw); -/* Perlin noise in the range [-1, 1]. */ +/* Hash floats to `uint32_t`. */ +uint32_t hash_float(float kx); +uint32_t hash_float(float2 k); +uint32_t hash_float(float3 k); +uint32_t hash_float(float4 k); +/* Hash integers to `float` between 0 and 1. */ +float hash_to_float(uint32_t kx); +float hash_to_float(uint32_t kx, uint32_t ky); +float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz); +float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw); + +/* Hash floats to `float` between 0 and 1. */ +float hash_float_to_float(float k); +float hash_float_to_float(float2 k); +float hash_float_to_float(float3 k); +float hash_float_to_float(float4 k); + +/* -------------------------------------------------------------------- + * Perlin noise. + */ + +/* Perlin noise in the range [-1, 1]. */ float perlin_signed(float position); float perlin_signed(float2 position); float perlin_signed(float3 position); float perlin_signed(float4 position); /* Perlin noise in the range [0, 1]. */ - float perlin(float position); float perlin(float2 position); float perlin(float3 position); float perlin(float4 position); /* Fractal perlin noise in the range [0, 1]. */ - float perlin_fractal(float position, float octaves, float roughness); float perlin_fractal(float2 position, float octaves, float roughness); float perlin_fractal(float3 position, float octaves, float roughness); float perlin_fractal(float4 position, float octaves, float roughness); /* Positive distorted fractal perlin noise. */ - float perlin_fractal_distorted(float position, float octaves, float roughness, float distortion); float perlin_fractal_distorted(float2 position, float octaves, float roughness, float distortion); float perlin_fractal_distorted(float3 position, float octaves, float roughness, float distortion); float perlin_fractal_distorted(float4 position, float octaves, float roughness, float distortion); /* Positive distorted fractal perlin noise that outputs a float3. */ - float3 perlin_float3_fractal_distorted(float position, float octaves, float roughness, diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index 0f80fa0f294..e80975f618c 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -161,30 +161,6 @@ uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) return c; } -/* Hashing a number of uint32_t into a float in the range [0, 1]. */ - -BLI_INLINE float hash_to_float(uint32_t kx) -{ - return static_cast(hash(kx)) / static_cast(0xFFFFFFFFu); -} - -BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky) -{ - return static_cast(hash(kx, ky)) / static_cast(0xFFFFFFFFu); -} - -BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz) -{ - return static_cast(hash(kx, ky, kz)) / static_cast(0xFFFFFFFFu); -} - -BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) -{ - return static_cast(hash(kx, ky, kz, kw)) / static_cast(0xFFFFFFFFu); -} - -/* Hashing a number of floats into a float in the range [0, 1]. */ - BLI_INLINE uint32_t float_as_uint(float f) { union { @@ -195,25 +171,73 @@ BLI_INLINE uint32_t float_as_uint(float f) return u.i; } -BLI_INLINE float hash_to_float(float k) +uint32_t hash_float(float kx) { - return hash_to_float(float_as_uint(k)); + return hash(float_as_uint(kx)); } -BLI_INLINE float hash_to_float(float2 k) +uint32_t hash_float(float2 k) { - return hash_to_float(float_as_uint(k.x), float_as_uint(k.y)); + return hash(float_as_uint(k.x), float_as_uint(k.y)); } -BLI_INLINE float hash_to_float(float3 k) +uint32_t hash_float(float3 k) { - return hash_to_float(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z)); + return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z)); } -BLI_INLINE float hash_to_float(float4 k) +uint32_t hash_float(float4 k) { - return hash_to_float( - float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w)); + return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w)); +} + +/* Hashing a number of uint32_t into a float in the range [0, 1]. */ + +BLI_INLINE float uint_to_float_01(uint32_t k) +{ + return static_cast(k) / static_cast(0xFFFFFFFFu); +} + +float hash_to_float(uint32_t kx) +{ + return uint_to_float_01(hash(kx)); +} + +float hash_to_float(uint32_t kx, uint32_t ky) +{ + return uint_to_float_01(hash(kx, ky)); +} + +float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz) +{ + return uint_to_float_01(hash(kx, ky, kz)); +} + +float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) +{ + return uint_to_float_01(hash(kx, ky, kz, kw)); +} + +/* Hashing a number of floats into a float in the range [0, 1]. */ + +float hash_float_to_float(float k) +{ + return hash_to_float(hash_float(k)); +} + +float hash_float_to_float(float2 k) +{ + return hash_to_float(hash_float(k)); +} + +float hash_float_to_float(float3 k) +{ + return hash_to_float(hash_float(k)); +} + +float hash_float_to_float(float4 k) +{ + return hash_to_float(hash_float(k)); } /* ------------ @@ -565,28 +589,28 @@ float perlin_fractal(float4 position, float octaves, float roughness) BLI_INLINE float random_float_offset(float seed) { - return 100.0f + hash_to_float(seed) * 100.0f; + return 100.0f + hash_float_to_float(seed) * 100.0f; } BLI_INLINE float2 random_float2_offset(float seed) { - return float2(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f); + return float2(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f); } BLI_INLINE float3 random_float3_offset(float seed) { - return float3(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f); + return float3(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 2.0f)) * 100.0f); } BLI_INLINE float4 random_float4_offset(float seed) { - return float4(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 3.0f)) * 100.0f); + return float4(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 2.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 3.0f)) * 100.0f); } /* Perlin noises to be added to the position to distort other noises. */ From e7ae2840a5e01e136ebf1f0375bdea7ad8dc2067 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Sep 2021 11:50:02 +0200 Subject: [PATCH 0183/1500] Geometry Nodes: new Distribute Points on Faces node This adds a replacement for the deprecated Point Distribute node. Arguments for the name change can be found in T91155. Descriptions of the sockets are available in D12536. Thanks to Jarrett Johnson for the initial patch! Differential Revision: https://developer.blender.org/D12536 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_geometry_set.hh | 8 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesdna/DNA_node_types.h | 5 + source/blender/makesrna/intern/rna_nodetree.c | 27 + source/blender/nodes/CMakeLists.txt | 3 +- source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../{ => legacy}/node_geo_point_distribute.cc | 0 .../node_geo_distribute_points_on_faces.cc | 753 ++++++++++++++++++ 11 files changed, 800 insertions(+), 1 deletion(-) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_point_distribute.cc (100%) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 9ad162da7dc..1884f53460e 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -592,6 +592,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshUVSphere"), ]), GeometryNodeCategory("GEO_POINT", "Point", items=[ + NodeItem("GeometryNodeDistributePointsOnFaces", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_fields_legacy_poll), NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_fields_legacy_poll), NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_fields_legacy_poll), diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index a6c77c74b9e..5fcdbc83e25 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -715,6 +715,14 @@ class AnonymousAttributeFieldInput : public fn::FieldInput { { } + template static fn::Field Create(StrongAnonymousAttributeID anonymous_id) + { + const CPPType &type = CPPType::get(); + auto field_input = std::make_shared(std::move(anonymous_id), + type); + return fn::Field{field_input}; + } + const GVArray *get_varray_for_context(const fn::FieldContext &context, IndexMask mask, ResourceScope &scope) const override; diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 2e843e82a9f..6f2d0d9dd90 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1500,6 +1500,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_STRING_JOIN 1087 #define GEO_NODE_CURVE_PARAMETER 1088 #define GEO_NODE_CURVE_FILLET 1089 +#define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES 1090 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 7fd681960cf..78789b2e771 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5762,6 +5762,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_to_points(); register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); + register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_edge_split(); register_node_type_geo_input_index(); register_node_type_geo_input_material(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index a52816ecf57..6e4737b7d07 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1980,6 +1980,11 @@ typedef enum GeometryNodePointDistributeMode { GEO_NODE_POINT_DISTRIBUTE_POISSON = 1, } GeometryNodePointDistributeMode; +typedef enum GeometryNodeDistributePointsOnFacesMode { + GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM = 0, + GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON = 1, +} GeometryNodeDistributePointsOnFacesMode; + typedef enum GeometryNodeRotatePointsType { GEO_NODE_POINT_ROTATE_TYPE_EULER = 0, GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b631e76c094..dcac157d1a2 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9479,6 +9479,33 @@ static void def_geo_point_distribute(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_distribute_points_on_faces(StructRNA *srna) +{ + PropertyRNA *prop; + + static const EnumPropertyItem rna_node_geometry_distribute_points_on_faces_mode_items[] = { + {GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM, + "RANDOM", + 0, + "Random", + "Distribute points randomly on the surface"}, + {GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON, + "POISSON", + 0, + "Poisson Disk", + "Distribute the points randomly on the surface while taking a minimum distance between " + "points into account"}, + {0, NULL, 0, NULL, NULL}, + }; + + prop = RNA_def_property(srna, "distribute_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_node_geometry_distribute_points_on_faces_mode_items); + RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM); + RNA_def_property_ui_text(prop, "Distribution Method", "Method to use for scattering points"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_attribute_color_ramp(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index e6af3ecafbc..65f0999c63b 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -146,6 +146,7 @@ set(SRC geometry/nodes/legacy/node_geo_material_assign.cc geometry/nodes/legacy/node_geo_select_by_material.cc + geometry/nodes/legacy/node_geo_point_distribute.cc geometry/nodes/node_geo_align_rotation_to_vector.cc geometry/nodes/node_geo_attribute_capture.cc @@ -196,6 +197,7 @@ set(SRC geometry/nodes/node_geo_curve_to_points.cc geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_delete_geometry.cc + geometry/nodes/node_geo_distribute_points_on_faces.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_normal.cc @@ -218,7 +220,6 @@ set(SRC geometry/nodes/node_geo_mesh_subdivide.cc geometry/nodes/node_geo_mesh_to_curve.cc geometry/nodes/node_geo_object_info.cc - geometry/nodes/node_geo_point_distribute.cc geometry/nodes/node_geo_point_instance.cc geometry/nodes/node_geo_point_rotate.cc geometry/nodes/node_geo_point_scale.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 47bc54132eb..9cd3365bd91 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -78,6 +78,7 @@ void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); +void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_input_index(void); void register_node_type_geo_input_material(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index ab673d814bb..b1c4f1d6367 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -332,6 +332,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") +DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc new file mode 100644 index 00000000000..95987a15f32 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -0,0 +1,753 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_kdtree.h" +#include "BLI_noise.hh" +#include "BLI_rand.hh" +#include "BLI_timeit.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_bvhutils.h" +#include "BKE_geometry_set_instances.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" +#include "BKE_pointcloud.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::bke::GeometryInstanceGroup; + +namespace blender::nodes { + +static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Distance Min").min(0.0f).subtype(PROP_DISTANCE); + b.add_input("Density Max").default_value(10.0f).min(0.0f); + b.add_input("Density").default_value(10.0f).supports_field(); + b.add_input("Density Factor") + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .supports_field(); + b.add_input("Seed"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + + b.add_output("Points"); + b.add_output("Normal").field_source(); + b.add_output("Rotation").subtype(PROP_EULER).field_source(); + b.add_output("Stable ID").field_source(); +} + +static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE); +} + +static void node_point_distribute_points_on_faces_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + bNodeSocket *sock_density_max = (bNodeSocket *)sock_distance_min->next; + bNodeSocket *sock_density = sock_density_max->next; + bNodeSocket *sock_density_factor = sock_density->next; + nodeSetSocketAvailability(sock_distance_min, + node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON); + nodeSetSocketAvailability(sock_density_max, + node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON); + nodeSetSocketAvailability(sock_density, + node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM); + nodeSetSocketAvailability(sock_density_factor, + node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON); +} + +/** + * Use an arbitrary choice of axes for a usable rotation attribute directly out of this node. + */ +static float3 normal_to_euler_rotation(const float3 normal) +{ + float quat[4]; + vec_to_quat(quat, normal, OB_NEGZ, OB_POSY); + float3 rotation; + quat_to_eul(rotation, quat); + return rotation; +} + +static void sample_mesh_surface(const Mesh &mesh, + const float4x4 &transform, + const float base_density, + const Span density_factors, + const int seed, + Vector &r_positions, + Vector &r_bary_coords, + Vector &r_looptri_indices) +{ + const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; + + for (const int looptri_index : looptris.index_range()) { + const MLoopTri &looptri = looptris[looptri_index]; + const int v0_loop = looptri.tri[0]; + const int v1_loop = looptri.tri[1]; + const int v2_loop = looptri.tri[2]; + const int v0_index = mesh.mloop[v0_loop].v; + const int v1_index = mesh.mloop[v1_loop].v; + const int v2_index = mesh.mloop[v2_loop].v; + const float3 v0_pos = transform * float3(mesh.mvert[v0_index].co); + const float3 v1_pos = transform * float3(mesh.mvert[v1_index].co); + const float3 v2_pos = transform * float3(mesh.mvert[v2_index].co); + + float looptri_density_factor = 1.0f; + if (!density_factors.is_empty()) { + const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]); + const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]); + const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]); + looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f; + } + const float area = area_tri_v3(v0_pos, v1_pos, v2_pos); + + const int looptri_seed = noise::hash(looptri_index, seed); + RandomNumberGenerator looptri_rng(looptri_seed); + + const float points_amount_fl = area * base_density * looptri_density_factor; + const float add_point_probability = fractf(points_amount_fl); + const bool add_point = add_point_probability > looptri_rng.get_float(); + const int point_amount = (int)points_amount_fl + (int)add_point; + + for (int i = 0; i < point_amount; i++) { + const float3 bary_coord = looptri_rng.get_barycentric_coordinates(); + float3 point_pos; + interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coord); + r_positions.append(point_pos); + r_bary_coords.append(bary_coord); + r_looptri_indices.append(looptri_index); + } + } +} + +BLI_NOINLINE static KDTree_3d *build_kdtree(Span> positions_all, + const int initial_points_len) +{ + KDTree_3d *kdtree = BLI_kdtree_3d_new(initial_points_len); + + int i_point = 0; + for (const Vector &positions : positions_all) { + for (const float3 position : positions) { + BLI_kdtree_3d_insert(kdtree, i_point, position); + i_point++; + } + } + BLI_kdtree_3d_balance(kdtree); + return kdtree; +} + +BLI_NOINLINE static void update_elimination_mask_for_close_points( + Span> positions_all, + Span instance_start_offsets, + const float minimum_distance, + MutableSpan elimination_mask, + const int initial_points_len) +{ + if (minimum_distance <= 0.0f) { + return; + } + + KDTree_3d *kdtree = build_kdtree(positions_all, initial_points_len); + + /* The elimination mask is a flattened array for every point, + * so keep track of the index to it separately. */ + for (const int i_instance : positions_all.index_range()) { + Span positions = positions_all[i_instance]; + const int offset = instance_start_offsets[i_instance]; + + for (const int i : positions.index_range()) { + if (elimination_mask[offset + i]) { + continue; + } + + struct CallbackData { + int index; + MutableSpan elimination_mask; + } callback_data = {offset + i, elimination_mask}; + + BLI_kdtree_3d_range_search_cb( + kdtree, + positions[i], + minimum_distance, + [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) { + CallbackData &callback_data = *static_cast(user_data); + if (index != callback_data.index) { + callback_data.elimination_mask[index] = true; + } + return true; + }, + &callback_data); + } + } + BLI_kdtree_3d_free(kdtree); +} + +BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( + const Mesh &mesh, + const Span density_factors, + const Span bary_coords, + const Span looptri_indices, + const MutableSpan elimination_mask) +{ + const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; + for (const int i : bary_coords.index_range()) { + if (elimination_mask[i]) { + continue; + } + + const MLoopTri &looptri = looptris[looptri_indices[i]]; + const float3 bary_coord = bary_coords[i]; + + const int v0_loop = looptri.tri[0]; + const int v1_loop = looptri.tri[1]; + const int v2_loop = looptri.tri[2]; + + const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]); + const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]); + const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]); + + const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + + v2_density_factor * bary_coord.z; + + const float hash = noise::hash_float_to_float(bary_coord); + if (hash > probablity) { + elimination_mask[i] = true; + } + } +} + +BLI_NOINLINE static void eliminate_points_based_on_mask(const Span elimination_mask, + Vector &positions, + Vector &bary_coords, + Vector &looptri_indices) +{ + for (int i = positions.size() - 1; i >= 0; i--) { + if (elimination_mask[i]) { + positions.remove_and_reorder(i); + bary_coords.remove_and_reorder(i); + looptri_indices.remove_and_reorder(i); + } + } +} + +BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, + const Span bary_coords, + const Span looptri_indices, + const AttributeDomain source_domain, + const GVArray &source_data, + GMutableSpan output_data) +{ + switch (source_domain) { + case ATTR_DOMAIN_POINT: { + bke::mesh_surface_sample::sample_point_attribute( + mesh, looptri_indices, bary_coords, source_data, output_data); + break; + } + case ATTR_DOMAIN_CORNER: { + bke::mesh_surface_sample::sample_corner_attribute( + mesh, looptri_indices, bary_coords, source_data, output_data); + break; + } + case ATTR_DOMAIN_FACE: { + bke::mesh_surface_sample::sample_face_attribute( + mesh, looptri_indices, source_data, output_data); + break; + } + default: { + /* Not supported currently. */ + return; + } + } +} + +BLI_NOINLINE static void propagate_existing_attributes( + const Span set_groups, + const Span instance_start_offsets, + const Map &attributes, + GeometryComponent &component, + const Span> bary_coords_array, + const Span> looptri_indices_array) +{ + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + const CustomDataType output_data_type = entry.value.data_type; + /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */ + OutputAttribute attribute_out = component.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, output_data_type); + if (!attribute_out) { + continue; + } + + GMutableSpan out_span = attribute_out.as_span(); + + int i_instance = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + const MeshComponent &source_component = *set.get_component_for_read(); + const Mesh &mesh = *source_component.get_for_read(); + + std::optional attribute_info = component.attribute_get_meta_data( + attribute_id); + if (!attribute_info) { + i_instance += set_group.transforms.size(); + continue; + } + + const AttributeDomain source_domain = attribute_info->domain; + GVArrayPtr source_attribute = source_component.attribute_get_for_read( + attribute_id, source_domain, output_data_type, nullptr); + if (!source_attribute) { + i_instance += set_group.transforms.size(); + continue; + } + + for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { + const int offset = instance_start_offsets[i_instance]; + Span bary_coords = bary_coords_array[i_instance]; + Span looptri_indices = looptri_indices_array[i_instance]; + + GMutableSpan instance_span = out_span.slice(offset, bary_coords.size()); + interpolate_attribute( + mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span); + + i_instance++; + } + + attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) { + using T = decltype(dummy); + + GVArray_Span source_span{*source_attribute}; + }); + } + + attribute_out.save(); + } +} + +namespace { +struct AttributeOutputs { + StrongAnonymousAttributeID normal_id; + StrongAnonymousAttributeID rotation_id; + StrongAnonymousAttributeID stable_id_id; +}; +} // namespace + +BLI_NOINLINE static void compute_attribute_outputs(const Span sets, + const Span instance_start_offsets, + GeometryComponent &component, + const Span> bary_coords_array, + const Span> looptri_indices_array, + const AttributeOutputs &attribute_outputs) +{ + std::optional> id_attribute; + std::optional> normal_attribute; + std::optional> rotation_attribute; + + MutableSpan result_ids; + MutableSpan result_normals; + MutableSpan result_rotations; + + if (attribute_outputs.stable_id_id) { + id_attribute.emplace(component.attribute_try_get_for_output_only( + attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT)); + result_ids = id_attribute->as_span(); + } + if (attribute_outputs.normal_id) { + normal_attribute.emplace(component.attribute_try_get_for_output_only( + attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT)); + result_normals = normal_attribute->as_span(); + } + if (attribute_outputs.rotation_id) { + rotation_attribute.emplace(component.attribute_try_get_for_output_only( + attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT)); + result_rotations = rotation_attribute->as_span(); + } + + int i_instance = 0; + for (const GeometryInstanceGroup &set_group : sets) { + const GeometrySet &set = set_group.geometry_set; + const MeshComponent &component = *set.get_component_for_read(); + const Mesh &mesh = *component.get_for_read(); + const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; + + for (const float4x4 &transform : set_group.transforms) { + const int offset = instance_start_offsets[i_instance]; + + Span bary_coords = bary_coords_array[i_instance]; + Span looptri_indices = looptri_indices_array[i_instance]; + MutableSpan ids = result_ids.slice(offset, bary_coords.size()); + MutableSpan normals = result_normals.slice(offset, bary_coords.size()); + MutableSpan rotations = result_rotations.slice(offset, bary_coords.size()); + + /* Use one matrix multiplication per point instead of three (for each triangle corner). */ + float rotation_matrix[3][3]; + mat4_to_rot(rotation_matrix, transform.values); + + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; + + const int v0_index = mesh.mloop[looptri.tri[0]].v; + const int v1_index = mesh.mloop[looptri.tri[1]].v; + const int v2_index = mesh.mloop[looptri.tri[2]].v; + const float3 v0_pos = float3(mesh.mvert[v0_index].co); + const float3 v1_pos = float3(mesh.mvert[v1_index].co); + const float3 v2_pos = float3(mesh.mvert[v2_index].co); + + if (!result_ids.is_empty()) { + ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index); + } + float3 normal; + if (!result_normals.is_empty() || !result_rotations.is_empty()) { + normal_tri_v3(normal, v0_pos, v1_pos, v2_pos); + mul_m3_v3(rotation_matrix, normal); + } + if (!result_normals.is_empty()) { + normals[i] = normal; + } + if (!result_rotations.is_empty()) { + rotations[i] = normal_to_euler_rotation(normal); + } + } + + i_instance++; + } + } + + if (id_attribute) { + id_attribute->save(); + } + if (normal_attribute) { + normal_attribute->save(); + } + if (rotation_attribute) { + rotation_attribute->save(); + } +} + +static Array calc_full_density_factors_with_selection(const MeshComponent &component, + const Field &density_field, + const Field &selection_field) +{ + const AttributeDomain attribute_domain = ATTR_DOMAIN_CORNER; + GeometryComponentFieldContext field_context{component, attribute_domain}; + const int domain_size = component.attribute_domain_size(attribute_domain); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection_mask = selection_evaluator.get_evaluated_as_mask(0); + + Array densities(domain_size, 0.0f); + + fn::FieldEvaluator density_evaluator{field_context, &selection_mask}; + density_evaluator.add_with_destination(density_field, densities.as_mutable_span()); + density_evaluator.evaluate(); + return densities; +} + +static void distribute_points_random(Span set_groups, + const Field &density_field, + const Field &selection_field, + const int seed, + MutableSpan> positions_all, + MutableSpan> bary_coords_all, + MutableSpan> looptri_indices_all) +{ + int i_instance = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + const MeshComponent &component = *set.get_component_for_read(); + const Array densities = calc_full_density_factors_with_selection( + component, density_field, selection_field); + const Mesh &mesh = *component.get_for_read(); + for (const float4x4 &transform : set_group.transforms) { + Vector &positions = positions_all[i_instance]; + Vector &bary_coords = bary_coords_all[i_instance]; + Vector &looptri_indices = looptri_indices_all[i_instance]; + const int instance_seed = noise::hash(seed, i_instance); + sample_mesh_surface(mesh, + transform, + 1.0f, + densities, + instance_seed, + positions, + bary_coords, + looptri_indices); + i_instance++; + } + } +} + +static void distribute_points_poisson_disk(Span set_groups, + const float minimum_distance, + const float max_density, + const Field &density_factor_field, + const Field &selection_field, + const int seed, + MutableSpan> positions_all, + MutableSpan> bary_coords_all, + MutableSpan> looptri_indices_all) +{ + Array instance_start_offsets(positions_all.size()); + int initial_points_len = 0; + int i_instance = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + const MeshComponent &component = *set.get_component_for_read(); + const Mesh &mesh = *component.get_for_read(); + for (const float4x4 &transform : set_group.transforms) { + Vector &positions = positions_all[i_instance]; + Vector &bary_coords = bary_coords_all[i_instance]; + Vector &looptri_indices = looptri_indices_all[i_instance]; + const int instance_seed = noise::hash(seed, i_instance); + sample_mesh_surface(mesh, + transform, + max_density, + {}, + instance_seed, + positions, + bary_coords, + looptri_indices); + + instance_start_offsets[i_instance] = initial_points_len; + initial_points_len += positions.size(); + i_instance++; + } + } + + /* Unlike the other result arrays, the elimination mask in stored as a flat array for every + * point, in order to simplify culling points from the KDTree (which needs to know about all + * points at once). */ + Array elimination_mask(initial_points_len, false); + update_elimination_mask_for_close_points(positions_all, + instance_start_offsets, + minimum_distance, + elimination_mask, + initial_points_len); + + i_instance = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + const MeshComponent &component = *set.get_component_for_read(); + const Mesh &mesh = *component.get_for_read(); + + const Array density_factors = calc_full_density_factors_with_selection( + component, density_factor_field, selection_field); + + for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { + Vector &positions = positions_all[i_instance]; + Vector &bary_coords = bary_coords_all[i_instance]; + Vector &looptri_indices = looptri_indices_all[i_instance]; + + const int offset = instance_start_offsets[i_instance]; + update_elimination_mask_based_on_density_factors( + mesh, + density_factors, + bary_coords, + looptri_indices, + elimination_mask.as_mutable_span().slice(offset, positions.size())); + + eliminate_points_based_on_mask(elimination_mask.as_span().slice(offset, positions.size()), + positions, + bary_coords, + looptri_indices); + + i_instance++; + } + } +} + +static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + const GeometryNodeDistributePointsOnFacesMode distribute_method = + static_cast(params.node().custom1); + + const int seed = params.get_input("Seed") * 5383843; + const Field selection_field = params.extract_input>("Selection"); + + Vector set_groups; + geometry_set_gather_instances(geometry_set, set_groups); + if (set_groups.is_empty()) { + params.set_output("Points", GeometrySet()); + return; + } + + /* Remove any set inputs that don't contain a mesh, to avoid checking later on. */ + for (int i = set_groups.size() - 1; i >= 0; i--) { + const GeometrySet &set = set_groups[i].geometry_set; + if (!set.has_mesh()) { + set_groups.remove_and_reorder(i); + } + } + + if (set_groups.is_empty()) { + params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh")); + params.set_output("Points", GeometrySet()); + return; + } + + int instances_len = 0; + for (GeometryInstanceGroup &set_group : set_groups) { + instances_len += set_group.transforms.size(); + } + + /* Store data per-instance in order to simplify attribute access after the scattering, + * and to make the point elimination simpler for the poisson disk mode. Note that some + * vectors will be empty if any instances don't contain mesh data. */ + Array> positions_all(instances_len); + Array> bary_coords_all(instances_len); + Array> looptri_indices_all(instances_len); + + switch (distribute_method) { + case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: { + const Field density_field = params.extract_input>("Density"); + distribute_points_random(set_groups, + density_field, + selection_field, + seed, + positions_all, + bary_coords_all, + looptri_indices_all); + break; + } + case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: { + const float minimum_distance = params.extract_input("Distance Min"); + const float density_max = params.extract_input("Density Max"); + const Field density_factors_field = params.extract_input>( + "Density Factor"); + distribute_points_poisson_disk(set_groups, + minimum_distance, + density_max, + density_factors_field, + selection_field, + seed, + positions_all, + bary_coords_all, + looptri_indices_all); + break; + } + } + + int final_points_len = 0; + Array instance_start_offsets(set_groups.size()); + for (const int i : positions_all.index_range()) { + Vector &positions = positions_all[i]; + instance_start_offsets[i] = final_points_len; + final_points_len += positions.size(); + } + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(final_points_len); + for (const int instance_index : positions_all.index_range()) { + const int offset = instance_start_offsets[instance_index]; + Span positions = positions_all[instance_index]; + memcpy(pointcloud->co + offset, positions.data(), sizeof(float3) * positions.size()); + } + + uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); + + GeometrySet geometry_set_out = GeometrySet::create_with_pointcloud(pointcloud); + PointCloudComponent &point_component = + geometry_set_out.get_component_for_write(); + + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, true, attributes); + + /* Position is set separately. */ + attributes.remove("position"); + + propagate_existing_attributes(set_groups, + instance_start_offsets, + attributes, + point_component, + bary_coords_all, + looptri_indices_all); + + AttributeOutputs attribute_outputs; + if (params.output_is_required("Normal")) { + attribute_outputs.normal_id = StrongAnonymousAttributeID("normal"); + } + if (params.output_is_required("Rotation")) { + attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation"); + } + if (params.output_is_required("Stable ID")) { + attribute_outputs.stable_id_id = StrongAnonymousAttributeID("stable id"); + } + + compute_attribute_outputs(set_groups, + instance_start_offsets, + point_component, + bary_coords_all, + looptri_indices_all, + attribute_outputs); + + params.set_output("Points", std::move(geometry_set_out)); + + if (attribute_outputs.normal_id) { + params.set_output( + "Normal", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.normal_id))); + } + if (attribute_outputs.rotation_id) { + params.set_output( + "Rotation", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.rotation_id))); + } + if (attribute_outputs.stable_id_id) { + params.set_output( + "Stable ID", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.stable_id_id))); + } +} + +} // namespace blender::nodes + +void register_node_type_geo_distribute_points_on_faces() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, + GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, + "Distribute Points on Faces", + NODE_CLASS_GEOMETRY, + 0); + node_type_update(&ntype, blender::nodes::node_point_distribute_points_on_faces_update); + node_type_size(&ntype, 170, 100, 320); + ntype.declare = blender::nodes::geo_node_point_distribute_points_on_faces_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_points_on_faces_exec; + ntype.draw_buttons = blender::nodes::geo_node_point_distribute_points_on_faces_layout; + nodeRegisterType(&ntype); +} From cdcdd2c479b59a0030557882d05aebd08dfeca44 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 21 Sep 2021 15:11:09 +0200 Subject: [PATCH 0184/1500] LibOverride: Add utils to convert all proxies to overrides. --- source/blender/blenkernel/BKE_lib_override.h | 2 + .../blender/blenkernel/intern/lib_override.c | 71 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index c6658ff424a..20128e2608a 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -84,6 +84,8 @@ bool BKE_lib_override_library_proxy_convert(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct Object *ob_proxy); +void BKE_lib_override_library_main_proxy_convert(struct Main *bmain, + struct BlendFileReadReport *reports); bool BKE_lib_override_library_resync(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index c60a9104144..5e14c603d4a 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1003,6 +1003,77 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL); } +static void lib_override_library_proxy_convert_do(Main *bmain, + Scene *scene, + Object *ob_proxy, + BlendFileReadReport *reports) +{ + Object *ob_proxy_group = ob_proxy->proxy_group; + const bool is_override_instancing_object = ob_proxy_group != NULL; + + const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy); + + if (success) { + CLOG_INFO(&LOG, + 4, + "Proxy object '&s' successfuly converted to library overrides", + ob_proxy->id.name); + /* Remove the instance empty from this scene, the items now have an overridden collection + * instead. */ + if (is_override_instancing_object) { + BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true); + } + reports->count.proxies_to_lib_overrides_success++; + } +} + +/** + * Convert all proxy objects into library overrides. + * + * \note Only affects local proxies, linked ones are not affected. + * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). + */ +void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports) +{ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + FOREACH_SCENE_OBJECT_BEGIN (scene, object) { + if (object->proxy_group == NULL) { + continue; + } + + lib_override_library_proxy_convert_do(bmain, scene, object, reports); + } + FOREACH_SCENE_OBJECT_END; + + FOREACH_SCENE_OBJECT_BEGIN (scene, object) { + if (object->proxy == NULL) { + continue; + } + + lib_override_library_proxy_convert_do(bmain, scene, object, reports); + } + FOREACH_SCENE_OBJECT_END; + } + + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + if (ID_IS_LINKED(object)) { + if (object->proxy != NULL) { + CLOG_WARN(&LOG, "Did not try to convert linked proxy object '%s'", object->id.name); + reports->count.linked_proxies++; + } + continue; + } + + if (object->proxy_group != NULL || object->proxy != NULL) { + CLOG_WARN( + &LOG, "Proxy object '%s' failed to be converted to library override", object->id.name); + reports->count.proxies_to_lib_overrides_failures++; + } + } +} + /** * Advanced 'smart' function to resync, re-create fully functional overrides up-to-date with linked * data, from an existing override hierarchy. From 501b0190d679b54f47a11d2c3d345e4af3a0c64c Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 21 Sep 2021 15:12:05 +0200 Subject: [PATCH 0185/1500] LibOverride: Deprecate Proxies: Add auto-conversion on file load. This commit also add an experimental userPreferences to prevent proxies conversions on file load, and reporting for amount of coverted proxies (and possible issues). Note that potentially linked proxies from other libraries are not hamdled here (this feature seems to be broken anyway in master currently?). --- release/scripts/startup/bl_ui/space_userpref.py | 1 + source/blender/blenkernel/intern/blendfile.c | 7 +++++++ source/blender/blenkernel/intern/lib_override.c | 2 +- source/blender/blenloader/BLO_readfile.h | 8 ++++++++ source/blender/makesdna/DNA_userdef_types.h | 3 ++- source/blender/makesrna/intern/rna_userdef.c | 7 +++++++ source/blender/windowmanager/intern/wm_files.c | 14 ++++++++++++++ 7 files changed, 40 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 0093110d326..ef1c7a77a2b 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2283,6 +2283,7 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): context, ( ({"property": "use_undo_legacy"}, "T60695"), ({"property": "override_auto_resync"}, "T83811"), + ({"property": "proxy_to_override_auto_conversion"}, "T91671"), ({"property": "use_cycles_debug"}, None), ), ) diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 1c5d8804280..6957f9b5a69 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -344,6 +344,13 @@ static void setup_app_data(bContext *C, do_versions_ipos_to_animato(bmain); } + /* FIXME: Same as above, readfile's `do_version` do not allow to create new IDs. */ + /* TODO: Once this is definitively validated for 3.0 and option to not do it is removed, add a + * version bump and check here. */ + if (!USER_EXPERIMENTAL_TEST(&U, no_proxy_to_override_conversion)) { + BKE_lib_override_library_main_proxy_convert(bmain, reports); + } + bmain->recovered = 0; /* startup.blend or recovered startup */ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 5e14c603d4a..f382fc0614b 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1016,7 +1016,7 @@ static void lib_override_library_proxy_convert_do(Main *bmain, if (success) { CLOG_INFO(&LOG, 4, - "Proxy object '&s' successfuly converted to library overrides", + "Proxy object '%s' successfuly converted to library overrides", ob_proxy->id.name); /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 4b4311d32bf..9093c6fd85b 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -111,8 +111,16 @@ typedef struct BlendFileReadReport { /* Some sub-categories of the above `missing_linked_id` counter. */ int missing_obdata; int missing_obproxies; + /* Number of root override IDs that were resynced. */ int resynced_lib_overrides; + + /* Number of (non-converted) linked proxies. */ + int linked_proxies; + /* Number of proxies converted to library overrides. */ + int proxies_to_lib_overrides_success; + /* Number of proxies that failed to convert to library overrides. */ + int proxies_to_lib_overrides_failures; } count; /* Number of libraries which had overrides that needed to be resynced, and a single linked list diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 4f86201ced2..2399fb324d7 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -635,6 +635,7 @@ typedef struct UserDef_Experimental { /* Debug options, always available. */ char use_undo_legacy; char no_override_auto_resync; + char no_proxy_to_override_conversion; char use_cycles_debug; char SANITIZE_AFTER_HERE; /* The following options are automatically sanitized (set to 0) @@ -647,7 +648,7 @@ typedef struct UserDef_Experimental { char use_extended_asset_browser; char use_override_templates; char use_geometry_nodes_fields; - char _pad[4]; + char _pad[3]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 563c6ea35e0..16f33507bed 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6285,6 +6285,13 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) "Enable library overrides automatic resync detection and process on file load. Disable when " "dealing with older .blend files that need manual Resync (Enforce) handling"); + prop = RNA_def_property(srna, "proxy_to_override_auto_conversion", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "no_proxy_to_override_conversion", 1); + RNA_def_property_ui_text( + prop, + "Proxy to Override Auto Conversion", + "Enable automatic conversion of proxies to library overrides on file load"); + prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1); RNA_def_property_ui_text( diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 2ce2bcc2f3c..bbab3a8b326 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -857,6 +857,20 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports) duration_lib_override_recursive_resync_seconds); } + if (bf_reports->count.linked_proxies != 0 || + bf_reports->count.proxies_to_lib_overrides_success != 0 || + bf_reports->count.proxies_to_lib_overrides_failures != 0) { + BKE_reportf(bf_reports->reports, + RPT_WARNING, + "Proxies are deprecated (%d proxies were automatically converted to library " + "overrides, %d proxies could not be converted and %d linked proxies were kept " + "untouched). If you need to keep proxies for the time being, please disable the " + "`Proxy to Override Auto Conversion` in Experimental user preferences", + bf_reports->count.proxies_to_lib_overrides_success, + bf_reports->count.proxies_to_lib_overrides_failures, + bf_reports->count.linked_proxies); + } + BLI_linklist_free(bf_reports->resynced_lib_overrides_libraries, NULL); bf_reports->resynced_lib_overrides_libraries = NULL; } From 59387aabe80547641729e79d8dc3639cd23f8c3a Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 21 Sep 2021 15:40:18 +0200 Subject: [PATCH 0186/1500] LibOverride: deprecate Proxies: Remove 'Make Proxy' operator. --- .../scripts/modules/rna_manual_reference.py | 1 - .../keyconfig/keymap_data/blender_default.py | 1 - release/scripts/startup/bl_ui/space_view3d.py | 2 - source/blender/editors/object/object_intern.h | 1 - source/blender/editors/object/object_ops.c | 1 - .../blender/editors/object/object_relations.c | 161 ------------------ 6 files changed, 167 deletions(-) diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 40f59307bec..30cbcf4ea05 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -1944,7 +1944,6 @@ url_manual_mapping = ( ("bpy.ops.object.origin_set*", "scene_layout/object/origin.html#bpy-ops-object-origin-set"), ("bpy.ops.object.parent_set*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-set"), ("bpy.ops.object.pointcloud*", "modeling/point_cloud.html#bpy-ops-object-pointcloud"), - ("bpy.ops.object.proxy_make*", "files/linked_libraries/library_proxies.html#bpy-ops-object-proxy-make"), ("bpy.ops.object.select_all*", "scene_layout/object/selecting.html#bpy-ops-object-select-all"), ("bpy.ops.object.shade_flat*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-flat"), ("bpy.ops.pose.group_assign*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-assign"), diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 3da434ac9d9..ada8824519e 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4396,7 +4396,6 @@ def km_object_mode(params): ("object.duplicates_make_real", {"type": 'A', "value": 'PRESS', "shift": True, "ctrl": True}, None), op_menu("VIEW3D_MT_make_single_user", {"type": 'U', "value": 'PRESS'}), ("object.convert", {"type": 'C', "value": 'PRESS', "alt": True}, None), - ("object.proxy_make", {"type": 'P', "value": 'PRESS', "ctrl": True, "alt": True}, None), ("object.make_local", {"type": 'L', "value": 'PRESS'}, None), ("object.data_transfer", {"type": 'T', "value": 'PRESS', "shift": True, "ctrl": True}, None), ]) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 3879f7de250..5d609c0afdf 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2234,8 +2234,6 @@ class VIEW3D_MT_object_relations(Menu): def draw(self, _context): layout = self.layout - layout.operator("object.proxy_make", text="Make Proxy...") - layout.operator("object.make_override_library", text="Make Library Override...") layout.operator("object.convert_proxy_to_override") diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 50dd9322c5c..d00e6efeb29 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -78,7 +78,6 @@ void OBJECT_OT_mode_set(struct wmOperatorType *ot); void OBJECT_OT_mode_set_with_submode(struct wmOperatorType *ot); void OBJECT_OT_editmode_toggle(struct wmOperatorType *ot); void OBJECT_OT_posemode_toggle(struct wmOperatorType *ot); -void OBJECT_OT_proxy_make(struct wmOperatorType *ot); void OBJECT_OT_shade_smooth(struct wmOperatorType *ot); void OBJECT_OT_shade_flat(struct wmOperatorType *ot); void OBJECT_OT_paths_calculate(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index aa9ae082317..fa0208a7022 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -56,7 +56,6 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_mode_set_with_submode); WM_operatortype_append(OBJECT_OT_editmode_toggle); WM_operatortype_append(OBJECT_OT_posemode_toggle); - WM_operatortype_append(OBJECT_OT_proxy_make); WM_operatortype_append(OBJECT_OT_shade_smooth); WM_operatortype_append(OBJECT_OT_shade_flat); WM_operatortype_append(OBJECT_OT_paths_calculate); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 75269dffec8..5c7e1e1fa01 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -330,167 +330,6 @@ void OBJECT_OT_vertex_parent_set(wmOperatorType *ot) /** \} */ -/* ------------------------------------------------------------------- */ -/** \name Make Proxy Operator - * \{ */ - -/* set the object to proxify */ -static int make_proxy_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Scene *scene = CTX_data_scene(C); - Object *ob = ED_object_active_context(C); - - /* sanity checks */ - if (!scene || ID_IS_LINKED(scene) || !ob) { - return OPERATOR_CANCELLED; - } - - /* Get object to work on - use a menu if we need to... */ - if (ob->instance_collection && ID_IS_LINKED(ob->instance_collection)) { - /* gives menu with list of objects in group */ - /* proxy_group_objects_menu(C, op, ob, ob->instance_collection); */ - WM_enum_search_invoke(C, op, event); - return OPERATOR_CANCELLED; - } - if (ID_IS_LINKED(ob)) { - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION); - uiLayout *layout = UI_popup_menu_layout(pup); - - /* create operator menu item with relevant properties filled in */ - PointerRNA opptr_dummy; - uiItemFullO_ptr( - layout, op->type, op->type->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, 0, &opptr_dummy); - - /* present the menu and be done... */ - UI_popup_menu_end(C, pup); - - /* this invoke just calls another instance of this operator... */ - return OPERATOR_INTERFACE; - } - - /* error.. cannot continue */ - BKE_report(op->reports, RPT_ERROR, "Can only make proxy for a referenced object or collection"); - return OPERATOR_CANCELLED; -} - -static int make_proxy_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Object *ob, *gob = ED_object_active_context(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - - if (gob->instance_collection != NULL) { - const ListBase instance_collection_objects = BKE_collection_object_cache_get( - gob->instance_collection); - Base *base = BLI_findlink(&instance_collection_objects, RNA_enum_get(op->ptr, "object")); - ob = base->object; - } - else { - ob = gob; - gob = NULL; - } - - if (ob) { - Object *newob; - char name[MAX_ID_NAME + 4]; - - BLI_snprintf(name, sizeof(name), "%s_proxy", ((ID *)(gob ? gob : ob))->name + 2); - - /* Add new object for the proxy */ - newob = BKE_object_add_from(bmain, scene, view_layer, OB_EMPTY, name, gob ? gob : ob); - - /* set layers OK */ - BKE_object_make_proxy(bmain, newob, ob, gob); - - /* Set back pointer immediately so dependency graph knows that this is - * is a proxy and will act accordingly. Otherwise correctness of graph - * will depend on order of bases. - * - * TODO(sergey): We really need to get rid of this bi-directional links - * in proxies with something like library overrides. - */ - if (newob->proxy != NULL) { - newob->proxy->proxy_from = newob; - } - else { - BKE_report(op->reports, RPT_ERROR, "Unable to assign proxy"); - } - - /* depsgraph flushes are needed for the new data */ - DEG_relations_tag_update(bmain); - DEG_id_tag_update(&newob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, newob); - } - else { - BKE_report(op->reports, RPT_ERROR, "No object to make proxy for"); - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; -} - -/* Generic itemf's for operators that take library args */ -static const EnumPropertyItem *proxy_collection_object_itemf(bContext *C, - PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), - bool *r_free) -{ - EnumPropertyItem item_tmp = {0}, *item = NULL; - int totitem = 0; - int i = 0; - Object *ob = ED_object_active_context(C); - - if (!ob || !ob->instance_collection) { - return DummyRNA_DEFAULT_items; - } - - /* find the object to affect */ - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (ob->instance_collection, object) { - item_tmp.identifier = item_tmp.name = object->id.name + 2; - item_tmp.value = i++; - RNA_enum_item_add(&item, &totitem, &item_tmp); - } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; -} - -void OBJECT_OT_proxy_make(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Make Proxy"; - ot->idname = "OBJECT_OT_proxy_make"; - ot->description = "Add empty object to become local replacement data of a library-linked object"; - - /* callbacks */ - ot->invoke = make_proxy_invoke; - ot->exec = make_proxy_exec; - ot->poll = ED_operator_object_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - /* XXX, relies on hard coded ID at the moment */ - prop = RNA_def_enum(ot->srna, - "object", - DummyRNA_DEFAULT_items, - 0, - "Proxy Object", - "Name of library-linked/collection object to make a proxy for"); - RNA_def_enum_funcs(prop, proxy_collection_object_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); - ot->prop = prop; -} - -/** \} */ - /* ------------------------------------------------------------------- */ /** \name Clear Parent Operator * \{ */ From e8c6e32348fcd898b2b06e2af0c92c477739992d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 24 Sep 2021 12:52:32 +0200 Subject: [PATCH 0187/1500] Asset Catalogs: fix trailing slash test case The "path with trailing slash" test catalog was using the same path as another catalog, which meant it was ignored after doing the path cleanup. It's now different in the test file in SVN, so it'll actually show up in the test. --- source/blender/blenkernel/intern/asset_catalog_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 9110b2cb0ba..074ffdbcaac 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -167,6 +167,7 @@ TEST_F(AssetCatalogTest, load_single_file_into_tree) {"character", 0}, {"character/Ellie", 1}, {"character/Ellie/poselib", 2}, + {"character/Ellie/poselib/tailslash", 3}, {"character/Ellie/poselib/white space", 3}, {"character/Ružena", 1}, {"character/Ružena/poselib", 2}, @@ -338,6 +339,7 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf) {"character", 0}, {"character/Ellie", 1}, {"character/Ellie/poselib", 2}, + {"character/Ellie/poselib/tailslash", 3}, {"character/Ellie/poselib/white space", 3}, {"character/Ružena", 1}, {"character/Ružena/poselib", 2}, From dfe01628b02725471a11e3afc825063b93ab392f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 24 Sep 2021 21:10:01 +1000 Subject: [PATCH 0188/1500] Cleanup: old-style-declaration warning --- source/blender/nodes/shader/nodes/node_shader_hair_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.c b/source/blender/nodes/shader/nodes/node_shader_hair_info.c index 559c5cf7ae8..c721fb9c77a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_hair_info.c +++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.c @@ -37,7 +37,7 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat, GPUNodeStack *out) { /* Length: don't request length if not needed. */ - const static float zero = 0; + static const float zero = 0; GPUNodeLink *length_link = (!out[2].hasoutput) ? GPU_constant(&zero) : GPU_attribute(mat, CD_HAIRLENGTH, ""); return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link); From 2b5733ff0122859b713ad9199d715add496c1608 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 24 Sep 2021 21:10:02 +1000 Subject: [PATCH 0189/1500] Fix T91192: Context.copy() crashes on file load The `ui_list` lookup from 87c1c8112fa44ccb94a3e996b7499d6577d85d7f didn't account for the region being unset. --- source/blender/editors/screen/screen_context.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 2ccefb993c7..3d447d90626 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -1073,9 +1073,14 @@ static eContextResult screen_ctx_ui_list(const bContext *C, bContextDataResult * { wmWindow *win = CTX_wm_window(C); ARegion *region = CTX_wm_region(C); - uiList *list = UI_list_find_mouse_over(region, win->eventstate); - CTX_data_pointer_set(result, NULL, &RNA_UIList, list); - return CTX_RESULT_OK; + if (region) { + uiList *list = UI_list_find_mouse_over(region, win->eventstate); + if (list) { + CTX_data_pointer_set(result, NULL, &RNA_UIList, list); + return CTX_RESULT_OK; + } + } + return CTX_RESULT_NO_DATA; } /* Registry of context callback functions. */ From 7e904139a3f638202e1c68d8860dc14c8c1890f2 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Sep 2021 13:33:06 +0200 Subject: [PATCH 0190/1500] Nodes: make dot in socket shape circular Previously, it was a diamond shape when the overall shape was a diamond. --- source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl index f0ff70f7690..a3b61dca8b4 100644 --- a/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl @@ -67,7 +67,7 @@ void main() if (outline_dist < 0) { /* Middle dot */ if (test(GPU_KEYFRAME_SHAPE_INNER_DOT)) { - alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], radius)); + alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], length(absPos))); } /* Up and down arrow-like shading. */ From 2b9ca0f112ad41e9402d272cc0c691bd5e1c5956 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 24 Sep 2021 13:38:03 +0200 Subject: [PATCH 0191/1500] Codestyle: Add brackets around body of if statements. --- source/blender/draw/intern/draw_hair.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index 6a23eb8695e..5c7eb083fc9 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -316,8 +316,9 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, } DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex); - if (hair_cache->length_tex) + if (hair_cache->length_tex) { DRW_shgroup_uniform_texture(shgrp, "hairLen", hair_cache->length_tex); + } DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1); DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res); DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape); From ab9644382d14eb1254ce33e38227c874fd80a08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 24 Sep 2021 12:55:26 +0200 Subject: [PATCH 0192/1500] UUID: add less-than operator Add `operator<` to C++ class to allow lexicographic ordering of UUIDs. This will be necessary when writing asset catalogs to disk in a predictable (i.e. ordered) manner. --- source/blender/blenlib/BLI_uuid.h | 5 ++++ source/blender/blenlib/intern/uuid.cc | 19 +++++++++++++++ source/blender/blenlib/tests/BLI_uuid_test.cc | 23 +++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 98a600a5de8..ed0d31b625f 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -98,6 +98,11 @@ class bUUID : public ::bUUID { bool operator==(bUUID uuid1, bUUID uuid2); bool operator!=(bUUID uuid1, bUUID uuid2); +/** + * Lexicographic comparison of the UUIDs. + * Equivalent to string comparison on the formatted UUIDs. */ +bool operator<(bUUID uuid1, bUUID uuid2); + } // namespace blender #endif diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index fe237b8aae6..3c86238036c 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -27,6 +27,7 @@ #include #include #include +#include /* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */ static_assert(sizeof(bUUID) == 16, "expect UUIDs to be 128 bit exactly"); @@ -189,4 +190,22 @@ bool operator!=(const bUUID uuid1, const bUUID uuid2) return !(uuid1 == uuid2); } +bool operator<(const bUUID uuid1, const bUUID uuid2) +{ + auto simple_fields1 = std::tie(uuid1.time_low, + uuid1.time_mid, + uuid1.time_hi_and_version, + uuid1.clock_seq_hi_and_reserved, + uuid1.clock_seq_low); + auto simple_fields2 = std::tie(uuid2.time_low, + uuid2.time_mid, + uuid2.time_hi_and_version, + uuid2.clock_seq_hi_and_reserved, + uuid2.clock_seq_low); + if (simple_fields1 == simple_fields2) { + return std::memcmp(uuid1.node, uuid2.node, sizeof(uuid1.node)) < 0; + } + return simple_fields1 < simple_fields2; +} + } // namespace blender diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc index f2160f614ba..b406a0521a1 100644 --- a/source/blender/blenlib/tests/BLI_uuid_test.cc +++ b/source/blender/blenlib/tests/BLI_uuid_test.cc @@ -75,6 +75,29 @@ TEST(BLI_uuid, equality) EXPECT_NE(uuid1, uuid2); } +TEST(BLI_uuid, comparison_trivial) +{ + const bUUID uuid0{}; + const bUUID uuid1("11111111-1111-1111-1111-111111111111"); + const bUUID uuid2("22222222-2222-2222-2222-222222222222"); + + EXPECT_LT(uuid0, uuid1); + EXPECT_LT(uuid0, uuid2); + EXPECT_LT(uuid1, uuid2); +} + +TEST(BLI_uuid, comparison_byte_order_check) +{ + const bUUID uuid0{}; + /* Chosen to test byte ordering is taken into account correctly when comparing. */ + const bUUID uuid12("12222222-2222-2222-2222-222222222222"); + const bUUID uuid21("21111111-1111-1111-1111-111111111111"); + + EXPECT_LT(uuid0, uuid12); + EXPECT_LT(uuid0, uuid21); + EXPECT_LT(uuid12, uuid21); +} + TEST(BLI_uuid, string_formatting) { bUUID uuid; From e1e380ba388fbe514b38c3023ca9eb02cc2c78ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 24 Sep 2021 14:42:44 +0200 Subject: [PATCH 0193/1500] Asset Catalogs: write catalogs sorted by path & UUID When writing asset catalog definition files, order the catalogs by (path, UUID). This ensures that every write produces the same file, playing nice with versioning / synchronisation systems. --- .../blender/blenkernel/BKE_asset_catalog.hh | 13 +++++++ .../blenkernel/intern/asset_catalog.cc | 9 ++++- .../blenkernel/intern/asset_catalog_test.cc | 38 +++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 5fd68e33f4a..51cfa8452c0 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -32,6 +32,7 @@ #include #include +#include #include namespace blender::bke { @@ -255,4 +256,16 @@ class AssetCatalog { static std::string sensible_simple_name_for_path(const CatalogPath &path); }; +struct AssetCatalogPathCmp { + bool operator()(const AssetCatalog *lhs, const AssetCatalog *rhs) const + { + if (lhs->path == rhs->path) { + return lhs->catalog_id < rhs->catalog_id; + } + return lhs->path < rhs->path; + } +}; + +using AssetCatalogOrderedSet = std::set; + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 20f2dc0a571..4f1de09e148 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -30,6 +30,7 @@ #endif #include +#include namespace blender::bke { @@ -525,10 +526,16 @@ bool AssetCatalogDefinitionFile::write_to_disk_unsafe(const CatalogFilePath &des // Write the catalogs. // TODO(@sybren): order them by Catalog ID or Catalog Path. - for (const auto &catalog : catalogs_.values()) { + + AssetCatalogOrderedSet catalogs_by_path; + for (const AssetCatalog *catalog : catalogs_.values()) { if (catalog->flags.is_deleted) { continue; } + catalogs_by_path.insert(catalog); + } + + for (const AssetCatalog *catalog : catalogs_by_path) { output << catalog->catalog_id << ":" << catalog->path << ":" << catalog->simple_name << std::endl; } diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 074ffdbcaac..b40aae5d64a 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -442,4 +442,42 @@ TEST_F(AssetCatalogTest, backups) EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); } +TEST_F(AssetCatalogTest, order_by_path) +{ + const bUUID cat2_uuid("22222222-b847-44d9-bdca-ff04db1c24f5"); + const bUUID cat4_uuid("11111111-b847-44d9-bdca-ff04db1c24f5"); // Sorts earlier than above. + const AssetCatalog cat1(BLI_uuid_generate_random(), "simple/path/child", ""); + const AssetCatalog cat2(cat2_uuid, "simple/path", ""); + const AssetCatalog cat3(BLI_uuid_generate_random(), "complex/path/...or/is/it?", ""); + const AssetCatalog cat4(cat4_uuid, "simple/path", "different ID, same path"); // should be kept + const AssetCatalog cat5(cat4_uuid, "simple/path", "same ID, same path"); // disappears + + AssetCatalogOrderedSet by_path; + by_path.insert(&cat1); + by_path.insert(&cat2); + by_path.insert(&cat3); + by_path.insert(&cat4); + by_path.insert(&cat5); + + AssetCatalogOrderedSet::const_iterator set_iter = by_path.begin(); + + EXPECT_EQ(1, by_path.count(&cat1)); + EXPECT_EQ(1, by_path.count(&cat2)); + EXPECT_EQ(1, by_path.count(&cat3)); + EXPECT_EQ(1, by_path.count(&cat4)); + ASSERT_EQ(4, by_path.size()) << "Expecting cat5 to not be stored in the set, as it duplicates " + "an already-existing path + UUID"; + + EXPECT_EQ(cat3.catalog_id, (*(set_iter++))->catalog_id); // complex/path + EXPECT_EQ(cat4.catalog_id, (*(set_iter++))->catalog_id); // simple/path with 111.. ID + EXPECT_EQ(cat2.catalog_id, (*(set_iter++))->catalog_id); // simple/path with 222.. ID + EXPECT_EQ(cat1.catalog_id, (*(set_iter++))->catalog_id); // simple/path/child + + if (set_iter != by_path.end()) { + const AssetCatalog *next_cat = *set_iter; + FAIL() << "Did not expect more items in the set, had at least " << next_cat->catalog_id << ":" + << next_cat->path; + } +} + } // namespace blender::bke::tests From 1a1c54612472fd28e4c23b695aa73bd7f81ffeaf Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 24 Sep 2021 15:25:49 +0200 Subject: [PATCH 0194/1500] Fix T91240: Object duplication was duplicating its action twice. Weird that this was not reported before, this was creating a lot of extra actions everytime... --- source/blender/blenkernel/intern/object.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 465ec9dc665..fbdf99c91c2 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2656,8 +2656,6 @@ Object *BKE_object_duplicate(Main *bmain, return obn; } - BKE_animdata_duplicate_id_action(bmain, &obn->id, dupflag); - if (dupflag & USER_DUP_MAT) { for (int i = 0; i < obn->totcol; i++) { BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag); From 95ec6e4dd373f58eebf3119e470ead4156c724b4 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Sep 2021 16:02:59 +0200 Subject: [PATCH 0195/1500] Geometry Nodes: make index field more reusable Some inputs will be the index field implicitly, so we want this class to be available outside of `node_geo_input_index.cc`. --- source/blender/functions/FN_field.hh | 9 +++++++++ source/blender/functions/intern/field.cc | 15 ++++++++++++++ .../geometry/nodes/node_geo_input_index.cc | 20 +------------------ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index d4375b625ce..3ce0993da59 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -484,4 +484,13 @@ template Field make_constant_field(T value) GField make_field_constant_if_possible(GField field); +class IndexFieldInput final : public FieldInput { + public: + IndexFieldInput(); + + const GVArray *get_varray_for_context(const FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final; +}; + } // namespace blender::fn diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 599e4d4595a..fbd35c2c377 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -526,6 +526,21 @@ const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input, return field_input.get_varray_for_context(*this, mask, scope); } +IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get(), "Index") +{ +} + +const GVArray *IndexFieldInput::get_varray_for_context(const fn::FieldContext &UNUSED(context), + IndexMask mask, + ResourceScope &scope) const +{ + /* TODO: Investigate a similar method to IndexRange::as_span() */ + auto index_func = [](int i) { return i; }; + return &scope.construct< + fn::GVArray_For_EmbeddedVArray>>( + mask.min_array_size(), mask.min_array_size(), index_func); +} + /* -------------------------------------------------------------------- * FieldOperation. */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc index f18b0ab090a..7fcbaf429dd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc @@ -23,27 +23,9 @@ static void geo_node_input_index_declare(NodeDeclarationBuilder &b) b.add_output("Index").field_source(); } -class IndexFieldInput final : public fn::FieldInput { - public: - IndexFieldInput() : FieldInput(CPPType::get(), "Index") - { - } - - const GVArray *get_varray_for_context(const fn::FieldContext &UNUSED(context), - IndexMask mask, - ResourceScope &scope) const final - { - /* TODO: Investigate a similar method to IndexRange::as_span() */ - auto index_func = [](int i) { return i; }; - return &scope.construct< - fn::GVArray_For_EmbeddedVArray>>( - mask.min_array_size(), mask.min_array_size(), index_func); - } -}; - static void geo_node_input_index_exec(GeoNodeExecParams params) { - Field index_field{std::make_shared()}; + Field index_field{std::make_shared()}; params.set_output("Index", std::move(index_field)); } From ede14b3856c5b8938e57d3db217d68a9399385f2 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 24 Sep 2021 16:08:46 +0200 Subject: [PATCH 0196/1500] GPencil: Invert weight in Weight Proximity modifier In meshes modifer the Lowest is 0 and Highest is 1.0 and this was working inverted for grease pencil. Now, it works equals to meshes modifier. Also changed the tooltip to keep consistency with meshes modifier. --- .../gpencil_modifiers/intern/MOD_gpencilweight_proximity.c | 6 +++--- source/blender/makesrna/intern/rna_gpencil_modifier.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c index 0885828a3a0..4079485de8d 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c @@ -83,13 +83,13 @@ static float calc_point_weight_by_distance(Object *ob, float dist = len_v3v3(mmd->object->obmat[3], gvert); if (dist > dist_max) { - weight = 0.0f; + weight = 1.0f; } else if (dist <= dist_max && dist > dist_min) { - weight = (dist_max - dist) / max_ff((dist_max - dist_min), 0.0001f); + weight = 1.0f - ((dist_max - dist) / max_ff((dist_max - dist_min), 0.0001f)); } else { - weight = 1.0f; + weight = 0.0f; } return weight; diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 6480d16ccd2..ae6c7dcd76f 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -2867,7 +2867,7 @@ static void rna_def_modifier_gpencilweight_proximity(BlenderRNA *brna) prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "dist_start"); RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); - RNA_def_property_ui_text(prop, "Lowest", "Start value for distance calculation"); + RNA_def_property_ui_text(prop, "Lowest", "Distance mapping to 0.0 weight"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "minimum_weight", PROP_FLOAT, PROP_FACTOR); @@ -2878,7 +2878,7 @@ static void rna_def_modifier_gpencilweight_proximity(BlenderRNA *brna) prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "dist_end"); RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); - RNA_def_property_ui_text(prop, "Highest", "Max value for distance calculation"); + RNA_def_property_ui_text(prop, "Highest", "Distance mapping to 1.0 weight"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); From 45e432b894b6798b394708186034e066ac29694b Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 24 Sep 2021 16:16:28 +0200 Subject: [PATCH 0197/1500] GPencil: Change Proximity distance properties to distance type This keep consistency with mesh modifer. --- source/blender/makesrna/intern/rna_gpencil_modifier.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index ae6c7dcd76f..5e9dc1f6cc6 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -2864,9 +2864,10 @@ static void rna_def_modifier_gpencilweight_proximity(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); - prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "dist_start"); - RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 1000.0, 1.0, 2); RNA_def_property_ui_text(prop, "Lowest", "Distance mapping to 0.0 weight"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -2875,9 +2876,10 @@ static void rna_def_modifier_gpencilweight_proximity(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Minimum", "Minimum value for vertex weight"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "dist_end"); - RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 1000.0, 1.0, 2); RNA_def_property_ui_text(prop, "Highest", "Distance mapping to 1.0 weight"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); From 9cf593f30519386a987d6378f9512b7cc22e20a9 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 24 Sep 2021 16:27:58 +0200 Subject: [PATCH 0198/1500] GPencil: Reorganize list of modifiers As we have a now a new `Modify` column, we move some modifers to this column. --- .../blender/makesrna/intern/rna_gpencil_modifier.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 5e9dc1f6cc6..5817a200192 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -59,6 +59,12 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { {0, "", 0, N_("Modify"), ""}, + {eGpencilModifierType_Texture, + "GP_TEXTURE", + ICON_MOD_UVPROJECT, + "Texture Mapping", + "Change stroke uv texture values"}, + {eGpencilModifierType_Time, "GP_TIME", ICON_MOD_TIME, "Time Offset", "Offset keyframes"}, {eGpencilModifierType_WeightAngle, "GP_WEIGHT_ANGLE", ICON_MOD_VERTEX_WEIGHT, @@ -143,7 +149,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_THICKNESS, "Thickness", "Change stroke thickness"}, - {eGpencilModifierType_Time, "GP_TIME", ICON_MOD_TIME, "Time Offset", "Offset keyframes"}, {0, "", 0, N_("Color"), ""}, {eGpencilModifierType_Color, "GP_COLOR", @@ -155,11 +160,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_OPACITY, "Opacity", "Opacity of the strokes"}, - {eGpencilModifierType_Texture, - "GP_TEXTURE", - ICON_TEXTURE, - "Texture Mapping", - "Change stroke uv texture values"}, {eGpencilModifierType_Tint, "GP_TINT", ICON_MOD_TINT, "Tint", "Tint strokes with new color"}, {0, NULL, 0, NULL, NULL}, }; @@ -2685,7 +2685,7 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna) RNA_def_struct_ui_text( srna, "Texture Modifier", "Transform stroke texture coordinates Modifier"); RNA_def_struct_sdna(srna, "TextureGpencilModifierData"); - RNA_def_struct_ui_icon(srna, ICON_TEXTURE); + RNA_def_struct_ui_icon(srna, ICON_MOD_UVPROJECT); RNA_define_lib_overridable(true); From ab8f24811d2a2383e59f1facaf00350c224227e0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 24 Sep 2021 02:31:47 +0200 Subject: [PATCH 0199/1500] Cleanup: remove unused device code and includes --- intern/cycles/device/cpu/device_impl.cpp | 149 ---------------------- intern/cycles/device/cuda/device_impl.cpp | 136 -------------------- intern/cycles/device/cuda/device_impl.h | 1 - intern/cycles/device/device.cpp | 1 - intern/cycles/render/buffers.cpp | 1 - intern/cycles/render/session.cpp | 1 - 6 files changed, 289 deletions(-) diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp index bd0a32e610b..d02c18daee9 100644 --- a/intern/cycles/device/cpu/device_impl.cpp +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -54,7 +54,6 @@ #include "util/util_function.h" #include "util/util_logging.h" #include "util/util_map.h" -#include "util/util_opengl.h" #include "util/util_openimagedenoise.h" #include "util/util_optimization.h" #include "util/util_progress.h" @@ -298,154 +297,6 @@ void CPUDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) Device::build_bvh(bvh, progress, refit); } -#if 0 -void CPUDevice::render(DeviceTask &task, RenderTile &tile, KernelGlobals *kg) -{ - const bool use_coverage = kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE; - - scoped_timer timer(&tile.buffers->render_time); - - Coverage coverage(kg, tile); - if (use_coverage) { - coverage.init_path_trace(); - } - - float *render_buffer = (float *)tile.buffer; - int start_sample = tile.start_sample; - int end_sample = tile.start_sample + tile.num_samples; - - /* Needed for Embree. */ - SIMD_SET_FLUSH_TO_ZERO; - - for (int sample = start_sample; sample < end_sample; sample++) { - if (task.get_cancel() || TaskPool::canceled()) { - if (task.need_finish_queue == false) - break; - } - - if (tile.stealing_state == RenderTile::CAN_BE_STOLEN && task.get_tile_stolen()) { - tile.stealing_state = RenderTile::WAS_STOLEN; - break; - } - - if (tile.task == RenderTile::PATH_TRACE) { - for (int y = tile.y; y < tile.y + tile.h; y++) { - for (int x = tile.x; x < tile.x + tile.w; x++) { - if (use_coverage) { - coverage.init_pixel(x, y); - } - kernels.path_trace(kg, render_buffer, sample, x, y, tile.offset, tile.stride); - } - } - } - else { - for (int y = tile.y; y < tile.y + tile.h; y++) { - for (int x = tile.x; x < tile.x + tile.w; x++) { - kernels.bake(kg, render_buffer, sample, x, y, tile.offset, tile.stride); - } - } - } - tile.sample = sample + 1; - - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(sample)) { - const bool stop = adaptive_sampling_filter(kg, tile, sample); - if (stop) { - const int num_progress_samples = end_sample - sample; - tile.sample = end_sample; - task.update_progress(&tile, tile.w * tile.h * num_progress_samples); - break; - } - } - - task.update_progress(&tile, tile.w * tile.h); - } - if (use_coverage) { - coverage.finalize(); - } - - if (task.adaptive_sampling.use && (tile.stealing_state != RenderTile::WAS_STOLEN)) { - adaptive_sampling_post(tile, kg); - } -} - -void CPUDevice::thread_render(DeviceTask &task) -{ - if (TaskPool::canceled()) { - if (task.need_finish_queue == false) - return; - } - - /* allocate buffer for kernel globals */ - CPUKernelThreadGlobals kg(kernel_globals, get_cpu_osl_memory()); - - profiler.add_state(&kg.profiler); - - /* NLM denoiser. */ - DenoisingTask *denoising = NULL; - - /* OpenImageDenoise: we can only denoise with one thread at a time, so to - * avoid waiting with mutex locks in the denoiser, we let only a single - * thread acquire denoising tiles. */ - uint tile_types = task.tile_types; - bool hold_denoise_lock = false; - if ((tile_types & RenderTile::DENOISE) && task.denoising.type == DENOISER_OPENIMAGEDENOISE) { - if (!oidn_task_lock.try_lock()) { - tile_types &= ~RenderTile::DENOISE; - hold_denoise_lock = true; - } - } - - RenderTile tile; - while (task.acquire_tile(this, tile, tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) { - render(task, tile, &kg); - } - else if (tile.task == RenderTile::BAKE) { - render(task, tile, &kg); - } - else if (tile.task == RenderTile::DENOISE) { - denoise_openimagedenoise(task, tile); - task.update_progress(&tile, tile.w * tile.h); - } - - task.release_tile(tile); - - if (TaskPool::canceled()) { - if (task.need_finish_queue == false) - break; - } - } - - if (hold_denoise_lock) { - oidn_task_lock.unlock(); - } - - profiler.remove_state(&kg.profiler); - - delete denoising; -} - -void CPUDevice::thread_denoise(DeviceTask &task) -{ - RenderTile tile; - tile.x = task.x; - tile.y = task.y; - tile.w = task.w; - tile.h = task.h; - tile.buffer = task.buffer; - tile.sample = task.sample + task.num_samples; - tile.num_samples = task.num_samples; - tile.start_sample = task.sample; - tile.offset = task.offset; - tile.stride = task.stride; - tile.buffers = task.buffers; - - denoise_openimagedenoise(task, tile); - - task.update_progress(&tile, tile.w * tile.h); -} -#endif - const CPUKernels *CPUDevice::get_cpu_kernels() const { return &kernels; diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp index 0f10119e24f..5e1a63c04df 100644 --- a/intern/cycles/device/cuda/device_impl.cpp +++ b/intern/cycles/device/cuda/device_impl.cpp @@ -31,7 +31,6 @@ # include "util/util_logging.h" # include "util/util_map.h" # include "util/util_md5.h" -# include "util/util_opengl.h" # include "util/util_path.h" # include "util/util_string.h" # include "util/util_system.h" @@ -1169,141 +1168,6 @@ void CUDADevice::tex_free(device_texture &mem) } } -# if 0 -void CUDADevice::render(DeviceTask &task, - RenderTile &rtile, - device_vector &work_tiles) -{ - scoped_timer timer(&rtile.buffers->render_time); - - if (have_error()) - return; - - CUDAContextScope scope(this); - CUfunction cuRender; - - /* Get kernel function. */ - if (rtile.task == RenderTile::BAKE) { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_bake")); - } - else { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_path_trace")); - } - - if (have_error()) { - return; - } - - cuda_assert(cuFuncSetCacheConfig(cuRender, CU_FUNC_CACHE_PREFER_L1)); - - /* Allocate work tile. */ - work_tiles.alloc(1); - - KernelWorkTile *wtile = work_tiles.data(); - wtile->x = rtile.x; - wtile->y = rtile.y; - wtile->w = rtile.w; - wtile->h = rtile.h; - wtile->offset = rtile.offset; - wtile->stride = rtile.stride; - wtile->buffer = (float *)(CUdeviceptr)rtile.buffer; - - /* Prepare work size. More step samples render faster, but for now we - * remain conservative for GPUs connected to a display to avoid driver - * timeouts and display freezing. */ - int min_blocks, num_threads_per_block; - cuda_assert( - cuOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, cuRender, NULL, 0, 0)); - if (!info.display_device) { - min_blocks *= 8; - } - - uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h); - - /* Render all samples. */ - uint start_sample = rtile.start_sample; - uint end_sample = rtile.start_sample + rtile.num_samples; - - for (int sample = start_sample; sample < end_sample;) { - /* Setup and copy work tile to device. */ - wtile->start_sample = sample; - wtile->num_samples = step_samples; - if (task.adaptive_sampling.use) { - wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples); - } - wtile->num_samples = min(wtile->num_samples, end_sample - sample); - work_tiles.copy_to_device(); - - CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer; - uint total_work_size = wtile->w * wtile->h * wtile->num_samples; - uint num_blocks = divide_up(total_work_size, num_threads_per_block); - - /* Launch kernel. */ - void *args[] = {&d_work_tiles, &total_work_size}; - - cuda_assert( - cuLaunchKernel(cuRender, num_blocks, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0)); - - /* Run the adaptive sampling kernels at selected samples aligned to step samples. */ - uint filter_sample = sample + wtile->num_samples - 1; - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) { - adaptive_sampling_filter(filter_sample, wtile, d_work_tiles); - } - - cuda_assert(cuCtxSynchronize()); - - /* Update progress. */ - sample += wtile->num_samples; - rtile.sample = sample; - task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); - - if (task.get_cancel()) { - if (task.need_finish_queue == false) - break; - } - } - - /* Finalize adaptive sampling. */ - if (task.adaptive_sampling.use) { - CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer; - adaptive_sampling_post(rtile, wtile, d_work_tiles); - cuda_assert(cuCtxSynchronize()); - task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); - } -} - -void CUDADevice::thread_run(DeviceTask &task) -{ - CUDAContextScope scope(this); - - if (task.type == DeviceTask::RENDER) { - device_vector work_tiles(this, "work_tiles", MEM_READ_ONLY); - - /* keep rendering tiles until done */ - RenderTile tile; - DenoisingTask denoising(this, task); - - while (task.acquire_tile(this, tile, task.tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) { - render(task, tile, work_tiles); - } - else if (tile.task == RenderTile::BAKE) { - render(task, tile, work_tiles); - } - - task.release_tile(tile); - - if (task.get_cancel()) { - if (task.need_finish_queue == false) - break; - } - } - - work_tiles.free(); - } -} -# endif - unique_ptr CUDADevice::gpu_queue_create() { return make_unique(this); diff --git a/intern/cycles/device/cuda/device_impl.h b/intern/cycles/device/cuda/device_impl.h index 76c09d84651..c0316d18ba0 100644 --- a/intern/cycles/device/cuda/device_impl.h +++ b/intern/cycles/device/cuda/device_impl.h @@ -26,7 +26,6 @@ # ifdef WITH_CUDA_DYNLOAD # include "cuew.h" # else -# include "util/util_opengl.h" # include # include # endif diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index 6ccedcf54ef..70935598437 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -32,7 +32,6 @@ #include "util/util_half.h" #include "util/util_logging.h" #include "util/util_math.h" -#include "util/util_opengl.h" #include "util/util_string.h" #include "util/util_system.h" #include "util/util_time.h" diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 1882510cd70..3682b55049a 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -22,7 +22,6 @@ #include "util/util_foreach.h" #include "util/util_hash.h" #include "util/util_math.h" -#include "util/util_opengl.h" #include "util/util_time.h" #include "util/util_types.h" diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 823c34ed519..a8957b8def6 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -38,7 +38,6 @@ #include "util/util_function.h" #include "util/util_logging.h" #include "util/util_math.h" -#include "util/util_opengl.h" #include "util/util_task.h" #include "util/util_time.h" From 585998987ab1a948d3aee7e377764fe2900b4e03 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 24 Sep 2021 03:52:25 +0200 Subject: [PATCH 0200/1500] Cycles: some steps towards getting standalone compiling again Render output and display still need to be rewritten to work with the new system. --- intern/cycles/app/cycles_standalone.cpp | 40 ++++++++++++------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp index 270096d70b0..258e67b3459 100644 --- a/intern/cycles/app/cycles_standalone.cpp +++ b/intern/cycles/app/cycles_standalone.cpp @@ -53,7 +53,7 @@ struct Options { SessionParams session_params; bool quiet; bool show_help, interactive, pause; - string output_path; + string output_filepath; } options; static void session_print(const string &str) @@ -160,7 +160,7 @@ static void session_init() /* load scene */ scene_init(); - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); options.session->start(); } @@ -222,9 +222,7 @@ static void display_info(Progress &progress) static void display() { - static DeviceDrawParams draw_params = DeviceDrawParams(); - - options.session->draw(session_buffer_params(), draw_params); + options.session->draw(); display_info(options.session->progress); } @@ -254,7 +252,7 @@ static void motion(int x, int y, int button) options.session->scene->camera->need_flags_update = true; options.session->scene->camera->need_device_update = true; - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); } } @@ -271,7 +269,7 @@ static void resize(int width, int height) options.session->scene->camera->need_flags_update = true; options.session->scene->camera->need_device_update = true; - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); } } @@ -283,7 +281,7 @@ static void keyboard(unsigned char key) /* Reset */ else if (key == 'r') - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); /* Cancel */ else if (key == 27) // escape @@ -320,7 +318,7 @@ static void keyboard(unsigned char key) options.session->scene->camera->need_flags_update = true; options.session->scene->camera->need_device_update = true; - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); } /* Set Max Bounces */ @@ -346,7 +344,7 @@ static void keyboard(unsigned char key) options.session->scene->integrator->set_max_bounce(bounce); - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); } } #endif @@ -361,11 +359,13 @@ static int files_parse(int argc, const char *argv[]) static void options_parse(int argc, const char **argv) { - options.width = 0; - options.height = 0; + options.width = 1024; + options.height = 512; options.filepath = ""; options.session = NULL; options.quiet = false; + options.session_params.use_auto_tile = false; + options.session_params.tile_size = 0; /* device names */ string device_names = ""; @@ -411,7 +411,7 @@ static void options_parse(int argc, const char **argv) &options.session_params.samples, "Number of samples to render", "--output %s", - &options.output_path, + &options.output_filepath, "File path to write output image", "--threads %d", &options.session_params.threads, @@ -422,12 +422,9 @@ static void options_parse(int argc, const char **argv) "--height %d", &options.height, "Window height in pixel", - "--tile-width %d", - &options.session_params.tile_size.x, - "Tile width in pixels", - "--tile-height %d", - &options.session_params.tile_size.y, - "Tile height in pixels", + "--tile-size %d", + &options.session_params.tile_size, + "Tile size in pixels", "--list-devices", &list, "List information about all available devices", @@ -489,8 +486,9 @@ static void options_parse(int argc, const char **argv) options.session_params.background = true; #endif - /* Use progressive rendering */ - options.session_params.progressive = true; + if (options.session_params.tile_size > 0) { + options.session_params.use_auto_tile = true; + } /* find matching device */ DeviceType device_type = Device::type_from_string(devicename.c_str()); From c0db8e3b41e21bb6f8327a038601827a0d20a9cc Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 24 Sep 2021 16:16:16 +0200 Subject: [PATCH 0201/1500] Fix T91660: Cycles remaining render time does not take into account time limit --- intern/cycles/blender/blender_session.cpp | 5 +++-- intern/cycles/render/session.cpp | 19 ++++++++++++++++++ intern/cycles/render/session.h | 2 ++ intern/cycles/util/util_progress.h | 24 +++++++++++------------ 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 33092129ddf..ef1bc038a87 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -993,8 +993,9 @@ void BlenderSession::update_status_progress() get_status(status, substatus); get_progress(progress, total_time, render_time); - if (progress > 0) - remaining_time = (1.0 - (double)progress) * (render_time / (double)progress); + if (progress > 0) { + remaining_time = session->get_estimated_remaining_time(); + } if (background) { if (scene) diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index a8957b8def6..56d92fb0ad8 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -519,6 +519,25 @@ void Session::set_gpu_display(unique_ptr gpu_display) path_trace_->set_gpu_display(move(gpu_display)); } +double Session::get_estimated_remaining_time() const +{ + const float completed = progress.get_progress(); + if (completed == 0.0f) { + return 0.0; + } + + double total_time, render_time; + progress.get_time(total_time, render_time); + double remaining = (1.0 - (double)completed) * (render_time / (double)completed); + + const double time_limit = render_scheduler_.get_time_limit(); + if (time_limit != 0.0) { + remaining = min(remaining, max(time_limit - render_time, 0.0)); + } + + return remaining; +} + void Session::wait() { if (session_thread_) { diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index 5623604bfe8..e3056e7778b 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -145,6 +145,8 @@ class Session { void set_gpu_display(unique_ptr gpu_display); + double get_estimated_remaining_time() const; + void device_free(); /* Returns the rendering progress or 0 if no progress can be determined diff --git a/intern/cycles/util/util_progress.h b/intern/cycles/util/util_progress.h index dca8d3d0ab5..176ee11e1e9 100644 --- a/intern/cycles/util/util_progress.h +++ b/intern/cycles/util/util_progress.h @@ -100,7 +100,7 @@ class Progress { cancel = true; } - bool get_cancel() + bool get_cancel() const { if (!cancel && cancel_cb) cancel_cb(); @@ -108,7 +108,7 @@ class Progress { return cancel; } - string get_cancel_message() + string get_cancel_message() const { thread_scoped_lock lock(progress_mutex); return cancel_message; @@ -130,12 +130,12 @@ class Progress { cancel = true; } - bool get_error() + bool get_error() const { return error; } - string get_error_message() + string get_error_message() const { thread_scoped_lock lock(progress_mutex); return error_message; @@ -168,7 +168,7 @@ class Progress { } } - void get_time(double &total_time_, double &render_time_) + void get_time(double &total_time_, double &render_time_) const { thread_scoped_lock lock(progress_mutex); @@ -200,7 +200,7 @@ class Progress { total_pixel_samples = total_pixel_samples_; } - float get_progress() + float get_progress() const { thread_scoped_lock lock(progress_mutex); @@ -236,7 +236,7 @@ class Progress { } } - int get_current_sample() + int get_current_sample() const { thread_scoped_lock lock(progress_mutex); /* Note that the value here always belongs to the last tile that updated, @@ -244,13 +244,13 @@ class Progress { return current_tile_sample; } - int get_rendered_tiles() + int get_rendered_tiles() const { thread_scoped_lock lock(progress_mutex); return rendered_tiles; } - int get_denoised_tiles() + int get_denoised_tiles() const { thread_scoped_lock lock(progress_mutex); return denoised_tiles; @@ -300,7 +300,7 @@ class Progress { set_update(); } - void get_status(string &status_, string &substatus_) + void get_status(string &status_, string &substatus_) const { thread_scoped_lock lock(progress_mutex); @@ -330,8 +330,8 @@ class Progress { } protected: - thread_mutex progress_mutex; - thread_mutex update_mutex; + mutable thread_mutex progress_mutex; + mutable thread_mutex update_mutex; function update_cb; function cancel_cb; From 25d4de92fa763a5d1d72696cf1b4567a01099c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 24 Sep 2021 16:12:08 +0200 Subject: [PATCH 0202/1500] Cleanup: UUID, add some documenting comments No functional changes. --- source/blender/blenkernel/BKE_asset_catalog.hh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 51cfa8452c0..cf9d581b148 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -256,6 +256,7 @@ class AssetCatalog { static std::string sensible_simple_name_for_path(const CatalogPath &path); }; +/** Comparator for asset catalogs, ordering by (path, UUID). */ struct AssetCatalogPathCmp { bool operator()(const AssetCatalog *lhs, const AssetCatalog *rhs) const { @@ -266,6 +267,9 @@ struct AssetCatalogPathCmp { } }; +/** + * Set that stores catalogs ordered by (path, UUID). + * Being a set, duplicates are removed. The catalog's simple name is ignored in this. */ using AssetCatalogOrderedSet = std::set; } // namespace blender::bke From 90b410fe74b8710eaaa0372cb4f2d7da60f002a7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Sep 2021 16:38:43 +0200 Subject: [PATCH 0203/1500] Fix: field evaluation crash when the domain size is zero --- source/blender/functions/intern/field.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index fbd35c2c377..4f7ea8ec0ef 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -418,7 +418,10 @@ Vector evaluate_fields(ResourceScope &scope, build_multi_function_procedure_for_fields( procedure, scope, field_tree_info, constant_fields_to_evaluate); MFProcedureExecutor procedure_executor{"Procedure", procedure}; - MFParamsBuilder mf_params{procedure_executor, 1}; + /* Run the code below even when the mask is empty, so that outputs are properly prepared. + * Higher level code can detect this as well and just skip evaluating the field. */ + const int mask_size = mask.is_empty() ? 0 : 1; + MFParamsBuilder mf_params{procedure_executor, mask_size}; MFContextBuilder mf_context; /* Provide inputs to the procedure executor. */ @@ -435,11 +438,11 @@ Vector evaluate_fields(ResourceScope &scope, /* Use this to make sure that the value is destructed in the end. */ PartiallyInitializedArray &destruct_helper = scope.construct(); destruct_helper.buffer = buffer; - destruct_helper.mask = IndexRange(1); + destruct_helper.mask = IndexRange(mask_size); destruct_helper.type = &type; /* Pass output buffer to the procedure executor. */ - mf_params.add_uninitialized_single_output({type, buffer, 1}); + mf_params.add_uninitialized_single_output({type, buffer, mask_size}); /* Create virtual array that can be used after the procedure has been executed below. */ const int out_index = constant_field_indices[i]; From 4a2c63f4bd7e3cbb8cd80655270f3184d660e51d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Sep 2021 16:42:07 +0200 Subject: [PATCH 0204/1500] Nodes: hide socket value when input is a field implicitly --- source/blender/nodes/NOD_node_declaration.hh | 1 + source/blender/nodes/geometry/nodes/node_geo_set_position.cc | 2 +- source/blender/nodes/shader/nodes/node_shader_tex_noise.cc | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 32e63ffb2df..6780dfcad59 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -202,6 +202,7 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { /** The input supports a field and is a field by default when nothing is connected. */ Self &implicit_field() { + this->hide_value(); decl_->input_field_type_ = InputSocketFieldType::Implicit; return *(Self *)this; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 78bdac1b01b..be923fdccb0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -23,7 +23,7 @@ namespace blender::nodes { static void geo_node_set_position_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Position").hide_value().implicit_field(); + b.add_input("Position").implicit_field(); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index 5bf5e0f9876..6ffc8979815 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -26,7 +26,7 @@ namespace blender::nodes { static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").hide_value().implicit_field(); + b.add_input("Vector").implicit_field(); b.add_input("W").min(-1000.0f).max(1000.0f); b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); b.add_input("Detail").min(0.0f).max(16.0f).default_value(2.0f); From ef45399f3be0955ba8a8c678a5f5ed18ed8c7759 Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Fri, 24 Sep 2021 17:02:51 +0200 Subject: [PATCH 0205/1500] Nodes: initial support for socket tooltips This adds initial limited support for socket tooltips. It's limited in a couple of ways for now: * Only works when hovering over the socket shape, not when hovering over the value in the socket. * Only works for built-in nodes that already use the new node declaration system. This can later be extended to support pynodes. Those limitations are well worth it for now, given that the implementation is quite simple and the impact on usability is quite large. More complex updates to the layout system, that would allow showing socket tooltips in the nodes, can be done later. With the current implementation we can at least start writing tooltips for geometry nodes now. This commit already adds tooltips for the Cylinder node as an example. Differential Revision: https://developer.blender.org/D12607 --- .../blender/editors/space_node/node_draw.cc | 34 ++++++++++++++++--- source/blender/nodes/NOD_node_declaration.hh | 11 ++++++ .../nodes/node_geo_mesh_primitive_cylinder.cc | 18 ++++++++-- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index b57696cb676..499ccb8a889 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -79,6 +79,7 @@ #include "RNA_access.h" #include "NOD_geometry_nodes_eval_log.hh" +#include "NOD_node_declaration.hh" #include "FN_field_cpp_type.hh" @@ -1085,12 +1086,37 @@ static void node_socket_draw_nested(const bContext *C, but, [](bContext *C, void *argN, const char *UNUSED(tip)) { SocketTooltipData *data = (SocketTooltipData *)argN; - std::optional str = create_socket_inspection_string( + std::optional socket_inspection_str = create_socket_inspection_string( C, *data->ntree, *data->node, *data->socket); - if (str.has_value()) { - return BLI_strdup(str->c_str()); + + std::stringstream output; + if (data->node->declaration != nullptr) { + ListBase *list; + Span decl_list; + + if (data->socket->in_out == SOCK_IN) { + list = &data->node->inputs; + decl_list = data->node->declaration->inputs(); + } + else { + list = &data->node->outputs; + decl_list = data->node->declaration->outputs(); + } + + const int socket_index = BLI_findindex(list, data->socket); + const blender::nodes::SocketDeclaration &socket_decl = *decl_list[socket_index]; + blender::StringRef description = socket_decl.description(); + if (!description.is_empty()) { + output << TIP_(description.data()) << ".\n\n"; + } + + if (socket_inspection_str.has_value()) { + output << *socket_inspection_str; + return BLI_strdup(output.str().c_str()); + } } - return BLI_strdup(TIP_("The socket value has not been computed yet")); + output << TIP_("The socket value has not been computed yet"); + return BLI_strdup(output.str().c_str()); }, data, MEM_freeN); diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 6780dfcad59..8c7d343c001 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -119,6 +119,7 @@ class SocketDeclaration { protected: std::string name_; std::string identifier_; + std::string description_; bool hide_label_ = false; bool hide_value_ = false; bool is_multi_input_ = false; @@ -138,6 +139,7 @@ class SocketDeclaration { virtual bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const; StringRefNull name() const; + StringRefNull description() const; StringRefNull identifier() const; InputSocketFieldType input_field_type() const; @@ -186,6 +188,11 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { return *(Self *)this; } + Self &description(std::string value = "") + { + decl_->description_ = std::move(value); + return *(Self *)this; + } Self &no_muted_links(bool value = true) { decl_->no_mute_links_ = value; @@ -297,6 +304,10 @@ inline StringRefNull SocketDeclaration::identifier() const return identifier_; } +inline StringRefNull SocketDeclaration::description() const +{ + return description_; +} inline InputSocketFieldType SocketDeclaration::input_field_type() const { return input_field_type_; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index ed701c921ca..8c4defc3ca3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -29,9 +29,21 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) { - b.add_input("Vertices").default_value(32).min(3).max(4096); - b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input("Vertices") + .default_value(32) + .min(3) + .max(4096) + .description("The number of vertices around the circumference"); + b.add_input("Radius") + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description("The radius of the cylinder"); + b.add_input("Depth") + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description("The height of the cylinder on the Z axis"); b.add_output("Geometry"); } From bdb8ee9717c83d34783388e4c4c7fa73f2481fef Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 24 Sep 2021 17:46:19 +0200 Subject: [PATCH 0206/1500] Fix Cycles memory leak in baking, after recent changes --- intern/cycles/blender/blender_session.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index ef1bc038a87..c4a3f99ec2d 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -311,6 +311,8 @@ void BlenderSession::read_render_tile() for (BL::RenderPass &b_pass : b_rlay.passes) { session->set_render_tile_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect()); } + + b_engine.end_result(b_rr, false, false, false); } void BlenderSession::write_render_tile() From 5c0017e85a75ad004ef5f4944828074a7fa95f21 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Fri, 24 Sep 2021 10:59:52 -0500 Subject: [PATCH 0207/1500] Geometry Nodes: Special string characters node This patch adds a new node called "Special Characters" with two string outputs: "Line Break" and "Tab". This is necessary because the newline character cannot be easily typed with a keyboard, but is necessary for the string to curve node. Differential Revision: https://developer.blender.org/D12620 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_function.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_fn_input_special_characters.cc | 74 +++++++++++++++++++ 7 files changed, 80 insertions(+) create mode 100644 source/blender/nodes/function/nodes/node_fn_input_special_characters.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 1884f53460e..36c6f0061e8 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -606,6 +606,7 @@ geometry_node_categories = [ NodeItem("FunctionNodeStringSubstring"), NodeItem("FunctionNodeValueToString"), NodeItem("GeometryNodeStringJoin"), + NodeItem("FunctionNodeInputSpecialCharacters"), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 6f2d0d9dd90..bd969400f17 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1517,6 +1517,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_VALUE_TO_STRING 1210 #define FN_NODE_STRING_LENGTH 1211 #define FN_NODE_STRING_SUBSTRING 1212 +#define FN_NODE_INPUT_SPECIAL_CHARACTERS 1213 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 78789b2e771..891d6c05a19 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5812,6 +5812,7 @@ static void registerFunctionNodes() register_node_type_fn_boolean_math(); register_node_type_fn_float_compare(); register_node_type_fn_float_to_int(); + register_node_type_fn_input_special_characters(); register_node_type_fn_input_string(); register_node_type_fn_input_vector(); register_node_type_fn_random_float(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 65f0999c63b..f8898fbda93 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -136,6 +136,7 @@ set(SRC function/nodes/node_fn_boolean_math.cc function/nodes/node_fn_float_compare.cc function/nodes/node_fn_float_to_int.cc + function/nodes/node_fn_input_special_characters.cc function/nodes/node_fn_input_string.cc function/nodes/node_fn_input_vector.cc function/nodes/node_fn_random_float.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index a67458418f2..b922b36686e 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -23,6 +23,7 @@ extern "C" { void register_node_type_fn_boolean_math(void); void register_node_type_fn_float_compare(void); void register_node_type_fn_float_to_int(void); +void register_node_type_fn_input_special_characters(void); void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); void register_node_type_fn_random_float(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index b1c4f1d6367..170d615b43f 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -265,6 +265,7 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") +DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "") DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") diff --git a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc new file mode 100644 index 00000000000..11c64d3f694 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc @@ -0,0 +1,74 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_input_special_characters_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Line Break"); + b.add_output("Tab"); +}; + +class MF_SpecialCharacters : public fn::MultiFunction { + public: + MF_SpecialCharacters() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Special Characters"}; + signature.single_output("Line Break"); + signature.single_output("Tab"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + MutableSpan lb = params.uninitialized_single_output(0, "Line Break"); + MutableSpan tab = params.uninitialized_single_output(1, "Tab"); + + for (const int i : mask) { + new (&lb[i]) std::string("\n"); + new (&tab[i]) std::string("\t"); + } + } +}; + +static void fn_node_input_special_characters_build_multi_function( + NodeMultiFunctionBuilder &builder) +{ + static MF_SpecialCharacters special_characters_fn; + builder.set_matching_fn(special_characters_fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_input_special_characters() +{ + static bNodeType ntype; + + fn_node_type_base( + &ntype, FN_NODE_INPUT_SPECIAL_CHARACTERS, "Special Characters", NODE_CLASS_INPUT, 0); + ntype.declare = blender::nodes::fn_node_input_special_characters_declare; + ntype.build_multi_function = + blender::nodes::fn_node_input_special_characters_build_multi_function; + nodeRegisterType(&ntype); +} From be16794ba17246eb035bdda42bb5e69d6bf5fa40 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Fri, 24 Sep 2021 12:41:49 -0500 Subject: [PATCH 0208/1500] Geometry Nodes: String to Curves Node This commit adds a node that generates a text paragraph as curve instances. The inputs on the node control the overall shape of the paragraph, and other nodes can be used to move the individual instances afterwards. To output more than one line, the "Special Characters" node can be used. The node outputs instances instead of real geometry so that it doesn't have to duplicate work for every character afterwards. This is much more efficient, because all of the curve evaluation and nodes like fill curve don't have to repeat the same calculation for every instance of the same character. In the future, the instances component will support attributes, and the node can output attribute fields like "Word Index" and "Line Index". Differential Revision: https://developer.blender.org/D11522 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_font.h | 9 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/font.c | 20 +- source/blender/blenkernel/intern/node.cc | 1 + source/blender/blenlib/BLI_float4x4.hh | 7 + .../intern/builder/deg_builder_nodes.cc | 15 + .../intern/builder/deg_builder_nodes.h | 2 + .../intern/builder/deg_builder_relations.cc | 15 + .../intern/builder/deg_builder_relations.h | 2 + source/blender/makesdna/DNA_node_types.h | 32 ++ source/blender/makesrna/intern/rna_nodetree.c | 114 +++++++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_geo_string_to_curves.cc | 307 ++++++++++++++++++ 16 files changed, 519 insertions(+), 10 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 36c6f0061e8..715874545a5 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -607,6 +607,7 @@ geometry_node_categories = [ NodeItem("FunctionNodeValueToString"), NodeItem("GeometryNodeStringJoin"), NodeItem("FunctionNodeInputSpecialCharacters"), + NodeItem("GeometryNodeStringToCurves"), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), diff --git a/source/blender/blenkernel/BKE_font.h b/source/blender/blenkernel/BKE_font.h index 522d3843bb2..827ae1b6a0f 100644 --- a/source/blender/blenkernel/BKE_font.h +++ b/source/blender/blenkernel/BKE_font.h @@ -85,6 +85,15 @@ bool BKE_vfont_to_curve_ex(struct Object *ob, struct CharTrans **r_chartransdata); bool BKE_vfont_to_curve_nubase(struct Object *ob, int mode, struct ListBase *r_nubase); bool BKE_vfont_to_curve(struct Object *ob, int mode); +void BKE_vfont_build_char(struct Curve *cu, + struct ListBase *nubase, + unsigned int character, + struct CharInfo *info, + float ofsx, + float ofsy, + float rot, + int charidx, + const float fsize); int BKE_vfont_select_get(struct Object *ob, int *r_start, int *r_end); void BKE_vfont_select_clamp(struct Object *ob); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index bd969400f17..90285d9fc7c 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1501,6 +1501,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_PARAMETER 1088 #define GEO_NODE_CURVE_FILLET 1089 #define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES 1090 +#define GEO_NODE_STRING_TO_CURVES 1091 /** \} */ diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index aa13f86523a..1053b727cbc 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -490,15 +490,15 @@ static void build_underline(Curve *cu, mul_v2_fl(bp[3].vec, font_size); } -static void buildchar(Curve *cu, - ListBase *nubase, - unsigned int character, - CharInfo *info, - float ofsx, - float ofsy, - float rot, - int charidx, - const float fsize) +void BKE_vfont_build_char(Curve *cu, + ListBase *nubase, + unsigned int character, + CharInfo *info, + float ofsx, + float ofsy, + float rot, + int charidx, + const float fsize) { VFontData *vfd = vfont_get_data(which_vfont(cu, info)); if (!vfd) { @@ -1525,7 +1525,7 @@ static bool vfont_to_curve(Object *ob, } /* We do not want to see any character for \n or \r */ if (cha != '\n') { - buildchar(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size); + BKE_vfont_build_char(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size); } if ((info->flag & CU_CHINFO_UNDERLINE) && (cha != '\n')) { diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 891d6c05a19..bf7c47e9cf0 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5799,6 +5799,7 @@ static void registerGeometryNodes() register_node_type_geo_material_selection(); register_node_type_geo_separate_components(); register_node_type_geo_set_position(); + register_node_type_geo_string_to_curves(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); register_node_type_geo_transform(); diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index 347ce2caa34..14e61d53845 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -45,6 +45,13 @@ struct float4x4 { return mat; } + static float4x4 from_location(const float3 location) + { + float4x4 mat = float4x4::identity(); + copy_v3_v3(mat.values[3], location); + return mat; + } + static float4x4 from_normalized_axis_data(const float3 location, const float3 forward, const float3 up) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 36c6b56caae..463bb02afa4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -63,6 +63,7 @@ #include "DNA_sound_types.h" #include "DNA_speaker_types.h" #include "DNA_texture_types.h" +#include "DNA_vfont_types.h" #include "DNA_world_types.h" #include "BKE_action.h" @@ -1764,6 +1765,9 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) else if (id_type == ID_MC) { build_movieclip((MovieClip *)id); } + else if (id_type == ID_VF) { + build_vfont((VFont *)id); + } else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { bNodeTree *group_ntree = (bNodeTree *)id; build_nodetree(group_ntree); @@ -2015,6 +2019,17 @@ void DepsgraphNodeBuilder::build_simulation(Simulation *simulation) }); } +void DepsgraphNodeBuilder::build_vfont(VFont *vfont) +{ + if (built_map_.checkIsBuiltAndTag(vfont)) { + return; + } + build_parameters(&vfont->id); + build_idproperties(vfont->id.properties); + add_operation_node( + &vfont->id, NodeType::GENERIC_DATABLOCK, OperationCode::GENERIC_DATABLOCK_UPDATE); +} + static bool seq_node_build_cb(Sequence *seq, void *user_data) { DepsgraphNodeBuilder *nb = (DepsgraphNodeBuilder *)user_data; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 2378f3fc100..d31290ecbff 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -55,6 +55,7 @@ struct Scene; struct Simulation; struct Speaker; struct Tex; +struct VFont; struct World; struct bAction; struct bArmature; @@ -235,6 +236,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_scene_sequencer(Scene *scene); virtual void build_scene_audio(Scene *scene); virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer); + virtual void build_vfont(VFont *vfont); /* Per-ID information about what was already in the dependency graph. * Allows to re-use certain values, to speed up following evaluation. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 28cfc5a9e1a..55e8c5ed033 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -65,6 +65,7 @@ #include "DNA_sound_types.h" #include "DNA_speaker_types.h" #include "DNA_texture_types.h" +#include "DNA_vfont_types.h" #include "DNA_volume_types.h" #include "DNA_world_types.h" @@ -2496,6 +2497,11 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) OperationKey clip_key(id, NodeType::PARAMETERS, OperationCode::MOVIECLIP_EVAL); add_relation(clip_key, shading_key, "Clip -> Node"); } + else if (id_type == ID_VF) { + build_vfont((VFont *)id); + ComponentKey vfont_key(id, NodeType::GENERIC_DATABLOCK); + add_relation(vfont_key, shading_key, "VFont -> Node"); + } else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { bNodeTree *group_ntree = (bNodeTree *)id; build_nodetree(group_ntree); @@ -2842,6 +2848,15 @@ void DepsgraphRelationBuilder::build_scene_speakers(Scene * /*scene*/, ViewLayer } } +void DepsgraphRelationBuilder::build_vfont(VFont *vfont) +{ + if (built_map_.checkIsBuiltAndTag(vfont)) { + return; + } + build_parameters(&vfont->id); + build_idproperties(vfont->id.properties); +} + void DepsgraphRelationBuilder::build_copy_on_write_relations() { for (IDNode *id_node : graph_->id_nodes) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 1ad61c25305..f0393544511 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -72,6 +72,7 @@ struct Simulation; struct Speaker; struct Tex; struct ViewLayer; +struct VFont; struct World; struct bAction; struct bArmature; @@ -296,6 +297,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_scene_sequencer(Scene *scene); virtual void build_scene_audio(Scene *scene); virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer); + virtual void build_vfont(VFont *vfont); virtual void build_nested_datablock(ID *owner, ID *id); virtual void build_nested_nodetree(ID *owner, bNodeTree *ntree); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 6e4737b7d07..35a2dba627a 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1502,6 +1502,16 @@ typedef struct NodeGeometryAttributeCapture { int8_t domain; } NodeGeometryAttributeCapture; +typedef struct NodeGeometryStringToCurves { + /* GeometryNodeStringToCurvesOverflowMode */ + uint8_t overflow; + /* GeometryNodeStringToCurvesAlignXMode */ + uint8_t align_x; + /* GeometryNodeStringToCurvesAlignYMode */ + uint8_t align_y; + char _pad[1]; +} NodeGeometryStringToCurves; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -2098,6 +2108,28 @@ typedef enum GeometryNodeCurveFillMode { GEO_NODE_CURVE_FILL_MODE_NGONS = 1, } GeometryNodeCurveFillMode; +typedef enum GeometryNodeStringToCurvesOverflowMode { + GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW = 0, + GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT = 1, + GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE = 2, +} GeometryNodeStringToCurvesOverflowMode; + +typedef enum GeometryNodeStringToCurvesAlignXMode { + GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT = 0, + GEO_NODE_STRING_TO_CURVES_ALIGN_X_CENTER = 1, + GEO_NODE_STRING_TO_CURVES_ALIGN_X_RIGHT = 2, + GEO_NODE_STRING_TO_CURVES_ALIGN_X_JUSTIFY = 3, + GEO_NODE_STRING_TO_CURVES_ALIGN_X_FLUSH = 4, +} GeometryNodeStringToCurvesAlignXMode; + +typedef enum GeometryNodeStringToCurvesAlignYMode { + GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE = 0, + GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP = 1, + GEO_NODE_STRING_TO_CURVES_ALIGN_Y_MIDDLE = 2, + GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM_BASELINE = 3, + GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM = 4, +} GeometryNodeStringToCurvesAlignYMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index dcac157d1a2..333f1584800 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10425,6 +10425,120 @@ static void def_geo_attribute_capture(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_string_to_curves(StructRNA *srna) +{ + static const EnumPropertyItem rna_node_geometry_string_to_curves_overflow_items[] = { + {GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW, + "OVERFLOW", + ICON_NONE, + "Overflow", + "Let the text use more space than the specified height"}, + {GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT, + "SCALE_TO_FIT", + ICON_NONE, + "Scale To Fit", + "Scale the text size to fit inside the width and height"}, + {GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE, + "TRUNCATE", + ICON_NONE, + "Truncate", + "Only output curves that fit within the width and height. Output the remainder to the " + "\"Remainder\" output"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem rna_node_geometry_string_to_curves_align_x_items[] = { + {GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT, + "LEFT", + ICON_ALIGN_LEFT, + "Left", + "Align text to the left"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_X_CENTER, + "CENTER", + ICON_ALIGN_CENTER, + "Center", + "Align text to the center"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_X_RIGHT, + "RIGHT", + ICON_ALIGN_RIGHT, + "Right", + "Align text to the right"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_X_JUSTIFY, + "JUSTIFY", + ICON_ALIGN_JUSTIFY, + "Justify", + "Align text to the left and the right"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_X_FLUSH, + "FLUSH", + ICON_ALIGN_FLUSH, + "Flush", + "Align text to the left and the right, with equal character spacing"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem rna_node_geometry_string_to_curves_align_y_items[] = { + {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE, + "TOP_BASELINE", + ICON_ALIGN_TOP, + "Top Baseline", + "Align text to the top baseline"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP, + "TOP", + ICON_ALIGN_TOP, + "Top", + "Align text to the top"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_MIDDLE, + "MIDDLE", + ICON_ALIGN_MIDDLE, + "Middle", + "Align text to the middle"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM_BASELINE, + "BOTTOM_BASELINE", + ICON_ALIGN_BOTTOM, + "Bottom Baseline", + "Align text to the bottom baseline"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM, + "BOTTOM", + ICON_ALIGN_BOTTOM, + "Bottom", + "Align text to the bottom"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + prop = RNA_def_property(srna, "font", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "id"); + RNA_def_property_struct_type(prop, "VectorFont"); + RNA_def_property_ui_text(prop, "Font", "Font of the text. Falls back to the UI font by default"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + RNA_def_struct_sdna_from(srna, "NodeGeometryStringToCurves", "storage"); + + prop = RNA_def_property(srna, "overflow", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "overflow"); + RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_overflow_items); + RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW); + RNA_def_property_ui_text(prop, "Overflow", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "align_x", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "align_x"); + RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_align_x_items); + RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT); + RNA_def_property_ui_text(prop, "Align X", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "align_y", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "align_y"); + RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_align_y_items); + RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE); + RNA_def_property_ui_text(prop, "Align Y", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index f8898fbda93..5056c0889bc 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -232,6 +232,7 @@ set(SRC geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_set_position.cc geometry/nodes/node_geo_string_join.cc + geometry/nodes/node_geo_string_to_curves.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc geometry/nodes/node_geo_transform.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 9cd3365bd91..823c48f71e3 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -115,6 +115,7 @@ void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_separate_components(void); void register_node_type_geo_set_position(void); void register_node_type_geo_string_join(void); +void register_node_type_geo_string_to_curves(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); void register_node_type_geo_transform(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 170d615b43f..6d71cb8d0b6 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -359,6 +359,7 @@ DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", Realiz DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "String Join", "") +DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc new file mode 100644 index 00000000000..0bd050ac3d1 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -0,0 +1,307 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "DNA_curve_types.h" +#include "DNA_vfont_types.h" + +#include "BKE_curve.h" +#include "BKE_font.h" +#include "BKE_spline.hh" + +#include "BLI_hash.h" +#include "BLI_string_utf8.h" +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b) +{ + b.add_input("String"); + b.add_input("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input("Character Spacing") + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input("Word Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input("Line Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input("Text Box Width").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input("Text Box Height").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output("Curves"); + b.add_output("Remainder"); +} + +static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiTemplateID(layout, + C, + ptr, + "font", + nullptr, + "FONT_OT_open", + "FONT_OT_unlink", + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + uiItemR(layout, ptr, "overflow", 0, "", ICON_NONE); + uiItemR(layout, ptr, "align_x", 0, "", ICON_NONE); + uiItemR(layout, ptr, "align_y", 0, "", ICON_NONE); +} + +static void geo_node_string_to_curves_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryStringToCurves *data = (NodeGeometryStringToCurves *)MEM_callocN( + sizeof(NodeGeometryStringToCurves), __func__); + + data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW; + data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT; + data->align_y = GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE; + node->storage = data; + node->id = (ID *)BKE_vfont_builtin_get(); +} + +static void geo_node_string_to_curves_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryStringToCurves *storage = (const NodeGeometryStringToCurves *)node->storage; + const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode) + storage->overflow; + bNodeSocket *socket_remainder = ((bNodeSocket *)node->outputs.first)->next; + nodeSetSocketAvailability(socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE); + + bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last; + bNodeSocket *width_socket = height_socket->prev; + nodeSetSocketAvailability(height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW); + node_sock_label(width_socket, + overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? N_("Max Width") : + N_("Text Box Width")); +} + +struct TextLayout { + /* Position of each character. */ + Vector positions; + + /* The text that fit into the text box, with newline character sequences replaced. */ + std::string text; + + /* The text that didn't fit into the text box in 'Truncate' mode. May be empty. */ + std::string truncated_text; + + /* Font size could be modified if in 'Scale to fit'-mode. */ + float final_font_size; +}; + +static TextLayout get_text_layout(GeoNodeExecParams ¶ms) +{ + TextLayout layout; + layout.text = params.extract_input("String"); + if (layout.text.empty()) { + return {}; + } + + const NodeGeometryStringToCurves &storage = + *(const NodeGeometryStringToCurves *)params.node().storage; + const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode) + storage.overflow; + const GeometryNodeStringToCurvesAlignXMode align_x = (GeometryNodeStringToCurvesAlignXMode) + storage.align_x; + const GeometryNodeStringToCurvesAlignYMode align_y = (GeometryNodeStringToCurvesAlignYMode) + storage.align_y; + + const float font_size = std::max(params.extract_input("Size"), 0.0f); + const float char_spacing = params.extract_input("Character Spacing"); + const float word_spacing = params.extract_input("Word Spacing"); + const float line_spacing = params.extract_input("Line Spacing"); + const float textbox_w = params.extract_input("Text Box Width"); + const float textbox_h = overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? + 0.0f : + params.extract_input("Text Box Height"); + VFont *vfont = (VFont *)params.node().id; + + Curve cu = {nullptr}; + cu.type = OB_FONT; + /* Set defaults */ + cu.resolu = 12; + cu.smallcaps_scale = 0.75f; + cu.wordspace = 1.0f; + /* Set values from inputs */ + cu.spacemode = align_x; + cu.align_y = align_y; + cu.fsize = font_size; + cu.spacing = char_spacing; + cu.wordspace = word_spacing; + cu.linedist = line_spacing; + cu.vfont = vfont; + cu.overflow = overflow; + cu.tb = (TextBox *)MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), __func__); + cu.tb->w = textbox_w; + cu.tb->h = textbox_h; + cu.totbox = 1; + size_t len_bytes; + size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes); + cu.len_char32 = len_chars; + cu.len = len_bytes; + cu.pos = len_chars; + /* The reason for the additional character here is unknown, but reflects other code elsewhere. */ + cu.str = (char *)MEM_mallocN(len_bytes + sizeof(char32_t), __func__); + cu.strinfo = (CharInfo *)MEM_callocN((len_chars + 1) * sizeof(CharInfo), __func__); + BLI_strncpy(cu.str, layout.text.c_str(), len_bytes + 1); + + struct CharTrans *chartransdata = nullptr; + int text_len; + bool text_free; + const char32_t *r_text = nullptr; + /* Mode FO_DUPLI used because it doesn't create curve splines. */ + BKE_vfont_to_curve_ex( + nullptr, &cu, FO_DUPLI, nullptr, &r_text, &text_len, &text_free, &chartransdata); + + if (text_free) { + MEM_freeN((void *)r_text); + } + + Span info{cu.strinfo, text_len}; + layout.final_font_size = cu.fsize_realtime; + layout.positions.reserve(text_len); + + for (const int i : IndexRange(text_len)) { + CharTrans &ct = chartransdata[i]; + layout.positions.append(float2(ct.xof, ct.yof) * layout.final_font_size); + + if ((info[i].flag & CU_CHINFO_OVERFLOW) && (cu.overflow == CU_OVERFLOW_TRUNCATE)) { + const int offset = BLI_str_utf8_offset_from_index(layout.text.c_str(), i + 1); + layout.truncated_text = layout.text.substr(offset); + layout.text = layout.text.substr(0, offset); + break; + } + } + + MEM_SAFE_FREE(chartransdata); + MEM_SAFE_FREE(cu.str); + MEM_SAFE_FREE(cu.strinfo); + MEM_SAFE_FREE(cu.tb); + + return layout; +} + +/* Returns a mapping of UTF-32 character code to instance handle. */ +static Map create_curve_instances(GeoNodeExecParams ¶ms, + const float fontsize, + const std::u32string &charcodes, + InstancesComponent &instance_component) +{ + VFont *vfont = (VFont *)params.node().id; + Map handles; + + for (int i : IndexRange(charcodes.length())) { + if (handles.contains(charcodes[i])) { + continue; + } + Curve cu = {nullptr}; + cu.type = OB_FONT; + cu.resolu = 12; + cu.vfont = vfont; + CharInfo charinfo = {0}; + charinfo.mat_nr = 1; + + BKE_vfont_build_char(&cu, &cu.nurb, charcodes[i], &charinfo, 0, 0, 0, i, 1); + std::unique_ptr curve_eval = curve_eval_from_dna_curve(cu); + BKE_nurbList_free(&cu.nurb); + float4x4 size_matrix = float4x4::identity(); + size_matrix.apply_scale(fontsize); + curve_eval->transform(size_matrix); + + GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release()); + handles.add_new(charcodes[i], instance_component.add_reference(std::move(geometry_set_curve))); + } + return handles; +} + +static void add_instances_from_handles(InstancesComponent &instances, + const Map &char_handles, + const std::u32string &charcodes, + const Span positions) +{ + instances.resize(positions.size()); + MutableSpan handles = instances.instance_reference_handles(); + MutableSpan transforms = instances.instance_transforms(); + MutableSpan instance_ids = instances.instance_ids(); + + threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) { + for (const int i : range) { + handles[i] = char_handles.lookup(charcodes[i]); + transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0}); + instance_ids[i] = i; + } + }); +} + +static void geo_node_string_to_curves_exec(GeoNodeExecParams params) +{ + TextLayout layout = get_text_layout(params); + + const NodeGeometryStringToCurves &storage = + *(const NodeGeometryStringToCurves *)params.node().storage; + if (storage.overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE) { + params.set_output("Remainder", std::move(layout.truncated_text)); + } + + if (layout.positions.size() == 0) { + params.set_output("Curves", GeometrySet()); + return; + } + + /* Convert UTF-8 encoded string to UTF-32. */ + std::wstring_convert, char32_t> converter; + std::u32string utf32_text = converter.from_bytes(layout.text); + + /* Create and add instances. */ + GeometrySet geometry_set_out; + InstancesComponent &instances = geometry_set_out.get_component_for_write(); + Map char_handles = create_curve_instances( + params, layout.final_font_size, utf32_text, instances); + add_instances_from_handles(instances, char_handles, utf32_text, layout.positions); + + params.set_output("Curves", std::move(geometry_set_out)); +} + +} // namespace blender::nodes + +void register_node_type_geo_string_to_curves() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_string_to_curves_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_string_to_curves_exec; + node_type_init(&ntype, blender::nodes::geo_node_string_to_curves_init); + node_type_update(&ntype, blender::nodes::geo_node_string_to_curves_update); + node_type_size(&ntype, 190, 120, 700); + node_type_storage(&ntype, + "NodeGeometryStringToCurves", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_string_to_curves_layout; + nodeRegisterType(&ntype); +} From c87e6b23be5683e006d75a69281ecf64dc86c7d6 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 24 Sep 2021 20:12:07 +0200 Subject: [PATCH 0209/1500] Assets: Read catalogs immediately when loading a library Until now, the asset catalogs would only show up after all assets from the library were loaded. Now the catalogs are read first, which makes them appear pretty much immediately. This makes the UI more responsive and feel less heavy. I added a dedicated file-list type for asset libraries now. While not necessarily needed, I prefer that so asset library specific stuff can be handled in there. --- .../editors/asset/intern/asset_list.cc | 2 +- source/blender/editors/space_file/filelist.c | 67 +++++++++++++------ source/blender/editors/space_file/filelist.h | 5 +- source/blender/editors/space_file/filesel.c | 3 +- source/blender/makesdna/DNA_space_types.h | 3 + 5 files changed, 58 insertions(+), 22 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index 57c25e1614b..cda117533cd 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -390,7 +390,7 @@ std::optional AssetListStorage::asset_library_reference_to_file { switch (library_reference.type) { case ASSET_LIBRARY_CUSTOM: - return FILE_LOADLIB; + return FILE_ASSET_LIBRARY; case ASSET_LIBRARY_LOCAL: return FILE_MAIN_ASSET; } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 5c0976e18f2..194e577e19e 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -473,6 +473,10 @@ static void filelist_readjob_dir(struct FileListReadJob *job_params, short *stop, short *do_update, float *progress); +static void filelist_readjob_asset_library(struct FileListReadJob *job_params, + short *stop, + short *do_update, + float *progress); static void filelist_readjob_main_assets(struct FileListReadJob *job_params, short *stop, short *do_update, @@ -1725,6 +1729,11 @@ void filelist_settype(FileList *filelist, short type) filelist->read_job_fn = filelist_readjob_lib; filelist->filter_fn = is_filtered_lib; break; + case FILE_ASSET_LIBRARY: + filelist->check_dir_fn = filelist_checkdir_lib; + filelist->read_job_fn = filelist_readjob_asset_library; + filelist->filter_fn = is_filtered_lib; + break; case FILE_MAIN_ASSET: filelist->check_dir_fn = filelist_checkdir_main_assets; filelist->read_job_fn = filelist_readjob_main_assets; @@ -1741,7 +1750,10 @@ void filelist_settype(FileList *filelist, short type) filelist->flags |= FL_FORCE_RESET; } -void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection) +void filelist_clear_ex(struct FileList *filelist, + const bool do_asset_library, + const bool do_cache, + const bool do_selection) { if (!filelist) { return; @@ -1761,7 +1773,7 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const boo BLI_ghash_clear(filelist->selection_state, NULL, NULL); } - if (filelist->asset_library != NULL) { + if (do_asset_library && (filelist->asset_library != NULL)) { /* There is no way to refresh the catalogs stored by the AssetLibrary struct, so instead of * "clearing" it, the entire struct is freed. It will be reallocated when needed. */ BKE_asset_library_free(filelist->asset_library); @@ -1771,7 +1783,7 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const boo void filelist_clear(struct FileList *filelist) { - filelist_clear_ex(filelist, true, true); + filelist_clear_ex(filelist, true, true, true); } void filelist_free(struct FileList *filelist) @@ -1782,7 +1794,7 @@ void filelist_free(struct FileList *filelist) } /* No need to clear cache & selection_state, we free them anyway. */ - filelist_clear_ex(filelist, false, false); + filelist_clear_ex(filelist, true, false, false); filelist_cache_free(&filelist->filelist_cache); if (filelist->selection_state) { @@ -3368,13 +3380,6 @@ static void filelist_readjob_do(const bool do_lib, BLI_stack_discard(todo_dirs); } BLI_stack_free(todo_dirs); - - /* Check whether assets catalogs need to be loaded. */ - if (job_params->filelist->asset_library_ref != NULL) { - /* Load asset catalogs, into the temp filelist for thread-safety. - * #filelist_readjob_endjob() will move it into the real filelist. */ - job_params->tmp_filelist->asset_library = BKE_asset_library_load(filelist->filelist.root); - } } static void filelist_readjob_dir(FileListReadJob *job_params, @@ -3393,6 +3398,28 @@ static void filelist_readjob_lib(FileListReadJob *job_params, filelist_readjob_do(true, job_params, stop, do_update, progress); } +static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params, short *do_update) +{ + FileList *tmp_filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + + /* Check whether assets catalogs need to be loaded. */ + if (job_params->filelist->asset_library_ref != NULL) { + /* Load asset catalogs, into the temp filelist for thread-safety. + * #filelist_readjob_endjob() will move it into the real filelist. */ + tmp_filelist->asset_library = BKE_asset_library_load(tmp_filelist->filelist.root); + *do_update = true; + } +} + +static void filelist_readjob_asset_library(FileListReadJob *job_params, + short *stop, + short *do_update, + float *progress) +{ + filelist_readjob_load_asset_library_data(job_params, do_update); + filelist_readjob_lib(job_params, stop, do_update, progress); +} + static void filelist_readjob_main(FileListReadJob *job_params, short *stop, short *do_update, @@ -3414,6 +3441,8 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params, BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + filelist_readjob_load_asset_library_data(job_params, do_update); + /* A valid, but empty directory from now. */ filelist->filelist.nbr_entries = 0; @@ -3505,11 +3534,17 @@ static void filelist_readjob_update(void *flrjv) flrj->tmp_filelist->filelist.nbr_entries = 0; } + if (flrj->tmp_filelist->asset_library) { + flrj->filelist->asset_library = flrj->tmp_filelist->asset_library; + flrj->tmp_filelist->asset_library = NULL; /* MUST be NULL to avoid double-free. */ + } + BLI_mutex_unlock(&flrj->lock); if (new_nbr_entries) { - /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! */ - filelist_clear_ex(flrj->filelist, true, false); + /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! Keep + * the asset library data we just read. */ + filelist_clear_ex(flrj->filelist, false, true, false); flrj->filelist->flags |= (FL_NEED_SORTING | FL_NEED_FILTERING); } @@ -3526,12 +3561,6 @@ static void filelist_readjob_endjob(void *flrjv) /* In case there would be some dangling update... */ filelist_readjob_update(flrjv); - /* Move ownership of the asset library from the temporary list to the true filelist. */ - BLI_assert_msg(flrj->filelist->asset_library == NULL, - "asset library should not already have been allocated"); - flrj->filelist->asset_library = flrj->tmp_filelist->asset_library; - flrj->tmp_filelist->asset_library = NULL; /* MUST be NULL to avoid double-free. */ - flrj->filelist->flags &= ~FL_IS_PENDING; flrj->filelist->flags |= FL_IS_READY; } diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 1fb05e0f9ac..b51ceee4aa0 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -86,7 +86,10 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m struct FileList *filelist_new(short type); void filelist_settype(struct FileList *filelist, short type); void filelist_clear(struct FileList *filelist); -void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection); +void filelist_clear_ex(struct FileList *filelist, + const bool do_asset_library, + const bool do_cache, + const bool do_selection); void filelist_free(struct FileList *filelist); const char *filelist_dir(struct FileList *filelist); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index f7bdb4326a5..a741f2582ee 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -440,7 +440,8 @@ static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir)); break; } - base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB; + base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : + FILE_ASSET_LIBRARY; } void fileselect_refresh_params(SpaceFile *sfile) diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index cebe8fc61fe..075575775ed 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -965,7 +965,10 @@ enum eFileDetails { typedef enum eFileSelectType { FILE_LOADLIB = 1, FILE_MAIN = 2, + /** Load assets from #Main. */ FILE_MAIN_ASSET = 3, + /** Load assets of an asset library containing external files. */ + FILE_ASSET_LIBRARY = 4, FILE_UNIX = 8, FILE_BLENDER = 8, /* don't display relative paths */ From 536f9eb82e07778565b789f7408f3ce81aa6d675 Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Fri, 24 Sep 2021 14:03:42 -0500 Subject: [PATCH 0210/1500] Geometry Nodes: Random Value Node This node replaces the deprecated Attribute Randomize node, populating a vector, float, integer or boolean field with random values. Vector, float, and integer have min/max settings, which are also field aware. The boolean type has a probability value for controlling what portion of the output should be true. All four types have a field seed input which is implicitly driven by the index, otherwise, all values would be the same "random" value. The Random Float node is now deprecated like other nodes, since it is redundant with this node. Differential Revision: https://developer.blender.org/D12603 --- release/scripts/startup/nodeitems_builtins.py | 4 +- source/blender/blenkernel/BKE_node.h | 3 +- source/blender/blenkernel/intern/node.cc | 6 +- .../blenloader/intern/versioning_290.c | 2 +- source/blender/makesdna/DNA_node_types.h | 5 + source/blender/makesrna/intern/rna_nodetree.c | 28 ++ .../modifiers/intern/MOD_nodes_evaluator.cc | 4 + source/blender/nodes/CMakeLists.txt | 6 +- source/blender/nodes/NOD_function.h | 4 +- source/blender/nodes/NOD_geometry.h | 2 +- source/blender/nodes/NOD_static_types.h | 4 +- .../{ => legacy}/node_fn_random_float.cc | 12 +- .../function/nodes/node_fn_random_value.cc | 299 ++++++++++++++++++ .../node_geo_attribute_randomize.cc | 26 +- 14 files changed, 376 insertions(+), 29 deletions(-) rename source/blender/nodes/function/nodes/{ => legacy}/node_fn_random_float.cc (86%) create mode 100644 source/blender/nodes/function/nodes/node_fn_random_value.cc rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_randomize.cc (92%) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 715874545a5..e658706a946 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -554,9 +554,10 @@ geometry_node_categories = [ NodeItem("GeometryNodeRealizeInstances", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_INPUT", "Input", items=[ + NodeItem("FunctionNodeLegacyRandomFloat", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeObjectInfo"), NodeItem("GeometryNodeCollectionInfo"), - NodeItem("FunctionNodeRandomFloat"), NodeItem("ShaderNodeValue"), NodeItem("FunctionNodeInputString"), NodeItem("FunctionNodeInputVector"), @@ -617,6 +618,7 @@ geometry_node_categories = [ NodeItem("FunctionNodeFloatCompare"), NodeItem("FunctionNodeFloatToInt"), NodeItem("GeometryNodeSwitch"), + NodeItem("FunctionNodeRandomValue", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ NodeItem("ShaderNodeTexNoise", poll=geometry_nodes_fields_poll), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 90285d9fc7c..52f8a3d8136 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1511,7 +1511,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_BOOLEAN_MATH 1200 #define FN_NODE_FLOAT_COMPARE 1202 -#define FN_NODE_RANDOM_FLOAT 1206 +#define FN_NODE_LEGACY_RANDOM_FLOAT 1206 #define FN_NODE_INPUT_VECTOR 1207 #define FN_NODE_INPUT_STRING 1208 #define FN_NODE_FLOAT_TO_INT 1209 @@ -1519,6 +1519,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_STRING_LENGTH 1211 #define FN_NODE_STRING_SUBSTRING 1212 #define FN_NODE_INPUT_SPECIAL_CHARACTERS 1213 +#define FN_NODE_RANDOM_VALUE 1214 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index bf7c47e9cf0..c10aa3bbc5a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5713,6 +5713,7 @@ static void registerGeometryNodes() { register_node_type_geo_group(); + register_node_type_geo_legacy_attribute_randomize(); register_node_type_geo_legacy_material_assign(); register_node_type_geo_legacy_select_by_material(); @@ -5729,7 +5730,6 @@ static void registerGeometryNodes() register_node_type_geo_attribute_math(); register_node_type_geo_attribute_mix(); register_node_type_geo_attribute_proximity(); - register_node_type_geo_attribute_randomize(); register_node_type_geo_attribute_remove(); register_node_type_geo_attribute_separate_xyz(); register_node_type_geo_attribute_statistic(); @@ -5810,13 +5810,15 @@ static void registerGeometryNodes() static void registerFunctionNodes() { + register_node_type_fn_legacy_random_float(); + register_node_type_fn_boolean_math(); register_node_type_fn_float_compare(); register_node_type_fn_float_to_int(); register_node_type_fn_input_special_characters(); register_node_type_fn_input_string(); register_node_type_fn_input_vector(); - register_node_type_fn_random_float(); + register_node_type_fn_random_value(); register_node_type_fn_string_length(); register_node_type_fn_string_substring(); register_node_type_fn_value_to_string(); diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index be8c4b735be..bf5b0bdbf3c 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1540,7 +1540,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (STREQ(node->idname, "GeometryNodeRandomAttribute")) { - STRNCPY(node->idname, "GeometryNodeAttributeRandomize"); + STRNCPY(node->idname, "GeometryLegacyNodeAttributeRandomize"); } } } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 35a2dba627a..38f38b4ba1e 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1230,6 +1230,11 @@ typedef struct NodeAttributeMix { uint8_t input_type_b; } NodeAttributeMix; +typedef struct NodeRandomValue { + /* CustomDataType. */ + uint8_t data_type; +} NodeRandomValue; + typedef struct NodeAttributeRandomize { /* CustomDataType. */ uint8_t data_type; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 333f1584800..1cf0684448d 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2092,6 +2092,19 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf( return itemf_function_check(rna_enum_attribute_type_items, attribute_random_type_supported); } +static bool random_value_type_supported(const EnumPropertyItem *item) +{ + return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL, CD_PROP_INT32); +} +static const EnumPropertyItem *rna_FunctionNodeRandomValue_type_itemf(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_type_items, random_value_type_supported); +} + static const EnumPropertyItem *rna_GeometryNodeAttributeRandomize_operation_itemf( bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) { @@ -9168,6 +9181,21 @@ static void def_geo_subdivision_surface(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_fn_random_value(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeRandomValue", "storage"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "data_type"); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_FunctionNodeRandomValue_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_attribute_randomize(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index e50c07ce6f2..9f296f4cfe9 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -334,6 +334,10 @@ static void get_socket_value(const SocketRef &socket, void *r_value) std::make_shared("position", CPPType::get())); return; } + if (bsocket.type == SOCK_INT && bnode.type == FN_NODE_RANDOM_VALUE) { + new (r_value) Field(std::make_shared()); + return; + } } const bNodeSocketType *typeinfo = socket.typeinfo(); typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 5056c0889bc..e1cceae2964 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -133,18 +133,21 @@ set(SRC composite/node_composite_tree.c composite/node_composite_util.c + function/nodes/legacy/node_fn_random_float.cc + function/nodes/node_fn_boolean_math.cc function/nodes/node_fn_float_compare.cc function/nodes/node_fn_float_to_int.cc function/nodes/node_fn_input_special_characters.cc function/nodes/node_fn_input_string.cc function/nodes/node_fn_input_vector.cc - function/nodes/node_fn_random_float.cc + function/nodes/node_fn_random_value.cc function/nodes/node_fn_string_length.cc function/nodes/node_fn_string_substring.cc function/nodes/node_fn_value_to_string.cc function/node_function_util.cc + geometry/nodes/legacy/node_geo_attribute_randomize.cc geometry/nodes/legacy/node_geo_material_assign.cc geometry/nodes/legacy/node_geo_select_by_material.cc geometry/nodes/legacy/node_geo_point_distribute.cc @@ -162,7 +165,6 @@ set(SRC geometry/nodes/node_geo_attribute_math.cc geometry/nodes/node_geo_attribute_mix.cc geometry/nodes/node_geo_attribute_proximity.cc - geometry/nodes/node_geo_attribute_randomize.cc geometry/nodes/node_geo_attribute_remove.cc geometry/nodes/node_geo_attribute_sample_texture.cc geometry/nodes/node_geo_attribute_separate_xyz.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index b922b36686e..9aa4c04000e 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -20,13 +20,15 @@ extern "C" { #endif +void register_node_type_fn_legacy_random_float(void); + void register_node_type_fn_boolean_math(void); void register_node_type_fn_float_compare(void); void register_node_type_fn_float_to_int(void); void register_node_type_fn_input_special_characters(void); void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); -void register_node_type_fn_random_float(void); +void register_node_type_fn_random_value(void); void register_node_type_fn_string_length(void); void register_node_type_fn_string_substring(void); void register_node_type_fn_value_to_string(void); diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 823c48f71e3..b37d4956e7a 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -29,6 +29,7 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); void register_node_type_geo_custom_group(bNodeType *ntype); +void register_node_type_geo_legacy_attribute_randomize(void); void register_node_type_geo_legacy_material_assign(void); void register_node_type_geo_legacy_select_by_material(void); @@ -45,7 +46,6 @@ void register_node_type_geo_attribute_map_range(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_attribute_mix(void); void register_node_type_geo_attribute_proximity(void); -void register_node_type_geo_attribute_randomize(void); void register_node_type_geo_attribute_remove(void); void register_node_type_geo_attribute_separate_xyz(void); void register_node_type_geo_attribute_statistic(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 6d71cb8d0b6..6af9a7b4e98 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -262,13 +262,15 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NO DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" ) DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" ) +DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", LegacyRandomFloat, "Random Float", "") + DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "") DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") -DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") +DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "") DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") DefNode(FunctionNode, FN_NODE_STRING_SUBSTRING, 0, "STRING_SUBSTRING", StringSubstring, "String Substring", "") diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc similarity index 86% rename from source/blender/nodes/function/nodes/node_fn_random_float.cc rename to source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc index 20f5fa87685..7f6f554ba93 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc @@ -20,7 +20,7 @@ namespace blender::nodes { -static void fn_node_random_float_declare(NodeDeclarationBuilder &b) +static void fn_node_legacy_random_float_declare(NodeDeclarationBuilder &b) { b.is_function_node(); b.add_input("Min").min(-10000.0f).max(10000.0f); @@ -68,19 +68,19 @@ class RandomFloatFunction : public blender::fn::MultiFunction { } }; -static void fn_node_random_float_build_multi_function( +static void fn_node_legacy_random_float_build_multi_function( blender::nodes::NodeMultiFunctionBuilder &builder) { static RandomFloatFunction fn; builder.set_matching_fn(fn); } -void register_node_type_fn_random_float() +void register_node_type_fn_legacy_random_float() { static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); - ntype.declare = blender::nodes::fn_node_random_float_declare; - ntype.build_multi_function = fn_node_random_float_build_multi_function; + fn_node_type_base(&ntype, FN_NODE_LEGACY_RANDOM_FLOAT, "Random Float", 0, 0); + ntype.declare = blender::nodes::fn_node_legacy_random_float_declare; + ntype.build_multi_function = fn_node_legacy_random_float_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_random_value.cc b/source/blender/nodes/function/nodes/node_fn_random_value.cc new file mode 100644 index 00000000000..53ca77aab0c --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_random_value.cc @@ -0,0 +1,299 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// #include "BLI_hash.h" +#include "BLI_noise.hh" + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void fn_node_random_value_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Min").supports_field(); + b.add_input("Max").default_value({1.0f, 1.0f, 1.0f}).supports_field(); + b.add_input("Min", "Min_001").supports_field(); + b.add_input("Max", "Max_001").default_value(1.0f).supports_field(); + b.add_input("Min", "Min_002").min(-100000).max(100000).supports_field(); + b.add_input("Max", "Max_002") + .default_value(100) + .min(-100000) + .max(100000) + .supports_field(); + b.add_input("Probability") + .min(0.0f) + .max(1.0f) + .default_value(0.5f) + .subtype(PROP_FACTOR) + .supports_field(); + b.add_input("ID").implicit_field(); + b.add_input("Seed").default_value(0).min(-10000).max(10000).supports_field(); + + b.add_output("Value").dependent_field(); + b.add_output("Value", "Value_001").dependent_field(); + b.add_output("Value", "Value_002").dependent_field(); + b.add_output("Value", "Value_003").dependent_field(); +} + +static void fn_node_random_value_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static void fn_node_random_value_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeRandomValue *data = (NodeRandomValue *)MEM_callocN(sizeof(NodeRandomValue), __func__); + data->data_type = CD_PROP_FLOAT; + node->storage = data; +} + +static void fn_node_random_value_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeRandomValue &storage = *(const NodeRandomValue *)node->storage; + const CustomDataType data_type = static_cast(storage.data_type); + + bNodeSocket *sock_min_vector = (bNodeSocket *)node->inputs.first; + bNodeSocket *sock_max_vector = sock_min_vector->next; + bNodeSocket *sock_min_float = sock_max_vector->next; + bNodeSocket *sock_max_float = sock_min_float->next; + bNodeSocket *sock_min_int = sock_max_float->next; + bNodeSocket *sock_max_int = sock_min_int->next; + bNodeSocket *sock_probability = sock_max_int->next; + + bNodeSocket *sock_out_vector = (bNodeSocket *)node->outputs.first; + bNodeSocket *sock_out_float = sock_out_vector->next; + bNodeSocket *sock_out_int = sock_out_float->next; + bNodeSocket *sock_out_bool = sock_out_int->next; + + nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(sock_probability, data_type == CD_PROP_BOOL); + + nodeSetSocketAvailability(sock_out_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(sock_out_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(sock_out_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(sock_out_bool, data_type == CD_PROP_BOOL); +} + +class RandomVectorFunction : public fn::MultiFunction { + public: + RandomVectorFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Random Value"}; + signature.single_input("Min"); + signature.single_input("Max"); + signature.single_input("ID"); + signature.single_input("Seed"); + signature.single_output("Value"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &min_values = params.readonly_single_input(0, "Min"); + const VArray &max_values = params.readonly_single_input(1, "Max"); + const VArray &ids = params.readonly_single_input(2, "ID"); + const VArray &seeds = params.readonly_single_input(3, "Seed"); + MutableSpan values = params.uninitialized_single_output(4, "Value"); + + for (int64_t i : mask) { + const float3 min_value = min_values[i]; + const float3 max_value = max_values[i]; + const int seed = seeds[i]; + const int id = ids[i]; + + const float x = noise::hash_to_float(seed, id, 0); + const float y = noise::hash_to_float(seed, id, 1); + const float z = noise::hash_to_float(seed, id, 2); + + values[i] = float3(x, y, z) * (max_value - min_value) + min_value; + } + } +}; + +class RandomFloatFunction : public fn::MultiFunction { + public: + RandomFloatFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Random Value"}; + signature.single_input("Min"); + signature.single_input("Max"); + signature.single_input("ID"); + signature.single_input("Seed"); + signature.single_output("Value"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &min_values = params.readonly_single_input(0, "Min"); + const VArray &max_values = params.readonly_single_input(1, "Max"); + const VArray &ids = params.readonly_single_input(2, "ID"); + const VArray &seeds = params.readonly_single_input(3, "Seed"); + MutableSpan values = params.uninitialized_single_output(4, "Value"); + + for (int64_t i : mask) { + const float min_value = min_values[i]; + const float max_value = max_values[i]; + const int seed = seeds[i]; + const int id = ids[i]; + + const float value = noise::hash_to_float(seed, id); + values[i] = value * (max_value - min_value) + min_value; + } + } +}; + +class RandomIntFunction : public fn::MultiFunction { + public: + RandomIntFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Random Value"}; + signature.single_input("Min"); + signature.single_input("Max"); + signature.single_input("ID"); + signature.single_input("Seed"); + signature.single_output("Value"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &min_values = params.readonly_single_input(0, "Min"); + const VArray &max_values = params.readonly_single_input(1, "Max"); + const VArray &ids = params.readonly_single_input(2, "ID"); + const VArray &seeds = params.readonly_single_input(3, "Seed"); + MutableSpan values = params.uninitialized_single_output(4, "Value"); + + for (int64_t i : mask) { + const float min_value = min_values[i]; + const float max_value = max_values[i]; + const int seed = seeds[i]; + const int id = ids[i]; + + const float value = noise::hash_to_float(id, seed); + values[i] = round_fl_to_int(value * (max_value - min_value) + min_value); + } + } +}; + +class RandomBoolFunction : public fn::MultiFunction { + public: + RandomBoolFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Random Value"}; + signature.single_input("Probability"); + signature.single_input("ID"); + signature.single_input("Seed"); + signature.single_output("Value"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &probabilities = params.readonly_single_input(0, "Probability"); + const VArray &ids = params.readonly_single_input(1, "ID"); + const VArray &seeds = params.readonly_single_input(2, "Seed"); + MutableSpan values = params.uninitialized_single_output(3, "Value"); + + for (int64_t i : mask) { + const int seed = seeds[i]; + const int id = ids[i]; + const float probability = probabilities[i]; + values[i] = noise::hash_to_float(id, seed) <= probability; + } + } +}; + +static void fn_node_random_value_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const NodeRandomValue &storage = *(const NodeRandomValue *)builder.node().storage; + const CustomDataType data_type = static_cast(storage.data_type); + + switch (data_type) { + case CD_PROP_FLOAT3: { + static RandomVectorFunction fn; + builder.set_matching_fn(fn); + break; + } + case CD_PROP_FLOAT: { + static RandomFloatFunction fn; + builder.set_matching_fn(fn); + break; + } + case CD_PROP_INT32: { + static RandomIntFunction fn; + builder.set_matching_fn(fn); + break; + } + case CD_PROP_BOOL: { + static RandomBoolFunction fn; + builder.set_matching_fn(fn); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +} // namespace blender::nodes + +void register_node_type_fn_random_value() +{ + static bNodeType ntype; + fn_node_type_base(&ntype, FN_NODE_RANDOM_VALUE, "Random Value", NODE_CLASS_CONVERTER, 0); + node_type_init(&ntype, blender::nodes::fn_node_random_value_init); + node_type_update(&ntype, blender::nodes::fn_node_random_value_update); + ntype.draw_buttons = blender::nodes::fn_node_random_value_layout; + ntype.declare = blender::nodes::fn_node_random_value_declare; + ntype.build_multi_function = blender::nodes::fn_node_random_value_build_multi_function; + node_type_storage( + &ntype, "NodeRandomValue", node_free_standard_storage, node_copy_standard_storage); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc similarity index 92% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc index 60b9910399c..2e6ba456725 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc @@ -25,7 +25,7 @@ namespace blender::nodes { -static void geo_node_attribute_randomize_declare(NodeDeclarationBuilder &b) +static void geo_node_legacy_attribute_randomize_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_input("Attribute"); @@ -39,15 +39,15 @@ static void geo_node_attribute_randomize_declare(NodeDeclarationBuilder &b) b.add_output("Geometry"); } -static void geo_node_attribute_random_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void geo_node_legacy_attribute_random_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); } -static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node) +static void geo_node_legacy_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node) { NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN( sizeof(NodeAttributeRandomize), __func__); @@ -57,7 +57,7 @@ static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *no node->storage = data; } -static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node) +static void geo_node_legacy_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node) { bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2); bNodeSocket *sock_max_vector = sock_min_vector->next; @@ -280,7 +280,7 @@ static void randomize_attribute_on_component(GeometryComponent &component, attribute.save(); } -static void geo_node_random_attribute_exec(GeoNodeExecParams params) +static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); const std::string attribute_name = params.get_input("Attribute"); @@ -326,18 +326,18 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_attribute_randomize() +void register_node_type_geo_legacy_attribute_randomize() { static bNodeType ntype; geo_node_type_base( &ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0); - node_type_init(&ntype, blender::nodes::geo_node_attribute_randomize_init); - node_type_update(&ntype, blender::nodes::geo_node_attribute_randomize_update); + node_type_init(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_init); + node_type_update(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_update); - ntype.declare = blender::nodes::geo_node_attribute_randomize_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_random_layout; + ntype.declare = blender::nodes::geo_node_legacy_attribute_randomize_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_random_attribute_exec; + ntype.draw_buttons = blender::nodes::geo_node_legacy_attribute_random_layout; node_type_storage( &ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); From 2dd39683358100a39d7e7774e1051136ec1df7d9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 24 Sep 2021 14:06:41 -0500 Subject: [PATCH 0211/1500] Geometry Nodes: Add versioning and legacy warning for random float node --- .../blenloader/intern/versioning_300.c | 27 ++++++++++++++++++- .../modifiers/intern/MOD_nodes_evaluator.cc | 10 +++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 88df4f73d45..caba972c744 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -808,6 +808,7 @@ static void version_geometry_nodes_change_legacy_names(bNodeTree *ntree) } } } + static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data)) { StripTransform *transform = seq->strip->transform; @@ -1480,5 +1481,29 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Deprecate the random float node in favor of the random float node. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != FN_NODE_LEGACY_RANDOM_FLOAT) { + continue; + } + if (strstr(node->idname, "Legacy")) { + /* Make sure we haven't changed this idname already. */ + continue; + } + + char temp_idname[sizeof(node->idname)]; + BLI_strncpy(temp_idname, node->idname, sizeof(node->idname)); + + BLI_snprintf(node->idname, + sizeof(node->idname), + "FunctionNodeLegacy%s", + temp_idname + strlen("FunctionNode")); + } + } } -} +} \ No newline at end of file diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 9f296f4cfe9..fd0205cffc5 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -887,6 +887,16 @@ class GeometryNodesEvaluator { const MultiFunction &fn, NodeState &node_state) { + if (USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) { + if (node->idname().find("Legacy") != StringRef::not_found) { + /* Create geometry nodes params just for creating an error message. */ + NodeParamsProvider params_provider{*this, node, node_state}; + GeoNodeExecParams params{params_provider}; + params.error_message_add(geo_log::NodeWarningType::Legacy, + TIP_("Legacy node will be removed before Blender 4.0")); + } + } + LinearAllocator<> &allocator = local_allocators_.local(); /* Prepare the inputs for the multi function. */ From b314d3e7877f8e09c18a40d7d400e501f231a69b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 24 Sep 2021 20:50:35 +0200 Subject: [PATCH 0212/1500] Fix T91639: Cycles crash rendering high resolution images with multiple passes We were writing large 2048x2048 tiles into EXR files, which appears to cause integer overflow inside the OpenEXR library when there are multiple passes. Now use smaller tiles in the image file, while still rendering large tiles. This adds the requirement that the render tile size must be a multiple of 128 or be smaller than 128, this is adjusted automatically. --- intern/cycles/render/session.cpp | 4 +-- intern/cycles/render/tile.cpp | 59 +++++++++++++++++++------------- intern/cycles/render/tile.h | 6 ++++ 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 56d92fb0ad8..269d67e8bda 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -396,8 +396,8 @@ int2 Session::get_effective_tile_size() const /* TODO(sergey): Take available memory into account, and if there is enough memory do not tile * and prefer optimal performance. */ - - return make_int2(params.tile_size, params.tile_size); + const int tile_size = tile_manager_.compute_render_tile_size(params.tile_size); + return make_int2(tile_size, tile_size); } void Session::do_delayed_reset() diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 28910bffa7b..580931504f3 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -307,8 +307,8 @@ static bool configure_image_spec_from_buffer(ImageSpec *image_spec, DCHECK_GT(tile_size.x, 0); DCHECK_GT(tile_size.y, 0); - image_spec->tile_width = tile_size.x; - image_spec->tile_height = tile_size.y; + image_spec->tile_width = min(TileManager::IMAGE_TILE_SIZE, tile_size.x); + image_spec->tile_height = min(TileManager::IMAGE_TILE_SIZE, tile_size.y); } return true; @@ -335,6 +335,15 @@ TileManager::~TileManager() { } +int TileManager::compute_render_tile_size(const int suggested_tile_size) const +{ + /* Must be a multiple of IMAGE_TILE_SIZE so that we can write render tiles into the image file + * aligned on image tile boundaries. We can't set IMAGE_TILE_SIZE equal to the render tile size + * because too big tile size leads to integer overflow inside OpenEXR. */ + return (suggested_tile_size <= IMAGE_TILE_SIZE) ? suggested_tile_size : + align_up(suggested_tile_size, IMAGE_TILE_SIZE); +} + void TileManager::reset_scheduling(const BufferParams ¶ms, int2 tile_size) { VLOG(3) << "Using tile size of " << tile_size; @@ -466,32 +475,27 @@ bool TileManager::write_tile(const RenderBuffers &tile_buffers) const BufferParams &tile_params = tile_buffers.params; - vector pixel_storage; const float *pixels = tile_buffers.buffer.data(); - - /* Tiled writing expects pixels to contain data for an entire tile. Pad the render buffers with - * empty pixels for tiles which are on the image boundary. */ - if (tile_params.width != tile_size_.x || tile_params.height != tile_size_.y) { - const int64_t pass_stride = tile_params.pass_stride; - const int64_t src_row_stride = tile_params.width * pass_stride; - - const int64_t dst_row_stride = tile_size_.x * pass_stride; - pixel_storage.resize(dst_row_stride * tile_size_.y); - - const float *src = tile_buffers.buffer.data(); - float *dst = pixel_storage.data(); - pixels = dst; - - for (int y = 0; y < tile_params.height; ++y, src += src_row_stride, dst += dst_row_stride) { - memcpy(dst, src, src_row_stride * sizeof(float)); - } - } - const int tile_x = tile_params.full_x - buffer_params_.full_x; const int tile_y = tile_params.full_y - buffer_params_.full_y; VLOG(3) << "Write tile at " << tile_x << ", " << tile_y; - if (!write_state_.tile_out->write_tile(tile_x, tile_y, 0, TypeDesc::FLOAT, pixels)) { + + /* The image tile sizes in the OpenEXR file are different from the size of our big tiles. The + * write_tiles() method expects a contiguous image region that will be split into tiles + * internally. OpenEXR expects the size of this region to be a multiple of the tile size, + * however OpenImageIO automatically adds the required padding. + * + * The only thing we have to ensure is that the tile_x and tile_y are a multiple of the + * image tile size, which happens in compute_render_tile_size. */ + if (!write_state_.tile_out->write_tiles(tile_x, + tile_x + tile_params.width, + tile_y, + tile_y + tile_params.height, + 0, + 1, + TypeDesc::FLOAT, + pixels)) { LOG(ERROR) << "Error writing tile " << write_state_.tile_out->geterror(); } @@ -518,7 +522,14 @@ void TileManager::finish_write_tiles() VLOG(3) << "Write dummy tile at " << tile.x << ", " << tile.y; - write_state_.tile_out->write_tile(tile.x, tile.y, 0, TypeDesc::FLOAT, pixel_storage.data()); + write_state_.tile_out->write_tiles(tile.x, + tile.x + tile.width, + tile.y, + tile.y + tile.height, + 0, + 1, + TypeDesc::FLOAT, + pixel_storage.data()); } } diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h index 71b9e966278..8392274ff79 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/render/tile.h @@ -107,6 +107,12 @@ class TileManager { RenderBuffers *buffers, DenoiseParams *denoise_params); + /* Compute valid tile size compatible with image saving. */ + int compute_render_tile_size(const int suggested_tile_size) const; + + /* Tile size in the image file. */ + static const int IMAGE_TILE_SIZE = 128; + protected: /* Get tile configuration for its index. * The tile index must be within [0, state_.tile_state_). */ From a3027fb0941651d9b5287e3e0148aabd82d512d4 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 24 Sep 2021 21:14:21 +0200 Subject: [PATCH 0213/1500] Fix T91662: VSE Image overlay is drawn for backdrop Don't draw image overlay in timeline, image manipulation only works in preview. --- .../blender/editors/space_sequencer/sequencer_draw.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 53f1c35776c..86818661a7f 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -2719,12 +2719,14 @@ void sequencer_draw_preview(const bContext *C, sequencer_draw_borders_overlay(sseq, v2d, scene); } - SeqCollection *collection = SEQ_query_rendered_strips(&scene->ed->seqbase, timeline_frame, 0); - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, collection) { - seq_draw_image_origin_and_outline(C, seq); + if (!draw_backdrop) { + SeqCollection *collection = SEQ_query_rendered_strips(&scene->ed->seqbase, timeline_frame, 0); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + seq_draw_image_origin_and_outline(C, seq); + } + SEQ_collection_free(collection); } - SEQ_collection_free(collection); if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_OVERLAY)) { sequencer_draw_gpencil_overlay(C); From ab09844be8ff4a15cfdf9457f5b39256e3cbfd19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Sat, 25 Sep 2021 09:15:25 +0200 Subject: [PATCH 0214/1500] Cleanup: typos in code and comments. No functional changes. --- intern/opensubdiv/internal/evaluator/evaluator_impl.cc | 2 +- intern/opensubdiv/opensubdiv_capi_type.h | 2 +- source/blender/blenkernel/BKE_subdiv.h | 2 +- source/blender/blenkernel/BKE_subdiv_foreach.h | 6 +++--- source/blender/blenkernel/intern/subdiv_mesh.c | 2 +- source/blender/io/alembic/intern/abc_customdata.cc | 6 +++--- source/blender/io/alembic/intern/abc_customdata.h | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc index b3fc021e1ee..4f4f332ff15 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc +++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc @@ -553,7 +553,7 @@ void convertPatchCoordsToArray(const OpenSubdiv_PatchCoord *patch_coords, } // namespace -// Note: Define as a class instead of typedcef to make it possible +// Note: Define as a class instead of typedef to make it possible // to have anonymous class in opensubdiv_evaluator_internal.h class CpuEvalOutput : public VolatileEvalOutput abc_uv_maps; - /* OCRO coordinates, aka Generated Coordinates. */ - Alembic::AbcGeom::OV3fGeomParam abc_ocro; + /* ORCO coordinates, aka Generated Coordinates. */ + Alembic::AbcGeom::OV3fGeomParam abc_orco; CDStreamConfig() : mloop(NULL), From 43394e41a8e0a866631bc7fdf07ac50e9d7094e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Sat, 25 Sep 2021 09:20:51 +0200 Subject: [PATCH 0215/1500] Fix Alembic point cloud streaming. Point clouds are not imported and read anymore. This was caused by an API change in rB128eb6cbe928e58dfee1c64f340fd8d663134c26 which was not applied to `AbcPointsReader`. It did not cause a compile error as the base class as a default implementation for this method. --- source/blender/io/alembic/intern/abc_reader_points.cc | 4 +++- source/blender/io/alembic/intern/abc_reader_points.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc index 3aeacbd14fe..75ed18f57f2 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.cc +++ b/source/blender/io/alembic/intern/abc_reader_points.cc @@ -82,7 +82,7 @@ bool AbcPointsReader::accepts_object_type( void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) { Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); - Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, nullptr); + Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, "", 0.0f, nullptr); if (read_mesh != mesh) { BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); @@ -127,6 +127,8 @@ void read_points_sample(const IPointsSchema &schema, struct Mesh *AbcPointsReader::read_mesh(struct Mesh *existing_mesh, const ISampleSelector &sample_sel, int read_flag, + const char * /*velocity_name*/, + const float /*velocity_scale*/, const char **err_str) { IPointsSchema::Sample sample; diff --git a/source/blender/io/alembic/intern/abc_reader_points.h b/source/blender/io/alembic/intern/abc_reader_points.h index aed66699c75..a66b8a829ec 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.h +++ b/source/blender/io/alembic/intern/abc_reader_points.h @@ -44,6 +44,8 @@ class AbcPointsReader : public AbcObjectReader { struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str); }; From 505422220d6e172fddc8a40fabee47f5d35e0c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Sat, 25 Sep 2021 09:30:12 +0200 Subject: [PATCH 0216/1500] Cleanup: use override/final for derived classes. This will help detecting missing API changes. Those keywords were added on classes which did not already use them. Also added missing `accepts_object_type()` on NURBS reader. --- .../io/alembic/intern/abc_reader_camera.h | 8 ++++---- .../io/alembic/intern/abc_reader_curves.h | 10 +++++----- .../io/alembic/intern/abc_reader_mesh.h | 12 +++++------ .../io/alembic/intern/abc_reader_nurbs.cc | 20 +++++++++++++++++++ .../io/alembic/intern/abc_reader_nurbs.h | 10 +++++++--- .../io/alembic/intern/abc_reader_points.h | 10 +++++----- .../io/alembic/intern/abc_reader_transform.h | 8 ++++---- 7 files changed, 51 insertions(+), 27 deletions(-) diff --git a/source/blender/io/alembic/intern/abc_reader_camera.h b/source/blender/io/alembic/intern/abc_reader_camera.h index 408e9623970..ca8dee80c9d 100644 --- a/source/blender/io/alembic/intern/abc_reader_camera.h +++ b/source/blender/io/alembic/intern/abc_reader_camera.h @@ -23,18 +23,18 @@ namespace blender::io::alembic { -class AbcCameraReader : public AbcObjectReader { +class AbcCameraReader final : public AbcObjectReader { Alembic::AbcGeom::ICameraSchema m_schema; public: AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, - const char **err_str) const; + const char **err_str) const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; }; } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h index 11b23c8a8cf..df5d68d7850 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.h +++ b/source/blender/io/alembic/intern/abc_reader_curves.h @@ -31,24 +31,24 @@ struct Curve; namespace blender::io::alembic { -class AbcCurveReader : public AbcObjectReader { +class AbcCurveReader final : public AbcObjectReader { Alembic::AbcGeom::ICurvesSchema m_curves_schema; public: AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, - const char **err_str) const; + const char **err_str) const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, const int read_flag, const char *velocity_name, const float velocity_scale, - const char **err_str); + const char **err_str) override; void read_curve_sample(Curve *cu, const Alembic::AbcGeom::ICurvesSchema &schema, diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h index d9f89cc8085..2e34ca8ded0 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.h +++ b/source/blender/io/alembic/intern/abc_reader_mesh.h @@ -26,7 +26,7 @@ struct Mesh; namespace blender::io::alembic { -class AbcMeshReader : public AbcObjectReader { +class AbcMeshReader final : public AbcObjectReader { Alembic::AbcGeom::IPolyMeshSchema m_schema; CDStreamConfig m_mesh_data; @@ -60,7 +60,7 @@ class AbcMeshReader : public AbcObjectReader { std::map &r_mat_map); }; -class AbcSubDReader : public AbcObjectReader { +class AbcSubDReader final : public AbcObjectReader { Alembic::AbcGeom::ISubDSchema m_schema; CDStreamConfig m_mesh_data; @@ -68,17 +68,17 @@ class AbcSubDReader : public AbcObjectReader { public: AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, - const char **err_str) const; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + const char **err_str) const override; + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, const int read_flag, const char *velocity_name, const float velocity_scale, - const char **err_str); + const char **err_str) override; }; void read_mverts(MVert *mverts, diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc index 25567aa8c24..4492d1e1ca0 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc @@ -71,6 +71,26 @@ bool AbcNurbsReader::valid() const return true; } +bool AbcNurbsReader::accepts_object_type( + const Alembic::AbcCoreAbstract::v12::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const +{ + if (!Alembic::AbcGeom::INuPatch::matches(alembic_header)) { + *err_str = + "Object type mismatch, Alembic object path pointed to NURBS when importing, but not any " + "more."; + return false; + } + + if (ob->type != OB_CURVE) { + *err_str = "Object type mismatch, Alembic object path points to NURBS."; + return false; + } + + return true; +} + static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots) { if (!knots || knots->size() < 2) { diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.h b/source/blender/io/alembic/intern/abc_reader_nurbs.h index e8be2efba9f..66e68cf6942 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.h +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.h @@ -23,15 +23,19 @@ namespace blender::io::alembic { -class AbcNurbsReader : public AbcObjectReader { +class AbcNurbsReader final : public AbcObjectReader { std::vector> m_schemas; public: AbcNurbsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const override; + + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; private: void getNurbsPatches(const Alembic::Abc::IObject &obj); diff --git a/source/blender/io/alembic/intern/abc_reader_points.h b/source/blender/io/alembic/intern/abc_reader_points.h index a66b8a829ec..105d1276f7a 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.h +++ b/source/blender/io/alembic/intern/abc_reader_points.h @@ -27,26 +27,26 @@ namespace blender::io::alembic { -class AbcPointsReader : public AbcObjectReader { +class AbcPointsReader final : public AbcObjectReader { Alembic::AbcGeom::IPointsSchema m_schema; Alembic::AbcGeom::IPointsSchema::Sample m_sample; public: AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, - const char **err_str) const; + const char **err_str) const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char *velocity_name, const float velocity_scale, - const char **err_str); + const char **err_str) override; }; void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema, diff --git a/source/blender/io/alembic/intern/abc_reader_transform.h b/source/blender/io/alembic/intern/abc_reader_transform.h index e515560912f..6810cc214b7 100644 --- a/source/blender/io/alembic/intern/abc_reader_transform.h +++ b/source/blender/io/alembic/intern/abc_reader_transform.h @@ -25,18 +25,18 @@ namespace blender::io::alembic { -class AbcEmptyReader : public AbcObjectReader { +class AbcEmptyReader final : public AbcObjectReader { Alembic::AbcGeom::IXformSchema m_schema; public: AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, - const char **err_str) const; + const char **err_str) const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; }; } // namespace blender::io::alembic From 80f7bc6d8e7e7a5e543df5418313c04df5140c43 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Sun, 26 Sep 2021 19:43:12 +0800 Subject: [PATCH 0217/1500] LineArt: Smooth tolerance value for chaining. smooth out jaggy lines with a given threshold. For each point in a stroke, the ones with in a given distance of its previous segment will be removed, thus "zig-zag" artefacts can be cleaned up. Reviewed By: Antonio Vazquez (antoniov) Differential Revision: https://developer.blender.org/D12050 --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- .../blenloader/intern/versioning_300.c | 9 ++++++ .../intern/MOD_gpencillineart.c | 1 + .../intern/lineart/MOD_lineart.h | 3 ++ .../intern/lineart/lineart_chain.c | 28 +++++++++++++++++++ .../intern/lineart/lineart_cpu.c | 10 ++++++- .../makesdna/DNA_gpencil_modifier_defaults.h | 2 +- .../makesdna/DNA_gpencil_modifier_types.h | 6 +++- source/blender/makesdna/DNA_gpencil_types.h | 2 ++ .../makesrna/intern/rna_gpencil_modifier.c | 9 ++++++ source/tools | 2 +- 13 files changed, 71 insertions(+), 7 deletions(-) diff --git a/release/datafiles/locale b/release/datafiles/locale index 94c39b5832b..4833954c0ac 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 94c39b5832b9ef3b56ed94ce4011412e3d776eb2 +Subproject commit 4833954c0ac85cc407e1d5a153aa11b1d1823ec0 diff --git a/release/scripts/addons b/release/scripts/addons index ecf30de46c3..f86f25e6221 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit ecf30de46c368ffddad259c125402a38e6093382 +Subproject commit f86f25e62217264495d05f116ccb09d575fe9841 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 42da56aa737..5a82baad9f9 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 42da56aa73726710107031787af5eea186797984 +Subproject commit 5a82baad9f986722104280e8354a4427d8e9eab1 diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index caba972c744..692c114d206 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1127,6 +1127,15 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + if (ob->type == OB_GPENCIL) { + LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { + if (md->type == eGpencilModifierType_Lineart) { + LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; + lmd->flags |= LRT_GPENCIL_USE_CACHE; + lmd->chain_smooth_tolerance = 0.2f; + } + } + } } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 01488a8b2de..c5ccf1d8229 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -587,6 +587,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) is_geom ? IFACE_("Geometry Threshold") : NULL, ICON_NONE); + uiItemR(layout, ptr, "smooth_tolerance", UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemR(layout, ptr, "split_angle", UI_ITEM_R_SLIDER, NULL, ICON_NONE); } diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 134d9707ade..c00f34185dd 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -317,6 +317,8 @@ typedef struct LineartRenderBuffer { float chaining_image_threshold; float angle_splitting_threshold; + float chain_smooth_tolerance; + /* FIXME(Yiming): Temporary solution for speeding up calculation by not including lines that * are not in the selected source. This will not be needed after we have a proper scene-wise * cache running because multiple modifiers can then select results from that without further @@ -592,6 +594,7 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb); void MOD_lineart_chain_connect(LineartRenderBuffer *rb); void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold); void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); +void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance); int MOD_lineart_chain_count(const LineartEdgeChain *ec); void MOD_lineart_chain_clear_picked_flag(LineartCache *lc); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index d86253e7fe0..8935bdd1870 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -924,6 +924,34 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) } } +void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) +{ + LISTBASE_FOREACH (LineartEdgeChain *, rlc, &rb->chains) { + LineartEdgeChainItem *next_eci; + for (LineartEdgeChainItem *eci = rlc->chain.first; eci; eci = next_eci) { + next_eci = eci->next; + LineartEdgeChainItem *eci2, *eci3, *eci4; + + /* Not enough point to do simplify. */ + if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) { + continue; + } + + /* No need to care for different line types/occlusion and so on, because at this stage they + * are all the same within a chain. */ + + /* If p3 is within the p1-p2 segment of a width of "tolerance" */ + if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { + /* And if p4 is on the extension of p1-p2 , we remove p3. */ + if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { + BLI_remlink(&rlc->chain, eci3); + next_eci = eci; + } + } + } + } +} + /** * This should always be the last stage!, see the end of * #MOD_lineart_chain_split_for_fixed_occlusion(). diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 725cc0741f0..5b878a4326f 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -3056,8 +3056,9 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->shift_y /= (1 + rb->overscan); rb->crease_threshold = cos(M_PI - lmd->crease_threshold); - rb->angle_splitting_threshold = lmd->angle_splitting_threshold; rb->chaining_image_threshold = lmd->chaining_image_threshold; + rb->angle_splitting_threshold = lmd->angle_splitting_threshold; + rb->chain_smooth_tolerance = lmd->chain_smooth_tolerance; rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0; rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0; @@ -4172,6 +4173,13 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, /* This configuration ensures there won't be accidental lost of short unchained segments. */ MOD_lineart_chain_discard_short(rb, MIN2(*t_image, 0.001f) - FLT_EPSILON); + if (rb->chain_smooth_tolerance > FLT_EPSILON) { + /* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best + * effective range in image-space (Coordinate only goes from -1 to 1). This value is somewhat + * arbitrary, but works best for the moment. */ + MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50); + } + if (rb->angle_splitting_threshold > FLT_EPSILON) { MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold); } diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 2a3c6f4e3db..11299ae9717 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -318,7 +318,7 @@ .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | LRT_USE_CREASE_ON_SHARP_EDGES, \ .angle_splitting_threshold = DEG2RAD(60.0f), \ .chaining_image_threshold = 0.001f, \ - .overscan = 0.1f,\ + .chain_smooth_tolerance = 0.2f,\ } #define _DNA_DEFAULT_LengthGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 8d967a38808..ea5c81761c6 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -1040,7 +1040,11 @@ typedef struct LineartGpencilModifierData { /** `0..PI` angle, for splitting strokes at sharp points. */ float angle_splitting_threshold; - /* Doubles as geometry threshold when geometry space chaining is enabled */ + /** Strength for smoothing jagged chains. */ + float chain_smooth_tolerance; + int _pad1; + + /* CPU mode */ float chaining_image_threshold; /* Ported from SceneLineArt flags. */ diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 68bd2961f23..0f570f8603d 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -49,6 +49,8 @@ struct MDeformVert; #define GPENCIL_MIN_FILL_FAC 0.05f #define GPENCIL_MAX_FILL_FAC 8.0f +#define GPENCIL_MAX_THICKNESS 5000 + /* ***************************************** */ /* GP Stroke Points */ diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 5817a200192..675cff3e58c 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -26,6 +26,7 @@ #include "DNA_brush_types.h" #include "DNA_cachefile_types.h" #include "DNA_gpencil_modifier_types.h" +#include "DNA_gpencil_types.h" #include "DNA_mesh_types.h" #include "DNA_object_force_types.h" #include "DNA_object_types.h" @@ -3125,6 +3126,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f)); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "smooth_tolerance", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "chain_smooth_tolerance"); + RNA_def_property_ui_text( + prop, "Smooth Tolerance", "Strength of smoothing applied on jagged chains"); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.05f, 4); + RNA_def_property_range(prop, 0.0f, 30.0f); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_remove_doubles", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_REMOVE_DOUBLES); RNA_def_property_ui_text( diff --git a/source/tools b/source/tools index 723b24841df..01f51a0e551 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 723b24841df1ed8478949bca20c73878fab00dca +Subproject commit 01f51a0e551ab730f0934dc6488613690ac4bf8f From 93b36fad684f62119a7a27c5ba37902643574ae5 Mon Sep 17 00:00:00 2001 From: Cian Jinks Date: Sun, 26 Sep 2021 22:18:24 +0100 Subject: [PATCH 0218/1500] Knife: Expose XYZ axis locking in modal keymap A small quality of life improvement that will allow users to change the keys used for axis locking. --- .../keyconfig/keymap_data/blender_default.py | 3 +++ .../keymap_data/industry_compatible_data.py | 3 +++ source/blender/editors/mesh/editmesh_knife.c | 19 ++++++++++++++----- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index ada8824519e..56ff776ea48 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -5669,6 +5669,9 @@ def km_knife_tool_modal_map(_params): ("IGNORE_SNAP_OFF", {"type": 'LEFT_CTRL', "value": 'RELEASE', "any": True}, None), ("IGNORE_SNAP_ON", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None), ("IGNORE_SNAP_OFF", {"type": 'RIGHT_CTRL', "value": 'RELEASE', "any": True}, None), + ("X_AXIS", {"type": 'X', "value": 'PRESS'}, None), + ("Y_AXIS", {"type": 'Y', "value": 'PRESS'}, None), + ("Z_AXIS", {"type": 'Z', "value": 'PRESS'}, None), ("ANGLE_SNAP_TOGGLE", {"type": 'A', "value": 'PRESS'}, None), ("CYCLE_ANGLE_SNAP_EDGE", {"type": 'R', "value": 'PRESS'}, None), ("CUT_THROUGH_TOGGLE", {"type": 'C', "value": 'PRESS'}, None), diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 6a24f072ed0..886abae3602 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -3876,6 +3876,9 @@ def km_knife_tool_modal_map(_params): ("IGNORE_SNAP_OFF", {"type": 'LEFT_SHIFT', "value": 'RELEASE', "any": True}, None), ("IGNORE_SNAP_ON", {"type": 'RIGHT_SHIFT', "value": 'PRESS', "any": True}, None), ("IGNORE_SNAP_OFF", {"type": 'RIGHT_SHIFT', "value": 'RELEASE', "any": True}, None), + ("X_AXIS", {"type": 'X', "value": 'PRESS'}, None), + ("Y_AXIS", {"type": 'Y', "value": 'PRESS'}, None), + ("Z_AXIS", {"type": 'Z', "value": 'PRESS'}, None), ("ANGLE_SNAP_TOGGLE", {"type": 'A', "value": 'PRESS'}, None), ("CYCLE_ANGLE_SNAP_EDGE", {"type": 'R', "value": 'PRESS'}, None), ("CUT_THROUGH_TOGGLE", {"type": 'C', "value": 'PRESS'}, None), diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index e76a9641811..13519fad89d 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -333,6 +333,9 @@ enum { KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE, KNF_MODAL_DEPTH_TEST_TOGGLE, KNF_MODAL_PANNING, + KNF_MODAL_X_AXIS, + KNF_MODAL_Y_AXIS, + KNF_MODAL_Z_AXIS, KNF_MODAL_ADD_CUT_CLOSED, }; @@ -1112,7 +1115,7 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k "%s: start/define cut, %s: close cut, %s: new cut, " "%s: midpoint snap (%s), %s: ignore snap (%s), " "%s: angle constraint %.2f(%.2f) (%s%s%s%s), %s: cut through (%s), " - "%s: panning, XYZ: orientation lock (%s), " + "%s: panning, %s%s%s: orientation lock (%s), " "%s: distance/angle measurements (%s), " "%s: x-ray (%s)"), WM_MODALKEY(KNF_MODAL_CONFIRM), @@ -1144,6 +1147,9 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k WM_MODALKEY(KNF_MODAL_CUT_THROUGH_TOGGLE), WM_bool_as_string(kcd->cut_through), WM_MODALKEY(KNF_MODAL_PANNING), + WM_MODALKEY(KNF_MODAL_X_AXIS), + WM_MODALKEY(KNF_MODAL_Y_AXIS), + WM_MODALKEY(KNF_MODAL_Z_AXIS), (kcd->axis_constrained ? kcd->axis_string : WM_bool_as_string(kcd->axis_constrained)), WM_MODALKEY(KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE), WM_bool_as_string(kcd->show_dist_angle), @@ -4307,6 +4313,9 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf) {KNF_MODAL_ADD_CUT, "ADD_CUT", 0, "Add Cut", ""}, {KNF_MODAL_ADD_CUT_CLOSED, "ADD_CUT_CLOSED", 0, "Add Cut Closed", ""}, {KNF_MODAL_PANNING, "PANNING", 0, "Panning", ""}, + {KNF_MODAL_X_AXIS, "X_AXIS", 0, "X Axis Locking", ""}, + {KNF_MODAL_Y_AXIS, "Y_AXIS", 0, "Y Axis Locking", ""}, + {KNF_MODAL_Z_AXIS, "Z_AXIS", 0, "Z Axis Locking", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -4630,18 +4639,18 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) } /* Constrain axes with X,Y,Z keys. */ - if (event->val == KM_PRESS && ELEM(event->type, EVT_XKEY, EVT_YKEY, EVT_ZKEY)) { - if (event->type == EVT_XKEY && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) { + if (ELEM(event->val, KNF_MODAL_X_AXIS, KNF_MODAL_Y_AXIS, KNF_MODAL_Z_AXIS)) { + if (event->val == KNF_MODAL_X_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) { kcd->constrain_axis = KNF_CONSTRAIN_AXIS_X; kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; kcd->axis_string[0] = 'X'; } - else if (event->type == EVT_YKEY && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) { + else if (event->val == KNF_MODAL_Y_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) { kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Y; kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; kcd->axis_string[0] = 'Y'; } - else if (event->type == EVT_ZKEY && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) { + else if (event->val == KNF_MODAL_Z_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) { kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Z; kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; kcd->axis_string[0] = 'Z'; From 88a2b054daba747ab2904c19f73c2e2a1bb9617a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 26 Sep 2021 23:10:26 +0200 Subject: [PATCH 0219/1500] Fix T91732: crash in Set Position node on empty mesh --- source/blender/functions/intern/field.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_set_position.cc | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 4f7ea8ec0ef..88e4e5ccd71 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -450,7 +450,7 @@ Vector evaluate_fields(ResourceScope &scope, type, array_size, buffer); } - procedure_executor.call(IndexRange(1), mf_params, mf_context); + procedure_executor.call(IndexRange(mask_size), mf_params, mf_context); } /* Copy data to supplied destination arrays if necessary. In some cases the evaluation above has diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index be923fdccb0..8caf961fc04 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -34,6 +34,9 @@ static void set_position_in_component(GeometryComponent &component, { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } fn::FieldEvaluator selection_evaluator{field_context, domain_size}; selection_evaluator.add(selection_field); From 1cd8a438bb59d0e9e5160e8c91f9c937aab8195f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 26 Sep 2021 23:19:31 +0200 Subject: [PATCH 0220/1500] Cleanup: simplify field evaluation --- source/blender/functions/intern/field.cc | 34 +++++++----------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 88e4e5ccd71..39688ef3daf 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -260,20 +260,6 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure, BLI_assert(procedure.validate()); } -/** - * Utility class that destructs elements from a partially initialized array. - */ -struct PartiallyInitializedArray : NonCopyable, NonMovable { - void *buffer; - IndexMask mask; - const CPPType *type; - - ~PartiallyInitializedArray() - { - this->type->destruct_indices(this->buffer, this->mask); - } -}; - /** * Evaluate fields in the given context. If possible, multiple fields should be evaluated together, * because that can be more efficient when they share common sub-fields. @@ -387,11 +373,11 @@ Vector evaluate_fields(ResourceScope &scope, /* Allocate a new buffer for the computed result. */ buffer = scope.linear_allocator().allocate(type.size() * array_size, type.alignment()); - /* Make sure that elements in the buffer will be destructed. */ - PartiallyInitializedArray &destruct_helper = scope.construct(); - destruct_helper.buffer = buffer; - destruct_helper.mask = mask; - destruct_helper.type = &type; + if (!type.is_trivially_destructible()) { + /* Destruct values in the end. */ + scope.add_destruct_call( + [buffer, mask, &type]() { type.destruct_indices(buffer, mask); }); + } r_varrays[out_index] = &scope.construct( GSpan{type, buffer, array_size}); @@ -435,11 +421,11 @@ Vector evaluate_fields(ResourceScope &scope, /* Allocate memory where the computed value will be stored in. */ void *buffer = scope.linear_allocator().allocate(type.size(), type.alignment()); - /* Use this to make sure that the value is destructed in the end. */ - PartiallyInitializedArray &destruct_helper = scope.construct(); - destruct_helper.buffer = buffer; - destruct_helper.mask = IndexRange(mask_size); - destruct_helper.type = &type; + if (!type.is_trivially_destructible() && mask_size > 0) { + BLI_assert(mask_size == 1); + /* Destruct value in the end. */ + scope.add_destruct_call([buffer, &type]() { type.destruct(buffer); }); + } /* Pass output buffer to the procedure executor. */ mf_params.add_uninitialized_single_output({type, buffer, mask_size}); From d046a1f2fa46eb14b45bfe696a3e2ad08c5110c0 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 26 Sep 2021 23:27:57 +0200 Subject: [PATCH 0221/1500] Functions: fail early when multi-function throws an exception Multi-functions are not allowed to throw exceptions that are not caught in the same multi-function. Previously, it was difficult to backtrack a crash to a previously thrown exception. --- .../intern/multi_function_procedure_executor.cc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc index b97282accdd..6d2d121bafd 100644 --- a/source/blender/functions/intern/multi_function_procedure_executor.cc +++ b/source/blender/functions/intern/multi_function_procedure_executor.cc @@ -1022,7 +1022,13 @@ static void execute_call_instruction(const MFCallInstruction &instruction, } } - fn.call(IndexRange(1), params, context); + try { + fn.call(IndexRange(1), params, context); + } + catch (...) { + /* Multi-functions must not throw exceptions. */ + BLI_assert_unreachable(); + } } else { MFParamsBuilder params(fn, &mask); @@ -1038,7 +1044,13 @@ static void execute_call_instruction(const MFCallInstruction &instruction, } } - fn.call(mask, params, context); + try { + fn.call(mask, params, context); + } + catch (...) { + /* Multi-functions must not throw exceptions. */ + BLI_assert_unreachable(); + } } } From f9e09819766d3578aefcbfecfcd10f4523cebd9b Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 27 Sep 2021 04:20:37 +0200 Subject: [PATCH 0222/1500] Fix T91666: Missing pivot point in VSE Add pivot point setting for combined timeline and preview mode. --- release/scripts/startup/bl_ui/space_sequencer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 543164f25fc..2e4ea515575 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -152,6 +152,8 @@ class SEQUENCER_HT_header(Header): row = layout.row(align=True) row.prop(sequencer_tool_settings, "overlap_mode", text="") row = layout.row(align=True) + row.prop(sequencer_tool_settings, "pivot_point", text="", icon_only=True) + row = layout.row(align=True) row.prop(tool_settings, "use_snap_sequencer", text="") sub = row.row(align=True) sub.popover(panel="SEQUENCER_PT_snapping") From ad3e5d2bf59f78cfa95c7ced56c959c5ad83e000 Mon Sep 17 00:00:00 2001 From: Peter Fog Date: Mon, 27 Sep 2021 04:40:05 +0200 Subject: [PATCH 0223/1500] VSE: Expose Zoom to Fit in all display modes Zoom to Fit is working in all display modes, but was only exposed in Image mode. This patch exposes it in all Image modes. Reviewed By: ISS Differential Revision: https://developer.blender.org/D12632 --- release/scripts/startup/bl_ui/space_sequencer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 2e4ea515575..5cfd258b174 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -377,9 +377,9 @@ class SEQUENCER_MT_view(Menu): layout.operator("view2d.zoom_border", text="Zoom") layout.menu("SEQUENCER_MT_preview_zoom") - if st.display_mode == 'IMAGE': - layout.prop(st, "use_zoom_to_fit") - elif st.display_mode == 'WAVEFORM': + layout.prop(st, "use_zoom_to_fit") + + if st.display_mode == 'WAVEFORM': layout.separator() layout.prop(st, "show_separate_color", text="Show Separate Color Channels") From d2dda0e8b90208e1146a5e1317cc47f8f047dbbb Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 27 Sep 2021 06:58:07 +0200 Subject: [PATCH 0224/1500] Fix T91694: VSE crashes when creating new scene Crash happened due to NULL dereference. Add NULL checks. --- source/blender/editors/space_sequencer/sequencer_draw.c | 2 +- .../editors/transform/transform_convert_sequencer_image.c | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 86818661a7f..146ea970087 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -2719,7 +2719,7 @@ void sequencer_draw_preview(const bContext *C, sequencer_draw_borders_overlay(sseq, v2d, scene); } - if (!draw_backdrop) { + if (!draw_backdrop && scene->ed != NULL) { SeqCollection *collection = SEQ_query_rendered_strips(&scene->ed->seqbase, timeline_frame, 0); Sequence *seq; SEQ_ITERATOR_FOREACH (seq, collection) { diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index 5db9a2e092f..6e3f12de472 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -113,12 +113,17 @@ static void freeSeqData(TransInfo *UNUSED(t), void createTransSeqImageData(TransInfo *t) { Editing *ed = SEQ_editing_get(t->scene); + + if (ed == NULL) { + return; + } + ListBase *seqbase = SEQ_active_seqbase_get(ed); SeqCollection *strips = SEQ_query_rendered_strips(seqbase, t->scene->r.cfra, 0); SEQ_filter_selected_strips(strips); const int count = SEQ_collection_len(strips); - if (ed == NULL || count == 0) { + if (count == 0) { SEQ_collection_free(strips); return; } From 037e66999af2e0f8cd0f5bfbd0e3bc91e2581ccb Mon Sep 17 00:00:00 2001 From: Lasse Foster Date: Mon, 27 Sep 2021 08:04:18 +0200 Subject: [PATCH 0225/1500] FIX: T91697 Eevee Generated texture coordinates completly missing This patch fixes Eevee-regression https://developer.blender.org/T91697 Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12634 --- source/blender/gpu/intern/gpu_codegen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 6cc8d7d9d05..f0046e879a0 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -718,7 +718,7 @@ static char *code_generate_vertex(GPUNodeGraph *graph, BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); } - if (attr->type == CD_HAIRLENGTH) { + else if (attr->type == CD_HAIRLENGTH) { BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n"); } From 10a26d583d34bf9ca293ca698ee1612d335a66b8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 27 Sep 2021 14:39:54 +1000 Subject: [PATCH 0226/1500] Fix knife tool using an invalid event value check The events value was checked without checking the expected modal state. --- source/blender/editors/mesh/editmesh_knife.c | 78 ++++++++++---------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 13519fad89d..341119270c2 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -4623,52 +4623,56 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) if (kcd->num.str_cur >= 2) { knife_reset_snap_angle_input(kcd); } - /* Modal numinput inactive, try to handle numeric inputs last... */ - if (!handled && event->val == KM_PRESS && handleNumInput(C, &kcd->num, event)) { - applyNumInput(&kcd->num, &snapping_increment_temp); - /* Restrict number key input to 0 - 90 degree range. */ - if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && - snapping_increment_temp < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { - kcd->angle_snapping_increment = snapping_increment_temp; + if (event->type != EVT_MODAL_MAP) { + /* Modal number-input inactive, try to handle numeric inputs last. */ + if (!handled && event->val == KM_PRESS && handleNumInput(C, &kcd->num, event)) { + applyNumInput(&kcd->num, &snapping_increment_temp); + /* Restrict number key input to 0 - 90 degree range. */ + if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + snapping_increment_temp < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + kcd->angle_snapping_increment = snapping_increment_temp; + } + knife_update_active(C, kcd); + knife_update_header(C, op, kcd); + ED_region_tag_redraw(kcd->region); + return OPERATOR_RUNNING_MODAL; } - knife_update_active(C, kcd); - knife_update_header(C, op, kcd); - ED_region_tag_redraw(kcd->region); - return OPERATOR_RUNNING_MODAL; } } /* Constrain axes with X,Y,Z keys. */ - if (ELEM(event->val, KNF_MODAL_X_AXIS, KNF_MODAL_Y_AXIS, KNF_MODAL_Z_AXIS)) { - if (event->val == KNF_MODAL_X_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) { - kcd->constrain_axis = KNF_CONSTRAIN_AXIS_X; - kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; - kcd->axis_string[0] = 'X'; - } - else if (event->val == KNF_MODAL_Y_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) { - kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Y; - kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; - kcd->axis_string[0] = 'Y'; - } - else if (event->val == KNF_MODAL_Z_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) { - kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Z; - kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; - kcd->axis_string[0] = 'Z'; - } - else { - /* Cycle through modes with repeated key presses. */ - if (kcd->constrain_axis_mode != KNF_CONSTRAIN_AXIS_MODE_LOCAL) { - kcd->constrain_axis_mode++; - kcd->axis_string[0] += 32; /* Lower case. */ + if (event->type == EVT_MODAL_MAP) { + if (ELEM(event->val, KNF_MODAL_X_AXIS, KNF_MODAL_Y_AXIS, KNF_MODAL_Z_AXIS)) { + if (event->val == KNF_MODAL_X_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_X; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; + kcd->axis_string[0] = 'X'; + } + else if (event->val == KNF_MODAL_Y_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Y; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; + kcd->axis_string[0] = 'Y'; + } + else if (event->val == KNF_MODAL_Z_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Z; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; + kcd->axis_string[0] = 'Z'; } else { - kcd->constrain_axis = KNF_CONSTRAIN_AXIS_NONE; - kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_NONE; + /* Cycle through modes with repeated key presses. */ + if (kcd->constrain_axis_mode != KNF_CONSTRAIN_AXIS_MODE_LOCAL) { + kcd->constrain_axis_mode++; + kcd->axis_string[0] += 32; /* Lower case. */ + } + else { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_NONE; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_NONE; + } } + kcd->axis_constrained = (kcd->constrain_axis != KNF_CONSTRAIN_AXIS_NONE); + knifetool_disable_angle_snapping(kcd); + knife_update_header(C, op, kcd); } - kcd->axis_constrained = (kcd->constrain_axis != KNF_CONSTRAIN_AXIS_NONE); - knifetool_disable_angle_snapping(kcd); - knife_update_header(C, op, kcd); } if (kcd->mode == MODE_DRAGGING) { From ddb0dc252777d3af1e80d6767b0e35f326d34df6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 27 Sep 2021 14:40:07 +1000 Subject: [PATCH 0227/1500] Fix knife tool missing refresh changing the lock axes --- source/blender/editors/mesh/editmesh_knife.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 341119270c2..eaecda28287 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -4672,6 +4672,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->axis_constrained = (kcd->constrain_axis != KNF_CONSTRAIN_AXIS_NONE); knifetool_disable_angle_snapping(kcd); knife_update_header(C, op, kcd); + ED_region_tag_redraw(kcd->region); + do_refresh = true; } } From fe49904646ced298685dd1e24819acd53d9f2619 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 27 Sep 2021 09:34:17 +0200 Subject: [PATCH 0228/1500] Fix: wrong socket shape in Vector input node --- source/blender/nodes/function/nodes/node_fn_input_vector.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 387689b3d1e..9548df7b423 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -25,7 +25,6 @@ namespace blender::nodes { static void fn_node_input_vector_declare(NodeDeclarationBuilder &b) { - b.is_function_node(); b.add_output("Vector"); }; From f3ace5aa8015a5b5fcd063054a37c6d18d5164d2 Mon Sep 17 00:00:00 2001 From: William Leeson Date: Fri, 24 Sep 2021 08:55:48 +0200 Subject: [PATCH 0229/1500] Fixes T91632 by stopping the sample correlation between dimensions which was causing rendering artifacts on simple scenes. Fix T91632: Stops the sample correlation between dimensions which was causing rendering artefacts on simple scenes. This is done by increasing the amount of jitter the Cranley Patterson Rotation is allowed to add. Also, it uses the y dimension of the of the sample table for 1D sampling which causes further decorrelation between dimensions. As an additional measure the x and y dimensions are swapped randomly to provide further decorrelation. Maniphest Tasks: T91632 Differential Revision: https://developer.blender.org/D12610 --- intern/cycles/kernel/kernel_jitter.h | 32 +++++++++++++--------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/intern/cycles/kernel/kernel_jitter.h b/intern/cycles/kernel/kernel_jitter.h index 354e8115538..ab38895da54 100644 --- a/intern/cycles/kernel/kernel_jitter.h +++ b/intern/cycles/kernel/kernel_jitter.h @@ -74,10 +74,6 @@ ccl_device_inline float cmj_randfloat_simple(uint i, uint p) ccl_device float pmj_sample_1D(const KernelGlobals *kg, uint sample, uint rng_hash, uint dimension) { - /* The PMJ sample sets contain a sample with (x,y) with NUM_PMJ_SAMPLES so for 1D - * the x part is used as the sample (TODO(@leesonw): Add using both x and y parts - * independently). */ - /* Perform Owen shuffle of the sample number to reorder the samples. */ #ifdef _SIMPLE_HASH_ const uint rv = cmj_hash_simple(dimension, rng_hash); @@ -95,7 +91,10 @@ ccl_device float pmj_sample_1D(const KernelGlobals *kg, uint sample, uint rng_ha const uint sample_set = s / NUM_PMJ_SAMPLES; const uint d = (dimension + sample_set); const uint dim = d % NUM_PMJ_PATTERNS; - int index = 2 * (dim * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)); + + /* The PMJ sample sets contain a sample with (x,y) with NUM_PMJ_SAMPLES so for 1D + * the x part is used for even dims and the y for odd. */ + int index = 2 * ((dim >> 1) * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)) + (dim & 1); float fx = kernel_tex_fetch(__sample_pattern_lut, index); @@ -104,12 +103,11 @@ ccl_device float pmj_sample_1D(const KernelGlobals *kg, uint sample, uint rng_ha # ifdef _SIMPLE_HASH_ float dx = cmj_randfloat_simple(d, rng_hash); # else - /* Only jitter within the grid interval. */ float dx = cmj_randfloat(d, rng_hash); # endif - fx = fx + dx * (1.0f / NUM_PMJ_SAMPLES); + /* Jitter sample locations and map back into [0 1]. */ + fx = fx + dx; fx = fx - floorf(fx); - #else # warning "Not using Cranley-Patterson Rotation." #endif @@ -136,12 +134,12 @@ ccl_device void pmj_sample_2D( /* Based on the sample number a sample pattern is selected and offset by the dimension. */ const uint sample_set = s / NUM_PMJ_SAMPLES; const uint d = (dimension + sample_set); - const uint dim = d % NUM_PMJ_PATTERNS; + uint dim = d % NUM_PMJ_PATTERNS; int index = 2 * (dim * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)); float fx = kernel_tex_fetch(__sample_pattern_lut, index); float fy = kernel_tex_fetch(__sample_pattern_lut, index + 1); - + #ifndef _NO_CRANLEY_PATTERSON_ROTATION_ /* Use Cranley-Patterson rotation to displace the sample pattern. */ # ifdef _SIMPLE_HASH_ @@ -151,17 +149,17 @@ ccl_device void pmj_sample_2D( float dx = cmj_randfloat(d, rng_hash); float dy = cmj_randfloat(d + 1, rng_hash); # endif - /* Only jitter within the grid cells. */ - fx = fx + dx * (1.0f / NUM_PMJ_DIVISIONS); - fy = fy + dy * (1.0f / NUM_PMJ_DIVISIONS); - fx = fx - floorf(fx); - fy = fy - floorf(fy); + /* Jitter sample locations and map back to the unit square [0 1]x[0 1]. */ + float sx = fx + dx; + float sy = fy + dy; + sx = sx - floorf(sx); + sy = sy - floorf(sy); #else # warning "Not using Cranley Patterson Rotation." #endif - (*x) = fx; - (*y) = fy; + (*x) = sx; + (*y) = sy; } CCL_NAMESPACE_END From 547f7d23cafb682d3ac6d1348d70caef411ecc51 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 27 Sep 2021 09:51:41 +0200 Subject: [PATCH 0230/1500] Geometry Nodes: support outputting collection children as instances This adds two new input sockets to the Collection Info node: * `Separate Children`: When turned off, the entire collection is output as a single collection instance (same behavior as before). When turned on, each child of the collection is output as a separate instance (children can be objects and collections). Toggling this input should not change the visual transforms of the output geometry. * `Reset Children`: Only used when `Separate Children` is on. When used, the transforms of the instances are reset to the origin. This is useful when one wants to e.g. instance the collection children separately in the upcoming instancing node. Part of D12478. --- .../nodes/node_geo_collection_info.cc | 69 ++++++++++++++++--- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index f4c295b06fb..d03221703f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -21,6 +21,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "BKE_collection.h" + #include "node_geometry_util.hh" namespace blender::nodes { @@ -28,6 +30,12 @@ namespace blender::nodes { static void geo_node_collection_info_declare(NodeDeclarationBuilder &b) { b.add_input("Collection").hide_label(); + b.add_input("Separate Children") + .description("Output each child of the collection as a separate instance"); + b.add_input("Reset Children") + .description( + "Reset the transforms of every child instance in the output. Only used when Separate " + "Children is enabled"); b.add_output("Geometry"); } @@ -57,23 +65,66 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) const bNode &bnode = params.node(); NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage; - const bool transform_space_relative = (node_storage->transform_space == - GEO_NODE_TRANSFORM_SPACE_RELATIVE); + const bool use_relative_transform = (node_storage->transform_space == + GEO_NODE_TRANSFORM_SPACE_RELATIVE); InstancesComponent &instances = geometry_set_out.get_component_for_write(); - float transform_mat[4][4]; - unit_m4(transform_mat); const Object *self_object = params.self_object(); - if (transform_space_relative) { - copy_v3_v3(transform_mat[3], collection->instance_offset); + const bool separate_children = params.get_input("Separate Children"); + if (separate_children) { + const bool reset_children = params.get_input("Reset Children"); + Vector children_collections; + LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) { + children_collections.append(collection_child->collection); + } + Vector children_objects; + LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) { + children_objects.append(collection_object->ob); + } - mul_m4_m4_pre(transform_mat, self_object->imat); + instances.reserve(children_collections.size() + children_objects.size()); + + for (Collection *child_collection : children_collections) { + float4x4 transform = float4x4::identity(); + if (!reset_children) { + add_v3_v3(transform.values[3], child_collection->instance_offset); + if (use_relative_transform) { + mul_m4_m4_pre(transform.values, self_object->imat); + } + else { + sub_v3_v3(transform.values[3], collection->instance_offset); + } + } + const int handle = instances.add_reference(*child_collection); + instances.add_instance(handle, transform); + } + for (Object *child_object : children_objects) { + const int handle = instances.add_reference(*child_object); + float4x4 transform = float4x4::identity(); + if (!reset_children) { + if (use_relative_transform) { + transform = self_object->imat; + } + else { + sub_v3_v3(transform.values[3], collection->instance_offset); + } + mul_m4_m4_post(transform.values, child_object->obmat); + } + instances.add_instance(handle, transform); + } } + else { + float4x4 transform = float4x4::identity(); + if (use_relative_transform) { + copy_v3_v3(transform.values[3], collection->instance_offset); + mul_m4_m4_pre(transform.values, self_object->imat); + } - const int handle = instances.add_reference(*collection); - instances.add_instance(handle, transform_mat, -1); + const int handle = instances.add_reference(*collection); + instances.add_instance(handle, transform); + } params.set_output("Geometry", geometry_set_out); } From 617954c1438096810ce8e47f09c25c8311baac4d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 27 Sep 2021 10:16:38 +0200 Subject: [PATCH 0231/1500] Geometry Nodes: new Instance on Points node This adds a new Instance on Points node that is a replacement for the old Point Instance node. Contrary to the old node, it does not have a mode to instance objects or collections directly. Instead, the node has to be used with an Object/ Collection Info to achieve the same effect. Rotation and scale of the instances can be adjusted in the node directly or can be controlled with a field to get some variation between instances. The node supports placing different instances on different points. The user has control over which instance is placed on which point using an Instance Index input. If that functionality is used, the Instance Geometry has to contain multiple instances that can are instanced separately. Differential Revision: https://developer.blender.org/D12478 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_geometry_set.hh | 13 +- source/blender/blenkernel/BKE_node.h | 1 + .../intern/geometry_component_instances.cc | 83 +++++++ .../blender/blenkernel/intern/geometry_set.cc | 13 ++ source/blender/blenkernel/intern/node.cc | 1 + .../modifiers/intern/MOD_nodes_evaluator.cc | 20 +- source/blender/nodes/CMakeLists.txt | 3 +- source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_geometry_exec.hh | 2 + source/blender/nodes/NOD_static_types.h | 1 + .../{ => legacy}/node_geo_point_instance.cc | 0 .../nodes/node_geo_instance_on_points.cc | 203 ++++++++++++++++++ 13 files changed, 324 insertions(+), 18 deletions(-) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_point_instance.cc (100%) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index e658706a946..88a0782a102 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -593,6 +593,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshUVSphere"), ]), GeometryNodeCategory("GEO_POINT", "Point", items=[ + NodeItem("GeometryNodeInstanceOnPoints", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeDistributePointsOnFaces", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_fields_legacy_poll), NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_fields_legacy_poll), diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 5fcdbc83e25..571c6c6a0a0 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -323,6 +323,7 @@ struct GeometrySet { bool has_instances() const; bool has_volume() const; bool has_curve() const; + bool has_realized_data() const; const Mesh *get_mesh_for_read() const; const PointCloud *get_pointcloud_for_read() const; @@ -478,7 +479,7 @@ class InstanceReference { Type type_ = Type::None; /** Depending on the type this is either null, an Object or Collection pointer. */ void *data_ = nullptr; - std::unique_ptr geometry_set_; + std::shared_ptr geometry_set_; public: InstanceReference() = default; @@ -493,17 +494,10 @@ class InstanceReference { InstanceReference(GeometrySet geometry_set) : type_(Type::GeometrySet), - geometry_set_(std::make_unique(std::move(geometry_set))) + geometry_set_(std::make_shared(std::move(geometry_set))) { } - InstanceReference(const InstanceReference &src) : type_(src.type_), data_(src.data_) - { - if (src.type_ == Type::GeometrySet) { - geometry_set_ = std::make_unique(*src.geometry_set_); - } - } - Type type() const { return type_; @@ -595,6 +589,7 @@ class InstancesComponent : public GeometryComponent { void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1); blender::Span references() const; + void remove_unused_references(); void ensure_geometry_instances(); GeometrySet &geometry_set_from_reference(const int reference_index); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 52f8a3d8136..5491f2a3de9 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1502,6 +1502,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_FILLET 1089 #define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES 1090 #define GEO_NODE_STRING_TO_CURVES 1091 +#define GEO_NODE_INSTANCE_ON_POINTS 1092 /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 9479d012cb8..f1f60266545 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -14,11 +14,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include + #include "BLI_float4x4.hh" #include "BLI_map.hh" #include "BLI_rand.hh" #include "BLI_set.hh" #include "BLI_span.hh" +#include "BLI_task.hh" #include "BLI_vector.hh" #include "DNA_collection_types.h" @@ -182,6 +185,86 @@ blender::Span InstancesComponent::references() const return references_; } +void InstancesComponent::remove_unused_references() +{ + using namespace blender; + using namespace blender::bke; + + const int tot_instances = this->instances_amount(); + const int tot_references_before = references_.size(); + + if (tot_instances == 0) { + /* If there are no instances, no reference is needed. */ + references_.clear(); + return; + } + if (tot_references_before == 1) { + /* There is only one reference and at least one instance. So the only existing reference is + * used. Nothing to do here. */ + return; + } + + Array usage_by_handle(tot_references_before, false); + std::mutex mutex; + + /* Loop over all instances to see which references are used. */ + threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { + /* Use local counter to avoid lock contention. */ + Array local_usage_by_handle(tot_references_before, false); + + for (const int i : range) { + const int handle = instance_reference_handles_[i]; + BLI_assert(handle >= 0 && handle < tot_references_before); + local_usage_by_handle[handle] = true; + } + + std::lock_guard lock{mutex}; + for (const int i : IndexRange(tot_references_before)) { + usage_by_handle[i] |= local_usage_by_handle[i]; + } + }); + + if (!usage_by_handle.as_span().contains(false)) { + /* All references are used. */ + return; + } + + /* Create new references and a mapping for the handles. */ + Vector handle_mapping; + VectorSet new_references; + int next_new_handle = 0; + bool handles_have_to_be_updated = false; + for (const int old_handle : IndexRange(tot_references_before)) { + if (!usage_by_handle[old_handle]) { + /* Add some dummy value. It won't be read again. */ + handle_mapping.append(-1); + } + else { + const InstanceReference &reference = references_[old_handle]; + handle_mapping.append(next_new_handle); + new_references.add_new(reference); + if (old_handle != next_new_handle) { + handles_have_to_be_updated = true; + } + next_new_handle++; + } + } + references_ = new_references; + + if (!handles_have_to_be_updated) { + /* All remaining handles are the same as before, so they don't have to be updated. This happens + * when unused handles are only at the end. */ + return; + } + + /* Update handles of instances. */ + threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { + for (const int i : range) { + instance_reference_handles_[i] = handle_mapping[instance_reference_handles_[i]]; + } + }); +} + int InstancesComponent::instances_amount() const { return instance_transforms_.size(); diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 54e9fadf8ed..1ebdde75f46 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -291,6 +291,19 @@ bool GeometrySet::has_curve() const return component != nullptr && component->has_curve(); } +/* Returns true when the geometry set has any data that is not an instance. */ +bool GeometrySet::has_realized_data() const +{ + if (components_.is_empty()) { + return false; + } + if (components_.size() > 1) { + return true; + } + /* Check if the only component is an #InstancesComponent. */ + return this->get_component_for_read() == nullptr; +} + /* Create a new geometry set that only contains the given mesh. */ GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership) { diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index c10aa3bbc5a..f1f643ffed7 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5764,6 +5764,7 @@ static void registerGeometryNodes() register_node_type_geo_delete_geometry(); register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_edge_split(); + register_node_type_geo_instance_on_points(); register_node_type_geo_input_index(); register_node_type_geo_input_material(); register_node_type_geo_input_normal(); diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index fd0205cffc5..6f18c4d40db 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -328,17 +328,21 @@ static void get_socket_value(const SocketRef &socket, void *r_value) * more complex defaults (other than just single values) in their socket declarations. */ if (bsocket.flag & SOCK_HIDE_VALUE) { const bNode &bnode = *socket.bnode(); - if (bsocket.type == SOCK_VECTOR && - ELEM(bnode.type, GEO_NODE_SET_POSITION, SH_NODE_TEX_NOISE)) { - new (r_value) Field( - std::make_shared("position", CPPType::get())); - return; + if (bsocket.type == SOCK_VECTOR) { + if (ELEM(bnode.type, GEO_NODE_SET_POSITION, SH_NODE_TEX_NOISE)) { + new (r_value) Field( + std::make_shared("position", CPPType::get())); + return; + } } - if (bsocket.type == SOCK_INT && bnode.type == FN_NODE_RANDOM_VALUE) { - new (r_value) Field(std::make_shared()); - return; + else if (bsocket.type == SOCK_INT) { + if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) { + new (r_value) Field(std::make_shared()); + return; + } } } + const bNodeSocketType *typeinfo = socket.typeinfo(); typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value); } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index e1cceae2964..844e838272c 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -151,6 +151,7 @@ set(SRC geometry/nodes/legacy/node_geo_material_assign.cc geometry/nodes/legacy/node_geo_select_by_material.cc geometry/nodes/legacy/node_geo_point_distribute.cc + geometry/nodes/legacy/node_geo_point_instance.cc geometry/nodes/node_geo_align_rotation_to_vector.cc geometry/nodes/node_geo_attribute_capture.cc @@ -202,6 +203,7 @@ set(SRC geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_distribute_points_on_faces.cc geometry/nodes/node_geo_edge_split.cc + geometry/nodes/node_geo_instance_on_points.cc geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_normal.cc geometry/nodes/node_geo_input_position.cc @@ -223,7 +225,6 @@ set(SRC geometry/nodes/node_geo_mesh_subdivide.cc geometry/nodes/node_geo_mesh_to_curve.cc geometry/nodes/node_geo_object_info.cc - geometry/nodes/node_geo_point_instance.cc geometry/nodes/node_geo_point_rotate.cc geometry/nodes/node_geo_point_scale.cc geometry/nodes/node_geo_point_separate.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index b37d4956e7a..bf780042600 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -80,6 +80,7 @@ void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_edge_split(void); +void register_node_type_geo_instance_on_points(void); void register_node_type_geo_input_index(void); void register_node_type_geo_input_material(void); void register_node_type_geo_input_normal(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 6ce3d0f2ab5..962e1c3c48f 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -46,6 +46,8 @@ using bke::WeakAnonymousAttributeID; using bke::WriteAttributeLookup; using fn::CPPType; using fn::Field; +using fn::FieldContext; +using fn::FieldEvaluator; using fn::FieldInput; using fn::FieldOperation; using fn::GField; diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 6af9a7b4e98..e9f7ec2c7ff 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -337,6 +337,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_ DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") +DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_point_instance.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc new file mode 100644 index 00000000000..21a130da8f9 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -0,0 +1,203 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_collection_types.h" + +#include "BLI_hash.h" +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Points").description("Points to instance on"); + b.add_input("Instance").description("Geometry that is instanced on the points"); + b.add_input("Pick Instance") + .supports_field() + .description("Place different instances on different points"); + b.add_input("Instance Index") + .implicit_field() + .description( + "Index of the instance that used for each point. This is only used when Pick Instances " + "is on. By default the point index is used"); + b.add_input("Rotation") + .subtype(PROP_EULER) + .supports_field() + .description("Rotation of the instances"); + b.add_input("Scale") + .default_value({1.0f, 1.0f, 1.0f}) + .supports_field() + .description("Scale of the instances"); + b.add_input("Stable ID") + .supports_field() + .description( + "ID for every instance that is used to identify it over time even when the number of " + "instances changes. Used for example for motion blur"); + + b.add_output("Instances"); +} + +static void add_instances_from_component(InstancesComponent &dst_component, + const GeometryComponent &src_component, + const GeoNodeExecParams ¶ms) +{ + GeometrySet instance = params.get_input("Instance"); + + const AttributeDomain domain = ATTR_DOMAIN_POINT; + const int domain_size = src_component.attribute_domain_size(domain); + + /* The initial size of the component might be non-zero when this function is called for multiple + * component types. */ + const int start_len = dst_component.instances_amount(); + dst_component.resize(start_len + domain_size); + MutableSpan dst_handles = dst_component.instance_reference_handles().slice(start_len, + domain_size); + MutableSpan dst_transforms = dst_component.instance_transforms().slice(start_len, + domain_size); + MutableSpan dst_stable_ids = dst_component.instance_ids().slice(start_len, domain_size); + + GeometryComponentFieldContext field_context{src_component, domain}; + FieldEvaluator field_evaluator{field_context, domain_size}; + + const VArray *pick_instance = nullptr; + const VArray *indices = nullptr; + const VArray *rotations = nullptr; + const VArray *scales = nullptr; + field_evaluator.add(params.get_input>("Pick Instance"), &pick_instance); + field_evaluator.add(params.get_input>("Instance Index"), &indices); + field_evaluator.add(params.get_input>("Rotation"), &rotations); + field_evaluator.add(params.get_input>("Scale"), &scales); + field_evaluator.add_with_destination(params.get_input>("Stable ID"), dst_stable_ids); + field_evaluator.evaluate(); + + GVArray_Typed positions = src_component.attribute_get_for_read( + "position", domain, {0, 0, 0}); + + const InstancesComponent *src_instances = instance.get_component_for_read(); + + /* Maps handles from the source instances to handles on the new instance. */ + Array handle_mapping; + /* Only fill #handle_mapping when it may be used below. */ + if (src_instances != nullptr && + (!pick_instance->is_single() || pick_instance->get_internal_single())) { + Span src_references = src_instances->references(); + handle_mapping.reinitialize(src_references.size()); + for (const int src_instance_handle : src_references.index_range()) { + const InstanceReference &reference = src_references[src_instance_handle]; + const int dst_instance_handle = dst_component.add_reference(reference); + handle_mapping[src_instance_handle] = dst_instance_handle; + } + } + + const int full_instance_handle = dst_component.add_reference(instance); + /* Add this reference last, because it is the most likely one to be removed later on. */ + const int empty_reference_handle = dst_component.add_reference(InstanceReference()); + + threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { + for (const int i : range) { + /* Compute base transform for every instances. */ + float4x4 &dst_transform = dst_transforms[i]; + dst_transform = float4x4::from_loc_eul_scale( + positions[i], rotations->get(i), scales->get(i)); + + /* Reference that will be used by this new instance. */ + int dst_handle = empty_reference_handle; + + const bool use_individual_instance = pick_instance->get(i); + if (use_individual_instance) { + if (src_instances != nullptr) { + const int src_instances_amount = src_instances->instances_amount(); + const int original_index = indices->get(i); + /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 refers to + * the last element. */ + const int index = mod_i(original_index, std::max(src_instances_amount, 1)); + if (index < src_instances_amount) { + /* Get the reference to the source instance. */ + const int src_handle = src_instances->instance_reference_handles()[index]; + dst_handle = handle_mapping[src_handle]; + + /* Take transforms of the source instance into account. */ + mul_m4_m4_post(dst_transform.values, + src_instances->instance_transforms()[index].values); + } + } + } + else { + /* Use entire source geometry as instance. */ + dst_handle = full_instance_handle; + } + /* Set properties of new instance. */ + dst_handles[i] = dst_handle; + } + }); + + if (pick_instance->is_single()) { + if (pick_instance->get_internal_single()) { + if (instance.has_realized_data()) { + params.error_message_add( + NodeWarningType::Info, + TIP_("Realized geometry is not used when pick instances is true")); + } + } + } +} + +static void geo_node_instance_on_points_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Points"); + GeometrySet geometry_set_out; + + geometry_set = geometry_set_realize_instances(geometry_set); + + InstancesComponent &instances = geometry_set_out.get_component_for_write(); + + if (geometry_set.has()) { + add_instances_from_component( + instances, *geometry_set.get_component_for_read(), params); + } + if (geometry_set.has()) { + add_instances_from_component( + instances, *geometry_set.get_component_for_read(), params); + } + if (geometry_set.has()) { + add_instances_from_component( + instances, *geometry_set.get_component_for_read(), params); + } + + /* Unused references may have been added above. Remove those now so that other nodes don't + * process them needlessly. */ + instances.remove_unused_references(); + + params.set_output("Instances", std::move(geometry_set_out)); +} + +} // namespace blender::nodes + +void register_node_type_geo_instance_on_points() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_instance_on_points_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_instance_on_points_exec; + nodeRegisterType(&ntype); +} From 30ef197c7b57e2763f66abf21a4a148826c365f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 24 Sep 2021 16:34:13 +0200 Subject: [PATCH 0232/1500] Kernel: allow unregistering BKE callback functions Introduce `BKE_callback_remove()`, which undoes the effect of `BKE_callback_add()`. It also respects `funcstore->alloc` by freeing the removed `funcstore` when needed. This allows for shorter-lived objects in memory to unregister their callbacks at the end of their lifespan. `BKE_callback_global_finalize()` has been adjusted so that the responsibility "remove a callback" is given to one function only. Reviewed by: campbellbarton Differential Revision: https://developer.blender.org/D12625 --- source/blender/blenkernel/BKE_callbacks.h | 1 + source/blender/blenkernel/intern/callbacks.c | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h index ef2a0ed34a0..7c518f33c89 100644 --- a/source/blender/blenkernel/BKE_callbacks.h +++ b/source/blender/blenkernel/BKE_callbacks.h @@ -130,6 +130,7 @@ void BKE_callback_exec_id_depsgraph(struct Main *bmain, struct Depsgraph *depsgraph, eCbEvent evt); void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt); +void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt); void BKE_callback_global_init(void); void BKE_callback_global_finalize(void); diff --git a/source/blender/blenkernel/intern/callbacks.c b/source/blender/blenkernel/intern/callbacks.c index 11ee9492b44..87d5961b12e 100644 --- a/source/blender/blenkernel/intern/callbacks.c +++ b/source/blender/blenkernel/intern/callbacks.c @@ -80,6 +80,15 @@ void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt) BLI_addtail(lb, funcstore); } +void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt) +{ + ListBase *lb = &callback_slots[evt]; + BLI_remlink(lb, funcstore); + if (funcstore->alloc) { + MEM_freeN(funcstore); + } +} + void BKE_callback_global_init(void) { /* do nothing */ @@ -95,10 +104,7 @@ void BKE_callback_global_finalize(void) bCallbackFuncStore *funcstore_next; for (funcstore = lb->first; funcstore; funcstore = funcstore_next) { funcstore_next = funcstore->next; - BLI_remlink(lb, funcstore); - if (funcstore->alloc) { - MEM_freeN(funcstore); - } + BKE_callback_remove(funcstore, evt); } } } From 8dcddbcc0778b6ac737e63ba7751a4441876e03c Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 27 Sep 2021 11:09:48 +0200 Subject: [PATCH 0233/1500] Fix T91711: Blender 3.0 - The Rain demo scene breaks (Proxy to Override auto conversion). Proxy conversion is a fairly particular case of liboverride creation, in which remapping all local usages of linked data probably makes more sense, rather than only doing so whitin the overridden 'group' of IDs. --- source/blender/blenkernel/intern/lib_override.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index f382fc0614b..e6ce6734e64 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1000,6 +1000,18 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE); + /* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created + * overrides. + * While this might not be 100% the desired behavior, it is likely to be the case most of the + * time. Ref: T91711. */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + if (!ID_IS_LINKED(id_iter)) { + id_iter->tag |= LIB_TAG_DOIT; + } + } + FOREACH_MAIN_ID_END; + return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL); } From 69893ef27c9188916648adb84d5867f2d39c780e Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 20 Sep 2021 18:11:52 +0200 Subject: [PATCH 0234/1500] 3DView: Use "real" angle for viewport roll Since its introduction in {rB5c569d227b64}, the view roll was based on horizontal movement only. Using a "real" angle not only feels more natural but also has the benefit of getting more precission the further away from the center you are (just like regular rotation, brush/stencil rotation etc.). A similar thing has already been implemented in the Grease Pencil Tools Addon, now make the blender standard roll the same. Since this is not using the transform system, we are still lacking a line in the viewport (this could be added but since this is always based on the center of the view we dont necessarily need this), as well as the additional Shift-extra-precission behavior. Fixes T89883 Maniphest Tasks: T89883 Differential Revision: https://developer.blender.org/D12582 --- .../editors/space_view3d/view3d_edit.c | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 8ed134c7fd1..564cdbf047b 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -38,6 +38,7 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_dial_2d.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -210,6 +211,9 @@ typedef struct ViewOpsData { * If we want the value before running the operator, add a separate member. */ char persp; + + /** Used for roll */ + Dial *dial; } init; /** Previous state (previous modal event handled). */ @@ -577,6 +581,10 @@ static void viewops_data_free(bContext *C, wmOperator *op) WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer); } + if (vod->init.dial) { + MEM_SAFE_FREE(vod->init.dial); + } + MEM_freeN(vod); op->customdata = NULL; } @@ -4352,18 +4360,9 @@ static void view_roll_angle( rv3d->view = RV3D_VIEW_USER; } -static void viewroll_apply(ViewOpsData *vod, int x, int UNUSED(y)) +static void viewroll_apply(ViewOpsData *vod, int x, int y) { - float angle = 0.0; - - { - float len1, len2, tot; - - tot = vod->region->winrct.xmax - vod->region->winrct.xmin; - len1 = (vod->region->winrct.xmax - x) / tot; - len2 = (vod->region->winrct.xmax - vod->init.event_xy[0]) / tot; - angle = (len1 - len2) * (float)M_PI * 4.0f; - } + float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y}); if (angle != 0.0f) { view_roll_angle(vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle); @@ -4517,6 +4516,10 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) viewops_data_alloc(C, op); viewops_data_create(C, op, event, viewops_flag_from_prefs()); vod = op->customdata; + vod->init.dial = BLI_dial_init( + (const float[2]){(vod->region->winrct.xmax - vod->region->winrct.xmin) / 2, + (vod->region->winrct.ymax - vod->region->winrct.ymin) / 2}, + FLT_EPSILON); ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); From 5d5504d8a4b6ba5b56154ac50fea60b9c5576572 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 27 Sep 2021 11:54:25 +0200 Subject: [PATCH 0235/1500] 3DView: ability to cancel out of viewport roll This adds the ability to cancel out of the roll using ESC or RMB (which is not common for viewops -- but makes sense in the case of roll I think). This resets the view as well as potential locked cameras to the original orientations (but does not remove potential autokeys -- which no transform does on cancel btw.) Maniphest Tasks: T89883 Differential Revision: https://developer.blender.org/D12582 --- source/blender/editors/space_view3d/view3d_edit.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 564cdbf047b..615e29f574f 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4408,6 +4408,13 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } } + else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) { + /* Note this does not remove autokeys on locked cameras. */ + copy_qt_qt(vod->rv3d->viewquat, vod->init.quat); + ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); + viewops_data_free(C, op); + return OPERATOR_CANCELLED; + } else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { event_code = VIEW_CONFIRM; } From d90f542b04fcef8d9fcaeb536cd183cd983fc423 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 27 Sep 2021 12:31:06 +0200 Subject: [PATCH 0236/1500] Cleanup: Remove function declaration without definition There is no function definition for this declaration. Instead there is `BKE_preferences_asset_library_remove()`. --- source/blender/blenkernel/BKE_preferences.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index 04a41d425bb..0876653566f 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -29,8 +29,6 @@ extern "C" { struct UserDef; struct bUserAssetLibrary; -void BKE_preferences_asset_library_free(struct bUserAssetLibrary *library) ATTR_NONNULL(); - struct bUserAssetLibrary *BKE_preferences_asset_library_add(struct UserDef *userdef, const char *name, const char *path) ATTR_NONNULL(1); From a13b9d20b55539605d31c7a77a07a63f96ff3565 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 27 Sep 2021 12:32:21 +0200 Subject: [PATCH 0237/1500] Cleanup: Move asset library remove function next to add function Better to keep such related operations close together in code. --- source/blender/blenkernel/BKE_preferences.h | 6 +++--- source/blender/blenkernel/intern/preferences.c | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index 0876653566f..acba2048614 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -32,13 +32,13 @@ struct bUserAssetLibrary; struct bUserAssetLibrary *BKE_preferences_asset_library_add(struct UserDef *userdef, const char *name, const char *path) ATTR_NONNULL(1); +void BKE_preferences_asset_library_remove(struct UserDef *userdef, + struct bUserAssetLibrary *library) ATTR_NONNULL(); + void BKE_preferences_asset_library_name_set(struct UserDef *userdef, struct bUserAssetLibrary *library, const char *name) ATTR_NONNULL(); -void BKE_preferences_asset_library_remove(struct UserDef *userdef, - struct bUserAssetLibrary *library) ATTR_NONNULL(); - struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_index( const struct UserDef *userdef, int index) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_name( diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c index 8dcf6de164a..c666dd4342a 100644 --- a/source/blender/blenkernel/intern/preferences.c +++ b/source/blender/blenkernel/intern/preferences.c @@ -61,6 +61,15 @@ bUserAssetLibrary *BKE_preferences_asset_library_add(UserDef *userdef, return library; } +/** + * Unlink and free a library preference member. + * \note Free's \a library itself. + */ +void BKE_preferences_asset_library_remove(UserDef *userdef, bUserAssetLibrary *library) +{ + BLI_freelinkN(&userdef->asset_libraries, library); +} + void BKE_preferences_asset_library_name_set(UserDef *userdef, bUserAssetLibrary *library, const char *name) @@ -74,15 +83,6 @@ void BKE_preferences_asset_library_name_set(UserDef *userdef, sizeof(library->name)); } -/** - * Unlink and free a library preference member. - * \note Free's \a library itself. - */ -void BKE_preferences_asset_library_remove(UserDef *userdef, bUserAssetLibrary *library) -{ - BLI_freelinkN(&userdef->asset_libraries, library); -} - bUserAssetLibrary *BKE_preferences_asset_library_find_from_index(const UserDef *userdef, int index) { return BLI_findlink(&userdef->asset_libraries, index); From c618075541ded922ead69770ed4acf49106b0ead Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 27 Sep 2021 12:43:42 +0200 Subject: [PATCH 0238/1500] Cleanup: make format --- intern/cycles/kernel/kernel_jitter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/intern/cycles/kernel/kernel_jitter.h b/intern/cycles/kernel/kernel_jitter.h index ab38895da54..1beaf3cc2b2 100644 --- a/intern/cycles/kernel/kernel_jitter.h +++ b/intern/cycles/kernel/kernel_jitter.h @@ -91,7 +91,7 @@ ccl_device float pmj_sample_1D(const KernelGlobals *kg, uint sample, uint rng_ha const uint sample_set = s / NUM_PMJ_SAMPLES; const uint d = (dimension + sample_set); const uint dim = d % NUM_PMJ_PATTERNS; - + /* The PMJ sample sets contain a sample with (x,y) with NUM_PMJ_SAMPLES so for 1D * the x part is used for even dims and the y for odd. */ int index = 2 * ((dim >> 1) * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)) + (dim & 1); @@ -139,7 +139,7 @@ ccl_device void pmj_sample_2D( float fx = kernel_tex_fetch(__sample_pattern_lut, index); float fy = kernel_tex_fetch(__sample_pattern_lut, index + 1); - + #ifndef _NO_CRANLEY_PATTERSON_ROTATION_ /* Use Cranley-Patterson rotation to displace the sample pattern. */ # ifdef _SIMPLE_HASH_ From 32ffb858d6da920926fcd37a6720d2a7b408e04b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 27 Sep 2021 18:06:17 +1000 Subject: [PATCH 0239/1500] Keymap: resolve conflict with use_alt_cursor/Ctrl-LMB to add/extrude While the option allows tools be be activated on press instead of tweak, this meant box-select was catching Ctrl-LMB which is also used for add/extrude in edit mode. Resolve this by always using tweak for selection tools, only supporting activation on press for other tools. Note that this doesn't impact the default configuration. --- .../keyconfig/keymap_data/blender_default.py | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 56ff776ea48..41b5d6f7998 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -76,7 +76,14 @@ class Params: "select_mouse_value_fallback", # Shorthand for: `('CLICK_DRAG' if params.use_pie_click_drag else 'PRESS')` "pie_value", + # Shorthand for: `{"type": params.tool_tweak, "value": 'ANY'}`. + "tool_tweak_event", # Shorthand for: `{"type": params.tool_maybe_tweak, "value": params.tool_maybe_tweak_value}`. + # + # NOTE: This is typically used for active tool key-map items however it should never + # be used for selection tools (the default box-select tool for example). + # Since this means with RMB select enabled in edit-mode for e.g. + # `Ctrl-LMB` would be caught by box-select instead of add/extrude. "tool_maybe_tweak_event", ) @@ -187,6 +194,7 @@ class Params: self.use_fallback_tool_rmb = self.use_fallback_tool if self.select_mouse == 'RIGHT' else False self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS' + self.tool_tweak_event = {"type": self.tool_tweak, "value": 'ANY'} self.tool_maybe_tweak_event = {"type": self.tool_maybe_tweak, "value": self.tool_maybe_tweak_value} @@ -6128,12 +6136,9 @@ def km_image_editor_tool_uv_cursor(params): "Image Editor Tool: Uv, Cursor", {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("uv.cursor_set", - {"type": params.tool_mouse, "value": 'PRESS'}, - None), + ("uv.cursor_set", {"type": params.tool_mouse, "value": 'PRESS'}, None), # Don't use `tool_maybe_tweak_event` since it conflicts with `PRESS` that places the cursor. - ("transform.translate", - {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.translate", params.tool_tweak_event, {"properties": [("release_confirm", True), ("cursor_transform", True)]}), ]}, ) @@ -6158,7 +6163,8 @@ def km_image_editor_tool_uv_select_box(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_box", - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), + # Don't use `tool_maybe_tweak_event`, see comment for this slot. + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), *_template_uv_select_for_fallback(params, fallback), ]}, ) @@ -6187,7 +6193,7 @@ def km_image_editor_tool_uv_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_lasso", - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), *_template_uv_select_for_fallback(params, fallback), ]}, ) @@ -6324,7 +6330,7 @@ def km_3d_view_tool_cursor(params): {"items": [ ("view3d.cursor3d", {"type": params.tool_mouse, "value": 'PRESS'}, None), # Don't use `tool_maybe_tweak_event` since it conflicts with `PRESS` that places the cursor. - ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'}, + ("transform.translate", params.tool_tweak_event, {"properties": [("release_confirm", True), ("cursor_transform", True)]}), ]}, ) @@ -6350,7 +6356,8 @@ def km_3d_view_tool_select_box(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "view3d.select_box", - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), + # Don't use `tool_maybe_tweak_event`, see comment for this slot. + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), *_template_view3d_select_for_fallback(params, fallback), ]}, ) @@ -6380,7 +6387,7 @@ def km_3d_view_tool_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "view3d.select_lasso", - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), *_template_view3d_select_for_fallback(params, fallback), ]} ) @@ -7035,10 +7042,8 @@ def km_3d_view_tool_sculpt_mask_by_color(params): "3D View Tool: Sculpt, Mask by Color", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'ANY'}, - None), - ("sculpt.mask_by_color", {"type": params.tool_tweak, "value": 'ANY'}, - None), + ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'ANY'}, None), + ("sculpt.mask_by_color", params.tool_tweak_event, None), ]}, ) @@ -7048,8 +7053,7 @@ def km_3d_view_tool_sculpt_face_set_edit(params): "3D View Tool: Sculpt, Face Set Edit", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.face_set_edit", {"type": params.tool_mouse, "value": 'PRESS'}, - None), + ("sculpt.face_set_edit", {"type": params.tool_mouse, "value": 'PRESS'}, None), ]}, ) @@ -7238,7 +7242,8 @@ def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "gpencil.select_box", - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), + # Don't use `tool_maybe_tweak_event`, see comment for this slot. + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), *_template_view3d_gpencil_select_for_fallback(params, fallback), ]}, ) @@ -7268,7 +7273,7 @@ def km_3d_view_tool_edit_gpencil_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "gpencil.select_lasso", - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_maybe_tweak_event))), + **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), *_template_view3d_gpencil_select_for_fallback(params, fallback), ]} ) @@ -7367,7 +7372,7 @@ def km_3d_view_tool_sculpt_gpencil_select_box(params): return ( "3D View Tool: Sculpt Gpencil, Select Box", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions("gpencil.select_box", **params.tool_maybe_tweak_event)}, + {"items": _template_items_tool_select_actions("gpencil.select_box", **params.tool_tweak_event)}, ) @@ -7386,7 +7391,7 @@ def km_3d_view_tool_sculpt_gpencil_select_lasso(params): return ( "3D View Tool: Sculpt Gpencil, Select Lasso", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select_actions("gpencil.select_lasso", **params.tool_maybe_tweak_event)}, + {"items": _template_items_tool_select_actions("gpencil.select_lasso", **params.tool_tweak_event)}, ) @@ -7408,8 +7413,9 @@ def km_sequencer_editor_tool_select_box(params, *, fallback): _fallback_id("Sequencer Tool: Select Box", fallback), {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ + # Don't use `tool_maybe_tweak_event`, see comment for this slot. *_template_items_tool_select_actions_simple( - "sequencer.select_box", **params.tool_maybe_tweak_event, + "sequencer.select_box", **params.tool_tweak_event, properties=[("tweak", params.select_mouse == 'LEFTMOUSE')], ), # RMB select can already set the frame, match the tweak tool. From 2c2e1b3d612d87b54ce3c3f1e52b7c89ee88a5c3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 27 Sep 2021 20:43:20 +1000 Subject: [PATCH 0240/1500] Fix T81922: Pose bones F-Curves hidden for unselected objects Whilst in pose-mode, the selection filter only includes other objects in pose-mode instead of the object selection. This makes sense as the selection of the pose bones what the user as acting on in the 3D view. The object selection only makes sense to use in object mode. Reviewed By: sybren Maniphest Tasks: T81922 Ref D12494 --- .../blender/editors/animation/anim_filter.c | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index b12e0ae5cab..e1d046428a8 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -3087,7 +3087,10 @@ static size_t animdata_filter_dopesheet_movieclips(bAnimContext *ac, } /* Helper for animdata_filter_dopesheet() - For checking if an object should be included or not */ -static bool animdata_filter_base_is_ok(bDopeSheet *ads, Base *base, int filter_mode) +static bool animdata_filter_base_is_ok(bDopeSheet *ads, + Base *base, + const eObjectMode object_mode, + int filter_mode) { Object *ob = base->object; @@ -3144,10 +3147,21 @@ static bool animdata_filter_base_is_ok(bDopeSheet *ads, Base *base, int filter_m } /* check selection and object type filters */ - if ((ads->filterflag & ADS_FILTER_ONLYSEL) && - !((base->flag & BASE_SELECTED) /*|| (base == sce->basact) */)) { - /* only selected should be shown */ - return false; + if (ads->filterflag & ADS_FILTER_ONLYSEL) { + if (object_mode & OB_MODE_POSE) { + /* When in pose-mode handle all pose-mode objects. + * This avoids problems with pose-mode where objects may be unselected, + * where a selected bone of an unselected object would be hidden. see: T81922. */ + if (!(base->object->mode & object_mode)) { + return false; + } + } + else { + /* only selected should be shown (ignore active) */ + if (!(base->flag & BASE_SELECTED)) { + return false; + } + } } /* check if object belongs to the filtering group if option to filter @@ -3185,7 +3199,7 @@ static Base **animdata_filter_ds_sorted_bases(bDopeSheet *ads, Base **sorted_bases = MEM_mallocN(sizeof(Base *) * tot_bases, "Dopesheet Usable Sorted Bases"); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - if (animdata_filter_base_is_ok(ads, base, filter_mode)) { + if (animdata_filter_base_is_ok(ads, base, OB_MODE_OBJECT, filter_mode)) { sorted_bases[num_bases++] = base; } } @@ -3278,8 +3292,10 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac, /* Filter and add contents of each base (i.e. object) without them sorting first * NOTE: This saves performance in cases where order doesn't matter */ + Object *obact = OBACT(view_layer); + const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT; LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - if (animdata_filter_base_is_ok(ads, base, filter_mode)) { + if (animdata_filter_base_is_ok(ads, base, object_mode, filter_mode)) { /* since we're still here, this object should be usable */ items += animdata_filter_dopesheet_ob(ac, anim_data, ads, base, filter_mode); } From 95af9317f0a80c72e553fce57795e4530525a259 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 27 Sep 2021 20:46:55 +1000 Subject: [PATCH 0241/1500] Cleanup: remove unnecessary use of MEM_SAFE_FREE macro --- source/blender/editors/space_view3d/view3d_edit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 615e29f574f..817e2739573 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -582,7 +582,7 @@ static void viewops_data_free(bContext *C, wmOperator *op) } if (vod->init.dial) { - MEM_SAFE_FREE(vod->init.dial); + MEM_freeN(vod->init.dial); } MEM_freeN(vod); From e87783a5ec1ee10d7471aaf29e3861fda4ab9918 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 27 Sep 2021 21:01:48 +1000 Subject: [PATCH 0242/1500] Cleanup: spelling in comments --- intern/ghost/GHOST_IWindow.h | 2 +- source/blender/blenloader/intern/versioning_userdef.c | 2 +- source/blender/draw/engines/overlay/overlay_extra.c | 2 +- source/blender/draw/intern/draw_cache_impl_particles.c | 2 +- source/blender/editors/space_view3d/view3d_edit.c | 2 +- source/creator/creator_signals.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h index 5f9bd808c8c..91f576ca304 100644 --- a/intern/ghost/GHOST_IWindow.h +++ b/intern/ghost/GHOST_IWindow.h @@ -40,7 +40,7 @@ * There are two coordinate systems: * * - The screen coordinate system. The origin of the screen is located in the - * upper left corner of the screen. + * upper left corner of the screen. * - The client rectangle coordinate system. The client rectangle of a window * is the area that is drawable by the application (excluding title bars etc.). */ diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index f4853ff803f..a0f35d32a7c 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -728,7 +728,7 @@ void blo_do_versions_userdef(UserDef *userdef) } } - /* patch to set Dupli Lightprobes and Grease Pencil */ + /* Patch to set dupli light-probes and grease-pencil. */ if (!USER_VERSION_ATLEAST(280, 58)) { userdef->dupflag |= USER_DUP_LIGHTPROBE; userdef->dupflag |= USER_DUP_GPENCIL; diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index 8f9db7f6f13..98db7136398 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -696,7 +696,7 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Lightprobe +/** \name Light-probe * \{ */ void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob) diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index 087d88f4b1f..96bdca7d935 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -1132,7 +1132,7 @@ static void particle_batch_cache_ensure_procedural_pos(PTCacheEdit *edit, cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf); } - /* Checking hair length seperatly, only allocating gpu memory when needed */ + /* Checking hair length separately, only allocating gpu memory when needed. */ if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex == NULL) { ListBase gpu_attrs = GPU_material_attributes(gpu_material); LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) { diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 817e2739573..aced0ac3a6e 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4409,7 +4409,7 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) { - /* Note this does not remove autokeys on locked cameras. */ + /* Note this does not remove auto-keys on locked cameras. */ copy_qt_qt(vod->rv3d->viewquat, vod->init.quat); ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); viewops_data_free(C, op); diff --git a/source/creator/creator_signals.c b/source/creator/creator_signals.c index 29e12a96fe1..5604fb4c58d 100644 --- a/source/creator/creator_signals.c +++ b/source/creator/creator_signals.c @@ -79,7 +79,7 @@ static void sig_handle_fpe(int UNUSED(sig)) } # endif -/* handling ctrl-c event in console */ +/* Handling `Ctrl-C` event in the console. */ # if !defined(WITH_HEADLESS) static void sig_handle_blender_esc(int sig) { From 2a0db195c90d786beee34a8c3693063d2826d299 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 27 Sep 2021 13:46:55 +0200 Subject: [PATCH 0243/1500] Fix viewport roll working wrong Mistake in own {rB69893ef27c91}. Was mixing screen on region coordinates. --- source/blender/editors/space_view3d/view3d_edit.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index aced0ac3a6e..d917674194a 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4523,10 +4523,9 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) viewops_data_alloc(C, op); viewops_data_create(C, op, event, viewops_flag_from_prefs()); vod = op->customdata; - vod->init.dial = BLI_dial_init( - (const float[2]){(vod->region->winrct.xmax - vod->region->winrct.xmin) / 2, - (vod->region->winrct.ymax - vod->region->winrct.ymin) / 2}, - FLT_EPSILON); + vod->init.dial = BLI_dial_init((const float[2]){BLI_rcti_cent_x(&vod->region->winrct), + BLI_rcti_cent_y(&vod->region->winrct)}, + FLT_EPSILON); ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); From 4a562f50778c39ce087432094cd4eac86821a0b5 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 27 Sep 2021 14:23:56 +0200 Subject: [PATCH 0244/1500] Fix T91714: Cycles direct/indirect clamp distinction not working correctly --- intern/cycles/kernel/kernel_accumulate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index 9e12d24dcf4..f4d00e4c20c 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -386,7 +386,7 @@ ccl_device_inline void kernel_accum_light(INTEGRATOR_STATE_CONST_ARGS, { /* The throughput for shadow paths already contains the light shader evaluation. */ float3 contribution = INTEGRATOR_STATE(shadow_path, throughput); - kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(shadow_path, bounce) - 1); + kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(shadow_path, bounce)); ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, render_buffer); From 0419c74ae8b43cb259bd3539669eba52b4aa8fa5 Mon Sep 17 00:00:00 2001 From: Peter Fog Date: Mon, 27 Sep 2021 14:36:56 +0200 Subject: [PATCH 0245/1500] VSE: Clamp resulting frame in multiply mode The clamp added will ensure immediate speed direction change on changing to/from positive/negative speed factor when using the Speed effect strip's Multiply mode. Reviewed By: ISS, sergey Differential Revision: https://developer.blender.org/D12462 --- source/blender/sequencer/intern/effects.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 4448db013fe..427a8835879 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -3154,6 +3154,7 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq) float target_frame = 0; for (int frame_index = 1; frame_index < effect_strip_length; frame_index++) { target_frame += evaluate_fcurve(fcu, seq->startdisp + frame_index); + CLAMP(target_frame, 0, seq->seq1->len); v->frameMap[frame_index] = target_frame; } } From 8fecc2a8525467ee2fbbaae16ddbbc10b3050d46 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 27 Sep 2021 11:30:52 +0200 Subject: [PATCH 0246/1500] Increase VSE strip channels limit from 32 to 128 The original limit dates back from 2002 when Blender went open source. After that many years some productions (e.g., Sprite Fright) are already experiencing limitations for complex edits. The future plans is to support an initial shorter (2?) number of channels with support to "unlimited" channels. Finally, I'm bumping the minimum file requirement since files with more than 32 channels won't work well in old Blender versions. In a future commit I will implement a sanitization so that we only read (and write) 128 channels. Making sure future changes of this number won't corrupt Blender. Differential Revision: https://developer.blender.org/D12645 --- .../blender/blenkernel/BKE_blender_version.h | 4 +- .../blenloader/intern/versioning_300.c | 56 ++++++++++--------- source/blender/makesdna/DNA_sequence_types.h | 2 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 1f25106404a..6f7e6363a37 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,13 +39,13 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 25 +#define BLENDER_FILE_SUBVERSION 26 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file * was written with too new a version. */ #define BLENDER_FILE_MIN_VERSION 300 -#define BLENDER_FILE_MIN_SUBVERSION 11 +#define BLENDER_FILE_MIN_SUBVERSION 26 /** User readable version string. */ const char *BKE_blender_version_string(void); diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 692c114d206..aceba82a657 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -505,20 +505,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - #blo_do_versions_300 in this file. - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - do_versions_idproperty_ui_data(bmain); - + if (!MAIN_VERSION_ATLEAST(bmain, 300, 26)) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { ToolSettings *tool_settings = scene->toolsettings; ImagePaintSettings *imapaint = &tool_settings->imapaint; @@ -535,6 +522,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) imapaint->clone = NULL; } } + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { if (brush->clone.image != NULL && ELEM(brush->clone.image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { @@ -542,6 +530,21 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #blo_do_versions_300 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + do_versions_idproperty_ui_data(bmain); + } } static void version_switch_node_input_prefix(Main *bmain) @@ -1453,17 +1456,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 26)) { LISTBASE_FOREACH (Object *, ob, &bmain->objects) { LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { if (md->type == eModifierType_Nodes) { @@ -1515,4 +1508,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } } \ No newline at end of file diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 25330acd486..86369ff7684 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -486,7 +486,7 @@ typedef struct SequencerScopes { struct ImBuf *histogram_ibuf; } SequencerScopes; -#define MAXSEQ 32 +#define MAXSEQ 128 #define SELECT 1 From 2bd020521578549eb47c58c7984c9a35b7c35cd8 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 27 Sep 2021 15:10:28 +0200 Subject: [PATCH 0247/1500] Geometry Nodes: make field links thinner than other links This makes it easier to spot which links contain fields and which contain data. Actually, the patch makes all other links a bit thicker. However, with soon-to-be-implemented theme changes, the perceived thickness will be the same as before. This is part of T91563. Differential Revision: https://developer.blender.org/D12646 --- source/blender/editors/space_node/drawnode.cc | 21 +++++++++++++++++-- .../shaders/gpu_shader_2D_nodelink_vert.glsl | 4 +++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index b5746dc772a..f64df0e7142 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3933,9 +3933,11 @@ static struct { uint p0_id, p1_id, p2_id, p3_id; uint colid_id, muted_id; uint dim_factor_id; + uint thickness_id; GPUVertBufRaw p0_step, p1_step, p2_step, p3_step; GPUVertBufRaw colid_step, muted_step; GPUVertBufRaw dim_factor_step; + GPUVertBufRaw thickness_step; uint count; bool enabled; } g_batch_link; @@ -3952,6 +3954,8 @@ static void nodelink_batch_reset() g_batch_link.inst_vbo, g_batch_link.muted_id, &g_batch_link.muted_step); GPU_vertbuf_attr_get_raw_data( g_batch_link.inst_vbo, g_batch_link.dim_factor_id, &g_batch_link.dim_factor_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.thickness_id, &g_batch_link.thickness_step); g_batch_link.count = 0; } @@ -4071,6 +4075,8 @@ static void nodelink_batch_init() &format_inst, "domuted", GPU_COMP_U8, 2, GPU_FETCH_INT); g_batch_link.dim_factor_id = GPU_vertformat_attr_add( &format_inst, "dim_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + g_batch_link.thickness_id = GPU_vertformat_attr_add( + &format_inst, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM); /* Alloc max count but only draw the range we need. */ GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE); @@ -4147,7 +4153,8 @@ static void nodelink_batch_add_link(const SpaceNode *snode, int th_col3, bool drawarrow, bool drawmuted, - float dim_factor) + float dim_factor, + float thickness) { /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); @@ -4167,6 +4174,7 @@ static void nodelink_batch_add_link(const SpaceNode *snode, char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step); muted[0] = drawmuted; *(float *)GPU_vertbuf_raw_step(&g_batch_link.dim_factor_step) = dim_factor; + *(float *)GPU_vertbuf_raw_step(&g_batch_link.thickness_step) = thickness; if (g_batch_link.count == NODELINK_GROUP_SIZE) { nodelink_batch_draw(snode); @@ -4182,6 +4190,13 @@ void node_draw_link_bezier(const View2D *v2d, int th_col3) { const float dim_factor = node_link_dim_factor(v2d, link); + float thickness = 1.5f; + if (snode->edittree->type == NTREE_GEOMETRY) { + if (link->fromsock && link->fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) { + /* Make field links a bit thinner. */ + thickness = 1.0f; + } + } float vec[4][2]; const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT; @@ -4205,7 +4220,8 @@ void node_draw_link_bezier(const View2D *v2d, th_col3, drawarrow, drawmuted, - dim_factor); + dim_factor, + thickness); } else { /* Draw single link. */ @@ -4231,6 +4247,7 @@ void node_draw_link_bezier(const View2D *v2d, GPU_batch_uniform_1i(batch, "doArrow", drawarrow); GPU_batch_uniform_1i(batch, "doMuted", drawmuted); GPU_batch_uniform_1f(batch, "dim_factor", dim_factor); + GPU_batch_uniform_1f(batch, "thickness", thickness); GPU_batch_draw(batch); } } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl index aae7f641af8..2194d23a4ea 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl @@ -19,6 +19,7 @@ in vec2 P3; in ivec4 colid_doarrow; in ivec2 domuted; in float dim_factor; +in float thickness; uniform vec4 colors[6]; @@ -41,6 +42,7 @@ uniform vec4 colors[3]; uniform bool doArrow; uniform bool doMuted; uniform float dim_factor; +uniform float thickness; # define colShadow colors[0] # define colStart colors[1] @@ -103,7 +105,7 @@ void main(void) finalColor[3] *= dim_factor; /* Expand into a line */ - gl_Position.xy += exp_axis * expandSize * expand_dist; + gl_Position.xy += exp_axis * expandSize * expand_dist * thickness; /* If the link is not muted or is not a reroute arrow the points are squashed to the center of * the line. Magic numbers are defined in drawnode.c */ From a6b53ef99492267f8f27fd58ea35104b88e1bec8 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 27 Sep 2021 14:47:51 +0200 Subject: [PATCH 0248/1500] Cycles: print name of kernels on errors in CUDA queue, for debugging --- intern/cycles/device/cuda/queue.cpp | 54 ++++++++++++++++----------- intern/cycles/device/cuda/queue.h | 2 + intern/cycles/device/device_queue.cpp | 14 +++++-- intern/cycles/device/device_queue.h | 2 + 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/intern/cycles/device/cuda/queue.cpp b/intern/cycles/device/cuda/queue.cpp index b7f86c10553..1149a835b14 100644 --- a/intern/cycles/device/cuda/queue.cpp +++ b/intern/cycles/device/cuda/queue.cpp @@ -116,18 +116,18 @@ bool CUDADeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *ar } /* Launch kernel. */ - cuda_device_assert(cuda_device_, - cuLaunchKernel(cuda_kernel.function, - num_blocks, - 1, - 1, - num_threads_per_block, - 1, - 1, - shared_mem_bytes, - cuda_stream_, - args, - 0)); + assert_success(cuLaunchKernel(cuda_kernel.function, + num_blocks, + 1, + 1, + num_threads_per_block, + 1, + 1, + shared_mem_bytes, + cuda_stream_, + args, + 0), + "enqueue"); return !(cuda_device_->have_error()); } @@ -139,7 +139,8 @@ bool CUDADeviceQueue::synchronize() } const CUDAContextScope scope(cuda_device_); - cuda_device_assert(cuda_device_, cuStreamSynchronize(cuda_stream_)); + assert_success(cuStreamSynchronize(cuda_stream_), "synchronize"); + debug_synchronize(); return !(cuda_device_->have_error()); @@ -162,9 +163,9 @@ void CUDADeviceQueue::zero_to_device(device_memory &mem) assert(mem.device_pointer != 0); const CUDAContextScope scope(cuda_device_); - cuda_device_assert( - cuda_device_, - cuMemsetD8Async((CUdeviceptr)mem.device_pointer, 0, mem.memory_size(), cuda_stream_)); + assert_success( + cuMemsetD8Async((CUdeviceptr)mem.device_pointer, 0, mem.memory_size(), cuda_stream_), + "zero_to_device"); } void CUDADeviceQueue::copy_to_device(device_memory &mem) @@ -185,10 +186,10 @@ void CUDADeviceQueue::copy_to_device(device_memory &mem) /* Copy memory to device. */ const CUDAContextScope scope(cuda_device_); - cuda_device_assert( - cuda_device_, + assert_success( cuMemcpyHtoDAsync( - (CUdeviceptr)mem.device_pointer, mem.host_pointer, mem.memory_size(), cuda_stream_)); + (CUdeviceptr)mem.device_pointer, mem.host_pointer, mem.memory_size(), cuda_stream_), + "copy_to_device"); } void CUDADeviceQueue::copy_from_device(device_memory &mem) @@ -204,10 +205,19 @@ void CUDADeviceQueue::copy_from_device(device_memory &mem) /* Copy memory from device. */ const CUDAContextScope scope(cuda_device_); - cuda_device_assert( - cuda_device_, + assert_success( cuMemcpyDtoHAsync( - mem.host_pointer, (CUdeviceptr)mem.device_pointer, mem.memory_size(), cuda_stream_)); + mem.host_pointer, (CUdeviceptr)mem.device_pointer, mem.memory_size(), cuda_stream_), + "copy_from_device"); +} + +void CUDADeviceQueue::assert_success(CUresult result, const char *operation) +{ + if (result != CUDA_SUCCESS) { + const char *name = cuewErrorString(result); + cuda_device_->set_error(string_printf( + "%s in CUDA queue %s (%s)", name, operation, debug_active_kernels().c_str())); + } } unique_ptr CUDADeviceQueue::graphics_interop_create() diff --git a/intern/cycles/device/cuda/queue.h b/intern/cycles/device/cuda/queue.h index 62e3aa3d6c2..4d1995ed69e 100644 --- a/intern/cycles/device/cuda/queue.h +++ b/intern/cycles/device/cuda/queue.h @@ -60,6 +60,8 @@ class CUDADeviceQueue : public DeviceQueue { protected: CUDADevice *cuda_device_; CUstream cuda_stream_; + + void assert_success(CUresult result, const char *operation); }; CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_queue.cpp b/intern/cycles/device/device_queue.cpp index a89ba68d62c..f2b2f3496e0 100644 --- a/intern/cycles/device/device_queue.cpp +++ b/intern/cycles/device/device_queue.cpp @@ -57,8 +57,9 @@ void DeviceQueue::debug_init_execution() { if (VLOG_IS_ON(3)) { last_sync_time_ = time_dt(); - last_kernels_enqueued_ = 0; } + + last_kernels_enqueued_ = 0; } void DeviceQueue::debug_enqueue(DeviceKernel kernel, const int work_size) @@ -66,8 +67,9 @@ void DeviceQueue::debug_enqueue(DeviceKernel kernel, const int work_size) if (VLOG_IS_ON(3)) { VLOG(4) << "GPU queue launch " << device_kernel_as_string(kernel) << ", work_size " << work_size; - last_kernels_enqueued_ |= (uint64_t(1) << (uint64_t)kernel); } + + last_kernels_enqueued_ |= (uint64_t(1) << (uint64_t)kernel); } void DeviceQueue::debug_synchronize() @@ -80,8 +82,14 @@ void DeviceQueue::debug_synchronize() stats_kernel_time_[last_kernels_enqueued_] += elapsed_time; last_sync_time_ = new_time; - last_kernels_enqueued_ = 0; } + + last_kernels_enqueued_ = 0; +} + +string DeviceQueue::debug_active_kernels() +{ + return device_kernel_mask_as_string(last_kernels_enqueued_); } CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_queue.h b/intern/cycles/device/device_queue.h index edda3e61d51..e6835b787cf 100644 --- a/intern/cycles/device/device_queue.h +++ b/intern/cycles/device/device_queue.h @@ -21,6 +21,7 @@ #include "device/device_graphics_interop.h" #include "util/util_logging.h" #include "util/util_map.h" +#include "util/util_string.h" #include "util/util_unique_ptr.h" CCL_NAMESPACE_BEGIN @@ -101,6 +102,7 @@ class DeviceQueue { void debug_init_execution(); void debug_enqueue(DeviceKernel kernel, const int work_size); void debug_synchronize(); + string debug_active_kernels(); /* Combination of kernels enqueued together sync last synchronize. */ DeviceKernelMask last_kernels_enqueued_; From b077f0684e28ce3f200cab2a36657fb253be1786 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 27 Sep 2021 12:29:42 +0200 Subject: [PATCH 0249/1500] RNA: Fix bad usages of `scene` pointer in Update callbacks. Scene passed to the update callback is the active scene it //may// not be that actual ID owner of the affected data (although in practice it should always be currently). --- source/blender/makesrna/intern/rna_scene.c | 20 +++++++++++-------- .../blender/makesrna/intern/rna_sequencer.c | 7 ++++--- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 80fc13faab4..ba5b3095996 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -848,8 +848,9 @@ static void rna_Scene_camera_update(Main *bmain, Scene *UNUSED(scene_unused), Po DEG_relations_tag_update(bmain); } -static void rna_Scene_fps_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Scene_fps_update(Main *bmain, Scene *UNUSED(active_scene), PointerRNA *ptr) { + Scene *scene = (Scene *)ptr->owner_id; DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_FPS | ID_RECALC_SEQUENCER_STRIPS); /* NOTE: Tag via dependency graph will take care of all the updates ion the evaluated domain, * however, changes in FPS actually modifies an original skip length, @@ -857,9 +858,9 @@ static void rna_Scene_fps_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(p SEQ_sound_update_length(bmain, scene); } -static void rna_Scene_listener_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Scene_listener_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_LISTENER); + DEG_id_tag_update(ptr->owner_id, ID_RECALC_AUDIO_LISTENER); } static void rna_Scene_volume_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) @@ -885,8 +886,11 @@ static const char *rna_Scene_statistics_string_get(Scene *scene, return ED_info_statistics_string(bmain, scene, view_layer); } -static void rna_Scene_framelen_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Scene_framelen_update(Main *UNUSED(bmain), + Scene *UNUSED(active_scene), + PointerRNA *ptr) { + Scene *scene = (Scene *)ptr->owner_id; scene->r.framelen = (float)scene->r.framapto / (float)scene->r.images; } @@ -1940,9 +1944,9 @@ static void rna_Scene_use_audio_set(PointerRNA *ptr, bool value) } } -static void rna_Scene_use_audio_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Scene_use_audio_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_MUTE); + DEG_id_tag_update(ptr->owner_id, ID_RECALC_AUDIO_MUTE); } static int rna_Scene_sync_mode_get(PointerRNA *ptr) @@ -2199,9 +2203,9 @@ static void rna_SceneCamera_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Po } } -static void rna_SceneSequencer_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_SceneSequencer_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { - SEQ_cache_cleanup(scene); + SEQ_cache_cleanup((Scene *)ptr->owner_id); } static char *rna_ToolSettings_path(PointerRNA *UNUSED(ptr)) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index b713ffb68b4..6a03ee03f71 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -840,9 +840,9 @@ static int rna_Sequence_proxy_filepath_length(PointerRNA *ptr) return strlen(path); } -static void rna_Sequence_audio_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Sequence_audio_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { - DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + DEG_id_tag_update(ptr->owner_id, ID_RECALC_SEQUENCER_STRIPS); } static void rna_Sequence_pan_range( @@ -940,8 +940,9 @@ static void rna_Sequence_filepath_update(Main *bmain, Scene *UNUSED(scene), Poin rna_Sequence_invalidate_raw_update(bmain, scene, ptr); } -static void rna_Sequence_sound_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Sequence_sound_update(Main *bmain, Scene *UNUSED(active_scene), PointerRNA *ptr) { + Scene *scene = (Scene *)ptr->owner_id; DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO); DEG_relations_tag_update(bmain); } From 5949d598bc33e0f15fc6dd127c6df03f6f0caced Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 27 Sep 2021 14:57:00 +0200 Subject: [PATCH 0250/1500] RNA: Make is clear that `Scene` parameter of `update` callback may be NULL. There are cases where there is no way to ensure we do have/know about an active scene. Further more, this should not be required to perform 'real' updates on data, only to perform additional special handling in current scene (mostly related to editing tools, UI, etc.). This pointer is actually almost never used in practice, and half of its current usages are fairly close to abuse of the system (like calls to `ED_gpencil_tag_scene_gpencil` or `BKE_rigidbody_cache_reset`). This commit ensures that the few places using this 'active scene' pointer are safely handling the `NULL` case, and clearly document the fact that a NULL scene pointer is valid. --- source/blender/makesrna/intern/rna_access.c | 1 + source/blender/makesrna/intern/rna_image.c | 4 +++- source/blender/makesrna/intern/rna_internal_types.h | 11 ++++++++++- source/blender/makesrna/intern/rna_nodetree.c | 6 +++--- source/blender/makesrna/intern/rna_rigidbody.c | 7 ++++--- source/blender/makesrna/intern/rna_scene.c | 4 +++- source/blender/makesrna/intern/rna_sculpt_paint.c | 4 +++- source/blender/makesrna/intern/rna_space.c | 6 ++++-- 8 files changed, 31 insertions(+), 12 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index fceb6d045c3..f1980eed811 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -2206,6 +2206,7 @@ void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop) rna_property_update(C, CTX_data_main(C), CTX_data_scene(C), ptr, prop); } +/* NOTE: `scene` pointer may be NULL. */ void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop) { rna_property_update(NULL, bmain, scene, ptr, prop); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 4a013dc9bd7..2f42e521b52 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -161,7 +161,9 @@ static void rna_ImageUser_update(Main *bmain, Scene *scene, PointerRNA *ptr) ImageUser *iuser = ptr->data; ID *id = ptr->owner_id; - BKE_image_user_frame_calc(NULL, iuser, scene->r.cfra); + if (scene != NULL) { + BKE_image_user_frame_calc(NULL, iuser, scene->r.cfra); + } if (id) { if (GS(id->name) == ID_NT) { diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index 22a9b9d930a..29df7a53c44 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -44,11 +44,20 @@ typedef struct IDProperty IDProperty; /* Function Callbacks */ -typedef void (*UpdateFunc)(struct Main *main, struct Scene *scene, struct PointerRNA *ptr); +/** Update callback for an RNA property. + * + * \note This is NOT called automatically when writing into the property, it needs to be called + * manually (through #RNA_property_update or #RNA_property_update_main) when needed. + * + * \param bmain: the Main data-base to which `ptr` data belongs. + * \param active_scene: The current active scene (may be NULL in some cases). + * \param ptr: The RNA pointer data to update. */ +typedef void (*UpdateFunc)(struct Main *bmain, struct Scene *active_scene, struct PointerRNA *ptr); typedef void (*ContextPropUpdateFunc)(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop); typedef void (*ContextUpdateFunc)(struct bContext *C, struct PointerRNA *ptr); + typedef int (*EditableFunc)(struct PointerRNA *ptr, const char **r_info); typedef int (*ItemEditableFunc)(struct PointerRNA *ptr, int index); typedef struct IDProperty **(*IDPropertiesFunc)(struct PointerRNA *ptr); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 1cf0684448d..c06ff7715b3 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -3742,7 +3742,7 @@ static void rna_Node_image_layer_update(Main *bmain, Scene *scene, PointerRNA *p rna_Node_update(bmain, scene, ptr); - if (scene->nodetree != NULL) { + if (scene != NULL && scene->nodetree != NULL) { ntreeCompositUpdateRLayers(scene->nodetree); } } @@ -3914,7 +3914,7 @@ static const EnumPropertyItem *rna_Node_view_layer_itemf(bContext *UNUSED(C), static void rna_Node_view_layer_update(Main *bmain, Scene *scene, PointerRNA *ptr) { rna_Node_update(bmain, scene, ptr); - if (scene->nodetree != NULL) { + if (scene != NULL && scene->nodetree != NULL) { ntreeCompositUpdateRLayers(scene->nodetree); } } @@ -4349,7 +4349,7 @@ static void rna_ShaderNodeScript_update(Main *bmain, Scene *scene, PointerRNA *p { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - RenderEngineType *engine_type = RE_engines_find(scene->r.engine); + RenderEngineType *engine_type = (scene != NULL) ? RE_engines_find(scene->r.engine) : NULL; if (engine_type && engine_type->update_script_node) { /* auto update node */ diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c index 0b56a73efa2..c51931d0d1a 100644 --- a/source/blender/makesrna/intern/rna_rigidbody.c +++ b/source/blender/makesrna/intern/rna_rigidbody.c @@ -215,9 +215,10 @@ static void rna_RigidBodyWorld_constraints_collection_update(Main *bmain, static void rna_RigidBodyOb_reset(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { - RigidBodyWorld *rbw = scene->rigidbody_world; - - BKE_rigidbody_cache_reset(rbw); + if (scene != NULL) { + RigidBodyWorld *rbw = scene->rigidbody_world; + BKE_rigidbody_cache_reset(rbw); + } } static void rna_RigidBodyOb_shape_update(Main *bmain, Scene *scene, PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index ba5b3095996..00b7f0c9106 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -676,7 +676,9 @@ static void rna_ToolSettings_snap_mode_set(struct PointerRNA *ptr, int value) /* Grease Pencil update cache */ static void rna_GPencil_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { - ED_gpencil_tag_scene_gpencil(scene); + if (scene != NULL) { + ED_gpencil_tag_scene_gpencil(scene); + } } static void rna_Gpencil_extend_selection(bContext *C, PointerRNA *UNUSED(ptr)) diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 45e85d14865..a2db1536f55 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -130,7 +130,9 @@ const EnumPropertyItem rna_enum_symmetrize_direction_items[] = { static void rna_GPencil_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { /* mark all grease pencil datablocks of the scene */ - ED_gpencil_tag_scene_gpencil(scene); + if (scene != NULL) { + ED_gpencil_tag_scene_gpencil(scene); + } } const EnumPropertyItem rna_enum_particle_edit_disconnected_hair_brush_items[] = { diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 7b57c0fd6a5..88ef1597e4c 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -906,7 +906,7 @@ static void rna_GPencil_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UN static void rna_SpaceView3D_camera_update(Main *bmain, Scene *scene, PointerRNA *ptr) { View3D *v3d = (View3D *)(ptr->data); - if (v3d->scenelock) { + if (v3d->scenelock && scene != NULL) { wmWindowManager *wm = bmain->wm.first; scene->camera = v3d->camera; @@ -1540,7 +1540,9 @@ static PointerRNA rna_SpaceImageEditor_uvedit_get(PointerRNA *ptr) static void rna_SpaceImageEditor_mode_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) { - ED_space_image_paint_update(bmain, bmain->wm.first, scene); + if (scene != NULL) { + ED_space_image_paint_update(bmain, bmain->wm.first, scene); + } } static void rna_SpaceImageEditor_show_stereo_set(PointerRNA *ptr, int value) From 43167a2c251b8388be3dc4d2460913f41de13c49 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 27 Sep 2021 15:08:28 +0200 Subject: [PATCH 0251/1500] Fix T90570: Constraint validity not updated with library overrides. For some reason was assuming setting a property in RNA would call its update callback if any, but this actually needs to be done separately. So add this call to `rna_property_override_operation_apply`. --- .../intern/rna_access_compare_override.c | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index f8a36c1b2e6..be8972dbff3 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -614,20 +614,25 @@ static bool rna_property_override_operation_apply(Main *bmain, } /* get and set the default values as appropriate for the various types */ - return override_apply(bmain, - ptr_dst, - ptr_src, - ptr_storage, - prop_dst, - prop_src, - prop_storage, - len_dst, - len_src, - len_storage, - ptr_item_dst, - ptr_item_src, - ptr_item_storage, - opop); + const bool sucess = override_apply(bmain, + ptr_dst, + ptr_src, + ptr_storage, + prop_dst, + prop_src, + prop_storage, + len_dst, + len_src, + len_storage, + ptr_item_dst, + ptr_item_src, + ptr_item_storage, + opop); + if (sucess) { + RNA_property_update_main(bmain, NULL, ptr_dst, prop_dst); + } + + return sucess; } /** From 3d2ce25afd7e8ed823277f34f370fb1fb49a739e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 27 Sep 2021 15:33:48 +0200 Subject: [PATCH 0252/1500] Geometry Nodes: support creating new attributes in modifier This patch allows passing a field to the modifier as output. In the modifier, the user can choose an attribute name. The attribute will be filled with values computed by the field. This only works for realized mesh/curve/point data. As mentioned in T91376, the output domain is selected in the node group itself. We might want to add this functionality to the modifier later as well, but not now. Differential Revision: https://developer.blender.org/D12644 --- release/scripts/startup/bl_ui/space_node.py | 5 + source/blender/makesdna/DNA_node_types.h | 5 +- source/blender/makesrna/intern/rna_nodetree.c | 8 + source/blender/modifiers/intern/MOD_nodes.cc | 157 ++++++++++++++++-- 4 files changed, 156 insertions(+), 19 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 5f36009901a..f806fc345d1 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -754,6 +754,11 @@ class NodeTreeInterfacePanel: # Display descriptions only for Geometry Nodes, since it's only used in the modifier panel. if tree.type == 'GEOMETRY': layout.prop(active_socket, "description") + field_socket_prefixes = { + "NodeSocketInt", "NodeSocketColor", "NodeSocketVector", "NodeSocketBool", "NodeSocketFloat"} + is_field_type = any(active_socket.bl_socket_idname.startswith(prefix) for prefix in field_socket_prefixes) + if in_out == "OUT" and is_field_type: + layout.prop(active_socket, "attribute_domain") active_socket.draw(context, layout) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 38f38b4ba1e..34c74e5bd88 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -120,7 +120,10 @@ typedef struct bNodeSocket { /* XXX deprecated, kept for forward compatibility */ short stack_type DNA_DEPRECATED; char display_shape; - char _pad[1]; + + /* #AttributeDomain used when the geometry nodes modifier creates an attribute for a group + * output. */ + char attribute_domain; /* Runtime-only cache of the number of input links, for multi-input sockets. */ short total_inputs; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index c06ff7715b3..04f60c0d229 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10833,6 +10833,14 @@ static void rna_def_node_socket_interface(BlenderRNA *brna) prop, "Hide Value", "Hide the socket input value even when the socket is not connected"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); + RNA_def_property_ui_text( + prop, + "Attribute Domain", + "Attribute domain used by the geometry nodes modifier to create an attribute output"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + /* registration */ prop = RNA_def_property(srna, "bl_socket_idname", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "typeinfo->idname"); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 8c02c83d479..c39beb63eb3 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -103,6 +103,8 @@ using blender::Span; using blender::StringRef; using blender::StringRefNull; using blender::Vector; +using blender::bke::OutputAttribute; +using blender::fn::GField; using blender::fn::GMutablePointer; using blender::fn::GPointer; using blender::nodes::GeoNodeExecParams; @@ -589,6 +591,35 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) } } + LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { + if (!socket_type_has_attribute_toggle(*socket)) { + continue; + } + + const std::string idprop_name = socket->identifier + attribute_name_suffix; + IDProperty *new_prop = IDP_NewString("", idprop_name.c_str(), MAX_NAME); + if (socket->description[0] != '\0') { + IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop); + ui_data->description = BLI_strdup(socket->description); + } + IDP_AddToGroup(nmd->settings.properties, new_prop); + + if (old_properties != nullptr) { + IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, idprop_name.c_str()); + if (old_prop != nullptr) { + /* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only + * want to replace the values). So release it temporarily and replace it after. */ + IDPropertyUIData *ui_data = new_prop->ui_data; + new_prop->ui_data = nullptr; + IDP_CopyPropertyContent(new_prop, old_prop); + if (new_prop->ui_data != nullptr) { + IDP_ui_data_free(new_prop); + } + new_prop->ui_data = ui_data; + } + } + } + if (old_properties != nullptr) { IDP_FreeProperty(old_properties); } @@ -778,6 +809,72 @@ static void clear_runtime_data(NodesModifierData *nmd) } } +static void store_field_on_geometry_component(GeometryComponent &component, + const StringRef attribute_name, + AttributeDomain domain, + const GField &field) +{ + /* If the attribute name corresponds to a built-in attribute, use the domain of the built-in + * attribute instead. */ + if (component.attribute_is_builtin(attribute_name)) { + component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); + std::optional meta_data = component.attribute_get_meta_data(attribute_name); + if (meta_data.has_value()) { + domain = meta_data->domain; + } + else { + return; + } + } + const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(field.cpp_type()); + OutputAttribute attribute = component.attribute_try_get_for_output_only( + attribute_name, domain, data_type); + if (attribute) { + /* In the future we could also evaluate all output fields at once. */ + const int domain_size = component.attribute_domain_size(domain); + blender::bke::GeometryComponentFieldContext field_context{component, domain}; + blender::fn::FieldEvaluator field_evaluator{field_context, domain_size}; + field_evaluator.add_with_destination(field, attribute.varray()); + field_evaluator.evaluate(); + attribute.save(); + } +} + +static void store_output_value_in_geometry(GeometrySet &geometry_set, + NodesModifierData *nmd, + const InputSocketRef &socket, + const GPointer value) +{ + if (!socket_type_has_attribute_toggle(*socket.bsocket())) { + return; + } + const std::string prop_name = socket.identifier() + attribute_name_suffix; + const IDProperty *prop = IDP_GetPropertyFromGroup(nmd->settings.properties, prop_name.c_str()); + if (prop == nullptr) { + return; + } + const StringRefNull attribute_name = IDP_String(prop); + if (attribute_name.is_empty()) { + return; + } + const GField &field = *(const GField *)value.get(); + const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&nmd->node_group->outputs, + socket.index()); + const AttributeDomain domain = (AttributeDomain)interface_socket->attribute_domain; + if (geometry_set.has_mesh()) { + MeshComponent &component = geometry_set.get_component_for_write(); + store_field_on_geometry_component(component, attribute_name, domain, field); + } + if (geometry_set.has_pointcloud()) { + PointCloudComponent &component = geometry_set.get_component_for_write(); + store_field_on_geometry_component(component, attribute_name, domain, field); + } + if (geometry_set.has_curve()) { + CurveComponent &component = geometry_set.get_component_for_write(); + store_field_on_geometry_component(component, attribute_name, domain, field); + } +} + /** * Evaluate a node group to compute the output geometry. * Currently, this uses a fairly basic and inefficient algorithm that might compute things more @@ -785,7 +882,7 @@ static void clear_runtime_data(NodesModifierData *nmd) */ static GeometrySet compute_geometry(const DerivedNodeTree &tree, Span group_input_nodes, - const InputSocketRef &socket_to_compute, + const NodeRef &output_node, GeometrySet input_geometry_set, NodesModifierData *nmd, const ModifierEvalContext *ctx) @@ -828,7 +925,9 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, input_geometry_set.clear(); Vector group_outputs; - group_outputs.append({root_context, &socket_to_compute}); + for (const InputSocketRef *socket_ref : output_node.inputs().drop_back(1)) { + group_outputs.append({root_context, socket_ref}); + } std::optional geo_logger; @@ -856,9 +955,15 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger); } - BLI_assert(eval_params.r_output_values.size() == 1); - GMutablePointer result = eval_params.r_output_values[0]; - return result.relocate_out(); + GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out(); + + for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) { + GMutablePointer socket_value = eval_params.r_output_values[socket->index()]; + store_output_value_in_geometry(output_geometry_set, nmd, *socket, socket_value); + socket_value.destruct(); + } + + return output_geometry_set; } /** @@ -928,24 +1033,23 @@ static void modifyGeometry(ModifierData *md, const NodeTreeRef &root_tree_ref = tree.root_context().tree(); Span input_nodes = root_tree_ref.nodes_by_type("NodeGroupInput"); Span output_nodes = root_tree_ref.nodes_by_type("NodeGroupOutput"); - if (output_nodes.size() != 1) { return; } - Span group_outputs = output_nodes[0]->inputs().drop_back(1); - - if (group_outputs.size() == 0) { + const NodeRef &output_node = *output_nodes[0]; + Span group_outputs = output_node.inputs().drop_back(1); + if (group_outputs.is_empty()) { return; } - const InputSocketRef *group_output = group_outputs[0]; - if (group_output->idname() != "NodeSocketGeometry") { + const InputSocketRef *first_output_socket = group_outputs[0]; + if (first_output_socket->idname() != "NodeSocketGeometry") { return; } geometry_set = compute_geometry( - tree, input_nodes, *group_outputs[0], std::move(geometry_set), nmd, ctx); + tree, input_nodes, output_node, std::move(geometry_set), nmd, ctx); } static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) @@ -977,11 +1081,11 @@ static void modifyGeometrySet(ModifierData *md, /* Drawing the properties manually with #uiItemR instead of #uiDefAutoButsRNA allows using * the node socket identifier for the property names, since they are unique, but also having * the correct label displayed in the UI. */ -static void draw_property_for_socket(uiLayout *layout, - NodesModifierData *nmd, - PointerRNA *bmain_ptr, - PointerRNA *md_ptr, - const bNodeSocket &socket) +static void draw_property_for_input_socket(uiLayout *layout, + NodesModifierData *nmd, + PointerRNA *bmain_ptr, + PointerRNA *md_ptr, + const bNodeSocket &socket) { /* The property should be created in #MOD_nodes_update_interface with the correct type. */ IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket.identifier); @@ -1060,6 +1164,18 @@ static void draw_property_for_socket(uiLayout *layout, } } +static void draw_property_for_output_socket(uiLayout *layout, + PointerRNA *md_ptr, + const bNodeSocket &socket) +{ + char socket_id_esc[sizeof(socket.identifier) * 2]; + BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + const std::string rna_path_attribute_name = "[\"" + StringRef(socket_id_esc) + + attribute_name_suffix + "\"]"; + + uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, socket.name, ICON_NONE); +} + static void panel_draw(const bContext *C, Panel *panel) { uiLayout *layout = panel->layout; @@ -1087,7 +1203,12 @@ static void panel_draw(const bContext *C, Panel *panel) RNA_main_pointer_create(bmain, &bmain_ptr); LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) { - draw_property_for_socket(layout, nmd, &bmain_ptr, ptr, *socket); + draw_property_for_input_socket(layout, nmd, &bmain_ptr, ptr, *socket); + } + LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { + if (socket_type_has_attribute_toggle(*socket)) { + draw_property_for_output_socket(layout, ptr, *socket); + } } } From 4a0ddeb62bb4a438dfa9cedc7ea5a528531eaaee Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 27 Sep 2021 15:37:07 +0200 Subject: [PATCH 0253/1500] Nodes: Change wire color to increase contrast If the theme used by the user did not touch the wire or the wire outline colors this will update them as well. This was supposed to be a part of a bigger UI theme change for 3.0. But it was expedited because of the recent change in line thickness for the noodles (2bd02052157). Theme change by Pablo Vazquez. Differential Revision: https://developer.blender.org/D12649 --- release/datafiles/userdef/userdef_default_theme.c | 2 +- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenloader/intern/versioning_userdef.c | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 85532d01d5c..05c5a625215 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -817,7 +817,7 @@ const bTheme U_theme_default = { }, .shade2 = RGBA(0x7f707064), .grid = RGBA(0x23232300), - .wire = RGBA(0x808080ff), + .wire = RGBA(0x232323ff), .select = RGBA(0xed5700ff), .active = RGBA(0xffffffff), .edge_select = RGBA(0xffffffff), diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 6f7e6363a37..b973d8b9a18 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 26 +#define BLENDER_FILE_SUBVERSION 27 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index a0f35d32a7c..64ecfbe78de 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -291,6 +291,16 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) btheme->space_sequencer.grid[3] = 255; } + if (!USER_VERSION_ATLEAST(300, 27)) { + /* If users have not changed the color of the wires inner color or main color, + * set it to the new default. */ + if ((btheme->space_node.wire[0] == 35) && (btheme->space_node.wire[1] == 35) && + (btheme->space_node.wire[2] == 35) && (btheme->space_node.syntaxr[0] == 128) && + (btheme->space_node.syntaxr[1] == 128) && (btheme->space_node.syntaxr[2] == 128)) { + FROM_DEFAULT_V4_UCHAR(space_node.wire); + } + } + /** * Versioning code until next subversion bump goes here. * From 7270ba011cd08656c0b18a60e6503223c1f50dcc Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 27 Sep 2021 16:33:41 +0200 Subject: [PATCH 0254/1500] Fix two issues with recent new Append code. * ID pointer returned by `wm_file_link_append_datablock_ex` was improperly extracted from `WMLinkAppendDataItem` before append step. * Code deleting linked IDs when their local matching version was re-used did not properly clear `LIB_TAG_DOIT` beforehand. --- source/blender/windowmanager/intern/wm_files_link.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 92335f28d94..ea0b0a9feaa 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -809,6 +809,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } /* Remove linked IDs when a local existing data has been reused instead. */ + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { WMLinkAppendDataItem *item = itemlink->link; @@ -822,7 +823,6 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } BLI_assert(ID_IS_LINKED(id)); BLI_assert(id->newid != NULL); - BLI_assert((id->tag & LIB_TAG_DOIT) == 0); id->tag |= LIB_TAG_DOIT; item->new_id = id->newid; @@ -1347,13 +1347,13 @@ static ID *wm_file_link_append_datablock_ex(Main *bmain, /* Link datablock. */ wm_link_do(lapp_data, NULL, bmain, scene, view_layer, v3d); - /* Get linked datablock and free working data. */ - ID *id = item->new_id; - if (do_append) { wm_append_do(lapp_data, NULL, bmain, scene, view_layer, v3d); } + /* Get linked datablock and free working data. */ + ID *id = item->new_id; + wm_link_append_data_free(lapp_data); BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); From 2189dfd6e25a7bb6b734116619d87bc2d2a535ff Mon Sep 17 00:00:00 2001 From: Patrick Mours Date: Wed, 22 Sep 2021 16:23:08 +0200 Subject: [PATCH 0255/1500] Cycles: Rework OptiX visibility flags handling Before the visibility test against the visibility flags was performed in an any-hit program in OptiX (called `__anyhit__kernel_optix_visibility_test`), which was using the `__prim_visibility` array. This is not entirely correct however, since `__prim_visibility` is filled with the merged visibility flags of all objects that reference that primitive, so if one object uses different visibility flags than another object, but they both are instances of the same geometry, they would appear the same way. The reason that the any-hit program was used rather than the OptiX instance visibility mask is that the latter is currently limited to 8 bits only, which is not sufficient to contain all Cycles visibility flags (12 bits). To mostly fix the problem with multiple instances and different visibility flags, I changed things to use the OptiX instance visibility mask for a subset of the Cycles visibility flags (`PATH_RAY_CAMERA` to `PATH_RAY_VOLUME_SCATTER`, which fit into 8 bits) and only fall back to the visibility test any-hit program if that isn't enough (e.g. the ray visibility mask exceeds 8 bits or when using the built-in curves from OptiX, since the any-hit program is then also used to skip the curve endcaps). This may also improve performance in some cases, since by default OptiX can now perform the normal scene intersection trace calls entirely on RT cores without having to jump back to the SM on every hit to execute the any-hit program. Fixes T89801 Differential Revision: https://developer.blender.org/D12604 --- intern/cycles/device/optix/device_impl.cpp | 44 +++++++---- intern/cycles/device/optix/device_impl.h | 3 +- intern/cycles/kernel/bvh/bvh.h | 53 +++++++++---- intern/cycles/kernel/device/optix/kernel.cu | 87 ++++++++++++++------- 4 files changed, 127 insertions(+), 60 deletions(-) diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index b54d423a183..5f5eff53063 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -315,6 +315,11 @@ bool OptiXDevice::load_kernels(const uint kernel_features) group_descs[PG_HITS].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; group_descs[PG_HITS].hitgroup.moduleAH = optix_module; group_descs[PG_HITS].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_shadow_all_hit"; + group_descs[PG_HITV].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; + group_descs[PG_HITV].hitgroup.moduleCH = optix_module; + group_descs[PG_HITV].hitgroup.entryFunctionNameCH = "__closesthit__kernel_optix_hit"; + group_descs[PG_HITV].hitgroup.moduleAH = optix_module; + group_descs[PG_HITV].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_volume_test"; if (kernel_features & KERNEL_FEATURE_HAIR) { if (kernel_features & KERNEL_FEATURE_HAIR_THICK) { @@ -397,6 +402,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features) trace_css = std::max(trace_css, stack_size[PG_HITD].cssIS + stack_size[PG_HITD].cssAH); trace_css = std::max(trace_css, stack_size[PG_HITS].cssIS + stack_size[PG_HITS].cssAH); trace_css = std::max(trace_css, stack_size[PG_HITL].cssIS + stack_size[PG_HITL].cssAH); + trace_css = std::max(trace_css, stack_size[PG_HITV].cssIS + stack_size[PG_HITV].cssAH); trace_css = std::max(trace_css, stack_size[PG_HITD_MOTION].cssIS + stack_size[PG_HITD_MOTION].cssAH); trace_css = std::max(trace_css, @@ -421,6 +427,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features) pipeline_groups.push_back(groups[PG_HITD]); pipeline_groups.push_back(groups[PG_HITS]); pipeline_groups.push_back(groups[PG_HITL]); + pipeline_groups.push_back(groups[PG_HITV]); if (motion_blur) { pipeline_groups.push_back(groups[PG_HITD_MOTION]); pipeline_groups.push_back(groups[PG_HITS_MOTION]); @@ -459,6 +466,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features) pipeline_groups.push_back(groups[PG_HITD]); pipeline_groups.push_back(groups[PG_HITS]); pipeline_groups.push_back(groups[PG_HITL]); + pipeline_groups.push_back(groups[PG_HITV]); if (motion_blur) { pipeline_groups.push_back(groups[PG_HITD_MOTION]); pipeline_groups.push_back(groups[PG_HITS_MOTION]); @@ -1390,25 +1398,33 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) /* Set user instance ID to object index (but leave low bit blank). */ instance.instanceId = ob->get_device_index() << 1; - /* Have to have at least one bit in the mask, or else instance would always be culled. */ - instance.visibilityMask = 1; + /* Add some of the object visibility bits to the mask. + * __prim_visibility contains the combined visibility bits of all instances, so is not + * reliable if they differ between instances. But the OptiX visibility mask can only contain + * 8 bits, so have to trade-off here and select just a few important ones. + */ + instance.visibilityMask = ob->visibility_for_tracing() & 0xFF; - if (ob->get_geometry()->has_volume) { - /* Volumes have a special bit set in the visibility mask so a trace can mask only volumes. - */ - instance.visibilityMask |= 2; + /* Have to have at least one bit in the mask, or else instance would always be culled. */ + if (0 == instance.visibilityMask) { + instance.visibilityMask = 0xFF; } - if (ob->get_geometry()->geometry_type == Geometry::HAIR) { - /* Same applies to curves (so they can be skipped in local trace calls). */ - instance.visibilityMask |= 4; - - if (motion_blur && ob->get_geometry()->has_motion_blur() && - static_cast(ob->get_geometry())->curve_shape == CURVE_THICK) { + if (ob->get_geometry()->geometry_type == Geometry::HAIR && + static_cast(ob->get_geometry())->curve_shape == CURVE_THICK) { + if (motion_blur && ob->get_geometry()->has_motion_blur()) { /* Select between motion blur and non-motion blur built-in intersection module. */ instance.sbtOffset = PG_HITD_MOTION - PG_HITD; } } + else { + /* Can disable __anyhit__kernel_optix_visibility_test by default (except for thick curves, + * since it needs to filter out endcaps there). + * It is enabled where necessary (visibility mask exceeds 8 bits or the other any-hit + * programs like __anyhit__kernel_optix_shadow_all_hit) via OPTIX_RAY_FLAG_ENFORCE_ANYHIT. + */ + instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_ANYHIT; + } /* Insert motion traversable if object has motion. */ if (motion_blur && ob->use_motion()) { @@ -1474,7 +1490,7 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) delete[] reinterpret_cast(&motion_transform); /* Disable instance transform if object uses motion transform already. */ - instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + instance.flags |= OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; /* Get traversable handle to motion transform. */ optixConvertPointerToTraversableHandle(context, @@ -1491,7 +1507,7 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) } else { /* Disable instance transform if geometry already has it applied to vertex data. */ - instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + instance.flags |= OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; /* Non-instanced objects read ID from 'prim_object', so distinguish * them from instanced objects with the low bit set. */ instance.instanceId |= 1; diff --git a/intern/cycles/device/optix/device_impl.h b/intern/cycles/device/optix/device_impl.h index 91ef52e0a5a..3695ac6afc2 100644 --- a/intern/cycles/device/optix/device_impl.h +++ b/intern/cycles/device/optix/device_impl.h @@ -40,6 +40,7 @@ enum { PG_HITD, /* Default hit group. */ PG_HITS, /* __SHADOW_RECORD_ALL__ hit group. */ PG_HITL, /* __BVH_LOCAL__ hit group (only used for triangles). */ + PG_HITV, /* __VOLUME__ hit group. */ PG_HITD_MOTION, PG_HITS_MOTION, PG_CALL_SVM_AO, @@ -51,7 +52,7 @@ enum { static const int MISS_PROGRAM_GROUP_OFFSET = PG_MISS; static const int NUM_MIS_PROGRAM_GROUPS = 1; static const int HIT_PROGAM_GROUP_OFFSET = PG_HITD; -static const int NUM_HIT_PROGRAM_GROUPS = 5; +static const int NUM_HIT_PROGRAM_GROUPS = 6; static const int CALLABLE_PROGRAM_GROUPS_BASE = PG_CALL_SVM_AO; static const int NUM_CALLABLE_PROGRAM_GROUPS = 3; diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index 539e9fd05fb..0b44cc5db34 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -167,15 +167,25 @@ ccl_device_intersect bool scene_intersect(const KernelGlobals *kg, uint p4 = visibility; uint p5 = PRIMITIVE_NONE; + uint ray_mask = visibility & 0xFF; + uint ray_flags = OPTIX_RAY_FLAG_NONE; + if (0 == ray_mask && (visibility & ~0xFF) != 0) { + ray_mask = 0xFF; + ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT; + } + else if (visibility & PATH_RAY_SHADOW_OPAQUE) { + ray_flags = OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT; + } + optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, ray->P, ray->D, 0.0f, ray->t, ray->time, - 0xF, - OPTIX_RAY_FLAG_NONE, - 0, // SBT offset for PG_HITD + ray_mask, + ray_flags, + 0, /* SBT offset for PG_HITD */ 0, 0, p0, @@ -251,11 +261,11 @@ ccl_device_intersect bool scene_intersect_local(const KernelGlobals *kg, uint p2 = ((uint64_t)local_isect) & 0xFFFFFFFF; uint p3 = (((uint64_t)local_isect) >> 32) & 0xFFFFFFFF; uint p4 = local_object; - // Is set to zero on miss or if ray is aborted, so can be used as return value + /* Is set to zero on miss or if ray is aborted, so can be used as return value. */ uint p5 = max_hits; if (local_isect) { - local_isect->num_hits = 0; // Initialize hit count to zero + local_isect->num_hits = 0; /* Initialize hit count to zero. */ } optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, ray->P, @@ -263,11 +273,10 @@ ccl_device_intersect bool scene_intersect_local(const KernelGlobals *kg, 0.0f, ray->t, ray->time, - // Skip curves - 0x3, - // Need to always call into __anyhit__kernel_optix_local_hit + 0xFF, + /* Need to always call into __anyhit__kernel_optix_local_hit. */ OPTIX_RAY_FLAG_ENFORCE_ANYHIT, - 2, // SBT offset for PG_HITL + 2, /* SBT offset for PG_HITL */ 0, 0, p0, @@ -365,17 +374,22 @@ ccl_device_intersect bool scene_intersect_shadow_all(const KernelGlobals *kg, uint p4 = visibility; uint p5 = false; - *num_hits = 0; // Initialize hit count to zero + uint ray_mask = visibility & 0xFF; + if (0 == ray_mask && (visibility & ~0xFF) != 0) { + ray_mask = 0xFF; + } + + *num_hits = 0; /* Initialize hit count to zero. */ optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, ray->P, ray->D, 0.0f, ray->t, ray->time, - 0xF, - // Need to always call into __anyhit__kernel_optix_shadow_all_hit + ray_mask, + /* Need to always call into __anyhit__kernel_optix_shadow_all_hit. */ OPTIX_RAY_FLAG_ENFORCE_ANYHIT, - 1, // SBT offset for PG_HITS + 1, /* SBT offset for PG_HITS */ 0, 0, p0, @@ -444,16 +458,21 @@ ccl_device_intersect bool scene_intersect_volume(const KernelGlobals *kg, uint p4 = visibility; uint p5 = PRIMITIVE_NONE; + uint ray_mask = visibility & 0xFF; + if (0 == ray_mask && (visibility & ~0xFF) != 0) { + ray_mask = 0xFF; + } + optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, ray->P, ray->D, 0.0f, ray->t, ray->time, - // Skip everything but volumes - 0x2, - OPTIX_RAY_FLAG_NONE, - 0, // SBT offset for PG_HITD + ray_mask, + /* Need to always call into __anyhit__kernel_optix_volume_test. */ + OPTIX_RAY_FLAG_ENFORCE_ANYHIT, + 3, /* SBT offset for PG_HITV */ 0, 0, p0, diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu index c1e36febfc0..7a79e0c4823 100644 --- a/intern/cycles/kernel/device/optix/kernel.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -19,7 +19,7 @@ #include "kernel/device/optix/compat.h" #include "kernel/device/optix/globals.h" -#include "kernel/device/gpu/image.h" // Texture lookup uses normal CUDA intrinsics +#include "kernel/device/gpu/image.h" /* Texture lookup uses normal CUDA intrinsics. */ #include "kernel/integrator/integrator_state.h" #include "kernel/integrator/integrator_state_flow.h" @@ -44,18 +44,18 @@ template ccl_device_forceinline T *get_payload_ptr_2() template ccl_device_forceinline uint get_object_id() { #ifdef __OBJECT_MOTION__ - // Always get the the instance ID from the TLAS - // There might be a motion transform node between TLAS and BLAS which does not have one + /* Always get the the instance ID from the TLAS. + * There might be a motion transform node between TLAS and BLAS which does not have one. */ uint object = optixGetInstanceIdFromHandle(optixGetTransformListHandle(0)); #else uint object = optixGetInstanceId(); #endif - // Choose between always returning object ID or only for instances + /* Choose between always returning object ID or only for instances. */ if (always || (object & 1) == 0) - // Can just remove the low bit since instance always contains object ID + /* Can just remove the low bit since instance always contains object ID. */ return object >> 1; else - // Set to OBJECT_NONE if this is not an instanced object + /* Set to OBJECT_NONE if this is not an instanced object. */ return OBJECT_NONE; } @@ -93,23 +93,30 @@ extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_volume_st extern "C" __global__ void __miss__kernel_optix_miss() { - // 'kernel_path_lamp_emission' checks intersection distance, so need to set it even on a miss + /* 'kernel_path_lamp_emission' checks intersection distance, so need to set it even on a miss. */ optixSetPayload_0(__float_as_uint(optixGetRayTmax())); optixSetPayload_5(PRIMITIVE_NONE); } extern "C" __global__ void __anyhit__kernel_optix_local_hit() { +#ifdef __HAIR__ + if (!optixIsTriangleHit()) { + /* Ignore curves. */ + return optixIgnoreIntersection(); + } +#endif + #ifdef __BVH_LOCAL__ const uint object = get_object_id(); if (object != optixGetPayload_4() /* local_object */) { - // Only intersect with matching object + /* Only intersect with matching object. */ return optixIgnoreIntersection(); } const uint max_hits = optixGetPayload_5(); if (max_hits == 0) { - // Special case for when no hit information is requested, just report that something was hit + /* Special case for when no hit information is requested, just report that something was hit */ optixSetPayload_5(true); return optixTerminateRay(); } @@ -136,8 +143,9 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit() } else { if (local_isect->num_hits && optixGetRayTmax() > local_isect->hits[0].t) { - // Record closest intersection only - // Do not terminate ray here, since there is no guarantee about distance ordering in any-hit + /* Record closest intersection only. + * Do not terminate ray here, since there is no guarantee about distance ordering in any-hit. + */ return optixIgnoreIntersection(); } @@ -154,14 +162,14 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit() isect->u = 1.0f - barycentrics.y - barycentrics.x; isect->v = barycentrics.x; - // Record geometric normal + /* Record geometric normal. */ const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect->prim); const float3 tri_a = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0)); const float3 tri_b = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1)); const float3 tri_c = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2)); local_isect->Ng[hit] = normalize(cross(tri_b - tri_a, tri_c - tri_a)); - // Continue tracing (without this the trace call would return after the first hit) + /* Continue tracing (without this the trace call would return after the first hit). */ optixIgnoreIntersection(); #endif } @@ -190,7 +198,7 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() u = __uint_as_float(optixGetAttribute_0()); v = __uint_as_float(optixGetAttribute_1()); - // Filter out curve endcaps + /* Filter out curve endcaps. */ if (u == 0.0f || u == 1.0f) { ignore_intersection = true; } @@ -241,10 +249,10 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() isect->type = kernel_tex_fetch(__prim_type, prim); # ifdef __TRANSPARENT_SHADOWS__ - // Detect if this surface has a shader with transparent shadows + /* Detect if this surface has a shader with transparent shadows. */ if (!shader_transparent_shadow(NULL, isect) || max_hits == 0) { # endif - // If no transparent shadows, all light is blocked and we can stop immediately + /* If no transparent shadows, all light is blocked and we can stop immediately. */ optixSetPayload_5(true); return optixTerminateRay(); # ifdef __TRANSPARENT_SHADOWS__ @@ -252,24 +260,39 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() # endif } - // Continue tracing + /* Continue tracing. */ optixIgnoreIntersection(); #endif } -extern "C" __global__ void __anyhit__kernel_optix_visibility_test() +extern "C" __global__ void __anyhit__kernel_optix_volume_test() { - uint visibility = optixGetPayload_4(); +#ifdef __HAIR__ + if (!optixIsTriangleHit()) { + /* Ignore curves. */ + return optixIgnoreIntersection(); + } +#endif + #ifdef __VISIBILITY_FLAG__ const uint prim = optixGetPrimitiveIndex(); + const uint visibility = optixGetPayload_4(); if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) { return optixIgnoreIntersection(); } #endif + const uint object = get_object_id(); + if ((kernel_tex_fetch(__object_flag, object) & SD_OBJECT_HAS_VOLUME) == 0) { + return optixIgnoreIntersection(); + } +} + +extern "C" __global__ void __anyhit__kernel_optix_visibility_test() +{ #ifdef __HAIR__ if (!optixIsTriangleHit()) { - // Filter out curve endcaps + /* Filter out curve endcaps. */ const float u = __uint_as_float(optixGetAttribute_0()); if (u == 0.0f || u == 1.0f) { return optixIgnoreIntersection(); @@ -277,18 +300,26 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test() } #endif - // Shadow ray early termination +#ifdef __VISIBILITY_FLAG__ + const uint prim = optixGetPrimitiveIndex(); + const uint visibility = optixGetPayload_4(); + if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) { + return optixIgnoreIntersection(); + } + + /* Shadow ray early termination. */ if (visibility & PATH_RAY_SHADOW_OPAQUE) { return optixTerminateRay(); } +#endif } extern "C" __global__ void __closesthit__kernel_optix_hit() { - optixSetPayload_0(__float_as_uint(optixGetRayTmax())); // Intersection distance + optixSetPayload_0(__float_as_uint(optixGetRayTmax())); /* Intersection distance */ optixSetPayload_3(optixGetPrimitiveIndex()); optixSetPayload_4(get_object_id()); - // Can be PRIMITIVE_TRIANGLE and PRIMITIVE_MOTION_TRIANGLE or curve type and segment index + /* Can be PRIMITIVE_TRIANGLE and PRIMITIVE_MOTION_TRIANGLE or curve type and segment index. */ optixSetPayload_5(kernel_tex_fetch(__prim_type, optixGetPrimitiveIndex())); if (optixIsTriangleHit()) { @@ -297,7 +328,7 @@ extern "C" __global__ void __closesthit__kernel_optix_hit() optixSetPayload_2(__float_as_uint(barycentrics.x)); } else { - optixSetPayload_1(optixGetAttribute_0()); // Same as 'optixGetCurveParameter()' + optixSetPayload_1(optixGetAttribute_0()); /* Same as 'optixGetCurveParameter()' */ optixSetPayload_2(optixGetAttribute_1()); } } @@ -311,7 +342,7 @@ ccl_device_inline void optix_intersection_curve(const uint prim, const uint type float3 P = optixGetObjectRayOrigin(); float3 dir = optixGetObjectRayDirection(); - // The direction is not normalized by default, but the curve intersection routine expects that + /* The direction is not normalized by default, but the curve intersection routine expects that */ float len; dir = normalize_len(dir, &len); @@ -323,15 +354,15 @@ ccl_device_inline void optix_intersection_curve(const uint prim, const uint type Intersection isect; isect.t = optixGetRayTmax(); - // Transform maximum distance into object space + /* Transform maximum distance into object space. */ if (isect.t != FLT_MAX) isect.t *= len; if (curve_intersect(NULL, &isect, P, dir, isect.t, visibility, object, prim, time, type)) { optixReportIntersection(isect.t / len, type & PRIMITIVE_ALL, - __float_as_int(isect.u), // Attribute_0 - __float_as_int(isect.v)); // Attribute_1 + __float_as_int(isect.u), /* Attribute_0 */ + __float_as_int(isect.v)); /* Attribute_1 */ } } From 0559971ab3772e3b5efb0fcad396735ea4ac22fe Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 27 Sep 2021 17:35:26 +0200 Subject: [PATCH 0256/1500] Geometry Nodes: add utility to process all instances separately This adds a new `GeometrySet::modify_geometry_sets` method that can be used to update each sub-geometry-set separately without making any instances real. Differential Revision: https://developer.blender.org/D12650 --- source/blender/blenkernel/BKE_geometry_set.hh | 25 ++++++++- .../intern/geometry_component_instances.cc | 34 +----------- .../blender/blenkernel/intern/geometry_set.cc | 22 ++++++++ .../intern/geometry_set_instances.cc | 55 +++++++++++++++++++ .../geometry/nodes/node_geo_curve_fill.cc | 16 +----- 5 files changed, 104 insertions(+), 48 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 571c6c6a0a0..08bfdbf2382 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -309,6 +309,10 @@ struct GeometrySet { bool include_instances, blender::Map &r_attributes) const; + using ForeachSubGeometryCallback = blender::FunctionRef; + + void modify_geometry_sets(ForeachSubGeometryCallback callback); + /* Utility methods for creation. */ static GeometrySet create_with_mesh( Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); @@ -479,7 +483,7 @@ class InstanceReference { Type type_ = Type::None; /** Depending on the type this is either null, an Object or Collection pointer. */ void *data_ = nullptr; - std::shared_ptr geometry_set_; + std::unique_ptr geometry_set_; public: InstanceReference() = default; @@ -494,10 +498,27 @@ class InstanceReference { InstanceReference(GeometrySet geometry_set) : type_(Type::GeometrySet), - geometry_set_(std::make_shared(std::move(geometry_set))) + geometry_set_(std::make_unique(std::move(geometry_set))) { } + InstanceReference(const InstanceReference &other) : type_(other.type_), data_(other.data_) + { + if (other.geometry_set_) { + geometry_set_ = std::make_unique(*other.geometry_set_); + } + } + + InstanceReference &operator=(const InstanceReference &other) + { + if (this == &other) { + return *this; + } + this->~InstanceReference(); + new (this) InstanceReference(other); + return *this; + } + Type type() const { return type_; diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index f1f60266545..4204d62e1a7 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -124,37 +124,6 @@ blender::Span InstancesComponent::instance_ids() const return instance_ids_; } -/** - * If references have a collection or object type, convert them into geometry instances. This - * will join geometry components from nested instances if necessary. After that, the geometry - * sets can be edited. - */ -void InstancesComponent::ensure_geometry_instances() -{ - VectorSet new_references; - new_references.reserve(references_.size()); - for (const InstanceReference &reference : references_) { - if (reference.type() == InstanceReference::Type::Object) { - GeometrySet geometry_set; - InstancesComponent &instances = geometry_set.get_component_for_write(); - const int handle = instances.add_reference(reference.object()); - instances.add_instance(handle, float4x4::identity()); - new_references.add_new(geometry_set); - } - else if (reference.type() == InstanceReference::Type::Collection) { - GeometrySet geometry_set; - InstancesComponent &instances = geometry_set.get_component_for_write(); - const int handle = instances.add_reference(reference.collection()); - instances.add_instance(handle, float4x4::identity()); - new_references.add_new(geometry_set); - } - else { - new_references.add_new(reference); - } - } - references_ = std::move(new_references); -} - /** * With write access to the instances component, the data in the instanced geometry sets can be * changed. This is a function on the component rather than each reference to ensure `const` @@ -162,7 +131,8 @@ void InstancesComponent::ensure_geometry_instances() */ GeometrySet &InstancesComponent::geometry_set_from_reference(const int reference_index) { - /* If this assert fails, it means #ensure_geometry_instances must be called first. */ + /* If this assert fails, it means #ensure_geometry_instances must be called first or that the + * reference can't be converted to a geometry set. */ BLI_assert(references_[reference_index].type() == InstanceReference::Type::GeometrySet); /* The const cast is okay because the instance's hash in the set diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 1ebdde75f46..3abe2fd2213 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -458,6 +458,28 @@ void GeometrySet::gather_attributes_for_propagation( delete dummy_component; } +/** + * Modify every (recursive) instance separately. This is often more efficient than realizing all + * instances just to change the same thing on all of them. + */ +void GeometrySet::modify_geometry_sets(ForeachSubGeometryCallback callback) +{ + callback(*this); + if (!this->has_instances()) { + return; + } + /* In the future this can be improved by deduplicating instance references across different + * instances. */ + InstancesComponent &instances_component = this->get_component_for_write(); + instances_component.ensure_geometry_instances(); + for (const int handle : instances_component.references().index_range()) { + if (instances_component.references()[handle].type() == InstanceReference::Type::GeometrySet) { + GeometrySet &instance_geometry = instances_component.geometry_set_from_reference(handle); + instance_geometry.modify_geometry_sets(callback); + } + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 162f91a4a47..ad13342ad9e 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -652,3 +652,58 @@ void InstancesComponent::foreach_referenced_geometry( } } } + +/** + * If references have a collection or object type, convert them into geometry instances + * recursively. After that, the geometry sets can be edited. There may still be instances of other + * types of they can't be converted to geometry sets. + */ +void InstancesComponent::ensure_geometry_instances() +{ + using namespace blender; + using namespace blender::bke; + VectorSet new_references; + new_references.reserve(references_.size()); + for (const InstanceReference &reference : references_) { + switch (reference.type()) { + case InstanceReference::Type::None: + case InstanceReference::Type::GeometrySet: { + /* Those references can stay as their were. */ + new_references.add_new(reference); + break; + } + case InstanceReference::Type::Object: { + /* Create a new reference that contains the geometry set of the object. We may want to + * treat e.g. lamps and similar object types separately here. */ + const Object &object = reference.object(); + GeometrySet object_geometry_set = object_get_geometry_set_for_read(object); + if (object_geometry_set.has_instances()) { + InstancesComponent &component = + object_geometry_set.get_component_for_write(); + component.ensure_geometry_instances(); + } + new_references.add_new(std::move(object_geometry_set)); + break; + } + case InstanceReference::Type::Collection: { + /* Create a new reference that contains a geometry set that contains all objects from the + * collection as instances. */ + GeometrySet collection_geometry_set; + InstancesComponent &component = + collection_geometry_set.get_component_for_write(); + Collection &collection = reference.collection(); + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) { + const int handle = component.add_reference(*object); + component.add_instance(handle, object->obmat); + float4x4 &transform = component.instance_transforms().last(); + sub_v3_v3(transform.values[3], collection.instance_offset); + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + component.ensure_geometry_instances(); + new_references.add_new(std::move(collection_geometry_set)); + break; + } + } + } + references_ = std::move(new_references); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index fcafdf93197..c30741cf786 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -154,20 +154,8 @@ static void geo_node_curve_fill_exec(GeoNodeExecParams params) const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage; const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode; - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write(); - instances.ensure_geometry_instances(); - - threading::parallel_for(IndexRange(instances.references_amount()), 16, [&](IndexRange range) { - for (int i : range) { - GeometrySet &geometry_set = instances.geometry_set_from_reference(i); - geometry_set = bke::geometry_set_realize_instances(geometry_set); - curve_fill_calculate(geometry_set, mode); - } - }); - } - - curve_fill_calculate(geometry_set, mode); + geometry_set.modify_geometry_sets( + [&](GeometrySet &geometry_set) { curve_fill_calculate(geometry_set, mode); }); params.set_output("Mesh", std::move(geometry_set)); } From 5bea5e25d52fe26b53928781ec0b1f5d4ddf5ad0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 27 Sep 2021 17:38:53 +0200 Subject: [PATCH 0257/1500] Fix T91728: Cycles render artifacts with motion blur and object attributes --- intern/cycles/kernel/geom/geom_motion_triangle.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/geom_motion_triangle.h index eb4a39e062b..239bd0a37b2 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle.h @@ -41,7 +41,18 @@ ccl_device_inline int find_attribute_motion(const KernelGlobals *kg, uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset); while (attr_map.x != id) { - attr_offset += ATTR_PRIM_TYPES; + if (UNLIKELY(attr_map.x == ATTR_STD_NONE)) { + if (UNLIKELY(attr_map.y == 0)) { + return (int)ATTR_STD_NOT_FOUND; + } + else { + /* Chain jump to a different part of the table. */ + attr_offset = attr_map.z; + } + } + else { + attr_offset += ATTR_PRIM_TYPES; + } attr_map = kernel_tex_fetch(__attributes_map, attr_offset); } From 824733ea47d13030c18faa618c1dc31a08dfa43d Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 27 Sep 2021 17:45:02 +0200 Subject: [PATCH 0258/1500] Assets: Additions/fixes to the catalog system in preparation for the UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixes missing update of the catalog tree when adding catalogs. * Adds iterators for the catalogs, needed for UI code. * Store catalog ID in the catalog tree items, needed for UI code. * Other smaller API additions for the UI. * Improve comments and smaller cleanups. New functions are covered with unit tests. Differential Revision: https://developer.blender.org/D12618 Reviewed by: Sybren Stüvel --- .../blender/blenkernel/BKE_asset_catalog.hh | 46 +++- .../blenkernel/intern/asset_catalog.cc | 147 ++++++++++--- .../blenkernel/intern/asset_catalog_test.cc | 200 +++++++++++++++++- 3 files changed, 343 insertions(+), 50 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index cf9d581b148..07373caf701 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -86,6 +86,10 @@ class AssetCatalogService { /** Return catalog with the given ID. Return nullptr if not found. */ AssetCatalog *find_catalog(CatalogID catalog_id); + /** Return first catalog with the given path. Return nullptr if not found. This is not an + * efficient call as it's just a linear search over the catalogs. */ + AssetCatalog *find_catalog_by_path(const CatalogPath &path) const; + /** Create a catalog with some sensible auto-generated catalog ID. * The catalog will be saved to the default catalog file.*/ AssetCatalog *create_catalog(const CatalogPath &catalog_path); @@ -124,48 +128,74 @@ class AssetCatalogService { void rebuild_tree(); }; +/** + * Representation of a catalog path in the #AssetCatalogTree. + */ class AssetCatalogTreeItem { - friend class AssetCatalogService; + friend class AssetCatalogTree; public: + /** Container for child items. Uses a #std::map to keep items ordered by their name (i.e. their + * last catalog component). */ using ChildMap = std::map; - using ItemIterFn = FunctionRef; + using ItemIterFn = FunctionRef; - AssetCatalogTreeItem(StringRef name, const AssetCatalogTreeItem *parent = nullptr); + AssetCatalogTreeItem(StringRef name, + CatalogID catalog_id, + const AssetCatalogTreeItem *parent = nullptr); + CatalogID get_catalog_id() const; StringRef get_name() const; /** Return the full catalog path, defined as the name of this catalog prefixed by the full * catalog path of its parent and a separator. */ CatalogPath catalog_path() const; int count_parents() const; + bool has_children() const; - static void foreach_item_recursive(const ChildMap &children_, const ItemIterFn callback); + /** Iterate over children calling \a callback for each of them, but do not recurse into their + * children. */ + void foreach_child(const ItemIterFn callback); protected: /** Child tree items, ordered by their names. */ ChildMap children_; /** The user visible name of this component. */ CatalogPathComponent name_; + CatalogID catalog_id_; /** Pointer back to the parent item. Used to reconstruct the hierarchy from an item (e.g. to * build a path). */ const AssetCatalogTreeItem *parent_ = nullptr; + + private: + static void foreach_item_recursive(ChildMap &children_, ItemIterFn callback); }; /** * A representation of the catalog paths as tree structure. Each component of the catalog tree is - * represented by a #AssetCatalogTreeItem. + * represented by an #AssetCatalogTreeItem. The last path component of an item is used as its name, + * which may also be shown to the user. + * An item can not have multiple children with the same name. That means the name uniquely + * identifies an item within its parent. + * * There is no single root tree element, the #AssetCatalogTree instance itself represents the root. */ class AssetCatalogTree { - friend class AssetCatalogService; + using ChildMap = AssetCatalogTreeItem::ChildMap; + using ItemIterFn = AssetCatalogTreeItem::ItemIterFn; public: - void foreach_item(const AssetCatalogTreeItem::ItemIterFn callback) const; + /** Ensure an item representing \a path is in the tree, adding it if necessary. */ + void insert_item(const AssetCatalog &catalog); + + void foreach_item(const AssetCatalogTreeItem::ItemIterFn callback); + /** Iterate over root items calling \a callback for each of them, but do not recurse into their + * children. */ + void foreach_root_item(const ItemIterFn callback); protected: /** Child tree items, ordered by their names. */ - AssetCatalogTreeItem::ChildMap children_; + ChildMap root_items_; }; /** Keeps track of which catalogs are defined in a certain file on disk. diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 4f1de09e148..b65ae12e5a7 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -64,6 +64,17 @@ AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) return catalog_uptr_ptr->get(); } +AssetCatalog *AssetCatalogService::find_catalog_by_path(const CatalogPath &path) const +{ + for (auto &catalog : catalogs_.values()) { + if (catalog->path == path) { + return catalog.get(); + } + } + + return nullptr; +} + void AssetCatalogService::delete_catalog(CatalogID catalog_id) { std::unique_ptr *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id); @@ -104,6 +115,12 @@ AssetCatalog *AssetCatalogService::create_catalog(const CatalogPath &catalog_pat catalog_definition_file_->add_new(catalog_ptr); } + /* The tree may not exist; this happens when no catalog definition file has been loaded yet. When + * the tree is created any in-memory catalogs will be added, so it doesn't need to happen now. */ + if (catalog_tree_) { + catalog_tree_->insert_item(*catalog_ptr); + } + return catalog_ptr; } @@ -268,34 +285,7 @@ std::unique_ptr AssetCatalogService::read_into_tree() /* Go through the catalogs, insert each path component into the tree where needed. */ for (auto &catalog : catalogs_.values()) { - const AssetCatalogTreeItem *parent = nullptr; - AssetCatalogTreeItem::ChildMap *insert_to_map = &tree->children_; - - BLI_assert_msg(!ELEM(catalog->path[0], '/', '\\'), - "Malformed catalog path; should not start with a separator"); - - const char *next_slash_ptr; - /* Looks more complicated than it is, this just iterates over path components. E.g. - * "just/some/path" iterates over "just", then "some" then "path". */ - for (const char *name_begin = catalog->path.data(); name_begin && name_begin[0]; - /* Jump to one after the next slash if there is any. */ - name_begin = next_slash_ptr ? next_slash_ptr + 1 : nullptr) { - next_slash_ptr = BLI_path_slash_find(name_begin); - - /* Note that this won't be null terminated. */ - StringRef component_name = next_slash_ptr ? - StringRef(name_begin, next_slash_ptr - name_begin) : - /* Last component in the path. */ - name_begin; - - /* Insert new tree element - if no matching one is there yet! */ - auto [item, was_inserted] = insert_to_map->emplace( - component_name, AssetCatalogTreeItem(component_name, parent)); - - /* Walk further into the path (no matter if a new item was created or not). */ - parent = &item->second; - insert_to_map = &item->second.children_; - } + tree->insert_item(*catalog); } return tree; @@ -306,11 +296,20 @@ void AssetCatalogService::rebuild_tree() this->catalog_tree_ = read_into_tree(); } -AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name, const AssetCatalogTreeItem *parent) - : name_(name), parent_(parent) +/* ---------------------------------------------------------------------- */ + +AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name, + CatalogID catalog_id, + const AssetCatalogTreeItem *parent) + : name_(name), catalog_id_(catalog_id), parent_(parent) { } +CatalogID AssetCatalogTreeItem::get_catalog_id() const +{ + return catalog_id_; +} + StringRef AssetCatalogTreeItem::get_name() const { return name_; @@ -334,20 +333,100 @@ int AssetCatalogTreeItem::count_parents() const return i; } -void AssetCatalogTree::foreach_item(const AssetCatalogTreeItem::ItemIterFn callback) const +bool AssetCatalogTreeItem::has_children() const { - AssetCatalogTreeItem::foreach_item_recursive(children_, callback); + return !children_.empty(); } -void AssetCatalogTreeItem::foreach_item_recursive(const AssetCatalogTreeItem::ChildMap &children, +/* ---------------------------------------------------------------------- */ + +/** + * Iterate over path components, calling \a callback for each component. E.g. "just/some/path" + * iterates over "just", then "some" then "path". + */ +static void iterate_over_catalog_path_components( + const CatalogPath &path, + FunctionRef callback) +{ + const char *next_slash_ptr; + + for (const char *path_component = path.data(); path_component && path_component[0]; + /* Jump to one after the next slash if there is any. */ + path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) { + next_slash_ptr = BLI_path_slash_find(path_component); + + const bool is_last_component = next_slash_ptr == nullptr; + /* Note that this won't be null terminated. */ + const StringRef component_name = is_last_component ? + path_component : + StringRef(path_component, + next_slash_ptr - path_component); + + callback(component_name, is_last_component); + } +} + +void AssetCatalogTree::insert_item(const AssetCatalog &catalog) +{ + const AssetCatalogTreeItem *parent = nullptr; + /* The children for the currently iterated component, where the following component should be + * added to (if not there yet). */ + AssetCatalogTreeItem::ChildMap *current_item_children = &root_items_; + + BLI_assert_msg(!ELEM(catalog.path[0], '/', '\\'), + "Malformed catalog path; should not start with a separator"); + + const CatalogID nil_id{}; + + iterate_over_catalog_path_components( + catalog.path, [&](StringRef component_name, const bool is_last_component) { + /* Insert new tree element - if no matching one is there yet! */ + auto [key_and_item, was_inserted] = current_item_children->emplace( + component_name, + AssetCatalogTreeItem( + component_name, is_last_component ? catalog.catalog_id : nil_id, parent)); + AssetCatalogTreeItem &item = key_and_item->second; + + /* If full path of this catalog already exists as parent path of a previously read catalog, + * we can ensure this tree item's UUID is set here. */ + if (is_last_component && BLI_uuid_is_nil(item.catalog_id_)) { + item.catalog_id_ = catalog.catalog_id; + } + + /* Walk further into the path (no matter if a new item was created or not). */ + parent = &item; + current_item_children = &item.children_; + }); +} + +void AssetCatalogTree::foreach_item(AssetCatalogTreeItem::ItemIterFn callback) +{ + AssetCatalogTreeItem::foreach_item_recursive(root_items_, callback); +} + +void AssetCatalogTreeItem::foreach_item_recursive(AssetCatalogTreeItem::ChildMap &children, const ItemIterFn callback) { - for (const auto &[key, item] : children) { + for (auto &[key, item] : children) { callback(item); foreach_item_recursive(item.children_, callback); } } +void AssetCatalogTree::foreach_root_item(const ItemIterFn callback) +{ + for (auto &[key, item] : root_items_) { + callback(item); + } +} + +void AssetCatalogTreeItem::foreach_child(const ItemIterFn callback) +{ + for (auto &[key, item] : children_) { + callback(item); + } +} + AssetCatalogTree *AssetCatalogService::get_catalog_tree() { return catalog_tree_.get(); diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index b40aae5d64a..87ee61b015f 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -92,6 +92,22 @@ class AssetCatalogTest : public testing::Test { int parent_count; }; + void assert_expected_item(const CatalogPathInfo &expected_path, + const AssetCatalogTreeItem &actual_item) + { + char expected_filename[FILE_MAXFILE]; + /* Is the catalog name as expected? "character", "Ellie", ... */ + BLI_split_file_part(expected_path.name.data(), expected_filename, sizeof(expected_filename)); + EXPECT_EQ(expected_filename, actual_item.get_name()); + /* Does the computed number of parents match? */ + EXPECT_EQ(expected_path.parent_count, actual_item.count_parents()); + EXPECT_EQ(expected_path.name, actual_item.catalog_path()); + } + + /** + * Recursively iterate over all tree items using #AssetCatalogTree::foreach_item() and check if + * the items map exactly to \a expected_paths. + */ void assert_expected_tree_items(AssetCatalogTree *tree, const std::vector &expected_paths) { @@ -99,16 +115,43 @@ class AssetCatalogTest : public testing::Test { tree->foreach_item([&](const AssetCatalogTreeItem &actual_item) { ASSERT_LT(i, expected_paths.size()) << "More catalogs in tree than expected; did not expect " << actual_item.catalog_path(); + assert_expected_item(expected_paths[i], actual_item); + i++; + }); + } - char expected_filename[FILE_MAXFILE]; - /* Is the catalog name as expected? "character", "Ellie", ... */ - BLI_split_file_part( - expected_paths[i].name.data(), expected_filename, sizeof(expected_filename)); - EXPECT_EQ(expected_filename, actual_item.get_name()); - /* Does the computed number of parents match? */ - EXPECT_EQ(expected_paths[i].parent_count, actual_item.count_parents()); - EXPECT_EQ(expected_paths[i].name, actual_item.catalog_path()); + /** + * Iterate over the root items of \a tree and check if the items map exactly to \a + * expected_paths. Similar to #assert_expected_tree_items() but calls + * #AssetCatalogTree::foreach_root_item() instead of #AssetCatalogTree::foreach_item(). + */ + void assert_expected_tree_root_items(AssetCatalogTree *tree, + const std::vector &expected_paths) + { + int i = 0; + tree->foreach_root_item([&](const AssetCatalogTreeItem &actual_item) { + ASSERT_LT(i, expected_paths.size()) + << "More catalogs in tree root than expected; did not expect " + << actual_item.catalog_path(); + assert_expected_item(expected_paths[i], actual_item); + i++; + }); + } + /** + * Iterate over the child items of \a parent_item and check if the items map exactly to \a + * expected_paths. Similar to #assert_expected_tree_items() but calls + * #AssetCatalogTreeItem::foreach_child() instead of #AssetCatalogTree::foreach_item(). + */ + void assert_expected_tree_item_child_items(AssetCatalogTreeItem *parent_item, + const std::vector &expected_paths) + { + int i = 0; + parent_item->foreach_child([&](const AssetCatalogTreeItem &actual_item) { + ASSERT_LT(i, expected_paths.size()) + << "More catalogs in tree item than expected; did not expect " + << actual_item.catalog_path(); + assert_expected_item(expected_paths[i], actual_item); i++; }); } @@ -156,6 +199,87 @@ TEST_F(AssetCatalogTest, load_single_file) EXPECT_EQ("POSES_RUŽENA", poses_ruzena->simple_name); } +TEST_F(AssetCatalogTest, insert_item_into_tree) +{ + { + AssetCatalogTree tree; + std::unique_ptr catalog_empty_path = AssetCatalog::from_path(""); + tree.insert_item(*catalog_empty_path); + + assert_expected_tree_items(&tree, {}); + } + + { + AssetCatalogTree tree; + + std::unique_ptr catalog = AssetCatalog::from_path("item"); + tree.insert_item(*catalog); + assert_expected_tree_items(&tree, {{"item", 0}}); + + /* Insert child after parent already exists. */ + std::unique_ptr child_catalog = AssetCatalog::from_path("item/child"); + tree.insert_item(*catalog); + assert_expected_tree_items(&tree, {{"item", 0}, {"item/child", 1}}); + + std::vector expected_paths; + + /* Test inserting multi-component sub-path. */ + std::unique_ptr grandgrandchild_catalog = AssetCatalog::from_path( + "item/child/grandchild/grandgrandchild"); + tree.insert_item(*catalog); + expected_paths = {{"item", 0}, + {"item/child", 1}, + {"item/child/grandchild", 2}, + {"item/child/grandchild/grandgrandchild", 3}}; + assert_expected_tree_items(&tree, expected_paths); + + std::unique_ptr root_level_catalog = AssetCatalog::from_path("root level"); + tree.insert_item(*catalog); + expected_paths = {{"item", 0}, + {"item/child", 1}, + {"item/child/grandchild", 2}, + {"item/child/grandchild/grandgrandchild", 3}, + {"root level", 0}}; + assert_expected_tree_items(&tree, expected_paths); + } + + { + AssetCatalogTree tree; + + std::unique_ptr catalog = AssetCatalog::from_path("item/child"); + tree.insert_item(*catalog); + assert_expected_tree_items(&tree, {{"item", 0}, {"item/child", 1}}); + } + + { + AssetCatalogTree tree; + + std::unique_ptr catalog = AssetCatalog::from_path("white space"); + tree.insert_item(*catalog); + assert_expected_tree_items(&tree, {{"white space", 0}}); + } + + { + AssetCatalogTree tree; + + std::unique_ptr catalog = AssetCatalog::from_path("/item/white space"); + tree.insert_item(*catalog); + assert_expected_tree_items(&tree, {{"item", 0}, {"item/white space", 1}}); + } + + { + AssetCatalogTree tree; + + std::unique_ptr catalog_unicode_path = AssetCatalog::from_path("Ružena"); + tree.insert_item(*catalog_unicode_path); + assert_expected_tree_items(&tree, {{"Ružena", 0}}); + + catalog_unicode_path = AssetCatalog::from_path("Ružena/Ružena"); + tree.insert_item(*catalog_unicode_path); + assert_expected_tree_items(&tree, {{"Ružena", 0}, {"Ružena/Ružena", 1}}); + } +} + TEST_F(AssetCatalogTest, load_single_file_into_tree) { AssetCatalogService service(asset_library_root_); @@ -182,6 +306,66 @@ TEST_F(AssetCatalogTest, load_single_file_into_tree) assert_expected_tree_items(tree, expected_paths); } +TEST_F(AssetCatalogTest, foreach_in_tree) +{ + { + AssetCatalogTree tree{}; + const std::vector no_catalogs{}; + + assert_expected_tree_items(&tree, no_catalogs); + assert_expected_tree_root_items(&tree, no_catalogs); + /* Need a root item to check child items. */ + std::unique_ptr catalog = AssetCatalog::from_path("something"); + tree.insert_item(*catalog); + tree.foreach_root_item([&no_catalogs, this](AssetCatalogTreeItem &item) { + assert_expected_tree_item_child_items(&item, no_catalogs); + }); + } + + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + std::vector expected_root_items{{"character", 0}, {"path", 0}}; + AssetCatalogTree *tree = service.get_catalog_tree(); + assert_expected_tree_root_items(tree, expected_root_items); + + /* Test if the direct children of the root item are what's expected. */ + std::vector> expected_root_child_items = { + /* Children of the "character" root item. */ + {{"character/Ellie", 1}, {"character/Ružena", 1}}, + /* Children of the "path" root item. */ + {{"path/without", 1}}, + }; + int i = 0; + tree->foreach_root_item([&expected_root_child_items, &i, this](AssetCatalogTreeItem &item) { + assert_expected_tree_item_child_items(&item, expected_root_child_items[i]); + i++; + }); +} + +TEST_F(AssetCatalogTest, find_catalog_by_path) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + AssetCatalog *catalog; + + EXPECT_EQ(nullptr, service.find_catalog_by_path("")); + catalog = service.find_catalog_by_path("character/Ellie/poselib/white space"); + EXPECT_NE(nullptr, catalog); + EXPECT_EQ(UUID_POSES_ELLIE_WHITESPACE, catalog->catalog_id); + catalog = service.find_catalog_by_path("character/Ružena/poselib"); + EXPECT_NE(nullptr, catalog); + EXPECT_EQ(UUID_POSES_RUZENA, catalog->catalog_id); + + /* "character/Ellie/poselib" is used by two catalogs. Check if it's using the first one. */ + catalog = service.find_catalog_by_path("character/Ellie/poselib"); + EXPECT_NE(nullptr, catalog); + EXPECT_EQ(UUID_POSES_ELLIE, catalog->catalog_id); + EXPECT_NE(UUID_POSES_ELLIE_TRAILING_SLASH, catalog->catalog_id); +} + TEST_F(AssetCatalogTest, write_single_file) { TestableAssetCatalogService service(asset_library_root_); From 6578db57cd878a94bdbdabe953c8819625c5a811 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 27 Sep 2021 18:07:01 +0200 Subject: [PATCH 0259/1500] Fix T91691: Selecting "Remove unused slots" in Materials panel removes slots that are assigned to particle systems/hair. `BKE_object_material_slot_used` would only check obdata usages, but particle settings can also (weirdly enough) use objects' material slots. So now, as its name suggests, `BKE_object_material_slot_used` does take an object as parameter, and also checks for potential slot usage from psys in the object. --- source/blender/blenkernel/BKE_material.h | 2 +- .../blender/blenkernel/intern/gpencil_curve.c | 2 +- source/blender/blenkernel/intern/material.c | 28 ++++++++++++++----- source/blender/editors/gpencil/gpencil_edit.c | 2 +- source/blender/editors/gpencil/gpencil_mesh.c | 2 +- source/blender/editors/object/object_add.c | 3 +- .../blender/editors/render/render_shading.c | 2 +- 7 files changed, 27 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index 69e2d52e1dd..b1eaf7207fa 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -90,7 +90,7 @@ void BKE_object_material_array_assign(struct Main *bmain, short BKE_object_material_slot_find_index(struct Object *ob, struct Material *ma); bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob); bool BKE_object_material_slot_remove(struct Main *bmain, struct Object *ob); -bool BKE_object_material_slot_used(struct ID *id, short actcol); +bool BKE_object_material_slot_used(struct Object *object, short actcol); struct Material *BKE_gpencil_material(struct Object *ob, short act); struct MaterialGPencilStyle *BKE_gpencil_material_settings(struct Object *ob, short act); diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 0752424df71..3819c0699f4 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -543,7 +543,7 @@ void BKE_gpencil_convert_curve(Main *bmain, int actcol = ob_gp->actcol; for (int slot = 1; slot <= ob_gp->totcol; slot++) { - while (slot <= ob_gp->totcol && !BKE_object_material_slot_used(ob_gp->data, slot)) { + while (slot <= ob_gp->totcol && !BKE_object_material_slot_used(ob_gp, slot)) { ob_gp->actcol = slot; BKE_object_material_slot_remove(bmain, ob_gp); diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 84e1f6c3c5f..fa3fbd457d1 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -46,6 +46,7 @@ #include "DNA_meta_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" +#include "DNA_particle_types.h" #include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" #include "DNA_volume_types.h" @@ -73,6 +74,7 @@ #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" +#include "BKE_object.h" #include "BKE_scene.h" #include "DEG_depsgraph.h" @@ -462,21 +464,33 @@ static void material_data_index_remove_id(ID *id, short index) } } -bool BKE_object_material_slot_used(ID *id, short actcol) +bool BKE_object_material_slot_used(Object *object, short actcol) { - /* ensure we don't try get materials from non-obdata */ - BLI_assert(OB_DATA_SUPPORT_ID(GS(id->name))); + if (!BKE_object_supports_material_slots(object)) { + return false; + } - switch (GS(id->name)) { + LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { + if (psys->part->omat == actcol) { + return true; + } + } + + ID *ob_data = object->data; + if (ob_data == NULL || !OB_DATA_SUPPORT_ID(GS(ob_data->name))) { + return false; + } + + switch (GS(ob_data->name)) { case ID_ME: - return BKE_mesh_material_index_used((Mesh *)id, actcol - 1); + return BKE_mesh_material_index_used((Mesh *)ob_data, actcol - 1); case ID_CU: - return BKE_curve_material_index_used((Curve *)id, actcol - 1); + return BKE_curve_material_index_used((Curve *)ob_data, actcol - 1); case ID_MB: /* Meta-elements don't support materials at the moment. */ return false; case ID_GD: - return BKE_gpencil_material_index_used((bGPdata *)id, actcol - 1); + return BKE_gpencil_material_index_used((bGPdata *)ob_data, actcol - 1); default: return false; } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 75ddfa47c57..1f31c60367e 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -4729,7 +4729,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) /* Remove unused slots. */ int actcol = ob_dst->actcol; for (int slot = 1; slot <= ob_dst->totcol; slot++) { - while (slot <= ob_dst->totcol && !BKE_object_material_slot_used(ob_dst->data, slot)) { + while (slot <= ob_dst->totcol && !BKE_object_material_slot_used(ob_dst, slot)) { ob_dst->actcol = slot; BKE_object_material_slot_remove(bmain, ob_dst); if (actcol >= slot) { diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index 0939d53736b..079089786d0 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -345,7 +345,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) /* Remove unused materials. */ int actcol = ob_gpencil->actcol; for (int slot = 1; slot <= ob_gpencil->totcol; slot++) { - while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil->data, slot)) { + while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil, slot)) { ob_gpencil->actcol = slot; BKE_object_material_slot_remove(CTX_data_main(C), ob_gpencil); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index beadbf2689e..236246924a9 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2828,8 +2828,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* Remove unused materials. */ int actcol = ob_gpencil->actcol; for (int slot = 1; slot <= ob_gpencil->totcol; slot++) { - while (slot <= ob_gpencil->totcol && - !BKE_object_material_slot_used(ob_gpencil->data, slot)) { + while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil, slot)) { ob_gpencil->actcol = slot; BKE_object_material_slot_remove(CTX_data_main(C), ob_gpencil); diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 8a3d8f9636b..7b2667905ff 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -690,7 +690,7 @@ static int material_slot_remove_unused_exec(bContext *C, wmOperator *op) Object *ob = objects[ob_index]; int actcol = ob->actcol; for (int slot = 1; slot <= ob->totcol; slot++) { - while (slot <= ob->totcol && !BKE_object_material_slot_used(ob->data, slot)) { + while (slot <= ob->totcol && !BKE_object_material_slot_used(ob, slot)) { ob->actcol = slot; BKE_object_material_slot_remove(bmain, ob); From aafbe111fc90876d801135f0dc5e01e9742f647a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 27 Sep 2021 15:22:53 +0200 Subject: [PATCH 0260/1500] BLI Path: add function `BLI_path_contains()` Add function `BLI_path_contains(container, containee)` that returns true if and only `container` contains `containee`. Paths are normalised and converted to native path separators before comparing. Relative paths are *not* made absolute, to simplify the function call; if this is necessary the caller has to do this conversion first. --- source/blender/blenlib/BLI_path_util.h | 4 +++ source/blender/blenlib/intern/path_util.c | 28 +++++++++++++++++++ .../blenlib/tests/BLI_path_util_test.cc | 23 +++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index 2a56d11276a..e4774c58e84 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -55,6 +55,10 @@ bool BLI_path_name_at_index(const char *__restrict path, int *__restrict r_offset, int *__restrict r_len) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +/** Return true only if #containee_path is contained in #container_path. */ +bool BLI_path_contains(const char *container_path, + const char *containee_path) ATTR_WARN_UNUSED_RESULT; + const char *BLI_path_slash_rfind(const char *string) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; int BLI_path_slash_ensure(char *string) ATTR_NONNULL(); void BLI_path_slash_rstrip(char *string) ATTR_NONNULL(); diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 4d0678035ba..4fc59e6cffd 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1935,6 +1935,34 @@ bool BLI_path_name_at_index(const char *__restrict path, return false; } +bool BLI_path_contains(const char *container_path, const char *containee_path) +{ + char container_native[PATH_MAX]; + char containee_native[PATH_MAX]; + + /* Keep space for a trailing slash. If the path is truncated by this, the containee path is + * longer than PATH_MAX and the result is ill-defined. */ + BLI_strncpy(container_native, container_path, PATH_MAX - 1); + BLI_strncpy(containee_native, containee_path, PATH_MAX); + + BLI_path_slash_native(container_native); + BLI_path_slash_native(containee_native); + + BLI_path_normalize(NULL, container_native); + BLI_path_normalize(NULL, containee_native); + + if (STREQ(container_native, containee_native)) { + /* The paths are equal, they contain each other. */ + return true; + } + + /* Add a trailing slash to prevent same-prefix directories from matching. + * e.g. "/some/path" doesn't contain "/some/pathlib". */ + BLI_path_slash_ensure(container_native); + + return BLI_str_startswith(containee_native, container_native); +} + /** * Returns pointer to the leftmost path separator in string. Not actually used anywhere. */ diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc index cf5135731e2..fde28ebaf55 100644 --- a/source/blender/blenlib/tests/BLI_path_util_test.cc +++ b/source/blender/blenlib/tests/BLI_path_util_test.cc @@ -655,3 +655,26 @@ TEST(path_util, PathRelPath) # undef PATH_REL #endif + +/* BLI_path_contains */ +TEST(path_util, PathContains) +{ + EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path")) << "A path contains itself"; + EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/inside")) + << "A path contains its subdirectory"; + EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/../path/inside")) + << "Paths should be normalised"; + EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "C:\\some\\path\\inside")) + << "Windows paths should be supported as well"; + + EXPECT_FALSE(BLI_path_contains("C:\\some\\path", "C:\\some\\other\\path")) + << "Windows paths should be supported as well"; + EXPECT_FALSE(BLI_path_contains("/some/path", "/")) + << "Root directory not be contained in a subdirectory"; + EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path/../outside")) + << "Paths should be normalised"; + EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path_library")) + << "Just sharing a suffix is not enough, path semantics should be followed"; + EXPECT_FALSE(BLI_path_contains("/some/path", "./contents")) + << "Relative paths are not supported"; +} From 90aa0a52568c629e466b6ccc513afadd0144ee1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 27 Sep 2021 15:53:05 +0200 Subject: [PATCH 0261/1500] BKE Preferences: find asset library containing a path Add `BKE_preferences_asset_library_containing_path(&U, some_path)` that finds the asset library that contains the given path. This is a simple linear search, returning the first asset library that matches. There is no smartness when it comes to nested asset libraries (like returning the library with the best-matching path), although this could be a useful feature to add later. --- source/blender/blenkernel/BKE_preferences.h | 12 ++++++++++++ source/blender/blenkernel/intern/preferences.c | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index acba2048614..bd887c1ea0d 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -43,6 +43,18 @@ struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_index( const struct UserDef *userdef, int index) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_name( const struct UserDef *userdef, const char *name) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; + +/** + * Return the bUserAssetLibrary that contains the given file/directory path. The given path can be + * the library's top-level directory, or any path inside that directory. + * + * When more than one asset libraries match, the first matching one is returned (no smartness when + * there nested asset libraries). + * + * Return NULL when no such asset library is found. */ +struct bUserAssetLibrary *BKE_preferences_asset_library_containing_path( + const struct UserDef *userdef, const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; + int BKE_preferences_asset_library_get_index(const struct UserDef *userdef, const struct bUserAssetLibrary *library) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c index c666dd4342a..0b8e8d7c311 100644 --- a/source/blender/blenkernel/intern/preferences.c +++ b/source/blender/blenkernel/intern/preferences.c @@ -94,6 +94,17 @@ bUserAssetLibrary *BKE_preferences_asset_library_find_from_name(const UserDef *u return BLI_findstring(&userdef->asset_libraries, name, offsetof(bUserAssetLibrary, name)); } +bUserAssetLibrary *BKE_preferences_asset_library_containing_path(const UserDef *userdef, + const char *path) +{ + LISTBASE_FOREACH (bUserAssetLibrary *, asset_lib_pref, &userdef->asset_libraries) { + if (BLI_path_contains(asset_lib_pref->path, path)) { + return asset_lib_pref; + } + } + return NULL; +} + int BKE_preferences_asset_library_get_index(const UserDef *userdef, const bUserAssetLibrary *library) { From 10061ee18ae97968bc9e0a7d83a353b077329aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 27 Sep 2021 15:28:16 +0200 Subject: [PATCH 0262/1500] Asset Catalogs: write catalogs to disk when saving the blend file The Asset Catalog Definition File is now saved whenever the blend file is saved. The location of the CDF depends on where the blend file is saved, and whether previously a CDF was already loaded, according to the following rules. The first matching rule wins: 1. Already loaded a CDF from disk? -> Always write to that file. 2. The directory containing the blend file has a `blender_assets.cats.txt` file? -> Merge with & write to that file. 3. The directory containing the blend file is part of an asset library, as per the user's preferences? -> Merge with & write to `${ASSET_LIBRARY_ROOT}/blender_assets.cats.txt` 4. Create a new file `blender_assets.cats.txt` next to the blend file. --- .../blender/blenkernel/BKE_asset_catalog.hh | 28 +++ .../blender/blenkernel/BKE_asset_library.hh | 9 + .../blenkernel/intern/asset_catalog.cc | 62 +++++++ .../blenkernel/intern/asset_catalog_test.cc | 165 +++++++++++++++++- .../blenkernel/intern/asset_library.cc | 42 +++++ 5 files changed, 298 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 07373caf701..7b54d7cf572 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -73,6 +73,25 @@ class AssetCatalogService { * save was successful. */ bool write_to_disk(const CatalogFilePath &directory_for_new_files); + /** + * Write the catalog definitions to disk in response to the blend file being saved. + * + * The location where the catalogs are saved is variable, and depends on the location of the + * blend file. The first matching rule wins: + * + * - Already loaded a CDF from disk? + * -> Always write to that file. + * - The directory containing the blend file has a blender_assets.cats.txt file? + * -> Merge with & write to that file. + * - The directory containing the blend file is part of an asset library, as per + * the user's preferences? + * -> Merge with & write to ${ASSET_LIBRARY_ROOT}/blender_assets.cats.txt + * - Create a new file blender_assets.cats.txt next to the blend file. + * + * Return true on success, which either means there were no in-memory categories to save, + * or the save was successful. */ + bool write_to_disk_on_blendfile_save(const char *blend_file_path); + /** * Merge on-disk changes into the in-memory asset catalogs. * This should be called before writing the asset catalogs to disk. @@ -124,6 +143,15 @@ class AssetCatalogService { std::unique_ptr construct_cdf_in_memory( const CatalogFilePath &file_path); + /** + * Find a suitable path to write a CDF to. + * + * This depends on the location of the blend file, and on whether a CDF already exists next to it + * or whether the blend file is saved inside an asset library. + */ + static CatalogFilePath find_suitable_cdf_path_for_writing( + const CatalogFilePath &blend_file_path); + std::unique_ptr read_into_tree(); void rebuild_tree(); }; diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh index 68f7481574e..fc5e137dd3e 100644 --- a/source/blender/blenkernel/BKE_asset_library.hh +++ b/source/blender/blenkernel/BKE_asset_library.hh @@ -27,6 +27,7 @@ #include "BKE_asset_library.h" #include "BKE_asset_catalog.hh" +#include "BKE_callbacks.h" #include @@ -36,6 +37,14 @@ struct AssetLibrary { std::unique_ptr catalog_service; void load(StringRefNull library_root_directory); + + void on_save_handler_register(); + void on_save_handler_unregister(); + + void on_save_post(struct Main *, struct PointerRNA **pointers, const int num_pointers); + + private: + bCallbackFuncStore on_save_callback_store_; }; } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index b65ae12e5a7..ee22e25a6ab 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -19,11 +19,14 @@ */ #include "BKE_asset_catalog.hh" +#include "BKE_preferences.h" #include "BLI_fileops.h" #include "BLI_path_util.h" #include "BLI_string_ref.hh" +#include "DNA_userdef_types.h" + /* For S_ISREG() and S_ISDIR() on Windows. */ #ifdef WIN32 # include "BLI_winstuff.h" @@ -266,6 +269,65 @@ bool AssetCatalogService::write_to_disk(const CatalogFilePath &directory_for_new return catalog_definition_file_->write_to_disk(); } +bool AssetCatalogService::write_to_disk_on_blendfile_save(const char *blend_file_path) +{ + /* TODO(Sybren): deduplicate this and write_to_disk(...); maybe the latter function isn't even + * necessary any more. */ + + /* - Already loaded a CDF from disk? -> Always write to that file. */ + if (this->catalog_definition_file_) { + merge_from_disk_before_writing(); + return catalog_definition_file_->write_to_disk(); + } + + if (catalogs_.is_empty() && deleted_catalogs_.is_empty()) { + /* Avoid saving anything, when there is nothing to save. */ + return true; /* Writing nothing when there is nothing to write is still a success. */ + } + + const CatalogFilePath cdf_path_to_write = find_suitable_cdf_path_for_writing(blend_file_path); + this->catalog_definition_file_ = construct_cdf_in_memory(cdf_path_to_write); + merge_from_disk_before_writing(); + return catalog_definition_file_->write_to_disk(); +} + +CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing( + const CatalogFilePath &blend_file_path) +{ + /* Determine the default CDF path in the same directory of the blend file. */ + char blend_dir_path[PATH_MAX]; + BLI_split_dir_part(blend_file_path.c_str(), blend_dir_path, sizeof(blend_dir_path)); + const CatalogFilePath cdf_path_next_to_blend = asset_definition_default_file_path_from_dir( + blend_dir_path); + + if (BLI_exists(cdf_path_next_to_blend.c_str())) { + /* - The directory containing the blend file has a blender_assets.cats.txt file? + * -> Merge with & write to that file. */ + return cdf_path_next_to_blend; + } + + const bUserAssetLibrary *asset_lib_pref = BKE_preferences_asset_library_containing_path( + &U, blend_file_path.c_str()); + if (asset_lib_pref) { + /* - The directory containing the blend file is part of an asset library, as per + * the user's preferences? + * -> Merge with & write to ${ASSET_LIBRARY_ROOT}/blender_assets.cats.txt */ + + char asset_lib_cdf_path[PATH_MAX]; + BLI_path_join(asset_lib_cdf_path, + sizeof(asset_lib_cdf_path), + asset_lib_pref->path, + DEFAULT_CATALOG_FILENAME.c_str(), + NULL); + + return asset_lib_cdf_path; + } + + /* - Otherwise + * -> Create a new file blender_assets.cats.txt next to the blend file. */ + return cdf_path_next_to_blend; +} + std::unique_ptr AssetCatalogService::construct_cdf_in_memory( const CatalogFilePath &file_path) { diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 87ee61b015f..5177abd2820 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -19,10 +19,13 @@ #include "BKE_appdir.h" #include "BKE_asset_catalog.hh" +#include "BKE_preferences.h" #include "BLI_fileops.h" #include "BLI_path_util.h" +#include "DNA_userdef_types.h" + #include "testing/testing.h" namespace blender::bke::tests { @@ -43,6 +46,8 @@ const bUUID UUID_AGENT_47("c5744ba5-43f5-4f73-8e52-010ad4a61b34"); /* Subclass that adds accessors such that protected fields can be used in tests. */ class TestableAssetCatalogService : public AssetCatalogService { public: + TestableAssetCatalogService() = default; + explicit TestableAssetCatalogService(const CatalogFilePath &asset_library_root) : AssetCatalogService(asset_library_root) { @@ -405,6 +410,152 @@ TEST_F(AssetCatalogTest, no_writing_empty_files) EXPECT_FALSE(BLI_exists(default_cdf_path.c_str())); } +/* Already loaded a CDF, saving to some unrelated directory. */ +TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf) +{ + const CatalogFilePath top_level_dir = create_temp_path(); // Has trailing slash. + + /* Create a copy of the CDF in SVN, so we can safely write to it. */ + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath cdf_dirname = top_level_dir + "other_dir/"; + const CatalogFilePath cdf_filename = cdf_dirname + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + ASSERT_TRUE(BLI_dir_create_recursive(cdf_dirname.c_str())); + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), cdf_filename.c_str())) + << "Unable to copy " << original_cdf_file << " to " << cdf_filename; + + /* Load the CDF, add a catalog, and trigger a write. This should write to the loaded CDF. */ + TestableAssetCatalogService service(cdf_filename); + service.load_from_disk(); + const AssetCatalog *cat = service.create_catalog("some/catalog/path"); + + const CatalogFilePath blendfilename = top_level_dir + "subdir/some_file.blend"; + ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str())); + EXPECT_EQ(cdf_filename, service.get_catalog_definition_file()->file_path); + + /* Test that the CDF was created in the expected location. */ + const CatalogFilePath backup_filename = cdf_filename + "~"; + EXPECT_TRUE(BLI_exists(cdf_filename.c_str())); + EXPECT_TRUE(BLI_exists(backup_filename.c_str())) + << "Overwritten CDF should have been backed up."; + + /* Test that the on-disk CDF contains the expected catalogs. */ + AssetCatalogService loaded_service(cdf_filename); + loaded_service.load_from_disk(); + EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id)) + << "Expected to see the newly-created catalog."; + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)) + << "Expected to see the already-existing catalog."; +} + +/* Create some catalogs in memory, save to directory that doesn't contain anything else. */ +TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_empty_directory) +{ + const CatalogFilePath target_dir = create_temp_path(); // Has trailing slash. + + TestableAssetCatalogService service; + const AssetCatalog *cat = service.create_catalog("some/catalog/path"); + + const CatalogFilePath blendfilename = target_dir + "some_file.blend"; + ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str())); + + /* Test that the CDF was created in the expected location. */ + const CatalogFilePath expected_cdf_path = target_dir + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + EXPECT_TRUE(BLI_exists(expected_cdf_path.c_str())); + + /* Test that the in-memory CDF has been created, and contains the expected catalog. */ + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + ASSERT_NE(nullptr, cdf); + EXPECT_TRUE(cdf->contains(cat->catalog_id)); + + /* Test that the on-disk CDF contains the expected catalog. */ + AssetCatalogService loaded_service(expected_cdf_path); + loaded_service.load_from_disk(); + EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id)); +} + +/* Create some catalogs in memory, save to directory that contains a default CDF. */ +TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_merge) +{ + const CatalogFilePath target_dir = create_temp_path(); // Has trailing slash. + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath writable_cdf_file = target_dir + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); + + /* Create the catalog service without loading the already-existing CDF. */ + TestableAssetCatalogService service; + const AssetCatalog *cat = service.create_catalog("some/catalog/path"); + + /* Mock that the blend file is written to a subdirectory of the asset library. */ + const CatalogFilePath blendfilename = target_dir + "some_file.blend"; + ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str())); + + /* Test that the CDF still exists in the expected location. */ + const CatalogFilePath backup_filename = writable_cdf_file + "~"; + EXPECT_TRUE(BLI_exists(writable_cdf_file.c_str())); + EXPECT_TRUE(BLI_exists(backup_filename.c_str())) + << "Overwritten CDF should have been backed up."; + + /* Test that the in-memory CDF has the expected file path. */ + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + ASSERT_NE(nullptr, cdf); + EXPECT_EQ(writable_cdf_file, cdf->file_path); + + /* Test that the in-memory catalogs have been merged with the on-disk one. */ + AssetCatalogService loaded_service(writable_cdf_file); + loaded_service.load_from_disk(); + EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); +} + +/* Create some catalogs in memory, save to subdirectory of a registered asset library. */ +TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_asset_lib) +{ + const CatalogFilePath target_dir = create_temp_path(); // Has trailing slash. + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath registered_asset_lib = target_dir + "my_asset_library/"; + CatalogFilePath writable_cdf_file = registered_asset_lib + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + BLI_path_slash_native(writable_cdf_file.data()); + + /* Set up a temporary asset library for testing. */ + bUserAssetLibrary *asset_lib_pref = BKE_preferences_asset_library_add( + &U, "Test", registered_asset_lib.c_str()); + ASSERT_NE(nullptr, asset_lib_pref); + ASSERT_TRUE(BLI_dir_create_recursive(registered_asset_lib.c_str())); + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); + + /* Create the catalog service without loading the already-existing CDF. */ + TestableAssetCatalogService service; + const CatalogFilePath blenddirname = registered_asset_lib + "subdirectory/"; + const CatalogFilePath blendfilename = blenddirname + "some_file.blend"; + ASSERT_TRUE(BLI_dir_create_recursive(blenddirname.c_str())); + const AssetCatalog *cat = service.create_catalog("some/catalog/path"); + + /* Mock that the blend file is written to the directory already containing a CDF. */ + ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str())); + + /* Test that the CDF still exists in the expected location. */ + EXPECT_TRUE(BLI_exists(writable_cdf_file.c_str())); + const CatalogFilePath backup_filename = writable_cdf_file + "~"; + EXPECT_TRUE(BLI_exists(backup_filename.c_str())) + << "Overwritten CDF should have been backed up."; + + /* Test that the in-memory CDF has the expected file path. */ + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + BLI_path_slash_native(cdf->file_path.data()); + EXPECT_EQ(writable_cdf_file, cdf->file_path); + + /* Test that the in-memory catalogs have been merged with the on-disk one. */ + AssetCatalogService loaded_service(writable_cdf_file); + loaded_service.load_from_disk(); + EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); + + BKE_preferences_asset_library_remove(&U, asset_lib_pref); +} + TEST_F(AssetCatalogTest, create_first_catalog_from_scratch) { /* Even from scratch a root directory should be known. */ @@ -450,7 +601,7 @@ TEST_F(AssetCatalogTest, create_catalog_after_loading_file) AssetCatalogService::DEFAULT_CATALOG_FILENAME; const CatalogFilePath writable_catalog_path = temp_lib_root + AssetCatalogService::DEFAULT_CATALOG_FILENAME; - BLI_copy(default_catalog_path.c_str(), writable_catalog_path.c_str()); + ASSERT_EQ(0, BLI_copy(default_catalog_path.c_str(), writable_catalog_path.c_str())); EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str())); EXPECT_TRUE(BLI_is_file(writable_catalog_path.c_str())); @@ -485,8 +636,7 @@ TEST_F(AssetCatalogTest, create_catalog_after_loading_file) TEST_F(AssetCatalogTest, create_catalog_path_cleanup) { - const CatalogFilePath temp_lib_root = use_temp_path(); - AssetCatalogService service(temp_lib_root); + AssetCatalogService service; AssetCatalog *cat = service.create_catalog(" /some/path / "); EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id)); @@ -496,8 +646,7 @@ TEST_F(AssetCatalogTest, create_catalog_path_cleanup) TEST_F(AssetCatalogTest, create_catalog_simple_name) { - const CatalogFilePath temp_lib_root = use_temp_path(); - AssetCatalogService service(temp_lib_root); + AssetCatalogService service; AssetCatalog *cat = service.create_catalog( "production/Spite Fright/Characters/Victora/Pose Library/Approved/Body Parts/Hands"); @@ -568,14 +717,14 @@ TEST_F(AssetCatalogTest, merge_catalog_files) const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; const CatalogFilePath modified_cdf_file = asset_library_root_ + "/modified_assets.cats.txt"; const CatalogFilePath temp_cdf_file = cdf_dir + "blender_assets.cats.txt"; - BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str()); + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str())); // Load the unmodified, original CDF. TestableAssetCatalogService service(asset_library_root_); service.load_from_disk(cdf_dir); // Copy a modified file, to mimick a situation where someone changed the CDF after we loaded it. - BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str()); + ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str())); // Overwrite the modified file. This should merge the on-disk file with our catalogs. service.write_to_disk(cdf_dir); @@ -602,7 +751,7 @@ TEST_F(AssetCatalogTest, backups) const CatalogFilePath cdf_dir = create_temp_path(); const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; const CatalogFilePath writable_cdf_file = cdf_dir + "/blender_assets.cats.txt"; - BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str()); + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); /* Read a CDF, modify, and write it. */ AssetCatalogService service(cdf_dir); diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index 1153e7b29f5..6fed355c41b 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -19,6 +19,8 @@ */ #include "BKE_asset_library.hh" +#include "BKE_callbacks.h" +#include "BKE_main.h" #include "MEM_guardedalloc.h" @@ -31,6 +33,7 @@ struct AssetLibrary *BKE_asset_library_load(const char *library_path) { blender::bke::AssetLibrary *lib = new blender::bke::AssetLibrary(); + lib->on_save_handler_register(); lib->load(library_path); return reinterpret_cast(lib); } @@ -38,6 +41,7 @@ struct AssetLibrary *BKE_asset_library_load(const char *library_path) void BKE_asset_library_free(struct AssetLibrary *asset_library) { blender::bke::AssetLibrary *lib = reinterpret_cast(asset_library); + lib->on_save_handler_unregister(); delete lib; } @@ -50,4 +54,42 @@ void AssetLibrary::load(StringRefNull library_root_directory) this->catalog_service = std::move(catalog_service); } +namespace { +void asset_library_on_save_post(struct Main *main, + struct PointerRNA **pointers, + const int num_pointers, + void *arg) +{ + AssetLibrary *asset_lib = static_cast(arg); + asset_lib->on_save_post(main, pointers, num_pointers); +} +} // namespace + +void AssetLibrary::on_save_handler_register() +{ + /* The callback system doesn't own `on_save_callback_store_`. */ + on_save_callback_store_.alloc = false; + + on_save_callback_store_.func = asset_library_on_save_post; + on_save_callback_store_.arg = this; + + BKE_callback_add(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST); +} + +void AssetLibrary::on_save_handler_unregister() +{ + BKE_callback_remove(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST); +} + +void AssetLibrary::on_save_post(struct Main *main, + struct PointerRNA ** /*pointers*/, + const int /*num_pointers*/) +{ + if (this->catalog_service) { + return; + } + + this->catalog_service->write_to_disk_on_blendfile_save(main->name); +} + } // namespace blender::bke From efa9667c092516dea772c5ea81d904f2aade0c7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 27 Sep 2021 18:14:44 +0200 Subject: [PATCH 0263/1500] Cleanup: Asset catalogs, fix clang-tidy warning Change `auto &catalog` to `const auto &catalog`. No functional changes. --- source/blender/blenkernel/intern/asset_catalog.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index ee22e25a6ab..8f57f96e771 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -69,7 +69,7 @@ AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) AssetCatalog *AssetCatalogService::find_catalog_by_path(const CatalogPath &path) const { - for (auto &catalog : catalogs_.values()) { + for (const auto &catalog : catalogs_.values()) { if (catalog->path == path) { return catalog.get(); } From c75c08a737c0737304bc7f6757152e4c99d4fe4d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 27 Sep 2021 11:16:25 -0500 Subject: [PATCH 0264/1500] Geometry Nodes: Distribute points once per instance reference With this commit, the distribute points on faces node runs only once for every unique mesh in its input. That means if there are 100 instances of the same mesh, it will only run once. This basically reverts rB84a4f2ae68d408301. The optimization there didn't end up being worth it in the end, since it complicates code quite a lot. It's also incompatible with this method of dealing with instances, and it breaks field evaluation for instances, where we would have to make sure to handle each instance transform properly otherwise, evaluating the field separately for every instance. Differential Revision: https://developer.blender.org/D12630 --- .../node_geo_distribute_points_on_faces.cc | 531 ++++++------------ 1 file changed, 185 insertions(+), 346 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 95987a15f32..3f2541dcded 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -17,6 +17,7 @@ #include "BLI_kdtree.h" #include "BLI_noise.hh" #include "BLI_rand.hh" +#include "BLI_task.hh" #include "BLI_timeit.hh" #include "DNA_mesh_types.h" @@ -25,7 +26,6 @@ #include "BKE_attribute_math.hh" #include "BKE_bvhutils.h" -#include "BKE_geometry_set_instances.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_mesh_sample.hh" @@ -96,7 +96,6 @@ static float3 normal_to_euler_rotation(const float3 normal) } static void sample_mesh_surface(const Mesh &mesh, - const float4x4 &transform, const float base_density, const Span density_factors, const int seed, @@ -115,9 +114,9 @@ static void sample_mesh_surface(const Mesh &mesh, const int v0_index = mesh.mloop[v0_loop].v; const int v1_index = mesh.mloop[v1_loop].v; const int v2_index = mesh.mloop[v2_loop].v; - const float3 v0_pos = transform * float3(mesh.mvert[v0_index].co); - const float3 v1_pos = transform * float3(mesh.mvert[v1_index].co); - const float3 v2_pos = transform * float3(mesh.mvert[v2_index].co); + const float3 v0_pos = float3(mesh.mvert[v0_index].co); + const float3 v1_pos = float3(mesh.mvert[v1_index].co); + const float3 v2_pos = float3(mesh.mvert[v2_index].co); float looptri_density_factor = 1.0f; if (!density_factors.is_empty()) { @@ -147,65 +146,53 @@ static void sample_mesh_surface(const Mesh &mesh, } } -BLI_NOINLINE static KDTree_3d *build_kdtree(Span> positions_all, - const int initial_points_len) +BLI_NOINLINE static KDTree_3d *build_kdtree(Span positions) { - KDTree_3d *kdtree = BLI_kdtree_3d_new(initial_points_len); + KDTree_3d *kdtree = BLI_kdtree_3d_new(positions.size()); int i_point = 0; - for (const Vector &positions : positions_all) { - for (const float3 position : positions) { - BLI_kdtree_3d_insert(kdtree, i_point, position); - i_point++; - } + for (const float3 position : positions) { + BLI_kdtree_3d_insert(kdtree, i_point, position); + i_point++; } + BLI_kdtree_3d_balance(kdtree); return kdtree; } BLI_NOINLINE static void update_elimination_mask_for_close_points( - Span> positions_all, - Span instance_start_offsets, - const float minimum_distance, - MutableSpan elimination_mask, - const int initial_points_len) + Span positions, const float minimum_distance, MutableSpan elimination_mask) { if (minimum_distance <= 0.0f) { return; } - KDTree_3d *kdtree = build_kdtree(positions_all, initial_points_len); + KDTree_3d *kdtree = build_kdtree(positions); - /* The elimination mask is a flattened array for every point, - * so keep track of the index to it separately. */ - for (const int i_instance : positions_all.index_range()) { - Span positions = positions_all[i_instance]; - const int offset = instance_start_offsets[i_instance]; - - for (const int i : positions.index_range()) { - if (elimination_mask[offset + i]) { - continue; - } - - struct CallbackData { - int index; - MutableSpan elimination_mask; - } callback_data = {offset + i, elimination_mask}; - - BLI_kdtree_3d_range_search_cb( - kdtree, - positions[i], - minimum_distance, - [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) { - CallbackData &callback_data = *static_cast(user_data); - if (index != callback_data.index) { - callback_data.elimination_mask[index] = true; - } - return true; - }, - &callback_data); + for (const int i : positions.index_range()) { + if (elimination_mask[i]) { + continue; } + + struct CallbackData { + int index; + MutableSpan elimination_mask; + } callback_data = {i, elimination_mask}; + + BLI_kdtree_3d_range_search_cb( + kdtree, + positions[i], + minimum_distance, + [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) { + CallbackData &callback_data = *static_cast(user_data); + if (index != callback_data.index) { + callback_data.elimination_mask[index] = true; + } + return true; + }, + &callback_data); } + BLI_kdtree_3d_free(kdtree); } @@ -289,18 +276,19 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, } BLI_NOINLINE static void propagate_existing_attributes( - const Span set_groups, - const Span instance_start_offsets, + const MeshComponent &mesh_component, const Map &attributes, - GeometryComponent &component, - const Span> bary_coords_array, - const Span> looptri_indices_array) + GeometryComponent &point_component, + const Span bary_coords, + const Span looptri_indices) { + const Mesh &mesh = *mesh_component.get_for_read(); + for (Map::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; const CustomDataType output_data_type = entry.value.data_type; /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */ - OutputAttribute attribute_out = component.attribute_try_get_for_output_only( + OutputAttribute attribute_out = point_component.attribute_try_get_for_output_only( attribute_id, ATTR_DOMAIN_POINT, output_data_type); if (!attribute_out) { continue; @@ -308,46 +296,22 @@ BLI_NOINLINE static void propagate_existing_attributes( GMutableSpan out_span = attribute_out.as_span(); - int i_instance = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - const MeshComponent &source_component = *set.get_component_for_read(); - const Mesh &mesh = *source_component.get_for_read(); - - std::optional attribute_info = component.attribute_get_meta_data( - attribute_id); - if (!attribute_info) { - i_instance += set_group.transforms.size(); - continue; - } - - const AttributeDomain source_domain = attribute_info->domain; - GVArrayPtr source_attribute = source_component.attribute_get_for_read( - attribute_id, source_domain, output_data_type, nullptr); - if (!source_attribute) { - i_instance += set_group.transforms.size(); - continue; - } - - for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { - const int offset = instance_start_offsets[i_instance]; - Span bary_coords = bary_coords_array[i_instance]; - Span looptri_indices = looptri_indices_array[i_instance]; - - GMutableSpan instance_span = out_span.slice(offset, bary_coords.size()); - interpolate_attribute( - mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span); - - i_instance++; - } - - attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) { - using T = decltype(dummy); - - GVArray_Span source_span{*source_attribute}; - }); + std::optional attribute_info = point_component.attribute_get_meta_data( + attribute_id); + if (!attribute_info) { + continue; } + const AttributeDomain source_domain = attribute_info->domain; + GVArrayPtr source_attribute = mesh_component.attribute_get_for_read( + attribute_id, source_domain, output_data_type, nullptr); + if (!source_attribute) { + continue; + } + + interpolate_attribute( + mesh, bary_coords, looptri_indices, source_domain, *source_attribute, out_span); + attribute_out.save(); } } @@ -360,87 +324,64 @@ struct AttributeOutputs { }; } // namespace -BLI_NOINLINE static void compute_attribute_outputs(const Span sets, - const Span instance_start_offsets, - GeometryComponent &component, - const Span> bary_coords_array, - const Span> looptri_indices_array, +BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_component, + PointCloudComponent &point_component, + const Span bary_coords, + const Span looptri_indices, const AttributeOutputs &attribute_outputs) { std::optional> id_attribute; std::optional> normal_attribute; std::optional> rotation_attribute; - MutableSpan result_ids; - MutableSpan result_normals; - MutableSpan result_rotations; + MutableSpan ids; + MutableSpan normals; + MutableSpan rotations; if (attribute_outputs.stable_id_id) { - id_attribute.emplace(component.attribute_try_get_for_output_only( + id_attribute.emplace(point_component.attribute_try_get_for_output_only( attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT)); - result_ids = id_attribute->as_span(); + ids = id_attribute->as_span(); } if (attribute_outputs.normal_id) { - normal_attribute.emplace(component.attribute_try_get_for_output_only( + normal_attribute.emplace(point_component.attribute_try_get_for_output_only( attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT)); - result_normals = normal_attribute->as_span(); + normals = normal_attribute->as_span(); } if (attribute_outputs.rotation_id) { - rotation_attribute.emplace(component.attribute_try_get_for_output_only( + rotation_attribute.emplace(point_component.attribute_try_get_for_output_only( attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT)); - result_rotations = rotation_attribute->as_span(); + rotations = rotation_attribute->as_span(); } - int i_instance = 0; - for (const GeometryInstanceGroup &set_group : sets) { - const GeometrySet &set = set_group.geometry_set; - const MeshComponent &component = *set.get_component_for_read(); - const Mesh &mesh = *component.get_for_read(); - const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; + const Mesh &mesh = *mesh_component.get_for_read(); + const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; - for (const float4x4 &transform : set_group.transforms) { - const int offset = instance_start_offsets[i_instance]; + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; - Span bary_coords = bary_coords_array[i_instance]; - Span looptri_indices = looptri_indices_array[i_instance]; - MutableSpan ids = result_ids.slice(offset, bary_coords.size()); - MutableSpan normals = result_normals.slice(offset, bary_coords.size()); - MutableSpan rotations = result_rotations.slice(offset, bary_coords.size()); + const int v0_index = mesh.mloop[looptri.tri[0]].v; + const int v1_index = mesh.mloop[looptri.tri[1]].v; + const int v2_index = mesh.mloop[looptri.tri[2]].v; + const float3 v0_pos = float3(mesh.mvert[v0_index].co); + const float3 v1_pos = float3(mesh.mvert[v1_index].co); + const float3 v2_pos = float3(mesh.mvert[v2_index].co); - /* Use one matrix multiplication per point instead of three (for each triangle corner). */ - float rotation_matrix[3][3]; - mat4_to_rot(rotation_matrix, transform.values); - - for (const int i : bary_coords.index_range()) { - const int looptri_index = looptri_indices[i]; - const MLoopTri &looptri = looptris[looptri_index]; - const float3 &bary_coord = bary_coords[i]; - - const int v0_index = mesh.mloop[looptri.tri[0]].v; - const int v1_index = mesh.mloop[looptri.tri[1]].v; - const int v2_index = mesh.mloop[looptri.tri[2]].v; - const float3 v0_pos = float3(mesh.mvert[v0_index].co); - const float3 v1_pos = float3(mesh.mvert[v1_index].co); - const float3 v2_pos = float3(mesh.mvert[v2_index].co); - - if (!result_ids.is_empty()) { - ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index); - } - float3 normal; - if (!result_normals.is_empty() || !result_rotations.is_empty()) { - normal_tri_v3(normal, v0_pos, v1_pos, v2_pos); - mul_m3_v3(rotation_matrix, normal); - } - if (!result_normals.is_empty()) { - normals[i] = normal; - } - if (!result_rotations.is_empty()) { - rotations[i] = normal_to_euler_rotation(normal); - } - } - - i_instance++; + if (!ids.is_empty()) { + ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index); + } + float3 normal; + if (!normals.is_empty() || !rotations.is_empty()) { + normal_tri_v3(normal, v0_pos, v1_pos, v2_pos); + } + if (!normals.is_empty()) { + normals[i] = normal; + } + if (!rotations.is_empty()) { + rotations[i] = normal_to_euler_rotation(normal); } } @@ -476,226 +417,126 @@ static Array calc_full_density_factors_with_selection(const MeshComponent return densities; } -static void distribute_points_random(Span set_groups, +static void distribute_points_random(const MeshComponent &component, const Field &density_field, const Field &selection_field, const int seed, - MutableSpan> positions_all, - MutableSpan> bary_coords_all, - MutableSpan> looptri_indices_all) + Vector &positions, + Vector &bary_coords, + Vector &looptri_indices) { - int i_instance = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - const MeshComponent &component = *set.get_component_for_read(); - const Array densities = calc_full_density_factors_with_selection( - component, density_field, selection_field); - const Mesh &mesh = *component.get_for_read(); - for (const float4x4 &transform : set_group.transforms) { - Vector &positions = positions_all[i_instance]; - Vector &bary_coords = bary_coords_all[i_instance]; - Vector &looptri_indices = looptri_indices_all[i_instance]; - const int instance_seed = noise::hash(seed, i_instance); - sample_mesh_surface(mesh, - transform, - 1.0f, - densities, - instance_seed, - positions, - bary_coords, - looptri_indices); - i_instance++; - } - } + const Array densities = calc_full_density_factors_with_selection( + component, density_field, selection_field); + const Mesh &mesh = *component.get_for_read(); + sample_mesh_surface(mesh, 1.0f, densities, seed, positions, bary_coords, looptri_indices); } -static void distribute_points_poisson_disk(Span set_groups, +static void distribute_points_poisson_disk(const MeshComponent &mesh_component, const float minimum_distance, const float max_density, const Field &density_factor_field, const Field &selection_field, const int seed, - MutableSpan> positions_all, - MutableSpan> bary_coords_all, - MutableSpan> looptri_indices_all) + Vector &positions, + Vector &bary_coords, + Vector &looptri_indices) { - Array instance_start_offsets(positions_all.size()); - int initial_points_len = 0; - int i_instance = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - const MeshComponent &component = *set.get_component_for_read(); - const Mesh &mesh = *component.get_for_read(); - for (const float4x4 &transform : set_group.transforms) { - Vector &positions = positions_all[i_instance]; - Vector &bary_coords = bary_coords_all[i_instance]; - Vector &looptri_indices = looptri_indices_all[i_instance]; - const int instance_seed = noise::hash(seed, i_instance); - sample_mesh_surface(mesh, - transform, - max_density, - {}, - instance_seed, - positions, - bary_coords, - looptri_indices); + const Mesh &mesh = *mesh_component.get_for_read(); + sample_mesh_surface(mesh, max_density, {}, seed, positions, bary_coords, looptri_indices); - instance_start_offsets[i_instance] = initial_points_len; - initial_points_len += positions.size(); - i_instance++; - } + Array elimination_mask(positions.size(), false); + update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask); + + const Array density_factors = calc_full_density_factors_with_selection( + mesh_component, density_factor_field, selection_field); + + update_elimination_mask_based_on_density_factors( + mesh, density_factors, bary_coords, looptri_indices, elimination_mask.as_mutable_span()); + + eliminate_points_based_on_mask( + elimination_mask.as_span(), positions, bary_coords, looptri_indices); +} + +static void point_distribution_calculate(GeometrySet &geometry_set, + const Field selection_field, + const GeometryNodeDistributePointsOnFacesMode method, + const int seed, + const AttributeOutputs &attribute_outputs, + const GeoNodeExecParams ¶ms) +{ + if (!geometry_set.has_mesh()) { + return; } - /* Unlike the other result arrays, the elimination mask in stored as a flat array for every - * point, in order to simplify culling points from the KDTree (which needs to know about all - * points at once). */ - Array elimination_mask(initial_points_len, false); - update_elimination_mask_for_close_points(positions_all, - instance_start_offsets, - minimum_distance, - elimination_mask, - initial_points_len); + const MeshComponent &mesh_component = *geometry_set.get_component_for_read(); - i_instance = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - const MeshComponent &component = *set.get_component_for_read(); - const Mesh &mesh = *component.get_for_read(); + Vector positions; + Vector bary_coords; + Vector looptri_indices; - const Array density_factors = calc_full_density_factors_with_selection( - component, density_factor_field, selection_field); - - for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { - Vector &positions = positions_all[i_instance]; - Vector &bary_coords = bary_coords_all[i_instance]; - Vector &looptri_indices = looptri_indices_all[i_instance]; - - const int offset = instance_start_offsets[i_instance]; - update_elimination_mask_based_on_density_factors( - mesh, - density_factors, - bary_coords, - looptri_indices, - elimination_mask.as_mutable_span().slice(offset, positions.size())); - - eliminate_points_based_on_mask(elimination_mask.as_span().slice(offset, positions.size()), + switch (method) { + case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: { + const Field density_field = params.get_input>("Density"); + distribute_points_random(mesh_component, + density_field, + selection_field, + seed, + positions, + bary_coords, + looptri_indices); + break; + } + case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: { + const float minimum_distance = params.get_input("Distance Min"); + const float density_max = params.get_input("Density Max"); + const Field density_factors_field = params.get_input>("Density Factor"); + distribute_points_poisson_disk(mesh_component, + minimum_distance, + density_max, + density_factors_field, + selection_field, + seed, positions, bary_coords, looptri_indices); - - i_instance++; + break; } } + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); + memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size()); + uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); + geometry_set.replace_pointcloud(pointcloud); + + PointCloudComponent &point_component = + geometry_set.get_component_for_write(); + + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); + + /* Position is set separately. */ + attributes.remove("position"); + + propagate_existing_attributes( + mesh_component, attributes, point_component, bary_coords, looptri_indices); + + compute_attribute_outputs( + mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs); + + geometry_set.replace_mesh(nullptr); } static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); - const GeometryNodeDistributePointsOnFacesMode distribute_method = + const GeometryNodeDistributePointsOnFacesMode method = static_cast(params.node().custom1); const int seed = params.get_input("Seed") * 5383843; const Field selection_field = params.extract_input>("Selection"); - Vector set_groups; - geometry_set_gather_instances(geometry_set, set_groups); - if (set_groups.is_empty()) { - params.set_output("Points", GeometrySet()); - return; - } - - /* Remove any set inputs that don't contain a mesh, to avoid checking later on. */ - for (int i = set_groups.size() - 1; i >= 0; i--) { - const GeometrySet &set = set_groups[i].geometry_set; - if (!set.has_mesh()) { - set_groups.remove_and_reorder(i); - } - } - - if (set_groups.is_empty()) { - params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh")); - params.set_output("Points", GeometrySet()); - return; - } - - int instances_len = 0; - for (GeometryInstanceGroup &set_group : set_groups) { - instances_len += set_group.transforms.size(); - } - - /* Store data per-instance in order to simplify attribute access after the scattering, - * and to make the point elimination simpler for the poisson disk mode. Note that some - * vectors will be empty if any instances don't contain mesh data. */ - Array> positions_all(instances_len); - Array> bary_coords_all(instances_len); - Array> looptri_indices_all(instances_len); - - switch (distribute_method) { - case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: { - const Field density_field = params.extract_input>("Density"); - distribute_points_random(set_groups, - density_field, - selection_field, - seed, - positions_all, - bary_coords_all, - looptri_indices_all); - break; - } - case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: { - const float minimum_distance = params.extract_input("Distance Min"); - const float density_max = params.extract_input("Density Max"); - const Field density_factors_field = params.extract_input>( - "Density Factor"); - distribute_points_poisson_disk(set_groups, - minimum_distance, - density_max, - density_factors_field, - selection_field, - seed, - positions_all, - bary_coords_all, - looptri_indices_all); - break; - } - } - - int final_points_len = 0; - Array instance_start_offsets(set_groups.size()); - for (const int i : positions_all.index_range()) { - Vector &positions = positions_all[i]; - instance_start_offsets[i] = final_points_len; - final_points_len += positions.size(); - } - - PointCloud *pointcloud = BKE_pointcloud_new_nomain(final_points_len); - for (const int instance_index : positions_all.index_range()) { - const int offset = instance_start_offsets[instance_index]; - Span positions = positions_all[instance_index]; - memcpy(pointcloud->co + offset, positions.data(), sizeof(float3) * positions.size()); - } - - uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); - - GeometrySet geometry_set_out = GeometrySet::create_with_pointcloud(pointcloud); - PointCloudComponent &point_component = - geometry_set_out.get_component_for_write(); - - Map attributes; - geometry_set.gather_attributes_for_propagation( - {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, true, attributes); - - /* Position is set separately. */ - attributes.remove("position"); - - propagate_existing_attributes(set_groups, - instance_start_offsets, - attributes, - point_component, - bary_coords_all, - looptri_indices_all); - AttributeOutputs attribute_outputs; if (params.output_is_required("Normal")) { attribute_outputs.normal_id = StrongAnonymousAttributeID("normal"); @@ -707,14 +548,12 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par attribute_outputs.stable_id_id = StrongAnonymousAttributeID("stable id"); } - compute_attribute_outputs(set_groups, - instance_start_offsets, - point_component, - bary_coords_all, - looptri_indices_all, - attribute_outputs); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + point_distribution_calculate( + geometry_set, selection_field, method, seed, attribute_outputs, params); + }); - params.set_output("Points", std::move(geometry_set_out)); + params.set_output("Points", std::move(geometry_set)); if (attribute_outputs.normal_id) { params.set_output( From 11bfbc3337bc89f1476b0894823e3dc923e4e4b0 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 27 Sep 2021 11:21:33 -0500 Subject: [PATCH 0265/1500] Fix: Incorrect node socket name after recent refactor Caused by rBc99cb814520480379 --- source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index 75c72754af6..c9b26fa5199 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -29,7 +29,7 @@ static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b) { b.is_function_node(); b.add_input("Vector").min(0.0f).max(1.0f).hide_value(); - b.add_input("Vector"); + b.add_input("Center"); b.add_input("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); b.add_input("Angle").subtype(PROP_ANGLE); b.add_input("Rotation").subtype(PROP_EULER); From c76ccd85bed2878ab093165568d66e10e712ef69 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 27 Sep 2021 18:42:19 +0200 Subject: [PATCH 0266/1500] Cleanup: incorrect null check in asset library Found by clang tidy (P2439). --- source/blender/blenkernel/intern/asset_library.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index 6fed355c41b..8d38f2106c1 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -85,7 +85,7 @@ void AssetLibrary::on_save_post(struct Main *main, struct PointerRNA ** /*pointers*/, const int /*num_pointers*/) { - if (this->catalog_service) { + if (this->catalog_service == nullptr) { return; } From 847d355cab47bca3a2afa1c516dcd712c321b856 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 27 Sep 2021 18:45:49 +0200 Subject: [PATCH 0267/1500] File/Asset Browser: Don't deselect other items when dragging Basically this enables the select-tweaking behavior as per the guidelines: https://wiki.blender.org/wiki/Human_Interface_Guidelines/Selection#Select-tweaking. We use this in most other other editors that allow selecting and dragging multiple items. But besides the consistency improvement, this is important if we want to support dragging multiple assets (or files) in future. We want to support this at least for dragging multiple assets into an asset catalog for the upcoming asset catalog UI. --- source/blender/editors/space_file/file_ops.c | 27 +++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 2f1acd2ca4d..d0f2a4fdc4c 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -536,7 +536,7 @@ static rcti file_select_mval_to_select_rect(const int mval[2]) return rect; } -static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int file_select_exec(bContext *C, wmOperator *op) { ARegion *region = CTX_wm_region(C); SpaceFile *sfile = CTX_wm_space_file(C); @@ -549,17 +549,27 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool only_activate_if_selected = RNA_boolean_get(op->ptr, "only_activate_if_selected"); /* Used so right mouse clicks can do both, activate and spawn the context menu. */ const bool pass_through = RNA_boolean_get(op->ptr, "pass_through"); + bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); if (region->regiontype != RGN_TYPE_WINDOW) { return OPERATOR_CANCELLED; } - rect = file_select_mval_to_select_rect(event->mval); + int mval[2]; + mval[0] = RNA_int_get(op->ptr, "mouse_x"); + mval[1] = RNA_int_get(op->ptr, "mouse_y"); + rect = file_select_mval_to_select_rect(mval); if (!ED_fileselect_layout_is_inside_pt(sfile->layout, ®ion->v2d, rect.xmin, rect.ymin)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } + if (extend || fill) { + wait_to_deselect_others = false; + } + + int ret_val = OPERATOR_FINISHED; + const FileSelectParams *params = ED_fileselect_get_active_params(sfile); if (sfile && params) { int idx = params->highlight_file; @@ -571,6 +581,9 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (only_activate_if_selected && is_selected) { /* Don't deselect other items. */ } + else if (wait_to_deselect_others && is_selected) { + ret_val = OPERATOR_RUNNING_MODAL; + } /* single select, deselect all selected first */ else if (!extend) { file_select_deselect_all(sfile, FILE_SEL_SELECTED); @@ -601,7 +614,10 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) WM_event_add_mousemove(CTX_wm_window(C)); /* for directory changes */ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); - return pass_through ? (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH) : OPERATOR_FINISHED; + if ((ret_val == OPERATOR_FINISHED) && pass_through) { + ret_val |= OPERATOR_PASS_THROUGH; + } + return ret_val; } void FILE_OT_select(wmOperatorType *ot) @@ -614,11 +630,14 @@ void FILE_OT_select(wmOperatorType *ot) ot->description = "Handle mouse clicks to select and activate items"; /* api callbacks */ - ot->invoke = file_select_invoke; + ot->invoke = WM_generic_select_invoke; + ot->exec = file_select_exec; + ot->modal = WM_generic_select_modal; /* Operator works for file or asset browsing */ ot->poll = ED_operator_file_active; /* properties */ + WM_operator_properties_generic_select(ot); prop = RNA_def_boolean(ot->srna, "extend", false, From 8967bcb7555def99e0c9d3c59aedd36fb85548f4 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 27 Sep 2021 13:42:12 -0400 Subject: [PATCH 0268/1500] Update RNA to user manual url mappings --- .../scripts/modules/rna_manual_reference.py | 83 +++++++++++-------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 30cbcf4ea05..797eb2627b3 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -39,8 +39,8 @@ if LANG is not None: url_manual_prefix = url_manual_prefix.replace("manual/en", "manual/" + LANG) url_manual_mapping = ( - ("bpy.types.cyclesworldsettings.sample_mbpy.types.cyclesworldsettings.sample_map_resolutionbpy.types.cyclesworldsettings.sample_map_resolutionap_resolution*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-sample-mbpy-types-cyclesworldsettings-sample-map-resolutionbpy-types-cyclesworldsettings-sample-map-resolutionap-resolution"), ("bpy.types.movietrackingsettings.refine_intrinsics_tangential_distortion*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-tangential-distortion"), + ("bpy.types.spacesequesequencertimelineoverlaynceeditor.show_strip_offset*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequesequencertimelineoverlaynceeditor-show-strip-offset"), ("bpy.types.movietrackingsettings.refine_intrinsics_radial_distortion*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-radial-distortion"), ("bpy.types.fluiddomainsettings.sndparticle_potential_max_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-trappedair"), ("bpy.types.fluiddomainsettings.sndparticle_potential_min_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-trappedair"), @@ -60,11 +60,13 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"), ("bpy.types.rigidbodyconstraint.use_override_solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-override-solver-iterations"), ("bpy.types.toolsettings.use_transform_correct_face_attributes*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-face-attributes"), + ("bpy.types.cyclesrendersettings.preview_adaptive_min_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-adaptive-min-samples"), ("bpy.types.rendersettings.use_sequencer_override_scene_strip*", "video_editing/preview/sidebar.html#bpy-types-rendersettings-use-sequencer-override-scene-strip"), ("bpy.types.toolsettings.use_transform_correct_keep_connected*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-keep-connected"), + ("bpy.types.cyclesrendersettings.preview_denoising_prefilter*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoising-prefilter"), ("bpy.types.fluiddomainsettings.sndparticle_potential_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-radius"), ("bpy.types.cyclesrendersettings.film_transparent_roughness*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-roughness"), - ("bpy.types.cyclesrendersettings.sample_all_lights_indirect*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sample-all-lights-indirect"), + ("bpy.types.cyclesrendersettings.preview_adaptive_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-adaptive-threshold"), ("bpy.types.fluiddomainsettings.openvdb_cache_compress_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-cache-compress-type"), ("bpy.types.fluiddomainsettings.sndparticle_bubble_buoyancy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-buoyancy"), ("bpy.types.fluiddomainsettings.sndparticle_combined_export*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-combined-export"), @@ -76,14 +78,13 @@ url_manual_mapping = ( ("bpy.types.toolsettings.annotation_stroke_placement_view3d*", "interface/annotate_tool.html#bpy-types-toolsettings-annotation-stroke-placement-view3d"), ("bpy.types.fluiddomainsettings.use_collision_border_front*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-front"), ("bpy.types.fluiddomainsettings.use_collision_border_right*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-right"), + ("bpy.types.sequencertimelineoverlay.waveform_display_type*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-waveform-display-type"), ("bpy.types.cyclesmaterialsettings.use_transparent_shadow*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-use-transparent-shadow"), ("bpy.types.cyclesobjectsettings.shadow_terminator_offset*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-shadow-terminator-offset"), ("bpy.types.cyclesobjectsettings.use_adaptive_subdivision*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-use-adaptive-subdivision"), ("bpy.types.cyclesrendersettings.debug_use_spatial_splits*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-use-spatial-splits"), ("bpy.types.cyclesrendersettings.light_sampling_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-light-sampling-threshold"), - ("bpy.types.cyclesrendersettings.preview_start_resolution*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-preview-start-resolution"), ("bpy.types.cyclesrendersettings.rolling_shutter_duration*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-rolling-shutter-duration"), - ("bpy.types.cyclesrendersettings.sample_all_lights_direct*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sample-all-lights-direct"), ("bpy.types.cyclesrendersettings.volume_preview_step_rate*", "render/cycles/render_settings/volumes.html#bpy-types-cyclesrendersettings-volume-preview-step-rate"), ("bpy.types.fluiddomainsettings.sndparticle_update_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-update-radius"), ("bpy.types.fluiddomainsettings.use_collision_border_back*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-back"), @@ -97,14 +98,15 @@ url_manual_mapping = ( ("bpy.types.gpencilsculptsettings.use_multiframe_falloff*", "grease_pencil/multiframe.html#bpy-types-gpencilsculptsettings-use-multiframe-falloff"), ("bpy.types.movietrackingsettings.use_keyframe_selection*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-keyframe-selection"), ("bpy.types.rendersettings.simplify_gpencil_antialiasing*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-antialiasing"), + ("bpy.types.sequencertimelineoverlay.show_strip_duration*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-duration"), ("bpy.types.spaceoutliner.use_filter_lib_override_system*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override-system"), ("bpy.types.toolsettings.use_transform_pivot_point_align*", "scene_layout/object/tools/tool_settings.html#bpy-types-toolsettings-use-transform-pivot-point-align"), ("bpy.types.brush.show_multiplane_scrape_planes_preview*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-show-multiplane-scrape-planes-preview"), ("bpy.types.cyclesmaterialsettings.volume_interpolation*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-interpolation"), ("bpy.types.cyclesrendersettings.debug_optix_curves_api*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-optix-curves-api"), + ("bpy.types.cyclesrendersettings.denoising_input_passes*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoising-input-passes"), ("bpy.types.cyclesrendersettings.film_transparent_glass*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-glass"), ("bpy.types.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"), - ("bpy.types.cyclesrendersettings.use_progressive_refine*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-use-progressive-refine"), ("bpy.types.fluiddomainsettings.sndparticle_bubble_drag*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-drag"), ("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/view_layer/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"), ("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/view_layer/line_style/modifiers/geometry/sinus_displacement.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"), @@ -123,6 +125,7 @@ url_manual_mapping = ( ("bpy.types.gpencillayer.use_annotation_onion_skinning*", "interface/annotate_tool.html#bpy-types-gpencillayer-use-annotation-onion-skinning"), ("bpy.types.greasepencil.use_adaptive_curve_resolution*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-adaptive-curve-resolution"), ("bpy.types.linestylegeometrymodifier_polygonalization*", "render/freestyle/view_layer/line_style/modifiers/geometry/polygonization.html#bpy-types-linestylegeometrymodifier-polygonalization"), + ("bpy.types.sequencertimelineoverlay.show_strip_source*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-source"), ("bpy.types.toolsettings.use_gpencil_automerge_strokes*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-automerge-strokes"), ("bpy.types.toolsettings.use_proportional_edit_objects*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-edit-objects"), ("bpy.ops.view3d.edit_mesh_extrude_move_shrink_fatten*", "modeling/meshes/editing/face/extrude_faces_normal.html#bpy-ops-view3d-edit-mesh-extrude-move-shrink-fatten"), @@ -133,7 +136,7 @@ url_manual_mapping = ( ("bpy.types.cyclesrendersettings.motion_blur_position*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-motion-blur-position"), ("bpy.types.cyclesrendersettings.rolling_shutter_type*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-rolling-shutter-type"), ("bpy.types.cyclesrendersettings.transmission_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-transmission-bounces"), - ("bpy.types.cyclesrendersettings.transmission_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-transmission-samples"), + ("bpy.types.cyclesworldsettings.sample_map_resolution*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-sample-map-resolution"), ("bpy.types.fluiddomainsettings.display_interpolation*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-display-interpolation"), ("bpy.types.fluiddomainsettings.gridlines_cell_filter*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-cell-filter"), ("bpy.types.fluiddomainsettings.gridlines_color_field*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-color-field"), @@ -147,13 +150,13 @@ url_manual_mapping = ( ("bpy.types.rendersettings_simplify_gpencil_shader_fx*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-shader-fx"), ("bpy.types.rendersettings_simplify_gpencil_view_fill*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-fill"), ("bpy.types.sequencertoolsettings.snap_to_hold_offset*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-to-hold-offset"), - ("bpy.types.spacesequenceeditor.waveform_display_type*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-waveform-display-type"), ("bpy.types.toolsettings.use_mesh_automerge_and_split*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-mesh-automerge-and-split"), ("bpy.types.brush.cloth_constraint_softbody_strength*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-constraint-softbody-strength"), ("bpy.types.brush.elastic_deform_volume_preservation*", "sculpt_paint/sculpting/tools/elastic_deform.html#bpy-types-brush-elastic-deform-volume-preservation"), ("bpy.types.brushgpencilsettings.fill_simplify_level*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-simplify-level"), ("bpy.types.brushgpencilsettings.use_jitter_pressure*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-jitter-pressure"), ("bpy.types.brushgpencilsettings.use_settings_random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-random"), + ("bpy.types.cyclesrendersettings.denoising_prefilter*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoising-prefilter"), ("bpy.types.cyclesrendersettings.preview_dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-preview-dicing-rate"), ("bpy.types.cyclesrendersettings.sample_clamp_direct*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-sample-clamp-direct"), ("bpy.types.cyclesworldsettings.volume_interpolation*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-interpolation"), @@ -166,6 +169,7 @@ url_manual_mapping = ( ("bpy.types.freestylelineset.select_external_contour*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-external-contour"), ("bpy.types.linestylegeometrymodifier_simplification*", "render/freestyle/view_layer/line_style/modifiers/geometry/simplification.html#bpy-types-linestylegeometrymodifier-simplification"), ("bpy.types.materialgpencilstyle.use_overlap_strokes*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-overlap-strokes"), + ("bpy.types.sequencertimelineoverlay.show_strip_name*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-name"), ("bpy.types.spacespreadsheet.geometry_component_type*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-geometry-component-type"), ("bpy.types.toolsettings.use_gpencil_weight_data_add*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-weight-data-add"), ("bpy.types.view3doverlay.texture_paint_mode_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-texture-paint-mode-opacity"), @@ -180,10 +184,6 @@ url_manual_mapping = ( ("bpy.types.cyclesrendersettings.adaptive_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-adaptive-threshold"), ("bpy.types.cyclesrendersettings.camera_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-camera-cull-margin"), ("bpy.types.cyclesrendersettings.debug_use_hair_bvh*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-use-hair-bvh"), - ("bpy.types.cyclesrendersettings.mesh_light_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-mesh-light-samples"), - ("bpy.types.cyclesrendersettings.preview_aa_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-aa-samples"), - ("bpy.types.cyclesrendersettings.subsurface_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-subsurface-samples"), - ("bpy.types.cyclesrendersettings.use_square_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-square-samples"), ("bpy.types.fluiddomainsettings.export_manta_script*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-export-manta-script"), ("bpy.types.fluiddomainsettings.fractions_threshold*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-fractions-threshold"), ("bpy.types.fluiddomainsettings.particle_band_width*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-particle-band-width"), @@ -201,11 +201,12 @@ url_manual_mapping = ( ("bpy.types.movietrackingsettings.use_tripod_solver*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-tripod-solver"), ("bpy.types.rendersettings.simplify_child_particles*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-child-particles"), ("bpy.types.rendersettings.use_high_quality_normals*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-use-high-quality-normals"), + ("bpy.types.sequencerpreviewoverlay.show_annotation*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay-show-annotation"), + ("bpy.types.sequencerpreviewoverlay.show_safe_areas*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay-show-safe-areas"), ("bpy.types.sequencertoolsettings.snap_ignore_muted*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-muted"), ("bpy.types.sequencertoolsettings.snap_ignore_sound*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-sound"), ("bpy.types.spaceoutliner.use_filter_case_sensitive*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-case-sensitive"), ("bpy.types.spaceoutliner.use_filter_object_content*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-content"), - ("bpy.types.spacesequenceeditor.show_strip_duration*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-duration"), ("bpy.types.toolsettings.use_proportional_connected*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-connected"), ("bpy.types.toolsettings.use_proportional_projected*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-projected"), ("bpy.types.view3doverlay.vertex_paint_mode_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-vertex-paint-mode-opacity"), @@ -284,13 +285,13 @@ url_manual_mapping = ( ("bpy.types.materialgpencilstyle.use_fill_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-holdout"), ("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"), ("bpy.types.rigidbodyconstraint.solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-solver-iterations"), + ("bpy.types.sequencerpreviewoverlay.show_metadata*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay-show-metadata"), + ("bpy.types.sequencertimelineoverlay.show_fcurves*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-fcurves"), ("bpy.types.spaceclipeditor.use_grayscale_preview*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-grayscale-preview"), ("bpy.types.spaceoutliner.use_filter_lib_override*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override"), ("bpy.types.spaceoutliner.use_filter_object_empty*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-empty"), ("bpy.types.spaceoutliner.use_filter_object_light*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-light"), ("bpy.types.spacesequenceeditor.proxy_render_size*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"), - ("bpy.types.spacesequenceeditor.show_strip_offset*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-offset"), - ("bpy.types.spacesequenceeditor.show_strip_source*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-source"), ("bpy.types.spacespreadsheetrowfilter.column_name*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-column-name"), ("bpy.types.toolsettings.gpencil_stroke_placement*", "grease_pencil/modes/draw/stroke_placement.html#bpy-types-toolsettings-gpencil-stroke-placement"), ("bpy.types.toolsettings.use_keyframe_cycle_aware*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-cycle-aware"), @@ -301,7 +302,6 @@ url_manual_mapping = ( ("bpy.types.cyclesobjectsettings.use_camera_cull*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-camera-cull"), ("bpy.types.cyclesobjectsettings.use_motion_blur*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-motion-blur"), ("bpy.types.cyclesrendersettings.diffuse_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-diffuse-bounces"), - ("bpy.types.cyclesrendersettings.diffuse_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-diffuse-samples"), ("bpy.types.cyclesrendersettings.preview_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-samples"), ("bpy.types.cyclesrendersettings.use_camera_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-camera-cull"), ("bpy.types.cyclesworldsettings.volume_step_size*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-step-size"), @@ -340,9 +340,7 @@ url_manual_mapping = ( ("bpy.types.cyclesmaterialsettings.displacement*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-displacement"), ("bpy.types.cyclesrendersettings.debug_bvh_type*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-bvh-type"), ("bpy.types.cyclesrendersettings.glossy_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-glossy-bounces"), - ("bpy.types.cyclesrendersettings.glossy_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-glossy-samples"), ("bpy.types.cyclesrendersettings.volume_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-volume-bounces"), - ("bpy.types.cyclesrendersettings.volume_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-volume-samples"), ("bpy.types.cyclesworldsettings.sampling_method*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-sampling-method"), ("bpy.types.cyclesworldsettings.volume_sampling*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-sampling"), ("bpy.types.editbone.bbone_handle_use_scale_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-scale-end"), @@ -370,16 +368,12 @@ url_manual_mapping = ( ("bpy.types.spacegrapheditor.show_extrapolation*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-extrapolation"), ("bpy.types.spaceoutliner.use_filter_collection*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-collection"), ("bpy.types.spacesequenceeditor.display_channel*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-display-channel"), - ("bpy.types.spacesequenceeditor.show_annotation*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-annotation"), ("bpy.types.spacesequenceeditor.show_region_hud*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-region-hud"), - ("bpy.types.spacesequenceeditor.show_safe_areas*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-safe-areas"), - ("bpy.types.spacesequenceeditor.show_strip_name*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-name"), ("bpy.types.spacespreadsheet.show_only_selected*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-show-only-selected"), ("bpy.types.spacespreadsheetrowfilter.operation*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-operation"), ("bpy.types.spacespreadsheetrowfilter.threshold*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-threshold"), ("bpy.types.toolsettings.use_snap_grid_absolute*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-grid-absolute"), ("bpy.types.view3doverlay.show_face_orientation*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-face-orientation"), - ("bpy.types.worldlighting.use_ambient_occlusion*", "render/cycles/world_settings.html#bpy-types-worldlighting-use-ambient-occlusion"), ("bpy.ops.object.blenderkit_material_thumbnail*", "addons/3d_view/blenderkit.html#bpy-ops-object-blenderkit-material-thumbnail"), ("bpy.ops.object.multires_higher_levels_delete*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-higher-levels-delete"), ("bpy.ops.object.vertex_group_copy_to_selected*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-copy-to-selected"), @@ -460,9 +454,9 @@ url_manual_mapping = ( ("bpy.types.sculpt.constant_detail_resolution*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-constant-detail-resolution"), ("bpy.types.spaceclipeditor.annotation_source*", "movie_clip/tracking/clip/sidebar/view.html#bpy-types-spaceclipeditor-annotation-source"), ("bpy.types.spaceclipeditor.show_blue_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-blue-channel"), + ("bpy.types.spacefilebrowser.system_bookmarks*", "editors/file_browser.html#bpy-types-spacefilebrowser-system-bookmarks"), ("bpy.types.spaceoutliner.use_filter_children*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-children"), ("bpy.types.spaceoutliner.use_filter_complete*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-complete"), - ("bpy.types.spacesequenceeditor.show_metadata*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-metadata"), ("bpy.types.spacespreadsheet.attribute_domain*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-attribute-domain"), ("bpy.types.spacespreadsheetrowfilter.enabled*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-enabled"), ("bpy.types.spaceview3d.transform_orientation*", "editors/3dview/controls/orientation.html#bpy-types-spaceview3d-transform-orientation"), @@ -484,10 +478,10 @@ url_manual_mapping = ( ("bpy.types.cyclesrendersettings.blur_glossy*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-blur-glossy"), ("bpy.types.cyclesrendersettings.dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-rate"), ("bpy.types.cyclesrendersettings.max_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-max-bounces"), - ("bpy.types.cyclesrendersettings.progressive*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-progressive"), ("bpy.types.cyclesrendersettings.use_fast_gi*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-use-fast-gi"), ("bpy.types.editbone.bbone_custom_handle_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-custom-handle-end"), ("bpy.types.editbone.bbone_handle_type_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-type-start"), + ("bpy.types.fileselectparams.recursion_level*", "editors/file_browser.html#bpy-types-fileselectparams-recursion-level"), ("bpy.types.fluiddomainsettings.adapt_margin*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-adapt-margin"), ("bpy.types.fluiddomainsettings.burning_rate*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-burning-rate"), ("bpy.types.fluiddomainsettings.guide_parent*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-parent"), @@ -516,7 +510,6 @@ url_manual_mapping = ( ("bpy.types.spaceclipeditor.show_red_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-red-channel"), ("bpy.types.spaceclipeditor.use_mute_footage*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-mute-footage"), ("bpy.types.spacesequenceeditor.overlay_type*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-overlay-type"), - ("bpy.types.spacesequenceeditor.show_fcurves*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-fcurves"), ("bpy.types.spaceuveditor.sticky_select_mode*", "editors/uv/selecting.html#bpy-types-spaceuveditor-sticky-select-mode"), ("bpy.types.spaceview3d.show_object_viewport*", "editors/3dview/display/visibility.html#bpy-types-spaceview3d-show-object-viewport"), ("bpy.types.view3doverlay.show_fade_inactive*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-fade-inactive"), @@ -536,10 +529,8 @@ url_manual_mapping = ( ("bpy.types.brush.surface_smooth_iterations*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-iterations"), ("bpy.types.brushgpencilsettings.pen_jitter*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-jitter"), ("bpy.types.cyclescurverendersettings.shape*", "render/cycles/render_settings/hair.html#bpy-types-cyclescurverendersettings-shape"), - ("bpy.types.cyclesrendersettings.aa_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-aa-samples"), ("bpy.types.cyclesrendersettings.ao_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces"), - ("bpy.types.cyclesrendersettings.ao_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-ao-samples"), - ("bpy.types.cyclesrendersettings.tile_order*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-tile-order"), + ("bpy.types.cyclesrendersettings.time_limit*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-time-limit"), ("bpy.types.cyclesvisibilitysettings.camera*", "render/cycles/world_settings.html#bpy-types-cyclesvisibilitysettings-camera"), ("bpy.types.cyclesworldsettings.max_bounces*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-max-bounces"), ("bpy.types.fluiddomainsettings.domain_type*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-domain-type"), @@ -575,6 +566,8 @@ url_manual_mapping = ( ("bpy.types.rendersettings.use_single_layer*", "render/layers/view_layer.html#bpy-types-rendersettings-use-single-layer"), ("bpy.types.sceneeevee.use_taa_reprojection*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-use-taa-reprojection"), ("bpy.types.sequenceeditor.use_overlay_lock*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-overlay-lock"), + ("bpy.types.spacefilebrowser.recent_folders*", "editors/file_browser.html#bpy-types-spacefilebrowser-recent-folders"), + ("bpy.types.spacefilebrowser.system_folders*", "editors/file_browser.html#bpy-types-spacefilebrowser-system-folders"), ("bpy.types.spaceoutliner.use_filter_object*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object"), ("bpy.types.spacesequenceeditor.use_proxies*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-use-proxies"), ("bpy.types.spaceuveditor.show_pixel_coords*", "editors/uv/sidebar.html#bpy-types-spaceuveditor-show-pixel-coords"), @@ -606,6 +599,7 @@ url_manual_mapping = ( ("bpy.types.cyclescamerasettings.longitude*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-longitude"), ("bpy.types.editbone.bbone_handle_type_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-type-end"), ("bpy.types.editbone.use_endroll_as_inroll*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-use-endroll-as-inroll"), + ("bpy.types.fileselectparams.filter_search*", "editors/file_browser.html#bpy-types-fileselectparams-filter-search"), ("bpy.types.fluiddomainsettings.cache_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-type"), ("bpy.types.fluiddomainsettings.flip_ratio*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-flip-ratio"), ("bpy.types.fluiddomainsettings.guide_beta*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-beta"), @@ -674,6 +668,8 @@ url_manual_mapping = ( ("bpy.types.cyclesrendersettings.caustics*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-caustics"), ("bpy.types.cyclesrendersettings.denoiser*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoiser"), ("bpy.types.editbone.use_inherit_rotation*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-inherit-rotation"), + ("bpy.types.fileselectparams.display_size*", "editors/file_browser.html#bpy-types-fileselectparams-display-size"), + ("bpy.types.fileselectparams.display_type*", "editors/file_browser.html#bpy-types-fileselectparams-display-type"), ("bpy.types.fluiddomainsettings.use_guide*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-use-guide"), ("bpy.types.fluiddomainsettings.use_noise*", "physics/fluid/type/domain/gas/noise.html#bpy-types-fluiddomainsettings-use-noise"), ("bpy.types.fluiddomainsettings.use_slice*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-use-slice"), @@ -736,6 +732,8 @@ url_manual_mapping = ( ("bpy.types.compositornodebrightcontrast*", "compositing/types/color/bright_contrast.html#bpy-types-compositornodebrightcontrast"), ("bpy.types.compositornodedoubleedgemask*", "compositing/types/matte/double_edge_mask.html#bpy-types-compositornodedoubleedgemask"), ("bpy.types.cyclesrendersettings.samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-samples"), + ("bpy.types.fileselectparams.show_hidden*", "editors/file_browser.html#bpy-types-fileselectparams-show-hidden"), + ("bpy.types.fileselectparams.sort_method*", "editors/file_browser.html#bpy-types-fileselectparams-sort-method"), ("bpy.types.fluiddomainsettings.clipping*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-clipping"), ("bpy.types.fluiddomainsettings.use_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-use-mesh"), ("bpy.types.freestylelinestyle.angle_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-angle-max"), @@ -752,6 +750,7 @@ url_manual_mapping = ( ("bpy.types.object.use_empty_image_alpha*", "modeling/empties.html#bpy-types-object-use-empty-image-alpha"), ("bpy.types.rendersettings.frame_map_new*", "render/output/properties/frame_range.html#bpy-types-rendersettings-frame-map-new"), ("bpy.types.rendersettings.frame_map_old*", "render/output/properties/frame_range.html#bpy-types-rendersettings-frame-map-old"), + ("bpy.types.rendersettings.use_auto_tile*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-auto-tile"), ("bpy.types.rendersettings.use_overwrite*", "render/output/properties/output.html#bpy-types-rendersettings-use-overwrite"), ("bpy.types.rendersettings.use_sequencer*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-sequencer"), ("bpy.types.sceneeevee.volumetric_shadow*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric-shadow"), @@ -797,6 +796,7 @@ url_manual_mapping = ( ("bpy.types.compositornodesetalpha.mode*", "compositing/types/converter/set_alpha.html#bpy-types-compositornodesetalpha-mode"), ("bpy.types.dopesheet.use_filter_invert*", "editors/graph_editor/channels.html#bpy-types-dopesheet-use-filter-invert"), ("bpy.types.editbone.use_local_location*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-local-location"), + ("bpy.types.fileselectparams.use_filter*", "editors/file_browser.html#bpy-types-fileselectparams-use-filter"), ("bpy.types.fluiddomainsettings.gravity*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-gravity"), ("bpy.types.fluidflowsettings.flow_type*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-flow-type"), ("bpy.types.fluidflowsettings.subframes*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-subframes"), @@ -868,6 +868,7 @@ url_manual_mapping = ( ("bpy.types.compositornodecolorbalance*", "compositing/types/color/color_balance.html#bpy-types-compositornodecolorbalance"), ("bpy.types.compositornodekeyingscreen*", "compositing/types/matte/keying_screen.html#bpy-types-compositornodekeyingscreen"), ("bpy.types.dynamicpaintcanvassettings*", "physics/dynamic_paint/canvas.html#bpy-types-dynamicpaintcanvassettings"), + ("bpy.types.fileselectparams.directory*", "editors/file_browser.html#bpy-types-fileselectparams-directory"), ("bpy.types.fluidflowsettings.use_flow*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-flow"), ("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierfunctiongenerator"), ("bpy.types.geometrynodeattributeclamp*", "modeling/geometry_nodes/attribute/attribute_clamp.html#bpy-types-geometrynodeattributeclamp"), @@ -891,6 +892,7 @@ url_manual_mapping = ( ("bpy.types.shadernodeambientocclusion*", "render/shader_nodes/input/ao.html#bpy-types-shadernodeambientocclusion"), ("bpy.types.shadernodevolumeabsorption*", "render/shader_nodes/shader/volume_absorption.html#bpy-types-shadernodevolumeabsorption"), ("bpy.types.shadernodevolumeprincipled*", "render/shader_nodes/shader/volume_principled.html#bpy-types-shadernodevolumeprincipled"), + ("bpy.types.spacefilebrowser.bookmarks*", "editors/file_browser.html#bpy-types-spacefilebrowser-bookmarks"), ("bpy.types.spaceoutliner.display_mode*", "editors/outliner/interface.html#bpy-types-spaceoutliner-display-mode"), ("bpy.types.spaceoutliner.filter_state*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-state"), ("bpy.types.toolsettings.keyframe_type*", "editors/timeline.html#bpy-types-toolsettings-keyframe-type"), @@ -940,6 +942,7 @@ url_manual_mapping = ( ("bpy.types.cyclesrendersettings.seed*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-seed"), ("bpy.types.dynamicpaintbrushsettings*", "physics/dynamic_paint/brush.html#bpy-types-dynamicpaintbrushsettings"), ("bpy.types.editbone.use_scale_easing*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-use-scale-easing"), + ("bpy.types.fileselectparams.filename*", "editors/file_browser.html#bpy-types-fileselectparams-filename"), ("bpy.types.fluiddomainsettings.alpha*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-alpha"), ("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"), ("bpy.types.freestylelineset.qi_start*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-qi-start"), @@ -1048,6 +1051,8 @@ url_manual_mapping = ( ("bpy.types.nodesocketinterface.name*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-name"), ("bpy.types.object.is_shadow_catcher*", "render/cycles/object_settings/object_data.html#bpy-types-object-is-shadow-catcher"), ("bpy.types.particleinstancemodifier*", "modeling/modifiers/physics/particle_instance.html#bpy-types-particleinstancemodifier"), + ("bpy.types.rendersettings.tile_size*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-size"), + ("bpy.types.sequencertimelineoverlay*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay"), ("bpy.types.sequencetransform.offset*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-offset"), ("bpy.types.shadernodebrightcontrast*", "render/shader_nodes/color/bright_contrast.html#bpy-types-shadernodebrightcontrast"), ("bpy.types.shadernodebsdfprincipled*", "render/shader_nodes/shader/principled.html#bpy-types-shadernodebsdfprincipled"), @@ -1143,6 +1148,7 @@ url_manual_mapping = ( ("bpy.types.rendersettings.fps_base*", "render/output/properties/format.html#bpy-types-rendersettings-fps-base"), ("bpy.types.rigidbodyobject.enabled*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-enabled"), ("bpy.types.sceneeevee.use_overscan*", "render/eevee/render_settings/film.html#bpy-types-sceneeevee-use-overscan"), + ("bpy.types.sequencerpreviewoverlay*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay"), ("bpy.types.sequencetransform.scale*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-scale"), ("bpy.types.shadernodeeeveespecular*", "render/shader_nodes/shader/specular_bsdf.html#bpy-types-shadernodeeeveespecular"), ("bpy.types.shadernodehuesaturation*", "render/shader_nodes/color/hue_saturation.html#bpy-types-shadernodehuesaturation"), @@ -1153,7 +1159,7 @@ url_manual_mapping = ( ("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"), ("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer/freestyle.html#bpy-types-viewlayer-use-freestyle"), ("bpy.types.volumedisplay.use_slice*", "modeling/volumes/properties.html#bpy-types-volumedisplay-use-slice"), - ("bpy.types.worldlighting.ao_factor*", "render/cycles/world_settings.html#bpy-types-worldlighting-ao-factor"), + ("bpy.types.worldlighting.ao_factor*", "render/cycles/render_settings/light_paths.html#bpy-types-worldlighting-ao-factor"), ("bpy.types.worldmistsettings.depth*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-depth"), ("bpy.types.worldmistsettings.start*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-start"), ("bpy.ops.armature.armature_layers*", "animation/armatures/bones/editing/change_layers.html#bpy-ops-armature-armature-layers"), @@ -1255,11 +1261,11 @@ url_manual_mapping = ( ("bpy.types.shadernodevectorrotate*", "render/shader_nodes/vector/vector_rotate.html#bpy-types-shadernodevectorrotate"), ("bpy.types.sound.use_memory_cache*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sound-use-memory-cache"), ("bpy.types.spaceview3d.show_gizmo*", "editors/3dview/display/gizmo.html#bpy-types-spaceview3d-show-gizmo"), - ("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/color/texture_mapping.html#bpy-types-texturegpencilmodifier"), + ("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/modify/texture_mapping.html#bpy-types-texturegpencilmodifier"), ("bpy.types.volumedisplacemodifier*", "modeling/modifiers/deform/volume_displace.html#bpy-types-volumedisplacemodifier"), ("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"), ("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"), - ("bpy.types.worldlighting.distance*", "render/cycles/world_settings.html#bpy-types-worldlighting-distance"), + ("bpy.types.worldlighting.distance*", "render/cycles/render_settings/light_paths.html#bpy-types-worldlighting-distance"), ("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"), ("bpy.ops.armature.calculate_roll*", "animation/armatures/bones/editing/bone_roll.html#bpy-ops-armature-calculate-roll"), ("bpy.ops.armature.duplicate_move*", "animation/armatures/bones/editing/duplicate.html#bpy-ops-armature-duplicate-move"), @@ -1360,8 +1366,6 @@ url_manual_mapping = ( ("bpy.types.object.visible_shadow*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-shadow"), ("bpy.types.offsetgpencilmodifier*", "grease_pencil/modifiers/deform/offset.html#bpy-types-offsetgpencilmodifier"), ("bpy.types.posebone.custom_shape*", "animation/armatures/bones/properties/display.html#bpy-types-posebone-custom-shape"), - ("bpy.types.rendersettings.tile_x*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-x"), - ("bpy.types.rendersettings.tile_y*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-y"), ("bpy.types.rigifyselectioncolors*", "addons/rigging/rigify/metarigs.html#bpy-types-rigifyselectioncolors"), ("bpy.types.sceneeevee.volumetric*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric"), ("bpy.types.screen.show_statusbar*", "interface/window_system/topbar.html#bpy-types-screen-show-statusbar"), @@ -1573,7 +1577,7 @@ url_manual_mapping = ( ("bpy.types.stretchtoconstraint*", "animation/constraints/tracking/stretch_to.html#bpy-types-stretchtoconstraint"), ("bpy.types.texturenodecurvergb*", "editors/texture_node/types/color/rgb_curves.html#bpy-types-texturenodecurvergb"), ("bpy.types.texturenodevaltorgb*", "editors/texture_node/types/converter/rgb_to_bw.html#bpy-types-texturenodevaltorgb"), - ("bpy.types.timegpencilmodifier*", "grease_pencil/modifiers/deform/time_offset.html#bpy-types-timegpencilmodifier"), + ("bpy.types.timegpencilmodifier*", "grease_pencil/modifiers/modify/time_offset.html#bpy-types-timegpencilmodifier"), ("bpy.types.tintgpencilmodifier*", "grease_pencil/modifiers/color/tint.html#bpy-types-tintgpencilmodifier"), ("bpy.types.transformconstraint*", "animation/constraints/transform/transformation.html#bpy-types-transformconstraint"), ("bpy.types.triangulatemodifier*", "modeling/modifiers/generate/triangulate.html#bpy-types-triangulatemodifier"), @@ -1652,6 +1656,7 @@ url_manual_mapping = ( ("bpy.types.curve.twist_smooth*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-smooth"), ("bpy.types.curvepaintsettings*", "modeling/curves/tools/draw.html#bpy-types-curvepaintsettings"), ("bpy.types.fcurve.array_index*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-array-index"), + ("bpy.types.fileselectidfilter*", "editors/file_browser.html#bpy-types-fileselectidfilter"), ("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"), ("bpy.types.freestylelinestyle*", "render/freestyle/view_layer/line_style/index.html#bpy-types-freestylelinestyle"), ("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/gamma_cross.html#bpy-types-gammacrosssequence"), @@ -1820,6 +1825,7 @@ url_manual_mapping = ( ("bpy.ops.clip.track_markers*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-track-markers"), ("bpy.ops.curve.extrude_move*", "modeling/curves/editing/control_points.html#bpy-ops-curve-extrude-move"), ("bpy.ops.curve.make_segment*", "modeling/curves/editing/control_points.html#bpy-ops-curve-make-segment"), + ("bpy.ops.file.directory_new*", "editors/file_browser.html#bpy-ops-file-directory-new"), ("bpy.ops.graph.euler_filter*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-euler-filter"), ("bpy.ops.marker.camera_bind*", "animation/markers.html#bpy-ops-marker-camera-bind"), ("bpy.ops.mask.select_circle*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-circle"), @@ -1874,6 +1880,7 @@ url_manual_mapping = ( ("bpy.types.editbone.bbone_x*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-x"), ("bpy.types.editbone.bbone_z*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-z"), ("bpy.types.fcurve.data_path*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-data-path"), + ("bpy.types.fileselectparams*", "editors/file_browser.html#bpy-types-fileselectparams"), ("bpy.types.fmodifierstepped*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierstepped"), ("bpy.types.freestylelineset*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset"), ("bpy.types.mask.frame_start*", "movie_clip/masking/sidebar.html#bpy-types-mask-frame-start"), @@ -1904,6 +1911,7 @@ url_manual_mapping = ( ("bpy.types.softbodymodifier*", "physics/soft_body/index.html#bpy-types-softbodymodifier"), ("bpy.types.softbodysettings*", "physics/soft_body/settings/index.html#bpy-types-softbodysettings"), ("bpy.types.solidifymodifier*", "modeling/modifiers/generate/solidify.html#bpy-types-solidifymodifier"), + ("bpy.types.spacefilebrowser*", "editors/file_browser.html#bpy-types-spacefilebrowser"), ("bpy.types.spacegrapheditor*", "editors/graph_editor/index.html#bpy-types-spacegrapheditor"), ("bpy.types.spacepreferences*", "editors/preferences/index.html#bpy-types-spacepreferences"), ("bpy.types.spacespreadsheet*", "editors/spreadsheet.html#bpy-types-spacespreadsheet"), @@ -1923,6 +1931,7 @@ url_manual_mapping = ( ("bpy.ops.clip.solve_camera*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-solve-camera"), ("bpy.ops.constraint.delete*", "animation/constraints/interface/header.html#bpy-ops-constraint-delete"), ("bpy.ops.curve.smooth_tilt*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-tilt"), + ("bpy.ops.file.reset_recent*", "editors/file_browser.html#bpy-ops-file-reset-recent"), ("bpy.ops.fluid.bake_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-bake-guides"), ("bpy.ops.fluid.free_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-free-guides"), ("bpy.ops.font.style_toggle*", "modeling/texts/editing.html#bpy-ops-font-style-toggle"), @@ -1944,6 +1953,7 @@ url_manual_mapping = ( ("bpy.ops.object.origin_set*", "scene_layout/object/origin.html#bpy-ops-object-origin-set"), ("bpy.ops.object.parent_set*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-set"), ("bpy.ops.object.pointcloud*", "modeling/point_cloud.html#bpy-ops-object-pointcloud"), + ("bpy.ops.object.proxy_make*", "files/linked_libraries/library_proxies.html#bpy-ops-object-proxy-make"), ("bpy.ops.object.select_all*", "scene_layout/object/selecting.html#bpy-ops-object-select-all"), ("bpy.ops.object.shade_flat*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-flat"), ("bpy.ops.pose.group_assign*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-assign"), @@ -2251,6 +2261,7 @@ url_manual_mapping = ( ("bpy.ops.clip.prefetch*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-prefetch"), ("bpy.ops.clip.set_axis*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-axis"), ("bpy.ops.file.pack_all*", "files/blend/packed_data.html#bpy-ops-file-pack-all"), + ("bpy.ops.file.previous*", "editors/file_browser.html#bpy-ops-file-previous"), ("bpy.ops.gpencil.paste*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-paste"), ("bpy.ops.image.project*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-ops-image-project"), ("bpy.ops.image.replace*", "editors/image/editing.html#bpy-ops-image-replace"), @@ -2299,6 +2310,8 @@ url_manual_mapping = ( ("bpy.ops.curve.delete*", "modeling/curves/editing/curve.html#bpy-ops-curve-delete"), ("bpy.ops.curve.reveal*", "modeling/curves/editing/curve.html#bpy-ops-curve-reveal"), ("bpy.ops.curve.smooth*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth"), + ("bpy.ops.file.execute*", "editors/file_browser.html#bpy-ops-file-execute"), + ("bpy.ops.file.refresh*", "editors/file_browser.html#bpy-ops-file-refresh"), ("bpy.ops.fluid.preset*", "physics/fluid/type/domain/liquid/diffusion.html#bpy-ops-fluid-preset"), ("bpy.ops.gpencil.copy*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-copy"), ("bpy.ops.graph.delete*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-delete"), @@ -2340,6 +2353,8 @@ url_manual_mapping = ( ("bpy.types.vectorfont*", "modeling/texts/index.html#bpy-types-vectorfont"), ("bpy.ops.clip.reload*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-reload"), ("bpy.ops.curve.split*", "modeling/curves/editing/curve.html#bpy-ops-curve-split"), + ("bpy.ops.file.cancel*", "editors/file_browser.html#bpy-ops-file-cancel"), + ("bpy.ops.file.parent*", "editors/file_browser.html#bpy-ops-file-parent"), ("bpy.ops.graph.clean*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-clean"), ("bpy.ops.graph.paste*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-paste"), ("bpy.ops.mesh.bisect*", "modeling/meshes/editing/mesh/bisect.html#bpy-ops-mesh-bisect"), @@ -2419,6 +2434,7 @@ url_manual_mapping = ( ("bpy.types.spacenla*", "editors/nla/index.html#bpy-types-spacenla"), ("bpy.types.sunlight*", "render/lights/light_object.html#bpy-types-sunlight"), ("bpy.ops.clip.open*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-open"), + ("bpy.ops.file.next*", "editors/file_browser.html#bpy-ops-file-next"), ("bpy.ops.image.new*", "editors/image/editing.html#bpy-ops-image-new"), ("bpy.ops.mesh.fill*", "modeling/meshes/editing/face/fill.html#bpy-ops-mesh-fill"), ("bpy.ops.mesh.poke*", "modeling/meshes/editing/face/poke_faces.html#bpy-ops-mesh-poke"), @@ -2529,6 +2545,7 @@ url_manual_mapping = ( ("bpy.ops.anim*", "animation/index.html#bpy-ops-anim"), ("bpy.ops.boid*", "physics/particles/emitter/physics/boids.html#bpy-ops-boid"), ("bpy.ops.clip*", "movie_clip/index.html#bpy-ops-clip"), + ("bpy.ops.file*", "editors/file_browser.html#bpy-ops-file"), ("bpy.ops.font*", "modeling/texts/index.html#bpy-ops-font"), ("bpy.ops.mask*", "movie_clip/masking/index.html#bpy-ops-mask"), ("bpy.ops.mesh*", "modeling/meshes/index.html#bpy-ops-mesh"), From 8da23fd5aaa46354005b6caff83285cd61421c68 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 26 Sep 2021 12:50:25 +0300 Subject: [PATCH 0269/1500] Constraints: change default Stretch To rotation type to Swing. As also explained in D6134, in most case of Stretch To usage in rigs, it is desirable to use swing rotation, either via the old method of pairing the constraint with Damped Track, or via the Swing rotation type introduced in 2.82. This is for instance true for all usages of the constraint in Rigify. The reason can be understood by realizing that unlike order- dependent euler rotations, swing is not biased to an axis, and isn't affected by gimbal lock effects at merely 90 degrees of rotation (it has only one singularity at 180 degrees). Thus it makes sense to change the default for newly created constraints to the Swing mode. This has no backward compatibility concerns except for old tutorials and rig generation scripts. Differential Revision: https://developer.blender.org/D12643 --- source/blender/blenkernel/intern/constraint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index b9b15eba6a4..b2b03d28483 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -3499,7 +3499,7 @@ static void stretchto_new_data(void *cdata) bStretchToConstraint *data = (bStretchToConstraint *)cdata; data->volmode = 0; - data->plane = 0; + data->plane = SWING_Y; data->orglength = 0.0; data->bulge = 1.0; data->bulge_max = 1.0f; From 50b7253257302a7bcae179962456ff4cd01576d0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 27 Sep 2021 19:46:51 +0200 Subject: [PATCH 0270/1500] Cleanup: fix (harmless) uninitialized variable usage --- intern/cycles/blender/blender_session.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index c4a3f99ec2d..8d6db30f1a8 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -71,7 +71,8 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine, width(0), height(0), preview_osl(preview_osl), - python_thread_state(NULL) + python_thread_state(NULL), + use_developer_ui(false) { /* offline render */ background = true; From e6aabcae143299893aeacc00ec0c865fc72e9dcf Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 27 Sep 2021 18:52:09 +0200 Subject: [PATCH 0271/1500] Fix part of T91516: Cycles not rendering geometry nodes instances Part of the fix is by Jacques. This fixes the most obvious case, but it's still not clear how to deal with non-mesh geometry instances or how to handle motion blur for such instances. --- intern/cycles/blender/blender_geometry.cpp | 6 +++-- intern/cycles/blender/blender_util.h | 29 +++++++++++----------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp index fca8cb9eda3..7b49bb7fbb7 100644 --- a/intern/cycles/blender/blender_geometry.cpp +++ b/intern/cycles/blender/blender_geometry.cpp @@ -80,8 +80,10 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, { /* Test if we can instance or if the object is modified. */ Geometry::Type geom_type = determine_geom_type(b_ob_info, use_particle_hair); - BL::ID b_key_id = (BKE_object_is_modified(b_ob_info.real_object)) ? b_ob_info.real_object : - b_ob_info.object_data; + BL::ID b_key_id = (b_ob_info.is_real_object_data() && + BKE_object_is_modified(b_ob_info.real_object)) ? + b_ob_info.real_object : + b_ob_info.object_data; GeometryKey key(b_key_id.ptr.data, geom_type); /* Find shader indices. */ diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 04008d77d89..128fcbd7055 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -90,26 +90,27 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/, } #endif - BL::Mesh mesh(PointerRNA_NULL); - if (b_ob_info.object_data.is_a(&RNA_Mesh)) { - /* TODO: calc_undeformed is not used. */ - mesh = BL::Mesh(b_ob_info.object_data); + BL::Mesh mesh = (b_ob_info.object_data.is_a(&RNA_Mesh)) ? BL::Mesh(b_ob_info.object_data) : + BL::Mesh(PointerRNA_NULL); - /* Make a copy to split faces if we use autosmooth, otherwise not needed. - * Also in edit mode do we need to make a copy, to ensure data layers like - * UV are not empty. */ - if (mesh.is_editmode() || - (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) { + if (b_ob_info.is_real_object_data()) { + if (mesh) { + /* Make a copy to split faces if we use autosmooth, otherwise not needed. + * Also in edit mode do we need to make a copy, to ensure data layers like + * UV are not empty. */ + if (mesh.is_editmode() || + (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) { + BL::Depsgraph depsgraph(PointerRNA_NULL); + mesh = b_ob_info.real_object.to_mesh(false, depsgraph); + } + } + else { BL::Depsgraph depsgraph(PointerRNA_NULL); - assert(b_ob_info.is_real_object_data()); mesh = b_ob_info.real_object.to_mesh(false, depsgraph); } } else { - BL::Depsgraph depsgraph(PointerRNA_NULL); - if (b_ob_info.is_real_object_data()) { - mesh = b_ob_info.real_object.to_mesh(false, depsgraph); - } + /* TODO: what to do about non-mesh geometry instances? */ } #if 0 From 5d70a4d7ee4e31de7784acc9dc0637e39c949583 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 27 Sep 2021 13:04:58 -0500 Subject: [PATCH 0272/1500] Geometry Nodes: Move output attribute names to a subpanel In a sub-panel it will be clearer that they are outputs, since they just look like more inputs now. Unfortunately it is not possible to make sub-panels display conditionally currently, so the output sub-panel will always be visible whether or not it is empty. Differential Revision: https://developer.blender.org/D12653 --- source/blender/modifiers/intern/MOD_nodes.cc | 32 ++++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index c39beb63eb3..6e930e391d0 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1205,11 +1205,6 @@ static void panel_draw(const bContext *C, Panel *panel) LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) { draw_property_for_input_socket(layout, nmd, &bmain_ptr, ptr, *socket); } - LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { - if (socket_type_has_attribute_toggle(*socket)) { - draw_property_for_output_socket(layout, ptr, *socket); - } - } } /* Draw node warnings. */ @@ -1239,9 +1234,34 @@ static void panel_draw(const bContext *C, Panel *panel) modifier_panel_end(layout, ptr); } +static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + NodesModifierData *nmd = static_cast(ptr->data); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, true); + + if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { + LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { + if (socket_type_has_attribute_toggle(*socket)) { + draw_property_for_output_socket(layout, ptr, *socket); + } + } + } +} + static void panelRegister(ARegionType *region_type) { - modifier_panel_register(region_type, eModifierType_Nodes, panel_draw); + PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Nodes, panel_draw); + modifier_subpanel_register(region_type, + "output_attributes", + N_("Output Attributes"), + nullptr, + output_attribute_panel_draw, + panel_type); } static void blendWrite(BlendWriter *writer, const ModifierData *md) From f94164d89629f0d2ce8c3ef76db72541687b5e9c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 27 Sep 2021 13:22:44 -0500 Subject: [PATCH 0273/1500] Geometry Nodes: Do not realize instances in the material assign node Only run the node once for every unique geometry set in the input's instance heirarchy. This can massively improve performance when there are many instances, but it will mean that the result is the same for every instance. For the previous behavior, a "Realize Instances" node can be used before this one. This node can be changed without versioning since the old material assign node was already deprecated and replaced. --- .../nodes/node_geo_material_assign.cc | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc index 2a4481d5404..780994996ae 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -64,23 +64,22 @@ static void geo_node_material_assign_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + Mesh *mesh = mesh_component.get_for_write(); + if (mesh != nullptr) { + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; - if (geometry_set.has()) { - MeshComponent &mesh_component = geometry_set.get_component_for_write(); - Mesh *mesh = mesh_component.get_for_write(); - if (mesh != nullptr) { + fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; - - fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly}; - selection_evaluator.add(selection_field); - selection_evaluator.evaluate(); - const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - - assign_material_to_faces(*mesh, selection, material); + assign_material_to_faces(*mesh, selection, material); + } } - } + }); params.set_output("Geometry", std::move(geometry_set)); } From c53ffda8a496989c4e523085ed5440e89b59001c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 27 Sep 2021 13:29:53 -0500 Subject: [PATCH 0274/1500] Cleanup: Fix incorrect comments --- source/blender/blenloader/intern/versioning_300.c | 2 +- source/blender/modifiers/intern/MOD_nodes.cc | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index aceba82a657..b86f98af4f1 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1484,7 +1484,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Deprecate the random float node in favor of the random float node. */ + /* Deprecate the random float node in favor of the random value node. */ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type != NTREE_GEOMETRY) { continue; diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 6e930e391d0..d5dcf88b914 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -877,8 +877,6 @@ static void store_output_value_in_geometry(GeometrySet &geometry_set, /** * Evaluate a node group to compute the output geometry. - * Currently, this uses a fairly basic and inefficient algorithm that might compute things more - * often than necessary. It's going to be replaced soon. */ static GeometrySet compute_geometry(const DerivedNodeTree &tree, Span group_input_nodes, From 986d60490c0694941e27c070780c55f07b7b4842 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Mon, 27 Sep 2021 21:00:17 -0700 Subject: [PATCH 0275/1500] Asset Browser: Allow World assets to be drag/dropped onto the viewport While World data has always been able to be marked as an asset, there was no way to actually use them from the asset browser. This change allows users to drag-drop world assets onto the Viewport and have them appended/linked to their scene. Differential Revision: https://developer.blender.org/D12566 --- .../editors/space_view3d/space_view3d.c | 20 ++++++- .../editors/space_view3d/view3d_edit.c | 54 +++++++++++++++++++ .../editors/space_view3d/view3d_intern.h | 1 + .../blender/editors/space_view3d/view3d_ops.c | 1 + 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 72d0c11e192..4bee9633ece 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -539,6 +539,11 @@ static char *view3d_mat_drop_tooltip(bContext *C, return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event); } +static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + return view3d_drop_id_in_main_region_poll(C, drag, event, ID_WO); +} + static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ID_Type id_type = view3d_drop_id_in_main_region_poll_get_id_type(C, drag, event); @@ -732,6 +737,12 @@ static void view3d_dropboxes(void) view3d_id_drop_copy_with_type, WM_drag_free_imported_drag_ID, view3d_object_data_drop_tooltip); + WM_dropbox_add(lb, + "VIEW3D_OT_drop_world", + view3d_world_drop_poll, + view3d_id_drop_copy, + WM_drag_free_imported_drag_ID, + NULL); } static void view3d_widgets(void) @@ -1555,11 +1566,16 @@ static void space_view3d_listener(const wmSpaceTypeListenerParams *params) switch (wmn->category) { case NC_SCENE: switch (wmn->data) { - case ND_WORLD: - if (v3d->flag2 & V3D_HIDE_OVERLAYS) { + case ND_WORLD: { + const bool use_scene_world = ((v3d->shading.type == OB_MATERIAL) && + (v3d->shading.flag & V3D_SHADING_SCENE_WORLD)) || + ((v3d->shading.type == OB_RENDER) && + (v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)); + if (v3d->flag2 & V3D_HIDE_OVERLAYS || use_scene_world) { ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW); } break; + } } break; case NC_WORLD: diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index d917674194a..15ccf5891d4 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -34,6 +34,7 @@ #include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_world_types.h" #include "MEM_guardedalloc.h" @@ -4873,6 +4874,59 @@ void VIEW3D_OT_background_image_remove(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Drop World Operator + * \{ */ + +static int drop_world_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + char name[MAX_ID_NAME - 2]; + + RNA_string_get(op->ptr, "name", name); + World *world = (World *)BKE_libblock_find_name(bmain, ID_WO, name); + if (world == NULL) { + return OPERATOR_CANCELLED; + } + + id_us_plus(&world->id); + scene->world = world; + + DEG_id_tag_update(&scene->id, 0); + DEG_relations_tag_update(bmain); + + WM_event_add_notifier(C, NC_SCENE | ND_WORLD, scene); + + return OPERATOR_FINISHED; +} + +static bool drop_world_poll(bContext *C) +{ + return ED_operator_scene_editable(C); +} + +void VIEW3D_OT_drop_world(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Drop World"; + ot->description = "Drop a world into the scene"; + ot->idname = "VIEW3D_OT_drop_world"; + + /* api callbacks */ + ot->exec = drop_world_exec; + ot->poll = drop_world_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; + + /* properties */ + RNA_def_string(ot->srna, "name", "World", MAX_ID_NAME - 2, "Name", "World to assign"); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name View Clipping Planes Operator * diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index ab80928e0c1..a21fc006b02 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -80,6 +80,7 @@ void VIEW3D_OT_view_persportho(struct wmOperatorType *ot); void VIEW3D_OT_navigate(struct wmOperatorType *ot); void VIEW3D_OT_background_image_add(struct wmOperatorType *ot); void VIEW3D_OT_background_image_remove(struct wmOperatorType *ot); +void VIEW3D_OT_drop_world(struct wmOperatorType *ot); void VIEW3D_OT_view_orbit(struct wmOperatorType *ot); void VIEW3D_OT_view_roll(struct wmOperatorType *ot); void VIEW3D_OT_clip_border(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index 56dedbbdbb2..eb8c043319c 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -169,6 +169,7 @@ void view3d_operatortypes(void) WM_operatortype_append(VIEW3D_OT_view_persportho); WM_operatortype_append(VIEW3D_OT_background_image_add); WM_operatortype_append(VIEW3D_OT_background_image_remove); + WM_operatortype_append(VIEW3D_OT_drop_world); WM_operatortype_append(VIEW3D_OT_view_selected); WM_operatortype_append(VIEW3D_OT_view_lock_clear); WM_operatortype_append(VIEW3D_OT_view_lock_to_active); From c7d94a7827a5be9343eea22a9638bb059f185206 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 28 Sep 2021 14:07:59 +1000 Subject: [PATCH 0276/1500] UI: avoid excessive padding for labels in headers Labels in headers reserved space for an icon even when no icon was used. This is caused by the shared function ui_text_icon_width adding 1.5x a buttons X-units width the the width of the string. Menu buttons detected this and subtracted the extra padding. Instead of adding the same workaround for labels, add ui_text_icon_width_ex that takes a padding argument. Add presets for 'default', 'compact' and 'none' to avoid duplicating padding values. This allows removal of hard-coded label scaling for the add-object tool. --- .../startup/bl_ui/space_toolsystem_toolbar.py | 4 -- .../editors/interface/interface_layout.c | 71 +++++++++++++++---- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 7bdc3ab160f..5970d6fdf2b 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -497,18 +497,14 @@ class _defs_view3d_add: props = tool.operator_properties("view3d.interactive_add") if not extra: row = layout.row() - row.scale_x = 0.8 row.label(text="Depth:") row = layout.row() - row.scale_x = 0.9 row.prop(props, "plane_depth", text="") row = layout.row() - row.scale_x = 0.8 row.label(text="Orientation:") row = layout.row() row.prop(props, "plane_orientation", text="") row = layout.row() - row.scale_x = 0.8 row.prop(props, "snap_target") region_is_header = bpy.context.region.type == 'TOOL_HEADER' diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 66c75c63050..c10ba3894ea 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -288,8 +288,48 @@ static bool ui_layout_variable_size(uiLayout *layout) return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size; } -/* estimated size of text + icon */ -static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool compact) +/** + * Factors to apply to #UI_UNIT_X when calculating button width. + * This is used when the layout is a varying size, see #ui_layout_variable_size. + */ +struct uiTextIconPadFactor { + float text; + float icon; +}; + +/** + * This adds over an icons width of padding even when no icon is used, + * this is done because most buttons need additional space (drop-down chevron for example). + * menus and labels use much smaller `text` values compared to this default. + * + * \note It may seem odd that the icon only adds 0.25 + * but taking margins into account its fine, + * except for #ui_text_pad_compact where a bit more margin is required. + */ +static const struct uiTextIconPadFactor ui_text_pad_default = { + .text = 1.50f, + .icon = 0.25f, +}; + +/** #ui_text_pad_default scaled down. */ +static const struct uiTextIconPadFactor ui_text_pad_compact = { + .text = 1.25f, + .icon = 0.35, +}; + +/** Least amount of padding not to clip the text or icon. */ +static const struct uiTextIconPadFactor ui_text_pad_none = { + .text = 0.25, + .icon = 1.50f, +}; + +/** + * Estimated size of text + icon. + */ +static int ui_text_icon_width_ex(uiLayout *layout, + const char *name, + int icon, + const struct uiTextIconPadFactor *pad_factor) { const int unit_x = UI_UNIT_X * (layout->scale[0] ? layout->scale[0] : 1.0f); @@ -305,18 +345,21 @@ static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool layout->item.flag |= UI_ITEM_FIXED_SIZE; } const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - float margin = compact ? 1.25 : 1.50; + float margin = pad_factor->text; if (icon) { - /* It may seem odd that the icon only adds (unit_x / 4) - * but taking margins into account its fine, except - * in compact mode a bit more margin is required. */ - margin += compact ? 0.35 : 0.25; + margin += pad_factor->icon; } return UI_fontstyle_string_width(fstyle, name) + (unit_x * margin); } return unit_x * 10; } +static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool compact) +{ + return ui_text_icon_width_ex( + layout, name, icon, compact ? &ui_text_pad_compact : &ui_text_pad_default); +} + static void ui_item_size(uiItem *item, int *r_w, int *r_h) { if (item->type == ITEM_BUTTON) { @@ -2857,23 +2900,24 @@ static uiBut *ui_item_menu(uiLayout *layout, icon = ICON_BLANK1; } - int w = ui_text_icon_width(layout, name, icon, 1); - const int h = UI_UNIT_Y; - + struct uiTextIconPadFactor pad_factor = ui_text_pad_compact; if (layout->root->type == UI_LAYOUT_HEADER) { /* Ugly! */ if (icon == ICON_NONE && force_menu) { /* pass */ } else if (force_menu) { - w += 0.6f * UI_UNIT_X; + pad_factor.text = 1.85; } else { if (name[0]) { - w -= UI_UNIT_X / 2; + pad_factor.text = 0.75; } } } + const int w = ui_text_icon_width_ex(layout, name, icon, &pad_factor); + const int h = UI_UNIT_Y; + if (heading_layout) { ui_layout_heading_label_add(layout, heading_layout, true, true); } @@ -3133,8 +3177,7 @@ static uiBut *uiItemL_(uiLayout *layout, const char *name, int icon) icon = ICON_BLANK1; } - const int w = ui_text_icon_width(layout, name, icon, 0); - + const int w = ui_text_icon_width_ex(layout, name, icon, &ui_text_pad_none); uiBut *but; if (icon && name[0]) { but = uiDefIconTextBut( From e7b9423623d30ff962c926d4ccc6e27f3cba675c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 28 Sep 2021 10:17:00 +0200 Subject: [PATCH 0277/1500] Geometry Nodes: multi-threading when modifying multiple geometry sets Differential Revision: https://developer.blender.org/D12652 --- .../blender/blenkernel/intern/geometry_set.cc | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 3abe2fd2213..e8e2f64d6e1 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -15,6 +15,7 @@ */ #include "BLI_map.hh" +#include "BLI_task.hh" #include "BKE_attribute.h" #include "BKE_attribute_access.hh" @@ -458,26 +459,36 @@ void GeometrySet::gather_attributes_for_propagation( delete dummy_component; } +static void gather_mutable_geometry_sets(GeometrySet &geometry_set, + Vector &r_geometry_sets) +{ + r_geometry_sets.append(&geometry_set); + if (!geometry_set.has_instances()) { + return; + } + /* In the future this can be improved by deduplicating instance references across different + * instances. */ + InstancesComponent &instances_component = + geometry_set.get_component_for_write(); + instances_component.ensure_geometry_instances(); + for (const int handle : instances_component.references().index_range()) { + if (instances_component.references()[handle].type() == InstanceReference::Type::GeometrySet) { + GeometrySet &instance_geometry = instances_component.geometry_set_from_reference(handle); + gather_mutable_geometry_sets(instance_geometry, r_geometry_sets); + } + } +} + /** * Modify every (recursive) instance separately. This is often more efficient than realizing all * instances just to change the same thing on all of them. */ void GeometrySet::modify_geometry_sets(ForeachSubGeometryCallback callback) { - callback(*this); - if (!this->has_instances()) { - return; - } - /* In the future this can be improved by deduplicating instance references across different - * instances. */ - InstancesComponent &instances_component = this->get_component_for_write(); - instances_component.ensure_geometry_instances(); - for (const int handle : instances_component.references().index_range()) { - if (instances_component.references()[handle].type() == InstanceReference::Type::GeometrySet) { - GeometrySet &instance_geometry = instances_component.geometry_set_from_reference(handle); - instance_geometry.modify_geometry_sets(callback); - } - } + Vector geometry_sets; + gather_mutable_geometry_sets(*this, geometry_sets); + blender::threading::parallel_for_each( + geometry_sets, [&](GeometrySet *geometry_set) { callback(*geometry_set); }); } /** \} */ From def8fd6330fde5a7add9ab89efb6c08576355eb2 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 28 Sep 2021 10:17:49 +0200 Subject: [PATCH 0278/1500] Cleanup: support moving InstanceReference --- source/blender/blenkernel/BKE_geometry_set.hh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 08bfdbf2382..f6a58e57298 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -509,6 +509,13 @@ class InstanceReference { } } + InstanceReference(InstanceReference &&other) + : type_(other.type_), data_(other.data_), geometry_set_(std::move(other.geometry_set_)) + { + other.type_ = Type::None; + other.data_ = nullptr; + } + InstanceReference &operator=(const InstanceReference &other) { if (this == &other) { @@ -519,6 +526,16 @@ class InstanceReference { return *this; } + InstanceReference &operator=(InstanceReference &&other) + { + if (this == &other) { + return *this; + } + this->~InstanceReference(); + new (this) InstanceReference(std::move(other)); + return *this; + } + Type type() const { return type_; From 7cd43a9d2887cffa8b2c24aa0d51f1e87a70e701 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 28 Sep 2021 10:19:16 +0200 Subject: [PATCH 0279/1500] Fix: field inferencing fails when there are undefined nodes --- source/blender/blenkernel/intern/node.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index f1f643ffed7..f2843e5f88e 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4513,6 +4513,9 @@ static InputSocketFieldType get_interface_input_field_type(const NodeRef &node, /* Outputs always support fields when the data type is correct. */ return InputSocketFieldType::IsSupported; } + if (node.is_undefined()) { + return InputSocketFieldType::None; + } const NodeDeclaration *node_decl = node.declaration(); @@ -4547,6 +4550,9 @@ static OutputFieldDependency get_interface_output_field_dependency(const NodeRef /* Input nodes get special treatment in #determine_group_input_states. */ return OutputFieldDependency::ForDependentField(); } + if (node.is_undefined()) { + return OutputFieldDependency::ForDataSource(); + } const NodeDeclaration *node_decl = node.declaration(); From a64782b1334118d1bd98c78f665e7e0bfd232076 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 27 Sep 2021 15:05:23 +0200 Subject: [PATCH 0280/1500] VSE: Implement sanity check for files with more channels than supported This is a follow up to 8fecc2a8525467ee2fbbaae16ddbbc10b3050d46. This makes sure future .blend files that have more channels than the limit won't break Blender. It can be backported to LTS. This is part of https://developer.blender.org/D12645 Differential Revision: https://developer.blender.org/D12648 --- source/blender/blenkernel/intern/scene.c | 11 +++++++++-- source/blender/blenloader/BLO_readfile.h | 2 ++ source/blender/windowmanager/intern/wm_files.c | 8 ++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index ccd3e0bfd6c..1dd7fcf1d1a 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -63,6 +63,8 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLO_readfile.h" + #include "BLT_translation.h" #include "BKE_action.h" @@ -993,8 +995,13 @@ static void link_recurs_seq(BlendDataReader *reader, ListBase *lb) { BLO_read_list(reader, lb); - LISTBASE_FOREACH (Sequence *, seq, lb) { - if (seq->seqbase.first) { + LISTBASE_FOREACH_MUTABLE (Sequence *, seq, lb) { + /* Sanity check. */ + if ((seq->machine < 1) || (seq->machine > MAXSEQ)) { + BLI_freelinkN(lb, seq); + BLO_read_data_reports(reader)->count.vse_strips_skipped++; + } + else if (seq->seqbase.first) { link_recurs_seq(reader, &seq->seqbase); } } diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 9093c6fd85b..c6637b17d47 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -121,6 +121,8 @@ typedef struct BlendFileReadReport { int proxies_to_lib_overrides_success; /* Number of proxies that failed to convert to library overrides. */ int proxies_to_lib_overrides_failures; + /* Number of VSE strips that were not read because were in non-supported channels. */ + int vse_strips_skipped; } count; /* Number of libraries which had overrides that needed to be resynced, and a single linked list diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index bbab3a8b326..a5ebf988edd 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -871,6 +871,14 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports) bf_reports->count.linked_proxies); } + if (bf_reports->count.vse_strips_skipped != 0) { + BKE_reportf(bf_reports->reports, + RPT_ERROR, + "%d sequence strips were not read because they were in a channel larger than %d", + bf_reports->count.vse_strips_skipped, + MAXSEQ); + } + BLI_linklist_free(bf_reports->resynced_lib_overrides_libraries, NULL); bf_reports->resynced_lib_overrides_libraries = NULL; } From e5ff9f3615dec3ad8701e404bb4f57e758f71032 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Tue, 28 Sep 2021 10:33:42 +0200 Subject: [PATCH 0281/1500] Cleanup: Move VSE channels check into own util function Differential Revision: https://developer.blender.org/D12661 --- source/blender/blenkernel/intern/scene.c | 2 +- source/blender/sequencer/SEQ_sequencer.h | 1 + source/blender/sequencer/intern/sequencer.c | 12 ++++++++++++ source/blender/sequencer/intern/strip_transform.c | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 1dd7fcf1d1a..03f19cef94e 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -997,7 +997,7 @@ static void link_recurs_seq(BlendDataReader *reader, ListBase *lb) LISTBASE_FOREACH_MUTABLE (Sequence *, seq, lb) { /* Sanity check. */ - if ((seq->machine < 1) || (seq->machine > MAXSEQ)) { + if (!SEQ_valid_strip_channel(seq)) { BLI_freelinkN(lb, seq); BLO_read_data_reports(reader)->count.vse_strips_skipped++; } diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index 7e733817630..1b8982da0d2 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -89,6 +89,7 @@ void SEQ_sequence_base_dupli_recursive(const struct Scene *scene_src, const struct ListBase *seqbase, int dupe_flag, const int flag); +bool SEQ_valid_strip_channel(struct Sequence *seq); /* Read and Write functions for .blend file data */ void SEQ_blend_write(struct BlendWriter *writer, struct ListBase *seqbase); diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index 382bd51aae1..c164e7fc2ee 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -636,6 +636,18 @@ void SEQ_sequence_base_dupli_recursive(const Scene *scene_src, seq_new_fix_links_recursive(seq); } } + +bool SEQ_valid_strip_channel(Sequence *seq) +{ + if (seq->machine < 1) { + return false; + } + if (seq->machine > MAXSEQ) { + return false; + } + return true; +} + /* r_prefix + [" + escaped_name + "] + \0 */ #define SEQ_RNAPATH_MAXSTR ((30 + 2 + (SEQ_NAME_MAXSTR * 2) + 2) + 1) diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index d5ff455c694..54ca4ef487f 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -278,7 +278,7 @@ bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep, SEQ_time_update_sequence(evil_scene, test); } - if ((test->machine < 1) || (test->machine > MAXSEQ)) { + if (!SEQ_valid_strip_channel(test)) { /* Blender 2.4x would remove the strip. * nicer to move it to the end */ From 741fa8180c4fbe98f3cc8b3eee239b03e26309f4 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 28 Sep 2021 10:47:39 +0200 Subject: [PATCH 0282/1500] Fix T91679: Crash when saving bordered render as multilayer exr The related issue which is fixed by this change is the missing noisy image pass when denoising and border render is used, Need to allocate passes after the passes has been copied from the original render result. --- source/blender/render/intern/pipeline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 72ff920561d..931282e26dd 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -1015,10 +1015,10 @@ static void render_result_uncrop(Render *re) render_result_disprect_to_full_resolution(re); rres = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); - render_result_passes_allocated_ensure(rres); rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data); render_result_clone_passes(re, rres, NULL); + render_result_passes_allocated_ensure(rres); render_result_merge(rres, re->result); render_result_free(re->result); From b91946780cd4fecd4dacebd1cd5b785a89773f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 28 Sep 2021 11:05:45 +0200 Subject: [PATCH 0283/1500] Path util: BLI_path_contains() case-insensitive on Windows Make `BLI_path_contains()` case-insensitive on Windows. This behaviour is dependent on the platform Blender is running on, like the rest of BLI_path, and not on the style of paths (Windows-style paths will be treated case-sensitively when Blender is running on Linux/macOS). --- source/blender/blenlib/intern/path_util.c | 5 +++++ source/blender/blenlib/tests/BLI_path_util_test.cc | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 4fc59e6cffd..4405f25bf2a 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1951,6 +1951,11 @@ bool BLI_path_contains(const char *container_path, const char *containee_path) BLI_path_normalize(NULL, container_native); BLI_path_normalize(NULL, containee_native); +#ifdef WIN32 + BLI_str_tolower_ascii(container_native, PATH_MAX); + BLI_str_tolower_ascii(containee_native, PATH_MAX); +#endif + if (STREQ(container_native, containee_native)) { /* The paths are equal, they contain each other. */ return true; diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc index fde28ebaf55..65b02a19960 100644 --- a/source/blender/blenlib/tests/BLI_path_util_test.cc +++ b/source/blender/blenlib/tests/BLI_path_util_test.cc @@ -678,3 +678,11 @@ TEST(path_util, PathContains) EXPECT_FALSE(BLI_path_contains("/some/path", "./contents")) << "Relative paths are not supported"; } + +#ifdef WIN32 +TEST(path_util, PathContains_Windows_case_insensitive) +{ + EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "c:\\SOME\\path\\inside")) + << "On Windows path comparison should ignore case"; +} +#endif From 6f29801f1b693c5526bdb41d0c2bcf89caf5a05b Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 3 Sep 2021 15:51:38 +0200 Subject: [PATCH 0284/1500] Fix Drivers Editor not hiding vertical part of cursor When "Show Cursor" is unchecked, the Drivers Editor would still display the vertical line representing the cursor x. Probably overseen in {rB65072499c65a} (historically the vertical line could represent either the current frame of the cursor X in drawing, but this is now much more separate). There is no point in seeing part of the cursor in the Drivers Editor if this is disabled. Also correct outdated comments. ref. T91157 Maniphest Tasks: T91157 Differential Revision: https://developer.blender.org/D12391 --- .../blender/editors/space_graph/space_graph.c | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 720d69eaf4f..86e12714d62 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -237,29 +237,27 @@ static void graph_main_region_draw(const bContext *C, ARegion *region) v2d->tot.xmax += 10.0f; } - if (((sipo->flag & SIPO_NODRAWCURSOR) == 0) || (sipo->mode == SIPO_MODE_DRIVERS)) { + if (((sipo->flag & SIPO_NODRAWCURSOR) == 0)) { uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* horizontal component of value-cursor (value line before the current frame line) */ - if ((sipo->flag & SIPO_NODRAWCURSOR) == 0) { - float y = sipo->cursorVal; + float y = sipo->cursorVal; - /* Draw a green line to indicate the cursor value */ - immUniformThemeColorShadeAlpha(TH_CFRAME, -10, -50); - GPU_blend(GPU_BLEND_ALPHA); - GPU_line_width(2.0); + /* Draw a line to indicate the cursor value. */ + immUniformThemeColorShadeAlpha(TH_CFRAME, -10, -50); + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_width(2.0); - immBegin(GPU_PRIM_LINES, 2); - immVertex2f(pos, v2d->cur.xmin, y); - immVertex2f(pos, v2d->cur.xmax, y); - immEnd(); + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, v2d->cur.xmin, y); + immVertex2f(pos, v2d->cur.xmax, y); + immEnd(); - GPU_blend(GPU_BLEND_NONE); - } + GPU_blend(GPU_BLEND_NONE); - /* current frame or vertical component of vertical component of the cursor */ + /* Vertical component of of the cursor. */ if (sipo->mode == SIPO_MODE_DRIVERS) { /* cursor x-value */ float x = sipo->cursorTime; From b3431a88465db2433b46e1f6426c801125d0047d Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 3 Sep 2021 16:04:01 +0200 Subject: [PATCH 0285/1500] Fix Drivers Editor showing playhead on the X Axis Currently the Drivers Editor shows this (the blue thing can be dragged to change frame): {F10647661} But the Drivers Editors X axis is the output of the driver [which can be further tweaked by the curve] not time(frame). So it seems better to not mix them here, it is just confusing to have two different units on one axis. Especially since what we really want to look at in X (the drivers output value) can be in a totally unrelated range compared to frames, so e.g. we might be interested in a drivers range from 0.0 to 1.0 and a framerange of 100 to 200, so putting this on one axis just does not make sense. Better to use a separate timeline for this. Note 2.79 also did not do this. Maniphest Tasks: T91157 Differential Revision: https://developer.blender.org/D12392 --- source/blender/editors/animation/anim_ops.c | 9 ++++- .../blender/editors/animation/time_scrub_ui.c | 36 +++++++++---------- .../editors/include/ED_time_scrub_ui.h | 3 +- .../editors/space_action/space_action.c | 2 +- .../blender/editors/space_graph/space_graph.c | 8 ++++- source/blender/editors/space_nla/space_nla.c | 2 +- .../editors/space_sequencer/sequencer_draw.c | 2 +- 7 files changed, 35 insertions(+), 27 deletions(-) diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index b4ea33920b2..3958c7f9e34 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -74,9 +74,16 @@ static bool change_frame_poll(bContext *C) * this shouldn't show up in 3D editor (or others without 2D timeline view) via search */ if (area) { - if (ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP, SPACE_GRAPH)) { + if (ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP)) { return true; } + if (area->spacetype == SPACE_GRAPH) { + const SpaceGraph *sipo = area->spacedata.first; + /* Driver Editor's X axis is not time. */ + if (sipo->mode != SIPO_MODE_DRIVERS) { + return true; + } + } } CTX_wm_operator_poll_msg_set(C, "Expected an animation area to be active"); diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 8aeb6a57124..70a8973864c 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -91,8 +91,7 @@ static void draw_current_frame(const Scene *scene, bool display_seconds, const View2D *v2d, const rcti *scrub_region_rect, - int current_frame, - bool draw_line) + int current_frame) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; int frame_x = UI_view2d_view_to_region_x(v2d, current_frame); @@ -106,21 +105,19 @@ static void draw_current_frame(const Scene *scene, float bg_color[4]; UI_GetThemeColorShade4fv(TH_CFRAME, -5, bg_color); - if (draw_line) { - /* Draw vertical line to from the bottom of the current frame box to the bottom of the screen. - */ - const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene)); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColor(TH_CFRAME); - immRectf(pos, - subframe_x - U.pixelsize, - scrub_region_rect->ymax - box_padding, - subframe_x + U.pixelsize, - 0.0f); - immUnbindProgram(); - } + /* Draw vertical line to from the bottom of the current frame box to the bottom of the screen. + */ + const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene)); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColor(TH_CFRAME); + immRectf(pos, + subframe_x - U.pixelsize, + scrub_region_rect->ymax - box_padding, + subframe_x + U.pixelsize, + 0.0f); + immUnbindProgram(); UI_draw_roundbox_corner_set(UI_CNR_ALL); @@ -152,8 +149,7 @@ static void draw_current_frame(const Scene *scene, void ED_time_scrub_draw_current_frame(const ARegion *region, const Scene *scene, - bool display_seconds, - bool draw_line) + bool display_seconds) { const View2D *v2d = ®ion->v2d; GPU_matrix_push_projection(); @@ -162,7 +158,7 @@ void ED_time_scrub_draw_current_frame(const ARegion *region, rcti scrub_region_rect; get_time_scrub_region_rect(region, &scrub_region_rect); - draw_current_frame(scene, display_seconds, v2d, &scrub_region_rect, scene->r.cfra, draw_line); + draw_current_frame(scene, display_seconds, v2d, &scrub_region_rect, scene->r.cfra); GPU_matrix_pop_projection(); } diff --git a/source/blender/editors/include/ED_time_scrub_ui.h b/source/blender/editors/include/ED_time_scrub_ui.h index 6420aaf5ef0..812cb31c9b0 100644 --- a/source/blender/editors/include/ED_time_scrub_ui.h +++ b/source/blender/editors/include/ED_time_scrub_ui.h @@ -33,8 +33,7 @@ struct wmEvent; void ED_time_scrub_draw_current_frame(const struct ARegion *region, const struct Scene *scene, - bool display_seconds, - bool draw_line); + bool display_seconds); void ED_time_scrub_draw(const struct ARegion *region, const struct Scene *scene, diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index f59429ba981..738eeb21e2e 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -247,7 +247,7 @@ static void action_main_region_draw_overlay(const bContext *C, ARegion *region) View2D *v2d = ®ion->v2d; /* scrubbing region */ - ED_time_scrub_draw_current_frame(region, scene, saction->flag & SACTION_DRAWTIME, true); + ED_time_scrub_draw_current_frame(region, scene, saction->flag & SACTION_DRAWTIME); /* scrollers */ UI_view2d_scrollers_draw(v2d, NULL); diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 86e12714d62..3512db961ab 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -309,12 +309,18 @@ static void graph_main_region_draw_overlay(const bContext *C, ARegion *region) { /* draw entirely, view changes should be handled here */ const SpaceGraph *sipo = CTX_wm_space_graph(C); + + /* Driver Editor's X axis is not time. */ + if (sipo->mode == SIPO_MODE_DRIVERS) { + return; + } + const Scene *scene = CTX_data_scene(C); const bool draw_vert_line = sipo->mode != SIPO_MODE_DRIVERS; View2D *v2d = ®ion->v2d; /* scrubbing region */ - ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME, draw_vert_line); + ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME); /* scrollers */ /* FIXME: args for scrollers depend on the type of data being shown. */ diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index 987d06cfe5c..8b44c26f07c 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -289,7 +289,7 @@ static void nla_main_region_draw_overlay(const bContext *C, ARegion *region) View2D *v2d = ®ion->v2d; /* scrubbing region */ - ED_time_scrub_draw_current_frame(region, scene, snla->flag & SNLA_DRAWTIME, true); + ED_time_scrub_draw_current_frame(region, scene, snla->flag & SNLA_DRAWTIME); /* scrollers */ UI_view2d_scrollers_draw(v2d, NULL); diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 146ea970087..04377f02a7f 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -3294,6 +3294,6 @@ void draw_timeline_seq_display(const bContext *C, ARegion *region) UI_view2d_view_restore(C); } - ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES), true); + ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES)); UI_view2d_scrollers_draw(v2d, NULL); } From 5d160dec3b8d469994ea9c6c22bd8100789d8c1e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 28 Sep 2021 11:31:35 +0200 Subject: [PATCH 0286/1500] Geometry Nodes: move legacy nodes to separate folder Previously, we were moving them one by one. It's a lot easier to just move all files at the same time. --- source/blender/blenkernel/BKE_node.h | 4 +- .../blenloader/intern/versioning_300.c | 6 +- source/blender/nodes/CMakeLists.txt | 62 +++++++++---------- source/blender/nodes/NOD_static_types.h | 4 +- .../node_geo_align_rotation_to_vector.cc | 0 .../{ => legacy}/node_geo_attribute_clamp.cc | 2 +- .../node_geo_attribute_color_ramp.cc | 0 .../node_geo_attribute_combine_xyz.cc | 0 .../node_geo_attribute_compare.cc | 0 .../node_geo_attribute_convert.cc | 0 .../node_geo_attribute_curve_map.cc | 0 .../{ => legacy}/node_geo_attribute_fill.cc | 0 .../node_geo_attribute_map_range.cc | 0 .../{ => legacy}/node_geo_attribute_math.cc | 0 .../{ => legacy}/node_geo_attribute_mix.cc | 0 .../node_geo_attribute_proximity.cc | 0 .../node_geo_attribute_sample_texture.cc | 0 .../node_geo_attribute_separate_xyz.cc | 0 .../node_geo_attribute_transfer.cc | 0 .../node_geo_attribute_vector_math.cc | 0 .../node_geo_attribute_vector_rotate.cc | 2 +- .../{ => legacy}/node_geo_curve_reverse.cc | 0 .../node_geo_curve_select_by_handle_type.cc | 0 .../node_geo_curve_set_handles.cc | 0 .../node_geo_curve_spline_type.cc | 0 .../{ => legacy}/node_geo_curve_subdivide.cc | 0 .../{ => legacy}/node_geo_delete_geometry.cc | 0 .../{ => legacy}/node_geo_mesh_to_curve.cc | 0 .../{ => legacy}/node_geo_point_rotate.cc | 0 .../{ => legacy}/node_geo_point_scale.cc | 0 .../{ => legacy}/node_geo_point_separate.cc | 0 .../{ => legacy}/node_geo_point_translate.cc | 0 .../{ => legacy}/node_geo_points_to_volume.cc | 0 .../nodes/{ => legacy}/node_geo_raycast.cc | 0 34 files changed, 40 insertions(+), 40 deletions(-) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_align_rotation_to_vector.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_clamp.cc (99%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_color_ramp.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_combine_xyz.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_compare.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_convert.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_curve_map.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_fill.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_map_range.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_math.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_mix.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_proximity.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_sample_texture.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_separate_xyz.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_transfer.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_vector_math.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_attribute_vector_rotate.cc (99%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_curve_reverse.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_curve_select_by_handle_type.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_curve_set_handles.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_curve_spline_type.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_curve_subdivide.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_delete_geometry.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_mesh_to_curve.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_point_rotate.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_point_scale.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_point_separate.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_point_translate.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_points_to_volume.cc (100%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_raycast.cc (100%) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 5491f2a3de9..eb146231b41 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1451,14 +1451,14 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MESH_PRIMITIVE_LINE 1038 #define GEO_NODE_MESH_PRIMITIVE_GRID 1039 #define GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE 1040 -#define GEO_NODE_LECAGY_ATTRIBUTE_CLAMP 1041 +#define GEO_NODE_LEGACY_ATTRIBUTE_CLAMP 1041 #define GEO_NODE_BOUNDING_BOX 1042 #define GEO_NODE_SWITCH 1043 #define GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER 1044 #define GEO_NODE_CURVE_TO_MESH 1045 #define GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP 1046 #define GEO_NODE_CURVE_RESAMPLE 1047 -#define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048 +#define GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE 1048 #define GEO_NODE_LEGACY_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 #define GEO_NODE_MATERIAL_REPLACE 1051 diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index b86f98af4f1..26903fff70f 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -753,10 +753,10 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR: case GEO_NODE_LEGACY_POINT_SCALE: case GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE: - case GEO_NODE_ATTRIBUTE_VECTOR_ROTATE: + case GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE: case GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP: case GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE: - case GEO_NODE_LECAGY_ATTRIBUTE_CLAMP: + case GEO_NODE_LEGACY_ATTRIBUTE_CLAMP: case GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH: case GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ: case GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ: @@ -1521,4 +1521,4 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { /* Keep this block, even when empty. */ } -} \ No newline at end of file +} diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 844e838272c..bb67d931963 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -147,32 +147,45 @@ set(SRC function/nodes/node_fn_value_to_string.cc function/node_function_util.cc + geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc + geometry/nodes/legacy/node_geo_attribute_clamp.cc + geometry/nodes/legacy/node_geo_attribute_color_ramp.cc + geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc + geometry/nodes/legacy/node_geo_attribute_compare.cc + geometry/nodes/legacy/node_geo_attribute_convert.cc + geometry/nodes/legacy/node_geo_attribute_curve_map.cc + geometry/nodes/legacy/node_geo_attribute_fill.cc + geometry/nodes/legacy/node_geo_attribute_map_range.cc + geometry/nodes/legacy/node_geo_attribute_math.cc + geometry/nodes/legacy/node_geo_attribute_mix.cc + geometry/nodes/legacy/node_geo_attribute_proximity.cc geometry/nodes/legacy/node_geo_attribute_randomize.cc + geometry/nodes/legacy/node_geo_attribute_sample_texture.cc + geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc + geometry/nodes/legacy/node_geo_attribute_transfer.cc + geometry/nodes/legacy/node_geo_attribute_vector_math.cc + geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc + geometry/nodes/legacy/node_geo_curve_reverse.cc + geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc + geometry/nodes/legacy/node_geo_curve_set_handles.cc + geometry/nodes/legacy/node_geo_curve_spline_type.cc + geometry/nodes/legacy/node_geo_curve_subdivide.cc + geometry/nodes/legacy/node_geo_delete_geometry.cc geometry/nodes/legacy/node_geo_material_assign.cc - geometry/nodes/legacy/node_geo_select_by_material.cc + geometry/nodes/legacy/node_geo_mesh_to_curve.cc geometry/nodes/legacy/node_geo_point_distribute.cc geometry/nodes/legacy/node_geo_point_instance.cc + geometry/nodes/legacy/node_geo_point_rotate.cc + geometry/nodes/legacy/node_geo_point_scale.cc + geometry/nodes/legacy/node_geo_point_separate.cc + geometry/nodes/legacy/node_geo_point_translate.cc + geometry/nodes/legacy/node_geo_points_to_volume.cc + geometry/nodes/legacy/node_geo_raycast.cc + geometry/nodes/legacy/node_geo_select_by_material.cc - geometry/nodes/node_geo_align_rotation_to_vector.cc geometry/nodes/node_geo_attribute_capture.cc - geometry/nodes/node_geo_attribute_clamp.cc - geometry/nodes/node_geo_attribute_color_ramp.cc - geometry/nodes/node_geo_attribute_combine_xyz.cc - geometry/nodes/node_geo_attribute_compare.cc - geometry/nodes/node_geo_attribute_convert.cc - geometry/nodes/node_geo_attribute_curve_map.cc - geometry/nodes/node_geo_attribute_fill.cc - geometry/nodes/node_geo_attribute_map_range.cc - geometry/nodes/node_geo_attribute_math.cc - geometry/nodes/node_geo_attribute_mix.cc - geometry/nodes/node_geo_attribute_proximity.cc geometry/nodes/node_geo_attribute_remove.cc - geometry/nodes/node_geo_attribute_sample_texture.cc - geometry/nodes/node_geo_attribute_separate_xyz.cc geometry/nodes/node_geo_attribute_statistic.cc - geometry/nodes/node_geo_attribute_transfer.cc - geometry/nodes/node_geo_attribute_vector_math.cc - geometry/nodes/node_geo_attribute_vector_rotate.cc geometry/nodes/node_geo_boolean.cc geometry/nodes/node_geo_bounding_box.cc geometry/nodes/node_geo_collection_info.cc @@ -191,16 +204,10 @@ set(SRC geometry/nodes/node_geo_curve_primitive_spiral.cc geometry/nodes/node_geo_curve_primitive_star.cc geometry/nodes/node_geo_curve_resample.cc - geometry/nodes/node_geo_curve_reverse.cc - geometry/nodes/node_geo_curve_select_by_handle_type.cc - geometry/nodes/node_geo_curve_set_handles.cc - geometry/nodes/node_geo_curve_spline_type.cc - geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_curve_fillet.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_to_points.cc geometry/nodes/node_geo_curve_trim.cc - geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_distribute_points_on_faces.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_instance_on_points.cc @@ -223,14 +230,7 @@ set(SRC geometry/nodes/node_geo_mesh_primitive_line.cc geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc geometry/nodes/node_geo_mesh_subdivide.cc - geometry/nodes/node_geo_mesh_to_curve.cc geometry/nodes/node_geo_object_info.cc - geometry/nodes/node_geo_point_rotate.cc - geometry/nodes/node_geo_point_scale.cc - geometry/nodes/node_geo_point_separate.cc - geometry/nodes/node_geo_point_translate.cc - geometry/nodes/node_geo_points_to_volume.cc - geometry/nodes/node_geo_raycast.cc geometry/nodes/node_geo_realize_instances.cc geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_set_position.cc diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index e9f7ec2c7ff..43d86223e9b 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -275,7 +275,7 @@ DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToStri DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") DefNode(FunctionNode, FN_NODE_STRING_SUBSTRING, 0, "STRING_SUBSTRING", StringSubstring, "String Substring", "") -DefNode(GeometryNode, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "LEGACY_ATTRIBUTE_COLOR_RAMP", LegacyAttributeColorRamp, "Attribute Color Ramp", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "LEGACY_ATTRIBUTE_COMBINE_XYZ", LegacyAttributeCombineXYZ, "Attribute Combine XYZ", "") @@ -292,6 +292,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, 0, "LEGACY_ATTRI DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "LEGACY_ATTRIBUTE_SEPARATE_XYZ", LegacyAttributeSeparateXYZ, "Attribute Separate XYZ", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "LEGACY_ATTRIBUTE_TRANSFER", LegacyAttributeTransfer, "Attribute Transfer", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "LEGACY_ATTRIBUTE_VECTOR_MATH", LegacyAttributeVectorMath, "Attribute Vector Math", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") @@ -312,7 +313,6 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_M DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "") DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc similarity index 99% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc index 97070184609..2e931a2da98 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc @@ -269,7 +269,7 @@ void register_node_type_geo_attribute_clamp() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0); node_type_init(&ntype, blender::nodes::geo_node_attribute_clamp_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_clamp_update); ntype.declare = blender::nodes::geo_node_attribute_clamp_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc similarity index 99% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc index adaa4de3029..0c515fa63fb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc @@ -332,7 +332,7 @@ void register_node_type_geo_attribute_vector_rotate() static bNodeType ntype; geo_node_type_base(&ntype, - GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, + GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, "Attribute Vector Rotate", NODE_CLASS_ATTRIBUTE, 0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_point_scale.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_point_separate.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_point_translate.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc similarity index 100% rename from source/blender/nodes/geometry/nodes/node_geo_raycast.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc From 6a745e54f65d831fe7d4ca101b68bea149c8349a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 28 Sep 2021 13:32:22 +0200 Subject: [PATCH 0287/1500] Cleanup: remove incorrect assert The method works perfectly fine when `resource` is empty. --- source/blender/blenlib/BLI_resource_scope.hh | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/blenlib/BLI_resource_scope.hh b/source/blender/blenlib/BLI_resource_scope.hh index edffb148477..761e1ef834c 100644 --- a/source/blender/blenlib/BLI_resource_scope.hh +++ b/source/blender/blenlib/BLI_resource_scope.hh @@ -73,7 +73,6 @@ class ResourceScope : NonCopyable, NonMovable { */ template T *add(std::unique_ptr resource) { - BLI_assert(resource.get() != nullptr); T *ptr = resource.release(); if (ptr == nullptr) { return nullptr; From 9490db1ad26812c6acf6c45e7944450032026f50 Mon Sep 17 00:00:00 2001 From: Vitor Boschi da Silva Date: Tue, 28 Sep 2021 14:28:11 +0200 Subject: [PATCH 0288/1500] Cleanup: remove unused variable As the title says Reviewed By: lichtwerk, campbellbarton Differential Revision: https://developer.blender.org/D12665 --- source/blender/editors/space_graph/space_graph.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 3512db961ab..0e2c9b85bc6 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -316,7 +316,6 @@ static void graph_main_region_draw_overlay(const bContext *C, ARegion *region) } const Scene *scene = CTX_data_scene(C); - const bool draw_vert_line = sipo->mode != SIPO_MODE_DRIVERS; View2D *v2d = ®ion->v2d; /* scrubbing region */ From 73b2ecb2975eb0438473e65c4c8f691e36524399 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 28 Sep 2021 14:37:06 +0200 Subject: [PATCH 0289/1500] Cleanup: typo in comment Noticed by @david_black in D12392, thx! --- source/blender/editors/animation/time_scrub_ui.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 70a8973864c..b0eb014c5d9 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -105,8 +105,7 @@ static void draw_current_frame(const Scene *scene, float bg_color[4]; UI_GetThemeColorShade4fv(TH_CFRAME, -5, bg_color); - /* Draw vertical line to from the bottom of the current frame box to the bottom of the screen. - */ + /* Draw vertical line from the bottom of the current frame box to the bottom of the screen. */ const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene)); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); From 3674347849311b3722c27c67a61b1fac5528bd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 28 Sep 2021 15:04:55 +0200 Subject: [PATCH 0290/1500] Assets: Clear Asset operator variants for clearing/setting Fake User The Clear Asset operator (`ASSET_OT_clear`) now clears the Fake User. This makes it symmetrical with the Mark Asset (`ASSET_OT_mark`) operator, which sets Fake User to ensure assets are always saved to disk. Clear Asset now also has a `set_fake_user` boolean option, which allows users to Clear Asset and set Fake User in one go. The asset browser now shows these options in the context menu: - Clear Asset: also clears Fake User. This makes it possible to actually remove assets from the blend file without leaving the Asset Browser. - Clear Asset (Set Fake User): keeps the Fake User bit set. This makes it possible to "hide" the asset from the asset browser, without loosing the actual data. Internally, the `ED_asset_clear_id(id)` function now always clears the Fake User bit. If it was intended that this bit was kept set, it's up to the caller to explicitly call `id_fake_user_set(id)` afterwards. Manifest Task: T90844 Reviewed By: Severin Differential Revision: https://developer.blender.org/D12663 --- .../startup/bl_ui/space_filebrowser.py | 5 +- .../scripts/startup/bl_ui/space_outliner.py | 3 +- .../editors/asset/ED_asset_mark_clear.h | 15 ++++ .../editors/asset/intern/asset_mark_clear.cc | 3 +- .../blender/editors/asset/intern/asset_ops.cc | 87 +++++++++++++++++-- 5 files changed, 103 insertions(+), 10 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index ee9a8cc3346..b47404fd727 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -786,9 +786,10 @@ class ASSETBROWSER_MT_context_menu(AssetBrowserMenu, Menu): layout.separator() - sub = layout.row() + sub = layout.column() sub.operator_context = 'EXEC_DEFAULT' - sub.operator("asset.clear", text="Clear Asset") + sub.operator("asset.clear", text="Clear Asset").set_fake_user = False + sub.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True layout.separator() diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index febd064147f..3d18160d90f 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -323,7 +323,8 @@ class OUTLINER_MT_asset(Menu): space = context.space_data layout.operator("asset.mark") - layout.operator("asset.clear") + layout.operator("asset.clear", text="Clear Asset").set_fake_user = False + layout.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True class OUTLINER_PT_filter(Panel): diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h index 7524ec6b02a..d8b8f15a109 100644 --- a/source/blender/editors/asset/ED_asset_mark_clear.h +++ b/source/blender/editors/asset/ED_asset_mark_clear.h @@ -27,7 +27,22 @@ extern "C" { struct ID; struct bContext; +/** + * Mark the datablock as asset. + * + * To ensure the datablock is saved, this sets Fake User. + * + * \return whether the datablock was marked as asset; false when it is not capable of becoming an + * asset, or when it already was an asset. */ bool ED_asset_mark_id(const struct bContext *C, struct ID *id); + +/** + * Remove the asset metadata, turning the ID into a "normal" ID. + * + * This clears the Fake User. If for some reason the datablock is meant to be saved anyway, the + * caller is responsible for explicitly setting the Fake User. + * + * \return whether the asset metadata was actually removed; false when the ID was not an asset. */ bool ED_asset_clear_id(struct ID *id); bool ED_asset_can_mark_single_from_context(const struct bContext *C); diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc index ba348e38823..8290124c209 100644 --- a/source/blender/editors/asset/intern/asset_mark_clear.cc +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -67,8 +67,7 @@ bool ED_asset_clear_id(ID *id) return false; } BKE_asset_metadata_free(&id->asset_data); - /* Don't clear fake user here, there's no guarantee that it was actually set by - * #ED_asset_mark_id(), it might have been something/someone else. */ + id_fake_user_clear(id); /* Important for asset storage to update properly! */ ED_assetlist_storage_tag_main_data_dirty(); diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index d69a2cae94d..a18b7649060 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -19,6 +19,7 @@ */ #include "BKE_context.h" +#include "BKE_lib_id.h" #include "BKE_report.h" #include "BLI_vector.hh" @@ -26,6 +27,7 @@ #include "ED_asset.h" #include "RNA_access.h" +#include "RNA_define.h" #include "WM_api.h" #include "WM_types.h" @@ -34,11 +36,30 @@ using PointerRNAVec = blender::Vector; +static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C); +static PointerRNAVec asset_operation_get_nonexperimental_ids_from_context(const bContext *C); +static bool asset_type_is_nonexperimental(const ID_Type id_type); + static bool asset_operation_poll(bContext * /*C*/) { + /* At this moment only the pose library is non-experimental. Still, directly marking arbitrary + * Actions as asset is not part of the stable functionality; instead, the pose library "Create + * Pose Asset" operator should be used. Actions can still be marked as asset via + * `the_action.asset_mark()` (so a function call instead of this operator), which is what the + * pose library uses internally. */ return U.experimental.use_extended_asset_browser; } +static bool asset_clear_poll(bContext *C) +{ + if (asset_operation_poll(C)) { + return true; + } + + PointerRNAVec pointers = asset_operation_get_nonexperimental_ids_from_context(C); + return !pointers.is_empty(); +} + /** * Return the IDs to operate on as PointerRNA vector. Either a single one ("id" context member) or * multiple ones ("selected_ids" context member). @@ -64,6 +85,28 @@ static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C) return ids; } +static PointerRNAVec asset_operation_get_nonexperimental_ids_from_context(const bContext *C) +{ + PointerRNAVec nonexperimental; + PointerRNAVec pointers = asset_operation_get_ids_from_context(C); + for (PointerRNA &ptr : pointers) { + BLI_assert(RNA_struct_is_ID(ptr.type)); + + ID *id = static_cast(ptr.data); + if (asset_type_is_nonexperimental(GS(id->name))) { + nonexperimental.append(ptr); + } + } + return nonexperimental; +} + +static bool asset_type_is_nonexperimental(const ID_Type id_type) +{ + /* At this moment only the pose library is non-experimental. For simplicity, allow asset + * operations on all Action datablocks (even though pose assets are limited to single frames). */ + return ELEM(id_type, ID_AC); +} + /* -------------------------------------------------------------------- */ class AssetMarkHelper { @@ -166,7 +209,13 @@ static void ASSET_OT_mark(wmOperatorType *ot) /* -------------------------------------------------------------------- */ class AssetClearHelper { + const bool set_fake_user_; + public: + AssetClearHelper(const bool set_fake_user) : set_fake_user_(set_fake_user) + { + } + void operator()(PointerRNAVec &ids); void reportResults(const bContext *C, ReportList &reports) const; @@ -191,10 +240,16 @@ void AssetClearHelper::operator()(PointerRNAVec &ids) continue; } - if (ED_asset_clear_id(id)) { - stats.tot_cleared++; - stats.last_id = id; + if (!ED_asset_clear_id(id)) { + continue; } + + if (set_fake_user_) { + id_fake_user_set(id); + } + + stats.tot_cleared++; + stats.last_id = id; } } @@ -234,7 +289,8 @@ static int asset_clear_exec(bContext *C, wmOperator *op) { PointerRNAVec ids = asset_operation_get_ids_from_context(C); - AssetClearHelper clear_helper; + const bool set_fake_user = RNA_boolean_get(op->ptr, "set_fake_user"); + AssetClearHelper clear_helper(set_fake_user); clear_helper(ids); clear_helper.reportResults(C, *op->reports); @@ -248,18 +304,39 @@ static int asset_clear_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static char *asset_clear_get_description(struct bContext *UNUSED(C), + struct wmOperatorType *UNUSED(op), + struct PointerRNA *values) +{ + const bool set_fake_user = RNA_boolean_get(values, "set_fake_user"); + if (!set_fake_user) { + return nullptr; + } + + return BLI_strdup( + "Delete all asset metadata, turning the selected asset data-blocks back into normal " + "data-blocks, and set Fake User to ensure the data-blocks will still be saved"); +} + static void ASSET_OT_clear(wmOperatorType *ot) { ot->name = "Clear Asset"; ot->description = "Delete all asset metadata and turn the selected asset data-blocks back into normal " "data-blocks"; + ot->get_description = asset_clear_get_description; ot->idname = "ASSET_OT_clear"; ot->exec = asset_clear_exec; - ot->poll = asset_operation_poll; + ot->poll = asset_clear_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, + "set_fake_user", + false, + "Set Fake User", + "Ensure the data-block is saved, even when it is no longer marked as asset"); } /* -------------------------------------------------------------------- */ From 3e78c9e5bb179e84d542bc698fc6f6d7c111d1e1 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 28 Sep 2021 10:23:57 -0300 Subject: [PATCH 0291/1500] Fix T91766: NLA Editor - Segmentation fault on strip resize NLA and Dope Sheet use a specific transform operation to scale. Unlike the conventional resize operation, `TIME_SCALE` operates on `td->val`. This is a bit outside the convention of transform operators. The expected thing in this case would be to work in `td->loc` and use the conventional resize operator. But for now, to fix the problem, use `td->loc` in the `TIME_SCALE` operation. This commit also brings a cleanup in the style of some comments and removing unnecessary `memset`. --- .../editors/transform/transform_convert_nla.c | 44 +++++-------------- .../transform/transform_mode_timescale.c | 2 +- 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index 7e5b80c2453..acef8a666e3 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -208,30 +208,18 @@ void createTransNlaData(bContext *C, TransInfo *t) /* just set tdn to assume that it only has one handle for now */ tdn->handle = -1; - /* now, link the transform data up to this data */ + /* Now, link the transform data up to this data. */ + td->loc = tdn->h1; + copy_v3_v3(td->iloc, tdn->h1); + if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) { - td->loc = tdn->h1; - copy_v3_v3(td->iloc, tdn->h1); - - /* store all the other gunk that is required by transform */ + /* Store all the other gunk that is required by transform. */ copy_v3_v3(td->center, center); - memset(td->axismtx, 0, sizeof(td->axismtx)); td->axismtx[2][2] = 1.0f; - - td->ext = NULL; - td->val = NULL; - td->flag |= TD_SELECTED; - td->dist = 0.0f; - unit_m3(td->mtx); unit_m3(td->smtx); } - else { - /* time scaling only needs single value */ - td->val = &tdn->h1[0]; - td->ival = tdn->h1[0]; - } td->extra = tdn; td++; @@ -241,30 +229,18 @@ void createTransNlaData(bContext *C, TransInfo *t) * then we're doing both, otherwise, only end */ tdn->handle = (tdn->handle) ? 2 : 1; - /* now, link the transform data up to this data */ + /* Now, link the transform data up to this data. */ + td->loc = tdn->h2; + copy_v3_v3(td->iloc, tdn->h2); + if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) { - td->loc = tdn->h2; - copy_v3_v3(td->iloc, tdn->h2); - - /* store all the other gunk that is required by transform */ + /* Store all the other gunk that is required by transform. */ copy_v3_v3(td->center, center); - memset(td->axismtx, 0, sizeof(td->axismtx)); td->axismtx[2][2] = 1.0f; - - td->ext = NULL; - td->val = NULL; - td->flag |= TD_SELECTED; - td->dist = 0.0f; - unit_m3(td->mtx); unit_m3(td->smtx); } - else { - /* time scaling only needs single value */ - td->val = &tdn->h2[0]; - td->ival = tdn->h2[0]; - } td->extra = tdn; td++; diff --git a/source/blender/editors/transform/transform_mode_timescale.c b/source/blender/editors/transform/transform_mode_timescale.c index 50fd714727b..0a7ae54982e 100644 --- a/source/blender/editors/transform/transform_mode_timescale.c +++ b/source/blender/editors/transform/transform_mode_timescale.c @@ -87,7 +87,7 @@ static void applyTimeScaleValue(TransInfo *t, float value) } /* now, calculate the new value */ - *(td->val) = ((td->ival - startx) * fac) + startx; + td->loc[0] = ((td->iloc[0] - startx) * fac) + startx; } } } From d2004326a1f96f85cb1b6f7c57712de8998ecca0 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 28 Sep 2021 15:52:04 +0200 Subject: [PATCH 0292/1500] Geometry Nodes: remove empty mesh component in distribute node output --- source/blender/blenkernel/BKE_geometry_set.hh | 2 ++ source/blender/blenkernel/intern/geometry_set.cc | 13 +++++++++++++ .../nodes/node_geo_distribute_points_on_faces.cc | 5 +++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index f6a58e57298..4954b1d5ab2 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -281,6 +281,8 @@ struct GeometrySet { return this->remove(Component::static_type); } + void keep_only(const blender::Span component_types); + void add(const GeometryComponent &component); blender::Vector get_components_for_read() const; diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index e8e2f64d6e1..400e0fda518 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -152,6 +152,19 @@ void GeometrySet::remove(const GeometryComponentType component_type) components_.remove(component_type); } +/** + * Remove all geometry components with types that are not in the provided list. + */ +void GeometrySet::keep_only(const blender::Span component_types) +{ + for (auto it = components_.keys().begin(); it != components_.keys().end(); ++it) { + const GeometryComponentType type = *it; + if (!component_types.contains(type)) { + components_.remove(it); + } + } +} + void GeometrySet::add(const GeometryComponent &component) { BLI_assert(!components_.contains(component.type())); diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 3f2541dcded..f27544cbdda 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -523,8 +523,6 @@ static void point_distribution_calculate(GeometrySet &geometry_set, compute_attribute_outputs( mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs); - - geometry_set.replace_mesh(nullptr); } static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params) @@ -551,6 +549,9 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { point_distribution_calculate( geometry_set, selection_field, method, seed, attribute_outputs, params); + /* Keep instances because the original geometry set may contain instances that are processed as + * well. */ + geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES}); }); params.set_output("Points", std::move(geometry_set)); From 52a702468a59f1945ecfcf6dde6bccf648a27d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 28 Sep 2021 16:07:18 +0200 Subject: [PATCH 0293/1500] Asset Catalog Service: add function to change catalog path Add `AssetCatalogService::update_catalog_path()` to change the catalog path of the given catalog, and also change the path of all the catalogs contained within the given catalog. Rebuilds the tree structure for the UI, but does not save the new catalog definitions to disk. No user-facing changes, just backend preparation for UI work. --- .../blender/blenkernel/BKE_asset_catalog.hh | 14 ++++++ .../blenkernel/intern/asset_catalog.cc | 41 ++++++++++++++++ .../blenkernel/intern/asset_catalog_test.cc | 48 +++++++++++++++++++ 3 files changed, 103 insertions(+) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 7b54d7cf572..9b8b99f8f6d 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -118,6 +118,11 @@ class AssetCatalogService { * written. */ void delete_catalog(CatalogID catalog_id); + /** + * Update the catalog path, also updating the catalog path of all sub-catalogs. + */ + void update_catalog_path(CatalogID catalog_id, const CatalogPath &new_catalog_path); + AssetCatalogTree *get_catalog_tree(); /** Return true only if there are no catalogs known. */ @@ -300,6 +305,15 @@ class AssetCatalog { bool is_deleted = false; } flags; + /** + * \return true only if this catalog's path is contained within the given path. + * When this catalog's path is equal to the given path, return true as well. + * + * Note that non-normalized paths (so for example starting or ending with a slash) are not + * supported, and result in undefined behaviour. + */ + bool is_contained_in(const CatalogPath &other_path) const; + /** * Create a new Catalog with the given path, auto-generating a sensible catalog simple-name. * diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 8f57f96e771..05e27a93621 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -99,6 +99,25 @@ void AssetCatalogService::delete_catalog(CatalogID catalog_id) this->rebuild_tree(); } +void AssetCatalogService::update_catalog_path(CatalogID catalog_id, + const CatalogPath &new_catalog_path) +{ + AssetCatalog *renamed_cat = this->find_catalog(catalog_id); + const CatalogPath old_cat_path = renamed_cat->path; + + for (auto &catalog_uptr : catalogs_.values()) { + AssetCatalog *cat = catalog_uptr.get(); + if (!cat->is_contained_in(old_cat_path)) { + continue; + } + + const CatalogPath path_suffix = cat->path.substr(old_cat_path.length()); + cat->path = new_catalog_path + path_suffix; + } + + this->rebuild_tree(); +} + AssetCatalog *AssetCatalogService::create_catalog(const CatalogPath &catalog_path) { std::unique_ptr catalog = AssetCatalog::from_path(catalog_path); @@ -756,4 +775,26 @@ CatalogPath AssetCatalog::cleanup_path(const CatalogPath &path) return clean_path; } +bool AssetCatalog::is_contained_in(const CatalogPath &other_path) const +{ + if (other_path.empty()) { + return true; + } + + if (this->path == other_path) { + return true; + } + + /* To be a child path of 'other_path', our path must be at least a separator and another + * character longer. */ + if (this->path.length() < other_path.length() + 2) { + return false; + } + + const StringRef this_path(this->path); + const bool prefix_ok = this_path.startswith(other_path); + const char next_char = this_path[other_path.length()]; + return prefix_ok && next_char == AssetCatalogService::PATH_SEPARATOR; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 5177abd2820..b36c15940b8 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -711,6 +711,37 @@ TEST_F(AssetCatalogTest, delete_catalog_write_to_disk) EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); } +TEST_F(AssetCatalogTest, update_catalog_path) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA); + const CatalogPath orig_path = orig_cat->path; + + service.update_catalog_path(UUID_POSES_RUZENA, "charlib/Ružena"); + + EXPECT_EQ(nullptr, service.find_catalog_by_path(orig_path)) + << "The original (pre-rename) path should not be associated with a catalog any more."; + + const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA); + ASSERT_NE(nullptr, renamed_cat); + ASSERT_EQ(orig_cat, renamed_cat) << "Changing the path should not reallocate the catalog."; + EXPECT_EQ(orig_cat->simple_name, renamed_cat->simple_name) + << "Changing the path should not change the simple name."; + EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id) + << "Changing the path should not change the catalog ID."; + + EXPECT_EQ("charlib/Ružena", renamed_cat->path) + << "Changing the path should change the path. Surprise."; + + EXPECT_EQ("charlib/Ružena/hand", service.find_catalog(UUID_POSES_RUZENA_HAND)->path) + << "Changing the path should update children."; + EXPECT_EQ("charlib/Ružena/face", service.find_catalog(UUID_POSES_RUZENA_FACE)->path) + << "Changing the path should update children."; +} + TEST_F(AssetCatalogTest, merge_catalog_files) { const CatalogFilePath cdf_dir = create_temp_path(); @@ -813,4 +844,21 @@ TEST_F(AssetCatalogTest, order_by_path) } } +TEST_F(AssetCatalogTest, is_contained_in) +{ + const AssetCatalog cat(BLI_uuid_generate_random(), "simple/path/child", ""); + + EXPECT_FALSE(cat.is_contained_in("unrelated")); + EXPECT_FALSE(cat.is_contained_in("sim")); + EXPECT_FALSE(cat.is_contained_in("simple/pathx")); + EXPECT_FALSE(cat.is_contained_in("simple/path/c")); + EXPECT_FALSE(cat.is_contained_in("simple/path/child/grandchild")); + EXPECT_FALSE(cat.is_contained_in("simple/path/")) + << "Non-normalized paths are not expected to work."; + + EXPECT_TRUE(cat.is_contained_in("")); + EXPECT_TRUE(cat.is_contained_in("simple")); + EXPECT_TRUE(cat.is_contained_in("simple/path")); +} + } // namespace blender::bke::tests From 728ae33f3720894a59009e28d33ae82f77c380b3 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 23 Sep 2021 17:20:59 +0200 Subject: [PATCH 0294/1500] Cycles: Improve handling of tile file error Expose them to the interface, and stop rendering as soon as possible. Differential Revision: https://developer.blender.org/D12617 --- intern/cycles/blender/blender_session.cpp | 39 +++++++++++++++-------- intern/cycles/blender/blender_session.h | 5 +++ intern/cycles/integrator/path_trace.cpp | 11 +++++-- intern/cycles/render/tile.cpp | 8 ++++- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 8d6db30f1a8..88edc7eafe7 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -589,6 +589,12 @@ void BlenderSession::render_frame_finish() for (string_view filename : full_buffer_files_) { session->process_full_buffer_from_disk(filename); + if (check_and_report_session_error()) { + break; + } + } + + for (string_view filename : full_buffer_files_) { path_remove(filename); } @@ -1036,20 +1042,27 @@ void BlenderSession::update_status_progress() last_progress = progress; } - if (session->progress.get_error()) { - string error = session->progress.get_error_message(); - if (error != last_error) { - /* TODO(sergey): Currently C++ RNA API doesn't let us to - * use mnemonic name for the variable. Would be nice to - * have this figured out. - * - * For until then, 1 << 5 means RPT_ERROR. - */ - b_engine.report(1 << 5, error.c_str()); - b_engine.error_set(error.c_str()); - last_error = error; - } + check_and_report_session_error(); +} + +bool BlenderSession::check_and_report_session_error() +{ + if (!session->progress.get_error()) { + return false; } + + const string error = session->progress.get_error_message(); + if (error != last_error) { + /* TODO(sergey): Currently C++ RNA API doesn't let us to use mnemonic name for the variable. + * Would be nice to have this figured out. + * + * For until then, 1 << 5 means RPT_ERROR. */ + b_engine.report(1 << 5, error.c_str()); + b_engine.error_set(error.c_str()); + last_error = error; + } + + return true; } void BlenderSession::tag_update() diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index 11e2657a325..fb0e5252e3b 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -147,6 +147,11 @@ class BlenderSession { protected: void stamp_view_layer_metadata(Scene *scene, const string &view_layer_name); + /* Check whether session error happenned. + * If so, it is reported to the render engine and true is returned. + * Otherwise false is returned. */ + bool check_and_report_session_error(); + void builtin_images_load(); /* Is used after each render layer synchronization is done with the goal diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index e785c0d1b19..10d0c5e7b4c 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -801,7 +801,7 @@ void PathTrace::tile_buffer_write_to_disk() } if (!tile_manager_.write_tile(*buffers)) { - LOG(ERROR) << "Error writing tile to file."; + device_->set_error("Error writing tile to file"); } } @@ -894,7 +894,14 @@ void PathTrace::process_full_buffer_from_disk(string_view filename) DenoiseParams denoise_params; if (!tile_manager_.read_full_buffer_from_disk(filename, &full_frame_buffers, &denoise_params)) { - LOG(ERROR) << "Error reading tiles from file."; + const string error_message = "Error reading tiles from file"; + if (progress_) { + progress_->set_error(error_message); + progress_->set_cancel(error_message); + } + else { + LOG(ERROR) << error_message; + } return; } diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 580931504f3..4ab2e856c5d 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -436,7 +436,12 @@ bool TileManager::open_tile_output() return false; } - write_state_.tile_out->open(write_state_.filename, write_state_.image_spec); + if (!write_state_.tile_out->open(write_state_.filename, write_state_.image_spec)) { + LOG(ERROR) << "Error opening tile file: " << write_state_.tile_out->geterror(); + write_state_.tile_out = nullptr; + return false; + } + write_state_.num_tiles_written = 0; VLOG(3) << "Opened tile file " << write_state_.filename; @@ -497,6 +502,7 @@ bool TileManager::write_tile(const RenderBuffers &tile_buffers) TypeDesc::FLOAT, pixels)) { LOG(ERROR) << "Error writing tile " << write_state_.tile_out->geterror(); + return false; } ++write_state_.num_tiles_written; From ff7e67afd5de2d0ce6614ecfda9a70735a2479ba Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Tue, 28 Sep 2021 17:02:47 +0200 Subject: [PATCH 0295/1500] Geometry Nodes: Dashed lines for function flow Use dashes to represent the function flow (while keeping continuous lines for the data-flow). It is important to tell both flows apart (the data and the function flow). The sockets help with that, the noodles help this further. The "data flow" is evaluated at every single node. A user can inspect the output sockets of those nodes and have a glimpse at their values. The "function flow" (nodes) however is only evaluated in the geometry nodes. The noodles are not transporting data in the same sense of the "data flow". All that can be inspected are the attributes the functions depend on. Having this clearly communicated should help users to inspect the nodetrees, read and understand the different flows in the same tree. --- Known limitations: At the moment the dash lines are not equidistant: * It would be nice to get the "uv.x" to be resampled for the bezier curve so the dashes are equally distributed in the curve. * Using distance between the P3 and P0 instead of the real bezier curve length seems to be fine. --- Full disclaimer: Changes with that much of a visual impact tend to be controversial. So far the main feedback is that dashed lines can be associated to broken link, and that there are better ways to represent the flows (or different information that should be visually represented). I'm fully aware of that. However dashed lines are already used in the viewport and outliner to indicate (hierarchical) relation. Besides, other approaches (double-lines, having the data flow to be more distinct, ...) didn't pan out in the end (or didn't look as good as this). --- Impact in other editors: The compositor uses mostly a "data flow" nodetree, so no change is expected there. The shader nodetree is one that could but doesn't have to change its visual language. The shader nodetree uses mostly "function flow" with some "data flow" nodes. One can argue that it should be adapted to follow the same pattern as geometry nodes (with the new noodles and the diamond sockets). Oh the other hand, a shader nodetree has a single context. When a node depends on the "UV", there is only one UV at a time for the entire nodetree. So it can also be treated as a psedo "data flow" nodetree if we want to avoid too many changes in other parts of Blender. Differential Revision: https://developer.blender.org/D12602 --- source/blender/editors/space_node/drawnode.cc | 17 +++++++++-- .../shaders/gpu_shader_2D_nodelink_frag.glsl | 30 +++++++++++++++++++ .../shaders/gpu_shader_2D_nodelink_vert.glsl | 14 +++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index f64df0e7142..9e6662855cc 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3934,10 +3934,12 @@ static struct { uint colid_id, muted_id; uint dim_factor_id; uint thickness_id; + uint dash_factor_id; GPUVertBufRaw p0_step, p1_step, p2_step, p3_step; GPUVertBufRaw colid_step, muted_step; GPUVertBufRaw dim_factor_step; GPUVertBufRaw thickness_step; + GPUVertBufRaw dash_factor_step; uint count; bool enabled; } g_batch_link; @@ -3956,6 +3958,8 @@ static void nodelink_batch_reset() g_batch_link.inst_vbo, g_batch_link.dim_factor_id, &g_batch_link.dim_factor_step); GPU_vertbuf_attr_get_raw_data( g_batch_link.inst_vbo, g_batch_link.thickness_id, &g_batch_link.thickness_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.dash_factor_id, &g_batch_link.dash_factor_step); g_batch_link.count = 0; } @@ -4077,6 +4081,8 @@ static void nodelink_batch_init() &format_inst, "dim_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_batch_link.thickness_id = GPU_vertformat_attr_add( &format_inst, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + g_batch_link.dash_factor_id = GPU_vertformat_attr_add( + &format_inst, "dash_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM); /* Alloc max count but only draw the range we need. */ GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE); @@ -4154,7 +4160,8 @@ static void nodelink_batch_add_link(const SpaceNode *snode, bool drawarrow, bool drawmuted, float dim_factor, - float thickness) + float thickness, + float dash_factor) { /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); @@ -4175,6 +4182,7 @@ static void nodelink_batch_add_link(const SpaceNode *snode, muted[0] = drawmuted; *(float *)GPU_vertbuf_raw_step(&g_batch_link.dim_factor_step) = dim_factor; *(float *)GPU_vertbuf_raw_step(&g_batch_link.thickness_step) = thickness; + *(float *)GPU_vertbuf_raw_step(&g_batch_link.dash_factor_step) = dash_factor; if (g_batch_link.count == NODELINK_GROUP_SIZE) { nodelink_batch_draw(snode); @@ -4191,10 +4199,13 @@ void node_draw_link_bezier(const View2D *v2d, { const float dim_factor = node_link_dim_factor(v2d, link); float thickness = 1.5f; + float dash_factor = 1.0f; if (snode->edittree->type == NTREE_GEOMETRY) { if (link->fromsock && link->fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) { /* Make field links a bit thinner. */ thickness = 1.0f; + /* Draw field as dashes. */ + dash_factor = 0.75f; } } @@ -4221,7 +4232,8 @@ void node_draw_link_bezier(const View2D *v2d, drawarrow, drawmuted, dim_factor, - thickness); + thickness, + dash_factor); } else { /* Draw single link. */ @@ -4248,6 +4260,7 @@ void node_draw_link_bezier(const View2D *v2d, GPU_batch_uniform_1i(batch, "doMuted", drawmuted); GPU_batch_uniform_1f(batch, "dim_factor", dim_factor); GPU_batch_uniform_1f(batch, "thickness", thickness); + GPU_batch_uniform_1f(batch, "dash_factor", dash_factor); GPU_batch_draw(batch); } } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl index f07bd7f1d6f..55d5d941290 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl @@ -1,11 +1,41 @@ in float colorGradient; in vec4 finalColor; +in float lineU; +flat in float lineLength; +flat in float dashFactor; +flat in int isMainLine; out vec4 fragColor; +#define DASH_WIDTH 20.0 +#define ANTIALIAS 1.0 + void main() { fragColor = finalColor; + + if ((isMainLine != 0) && (dashFactor < 1.0)) { + float distance_along_line = lineLength * lineU; + float normalized_distance = fract(distance_along_line / DASH_WIDTH); + + /* Checking if `normalized_distance <= dashFactor` is already enough for a basic + * dash, however we want to handle a nice antialias. */ + + float dash_center = DASH_WIDTH * dashFactor * 0.5; + float normalized_distance_triangle = + 1.0 - abs((fract((distance_along_line - dash_center) / DASH_WIDTH)) * 2.0 - 1.0); + float t = ANTIALIAS / DASH_WIDTH; + float slope = 1.0 / (2.0 * t); + + float alpha = min(1.0, max(0.0, slope * (normalized_distance_triangle - dashFactor + t))); + + if (alpha < 0.0) { + discard; + } + + fragColor.a *= 1.0 - alpha; + } + fragColor.a *= smoothstep(1.0, 0.1, abs(colorGradient)); } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl index 2194d23a4ea..8f46c8eda4b 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl @@ -20,6 +20,7 @@ in ivec4 colid_doarrow; in ivec2 domuted; in float dim_factor; in float thickness; +in float dash_factor; uniform vec4 colors[6]; @@ -43,6 +44,7 @@ uniform bool doArrow; uniform bool doMuted; uniform float dim_factor; uniform float thickness; +uniform float dash_factor; # define colShadow colors[0] # define colStart colors[1] @@ -56,9 +58,21 @@ uniform mat4 ModelViewProjectionMatrix; out float colorGradient; out vec4 finalColor; +out float lineU; +flat out float lineLength; +flat out float dashFactor; +flat out int isMainLine; void main(void) { + /* Parameters for the dashed line. */ + isMainLine = expand.y != 1.0 ? 0 : 1; + dashFactor = dash_factor; + /* Approximate line length, no need for real bezier length calculation. */ + lineLength = distance(P0, P3); + /* TODO: Incorrect U, this leads to non-uniform dash distribution. */ + lineU = uv.x; + float t = uv.x; float t2 = t * t; float t2_3 = 3.0 * t2; From 640c4ace0b0d9c7766ddfe5b658f966ba8bef3a5 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 28 Sep 2021 11:49:53 +0200 Subject: [PATCH 0296/1500] Cycles: Disable tile-level denoising Only do denoising on the full-frame result. Saves render time. Can re-consider in the future when/if we'll want to support denoising during rendering (similar to viewport) to allow artists to stop rendering when they see image to be good enough. Until there is a design for that workflow stick to a more time efficient rendering. Differential Revision: https://developer.blender.org/D12662 --- intern/cycles/integrator/render_scheduler.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp index 3e5b3417a6a..322d3d5f94c 100644 --- a/intern/cycles/integrator/render_scheduler.cpp +++ b/intern/cycles/integrator/render_scheduler.cpp @@ -384,7 +384,7 @@ bool RenderScheduler::set_postprocess_render_work(RenderWork *render_work) } if (denoiser_params_.use && !state_.last_work_tile_was_denoised) { - render_work->tile.denoise = true; + render_work->tile.denoise = !tile_manager_.has_multiple_tiles(); any_scheduled = true; } @@ -903,6 +903,12 @@ bool RenderScheduler::work_need_denoise(bool &delayed, bool &ready_to_display) return false; } + /* When multiple tiles are used the full frame will be denoised. + * Avoid per-tile denoising to save up render time. */ + if (tile_manager_.has_multiple_tiles()) { + return false; + } + if (done()) { /* Always denoise at the last sample. */ return true; From f17ca53cdd2f3e3f4ccfcae404deebb02ead158d Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 28 Sep 2021 14:37:59 +0200 Subject: [PATCH 0297/1500] Fix wrong update with shadow catcher and transparent film This change fixes an issue when scene has a shadow catcher and film is configured to be transparent. Starting viewport render and making the background non-transparent will cause bad memory access (wrong render and possibly crash). Film passes depends on transparency of background, so check for this. Demo file: F10650585 Differential Revision: https://developer.blender.org/D12666 --- intern/cycles/render/film.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 8e14b338bd3..ad3336ca089 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -434,7 +434,8 @@ void Film::update_passes(Scene *scene, bool add_sample_count_pass) const ObjectManager *object_manager = scene->object_manager; Integrator *integrator = scene->integrator; - if (!is_modified() && !object_manager->need_update() && !integrator->is_modified()) { + if (!is_modified() && !object_manager->need_update() && !integrator->is_modified() && + !background->is_modified()) { return; } From 34ba6968b2d4b4803652c3c109963a2de4b070ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 28 Sep 2021 17:32:54 +0200 Subject: [PATCH 0298/1500] Cleanup: asset catalog service, remove obsolete `write_to_disk` function Remove `AssetCatalogService::write_to_disk()` function. It has been superseded by `write_to_disk_on_blendfile_save()`; the handful of test functions that called the old function have been adjusted to use the new one. No functional changes to Blender itself. --- .../blender/blenkernel/BKE_asset_catalog.hh | 11 +-------- .../blenkernel/intern/asset_catalog.cc | 23 +------------------ .../blenkernel/intern/asset_catalog_test.cc | 10 ++++---- 3 files changed, 7 insertions(+), 37 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 9b8b99f8f6d..cae476708cd 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -64,15 +64,6 @@ class AssetCatalogService { /** Load asset catalog definitions from the given file or directory. */ void load_from_disk(const CatalogFilePath &file_or_directory_path); - /** - * Write the catalog definitions to disk. - * The provided directory path is only used when there is no CDF loaded from disk yet but assets - * still have to be saved. - * - * Return true on success, which either means there were no in-memory categories to save, or the - * save was successful. */ - bool write_to_disk(const CatalogFilePath &directory_for_new_files); - /** * Write the catalog definitions to disk in response to the blend file being saved. * @@ -90,7 +81,7 @@ class AssetCatalogService { * * Return true on success, which either means there were no in-memory categories to save, * or the save was successful. */ - bool write_to_disk_on_blendfile_save(const char *blend_file_path); + bool write_to_disk_on_blendfile_save(const CatalogFilePath &blend_file_path); /** * Merge on-disk changes into the in-memory asset catalogs. diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 05e27a93621..d98d83c477f 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -268,31 +268,10 @@ void AssetCatalogService::merge_from_disk_before_writing() catalog_parsed_callback); } -bool AssetCatalogService::write_to_disk(const CatalogFilePath &directory_for_new_files) +bool AssetCatalogService::write_to_disk_on_blendfile_save(const CatalogFilePath &blend_file_path) { /* TODO(Sybren): expand to support multiple CDFs. */ - if (!catalog_definition_file_) { - if (catalogs_.is_empty() && deleted_catalogs_.is_empty()) { - /* Avoid saving anything, when there is nothing to save. */ - return true; /* Writing nothing when there is nothing to write is still a success. */ - } - - /* A CDF has to be created to contain all current in-memory catalogs. */ - const CatalogFilePath cdf_path = asset_definition_default_file_path_from_dir( - directory_for_new_files); - catalog_definition_file_ = construct_cdf_in_memory(cdf_path); - } - - merge_from_disk_before_writing(); - return catalog_definition_file_->write_to_disk(); -} - -bool AssetCatalogService::write_to_disk_on_blendfile_save(const char *blend_file_path) -{ - /* TODO(Sybren): deduplicate this and write_to_disk(...); maybe the latter function isn't even - * necessary any more. */ - /* - Already loaded a CDF from disk? -> Always write to that file. */ if (this->catalog_definition_file_) { merge_from_disk_before_writing(); diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index b36c15940b8..a833e7903fa 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -403,7 +403,7 @@ TEST_F(AssetCatalogTest, no_writing_empty_files) { const CatalogFilePath temp_lib_root = create_temp_path(); AssetCatalogService service(temp_lib_root); - service.write_to_disk(temp_lib_root); + service.write_to_disk_on_blendfile_save(temp_lib_root + "phony.blend"); const CatalogFilePath default_cdf_path = temp_lib_root + AssetCatalogService::DEFAULT_CATALOG_FILENAME; @@ -574,7 +574,7 @@ TEST_F(AssetCatalogTest, create_first_catalog_from_scratch) EXPECT_FALSE(BLI_exists(temp_lib_root.c_str())); /* Writing to disk should create the directory + the default file. */ - service.write_to_disk(temp_lib_root); + service.write_to_disk_on_blendfile_save(temp_lib_root + "phony.blend"); EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str())); const CatalogFilePath definition_file_path = temp_lib_root + "/" + @@ -625,7 +625,7 @@ TEST_F(AssetCatalogTest, create_catalog_after_loading_file) << "expecting newly added catalog to not yet be saved to " << temp_lib_root; /* Write and reload the catalog file. */ - service.write_to_disk(temp_lib_root); + service.write_to_disk_on_blendfile_save(temp_lib_root + "phony.blend"); AssetCatalogService reloaded_service(temp_lib_root); reloaded_service.load_from_disk(); EXPECT_NE(nullptr, reloaded_service.find_catalog(UUID_POSES_ELLIE)) @@ -758,7 +758,7 @@ TEST_F(AssetCatalogTest, merge_catalog_files) ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str())); // Overwrite the modified file. This should merge the on-disk file with our catalogs. - service.write_to_disk(cdf_dir); + service.write_to_disk_on_blendfile_save(cdf_dir + "phony.blend"); AssetCatalogService loaded_service(cdf_dir); loaded_service.load_from_disk(); @@ -788,7 +788,7 @@ TEST_F(AssetCatalogTest, backups) AssetCatalogService service(cdf_dir); service.load_from_disk(); service.delete_catalog(UUID_POSES_ELLIE); - service.write_to_disk(cdf_dir); + service.write_to_disk_on_blendfile_save(cdf_dir + "phony.blend"); const CatalogFilePath backup_path = writable_cdf_file + "~"; ASSERT_TRUE(BLI_is_file(backup_path.c_str())); From 53fa4801a0f8e03248d3f949cf4cd49792949698 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 28 Sep 2021 16:38:24 +0200 Subject: [PATCH 0299/1500] Fix: Fluid/Cloth/DynamicPaint: Only share pointcaches in CoW case. Particle copying code was already properly sharing pointcache between orig data and its copy only when `LIB_ID_COPY_SET_COPIED_ON_WRITE` is set, do the same for the other point cache users. Using `LIB_ID_CREATE_NO_MAIN` here is waaaaaaay to much wide scope for such a dangerous/advanced behavior, that kind of things has to be strictly restricted in scope. --- source/blender/blenkernel/intern/dynamicpaint.c | 2 +- source/blender/blenkernel/intern/fluid.c | 2 +- source/blender/modifiers/intern/MOD_cloth.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 2bad47f2ed1..9083c507160 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -1231,7 +1231,7 @@ void dynamicPaint_Modifier_copy(const struct DynamicPaintModifierData *pmd, /* copy existing surfaces */ for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { DynamicPaintSurface *t_surface = dynamicPaint_createNewSurface(tpmd->canvas, NULL); - if (flag & LIB_ID_CREATE_NO_MAIN) { + if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) { /* TODO(sergey): Consider passing some tips to the surface * creation to avoid this allocate-and-free cache behavior. */ BKE_ptcache_free_list(&t_surface->ptcaches); diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 1324b37f39c..e272b71acb8 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -5094,7 +5094,7 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd, /* pointcache options */ BKE_ptcache_free_list(&(tfds->ptcaches[0])); - if (flag & LIB_ID_CREATE_NO_MAIN) { + if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) { /* Share the cache with the original object's modifier. */ tfmd->modifier.flag |= eModifierFlag_SharedCaches; tfds->point_cache[0] = fds->point_cache[0]; diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index fa2f70e1a9c..cf0658d4b39 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -194,7 +194,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla } BKE_ptcache_free_list(&tclmd->ptcaches); - if (flag & LIB_ID_CREATE_NO_MAIN) { + if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) { /* Share the cache with the original object's modifier. */ tclmd->modifier.flag |= eModifierFlag_SharedCaches; tclmd->ptcaches = clmd->ptcaches; From 330a04d7c7928c213c121bedcab9dd77a16d106e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 28 Sep 2021 17:37:35 +0200 Subject: [PATCH 0300/1500] Fix T91393: Duplicating an action with python crashes Blender. Own mistake when making NLA overridable, instead of assuming things about the ID owner of the animation data being processed, properly return and use the one found by `ED_actedit_animdata_from_context`. --- source/blender/editors/include/ED_anim_api.h | 2 +- .../editors/space_action/action_data.c | 51 +++++++++++-------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 6a0a42ee77b..e9601220f2e 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -861,7 +861,7 @@ void ED_operatormacros_action(void); /* XXX: Should we be doing these here, or at all? */ /* Action Editor - Action Management */ -struct AnimData *ED_actedit_animdata_from_context(struct bContext *C); +struct AnimData *ED_actedit_animdata_from_context(struct bContext *C, struct ID **r_adt_id_owner); void ED_animedit_unlink_action(struct bContext *C, struct ID *id, struct AnimData *adt, diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index 717d87c4972..a4fd2d81778 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -71,7 +71,7 @@ /* ACTION CREATION */ /* Helper function to find the active AnimData block from the Action Editor context */ -AnimData *ED_actedit_animdata_from_context(bContext *C) +AnimData *ED_actedit_animdata_from_context(bContext *C, ID **r_adt_id_owner) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); Object *ob = CTX_data_active_object(C); @@ -82,12 +82,18 @@ AnimData *ED_actedit_animdata_from_context(bContext *C) /* Currently, "Action Editor" means object-level only... */ if (ob) { adt = ob->adt; + if (r_adt_id_owner) { + *r_adt_id_owner = &ob->id; + } } } else if (saction->mode == SACTCONT_SHAPEKEY) { Key *key = BKE_key_from_object(ob); if (key) { adt = key->adt; + if (r_adt_id_owner) { + *r_adt_id_owner = &key->id; + } } } @@ -212,6 +218,7 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) bAction *oldact = NULL; AnimData *adt = NULL; + ID *adt_id_owner = NULL; /* hook into UI */ UI_context_active_but_prop_get_templateID(C, &ptr, &prop); @@ -225,13 +232,14 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) /* stash the old action to prevent it from being lost */ if (ptr.type == &RNA_AnimData) { adt = ptr.data; + adt_id_owner = ptr.owner_id; } else if (ptr.type == &RNA_SpaceDopeSheetEditor) { - adt = ED_actedit_animdata_from_context(C); + adt = ED_actedit_animdata_from_context(C, &adt_id_owner); } } else { - adt = ED_actedit_animdata_from_context(C); + adt = ED_actedit_animdata_from_context(C, &adt_id_owner); oldact = adt->action; } { @@ -239,8 +247,9 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) /* Perform stashing operation - But only if there is an action */ if (adt && oldact) { + BLI_assert(adt_id_owner != NULL); /* stash the action */ - if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ptr.owner_id))) { + if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) { /* The stash operation will remove the user already * (and unlink the action from the AnimData action slot). * Hence, we must unset the ref to the action in the @@ -306,7 +315,7 @@ static bool action_pushdown_poll(bContext *C) { if (ED_operator_action_active(C)) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); /* Check for AnimData, Actions, and that tweak-mode is off. */ if (adt && saction->action) { @@ -326,7 +335,8 @@ static bool action_pushdown_poll(bContext *C) static int action_pushdown_exec(bContext *C, wmOperator *op) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = ED_actedit_animdata_from_context(C); + ID *adt_id_owner = NULL; + AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner); /* Do the deed... */ if (adt) { @@ -339,8 +349,7 @@ static int action_pushdown_exec(bContext *C, wmOperator *op) } /* action can be safely added */ - const Object *ob = CTX_data_active_object(C); - BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(ob)); + BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner)); /* Stop displaying this action in this editor * NOTE: The editor itself doesn't set a user... @@ -373,7 +382,8 @@ void ACTION_OT_push_down(wmOperatorType *ot) static int action_stash_exec(bContext *C, wmOperator *op) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = ED_actedit_animdata_from_context(C); + ID *adt_id_owner = NULL; + AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner); /* Perform stashing operation */ if (adt) { @@ -385,8 +395,7 @@ static int action_stash_exec(bContext *C, wmOperator *op) } /* stash the action */ - Object *ob = CTX_data_active_object(C); - if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ob))) { + if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) { /* The stash operation will remove the user already, * so the flushing step later shouldn't double up * the user-count fixes. Hence, we must unset this ref @@ -439,7 +448,7 @@ void ACTION_OT_stash(wmOperatorType *ot) static bool action_stash_create_poll(bContext *C) { if (ED_operator_action_active(C)) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); /* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */ /* NOTE: unlike for pushdown, @@ -471,7 +480,8 @@ static bool action_stash_create_poll(bContext *C) static int action_stash_create_exec(bContext *C, wmOperator *op) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = ED_actedit_animdata_from_context(C); + ID *adt_id_owner = NULL; + AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner); /* Check for no action... */ if (saction->action == NULL) { @@ -488,8 +498,7 @@ static int action_stash_create_exec(bContext *C, wmOperator *op) } /* stash the action */ - Object *ob = CTX_data_active_object(C); - if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ob))) { + if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) { bAction *new_action = NULL; /* Create new action not based on the old one @@ -636,7 +645,7 @@ static bool action_unlink_poll(bContext *C) { if (ED_operator_action_active(C)) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); /* Only when there's an active action, in the right modes... */ if (saction->action && adt) { @@ -650,7 +659,7 @@ static bool action_unlink_poll(bContext *C) static int action_unlink_exec(bContext *C, wmOperator *op) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); bool force_delete = RNA_boolean_get(op->ptr, "force_delete"); if (adt && adt->action) { @@ -775,7 +784,7 @@ static bool action_layer_next_poll(bContext *C) { /* Action Editor's action editing modes only */ if (ED_operator_action_active(C)) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); if (adt) { /* only allow if we're in tweak-mode, and there's something above us... */ if (adt->flag & ADT_NLA_EDIT_ON) { @@ -809,7 +818,7 @@ static bool action_layer_next_poll(bContext *C) static int action_layer_next_exec(bContext *C, wmOperator *op) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); NlaTrack *act_track; Scene *scene = CTX_data_scene(C); @@ -886,7 +895,7 @@ static bool action_layer_prev_poll(bContext *C) { /* Action Editor's action editing modes only */ if (ED_operator_action_active(C)) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); if (adt) { if (adt->flag & ADT_NLA_EDIT_ON) { /* Tweak Mode: We need to check if there are any tracks below the active one @@ -920,7 +929,7 @@ static bool action_layer_prev_poll(bContext *C) static int action_layer_prev_exec(bContext *C, wmOperator *op) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); NlaTrack *act_track; NlaTrack *nlt; From 3acf3e9e2ff5bb8a93fcad2df925058d3e7ee748 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 28 Sep 2021 17:46:20 +0200 Subject: [PATCH 0301/1500] Geometry Nodes: don't realize instances in Instance on Points node Part of T91672. --- .../blender/nodes/geometry/nodes/node_geo_instance_on_points.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 21a130da8f9..cf9f04f3fe8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -165,8 +165,6 @@ static void geo_node_instance_on_points_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Points"); GeometrySet geometry_set_out; - geometry_set = geometry_set_realize_instances(geometry_set); - InstancesComponent &instances = geometry_set_out.get_component_for_write(); if (geometry_set.has()) { From 10d926cd4aae8b74e0b85d00d899643a0ff113cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 28 Sep 2021 17:47:16 +0200 Subject: [PATCH 0302/1500] Cleanup: asset catalogs, move file header definition to constant Define the standard catalog definition file header in a constant, separating it from the function that writes the entire file. No functional changes. --- source/blender/blenkernel/BKE_asset_catalog.hh | 1 + source/blender/blenkernel/intern/asset_catalog.cc | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index cae476708cd..8e6aeec5204 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -231,6 +231,7 @@ class AssetCatalogDefinitionFile { * Later versioning code may be added to handle older files. */ const static int SUPPORTED_VERSION; const static std::string VERSION_MARKER; + const static std::string HEADER; CatalogFilePath file_path; diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index d98d83c477f..e16537fcb7f 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -48,6 +48,13 @@ const int AssetCatalogDefinitionFile::SUPPORTED_VERSION = 1; * that starts with "VERSION". */ const std::string AssetCatalogDefinitionFile::VERSION_MARKER = "VERSION "; +const std::string AssetCatalogDefinitionFile::HEADER = + "# This is an Asset Catalog Definition file for Blender.\n" + "#\n" + "# Empty lines and lines starting with `#` will be ignored.\n" + "# The first non-ignored line should be the version indicator.\n" + "# Other lines are of the format \"UUID:catalog/path/for/assets:simple catalog name\"\n"; + AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_root) : asset_library_root_(asset_library_root) { @@ -652,13 +659,7 @@ bool AssetCatalogDefinitionFile::write_to_disk_unsafe(const CatalogFilePath &des // the file again. // Write the header. - // TODO(@sybren): move the header definition to some other place. - output << "# This is an Asset Catalog Definition file for Blender." << std::endl; - output << "#" << std::endl; - output << "# Empty lines and lines starting with `#` will be ignored." << std::endl; - output << "# The first non-ignored line should be the version indicator." << std::endl; - output << "# Other lines are of the format \"UUID:catalog/path/for/assets:simple catalog name\"" - << std::endl; + output << HEADER; output << "" << std::endl; output << VERSION_MARKER << SUPPORTED_VERSION << std::endl; output << "" << std::endl; From 6ee2f2da96e1137d64f782801d5616ceb47ebd41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 28 Sep 2021 17:47:28 +0200 Subject: [PATCH 0303/1500] Cleanup: asset catalog, remove obsolete TODO No functional changes. --- source/blender/blenkernel/intern/asset_catalog.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index e16537fcb7f..d54e3b93dda 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -664,9 +664,7 @@ bool AssetCatalogDefinitionFile::write_to_disk_unsafe(const CatalogFilePath &des output << VERSION_MARKER << SUPPORTED_VERSION << std::endl; output << "" << std::endl; - // Write the catalogs. - // TODO(@sybren): order them by Catalog ID or Catalog Path. - + // Write the catalogs, ordered by path (primary) and UUID (secondary). AssetCatalogOrderedSet catalogs_by_path; for (const AssetCatalog *catalog : catalogs_.values()) { if (catalog->flags.is_deleted) { From e6941651231a4ab9bd416ca01d58e589aadb772c Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Tue, 28 Sep 2021 17:55:21 +0200 Subject: [PATCH 0304/1500] VSE: fix versioning code to the new maximum zoom level (128) Since we bumped the number of channels to 128, I forgot to doversion the editors. So new files (new editors) would have this right, but not existing files. Fixup to: 8fecc2a8525467ee2fbbaae16ddbbc10b3050d46 --- .../blender/blenloader/intern/versioning_300.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 26903fff70f..b88978d1683 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1520,5 +1520,21 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + LISTBASE_FOREACH (ARegion *, region, regionbase) { + if (region->regiontype == RGN_TYPE_WINDOW) { + region->v2d.max[1] = MAXSEQ; + } + } + } + } + } + } } } From f35ea668a11d80fb37526f637f95db2c79eb1e91 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 28 Sep 2021 11:03:12 -0500 Subject: [PATCH 0305/1500] Geometry Nodes: Move more nodes to legacy - Curve to Points: Needs output sockets - Curve Endpoitns: Needs the same output sockets - Edge Split: Should have a selection input instead - Subdivision Surface: Should not use "crease" implicitly All new versions of these nodes should also not implicitly realize instances. --- release/scripts/startup/nodeitems_builtins.py | 9 ++-- source/blender/blenkernel/BKE_node.h | 8 +-- .../blenloader/intern/versioning_300.c | 49 ++++++++++--------- source/blender/nodes/CMakeLists.txt | 8 +-- source/blender/nodes/NOD_static_types.h | 10 ++-- .../{ => legacy}/node_geo_curve_endpoints.cc | 3 +- .../{ => legacy}/node_geo_curve_to_points.cc | 3 +- .../nodes/{ => legacy}/node_geo_edge_split.cc | 2 +- .../node_geo_subdivision_surface.cc | 2 +- 9 files changed, 49 insertions(+), 45 deletions(-) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_curve_endpoints.cc (98%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_curve_to_points.cc (99%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_edge_split.cc (96%) rename source/blender/nodes/geometry/nodes/{ => legacy}/node_geo_subdivision_surface.cc (98%) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 88a0782a102..2c13d4eac9b 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -519,11 +519,11 @@ geometry_node_categories = [ NodeItem("GeometryNodeLegacyCurveSetHandles", poll=geometry_nodes_fields_legacy_poll), NodeItem("GeometryNodeLegacyCurveSelectHandles", poll=geometry_nodes_fields_legacy_poll), NodeItem("GeometryNodeLegacyMeshToCurve", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyCurveToPoints", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyCurveEndpoints", poll=geometry_nodes_fields_legacy_poll), NodeItem("GeometryNodeCurveToMesh"), NodeItem("GeometryNodeCurveResample"), - NodeItem("GeometryNodeCurveToPoints"), - NodeItem("GeometryNodeCurveEndpoints"), NodeItem("GeometryNodeCurveFill"), NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), @@ -576,10 +576,11 @@ geometry_node_categories = [ NodeItem("GeometryNodeMaterialReplace"), ]), GeometryNodeCategory("GEO_MESH", "Mesh", items=[ + NodeItem("GeometryNodeLegacyEdgeSplit", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeBoolean"), NodeItem("GeometryNodeTriangulate"), - NodeItem("GeometryNodeEdgeSplit"), - NodeItem("GeometryNodeSubdivisionSurface"), NodeItem("GeometryNodeMeshSubdivide"), ]), GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index eb146231b41..16d4eb51d31 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1411,12 +1411,12 @@ int ntreeTexExecTree(struct bNodeTree *ntree, * \{ */ #define GEO_NODE_TRIANGULATE 1000 -#define GEO_NODE_EDGE_SPLIT 1001 +#define GEO_NODE_LEGACY_EDGE_SPLIT 1001 #define GEO_NODE_TRANSFORM 1002 #define GEO_NODE_BOOLEAN 1003 #define GEO_NODE_LEGACY_POINT_DISTRIBUTE 1004 #define GEO_NODE_LEGACY_POINT_INSTANCE 1005 -#define GEO_NODE_SUBDIVISION_SURFACE 1006 +#define GEO_NODE_LEGACY_SUBDIVISION_SURFACE 1006 #define GEO_NODE_OBJECT_INFO 1007 #define GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE 1008 #define GEO_NODE_LEGACY_ATTRIBUTE_MATH 1009 @@ -1467,7 +1467,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_LENGTH 1054 #define GEO_NODE_LEGACY_SELECT_BY_MATERIAL 1055 #define GEO_NODE_CONVEX_HULL 1056 -#define GEO_NODE_CURVE_TO_POINTS 1057 +#define GEO_NODE_LEGACY_CURVE_TO_POINTS 1057 #define GEO_NODE_LEGACY_CURVE_REVERSE 1058 #define GEO_NODE_SEPARATE_COMPONENTS 1059 #define GEO_NODE_LEGACY_CURVE_SUBDIVIDE 1060 @@ -1479,7 +1479,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_PRIMITIVE_CIRCLE 1066 #define GEO_NODE_VIEWER 1067 #define GEO_NODE_CURVE_PRIMITIVE_LINE 1068 -#define GEO_NODE_CURVE_ENDPOINTS 1069 +#define GEO_NODE_LEGACY_CURVE_ENDPOINTS 1069 #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 #define GEO_NODE_CURVE_TRIM 1071 #define GEO_NODE_LEGACY_CURVE_SET_HANDLES 1072 diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index b88978d1683..86755cbaf95 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -682,10 +682,8 @@ static bool geometry_node_is_293_legacy(const short node_type) switch (node_type) { /* Not legacy: No attribute inputs or outputs. */ case GEO_NODE_TRIANGULATE: - case GEO_NODE_EDGE_SPLIT: case GEO_NODE_TRANSFORM: case GEO_NODE_BOOLEAN: - case GEO_NODE_SUBDIVISION_SURFACE: case GEO_NODE_IS_VIEWPORT: case GEO_NODE_MESH_SUBDIVIDE: case GEO_NODE_MESH_PRIMITIVE_CUBE: @@ -732,16 +730,16 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_COLLECTION_INFO: return false; - /* Maybe legacy: Transferred *all* attributes before, will not transfer all built-ins now. */ - case GEO_NODE_CURVE_ENDPOINTS: - case GEO_NODE_CURVE_TO_POINTS: - return false; - - /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to generate - * a mesh for all grids in the volume. */ + /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to + * generate a mesh for all grids in the volume. */ case GEO_NODE_VOLUME_TO_MESH: return false; + /* Legacy: Transferred *all* attributes before, will not transfer all built-ins now. */ + case GEO_NODE_LEGACY_CURVE_ENDPOINTS: + case GEO_NODE_LEGACY_CURVE_TO_POINTS: + return true; + /* Legacy: Attribute operation completely replaced by field nodes. */ case GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE: case GEO_NODE_LEGACY_ATTRIBUTE_MATH: @@ -778,15 +776,17 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_LEGACY_CURVE_SET_HANDLES: return true; - /* Legacy: More complex attribute inputs or outputs. */ - case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain drop-down. */ - case GEO_NODE_LEGACY_CURVE_SUBDIVIDE: /* Needs field count input. */ - case GEO_NODE_LEGACY_POINTS_TO_VOLUME: /* Needs field radius input. */ - case GEO_NODE_LEGACY_SELECT_BY_MATERIAL: /* Output anonymous attribute. */ - case GEO_NODE_LEGACY_POINT_TRANSLATE: /* Needs field inputs. */ - case GEO_NODE_LEGACY_POINT_INSTANCE: /* Needs field inputs. */ - case GEO_NODE_LEGACY_POINT_DISTRIBUTE: /* Needs field input, remove max for random mode. */ - case GEO_NODE_LEGACY_ATTRIBUTE_CONVERT: /* Attribute Capture, Store Attribute. */ + /* Legacy: More complex attribute inputs or outputs. */ + case GEO_NODE_LEGACY_SUBDIVISION_SURFACE: /* Used "crease" attribute. */ + case GEO_NODE_LEGACY_EDGE_SPLIT: /* Needs selection input version. */ + case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain drop-down. */ + case GEO_NODE_LEGACY_CURVE_SUBDIVIDE: /* Needs field count input. */ + case GEO_NODE_LEGACY_POINTS_TO_VOLUME: /* Needs field radius input. */ + case GEO_NODE_LEGACY_SELECT_BY_MATERIAL: /* Output anonymous attribute. */ + case GEO_NODE_LEGACY_POINT_TRANSLATE: /* Needs field inputs. */ + case GEO_NODE_LEGACY_POINT_INSTANCE: /* Needs field inputs. */ + case GEO_NODE_LEGACY_POINT_DISTRIBUTE: /* Needs field input, remove max for random mode. */ + case GEO_NODE_LEGACY_ATTRIBUTE_CONVERT: /* Attribute Capture, Store Attribute. */ return true; } return false; @@ -1269,7 +1269,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_SUBDIVISION_SURFACE) { + if (node->type == GEO_NODE_LEGACY_SUBDIVISION_SURFACE) { if (node->storage == NULL) { NodeGeometrySubdivisionSurface *data = MEM_callocN( sizeof(NodeGeometrySubdivisionSurface), __func__); @@ -1340,11 +1340,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } if (!MAIN_VERSION_ATLEAST(bmain, 300, 22)) { - LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { - if (ntree->type == NTREE_GEOMETRY) { - version_geometry_nodes_change_legacy_names(ntree); - } - } if (!DNA_struct_elem_find( fd->filesdna, "LineartGpencilModifierData", "bool", "use_crease_on_smooth")) { LISTBASE_FOREACH (Object *, ob, &bmain->objects) { @@ -1536,5 +1531,11 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_geometry_nodes_change_legacy_names(ntree); + } + } } } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index bb67d931963..9ce3d59c121 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -165,12 +165,15 @@ set(SRC geometry/nodes/legacy/node_geo_attribute_transfer.cc geometry/nodes/legacy/node_geo_attribute_vector_math.cc geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc + geometry/nodes/legacy/node_geo_curve_endpoints.cc geometry/nodes/legacy/node_geo_curve_reverse.cc geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc geometry/nodes/legacy/node_geo_curve_set_handles.cc geometry/nodes/legacy/node_geo_curve_spline_type.cc geometry/nodes/legacy/node_geo_curve_subdivide.cc + geometry/nodes/legacy/node_geo_curve_to_points.cc geometry/nodes/legacy/node_geo_delete_geometry.cc + geometry/nodes/legacy/node_geo_edge_split.cc geometry/nodes/legacy/node_geo_material_assign.cc geometry/nodes/legacy/node_geo_mesh_to_curve.cc geometry/nodes/legacy/node_geo_point_distribute.cc @@ -182,6 +185,7 @@ set(SRC geometry/nodes/legacy/node_geo_points_to_volume.cc geometry/nodes/legacy/node_geo_raycast.cc geometry/nodes/legacy/node_geo_select_by_material.cc + geometry/nodes/legacy/node_geo_subdivision_surface.cc geometry/nodes/node_geo_attribute_capture.cc geometry/nodes/node_geo_attribute_remove.cc @@ -192,7 +196,6 @@ set(SRC geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_convex_hull.cc geometry/nodes/node_geo_curve_sample.cc - geometry/nodes/node_geo_curve_endpoints.cc geometry/nodes/node_geo_curve_fill.cc geometry/nodes/node_geo_curve_length.cc geometry/nodes/node_geo_curve_parameter.cc @@ -206,10 +209,8 @@ set(SRC geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_fillet.cc geometry/nodes/node_geo_curve_to_mesh.cc - geometry/nodes/node_geo_curve_to_points.cc geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_distribute_points_on_faces.cc - geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_instance_on_points.cc geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_normal.cc @@ -236,7 +237,6 @@ set(SRC geometry/nodes/node_geo_set_position.cc geometry/nodes/node_geo_string_join.cc geometry/nodes/node_geo_string_to_curves.cc - geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 43d86223e9b..f94394d4986 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -275,8 +275,8 @@ DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToStri DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") DefNode(FunctionNode, FN_NODE_STRING_SUBSTRING, 0, "STRING_SUBSTRING", StringSubstring, "String Substring", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "LEGACY_ATTRIBUTE_COLOR_RAMP", LegacyAttributeColorRamp, "Attribute Color Ramp", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "LEGACY_ATTRIBUTE_COMBINE_XYZ", LegacyAttributeCombineXYZ, "Attribute Combine XYZ", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "LEGACY_ATTRIBUTE_COMPARE", LegacyAttributeCompare, "Attribute Compare", "") @@ -293,12 +293,15 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_ DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "LEGACY_ATTRIBUTE_TRANSFER", LegacyAttributeTransfer, "Attribute Transfer", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "LEGACY_ATTRIBUTE_VECTOR_MATH", LegacyAttributeVectorMath, "Attribute Vector Math", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_ENDPOINTS, 0, "LEGACY_CURVE_ENDPOINTS", LegacyCurveEndpoints, "Curve Endpoints", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_TO_POINTS, def_geo_curve_to_points, "LEGACY_CURVE_TO_POINTS", LegacyCurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_EDGE_SPLIT, 0, "LEGACY_EDGE_SPLIT", LegacyEdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_LEGACY_MATERIAL_ASSIGN, 0, "LEGACY_MATERIAL_ASSIGN", LegacyMaterialAssign, "Material Assign", "") DefNode(GeometryNode, GEO_NODE_LEGACY_MESH_TO_CURVE, 0, "LEGACY_MESH_TO_CURVE", LegacyMeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_DISTRIBUTE, def_geo_point_distribute, "LEGACY_POINT_DISTRIBUTE", LegacyPointDistribute, "Point Distribute", "") @@ -310,6 +313,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") @@ -319,7 +323,6 @@ DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bound DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "") -DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "") DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Curve Fill", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") @@ -333,10 +336,8 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Curve Fillet", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") -DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") -DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") @@ -363,7 +364,6 @@ DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", Se DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "String Join", "") DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") -DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc similarity index 98% rename from source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc index 7853c5aa04a..65d22eca39c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc @@ -212,7 +212,8 @@ void register_node_type_geo_curve_endpoints() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_endpoints_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_endpoints_exec; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc similarity index 99% rename from source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc index 1e66b340f5c..0c435d69991 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc @@ -358,7 +358,8 @@ void register_node_type_geo_curve_to_points() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_to_points_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_points_exec; ntype.draw_buttons = blender::nodes::geo_node_curve_to_points_layout; diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc similarity index 96% rename from source/blender/nodes/geometry/nodes/node_geo_edge_split.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc index 867fecea251..2ea6516996d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc @@ -82,7 +82,7 @@ void register_node_type_geo_edge_split() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0); ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec; ntype.declare = blender::nodes::geo_node_edge_split_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc similarity index 98% rename from source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc index 4541bf3569f..07d3f89bdb7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc @@ -133,7 +133,7 @@ void register_node_type_geo_subdivision_surface() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_subdivision_surface_declare; ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec; ntype.draw_buttons = blender::nodes::geo_node_subdivision_surface_layout; From b32b38b3805fea5baec551acd1a98c4a58b2bb1c Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 28 Sep 2021 18:29:02 +0200 Subject: [PATCH 0306/1500] Fix T89400: Possible to delete objects used by overrides of collections. This should not be allowed in general, added some initial call to check when user is allowed to delete a data to search for mandatory override usages... --- source/blender/blenkernel/BKE_lib_override.h | 2 ++ .../blender/blenkernel/intern/lib_override.c | 25 +++++++++++++++++++ source/blender/editors/object/object_add.c | 10 ++++++++ 3 files changed, 37 insertions(+) diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 20128e2608a..b94a1b33606 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -173,6 +173,8 @@ void BKE_lib_override_library_main_unused_cleanup(struct Main *bmain); void BKE_lib_override_library_update(struct Main *bmain, struct ID *local); void BKE_lib_override_library_main_update(struct Main *bmain); +bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id); + /* Storage (.blend file writing) part. */ /* For now, we just use a temp main list. */ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index e6ce6734e64..68675e5fc91 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -2972,6 +2972,31 @@ void BKE_lib_override_library_main_update(Main *bmain) G_MAIN = orig_gmain; } +/** In case an ID is used by another liboverride ID, user may not be allowed to delete it. */ +bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id) +{ + if (!(ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY(id))) { + return true; + } + + /* The only strong known case currently are objects used by override collections. */ + /* TODO: There are most likely other cases... This may need to be addressed in a better way at + * some point. */ + if (GS(id->name) != ID_OB) { + return true; + } + Object *ob = (Object *)id; + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (!ID_IS_OVERRIDE_LIBRARY(collection)) { + continue; + } + if (BKE_collection_has_object(collection, ob)) { + return false; + } + } + return true; +} + /** * Storage (how to store overriding data into `.blend` files). * diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 236246924a9..cc4f2acc346 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -75,6 +75,7 @@ #include "BKE_lattice.h" #include "BKE_layer.h" #include "BKE_lib_id.h" +#include "BKE_lib_override.h" #include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_light.h" @@ -2015,6 +2016,15 @@ static int object_delete_exec(bContext *C, wmOperator *op) continue; } + if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) { + /* Can this case ever happen? */ + BKE_reportf(op->reports, + RPT_WARNING, + "Cannot delete object '%s' as it used by override collections", + ob->id.name + 2); + continue; + } + if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 && BKE_library_ID_is_indirectly_used(bmain, ob)) { BKE_reportf(op->reports, From 44e4f077a9d7b5b609f6199874802675d75f7266 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 28 Sep 2021 11:36:28 -0500 Subject: [PATCH 0307/1500] Geometry Nodes: Run nodes once on unique instance data As described in T91672, often it can be much more efficient to run each node only on the unique geometry of the instances, rather than realizing all instances and potentially processing redundant data. Sometimes the performance difference can be completely smooth vs. completely unusable. Geometry nodes used to hide that choice from users by always realizing instances, but recently we have decided to expose it. So this commit makes nodes run once per unique reference in the entire tree of nested instances in their input geometries, continuing the work started in rB0559971ab377 and rBf94164d89629f0d2. For the old behavior, a realize instances node can be added before the nodes, which is done in the versioning code. Differential Revision: https://developer.blender.org/D12656 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/BKE_geometry_set.hh | 1 + .../blender/blenkernel/intern/geometry_set.cc | 10 +++ .../blenloader/intern/versioning_300.c | 75 +++++++++++++++++++ .../nodes/node_geo_attribute_capture.cc | 2 - .../nodes/node_geo_attribute_remove.cc | 2 - .../nodes/geometry/nodes/node_geo_boolean.cc | 6 +- .../geometry/nodes/node_geo_convex_hull.cc | 64 +++++++++------- .../geometry/nodes/node_geo_curve_fillet.cc | 47 ++++++++---- .../geometry/nodes/node_geo_curve_length.cc | 1 - .../geometry/nodes/node_geo_curve_resample.cc | 23 ++++-- .../geometry/nodes/node_geo_curve_to_mesh.cc | 53 ++++++++----- .../geometry/nodes/node_geo_curve_trim.cc | 43 +++++++---- .../nodes/node_geo_material_replace.cc | 8 +- .../geometry/nodes/node_geo_mesh_subdivide.cc | 47 ++++++------ .../nodes/node_geo_separate_components.cc | 10 ++- .../geometry/nodes/node_geo_triangulate.cc | 16 ++-- 17 files changed, 279 insertions(+), 131 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index b973d8b9a18..5ef0459663b 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 27 +#define BLENDER_FILE_SUBVERSION 28 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 4954b1d5ab2..724ca224cab 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -330,6 +330,7 @@ struct GeometrySet { bool has_volume() const; bool has_curve() const; bool has_realized_data() const; + bool is_empty() const; const Mesh *get_mesh_for_read() const; const PointCloud *get_pointcloud_for_read() const; diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 400e0fda518..0aac6ae3adf 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -318,6 +318,16 @@ bool GeometrySet::has_realized_data() const return this->get_component_for_read() == nullptr; } +/* Return true if the geometry set has any component that isn't empty. */ +bool GeometrySet::is_empty() const +{ + if (components_.is_empty()) { + return true; + } + return !(this->has_mesh() || this->has_curve() || this->has_pointcloud() || + this->has_instances()); +} + /* Create a new geometry set that only contains the given mesh. */ GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 86755cbaf95..3b51e40c218 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -449,6 +449,73 @@ static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const Lis #undef SEQ_SPEED_COMPRESS_IPO_Y } +static bNodeLink *find_connected_link(bNodeTree *ntree, bNodeSocket *in_socket) +{ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->tosock == in_socket) { + return link; + } + } + return NULL; +} + +static void add_realize_instances_before_socket(bNodeTree *ntree, + bNode *node, + bNodeSocket *geometry_socket) +{ + BLI_assert(geometry_socket->type == SOCK_GEOMETRY); + bNodeLink *link = find_connected_link(ntree, geometry_socket); + if (link == NULL) { + return; + } + + /* If the realize instances node is already before this socket, no need to continue. */ + if (link->fromnode->type == GEO_NODE_REALIZE_INSTANCES) { + return; + } + + bNode *realize_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_REALIZE_INSTANCES); + realize_node->parent = node->parent; + realize_node->locx = node->locx - 100; + realize_node->locy = node->locy; + nodeAddLink(ntree, link->fromnode, link->fromsock, realize_node, realize_node->inputs.first); + link->fromnode = realize_node; + link->fromsock = realize_node->outputs.first; +} + +/** + * If a node used to realize instances implicitly and will no longer do so in 3.0, add a "Realize + * Instances" node in front of it to avoid changing behavior. Don't do this if the node will be + * replaced anyway though. + */ +static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree) +{ + LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { + if (ELEM(node->type, + GEO_NODE_ATTRIBUTE_CAPTURE, + GEO_NODE_SEPARATE_COMPONENTS, + GEO_NODE_CONVEX_HULL, + GEO_NODE_CURVE_LENGTH, + GEO_NODE_BOOLEAN, + GEO_NODE_CURVE_FILLET, + GEO_NODE_CURVE_RESAMPLE, + GEO_NODE_CURVE_TO_MESH, + GEO_NODE_CURVE_TRIM, + GEO_NODE_MATERIAL_REPLACE, + GEO_NODE_MESH_SUBDIVIDE, + GEO_NODE_ATTRIBUTE_REMOVE, + GEO_NODE_TRIANGULATE)) { + bNodeSocket *geometry_socket = node->inputs.first; + add_realize_instances_before_socket(ntree, node, geometry_socket); + } + /* Also realize instances for the profile input of the curve to mesh node. */ + if (node->type == GEO_NODE_CURVE_TO_MESH) { + bNodeSocket *profile_socket = node->inputs.last; + add_realize_instances_before_socket(ntree, node, profile_socket); + } + } +} + void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) { if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) { @@ -531,6 +598,14 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 28)) { + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_geometry_nodes_add_realize_instance_nodes(ntree); + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 81629a0f8b4..43fb00a482c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -117,8 +117,6 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); - const bNode &node = params.node(); const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *) node.storage; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index 21a9a338857..f93ef6f1db3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -47,8 +47,6 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Geometry"); Vector attribute_names = params.extract_multi_input("Attribute"); - geometry_set = geometry_set_realize_instances(geometry_set); - if (geometry_set.has()) { remove_attribute( geometry_set.get_component_for_write(), params, attribute_names); diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 2a1c43a89fe..21b425c0ed4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -83,10 +83,14 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) GeometrySet set_a; if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) { set_a = params.extract_input("Geometry 1"); + if (set_a.has_instances()) { + params.error_message_add( + NodeWarningType::Info, + TIP_("Instances are not supported for the first geometry input, and will not be used")); + } /* Note that it technically wouldn't be necessary to realize the instances for the first * geometry input, but the boolean code expects the first shape for the difference operation * to be a single mesh. */ - set_a = geometry_set_realize_instances(set_a); const Mesh *mesh_in_a = set_a.get_mesh_for_read(); if (mesh_in_a != nullptr) { meshes.append(mesh_in_a); diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index f1e10e3d276..4377d32210d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -227,9 +227,13 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) return hull_from_bullet(geometry_set.get_mesh_for_read(), positions); } +/* Since only positions are read from the instances, this can be used as an internal optimization + * to avoid the cost of realizing instances before the node. But disable this for now, since + * re-enabling that optimization will be a separate step. */ +# if 0 static void read_positions(const GeometryComponent &component, - Span transforms, - Vector *r_coords) + Span transforms, + Vector *r_coords) { GVArray_Typed positions = component.attribute_get_for_read( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); @@ -265,6 +269,31 @@ static void read_curve_positions(const CurveEval &curve, } } +static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set) +{ + Vector set_groups; + bke::geometry_set_gather_instances(geometry_set, set_groups); + + Vector coords; + + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + Span transforms = set_group.transforms; + + if (set.has_pointcloud()) { + read_positions(*set.get_component_for_read(), transforms, &coords); + } + if (set.has_mesh()) { + read_positions(*set.get_component_for_read(), transforms, &coords); + } + if (set.has_curve()) { + read_curve_positions(*set.get_curve_for_read(), transforms, &coords); + } + } + return hull_from_bullet(nullptr, coords); +} +# endif + #endif /* WITH_BULLET */ static void geo_node_convex_hull_exec(GeoNodeExecParams params) @@ -272,33 +301,14 @@ static void geo_node_convex_hull_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Geometry"); #ifdef WITH_BULLET - Mesh *mesh = nullptr; - if (geometry_set.has_instances()) { - Vector set_groups; - bke::geometry_set_gather_instances(geometry_set, set_groups); - Vector coords; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + Mesh *mesh = compute_hull(geometry_set); + geometry_set.replace_mesh(mesh); + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + }); - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - Span transforms = set_group.transforms; - - if (set.has_pointcloud()) { - read_positions(*set.get_component_for_read(), transforms, &coords); - } - if (set.has_mesh()) { - read_positions(*set.get_component_for_read(), transforms, &coords); - } - if (set.has_curve()) { - read_curve_positions(*set.get_curve_for_read(), transforms, &coords); - } - } - mesh = hull_from_bullet(nullptr, coords); - } - else { - mesh = compute_hull(geometry_set); - } - params.set_output("Convex Hull", GeometrySet::create_with_mesh(mesh)); + params.set_output("Convex Hull", std::move(geometry_set)); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without Bullet")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 830cfcc8331..67ce20efd9d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -563,19 +563,16 @@ static std::unique_ptr fillet_curve(const CurveEval &input_curve, return output_curve; } -static void geo_node_fillet_exec(GeoNodeExecParams params) +static void calculate_curve_fillet(GeometrySet &geometry_set, + const GeometryNodeCurveFilletMode mode, + const Field &radius_field, + const std::optional> &count_field, + const bool limit_radius) { - GeometrySet geometry_set = params.extract_input("Curve"); - - geometry_set = bke::geometry_set_realize_instances(geometry_set); - if (!geometry_set.has_curve()) { - params.set_output("Curve", geometry_set); return; } - NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage; - const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode; FilletParam fillet_param; fillet_param.mode = mode; @@ -584,19 +581,16 @@ static void geo_node_fillet_exec(GeoNodeExecParams params) const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); fn::FieldEvaluator field_evaluator{field_context, domain_size}; - Field radius_field = params.extract_input>("Radius"); - field_evaluator.add(std::move(radius_field)); + field_evaluator.add(radius_field); if (mode == GEO_NODE_CURVE_FILLET_POLY) { - Field count_field = params.extract_input>("Count"); - field_evaluator.add(std::move(count_field)); + field_evaluator.add(*count_field); } field_evaluator.evaluate(); fillet_param.radii = &field_evaluator.get_evaluated(0); if (fillet_param.radii->is_single() && fillet_param.radii->get_internal_single() < 0.0f) { - params.set_output("Geometry", geometry_set); return; } @@ -604,13 +598,36 @@ static void geo_node_fillet_exec(GeoNodeExecParams params) fillet_param.counts = &field_evaluator.get_evaluated(1); } - fillet_param.limit_radius = params.extract_input("Limit Radius"); + fillet_param.limit_radius = limit_radius; const CurveEval &input_curve = *geometry_set.get_curve_for_read(); std::unique_ptr output_curve = fillet_curve(input_curve, fillet_param); - params.set_output("Curve", GeometrySet::create_with_curve(output_curve.release())); + geometry_set.replace_curve(output_curve.release()); } + +static void geo_node_fillet_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Curve"); + + NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage; + const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode; + + Field radius_field = params.extract_input>("Radius"); + const bool limit_radius = params.extract_input("Limit Radius"); + + std::optional> count_field; + if (mode == GEO_NODE_CURVE_FILLET_POLY) { + count_field.emplace(params.extract_input>("Count")); + } + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + calculate_curve_fillet(geometry_set, mode, radius_field, count_field, limit_radius); + }); + + params.set_output("Curve", std::move(geometry_set)); +} + } // namespace blender::nodes void register_node_type_geo_curve_fillet() diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index 8fe054633a1..ac7df35bb72 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -28,7 +28,6 @@ static void geo_node_curve_length_declare(NodeDeclarationBuilder &b) static void geo_node_curve_length_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input("Curve"); - curve_set = bke::geometry_set_realize_instances(curve_set); if (!curve_set.has_curve()) { params.set_output("Length", 0.0f); return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 208525f17f6..b8f62460069 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -202,20 +202,26 @@ static std::unique_ptr resample_curve(const CurveEval &input_curve, return output_curve; } -static void geo_node_resample_exec(GeoNodeExecParams params) +static void geometry_set_curve_resample(GeometrySet &geometry_set, + const SampleModeParam &mode_param) { - GeometrySet geometry_set = params.extract_input("Geometry"); - - geometry_set = bke::geometry_set_realize_instances(geometry_set); - if (!geometry_set.has_curve()) { - params.set_output("Geometry", GeometrySet()); return; } const CurveEval &input_curve = *geometry_set.get_curve_for_read(); + std::unique_ptr output_curve = resample_curve(input_curve, mode_param); + + geometry_set.replace_curve(output_curve.release()); +} + +static void geo_node_resample_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage; const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; + SampleModeParam mode_param; mode_param.mode = mode; if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { @@ -232,9 +238,10 @@ static void geo_node_resample_exec(GeoNodeExecParams params) mode_param.length.emplace(resolution); } - std::unique_ptr output_curve = resample_curve(input_curve, mode_param); + geometry_set.modify_geometry_sets( + [&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); }); - params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); + params.set_output("Geometry", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index 89ba635ff4b..f7cef9bbf63 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -32,41 +32,54 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) b.add_output("Mesh"); } -static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) +static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, + const GeometrySet &profile_set, + const GeoNodeExecParams ¶ms) { - GeometrySet curve_set = params.extract_input("Curve"); - GeometrySet profile_set = params.extract_input("Profile Curve"); - - curve_set = bke::geometry_set_realize_instances(curve_set); - profile_set = bke::geometry_set_realize_instances(profile_set); - - /* NOTE: Theoretically an "is empty" check would be more correct for errors. */ - if (profile_set.has_mesh() && !profile_set.has_curve()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("No curve data available in profile input")); - } - - if (!curve_set.has_curve()) { - if (curve_set.has_mesh()) { + if (!geometry_set.has_curve()) { + if (!geometry_set.is_empty()) { params.error_message_add(NodeWarningType::Warning, TIP_("No curve data available in curve input")); } - params.set_output("Mesh", GeometrySet()); return; } const CurveEval *profile_curve = profile_set.get_curve_for_read(); if (profile_curve == nullptr) { - Mesh *mesh = bke::curve_to_wire_mesh(*curve_set.get_curve_for_read()); - params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); + Mesh *mesh = bke::curve_to_wire_mesh(*geometry_set.get_curve_for_read()); + geometry_set.replace_mesh(mesh); } else { - Mesh *mesh = bke::curve_to_mesh_sweep(*curve_set.get_curve_for_read(), *profile_curve); - params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); + Mesh *mesh = bke::curve_to_mesh_sweep(*geometry_set.get_curve_for_read(), *profile_curve); + geometry_set.replace_mesh(mesh); } } +static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) +{ + GeometrySet curve_set = params.extract_input("Curve"); + GeometrySet profile_set = params.extract_input("Profile Curve"); + + if (profile_set.has_instances()) { + params.error_message_add(NodeWarningType::Error, + TIP_("Instances are not supported in the profile input")); + params.set_output("Mesh", GeometrySet()); + return; + } + + if (!profile_set.has_curve() && !profile_set.is_empty()) { + params.error_message_add(NodeWarningType::Warning, + TIP_("No curve data available in the profile input")); + } + + curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + geometry_set_curve_to_mesh(geometry_set, profile_set, params); + }); + + params.set_output("Mesh", std::move(curve_set)); +} + } // namespace blender::nodes void register_node_type_geo_curve_to_mesh() diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 2b6d25b6bf3..97043980899 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -320,28 +320,18 @@ static void trim_bezier_spline(Spline &spline, bezier_spline.resize(size); } -static void geo_node_curve_trim_exec(GeoNodeExecParams params) +static void geometry_set_curve_trim(GeometrySet &geometry_set, + const GeometryNodeCurveSampleMode mode, + const float start, + const float end) { - const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; - - GeometrySet geometry_set = params.extract_input("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); if (!geometry_set.has_curve()) { - params.set_output("Curve", std::move(geometry_set)); return; } - CurveComponent &curve_component = geometry_set.get_component_for_write(); - CurveEval &curve = *curve_component.get_for_write(); + CurveEval &curve = *geometry_set.get_curve_for_write(); MutableSpan splines = curve.splines(); - const float start = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? - params.extract_input("Start") : - params.extract_input("Start_001"); - const float end = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? params.extract_input("End") : - params.extract_input("End_001"); - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { Spline &spline = *splines[i]; @@ -382,6 +372,29 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params) splines[i]->mark_cache_invalid(); } }); +} + +static void geo_node_curve_trim_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + + GeometrySet geometry_set = params.extract_input("Curve"); + + if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) { + const float start = params.extract_input("Start"); + const float end = params.extract_input("End"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + geometry_set_curve_trim(geometry_set, mode, start, end); + }); + } + else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + const float start = params.extract_input("Start_001"); + const float end = params.extract_input("End_001"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + geometry_set_curve_trim(geometry_set, mode, start, end); + }); + } params.set_output("Curve", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc index a9c3bfc6ce0..a917434fa00 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc @@ -40,11 +40,9 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params) Material *new_material = params.extract_input("New"); GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); - if (geometry_set.has()) { - MeshComponent &mesh_component = geometry_set.get_component_for_write(); - Mesh *mesh = mesh_component.get_for_write(); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + Mesh *mesh = geometry_set.get_mesh_for_write(); if (mesh != nullptr) { for (const int i : IndexRange(mesh->totcol)) { if (mesh->mat[i] == old_material) { @@ -52,7 +50,7 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params) } } } - } + }); params.set_output("Geometry", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index 9ca74fed9a7..c436f5bd480 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -32,28 +32,9 @@ static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b) b.add_output("Geometry"); } -static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) +static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level) { - GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); - if (!geometry_set.has_mesh()) { - params.set_output("Geometry", geometry_set); - return; - } - -#ifndef WITH_OPENSUBDIV - params.error_message_add(NodeWarningType::Error, - TIP_("Disabled, Blender was compiled without OpenSubdiv")); - params.set_output("Geometry", std::move(geometry_set)); - return; -#endif - - /* See CCGSUBSURF_LEVEL_MAX for max limit. */ - const int subdiv_level = clamp_i(params.extract_input("Level"), 0, 11); - - if (subdiv_level == 0) { - params.set_output("Geometry", std::move(geometry_set)); return; } @@ -61,7 +42,7 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) /* Initialize mesh settings. */ SubdivToMeshSettings mesh_settings; - mesh_settings.resolution = (1 << subdiv_level) + 1; + mesh_settings.resolution = (1 << level) + 1; mesh_settings.use_optimal_display = false; /* Initialize subdivision settings. */ @@ -79,7 +60,6 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) /* In case of bad topology, skip to input mesh. */ if (subdiv == nullptr) { - params.set_output("Geometry", std::move(geometry_set)); return; } @@ -90,6 +70,29 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) mesh_component.replace(mesh_out); BKE_subdiv_free(subdiv); +} + +static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + +#ifndef WITH_OPENSUBDIV + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenSubdiv")); + params.set_output("Geometry", std::move(geometry_set)); + return; +#endif + + /* See CCGSUBSURF_LEVEL_MAX for max limit. */ + const int subdiv_level = clamp_i(params.extract_input("Level"), 0, 11); + + if (subdiv_level == 0) { + params.set_output("Geometry", std::move(geometry_set)); + return; + } + + geometry_set.modify_geometry_sets( + [&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); }); params.set_output("Geometry", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc index c63e4ec49d9..dafd10cee2d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc @@ -25,20 +25,18 @@ static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) b.add_output("Point Cloud"); b.add_output("Curve"); b.add_output("Volume"); + b.add_output("Instances"); } static void geo_node_separate_components_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); - /* Note that it will be possible to skip realizing instances here when instancing - * geometry directly is supported by creating corresponding geometry instances. */ - geometry_set = bke::geometry_set_realize_instances(geometry_set); - GeometrySet meshes; GeometrySet point_clouds; GeometrySet volumes; GeometrySet curves; + GeometrySet instances; if (geometry_set.has()) { meshes.add(*geometry_set.get_component_for_read()); @@ -52,11 +50,15 @@ static void geo_node_separate_components_exec(GeoNodeExecParams params) if (geometry_set.has()) { volumes.add(*geometry_set.get_component_for_read()); } + if (geometry_set.has()) { + instances.add(*geometry_set.get_component_for_read()); + } params.set_output("Mesh", meshes); params.set_output("Point Cloud", point_clouds); params.set_output("Curve", curves); params.set_output("Volume", volumes); + params.set_output("Instances", instances); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 8886c7194db..7ef0913622c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -58,14 +58,14 @@ static void geo_node_triangulate_exec(GeoNodeExecParams params) GeometryNodeTriangulateNGons ngon_method = static_cast( params.node().custom2); - geometry_set = geometry_set_realize_instances(geometry_set); - - /* #triangulate_mesh might modify the input mesh currently. */ - Mesh *mesh_in = geometry_set.get_mesh_for_write(); - if (mesh_in != nullptr) { - Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0); - geometry_set.replace_mesh(mesh_out); - } + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + /* #triangulate_mesh might modify the input mesh currently. */ + Mesh *mesh_in = geometry_set.get_mesh_for_write(); + if (mesh_in != nullptr) { + Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0); + geometry_set.replace_mesh(mesh_out); + } + }); params.set_output("Geometry", std::move(geometry_set)); } From 797064544ea3e176c511f7ff7c1303498a783182 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 28 Sep 2021 12:05:42 -0500 Subject: [PATCH 0308/1500] Geometry Nodes: Only show attribute toggle for field inputs Change the toggle to switch between an attribute and a single value to only display for inputs that are fields, as determined statically by the field inferencing added in rB61f3d4eb7c7db7. This means the field inferencing must be calculated on file load, since it's used in the UI. Differential Revision: https://developer.blender.org/D12623 --- source/blender/blenkernel/intern/node.cc | 24 ++----- source/blender/makesdna/DNA_node_types.h | 10 ++- source/blender/modifiers/intern/MOD_nodes.cc | 67 +++++++++++++------- source/blender/nodes/NOD_node_declaration.hh | 18 ++++++ 4 files changed, 76 insertions(+), 43 deletions(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index f2843e5f88e..c8ba7991f31 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -104,6 +104,7 @@ using blender::Span; using blender::Stack; using blender::Vector; using blender::VectorSet; +using blender::nodes::FieldInferencingInterface; using blender::nodes::InputSocketFieldType; using blender::nodes::NodeDeclaration; using blender::nodes::OutputFieldDependency; @@ -823,6 +824,11 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) /* TODO: should be dealt by new generic cache handling of IDs... */ ntree->previews = nullptr; + if (ntree->type == NTREE_GEOMETRY) { + /* Update field referencing for the geometry nodes modifier. */ + ntree->update |= NTREE_UPDATE_FIELD_INFERENCING; + } + /* type verification is in lib-link */ } @@ -4456,24 +4462,6 @@ void ntreeUpdateAllNew(Main *main) FOREACH_NODETREE_END; } -/** - * Information about how a node interacts with fields. - */ -struct FieldInferencingInterface { - Vector inputs; - Vector outputs; - - friend bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) - { - return a.inputs == b.inputs && a.outputs == b.outputs; - } - - friend bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b) - { - return !(a == b); - } -}; - static FieldInferencingInterface *node_field_inferencing_interface_copy( const FieldInferencingInterface &field_inferencing_interface) { diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 34c74e5bd88..f7f2ccaa7be 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -463,7 +463,15 @@ typedef struct bNodeLink { #define NTREE_CHUNKSIZE_512 512 #define NTREE_CHUNKSIZE_1024 1024 +/** Workaround to forward-declare C++ type in C header. */ +#ifdef __cplusplus +namespace blender::nodes { struct FieldInferencingInterface; +} +using FieldInferencingInterfaceHandle = blender::nodes::FieldInferencingInterface; +#else +typedef struct FieldInferencingInterfaceHandle FieldInferencingInterfaceHandle; +#endif /* the basis for a Node tree, all links and nodes reside internal here */ /* only re-usable node trees are in the library though, @@ -488,7 +496,7 @@ typedef struct bNodeTree { ListBase nodes, links; /** Information about how inputs and outputs of the node group interact with fields. */ - struct FieldInferencingInterface *field_inferencing_interface; + FieldInferencingInterfaceHandle *field_inferencing_interface; /** Set init on fileread. */ int type, init; diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index d5dcf88b914..f2f9b82480d 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -88,6 +88,7 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" #include "NOD_geometry_nodes_eval_log.hh" +#include "NOD_node_declaration.hh" #include "FN_field.hh" #include "FN_multi_function.hh" @@ -107,7 +108,9 @@ using blender::bke::OutputAttribute; using blender::fn::GField; using blender::fn::GMutablePointer; using blender::fn::GPointer; +using blender::nodes::FieldInferencingInterface; using blender::nodes::GeoNodeExecParams; +using blender::nodes::InputSocketFieldType; using blender::threading::EnumerableThreadSpecific; using namespace blender::fn::multi_function_types; using namespace blender::nodes::derived_node_tree_types; @@ -308,6 +311,17 @@ static bool socket_type_has_attribute_toggle(const bNodeSocket &socket) return ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT); } +/** + * \return Whether using an attribute to input values of this type is supported, and the node + * group's input for this socket accepts a field rather than just single values. + */ +static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index) +{ + BLI_assert(node_tree.field_inferencing_interface != nullptr); + const FieldInferencingInterface &field_interface = *node_tree.field_inferencing_interface; + return field_interface.inputs[socket_index] != InputSocketFieldType::None; +} + static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) { switch (socket.type) { @@ -533,7 +547,8 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) nmd->settings.properties = IDP_New(IDP_GROUP, &idprop, "Nodes Modifier Settings"); } - LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) { + int socket_index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) { IDProperty *new_prop = id_property_create_from_socket(*socket); if (new_prop == nullptr) { /* Out of the set of supported input sockets, only @@ -564,7 +579,7 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) } } - if (socket_type_has_attribute_toggle(*socket)) { + if (input_has_attribute_toggle(*nmd->node_group, socket_index)) { const std::string use_attribute_id = socket->identifier + use_attribute_suffix; const std::string attribute_name_id = socket->identifier + attribute_name_suffix; @@ -655,37 +670,39 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd) } static void initialize_group_input(NodesModifierData &nmd, - const bNodeSocket &socket, + const OutputSocketRef &socket, void *r_value) { + const bNodeSocketType &socket_type = *socket.typeinfo(); + const bNodeSocket &bsocket = *socket.bsocket(); if (nmd.settings.properties == nullptr) { - socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); + socket_type.get_geometry_nodes_cpp_value(bsocket, r_value); return; } const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties, - socket.identifier); + socket.identifier().c_str()); if (property == nullptr) { - socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); + socket_type.get_geometry_nodes_cpp_value(bsocket, r_value); return; } - if (!id_property_type_matches_socket(socket, *property)) { - socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); + if (!id_property_type_matches_socket(bsocket, *property)) { + socket_type.get_geometry_nodes_cpp_value(bsocket, r_value); return; } - if (!socket_type_has_attribute_toggle(socket)) { + if (!input_has_attribute_toggle(*nmd.node_group, socket.index())) { init_socket_cpp_value_from_property( - *property, static_cast(socket.type), r_value); + *property, static_cast(bsocket.type), r_value); return; } const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup( - nmd.settings.properties, (socket.identifier + use_attribute_suffix).c_str()); + nmd.settings.properties, (socket.identifier() + use_attribute_suffix).c_str()); const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup( - nmd.settings.properties, (socket.identifier + attribute_name_suffix).c_str()); + nmd.settings.properties, (socket.identifier() + attribute_name_suffix).c_str()); if (property_use_attribute == nullptr || property_attribute_name == nullptr) { init_socket_cpp_value_from_property( - *property, static_cast(socket.type), r_value); + *property, static_cast(bsocket.type), r_value); return; } @@ -693,12 +710,12 @@ static void initialize_group_input(NodesModifierData &nmd, if (use_attribute) { const StringRef attribute_name{IDP_String(property_attribute_name)}; auto attribute_input = std::make_shared( - attribute_name, *socket.typeinfo->get_base_cpp_type()); + attribute_name, *socket_type.get_base_cpp_type()); new (r_value) blender::fn::GField(std::move(attribute_input), 0); } else { init_socket_cpp_value_from_property( - *property, static_cast(socket.type), r_value); + *property, static_cast(bsocket.type), r_value); } } @@ -914,7 +931,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, for (const OutputSocketRef *socket : remaining_input_sockets) { const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type(); void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment()); - initialize_group_input(*nmd, *socket->bsocket(), value_in); + initialize_group_input(*nmd, *socket, value_in); group_inputs.add_new({root_context, socket}, {cpp_type, value_in}); } } @@ -1079,11 +1096,12 @@ static void modifyGeometrySet(ModifierData *md, /* Drawing the properties manually with #uiItemR instead of #uiDefAutoButsRNA allows using * the node socket identifier for the property names, since they are unique, but also having * the correct label displayed in the UI. */ -static void draw_property_for_input_socket(uiLayout *layout, - NodesModifierData *nmd, - PointerRNA *bmain_ptr, - PointerRNA *md_ptr, - const bNodeSocket &socket) +static void draw_property_for_socket(uiLayout *layout, + NodesModifierData *nmd, + PointerRNA *bmain_ptr, + PointerRNA *md_ptr, + const bNodeSocket &socket, + const int socket_index) { /* The property should be created in #MOD_nodes_update_interface with the correct type. */ IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket.identifier); @@ -1128,7 +1146,7 @@ static void draw_property_for_input_socket(uiLayout *layout, break; } default: { - if (socket_type_has_attribute_toggle(socket) && + if (input_has_attribute_toggle(*nmd->node_group, socket_index) && USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) { const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + use_attribute_suffix + "\"]"; @@ -1200,8 +1218,9 @@ static void panel_draw(const bContext *C, Panel *panel) PointerRNA bmain_ptr; RNA_main_pointer_create(bmain, &bmain_ptr); - LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) { - draw_property_for_input_socket(layout, nmd, &bmain_ptr, ptr, *socket); + int socket_index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) { + draw_property_for_socket(layout, nmd, &bmain_ptr, ptr, *socket, socket_index); } } diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 8c7d343c001..07d4e05cda8 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -112,6 +112,24 @@ class OutputFieldDependency { } }; +/** + * Information about how a node interacts with fields. + */ +struct FieldInferencingInterface { + Vector inputs; + Vector outputs; + + friend bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) + { + return a.inputs == b.inputs && a.outputs == b.outputs; + } + + friend bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b) + { + return !(a == b); + } +}; + /** * Describes a single input or output socket. This is subclassed for different socket types. */ From 262b2118565826177133013c324212c66d882456 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 28 Sep 2021 12:14:13 -0500 Subject: [PATCH 0309/1500] Geometry Nodes: Mesh Point Cloud Conversion Nodes This commit adds nodes to do direct conversion between meshes and point clouds in geometry nodes. The conversion from mesh to points is helpful to instance once per face, or once per edge, which was previously only possibly with ugly work-arounds. Fields can be evaluated on the mesh to pass them to the points with the attribute capture node. The other conversion, point cloud to mesh vertices, is a bit less obvious, though it is still a common request from users. It's helpful for flexibility when passing data around, better visualization in the viewport (and in the future, cycles), and the simplicity of points. This is a step towards T91754, where point clouds are currently combined with meshes when outputing to the next modifier after geometry nodes. Since we're removing the implicit behavior for realizing instances, it feels natural to use an explicit node to convert points to vertices too. Differential Revision: https://developer.blender.org/D12657 --- release/scripts/startup/nodeitems_builtins.py | 2 + source/blender/blenkernel/BKE_node.h | 2 + source/blender/blenkernel/intern/node.cc | 2 + source/blender/makesdna/DNA_node_types.h | 12 ++ source/blender/makesrna/intern/rna_nodetree.c | 36 ++++ .../modifiers/intern/MOD_nodes_evaluator.cc | 2 +- source/blender/nodes/CMakeLists.txt | 2 + source/blender/nodes/NOD_geometry.h | 2 + source/blender/nodes/NOD_static_types.h | 2 + .../geometry/nodes/node_geo_mesh_to_points.cc | 189 ++++++++++++++++++ .../nodes/node_geo_points_to_vertices.cc | 118 +++++++++++ 11 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 2c13d4eac9b..d1f1cfef682 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -582,6 +582,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeBoolean"), NodeItem("GeometryNodeTriangulate"), NodeItem("GeometryNodeMeshSubdivide"), + NodeItem("GeometryNodePointsToVertices", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[ NodeItem("GeometryNodeMeshCircle"), @@ -594,6 +595,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshUVSphere"), ]), GeometryNodeCategory("GEO_POINT", "Point", items=[ + NodeItem("GeometryNodeMeshToPoints", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeInstanceOnPoints", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeDistributePointsOnFaces", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_fields_legacy_poll), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 16d4eb51d31..70700246d29 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1503,6 +1503,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES 1090 #define GEO_NODE_STRING_TO_CURVES 1091 #define GEO_NODE_INSTANCE_ON_POINTS 1092 +#define GEO_NODE_MESH_TO_POINTS 1093 +#define GEO_NODE_POINTS_TO_VERTICES 1094 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index c8ba7991f31..8a6f1471ddb 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5778,6 +5778,7 @@ static void registerGeometryNodes() register_node_type_geo_mesh_primitive_uv_sphere(); register_node_type_geo_mesh_subdivide(); register_node_type_geo_mesh_to_curve(); + register_node_type_geo_mesh_to_points(); register_node_type_geo_object_info(); register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); @@ -5785,6 +5786,7 @@ static void registerGeometryNodes() register_node_type_geo_point_scale(); register_node_type_geo_point_separate(); register_node_type_geo_point_translate(); + register_node_type_geo_points_to_vertices(); register_node_type_geo_points_to_volume(); register_node_type_geo_raycast(); register_node_type_geo_realize_instances(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index f7f2ccaa7be..538a80ce3e1 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1511,6 +1511,11 @@ typedef struct NodeGeometryCurveFill { uint8_t mode; } NodeGeometryCurveFill; +typedef struct NodeGeometryMeshToPoints { + /* GeometryNodeMeshToPointsMode */ + uint8_t mode; +} NodeGeometryMeshToPoints; + typedef struct NodeGeometryAttributeCapture { /* CustomDataType. */ int8_t data_type; @@ -2124,6 +2129,13 @@ typedef enum GeometryNodeCurveFillMode { GEO_NODE_CURVE_FILL_MODE_NGONS = 1, } GeometryNodeCurveFillMode; +typedef enum GeometryNodeMeshToPointsMode { + GEO_NODE_MESH_TO_POINTS_VERTICES = 0, + GEO_NODE_MESH_TO_POINTS_EDGES = 1, + GEO_NODE_MESH_TO_POINTS_FACES = 2, + GEO_NODE_MESH_TO_POINTS_CORNERS = 3, +} GeometryNodeMeshToPointsMode; + typedef enum GeometryNodeStringToCurvesOverflowMode { GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW = 0, GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT = 1, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 04f60c0d229..556b8e94fe6 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10306,6 +10306,42 @@ static void def_geo_curve_to_points(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_mesh_to_points(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_MESH_TO_POINTS_VERTICES, + "VERTICES", + 0, + "Vertices", + "Create a point in the point cloud for each selected vertex"}, + {GEO_NODE_MESH_TO_POINTS_EDGES, + "EDGES", + 0, + "Edges", + "Create a point in the point cloud for each selected edge"}, + {GEO_NODE_MESH_TO_POINTS_FACES, + "FACES", + 0, + "Faces", + "Create a point in the point cloud for each selected face"}, + {GEO_NODE_MESH_TO_POINTS_CORNERS, + "CORNERS", + 0, + "Corners", + "Create a point in the point cloud for each selected face corner"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryMeshToPoints", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_curve_trim(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 6f18c4d40db..d9ea5696bf5 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -329,7 +329,7 @@ static void get_socket_value(const SocketRef &socket, void *r_value) if (bsocket.flag & SOCK_HIDE_VALUE) { const bNode &bnode = *socket.bnode(); if (bsocket.type == SOCK_VECTOR) { - if (ELEM(bnode.type, GEO_NODE_SET_POSITION, SH_NODE_TEX_NOISE)) { + if (ELEM(bnode.type, GEO_NODE_SET_POSITION, SH_NODE_TEX_NOISE, GEO_NODE_MESH_TO_POINTS)) { new (r_value) Field( std::make_shared("position", CPPType::get())); return; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 9ce3d59c121..d2ae91ee4bc 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -231,7 +231,9 @@ set(SRC geometry/nodes/node_geo_mesh_primitive_line.cc geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc geometry/nodes/node_geo_mesh_subdivide.cc + geometry/nodes/node_geo_mesh_to_points.cc geometry/nodes/node_geo_object_info.cc + geometry/nodes/node_geo_points_to_vertices.cc geometry/nodes/node_geo_realize_instances.cc geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_set_position.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index bf780042600..6cbb45f80aa 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -101,6 +101,7 @@ void register_node_type_geo_mesh_primitive_line(void); void register_node_type_geo_mesh_primitive_uv_sphere(void); void register_node_type_geo_mesh_subdivide(void); void register_node_type_geo_mesh_to_curve(void); +void register_node_type_geo_mesh_to_points(void); void register_node_type_geo_object_info(void); void register_node_type_geo_point_distribute(void); void register_node_type_geo_point_instance(void); @@ -108,6 +109,7 @@ void register_node_type_geo_point_rotate(void); void register_node_type_geo_point_scale(void); void register_node_type_geo_point_separate(void); void register_node_type_geo_point_translate(void); +void register_node_type_geo_points_to_vertices(void); void register_node_type_geo_points_to_volume(void); void register_node_type_geo_raycast(void); void register_node_type_geo_realize_instances(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index f94394d4986..d653400361a 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -358,7 +358,9 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "") +DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") +DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "") DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc new file mode 100644 index 00000000000..2f59a3c968b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -0,0 +1,189 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_pointcloud.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::Array; + +namespace blender::nodes { + +static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Mesh"); + b.add_input("Position").implicit_field(); + b.add_input("Radius") + .default_value(0.05f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_input("Selection").default_value(true).supports_field().hide_value(); + b.add_output("Points"); +} + +static void geo_node_mesh_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void geo_node_mesh_to_points_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryMeshToPoints *data = (NodeGeometryMeshToPoints *)MEM_callocN( + sizeof(NodeGeometryMeshToPoints), __func__); + data->mode = GEO_NODE_MESH_TO_POINTS_FACES; + node->storage = data; +} + +template +static void copy_attribute_to_points(const VArray &src, + const IndexMask mask, + MutableSpan dst) +{ + for (const int i : mask.index_range()) { + dst[i] = src[mask[i]]; + } +} + +static void geometry_set_mesh_to_points(GeometrySet &geometry_set, + Field &position_field, + Field &radius_field, + Field &selection_field, + const AttributeDomain domain) +{ + const MeshComponent *mesh_component = geometry_set.get_component_for_read(); + if (mesh_component == nullptr) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + GeometryComponentFieldContext field_context{*mesh_component, domain}; + const int domain_size = mesh_component->attribute_domain_size(domain); + if (domain_size == 0) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); + uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); + geometry_set.replace_pointcloud(pointcloud); + PointCloudComponent &point_component = + geometry_set.get_component_for_write(); + + /* Evaluating directly into the point cloud doesn't work because we are not using the full + * "min_array_size" array but compressing the selected elements into the final array with no + * gaps. */ + fn::FieldEvaluator evaluator{field_context, &selection}; + evaluator.add(position_field); + evaluator.add(radius_field); + evaluator.evaluate(); + copy_attribute_to_points(evaluator.get_evaluated(0), + selection, + {(float3 *)pointcloud->co, pointcloud->totpoint}); + copy_attribute_to_points( + evaluator.get_evaluated(1), selection, {pointcloud->radius, pointcloud->totpoint}); + + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); + attributes.remove("position"); + + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + const CustomDataType data_type = entry.value.data_type; + GVArrayPtr src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type); + OutputAttribute dst = point_component.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, data_type); + if (dst && src) { + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed src_typed{*src}; + copy_attribute_to_points(*src_typed, selection, dst.as_span().typed()); + }); + dst.save(); + } + } + + geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES}); +} + +static void geo_node_mesh_to_points_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Mesh"); + Field position = params.extract_input>("Position"); + Field radius = params.extract_input>("Radius"); + Field selection = params.extract_input>("Selection"); + + /* Use another multi-function operation to make sure the input radius is greater than zero. + * TODO: Use mutable multi-function once that is supported. */ + static fn::CustomMF_SI_SO max_zero_fn( + __func__, [](float value) { return std::max(0.0f, value); }); + auto max_zero_op = std::make_shared( + FieldOperation(max_zero_fn, {std::move(radius)})); + Field positive_radius(std::move(max_zero_op), 0); + + const NodeGeometryMeshToPoints &storage = + *(const NodeGeometryMeshToPoints *)params.node().storage; + const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode; + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + switch (mode) { + case GEO_NODE_MESH_TO_POINTS_VERTICES: + geometry_set_mesh_to_points( + geometry_set, position, positive_radius, selection, ATTR_DOMAIN_POINT); + break; + case GEO_NODE_MESH_TO_POINTS_EDGES: + geometry_set_mesh_to_points( + geometry_set, position, positive_radius, selection, ATTR_DOMAIN_EDGE); + break; + case GEO_NODE_MESH_TO_POINTS_FACES: + geometry_set_mesh_to_points( + geometry_set, position, positive_radius, selection, ATTR_DOMAIN_FACE); + break; + case GEO_NODE_MESH_TO_POINTS_CORNERS: + geometry_set_mesh_to_points( + geometry_set, position, positive_radius, selection, ATTR_DOMAIN_CORNER); + break; + } + }); + + params.set_output("Points", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_to_points() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_mesh_to_points_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_points_exec; + node_type_init(&ntype, blender::nodes::geo_node_mesh_to_points_init); + ntype.draw_buttons = blender::nodes::geo_node_mesh_to_points_layout; + node_type_storage( + &ntype, "NodeGeometryMeshToPoints", node_free_standard_storage, node_copy_standard_storage); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc new file mode 100644 index 00000000000..afd0ced6360 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -0,0 +1,118 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +using blender::Array; + +namespace blender::nodes { + +static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Points"); + b.add_input("Selection").default_value(true).supports_field().hide_value(); + b.add_output("Mesh"); +} + +template +static void copy_attribute_to_vertices(const Span src, const IndexMask mask, MutableSpan dst) +{ + for (const int i : mask.index_range()) { + dst[i] = src[mask[i]]; + } +} + +/* One improvement would be to move the attribute arrays directly to the mesh when possible. */ +static void geometry_set_points_to_vertices(GeometrySet &geometry_set, + Field &selection_field) +{ + const PointCloudComponent *point_component = + geometry_set.get_component_for_read(); + if (point_component == nullptr) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT}; + const int domain_size = point_component->attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_MESH, false, attributes); + + Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0); + geometry_set.replace_mesh(mesh); + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + const CustomDataType data_type = entry.value.data_type; + GVArrayPtr src = point_component->attribute_get_for_read( + attribute_id, ATTR_DOMAIN_POINT, data_type); + OutputAttribute dst = mesh_component.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, data_type); + if (dst && src) { + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed src_typed{*src}; + VArray_Span src_typed_span{*src_typed}; + copy_attribute_to_vertices(src_typed_span, selection, dst.as_span().typed()); + }); + dst.save(); + } + } + + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); +} + +static void geo_node_points_to_vertices_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Points"); + Field selection_field = params.extract_input>("Selection"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + geometry_set_points_to_vertices(geometry_set, selection_field); + }); + + params.set_output("Mesh", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_points_to_vertices() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_POINTS_TO_VERTICES, "Points to Vertices", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_points_to_vertices_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_points_to_vertices_exec; + nodeRegisterType(&ntype); +} From 044a77352f8a8a0e1f60190369d69ef26587b65f Mon Sep 17 00:00:00 2001 From: Brian Savery Date: Tue, 28 Sep 2021 16:51:14 +0200 Subject: [PATCH 0310/1500] Cycles: add HIP device support for AMD GPUs NOTE: this feature is not ready for user testing, and not yet enabled in daily builds. It is being merged now for easier collaboration on development. HIP is a heterogenous compute interface allowing C++ code to be executed on GPUs similar to CUDA. It is intended to bring back AMD GPU rendering support on Windows and Linux. https://github.com/ROCm-Developer-Tools/HIP. As of the time of writing, it should compile and run on Linux with existing HIP compilers and driver runtimes. Publicly available compilers and drivers for Windows will come later. See task T91571 for more details on the current status and work remaining to be done. Credits: Sayak Biswas (AMD) Arya Rafii (AMD) Brian Savery (AMD) Differential Revision: https://developer.blender.org/D12578 --- CMakeLists.txt | 10 + extern/CMakeLists.txt | 3 + extern/hipew/CMakeLists.txt | 39 + extern/hipew/include/hipew.h | 1207 +++++++++++++++ extern/hipew/src/hipew.c | 533 +++++++ intern/cycles/CMakeLists.txt | 1 + intern/cycles/blender/CMakeLists.txt | 3 + intern/cycles/blender/addon/engine.py | 2 +- intern/cycles/blender/addon/properties.py | 13 +- intern/cycles/blender/addon/ui.py | 5 + intern/cycles/blender/blender_device.cpp | 4 + intern/cycles/blender/blender_python.cpp | 9 +- intern/cycles/cmake/external_libs.cmake | 4 + intern/cycles/cmake/macros.cmake | 4 + intern/cycles/device/CMakeLists.txt | 32 + intern/cycles/device/device.cpp | 42 + intern/cycles/device/device.h | 3 + intern/cycles/device/device_memory.h | 1 + intern/cycles/device/hip/device.cpp | 276 ++++ intern/cycles/device/hip/device.h | 37 + intern/cycles/device/hip/device_impl.cpp | 1343 +++++++++++++++++ intern/cycles/device/hip/device_impl.h | 153 ++ intern/cycles/device/hip/graphics_interop.cpp | 93 ++ intern/cycles/device/hip/graphics_interop.h | 61 + intern/cycles/device/hip/kernel.cpp | 69 + intern/cycles/device/hip/kernel.h | 54 + intern/cycles/device/hip/queue.cpp | 209 +++ intern/cycles/device/hip/queue.h | 68 + intern/cycles/device/hip/util.cpp | 61 + intern/cycles/device/hip/util.h | 63 + intern/cycles/integrator/path_trace.cpp | 2 + intern/cycles/kernel/CMakeLists.txt | 116 ++ .../kernel/device/gpu/parallel_active_index.h | 6 +- .../kernel/device/gpu/parallel_prefix_sum.h | 6 +- .../kernel/device/gpu/parallel_reduce.h | 6 +- .../kernel/device/gpu/parallel_sorted_index.h | 6 +- intern/cycles/kernel/device/hip/compat.h | 121 ++ intern/cycles/kernel/device/hip/config.h | 57 + intern/cycles/kernel/device/hip/globals.h | 49 + intern/cycles/kernel/device/hip/kernel.cpp | 28 + intern/cycles/util/util_atomic.h | 2 +- intern/cycles/util/util_debug.cpp | 15 + intern/cycles/util/util_debug.h | 14 + intern/cycles/util/util_half.h | 24 +- intern/cycles/util/util_math.h | 19 +- 45 files changed, 4854 insertions(+), 19 deletions(-) create mode 100644 extern/hipew/CMakeLists.txt create mode 100644 extern/hipew/include/hipew.h create mode 100644 extern/hipew/src/hipew.c create mode 100644 intern/cycles/device/hip/device.cpp create mode 100644 intern/cycles/device/hip/device.h create mode 100644 intern/cycles/device/hip/device_impl.cpp create mode 100644 intern/cycles/device/hip/device_impl.h create mode 100644 intern/cycles/device/hip/graphics_interop.cpp create mode 100644 intern/cycles/device/hip/graphics_interop.h create mode 100644 intern/cycles/device/hip/kernel.cpp create mode 100644 intern/cycles/device/hip/kernel.h create mode 100644 intern/cycles/device/hip/queue.cpp create mode 100644 intern/cycles/device/hip/queue.h create mode 100644 intern/cycles/device/hip/util.cpp create mode 100644 intern/cycles/device/hip/util.h create mode 100644 intern/cycles/kernel/device/hip/compat.h create mode 100644 intern/cycles/kernel/device/hip/config.h create mode 100644 intern/cycles/kernel/device/hip/globals.h create mode 100644 intern/cycles/kernel/device/hip/kernel.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e807b84e22..c4b8bf6dcd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -419,6 +419,8 @@ mark_as_advanced(WITH_CYCLES_NATIVE_ONLY) option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles CUDA compute support" ON) option(WITH_CYCLES_DEVICE_OPTIX "Enable Cycles OptiX support" ON) +option(WITH_CYCLES_DEVICE_HIP "Enable Cycles HIP support" OFF) +mark_as_advanced(WITH_CYCLES_DEVICE_HIP) mark_as_advanced(WITH_CYCLES_DEVICE_CUDA) option(WITH_CUDA_DYNLOAD "Dynamically load CUDA libraries at runtime" ON) @@ -821,6 +823,11 @@ if(NOT WITH_CUDA_DYNLOAD) endif() endif() +if(WITH_CYCLES_DEVICE_HIP) + # Currently HIP must be dynamically loaded, this may change in future toolkits + set(WITH_HIP_DYNLOAD ON) +endif() + #----------------------------------------------------------------------------- # Check check if submodules are cloned @@ -1850,6 +1857,9 @@ elseif(WITH_CYCLES_STANDALONE) if(WITH_CUDA_DYNLOAD) add_subdirectory(extern/cuew) endif() + if(WITH_HIP_DYNLOAD) + add_subdirectory(extern/hipew) + endif() if(NOT WITH_SYSTEM_GLEW) add_subdirectory(extern/glew) endif() diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 7f7d91f0765..2b2cca04503 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -70,6 +70,9 @@ if(WITH_CYCLES OR WITH_COMPOSITOR OR WITH_OPENSUBDIV) if(WITH_CUDA_DYNLOAD) add_subdirectory(cuew) endif() + if(WITH_HIP_DYNLOAD) + add_subdirectory(hipew) + endif() endif() if(WITH_GHOST_X11 AND WITH_GHOST_XDND) diff --git a/extern/hipew/CMakeLists.txt b/extern/hipew/CMakeLists.txt new file mode 100644 index 00000000000..d215ea8c691 --- /dev/null +++ b/extern/hipew/CMakeLists.txt @@ -0,0 +1,39 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2021, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + include +) + +set(INC_SYS + +) + +set(SRC + src/hipew.c + + include/hipew.h +) + +set(LIB +) + +blender_add_lib(extern_hipew "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/extern/hipew/include/hipew.h b/extern/hipew/include/hipew.h new file mode 100644 index 00000000000..02fffc331bf --- /dev/null +++ b/extern/hipew/include/hipew.h @@ -0,0 +1,1207 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#ifndef __HIPEW_H__ +#define __HIPEW_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define HIP_IPC_HANDLE_SIZE 64 +#define hipHostMallocPortable 0x01 +#define hipHostMallocMapped 0x02 +#define hipHostMallocWriteCombined 0x04 +#define hipHostRegisterPortable 0x01 +#define hipHostRegisterMapped 0x02 +#define hipHostRegisterIoMemory 0x04 +#define hipCooperativeLaunchMultiDeviceNoPreSync 0x01 +#define hipCooperativeLaunchMultiDeviceNoPostSync 0x02 +#define hipArrayLayered 0x01 +#define hipArraySurfaceLoadStore 0x02 +#define hipArrayCubemap 0x04 +#define hipArrayTextureGather 0x08 +#define HIP_TRSA_OVERRIDE_FORMAT 0x01 +#define HIP_TRSF_READ_AS_INTEGER 0x01 +#define HIP_TRSF_NORMALIZED_COORDINATES 0x02 +#define HIP_LAUNCH_PARAM_END ((void*)0x00) +#define HIP_LAUNCH_PARAM_BUFFER_POINTER ((void*)0x01) +#define HIP_LAUNCH_PARAM_BUFFER_SIZE ((void*)0x02) + +/* Functions which changed 3.1 -> 3.2 for 64 bit stuff, + * the cuda library has both the old ones for compatibility and new + * ones with _v2 postfix, + */ +#define hipModuleGetGlobal hipModuleGetGlobal +#define hipMemGetInfo hipMemGetInfo +#define hipMemAllocPitch hipMemAllocPitch +#define hipMemGetAddressRange hipMemGetAddressRange +#define hipMemcpyHtoD hipMemcpyHtoD +#define hipMemcpyDtoH hipMemcpyDtoH +#define hipMemcpyDtoD hipMemcpyDtoD +#define hipMemcpyHtoA hipMemcpyHtoA +#define hipMemcpyAtoH hipMemcpyAtoH +#define hipMemcpyHtoDAsync hipMemcpyHtoDAsync +#define hipMemcpyDtoHAsync hipMemcpyDtoHAsync +#define hipMemcpyDtoDAsync hipMemcpyDtoDAsync +#define hipMemsetD8 hipMemsetD8 +#define hipMemsetD16 hipMemsetD16 +#define hipMemsetD32 hipMemsetD32 +#define hipArrayCreate hipArrayCreate +#define hipArray3DCreate hipArray3DCreate +#define hipTexRefSetAddress hipTexRefSetAddress +#define hipTexRefGetAddress hipTexRefGetAddress +#define hipStreamDestroy hipStreamDestroy +#define hipEventDestroy hipEventDestroy +#define hipTexRefSetAddress2D hipTexRefSetAddress2D + +/* Types. */ +#ifdef _MSC_VER +typedef unsigned __int32 hipuint32_t; +typedef unsigned __int64 hipuint64_t; +#else +#include +typedef uint32_t hipuint32_t; +typedef uint64_t hipuint64_t; +#endif + +#if defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) || defined (__aarch64__) +typedef unsigned long long hipDeviceptr_t; +#else +typedef unsigned int hipDeviceptr_t; +#endif + + +#ifdef _WIN32 +# define HIPAPI __stdcall +# define HIP_CB __stdcall +#else +# define HIPAPI +# define HIP_CB +#endif + +typedef int hipDevice_t; +typedef struct ihipCtx_t* hipCtx_t; +typedef struct ihipModule_t* hipModule_t; +typedef struct ihipModuleSymbol_t* hipFunction_t; +typedef struct hipArray* hArray; +typedef struct hipMipmappedArray_st* hipMipmappedArray_t; +typedef struct ihipEvent_t* hipEvent_t; +typedef struct ihipStream_t* hipStream_t; +typedef unsigned long long hipTextureObject_t; + +typedef struct HIPuuid_st { + char bytes[16]; +} HIPuuid; + +typedef enum hipChannelFormatKind { + hipChannelFormatKindSigned = 0, + hipChannelFormatKindUnsigned = 1, + hipChannelFormatKindFloat = 2, + hipChannelFormatKindNone = 3, +}hipChannelFormatKind; + +typedef struct hipChannelFormatDesc { + int x; + int y; + int z; + int w; + enum hipChannelFormatKind f; +}hipChannelFormatDesc; + +typedef enum hipTextureFilterMode { + hipFilterModePoint = 0, + hipFilterModeLinear = 1, +} hipTextureFilterMode; + +typedef enum hipArray_Format { + HIP_AD_FORMAT_UNSIGNED_INT8 = 0x01, + HIP_AD_FORMAT_SIGNED_INT8 = 0x08, + HIP_AD_FORMAT_UNSIGNED_INT16 = 0x02, + HIP_AD_FORMAT_SIGNED_INT16 = 0x09, + HIP_AD_FORMAT_UNSIGNED_INT32 = 0x03, + HIP_AD_FORMAT_SIGNED_INT32 = 0x0a, + HIP_AD_FORMAT_HALF = 0x10, + HIP_AD_FORMAT_FLOAT = 0x20, +} hipArray_Format; + +typedef enum hipTextureAddressMode { + hipAddressModeWrap = 0, + hipAddressModeClamp = 1, + hipAddressModeMirror = 2, + hipAddressModeBorder = 3, +} hipTextureAddressMode; + +/** + * hip texture reference + */ +typedef struct textureReference { + int normalized; + //enum hipTextureReadMode readMode;// used only for driver API's + enum hipTextureFilterMode filterMode; + enum hipTextureAddressMode addressMode[3]; // Texture address mode for up to 3 dimensions + struct hipChannelFormatDesc channelDesc; + int sRGB; // Perform sRGB->linear conversion during texture read + unsigned int maxAnisotropy; // Limit to the anisotropy ratio + enum hipTextureFilterMode mipmapFilterMode; + float mipmapLevelBias; + float minMipmapLevelClamp; + float maxMipmapLevelClamp; + + hipTextureObject_t textureObject; + int numChannels; + enum hipArray_Format format; +}textureReference; + +typedef textureReference* hipTexRef; + +typedef enum hipMemoryType { + hipMemoryTypeHost = 0x00, + hipMemoryTypeDevice = 0x01, + hipMemoryTypeArray = 0x02, + hipMemoryTypeUnified = 0x03, +} hipMemoryType; + +/** + * Pointer attributes + */ +typedef struct hipPointerAttribute_t { + enum hipMemoryType memoryType; + int device; + void* devicePointer; + void* hostPointer; + int isManaged; + unsigned allocationFlags; /* flags specified when memory was allocated*/ + /* peers? */ +} hipPointerAttribute_t; + +typedef struct ihipIpcEventHandle_t { + char reserved[HIP_IPC_HANDLE_SIZE]; +} ihipIpcEventHandle_t; + +typedef struct hipIpcMemHandle_st { + char reserved[HIP_IPC_HANDLE_SIZE]; +} hipIpcMemHandle_t; + +typedef enum HIPipcMem_flags_enum { + hipIpcMemLazyEnablePeerAccess = 0x1, +} HIPipcMem_flags; + +typedef enum HIPmemAttach_flags_enum { + hipMemAttachGlobal = 0x1, + hipMemAttachHost = 0x2, + HIP_MEM_ATTACH_SINGLE = 0x4, +} HIPmemAttach_flags; + +typedef enum HIPctx_flags_enum { + hipDeviceScheduleAuto = 0x00, + hipDeviceScheduleSpin = 0x01, + hipDeviceScheduleYield = 0x02, + hipDeviceScheduleBlockingSync = 0x04, + hipDeviceScheduleMask = 0x07, + hipDeviceMapHost = 0x08, + hipDeviceLmemResizeToMax = 0x10, +} HIPctx_flags; + +typedef enum HIPstream_flags_enum { + hipStreamDefault = 0x0, + hipStreamNonBlocking = 0x1, +} HIPstream_flags; + +typedef enum HIPevent_flags_enum { + hipEventDefault = 0x0, + hipEventBlockingSync = 0x1, + hipEventDisableTiming = 0x2, + hipEventInterprocess = 0x4, +} HIPevent_flags; + +typedef enum HIPstreamWaitValue_flags_enum { + HIP_STREAM_WAIT_VALUE_GEQ = 0x0, + HIP_STREAM_WAIT_VALUE_EQ = 0x1, + HIP_STREAM_WAIT_VALUE_AND = 0x2, + HIP_STREAM_WAIT_VALUE_NOR = 0x3, + HIP_STREAM_WAIT_VALUE_FLUSH = (1 << 30), +} HIPstreamWaitValue_flags; + +typedef enum HIPstreamWriteValue_flags_enum { + HIP_STREAM_WRITE_VALUE_DEFAULT = 0x0, + HIP_STREAM_WRITE_VALUE_NO_MEMORY_BARRIER = 0x1, +} HIPstreamWriteValue_flags; + +typedef enum HIPstreamBatchMemOpType_enum { + HIP_STREAM_MEM_OP_WAIT_VALUE_32 = 1, + HIP_STREAM_MEM_OP_WRITE_VALUE_32 = 2, + HIP_STREAM_MEM_OP_WAIT_VALUE_64 = 4, + HIP_STREAM_MEM_OP_WRITE_VALUE_64 = 5, + HIP_STREAM_MEM_OP_FLUSH_REMOTE_WRITES = 3, +} HIPstreamBatchMemOpType; + + +typedef union HIPstreamBatchMemOpParams_union { + HIPstreamBatchMemOpType operation; + struct HIPstreamMemOpWaitValueParams_st { + HIPstreamBatchMemOpType operation; + hipDeviceptr_t address; + union { + hipuint32_t value; + hipuint64_t value64; + }; + unsigned int flags; + hipDeviceptr_t alias; + } waitValue; + struct HIPstreamMemOpWriteValueParams_st { + HIPstreamBatchMemOpType operation; + hipDeviceptr_t address; + union { + hipuint32_t value; + hipuint64_t value64; + }; + unsigned int flags; + hipDeviceptr_t alias; + } writeValue; + struct HIPstreamMemOpFlushRemoteWritesParams_st { + HIPstreamBatchMemOpType operation; + unsigned int flags; + } flushRemoteWrites; + hipuint64_t pad[6]; +} HIPstreamBatchMemOpParams; + +typedef enum HIPoccupancy_flags_enum { + hipOccupancyDefault = 0x0, + HIP_OCCUPANCY_DISABLE_CACHING_OVERRIDE = 0x1, +} HIPoccupancy_flags; + +typedef enum hipDeviceAttribute_t { + hipDeviceAttributeCudaCompatibleBegin = 0, + hipDeviceAttributeEccEnabled = hipDeviceAttributeCudaCompatibleBegin, ///< Whether ECC support is enabled. + hipDeviceAttributeAccessPolicyMaxWindowSize, ///< Cuda only. The maximum size of the window policy in bytes. + hipDeviceAttributeAsyncEngineCount, ///< Cuda only. Asynchronous engines number. + hipDeviceAttributeCanMapHostMemory, ///< Whether host memory can be mapped into device address space + hipDeviceAttributeCanUseHostPointerForRegisteredMem,///< Cuda only. Device can access host registered memory + ///< at the same virtual address as the CPU + hipDeviceAttributeClockRate, ///< Peak clock frequency in kilohertz. + hipDeviceAttributeComputeMode, ///< Compute mode that device is currently in. + hipDeviceAttributeComputePreemptionSupported, ///< Cuda only. Device supports Compute Preemption. + hipDeviceAttributeConcurrentKernels, ///< Device can possibly execute multiple kernels concurrently. + hipDeviceAttributeConcurrentManagedAccess, ///< Device can coherently access managed memory concurrently with the CPU + hipDeviceAttributeCooperativeLaunch, ///< Support cooperative launch + hipDeviceAttributeCooperativeMultiDeviceLaunch, ///< Support cooperative launch on multiple devices + hipDeviceAttributeDeviceOverlap, ///< Cuda only. Device can concurrently copy memory and execute a kernel. + ///< Deprecated. Use instead asyncEngineCount. + hipDeviceAttributeDirectManagedMemAccessFromHost, ///< Host can directly access managed memory on + ///< the device without migration + hipDeviceAttributeGlobalL1CacheSupported, ///< Cuda only. Device supports caching globals in L1 + hipDeviceAttributeHostNativeAtomicSupported, ///< Cuda only. Link between the device and the host supports native atomic operations + hipDeviceAttributeIntegrated, ///< Device is integrated GPU + hipDeviceAttributeIsMultiGpuBoard, ///< Multiple GPU devices. + hipDeviceAttributeKernelExecTimeout, ///< Run time limit for kernels executed on the device + hipDeviceAttributeL2CacheSize, ///< Size of L2 cache in bytes. 0 if the device doesn't have L2 cache. + hipDeviceAttributeLocalL1CacheSupported, ///< caching locals in L1 is supported + hipDeviceAttributeLuid, ///< Cuda only. 8-byte locally unique identifier in 8 bytes. Undefined on TCC and non-Windows platforms + hipDeviceAttributeLuidDeviceNodeMask, ///< Cuda only. Luid device node mask. Undefined on TCC and non-Windows platforms + hipDeviceAttributeComputeCapabilityMajor, ///< Major compute capability version number. + hipDeviceAttributeManagedMemory, ///< Device supports allocating managed memory on this system + hipDeviceAttributeMaxBlocksPerMultiProcessor, ///< Cuda only. Max block size per multiprocessor + hipDeviceAttributeMaxBlockDimX, ///< Max block size in width. + hipDeviceAttributeMaxBlockDimY, ///< Max block size in height. + hipDeviceAttributeMaxBlockDimZ, ///< Max block size in depth. + hipDeviceAttributeMaxGridDimX, ///< Max grid size in width. + hipDeviceAttributeMaxGridDimY, ///< Max grid size in height. + hipDeviceAttributeMaxGridDimZ, ///< Max grid size in depth. + hipDeviceAttributeMaxSurface1D, ///< Maximum size of 1D surface. + hipDeviceAttributeMaxSurface1DLayered, ///< Cuda only. Maximum dimensions of 1D layered surface. + hipDeviceAttributeMaxSurface2D, ///< Maximum dimension (width, height) of 2D surface. + hipDeviceAttributeMaxSurface2DLayered, ///< Cuda only. Maximum dimensions of 2D layered surface. + hipDeviceAttributeMaxSurface3D, ///< Maximum dimension (width, height, depth) of 3D surface. + hipDeviceAttributeMaxSurfaceCubemap, ///< Cuda only. Maximum dimensions of Cubemap surface. + hipDeviceAttributeMaxSurfaceCubemapLayered, ///< Cuda only. Maximum dimension of Cubemap layered surface. + hipDeviceAttributeMaxTexture1DWidth, ///< Maximum size of 1D texture. + hipDeviceAttributeMaxTexture1DLayered, ///< Cuda only. Maximum dimensions of 1D layered texture. + hipDeviceAttributeMaxTexture1DLinear, ///< Maximum number of elements allocatable in a 1D linear texture. + ///< Use cudaDeviceGetTexture1DLinearMaxWidth() instead on Cuda. + hipDeviceAttributeMaxTexture1DMipmap, ///< Cuda only. Maximum size of 1D mipmapped texture. + hipDeviceAttributeMaxTexture2DWidth, ///< Maximum dimension width of 2D texture. + hipDeviceAttributeMaxTexture2DHeight, ///< Maximum dimension hight of 2D texture. + hipDeviceAttributeMaxTexture2DGather, ///< Cuda only. Maximum dimensions of 2D texture if gather operations performed. + hipDeviceAttributeMaxTexture2DLayered, ///< Cuda only. Maximum dimensions of 2D layered texture. + hipDeviceAttributeMaxTexture2DLinear, ///< Cuda only. Maximum dimensions (width, height, pitch) of 2D textures bound to pitched memory. + hipDeviceAttributeMaxTexture2DMipmap, ///< Cuda only. Maximum dimensions of 2D mipmapped texture. + hipDeviceAttributeMaxTexture3DWidth, ///< Maximum dimension width of 3D texture. + hipDeviceAttributeMaxTexture3DHeight, ///< Maximum dimension height of 3D texture. + hipDeviceAttributeMaxTexture3DDepth, ///< Maximum dimension depth of 3D texture. + hipDeviceAttributeMaxTexture3DAlt, ///< Cuda only. Maximum dimensions of alternate 3D texture. + hipDeviceAttributeMaxTextureCubemap, ///< Cuda only. Maximum dimensions of Cubemap texture + hipDeviceAttributeMaxTextureCubemapLayered, ///< Cuda only. Maximum dimensions of Cubemap layered texture. + hipDeviceAttributeMaxThreadsDim, ///< Maximum dimension of a block + hipDeviceAttributeMaxThreadsPerBlock, ///< Maximum number of threads per block. + hipDeviceAttributeMaxThreadsPerMultiProcessor, ///< Maximum resident threads per multiprocessor. + hipDeviceAttributeMaxPitch, ///< Maximum pitch in bytes allowed by memory copies + hipDeviceAttributeMemoryBusWidth, ///< Global memory bus width in bits. + hipDeviceAttributeMemoryClockRate, ///< Peak memory clock frequency in kilohertz. + hipDeviceAttributeComputeCapabilityMinor, ///< Minor compute capability version number. + hipDeviceAttributeMultiGpuBoardGroupID, ///< Cuda only. Unique ID of device group on the same multi-GPU board + hipDeviceAttributeMultiprocessorCount, ///< Number of multiprocessors on the device. + hipDeviceAttributeName, ///< Device name. + hipDeviceAttributePageableMemoryAccess, ///< Device supports coherently accessing pageable memory + ///< without calling hipHostRegister on it + hipDeviceAttributePageableMemoryAccessUsesHostPageTables, ///< Device accesses pageable memory via the host's page tables + hipDeviceAttributePciBusId, ///< PCI Bus ID. + hipDeviceAttributePciDeviceId, ///< PCI Device ID. + hipDeviceAttributePciDomainID, ///< PCI Domain ID. + hipDeviceAttributePersistingL2CacheMaxSize, ///< Cuda11 only. Maximum l2 persisting lines capacity in bytes + hipDeviceAttributeMaxRegistersPerBlock, ///< 32-bit registers available to a thread block. This number is shared + ///< by all thread blocks simultaneously resident on a multiprocessor. + hipDeviceAttributeMaxRegistersPerMultiprocessor, ///< 32-bit registers available per block. + hipDeviceAttributeReservedSharedMemPerBlock, ///< Cuda11 only. Shared memory reserved by CUDA driver per block. + hipDeviceAttributeMaxSharedMemoryPerBlock, ///< Maximum shared memory available per block in bytes. + hipDeviceAttributeSharedMemPerBlockOptin, ///< Cuda only. Maximum shared memory per block usable by special opt in. + hipDeviceAttributeSharedMemPerMultiprocessor, ///< Cuda only. Shared memory available per multiprocessor. + hipDeviceAttributeSingleToDoublePrecisionPerfRatio, ///< Cuda only. Performance ratio of single precision to double precision. + hipDeviceAttributeStreamPrioritiesSupported, ///< Cuda only. Whether to support stream priorities. + hipDeviceAttributeSurfaceAlignment, ///< Cuda only. Alignment requirement for surfaces + hipDeviceAttributeTccDriver, ///< Cuda only. Whether device is a Tesla device using TCC driver + hipDeviceAttributeTextureAlignment, ///< Alignment requirement for textures + hipDeviceAttributeTexturePitchAlignment, ///< Pitch alignment requirement for 2D texture references bound to pitched memory; + hipDeviceAttributeTotalConstantMemory, ///< Constant memory size in bytes. + hipDeviceAttributeTotalGlobalMem, ///< Global memory available on devicice. + hipDeviceAttributeUnifiedAddressing, ///< Cuda only. An unified address space shared with the host. + hipDeviceAttributeUuid, ///< Cuda only. Unique ID in 16 byte. + hipDeviceAttributeWarpSize, ///< Warp size in threads. + hipDeviceAttributeCudaCompatibleEnd = 9999, + hipDeviceAttributeAmdSpecificBegin = 10000, + hipDeviceAttributeClockInstructionRate = hipDeviceAttributeAmdSpecificBegin, ///< Frequency in khz of the timer used by the device-side "clock*" + hipDeviceAttributeArch, ///< Device architecture + hipDeviceAttributeMaxSharedMemoryPerMultiprocessor, ///< Maximum Shared Memory PerMultiprocessor. + hipDeviceAttributeGcnArch, ///< Device gcn architecture + hipDeviceAttributeGcnArchName, ///< Device gcnArch name in 256 bytes + hipDeviceAttributeHdpMemFlushCntl, ///< Address of the HDP_MEM_COHERENCY_FLUSH_CNTL register + hipDeviceAttributeHdpRegFlushCntl, ///< Address of the HDP_REG_COHERENCY_FLUSH_CNTL register + hipDeviceAttributeCooperativeMultiDeviceUnmatchedFunc, ///< Supports cooperative launch on multiple + ///< devices with unmatched functions + hipDeviceAttributeCooperativeMultiDeviceUnmatchedGridDim, ///< Supports cooperative launch on multiple + ///< devices with unmatched grid dimensions + hipDeviceAttributeCooperativeMultiDeviceUnmatchedBlockDim, ///< Supports cooperative launch on multiple + ///< devices with unmatched block dimensions + hipDeviceAttributeCooperativeMultiDeviceUnmatchedSharedMem, ///< Supports cooperative launch on multiple + ///< devices with unmatched shared memories + hipDeviceAttributeIsLargeBar, ///< Whether it is LargeBar + hipDeviceAttributeAsicRevision, ///< Revision of the GPU in this device + hipDeviceAttributeCanUseStreamWaitValue, ///< '1' if Device supports hipStreamWaitValue32() and + ///< hipStreamWaitValue64() , '0' otherwise. + hipDeviceAttributeAmdSpecificEnd = 19999, + hipDeviceAttributeVendorSpecificBegin = 20000, + // Extended attributes for vendors +} hipDeviceAttribute_t; + +typedef struct HIPdevprop_st { + int maxThreadsPerBlock; + int maxThreadsDim[3]; + int maxGridSize[3]; + int sharedMemPerBlock; + int totalConstantMemory; + int SIMDWidth; + int memPitch; + int regsPerBlock; + int clockRate; + int textureAlign; +} HIPdevprop; + +typedef enum HIPpointer_attribute_enum { + HIP_POINTER_ATTRIBUTE_CONTEXT = 1, + HIP_POINTER_ATTRIBUTE_MEMORY_TYPE = 2, + HIP_POINTER_ATTRIBUTE_DEVICE_POINTER = 3, + HIP_POINTER_ATTRIBUTE_HOST_POINTER = 4, + HIP_POINTER_ATTRIBUTE_SYNC_MEMOPS = 6, + HIP_POINTER_ATTRIBUTE_BUFFER_ID = 7, + HIP_POINTER_ATTRIBUTE_IS_MANAGED = 8, + HIP_POINTER_ATTRIBUTE_DEVICE_ORDINAL = 9, +} HIPpointer_attribute; + +typedef enum hipFunction_attribute { + HIP_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK = 0, + HIP_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES = 1, + HIP_FUNC_ATTRIBUTE_CONST_SIZE_BYTES = 2, + HIP_FUNC_ATTRIBUTE_LOCAL_SIZE_BYTES = 3, + HIP_FUNC_ATTRIBUTE_NUM_REGS = 4, + HIP_FUNC_ATTRIBUTE_PTX_VERSION = 5, + HIP_FUNC_ATTRIBUTE_BINARY_VERSION = 6, + HIP_FUNC_ATTRIBUTE_CACHE_MODE_CA = 7, + HIP_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES = 8, + HIP_FUNC_ATTRIBUTE_PREFERRED_SHARED_MEMORY_CARVEOUT = 9, + HIP_FUNC_ATTRIBUTE_MAX, +} hipFunction_attribute; + +typedef enum hipFuncCache_t { + hipFuncCachePreferNone = 0x00, + hipFuncCachePreferShared = 0x01, + hipFuncCachePreferL1 = 0x02, + hipFuncCachePreferEqual = 0x03, +} hipFuncCache_t; + +typedef enum hipSharedMemConfig { + hipSharedMemBankSizeDefault = 0x00, + hipSharedMemBankSizeFourByte = 0x01, + hipSharedMemBankSizeEightByte = 0x02, +} hipSharedMemConfig; + +typedef enum HIPshared_carveout_enum { + HIP_SHAREDMEM_CARVEOUT_DEFAULT, + HIP_SHAREDMEM_CARVEOUT_MAX_SHARED = 100, + HIP_SHAREDMEM_CARVEOUT_MAX_L1 = 0, +} HIPshared_carveout; + + + +typedef enum hipComputeMode { + hipComputeModeDefault = 0, + hipComputeModeProhibited = 2, + hipComputeModeExclusiveProcess = 3, +} hipComputeMode; + +typedef enum HIPmem_advise_enum { + HIP_MEM_ADVISE_SET_READ_MOSTLY = 1, + HIP_MEM_ADVISE_UNSET_READ_MOSTLY = 2, + HIP_MEM_ADVISE_SET_PREFERRED_LOCATION = 3, + HIP_MEM_ADVISE_UNSET_PREFERRED_LOCATION = 4, + HIP_MEM_ADVISE_SET_ACCESSED_BY = 5, + HIP_MEM_ADVISE_UNSET_ACCESSED_BY = 6, +} HIPmem_advise; + +typedef enum HIPmem_range_attribute_enum { + HIP_MEM_RANGE_ATTRIBUTE_READ_MOSTLY = 1, + HIP_MEM_RANGE_ATTRIBUTE_PREFERRED_LOCATION = 2, + HIP_MEM_RANGE_ATTRIBUTE_ACCESSED_BY = 3, + HIP_MEM_RANGE_ATTRIBUTE_LAST_PREFETCH_LOCATION = 4, +} HIPmem_range_attribute; + +typedef enum hipJitOption { + hipJitOptionMaxRegisters = 0, + hipJitOptionThreadsPerBlock, + hipJitOptionWallTime, + hipJitOptionInfoLogBuffer, + hipJitOptionInfoLogBufferSizeBytes, + hipJitOptionErrorLogBuffer, + hipJitOptionErrorLogBufferSizeBytes, + hipJitOptionOptimizationLevel, + hipJitOptionTargetFromContext, + hipJitOptionTarget, + hipJitOptionFallbackStrategy, + hipJitOptionGenerateDebugInfo, + hipJitOptionLogVerbose, + hipJitOptionGenerateLineInfo, + hipJitOptionCacheMode, + hipJitOptionSm3xOpt, + hipJitOptionFastCompile, + hipJitOptionNumOptions, +} hipJitOption; + +typedef enum HIPjit_target_enum { + HIP_TARGET_COMPUTE_20 = 20, + HIP_TARGET_COMPUTE_21 = 21, + HIP_TARGET_COMPUTE_30 = 30, + HIP_TARGET_COMPUTE_32 = 32, + HIP_TARGET_COMPUTE_35 = 35, + HIP_TARGET_COMPUTE_37 = 37, + HIP_TARGET_COMPUTE_50 = 50, + HIP_TARGET_COMPUTE_52 = 52, + HIP_TARGET_COMPUTE_53 = 53, + HIP_TARGET_COMPUTE_60 = 60, + HIP_TARGET_COMPUTE_61 = 61, + HIP_TARGET_COMPUTE_62 = 62, + HIP_TARGET_COMPUTE_70 = 70, + HIP_TARGET_COMPUTE_73 = 73, + HIP_TARGET_COMPUTE_75 = 75, +} HIPjit_target; + +typedef enum HIPjit_fallback_enum { + HIP_PREFER_PTX = 0, + HIP_PREFER_BINARY, +} HIPjit_fallback; + +typedef enum HIPjit_cacheMode_enum { + HIP_JIT_CACHE_OPTION_NONE = 0, + HIP_JIT_CACHE_OPTION_CG, + HIP_JIT_CACHE_OPTION_CA, +} HIPjit_cacheMode; + +typedef enum HIPjitInputType_enum { + HIP_JIT_INPUT_HIPBIN = 0, + HIP_JIT_INPUT_PTX, + HIP_JIT_INPUT_FATBINARY, + HIP_JIT_INPUT_OBJECT, + HIP_JIT_INPUT_LIBRARY, + HIP_JIT_NUM_INPUT_TYPES, +} HIPjitInputType; + +typedef struct HIPlinkState_st* HIPlinkState; + +typedef enum hipGLDeviceList { + hipGLDeviceListAll = 1, ///< All hip devices used by current OpenGL context. + hipGLDeviceListCurrentFrame = 2, ///< Hip devices used by current OpenGL context in current + ///< frame + hipGLDeviceListNextFrame = 3 ///< Hip devices used by current OpenGL context in next + ///< frame. +} hipGLDeviceList; + +typedef enum hipGraphicsRegisterFlags { + hipGraphicsRegisterFlagsNone = 0, + hipGraphicsRegisterFlagsReadOnly = 1, ///< HIP will not write to this registered resource + hipGraphicsRegisterFlagsWriteDiscard = + 2, ///< HIP will only write and will not read from this registered resource + hipGraphicsRegisterFlagsSurfaceLoadStore = 4, ///< HIP will bind this resource to a surface + hipGraphicsRegisterFlagsTextureGather = + 8 ///< HIP will perform texture gather operations on this registered resource +} hipGraphicsRegisterFlags; + +typedef enum HIPgraphicsRegisterFlags_enum { + HIP_GRAPHICS_REGISTER_FLAGS_NONE = 0x00, + HIP_GRAPHICS_REGISTER_FLAGS_READ_ONLY = 0x01, + HIP_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD = 0x02, + HIP_GRAPHICS_REGISTER_FLAGS_SURFACE_LDST = 0x04, + HIP_GRAPHICS_REGISTER_FLAGS_TEXTURE_GATHER = 0x08, +} HIPgraphicsRegisterFlags; + +typedef enum HIPgraphicsMapResourceFlags_enum { + HIP_GRAPHICS_MAP_RESOURCE_FLAGS_NONE = 0x00, + HIP_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY = 0x01, + HIP_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD = 0x02, +} HIPgraphicsMapResourceFlags; + +typedef enum HIParray_cubemap_face_enum { + HIP_HIPBEMAP_FACE_POSITIVE_X = 0x00, + HIP_HIPBEMAP_FACE_NEGATIVE_X = 0x01, + HIP_HIPBEMAP_FACE_POSITIVE_Y = 0x02, + HIP_HIPBEMAP_FACE_NEGATIVE_Y = 0x03, + HIP_HIPBEMAP_FACE_POSITIVE_Z = 0x04, + HIP_HIPBEMAP_FACE_NEGATIVE_Z = 0x05, +} HIParray_cubemap_face; + +typedef enum hipLimit_t { + HIP_LIMIT_STACK_SIZE = 0x00, + HIP_LIMIT_PRINTF_FIFO_SIZE = 0x01, + hipLimitMallocHeapSize = 0x02, + HIP_LIMIT_DEV_RUNTIME_SYNC_DEPTH = 0x03, + HIP_LIMIT_DEV_RUNTIME_PENDING_LAUNCH_COUNT = 0x04, + HIP_LIMIT_MAX, +} hipLimit_t; + +typedef enum hipResourceType { + hipResourceTypeArray = 0x00, + hipResourceTypeMipmappedArray = 0x01, + hipResourceTypeLinear = 0x02, + hipResourceTypePitch2D = 0x03, +} hipResourceType; + +typedef enum hipError_t { + hipSuccess = 0, + hipErrorInvalidValue = 1, + hipErrorOutOfMemory = 2, + hipErrorNotInitialized = 3, + hipErrorDeinitialized = 4, + hipErrorProfilerDisabled = 5, + hipErrorProfilerNotInitialized = 6, + hipErrorProfilerAlreadyStarted = 7, + hipErrorProfilerAlreadyStopped = 8, + hipErrorNoDevice = 100, + hipErrorInvalidDevice = 101, + hipErrorInvalidImage = 200, + hipErrorInvalidContext = 201, + hipErrorContextAlreadyCurrent = 202, + hipErrorMapFailed = 205, + hipErrorUnmapFailed = 206, + hipErrorArrayIsMapped = 207, + hipErrorAlreadyMapped = 208, + hipErrorNoBinaryForGpu = 209, + hipErrorAlreadyAcquired = 210, + hipErrorNotMapped = 211, + hipErrorNotMappedAsArray = 212, + hipErrorNotMappedAsPointer = 213, + hipErrorECCNotCorrectable = 214, + hipErrorUnsupportedLimit = 215, + hipErrorContextAlreadyInUse = 216, + hipErrorPeerAccessUnsupported = 217, + hipErrorInvalidKernelFile = 218, + hipErrorInvalidGraphicsContext = 219, + hipErrorInvalidSource = 300, + hipErrorFileNotFound = 301, + hipErrorSharedObjectSymbolNotFound = 302, + hipErrorSharedObjectInitFailed = 303, + hipErrorOperatingSystem = 304, + hipErrorInvalidHandle = 400, + hipErrorNotFound = 500, + hipErrorNotReady = 600, + hipErrorIllegalAddress = 700, + hipErrorLaunchOutOfResources = 701, + hipErrorLaunchTimeOut = 702, + hipErrorPeerAccessAlreadyEnabled = 704, + hipErrorPeerAccessNotEnabled = 705, + hipErrorSetOnActiveProcess = 708, + hipErrorAssert = 710, + hipErrorHostMemoryAlreadyRegistered = 712, + hipErrorHostMemoryNotRegistered = 713, + hipErrorLaunchFailure = 719, + hipErrorCooperativeLaunchTooLarge = 720, + hipErrorNotSupported = 801, + hipErrorUnknown = 999, +} hipError_t; + +/** + * Stream CallBack struct + */ +typedef void (*hipStreamCallback_t)(hipStream_t stream, hipError_t status, void* userData); + +typedef enum HIPdevice_P2PAttribute_enum { + HIP_DEVICE_P2P_ATTRIBUTE_PERFORMANCE_RANK = 0x01, + HIP_DEVICE_P2P_ATTRIBUTE_ACCESS_SUPPORTED = 0x02, + HIP_DEVICE_P2P_ATTRIBUTE_NATIVE_ATOMIC_SUPPORTED = 0x03, + HIP_DEVICE_P2P_ATTRIBUTE_ARRAY_ACCESS_ACCESS_SUPPORTED = 0x04, +} HIPdevice_P2PAttribute; + +typedef struct hipGraphicsResource_st* hipGraphicsResource; + +typedef struct hip_Memcpy2D { + size_t srcXInBytes; + size_t srcY; + hipMemoryType srcMemoryType; + const void* srcHost; + hipDeviceptr_t srcDevice; + hArray * srcArray; + size_t srcPitch; + size_t dstXInBytes; + size_t dstY; + hipMemoryType dstMemoryType; + void* dstHost; + hipDeviceptr_t dstDevice; + hArray * dstArray; + size_t dstPitch; + size_t WidthInBytes; + size_t Height; +} hip_Memcpy2D; + +typedef enum hipDeviceP2PAttr { + hipDevP2PAttrPerformanceRank = 0, + hipDevP2PAttrAccessSupported, + hipDevP2PAttrNativeAtomicSupported, + hipDevP2PAttrHipArrayAccessSupported +} hipDeviceP2PAttr; + +typedef struct HIP_MEMCPY3D { + size_t srcXInBytes; + size_t srcY; + size_t srcZ; + size_t srcLOD; + hipMemoryType srcMemoryType; + const void* srcHost; + hipDeviceptr_t srcDevice; + hArray * srcArray; + void* reserved0; + size_t srcPitch; + size_t srcHeight; + size_t dstXInBytes; + size_t dstY; + size_t dstZ; + size_t dstLOD; + hipMemoryType dstMemoryType; + void* dstHost; + hipDeviceptr_t dstDevice; + hArray * dstArray; + void* reserved1; + size_t dstPitch; + size_t dstHeight; + size_t WidthInBytes; + size_t Height; + size_t Depth; +} HIP_MEMCPY3D; + +typedef struct HIP_MEMCPY3D_PEER_st { + size_t srcXInBytes; + size_t srcY; + size_t srcZ; + size_t srcLOD; + hipMemoryType srcMemoryType; + const void* srcHost; + hipDeviceptr_t srcDevice; + hArray * srcArray; + hipCtx_t srcContext; + size_t srcPitch; + size_t srcHeight; + size_t dstXInBytes; + size_t dstY; + size_t dstZ; + size_t dstLOD; + hipMemoryType dstMemoryType; + void* dstHost; + hipDeviceptr_t dstDevice; + hArray * dstArray; + hipCtx_t dstContext; + size_t dstPitch; + size_t dstHeight; + size_t WidthInBytes; + size_t Height; + size_t Depth; +} HIP_MEMCPY3D_PEER; + +typedef struct HIP_ARRAY_DESCRIPTOR { + size_t Width; + size_t Height; + hipArray_Format Format; + unsigned int NumChannels; +} HIP_ARRAY_DESCRIPTOR; + +typedef struct HIP_ARRAY3D_DESCRIPTOR { + size_t Width; + size_t Height; + size_t Depth; + hipArray_Format Format; + unsigned int NumChannels; + unsigned int Flags; +} HIP_ARRAY3D_DESCRIPTOR; + +typedef struct HIP_RESOURCE_DESC_st { + hipResourceType resType; + union { + struct { + hArray * h_Array; + } array; + struct { + hipMipmappedArray_t hMipmappedArray; + } mipmap; + struct { + hipDeviceptr_t devPtr; + hipArray_Format format; + unsigned int numChannels; + size_t sizeInBytes; + } linear; + struct { + hipDeviceptr_t devPtr; + hipArray_Format format; + unsigned int numChannels; + size_t width; + size_t height; + size_t pitchInBytes; + } pitch2D; + struct { + int reserved[32]; + } reserved; + } res; + unsigned int flags; +} hipResourceDesc; + +/** + * hip texture resource view formats + */ +typedef enum hipResourceViewFormat { + hipResViewFormatNone = 0x00, + hipResViewFormatUnsignedChar1 = 0x01, + hipResViewFormatUnsignedChar2 = 0x02, + hipResViewFormatUnsignedChar4 = 0x03, + hipResViewFormatSignedChar1 = 0x04, + hipResViewFormatSignedChar2 = 0x05, + hipResViewFormatSignedChar4 = 0x06, + hipResViewFormatUnsignedShort1 = 0x07, + hipResViewFormatUnsignedShort2 = 0x08, + hipResViewFormatUnsignedShort4 = 0x09, + hipResViewFormatSignedShort1 = 0x0a, + hipResViewFormatSignedShort2 = 0x0b, + hipResViewFormatSignedShort4 = 0x0c, + hipResViewFormatUnsignedInt1 = 0x0d, + hipResViewFormatUnsignedInt2 = 0x0e, + hipResViewFormatUnsignedInt4 = 0x0f, + hipResViewFormatSignedInt1 = 0x10, + hipResViewFormatSignedInt2 = 0x11, + hipResViewFormatSignedInt4 = 0x12, + hipResViewFormatHalf1 = 0x13, + hipResViewFormatHalf2 = 0x14, + hipResViewFormatHalf4 = 0x15, + hipResViewFormatFloat1 = 0x16, + hipResViewFormatFloat2 = 0x17, + hipResViewFormatFloat4 = 0x18, + hipResViewFormatUnsignedBlockCompressed1 = 0x19, + hipResViewFormatUnsignedBlockCompressed2 = 0x1a, + hipResViewFormatUnsignedBlockCompressed3 = 0x1b, + hipResViewFormatUnsignedBlockCompressed4 = 0x1c, + hipResViewFormatSignedBlockCompressed4 = 0x1d, + hipResViewFormatUnsignedBlockCompressed5 = 0x1e, + hipResViewFormatSignedBlockCompressed5 = 0x1f, + hipResViewFormatUnsignedBlockCompressed6H = 0x20, + hipResViewFormatSignedBlockCompressed6H = 0x21, + hipResViewFormatUnsignedBlockCompressed7 = 0x22 +}hipResourceViewFormat; + +typedef enum HIPresourceViewFormat_enum +{ + HIP_RES_VIEW_FORMAT_NONE = 0x00, /**< No resource view format (use underlying resource format) */ + HIP_RES_VIEW_FORMAT_UINT_1X8 = 0x01, /**< 1 channel unsigned 8-bit integers */ + HIP_RES_VIEW_FORMAT_UINT_2X8 = 0x02, /**< 2 channel unsigned 8-bit integers */ + HIP_RES_VIEW_FORMAT_UINT_4X8 = 0x03, /**< 4 channel unsigned 8-bit integers */ + HIP_RES_VIEW_FORMAT_SINT_1X8 = 0x04, /**< 1 channel signed 8-bit integers */ + HIP_RES_VIEW_FORMAT_SINT_2X8 = 0x05, /**< 2 channel signed 8-bit integers */ + HIP_RES_VIEW_FORMAT_SINT_4X8 = 0x06, /**< 4 channel signed 8-bit integers */ + HIP_RES_VIEW_FORMAT_UINT_1X16 = 0x07, /**< 1 channel unsigned 16-bit integers */ + HIP_RES_VIEW_FORMAT_UINT_2X16 = 0x08, /**< 2 channel unsigned 16-bit integers */ + HIP_RES_VIEW_FORMAT_UINT_4X16 = 0x09, /**< 4 channel unsigned 16-bit integers */ + HIP_RES_VIEW_FORMAT_SINT_1X16 = 0x0a, /**< 1 channel signed 16-bit integers */ + HIP_RES_VIEW_FORMAT_SINT_2X16 = 0x0b, /**< 2 channel signed 16-bit integers */ + HIP_RES_VIEW_FORMAT_SINT_4X16 = 0x0c, /**< 4 channel signed 16-bit integers */ + HIP_RES_VIEW_FORMAT_UINT_1X32 = 0x0d, /**< 1 channel unsigned 32-bit integers */ + HIP_RES_VIEW_FORMAT_UINT_2X32 = 0x0e, /**< 2 channel unsigned 32-bit integers */ + HIP_RES_VIEW_FORMAT_UINT_4X32 = 0x0f, /**< 4 channel unsigned 32-bit integers */ + HIP_RES_VIEW_FORMAT_SINT_1X32 = 0x10, /**< 1 channel signed 32-bit integers */ + HIP_RES_VIEW_FORMAT_SINT_2X32 = 0x11, /**< 2 channel signed 32-bit integers */ + HIP_RES_VIEW_FORMAT_SINT_4X32 = 0x12, /**< 4 channel signed 32-bit integers */ + HIP_RES_VIEW_FORMAT_FLOAT_1X16 = 0x13, /**< 1 channel 16-bit floating point */ + HIP_RES_VIEW_FORMAT_FLOAT_2X16 = 0x14, /**< 2 channel 16-bit floating point */ + HIP_RES_VIEW_FORMAT_FLOAT_4X16 = 0x15, /**< 4 channel 16-bit floating point */ + HIP_RES_VIEW_FORMAT_FLOAT_1X32 = 0x16, /**< 1 channel 32-bit floating point */ + HIP_RES_VIEW_FORMAT_FLOAT_2X32 = 0x17, /**< 2 channel 32-bit floating point */ + HIP_RES_VIEW_FORMAT_FLOAT_4X32 = 0x18, /**< 4 channel 32-bit floating point */ + HIP_RES_VIEW_FORMAT_UNSIGNED_BC1 = 0x19, /**< Block compressed 1 */ + HIP_RES_VIEW_FORMAT_UNSIGNED_BC2 = 0x1a, /**< Block compressed 2 */ + HIP_RES_VIEW_FORMAT_UNSIGNED_BC3 = 0x1b, /**< Block compressed 3 */ + HIP_RES_VIEW_FORMAT_UNSIGNED_BC4 = 0x1c, /**< Block compressed 4 unsigned */ + HIP_RES_VIEW_FORMAT_SIGNED_BC4 = 0x1d, /**< Block compressed 4 signed */ + HIP_RES_VIEW_FORMAT_UNSIGNED_BC5 = 0x1e, /**< Block compressed 5 unsigned */ + HIP_RES_VIEW_FORMAT_SIGNED_BC5 = 0x1f, /**< Block compressed 5 signed */ + HIP_RES_VIEW_FORMAT_UNSIGNED_BC6H = 0x20, /**< Block compressed 6 unsigned half-float */ + HIP_RES_VIEW_FORMAT_SIGNED_BC6H = 0x21, /**< Block compressed 6 signed half-float */ + HIP_RES_VIEW_FORMAT_UNSIGNED_BC7 = 0x22 /**< Block compressed 7 */ +} HIPresourceViewFormat; + +/** + * hip resource view descriptor + */ +struct hipResourceViewDesc { + enum hipResourceViewFormat format; + size_t width; + size_t height; + size_t depth; + unsigned int firstMipmapLevel; + unsigned int lastMipmapLevel; + unsigned int firstLayer; + unsigned int lastLayer; +}; + + +typedef struct hipTextureDesc_st { + hipTextureAddressMode addressMode[3]; + hipTextureFilterMode filterMode; + unsigned int flags; + unsigned int maxAnisotropy; + hipTextureFilterMode mipmapFilterMode; + float mipmapLevelBias; + float minMipmapLevelClamp; + float maxMipmapLevelClamp; + float borderColor[4]; + int reserved[12]; +} hipTextureDesc; + +/** + * Resource view descriptor + */ +typedef struct HIP_RESOURCE_VIEW_DESC_st { + hipResourceViewFormat format; + size_t width; + size_t height; + size_t depth; + unsigned int firstMipmapLevel; + unsigned int lastMipmapLevel; + unsigned int firstLayer; + unsigned int lastLayer; + unsigned int reserved[16]; +} HIP_RESOURCE_VIEW_DESC; + +typedef struct HIP_POINTER_ATTRIBUTE_P2P_TOKENS_st { + unsigned long long p2pToken; + unsigned int vaSpaceToken; +} HIP_POINTER_ATTRIBUTE_P2P_TOKENS; + + +typedef unsigned int GLenum; +typedef unsigned int GLuint; +typedef int GLint; + +typedef enum HIPGLDeviceList_enum { + HIP_GL_DEVICE_LIST_ALL = 0x01, + HIP_GL_DEVICE_LIST_CURRENT_FRAME = 0x02, + HIP_GL_DEVICE_LIST_NEXT_FRAME = 0x03, +} HIPGLDeviceList; + +typedef enum HIPGLmap_flags_enum { + HIP_GL_MAP_RESOURCE_FLAGS_NONE = 0x00, + HIP_GL_MAP_RESOURCE_FLAGS_READ_ONLY = 0x01, + HIP_GL_MAP_RESOURCE_FLAGS_WRITE_DISCARD = 0x02, +} HIPGLmap_flags; + + +/* Function types. */ +typedef hipError_t HIPAPI thipGetErrorName(hipError_t error, const char** pStr); +typedef hipError_t HIPAPI thipInit(unsigned int Flags); +typedef hipError_t HIPAPI thipDriverGetVersion(int* driverVersion); +typedef hipError_t HIPAPI thipGetDevice(hipDevice_t* device, int ordinal); +typedef hipError_t HIPAPI thipGetDeviceCount(int* count); +typedef hipError_t HIPAPI thipDeviceGetName(char* name, int len, hipDevice_t dev); +typedef hipError_t HIPAPI thipDeviceGetAttribute(int* pi, hipDeviceAttribute_t attrib, hipDevice_t dev); +typedef hipError_t HIPAPI thipDeviceComputeCapability(int* major, int* minor, hipDevice_t dev); +typedef hipError_t HIPAPI thipDevicePrimaryCtxRetain(hipCtx_t* pctx, hipDevice_t dev); +typedef hipError_t HIPAPI thipDevicePrimaryCtxRelease(hipDevice_t dev); +typedef hipError_t HIPAPI thipDevicePrimaryCtxSetFlags(hipDevice_t dev, unsigned int flags); +typedef hipError_t HIPAPI thipDevicePrimaryCtxGetState(hipDevice_t dev, unsigned int* flags, int* active); +typedef hipError_t HIPAPI thipDevicePrimaryCtxReset(hipDevice_t dev); +typedef hipError_t HIPAPI thipCtxCreate(hipCtx_t* pctx, unsigned int flags, hipDevice_t dev); +typedef hipError_t HIPAPI thipCtxDestroy(hipCtx_t ctx); +typedef hipError_t HIPAPI thipCtxPushCurrent(hipCtx_t ctx); +typedef hipError_t HIPAPI thipCtxPopCurrent(hipCtx_t* pctx); +typedef hipError_t HIPAPI thipCtxSetCurrent(hipCtx_t ctx); +typedef hipError_t HIPAPI thipCtxGetCurrent(hipCtx_t* pctx); +typedef hipError_t HIPAPI thipCtxGetDevice(hipDevice_t* device); +typedef hipError_t HIPAPI thipCtxGetFlags(unsigned int* flags); +typedef hipError_t HIPAPI thipCtxSynchronize(void); +typedef hipError_t HIPAPI thipDeviceSynchronize(void); +typedef hipError_t HIPAPI thipCtxGetCacheConfig(hipFuncCache_t* pconfig); +typedef hipError_t HIPAPI thipCtxSetCacheConfig(hipFuncCache_t config); +typedef hipError_t HIPAPI thipCtxGetSharedMemConfig(hipSharedMemConfig* pConfig); +typedef hipError_t HIPAPI thipCtxSetSharedMemConfig(hipSharedMemConfig config); +typedef hipError_t HIPAPI thipCtxGetApiVersion(hipCtx_t ctx, unsigned int* version); +typedef hipError_t HIPAPI thipModuleLoad(hipModule_t* module, const char* fname); +typedef hipError_t HIPAPI thipModuleLoadData(hipModule_t* module, const void* image); +typedef hipError_t HIPAPI thipModuleLoadDataEx(hipModule_t* module, const void* image, unsigned int numOptions, hipJitOption* options, void** optionValues); +typedef hipError_t HIPAPI thipModuleUnload(hipModule_t hmod); +typedef hipError_t HIPAPI thipModuleGetFunction(hipFunction_t* hfunc, hipModule_t hmod, const char* name); +typedef hipError_t HIPAPI thipModuleGetGlobal(hipDeviceptr_t* dptr, size_t* bytes, hipModule_t hmod, const char* name); +typedef hipError_t HIPAPI thipModuleGetTexRef(textureReference** pTexRef, hipModule_t hmod, const char* name); +typedef hipError_t HIPAPI thipMemGetInfo(size_t* free, size_t* total); +typedef hipError_t HIPAPI thipMalloc(hipDeviceptr_t* dptr, size_t bytesize); +typedef hipError_t HIPAPI thipMemAllocPitch(hipDeviceptr_t* dptr, size_t* pPitch, size_t WidthInBytes, size_t Height, unsigned int ElementSizeBytes); +typedef hipError_t HIPAPI thipFree(hipDeviceptr_t dptr); +typedef hipError_t HIPAPI thipMemGetAddressRange(hipDeviceptr_t* pbase, size_t* psize, hipDeviceptr_t dptr); +typedef hipError_t HIPAPI thipHostMalloc(void** pp, size_t bytesize); +typedef hipError_t HIPAPI thipHostFree(void* p); +typedef hipError_t HIPAPI thipMemHostAlloc(void** pp, size_t bytesize, unsigned int Flags); +typedef hipError_t HIPAPI thipHostGetDevicePointer(hipDeviceptr_t* pdptr, void* p, unsigned int Flags); +typedef hipError_t HIPAPI thipHostGetFlags(unsigned int* pFlags, void* p); +typedef hipError_t HIPAPI thipMallocManaged(hipDeviceptr_t* dptr, size_t bytesize, unsigned int flags); +typedef hipError_t HIPAPI thipDeviceGetByPCIBusId(hipDevice_t* dev, const char* pciBusId); +typedef hipError_t HIPAPI thipDeviceGetPCIBusId(char* pciBusId, int len, hipDevice_t dev); +typedef hipError_t HIPAPI thipMemHostUnregister(void* p); +typedef hipError_t HIPAPI thipMemcpy(hipDeviceptr_t dst, hipDeviceptr_t src, size_t ByteCount); +typedef hipError_t HIPAPI thipMemcpyPeer(hipDeviceptr_t dstDevice, hipCtx_t dstContext, hipDeviceptr_t srcDevice, hipCtx_t srcContext, size_t ByteCount); +typedef hipError_t HIPAPI thipMemcpyHtoD(hipDeviceptr_t dstDevice, void* srcHost, size_t ByteCount); +typedef hipError_t HIPAPI thipMemcpyDtoH(void* dstHost, hipDeviceptr_t srcDevice, size_t ByteCount); +typedef hipError_t HIPAPI thipMemcpyDtoD(hipDeviceptr_t dstDevice, hipDeviceptr_t srcDevice, size_t ByteCount); +typedef hipError_t HIPAPI thipDrvMemcpy2DUnaligned(const hip_Memcpy2D* pCopy); +typedef hipError_t HIPAPI thipMemcpyParam2D(const hip_Memcpy2D* pCopy); +typedef hipError_t HIPAPI thipDrvMemcpy3D(const HIP_MEMCPY3D* pCopy); +typedef hipError_t HIPAPI thipMemcpyHtoDAsync(hipDeviceptr_t dstDevice, const void* srcHost, size_t ByteCount, hipStream_t hStream); +typedef hipError_t HIPAPI thipMemcpyDtoHAsync(void* dstHost, hipDeviceptr_t srcDevice, size_t ByteCount, hipStream_t hStream); +typedef hipError_t HIPAPI thipMemcpyParam2DAsync(const hip_Memcpy2D* pCopy, hipStream_t hStream); +typedef hipError_t HIPAPI thipDrvMemcpy3DAsync(const HIP_MEMCPY3D* pCopy, hipStream_t hStream); +typedef hipError_t HIPAPI thipMemsetD8(hipDeviceptr_t dstDevice, unsigned char uc, size_t N); +typedef hipError_t HIPAPI thipMemsetD16(hipDeviceptr_t dstDevice, unsigned short us, size_t N); +typedef hipError_t HIPAPI thipMemsetD32(hipDeviceptr_t dstDevice, unsigned int ui, size_t N); +typedef hipError_t HIPAPI thipMemsetD8Async(hipDeviceptr_t dstDevice, unsigned char uc, size_t N, hipStream_t hStream); +typedef hipError_t HIPAPI thipMemsetD16Async(hipDeviceptr_t dstDevice, unsigned short us, size_t N, hipStream_t hStream); +typedef hipError_t HIPAPI thipMemsetD32Async(hipDeviceptr_t dstDevice, unsigned int ui, size_t N, hipStream_t hStream); +typedef hipError_t HIPAPI thipMemsetD2D8Async(hipDeviceptr_t dstDevice, size_t dstPitch, unsigned char uc, size_t Width, size_t Height, hipStream_t hStream); +typedef hipError_t HIPAPI thipMemsetD2D16Async(hipDeviceptr_t dstDevice, size_t dstPitch, unsigned short us, size_t Width, size_t Height, hipStream_t hStream); +typedef hipError_t HIPAPI thipMemsetD2D32Async(hipDeviceptr_t dstDevice, size_t dstPitch, unsigned int ui, size_t Width, size_t Height, hipStream_t hStream); +typedef hipError_t HIPAPI thipArrayCreate(hArray ** pHandle, const HIP_ARRAY_DESCRIPTOR* pAllocateArray); +typedef hipError_t HIPAPI thipArrayDestroy(hArray hArray); +typedef hipError_t HIPAPI thipArray3DCreate(hArray * pHandle, const HIP_ARRAY3D_DESCRIPTOR* pAllocateArray); +typedef hipError_t HIPAPI hipPointerGetAttributes(hipPointerAttribute_t* attributes, const void* ptr); +typedef hipError_t HIPAPI thipStreamCreateWithFlags(hipStream_t* phStream, unsigned int Flags); +typedef hipError_t HIPAPI thipStreamCreateWithPriority(hipStream_t* phStream, unsigned int flags, int priority); +typedef hipError_t HIPAPI thipStreamGetPriority(hipStream_t hStream, int* priority); +typedef hipError_t HIPAPI thipStreamGetFlags(hipStream_t hStream, unsigned int* flags); +typedef hipError_t HIPAPI thipStreamWaitEvent(hipStream_t hStream, hipEvent_t hEvent, unsigned int Flags); +typedef hipError_t HIPAPI thipStreamAddCallback(hipStream_t hStream, hipStreamCallback_t callback, void* userData, unsigned int flags); +typedef hipError_t HIPAPI thipStreamQuery(hipStream_t hStream); +typedef hipError_t HIPAPI thipStreamSynchronize(hipStream_t hStream); +typedef hipError_t HIPAPI thipStreamDestroy(hipStream_t hStream); +typedef hipError_t HIPAPI thipEventCreateWithFlags(hipEvent_t* phEvent, unsigned int Flags); +typedef hipError_t HIPAPI thipEventRecord(hipEvent_t hEvent, hipStream_t hStream); +typedef hipError_t HIPAPI thipEventQuery(hipEvent_t hEvent); +typedef hipError_t HIPAPI thipEventSynchronize(hipEvent_t hEvent); +typedef hipError_t HIPAPI thipEventDestroy(hipEvent_t hEvent); +typedef hipError_t HIPAPI thipEventElapsedTime(float* pMilliseconds, hipEvent_t hStart, hipEvent_t hEnd); +typedef hipError_t HIPAPI thipFuncGetAttribute(int* pi, hipFunction_attribute attrib, hipFunction_t hfunc); +typedef hipError_t HIPAPI thipFuncSetCacheConfig(hipFunction_t hfunc, hipFuncCache_t config); +typedef hipError_t HIPAPI thipModuleLaunchKernel(hipFunction_t f, unsigned int gridDimX, unsigned int gridDimY, unsigned int gridDimZ, unsigned int blockDimX, unsigned int blockDimY, unsigned int blockDimZ, unsigned int sharedMemBytes, hipStream_t hStream, void** kernelParams, void** extra); +typedef hipError_t HIPAPI thipDrvOccupancyMaxActiveBlocksPerMultiprocessor(int* numBlocks, hipFunction_t func, int blockSize, size_t dynamicSMemSize); +typedef hipError_t HIPAPI thipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags(int* numBlocks, hipFunction_t func, int blockSize, size_t dynamicSMemSize, unsigned int flags); +typedef hipError_t HIPAPI thipModuleOccupancyMaxPotentialBlockSize(int* minGridSize, int* blockSize, hipFunction_t func, size_t dynamicSMemSize, int blockSizeLimit); +typedef hipError_t HIPAPI thipTexRefSetArray(hipTexRef hTexRef, hArray * hArray, unsigned int Flags); +typedef hipError_t HIPAPI thipTexRefSetAddress(size_t* ByteOffset, hipTexRef hTexRef, hipDeviceptr_t dptr, size_t bytes); +typedef hipError_t HIPAPI thipTexRefSetAddress2D(hipTexRef hTexRef, const HIP_ARRAY_DESCRIPTOR* desc, hipDeviceptr_t dptr, size_t Pitch); +typedef hipError_t HIPAPI thipTexRefSetFormat(hipTexRef hTexRef, hipArray_Format fmt, int NumPackedComponents); +typedef hipError_t HIPAPI thipTexRefSetAddressMode(hipTexRef hTexRef, int dim, hipTextureAddressMode am); +typedef hipError_t HIPAPI thipTexRefSetFilterMode(hipTexRef hTexRef, hipTextureFilterMode fm); +typedef hipError_t HIPAPI thipTexRefSetFlags(hipTexRef hTexRef, unsigned int Flags); +typedef hipError_t HIPAPI thipTexRefGetAddress(hipDeviceptr_t* pdptr, hipTexRef hTexRef); +typedef hipError_t HIPAPI thipTexRefGetArray(hArray ** phArray, hipTexRef hTexRef); +typedef hipError_t HIPAPI thipTexRefGetAddressMode(hipTextureAddressMode* pam, hipTexRef hTexRef, int dim); +typedef hipError_t HIPAPI thipTexObjectCreate(hipTextureObject_t* pTexObject, const hipResourceDesc* pResDesc, const hipTextureDesc* pTexDesc, const HIP_RESOURCE_VIEW_DESC* pResViewDesc); +typedef hipError_t HIPAPI thipTexObjectDestroy(hipTextureObject_t texObject); +typedef hipError_t HIPAPI thipDeviceCanAccessPeer(int* canAccessPeer, hipDevice_t dev, hipDevice_t peerDev); +typedef hipError_t HIPAPI thipCtxEnablePeerAccess(hipCtx_t peerContext, unsigned int Flags); +typedef hipError_t HIPAPI thipCtxDisablePeerAccess(hipCtx_t peerContext); +typedef hipError_t HIPAPI thipDeviceGetP2PAttribute(int* value, hipDeviceP2PAttr attrib, hipDevice_t srcDevice, hipDevice_t dstDevice); +typedef hipError_t HIPAPI thipGraphicsUnregisterResource(hipGraphicsResource resource); +typedef hipError_t HIPAPI thipGraphicsResourceGetMappedMipmappedArray(hipMipmappedArray_t* pMipmappedArray, hipGraphicsResource resource); +typedef hipError_t HIPAPI thipGraphicsResourceGetMappedPointer(hipDeviceptr_t* pDevPtr, size_t* pSize, hipGraphicsResource resource); +typedef hipError_t HIPAPI thipGraphicsMapResources(unsigned int count, hipGraphicsResource* resources, hipStream_t hStream); +typedef hipError_t HIPAPI thipGraphicsUnmapResources(unsigned int count, hipGraphicsResource* resources, hipStream_t hStream); +typedef hipError_t HIPAPI thipGraphicsGLRegisterBuffer(hipGraphicsResource* pCudaResource, GLuint buffer, unsigned int Flags); +typedef hipError_t HIPAPI thipGLGetDevices(unsigned int* pHipDeviceCount, int* pHipDevices, unsigned int hipDeviceCount, hipGLDeviceList deviceList); + + +/* Function declarations. */ +extern thipGetErrorName *hipGetErrorName; +extern thipInit *hipInit; +extern thipDriverGetVersion *hipDriverGetVersion; +extern thipGetDevice *hipGetDevice; +extern thipGetDeviceCount *hipGetDeviceCount; +extern thipDeviceGetName *hipDeviceGetName; +extern thipDeviceGetAttribute *hipDeviceGetAttribute; +extern thipDeviceComputeCapability *hipDeviceComputeCapability; +extern thipDevicePrimaryCtxRetain *hipDevicePrimaryCtxRetain; +extern thipDevicePrimaryCtxRelease *hipDevicePrimaryCtxRelease; +extern thipDevicePrimaryCtxSetFlags *hipDevicePrimaryCtxSetFlags; +extern thipDevicePrimaryCtxGetState *hipDevicePrimaryCtxGetState; +extern thipDevicePrimaryCtxReset *hipDevicePrimaryCtxReset; +extern thipCtxCreate *hipCtxCreate; +extern thipCtxDestroy *hipCtxDestroy; +extern thipCtxPushCurrent *hipCtxPushCurrent; +extern thipCtxPopCurrent *hipCtxPopCurrent; +extern thipCtxSetCurrent *hipCtxSetCurrent; +extern thipCtxGetCurrent *hipCtxGetCurrent; +extern thipCtxGetDevice *hipCtxGetDevice; +extern thipCtxGetFlags *hipCtxGetFlags; +extern thipCtxSynchronize *hipCtxSynchronize; +extern thipDeviceSynchronize *hipDeviceSynchronize; +extern thipCtxGetCacheConfig *hipCtxGetCacheConfig; +extern thipCtxSetCacheConfig *hipCtxSetCacheConfig; +extern thipCtxGetSharedMemConfig *hipCtxGetSharedMemConfig; +extern thipCtxSetSharedMemConfig *hipCtxSetSharedMemConfig; +extern thipCtxGetApiVersion *hipCtxGetApiVersion; +extern thipModuleLoad *hipModuleLoad; +extern thipModuleLoadData *hipModuleLoadData; +extern thipModuleLoadDataEx *hipModuleLoadDataEx; +extern thipModuleUnload *hipModuleUnload; +extern thipModuleGetFunction *hipModuleGetFunction; +extern thipModuleGetGlobal *hipModuleGetGlobal; +extern thipModuleGetTexRef *hipModuleGetTexRef; +extern thipMemGetInfo *hipMemGetInfo; +extern thipMalloc *hipMalloc; +extern thipMemAllocPitch *hipMemAllocPitch; +extern thipFree *hipFree; +extern thipMemGetAddressRange *hipMemGetAddressRange; +extern thipHostMalloc *hipHostMalloc; +extern thipHostFree *hipHostFree; +extern thipHostGetDevicePointer *hipHostGetDevicePointer; +extern thipHostGetFlags *hipHostGetFlags; +extern thipMallocManaged *hipMallocManaged; +extern thipDeviceGetByPCIBusId *hipDeviceGetByPCIBusId; +extern thipDeviceGetPCIBusId *hipDeviceGetPCIBusId; +extern thipMemcpyPeer *hipMemcpyPeer; +extern thipMemcpyHtoD *hipMemcpyHtoD; +extern thipMemcpyDtoH *hipMemcpyDtoH; +extern thipMemcpyDtoD *hipMemcpyDtoD; +extern thipDrvMemcpy2DUnaligned *hipDrvMemcpy2DUnaligned; +extern thipMemcpyParam2D *hipMemcpyParam2D; +extern thipDrvMemcpy3D *hipDrvMemcpy3D; +extern thipMemcpyHtoDAsync *hipMemcpyHtoDAsync; +extern thipMemcpyDtoHAsync *hipMemcpyDtoHAsync; +extern thipMemcpyParam2DAsync *hipMemcpyParam2DAsync; +extern thipDrvMemcpy3DAsync *hipDrvMemcpy3DAsync; +extern thipMemsetD8 *hipMemsetD8; +extern thipMemsetD16 *hipMemsetD16; +extern thipMemsetD32 *hipMemsetD32; +extern thipMemsetD8Async *hipMemsetD8Async; +extern thipMemsetD16Async *hipMemsetD16Async; +extern thipMemsetD32Async *hipMemsetD32Async; +extern thipArrayCreate *hipArrayCreate; +extern thipArrayDestroy *hipArrayDestroy; +extern thipArray3DCreate *hipArray3DCreate; +extern thipStreamCreateWithFlags *hipStreamCreateWithFlags; +extern thipStreamCreateWithPriority *hipStreamCreateWithPriority; +extern thipStreamGetPriority *hipStreamGetPriority; +extern thipStreamGetFlags *hipStreamGetFlags; +extern thipStreamWaitEvent *hipStreamWaitEvent; +extern thipStreamAddCallback *hipStreamAddCallback; +extern thipStreamQuery *hipStreamQuery; +extern thipStreamSynchronize *hipStreamSynchronize; +extern thipStreamDestroy *hipStreamDestroy; +extern thipEventCreateWithFlags *hipEventCreateWithFlags; +extern thipEventRecord *hipEventRecord; +extern thipEventQuery *hipEventQuery; +extern thipEventSynchronize *hipEventSynchronize; +extern thipEventDestroy *hipEventDestroy; +extern thipEventElapsedTime *hipEventElapsedTime; +extern thipFuncGetAttribute *hipFuncGetAttribute; +extern thipFuncSetCacheConfig *hipFuncSetCacheConfig; +extern thipModuleLaunchKernel *hipModuleLaunchKernel; +extern thipDrvOccupancyMaxActiveBlocksPerMultiprocessor *hipDrvOccupancyMaxActiveBlocksPerMultiprocessor; +extern thipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags *hipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags; +extern thipModuleOccupancyMaxPotentialBlockSize *hipModuleOccupancyMaxPotentialBlockSize; +extern thipTexRefSetArray *hipTexRefSetArray; +extern thipTexRefSetAddress *hipTexRefSetAddress; +extern thipTexRefSetAddress2D *hipTexRefSetAddress2D; +extern thipTexRefSetFormat *hipTexRefSetFormat; +extern thipTexRefSetAddressMode *hipTexRefSetAddressMode; +extern thipTexRefSetFilterMode *hipTexRefSetFilterMode; +extern thipTexRefSetFlags *hipTexRefSetFlags; +extern thipTexRefGetAddress *hipTexRefGetAddress; +extern thipTexRefGetArray *hipTexRefGetArray; +extern thipTexRefGetAddressMode *hipTexRefGetAddressMode; +extern thipTexObjectCreate *hipTexObjectCreate; +extern thipTexObjectDestroy *hipTexObjectDestroy; +extern thipDeviceCanAccessPeer *hipDeviceCanAccessPeer; +extern thipCtxEnablePeerAccess *hipCtxEnablePeerAccess; +extern thipCtxDisablePeerAccess *hipCtxDisablePeerAccess; +extern thipDeviceGetP2PAttribute *hipDeviceGetP2PAttribute; +extern thipGraphicsUnregisterResource *hipGraphicsUnregisterResource; +extern thipGraphicsResourceGetMappedMipmappedArray *hipGraphicsResourceGetMappedMipmappedArray; +extern thipGraphicsResourceGetMappedPointer *hipGraphicsResourceGetMappedPointer; +extern thipGraphicsMapResources *hipGraphicsMapResources; +extern thipGraphicsUnmapResources *hipGraphicsUnmapResources; + +extern thipGraphicsGLRegisterBuffer *hipGraphicsGLRegisterBuffer; +extern thipGLGetDevices *hipGLGetDevices; + + +enum { + HIPEW_SUCCESS = 0, + HIPEW_ERROR_OPEN_FAILED = -1, + HIPEW_ERROR_ATEXIT_FAILED = -2, +}; + +enum { + HIPEW_INIT_HIP = 1, +}; + +int hipewInit(hipuint32_t flags); +const char *hipewErrorString(hipError_t result); +const char *hipewCompilerPath(void); +int hipewCompilerVersion(void); +int hipewNvrtcVersion(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __HIPEW_H__ */ diff --git a/extern/hipew/src/hipew.c b/extern/hipew/src/hipew.c new file mode 100644 index 00000000000..9d5a63f869a --- /dev/null +++ b/extern/hipew/src/hipew.c @@ -0,0 +1,533 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +#ifdef _MSC_VER +# if _MSC_VER < 1900 +# define snprintf _snprintf +# endif +# define popen _popen +# define pclose _pclose +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# define VC_EXTRALEAN +# include + +/* Utility macros. */ + +typedef HMODULE DynamicLibrary; + +# define dynamic_library_open(path) LoadLibraryA(path) +# define dynamic_library_close(lib) FreeLibrary(lib) +# define dynamic_library_find(lib, symbol) GetProcAddress(lib, symbol) +#else +# include + +typedef void* DynamicLibrary; + +# define dynamic_library_open(path) dlopen(path, RTLD_NOW) +# define dynamic_library_close(lib) dlclose(lib) +# define dynamic_library_find(lib, symbol) dlsym(lib, symbol) +#endif + +#define _LIBRARY_FIND_CHECKED(lib, name) \ + name = (t##name *)dynamic_library_find(lib, #name); \ + assert(name); + +#define _LIBRARY_FIND(lib, name) \ + name = (t##name *)dynamic_library_find(lib, #name); + +#define HIP_LIBRARY_FIND_CHECKED(name) \ + _LIBRARY_FIND_CHECKED(hip_lib, name) +#define HIP_LIBRARY_FIND(name) _LIBRARY_FIND(hip_lib, name) + + +static DynamicLibrary hip_lib; + +/* Function definitions. */ +thipGetErrorName *hipGetErrorName; +thipInit *hipInit; +thipDriverGetVersion *hipDriverGetVersion; +thipGetDevice *hipGetDevice; +thipGetDeviceCount *hipGetDeviceCount; +thipDeviceGetName *hipDeviceGetName; +thipDeviceGetAttribute *hipDeviceGetAttribute; +thipDeviceComputeCapability *hipDeviceComputeCapability; +thipDevicePrimaryCtxRetain *hipDevicePrimaryCtxRetain; +thipDevicePrimaryCtxRelease *hipDevicePrimaryCtxRelease; +thipDevicePrimaryCtxSetFlags *hipDevicePrimaryCtxSetFlags; +thipDevicePrimaryCtxGetState *hipDevicePrimaryCtxGetState; +thipDevicePrimaryCtxReset *hipDevicePrimaryCtxReset; +thipCtxCreate *hipCtxCreate; +thipCtxDestroy *hipCtxDestroy; +thipCtxPushCurrent *hipCtxPushCurrent; +thipCtxPopCurrent *hipCtxPopCurrent; +thipCtxSetCurrent *hipCtxSetCurrent; +thipCtxGetCurrent *hipCtxGetCurrent; +thipCtxGetDevice *hipCtxGetDevice; +thipCtxGetFlags *hipCtxGetFlags; +thipCtxSynchronize *hipCtxSynchronize; +thipDeviceSynchronize *hipDeviceSynchronize; +thipCtxGetCacheConfig *hipCtxGetCacheConfig; +thipCtxSetCacheConfig *hipCtxSetCacheConfig; +thipCtxGetSharedMemConfig *hipCtxGetSharedMemConfig; +thipCtxSetSharedMemConfig *hipCtxSetSharedMemConfig; +thipCtxGetApiVersion *hipCtxGetApiVersion; +thipModuleLoad *hipModuleLoad; +thipModuleLoadData *hipModuleLoadData; +thipModuleLoadDataEx *hipModuleLoadDataEx; +thipModuleUnload *hipModuleUnload; +thipModuleGetFunction *hipModuleGetFunction; +thipModuleGetGlobal *hipModuleGetGlobal; +thipModuleGetTexRef *hipModuleGetTexRef; +thipMemGetInfo *hipMemGetInfo; +thipMalloc *hipMalloc; +thipMemAllocPitch *hipMemAllocPitch; +thipFree *hipFree; +thipMemGetAddressRange *hipMemGetAddressRange; +thipHostMalloc *hipHostMalloc; +thipHostFree *hipHostFree; +thipHostGetDevicePointer *hipHostGetDevicePointer; +thipHostGetFlags *hipHostGetFlags; +thipMallocManaged *hipMallocManaged; +thipDeviceGetByPCIBusId *hipDeviceGetByPCIBusId; +thipDeviceGetPCIBusId *hipDeviceGetPCIBusId; +thipMemcpyPeer *hipMemcpyPeer; +thipMemcpyHtoD *hipMemcpyHtoD; +thipMemcpyDtoH *hipMemcpyDtoH; +thipMemcpyDtoD *hipMemcpyDtoD; +thipDrvMemcpy2DUnaligned *hipDrvMemcpy2DUnaligned; +thipMemcpyParam2D *hipMemcpyParam2D; +thipDrvMemcpy3D *hipDrvMemcpy3D; +thipMemcpyHtoDAsync *hipMemcpyHtoDAsync; +thipMemcpyDtoHAsync *hipMemcpyDtoHAsync; +thipMemcpyParam2DAsync *hipMemcpyParam2DAsync; +thipDrvMemcpy3DAsync *hipDrvMemcpy3DAsync; +thipMemsetD8 *hipMemsetD8; +thipMemsetD16 *hipMemsetD16; +thipMemsetD32 *hipMemsetD32; +thipMemsetD8Async *hipMemsetD8Async; +thipMemsetD16Async *hipMemsetD16Async; +thipMemsetD32Async *hipMemsetD32Async; +thipArrayCreate *hipArrayCreate; +thipArrayDestroy *hipArrayDestroy; +thipArray3DCreate *hipArray3DCreate; +thipStreamCreateWithFlags *hipStreamCreateWithFlags; +thipStreamCreateWithPriority *hipStreamCreateWithPriority; +thipStreamGetPriority *hipStreamGetPriority; +thipStreamGetFlags *hipStreamGetFlags; +thipStreamWaitEvent *hipStreamWaitEvent; +thipStreamAddCallback *hipStreamAddCallback; +thipStreamQuery *hipStreamQuery; +thipStreamSynchronize *hipStreamSynchronize; +thipStreamDestroy *hipStreamDestroy; +thipEventCreateWithFlags *hipEventCreateWithFlags; +thipEventRecord *hipEventRecord; +thipEventQuery *hipEventQuery; +thipEventSynchronize *hipEventSynchronize; +thipEventDestroy *hipEventDestroy; +thipEventElapsedTime *hipEventElapsedTime; +thipFuncGetAttribute *hipFuncGetAttribute; +thipFuncSetCacheConfig *hipFuncSetCacheConfig; +thipModuleLaunchKernel *hipModuleLaunchKernel; +thipDrvOccupancyMaxActiveBlocksPerMultiprocessor *hipDrvOccupancyMaxActiveBlocksPerMultiprocessor; +thipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags *hipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags; +thipModuleOccupancyMaxPotentialBlockSize *hipModuleOccupancyMaxPotentialBlockSize; +thipTexRefSetArray *hipTexRefSetArray; +thipTexRefSetAddress *hipTexRefSetAddress; +thipTexRefSetAddress2D *hipTexRefSetAddress2D; +thipTexRefSetFormat *hipTexRefSetFormat; +thipTexRefSetAddressMode *hipTexRefSetAddressMode; +thipTexRefSetFilterMode *hipTexRefSetFilterMode; +thipTexRefSetFlags *hipTexRefSetFlags; +thipTexRefGetAddress *hipTexRefGetAddress; +thipTexRefGetArray *hipTexRefGetArray; +thipTexRefGetAddressMode *hipTexRefGetAddressMode; +thipTexObjectCreate *hipTexObjectCreate; +thipTexObjectDestroy *hipTexObjectDestroy; +thipDeviceCanAccessPeer *hipDeviceCanAccessPeer; + +thipCtxEnablePeerAccess *hipCtxEnablePeerAccess; +thipCtxDisablePeerAccess *hipCtxDisablePeerAccess; +thipDeviceGetP2PAttribute *hipDeviceGetP2PAttribute; +thipGraphicsUnregisterResource *hipGraphicsUnregisterResource; +thipGraphicsMapResources *hipGraphicsMapResources; +thipGraphicsUnmapResources *hipGraphicsUnmapResources; +thipGraphicsResourceGetMappedPointer *hipGraphicsResourceGetMappedPointer; + +thipGraphicsGLRegisterBuffer *hipGraphicsGLRegisterBuffer; +thipGLGetDevices *hipGLGetDevices; + + + +static DynamicLibrary dynamic_library_open_find(const char **paths) { + int i = 0; + while (paths[i] != NULL) { + DynamicLibrary lib = dynamic_library_open(paths[i]); + if (lib != NULL) { + return lib; + } + ++i; + } + return NULL; +} + +/* Implementation function. */ +static void hipewHipExit(void) { + if (hip_lib != NULL) { + /* Ignore errors. */ + dynamic_library_close(hip_lib); + hip_lib = NULL; + } +} + +static int hipewHipInit(void) { + /* Library paths. */ +#ifdef _WIN32 + /* Expected in c:/windows/system or similar, no path needed. */ + const char *hip_paths[] = {"amdhip64.dll", NULL}; +#elif defined(__APPLE__) + /* Default installation path. */ + const char *hip_paths[] = {"", NULL}; +#else + const char *hip_paths[] = {"/opt/rocm/hip/lib/libamdhip64.so", NULL}; +#endif + static int initialized = 0; + static int result = 0; + int error, driver_version; + + if (initialized) { + return result; + } + + initialized = 1; + + error = atexit(hipewHipExit); + if (error) { + result = HIPEW_ERROR_ATEXIT_FAILED; + return result; + } + + /* Load library. */ + hip_lib = dynamic_library_open_find(hip_paths); + + if (hip_lib == NULL) { + result = HIPEW_ERROR_OPEN_FAILED; + return result; + } + + /* Fetch all function pointers. */ + HIP_LIBRARY_FIND_CHECKED(hipGetErrorName); + HIP_LIBRARY_FIND_CHECKED(hipInit); + HIP_LIBRARY_FIND_CHECKED(hipDriverGetVersion); + HIP_LIBRARY_FIND_CHECKED(hipGetDevice); + HIP_LIBRARY_FIND_CHECKED(hipGetDeviceCount); + HIP_LIBRARY_FIND_CHECKED(hipDeviceGetName); + HIP_LIBRARY_FIND_CHECKED(hipDeviceGetAttribute); + HIP_LIBRARY_FIND_CHECKED(hipDeviceComputeCapability); + HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxRetain); + HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxRelease); + HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxSetFlags); + HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxGetState); + HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxReset); + HIP_LIBRARY_FIND_CHECKED(hipCtxCreate); + HIP_LIBRARY_FIND_CHECKED(hipCtxDestroy); + HIP_LIBRARY_FIND_CHECKED(hipCtxPushCurrent); + HIP_LIBRARY_FIND_CHECKED(hipCtxPopCurrent); + HIP_LIBRARY_FIND_CHECKED(hipCtxSetCurrent); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetCurrent); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetDevice); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetFlags); + HIP_LIBRARY_FIND_CHECKED(hipCtxSynchronize); + HIP_LIBRARY_FIND_CHECKED(hipDeviceSynchronize); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetCacheConfig); + HIP_LIBRARY_FIND_CHECKED(hipCtxSetCacheConfig); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetSharedMemConfig); + HIP_LIBRARY_FIND_CHECKED(hipCtxSetSharedMemConfig); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetApiVersion); + HIP_LIBRARY_FIND_CHECKED(hipModuleLoad); + HIP_LIBRARY_FIND_CHECKED(hipModuleLoadData); + HIP_LIBRARY_FIND_CHECKED(hipModuleLoadDataEx); + HIP_LIBRARY_FIND_CHECKED(hipModuleUnload); + HIP_LIBRARY_FIND_CHECKED(hipModuleGetFunction); + HIP_LIBRARY_FIND_CHECKED(hipModuleGetGlobal); + HIP_LIBRARY_FIND_CHECKED(hipModuleGetTexRef); + HIP_LIBRARY_FIND_CHECKED(hipMemGetInfo); + HIP_LIBRARY_FIND_CHECKED(hipMalloc); + HIP_LIBRARY_FIND_CHECKED(hipMemAllocPitch); + HIP_LIBRARY_FIND_CHECKED(hipFree); + HIP_LIBRARY_FIND_CHECKED(hipMemGetAddressRange); + HIP_LIBRARY_FIND_CHECKED(hipHostMalloc); + HIP_LIBRARY_FIND_CHECKED(hipHostFree); + HIP_LIBRARY_FIND_CHECKED(hipHostGetDevicePointer); + HIP_LIBRARY_FIND_CHECKED(hipHostGetFlags); + HIP_LIBRARY_FIND_CHECKED(hipMallocManaged); + HIP_LIBRARY_FIND_CHECKED(hipDeviceGetByPCIBusId); + HIP_LIBRARY_FIND_CHECKED(hipDeviceGetPCIBusId); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyPeer); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyHtoD); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyDtoH); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyDtoD); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyParam2D); + HIP_LIBRARY_FIND_CHECKED(hipDrvMemcpy3D); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyHtoDAsync); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyDtoHAsync); + HIP_LIBRARY_FIND_CHECKED(hipDrvMemcpy2DUnaligned); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyParam2DAsync); + HIP_LIBRARY_FIND_CHECKED(hipDrvMemcpy3DAsync); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD8); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD16); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD32); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD8Async); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD16Async); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD32Async); + HIP_LIBRARY_FIND_CHECKED(hipArrayCreate); + HIP_LIBRARY_FIND_CHECKED(hipArrayDestroy); + HIP_LIBRARY_FIND_CHECKED(hipArray3DCreate); + HIP_LIBRARY_FIND_CHECKED(hipStreamCreateWithFlags); + HIP_LIBRARY_FIND_CHECKED(hipStreamCreateWithPriority); + HIP_LIBRARY_FIND_CHECKED(hipStreamGetPriority); + HIP_LIBRARY_FIND_CHECKED(hipStreamGetFlags); + HIP_LIBRARY_FIND_CHECKED(hipStreamWaitEvent); + HIP_LIBRARY_FIND_CHECKED(hipStreamAddCallback); + HIP_LIBRARY_FIND_CHECKED(hipStreamQuery); + HIP_LIBRARY_FIND_CHECKED(hipStreamSynchronize); + HIP_LIBRARY_FIND_CHECKED(hipStreamDestroy); + HIP_LIBRARY_FIND_CHECKED(hipEventCreateWithFlags); + HIP_LIBRARY_FIND_CHECKED(hipEventRecord); + HIP_LIBRARY_FIND_CHECKED(hipEventQuery); + HIP_LIBRARY_FIND_CHECKED(hipEventSynchronize); + HIP_LIBRARY_FIND_CHECKED(hipEventDestroy); + HIP_LIBRARY_FIND_CHECKED(hipEventElapsedTime); + HIP_LIBRARY_FIND_CHECKED(hipFuncGetAttribute); + HIP_LIBRARY_FIND_CHECKED(hipFuncSetCacheConfig); + HIP_LIBRARY_FIND_CHECKED(hipModuleLaunchKernel); + HIP_LIBRARY_FIND_CHECKED(hipModuleOccupancyMaxPotentialBlockSize); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetArray); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetAddress); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetAddress2D); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetFormat); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetAddressMode); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetFilterMode); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetFlags); + HIP_LIBRARY_FIND_CHECKED(hipTexRefGetAddress); + HIP_LIBRARY_FIND_CHECKED(hipTexRefGetAddressMode); + HIP_LIBRARY_FIND_CHECKED(hipTexObjectCreate); + HIP_LIBRARY_FIND_CHECKED(hipTexObjectDestroy); + HIP_LIBRARY_FIND_CHECKED(hipDeviceCanAccessPeer); + HIP_LIBRARY_FIND_CHECKED(hipCtxEnablePeerAccess); + HIP_LIBRARY_FIND_CHECKED(hipCtxDisablePeerAccess); + HIP_LIBRARY_FIND_CHECKED(hipDeviceGetP2PAttribute); +#ifdef _WIN32 + HIP_LIBRARY_FIND_CHECKED(hipGraphicsUnregisterResource); + HIP_LIBRARY_FIND_CHECKED(hipGraphicsMapResources); + HIP_LIBRARY_FIND_CHECKED(hipGraphicsUnmapResources); + HIP_LIBRARY_FIND_CHECKED(hipGraphicsResourceGetMappedPointer); + HIP_LIBRARY_FIND_CHECKED(hipGraphicsGLRegisterBuffer); + HIP_LIBRARY_FIND_CHECKED(hipGLGetDevices); +#endif + result = HIPEW_SUCCESS; + return result; +} + + + +int hipewInit(hipuint32_t flags) { + int result = HIPEW_SUCCESS; + + if (flags & HIPEW_INIT_HIP) { + result = hipewHipInit(); + if (result != HIPEW_SUCCESS) { + return result; + } + } + + return result; +} + + +const char *hipewErrorString(hipError_t result) { + switch (result) { + case hipSuccess: return "No errors"; + case hipErrorInvalidValue: return "Invalid value"; + case hipErrorOutOfMemory: return "Out of memory"; + case hipErrorNotInitialized: return "Driver not initialized"; + case hipErrorDeinitialized: return "Driver deinitialized"; + case hipErrorProfilerDisabled: return "Profiler disabled"; + case hipErrorProfilerNotInitialized: return "Profiler not initialized"; + case hipErrorProfilerAlreadyStarted: return "Profiler already started"; + case hipErrorProfilerAlreadyStopped: return "Profiler already stopped"; + case hipErrorNoDevice: return "No HIP-capable device available"; + case hipErrorInvalidDevice: return "Invalid device"; + case hipErrorInvalidImage: return "Invalid kernel image"; + case hipErrorInvalidContext: return "Invalid context"; + case hipErrorContextAlreadyCurrent: return "Context already current"; + case hipErrorMapFailed: return "Map failed"; + case hipErrorUnmapFailed: return "Unmap failed"; + case hipErrorArrayIsMapped: return "Array is mapped"; + case hipErrorAlreadyMapped: return "Already mapped"; + case hipErrorNoBinaryForGpu: return "No binary for GPU"; + case hipErrorAlreadyAcquired: return "Already acquired"; + case hipErrorNotMapped: return "Not mapped"; + case hipErrorNotMappedAsArray: return "Mapped resource not available for access as an array"; + case hipErrorNotMappedAsPointer: return "Mapped resource not available for access as a pointer"; + case hipErrorECCNotCorrectable: return "Uncorrectable ECC error detected"; + case hipErrorUnsupportedLimit: return "hipLimit_t not supported by device"; + case hipErrorContextAlreadyInUse: return "Context already in use"; + case hipErrorPeerAccessUnsupported: return "Peer access unsupported"; + case hipErrorInvalidKernelFile: return "Invalid ptx"; + case hipErrorInvalidGraphicsContext: return "Invalid graphics context"; + case hipErrorInvalidSource: return "Invalid source"; + case hipErrorFileNotFound: return "File not found"; + case hipErrorSharedObjectSymbolNotFound: return "Link to a shared object failed to resolve"; + case hipErrorSharedObjectInitFailed: return "Shared object initialization failed"; + case hipErrorOperatingSystem: return "Operating system"; + case hipErrorInvalidHandle: return "Invalid handle"; + case hipErrorNotFound: return "Not found"; + case hipErrorNotReady: return "HIP not ready"; + case hipErrorIllegalAddress: return "Illegal address"; + case hipErrorLaunchOutOfResources: return "Launch exceeded resources"; + case hipErrorLaunchTimeOut: return "Launch exceeded timeout"; + case hipErrorPeerAccessAlreadyEnabled: return "Peer access already enabled"; + case hipErrorPeerAccessNotEnabled: return "Peer access not enabled"; + case hipErrorSetOnActiveProcess: return "Primary context active"; + case hipErrorAssert: return "Assert"; + case hipErrorHostMemoryAlreadyRegistered: return "Host memory already registered"; + case hipErrorHostMemoryNotRegistered: return "Host memory not registered"; + case hipErrorLaunchFailure: return "Launch failed"; + case hipErrorCooperativeLaunchTooLarge: return "Cooperative launch too large"; + case hipErrorNotSupported: return "Not supported"; + case hipErrorUnknown: return "Unknown error"; + default: return "Unknown HIP error value"; + } +} + +static void path_join(const char *path1, + const char *path2, + int maxlen, + char *result) { +#if defined(WIN32) || defined(_WIN32) + const char separator = '\\'; +#else + const char separator = '/'; +#endif + int n = snprintf(result, maxlen, "%s%c%s", path1, separator, path2); + if (n != -1 && n < maxlen) { + result[n] = '\0'; + } + else { + result[maxlen - 1] = '\0'; + } +} + +static int path_exists(const char *path) { + struct stat st; + if (stat(path, &st)) { + return 0; + } + return 1; +} + +const char *hipewCompilerPath(void) { + #ifdef _WIN32 + const char *hipPath = getenv("HIP_ROCCLR_HOME"); + const char *windowsCommand = "perl "; + const char *executable = "bin/hipcc"; + + static char hipcc[65536]; + static char finalCommand[65536]; + if(hipPath) { + path_join(hipPath, executable, sizeof(hipcc), hipcc); + if(path_exists(hipcc)) { + snprintf(finalCommand, sizeof(hipcc), "%s %s", windowsCommand, hipcc); + return finalCommand; + } else { + printf("Could not find hipcc. Make sure HIP_ROCCLR_HOME points to the directory holding /bin/hipcc"); + } + } + #else + const char *hipPath = "opt/rocm/hip/bin"; + const char *executable = "hipcc"; + + static char hipcc[65536]; + if(hipPath) { + path_join(hipPath, executable, sizeof(hipcc), hipcc); + if(path_exists(hipcc)){ + return hipcc; + } + } + #endif + + { +#ifdef _WIN32 + FILE *handle = popen("where hipcc", "r"); +#else + FILE *handle = popen("which hipcc", "r"); +#endif + if (handle) { + char buffer[4096] = {0}; + int len = fread(buffer, 1, sizeof(buffer) - 1, handle); + buffer[len] = '\0'; + pclose(handle); + if (buffer[0]) { + return "hipcc"; + } + } + } + + return NULL; +} + +int hipewCompilerVersion(void) { + const char *path = hipewCompilerPath(); + const char *marker = "Hip compilation tools, release "; + FILE *pipe; + int major, minor; + char *versionstr; + char buf[128]; + char output[65536] = "\0"; + char command[65536] = "\0"; + + if (path == NULL) { + return 0; + } + + /* get --version output */ + strcat(command, "\""); + strncat(command, path, sizeof(command) - 1); + strncat(command, "\" --version", sizeof(command) - strlen(path) - 1); + pipe = popen(command, "r"); + if (!pipe) { + fprintf(stderr, "HIP: failed to run compiler to retrieve version"); + return 0; + } + + while (!feof(pipe)) { + if (fgets(buf, sizeof(buf), pipe) != NULL) { + strncat(output, buf, sizeof(output) - strlen(output) - 1); + } + } + + pclose(pipe); + return 40; +} diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index 17096d441f0..2018c1d9648 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -297,6 +297,7 @@ endif() if(WITH_CYCLES_STANDALONE) set(WITH_CYCLES_DEVICE_CUDA TRUE) + set(WITH_CYCLES_DEVICE_HIP TRUE) endif() # TODO(sergey): Consider removing it, only causes confusion in interface. set(WITH_CYCLES_DEVICE_MULTI TRUE) diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index 5bdcfd56a4d..64d226cb9ec 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -95,6 +95,9 @@ set(ADDON_FILES add_definitions(${GL_DEFINITIONS}) +if(WITH_CYCLES_DEVICE_HIP) + add_definitions(-DWITH_HIP) +endif() if(WITH_MOD_FLUID) add_definitions(-DWITH_FLUID) endif() diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index e0e8ca10bef..d729cb1ee69 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -28,7 +28,7 @@ def _configure_argument_parser(): action='store_true') parser.add_argument("--cycles-device", help="Set the device to use for Cycles, overriding user preferences and the scene setting." - "Valid options are 'CPU', 'CUDA' or 'OPTIX'." + "Valid options are 'CPU', 'CUDA', 'OPTIX', or 'HIP'" "Additionally, you can append '+CPU' to any GPU type for hybrid rendering.", default=None) return parser diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index e2b671848d0..67207874431 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -111,6 +111,7 @@ enum_device_type = ( ('CPU', "CPU", "CPU", 0), ('CUDA', "CUDA", "CUDA", 1), ('OPTIX', "OptiX", "OptiX", 3), + ("HIP", "HIP", "HIP", 4) ) enum_texture_limit = ( @@ -1266,12 +1267,16 @@ class CyclesPreferences(bpy.types.AddonPreferences): def get_device_types(self, context): import _cycles - has_cuda, has_optix = _cycles.get_device_types() + has_cuda, has_optix, has_hip = _cycles.get_device_types() + list = [('NONE', "None", "Don't use compute device", 0)] if has_cuda: list.append(('CUDA', "CUDA", "Use CUDA for GPU acceleration", 1)) if has_optix: list.append(('OPTIX', "OptiX", "Use OptiX for GPU acceleration", 3)) + if has_hip: + list.append(('HIP', "HIP", "Use HIP for GPU acceleration", 4)) + return list compute_device_type: EnumProperty( @@ -1296,7 +1301,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): def update_device_entries(self, device_list): for device in device_list: - if not device[1] in {'CUDA', 'OPTIX', 'CPU'}: + if not device[1] in {'CUDA', 'OPTIX', 'CPU', 'HIP'}: continue # Try to find existing Device entry entry = self.find_existing_device_entry(device) @@ -1330,7 +1335,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): elif entry.type == 'CPU': cpu_devices.append(entry) # Extend all GPU devices with CPU. - if compute_device_type != 'CPU': + if compute_device_type != 'CPU' and compute_device_type != 'HIP': devices.extend(cpu_devices) return devices @@ -1340,7 +1345,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): import _cycles # Ensure `self.devices` is not re-allocated when the second call to # get_devices_for_type is made, freeing items from the first list. - for device_type in ('CUDA', 'OPTIX', 'OPENCL'): + for device_type in ('CUDA', 'OPTIX', 'HIP'): self.update_device_entries(_cycles.available_devices(device_type)) # Deprecated: use refresh_devices instead. diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index ac96ddf5e10..c4a1844480c 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -99,6 +99,11 @@ def use_cuda(context): return (get_device_type(context) == 'CUDA' and cscene.device == 'GPU') +def use_hip(context): + cscene = context.scene.cycles + + return (get_device_type(context) == 'HIP' and cscene.device == 'GPU') + def use_optix(context): cscene = context.scene.cycles diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/blender_device.cpp index ce1770f18a3..7bed33855c2 100644 --- a/intern/cycles/blender/blender_device.cpp +++ b/intern/cycles/blender/blender_device.cpp @@ -26,6 +26,7 @@ enum ComputeDevice { COMPUTE_DEVICE_CPU = 0, COMPUTE_DEVICE_CUDA = 1, COMPUTE_DEVICE_OPTIX = 3, + COMPUTE_DEVICE_HIP = 4, COMPUTE_DEVICE_NUM }; @@ -81,6 +82,9 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen else if (compute_device == COMPUTE_DEVICE_OPTIX) { mask |= DEVICE_MASK_OPTIX; } + else if (compute_device == COMPUTE_DEVICE_HIP) { + mask |= DEVICE_MASK_HIP; + } vector devices = Device::available_devices(mask); /* Match device preferences and available devices. */ diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index 694d8454422..d681517c9e1 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -911,14 +911,16 @@ static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args* static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/) { vector device_types = Device::available_types(); - bool has_cuda = false, has_optix = false; + bool has_cuda = false, has_optix = false, has_hip = false; foreach (DeviceType device_type, device_types) { has_cuda |= (device_type == DEVICE_CUDA); has_optix |= (device_type == DEVICE_OPTIX); + has_hip |= (device_type == DEVICE_HIP); } - PyObject *list = PyTuple_New(2); + PyObject *list = PyTuple_New(3); PyTuple_SET_ITEM(list, 0, PyBool_FromLong(has_cuda)); PyTuple_SET_ITEM(list, 1, PyBool_FromLong(has_optix)); + PyTuple_SET_ITEM(list, 2, PyBool_FromLong(has_hip)); return list; } @@ -944,6 +946,9 @@ static PyObject *set_device_override_func(PyObject * /*self*/, PyObject *arg) else if (override == "OPTIX") { BlenderSession::device_override = DEVICE_MASK_OPTIX; } + else if (override == "HIP") { + BlenderSession::device_override = DEVICE_MASK_HIP; + } else { printf("\nError: %s is not a valid Cycles device.\n", override.c_str()); Py_RETURN_FALSE; diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake index da259171844..5653f51ec05 100644 --- a/intern/cycles/cmake/external_libs.cmake +++ b/intern/cycles/cmake/external_libs.cmake @@ -531,5 +531,9 @@ if(WITH_CYCLES_CUDA_BINARIES OR NOT WITH_CUDA_DYNLOAD) endif() endif() endif() +if(NOT WITH_HIP_DYNLOAD) + message(STATUS "Setting up HIP Dynamic Load") + set(WITH_HIP_DYNLOAD ON) +endif() unset(_cycles_lib_dir) diff --git a/intern/cycles/cmake/macros.cmake b/intern/cycles/cmake/macros.cmake index 47196dfd1ce..172ae280cea 100644 --- a/intern/cycles/cmake/macros.cmake +++ b/intern/cycles/cmake/macros.cmake @@ -162,6 +162,10 @@ macro(cycles_target_link_libraries target) target_link_libraries(${target} ${CUDA_CUDA_LIBRARY}) endif() + if(WITH_HIP_DYNLOAD) + target_link_libraries(${target} extern_hipew) + endif() + if(CYCLES_STANDALONE_REPOSITORY) target_link_libraries(${target} extern_numaapi) else() diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt index d18f4360aef..9af68550d17 100644 --- a/intern/cycles/device/CMakeLists.txt +++ b/intern/cycles/device/CMakeLists.txt @@ -34,6 +34,13 @@ else() add_definitions(-DCYCLES_CUDA_NVCC_EXECUTABLE="${CUDA_NVCC_EXECUTABLE}") endif() +if(WITH_HIP_DYNLOAD) + list(APPEND INC + ../../../extern/hipew/include + ) + add_definitions(-DWITH_HIP_DYNLOAD) +endif() + set(SRC device.cpp device_denoise.cpp @@ -70,6 +77,21 @@ set(SRC_CUDA cuda/util.h ) +set(SRC_HIP + hip/device.cpp + hip/device.h + hip/device_impl.cpp + hip/device_impl.h + hip/graphics_interop.cpp + hip/graphics_interop.h + hip/kernel.cpp + hip/kernel.h + hip/queue.cpp + hip/queue.h + hip/util.cpp + hip/util.h +) + set(SRC_DUMMY dummy/device.cpp dummy/device.h @@ -115,11 +137,20 @@ else() ) endif() +if(WITH_HIP_DYNLOAD) + list(APPEND LIB + extern_hipew + ) +endif() + add_definitions(${GL_DEFINITIONS}) if(WITH_CYCLES_DEVICE_CUDA) add_definitions(-DWITH_CUDA) endif() +if(WITH_CYCLES_DEVICE_HIP) + add_definitions(-DWITH_HIP) +endif() if(WITH_CYCLES_DEVICE_OPTIX) add_definitions(-DWITH_OPTIX) endif() @@ -140,6 +171,7 @@ cycles_add_library(cycles_device "${LIB}" ${SRC} ${SRC_CPU} ${SRC_CUDA} + ${SRC_HIP} ${SRC_DUMMY} ${SRC_MULTI} ${SRC_OPTIX} diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index 70935598437..81574e8b184 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -25,6 +25,7 @@ #include "device/cpu/device.h" #include "device/cuda/device.h" #include "device/dummy/device.h" +#include "device/hip/device.h" #include "device/multi/device.h" #include "device/optix/device.h" @@ -46,6 +47,7 @@ thread_mutex Device::device_mutex; vector Device::cuda_devices; vector Device::optix_devices; vector Device::cpu_devices; +vector Device::hip_devices; uint Device::devices_initialized_mask = 0; /* Device */ @@ -96,6 +98,14 @@ Device *Device::create(const DeviceInfo &info, Stats &stats, Profiler &profiler) device = device_optix_create(info, stats, profiler); break; #endif + +#ifdef WITH_HIP + case DEVICE_HIP: + if (device_hip_init()) + device = device_hip_create(info, stats, profiler); + break; +#endif + default: break; } @@ -117,6 +127,8 @@ DeviceType Device::type_from_string(const char *name) return DEVICE_OPTIX; else if (strcmp(name, "MULTI") == 0) return DEVICE_MULTI; + else if (strcmp(name, "HIP") == 0) + return DEVICE_HIP; return DEVICE_NONE; } @@ -131,6 +143,8 @@ string Device::string_from_type(DeviceType type) return "OPTIX"; else if (type == DEVICE_MULTI) return "MULTI"; + else if (type == DEVICE_HIP) + return "HIP"; return ""; } @@ -145,6 +159,10 @@ vector Device::available_types() #ifdef WITH_OPTIX types.push_back(DEVICE_OPTIX); #endif +#ifdef WITH_HIP + types.push_back(DEVICE_HIP); +#endif + return types; } @@ -186,6 +204,20 @@ vector Device::available_devices(uint mask) } #endif +#ifdef WITH_HIP + if (mask & DEVICE_MASK_HIP) { + if (!(devices_initialized_mask & DEVICE_MASK_HIP)) { + if (device_hip_init()) { + device_hip_info(hip_devices); + } + devices_initialized_mask |= DEVICE_MASK_HIP; + } + foreach (DeviceInfo &info, hip_devices) { + devices.push_back(info); + } + } +#endif + if (mask & DEVICE_MASK_CPU) { if (!(devices_initialized_mask & DEVICE_MASK_CPU)) { device_cpu_info(cpu_devices); @@ -226,6 +258,15 @@ string Device::device_capabilities(uint mask) } #endif +#ifdef WITH_HIP + if (mask & DEVICE_MASK_HIP) { + if (device_hip_init()) { + capabilities += "\nHIP device capabilities:\n"; + capabilities += device_hip_capabilities(); + } + } +#endif + return capabilities; } @@ -314,6 +355,7 @@ void Device::free_memory() devices_initialized_mask = 0; cuda_devices.free_memory(); optix_devices.free_memory(); + hip_devices.free_memory(); cpu_devices.free_memory(); } diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index 3bbad179f52..c73d74cdccc 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -51,6 +51,7 @@ enum DeviceType { DEVICE_CUDA, DEVICE_MULTI, DEVICE_OPTIX, + DEVICE_HIP, DEVICE_DUMMY, }; @@ -58,6 +59,7 @@ enum DeviceTypeMask { DEVICE_MASK_CPU = (1 << DEVICE_CPU), DEVICE_MASK_CUDA = (1 << DEVICE_CUDA), DEVICE_MASK_OPTIX = (1 << DEVICE_OPTIX), + DEVICE_MASK_HIP = (1 << DEVICE_HIP), DEVICE_MASK_ALL = ~0 }; @@ -284,6 +286,7 @@ class Device { static vector cuda_devices; static vector optix_devices; static vector cpu_devices; + static vector hip_devices; static uint devices_initialized_mask; }; diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/device_memory.h index a854cc9b693..be6123e09b2 100644 --- a/intern/cycles/device/device_memory.h +++ b/intern/cycles/device/device_memory.h @@ -277,6 +277,7 @@ class device_memory { protected: friend class CUDADevice; friend class OptiXDevice; + friend class HIPDevice; /* Only create through subclasses. */ device_memory(Device *device, const char *name, MemoryType type); diff --git a/intern/cycles/device/hip/device.cpp b/intern/cycles/device/hip/device.cpp new file mode 100644 index 00000000000..90028ac7f10 --- /dev/null +++ b/intern/cycles/device/hip/device.cpp @@ -0,0 +1,276 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/hip/device.h" + +#include "util/util_logging.h" + +#ifdef WITH_HIP +# include "device/device.h" +# include "device/hip/device_impl.h" + +# include "util/util_string.h" +# include "util/util_windows.h" +#endif /* WITH_HIP */ + +CCL_NAMESPACE_BEGIN + +bool device_hip_init() +{ +#if !defined(WITH_HIP) + return false; +#elif defined(WITH_HIP_DYNLOAD) + static bool initialized = false; + static bool result = false; + + if (initialized) + return result; + + initialized = true; + int hipew_result = hipewInit(HIPEW_INIT_HIP); + if (hipew_result == HIPEW_SUCCESS) { + VLOG(1) << "HIPEW initialization succeeded"; + if (HIPDevice::have_precompiled_kernels()) { + VLOG(1) << "Found precompiled kernels"; + result = true; + } + else if (hipewCompilerPath() != NULL) { + VLOG(1) << "Found HIPCC " << hipewCompilerPath(); + result = true; + } + else { + VLOG(1) << "Neither precompiled kernels nor HIPCC was found," + << " unable to use HIP"; + } + } + else { + VLOG(1) << "HIPEW initialization failed: " + << ((hipew_result == HIPEW_ERROR_ATEXIT_FAILED) ? "Error setting up atexit() handler" : + "Error opening the library"); + } + + return result; +#else /* WITH_HIP_DYNLOAD */ + return true; +#endif /* WITH_HIP_DYNLOAD */ +} + +Device *device_hip_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) +{ +#ifdef WITH_HIP + return new HIPDevice(info, stats, profiler); +#else + (void)info; + (void)stats; + (void)profiler; + + LOG(FATAL) << "Request to create HIP device without compiled-in support. Should never happen."; + + return nullptr; +#endif +} + +#ifdef WITH_HIP +static hipError_t device_hip_safe_init() +{ +# ifdef _WIN32 + __try { + return hipInit(0); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + /* Ignore crashes inside the HIP driver and hope we can + * survive even with corrupted HIP installs. */ + fprintf(stderr, "Cycles HIP: driver crashed, continuing without HIP.\n"); + } + + return hipErrorNoDevice; +# else + return hipInit(0); +# endif +} +#endif /* WITH_HIP */ + +void device_hip_info(vector &devices) +{ +#ifdef WITH_HIP + hipError_t result = device_hip_safe_init(); + if (result != hipSuccess) { + if (result != hipErrorNoDevice) + fprintf(stderr, "HIP hipInit: %s\n", hipewErrorString(result)); + return; + } + + int count = 0; + result = hipGetDeviceCount(&count); + if (result != hipSuccess) { + fprintf(stderr, "HIP hipGetDeviceCount: %s\n", hipewErrorString(result)); + return; + } + + vector display_devices; + + for (int num = 0; num < count; num++) { + char name[256]; + + result = hipDeviceGetName(name, 256, num); + if (result != hipSuccess) { + fprintf(stderr, "HIP :hipDeviceGetName: %s\n", hipewErrorString(result)); + continue; + } + + int major; + hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, num); + // TODO : (Arya) What is the last major version we are supporting? + + DeviceInfo info; + + info.type = DEVICE_HIP; + info.description = string(name); + info.num = num; + + info.has_half_images = (major >= 3); + info.has_nanovdb = true; + info.denoisers = 0; + + info.has_gpu_queue = true; + /* Check if the device has P2P access to any other device in the system. */ + for (int peer_num = 0; peer_num < count && !info.has_peer_memory; peer_num++) { + if (num != peer_num) { + int can_access = 0; + hipDeviceCanAccessPeer(&can_access, num, peer_num); + info.has_peer_memory = (can_access != 0); + } + } + + int pci_location[3] = {0, 0, 0}; + hipDeviceGetAttribute(&pci_location[0], hipDeviceAttributePciDomainID, num); + hipDeviceGetAttribute(&pci_location[1], hipDeviceAttributePciBusId, num); + hipDeviceGetAttribute(&pci_location[2], hipDeviceAttributePciDeviceId, num); + info.id = string_printf("HIP_%s_%04x:%02x:%02x", + name, + (unsigned int)pci_location[0], + (unsigned int)pci_location[1], + (unsigned int)pci_location[2]); + + /* If device has a kernel timeout and no compute preemption, we assume + * it is connected to a display and will freeze the display while doing + * computations. */ + int timeout_attr = 0, preempt_attr = 0; + hipDeviceGetAttribute(&timeout_attr, hipDeviceAttributeKernelExecTimeout, num); + + if (timeout_attr && !preempt_attr) { + VLOG(1) << "Device is recognized as display."; + info.description += " (Display)"; + info.display_device = true; + display_devices.push_back(info); + } + else { + VLOG(1) << "Device has compute preemption or is not used for display."; + devices.push_back(info); + } + VLOG(1) << "Added device \"" << name << "\" with id \"" << info.id << "\"."; + } + + if (!display_devices.empty()) + devices.insert(devices.end(), display_devices.begin(), display_devices.end()); +#else /* WITH_HIP */ + (void)devices; +#endif /* WITH_HIP */ +} + +string device_hip_capabilities() +{ +#ifdef WITH_HIP + hipError_t result = device_hip_safe_init(); + if (result != hipSuccess) { + if (result != hipErrorNoDevice) { + return string("Error initializing HIP: ") + hipewErrorString(result); + } + return "No HIP device found\n"; + } + + int count; + result = hipGetDeviceCount(&count); + if (result != hipSuccess) { + return string("Error getting devices: ") + hipewErrorString(result); + } + + string capabilities = ""; + for (int num = 0; num < count; num++) { + char name[256]; + if (hipDeviceGetName(name, 256, num) != hipSuccess) { + continue; + } + capabilities += string("\t") + name + "\n"; + int value; +# define GET_ATTR(attr) \ + { \ + if (hipDeviceGetAttribute(&value, hipDeviceAttribute##attr, num) == hipSuccess) { \ + capabilities += string_printf("\t\thipDeviceAttribute" #attr "\t\t\t%d\n", value); \ + } \ + } \ + (void)0 + /* TODO(sergey): Strip all attributes which are not useful for us + * or does not depend on the driver. + */ + GET_ATTR(MaxThreadsPerBlock); + GET_ATTR(MaxBlockDimX); + GET_ATTR(MaxBlockDimY); + GET_ATTR(MaxBlockDimZ); + GET_ATTR(MaxGridDimX); + GET_ATTR(MaxGridDimY); + GET_ATTR(MaxGridDimZ); + GET_ATTR(MaxSharedMemoryPerBlock); + GET_ATTR(TotalConstantMemory); + GET_ATTR(WarpSize); + GET_ATTR(MaxPitch); + GET_ATTR(MaxRegistersPerBlock); + GET_ATTR(ClockRate); + GET_ATTR(TextureAlignment); + GET_ATTR(MultiprocessorCount); + GET_ATTR(KernelExecTimeout); + GET_ATTR(Integrated); + GET_ATTR(CanMapHostMemory); + GET_ATTR(ComputeMode); + GET_ATTR(MaxTexture1DWidth); + GET_ATTR(MaxTexture2DWidth); + GET_ATTR(MaxTexture2DHeight); + GET_ATTR(MaxTexture3DWidth); + GET_ATTR(MaxTexture3DHeight); + GET_ATTR(MaxTexture3DDepth); + GET_ATTR(ConcurrentKernels); + GET_ATTR(EccEnabled); + GET_ATTR(MemoryClockRate); + GET_ATTR(MemoryBusWidth); + GET_ATTR(L2CacheSize); + GET_ATTR(MaxThreadsPerMultiProcessor); + GET_ATTR(ComputeCapabilityMajor); + GET_ATTR(ComputeCapabilityMinor); + GET_ATTR(MaxSharedMemoryPerMultiprocessor); + GET_ATTR(ManagedMemory); + GET_ATTR(IsMultiGpuBoard); +# undef GET_ATTR + capabilities += "\n"; + } + + return capabilities; + +#else /* WITH_HIP */ + return ""; +#endif /* WITH_HIP */ +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/hip/device.h b/intern/cycles/device/hip/device.h new file mode 100644 index 00000000000..76fa8995bed --- /dev/null +++ b/intern/cycles/device/hip/device.h @@ -0,0 +1,37 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/util_string.h" +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +bool device_hip_init(); + +Device *device_hip_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +void device_hip_info(vector &devices); + +string device_hip_capabilities(); + +CCL_NAMESPACE_END \ No newline at end of file diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp new file mode 100644 index 00000000000..0e5ac6ce401 --- /dev/null +++ b/intern/cycles/device/hip/device_impl.cpp @@ -0,0 +1,1343 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include +# include +# include +# include +# include + +# include "device/hip/device_impl.h" + +# include "render/buffers.h" + +# include "util/util_debug.h" +# include "util/util_foreach.h" +# include "util/util_logging.h" +# include "util/util_map.h" +# include "util/util_md5.h" +# include "util/util_opengl.h" +# include "util/util_path.h" +# include "util/util_string.h" +# include "util/util_system.h" +# include "util/util_time.h" +# include "util/util_types.h" +# include "util/util_windows.h" + +CCL_NAMESPACE_BEGIN + +class HIPDevice; + +bool HIPDevice::have_precompiled_kernels() +{ + string fatbins_path = path_get("lib"); + return path_exists(fatbins_path); +} + +bool HIPDevice::show_samples() const +{ + /* The HIPDevice only processes one tile at a time, so showing samples is fine. */ + return true; +} + +BVHLayoutMask HIPDevice::get_bvh_layout_mask() const +{ + return BVH_LAYOUT_BVH2; +} + +void HIPDevice::set_error(const string &error) +{ + Device::set_error(error); + + if (first_error) { + fprintf(stderr, "\nRefer to the Cycles GPU rendering documentation for possible solutions:\n"); + fprintf(stderr, + "https://docs.blender.org/manual/en/latest/render/cycles/gpu_rendering.html\n\n"); + first_error = false; + } +} + +HIPDevice::HIPDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler) + : Device(info, stats, profiler), texture_info(this, "__texture_info", MEM_GLOBAL) +{ + first_error = true; + + hipDevId = info.num; + hipDevice = 0; + hipContext = 0; + + hipModule = 0; + + need_texture_info = false; + + device_texture_headroom = 0; + device_working_headroom = 0; + move_texture_to_host = false; + map_host_limit = 0; + map_host_used = 0; + can_map_host = 0; + pitch_alignment = 0; + + /* Initialize HIP. */ + hipError_t result = hipInit(0); + if (result != hipSuccess) { + set_error(string_printf("Failed to initialize HIP runtime (%s)", hipewErrorString(result))); + return; + } + + /* Setup device and context. */ + result = hipGetDevice(&hipDevice, hipDevId); + if (result != hipSuccess) { + set_error(string_printf("Failed to get HIP device handle from ordinal (%s)", + hipewErrorString(result))); + return; + } + + hip_assert(hipDeviceGetAttribute(&can_map_host, hipDeviceAttributeCanMapHostMemory, hipDevice)); + + hip_assert( + hipDeviceGetAttribute(&pitch_alignment, hipDeviceAttributeTexturePitchAlignment, hipDevice)); + + unsigned int ctx_flags = hipDeviceLmemResizeToMax; + if (can_map_host) { + ctx_flags |= hipDeviceMapHost; + init_host_memory(); + } + + /* Create context. */ + result = hipCtxCreate(&hipContext, ctx_flags, hipDevice); + + if (result != hipSuccess) { + set_error(string_printf("Failed to create HIP context (%s)", hipewErrorString(result))); + return; + } + + int major, minor; + hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId); + hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId); + hipDevArchitecture = major * 100 + minor * 10; + + /* Pop context set by hipCtxCreate. */ + hipCtxPopCurrent(NULL); +} + +HIPDevice::~HIPDevice() +{ + texture_info.free(); + + hip_assert(hipCtxDestroy(hipContext)); +} + +bool HIPDevice::support_device(const uint /*kernel_features*/) +{ + int major, minor; + hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId); + hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId); + + // TODO : (Arya) What versions do we plan to support? + return true; +} + +bool HIPDevice::check_peer_access(Device *peer_device) +{ + if (peer_device == this) { + return false; + } + if (peer_device->info.type != DEVICE_HIP && peer_device->info.type != DEVICE_OPTIX) { + return false; + } + + HIPDevice *const peer_device_hip = static_cast(peer_device); + + int can_access = 0; + hip_assert(hipDeviceCanAccessPeer(&can_access, hipDevice, peer_device_hip->hipDevice)); + if (can_access == 0) { + return false; + } + + // Ensure array access over the link is possible as well (for 3D textures) + hip_assert(hipDeviceGetP2PAttribute( + &can_access, hipDevP2PAttrHipArrayAccessSupported, hipDevice, peer_device_hip->hipDevice)); + if (can_access == 0) { + return false; + } + + // Enable peer access in both directions + { + const HIPContextScope scope(this); + hipError_t result = hipCtxEnablePeerAccess(peer_device_hip->hipContext, 0); + if (result != hipSuccess) { + set_error(string_printf("Failed to enable peer access on HIP context (%s)", + hipewErrorString(result))); + return false; + } + } + { + const HIPContextScope scope(peer_device_hip); + hipError_t result = hipCtxEnablePeerAccess(hipContext, 0); + if (result != hipSuccess) { + set_error(string_printf("Failed to enable peer access on HIP context (%s)", + hipewErrorString(result))); + return false; + } + } + + return true; +} + +bool HIPDevice::use_adaptive_compilation() +{ + return DebugFlags().hip.adaptive_compile; +} + +/* Common NVCC flags which stays the same regardless of shading model, + * kernel sources md5 and only depends on compiler or compilation settings. + */ +string HIPDevice::compile_kernel_get_common_cflags(const uint kernel_features) +{ + const int machine = system_cpu_bits(); + const string source_path = path_get("source"); + const string include_path = source_path; + string cflags = string_printf( + "-m%d " + "--ptxas-options=\"-v\" " + "--use_fast_math " + "-DHIPCC " + "-I\"%s\"", + machine, + include_path.c_str()); + if (use_adaptive_compilation()) { + cflags += " -D__KERNEL_FEATURES__=" + to_string(kernel_features); + } + return cflags; +} + +string HIPDevice::compile_kernel(const uint kernel_features, + const char *name, + const char *base, + bool force_ptx) +{ + /* Compute kernel name. */ + int major, minor; + hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId); + hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId); + + /* Attempt to use kernel provided with Blender. */ + if (!use_adaptive_compilation()) { + if (!force_ptx) { + const string fatbin = path_get(string_printf("lib/%s_sm_%d%d.cubin", name, major, minor)); + VLOG(1) << "Testing for pre-compiled kernel " << fatbin << "."; + if (path_exists(fatbin)) { + VLOG(1) << "Using precompiled kernel."; + return fatbin; + } + } + + /* The driver can JIT-compile PTX generated for older generations, so find the closest one. */ + int ptx_major = major, ptx_minor = minor; + while (ptx_major >= 3) { + const string ptx = path_get( + string_printf("lib/%s_compute_%d%d.ptx", name, ptx_major, ptx_minor)); + VLOG(1) << "Testing for pre-compiled kernel " << ptx << "."; + if (path_exists(ptx)) { + VLOG(1) << "Using precompiled kernel."; + return ptx; + } + + if (ptx_minor > 0) { + ptx_minor--; + } + else { + ptx_major--; + ptx_minor = 9; + } + } + } + + /* Try to use locally compiled kernel. */ + string source_path = path_get("source"); + const string source_md5 = path_files_md5_hash(source_path); + + /* We include cflags into md5 so changing hip toolkit or changing other + * compiler command line arguments makes sure fatbin gets re-built. + */ + string common_cflags = compile_kernel_get_common_cflags(kernel_features); + const string kernel_md5 = util_md5_string(source_md5 + common_cflags); + + const char *const kernel_ext = "genco"; +# ifdef _WIN32 + const char *const options = + "save-temps -Wno-parentheses-equality -Wno-unused-value --hipcc-func-supp"; +# else + const char *const options = + "save-temps -Wno-parentheses-equality -Wno-unused-value --hipcc-func-supp -O3 -ggdb"; +# endif + const string include_path = source_path; + const char *const kernel_arch = force_ptx ? "compute" : "sm"; + const string fatbin_file = string_printf( + "cycles_%s_%s_%d%d_%s", name, kernel_arch, major, minor, kernel_md5.c_str()); + const string fatbin = path_cache_get(path_join("kernels", fatbin_file)); + VLOG(1) << "Testing for locally compiled kernel " << fatbin << "."; + if (path_exists(fatbin)) { + VLOG(1) << "Using locally compiled kernel."; + return fatbin; + } + +# ifdef _WIN32 + if (!use_adaptive_compilation() && have_precompiled_kernels()) { + if (major < 3) { + set_error( + string_printf("HIP backend requires compute capability 3.0 or up, but found %d.%d. " + "Your GPU is not supported.", + major, + minor)); + } + else { + set_error( + string_printf("HIP binary kernel for this graphics card compute " + "capability (%d.%d) not found.", + major, + minor)); + } + return string(); + } +# endif + + /* Compile. */ + const char *const hipcc = hipewCompilerPath(); + if (hipcc == NULL) { + set_error( + "HIP hipcc compiler not found. " + "Install HIP toolkit in default location."); + return string(); + } + + const int hipcc_hip_version = hipewCompilerVersion(); + VLOG(1) << "Found hipcc " << hipcc << ", HIP version " << hipcc_hip_version << "."; + if (hipcc_hip_version < 40) { + printf( + "Unsupported HIP version %d.%d detected, " + "you need HIP 4.0 or newer.\n", + hipcc_hip_version / 10, + hipcc_hip_version % 10); + return string(); + } + + double starttime = time_dt(); + + path_create_directories(fatbin); + + source_path = path_join(path_join(source_path, "kernel"), + path_join("device", path_join(base, string_printf("%s.cpp", name)))); + + string command = string_printf("%s -%s -I %s --%s %s -o \"%s\"", + hipcc, + options, + include_path.c_str(), + kernel_ext, + source_path.c_str(), + fatbin.c_str()); + + printf("Compiling HIP kernel ...\n%s\n", command.c_str()); + +# ifdef _WIN32 + command = "call " + command; +# endif + if (system(command.c_str()) != 0) { + set_error( + "Failed to execute compilation command, " + "see console for details."); + return string(); + } + + /* Verify if compilation succeeded */ + if (!path_exists(fatbin)) { + set_error( + "HIP kernel compilation failed, " + "see console for details."); + return string(); + } + + printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime); + + return fatbin; +} + +bool HIPDevice::load_kernels(const uint kernel_features) +{ + /* TODO(sergey): Support kernels re-load for HIP devices. + * + * Currently re-loading kernel will invalidate memory pointers, + * causing problems in hipCtxSynchronize. + */ + if (hipModule) { + VLOG(1) << "Skipping kernel reload, not currently supported."; + return true; + } + + /* check if hip init succeeded */ + if (hipContext == 0) + return false; + + /* check if GPU is supported */ + if (!support_device(kernel_features)) + return false; + + /* get kernel */ + const char *kernel_name = "kernel"; + string fatbin = compile_kernel(kernel_features, kernel_name); + if (fatbin.empty()) + return false; + + /* open module */ + HIPContextScope scope(this); + + string fatbin_data; + hipError_t result; + + if (path_read_text(fatbin, fatbin_data)) + result = hipModuleLoadData(&hipModule, fatbin_data.c_str()); + else + result = hipErrorFileNotFound; + + if (result != hipSuccess) + set_error(string_printf( + "Failed to load HIP kernel from '%s' (%s)", fatbin.c_str(), hipewErrorString(result))); + + if (result == hipSuccess) { + kernels.load(this); + reserve_local_memory(kernel_features); + } + + return (result == hipSuccess); +} + +void HIPDevice::reserve_local_memory(const uint) +{ + /* Together with hipDeviceLmemResizeToMax, this reserves local memory + * needed for kernel launches, so that we can reliably figure out when + * to allocate scene data in mapped host memory. */ + size_t total = 0, free_before = 0, free_after = 0; + + { + HIPContextScope scope(this); + hipMemGetInfo(&free_before, &total); + } + + { + /* Use the biggest kernel for estimation. */ + const DeviceKernel test_kernel = DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE; + + /* Launch kernel, using just 1 block appears sufficient to reserve memory for all + * multiprocessors. It would be good to do this in parallel for the multi GPU case + * still to make it faster. */ + HIPDeviceQueue queue(this); + + void *d_path_index = nullptr; + void *d_render_buffer = nullptr; + int d_work_size = 0; + void *args[] = {&d_path_index, &d_render_buffer, &d_work_size}; + + queue.init_execution(); + queue.enqueue(test_kernel, 1, args); + queue.synchronize(); + } + + { + HIPContextScope scope(this); + hipMemGetInfo(&free_after, &total); + } + + VLOG(1) << "Local memory reserved " << string_human_readable_number(free_before - free_after) + << " bytes. (" << string_human_readable_size(free_before - free_after) << ")"; + +# if 0 + /* For testing mapped host memory, fill up device memory. */ + const size_t keep_mb = 1024; + + while (free_after > keep_mb * 1024 * 1024LL) { + hipDeviceptr_t tmp; + hip_assert(hipMalloc(&tmp, 10 * 1024 * 1024LL)); + hipMemGetInfo(&free_after, &total); + } +# endif +} + +void HIPDevice::init_host_memory() +{ + /* Limit amount of host mapped memory, because allocating too much can + * cause system instability. Leave at least half or 4 GB of system + * memory free, whichever is smaller. */ + size_t default_limit = 4 * 1024 * 1024 * 1024LL; + size_t system_ram = system_physical_ram(); + + if (system_ram > 0) { + if (system_ram / 2 > default_limit) { + map_host_limit = system_ram - default_limit; + } + else { + map_host_limit = system_ram / 2; + } + } + else { + VLOG(1) << "Mapped host memory disabled, failed to get system RAM"; + map_host_limit = 0; + } + + /* Amount of device memory to keep is free after texture memory + * and working memory allocations respectively. We set the working + * memory limit headroom lower so that some space is left after all + * texture memory allocations. */ + device_working_headroom = 32 * 1024 * 1024LL; // 32MB + device_texture_headroom = 128 * 1024 * 1024LL; // 128MB + + VLOG(1) << "Mapped host memory limit set to " << string_human_readable_number(map_host_limit) + << " bytes. (" << string_human_readable_size(map_host_limit) << ")"; +} + +void HIPDevice::load_texture_info() +{ + if (need_texture_info) { + /* Unset flag before copying, so this does not loop indefinitely if the copy below calls + * into 'move_textures_to_host' (which calls 'load_texture_info' again). */ + need_texture_info = false; + texture_info.copy_to_device(); + } +} + +void HIPDevice::move_textures_to_host(size_t size, bool for_texture) +{ + /* Break out of recursive call, which can happen when moving memory on a multi device. */ + static bool any_device_moving_textures_to_host = false; + if (any_device_moving_textures_to_host) { + return; + } + + /* Signal to reallocate textures in host memory only. */ + move_texture_to_host = true; + + while (size > 0) { + /* Find suitable memory allocation to move. */ + device_memory *max_mem = NULL; + size_t max_size = 0; + bool max_is_image = false; + + thread_scoped_lock lock(hip_mem_map_mutex); + foreach (HIPMemMap::value_type &pair, hip_mem_map) { + device_memory &mem = *pair.first; + HIPMem *cmem = &pair.second; + + /* Can only move textures allocated on this device (and not those from peer devices). + * And need to ignore memory that is already on the host. */ + if (!mem.is_resident(this) || cmem->use_mapped_host) { + continue; + } + + bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && + (&mem != &texture_info); + bool is_image = is_texture && (mem.data_height > 1); + + /* Can't move this type of memory. */ + if (!is_texture || cmem->array) { + continue; + } + + /* For other textures, only move image textures. */ + if (for_texture && !is_image) { + continue; + } + + /* Try to move largest allocation, prefer moving images. */ + if (is_image > max_is_image || (is_image == max_is_image && mem.device_size > max_size)) { + max_is_image = is_image; + max_size = mem.device_size; + max_mem = &mem; + } + } + lock.unlock(); + + /* Move to host memory. This part is mutex protected since + * multiple HIP devices could be moving the memory. The + * first one will do it, and the rest will adopt the pointer. */ + if (max_mem) { + VLOG(1) << "Move memory from device to host: " << max_mem->name; + + static thread_mutex move_mutex; + thread_scoped_lock lock(move_mutex); + + any_device_moving_textures_to_host = true; + + /* Potentially need to call back into multi device, so pointer mapping + * and peer devices are updated. This is also necessary since the device + * pointer may just be a key here, so cannot be accessed and freed directly. + * Unfortunately it does mean that memory is reallocated on all other + * devices as well, which is potentially dangerous when still in use (since + * a thread rendering on another devices would only be caught in this mutex + * if it so happens to do an allocation at the same time as well. */ + max_mem->device_copy_to(); + size = (max_size >= size) ? 0 : size - max_size; + + any_device_moving_textures_to_host = false; + } + else { + break; + } + } + + /* Unset flag before texture info is reloaded, since it should stay in device memory. */ + move_texture_to_host = false; + + /* Update texture info array with new pointers. */ + load_texture_info(); +} + +HIPDevice::HIPMem *HIPDevice::generic_alloc(device_memory &mem, size_t pitch_padding) +{ + HIPContextScope scope(this); + + hipDeviceptr_t device_pointer = 0; + size_t size = mem.memory_size() + pitch_padding; + + hipError_t mem_alloc_result = hipErrorOutOfMemory; + const char *status = ""; + + /* First try allocating in device memory, respecting headroom. We make + * an exception for texture info. It is small and frequently accessed, + * so treat it as working memory. + * + * If there is not enough room for working memory, we will try to move + * textures to host memory, assuming the performance impact would have + * been worse for working memory. */ + bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && (&mem != &texture_info); + bool is_image = is_texture && (mem.data_height > 1); + + size_t headroom = (is_texture) ? device_texture_headroom : device_working_headroom; + + size_t total = 0, free = 0; + hipMemGetInfo(&free, &total); + + /* Move textures to host memory if needed. */ + if (!move_texture_to_host && !is_image && (size + headroom) >= free && can_map_host) { + move_textures_to_host(size + headroom - free, is_texture); + hipMemGetInfo(&free, &total); + } + + /* Allocate in device memory. */ + if (!move_texture_to_host && (size + headroom) < free) { + mem_alloc_result = hipMalloc(&device_pointer, size); + if (mem_alloc_result == hipSuccess) { + status = " in device memory"; + } + } + + /* Fall back to mapped host memory if needed and possible. */ + + void *shared_pointer = 0; + + if (mem_alloc_result != hipSuccess && can_map_host) { + if (mem.shared_pointer) { + /* Another device already allocated host memory. */ + mem_alloc_result = hipSuccess; + shared_pointer = mem.shared_pointer; + } + else if (map_host_used + size < map_host_limit) { + /* Allocate host memory ourselves. */ + mem_alloc_result = hipHostMalloc(&shared_pointer, size); + + assert((mem_alloc_result == hipSuccess && shared_pointer != 0) || + (mem_alloc_result != hipSuccess && shared_pointer == 0)); + } + + if (mem_alloc_result == hipSuccess) { + hip_assert(hipHostGetDevicePointer(&device_pointer, shared_pointer, 0)); + map_host_used += size; + status = " in host memory"; + } + } + + if (mem_alloc_result != hipSuccess) { + status = " failed, out of device and host memory"; + set_error("System is out of GPU and shared host memory"); + } + + if (mem.name) { + VLOG(1) << "Buffer allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")" << status; + } + + mem.device_pointer = (device_ptr)device_pointer; + mem.device_size = size; + stats.mem_alloc(size); + + if (!mem.device_pointer) { + return NULL; + } + + /* Insert into map of allocations. */ + thread_scoped_lock lock(hip_mem_map_mutex); + HIPMem *cmem = &hip_mem_map[&mem]; + if (shared_pointer != 0) { + /* Replace host pointer with our host allocation. Only works if + * HIP memory layout is the same and has no pitch padding. Also + * does not work if we move textures to host during a render, + * since other devices might be using the memory. */ + + if (!move_texture_to_host && pitch_padding == 0 && mem.host_pointer && + mem.host_pointer != shared_pointer) { + memcpy(shared_pointer, mem.host_pointer, size); + + /* A Call to device_memory::host_free() should be preceded by + * a call to device_memory::device_free() for host memory + * allocated by a device to be handled properly. Two exceptions + * are here and a call in OptiXDevice::generic_alloc(), where + * the current host memory can be assumed to be allocated by + * device_memory::host_alloc(), not by a device */ + + mem.host_free(); + mem.host_pointer = shared_pointer; + } + mem.shared_pointer = shared_pointer; + mem.shared_counter++; + cmem->use_mapped_host = true; + } + else { + cmem->use_mapped_host = false; + } + + return cmem; +} + +void HIPDevice::generic_copy_to(device_memory &mem) +{ + if (!mem.host_pointer || !mem.device_pointer) { + return; + } + + /* If use_mapped_host of mem is false, the current device only uses device memory allocated by + * hipMalloc regardless of mem.host_pointer and mem.shared_pointer, and should copy data from + * mem.host_pointer. */ + thread_scoped_lock lock(hip_mem_map_mutex); + if (!hip_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { + const HIPContextScope scope(this); + hip_assert( + hipMemcpyHtoD((hipDeviceptr_t)mem.device_pointer, mem.host_pointer, mem.memory_size())); + } +} + +void HIPDevice::generic_free(device_memory &mem) +{ + if (mem.device_pointer) { + HIPContextScope scope(this); + thread_scoped_lock lock(hip_mem_map_mutex); + const HIPMem &cmem = hip_mem_map[&mem]; + + /* If cmem.use_mapped_host is true, reference counting is used + * to safely free a mapped host memory. */ + + if (cmem.use_mapped_host) { + assert(mem.shared_pointer); + if (mem.shared_pointer) { + assert(mem.shared_counter > 0); + if (--mem.shared_counter == 0) { + if (mem.host_pointer == mem.shared_pointer) { + mem.host_pointer = 0; + } + hipHostFree(mem.shared_pointer); + mem.shared_pointer = 0; + } + } + map_host_used -= mem.device_size; + } + else { + /* Free device memory. */ + hip_assert(hipFree(mem.device_pointer)); + } + + stats.mem_free(mem.device_size); + mem.device_pointer = 0; + mem.device_size = 0; + + hip_mem_map.erase(hip_mem_map.find(&mem)); + } +} + +void HIPDevice::mem_alloc(device_memory &mem) +{ + if (mem.type == MEM_TEXTURE) { + assert(!"mem_alloc not supported for textures."); + } + else if (mem.type == MEM_GLOBAL) { + assert(!"mem_alloc not supported for global memory."); + } + else { + generic_alloc(mem); + } +} + +void HIPDevice::mem_copy_to(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + global_alloc(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + tex_alloc((device_texture &)mem); + } + else { + if (!mem.device_pointer) { + generic_alloc(mem); + } + generic_copy_to(mem); + } +} + +void HIPDevice::mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) +{ + if (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) { + assert(!"mem_copy_from not supported for textures."); + } + else if (mem.host_pointer) { + const size_t size = elem * w * h; + const size_t offset = elem * y * w; + + if (mem.device_pointer) { + const HIPContextScope scope(this); + hip_assert(hipMemcpyDtoH( + (char *)mem.host_pointer + offset, (hipDeviceptr_t)mem.device_pointer + offset, size)); + } + else { + memset((char *)mem.host_pointer + offset, 0, size); + } + } +} + +void HIPDevice::mem_zero(device_memory &mem) +{ + if (!mem.device_pointer) { + mem_alloc(mem); + } + if (!mem.device_pointer) { + return; + } + + /* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory + * regardless of mem.host_pointer and mem.shared_pointer. */ + thread_scoped_lock lock(hip_mem_map_mutex); + if (!hip_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { + const HIPContextScope scope(this); + hip_assert(hipMemsetD8((hipDeviceptr_t)mem.device_pointer, 0, mem.memory_size())); + } + else if (mem.host_pointer) { + memset(mem.host_pointer, 0, mem.memory_size()); + } +} + +void HIPDevice::mem_free(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + } + else { + generic_free(mem); + } +} + +device_ptr HIPDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) +{ + return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); +} + +void HIPDevice::const_copy_to(const char *name, void *host, size_t size) +{ + HIPContextScope scope(this); + hipDeviceptr_t mem; + size_t bytes; + + hip_assert(hipModuleGetGlobal(&mem, &bytes, hipModule, name)); + assert(bytes == size); + hip_assert(hipMemcpyHtoD(mem, host, size)); +} + +void HIPDevice::global_alloc(device_memory &mem) +{ + if (mem.is_resident(this)) { + generic_alloc(mem); + generic_copy_to(mem); + } + + const_copy_to(mem.name, &mem.device_pointer, sizeof(mem.device_pointer)); +} + +void HIPDevice::global_free(device_memory &mem) +{ + if (mem.is_resident(this) && mem.device_pointer) { + generic_free(mem); + } +} + +void HIPDevice::tex_alloc(device_texture &mem) +{ + HIPContextScope scope(this); + + /* General variables for both architectures */ + string bind_name = mem.name; + size_t dsize = datatype_size(mem.data_type); + size_t size = mem.memory_size(); + + hipTextureAddressMode address_mode = hipAddressModeWrap; + switch (mem.info.extension) { + case EXTENSION_REPEAT: + address_mode = hipAddressModeWrap; + break; + case EXTENSION_EXTEND: + 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; + break; + default: + assert(0); + break; + } + + hipTextureFilterMode filter_mode; + if (mem.info.interpolation == INTERPOLATION_CLOSEST) { + filter_mode = hipFilterModePoint; + } + else { + filter_mode = hipFilterModeLinear; + } + + /* Image Texture Storage */ + hipArray_Format format; + switch (mem.data_type) { + case TYPE_UCHAR: + format = HIP_AD_FORMAT_UNSIGNED_INT8; + break; + case TYPE_UINT16: + format = HIP_AD_FORMAT_UNSIGNED_INT16; + break; + case TYPE_UINT: + format = HIP_AD_FORMAT_UNSIGNED_INT32; + break; + case TYPE_INT: + format = HIP_AD_FORMAT_SIGNED_INT32; + break; + case TYPE_FLOAT: + format = HIP_AD_FORMAT_FLOAT; + break; + case TYPE_HALF: + format = HIP_AD_FORMAT_HALF; + break; + default: + assert(0); + return; + } + + HIPMem *cmem = NULL; + hArray array_3d = NULL; + size_t src_pitch = mem.data_width * dsize * mem.data_elements; + size_t dst_pitch = src_pitch; + + if (!mem.is_resident(this)) { + thread_scoped_lock lock(hip_mem_map_mutex); + cmem = &hip_mem_map[&mem]; + cmem->texobject = 0; + + if (mem.data_depth > 1) { + array_3d = (hArray)mem.device_pointer; + cmem->array = array_3d; + } + else if (mem.data_height > 0) { + dst_pitch = align_up(src_pitch, pitch_alignment); + } + } + else if (mem.data_depth > 1) { + /* 3D texture using array, there is no API for linear memory. */ + HIP_ARRAY3D_DESCRIPTOR desc; + + desc.Width = mem.data_width; + desc.Height = mem.data_height; + desc.Depth = mem.data_depth; + desc.Format = format; + desc.NumChannels = mem.data_elements; + desc.Flags = 0; + + VLOG(1) << "Array 3D allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; + + hip_assert(hipArray3DCreate(&array_3d, &desc)); + + if (!array_3d) { + return; + } + + HIP_MEMCPY3D param; + memset(¶m, 0, sizeof(param)); + param.dstMemoryType = hipMemoryTypeArray; + param.dstArray = &array_3d; + param.srcMemoryType = hipMemoryTypeHost; + param.srcHost = mem.host_pointer; + param.srcPitch = src_pitch; + param.WidthInBytes = param.srcPitch; + param.Height = mem.data_height; + param.Depth = mem.data_depth; + + hip_assert(hipDrvMemcpy3D(¶m)); + + mem.device_pointer = (device_ptr)array_3d; + mem.device_size = size; + stats.mem_alloc(size); + + thread_scoped_lock lock(hip_mem_map_mutex); + cmem = &hip_mem_map[&mem]; + cmem->texobject = 0; + cmem->array = array_3d; + } + else if (mem.data_height > 0) { + /* 2D texture, using pitch aligned linear memory. */ + dst_pitch = align_up(src_pitch, pitch_alignment); + size_t dst_size = dst_pitch * mem.data_height; + + cmem = generic_alloc(mem, dst_size - mem.memory_size()); + if (!cmem) { + return; + } + + hip_Memcpy2D param; + memset(¶m, 0, sizeof(param)); + param.dstMemoryType = hipMemoryTypeDevice; + param.dstDevice = mem.device_pointer; + param.dstPitch = dst_pitch; + param.srcMemoryType = hipMemoryTypeHost; + param.srcHost = mem.host_pointer; + param.srcPitch = src_pitch; + param.WidthInBytes = param.srcPitch; + param.Height = mem.data_height; + + hip_assert(hipDrvMemcpy2DUnaligned(¶m)); + } + else { + /* 1D texture, using linear memory. */ + cmem = generic_alloc(mem); + if (!cmem) { + return; + } + + hip_assert(hipMemcpyHtoD(mem.device_pointer, mem.host_pointer, size)); + } + + /* Resize once */ + const uint slot = mem.slot; + if (slot >= texture_info.size()) { + /* Allocate some slots in advance, to reduce amount + * of re-allocations. */ + texture_info.resize(slot + 128); + } + + /* Set Mapping and tag that we need to (re-)upload to device */ + texture_info[slot] = mem.info; + need_texture_info = true; + + if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && + mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { + /* Kepler+, bindless textures. */ + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + + if (array_3d) { + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.h_Array = &array_3d; + resDesc.flags = 0; + } + else if (mem.data_height > 0) { + resDesc.resType = hipResourceTypePitch2D; + resDesc.res.pitch2D.devPtr = mem.device_pointer; + resDesc.res.pitch2D.format = format; + resDesc.res.pitch2D.numChannels = mem.data_elements; + resDesc.res.pitch2D.height = mem.data_height; + resDesc.res.pitch2D.width = mem.data_width; + resDesc.res.pitch2D.pitchInBytes = dst_pitch; + } + else { + resDesc.resType = hipResourceTypeLinear; + resDesc.res.linear.devPtr = mem.device_pointer; + resDesc.res.linear.format = format; + resDesc.res.linear.numChannels = mem.data_elements; + resDesc.res.linear.sizeInBytes = mem.device_size; + } + + hipTextureDesc texDesc; + memset(&texDesc, 0, sizeof(texDesc)); + texDesc.addressMode[0] = address_mode; + texDesc.addressMode[1] = address_mode; + texDesc.addressMode[2] = address_mode; + texDesc.filterMode = filter_mode; + texDesc.flags = HIP_TRSF_NORMALIZED_COORDINATES; + + thread_scoped_lock lock(hip_mem_map_mutex); + cmem = &hip_mem_map[&mem]; + + hip_assert(hipTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL)); + + texture_info[slot].data = (uint64_t)cmem->texobject; + } + else { + texture_info[slot].data = (uint64_t)mem.device_pointer; + } +} + +void HIPDevice::tex_free(device_texture &mem) +{ + if (mem.device_pointer) { + HIPContextScope scope(this); + thread_scoped_lock lock(hip_mem_map_mutex); + const HIPMem &cmem = hip_mem_map[&mem]; + + if (cmem.texobject) { + /* Free bindless texture. */ + hipTexObjectDestroy(cmem.texobject); + } + + if (!mem.is_resident(this)) { + /* Do not free memory here, since it was allocated on a different device. */ + hip_mem_map.erase(hip_mem_map.find(&mem)); + } + else if (cmem.array) { + /* Free array. */ + hipArrayDestroy(cmem.array); + stats.mem_free(mem.device_size); + mem.device_pointer = 0; + mem.device_size = 0; + + hip_mem_map.erase(hip_mem_map.find(&mem)); + } + else { + lock.unlock(); + generic_free(mem); + } + } +} + +# if 0 +void HIPDevice::render(DeviceTask &task, + RenderTile &rtile, + device_vector &work_tiles) +{ + scoped_timer timer(&rtile.buffers->render_time); + + if (have_error()) + return; + + HIPContextScope scope(this); + hipFunction_t hipRender; + + /* Get kernel function. */ + if (rtile.task == RenderTile::BAKE) { + hip_assert(hipModuleGetFunction(&hipRender, hipModule, "kernel_hip_bake")); + } + else { + hip_assert(hipModuleGetFunction(&hipRender, hipModule, "kernel_hip_path_trace")); + } + + if (have_error()) { + return; + } + + hip_assert(hipFuncSetCacheConfig(hipRender, hipFuncCachePreferL1)); + + /* Allocate work tile. */ + work_tiles.alloc(1); + + KernelWorkTile *wtile = work_tiles.data(); + wtile->x = rtile.x; + wtile->y = rtile.y; + wtile->w = rtile.w; + wtile->h = rtile.h; + wtile->offset = rtile.offset; + wtile->stride = rtile.stride; + wtile->buffer = (float *)(hipDeviceptr_t)rtile.buffer; + + /* Prepare work size. More step samples render faster, but for now we + * remain conservative for GPUs connected to a display to avoid driver + * timeouts and display freezing. */ + int min_blocks, num_threads_per_block; + hip_assert( + hipModuleOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, hipRender, NULL, 0, 0)); + if (!info.display_device) { + min_blocks *= 8; + } + + uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h); + + /* Render all samples. */ + uint start_sample = rtile.start_sample; + uint end_sample = rtile.start_sample + rtile.num_samples; + + for (int sample = start_sample; sample < end_sample;) { + /* Setup and copy work tile to device. */ + wtile->start_sample = sample; + wtile->num_samples = step_samples; + if (task.adaptive_sampling.use) { + wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples); + } + wtile->num_samples = min(wtile->num_samples, end_sample - sample); + work_tiles.copy_to_device(); + + hipDeviceptr_t d_work_tiles = (hipDeviceptr_t)work_tiles.device_pointer; + uint total_work_size = wtile->w * wtile->h * wtile->num_samples; + uint num_blocks = divide_up(total_work_size, num_threads_per_block); + + /* Launch kernel. */ + void *args[] = {&d_work_tiles, &total_work_size}; + + hip_assert( + hipModuleLaunchKernel(hipRender, num_blocks, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0)); + + /* Run the adaptive sampling kernels at selected samples aligned to step samples. */ + uint filter_sample = sample + wtile->num_samples - 1; + if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) { + adaptive_sampling_filter(filter_sample, wtile, d_work_tiles); + } + + hip_assert(hipDeviceSynchronize()); + + /* Update progress. */ + sample += wtile->num_samples; + rtile.sample = sample; + task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); + + if (task.get_cancel()) { + if (task.need_finish_queue == false) + break; + } + } + + /* Finalize adaptive sampling. */ + if (task.adaptive_sampling.use) { + hipDeviceptr_t d_work_tiles = (hipDeviceptr_t)work_tiles.device_pointer; + adaptive_sampling_post(rtile, wtile, d_work_tiles); + hip_assert(hipDeviceSynchronize()); + task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); + } +} + +void HIPDevice::thread_run(DeviceTask &task) +{ + HIPContextScope scope(this); + + if (task.type == DeviceTask::RENDER) { + device_vector work_tiles(this, "work_tiles", MEM_READ_ONLY); + + /* keep rendering tiles until done */ + RenderTile tile; + DenoisingTask denoising(this, task); + + while (task.acquire_tile(this, tile, task.tile_types)) { + if (tile.task == RenderTile::PATH_TRACE) { + render(task, tile, work_tiles); + } + else if (tile.task == RenderTile::BAKE) { + render(task, tile, work_tiles); + } + + task.release_tile(tile); + + if (task.get_cancel()) { + if (task.need_finish_queue == false) + break; + } + } + + work_tiles.free(); + } +} +# endif + +unique_ptr HIPDevice::gpu_queue_create() +{ + return make_unique(this); +} + +bool HIPDevice::should_use_graphics_interop() +{ + /* Check whether this device is part of OpenGL context. + * + * Using HIP device for graphics interoperability which is not part of the OpenGL context is + * possible, but from the empiric measurements it can be considerably slower than using naive + * pixels copy. */ + + HIPContextScope scope(this); + + int num_all_devices = 0; + hip_assert(hipGetDeviceCount(&num_all_devices)); + + if (num_all_devices == 0) { + return false; + } + + vector gl_devices(num_all_devices); + uint num_gl_devices = 0; + hipGLGetDevices(&num_gl_devices, gl_devices.data(), num_all_devices, hipGLDeviceListAll); + + for (hipDevice_t gl_device : gl_devices) { + if (gl_device == hipDevice) { + return true; + } + } + + return false; +} + +int HIPDevice::get_num_multiprocessors() +{ + return get_device_default_attribute(hipDeviceAttributeMultiprocessorCount, 0); +} + +int HIPDevice::get_max_num_threads_per_multiprocessor() +{ + return get_device_default_attribute(hipDeviceAttributeMaxThreadsPerMultiProcessor, 0); +} + +bool HIPDevice::get_device_attribute(hipDeviceAttribute_t attribute, int *value) +{ + HIPContextScope scope(this); + + return hipDeviceGetAttribute(value, attribute, hipDevice) == hipSuccess; +} + +int HIPDevice::get_device_default_attribute(hipDeviceAttribute_t attribute, int default_value) +{ + int value = 0; + if (!get_device_attribute(attribute, &value)) { + return default_value; + } + return value; +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/hip/device_impl.h b/intern/cycles/device/hip/device_impl.h new file mode 100644 index 00000000000..1d138ee9856 --- /dev/null +++ b/intern/cycles/device/hip/device_impl.h @@ -0,0 +1,153 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/device.h" +# include "device/hip/kernel.h" +# include "device/hip/queue.h" +# include "device/hip/util.h" + +# include "util/util_map.h" + +# ifdef WITH_HIP_DYNLOAD +# include "hipew.h" +# else +# include "util/util_opengl.h" +# endif + +CCL_NAMESPACE_BEGIN + +class DeviceQueue; + +class HIPDevice : public Device { + + friend class HIPContextScope; + + public: + hipDevice_t hipDevice; + hipCtx_t hipContext; + hipModule_t hipModule; + size_t device_texture_headroom; + size_t device_working_headroom; + bool move_texture_to_host; + size_t map_host_used; + size_t map_host_limit; + int can_map_host; + int pitch_alignment; + int hipDevId; + int hipDevArchitecture; + bool first_error; + + struct HIPMem { + HIPMem() : texobject(0), array(0), use_mapped_host(false) + { + } + + hipTextureObject_t texobject; + hArray array; + + /* If true, a mapped host memory in shared_pointer is being used. */ + bool use_mapped_host; + }; + typedef map HIPMemMap; + HIPMemMap hip_mem_map; + thread_mutex hip_mem_map_mutex; + + /* Bindless Textures */ + device_vector texture_info; + bool need_texture_info; + + HIPDeviceKernels kernels; + + static bool have_precompiled_kernels(); + + virtual bool show_samples() const override; + + virtual BVHLayoutMask get_bvh_layout_mask() const override; + + void set_error(const string &error) override; + + HIPDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler); + + virtual ~HIPDevice(); + + bool support_device(const uint /*kernel_features*/); + + bool check_peer_access(Device *peer_device) override; + + bool use_adaptive_compilation(); + + virtual string compile_kernel_get_common_cflags(const uint kernel_features); + + string compile_kernel(const uint kernel_features, + const char *name, + const char *base = "hip", + bool force_ptx = false); + + virtual bool load_kernels(const uint kernel_features) override; + void reserve_local_memory(const uint kernel_features); + + void init_host_memory(); + + void load_texture_info(); + + void move_textures_to_host(size_t size, bool for_texture); + + HIPMem *generic_alloc(device_memory &mem, size_t pitch_padding = 0); + + void generic_copy_to(device_memory &mem); + + void generic_free(device_memory &mem); + + void mem_alloc(device_memory &mem) override; + + void mem_copy_to(device_memory &mem) override; + + void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override; + + void mem_zero(device_memory &mem) override; + + void mem_free(device_memory &mem) override; + + device_ptr mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) override; + + virtual void const_copy_to(const char *name, void *host, size_t size) override; + + void global_alloc(device_memory &mem); + + void global_free(device_memory &mem); + + void tex_alloc(device_texture &mem); + + void tex_free(device_texture &mem); + + /* Graphics resources interoperability. */ + virtual bool should_use_graphics_interop() override; + + virtual unique_ptr gpu_queue_create() override; + + int get_num_multiprocessors(); + int get_max_num_threads_per_multiprocessor(); + + protected: + bool get_device_attribute(hipDeviceAttribute_t attribute, int *value); + int get_device_default_attribute(hipDeviceAttribute_t attribute, int default_value); +}; + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/hip/graphics_interop.cpp b/intern/cycles/device/hip/graphics_interop.cpp new file mode 100644 index 00000000000..add6dbed5e1 --- /dev/null +++ b/intern/cycles/device/hip/graphics_interop.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/hip/graphics_interop.h" + +# include "device/hip/device_impl.h" +# include "device/hip/util.h" + +CCL_NAMESPACE_BEGIN + +HIPDeviceGraphicsInterop::HIPDeviceGraphicsInterop(HIPDeviceQueue *queue) + : queue_(queue), device_(static_cast(queue->device)) +{ +} + +HIPDeviceGraphicsInterop::~HIPDeviceGraphicsInterop() +{ + HIPContextScope scope(device_); + + if (hip_graphics_resource_) { + hip_device_assert(device_, hipGraphicsUnregisterResource(hip_graphics_resource_)); + } +} + +void HIPDeviceGraphicsInterop::set_destination(const DeviceGraphicsInteropDestination &destination) +{ + const int64_t new_buffer_area = int64_t(destination.buffer_width) * destination.buffer_height; + + if (opengl_pbo_id_ == destination.opengl_pbo_id && buffer_area_ == new_buffer_area) { + return; + } + + HIPContextScope scope(device_); + + if (hip_graphics_resource_) { + hip_device_assert(device_, hipGraphicsUnregisterResource(hip_graphics_resource_)); + } + + const hipError_t result = hipGraphicsGLRegisterBuffer( + &hip_graphics_resource_, destination.opengl_pbo_id, hipGraphicsRegisterFlagsNone); + if (result != hipSuccess) { + LOG(ERROR) << "Error registering OpenGL buffer: " << hipewErrorString(result); + } + + opengl_pbo_id_ = destination.opengl_pbo_id; + buffer_area_ = new_buffer_area; +} + +device_ptr HIPDeviceGraphicsInterop::map() +{ + if (!hip_graphics_resource_) { + return 0; + } + + HIPContextScope scope(device_); + + hipDeviceptr_t hip_buffer; + size_t bytes; + + hip_device_assert(device_, + hipGraphicsMapResources(1, &hip_graphics_resource_, queue_->stream())); + hip_device_assert( + device_, hipGraphicsResourceGetMappedPointer(&hip_buffer, &bytes, hip_graphics_resource_)); + + return static_cast(hip_buffer); +} + +void HIPDeviceGraphicsInterop::unmap() +{ + HIPContextScope scope(device_); + + hip_device_assert(device_, + hipGraphicsUnmapResources(1, &hip_graphics_resource_, queue_->stream())); +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/hip/graphics_interop.h b/intern/cycles/device/hip/graphics_interop.h new file mode 100644 index 00000000000..adcaa13a2d7 --- /dev/null +++ b/intern/cycles/device/hip/graphics_interop.h @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/device_graphics_interop.h" + +# ifdef WITH_HIP_DYNLOAD +# include "hipew.h" +# endif + +CCL_NAMESPACE_BEGIN + +class HIPDevice; +class HIPDeviceQueue; + +class HIPDeviceGraphicsInterop : public DeviceGraphicsInterop { + public: + explicit HIPDeviceGraphicsInterop(HIPDeviceQueue *queue); + + HIPDeviceGraphicsInterop(const HIPDeviceGraphicsInterop &other) = delete; + HIPDeviceGraphicsInterop(HIPDeviceGraphicsInterop &&other) noexcept = delete; + + ~HIPDeviceGraphicsInterop(); + + HIPDeviceGraphicsInterop &operator=(const HIPDeviceGraphicsInterop &other) = delete; + HIPDeviceGraphicsInterop &operator=(HIPDeviceGraphicsInterop &&other) = delete; + + virtual void set_destination(const DeviceGraphicsInteropDestination &destination) override; + + virtual device_ptr map() override; + virtual void unmap() override; + + protected: + HIPDeviceQueue *queue_ = nullptr; + HIPDevice *device_ = nullptr; + + /* OpenGL PBO which is currently registered as the destination for the CUDA buffer. */ + uint opengl_pbo_id_ = 0; + /* Buffer area in pixels of the corresponding PBO. */ + int64_t buffer_area_ = 0; + + hipGraphicsResource hip_graphics_resource_ = nullptr; +}; + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/hip/kernel.cpp b/intern/cycles/device/hip/kernel.cpp new file mode 100644 index 00000000000..e0acd6f17c6 --- /dev/null +++ b/intern/cycles/device/hip/kernel.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/hip/kernel.h" +# include "device/hip/device_impl.h" + +CCL_NAMESPACE_BEGIN + +void HIPDeviceKernels::load(HIPDevice *device) +{ + hipModule_t hipModule = device->hipModule; + + for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) { + HIPDeviceKernel &kernel = kernels_[i]; + + /* No megakernel used for GPU. */ + if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { + continue; + } + + const std::string function_name = std::string("kernel_gpu_") + + device_kernel_as_string((DeviceKernel)i); + hip_device_assert(device, + hipModuleGetFunction(&kernel.function, hipModule, function_name.c_str())); + + if (kernel.function) { + hip_device_assert(device, hipFuncSetCacheConfig(kernel.function, hipFuncCachePreferL1)); + + hip_device_assert( + device, + hipModuleOccupancyMaxPotentialBlockSize( + &kernel.min_blocks, &kernel.num_threads_per_block, kernel.function, 0, 0)); + } + else { + LOG(ERROR) << "Unable to load kernel " << function_name; + } + } + + loaded = true; +} + +const HIPDeviceKernel &HIPDeviceKernels::get(DeviceKernel kernel) const +{ + return kernels_[(int)kernel]; +} + +bool HIPDeviceKernels::available(DeviceKernel kernel) const +{ + return kernels_[(int)kernel].function != nullptr; +} + +CCL_NAMESPACE_END + +#endif /* WITH_HIP*/ diff --git a/intern/cycles/device/hip/kernel.h b/intern/cycles/device/hip/kernel.h new file mode 100644 index 00000000000..3301731f56e --- /dev/null +++ b/intern/cycles/device/hip/kernel.h @@ -0,0 +1,54 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_HIP + +# include "device/device_kernel.h" + +# ifdef WITH_HIP_DYNLOAD +# include "hipew.h" +# endif + +CCL_NAMESPACE_BEGIN + +class HIPDevice; + +/* HIP kernel and associate occupancy information. */ +class HIPDeviceKernel { + public: + hipFunction_t function = nullptr; + + int num_threads_per_block = 0; + int min_blocks = 0; +}; + +/* Cache of HIP kernels for each DeviceKernel. */ +class HIPDeviceKernels { + public: + void load(HIPDevice *device); + const HIPDeviceKernel &get(DeviceKernel kernel) const; + bool available(DeviceKernel kernel) const; + + protected: + HIPDeviceKernel kernels_[DEVICE_KERNEL_NUM]; + bool loaded = false; +}; + +CCL_NAMESPACE_END + +#endif /* WITH_HIP */ diff --git a/intern/cycles/device/hip/queue.cpp b/intern/cycles/device/hip/queue.cpp new file mode 100644 index 00000000000..78c77e5fdae --- /dev/null +++ b/intern/cycles/device/hip/queue.cpp @@ -0,0 +1,209 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/hip/queue.h" + +# include "device/hip/device_impl.h" +# include "device/hip/graphics_interop.h" +# include "device/hip/kernel.h" + +CCL_NAMESPACE_BEGIN + +/* HIPDeviceQueue */ + +HIPDeviceQueue::HIPDeviceQueue(HIPDevice *device) + : DeviceQueue(device), hip_device_(device), hip_stream_(nullptr) +{ + const HIPContextScope scope(hip_device_); + hip_device_assert(hip_device_, hipStreamCreateWithFlags(&hip_stream_, hipStreamNonBlocking)); +} + +HIPDeviceQueue::~HIPDeviceQueue() +{ + const HIPContextScope scope(hip_device_); + hipStreamDestroy(hip_stream_); +} + +int HIPDeviceQueue::num_concurrent_states(const size_t /*state_size*/) const +{ + /* TODO: compute automatically. */ + /* TODO: must have at least num_threads_per_block. */ + return 14416128; +} + +int HIPDeviceQueue::num_concurrent_busy_states() const +{ + const int max_num_threads = hip_device_->get_num_multiprocessors() * + hip_device_->get_max_num_threads_per_multiprocessor(); + + if (max_num_threads == 0) { + return 65536; + } + + return 4 * max_num_threads; +} + +void HIPDeviceQueue::init_execution() +{ + /* Synchronize all textures and memory copies before executing task. */ + HIPContextScope scope(hip_device_); + hip_device_->load_texture_info(); + hip_device_assert(hip_device_, hipDeviceSynchronize()); + + debug_init_execution(); +} + +bool HIPDeviceQueue::kernel_available(DeviceKernel kernel) const +{ + return hip_device_->kernels.available(kernel); +} + +bool HIPDeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *args[]) +{ + if (hip_device_->have_error()) { + return false; + } + + debug_enqueue(kernel, work_size); + + const HIPContextScope scope(hip_device_); + const HIPDeviceKernel &hip_kernel = hip_device_->kernels.get(kernel); + + /* Compute kernel launch parameters. */ + const int num_threads_per_block = hip_kernel.num_threads_per_block; + const int num_blocks = divide_up(work_size, num_threads_per_block); + + int shared_mem_bytes = 0; + + switch (kernel) { + case DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY: + /* See parall_active_index.h for why this amount of shared memory is needed. */ + shared_mem_bytes = (num_threads_per_block + 1) * sizeof(int); + break; + default: + break; + } + + /* Launch kernel. */ + hip_device_assert(hip_device_, + hipModuleLaunchKernel(hip_kernel.function, + num_blocks, + 1, + 1, + num_threads_per_block, + 1, + 1, + shared_mem_bytes, + hip_stream_, + args, + 0)); + return !(hip_device_->have_error()); +} + +bool HIPDeviceQueue::synchronize() +{ + if (hip_device_->have_error()) { + return false; + } + + const HIPContextScope scope(hip_device_); + hip_device_assert(hip_device_, hipStreamSynchronize(hip_stream_)); + debug_synchronize(); + + return !(hip_device_->have_error()); +} + +void HIPDeviceQueue::zero_to_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + /* Allocate on demand. */ + if (mem.device_pointer == 0) { + hip_device_->mem_alloc(mem); + } + + /* Zero memory on device. */ + assert(mem.device_pointer != 0); + + const HIPContextScope scope(hip_device_); + hip_device_assert( + hip_device_, + hipMemsetD8Async((hipDeviceptr_t)mem.device_pointer, 0, mem.memory_size(), hip_stream_)); +} + +void HIPDeviceQueue::copy_to_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + /* Allocate on demand. */ + if (mem.device_pointer == 0) { + hip_device_->mem_alloc(mem); + } + + assert(mem.device_pointer != 0); + assert(mem.host_pointer != nullptr); + + /* Copy memory to device. */ + const HIPContextScope scope(hip_device_); + hip_device_assert( + hip_device_, + hipMemcpyHtoDAsync( + (hipDeviceptr_t)mem.device_pointer, mem.host_pointer, mem.memory_size(), hip_stream_)); +} + +void HIPDeviceQueue::copy_from_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + assert(mem.device_pointer != 0); + assert(mem.host_pointer != nullptr); + + /* Copy memory from device. */ + const HIPContextScope scope(hip_device_); + hip_device_assert( + hip_device_, + hipMemcpyDtoHAsync( + mem.host_pointer, (hipDeviceptr_t)mem.device_pointer, mem.memory_size(), hip_stream_)); +} + +// TODO : (Arya) Enable this after stabilizing dev branch +unique_ptr HIPDeviceQueue::graphics_interop_create() +{ + return make_unique(this); +} + +CCL_NAMESPACE_END + +#endif /* WITH_HIP */ diff --git a/intern/cycles/device/hip/queue.h b/intern/cycles/device/hip/queue.h new file mode 100644 index 00000000000..04c8a5982ce --- /dev/null +++ b/intern/cycles/device/hip/queue.h @@ -0,0 +1,68 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_HIP + +# include "device/device_kernel.h" +# include "device/device_memory.h" +# include "device/device_queue.h" + +# include "device/hip/util.h" + +CCL_NAMESPACE_BEGIN + +class HIPDevice; +class device_memory; + +/* Base class for HIP queues. */ +class HIPDeviceQueue : public DeviceQueue { + public: + HIPDeviceQueue(HIPDevice *device); + ~HIPDeviceQueue(); + + virtual int num_concurrent_states(const size_t state_size) const override; + virtual int num_concurrent_busy_states() const override; + + virtual void init_execution() override; + + virtual bool kernel_available(DeviceKernel kernel) const override; + + virtual bool enqueue(DeviceKernel kernel, const int work_size, void *args[]) override; + + virtual bool synchronize() override; + + virtual void zero_to_device(device_memory &mem) override; + virtual void copy_to_device(device_memory &mem) override; + virtual void copy_from_device(device_memory &mem) override; + + virtual hipStream_t stream() + { + return hip_stream_; + } + + // TODO : (Arya) Enable this after stabilizing the dev branch + virtual unique_ptr graphics_interop_create() override; + + protected: + HIPDevice *hip_device_; + hipStream_t hip_stream_; +}; + +CCL_NAMESPACE_END + +#endif /* WITH_HIP */ diff --git a/intern/cycles/device/hip/util.cpp b/intern/cycles/device/hip/util.cpp new file mode 100644 index 00000000000..44f52c4e17b --- /dev/null +++ b/intern/cycles/device/hip/util.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/hip/util.h" +# include "device/hip/device_impl.h" + +CCL_NAMESPACE_BEGIN + +HIPContextScope::HIPContextScope(HIPDevice *device) : device(device) +{ + hip_device_assert(device, hipCtxPushCurrent(device->hipContext)); +} + +HIPContextScope::~HIPContextScope() +{ + hip_device_assert(device, hipCtxPopCurrent(NULL)); +} + +# ifndef WITH_HIP_DYNLOAD +const char *hipewErrorString(hipError_t result) +{ + /* We can only give error code here without major code duplication, that + * should be enough since dynamic loading is only being disabled by folks + * who knows what they're doing anyway. + * + * NOTE: Avoid call from several threads. + */ + static string error; + error = string_printf("%d", result); + return error.c_str(); +} + +const char *hipewCompilerPath() +{ + return CYCLES_HIP_HIPCC_EXECUTABLE; +} + +int hipewCompilerVersion() +{ + return (HIP_VERSION / 100) + (HIP_VERSION % 100 / 10); +} +# endif + +CCL_NAMESPACE_END + +#endif /* WITH_HIP */ diff --git a/intern/cycles/device/hip/util.h b/intern/cycles/device/hip/util.h new file mode 100644 index 00000000000..f8468e0dc5c --- /dev/null +++ b/intern/cycles/device/hip/util.h @@ -0,0 +1,63 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_HIP + +# ifdef WITH_HIP_DYNLOAD +# include "hipew.h" +# endif + +CCL_NAMESPACE_BEGIN + +class HIPDevice; + +/* Utility to push/pop HIP context. */ +class HIPContextScope { + public: + HIPContextScope(HIPDevice *device); + ~HIPContextScope(); + + private: + HIPDevice *device; +}; + +/* Utility for checking return values of HIP function calls. */ +# define hip_device_assert(hip_device, stmt) \ + { \ + hipError_t result = stmt; \ + if (result != hipSuccess) { \ + const char *name = hipewErrorString(result); \ + hip_device->set_error( \ + string_printf("%s in %s (%s:%d)", name, #stmt, __FILE__, __LINE__)); \ + } \ + } \ + (void)0 + +# define hip_assert(stmt) hip_device_assert(this, stmt) + +# ifndef WITH_HIP_DYNLOAD +/* Transparently implement some functions, so majority of the file does not need + * to worry about difference between dynamically loaded and linked HIP at all. */ +const char *hipewErrorString(hipError_t result); +const char *hipewCompilerPath(); +int hipewCompilerVersion(); +# endif /* WITH_HIP_DYNLOAD */ + +CCL_NAMESPACE_END + +#endif /* WITH_HIP */ \ No newline at end of file diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 10d0c5e7b4c..9633d3b87d3 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -1035,6 +1035,8 @@ static const char *device_type_for_description(const DeviceType type) return "CUDA"; case DEVICE_OPTIX: return "OptiX"; + case DEVICE_HIP: + return "HIP"; case DEVICE_DUMMY: return "Dummy"; case DEVICE_MULTI: diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 4196539a9b1..8c2cb2c68de 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -35,6 +35,10 @@ set(SRC_DEVICE_CUDA device/cuda/kernel.cu ) +set(SRC_DEVICE_HIP + device/hip/kernel.cpp +) + set(SRC_DEVICE_OPTIX device/optix/kernel.cu device/optix/kernel_shader_raytrace.cu @@ -106,6 +110,12 @@ set(SRC_DEVICE_CUDA_HEADERS device/cuda/globals.h ) +set(SRC_DEVICE_HIP_HEADERS + device/hip/compat.h + device/hip/config.h + device/hip/globals.h +) + set(SRC_DEVICE_OPTIX_HEADERS device/optix/compat.h device/optix/globals.h @@ -458,6 +468,104 @@ if(WITH_CYCLES_CUDA_BINARIES) cycles_set_solution_folder(cycles_kernel_cuda) endif() +####################################################### START + +# HIP module + +if(WITH_CYCLES_HIP_BINARIES) + # 64 bit only + set(HIP_BITS 64) + + # HIP version + execute_process(COMMAND ${HIP_HIPCC_EXECUTABLE} "--version" OUTPUT_VARIABLE HIPCC_OUT) + string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\1" HIP_VERSION_MAJOR "${HIPCC_OUT}") + string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\2" HIP_VERSION_MINOR "${HIPCC_OUT}") + set(HIP_VERSION "${HIP_VERSION_MAJOR}${HIP_VERSION_MINOR}") + + + message(WARNING + "HIP version ${HIP_VERSION_MAJOR}.${HIP_VERSION_MINOR} detected") + + # build for each arch + set(hip_sources device/hip/kernel.cpp + ${SRC_HEADERS} + ${SRC_DEVICE_HIP_HEADERS} + ${SRC_BVH_HEADERS} + ${SRC_SVM_HEADERS} + ${SRC_GEOM_HEADERS} + ${SRC_INTEGRATOR_HEADERS} + ${SRC_CLOSURE_HEADERS} + ${SRC_UTIL_HEADERS} + ) + set(hip_fatbins) + + macro(CYCLES_HIP_KERNEL_ADD arch prev_arch name flags sources experimental) + if(${arch} MATCHES "compute_.*") + set(format "ptx") + else() + set(format "fatbin") + endif() + set(hip_file ${name}_${arch}.${format}) + + set(kernel_sources ${sources}) + if(NOT ${prev_arch} STREQUAL "none") + if(${prev_arch} MATCHES "compute_.*") + set(kernel_sources ${kernel_sources} ${name}_${prev_arch}.ptx) + else() + set(kernel_sources ${kernel_sources} ${name}_${prev_arch}.fatbin) + endif() + endif() + + set(hip_kernel_src "/device/hip/${name}.cpp") + + set(hip_flags ${flags} + -D CCL_NAMESPACE_BEGIN= + -D CCL_NAMESPACE_END= + -D HIPCC + -m ${HIP_BITS} + -I ${CMAKE_CURRENT_SOURCE_DIR}/.. + -I ${CMAKE_CURRENT_SOURCE_DIR}/device/hip + --use_fast_math + -o ${CMAKE_CURRENT_BINARY_DIR}/${hip_file}) + + if(${experimental}) + set(hip_flags ${hip_flags} -D __KERNEL_EXPERIMENTAL__) + set(name ${name}_experimental) + endif() + + if(WITH_CYCLES_DEBUG) + set(hip_flags ${hip_flags} -D __KERNEL_DEBUG__) + endif() + + if(WITH_NANOVDB) + set(hip_flags ${hip_flags} + -D WITH_NANOVDB + -I "${NANOVDB_INCLUDE_DIR}") + endif() + + + set(prev_arch "none") + foreach(arch ${CYCLES_HIP_BINARIES_ARCH}) + set(hip_hipcc_executable ${HIP_HIPCC_EXECUTABLE}) + set(hip_toolkit_root_dir ${HIP_TOOLKIT_ROOT_DIR}) + if(DEFINED hip_hipcc_executable AND DEFINED hip_toolkit_root_dir) + # Compile regular kernel + CYCLES_HIP_KERNEL_ADD(${arch} ${prev_arch} kernel "" "${hip_sources}" FALSE) + + if(WITH_CYCLES_HIP_BUILD_SERIAL) + set(prev_arch ${arch}) + endif() + + unset(hip_hipcc_executable) + unset(hip_toolkit_root_dir) + endif() + endforeach() + + add_custom_target(cycles_kernel_hip ALL DEPENDS ${hip_fatbins}) + cycles_set_solution_folder(cycles_kernel_hip) +endif() + +####################################################### END # OptiX PTX modules if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) @@ -602,11 +710,13 @@ endif() cycles_add_library(cycles_kernel "${LIB}" ${SRC_DEVICE_CPU} ${SRC_DEVICE_CUDA} + ${SRC_DEVICE_HIP} ${SRC_DEVICE_OPTIX} ${SRC_HEADERS} ${SRC_DEVICE_CPU_HEADERS} ${SRC_DEVICE_GPU_HEADERS} ${SRC_DEVICE_CUDA_HEADERS} + ${SRC_DEVICE_HIP_HEADERS} ${SRC_DEVICE_OPTIX_HEADERS} ${SRC_BVH_HEADERS} ${SRC_CLOSURE_HEADERS} @@ -621,6 +731,7 @@ source_group("geom" FILES ${SRC_GEOM_HEADERS}) source_group("integrator" FILES ${SRC_INTEGRATOR_HEADERS}) source_group("kernel" FILES ${SRC_HEADERS}) source_group("device\\cpu" FILES ${SRC_DEVICE_CPU} ${SRC_DEVICE_CPU_HEADERS}) +source_group("device\\hip" FILES ${SRC_DEVICE_HIP} ${SRC_DEVICE_HIP_HEADERS}) source_group("device\\gpu" FILES ${SRC_DEVICE_GPU_HEADERS}) source_group("device\\cuda" FILES ${SRC_DEVICE_CUDA} ${SRC_DEVICE_CUDA_HEADERS}) source_group("device\\optix" FILES ${SRC_DEVICE_OPTIX} ${SRC_DEVICE_OPTIX_HEADERS}) @@ -632,14 +743,19 @@ endif() if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) add_dependencies(cycles_kernel cycles_kernel_optix) endif() +if(WITH_CYCLES_HIP) + add_dependencies(cycles_kernel cycles_kernel_hip) +endif() # Install kernel source for runtime compilation delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_CUDA}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_HIP}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_OPTIX}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_GPU_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/gpu) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_CUDA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_HIP_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_OPTIX_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_BVH_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bvh) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_CLOSURE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/closure) diff --git a/intern/cycles/kernel/device/gpu/parallel_active_index.h b/intern/cycles/kernel/device/gpu/parallel_active_index.h index a68d1d80c7d..db4a4bf71e0 100644 --- a/intern/cycles/kernel/device/gpu/parallel_active_index.h +++ b/intern/cycles/kernel/device/gpu/parallel_active_index.h @@ -25,7 +25,11 @@ CCL_NAMESPACE_BEGIN #include "util/util_atomic.h" -#define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 512 +#ifdef __HIP__ +# define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 1024 +#else +# define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 512 +#endif template __device__ void gpu_parallel_active_index_array(const uint num_states, diff --git a/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h index f609520b8b4..a1349e82efb 100644 --- a/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h +++ b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h @@ -27,7 +27,11 @@ CCL_NAMESPACE_BEGIN #include "util/util_atomic.h" -#define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 512 +#ifdef __HIP__ +# define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 1024 +#else +# define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 512 +#endif template __device__ void gpu_parallel_prefix_sum(int *values, const int num_values) { diff --git a/intern/cycles/kernel/device/gpu/parallel_reduce.h b/intern/cycles/kernel/device/gpu/parallel_reduce.h index 65b1990dbb8..b60dceb2ed0 100644 --- a/intern/cycles/kernel/device/gpu/parallel_reduce.h +++ b/intern/cycles/kernel/device/gpu/parallel_reduce.h @@ -26,7 +26,11 @@ CCL_NAMESPACE_BEGIN * the overall cost of the algorithm while keeping the work complexity O(n) and * the step complexity O(log n). (Brent's Theorem optimization) */ -#define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 512 +#ifdef __HIP__ +# define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 1024 +#else +# define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 512 +#endif template __device__ void gpu_parallel_sum( diff --git a/intern/cycles/kernel/device/gpu/parallel_sorted_index.h b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h index 99b35468517..9bca1fad22f 100644 --- a/intern/cycles/kernel/device/gpu/parallel_sorted_index.h +++ b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h @@ -26,7 +26,11 @@ CCL_NAMESPACE_BEGIN #include "util/util_atomic.h" -#define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 512 +#ifdef __HIP__ +# define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 1024 +#else +# define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 512 +#endif #define GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY (~0) template diff --git a/intern/cycles/kernel/device/hip/compat.h b/intern/cycles/kernel/device/hip/compat.h new file mode 100644 index 00000000000..3644925d5be --- /dev/null +++ b/intern/cycles/kernel/device/hip/compat.h @@ -0,0 +1,121 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#define __KERNEL_GPU__ +#define __KERNEL_HIP__ +#define CCL_NAMESPACE_BEGIN +#define CCL_NAMESPACE_END + +#ifndef ATTR_FALLTHROUGH +# define ATTR_FALLTHROUGH +#endif + +#ifdef __HIPCC_RTC__ +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +#else +# include +#endif + +#ifdef CYCLES_HIPBIN_CC +# define FLT_MIN 1.175494350822287507969e-38f +# define FLT_MAX 340282346638528859811704183484516925440.0f +# define FLT_EPSILON 1.192092896e-07F +#endif + +/* Qualifiers */ + +#define ccl_device __device__ __inline__ +#define ccl_device_inline __device__ __inline__ +#define ccl_device_forceinline __device__ __forceinline__ +#define ccl_device_noinline __device__ __noinline__ +#define ccl_device_noinline_cpu ccl_device +#define ccl_global +#define ccl_static_constant __constant__ +#define ccl_device_constant __constant__ __device__ +#define ccl_constant const +#define ccl_gpu_shared __shared__ +#define ccl_private +#define ccl_may_alias +#define ccl_addr_space +#define ccl_restrict __restrict__ +#define ccl_loop_no_unroll +#define ccl_align(n) __align__(n) +#define ccl_optional_struct_init + +#define kernel_assert(cond) + +/* Types */ +#ifdef __HIP__ +# include "hip/hip_fp16.h" +# include "hip/hip_runtime.h" +#endif + +#ifdef _MSC_VER +# include +#endif + +#define ccl_gpu_thread_idx_x (threadIdx.x) +#define ccl_gpu_block_dim_x (blockDim.x) +#define ccl_gpu_block_idx_x (blockIdx.x) +#define ccl_gpu_grid_dim_x (gridDim.x) +#define ccl_gpu_warp_size (warpSize) + +#define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) +#define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) + +/* GPU warp synchronizaton */ + +#define ccl_gpu_syncthreads() __syncthreads() +#define ccl_gpu_ballot(predicate) __ballot(predicate) +#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down(var, detla) +#define ccl_gpu_popc(x) __popc(x) + +/* GPU texture objects */ +typedef hipTextureObject_t ccl_gpu_tex_object; + +template +ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object texobj, + const float x, + const float y) +{ + return tex2D(texobj, x, y); +} + +template +ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object texobj, + const float x, + const float y, + const float z) +{ + return tex3D(texobj, x, y, z); +} + +/* Use fast math functions */ + +#define cosf(x) __cosf(((float)(x))) +#define sinf(x) __sinf(((float)(x))) +#define powf(x, y) __powf(((float)(x)), ((float)(y))) +#define tanf(x) __tanf(((float)(x))) +#define logf(x) __logf(((float)(x))) +#define expf(x) __expf(((float)(x))) + +/* Types */ + +#include "util/util_half.h" +#include "util/util_types.h" diff --git a/intern/cycles/kernel/device/hip/config.h b/intern/cycles/kernel/device/hip/config.h new file mode 100644 index 00000000000..2fde0d46015 --- /dev/null +++ b/intern/cycles/kernel/device/hip/config.h @@ -0,0 +1,57 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Device data taken from HIP occupancy calculator. + * + * Terminology + * - HIP GPUs have multiple streaming multiprocessors + * - Each multiprocessor executes multiple thread blocks + * - Each thread block contains a number of threads, also known as the block size + * - Multiprocessors have a fixed number of registers, and the amount of registers + * used by each threads limits the number of threads per block. + */ + +/* Launch Bound Definitions */ +#define GPU_MULTIPRESSOR_MAX_REGISTERS 65536 +#define GPU_MULTIPROCESSOR_MAX_BLOCKS 64 +#define GPU_BLOCK_MAX_THREADS 1024 +#define GPU_THREAD_MAX_REGISTERS 255 + +#define GPU_KERNEL_BLOCK_NUM_THREADS 1024 +#define GPU_KERNEL_MAX_REGISTERS 64 + +/* Compute number of threads per block and minimum blocks per multiprocessor + * given the maximum number of registers per thread. */ + +#define ccl_gpu_kernel(block_num_threads, thread_num_registers) \ + extern "C" __global__ void __launch_bounds__(block_num_threads, \ + GPU_MULTIPRESSOR_MAX_REGISTERS / \ + (block_num_threads * thread_num_registers)) + +/* sanity checks */ + +#if GPU_KERNEL_BLOCK_NUM_THREADS > GPU_BLOCK_MAX_THREADS +# error "Maximum number of threads per block exceeded" +#endif + +#if GPU_MULTIPRESSOR_MAX_REGISTERS / (GPU_KERNEL_BLOCK_NUM_THREADS * GPU_KERNEL_MAX_REGISTERS) > \ + GPU_MULTIPROCESSOR_MAX_BLOCKS +# error "Maximum number of blocks per multiprocessor exceeded" +#endif + +#if GPU_KERNEL_MAX_REGISTERS > GPU_THREAD_MAX_REGISTERS +# error "Maximum number of registers per thread exceeded" +#endif diff --git a/intern/cycles/kernel/device/hip/globals.h b/intern/cycles/kernel/device/hip/globals.h new file mode 100644 index 00000000000..39978ae7899 --- /dev/null +++ b/intern/cycles/kernel/device/hip/globals.h @@ -0,0 +1,49 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Constant Globals */ + +#pragma once + +#include "kernel/kernel_profiling.h" +#include "kernel/kernel_types.h" + +#include "kernel/integrator/integrator_state.h" + +CCL_NAMESPACE_BEGIN + +/* Not actually used, just a NULL pointer that gets passed everywhere, which we + * hope gets optimized out by the compiler. */ +struct KernelGlobals { + /* NOTE: Keep the size in sync with SHADOW_STACK_MAX_HITS. */ + int unused[1]; +}; + +/* Global scene data and textures */ +__constant__ KernelData __data; +#define KERNEL_TEX(type, name) __attribute__((used)) const __constant__ __device__ type *name; +#include "kernel/kernel_textures.h" + +/* Integrator state */ +__constant__ IntegratorStateGPU __integrator_state; + +/* Abstraction macros */ +#define kernel_data __data +#define kernel_tex_fetch(t, index) t[(index)] +#define kernel_tex_array(t) (t) +#define kernel_integrator_state __integrator_state + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/hip/kernel.cpp b/intern/cycles/kernel/device/hip/kernel.cpp new file mode 100644 index 00000000000..c801320a2e1 --- /dev/null +++ b/intern/cycles/kernel/device/hip/kernel.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* HIP kernel entry points */ + +#ifdef __HIP_DEVICE_COMPILE__ + +# include "kernel/device/hip/compat.h" +# include "kernel/device/hip/config.h" +# include "kernel/device/hip/globals.h" + +# include "kernel/device/gpu/image.h" +# include "kernel/device/gpu/kernel.h" + +#endif diff --git a/intern/cycles/util/util_atomic.h b/intern/cycles/util/util_atomic.h index de17efafcf2..faba411c769 100644 --- a/intern/cycles/util/util_atomic.h +++ b/intern/cycles/util/util_atomic.h @@ -34,7 +34,7 @@ #else /* __KERNEL_GPU__ */ -# ifdef __KERNEL_CUDA__ +# if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__) # define atomic_add_and_fetch_float(p, x) (atomicAdd((float *)(p), (float)(x)) + (float)(x)) diff --git a/intern/cycles/util/util_debug.cpp b/intern/cycles/util/util_debug.cpp index 1d598725c84..2245668d02f 100644 --- a/intern/cycles/util/util_debug.cpp +++ b/intern/cycles/util/util_debug.cpp @@ -59,12 +59,23 @@ DebugFlags::CUDA::CUDA() : adaptive_compile(false) reset(); } +DebugFlags::HIP::HIP() : adaptive_compile(false) +{ + reset(); +} + void DebugFlags::CUDA::reset() { if (getenv("CYCLES_CUDA_ADAPTIVE_COMPILE") != NULL) adaptive_compile = true; } +void DebugFlags::HIP::reset() +{ + if (getenv("CYCLES_HIP_ADAPTIVE_COMPILE") != NULL) + adaptive_compile = true; +} + DebugFlags::OptiX::OptiX() { reset(); @@ -103,6 +114,10 @@ std::ostream &operator<<(std::ostream &os, DebugFlagsConstRef debug_flags) os << "OptiX flags:\n" << " Debug : " << string_from_bool(debug_flags.optix.use_debug) << "\n"; + + os << "HIP flags:\n" + << " HIP streams : " << string_from_bool(debug_flags.hip.adaptive_compile) << "\n"; + return os; } diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/util_debug.h index a2acaea5675..81677201790 100644 --- a/intern/cycles/util/util_debug.h +++ b/intern/cycles/util/util_debug.h @@ -93,6 +93,17 @@ class DebugFlags { bool adaptive_compile; }; + /* Descriptor of HIP feature-set to be used. */ + struct HIP { + HIP(); + + /* Reset flags to their defaults. */ + void reset(); + + /* Whether adaptive feature based runtime compile is enabled or not.*/ + bool adaptive_compile; + }; + /* Descriptor of OptiX feature-set to be used. */ struct OptiX { OptiX(); @@ -124,6 +135,9 @@ class DebugFlags { /* Requested OptiX flags. */ OptiX optix; + /* Requested HIP flags. */ + HIP hip; + private: DebugFlags(); diff --git a/intern/cycles/util/util_half.h b/intern/cycles/util/util_half.h index d9edfec5da3..f36a492a1b0 100644 --- a/intern/cycles/util/util_half.h +++ b/intern/cycles/util/util_half.h @@ -29,7 +29,7 @@ CCL_NAMESPACE_BEGIN /* Half Floats */ /* CUDA has its own half data type, no need to define then */ -#ifndef __KERNEL_CUDA__ +#if !defined(__KERNEL_CUDA__) && !defined(__KERNEL_HIP__) /* Implementing this as a class rather than a typedef so that the compiler can tell it apart from * unsigned shorts. */ class half { @@ -59,7 +59,7 @@ struct half4 { half x, y, z, w; }; -#ifdef __KERNEL_CUDA__ +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__) ccl_device_inline void float4_store_half(half *h, float4 f) { @@ -73,6 +73,7 @@ ccl_device_inline void float4_store_half(half *h, float4 f) ccl_device_inline void float4_store_half(half *h, float4 f) { + # ifndef __KERNEL_SSE2__ for (int i = 0; i < 4; i++) { /* optimized float to half for pixels: @@ -109,6 +110,8 @@ ccl_device_inline void float4_store_half(half *h, float4 f) # endif } +# ifndef __KERNEL_HIP__ + ccl_device_inline float half_to_float(half h) { float f; @@ -117,6 +120,23 @@ ccl_device_inline float half_to_float(half h) return f; } +# else + +ccl_device_inline float half_to_float(std::uint32_t a) noexcept +{ + + std::uint32_t u = ((a << 13) + 0x70000000U) & 0x8fffe000U; + + std::uint32_t v = __float_as_uint(__uint_as_float(u) * + __uint_as_float(0x77800000U) /*0x1.0p+112f*/) + + 0x38000000U; + + u = (a & 0x7fff) != 0 ? v : u; + + return __uint_as_float(u) * __uint_as_float(0x07800000U) /*0x1.0p-112f*/; +} + +# endif /* __KERNEL_HIP__ */ ccl_device_inline float4 half4_to_float4(half4 h) { diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index 6d728dde679..cb1e94c838c 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -26,6 +26,10 @@ # include #endif +#ifdef __HIP__ +# include +#endif + #include #include #include @@ -83,7 +87,8 @@ CCL_NAMESPACE_BEGIN /* Scalar */ -#ifdef _WIN32 +#ifndef __HIP__ +# ifdef _WIN32 ccl_device_inline float fmaxf(float a, float b) { return (a > b) ? a : b; @@ -93,7 +98,9 @@ ccl_device_inline float fminf(float a, float b) { return (a < b) ? a : b; } -#endif /* _WIN32 */ + +# endif /* _WIN32 */ +#endif /* __HIP__ */ #ifndef __KERNEL_GPU__ using std::isfinite; @@ -199,6 +206,7 @@ ccl_device_inline uint as_uint(float f) return u.i; } +#ifndef __HIP__ ccl_device_inline int __float_as_int(float f) { union { @@ -238,6 +246,7 @@ ccl_device_inline float __uint_as_float(uint i) u.i = i; return u.f; } +#endif ccl_device_inline int4 __float4_as_int4(float4 f) { @@ -669,7 +678,7 @@ ccl_device float bits_to_01(uint bits) ccl_device_inline uint count_leading_zeros(uint x) { -#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) || defined(__KERNEL_HIP__) return __clz(x); #else assert(x != 0); @@ -685,7 +694,7 @@ ccl_device_inline uint count_leading_zeros(uint x) ccl_device_inline uint count_trailing_zeros(uint x) { -#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) || defined(__KERNEL_HIP__) return (__ffs(x) - 1); #else assert(x != 0); @@ -701,7 +710,7 @@ ccl_device_inline uint count_trailing_zeros(uint x) ccl_device_inline uint find_first_set(uint x) { -#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) || defined(__KERNEL_HIP__) return __ffs(x); #else # ifdef _MSC_VER From faedfd574015b0eca0371baf02f2c7ef1d478e45 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Tue, 28 Sep 2021 12:37:56 -0500 Subject: [PATCH 0311/1500] Fix VS2017 compile error in String to Curves node Because of a bug in VS2017 codecvt is replaced with Blender BLI functions to convert from UTF8 to UTF32. Differential Revision: https://developer.blender.org/D12655 --- .../nodes/node_geo_string_to_curves.cc | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 0bd050ac3d1..5e2f03806c3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -14,9 +14,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include - #include "DNA_curve_types.h" #include "DNA_vfont_types.h" @@ -207,13 +204,13 @@ static TextLayout get_text_layout(GeoNodeExecParams ¶ms) /* Returns a mapping of UTF-32 character code to instance handle. */ static Map create_curve_instances(GeoNodeExecParams ¶ms, const float fontsize, - const std::u32string &charcodes, + const Span charcodes, InstancesComponent &instance_component) { VFont *vfont = (VFont *)params.node().id; Map handles; - for (int i : IndexRange(charcodes.length())) { + for (int i : charcodes.index_range()) { if (handles.contains(charcodes[i])) { continue; } @@ -239,7 +236,7 @@ static Map create_curve_instances(GeoNodeExecParams ¶ms, static void add_instances_from_handles(InstancesComponent &instances, const Map &char_handles, - const std::u32string &charcodes, + const Span charcodes, const Span positions) { instances.resize(positions.size()); @@ -272,15 +269,17 @@ static void geo_node_string_to_curves_exec(GeoNodeExecParams params) } /* Convert UTF-8 encoded string to UTF-32. */ - std::wstring_convert, char32_t> converter; - std::u32string utf32_text = converter.from_bytes(layout.text); + size_t len_bytes; + size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes); + Array char_codes(len_chars + 1); + BLI_str_utf8_as_utf32(char_codes.data(), layout.text.c_str(), len_chars + 1); /* Create and add instances. */ GeometrySet geometry_set_out; InstancesComponent &instances = geometry_set_out.get_component_for_write(); Map char_handles = create_curve_instances( - params, layout.final_font_size, utf32_text, instances); - add_instances_from_handles(instances, char_handles, utf32_text, layout.positions); + params, layout.final_font_size, char_codes, instances); + add_instances_from_handles(instances, char_handles, char_codes, layout.positions); params.set_output("Curves", std::move(geometry_set_out)); } From e45ffce5fadd55ebead3fd1a89964f330baac526 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 28 Sep 2021 12:44:50 -0500 Subject: [PATCH 0312/1500] Geometry Nodes: Use factor slider for distribution density factor Though the factor isn't so useful to adjust by itself, and is mostly useful when used with a field connected, the slider from 0 to 1 can help to make it clear that it's just used as a multiplier for the max density after distribution. Differential Revision: https://developer.blender.org/D12654 --- .../nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index f27544cbdda..1a4c5d84dbf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -50,6 +50,7 @@ static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBui .default_value(1.0f) .min(0.0f) .max(1.0f) + .subtype(PROP_FACTOR) .supports_field(); b.add_input("Seed"); b.add_input("Selection").default_value(true).hide_value().supports_field(); From 86ec9d79eca2a31044a5096df5d5ee244d15708d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 28 Sep 2021 19:55:25 +0200 Subject: [PATCH 0313/1500] Fix build without Cycles HIP device --- extern/CMakeLists.txt | 4 +-- intern/cycles/cmake/external_libs.cmake | 7 +++- intern/cycles/cmake/macros.cmake | 12 ++++--- intern/cycles/device/CMakeLists.txt | 44 ++++++++++++++----------- intern/cycles/kernel/CMakeLists.txt | 2 +- 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 2b2cca04503..1fdc8e60167 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -67,10 +67,10 @@ endif() if(WITH_CYCLES OR WITH_COMPOSITOR OR WITH_OPENSUBDIV) add_subdirectory(clew) - if(WITH_CUDA_DYNLOAD) + if((WITH_CYCLES_DEVICE_CUDA OR WITH_CYCLES_DEVICE_OPTIX) AND WITH_CUDA_DYNLOAD) add_subdirectory(cuew) endif() - if(WITH_HIP_DYNLOAD) + if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD) add_subdirectory(hipew) endif() endif() diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake index 5653f51ec05..b966edd4298 100644 --- a/intern/cycles/cmake/external_libs.cmake +++ b/intern/cycles/cmake/external_libs.cmake @@ -531,8 +531,13 @@ if(WITH_CYCLES_CUDA_BINARIES OR NOT WITH_CUDA_DYNLOAD) endif() endif() endif() + + +########################################################################### +# HIP +########################################################################### + if(NOT WITH_HIP_DYNLOAD) - message(STATUS "Setting up HIP Dynamic Load") set(WITH_HIP_DYNLOAD ON) endif() diff --git a/intern/cycles/cmake/macros.cmake b/intern/cycles/cmake/macros.cmake index 172ae280cea..a470fb9c574 100644 --- a/intern/cycles/cmake/macros.cmake +++ b/intern/cycles/cmake/macros.cmake @@ -156,13 +156,15 @@ macro(cycles_target_link_libraries target) ${PLATFORM_LINKLIBS} ) - if(WITH_CUDA_DYNLOAD) - target_link_libraries(${target} extern_cuew) - else() - target_link_libraries(${target} ${CUDA_CUDA_LIBRARY}) + if(WITH_CYCLES_DEVICE_CUDA OR WITH_CYCLES_DEVICE_OPTIX) + if(WITH_CUDA_DYNLOAD) + target_link_libraries(${target} extern_cuew) + else() + target_link_libraries(${target} ${CUDA_CUDA_LIBRARY}) + endif() endif() - if(WITH_HIP_DYNLOAD) + if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD) target_link_libraries(${target} extern_hipew) endif() diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt index 9af68550d17..6d33a6f107f 100644 --- a/intern/cycles/device/CMakeLists.txt +++ b/intern/cycles/device/CMakeLists.txt @@ -22,19 +22,21 @@ set(INC_SYS ../../../extern/clew/include ) -if(WITH_CUDA_DYNLOAD) - list(APPEND INC - ../../../extern/cuew/include - ) - add_definitions(-DWITH_CUDA_DYNLOAD) -else() - list(APPEND INC_SYS - ${CUDA_TOOLKIT_INCLUDE} - ) - add_definitions(-DCYCLES_CUDA_NVCC_EXECUTABLE="${CUDA_NVCC_EXECUTABLE}") +if(WITH_CYCLES_DEVICE_OPTIX OR WITH_CYCLES_DEVICE_CUDA) + if(WITH_CUDA_DYNLOAD) + list(APPEND INC + ../../../extern/cuew/include + ) + add_definitions(-DWITH_CUDA_DYNLOAD) + else() + list(APPEND INC_SYS + ${CUDA_TOOLKIT_INCLUDE} + ) + add_definitions(-DCYCLES_CUDA_NVCC_EXECUTABLE="${CUDA_NVCC_EXECUTABLE}") + endif() endif() -if(WITH_HIP_DYNLOAD) +if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD) list(APPEND INC ../../../extern/hipew/include ) @@ -127,17 +129,19 @@ set(LIB ${CYCLES_GL_LIBRARIES} ) -if(WITH_CUDA_DYNLOAD) - list(APPEND LIB - extern_cuew - ) -else() - list(APPEND LIB - ${CUDA_CUDA_LIBRARY} - ) +if(WITH_CYCLES_DEVICE_OPTIX OR WITH_CYCLES_DEVICE_CUDA) + if(WITH_CUDA_DYNLOAD) + list(APPEND LIB + extern_cuew + ) + else() + list(APPEND LIB + ${CUDA_CUDA_LIBRARY} + ) + endif() endif() -if(WITH_HIP_DYNLOAD) +if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD) list(APPEND LIB extern_hipew ) diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 8c2cb2c68de..7b56216e887 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -542,7 +542,7 @@ if(WITH_CYCLES_HIP_BINARIES) -D WITH_NANOVDB -I "${NANOVDB_INCLUDE_DIR}") endif() - + endmacro() set(prev_arch "none") foreach(arch ${CYCLES_HIP_BINARIES_ARCH}) From 95fca22bfeb1d3e2edeeb71b9717e3528be14c17 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 28 Sep 2021 13:22:52 -0500 Subject: [PATCH 0314/1500] Geometry Nodes: Remove experimental option for fields This enables fields as the official workflow for geometry nodes. While many features are converted to use fields rather than the old attribute workflow, many are not yet converted. In that case, the unconverted nodes are still accessible with an experimental option. In the coming weeks the rest of the nodes will be converted. Differential Revision: https://developer.blender.org/D12672 --- .../scripts/startup/bl_ui/space_userpref.py | 2 +- release/scripts/startup/nodeitems_builtins.py | 129 +++++++++--------- source/blender/makesdna/DNA_userdef_types.h | 2 +- source/blender/makesrna/intern/rna_userdef.c | 7 +- source/blender/modifiers/intern/MOD_nodes.cc | 5 +- .../modifiers/intern/MOD_nodes_evaluator.cc | 22 ++- 6 files changed, 80 insertions(+), 87 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index ef1c7a77a2b..6efac235359 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2251,7 +2251,6 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): ({"property": "use_sculpt_tools_tilt"}, "T82877"), ({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")), ({"property": "use_override_templates"}, ("T73318", "Milestone 4")), - ({"property": "use_geometry_nodes_fields"}, "T91274"), ), ) @@ -2285,6 +2284,7 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): ({"property": "override_auto_resync"}, "T83811"), ({"property": "proxy_to_override_auto_conversion"}, "T91671"), ({"property": "use_cycles_debug"}, None), + ({"property": "use_geometry_nodes_legacy"}, "T91274"), ), ) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index d1f1cfef682..3a7a2d62a0e 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -180,11 +180,8 @@ def object_eevee_cycles_shader_nodes_poll(context): eevee_cycles_shader_nodes_poll(context)) -def geometry_nodes_fields_poll(context): - return context.preferences.experimental.use_geometry_nodes_fields - -def geometry_nodes_fields_legacy_poll(context): - return not context.preferences.experimental.use_geometry_nodes_fields +def geometry_nodes_legacy_poll(context): + return context.preferences.experimental.use_geometry_nodes_legacy # All standard node categories currently used in nodes. @@ -483,27 +480,27 @@ texture_node_categories = [ geometry_node_categories = [ # Geometry Nodes GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ - NodeItem("GeometryNodeLegacyAttributeRandomize", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeMath", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeClamp", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeCompare", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeConvert", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeCurveMap", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeFill", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeMix", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeProximity", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeColorRamp", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeVectorMath", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeVectorRotate", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeSampleTexture", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeCombineXYZ", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeSeparateXYZ", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeMapRange", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeRandomize", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeMath", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeClamp", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeCompare", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeConvert", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeCurveMap", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeFill", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeMix", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeProximity", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeColorRamp", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeVectorMath", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeVectorRotate", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeSampleTexture", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeCombineXYZ", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeSeparateXYZ", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeMapRange", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeAttributeCapture", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeAttributeStatistic", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeAttributeCapture"), + NodeItem("GeometryNodeAttributeStatistic"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ NodeItem("ShaderNodeMixRGB"), @@ -513,24 +510,24 @@ geometry_node_categories = [ NodeItem("ShaderNodeCombineRGB"), ]), GeometryNodeCategory("GEO_CURVE", "Curve", items=[ - NodeItem("GeometryNodeLegacyCurveSubdivide", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyCurveReverse", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyCurveSplineType", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyCurveSetHandles", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyCurveSelectHandles", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyMeshToCurve", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyCurveToPoints", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyCurveEndpoints", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyCurveSubdivide", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyCurveReverse", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyCurveSplineType", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyCurveSetHandles", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyCurveSelectHandles", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyMeshToCurve", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyCurveToPoints", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyCurveEndpoints", poll=geometry_nodes_legacy_poll), NodeItem("GeometryNodeCurveToMesh"), NodeItem("GeometryNodeCurveResample"), NodeItem("GeometryNodeCurveFill"), NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), - NodeItem("GeometryNodeCurveParameter", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeInputTangent", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeCurveSample", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeCurveFillet", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeCurveParameter"), + NodeItem("GeometryNodeInputTangent"), + NodeItem("GeometryNodeCurveSample"), + NodeItem("GeometryNodeCurveFillet"), ]), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ NodeItem("GeometryNodeCurvePrimitiveLine"), @@ -542,20 +539,20 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurvePrimitiveBezierSegment"), ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ - NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_legacy_poll), NodeItem("GeometryNodeBoundBox"), NodeItem("GeometryNodeConvexHull"), NodeItem("GeometryNodeTransform"), NodeItem("GeometryNodeJoinGeometry"), NodeItem("GeometryNodeSeparateComponents"), - NodeItem("GeometryNodeSetPosition", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeRealizeInstances", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeSetPosition"), + NodeItem("GeometryNodeRealizeInstances"), ]), GeometryNodeCategory("GEO_INPUT", "Input", items=[ - NodeItem("FunctionNodeLegacyRandomFloat", poll=geometry_nodes_fields_legacy_poll), - + NodeItem("FunctionNodeLegacyRandomFloat", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeObjectInfo"), NodeItem("GeometryNodeCollectionInfo"), NodeItem("ShaderNodeValue"), @@ -563,26 +560,26 @@ geometry_node_categories = [ NodeItem("FunctionNodeInputVector"), NodeItem("GeometryNodeInputMaterial"), NodeItem("GeometryNodeIsViewport"), - NodeItem("GeometryNodeInputPosition", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeInputIndex", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeInputNormal", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeInputPosition"), + NodeItem("GeometryNodeInputIndex"), + NodeItem("GeometryNodeInputNormal"), ]), GeometryNodeCategory("GEO_MATERIAL", "Material", items=[ - NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeMaterialAssign", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeMaterialSelection", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeMaterialAssign"), + NodeItem("GeometryNodeMaterialSelection"), NodeItem("GeometryNodeMaterialReplace"), ]), GeometryNodeCategory("GEO_MESH", "Mesh", items=[ - NodeItem("GeometryNodeLegacyEdgeSplit", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyEdgeSplit", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_legacy_poll), NodeItem("GeometryNodeBoolean"), NodeItem("GeometryNodeTriangulate"), NodeItem("GeometryNodeMeshSubdivide"), - NodeItem("GeometryNodePointsToVertices", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodePointsToVertices"), ]), GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[ NodeItem("GeometryNodeMeshCircle"), @@ -595,16 +592,16 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshUVSphere"), ]), GeometryNodeCategory("GEO_POINT", "Point", items=[ - NodeItem("GeometryNodeMeshToPoints", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeInstanceOnPoints", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeDistributePointsOnFaces", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyPointScale", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeMeshToPoints"), + NodeItem("GeometryNodeInstanceOnPoints"), + NodeItem("GeometryNodeDistributePointsOnFaces"), + NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyPointScale", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_legacy_poll), ]), GeometryNodeCategory("GEO_TEXT", "Text", items=[ NodeItem("FunctionNodeStringLength"), @@ -622,10 +619,10 @@ geometry_node_categories = [ NodeItem("FunctionNodeFloatCompare"), NodeItem("FunctionNodeFloatToInt"), NodeItem("GeometryNodeSwitch"), - NodeItem("FunctionNodeRandomValue", poll=geometry_nodes_fields_poll), + NodeItem("FunctionNodeRandomValue"), ]), GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ - NodeItem("ShaderNodeTexNoise", poll=geometry_nodes_fields_poll), + NodeItem("ShaderNodeTexNoise"), ]), GeometryNodeCategory("GEO_VECTOR", "Vector", items=[ NodeItem("ShaderNodeVectorCurve"), @@ -638,7 +635,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeViewer"), ]), GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ - NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_legacy_poll), NodeItem("GeometryNodeVolumeToMesh"), ]), diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 2399fb324d7..247f67f6b95 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -637,6 +637,7 @@ typedef struct UserDef_Experimental { char no_override_auto_resync; char no_proxy_to_override_conversion; char use_cycles_debug; + char use_geometry_nodes_legacy; char SANITIZE_AFTER_HERE; /* The following options are automatically sanitized (set to 0) * when the release cycle is not alpha. */ @@ -647,7 +648,6 @@ typedef struct UserDef_Experimental { char use_sculpt_tools_tilt; char use_extended_asset_browser; char use_override_templates; - char use_geometry_nodes_fields; char _pad[3]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 16f33507bed..684c331e14e 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6335,9 +6335,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Override Templates", "Enable library override template in the python API"); - prop = RNA_def_property(srna, "use_geometry_nodes_fields", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_fields", 1); - RNA_def_property_ui_text(prop, "Geometry Nodes Fields", "Enable field nodes in geometry nodes"); + prop = RNA_def_property(srna, "use_geometry_nodes_legacy", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_legacy", 1); + RNA_def_property_ui_text( + prop, "Geometry Nodes Legacy", "Enable legacy geometry nodes in the menu"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index f2f9b82480d..d294491d51c 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1146,8 +1146,7 @@ static void draw_property_for_socket(uiLayout *layout, break; } default: { - if (input_has_attribute_toggle(*nmd->node_group, socket_index) && - USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) { + if (input_has_attribute_toggle(*nmd->node_group, socket_index)) { const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + use_attribute_suffix + "\"]"; const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) + @@ -1240,7 +1239,7 @@ static void panel_draw(const bContext *C, Panel *panel) }); } - if (USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields) && has_legacy_node) { + if (has_legacy_node) { uiLayout *row = uiLayoutRow(layout, false); uiItemL(row, IFACE_("Node tree has legacy node"), ICON_ERROR); uiLayout *sub = uiLayoutRow(row, false); diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index d9ea5696bf5..b9a9437d761 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -878,11 +878,9 @@ class GeometryNodesEvaluator { NodeParamsProvider params_provider{*this, node, node_state}; GeoNodeExecParams params{params_provider}; - if (USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) { - if (node->idname().find("Legacy") != StringRef::not_found) { - params.error_message_add(geo_log::NodeWarningType::Legacy, - TIP_("Legacy node will be removed before Blender 4.0")); - } + if (node->idname().find("Legacy") != StringRef::not_found) { + params.error_message_add(geo_log::NodeWarningType::Legacy, + TIP_("Legacy node will be removed before Blender 4.0")); } bnode.typeinfo->geometry_node_execute(params); } @@ -891,14 +889,12 @@ class GeometryNodesEvaluator { const MultiFunction &fn, NodeState &node_state) { - if (USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) { - if (node->idname().find("Legacy") != StringRef::not_found) { - /* Create geometry nodes params just for creating an error message. */ - NodeParamsProvider params_provider{*this, node, node_state}; - GeoNodeExecParams params{params_provider}; - params.error_message_add(geo_log::NodeWarningType::Legacy, - TIP_("Legacy node will be removed before Blender 4.0")); - } + if (node->idname().find("Legacy") != StringRef::not_found) { + /* Create geometry nodes params just for creating an error message. */ + NodeParamsProvider params_provider{*this, node, node_state}; + GeoNodeExecParams params{params_provider}; + params.error_message_add(geo_log::NodeWarningType::Legacy, + TIP_("Legacy node will be removed before Blender 4.0")); } LinearAllocator<> &allocator = local_allocators_.local(); From cc653c9b02b34349e7896b8a4e92a5b0e69c0322 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 28 Sep 2021 20:36:20 +0200 Subject: [PATCH 0315/1500] Fix potential render tests error with invalid utf-8 characters In general should not happen, but better to report the actual error instead of the Python test code failing. --- tests/performance/api/device.py | 2 +- tests/python/modules/render_report.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/performance/api/device.py b/tests/performance/api/device.py index b61ae42be36..1e930a12352 100644 --- a/tests/performance/api/device.py +++ b/tests/performance/api/device.py @@ -11,7 +11,7 @@ def get_cpu_name() -> str: return platform.processor() elif platform.system() == "Darwin": cmd = ['/usr/sbin/sysctl', "-n", "machdep.cpu.brand_string"] - return subprocess.check_output(cmd).strip().decode('utf-8') + return subprocess.check_output(cmd).strip().decode('utf-8', 'ignore') else: with open('/proc/cpuinfo') as f: for line in f: diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py index 560f8e33585..90f16dc80fb 100755 --- a/tests/python/modules/render_report.py +++ b/tests/python/modules/render_report.py @@ -410,7 +410,7 @@ class Report: failed = False except subprocess.CalledProcessError as e: if self.verbose: - print_message(e.output.decode("utf-8")) + print_message(e.output.decode("utf-8", 'ignore')) failed = e.returncode != 1 else: if not self.update: @@ -437,7 +437,7 @@ class Report: subprocess.check_output(command) except subprocess.CalledProcessError as e: if self.verbose: - print_message(e.output.decode("utf-8")) + print_message(e.output.decode("utf-8", 'ignore')) return not failed @@ -488,7 +488,7 @@ class Report: if verbose: print(" ".join(command)) if (verbose or crash) and output: - print(output.decode("utf-8")) + print(output.decode("utf-8", 'ignore')) # Detect missing filepaths and consider those errors for filepath, output_filepath in zip(remaining_filepaths[:], output_filepaths): From 85aac0ef6a08628377cd39a0cb505d21c8e43c90 Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Tue, 28 Sep 2021 13:53:20 -0500 Subject: [PATCH 0316/1500] Geometry Nodes: Field version of curve reverse node The updated version has a selection input as a field and does not realize instances implicitly. Differential Revision: https://developer.blender.org/D12506 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 7 +- .../nodes/legacy/node_geo_curve_reverse.cc | 2 +- .../geometry/nodes/node_geo_curve_reverse.cc | 72 +++++++++++++++++++ 8 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 3a7a2d62a0e..bc78413f4e5 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -528,6 +528,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeInputTangent"), NodeItem("GeometryNodeCurveSample"), NodeItem("GeometryNodeCurveFillet"), + NodeItem("GeometryNodeCurveReverse"), ]), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ NodeItem("GeometryNodeCurvePrimitiveLine"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 70700246d29..8eff70af364 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1505,6 +1505,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_INSTANCE_ON_POINTS 1092 #define GEO_NODE_MESH_TO_POINTS 1093 #define GEO_NODE_POINTS_TO_VERTICES 1094 +#define GEO_NODE_CURVE_REVERSE 1095 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 8a6f1471ddb..b942a9eacc4 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5710,6 +5710,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_attribute_randomize(); register_node_type_geo_legacy_material_assign(); register_node_type_geo_legacy_select_by_material(); + register_node_type_geo_legacy_curve_reverse(); register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_clamp(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index d2ae91ee4bc..559a0cb5002 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -207,6 +207,7 @@ set(SRC geometry/nodes/node_geo_curve_primitive_spiral.cc geometry/nodes/node_geo_curve_primitive_star.cc geometry/nodes/node_geo_curve_resample.cc + geometry/nodes/node_geo_curve_reverse.cc geometry/nodes/node_geo_curve_fillet.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_trim.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 6cbb45f80aa..fdd05bacc76 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -32,6 +32,7 @@ void register_node_type_geo_custom_group(bNodeType *ntype); void register_node_type_geo_legacy_attribute_randomize(void); void register_node_type_geo_legacy_material_assign(void); void register_node_type_geo_legacy_select_by_material(void); +void register_node_type_geo_legacy_curve_reverse(void); void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_clamp(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index d653400361a..ce270572c0f 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -322,8 +322,8 @@ DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Bo DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "") DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Curve Fill", "") +DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Curve Fillet", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") @@ -334,16 +334,17 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_prim DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") -DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Curve Fillet", "") +DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") -DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "") +DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc index 32bcbe2c608..d1c81333c30 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc @@ -60,7 +60,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_curve_reverse() +void register_node_type_geo_legacy_curve_reverse() { static bNodeType ntype; geo_node_type_base( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc new file mode 100644 index 00000000000..b644faabedb --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -0,0 +1,72 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Curve"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Curve"); +} + +static void geo_node_curve_reverse_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Curve"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; + } + + Field selection_field = params.extract_input>("Selection"); + CurveComponent &component = geometry_set.get_component_for_write(); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + CurveEval &curve = *component.get_for_write(); + MutableSpan splines = curve.splines(); + threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + splines[selection[i]]->reverse(); + } + }); + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_reverse() +{ + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_reverse_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; + nodeRegisterType(&ntype); +} From 87e315c2375650acf091eb28a6a4badb1c9e50d2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 28 Sep 2021 14:20:29 -0500 Subject: [PATCH 0317/1500] Cleanup: Sort node types alphabetically --- source/blender/blenkernel/intern/node.cc | 12 ++++++------ source/blender/nodes/CMakeLists.txt | 9 +++++---- source/blender/nodes/NOD_geometry.h | 8 ++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index b942a9eacc4..416dae4c41e 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5713,6 +5713,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_curve_reverse(); register_node_type_geo_align_rotation_to_vector(); + register_node_type_geo_attribute_capture(); register_node_type_geo_attribute_clamp(); register_node_type_geo_attribute_color_ramp(); register_node_type_geo_attribute_combine_xyz(); @@ -5720,7 +5721,6 @@ static void registerGeometryNodes() register_node_type_geo_attribute_convert(); register_node_type_geo_attribute_curve_map(); register_node_type_geo_attribute_fill(); - register_node_type_geo_attribute_capture(); register_node_type_geo_attribute_map_range(); register_node_type_geo_attribute_math(); register_node_type_geo_attribute_mix(); @@ -5735,9 +5735,9 @@ static void registerGeometryNodes() register_node_type_geo_bounding_box(); register_node_type_geo_collection_info(); register_node_type_geo_convex_hull(); - register_node_type_geo_curve_sample(); register_node_type_geo_curve_endpoints(); register_node_type_geo_curve_fill(); + register_node_type_geo_curve_fillet(); register_node_type_geo_curve_length(); register_node_type_geo_curve_parameter(); register_node_type_geo_curve_primitive_bezier_segment(); @@ -5749,26 +5749,27 @@ static void registerGeometryNodes() register_node_type_geo_curve_primitive_star(); register_node_type_geo_curve_resample(); register_node_type_geo_curve_reverse(); + register_node_type_geo_curve_sample(); register_node_type_geo_curve_set_handles(); register_node_type_geo_curve_spline_type(); register_node_type_geo_curve_subdivide(); - register_node_type_geo_curve_fillet(); register_node_type_geo_curve_to_mesh(); register_node_type_geo_curve_to_points(); register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_edge_split(); - register_node_type_geo_instance_on_points(); register_node_type_geo_input_index(); register_node_type_geo_input_material(); register_node_type_geo_input_normal(); register_node_type_geo_input_position(); register_node_type_geo_input_tangent(); + register_node_type_geo_instance_on_points(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); register_node_type_geo_material_assign(); register_node_type_geo_material_replace(); + register_node_type_geo_material_selection(); register_node_type_geo_mesh_primitive_circle(); register_node_type_geo_mesh_primitive_cone(); register_node_type_geo_mesh_primitive_cube(); @@ -5793,10 +5794,9 @@ static void registerGeometryNodes() register_node_type_geo_realize_instances(); register_node_type_geo_sample_texture(); register_node_type_geo_select_by_handle_type(); - register_node_type_geo_string_join(); - register_node_type_geo_material_selection(); register_node_type_geo_separate_components(); register_node_type_geo_set_position(); + register_node_type_geo_string_join(); register_node_type_geo_string_to_curves(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 559a0cb5002..59cfe885886 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -195,8 +195,8 @@ set(SRC geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_convex_hull.cc - geometry/nodes/node_geo_curve_sample.cc geometry/nodes/node_geo_curve_fill.cc + geometry/nodes/node_geo_curve_fillet.cc geometry/nodes/node_geo_curve_length.cc geometry/nodes/node_geo_curve_parameter.cc geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -208,16 +208,16 @@ set(SRC geometry/nodes/node_geo_curve_primitive_star.cc geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc - geometry/nodes/node_geo_curve_fillet.cc + geometry/nodes/node_geo_curve_sample.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_distribute_points_on_faces.cc - geometry/nodes/node_geo_instance_on_points.cc + geometry/nodes/node_geo_input_index.cc geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_normal.cc geometry/nodes/node_geo_input_position.cc geometry/nodes/node_geo_input_tangent.cc - geometry/nodes/node_geo_input_index.cc + geometry/nodes/node_geo_instance_on_points.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc geometry/nodes/node_geo_material_assign.cc @@ -245,6 +245,7 @@ set(SRC geometry/nodes/node_geo_triangulate.cc geometry/nodes/node_geo_viewer.cc geometry/nodes/node_geo_volume_to_mesh.cc + geometry/node_geometry_exec.cc geometry/node_geometry_tree.cc geometry/node_geometry_util.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index fdd05bacc76..7415563803b 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -35,6 +35,7 @@ void register_node_type_geo_legacy_select_by_material(void); void register_node_type_geo_legacy_curve_reverse(void); void register_node_type_geo_align_rotation_to_vector(void); +void register_node_type_geo_attribute_capture(void); void register_node_type_geo_attribute_clamp(void); void register_node_type_geo_attribute_color_ramp(void); void register_node_type_geo_attribute_combine_xyz(void); @@ -42,7 +43,6 @@ void register_node_type_geo_attribute_compare(void); void register_node_type_geo_attribute_convert(void); void register_node_type_geo_attribute_curve_map(void); void register_node_type_geo_attribute_fill(void); -void register_node_type_geo_attribute_capture(void); void register_node_type_geo_attribute_map_range(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_attribute_mix(void); @@ -59,9 +59,9 @@ void register_node_type_geo_collection_info(void); void register_node_type_geo_convex_hull(void); void register_node_type_geo_curve_endpoints(void); void register_node_type_geo_curve_fill(void); +void register_node_type_geo_curve_fillet(void); void register_node_type_geo_curve_length(void); void register_node_type_geo_curve_parameter(void); -void register_node_type_geo_curve_sample(void); void register_node_type_geo_curve_primitive_bezier_segment(void); void register_node_type_geo_curve_primitive_circle(void); void register_node_type_geo_curve_primitive_line(void); @@ -71,22 +71,22 @@ void register_node_type_geo_curve_primitive_spiral(void); void register_node_type_geo_curve_primitive_star(void); void register_node_type_geo_curve_resample(void); void register_node_type_geo_curve_reverse(void); +void register_node_type_geo_curve_sample(void); void register_node_type_geo_curve_set_handles(void); void register_node_type_geo_curve_spline_type(void); void register_node_type_geo_curve_subdivide(void); -void register_node_type_geo_curve_fillet(void); void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_edge_split(void); -void register_node_type_geo_instance_on_points(void); void register_node_type_geo_input_index(void); void register_node_type_geo_input_material(void); void register_node_type_geo_input_normal(void); void register_node_type_geo_input_position(void); void register_node_type_geo_input_tangent(void); +void register_node_type_geo_instance_on_points(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); void register_node_type_geo_material_assign(void); From c7a7c3f5e5ddd5d8919da84e75c1a277ba0b6de9 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Tue, 28 Sep 2021 15:29:16 -0400 Subject: [PATCH 0318/1500] Cleanup: convert compositor nodes to c++ - Many cleanups of to use list base - Some variable changes These change is needed to migrate to the new socket builder API Reviewed By: manzanilla Differential Revision: https://developer.blender.org/D12366 --- source/blender/nodes/CMakeLists.txt | 172 +++++++++--------- source/blender/nodes/NOD_composite.h | 2 +- ...omposite_tree.c => node_composite_tree.cc} | 76 ++++---- ...omposite_util.c => node_composite_util.cc} | 7 +- ...omposite_util.h => node_composite_util.hh} | 10 +- ...lphaOver.c => node_composite_alphaOver.cc} | 2 +- ...asing.c => node_composite_antialiasing.cc} | 5 +- ...blur.c => node_composite_bilateralblur.cc} | 6 +- ...omposite_blur.c => node_composite_blur.cc} | 4 +- ...okehblur.c => node_composite_bokehblur.cc} | 2 +- ...ehimage.c => node_composite_bokehimage.cc} | 6 +- ...te_boxmask.c => node_composite_boxmask.cc} | 4 +- ...ghtness.c => node_composite_brightness.cc} | 2 +- ...Matte.c => node_composite_channelMatte.cc} | 4 +- ...aMatte.c => node_composite_chromaMatte.cc} | 4 +- ...orMatte.c => node_composite_colorMatte.cc} | 4 +- ...orSpill.c => node_composite_colorSpill.cc} | 4 +- ...lance.c => node_composite_colorbalance.cc} | 16 +- ...on.c => node_composite_colorcorrection.cc} | 7 +- ...site_common.c => node_composite_common.cc} | 12 +- ...omposite.c => node_composite_composite.cc} | 6 +- ...ornerpin.c => node_composite_cornerpin.cc} | 2 +- ...omposite_crop.c => node_composite_crop.cc} | 4 +- .../nodes/node_composite_cryptomatte.cc | 2 +- ...site_curves.c => node_composite_curves.cc} | 4 +- ...te_defocus.c => node_composite_defocus.cc} | 6 +- ...te_denoise.c => node_composite_denoise.cc} | 4 +- ...especkle.c => node_composite_despeckle.cc} | 2 +- ...iffMatte.c => node_composite_diffMatte.cc} | 4 +- ...site_dilate.c => node_composite_dilate.cc} | 5 +- ...ur.c => node_composite_directionalblur.cc} | 4 +- ..._displace.c => node_composite_displace.cc} | 2 +- ...atte.c => node_composite_distanceMatte.cc} | 4 +- ...ask.c => node_composite_doubleEdgeMask.cc} | 2 +- ...semask.c => node_composite_ellipsemask.cc} | 5 +- ..._exposure.c => node_composite_exposure.cc} | 2 +- ...site_filter.c => node_composite_filter.cc} | 2 +- ...omposite_flip.c => node_composite_flip.cc} | 2 +- ...posite_gamma.c => node_composite_gamma.cc} | 2 +- ...posite_glare.c => node_composite_glare.cc} | 4 +- ...ueSatVal.c => node_composite_hueSatVal.cc} | 2 +- ...correct.c => node_composite_huecorrect.cc} | 9 +- ...site_idMask.c => node_composite_idMask.cc} | 2 +- ...posite_image.c => node_composite_image.cc} | 105 ++++++----- ...te_inpaint.c => node_composite_inpaint.cc} | 2 +- ...site_invert.c => node_composite_invert.cc} | 2 +- ...site_keying.c => node_composite_keying.cc} | 7 +- ...creen.c => node_composite_keyingscreen.cc} | 10 +- ..._lensdist.c => node_composite_lensdist.cc} | 4 +- ...site_levels.c => node_composite_levels.cc} | 2 +- ...maMatte.c => node_composite_lummaMatte.cc} | 4 +- ..._mapRange.c => node_composite_mapRange.cc} | 2 +- ...posite_mapUV.c => node_composite_mapUV.cc} | 2 +- ..._mapValue.c => node_composite_mapValue.cc} | 2 +- ...omposite_mask.c => node_composite_mask.cc} | 8 +- ...omposite_math.c => node_composite_math.cc} | 2 +- ...site_mixrgb.c => node_composite_mixrgb.cc} | 2 +- ...ovieclip.c => node_composite_movieclip.cc} | 9 +- ...on.c => node_composite_moviedistortion.cc} | 12 +- ...site_normal.c => node_composite_normal.cc} | 2 +- ...ormalize.c => node_composite_normalize.cc} | 2 +- ...putFile.c => node_composite_outputFile.cc} | 81 ++++----- ..._pixelate.c => node_composite_pixelate.cc} | 2 +- ...m.c => node_composite_planetrackdeform.cc} | 6 +- ...osterize.c => node_composite_posterize.cc} | 2 +- ...remulkey.c => node_composite_premulkey.cc} | 2 +- ..._composite_rgb.c => node_composite_rgb.cc} | 4 +- ...site_rotate.c => node_composite_rotate.cc} | 2 +- ...posite_scale.c => node_composite_scale.cc} | 4 +- ...mbHSVA.c => node_composite_sepcombHSVA.cc} | 2 +- ...mbRGBA.c => node_composite_sepcombRGBA.cc} | 2 +- ...mbYCCA.c => node_composite_sepcombYCCA.cc} | 2 +- ...mbYUVA.c => node_composite_sepcombYUVA.cc} | 2 +- ..._setalpha.c => node_composite_setalpha.cc} | 4 +- ...Viewer.c => node_composite_splitViewer.cc} | 8 +- ...lize2d.c => node_composite_stabilize2d.cc} | 4 +- ..._sunbeams.c => node_composite_sunbeams.cc} | 5 +- ...site_switch.c => node_composite_switch.cc} | 2 +- ...tchview.c => node_composite_switchview.cc} | 37 ++-- ...te_texture.c => node_composite_texture.cc} | 2 +- ...te_tonemap.c => node_composite_tonemap.cc} | 4 +- ..._trackpos.c => node_composite_trackpos.cc} | 7 +- ...ransform.c => node_composite_transform.cc} | 2 +- ...ranslate.c => node_composite_translate.cc} | 5 +- ..._valToRgb.c => node_composite_valToRgb.cc} | 2 +- ...posite_value.c => node_composite_value.cc} | 4 +- ...te_vecBlur.c => node_composite_vecBlur.cc} | 4 +- ...site_viewer.c => node_composite_viewer.cc} | 8 +- ..._zcombine.c => node_composite_zcombine.cc} | 2 +- .../windowmanager/intern/wm_init_exit.c | 7 - 90 files changed, 397 insertions(+), 430 deletions(-) rename source/blender/nodes/composite/{node_composite_tree.c => node_composite_tree.cc} (85%) rename source/blender/nodes/composite/{node_composite_util.c => node_composite_util.cc} (92%) rename source/blender/nodes/composite/{node_composite_util.h => node_composite_util.hh} (92%) rename source/blender/nodes/composite/nodes/{node_composite_alphaOver.c => node_composite_alphaOver.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_antialiasing.c => node_composite_antialiasing.cc} (89%) rename source/blender/nodes/composite/nodes/{node_composite_bilateralblur.c => node_composite_bilateralblur.cc} (88%) rename source/blender/nodes/composite/nodes/{node_composite_blur.c => node_composite_blur.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_bokehblur.c => node_composite_bokehblur.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_bokehimage.c => node_composite_bokehimage.cc} (88%) rename source/blender/nodes/composite/nodes/{node_composite_boxmask.c => node_composite_boxmask.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_brightness.c => node_composite_brightness.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_channelMatte.c => node_composite_channelMatte.cc} (94%) rename source/blender/nodes/composite/nodes/{node_composite_chromaMatte.c => node_composite_chromaMatte.cc} (94%) rename source/blender/nodes/composite/nodes/{node_composite_colorMatte.c => node_composite_colorMatte.cc} (94%) rename source/blender/nodes/composite/nodes/{node_composite_colorSpill.c => node_composite_colorSpill.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_colorbalance.c => node_composite_colorbalance.cc} (88%) rename source/blender/nodes/composite/nodes/{node_composite_colorcorrection.c => node_composite_colorcorrection.cc} (91%) rename source/blender/nodes/composite/nodes/{node_composite_common.c => node_composite_common.cc} (89%) rename source/blender/nodes/composite/nodes/{node_composite_composite.c => node_composite_composite.cc} (90%) rename source/blender/nodes/composite/nodes/{node_composite_cornerpin.c => node_composite_cornerpin.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_crop.c => node_composite_crop.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_curves.c => node_composite_curves.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_defocus.c => node_composite_defocus.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_denoise.c => node_composite_denoise.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_despeckle.c => node_composite_despeckle.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_diffMatte.c => node_composite_diffMatte.cc} (94%) rename source/blender/nodes/composite/nodes/{node_composite_dilate.c => node_composite_dilate.cc} (90%) rename source/blender/nodes/composite/nodes/{node_composite_directionalblur.c => node_composite_directionalblur.cc} (92%) rename source/blender/nodes/composite/nodes/{node_composite_displace.c => node_composite_displace.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_distanceMatte.c => node_composite_distanceMatte.cc} (94%) rename source/blender/nodes/composite/nodes/{node_composite_doubleEdgeMask.c => node_composite_doubleEdgeMask.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_ellipsemask.c => node_composite_ellipsemask.cc} (90%) rename source/blender/nodes/composite/nodes/{node_composite_exposure.c => node_composite_exposure.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_filter.c => node_composite_filter.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_flip.c => node_composite_flip.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_gamma.c => node_composite_gamma.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_glare.c => node_composite_glare.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_hueSatVal.c => node_composite_hueSatVal.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_huecorrect.c => node_composite_huecorrect.cc} (91%) rename source/blender/nodes/composite/nodes/{node_composite_idMask.c => node_composite_idMask.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_image.c => node_composite_image.cc} (85%) rename source/blender/nodes/composite/nodes/{node_composite_inpaint.c => node_composite_inpaint.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_invert.c => node_composite_invert.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_keying.c => node_composite_keying.cc} (94%) rename source/blender/nodes/composite/nodes/{node_composite_keyingscreen.c => node_composite_keyingscreen.cc} (84%) rename source/blender/nodes/composite/nodes/{node_composite_lensdist.c => node_composite_lensdist.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_levels.c => node_composite_levels.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_lummaMatte.c => node_composite_lummaMatte.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_mapRange.c => node_composite_mapRange.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_mapUV.c => node_composite_mapUV.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_mapValue.c => node_composite_mapValue.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_mask.c => node_composite_mask.cc} (89%) rename source/blender/nodes/composite/nodes/{node_composite_math.c => node_composite_math.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_mixrgb.c => node_composite_mixrgb.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_movieclip.c => node_composite_movieclip.cc} (85%) rename source/blender/nodes/composite/nodes/{node_composite_moviedistortion.c => node_composite_moviedistortion.cc} (87%) rename source/blender/nodes/composite/nodes/{node_composite_normal.c => node_composite_normal.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_normalize.c => node_composite_normalize.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_outputFile.c => node_composite_outputFile.cc} (76%) rename source/blender/nodes/composite/nodes/{node_composite_pixelate.c => node_composite_pixelate.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_planetrackdeform.c => node_composite_planetrackdeform.cc} (90%) rename source/blender/nodes/composite/nodes/{node_composite_posterize.c => node_composite_posterize.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_premulkey.c => node_composite_premulkey.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_rgb.c => node_composite_rgb.cc} (92%) rename source/blender/nodes/composite/nodes/{node_composite_rotate.c => node_composite_rotate.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_scale.c => node_composite_scale.cc} (94%) rename source/blender/nodes/composite/nodes/{node_composite_sepcombHSVA.c => node_composite_sepcombHSVA.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_sepcombRGBA.c => node_composite_sepcombRGBA.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_sepcombYCCA.c => node_composite_sepcombYCCA.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_sepcombYUVA.c => node_composite_sepcombYUVA.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_setalpha.c => node_composite_setalpha.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_splitViewer.c => node_composite_splitViewer.cc} (88%) rename source/blender/nodes/composite/nodes/{node_composite_stabilize2d.c => node_composite_stabilize2d.cc} (96%) rename source/blender/nodes/composite/nodes/{node_composite_sunbeams.c => node_composite_sunbeams.cc} (92%) rename source/blender/nodes/composite/nodes/{node_composite_switch.c => node_composite_switch.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_switchview.c => node_composite_switchview.cc} (78%) rename source/blender/nodes/composite/nodes/{node_composite_texture.c => node_composite_texture.cc} (97%) rename source/blender/nodes/composite/nodes/{node_composite_tonemap.c => node_composite_tonemap.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_trackpos.c => node_composite_trackpos.cc} (84%) rename source/blender/nodes/composite/nodes/{node_composite_transform.c => node_composite_transform.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_translate.c => node_composite_translate.cc} (89%) rename source/blender/nodes/composite/nodes/{node_composite_valToRgb.c => node_composite_valToRgb.cc} (98%) rename source/blender/nodes/composite/nodes/{node_composite_value.c => node_composite_value.cc} (92%) rename source/blender/nodes/composite/nodes/{node_composite_vecBlur.c => node_composite_vecBlur.cc} (93%) rename source/blender/nodes/composite/nodes/{node_composite_viewer.c => node_composite_viewer.cc} (88%) rename source/blender/nodes/composite/nodes/{node_composite_zcombine.c => node_composite_zcombine.cc} (98%) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 59cfe885886..49e76ffd1ad 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -45,93 +45,93 @@ set(INC set(SRC - composite/nodes/node_composite_alphaOver.c - composite/nodes/node_composite_antialiasing.c - composite/nodes/node_composite_bilateralblur.c - composite/nodes/node_composite_blur.c - composite/nodes/node_composite_bokehblur.c - composite/nodes/node_composite_bokehimage.c - composite/nodes/node_composite_boxmask.c - composite/nodes/node_composite_brightness.c - composite/nodes/node_composite_channelMatte.c - composite/nodes/node_composite_chromaMatte.c - composite/nodes/node_composite_colorMatte.c - composite/nodes/node_composite_colorSpill.c - composite/nodes/node_composite_colorbalance.c - composite/nodes/node_composite_colorcorrection.c - composite/nodes/node_composite_common.c - composite/nodes/node_composite_composite.c - composite/nodes/node_composite_cornerpin.c - composite/nodes/node_composite_crop.c + composite/nodes/node_composite_alphaOver.cc + composite/nodes/node_composite_antialiasing.cc + composite/nodes/node_composite_bilateralblur.cc + composite/nodes/node_composite_blur.cc + composite/nodes/node_composite_bokehblur.cc + composite/nodes/node_composite_bokehimage.cc + composite/nodes/node_composite_boxmask.cc + composite/nodes/node_composite_brightness.cc + composite/nodes/node_composite_channelMatte.cc + composite/nodes/node_composite_chromaMatte.cc + composite/nodes/node_composite_colorMatte.cc + composite/nodes/node_composite_colorSpill.cc + composite/nodes/node_composite_colorbalance.cc + composite/nodes/node_composite_colorcorrection.cc + composite/nodes/node_composite_common.cc + composite/nodes/node_composite_composite.cc + composite/nodes/node_composite_cornerpin.cc + composite/nodes/node_composite_crop.cc composite/nodes/node_composite_cryptomatte.cc - composite/nodes/node_composite_curves.c - composite/nodes/node_composite_defocus.c - composite/nodes/node_composite_denoise.c - composite/nodes/node_composite_despeckle.c - composite/nodes/node_composite_diffMatte.c - composite/nodes/node_composite_dilate.c - composite/nodes/node_composite_directionalblur.c - composite/nodes/node_composite_displace.c - composite/nodes/node_composite_distanceMatte.c - composite/nodes/node_composite_doubleEdgeMask.c - composite/nodes/node_composite_ellipsemask.c - composite/nodes/node_composite_exposure.c - composite/nodes/node_composite_filter.c - composite/nodes/node_composite_flip.c - composite/nodes/node_composite_gamma.c - composite/nodes/node_composite_glare.c - composite/nodes/node_composite_hueSatVal.c - composite/nodes/node_composite_huecorrect.c - composite/nodes/node_composite_idMask.c - composite/nodes/node_composite_image.c - composite/nodes/node_composite_inpaint.c - composite/nodes/node_composite_invert.c - composite/nodes/node_composite_keying.c - composite/nodes/node_composite_keyingscreen.c - composite/nodes/node_composite_lensdist.c - composite/nodes/node_composite_levels.c - composite/nodes/node_composite_lummaMatte.c - composite/nodes/node_composite_mapRange.c - composite/nodes/node_composite_mapUV.c - composite/nodes/node_composite_mapValue.c - composite/nodes/node_composite_mask.c - composite/nodes/node_composite_math.c - composite/nodes/node_composite_mixrgb.c - composite/nodes/node_composite_movieclip.c - composite/nodes/node_composite_moviedistortion.c - composite/nodes/node_composite_normal.c - composite/nodes/node_composite_normalize.c - composite/nodes/node_composite_outputFile.c - composite/nodes/node_composite_pixelate.c - composite/nodes/node_composite_planetrackdeform.c - composite/nodes/node_composite_posterize.c - composite/nodes/node_composite_premulkey.c - composite/nodes/node_composite_rgb.c - composite/nodes/node_composite_rotate.c - composite/nodes/node_composite_scale.c - composite/nodes/node_composite_sepcombHSVA.c - composite/nodes/node_composite_sepcombRGBA.c - composite/nodes/node_composite_sepcombYCCA.c - composite/nodes/node_composite_sepcombYUVA.c - composite/nodes/node_composite_setalpha.c - composite/nodes/node_composite_splitViewer.c - composite/nodes/node_composite_stabilize2d.c - composite/nodes/node_composite_sunbeams.c - composite/nodes/node_composite_switch.c - composite/nodes/node_composite_switchview.c - composite/nodes/node_composite_texture.c - composite/nodes/node_composite_tonemap.c - composite/nodes/node_composite_trackpos.c - composite/nodes/node_composite_transform.c - composite/nodes/node_composite_translate.c - composite/nodes/node_composite_valToRgb.c - composite/nodes/node_composite_value.c - composite/nodes/node_composite_vecBlur.c - composite/nodes/node_composite_viewer.c - composite/nodes/node_composite_zcombine.c + composite/nodes/node_composite_curves.cc + composite/nodes/node_composite_defocus.cc + composite/nodes/node_composite_denoise.cc + composite/nodes/node_composite_despeckle.cc + composite/nodes/node_composite_diffMatte.cc + composite/nodes/node_composite_dilate.cc + composite/nodes/node_composite_directionalblur.cc + composite/nodes/node_composite_displace.cc + composite/nodes/node_composite_distanceMatte.cc + composite/nodes/node_composite_doubleEdgeMask.cc + composite/nodes/node_composite_ellipsemask.cc + composite/nodes/node_composite_exposure.cc + composite/nodes/node_composite_filter.cc + composite/nodes/node_composite_flip.cc + composite/nodes/node_composite_gamma.cc + composite/nodes/node_composite_glare.cc + composite/nodes/node_composite_hueSatVal.cc + composite/nodes/node_composite_huecorrect.cc + composite/nodes/node_composite_idMask.cc + composite/nodes/node_composite_image.cc + composite/nodes/node_composite_inpaint.cc + composite/nodes/node_composite_invert.cc + composite/nodes/node_composite_keying.cc + composite/nodes/node_composite_keyingscreen.cc + composite/nodes/node_composite_lensdist.cc + composite/nodes/node_composite_levels.cc + composite/nodes/node_composite_lummaMatte.cc + composite/nodes/node_composite_mapRange.cc + composite/nodes/node_composite_mapUV.cc + composite/nodes/node_composite_mapValue.cc + composite/nodes/node_composite_mask.cc + composite/nodes/node_composite_math.cc + composite/nodes/node_composite_mixrgb.cc + composite/nodes/node_composite_movieclip.cc + composite/nodes/node_composite_moviedistortion.cc + composite/nodes/node_composite_normal.cc + composite/nodes/node_composite_normalize.cc + composite/nodes/node_composite_outputFile.cc + composite/nodes/node_composite_pixelate.cc + composite/nodes/node_composite_planetrackdeform.cc + composite/nodes/node_composite_posterize.cc + composite/nodes/node_composite_premulkey.cc + composite/nodes/node_composite_rgb.cc + composite/nodes/node_composite_rotate.cc + composite/nodes/node_composite_scale.cc + composite/nodes/node_composite_sepcombHSVA.cc + composite/nodes/node_composite_sepcombRGBA.cc + composite/nodes/node_composite_sepcombYCCA.cc + composite/nodes/node_composite_sepcombYUVA.cc + composite/nodes/node_composite_setalpha.cc + composite/nodes/node_composite_splitViewer.cc + composite/nodes/node_composite_stabilize2d.cc + composite/nodes/node_composite_sunbeams.cc + composite/nodes/node_composite_switch.cc + composite/nodes/node_composite_switchview.cc + composite/nodes/node_composite_texture.cc + composite/nodes/node_composite_tonemap.cc + composite/nodes/node_composite_trackpos.cc + composite/nodes/node_composite_transform.cc + composite/nodes/node_composite_translate.cc + composite/nodes/node_composite_valToRgb.cc + composite/nodes/node_composite_value.cc + composite/nodes/node_composite_vecBlur.cc + composite/nodes/node_composite_viewer.cc + composite/nodes/node_composite_zcombine.cc - composite/node_composite_tree.c - composite/node_composite_util.c + composite/node_composite_tree.cc + composite/node_composite_util.cc function/nodes/legacy/node_fn_random_float.cc @@ -383,7 +383,7 @@ set(SRC intern/node_util.c intern/type_conversions.cc - composite/node_composite_util.h + composite/node_composite_util.hh function/node_function_util.hh shader/node_shader_util.h geometry/node_geometry_util.hh diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 2cbbd31c97a..d243577f68d 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -145,7 +145,7 @@ void node_cmp_rlayers_register_pass(struct bNodeTree *ntree, struct Scene *scene, struct ViewLayer *view_layer, const char *name, - int type); + eNodeSocketDatatype type); const char *node_cmp_rlayers_sock_to_pass(int sock_index); void register_node_type_cmp_custom_group(bNodeType *ntype); diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.cc similarity index 85% rename from source/blender/nodes/composite/node_composite_tree.c rename to source/blender/nodes/composite/node_composite_tree.cc index cc657d6f91d..d695096903f 100644 --- a/source/blender/nodes/composite/node_composite_tree.c +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -21,7 +21,7 @@ * \ingroup nodes */ -#include +#include #include "DNA_color_types.h" #include "DNA_node_types.h" @@ -41,7 +41,7 @@ #include "RNA_access.h" #include "NOD_composite.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" #ifdef WITH_COMPOSITOR # include "COM_compositor.h" @@ -55,7 +55,7 @@ static void composite_get_from_context(const bContext *C, { Scene *scene = CTX_data_scene(C); - *r_from = NULL; + *r_from = nullptr; *r_id = &scene->id; *r_ntree = scene->nodetree; } @@ -77,19 +77,16 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa static void free_node_cache(bNodeTree *UNUSED(ntree), bNode *node) { - bNodeSocket *sock; - - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (sock->cache) { - sock->cache = NULL; + sock->cache = nullptr; } } } static void free_cache(bNodeTree *ntree) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { free_node_cache(ntree, node); } } @@ -98,9 +95,9 @@ static void free_cache(bNodeTree *ntree) static void localize(bNodeTree *localtree, bNodeTree *ntree) { - bNode *node = ntree->nodes.first; - bNode *local_node = localtree->nodes.first; - while (node != NULL) { + bNode *node = (bNode *)ntree->nodes.first; + bNode *local_node = (bNode *)localtree->nodes.first; + while (node != nullptr) { /* Ensure new user input gets handled ok. */ node->need_exec = 0; @@ -115,16 +112,16 @@ static void localize(bNodeTree *localtree, bNodeTree *ntree) local_node->id = (ID *)node->id; } else { - local_node->id = NULL; + local_node->id = nullptr; } } } - bNodeSocket *output_sock = node->outputs.first; - bNodeSocket *local_output_sock = local_node->outputs.first; - while (output_sock != NULL) { + bNodeSocket *output_sock = (bNodeSocket *)node->outputs.first; + bNodeSocket *local_output_sock = (bNodeSocket *)local_node->outputs.first; + while (output_sock != nullptr) { local_output_sock->cache = output_sock->cache; - output_sock->cache = NULL; + output_sock->cache = nullptr; /* This is actually link to original: someone was just lazy enough and tried to save few * bytes in the cost of readability. */ local_output_sock->new_sock = output_sock; @@ -151,7 +148,7 @@ static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree) /* move over the compbufs and previews */ BKE_node_preview_merge_tree(ntree, localtree, true); - for (lnode = localtree->nodes.first; lnode; lnode = lnode->next) { + for (lnode = (bNode *)localtree->nodes.first; lnode; lnode = lnode->next) { if (ntreeNodeExists(ntree, lnode->new_node)) { if (ELEM(lnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { if (lnode->id && (lnode->flag & NODE_DO_OUTPUT)) { @@ -165,18 +162,19 @@ static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree) * copied back to original node */ if (lnode->storage) { if (lnode->new_node->storage) { - BKE_tracking_distortion_free(lnode->new_node->storage); + BKE_tracking_distortion_free((MovieDistortion *)lnode->new_node->storage); } - lnode->new_node->storage = BKE_tracking_distortion_copy(lnode->storage); + lnode->new_node->storage = BKE_tracking_distortion_copy( + (MovieDistortion *)lnode->storage); } } - for (lsock = lnode->outputs.first; lsock; lsock = lsock->next) { + for (lsock = (bNodeSocket *)lnode->outputs.first; lsock; lsock = lsock->next) { if (ntreeOutputExists(lnode->new_node, lsock->new_sock)) { lsock->new_sock->cache = lsock->cache; - lsock->cache = NULL; - lsock->new_sock = NULL; + lsock->cache = nullptr; + lsock->new_sock = nullptr; } } } @@ -216,8 +214,8 @@ bNodeTreeType *ntreeType_Composite; void register_node_tree_type_cmp(void) { - bNodeTreeType *tt = ntreeType_Composite = MEM_callocN(sizeof(bNodeTreeType), - "compositor node tree type"); + bNodeTreeType *tt = ntreeType_Composite = (bNodeTreeType *)MEM_callocN( + sizeof(bNodeTreeType), "compositor node tree type"); tt->type = NTREE_COMPOSIT; strcpy(tt->idname, "CompositorNodeTree"); @@ -241,9 +239,6 @@ void register_node_tree_type_cmp(void) ntreeTypeAdd(tt); } -extern void *COM_linker_hack; /* Quiet warning. */ -void *COM_linker_hack = NULL; - void ntreeCompositExecTree(Scene *scene, bNodeTree *ntree, RenderData *rd, @@ -276,13 +271,11 @@ void ntreeCompositExecTree(Scene *scene, */ void ntreeCompositUpdateRLayers(bNodeTree *ntree) { - bNode *node; - - if (ntree == NULL) { + if (ntree == nullptr) { return; } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == CMP_NODE_R_LAYERS) { node_cmp_rlayers_outputs(ntree, node); } @@ -295,13 +288,11 @@ void ntreeCompositRegisterPass(bNodeTree *ntree, const char *name, eNodeSocketDatatype type) { - bNode *node; - - if (ntree == NULL) { + if (ntree == nullptr) { return; } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == CMP_NODE_R_LAYERS) { node_cmp_rlayers_register_pass(ntree, node, scene, view_layer, name, type); } @@ -317,11 +308,10 @@ void ntreeCompositTagRender(Scene *scene) * This is still rather weak though, * ideally render struct would store own main AND original G_MAIN. */ - for (Scene *sce_iter = G_MAIN->scenes.first; sce_iter; sce_iter = sce_iter->id.next) { + for (Scene *sce_iter = (Scene *)G_MAIN->scenes.first; sce_iter; + sce_iter = (Scene *)sce_iter->id.next) { if (sce_iter->nodetree) { - bNode *node; - - for (node = sce_iter->nodetree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &sce_iter->nodetree->nodes) { if (node->id == (ID *)scene || node->type == CMP_NODE_COMPOSITE) { nodeUpdate(sce_iter->nodetree, node); } @@ -336,13 +326,11 @@ void ntreeCompositTagRender(Scene *scene) /* XXX after render animation system gets a refresh, this call allows composite to end clean */ void ntreeCompositClearTags(bNodeTree *ntree) { - bNode *node; - - if (ntree == NULL) { + if (ntree == nullptr) { return; } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node->need_exec = 0; if (node->type == NODE_GROUP) { ntreeCompositClearTags((bNodeTree *)node->id); diff --git a/source/blender/nodes/composite/node_composite_util.c b/source/blender/nodes/composite/node_composite_util.cc similarity index 92% rename from source/blender/nodes/composite/node_composite_util.c rename to source/blender/nodes/composite/node_composite_util.cc index 6cc17d8c272..86aaec61bc3 100644 --- a/source/blender/nodes/composite/node_composite_util.c +++ b/source/blender/nodes/composite/node_composite_util.cc @@ -21,7 +21,7 @@ * \ingroup nodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" bool cmp_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, @@ -36,11 +36,10 @@ bool cmp_node_poll_default(bNodeType *UNUSED(ntype), void cmp_node_update_default(bNodeTree *UNUSED(ntree), bNode *node) { - bNodeSocket *sock; - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (sock->cache) { // free_compbuf(sock->cache); - // sock->cache = NULL; + // sock->cache = nullptr; } } node->need_exec = 1; diff --git a/source/blender/nodes/composite/node_composite_util.h b/source/blender/nodes/composite/node_composite_util.hh similarity index 92% rename from source/blender/nodes/composite/node_composite_util.h rename to source/blender/nodes/composite/node_composite_util.hh index 4fcccbb79f0..62ef9570358 100644 --- a/source/blender/nodes/composite/node_composite_util.h +++ b/source/blender/nodes/composite/node_composite_util.hh @@ -48,19 +48,11 @@ /* only for forward declarations */ #include "NOD_composite.h" -#ifdef __cplusplus -extern "C" { -#endif - #define CMP_SCALE_MAX 12000 bool cmp_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree, - const char **r_disabled_info); + const char **r_disabled_hint); void cmp_node_update_default(struct bNodeTree *ntree, struct bNode *node); void cmp_node_type_base( struct bNodeType *ntype, int type, const char *name, short nclass, short flag); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/nodes/composite/nodes/node_composite_alphaOver.c b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_alphaOver.c rename to source/blender/nodes/composite/nodes/node_composite_alphaOver.cc index 7a08bd51575..156faed8524 100644 --- a/source/blender/nodes/composite/nodes/node_composite_alphaOver.c +++ b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** ALPHAOVER ******************** */ static bNodeSocketTemplate cmp_node_alphaover_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc similarity index 89% rename from source/blender/nodes/composite/nodes/node_composite_antialiasing.c rename to source/blender/nodes/composite/nodes/node_composite_antialiasing.cc index a5906c31093..23e63b9a53b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c +++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc @@ -23,7 +23,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Anti-Aliasing (SMAA 1x) ******************** */ @@ -34,7 +34,8 @@ static bNodeSocketTemplate cmp_node_antialiasing_out[] = {{SOCK_RGBA, N_("Image" static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAntiAliasingData *data = MEM_callocN(sizeof(NodeAntiAliasingData), "node antialiasing data"); + NodeAntiAliasingData *data = (NodeAntiAliasingData *)MEM_callocN(sizeof(NodeAntiAliasingData), + "node antialiasing data"); data->threshold = CMP_DEFAULT_SMAA_THRESHOLD; data->contrast_limit = CMP_DEFAULT_SMAA_CONTRAST_LIMIT; diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.c b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc similarity index 88% rename from source/blender/nodes/composite/nodes/node_composite_bilateralblur.c rename to source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc index 270a137280c..3e724d17a10 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.c +++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** BILATERALBLUR ******************** */ static bNodeSocketTemplate cmp_node_bilateralblur_in[] = { @@ -36,8 +36,8 @@ static bNodeSocketTemplate cmp_node_bilateralblur_out[] = { static void node_composit_init_bilateralblur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBilateralBlurData *nbbd = MEM_callocN(sizeof(NodeBilateralBlurData), - "node bilateral blur data"); + NodeBilateralBlurData *nbbd = (NodeBilateralBlurData *)MEM_callocN(sizeof(NodeBilateralBlurData), + "node bilateral blur data"); node->storage = nbbd; nbbd->iter = 1; nbbd->sigma_color = 0.3; diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.c b/source/blender/nodes/composite/nodes/node_composite_blur.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_blur.c rename to source/blender/nodes/composite/nodes/node_composite_blur.cc index 92379f4552b..c5c0c21929e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_blur.c +++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc @@ -22,7 +22,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** BLUR ******************** */ static bNodeSocketTemplate cmp_node_blur_in[] = { @@ -33,7 +33,7 @@ static bNodeSocketTemplate cmp_node_blur_out[] = {{SOCK_RGBA, N_("Image")}, {-1, static void node_composit_init_blur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBlurData *data = MEM_callocN(sizeof(NodeBlurData), "node blur data"); + NodeBlurData *data = (NodeBlurData *)MEM_callocN(sizeof(NodeBlurData), "node blur data"); data->filtertype = R_FILTER_GAUSS; node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.c b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_bokehblur.c rename to source/blender/nodes/composite/nodes/node_composite_bokehblur.cc index d724a83e5a2..f130a642e20 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.c +++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc @@ -22,7 +22,7 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** BLUR ******************** */ static bNodeSocketTemplate cmp_node_bokehblur_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.c b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc similarity index 88% rename from source/blender/nodes/composite/nodes/node_composite_bokehimage.c rename to source/blender/nodes/composite/nodes/node_composite_bokehimage.cc index 744aba417be..230f466594b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.c +++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** Bokeh image Tools ******************** */ @@ -32,7 +32,7 @@ static bNodeSocketTemplate cmp_node_bokehimage_out[] = { static void node_composit_init_bokehimage(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBokehImage *data = MEM_callocN(sizeof(NodeBokehImage), "NodeBokehImage"); + NodeBokehImage *data = (NodeBokehImage *)MEM_callocN(sizeof(NodeBokehImage), "NodeBokehImage"); data->angle = 0.0f; data->flaps = 5; data->rounding = 0.0f; @@ -46,7 +46,7 @@ void register_node_type_cmp_bokehimage(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_BOKEHIMAGE, "Bokeh Image", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, NULL, cmp_node_bokehimage_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_bokehimage_out); node_type_init(&ntype, node_composit_init_bokehimage); node_type_storage( &ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.c b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_boxmask.c rename to source/blender/nodes/composite/nodes/node_composite_boxmask.cc index e646b9a9adf..cdf96065f97 100644 --- a/source/blender/nodes/composite/nodes/node_composite_boxmask.c +++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** SCALAR MATH ******************** */ static bNodeSocketTemplate cmp_node_boxmask_in[] = { @@ -34,7 +34,7 @@ static bNodeSocketTemplate cmp_node_boxmask_out[] = { static void node_composit_init_boxmask(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBoxMask *data = MEM_callocN(sizeof(NodeBoxMask), "NodeBoxMask"); + NodeBoxMask *data = (NodeBoxMask *)MEM_callocN(sizeof(NodeBoxMask), "NodeBoxMask"); data->x = 0.5; data->y = 0.5; data->width = 0.2; diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.c b/source/blender/nodes/composite/nodes/node_composite_brightness.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_brightness.c rename to source/blender/nodes/composite/nodes/node_composite_brightness.cc index 5beecb55665..9c0716169c6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_brightness.c +++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Brigh and contrsast ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_channelMatte.c b/source/blender/nodes/composite/nodes/node_composite_channelMatte.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_channelMatte.c rename to source/blender/nodes/composite/nodes/node_composite_channelMatte.cc index 9912c10b368..e211bc45b17 100644 --- a/source/blender/nodes/composite/nodes/node_composite_channelMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_channelMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Channel Matte Node ********************************* */ static bNodeSocketTemplate cmp_node_channel_matte_in[] = { @@ -37,7 +37,7 @@ static bNodeSocketTemplate cmp_node_channel_matte_out[] = { static void node_composit_init_channel_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); node->storage = c; c->t1 = 1.0f; c->t2 = 0.0f; diff --git a/source/blender/nodes/composite/nodes/node_composite_chromaMatte.c b/source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_chromaMatte.c rename to source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc index 705566df35a..990778160df 100644 --- a/source/blender/nodes/composite/nodes/node_composite_chromaMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Chroma Key ********************************************************** */ static bNodeSocketTemplate cmp_node_chroma_in[] = { @@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_chroma_out[] = { static void node_composit_init_chroma_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); node->storage = c; c->t1 = DEG2RADF(30.0f); c->t2 = DEG2RADF(10.0f); diff --git a/source/blender/nodes/composite/nodes/node_composite_colorMatte.c b/source/blender/nodes/composite/nodes/node_composite_colorMatte.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_colorMatte.c rename to source/blender/nodes/composite/nodes/node_composite_colorMatte.cc index f5cf7bcbf22..fc9a0075b14 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_colorMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Color Key ********************************************************** */ static bNodeSocketTemplate cmp_node_color_in[] = { @@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_color_out[] = { static void node_composit_init_color_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node color"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node color"); node->storage = c; c->t1 = 0.01f; c->t2 = 0.1f; diff --git a/source/blender/nodes/composite/nodes/node_composite_colorSpill.c b/source/blender/nodes/composite/nodes/node_composite_colorSpill.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_colorSpill.c rename to source/blender/nodes/composite/nodes/node_composite_colorSpill.cc index 8ff4bcdced3..7bdc2e8289e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorSpill.c +++ b/source/blender/nodes/composite/nodes/node_composite_colorSpill.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Color Spill Suppression ********************************* */ static bNodeSocketTemplate cmp_node_color_spill_in[] = { @@ -37,7 +37,7 @@ static bNodeSocketTemplate cmp_node_color_spill_out[] = { static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorspill *ncs = MEM_callocN(sizeof(NodeColorspill), "node colorspill"); + NodeColorspill *ncs = (NodeColorspill *)MEM_callocN(sizeof(NodeColorspill), "node colorspill"); node->storage = ncs; node->custom1 = 2; /* green channel */ node->custom2 = 0; /* simple limit algorithm */ diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.c b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc similarity index 88% rename from source/blender/nodes/composite/nodes/node_composite_colorbalance.c rename to source/blender/nodes/composite/nodes/node_composite_colorbalance.cc index 0525229697a..0ca8eecd7db 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.c +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Color Balance ********************************* */ static bNodeSocketTemplate cmp_node_colorbalance_in[] = { @@ -43,10 +43,9 @@ static bNodeSocketTemplate cmp_node_colorbalance_out[] = { void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorBalance *n = node->storage; - int c; + NodeColorBalance *n = (NodeColorBalance *)node->storage; - for (c = 0; c < 3; c++) { + for (int c = 0; c < 3; c++) { n->slope[c] = (2.0f - n->lift[c]) * n->gain[c]; n->offset[c] = (n->lift[c] - 1.0f) * n->gain[c]; n->power[c] = (n->gamma[c] != 0.0f) ? 1.0f / n->gamma[c] : 1000000.0f; @@ -55,10 +54,9 @@ void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *UNUSED(ntree), bNode *node) void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorBalance *n = node->storage; - int c; + NodeColorBalance *n = (NodeColorBalance *)node->storage; - for (c = 0; c < 3; c++) { + for (int c = 0; c < 3; c++) { float d = n->slope[c] + n->offset[c]; n->lift[c] = (d != 0.0f ? n->slope[c] + 2.0f * n->offset[c] / d : 0.0f); n->gain[c] = d; @@ -68,7 +66,8 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node) static void node_composit_init_colorbalance(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorBalance *n = node->storage = MEM_callocN(sizeof(NodeColorBalance), "node colorbalance"); + NodeColorBalance *n = (NodeColorBalance *)MEM_callocN(sizeof(NodeColorBalance), + "node colorbalance"); n->lift[0] = n->lift[1] = n->lift[2] = 1.0f; n->gamma[0] = n->gamma[1] = n->gamma[2] = 1.0f; @@ -77,6 +76,7 @@ static void node_composit_init_colorbalance(bNodeTree *UNUSED(ntree), bNode *nod n->slope[0] = n->slope[1] = n->slope[2] = 1.0f; n->offset[0] = n->offset[1] = n->offset[2] = 0.0f; n->power[0] = n->power[1] = n->power[2] = 1.0f; + node->storage = n; } void register_node_type_cmp_colorbalance(void) diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.c b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc similarity index 91% rename from source/blender/nodes/composite/nodes/node_composite_colorcorrection.c rename to source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc index 45d39f8be8d..93f6af7bf81 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.c +++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Color Balance ********************************* */ static bNodeSocketTemplate cmp_node_colorcorrection_in[] = { @@ -37,8 +37,8 @@ static bNodeSocketTemplate cmp_node_colorcorrection_out[] = { static void node_composit_init_colorcorrection(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorCorrection *n = node->storage = MEM_callocN(sizeof(NodeColorCorrection), - "node colorcorrection"); + NodeColorCorrection *n = (NodeColorCorrection *)MEM_callocN(sizeof(NodeColorCorrection), + "node colorcorrection"); n->startmidtones = 0.2f; n->endmidtones = 0.7f; n->master.contrast = 1.0f; @@ -62,6 +62,7 @@ static void node_composit_init_colorcorrection(bNodeTree *UNUSED(ntree), bNode * n->highlights.lift = 0.0f; n->highlights.saturation = 1.0f; node->custom1 = 7; // red + green + blue enabled + node->storage = n; } void register_node_type_cmp_colorcorrection(void) diff --git a/source/blender/nodes/composite/nodes/node_composite_common.c b/source/blender/nodes/composite/nodes/node_composite_common.cc similarity index 89% rename from source/blender/nodes/composite/nodes/node_composite_common.c rename to source/blender/nodes/composite/nodes/node_composite_common.cc index 61abc80fe93..fecf6795ef7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_common.c +++ b/source/blender/nodes/composite/nodes/node_composite_common.cc @@ -26,7 +26,7 @@ #include "NOD_common.h" #include "node_common.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_node.h" @@ -46,10 +46,10 @@ void register_node_type_cmp_group(void) ntype.insert_link = node_insert_link_default; ntype.update_internal_links = node_update_internal_links_default; ntype.rna_ext.srna = RNA_struct_find("CompositorNodeGroup"); - BLI_assert(ntype.rna_ext.srna != NULL); + BLI_assert(ntype.rna_ext.srna != nullptr); RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype); - node_type_socket_templates(&ntype, NULL, NULL); + node_type_socket_templates(&ntype, nullptr, nullptr); node_type_size(&ntype, 140, 60, 400); node_type_label(&ntype, node_group_label); node_type_group_update(&ntype, node_group_update); @@ -60,13 +60,13 @@ void register_node_type_cmp_group(void) void register_node_type_cmp_custom_group(bNodeType *ntype) { /* These methods can be overridden but need a default implementation otherwise. */ - if (ntype->poll == NULL) { + if (ntype->poll == nullptr) { ntype->poll = cmp_node_poll_default; } - if (ntype->insert_link == NULL) { + if (ntype->insert_link == nullptr) { ntype->insert_link = node_insert_link_default; } - if (ntype->update_internal_links == NULL) { + if (ntype->update_internal_links == nullptr) { ntype->update_internal_links = node_update_internal_links_default; } } diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.c b/source/blender/nodes/composite/nodes/node_composite_composite.cc similarity index 90% rename from source/blender/nodes/composite/nodes/node_composite_composite.c rename to source/blender/nodes/composite/nodes/node_composite_composite.cc index dee2ce6b3ec..68884321b20 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.c +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** COMPOSITE ******************** */ static bNodeSocketTemplate cmp_node_composite_in[] = { @@ -36,10 +36,10 @@ void register_node_type_cmp_composite(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_composite_in, NULL); + node_type_socket_templates(&ntype, cmp_node_composite_in, nullptr); /* Do not allow muting for this node. */ - node_type_internal_links(&ntype, NULL); + node_type_internal_links(&ntype, nullptr); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.c b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_cornerpin.c rename to source/blender/nodes/composite/nodes/node_composite_cornerpin.cc index 135120c45aa..b5ca1fb015e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.c +++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate inputs[] = { {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.c b/source/blender/nodes/composite/nodes/node_composite_crop.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_crop.c rename to source/blender/nodes/composite/nodes/node_composite_crop.cc index 868df5367c4..f07dba8a74b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_crop.c +++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Crop ******************** */ @@ -36,7 +36,7 @@ static bNodeSocketTemplate cmp_node_crop_out[] = { static void node_composit_init_crop(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTwoXYs *nxy = MEM_callocN(sizeof(NodeTwoXYs), "node xy data"); + NodeTwoXYs *nxy = (NodeTwoXYs *)MEM_callocN(sizeof(NodeTwoXYs), "node xy data"); node->storage = nxy; nxy->x1 = 0; nxy->x2 = 0; diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index 51dd73a86af..6657267b016 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BLI_assert.h" #include "BLI_dynstr.h" diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.c b/source/blender/nodes/composite/nodes/node_composite_curves.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_curves.c rename to source/blender/nodes/composite/nodes/node_composite_curves.cc index 470540d3337..8a8a7624fce 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.c +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** CURVE Time ******************** */ @@ -43,7 +43,7 @@ void register_node_type_cmp_curve_time(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_TIME, "Time", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_time_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_time_out); node_type_size(&ntype, 140, 100, 320); node_type_init(&ntype, node_composit_init_curves_time); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); diff --git a/source/blender/nodes/composite/nodes/node_composite_defocus.c b/source/blender/nodes/composite/nodes/node_composite_defocus.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_defocus.c rename to source/blender/nodes/composite/nodes/node_composite_defocus.cc index 3803f450f49..b1ac170217a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_defocus.c +++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc @@ -21,9 +21,9 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" -#include +#include /* ************ qdn: Defocus node ****************** */ static bNodeSocketTemplate cmp_node_defocus_in[] = { @@ -39,7 +39,7 @@ static bNodeSocketTemplate cmp_node_defocus_out[] = { static void node_composit_init_defocus(bNodeTree *UNUSED(ntree), bNode *node) { /* qdn: defocus node */ - NodeDefocus *nbd = MEM_callocN(sizeof(NodeDefocus), "node defocus data"); + NodeDefocus *nbd = (NodeDefocus *)MEM_callocN(sizeof(NodeDefocus), "node defocus data"); nbd->bktype = 0; nbd->rotation = 0.0f; nbd->preview = 1; diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.c b/source/blender/nodes/composite/nodes/node_composite_denoise.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_denoise.c rename to source/blender/nodes/composite/nodes/node_composite_denoise.cc index e2c7c7b995f..ec085794462 100644 --- a/source/blender/nodes/composite/nodes/node_composite_denoise.c +++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc @@ -23,7 +23,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_denoise_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, @@ -34,7 +34,7 @@ static bNodeSocketTemplate cmp_node_denoise_out[] = {{SOCK_RGBA, N_("Image")}, { static void node_composit_init_denonise(bNodeTree *UNUSED(ntree), bNode *node) { - NodeDenoise *ndg = MEM_callocN(sizeof(NodeDenoise), "node denoise data"); + NodeDenoise *ndg = (NodeDenoise *)MEM_callocN(sizeof(NodeDenoise), "node denoise data"); ndg->hdr = true; ndg->prefilter = CMP_NODE_DENOISE_PREFILTER_ACCURATE; node->storage = ndg; diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.c b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_despeckle.c rename to source/blender/nodes/composite/nodes/node_composite_despeckle.cc index 18567ee2006..52d91dabeb1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_despeckle.c +++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** FILTER ******************** */ static bNodeSocketTemplate cmp_node_despeckle_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_diffMatte.c b/source/blender/nodes/composite/nodes/node_composite_diffMatte.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_diffMatte.c rename to source/blender/nodes/composite/nodes/node_composite_diffMatte.cc index 7871a9e8b04..1e1a48381b7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_diffMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_diffMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* channel Difference Matte ********************************* */ static bNodeSocketTemplate cmp_node_diff_matte_in[] = { @@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_diff_matte_out[] = { static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); node->storage = c; c->t1 = 0.1f; c->t2 = 0.1f; diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.c b/source/blender/nodes/composite/nodes/node_composite_dilate.cc similarity index 90% rename from source/blender/nodes/composite/nodes/node_composite_dilate.c rename to source/blender/nodes/composite/nodes/node_composite_dilate.cc index 12f1f229258..57884a299da 100644 --- a/source/blender/nodes/composite/nodes/node_composite_dilate.c +++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Dilate/Erode ******************** */ @@ -31,7 +31,8 @@ static bNodeSocketTemplate cmp_node_dilateerode_out[] = {{SOCK_FLOAT, N_("Mask") static void node_composit_init_dilateerode(bNodeTree *UNUSED(ntree), bNode *node) { - NodeDilateErode *data = MEM_callocN(sizeof(NodeDilateErode), "NodeDilateErode"); + NodeDilateErode *data = (NodeDilateErode *)MEM_callocN(sizeof(NodeDilateErode), + "NodeDilateErode"); data->falloff = PROP_SMOOTH; node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.c b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc similarity index 92% rename from source/blender/nodes/composite/nodes/node_composite_directionalblur.c rename to source/blender/nodes/composite/nodes/node_composite_directionalblur.cc index 6dd60526edf..d9f82ba5009 100644 --- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.c +++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_dblur_in[] = {{SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}}; @@ -30,7 +30,7 @@ static bNodeSocketTemplate cmp_node_dblur_out[] = {{SOCK_RGBA, N_("Image")}, {-1 static void node_composit_init_dblur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeDBlurData *ndbd = MEM_callocN(sizeof(NodeDBlurData), "node dblur data"); + NodeDBlurData *ndbd = (NodeDBlurData *)MEM_callocN(sizeof(NodeDBlurData), "node dblur data"); node->storage = ndbd; ndbd->iter = 1; ndbd->center_x = 0.5; diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.c b/source/blender/nodes/composite/nodes/node_composite_displace.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_displace.c rename to source/blender/nodes/composite/nodes/node_composite_displace.cc index 819a4f29b3a..b1ed7f05794 100644 --- a/source/blender/nodes/composite/nodes/node_composite_displace.c +++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Displace ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_distanceMatte.c b/source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_distanceMatte.c rename to source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc index 3e659fe662b..3f8767ecd08 100644 --- a/source/blender/nodes/composite/nodes/node_composite_distanceMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* channel Distance Matte ********************************* */ static bNodeSocketTemplate cmp_node_distance_matte_in[] = { @@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_distance_matte_out[] = { static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); node->storage = c; c->channel = 1; c->t1 = 0.1f; diff --git a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c b/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c rename to source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc index 6f68b187775..7c9a48efc2d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c +++ b/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc @@ -20,7 +20,7 @@ /** \file * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Double Edge Mask ******************** */ static bNodeSocketTemplate cmp_node_doubleedgemask_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.c b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc similarity index 90% rename from source/blender/nodes/composite/nodes/node_composite_ellipsemask.c rename to source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc index d5e1d519a1c..67196fb0d35 100644 --- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.c +++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** SCALAR MATH ******************** */ static bNodeSocketTemplate cmp_node_ellipsemask_in[] = { @@ -34,7 +34,8 @@ static bNodeSocketTemplate cmp_node_ellipsemask_out[] = { static void node_composit_init_ellipsemask(bNodeTree *UNUSED(ntree), bNode *node) { - NodeEllipseMask *data = MEM_callocN(sizeof(NodeEllipseMask), "NodeEllipseMask"); + NodeEllipseMask *data = (NodeEllipseMask *)MEM_callocN(sizeof(NodeEllipseMask), + "NodeEllipseMask"); data->x = 0.5; data->y = 0.5; data->width = 0.2; diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.c b/source/blender/nodes/composite/nodes/node_composite_exposure.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_exposure.c rename to source/blender/nodes/composite/nodes/node_composite_exposure.cc index bd27e4a3d76..f0259f781a5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_exposure.c +++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Exposure ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.c b/source/blender/nodes/composite/nodes/node_composite_filter.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_filter.c rename to source/blender/nodes/composite/nodes/node_composite_filter.cc index d0ad090ece4..f07619877f4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_filter.c +++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** FILTER ******************** */ static bNodeSocketTemplate cmp_node_filter_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_flip.c b/source/blender/nodes/composite/nodes/node_composite_flip.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_flip.c rename to source/blender/nodes/composite/nodes/node_composite_flip.cc index 91a91bb5f5f..42aa3141f5c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_flip.c +++ b/source/blender/nodes/composite/nodes/node_composite_flip.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Flip ******************** */ static bNodeSocketTemplate cmp_node_flip_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.c b/source/blender/nodes/composite/nodes/node_composite_gamma.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_gamma.c rename to source/blender/nodes/composite/nodes/node_composite_gamma.cc index ddcaf691fd2..b2b63c58411 100644 --- a/source/blender/nodes/composite/nodes/node_composite_gamma.c +++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Gamma Tools ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_glare.c b/source/blender/nodes/composite/nodes/node_composite_glare.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_glare.c rename to source/blender/nodes/composite/nodes/node_composite_glare.cc index a792fcc86cd..8a2fd1e1584 100644 --- a/source/blender/nodes/composite/nodes/node_composite_glare.c +++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_glare_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, @@ -34,7 +34,7 @@ static bNodeSocketTemplate cmp_node_glare_out[] = { static void node_composit_init_glare(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGlare *ndg = MEM_callocN(sizeof(NodeGlare), "node glare data"); + NodeGlare *ndg = (NodeGlare *)MEM_callocN(sizeof(NodeGlare), "node glare data"); ndg->quality = 1; ndg->type = 2; ndg->iter = 3; diff --git a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.c b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_hueSatVal.c rename to source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc index 494b6136a6e..c0fb89bd3c4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.c +++ b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Hue Saturation ******************** */ static bNodeSocketTemplate cmp_node_hue_sat_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.c b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc similarity index 91% rename from source/blender/nodes/composite/nodes/node_composite_huecorrect.c rename to source/blender/nodes/composite/nodes/node_composite_huecorrect.cc index 6a5c918d9ae..c577781d96d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.c +++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_huecorrect_in[] = { {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, @@ -36,12 +36,13 @@ static bNodeSocketTemplate cmp_node_huecorrect_out[] = { static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node) { - CurveMapping *cumapping = node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - int c; + node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + + CurveMapping *cumapping = (CurveMapping *)node->storage; cumapping->preset = CURVE_PRESET_MID9; - for (c = 0; c < 3; c++) { + for (int c = 0; c < 3; c++) { CurveMap *cuma = &cumapping->cm[c]; BKE_curvemap_reset(cuma, &cumapping->clipr, cumapping->preset, CURVEMAP_SLOPE_POSITIVE); } diff --git a/source/blender/nodes/composite/nodes/node_composite_idMask.c b/source/blender/nodes/composite/nodes/node_composite_idMask.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_idMask.c rename to source/blender/nodes/composite/nodes/node_composite_idMask.cc index 84563e7560b..13e3536f9ee 100644 --- a/source/blender/nodes/composite/nodes/node_composite_idMask.c +++ b/source/blender/nodes/composite/nodes/node_composite_idMask.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** ID Mask ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.cc similarity index 85% rename from source/blender/nodes/composite/nodes/node_composite_image.c rename to source/blender/nodes/composite/nodes/node_composite_image.cc index a56dfea9dbf..aee1c1d63fd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.c +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BLI_linklist.h" #include "BLI_utildefines.h" @@ -84,16 +84,17 @@ static void cmp_node_image_add_pass_output(bNodeTree *ntree, LinkNodePair *available_sockets, int *prev_index) { - bNodeSocket *sock = BLI_findstring(&node->outputs, name, offsetof(bNodeSocket, name)); + bNodeSocket *sock = (bNodeSocket *)BLI_findstring( + &node->outputs, name, offsetof(bNodeSocket, name)); /* Replace if types don't match. */ if (sock && sock->type != type) { nodeRemoveSocket(ntree, node, sock); - sock = NULL; + sock = nullptr; } /* Create socket if it doesn't exist yet. */ - if (sock == NULL) { + if (sock == nullptr) { if (rres_index >= 0) { sock = node_add_socket_from_template( ntree, node, &cmp_node_rlayers_out[rres_index], SOCK_OUT); @@ -102,18 +103,19 @@ static void cmp_node_image_add_pass_output(bNodeTree *ntree, sock = nodeAddStaticSocket(ntree, node, SOCK_OUT, type, PROP_NONE, name, name); } /* extra socket info */ - NodeImageLayer *sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer"); + NodeImageLayer *sockdata = (NodeImageLayer *)MEM_callocN(sizeof(NodeImageLayer), + "node image layer"); sock->storage = sockdata; } - NodeImageLayer *sockdata = sock->storage; + NodeImageLayer *sockdata = (NodeImageLayer *)sock->storage; if (sockdata) { BLI_strncpy(sockdata->pass_name, passname, sizeof(sockdata->pass_name)); } /* Reorder sockets according to order that passes are added. */ const int after_index = (*prev_index)++; - bNodeSocket *after_sock = BLI_findlink(&node->outputs, after_index); + bNodeSocket *after_sock = (bNodeSocket *)BLI_findlink(&node->outputs, after_index); BLI_remlink(&node->outputs, sock); BLI_insertlinkafter(&node->outputs, after_sock, sock); @@ -128,8 +130,8 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, ImBuf *ibuf; int prev_index = -1; if (ima) { - ImageUser *iuser = node->storage; - ImageUser load_iuser = {NULL}; + ImageUser *iuser = (ImageUser *)node->storage; + ImageUser load_iuser = {nullptr}; int offset = BKE_image_sequence_guess_offset(ima); /* It is possible that image user in this node is not @@ -144,15 +146,14 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, load_iuser.framenr = offset; /* make sure ima->type is correct */ - ibuf = BKE_image_acquire_ibuf(ima, &load_iuser, NULL); + ibuf = BKE_image_acquire_ibuf(ima, &load_iuser, nullptr); if (ima->rr) { - RenderLayer *rl = BLI_findlink(&ima->rr->layers, iuser->layer); + RenderLayer *rl = (RenderLayer *)BLI_findlink(&ima->rr->layers, iuser->layer); if (rl) { - RenderPass *rpass; - for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - int type; + LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) { + eNodeSocketDatatype type; if (rpass->channels == 1) { type = SOCK_FLOAT; } @@ -182,7 +183,7 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, &prev_index); } } - BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(ima, ibuf, nullptr); return; } } @@ -219,14 +220,14 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, available_sockets, &prev_index); } - BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(ima, ibuf, nullptr); } } -typedef struct RLayerUpdateData { +struct RLayerUpdateData { LinkNodePair *available_sockets; int prev_index; -} RLayerUpdateData; +}; void node_cmp_rlayers_register_pass(bNodeTree *ntree, bNode *node, @@ -235,13 +236,13 @@ void node_cmp_rlayers_register_pass(bNodeTree *ntree, const char *name, eNodeSocketDatatype type) { - RLayerUpdateData *data = node->storage; + RLayerUpdateData *data = (RLayerUpdateData *)node->storage; - if (scene == NULL || view_layer == NULL || data == NULL || node->id != (ID *)scene) { + if (scene == nullptr || view_layer == nullptr || data == nullptr || node->id != (ID *)scene) { return; } - ViewLayer *node_view_layer = BLI_findlink(&scene->view_layers, node->custom1); + ViewLayer *node_view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, node->custom1); if (node_view_layer != view_layer) { return; } @@ -281,7 +282,7 @@ static void cmp_node_rlayer_create_outputs_cb(void *UNUSED(userdata), * unless we want to register that for every other temp Main we could generate??? */ ntreeCompositRegisterPass(scene->nodetree, scene, view_layer, name, type); - for (Scene *sce = G_MAIN->scenes.first; sce; sce = sce->id.next) { + for (Scene *sce = (Scene *)G_MAIN->scenes.first; sce; sce = (Scene *)sce->id.next) { if (sce->nodetree && sce != scene) { ntreeCompositRegisterPass(sce->nodetree, scene, view_layer, name, type); } @@ -297,16 +298,17 @@ static void cmp_node_rlayer_create_outputs(bNodeTree *ntree, if (scene) { RenderEngineType *engine_type = RE_engines_find(scene->r.engine); if (engine_type && engine_type->update_render_passes) { - ViewLayer *view_layer = BLI_findlink(&scene->view_layers, node->custom1); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, node->custom1); if (view_layer) { - RLayerUpdateData *data = MEM_mallocN(sizeof(RLayerUpdateData), "render layer update data"); + RLayerUpdateData *data = (RLayerUpdateData *)MEM_mallocN(sizeof(RLayerUpdateData), + "render layer update data"); data->available_sockets = available_sockets; data->prev_index = -1; node->storage = data; RenderEngine *engine = RE_engine_create(engine_type); RE_engine_update_render_passes( - engine, scene, view_layer, cmp_node_rlayer_create_outputs_cb, NULL); + engine, scene, view_layer, cmp_node_rlayer_create_outputs_cb, nullptr); RE_engine_free(engine); if ((scene->r.mode & R_EDGE_FRS) && @@ -315,7 +317,7 @@ static void cmp_node_rlayer_create_outputs(bNodeTree *ntree, } MEM_freeN(data); - node->storage = NULL; + node->storage = nullptr; return; } @@ -348,8 +350,7 @@ static void cmp_node_rlayer_create_outputs(bNodeTree *ntree, static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rlayer) { bNodeSocket *sock, *sock_next; - LinkNodePair available_sockets = {NULL, NULL}; - int sock_index; + LinkNodePair available_sockets = {nullptr, nullptr}; /* XXX make callback */ if (rlayer) { @@ -369,15 +370,15 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl * the first 31 passes to belong to a specific pass type. * So, we keep those 31 always allocated before the others as well, * even if they have no links attached. */ - sock_index = 0; - for (sock = node->outputs.first; sock; sock = sock_next, sock_index++) { + int sock_index = 0; + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock_next, sock_index++) { sock_next = sock->next; if (BLI_linklist_index(available_sockets.list, sock) >= 0) { sock->flag &= ~(SOCK_UNAVAIL | SOCK_HIDDEN); } else { bNodeLink *link; - for (link = ntree->links.first; link; link = link->next) { + for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { if (link->fromsock == sock) { break; } @@ -392,7 +393,7 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl } } - BLI_linklist_free(available_sockets.list, NULL); + BLI_linklist_free(available_sockets.list, nullptr); } static void cmp_node_image_update(bNodeTree *ntree, bNode *node) @@ -407,7 +408,7 @@ static void cmp_node_image_update(bNodeTree *ntree, bNode *node) static void node_composit_init_image(bNodeTree *ntree, bNode *node) { - ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user"); + ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->frames = 1; iuser->sfra = 1; @@ -420,10 +421,8 @@ static void node_composit_init_image(bNodeTree *ntree, bNode *node) static void node_composit_free_image(bNode *node) { - bNodeSocket *sock; - /* free extra socket info */ - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { MEM_freeN(sock->storage); } @@ -436,9 +435,9 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree), { dest_node->storage = MEM_dupallocN(src_node->storage); - const bNodeSocket *src_output_sock = src_node->outputs.first; - bNodeSocket *dest_output_sock = dest_node->outputs.first; - while (dest_output_sock != NULL) { + const bNodeSocket *src_output_sock = (bNodeSocket *)src_node->outputs.first; + bNodeSocket *dest_output_sock = (bNodeSocket *)dest_node->outputs.first; + while (dest_output_sock != nullptr) { dest_output_sock->storage = MEM_dupallocN(src_output_sock->storage); src_output_sock = src_output_sock->next; @@ -469,7 +468,7 @@ void node_cmp_rlayers_outputs(bNodeTree *ntree, bNode *node) const char *node_cmp_rlayers_sock_to_pass(int sock_index) { if (sock_index >= NUM_LEGACY_SOCKETS) { - return NULL; + return nullptr; } const char *name = cmp_node_rlayers_out[sock_index].name; /* Exception for alpha, which is derived from Combined. */ @@ -479,14 +478,16 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index) static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; int sock_index = 0; node->id = &scene->id; id_us_plus(node->id); - for (bNodeSocket *sock = node->outputs.first; sock; sock = sock->next, sock_index++) { - NodeImageLayer *sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer"); + for (bNodeSocket *sock = (bNodeSocket *)node->outputs.first; sock; + sock = sock->next, sock_index++) { + NodeImageLayer *sockdata = (NodeImageLayer *)MEM_callocN(sizeof(NodeImageLayer), + "node image layer"); sock->storage = sockdata; BLI_strncpy(sockdata->pass_name, @@ -510,13 +511,13 @@ static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype), * Render layers node can only be used in local scene->nodetree, * since it directly links to the scene. */ - for (scene = G.main->scenes.first; scene; scene = scene->id.next) { + for (scene = (Scene *)G.main->scenes.first; scene; scene = (Scene *)scene->id.next) { if (scene->nodetree == ntree) { break; } } - if (scene == NULL) { + if (scene == nullptr) { *r_disabled_hint = "The node tree must be the compositing node tree of any scene in the file"; return false; } @@ -525,10 +526,8 @@ static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype), static void node_composit_free_rlayers(bNode *node) { - bNodeSocket *sock; - /* free extra socket info */ - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (sock->storage) { MEM_freeN(sock->storage); } @@ -540,9 +539,9 @@ static void node_composit_copy_rlayers(bNodeTree *UNUSED(dest_ntree), const bNode *src_node) { /* copy extra socket info */ - const bNodeSocket *src_output_sock = src_node->outputs.first; - bNodeSocket *dest_output_sock = dest_node->outputs.first; - while (dest_output_sock != NULL) { + const bNodeSocket *src_output_sock = (bNodeSocket *)src_node->outputs.first; + bNodeSocket *dest_output_sock = (bNodeSocket *)dest_node->outputs.first; + while (dest_output_sock != nullptr) { dest_output_sock->storage = MEM_dupallocN(src_output_sock->storage); src_output_sock = src_output_sock->next; @@ -562,10 +561,10 @@ void register_node_type_cmp_rlayers(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_R_LAYERS, "Render Layers", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, NULL, cmp_node_rlayers_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_rlayers_out); ntype.initfunc_api = node_composit_init_rlayers; ntype.poll = node_composit_poll_rlayers; - node_type_storage(&ntype, NULL, node_composit_free_rlayers, node_composit_copy_rlayers); + node_type_storage(&ntype, nullptr, node_composit_free_rlayers, node_composit_copy_rlayers); node_type_update(&ntype, cmp_node_rlayers_update); node_type_init(&ntype, node_cmp_rlayers_outputs); node_type_size_preset(&ntype, NODE_SIZE_LARGE); diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.c b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_inpaint.c rename to source/blender/nodes/composite/nodes/node_composite_inpaint.cc index 6c02bca9b39..d0ff97a2ca0 100644 --- a/source/blender/nodes/composite/nodes/node_composite_inpaint.c +++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Inpaint/ ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.c b/source/blender/nodes/composite/nodes/node_composite_invert.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_invert.c rename to source/blender/nodes/composite/nodes/node_composite_invert.cc index d229261f208..88fd18326d9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_invert.c +++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** INVERT ******************** */ static bNodeSocketTemplate cmp_node_invert_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_keying.c b/source/blender/nodes/composite/nodes/node_composite_keying.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_keying.c rename to source/blender/nodes/composite/nodes/node_composite_keying.cc index 73e86a21ebe..d5547161069 100644 --- a/source/blender/nodes/composite/nodes/node_composite_keying.c +++ b/source/blender/nodes/composite/nodes/node_composite_keying.cc @@ -27,7 +27,7 @@ #include "BLI_math_base.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -48,9 +48,7 @@ static bNodeSocketTemplate cmp_node_keying_out[] = { static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode *node) { - NodeKeyingData *data; - - data = MEM_callocN(sizeof(NodeKeyingData), "node keying data"); + NodeKeyingData *data = (NodeKeyingData *)MEM_callocN(sizeof(NodeKeyingData), "node keying data"); data->screen_balance = 0.5f; data->despill_balance = 0.5f; @@ -59,7 +57,6 @@ static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode *node) data->edge_kernel_tolerance = 0.1f; data->clip_black = 0.0f; data->clip_white = 1.0f; - node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.c b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc similarity index 84% rename from source/blender/nodes/composite/nodes/node_composite_keyingscreen.c rename to source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc index e5e97cd72e4..c976a92b92d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.c +++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc @@ -26,7 +26,7 @@ #include "BLI_math_base.h" #include "BLI_math_color.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -37,10 +37,8 @@ static bNodeSocketTemplate cmp_node_keyingscreen_out[] = { static void node_composit_init_keyingscreen(bNodeTree *UNUSED(ntree), bNode *node) { - NodeKeyingScreenData *data; - - data = MEM_callocN(sizeof(NodeKeyingScreenData), "node keyingscreen data"); - + NodeKeyingScreenData *data = (NodeKeyingScreenData *)MEM_callocN(sizeof(NodeKeyingScreenData), + "node keyingscreen data"); node->storage = data; } @@ -49,7 +47,7 @@ void register_node_type_cmp_keyingscreen(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_KEYINGSCREEN, "Keying Screen", NODE_CLASS_MATTE, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_keyingscreen_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_keyingscreen_out); node_type_init(&ntype, node_composit_init_keyingscreen); node_type_storage( &ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.c b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_lensdist.c rename to source/blender/nodes/composite/nodes/node_composite_lensdist.cc index ce8c8c00e24..2a8dc035792 100644 --- a/source/blender/nodes/composite/nodes/node_composite_lensdist.c +++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_lensdist_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, @@ -36,7 +36,7 @@ static bNodeSocketTemplate cmp_node_lensdist_out[] = { static void node_composit_init_lensdist(bNodeTree *UNUSED(ntree), bNode *node) { - NodeLensDist *nld = MEM_callocN(sizeof(NodeLensDist), "node lensdist data"); + NodeLensDist *nld = (NodeLensDist *)MEM_callocN(sizeof(NodeLensDist), "node lensdist data"); nld->jit = nld->proj = nld->fit = 0; node->storage = nld; } diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.c b/source/blender/nodes/composite/nodes/node_composite_levels.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_levels.c rename to source/blender/nodes/composite/nodes/node_composite_levels.cc index 7c70ccf412a..3a3d8a3e99c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_levels.c +++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** LEVELS ******************** */ static bNodeSocketTemplate cmp_node_view_levels_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_lummaMatte.c b/source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_lummaMatte.c rename to source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc index cb0f59c2f4a..600406d22b9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_lummaMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Luma Matte Node ********************************* */ static bNodeSocketTemplate cmp_node_luma_matte_in[] = { @@ -37,7 +37,7 @@ static bNodeSocketTemplate cmp_node_luma_matte_out[] = { static void node_composit_init_luma_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); node->storage = c; c->t1 = 1.0f; c->t2 = 0.0f; diff --git a/source/blender/nodes/composite/nodes/node_composite_mapRange.c b/source/blender/nodes/composite/nodes/node_composite_mapRange.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_mapRange.c rename to source/blender/nodes/composite/nodes/node_composite_mapRange.cc index cd95e73ba5c..808ad538e55 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mapRange.c +++ b/source/blender/nodes/composite/nodes/node_composite_mapRange.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** MAP VALUE ******************** */ static bNodeSocketTemplate cmp_node_map_range_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_mapUV.c b/source/blender/nodes/composite/nodes/node_composite_mapUV.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_mapUV.c rename to source/blender/nodes/composite/nodes/node_composite_mapUV.cc index e88a303e449..99032bd042e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mapUV.c +++ b/source/blender/nodes/composite/nodes/node_composite_mapUV.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Map UV ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_mapValue.c b/source/blender/nodes/composite/nodes/node_composite_mapValue.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_mapValue.c rename to source/blender/nodes/composite/nodes/node_composite_mapValue.cc index c93807c3801..25c00c2ba13 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mapValue.c +++ b/source/blender/nodes/composite/nodes/node_composite_mapValue.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** MAP VALUE ******************** */ static bNodeSocketTemplate cmp_node_map_value_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.c b/source/blender/nodes/composite/nodes/node_composite_mask.cc similarity index 89% rename from source/blender/nodes/composite/nodes/node_composite_mask.c rename to source/blender/nodes/composite/nodes/node_composite_mask.cc index e6a5df6c24b..bb33a874ae7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mask.c +++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc @@ -23,7 +23,7 @@ #include "DNA_mask_types.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -31,7 +31,7 @@ static bNodeSocketTemplate cmp_node_mask_out[] = {{SOCK_FLOAT, "Mask"}, {-1, ""} static void node_composit_init_mask(bNodeTree *UNUSED(ntree), bNode *node) { - NodeMask *data = MEM_callocN(sizeof(NodeMask), "NodeMask"); + NodeMask *data = (NodeMask *)MEM_callocN(sizeof(NodeMask), "NodeMask"); data->size_x = data->size_y = 256; node->storage = data; @@ -41,7 +41,7 @@ static void node_composit_init_mask(bNodeTree *UNUSED(ntree), bNode *node) static void node_mask_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) { - if (node->id != NULL) { + if (node->id != nullptr) { BLI_strncpy(label, node->id->name + 2, maxlen); } else { @@ -54,7 +54,7 @@ void register_node_type_cmp_mask(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MASK, "Mask", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_mask_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_mask_out); node_type_init(&ntype, node_composit_init_mask); node_type_label(&ntype, node_mask_label); diff --git a/source/blender/nodes/composite/nodes/node_composite_math.c b/source/blender/nodes/composite/nodes/node_composite_math.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_math.c rename to source/blender/nodes/composite/nodes/node_composite_math.cc index 2191c6bcdc3..ecddcc2ad32 100644 --- a/source/blender/nodes/composite/nodes/node_composite_math.c +++ b/source/blender/nodes/composite/nodes/node_composite_math.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ static bNodeSocketTemplate cmp_node_math_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.c b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_mixrgb.c rename to source/blender/nodes/composite/nodes/node_composite_mixrgb.cc index 9d3751c7da3..985159c54c2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.c +++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** MIX RGB ******************** */ static bNodeSocketTemplate cmp_node_mix_rgb_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.c b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc similarity index 85% rename from source/blender/nodes/composite/nodes/node_composite_movieclip.c rename to source/blender/nodes/composite/nodes/node_composite_movieclip.cc index 4f5aef05425..2dffa8b4841 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.c +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_context.h" #include "BKE_lib_id.h" @@ -38,9 +38,10 @@ static bNodeSocketTemplate cmp_node_movieclip_out[] = { static void init(const bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; Scene *scene = CTX_data_scene(C); - MovieClipUser *user = MEM_callocN(sizeof(MovieClipUser), "node movie clip user"); + MovieClipUser *user = (MovieClipUser *)MEM_callocN(sizeof(MovieClipUser), + "node movie clip user"); node->id = (ID *)scene->clip; id_us_plus(node->id); @@ -53,7 +54,7 @@ void register_node_type_cmp_movieclip(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MOVIECLIP, "Movie Clip", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, NULL, cmp_node_movieclip_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_movieclip_out); ntype.initfunc_api = init; node_type_storage( &ntype, "MovieClipUser", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.c b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc similarity index 87% rename from source/blender/nodes/composite/nodes/node_composite_moviedistortion.c rename to source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc index 7e30d004b45..2bac30cc152 100644 --- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.c +++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_context.h" #include "BKE_lib_id.h" @@ -50,7 +50,7 @@ static void label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen static void init(const bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; Scene *scene = CTX_data_scene(C); node->id = (ID *)scene->clip; @@ -60,16 +60,16 @@ static void init(const bContext *C, PointerRNA *ptr) static void storage_free(bNode *node) { if (node->storage) { - BKE_tracking_distortion_free(node->storage); + BKE_tracking_distortion_free((MovieDistortion *)node->storage); } - node->storage = NULL; + node->storage = nullptr; } static void storage_copy(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node) { if (src_node->storage) { - dest_node->storage = BKE_tracking_distortion_copy(src_node->storage); + dest_node->storage = BKE_tracking_distortion_copy((MovieDistortion *)src_node->storage); } } @@ -82,7 +82,7 @@ void register_node_type_cmp_moviedistortion(void) node_type_label(&ntype, label); ntype.initfunc_api = init; - node_type_storage(&ntype, NULL, storage_free, storage_copy); + node_type_storage(&ntype, nullptr, storage_free, storage_copy); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.c b/source/blender/nodes/composite/nodes/node_composite_normal.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_normal.c rename to source/blender/nodes/composite/nodes/node_composite_normal.cc index 91300e66339..7531025daa5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normal.c +++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** NORMAL ******************** */ static bNodeSocketTemplate cmp_node_normal_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.c b/source/blender/nodes/composite/nodes/node_composite_normalize.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_normalize.c rename to source/blender/nodes/composite/nodes/node_composite_normalize.cc index 26f2abc745f..7cc54e4eed6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normalize.c +++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** NORMALIZE single channel, useful for Z buffer ******************** */ static bNodeSocketTemplate cmp_node_normalize_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_outputFile.c b/source/blender/nodes/composite/nodes/node_composite_outputFile.cc similarity index 76% rename from source/blender/nodes/composite/nodes/node_composite_outputFile.c rename to source/blender/nodes/composite/nodes/node_composite_outputFile.cc index c10edd8d5ad..a372d2f7419 100644 --- a/source/blender/nodes/composite/nodes/node_composite_outputFile.c +++ b/source/blender/nodes/composite/nodes/node_composite_outputFile.cc @@ -23,13 +23,13 @@ #include "BLI_string_utils.h" #include "BLI_utildefines.h" -#include +#include #include "BKE_context.h" #include "RNA_access.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "intern/openexr/openexr_multi.h" @@ -38,14 +38,15 @@ /* find unique path */ static bool unique_path_unique_check(void *arg, const char *name) { - struct { + struct Args { ListBase *lb; bNodeSocket *sock; - } *data = arg; - bNodeSocket *sock; - for (sock = data->lb->first; sock; sock = sock->next) { + }; + Args *data = (Args *)arg; + + LISTBASE_FOREACH (bNodeSocket *, sock, data->lb) { if (sock != data->sock) { - NodeImageMultiFileSocket *sockdata = sock->storage; + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; if (STREQ(sockdata->path, name)) { return true; } @@ -67,11 +68,11 @@ void ntreeCompositOutputFileUniquePath(ListBase *list, data.sock = sock; /* See if we are given an empty string */ - if (ELEM(NULL, sock, defname)) { + if (ELEM(nullptr, sock, defname)) { return; } - sockdata = sock->storage; + sockdata = (NodeImageMultiFileSocket *)sock->storage; BLI_uniquename_cb( unique_path_unique_check, &data, defname, delim, sockdata->path, sizeof(sockdata->path)); } @@ -79,14 +80,15 @@ void ntreeCompositOutputFileUniquePath(ListBase *list, /* find unique EXR layer */ static bool unique_layer_unique_check(void *arg, const char *name) { - struct { + struct Args { ListBase *lb; bNodeSocket *sock; - } *data = arg; - bNodeSocket *sock; - for (sock = data->lb->first; sock; sock = sock->next) { + }; + Args *data = (Args *)arg; + + LISTBASE_FOREACH (bNodeSocket *, sock, data->lb) { if (sock != data->sock) { - NodeImageMultiFileSocket *sockdata = sock->storage; + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; if (STREQ(sockdata->layer, name)) { return true; } @@ -99,7 +101,6 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list, const char defname[], char delim) { - NodeImageMultiFileSocket *sockdata; struct { ListBase *lb; bNodeSocket *sock; @@ -108,11 +109,11 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list, data.sock = sock; /* See if we are given an empty string */ - if (ELEM(NULL, sock, defname)) { + if (ELEM(nullptr, sock, defname)) { return; } - sockdata = sock->storage; + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; BLI_uniquename_cb( unique_layer_unique_check, &data, defname, delim, sockdata->layer, sizeof(sockdata->layer)); } @@ -122,12 +123,13 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, const char *name, ImageFormatData *im_format) { - NodeImageMultiFile *nimf = node->storage; - bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name); + NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; + bNodeSocket *sock = nodeAddStaticSocket( + ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, nullptr, name); /* create format data for the input socket */ - NodeImageMultiFileSocket *sockdata = MEM_callocN(sizeof(NodeImageMultiFileSocket), - "socket image format"); + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)MEM_callocN( + sizeof(NodeImageMultiFileSocket), "socket image format"); sock->storage = sockdata; BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path)); @@ -155,8 +157,8 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node) { - NodeImageMultiFile *nimf = node->storage; - bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input); + NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; + bNodeSocket *sock = (bNodeSocket *)BLI_findlink(&node->inputs, nimf->active_input); int totinputs = BLI_listbase_count(&node->inputs); if (!sock) { @@ -176,14 +178,14 @@ int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node) void ntreeCompositOutputFileSetPath(bNode *node, bNodeSocket *sock, const char *name) { - NodeImageMultiFileSocket *sockdata = sock->storage; + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path)); ntreeCompositOutputFileUniquePath(&node->inputs, sock, name, '_'); } void ntreeCompositOutputFileSetLayer(bNode *node, bNodeSocket *sock, const char *name) { - NodeImageMultiFileSocket *sockdata = sock->storage; + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; BLI_strncpy_utf8(sockdata->layer, name, sizeof(sockdata->layer)); ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_'); } @@ -193,9 +195,10 @@ static void init_output_file(const bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = ptr->data; - NodeImageMultiFile *nimf = MEM_callocN(sizeof(NodeImageMultiFile), "node image multi file"); - ImageFormatData *format = NULL; + bNode *node = (bNode *)ptr->data; + NodeImageMultiFile *nimf = (NodeImageMultiFile *)MEM_callocN(sizeof(NodeImageMultiFile), + "node image multi file"); + ImageFormatData *format = nullptr; node->storage = nimf; if (scene) { @@ -219,10 +222,8 @@ static void init_output_file(const bContext *C, PointerRNA *ptr) static void free_output_file(bNode *node) { - bNodeSocket *sock; - /* free storage data in sockets */ - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { MEM_freeN(sock->storage); } @@ -238,37 +239,35 @@ static void copy_output_file(bNodeTree *UNUSED(dest_ntree), dest_node->storage = MEM_dupallocN(src_node->storage); /* duplicate storage data in sockets */ - for (src_sock = src_node->inputs.first, dest_sock = dest_node->inputs.first; + for (src_sock = (bNodeSocket *)src_node->inputs.first, + dest_sock = (bNodeSocket *)dest_node->inputs.first; src_sock && dest_sock; - src_sock = src_sock->next, dest_sock = dest_sock->next) { + src_sock = src_sock->next, dest_sock = (bNodeSocket *)dest_sock->next) { dest_sock->storage = MEM_dupallocN(src_sock->storage); } } static void update_output_file(bNodeTree *ntree, bNode *node) { - bNodeSocket *sock, *sock_next; PointerRNA ptr; /* XXX fix for T36706: remove invalid sockets added with bpy API. * This is not ideal, but prevents crashes from missing storage. * FileOutput node needs a redesign to support this properly. */ - for (sock = node->inputs.first; sock; sock = sock_next) { - sock_next = sock->next; - if (sock->storage == NULL) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (sock->storage == nullptr) { nodeRemoveSocket(ntree, node, sock); } } - for (sock = node->outputs.first; sock; sock = sock_next) { - sock_next = sock->next; + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { nodeRemoveSocket(ntree, node, sock); } cmp_node_update_default(ntree, node); /* automatically update the socket type based on linked input */ - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { if (sock->link) { RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); RNA_enum_set(&ptr, "type", sock->link->fromsock->type); @@ -281,7 +280,7 @@ void register_node_type_cmp_output_file(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_OUTPUT_FILE, "File Output", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, NULL, NULL); + node_type_socket_templates(&ntype, nullptr, nullptr); ntype.initfunc_api = init_output_file; node_type_storage(&ntype, "NodeImageMultiFile", free_output_file, copy_output_file); node_type_update(&ntype, update_output_file); diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.c b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_pixelate.c rename to source/blender/nodes/composite/nodes/node_composite_pixelate.cc index 6e8a28df76f..19975c21a0b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_pixelate.c +++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Pixelate ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc similarity index 90% rename from source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c rename to source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc index ab5db41e5b5..e122b710b7b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c +++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_planetrackdeform_in[] = { {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, {-1, ""}}; @@ -34,8 +34,8 @@ static bNodeSocketTemplate cmp_node_planetrackdeform_out[] = { static void init(bNodeTree *UNUSED(ntree), bNode *node) { - NodePlaneTrackDeformData *data = MEM_callocN(sizeof(NodePlaneTrackDeformData), - "node plane track deform data"); + NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)MEM_callocN( + sizeof(NodePlaneTrackDeformData), "node plane track deform data"); data->motion_blur_samples = 16; data->motion_blur_shutter = 0.5f; node->storage = data; diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.c b/source/blender/nodes/composite/nodes/node_composite_posterize.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_posterize.c rename to source/blender/nodes/composite/nodes/node_composite_posterize.cc index 5093e581cdc..45a98e68b4b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_posterize.c +++ b/source/blender/nodes/composite/nodes/node_composite_posterize.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Posterize ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.c b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_premulkey.c rename to source/blender/nodes/composite/nodes/node_composite_premulkey.cc index be76bbf01cf..68716ee53b5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_premulkey.c +++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Premul and Key Alpha Convert ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.c b/source/blender/nodes/composite/nodes/node_composite_rgb.cc similarity index 92% rename from source/blender/nodes/composite/nodes/node_composite_rgb.c rename to source/blender/nodes/composite/nodes/node_composite_rgb.cc index dae63f7a702..c9c3dfcb019 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rgb.c +++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** RGB ******************** */ static bNodeSocketTemplate cmp_node_rgb_out[] = { @@ -34,7 +34,7 @@ void register_node_type_cmp_rgb(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_rgb_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_rgb_out); node_type_size_preset(&ntype, NODE_SIZE_SMALL); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_rotate.c b/source/blender/nodes/composite/nodes/node_composite_rotate.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_rotate.c rename to source/blender/nodes/composite/nodes/node_composite_rotate.cc index 7dd39d5eaa1..d28b35ec9fb 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rotate.c +++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Rotate ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.c b/source/blender/nodes/composite/nodes/node_composite_scale.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_scale.c rename to source/blender/nodes/composite/nodes/node_composite_scale.cc index 963832de03a..3972fc0d949 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.c +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Scale ******************** */ @@ -38,7 +38,7 @@ static void node_composite_update_scale(bNodeTree *UNUSED(ntree), bNode *node) bool use_xy_scale = ELEM(node->custom1, CMP_SCALE_RELATIVE, CMP_SCALE_ABSOLUTE); /* Only show X/Y scale factor inputs for modes using them! */ - for (sock = node->inputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { if (STR_ELEM(sock->name, "X", "Y")) { if (use_xy_scale) { sock->flag &= ~SOCK_UNAVAIL; diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c rename to source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc index 001b197e23a..17b1ab9ac59 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SEPARATE HSVA ******************** */ static bNodeSocketTemplate cmp_node_sephsva_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c rename to source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc index e08f27db254..d3a021ed7ba 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SEPARATE RGBA ******************** */ static bNodeSocketTemplate cmp_node_seprgba_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c rename to source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc index b3884296600..2090841b3b9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SEPARATE YCCA ******************** */ static bNodeSocketTemplate cmp_node_sepycca_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c rename to source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc index 4da79ec7981..59982b81468 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SEPARATE YUVA ******************** */ static bNodeSocketTemplate cmp_node_sepyuva_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.c b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_setalpha.c rename to source/blender/nodes/composite/nodes/node_composite_setalpha.cc index 1b44cc011e9..a2089bd0913 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.c +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SET ALPHA ******************** */ static bNodeSocketTemplate cmp_node_setalpha_in[] = { @@ -36,7 +36,7 @@ static bNodeSocketTemplate cmp_node_setalpha_out[] = { static void node_composit_init_setalpha(bNodeTree *UNUSED(ntree), bNode *node) { - NodeSetAlpha *settings = MEM_callocN(sizeof(NodeSetAlpha), __func__); + NodeSetAlpha *settings = (NodeSetAlpha *)MEM_callocN(sizeof(NodeSetAlpha), __func__); node->storage = settings; settings->mode = CMP_NODE_SETALPHA_MODE_APPLY; } diff --git a/source/blender/nodes/composite/nodes/node_composite_splitViewer.c b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc similarity index 88% rename from source/blender/nodes/composite/nodes/node_composite_splitViewer.c rename to source/blender/nodes/composite/nodes/node_composite_splitViewer.cc index 8afb3fd4841..54a1c59fca6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_splitViewer.c +++ b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_global.h" #include "BKE_image.h" @@ -35,7 +35,7 @@ static bNodeSocketTemplate cmp_node_splitviewer_in[] = { static void node_composit_init_splitviewer(bNodeTree *UNUSED(ntree), bNode *node) { - ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user"); + ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->sfra = 1; iuser->ok = 1; @@ -50,12 +50,12 @@ void register_node_type_cmp_splitviewer(void) cmp_node_type_base( &ntype, CMP_NODE_SPLITVIEWER, "Split Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_splitviewer_in, NULL); + node_type_socket_templates(&ntype, cmp_node_splitviewer_in, nullptr); node_type_init(&ntype, node_composit_init_splitviewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); /* Do not allow muting for this node. */ - node_type_internal_links(&ntype, NULL); + node_type_internal_links(&ntype, nullptr); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.c b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc similarity index 96% rename from source/blender/nodes/composite/nodes/node_composite_stabilize2d.c rename to source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc index b89f245c542..e5ce2e8ceb9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.c +++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_context.h" #include "BKE_lib_id.h" @@ -40,7 +40,7 @@ static bNodeSocketTemplate cmp_node_stabilize2d_out[] = { static void init(const bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; Scene *scene = CTX_data_scene(C); node->id = (ID *)scene->clip; diff --git a/source/blender/nodes/composite/nodes/node_composite_sunbeams.c b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc similarity index 92% rename from source/blender/nodes/composite/nodes/node_composite_sunbeams.c rename to source/blender/nodes/composite/nodes/node_composite_sunbeams.cc index 84ab2d30d34..73907d2e27f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sunbeams.c +++ b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate inputs[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, @@ -34,11 +34,10 @@ static bNodeSocketTemplate outputs[] = { static void init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeSunBeams *data = MEM_callocN(sizeof(NodeSunBeams), "sun beams node"); + NodeSunBeams *data = (NodeSunBeams *)MEM_callocN(sizeof(NodeSunBeams), "sun beams node"); data->source[0] = 0.5f; data->source[1] = 0.5f; - node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_switch.c b/source/blender/nodes/composite/nodes/node_composite_switch.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_switch.c rename to source/blender/nodes/composite/nodes/node_composite_switch.cc index efbb3390e06..71226a6da0b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switch.c +++ b/source/blender/nodes/composite/nodes/node_composite_switch.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** MIX RGB ******************** */ static bNodeSocketTemplate cmp_node_switch_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.cc similarity index 78% rename from source/blender/nodes/composite/nodes/node_composite_switchview.c rename to source/blender/nodes/composite/nodes/node_composite_switchview.cc index b09d5119bc4..a61712f7f8d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switchview.c +++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc @@ -25,7 +25,7 @@ #include "BKE_context.h" #include "BKE_lib_id.h" -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** SWITCH VIEW ******************** */ static bNodeSocketTemplate cmp_node_switch_view_out[] = { @@ -37,26 +37,23 @@ static bNodeSocket *ntreeCompositSwitchViewAddSocket(bNodeTree *ntree, bNode *node, const char *name) { - bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name); + bNodeSocket *sock = nodeAddStaticSocket( + ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, nullptr, name); return sock; } static void cmp_node_switch_view_sanitycheck(bNodeTree *ntree, bNode *node) { - bNodeSocket *sock; - if (!BLI_listbase_is_empty(&node->inputs)) { return; } - sock = ntreeCompositSwitchViewAddSocket(ntree, node, "No View"); + bNodeSocket *sock = ntreeCompositSwitchViewAddSocket(ntree, node, "No View"); sock->flag |= SOCK_HIDDEN; } static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *sock; - SceneRenderView *srv; Scene *scene = (Scene *)node->id; /* only update when called from the operator button */ @@ -64,7 +61,7 @@ static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node) return; } - if (scene == NULL) { + if (scene == nullptr) { nodeRemoveAllSockets(ntree, node); /* make sure there is always one socket */ cmp_node_switch_view_sanitycheck(ntree, node); @@ -72,11 +69,12 @@ static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node) } /* remove the views that were removed */ - sock = node->inputs.last; + bNodeSocket *sock = (bNodeSocket *)node->inputs.last; while (sock) { - srv = BLI_findstring(&scene->r.views, sock->name, offsetof(SceneRenderView, name)); + SceneRenderView *srv = (SceneRenderView *)BLI_findstring( + &scene->r.views, sock->name, offsetof(SceneRenderView, name)); - if (srv == NULL) { + if (srv == nullptr) { bNodeSocket *sock_del = sock; sock = sock->prev; nodeRemoveSocket(ntree, node, sock_del); @@ -94,10 +92,10 @@ static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node) } /* add the new views */ - for (srv = scene->r.views.first; srv; srv = srv->next) { - sock = BLI_findstring(&node->inputs, srv->name, offsetof(bNodeSocket, name)); + LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) { + sock = (bNodeSocket *)BLI_findstring(&node->inputs, srv->name, offsetof(bNodeSocket, name)); - if (sock == NULL) { + if (sock == nullptr) { sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); } @@ -117,10 +115,7 @@ static void init_switch_view(const bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = ptr->data; - SceneRenderView *srv; - bNodeSocket *sock; - int nr; + bNode *node = (bNode *)ptr->data; /* store scene for updates */ node->id = (ID *)scene; @@ -129,8 +124,8 @@ static void init_switch_view(const bContext *C, PointerRNA *ptr) if (scene) { RenderData *rd = &scene->r; - for (nr = 0, srv = rd->views.first; srv; srv = srv->next, nr++) { - sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); + LISTBASE_FOREACH (SceneRenderView *, srv, &rd->views) { + bNodeSocket *sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); if (srv->viewflag & SCE_VIEW_DISABLE) { sock->flag |= SOCK_HIDDEN; @@ -147,7 +142,7 @@ void register_node_type_cmp_switch_view(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_switch_view_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_switch_view_out); ntype.initfunc_api = init_switch_view; diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.c b/source/blender/nodes/composite/nodes/node_composite_texture.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_texture.c rename to source/blender/nodes/composite/nodes/node_composite_texture.cc index 50be05fe5a6..4421abdf998 100644 --- a/source/blender/nodes/composite/nodes/node_composite_texture.c +++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** TEXTURE ******************** */ static bNodeSocketTemplate cmp_node_texture_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.c b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_tonemap.c rename to source/blender/nodes/composite/nodes/node_composite_tonemap.cc index 5fc86c997f5..29984e3687c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_tonemap.c +++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_tonemap_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, @@ -34,7 +34,7 @@ static bNodeSocketTemplate cmp_node_tonemap_out[] = { static void node_composit_init_tonemap(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTonemap *ntm = MEM_callocN(sizeof(NodeTonemap), "node tonemap data"); + NodeTonemap *ntm = (NodeTonemap *)MEM_callocN(sizeof(NodeTonemap), "node tonemap data"); ntm->type = 1; ntm->key = 0.18; ntm->offset = 1; diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.c b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc similarity index 84% rename from source/blender/nodes/composite/nodes/node_composite_trackpos.c rename to source/blender/nodes/composite/nodes/node_composite_trackpos.cc index d59ce9b8b7a..32bf1b5c0d1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_trackpos.c +++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_trackpos_out[] = { {SOCK_FLOAT, N_("X")}, @@ -32,7 +32,8 @@ static bNodeSocketTemplate cmp_node_trackpos_out[] = { static void init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTrackPosData *data = MEM_callocN(sizeof(NodeTrackPosData), "node track position data"); + NodeTrackPosData *data = (NodeTrackPosData *)MEM_callocN(sizeof(NodeTrackPosData), + "node track position data"); node->storage = data; } @@ -42,7 +43,7 @@ void register_node_type_cmp_trackpos(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_TRACKPOS, "Track Position", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_trackpos_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_trackpos_out); node_type_init(&ntype, init); node_type_storage( &ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.c b/source/blender/nodes/composite/nodes/node_composite_transform.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_transform.c rename to source/blender/nodes/composite/nodes/node_composite_transform.cc index be526c1059c..1695101cdbf 100644 --- a/source/blender/nodes/composite/nodes/node_composite_transform.c +++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Transform ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.c b/source/blender/nodes/composite/nodes/node_composite_translate.cc similarity index 89% rename from source/blender/nodes/composite/nodes/node_composite_translate.c rename to source/blender/nodes/composite/nodes/node_composite_translate.cc index 43337ec6f7e..0ee8a41a5ea 100644 --- a/source/blender/nodes/composite/nodes/node_composite_translate.c +++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -38,7 +38,8 @@ static bNodeSocketTemplate cmp_node_translate_out[] = { static void node_composit_init_translate(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTranslateData *data = MEM_callocN(sizeof(NodeTranslateData), "node translate data"); + NodeTranslateData *data = (NodeTranslateData *)MEM_callocN(sizeof(NodeTranslateData), + "node translate data"); node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_valToRgb.c rename to source/blender/nodes/composite/nodes/node_composite_valToRgb.cc index ed6dbfa2bf3..ece52dea269 100644 --- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c +++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** VALTORGB ******************** */ static bNodeSocketTemplate cmp_node_valtorgb_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_value.c b/source/blender/nodes/composite/nodes/node_composite_value.cc similarity index 92% rename from source/blender/nodes/composite/nodes/node_composite_value.c rename to source/blender/nodes/composite/nodes/node_composite_value.cc index 2ede2cb8c83..7aab78e6d91 100644 --- a/source/blender/nodes/composite/nodes/node_composite_value.c +++ b/source/blender/nodes/composite/nodes/node_composite_value.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** VALUE ******************** */ static bNodeSocketTemplate cmp_node_value_out[] = { @@ -34,7 +34,7 @@ void register_node_type_cmp_value(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_value_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_value_out); node_type_size_preset(&ntype, NODE_SIZE_SMALL); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_vecBlur.c b/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_vecBlur.c rename to source/blender/nodes/composite/nodes/node_composite_vecBlur.cc index 7005ea42afe..c34fdcabc0a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_vecBlur.c +++ b/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** VECTOR BLUR ******************** */ static bNodeSocketTemplate cmp_node_vecblur_in[] = { @@ -33,7 +33,7 @@ static bNodeSocketTemplate cmp_node_vecblur_out[] = {{SOCK_RGBA, N_("Image")}, { static void node_composit_init_vecblur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBlurData *nbd = MEM_callocN(sizeof(NodeBlurData), "node blur data"); + NodeBlurData *nbd = (NodeBlurData *)MEM_callocN(sizeof(NodeBlurData), "node blur data"); node->storage = nbd; nbd->samples = 32; nbd->fac = 1.0f; diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.c b/source/blender/nodes/composite/nodes/node_composite_viewer.cc similarity index 88% rename from source/blender/nodes/composite/nodes/node_composite_viewer.c rename to source/blender/nodes/composite/nodes/node_composite_viewer.cc index b5f74d5c937..a73a7c81357 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.c +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_global.h" #include "BKE_image.h" @@ -35,7 +35,7 @@ static bNodeSocketTemplate cmp_node_viewer_in[] = { static void node_composit_init_viewer(bNodeTree *UNUSED(ntree), bNode *node) { - ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user"); + ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->sfra = 1; iuser->ok = 1; @@ -50,11 +50,11 @@ void register_node_type_cmp_viewer(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_viewer_in, NULL); + node_type_socket_templates(&ntype, cmp_node_viewer_in, nullptr); node_type_init(&ntype, node_composit_init_viewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); - node_type_internal_links(&ntype, NULL); + node_type_internal_links(&ntype, nullptr); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.c b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_zcombine.c rename to source/blender/nodes/composite/nodes/node_composite_zcombine.cc index 5041b16c303..990f9fcd366 100644 --- a/source/blender/nodes/composite/nodes/node_composite_zcombine.c +++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Z COMBINE ******************** */ /* lazy coder NOTE: node->custom2 is abused to send signal. */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index a8d2e000108..ad04416613a 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -373,13 +373,6 @@ void WM_init(bContext *C, int argc, const char **argv) BLI_strncpy(G.lib, BKE_main_blendfile_path_from_global(), sizeof(G.lib)); -#ifdef WITH_COMPOSITOR - if (1) { - extern void *COM_linker_hack; - COM_linker_hack = COM_execute; - } -#endif - wm_homefile_read_post(C, params_file_read_post); } From 5cdb2aadfcc7906050e163c48e88a005cfd42089 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 28 Sep 2021 16:53:24 -0300 Subject: [PATCH 0319/1500] Fix flag wrongly set in 'BM_face_split_edgenet_connect_islands' Sometimes the `use_partial_connect` option could trigger the assert: ``` BLI_assert(!BM_elem_flag_test(l_iter->v, VERT_NOT_IN_STACK)); ``` This can happen when `v_delimit->e` is not part of edgenet, so `v_other` will not have the flag. --- source/blender/bmesh/intern/bmesh_polygon_edgenet.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c index 86a7d8153f0..3ce572077b8 100644 --- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c +++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c @@ -1135,11 +1135,13 @@ static BMVert *bm_face_split_edgenet_partial_connect(BMesh *bm, BMVert *v_delimi BMVert *v_other = BM_edge_other_vert(e_face_init ? e_face_init : v_delimit->e, v_delimit); BLI_SMALLSTACK_PUSH(search, v_other); - BM_elem_flag_disable(v_other, VERT_NOT_IN_STACK); + if (BM_elem_flag_test(v_other, VERT_NOT_IN_STACK)) { + BM_elem_flag_disable(v_other, VERT_NOT_IN_STACK); + BLI_linklist_prepend_alloca(&vert_stack, v_other); + } while ((v_other = BLI_SMALLSTACK_POP(search))) { BLI_assert(BM_elem_flag_test(v_other, VERT_NOT_IN_STACK) == false); - BLI_linklist_prepend_alloca(&vert_stack, v_other); BMEdge *e_iter = v_other->e; do { BMVert *v_step = BM_edge_other_vert(e_iter, v_other); @@ -1147,6 +1149,7 @@ static BMVert *bm_face_split_edgenet_partial_connect(BMesh *bm, BMVert *v_delimi if (BM_elem_flag_test(v_step, VERT_NOT_IN_STACK)) { BM_elem_flag_disable(v_step, VERT_NOT_IN_STACK); BLI_SMALLSTACK_PUSH(search, v_step); + BLI_linklist_prepend_alloca(&vert_stack, v_other); } } } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_other)) != v_other->e); From 2ecd963d87e4f5215d1d86e7f1c22ab7833697f3 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 28 Sep 2021 16:57:49 -0300 Subject: [PATCH 0320/1500] Fix error in previous commit `v_other` -> `v_step` --- source/blender/bmesh/intern/bmesh_polygon_edgenet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c index 3ce572077b8..103d7621f87 100644 --- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c +++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c @@ -1149,7 +1149,7 @@ static BMVert *bm_face_split_edgenet_partial_connect(BMesh *bm, BMVert *v_delimi if (BM_elem_flag_test(v_step, VERT_NOT_IN_STACK)) { BM_elem_flag_disable(v_step, VERT_NOT_IN_STACK); BLI_SMALLSTACK_PUSH(search, v_step); - BLI_linklist_prepend_alloca(&vert_stack, v_other); + BLI_linklist_prepend_alloca(&vert_stack, v_step); } } } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_other)) != v_other->e); From 76377f0176b9561a7fc8f46b4ed704c631ddd90d Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 28 Sep 2021 19:32:49 +0200 Subject: [PATCH 0321/1500] Compositor: Replace resolution concept by canvas This is a code refactor in preparation of supporting canvas compositing. See {D12466}. No functional changes, all canvases are at (0,0) position matching tiled implementation. Differential Revision: https://developer.blender.org/D12465 --- source/blender/compositor/COM_defines.h | 3 +- .../compositor/intern/COM_BufferOperation.cc | 7 +- .../compositor/intern/COM_Converter.cc | 22 ++--- .../blender/compositor/intern/COM_Converter.h | 6 +- source/blender/compositor/intern/COM_Debug.cc | 4 +- .../intern/COM_FullFrameExecutionModel.cc | 10 +- .../compositor/intern/COM_NodeOperation.cc | 85 ++++++++--------- .../compositor/intern/COM_NodeOperation.h | 92 ++++++++----------- .../intern/COM_NodeOperationBuilder.cc | 25 +++-- .../intern/COM_NodeOperationBuilder.h | 4 +- .../compositor/nodes/COM_AlphaOverNode.cc | 6 +- .../compositor/nodes/COM_CombineColorNode.cc | 8 +- .../blender/compositor/nodes/COM_GlareNode.cc | 2 +- .../COM_HueSaturationValueCorrectNode.cc | 2 +- .../nodes/COM_HueSaturationValueNode.cc | 2 +- .../blender/compositor/nodes/COM_MapUVNode.cc | 2 +- .../compositor/nodes/COM_SetAlphaNode.cc | 2 +- .../compositor/nodes/COM_ViewerNode.cc | 4 +- .../operations/COM_BlurBaseOperation.cc | 23 +++-- .../operations/COM_BlurBaseOperation.h | 3 +- .../operations/COM_BokehBlurOperation.cc | 30 +++--- .../operations/COM_BokehBlurOperation.h | 3 +- .../operations/COM_BokehImageOperation.cc | 10 +- .../operations/COM_BokehImageOperation.h | 3 +- .../operations/COM_CalculateMeanOperation.cc | 6 +- .../COM_ColorBalanceASCCDLOperation.cc | 2 +- .../COM_ColorBalanceLGGOperation.cc | 2 +- .../operations/COM_ColorCurveOperation.cc | 4 +- .../operations/COM_CompositorOperation.cc | 13 +-- .../operations/COM_CompositorOperation.h | 3 +- .../operations/COM_ConstantOperation.cc | 4 +- .../operations/COM_ConstantOperation.h | 2 +- .../operations/COM_ConvertOperation.cc | 2 +- .../COM_ConvolutionFilterOperation.cc | 2 +- .../operations/COM_CropOperation.cc | 9 +- .../compositor/operations/COM_CropOperation.h | 3 +- .../operations/COM_DenoiseOperation.cc | 5 +- .../operations/COM_DespeckleOperation.cc | 2 +- .../COM_DirectionalBlurOperation.cc | 5 +- .../operations/COM_DisplaceOperation.cc | 5 +- .../operations/COM_DisplaceSimpleOperation.cc | 5 +- .../operations/COM_DotproductOperation.cc | 2 +- .../operations/COM_DoubleEdgeMaskOperation.cc | 5 +- .../COM_FastGaussianBlurOperation.cc | 10 +- .../operations/COM_FlipOperation.cc | 2 +- .../operations/COM_GlareThresholdOperation.cc | 11 ++- .../operations/COM_GlareThresholdOperation.h | 3 +- .../operations/COM_ImageOperation.cc | 11 +-- .../operations/COM_ImageOperation.h | 3 +- .../operations/COM_InpaintOperation.cc | 5 +- .../operations/COM_InvertOperation.cc | 2 +- .../operations/COM_KeyingScreenOperation.cc | 12 +-- .../operations/COM_KeyingScreenOperation.h | 3 +- .../operations/COM_MapUVOperation.cc | 19 ++-- .../operations/COM_MapUVOperation.h | 2 + .../operations/COM_MaskOperation.cc | 17 +--- .../compositor/operations/COM_MaskOperation.h | 3 +- .../operations/COM_MathBaseOperation.cc | 17 ++-- .../operations/COM_MathBaseOperation.h | 3 +- .../compositor/operations/COM_MixOperation.cc | 22 ++--- .../compositor/operations/COM_MixOperation.h | 3 +- .../COM_MovieClipAttributeOperation.cc | 10 +- .../COM_MovieClipAttributeOperation.h | 3 +- .../operations/COM_MovieClipOperation.cc | 12 +-- .../operations/COM_MovieClipOperation.h | 3 +- .../COM_MovieDistortionOperation.cc | 16 ++-- .../operations/COM_NormalizeOperation.cc | 6 +- .../operations/COM_OutputFileOperation.cc | 2 +- .../operations/COM_PixelateOperation.cc | 2 +- .../operations/COM_PlaneCornerPinOperation.cc | 14 ++- .../operations/COM_PlaneCornerPinOperation.h | 3 +- .../COM_PlaneDistortCommonOperation.cc | 5 +- .../operations/COM_PlaneTrackOperation.cc | 12 +-- .../operations/COM_PlaneTrackOperation.h | 23 ++--- .../operations/COM_PreviewOperation.cc | 15 ++- .../operations/COM_PreviewOperation.h | 3 +- .../COM_ProjectorLensDistortionOperation.cc | 2 +- .../operations/COM_ReadBufferOperation.cc | 9 +- .../operations/COM_ReadBufferOperation.h | 3 +- .../operations/COM_RenderLayersProg.cc | 9 +- .../operations/COM_RenderLayersProg.h | 3 +- .../operations/COM_RotateOperation.cc | 5 +- .../operations/COM_ScaleOperation.cc | 19 ++-- .../operations/COM_ScaleOperation.h | 3 +- .../COM_ScreenLensDistortionOperation.cc | 7 +- .../operations/COM_SetColorOperation.cc | 6 +- .../operations/COM_SetColorOperation.h | 3 +- .../operations/COM_SetValueOperation.cc | 6 +- .../operations/COM_SetValueOperation.h | 4 +- .../operations/COM_SetVectorOperation.cc | 6 +- .../operations/COM_SetVectorOperation.h | 3 +- .../operations/COM_SplitOperation.cc | 12 +-- .../operations/COM_SplitOperation.h | 3 +- .../operations/COM_SunBeamsOperation.cc | 2 +- .../operations/COM_TextureOperation.cc | 26 ++---- .../operations/COM_TextureOperation.h | 3 +- .../operations/COM_TonemapOperation.cc | 6 +- .../operations/COM_TrackPositionOperation.cc | 6 +- .../operations/COM_TrackPositionOperation.h | 3 +- .../operations/COM_TransformOperation.cc | 2 +- .../operations/COM_TranslateOperation.cc | 2 +- .../COM_VariableSizeBokehBlurOperation.cc | 19 ++-- .../COM_VariableSizeBokehBlurOperation.h | 3 +- .../operations/COM_VectorBlurOperation.cc | 5 +- .../operations/COM_ViewerOperation.cc | 10 +- .../operations/COM_ViewerOperation.h | 3 +- .../operations/COM_WrapOperation.cc | 4 +- .../operations/COM_WriteBufferOperation.cc | 15 ++- .../operations/COM_WriteBufferOperation.h | 3 +- 109 files changed, 389 insertions(+), 542 deletions(-) diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 73c4343a230..e0f23fbede3 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -121,7 +121,8 @@ constexpr float COM_PREVIEW_SIZE = 140.f; constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f; constexpr float COM_BLUR_BOKEH_PIXELS = 512; -constexpr rcti COM_SINGLE_ELEM_AREA = {0, 1, 0, 1}; +constexpr rcti COM_AREA_NONE = {0, 0, 0, 0}; +constexpr rcti COM_CONSTANT_INPUT_AREA_OF_INTEREST = COM_AREA_NONE; constexpr IndexRange XRange(const rcti &area) { diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc index cafdff89c8e..c6530cf6bd1 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.cc +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -24,12 +24,7 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) { buffer_ = buffer; inflated_buffer_ = nullptr; - /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following - * code to: set_resolution(buffer.get_size()) */ - unsigned int resolution[2]; - resolution[0] = buffer->getWidth(); - resolution[1] = buffer->getHeight(); - setResolution(resolution); + set_canvas(buffer->get_rect()); addOutputSocket(data_type); flags.is_constant_operation = buffer_->is_a_single_elem(); flags.is_fullframe_operation = false; diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index 4b103c21c75..bd05a8e4ef0 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -460,9 +460,9 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, const Node return nullptr; } -void COM_convert_resolution(NodeOperationBuilder &builder, - NodeOperationOutput *fromSocket, - NodeOperationInput *toSocket) +void COM_convert_canvas(NodeOperationBuilder &builder, + NodeOperationOutput *fromSocket, + NodeOperationInput *toSocket) { /* Data type conversions are executed before resolutions to ensure convert operations have * resolution. This method have to ensure same datatypes are linked for new operations. */ @@ -535,10 +535,10 @@ void COM_convert_resolution(NodeOperationBuilder &builder, builder.addOperation(sxop); builder.addOperation(syop); - unsigned int resolution[2] = {fromOperation->getWidth(), fromOperation->getHeight()}; - scaleOperation->setResolution(resolution); - sxop->setResolution(resolution); - syop->setResolution(resolution); + const rcti &scale_canvas = fromOperation->get_canvas(); + scaleOperation->set_canvas(scale_canvas); + sxop->set_canvas(scale_canvas); + syop->set_canvas(scale_canvas); builder.addOperation(scaleOperation); } @@ -557,10 +557,10 @@ void COM_convert_resolution(NodeOperationBuilder &builder, builder.addOperation(xop); builder.addOperation(yop); - unsigned int resolution[2] = {toOperation->getWidth(), toOperation->getHeight()}; - translateOperation->setResolution(resolution); - xop->setResolution(resolution); - yop->setResolution(resolution); + const rcti &translate_canvas = toOperation->get_canvas(); + translateOperation->set_canvas(translate_canvas); + xop->set_canvas(translate_canvas); + yop->set_canvas(translate_canvas); builder.addOperation(translateOperation); if (doScale) { diff --git a/source/blender/compositor/intern/COM_Converter.h b/source/blender/compositor/intern/COM_Converter.h index 28136437103..7f0402d4e70 100644 --- a/source/blender/compositor/intern/COM_Converter.h +++ b/source/blender/compositor/intern/COM_Converter.h @@ -65,8 +65,8 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, * \note Conversion logic is implemented in this function. * \see InputSocketResizeMode for the possible conversions. */ -void COM_convert_resolution(NodeOperationBuilder &builder, - NodeOperationOutput *fromSocket, - NodeOperationInput *toSocket); +void COM_convert_canvas(NodeOperationBuilder &builder, + NodeOperationOutput *fromSocket, + NodeOperationInput *toSocket); } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index f2dcba65b7c..9e47aa9fcb5 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -162,8 +162,10 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system, len += snprintf(str + len, maxlen > len ? maxlen - len : 0, - "#%d (%u,%u)", + "#%d (%i,%i) (%u,%u)", operation->get_id(), + operation->get_canvas().xmin, + operation->get_canvas().ymin, operation->getWidth(), operation->getHeight()); diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index bd3a481d691..957bbe24e5f 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -87,12 +87,9 @@ Vector FullFrameExecutionModel::get_input_buffers(NodeOperation MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op) { - rcti op_rect; - BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight()); - const DataType data_type = op->getOutputSocket(0)->getDataType(); const bool is_a_single_elem = op->get_flags().is_constant_operation; - return new MemoryBuffer(data_type, op_rect, is_a_single_elem); + return new MemoryBuffer(data_type, op->get_canvas(), is_a_single_elem); } void FullFrameExecutionModel::render_operation(NodeOperation *op) @@ -199,12 +196,11 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op const int num_inputs = operation->getNumberOfInputSockets(); for (int i = 0; i < num_inputs; i++) { NodeOperation *input_op = operation->get_input_operation(i); - rcti input_op_rect, input_area; - BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight()); + rcti input_area; operation->get_area_of_interest(input_op, render_area, input_area); /* Ensure area of interest is within operation bounds, cropping areas outside. */ - BLI_rcti_isect(&input_area, &input_op_rect, &input_area); + BLI_rcti_isect(&input_area, &input_op->get_canvas(), &input_area); stack.append({input_op, input_area}); } diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 3bbd1b22d60..ff232efdb08 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -35,9 +35,8 @@ namespace blender::compositor { NodeOperation::NodeOperation() { - this->m_resolutionInputSocketIndex = 0; - this->m_width = 0; - this->m_height = 0; + canvas_input_index_ = 0; + canvas_ = COM_AREA_NONE; this->m_btree = nullptr; } @@ -48,7 +47,7 @@ NodeOperation::NodeOperation() */ std::optional NodeOperation::generate_hash() { - params_hash_ = get_default_hash_2(m_width, m_height); + params_hash_ = get_default_hash_2(canvas_.xmin, canvas_.xmax); /* Hash subclasses params. */ is_hash_output_params_implemented_ = true; @@ -57,7 +56,11 @@ std::optional NodeOperation::generate_hash() return std::nullopt; } - hash_param(getOutputSocket()->getDataType()); + hash_params(canvas_.ymin, canvas_.ymax); + if (m_outputs.size() > 0) { + BLI_assert(m_outputs.size() == 1); + hash_param(this->getOutputSocket()->getDataType()); + } NodeOperationHash hash; hash.params_hash_ = params_hash_; @@ -108,48 +111,46 @@ void NodeOperation::addOutputSocket(DataType datatype) m_outputs.append(NodeOperationOutput(this, datatype)); } -void NodeOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void NodeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - unsigned int used_resolution_index = 0; - if (m_resolutionInputSocketIndex == RESOLUTION_INPUT_ANY) { + unsigned int used_canvas_index = 0; + if (canvas_input_index_ == RESOLUTION_INPUT_ANY) { for (NodeOperationInput &input : m_inputs) { - unsigned int any_resolution[2] = {0, 0}; - input.determineResolution(any_resolution, preferredResolution); - if (any_resolution[0] * any_resolution[1] > 0) { - resolution[0] = any_resolution[0]; - resolution[1] = any_resolution[1]; + rcti any_area = COM_AREA_NONE; + const bool determined = input.determine_canvas(preferred_area, any_area); + if (determined) { + r_area = any_area; break; } - used_resolution_index += 1; + used_canvas_index += 1; } } - else if (m_resolutionInputSocketIndex < m_inputs.size()) { - NodeOperationInput &input = m_inputs[m_resolutionInputSocketIndex]; - input.determineResolution(resolution, preferredResolution); - used_resolution_index = m_resolutionInputSocketIndex; + else if (canvas_input_index_ < m_inputs.size()) { + NodeOperationInput &input = m_inputs[canvas_input_index_]; + input.determine_canvas(preferred_area, r_area); + used_canvas_index = canvas_input_index_; } - if (modify_determined_resolution_fn_) { - modify_determined_resolution_fn_(resolution); + if (modify_determined_canvas_fn_) { + modify_determined_canvas_fn_(r_area); } - unsigned int temp2[2] = {resolution[0], resolution[1]}; - unsigned int temp[2]; + rcti unused_area; + const rcti &local_preferred_area = r_area; for (unsigned int index = 0; index < m_inputs.size(); index++) { - if (index == used_resolution_index) { + if (index == used_canvas_index) { continue; } NodeOperationInput &input = m_inputs[index]; if (input.isConnected()) { - input.determineResolution(temp, temp2); + input.determine_canvas(local_preferred_area, unused_area); } } } -void NodeOperation::setResolutionInputSocketIndex(unsigned int index) +void NodeOperation::set_canvas_input_index(unsigned int index) { - this->m_resolutionInputSocketIndex = index; + this->canvas_input_index_ = index; } void NodeOperation::init_data() @@ -260,7 +261,7 @@ void NodeOperation::get_area_of_interest(const int input_idx, /* Non full-frame operations never implement this method. To ensure correctness assume * whole area is used. */ NodeOperation *input_op = getInputOperation(input_idx); - BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight()); + r_input_area = input_op->get_canvas(); } } @@ -420,12 +421,16 @@ SocketReader *NodeOperationInput::getReader() return nullptr; } -void NodeOperationInput::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +/** + * \return Whether canvas area could be determined. + */ +bool NodeOperationInput::determine_canvas(const rcti &preferred_area, rcti &r_area) { if (m_link) { - m_link->determineResolution(resolution, preferredResolution); + m_link->determine_canvas(preferred_area, r_area); + return !BLI_rcti_is_empty(&r_area); } + return false; } /****************** @@ -437,18 +442,16 @@ NodeOperationOutput::NodeOperationOutput(NodeOperation *op, DataType datatype) { } -void NodeOperationOutput::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void NodeOperationOutput::determine_canvas(const rcti &preferred_area, rcti &r_area) { NodeOperation &operation = getOperation(); - if (operation.get_flags().is_resolution_set) { - resolution[0] = operation.getWidth(); - resolution[1] = operation.getHeight(); + if (operation.get_flags().is_canvas_set) { + r_area = operation.get_canvas(); } else { - operation.determineResolution(resolution, preferredResolution); - if (resolution[0] > 0 && resolution[1] > 0) { - operation.setResolution(resolution); + operation.determine_canvas(preferred_area, r_area); + if (!BLI_rcti_is_empty(&r_area)) { + operation.set_canvas(r_area); } } } @@ -470,8 +473,8 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat if (node_operation_flags.use_viewer_border) { os << "view_border,"; } - if (node_operation_flags.is_resolution_set) { - os << "resolution_set,"; + if (node_operation_flags.is_canvas_set) { + os << "canvas_set,"; } if (node_operation_flags.is_set_operation) { os << "set_operation,"; diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index ef7cf319222..9f113b60345 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -130,7 +130,7 @@ class NodeOperationInput { SocketReader *getReader(); - void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]); + bool determine_canvas(const rcti &preferred_area, rcti &r_area); #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation") @@ -158,12 +158,7 @@ class NodeOperationOutput { return m_datatype; } - /** - * \brief determine the resolution of this data going through this socket - * \param resolution: the result of this operation - * \param preferredResolution: the preferable resolution as no resolution could be determined - */ - void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]); + void determine_canvas(const rcti &preferred_area, rcti &r_area); #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation") @@ -211,9 +206,9 @@ struct NodeOperationFlags { bool use_viewer_border : 1; /** - * Is the resolution of the operation set. + * Is the canvas of the operation set. */ - bool is_resolution_set : 1; + bool is_canvas_set : 1; /** * Is this a set operation (value, color, vector). @@ -257,7 +252,7 @@ struct NodeOperationFlags { open_cl = false; use_render_border = false; use_viewer_border = false; - is_resolution_set = false; + is_canvas_set = false; is_set_operation = false; is_read_buffer_operation = false; is_write_buffer_operation = false; @@ -324,11 +319,11 @@ class NodeOperation { bool is_hash_output_params_implemented_; /** - * \brief the index of the input socket that will be used to determine the resolution + * \brief the index of the input socket that will be used to determine the canvas */ - unsigned int m_resolutionInputSocketIndex; + unsigned int canvas_input_index_; - std::function modify_determined_resolution_fn_; + std::function modify_determined_canvas_fn_; /** * \brief mutex reference for very special node initializations @@ -352,15 +347,7 @@ class NodeOperation { */ eExecutionModel execution_model_; - /** - * Width of the output of this operation. - */ - unsigned int m_width; - - /** - * Height of the output of this operation. - */ - unsigned int m_height; + rcti canvas_; /** * Flags how to evaluate this operation. @@ -374,11 +361,6 @@ class NodeOperation { { } - void set_execution_model(const eExecutionModel model) - { - execution_model_ = model; - } - void set_name(const std::string name) { m_name = name; @@ -424,14 +406,7 @@ class NodeOperation { return getInputOperation(index); } - /** - * \brief determine the resolution of this node - * \note this method will not set the resolution, this is the responsibility of the caller - * \param resolution: the result of this operation - * \param preferredResolution: the preferable resolution as no resolution could be determined - */ - virtual void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]); + virtual void determine_canvas(const rcti &preferred_area, rcti &r_area); /** * \brief isOutputOperation determines whether this operation is an output of the @@ -453,6 +428,11 @@ class NodeOperation { return false; } + void set_execution_model(const eExecutionModel model) + { + execution_model_ = model; + } + void setbNodeTree(const bNodeTree *tree) { this->m_btree = tree; @@ -527,19 +507,19 @@ class NodeOperation { } virtual void deinitExecution(); - /** - * \brief set the resolution - * \param resolution: the resolution to set - */ - void setResolution(unsigned int resolution[2]) + void set_canvas(const rcti &canvas_area) { - if (!this->flags.is_resolution_set) { - this->m_width = resolution[0]; - this->m_height = resolution[1]; - this->flags.is_resolution_set = true; + if (!this->flags.is_canvas_set) { + canvas_ = canvas_area; + flags.is_canvas_set = true; } } + const rcti &get_canvas() const + { + return canvas_; + } + /** * \brief is this operation the active viewer output * user can select an ViewerNode to be active @@ -557,18 +537,18 @@ class NodeOperation { rcti *output); /** - * \brief set the index of the input socket that will determine the resolution of this + * \brief set the index of the input socket that will determine the canvas of this * operation \param index: the index to set */ - void setResolutionInputSocketIndex(unsigned int index); + void set_canvas_input_index(unsigned int index); /** - * Set a custom function to modify determined resolution from main input just before setting it - * as preferred resolution for the other inputs. + * Set a custom function to modify determined canvas from main input just before setting it + * as preferred for the other inputs. */ - void set_determined_resolution_modifier(std::function fn) + void set_determined_canvas_modifier(std::function fn) { - modify_determined_resolution_fn_ = fn; + modify_determined_canvas_fn_ = fn; } /** @@ -595,12 +575,12 @@ class NodeOperation { unsigned int getWidth() const { - return m_width; + return BLI_rcti_size_x(&canvas_); } unsigned int getHeight() const { - return m_height; + return BLI_rcti_size_y(&canvas_); } inline void readSampled(float result[4], float x, float y, PixelSampler sampler) @@ -699,13 +679,13 @@ class NodeOperation { void setWidth(unsigned int width) { - this->m_width = width; - this->flags.is_resolution_set = true; + canvas_.xmax = canvas_.xmin + width; + this->flags.is_canvas_set = true; } void setHeight(unsigned int height) { - this->m_height = height; - this->flags.is_resolution_set = true; + canvas_.ymax = canvas_.ymin + height; + this->flags.is_canvas_set = true; } SocketReader *getInputSocketReader(unsigned int inputSocketindex); NodeOperation *getInputOperation(unsigned int inputSocketindex); diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index b2cd76be2c3..3791d05602d 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -106,7 +106,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) folder.fold_operations(); } - determineResolutions(); + determine_canvases(); save_graphviz("compositor_prior_merging"); merge_equal_operations(); @@ -423,28 +423,27 @@ void NodeOperationBuilder::resolve_proxies() } } -void NodeOperationBuilder::determineResolutions() +void NodeOperationBuilder::determine_canvases() { - /* determine all resolutions of the operations (Width/Height) */ + /* Determine all canvas areas of the operations. */ + const rcti &preferred_area = COM_AREA_NONE; for (NodeOperation *op : m_operations) { if (op->isOutputOperation(m_context->isRendering()) && !op->get_flags().is_preview_operation) { - unsigned int resolution[2] = {0, 0}; - unsigned int preferredResolution[2] = {0, 0}; - op->determineResolution(resolution, preferredResolution); - op->setResolution(resolution); + rcti canvas = COM_AREA_NONE; + op->determine_canvas(preferred_area, canvas); + op->set_canvas(canvas); } } for (NodeOperation *op : m_operations) { if (op->isOutputOperation(m_context->isRendering()) && op->get_flags().is_preview_operation) { - unsigned int resolution[2] = {0, 0}; - unsigned int preferredResolution[2] = {0, 0}; - op->determineResolution(resolution, preferredResolution); - op->setResolution(resolution); + rcti canvas = COM_AREA_NONE; + op->determine_canvas(preferred_area, canvas); + op->set_canvas(canvas); } } - /* add convert resolution operations when needed */ + /* Convert operation canvases when needed. */ { Vector convert_links; for (const Link &link : m_links) { @@ -457,7 +456,7 @@ void NodeOperationBuilder::determineResolutions() } } for (const Link &link : convert_links) { - COM_convert_resolution(*this, link.from(), link.to()); + COM_convert_canvas(*this, link.from(), link.to()); } } } diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h index aca4d043d41..1f9c86b51cd 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h @@ -145,8 +145,8 @@ class NodeOperationBuilder { /** Replace proxy operations with direct links */ void resolve_proxies(); - /** Calculate resolution for each operation */ - void determineResolutions(); + /** Calculate canvas area for each operation. */ + void determine_canvases(); /** Helper function to store connected inputs for replacement */ Vector cache_output_links(NodeOperationOutput *output) const; diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.cc b/source/blender/compositor/nodes/COM_AlphaOverNode.cc index 5e09902aee2..c9038886b0d 100644 --- a/source/blender/compositor/nodes/COM_AlphaOverNode.cc +++ b/source/blender/compositor/nodes/COM_AlphaOverNode.cc @@ -51,13 +51,13 @@ void AlphaOverNode::convertToOperations(NodeConverter &converter, convertProg->setUseValueAlphaMultiply(false); if (color1Socket->isLinked()) { - convertProg->setResolutionInputSocketIndex(1); + convertProg->set_canvas_input_index(1); } else if (color2Socket->isLinked()) { - convertProg->setResolutionInputSocketIndex(2); + convertProg->set_canvas_input_index(2); } else { - convertProg->setResolutionInputSocketIndex(0); + convertProg->set_canvas_input_index(0); } converter.addOperation(convertProg); diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc index 8a2bbba1c1e..dd68780dc19 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.cc +++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc @@ -37,16 +37,16 @@ void CombineColorNode::convertToOperations(NodeConverter &converter, CombineChannelsOperation *operation = new CombineChannelsOperation(); if (inputRSocket->isLinked()) { - operation->setResolutionInputSocketIndex(0); + operation->set_canvas_input_index(0); } else if (inputGSocket->isLinked()) { - operation->setResolutionInputSocketIndex(1); + operation->set_canvas_input_index(1); } else if (inputBSocket->isLinked()) { - operation->setResolutionInputSocketIndex(2); + operation->set_canvas_input_index(2); } else { - operation->setResolutionInputSocketIndex(3); + operation->set_canvas_input_index(3); } converter.addOperation(operation); diff --git a/source/blender/compositor/nodes/COM_GlareNode.cc b/source/blender/compositor/nodes/COM_GlareNode.cc index cd0b5306be1..9c26d7c86a9 100644 --- a/source/blender/compositor/nodes/COM_GlareNode.cc +++ b/source/blender/compositor/nodes/COM_GlareNode.cc @@ -66,7 +66,7 @@ void GlareNode::convertToOperations(NodeConverter &converter, mixvalueoperation->setValue(glare->mix); MixGlareOperation *mixoperation = new MixGlareOperation(); - mixoperation->setResolutionInputSocketIndex(1); + mixoperation->set_canvas_input_index(1); mixoperation->getInputSocket(2)->setResizeMode(ResizeMode::FitAny); converter.addOperation(glareoperation); diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc index 5042d217f9a..e7b1664c354 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc +++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc @@ -53,7 +53,7 @@ void HueSaturationValueCorrectNode::convertToOperations( converter.addOperation(changeHSV); MixBlendOperation *blend = new MixBlendOperation(); - blend->setResolutionInputSocketIndex(1); + blend->set_canvas_input_index(1); converter.addOperation(blend); converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0)); diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc index 54d2caa75af..29e5f39a144 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc +++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc @@ -56,7 +56,7 @@ void HueSaturationValueNode::convertToOperations(NodeConverter &converter, converter.addOperation(changeHSV); MixBlendOperation *blend = new MixBlendOperation(); - blend->setResolutionInputSocketIndex(1); + blend->set_canvas_input_index(1); converter.addOperation(blend); converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0)); diff --git a/source/blender/compositor/nodes/COM_MapUVNode.cc b/source/blender/compositor/nodes/COM_MapUVNode.cc index 4b7a9e8af0f..bbf9e8f3aeb 100644 --- a/source/blender/compositor/nodes/COM_MapUVNode.cc +++ b/source/blender/compositor/nodes/COM_MapUVNode.cc @@ -34,7 +34,7 @@ void MapUVNode::convertToOperations(NodeConverter &converter, MapUVOperation *operation = new MapUVOperation(); operation->setAlpha((float)node->custom1); - operation->setResolutionInputSocketIndex(1); + operation->set_canvas_input_index(1); converter.addOperation(operation); converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.cc b/source/blender/compositor/nodes/COM_SetAlphaNode.cc index dc41c126ba8..c7365b09f71 100644 --- a/source/blender/compositor/nodes/COM_SetAlphaNode.cc +++ b/source/blender/compositor/nodes/COM_SetAlphaNode.cc @@ -39,7 +39,7 @@ void SetAlphaNode::convertToOperations(NodeConverter &converter, } if (!this->getInputSocket(0)->isLinked() && this->getInputSocket(1)->isLinked()) { - operation->setResolutionInputSocketIndex(1); + operation->set_canvas_input_index(1); } converter.addOperation(operation); diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cc b/source/blender/compositor/nodes/COM_ViewerNode.cc index 3833a8d7ca8..4dbcdbe9e40 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.cc +++ b/source/blender/compositor/nodes/COM_ViewerNode.cc @@ -60,10 +60,10 @@ void ViewerNode::convertToOperations(NodeConverter &converter, viewerOperation->setViewSettings(context.getViewSettings()); viewerOperation->setDisplaySettings(context.getDisplaySettings()); - viewerOperation->setResolutionInputSocketIndex(0); + viewerOperation->set_canvas_input_index(0); if (!imageSocket->isLinked()) { if (alphaSocket->isLinked()) { - viewerOperation->setResolutionInputSocketIndex(1); + viewerOperation->set_canvas_input_index(1); } } diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc index 058b422c4a5..2c162425f13 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc @@ -212,31 +212,30 @@ void BlurBaseOperation::updateSize() this->m_sizeavailable = true; } -void BlurBaseOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void BlurBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { if (!m_extend_bounds) { - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); return; } switch (execution_model_) { case eExecutionModel::Tiled: { - NodeOperation::determineResolution(resolution, preferredResolution); - resolution[0] += 2 * m_size * m_data.sizex; - resolution[1] += 2 * m_size * m_data.sizey; + NodeOperation::determine_canvas(preferred_area, r_area); + r_area.xmax += 2 * m_size * m_data.sizex; + r_area.ymax += 2 * m_size * m_data.sizey; break; } case eExecutionModel::FullFrame: { /* Setting a modifier ensures all non main inputs have extended bounds as preferred - * resolution, avoiding unnecessary resolution conversions that would hide constant + * canvas, avoiding unnecessary canvas convertions that would hide constant * operations. */ - set_determined_resolution_modifier([=](unsigned int res[2]) { + set_determined_canvas_modifier([=](rcti &canvas) { /* Rounding to even prevents jiggling in backdrop while switching size values. */ - res[0] += round_to_even(2 * m_size * m_data.sizex); - res[1] += round_to_even(2 * m_size * m_data.sizey); + canvas.xmax += round_to_even(2 * m_size * m_data.sizex); + canvas.ymax += round_to_even(2 * m_size * m_data.sizey); }); - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); break; } } @@ -251,7 +250,7 @@ void BlurBaseOperation::get_area_of_interest(const int input_idx, r_input_area = output_area; break; case 1: - r_input_area = use_variable_size_ ? output_area : COM_SINGLE_ELEM_AREA; + r_input_area = use_variable_size_ ? output_area : COM_CONSTANT_INPUT_AREA_OF_INTEREST; break; } } diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h index 78b1e919aa6..9ab9bf5a173 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h @@ -85,8 +85,7 @@ class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelpe int get_blur_size(eDimension dim) const; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; virtual void get_area_of_interest(int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc index f2a43b7dbca..3f61a300849 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc @@ -266,31 +266,30 @@ void BokehBlurOperation::updateSize() this->m_sizeavailable = true; } -void BokehBlurOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void BokehBlurOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { if (!m_extend_bounds) { - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); return; } switch (execution_model_) { case eExecutionModel::Tiled: { - NodeOperation::determineResolution(resolution, preferredResolution); - const float max_dim = MAX2(resolution[0], resolution[1]); - resolution[0] += 2 * this->m_size * max_dim / 100.0f; - resolution[1] += 2 * this->m_size * max_dim / 100.0f; + NodeOperation::determine_canvas(preferred_area, r_area); + const float max_dim = MAX2(BLI_rcti_size_x(&r_area), BLI_rcti_size_y(&r_area)); + r_area.xmax += 2 * this->m_size * max_dim / 100.0f; + r_area.ymax += 2 * this->m_size * max_dim / 100.0f; break; } case eExecutionModel::FullFrame: { - set_determined_resolution_modifier([=](unsigned int res[2]) { - const float max_dim = MAX2(res[0], res[1]); + set_determined_canvas_modifier([=](rcti &canvas) { + const float max_dim = MAX2(BLI_rcti_size_x(&canvas), BLI_rcti_size_y(&canvas)); /* Rounding to even prevents image jiggling in backdrop while switching size values. */ float add_size = round_to_even(2 * this->m_size * max_dim / 100.0f); - res[0] += add_size; - res[1] += add_size; + canvas.xmax += add_size; + canvas.ymax += add_size; }); - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); break; } } @@ -312,17 +311,14 @@ void BokehBlurOperation::get_area_of_interest(const int input_idx, } case BOKEH_INPUT_INDEX: { NodeOperation *bokeh_input = getInputOperation(BOKEH_INPUT_INDEX); - r_input_area.xmin = 0; - r_input_area.xmax = bokeh_input->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = bokeh_input->getHeight(); + r_input_area = bokeh_input->get_canvas(); break; } case BOUNDING_BOX_INPUT_INDEX: r_input_area = output_area; break; case SIZE_INPUT_INDEX: { - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; break; } } diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.h b/source/blender/compositor/operations/COM_BokehBlurOperation.h index 59c14305393..84f5a8293ba 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.h @@ -80,8 +80,7 @@ class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelp this->m_extend_bounds = extend_bounds; } - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) 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, diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc index bd5b25b5af8..5c9c8b36ee0 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.cc +++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc @@ -145,11 +145,13 @@ void BokehImageOperation::deinitExecution() } } -void BokehImageOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void BokehImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = COM_BLUR_BOKEH_PIXELS; - resolution[1] = COM_BLUR_BOKEH_PIXELS; + BLI_rcti_init(&r_area, + preferred_area.xmin, + preferred_area.xmin + COM_BLUR_BOKEH_PIXELS, + preferred_area.ymin, + preferred_area.ymin + COM_BLUR_BOKEH_PIXELS); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h index 2527233fabd..f7fec5d71a5 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.h +++ b/source/blender/compositor/operations/COM_BokehImageOperation.h @@ -128,8 +128,7 @@ class BokehImageOperation : public MultiThreadedOperation { * \brief determine the resolution of this operation. currently fixed at [COM_BLUR_BOKEH_PIXELS, * COM_BLUR_BOKEH_PIXELS] \param resolution: \param preferredResolution: */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; /** * \brief set the node data diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc index 7457ac9e227..5fff4da62ae 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc @@ -165,11 +165,7 @@ void CalculateMeanOperation::get_area_of_interest(int input_idx, rcti &r_input_area) { BLI_assert(input_idx == 0); - NodeOperation *operation = getInputOperation(input_idx); - r_input_area.xmin = 0; - r_input_area.ymin = 0; - r_input_area.xmax = operation->getWidth(); - r_input_area.ymax = operation->getHeight(); + r_input_area = get_input_operation(input_idx)->get_canvas(); } void CalculateMeanOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc index aee8c0d52e8..0b6590ae4c7 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc @@ -40,7 +40,7 @@ ColorBalanceASCCDLOperation::ColorBalanceASCCDLOperation() this->addOutputSocket(DataType::Color); this->m_inputValueOperation = nullptr; this->m_inputColorOperation = nullptr; - this->setResolutionInputSocketIndex(1); + this->set_canvas_input_index(1); flags.can_be_constant = true; } diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc index 674cb79a238..c658ecd6394 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc @@ -45,7 +45,7 @@ ColorBalanceLGGOperation::ColorBalanceLGGOperation() this->addOutputSocket(DataType::Color); this->m_inputValueOperation = nullptr; this->m_inputColorOperation = nullptr; - this->setResolutionInputSocketIndex(1); + this->set_canvas_input_index(1); flags.can_be_constant = true; } diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc index 646238460ba..364b310945e 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc @@ -37,7 +37,7 @@ ColorCurveOperation::ColorCurveOperation() this->m_inputBlackProgram = nullptr; this->m_inputWhiteProgram = nullptr; - this->setResolutionInputSocketIndex(1); + this->set_canvas_input_index(1); } void ColorCurveOperation::initExecution() { @@ -139,7 +139,7 @@ ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation() this->m_inputFacProgram = nullptr; this->m_inputImageProgram = nullptr; - this->setResolutionInputSocketIndex(1); + this->set_canvas_input_index(1); } void ConstantLevelColorCurveOperation::initExecution() { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index fb9e2e43c60..52bc9ed6c2f 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -236,8 +236,7 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp depth_buf.copy_from(inputs[2], area); } -void CompositorOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void CompositorOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { int width = this->m_rd->xsch * this->m_rd->size / 100; int height = this->m_rd->ysch * this->m_rd->size / 100; @@ -254,13 +253,11 @@ void CompositorOperation::determineResolution(unsigned int resolution[2], RE_ReleaseResult(re); } - preferredResolution[0] = width; - preferredResolution[1] = height; + rcti local_preferred; + BLI_rcti_init(&local_preferred, 0, width, 0, height); - NodeOperation::determineResolution(resolution, preferredResolution); - - resolution[0] = width; - resolution[1] = height; + NodeOperation::determine_canvas(local_preferred, r_area); + r_area = local_preferred; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index 66367ec8bae..6eb96e01b47 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -115,8 +115,7 @@ class CompositorOperation : public MultiThreadedOperation { { return eCompositorPriority::Medium; } - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void setUseAlphaInput(bool value) { this->m_useAlphaInput = value; diff --git a/source/blender/compositor/operations/COM_ConstantOperation.cc b/source/blender/compositor/operations/COM_ConstantOperation.cc index 33d51cca432..c127860a89c 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.cc +++ b/source/blender/compositor/operations/COM_ConstantOperation.cc @@ -22,14 +22,14 @@ namespace blender::compositor { ConstantOperation::ConstantOperation() { - needs_resolution_to_get_constant_ = false; + needs_canvas_to_get_constant_ = false; flags.is_constant_operation = true; flags.is_fullframe_operation = true; } bool ConstantOperation::can_get_constant_elem() const { - return !needs_resolution_to_get_constant_ || this->flags.is_resolution_set; + return !needs_canvas_to_get_constant_ || this->flags.is_canvas_set; } void ConstantOperation::update_memory_buffer(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConstantOperation.h b/source/blender/compositor/operations/COM_ConstantOperation.h index 31b8d30254b..d44d1939424 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.h +++ b/source/blender/compositor/operations/COM_ConstantOperation.h @@ -31,7 +31,7 @@ namespace blender::compositor { */ class ConstantOperation : public NodeOperation { protected: - bool needs_resolution_to_get_constant_; + bool needs_canvas_to_get_constant_; public: ConstantOperation(); diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 9a3733dda5b..7b2721ebbb2 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -587,7 +587,7 @@ CombineChannelsOperation::CombineChannelsOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_inputChannel1Operation = nullptr; this->m_inputChannel2Operation = nullptr; this->m_inputChannel3Operation = nullptr; diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc index 11a077229fd..807223fd45f 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc @@ -29,7 +29,7 @@ ConvolutionFilterOperation::ConvolutionFilterOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_inputOperation = nullptr; this->flags.complex = true; } diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc index 12833660fcb..73805b76864 100644 --- a/source/blender/compositor/operations/COM_CropOperation.cc +++ b/source/blender/compositor/operations/COM_CropOperation.cc @@ -142,13 +142,12 @@ void CropImageOperation::get_area_of_interest(const int input_idx, r_input_area.ymin = output_area.ymin + this->m_ymin; } -void CropImageOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void CropImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); updateArea(); - resolution[0] = this->m_xmax - this->m_xmin; - resolution[1] = this->m_ymax - this->m_ymin; + r_area.xmax = r_area.xmin + (m_xmax - m_xmin); + r_area.ymax = r_area.ymin + (m_ymax - m_ymin); } void CropImageOperation::executePixelSampled(float output[4], diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h index 57caa4e5834..a156727402b 100644 --- a/source/blender/compositor/operations/COM_CropOperation.h +++ b/source/blender/compositor/operations/COM_CropOperation.h @@ -66,8 +66,7 @@ class CropImageOperation : public CropBaseOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index 0c660e0b723..f8a575acc3a 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -153,10 +153,7 @@ void DenoiseBaseOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &UNUSED(output_area), rcti &r_input_area) { - r_input_area.xmin = 0; - r_input_area.xmax = this->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = this->getHeight(); + r_input_area = this->get_canvas(); } DenoiseOperation::DenoiseOperation() diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc index 19bd7b2af6f..df637ee6709 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.cc +++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc @@ -29,7 +29,7 @@ DespeckleOperation::DespeckleOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_inputOperation = nullptr; this->flags.complex = true; } diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc index 102025ed915..e69124205d0 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc @@ -152,10 +152,7 @@ void DirectionalBlurOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = 0; - r_input_area.xmax = this->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = this->getHeight(); + r_input_area = this->get_canvas(); } void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc index a4c01fda7ca..d08ff60d5d0 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc @@ -211,10 +211,7 @@ void DisplaceOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case 0: { - r_input_area.xmin = 0; - r_input_area.ymin = 0; - r_input_area.xmax = getInputOperation(input_idx)->getWidth(); - r_input_area.ymax = getInputOperation(input_idx)->getHeight(); + r_input_area = getInputOperation(input_idx)->get_canvas(); break; } case 1: { diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc index e1c531bd49e..712b61be805 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc @@ -138,10 +138,7 @@ void DisplaceSimpleOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case 0: { - r_input_area.xmin = 0; - r_input_area.ymin = 0; - r_input_area.xmax = getInputOperation(input_idx)->getWidth(); - r_input_area.ymax = getInputOperation(input_idx)->getHeight(); + r_input_area = get_input_operation(input_idx)->get_canvas(); break; } default: { diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc index 875b161e208..aa18ff1e827 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.cc +++ b/source/blender/compositor/operations/COM_DotproductOperation.cc @@ -25,7 +25,7 @@ DotproductOperation::DotproductOperation() this->addInputSocket(DataType::Vector); this->addInputSocket(DataType::Vector); this->addOutputSocket(DataType::Value); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_input1Operation = nullptr; this->m_input2Operation = nullptr; flags.can_be_constant = true; diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc index 55ae19ad194..d112334b749 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc @@ -1399,10 +1399,7 @@ void DoubleEdgeMaskOperation::get_area_of_interest(int UNUSED(input_idx), const rcti &UNUSED(output_area), rcti &r_input_area) { - r_input_area.xmax = this->getWidth(); - r_input_area.xmin = 0; - r_input_area.ymax = this->getHeight(); - r_input_area.ymin = 0; + r_input_area = this->get_canvas(); } void DoubleEdgeMaskOperation::update_memory_buffer(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index e0fc45811cb..f45b77c6ebc 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -271,10 +271,7 @@ void FastGaussianBlurOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: - r_input_area.xmin = 0; - r_input_area.xmax = getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = getHeight(); + r_input_area = this->get_canvas(); break; default: BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area); @@ -411,10 +408,7 @@ void FastGaussianBlurValueOperation::get_area_of_interest(const int UNUSED(input const rcti &UNUSED(output_area), rcti &r_input_area) { - r_input_area.xmin = 0; - r_input_area.xmax = getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = getHeight(); + r_input_area = this->get_canvas(); } void FastGaussianBlurValueOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc index d0dc6c0b570..c88fcaa7da2 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.cc +++ b/source/blender/compositor/operations/COM_FlipOperation.cc @@ -24,7 +24,7 @@ FlipOperation::FlipOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_inputOperation = nullptr; this->m_flipX = true; this->m_flipY = false; diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc index 1d3402f5b7b..f8da0b9a102 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc @@ -30,12 +30,13 @@ GlareThresholdOperation::GlareThresholdOperation() this->m_inputProgram = nullptr; } -void GlareThresholdOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void GlareThresholdOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - NodeOperation::determineResolution(resolution, preferredResolution); - resolution[0] = resolution[0] / (1 << this->m_settings->quality); - resolution[1] = resolution[1] / (1 << this->m_settings->quality); + NodeOperation::determine_canvas(preferred_area, r_area); + const int width = BLI_rcti_size_x(&r_area) / (1 << this->m_settings->quality); + const int height = BLI_rcti_size_y(&r_area) / (1 << this->m_settings->quality); + r_area.xmax = r_area.xmin + width; + r_area.ymax = r_area.ymin + height; } void GlareThresholdOperation::initExecution() diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h index a6e971dada7..1f247f58324 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h @@ -58,8 +58,7 @@ class GlareThresholdOperation : public NodeOperation { this->m_settings = settings; } - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ImageOperation.cc b/source/blender/compositor/operations/COM_ImageOperation.cc index e78d389410f..ff389093f5a 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cc +++ b/source/blender/compositor/operations/COM_ImageOperation.cc @@ -112,17 +112,14 @@ void BaseImageOperation::deinitExecution() } } -void BaseImageOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void BaseImageOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { ImBuf *stackbuf = getImBuf(); - resolution[0] = 0; - resolution[1] = 0; + r_area = COM_AREA_NONE; if (stackbuf) { - resolution[0] = stackbuf->x; - resolution[1] = stackbuf->y; + BLI_rcti_init(&r_area, 0, stackbuf->x, 0, stackbuf->y); } BKE_image_release_ibuf(this->m_image, stackbuf, nullptr); @@ -222,7 +219,7 @@ void ImageDepthOperation::executePixelSampled(float output[4], output[0] = 0.0f; } else { - int offset = y * this->m_width + x; + int offset = y * getWidth() + x; output[0] = this->m_depthBuffer[offset]; } } diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h index f8b4239c9f8..e2fdfbf6f81 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.h +++ b/source/blender/compositor/operations/COM_ImageOperation.h @@ -54,8 +54,7 @@ class BaseImageOperation : public MultiThreadedOperation { /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; virtual ImBuf *getImBuf(); diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc index 5e76c41752c..5d440dd7d76 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cc +++ b/source/blender/compositor/operations/COM_InpaintOperation.cc @@ -293,10 +293,7 @@ void InpaintSimpleOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = 0; - r_input_area.xmax = this->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = this->getHeight(); + r_input_area = this->get_canvas(); } void InpaintSimpleOperation::update_memory_buffer(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc index 4f71a1d0d1d..d406b809c7a 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.cc +++ b/source/blender/compositor/operations/COM_InvertOperation.cc @@ -29,7 +29,7 @@ InvertOperation::InvertOperation() this->m_inputColorProgram = nullptr; this->m_color = true; this->m_alpha = false; - setResolutionInputSocketIndex(1); + set_canvas_input_index(1); this->flags.can_be_constant = true; } void InvertOperation::initExecution() diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc index c00aafc19a2..b4840926d55 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc @@ -303,11 +303,9 @@ void KeyingScreenOperation::deinitializeTileData(rcti * /*rect*/, void *data) MEM_freeN(tile_data); } -void KeyingScreenOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void KeyingScreenOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = 0; - resolution[1] = 0; + r_area = COM_AREA_NONE; if (this->m_movieClip) { MovieClipUser user = {0}; @@ -317,9 +315,9 @@ void KeyingScreenOperation::determineResolution(unsigned int resolution[2], BKE_movieclip_user_set_frame(&user, clip_frame); BKE_movieclip_get_size(this->m_movieClip, &user, &width, &height); - - resolution[0] = width; - resolution[1] = height; + r_area = preferred_area; + r_area.xmax = r_area.xmin + width; + r_area.ymax = r_area.ymin + height; } } diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h index 0bc47dbea30..7a7dda27710 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h @@ -57,8 +57,7 @@ class KeyingScreenOperation : public MultiThreadedOperation { /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; TriangulationData *buildVoronoiTriangulation(); diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index ad047c619f8..5062b0c42cb 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -28,7 +28,7 @@ MapUVOperation::MapUVOperation() this->addOutputSocket(DataType::Color); this->m_alpha = 0.0f; this->flags.complex = true; - setResolutionInputSocketIndex(1); + set_canvas_input_index(UV_INPUT_INDEX); this->m_inputUVProgram = nullptr; this->m_inputColorProgram = nullptr; @@ -36,11 +36,11 @@ MapUVOperation::MapUVOperation() void MapUVOperation::init_data() { - NodeOperation *image_input = get_input_operation(0); + NodeOperation *image_input = get_input_operation(IMAGE_INPUT_INDEX); image_width_ = image_input->getWidth(); image_height_ = image_input->getHeight(); - NodeOperation *uv_input = get_input_operation(1); + NodeOperation *uv_input = get_input_operation(UV_INPUT_INDEX); uv_width_ = uv_input->getWidth(); uv_height_ = uv_input->getHeight(); } @@ -205,14 +205,11 @@ void MapUVOperation::get_area_of_interest(const int input_idx, rcti &r_input_area) { switch (input_idx) { - case 0: { - r_input_area.xmin = 0; - r_input_area.xmax = image_width_; - r_input_area.ymin = 0; - r_input_area.ymax = image_height_; + case IMAGE_INPUT_INDEX: { + r_input_area = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas(); break; } - case 1: { + case UV_INPUT_INDEX: { r_input_area = output_area; expand_area_for_sampler(r_input_area, PixelSampler::Bilinear); break; @@ -224,7 +221,7 @@ void MapUVOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span inputs) { - const MemoryBuffer *uv_input = inputs[1]; + const MemoryBuffer *uv_input = inputs[UV_INPUT_INDEX]; uv_input_read_fn_ = [=](float x, float y, float *out) { uv_input->read_elem_bilinear(x, y, out); }; @@ -234,7 +231,7 @@ void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const MemoryBuffer *input_image = inputs[0]; + const MemoryBuffer *input_image = inputs[IMAGE_INPUT_INDEX]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { float xy[2] = {(float)it.x, (float)it.y}; float uv[2]; diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h index 65fbcb461c9..49c6689f700 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.h +++ b/source/blender/compositor/operations/COM_MapUVOperation.h @@ -24,6 +24,8 @@ namespace blender::compositor { class MapUVOperation : public MultiThreadedOperation { private: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int UV_INPUT_INDEX = 1; /** * Cached reference to the inputProgram */ diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc index 84992f23924..65b89a8c79a 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cc +++ b/source/blender/compositor/operations/COM_MaskOperation.cc @@ -109,22 +109,15 @@ void MaskOperation::deinitExecution() } } -void MaskOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void MaskOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { if (this->m_maskWidth == 0 || this->m_maskHeight == 0) { - NodeOperation::determineResolution(resolution, preferredResolution); + r_area = COM_AREA_NONE; } else { - unsigned int nr[2]; - - nr[0] = this->m_maskWidth; - nr[1] = this->m_maskHeight; - - NodeOperation::determineResolution(resolution, nr); - - resolution[0] = this->m_maskWidth; - resolution[1] = this->m_maskHeight; + r_area = preferred_area; + r_area.xmax = r_area.xmin + m_maskWidth; + r_area.ymax = r_area.ymin + m_maskHeight; } } diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h index 81e344c0451..cc7eb0c022c 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.h +++ b/source/blender/compositor/operations/COM_MaskOperation.h @@ -54,8 +54,7 @@ class MaskOperation : public MultiThreadedOperation { /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; public: MaskOperation(); diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc index 2256dce011b..d3fb83caf7c 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.cc +++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc @@ -51,22 +51,19 @@ void MathBaseOperation::deinitExecution() this->m_inputValue3Operation = nullptr; } -void MathBaseOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void MathBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { NodeOperationInput *socket; - unsigned int tempPreferredResolution[2] = {0, 0}; - unsigned int tempResolution[2]; - + rcti temp_area; socket = this->getInputSocket(0); - socket->determineResolution(tempResolution, tempPreferredResolution); - if ((tempResolution[0] != 0) && (tempResolution[1] != 0)) { - this->setResolutionInputSocketIndex(0); + const bool determined = socket->determine_canvas(COM_AREA_NONE, temp_area); + if (determined) { + this->set_canvas_input_index(0); } else { - this->setResolutionInputSocketIndex(1); + this->set_canvas_input_index(1); } - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); } void MathBaseOperation::clampIfNeeded(float *color) diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h index d2da05db68e..0188eb50fa8 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.h +++ b/source/blender/compositor/operations/COM_MathBaseOperation.h @@ -75,8 +75,7 @@ class MathBaseOperation : public MultiThreadedOperation { /** * Determine resolution */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void setUseClamp(bool value) { diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc index 77ecbf60356..4b9f4786e79 100644 --- a/source/blender/compositor/operations/COM_MixOperation.cc +++ b/source/blender/compositor/operations/COM_MixOperation.cc @@ -66,29 +66,27 @@ void MixBaseOperation::executePixelSampled(float output[4], float x, float y, Pi output[3] = inputColor1[3]; } -void MixBaseOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void MixBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { NodeOperationInput *socket; - unsigned int tempPreferredResolution[2] = {0, 0}; - unsigned int tempResolution[2]; + rcti temp_area; socket = this->getInputSocket(1); - socket->determineResolution(tempResolution, tempPreferredResolution); - if ((tempResolution[0] != 0) && (tempResolution[1] != 0)) { - this->setResolutionInputSocketIndex(1); + bool determined = socket->determine_canvas(COM_AREA_NONE, temp_area); + if (determined) { + this->set_canvas_input_index(1); } else { socket = this->getInputSocket(2); - socket->determineResolution(tempResolution, tempPreferredResolution); - if ((tempResolution[0] != 0) && (tempResolution[1] != 0)) { - this->setResolutionInputSocketIndex(2); + determined = socket->determine_canvas(COM_AREA_NONE, temp_area); + if (determined) { + this->set_canvas_input_index(2); } else { - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); } } - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); } void MixBaseOperation::deinitExecution() diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h index 7ef9d78d58f..fabbea422eb 100644 --- a/source/blender/compositor/operations/COM_MixOperation.h +++ b/source/blender/compositor/operations/COM_MixOperation.h @@ -87,8 +87,7 @@ class MixBaseOperation : public MultiThreadedOperation { */ void deinitExecution() override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void setUseValueAlphaMultiply(const bool value) { diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc index e36e93984fb..5654ea0425d 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc @@ -29,7 +29,7 @@ MovieClipAttributeOperation::MovieClipAttributeOperation() this->m_framenumber = 0; this->m_attribute = MCA_X; this->m_invert = false; - needs_resolution_to_get_constant_ = true; + needs_canvas_to_get_constant_ = true; is_value_calculated_ = false; } @@ -42,7 +42,7 @@ void MovieClipAttributeOperation::initExecution() void MovieClipAttributeOperation::calc_value() { - BLI_assert(this->get_flags().is_resolution_set); + BLI_assert(this->get_flags().is_canvas_set); is_value_calculated_ = true; if (this->m_clip == nullptr) { return; @@ -87,11 +87,9 @@ void MovieClipAttributeOperation::executePixelSampled(float output[4], output[0] = this->m_value; } -void MovieClipAttributeOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void MovieClipAttributeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } const float *MovieClipAttributeOperation::get_constant_elem() diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h index 28c39d4dad3..e42605e5026 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h @@ -55,8 +55,7 @@ class MovieClipAttributeOperation : public ConstantOperation { * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; const float *get_constant_elem() override; diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc index e520b928edf..1ca33b12432 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc @@ -71,19 +71,13 @@ void MovieClipBaseOperation::deinitExecution() } } -void MovieClipBaseOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void MovieClipBaseOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - resolution[0] = 0; - resolution[1] = 0; - + r_area = COM_AREA_NONE; if (this->m_movieClip) { int width, height; - BKE_movieclip_get_size(this->m_movieClip, this->m_movieClipUser, &width, &height); - - resolution[0] = width; - resolution[1] = height; + BLI_rcti_init(&r_area, 0, width, 0, height); } } diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.h b/source/blender/compositor/operations/COM_MovieClipOperation.h index 0a0c4c00f81..465ccd4fc00 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipOperation.h @@ -41,8 +41,7 @@ class MovieClipBaseOperation : public MultiThreadedOperation { /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; public: MovieClipBaseOperation(); diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc index d3424959061..72162ffb110 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc @@ -29,7 +29,7 @@ MovieDistortionOperation::MovieDistortionOperation(bool distortion) { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_inputOperation = nullptr; this->m_movieClip = nullptr; this->m_apply = distortion; @@ -49,10 +49,10 @@ void MovieDistortionOperation::initExecution() float delta[2]; rcti full_frame; full_frame.xmin = full_frame.ymin = 0; - full_frame.xmax = this->m_width; - full_frame.ymax = this->m_height; + full_frame.xmax = this->getWidth(); + full_frame.ymax = this->getHeight(); BKE_tracking_max_distortion_delta_across_bound( - tracking, this->m_width, this->m_height, &full_frame, !this->m_apply, delta); + tracking, this->getWidth(), this->getHeight(), &full_frame, !this->m_apply, delta); /* 5 is just in case we didn't hit real max of distortion in * BKE_tracking_max_undistortion_delta_across_bound @@ -89,8 +89,8 @@ void MovieDistortionOperation::executePixelSampled(float output[4], if (this->m_distortion != nullptr) { /* float overscan = 0.0f; */ const float pixel_aspect = this->m_pixel_aspect; - const float w = (float)this->m_width /* / (1 + overscan) */; - const float h = (float)this->m_height /* / (1 + overscan) */; + const float w = (float)this->getWidth() /* / (1 + overscan) */; + const float h = (float)this->getHeight() /* / (1 + overscan) */; const float aspx = w / (float)this->m_calibration_width; const float aspy = h / (float)this->m_calibration_height; float in[2]; @@ -152,8 +152,8 @@ void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output /* `float overscan = 0.0f;` */ const float pixel_aspect = this->m_pixel_aspect; - const float w = (float)this->m_width /* `/ (1 + overscan)` */; - const float h = (float)this->m_height /* `/ (1 + overscan)` */; + const float w = (float)this->getWidth() /* `/ (1 + overscan)` */; + const float h = (float)this->getHeight() /* `/ (1 + overscan)` */; const float aspx = w / (float)this->m_calibration_width; const float aspy = h / (float)this->m_calibration_height; float xy[2]; diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc index c3e72d2575f..7d79b375b45 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.cc +++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc @@ -133,11 +133,7 @@ void NormalizeOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &UNUSED(output_area), rcti &r_input_area) { - NodeOperation *input = get_input_operation(0); - r_input_area.xmin = 0; - r_input_area.xmax = input->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = input->getHeight(); + r_input_area = get_input_operation(0)->get_canvas(); } void NormalizeOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 402d29893a4..79be95bb686 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -339,7 +339,7 @@ OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene * this->m_exr_codec = exr_codec; this->m_exr_half_float = exr_half_float; this->m_viewName = viewName; - this->setResolutionInputSocketIndex(RESOLUTION_INPUT_ANY); + this->set_canvas_input_index(RESOLUTION_INPUT_ANY); } void OutputOpenExrMultiLayerOperation::add_layer(const char *name, diff --git a/source/blender/compositor/operations/COM_PixelateOperation.cc b/source/blender/compositor/operations/COM_PixelateOperation.cc index 94827cd1b02..4fc238bc094 100644 --- a/source/blender/compositor/operations/COM_PixelateOperation.cc +++ b/source/blender/compositor/operations/COM_PixelateOperation.cc @@ -24,7 +24,7 @@ PixelateOperation::PixelateOperation(DataType datatype) { this->addInputSocket(datatype); this->addOutputSocket(datatype); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_inputOperation = nullptr; } diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc index d2a06ddd7c4..65cd08456ef 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc @@ -209,15 +209,13 @@ void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect) return data; } -void PlaneCornerPinMaskOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void PlaneCornerPinMaskOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { if (execution_model_ == eExecutionModel::FullFrame) { - /* Determine inputs resolution. */ - PlaneDistortMaskOperation::determineResolution(resolution, preferredResolution); + /* Determine input canvases. */ + PlaneDistortMaskOperation::determine_canvas(preferred_area, r_area); } - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -225,7 +223,7 @@ void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_id rcti &r_input_area) { /* All corner inputs are used as constants. */ - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; } /* ******** PlaneCornerPinWarpImageOperation ******** */ @@ -322,7 +320,7 @@ void PlaneCornerPinWarpImageOperation::get_area_of_interest(const int input_idx, } else { /* Corner inputs are used as constants. */ - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; } } diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h index 2831e937147..7b3917923d2 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h @@ -43,8 +43,7 @@ class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation { void *initializeTileData(rcti *rect) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; }; diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index ccabb3cf11c..ab45899b7f5 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -196,10 +196,7 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, } /* TODO: figure out the area needed for warping and EWA filtering. */ - r_input_area.xmin = 0; - r_input_area.ymin = 0; - r_input_area.xmax = get_input_operation(0)->getWidth(); - r_input_area.ymax = get_input_operation(0)->getHeight(); + r_input_area = get_input_operation(0)->get_canvas(); /* Old implementation but resulting coordinates are way out of input operation bounds and in some * cases the area result may incorrectly cause cropping. */ diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc index bf24f843ca2..7226a133a52 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc @@ -83,19 +83,17 @@ void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) } } -void PlaneTrackCommon::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void PlaneTrackCommon::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = 0; - resolution[1] = 0; - + r_area = COM_AREA_NONE; if (this->m_movieClip) { int width, height; MovieClipUser user = {0}; BKE_movieclip_user_set_frame(&user, this->m_framenumber); BKE_movieclip_get_size(this->m_movieClip, &user, &width, &height); - resolution[0] = width; - resolution[1] = height; + r_area = preferred_area; + r_area.xmax = r_area.xmin + width; + r_area.ymax = r_area.ymin + height; } } diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h index d2027755162..60845aac514 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h @@ -41,7 +41,7 @@ class PlaneTrackCommon { * implementation classes must make wrappers to use these methods, see below. */ void read_and_calculate_corners(PlaneDistortBaseOperation *distort_op); - void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]); + void determine_canvas(const rcti &preferred_area, rcti &r_area); public: PlaneTrackCommon(); @@ -77,13 +77,13 @@ class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTr void initExecution() override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override + void determine_canvas(const rcti &preferred_area, rcti &r_area) override { - PlaneTrackCommon::determineResolution(resolution, preferredResolution); + PlaneTrackCommon::determine_canvas(preferred_area, r_area); - unsigned int temp[2]; - NodeOperation::determineResolution(temp, resolution); + rcti unused; + rcti &preferred = r_area; + NodeOperation::determine_canvas(preferred, unused); } }; @@ -98,12 +98,13 @@ class PlaneTrackWarpImageOperation : public PlaneDistortWarpImageOperation, void initExecution() override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override + void determine_canvas(const rcti &preferred_area, rcti &r_area) override { - PlaneTrackCommon::determineResolution(resolution, preferredResolution); - unsigned int temp[2]; - NodeOperation::determineResolution(temp, resolution); + PlaneTrackCommon::determine_canvas(preferred_area, r_area); + + rcti unused; + rcti &preferred = r_area; + NodeOperation::determine_canvas(preferred, unused); } }; diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc index fa8b5ffcabf..7b1dd89bd75 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cc +++ b/source/blender/compositor/operations/COM_PreviewOperation.cc @@ -130,14 +130,14 @@ bool PreviewOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } -void PreviewOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { /* Use default preview resolution as preferred ensuring it has size so that * generated inputs (which don't have resolution on their own) are displayed */ BLI_assert(this->m_defaultWidth > 0 && this->m_defaultHeight > 0); - unsigned int previewPreferredRes[2] = {this->m_defaultWidth, this->m_defaultHeight}; - NodeOperation::determineResolution(resolution, previewPreferredRes); + rcti local_preferred; + BLI_rcti_init(&local_preferred, 0, m_defaultWidth, 0, m_defaultHeight); + NodeOperation::determine_canvas(local_preferred, r_area); /* If resolution is 0 there are two possible scenarios: * - Either node is not connected at all @@ -148,8 +148,8 @@ void PreviewOperation::determineResolution(unsigned int resolution[2], * The latter case would only happen if an input doesn't set any resolution ignoring output * preferred resolution. In such case preview size will be 0 too. */ - int width = resolution[0]; - int height = resolution[1]; + int width = BLI_rcti_size_x(&r_area); + int height = BLI_rcti_size_y(&r_area); this->m_divider = 0.0f; if (width > 0 && height > 0) { if (width > height) { @@ -162,8 +162,7 @@ void PreviewOperation::determineResolution(unsigned int resolution[2], width = width * this->m_divider; height = height * this->m_divider; - resolution[0] = width; - resolution[1] = height; + BLI_rcti_init(&r_area, 0, width, 0, height); } eCompositorPriority PreviewOperation::getRenderPriority() const diff --git a/source/blender/compositor/operations/COM_PreviewOperation.h b/source/blender/compositor/operations/COM_PreviewOperation.h index 05dae9c4dd8..4bd0f07d882 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.h +++ b/source/blender/compositor/operations/COM_PreviewOperation.h @@ -58,8 +58,7 @@ class PreviewOperation : public MultiThreadedOperation { eCompositorPriority getRenderPriority() const override; void executeRegion(rcti *rect, unsigned int tileNumber) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc index fcab5dd5751..faebaf657cc 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc @@ -137,7 +137,7 @@ void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx, { if (input_idx == 1) { /* Dispersion input is used as constant only. */ - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; return; } diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cc b/source/blender/compositor/operations/COM_ReadBufferOperation.cc index d35d2516f16..599370751bb 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.cc +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cc @@ -36,16 +36,17 @@ void *ReadBufferOperation::initializeTileData(rcti * /*rect*/) return m_buffer; } -void ReadBufferOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void ReadBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { if (this->m_memoryProxy != nullptr) { WriteBufferOperation *operation = this->m_memoryProxy->getWriteBufferOperation(); - operation->determineResolution(resolution, preferredResolution); - operation->setResolution(resolution); + operation->determine_canvas(preferred_area, r_area); + operation->set_canvas(r_area); /** \todo may not occur! But does with blur node. */ if (this->m_memoryProxy->getExecutor()) { + uint resolution[2] = {static_cast(BLI_rcti_size_x(&r_area)), + static_cast(BLI_rcti_size_y(&r_area))}; this->m_memoryProxy->getExecutor()->setResolution(resolution); } diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.h b/source/blender/compositor/operations/COM_ReadBufferOperation.h index 8b96b961a43..02f6eccd246 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.h +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.h @@ -43,8 +43,7 @@ class ReadBufferOperation : public NodeOperation { return this->m_memoryProxy; } - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void *initializeTileData(rcti *rect) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc index 72e2c92c9cf..2ac551ffe6f 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cc +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc @@ -196,15 +196,13 @@ void RenderLayersProg::deinitExecution() } } -void RenderLayersProg::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void RenderLayersProg::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { Scene *sce = this->getScene(); Render *re = (sce) ? RE_GetSceneRender(sce) : nullptr; RenderResult *rr = nullptr; - resolution[0] = 0; - resolution[1] = 0; + r_area = COM_AREA_NONE; if (re) { rr = RE_AcquireResultRead(re); @@ -215,8 +213,7 @@ void RenderLayersProg::determineResolution(unsigned int resolution[2], if (view_layer) { RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name); if (rl) { - resolution[0] = rl->rectx; - resolution[1] = rl->recty; + BLI_rcti_init(&r_area, 0, rl->rectx, 0, rl->recty); } } } diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h index dd76a56d645..b499afd45df 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.h +++ b/source/blender/compositor/operations/COM_RenderLayersProg.h @@ -73,8 +73,7 @@ class RenderLayersProg : public MultiThreadedOperation { /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; /** * retrieve the reference to the float buffer of the renderer. diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc index e3c482c15cb..8578e5c3269 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.cc +++ b/source/blender/compositor/operations/COM_RotateOperation.cc @@ -28,7 +28,7 @@ RotateOperation::RotateOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_imageSocket = nullptr; this->m_degreeSocket = nullptr; this->m_doDegree2RadConversion = false; @@ -164,8 +164,7 @@ void RotateOperation::get_area_of_interest(const int input_idx, rcti &r_input_area) { if (input_idx == DEGREE_INPUT_INDEX) { - /* Degrees input is always used as constant. */ - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; return; } diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index 0161b837915..f5a423ea8e3 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -48,7 +48,7 @@ ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(data_type); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_inputOperation = nullptr; this->m_inputXOperation = nullptr; this->m_inputYOperation = nullptr; @@ -267,7 +267,7 @@ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation() { this->addInputSocket(DataType::Color, ResizeMode::None); this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_inputOperation = nullptr; this->m_is_offset = false; } @@ -366,15 +366,14 @@ bool ScaleFixedSizeOperation::determineDependingAreaOfInterest(rcti *input, return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } -void ScaleFixedSizeOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void ScaleFixedSizeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - unsigned int nr[2]; - nr[0] = this->m_newWidth; - nr[1] = this->m_newHeight; - BaseScaleOperation::determineResolution(resolution, nr); - resolution[0] = this->m_newWidth; - resolution[1] = this->m_newHeight; + rcti local_preferred = preferred_area; + local_preferred.xmax = local_preferred.xmin + m_newWidth; + local_preferred.ymax = local_preferred.ymin + m_newHeight; + BaseScaleOperation::determine_canvas(local_preferred, r_area); + r_area.xmax = r_area.xmin + m_newWidth; + r_area.ymax = r_area.ymin + m_newHeight; } void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx, diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h index 65762d1ce62..04fa4fe62d1 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.h +++ b/source/blender/compositor/operations/COM_ScaleOperation.h @@ -141,8 +141,7 @@ class ScaleFixedSizeOperation : public BaseScaleOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; void init_data() override; diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index 628da686a42..87949a1b24f 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -388,7 +388,7 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, { if (input_idx != 0) { /* Dispersion and distortion inputs are used as constants only. */ - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; } /* XXX the original method of estimating the area-of-interest does not work @@ -398,10 +398,7 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, */ #if 1 NodeOperation *image = getInputOperation(0); - r_input_area.xmax = image->getWidth(); - r_input_area.xmin = 0; - r_input_area.ymax = image->getHeight(); - r_input_area.ymin = 0; + r_input_area = image->get_canvas(); #else /* Original method in tiled implementation. */ rcti newInput; diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index dbe45fa60db..1e7a950e727 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -34,11 +34,9 @@ void SetColorOperation::executePixelSampled(float output[4], copy_v4_v4(output, this->m_color); } -void SetColorOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void SetColorOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h index f546d5e7668..34ea35bdcbc 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.h +++ b/source/blender/compositor/operations/COM_SetColorOperation.h @@ -83,8 +83,7 @@ class SetColorOperation : public ConstantOperation { */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index ef43cf64653..b7c50f94887 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -34,11 +34,9 @@ void SetValueOperation::executePixelSampled(float output[4], output[0] = this->m_value; } -void SetValueOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void SetValueOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h index 726624c1c6a..e72652450a9 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.h +++ b/source/blender/compositor/operations/COM_SetValueOperation.h @@ -54,8 +54,8 @@ class SetValueOperation : public ConstantOperation { * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.cc b/source/blender/compositor/operations/COM_SetVectorOperation.cc index 7b8cf44048c..3e8514f1f59 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.cc +++ b/source/blender/compositor/operations/COM_SetVectorOperation.cc @@ -37,11 +37,9 @@ void SetVectorOperation::executePixelSampled(float output[4], output[2] = vector_.z; } -void SetVectorOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void SetVectorOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.h b/source/blender/compositor/operations/COM_SetVectorOperation.h index 41fd06659d6..920ea8051e4 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.h +++ b/source/blender/compositor/operations/COM_SetVectorOperation.h @@ -84,8 +84,7 @@ class SetVectorOperation : public ConstantOperation { */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void setVector(const float vector[3]) { diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc index b2a50e2c740..47b2bbb7e94 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.cc +++ b/source/blender/compositor/operations/COM_SplitOperation.cc @@ -67,16 +67,14 @@ void SplitOperation::executePixelSampled(float output[4], } } -void SplitOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void SplitOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - unsigned int tempPreferredResolution[2] = {0, 0}; - unsigned int tempResolution[2]; + rcti unused_area; - this->getInputSocket(0)->determineResolution(tempResolution, tempPreferredResolution); - this->setResolutionInputSocketIndex((tempResolution[0] && tempResolution[1]) ? 0 : 1); + const bool determined = this->getInputSocket(0)->determine_canvas(COM_AREA_NONE, unused_area); + this->set_canvas_input_index(determined ? 0 : 1); - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); } void SplitOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h index 2d09d2a07dc..f923c9f8b7a 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.h +++ b/source/blender/compositor/operations/COM_SplitOperation.h @@ -35,8 +35,7 @@ class SplitOperation : public MultiThreadedOperation { void initExecution() override; void deinitExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void setSplitPercentage(float splitPercentage) { this->m_splitPercentage = splitPercentage; diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index ad96e3a02ba..494506389c5 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -25,7 +25,7 @@ SunBeamsOperation::SunBeamsOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->flags.complex = true; } diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index c8e0844d35f..94977ed5adf 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -72,32 +72,26 @@ void TextureBaseOperation::deinitExecution() NodeOperation::deinitExecution(); } -void TextureBaseOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { switch (execution_model_) { case eExecutionModel::Tiled: { - if (preferredResolution[0] == 0 || preferredResolution[1] == 0) { + r_area = preferred_area; + if (BLI_rcti_is_empty(&preferred_area)) { int width = this->m_rd->xsch * this->m_rd->size / 100; int height = this->m_rd->ysch * this->m_rd->size / 100; - resolution[0] = width; - resolution[1] = height; - } - else { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area.xmax = preferred_area.xmin + width; + r_area.ymax = preferred_area.ymin + height; } break; } case eExecutionModel::FullFrame: { - /* Determine inputs resolutions. */ - unsigned int temp[2]; - NodeOperation::determineResolution(temp, preferredResolution); + /* Determine inputs. */ + rcti temp; + NodeOperation::determine_canvas(preferred_area, temp); - /* We don't use inputs resolutions because they are only used as parameters, not image data. - */ - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + /* Don't use input areas, they are only used as parameters. */ + r_area = preferred_area; break; } } diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index 1e95cb270d0..5bc21da1f96 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -46,8 +46,7 @@ class TextureBaseOperation : public MultiThreadedOperation { /** * Determine the output resolution. */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; /** * Constructor diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index 20da468eeb1..67eb26993d0 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -160,11 +160,7 @@ void TonemapOperation::get_area_of_interest(const int input_idx, rcti &r_input_area) { BLI_assert(input_idx == 0); - NodeOperation *operation = getInputOperation(input_idx); - r_input_area.xmin = 0; - r_input_area.ymin = 0; - r_input_area.xmax = operation->getWidth(); - r_input_area.ymax = operation->getHeight(); + r_input_area = get_input_operation(input_idx)->get_canvas(); } struct Luminance { diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index 0f4be16a620..1929c578177 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -157,11 +157,9 @@ const float *TrackPositionOperation::get_constant_elem() return &track_position_; } -void TrackPositionOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void TrackPositionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h index f716bd97737..a47d3e03ed4 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.h +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h @@ -53,8 +53,7 @@ class TrackPositionOperation : public ConstantOperation { /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; public: TrackPositionOperation(); diff --git a/source/blender/compositor/operations/COM_TransformOperation.cc b/source/blender/compositor/operations/COM_TransformOperation.cc index 2feaa0ae16d..f73c9e9d956 100644 --- a/source/blender/compositor/operations/COM_TransformOperation.cc +++ b/source/blender/compositor/operations/COM_TransformOperation.cc @@ -99,7 +99,7 @@ void TransformOperation::get_area_of_interest(const int input_idx, case X_INPUT_INDEX: case Y_INPUT_INDEX: case DEGREE_INPUT_INDEX: { - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; break; } case SCALE_INPUT_INDEX: { diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc index a3db086e974..266e960037c 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.cc +++ b/source/blender/compositor/operations/COM_TranslateOperation.cc @@ -29,7 +29,7 @@ TranslateOperation::TranslateOperation(DataType data_type) this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(data_type); - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); this->m_inputOperation = nullptr; this->m_inputXOperation = nullptr; this->m_inputYOperation = nullptr; diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc index b8274576cb5..0fe44e3a61f 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc @@ -77,7 +77,7 @@ void *VariableSizeBokehBlurOperation::initializeTileData(rcti *rect) this->determineDependingAreaOfInterest( rect, (ReadBufferOperation *)this->m_inputSizeProgram, &rect2); - const float max_dim = MAX2(m_width, m_height); + const float max_dim = MAX2(this->getWidth(), this->getHeight()); const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; data->maxBlurScalar = (int)(data->size->get_max_value(rect2) * scalar); @@ -105,7 +105,7 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, float multiplier_accum[4]; float color_accum[4]; - const float max_dim = MAX2(m_width, m_height); + const float max_dim = MAX2(getWidth(), getHeight()); const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; int maxBlurScalar = tileData->maxBlurScalar; @@ -125,8 +125,8 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, #else int minx = MAX2(x - maxBlurScalar, 0); int miny = MAX2(y - maxBlurScalar, 0); - int maxx = MIN2(x + maxBlurScalar, (int)m_width); - int maxy = MIN2(y + maxBlurScalar, (int)m_height); + int maxx = MIN2(x + maxBlurScalar, (int)getWidth()); + int maxy = MIN2(y + maxBlurScalar, (int)getHeight()); #endif { inputSizeBuffer->readNoCheck(tempSize, x, y); @@ -200,7 +200,7 @@ void VariableSizeBokehBlurOperation::executeOpenCL(OpenCLDevice *device, MemoryBuffer *sizeMemoryBuffer = this->m_inputSizeProgram->getInputMemoryBuffer( inputMemoryBuffers); - const float max_dim = MAX2(m_width, m_height); + const float max_dim = MAX2(getWidth(), getHeight()); cl_float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; maxBlur = (cl_int)min_ff(sizeMemoryBuffer->get_max_value() * scalar, (float)this->m_maxBlur); @@ -238,7 +238,7 @@ bool VariableSizeBokehBlurOperation::determineDependingAreaOfInterest( rcti newInput; rcti bokehInput; - const float max_dim = MAX2(m_width, m_height); + const float max_dim = MAX2(getWidth(), getHeight()); const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; int maxBlurScalar = this->m_maxBlur * scalar; @@ -294,10 +294,9 @@ void VariableSizeBokehBlurOperation::get_area_of_interest(const int input_idx, break; } case BOKEH_INPUT_INDEX: { - r_input_area.xmax = COM_BLUR_BOKEH_PIXELS; - r_input_area.xmin = 0; - r_input_area.ymax = COM_BLUR_BOKEH_PIXELS; - r_input_area.ymin = 0; + r_input_area = output_area; + r_input_area.xmax = r_input_area.xmin + COM_BLUR_BOKEH_PIXELS; + r_input_area.ymax = r_input_area.ymin + COM_BLUR_BOKEH_PIXELS; break; } #ifdef COM_DEFOCUS_SEARCH diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h index 56b4677087b..3634bc2f1d7 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h @@ -130,8 +130,7 @@ class InverseSearchRadiusOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void setMaxBlur(int maxRadius) { diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc index 5405e6d424a..63956410b60 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc @@ -126,10 +126,7 @@ void VectorBlurOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &UNUSED(output_area), rcti &r_input_area) { - r_input_area.xmin = 0; - r_input_area.xmax = this->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = this->getHeight(); + r_input_area = this->get_canvas(); } void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index a038e8994d8..1cb98ac8474 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -122,15 +122,15 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) updateImage(rect); } -void ViewerOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { const int sceneRenderWidth = this->m_rd->xsch * this->m_rd->size / 100; const int sceneRenderHeight = this->m_rd->ysch * this->m_rd->size / 100; - unsigned int localPrefRes[2] = {static_cast(sceneRenderWidth), - static_cast(sceneRenderHeight)}; - NodeOperation::determineResolution(resolution, localPrefRes); + rcti local_preferred = preferred_area; + local_preferred.xmax = local_preferred.xmin + sceneRenderWidth; + local_preferred.ymax = local_preferred.ymin + sceneRenderHeight; + NodeOperation::determine_canvas(local_preferred, r_area); } void ViewerOperation::initImage() diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h index 06ac501a535..e759bcf9898 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.h +++ b/source/blender/compositor/operations/COM_ViewerOperation.h @@ -55,8 +55,7 @@ class ViewerOperation : public MultiThreadedOperation { void initExecution() override; void deinitExecution() override; void executeRegion(rcti *rect, unsigned int tileNumber) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; bool isOutputOperation(bool /*rendering*/) const override { if (G.background) { diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc index 888602114cc..393128ded41 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.cc +++ b/source/blender/compositor/operations/COM_WrapOperation.cc @@ -33,7 +33,7 @@ inline float WrapOperation::getWrappedOriginalXPos(float x) return 0; } while (x < 0) { - x += this->m_width; + x += this->getWidth(); } return fmodf(x, this->getWidth()); } @@ -44,7 +44,7 @@ inline float WrapOperation::getWrappedOriginalYPos(float y) return 0; } while (y < 0) { - y += this->m_height; + y += this->getHeight(); } return fmodf(y, this->getHeight()); } diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cc b/source/blender/compositor/operations/COM_WriteBufferOperation.cc index 6380f6bf3df..a1c1e514eb7 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.cc +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cc @@ -50,7 +50,7 @@ void WriteBufferOperation::executePixelSampled(float output[4], void WriteBufferOperation::initExecution() { this->m_input = this->getInputOperation(0); - this->m_memoryProxy->allocate(this->m_width, this->m_height); + this->m_memoryProxy->allocate(this->getWidth(), this->getHeight()); } void WriteBufferOperation::deinitExecution() @@ -206,18 +206,17 @@ void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, delete clKernelsToCleanUp; } -void WriteBufferOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void WriteBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); /* make sure there is at least one pixel stored in case the input is a single value */ m_single_value = false; - if (resolution[0] == 0) { - resolution[0] = 1; + if (BLI_rcti_size_x(&r_area) == 0) { + r_area.xmax += 1; m_single_value = true; } - if (resolution[1] == 0) { - resolution[1] = 1; + if (BLI_rcti_size_y(&r_area) == 0) { + r_area.ymax += 1; m_single_value = true; } } diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.h b/source/blender/compositor/operations/COM_WriteBufferOperation.h index 2817fbe24b9..b7dededc69f 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.h +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.h @@ -56,8 +56,7 @@ class WriteBufferOperation : public NodeOperation { unsigned int chunkNumber, MemoryBuffer **memoryBuffers, MemoryBuffer *outputBuffer) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void readResolutionFromInputSocket(); inline NodeOperation *getInput() { From f84fb12f5d72433780a96c3cc4381399f153cf1a Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 28 Sep 2021 19:33:06 +0200 Subject: [PATCH 0322/1500] Compositor: Add support for canvas compositing This commit adds functionality for operations that require pixel translation or resizing on "Full Frame" mode, allowing to adjust their canvas. It fixes most cropping issues in translate, scale, rotate and transform nodes by adjusting their canvas to the result, instead of the input canvas. Operations output buffer is still always on (0,0) position for easier image algorithm implementation, even when the canvas is not. Current limitations (will be addressed on bcon2): - Displayed translation in Viewer node is limited to 6000px. - When scaling up the canvas size is limited to the scene resolution size x 1.5 . From that point it crops. If none of these limitations are hit, the Viewer node displays the full input with any translation. Differential Revision: https://developer.blender.org/D12466 --- source/blender/compositor/COM_defines.h | 3 + .../intern/COM_CompositorContext.cc | 6 + .../compositor/intern/COM_CompositorContext.h | 2 + .../compositor/intern/COM_Converter.cc | 124 +++++---- .../intern/COM_FullFrameExecutionModel.cc | 64 +++-- .../intern/COM_FullFrameExecutionModel.h | 6 +- .../compositor/intern/COM_MemoryBuffer.cc | 2 + .../compositor/intern/COM_MemoryBuffer.h | 58 ++-- .../compositor/intern/COM_NodeOperation.cc | 40 +++ .../compositor/intern/COM_NodeOperation.h | 32 +-- .../intern/COM_NodeOperationBuilder.cc | 17 +- .../intern/COM_SharedOperationBuffers.cc | 12 +- .../intern/COM_SharedOperationBuffers.h | 2 +- .../compositor/nodes/COM_BoxMaskNode.cc | 2 +- .../compositor/nodes/COM_EllipseMaskNode.cc | 2 +- .../blender/compositor/nodes/COM_ScaleNode.cc | 6 +- .../compositor/nodes/COM_Stabilize2dNode.cc | 59 +++- .../compositor/nodes/COM_TransformNode.cc | 35 ++- .../compositor/nodes/COM_TranslateNode.cc | 4 +- .../operations/COM_BokehBlurOperation.cc | 2 +- .../operations/COM_CalculateMeanOperation.cc | 2 +- .../operations/COM_CropOperation.cc | 2 +- .../operations/COM_MapUVOperation.cc | 2 +- .../COM_MovieClipAttributeOperation.cc | 14 +- .../COM_MovieClipAttributeOperation.h | 9 + .../COM_PlaneDistortCommonOperation.cc | 2 +- .../operations/COM_PreviewOperation.cc | 4 +- .../COM_ProjectorLensDistortionOperation.cc | 17 ++ .../COM_ProjectorLensDistortionOperation.h | 1 + .../operations/COM_RotateOperation.cc | 133 +++++++-- .../operations/COM_RotateOperation.h | 25 ++ .../operations/COM_ScaleOperation.cc | 263 ++++++++++++++---- .../operations/COM_ScaleOperation.h | 72 +++-- .../COM_ScreenLensDistortionOperation.cc | 17 ++ .../COM_ScreenLensDistortionOperation.h | 1 + .../operations/COM_TextureOperation.cc | 30 +- .../operations/COM_TonemapOperation.cc | 2 +- .../operations/COM_TransformOperation.cc | 211 +++++++++----- .../operations/COM_TransformOperation.h | 20 +- .../operations/COM_TranslateOperation.cc | 35 ++- .../operations/COM_TranslateOperation.h | 27 +- .../COM_VariableSizeBokehBlurOperation.cc | 6 +- .../operations/COM_ViewerOperation.cc | 58 +++- .../operations/COM_ViewerOperation.h | 3 + 44 files changed, 1066 insertions(+), 368 deletions(-) diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index e0f23fbede3..55b331e279f 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -18,11 +18,14 @@ #pragma once +#include "BLI_float2.hh" #include "BLI_index_range.hh" #include "BLI_rect.h" namespace blender::compositor { +using Size2f = float2; + enum class eExecutionModel { /** * Operations are executed from outputs to inputs grouped in execution groups and rendered diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index a93820b66dc..f5f490b0bf6 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -43,6 +43,12 @@ int CompositorContext::getFramenumber() const return m_rd->cfra; } +Size2f CompositorContext::get_render_size() const +{ + return {getRenderData()->xsch * getRenderPercentageAsFactor(), + getRenderData()->ysch * getRenderPercentageAsFactor()}; +} + eExecutionModel CompositorContext::get_execution_model() const { if (U.experimental.use_full_frame_compositor) { diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index c6e83f93777..ae298c5a65a 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -288,6 +288,8 @@ class CompositorContext { return m_rd->size * 0.01f; } + Size2f get_render_size() const; + /** * Get active execution model. */ diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index bd05a8e4ef0..ee77beb8a82 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -467,7 +467,9 @@ void COM_convert_canvas(NodeOperationBuilder &builder, /* Data type conversions are executed before resolutions to ensure convert operations have * resolution. This method have to ensure same datatypes are linked for new operations. */ BLI_assert(fromSocket->getDataType() == toSocket->getDataType()); + ResizeMode mode = toSocket->getResizeMode(); + BLI_assert(mode != ResizeMode::None); NodeOperation *toOperation = &toSocket->getOperation(); const float toWidth = toOperation->getWidth(); @@ -477,13 +479,12 @@ void COM_convert_canvas(NodeOperationBuilder &builder, const float fromHeight = fromOperation->getHeight(); bool doCenter = false; bool doScale = false; - float addX = (toWidth - fromWidth) / 2.0f; - float addY = (toHeight - fromHeight) / 2.0f; float scaleX = 0; float scaleY = 0; switch (mode) { case ResizeMode::None: + case ResizeMode::Align: break; case ResizeMode::Center: doCenter = true; @@ -518,63 +519,74 @@ void COM_convert_canvas(NodeOperationBuilder &builder, break; } - if (doCenter) { - NodeOperation *first = nullptr; - ScaleOperation *scaleOperation = nullptr; - if (doScale) { - scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType()); - scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None); - scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None); - first = scaleOperation; - SetValueOperation *sxop = new SetValueOperation(); - sxop->setValue(scaleX); - builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1)); - SetValueOperation *syop = new SetValueOperation(); - syop->setValue(scaleY); - builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2)); - builder.addOperation(sxop); - builder.addOperation(syop); + float addX = doCenter ? (toWidth - fromWidth) / 2.0f : 0.0f; + float addY = doCenter ? (toHeight - fromHeight) / 2.0f : 0.0f; + NodeOperation *first = nullptr; + ScaleOperation *scaleOperation = nullptr; + if (doScale) { + scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType()); + scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None); + scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None); + first = scaleOperation; + SetValueOperation *sxop = new SetValueOperation(); + sxop->setValue(scaleX); + builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1)); + SetValueOperation *syop = new SetValueOperation(); + syop->setValue(scaleY); + builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2)); + builder.addOperation(sxop); + builder.addOperation(syop); - const rcti &scale_canvas = fromOperation->get_canvas(); - scaleOperation->set_canvas(scale_canvas); - sxop->set_canvas(scale_canvas); - syop->set_canvas(scale_canvas); - builder.addOperation(scaleOperation); + rcti scale_canvas = fromOperation->get_canvas(); + if (builder.context().get_execution_model() == eExecutionModel::FullFrame) { + ScaleOperation::scale_area(scale_canvas, scaleX, scaleY); + scale_canvas.xmax = scale_canvas.xmin + toOperation->getWidth(); + scale_canvas.ymax = scale_canvas.ymin + toOperation->getHeight(); + addX = 0; + addY = 0; } - - TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType()); - translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None); - translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None); - if (!first) { - first = translateOperation; - } - SetValueOperation *xop = new SetValueOperation(); - xop->setValue(addX); - builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1)); - SetValueOperation *yop = new SetValueOperation(); - yop->setValue(addY); - builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2)); - builder.addOperation(xop); - builder.addOperation(yop); - - const rcti &translate_canvas = toOperation->get_canvas(); - translateOperation->set_canvas(translate_canvas); - xop->set_canvas(translate_canvas); - yop->set_canvas(translate_canvas); - builder.addOperation(translateOperation); - - if (doScale) { - translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None); - builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0)); - } - - /* remove previous link and replace */ - builder.removeInputLink(toSocket); - first->getInputSocket(0)->setResizeMode(ResizeMode::None); - toSocket->setResizeMode(ResizeMode::None); - builder.addLink(fromSocket, first->getInputSocket(0)); - builder.addLink(translateOperation->getOutputSocket(), toSocket); + scaleOperation->set_canvas(scale_canvas); + sxop->set_canvas(scale_canvas); + syop->set_canvas(scale_canvas); + builder.addOperation(scaleOperation); } + + TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType()); + translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None); + translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None); + if (!first) { + first = translateOperation; + } + SetValueOperation *xop = new SetValueOperation(); + xop->setValue(addX); + builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1)); + SetValueOperation *yop = new SetValueOperation(); + yop->setValue(addY); + builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2)); + builder.addOperation(xop); + builder.addOperation(yop); + + rcti translate_canvas = toOperation->get_canvas(); + if (mode == ResizeMode::Align) { + translate_canvas.xmax = translate_canvas.xmin + fromWidth; + translate_canvas.ymax = translate_canvas.ymin + fromHeight; + } + translateOperation->set_canvas(translate_canvas); + xop->set_canvas(translate_canvas); + yop->set_canvas(translate_canvas); + builder.addOperation(translateOperation); + + if (doScale) { + translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None); + builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0)); + } + + /* remove previous link and replace */ + builder.removeInputLink(toSocket); + first->getInputSocket(0)->setResizeMode(ResizeMode::None); + toSocket->setResizeMode(ResizeMode::None); + builder.addLink(fromSocket, first->getInputSocket(0)); + builder.addLink(translateOperation->getOutputSocket(), toSocket); } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index 957bbe24e5f..c44a168390b 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -74,34 +74,61 @@ void FullFrameExecutionModel::determine_areas_to_render_and_reads() } } -Vector FullFrameExecutionModel::get_input_buffers(NodeOperation *op) +/** + * Returns input buffers with an offset relative to given output coordinates. Returned memory + * buffers must be deleted. + */ +Vector FullFrameExecutionModel::get_input_buffers(NodeOperation *op, + const int output_x, + const int output_y) { const int num_inputs = op->getNumberOfInputSockets(); Vector inputs_buffers(num_inputs); for (int i = 0; i < num_inputs; i++) { - NodeOperation *input_op = op->get_input_operation(i); - inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op); + NodeOperation *input = op->get_input_operation(i); + const int offset_x = (input->get_canvas().xmin - op->get_canvas().xmin) + output_x; + const int offset_y = (input->get_canvas().ymin - op->get_canvas().ymin) + output_y; + MemoryBuffer *buf = active_buffers_.get_rendered_buffer(input); + + rcti rect = buf->get_rect(); + BLI_rcti_translate(&rect, offset_x, offset_y); + inputs_buffers[i] = new MemoryBuffer( + buf->getBuffer(), buf->get_num_channels(), rect, buf->is_a_single_elem()); } return inputs_buffers; } -MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op) +MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op, + const int output_x, + const int output_y) { + rcti rect; + BLI_rcti_init(&rect, output_x, output_x + op->getWidth(), output_y, output_y + op->getHeight()); + const DataType data_type = op->getOutputSocket(0)->getDataType(); const bool is_a_single_elem = op->get_flags().is_constant_operation; - return new MemoryBuffer(data_type, op->get_canvas(), is_a_single_elem); + return new MemoryBuffer(data_type, rect, is_a_single_elem); } void FullFrameExecutionModel::render_operation(NodeOperation *op) { - Vector input_bufs = get_input_buffers(op); + /* Output has no offset for easier image algorithms implementation on operations. */ + constexpr int output_x = 0; + constexpr int output_y = 0; const bool has_outputs = op->getNumberOfOutputSockets() > 0; - MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr; + MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op, output_x, output_y) : nullptr; if (op->getWidth() > 0 && op->getHeight() > 0) { - Span areas = active_buffers_.get_areas_to_render(op); + Vector input_bufs = get_input_buffers(op, output_x, output_y); + const int op_offset_x = output_x - op->get_canvas().xmin; + const int op_offset_y = output_y - op->get_canvas().ymin; + Span areas = active_buffers_.get_areas_to_render(op, op_offset_x, op_offset_y); op->render(op_buf, areas, input_bufs); DebugInfo::operation_rendered(op, op_buf); + + for (MemoryBuffer *buf : input_bufs) { + delete buf; + } } /* Even if operation has no resolution set the empty buffer. It will be clipped with a * TranslateOperation from convert resolutions if linked to an operation with resolution. */ @@ -187,7 +214,8 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op std::pair pair = stack.pop_last(); NodeOperation *operation = pair.first; const rcti &render_area = pair.second; - if (active_buffers_.is_area_registered(operation, render_area)) { + if (BLI_rcti_is_empty(&render_area) || + active_buffers_.is_area_registered(operation, render_area)) { continue; } @@ -239,9 +267,8 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r BLI_assert(output_op->isOutputOperation(context_.isRendering())); /* By default return operation bounds (no border). */ - const int op_width = output_op->getWidth(); - const int op_height = output_op->getHeight(); - BLI_rcti_init(&r_area, 0, op_width, 0, op_height); + rcti canvas = output_op->get_canvas(); + r_area = canvas; const bool has_viewer_border = border_.use_viewer_border && (output_op->get_flags().is_viewer_operation || @@ -251,12 +278,13 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r /* Get border with normalized coordinates. */ const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border; - /* Return de-normalized border. */ - BLI_rcti_init(&r_area, - norm_border->xmin * op_width, - norm_border->xmax * op_width, - norm_border->ymin * op_height, - norm_border->ymax * op_height); + /* Return de-normalized border within canvas. */ + const int w = output_op->getWidth(); + const int h = output_op->getHeight(); + r_area.xmin = canvas.xmin + norm_border->xmin * w; + r_area.xmax = canvas.xmin + norm_border->xmax * w; + r_area.ymin = canvas.ymin + norm_border->ymin * h; + r_area.ymax = canvas.ymin + norm_border->ymax * h; } } diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h index f75d4f1afdc..66dfb8f052c 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h @@ -61,8 +61,10 @@ class FullFrameExecutionModel : public ExecutionModel { void determine_areas_to_render_and_reads(); void render_operations(); void render_output_dependencies(NodeOperation *output_op); - Vector get_input_buffers(NodeOperation *op); - MemoryBuffer *create_operation_buffer(NodeOperation *op); + Vector get_input_buffers(NodeOperation *op, + const int output_x, + const int output_y); + MemoryBuffer *create_operation_buffer(NodeOperation *op, const int output_x, const int output_y); void render_operation(NodeOperation *op); void operation_finished(NodeOperation *operation); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 5327be50b53..f57f0f055bf 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -122,6 +122,8 @@ void MemoryBuffer::set_strides() this->elem_stride = m_num_channels; this->row_stride = getWidth() * m_num_channels; } + to_positive_x_stride_ = m_rect.xmin < 0 ? -m_rect.xmin + 1 : (m_rect.xmin == 0 ? 1 : 0); + to_positive_y_stride_ = m_rect.ymin < 0 ? -m_rect.ymin + 1 : (m_rect.ymin == 0 ? 1 : 0); } void MemoryBuffer::clear() diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index f730d53acec..4d6c5790987 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -114,6 +114,12 @@ class MemoryBuffer { */ bool owns_data_; + /** Stride to make any x coordinate within buffer positive (non-zero). */ + int to_positive_x_stride_; + + /** Stride to make any y coordinate within buffer positive (non-zero). */ + int to_positive_y_stride_; + public: /** * \brief construct new temporarily MemoryBuffer for an area @@ -166,9 +172,9 @@ class MemoryBuffer { /** * Get offset needed to jump from buffer start to given coordinates. */ - int get_coords_offset(int x, int y) const + intptr_t get_coords_offset(int x, int y) const { - return (y - m_rect.ymin) * row_stride + (x - m_rect.xmin) * elem_stride; + return ((intptr_t)y - m_rect.ymin) * row_stride + ((intptr_t)x - m_rect.xmin) * elem_stride; } /** @@ -176,7 +182,7 @@ class MemoryBuffer { */ float *get_elem(int x, int y) { - BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax); + BLI_assert(has_coords(x, y)); return m_buffer + get_coords_offset(x, y); } @@ -185,7 +191,7 @@ class MemoryBuffer { */ const float *get_elem(int x, int y) const { - BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax); + BLI_assert(has_coords(x, y)); return m_buffer + get_coords_offset(x, y); } @@ -196,7 +202,7 @@ class MemoryBuffer { void read_elem_checked(int x, int y, float *out) const { - if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) { + if (!has_coords(x, y)) { clear_elem(out); } else { @@ -206,12 +212,7 @@ class MemoryBuffer { void read_elem_checked(float x, float y, float *out) const { - if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) { - clear_elem(out); - } - else { - read_elem(x, y, out); - } + read_elem_checked(floor_x(x), floor_y(y), out); } void read_elem_bilinear(float x, float y, float *out) const @@ -286,8 +287,7 @@ class MemoryBuffer { */ float &get_value(int x, int y, int channel) { - BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax && - channel >= 0 && channel < m_num_channels); + BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels); return m_buffer[get_coords_offset(x, y) + channel]; } @@ -296,8 +296,7 @@ class MemoryBuffer { */ const float &get_value(int x, int y, int channel) const { - BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax && - channel >= 0 && channel < m_num_channels); + BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels); return m_buffer[get_coords_offset(x, y) + channel]; } @@ -306,7 +305,7 @@ class MemoryBuffer { */ const float *get_row_end(int y) const { - BLI_assert(y >= 0 && y < getHeight()); + BLI_assert(has_y(y)); return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y)); } @@ -681,6 +680,33 @@ class MemoryBuffer { return y - m_rect.ymin; } + template bool has_coords(T x, T y) const + { + return has_x(x) && has_y(y); + } + + template bool has_x(T x) const + { + return x >= m_rect.xmin && x < m_rect.xmax; + } + + template bool has_y(T y) const + { + return y >= m_rect.ymin && y < m_rect.ymax; + } + + /* Fast floor functions. The caller should check result is within buffer bounds. It ceils in near + * cases and when given coordinate is negative and less than buffer rect `min - 1`. */ + int floor_x(float x) const + { + return (int)(x + to_positive_x_stride_) - to_positive_x_stride_; + } + + int floor_y(float y) const + { + return (int)(y + to_positive_y_stride_) - to_positive_y_stride_; + } + void copy_single_elem_from(const MemoryBuffer *src, int channel_offset, int elem_size, diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index ff232efdb08..a6a395261f2 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -40,6 +40,24 @@ NodeOperation::NodeOperation() this->m_btree = nullptr; } +/** Get constant value when operation is constant, otherwise return default_value. */ +float NodeOperation::get_constant_value_default(float default_value) +{ + BLI_assert(m_outputs.size() > 0 && getOutputSocket()->getDataType() == DataType::Value); + return *get_constant_elem_default(&default_value); +} + +/** Get constant elem when operation is constant, otherwise return default_elem. */ +const float *NodeOperation::get_constant_elem_default(const float *default_elem) +{ + BLI_assert(m_outputs.size() > 0); + if (get_flags().is_constant_operation) { + return static_cast(this)->get_constant_elem(); + } + + return default_elem; +} + /** * Generate a hash that identifies the operation result in the current execution. * Requires `hash_output_params` to be implemented, otherwise `std::nullopt` is returned. @@ -186,6 +204,28 @@ void NodeOperation::deinitExecution() { /* pass */ } + +void NodeOperation::set_canvas(const rcti &canvas_area) +{ + canvas_ = canvas_area; + flags.is_canvas_set = true; +} + +const rcti &NodeOperation::get_canvas() const +{ + return canvas_; +} + +/** + * Mainly used for re-determining canvas of constant operations in cases where preferred canvas + * depends on the constant element. + */ +void NodeOperation::unset_canvas() +{ + BLI_assert(m_inputs.size() == 0); + flags.is_canvas_set = false; +} + SocketReader *NodeOperation::getInputSocketReader(unsigned int inputSocketIndex) { return this->getInputSocket(inputSocketIndex)->getReader(); diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index 9f113b60345..f507665bee3 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -62,9 +62,13 @@ enum class ResizeMode { /** \brief Center the input image to the center of the working area of the node, no resizing * occurs */ Center = NS_CR_CENTER, - /** \brief The bottom left of the input image is the bottom left of the working area of the node, - * no resizing occurs */ + /** No resizing or translation. */ None = NS_CR_NONE, + /** + * Input image is translated so that its bottom left matches the bottom left of the working area + * of the node, no resizing occurs. + */ + Align = 100, /** \brief Fit the width of the input image to the width of the working area of the node */ FitWidth = NS_CR_FIT_WIDTH, /** \brief Fit the height of the input image to the height of the working area of the node */ @@ -381,6 +385,9 @@ class NodeOperation { return m_id; } + float get_constant_value_default(float default_value); + const float *get_constant_elem_default(const float *default_elem); + const NodeOperationFlags get_flags() const { return flags; @@ -507,18 +514,9 @@ class NodeOperation { } virtual void deinitExecution(); - void set_canvas(const rcti &canvas_area) - { - if (!this->flags.is_canvas_set) { - canvas_ = canvas_area; - flags.is_canvas_set = true; - } - } - - const rcti &get_canvas() const - { - return canvas_; - } + void set_canvas(const rcti &canvas_area); + const rcti &get_canvas() const; + void unset_canvas(); /** * \brief is this operation the active viewer output @@ -575,12 +573,12 @@ class NodeOperation { unsigned int getWidth() const { - return BLI_rcti_size_x(&canvas_); + return BLI_rcti_size_x(&get_canvas()); } unsigned int getHeight() const { - return BLI_rcti_size_y(&canvas_); + return BLI_rcti_size_y(&get_canvas()); } inline void readSampled(float result[4], float x, float y, PixelSampler sampler) @@ -677,6 +675,7 @@ class NodeOperation { void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center); void addOutputSocket(DataType datatype); + /* TODO(manzanilla): to be removed with tiled implementation. */ void setWidth(unsigned int width) { canvas_.xmax = canvas_.xmin + width; @@ -687,6 +686,7 @@ class NodeOperation { canvas_.ymax = canvas_.ymin + height; this->flags.is_canvas_set = true; } + SocketReader *getInputSocketReader(unsigned int inputSocketindex); NodeOperation *getInputOperation(unsigned int inputSocketindex); diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 3791d05602d..acb7f61f6dd 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -33,6 +33,7 @@ #include "COM_SetValueOperation.h" #include "COM_SetVectorOperation.h" #include "COM_SocketProxyOperation.h" +#include "COM_TranslateOperation.h" #include "COM_ViewerOperation.h" #include "COM_WriteBufferOperation.h" @@ -448,9 +449,19 @@ void NodeOperationBuilder::determine_canvases() Vector convert_links; for (const Link &link : m_links) { if (link.to()->getResizeMode() != ResizeMode::None) { - NodeOperation &from_op = link.from()->getOperation(); - NodeOperation &to_op = link.to()->getOperation(); - if (from_op.getWidth() != to_op.getWidth() || from_op.getHeight() != to_op.getHeight()) { + const rcti &from_canvas = link.from()->getOperation().get_canvas(); + const rcti &to_canvas = link.to()->getOperation().get_canvas(); + + bool needs_conversion; + if (link.to()->getResizeMode() == ResizeMode::Align) { + needs_conversion = from_canvas.xmin != to_canvas.xmin || + from_canvas.ymin != to_canvas.ymin; + } + else { + needs_conversion = !BLI_rcti_compare(&from_canvas, &to_canvas); + } + + if (needs_conversion) { convert_links.append(link); } } diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc index 7e0486b0f54..55153bd4f0a 100644 --- a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc @@ -76,9 +76,17 @@ void SharedOperationBuffers::register_read(NodeOperation *read_op) /** * Get registered areas given operation needs to render. */ -blender::Span SharedOperationBuffers::get_areas_to_render(NodeOperation *op) +Vector SharedOperationBuffers::get_areas_to_render(NodeOperation *op, + const int offset_x, + const int offset_y) { - return get_buffer_data(op).render_areas.as_span(); + Span render_areas = get_buffer_data(op).render_areas.as_span(); + Vector dst_areas; + for (rcti dst : render_areas) { + BLI_rcti_translate(&dst, offset_x, offset_y); + dst_areas.append(std::move(dst)); + } + return dst_areas; } /** diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h index f7763cd8ae4..4461ba75cbe 100644 --- a/source/blender/compositor/intern/COM_SharedOperationBuffers.h +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h @@ -53,7 +53,7 @@ class SharedOperationBuffers { bool has_registered_reads(NodeOperation *op); void register_read(NodeOperation *read_op); - blender::Span get_areas_to_render(NodeOperation *op); + Vector get_areas_to_render(NodeOperation *op, int offset_x, int offset_y); bool is_operation_rendered(NodeOperation *op); void set_rendered_buffer(NodeOperation *op, std::unique_ptr buffer); MemoryBuffer *get_rendered_buffer(NodeOperation *op); diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.cc b/source/blender/compositor/nodes/COM_BoxMaskNode.cc index 14f42cc42f7..8017e063a69 100644 --- a/source/blender/compositor/nodes/COM_BoxMaskNode.cc +++ b/source/blender/compositor/nodes/COM_BoxMaskNode.cc @@ -62,7 +62,7 @@ void BoxMaskNode::convertToOperations(NodeConverter &converter, scaleOperation->setOffset(0.0f, 0.0f); scaleOperation->setNewWidth(rd->xsch * render_size_factor); scaleOperation->setNewHeight(rd->ysch * render_size_factor); - scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None); + scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align); converter.addOperation(scaleOperation); converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0)); diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc index 3b4f5ca8c94..752597ef937 100644 --- a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc +++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc @@ -62,7 +62,7 @@ void EllipseMaskNode::convertToOperations(NodeConverter &converter, scaleOperation->setOffset(0.0f, 0.0f); scaleOperation->setNewWidth(rd->xsch * render_size_factor); scaleOperation->setNewHeight(rd->ysch * render_size_factor); - scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None); + scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align); converter.addOperation(scaleOperation); converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0)); diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc index 819d2e72f30..f1f41375eba 100644 --- a/source/blender/compositor/nodes/COM_ScaleNode.cc +++ b/source/blender/compositor/nodes/COM_ScaleNode.cc @@ -52,6 +52,8 @@ void ScaleNode::convertToOperations(NodeConverter &converter, converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f); + break; } case CMP_SCALE_SCENEPERCENT: { @@ -68,6 +70,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter, converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f); break; } @@ -81,13 +84,13 @@ void ScaleNode::convertToOperations(NodeConverter &converter, operation->setOffset(bnode->custom3, bnode->custom4); operation->setNewWidth(rd->xsch * render_size_factor); operation->setNewHeight(rd->ysch * render_size_factor); - operation->getInputSocket(0)->setResizeMode(ResizeMode::None); converter.addOperation(operation); converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_scale_canvas_max_size(context.get_render_size() * 3.0f); break; } @@ -102,6 +105,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter, converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f); break; } diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc index 90f62c6d562..3d8f0bbda7e 100644 --- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc +++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc @@ -123,17 +123,54 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter, break; } case eExecutionModel::FullFrame: { - TransformOperation *transform_op = new TransformOperation(); - transform_op->set_sampler(sampler); - transform_op->set_convert_rotate_degree_to_rad(false); - transform_op->set_invert(invert); - converter.addOperation(transform_op); - converter.mapInputSocket(imageInput, transform_op->getInputSocket(0)); - converter.addLink(xAttribute->getOutputSocket(), transform_op->getInputSocket(1)); - converter.addLink(yAttribute->getOutputSocket(), transform_op->getInputSocket(2)); - converter.addLink(angleAttribute->getOutputSocket(), transform_op->getInputSocket(3)); - converter.addLink(scaleAttribute->getOutputSocket(), transform_op->getInputSocket(4)); - converter.mapOutputSocket(getOutputSocket(), transform_op->getOutputSocket()); + ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); + scaleOperation->setSampler(sampler); + RotateOperation *rotateOperation = new RotateOperation(); + rotateOperation->setDoDegree2RadConversion(false); + rotateOperation->set_sampler(sampler); + TranslateOperation *translateOperation = new TranslateCanvasOperation(); + + converter.addOperation(scaleOperation); + converter.addOperation(translateOperation); + converter.addOperation(rotateOperation); + + converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1)); + converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2)); + + converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1)); + + converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1)); + converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2)); + + NodeOperationInput *stabilization_socket = nullptr; + if (invert) { + /* Translate -> Rotate -> Scale. */ + stabilization_socket = translateOperation->getInputSocket(0); + converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0)); + + converter.addLink(translateOperation->getOutputSocket(), + rotateOperation->getInputSocket(0)); + converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0)); + + converter.mapOutputSocket(getOutputSocket(), scaleOperation->getOutputSocket()); + } + else { + /* Scale -> Rotate -> Translate. */ + stabilization_socket = scaleOperation->getInputSocket(0); + converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0)); + + converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); + converter.addLink(rotateOperation->getOutputSocket(), + translateOperation->getInputSocket(0)); + + converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket()); + } + + xAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + yAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + scaleAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + angleAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + break; } } } diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc index d2fb7b54633..b38aad78d90 100644 --- a/source/blender/compositor/nodes/COM_TransformNode.cc +++ b/source/blender/compositor/nodes/COM_TransformNode.cc @@ -73,16 +73,33 @@ void TransformNode::convertToOperations(NodeConverter &converter, break; } case eExecutionModel::FullFrame: { - TransformOperation *op = new TransformOperation(); - op->set_sampler((PixelSampler)this->getbNode()->custom1); - converter.addOperation(op); + ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); + converter.addOperation(scaleOperation); - converter.mapInputSocket(imageInput, op->getInputSocket(0)); - converter.mapInputSocket(xInput, op->getInputSocket(1)); - converter.mapInputSocket(yInput, op->getInputSocket(2)); - converter.mapInputSocket(angleInput, op->getInputSocket(3)); - converter.mapInputSocket(scaleInput, op->getInputSocket(4)); - converter.mapOutputSocket(getOutputSocket(), op->getOutputSocket()); + RotateOperation *rotateOperation = new RotateOperation(); + rotateOperation->setDoDegree2RadConversion(false); + converter.addOperation(rotateOperation); + + TranslateOperation *translateOperation = new TranslateCanvasOperation(); + converter.addOperation(translateOperation); + + PixelSampler sampler = (PixelSampler)this->getbNode()->custom1; + scaleOperation->setSampler(sampler); + rotateOperation->set_sampler(sampler); + scaleOperation->set_scale_canvas_max_size(context.get_render_size()); + + converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0)); + converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1)); + converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale + + converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); + converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1)); + + converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0)); + converter.mapInputSocket(xInput, translateOperation->getInputSocket(1)); + converter.mapInputSocket(yInput, translateOperation->getInputSocket(2)); + + converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket()); break; } } diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc index 3a3e98c3472..165a03baf41 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cc +++ b/source/blender/compositor/nodes/COM_TranslateNode.cc @@ -41,7 +41,9 @@ void TranslateNode::convertToOperations(NodeConverter &converter, NodeInput *inputYSocket = this->getInputSocket(2); NodeOutput *outputSocket = this->getOutputSocket(0); - TranslateOperation *operation = new TranslateOperation(); + TranslateOperation *operation = context.get_execution_model() == eExecutionModel::Tiled ? + new TranslateOperation() : + new TranslateCanvasOperation(); operation->set_wrapping(data->wrap_axis); if (data->relative) { const RenderData *rd = context.getRenderData(); diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc index 3f61a300849..93482dd2a54 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc @@ -34,7 +34,7 @@ constexpr int SIZE_INPUT_INDEX = 3; BokehBlurOperation::BokehBlurOperation() { this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color, ResizeMode::None); + this->addInputSocket(DataType::Color, ResizeMode::Align); this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc index 5fff4da62ae..a573a9d7eed 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc @@ -27,7 +27,7 @@ namespace blender::compositor { CalculateMeanOperation::CalculateMeanOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); + this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Value); this->m_imageReader = nullptr; this->m_iscalculated = false; diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc index 73805b76864..6ac30c22ad1 100644 --- a/source/blender/compositor/operations/COM_CropOperation.cc +++ b/source/blender/compositor/operations/COM_CropOperation.cc @@ -23,7 +23,7 @@ namespace blender::compositor { CropBaseOperation::CropBaseOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); + this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Color); this->m_inputOperation = nullptr; this->m_settings = nullptr; diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index 5062b0c42cb..ba38e583b30 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -23,7 +23,7 @@ namespace blender::compositor { MapUVOperation::MapUVOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); + this->addInputSocket(DataType::Color, ResizeMode::Align); this->addInputSocket(DataType::Vector); this->addOutputSocket(DataType::Color); this->m_alpha = 0.0f; diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc index 5654ea0425d..aed91d4d46e 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc @@ -31,6 +31,7 @@ MovieClipAttributeOperation::MovieClipAttributeOperation() this->m_invert = false; needs_canvas_to_get_constant_ = true; is_value_calculated_ = false; + stabilization_resolution_socket_ = nullptr; } void MovieClipAttributeOperation::initExecution() @@ -53,8 +54,17 @@ void MovieClipAttributeOperation::calc_value() scale = 1.0f; angle = 0.0f; int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_clip, this->m_framenumber); - BKE_tracking_stabilization_data_get( - this->m_clip, clip_framenr, getWidth(), getHeight(), loc, &scale, &angle); + NodeOperation &stabilization_operation = + stabilization_resolution_socket_ ? + stabilization_resolution_socket_->getLink()->getOperation() : + *this; + BKE_tracking_stabilization_data_get(this->m_clip, + clip_framenr, + stabilization_operation.getWidth(), + stabilization_operation.getHeight(), + loc, + &scale, + &angle); switch (this->m_attribute) { case MCA_SCALE: this->m_value = scale; diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h index e42605e5026..50680653aea 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h @@ -42,6 +42,7 @@ class MovieClipAttributeOperation : public ConstantOperation { bool m_invert; MovieClipAttribute m_attribute; bool is_value_calculated_; + NodeOperationInput *stabilization_resolution_socket_; public: /** @@ -76,6 +77,14 @@ class MovieClipAttributeOperation : public ConstantOperation { this->m_invert = invert; } + /** + * Set an operation socket which input will be used to get the resolution for stabilization. + */ + void set_socket_input_resolution_for_stabilization(NodeOperationInput *input_socket) + { + stabilization_resolution_socket_ = input_socket; + } + private: void calc_value(); }; diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index ab45899b7f5..31ef41789fd 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -73,7 +73,7 @@ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], flo PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortBaseOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); + this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Color); this->m_pixelReader = nullptr; this->flags.complex = true; diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc index 7b1dd89bd75..34520264d54 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cc +++ b/source/blender/compositor/operations/COM_PreviewOperation.cc @@ -41,7 +41,7 @@ PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings, const unsigned int defaultHeight) { - this->addInputSocket(DataType::Color, ResizeMode::None); + this->addInputSocket(DataType::Color, ResizeMode::Align); this->m_preview = nullptr; this->m_outputBuffer = nullptr; this->m_input = nullptr; @@ -162,7 +162,7 @@ void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti width = width * this->m_divider; height = height * this->m_divider; - BLI_rcti_init(&r_area, 0, width, 0, height); + BLI_rcti_init(&r_area, r_area.xmin, r_area.xmin + width, r_area.ymin, r_area.ymin + height); } eCompositorPriority PreviewOperation::getRenderPriority() const diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc index faebaf657cc..2982f59a019 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc @@ -131,6 +131,23 @@ void ProjectorLensDistortionOperation::updateDispersion() this->unlockMutex(); } +void ProjectorLensDistortionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + switch (execution_model_) { + case eExecutionModel::FullFrame: { + set_determined_canvas_modifier([=](rcti &canvas) { + /* Ensure screen space. */ + BLI_rcti_translate(&canvas, -canvas.xmin, -canvas.ymin); + }); + break; + } + default: + break; + } + + NodeOperation::determine_canvas(preferred_area, r_area); +} + void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx, const rcti &output_area, rcti &r_input_area) diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h index 7c7626bf271..b42fa3a361c 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h @@ -62,6 +62,7 @@ class ProjectorLensDistortionOperation : public MultiThreadedOperation { void updateDispersion(); + void determine_canvas(const rcti &preferred_area, rcti &r_area) 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, diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc index 8578e5c3269..9e26c93feac 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.cc +++ b/source/blender/compositor/operations/COM_RotateOperation.cc @@ -25,8 +25,8 @@ namespace blender::compositor { RotateOperation::RotateOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); + this->addInputSocket(DataType::Color, ResizeMode::None); + this->addInputSocket(DataType::Value, ResizeMode::None); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); this->m_imageSocket = nullptr; @@ -36,6 +36,21 @@ RotateOperation::RotateOperation() sampler_ = PixelSampler::Bilinear; } +void RotateOperation::get_rotation_center(const rcti &area, float &r_x, float &r_y) +{ + r_x = (BLI_rcti_size_x(&area) - 1) / 2.0; + r_y = (BLI_rcti_size_y(&area) - 1) / 2.0; +} + +void RotateOperation::get_rotation_offset(const rcti &input_canvas, + const rcti &rotate_canvas, + float &r_offset_x, + float &r_offset_y) +{ + r_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&rotate_canvas)) / 2.0f; + r_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&rotate_canvas)) / 2.0f; +} + void RotateOperation::get_area_rotation_bounds(const rcti &area, const float center_x, const float center_y, @@ -48,14 +63,14 @@ void RotateOperation::get_area_rotation_bounds(const rcti &area, const float dxmax = area.xmax - center_x; const float dymax = area.ymax - center_y; - const float x1 = center_x + (cosine * dxmin + sine * dymin); - const float x2 = center_x + (cosine * dxmax + sine * dymin); - const float x3 = center_x + (cosine * dxmin + sine * dymax); - const float x4 = center_x + (cosine * dxmax + sine * dymax); - const float y1 = center_y + (-sine * dxmin + cosine * dymin); - const float y2 = center_y + (-sine * dxmax + cosine * dymin); - const float y3 = center_y + (-sine * dxmin + cosine * dymax); - const float y4 = center_y + (-sine * dxmax + cosine * dymax); + const float x1 = center_x + (cosine * dxmin + (-sine) * dymin); + const float x2 = center_x + (cosine * dxmax + (-sine) * dymin); + const float x3 = center_x + (cosine * dxmin + (-sine) * dymax); + const float x4 = center_x + (cosine * dxmax + (-sine) * dymax); + const float y1 = center_y + (sine * dxmin + cosine * dymin); + const float y2 = center_y + (sine * dxmax + cosine * dymin); + const float y3 = center_y + (sine * dxmin + cosine * dymax); + const float y4 = center_y + (sine * dxmax + cosine * dymax); const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4))); const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4))); const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4))); @@ -67,10 +82,56 @@ void RotateOperation::get_area_rotation_bounds(const rcti &area, r_bounds.ymax = ceil(maxy); } +void RotateOperation::get_area_rotation_bounds_inverted(const rcti &area, + const float center_x, + const float center_y, + const float sine, + const float cosine, + rcti &r_bounds) +{ + get_area_rotation_bounds(area, center_x, center_y, -sine, cosine, r_bounds); +} + +void RotateOperation::get_rotation_area_of_interest(const rcti &input_canvas, + const rcti &rotate_canvas, + const float sine, + const float cosine, + const rcti &output_area, + rcti &r_input_area) +{ + float center_x, center_y; + get_rotation_center(input_canvas, center_x, center_y); + + float rotate_offset_x, rotate_offset_y; + get_rotation_offset(input_canvas, rotate_canvas, rotate_offset_x, rotate_offset_y); + + r_input_area = output_area; + BLI_rcti_translate(&r_input_area, rotate_offset_x, rotate_offset_y); + get_area_rotation_bounds_inverted(r_input_area, center_x, center_y, sine, cosine, r_input_area); +} + +void RotateOperation::get_rotation_canvas(const rcti &input_canvas, + const float sine, + const float cosine, + rcti &r_canvas) +{ + float center_x, center_y; + get_rotation_center(input_canvas, center_x, center_y); + + rcti rot_bounds; + get_area_rotation_bounds(input_canvas, center_x, center_y, sine, cosine, rot_bounds); + + float offset_x, offset_y; + get_rotation_offset(input_canvas, rot_bounds, offset_x, offset_y); + r_canvas = rot_bounds; + BLI_rcti_translate(&r_canvas, -offset_x, -offset_y); +} + void RotateOperation::init_data() { - this->m_centerX = (getWidth() - 1) / 2.0; - this->m_centerY = (getHeight() - 1) / 2.0; + if (execution_model_ == eExecutionModel::Tiled) { + get_rotation_center(get_canvas(), m_centerX, m_centerY); + } } void RotateOperation::initExecution() @@ -94,11 +155,7 @@ inline void RotateOperation::ensureDegree() this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest); break; case eExecutionModel::FullFrame: - NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX); - const bool is_constant_degree = degree_op->get_flags().is_constant_operation; - degree[0] = is_constant_degree ? - static_cast(degree_op)->get_constant_elem()[0] : - 0.0f; + degree[0] = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f); break; } @@ -159,6 +216,26 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void RotateOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + if (execution_model_ == eExecutionModel::Tiled) { + NodeOperation::determine_canvas(preferred_area, r_area); + return; + } + + const bool image_determined = + getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + if (image_determined) { + rcti input_canvas = r_area; + rcti unused; + getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(input_canvas, unused); + + ensureDegree(); + + get_rotation_canvas(input_canvas, m_sine, m_cosine, r_area); + } +} + void RotateOperation::get_area_of_interest(const int input_idx, const rcti &output_area, rcti &r_input_area) @@ -169,7 +246,10 @@ void RotateOperation::get_area_of_interest(const int input_idx, } ensureDegree(); - get_area_rotation_bounds(output_area, m_centerX, m_centerY, m_sine, m_cosine, r_input_area); + + const rcti &input_image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas(); + get_rotation_area_of_interest( + input_image_canvas, this->get_canvas(), m_sine, m_cosine, output_area, r_input_area); expand_area_for_sampler(r_input_area, sampler_); } @@ -177,13 +257,20 @@ void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - ensureDegree(); const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX]; + + NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX); + float center_x, center_y; + get_rotation_center(image_op->get_canvas(), center_x, center_y); + float rotate_offset_x, rotate_offset_y; + get_rotation_offset( + image_op->get_canvas(), this->get_canvas(), rotate_offset_x, rotate_offset_y); + for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - float x = it.x; - float y = it.y; - rotate_coords(x, y, m_centerX, m_centerY, m_sine, m_cosine); - input_img->read_elem_sampled(x, y, sampler_, it.out); + float x = rotate_offset_x + it.x + canvas_.xmin; + float y = rotate_offset_y + it.y + canvas_.ymin; + rotate_coords(x, y, center_x, center_y, m_sine, m_cosine); + input_img->read_elem_sampled(x - canvas_.xmin, y - canvas_.ymin, sampler_, it.out); } } diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h index f0de699f9ce..76f203cfbc0 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.h +++ b/source/blender/compositor/operations/COM_RotateOperation.h @@ -29,8 +29,10 @@ class RotateOperation : public MultiThreadedOperation { SocketReader *m_imageSocket; SocketReader *m_degreeSocket; + /* TODO(manzanilla): to be removed with tiled implementation. */ float m_centerX; float m_centerY; + float m_cosine; float m_sine; bool m_doDegree2RadConversion; @@ -49,12 +51,33 @@ class RotateOperation : public MultiThreadedOperation { y = center_y + (-sine * dx + cosine * dy); } + static void get_rotation_center(const rcti &area, float &r_x, float &r_y); + static void get_rotation_offset(const rcti &input_canvas, + const rcti &rotate_canvas, + float &r_offset_x, + float &r_offset_y); static void get_area_rotation_bounds(const rcti &area, const float center_x, const float center_y, const float sine, const float cosine, rcti &r_bounds); + static void get_area_rotation_bounds_inverted(const rcti &area, + const float center_x, + const float center_y, + const float sine, + const float cosine, + rcti &r_bounds); + static void get_rotation_area_of_interest(const rcti &input_canvas, + const rcti &rotate_canvas, + const float sine, + const float cosine, + const rcti &output_area, + rcti &r_input_area); + static void get_rotation_canvas(const rcti &input_canvas, + const float sine, + const float cosine, + rcti &r_canvas); bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, @@ -80,6 +103,8 @@ class RotateOperation : public MultiThreadedOperation { void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; + + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index f5a423ea8e3..dbd8faf0f1d 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -38,17 +38,21 @@ BaseScaleOperation::BaseScaleOperation() m_variable_size = false; } +void BaseScaleOperation::set_scale_canvas_max_size(Size2f size) +{ + max_scale_canvas_size_ = size; +} + ScaleOperation::ScaleOperation() : ScaleOperation(DataType::Color) { } ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation() { - this->addInputSocket(data_type); + this->addInputSocket(data_type, ResizeMode::None); this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(data_type); - this->set_canvas_input_index(0); this->m_inputOperation = nullptr; this->m_inputXOperation = nullptr; this->m_inputYOperation = nullptr; @@ -64,34 +68,52 @@ float ScaleOperation::get_constant_scale(const int input_op_idx, const float fac return 1.0f; } -float ScaleOperation::get_constant_scale_x() +float ScaleOperation::get_constant_scale_x(const float width) { - return get_constant_scale(1, get_relative_scale_x_factor()); + return get_constant_scale(X_INPUT_INDEX, get_relative_scale_x_factor(width)); } -float ScaleOperation::get_constant_scale_y() +float ScaleOperation::get_constant_scale_y(const float height) { - return get_constant_scale(2, get_relative_scale_y_factor()); + return get_constant_scale(Y_INPUT_INDEX, get_relative_scale_y_factor(height)); } -void ScaleOperation::scale_area( - rcti &rect, float center_x, float center_y, float scale_x, float scale_y) +bool ScaleOperation::is_scaling_variable() { - rect.xmin = scale_coord(rect.xmin, center_x, scale_x); - rect.xmax = scale_coord(rect.xmax, center_x, scale_x); - rect.ymin = scale_coord(rect.ymin, center_y, scale_y); - rect.ymax = scale_coord(rect.ymax, center_y, scale_y); + return !get_input_operation(X_INPUT_INDEX)->get_flags().is_constant_operation || + !get_input_operation(Y_INPUT_INDEX)->get_flags().is_constant_operation; } -void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y) +void ScaleOperation::scale_area(rcti &area, float relative_scale_x, float relative_scale_y) { - scale_area(rect, m_centerX, m_centerY, scale_x, scale_y); + const rcti src_area = area; + const float center_x = BLI_rcti_size_x(&area) / 2.0f; + const float center_y = BLI_rcti_size_y(&area) / 2.0f; + area.xmin = floorf(scale_coord(area.xmin, center_x, relative_scale_x)); + area.xmax = ceilf(scale_coord(area.xmax, center_x, relative_scale_x)); + area.ymin = floorf(scale_coord(area.ymin, center_y, relative_scale_y)); + area.ymax = ceilf(scale_coord(area.ymax, center_y, relative_scale_y)); + + float scale_offset_x, scale_offset_y; + ScaleOperation::get_scale_offset(src_area, area, scale_offset_x, scale_offset_y); + BLI_rcti_translate(&area, -scale_offset_x, -scale_offset_y); +} + +void ScaleOperation::clamp_area_size_max(rcti &area, Size2f max_size) +{ + + if (BLI_rcti_size_x(&area) > max_size.x) { + area.xmax = area.xmin + max_size.x; + } + if (BLI_rcti_size_y(&area) > max_size.y) { + area.ymax = area.ymin + max_size.y; + } } void ScaleOperation::init_data() { - m_centerX = getWidth() / 2.0f; - m_centerY = getHeight() / 2.0f; + canvas_center_x_ = canvas_.xmin + getWidth() / 2.0f; + canvas_center_y_ = canvas_.ymin + getHeight() / 2.0f; } void ScaleOperation::initExecution() @@ -108,18 +130,52 @@ void ScaleOperation::deinitExecution() this->m_inputYOperation = nullptr; } +void ScaleOperation::get_scale_offset(const rcti &input_canvas, + const rcti &scale_canvas, + float &r_scale_offset_x, + float &r_scale_offset_y) +{ + r_scale_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&scale_canvas)) / 2.0f; + r_scale_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&scale_canvas)) / 2.0f; +} + +void ScaleOperation::get_scale_area_of_interest(const rcti &input_canvas, + const rcti &scale_canvas, + const float relative_scale_x, + const float relative_scale_y, + const rcti &output_area, + rcti &r_input_area) +{ + const float scale_center_x = BLI_rcti_size_x(&input_canvas) / 2.0f; + const float scale_center_y = BLI_rcti_size_y(&input_canvas) / 2.0f; + float scale_offset_x, scale_offset_y; + ScaleOperation::get_scale_offset(input_canvas, scale_canvas, scale_offset_x, scale_offset_y); + + r_input_area.xmin = floorf( + scale_coord_inverted(output_area.xmin + scale_offset_x, scale_center_x, relative_scale_x)); + r_input_area.xmax = ceilf( + scale_coord_inverted(output_area.xmax + scale_offset_x, scale_center_x, relative_scale_x)); + r_input_area.ymin = floorf( + scale_coord_inverted(output_area.ymin + scale_offset_y, scale_center_y, relative_scale_y)); + r_input_area.ymax = ceilf( + scale_coord_inverted(output_area.ymax + scale_offset_y, scale_center_y, relative_scale_y)); +} + void ScaleOperation::get_area_of_interest(const int input_idx, const rcti &output_area, rcti &r_input_area) { r_input_area = output_area; - if (input_idx != 0 || m_variable_size) { + if (input_idx != 0 || is_scaling_variable()) { return; } - float scale_x = get_constant_scale_x(); - float scale_y = get_constant_scale_y(); - scale_area(r_input_area, scale_x, scale_y); + NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX); + const float scale_x = get_constant_scale_x(image_op->getWidth()); + const float scale_y = get_constant_scale_y(image_op->getHeight()); + + get_scale_area_of_interest( + image_op->get_canvas(), this->get_canvas(), scale_x, scale_y, output_area, r_input_area); expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler); } @@ -127,18 +183,70 @@ void ScaleOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const MemoryBuffer *input_img = inputs[0]; - MemoryBuffer *input_x = inputs[1]; - MemoryBuffer *input_y = inputs[2]; - const float scale_x_factor = get_relative_scale_x_factor(); - const float scale_y_factor = get_relative_scale_y_factor(); + NodeOperation *input_image_op = get_input_operation(IMAGE_INPUT_INDEX); + const int input_image_width = input_image_op->getWidth(); + const int input_image_height = input_image_op->getHeight(); + const float scale_x_factor = get_relative_scale_x_factor(input_image_width); + const float scale_y_factor = get_relative_scale_y_factor(input_image_height); + const float scale_center_x = input_image_width / 2.0f; + const float scale_center_y = input_image_height / 2.0f; + float from_scale_offset_x, from_scale_offset_y; + ScaleOperation::get_scale_offset( + input_image_op->get_canvas(), this->get_canvas(), from_scale_offset_x, from_scale_offset_y); + + const MemoryBuffer *input_image = inputs[IMAGE_INPUT_INDEX]; + MemoryBuffer *input_x = inputs[X_INPUT_INDEX]; + MemoryBuffer *input_y = inputs[Y_INPUT_INDEX]; BuffersIterator it = output->iterate_with({input_x, input_y}, area); for (; !it.is_end(); ++it) { const float rel_scale_x = *it.in(0) * scale_x_factor; const float rel_scale_y = *it.in(1) * scale_y_factor; - const float scaled_x = scale_coord(it.x, m_centerX, rel_scale_x); - const float scaled_y = scale_coord(it.y, m_centerY, rel_scale_y); - input_img->read_elem_sampled(scaled_x, scaled_y, (PixelSampler)m_sampler, it.out); + const float scaled_x = scale_coord_inverted( + from_scale_offset_x + canvas_.xmin + it.x, scale_center_x, rel_scale_x); + const float scaled_y = scale_coord_inverted( + from_scale_offset_y + canvas_.ymin + it.y, scale_center_y, rel_scale_y); + + input_image->read_elem_sampled( + scaled_x - canvas_.xmin, scaled_y - canvas_.ymin, (PixelSampler)m_sampler, it.out); + } +} + +void ScaleOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + if (execution_model_ == eExecutionModel::Tiled) { + NodeOperation::determine_canvas(preferred_area, r_area); + return; + } + + const bool image_determined = + getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + if (image_determined) { + rcti image_canvas = r_area; + rcti unused; + NodeOperationInput *x_socket = getInputSocket(X_INPUT_INDEX); + NodeOperationInput *y_socket = getInputSocket(Y_INPUT_INDEX); + x_socket->determine_canvas(image_canvas, unused); + y_socket->determine_canvas(image_canvas, unused); + if (is_scaling_variable()) { + /* Do not scale canvas. */ + return; + } + + /* Determine scaled canvas. */ + const float input_width = BLI_rcti_size_x(&r_area); + const float input_height = BLI_rcti_size_y(&r_area); + const float scale_x = get_constant_scale_x(input_width); + const float scale_y = get_constant_scale_y(input_height); + scale_area(r_area, scale_x, scale_y); + const Size2f max_scale_size = {MAX2(input_width, max_scale_canvas_size_.x), + MAX2(input_height, max_scale_canvas_size_.y)}; + clamp_area_size_max(r_area, max_scale_size); + + /* Re-determine canvases of x and y constant inputs with scaled canvas as preferred. */ + get_input_operation(X_INPUT_INDEX)->unset_canvas(); + get_input_operation(Y_INPUT_INDEX)->unset_canvas(); + x_socket->determine_canvas(r_area, unused); + y_socket->determine_canvas(r_area, unused); } } @@ -166,8 +274,8 @@ void ScaleRelativeOperation::executePixelSampled(float output[4], const float scx = scaleX[0]; const float scy = scaleY[0]; - float nx = this->m_centerX + (x - this->m_centerX) / scx; - float ny = this->m_centerY + (y - this->m_centerY) / scy; + float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / scx; + float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / scy; this->m_inputOperation->readSampled(output, nx, ny, effective_sampler); } @@ -186,10 +294,10 @@ bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input, const float scx = scaleX[0]; const float scy = scaleY[0]; - newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / scx + 1; - newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / scx - 1; - newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / scy + 1; - newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / scy - 1; + newInput.xmax = this->canvas_center_x_ + (input->xmax - this->canvas_center_x_) / scx + 1; + newInput.xmin = this->canvas_center_x_ + (input->xmin - this->canvas_center_x_) / scx - 1; + newInput.ymax = this->canvas_center_y_ + (input->ymax - this->canvas_center_y_) / scy + 1; + newInput.ymin = this->canvas_center_y_ + (input->ymin - this->canvas_center_y_) / scy - 1; } else { newInput.xmax = this->getWidth(); @@ -222,8 +330,8 @@ void ScaleAbsoluteOperation::executePixelSampled(float output[4], float relativeXScale = scx / width; float relativeYScale = scy / height; - float nx = this->m_centerX + (x - this->m_centerX) / relativeXScale; - float ny = this->m_centerY + (y - this->m_centerY) / relativeYScale; + float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / relativeXScale; + float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / relativeYScale; this->m_inputOperation->readSampled(output, nx, ny, effective_sampler); } @@ -248,10 +356,14 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input, float relateveXScale = scx / width; float relateveYScale = scy / height; - newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / relateveXScale; - newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / relateveXScale; - newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / relateveYScale; - newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / relateveYScale; + newInput.xmax = this->canvas_center_x_ + + (input->xmax - this->canvas_center_x_) / relateveXScale; + newInput.xmin = this->canvas_center_x_ + + (input->xmin - this->canvas_center_x_) / relateveXScale; + newInput.ymax = this->canvas_center_y_ + + (input->ymax - this->canvas_center_y_) / relateveYScale; + newInput.ymin = this->canvas_center_y_ + + (input->ymin - this->canvas_center_y_) / relateveYScale; } else { newInput.xmax = this->getWidth(); @@ -272,11 +384,12 @@ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation() this->m_is_offset = false; } -void ScaleFixedSizeOperation::init_data() +void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) { - const NodeOperation *input_op = getInputOperation(0); - this->m_relX = input_op->getWidth() / (float)this->m_newWidth; - this->m_relY = input_op->getHeight() / (float)this->m_newHeight; + const int input_width = BLI_rcti_size_x(&input_canvas); + const int input_height = BLI_rcti_size_y(&input_canvas); + this->m_relX = input_width / (float)this->m_newWidth; + this->m_relY = input_height / (float)this->m_newHeight; /* *** all the options below are for a fairly special case - camera framing *** */ if (this->m_offsetX != 0.0f || this->m_offsetY != 0.0f) { @@ -294,8 +407,8 @@ void ScaleFixedSizeOperation::init_data() if (this->m_is_aspect) { /* apply aspect from clip */ - const float w_src = input_op->getWidth(); - const float h_src = input_op->getHeight(); + const float w_src = input_width; + const float h_src = input_height; /* destination aspect is already applied from the camera frame */ const float w_dst = this->m_newWidth; @@ -310,12 +423,32 @@ void ScaleFixedSizeOperation::init_data() const float div = asp_src / asp_dst; this->m_relX /= div; this->m_offsetX += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f; + if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) { + int fit_width = m_newWidth * div; + if (fit_width > max_scale_canvas_size_.x) { + fit_width = max_scale_canvas_size_.x; + } + + const int added_width = fit_width - m_newWidth; + m_newWidth += added_width; + m_offsetX += added_width / 2.0f; + } } else { /* fit Y */ const float div = asp_dst / asp_src; this->m_relY /= div; this->m_offsetY += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f; + if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) { + int fit_height = m_newHeight * div; + if (fit_height > max_scale_canvas_size_.y) { + fit_height = max_scale_canvas_size_.y; + } + + const int added_height = fit_height - m_newHeight; + m_newHeight += added_height; + m_offsetY += added_height / 2.0f; + } } this->m_is_offset = true; @@ -371,9 +504,21 @@ void ScaleFixedSizeOperation::determine_canvas(const rcti &preferred_area, rcti rcti local_preferred = preferred_area; local_preferred.xmax = local_preferred.xmin + m_newWidth; local_preferred.ymax = local_preferred.ymin + m_newHeight; - BaseScaleOperation::determine_canvas(local_preferred, r_area); - r_area.xmax = r_area.xmin + m_newWidth; - r_area.ymax = r_area.ymin + m_newHeight; + rcti input_canvas; + const bool input_determined = getInputSocket(0)->determine_canvas(local_preferred, input_canvas); + if (input_determined) { + init_data(input_canvas); + r_area = input_canvas; + if (execution_model_ == eExecutionModel::FullFrame) { + r_area.xmin /= m_relX; + r_area.ymin /= m_relY; + r_area.xmin += m_offsetX; + r_area.ymin += m_offsetY; + } + + r_area.xmax = r_area.xmin + m_newWidth; + r_area.ymax = r_area.ymin + m_newHeight; + } } void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx, @@ -382,10 +527,11 @@ void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmax = (output_area.xmax - m_offsetX) * this->m_relX; - r_input_area.xmin = (output_area.xmin - m_offsetX) * this->m_relX; - r_input_area.ymax = (output_area.ymax - m_offsetY) * this->m_relY; - r_input_area.ymin = (output_area.ymin - m_offsetY) * this->m_relY; + + r_input_area.xmax = ceilf((output_area.xmax - m_offsetX) * this->m_relX); + r_input_area.xmin = floorf((output_area.xmin - m_offsetX) * this->m_relX); + r_input_area.ymax = ceilf((output_area.ymax - m_offsetY) * this->m_relY); + r_input_area.ymin = floorf((output_area.ymin - m_offsetY) * this->m_relY); expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler); } @@ -398,14 +544,17 @@ void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output, BuffersIterator it = output->iterate_with({}, area); if (this->m_is_offset) { for (; !it.is_end(); ++it) { - const float nx = (it.x - this->m_offsetX) * this->m_relX; - const float ny = (it.y - this->m_offsetY) * this->m_relY; - input_img->read_elem_sampled(nx, ny, sampler, it.out); + const float nx = (canvas_.xmin + it.x - this->m_offsetX) * this->m_relX; + const float ny = (canvas_.ymin + it.y - this->m_offsetY) * this->m_relY; + input_img->read_elem_sampled(nx - canvas_.xmin, ny - canvas_.ymin, sampler, it.out); } } else { for (; !it.is_end(); ++it) { - input_img->read_elem_sampled(it.x * this->m_relX, it.y * this->m_relY, sampler, it.out); + input_img->read_elem_sampled((canvas_.xmin + it.x) * this->m_relX - canvas_.xmin, + (canvas_.ymin + it.y) * this->m_relY - canvas_.ymin, + sampler, + it.out); } } } diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h index 04fa4fe62d1..746e490d900 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.h +++ b/source/blender/compositor/operations/COM_ScaleOperation.h @@ -23,6 +23,9 @@ namespace blender::compositor { class BaseScaleOperation : public MultiThreadedOperation { + public: + static constexpr float DEFAULT_MAX_SCALE_CANVAS_SIZE = 12000; + public: void setSampler(PixelSampler sampler) { @@ -33,6 +36,8 @@ class BaseScaleOperation : public MultiThreadedOperation { m_variable_size = variable_size; }; + void set_scale_canvas_max_size(Size2f size); + protected: BaseScaleOperation(); @@ -41,20 +46,26 @@ class BaseScaleOperation : public MultiThreadedOperation { return (m_sampler == -1) ? sampler : (PixelSampler)m_sampler; } + Size2f max_scale_canvas_size_ = {DEFAULT_MAX_SCALE_CANVAS_SIZE, DEFAULT_MAX_SCALE_CANVAS_SIZE}; int m_sampler; + /* TODO(manzanilla): to be removed with tiled implementation. */ bool m_variable_size; }; class ScaleOperation : public BaseScaleOperation { public: - static constexpr float MIN_SCALE = 0.0001f; + static constexpr float MIN_RELATIVE_SCALE = 0.0001f; protected: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int X_INPUT_INDEX = 1; + static constexpr int Y_INPUT_INDEX = 2; + SocketReader *m_inputOperation; SocketReader *m_inputXOperation; SocketReader *m_inputYOperation; - float m_centerX; - float m_centerY; + float canvas_center_x_; + float canvas_center_y_; public: ScaleOperation(); @@ -62,9 +73,28 @@ class ScaleOperation : public BaseScaleOperation { static float scale_coord(const float coord, const float center, const float relative_scale) { - return center + (coord - center) / MAX2(relative_scale, MIN_SCALE); + return center + (coord - center) * MAX2(relative_scale, MIN_RELATIVE_SCALE); } - static void scale_area(rcti &rect, float center_x, float center_y, float scale_x, float scale_y); + + static float scale_coord_inverted(const float coord, + const float center, + const float relative_scale) + { + return center + (coord - center) / MAX2(relative_scale, MIN_RELATIVE_SCALE); + } + + static void get_scale_offset(const rcti &input_canvas, + const rcti &scale_canvas, + float &r_scale_offset_x, + float &r_scale_offset_y); + static void scale_area(rcti &area, float relative_scale_x, float relative_scale_y); + static void get_scale_area_of_interest(const rcti &input_canvas, + const rcti &scale_canvas, + const float relative_scale_x, + const float relative_scale_y, + const rcti &output_area, + rcti &r_input_area); + static void clamp_area_size_max(rcti &area, Size2f max_size); void init_data() override; void initExecution() override; @@ -75,15 +105,17 @@ class ScaleOperation : public BaseScaleOperation { const rcti &area, Span inputs) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + protected: - virtual float get_relative_scale_x_factor() = 0; - virtual float get_relative_scale_y_factor() = 0; + virtual float get_relative_scale_x_factor(float width) = 0; + virtual float get_relative_scale_y_factor(float height) = 0; private: + bool is_scaling_variable(); float get_constant_scale(int input_op_idx, float factor); - float get_constant_scale_x(); - float get_constant_scale_y(); - void scale_area(rcti &rect, float scale_x, float scale_y); + float get_constant_scale_x(float width); + float get_constant_scale_y(float height); }; class ScaleRelativeOperation : public ScaleOperation { @@ -94,11 +126,13 @@ class ScaleRelativeOperation : public ScaleOperation { ReadBufferOperation *readOperation, rcti *output) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - float get_relative_scale_x_factor() override + + float get_relative_scale_x_factor(float UNUSED(width)) override { return 1.0f; } - float get_relative_scale_y_factor() override + + float get_relative_scale_y_factor(float UNUSED(height)) override { return 1.0f; } @@ -110,13 +144,15 @@ class ScaleAbsoluteOperation : public ScaleOperation { ReadBufferOperation *readOperation, rcti *output) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - float get_relative_scale_x_factor() override + + float get_relative_scale_x_factor(float width) override { - return 1.0f / getWidth(); + return 1.0f / width; } - float get_relative_scale_y_factor() override + + float get_relative_scale_y_factor(float height) override { - return 1.0f / getHeight(); + return 1.0f / height; } }; @@ -144,7 +180,6 @@ class ScaleFixedSizeOperation : public BaseScaleOperation { void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void init_data() override; void initExecution() override; void deinitExecution() override; void setNewWidth(int width) @@ -173,6 +208,9 @@ class ScaleFixedSizeOperation : public BaseScaleOperation { void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; + + private: + void init_data(const rcti &input_canvas); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index 87949a1b24f..21d9210bdac 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -382,6 +382,23 @@ void ScreenLensDistortionOperation::updateVariables(float distortion, float disp mul_v3_v3fl(m_k4, m_k, 4.0f); } +void ScreenLensDistortionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + switch (execution_model_) { + case eExecutionModel::FullFrame: { + set_determined_canvas_modifier([=](rcti &canvas) { + /* Ensure screen space. */ + BLI_rcti_translate(&canvas, -canvas.xmin, -canvas.ymin); + }); + break; + } + default: + break; + } + + NodeOperation::determine_canvas(preferred_area, r_area); +} + void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, const rcti &UNUSED(output_area), rcti &r_input_area) diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h index 616fc8883b0..93681b2f934 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h @@ -86,6 +86,7 @@ class ScreenLensDistortionOperation : public MultiThreadedOperation { ReadBufferOperation *readOperation, rcti *output) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) 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, diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index 94977ed5adf..c06e3ac7cb0 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -74,26 +74,18 @@ void TextureBaseOperation::deinitExecution() void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - switch (execution_model_) { - case eExecutionModel::Tiled: { - r_area = preferred_area; - if (BLI_rcti_is_empty(&preferred_area)) { - int width = this->m_rd->xsch * this->m_rd->size / 100; - int height = this->m_rd->ysch * this->m_rd->size / 100; - r_area.xmax = preferred_area.xmin + width; - r_area.ymax = preferred_area.ymin + height; - } - break; - } - case eExecutionModel::FullFrame: { - /* Determine inputs. */ - rcti temp; - NodeOperation::determine_canvas(preferred_area, temp); + r_area = preferred_area; + if (BLI_rcti_is_empty(&preferred_area)) { + int width = this->m_rd->xsch * this->m_rd->size / 100; + int height = this->m_rd->ysch * this->m_rd->size / 100; + r_area.xmax = preferred_area.xmin + width; + r_area.ymax = preferred_area.ymin + height; + } - /* Don't use input areas, they are only used as parameters. */ - r_area = preferred_area; - break; - } + if (execution_model_ == eExecutionModel::FullFrame) { + /* Determine inputs. */ + rcti temp; + NodeOperation::determine_canvas(r_area, temp); } } diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index 67eb26993d0..cb671c54abe 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -28,7 +28,7 @@ namespace blender::compositor { TonemapOperation::TonemapOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); + this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Color); this->m_imageReader = nullptr; this->m_data = nullptr; diff --git a/source/blender/compositor/operations/COM_TransformOperation.cc b/source/blender/compositor/operations/COM_TransformOperation.cc index f73c9e9d956..5f6e9ed4d21 100644 --- a/source/blender/compositor/operations/COM_TransformOperation.cc +++ b/source/blender/compositor/operations/COM_TransformOperation.cc @@ -27,55 +27,40 @@ namespace blender::compositor { TransformOperation::TransformOperation() { - addInputSocket(DataType::Color); - addInputSocket(DataType::Value); - addInputSocket(DataType::Value); - addInputSocket(DataType::Value); - addInputSocket(DataType::Value); + addInputSocket(DataType::Color, ResizeMode::None); + addInputSocket(DataType::Value, ResizeMode::None); + addInputSocket(DataType::Value, ResizeMode::None); + addInputSocket(DataType::Value, ResizeMode::None); + addInputSocket(DataType::Value, ResizeMode::None); addOutputSocket(DataType::Color); translate_factor_x_ = 1.0f; translate_factor_y_ = 1.0f; convert_degree_to_rad_ = false; sampler_ = PixelSampler::Bilinear; invert_ = false; + max_scale_canvas_size_ = {ScaleOperation::DEFAULT_MAX_SCALE_CANVAS_SIZE, + ScaleOperation::DEFAULT_MAX_SCALE_CANVAS_SIZE}; +} + +void TransformOperation::set_scale_canvas_max_size(Size2f size) +{ + max_scale_canvas_size_ = size; } void TransformOperation::init_data() { - /* Translation. */ - translate_x_ = 0; - NodeOperation *x_op = getInputOperation(X_INPUT_INDEX); - if (x_op->get_flags().is_constant_operation) { - translate_x_ = static_cast(x_op)->get_constant_elem()[0] * - translate_factor_x_; - } - translate_y_ = 0; - NodeOperation *y_op = getInputOperation(Y_INPUT_INDEX); - if (y_op->get_flags().is_constant_operation) { - translate_y_ = static_cast(y_op)->get_constant_elem()[0] * - translate_factor_y_; - } - /* Scaling. */ - scale_center_x_ = getWidth() / 2.0; - scale_center_y_ = getHeight() / 2.0; - constant_scale_ = 1.0f; - NodeOperation *scale_op = getInputOperation(SCALE_INPUT_INDEX); - if (scale_op->get_flags().is_constant_operation) { - constant_scale_ = static_cast(scale_op)->get_constant_elem()[0]; - } + translate_x_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f) * + translate_factor_x_; + translate_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f) * + translate_factor_y_; - /* Rotation. */ - rotate_center_x_ = (getWidth() - 1.0) / 2.0; - rotate_center_y_ = (getHeight() - 1.0) / 2.0; - NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX); - const bool is_constant_degree = degree_op->get_flags().is_constant_operation; - const float degree = is_constant_degree ? - static_cast(degree_op)->get_constant_elem()[0] : - 0.0f; + const float degree = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f); const double rad = convert_degree_to_rad_ ? DEG2RAD((double)degree) : degree; rotate_cosine_ = cos(rad); rotate_sine_ = sin(rad); + + scale_ = get_input_operation(SCALE_INPUT_INDEX)->get_constant_value_default(1.0f); } void TransformOperation::get_area_of_interest(const int input_idx, @@ -84,26 +69,41 @@ void TransformOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - BLI_rcti_translate(&r_input_area, translate_x_, translate_y_); - ScaleOperation::scale_area( - r_input_area, scale_center_x_, scale_center_y_, constant_scale_, constant_scale_); - RotateOperation::get_area_rotation_bounds(r_input_area, - rotate_center_x_, - rotate_center_y_, - rotate_sine_, - rotate_cosine_, - r_input_area); + NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX); + const rcti &image_canvas = image_op->get_canvas(); + if (invert_) { + /* Scale -> Rotate -> Translate. */ + r_input_area = output_area; + BLI_rcti_translate(&r_input_area, -translate_x_, -translate_y_); + RotateOperation::get_rotation_area_of_interest(scale_canvas_, + rotate_canvas_, + rotate_sine_, + rotate_cosine_, + r_input_area, + r_input_area); + ScaleOperation::get_scale_area_of_interest( + image_canvas, scale_canvas_, scale_, scale_, r_input_area, r_input_area); + } + else { + /* Translate -> Rotate -> Scale. */ + ScaleOperation::get_scale_area_of_interest( + rotate_canvas_, scale_canvas_, scale_, scale_, output_area, r_input_area); + RotateOperation::get_rotation_area_of_interest(translate_canvas_, + rotate_canvas_, + rotate_sine_, + rotate_cosine_, + r_input_area, + r_input_area); + BLI_rcti_translate(&r_input_area, -translate_x_, -translate_y_); + } expand_area_for_sampler(r_input_area, sampler_); break; } case X_INPUT_INDEX: case Y_INPUT_INDEX: - case DEGREE_INPUT_INDEX: { - r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; - break; - } + case DEGREE_INPUT_INDEX: case SCALE_INPUT_INDEX: { - r_input_area = output_area; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; break; } } @@ -114,8 +114,7 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX]; - MemoryBuffer *input_scale = inputs[SCALE_INPUT_INDEX]; - BuffersIterator it = output->iterate_with({input_scale}, area); + BuffersIterator it = output->iterate_with({}, area); if (invert_) { transform_inverted(it, input_img); } @@ -124,31 +123,111 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void TransformOperation::transform(BuffersIterator &it, const MemoryBuffer *input_img) +void TransformOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - for (; !it.is_end(); ++it) { - const float scale = *it.in(0); - float x = it.x - translate_x_; - float y = it.y - translate_y_; - RotateOperation::rotate_coords( - x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_); - x = ScaleOperation::scale_coord(x, scale_center_x_, scale); - y = ScaleOperation::scale_coord(y, scale_center_y_, scale); - input_img->read_elem_sampled(x, y, sampler_, it.out); + const bool image_determined = + getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + if (image_determined) { + rcti image_canvas = r_area; + rcti unused; + getInputSocket(X_INPUT_INDEX)->determine_canvas(image_canvas, unused); + getInputSocket(Y_INPUT_INDEX)->determine_canvas(image_canvas, unused); + getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(image_canvas, unused); + getInputSocket(SCALE_INPUT_INDEX)->determine_canvas(image_canvas, unused); + + init_data(); + if (invert_) { + /* Scale -> Rotate -> Translate. */ + scale_canvas_ = image_canvas; + ScaleOperation::scale_area(scale_canvas_, scale_, scale_); + const Size2f max_scale_size = { + MAX2(BLI_rcti_size_x(&image_canvas), max_scale_canvas_size_.x), + MAX2(BLI_rcti_size_y(&image_canvas), max_scale_canvas_size_.y)}; + ScaleOperation::clamp_area_size_max(scale_canvas_, max_scale_size); + + RotateOperation::get_rotation_canvas( + scale_canvas_, rotate_sine_, rotate_cosine_, rotate_canvas_); + + translate_canvas_ = rotate_canvas_; + BLI_rcti_translate(&translate_canvas_, translate_x_, translate_y_); + + r_area = translate_canvas_; + } + else { + /* Translate -> Rotate -> Scale. */ + translate_canvas_ = image_canvas; + BLI_rcti_translate(&translate_canvas_, translate_x_, translate_y_); + + RotateOperation::get_rotation_canvas( + translate_canvas_, rotate_sine_, rotate_cosine_, rotate_canvas_); + + scale_canvas_ = rotate_canvas_; + ScaleOperation::scale_area(scale_canvas_, scale_, scale_); + + const Size2f max_scale_size = { + MAX2(BLI_rcti_size_x(&rotate_canvas_), max_scale_canvas_size_.x), + MAX2(BLI_rcti_size_y(&rotate_canvas_), max_scale_canvas_size_.y)}; + ScaleOperation::clamp_area_size_max(scale_canvas_, max_scale_size); + + r_area = scale_canvas_; + } } } +/** Translate -> Rotate -> Scale. */ +void TransformOperation::transform(BuffersIterator &it, const MemoryBuffer *input_img) +{ + float rotate_center_x, rotate_center_y; + RotateOperation::get_rotation_center(translate_canvas_, rotate_center_x, rotate_center_y); + float rotate_offset_x, rotate_offset_y; + RotateOperation::get_rotation_offset( + translate_canvas_, rotate_canvas_, rotate_offset_x, rotate_offset_y); + + const float scale_center_x = BLI_rcti_size_x(&rotate_canvas_) / 2.0f; + const float scale_center_y = BLI_rcti_size_y(&rotate_canvas_) / 2.0f; + float scale_offset_x, scale_offset_y; + ScaleOperation::get_scale_offset(rotate_canvas_, scale_canvas_, scale_offset_x, scale_offset_y); + + for (; !it.is_end(); ++it) { + float x = ScaleOperation::scale_coord_inverted(it.x + scale_offset_x, scale_center_x, scale_); + float y = ScaleOperation::scale_coord_inverted(it.y + scale_offset_y, scale_center_y, scale_); + + x = rotate_offset_x + x; + y = rotate_offset_y + y; + RotateOperation::rotate_coords( + x, y, rotate_center_x, rotate_center_y, rotate_sine_, rotate_cosine_); + + input_img->read_elem_sampled(x - translate_x_, y - translate_y_, sampler_, it.out); + } +} + +/** Scale -> Rotate -> Translate. */ void TransformOperation::transform_inverted(BuffersIterator &it, const MemoryBuffer *input_img) { + const rcti &image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas(); + const float scale_center_x = BLI_rcti_size_x(&image_canvas) / 2.0f - translate_x_; + const float scale_center_y = BLI_rcti_size_y(&image_canvas) / 2.0f - translate_y_; + float scale_offset_x, scale_offset_y; + ScaleOperation::get_scale_offset(image_canvas, scale_canvas_, scale_offset_x, scale_offset_y); + + float rotate_center_x, rotate_center_y; + RotateOperation::get_rotation_center(translate_canvas_, rotate_center_x, rotate_center_y); + rotate_center_x -= translate_x_; + rotate_center_y -= translate_y_; + float rotate_offset_x, rotate_offset_y; + RotateOperation::get_rotation_offset( + scale_canvas_, rotate_canvas_, rotate_offset_x, rotate_offset_y); + for (; !it.is_end(); ++it) { - const float scale = *it.in(0); - float x = ScaleOperation::scale_coord(it.x, scale_center_x_, scale); - float y = ScaleOperation::scale_coord(it.y, scale_center_y_, scale); + float x = rotate_offset_x + (it.x - translate_x_); + float y = rotate_offset_y + (it.y - translate_y_); RotateOperation::rotate_coords( - x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_); - x -= translate_x_; - y -= translate_y_; + x, y, rotate_center_x, rotate_center_y, rotate_sine_, rotate_cosine_); + + x = ScaleOperation::scale_coord_inverted(x + scale_offset_x, scale_center_x, scale_); + y = ScaleOperation::scale_coord_inverted(y + scale_offset_y, scale_center_y, scale_); + input_img->read_elem_sampled(x, y, sampler_, it.out); } } diff --git a/source/blender/compositor/operations/COM_TransformOperation.h b/source/blender/compositor/operations/COM_TransformOperation.h index 480998a0207..3c5584a1bea 100644 --- a/source/blender/compositor/operations/COM_TransformOperation.h +++ b/source/blender/compositor/operations/COM_TransformOperation.h @@ -30,15 +30,14 @@ class TransformOperation : public MultiThreadedOperation { constexpr static int DEGREE_INPUT_INDEX = 3; constexpr static int SCALE_INPUT_INDEX = 4; - float scale_center_x_; - float scale_center_y_; - float rotate_center_x_; - float rotate_center_y_; float rotate_cosine_; float rotate_sine_; - float translate_x_; - float translate_y_; - float constant_scale_; + int translate_x_; + int translate_y_; + float scale_; + rcti scale_canvas_; + rcti rotate_canvas_; + rcti translate_canvas_; /* Set variables. */ PixelSampler sampler_; @@ -46,6 +45,7 @@ class TransformOperation : public MultiThreadedOperation { float translate_factor_x_; float translate_factor_y_; bool invert_; + Size2f max_scale_canvas_size_; public: TransformOperation(); @@ -71,16 +71,18 @@ class TransformOperation : public MultiThreadedOperation { invert_ = value; } + void set_scale_canvas_max_size(Size2f size); + void init_data() 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, Span inputs) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + private: - /** Translate -> Rotate -> Scale. */ void transform(BuffersIterator &it, const MemoryBuffer *input_img); - /** Scale -> Rotate -> Translate. */ void transform_inverted(BuffersIterator &it, const MemoryBuffer *input_img); }; diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc index 266e960037c..9868f21654e 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.cc +++ b/source/blender/compositor/operations/COM_TranslateOperation.cc @@ -23,11 +23,11 @@ namespace blender::compositor { TranslateOperation::TranslateOperation() : TranslateOperation(DataType::Color) { } -TranslateOperation::TranslateOperation(DataType data_type) +TranslateOperation::TranslateOperation(DataType data_type, ResizeMode resize_mode) { - this->addInputSocket(data_type); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); + this->addInputSocket(data_type, resize_mode); + this->addInputSocket(DataType::Value, ResizeMode::None); + this->addInputSocket(DataType::Value, ResizeMode::None); this->addOutputSocket(data_type); this->set_canvas_input_index(0); this->m_inputOperation = nullptr; @@ -39,6 +39,7 @@ TranslateOperation::TranslateOperation(DataType data_type) this->x_extend_mode_ = MemoryBufferExtend::Clip; this->y_extend_mode_ = MemoryBufferExtend::Clip; } + void TranslateOperation::initExecution() { this->m_inputOperation = this->getInputSocketReader(0); @@ -122,6 +123,9 @@ void TranslateOperation::get_area_of_interest(const int input_idx, BLI_rcti_translate(&r_input_area, 0, -delta_y); } } + else { + r_input_area = output_area; + } } void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -142,4 +146,27 @@ void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output, } } +TranslateCanvasOperation::TranslateCanvasOperation() + : TranslateOperation(DataType::Color, ResizeMode::None) +{ +} + +void TranslateCanvasOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + const bool determined = + getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + if (determined) { + NodeOperationInput *x_socket = getInputSocket(X_INPUT_INDEX); + NodeOperationInput *y_socket = getInputSocket(Y_INPUT_INDEX); + rcti unused; + x_socket->determine_canvas(r_area, unused); + y_socket->determine_canvas(r_area, unused); + + ensureDelta(); + const float delta_x = x_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaX() : 0.0f; + const float delta_y = y_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaY() : 0.0f; + BLI_rcti_translate(&r_area, delta_x, delta_y); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h index ce1965cecef..c5a3d1ffd99 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.h +++ b/source/blender/compositor/operations/COM_TranslateOperation.h @@ -24,6 +24,11 @@ namespace blender::compositor { class TranslateOperation : public MultiThreadedOperation { + protected: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int X_INPUT_INDEX = 1; + static constexpr int Y_INPUT_INDEX = 2; + private: SocketReader *m_inputOperation; SocketReader *m_inputXOperation; @@ -33,12 +38,14 @@ class TranslateOperation : public MultiThreadedOperation { bool m_isDeltaSet; float m_factorX; float m_factorY; + + protected: MemoryBufferExtend x_extend_mode_; MemoryBufferExtend y_extend_mode_; public: TranslateOperation(); - TranslateOperation(DataType data_type); + TranslateOperation(DataType data_type, ResizeMode mode = ResizeMode::Center); bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; @@ -67,16 +74,8 @@ class TranslateOperation : public MultiThreadedOperation { this->m_deltaY = tempDelta[0]; } else { - this->m_deltaX = 0; - NodeOperation *x_op = getInputOperation(1); - if (x_op->get_flags().is_constant_operation) { - this->m_deltaX = ((ConstantOperation *)x_op)->get_constant_elem()[0]; - } - this->m_deltaY = 0; - NodeOperation *y_op = getInputOperation(2); - if (y_op->get_flags().is_constant_operation) { - this->m_deltaY = ((ConstantOperation *)y_op)->get_constant_elem()[0]; - } + m_deltaX = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f); + m_deltaY = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f); } this->m_isDeltaSet = true; @@ -93,4 +92,10 @@ class TranslateOperation : public MultiThreadedOperation { Span inputs) override; }; +class TranslateCanvasOperation : public TranslateOperation { + public: + TranslateCanvasOperation(); + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; +}; + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc index 0fe44e3a61f..c524447a4fa 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc @@ -28,8 +28,8 @@ namespace blender::compositor { VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation() { this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color, ResizeMode::None); /* Do not resize the bokeh image. */ - this->addInputSocket(DataType::Value); /* Radius. */ + this->addInputSocket(DataType::Color, ResizeMode::Align); /* Do not resize the bokeh image. */ + this->addInputSocket(DataType::Value); /* Radius. */ #ifdef COM_DEFOCUS_SEARCH /* Inverse search radius optimization structure. */ this->addInputSocket(DataType::Color, ResizeMode::None); @@ -440,7 +440,7 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer * /* #InverseSearchRadiusOperation. */ InverseSearchRadiusOperation::InverseSearchRadiusOperation() { - this->addInputSocket(DataType::Value, ResizeMode::None); /* Radius. */ + this->addInputSocket(DataType::Value, ResizeMode::Align); /* Radius. */ this->addOutputSocket(DataType::Color); this->flags.complex = true; this->m_inputRadius = nullptr; diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index 1cb98ac8474..1faff0fd07f 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -23,6 +23,7 @@ #include "BLI_math_color.h" #include "BLI_math_vector.h" #include "BLI_utildefines.h" +#include "COM_ExecutionSystem.h" #include "MEM_guardedalloc.h" #include "PIL_time.h" #include "WM_api.h" @@ -34,6 +35,8 @@ namespace blender::compositor { +static int MAX_VIEWER_TRANSLATION_PADDING = 12000; + ViewerOperation::ViewerOperation() { this->setImage(nullptr); @@ -67,7 +70,7 @@ void ViewerOperation::initExecution() this->m_depthInput = getInputSocketReader(2); this->m_doDepthBuffer = (this->m_depthInput != nullptr); - if (isActiveViewerOutput()) { + if (isActiveViewerOutput() && !exec_system_->is_breaked()) { initImage(); } } @@ -130,6 +133,7 @@ void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) rcti local_preferred = preferred_area; local_preferred.xmax = local_preferred.xmin + sceneRenderWidth; local_preferred.ymax = local_preferred.ymin + sceneRenderHeight; + NodeOperation::determine_canvas(local_preferred, r_area); } @@ -155,13 +159,24 @@ void ViewerOperation::initImage() BLI_thread_unlock(LOCK_DRAW_IMAGE); return; } - if (ibuf->x != (int)getWidth() || ibuf->y != (int)getHeight()) { + int padding_x = abs(canvas_.xmin) * 2; + int padding_y = abs(canvas_.ymin) * 2; + if (padding_x > MAX_VIEWER_TRANSLATION_PADDING) { + padding_x = MAX_VIEWER_TRANSLATION_PADDING; + } + if (padding_y > MAX_VIEWER_TRANSLATION_PADDING) { + padding_y = MAX_VIEWER_TRANSLATION_PADDING; + } + + display_width_ = getWidth() + padding_x; + display_height_ = getHeight() + padding_y; + if (ibuf->x != display_width_ || ibuf->y != display_height_) { imb_freerectImBuf(ibuf); imb_freerectfloatImBuf(ibuf); IMB_freezbuffloatImBuf(ibuf); - ibuf->x = getWidth(); - ibuf->y = getHeight(); + ibuf->x = display_width_; + ibuf->y = display_height_; /* zero size can happen if no image buffers exist to define a sensible resolution */ if (ibuf->x > 0 && ibuf->y > 0) { imb_addrectfloatImBuf(ibuf); @@ -193,11 +208,15 @@ void ViewerOperation::initImage() void ViewerOperation::updateImage(const rcti *rect) { + if (exec_system_->is_breaked()) { + return; + } + float *buffer = m_outputBuffer; IMB_partial_display_buffer_update(this->m_ibuf, buffer, nullptr, - getWidth(), + display_width_, 0, 0, this->m_viewSettings, @@ -227,29 +246,46 @@ void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), return; } + const int offset_x = area.xmin + (canvas_.xmin > 0 ? canvas_.xmin * 2 : 0); + const int offset_y = area.ymin + (canvas_.ymin > 0 ? canvas_.ymin * 2 : 0); MemoryBuffer output_buffer( - m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); + m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, display_width_, display_height_); const MemoryBuffer *input_image = inputs[0]; - output_buffer.copy_from(input_image, area); + output_buffer.copy_from(input_image, area, offset_x, offset_y); if (this->m_useAlphaInput) { const MemoryBuffer *input_alpha = inputs[1]; - output_buffer.copy_from(input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3); + output_buffer.copy_from( + input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, offset_x, offset_y, 3); } if (m_depthBuffer) { MemoryBuffer depth_buffer( - m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); + m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, display_width_, display_height_); const MemoryBuffer *input_depth = inputs[2]; - depth_buffer.copy_from(input_depth, area); + depth_buffer.copy_from(input_depth, area, offset_x, offset_y); } - updateImage(&area); + rcti display_area; + BLI_rcti_init(&display_area, + offset_x, + offset_x + BLI_rcti_size_x(&area), + offset_y, + offset_y + BLI_rcti_size_y(&area)); + updateImage(&display_area); } void ViewerOperation::clear_display_buffer() { BLI_assert(isActiveViewerOutput()); + if (exec_system_->is_breaked()) { + return; + } + initImage(); + if (m_outputBuffer == nullptr) { + return; + } + size_t buf_bytes = (size_t)m_ibuf->y * m_ibuf->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float); if (buf_bytes > 0) { memset(m_outputBuffer, 0, buf_bytes); diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h index e759bcf9898..95ee982f692 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.h +++ b/source/blender/compositor/operations/COM_ViewerOperation.h @@ -50,6 +50,9 @@ class ViewerOperation : public MultiThreadedOperation { SocketReader *m_alphaInput; SocketReader *m_depthInput; + int display_width_; + int display_height_; + public: ViewerOperation(); void initExecution() override; From 0830211c952983075f420175904fa10edf7b7f08 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 28 Sep 2021 19:33:18 +0200 Subject: [PATCH 0323/1500] Cleanup: Remove XRange and YRange in Compositor Mostly unused and originally meant for areas with positive values. With canvas compositing areas position may be negative. --- source/blender/compositor/COM_defines.h | 20 ------------------- .../operations/COM_EllipseMaskOperation.cc | 4 ++-- .../compositor/operations/COM_MixOperation.cc | 2 +- 3 files changed, 3 insertions(+), 23 deletions(-) diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 55b331e279f..9991414aba4 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -127,24 +127,4 @@ constexpr float COM_BLUR_BOKEH_PIXELS = 512; constexpr rcti COM_AREA_NONE = {0, 0, 0, 0}; constexpr rcti COM_CONSTANT_INPUT_AREA_OF_INTEREST = COM_AREA_NONE; -constexpr IndexRange XRange(const rcti &area) -{ - return IndexRange(area.xmin, area.xmax - area.xmin); -} - -constexpr IndexRange YRange(const rcti &area) -{ - return IndexRange(area.ymin, area.ymax - area.ymin); -} - -constexpr IndexRange XRange(const rcti *area) -{ - return XRange(*area); -} - -constexpr IndexRange YRange(const rcti *area) -{ - return YRange(*area); -} - } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc index eb1fd98a590..bf6eee6d3f9 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc @@ -162,13 +162,13 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, const float half_h = this->m_data->height / 2.0f; const float tx = half_w * half_w; const float ty = half_h * half_h; - for (const int y : YRange(area)) { + for (int y = area.ymin; y < area.ymax; y++) { const float op_ry = y / op_h; const float dy = (op_ry - this->m_data->y) / m_aspectRatio; float *out = output->get_elem(area.xmin, y); const float *mask = input_mask->get_elem(area.xmin, y); const float *value = input_value->get_elem(area.xmin, y); - for (const int x : XRange(area)) { + for (int x = area.xmin; x < area.xmax; x++) { const float op_rx = x / op_w; const float dx = op_rx - this->m_data->x; const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy); diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc index 4b9f4786e79..895d32e6fee 100644 --- a/source/blender/compositor/operations/COM_MixOperation.cc +++ b/source/blender/compositor/operations/COM_MixOperation.cc @@ -109,7 +109,7 @@ void MixBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, p.value_stride = input_value->elem_stride; p.color1_stride = input_color1->elem_stride; p.color2_stride = input_color2->elem_stride; - for (const int y : YRange(area)) { + for (int y = area.ymin; y < area.ymax; y++) { p.out = output->get_elem(area.xmin, y); p.row_end = p.out + width * output->elem_stride; p.value = input_value->get_elem(area.xmin, y); From 283d76a70dba69665d08039a0a7c675c9efc7110 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 28 Sep 2021 19:33:26 +0200 Subject: [PATCH 0324/1500] Compositor: Full frame Glare node Part of T88150. --- .../operations/COM_GlareBaseOperation.cc | 34 +++++++++++++++++++ .../operations/COM_GlareBaseOperation.h | 10 ++++++ .../operations/COM_GlareThresholdOperation.cc | 20 +++++++++++ .../operations/COM_GlareThresholdOperation.h | 7 ++-- 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cc b/source/blender/compositor/operations/COM_GlareBaseOperation.cc index 90755d9f27a..cd4607b1dde 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cc @@ -26,6 +26,8 @@ GlareBaseOperation::GlareBaseOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); this->m_settings = nullptr; + flags.is_fullframe_operation = true; + is_output_rendered_ = false; } void GlareBaseOperation::initExecution() { @@ -69,4 +71,36 @@ bool GlareBaseOperation::determineDependingAreaOfInterest(rcti * /*input*/, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void GlareBaseOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = 0; + r_input_area.xmax = this->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = this->getHeight(); +} + +void GlareBaseOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &UNUSED(area), + Span inputs) +{ + if (!is_output_rendered_) { + MemoryBuffer *input = inputs[0]; + const bool is_input_inflated = input->is_a_single_elem(); + if (is_input_inflated) { + input = input->inflate(); + } + + this->generateGlare(output->getBuffer(), input, m_settings); + is_output_rendered_ = true; + + if (is_input_inflated) { + delete input; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h index 6dac6f5ecc7..5ca240a9e66 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.h +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h @@ -49,6 +49,8 @@ class GlareBaseOperation : public SingleThreadedOperation { */ NodeGlare *m_settings; + bool is_output_rendered_; + public: /** * Initialize the execution @@ -68,6 +70,14 @@ class GlareBaseOperation : public SingleThreadedOperation { ReadBufferOperation *readOperation, rcti *output) override; + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) final; + + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span inputs) final; + protected: GlareBaseOperation(); diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc index f8da0b9a102..1bf7cf5ae07 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc @@ -71,4 +71,24 @@ void GlareThresholdOperation::deinitExecution() this->m_inputProgram = nullptr; } +void GlareThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + const float threshold = this->m_settings->threshold; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color = it.in(0); + if (IMB_colormanagement_get_luminance(color) >= threshold) { + it.out[0] = color[0] - threshold; + it.out[1] = color[1] - threshold; + it.out[2] = color[2] - threshold; + + CLAMP3_MIN(it.out, 0.0f); + } + else { + zero_v3(it.out); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h index 1f247f58324..44f2d717c0e 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_light_types.h" namespace blender::compositor { -class GlareThresholdOperation : public NodeOperation { +class GlareThresholdOperation : public MultiThreadedOperation { private: /** * \brief Cached reference to the inputProgram @@ -59,6 +59,9 @@ class GlareThresholdOperation : public NodeOperation { } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor From 9f0a3a99ab664bc0f6378c5a4d6ac9e16f1f59f7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 28 Sep 2021 15:21:36 -0500 Subject: [PATCH 0325/1500] Geometry Nodes: Fields version of attribute proximity node Add a fields-aware implementation of the attribute proximity node. The Source position is an implicit position field, but can be connected with a position input node with alterations before use. The target input and mode function the same as the original node. Patch by Johnny Matthews with edits from Hans Goudey (@HooglyBoogly). Differential Revision: https://developer.blender.org/D12635 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 3 +- source/blender/makesdna/DNA_node_types.h | 11 + source/blender/makesrna/intern/rna_nodetree.c | 35 ++- .../modifiers/intern/MOD_nodes_evaluator.cc | 6 +- source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 3 +- source/blender/nodes/NOD_static_types.h | 3 +- .../legacy/node_geo_attribute_proximity.cc | 2 +- .../geometry/nodes/node_geo_proximity.cc | 235 ++++++++++++++++++ 11 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_proximity.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index bc78413f4e5..06992022edb 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -543,6 +543,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_legacy_poll), NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeProximity"), NodeItem("GeometryNodeBoundBox"), NodeItem("GeometryNodeConvexHull"), NodeItem("GeometryNodeTransform"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 8eff70af364..76a3e75d285 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1506,6 +1506,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MESH_TO_POINTS 1093 #define GEO_NODE_POINTS_TO_VERTICES 1094 #define GEO_NODE_CURVE_REVERSE 1095 +#define GEO_NODE_PROXIMITY 1096 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 416dae4c41e..a3a82bee8dc 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5707,6 +5707,7 @@ static void registerGeometryNodes() { register_node_type_geo_group(); + register_node_type_geo_legacy_attribute_proximity(); register_node_type_geo_legacy_attribute_randomize(); register_node_type_geo_legacy_material_assign(); register_node_type_geo_legacy_select_by_material(); @@ -5724,7 +5725,6 @@ static void registerGeometryNodes() register_node_type_geo_attribute_map_range(); register_node_type_geo_attribute_math(); register_node_type_geo_attribute_mix(); - register_node_type_geo_attribute_proximity(); register_node_type_geo_attribute_remove(); register_node_type_geo_attribute_separate_xyz(); register_node_type_geo_attribute_statistic(); @@ -5790,6 +5790,7 @@ static void registerGeometryNodes() register_node_type_geo_point_translate(); register_node_type_geo_points_to_vertices(); register_node_type_geo_points_to_volume(); + register_node_type_geo_proximity(); register_node_type_geo_raycast(); register_node_type_geo_realize_instances(); register_node_type_geo_sample_texture(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 538a80ce3e1..ffa6cb307b8 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1362,6 +1362,11 @@ typedef struct NodeGeometryAttributeProximity { uint8_t target_geometry_element; } NodeGeometryAttributeProximity; +typedef struct NodeGeometryProximity { + /* GeometryNodeProximityTargetType. */ + uint8_t target_element; +} NodeGeometryProximity; + typedef struct NodeGeometryVolumeToMesh { /* VolumeToMeshResolutionMode */ uint8_t resolution_mode; @@ -1946,6 +1951,12 @@ typedef enum GeometryNodeAttributeProximityTargetType { GEO_NODE_PROXIMITY_TARGET_FACES = 2, } GeometryNodeAttributeProximityTargetType; +typedef enum GeometryNodeProximityTargetType { + GEO_NODE_PROX_TARGET_POINTS = 0, + GEO_NODE_PROX_TARGET_EDGES = 1, + GEO_NODE_PROX_TARGET_FACES = 2, +} GeometryNodeProximityTargetType; + typedef enum GeometryNodeBooleanOperation { GEO_NODE_BOOLEAN_INTERSECT = 0, GEO_NODE_BOOLEAN_UNION = 1, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 556b8e94fe6..b5a42babc81 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9972,7 +9972,7 @@ static void def_geo_collection_info(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_attribute_proximity(StructRNA *srna) +static void def_geo_legacy_attribute_proximity(StructRNA *srna) { static const EnumPropertyItem target_geometry_element[] = { {GEO_NODE_PROXIMITY_TARGET_POINTS, @@ -10005,6 +10005,39 @@ static void def_geo_attribute_proximity(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_proximity(StructRNA *srna) +{ + static const EnumPropertyItem target_element_items[] = { + {GEO_NODE_PROX_TARGET_POINTS, + "POINTS", + ICON_NONE, + "Points", + "Calculate the proximity to the target's points (faster than the other modes)"}, + {GEO_NODE_PROX_TARGET_EDGES, + "EDGES", + ICON_NONE, + "Edges", + "Calculate the proximity to the target's edges"}, + {GEO_NODE_PROX_TARGET_FACES, + "FACES", + ICON_NONE, + "Faces", + "Calculate the proximity to the target's faces"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryProximity", "storage"); + + prop = RNA_def_property(srna, "target_element", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, target_element_items); + RNA_def_property_enum_default(prop, GEO_NODE_PROX_TARGET_FACES); + RNA_def_property_ui_text( + prop, "Target Geometry", "Element of the target geometry to calculate the distance from"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_volume_to_mesh(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index b9a9437d761..592e6180f80 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -329,7 +329,11 @@ static void get_socket_value(const SocketRef &socket, void *r_value) if (bsocket.flag & SOCK_HIDE_VALUE) { const bNode &bnode = *socket.bnode(); if (bsocket.type == SOCK_VECTOR) { - if (ELEM(bnode.type, GEO_NODE_SET_POSITION, SH_NODE_TEX_NOISE, GEO_NODE_MESH_TO_POINTS)) { + if (ELEM(bnode.type, + GEO_NODE_SET_POSITION, + SH_NODE_TEX_NOISE, + GEO_NODE_MESH_TO_POINTS, + GEO_NODE_PROXIMITY)) { new (r_value) Field( std::make_shared("position", CPPType::get())); return; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 49e76ffd1ad..416fcd8f9fd 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -235,6 +235,7 @@ set(SRC geometry/nodes/node_geo_mesh_to_points.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_points_to_vertices.cc + geometry/nodes/node_geo_proximity.cc geometry/nodes/node_geo_realize_instances.cc geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_set_position.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 7415563803b..af8661d9b8d 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -29,6 +29,7 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); void register_node_type_geo_custom_group(bNodeType *ntype); +void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); void register_node_type_geo_legacy_material_assign(void); void register_node_type_geo_legacy_select_by_material(void); @@ -46,7 +47,6 @@ void register_node_type_geo_attribute_fill(void); void register_node_type_geo_attribute_map_range(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_attribute_mix(void); -void register_node_type_geo_attribute_proximity(void); void register_node_type_geo_attribute_remove(void); void register_node_type_geo_attribute_separate_xyz(void); void register_node_type_geo_attribute_statistic(void); @@ -112,6 +112,7 @@ void register_node_type_geo_point_separate(void); void register_node_type_geo_point_translate(void); void register_node_type_geo_points_to_vertices(void); void register_node_type_geo_points_to_volume(void); +void register_node_type_geo_proximity(void); void register_node_type_geo_raycast(void); void register_node_type_geo_realize_instances(void); void register_node_type_geo_sample_texture(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index ce270572c0f..3ee3faf6122 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -286,7 +286,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_FILL, def_geo_attribute_fill, "L DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "LEGACY_ATTRIBUTE_MAP_RANGE", LegacyAttributeMapRange, "Attribute Map Range", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MATH, def_geo_attribute_math, "LEGACY_ATTRIBUTE_MATH", LegacyAttributeMath, "Attribute Math", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MIX, def_geo_attribute_mix, "LEGACY_ATTRIBUTE_MIX", LegacyAttributeMix, "Attribute Mix", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_legacy_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "LEGACY_ATTRIBUTE_RANDOMIZE", LegacyAttributeRandomize, "Attribute Randomize", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, 0, "LEGACY_ATTRIBUTE_SAMPLE_TEXTURE", LegacyAttributeSampleTexture, "Attribute Sample Texture", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "LEGACY_ATTRIBUTE_SEPARATE_XYZ", LegacyAttributeSeparateXYZ, "Attribute Separate XYZ", "") @@ -362,6 +362,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivid DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "") +DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "") DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc index 0cf411343cf..6120118f611 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc @@ -232,7 +232,7 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_attribute_proximity() +void register_node_type_geo_legacy_attribute_proximity() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc new file mode 100644 index 00000000000..2b1de5fbf95 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -0,0 +1,235 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "DNA_mesh_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_geometry_set.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_proximity_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Source Position").implicit_field(); + b.add_input("Target"); + b.add_output("Position").dependent_field(); + b.add_output("Distance").dependent_field(); +} + +static void geo_node_proximity_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "target_element", 0, "", ICON_NONE); +} + +static void geo_proximity_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryProximity *node_storage = (NodeGeometryProximity *)MEM_callocN( + sizeof(NodeGeometryProximity), __func__); + node_storage->target_element = GEO_NODE_PROX_TARGET_FACES; + node->storage = node_storage; +} + +static void calculate_mesh_proximity(const VArray &positions, + const IndexMask mask, + const Mesh &mesh, + const GeometryNodeProximityTargetType type, + const MutableSpan r_distances, + const MutableSpan r_locations) +{ + BVHTreeFromMesh bvh_data; + switch (type) { + case GEO_NODE_PROX_TARGET_POINTS: + BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_VERTS, 2); + break; + case GEO_NODE_PROX_TARGET_EDGES: + BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_EDGES, 2); + break; + case GEO_NODE_PROX_TARGET_FACES: + BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); + break; + } + + if (bvh_data.tree == nullptr) { + return; + } + + threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) { + BVHTreeNearest nearest; + copy_v3_fl(nearest.co, FLT_MAX); + nearest.index = -1; + + for (int i : range) { + const int index = mask[i]; + /* Use the distance to the last found point as upper bound to speedup the bvh lookup. */ + nearest.dist_sq = float3::distance_squared(nearest.co, positions[index]); + + BLI_bvhtree_find_nearest( + bvh_data.tree, positions[index], &nearest, bvh_data.nearest_callback, &bvh_data); + + if (nearest.dist_sq < r_distances[index]) { + r_distances[index] = nearest.dist_sq; + if (!r_locations.is_empty()) { + r_locations[index] = nearest.co; + } + } + } + }); + + free_bvhtree_from_mesh(&bvh_data); +} + +static void calculate_pointcloud_proximity(const VArray &positions, + const IndexMask mask, + const PointCloud &pointcloud, + const MutableSpan r_distances, + const MutableSpan r_locations) +{ + BVHTreeFromPointCloud bvh_data; + BKE_bvhtree_from_pointcloud_get(&bvh_data, &pointcloud, 2); + if (bvh_data.tree == nullptr) { + return; + } + + threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) { + BVHTreeNearest nearest; + copy_v3_fl(nearest.co, FLT_MAX); + nearest.index = -1; + + for (int i : range) { + const int index = mask[i]; + /* Use the distance to the closest point in the mesh to speedup the pointcloud bvh lookup. + * This is ok because we only need to find the closest point in the pointcloud if it's + * closer than the mesh. */ + nearest.dist_sq = r_distances[index]; + + BLI_bvhtree_find_nearest( + bvh_data.tree, positions[index], &nearest, bvh_data.nearest_callback, &bvh_data); + + if (nearest.dist_sq < r_distances[index]) { + r_distances[index] = nearest.dist_sq; + if (!r_locations.is_empty()) { + r_locations[index] = nearest.co; + } + } + } + }); + + free_bvhtree_from_pointcloud(&bvh_data); +} + +class ProximityFunction : public fn::MultiFunction { + private: + GeometrySet target_; + GeometryNodeProximityTargetType type_; + + public: + ProximityFunction(GeometrySet target, GeometryNodeProximityTargetType type) + : target_(std::move(target)), type_(type) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Geometry Proximity"}; + signature.single_input("Source Position"); + signature.single_output("Position"); + signature.single_output("Distance"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &src_positions = params.readonly_single_input(0, + "Source Position"); + MutableSpan positions = params.uninitialized_single_output_if_required( + 1, "Position"); + /* Make sure there is a distance array, used for finding the smaller distance when there are + * multiple components. Theoretically it would be possible to avoid using the distance array + * when there is only one component. However, this only adds an allocation and a single float + * comparison per vertex, so it's likely not worth it. */ + MutableSpan distances = params.uninitialized_single_output(2, "Distance"); + + distances.fill(FLT_MAX); + + if (target_.has_mesh()) { + calculate_mesh_proximity( + src_positions, mask, *target_.get_mesh_for_read(), type_, distances, positions); + } + + if (target_.has_pointcloud() && type_ == GEO_NODE_PROX_TARGET_POINTS) { + calculate_pointcloud_proximity( + src_positions, mask, *target_.get_pointcloud_for_read(), distances, positions); + } + + if (params.single_output_is_required(2, "Distance")) { + threading::parallel_for(mask.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + const int j = mask[i]; + distances[j] = std::sqrt(distances[j]); + } + }); + } + } +}; + +static void geo_node_proximity_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set_target = params.extract_input("Target"); + + if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) { + params.set_output("Position", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + params.set_output("Distance", fn::make_constant_field({0.0f})); + return; + } + + const NodeGeometryProximity &storage = *(const NodeGeometryProximity *)params.node().storage; + Field position_field = params.extract_input>("Source Position"); + + auto proximity_fn = std::make_unique( + std::move(geometry_set_target), + static_cast(storage.target_element)); + auto proximity_op = std::make_shared( + FieldOperation(std::move(proximity_fn), {std::move(position_field)})); + + params.set_output("Position", Field(proximity_op, 0)); + params.set_output("Distance", Field(proximity_op, 1)); +} + +} // namespace blender::nodes + +void register_node_type_geo_proximity() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_PROXIMITY, "Geometry Proximity", NODE_CLASS_GEOMETRY, 0); + node_type_init(&ntype, blender::nodes::geo_proximity_init); + node_type_storage( + &ntype, "NodeGeometryProximity", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_proximity_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_proximity_exec; + ntype.draw_buttons = blender::nodes::geo_node_proximity_layout; + nodeRegisterType(&ntype); +} From efe3a13b55c0295b68697c19e4379b5bbe8684ca Mon Sep 17 00:00:00 2001 From: Cian Jinks Date: Tue, 28 Sep 2021 21:57:42 +0100 Subject: [PATCH 0326/1500] Cleanup: Removed redundant if macro --- source/blender/editors/mesh/editmesh_knife.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index eaecda28287..4fa8df0b672 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -369,7 +369,6 @@ enum { /** \name Drawing * \{ */ -#if 1 static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], float r_v2[3]) { float planes[4][4]; @@ -383,10 +382,6 @@ static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], float lambda_best[2] = {-FLT_MAX, FLT_MAX}; int i; - /* We (sometimes) need the lines to be at the same depth before projecting. */ -# if 0 - sub_v3_v3v3(ray_dir, kcd->curr.cage, kcd->prev.cage); -# else { float curr_cage_adjust[3]; float co_depth[3]; @@ -396,7 +391,6 @@ static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage); } -# endif for (i = 0; i < 4; i++) { float ray_hit[3]; @@ -483,7 +477,6 @@ static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd) immUnbindProgram(); } } -#endif static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd) { From 79290f51605e31cff09e4984d4f493d05bfe17e2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 29 Sep 2021 07:29:15 +1000 Subject: [PATCH 0327/1500] Cleanup: spelling in comments --- intern/cycles/blender/blender_session.h | 5 ++--- intern/cycles/kernel/device/hip/compat.h | 2 +- source/blender/blenkernel/BKE_asset_catalog.hh | 2 +- source/blender/blenkernel/BKE_volume.h | 12 ++++++------ .../blender/blenkernel/intern/asset_catalog_test.cc | 6 +++--- source/blender/blenlib/intern/path_util.c | 2 +- source/blender/compositor/intern/COM_MemoryBuffer.h | 5 +++-- .../compositor/operations/COM_BlurBaseOperation.cc | 2 +- source/blender/editors/space_node/drawnode.cc | 2 +- source/blender/makesdna/DNA_node_types.h | 8 ++++---- .../blender/nodes/composite/node_composite_tree.cc | 2 +- .../composite/nodes/node_composite_brightness.cc | 2 +- .../nodes/composite/nodes/node_composite_curves.cc | 2 +- .../nodes/composite/nodes/node_composite_defocus.cc | 4 ++-- .../nodes/composite/nodes/node_composite_image.cc | 3 +-- .../nodes/composite/nodes/node_composite_vecBlur.cc | 2 +- .../nodes/legacy/node_geo_attribute_transfer.cc | 2 +- source/blender/nodes/intern/node_common.c | 2 +- .../nodes/shader/nodes/node_shader_brightness.c | 2 +- source/blender/render/intern/texture_image.c | 12 +++++------- 20 files changed, 38 insertions(+), 41 deletions(-) diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index fb0e5252e3b..58683ee07a1 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -110,8 +110,7 @@ class BlenderSession { BL::RenderSettings b_render; BL::Depsgraph b_depsgraph; /* NOTE: Blender's scene might become invalid after call - * free_blender_memory_if_possible(). - */ + * #free_blender_memory_if_possible(). */ BL::Scene b_scene; BL::SpaceView3D b_v3d; BL::RegionView3D b_rv3d; @@ -147,7 +146,7 @@ class BlenderSession { protected: void stamp_view_layer_metadata(Scene *scene, const string &view_layer_name); - /* Check whether session error happenned. + /* Check whether session error happened. * If so, it is reported to the render engine and true is returned. * Otherwise false is returned. */ bool check_and_report_session_error(); diff --git a/intern/cycles/kernel/device/hip/compat.h b/intern/cycles/kernel/device/hip/compat.h index 3644925d5be..95338fe7d6e 100644 --- a/intern/cycles/kernel/device/hip/compat.h +++ b/intern/cycles/kernel/device/hip/compat.h @@ -79,7 +79,7 @@ typedef unsigned long long uint64_t; #define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) #define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) -/* GPU warp synchronizaton */ +/* GPU warp synchronization */ #define ccl_gpu_syncthreads() __syncthreads() #define ccl_gpu_ballot(predicate) __ballot(predicate) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 8e6aeec5204..05db3c808cf 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -302,7 +302,7 @@ class AssetCatalog { * When this catalog's path is equal to the given path, return true as well. * * Note that non-normalized paths (so for example starting or ending with a slash) are not - * supported, and result in undefined behaviour. + * supported, and result in undefined behavior. */ bool is_contained_in(const CatalogPath &other_path) const; diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h index d9333996632..5fe0d54c2cf 100644 --- a/source/blender/blenkernel/BKE_volume.h +++ b/source/blender/blenkernel/BKE_volume.h @@ -18,7 +18,7 @@ /** \file * \ingroup bke - * \brief Volume datablock. + * \brief Volume data-block. */ #ifdef __cplusplus extern "C" { @@ -37,7 +37,7 @@ struct VolumeGridVector; void BKE_volumes_init(void); -/* Datablock Management */ +/* Data-block Management */ void BKE_volume_init_grids(struct Volume *volume); void *BKE_volume_add(struct Main *bmain, const char *name); @@ -122,13 +122,13 @@ void BKE_volume_grid_transform_matrix(const struct VolumeGrid *grid, float mat[4 /* Volume Editing * - * These are intended for modifiers to use on evaluated datablocks. + * These are intended for modifiers to use on evaluated data-blocks. * - * new_for_eval creates a volume datablock with no grids or file path, but + * new_for_eval creates a volume data-block with no grids or file path, but * preserves other settings such as viewport display options. * - * copy_for_eval creates a volume datablock preserving everything except the - * file path. Grids are shared with the source datablock, not copied. */ + * copy_for_eval creates a volume data-block preserving everything except the + * file path. Grids are shared with the source data-block, not copied. */ struct Volume *BKE_volume_new_for_eval(const struct Volume *volume_src); struct Volume *BKE_volume_copy_for_eval(struct Volume *volume_src, bool reference); diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index a833e7903fa..9ac2c9f5512 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -175,7 +175,7 @@ TEST_F(AssetCatalogTest, load_single_file) AssetCatalogService service(asset_library_root_); service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); - // Test getting a non-existant catalog ID. + // Test getting a non-existent catalog ID. EXPECT_EQ(nullptr, service.find_catalog(BLI_uuid_generate_random())); // Test getting an invalid catalog (without path definition). @@ -677,7 +677,7 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf) {"character/Ružena", 1}, {"character/Ružena/poselib", 2}, {"character/Ružena/poselib/face", 3}, - // {"character/Ružena/poselib/hand", 3}, // this is the deleted one + // {"character/Ružena/poselib/hand", 3}, // This is the deleted one. {"path", 0}, {"path/without", 1}, {"path/without/simplename", 2}, @@ -754,7 +754,7 @@ TEST_F(AssetCatalogTest, merge_catalog_files) TestableAssetCatalogService service(asset_library_root_); service.load_from_disk(cdf_dir); - // Copy a modified file, to mimick a situation where someone changed the CDF after we loaded it. + // Copy a modified file, to mimic a situation where someone changed the CDF after we loaded it. ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str())); // Overwrite the modified file. This should merge the on-disk file with our catalogs. diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 4405f25bf2a..066749f3a94 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1962,7 +1962,7 @@ bool BLI_path_contains(const char *container_path, const char *containee_path) } /* Add a trailing slash to prevent same-prefix directories from matching. - * e.g. "/some/path" doesn't contain "/some/pathlib". */ + * e.g. "/some/path" doesn't contain "/some/path_lib". */ BLI_path_slash_ensure(container_native); return BLI_str_startswith(containee_native, container_native); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 4d6c5790987..9e173f73f63 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -695,8 +695,9 @@ class MemoryBuffer { return y >= m_rect.ymin && y < m_rect.ymax; } - /* Fast floor functions. The caller should check result is within buffer bounds. It ceils in near - * cases and when given coordinate is negative and less than buffer rect `min - 1`. */ + /* Fast `floor(..)` functions. The caller should check result is within buffer bounds. + * It `ceil(..)` in near cases and when given coordinate + * is negative and less than buffer rect `min - 1`. */ int floor_x(float x) const { return (int)(x + to_positive_x_stride_) - to_positive_x_stride_; diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc index 2c162425f13..412632e2e22 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc @@ -228,7 +228,7 @@ void BlurBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_are } case eExecutionModel::FullFrame: { /* Setting a modifier ensures all non main inputs have extended bounds as preferred - * canvas, avoiding unnecessary canvas convertions that would hide constant + * canvas, avoiding unnecessary canvas conversions that would hide constant * operations. */ set_determined_canvas_modifier([=](rcti &canvas) { /* Rounding to even prevents jiggling in backdrop while switching size values. */ diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 9e6662855cc..500e23e5451 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1563,7 +1563,7 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE); } -/* qdn: glare node */ +/* glare node */ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "glare_type", DEFAULT_FLAGS, "", ICON_NONE); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index ffa6cb307b8..35be6a4b48e 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -855,7 +855,7 @@ typedef struct NodeVertexCol { char name[64]; } NodeVertexCol; -/* qdn: Defocus blur node */ +/** Defocus blur node. */ typedef struct NodeDefocus { char bktype, _pad0, preview, gamco; short samples, no_zbuf; @@ -871,7 +871,7 @@ typedef struct NodeScriptDict { void *node; } NodeScriptDict; -/* qdn: glare node */ +/** glare node. */ typedef struct NodeGlare { char quality, type, iter; /* XXX angle is only kept for backward/forward compatibility, @@ -882,14 +882,14 @@ typedef struct NodeGlare { char _pad1[4]; } NodeGlare; -/* qdn: tonemap node */ +/** Tonemap node. */ typedef struct NodeTonemap { float key, offset, gamma; float f, m, a, c; int type; } NodeTonemap; -/* qdn: lens distortion node */ +/** Lens distortion node. */ typedef struct NodeLensDist { short jit, proj, fit; char _pad[2]; diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index d695096903f..ea54673faee 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -315,7 +315,7 @@ void ntreeCompositTagRender(Scene *scene) if (node->id == (ID *)scene || node->type == CMP_NODE_COMPOSITE) { nodeUpdate(sce_iter->nodetree, node); } - else if (node->type == CMP_NODE_TEXTURE) /* uses scene sizex/sizey */ { + else if (node->type == CMP_NODE_TEXTURE) /* uses scene size_x/size_y */ { nodeUpdate(sce_iter->nodetree, node); } } diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc index 9c0716169c6..790ccea4dc5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc +++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc @@ -23,7 +23,7 @@ #include "node_composite_util.hh" -/* **************** Brigh and contrsast ******************** */ +/* **************** Bright and Contrast ******************** */ static bNodeSocketTemplate cmp_node_brightcontrast_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc index 8a8a7624fce..58df008a111 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.cc +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -25,7 +25,7 @@ /* **************** CURVE Time ******************** */ -/* custom1 = sfra, custom2 = efra */ +/* custom1 = start_frame, custom2 = end_frame */ static bNodeSocketTemplate cmp_node_time_out[] = { {SOCK_FLOAT, N_("Fac")}, {-1, ""}, diff --git a/source/blender/nodes/composite/nodes/node_composite_defocus.cc b/source/blender/nodes/composite/nodes/node_composite_defocus.cc index b1ac170217a..1103aff4366 100644 --- a/source/blender/nodes/composite/nodes/node_composite_defocus.cc +++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc @@ -25,7 +25,7 @@ #include -/* ************ qdn: Defocus node ****************** */ +/* ************ Defocus Node ****************** */ static bNodeSocketTemplate cmp_node_defocus_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, @@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_defocus_out[] = { static void node_composit_init_defocus(bNodeTree *UNUSED(ntree), bNode *node) { - /* qdn: defocus node */ + /* defocus node */ NodeDefocus *nbd = (NodeDefocus *)MEM_callocN(sizeof(NodeDefocus), "node defocus data"); nbd->bktype = 0; nbd->rotation = 0.0f; diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc index aee1c1d63fd..3cef3a7625f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.cc +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -140,8 +140,7 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, * * So we manually construct image user to be sure first * image from sequence (that one which is set as filename - * for image datablock) is used for sockets detection - */ + * for image data-block) is used for sockets detection. */ load_iuser.ok = 1; load_iuser.framenr = offset; diff --git a/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc b/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc index c34fdcabc0a..ce6ba659609 100644 --- a/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc @@ -39,7 +39,7 @@ static void node_composit_init_vecblur(bNodeTree *UNUSED(ntree), bNode *node) nbd->fac = 1.0f; } -/* custom1: iterations, custom2: maxspeed (0 = nolimit) */ +/* custom1: iterations, custom2: max_speed (0 = no_limit). */ void register_node_type_cmp_vecblur(void) { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc index 874350cd714..f187ee39b94 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc @@ -404,7 +404,7 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry, data_type); for (const int i : IndexRange(tot_samples)) { if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) { - /* Pointcloud point is closer. */ + /* Point-cloud point is closer. */ const int index = pointcloud_indices[i]; pointcloud_src_attribute.varray->get(index, buffer); dst_attribute->set_by_relocate(i, buffer); diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.c index b8c89d1db37..7625cb9e3f6 100644 --- a/source/blender/nodes/intern/node_common.c +++ b/source/blender/nodes/intern/node_common.c @@ -198,7 +198,7 @@ void node_group_update(struct bNodeTree *ntree, struct bNode *node) nodeRemoveAllSockets(ntree, node); } else if ((ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING))) { - /* Missing datablock, leave sockets unchanged so that when it comes back + /* Missing data-block, leave sockets unchanged so that when it comes back * the links remain valid. */ } else { diff --git a/source/blender/nodes/shader/nodes/node_shader_brightness.c b/source/blender/nodes/shader/nodes/node_shader_brightness.c index d8f560277f2..4f375c666de 100644 --- a/source/blender/nodes/shader/nodes/node_shader_brightness.c +++ b/source/blender/nodes/shader/nodes/node_shader_brightness.c @@ -19,7 +19,7 @@ #include "node_shader_util.h" -/* **************** Brigh and contrsast ******************** */ +/* **************** Bright and contrast ******************** */ static bNodeSocketTemplate sh_node_brightcontrast_in[] = { {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f}, diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c index 62aee564626..edfa284242c 100644 --- a/source/blender/render/intern/texture_image.c +++ b/source/blender/render/intern/texture_image.c @@ -1958,13 +1958,11 @@ int imagewraposa(Tex *tex, } if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { - /* qdn: normal from color - * The invert of the red channel is to make - * the normal map compliant with the outside world. - * It needs to be done because in Blender - * the normal used in the renderer points inward. It is generated - * this way in calc_vertexnormals(). Should this ever change - * this negate must be removed. */ + /* Normal from color: + * The invert of the red channel is to make the normal map compliant with the outside world. + * It needs to be done because in Blender the normal used in the renderer points inward. + * It is generated this way in #calc_vertexnormals(). + * Should this ever change this negate must be removed. */ texres->nor[0] = -2.0f * (texres->tr - 0.5f); texres->nor[1] = 2.0f * (texres->tg - 0.5f); texres->nor[2] = 2.0f * (texres->tb - 0.5f); From b524153d61e20419bf7ebe2df099c237c2b4427c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 29 Sep 2021 07:29:17 +1000 Subject: [PATCH 0328/1500] Cleanup: use C comments for plain text --- .../blenkernel/intern/asset_catalog_test.cc | 68 ++++++++++--------- source/blender/blenkernel/intern/nla.c | 2 +- .../intern/builder/deg_builder_nodes.cc | 4 +- .../intern/node/deg_node_component.cc | 2 +- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 9ac2c9f5512..5b94f021797 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -175,28 +175,28 @@ TEST_F(AssetCatalogTest, load_single_file) AssetCatalogService service(asset_library_root_); service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); - // Test getting a non-existent catalog ID. + /* Test getting a non-existent catalog ID. */ EXPECT_EQ(nullptr, service.find_catalog(BLI_uuid_generate_random())); - // Test getting an invalid catalog (without path definition). + /* Test getting an invalid catalog (without path definition). */ AssetCatalog *cat_without_path = service.find_catalog(UUID_ID_WITHOUT_PATH); ASSERT_EQ(nullptr, cat_without_path); - // Test getting a regular catalog. + /* Test getting a regular catalog. */ AssetCatalog *poses_ellie = service.find_catalog(UUID_POSES_ELLIE); ASSERT_NE(nullptr, poses_ellie); EXPECT_EQ(UUID_POSES_ELLIE, poses_ellie->catalog_id); EXPECT_EQ("character/Ellie/poselib", poses_ellie->path); EXPECT_EQ("POSES_ELLIE", poses_ellie->simple_name); - // Test whitespace stripping and support in the path. + /* Test white-space stripping and support in the path. */ AssetCatalog *poses_whitespace = service.find_catalog(UUID_POSES_ELLIE_WHITESPACE); ASSERT_NE(nullptr, poses_whitespace); EXPECT_EQ(UUID_POSES_ELLIE_WHITESPACE, poses_whitespace->catalog_id); EXPECT_EQ("character/Ellie/poselib/white space", poses_whitespace->path); EXPECT_EQ("POSES_ELLIE WHITESPACE", poses_whitespace->simple_name); - // Test getting a UTF-8 catalog ID. + /* Test getting a UTF-8 catalog ID. */ AssetCatalog *poses_ruzena = service.find_catalog(UUID_POSES_RUZENA); ASSERT_NE(nullptr, poses_ruzena); EXPECT_EQ(UUID_POSES_RUZENA, poses_ruzena->catalog_id); @@ -302,9 +302,9 @@ TEST_F(AssetCatalogTest, load_single_file_into_tree) {"character/Ružena/poselib", 2}, {"character/Ružena/poselib/face", 3}, {"character/Ružena/poselib/hand", 3}, - {"path", 0}, // Implicit. - {"path/without", 1}, // Implicit. - {"path/without/simplename", 2}, // From CDF. + {"path", 0}, /* Implicit. */ + {"path/without", 1}, /* Implicit. */ + {"path/without/simplename", 2}, /* From CDF. */ }; AssetCatalogTree *tree = service.get_catalog_tree(); @@ -385,7 +385,7 @@ TEST_F(AssetCatalogTest, write_single_file) AssetCatalogService loaded_service(save_to_path); loaded_service.load_from_disk(); - // Test that the expected catalogs are there. + /* Test that the expected catalogs are there. */ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); @@ -393,10 +393,10 @@ TEST_F(AssetCatalogTest, write_single_file) EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); - // Test that the invalid catalog definition wasn't copied. + /* Test that the invalid catalog definition wasn't copied. */ EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_ID_WITHOUT_PATH)); - // TODO(@sybren): test ordering of catalogs in the file. + /* TODO(@sybren): test ordering of catalogs in the file. */ } TEST_F(AssetCatalogTest, no_writing_empty_files) @@ -413,7 +413,7 @@ TEST_F(AssetCatalogTest, no_writing_empty_files) /* Already loaded a CDF, saving to some unrelated directory. */ TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf) { - const CatalogFilePath top_level_dir = create_temp_path(); // Has trailing slash. + const CatalogFilePath top_level_dir = create_temp_path(); /* Has trailing slash. */ /* Create a copy of the CDF in SVN, so we can safely write to it. */ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; @@ -450,7 +450,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf) /* Create some catalogs in memory, save to directory that doesn't contain anything else. */ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_empty_directory) { - const CatalogFilePath target_dir = create_temp_path(); // Has trailing slash. + const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ TestableAssetCatalogService service; const AssetCatalog *cat = service.create_catalog("some/catalog/path"); @@ -477,7 +477,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_empty_directory) /* Create some catalogs in memory, save to directory that contains a default CDF. */ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_merge) { - const CatalogFilePath target_dir = create_temp_path(); // Has trailing slash. + const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; const CatalogFilePath writable_cdf_file = target_dir + AssetCatalogService::DEFAULT_CATALOG_FILENAME; @@ -512,7 +512,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_me /* Create some catalogs in memory, save to subdirectory of a registered asset library. */ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_asset_lib) { - const CatalogFilePath target_dir = create_temp_path(); // Has trailing slash. + const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; const CatalogFilePath registered_asset_lib = target_dir + "my_asset_library/"; CatalogFilePath writable_cdf_file = registered_asset_lib + @@ -584,7 +584,7 @@ TEST_F(AssetCatalogTest, create_first_catalog_from_scratch) AssetCatalogService loaded_service(temp_lib_root); loaded_service.load_from_disk(); - // Test that the expected catalog is there. + /* Test that the expected catalog is there. */ AssetCatalog *written_cat = loaded_service.find_catalog(cat->catalog_id); ASSERT_NE(nullptr, written_cat); EXPECT_EQ(written_cat->catalog_id, cat->catalog_id); @@ -677,7 +677,7 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf) {"character/Ružena", 1}, {"character/Ružena/poselib", 2}, {"character/Ružena/poselib/face", 3}, - // {"character/Ružena/poselib/hand", 3}, // This is the deleted one. + // {"character/Ružena/poselib/hand", 3}, /* This is the deleted one. */ {"path", 0}, {"path/without", 1}, {"path/without/simplename", 2}, @@ -702,7 +702,7 @@ TEST_F(AssetCatalogTest, delete_catalog_write_to_disk) AssetCatalogService loaded_service(save_to_path); loaded_service.load_from_disk(); - // Test that the expected catalogs are there, except the deleted one. + /* Test that the expected catalogs are there, except the deleted one. */ EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); @@ -750,29 +750,30 @@ TEST_F(AssetCatalogTest, merge_catalog_files) const CatalogFilePath temp_cdf_file = cdf_dir + "blender_assets.cats.txt"; ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str())); - // Load the unmodified, original CDF. + /* Load the unmodified, original CDF. */ TestableAssetCatalogService service(asset_library_root_); service.load_from_disk(cdf_dir); - // Copy a modified file, to mimic a situation where someone changed the CDF after we loaded it. + /* Copy a modified file, to mimic a situation where someone changed the + * CDF after we loaded it. */ ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str())); - // Overwrite the modified file. This should merge the on-disk file with our catalogs. + /* Overwrite the modified file. This should merge the on-disk file with our catalogs. */ service.write_to_disk_on_blendfile_save(cdf_dir + "phony.blend"); AssetCatalogService loaded_service(cdf_dir); loaded_service.load_from_disk(); - // Test that the expected catalogs are there. + /* Test that the expected catalogs are there. */ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); - EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_AGENT_47)); // New in the modified file. + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_AGENT_47)); /* New in the modified file. */ - // When there are overlaps, the in-memory (i.e. last-saved) paths should win. + /* When there are overlaps, the in-memory (i.e. last-saved) paths should win. */ const AssetCatalog *ruzena_face = loaded_service.find_catalog(UUID_POSES_RUZENA_FACE); EXPECT_EQ("character/Ružena/poselib/face", ruzena_face->path); } @@ -796,8 +797,8 @@ TEST_F(AssetCatalogTest, backups) AssetCatalogService loaded_service; loaded_service.load_from_disk(backup_path); - // Test that the expected catalogs are there, including the deleted one. - // This is the backup, after all. + /* Test that the expected catalogs are there, including the deleted one. + * This is the backup, after all. */ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); @@ -809,12 +810,13 @@ TEST_F(AssetCatalogTest, backups) TEST_F(AssetCatalogTest, order_by_path) { const bUUID cat2_uuid("22222222-b847-44d9-bdca-ff04db1c24f5"); - const bUUID cat4_uuid("11111111-b847-44d9-bdca-ff04db1c24f5"); // Sorts earlier than above. + const bUUID cat4_uuid("11111111-b847-44d9-bdca-ff04db1c24f5"); /* Sorts earlier than above. */ const AssetCatalog cat1(BLI_uuid_generate_random(), "simple/path/child", ""); const AssetCatalog cat2(cat2_uuid, "simple/path", ""); const AssetCatalog cat3(BLI_uuid_generate_random(), "complex/path/...or/is/it?", ""); - const AssetCatalog cat4(cat4_uuid, "simple/path", "different ID, same path"); // should be kept - const AssetCatalog cat5(cat4_uuid, "simple/path", "same ID, same path"); // disappears + const AssetCatalog cat4( + cat4_uuid, "simple/path", "different ID, same path"); /* should be kept */ + const AssetCatalog cat5(cat4_uuid, "simple/path", "same ID, same path"); /* disappears */ AssetCatalogOrderedSet by_path; by_path.insert(&cat1); @@ -832,10 +834,10 @@ TEST_F(AssetCatalogTest, order_by_path) ASSERT_EQ(4, by_path.size()) << "Expecting cat5 to not be stored in the set, as it duplicates " "an already-existing path + UUID"; - EXPECT_EQ(cat3.catalog_id, (*(set_iter++))->catalog_id); // complex/path - EXPECT_EQ(cat4.catalog_id, (*(set_iter++))->catalog_id); // simple/path with 111.. ID - EXPECT_EQ(cat2.catalog_id, (*(set_iter++))->catalog_id); // simple/path with 222.. ID - EXPECT_EQ(cat1.catalog_id, (*(set_iter++))->catalog_id); // simple/path/child + EXPECT_EQ(cat3.catalog_id, (*(set_iter++))->catalog_id); /* complex/path */ + EXPECT_EQ(cat4.catalog_id, (*(set_iter++))->catalog_id); /* simple/path with 111.. ID */ + EXPECT_EQ(cat2.catalog_id, (*(set_iter++))->catalog_id); /* simple/path with 222.. ID */ + EXPECT_EQ(cat1.catalog_id, (*(set_iter++))->catalog_id); /* simple/path/child */ if (set_iter != by_path.end()) { const AssetCatalog *next_cat = *set_iter; diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 4ce2ae3c11f..487e925df79 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -1484,7 +1484,7 @@ void BKE_nlastrip_recalculate_bounds(NlaStrip *strip) } /* Is the given NLA-strip the first one to occur for the given AnimData block */ -// TODO: make this an api method if necessary, but need to add prefix first +/* TODO: make this an api method if necessary, but need to add prefix first */ static bool nlastrip_is_first(AnimData *adt, NlaStrip *strip) { NlaTrack *nlt; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 463bb02afa4..a09f79ffa39 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1467,7 +1467,7 @@ void DepsgraphNodeBuilder::build_shapekeys(Key *key) } /* ObData Geometry Evaluation */ -// XXX: what happens if the datablock is shared! +/* XXX: what happens if the datablock is shared! */ void DepsgraphNodeBuilder::build_object_data_geometry(Object *object, bool is_object_visible) { OperationNode *op_node; @@ -1784,7 +1784,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) build_idproperties(socket->prop); } - // TODO: link from nodetree to owner_component? + /* TODO: link from nodetree to owner_component? */ } /* Recursively build graph for material */ diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc index a29618cefa8..0947fad7670 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.cc +++ b/source/blender/depsgraph/intern/node/deg_node_component.cc @@ -90,7 +90,7 @@ ComponentNode::ComponentNode() void ComponentNode::init(const ID * /*id*/, const char * /*subdata*/) { /* hook up eval context? */ - // XXX: maybe this needs a special API? + /* XXX: maybe this needs a special API? */ } /* Free 'component' node */ From 6dceaafe5a21fc0f112b2d206980c30eb4e678e8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 29 Sep 2021 07:29:18 +1000 Subject: [PATCH 0329/1500] Cleanup: trailing space, newlines at EOF --- intern/cycles/blender/addon/properties.py | 2 +- intern/cycles/device/hip/device.h | 2 +- intern/cycles/device/hip/util.h | 2 +- source/blender/editors/space_file/space_file.c | 2 +- source/blender/sequencer/intern/strip_relations.c | 2 +- tests/python/bl_blendfile_liblink.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 67207874431..cea70033784 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -124,7 +124,7 @@ enum_texture_limit = ( ('4096', "4096", "Limit texture size to 4096 pixels", 6), ('8192', "8192", "Limit texture size to 8192 pixels", 7), ) - + # NOTE: Identifiers are expected to be an upper case version of identifiers from `Pass::get_type_enum()` enum_view3d_shading_render_pass = ( ('', "General", ""), diff --git a/intern/cycles/device/hip/device.h b/intern/cycles/device/hip/device.h index 76fa8995bed..965fd9e484b 100644 --- a/intern/cycles/device/hip/device.h +++ b/intern/cycles/device/hip/device.h @@ -34,4 +34,4 @@ void device_hip_info(vector &devices); string device_hip_capabilities(); -CCL_NAMESPACE_END \ No newline at end of file +CCL_NAMESPACE_END diff --git a/intern/cycles/device/hip/util.h b/intern/cycles/device/hip/util.h index f8468e0dc5c..0db5174a3db 100644 --- a/intern/cycles/device/hip/util.h +++ b/intern/cycles/device/hip/util.h @@ -60,4 +60,4 @@ int hipewCompilerVersion(); CCL_NAMESPACE_END -#endif /* WITH_HIP */ \ No newline at end of file +#endif /* WITH_HIP */ diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 42a9c4aa2d5..ac23767f933 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -1,4 +1,4 @@ -/* +/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c index 46fdd2c3d14..9822bfe38f9 100644 --- a/source/blender/sequencer/intern/strip_relations.c +++ b/source/blender/sequencer/intern/strip_relations.c @@ -526,4 +526,4 @@ struct Sequence *SEQ_find_metastrip_by_sequence(ListBase *seqbase, Sequence *met } return NULL; -} \ No newline at end of file +} diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index 4186ba58817..4545e0b846a 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -10,7 +10,7 @@ from bl_blendfile_utils import TestHelper class TestBlendLibLinkHelper(TestHelper): - + def __init__(self, args): self.args = args From 960b21e1d79d2b1a774e8ee1bf1cc82f7c384d15 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 29 Sep 2021 07:29:19 +1000 Subject: [PATCH 0330/1500] Cleanup: sort cmake file lists --- source/blender/blenkernel/CMakeLists.txt | 4 ++-- source/blender/functions/CMakeLists.txt | 2 +- source/blender/gpencil_modifiers/CMakeLists.txt | 2 +- source/blender/nodes/CMakeLists.txt | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index d04d4558fed..24de91959bb 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -83,9 +83,9 @@ set(SRC intern/armature_pose.cc intern/armature_selection.cc intern/armature_update.c + intern/asset.cc intern/asset_catalog.cc intern/asset_library.cc - intern/asset.cc intern/attribute.c intern/attribute_access.cc intern/attribute_math.cc @@ -304,10 +304,10 @@ set(SRC BKE_appdir.h BKE_armature.h BKE_armature.hh + BKE_asset.h BKE_asset_catalog.hh BKE_asset_library.h BKE_asset_library.hh - BKE_asset.h BKE_attribute.h BKE_attribute_access.hh BKE_attribute_math.hh diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 856668f01d7..309b92f1cb4 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -53,9 +53,9 @@ set(SRC FN_multi_function_builder.hh FN_multi_function_context.hh FN_multi_function_data_type.hh + FN_multi_function_parallel.hh FN_multi_function_param_type.hh FN_multi_function_params.hh - FN_multi_function_parallel.hh FN_multi_function_procedure.hh FN_multi_function_procedure_builder.hh FN_multi_function_procedure_executor.hh diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index eb1f61b1862..afcd551d0af 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -69,8 +69,8 @@ set(SRC intern/MOD_gpencilthick.c intern/MOD_gpenciltime.c intern/MOD_gpenciltint.c - intern/MOD_gpencilweight_proximity.c intern/MOD_gpencilweight_angle.c + intern/MOD_gpencilweight_proximity.c MOD_gpencil_lineart.h MOD_gpencil_modifiertypes.h diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 416fcd8f9fd..8eaf88a6f1e 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -374,12 +374,12 @@ set(SRC intern/geometry_nodes_eval_log.cc intern/math_functions.cc intern/node_common.c + intern/node_declaration.cc intern/node_exec.cc intern/node_geometry_exec.cc intern/node_multi_function.cc - intern/node_declaration.cc - intern/node_socket_declarations.cc intern/node_socket.cc + intern/node_socket_declarations.cc intern/node_tree_ref.cc intern/node_util.c intern/type_conversions.cc @@ -400,10 +400,10 @@ set(SRC NOD_math_functions.hh NOD_multi_function.hh NOD_node_declaration.hh - NOD_socket_declarations.hh NOD_node_tree_ref.hh NOD_shader.h NOD_socket.h + NOD_socket_declarations.hh NOD_static_types.h NOD_texture.h NOD_type_conversions.hh From 4a484822478d544b63314504964ee95b709e7246 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Tue, 28 Sep 2021 17:57:37 -0400 Subject: [PATCH 0331/1500] Cleanup: Compositor: Migrate most input nodes to new socket builder This migrates most nodes except for the image/render layer nodes. These nodes require dynamic sockets so their implementation will be more involved. --- .../nodes/composite/node_composite_util.hh | 1 + .../nodes/node_composite_bokehimage.cc | 14 ++++++---- .../composite/nodes/node_composite_curves.cc | 16 ++++++----- .../composite/nodes/node_composite_mask.cc | 13 ++++++--- .../nodes/node_composite_movieclip.cc | 27 ++++++++++++------- .../composite/nodes/node_composite_rgb.cc | 15 +++++++---- .../composite/nodes/node_composite_texture.cc | 26 ++++++++++-------- .../nodes/node_composite_trackpos.cc | 19 ++++++++----- .../composite/nodes/node_composite_value.cc | 15 +++++++---- 9 files changed, 94 insertions(+), 52 deletions(-) diff --git a/source/blender/nodes/composite/node_composite_util.hh b/source/blender/nodes/composite/node_composite_util.hh index 62ef9570358..6fd82ffc93f 100644 --- a/source/blender/nodes/composite/node_composite_util.hh +++ b/source/blender/nodes/composite/node_composite_util.hh @@ -47,6 +47,7 @@ /* only for forward declarations */ #include "NOD_composite.h" +#include "NOD_socket_declarations.hh" #define CMP_SCALE_MAX 12000 diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc index 230f466594b..3a4bf94d256 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc @@ -25,10 +25,14 @@ /* **************** Bokeh image Tools ******************** */ -static bNodeSocketTemplate cmp_node_bokehimage_out[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Image"); +} + +} // namespace blender::nodes static void node_composit_init_bokehimage(bNodeTree *UNUSED(ntree), bNode *node) { @@ -46,7 +50,7 @@ void register_node_type_cmp_bokehimage(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_BOKEHIMAGE, "Bokeh Image", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, nullptr, cmp_node_bokehimage_out); + ntype.declare = blender::nodes::cmp_node_bokehimage_declare; node_type_init(&ntype, node_composit_init_bokehimage); node_type_storage( &ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc index 58df008a111..885a5511165 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.cc +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -25,12 +25,16 @@ /* **************** CURVE Time ******************** */ -/* custom1 = start_frame, custom2 = end_frame */ -static bNodeSocketTemplate cmp_node_time_out[] = { - {SOCK_FLOAT, N_("Fac")}, - {-1, ""}, -}; +namespace blender::nodes { +static void cmp_node_time_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Fac"); +} + +} // namespace blender::nodes + +/* custom1 = start_frame, custom2 = end_frame */ static void node_composit_init_curves_time(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = 1; @@ -43,7 +47,7 @@ void register_node_type_cmp_curve_time(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_TIME, "Time", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, cmp_node_time_out); + ntype.declare = blender::nodes::cmp_node_time_declare; node_type_size(&ntype, 140, 100, 320); node_type_init(&ntype, node_composit_init_curves_time); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc index bb33a874ae7..db8fb5e4ed2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc @@ -25,9 +25,16 @@ #include "node_composite_util.hh" -/* **************** Translate ******************** */ +/* **************** Mask ******************** */ -static bNodeSocketTemplate cmp_node_mask_out[] = {{SOCK_FLOAT, "Mask"}, {-1, ""}}; +namespace blender::nodes { + +static void cmp_node_mask_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Mask"); +} + +} // namespace blender::nodes static void node_composit_init_mask(bNodeTree *UNUSED(ntree), bNode *node) { @@ -54,7 +61,7 @@ void register_node_type_cmp_mask(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MASK, "Mask", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, cmp_node_mask_out); + ntype.declare = blender::nodes::cmp_node_mask_declare; node_type_init(&ntype, node_composit_init_mask); node_type_label(&ntype, node_mask_label); diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc index 2dffa8b4841..ea9c1268d06 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -26,15 +26,22 @@ #include "BKE_context.h" #include "BKE_lib_id.h" -static bNodeSocketTemplate cmp_node_movieclip_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Alpha")}, - {SOCK_FLOAT, N_("Offset X")}, - {SOCK_FLOAT, N_("Offset Y")}, - {SOCK_FLOAT, N_("Scale")}, - {SOCK_FLOAT, N_("Angle")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_movieclip_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Image"); + b.add_output("Alpha"); + b.add_output("Offset X"); + b.add_output("Offset Y"); + b.add_output("Scale"); + b.add_output("Angle"); + + +} + +} // namespace blender::nodes static void init(const bContext *C, PointerRNA *ptr) { @@ -54,7 +61,7 @@ void register_node_type_cmp_movieclip(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MOVIECLIP, "Movie Clip", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, nullptr, cmp_node_movieclip_out); + ntype.declare = blender::nodes::cmp_node_movieclip_declare; ntype.initfunc_api = init; node_type_storage( &ntype, "MovieClipUser", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc index c9c3dfcb019..332e56e26b1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc @@ -24,17 +24,22 @@ #include "node_composite_util.hh" /* **************** RGB ******************** */ -static bNodeSocketTemplate cmp_node_rgb_out[] = { - {SOCK_RGBA, N_("RGBA"), 0.5f, 0.5f, 0.5f, 1.0f}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_rgb_declare(NodeDeclarationBuilder &b) +{ + b.add_output("RGBA").default_value({0.5f, 0.5f, 0.5f, 1.0f}); +} + +} // namespace blender::nodes void register_node_type_cmp_rgb(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, cmp_node_rgb_out); + ntype.declare = blender::nodes::cmp_node_rgb_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc index 4421abdf998..7ee378a1a43 100644 --- a/source/blender/nodes/composite/nodes/node_composite_texture.cc +++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc @@ -24,23 +24,27 @@ #include "node_composite_util.hh" /* **************** TEXTURE ******************** */ -static bNodeSocketTemplate cmp_node_texture_in[] = { - {SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 2.0f, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -10.0f, 10.0f, PROP_XYZ}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_texture_out[] = { - {SOCK_FLOAT, N_("Value")}, - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_texture_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Offset").min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION); + b.add_input("Scale").default_value({1.0f, 1.0f, 1.0f}).min(-10.0f).max(10.0f).subtype(PROP_XYZ); + b.add_output("Value"); + b.add_output("Color"); + + +} + +} // namespace blender::nodes void register_node_type_cmp_texture(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_texture_in, cmp_node_texture_out); + ntype.declare = blender::nodes::cmp_node_texture_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc index 32bf1b5c0d1..433f9dffb2b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc +++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc @@ -23,12 +23,17 @@ #include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_trackpos_out[] = { - {SOCK_FLOAT, N_("X")}, - {SOCK_FLOAT, N_("Y")}, - {SOCK_VECTOR, N_("Speed"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_VELOCITY}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b) +{ + b.add_output("X"); + b.add_output("Y"); + b.add_output("Speed").subtype(PROP_VELOCITY); + +} + +} // namespace blender::nodes static void init(bNodeTree *UNUSED(ntree), bNode *node) { @@ -43,7 +48,7 @@ void register_node_type_cmp_trackpos(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_TRACKPOS, "Track Position", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, cmp_node_trackpos_out); + ntype.declare = blender::nodes::cmp_node_trackpos_declare; node_type_init(&ntype, init); node_type_storage( &ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_value.cc b/source/blender/nodes/composite/nodes/node_composite_value.cc index 7aab78e6d91..5459801bcc7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_value.cc +++ b/source/blender/nodes/composite/nodes/node_composite_value.cc @@ -24,17 +24,22 @@ #include "node_composite_util.hh" /* **************** VALUE ******************** */ -static bNodeSocketTemplate cmp_node_value_out[] = { - {SOCK_FLOAT, N_("Value"), 0.5f, 0, 0, 0, -FLT_MAX, FLT_MAX, PROP_NONE}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_value_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Value").default_value(0.5f); +} + +} // namespace blender::nodes void register_node_type_cmp_value(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, cmp_node_value_out); + ntype.declare = blender::nodes::cmp_node_value_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); nodeRegisterType(&ntype); From 7f5d62dfc62275cb9ba02f0110dc79f9053f3e67 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Tue, 28 Sep 2021 18:03:33 -0400 Subject: [PATCH 0332/1500] Cleanup: Make format --- .../blender/nodes/composite/nodes/node_composite_mask.cc | 2 +- .../nodes/composite/nodes/node_composite_movieclip.cc | 3 --- .../nodes/composite/nodes/node_composite_texture.cc | 8 +++++--- .../nodes/composite/nodes/node_composite_trackpos.cc | 1 - 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc index db8fb5e4ed2..8b415bb8b63 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc @@ -61,7 +61,7 @@ void register_node_type_cmp_mask(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MASK, "Mask", NODE_CLASS_INPUT, 0); - ntype.declare = blender::nodes::cmp_node_mask_declare; + ntype.declare = blender::nodes::cmp_node_mask_declare; node_type_init(&ntype, node_composit_init_mask); node_type_label(&ntype, node_mask_label); diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc index ea9c1268d06..ae91212f811 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -26,7 +26,6 @@ #include "BKE_context.h" #include "BKE_lib_id.h" - namespace blender::nodes { static void cmp_node_movieclip_declare(NodeDeclarationBuilder &b) @@ -37,8 +36,6 @@ static void cmp_node_movieclip_declare(NodeDeclarationBuilder &b) b.add_output("Offset Y"); b.add_output("Scale"); b.add_output("Angle"); - - } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc index 7ee378a1a43..eff008b4b41 100644 --- a/source/blender/nodes/composite/nodes/node_composite_texture.cc +++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc @@ -30,11 +30,13 @@ namespace blender::nodes { static void cmp_node_texture_declare(NodeDeclarationBuilder &b) { b.add_input("Offset").min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION); - b.add_input("Scale").default_value({1.0f, 1.0f, 1.0f}).min(-10.0f).max(10.0f).subtype(PROP_XYZ); + b.add_input("Scale") + .default_value({1.0f, 1.0f, 1.0f}) + .min(-10.0f) + .max(10.0f) + .subtype(PROP_XYZ); b.add_output("Value"); b.add_output("Color"); - - } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc index 433f9dffb2b..cb5c9468daa 100644 --- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc +++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc @@ -30,7 +30,6 @@ static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b) b.add_output("X"); b.add_output("Y"); b.add_output("Speed").subtype(PROP_VELOCITY); - } } // namespace blender::nodes From f0f70729b10ecb633a0005bc9f70b62a595a43d7 Mon Sep 17 00:00:00 2001 From: Cian Jinks Date: Tue, 28 Sep 2021 23:06:58 +0100 Subject: [PATCH 0333/1500] Fix: Knife undo with no cut segments left Now if a user presses the knife tool undo key when there are no more cut segments to undo, the operator exits. Previously, it did nothing. --- source/blender/editors/mesh/editmesh_knife.c | 130 ++++++++++--------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 4fa8df0b672..64c008acf8e 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -3899,71 +3899,69 @@ static void knifetool_undo(KnifeTool_OpData *kcd) KnifeUndoFrame *undo; BLI_mempool_iter iterkfe; - if (!BLI_stack_is_empty(kcd->undostack)) { - undo = BLI_stack_peek(kcd->undostack); + undo = BLI_stack_peek(kcd->undostack); - /* Undo edge splitting. */ - for (int i = 0; i < undo->splits; i++) { - BLI_stack_pop(kcd->splitstack, &newkfe); - BLI_stack_pop(kcd->splitstack, &kfe); - knife_join_edge(newkfe, kfe); - } - - for (int i = 0; i < undo->cuts; i++) { - - BLI_mempool_iternew(kcd->kedges, &iterkfe); - for (kfe = BLI_mempool_iterstep(&iterkfe); kfe; kfe = BLI_mempool_iterstep(&iterkfe)) { - if (!kfe->is_cut || kfe->is_invalid || kfe->splits) { - continue; - } - lastkfe = kfe; - } - - if (lastkfe) { - lastkfe->is_invalid = true; - - /* TODO: Are they always guaranteed to be in this order? */ - v1 = lastkfe->v1; - v2 = lastkfe->v2; - - /* Only remove first vertex if it is the start segment of the cut. */ - if (!v1->is_invalid && !v1->is_splitting) { - v1->is_invalid = true; - /* If the first vertex is touching any other cut edges don't remove it. */ - for (ref = v1->edges.first; ref; ref = ref->next) { - kfe = ref->ref; - if (kfe->is_cut && !kfe->is_invalid) { - v1->is_invalid = false; - break; - } - } - } - - /* Only remove second vertex if it is the end segment of the cut. */ - if (!v2->is_invalid && !v2->is_splitting) { - v2->is_invalid = true; - /* If the second vertex is touching any other cut edges don't remove it. */ - for (ref = v2->edges.first; ref; ref = ref->next) { - kfe = ref->ref; - if (kfe->is_cut && !kfe->is_invalid) { - v2->is_invalid = false; - break; - } - } - } - } - } - - if (kcd->mode == MODE_DRAGGING) { - /* Restore kcd->prev. */ - kcd->prev = undo->pos; - } - - /* Restore data for distance and angle measurements. */ - kcd->mdata = undo->mdata; - - BLI_stack_discard(kcd->undostack); + /* Undo edge splitting. */ + for (int i = 0; i < undo->splits; i++) { + BLI_stack_pop(kcd->splitstack, &newkfe); + BLI_stack_pop(kcd->splitstack, &kfe); + knife_join_edge(newkfe, kfe); } + + for (int i = 0; i < undo->cuts; i++) { + + BLI_mempool_iternew(kcd->kedges, &iterkfe); + for (kfe = BLI_mempool_iterstep(&iterkfe); kfe; kfe = BLI_mempool_iterstep(&iterkfe)) { + if (!kfe->is_cut || kfe->is_invalid || kfe->splits) { + continue; + } + lastkfe = kfe; + } + + if (lastkfe) { + lastkfe->is_invalid = true; + + /* TODO: Are they always guaranteed to be in this order? */ + v1 = lastkfe->v1; + v2 = lastkfe->v2; + + /* Only remove first vertex if it is the start segment of the cut. */ + if (!v1->is_invalid && !v1->is_splitting) { + v1->is_invalid = true; + /* If the first vertex is touching any other cut edges don't remove it. */ + for (ref = v1->edges.first; ref; ref = ref->next) { + kfe = ref->ref; + if (kfe->is_cut && !kfe->is_invalid) { + v1->is_invalid = false; + break; + } + } + } + + /* Only remove second vertex if it is the end segment of the cut. */ + if (!v2->is_invalid && !v2->is_splitting) { + v2->is_invalid = true; + /* If the second vertex is touching any other cut edges don't remove it. */ + for (ref = v2->edges.first; ref; ref = ref->next) { + kfe = ref->ref; + if (kfe->is_cut && !kfe->is_invalid) { + v2->is_invalid = false; + break; + } + } + } + } + } + + if (kcd->mode == MODE_DRAGGING) { + /* Restore kcd->prev. */ + kcd->prev = undo->pos; + } + + /* Restore data for distance and angle measurements. */ + kcd->mdata = undo->mdata; + + BLI_stack_discard(kcd->undostack); } /** \} */ @@ -4406,6 +4404,12 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_FINISHED; case KNF_MODAL_UNDO: + if (BLI_stack_is_empty(kcd->undostack)) { + ED_region_tag_redraw(kcd->region); + knifetool_exit(op); + ED_workspace_status_text(C, NULL); + return OPERATOR_CANCELLED; + } knifetool_undo(kcd); knife_update_active(C, kcd); ED_region_tag_redraw(kcd->region); From e81533b25e6bcaf703d8b9961fec89eae62dcb9c Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Tue, 28 Sep 2021 18:45:53 -0400 Subject: [PATCH 0334/1500] Cleanup: Simplify compositor Z Combine operation conversion --- source/blender/compositor/nodes/COM_ZCombineNode.cc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.cc b/source/blender/compositor/nodes/COM_ZCombineNode.cc index e29748dc317..9753e812a8b 100644 --- a/source/blender/compositor/nodes/COM_ZCombineNode.cc +++ b/source/blender/compositor/nodes/COM_ZCombineNode.cc @@ -64,18 +64,14 @@ void ZCombineNode::convertToOperations(NodeConverter &converter, NodeOperation *maskoperation; if (this->getbNode()->custom1) { maskoperation = new MathGreaterThanOperation(); - converter.addOperation(maskoperation); - - converter.mapInputSocket(getInputSocket(1), maskoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(3), maskoperation->getInputSocket(1)); } else { maskoperation = new MathLessThanOperation(); - converter.addOperation(maskoperation); - - converter.mapInputSocket(getInputSocket(1), maskoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(3), maskoperation->getInputSocket(1)); } + converter.addOperation(maskoperation); + + converter.mapInputSocket(getInputSocket(1), maskoperation->getInputSocket(0)); + converter.mapInputSocket(getInputSocket(3), maskoperation->getInputSocket(1)); /* Step 2 anti alias mask bit of an expensive operation, but does the trick. */ AntiAliasOperation *antialiasoperation = new AntiAliasOperation(); From 84251acfcc4534059d6ccd6682f8e37d529b9063 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Tue, 28 Sep 2021 20:13:42 -0400 Subject: [PATCH 0335/1500] Cleanup: Compositor: Migrate most output nodes to new socket builder This migrates most nodes except for the file output node. This node requires dynamic sockets so its implementation will be more involved. --- .../nodes/node_composite_composite.cc | 19 ++++++++++------- .../composite/nodes/node_composite_levels.cc | 21 ++++++++++--------- .../nodes/node_composite_splitViewer.cc | 17 +++++++++------ .../composite/nodes/node_composite_viewer.cc | 18 ++++++++++------ 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc index 68884321b20..5076a24d155 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.cc +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -24,19 +24,24 @@ #include "node_composite_util.hh" /* **************** COMPOSITE ******************** */ -static bNodeSocketTemplate cmp_node_composite_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Z"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_composite_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image"); + b.add_input("Alpha").default_value(1.0f).min(0.0f).max(1.0f); + b.add_input("Z").default_value(1.0f).min(0.0f).max(1.0f); +} + +} // namespace blender::nodes void register_node_type_cmp_composite(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_composite_in, nullptr); + ntype.declare = blender::nodes::cmp_node_composite_declare; /* Do not allow muting for this node. */ node_type_internal_links(&ntype, nullptr); diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc index 3a3d8a3e99c..b447b7510f1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_levels.cc +++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc @@ -24,16 +24,17 @@ #include "node_composite_util.hh" /* **************** LEVELS ******************** */ -static bNodeSocketTemplate cmp_node_view_levels_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_view_levels_out[] = { - {SOCK_FLOAT, N_("Mean")}, - {SOCK_FLOAT, N_("Std Dev")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_levels_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image"); + b.add_output("Mean"); + b.add_output("Std Dev"); +} + +} // namespace blender::nodes static void node_composit_init_view_levels(bNodeTree *UNUSED(ntree), bNode *node) { @@ -45,7 +46,7 @@ void register_node_type_cmp_view_levels(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_VIEW_LEVELS, "Levels", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_view_levels_in, cmp_node_view_levels_out); + ntype.declare = blender::nodes::cmp_node_levels_declare; node_type_init(&ntype, node_composit_init_view_levels); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc index 54a1c59fca6..27fbb0fafd4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc @@ -27,11 +27,16 @@ #include "BKE_image.h" /* **************** SPLIT VIEWER ******************** */ -static bNodeSocketTemplate cmp_node_splitviewer_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_splitviewer_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image"); + b.add_input("Image"); +} + +} // namespace blender::nodes static void node_composit_init_splitviewer(bNodeTree *UNUSED(ntree), bNode *node) { @@ -50,7 +55,7 @@ void register_node_type_cmp_splitviewer(void) cmp_node_type_base( &ntype, CMP_NODE_SPLITVIEWER, "Split Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_splitviewer_in, nullptr); + ntype.declare = blender::nodes::cmp_node_splitviewer_declare; node_type_init(&ntype, node_composit_init_splitviewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc index a73a7c81357..48ba0abc922 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -27,11 +27,17 @@ #include "BKE_image.h" /* **************** VIEWER ******************** */ -static bNodeSocketTemplate cmp_node_viewer_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Z"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}}; + +namespace blender::nodes { + +static void cmp_node_viewer_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image"); + b.add_input("Alpha").default_value(1.0f).min(0.0f).max(1.0f); + b.add_input("Z").default_value(1.0f).min(0.0f).max(1.0f); +} + +} // namespace blender::nodes static void node_composit_init_viewer(bNodeTree *UNUSED(ntree), bNode *node) { @@ -50,7 +56,7 @@ void register_node_type_cmp_viewer(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_viewer_in, nullptr); + ntype.declare = blender::nodes::cmp_node_viewer_declare; node_type_init(&ntype, node_composit_init_viewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); From 8e40bb2dea005a691160b48c94087868a8d21548 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Tue, 28 Sep 2021 20:33:42 -0400 Subject: [PATCH 0336/1500] Nodes: Fix compositor viewer nodes having wrong alpha channel Default should be black image with an alpha value of 1. --- .../blender/nodes/composite/nodes/node_composite_composite.cc | 2 +- source/blender/nodes/composite/nodes/node_composite_levels.cc | 2 +- source/blender/nodes/composite/nodes/node_composite_viewer.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc index 5076a24d155..170fecb251c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.cc +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void cmp_node_composite_declare(NodeDeclarationBuilder &b) { - b.add_input("Image"); + b.add_input("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); b.add_input("Alpha").default_value(1.0f).min(0.0f).max(1.0f); b.add_input("Z").default_value(1.0f).min(0.0f).max(1.0f); } diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc index b447b7510f1..aaab8dcc874 100644 --- a/source/blender/nodes/composite/nodes/node_composite_levels.cc +++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void cmp_node_levels_declare(NodeDeclarationBuilder &b) { - b.add_input("Image"); + b.add_input("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); b.add_output("Mean"); b.add_output("Std Dev"); } diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc index 48ba0abc922..7234d4d8eb2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -32,7 +32,7 @@ namespace blender::nodes { static void cmp_node_viewer_declare(NodeDeclarationBuilder &b) { - b.add_input("Image"); + b.add_input("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); b.add_input("Alpha").default_value(1.0f).min(0.0f).max(1.0f); b.add_input("Z").default_value(1.0f).min(0.0f).max(1.0f); } From d1220f795f3b81d2b1a901fd4980f9446ce07431 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Tue, 28 Sep 2021 21:36:15 -0400 Subject: [PATCH 0337/1500] UI: Update Levels Nodes - Instead of making the enum expanded leave it in menu form. - Use full words instead of letters. Differential Revision: https://developer.blender.org/D12686 --- source/blender/editors/space_node/drawnode.cc | 2 +- source/blender/makesrna/intern/rna_nodetree.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 500e23e5451..b4a9b90434c 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -2079,7 +2079,7 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "channel", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b5a42babc81..8fb55d37375 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -6545,11 +6545,11 @@ static void def_cmp_levels(StructRNA *srna) PropertyRNA *prop; static const EnumPropertyItem channel_items[] = { - {1, "COMBINED_RGB", 0, "C", "Combined RGB"}, - {2, "RED", 0, "R", "Red Channel"}, - {3, "GREEN", 0, "G", "Green Channel"}, - {4, "BLUE", 0, "B", "Blue Channel"}, - {5, "LUMINANCE", 0, "L", "Luminance Channel"}, + {1, "COMBINED_RGB", 0, "Combined", "Combined RGB"}, + {2, "RED", 0, "Red", "Red Channel"}, + {3, "GREEN", 0, "Green", "Green Channel"}, + {4, "BLUE", 0, "Blue", "Blue Channel"}, + {5, "LUMINANCE", 0, "Luminance", "Luminance Channel"}, {0, NULL, 0, NULL, NULL}, }; From 756c22bb411f50e6fcb400fb8dcb3e58968e5f75 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 29 Sep 2021 00:09:41 -0400 Subject: [PATCH 0338/1500] Cleanup: Compositor: Migrate color nodes to new socket builder --- .../nodes/node_composite_alphaOver.cc | 24 +++++++------- .../nodes/node_composite_brightness.cc | 23 ++++++------- .../nodes/node_composite_colorbalance.cc | 21 ++++++------ .../nodes/node_composite_colorcorrection.cc | 23 ++++++------- .../composite/nodes/node_composite_curves.cc | 25 ++++++++------- .../nodes/node_composite_exposure.cc | 21 ++++++------ .../composite/nodes/node_composite_gamma.cc | 25 +++++++++------ .../nodes/node_composite_hueSatVal.cc | 32 +++++++++++-------- .../nodes/node_composite_huecorrect.cc | 20 ++++++------ .../composite/nodes/node_composite_invert.cc | 17 ++++++---- .../composite/nodes/node_composite_mixrgb.cc | 24 +++++++------- .../composite/nodes/node_composite_tonemap.cc | 19 +++++------ .../nodes/node_composite_zcombine.cc | 30 +++++++++-------- 13 files changed, 166 insertions(+), 138 deletions(-) diff --git a/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc index 156faed8524..6210d946bc7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc +++ b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc @@ -24,16 +24,18 @@ #include "node_composite_util.hh" /* **************** ALPHAOVER ******************** */ -static bNodeSocketTemplate cmp_node_alphaover_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_alphaover_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Image"); +} + +} // namespace blender::nodes static void node_alphaover_init(bNodeTree *UNUSED(ntree), bNode *node) { @@ -45,7 +47,7 @@ void register_node_type_cmp_alphaover(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_ALPHAOVER, "Alpha Over", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_alphaover_in, cmp_node_alphaover_out); + ntype.declare = blender::nodes::cmp_node_alphaover_declare; node_type_init(&ntype, node_alphaover_init); node_type_storage( &ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc index 790ccea4dc5..ad4b09c69d0 100644 --- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc +++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc @@ -25,16 +25,17 @@ /* **************** Bright and Contrast ******************** */ -static bNodeSocketTemplate cmp_node_brightcontrast_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Bright"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Contrast"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_brightcontrast_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Bright").min(-100.0f).max(100.0f); + b.add_input("Contrast").min(-100.0f).max(100.0f); + b.add_output("Image"); +} + +} // namespace blender::nodes static void node_composit_init_brightcontrast(bNodeTree *UNUSED(ntree), bNode *node) { @@ -46,7 +47,7 @@ void register_node_type_cmp_brightcontrast(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_BRIGHTCONTRAST, "Bright/Contrast", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_brightcontrast_in, cmp_node_brightcontrast_out); + ntype.declare = blender::nodes::cmp_node_brightcontrast_declare; node_type_init(&ntype, node_composit_init_brightcontrast); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc index 0ca8eecd7db..440e37fe741 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -24,16 +24,17 @@ #include "node_composite_util.hh" /* ******************* Color Balance ********************************* */ -static bNodeSocketTemplate cmp_node_colorbalance_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_colorbalance_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Image"); +} + +} // namespace blender::nodes /* Sync functions update formula parameters for other modes, such that the result is comparable. * Note that the results are not exactly the same due to differences in color handling @@ -84,7 +85,7 @@ void register_node_type_cmp_colorbalance(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COLORBALANCE, "Color Balance", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_colorbalance_in, cmp_node_colorbalance_out); + ntype.declare = blender::nodes::cmp_node_colorbalance_declare; node_type_size(&ntype, 400, 200, 400); node_type_init(&ntype, node_composit_init_colorbalance); node_type_storage( diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc index 93f6af7bf81..0682c66f1e8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc @@ -23,17 +23,18 @@ #include "node_composite_util.hh" -/* ******************* Color Balance ********************************* */ -static bNodeSocketTemplate cmp_node_colorcorrection_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Mask"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; +/* ******************* Color Correction ********************************* */ -static bNodeSocketTemplate cmp_node_colorcorrection_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Mask").default_value(1.0f).min(0.0f).max(1.0f); + b.add_output("Image"); +} + +} // namespace blender::nodes static void node_composit_init_colorcorrection(bNodeTree *UNUSED(ntree), bNode *node) { @@ -70,7 +71,7 @@ void register_node_type_cmp_colorcorrection(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COLORCORRECTION, "Color Correction", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_colorcorrection_in, cmp_node_colorcorrection_out); + ntype.declare = blender::nodes::cmp_node_colorcorrection_declare; node_type_size(&ntype, 400, 200, 600); node_type_init(&ntype, node_composit_init_colorcorrection); node_type_storage( diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc index 885a5511165..88d96e1ca4a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.cc +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -85,18 +85,19 @@ void register_node_type_cmp_curve_vec(void) } /* **************** CURVE RGB ******************** */ -static bNodeSocketTemplate cmp_node_curve_rgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_RGBA, N_("Black Level"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("White Level"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_curve_rgb_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").default_value(1.0f).min(-1.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Black Level").default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input("White Level").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Image"); +} + +} // namespace blender::nodes static void node_composit_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node) { @@ -108,7 +109,7 @@ void register_node_type_cmp_curve_rgb(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_curve_rgb_in, cmp_node_curve_rgb_out); + ntype.declare = blender::nodes::cmp_node_rgbcurves_declare; node_type_size(&ntype, 200, 140, 320); node_type_init(&ntype, node_composit_init_curve_rgb); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc index f0259f781a5..fd959376afe 100644 --- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc +++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc @@ -25,22 +25,23 @@ /* **************** Exposure ******************** */ -static bNodeSocketTemplate cmp_node_exposure_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Exposure"), 0.0f, 0.0f, 0.0f, 0.0f, -10.0f, 10.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_exposure_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_exposure_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Exposure").min(-10.0f).max(10.0f); + b.add_output("Image"); +} + +} // namespace blender::nodes void register_node_type_cmp_exposure(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_exposure_in, cmp_node_exposure_out); + ntype.declare = blender::nodes::cmp_node_exposure_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc index b2b63c58411..62bc99ff1be 100644 --- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc +++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc @@ -25,22 +25,27 @@ /* **************** Gamma Tools ******************** */ -static bNodeSocketTemplate cmp_node_gamma_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Gamma"), 1.0f, 0.0f, 0.0f, 0.0f, 0.001f, 10.0f, PROP_UNSIGNED}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_gamma_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_gamma_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Gamma") + .default_value(1.0f) + .min(0.001f) + .max(10.0f) + .subtype(PROP_UNSIGNED); + b.add_output("Image"); +} + +} // namespace blender::nodes void register_node_type_cmp_gamma(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_gamma_in, cmp_node_gamma_out); + ntype.declare = blender::nodes::cmp_node_gamma_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc index c0fb89bd3c4..07746918a94 100644 --- a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc +++ b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc @@ -24,25 +24,31 @@ #include "node_composite_util.hh" /* **************** Hue Saturation ******************** */ -static bNodeSocketTemplate cmp_node_hue_sat_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Hue"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Saturation"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Value"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_hue_sat_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Hue").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Saturation") + .default_value(1.0f) + .min(0.0f) + .max(2.0f) + .subtype(PROP_FACTOR); + b.add_input("Value").default_value(1.0f).min(0.0f).max(2.0f).subtype(PROP_FACTOR); + b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output("Image"); +} + +} // namespace blender::nodes void register_node_type_cmp_hue_sat(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_hue_sat_in, cmp_node_hue_sat_out); + ntype.declare = blender::nodes::cmp_node_huesatval_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc index c577781d96d..39014896a7b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc +++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc @@ -23,16 +23,16 @@ #include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_huecorrect_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate cmp_node_huecorrect_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Image"); +} + +} // namespace blender::nodes static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node) { @@ -56,7 +56,7 @@ void register_node_type_cmp_huecorrect(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_HUECORRECT, "Hue Correct", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_huecorrect_in, cmp_node_huecorrect_out); + ntype.declare = blender::nodes::cmp_node_huecorrect_declare; node_type_size(&ntype, 320, 140, 500); node_type_init(&ntype, node_composit_init_huecorrect); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.cc b/source/blender/nodes/composite/nodes/node_composite_invert.cc index 88fd18326d9..57b7ed36ccd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_invert.cc +++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc @@ -24,12 +24,17 @@ #include "node_composite_util.hh" /* **************** INVERT ******************** */ -static bNodeSocketTemplate cmp_node_invert_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_invert_out[] = {{SOCK_RGBA, N_("Color")}, {-1, ""}}; +namespace blender::nodes { + +static void cmp_node_invert_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Color"); +} + +} // namespace blender::nodes static void node_composit_init_invert(bNodeTree *UNUSED(ntree), bNode *node) { @@ -42,7 +47,7 @@ void register_node_type_cmp_invert(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_INVERT, "Invert", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_invert_in, cmp_node_invert_out); + ntype.declare = blender::nodes::cmp_node_invert_declare; node_type_init(&ntype, node_composit_init_invert); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc index 985159c54c2..4f2a9c2717c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc @@ -24,16 +24,18 @@ #include "node_composite_util.hh" /* **************** MIX RGB ******************** */ -static bNodeSocketTemplate cmp_node_mix_rgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_mix_rgb_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_mixrgb_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Image"); +} + +} // namespace blender::nodes /* custom1 = mix type */ void register_node_type_cmp_mix_rgb(void) @@ -41,7 +43,7 @@ void register_node_type_cmp_mix_rgb(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_mix_rgb_in, cmp_node_mix_rgb_out); + ntype.declare = blender::nodes::cmp_node_mixrgb_declare; node_type_label(&ntype, node_blend_label); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc index 29984e3687c..85fd240ce2e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc +++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc @@ -23,14 +23,15 @@ #include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_tonemap_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_tonemap_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_tonemap_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Image"); +} + +} // namespace blender::nodes static void node_composit_init_tonemap(bNodeTree *UNUSED(ntree), bNode *node) { @@ -53,7 +54,7 @@ void register_node_type_cmp_tonemap(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_TONEMAP, "Tonemap", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_tonemap_in, cmp_node_tonemap_out); + ntype.declare = blender::nodes::cmp_node_tonemap_declare; node_type_init(&ntype, node_composit_init_tonemap); node_type_storage(&ntype, "NodeTonemap", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc index 990f9fcd366..79e4d449159 100644 --- a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc +++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc @@ -24,26 +24,28 @@ #include "node_composite_util.hh" /* **************** Z COMBINE ******************** */ -/* lazy coder NOTE: node->custom2 is abused to send signal. */ -static bNodeSocketTemplate cmp_node_zcombine_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_NONE}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_zcombine_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Z")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_zcombine_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Z").default_value(1.0f).min(0.0f).max(10000.0f); + b.add_input("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Z", "Z_001").default_value(1.0f).min(0.0f).max(10000.0f); + b.add_output("Image"); + b.add_output("Z"); +} + +} // namespace blender::nodes + +/* lazy coder NOTE: node->custom2 is abused to send signal. */ void register_node_type_cmp_zcombine(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_ZCOMBINE, "Z Combine", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_zcombine_in, cmp_node_zcombine_out); + ntype.declare = blender::nodes::cmp_node_zcombine_declare; nodeRegisterType(&ntype); } From eddc3f5bc8473a4914476dc3ff3ee9091bb42b9a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 28 Sep 2021 23:19:33 -0500 Subject: [PATCH 0339/1500] Cleanup: Add constructor for AttributeFieldInput --- source/blender/blenkernel/BKE_geometry_set.hh | 7 +++++++ source/blender/modifiers/intern/MOD_nodes_evaluator.cc | 3 +-- .../nodes/geometry/nodes/node_geo_input_position.cc | 3 +-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 724ca224cab..f182fb527e1 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -722,6 +722,13 @@ class AttributeFieldInput : public fn::FieldInput { { } + template static fn::Field Create(std::string name) + { + const CPPType &type = CPPType::get(); + auto field_input = std::make_shared(std::move(name), type); + return fn::Field{field_input}; + } + StringRefNull attribute_name() const { return name_; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 592e6180f80..c5213dc304b 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -334,8 +334,7 @@ static void get_socket_value(const SocketRef &socket, void *r_value) SH_NODE_TEX_NOISE, GEO_NODE_MESH_TO_POINTS, GEO_NODE_PROXIMITY)) { - new (r_value) Field( - std::make_shared("position", CPPType::get())); + new (r_value) Field(bke::AttributeFieldInput::Create("position")); return; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc index f439fb32d31..44874259e20 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc @@ -25,8 +25,7 @@ static void geo_node_input_position_declare(NodeDeclarationBuilder &b) static void geo_node_input_position_exec(GeoNodeExecParams params) { - Field position_field{ - std::make_shared("position", CPPType::get())}; + Field position_field{AttributeFieldInput::Create("position")}; params.set_output("Position", std::move(position_field)); } From 008ae26712f85475a8a9dc4d031447e12fb05522 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 16 Sep 2021 12:40:22 +0200 Subject: [PATCH 0340/1500] Fix T91237: Wrong Editors could sync animation 'Visible Range' This was reported for the Outliner. It was possible to set 'show_locked_time' on any space (via python, not sure if there are other ways to achieve this). Navigating in an animation editor obviously ruined the layout in certain Editors that are not made for this. Now restrict syncing to editors that support it well (the ones that have this setting exposed in their menus) and prevent setting this in RNA. Maniphest Tasks: T91237 Differential Revision: https://developer.blender.org/D12512 --- source/blender/editors/include/UI_view2d.h | 1 + source/blender/editors/interface/view2d.c | 8 ++++++++ source/blender/makesrna/intern/rna_space.c | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index e3c02b4c249..fdfa07a7e02 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -123,6 +123,7 @@ void UI_view2d_region_reinit(struct View2D *v2d, short type, int winx, int winy) void UI_view2d_curRect_validate(struct View2D *v2d); void UI_view2d_curRect_reset(struct View2D *v2d); +bool UI_view2d_area_supports_sync(struct ScrArea *area); void UI_view2d_sync(struct bScreen *screen, struct ScrArea *area, struct View2D *v2dcur, int flag); /* Perform all required updates after `v2d->cur` as been modified. diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 23c8a0d35bf..0036a812a87 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -866,6 +866,11 @@ void UI_view2d_curRect_changed(const bContext *C, View2D *v2d) /* ------------------ */ +bool UI_view2d_area_supports_sync(ScrArea *area) +{ + return ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP, SPACE_GRAPH); +} + /* Called by menus to activate it, or by view2d operators * to make sure 'related' views stay in synchrony */ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag) @@ -903,6 +908,9 @@ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag) /* check if doing whole screen syncing (i.e. time/horizontal) */ if ((v2dcur->flag & V2D_VIEWSYNC_SCREEN_TIME) && (screen)) { LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { + if (!UI_view2d_area_supports_sync(area_iter)) { + continue; + } LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) { /* don't operate on self */ if (v2dcur != ®ion->v2d) { diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 88ef1597e4c..b5b9ac6ee56 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -856,6 +856,14 @@ static void rna_Space_view2d_sync_set(PointerRNA *ptr, bool value) ARegion *region; area = rna_area_from_space(ptr); /* can be NULL */ + if ((area != NULL) && !UI_view2d_area_supports_sync(area)) { + BKE_reportf(NULL, + RPT_ERROR, + "'show_locked_time' is not supported for the '%s' editor", + area->type->name); + return; + } + region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); if (region) { View2D *v2d = ®ion->v2d; From bf06f76be6316be92a4655a41391e163d2fb1221 Mon Sep 17 00:00:00 2001 From: Siddhartha Jejurkar Date: Wed, 29 Sep 2021 17:47:32 +1000 Subject: [PATCH 0341/1500] UV Editor: Grid and snapping improvements Implements T89789, T89792, custom grid (described as dynamic grid in T78389) and UV grid snapping (T78391) Replaces the default UV editor grid with 2 new types of grid : * Custom grid: Allows the user to create an NxN grid, where the value of N is specified by the user. * Subdividing grid: Subdivides the UV editor grid when the user zooms in the viewport and vice versa when zooming out. UV snapping improvements : * Increment snapping: Increment values for snapping are calculated based on which grid type is being used in the UV editor (subdividing or custom). In general the increment value is equal to the distance between 2 visible grid lines. * Absolute grid snap: New toggle added to increment snapping option in the UV editor, allows UV grid snapping during translation. Reviewed By: campbellbarton Ref D12684 --- release/scripts/startup/bl_ui/space_image.py | 32 ++++++++++ .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_280.c | 2 +- .../blenloader/intern/versioning_300.c | 46 ++++++++------ .../draw/engines/overlay/overlay_grid.c | 16 ++++- .../draw/engines/overlay/overlay_private.h | 3 +- .../engines/overlay/shaders/grid_frag.glsl | 15 ++++- source/blender/editors/include/ED_image.h | 10 ++++ .../blender/editors/space_image/image_draw.c | 60 +++++++++++++++++++ .../blender/editors/space_image/space_image.c | 2 + source/blender/editors/transform/transform.c | 13 +++- .../editors/transform/transform_snap.c | 8 ++- source/blender/makesdna/DNA_scene_types.h | 7 ++- source/blender/makesdna/DNA_space_types.h | 12 +++- source/blender/makesrna/intern/rna_scene.c | 8 +++ source/blender/makesrna/intern/rna_space.c | 13 ++++ .../composite/nodes/node_composite_gamma.cc | 7 +-- 17 files changed, 218 insertions(+), 38 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 3ee668888f3..797d0c62b72 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -934,6 +934,10 @@ class IMAGE_PT_snapping(Panel): row = col.row(align=True) row.prop(tool_settings, "snap_target", expand=True) + col.separator() + if 'INCREMENT' in tool_settings.snap_uv_element: + col.prop(tool_settings, "use_snap_uv_grid_absolute") + col.label(text="Affect") row = col.row(align=True) row.prop(tool_settings, "use_snap_translate", text="Move", toggle=True) @@ -1467,6 +1471,33 @@ class IMAGE_PT_udim_grid(Panel): col = layout.column() col.prop(uvedit, "tile_grid_shape", text="Grid Shape") +class IMAGE_PT_custom_grid(Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'UI' + bl_category = "View" + bl_label = "Custom Grid" + + @classmethod + def poll(cls, context): + sima = context.space_data + return sima.show_uvedit + + def draw_header(self, context): + sima = context.space_data + uvedit = sima.uv_editor + self.layout.prop(uvedit, "use_custom_grid", text="") + + def draw(self, context): + layout = self.layout + + sima = context.space_data + uvedit = sima.uv_editor + + layout.use_property_split = True + layout.use_property_decorate = False + + col = layout.column() + col.prop(uvedit, "custom_grid_subdivisions", text="Subdivisions") class IMAGE_PT_overlay(Panel): bl_space_type = 'IMAGE_EDITOR' @@ -1652,6 +1683,7 @@ classes = ( IMAGE_PT_uv_cursor, IMAGE_PT_annotation, IMAGE_PT_udim_grid, + IMAGE_PT_custom_grid, IMAGE_PT_overlay, IMAGE_PT_overlay_uv_edit, IMAGE_PT_overlay_uv_edit_geometry, diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 5ef0459663b..65f9da3b852 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 28 +#define BLENDER_FILE_SUBVERSION 29 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 292ca726b6f..d8f4d01a2e9 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3394,7 +3394,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) SpaceImage *sima = (SpaceImage *)sl; sima->flag &= ~(SI_FLAG_UNUSED_0 | SI_FLAG_UNUSED_1 | SI_FLAG_UNUSED_3 | SI_FLAG_UNUSED_6 | SI_FLAG_UNUSED_7 | SI_FLAG_UNUSED_8 | - SI_FLAG_UNUSED_17 | SI_FLAG_UNUSED_18 | SI_FLAG_UNUSED_23 | + SI_FLAG_UNUSED_17 | SI_CUSTOM_GRID | SI_FLAG_UNUSED_23 | SI_FLAG_UNUSED_24); break; } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 3b51e40c218..ea368c0b3b1 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1579,28 +1579,25 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 300, 29)) { LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { - if (sl->spacetype == SPACE_SEQ) { - ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : - &sl->regionbase; - LISTBASE_FOREACH (ARegion *, region, regionbase) { - if (region->regiontype == RGN_TYPE_WINDOW) { - region->v2d.max[1] = MAXSEQ; + switch (sl->spacetype) { + case SPACE_SEQ: { + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + LISTBASE_FOREACH (ARegion *, region, regionbase) { + if (region->regiontype == RGN_TYPE_WINDOW) { + region->v2d.max[1] = MAXSEQ; + } } + break; + } + case SPACE_IMAGE: { + SpaceImage *sima = (SpaceImage *)sl; + sima->custom_grid_subdiv = 10; + break; } } } @@ -1613,4 +1610,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } } diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c index 60cda9f2d61..31c8ed9d664 100644 --- a/source/blender/draw/engines/overlay/overlay_grid.c +++ b/source/blender/draw/engines/overlay/overlay_grid.c @@ -23,6 +23,7 @@ #include "DRW_render.h" #include "DNA_camera_types.h" +#include "DNA_screen_types.h" #include "DEG_depsgraph_query.h" @@ -46,6 +47,7 @@ enum { GRID_BACK = (1 << 9), GRID_CAMERA = (1 << 10), PLANE_IMAGE = (1 << 11), + CUSTOM_GRID = (1 << 12), }; void OVERLAY_grid_init(OVERLAY_Data *vedata) @@ -61,6 +63,7 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) if (pd->space_type == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; + View2D *v2d = &draw_ctx->region->v2d; if (sima->mode == SI_MODE_UV || !ED_space_image_has_buffer(sima)) { shd->grid_flag = GRID_BACK | PLANE_IMAGE | SHOW_GRID; } @@ -68,15 +71,21 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) shd->grid_flag = 0; } + if (sima->flag & SI_CUSTOM_GRID) { + shd->grid_flag |= CUSTOM_GRID; + } + shd->grid_distance = 1.0f; copy_v3_fl3(shd->grid_size, 1.0f, 1.0f, 1.0f); if (sima->mode == SI_MODE_UV) { shd->grid_size[0] = (float)sima->tile_grid_shape[0]; shd->grid_size[1] = (float)sima->tile_grid_shape[1]; } - for (int step = 0; step < 8; step++) { - shd->grid_steps[step] = powf(4, step) * (1.0f / 16.0f); - } + + const int grid_size = SI_GRID_STEPS_LEN; + shd->zoom_factor = ED_space_image_zoom_level(v2d, grid_size); + ED_space_image_grid_steps(sima, shd->grid_steps, grid_size); + return; } @@ -248,6 +257,7 @@ void OVERLAY_grid_cache_init(OVERLAY_Data *vedata) grp = DRW_shgroup_create(sh, psl->grid_ps); DRW_shgroup_uniform_int(grp, "gridFlag", &shd->grid_flag, 1); + DRW_shgroup_uniform_float_copy(grp, "zoomFactor", shd->zoom_factor); DRW_shgroup_uniform_vec3(grp, "planeAxes", shd->grid_axes, 1); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 23df571e8de..def278f98df 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -139,9 +139,10 @@ typedef struct OVERLAY_ShadingData { /** Grid */ float grid_axes[3], grid_distance; float zplane_axes[3], grid_size[3]; - float grid_steps[8]; + float grid_steps[SI_GRID_STEPS_LEN]; float inv_viewport_size[2]; float grid_line_size; + float zoom_factor; /* Only for UV editor */ int grid_flag; int zpos_flag; int zneg_flag; diff --git a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl index 3220adbff36..9feca644bd3 100644 --- a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl @@ -15,8 +15,9 @@ uniform float lineKernel = 0.0; uniform sampler2D depthBuffer; uniform int gridFlag; +uniform float zoomFactor; -#define STEPS_LEN 8 +#define STEPS_LEN 8 /* Match: #SI_GRID_STEPS_LEN */ uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0); #define AXIS_X (1 << 0) @@ -28,6 +29,8 @@ uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0, #define PLANE_YZ (1 << 6) #define GRID_BACK (1 << 9) /* grid is behind objects */ #define GRID_CAMERA (1 << 10) /* In camera view */ +#define PLANE_IMAGE (1 << 11) /* UV/Image Image editor */ +#define CUSTOM_GRID (1 << 12) /* UV Editor only */ #define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */ @@ -122,9 +125,17 @@ void main() * would be more accurate, but not really necessary. */ float grid_res = dot(dFdxPos, screenVecs[0].xyz); - /* The gride begins to appear when it comprises 4 pixels */ + /* The grid begins to appear when it comprises 4 pixels */ grid_res *= 4; + /* For UV/Image editor use zoomFactor */ + if ((gridFlag & PLANE_IMAGE) != 0 && + /* Grid begins to appear when the length of one grid unit is at least + * (256/grid_size) pixels Value of grid_size defined in `overlay_grid.c`. */ + (gridFlag & CUSTOM_GRID) == 0) { + grid_res = zoomFactor; + } + /* from biggest to smallest */ vec4 scale; #if 0 diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index 6b0b9f4a27c..9532035a1cd 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -41,6 +41,16 @@ struct SpaceImage; struct bContext; struct wmOperator; struct wmWindowManager; +struct View2D; + +/* image_draw.c */ +float ED_space_image_zoom_level(const struct View2D *v2d, const int grid_dimension); +void ED_space_image_grid_steps(struct SpaceImage *sima, + float grid_steps[SI_GRID_STEPS_LEN], + const int grid_dimension); +float ED_space_image_increment_snap_value(const int grid_dimesnions, + const float grid_steps[SI_GRID_STEPS_LEN], + const float zoom_factor); /* image_edit.c, exported for transform */ struct Image *ED_space_image(struct SpaceImage *sima); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index d2af26aa1d7..fc04ec1fe02 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -34,6 +34,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "DNA_view2d_types.h" #include "PIL_time.h" @@ -576,3 +577,62 @@ void draw_image_cache(const bContext *C, ARegion *region) ED_mask_draw_frames(mask, region, cfra, sfra, efra); } } + +float ED_space_image_zoom_level(const View2D *v2d, const int grid_dimension) +{ + /* UV-space length per pixel */ + float xzoom = (v2d->cur.xmax - v2d->cur.xmin) / ((float)(v2d->mask.xmax - v2d->mask.xmin)); + float yzoom = (v2d->cur.ymax - v2d->cur.ymin) / ((float)(v2d->mask.ymax - v2d->mask.ymin)); + + /* Zoom_factor for UV/Image editor is calculated based on: + * - Default grid size on startup, which is 256x256 pixels + * - How blend factor for grid lines is set up in the fragment shader `grid_frag.glsl`. */ + float zoom_factor; + zoom_factor = (xzoom + yzoom) / 2.0f; /* Average for accuracy. */ + zoom_factor *= 256.0f / (powf(grid_dimension, 2)); + return zoom_factor; +} + +void ED_space_image_grid_steps(SpaceImage *sima, + float grid_steps[SI_GRID_STEPS_LEN], + const int grid_dimension) +{ + if (sima->flag & SI_CUSTOM_GRID) { + for (int step = 0; step < SI_GRID_STEPS_LEN; step++) { + grid_steps[step] = powf(1, step) * (1.0f / ((float)sima->custom_grid_subdiv)); + } + } + else { + for (int step = 0; step < SI_GRID_STEPS_LEN; step++) { + grid_steps[step] = powf(grid_dimension, step) * + (1.0f / (powf(grid_dimension, SI_GRID_STEPS_LEN))); + } + } +} + +/** + * Calculate the increment snapping value for UV/image editor based on the zoom factor + * The code in here (except the offset part) is used in `grid_frag.glsl` (see `grid_res`) for + * drawing the grid overlay for the UV/Image editor. + */ +float ED_space_image_increment_snap_value(const int grid_dimesnions, + const float grid_steps[SI_GRID_STEPS_LEN], + const float zoom_factor) +{ + /* Small offset on each grid_steps[] so that snapping value doesn't change until grid lines are + * significantly visible. + * `Offset = 3/4 * (grid_steps[i] - (grid_steps[i] / grid_dimesnsions))` + * + * Refer `grid_frag.glsl` to find out when grid lines actually start appearing */ + + for (int step = 0; step < SI_GRID_STEPS_LEN; step++) { + float offset = (3.0f / 4.0f) * (grid_steps[step] - (grid_steps[step] / grid_dimesnions)); + + if ((grid_steps[step] - offset) > zoom_factor) { + return grid_steps[step]; + } + } + + /* Fallback */ + return grid_steps[0]; +} diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index de8e4684d45..5adcdacd49d 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -126,6 +126,8 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED( simage->tile_grid_shape[0] = 1; simage->tile_grid_shape[1] = 1; + simage->custom_grid_subdiv = 10; + /* tool header */ region = MEM_callocN(sizeof(ARegion), "tool header for image"); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index e58e524e341..6ed2c28a7eb 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -28,6 +28,7 @@ #include "DNA_gpencil_types.h" #include "DNA_mask_types.h" #include "DNA_mesh_types.h" +#include "DNA_screen_types.h" #include "BLI_math.h" #include "BLI_rect.h" @@ -1609,8 +1610,16 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]) } } else if (t->spacetype == SPACE_IMAGE) { - r_snap[0] = 0.0625f; - r_snap[1] = 0.03125f; + SpaceImage *sima = t->area->spacedata.first; + View2D *v2d = &t->region->v2d; + int grid_size = SI_GRID_STEPS_LEN; + float zoom_factor = ED_space_image_zoom_level(v2d, grid_size); + float grid_steps[SI_GRID_STEPS_LEN]; + + ED_space_image_grid_steps(sima, grid_steps, grid_size); + /* Snapping value based on what type of grid is used (adaptive-subdividing or custom-grid). */ + r_snap[0] = ED_space_image_increment_snap_value(grid_size, grid_steps, zoom_factor); + r_snap[1] = r_snap[0] / 2.0f; } else if (t->spacetype == SPACE_CLIP) { r_snap[0] = 0.125f; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 05a20a14477..39a70f5477e 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -590,6 +590,11 @@ static void initSnappingMode(TransInfo *t) t->tsnap.project = 0; t->tsnap.mode = ts->snap_uv_mode; + if ((t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_uv_flag & SCE_SNAP_ABS_GRID) && + (t->mode == TFM_TRANSLATION)) { + t->tsnap.mode &= ~SCE_SNAP_MODE_INCREMENT; + t->tsnap.mode |= SCE_SNAP_MODE_GRID; + } } else if (t->spacetype == SPACE_SEQ) { t->tsnap.mode = SEQ_tool_settings_snap_mode_get(t->scene); @@ -1502,7 +1507,8 @@ bool transform_snap_grid(TransInfo *t, float *val) return false; } - if (t->spacetype != SPACE_VIEW3D) { + /* Don't do grid snapping if not in 3D viewport or UV editor */ + if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) { return false; } diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index b28c3ac2b85..9a5c51825e1 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1463,14 +1463,15 @@ typedef struct ToolSettings { char edge_mode_live_unwrap; - char _pad1[1]; - /* Transform */ char transform_pivot_point; char transform_flag; - char snap_mode, snap_node_mode; + char snap_mode; + char snap_node_mode; char snap_uv_mode; char snap_flag; + /** UV equivalent of `snap_flag`, limited to: #SCE_SNAP_ABS_GRID. */ + char snap_uv_flag; char snap_target; char snap_transform_mode_flag; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 075575775ed..a8f21e597c5 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1200,6 +1200,12 @@ typedef struct SpaceImage { float uv_opacity; int tile_grid_shape[2]; + /** + * UV editor custom-grid. Value of `N` will produce `NxN` grid. + * Use when #SI_CUSTOM_GRID is set. + */ + int custom_grid_subdiv; + char _pad3[4]; MaskSpaceInfo mask_info; SpaceImageOverlay overlay; @@ -1255,6 +1261,7 @@ typedef enum eSpaceImage_Flag { SI_FLAG_UNUSED_7 = (1 << 7), /* cleared */ SI_FLAG_UNUSED_8 = (1 << 8), /* cleared */ SI_COORDFLOATS = (1 << 9), + SI_FLAG_UNUSED_10 = (1 << 10), SI_LIVE_UNWRAP = (1 << 11), SI_USE_ALPHA = (1 << 12), @@ -1266,7 +1273,7 @@ typedef enum eSpaceImage_Flag { SI_FULLWINDOW = (1 << 16), SI_FLAG_UNUSED_17 = (1 << 17), - SI_FLAG_UNUSED_18 = (1 << 18), /* cleared */ + SI_CUSTOM_GRID = (1 << 18), /** * This means that the image is drawn until it reaches the view edge, @@ -1292,6 +1299,9 @@ typedef enum eSpaceImageOverlay_Flag { SI_OVERLAY_SHOW_OVERLAYS = (1 << 0), } eSpaceImageOverlay_Flag; +/** Keep in sync with `STEPS_LEN` in `grid_frag.glsl`. */ +#define SI_GRID_STEPS_LEN 8 + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 00b7f0c9106..5b46dfa8210 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3162,6 +3162,14 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Snap UV Element", "Type of element to snap to"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "use_snap_uv_grid_absolute", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_uv_flag", SCE_SNAP_ABS_GRID); + RNA_def_property_ui_text( + prop, + "Absolute Grid Snap", + "Absolute grid alignment while translating (based on the pivot center)"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "snap_target", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "snap_target"); RNA_def_property_enum_items(prop, rna_enum_snap_target_items); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index b5b9ac6ee56..a9219fb11be 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3481,6 +3481,19 @@ static void rna_def_space_image_uv(BlenderRNA *brna) prop, "Tile Grid Shape", "How many tiles will be shown in the background"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + prop = RNA_def_property(srna, "use_custom_grid", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_CUSTOM_GRID); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Custom Grid", "Use a grid with a user-defined number of steps"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + + prop = RNA_def_property(srna, "custom_grid_subdivisions", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "custom_grid_subdiv"); + RNA_def_property_range(prop, 1, 5000); + RNA_def_property_ui_text( + prop, "Dynamic Grid Size", "Number of grid units in UV space that make one UV Unit"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + prop = RNA_def_property(srna, "uv_opacity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "uv_opacity"); RNA_def_property_range(prop, 0.0f, 1.0f); diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc index 62bc99ff1be..a29a001688a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc +++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc @@ -30,11 +30,8 @@ namespace blender::nodes { static void cmp_node_gamma_declare(NodeDeclarationBuilder &b) { b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Gamma") - .default_value(1.0f) - .min(0.001f) - .max(10.0f) - .subtype(PROP_UNSIGNED); + b.add_input("Gamma").default_value(1.0f).min(0.001f).max(10.0f).subtype( + PROP_UNSIGNED); b.add_output("Image"); } From a285299ebbf9dcb6af5734d3933b4836d38c188f Mon Sep 17 00:00:00 2001 From: Siddhartha Jejurkar Date: Wed, 29 Sep 2021 17:51:41 +1000 Subject: [PATCH 0342/1500] UV: Pack to closest/active UDIM Implements T78397 Extends the functionality of pack islands operator to allow packing UVs to either the closest or active UDIM tile. This provides 2 new options for packing UVs : * Closest UDIM: Selected UVs will be packed to the UDIM tile they were placed on. If not present on a valid UDIM tile, the UVs will be packed to the closest UDIM in UV space * Active UDIM: Selected UVs will be packed to the active UDIM image tile In case, no image is present in the UV editor, then UVs will be packed to the tile on the UDIM grid where the 2D cursor is located. Reviewed By: campbellbarton Maniphest Tasks: T78397 Ref D12680 --- source/blender/editors/include/ED_uvedit.h | 5 + .../blender/editors/uvedit/uvedit_islands.c | 176 +++++++++++++++++- .../editors/uvedit/uvedit_unwrap_ops.c | 72 ++++++- 3 files changed, 247 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index ea3d921f2c5..516239a7176 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -246,9 +246,14 @@ struct UVPackIsland_Params { uint use_seams : 1; uint correct_aspect : 1; }; + +bool uv_coords_isect_udim(const struct Image *image, const int udim_grid[2], float coords[2]); void ED_uvedit_pack_islands_multi(const struct Scene *scene, + const struct SpaceImage *sima, Object **objects, const uint objects_len, + const bool use_target_udim, + int target_udim, const struct UVPackIsland_Params *params); #ifdef __cplusplus diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c index 56bcbc63de1..2aa09d7e731 100644 --- a/source/blender/editors/uvedit/uvedit_islands.c +++ b/source/blender/editors/uvedit/uvedit_islands.c @@ -29,6 +29,7 @@ #include "DNA_meshdata_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BLI_boxpack_2d.h" #include "BLI_convexhull_2d.h" @@ -38,6 +39,7 @@ #include "BKE_customdata.h" #include "BKE_editmesh.h" +#include "BKE_image.h" #include "DEG_depsgraph.h" @@ -231,6 +233,99 @@ static void bm_face_array_uv_scale_y(BMFace **faces, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name UDIM packing helper functions + * \{ */ + +/* Returns true if UV coordinates lie on a valid tile in UDIM grid or tiled image. */ +bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], float coords[2]) +{ + const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])}; + const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); + + if (coords[0] < udim_grid[0] && coords[0] > 0 && coords[1] < udim_grid[1] && coords[1] > 0) { + return true; + } + /* Check if selection lies on a valid UDIM image tile. */ + else if (is_tiled_image) { + LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) { + const int tile_index = tile->tile_number - 1001; + const int target_x = (tile_index % 10); + const int target_y = (tile_index / 10); + if (coords_floor[0] == target_x && coords_floor[1] == target_y) { + return true; + } + } + } + /* Probably not required since UDIM grid checks for 1001. */ + else if (image && !is_tiled_image) { + if (is_zero_v2(coords_floor)) { + return true; + } + } + + return false; +} + +/** + * Calculates distance to nearest UDIM image tile in UV space and its UDIM tile number. + */ +static float uv_nearest_image_tile_distance(const Image *image, + float coords[2], + float nearest_tile_co[2]) +{ + int nearest_image_tile_index = BKE_image_find_nearest_tile(image, coords); + if (nearest_image_tile_index == -1) { + nearest_image_tile_index = 1001; + } + + nearest_tile_co[0] = (nearest_image_tile_index - 1001) % 10; + nearest_tile_co[1] = (nearest_image_tile_index - 1001) / 10; + /* Add 0.5 to get tile center coordinates. */ + float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]}; + add_v2_fl(nearest_tile_center_co, 0.5f); + + return len_squared_v2v2(coords, nearest_tile_center_co); +} + +/** + * Calculates distance to nearest UDIM grid tile in UV space and its UDIM tile number. + */ +static float uv_nearest_grid_tile_distance(const int udim_grid[2], + float coords[2], + float nearest_tile_co[2]) +{ + const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])}; + + if (coords[0] > udim_grid[0]) { + nearest_tile_co[0] = udim_grid[0] - 1; + } + else if (coords[0] < 0) { + nearest_tile_co[0] = 0; + } + else { + nearest_tile_co[0] = coords_floor[0]; + } + + if (coords[1] > udim_grid[1]) { + nearest_tile_co[1] = udim_grid[1] - 1; + } + else if (coords[1] < 0) { + nearest_tile_co[1] = 0; + } + else { + nearest_tile_co[1] = coords_floor[1]; + } + + /* Add 0.5 to get tile center coordinates. */ + float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]}; + add_v2_fl(nearest_tile_center_co, 0.5f); + + return len_squared_v2v2(coords, nearest_tile_center_co); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Calculate UV Islands * @@ -357,8 +452,11 @@ static int bm_mesh_calc_uv_islands(const Scene *scene, * \{ */ void ED_uvedit_pack_islands_multi(const Scene *scene, + const SpaceImage *sima, Object **objects, const uint objects_len, + const bool use_target_udim, + int target_udim, const struct UVPackIsland_Params *params) { /* Align to the Y axis, could make this configurable. */ @@ -407,8 +505,26 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, BoxPack *boxarray = MEM_mallocN(sizeof(*boxarray) * island_list_len, __func__); int index; + /* Coordinates of bounding box containing all selected UVs. */ + float selection_min_co[2], selection_max_co[2]; + INIT_MINMAX2(selection_min_co, selection_max_co); + LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list, index) { + /* Skip calculation if using specified UDIM option. */ + if (!use_target_udim) { + float bounds_min[2], bounds_max[2]; + INIT_MINMAX2(bounds_min, bounds_max); + for (int i = 0; i < island->faces_len; i++) { + BMFace *f = island->faces[i]; + BM_face_uv_minmax(f, bounds_min, bounds_max, island->cd_loop_uv_offset); + } + + selection_min_co[0] = MIN2(bounds_min[0], selection_min_co[0]); + selection_min_co[1] = MIN2(bounds_min[1], selection_min_co[1]); + selection_max_co[0] = MAX2(bounds_max[0], selection_max_co[0]); + selection_max_co[1] = MAX2(bounds_max[1], selection_max_co[1]); + } if (params->rotate) { if (island->aspect_y != 1.0f) { bm_face_array_uv_scale_y( @@ -441,6 +557,13 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, } } + /* Center of bounding box containing all selected UVs. */ + float selection_center[2]; + if (!use_target_udim) { + selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f; + selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f; + } + if (margin > 0.0f) { /* Logic matches behavior from #param_pack, * use area so multiply the margin by the area to give @@ -464,6 +587,55 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, const float scale[2] = {1.0f / boxarray_size[0], 1.0f / boxarray_size[1]}; + /* Tile offset. */ + float base_offset[2] = {0.0f, 0.0f}; + + /* CASE: Active/specified(smart uv project) UDIM. */ + if (use_target_udim) { + /* Calculate offset based on specified_tile_index. */ + base_offset[0] = (target_udim - 1001) % 10; + base_offset[1] = (target_udim - 1001) / 10; + } + + /* CASE: Closest UDIM. */ + else { + const Image *image; + int udim_grid[2] = {1, 1}; + /* To handle cases where `sima == NULL` - Smart UV project in 3D viewport. */ + if (sima != NULL) { + image = sima->image; + udim_grid[0] = sima->tile_grid_shape[0]; + udim_grid[1] = sima->tile_grid_shape[1]; + } + + /* Check if selection lies on a valid UDIM grid tile. */ + bool is_valid_udim = uv_coords_isect_udim(image, udim_grid, selection_center); + if (is_valid_udim) { + base_offset[0] = floorf(selection_center[0]); + base_offset[1] = floorf(selection_center[1]); + } + /* If selection doesn't lie on any UDIM then find the closest UDIM grid or image tile. */ + else { + float nearest_image_tile_co[2] = {FLT_MAX, FLT_MAX}; + float nearest_image_tile_dist = FLT_MAX, nearest_grid_tile_dist = FLT_MAX; + if (image) { + nearest_image_tile_dist = uv_nearest_image_tile_distance( + image, selection_center, nearest_image_tile_co); + } + + float nearest_grid_tile_co[2] = {0.0f, 0.0f}; + nearest_grid_tile_dist = uv_nearest_grid_tile_distance( + udim_grid, selection_center, nearest_grid_tile_co); + + base_offset[0] = (nearest_image_tile_dist < nearest_grid_tile_dist) ? + nearest_image_tile_co[0] : + nearest_grid_tile_co[0]; + base_offset[1] = (nearest_image_tile_dist < nearest_grid_tile_dist) ? + nearest_image_tile_co[1] : + nearest_grid_tile_co[1]; + } + } + for (int i = 0; i < island_list_len; i++) { struct FaceIsland *island = island_array[boxarray[i].index]; const float pivot[2] = { @@ -471,8 +643,8 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, island->bounds_rect.ymin, }; const float offset[2] = { - (boxarray[i].x * scale[0]) - island->bounds_rect.xmin, - (boxarray[i].y * scale[1]) - island->bounds_rect.ymin, + (boxarray[i].x * scale[0]) - island->bounds_rect.xmin + base_offset[0], + (boxarray[i].y * scale[1]) - island->bounds_rect.ymin + base_offset[1], }; for (int j = 0; j < island->faces_len; j++) { BMFace *efa = island->faces[j]; diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 3d5dabda23d..4e183732db9 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -37,6 +37,7 @@ #include "BLI_alloca.h" #include "BLI_array.h" #include "BLI_linklist.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_string.h" @@ -64,6 +65,7 @@ #include "PIL_time.h" #include "UI_interface.h" +#include "UI_view2d.h" #include "ED_image.h" #include "ED_mesh.h" @@ -1005,10 +1007,17 @@ static void uvedit_pack_islands_multi(const Scene *scene, } } +/* Packing targets. */ +enum { + PACK_UDIM_SRC_CLOSEST = 0, + PACK_UDIM_SRC_ACTIVE = 1, +}; + static int pack_islands_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); const Scene *scene = CTX_data_scene(C); + const SpaceImage *sima = CTX_wm_space_image(C); const UnwrapOptions options = { .topology_from_uvs = true, @@ -1018,17 +1027,20 @@ static int pack_islands_exec(bContext *C, wmOperator *op) .correct_aspect = true, }; - bool rotate = RNA_boolean_get(op->ptr, "rotate"); - uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, CTX_wm_view3d(C), &objects_len); + /* Early exit in case no UVs are selected. */ if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) { MEM_freeN(objects); return OPERATOR_CANCELLED; } + /* RNA props */ + const bool rotate = RNA_boolean_get(op->ptr, "rotate"); + const int udim_source = RNA_enum_get(op->ptr, "udim_source"); + bool use_target_udim = false; if (RNA_struct_property_is_set(op->ptr, "margin")) { scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin"); } @@ -1036,9 +1048,46 @@ static int pack_islands_exec(bContext *C, wmOperator *op) RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin); } + int target_udim = 1001; + if (udim_source == PACK_UDIM_SRC_CLOSEST) { + /* pass */ + } + else if (udim_source == PACK_UDIM_SRC_ACTIVE) { + int active_udim = 1001; + /* NOTE: Presently, when UDIM grid and tiled image are present together, only active tile for + * the tiled imgae is considered. */ + if (sima && sima->image) { + Image *image = sima->image; + ImageTile *active_tile = BLI_findlink(&image->tiles, image->active_tile_index); + if (active_tile) { + active_udim = active_tile->tile_number; + } + } + else if (sima && !sima->image) { + /* Use 2D cursor to find the active tile index for the UDIM grid. */ + float cursor_loc[2] = {sima->cursor[0], sima->cursor[1]}; + if (uv_coords_isect_udim(sima->image, sima->tile_grid_shape, cursor_loc)) { + int tile_number = 1001; + tile_number += floorf(cursor_loc[1]) * 10; + tile_number += floorf(cursor_loc[0]); + active_udim = tile_number; + } + /* TODO: Support storing an active UDIM when there are no tiles present. */ + } + + target_udim = active_udim; + use_target_udim = true; + } + else { + BLI_assert_unreachable(); + } + ED_uvedit_pack_islands_multi(scene, + sima, objects, objects_len, + use_target_udim, + target_udim, &(struct UVPackIsland_Params){ .rotate = rotate, .rotate_align_axis = -1, @@ -1048,16 +1097,25 @@ static int pack_islands_exec(bContext *C, wmOperator *op) }); MEM_freeN(objects); - return OPERATOR_FINISHED; } void UV_OT_pack_islands(wmOperatorType *ot) { + static const EnumPropertyItem pack_target[] = { + {PACK_UDIM_SRC_CLOSEST, "CLOSEST_UDIM", 0, "Closest UDIM", "Pack islands to closest UDIM"}, + {PACK_UDIM_SRC_ACTIVE, + "ACTIVE_UDIM", + 0, + "Active UDIM", + "Pack islands to active UDIM image tile or UDIM grid tile where 2D cursor is located"}, + {0, NULL, 0, NULL, NULL}, + }; /* identifiers */ ot->name = "Pack Islands"; ot->idname = "UV_OT_pack_islands"; - ot->description = "Transform all islands so that they fill up the UV space as much as possible"; + ot->description = + "Transform all islands so that they fill up the UV/UDIM space as much as possible"; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1066,6 +1124,7 @@ void UV_OT_pack_islands(wmOperatorType *ot) ot->poll = ED_operator_uvedit; /* properties */ + RNA_def_enum(ot->srna, "udim_source", pack_target, PACK_UDIM_SRC_CLOSEST, "Pack to", ""); RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit"); RNA_def_float_factor( ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f); @@ -2055,6 +2114,7 @@ static int smart_project_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + const SpaceImage *sima = CTX_wm_space_image(C); /* May be NULL. */ View3D *v3d = CTX_wm_view3d(C); @@ -2065,6 +2125,7 @@ static int smart_project_exec(bContext *C, wmOperator *op) const float project_angle_limit_cos = cosf(project_angle_limit); const float project_angle_limit_half_cos = cosf(project_angle_limit / 2); + const int target_udim = 1001; /* 0-1 UV space. */ /* Memory arena for list links (cleared for each object). */ MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); @@ -2204,8 +2265,11 @@ static int smart_project_exec(bContext *C, wmOperator *op) /* Depsgraph refresh functions are called here. */ const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"); ED_uvedit_pack_islands_multi(scene, + sima, objects_changed, object_changed_len, + true, + target_udim, &(struct UVPackIsland_Params){ .rotate = true, /* We could make this optional. */ From 8cbec0beb2f31447dc20784f66334c997bbc3b0f Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 29 Sep 2021 09:35:05 +0200 Subject: [PATCH 0343/1500] Fix/bypass doversion for wire new colors Some cases were not covered by the original doversion in 4a0ddeb62bb. For now we make it always change the wire color regardless of whether it was changed before or not. This do a subversion bump. --- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenloader/intern/versioning_300.c | 5 ++++- source/blender/blenloader/intern/versioning_userdef.c | 10 ++-------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 65f9da3b852..fc98abf619d 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 29 +#define BLENDER_FILE_SUBVERSION 30 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index ea368c0b3b1..71c7e7cac6a 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -606,6 +606,10 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 30)) { + do_versions_idproperty_ui_data(bmain); + } + /** * Versioning code until next subversion bump goes here. * @@ -618,7 +622,6 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) */ { /* Keep this block, even when empty. */ - do_versions_idproperty_ui_data(bmain); } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 64ecfbe78de..54a0f1beec1 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -291,14 +291,8 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) btheme->space_sequencer.grid[3] = 255; } - if (!USER_VERSION_ATLEAST(300, 27)) { - /* If users have not changed the color of the wires inner color or main color, - * set it to the new default. */ - if ((btheme->space_node.wire[0] == 35) && (btheme->space_node.wire[1] == 35) && - (btheme->space_node.wire[2] == 35) && (btheme->space_node.syntaxr[0] == 128) && - (btheme->space_node.syntaxr[1] == 128) && (btheme->space_node.syntaxr[2] == 128)) { - FROM_DEFAULT_V4_UCHAR(space_node.wire); - } + if (!USER_VERSION_ATLEAST(300, 30)) { + FROM_DEFAULT_V4_UCHAR(space_node.wire); } /** From f51bef75f44681c7a33cf54c790ff4cee949c274 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 29 Sep 2021 10:08:40 +0200 Subject: [PATCH 0344/1500] Geometry Nodes: instance on points in instances For consistency with other nodes, we also want to process all instances in the Points input independently. This allows for more efficient instancing of many objects but also leads to nested instancing. All instances are processed in their local space, so that instances don't have to be realized. Differential Revision: https://developer.blender.org/D12660 --- .../nodes/node_geo_instance_on_points.cc | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index cf9f04f3fe8..490535224c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -163,28 +163,31 @@ static void add_instances_from_component(InstancesComponent &dst_component, static void geo_node_instance_on_points_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Points"); - GeometrySet geometry_set_out; - InstancesComponent &instances = geometry_set_out.get_component_for_write(); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + InstancesComponent &instances = geometry_set.get_component_for_write(); - if (geometry_set.has()) { - add_instances_from_component( - instances, *geometry_set.get_component_for_read(), params); - } - if (geometry_set.has()) { - add_instances_from_component( - instances, *geometry_set.get_component_for_read(), params); - } - if (geometry_set.has()) { - add_instances_from_component( - instances, *geometry_set.get_component_for_read(), params); - } + if (geometry_set.has()) { + add_instances_from_component( + instances, *geometry_set.get_component_for_read(), params); + geometry_set.remove(GEO_COMPONENT_TYPE_MESH); + } + if (geometry_set.has()) { + add_instances_from_component( + instances, *geometry_set.get_component_for_read(), params); + geometry_set.remove(GEO_COMPONENT_TYPE_POINT_CLOUD); + } + if (geometry_set.has()) { + add_instances_from_component( + instances, *geometry_set.get_component_for_read(), params); + geometry_set.remove(GEO_COMPONENT_TYPE_CURVE); + } + /* Unused references may have been added above. Remove those now so that other nodes don't + * process them needlessly. */ + instances.remove_unused_references(); + }); - /* Unused references may have been added above. Remove those now so that other nodes don't - * process them needlessly. */ - instances.remove_unused_references(); - - params.set_output("Instances", std::move(geometry_set_out)); + params.set_output("Instances", std::move(geometry_set)); } } // namespace blender::nodes From 24a965bb16c22e33752dfb6c22105b96a8649aeb Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 29 Sep 2021 10:55:32 +0200 Subject: [PATCH 0345/1500] Fix T91767: crash when instancing geometry coming from modifier The issue was that the `GeometrySet` that comes from the modifier does not have full ownership of the mesh for legacy reasons to avoid copies. Calling `ensure_owns_direct_data` makes sure that the geometry set actually owns the geometry so that it can be instanced. --- .../geometry/nodes/node_geo_instance_on_points.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 490535224c8..8c0c0763be8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -57,10 +57,9 @@ static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b) static void add_instances_from_component(InstancesComponent &dst_component, const GeometryComponent &src_component, + const GeometrySet &instance, const GeoNodeExecParams ¶ms) { - GeometrySet instance = params.get_input("Instance"); - const AttributeDomain domain = ATTR_DOMAIN_POINT; const int domain_size = src_component.attribute_domain_size(domain); @@ -163,23 +162,27 @@ static void add_instances_from_component(InstancesComponent &dst_component, static void geo_node_instance_on_points_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Points"); + GeometrySet instance = params.get_input("Instance"); + instance.ensure_owns_direct_data(); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { InstancesComponent &instances = geometry_set.get_component_for_write(); if (geometry_set.has()) { add_instances_from_component( - instances, *geometry_set.get_component_for_read(), params); + instances, *geometry_set.get_component_for_read(), instance, params); geometry_set.remove(GEO_COMPONENT_TYPE_MESH); } if (geometry_set.has()) { - add_instances_from_component( - instances, *geometry_set.get_component_for_read(), params); + add_instances_from_component(instances, + *geometry_set.get_component_for_read(), + instance, + params); geometry_set.remove(GEO_COMPONENT_TYPE_POINT_CLOUD); } if (geometry_set.has()) { add_instances_from_component( - instances, *geometry_set.get_component_for_read(), params); + instances, *geometry_set.get_component_for_read(), instance, params); geometry_set.remove(GEO_COMPONENT_TYPE_CURVE); } /* Unused references may have been added above. Remove those now so that other nodes don't From eabb1348409012547b1220a80c08163d7c29afaa Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 29 Sep 2021 11:01:17 +0200 Subject: [PATCH 0346/1500] Fix T91802: vertex color layer name collides with itself Vertex colors are already included in `mesh.attributes`. So they don't have to be added to the collision checks separately. --- release/scripts/startup/bl_ui/properties_data_mesh.py | 1 - 1 file changed, 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index d9ad094ac4f..ba5ecd1efde 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -639,7 +639,6 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel): add_attributes(mesh.attributes) add_attributes(mesh.uv_layers) - add_attributes(mesh.vertex_colors) add_attributes(ob.vertex_groups) colliding_names = [name for name, layers in attributes_by_name.items() if len(layers) >= 2] From 4cf4bb2664ebe145dac9715bbbfcc2b96f5ff175 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 28 Sep 2021 14:44:36 +1000 Subject: [PATCH 0347/1500] UI: swap tool and regular header Swap the tool-header and header order so the tool-header so the header is always next to the window edge. Note that files saved in 3.0 will have overlapping headers when opened in any version of Blender before this commit. Reviewed By: Severin, fsiddi Maniphest Tasks: T91536 Ref D12631 --- release/scripts/startup/bl_ui/space_image.py | 12 ++------- .../scripts/startup/bl_ui/space_sequencer.py | 5 +--- .../startup/bl_ui/space_toolsystem_common.py | 11 +++++--- release/scripts/startup/bl_ui/space_view3d.py | 12 ++------- .../blenloader/intern/versioning_300.c | 25 +++++++++++++++++++ source/blender/editors/screen/area.c | 4 +-- .../blender/editors/space_image/space_image.c | 14 +++++------ .../editors/space_sequencer/space_sequencer.c | 14 +++++------ .../editors/space_view3d/space_view3d.c | 14 +++++------ 9 files changed, 60 insertions(+), 51 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 797d0c62b72..6a769b1aecc 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -598,16 +598,10 @@ class IMAGE_HT_tool_header(Header): def draw(self, context): layout = self.layout - layout.template_header() - self.draw_tool_settings(context) layout.separator_spacer() - IMAGE_HT_header.draw_xform_template(layout, context) - - layout.separator_spacer() - self.draw_mode_settings(context) def draw_tool_settings(self, context): @@ -762,8 +756,7 @@ class IMAGE_HT_header(Header): show_uvedit = sima.show_uvedit show_maskedit = sima.show_maskedit - if not show_region_tool_header: - layout.template_header() + layout.template_header() if sima.mode != 'UV': layout.prop(sima, "ui_mode", text="") @@ -784,8 +777,7 @@ class IMAGE_HT_header(Header): layout.separator_spacer() - if not show_region_tool_header: - IMAGE_HT_header.draw_xform_template(layout, context) + IMAGE_HT_header.draw_xform_template(layout, context) layout.template_ID(sima, "image", new="image.new", open="image.open") diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 5cfd258b174..3622154a178 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -99,8 +99,6 @@ class SEQUENCER_HT_tool_header(Header): def draw(self, context): layout = self.layout - layout.template_header() - self.draw_tool_settings(context) # TODO: options popover. @@ -132,8 +130,7 @@ class SEQUENCER_HT_header(Header): show_region_tool_header = st.show_region_tool_header - if not show_region_tool_header: - layout.template_header() + layout.template_header() layout.prop(st, "view_type", text="") diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 98e29d3baba..4a598d0aa63 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -190,7 +190,7 @@ class ToolActivePanelHelper: ToolSelectPanelHelper.draw_active_tool_header( context, layout.column(), - show_tool_name=True, + show_tool_icon=True, tool_key=ToolSelectPanelHelper._tool_key_from_context(context, space_type=self.bl_space_type), ) @@ -766,7 +766,7 @@ class ToolSelectPanelHelper: def draw_active_tool_header( context, layout, *, - show_tool_name=False, + show_tool_icon=False, tool_key=None, ): if tool_key is None: @@ -783,9 +783,12 @@ class ToolSelectPanelHelper: return None # Note: we could show 'item.text' here but it makes the layout jitter when switching tools. # Add some spacing since the icon is currently assuming regular small icon size. - layout.label(text=" " + item.label if show_tool_name else " ", icon_value=icon_value) - if show_tool_name: + if show_tool_icon: + layout.label(text=" " + item.label, icon_value=icon_value) layout.separator() + else: + layout.label(text=item.label) + draw_settings = item.draw_settings if draw_settings is not None: draw_settings(context, layout, tool) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 5d609c0afdf..281c57b282f 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -46,16 +46,10 @@ class VIEW3D_HT_tool_header(Header): def draw(self, context): layout = self.layout - layout.row(align=True).template_header() - self.draw_tool_settings(context) layout.separator_spacer() - VIEW3D_HT_header.draw_xform_template(layout, context) - - layout.separator_spacer() - self.draw_mode_settings(context) def draw_tool_settings(self, context): @@ -604,10 +598,8 @@ class VIEW3D_HT_header(Header): tool_settings = context.tool_settings view = context.space_data shading = view.shading - show_region_tool_header = view.show_region_tool_header - if not show_region_tool_header: - layout.row(align=True).template_header() + layout.row(align=True).template_header() row = layout.row(align=True) obj = context.active_object @@ -754,7 +746,7 @@ class VIEW3D_HT_header(Header): ) layout.separator_spacer() - elif not show_region_tool_header: + else: # Transform settings depending on tool header visibility VIEW3D_HT_header.draw_xform_template(layout, context) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 71c7e7cac6a..1c4a0690886 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1625,5 +1625,30 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Swap header with the tool header so the regular header is always on the edge. */ + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + ARegion *region_tool = NULL, *region_head = NULL; + int region_tool_index = -1, region_head_index = -1, i; + LISTBASE_FOREACH_INDEX (ARegion *, region, regionbase, i) { + if (region->regiontype == RGN_TYPE_TOOL_HEADER) { + region_tool = region; + region_tool_index = i; + } + else if (region->regiontype == RGN_TYPE_HEADER) { + region_head = region; + region_head_index = i; + } + } + if ((region_tool && region_head) && (region_head_index > region_tool_index)) { + BLI_listbase_swaplinks(regionbase, region_tool, region_head); + } + } + } + } } } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index d3cbeb4e1d1..d791c0717be 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1284,8 +1284,8 @@ bool ED_region_is_overlap(int spacetype, int regiontype) RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS, - RGN_TYPE_HEADER, - RGN_TYPE_FOOTER)) { + RGN_TYPE_FOOTER, + RGN_TYPE_TOOL_HEADER)) { return true; } } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 5adcdacd49d..f14a8266cdd 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -128,6 +128,13 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED( simage->custom_grid_subdiv = 10; + /* header */ + region = MEM_callocN(sizeof(ARegion), "header for image"); + + BLI_addtail(&simage->regionbase, region); + region->regiontype = RGN_TYPE_HEADER; + region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + /* tool header */ region = MEM_callocN(sizeof(ARegion), "tool header for image"); @@ -136,13 +143,6 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED( region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER; - /* header */ - region = MEM_callocN(sizeof(ARegion), "header for image"); - - BLI_addtail(&simage->regionbase, region); - region->regiontype = RGN_TYPE_HEADER; - region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; - /* buttons/list view */ region = MEM_callocN(sizeof(ARegion), "buttons for image"); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 99b75f82922..813259159f9 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -109,6 +109,13 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce BLI_rctf_init(&sseq->runtime.last_thumbnail_area, 0.0f, 0.0f, 0.0f, 0.0f); sseq->runtime.last_displayed_thumbnails = NULL; + /* Header. */ + region = MEM_callocN(sizeof(ARegion), "header for sequencer"); + + BLI_addtail(&sseq->regionbase, region); + region->regiontype = RGN_TYPE_HEADER; + region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + /* Tool header. */ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); @@ -117,13 +124,6 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER; - /* Header. */ - region = MEM_callocN(sizeof(ARegion), "header for sequencer"); - - BLI_addtail(&sseq->regionbase, region); - region->regiontype = RGN_TYPE_HEADER; - region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; - /* Buttons/list view. */ region = MEM_callocN(sizeof(ARegion), "buttons for sequencer"); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 4bee9633ece..f68a4d78a00 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -276,6 +276,13 @@ static SpaceLink *view3d_create(const ScrArea *UNUSED(area), const Scene *scene) v3d->camera = scene->camera; } + /* header */ + region = MEM_callocN(sizeof(ARegion), "header for view3d"); + + BLI_addtail(&v3d->regionbase, region); + region->regiontype = RGN_TYPE_HEADER; + region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + /* tool header */ region = MEM_callocN(sizeof(ARegion), "tool header for view3d"); @@ -284,13 +291,6 @@ static SpaceLink *view3d_create(const ScrArea *UNUSED(area), const Scene *scene) region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER; - /* header */ - region = MEM_callocN(sizeof(ARegion), "header for view3d"); - - BLI_addtail(&v3d->regionbase, region); - region->regiontype = RGN_TYPE_HEADER; - region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; - /* tool shelf */ region = MEM_callocN(sizeof(ARegion), "toolshelf for view3d"); From 0c32e3b312212196b2748327713ab3bc31825412 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 29 Sep 2021 12:40:36 +0200 Subject: [PATCH 0348/1500] Fix T91756: String to Curve node produces NaN when size is zero --- source/blender/blenkernel/intern/font.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 1053b727cbc..0e159418724 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -34,6 +34,7 @@ #include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_math_base_safe.h" #include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_string_utf8.h" @@ -794,8 +795,8 @@ static bool vfont_to_curve(Object *ob, bool ok = false; const float font_size = cu->fsize * iter_data->scale_to_fit; const bool word_wrap = iter_data->word_wrap; - const float xof_scale = cu->xof / font_size; - const float yof_scale = cu->yof / font_size; + const float xof_scale = safe_divide(cu->xof, font_size); + const float yof_scale = safe_divide(cu->yof, font_size); int last_line = -1; /* Length of the text disregarding \n breaks. */ float current_line_length = 0.0f; @@ -889,7 +890,7 @@ static bool vfont_to_curve(Object *ob, linedist = cu->linedist; curbox = 0; - textbox_scale(&tb_scale, &cu->tb[curbox], 1.0f / font_size); + textbox_scale(&tb_scale, &cu->tb[curbox], safe_divide(1.0f, font_size)); use_textbox = (tb_scale.w != 0.0f); xof = MARGIN_X_MIN; From 6351c73b758d1b2f4d6b9e85c9bf07074b369be5 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 29 Sep 2021 12:52:48 +0200 Subject: [PATCH 0349/1500] Fix T88954: Rearranging of modifiers for linked objects no longer works. There would be no modifier set in context in drag and drop case, in that case try to get active modifier from active object instead. --- source/blender/editors/object/object_modifier.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index b9942bc563a..125cd65631a 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -1044,6 +1044,10 @@ bool edit_modifier_poll_generic(bContext *C, Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); ModifierData *mod = ptr.data; /* May be NULL. */ + if (mod == NULL && ob != NULL) { + mod = BKE_object_active_modifier(ob); + } + if (!ob || ID_IS_LINKED(ob)) { return false; } @@ -1923,8 +1927,8 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)(RNA_enum_get( - op->ptr, "mode")); + const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)( + RNA_enum_get(op->ptr, "mode")); multiresModifier_subdivide(object, mmd, subdivide_mode); ED_object_iter_other( From 78b9a8c7b993991c22ac2bd1ffbfaf1d896e4431 Mon Sep 17 00:00:00 2001 From: Gaia Clary Date: Tue, 28 Sep 2021 14:07:02 +0200 Subject: [PATCH 0350/1500] Add an option to silence bpy.ops.anim.keyframe_delete_v3d when used in Addons The issues: 1.) When we want to remove keyframes from a range of frames in an action, then we can use bpy.ops.anim.keyframe_delete_v3d to remove the keys frame by frame. However, whenever the operator hits a frame with no keyframes, then it generates an error. While when it hits a frame with keyframes, then it reports the numbner of removed keys. This creates a lot of unnecessary noise in the Blender console. 2.) Furthermore a related issue is that WM_event_add_notifier() is called also when no frames where removed. This seems to significantly slow down the removal of keyframes in a range of frames at least when i use vscode for debugging. A proposal for improvement: This patch adds an attribute 'confirm_success' which controls if the operator reports back what it did (or did not) while executing. Silent mode would then be called like this: bpy.ops.anim.keyframe_delete_v3d(confirm_success=False) Note: confirm_success is True by default so this patchj does not change the behavior of Blender, it only gives the option to scripts. 3.) Personal note: I have chosen the attribute name to be equal as it is used in other related operators. However i rather would rename the attribute to "verbose" (preferred) or "with_confirm". But i let this to be decided by the reviewers. Thanks for your time to review! Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D12629 --- source/blender/editors/animation/keyframing.c | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 8dc4aed9f0e..1ef7ee755ea 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -2107,10 +2107,12 @@ static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *k return OPERATOR_CANCELLED; } + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "confirm_success"); + bool confirm = (prop != NULL && RNA_property_boolean_get(op->ptr, prop)); + if (num_channels > 0) { /* if the appropriate properties have been set, make a note that we've inserted something */ - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "confirm_success"); - if (prop != NULL && RNA_property_boolean_get(op->ptr, prop)) { + if (confirm) { BKE_reportf(op->reports, RPT_INFO, "Successfully removed %d keyframes for keying set '%s'", @@ -2121,7 +2123,7 @@ static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *k /* send notifiers that keyframes have been changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL); } - else { + else if (confirm) { BKE_report(op->reports, RPT_WARNING, "Keying set failed to remove any keyframes"); } @@ -2289,6 +2291,8 @@ static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op) int selected_objects_success_len = 0; int success_multi = 0; + bool confirm = op->flag & OP_IS_INVOKE; + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { ID *id = &ob->id; int success = 0; @@ -2370,20 +2374,20 @@ static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op) /* report success (or failure) */ if (selected_objects_success_len) { - BKE_reportf(op->reports, - RPT_INFO, - "%d object(s) successfully had %d keyframes removed", - selected_objects_success_len, - success_multi); + if (confirm) { + BKE_reportf(op->reports, + RPT_INFO, + "%d object(s) successfully had %d keyframes removed", + selected_objects_success_len, + success_multi); + } + /* send updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); } - else { + else if (confirm) { BKE_reportf( op->reports, RPT_ERROR, "No keyframes removed from %d object(s)", selected_objects_len); } - - /* send updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); - return OPERATOR_FINISHED; } From adaf4f56e1ed2d8ff55be4681838c9705da022ad Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 29 Sep 2021 13:18:44 +0200 Subject: [PATCH 0351/1500] Support loading catalogs in the Current File asset library When the Asset Browser shows the "Current File" asset library, now it also attempts to load an asset catalog definition file from location of the current .blend file. This happens as follows: * First, see if the file is inside of an asset library that is "mounted" in the Preferences. Load the catalogs from there if so. * Otherwise, if the file is saved, load the catalogs from the directory the file is saved in. * If the file is not saved, no catalogs will be loaded. Unit tests are being worked on in D12689. Creating catalogs from the "Current File" asset library still doesn't work, as the asset catalog service doesn't construct an in-memory catalog definition file in that case yet. Differential Revision: https://developer.blender.org/D12675 --- source/blender/blenkernel/BKE_asset_library.h | 37 +++++++++++++++++++ .../blenkernel/intern/asset_catalog.cc | 36 +++++++++--------- .../blenkernel/intern/asset_library.cc | 23 ++++++++++++ source/blender/editors/space_file/filelist.c | 23 +++++++++++- 4 files changed, 98 insertions(+), 21 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_library.h b/source/blender/blenkernel/BKE_asset_library.h index 709b915f9ff..04486a7c132 100644 --- a/source/blender/blenkernel/BKE_asset_library.h +++ b/source/blender/blenkernel/BKE_asset_library.h @@ -20,6 +20,8 @@ #pragma once +struct Main; + #ifdef __cplusplus extern "C" { #endif @@ -31,6 +33,41 @@ typedef struct AssetLibrary AssetLibrary; struct AssetLibrary *BKE_asset_library_load(const char *library_path); void BKE_asset_library_free(struct AssetLibrary *asset_library); +/** + * Try to find an appropriate location for an asset library root from a file or directory path. + * Does not check if \a input_path exists. + * + * The design is made to find an appropriate asset library path from a .blend file path, but + * technically works with any file or directory as \a input_path. + * Design is: + * * If \a input_path lies within a known asset library path (i.e. an asset library registered in + * the Preferences), return the asset library path. + * * Otherwise, if \a input_path has a parent path, return the parent path (e.g. to use the + * directory a .blend file is in as asset library root). + * * If \a input_path is empty or doesn't have a parent path (e.g. because a .blend wasn't saved + * yet), there is no suitable path. The caller has to decide how to handle this case. + * + * \param r_library_path: The returned asset library path with a trailing slash, or an empty string + * if no suitable path is found. Assumed to be a buffer of at least + * #FILE_MAXDIR bytes. + * + * \return True if the function could find a valid, that is, a non-empty path to return in \a + * r_library_path. + */ +bool BKE_asset_library_find_suitable_root_path_from_path( + const char *input_path, char r_library_path[768 /* FILE_MAXDIR */]); +/** + * Uses the current location on disk of the file represented by \a bmain as input to + * #BKE_asset_library_find_suitable_root_path_from_path(). Refer to it for a design + * description. + * + * \return True if the function could find a valid, that is, a non-empty path to return in \a + * r_library_path. If \a bmain wasn't saved into a file yet, the return value will be + * false. + */ +bool BKE_asset_library_find_suitable_root_path_from_main( + const struct Main *bmain, char r_library_path[768 /* FILE_MAXDIR */]); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index d54e3b93dda..b00f4305aa6 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -19,6 +19,7 @@ */ #include "BKE_asset_catalog.hh" +#include "BKE_asset_library.h" #include "BKE_preferences.h" #include "BLI_fileops.h" @@ -299,6 +300,10 @@ bool AssetCatalogService::write_to_disk_on_blendfile_save(const CatalogFilePath CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing( const CatalogFilePath &blend_file_path) { + BLI_assert_msg(!blend_file_path.empty(), + "A non-empty .blend file path is required to be able to determine where the " + "catalog definition file should be put"); + /* Determine the default CDF path in the same directory of the blend file. */ char blend_dir_path[PATH_MAX]; BLI_split_dir_part(blend_file_path.c_str(), blend_dir_path, sizeof(blend_dir_path)); @@ -311,26 +316,19 @@ CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing( return cdf_path_next_to_blend; } - const bUserAssetLibrary *asset_lib_pref = BKE_preferences_asset_library_containing_path( - &U, blend_file_path.c_str()); - if (asset_lib_pref) { - /* - The directory containing the blend file is part of an asset library, as per - * the user's preferences? - * -> Merge with & write to ${ASSET_LIBRARY_ROOT}/blender_assets.cats.txt */ + /* - There's no definition file next to the .blend file. + * -> Ask the asset library API for an appropriate location. */ + char suitable_root_path[PATH_MAX]; + BKE_asset_library_find_suitable_root_path_from_path(blend_file_path.c_str(), + suitable_root_path); + char asset_lib_cdf_path[PATH_MAX]; + BLI_path_join(asset_lib_cdf_path, + sizeof(asset_lib_cdf_path), + suitable_root_path, + DEFAULT_CATALOG_FILENAME.c_str(), + NULL); - char asset_lib_cdf_path[PATH_MAX]; - BLI_path_join(asset_lib_cdf_path, - sizeof(asset_lib_cdf_path), - asset_lib_pref->path, - DEFAULT_CATALOG_FILENAME.c_str(), - NULL); - - return asset_lib_cdf_path; - } - - /* - Otherwise - * -> Create a new file blender_assets.cats.txt next to the blend file. */ - return cdf_path_next_to_blend; + return asset_lib_cdf_path; } std::unique_ptr AssetCatalogService::construct_cdf_in_memory( diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index 8d38f2106c1..1086efe45fd 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -21,6 +21,11 @@ #include "BKE_asset_library.hh" #include "BKE_callbacks.h" #include "BKE_main.h" +#include "BKE_preferences.h" + +#include "BLI_path_util.h" + +#include "DNA_userdef_types.h" #include "MEM_guardedalloc.h" @@ -45,6 +50,24 @@ void BKE_asset_library_free(struct AssetLibrary *asset_library) delete lib; } +bool BKE_asset_library_find_suitable_root_path_from_path(const char *input_path, + char *r_library_path) +{ + if (bUserAssetLibrary *preferences_lib = BKE_preferences_asset_library_containing_path( + &U, input_path)) { + BLI_strncpy(r_library_path, preferences_lib->path, FILE_MAXDIR); + return true; + } + + BLI_split_dir_part(input_path, r_library_path, FILE_MAXDIR); + return r_library_path[0] != '\0'; +} + +bool BKE_asset_library_find_suitable_root_path_from_main(const Main *bmain, char *r_library_path) +{ + return BKE_asset_library_find_suitable_root_path_from_path(bmain->name, r_library_path); +} + namespace blender::bke { void AssetLibrary::load(StringRefNull library_root_directory) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 194e577e19e..60fe5364aba 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -3398,15 +3398,34 @@ static void filelist_readjob_lib(FileListReadJob *job_params, filelist_readjob_do(true, job_params, stop, do_update, progress); } +static void filelist_asset_library_path(const FileListReadJob *job_params, + char r_library_root_path[FILE_MAX]) +{ + if (job_params->filelist->type == FILE_MAIN_ASSET) { + /* For the "Current File" library (#FILE_MAIN_ASSET) we get the asset library root path based + * on main. */ + BKE_asset_library_find_suitable_root_path_from_main(job_params->current_main, + r_library_root_path); + } + else { + BLI_strncpy(r_library_root_path, job_params->tmp_filelist->filelist.root, FILE_MAX); + } +} + +/** + * Load asset library data, which currently means loading the asset catalogs for the library. + */ static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params, short *do_update) { FileList *tmp_filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ - /* Check whether assets catalogs need to be loaded. */ if (job_params->filelist->asset_library_ref != NULL) { + char library_root_path[FILE_MAX]; + filelist_asset_library_path(job_params, library_root_path); + /* Load asset catalogs, into the temp filelist for thread-safety. * #filelist_readjob_endjob() will move it into the real filelist. */ - tmp_filelist->asset_library = BKE_asset_library_load(tmp_filelist->filelist.root); + tmp_filelist->asset_library = BKE_asset_library_load(library_root_path); *do_update = true; } } From 731325a0223ed50179da689b8d80e3c2313a80a6 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 28 Sep 2021 16:18:26 +0200 Subject: [PATCH 0352/1500] Cycles: Make sure GPU transfer is finished prior display update Noticed while looking into flickering issues in viewport. Doesn't seem to solve the flicker issue for me, but is something what is supposed to be happening anyway. Differential Revision: https://developer.blender.org/D12673 --- intern/cycles/integrator/path_trace_work_gpu.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 135466becc6..450e8aaac04 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -738,7 +738,8 @@ void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display, get_render_tile_film_pixels(destination, pass_mode, num_samples); - gpu_display_rgba_half_.copy_from_device(); + queue_->copy_from_device(gpu_display_rgba_half_); + queue_->synchronize(); gpu_display->copy_pixels_to_texture( gpu_display_rgba_half_.data(), texture_x, texture_y, width, height); From ffb9577ac9a4c79483941389a052284b64930c8e Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 29 Sep 2021 12:46:32 +0200 Subject: [PATCH 0353/1500] Cycles: Ensure finite displacement and background evaluation Avoids possible numerical issues in the path tracing kernel, which is most important for displacement as non-finite values in BVH can lead to infinite node recursion during traversal. Differential Revision: https://developer.blender.org/D12690 --- intern/cycles/kernel/kernel_bake.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index e025bcd6674..abb1ba455e6 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -42,6 +42,16 @@ ccl_device void kernel_displace_evaluate(const KernelGlobals *kg, object_inverse_dir_transform(kg, &sd, &D); +#ifdef __KERNEL_DEBUG_NAN__ + if (!isfinite3_safe(D)) { + kernel_assert(!"Cycles displacement with non-finite value detected"); + } +#endif + + /* Ensure finite displacement, preventing BVH from becoming degenerate and avoiding possible + * traversal issues caused by non-finite math. */ + D = ensure_finite3(D); + /* Write output. */ output[offset] += make_float4(D.x, D.y, D.z, 0.0f); } @@ -66,7 +76,16 @@ ccl_device void kernel_background_evaluate(const KernelGlobals *kg, const int path_flag = PATH_RAY_EMISSION; shader_eval_surface( INTEGRATOR_STATE_PASS_NULL, &sd, NULL, path_flag); - const float3 color = shader_background_eval(&sd); + float3 color = shader_background_eval(&sd); + +#ifdef __KERNEL_DEBUG_NAN__ + if (!isfinite3_safe(color)) { + kernel_assert(!"Cycles background with non-finite value detected"); + } +#endif + + /* Ensure finite color, avoiding possible numerical instabilities in the path tracing kernels. */ + color = ensure_finite3(color); /* Write output. */ output[offset] += make_float4(color.x, color.y, color.z, 0.0f); From 5cebcb415e76aaff74bc03c66414aa93b5c90e70 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 29 Sep 2021 14:29:32 +0200 Subject: [PATCH 0354/1500] VSE: Add color tags to strips This patch adds color tags to VSE strips, an overlay option to toggle the colors on and off, a section in the theme settings to define the 9 possible colors and two ways of changing the color tag through the UI. You can change the color through the right-click context menu, or in the strip side panel next to the strip name. Color tags are defined in user preferences and they can be disabled in overlay settings. Reviewed By: campbellbarton, ISS Differential Revision: https://developer.blender.org/D12405 --- .../datafiles/userdef/userdef_default_theme.c | 29 +++++ .../scripts/startup/bl_ui/space_sequencer.py | 58 ++++++++- .../scripts/startup/bl_ui/space_userpref.py | 20 +++ .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_300.c | 51 ++++++-- .../blenloader/intern/versioning_defaults.c | 3 +- .../blenloader/intern/versioning_userdef.c | 6 + source/blender/editors/include/UI_icons.h | 10 ++ .../editors/interface/interface_icons.c | 40 ++++++ .../editors/space_sequencer/sequencer_draw.c | 117 +++++++++++------- .../editors/space_sequencer/sequencer_edit.c | 35 ++++++ .../space_sequencer/sequencer_intern.h | 7 +- .../editors/space_sequencer/sequencer_ops.c | 2 + .../editors/space_sequencer/space_sequencer.c | 2 +- source/blender/makesdna/DNA_sequence_types.h | 20 +++ source/blender/makesdna/DNA_space_types.h | 1 + source/blender/makesdna/DNA_userdef_types.h | 8 +- source/blender/makesrna/RNA_enum_items.h | 1 + .../blender/makesrna/intern/rna_sequencer.c | 34 +++++ source/blender/makesrna/intern/rna_space.c | 6 + source/blender/makesrna/intern/rna_userdef.c | 24 ++++ source/blender/sequencer/intern/sequencer.c | 2 + 22 files changed, 417 insertions(+), 61 deletions(-) diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 05c5a625215..6753dc8563e 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -1175,6 +1175,35 @@ const bTheme U_theme_default = { .color = RGBA(0x7a5441ff), }, }, + .strip_color = { + { + .color = RGBA(0xe2605bff), + }, + { + .color = RGBA(0xf1a355ff), + }, + { + .color = RGBA(0xf1dc55ff), + }, + { + .color = RGBA(0x7bcc7bff), + }, + { + .color = RGBA(0x5db6eaff), + }, + { + .color = RGBA(0x8d59daff), + }, + { + .color = RGBA(0xc673b8ff), + }, + { + .color = RGBA(0x7a5441ff), + }, + { + .color = RGBA(0x5f5f5fff), + }, + }, }; /* clang-format on */ diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 3622154a178..7b102604587 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -241,6 +241,7 @@ class SEQUENCER_PT_sequencer_overlay(Panel): layout.prop(overlay_settings, "show_strip_name", text="Name") layout.prop(overlay_settings, "show_strip_source", text="Source") layout.prop(overlay_settings, "show_strip_duration", text="Duration") + layout.prop(overlay_settings, "show_strip_tag_color", text="Color Tags") layout.separator() @@ -868,6 +869,9 @@ class SEQUENCER_MT_strip(Menu): layout.operator("sequencer.meta_make") layout.operator("sequencer.meta_toggle", text="Toggle Meta") + layout.separator() + layout.menu("SEQUENCER_MT_color_tag_picker") + layout.separator() layout.menu("SEQUENCER_MT_strip_lock_mute") @@ -964,6 +968,9 @@ class SEQUENCER_MT_context_menu(Menu): layout.operator("sequencer.meta_make") layout.operator("sequencer.meta_toggle", text="Toggle Meta") + layout.separator() + layout.menu("SEQUENCER_MT_color_tag_picker") + layout.separator() layout.menu("SEQUENCER_MT_strip_lock_mute") @@ -996,6 +1003,41 @@ class SequencerButtonsPanel_Output: return cls.has_preview(context) +class SEQUENCER_PT_color_tag_picker(Panel): + bl_label = "Color Tag" + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'UI' + bl_category = "Strip" + bl_options = {'HIDE_HEADER', 'INSTANCED'} + + @classmethod + def poll(cls, context): + return context.active_sequence_strip is not None + + def draw(self, context): + layout = self.layout + + row = layout.row(align=True) + row.operator("sequencer.strip_color_tag_set", icon="X").color = 'NONE' + for i in range(1, 10): + icon = 'SEQUENCE_COLOR_%02d' % i + row.operator("sequencer.strip_color_tag_set", icon=icon).color = 'COLOR_%02d' % i + + +class SEQUENCER_MT_color_tag_picker(Menu): + bl_label = "Set Color Tag" + + @classmethod + def poll(cls, context): + return context.active_sequence_strip is not None + + def draw(self, context): + layout = self.layout + + row = layout.row(align=True) + row.operator_enum("sequencer.strip_color_tag_set", "color", icon_only=True) + + class SEQUENCER_PT_strip(SequencerButtonsPanel, Panel): bl_label = "" bl_options = {'HIDE_HEADER'} @@ -1039,9 +1081,20 @@ class SEQUENCER_PT_strip(SequencerButtonsPanel, Panel): else: icon_header = 'SEQ_SEQUENCER' - row = layout.row() + row = layout.row(align=True) + row.use_property_decorate = False row.label(text="", icon=icon_header) + row.separator() row.prop(strip, "name", text="") + + sub = row.row(align=True) + if strip.color_tag == 'NONE': + sub.popover(panel="SEQUENCER_PT_color_tag_picker", text="", icon='COLOR') + else: + icon = 'SEQUENCE_' + strip.color_tag + sub.popover(panel="SEQUENCER_PT_color_tag_picker", text="", icon=icon) + + row.separator() row.prop(strip, "mute", toggle=True, icon_only=True, emboss=False) @@ -2327,8 +2380,11 @@ classes = ( SEQUENCER_MT_strip_transform, SEQUENCER_MT_strip_input, SEQUENCER_MT_strip_lock_mute, + SEQUENCER_MT_color_tag_picker, SEQUENCER_MT_context_menu, + SEQUENCER_PT_color_tag_picker, + SEQUENCER_PT_active_tool, SEQUENCER_PT_strip, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 6efac235359..7a8b6d42cad 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1073,6 +1073,25 @@ class USERPREF_PT_theme_collection_colors(ThemePanel, CenterAlignMixIn, Panel): flow.prop(ui, "color", text=iface_("Color %d") % i, translate=False) +class USERPREF_PT_theme_strip_colors(ThemePanel, CenterAlignMixIn, Panel): + bl_label = "Strip Colors" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, _context): + layout = self.layout + + layout.label(icon='SEQ_STRIP_DUPLICATE') + + def draw_centered(self, context, layout): + theme = context.preferences.themes[0] + + layout.use_property_split = True + + flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + for i, ui in enumerate(theme.strip_color, 1): + flow.prop(ui, "color", text=iface_("Color %d") % i, translate=False) + + # Base class for dynamically defined theme-space panels. # This is not registered. class PreferenceThemeSpacePanel: @@ -2348,6 +2367,7 @@ classes = ( USERPREF_PT_theme_text_style, USERPREF_PT_theme_bone_color_sets, USERPREF_PT_theme_collection_colors, + USERPREF_PT_theme_strip_colors, USERPREF_PT_file_paths_data, USERPREF_PT_file_paths_render, diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index fc98abf619d..b04bbdfb187 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 30 +#define BLENDER_FILE_SUBVERSION 31 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 1c4a0690886..8b8b7218c0e 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -449,6 +449,12 @@ static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const Lis #undef SEQ_SPEED_COMPRESS_IPO_Y } +static bool do_versions_sequencer_color_tags(Sequence *seq, void *UNUSED(user_data)) +{ + seq->color_tag = SEQUENCE_COLOR_NONE; + return true; +} + static bNodeLink *find_connected_link(bNodeTree *ntree, bNodeSocket *in_socket) { LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { @@ -1614,18 +1620,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 300, 31)) { /* Swap header with the tool header so the regular header is always on the edge. */ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { @@ -1650,5 +1645,37 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Set strip color tags to SEQUENCE_COLOR_NONE. */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != NULL) { + SEQ_for_each_callback(&scene->ed->seqbase, do_versions_sequencer_color_tags, NULL); + } + } + + /* Show vse color tags by default. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG; + } + } + } + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 152ef79a38f..c383c1cc4e5 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -160,7 +160,8 @@ static void blo_update_defaults_screen(bScreen *screen, seq->flag |= SEQ_SHOW_MARKERS | SEQ_ZOOM_TO_FIT | SEQ_USE_PROXIES | SEQ_SHOW_OVERLAY; seq->render_size = SEQ_RENDER_SIZE_PROXY_100; seq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_NAME | - SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID; + SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID | + SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG; seq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; } else if (area->spacetype == SPACE_TEXT) { diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 54a0f1beec1..cd365b6be78 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -295,6 +295,12 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) FROM_DEFAULT_V4_UCHAR(space_node.wire); } + if (!USER_VERSION_ATLEAST(300, 31)) { + for (int i = 0; i < SEQUENCE_COLOR_TOT; ++i) { + FROM_DEFAULT_V4_UCHAR(strip_color[i].color); + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index ddd9ca4a98c..8a7df5b54ff 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -989,6 +989,16 @@ DEF_ICON_VECTOR(COLLECTION_COLOR_06) DEF_ICON_VECTOR(COLLECTION_COLOR_07) DEF_ICON_VECTOR(COLLECTION_COLOR_08) +DEF_ICON_VECTOR(SEQUENCE_COLOR_01) +DEF_ICON_VECTOR(SEQUENCE_COLOR_02) +DEF_ICON_VECTOR(SEQUENCE_COLOR_03) +DEF_ICON_VECTOR(SEQUENCE_COLOR_04) +DEF_ICON_VECTOR(SEQUENCE_COLOR_05) +DEF_ICON_VECTOR(SEQUENCE_COLOR_06) +DEF_ICON_VECTOR(SEQUENCE_COLOR_07) +DEF_ICON_VECTOR(SEQUENCE_COLOR_08) +DEF_ICON_VECTOR(SEQUENCE_COLOR_09) + /* Events. */ DEF_ICON_COLOR(EVENT_A) DEF_ICON_COLOR(EVENT_B) diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 7f1a8ee99e0..c20129b4184 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -47,6 +47,7 @@ #include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "RNA_access.h" @@ -480,6 +481,35 @@ DEF_ICON_COLLECTION_COLOR_DRAW(08, COLLECTION_COLOR_08); # undef DEF_ICON_COLLECTION_COLOR_DRAW +static void vicon_strip_color_draw( + short color_tag, int x, int y, int w, int UNUSED(h), float UNUSED(alpha)) +{ + bTheme *btheme = UI_GetTheme(); + const ThemeStripColor *strip_color = &btheme->strip_color[color_tag]; + + const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w; + + UI_icon_draw_ex(x, y, ICON_SNAP_FACE, aspect, 1.0f, 0.0f, strip_color->color, true); +} + +# define DEF_ICON_STRIP_COLOR_DRAW(index, color) \ + static void vicon_strip_color_draw_##index(int x, int y, int w, int h, float alpha) \ + { \ + vicon_strip_color_draw(color, x, y, w, h, alpha); \ + } + +DEF_ICON_STRIP_COLOR_DRAW(01, SEQUENCE_COLOR_01); +DEF_ICON_STRIP_COLOR_DRAW(02, SEQUENCE_COLOR_02); +DEF_ICON_STRIP_COLOR_DRAW(03, SEQUENCE_COLOR_03); +DEF_ICON_STRIP_COLOR_DRAW(04, SEQUENCE_COLOR_04); +DEF_ICON_STRIP_COLOR_DRAW(05, SEQUENCE_COLOR_05); +DEF_ICON_STRIP_COLOR_DRAW(06, SEQUENCE_COLOR_06); +DEF_ICON_STRIP_COLOR_DRAW(07, SEQUENCE_COLOR_07); +DEF_ICON_STRIP_COLOR_DRAW(08, SEQUENCE_COLOR_08); +DEF_ICON_STRIP_COLOR_DRAW(09, SEQUENCE_COLOR_09); + +# undef DEF_ICON_STRIP_COLOR_DRAW + /* Dynamically render icon instead of rendering a plain color to a texture/buffer * This is not strictly a "vicon", as it needs access to icon->obj to get the color info, * but it works in a very similar way. @@ -995,6 +1025,16 @@ static void init_internal_icons(void) def_internal_vicon(ICON_COLLECTION_COLOR_06, vicon_collection_color_draw_06); def_internal_vicon(ICON_COLLECTION_COLOR_07, vicon_collection_color_draw_07); def_internal_vicon(ICON_COLLECTION_COLOR_08, vicon_collection_color_draw_08); + + def_internal_vicon(ICON_SEQUENCE_COLOR_01, vicon_strip_color_draw_01); + def_internal_vicon(ICON_SEQUENCE_COLOR_02, vicon_strip_color_draw_02); + def_internal_vicon(ICON_SEQUENCE_COLOR_03, vicon_strip_color_draw_03); + def_internal_vicon(ICON_SEQUENCE_COLOR_04, vicon_strip_color_draw_04); + def_internal_vicon(ICON_SEQUENCE_COLOR_05, vicon_strip_color_draw_05); + def_internal_vicon(ICON_SEQUENCE_COLOR_06, vicon_strip_color_draw_06); + def_internal_vicon(ICON_SEQUENCE_COLOR_07, vicon_strip_color_draw_07); + def_internal_vicon(ICON_SEQUENCE_COLOR_08, vicon_strip_color_draw_08); + def_internal_vicon(ICON_SEQUENCE_COLOR_09, vicon_strip_color_draw_09); } static void init_iconfile_list(struct ListBase *list) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 04377f02a7f..ae392980069 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -107,36 +107,53 @@ static Sequence *special_seq_update = NULL; -void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) +void color3ubv_from_seq(const Scene *curscene, + const Sequence *seq, + const bool show_strip_color_tag, + uchar r_col[3]) { + if (show_strip_color_tag && (uint)seq->color_tag < SEQUENCE_COLOR_TOT && + seq->color_tag != SEQUENCE_COLOR_NONE) { + bTheme *btheme = UI_GetTheme(); + const ThemeStripColor *strip_color = &btheme->strip_color[seq->color_tag]; + copy_v3_v3_uchar(r_col, strip_color->color); + return; + } + uchar blendcol[3]; + /* Sometimes the active theme is not the sequencer theme, e.g. when an operator invokes the file + * browser. This makes sure we get the right color values for the theme. */ + struct bThemeState theme_state; + UI_Theme_Store(&theme_state); + UI_SetTheme(SPACE_SEQ, RGN_TYPE_WINDOW); + switch (seq->type) { case SEQ_TYPE_IMAGE: - UI_GetThemeColor3ubv(TH_SEQ_IMAGE, col); + UI_GetThemeColor3ubv(TH_SEQ_IMAGE, r_col); break; case SEQ_TYPE_META: - UI_GetThemeColor3ubv(TH_SEQ_META, col); + UI_GetThemeColor3ubv(TH_SEQ_META, r_col); break; case SEQ_TYPE_MOVIE: - UI_GetThemeColor3ubv(TH_SEQ_MOVIE, col); + UI_GetThemeColor3ubv(TH_SEQ_MOVIE, r_col); break; case SEQ_TYPE_MOVIECLIP: - UI_GetThemeColor3ubv(TH_SEQ_MOVIECLIP, col); + UI_GetThemeColor3ubv(TH_SEQ_MOVIECLIP, r_col); break; case SEQ_TYPE_MASK: - UI_GetThemeColor3ubv(TH_SEQ_MASK, col); + UI_GetThemeColor3ubv(TH_SEQ_MASK, r_col); break; case SEQ_TYPE_SCENE: - UI_GetThemeColor3ubv(TH_SEQ_SCENE, col); + UI_GetThemeColor3ubv(TH_SEQ_SCENE, r_col); if (seq->scene == curscene) { - UI_GetColorPtrShade3ubv(col, col, 20); + UI_GetColorPtrShade3ubv(r_col, r_col, 20); } break; @@ -144,9 +161,9 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) case SEQ_TYPE_CROSS: case SEQ_TYPE_GAMCROSS: case SEQ_TYPE_WIPE: - col[0] = 130; - col[1] = 130; - col[2] = 130; + r_col[0] = 130; + r_col[1] = 130; + r_col[2] = 130; break; /* Effects. */ @@ -163,72 +180,74 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) case SEQ_TYPE_ADJUSTMENT: case SEQ_TYPE_GAUSSIAN_BLUR: case SEQ_TYPE_COLORMIX: - UI_GetThemeColor3ubv(TH_SEQ_EFFECT, col); + UI_GetThemeColor3ubv(TH_SEQ_EFFECT, r_col); /* Slightly offset hue to distinguish different effects. */ if (seq->type == SEQ_TYPE_ADD) { - rgb_byte_set_hue_float_offset(col, 0.03); + rgb_byte_set_hue_float_offset(r_col, 0.03); } else if (seq->type == SEQ_TYPE_SUB) { - rgb_byte_set_hue_float_offset(col, 0.06); + rgb_byte_set_hue_float_offset(r_col, 0.06); } else if (seq->type == SEQ_TYPE_MUL) { - rgb_byte_set_hue_float_offset(col, 0.13); + rgb_byte_set_hue_float_offset(r_col, 0.13); } else if (seq->type == SEQ_TYPE_ALPHAOVER) { - rgb_byte_set_hue_float_offset(col, 0.16); + rgb_byte_set_hue_float_offset(r_col, 0.16); } else if (seq->type == SEQ_TYPE_ALPHAUNDER) { - rgb_byte_set_hue_float_offset(col, 0.23); + rgb_byte_set_hue_float_offset(r_col, 0.23); } else if (seq->type == SEQ_TYPE_OVERDROP) { - rgb_byte_set_hue_float_offset(col, 0.26); + rgb_byte_set_hue_float_offset(r_col, 0.26); } else if (seq->type == SEQ_TYPE_COLORMIX) { - rgb_byte_set_hue_float_offset(col, 0.33); + rgb_byte_set_hue_float_offset(r_col, 0.33); } else if (seq->type == SEQ_TYPE_GAUSSIAN_BLUR) { - rgb_byte_set_hue_float_offset(col, 0.43); + rgb_byte_set_hue_float_offset(r_col, 0.43); } else if (seq->type == SEQ_TYPE_GLOW) { - rgb_byte_set_hue_float_offset(col, 0.46); + rgb_byte_set_hue_float_offset(r_col, 0.46); } else if (seq->type == SEQ_TYPE_ADJUSTMENT) { - rgb_byte_set_hue_float_offset(col, 0.55); + rgb_byte_set_hue_float_offset(r_col, 0.55); } else if (seq->type == SEQ_TYPE_SPEED) { - rgb_byte_set_hue_float_offset(col, 0.65); + rgb_byte_set_hue_float_offset(r_col, 0.65); } else if (seq->type == SEQ_TYPE_TRANSFORM) { - rgb_byte_set_hue_float_offset(col, 0.75); + rgb_byte_set_hue_float_offset(r_col, 0.75); } else if (seq->type == SEQ_TYPE_MULTICAM) { - rgb_byte_set_hue_float_offset(col, 0.85); + rgb_byte_set_hue_float_offset(r_col, 0.85); } break; case SEQ_TYPE_COLOR: - UI_GetThemeColor3ubv(TH_SEQ_COLOR, col); + UI_GetThemeColor3ubv(TH_SEQ_COLOR, r_col); break; case SEQ_TYPE_SOUND_RAM: - UI_GetThemeColor3ubv(TH_SEQ_AUDIO, col); + UI_GetThemeColor3ubv(TH_SEQ_AUDIO, r_col); blendcol[0] = blendcol[1] = blendcol[2] = 128; if (seq->flag & SEQ_MUTE) { - UI_GetColorPtrBlendShade3ubv(col, blendcol, col, 0.5, 20); + UI_GetColorPtrBlendShade3ubv(r_col, blendcol, r_col, 0.5, 20); } break; case SEQ_TYPE_TEXT: - UI_GetThemeColor3ubv(TH_SEQ_TEXT, col); + UI_GetThemeColor3ubv(TH_SEQ_TEXT, r_col); break; default: - col[0] = 10; - col[1] = 255; - col[2] = 40; + r_col[0] = 10; + r_col[1] = 255; + r_col[2] = 40; break; } + + UI_Theme_Restore(&theme_state); } typedef struct WaveVizData { @@ -558,7 +577,13 @@ static void draw_seq_waveform_overlay(View2D *v2d, } } -static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, float x2, float y2) +static void drawmeta_contents(Scene *scene, + Sequence *seqm, + float x1, + float y1, + float x2, + float y2, + const bool show_strip_color_tag) { Sequence *seq; uchar col[4]; @@ -614,7 +639,7 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, rgb_float_to_uchar(col, colvars->col); } else { - color3ubv_from_seq(scene, seq, col); + color3ubv_from_seq(scene, seq, show_strip_color_tag, col); } if ((seqm->flag & SEQ_MUTE) || (seq->flag & SEQ_MUTE)) { @@ -953,7 +978,8 @@ static void draw_seq_text_overlay(View2D *v2d, UI_view2d_text_cache_add_rectf(v2d, &rect, overlay_string, overlay_string_len, col); } -static void draw_sequence_extensions_overlay(Scene *scene, Sequence *seq, uint pos, float pixely) +static void draw_sequence_extensions_overlay( + Scene *scene, Sequence *seq, uint pos, float pixely, const bool show_strip_color_tag) { float x1, x2, y1, y2; uchar col[4], blend_col[3]; @@ -966,7 +992,7 @@ static void draw_sequence_extensions_overlay(Scene *scene, Sequence *seq, uint p GPU_blend(GPU_BLEND_ALPHA); - color3ubv_from_seq(scene, seq, col); + color3ubv_from_seq(scene, seq, show_strip_color_tag, col); if (seq->flag & SELECT) { UI_GetColorPtrShade3ubv(col, col, 50); } @@ -1036,7 +1062,8 @@ static void draw_seq_background(Scene *scene, float x2, float y1, float y2, - bool is_single_image) + bool is_single_image, + bool show_strip_color_tag) { uchar col[4]; GPU_blend(GPU_BLEND_ALPHA); @@ -1049,11 +1076,11 @@ static void draw_seq_background(Scene *scene, rgb_float_to_uchar(col, colvars->col); } else { - color3ubv_from_seq(scene, seq1, col); + color3ubv_from_seq(scene, seq1, show_strip_color_tag, col); } } else { - color3ubv_from_seq(scene, seq, col); + color3ubv_from_seq(scene, seq, show_strip_color_tag, col); } /* Draw muted strips semi-transparent. */ @@ -1108,7 +1135,7 @@ static void draw_seq_background(Scene *scene, rgb_float_to_uchar(col, colvars->col); } else { - color3ubv_from_seq(scene, seq2, col); + color3ubv_from_seq(scene, seq2, show_strip_color_tag, col); /* If the transition inputs are of the same type, draw the right side slightly darker. */ if (seq1->type == seq2->type) { UI_GetColorPtrShade3ubv(col, col, -15); @@ -1822,6 +1849,10 @@ static void draw_seq_strip(const bContext *C, /* Check if we are doing "solo preview". */ bool is_single_image = (char)SEQ_transform_single_image_check(seq); + /* Use the seq->color_tag to display the tag color. */ + const bool show_strip_color_tag = (sseq->timeline_overlay.flag & + SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG); + /* Draw strip body. */ x1 = (seq->startstill) ? seq->start : seq->startdisp; y1 = seq->machine + SEQ_STRIP_OFSBOTTOM; @@ -1852,7 +1883,7 @@ static void draw_seq_strip(const bContext *C, uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - draw_seq_background(scene, seq, pos, x1, x2, y1, y2, is_single_image); + draw_seq_background(scene, seq, pos, x1, x2, y1, y2, is_single_image, show_strip_color_tag); /* Draw a color band inside color strip. */ if (seq->type == SEQ_TYPE_COLOR && y_threshold) { @@ -1864,7 +1895,7 @@ static void draw_seq_strip(const bContext *C, if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) { if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_OFFSETS) || (seq == special_seq_update)) { - draw_sequence_extensions_overlay(scene, seq, pos, pixely); + draw_sequence_extensions_overlay(scene, seq, pos, pixely, show_strip_color_tag); } } } @@ -1875,7 +1906,7 @@ static void draw_seq_strip(const bContext *C, if ((seq->type == SEQ_TYPE_META) || ((seq->type == SEQ_TYPE_SCENE) && (seq->flag & SEQ_SCENE_STRIPS))) { - drawmeta_contents(scene, seq, x1, y1, x2, y2); + drawmeta_contents(scene, seq, x1, y1, x2, y2, show_strip_color_tag); } if ((sseq->flag & SEQ_SHOW_OVERLAY) && diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 9f21fc0676c..6daa8d690e5 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -63,6 +63,7 @@ #include "WM_types.h" #include "RNA_define.h" +#include "RNA_enum_types.h" /* For menu, popup, icons, etc. */ #include "ED_numinput.h" @@ -3322,4 +3323,38 @@ void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot) "Scale fit fit_method"); } +static int sequencer_strip_color_tag_set_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + const Editing *ed = SEQ_editing_get(scene); + const short color_tag = RNA_enum_get(op->ptr, "color"); + + LISTBASE_FOREACH (Sequence *, seq, &ed->seqbase) { + if (seq->flag & SELECT) { + seq->color_tag = color_tag; + } + } + + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_strip_color_tag_set(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Set Color Tag"; + ot->idname = "SEQUENCER_OT_strip_color_tag_set"; + ot->description = "Set a color tag for the selected strips"; + + /* Api callbacks. */ + ot->exec = sequencer_strip_color_tag_set_exec; + ot->poll = sequencer_edit_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum( + ot->srna, "color", rna_enum_strip_color_items, SEQUENCE_COLOR_NONE, "Color Tag", ""); +} + /** \} */ diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 5b5c381509f..202eda85dca 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -51,7 +51,10 @@ void sequencer_draw_preview(const struct bContext *C, int offset, bool draw_overlay, bool draw_backdrop); -void color3ubv_from_seq(struct Scene *curscene, struct Sequence *seq, unsigned char col[3]); +void color3ubv_from_seq(const struct Scene *curscene, + const struct Sequence *seq, + const bool show_strip_color_tag, + uchar r_col[3]); void sequencer_special_update_set(Sequence *seq); float sequence_handle_size_get_clamped(struct Sequence *seq, const float pixelx); @@ -148,6 +151,8 @@ void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot); void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot); void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot); +void SEQUENCER_OT_strip_color_tag_set(struct wmOperatorType *ot); + /* sequencer_select.c */ void SEQUENCER_OT_select_all(struct wmOperatorType *ot); void SEQUENCER_OT_select(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 48e6cfcdcd0..95f7b44264c 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -79,6 +79,8 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_strip_transform_clear); WM_operatortype_append(SEQUENCER_OT_strip_transform_fit); + WM_operatortype_append(SEQUENCER_OT_strip_color_tag_set); + /* sequencer_select.c */ WM_operatortype_append(SEQUENCER_OT_select_all); WM_operatortype_append(SEQUENCER_OT_select); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 813259159f9..ad0ceb82709 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -104,7 +104,7 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce sseq->preview_overlay.flag = SEQ_PREVIEW_SHOW_GPENCIL | SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; sseq->timeline_overlay.flag = SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID | - SEQ_TIMELINE_SHOW_FCURVES; + SEQ_TIMELINE_SHOW_FCURVES | SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG; BLI_rctf_init(&sseq->runtime.last_thumbnail_area, 0.0f, 0.0f, 0.0f, 0.0f); sseq->runtime.last_displayed_thumbnails = NULL; diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 86369ff7684..828702f9aa8 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -232,6 +232,10 @@ typedef struct Sequence { int blend_mode; float blend_opacity; + /* Tag color showed if `SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG` is set. */ + int16_t color_tag; + char _pad4[6]; + /* is sfra needed anymore? - it looks like its only used in one place */ /** Starting frame according to the timeline of the scene. */ int sfra; @@ -727,6 +731,22 @@ enum { SEQ_CACHE_STORE_THUMBNAIL = (1 << 12), }; +/* Sequence->color_tag. */ +typedef enum SequenceColorTag { + SEQUENCE_COLOR_NONE = -1, + SEQUENCE_COLOR_01, + SEQUENCE_COLOR_02, + SEQUENCE_COLOR_03, + SEQUENCE_COLOR_04, + SEQUENCE_COLOR_05, + SEQUENCE_COLOR_06, + SEQUENCE_COLOR_07, + SEQUENCE_COLOR_08, + SEQUENCE_COLOR_09, + + SEQUENCE_COLOR_TOT, +} SequenceColorTag; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index a8f21e597c5..a7fcf2cfb89 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -599,6 +599,7 @@ typedef struct SequencerTimelineOverlay { typedef enum eSpaceSeq_SequencerTimelineOverlay_Flag { SEQ_TIMELINE_SHOW_STRIP_OFFSETS = (1 << 1), SEQ_TIMELINE_SHOW_THUMBNAILS = (1 << 2), + SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG = (1 << 3), /* use Sequence->color_tag */ SEQ_TIMELINE_SHOW_FCURVES = (1 << 5), SEQ_TIMELINE_ALL_WAVEFORMS = (1 << 7), /* draw all waveforms */ SEQ_TIMELINE_NO_WAVEFORMS = (1 << 8), /* draw no waveforms */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 247f67f6b95..291f6de5ba2 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -458,6 +458,10 @@ typedef struct ThemeCollectionColor { unsigned char color[4]; } ThemeCollectionColor; +typedef struct ThemeStripColor { + unsigned char color[4]; +} ThemeStripColor; + /** * A theme. * @@ -500,8 +504,10 @@ typedef struct bTheme { /* See COLLECTION_COLOR_TOT for the number of collection colors. */ ThemeCollectionColor collection_color[8]; + /* See SEQUENCE_COLOR_TOT for the total number of strip colors. */ + ThemeStripColor strip_color[9]; + int active_theme_area; - char _pad0[4]; } bTheme; #define UI_THEMESPACE_START(btheme) \ diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index c8f44262020..03d371be1f7 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -211,6 +211,7 @@ DEF_ENUM(rna_enum_attribute_domain_items) DEF_ENUM(rna_enum_attribute_domain_with_auto_items) DEF_ENUM(rna_enum_collection_color_items) +DEF_ENUM(rna_enum_strip_color_items) DEF_ENUM(rna_enum_subdivision_uv_smooth_items) DEF_ENUM(rna_enum_subdivision_boundary_smooth_items) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 6a03ee03f71..7303f6c920a 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -81,6 +81,20 @@ const EnumPropertyItem rna_enum_sequence_modifier_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_strip_color_items[] = { + {SEQUENCE_COLOR_NONE, "NONE", ICON_X, "None", "Assign no color tag to the collection"}, + {SEQUENCE_COLOR_01, "COLOR_01", ICON_SEQUENCE_COLOR_01, "Color 01", ""}, + {SEQUENCE_COLOR_02, "COLOR_02", ICON_SEQUENCE_COLOR_02, "Color 02", ""}, + {SEQUENCE_COLOR_03, "COLOR_03", ICON_SEQUENCE_COLOR_03, "Color 03", ""}, + {SEQUENCE_COLOR_04, "COLOR_04", ICON_SEQUENCE_COLOR_04, "Color 04", ""}, + {SEQUENCE_COLOR_05, "COLOR_05", ICON_SEQUENCE_COLOR_05, "Color 05", ""}, + {SEQUENCE_COLOR_06, "COLOR_06", ICON_SEQUENCE_COLOR_06, "Color 06", ""}, + {SEQUENCE_COLOR_07, "COLOR_07", ICON_SEQUENCE_COLOR_07, "Color 07", ""}, + {SEQUENCE_COLOR_08, "COLOR_08", ICON_SEQUENCE_COLOR_08, "Color 08", ""}, + {SEQUENCE_COLOR_09, "COLOR_09", ICON_SEQUENCE_COLOR_09, "Color 09", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "BKE_global.h" @@ -1000,6 +1014,18 @@ static void rna_Sequence_opacity_set(PointerRNA *ptr, float value) seq->blend_opacity = value * 100.0f; } +static int rna_Sequence_color_tag_get(PointerRNA *ptr) +{ + Sequence *seq = (Sequence *)(ptr->data); + return seq->color_tag; +} + +static void rna_Sequence_color_tag_set(PointerRNA *ptr, int value) +{ + Sequence *seq = (Sequence *)(ptr->data); + seq->color_tag = value; +} + static bool colbalance_seq_cmp_fn(Sequence *seq, void *arg_pt) { SequenceSearchData *data = arg_pt; @@ -1938,6 +1964,14 @@ static void rna_def_sequence(BlenderRNA *brna) RNA_def_property_update( prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update"); + prop = RNA_def_property(srna, "color_tag", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "color_tag"); + RNA_def_property_enum_funcs( + prop, "rna_Sequence_color_tag_get", "rna_Sequence_color_tag_set", NULL); + RNA_def_property_enum_items(prop, rna_enum_strip_color_items); + RNA_def_property_ui_text(prop, "Strip Color", "Color tag for a strip"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); + /* modifiers */ prop = RNA_def_property(srna, "modifiers", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "SequenceModifier"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index a9219fb11be..a4f79696276 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5500,6 +5500,12 @@ static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_THUMBNAILS); RNA_def_property_ui_text(prop, "Show Thumbnails", "Show strip thumbnails"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_tag_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG); + RNA_def_property_ui_text( + prop, "Show Color Tags", "Display the strip color tags in the sequencer"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } static void rna_def_space_sequencer(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 684c331e14e..ccfc1222d4c 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -3680,6 +3680,23 @@ static void rna_def_userdef_theme_collection_color(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); } +static void rna_def_userdef_theme_strip_color(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ThemeStripColor", NULL); + RNA_def_struct_sdna(srna, "ThemeStripColor"); + RNA_def_struct_clear_flag(srna, STRUCT_UNDO); + RNA_def_struct_ui_text(srna, "Theme Strip Color", "Theme settings for strip colors"); + + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "color"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Color", "Strip Color"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); +} + static void rna_def_userdef_theme_space_clip(BlenderRNA *brna) { StructRNA *srna; @@ -4029,6 +4046,12 @@ static void rna_def_userdef_themes(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "collection_color", ""); RNA_def_property_struct_type(prop, "ThemeCollectionColor"); RNA_def_property_ui_text(prop, "Collection Color", ""); + + prop = RNA_def_property(srna, "strip_color", PROP_COLLECTION, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_collection_sdna(prop, NULL, "strip_color", ""); + RNA_def_property_struct_type(prop, "ThemeStripColor"); + RNA_def_property_ui_text(prop, "Strip Color", ""); } static void rna_def_userdef_addon(BlenderRNA *brna) @@ -4272,6 +4295,7 @@ static void rna_def_userdef_dothemes(BlenderRNA *brna) rna_def_userdef_theme_space_spreadsheet(brna); rna_def_userdef_theme_colorset(brna); rna_def_userdef_theme_collection_color(brna); + rna_def_userdef_theme_strip_color(brna); rna_def_userdef_themes(brna); } diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index c164e7fc2ee..3478c2d4f97 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -144,6 +144,8 @@ Sequence *SEQ_sequence_alloc(ListBase *lb, int timeline_frame, int machine, int seq->strip = seq_strip_alloc(type); seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format"); + seq->color_tag = SEQUENCE_COLOR_NONE; + SEQ_relations_session_uuid_generate(seq); return seq; From ef29bf9023f54667db7a0c2898d12a3bce0873ed Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 29 Sep 2021 15:01:36 +0200 Subject: [PATCH 0355/1500] Assets: Expose option to reuse data-block data when appending MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With 794c2828af60 & f48a4aa0f915 it's possible to reuse possibly expensive, nested data of a data-block when appending. E.g. the texture of a material, or the mesh of an object. Without this it's easy to bloat memory and the file size. Duplicated textures also cause unnecessary shader recompilations. The feature was intended to be the new default behavior for the Asset Browser, but it wasn't actually added to the UI yet. This patch adds a new import type option to the Asset Browser. So from the menu in the header, you can now choose between: * Link * Append * Append (Reuse Data) The latter is the new default. Maniphest Task: https://developer.blender.org/T91741 Differential Revision: https://developer.blender.org/D12647 Reviewed by: Sybren Stüvel, Bastien Montagne --- source/blender/blenloader/intern/versioning_300.c | 5 +++++ source/blender/editors/space_file/filesel.c | 2 +- source/blender/makesdna/DNA_space_types.h | 6 ++++++ source/blender/makesrna/intern/rna_space.c | 8 ++++++++ source/blender/windowmanager/intern/wm_dragdrop.c | 10 ++++++++++ 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 8b8b7218c0e..e65fd3e6754 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1554,6 +1554,11 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) sfile->params->flag &= ~(FILE_PARAMS_FLAG_UNUSED_1 | FILE_PARAMS_FLAG_UNUSED_2 | FILE_PARAMS_FLAG_UNUSED_3 | FILE_PARAMS_FLAG_UNUSED_4); } + + /* New default import type: Append with reuse. */ + if (sfile->asset_params) { + sfile->asset_params->import_type = FILE_ASSET_IMPORT_APPEND_REUSE; + } break; } default: diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index a741f2582ee..2ca08a3105c 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -120,7 +120,7 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) asset_params->base_params.details_flags = U_default.file_space_data.details_flags; asset_params->asset_library_ref.type = ASSET_LIBRARY_LOCAL; asset_params->asset_library_ref.custom_library_index = -1; - asset_params->import_type = FILE_ASSET_IMPORT_APPEND; + asset_params->import_type = FILE_ASSET_IMPORT_APPEND_REUSE; } FileSelectParams *base_params = &asset_params->base_params; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index a7fcf2cfb89..2f3f52a6b82 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -812,8 +812,14 @@ typedef struct FileAssetSelectParams { } FileAssetSelectParams; typedef enum eFileAssetImportType { + /** Regular data-block linking. */ FILE_ASSET_IMPORT_LINK = 0, + /** Regular data-block appending (basically linking + "Make Local"). */ FILE_ASSET_IMPORT_APPEND = 1, + /** Append data-block with the #BLO_LIBLINK_APPEND_LOCAL_ID_REUSE flag enabled. Some typically + * heavy data dependencies (e.g. the image data-blocks of a material, the mesh of an object) may + * be reused from an earlier append. */ + FILE_ASSET_IMPORT_APPEND_REUSE = 2, } eFileAssetImportType; /** diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index a4f79696276..9e06533d41b 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -6643,6 +6643,14 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) 0, "Append", "Import the assets as copied data-block, with no link to the original asset data-block"}, + {FILE_ASSET_IMPORT_APPEND_REUSE, + "APPEND_REUSE", + 0, + "Append (Reuse Data)", + "Import the assets as copied data-block while avoiding multiple copies of nested, " + "typically heavy data. For example the textures of a material asset, or the mesh of an " + "object asset, don't have to be copied every time this asset is imported. The instances of " + "the asset share the data instead"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index f3a57b72095..93038b5709c 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -398,6 +398,16 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) case FILE_ASSET_IMPORT_APPEND: return WM_file_append_datablock( G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name, BLO_LIBLINK_APPEND_RECURSIVE); + case FILE_ASSET_IMPORT_APPEND_REUSE: + return WM_file_append_datablock(G_MAIN, + NULL, + NULL, + NULL, + asset_drag->path, + idtype, + name, + BLO_LIBLINK_APPEND_RECURSIVE | + BLO_LIBLINK_APPEND_LOCAL_ID_REUSE); } BLI_assert_unreachable(); From c33a005297ea21d0afafea96579d13607b309d7d Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 6 Sep 2021 16:48:39 +0200 Subject: [PATCH 0356/1500] Texts in Outliner dont activate Texts in Outliner dont activate on selecting (Text Editor did not change to selected text) which is a bit inconsistent to other ID types. ref T90862 Maniphest Tasks: T90862 Differential Revision: https://developer.blender.org/D12412 --- source/blender/editors/include/ED_text.h | 2 ++ .../blender/editors/interface/interface_ops.c | 11 +--------- .../editors/space_outliner/outliner_select.c | 11 ++++++++++ source/blender/editors/space_text/text_draw.c | 20 +++++++++++++++++++ 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/include/ED_text.h b/source/blender/editors/include/ED_text.h index 2284c82b3d5..6e012ec1a91 100644 --- a/source/blender/editors/include/ED_text.h +++ b/source/blender/editors/include/ED_text.h @@ -34,6 +34,8 @@ struct UndoStep; struct UndoType; struct bContext; +bool ED_text_activate_in_screen(struct bContext *C, struct Text *text); + void ED_text_scroll_to_cursor(struct SpaceText *st, struct ARegion *region, bool center); bool ED_text_region_location_from_cursor(struct SpaceText *st, diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index dd10d942fc9..7b59a6f7263 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1381,16 +1381,7 @@ static int editsource_text_edit(bContext *C, /* naughty!, find text area to set, not good behavior * but since this is a developer tool lets allow it - campbell */ - ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0); - if (area) { - SpaceText *st = area->spacedata.first; - ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); - st->text = text; - if (region) { - ED_text_scroll_to_cursor(st, region, true); - } - } - else { + if (!ED_text_activate_in_screen(C, text)) { BKE_reportf(op->reports, RPT_INFO, "See '%s' in the text editor", text->id.name + 2); } diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 581892ebb3a..5e409db0059 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -34,6 +34,7 @@ #include "DNA_scene_types.h" #include "DNA_sequence_types.h" #include "DNA_shader_fx_types.h" +#include "DNA_text_types.h" #include "BLI_listbase.h" #include "BLI_utildefines.h" @@ -63,6 +64,7 @@ #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_sequencer.h" +#include "ED_text.h" #include "ED_undo.h" #include "SEQ_select.h" @@ -737,6 +739,12 @@ static void tree_element_layer_collection_activate(bContext *C, TreeElement *te) WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL); } +static void tree_element_text_activate(bContext *C, TreeElement *te) +{ + Text *text = (Text *)te->store_elem->id; + ED_text_activate_in_screen(C, text); +} + /* ---------------------------------------------- */ /* generic call for ID data check or make/check active in UI */ @@ -764,6 +772,9 @@ void tree_element_activate(bContext *C, case ID_CA: tree_element_camera_activate(C, tvc->scene, te); break; + case ID_TXT: + tree_element_text_activate(C, te); + break; } } diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 99fcb2092c3..b541b65d676 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -48,6 +48,9 @@ #include "text_format.h" #include "text_intern.h" +#include "WM_api.h" +#include "WM_types.h" + /******************** text font drawing ******************/ typedef struct TextDrawContext { @@ -1734,6 +1737,23 @@ void text_update_character_width(SpaceText *st) text_font_end(&tdc); } +bool ED_text_activate_in_screen(bContext *C, Text *text) +{ + ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0); + if (area) { + SpaceText *st = area->spacedata.first; + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + st->text = text; + if (region) { + ED_text_scroll_to_cursor(st, region, true); + } + WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text); + return true; + } + + return false; +} + /* Moves the view to the cursor location, * also used to make sure the view isn't outside the file */ void ED_text_scroll_to_cursor(SpaceText *st, ARegion *region, const bool center) From b80ed8396d285fd3f63831cb1b6589e7057514ce Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 28 Sep 2021 17:50:50 +0200 Subject: [PATCH 0357/1500] Fix T89164: Sculpt "Smooth" brush crash with zero pressure Caused by {rB3e5431fdf439} Issue is that sculpting could start with using `SCULPT_smooth` and (because of the Pressure sensitivity dropping to zero) code would switch to `SCULPT_enhance_details_brush` at strength zero. Issue with this though is that this can be in the middle or end of a stroke and the necessary `ss->cache->detail_directions` are only initialized for the first brush step (see `SCULPT_stroke_is_first_brush_step` in `SCULPT_enhance_details_brush`). With these missing, it could only go downhill from there. Suggest to prevent the "mode-flip" from `SCULPT_smooth` to `SCULPT_enhance_details_brush` (happening solely because of pressure strength) by changing the condition. Now do `SCULPT_enhance_details_brush` only if strength is **below** zero and `SCULPT_smooth` else (flipping from enhance_details to regular smooth is fine). If inverting the brush in the middle of the stroke will be supported at some point, the codepath of `smooth` would have to inform the cache that invert changed and detail_directions would have to be initialized then (even if not at the start of the stroke). Maniphest Tasks: T89164 Differential Revision: https://developer.blender.org/D12676 --- source/blender/editors/sculpt_paint/sculpt_smooth.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 38165b7622f..1bfe8e1cbf1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -395,7 +395,12 @@ void SCULPT_smooth(Sculpt *sd, void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; - if (ss->cache->bstrength <= 0.0f) { + + /* NOTE: The enhance brush needs to initialize its state on the first brush step. The stroke + * strength can become 0 during the stroke, but it can not change sign (the sign is determined + * in the beginning of the stroke. So here it is important to not switch to enhance brush in the + * middle of the stroke. */ + if (ss->cache->bstrength < 0.0f) { /* Invert mode, intensify details. */ SCULPT_enhance_details_brush(sd, ob, nodes, totnode); } From 1f4545dc9c91d5f27102ff6c05943de1903d7b42 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 29 Sep 2021 15:48:16 +0200 Subject: [PATCH 0358/1500] Cleanup: else-after-return --- source/blender/editors/uvedit/uvedit_islands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c index 2aa09d7e731..62173f4eaff 100644 --- a/source/blender/editors/uvedit/uvedit_islands.c +++ b/source/blender/editors/uvedit/uvedit_islands.c @@ -247,7 +247,7 @@ bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], float coor return true; } /* Check if selection lies on a valid UDIM image tile. */ - else if (is_tiled_image) { + if (is_tiled_image) { LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) { const int tile_index = tile->tile_number - 1001; const int target_x = (tile_index % 10); From 901fa96b7f595fb1d77075aaa68b2e14f57a8e31 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Tue, 28 Sep 2021 15:13:23 +0200 Subject: [PATCH 0359/1500] Keymap: New preference to open folders on single click in file browser Introduce a new keymap preference to navigate into folders by clicking on them once instead of twice. Makes browsing folders faster albeit non-standard, so keeping this off by default for now. Does not affect Industry Compatible or other keymaps. It is still the possible to right-click to open context menu, hold Ctrl or Shift to select multiple items: {F10651030, size=full} ---- Keymap preference: {F10652759, size=full} Part of T91537 Reviewed By: fsiddi, campbellbarton Differential Revision: https://developer.blender.org/D12667 --- release/scripts/presets/keyconfig/Blender.py | 14 ++++++++++++++ .../keyconfig/keymap_data/blender_default.py | 14 +++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index b165eaddcf5..1852e150589 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -217,6 +217,15 @@ class Prefs(bpy.types.KeyConfigPreferences): update=update_fn, ) + use_file_single_click: BoolProperty( + name="Open Folders on Single Click", + description=( + "Navigate into folders by clicking on them once instead of twice" + ), + default=False, + update=update_fn, + ) + def draw(self, layout): from bpy import context @@ -273,6 +282,10 @@ class Prefs(bpy.types.KeyConfigPreferences): sub.prop(self, "use_pie_click_drag") sub.prop(self, "use_v3d_shade_ex_pie") + # File Browser settings. + col = layout.column() + col.label(text="File Browser") + col.row().prop(self, "use_file_single_click") blender_default = bpy.utils.execfile(os.path.join(DIRNAME, "keymap_data", "blender_default.py")) @@ -312,6 +325,7 @@ def load(): ), use_alt_click_leader=kc_prefs.use_alt_click_leader, use_pie_click_drag=kc_prefs.use_pie_click_drag, + use_file_single_click=kc_prefs.use_file_single_click, ), ) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 41b5d6f7998..35eb6490265 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -66,6 +66,7 @@ class Params: # Alt-MMB axis switching 'RELATIVE' or 'ABSOLUTE' axis switching. "v3d_alt_mmb_drag_action", + "use_file_single_click", # Convenience variables: # (derived from other settings). # @@ -106,6 +107,7 @@ class Params: use_alt_tool_or_cursor=False, use_alt_click_leader=False, use_pie_click_drag=False, + use_file_single_click=False, v3d_tilde_action='VIEW', v3d_alt_mmb_drag_action='RELATIVE', ): @@ -190,6 +192,8 @@ class Params: self.use_alt_click_leader = use_alt_click_leader self.use_pie_click_drag = use_pie_click_drag + self.use_file_single_click = use_file_single_click + # Convenience variables: self.use_fallback_tool_rmb = self.use_fallback_tool if self.select_mouse == 'RIGHT' else False self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value @@ -2151,16 +2155,20 @@ def km_file_browser_main(params): {"items": items}, ) + if not params.use_file_single_click: + items.extend([ + ("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, + {"properties": [("open", True), ("deselect_all", not params.legacy)]}), + ]) + items.extend([ ("file.mouse_execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), # Both .execute and .select are needed here. The former only works if # there's a file operator (i.e. not in regular editor mode) but is # needed to load files. The latter makes selection work if there's no # operator (i.e. in regular editor mode). - ("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, - {"properties": [("open", True), ("deselect_all", not params.legacy)]}), ("file.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, - {"properties": [("open", False), ("deselect_all", not params.legacy)]}), + {"properties": [("open", params.use_file_single_click), ("deselect_all", not params.legacy)]}), ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True}, {"properties": [("extend", True), ("open", False)]}), ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, From fe070fe33ba84c50e8daebbd547e23106161a1f3 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 29 Sep 2021 12:51:48 +0200 Subject: [PATCH 0360/1500] Fix Cycles crash in certain hair configurations The issue was caused by hair shader setup setting normal to a non finite value, which then gets used to create a ray with non-finite direction, making BVH traversal to run out of stack memory. Happens with 150_0040_A.lighting.blend frame 112 of the Sprites project. Differential Revision: https://developer.blender.org/D12692 --- intern/cycles/kernel/geom/geom_curve_intersect.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h index 213f3e62ee0..b2101034bb6 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/geom_curve_intersect.h @@ -713,7 +713,7 @@ ccl_device_inline void curve_shader_setup(const KernelGlobals *kg, P = transform_point(&tfm, P); D = transform_direction(&tfm, D * t); - D = normalize_len(D, &t); + D = safe_normalize_len(D, &t); } int prim = kernel_tex_fetch(__prim_index, isect_prim); From 4d4113adc2623c50888b63eaca3a055d8cdf3045 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 29 Sep 2021 14:53:47 +0200 Subject: [PATCH 0361/1500] Cycles: record large number of transparent shadow intersections on CPU So we can do fewer intersection calls, only on the GPU do we need to save memory and do this in small steps. Ref T87836 --- .../cycles/integrator/path_trace_work_gpu.cpp | 4 ++-- .../kernel/integrator/integrator_state.h | 18 +++++++++++++----- .../integrator/integrator_state_template.h | 10 +++++++--- .../kernel/integrator/integrator_state_util.h | 11 ++++++++--- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 450e8aaac04..e41d8d1d252 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -95,8 +95,8 @@ void PathTraceWorkGPU::alloc_integrator_soa() #define KERNEL_STRUCT_END(name) \ break; \ } -#define KERNEL_STRUCT_END_ARRAY(name, array_size) \ - if (array_index == array_size - 1) { \ +#define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \ + if (array_index == gpu_array_size - 1) { \ break; \ } \ } diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h index 094446be02c..f745ad3f4b9 100644 --- a/intern/cycles/kernel/integrator/integrator_state.h +++ b/intern/cycles/kernel/integrator/integrator_state.h @@ -60,7 +60,15 @@ CCL_NAMESPACE_BEGIN * TODO: these could be made dynamic depending on the features used in the scene. */ #define INTEGRATOR_VOLUME_STACK_SIZE VOLUME_STACK_SIZE -#define INTEGRATOR_SHADOW_ISECT_SIZE 4 + +#define INTEGRATOR_SHADOW_ISECT_SIZE_CPU 1024 +#define INTEGRATOR_SHADOW_ISECT_SIZE_GPU 4 + +#ifdef __KERNEL_CPU__ +# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_CPU +#else +# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_GPU +#endif /* Data structures */ @@ -74,9 +82,9 @@ typedef struct IntegratorStateCPU { #define KERNEL_STRUCT_END(name) \ } \ name; -#define KERNEL_STRUCT_END_ARRAY(name, size) \ +#define KERNEL_STRUCT_END_ARRAY(name, cpu_size, gpu_size) \ } \ - name[size]; + name[cpu_size]; #include "kernel/integrator/integrator_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER @@ -103,9 +111,9 @@ typedef struct IntegratorStateGPU { #define KERNEL_STRUCT_END(name) \ } \ name; -#define KERNEL_STRUCT_END_ARRAY(name, size) \ +#define KERNEL_STRUCT_END_ARRAY(name, cpu_size, gpu_size) \ } \ - name[size]; + name[gpu_size]; #include "kernel/integrator/integrator_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER diff --git a/intern/cycles/kernel/integrator/integrator_state_template.h b/intern/cycles/kernel/integrator/integrator_state_template.h index 41dd1bfcdbf..0d8126c64aa 100644 --- a/intern/cycles/kernel/integrator/integrator_state_template.h +++ b/intern/cycles/kernel/integrator/integrator_state_template.h @@ -107,7 +107,7 @@ KERNEL_STRUCT_END(subsurface) KERNEL_STRUCT_BEGIN(volume_stack) KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, object, KERNEL_FEATURE_VOLUME) KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, shader, KERNEL_FEATURE_VOLUME) -KERNEL_STRUCT_END_ARRAY(volume_stack, INTEGRATOR_VOLUME_STACK_SIZE) +KERNEL_STRUCT_END_ARRAY(volume_stack, INTEGRATOR_VOLUME_STACK_SIZE, INTEGRATOR_VOLUME_STACK_SIZE) /********************************* Shadow Path State **************************/ @@ -153,11 +153,15 @@ KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, object, KERNEL_FEATURE_PATH_TRACIN KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, type, KERNEL_FEATURE_PATH_TRACING) /* TODO: exclude for GPU. */ KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float3, Ng, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_END_ARRAY(shadow_isect, INTEGRATOR_SHADOW_ISECT_SIZE) +KERNEL_STRUCT_END_ARRAY(shadow_isect, + INTEGRATOR_SHADOW_ISECT_SIZE_CPU, + INTEGRATOR_SHADOW_ISECT_SIZE_GPU) /**************************** Shadow Volume Stack *****************************/ KERNEL_STRUCT_BEGIN(shadow_volume_stack) KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, object, KERNEL_FEATURE_VOLUME) KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, shader, KERNEL_FEATURE_VOLUME) -KERNEL_STRUCT_END_ARRAY(shadow_volume_stack, INTEGRATOR_VOLUME_STACK_SIZE) +KERNEL_STRUCT_END_ARRAY(shadow_volume_stack, + INTEGRATOR_VOLUME_STACK_SIZE, + INTEGRATOR_VOLUME_STACK_SIZE) diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index cdf412fe22f..08d6cb00114 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -217,10 +217,10 @@ ccl_device_inline void integrator_state_copy_only(const IntegratorState to_state while (false) \ ; -# define KERNEL_STRUCT_END_ARRAY(name, array_size) \ +# define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \ ++index; \ } \ - while (index < array_size) \ + while (index < gpu_array_size) \ ; # include "kernel/integrator/integrator_state_template.h" @@ -264,7 +264,12 @@ ccl_device_inline void integrator_state_shadow_catcher_split(INTEGRATOR_STATE_AR IntegratorStateCPU *ccl_restrict split_state = state + 1; - *split_state = *state; + /* Only copy the required subset, since shadow intersections are big and irrelevant here. */ + split_state->path = state->path; + split_state->ray = state->ray; + split_state->isect = state->isect; + memcpy(split_state->volume_stack, state->volume_stack, sizeof(state->volume_stack)); + split_state->shadow_path = state->shadow_path; split_state->path.flag |= PATH_RAY_SHADOW_CATCHER_PASS; #endif From 367775ac6a2d1a4d002952aa2731778f99d13d6a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 29 Sep 2021 16:14:38 +0200 Subject: [PATCH 0362/1500] Fix Cycles use of uninitialized value in volume stack intersection on CPU Could cause an actual bug but probability is low in practice. --- intern/cycles/bvh/bvh_embree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index 96852510b63..20430cb164c 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -213,7 +213,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) if (ctx->num_hits < ctx->max_hits) { Intersection current_isect; kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - for (size_t i = 0; i < ctx->max_hits; ++i) { + for (size_t i = 0; i < ctx->num_hits; ++i) { if (current_isect.object == ctx->isect_s[i].object && current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) { /* This intersection was already recorded, skip it. */ From 6aac892fad9e6447cf7cfdaee5c2e9c61e2e99fe Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 29 Sep 2021 16:59:48 +0200 Subject: [PATCH 0363/1500] Cleanup: Enforce C linkage for internal File Browser header This will be used by C++ code in the upcoming asset catalog UI commit. --- source/blender/editors/space_file/file_intern.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 905c0aeb8e0..65e0ad94c72 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -23,6 +23,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + /* internal exports only */ struct ARegion; @@ -152,3 +156,7 @@ void file_execute_region_panels_register(struct ARegionType *art); void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds); void file_path_to_ui_path(const char *path, char *r_pathi, int max_size); + +#ifdef __cplusplus +} +#endif From df9120b365380cc1d64006e0d37a650eaaff9776 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 29 Sep 2021 15:32:53 +0200 Subject: [PATCH 0364/1500] Fix T89864: Adding an asset referencing other objects adds it to scene but only adds data-blocks of referenced objects. Link/append code needs proper access to scene/view3d data to handle collections/objects instantiation. Note that this is a temporary hack more than a proper fix, which would require a deeper redesign of drag&drop code. Also note that this will not handle 'properly' (i.e. as user would expect it) cases like implicitely appended parent objects, in that only the explicitely appended object will be dropped to the nes location, the others will remain at their original coordinates. Differential Revision: https://developer.blender.org/D12696 --- source/blender/editors/interface/interface.c | 7 ++++ source/blender/windowmanager/WM_types.h | 7 ++++ .../windowmanager/intern/wm_dragdrop.c | 40 ++++++++++++++----- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index beee622673c..a98af00572d 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -6224,6 +6224,13 @@ void UI_but_drag_set_asset(uiBut *but, asset_drag->id_type = ED_asset_handle_get_id_type(asset); asset_drag->import_type = import_type; + /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the + * #wmDropBox. + * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its + * copy callback. + * */ + asset_drag->evil_C = but->block->evil_C; + but->dragtype = WM_DRAG_ASSET; ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index df6dc3af3cb..d0690cfd738 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -944,6 +944,13 @@ typedef struct wmDragAsset { const char *path; int id_type; int import_type; /* eFileAssetImportType */ + + /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the + * #wmDropBox. + * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its + * copy callback. + * */ + struct bContext *evil_C; } wmDragAsset; typedef char *(*WMDropboxTooltipFunc)(struct bContext *, diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 93038b5709c..c5a89e3ad9f 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -42,6 +42,7 @@ #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" +#include "BKE_main.h" #include "BLO_readfile.h" @@ -392,21 +393,42 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) const char *name = asset_drag->name; ID_Type idtype = asset_drag->id_type; + /* FIXME: Link/Append should happens in the operator called at the end of drop process, not from + * here. */ + + Main *bmain = CTX_data_main(asset_drag->evil_C); + Scene *scene = CTX_data_scene(asset_drag->evil_C); + ViewLayer *view_layer = CTX_data_view_layer(asset_drag->evil_C); + View3D *view3d = CTX_wm_view3d(asset_drag->evil_C); + switch ((eFileAssetImportType)asset_drag->import_type) { case FILE_ASSET_IMPORT_LINK: - return WM_file_link_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name, 0); + return WM_file_link_datablock(bmain, + scene, + view_layer, + view3d, + asset_drag->path, + idtype, + name, + FILE_ACTIVE_COLLECTION); case FILE_ASSET_IMPORT_APPEND: - return WM_file_append_datablock( - G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name, BLO_LIBLINK_APPEND_RECURSIVE); - case FILE_ASSET_IMPORT_APPEND_REUSE: - return WM_file_append_datablock(G_MAIN, - NULL, - NULL, - NULL, + return WM_file_append_datablock(bmain, + scene, + view_layer, + view3d, asset_drag->path, idtype, name, - BLO_LIBLINK_APPEND_RECURSIVE | + BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION); + case FILE_ASSET_IMPORT_APPEND_REUSE: + return WM_file_append_datablock(G_MAIN, + scene, + view_layer, + view3d, + asset_drag->path, + idtype, + name, + BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | BLO_LIBLINK_APPEND_LOCAL_ID_REUSE); } From 9d9f205dc4a9ddae3d654c64894eaca8443cacc0 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 29 Sep 2021 17:01:13 +0200 Subject: [PATCH 0365/1500] Asset Browser: Initial Asset Catalog UI The Asset Browser now displays a tree with asset catalogs in the left sidebar. This replaces the asset categories. It uses the new UI tree-view API (https://wiki.blender.org/wiki/Source/Interface/Views#Tree-View). Buttons are displayed for adding and removing of catalogs. Parent items can be collapsed, but the collapsed/uncollapsed state is not stored in files yet. Note that edits to catalogs (e.g. new or removed catalogs) are only written to the asset library's catalog definition files when saving a .blend. In the "Current File" asset library, we try to show asset catalogs from a parent asset library, or if that fails, from the directory the file is stored in. See adaf4f56e1ed. There are plenty of TODOs and smaller glitches to be fixed still. Plus a UI polishing pass should be done. Important missing UI features: * Dragging assets into catalogs (WIP, close to being ready). * Renaming catalogs * Proper handling of catalogs in the "Current File" asset library (currently not working well). The "Current File" asset library is especially limited still. Since this is the only place where you can assign assets to a catalog, this makes the catalogs very cumbersome in general. To assign an asset to a catalog, one has to manually copy the Catalog ID (a random hash like number) to the asset metadata through a temporary UI in the Asset Browser Sidebar. These limitations should be addressed over the next few days, they are high priority. Differential Revision: https://developer.blender.org/D12670 --- .../scripts/modules/bpy_extras/asset_utils.py | 11 +- .../startup/bl_ui/space_filebrowser.py | 25 -- .../blender/blenkernel/BKE_asset_library.hh | 4 + .../blenkernel/intern/asset_library.cc | 23 ++ source/blender/editors/asset/CMakeLists.txt | 2 + .../blender/editors/asset/ED_asset_catalog.hh | 35 +++ .../editors/asset/intern/asset_catalog.cc | 87 +++++++ .../blender/editors/asset/intern/asset_ops.cc | 88 +++++++ .../blender/editors/include/ED_fileselect.h | 1 + source/blender/editors/include/UI_interface.h | 3 +- .../blender/editors/include/UI_tree_view.hh | 6 + source/blender/editors/interface/interface.c | 9 + source/blender/editors/interface/tree_view.cc | 19 +- .../blender/editors/space_file/CMakeLists.txt | 1 + .../space_file/asset_catalog_tree_view.cc | 230 ++++++++++++++++++ .../blender/editors/space_file/file_intern.h | 10 + .../blender/editors/space_file/file_panels.c | 32 +++ source/blender/editors/space_file/filelist.c | 52 ++++ source/blender/editors/space_file/filelist.h | 7 + source/blender/editors/space_file/filesel.c | 14 +- .../blender/editors/space_file/space_file.c | 9 +- source/blender/makesdna/DNA_space_types.h | 15 +- source/blender/makesrna/intern/rna_space.c | 62 ----- 23 files changed, 638 insertions(+), 107 deletions(-) create mode 100644 source/blender/editors/asset/ED_asset_catalog.hh create mode 100644 source/blender/editors/asset/intern/asset_catalog.cc create mode 100644 source/blender/editors/space_file/asset_catalog_tree_view.cc diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py index 2cd5dddefbc..c7ebdc1d5e1 100644 --- a/release/scripts/modules/bpy_extras/asset_utils.py +++ b/release/scripts/modules/bpy_extras/asset_utils.py @@ -52,19 +52,12 @@ class AssetBrowserPanel: bl_space_type = 'FILE_BROWSER' @classmethod - def poll(cls, context): + def asset_browser_panel_poll(cls, context): return SpaceAssetInfo.is_asset_browser_poll(context) - -class AssetBrowserSpecificCategoryPanel(AssetBrowserPanel): - asset_categories = set() # Set of strings like 'ANIMATIONS', see `asset_category_items` in rna_space.c - @classmethod def poll(cls, context): - return ( - SpaceAssetInfo.is_asset_browser_poll(context) - and context.space_data.params.asset_category in cls.asset_categories - ) + return cls.asset_browser_panel_poll(context) class AssetMetaDataPanel: diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index b47404fd727..5dd8c69f3d5 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -648,30 +648,6 @@ class ASSETBROWSER_MT_select(AssetBrowserMenu, Menu): layout.operator("file.select_box") -class ASSETBROWSER_PT_navigation_bar(asset_utils.AssetBrowserPanel, Panel): - bl_label = "Asset Navigation" - bl_region_type = 'TOOLS' - bl_options = {'HIDE_HEADER'} - - @classmethod - def poll(cls, context): - return ( - asset_utils.AssetBrowserPanel.poll(context) and - context.preferences.experimental.use_extended_asset_browser - ) - - def draw(self, context): - layout = self.layout - - space_file = context.space_data - - col = layout.column() - - col.scale_x = 1.3 - col.scale_y = 1.3 - col.prop(space_file.params, "asset_category", expand=True) - - class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): bl_region_type = 'TOOL_PROPS' bl_label = "Asset Metadata" @@ -821,7 +797,6 @@ classes = ( ASSETBROWSER_MT_editor_menus, ASSETBROWSER_MT_view, ASSETBROWSER_MT_select, - ASSETBROWSER_PT_navigation_bar, ASSETBROWSER_PT_metadata, ASSETBROWSER_PT_metadata_preview, ASSETBROWSER_PT_metadata_details, diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh index fc5e137dd3e..1dc02f7aa9b 100644 --- a/source/blender/blenkernel/BKE_asset_library.hh +++ b/source/blender/blenkernel/BKE_asset_library.hh @@ -48,3 +48,7 @@ struct AssetLibrary { }; } // namespace blender::bke + +blender::bke::AssetCatalogService *BKE_asset_library_get_catalog_service( + const ::AssetLibrary *library); +blender::bke::AssetCatalogTree *BKE_asset_library_get_catalog_tree(const ::AssetLibrary *library); diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index 1086efe45fd..27e66ee5725 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -68,6 +68,29 @@ bool BKE_asset_library_find_suitable_root_path_from_main(const Main *bmain, char return BKE_asset_library_find_suitable_root_path_from_path(bmain->name, r_library_path); } +blender::bke::AssetCatalogService *BKE_asset_library_get_catalog_service( + const ::AssetLibrary *library_c) +{ + if (library_c == nullptr) { + return nullptr; + } + + const blender::bke::AssetLibrary &library = reinterpret_cast( + *library_c); + return library.catalog_service.get(); +} + +blender::bke::AssetCatalogTree *BKE_asset_library_get_catalog_tree(const ::AssetLibrary *library) +{ + blender::bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service( + library); + if (catalog_service == nullptr) { + return nullptr; + } + + return catalog_service->get_catalog_tree(); +} + namespace blender::bke { void AssetLibrary::load(StringRefNull library_root_directory) diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 31c07580570..b6657bfca63 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -31,6 +31,7 @@ set(INC_SYS ) set(SRC + intern/asset_catalog.cc intern/asset_filter.cc intern/asset_handle.cc intern/asset_library_reference.cc @@ -40,6 +41,7 @@ set(SRC intern/asset_ops.cc intern/asset_temp_id_consumer.cc + ED_asset_catalog.hh ED_asset_filter.h ED_asset_handle.h ED_asset_library.h diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh new file mode 100644 index 00000000000..cffd7728a60 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_catalog.hh @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#pragma once + +#include "BKE_asset_catalog.hh" + +#include "BLI_string_ref.hh" + +struct AssetLibrary; +namespace blender::bke { +class AssetCatalog; +} // namespace blender::bke + +blender::bke::AssetCatalog *ED_asset_catalog_add(AssetLibrary *library, + blender::StringRefNull name, + blender::StringRef parent_path = nullptr); +void ED_asset_catalog_remove(AssetLibrary *library, const blender::bke::CatalogID &catalog_id); diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc new file mode 100644 index 00000000000..202d4234051 --- /dev/null +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -0,0 +1,87 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#include "BKE_asset_catalog.hh" +#include "BKE_asset_library.hh" + +#include "BLI_string_utils.h" + +#include "ED_asset_catalog.hh" + +using namespace blender; +using namespace blender::bke; + +struct CatalogUniqueNameFnData { + const AssetCatalogService &catalog_service; + StringRef parent_path; +}; + +static std::string to_full_path(StringRef parent_path, StringRef name) +{ + return parent_path.is_empty() ? + std::string(name) : + std::string(parent_path) + AssetCatalogService::PATH_SEPARATOR + name; +} + +static bool catalog_name_exists_fn(void *arg, const char *name) +{ + CatalogUniqueNameFnData &fn_data = *static_cast(arg); + std::string fullpath = to_full_path(fn_data.parent_path, name); + return fn_data.catalog_service.find_catalog_by_path(fullpath); +} + +static std::string catalog_name_ensure_unique(AssetCatalogService &catalog_service, + StringRefNull name, + StringRef parent_path) +{ + CatalogUniqueNameFnData fn_data = {catalog_service, parent_path}; + + char unique_name[NAME_MAX] = ""; + BLI_uniquename_cb( + catalog_name_exists_fn, &fn_data, name.c_str(), '.', unique_name, sizeof(unique_name)); + + return unique_name; +} + +AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library, + StringRefNull name, + StringRef parent_path) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + return nullptr; + } + + std::string unique_name = catalog_name_ensure_unique(*catalog_service, name, parent_path); + std::string fullpath = to_full_path(parent_path, unique_name); + + return catalog_service->create_catalog(fullpath); +} + +void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_id) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + BLI_assert_unreachable(); + return; + } + + catalog_service->delete_catalog(catalog_id); +} diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index a18b7649060..5424bae77b4 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -18,13 +18,18 @@ * \ingroup edasset */ +#include "BKE_asset_catalog.hh" #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_report.h" +#include "BLI_string_ref.hh" #include "BLI_vector.hh" #include "ED_asset.h" +#include "ED_asset_catalog.hh" +/* XXX needs access to the file list, should all be done via the asset system in future. */ +#include "ED_fileselect.h" #include "RNA_access.h" #include "RNA_define.h" @@ -32,6 +37,8 @@ #include "WM_api.h" #include "WM_types.h" +using namespace blender; + /* -------------------------------------------------------------------- */ using PointerRNAVec = blender::Vector; @@ -372,10 +379,91 @@ static void ASSET_OT_list_refresh(struct wmOperatorType *ot) /* -------------------------------------------------------------------- */ +static bool asset_catalog_operator_poll(bContext *C) +{ + const SpaceFile *sfile = CTX_wm_space_file(C); + return asset_operation_poll(C) && sfile && ED_fileselect_active_asset_library_get(sfile); +} + +static int asset_catalog_new_exec(bContext *C, wmOperator *op) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); + char *parent_path = RNA_string_get_alloc(op->ptr, "parent_path", nullptr, 0, nullptr); + + ED_asset_catalog_add(asset_library, "Catalog", parent_path); + + MEM_freeN(parent_path); + + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + + return OPERATOR_FINISHED; +} + +static void ASSET_OT_catalog_new(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Asset Catalog"; + ot->description = "Create a new catalog to put assets in"; + ot->idname = "ASSET_OT_catalog_new"; + + /* api callbacks */ + ot->exec = asset_catalog_new_exec; + ot->poll = asset_catalog_operator_poll; + + RNA_def_string(ot->srna, + "parent_path", + nullptr, + 0, + "Parent Path", + "Optional path defining the location to put the new catalog under"); +} + +static int asset_catalog_delete_exec(bContext *C, wmOperator *op) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); + char *catalog_id_str = RNA_string_get_alloc(op->ptr, "catalog_id", nullptr, 0, nullptr); + bke::CatalogID catalog_id; + if (!BLI_uuid_parse_string(&catalog_id, catalog_id_str)) { + return OPERATOR_CANCELLED; + } + + ED_asset_catalog_remove(asset_library, catalog_id); + + MEM_freeN(catalog_id_str); + + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + + return OPERATOR_FINISHED; +} + +static void ASSET_OT_catalog_delete(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Asset Catalog"; + ot->description = + "Remove an asset catalog from the asset library (contained assets will not be affected and " + "show up as unassigned)"; + ot->idname = "ASSET_OT_catalog_delete"; + + /* api callbacks */ + ot->exec = asset_catalog_delete_exec; + ot->invoke = WM_operator_confirm; + ot->poll = asset_catalog_operator_poll; + + RNA_def_string(ot->srna, "catalog_id", nullptr, 0, "Catalog ID", "ID of the catalog to delete"); +} + +/* -------------------------------------------------------------------- */ + void ED_operatortypes_asset(void) { WM_operatortype_append(ASSET_OT_mark); WM_operatortype_append(ASSET_OT_clear); + WM_operatortype_append(ASSET_OT_catalog_new); + WM_operatortype_append(ASSET_OT_catalog_delete); + WM_operatortype_append(ASSET_OT_list_refresh); } diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 82057c726a5..423d619f41a 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -142,6 +142,7 @@ void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile); bool ED_fileselect_is_file_browser(const struct SpaceFile *sfile); bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile); +struct AssetLibrary *ED_fileselect_active_asset_library_get(const struct SpaceFile *sfile); struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile); /* Activate the file that corresponds to the given ID. diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index f7842270746..106f6166760 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2761,7 +2761,8 @@ void UI_interface_tag_script_reload(void); /* Support click-drag motion which presses the button and closes a popover (like a menu). */ #define USE_UI_POPOVER_ONCE -bool UI_tree_view_item_is_active(uiTreeViewItemHandle *item_); +bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); +bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); #ifdef __cplusplus } diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index fac880a0a67..81a614cd195 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -190,6 +190,12 @@ class AbstractTreeViewItem : public TreeViewItemContainer { * last redraw to this item. If sub-classes introduce more advanced state they should override * this and make it update their state accordingly. */ virtual void update_from_old(const AbstractTreeViewItem &old); + /** Compare this item to \a other to check if they represent the same data. This is critical for + * being able to recognize an item from a previous redraw, to be able to keep its state (e.g. + * open/closed, active, etc.). Items are only matched if their parents also match. + * By default this just matches the items names/labels (if their parents match!). If that isn't + * good enough for a sub-class, that can override it. */ + virtual bool matches(const AbstractTreeViewItem &other) const; const AbstractTreeView &get_tree_view() const; int count_parents() const; diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index a98af00572d..c53bffca778 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -743,6 +743,15 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) return false; } + if ((but->type == UI_BTYPE_TREEROW) && (oldbut->type == UI_BTYPE_TREEROW)) { + uiButTreeRow *but_treerow = (uiButTreeRow *)but; + uiButTreeRow *oldbut_treerow = (uiButTreeRow *)oldbut; + if (!but_treerow->tree_item || !oldbut_treerow->tree_item || + !UI_tree_view_item_matches(but_treerow->tree_item, oldbut_treerow->tree_item)) { + return false; + } + } + return true; } diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index ee50126f974..16499065019 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -123,7 +123,7 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child( const AbstractTreeViewItem &lookup_item, const TreeViewItemContainer &items) { for (const auto &iter_item : items.children_) { - if (lookup_item.label_ == iter_item->label_) { + if (lookup_item.matches(*iter_item)) { /* We have a matching item! */ return iter_item.get(); } @@ -145,6 +145,11 @@ void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) is_active_ = old.is_active_; } +bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const +{ + return label_ == other.label_; +} + const AbstractTreeView &AbstractTreeViewItem::get_tree_view() const { return static_cast(*root_); @@ -309,8 +314,16 @@ uiBut *BasicTreeViewItem::button() using namespace blender::ui; -bool UI_tree_view_item_is_active(uiTreeViewItemHandle *item_) +bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle) { - AbstractTreeViewItem &item = reinterpret_cast(*item_); + const AbstractTreeViewItem &item = reinterpret_cast(*item_handle); return item.is_active(); } + +bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, + const uiTreeViewItemHandle *b_handle) +{ + const AbstractTreeViewItem &a = reinterpret_cast(*a_handle); + const AbstractTreeViewItem &b = reinterpret_cast(*b_handle); + return a.matches(b); +} diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt index b60f9df82f6..4b508f16c1e 100644 --- a/source/blender/editors/space_file/CMakeLists.txt +++ b/source/blender/editors/space_file/CMakeLists.txt @@ -34,6 +34,7 @@ set(INC ) set(SRC + asset_catalog_tree_view.cc file_draw.c file_ops.c file_panels.c diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc new file mode 100644 index 00000000000..7eea9af925b --- /dev/null +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -0,0 +1,230 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spfile + */ + +#include "ED_fileselect.h" + +#include "DNA_space_types.h" + +#include "BKE_asset_catalog.hh" +#include "BKE_asset_library.hh" + +#include "BLI_string_ref.hh" + +#include "BLT_translation.h" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" +#include "UI_tree_view.hh" + +#include "WM_api.h" +#include "WM_types.h" + +#include "file_intern.h" + +using namespace blender; +using namespace blender::bke; + +namespace blender::ed::asset_browser { + +class AssetCatalogTreeView : public ui::AbstractTreeView { + /** The asset catalog tree this tree-view represents. */ + bke::AssetCatalogTree *catalog_tree_; + FileAssetSelectParams *params_; + + friend class AssetCatalogTreeViewItem; + + public: + AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params); + + void build_tree() override; + + private: + ui::BasicTreeViewItem &build_catalog_items_recursive(ui::TreeViewItemContainer &view_parent_item, + AssetCatalogTreeItem &catalog); + + void add_all_item(); + void add_unassigned_item(); + bool is_active_catalog(CatalogID catalog_id) const; +}; +/* ---------------------------------------------------------------------- */ + +class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { + /** The catalog tree item this tree view item represents. */ + AssetCatalogTreeItem &catalog_item_; + + public: + AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item) + : BasicTreeViewItem(catalog_item->get_name()), catalog_item_(*catalog_item) + { + } + + void on_activate() override + { + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + tree_view.params_->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG; + tree_view.params_->catalog_id = catalog_item_.get_catalog_id(); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + } + + void build_row(uiLayout &row) override + { + ui::BasicTreeViewItem::build_row(row); + + if (!is_active()) { + return; + } + + PointerRNA *props; + const CatalogID catalog_id = catalog_item_.get_catalog_id(); + + props = UI_but_extra_operator_icon_add( + button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str()); + + /* Tree items without a catalog ID represent components of catalog paths that are not + * associated with an actual catalog. They exist merely by the presence of a child catalog, and + * thus cannot be deleted themselves. */ + if (!BLI_uuid_is_nil(catalog_id)) { + char catalog_id_str_buffer[UUID_STRING_LEN] = ""; + BLI_uuid_format(catalog_id_str_buffer, catalog_id); + + props = UI_but_extra_operator_icon_add( + button(), "ASSET_OT_catalog_delete", WM_OP_INVOKE_DEFAULT, ICON_X); + RNA_string_set(props, "catalog_id", catalog_id_str_buffer); + } + } +}; + +/** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level + * catalog. */ +class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { + using BasicTreeViewItem::BasicTreeViewItem; + + void build_row(uiLayout &row) override + { + ui::BasicTreeViewItem::build_row(row); + + if (!is_active()) { + return; + } + + PointerRNA *props; + props = UI_but_extra_operator_icon_add( + button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + /* No parent path to use the root level. */ + RNA_string_set(props, "parent_path", nullptr); + } +}; + +AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params) + : catalog_tree_(BKE_asset_library_get_catalog_tree(library)), params_(params) +{ +} + +void AssetCatalogTreeView::build_tree() +{ + add_all_item(); + + if (catalog_tree_) { + catalog_tree_->foreach_root_item([this](AssetCatalogTreeItem &item) { + ui::BasicTreeViewItem &child_view_item = build_catalog_items_recursive(*this, item); + + /* Open root-level items by default. */ + child_view_item.set_collapsed(false); + }); + } + + add_unassigned_item(); +} + +ui::BasicTreeViewItem &AssetCatalogTreeView::build_catalog_items_recursive( + ui::TreeViewItemContainer &view_parent_item, AssetCatalogTreeItem &catalog) +{ + ui::BasicTreeViewItem &view_item = view_parent_item.add_tree_item( + &catalog); + if (is_active_catalog(catalog.get_catalog_id())) { + view_item.set_active(); + } + + catalog.foreach_child([&view_item, this](AssetCatalogTreeItem &child) { + build_catalog_items_recursive(view_item, child); + }); + return view_item; +} + +void AssetCatalogTreeView::add_all_item() +{ + FileAssetSelectParams *params = params_; + + ui::AbstractTreeViewItem &item = add_tree_item( + IFACE_("All"), ICON_HOME, [params](ui::BasicTreeViewItem & /*item*/) { + params->asset_catalog_visibility = FILE_SHOW_ASSETS_ALL_CATALOGS; + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + }); + if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS) { + item.set_active(); + } +} + +void AssetCatalogTreeView::add_unassigned_item() +{ + FileAssetSelectParams *params = params_; + + ui::AbstractTreeViewItem &item = add_tree_item( + IFACE_("Unassigned"), ICON_FILE_HIDDEN, [params](ui::BasicTreeViewItem & /*item*/) { + params->asset_catalog_visibility = FILE_SHOW_ASSETS_WITHOUT_CATALOG; + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + }); + if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG) { + item.set_active(); + } +} + +bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const +{ + return (params_->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG) && + (params_->catalog_id == catalog_id); +} + +} // namespace blender::ed::asset_browser + +/* ---------------------------------------------------------------------- */ + +void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library, + uiLayout *layout, + FileAssetSelectParams *params) +{ + uiBlock *block = uiLayoutGetBlock(layout); + + ui::AbstractTreeView *tree_view = UI_block_add_view( + *block, + "asset catalog tree view", + std::make_unique(asset_library, params)); + + ui::TreeViewBuilder builder(*block); + builder.build_tree_view(*tree_view); +} diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 65e0ad94c72..d39aefff691 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -31,8 +31,11 @@ extern "C" { struct ARegion; struct ARegionType; +struct AssetLibrary; struct FileSelectParams; +struct FileAssetSelectParams; struct SpaceFile; +struct uiLayout; struct View2D; /* file_draw.c */ @@ -151,12 +154,19 @@ void file_on_reload_callback_register(struct SpaceFile *sfile, /* file_panels.c */ void file_tool_props_region_panels_register(struct ARegionType *art); void file_execute_region_panels_register(struct ARegionType *art); +void file_tools_region_panels_register(struct ARegionType *art); /* file_utils.c */ void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds); void file_path_to_ui_path(const char *path, char *r_pathi, int max_size); +/* asset_catalog_tree_view.cc */ + +void file_create_asset_catalog_tree_view_in_layout(struct AssetLibrary *asset_library, + struct uiLayout *layout, + struct FileAssetSelectParams *params); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index 7032d55b331..95aad202f1a 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -47,6 +47,7 @@ #include "WM_types.h" #include "file_intern.h" +#include "filelist.h" #include "fsmenu.h" #include @@ -57,6 +58,12 @@ static bool file_panel_operator_poll(const bContext *C, PanelType *UNUSED(pt)) return (sfile && sfile->op); } +static bool file_panel_asset_browsing_poll(const bContext *C, PanelType *UNUSED(pt)) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + return sfile && sfile->files && ED_fileselect_is_asset_browser(sfile); +} + static void file_panel_operator_header(const bContext *C, Panel *panel) { SpaceFile *sfile = CTX_wm_space_file(C); @@ -222,3 +229,28 @@ void file_execute_region_panels_register(ARegionType *art) pt->draw = file_panel_execution_buttons_draw; BLI_addtail(&art->paneltypes, pt); } + +static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *panel) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + /* May be null if the library wasn't loaded yet. */ + struct AssetLibrary *asset_library = filelist_asset_library(sfile->files); + FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile); + BLI_assert(params != NULL); + + file_create_asset_catalog_tree_view_in_layout(asset_library, panel->layout, params); +} + +void file_tools_region_panels_register(ARegionType *art) +{ + PanelType *pt; + + pt = MEM_callocN(sizeof(PanelType), "spacetype file asset catalog buttons"); + strcpy(pt->idname, "FILE_PT_asset_catalog_buttons"); + strcpy(pt->label, N_("Asset Catalogs")); + strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->flag = PANEL_TYPE_NO_HEADER; + pt->poll = file_panel_asset_browsing_poll; + pt->draw = file_panel_asset_catalog_buttons_draw; + BLI_addtail(&art->paneltypes, pt); +} diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 60fe5364aba..91178b85823 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -50,6 +50,7 @@ #include "BLI_task.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_uuid.h" #ifdef WIN32 # include "BLI_winstuff.h" @@ -369,6 +370,9 @@ typedef struct FileListFilter { char filter_glob[FILE_MAXFILE]; char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */ short flags; + + eFileSel_Params_AssetCatalogVisibility asset_catalog_visibility; + bUUID asset_catalog_id; } FileListFilter; /* FileListFilter.flags */ @@ -806,6 +810,22 @@ static bool is_filtered_hidden(const char *filename, return true; } + /* TODO Make catalog activation work properly with the "Current File" asset library. Currently + * this will only work for external asset data. */ + if (file->imported_asset_data) { + switch (filter->asset_catalog_visibility) { + case FILE_SHOW_ASSETS_WITHOUT_CATALOG: + return !BLI_uuid_is_nil(file->imported_asset_data->catalog_id); + case FILE_SHOW_ASSETS_FROM_CATALOG: + /* TODO show all assets that are in child catalogs of the selected catalog. */ + return BLI_uuid_is_nil(filter->asset_catalog_id) || + !BLI_uuid_equal(filter->asset_catalog_id, file->imported_asset_data->catalog_id); + case FILE_SHOW_ASSETS_ALL_CATALOGS: + /* All asset files should be visible. */ + break; + } + } + return false; } @@ -1037,6 +1057,33 @@ void filelist_setfilter_options(FileList *filelist, } } +/** + * \param catalog_id: The catalog that should be filtered by if \a catalog_visibility is + * #FILE_SHOW_ASSETS_FROM_CATALOG. May be NULL otherwise. + */ +void filelist_set_asset_catalog_filter_options( + FileList *filelist, + eFileSel_Params_AssetCatalogVisibility catalog_visibility, + const bUUID *catalog_id) +{ + bool update = false; + + if (filelist->filter_data.asset_catalog_visibility != catalog_visibility) { + filelist->filter_data.asset_catalog_visibility = catalog_visibility; + update = true; + } + + if (filelist->filter_data.asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG && + catalog_id && !BLI_uuid_equal(filelist->filter_data.asset_catalog_id, *catalog_id)) { + filelist->filter_data.asset_catalog_id = *catalog_id; + update = true; + } + + if (update) { + filelist_filter_clear(filelist); + } +} + /** * Checks two libraries for equality. * \return True if the libraries match. @@ -1809,6 +1856,11 @@ void filelist_free(struct FileList *filelist) filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING); } +AssetLibrary *filelist_asset_library(FileList *filelist) +{ + return filelist->asset_library; +} + void filelist_freelib(struct FileList *filelist) { if (filelist->libfiledata) { diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index b51ceee4aa0..d1f37b5b365 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -31,6 +31,7 @@ struct AssetLibraryReference; struct BlendHandle; struct FileList; struct FileSelection; +struct bUUID; struct wmWindowManager; struct FileDirEntry; @@ -71,6 +72,10 @@ void filelist_setfilter_options(struct FileList *filelist, const bool filter_assets_only, const char *filter_glob, const char *filter_search); +void filelist_set_asset_catalog_filter_options( + struct FileList *filelist, + eFileSel_Params_AssetCatalogVisibility catalog_visibility, + const struct bUUID *catalog_id); void filelist_filter(struct FileList *filelist); void filelist_setlibrary(struct FileList *filelist, const struct AssetLibraryReference *asset_library_ref); @@ -144,6 +149,8 @@ void filelist_entry_parent_select_set(struct FileList *filelist, void filelist_setrecursion(struct FileList *filelist, const int recursion_level); +struct AssetLibrary *filelist_asset_library(struct FileList *filelist); + struct BlendHandle *filelist_lib(struct FileList *filelist); bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group); void filelist_freelib(struct FileList *filelist); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 2ca08a3105c..83b33fe8aa9 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -126,13 +126,10 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) FileSelectParams *base_params = &asset_params->base_params; base_params->file[0] = '\0'; base_params->filter_glob[0] = '\0'; - /* TODO: this way of using filters to form categories is notably slower than specifying a - * "group" to read. That's because all types are read and filtering is applied afterwards. Would - * be nice if we could lazy-read individual groups. */ base_params->flag |= U_default.file_space_data.flag | FILE_ASSETS_ONLY | FILE_FILTER; base_params->flag &= ~FILE_DIRSEL_ONLY; base_params->filter |= FILE_TYPE_BLENDERLIB; - base_params->filter_id = FILTER_ID_OB | FILTER_ID_GR; + base_params->filter_id = FILTER_ID_ALL; base_params->display = FILE_IMGDISPLAY; base_params->sort = FILE_SORT_ALPHA; /* Asset libraries include all sub-directories, so enable maximal recursion. */ @@ -462,6 +459,15 @@ bool ED_fileselect_is_asset_browser(const SpaceFile *sfile) return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS); } +struct AssetLibrary *ED_fileselect_active_asset_library_get(const SpaceFile *sfile) +{ + if (!ED_fileselect_is_asset_browser(sfile) || !sfile->files) { + return NULL; + } + + return filelist_asset_library(sfile->files); +} + struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile) { if (!ED_fileselect_is_asset_browser(sfile)) { diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index ac23767f933..a563f24e24e 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -335,9 +335,9 @@ static void file_refresh(const bContext *C, ScrArea *area) params->highlight_file = -1; /* added this so it opens nicer (ton) */ } - if (!U.experimental.use_extended_asset_browser && ED_fileselect_is_asset_browser(sfile)) { + if (ED_fileselect_is_asset_browser(sfile)) { /* Only poses supported as non-experimental right now. */ - params->filter_id = FILTER_ID_AC; + params->filter_id = U.experimental.use_extended_asset_browser ? FILTER_ID_ALL : FILTER_ID_AC; } filelist_settype(sfile->files, params->type); @@ -355,6 +355,10 @@ static void file_refresh(const bContext *C, ScrArea *area) (params->flag & FILE_ASSETS_ONLY) != 0, params->filter_glob, params->filter_search); + if (asset_params) { + filelist_set_asset_catalog_filter_options( + sfile->files, asset_params->asset_catalog_visibility, &asset_params->catalog_id); + } /* Update the active indices of bookmarks & co. */ sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir); @@ -1050,6 +1054,7 @@ void ED_spacetype_file(void) art->init = file_tools_region_init; art->draw = file_tools_region_draw; BLI_addhead(&st->regiontypes, art); + file_tools_region_panels_register(art); /* regions: tool properties */ art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region"); diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 2f3f52a6b82..70a1ca63e21 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -806,9 +806,14 @@ typedef struct FileAssetSelectParams { FileSelectParams base_params; AssetLibraryReference asset_library_ref; + short asset_catalog_visibility; /* eFileSel_Params_AssetCatalogVisibility */ + char _pad[6]; + /** If #asset_catalog_visibility is #FILE_SHOW_ASSETS_FROM_CATALOG, this sets the ID of the + * catalog to show. */ + bUUID catalog_id; short import_type; /* eFileAssetImportType */ - char _pad[6]; + char _pad2[6]; } FileAssetSelectParams; typedef enum eFileAssetImportType { @@ -1009,8 +1014,16 @@ typedef enum eFileSel_Params_Flag { FILE_HIDE_TOOL_PROPS = (1 << 12), FILE_CHECK_EXISTING = (1 << 13), FILE_ASSETS_ONLY = (1 << 14), + /** Enables filtering by asset catalog. */ + FILE_FILTER_ASSET_CATALOG = (1 << 15), } eFileSel_Params_Flag; +typedef enum eFileSel_Params_AssetCatalogVisibility { + FILE_SHOW_ASSETS_ALL_CATALOGS, + FILE_SHOW_ASSETS_FROM_CATALOG, + FILE_SHOW_ASSETS_WITHOUT_CATALOG, +} eFileSel_Params_AssetCatalogVisibility; + /* sfile->params->rename_flag */ /* NOTE: short flag. Defined as bitflags, but currently only used as exclusive status markers... */ typedef enum eFileSel_Params_RenameFlag { diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 9e06533d41b..78c26df7be6 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2614,18 +2614,6 @@ static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int val params->asset_library_ref = ED_asset_library_reference_from_enum_value(value); } -static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value) -{ - FileSelectParams *params = ptr->data; - params->filter_id = value; -} - -static uint64_t rna_FileAssetSelectParams_asset_category_get(PointerRNA *ptr) -{ - FileSelectParams *params = ptr->data; - return params->filter_id; -} - static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr) { const FileDirEntry *entry = ptr->data; @@ -6595,47 +6583,6 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - /* XXX copied from rna_enum_id_type_filter_items. */ - static const EnumPropertyItem asset_category_items[] = { - {FILTER_ID_SCE, "SCENES", ICON_SCENE_DATA, "Scenes", "Show scenes"}, - {FILTER_ID_AC, "ANIMATIONS", ICON_ANIM_DATA, "Animations", "Show animation data"}, - {FILTER_ID_OB | FILTER_ID_GR, - "OBJECTS_AND_COLLECTIONS", - ICON_GROUP, - "Objects & Collections", - "Show objects and collections"}, - {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME - /* XXX avoid warning */ - // | FILTER_ID_HA | FILTER_ID_PT | FILTER_ID_VO - , - "GEOMETRY", - ICON_MESH_DATA, - "Geometry", - "Show meshes, curves, lattice, armatures and metaballs data"}, - {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE, - "SHADING", - ICON_MATERIAL_DATA, - "Shading", - "Show materials, nodetrees, textures and Freestyle's linestyles"}, - {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO, - "IMAGES_AND_SOUNDS", - ICON_IMAGE_DATA, - "Images & Sounds", - "Show images, movie clips, sounds and masks"}, - {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO, - "ENVIRONMENTS", - ICON_WORLD_DATA, - "Environment", - "Show worlds, lights, cameras and speakers"}, - {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | - FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS, - "MISC", - ICON_GREASEPENCIL, - "Miscellaneous", - "Show other data types"}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem asset_import_type_items[] = { {FILE_ASSET_IMPORT_LINK, "LINK", 0, "Link", "Import the assets as linked data-block"}, {FILE_ASSET_IMPORT_APPEND, @@ -6664,15 +6611,6 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Asset Library", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); - prop = RNA_def_property(srna, "asset_category", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, asset_category_items); - RNA_def_property_enum_funcs(prop, - "rna_FileAssetSelectParams_asset_category_get", - "rna_FileAssetSelectParams_asset_category_set", - NULL); - RNA_def_property_ui_text(prop, "Asset Category", "Determine which kind of assets to display"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL); - prop = RNA_def_property(srna, "import_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, asset_import_type_items); RNA_def_property_ui_text(prop, "Import Type", "Determine how the asset will be imported"); From 45a312fd8fb98b970cb999c7afa53f83c66b97ec Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 29 Sep 2021 17:49:06 +0200 Subject: [PATCH 0366/1500] Fix build failure on Windows + wrong buffer size Was using `NAME_MAX` which is defined in `limits.h`, which again may be implicitly included by the compiler. Intend was to use the Blender define `MAX_NAME`. --- source/blender/editors/asset/intern/asset_catalog.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index 202d4234051..68f11d77f44 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -53,7 +53,7 @@ static std::string catalog_name_ensure_unique(AssetCatalogService &catalog_servi { CatalogUniqueNameFnData fn_data = {catalog_service, parent_path}; - char unique_name[NAME_MAX] = ""; + char unique_name[MAX_NAME] = ""; BLI_uniquename_cb( catalog_name_exists_fn, &fn_data, name.c_str(), '.', unique_name, sizeof(unique_name)); From 214baf5422607edf45d0b24b6fdf3710964224f8 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 29 Sep 2021 18:51:10 +0200 Subject: [PATCH 0367/1500] Assets: Enable recursive reading for the asset view template as well This makes asset view templates, e.g. as used by the Pose Library add-on use recursive asset loading, see Also works around an issue that made assets not show up at all anymore since fc7beac8d6f4. I'm creating a separate report for that regression, but in the Asset Browser and Viewer it shouldn't be apparent currently. --- source/blender/editors/asset/intern/asset_list.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index cda117533cd..400b3572c9b 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -157,7 +157,7 @@ void AssetList::setup() /* Relevant bits from file_refresh(). */ /* TODO pass options properly. */ - filelist_setrecursion(files, 1); + filelist_setrecursion(files, FILE_SELECT_MAX_RECURSIONS); filelist_setsorting(files, FILE_SORT_ALPHA, false); filelist_setlibrary(files, &library_ref_); filelist_setfilter_options( From 1d478851f847047ad6253e8599407b138bc8d70e Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 29 Sep 2021 19:21:29 +0200 Subject: [PATCH 0368/1500] GPencil: Avoid double depsgraph tag The eval data is updated at the end of the function and this call just adds a calculation not used. --- source/blender/editors/gpencil/gpencil_paint.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 9b157224178..957d8087bbd 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1001,8 +1001,6 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); gps->dvert = NULL; - /* drawing batch cache is dirty now */ - gpencil_update_cache(p->gpd); /* set pointer to first non-initialized point */ pt = gps->points + (gps->totpoints - totelem); if (gps->dvert != NULL) { From 6f23e4484d03f75ba1618f76114b3f81d5db0ae7 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 29 Sep 2021 19:32:42 +0200 Subject: [PATCH 0369/1500] Fix non-finite curve normal causing Cycles to crash Similar to the previous change in the area: need to avoid ray point and direction becoming a non-finite value. Use the view direction when the geometrical normal can not be calculated. Collaboration and sanity inspiration with Brecht! Differential Revision: https://developer.blender.org/D12703 --- intern/cycles/kernel/geom/geom_curve_intersect.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h index b2101034bb6..a068e93790a 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/geom_curve_intersect.h @@ -764,8 +764,10 @@ ccl_device_inline void curve_shader_setup(const KernelGlobals *kg, /* Thick curves, compute normal using direction from inside the curve. * This could be optimized by recording the normal in the intersection, * however for Optix this would go beyond the size of the payload. */ + /* NOTE: It is possible that P will be the same as P_inside (precision issues, or very small + * radius). In this case use the view direction to approximate the normal. */ const float3 P_inside = float4_to_float3(catmull_rom_basis_eval(P_curve, sd->u)); - const float3 Ng = normalize(P - P_inside); + const float3 Ng = (!isequal_float3(P, P_inside)) ? normalize(P - P_inside) : -sd->I; sd->N = Ng; sd->Ng = Ng; From 22c61e80605079141293c749de37cbe85bf2b33b Mon Sep 17 00:00:00 2001 From: Himanshi Kalra Date: Wed, 29 Sep 2021 21:12:55 +0530 Subject: [PATCH 0370/1500] Tests: Disable tests for non-compiled libraries This diff disables tests for Boolean, subdivision surface and volume when GMP, Opensubdiv and Openvdb are not compiled respectively. It also changes the existing file structure and adds sub-folders for boolean and subdivison tests. The volume folder only has one test and is as unchanged structure-wise. Reviewed By: JacquesLucke, LazyDodo Differential Revision: https://developer.blender.org/D12448 --- tests/python/CMakeLists.txt | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 75f00c3c5cc..2b31b6362e9 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -749,10 +749,26 @@ set(geo_node_tests points utilities vector - volume - ) +if(WITH_GMP) + list(APPEND geo_node_tests mesh/boolean) +else() + MESSAGE(STATUS "Disabling mesh/boolean tests because WITH_GMP is off.") +endif() + +if(WITH_OPENVDB) + list(APPEND geo_node_tests volume) +else() + MESSAGE(STATUS "Disabling volume tests because WITH_OPENVDB is off.") +endif() + +if(WITH_OPENSUBDIV) + list(APPEND geo_node_tests mesh/subdivision_tests) +else() + MESSAGE(STATUS "Disabling mesh/subdivision_tests because WITH_OPENSUBDIV is off.") +endif() + foreach(geo_node_test ${geo_node_tests}) if(EXISTS "${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/") file(GLOB files "${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/*.blend") From 19785cb022f7b279dd78f2c533712f26fc0bc629 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 29 Sep 2021 19:20:11 +0200 Subject: [PATCH 0371/1500] Fix Cycles CPU performance regression after recent change for intersections size This struct is much bigger now, and does not actually need to be fully zero initialized. --- intern/cycles/integrator/path_trace_work_cpu.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp index b9a33b64051..14658d4d1ce 100644 --- a/intern/cycles/integrator/path_trace_work_cpu.cpp +++ b/intern/cycles/integrator/path_trace_work_cpu.cpp @@ -19,6 +19,8 @@ #include "device/cpu/kernel.h" #include "device/device.h" +#include "kernel/kernel_path_state.h" + #include "integrator/pass_accessor_cpu.h" #include "render/buffers.h" @@ -116,13 +118,17 @@ void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_global const KernelWorkTile &work_tile, const int samples_num) { - const bool has_shadow_catcher = device_scene_->data.integrator.has_shadow_catcher; const bool has_bake = device_scene_->data.bake.use; - IntegratorStateCPU integrator_states[2] = {}; + IntegratorStateCPU integrator_states[2]; IntegratorStateCPU *state = &integrator_states[0]; - IntegratorStateCPU *shadow_catcher_state = &integrator_states[1]; + IntegratorStateCPU *shadow_catcher_state = nullptr; + + if (device_scene_->data.integrator.has_shadow_catcher) { + shadow_catcher_state = &integrator_states[1]; + path_state_init_queues(kernel_globals, shadow_catcher_state); + } KernelWorkTile sample_work_tile = work_tile; float *render_buffer = buffers_->buffer.data(); @@ -147,7 +153,7 @@ void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_global kernels_.integrator_megakernel(kernel_globals, state, render_buffer); - if (has_shadow_catcher) { + if (shadow_catcher_state) { kernels_.integrator_megakernel(kernel_globals, shadow_catcher_state, render_buffer); } From d3d021601d7d91ddbc1ac55f733d662b5d49088c Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 29 Sep 2021 13:59:43 -0400 Subject: [PATCH 0372/1500] Nodes: Fix Split View missing identifier Mistake from rB84251acfcc4534059d6ccd6682f8e37d529b9063 --- .../blender/nodes/composite/nodes/node_composite_splitViewer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc index 27fbb0fafd4..f64abe87116 100644 --- a/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc @@ -33,7 +33,7 @@ namespace blender::nodes { static void cmp_node_splitviewer_declare(NodeDeclarationBuilder &b) { b.add_input("Image"); - b.add_input("Image"); + b.add_input("Image", "Image_001"); } } // namespace blender::nodes From a2e321aa6dff2782643cd121f42b18a0e2aaeb16 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 29 Sep 2021 14:48:21 -0400 Subject: [PATCH 0373/1500] Cleanup: Compositor: Migrate most converter nodes to new socket builder This migrates most nodes except for the switch view node. This node requires dynamic sockets so its implementation will be more involved. --- .../composite/nodes/node_composite_idMask.cc | 19 +++---- .../composite/nodes/node_composite_math.cc | 19 ++++--- .../nodes/node_composite_premulkey.cc | 21 ++++---- .../nodes/node_composite_sepcombHSVA.cc | 54 ++++++++++--------- .../nodes/node_composite_sepcombRGBA.cc | 52 +++++++++--------- .../nodes/node_composite_sepcombYCCA.cc | 50 +++++++++-------- .../nodes/node_composite_sepcombYUVA.cc | 50 +++++++++-------- .../nodes/node_composite_setalpha.cc | 22 ++++---- .../nodes/node_composite_valToRgb.cc | 42 ++++++++------- 9 files changed, 181 insertions(+), 148 deletions(-) diff --git a/source/blender/nodes/composite/nodes/node_composite_idMask.cc b/source/blender/nodes/composite/nodes/node_composite_idMask.cc index 13e3536f9ee..de011dd6274 100644 --- a/source/blender/nodes/composite/nodes/node_composite_idMask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_idMask.cc @@ -25,21 +25,22 @@ /* **************** ID Mask ******************** */ -static bNodeSocketTemplate cmp_node_idmask_in[] = { - {SOCK_FLOAT, N_("ID value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_idmask_out[] = { - {SOCK_FLOAT, N_("Alpha")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_idmask_declare(NodeDeclarationBuilder &b) +{ + b.add_input("ID value").default_value(1.0f).min(0.0f).max(1.0f); + b.add_output("Alpha"); +} + +} // namespace blender::nodes void register_node_type_cmp_idmask(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_idmask_in, cmp_node_idmask_out); + ntype.declare = blender::nodes::cmp_node_idmask_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_math.cc b/source/blender/nodes/composite/nodes/node_composite_math.cc index ecddcc2ad32..a9859425e28 100644 --- a/source/blender/nodes/composite/nodes/node_composite_math.cc +++ b/source/blender/nodes/composite/nodes/node_composite_math.cc @@ -24,20 +24,25 @@ #include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ -static bNodeSocketTemplate cmp_node_math_in[] = { - {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; +namespace blender::nodes { + +static void cmp_node_math_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Value").default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_input("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_input("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_output("Value"); +} + +} // namespace blender::nodes void register_node_type_cmp_math(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_math_in, cmp_node_math_out); + ntype.declare = blender::nodes::cmp_node_math_declare; node_type_label(&ntype, node_math_label); node_type_update(&ntype, node_math_update); diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc index 68716ee53b5..0abd7110aff 100644 --- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc +++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc @@ -25,21 +25,22 @@ /* **************** Premul and Key Alpha Convert ******************** */ -static bNodeSocketTemplate cmp_node_premulkey_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_premulkey_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Image"); +} + +} // namespace blender::nodes void register_node_type_cmp_premulkey(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_premulkey_in, cmp_node_premulkey_out); - + ntype.declare = blender::nodes::cmp_node_premulkey_declare; + nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc index 17b1ab9ac59..06952d3b6d9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc @@ -24,47 +24,51 @@ #include "node_composite_util.hh" /* **************** SEPARATE HSVA ******************** */ -static bNodeSocketTemplate cmp_node_sephsva_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_sephsva_out[] = { - {SOCK_FLOAT, N_("H")}, - {SOCK_FLOAT, N_("S")}, - {SOCK_FLOAT, N_("V")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("H"); + b.add_output("S"); + b.add_output("V"); + b.add_output("A"); + +} + +} // namespace blender::nodes void register_node_type_cmp_sephsva(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_sephsva_in, cmp_node_sephsva_out); - + ntype.declare = blender::nodes::cmp_node_sephsva_declare; nodeRegisterType(&ntype); } /* **************** COMBINE HSVA ******************** */ -static bNodeSocketTemplate cmp_node_combhsva_in[] = { - {SOCK_FLOAT, N_("H"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("S"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("V"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combhsva_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b) +{ + b.add_input("H").min(0.0f).max(1.0f); + b.add_input("S").min(0.0f).max(1.0f); + b.add_input("V").min(0.0f).max(1.0f); + b.add_input("A").default_value(1.0f).min(0.0f).max(1.0f); + b.add_output("Image"); +} + +} // namespace blender::nodes void register_node_type_cmp_combhsva(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combhsva_in, cmp_node_combhsva_out); + ntype.declare = blender::nodes::cmp_node_combhsva_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc index d3a021ed7ba..d119b637595 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc @@ -24,47 +24,51 @@ #include "node_composite_util.hh" /* **************** SEPARATE RGBA ******************** */ -static bNodeSocketTemplate cmp_node_seprgba_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_seprgba_out[] = { - {SOCK_FLOAT, N_("R")}, - {SOCK_FLOAT, N_("G")}, - {SOCK_FLOAT, N_("B")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("R"); + b.add_output("G"); + b.add_output("B"); + b.add_output("A"); + +} + +} // namespace blender::nodes void register_node_type_cmp_seprgba(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_seprgba_in, cmp_node_seprgba_out); + ntype.declare = blender::nodes::cmp_node_seprgba_declare; nodeRegisterType(&ntype); } /* **************** COMBINE RGBA ******************** */ -static bNodeSocketTemplate cmp_node_combrgba_in[] = { - {SOCK_FLOAT, N_("R"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("G"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combrgba_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b) +{ + b.add_input("R").min(0.0f).max(1.0f); + b.add_input("G").min(0.0f).max(1.0f); + b.add_input("B").min(0.0f).max(1.0f); + b.add_input("A").default_value(1.0f).min(0.0f).max(1.0f); + b.add_output("Image"); +} + +} // namespace blender::nodes void register_node_type_cmp_combrgba(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combrgba_in, cmp_node_combrgba_out); + ntype.declare = blender::nodes::cmp_node_combrgba_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc index 2090841b3b9..526d6b4eb5b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc @@ -24,15 +24,19 @@ #include "node_composite_util.hh" /* **************** SEPARATE YCCA ******************** */ -static bNodeSocketTemplate cmp_node_sepycca_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}}; -static bNodeSocketTemplate cmp_node_sepycca_out[] = { - {SOCK_FLOAT, N_("Y")}, - {SOCK_FLOAT, N_("Cb")}, - {SOCK_FLOAT, N_("Cr")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Y"); + b.add_output("Cb"); + b.add_output("Cr"); + b.add_output("A"); +} + +} // namespace blender::nodes static void node_composit_init_mode_sepycca(bNodeTree *UNUSED(ntree), bNode *node) { @@ -44,24 +48,26 @@ void register_node_type_cmp_sepycca(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_sepycca_in, cmp_node_sepycca_out); + ntype.declare = blender::nodes::cmp_node_sepycca_declare; node_type_init(&ntype, node_composit_init_mode_sepycca); nodeRegisterType(&ntype); } /* **************** COMBINE YCCA ******************** */ -static bNodeSocketTemplate cmp_node_combycca_in[] = { - {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Cb"), 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Cr"), 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combycca_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_combycca_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Y").min(0.0f).max(1.0f); + b.add_input("Cb").default_value(0.5f).min(0.0f).max(1.0f); + b.add_input("Cr").default_value(0.5f).min(0.0f).max(1.0f); + b.add_input("A").default_value(1.0f).min(0.0f).max(1.0f); + b.add_output("Image"); +} + +} // namespace blender::nodes static void node_composit_init_mode_combycca(bNodeTree *UNUSED(ntree), bNode *node) { @@ -73,7 +79,7 @@ void register_node_type_cmp_combycca(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combycca_in, cmp_node_combycca_out); + ntype.declare = blender::nodes::cmp_node_combycca_declare; node_type_init(&ntype, node_composit_init_mode_combycca); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc index 59982b81468..4619b0c97f1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc @@ -24,45 +24,51 @@ #include "node_composite_util.hh" /* **************** SEPARATE YUVA ******************** */ -static bNodeSocketTemplate cmp_node_sepyuva_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}}; -static bNodeSocketTemplate cmp_node_sepyuva_out[] = { - {SOCK_FLOAT, N_("Y")}, - {SOCK_FLOAT, N_("U")}, - {SOCK_FLOAT, N_("V")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output("Y"); + b.add_output("U"); + b.add_output("V"); + b.add_output("A"); +} + +} // namespace blender::nodes void register_node_type_cmp_sepyuva(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_sepyuva_in, cmp_node_sepyuva_out); + ntype.declare = blender::nodes::cmp_node_sepyuva_declare; nodeRegisterType(&ntype); } /* **************** COMBINE YUVA ******************** */ -static bNodeSocketTemplate cmp_node_combyuva_in[] = { - {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("U"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("V"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combyuva_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Y").min(0.0f).max(1.0f); + b.add_input("U").min(0.0f).max(1.0f); + b.add_input("V").min(0.0f).max(1.0f); + b.add_input("A").default_value(1.0f).min(0.0f).max(1.0f); + b.add_output("Image"); +} + +} // namespace blender::nodes void register_node_type_cmp_combyuva(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combyuva_in, cmp_node_combyuva_out); + ntype.declare = blender::nodes::cmp_node_combyuva_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc index a2089bd0913..5d2490b0c81 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -24,15 +24,17 @@ #include "node_composite_util.hh" /* **************** SET ALPHA ******************** */ -static bNodeSocketTemplate cmp_node_setalpha_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_setalpha_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Alpha").default_value(1.0f).min(0.0f).max(1.0f); + b.add_output("Image"); +} + +} // namespace blender::nodes static void node_composit_init_setalpha(bNodeTree *UNUSED(ntree), bNode *node) { @@ -46,7 +48,7 @@ void register_node_type_cmp_setalpha(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SETALPHA, "Set Alpha", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_setalpha_in, cmp_node_setalpha_out); + ntype.declare = blender::nodes::cmp_node_setalpha_declare; node_type_init(&ntype, node_composit_init_setalpha); node_type_storage( &ntype, "NodeSetAlpha", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc index ece52dea269..ba98ee12f30 100644 --- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc @@ -24,15 +24,17 @@ #include "node_composite_util.hh" /* **************** VALTORGB ******************** */ -static bNodeSocketTemplate cmp_node_valtorgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_valtorgb_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Alpha")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output("Image"); + b.add_output("Alpha"); +} + +} // namespace blender::nodes static void node_composit_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node) { @@ -44,7 +46,7 @@ void register_node_type_cmp_valtorgb(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_valtorgb_in, cmp_node_valtorgb_out); + ntype.declare = blender::nodes::cmp_node_valtorgb_declare; node_type_size(&ntype, 240, 200, 320); node_type_init(&ntype, node_composit_init_valtorgb); node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); @@ -53,21 +55,23 @@ void register_node_type_cmp_valtorgb(void) } /* **************** RGBTOBW ******************** */ -static bNodeSocketTemplate cmp_node_rgbtobw_in[] = { - {SOCK_RGBA, N_("Image"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_rgbtobw_out[] = { - {SOCK_FLOAT, N_("Val"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output("Val"); +} + +} // namespace blender::nodes void register_node_type_cmp_rgbtobw(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_rgbtobw_in, cmp_node_rgbtobw_out); + ntype.declare = blender::nodes::cmp_node_rgbtobw_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); nodeRegisterType(&ntype); From 0cddbcf1d7054c10c483061d9eaf92eedca3d976 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 29 Sep 2021 21:13:03 +0200 Subject: [PATCH 0374/1500] Fix T91803: Freestyle rendering as pass broken after recent changes --- .../blender_interface/FRS_freestyle.cpp | 4 +- source/blender/render/RE_pipeline.h | 3 +- source/blender/render/intern/engine.c | 11 ++- source/blender/render/intern/pipeline.c | 2 +- source/blender/render/intern/render_result.c | 70 +++++++++++-------- source/blender/render/intern/render_result.h | 3 +- 6 files changed, 53 insertions(+), 40 deletions(-) diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index c74fd60fe35..405deaf00b0 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -494,7 +494,7 @@ void FRS_composite_result(Render *re, ViewLayer *view_layer, Render *freestyle_r if (view_layer->freestyle_config.flags & FREESTYLE_AS_RENDER_PASS) { // Create a blank render pass output. RE_create_render_pass( - re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname); + re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname, true); } return; } @@ -530,7 +530,7 @@ void FRS_composite_result(Render *re, ViewLayer *view_layer, Render *freestyle_r if (view_layer->freestyle_config.flags & FREESTYLE_AS_RENDER_PASS) { RE_create_render_pass( - re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname); + re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname, true); dest = RE_RenderLayerGetPass(rl, RE_PASSNAME_FREESTYLE, re->viewname); } else { diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index 3237772dd80..0d2d93ae026 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -233,7 +233,8 @@ void RE_create_render_pass(struct RenderResult *rr, int channels, const char *chan_id, const char *layername, - const char *viewname); + const char *viewname, + const bool allocate); /* obligatory initialize call, disprect is optional */ void RE_InitState(struct Render *re, diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 389b821ca35..790c46dad0f 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -207,11 +207,10 @@ static RenderResult *render_result_from_bake(RenderEngine *engine, int x, int y, /* Add render passes. */ RenderPass *result_pass = render_layer_add_pass( - rr, rl, engine->bake.depth, RE_PASSNAME_COMBINED, "", "RGBA"); - RenderPass *primitive_pass = render_layer_add_pass(rr, rl, 4, "BakePrimitive", "", "RGBA"); - RenderPass *differential_pass = render_layer_add_pass(rr, rl, 4, "BakeDifferential", "", "RGBA"); - - render_result_passes_allocated_ensure(rr); + rr, rl, engine->bake.depth, RE_PASSNAME_COMBINED, "", "RGBA", true); + RenderPass *primitive_pass = render_layer_add_pass(rr, rl, 4, "BakePrimitive", "", "RGBA", true); + RenderPass *differential_pass = render_layer_add_pass( + rr, rl, 4, "BakeDifferential", "", "RGBA", true); /* Fill render passes from bake pixel array, to be read by the render engine. */ for (int ty = 0; ty < h; ty++) { @@ -414,7 +413,7 @@ void RE_engine_add_pass(RenderEngine *engine, return; } - RE_create_render_pass(re->result, name, channels, chan_id, layername, NULL); + RE_create_render_pass(re->result, name, channels, chan_id, layername, NULL, false); } void RE_engine_end_result( diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 931282e26dd..7c5259a1c5c 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -2817,7 +2817,7 @@ RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const cha BLI_freelinkN(&rl->passes, rp); } /* create a totally new pass */ - return render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname, "RGBA"); + return render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname, "RGBA", true); } bool RE_allow_render_generic_object(Object *ob) diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index db14f7a2982..0681bcd9aa5 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -213,12 +213,37 @@ static void set_pass_full_name( /********************************** New **************************************/ +static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp) +{ + if (rp->rect != NULL) { + return; + } + + const size_t rectsize = ((size_t)rr->rectx) * rr->recty * rp->channels; + rp->rect = MEM_callocN(sizeof(float) * rectsize, rp->name); + + if (STREQ(rp->name, RE_PASSNAME_VECTOR)) { + /* initialize to max speed */ + float *rect = rp->rect; + for (int x = rectsize - 1; x >= 0; x--) { + rect[x] = PASS_VECTOR_MAX; + } + } + else if (STREQ(rp->name, RE_PASSNAME_Z)) { + float *rect = rp->rect; + for (int x = rectsize - 1; x >= 0; x--) { + rect[x] = 10e10; + } + } +} + RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, const char *name, const char *viewname, - const char *chan_id) + const char *chan_id, + const bool allocate) { const int view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name)); RenderPass *rpass = MEM_callocN(sizeof(RenderPass), name); @@ -250,8 +275,13 @@ RenderPass *render_layer_add_pass(RenderResult *rr, BLI_addtail(&rl->passes, rpass); - /* The result contains non-allocated pass now, so tag it as such. */ - rr->passes_allocated = false; + if (allocate) { + render_layer_allocate_pass(rr, rpass); + } + else { + /* The result contains non-allocated pass now, so tag it as such. */ + rr->passes_allocated = false; + } return rpass; } @@ -323,14 +353,14 @@ RenderResult *render_result_new(Render *re, #define RENDER_LAYER_ADD_PASS_SAFE(rr, rl, channels, name, viewname, chan_id) \ do { \ - if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id) == NULL) { \ + if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id, false) == NULL) { \ render_result_free(rr); \ return NULL; \ } \ } while (false) /* A renderlayer should always have a Combined pass. */ - render_layer_add_pass(rr, rl, 4, "Combined", view, "RGBA"); + render_layer_add_pass(rr, rl, 4, "Combined", view, "RGBA", false); if (view_layer->passflag & SCE_PASS_Z) { RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_Z, view, "Z"); @@ -427,7 +457,7 @@ RenderResult *render_result_new(Render *re, } /* a renderlayer should always have a Combined pass */ - render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, view, "RGBA"); + render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, view, "RGBA", false); } /* NOTE: this has to be in sync with `scene.c`. */ @@ -453,26 +483,7 @@ void render_result_passes_allocated_ensure(RenderResult *rr) continue; } - if (rp->rect != NULL) { - continue; - } - - const size_t rectsize = ((size_t)rr->rectx) * rr->recty * rp->channels; - rp->rect = MEM_callocN(sizeof(float) * rectsize, rp->name); - - if (STREQ(rp->name, RE_PASSNAME_VECTOR)) { - /* initialize to max speed */ - float *rect = rp->rect; - for (int x = rectsize - 1; x >= 0; x--) { - rect[x] = PASS_VECTOR_MAX; - } - } - else if (STREQ(rp->name, RE_PASSNAME_Z)) { - float *rect = rp->rect; - for (int x = rectsize - 1; x >= 0; x--) { - rect[x] = 10e10; - } - } + render_layer_allocate_pass(rr, rp); } } @@ -501,7 +512,7 @@ void render_result_clone_passes(Render *re, RenderResult *rr, const char *viewna &rl->passes, main_rp->fullname, offsetof(RenderPass, fullname)); if (!rp) { render_layer_add_pass( - rr, rl, main_rp->channels, main_rp->name, main_rp->view, main_rp->chan_id); + rr, rl, main_rp->channels, main_rp->name, main_rp->view, main_rp->chan_id, false); } } } @@ -512,7 +523,8 @@ void RE_create_render_pass(RenderResult *rr, int channels, const char *chan_id, const char *layername, - const char *viewname) + const char *viewname, + const bool allocate) { RenderLayer *rl; RenderPass *rp; @@ -542,7 +554,7 @@ void RE_create_render_pass(RenderResult *rr, } if (!rp) { - render_layer_add_pass(rr, rl, channels, name, view, chan_id); + render_layer_add_pass(rr, rl, channels, name, view, chan_id, allocate); } } } diff --git a/source/blender/render/intern/render_result.h b/source/blender/render/intern/render_result.h index 4145bb3b8ab..34b8143869c 100644 --- a/source/blender/render/intern/render_result.h +++ b/source/blender/render/intern/render_result.h @@ -83,7 +83,8 @@ struct RenderPass *render_layer_add_pass(struct RenderResult *rr, int channels, const char *name, const char *viewname, - const char *chan_id); + const char *chan_id, + const bool allocate); int render_result_exr_file_read_path(struct RenderResult *rr, struct RenderLayer *rl_single, From cd03f5b6e572640f6e182c4989fe20ced156effa Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 29 Sep 2021 17:06:45 +0200 Subject: [PATCH 0375/1500] Add RNA path funcs for VolumeRender & VolumeDisplay Without proper RNA paths, Alt-click editing properties on multiple selected objects doesn not work (as well as the 'Copy To Selected' operator). Fixes T91806. Maniphest Tasks: T91806 Differential Revision: https://developer.blender.org/D12700 --- source/blender/makesrna/intern/rna_volume.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c index 8ed53c9f70f..f6b8b55688c 100644 --- a/source/blender/makesrna/intern/rna_volume.c +++ b/source/blender/makesrna/intern/rna_volume.c @@ -41,6 +41,16 @@ # include "WM_api.h" # include "WM_types.h" +static char *rna_VolumeRender_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("render"); +} + +static char *rna_VolumeDisplay_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("display"); +} + /* Updates */ static void rna_Volume_update_display(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) @@ -371,6 +381,7 @@ static void rna_def_volume_display(BlenderRNA *brna) srna = RNA_def_struct(brna, "VolumeDisplay", NULL); RNA_def_struct_ui_text(srna, "Volume Display", "Volume object display settings for 3D viewport"); RNA_def_struct_sdna(srna, "VolumeDisplay"); + RNA_def_struct_path_func(srna, "rna_VolumeDisplay_path"); prop = RNA_def_property(srna, "density", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -477,6 +488,7 @@ static void rna_def_volume_render(BlenderRNA *brna) srna = RNA_def_struct(brna, "VolumeRender", NULL); RNA_def_struct_ui_text(srna, "Volume Render", "Volume object render settings"); RNA_def_struct_sdna(srna, "VolumeRender"); + RNA_def_struct_path_func(srna, "rna_VolumeRender_path"); static const EnumPropertyItem space_items[] = { {VOLUME_SPACE_OBJECT, From 81f552e9ad1ab5705ef69cf8e7ff7ee67575d45f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 29 Sep 2021 15:29:29 -0500 Subject: [PATCH 0376/1500] Geometry Nodes: Expose Bezier handle positions as an attribute This commit exposes left and right bezier handles as an attribute. Interaction basically works like edit mode. If you move an aligned handle, it also moves the opposite handle of the control point. The difference is that you can't edit "Auto" or "Vector" handles, you have to first use the "Set Handle Type" node. That gives the handle types a bit more meaning in the node tree-- changing them in edit mod is more like a "UI override". The attributes are named `handle_start` and `handle_end`, which is the same name used in the curve RNA API. A new virtual array implementation is added which handles the case of splines that don't have these attributes, and it also calls two new functions on `BezierSpline` to set the handle position accounting for aligned handles. The virtual arrays and attribute providers will be refactored (probably templated) in the future, as a next step after the last built-in curve attribute provider has landed. Differential Revision: https://developer.blender.org/D12005 --- source/blender/blenkernel/BKE_spline.hh | 3 + .../intern/geometry_component_curve.cc | 283 +++++++++++++++++- .../intern/geometry_set_instances.cc | 2 +- .../blenkernel/intern/spline_bezier.cc | 50 ++++ .../geometry/nodes/node_geo_join_geometry.cc | 2 +- 5 files changed, 332 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 541ff19c1cd..97e0d8415a5 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -316,6 +316,9 @@ class BezierSpline final : public Spline { void translate(const blender::float3 &translation) override; void transform(const blender::float4x4 &matrix) override; + void set_handle_position_right(const int index, const blender::float3 &value); + void set_handle_position_left(const int index, const blender::float3 &value); + bool point_is_sharp(const int index) const; void mark_cache_invalid() final; diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 7d0537178ef..73c628d3f0f 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -535,6 +535,9 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve) * array implementations try to make it workable in common situations. * \{ */ +/** + * Individual spans in \a data may be empty if that spline contains no data for the attribute. + */ template static void point_attribute_materialize(Span> data, Span offsets, @@ -546,7 +549,15 @@ static void point_attribute_materialize(Span> data, for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; - r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]); + + Span src = data[spline_index]; + MutableSpan dst = r_span.slice(offset, next_offset - offset); + if (src.is_empty()) { + dst.fill(T()); + } + else { + dst.copy_from(src); + } } } else { @@ -557,11 +568,20 @@ static void point_attribute_materialize(Span> data, } const int index_in_spline = dst_index - offsets[spline_index]; - r_span[dst_index] = data[spline_index][index_in_spline]; + Span src = data[spline_index]; + if (src.is_empty()) { + r_span[dst_index] = T(); + } + else { + r_span[dst_index] = src[index_in_spline]; + } } } } +/** + * Individual spans in \a data may be empty if that spline contains no data for the attribute. + */ template static void point_attribute_materialize_to_uninitialized(Span> data, Span offsets, @@ -574,7 +594,14 @@ static void point_attribute_materialize_to_uninitialized(Span> data, for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; - uninitialized_copy_n(data[spline_index].data(), next_offset - offset, dst + offset); + + Span src = data[spline_index]; + if (src.is_empty()) { + uninitialized_fill_n(dst + offset, next_offset - offset, T()); + } + else { + uninitialized_copy_n(src.data(), next_offset - offset, dst + offset); + } } } else { @@ -585,7 +612,13 @@ static void point_attribute_materialize_to_uninitialized(Span> data, } const int index_in_spline = dst_index - offsets[spline_index]; - new (dst + dst_index) T(data[spline_index][index_in_spline]); + Span src = data[spline_index]; + if (src.is_empty()) { + new (dst + dst_index) T(); + } + else { + new (dst + dst_index) T(src[index_in_spline]); + } } } } @@ -769,6 +802,169 @@ class VMutableArray_For_SplinePosition final : public VMutableArray { } }; +class VArray_For_BezierHandle final : public VArray { + private: + Span splines_; + Array offsets_; + bool is_right_; + + public: + VArray_For_BezierHandle(Span splines, Array offsets, const bool is_right) + : VArray(offsets.last()), + splines_(std::move(splines)), + offsets_(std::move(offsets)), + is_right_(is_right) + { + } + + static float3 get_internal(const int64_t index, + Span splines, + Span offsets, + const bool is_right) + { + const PointIndices indices = lookup_point_indices(offsets, index); + const Spline &spline = *splines[indices.spline_index]; + if (spline.type() == Spline::Type::Bezier) { + const BezierSpline &bezier_spline = static_cast(spline); + return is_right ? bezier_spline.handle_positions_right()[indices.point_index] : + bezier_spline.handle_positions_left()[indices.point_index]; + } + return float3(0); + } + + float3 get_impl(const int64_t index) const final + { + return get_internal(index, splines_, offsets_, is_right_); + } + + /** + * Utility so we can pass handle positions to the materialize functions above. + * + * \note This relies on the ability of the materialize implementations to + * handle empty spans, since only Bezier splines have handles. + */ + static Array> get_handle_spans(Span splines, const bool is_right) + { + Array> spans(splines.size()); + for (const int i : spans.index_range()) { + if (splines[i]->type() == Spline::Type::Bezier) { + BezierSpline &bezier_spline = static_cast(*splines[i]); + spans[i] = is_right ? bezier_spline.handle_positions_right() : + bezier_spline.handle_positions_left(); + } + else { + spans[i] = {}; + } + } + return spans; + } + + static void materialize_internal(const IndexMask mask, + Span splines, + Span offsets, + const bool is_right, + MutableSpan r_span) + { + Array> spans = get_handle_spans(splines, is_right); + point_attribute_materialize(spans.as_span(), offsets, mask, r_span); + } + + static void materialize_to_uninitialized_internal(const IndexMask mask, + Span splines, + Span offsets, + const bool is_right, + MutableSpan r_span) + { + Array> spans = get_handle_spans(splines, is_right); + point_attribute_materialize_to_uninitialized(spans.as_span(), offsets, mask, r_span); + } + + void materialize_impl(const IndexMask mask, MutableSpan r_span) const final + { + materialize_internal(mask, splines_, offsets_, is_right_, r_span); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, + MutableSpan r_span) const final + { + materialize_to_uninitialized_internal(mask, splines_, offsets_, is_right_, r_span); + } +}; + +class VMutableArray_For_BezierHandles final : public VMutableArray { + private: + MutableSpan splines_; + Array offsets_; + bool is_right_; + + public: + VMutableArray_For_BezierHandles(MutableSpan splines, + Array offsets, + const bool is_right) + : VMutableArray(offsets.last()), + splines_(splines), + offsets_(std::move(offsets)), + is_right_(is_right) + { + } + + float3 get_impl(const int64_t index) const final + { + return VArray_For_BezierHandle::get_internal(index, splines_, offsets_, is_right_); + } + + void set_impl(const int64_t index, float3 value) final + { + const PointIndices indices = lookup_point_indices(offsets_, index); + Spline &spline = *splines_[indices.spline_index]; + if (spline.type() == Spline::Type::Bezier) { + BezierSpline &bezier_spline = static_cast(spline); + if (is_right_) { + bezier_spline.set_handle_position_right(indices.point_index, value); + } + else { + bezier_spline.set_handle_position_left(indices.point_index, value); + } + bezier_spline.mark_cache_invalid(); + } + } + + void set_all_impl(Span src) final + { + for (const int spline_index : splines_.index_range()) { + Spline &spline = *splines_[spline_index]; + if (spline.type() == Spline::Type::Bezier) { + const int offset = offsets_[spline_index]; + + BezierSpline &bezier_spline = static_cast(spline); + if (is_right_) { + for (const int i : IndexRange(bezier_spline.size())) { + bezier_spline.set_handle_position_right(i, src[offset + i]); + } + } + else { + for (const int i : IndexRange(bezier_spline.size())) { + bezier_spline.set_handle_position_left(i, src[offset + i]); + } + } + bezier_spline.mark_cache_invalid(); + } + } + } + + void materialize_impl(const IndexMask mask, MutableSpan r_span) const final + { + VArray_For_BezierHandle::materialize_internal(mask, splines_, offsets_, is_right_, r_span); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, + MutableSpan r_span) const final + { + VArray_For_BezierHandle::materialize_to_uninitialized_internal( + mask, splines_, offsets_, is_right_, r_span); + } +}; + /** * Provider for any builtin control point attribute that doesn't need * special handling like access to other arrays in the spline. @@ -906,6 +1102,78 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProviderhas_spline_with_type(Spline::Type::Bezier)) { + return {}; + } + + Array offsets = curve->control_point_offsets(); + return std::make_unique>( + offsets.last(), curve->splines(), std::move(offsets), is_right_); + } + + GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override + { + CurveEval *curve = get_curve_from_component_for_write(component); + if (curve == nullptr) { + return {}; + } + + if (!curve->has_spline_with_type(Spline::Type::Bezier)) { + return {}; + } + + Array offsets = curve->control_point_offsets(); + return std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray>( + offsets.last(), curve->splines(), std::move(offsets), is_right_); + } + + bool try_delete(GeometryComponent &UNUSED(component)) const final + { + return false; + } + + bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const final + { + return false; + } + + bool exists(const GeometryComponent &component) const final + { + const CurveEval *curve = get_curve_from_component_for_read(component); + if (curve == nullptr) { + return false; + } + + return curve->has_spline_with_type(Spline::Type::Bezier) && + component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; + } +}; + /** \} */ /* -------------------------------------------------------------------- */ @@ -1196,6 +1464,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() spline_custom_data_access); static PositionAttributeProvider position; + static BezierHandleAttributeProvider handles_start(false); + static BezierHandleAttributeProvider handles_end(true); static BuiltinPointAttributeProvider radius( "radius", @@ -1213,8 +1483,9 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() static DynamicPointAttributeProvider point_custom_data; - return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic}, - {&spline_custom_data, &point_custom_data}); + return ComponentAttributeProviders( + {&position, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic}, + {&spline_custom_data, &point_custom_data}); } } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index ad13342ad9e..77348c3d22c 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -574,7 +574,7 @@ static void join_instance_groups_curve(Span set_groups, G geometry_set_gather_instances_attribute_info( set_groups, {GEO_COMPONENT_TYPE_CURVE}, - {"position", "radius", "tilt", "cyclic", "resolution"}, + {"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"}, attributes); join_attributes(set_groups, {GEO_COMPONENT_TYPE_CURVE}, diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index b36d7a21669..f719a1cfda2 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -289,6 +289,56 @@ void BezierSpline::transform(const blender::float4x4 &matrix) this->mark_cache_invalid(); } +static void set_handle_position(const float3 &position, + const BezierSpline::HandleType type, + const BezierSpline::HandleType type_other, + const float3 &new_value, + float3 &handle, + float3 &handle_other) +{ + /* Don't bother when the handle positions are calculated automatically anyway. */ + if (ELEM(type, BezierSpline::HandleType::Auto, BezierSpline::HandleType::Vector)) { + return; + } + + handle = new_value; + if (type_other == BezierSpline::HandleType::Align) { + /* Keep track of the old length of the opposite handle. */ + const float length = float3::distance(handle_other, position); + /* Set the other handle to directly opposite from the current handle. */ + const float3 dir = (handle - position).normalized(); + handle_other = position - dir * length; + } +} + +/** + * Set positions for the right handle of the control point, ensuring that + * aligned handles stay aligned. Has no effect for auto and vector type handles. + */ +void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value) +{ + set_handle_position(positions_[index], + handle_types_right_[index], + handle_types_left_[index], + value, + handle_positions_right_[index], + handle_positions_left_[index]); +} + +/** + * Set positions for the left handle of the control point, ensuring that + * aligned handles stay aligned. Has no effect for auto and vector type handles. + */ +void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value) +{ + set_handle_position(positions_[index], + handle_types_left_[index], + handle_types_right_[index], + value, + handle_positions_left_[index], + handle_positions_right_[index]); +} + bool BezierSpline::point_is_sharp(const int index) const { return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) || diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 93643298f92..3e9b615f478 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -437,7 +437,7 @@ static void join_curve_components(MutableSpan src_geometry_sets, Ge /* Retrieve attribute info before moving the splines out of the input components. */ const Map info = get_final_attribute_info( {(const GeometryComponent **)src_components.data(), src_components.size()}, - {"position", "radius", "tilt", "cyclic", "resolution"}); + {"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"}); CurveComponent &dst_component = result.get_component_for_write(); CurveEval *dst_curve = new CurveEval(); From 84dcf12ceb7f73a023e91275712bf84926043484 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Wed, 29 Sep 2021 15:05:58 -0700 Subject: [PATCH 0377/1500] UI: Increase Area Resize Edge Hit Size This patch increases the size of the Area BORDERPADDING a bit to make it easier to hit the edges when initiating area resizing. See D11925 for details and examples. Differential Revision: https://developer.blender.org/D11925 Reviewed by Campbell Barton --- source/blender/editors/screen/screen_intern.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index 4016ef84bfd..04ee62b1631 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -62,7 +62,7 @@ typedef enum eScreenAxis { #define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac) /* Expanded interaction influence of area borders. */ -#define BORDERPADDING (U.dpi_fac + U.pixelsize) +#define BORDERPADDING ((2.0f * U.dpi_fac) + U.pixelsize) /* area.c */ void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free); From 66e24ce35bb37753b8002283a72d55639bb40239 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 30 Sep 2021 12:41:01 +1000 Subject: [PATCH 0378/1500] Fix menu width regression in c7d94a7827a5be9343eea22a9638bb059f185206 Icon only popup buttons needed to be adjusted too, add an uiTextIconPadFactor.icon_only to support this. --- .../editors/interface/interface_layout.c | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index c10ba3894ea..3437b9ad2ac 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -295,6 +295,7 @@ static bool ui_layout_variable_size(uiLayout *layout) struct uiTextIconPadFactor { float text; float icon; + float icon_only; }; /** @@ -309,18 +310,21 @@ struct uiTextIconPadFactor { static const struct uiTextIconPadFactor ui_text_pad_default = { .text = 1.50f, .icon = 0.25f, + .icon_only = 0.0f, }; /** #ui_text_pad_default scaled down. */ static const struct uiTextIconPadFactor ui_text_pad_compact = { .text = 1.25f, - .icon = 0.35, + .icon = 0.35f, + .icon_only = 0.0f, }; /** Least amount of padding not to clip the text or icon. */ static const struct uiTextIconPadFactor ui_text_pad_none = { - .text = 0.25, + .text = 0.25f, .icon = 1.50f, + .icon_only = 0.0f, }; /** @@ -333,14 +337,13 @@ static int ui_text_icon_width_ex(uiLayout *layout, { const int unit_x = UI_UNIT_X * (layout->scale[0] ? layout->scale[0] : 1.0f); - if (icon && !name[0]) { - return unit_x; /* icon only */ + /* When there is no text, always behave as if this is an icon-only button + * since it's not useful to return empty space. */ + if (!name[0]) { + return unit_x * (1.0f + pad_factor->icon_only); } if (ui_layout_variable_size(layout)) { - if (!icon && !name[0]) { - return unit_x; /* No icon or name. */ - } if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) { layout->item.flag |= UI_ITEM_FIXED_SIZE; } @@ -2907,11 +2910,10 @@ static uiBut *ui_item_menu(uiLayout *layout, } else if (force_menu) { pad_factor.text = 1.85; + pad_factor.icon_only = 0.6f; } else { - if (name[0]) { - pad_factor.text = 0.75; - } + pad_factor.text = 0.75f; } } From 2c2516bfc92ba8fa0850ca6b6151dbc058b2cbce Mon Sep 17 00:00:00 2001 From: YimingWu Date: Thu, 30 Sep 2021 13:23:17 +0800 Subject: [PATCH 0379/1500] Fix(unreported): LineArt curve objects garbled result. This is caused by line art loading curve objects twice from curve and converted mesh instances (when instance option is on). Now only load mesh when instance option is on. --- .../blender/gpencil_modifiers/intern/lineart/lineart_cpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 5b878a4326f..7441c9a909c 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -2168,6 +2168,12 @@ static void lineart_main_load_geometries( use_mesh = use_ob->data; } else { + /* If DEG_ITER_OBJECT_FLAG_DUPLI is set, the curve objects are going to have a mesh + * equivalent already in the object list, so ignore converting the original curve in this + * case. */ + if (allow_duplicates) { + continue; + } use_mesh = BKE_mesh_new_from_object(depsgraph, use_ob, true, true); } From fdcae48663e87ebc2ed0a97916dcdecb3bd03e61 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 30 Sep 2021 17:32:45 +1000 Subject: [PATCH 0380/1500] Cleanup: isolate UDIM parameters into a struct Passing multiple UDIM arguments into the packing function is awkward especially since the caller may not be using UDIM. Use an argument to store UDIM packing parameters which can be NULL, which operates without any UDIM support. Add a function that extracts these parameters out of the image space allowing for multiple functions to take UDIM parameters in the future. --- source/blender/editors/include/ED_uvedit.h | 20 +++- .../blender/editors/uvedit/uvedit_islands.c | 41 ++++--- .../editors/uvedit/uvedit_unwrap_ops.c | 103 +++++++++++------- 3 files changed, 97 insertions(+), 67 deletions(-) diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 516239a7176..f3aba12a924 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -237,6 +237,18 @@ void ED_image_draw_cursor(struct ARegion *region, const float cursor[2]); void ED_uvedit_buttons_register(struct ARegionType *art); /* uvedit_islands.c */ + +struct UVMapUDIM_Params { + const struct Image *image; + /** Copied from #SpaceImage.tile_grid_shape */ + int grid_shape[2]; + bool use_target_udim; + int target_udim; +}; +bool ED_uvedit_udim_params_from_image_space(const struct SpaceImage *sima, + bool use_active, + struct UVMapUDIM_Params *udim_params); + struct UVPackIsland_Params { uint rotate : 1; /** -1 not to align to axis, otherwise 0,1 for X,Y. */ @@ -247,13 +259,13 @@ struct UVPackIsland_Params { uint correct_aspect : 1; }; -bool uv_coords_isect_udim(const struct Image *image, const int udim_grid[2], float coords[2]); +bool uv_coords_isect_udim(const struct Image *image, + const int udim_grid[2], + const float coords[2]); void ED_uvedit_pack_islands_multi(const struct Scene *scene, - const struct SpaceImage *sima, Object **objects, const uint objects_len, - const bool use_target_udim, - int target_udim, + const struct UVMapUDIM_Params *udim_params, const struct UVPackIsland_Params *params); #ifdef __cplusplus diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c index 62173f4eaff..6159758dbcd 100644 --- a/source/blender/editors/uvedit/uvedit_islands.c +++ b/source/blender/editors/uvedit/uvedit_islands.c @@ -237,8 +237,10 @@ static void bm_face_array_uv_scale_y(BMFace **faces, /** \name UDIM packing helper functions * \{ */ -/* Returns true if UV coordinates lie on a valid tile in UDIM grid or tiled image. */ -bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], float coords[2]) +/** + * Returns true if UV coordinates lie on a valid tile in UDIM grid or tiled image. + */ +bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const float coords[2]) { const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])}; const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); @@ -452,11 +454,9 @@ static int bm_mesh_calc_uv_islands(const Scene *scene, * \{ */ void ED_uvedit_pack_islands_multi(const Scene *scene, - const SpaceImage *sima, Object **objects, const uint objects_len, - const bool use_target_udim, - int target_udim, + const struct UVMapUDIM_Params *udim_params, const struct UVPackIsland_Params *params) { /* Align to the Y axis, could make this configurable. */ @@ -512,7 +512,7 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list, index) { /* Skip calculation if using specified UDIM option. */ - if (!use_target_udim) { + if (udim_params && (udim_params->use_target_udim == false)) { float bounds_min[2], bounds_max[2]; INIT_MINMAX2(bounds_min, bounds_max); for (int i = 0; i < island->faces_len; i++) { @@ -525,6 +525,7 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, selection_max_co[0] = MAX2(bounds_max[0], selection_max_co[0]); selection_max_co[1] = MAX2(bounds_max[1], selection_max_co[1]); } + if (params->rotate) { if (island->aspect_y != 1.0f) { bm_face_array_uv_scale_y( @@ -559,7 +560,7 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, /* Center of bounding box containing all selected UVs. */ float selection_center[2]; - if (!use_target_udim) { + if (udim_params && (udim_params->use_target_udim == false)) { selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f; selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f; } @@ -590,24 +591,22 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, /* Tile offset. */ float base_offset[2] = {0.0f, 0.0f}; + /* CASE: ignore UDIM. */ + if (udim_params == NULL) { + /* pass */ + } /* CASE: Active/specified(smart uv project) UDIM. */ - if (use_target_udim) { + else if (udim_params->use_target_udim) { + /* Calculate offset based on specified_tile_index. */ - base_offset[0] = (target_udim - 1001) % 10; - base_offset[1] = (target_udim - 1001) / 10; + base_offset[0] = (udim_params->target_udim - 1001) % 10; + base_offset[1] = (udim_params->target_udim - 1001) / 10; } /* CASE: Closest UDIM. */ else { - const Image *image; - int udim_grid[2] = {1, 1}; - /* To handle cases where `sima == NULL` - Smart UV project in 3D viewport. */ - if (sima != NULL) { - image = sima->image; - udim_grid[0] = sima->tile_grid_shape[0]; - udim_grid[1] = sima->tile_grid_shape[1]; - } - + const Image *image = udim_params->image; + const int *udim_grid = udim_params->grid_shape; /* Check if selection lies on a valid UDIM grid tile. */ bool is_valid_udim = uv_coords_isect_udim(image, udim_grid, selection_center); if (is_valid_udim) { @@ -643,8 +642,8 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, island->bounds_rect.ymin, }; const float offset[2] = { - (boxarray[i].x * scale[0]) - island->bounds_rect.xmin + base_offset[0], - (boxarray[i].y * scale[1]) - island->bounds_rect.ymin + base_offset[1], + ((boxarray[i].x * scale[0]) - island->bounds_rect.xmin) + base_offset[0], + ((boxarray[i].y * scale[1]) - island->bounds_rect.ymin) + base_offset[1], }; for (int j = 0; j < island->faces_len; j++) { BMFace *efa = island->faces[j]; diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 4e183732db9..38233b55d15 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -144,6 +144,61 @@ static bool ED_uvedit_ensure_uvs(Object *obedit) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name UDIM Access + * \{ */ + +bool ED_uvedit_udim_params_from_image_space(const SpaceImage *sima, + bool use_active, + struct UVMapUDIM_Params *udim_params) +{ + memset(udim_params, 0, sizeof(*udim_params)); + + udim_params->grid_shape[0] = 1; + udim_params->grid_shape[1] = 1; + udim_params->target_udim = 0; + udim_params->use_target_udim = false; + + if (sima == NULL) { + return false; + } + + udim_params->image = sima->image; + udim_params->grid_shape[0] = sima->tile_grid_shape[0]; + udim_params->grid_shape[1] = sima->tile_grid_shape[1]; + + if (use_active) { + int active_udim = 1001; + /* NOTE: Presently, when UDIM grid and tiled image are present together, only active tile for + * the tiled image is considered. */ + Image *image = sima->image; + if (image && image->source == IMA_SRC_TILED) { + ImageTile *active_tile = BLI_findlink(&image->tiles, image->active_tile_index); + if (active_tile) { + active_udim = active_tile->tile_number; + } + } + else { + /* TODO: Support storing an active UDIM when there are no tiles present. + * Until then, use 2D cursor to find the active tile index for the UDIM grid. */ + const float cursor_loc[2] = {sima->cursor[0], sima->cursor[1]}; + if (uv_coords_isect_udim(sima->image, sima->tile_grid_shape, cursor_loc)) { + int tile_number = 1001; + tile_number += floorf(cursor_loc[1]) * 10; + tile_number += floorf(cursor_loc[0]); + active_udim = tile_number; + } + } + + udim_params->target_udim = active_udim; + udim_params->use_target_udim = true; + } + + return true; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Parametrizer Conversion * \{ */ @@ -1040,7 +1095,6 @@ static int pack_islands_exec(bContext *C, wmOperator *op) /* RNA props */ const bool rotate = RNA_boolean_get(op->ptr, "rotate"); const int udim_source = RNA_enum_get(op->ptr, "udim_source"); - bool use_target_udim = false; if (RNA_struct_property_is_set(op->ptr, "margin")) { scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin"); } @@ -1048,46 +1102,15 @@ static int pack_islands_exec(bContext *C, wmOperator *op) RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin); } - int target_udim = 1001; - if (udim_source == PACK_UDIM_SRC_CLOSEST) { - /* pass */ - } - else if (udim_source == PACK_UDIM_SRC_ACTIVE) { - int active_udim = 1001; - /* NOTE: Presently, when UDIM grid and tiled image are present together, only active tile for - * the tiled imgae is considered. */ - if (sima && sima->image) { - Image *image = sima->image; - ImageTile *active_tile = BLI_findlink(&image->tiles, image->active_tile_index); - if (active_tile) { - active_udim = active_tile->tile_number; - } - } - else if (sima && !sima->image) { - /* Use 2D cursor to find the active tile index for the UDIM grid. */ - float cursor_loc[2] = {sima->cursor[0], sima->cursor[1]}; - if (uv_coords_isect_udim(sima->image, sima->tile_grid_shape, cursor_loc)) { - int tile_number = 1001; - tile_number += floorf(cursor_loc[1]) * 10; - tile_number += floorf(cursor_loc[0]); - active_udim = tile_number; - } - /* TODO: Support storing an active UDIM when there are no tiles present. */ - } - - target_udim = active_udim; - use_target_udim = true; - } - else { - BLI_assert_unreachable(); - } + struct UVMapUDIM_Params udim_params; + const bool use_active = (udim_source == PACK_UDIM_SRC_ACTIVE); + const bool use_udim_params = ED_uvedit_udim_params_from_image_space( + sima, use_active, &udim_params); ED_uvedit_pack_islands_multi(scene, - sima, objects, objects_len, - use_target_udim, - target_udim, + use_udim_params ? &udim_params : NULL, &(struct UVPackIsland_Params){ .rotate = rotate, .rotate_align_axis = -1, @@ -2114,7 +2137,6 @@ static int smart_project_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - const SpaceImage *sima = CTX_wm_space_image(C); /* May be NULL. */ View3D *v3d = CTX_wm_view3d(C); @@ -2125,7 +2147,6 @@ static int smart_project_exec(bContext *C, wmOperator *op) const float project_angle_limit_cos = cosf(project_angle_limit); const float project_angle_limit_half_cos = cosf(project_angle_limit / 2); - const int target_udim = 1001; /* 0-1 UV space. */ /* Memory arena for list links (cleared for each object). */ MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); @@ -2265,11 +2286,9 @@ static int smart_project_exec(bContext *C, wmOperator *op) /* Depsgraph refresh functions are called here. */ const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"); ED_uvedit_pack_islands_multi(scene, - sima, objects_changed, object_changed_len, - true, - target_udim, + NULL, &(struct UVPackIsland_Params){ .rotate = true, /* We could make this optional. */ From e9dac3eab839a44af93191dab0467c0fe3ec6d9c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 30 Sep 2021 17:32:46 +1000 Subject: [PATCH 0381/1500] Cleanup: reduce Sequence size by 8 bytes Also use int8_t for color tag. --- source/blender/makesdna/DNA_sequence_types.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 828702f9aa8..13bfa82b36d 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -233,18 +233,20 @@ typedef struct Sequence { float blend_opacity; /* Tag color showed if `SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG` is set. */ - int16_t color_tag; - char _pad4[6]; + int8_t color_tag; + + char alpha_mode; + char _pad4[2]; + + int cache_flag; /* is sfra needed anymore? - it looks like its only used in one place */ /** Starting frame according to the timeline of the scene. */ int sfra; - char alpha_mode; - char _pad[2]; - /* Multiview */ char views_format; + char _pad[3]; struct Stereo3dFormat *stereo3d_format; struct IDProperty *prop; @@ -252,9 +254,6 @@ typedef struct Sequence { /* modifiers */ ListBase modifiers; - int cache_flag; - int _pad2[3]; - SequenceRuntime runtime; } Sequence; From af13168a3f4ab67b66436a706e7069a8ba208c41 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 30 Sep 2021 17:32:48 +1000 Subject: [PATCH 0382/1500] Cleanup: remove unused SpaceImage.curtile --- source/blender/makesdna/DNA_space_types.h | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 70a1ca63e21..aa74e7712c0 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1198,13 +1198,10 @@ typedef struct SpaceImage { char mode_prev; char pin; - char _pad1; - /** - * The currently active tile of the image when tile is enabled, - * is kept in sync with the active faces tile. - */ - short curtile; - short lock; + + char pixel_snap_mode; + + char lock; /** UV draw type. */ char dt_uv; /** Sticky selection type. */ @@ -1212,10 +1209,9 @@ typedef struct SpaceImage { char dt_uvstretch; char around; - int flag; + char _pad1[3]; - char pixel_snap_mode; - char _pad2[7]; + int flag; float uv_opacity; From 5f632f9f6ed901baccf4d44fe0a2d846ccc266af Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 30 Sep 2021 17:45:09 +1000 Subject: [PATCH 0383/1500] Fix color width regression in 66e24ce35bb37753b8002283a72d55639bb40239 Color buttons were drawing single icon width. --- source/blender/editors/interface/interface_layout.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 3437b9ad2ac..64c16e57f56 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -339,11 +339,15 @@ static int ui_text_icon_width_ex(uiLayout *layout, /* When there is no text, always behave as if this is an icon-only button * since it's not useful to return empty space. */ - if (!name[0]) { + if (icon && !name[0]) { return unit_x * (1.0f + pad_factor->icon_only); } if (ui_layout_variable_size(layout)) { + if (!icon && !name[0]) { + return unit_x * (1.0f + pad_factor->icon_only); + } + if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) { layout->item.flag |= UI_ITEM_FIXED_SIZE; } From ae0f944a579c98af0095258d83665d1d5b3423da Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 30 Sep 2021 11:44:53 +0200 Subject: [PATCH 0384/1500] Fix Cycles viewport flickering Caused by a lack of synchronization between update process which sets clear flag and the draw code checking the flag outside of a lock. --- intern/cycles/blender/blender_gpu_display.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/intern/cycles/blender/blender_gpu_display.cpp b/intern/cycles/blender/blender_gpu_display.cpp index 456ca676cce..5a4567deac3 100644 --- a/intern/cycles/blender/blender_gpu_display.cpp +++ b/intern/cycles/blender/blender_gpu_display.cpp @@ -485,12 +485,6 @@ void BlenderGPUDisplay::do_draw(const GPUDisplayParams ¶ms) /* See do_update_begin() for why no locking is required here. */ const bool transparent = true; // TODO(sergey): Derive this from Film. - if (texture_.need_clear) { - /* Texture is requested to be cleared and was not yet cleared. - * Do early return which should be equivalent of drawing all-zero texture. */ - return; - } - if (!gl_draw_resources_ensure()) { return; } @@ -499,6 +493,16 @@ void BlenderGPUDisplay::do_draw(const GPUDisplayParams ¶ms) gl_context_mutex_.lock(); } + if (texture_.need_clear) { + /* Texture is requested to be cleared and was not yet cleared. + * + * Do early return which should be equivalent of drawing all-zero texture. + * Watchout for the lock though so that the clear happening during update is properly + * synchronized here. */ + gl_context_mutex_.unlock(); + return; + } + if (gl_upload_sync_) { glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); } From 80d7cac22d8726ca33590d644278f91a4b73c303 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 30 Sep 2021 12:33:11 +0200 Subject: [PATCH 0385/1500] Fix T91773: improve numerical stability in Curve Spiral node --- .../nodes/node_geo_curve_primitive_spiral.cc | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index 0803d43e5c3..7292fafc8b0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -43,26 +43,18 @@ static std::unique_ptr create_spiral_curve(const float rotations, const int totalpoints = std::max(int(resolution * rotations), 1); const float delta_radius = (end_radius - start_radius) / (float)totalpoints; - float radius = start_radius; const float delta_height = height / (float)totalpoints; - const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints; - float theta = 0.0f; + const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints * + (direction ? 1.0f : -1.0f); for (const int i : IndexRange(totalpoints + 1)) { + const float theta = i * delta_theta; + const float radius = start_radius + i * delta_radius; const float x = radius * cos(theta); const float y = radius * sin(theta); const float z = delta_height * i; spline->add_point(float3(x, y, z), 1.0f, 0.0f); - - radius += delta_radius; - - if (direction) { - theta += delta_theta; - } - else { - theta -= delta_theta; - } } spline->attributes.reallocate(spline->size()); From 9628ef413518e5ddda9c1c8106ce4fecbd1ae7c1 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 30 Sep 2021 12:51:09 +0200 Subject: [PATCH 0386/1500] Fix: wrong field input deduplication with Material Selection node --- .../nodes/geometry/nodes/node_geo_material_selection.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index 337bd88c6e6..9d4533b9bda 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -100,13 +100,16 @@ class MaterialSelectionFieldInput final : public fn::FieldInput { uint64_t hash() const override { - /* Some random constant hash. */ - return 91619626; + return get_default_hash(material_); } bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast(&other) != nullptr; + if (const MaterialSelectionFieldInput *other_material_selection = + dynamic_cast(&other)) { + return material_ == other_material_selection->material_; + } + return false; } }; From 3453b22b1e9cd11ca26960403cbc7b75bfcbe119 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 30 Sep 2021 13:33:21 +0200 Subject: [PATCH 0387/1500] Fix: Curve to Mesh node outputs original curve It should only output the new mesh (and potentially instances that are processed separately). --- source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index f7cef9bbf63..b123820989f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -75,6 +75,7 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { geometry_set_curve_to_mesh(geometry_set, profile_set, params); + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); }); params.set_output("Mesh", std::move(curve_set)); From 07c5d02a113180a6a088a397a25e03175ee5fcf5 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 30 Sep 2021 15:05:47 +0200 Subject: [PATCH 0388/1500] Asset Browser: Support activating catalogs in the "Current File" library If the "Current File" asset library is selected in the Asset Browser, now asssets are filtered based on the active asset catalog. Previously it would just show all assets. This was marked as a TODO in the code already. Maniphest Task: https://developer.blender.org/T91820 --- source/blender/editors/space_file/filelist.c | 61 ++++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 91178b85823..bdf61e792d3 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -810,22 +810,6 @@ static bool is_filtered_hidden(const char *filename, return true; } - /* TODO Make catalog activation work properly with the "Current File" asset library. Currently - * this will only work for external asset data. */ - if (file->imported_asset_data) { - switch (filter->asset_catalog_visibility) { - case FILE_SHOW_ASSETS_WITHOUT_CATALOG: - return !BLI_uuid_is_nil(file->imported_asset_data->catalog_id); - case FILE_SHOW_ASSETS_FROM_CATALOG: - /* TODO show all assets that are in child catalogs of the selected catalog. */ - return BLI_uuid_is_nil(filter->asset_catalog_id) || - !BLI_uuid_equal(filter->asset_catalog_id, file->imported_asset_data->catalog_id); - case FILE_SHOW_ASSETS_ALL_CATALOGS: - /* All asset files should be visible. */ - break; - } - } - return false; } @@ -913,6 +897,39 @@ static bool is_filtered_id_file(const FileListInternEntry *file, return is_filtered; } +/** + * Get the asset metadata of a file, if it represents an asset. This may either be of a local ID + * (ID in the current #Main) or read from an external asset library. + */ +static AssetMetaData *filelist_file_internal_get_asset_data(const FileListInternEntry *file) +{ + const ID *local_id = file->local_data.id; + return local_id ? local_id->asset_data : file->imported_asset_data; +} + +static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter) +{ + const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file); + bool is_visible = false; + + switch (filter->asset_catalog_visibility) { + case FILE_SHOW_ASSETS_WITHOUT_CATALOG: + is_visible = BLI_uuid_is_nil(asset_data->catalog_id); + break; + case FILE_SHOW_ASSETS_FROM_CATALOG: + /* TODO show all assets that are in child catalogs of the selected catalog. */ + is_visible = !BLI_uuid_is_nil(filter->asset_catalog_id) && + BLI_uuid_equal(filter->asset_catalog_id, asset_data->catalog_id); + break; + case FILE_SHOW_ASSETS_ALL_CATALOGS: + /* All asset files should be visible. */ + is_visible = true; + break; + } + + return is_visible; +} + static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) { bool is_filtered; @@ -930,6 +947,13 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis return is_filtered; } +static bool is_filtered_asset_library(FileListInternEntry *file, + const char *root, + FileListFilter *filter) +{ + return is_filtered_lib(file, root, filter) && is_filtered_asset(file, filter); +} + static bool is_filtered_main(FileListInternEntry *file, const char *UNUSED(dir), FileListFilter *filter) @@ -942,7 +966,8 @@ static bool is_filtered_main_assets(FileListInternEntry *file, FileListFilter *filter) { /* "Filtered" means *not* being filtered out... So return true if the file should be visible. */ - return is_filtered_id_file(file, file->relpath, file->name, filter); + return is_filtered_id_file(file, file->relpath, file->name, filter) && + is_filtered_asset(file, filter); } static void filelist_filter_clear(FileList *filelist) @@ -1779,7 +1804,7 @@ void filelist_settype(FileList *filelist, short type) case FILE_ASSET_LIBRARY: filelist->check_dir_fn = filelist_checkdir_lib; filelist->read_job_fn = filelist_readjob_asset_library; - filelist->filter_fn = is_filtered_lib; + filelist->filter_fn = is_filtered_asset_library; break; case FILE_MAIN_ASSET: filelist->check_dir_fn = filelist_checkdir_main_assets; From 779ea49af785bcd7a2911cba1a929856e21f2c3c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 30 Sep 2021 15:44:08 +0200 Subject: [PATCH 0389/1500] Cleanup: move node_common.c to c++ Buildbot compiled without problems. --- source/blender/nodes/CMakeLists.txt | 2 +- .../intern/{node_common.c => node_common.cc} | 115 +++++++++--------- 2 files changed, 60 insertions(+), 57 deletions(-) rename source/blender/nodes/intern/{node_common.c => node_common.cc} (82%) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 8eaf88a6f1e..5b6a5ac5b33 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -373,7 +373,7 @@ set(SRC intern/derived_node_tree.cc intern/geometry_nodes_eval_log.cc intern/math_functions.cc - intern/node_common.c + intern/node_common.cc intern/node_declaration.cc intern/node_exec.cc intern/node_geometry_exec.cc diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.cc similarity index 82% rename from source/blender/nodes/intern/node_common.c rename to source/blender/nodes/intern/node_common.cc index 7625cb9e3f6..3a896aea69c 100644 --- a/source/blender/nodes/intern/node_common.c +++ b/source/blender/nodes/intern/node_common.cc @@ -21,8 +21,8 @@ * \ingroup nodes */ -#include -#include +#include +#include #include "DNA_node_types.h" @@ -53,24 +53,22 @@ enum { bNodeSocket *node_group_find_input_socket(bNode *groupnode, const char *identifier) { - bNodeSocket *sock; - for (sock = groupnode->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &groupnode->inputs) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } bNodeSocket *node_group_find_output_socket(bNode *groupnode, const char *identifier) { - bNodeSocket *sock; - for (sock = groupnode->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &groupnode->outputs) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } /* groups display their internal tree name as label */ @@ -95,13 +93,12 @@ bool node_group_poll_instance(bNode *node, bNodeTree *nodetree, const char **dis bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_disabled_hint) { - bNode *node; bool valid = true; /* unspecified node group, generally allowed * (if anything, should be avoided on operator level) */ - if (grouptree == NULL) { + if (grouptree == nullptr) { return true; } @@ -110,7 +107,7 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis return false; } - for (node = grouptree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &grouptree->nodes) { if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) { valid = false; @@ -121,12 +118,15 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis } /* used for both group nodes and interface nodes */ -static bNodeSocket *group_verify_socket( - bNodeTree *ntree, bNode *gnode, bNodeSocket *iosock, ListBase *verify_lb, int in_out) +static bNodeSocket *group_verify_socket(bNodeTree *ntree, + bNode *gnode, + bNodeSocket *iosock, + ListBase *verify_lb, + eNodeSocketInOut in_out) { bNodeSocket *sock; - for (sock = verify_lb->first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)verify_lb->first; sock; sock = sock->next) { if (STREQ(sock->identifier, iosock->identifier)) { break; } @@ -163,29 +163,32 @@ static bNodeSocket *group_verify_socket( } /* used for both group nodes and interface nodes */ -static void group_verify_socket_list( - bNodeTree *ntree, bNode *gnode, ListBase *iosock_lb, ListBase *verify_lb, int in_out) +static void group_verify_socket_list(bNodeTree *ntree, + bNode *gnode, + ListBase *iosock_lb, + ListBase *verify_lb, + eNodeSocketInOut in_out) { - bNodeSocket *iosock, *sock, *nextsock; + bNodeSocket *sock, *nextsock; /* step by step compare */ - iosock = iosock_lb->first; + bNodeSocket *iosock = (bNodeSocket *)iosock_lb->first; for (; iosock; iosock = iosock->next) { /* abusing new_sock pointer for verification here! only used inside this function */ iosock->new_sock = group_verify_socket(ntree, gnode, iosock, verify_lb, in_out); } /* leftovers are removed */ - for (sock = verify_lb->first; sock; sock = nextsock) { + for (sock = (bNodeSocket *)verify_lb->first; sock; sock = nextsock) { nextsock = sock->next; nodeRemoveSocket(ntree, gnode, sock); } /* and we put back the verified sockets */ - iosock = iosock_lb->first; + iosock = (bNodeSocket *)iosock_lb->first; for (; iosock; iosock = iosock->next) { if (iosock->new_sock) { BLI_addtail(verify_lb, iosock->new_sock); - iosock->new_sock = NULL; + iosock->new_sock = nullptr; } } } @@ -194,7 +197,7 @@ static void group_verify_socket_list( void node_group_update(struct bNodeTree *ntree, struct bNode *node) { /* check inputs and outputs, and remove or insert them */ - if (node->id == NULL) { + if (node->id == nullptr) { nodeRemoveAllSockets(ntree, node); } else if ((ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING))) { @@ -227,7 +230,7 @@ static void node_frame_init(bNodeTree *UNUSED(ntree), bNode *node) void register_node_type_frame(void) { /* frame type is used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "frame node type"); + bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "frame node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_FRAME, "Frame", NODE_CLASS_LAYOUT, NODE_BACKGROUND); @@ -254,11 +257,11 @@ static void node_reroute_update_internal_links(bNodeTree *ntree, bNode *node) return; } - link = MEM_callocN(sizeof(bNodeLink), "internal node link"); + link = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "internal node link"); link->fromnode = node; - link->fromsock = node->inputs.first; + link->fromsock = (bNodeSocket *)node->inputs.first; link->tonode = node; - link->tosock = node->outputs.first; + link->tosock = (bNodeSocket *)node->outputs.first; /* internal link is always valid */ link->flag |= NODE_LINK_VALID; BLI_addtail(&node->internal_links, link); @@ -276,7 +279,7 @@ static void node_reroute_init(bNodeTree *ntree, bNode *node) void register_node_type_reroute(void) { /* frame type is used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "frame node type"); + bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "frame node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_REROUTE, "Reroute", NODE_CLASS_LAYOUT, 0); @@ -288,8 +291,8 @@ void register_node_type_reroute(void) static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, int flag) { - bNodeSocket *input = node->inputs.first; - bNodeSocket *output = node->outputs.first; + bNodeSocket *input = (bNodeSocket *)node->inputs.first; + bNodeSocket *output = (bNodeSocket *)node->outputs.first; bNodeLink *link; int type = SOCK_FLOAT; const char *type_idname = nodeStaticSocketType(type, PROP_NONE); @@ -302,7 +305,7 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i node->done = 1; /* recursive update */ - for (link = ntree->links.first; link; link = link->next) { + for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { bNode *fromnode = link->fromnode; bNode *tonode = link->tonode; if (!tonode || !fromnode) { @@ -336,7 +339,7 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i if (input->type != type) { bNodeSocket *ninput = nodeAddSocket(ntree, node, SOCK_IN, type_idname, "input", "Input"); - for (link = ntree->links.first; link; link = link->next) { + for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { if (link->tosock == input) { link->tosock = ninput; ninput->link = link; @@ -347,7 +350,7 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i if (output->type != type) { bNodeSocket *noutput = nodeAddSocket(ntree, node, SOCK_OUT, type_idname, "output", "Output"); - for (link = ntree->links.first; link; link = link->next) { + for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { if (link->fromsock == output) { link->fromsock = noutput; } @@ -366,11 +369,11 @@ void ntree_update_reroute_nodes(bNodeTree *ntree) bNode *node; /* clear tags */ - for (node = ntree->nodes.first; node; node = node->next) { + for (node = (bNode *)ntree->nodes.first; node; node = node->next) { node->done = 0; } - for (node = ntree->nodes.first; node; node = node->next) { + for (node = (bNode *)ntree->nodes.first; node; node = node->next) { if (node->type == NODE_REROUTE && !node->done) { node_reroute_inherit_type_recursive(ntree, node, REFINE_FORWARD | REFINE_BACKWARD); } @@ -393,7 +396,7 @@ static bool node_is_connected_to_output_recursive(bNodeTree *ntree, bNode *node) } /* test all connected nodes, first positive find is sufficient to return true */ - for (link = ntree->links.first; link; link = link->next) { + for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { if (link->fromnode == node) { if (node_is_connected_to_output_recursive(ntree, link->tonode)) { return true; @@ -408,7 +411,7 @@ bool BKE_node_is_connected_to_output(bNodeTree *ntree, bNode *node) bNode *tnode; /* clear flags */ - for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { + for (tnode = (bNode *)ntree->nodes.first; tnode; tnode = tnode->next) { tnode->done = 0; } @@ -419,9 +422,9 @@ void BKE_node_tree_unlink_id(ID *id, struct bNodeTree *ntree) { bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { + for (node = (bNode *)ntree->nodes.first; node; node = node->next) { if (node->id == id) { - node->id = NULL; + node->id = nullptr; } } } @@ -440,17 +443,17 @@ static void node_group_input_init(bNodeTree *ntree, bNode *node) bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier) { bNodeSocket *sock; - for (sock = node->outputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } void node_group_input_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *extsock = node->outputs.last; + bNodeSocket *extsock = (bNodeSocket *)node->outputs.last; bNodeLink *link, *linknext, *exposelink; /* Adding a tree socket and verifying will remove the extension socket! * This list caches the existing links from the extension socket @@ -460,14 +463,14 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) /* find links from the extension socket and store them */ BLI_listbase_clear(&tmplinks); - for (link = ntree->links.first; link; link = linknext) { + for (link = (bNodeLink *)ntree->links.first; link; link = linknext) { linknext = link->next; if (nodeLinkIsHidden(link)) { continue; } if (link->fromsock == extsock) { - bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link"); + bNodeLink *tlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "temporary link"); *tlink = *link; BLI_addtail(&tmplinks, tlink); @@ -476,8 +479,8 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) } /* find valid link to expose */ - exposelink = NULL; - for (link = tmplinks.first; link; link = link->next) { + exposelink = nullptr; + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { /* XXX Multiple sockets can be connected to the extension socket at once, * in that case the arbitrary first link determines name and type. * This could be improved by choosing the "best" type among all links, @@ -498,7 +501,7 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) newsock = node_group_input_find_socket(node, gsock->identifier); /* redirect links from the extension socket */ - for (link = tmplinks.first; link; link = link->next) { + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { nodeAddLink(ntree, node, newsock, link->tonode, link->tosock); } } @@ -518,7 +521,7 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) void register_node_type_group_input(void) { /* used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "node type"); + bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_GROUP_INPUT, "Group Input", NODE_CLASS_INTERFACE, 0); @@ -537,17 +540,17 @@ static void node_group_output_init(bNodeTree *ntree, bNode *node) bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier) { bNodeSocket *sock; - for (sock = node->inputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } void node_group_output_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *extsock = node->inputs.last; + bNodeSocket *extsock = (bNodeSocket *)node->inputs.last; bNodeLink *link, *linknext, *exposelink; /* Adding a tree socket and verifying will remove the extension socket! * This list caches the existing links to the extension socket @@ -557,14 +560,14 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) /* find links to the extension socket and store them */ BLI_listbase_clear(&tmplinks); - for (link = ntree->links.first; link; link = linknext) { + for (link = (bNodeLink *)ntree->links.first; link; link = linknext) { linknext = link->next; if (nodeLinkIsHidden(link)) { continue; } if (link->tosock == extsock) { - bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link"); + bNodeLink *tlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "temporary link"); *tlink = *link; BLI_addtail(&tmplinks, tlink); @@ -573,8 +576,8 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) } /* find valid link to expose */ - exposelink = NULL; - for (link = tmplinks.first; link; link = link->next) { + exposelink = nullptr; + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { /* XXX Multiple sockets can be connected to the extension socket at once, * in that case the arbitrary first link determines name and type. * This could be improved by choosing the "best" type among all links, @@ -596,7 +599,7 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) newsock = node_group_output_find_socket(node, gsock->identifier); /* redirect links to the extension socket */ - for (link = tmplinks.first; link; link = link->next) { + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { nodeAddLink(ntree, link->fromnode, link->fromsock, node, newsock); } } @@ -616,7 +619,7 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) void register_node_type_group_output(void) { /* used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "node type"); + bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_GROUP_OUTPUT, "Group Output", NODE_CLASS_INTERFACE, 0); From 1a72744ddc4a34ce32f308a9011423c2099b49d3 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 30 Sep 2021 15:22:16 +0200 Subject: [PATCH 0390/1500] Fix T90246: Full Copy'ing a scene confuses physics in the original scene. Handling of RigidBody data in duplicate of scenes/collections was very wrong. This commit: - Add handling of duplication of RB collections when fully duplicating a scene. - Fix Object duplication trying to add duplicated RB objects to matching RBW collections. While the later behavior is desired when only duplicated objects, when duplicating their collections and/or scenes it is actually very bad, as it would add back new object duplicates to old (RBW) collections. --- source/blender/blenkernel/BKE_lib_id.h | 6 ++- source/blender/blenkernel/intern/collection.c | 2 +- source/blender/blenkernel/intern/lib_id.c | 7 ++- source/blender/blenkernel/intern/object.c | 44 +++++++++++-------- source/blender/blenkernel/intern/rigidbody.c | 2 +- source/blender/blenkernel/intern/scene.c | 26 +++++++++-- 6 files changed, 60 insertions(+), 27 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 36f57209e33..d2a8ec2e332 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -133,6 +133,9 @@ enum { LIB_ID_COPY_SHAPEKEY = 1 << 26, /** EXCEPTION! Specific deep-copy of node trees used e.g. for rendering purposes. */ LIB_ID_COPY_NODETREE_LOCALIZE = 1 << 27, + /** EXCEPTION! Specific handling of RB objects regarding collections differs depending whether we + duplicate scene/collections, or objects. */ + LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING = 1 << 28, /* *** Helper 'defines' gathering most common flag sets. *** */ /** Shapekeys are not real ID's, more like local data to geometry IDs... */ @@ -261,7 +264,8 @@ struct ID *BKE_id_copy_ex(struct Main *bmain, const int flag); struct ID *BKE_id_copy_for_duplicate(struct Main *bmain, struct ID *id, - const uint duplicate_flags); + const uint duplicate_flags, + const int copy_flags); void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b); void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 2d172f23428..8e50b9e9534 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -597,7 +597,7 @@ static Collection *collection_duplicate_recursive(Main *bmain, } else if (collection_old->id.newid == NULL) { collection_new = (Collection *)BKE_id_copy_for_duplicate( - bmain, (ID *)collection_old, duplicate_flags); + bmain, (ID *)collection_old, duplicate_flags, LIB_ID_COPY_DEFAULT); if (collection_new == collection_old) { return collection_new; diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 18824e73ee5..3b2d2c5d2c3 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -674,7 +674,10 @@ ID *BKE_id_copy(Main *bmain, const ID *id) * Invokes the appropriate copy method for the block and returns the result in * newid, unless test. Returns true if the block can be copied. */ -ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplicate_flags) +ID *BKE_id_copy_for_duplicate(Main *bmain, + ID *id, + const eDupli_ID_Flags duplicate_flags, + const int copy_flags) { if (id == NULL) { return id; @@ -685,7 +688,7 @@ ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplica return id; } - ID *id_new = BKE_id_copy(bmain, id); + ID *id_new = BKE_id_copy_ex(bmain, id, NULL, copy_flags); /* Copying add one user by default, need to get rid of that one. */ id_us_min(id_new); ID_NEW_SET(id, id_new); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index fbdf99c91c2..ec39c5b45c4 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2634,10 +2634,16 @@ Object *BKE_object_duplicate(Main *bmain, { const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; const bool is_root_id = (duplicate_options & LIB_ID_DUPLICATE_IS_ROOT_ID) != 0; + int copy_flags = LIB_ID_COPY_DEFAULT; if (!is_subprocess) { BKE_main_id_newptr_and_tag_clear(bmain); } + else { + /* In case copying object is a sub-process of collection (or scene) copying, do not try to + * re-assign RB objects to existing RBW collections. */ + copy_flags |= LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING; + } if (is_root_id) { /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate * all expected linked data. */ @@ -2649,7 +2655,7 @@ Object *BKE_object_duplicate(Main *bmain, Material ***matarar; - Object *obn = (Object *)BKE_id_copy_for_duplicate(bmain, &ob->id, dupflag); + Object *obn = (Object *)BKE_id_copy_for_duplicate(bmain, &ob->id, dupflag, copy_flags); /* 0 == full linked. */ if (dupflag == 0) { @@ -2658,13 +2664,13 @@ Object *BKE_object_duplicate(Main *bmain, if (dupflag & USER_DUP_MAT) { for (int i = 0; i < obn->totcol; i++) { - BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag); + BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag, copy_flags); } } if (dupflag & USER_DUP_PSYS) { ParticleSystem *psys; for (psys = obn->particlesystem.first; psys; psys = psys->next) { - BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, dupflag); + BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, dupflag, copy_flags); } } @@ -2675,77 +2681,77 @@ Object *BKE_object_duplicate(Main *bmain, switch (obn->type) { case OB_MESH: if (dupflag & USER_DUP_MESH) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_CURVE: if (dupflag & USER_DUP_CURVE) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_SURF: if (dupflag & USER_DUP_SURF) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_FONT: if (dupflag & USER_DUP_FONT) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_MBALL: if (dupflag & USER_DUP_MBALL) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_LAMP: if (dupflag & USER_DUP_LAMP) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_ARMATURE: if (dupflag & USER_DUP_ARM) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_LATTICE: if (dupflag != 0) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_CAMERA: if (dupflag != 0) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_LIGHTPROBE: if (dupflag & USER_DUP_LIGHTPROBE) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_SPEAKER: if (dupflag != 0) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_GPENCIL: if (dupflag & USER_DUP_GPENCIL) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_HAIR: if (dupflag & USER_DUP_HAIR) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_POINTCLOUD: if (dupflag & USER_DUP_POINTCLOUD) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_VOLUME: if (dupflag & USER_DUP_VOLUME) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; } @@ -2756,7 +2762,7 @@ Object *BKE_object_duplicate(Main *bmain, matarar = BKE_object_material_array_p(obn); if (matarar) { for (int i = 0; i < obn->totcol; i++) { - BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], dupflag); + BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], dupflag, copy_flags); } } } diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 328c54fc21b..947efd88ec1 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -302,7 +302,7 @@ void BKE_rigidbody_object_copy(Main *bmain, Object *ob_dst, const Object *ob_src ob_dst->rigidbody_object = rigidbody_copy_object(ob_src, flag); ob_dst->rigidbody_constraint = rigidbody_copy_constraint(ob_src, flag); - if (flag & LIB_ID_CREATE_NO_MAIN) { + if ((flag & (LIB_ID_CREATE_NO_MAIN | LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING)) != 0) { return; } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 03f19cef94e..a9a8cd93b1d 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1801,6 +1801,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) /* Scene duplication is always root of duplication currently. */ const bool is_subprocess = false; const bool is_root_id = true; + const int copy_flags = LIB_ID_COPY_DEFAULT; if (!is_subprocess) { BKE_main_id_newptr_and_tag_clear(bmain); @@ -1816,21 +1817,40 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) /* Copy Freestyle LineStyle datablocks. */ LISTBASE_FOREACH (ViewLayer *, view_layer_dst, &sce_copy->view_layers) { LISTBASE_FOREACH (FreestyleLineSet *, lineset, &view_layer_dst->freestyle_config.linesets) { - BKE_id_copy_for_duplicate(bmain, (ID *)lineset->linestyle, duplicate_flags); + BKE_id_copy_for_duplicate(bmain, (ID *)lineset->linestyle, duplicate_flags, copy_flags); } } /* Full copy of world (included animations) */ - BKE_id_copy_for_duplicate(bmain, (ID *)sce->world, duplicate_flags); + BKE_id_copy_for_duplicate(bmain, (ID *)sce->world, duplicate_flags, copy_flags); /* Full copy of GreasePencil. */ - BKE_id_copy_for_duplicate(bmain, (ID *)sce->gpd, duplicate_flags); + BKE_id_copy_for_duplicate(bmain, (ID *)sce->gpd, duplicate_flags, copy_flags); /* Deep-duplicate collections and objects (using preferences' settings for which sub-data to * duplicate along the object itself). */ BKE_collection_duplicate( bmain, NULL, sce_copy->master_collection, duplicate_flags, LIB_ID_DUPLICATE_IS_SUBPROCESS); + /* Rigid body world collections may not be instantiated as scene's collections, ensure they + * also get properly duplicated. */ + if (sce_copy->rigidbody_world != NULL) { + if (sce_copy->rigidbody_world->group != NULL) { + BKE_collection_duplicate(bmain, + NULL, + sce_copy->rigidbody_world->group, + duplicate_flags, + LIB_ID_DUPLICATE_IS_SUBPROCESS); + } + if (sce_copy->rigidbody_world->constraints != NULL) { + BKE_collection_duplicate(bmain, + NULL, + sce_copy->rigidbody_world->constraints, + duplicate_flags, + LIB_ID_DUPLICATE_IS_SUBPROCESS); + } + } + if (!is_subprocess) { /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ BKE_libblock_relink_to_newid(&sce_copy->id); From d754d8584528331eac4918413ced8239d2a9c5ed Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 30 Sep 2021 15:29:43 +0200 Subject: [PATCH 0391/1500] Fix RigidBodyWorld copy using `NO_MAIN` instead of `COW` flag for cache handling. We only want to share caches in case of CoW copying for the depsgraph, not for regular `NO_MAIN` data. --- source/blender/blenkernel/intern/rigidbody.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 947efd88ec1..1ea659b2d41 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -1211,8 +1211,8 @@ RigidBodyWorld *BKE_rigidbody_world_copy(RigidBodyWorld *rbw, const int flag) id_us_plus((ID *)rbw_copy->constraints); } - if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { - /* This is a regular copy, and not a CoW copy for depsgraph evaluation */ + if ((flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0) { + /* This is a regular copy, and not a CoW copy for depsgraph evaluation. */ rbw_copy->shared = MEM_callocN(sizeof(*rbw_copy->shared), "RigidBodyWorld_Shared"); BKE_ptcache_copy_list(&rbw_copy->shared->ptcaches, &rbw->shared->ptcaches, LIB_ID_COPY_CACHES); rbw_copy->shared->pointcache = rbw_copy->shared->ptcaches.first; From 5d42ea036999a7e82dbc03947968f4ad61093d06 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 30 Sep 2021 15:28:44 +0200 Subject: [PATCH 0392/1500] GPencil: Change default template for better contrast in header Patch created by Pablo Vazquez This change darkens the header area a bit to create more contrast with the texts. Differential Revision: https://developer.blender.org/D12711 --- .../2D_Animation/__init__.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py index c8328f5ee42..f8b504b2e34 100644 --- a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py +++ b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py @@ -54,11 +54,26 @@ def update_factory_startup_grease_pencils(): gpd.onion_keyframe_type = 'ALL' +def update_factory_startup_theme(): + # To prevent saving over the current theme Preferences, + # store the current state of use_preferences_save to use later. + preferences = bpy.context.preferences + save_preferences_state = preferences.use_preferences_save + + # Turn use_preferences_save off and set header background alpha. + preferences.use_preferences_save = False + preferences.themes['Default'].view_3d.space.header[3] = 0.8 + + # Restore the original use_preferences_save status. + preferences.use_preferences_save = save_preferences_state + + @persistent def load_handler(_): update_factory_startup_screens() update_factory_startup_scenes() update_factory_startup_grease_pencils() + update_factory_startup_theme() def register(): From 628fab696cfaefdd2ac758849c8a1e9a3a0beef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 30 Sep 2021 16:29:14 +0200 Subject: [PATCH 0393/1500] Asset Catalog: introduce `AssetCatalogPath` class So far we have used `std::string` for asset catalog paths. Some operations are better described on a dedicated class for this, though. This commits switches catalog paths from using `std::string` to a dedicated `blender::bke::AssetCatalogPath` class. The `using CatalogPath = AssetCatalogPath` alias is still there, and will be removed in a following cleanup commit. New `AssetCatalogPath` code reviewed by @severin in D12710. --- .../blender/blenkernel/BKE_asset_catalog.hh | 15 +- .../blenkernel/BKE_asset_catalog_path.hh | 138 +++++++++++ source/blender/blenkernel/CMakeLists.txt | 3 + .../blenkernel/intern/asset_catalog.cc | 122 +++------ .../blenkernel/intern/asset_catalog_path.cc | 220 ++++++++++++++++ .../intern/asset_catalog_path_test.cc | 234 ++++++++++++++++++ .../blenkernel/intern/asset_catalog_test.cc | 39 +-- .../blenkernel/intern/asset_library_test.cc | 2 +- .../editors/asset/intern/asset_catalog.cc | 12 +- 9 files changed, 645 insertions(+), 140 deletions(-) create mode 100644 source/blender/blenkernel/BKE_asset_catalog_path.hh create mode 100644 source/blender/blenkernel/intern/asset_catalog_path.cc create mode 100644 source/blender/blenkernel/intern/asset_catalog_path_test.cc diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 05db3c808cf..c141ce5fca0 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -30,6 +30,8 @@ #include "BLI_uuid.h" #include "BLI_vector.hh" +#include "BKE_asset_catalog_path.hh" + #include #include #include @@ -38,7 +40,7 @@ namespace blender::bke { using CatalogID = bUUID; -using CatalogPath = std::string; +using CatalogPath = AssetCatalogPath; using CatalogPathComponent = std::string; /* Would be nice to be able to use `std::filesystem::path` for this, but it's currently not * available on the minimum macOS target version. */ @@ -52,7 +54,6 @@ class AssetCatalogTree; * directory hierarchy). */ class AssetCatalogService { public: - static const char PATH_SEPARATOR; static const CatalogFilePath DEFAULT_CATALOG_FILENAME; public: @@ -297,15 +298,6 @@ class AssetCatalog { bool is_deleted = false; } flags; - /** - * \return true only if this catalog's path is contained within the given path. - * When this catalog's path is equal to the given path, return true as well. - * - * Note that non-normalized paths (so for example starting or ending with a slash) are not - * supported, and result in undefined behavior. - */ - bool is_contained_in(const CatalogPath &other_path) const; - /** * Create a new Catalog with the given path, auto-generating a sensible catalog simple-name. * @@ -313,7 +305,6 @@ class AssetCatalog { * `AssetCatalog`'s path differ from the given one. */ static std::unique_ptr from_path(const CatalogPath &path); - static CatalogPath cleanup_path(const CatalogPath &path); protected: /** Generate a sensible catalog ID for the given path. */ diff --git a/source/blender/blenkernel/BKE_asset_catalog_path.hh b/source/blender/blenkernel/BKE_asset_catalog_path.hh new file mode 100644 index 00000000000..1e53df553a9 --- /dev/null +++ b/source/blender/blenkernel/BKE_asset_catalog_path.hh @@ -0,0 +1,138 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#pragma once + +#ifndef __cplusplus +# error This is a C++ header. +#endif + +#include "BLI_function_ref.hh" +#include "BLI_string_ref.hh" +#include "BLI_sys_types.h" + +#include + +namespace blender::bke { + +/** + * Location of an Asset Catalog in the catalog tree, denoted by slash-separated path components. + * + * Each path component is a string that is not allowed to have slashes or colons. The latter is to + * make things easy to save in the colon-delimited Catalog Definition File format. + * + * The path of a catalog determines where in the catalog hierarchy the catalog is shown. Examples + * are "Characters/Ellie/Poses/Hand" or "Kitbash/City/Skyscrapers". The path looks like a + * filesystem path, with a few differences: + * + * - Only slashes are used as path component separators. + * - All paths are absolute, so there is no need for a leading slash. + * + * See https://wiki.blender.org/wiki/Source/Architecture/Asset_System/Catalogs + * + * Paths are stored as byte sequences, and assumed to be UTF-8. + */ +class AssetCatalogPath { + friend std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append); + + private: + /** + * The path itself, such as "Agents/Secret/327". + */ + std::string path_; + + public: + static const char SEPARATOR; + + AssetCatalogPath() = delete; + AssetCatalogPath(StringRef path); + AssetCatalogPath(const std::string &path); + AssetCatalogPath(const char *path); + AssetCatalogPath(const AssetCatalogPath &other_path); + AssetCatalogPath(AssetCatalogPath &&other_path) noexcept; + ~AssetCatalogPath() = default; + + uint64_t hash() const; + uint64_t length() const; /* Length of the path in bytes. */ + + /** C-string representation of the path. */ + const char *c_str() const; + const std::string &str() const; + + /* In-class operators, because of the implicit `AssetCatalogPath(StringRef)` constructor. + * Otherwise `string == string` could cast both sides to `AssetCatalogPath`. */ + bool operator==(const AssetCatalogPath &other_path) const; + bool operator!=(const AssetCatalogPath &other_path) const; + bool operator<(const AssetCatalogPath &other_path) const; + AssetCatalogPath &operator=(const AssetCatalogPath &other_path) = default; + AssetCatalogPath &operator=(AssetCatalogPath &&other_path) = default; + + /** Concatenate two paths, returning the new path. */ + AssetCatalogPath operator/(const AssetCatalogPath &path_to_append) const; + + /* False when the path is empty, true otherwise. */ + operator bool() const; + + /** + * Clean up the path. This ensures: + * - Every path component is stripped of its leading/trailing spaces. + * - Empty components (caused by double slashes or leading/trailing slashes) are removed. + * - Invalid characters are replaced with valid ones. + */ + [[nodiscard]] AssetCatalogPath cleanup() const; + + /** + * \return true only if the given path is a parent of this catalog's path. + * When this catalog's path is equal to the given path, return true as well. + * In other words, this defines a weak subset. + * + * True: "some/path/there" is contained in "some/path" and "some". + * False: "path/there" is not contained in "some/path/there". + * + * Note that non-cleaned-up paths (so for example starting or ending with a + * slash) are not supported, and result in undefined behavior. + */ + bool is_contained_in(const AssetCatalogPath &other_path) const; + + /** + * Change the initial part of the path from `from_path` to `to_path`. + * If this path does not start with `from_path`, return an empty path as result. + * + * Example: + * + * AssetCatalogPath path("some/path/to/some/catalog"); + * path.rebase("some/path", "new/base") -> "new/base/to/some/catalog" + */ + AssetCatalogPath rebase(const AssetCatalogPath &from_path, + const AssetCatalogPath &to_path) const; + + /** Call the callback function for each path component, in left-to-right order. */ + using ComponentIteratorFn = FunctionRef; + void iterate_components(ComponentIteratorFn callback) const; + + protected: + /** Strip leading/trailing spaces and replace disallowed characters. */ + static std::string cleanup_component(StringRef component_name); +}; + +/** Output the path as string. */ +std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append); + +} // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 24de91959bb..fb7fdd1ac21 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -85,6 +85,7 @@ set(SRC intern/armature_update.c intern/asset.cc intern/asset_catalog.cc + intern/asset_catalog_path.cc intern/asset_library.cc intern/attribute.c intern/attribute_access.cc @@ -306,6 +307,7 @@ set(SRC BKE_armature.hh BKE_asset.h BKE_asset_catalog.hh + BKE_asset_catalog_path.hh BKE_asset_library.h BKE_asset_library.hh BKE_attribute.h @@ -789,6 +791,7 @@ if(WITH_GTESTS) intern/action_test.cc intern/armature_test.cc intern/asset_catalog_test.cc + intern/asset_catalog_path_test.cc intern/asset_library_test.cc intern/asset_test.cc intern/cryptomatte_test.cc diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index b00f4305aa6..300a15fad6d 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -38,7 +38,6 @@ namespace blender::bke { -const char AssetCatalogService::PATH_SEPARATOR = '/'; const CatalogFilePath AssetCatalogService::DEFAULT_CATALOG_FILENAME = "blender_assets.cats.txt"; /* For now this is the only version of the catalog definition files that is supported. @@ -115,12 +114,12 @@ void AssetCatalogService::update_catalog_path(CatalogID catalog_id, for (auto &catalog_uptr : catalogs_.values()) { AssetCatalog *cat = catalog_uptr.get(); - if (!cat->is_contained_in(old_cat_path)) { + + const CatalogPath new_path = cat->path.rebase(old_cat_path, new_catalog_path); + if (!new_path) { continue; } - - const CatalogPath path_suffix = cat->path.substr(old_cat_path.length()); - cat->path = new_catalog_path + path_suffix; + cat->path = new_path; } this->rebuild_tree(); @@ -319,8 +318,7 @@ CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing( /* - There's no definition file next to the .blend file. * -> Ask the asset library API for an appropriate location. */ char suitable_root_path[PATH_MAX]; - BKE_asset_library_find_suitable_root_path_from_path(blend_file_path.c_str(), - suitable_root_path); + BKE_asset_library_find_suitable_root_path_from_path(blend_file_path.c_str(), suitable_root_path); char asset_lib_cdf_path[PATH_MAX]; BLI_path_join(asset_lib_cdf_path, sizeof(asset_lib_cdf_path), @@ -382,9 +380,9 @@ StringRef AssetCatalogTreeItem::get_name() const CatalogPath AssetCatalogTreeItem::catalog_path() const { - std::string current_path = name_; + CatalogPath current_path = name_; for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) { - current_path = parent->name_ + AssetCatalogService::PATH_SEPARATOR + current_path; + current_path = CatalogPath(parent->name_) / current_path; } return current_path; } @@ -405,32 +403,6 @@ bool AssetCatalogTreeItem::has_children() const /* ---------------------------------------------------------------------- */ -/** - * Iterate over path components, calling \a callback for each component. E.g. "just/some/path" - * iterates over "just", then "some" then "path". - */ -static void iterate_over_catalog_path_components( - const CatalogPath &path, - FunctionRef callback) -{ - const char *next_slash_ptr; - - for (const char *path_component = path.data(); path_component && path_component[0]; - /* Jump to one after the next slash if there is any. */ - path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) { - next_slash_ptr = BLI_path_slash_find(path_component); - - const bool is_last_component = next_slash_ptr == nullptr; - /* Note that this won't be null terminated. */ - const StringRef component_name = is_last_component ? - path_component : - StringRef(path_component, - next_slash_ptr - path_component); - - callback(component_name, is_last_component); - } -} - void AssetCatalogTree::insert_item(const AssetCatalog &catalog) { const AssetCatalogTreeItem *parent = nullptr; @@ -438,30 +410,29 @@ void AssetCatalogTree::insert_item(const AssetCatalog &catalog) * added to (if not there yet). */ AssetCatalogTreeItem::ChildMap *current_item_children = &root_items_; - BLI_assert_msg(!ELEM(catalog.path[0], '/', '\\'), + BLI_assert_msg(!ELEM(catalog.path.str()[0], '/', '\\'), "Malformed catalog path; should not start with a separator"); const CatalogID nil_id{}; - iterate_over_catalog_path_components( - catalog.path, [&](StringRef component_name, const bool is_last_component) { - /* Insert new tree element - if no matching one is there yet! */ - auto [key_and_item, was_inserted] = current_item_children->emplace( - component_name, - AssetCatalogTreeItem( - component_name, is_last_component ? catalog.catalog_id : nil_id, parent)); - AssetCatalogTreeItem &item = key_and_item->second; + catalog.path.iterate_components([&](StringRef component_name, const bool is_last_component) { + /* Insert new tree element - if no matching one is there yet! */ + auto [key_and_item, was_inserted] = current_item_children->emplace( + component_name, + AssetCatalogTreeItem( + component_name, is_last_component ? catalog.catalog_id : nil_id, parent)); + AssetCatalogTreeItem &item = key_and_item->second; - /* If full path of this catalog already exists as parent path of a previously read catalog, - * we can ensure this tree item's UUID is set here. */ - if (is_last_component && BLI_uuid_is_nil(item.catalog_id_)) { - item.catalog_id_ = catalog.catalog_id; - } + /* If full path of this catalog already exists as parent path of a previously read catalog, + * we can ensure this tree item's UUID is set here. */ + if (is_last_component && BLI_uuid_is_nil(item.catalog_id_)) { + item.catalog_id_ = catalog.catalog_id; + } - /* Walk further into the path (no matter if a new item was created or not). */ - parent = &item; - current_item_children = &item.children_; - }); + /* Walk further into the path (no matter if a new item was created or not). */ + parent = &item; + current_item_children = &item.children_; + }); } void AssetCatalogTree::foreach_item(AssetCatalogTreeItem::ItemIterFn callback) @@ -592,7 +563,7 @@ std::unique_ptr AssetCatalogDefinitionFile::parse_catalog_line(con const StringRef path_and_simple_name = line.substr(first_delim + 1); const int64_t second_delim = path_and_simple_name.find_first_of(delim); - CatalogPath catalog_path; + std::string path_in_file; std::string simple_name; if (second_delim == 0) { /* Delimiter as first character means there is no path. These lines are to be ignored. */ @@ -601,16 +572,16 @@ std::unique_ptr AssetCatalogDefinitionFile::parse_catalog_line(con if (second_delim == StringRef::not_found) { /* No delimiter means no simple name, just treat it as all "path". */ - catalog_path = path_and_simple_name; + path_in_file = path_and_simple_name; simple_name = ""; } else { - catalog_path = path_and_simple_name.substr(0, second_delim); + path_in_file = path_and_simple_name.substr(0, second_delim); simple_name = path_and_simple_name.substr(second_delim + 1).trim(); } - catalog_path = AssetCatalog::cleanup_path(catalog_path); - return std::make_unique(catalog_id, catalog_path, simple_name); + CatalogPath catalog_path = path_in_file; + return std::make_unique(catalog_id, catalog_path.cleanup(), simple_name); } bool AssetCatalogDefinitionFile::write_to_disk() const @@ -724,7 +695,7 @@ AssetCatalog::AssetCatalog(const CatalogID catalog_id, std::unique_ptr AssetCatalog::from_path(const CatalogPath &path) { - const CatalogPath clean_path = cleanup_path(path); + const CatalogPath clean_path = path.cleanup(); const CatalogID cat_id = BLI_uuid_generate_random(); const std::string simple_name = sensible_simple_name_for_path(clean_path); auto catalog = std::make_unique(cat_id, clean_path, simple_name); @@ -733,8 +704,8 @@ std::unique_ptr AssetCatalog::from_path(const CatalogPath &path) std::string AssetCatalog::sensible_simple_name_for_path(const CatalogPath &path) { - std::string name = path; - std::replace(name.begin(), name.end(), AssetCatalogService::PATH_SEPARATOR, '-'); + std::string name = path.str(); + std::replace(name.begin(), name.end(), CatalogPath::SEPARATOR, '-'); if (name.length() < MAX_NAME - 1) { return name; } @@ -744,33 +715,4 @@ std::string AssetCatalog::sensible_simple_name_for_path(const CatalogPath &path) return "..." + name.substr(name.length() - 60); } -CatalogPath AssetCatalog::cleanup_path(const CatalogPath &path) -{ - /* TODO(@sybren): maybe go over each element of the path, and trim those? */ - CatalogPath clean_path = StringRef(path).trim().trim(AssetCatalogService::PATH_SEPARATOR).trim(); - return clean_path; -} - -bool AssetCatalog::is_contained_in(const CatalogPath &other_path) const -{ - if (other_path.empty()) { - return true; - } - - if (this->path == other_path) { - return true; - } - - /* To be a child path of 'other_path', our path must be at least a separator and another - * character longer. */ - if (this->path.length() < other_path.length() + 2) { - return false; - } - - const StringRef this_path(this->path); - const bool prefix_ok = this_path.startswith(other_path); - const char next_char = this_path[other_path.length()]; - return prefix_ok && next_char == AssetCatalogService::PATH_SEPARATOR; -} - } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc new file mode 100644 index 00000000000..d8af7be4a02 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_catalog_path.cc @@ -0,0 +1,220 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#include "BKE_asset_catalog_path.hh" + +#include "BLI_path_util.h" + +namespace blender::bke { + +const char AssetCatalogPath::SEPARATOR = '/'; + +AssetCatalogPath::AssetCatalogPath(const std::string &path) : path_(path) +{ +} + +AssetCatalogPath::AssetCatalogPath(StringRef path) : path_(path) +{ +} + +AssetCatalogPath::AssetCatalogPath(const char *path) : path_(path) +{ +} + +AssetCatalogPath::AssetCatalogPath(const AssetCatalogPath &other_path) : path_(other_path.path_) +{ +} + +AssetCatalogPath::AssetCatalogPath(AssetCatalogPath &&other_path) noexcept + : path_(std::move(other_path.path_)) +{ +} + +uint64_t AssetCatalogPath::hash() const +{ + std::hash hasher{}; + return hasher(this->path_); +} + +uint64_t AssetCatalogPath::length() const +{ + return this->path_.length(); +} + +const char *AssetCatalogPath::c_str() const +{ + return this->path_.c_str(); +} + +const std::string &AssetCatalogPath::str() const +{ + return this->path_; +} + +/* In-class operators, because of the implicit `AssetCatalogPath(StringRef)` constructor. + * Otherwise `string == string` could cast both sides to `AssetCatalogPath`. */ +bool AssetCatalogPath::operator==(const AssetCatalogPath &other_path) const +{ + return this->path_ == other_path.path_; +} + +bool AssetCatalogPath::operator!=(const AssetCatalogPath &other_path) const +{ + return !(*this == other_path); +} + +bool AssetCatalogPath::operator<(const AssetCatalogPath &other_path) const +{ + return this->path_ < other_path.path_; +} + +AssetCatalogPath AssetCatalogPath::operator/(const AssetCatalogPath &path_to_append) const +{ + /* `"" / "path"` or `"path" / ""` should just result in `"path"` */ + if (!*this) { + return path_to_append; + } + if (!path_to_append) { + return *this; + } + + std::stringstream new_path; + new_path << this->path_ << SEPARATOR << path_to_append.path_; + return AssetCatalogPath(new_path.str()); +} + +AssetCatalogPath::operator bool() const +{ + return !this->path_.empty(); +} + +std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append) +{ + stream << path_to_append.path_; + return stream; +} + +AssetCatalogPath AssetCatalogPath::cleanup() const +{ + std::stringstream clean_components; + bool first_component_seen = false; + + this->iterate_components([&clean_components, &first_component_seen](StringRef component_name, + bool /*is_last_component*/) { + const std::string clean_component = cleanup_component(component_name); + + if (clean_component.empty()) { + /* These are caused by leading, trailing, or double slashes. */ + return; + } + + /* If a previous path component has been streamed already, we need a path separator. This + * cannot use the `is_last_component` boolean, because the last component might be skipped due + * to the condition above. */ + if (first_component_seen) { + clean_components << SEPARATOR; + } + first_component_seen = true; + + clean_components << clean_component; + }); + + return AssetCatalogPath(clean_components.str()); +} + +std::string AssetCatalogPath::cleanup_component(StringRef component) +{ + std::string cleaned = component.trim(); + /* Replace colons with something else, as those are used in the CDF file as delimiter. */ + std::replace(cleaned.begin(), cleaned.end(), ':', '-'); + return cleaned; +} + +bool AssetCatalogPath::is_contained_in(const AssetCatalogPath &other_path) const +{ + if (!other_path) { + /* The empty path contains all other paths. */ + return true; + } + + if (this->path_ == other_path.path_) { + /* Weak is-in relation: equal paths contain each other. */ + return true; + } + + /* To be a child path of 'other_path', our path must be at least a separator and another + * character longer. */ + if (this->length() < other_path.length() + 2) { + return false; + } + + /* Create StringRef to be able to use .startswith(). */ + const StringRef this_path(this->path_); + const bool prefix_ok = this_path.startswith(other_path.path_); + const char next_char = this_path[other_path.length()]; + return prefix_ok && next_char == SEPARATOR; +} + +void AssetCatalogPath::iterate_components(ComponentIteratorFn callback) const +{ + const char *next_slash_ptr; + + for (const char *path_component = this->path_.data(); path_component && path_component[0]; + /* Jump to one after the next slash if there is any. */ + path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) { + next_slash_ptr = BLI_path_slash_find(path_component); + + const bool is_last_component = next_slash_ptr == nullptr; + /* Note that this won't be null terminated. */ + const StringRef component_name = is_last_component ? + path_component : + StringRef(path_component, + next_slash_ptr - path_component); + + callback(component_name, is_last_component); + } +} + +AssetCatalogPath AssetCatalogPath::rebase(const AssetCatalogPath &from_path, + const AssetCatalogPath &to_path) const +{ + if (!from_path) { + if (!to_path) { + return AssetCatalogPath(""); + } + return to_path / *this; + } + + if (!this->is_contained_in(from_path)) { + return AssetCatalogPath(""); + } + + if (*this == from_path) { + /* Early return, because otherwise the length+1 below is going to cause problems. */ + return to_path; + } + + /* When from_path = "abcd", we need to skip "abcd/" to get the rest of the path, hence the +1. */ + const StringRef suffix = StringRef(this->path_).substr(from_path.length() + 1); + const AssetCatalogPath path_suffix(suffix); + return to_path / path_suffix; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc new file mode 100644 index 00000000000..55919abbb8f --- /dev/null +++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc @@ -0,0 +1,234 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation + * All rights reserved. + */ + +#include "BKE_asset_catalog_path.hh" + +#include "BLI_set.hh" +#include "BLI_vector.hh" + +#include +#include + +#include "testing/testing.h" + +namespace blender::bke::tests { + +TEST(AssetCatalogPathTest, construction) +{ + AssetCatalogPath from_char_literal("the/path"); + + const std::string str_const = "the/path"; + AssetCatalogPath from_string_constant(str_const); + + std::string str_variable = "the/path"; + AssetCatalogPath from_string_variable(str_variable); + + std::string long_string = "this is a long/string/with/a/path in the middle"; + StringRef long_string_ref(long_string); + StringRef middle_bit = long_string_ref.substr(10, 23); + AssetCatalogPath from_string_ref(middle_bit); + EXPECT_EQ(from_string_ref, "long/string/with/a/path"); +} + +TEST(AssetCatalogPathTest, length) +{ + const AssetCatalogPath one("1"); + EXPECT_EQ(1, one.length()); + + const AssetCatalogPath empty(""); + EXPECT_EQ(0, empty.length()); + + const AssetCatalogPath utf8("some/родитель"); + EXPECT_EQ(21, utf8.length()) << "13 characters should be 21 bytes."; +} + +TEST(AssetCatalogPathTest, comparison_operators) +{ + const AssetCatalogPath empty(""); + const AssetCatalogPath the_path("the/path"); + const AssetCatalogPath the_path_child("the/path/child"); + const AssetCatalogPath unrelated_path("unrelated/path"); + const AssetCatalogPath other_instance_same_path("the/path"); + + EXPECT_LT(empty, the_path); + EXPECT_LT(the_path, the_path_child); + EXPECT_LT(the_path, unrelated_path); + + EXPECT_EQ(empty, empty) << "Identical empty instances should compare equal."; + EXPECT_EQ(empty, "") << "Comparison to empty string should be possible."; + EXPECT_EQ(the_path, the_path) << "Identical non-empty instances should compare equal."; + EXPECT_EQ(the_path, "the/path") << "Comparison to string should be possible."; + EXPECT_EQ(the_path, other_instance_same_path) + << "Different instances with equal path should compare equal."; + + EXPECT_NE(the_path, the_path_child); + EXPECT_NE(the_path, unrelated_path); + EXPECT_NE(the_path, empty); + + EXPECT_FALSE(empty); + EXPECT_TRUE(the_path); +} + +TEST(AssetCatalogPathTest, move_semantics) +{ + AssetCatalogPath source_path("source/path"); + EXPECT_TRUE(source_path); + + AssetCatalogPath dest_path = std::move(source_path); + EXPECT_FALSE(source_path); + EXPECT_TRUE(dest_path); +} + +TEST(AssetCatalogPathTest, concatenation) +{ + AssetCatalogPath some_parent("some/родитель"); + AssetCatalogPath child = some_parent / "ребенок"; + + EXPECT_EQ(some_parent, "some/родитель") + << "Appending a child path should not modify the parent."; + EXPECT_EQ(child, "some/родитель/ребенок"); + + AssetCatalogPath appended_compound_path = some_parent / "ребенок/внук"; + EXPECT_EQ(appended_compound_path, "some/родитель/ребенок/внук"); + + AssetCatalogPath empty(""); + AssetCatalogPath child_of_the_void = empty / "child"; + EXPECT_EQ(child_of_the_void, "child") + << "Appending to an empty path should not create an initial slash."; + + AssetCatalogPath parent_of_the_void = some_parent / empty; + EXPECT_EQ(parent_of_the_void, "some/родитель") + << "Prepending to an empty path should not create a trailing slash."; + + std::string subpath = "child"; + AssetCatalogPath concatenated_with_string = some_parent / subpath; + EXPECT_EQ(concatenated_with_string, "some/родитель/child"); +} + +TEST(AssetCatalogPathTest, hashable) +{ + AssetCatalogPath path("heyyyyy"); + + std::set path_std_set; + path_std_set.insert(path); + + blender::Set path_blender_set; + path_blender_set.add(path); +} + +TEST(AssetCatalogPathTest, stream_operator) +{ + AssetCatalogPath path("путь/в/Пермь"); + std::stringstream sstream; + sstream << path; + EXPECT_EQ("путь/в/Пермь", sstream.str()); +} + +TEST(AssetCatalogPathTest, is_contained_in) +{ + const AssetCatalogPath catpath("simple/path/child"); + EXPECT_FALSE(catpath.is_contained_in("unrelated")); + EXPECT_FALSE(catpath.is_contained_in("sim")); + EXPECT_FALSE(catpath.is_contained_in("simple/pathx")); + EXPECT_FALSE(catpath.is_contained_in("simple/path/c")); + EXPECT_FALSE(catpath.is_contained_in("simple/path/child/grandchild")); + EXPECT_FALSE(catpath.is_contained_in("simple/path/")) + << "Non-normalized paths are not expected to work."; + + EXPECT_TRUE(catpath.is_contained_in("")); + EXPECT_TRUE(catpath.is_contained_in("simple")); + EXPECT_TRUE(catpath.is_contained_in("simple/path")); + + /* Test with some UTF8 non-ASCII characters. */ + AssetCatalogPath some_parent("some/родитель"); + AssetCatalogPath child = some_parent / "ребенок"; + + EXPECT_TRUE(child.is_contained_in(some_parent)); + EXPECT_TRUE(child.is_contained_in("some")); + + AssetCatalogPath appended_compound_path = some_parent / "ребенок/внук"; + EXPECT_TRUE(appended_compound_path.is_contained_in(some_parent)); + EXPECT_TRUE(appended_compound_path.is_contained_in(child)); + + /* Test "going up" directory-style. */ + AssetCatalogPath child_with_dotdot = some_parent / "../../other/hierarchy/part"; + EXPECT_TRUE(child_with_dotdot.is_contained_in(some_parent)) + << "dotdot path components should have no meaning"; +} + +TEST(AssetCatalogPathTest, cleanup) +{ + AssetCatalogPath ugly_path("/ some / родитель / "); + AssetCatalogPath clean_path = ugly_path.cleanup(); + + EXPECT_EQ(AssetCatalogPath("/ some / родитель / "), ugly_path) + << "cleanup should not modify the path instance itself"; + + EXPECT_EQ(AssetCatalogPath("some/родитель"), clean_path); + + AssetCatalogPath double_slashed("some//родитель"); + EXPECT_EQ(AssetCatalogPath("some/родитель"), double_slashed.cleanup()); + + AssetCatalogPath with_colons("some/key:subkey=value/path"); + EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup()); +} + +TEST(AssetCatalogPathTest, iterate_components) +{ + AssetCatalogPath path("путь/в/Пермь"); + Vector> seen_components; + + path.iterate_components([&seen_components](StringRef component_name, bool is_last_component) { + std::pair parameter_pair = std::make_pair( + component_name, bool(is_last_component)); + seen_components.append(parameter_pair); + }); + + ASSERT_EQ(3, seen_components.size()); + + EXPECT_EQ("путь", seen_components[0].first); + EXPECT_EQ("в", seen_components[1].first); + EXPECT_EQ("Пермь", seen_components[2].first); + + EXPECT_FALSE(seen_components[0].second); + EXPECT_FALSE(seen_components[1].second); + EXPECT_TRUE(seen_components[2].second); +} + +TEST(AssetCatalogPathTest, rebase) +{ + AssetCatalogPath path("some/path/to/some/catalog"); + EXPECT_EQ(path.rebase("some/path", "new/base"), "new/base/to/some/catalog"); + EXPECT_EQ(path.rebase("", "new/base"), "new/base/some/path/to/some/catalog"); + + EXPECT_EQ(path.rebase("some/path/to/some/catalog", "some/path/to/some/catalog"), + "some/path/to/some/catalog") + << "Rebasing to itself should not change the path."; + + EXPECT_EQ(path.rebase("path/to", "new/base"), "") + << "Non-matching base path should return empty string to indicate 'NO'."; + + /* Empty strings should be handled without crashing or other nasty side-effects. */ + AssetCatalogPath empty(""); + EXPECT_EQ(empty.rebase("path/to", "new/base"), ""); + EXPECT_EQ(empty.rebase("", "new/base"), "new/base"); + EXPECT_EQ(empty.rebase("", ""), ""); +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 5b94f021797..cde16b97b5d 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -106,7 +106,7 @@ class AssetCatalogTest : public testing::Test { EXPECT_EQ(expected_filename, actual_item.get_name()); /* Does the computed number of parents match? */ EXPECT_EQ(expected_path.parent_count, actual_item.count_parents()); - EXPECT_EQ(expected_path.name, actual_item.catalog_path()); + EXPECT_EQ(expected_path.name, actual_item.catalog_path().str()); } /** @@ -186,21 +186,21 @@ TEST_F(AssetCatalogTest, load_single_file) AssetCatalog *poses_ellie = service.find_catalog(UUID_POSES_ELLIE); ASSERT_NE(nullptr, poses_ellie); EXPECT_EQ(UUID_POSES_ELLIE, poses_ellie->catalog_id); - EXPECT_EQ("character/Ellie/poselib", poses_ellie->path); + EXPECT_EQ("character/Ellie/poselib", poses_ellie->path.str()); EXPECT_EQ("POSES_ELLIE", poses_ellie->simple_name); /* Test white-space stripping and support in the path. */ AssetCatalog *poses_whitespace = service.find_catalog(UUID_POSES_ELLIE_WHITESPACE); ASSERT_NE(nullptr, poses_whitespace); EXPECT_EQ(UUID_POSES_ELLIE_WHITESPACE, poses_whitespace->catalog_id); - EXPECT_EQ("character/Ellie/poselib/white space", poses_whitespace->path); + EXPECT_EQ("character/Ellie/poselib/white space", poses_whitespace->path.str()); EXPECT_EQ("POSES_ELLIE WHITESPACE", poses_whitespace->simple_name); /* Test getting a UTF-8 catalog ID. */ AssetCatalog *poses_ruzena = service.find_catalog(UUID_POSES_RUZENA); ASSERT_NE(nullptr, poses_ruzena); EXPECT_EQ(UUID_POSES_RUZENA, poses_ruzena->catalog_id); - EXPECT_EQ("character/Ružena/poselib", poses_ruzena->path); + EXPECT_EQ("character/Ružena/poselib", poses_ruzena->path.str()); EXPECT_EQ("POSES_RUŽENA", poses_ruzena->simple_name); } @@ -588,7 +588,7 @@ TEST_F(AssetCatalogTest, create_first_catalog_from_scratch) AssetCatalog *written_cat = loaded_service.find_catalog(cat->catalog_id); ASSERT_NE(nullptr, written_cat); EXPECT_EQ(written_cat->catalog_id, cat->catalog_id); - EXPECT_EQ(written_cat->path, cat->path); + EXPECT_EQ(written_cat->path, cat->path.str()); } TEST_F(AssetCatalogTest, create_catalog_after_loading_file) @@ -640,7 +640,7 @@ TEST_F(AssetCatalogTest, create_catalog_path_cleanup) AssetCatalog *cat = service.create_catalog(" /some/path / "); EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id)); - EXPECT_EQ("some/path", cat->path); + EXPECT_EQ("some/path", cat->path.str()); EXPECT_EQ("some-path", cat->simple_name); } @@ -652,7 +652,7 @@ TEST_F(AssetCatalogTest, create_catalog_simple_name) EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id)); EXPECT_EQ("production/Spite Fright/Characters/Victora/Pose Library/Approved/Body Parts/Hands", - cat->path); + cat->path.str()); EXPECT_EQ("...ht-Characters-Victora-Pose Library-Approved-Body Parts-Hands", cat->simple_name); } @@ -733,12 +733,12 @@ TEST_F(AssetCatalogTest, update_catalog_path) EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id) << "Changing the path should not change the catalog ID."; - EXPECT_EQ("charlib/Ružena", renamed_cat->path) + EXPECT_EQ("charlib/Ružena", renamed_cat->path.str()) << "Changing the path should change the path. Surprise."; - EXPECT_EQ("charlib/Ružena/hand", service.find_catalog(UUID_POSES_RUZENA_HAND)->path) + EXPECT_EQ("charlib/Ružena/hand", service.find_catalog(UUID_POSES_RUZENA_HAND)->path.str()) << "Changing the path should update children."; - EXPECT_EQ("charlib/Ružena/face", service.find_catalog(UUID_POSES_RUZENA_FACE)->path) + EXPECT_EQ("charlib/Ružena/face", service.find_catalog(UUID_POSES_RUZENA_FACE)->path.str()) << "Changing the path should update children."; } @@ -775,7 +775,7 @@ TEST_F(AssetCatalogTest, merge_catalog_files) /* When there are overlaps, the in-memory (i.e. last-saved) paths should win. */ const AssetCatalog *ruzena_face = loaded_service.find_catalog(UUID_POSES_RUZENA_FACE); - EXPECT_EQ("character/Ružena/poselib/face", ruzena_face->path); + EXPECT_EQ("character/Ružena/poselib/face", ruzena_face->path.str()); } TEST_F(AssetCatalogTest, backups) @@ -846,21 +846,4 @@ TEST_F(AssetCatalogTest, order_by_path) } } -TEST_F(AssetCatalogTest, is_contained_in) -{ - const AssetCatalog cat(BLI_uuid_generate_random(), "simple/path/child", ""); - - EXPECT_FALSE(cat.is_contained_in("unrelated")); - EXPECT_FALSE(cat.is_contained_in("sim")); - EXPECT_FALSE(cat.is_contained_in("simple/pathx")); - EXPECT_FALSE(cat.is_contained_in("simple/path/c")); - EXPECT_FALSE(cat.is_contained_in("simple/path/child/grandchild")); - EXPECT_FALSE(cat.is_contained_in("simple/path/")) - << "Non-normalized paths are not expected to work."; - - EXPECT_TRUE(cat.is_contained_in("")); - EXPECT_TRUE(cat.is_contained_in("simple")); - EXPECT_TRUE(cat.is_contained_in("simple/path")); -} - } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc index 37686175aed..30ac4dc6ad8 100644 --- a/source/blender/blenkernel/intern/asset_library_test.cc +++ b/source/blender/blenkernel/intern/asset_library_test.cc @@ -49,7 +49,7 @@ TEST(AssetLibraryTest, load_and_free_c_functions) const bUUID uuid_poses_ellie("df60e1f6-2259-475b-93d9-69a1b4a8db78"); AssetCatalog *poses_ellie = service->find_catalog(uuid_poses_ellie); ASSERT_NE(nullptr, poses_ellie) << "unable to find POSES_ELLIE catalog"; - EXPECT_EQ("character/Ellie/poselib", poses_ellie->path); + EXPECT_EQ("character/Ellie/poselib", poses_ellie->path.str()); BKE_asset_library_free(library_c_ptr); } diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index 68f11d77f44..c3e5a888796 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -19,6 +19,7 @@ */ #include "BKE_asset_catalog.hh" +#include "BKE_asset_catalog_path.hh" #include "BKE_asset_library.hh" #include "BLI_string_utils.h" @@ -33,17 +34,10 @@ struct CatalogUniqueNameFnData { StringRef parent_path; }; -static std::string to_full_path(StringRef parent_path, StringRef name) -{ - return parent_path.is_empty() ? - std::string(name) : - std::string(parent_path) + AssetCatalogService::PATH_SEPARATOR + name; -} - static bool catalog_name_exists_fn(void *arg, const char *name) { CatalogUniqueNameFnData &fn_data = *static_cast(arg); - std::string fullpath = to_full_path(fn_data.parent_path, name); + CatalogPath fullpath = CatalogPath(fn_data.parent_path) / name; return fn_data.catalog_service.find_catalog_by_path(fullpath); } @@ -70,7 +64,7 @@ AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library, } std::string unique_name = catalog_name_ensure_unique(*catalog_service, name, parent_path); - std::string fullpath = to_full_path(parent_path, unique_name); + CatalogPath fullpath = CatalogPath(parent_path) / unique_name; return catalog_service->create_catalog(fullpath); } From 42ce88f15cb77c859c6b2fed119934836235b961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 30 Sep 2021 16:34:30 +0200 Subject: [PATCH 0394/1500] Cleanup: remove `CatalogPath` alias The `CatalogPath` name was an alias for `std::string`, so that it could be easily switched over to something else. This happened in the previous commit (switched to `AssetCatalogPath`), so the alias is no longer necessary. This commit removes the `CatalogPath` alias. No functional changes. --- .../blender/blenkernel/BKE_asset_catalog.hh | 17 ++++++----- .../blenkernel/intern/asset_catalog.cc | 28 +++++++++---------- .../blenkernel/intern/asset_catalog_test.cc | 2 +- .../editors/asset/intern/asset_catalog.cc | 4 +-- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index c141ce5fca0..9d179011b25 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -40,7 +40,6 @@ namespace blender::bke { using CatalogID = bUUID; -using CatalogPath = AssetCatalogPath; using CatalogPathComponent = std::string; /* Would be nice to be able to use `std::filesystem::path` for this, but it's currently not * available on the minimum macOS target version. */ @@ -99,11 +98,11 @@ class AssetCatalogService { /** Return first catalog with the given path. Return nullptr if not found. This is not an * efficient call as it's just a linear search over the catalogs. */ - AssetCatalog *find_catalog_by_path(const CatalogPath &path) const; + AssetCatalog *find_catalog_by_path(const AssetCatalogPath &path) const; /** Create a catalog with some sensible auto-generated catalog ID. * The catalog will be saved to the default catalog file.*/ - AssetCatalog *create_catalog(const CatalogPath &catalog_path); + AssetCatalog *create_catalog(const AssetCatalogPath &catalog_path); /** * Soft-delete the catalog, ensuring it actually gets deleted when the catalog definition file is @@ -113,7 +112,7 @@ class AssetCatalogService { /** * Update the catalog path, also updating the catalog path of all sub-catalogs. */ - void update_catalog_path(CatalogID catalog_id, const CatalogPath &new_catalog_path); + void update_catalog_path(CatalogID catalog_id, const AssetCatalogPath &new_catalog_path); AssetCatalogTree *get_catalog_tree(); @@ -173,7 +172,7 @@ class AssetCatalogTreeItem { StringRef get_name() const; /** Return the full catalog path, defined as the name of this catalog prefixed by the full * catalog path of its parent and a separator. */ - CatalogPath catalog_path() const; + AssetCatalogPath catalog_path() const; int count_parents() const; bool has_children() const; @@ -281,10 +280,10 @@ class AssetCatalogDefinitionFile { class AssetCatalog { public: AssetCatalog() = default; - AssetCatalog(CatalogID catalog_id, const CatalogPath &path, const std::string &simple_name); + AssetCatalog(CatalogID catalog_id, const AssetCatalogPath &path, const std::string &simple_name); CatalogID catalog_id; - CatalogPath path; + AssetCatalogPath path; /** * Simple, human-readable name for the asset catalog. This is stored on assets alongside the * catalog ID; the catalog ID is a UUID that is not human-readable, @@ -304,11 +303,11 @@ class AssetCatalog { * NOTE: the given path will be cleaned up (trailing spaces removed, etc.), so the returned * `AssetCatalog`'s path differ from the given one. */ - static std::unique_ptr from_path(const CatalogPath &path); + static std::unique_ptr from_path(const AssetCatalogPath &path); protected: /** Generate a sensible catalog ID for the given path. */ - static std::string sensible_simple_name_for_path(const CatalogPath &path); + static std::string sensible_simple_name_for_path(const AssetCatalogPath &path); }; /** Comparator for asset catalogs, ordering by (path, UUID). */ diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 300a15fad6d..bb213877e05 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -74,7 +74,7 @@ AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) return catalog_uptr_ptr->get(); } -AssetCatalog *AssetCatalogService::find_catalog_by_path(const CatalogPath &path) const +AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath &path) const { for (const auto &catalog : catalogs_.values()) { if (catalog->path == path) { @@ -107,15 +107,15 @@ void AssetCatalogService::delete_catalog(CatalogID catalog_id) } void AssetCatalogService::update_catalog_path(CatalogID catalog_id, - const CatalogPath &new_catalog_path) + const AssetCatalogPath &new_catalog_path) { AssetCatalog *renamed_cat = this->find_catalog(catalog_id); - const CatalogPath old_cat_path = renamed_cat->path; + const AssetCatalogPath old_cat_path = renamed_cat->path; for (auto &catalog_uptr : catalogs_.values()) { AssetCatalog *cat = catalog_uptr.get(); - const CatalogPath new_path = cat->path.rebase(old_cat_path, new_catalog_path); + const AssetCatalogPath new_path = cat->path.rebase(old_cat_path, new_catalog_path); if (!new_path) { continue; } @@ -125,7 +125,7 @@ void AssetCatalogService::update_catalog_path(CatalogID catalog_id, this->rebuild_tree(); } -AssetCatalog *AssetCatalogService::create_catalog(const CatalogPath &catalog_path) +AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalog_path) { std::unique_ptr catalog = AssetCatalog::from_path(catalog_path); @@ -378,11 +378,11 @@ StringRef AssetCatalogTreeItem::get_name() const return name_; } -CatalogPath AssetCatalogTreeItem::catalog_path() const +AssetCatalogPath AssetCatalogTreeItem::catalog_path() const { - CatalogPath current_path = name_; + AssetCatalogPath current_path = name_; for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) { - current_path = CatalogPath(parent->name_) / current_path; + current_path = AssetCatalogPath(parent->name_) / current_path; } return current_path; } @@ -580,7 +580,7 @@ std::unique_ptr AssetCatalogDefinitionFile::parse_catalog_line(con simple_name = path_and_simple_name.substr(second_delim + 1).trim(); } - CatalogPath catalog_path = path_in_file; + AssetCatalogPath catalog_path = path_in_file; return std::make_unique(catalog_id, catalog_path.cleanup(), simple_name); } @@ -687,25 +687,25 @@ bool AssetCatalogDefinitionFile::ensure_directory_exists( } AssetCatalog::AssetCatalog(const CatalogID catalog_id, - const CatalogPath &path, + const AssetCatalogPath &path, const std::string &simple_name) : catalog_id(catalog_id), path(path), simple_name(simple_name) { } -std::unique_ptr AssetCatalog::from_path(const CatalogPath &path) +std::unique_ptr AssetCatalog::from_path(const AssetCatalogPath &path) { - const CatalogPath clean_path = path.cleanup(); + const AssetCatalogPath clean_path = path.cleanup(); const CatalogID cat_id = BLI_uuid_generate_random(); const std::string simple_name = sensible_simple_name_for_path(clean_path); auto catalog = std::make_unique(cat_id, clean_path, simple_name); return catalog; } -std::string AssetCatalog::sensible_simple_name_for_path(const CatalogPath &path) +std::string AssetCatalog::sensible_simple_name_for_path(const AssetCatalogPath &path) { std::string name = path.str(); - std::replace(name.begin(), name.end(), CatalogPath::SEPARATOR, '-'); + std::replace(name.begin(), name.end(), AssetCatalogPath::SEPARATOR, '-'); if (name.length() < MAX_NAME - 1) { return name; } diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index cde16b97b5d..d7c3e50cbdf 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -718,7 +718,7 @@ TEST_F(AssetCatalogTest, update_catalog_path) AssetCatalogService::DEFAULT_CATALOG_FILENAME); const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA); - const CatalogPath orig_path = orig_cat->path; + const AssetCatalogPath orig_path = orig_cat->path; service.update_catalog_path(UUID_POSES_RUZENA, "charlib/Ružena"); diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index c3e5a888796..6e49ca2dd5c 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -37,7 +37,7 @@ struct CatalogUniqueNameFnData { static bool catalog_name_exists_fn(void *arg, const char *name) { CatalogUniqueNameFnData &fn_data = *static_cast(arg); - CatalogPath fullpath = CatalogPath(fn_data.parent_path) / name; + AssetCatalogPath fullpath = AssetCatalogPath(fn_data.parent_path) / name; return fn_data.catalog_service.find_catalog_by_path(fullpath); } @@ -64,7 +64,7 @@ AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library, } std::string unique_name = catalog_name_ensure_unique(*catalog_service, name, parent_path); - CatalogPath fullpath = CatalogPath(parent_path) / unique_name; + AssetCatalogPath fullpath = AssetCatalogPath(parent_path) / unique_name; return catalog_service->create_catalog(fullpath); } From 4ee2d9df428d16f07e351f5554b951ae75804ea0 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 30 Sep 2021 16:26:56 +0200 Subject: [PATCH 0395/1500] UI: Support easy dropping into/onto rows in new tree-view API Adds an easy way to add drop support for tree-view rows. Most of the work is handled by the tree-view UI code. The tree items can simply override a few functions (`can_drop()`, `on_drop()`, `drop_tooltip()`) to implement their custom drop behavior. While dragging over a tree-view item that can be dropped into/onto, the item can show a custom and dynamic tooltip explaining what's gonna happen on drop. This isn't used yet, but will soon be for asset catalogs. See documentation here: https://wiki.blender.org/wiki/Source/Interface/Views#Further_Customizations --- source/blender/editors/include/UI_interface.h | 9 +++ .../blender/editors/include/UI_tree_view.hh | 18 ++++- .../blender/editors/interface/CMakeLists.txt | 1 + .../editors/interface/interface_dropboxes.cc | 66 +++++++++++++++++++ .../editors/interface/interface_intern.h | 1 + .../blender/editors/interface/interface_ops.c | 47 +++++++++++++ .../editors/interface/interface_query.c | 10 +++ .../editors/interface/interface_view.cc | 17 +++++ source/blender/editors/interface/tree_view.cc | 52 +++++++++++++++ source/blender/editors/screen/area.c | 3 + source/blender/editors/space_api/spacetypes.c | 1 + 11 files changed, 222 insertions(+), 3 deletions(-) create mode 100644 source/blender/editors/interface/interface_dropboxes.cc diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 106f6166760..f642895f64e 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2593,6 +2593,7 @@ typedef struct uiDragColorHandle { void ED_operatortypes_ui(void); void ED_keymap_ui(struct wmKeyConfig *keyconf); +void ED_dropboxes_ui(void); void ED_uilisttypes_ui(void); void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop); @@ -2763,6 +2764,14 @@ void UI_interface_tag_script_reload(void); bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); +bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const struct wmDrag *drag); +bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const struct ListBase *drags); +char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, + const struct bContext *C, + const struct wmDrag *drag, + const struct wmEvent *event); + +uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, int x, int y); #ifdef __cplusplus } diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 81a614cd195..d36e688dd65 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -29,11 +29,14 @@ #include "UI_resources.h" +struct bContext; struct PointerRNA; struct uiBlock; struct uiBut; struct uiButTreeRow; struct uiLayout; +struct wmEvent; +struct wmDrag; namespace blender::ui { @@ -185,10 +188,19 @@ class AbstractTreeViewItem : public TreeViewItemContainer { virtual void build_row(uiLayout &row) = 0; virtual void on_activate(); + virtual bool on_drop(const wmDrag &drag); + virtual bool can_drop(const wmDrag &drag) const; + /** Custom text to display when dragging over a tree item. Should explain what happens when + * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() + * returns true, so the implementing override doesn't have to check that again. + * The returned value must be a translated string. */ + virtual std::string drop_tooltip(const bContext &C, + const wmDrag &drag, + const wmEvent &event) const; - /** Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of the - * last redraw to this item. If sub-classes introduce more advanced state they should override - * this and make it update their state accordingly. */ + /** Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of + * the last redraw to this item. If sub-classes introduce more advanced state they should + * override this and make it update their state accordingly. */ virtual void update_from_old(const AbstractTreeViewItem &old); /** Compare this item to \a other to check if they represent the same data. This is critical for * being able to recognize an item from a previous redraw, to be able to keep its state (e.g. diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 79e08f46292..8fcc704a301 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -42,6 +42,7 @@ set(SRC interface_button_group.c interface_context_menu.c interface_draw.c + interface_dropboxes.cc interface_eyedropper.c interface_eyedropper_color.c interface_eyedropper_colorband.c diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc new file mode 100644 index 00000000000..cb33e7f736e --- /dev/null +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -0,0 +1,66 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edinterface + */ + +#include "BKE_context.h" + +#include "DNA_space_types.h" + +#include "WM_api.h" + +#include "UI_interface.h" + +static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + const ARegion *region = CTX_wm_region(C); + const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( + region, event->x, event->y); + if (!hovered_tree_item) { + return false; + } + + return UI_tree_view_item_can_drop(hovered_tree_item, drag); +} + +static char *ui_tree_view_drop_tooltip(bContext *C, + wmDrag *drag, + const wmEvent *event, + wmDropBox *UNUSED(drop)) +{ + const ARegion *region = CTX_wm_region(C); + const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( + region, event->x, event->y); + if (!hovered_tree_item) { + return nullptr; + } + + return UI_tree_view_item_drop_tooltip(hovered_tree_item, C, drag, event); +} + +void ED_dropboxes_ui() +{ + ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0); + + WM_dropbox_add(lb, + "UI_OT_tree_view_drop", + ui_tree_view_drop_poll, + nullptr, + nullptr, + ui_tree_view_drop_tooltip); +} diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 95e6791b359..8b45d9faae6 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1171,6 +1171,7 @@ uiBut *ui_list_row_find_mouse_over(const struct ARegion *region, uiBut *ui_list_row_find_from_index(const struct ARegion *region, const int index, uiBut *listbox) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int x, const int y); typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region, diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 7b59a6f7263..1fc07bce341 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1917,6 +1917,51 @@ static void UI_OT_list_start_filter(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name UI Tree-View Drop Operator + * \{ */ + +static bool ui_tree_view_drop_poll(bContext *C) +{ + const wmWindow *win = CTX_wm_window(C); + const ARegion *region = CTX_wm_region(C); + const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( + region, win->eventstate->x, win->eventstate->y); + + return hovered_tree_item != NULL; +} + +static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + if (event->custom != EVT_DATA_DRAGDROP) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + const ARegion *region = CTX_wm_region(C); + uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( + region, event->x, event->y); + + if (!UI_tree_view_item_drop_handle(hovered_tree_item, event->customdata)) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + return OPERATOR_FINISHED; +} + +static void UI_OT_tree_view_drop(wmOperatorType *ot) +{ + ot->name = "Tree View drop"; + ot->idname = "UI_OT_tree_view_drop"; + ot->description = "Drag and drop items onto a tree item"; + + ot->invoke = ui_tree_view_drop_invoke; + ot->poll = ui_tree_view_drop_poll; + + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Operator & Keymap Registration * \{ */ @@ -1944,6 +1989,8 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_list_start_filter); + WM_operatortype_append(UI_OT_tree_view_drop); + /* external */ WM_operatortype_append(UI_OT_eyedropper_color); WM_operatortype_append(UI_OT_eyedropper_colorramp); diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 15d1d2f2eec..2f6bda3252d 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -463,6 +463,16 @@ uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut return ui_but_find(region, ui_but_is_listrow_at_index, &data); } +static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata)) +{ + return but->type == UI_BTYPE_TREEROW; +} + +uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int x, const int y) +{ + return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_treerow, NULL); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc index 7419f21cbc6..b199ce9562e 100644 --- a/source/blender/editors/interface/interface_view.cc +++ b/source/blender/editors/interface/interface_view.cc @@ -26,6 +26,8 @@ #include #include +#include "DNA_screen_types.h" + #include "BLI_listbase.h" #include "interface_intern.h" @@ -77,6 +79,21 @@ void ui_block_free_views(uiBlock *block) } } +/** + * \param x, y: Coordinate to find a tree-row item at, in window space. + */ +uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, + const int x, + const int y) +{ + uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, x, y); + if (!tree_row_but) { + return nullptr; + } + + return tree_row_but->tree_item; +} + static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view) { /* First get the idname the of the view we're looking for. */ diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 16499065019..0ea15a2a5bb 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -20,6 +20,8 @@ #include "DNA_userdef_types.h" +#include "BLT_translation.h" + #include "interface_intern.h" #include "UI_interface.h" @@ -139,6 +141,24 @@ void AbstractTreeViewItem::on_activate() /* Do nothing by default. */ } +bool AbstractTreeViewItem::on_drop(const wmDrag & /*drag*/) +{ + /* Do nothing by default. */ + return false; +} + +bool AbstractTreeViewItem::can_drop(const wmDrag & /*drag*/) const +{ + return false; +} + +std::string AbstractTreeViewItem::drop_tooltip(const bContext & /*C*/, + const wmDrag & /*drag*/, + const wmEvent & /*event*/) const +{ + return TIP_("Drop into/onto tree item"); +} + void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) { is_open_ = old.is_open_; @@ -327,3 +347,35 @@ bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, const AbstractTreeViewItem &b = reinterpret_cast(*b_handle); return a.matches(b); } + +bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const wmDrag *drag) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_); + return item.can_drop(*drag); +} + +char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, + const bContext *C, + const wmDrag *drag, + const wmEvent *event) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_); + return BLI_strdup(item.drop_tooltip(*C, *drag, *event).c_str()); +} + +/** + * Let a tree-view item handle a drop event. + * \return True if the drop was handled by the tree-view item. + */ +bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const ListBase *drags) +{ + AbstractTreeViewItem &item = reinterpret_cast(*item_); + + LISTBASE_FOREACH (const wmDrag *, drag, drags) { + if (item.can_drop(*drag)) { + return item.on_drop(*drag); + } + } + + return false; +} diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index d791c0717be..833c9accf95 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1699,6 +1699,9 @@ static void ed_default_handlers( wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "User Interface", 0, 0); WM_event_add_keymap_handler(handlers, keymap); + ListBase *dropboxes = WM_dropboxmap_find("User Interface", 0, 0); + WM_event_add_dropbox_handler(handlers, dropboxes); + /* user interface widgets */ UI_region_handlers_add(handlers); } diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index b3b3eafb6e7..3da283089c5 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -177,6 +177,7 @@ void ED_spacemacros_init(void) ED_operatormacros_gpencil(); /* Register dropboxes (can use macros). */ + ED_dropboxes_ui(); const ListBase *spacetypes = BKE_spacetypes_list(); LISTBASE_FOREACH (const SpaceType *, type, spacetypes) { if (type->dropboxes) { From 4389067929d9a57923b7a85ec29b8ca9633fef29 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 30 Sep 2021 16:33:25 +0200 Subject: [PATCH 0396/1500] Fix possible use-after-free in drag-drop handling logic Would happen when there were multiple drag items in parallel. There was a listbase constructed with twice the same item, even though that item would be deleted after it was handled the first time. --- source/blender/windowmanager/intern/wm_event_system.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 14fcc1d69cc..537d5264ba9 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3025,7 +3025,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers /* Other drop custom types allowed. */ if (event->custom == EVT_DATA_DRAGDROP) { ListBase *lb = (ListBase *)event->customdata; - LISTBASE_FOREACH (wmDrag *, drag, lb) { + LISTBASE_FOREACH_MUTABLE (wmDrag *, drag, lb) { if (drop->poll(C, drag, event)) { /* Optionally copy drag information to operator properties. Don't call it if the * operator fails anyway, it might do more than just set properties (e.g. @@ -3036,7 +3036,8 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers /* Pass single matched wmDrag onto the operator. */ BLI_remlink(lb, drag); - ListBase single_lb = {drag, drag}; + ListBase single_lb = {0}; + BLI_addtail(&single_lb, drag); event->customdata = &single_lb; int op_retval = wm_operator_call_internal( From dd3391dd996e90fba3227c1cc2b50f4ef490ccdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 30 Sep 2021 17:30:30 +0200 Subject: [PATCH 0397/1500] Asset Catalogs: create missing parent catalogs For every known catalog, ensure its parent catalog also exists. This ensures that assets can be assigned to parent catalogs, even when they didn't exist in the Catalog Definition File yet. --- .../blender/blenkernel/BKE_asset_catalog.hh | 5 +++ .../blenkernel/BKE_asset_catalog_path.hh | 5 +++ .../blenkernel/intern/asset_catalog.cc | 41 +++++++++++++++++- .../blenkernel/intern/asset_catalog_path.cc | 12 ++++++ .../intern/asset_catalog_path_test.cc | 17 ++++++++ .../blenkernel/intern/asset_catalog_test.cc | 43 +++++++++++++++++++ 6 files changed, 122 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 9d179011b25..a0bc1267826 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -150,6 +150,11 @@ class AssetCatalogService { std::unique_ptr read_into_tree(); void rebuild_tree(); + + /** + * For every catalog, ensure that its parent path also has a known catalog. + */ + void create_missing_catalogs(); }; /** diff --git a/source/blender/blenkernel/BKE_asset_catalog_path.hh b/source/blender/blenkernel/BKE_asset_catalog_path.hh index 1e53df553a9..b150f805ed5 100644 --- a/source/blender/blenkernel/BKE_asset_catalog_path.hh +++ b/source/blender/blenkernel/BKE_asset_catalog_path.hh @@ -111,6 +111,11 @@ class AssetCatalogPath { */ bool is_contained_in(const AssetCatalogPath &other_path) const; + /** + * \return the parent path, or an empty path if there is no parent. + */ + AssetCatalogPath parent() const; + /** * Change the initial part of the path from `from_path` to `to_path`. * If this path does not start with `from_path`, return an empty path as result. diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index bb213877e05..a948c0f96ff 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -188,7 +188,7 @@ void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_director /* TODO: Should there be a sanitize step? E.g. to remove catalogs with identical paths? */ - catalog_tree_ = read_into_tree(); + rebuild_tree(); } void AssetCatalogService::load_directory_recursive(const CatalogFilePath &directory_path) @@ -356,9 +356,48 @@ std::unique_ptr AssetCatalogService::read_into_tree() void AssetCatalogService::rebuild_tree() { + create_missing_catalogs(); this->catalog_tree_ = read_into_tree(); } +void AssetCatalogService::create_missing_catalogs() +{ + /* Construct an ordered set of paths to check, so that parents are ordered before children. */ + std::set paths_to_check; + for (auto &catalog : catalogs_.values()) { + paths_to_check.insert(catalog->path); + } + + std::set seen_paths; + /* The empty parent should never be created, so always be considered "seen". */ + seen_paths.insert(AssetCatalogPath("")); + + /* Find and create missing direct parents (so ignoring parents-of-parents). */ + while (!paths_to_check.empty()) { + /* Pop the first path of the queue. */ + const AssetCatalogPath path = *paths_to_check.begin(); + paths_to_check.erase(paths_to_check.begin()); + + if (seen_paths.find(path) != seen_paths.end()) { + /* This path has been seen already, so it can be ignored. */ + continue; + } + seen_paths.insert(path); + + const AssetCatalogPath parent_path = path.parent(); + if (seen_paths.find(parent_path) != seen_paths.end()) { + /* The parent exists, continue to the next path. */ + continue; + } + + /* The parent doesn't exist, so create it and queue it up for checking its parent. */ + create_catalog(parent_path); + paths_to_check.insert(parent_path); + } + + /* TODO(Sybren): bind the newly created catalogs to a CDF, if we know about it. */ +} + /* ---------------------------------------------------------------------- */ AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name, diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc index d8af7be4a02..cc6aceef8e7 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path.cc @@ -172,6 +172,18 @@ bool AssetCatalogPath::is_contained_in(const AssetCatalogPath &other_path) const return prefix_ok && next_char == SEPARATOR; } +AssetCatalogPath AssetCatalogPath::parent() const +{ + if (!*this) { + return AssetCatalogPath(""); + } + std::string::size_type last_sep_index = this->path_.rfind(SEPARATOR); + if (last_sep_index == std::string::npos) { + return AssetCatalogPath(""); + } + return AssetCatalogPath(this->path_.substr(0, last_sep_index)); +} + void AssetCatalogPath::iterate_components(ComponentIteratorFn callback) const { const char *next_slash_ptr; diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc index 55919abbb8f..af15cbf405a 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc @@ -231,4 +231,21 @@ TEST(AssetCatalogPathTest, rebase) EXPECT_EQ(empty.rebase("", ""), ""); } +TEST(AssetCatalogPathTest, parent) +{ + const AssetCatalogPath ascii_path("path/with/missing/parents"); + EXPECT_EQ(ascii_path.parent(), "path/with/missing"); + + const AssetCatalogPath path("путь/в/Пермь/долог/и/далек"); + EXPECT_EQ(path.parent(), "путь/в/Пермь/долог/и"); + EXPECT_EQ(path.parent().parent(), "путь/в/Пермь/долог"); + EXPECT_EQ(path.parent().parent().parent(), "путь/в/Пермь"); + + const AssetCatalogPath one_level("one"); + EXPECT_EQ(one_level.parent(), ""); + + const AssetCatalogPath empty(""); + EXPECT_EQ(empty.parent(), ""); +} + } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index d7c3e50cbdf..836b681c950 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -57,6 +57,11 @@ class TestableAssetCatalogService : public AssetCatalogService { { return catalog_definition_file_.get(); } + + void create_missing_catalogs() + { + AssetCatalogService::create_missing_catalogs(); + } }; class AssetCatalogTest : public testing::Test { @@ -846,4 +851,42 @@ TEST_F(AssetCatalogTest, order_by_path) } } +TEST_F(AssetCatalogTest, create_missing_catalogs) +{ + TestableAssetCatalogService new_service; + new_service.create_catalog("path/with/missing/parents"); + + EXPECT_EQ(nullptr, new_service.find_catalog_by_path("path/with/missing")) + << "Missing parents should not be immediately created."; + EXPECT_EQ(nullptr, new_service.find_catalog_by_path("")) << "Empty path should never be valid"; + + new_service.create_missing_catalogs(); + + EXPECT_NE(nullptr, new_service.find_catalog_by_path("path/with/missing")); + EXPECT_NE(nullptr, new_service.find_catalog_by_path("path/with")); + EXPECT_NE(nullptr, new_service.find_catalog_by_path("path")); + EXPECT_EQ(nullptr, new_service.find_catalog_by_path("")) + << "Empty path should never be valid, even when after missing catalogs"; +} + +TEST_F(AssetCatalogTest, create_missing_catalogs_after_loading) +{ + TestableAssetCatalogService loaded_service(asset_library_root_); + loaded_service.load_from_disk(); + + const AssetCatalog *cat_char = loaded_service.find_catalog_by_path("character"); + const AssetCatalog *cat_ellie = loaded_service.find_catalog_by_path("character/Ellie"); + const AssetCatalog *cat_ruzena = loaded_service.find_catalog_by_path("character/Ružena"); + ASSERT_NE(nullptr, cat_char) << "Missing parents should be created immediately after loading."; + ASSERT_NE(nullptr, cat_ellie) << "Missing parents should be created immediately after loading."; + ASSERT_NE(nullptr, cat_ruzena) << "Missing parents should be created immediately after loading."; + + AssetCatalogDefinitionFile *cdf = loaded_service.get_catalog_definition_file(); + ASSERT_NE(nullptr, cdf); + EXPECT_TRUE(cdf->contains(cat_char->catalog_id)) << "Missing parents should be saved to a CDF."; + EXPECT_TRUE(cdf->contains(cat_ellie->catalog_id)) << "Missing parents should be saved to a CDF."; + EXPECT_TRUE(cdf->contains(cat_ruzena->catalog_id)) + << "Missing parents should be saved to a CDF."; +} + } // namespace blender::bke::tests From 6cff1d648030f65bc299b1abd480c5a36504863d Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 30 Sep 2021 02:30:47 -0300 Subject: [PATCH 0398/1500] Fix T91734: Crash snapping mesh if a beveled curve is present `BKE_mesh_boundbox_get` cannot be called for objects of type Curve. The BoundBox however does not match the object seen in the scene. This will be dealt with in another commit. --- source/blender/editors/transform/transform_snap_object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 811f30c96e5..883a89b8467 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -724,7 +724,7 @@ static bool raycastMesh(SnapObjectContext *sctx, } /* Test BoundBox */ - BoundBox *bb = BKE_mesh_boundbox_get(ob_eval); + BoundBox *bb = BKE_object_boundbox_get(ob_eval); if (bb) { /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ if (!isect_ray_aabb_v3_simple( From 827e30bd1512f6917307195ccc934b57623f0f8b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 30 Sep 2021 11:53:48 -0500 Subject: [PATCH 0399/1500] Geometry Nodes: Change default for mesh to points node While "Vertices" may be less useful since mesh vertices are already points, the output is more easily understandable, so it's a better default. --- source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 2f59a3c968b..6863f685eae 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -50,7 +50,7 @@ static void geo_node_mesh_to_points_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryMeshToPoints *data = (NodeGeometryMeshToPoints *)MEM_callocN( sizeof(NodeGeometryMeshToPoints), __func__); - data->mode = GEO_NODE_MESH_TO_POINTS_FACES; + data->mode = GEO_NODE_MESH_TO_POINTS_VERTICES; node->storage = data; } From be70827e6f62763d524f00652f61da6fc86e2714 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Thu, 30 Sep 2021 19:05:08 +0100 Subject: [PATCH 0400/1500] Nodes: Add Float Curve for GN and Shader nodes. Replacement for float curve in legacy Attribute Curve Map node. Float Curve defaults to [0.0-1.0] range. Reviewed By: JacquesLucke, brecht Differential Revision: https://developer.blender.org/D12683 --- intern/cycles/blender/blender_shader.cpp | 17 ++- intern/cycles/blender/blender_util.h | 28 +++- intern/cycles/kernel/shaders/CMakeLists.txt | 1 + .../kernel/shaders/node_float_curve.osl | 32 ++++ intern/cycles/kernel/svm/svm.h | 4 +- intern/cycles/kernel/svm/svm_ramp.h | 66 +++++++++ intern/cycles/kernel/svm/svm_types.h | 1 + intern/cycles/render/nodes.cpp | 79 +++++++++- intern/cycles/render/nodes.h | 12 ++ release/scripts/startup/nodeitems_builtins.py | 2 + source/blender/blenkernel/BKE_colortools.h | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/colortools.c | 14 ++ source/blender/blenkernel/intern/node.cc | 4 +- source/blender/editors/space_node/drawnode.cc | 8 + source/blender/gpu/CMakeLists.txt | 1 + .../blender/gpu/intern/gpu_material_library.c | 7 + .../gpu_shader_material_float_curve.glsl | 33 +++++ source/blender/makesrna/RNA_access.h | 1 + source/blender/makesrna/intern/rna_nodetree.c | 11 ++ source/blender/nodes/NOD_shader.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/shader/nodes/node_shader_curves.cc | 139 ++++++++++++++++++ 23 files changed, 456 insertions(+), 8 deletions(-) create mode 100644 intern/cycles/kernel/shaders/node_float_curve.osl create mode 100644 source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 8c4f789ffd0..0b8aea15d6c 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -279,7 +279,7 @@ static ShaderNode *add_node(Scene *scene, array curve_mapping_curves; float min_x, max_x; curvemapping_color_to_array(mapping, curve_mapping_curves, RAMP_TABLE_SIZE, true); - curvemapping_minmax(mapping, true, &min_x, &max_x); + curvemapping_minmax(mapping, 4, &min_x, &max_x); curves->set_min_x(min_x); curves->set_max_x(max_x); curves->set_curves(curve_mapping_curves); @@ -292,12 +292,25 @@ static ShaderNode *add_node(Scene *scene, array curve_mapping_curves; float min_x, max_x; curvemapping_color_to_array(mapping, curve_mapping_curves, RAMP_TABLE_SIZE, false); - curvemapping_minmax(mapping, false, &min_x, &max_x); + curvemapping_minmax(mapping, 3, &min_x, &max_x); curves->set_min_x(min_x); curves->set_max_x(max_x); curves->set_curves(curve_mapping_curves); node = curves; } + else if (b_node.is_a(&RNA_ShaderNodeFloatCurve)) { + BL::ShaderNodeFloatCurve b_curve_node(b_node); + BL::CurveMapping mapping(b_curve_node.mapping()); + FloatCurveNode *curve = graph->create_node(); + array curve_mapping_curve; + float min_x, max_x; + curvemapping_float_to_array(mapping, curve_mapping_curve, RAMP_TABLE_SIZE); + curvemapping_minmax(mapping, 1, &min_x, &max_x); + curve->set_min_x(min_x); + curve->set_max_x(max_x); + curve->set_curve(curve_mapping_curve); + node = curve; + } else if (b_node.is_a(&RNA_ShaderNodeValToRGB)) { RGBRampNode *ramp = graph->create_node(); BL::ShaderNodeValToRGB b_ramp_node(b_node); diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 128fcbd7055..77b2bd5ac4f 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -171,12 +171,11 @@ static inline void curvemap_minmax_curve(/*const*/ BL::CurveMap &curve, float *m } static inline void curvemapping_minmax(/*const*/ BL::CurveMapping &cumap, - bool rgb_curve, + int num_curves, float *min_x, float *max_x) { // const int num_curves = cumap.curves.length(); /* Gives linking error so far. */ - const int num_curves = rgb_curve ? 4 : 3; *min_x = FLT_MAX; *max_x = -FLT_MAX; for (int i = 0; i < num_curves; ++i) { @@ -196,6 +195,28 @@ static inline void curvemapping_to_array(BL::CurveMapping &cumap, array & } } +static inline void curvemapping_float_to_array(BL::CurveMapping &cumap, + array &data, + int size) +{ + float min = 0.0f, max = 1.0f; + + curvemapping_minmax(cumap, 1, &min, &max); + + const float range = max - min; + + cumap.update(); + + BL::CurveMap map = cumap.curves[0]; + + data.resize(size); + + for (int i = 0; i < size; i++) { + float t = min + (float)i / (float)(size - 1) * range; + data[i] = cumap.evaluate(map, t); + } +} + static inline void curvemapping_color_to_array(BL::CurveMapping &cumap, array &data, int size, @@ -214,7 +235,8 @@ static inline void curvemapping_color_to_array(BL::CurveMapping &cumap, * * There might be some better estimations here tho. */ - curvemapping_minmax(cumap, rgb_curve, &min_x, &max_x); + const int num_curves = rgb_curve ? 4 : 3; + curvemapping_minmax(cumap, num_curves, &min_x, &max_x); const float range_x = max_x - min_x; diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/shaders/CMakeLists.txt index 02be7813369..6b62e7bb52f 100644 --- a/intern/cycles/kernel/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/shaders/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC_OSL node_vector_displacement.osl node_emission.osl node_environment_texture.osl + node_float_curve.osl node_fresnel.osl node_gamma.osl node_geometry.osl diff --git a/intern/cycles/kernel/shaders/node_float_curve.osl b/intern/cycles/kernel/shaders/node_float_curve.osl new file mode 100644 index 00000000000..f1f05fd88a9 --- /dev/null +++ b/intern/cycles/kernel/shaders/node_float_curve.osl @@ -0,0 +1,32 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "node_ramp_util.h" +#include "stdcycles.h" + +shader node_float_curve(float ramp[] = {0.0}, + float min_x = 0.0, + float max_x = 1.0, + float ValueIn = 0.0, + float Factor = 0.0, + output float ValueOut = 0.0) +{ + float c = (ValueIn - min_x) / (max_x - min_x); + + ValueOut = rgb_ramp_lookup(ramp, c, 1, 1); + + ValueOut = mix(ValueIn, ValueOut, Factor); +} diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 4aee1ef11b3..ad609b15f86 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -493,11 +493,13 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, case NODE_IES: svm_node_ies(kg, sd, stack, node); break; - case NODE_RGB_CURVES: case NODE_VECTOR_CURVES: offset = svm_node_curves(kg, sd, stack, node, offset); break; + case NODE_FLOAT_CURVE: + offset = svm_node_curve(kg, sd, stack, node, offset); + break; case NODE_TANGENT: svm_node_tangent(kg, sd, stack, node); break; diff --git a/intern/cycles/kernel/svm/svm_ramp.h b/intern/cycles/kernel/svm/svm_ramp.h index e92df3c093c..563e5bcb5e4 100644 --- a/intern/cycles/kernel/svm/svm_ramp.h +++ b/intern/cycles/kernel/svm/svm_ramp.h @@ -21,6 +21,48 @@ CCL_NAMESPACE_BEGIN /* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */ +ccl_device_inline float fetch_float(const KernelGlobals *kg, int offset) +{ + uint4 node = kernel_tex_fetch(__svm_nodes, offset); + return __uint_as_float(node.x); +} + +ccl_device_inline float float_ramp_lookup(const KernelGlobals *kg, + int offset, + float f, + bool interpolate, + bool extrapolate, + int table_size) +{ + if ((f < 0.0f || f > 1.0f) && extrapolate) { + float t0, dy; + if (f < 0.0f) { + t0 = fetch_float(kg, offset); + dy = t0 - fetch_float(kg, offset + 1); + f = -f; + } + else { + t0 = fetch_float(kg, offset + table_size - 1); + dy = t0 - fetch_float(kg, offset + table_size - 2); + f = f - 1.0f; + } + return t0 + dy * f * (table_size - 1); + } + + f = saturate(f) * (table_size - 1); + + /* clamp int as well in case of NaN */ + int i = clamp(float_to_int(f), 0, table_size - 1); + float t = f - (float)i; + + float a = fetch_float(kg, offset + i); + + if (interpolate && t > 0.0f) + a = (1.0f - t) * a + t * fetch_float(kg, offset + i + 1); + + return a; +} + ccl_device_inline float4 rgb_ramp_lookup(const KernelGlobals *kg, int offset, float f, @@ -105,6 +147,30 @@ ccl_device_noinline int svm_node_curves( return offset; } +ccl_device_noinline int svm_node_curve( + const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +{ + uint fac_offset, value_in_offset, out_offset; + svm_unpack_node_uchar3(node.y, &fac_offset, &value_in_offset, &out_offset); + + uint table_size = read_node(kg, &offset).x; + + float fac = stack_load_float(stack, fac_offset); + float in = stack_load_float(stack, value_in_offset); + + const float min = __int_as_float(node.z), max = __int_as_float(node.w); + const float range = max - min; + const float relpos = (in - min) / range; + + float v = float_ramp_lookup(kg, offset, relpos, true, true, table_size); + + in = (1.0f - fac) * in + fac * v; + stack_store_float(stack, out_offset, in); + + offset += table_size; + return offset; +} + CCL_NAMESPACE_END #endif /* __SVM_RAMP_H__ */ diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 313bc3235b9..59a0e33acbc 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -122,6 +122,7 @@ typedef enum ShaderNodeType { NODE_AOV_START, NODE_AOV_COLOR, NODE_AOV_VALUE, + NODE_FLOAT_CURVE, /* NOTE: for best OpenCL performance, item definition in the enum must * match the switch case order in svm.h. */ } ShaderNodeType; diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 90f70cf19ec..1629895ff6e 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -6382,7 +6382,7 @@ void BumpNode::constant_fold(const ConstantFolder &folder) /* TODO(sergey): Ignore bump with zero strength. */ } -/* Curve node */ +/* Curves node */ CurvesNode::CurvesNode(const NodeType *node_type) : ShaderNode(node_type) { @@ -6531,6 +6531,83 @@ void VectorCurvesNode::compile(OSLCompiler &compiler) CurvesNode::compile(compiler, "node_vector_curves"); } +/* FloatCurveNode */ + +NODE_DEFINE(FloatCurveNode) +{ + NodeType *type = NodeType::add("float_curve", create, NodeType::SHADER); + + SOCKET_FLOAT_ARRAY(curve, "Curve", array()); + SOCKET_FLOAT(min_x, "Min X", 0.0f); + SOCKET_FLOAT(max_x, "Max X", 1.0f); + + SOCKET_IN_FLOAT(fac, "Factor", 0.0f); + SOCKET_IN_FLOAT(value, "Value", 0.0f); + + SOCKET_OUT_FLOAT(value, "Value"); + + return type; +} + +FloatCurveNode::FloatCurveNode() : ShaderNode(get_node_type()) +{ +} + +void FloatCurveNode::constant_fold(const ConstantFolder &folder) +{ + ShaderInput *value_in = input("Value"); + ShaderInput *fac_in = input("Factor"); + + /* evaluate fully constant node */ + if (folder.all_inputs_constant()) { + if (curve.size() == 0) { + return; + } + + float pos = (value - min_x) / (max_x - min_x); + float result = float_ramp_lookup(curve.data(), pos, true, true, curve.size()); + + folder.make_constant(value + fac * (result - value)); + } + /* remove no-op node */ + else if (!fac_in->link && fac == 0.0f) { + /* link is not null because otherwise all inputs are constant */ + folder.bypass(value_in->link); + } +} + +void FloatCurveNode::compile(SVMCompiler &compiler) +{ + if (curve.size() == 0) + return; + + ShaderInput *value_in = input("Value"); + ShaderInput *fac_in = input("Factor"); + ShaderOutput *value_out = output("Value"); + + compiler.add_node(NODE_FLOAT_CURVE, + compiler.encode_uchar4(compiler.stack_assign(fac_in), + compiler.stack_assign(value_in), + compiler.stack_assign(value_out)), + __float_as_int(min_x), + __float_as_int(max_x)); + + compiler.add_node(curve.size()); + for (int i = 0; i < curve.size(); i++) + compiler.add_node(make_float4(curve[i])); +} + +void FloatCurveNode::compile(OSLCompiler &compiler) +{ + if (curve.size() == 0) + return; + + compiler.parameter_array("ramp", curve.data(), curve.size()); + compiler.parameter(this, "min_x"); + compiler.parameter(this, "max_x"); + compiler.add(this, "node_float_curve"); +} + /* RGBRampNode */ NODE_DEFINE(RGBRampNode) diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 22bdb06b059..5ac72835ac5 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -1398,6 +1398,18 @@ class VectorCurvesNode : public CurvesNode { void constant_fold(const ConstantFolder &folder); }; +class FloatCurveNode : public ShaderNode { + public: + SHADER_NODE_CLASS(FloatCurveNode) + void constant_fold(const ConstantFolder &folder); + + NODE_SOCKET_API_ARRAY(array, curve) + NODE_SOCKET_API(float, min_x) + NODE_SOCKET_API(float, max_x) + NODE_SOCKET_API(float, fac) + NODE_SOCKET_API(float, value) +}; + class RGBRampNode : public ShaderNode { public: SHADER_NODE_CLASS(RGBRampNode) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 06992022edb..5e6f9b9a76a 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -279,6 +279,7 @@ shader_node_categories = [ ]), ShaderNodeCategory("SH_NEW_CONVERTOR", "Converter", items=[ NodeItem("ShaderNodeMapRange"), + NodeItem("ShaderNodeFloatCurve"), NodeItem("ShaderNodeClamp"), NodeItem("ShaderNodeMath"), NodeItem("ShaderNodeValToRGB"), @@ -615,6 +616,7 @@ geometry_node_categories = [ ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), + NodeItem("ShaderNodeFloatCurve"), NodeItem("ShaderNodeClamp"), NodeItem("ShaderNodeMath"), NodeItem("FunctionNodeBooleanMath"), diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index ec2262d4f60..109947cece4 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -97,6 +97,7 @@ void BKE_curvemapping_evaluate_premulRGBF(const struct CurveMapping *cumap, float vecout[3], const float vecin[3]); bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap); +void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size); void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size); /* non-const, these modify the curve */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 76a3e75d285..a4fc4800fd2 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1102,6 +1102,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, #define SH_NODE_VERTEX_COLOR 706 #define SH_NODE_OUTPUT_AOV 707 #define SH_NODE_VECTOR_ROTATE 708 +#define SH_NODE_CURVE_FLOAT 709 /* custom defines options for Material node */ // #define SH_NODE_MAT_DIFF 1 diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index f2c2e552a9f..62b817487fc 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -1212,6 +1212,20 @@ void BKE_curvemapping_init(CurveMapping *cumap) } } +void BKE_curvemapping_table_F(const CurveMapping *cumap, float **array, int *size) +{ + int a; + + *size = CM_TABLE + 1; + *array = MEM_callocN(sizeof(float) * (*size) * 4, "CurveMapping"); + + for (a = 0; a < *size; a++) { + if (cumap->cm[0].table) { + (*array)[a * 4 + 0] = cumap->cm[0].table[a].y; + } + } +} + void BKE_curvemapping_table_RGBA(const CurveMapping *cumap, float **array, int *size) { int a; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index a3a82bee8dc..66f917ffd96 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -538,7 +538,7 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) if (node->storage) { /* could be handlerized at some point, now only 1 exception still */ if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) && - ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) { + ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB, SH_NODE_CURVE_FLOAT)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } else if ((ntree->type == NTREE_GEOMETRY) && @@ -714,6 +714,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) switch (node->type) { case SH_NODE_CURVE_VEC: case SH_NODE_CURVE_RGB: + case SH_NODE_CURVE_FLOAT: case CMP_NODE_TIME: case CMP_NODE_CURVE_VEC: case CMP_NODE_CURVE_RGB: @@ -5574,6 +5575,7 @@ static void registerShaderNodes() register_node_type_sh_shadertorgb(); register_node_type_sh_normal(); register_node_type_sh_mapping(); + register_node_type_sh_curve_float(); register_node_type_sh_curve_vec(); register_node_type_sh_curve_rgb(); register_node_type_sh_map_range(); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index b4a9b90434c..8d6d56fc383 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -164,6 +164,11 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false); } +static void node_buts_curvefloat(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiTemplateCurveMapping(layout, ptr, "mapping", 0, false, false, false, false); +} + #define SAMPLE_FLT_ISNONE FLT_MAX /* Bad bad, 2.5 will do better? ... no it won't! */ static float _sample_col[4] = {SAMPLE_FLT_ISNONE}; @@ -1183,6 +1188,9 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_CURVE_RGB: ntype->draw_buttons = node_buts_curvecol; break; + case SH_NODE_CURVE_FLOAT: + ntype->draw_buttons = node_buts_curvefloat; + break; case SH_NODE_MAPPING: ntype->draw_buttons = node_shader_buts_mapping; break; diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index df370c7079b..7a072900473 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -296,6 +296,7 @@ data_to_c_simple(shaders/material/gpu_shader_material_diffuse.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_displacement.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_eevee_specular.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_emission.glsl SRC) +data_to_c_simple(shaders/material/gpu_shader_material_float_curve.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_fractal_noise.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_fresnel.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_gamma.glsl SRC) diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c index 73a80c62bdc..74e0270c42a 100644 --- a/source/blender/gpu/intern/gpu_material_library.c +++ b/source/blender/gpu/intern/gpu_material_library.c @@ -62,6 +62,7 @@ extern char datatoc_gpu_shader_material_diffuse_glsl[]; extern char datatoc_gpu_shader_material_displacement_glsl[]; extern char datatoc_gpu_shader_material_eevee_specular_glsl[]; extern char datatoc_gpu_shader_material_emission_glsl[]; +extern char datatoc_gpu_shader_material_float_curve_glsl[]; extern char datatoc_gpu_shader_material_fractal_noise_glsl[]; extern char datatoc_gpu_shader_material_fresnel_glsl[]; extern char datatoc_gpu_shader_material_gamma_glsl[]; @@ -262,6 +263,11 @@ static GPUMaterialLibrary gpu_shader_material_emission_library = { .dependencies = {NULL}, }; +static GPUMaterialLibrary gpu_shader_material_float_curve_library = { + .code = datatoc_gpu_shader_material_float_curve_glsl, + .dependencies = {NULL}, +}; + static GPUMaterialLibrary gpu_shader_material_fresnel_library = { .code = datatoc_gpu_shader_material_fresnel_glsl, .dependencies = {NULL}, @@ -591,6 +597,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = { &gpu_shader_material_color_util_library, &gpu_shader_material_hash_library, &gpu_shader_material_noise_library, + &gpu_shader_material_float_curve_library, &gpu_shader_material_fractal_noise_library, &gpu_shader_material_add_shader_library, &gpu_shader_material_ambient_occlusion_library, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl new file mode 100644 index 00000000000..514409f7fdf --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl @@ -0,0 +1,33 @@ +/* ext is vec4(in_x, in_dy, out_x, out_dy). */ +float curve_float_extrapolate(float x, float y, vec4 ext) +{ + if (x < 0.0) { + return y + x * ext.y; + } + else if (x > 1.0) { + return y + (x - 1.0) * ext.w; + } + else { + return y; + } +} + +#define RANGE_RESCALE(x, min, range) ((x - min) * range) + +void curve_float(float fac, + float vec, + sampler1DArray curvemap, + float layer, + float range, + vec4 ext, + out float outvec) +{ + float xyz_min = ext.x; + vec = RANGE_RESCALE(vec, xyz_min, range); + + outvec = texture(curvemap, vec2(vec, layer)).x; + + outvec = curve_float_extrapolate(vec, outvec, ext); + + outvec = mix(vec, outvec, fac); +} diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index ce53e3390e1..188f933dba5 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -558,6 +558,7 @@ extern StructRNA RNA_ShaderFxWave; extern StructRNA RNA_ShaderNode; extern StructRNA RNA_ShaderNodeCameraData; extern StructRNA RNA_ShaderNodeCombineRGB; +extern StructRNA RNA_ShaderNodeFloatCurve; extern StructRNA RNA_ShaderNodeGamma; extern StructRNA RNA_ShaderNodeHueSaturation; extern StructRNA RNA_ShaderNodeInvert; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 8fb55d37375..894a8bad6da 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4900,6 +4900,17 @@ static void def_vector_curve(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_float_curve(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "mapping", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "storage"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Mapping", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_time(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 2911e0fbea6..76c174201e8 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -49,6 +49,7 @@ void register_node_type_sh_normal(void); void register_node_type_sh_gamma(void); void register_node_type_sh_brightcontrast(void); void register_node_type_sh_mapping(void); +void register_node_type_sh_curve_float(void); void register_node_type_sh_curve_vec(void); void register_node_type_sh_curve_rgb(void); void register_node_type_sh_map_range(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 3ee3faf6122..7972609aa27 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -132,6 +132,7 @@ DefNode(ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement," DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX_IES", TexIES, "IES Texture", "" ) DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "" ) +DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "" ) DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" ) DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" ) diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index 887cc84bb76..253bb3cc4b6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -343,3 +343,142 @@ void register_node_type_sh_curve_rgb(void) nodeRegisterType(&ntype); } + +/* **************** CURVE FLOAT ******************** */ + +namespace blender::nodes { + +static void sh_node_curve_float_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Factor").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input("Value").default_value(1.0f); + b.add_output("Value"); +}; + +} // namespace blender::nodes + +static void node_shader_exec_curve_float(void *UNUSED(data), + int UNUSED(thread), + bNode *node, + bNodeExecData *UNUSED(execdata), + bNodeStack **in, + bNodeStack **out) +{ + float value; + float fac; + + nodestack_get_vec(&fac, SOCK_FLOAT, in[0]); + nodestack_get_vec(&value, SOCK_FLOAT, in[1]); + out[0]->vec[0] = BKE_curvemapping_evaluateF((CurveMapping *)node->storage, 0, value); + if (fac != 1.0f) { + out[0]->vec[0] = (1.0f - fac) * value + fac * out[0]->vec[0]; + } +} + +static void node_shader_init_curve_float(bNodeTree *ntree, bNode *node) +{ + node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); +} + +static int gpu_shader_curve_float(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + float *array, layer; + int size; + + CurveMapping *cumap = (CurveMapping *)node->storage; + + BKE_curvemapping_table_F(cumap, &array, &size); + GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); + + float ext_xyz[4]; + float range_x; + + const CurveMap *cm = &cumap->cm[0]; + ext_xyz[0] = cm->mintable; + ext_xyz[2] = cm->maxtable; + range_x = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); + /* Compute extrapolation gradients. */ + if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { + ext_xyz[1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_x)) : 1e8f; + ext_xyz[3] = (cm->ext_out[0] != 0.0f) ? (cm->ext_out[1] / (cm->ext_out[0] * range_x)) : 1e8f; + } + else { + ext_xyz[1] = 0.0f; + ext_xyz[3] = 0.0f; + } + return GPU_stack_link(mat, + node, + "curve_float", + in, + out, + tex, + GPU_constant(&layer), + GPU_uniform(&range_x), + GPU_uniform(ext_xyz)); +} + +class CurveFloatFunction : public blender::fn::MultiFunction { + private: + const CurveMapping &cumap_; + + public: + CurveFloatFunction(const CurveMapping &cumap) : cumap_(cumap) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Curve Float"}; + signature.single_input("Factor"); + signature.single_input("Value"); + signature.single_output("Value"); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray &fac = params.readonly_single_input(0, "Factor"); + const blender::VArray &val_in = params.readonly_single_input(1, "Value"); + blender::MutableSpan val_out = params.uninitialized_single_output(2, "Value"); + + for (int64_t i : mask) { + val_out[i] = BKE_curvemapping_evaluateF(&cumap_, 0, val_in[i]); + if (fac[i] != 1.0f) { + val_out[i] = (1.0f - fac[i]) * val_in[i] + fac[i] * val_out[i]; + } + } + } +}; + +static void sh_node_curve_float_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &bnode = builder.node(); + CurveMapping *cumap = (CurveMapping *)bnode.storage; + BKE_curvemapping_init(cumap); + builder.construct_and_set_matching_fn(*cumap); +} + +void register_node_type_sh_curve_float(void) +{ + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_CURVE_FLOAT, "Float Curve", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::sh_node_curve_float_declare; + node_type_init(&ntype, node_shader_init_curve_float); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_float); + node_type_gpu(&ntype, gpu_shader_curve_float); + ntype.build_multi_function = sh_node_curve_float_build_multi_function; + + nodeRegisterType(&ntype); +} From ac582056e2e70f3b0d91ff69d0307dd357e2e2ed Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 30 Sep 2021 13:41:55 -0500 Subject: [PATCH 0401/1500] Geometry Nodes: Swap order of geometry proximity inputs "Target" is the most important, so it goes at the top. --- source/blender/nodes/geometry/nodes/node_geo_proximity.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index 2b1de5fbf95..7062deff2f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -31,8 +31,8 @@ namespace blender::nodes { static void geo_node_proximity_declare(NodeDeclarationBuilder &b) { - b.add_input("Source Position").implicit_field(); b.add_input("Target"); + b.add_input("Source Position").implicit_field(); b.add_output("Position").dependent_field(); b.add_output("Distance").dependent_field(); } From a754e35198d852ea34e2b82cd2b126538e6f5a3b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 14 Sep 2021 15:37:47 +0200 Subject: [PATCH 0402/1500] Cycles: refactor API for GPU display * Split GPUDisplay into two classes. PathTraceDisplay to implement the Cycles side, and DisplayDriver to implement the host application side. The DisplayDriver is now a fully abstract base class, embedded in the PathTraceDisplay. * Move copy_pixels_to_texture implementation out of the host side into the Cycles side, since it can be implemented in terms of the texture buffer mapping. * Move definition of DeviceGraphicsInteropDestination into display driver header, so that we do not need to expose private device headers in the public API. * Add more detailed comments about how the DisplayDriver should be implemented. The "driver" terminology might not be obvious, but is also used in other renderers. Differential Revision: https://developer.blender.org/D12626 --- intern/cycles/blender/CMakeLists.txt | 4 +- ...display.cpp => blender_display_driver.cpp} | 107 +++++--------- ...gpu_display.h => blender_display_driver.h} | 37 +++-- intern/cycles/blender/blender_session.cpp | 15 +- intern/cycles/blender/blender_session.h | 6 +- .../cycles/device/cuda/graphics_interop.cpp | 15 +- intern/cycles/device/cuda/graphics_interop.h | 2 +- .../cycles/device/device_graphics_interop.h | 19 +-- intern/cycles/device/hip/graphics_interop.cpp | 22 ++- intern/cycles/device/hip/graphics_interop.h | 5 +- intern/cycles/integrator/CMakeLists.txt | 2 + intern/cycles/integrator/path_trace.cpp | 47 ++++--- intern/cycles/integrator/path_trace.h | 15 +- .../path_trace_display.cpp} | 101 ++++++++++---- .../path_trace_display.h} | 100 ++++--------- intern/cycles/integrator/path_trace_work.cpp | 8 +- intern/cycles/integrator/path_trace_work.h | 12 +- .../cycles/integrator/path_trace_work_cpu.cpp | 20 +-- .../cycles/integrator/path_trace_work_cpu.h | 8 +- .../cycles/integrator/path_trace_work_gpu.cpp | 56 ++++---- .../cycles/integrator/path_trace_work_gpu.h | 24 ++-- intern/cycles/integrator/render_scheduler.h | 2 +- intern/cycles/render/CMakeLists.txt | 3 +- intern/cycles/render/display_driver.h | 131 ++++++++++++++++++ intern/cycles/render/session.cpp | 8 +- intern/cycles/render/session.h | 4 +- 26 files changed, 432 insertions(+), 341 deletions(-) rename intern/cycles/blender/{blender_gpu_display.cpp => blender_display_driver.cpp} (85%) rename intern/cycles/blender/{blender_gpu_display.h => blender_display_driver.h} (83%) rename intern/cycles/{render/gpu_display.cpp => integrator/path_trace_display.cpp} (61%) rename intern/cycles/{render/gpu_display.h => integrator/path_trace_display.h} (67%) create mode 100644 intern/cycles/render/display_driver.h diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index 64d226cb9ec..2660eee017b 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -31,9 +31,9 @@ set(INC_SYS set(SRC blender_camera.cpp blender_device.cpp + blender_display_driver.cpp blender_image.cpp blender_geometry.cpp - blender_gpu_display.cpp blender_light.cpp blender_mesh.cpp blender_object.cpp @@ -51,7 +51,7 @@ set(SRC CCL_api.h blender_device.h - blender_gpu_display.h + blender_display_driver.h blender_id_map.h blender_image.h blender_object_cull.h diff --git a/intern/cycles/blender/blender_gpu_display.cpp b/intern/cycles/blender/blender_display_driver.cpp similarity index 85% rename from intern/cycles/blender/blender_gpu_display.cpp rename to intern/cycles/blender/blender_display_driver.cpp index 5a4567deac3..5267f41eef7 100644 --- a/intern/cycles/blender/blender_gpu_display.cpp +++ b/intern/cycles/blender/blender_display_driver.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "blender/blender_gpu_display.h" +#include "blender/blender_display_driver.h" #include "device/device.h" #include "util/util_logging.h" @@ -273,17 +273,17 @@ uint BlenderDisplaySpaceShader::get_shader_program() } /* -------------------------------------------------------------------- - * BlenderGPUDisplay. + * BlenderDisplayDriver. */ -BlenderGPUDisplay::BlenderGPUDisplay(BL::RenderEngine &b_engine, BL::Scene &b_scene) +BlenderDisplayDriver::BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene) : b_engine_(b_engine), display_shader_(BlenderDisplayShader::create(b_engine, b_scene)) { /* Create context while on the main thread. */ gl_context_create(); } -BlenderGPUDisplay::~BlenderGPUDisplay() +BlenderDisplayDriver::~BlenderDisplayDriver() { gl_resources_destroy(); } @@ -292,19 +292,18 @@ BlenderGPUDisplay::~BlenderGPUDisplay() * Update procedure. */ -bool BlenderGPUDisplay::do_update_begin(const GPUDisplayParams ¶ms, +bool BlenderDisplayDriver::update_begin(const Params ¶ms, int texture_width, int texture_height) { - /* Note that it's the responsibility of BlenderGPUDisplay to ensure updating and drawing + /* Note that it's the responsibility of BlenderDisplayDriver to ensure updating and drawing * the texture does not happen at the same time. This is achieved indirectly. * * When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock. * This same lock is also held when do_draw() is called, which together ensure mutual * exclusion. * - * This locking is not performed at the GPU display level, because that would cause lock - * inversion. */ + * This locking is not performed on the Cycles side, because that would cause lock inversion. */ if (!gl_context_enable()) { return false; } @@ -361,7 +360,7 @@ bool BlenderGPUDisplay::do_update_begin(const GPUDisplayParams ¶ms, return true; } -void BlenderGPUDisplay::do_update_end() +void BlenderDisplayDriver::update_end() { gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); @@ -369,54 +368,18 @@ void BlenderGPUDisplay::do_update_end() gl_context_disable(); } -/* -------------------------------------------------------------------- - * Texture update from CPU buffer. - */ - -void BlenderGPUDisplay::do_copy_pixels_to_texture( - const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height) -{ - /* This call copies pixels to a Pixel Buffer Object (PBO) which is much cheaper from CPU time - * point of view than to copy data directly to the OpenGL texture. - * - * The possible downside of this approach is that it might require a higher peak memory when - * doing partial updates of the texture (although, in practice even partial updates might peak - * with a full-frame buffer stored on the CPU if the GPU is currently occupied). */ - - half4 *mapped_rgba_pixels = map_texture_buffer(); - if (!mapped_rgba_pixels) { - return; - } - - if (texture_x == 0 && texture_y == 0 && pixels_width == texture_.width && - pixels_height == texture_.height) { - const size_t size_in_bytes = sizeof(half4) * texture_.width * texture_.height; - memcpy(mapped_rgba_pixels, rgba_pixels, size_in_bytes); - } - else { - const half4 *rgba_row = rgba_pixels; - half4 *mapped_rgba_row = mapped_rgba_pixels + texture_y * texture_.width + texture_x; - for (int y = 0; y < pixels_height; - ++y, rgba_row += pixels_width, mapped_rgba_row += texture_.width) { - memcpy(mapped_rgba_row, rgba_row, sizeof(half4) * pixels_width); - } - } - - unmap_texture_buffer(); -} - /* -------------------------------------------------------------------- * Texture buffer mapping. */ -half4 *BlenderGPUDisplay::do_map_texture_buffer() +half4 *BlenderDisplayDriver::map_texture_buffer() { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); half4 *mapped_rgba_pixels = reinterpret_cast( glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); if (!mapped_rgba_pixels) { - LOG(ERROR) << "Error mapping BlenderGPUDisplay pixel buffer object."; + LOG(ERROR) << "Error mapping BlenderDisplayDriver pixel buffer object."; } if (texture_.need_clear) { @@ -431,7 +394,7 @@ half4 *BlenderGPUDisplay::do_map_texture_buffer() return mapped_rgba_pixels; } -void BlenderGPUDisplay::do_unmap_texture_buffer() +void BlenderDisplayDriver::unmap_texture_buffer() { glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); @@ -442,9 +405,9 @@ void BlenderGPUDisplay::do_unmap_texture_buffer() * Graphics interoperability. */ -DeviceGraphicsInteropDestination BlenderGPUDisplay::do_graphics_interop_get() +BlenderDisplayDriver::GraphicsInterop BlenderDisplayDriver::graphics_interop_get() { - DeviceGraphicsInteropDestination interop_dst; + GraphicsInterop interop_dst; interop_dst.buffer_width = texture_.buffer_width; interop_dst.buffer_height = texture_.buffer_height; @@ -456,12 +419,12 @@ DeviceGraphicsInteropDestination BlenderGPUDisplay::do_graphics_interop_get() return interop_dst; } -void BlenderGPUDisplay::graphics_interop_activate() +void BlenderDisplayDriver::graphics_interop_activate() { gl_context_enable(); } -void BlenderGPUDisplay::graphics_interop_deactivate() +void BlenderDisplayDriver::graphics_interop_deactivate() { gl_context_disable(); } @@ -470,17 +433,17 @@ void BlenderGPUDisplay::graphics_interop_deactivate() * Drawing. */ -void BlenderGPUDisplay::clear() +void BlenderDisplayDriver::clear() { texture_.need_clear = true; } -void BlenderGPUDisplay::set_zoom(float zoom_x, float zoom_y) +void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y) { zoom_ = make_float2(zoom_x, zoom_y); } -void BlenderGPUDisplay::do_draw(const GPUDisplayParams ¶ms) +void BlenderDisplayDriver::draw(const Params ¶ms) { /* See do_update_begin() for why no locking is required here. */ const bool transparent = true; // TODO(sergey): Derive this from Film. @@ -584,7 +547,7 @@ void BlenderGPUDisplay::do_draw(const GPUDisplayParams ¶ms) } } -void BlenderGPUDisplay::gl_context_create() +void BlenderDisplayDriver::gl_context_create() { /* When rendering in viewport there is no render context available via engine. * Check whether own context is to be created here. @@ -613,7 +576,7 @@ void BlenderGPUDisplay::gl_context_create() } } -bool BlenderGPUDisplay::gl_context_enable() +bool BlenderDisplayDriver::gl_context_enable() { if (use_gl_context_) { if (!gl_context_) { @@ -628,7 +591,7 @@ bool BlenderGPUDisplay::gl_context_enable() return true; } -void BlenderGPUDisplay::gl_context_disable() +void BlenderDisplayDriver::gl_context_disable() { if (use_gl_context_) { if (gl_context_) { @@ -641,7 +604,7 @@ void BlenderGPUDisplay::gl_context_disable() RE_engine_render_context_disable(reinterpret_cast(b_engine_.ptr.data)); } -void BlenderGPUDisplay::gl_context_dispose() +void BlenderDisplayDriver::gl_context_dispose() { if (gl_context_) { const bool drw_state = DRW_opengl_context_release(); @@ -653,7 +616,7 @@ void BlenderGPUDisplay::gl_context_dispose() } } -bool BlenderGPUDisplay::gl_draw_resources_ensure() +bool BlenderDisplayDriver::gl_draw_resources_ensure() { if (!texture_.gl_id) { /* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can @@ -680,7 +643,7 @@ bool BlenderGPUDisplay::gl_draw_resources_ensure() return true; } -void BlenderGPUDisplay::gl_resources_destroy() +void BlenderDisplayDriver::gl_resources_destroy() { gl_context_enable(); @@ -703,7 +666,7 @@ void BlenderGPUDisplay::gl_resources_destroy() gl_context_dispose(); } -bool BlenderGPUDisplay::gl_texture_resources_ensure() +bool BlenderDisplayDriver::gl_texture_resources_ensure() { if (texture_.creation_attempted) { return texture_.is_created; @@ -740,7 +703,7 @@ bool BlenderGPUDisplay::gl_texture_resources_ensure() return true; } -void BlenderGPUDisplay::texture_update_if_needed() +void BlenderDisplayDriver::texture_update_if_needed() { if (!texture_.need_update) { return; @@ -754,7 +717,7 @@ void BlenderGPUDisplay::texture_update_if_needed() texture_.need_update = false; } -void BlenderGPUDisplay::vertex_buffer_update(const GPUDisplayParams ¶ms) +void BlenderDisplayDriver::vertex_buffer_update(const Params ¶ms) { /* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be * rendered. */ @@ -767,23 +730,23 @@ void BlenderGPUDisplay::vertex_buffer_update(const GPUDisplayParams ¶ms) vpointer[0] = 0.0f; vpointer[1] = 0.0f; - vpointer[2] = params.offset.x; - vpointer[3] = params.offset.y; + vpointer[2] = params.full_offset.x; + vpointer[3] = params.full_offset.y; vpointer[4] = 1.0f; vpointer[5] = 0.0f; - vpointer[6] = (float)params.size.x + params.offset.x; - vpointer[7] = params.offset.y; + vpointer[6] = (float)params.size.x + params.full_offset.x; + vpointer[7] = params.full_offset.y; vpointer[8] = 1.0f; vpointer[9] = 1.0f; - vpointer[10] = (float)params.size.x + params.offset.x; - vpointer[11] = (float)params.size.y + params.offset.y; + vpointer[10] = (float)params.size.x + params.full_offset.x; + vpointer[11] = (float)params.size.y + params.full_offset.y; vpointer[12] = 0.0f; vpointer[13] = 1.0f; - vpointer[14] = params.offset.x; - vpointer[15] = (float)params.size.y + params.offset.y; + vpointer[14] = params.full_offset.x; + vpointer[15] = (float)params.size.y + params.full_offset.y; glUnmapBuffer(GL_ARRAY_BUFFER); } diff --git a/intern/cycles/blender/blender_gpu_display.h b/intern/cycles/blender/blender_display_driver.h similarity index 83% rename from intern/cycles/blender/blender_gpu_display.h rename to intern/cycles/blender/blender_display_driver.h index 89420567037..558997c6b4f 100644 --- a/intern/cycles/blender/blender_gpu_display.h +++ b/intern/cycles/blender/blender_display_driver.h @@ -22,12 +22,14 @@ #include "RNA_blender_cpp.h" -#include "render/gpu_display.h" +#include "render/display_driver.h" + +#include "util/util_thread.h" #include "util/util_unique_ptr.h" CCL_NAMESPACE_BEGIN -/* Base class of shader used for GPU display rendering. */ +/* Base class of shader used for display driver rendering. */ class BlenderDisplayShader { public: static constexpr const char *position_attribute_name = "pos"; @@ -96,11 +98,11 @@ class BlenderDisplaySpaceShader : public BlenderDisplayShader { uint shader_program_ = 0; }; -/* GPU display implementation which is specific for Blender viewport integration. */ -class BlenderGPUDisplay : public GPUDisplay { +/* Display driver implementation which is specific for Blender viewport integration. */ +class BlenderDisplayDriver : public DisplayDriver { public: - BlenderGPUDisplay(BL::RenderEngine &b_engine, BL::Scene &b_scene); - ~BlenderGPUDisplay(); + BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene); + ~BlenderDisplayDriver(); virtual void graphics_interop_activate() override; virtual void graphics_interop_deactivate() override; @@ -110,22 +112,15 @@ class BlenderGPUDisplay : public GPUDisplay { void set_zoom(float zoom_x, float zoom_y); protected: - virtual bool do_update_begin(const GPUDisplayParams ¶ms, - int texture_width, - int texture_height) override; - virtual void do_update_end() override; + virtual bool update_begin(const Params ¶ms, int texture_width, int texture_height) override; + virtual void update_end() override; - virtual void do_copy_pixels_to_texture(const half4 *rgba_pixels, - int texture_x, - int texture_y, - int pixels_width, - int pixels_height) override; - virtual void do_draw(const GPUDisplayParams ¶ms) override; + virtual half4 *map_texture_buffer() override; + virtual void unmap_texture_buffer() override; - virtual half4 *do_map_texture_buffer() override; - virtual void do_unmap_texture_buffer() override; + virtual GraphicsInterop graphics_interop_get() override; - virtual DeviceGraphicsInteropDestination do_graphics_interop_get() override; + virtual void draw(const Params ¶ms) override; /* Helper function which allocates new GPU context. */ void gl_context_create(); @@ -152,13 +147,13 @@ class BlenderGPUDisplay : public GPUDisplay { * This buffer is used to render texture in the viewport. * * NOTE: The buffer needs to be bound. */ - void vertex_buffer_update(const GPUDisplayParams ¶ms); + void vertex_buffer_update(const Params ¶ms); BL::RenderEngine b_engine_; /* OpenGL context which is used the render engine doesn't have its own. */ void *gl_context_ = nullptr; - /* The when Blender RenderEngine side context is not available and the GPUDisplay is to create + /* The when Blender RenderEngine side context is not available and the DisplayDriver is to create * its own context. */ bool use_gl_context_ = false; /* Mutex used to guard the `gl_context_`. */ diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 88edc7eafe7..1a42456eda0 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -42,7 +42,7 @@ #include "util/util_progress.h" #include "util/util_time.h" -#include "blender/blender_gpu_display.h" +#include "blender/blender_display_driver.h" #include "blender/blender_session.h" #include "blender/blender_sync.h" #include "blender/blender_util.h" @@ -159,9 +159,10 @@ void BlenderSession::create_session() /* Create GPU display. */ if (!b_engine.is_preview() && !headless) { - unique_ptr gpu_display = make_unique(b_engine, b_scene); - gpu_display_ = gpu_display.get(); - session->set_gpu_display(move(gpu_display)); + unique_ptr display_driver = make_unique(b_engine, + b_scene); + display_driver_ = display_driver.get(); + session->set_display_driver(move(display_driver)); } /* Viewport and preview (as in, material preview) does not do tiled rendering, so can inform @@ -446,7 +447,7 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) /* Use final write for preview renders, otherwise render result wouldn't be be updated on Blender * side. */ - /* TODO(sergey): Investigate whether GPUDisplay can be used for the preview as well. */ + /* TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */ if (b_engine.is_preview()) { session->update_render_tile_cb = [&]() { write_render_tile(); }; } @@ -708,7 +709,7 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, session->read_render_tile_cb = [&]() { read_render_tile(); }; session->write_render_tile_cb = [&]() { write_render_tile(); }; - session->set_gpu_display(nullptr); + session->set_display_driver(nullptr); if (!session->progress.get_cancel()) { /* Sync scene. */ @@ -895,7 +896,7 @@ void BlenderSession::draw(BL::SpaceImageEditor &space_image) } BL::Array zoom = space_image.zoom(); - gpu_display_->set_zoom(zoom[0], zoom[1]); + display_driver_->set_zoom(zoom[0], zoom[1]); session->draw(); } diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index 58683ee07a1..1ca8fdf87d0 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -29,7 +29,7 @@ CCL_NAMESPACE_BEGIN -class BlenderGPUDisplay; +class BlenderDisplayDriver; class BlenderSync; class ImageMetaData; class Scene; @@ -164,8 +164,8 @@ class BlenderSession { int last_pass_index = -1; } draw_state_; - /* NOTE: The BlenderSession references the GPU display. */ - BlenderGPUDisplay *gpu_display_ = nullptr; + /* NOTE: The BlenderSession references the display driver. */ + BlenderDisplayDriver *display_driver_ = nullptr; vector full_buffer_files_; }; diff --git a/intern/cycles/device/cuda/graphics_interop.cpp b/intern/cycles/device/cuda/graphics_interop.cpp index e8ca8b90eae..30efefd9b6b 100644 --- a/intern/cycles/device/cuda/graphics_interop.cpp +++ b/intern/cycles/device/cuda/graphics_interop.cpp @@ -37,14 +37,15 @@ CUDADeviceGraphicsInterop::~CUDADeviceGraphicsInterop() } } -void CUDADeviceGraphicsInterop::set_destination( - const DeviceGraphicsInteropDestination &destination) +void CUDADeviceGraphicsInterop::set_display_interop( + const DisplayDriver::GraphicsInterop &display_interop) { - const int64_t new_buffer_area = int64_t(destination.buffer_width) * destination.buffer_height; + const int64_t new_buffer_area = int64_t(display_interop.buffer_width) * + display_interop.buffer_height; - need_clear_ = destination.need_clear; + need_clear_ = display_interop.need_clear; - if (opengl_pbo_id_ == destination.opengl_pbo_id && buffer_area_ == new_buffer_area) { + if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) { return; } @@ -55,12 +56,12 @@ void CUDADeviceGraphicsInterop::set_destination( } const CUresult result = cuGraphicsGLRegisterBuffer( - &cu_graphics_resource_, destination.opengl_pbo_id, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE); + &cu_graphics_resource_, display_interop.opengl_pbo_id, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE); if (result != CUDA_SUCCESS) { LOG(ERROR) << "Error registering OpenGL buffer: " << cuewErrorString(result); } - opengl_pbo_id_ = destination.opengl_pbo_id; + opengl_pbo_id_ = display_interop.opengl_pbo_id; buffer_area_ = new_buffer_area; } diff --git a/intern/cycles/device/cuda/graphics_interop.h b/intern/cycles/device/cuda/graphics_interop.h index 8a70c8aa71d..ec480f20c86 100644 --- a/intern/cycles/device/cuda/graphics_interop.h +++ b/intern/cycles/device/cuda/graphics_interop.h @@ -41,7 +41,7 @@ class CUDADeviceGraphicsInterop : public DeviceGraphicsInterop { CUDADeviceGraphicsInterop &operator=(const CUDADeviceGraphicsInterop &other) = delete; CUDADeviceGraphicsInterop &operator=(CUDADeviceGraphicsInterop &&other) = delete; - virtual void set_destination(const DeviceGraphicsInteropDestination &destination) override; + virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) override; virtual device_ptr map() override; virtual void unmap() override; diff --git a/intern/cycles/device/device_graphics_interop.h b/intern/cycles/device/device_graphics_interop.h index 671b1c189d7..eaf76077141 100644 --- a/intern/cycles/device/device_graphics_interop.h +++ b/intern/cycles/device/device_graphics_interop.h @@ -16,25 +16,12 @@ #pragma once +#include "render/display_driver.h" + #include "util/util_types.h" CCL_NAMESPACE_BEGIN -/* Information about interoperability destination. - * Is provided by the GPUDisplay. */ -class DeviceGraphicsInteropDestination { - public: - /* Dimensions of the buffer, in pixels. */ - int buffer_width = 0; - int buffer_height = 0; - - /* OpenGL pixel buffer object. */ - int opengl_pbo_id = 0; - - /* Clear the entire destination before doing partial write to it. */ - bool need_clear = false; -}; - /* Device-side graphics interoperability support. * * Takes care of holding all the handlers needed by the device to implement interoperability with @@ -46,7 +33,7 @@ class DeviceGraphicsInterop { /* Update this device-side graphics interoperability object with the given destination resource * information. */ - virtual void set_destination(const DeviceGraphicsInteropDestination &destination) = 0; + virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) = 0; virtual device_ptr map() = 0; virtual void unmap() = 0; diff --git a/intern/cycles/device/hip/graphics_interop.cpp b/intern/cycles/device/hip/graphics_interop.cpp index add6dbed5e1..0d5d71019b3 100644 --- a/intern/cycles/device/hip/graphics_interop.cpp +++ b/intern/cycles/device/hip/graphics_interop.cpp @@ -37,11 +37,15 @@ HIPDeviceGraphicsInterop::~HIPDeviceGraphicsInterop() } } -void HIPDeviceGraphicsInterop::set_destination(const DeviceGraphicsInteropDestination &destination) +void HIPDeviceGraphicsInterop::set_display_interop( + const DisplayDriver::GraphicsInterop &display_interop) { - const int64_t new_buffer_area = int64_t(destination.buffer_width) * destination.buffer_height; + const int64_t new_buffer_area = int64_t(display_interop.buffer_width) * + display_interop.buffer_height; - if (opengl_pbo_id_ == destination.opengl_pbo_id && buffer_area_ == new_buffer_area) { + need_clear_ = display_interop.need_clear; + + if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) { return; } @@ -52,12 +56,12 @@ void HIPDeviceGraphicsInterop::set_destination(const DeviceGraphicsInteropDestin } const hipError_t result = hipGraphicsGLRegisterBuffer( - &hip_graphics_resource_, destination.opengl_pbo_id, hipGraphicsRegisterFlagsNone); + &hip_graphics_resource_, display_interop.opengl_pbo_id, hipGraphicsRegisterFlagsNone); if (result != hipSuccess) { LOG(ERROR) << "Error registering OpenGL buffer: " << hipewErrorString(result); } - opengl_pbo_id_ = destination.opengl_pbo_id; + opengl_pbo_id_ = display_interop.opengl_pbo_id; buffer_area_ = new_buffer_area; } @@ -77,6 +81,14 @@ device_ptr HIPDeviceGraphicsInterop::map() hip_device_assert( device_, hipGraphicsResourceGetMappedPointer(&hip_buffer, &bytes, hip_graphics_resource_)); + if (need_clear_) { + hip_device_assert( + device_, + hipMemsetD8Async(static_cast(hip_buffer), 0, bytes, queue_->stream())); + + need_clear_ = false; + } + return static_cast(hip_buffer); } diff --git a/intern/cycles/device/hip/graphics_interop.h b/intern/cycles/device/hip/graphics_interop.h index adcaa13a2d7..2b2d287ff6c 100644 --- a/intern/cycles/device/hip/graphics_interop.h +++ b/intern/cycles/device/hip/graphics_interop.h @@ -39,7 +39,7 @@ class HIPDeviceGraphicsInterop : public DeviceGraphicsInterop { HIPDeviceGraphicsInterop &operator=(const HIPDeviceGraphicsInterop &other) = delete; HIPDeviceGraphicsInterop &operator=(HIPDeviceGraphicsInterop &&other) = delete; - virtual void set_destination(const DeviceGraphicsInteropDestination &destination) override; + virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) override; virtual device_ptr map() override; virtual void unmap() override; @@ -53,6 +53,9 @@ class HIPDeviceGraphicsInterop : public DeviceGraphicsInterop { /* Buffer area in pixels of the corresponding PBO. */ int64_t buffer_area_ = 0; + /* The destination was requested to be cleared. */ + bool need_clear_ = false; + hipGraphicsResource hip_graphics_resource_ = nullptr; }; diff --git a/intern/cycles/integrator/CMakeLists.txt b/intern/cycles/integrator/CMakeLists.txt index bfabd35d7c3..8acd72f0508 100644 --- a/intern/cycles/integrator/CMakeLists.txt +++ b/intern/cycles/integrator/CMakeLists.txt @@ -27,6 +27,7 @@ set(SRC pass_accessor.cpp pass_accessor_cpu.cpp pass_accessor_gpu.cpp + path_trace_display.cpp path_trace_work.cpp path_trace_work_cpu.cpp path_trace_work_gpu.cpp @@ -47,6 +48,7 @@ set(SRC_HEADERS pass_accessor.h pass_accessor_cpu.h pass_accessor_gpu.h + path_trace_display.h path_trace_work.h path_trace_work_cpu.h path_trace_work_gpu.h diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 9633d3b87d3..36cd7314b4c 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -19,8 +19,8 @@ #include "device/cpu/device.h" #include "device/device.h" #include "integrator/pass_accessor.h" +#include "integrator/path_trace_display.h" #include "integrator/render_scheduler.h" -#include "render/gpu_display.h" #include "render/pass.h" #include "render/scene.h" #include "render/tile.h" @@ -67,11 +67,11 @@ PathTrace::PathTrace(Device *device, PathTrace::~PathTrace() { /* Destroy any GPU resource which was used for graphics interop. - * Need to have access to the GPUDisplay as it is the only source of drawing context which is - * used for interop. */ - if (gpu_display_) { + * Need to have access to the PathTraceDisplay as it is the only source of drawing context which + * is used for interop. */ + if (display_) { for (auto &&path_trace_work : path_trace_works_) { - path_trace_work->destroy_gpu_resources(gpu_display_.get()); + path_trace_work->destroy_gpu_resources(display_.get()); } } } @@ -94,7 +94,7 @@ bool PathTrace::ready_to_reset() { /* The logic here is optimized for the best feedback in the viewport, which implies having a GPU * display. Of there is no such display, the logic here will break. */ - DCHECK(gpu_display_); + DCHECK(display_); /* The logic here tries to provide behavior which feels the most interactive feel to artists. * General idea is to be able to reset as quickly as possible, while still providing interactive @@ -126,8 +126,8 @@ void PathTrace::reset(const BufferParams &full_params, const BufferParams &big_t /* NOTE: GPU display checks for buffer modification and avoids unnecessary re-allocation. * It is requires to inform about reset whenever it happens, so that the redraw state tracking is * properly updated. */ - if (gpu_display_) { - gpu_display_->reset(full_params); + if (display_) { + display_->reset(full_params); } render_state_.has_denoised_result = false; @@ -535,25 +535,30 @@ void PathTrace::denoise(const RenderWork &render_work) render_scheduler_.report_denoise_time(render_work, time_dt() - start_time); } -void PathTrace::set_gpu_display(unique_ptr gpu_display) +void PathTrace::set_display_driver(unique_ptr driver) { - gpu_display_ = move(gpu_display); + if (driver) { + display_ = make_unique(move(driver)); + } + else { + display_ = nullptr; + } } -void PathTrace::clear_gpu_display() +void PathTrace::clear_display() { - if (gpu_display_) { - gpu_display_->clear(); + if (display_) { + display_->clear(); } } void PathTrace::draw() { - if (!gpu_display_) { + if (!display_) { return; } - did_draw_after_reset_ |= gpu_display_->draw(); + did_draw_after_reset_ |= display_->draw(); } void PathTrace::update_display(const RenderWork &render_work) @@ -562,13 +567,13 @@ void PathTrace::update_display(const RenderWork &render_work) return; } - if (!gpu_display_ && !tile_buffer_update_cb) { + if (!display_ && !tile_buffer_update_cb) { VLOG(3) << "Ignore display update."; return; } if (full_params_.width == 0 || full_params_.height == 0) { - VLOG(3) << "Skipping GPUDisplay update due to 0 size of the render buffer."; + VLOG(3) << "Skipping PathTraceDisplay update due to 0 size of the render buffer."; return; } @@ -580,13 +585,13 @@ void PathTrace::update_display(const RenderWork &render_work) tile_buffer_update_cb(); } - if (gpu_display_) { + if (display_) { VLOG(3) << "Perform copy to GPUDisplay work."; const int resolution_divider = render_work.resolution_divider; const int texture_width = max(1, full_params_.width / resolution_divider); const int texture_height = max(1, full_params_.height / resolution_divider); - if (!gpu_display_->update_begin(texture_width, texture_height)) { + if (!display_->update_begin(texture_width, texture_height)) { LOG(ERROR) << "Error beginning GPUDisplay update."; return; } @@ -600,10 +605,10 @@ void PathTrace::update_display(const RenderWork &render_work) * all works in parallel. */ const int num_samples = get_num_samples_in_buffer(); for (auto &&path_trace_work : path_trace_works_) { - path_trace_work->copy_to_gpu_display(gpu_display_.get(), pass_mode, num_samples); + path_trace_work->copy_to_display(display_.get(), pass_mode, num_samples); } - gpu_display_->update_end(); + display_->update_end(); } render_scheduler_.report_display_update_time(render_work, time_dt() - start_time); diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h index f507c2d7e0a..46eb0435c91 100644 --- a/intern/cycles/integrator/path_trace.h +++ b/intern/cycles/integrator/path_trace.h @@ -31,12 +31,13 @@ CCL_NAMESPACE_BEGIN class AdaptiveSampling; class Device; class DeviceScene; +class DisplayDriver; class Film; class RenderBuffers; class RenderScheduler; class RenderWork; +class PathTraceDisplay; class Progress; -class GPUDisplay; class TileManager; /* PathTrace class takes care of kernel graph and scheduling on a (multi)device. It takes care of @@ -98,13 +99,13 @@ class PathTrace { * Use this to configure the adaptive sampler before rendering any samples. */ void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling); - /* Set GPU display which takes care of drawing the render result. */ - void set_gpu_display(unique_ptr gpu_display); + /* Set display driver which takes care of drawing the render result. */ + void set_display_driver(unique_ptr driver); - /* Clear the GPU display by filling it in with all zeroes. */ - void clear_gpu_display(); + /* Clear the display buffer by filling it in with all zeroes. */ + void clear_display(); - /* Perform drawing of the current state of the GPUDisplay. */ + /* Perform drawing of the current state of the DisplayDriver. */ void draw(); /* Cancel rendering process as soon as possible, without waiting for full tile to be sampled. @@ -252,7 +253,7 @@ class PathTrace { RenderScheduler &render_scheduler_; TileManager &tile_manager_; - unique_ptr gpu_display_; + unique_ptr display_; /* Per-compute device descriptors of work which is responsible for path tracing on its configured * device. */ diff --git a/intern/cycles/render/gpu_display.cpp b/intern/cycles/integrator/path_trace_display.cpp similarity index 61% rename from intern/cycles/render/gpu_display.cpp rename to intern/cycles/integrator/path_trace_display.cpp index a8f0cc50583..28f0a7f7745 100644 --- a/intern/cycles/render/gpu_display.cpp +++ b/intern/cycles/integrator/path_trace_display.cpp @@ -14,20 +14,25 @@ * limitations under the License. */ -#include "render/gpu_display.h" +#include "integrator/path_trace_display.h" #include "render/buffers.h" + #include "util/util_logging.h" CCL_NAMESPACE_BEGIN -void GPUDisplay::reset(const BufferParams &buffer_params) +PathTraceDisplay::PathTraceDisplay(unique_ptr driver) : driver_(move(driver)) +{ +} + +void PathTraceDisplay::reset(const BufferParams &buffer_params) { thread_scoped_lock lock(mutex_); - const GPUDisplayParams old_params = params_; + const DisplayDriver::Params old_params = params_; - params_.offset = make_int2(buffer_params.full_x, buffer_params.full_y); + params_.full_offset = make_int2(buffer_params.full_x, buffer_params.full_y); params_.full_size = make_int2(buffer_params.full_width, buffer_params.full_height); params_.size = make_int2(buffer_params.width, buffer_params.height); @@ -44,7 +49,7 @@ void GPUDisplay::reset(const BufferParams &buffer_params) texture_state_.is_outdated = true; } -void GPUDisplay::mark_texture_updated() +void PathTraceDisplay::mark_texture_updated() { texture_state_.is_outdated = false; texture_state_.is_usable = true; @@ -54,7 +59,7 @@ void GPUDisplay::mark_texture_updated() * Update procedure. */ -bool GPUDisplay::update_begin(int texture_width, int texture_height) +bool PathTraceDisplay::update_begin(int texture_width, int texture_height) { DCHECK(!update_state_.is_active); @@ -66,15 +71,15 @@ bool GPUDisplay::update_begin(int texture_width, int texture_height) /* Get parameters within a mutex lock, to avoid reset() modifying them at the same time. * The update itself is non-blocking however, for better performance and to avoid * potential deadlocks due to locks held by the subclass. */ - GPUDisplayParams params; + DisplayDriver::Params params; { thread_scoped_lock lock(mutex_); params = params_; texture_state_.size = make_int2(texture_width, texture_height); } - if (!do_update_begin(params, texture_width, texture_height)) { - LOG(ERROR) << "GPUDisplay implementation could not begin update."; + if (!driver_->update_begin(params, texture_width, texture_height)) { + LOG(ERROR) << "PathTraceDisplay implementation could not begin update."; return false; } @@ -83,7 +88,7 @@ bool GPUDisplay::update_begin(int texture_width, int texture_height) return true; } -void GPUDisplay::update_end() +void PathTraceDisplay::update_end() { DCHECK(update_state_.is_active); @@ -92,12 +97,12 @@ void GPUDisplay::update_end() return; } - do_update_end(); + driver_->update_end(); update_state_.is_active = false; } -int2 GPUDisplay::get_texture_size() const +int2 PathTraceDisplay::get_texture_size() const { return texture_state_.size; } @@ -106,25 +111,54 @@ int2 GPUDisplay::get_texture_size() const * Texture update from CPU buffer. */ -void GPUDisplay::copy_pixels_to_texture( +void PathTraceDisplay::copy_pixels_to_texture( const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height) { DCHECK(update_state_.is_active); if (!update_state_.is_active) { - LOG(ERROR) << "Attempt to copy pixels data outside of GPUDisplay update."; + LOG(ERROR) << "Attempt to copy pixels data outside of PathTraceDisplay update."; return; } mark_texture_updated(); - do_copy_pixels_to_texture(rgba_pixels, texture_x, texture_y, pixels_width, pixels_height); + + /* This call copies pixels to a mapped texture buffer which is typically much cheaper from CPU + * time point of view than to copy data directly to a texture. + * + * The possible downside of this approach is that it might require a higher peak memory when + * doing partial updates of the texture (although, in practice even partial updates might peak + * with a full-frame buffer stored on the CPU if the GPU is currently occupied). */ + half4 *mapped_rgba_pixels = map_texture_buffer(); + if (!mapped_rgba_pixels) { + return; + } + + const int texture_width = texture_state_.size.x; + const int texture_height = texture_state_.size.y; + + if (texture_x == 0 && texture_y == 0 && pixels_width == texture_width && + pixels_height == texture_height) { + const size_t size_in_bytes = sizeof(half4) * texture_width * texture_height; + memcpy(mapped_rgba_pixels, rgba_pixels, size_in_bytes); + } + else { + const half4 *rgba_row = rgba_pixels; + half4 *mapped_rgba_row = mapped_rgba_pixels + texture_y * texture_width + texture_x; + for (int y = 0; y < pixels_height; + ++y, rgba_row += pixels_width, mapped_rgba_row += texture_width) { + memcpy(mapped_rgba_row, rgba_row, sizeof(half4) * pixels_width); + } + } + + unmap_texture_buffer(); } /* -------------------------------------------------------------------- * Texture buffer mapping. */ -half4 *GPUDisplay::map_texture_buffer() +half4 *PathTraceDisplay::map_texture_buffer() { DCHECK(!texture_buffer_state_.is_mapped); DCHECK(update_state_.is_active); @@ -135,11 +169,11 @@ half4 *GPUDisplay::map_texture_buffer() } if (!update_state_.is_active) { - LOG(ERROR) << "Attempt to copy pixels data outside of GPUDisplay update."; + LOG(ERROR) << "Attempt to copy pixels data outside of PathTraceDisplay update."; return nullptr; } - half4 *mapped_rgba_pixels = do_map_texture_buffer(); + half4 *mapped_rgba_pixels = driver_->map_texture_buffer(); if (mapped_rgba_pixels) { texture_buffer_state_.is_mapped = true; @@ -148,7 +182,7 @@ half4 *GPUDisplay::map_texture_buffer() return mapped_rgba_pixels; } -void GPUDisplay::unmap_texture_buffer() +void PathTraceDisplay::unmap_texture_buffer() { DCHECK(texture_buffer_state_.is_mapped); @@ -160,14 +194,14 @@ void GPUDisplay::unmap_texture_buffer() texture_buffer_state_.is_mapped = false; mark_texture_updated(); - do_unmap_texture_buffer(); + driver_->unmap_texture_buffer(); } /* -------------------------------------------------------------------- * Graphics interoperability. */ -DeviceGraphicsInteropDestination GPUDisplay::graphics_interop_get() +DisplayDriver::GraphicsInterop PathTraceDisplay::graphics_interop_get() { DCHECK(!texture_buffer_state_.is_mapped); DCHECK(update_state_.is_active); @@ -175,38 +209,45 @@ DeviceGraphicsInteropDestination GPUDisplay::graphics_interop_get() if (texture_buffer_state_.is_mapped) { LOG(ERROR) << "Attempt to use graphics interoperability mode while the texture buffer is mapped."; - return DeviceGraphicsInteropDestination(); + return DisplayDriver::GraphicsInterop(); } if (!update_state_.is_active) { - LOG(ERROR) << "Attempt to use graphics interoperability outside of GPUDisplay update."; - return DeviceGraphicsInteropDestination(); + LOG(ERROR) << "Attempt to use graphics interoperability outside of PathTraceDisplay update."; + return DisplayDriver::GraphicsInterop(); } /* Assume that interop will write new values to the texture. */ mark_texture_updated(); - return do_graphics_interop_get(); + return driver_->graphics_interop_get(); } -void GPUDisplay::graphics_interop_activate() +void PathTraceDisplay::graphics_interop_activate() { + driver_->graphics_interop_activate(); } -void GPUDisplay::graphics_interop_deactivate() +void PathTraceDisplay::graphics_interop_deactivate() { + driver_->graphics_interop_deactivate(); } /* -------------------------------------------------------------------- * Drawing. */ -bool GPUDisplay::draw() +void PathTraceDisplay::clear() +{ + driver_->clear(); +} + +bool PathTraceDisplay::draw() { /* Get parameters within a mutex lock, to avoid reset() modifying them at the same time. * The drawing itself is non-blocking however, for better performance and to avoid * potential deadlocks due to locks held by the subclass. */ - GPUDisplayParams params; + DisplayDriver::Params params; bool is_usable; bool is_outdated; @@ -218,7 +259,7 @@ bool GPUDisplay::draw() } if (is_usable) { - do_draw(params); + driver_->draw(params); } return !is_outdated; diff --git a/intern/cycles/render/gpu_display.h b/intern/cycles/integrator/path_trace_display.h similarity index 67% rename from intern/cycles/render/gpu_display.h rename to intern/cycles/integrator/path_trace_display.h index 3c3cfaea513..24aaa0df6b1 100644 --- a/intern/cycles/render/gpu_display.h +++ b/intern/cycles/integrator/path_trace_display.h @@ -16,52 +16,30 @@ #pragma once -#include "device/device_graphics_interop.h" +#include "render/display_driver.h" + #include "util/util_half.h" #include "util/util_thread.h" #include "util/util_types.h" +#include "util/util_unique_ptr.h" CCL_NAMESPACE_BEGIN class BufferParams; -/* GPUDisplay class takes care of drawing render result in a viewport. The render result is stored - * in a GPU-side texture, which is updated from a path tracer and drawn by an application. +/* PathTraceDisplay is used for efficient render buffer display. * - * The base GPUDisplay does some special texture state tracking, which allows render Session to - * make decisions on whether reset for an updated state is possible or not. This state should only - * be tracked in a base class and a particular implementation should not worry about it. + * The host applications implements a DisplayDriver, storing a render pass in a GPU-side + * textures. This texture is continuously updated by the path tracer and drawn by the host + * application. * - * The subclasses should only implement the pure virtual methods, which allows them to not worry - * about parent method calls, which helps them to be as small and reliable as possible. */ + * PathTraceDisplay is a wrapper around the DisplayDriver, adding thread safety, state tracking + * and error checking. */ -class GPUDisplayParams { +class PathTraceDisplay { public: - /* Offset of the display within a viewport. - * For example, set to a lower-bottom corner of border render in Blender's viewport. */ - int2 offset = make_int2(0, 0); - - /* Full viewport size. - * - * NOTE: Is not affected by the resolution divider. */ - int2 full_size = make_int2(0, 0); - - /* Effective viewport size. - * In the case of border render, size of the border rectangle. - * - * NOTE: Is not affected by the resolution divider. */ - int2 size = make_int2(0, 0); - - bool modified(const GPUDisplayParams &other) const - { - return !(offset == other.offset && full_size == other.full_size && size == other.size); - } -}; - -class GPUDisplay { - public: - GPUDisplay() = default; - virtual ~GPUDisplay() = default; + PathTraceDisplay(unique_ptr driver); + virtual ~PathTraceDisplay() = default; /* Reset the display for the new state of render session. Is called whenever session is reset, * which happens on changes like viewport navigation or viewport dimension change. @@ -69,11 +47,6 @@ class GPUDisplay { * This call will configure parameters for a changed buffer and reset the texture state. */ void reset(const BufferParams &buffer_params); - const GPUDisplayParams &get_params() const - { - return params_; - } - /* -------------------------------------------------------------------- * Update procedure. * @@ -94,7 +67,8 @@ class GPUDisplay { /* -------------------------------------------------------------------- * Texture update from CPU buffer. * - * NOTE: The GPUDisplay should be marked for an update being in process with `update_begin()`. + * NOTE: The PathTraceDisplay should be marked for an update being in process with + * `update_begin()`. * * Most portable implementation, which must be supported by all platforms. Might not be the most * efficient one. @@ -115,7 +89,8 @@ class GPUDisplay { * This functionality is used to update GPU-side texture content without need to maintain CPU * side buffer on the caller. * - * NOTE: The GPUDisplay should be marked for an update being in process with `update_begin()`. + * NOTE: The PathTraceDisplay should be marked for an update being in process with + * `update_begin()`. * * NOTE: Texture buffer can not be mapped while graphics interoperability is active. This means * that `map_texture_buffer()` is not allowed between `graphics_interop_begin()` and @@ -145,14 +120,14 @@ class GPUDisplay { * that `graphics_interop_get()` is not allowed between `map_texture_buffer()` and * `unmap_texture_buffer()` calls. */ - /* Get GPUDisplay graphics interoperability information which acts as a destination for the + /* Get PathTraceDisplay graphics interoperability information which acts as a destination for the * device API. */ - DeviceGraphicsInteropDestination graphics_interop_get(); + DisplayDriver::GraphicsInterop graphics_interop_get(); /* (De)activate GPU display for graphics interoperability outside of regular display update * routines. */ - virtual void graphics_interop_activate(); - virtual void graphics_interop_deactivate(); + void graphics_interop_activate(); + void graphics_interop_deactivate(); /* -------------------------------------------------------------------- * Drawing. @@ -168,42 +143,21 @@ class GPUDisplay { * after clear will write new pixel values for an updating area, leaving everything else zeroed. * * If the GPU display supports graphics interoperability then the zeroing the display is to be - * delegated to the device via the `DeviceGraphicsInteropDestination`. */ - virtual void clear() = 0; + * delegated to the device via the `DisplayDriver::GraphicsInterop`. */ + void clear(); /* Draw the current state of the texture. * * Returns true if this call did draw an updated state of the texture. */ bool draw(); - protected: - /* Implementation-specific calls which subclasses are to implement. - * These `do_foo()` method corresponds to their `foo()` calls, but they are purely virtual to - * simplify their particular implementation. */ - virtual bool do_update_begin(const GPUDisplayParams ¶ms, - int texture_width, - int texture_height) = 0; - virtual void do_update_end() = 0; - - virtual void do_copy_pixels_to_texture(const half4 *rgba_pixels, - int texture_x, - int texture_y, - int pixels_width, - int pixels_height) = 0; - - virtual half4 *do_map_texture_buffer() = 0; - virtual void do_unmap_texture_buffer() = 0; - - /* Note that this might be called in parallel to do_update_begin() and do_update_end(), - * the subclass is responsible for appropriate mutex locks to avoid multiple threads - * editing and drawing the texture at the same time. */ - virtual void do_draw(const GPUDisplayParams ¶ms) = 0; - - virtual DeviceGraphicsInteropDestination do_graphics_interop_get() = 0; - private: + /* Display driver implemented by the host application. */ + unique_ptr driver_; + + /* Current display parameters */ thread_mutex mutex_; - GPUDisplayParams params_; + DisplayDriver::Params params_; /* Mark texture as its content has been updated. * Used from places which knows that the texture content has been brought up-to-date, so that the diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp index d9634acac10..c29177907c9 100644 --- a/intern/cycles/integrator/path_trace_work.cpp +++ b/intern/cycles/integrator/path_trace_work.cpp @@ -16,12 +16,12 @@ #include "device/device.h" +#include "integrator/path_trace_display.h" #include "integrator/path_trace_work.h" #include "integrator/path_trace_work_cpu.h" #include "integrator/path_trace_work_gpu.h" #include "render/buffers.h" #include "render/film.h" -#include "render/gpu_display.h" #include "render/scene.h" #include "kernel/kernel_types.h" @@ -185,12 +185,12 @@ PassAccessor::PassAccessInfo PathTraceWork::get_display_pass_access_info(PassMod return pass_access_info; } -PassAccessor::Destination PathTraceWork::get_gpu_display_destination_template( - const GPUDisplay *gpu_display) const +PassAccessor::Destination PathTraceWork::get_display_destination_template( + const PathTraceDisplay *display) const { PassAccessor::Destination destination(film_->get_display_pass()); - const int2 display_texture_size = gpu_display->get_texture_size(); + const int2 display_texture_size = display->get_texture_size(); const int texture_x = effective_buffer_params_.full_x - effective_full_params_.full_x; const int texture_y = effective_buffer_params_.full_y - effective_full_params_.full_y; diff --git a/intern/cycles/integrator/path_trace_work.h b/intern/cycles/integrator/path_trace_work.h index e1be1655edd..404165b7c55 100644 --- a/intern/cycles/integrator/path_trace_work.h +++ b/intern/cycles/integrator/path_trace_work.h @@ -28,7 +28,7 @@ class BufferParams; class Device; class DeviceScene; class Film; -class GPUDisplay; +class PathTraceDisplay; class RenderBuffers; class PathTraceWork { @@ -83,11 +83,9 @@ class PathTraceWork { * noisy pass mode will be passed here when it is known that the buffer does not have denoised * passes yet (because denoiser did not run). If the denoised pass is requested and denoiser is * not used then this function will fall-back to the noisy pass instead. */ - virtual void copy_to_gpu_display(GPUDisplay *gpu_display, - PassMode pass_mode, - int num_samples) = 0; + virtual void copy_to_display(PathTraceDisplay *display, PassMode pass_mode, int num_samples) = 0; - virtual void destroy_gpu_resources(GPUDisplay *gpu_display) = 0; + virtual void destroy_gpu_resources(PathTraceDisplay *display) = 0; /* Copy data from/to given render buffers. * Will copy pixels from a corresponding place (from multi-device point of view) of the render @@ -162,8 +160,8 @@ class PathTraceWork { /* Get destination which offset and stride are configured so that writing to it will write to a * proper location of GPU display texture, taking current tile and device slice into account. */ - PassAccessor::Destination get_gpu_display_destination_template( - const GPUDisplay *gpu_display) const; + PassAccessor::Destination get_display_destination_template( + const PathTraceDisplay *display) const; /* Device which will be used for path tracing. * Note that it is an actual render device (and never is a multi-device). */ diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp index 14658d4d1ce..18a5365453d 100644 --- a/intern/cycles/integrator/path_trace_work_cpu.cpp +++ b/intern/cycles/integrator/path_trace_work_cpu.cpp @@ -22,9 +22,9 @@ #include "kernel/kernel_path_state.h" #include "integrator/pass_accessor_cpu.h" +#include "integrator/path_trace_display.h" #include "render/buffers.h" -#include "render/gpu_display.h" #include "render/scene.h" #include "util/util_atomic.h" @@ -161,14 +161,14 @@ void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_global } } -void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display, - PassMode pass_mode, - int num_samples) +void PathTraceWorkCPU::copy_to_display(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) { - half4 *rgba_half = gpu_display->map_texture_buffer(); + half4 *rgba_half = display->map_texture_buffer(); if (!rgba_half) { - /* TODO(sergey): Look into using copy_to_gpu_display() if mapping failed. Might be needed for - * some implementations of GPUDisplay which can not map memory? */ + /* TODO(sergey): Look into using copy_to_display() if mapping failed. Might be needed for + * some implementations of PathTraceDisplay which can not map memory? */ return; } @@ -178,7 +178,7 @@ void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display, const PassAccessorCPU pass_accessor(pass_access_info, kfilm.exposure, num_samples); - PassAccessor::Destination destination = get_gpu_display_destination_template(gpu_display); + PassAccessor::Destination destination = get_display_destination_template(display); destination.pixels_half_rgba = rgba_half; tbb::task_arena local_arena = local_tbb_arena_create(device_); @@ -186,10 +186,10 @@ void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display, pass_accessor.get_render_tile_pixels(buffers_.get(), effective_buffer_params_, destination); }); - gpu_display->unmap_texture_buffer(); + display->unmap_texture_buffer(); } -void PathTraceWorkCPU::destroy_gpu_resources(GPUDisplay * /*gpu_display*/) +void PathTraceWorkCPU::destroy_gpu_resources(PathTraceDisplay * /*display*/) { } diff --git a/intern/cycles/integrator/path_trace_work_cpu.h b/intern/cycles/integrator/path_trace_work_cpu.h index ab729bbf879..d011e8d05bd 100644 --- a/intern/cycles/integrator/path_trace_work_cpu.h +++ b/intern/cycles/integrator/path_trace_work_cpu.h @@ -50,10 +50,10 @@ class PathTraceWorkCPU : public PathTraceWork { int start_sample, int samples_num) override; - virtual void copy_to_gpu_display(GPUDisplay *gpu_display, - PassMode pass_mode, - int num_samples) override; - virtual void destroy_gpu_resources(GPUDisplay *gpu_display) override; + virtual void copy_to_display(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) override; + virtual void destroy_gpu_resources(PathTraceDisplay *display) override; virtual bool copy_render_buffers_from_device() override; virtual bool copy_render_buffers_to_device() override; diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index e41d8d1d252..17c49f244d2 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -15,12 +15,12 @@ */ #include "integrator/path_trace_work_gpu.h" +#include "integrator/path_trace_display.h" #include "device/device.h" #include "integrator/pass_accessor_gpu.h" #include "render/buffers.h" -#include "render/gpu_display.h" #include "render/scene.h" #include "util/util_logging.h" #include "util/util_tbb.h" @@ -46,7 +46,7 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device, queued_paths_(device, "queued_paths", MEM_READ_WRITE), num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE), work_tiles_(device, "work_tiles", MEM_READ_WRITE), - gpu_display_rgba_half_(device, "display buffer half", MEM_READ_WRITE), + display_rgba_half_(device, "display buffer half", MEM_READ_WRITE), max_num_paths_(queue_->num_concurrent_states(sizeof(IntegratorStateCPU))), min_num_active_paths_(queue_->num_concurrent_busy_states()), max_active_path_index_(0) @@ -652,7 +652,7 @@ int PathTraceWorkGPU::get_num_active_paths() bool PathTraceWorkGPU::should_use_graphics_interop() { /* There are few aspects with the graphics interop when using multiple devices caused by the fact - * that the GPUDisplay has a single texture: + * that the PathTraceDisplay has a single texture: * * CUDA will return `CUDA_ERROR_NOT_SUPPORTED` from `cuGraphicsGLRegisterBuffer()` when * attempting to register OpenGL PBO which has been mapped. Which makes sense, because @@ -678,9 +678,9 @@ bool PathTraceWorkGPU::should_use_graphics_interop() return interop_use_; } -void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display, - PassMode pass_mode, - int num_samples) +void PathTraceWorkGPU::copy_to_display(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) { if (device_->have_error()) { /* Don't attempt to update GPU display if the device has errors: the error state will make @@ -694,7 +694,7 @@ void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display, } if (should_use_graphics_interop()) { - if (copy_to_gpu_display_interop(gpu_display, pass_mode, num_samples)) { + if (copy_to_display_interop(display, pass_mode, num_samples)) { return; } @@ -703,12 +703,12 @@ void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display, interop_use_ = false; } - copy_to_gpu_display_naive(gpu_display, pass_mode, num_samples); + copy_to_display_naive(display, pass_mode, num_samples); } -void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display, - PassMode pass_mode, - int num_samples) +void PathTraceWorkGPU::copy_to_display_naive(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) { const int full_x = effective_buffer_params_.full_x; const int full_y = effective_buffer_params_.full_y; @@ -725,44 +725,42 @@ void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display, * NOTE: allocation happens to the final resolution so that no re-allocation happens on every * change of the resolution divider. However, if the display becomes smaller, shrink the * allocated memory as well. */ - if (gpu_display_rgba_half_.data_width != final_width || - gpu_display_rgba_half_.data_height != final_height) { - gpu_display_rgba_half_.alloc(final_width, final_height); + if (display_rgba_half_.data_width != final_width || + display_rgba_half_.data_height != final_height) { + display_rgba_half_.alloc(final_width, final_height); /* TODO(sergey): There should be a way to make sure device-side memory is allocated without * transferring zeroes to the device. */ - queue_->zero_to_device(gpu_display_rgba_half_); + queue_->zero_to_device(display_rgba_half_); } PassAccessor::Destination destination(film_->get_display_pass()); - destination.d_pixels_half_rgba = gpu_display_rgba_half_.device_pointer; + destination.d_pixels_half_rgba = display_rgba_half_.device_pointer; get_render_tile_film_pixels(destination, pass_mode, num_samples); - queue_->copy_from_device(gpu_display_rgba_half_); + queue_->copy_from_device(display_rgba_half_); queue_->synchronize(); - gpu_display->copy_pixels_to_texture( - gpu_display_rgba_half_.data(), texture_x, texture_y, width, height); + display->copy_pixels_to_texture(display_rgba_half_.data(), texture_x, texture_y, width, height); } -bool PathTraceWorkGPU::copy_to_gpu_display_interop(GPUDisplay *gpu_display, - PassMode pass_mode, - int num_samples) +bool PathTraceWorkGPU::copy_to_display_interop(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) { if (!device_graphics_interop_) { device_graphics_interop_ = queue_->graphics_interop_create(); } - const DeviceGraphicsInteropDestination graphics_interop_dst = - gpu_display->graphics_interop_get(); - device_graphics_interop_->set_destination(graphics_interop_dst); + const DisplayDriver::GraphicsInterop graphics_interop_dst = display->graphics_interop_get(); + device_graphics_interop_->set_display_interop(graphics_interop_dst); const device_ptr d_rgba_half = device_graphics_interop_->map(); if (!d_rgba_half) { return false; } - PassAccessor::Destination destination = get_gpu_display_destination_template(gpu_display); + PassAccessor::Destination destination = get_display_destination_template(display); destination.d_pixels_half_rgba = d_rgba_half; get_render_tile_film_pixels(destination, pass_mode, num_samples); @@ -772,14 +770,14 @@ bool PathTraceWorkGPU::copy_to_gpu_display_interop(GPUDisplay *gpu_display, return true; } -void PathTraceWorkGPU::destroy_gpu_resources(GPUDisplay *gpu_display) +void PathTraceWorkGPU::destroy_gpu_resources(PathTraceDisplay *display) { if (!device_graphics_interop_) { return; } - gpu_display->graphics_interop_activate(); + display->graphics_interop_activate(); device_graphics_interop_ = nullptr; - gpu_display->graphics_interop_deactivate(); + display->graphics_interop_deactivate(); } void PathTraceWorkGPU::get_render_tile_film_pixels(const PassAccessor::Destination &destination, diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h index 38788122b0d..9212537d2fd 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.h +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -48,10 +48,10 @@ class PathTraceWorkGPU : public PathTraceWork { int start_sample, int samples_num) override; - virtual void copy_to_gpu_display(GPUDisplay *gpu_display, - PassMode pass_mode, - int num_samples) override; - virtual void destroy_gpu_resources(GPUDisplay *gpu_display) override; + virtual void copy_to_display(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) override; + virtual void destroy_gpu_resources(PathTraceDisplay *display) override; virtual bool copy_render_buffers_from_device() override; virtual bool copy_render_buffers_to_device() override; @@ -88,16 +88,16 @@ class PathTraceWorkGPU : public PathTraceWork { int get_num_active_paths(); - /* Check whether graphics interop can be used for the GPUDisplay update. */ + /* Check whether graphics interop can be used for the PathTraceDisplay update. */ bool should_use_graphics_interop(); - /* Naive implementation of the `copy_to_gpu_display()` which performs film conversion on the - * device, then copies pixels to the host and pushes them to the `gpu_display`. */ - void copy_to_gpu_display_naive(GPUDisplay *gpu_display, PassMode pass_mode, int num_samples); + /* Naive implementation of the `copy_to_display()` which performs film conversion on the + * device, then copies pixels to the host and pushes them to the `display`. */ + void copy_to_display_naive(PathTraceDisplay *display, PassMode pass_mode, int num_samples); - /* Implementation of `copy_to_gpu_display()` which uses driver's OpenGL/GPU interoperability + /* Implementation of `copy_to_display()` which uses driver's OpenGL/GPU interoperability * functionality, avoiding copy of pixels to the host. */ - bool copy_to_gpu_display_interop(GPUDisplay *gpu_display, PassMode pass_mode, int num_samples); + bool copy_to_display_interop(PathTraceDisplay *display, PassMode pass_mode, int num_samples); /* Synchronously run film conversion kernel and store display result in the given destination. */ void get_render_tile_film_pixels(const PassAccessor::Destination &destination, @@ -139,9 +139,9 @@ class PathTraceWorkGPU : public PathTraceWork { /* Temporary buffer for passing work tiles to kernel. */ device_vector work_tiles_; - /* Temporary buffer used by the copy_to_gpu_display() whenever graphics interoperability is not + /* Temporary buffer used by the copy_to_display() whenever graphics interoperability is not * available. Is allocated on-demand. */ - device_vector gpu_display_rgba_half_; + device_vector display_rgba_half_; unique_ptr device_graphics_interop_; diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h index 6ed368a2dc8..c4ab15e54ba 100644 --- a/intern/cycles/integrator/render_scheduler.h +++ b/intern/cycles/integrator/render_scheduler.h @@ -344,7 +344,7 @@ class RenderScheduler { /* Number of rendered samples on top of the start sample. */ int num_rendered_samples = 0; - /* Point in time the latest GPUDisplay work has been scheduled. */ + /* Point in time the latest PathTraceDisplay work has been scheduled. */ double last_display_update_time = 0.0; /* Value of -1 means display was never updated. */ int last_display_update_sample = -1; diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index 6edb5261b32..ce1a9e5f430 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -35,7 +35,6 @@ set(SRC denoising.cpp film.cpp geometry.cpp - gpu_display.cpp graph.cpp hair.cpp image.cpp @@ -78,9 +77,9 @@ set(SRC_HEADERS colorspace.h constant_fold.h denoising.h + display_driver.h film.h geometry.h - gpu_display.h graph.h hair.h image.h diff --git a/intern/cycles/render/display_driver.h b/intern/cycles/render/display_driver.h new file mode 100644 index 00000000000..85f305034d7 --- /dev/null +++ b/intern/cycles/render/display_driver.h @@ -0,0 +1,131 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/util_half.h" +#include "util/util_types.h" + +CCL_NAMESPACE_BEGIN + +/* Display driver for efficient interactive display of renders. + * + * Host applications implement this interface for viewport rendering. For best performance, we + * recommend: + * - Allocating a texture on the GPU to be interactively updated + * - Using the graphics interop mechanism to avoid CPU-GPU copying overhead + * - Using a dedicated or thread-safe graphics API context for updates, to avoid + * blocking the host application. + */ +class DisplayDriver { + public: + DisplayDriver() = default; + virtual ~DisplayDriver() = default; + + /* Render buffer parameters. */ + struct Params { + public: + /* Render resolution, ignoring progressive resolution changes. + * The texture buffer should be allocated with this size. */ + int2 size = make_int2(0, 0); + + /* For border rendering, the full resolution of the render, and the offset within that larger + * render. */ + int2 full_size = make_int2(0, 0); + int2 full_offset = make_int2(0, 0); + + bool modified(const Params &other) const + { + return !(full_offset == other.full_offset && full_size == other.full_size && + size == other.size); + } + }; + + /* Update the render from the rendering thread. + * + * Cycles periodically updates the render to be displayed. For multithreaded updates with + * potentially multiple rendering devices, it will call these methods as follows. + * + * if (driver.update_begin(params, width, height)) { + * parallel_for_each(rendering_device) { + * buffer = driver.map_texture_buffer(); + * if (buffer) { + * fill(buffer); + * driver.unmap_texture_buffer(); + * } + * } + * driver.update_end(); + * } + * + * The parameters may dynamically change due to camera changes in the scene, and resources should + * be re-allocated accordingly. + * + * The width and height passed to update_begin() are the effective render resolution taking into + * account progressive resolution changes, which may be equal to or smaller than the params.size. + * For efficiency, changes in this resolution should be handled without re-allocating resources, + * but rather by using a subset of the full resolution buffer. */ + virtual bool update_begin(const Params ¶ms, int width, int height) = 0; + virtual void update_end() = 0; + + virtual half4 *map_texture_buffer() = 0; + virtual void unmap_texture_buffer() = 0; + + /* Optionally return a handle to a native graphics API texture buffer. If supported, + * the rendering device may write directly to this buffer instead of calling + * map_texture_buffer() and unmap_texture_buffer(). */ + class GraphicsInterop { + public: + /* Dimensions of the buffer, in pixels. */ + int buffer_width = 0; + int buffer_height = 0; + + /* OpenGL pixel buffer object. */ + int opengl_pbo_id = 0; + + /* Clear the entire buffer before doing partial write to it. */ + bool need_clear = false; + }; + + virtual GraphicsInterop graphics_interop_get() + { + return GraphicsInterop(); + } + + /* (De)activate graphics context required for editing or deleting the graphics interop + * object. + * + * For example, destruction of the CUDA object associated with an OpenGL requires the + * OpenGL context to be active. */ + virtual void graphics_interop_activate(){}; + virtual void graphics_interop_deactivate(){}; + + /* Clear the display buffer by filling it with zeros. */ + virtual void clear() = 0; + + /* Draw the render using the native graphics API. + * + * Note that this may be called in parallel to updates. The implementation is responsible for + * mutex locking or other mechanisms to avoid conflicts. + * + * The parameters may have changed since the last update. The implementation is responsible for + * deciding to skip or adjust render display for such changes. + * + * Host application drawing the render buffer should use Session.draw(), which will + * call this method. */ + virtual void draw(const Params ¶ms) = 0; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 269d67e8bda..c191b9a9b4a 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -25,7 +25,7 @@ #include "render/bake.h" #include "render/buffers.h" #include "render/camera.h" -#include "render/gpu_display.h" +#include "render/display_driver.h" #include "render/graph.h" #include "render/integrator.h" #include "render/light.h" @@ -162,7 +162,7 @@ bool Session::ready_to_reset() void Session::run_main_render_loop() { - path_trace_->clear_gpu_display(); + path_trace_->clear_display(); while (true) { RenderWork render_work = run_update_for_next_iteration(); @@ -514,9 +514,9 @@ void Session::set_pause(bool pause) } } -void Session::set_gpu_display(unique_ptr gpu_display) +void Session::set_display_driver(unique_ptr driver) { - path_trace_->set_gpu_display(move(gpu_display)); + path_trace_->set_display_driver(move(driver)); } double Session::get_estimated_remaining_time() const diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index e3056e7778b..607e40c47c1 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -35,9 +35,9 @@ CCL_NAMESPACE_BEGIN class BufferParams; class Device; class DeviceScene; +class DisplayDriver; class PathTrace; class Progress; -class GPUDisplay; class RenderBuffers; class Scene; class SceneParams; @@ -143,7 +143,7 @@ class Session { void set_samples(int samples); void set_time_limit(double time_limit); - void set_gpu_display(unique_ptr gpu_display); + void set_display_driver(unique_ptr driver); double get_estimated_remaining_time() const; From 1a134c4c30a643ada1b9a7a037040b5f5c173a28 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 30 Sep 2021 16:51:03 +0200 Subject: [PATCH 0403/1500] Cycles: refactor API for render output * Add OutputDriver, replacing function callbacks in Session. * Add PathTraceTile, replacing tile access methods in Session. * Add more detailed comments about how this driver should be implemented. * Add OIIOOutputDriver for Cycles standalone to output an image. Differential Revision: https://developer.blender.org/D12627 --- intern/cycles/app/CMakeLists.txt | 4 +- intern/cycles/app/cycles_standalone.cpp | 40 ++--- intern/cycles/app/cycles_xml.cpp | 1 + intern/cycles/app/oiio_output_driver.cpp | 71 ++++++++ intern/cycles/app/oiio_output_driver.h | 42 +++++ intern/cycles/blender/CMakeLists.txt | 2 + .../cycles/blender/blender_output_driver.cpp | 127 +++++++++++++++ intern/cycles/blender/blender_output_driver.h | 40 +++++ intern/cycles/blender/blender_session.cpp | 154 +----------------- intern/cycles/blender/blender_session.h | 13 -- intern/cycles/integrator/CMakeLists.txt | 2 + intern/cycles/integrator/path_trace.cpp | 32 +++- intern/cycles/integrator/path_trace.h | 23 ++- intern/cycles/integrator/path_trace_tile.cpp | 107 ++++++++++++ intern/cycles/integrator/path_trace_tile.h | 43 +++++ intern/cycles/render/CMakeLists.txt | 1 + intern/cycles/render/output_driver.h | 82 ++++++++++ intern/cycles/render/session.cpp | 138 +--------------- intern/cycles/render/session.h | 26 +-- intern/cycles/render/tile.cpp | 5 + intern/cycles/render/tile.h | 1 + 21 files changed, 594 insertions(+), 360 deletions(-) create mode 100644 intern/cycles/app/oiio_output_driver.cpp create mode 100644 intern/cycles/app/oiio_output_driver.h create mode 100644 intern/cycles/blender/blender_output_driver.cpp create mode 100644 intern/cycles/blender/blender_output_driver.h create mode 100644 intern/cycles/integrator/path_trace_tile.cpp create mode 100644 intern/cycles/integrator/path_trace_tile.h create mode 100644 intern/cycles/render/output_driver.h diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index f9dc5f00802..3ed3f54ef9f 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -64,6 +64,8 @@ if(WITH_CYCLES_STANDALONE) cycles_standalone.cpp cycles_xml.cpp cycles_xml.h + oiio_output_driver.cpp + oiio_output_driver.h ) add_executable(cycles ${SRC} ${INC} ${INC_SYS}) unset(SRC) @@ -73,7 +75,7 @@ if(WITH_CYCLES_STANDALONE) if(APPLE) if(WITH_OPENCOLORIO) - set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit") + set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit -framework Carbon") endif() if(WITH_OPENIMAGEDENOISE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") # OpenImageDenoise uses BNNS from the Accelerate framework. diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp index 258e67b3459..00dc140648a 100644 --- a/intern/cycles/app/cycles_standalone.cpp +++ b/intern/cycles/app/cycles_standalone.cpp @@ -36,6 +36,9 @@ #include "util/util_unique_ptr.h" #include "util/util_version.h" +#include "app/cycles_xml.h" +#include "app/oiio_output_driver.h" + #ifdef WITH_CYCLES_STANDALONE_GUI # include "util/util_view.h" #endif @@ -54,6 +57,7 @@ struct Options { bool quiet; bool show_help, interactive, pause; string output_filepath; + string output_pass; } options; static void session_print(const string &str) @@ -89,30 +93,6 @@ static void session_print_status() session_print(status); } -static bool write_render(const uchar *pixels, int w, int h, int channels) -{ - string msg = string_printf("Writing image %s", options.output_path.c_str()); - session_print(msg); - - unique_ptr out = unique_ptr(ImageOutput::create(options.output_path)); - if (!out) { - return false; - } - - ImageSpec spec(w, h, channels, TypeDesc::UINT8); - if (!out->open(options.output_path, spec)) { - return false; - } - - /* conversion for different top/bottom convention */ - out->write_image( - TypeDesc::UINT8, pixels + (h - 1) * w * channels, AutoStride, -w * channels, AutoStride); - - out->close(); - - return true; -} - static BufferParams &session_buffer_params() { static BufferParams buffer_params; @@ -147,9 +127,14 @@ static void scene_init() static void session_init() { - options.session_params.write_render_cb = write_render; + options.output_pass = "combined"; options.session = new Session(options.session_params, options.scene_params); + if (!options.output_filepath.empty()) { + options.session->set_output_driver(make_unique( + options.output_filepath, options.output_pass, session_print)); + } + if (options.session_params.background && !options.quiet) options.session->progress.set_update_callback(function_bind(&session_print_status)); #ifdef WITH_CYCLES_STANDALONE_GUI @@ -160,6 +145,11 @@ static void session_init() /* load scene */ scene_init(); + /* add pass for output. */ + Pass *pass = options.scene->create_node(); + pass->set_name(ustring(options.output_pass.c_str())); + pass->set_type(PASS_COMBINED); + options.session->reset(options.session_params, session_buffer_params()); options.session->start(); } diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 54f97fddbd9..0b83c60f32d 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -333,6 +333,7 @@ static void xml_read_shader_graph(XMLReadState &state, Shader *shader, xml_node } snode = (ShaderNode *)node_type->create(node_type); + snode->set_owner(graph); } xml_read_node(graph_reader, snode, node); diff --git a/intern/cycles/app/oiio_output_driver.cpp b/intern/cycles/app/oiio_output_driver.cpp new file mode 100644 index 00000000000..d791c89772f --- /dev/null +++ b/intern/cycles/app/oiio_output_driver.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "app/oiio_output_driver.h" + +CCL_NAMESPACE_BEGIN + +OIIOOutputDriver::OIIOOutputDriver(const string_view filepath, + const string_view pass, + LogFunction log) + : filepath_(filepath), pass_(pass), log_(log) +{ +} + +OIIOOutputDriver::~OIIOOutputDriver() +{ +} + +void OIIOOutputDriver::write_render_tile(const Tile &tile) +{ + /* Only write the full buffer, no intermediate tiles. */ + if (!(tile.size == tile.full_size)) { + return; + } + + log_(string_printf("Writing image %s", filepath_.c_str())); + + unique_ptr image_output(ImageOutput::create(filepath_)); + if (image_output == nullptr) { + log_("Failed to create image file"); + return; + } + + const int width = tile.size.x; + const int height = tile.size.y; + + ImageSpec spec(width, height, 4, TypeDesc::FLOAT); + if (!image_output->open(filepath_, spec)) { + log_("Failed to create image file"); + return; + } + + vector pixels(width * height * 4); + if (!tile.get_pass_pixels(pass_, 4, pixels.data())) { + log_("Failed to read render pass pixels"); + return; + } + + /* Manipulate offset and stride to convert from bottom-up to top-down convention. */ + image_output->write_image(TypeDesc::FLOAT, + pixels.data() + (height - 1) * width * 4, + AutoStride, + -width * 4 * sizeof(float), + AutoStride); + image_output->close(); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/app/oiio_output_driver.h b/intern/cycles/app/oiio_output_driver.h new file mode 100644 index 00000000000..cdc4085d962 --- /dev/null +++ b/intern/cycles/app/oiio_output_driver.h @@ -0,0 +1,42 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "render/output_driver.h" + +#include "util/util_function.h" +#include "util/util_image.h" +#include "util/util_string.h" +#include "util/util_unique_ptr.h" +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +class OIIOOutputDriver : public OutputDriver { + public: + typedef function LogFunction; + + OIIOOutputDriver(const string_view filepath, const string_view pass, LogFunction log); + virtual ~OIIOOutputDriver(); + + void write_render_tile(const Tile &tile) override; + + protected: + string filepath_; + string pass_; + LogFunction log_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index 2660eee017b..a0442b3394b 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -38,6 +38,7 @@ set(SRC blender_mesh.cpp blender_object.cpp blender_object_cull.cpp + blender_output_driver.cpp blender_particles.cpp blender_curves.cpp blender_logging.cpp @@ -55,6 +56,7 @@ set(SRC blender_id_map.h blender_image.h blender_object_cull.h + blender_output_driver.h blender_sync.h blender_session.h blender_texture.h diff --git a/intern/cycles/blender/blender_output_driver.cpp b/intern/cycles/blender/blender_output_driver.cpp new file mode 100644 index 00000000000..f380b7b3bb1 --- /dev/null +++ b/intern/cycles/blender/blender_output_driver.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "blender/blender_output_driver.h" + +CCL_NAMESPACE_BEGIN + +BlenderOutputDriver::BlenderOutputDriver(BL::RenderEngine &b_engine) : b_engine_(b_engine) +{ +} + +BlenderOutputDriver::~BlenderOutputDriver() +{ +} + +bool BlenderOutputDriver::read_render_tile(const Tile &tile) +{ + /* Get render result. */ + BL::RenderResult b_rr = b_engine_.begin_result(tile.offset.x, + tile.offset.y, + tile.size.x, + tile.size.y, + tile.layer.c_str(), + tile.view.c_str()); + + /* Can happen if the intersected rectangle gives 0 width or height. */ + if (b_rr.ptr.data == NULL) { + return false; + } + + BL::RenderResult::layers_iterator b_single_rlay; + b_rr.layers.begin(b_single_rlay); + + /* layer will be missing if it was disabled in the UI */ + if (b_single_rlay == b_rr.layers.end()) { + return false; + } + + BL::RenderLayer b_rlay = *b_single_rlay; + + vector pixels(tile.size.x * tile.size.y * 4); + + /* Copy each pass. + * TODO:copy only the required ones for better performance? */ + for (BL::RenderPass &b_pass : b_rlay.passes) { + tile.set_pass_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect()); + } + + b_engine_.end_result(b_rr, false, false, false); + + return true; +} + +bool BlenderOutputDriver::update_render_tile(const Tile &tile) +{ + /* Use final write for preview renders, otherwise render result wouldn't be be updated + * quickly on Blender side. For all other cases we use the display driver. */ + if (b_engine_.is_preview()) { + write_render_tile(tile); + return true; + } + else { + /* Don't highlight full-frame tile. */ + if (!(tile.size == tile.full_size)) { + b_engine_.tile_highlight_clear_all(); + b_engine_.tile_highlight_set(tile.offset.x, tile.offset.y, tile.size.x, tile.size.y, true); + } + + return false; + } +} + +void BlenderOutputDriver::write_render_tile(const Tile &tile) +{ + b_engine_.tile_highlight_clear_all(); + + /* Get render result. */ + BL::RenderResult b_rr = b_engine_.begin_result(tile.offset.x, + tile.offset.y, + tile.size.x, + tile.size.y, + tile.layer.c_str(), + tile.view.c_str()); + + /* Can happen if the intersected rectangle gives 0 width or height. */ + if (b_rr.ptr.data == NULL) { + return; + } + + BL::RenderResult::layers_iterator b_single_rlay; + b_rr.layers.begin(b_single_rlay); + + /* Layer will be missing if it was disabled in the UI. */ + if (b_single_rlay == b_rr.layers.end()) { + return; + } + + BL::RenderLayer b_rlay = *b_single_rlay; + + vector pixels(tile.size.x * tile.size.y * 4); + + /* Copy each pass. */ + for (BL::RenderPass &b_pass : b_rlay.passes) { + if (!tile.get_pass_pixels(b_pass.name(), b_pass.channels(), &pixels[0])) { + memset(&pixels[0], 0, pixels.size() * sizeof(float)); + } + + b_pass.rect(&pixels[0]); + } + + b_engine_.end_result(b_rr, true, false, true); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_output_driver.h b/intern/cycles/blender/blender_output_driver.h new file mode 100644 index 00000000000..8a1cf92d7c7 --- /dev/null +++ b/intern/cycles/blender/blender_output_driver.h @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "RNA_blender_cpp.h" + +#include "render/output_driver.h" + +CCL_NAMESPACE_BEGIN + +class BlenderOutputDriver : public OutputDriver { + public: + BlenderOutputDriver(BL::RenderEngine &b_engine); + ~BlenderOutputDriver(); + + virtual void write_render_tile(const Tile &tile) override; + virtual bool update_render_tile(const Tile &tile) override; + virtual bool read_render_tile(const Tile &tile) override; + + protected: + BL::RenderEngine b_engine_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 1a42456eda0..3be7ff32bd8 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -43,6 +43,7 @@ #include "util/util_time.h" #include "blender/blender_display_driver.h" +#include "blender/blender_output_driver.h" #include "blender/blender_session.h" #include "blender/blender_sync.h" #include "blender/blender_util.h" @@ -157,7 +158,8 @@ void BlenderSession::create_session() b_v3d, b_rv3d, scene->camera, width, height); session->reset(session_params, buffer_params); - /* Create GPU display. */ + /* Create GPU display. + * TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */ if (!b_engine.is_preview() && !headless) { unique_ptr display_driver = make_unique(b_engine, b_scene); @@ -279,96 +281,6 @@ void BlenderSession::free_session() session = nullptr; } -void BlenderSession::read_render_tile() -{ - const int2 tile_offset = session->get_render_tile_offset(); - const int2 tile_size = session->get_render_tile_size(); - - /* get render result */ - BL::RenderResult b_rr = b_engine.begin_result(tile_offset.x, - tile_offset.y, - tile_size.x, - tile_size.y, - b_rlay_name.c_str(), - b_rview_name.c_str()); - - /* can happen if the intersected rectangle gives 0 width or height */ - if (b_rr.ptr.data == NULL) { - return; - } - - BL::RenderResult::layers_iterator b_single_rlay; - b_rr.layers.begin(b_single_rlay); - - /* layer will be missing if it was disabled in the UI */ - if (b_single_rlay == b_rr.layers.end()) - return; - - BL::RenderLayer b_rlay = *b_single_rlay; - - vector pixels(tile_size.x * tile_size.y * 4); - - /* Copy each pass. - * TODO:copy only the required ones for better performance? */ - for (BL::RenderPass &b_pass : b_rlay.passes) { - session->set_render_tile_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect()); - } - - b_engine.end_result(b_rr, false, false, false); -} - -void BlenderSession::write_render_tile() -{ - const int2 tile_offset = session->get_render_tile_offset(); - const int2 tile_size = session->get_render_tile_size(); - - const string_view render_layer_name = session->get_render_tile_layer(); - const string_view render_view_name = session->get_render_tile_view(); - - b_engine.tile_highlight_clear_all(); - - /* get render result */ - BL::RenderResult b_rr = b_engine.begin_result(tile_offset.x, - tile_offset.y, - tile_size.x, - tile_size.y, - render_layer_name.c_str(), - render_view_name.c_str()); - - /* can happen if the intersected rectangle gives 0 width or height */ - if (b_rr.ptr.data == NULL) { - return; - } - - BL::RenderResult::layers_iterator b_single_rlay; - b_rr.layers.begin(b_single_rlay); - - /* layer will be missing if it was disabled in the UI */ - if (b_single_rlay == b_rr.layers.end()) { - return; - } - - BL::RenderLayer b_rlay = *b_single_rlay; - - write_render_result(b_rlay); - - b_engine.end_result(b_rr, true, false, true); -} - -void BlenderSession::update_render_tile() -{ - if (!session->has_multiple_render_tiles()) { - /* Don't highlight full-frame tile. */ - return; - } - - const int2 tile_offset = session->get_render_tile_offset(); - const int2 tile_size = session->get_render_tile_size(); - - b_engine.tile_highlight_clear_all(); - b_engine.tile_highlight_set(tile_offset.x, tile_offset.y, tile_size.x, tile_size.y, true); -} - void BlenderSession::full_buffer_written(string_view filename) { full_buffer_files_.emplace_back(filename); @@ -442,18 +354,8 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) return; } - /* set callback to write out render results */ - session->write_render_tile_cb = [&]() { write_render_tile(); }; - - /* Use final write for preview renders, otherwise render result wouldn't be be updated on Blender - * side. */ - /* TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */ - if (b_engine.is_preview()) { - session->update_render_tile_cb = [&]() { write_render_tile(); }; - } - else { - session->update_render_tile_cb = [&]() { update_render_tile(); }; - } + /* Create driver to write out render results. */ + session->set_output_driver(make_unique(b_engine)); session->full_buffer_written_cb = [&](string_view filename) { full_buffer_written(filename); }; @@ -599,9 +501,8 @@ void BlenderSession::render_frame_finish() path_remove(filename); } - /* clear callback */ - session->write_render_tile_cb = function_null; - session->update_render_tile_cb = function_null; + /* Clear driver. */ + session->set_output_driver(nullptr); session->full_buffer_written_cb = function_null; } @@ -707,9 +608,8 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, pass->set_type(bake_type_to_pass(bake_type, bake_filter)); pass->set_include_albedo((bake_filter & BL::BakeSettings::pass_filter_COLOR)); - session->read_render_tile_cb = [&]() { read_render_tile(); }; - session->write_render_tile_cb = [&]() { write_render_tile(); }; session->set_display_driver(nullptr); + session->set_output_driver(make_unique(b_engine)); if (!session->progress.get_cancel()) { /* Sync scene. */ @@ -752,43 +652,7 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, session->wait(); } - session->read_render_tile_cb = function_null; - session->write_render_tile_cb = function_null; -} - -void BlenderSession::write_render_result(BL::RenderLayer &b_rlay) -{ - if (!session->copy_render_tile_from_device()) { - return; - } - - const int2 tile_size = session->get_render_tile_size(); - vector pixels(tile_size.x * tile_size.y * 4); - - /* Copy each pass. */ - for (BL::RenderPass &b_pass : b_rlay.passes) { - if (!session->get_render_tile_pixels(b_pass.name(), b_pass.channels(), &pixels[0])) { - memset(&pixels[0], 0, pixels.size() * sizeof(float)); - } - - b_pass.rect(&pixels[0]); - } -} - -void BlenderSession::update_render_result(BL::RenderLayer &b_rlay) -{ - if (!session->copy_render_tile_from_device()) { - return; - } - - const int2 tile_size = session->get_render_tile_size(); - vector pixels(tile_size.x * tile_size.y * 4); - - /* Copy combined pass. */ - BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str())); - if (session->get_render_tile_pixels("Combined", b_combined_pass.channels(), &pixels[0])) { - b_combined_pass.rect(&pixels[0]); - } + session->set_output_driver(nullptr); } void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index 1ca8fdf87d0..fef6ad1adfc 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -70,20 +70,7 @@ class BlenderSession { const int bake_width, const int bake_height); - void write_render_result(BL::RenderLayer &b_rlay); - void write_render_tile(); - - void update_render_tile(); - void full_buffer_written(string_view filename); - - /* update functions are used to update display buffer only after sample was rendered - * only needed for better visual feedback */ - void update_render_result(BL::RenderLayer &b_rlay); - - /* read functions for baking input */ - void read_render_tile(); - /* interactive updates */ void synchronize(BL::Depsgraph &b_depsgraph); diff --git a/intern/cycles/integrator/CMakeLists.txt b/intern/cycles/integrator/CMakeLists.txt index 8acd72f0508..949254606b8 100644 --- a/intern/cycles/integrator/CMakeLists.txt +++ b/intern/cycles/integrator/CMakeLists.txt @@ -28,6 +28,7 @@ set(SRC pass_accessor_cpu.cpp pass_accessor_gpu.cpp path_trace_display.cpp + path_trace_tile.cpp path_trace_work.cpp path_trace_work_cpu.cpp path_trace_work_gpu.cpp @@ -49,6 +50,7 @@ set(SRC_HEADERS pass_accessor_cpu.h pass_accessor_gpu.h path_trace_display.h + path_trace_tile.h path_trace_work.h path_trace_work_cpu.h path_trace_work_gpu.h diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 36cd7314b4c..7624b244175 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -20,6 +20,7 @@ #include "device/device.h" #include "integrator/pass_accessor.h" #include "integrator/path_trace_display.h" +#include "integrator/path_trace_tile.h" #include "integrator/render_scheduler.h" #include "render/pass.h" #include "render/scene.h" @@ -535,6 +536,11 @@ void PathTrace::denoise(const RenderWork &render_work) render_scheduler_.report_denoise_time(render_work, time_dt() - start_time); } +void PathTrace::set_output_driver(unique_ptr driver) +{ + output_driver_ = move(driver); +} + void PathTrace::set_display_driver(unique_ptr driver) { if (driver) { @@ -567,7 +573,7 @@ void PathTrace::update_display(const RenderWork &render_work) return; } - if (!display_ && !tile_buffer_update_cb) { + if (!display_ && !output_driver_) { VLOG(3) << "Ignore display update."; return; } @@ -579,10 +585,11 @@ void PathTrace::update_display(const RenderWork &render_work) const double start_time = time_dt(); - if (tile_buffer_update_cb) { + if (output_driver_) { VLOG(3) << "Invoke buffer update callback."; - tile_buffer_update_cb(); + PathTraceTile tile(*this); + output_driver_->update_render_tile(tile); } if (display_) { @@ -758,20 +765,26 @@ bool PathTrace::is_cancel_requested() void PathTrace::tile_buffer_write() { - if (!tile_buffer_write_cb) { + if (!output_driver_) { return; } - tile_buffer_write_cb(); + PathTraceTile tile(*this); + output_driver_->write_render_tile(tile); } void PathTrace::tile_buffer_read() { - if (!tile_buffer_read_cb) { + if (!device_scene_->data.bake.use) { return; } - if (tile_buffer_read_cb()) { + if (!output_driver_) { + return; + } + + PathTraceTile tile(*this); + if (output_driver_->read_render_tile(tile)) { tbb::parallel_for_each(path_trace_works_, [](unique_ptr &path_trace_work) { path_trace_work->copy_render_buffers_to_device(); }); @@ -1010,6 +1023,11 @@ int2 PathTrace::get_render_tile_offset() const return make_int2(tile.x, tile.y); } +int2 PathTrace::get_render_size() const +{ + return tile_manager_.get_size(); +} + const BufferParams &PathTrace::get_render_tile_params() const { if (full_frame_state_.render_buffers) { diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h index 46eb0435c91..dbb22c204d9 100644 --- a/intern/cycles/integrator/path_trace.h +++ b/intern/cycles/integrator/path_trace.h @@ -37,6 +37,7 @@ class RenderBuffers; class RenderScheduler; class RenderWork; class PathTraceDisplay; +class OutputDriver; class Progress; class TileManager; @@ -99,7 +100,10 @@ class PathTrace { * Use this to configure the adaptive sampler before rendering any samples. */ void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling); - /* Set display driver which takes care of drawing the render result. */ + /* Sets output driver for render buffer output. */ + void set_output_driver(unique_ptr driver); + + /* Set display driver for interactive render buffer display. */ void set_display_driver(unique_ptr driver); /* Clear the display buffer by filling it in with all zeroes. */ @@ -158,6 +162,7 @@ class PathTrace { * instead. */ int2 get_render_tile_size() const; int2 get_render_tile_offset() const; + int2 get_render_size() const; /* Get buffer parameters of the current tile. * @@ -169,18 +174,6 @@ class PathTrace { * times, and so on. */ string full_report() const; - /* Callback which communicates an updates state of the render buffer of the current big tile. - * Is called during path tracing to communicate work-in-progress state of the final buffer. */ - function tile_buffer_update_cb; - - /* Callback which communicates final rendered buffer. Is called after path-tracing is done. */ - function tile_buffer_write_cb; - - /* Callback which initializes rendered buffer. Is called before path-tracing starts. - * - * This is used for baking. */ - function tile_buffer_read_cb; - /* Callback which is called to report current rendering progress. * * It is supposed to be cheaper than buffer update/write, hence can be called more often. @@ -253,8 +246,12 @@ class PathTrace { RenderScheduler &render_scheduler_; TileManager &tile_manager_; + /* Display driver for interactive render buffer display. */ unique_ptr display_; + /* Output driver to write render buffer to. */ + unique_ptr output_driver_; + /* Per-compute device descriptors of work which is responsible for path tracing on its configured * device. */ vector> path_trace_works_; diff --git a/intern/cycles/integrator/path_trace_tile.cpp b/intern/cycles/integrator/path_trace_tile.cpp new file mode 100644 index 00000000000..540f4aa5f68 --- /dev/null +++ b/intern/cycles/integrator/path_trace_tile.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/path_trace_tile.h" +#include "integrator/pass_accessor_cpu.h" +#include "integrator/path_trace.h" + +#include "render/buffers.h" +#include "render/film.h" +#include "render/pass.h" +#include "render/scene.h" + +CCL_NAMESPACE_BEGIN + +PathTraceTile::PathTraceTile(PathTrace &path_trace) + : OutputDriver::Tile(path_trace.get_render_tile_offset(), + path_trace.get_render_tile_size(), + path_trace.get_render_size(), + path_trace.get_render_tile_params().layer, + path_trace.get_render_tile_params().view), + path_trace_(path_trace), + copied_from_device_(false) +{ +} + +bool PathTraceTile::get_pass_pixels(const string_view pass_name, + const int num_channels, + float *pixels) const +{ + /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification + * is happening while this function runs. */ + + if (!copied_from_device_) { + /* Copy from device on demand. */ + path_trace_.copy_render_tile_from_device(); + const_cast(this)->copied_from_device_ = true; + } + + const BufferParams &buffer_params = path_trace_.get_render_tile_params(); + + const BufferPass *pass = buffer_params.find_pass(pass_name); + if (pass == nullptr) { + return false; + } + + const bool has_denoised_result = path_trace_.has_denoised_result(); + if (pass->mode == PassMode::DENOISED && !has_denoised_result) { + pass = buffer_params.find_pass(pass->type); + if (pass == nullptr) { + /* Happens when denoised result pass is requested but is never written by the kernel. */ + return false; + } + } + + pass = buffer_params.get_actual_display_pass(pass); + + const float exposure = buffer_params.exposure; + const int num_samples = path_trace_.get_num_render_tile_samples(); + + PassAccessor::PassAccessInfo pass_access_info(*pass); + pass_access_info.use_approximate_shadow_catcher = buffer_params.use_approximate_shadow_catcher; + pass_access_info.use_approximate_shadow_catcher_background = + pass_access_info.use_approximate_shadow_catcher && !buffer_params.use_transparent_background; + + const PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); + const PassAccessor::Destination destination(pixels, num_channels); + + return path_trace_.get_render_tile_pixels(pass_accessor, destination); +} + +bool PathTraceTile::set_pass_pixels(const string_view pass_name, + const int num_channels, + const float *pixels) const +{ + /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification + * is happening while this function runs. */ + + const BufferParams &buffer_params = path_trace_.get_render_tile_params(); + const BufferPass *pass = buffer_params.find_pass(pass_name); + if (!pass) { + return false; + } + + const float exposure = buffer_params.exposure; + const int num_samples = 1; + + const PassAccessor::PassAccessInfo pass_access_info(*pass); + PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); + PassAccessor::Source source(pixels, num_channels); + + return path_trace_.set_render_tile_pixels(pass_accessor, source); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_tile.h b/intern/cycles/integrator/path_trace_tile.h new file mode 100644 index 00000000000..fd3e2969f6c --- /dev/null +++ b/intern/cycles/integrator/path_trace_tile.h @@ -0,0 +1,43 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "render/output_driver.h" + +CCL_NAMESPACE_BEGIN + +/* PathTraceTile + * + * Implementation of OutputDriver::Tile interface for path tracer. */ + +class PathTrace; + +class PathTraceTile : public OutputDriver::Tile { + public: + PathTraceTile(PathTrace &path_trace); + + bool get_pass_pixels(const string_view pass_name, const int num_channels, float *pixels) const; + bool set_pass_pixels(const string_view pass_name, + const int num_channels, + const float *pixels) const; + + private: + PathTrace &path_trace_; + bool copied_from_device_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index ce1a9e5f430..323222b8c85 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -78,6 +78,7 @@ set(SRC_HEADERS constant_fold.h denoising.h display_driver.h + output_driver.h film.h geometry.h graph.h diff --git a/intern/cycles/render/output_driver.h b/intern/cycles/render/output_driver.h new file mode 100644 index 00000000000..b7e980d71d4 --- /dev/null +++ b/intern/cycles/render/output_driver.h @@ -0,0 +1,82 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/util_math.h" +#include "util/util_string.h" +#include "util/util_types.h" + +CCL_NAMESPACE_BEGIN + +/* Output driver for reading render buffers. + * + * Host applications implement this interface for outputting render buffers for offline rendering. + * Drivers can be used to copy the buffers into the host application or write them directly to + * disk. This interface may also be used for interactive display, however the DisplayDriver is more + * efficient for that purpose. + */ +class OutputDriver { + public: + OutputDriver() = default; + virtual ~OutputDriver() = default; + + class Tile { + public: + Tile(const int2 offset, + const int2 size, + const int2 full_size, + const string_view layer, + const string_view view) + : offset(offset), size(size), full_size(full_size), layer(layer), view(view) + { + } + virtual ~Tile() = default; + + const int2 offset; + const int2 size; + const int2 full_size; + const string layer; + const string view; + + virtual bool get_pass_pixels(const string_view pass_name, + const int num_channels, + float *pixels) const = 0; + virtual bool set_pass_pixels(const string_view pass_name, + const int num_channels, + const float *pixels) const = 0; + }; + + /* Write tile once it has finished rendering. */ + virtual void write_render_tile(const Tile &tile) = 0; + + /* Update tile while rendering is in progress. Return true if any update + * was performed. */ + virtual bool update_render_tile(const Tile & /* tile */) + { + return false; + } + + /* For baking, read render pass PASS_BAKE_PRIMITIVE and PASS_BAKE_DIFFERENTIAL + * to determine which shading points to use for baking at each pixel. Return + * true if any data was read. */ + virtual bool read_render_tile(const Tile & /* tile */) + { + return false; + } +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index c191b9a9b4a..550188b196a 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -31,6 +31,7 @@ #include "render/light.h" #include "render/mesh.h" #include "render/object.h" +#include "render/output_driver.h" #include "render/scene.h" #include "render/session.h" @@ -64,25 +65,6 @@ Session::Session(const SessionParams ¶ms_, const SceneParams &scene_params) path_trace_ = make_unique( device, scene->film, &scene->dscene, render_scheduler_, tile_manager_); path_trace_->set_progress(&progress); - path_trace_->tile_buffer_update_cb = [&]() { - if (!update_render_tile_cb) { - return; - } - update_render_tile_cb(); - }; - path_trace_->tile_buffer_write_cb = [&]() { - if (!write_render_tile_cb) { - return; - } - write_render_tile_cb(); - }; - path_trace_->tile_buffer_read_cb = [&]() -> bool { - if (!read_render_tile_cb) { - return false; - } - read_render_tile_cb(); - return true; - }; path_trace_->progress_update_cb = [&]() { update_status_time(); }; tile_manager_.full_buffer_written_cb = [&](string_view filename) { @@ -97,24 +79,6 @@ Session::~Session() { cancel(); - /* TODO(sergey): Bring the passes in viewport back. - * It is unclear why there is such an exception needed though. */ -#if 0 - if (buffers && params.write_render_cb) { - /* Copy to display buffer and write out image if requested */ - delete display; - - display = new DisplayBuffer(device, false); - display->reset(buffers->params); - copy_to_display_buffer(params.samples); - - int w = display->draw_width; - int h = display->draw_height; - uchar4 *pixels = display->rgba_byte.copy_from_device(0, w, h); - params.write_render_cb((uchar *)pixels, w, h, 4); - } -#endif - /* Make sure path tracer is destroyed before the device. This is needed because destruction might * need to access device for device memory free. */ /* TODO(sergey): Convert device to be unique_ptr, and rely on C++ to destruct objects in the @@ -514,6 +478,11 @@ void Session::set_pause(bool pause) } } +void Session::set_output_driver(unique_ptr driver) +{ + path_trace_->set_output_driver(move(driver)); +} + void Session::set_display_driver(unique_ptr driver) { path_trace_->set_display_driver(move(driver)); @@ -636,101 +605,6 @@ void Session::collect_statistics(RenderStats *render_stats) } } -/* -------------------------------------------------------------------- - * Tile and tile pixels access. - */ - -bool Session::has_multiple_render_tiles() const -{ - return tile_manager_.has_multiple_tiles(); -} - -int2 Session::get_render_tile_size() const -{ - return path_trace_->get_render_tile_size(); -} - -int2 Session::get_render_tile_offset() const -{ - return path_trace_->get_render_tile_offset(); -} - -string_view Session::get_render_tile_layer() const -{ - const BufferParams &buffer_params = path_trace_->get_render_tile_params(); - return buffer_params.layer; -} - -string_view Session::get_render_tile_view() const -{ - const BufferParams &buffer_params = path_trace_->get_render_tile_params(); - return buffer_params.view; -} - -bool Session::copy_render_tile_from_device() -{ - return path_trace_->copy_render_tile_from_device(); -} - -bool Session::get_render_tile_pixels(const string &pass_name, int num_components, float *pixels) -{ - /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification - * is happening while this function runs. */ - - const BufferParams &buffer_params = path_trace_->get_render_tile_params(); - - const BufferPass *pass = buffer_params.find_pass(pass_name); - if (pass == nullptr) { - return false; - } - - const bool has_denoised_result = path_trace_->has_denoised_result(); - if (pass->mode == PassMode::DENOISED && !has_denoised_result) { - pass = buffer_params.find_pass(pass->type); - if (pass == nullptr) { - /* Happens when denoised result pass is requested but is never written by the kernel. */ - return false; - } - } - - pass = buffer_params.get_actual_display_pass(pass); - - const float exposure = buffer_params.exposure; - const int num_samples = path_trace_->get_num_render_tile_samples(); - - PassAccessor::PassAccessInfo pass_access_info(*pass); - pass_access_info.use_approximate_shadow_catcher = buffer_params.use_approximate_shadow_catcher; - pass_access_info.use_approximate_shadow_catcher_background = - pass_access_info.use_approximate_shadow_catcher && !buffer_params.use_transparent_background; - - const PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); - const PassAccessor::Destination destination(pixels, num_components); - - return path_trace_->get_render_tile_pixels(pass_accessor, destination); -} - -bool Session::set_render_tile_pixels(const string &pass_name, - int num_components, - const float *pixels) -{ - /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification - * is happening while this function runs. */ - - const BufferPass *pass = buffer_params_.find_pass(pass_name); - if (!pass) { - return false; - } - - const float exposure = scene->film->get_exposure(); - const int num_samples = render_scheduler_.get_num_rendered_samples(); - - const PassAccessor::PassAccessInfo pass_access_info(*pass); - PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); - PassAccessor::Source source(pixels, num_components); - - return path_trace_->set_render_tile_pixels(pass_accessor, source); -} - /* -------------------------------------------------------------------- * Full-frame on-disk storage. */ diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index 607e40c47c1..46c964bc98c 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -36,6 +36,7 @@ class BufferParams; class Device; class DeviceScene; class DisplayDriver; +class OutputDriver; class PathTrace; class Progress; class RenderBuffers; @@ -67,8 +68,6 @@ class SessionParams { ShadingSystem shadingsystem; - function write_render_cb; - SessionParams() { headless = false; @@ -114,10 +113,6 @@ class Session { Stats stats; Profiler profiler; - function write_render_tile_cb; - function update_render_tile_cb; - function read_render_tile_cb; - /* Callback is invoked by tile manager whenever on-dist tiles storage file is closed after * writing. Allows an engine integration to keep track of those files without worry about * transferring the information when it needs to re-create session during rendering. */ @@ -143,6 +138,7 @@ class Session { void set_samples(int samples); void set_time_limit(double time_limit); + void set_output_driver(unique_ptr driver); void set_display_driver(unique_ptr driver); double get_estimated_remaining_time() const; @@ -155,24 +151,6 @@ class Session { void collect_statistics(RenderStats *stats); - /* -------------------------------------------------------------------- - * Tile and tile pixels access. - */ - - bool has_multiple_render_tiles() const; - - /* Get size and offset (relative to the buffer's full x/y) of the currently rendering tile. */ - int2 get_render_tile_size() const; - int2 get_render_tile_offset() const; - - string_view get_render_tile_layer() const; - string_view get_render_tile_view() const; - - bool copy_render_tile_from_device(); - - bool get_render_tile_pixels(const string &pass_name, int num_components, float *pixels); - bool set_render_tile_pixels(const string &pass_name, int num_components, const float *pixels); - /* -------------------------------------------------------------------- * Full-frame on-disk storage. */ diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 4ab2e856c5d..7e53a9d0911 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -420,6 +420,11 @@ const Tile &TileManager::get_current_tile() const return tile_state_.current_tile; } +const int2 TileManager::get_size() const +{ + return make_int2(buffer_params_.width, buffer_params_.height); +} + bool TileManager::open_tile_output() { write_state_.filename = path_temp_get("cycles-tile-buffer-" + tile_file_unique_part_ + "-" + diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h index 8392274ff79..08eaa4034f0 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/render/tile.h @@ -82,6 +82,7 @@ class TileManager { bool done(); const Tile &get_current_tile() const; + const int2 get_size() const; /* Write render buffer of a tile to a file on disk. * From 8d60ac2bb0f3833e1ab1c9a5b70595a5d416b9e3 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 30 Sep 2021 14:01:56 -0500 Subject: [PATCH 0404/1500] Cleanup: Fix unused variable warning --- source/blender/nodes/shader/nodes/node_shader_curves.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index 253bb3cc4b6..8657d9e517d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -375,7 +375,7 @@ static void node_shader_exec_curve_float(void *UNUSED(data), } } -static void node_shader_init_curve_float(bNodeTree *ntree, bNode *node) +static void node_shader_init_curve_float(bNodeTree *UNUSED(ntree), bNode *node) { node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); } From 213554f24a17ea5545b8603fefcda0198297b452 Mon Sep 17 00:00:00 2001 From: Josef Raschen Date: Thu, 30 Sep 2021 21:09:47 +0200 Subject: [PATCH 0405/1500] VSE: Add ASC CDL color correction method Add Offset/Slope/Power controls to the color balance modifier. This is already available in compositor. Reviewed By: sergey, ISS Differential Revision: https://developer.blender.org/D12575 --- .../scripts/startup/bl_ui/space_sequencer.py | 101 +++++++++++----- source/blender/makesdna/DNA_sequence_types.h | 12 ++ .../blender/makesrna/intern/rna_sequencer.c | 61 +++++++++- source/blender/sequencer/intern/modifier.c | 112 ++++++++++++++++-- 4 files changed, 241 insertions(+), 45 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 7b102604587..455f5e2d841 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -46,44 +46,85 @@ def selected_sequences_len(context): def draw_color_balance(layout, color_balance): + layout.prop(color_balance, "correction_method") + layout.use_property_split = False flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - col = flow.column() - box = col.box() - split = box.split(factor=0.35) - col = split.column(align=True) - col.label(text="Lift:") - col.separator() - col.separator() - col.prop(color_balance, "lift", text="") - col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT') - split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True) + if color_balance.correction_method == 'LIFT_GAMMA_GAIN': + col = flow.column() - col = flow.column() + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Lift:") + col.separator() + col.separator() + col.prop(color_balance, "lift", text="") + col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True) - box = col.box() - split = box.split(factor=0.35) - col = split.column(align=True) - col.label(text="Gamma:") - col.separator() - col.separator() - col.prop(color_balance, "gamma", text="") - col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT') - split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True) + col = flow.column() - col = flow.column() + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Gamma:") + col.separator() + col.separator() + col.prop(color_balance, "gamma", text="") + col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True) - box = col.box() - split = box.split(factor=0.35) - col = split.column(align=True) - col.label(text="Gain:") - col.separator() - col.separator() - col.prop(color_balance, "gain", text="") - col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT') - split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True) + col = flow.column() + + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Gain:") + col.separator() + col.separator() + col.prop(color_balance, "gain", text="") + col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True) + + elif color_balance.correction_method == 'OFFSET_POWER_SLOPE': + col = flow.column() + + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Offset:") + col.separator() + col.separator() + col.prop(color_balance, "offset", text="") + col.prop(color_balance, "invert_offset", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "offset", value_slider=True, cubic=True) + + col = flow.column() + + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Power:") + col.separator() + col.separator() + col.prop(color_balance, "power", text="") + col.prop(color_balance, "invert_power", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "power", value_slider=True, cubic=True) + + col = flow.column() + + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Slope:") + col.separator() + col.separator() + col.prop(color_balance, "slope", text="") + col.prop(color_balance, "invert_slope", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "slope", value_slider=True, cubic=True) class SEQUENCER_PT_active_tool(ToolActivePanelHelper, Panel): diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 13bfa82b36d..a71f86eae9f 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -79,9 +79,13 @@ typedef struct StripTransform { } StripTransform; typedef struct StripColorBalance { + int method; float lift[3]; float gamma[3]; float gain[3]; + float slope[3]; + float offset[3]; + float power[3]; int flag; char _pad[4]; /* float exposure; */ @@ -434,6 +438,11 @@ typedef struct ColorBalanceModifierData { float color_multiply; } ColorBalanceModifierData; +enum { + SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN = 0, + SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER = 1, +}; + typedef struct CurvesModifierData { SequenceModifierData modifier; @@ -571,6 +580,9 @@ enum { #define SEQ_COLOR_BALANCE_INVERSE_GAIN 1 #define SEQ_COLOR_BALANCE_INVERSE_GAMMA 2 #define SEQ_COLOR_BALANCE_INVERSE_LIFT 4 +#define SEQ_COLOR_BALANCE_INVERSE_SLOPE 8 +#define SEQ_COLOR_BALANCE_INVERSE_OFFSET 16 +#define SEQ_COLOR_BALANCE_INVERSE_POWER 32 /* !!! has to be same as IMB_imbuf.h IMB_PROXY_... and IMB_TC_... */ diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 7303f6c920a..e519740259c 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1591,12 +1591,28 @@ static void rna_def_color_balance(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + static const EnumPropertyItem method_items[] = { + {SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN, "LIFT_GAMMA_GAIN", 0, "Lift/Gamma/Gain", ""}, + {SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER, + "OFFSET_POWER_SLOPE", + 0, + "Offset/Power/Slope (ASC-CDL)", + "ASC-CDL standard color correction"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "SequenceColorBalanceData", NULL); RNA_def_struct_ui_text(srna, "Sequence Color Balance Data", "Color balance parameters for a sequence strip and its modifiers"); RNA_def_struct_sdna(srna, "StripColorBalance"); + prop = RNA_def_property(srna, "correction_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "method"); + RNA_def_property_enum_items(prop, method_items); + RNA_def_property_ui_text(prop, "Correction Method", ""); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + prop = RNA_def_property(srna, "lift", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_ui_text(prop, "Lift", "Color balance lift (shadows)"); RNA_def_property_ui_range(prop, 0, 2, 0.1, 3); @@ -1615,9 +1631,27 @@ static void rna_def_color_balance(BlenderRNA *brna) RNA_def_property_float_default(prop, 1.0f); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); - prop = RNA_def_property(srna, "invert_gain", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAIN); - RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color`"); + prop = RNA_def_property(srna, "slope", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_ui_text(prop, "Slope", "Correction for highlights"); + RNA_def_property_ui_range(prop, 0, 2, 0.1, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_ui_text(prop, "Offset", "Correction for entire tonal range"); + RNA_def_property_ui_range(prop, 0, 2, 0.1, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "power", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_ui_text(prop, "Power", "Correction for midtones"); + RNA_def_property_ui_range(prop, 0, 2, 0.1, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "invert_lift", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_LIFT); + RNA_def_property_ui_text(prop, "Inverse Lift", "Invert the lift color"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); prop = RNA_def_property(srna, "invert_gamma", PROP_BOOLEAN, PROP_NONE); @@ -1625,9 +1659,24 @@ static void rna_def_color_balance(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Inverse Gamma", "Invert the gamma color"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); - prop = RNA_def_property(srna, "invert_lift", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_LIFT); - RNA_def_property_ui_text(prop, "Inverse Lift", "Invert the lift color"); + prop = RNA_def_property(srna, "invert_gain", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAIN); + RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color`"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "invert_slope", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_SLOPE); + RNA_def_property_ui_text(prop, "Inverse Slope", "Invert the slope color`"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "invert_offset", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_OFFSET); + RNA_def_property_ui_text(prop, "Inverse Offset", "Invert the offset color"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "invert_power", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_POWER); + RNA_def_property_ui_text(prop, "Inverse Power", "Invert the power color"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); /* not yet used */ diff --git a/source/blender/sequencer/intern/modifier.c b/source/blender/sequencer/intern/modifier.c index 07d09f4ae17..8414599b225 100644 --- a/source/blender/sequencer/intern/modifier.c +++ b/source/blender/sequencer/intern/modifier.c @@ -216,7 +216,7 @@ static void modifier_apply_threaded(ImBuf *ibuf, /** \name Color Balance Modifier * \{ */ -static StripColorBalance calc_cb(StripColorBalance *cb_) +static StripColorBalance calc_cb_lgg(StripColorBalance *cb_) { StripColorBalance cb = *cb_; int c; @@ -262,8 +262,53 @@ static StripColorBalance calc_cb(StripColorBalance *cb_) return cb; } +static StripColorBalance calc_cb_sop(StripColorBalance *cb_) +{ + StripColorBalance cb = *cb_; + int c; + + for (c = 0; c < 3; c++) { + if (cb.flag & SEQ_COLOR_BALANCE_INVERSE_SLOPE) { + if (cb.slope[c] != 0.0f) { + cb.slope[c] = 1.0f / cb.slope[c]; + } + else { + cb.slope[c] = 1000000; + } + } + + if (cb.flag & SEQ_COLOR_BALANCE_INVERSE_OFFSET) { + cb.offset[c] = -1.0f * (cb.offset[c] - 1.0f); + } + else { + cb.offset[c] = cb.offset[c] - 1.0f; + } + + if (!(cb.flag & SEQ_COLOR_BALANCE_INVERSE_POWER)) { + if (cb.power[c] != 0.0f) { + cb.power[c] = 1.0f / cb.power[c]; + } + else { + cb.power[c] = 1000000; + } + } + } + + return cb; +} + +static StripColorBalance calc_cb(StripColorBalance *cb_) +{ + if (cb_->method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { + return calc_cb_lgg(cb_); + } + else { /* cb_->method == SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER */ + return calc_cb_sop(cb_); + } +} + /* NOTE: lift is actually 2-lift. */ -MINLINE float color_balance_fl( +MINLINE float color_balance_fl_lgg( float in, const float lift, const float gain, const float gamma, const float mul) { float x = (((in - 1.0f) * lift) + 1.0f) * gain; @@ -278,12 +323,40 @@ MINLINE float color_balance_fl( return x; } -static void make_cb_table_float(float lift, float gain, float gamma, float *table, float mul) +MINLINE float color_balance_fl_sop(float in, + const float slope, + const float offset, + const float power, + const float pivot, + float mul) { - int y; + float x = in * slope + offset; - for (y = 0; y < 256; y++) { - float v = color_balance_fl((float)y * (1.0f / 255.0f), lift, gain, gamma, mul); + /* prevent NaN */ + if (x < 0.0f) { + x = 0.0f; + } + + x = powf(x / pivot, power) * pivot; + x *= mul; + CLAMP(x, FLT_MIN, FLT_MAX); + return x; +} + +static void make_cb_table_float_lgg(float lift, float gain, float gamma, float *table, float mul) +{ + for (int y = 0; y < 256; y++) { + float v = color_balance_fl_lgg((float)y * (1.0f / 255.0f), lift, gain, gamma, mul); + + table[y] = v; + } +} + +static void make_cb_table_float_sop( + float slope, float offset, float power, float pivot, float *table, float mul) +{ + for (int y = 0; y < 256; y++) { + float v = color_balance_fl_sop((float)y * (1.0f / 255.0f), slope, offset, power, pivot, mul); table[y] = v; } @@ -310,7 +383,13 @@ static void color_balance_byte_byte(StripColorBalance *cb_, straight_uchar_to_premul_float(p, cp); for (c = 0; c < 3; c++) { - float t = color_balance_fl(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul); + float t; + if (cb.method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { + t = color_balance_fl_lgg(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul); + } + else { + t = color_balance_fl_sop(p[c], cb.slope[c], cb.offset[c], cb.power[c], 1.0, mul); + } if (m) { float m_normal = (float)m[c] / 255.0f; @@ -352,7 +431,12 @@ static void color_balance_byte_float(StripColorBalance *cb_, cb = calc_cb(cb_); for (c = 0; c < 3; c++) { - make_cb_table_float(cb.lift[c], cb.gain[c], cb.gamma[c], cb_tab[c], mul); + if (cb.method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { + make_cb_table_float_lgg(cb.lift[c], cb.gain[c], cb.gamma[c], cb_tab[c], mul); + } + else { + make_cb_table_float_sop(cb.slope[c], cb.offset[c], cb.power[c], 1.0, cb_tab[c], mul); + } } for (i = 0; i < 256; i++) { @@ -397,7 +481,13 @@ static void color_balance_float_float(StripColorBalance *cb_, while (p < e) { int c; for (c = 0; c < 3; c++) { - float t = color_balance_fl(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul); + float t; + if (cb_->method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { + t = color_balance_fl_lgg(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul); + } + else { + t = color_balance_fl_sop(p[c], cb.slope[c], cb.offset[c], cb.power[c], 1.0, mul); + } if (m) { p[c] = p[c] * (1.0f - m[c]) + t * m[c]; @@ -507,11 +597,15 @@ static void colorBalance_init_data(SequenceModifierData *smd) int c; cbmd->color_multiply = 1.0f; + cbmd->color_balance.method = 0; for (c = 0; c < 3; c++) { cbmd->color_balance.lift[c] = 1.0f; cbmd->color_balance.gamma[c] = 1.0f; cbmd->color_balance.gain[c] = 1.0f; + cbmd->color_balance.slope[c] = 1.0f; + cbmd->color_balance.offset[c] = 1.0f; + cbmd->color_balance.power[c] = 1.0f; } } From 33dc584b371211428130596d99559a271c25bc66 Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Thu, 30 Sep 2021 21:11:09 +0200 Subject: [PATCH 0406/1500] Fix T91285: Bad tooltip for VSE Slip operator This patch is created to change the tooltip for Slip Strip Contents As per the present info, only active strip will be affected. But in reality selected strips can be trimmed with this operator. Word Trim changed to Slip in tooltip Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D12450 --- source/blender/editors/space_sequencer/sequencer_edit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 6daa8d690e5..9be947b9112 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -870,9 +870,9 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even void SEQUENCER_OT_slip(struct wmOperatorType *ot) { /* Identifiers. */ - ot->name = "Trim Strips"; + ot->name = "Slip Strips"; ot->idname = "SEQUENCER_OT_slip"; - ot->description = "Trim the contents of the active strip"; + ot->description = "Slip the contents of selected strips"; /* Api callbacks. */ ot->invoke = sequencer_slip_invoke; From 4569d9c0c3b046a412d54cc008d9d2a4be909ee1 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Wed, 29 Sep 2021 21:27:26 +0200 Subject: [PATCH 0407/1500] Compositor: Fix Movie Distortion node rendering an empty image Input area of interest calculation was incorrect because `m_margin` was uninitialized. Only "Full Frame" mode was affected. --- .../COM_MovieDistortionOperation.cc | 19 ++++++++++++++----- .../operations/COM_MovieDistortionOperation.h | 1 + 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc index 72162ffb110..49f43d2c1a7 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc @@ -35,9 +35,8 @@ MovieDistortionOperation::MovieDistortionOperation(bool distortion) this->m_apply = distortion; } -void MovieDistortionOperation::initExecution() +void MovieDistortionOperation::init_data() { - this->m_inputOperation = this->getInputSocketReader(0); if (this->m_movieClip) { MovieTracking *tracking = &this->m_movieClip->tracking; MovieClipUser clipUser = {0}; @@ -60,15 +59,25 @@ void MovieDistortionOperation::initExecution() m_margin[0] = delta[0] + 5; m_margin[1] = delta[1] + 5; - this->m_distortion = BKE_tracking_distortion_new( - tracking, calibration_width, calibration_height); this->m_calibration_width = calibration_width; this->m_calibration_height = calibration_height; this->m_pixel_aspect = tracking->camera.pixel_aspect; } else { m_margin[0] = m_margin[1] = 0; - this->m_distortion = nullptr; + } +} + +void MovieDistortionOperation::initExecution() +{ + m_inputOperation = this->getInputSocketReader(0); + if (m_movieClip) { + MovieTracking *tracking = &m_movieClip->tracking; + m_distortion = BKE_tracking_distortion_new( + tracking, m_calibration_width, m_calibration_height); + } + else { + m_distortion = nullptr; } } diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h index 69c2f9c269c..abf0553b75b 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h @@ -44,6 +44,7 @@ class MovieDistortionOperation : public MultiThreadedOperation { MovieDistortionOperation(bool distortion); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_data() override; void initExecution() override; void deinitExecution() override; From f3274bfa70f0bb24e8f4d8cdd8393babddbf986c Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Wed, 29 Sep 2021 21:42:46 +0200 Subject: [PATCH 0408/1500] Compositor: Fix Dilate/Erode node crash with Step option It was writing the buffer out of bounds. Only "Full Frame" mode was affected. --- .../blender/compositor/operations/COM_DilateErodeOperation.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index 28b40021cd9..b7fd714ba5b 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -783,7 +783,8 @@ static void step_update_memory_buffer(MemoryBuffer *output, start = half_window + (i - 1) * window + 1; for (int y = -MIN2(0, start); y < window - MAX2(0, start + window - bheight); y++) { - result.get_value(x, y + start + area.ymin, 0) = selector(temp[y], temp[y + window - 1]); + result.get_value(x + area.xmin, y + start + area.ymin, 0) = selector(temp[y], + temp[y + window - 1]); } } } From e2df5c8a56c06acde486d8a9094d41671ec09398 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Thu, 30 Sep 2021 21:35:32 +0200 Subject: [PATCH 0409/1500] Compositor: Fix Flip node not flipping translation on Full Frame To match tiled implementation, flip center should not be translated when canvas has offset. Instead the canvas offset needs to be flipped. --- .../operations/COM_FlipOperation.cc | 28 ++++++++++++++++--- .../compositor/operations/COM_FlipOperation.h | 1 + 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc index c88fcaa7da2..2d8865e41e0 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.cc +++ b/source/blender/compositor/operations/COM_FlipOperation.cc @@ -22,7 +22,7 @@ namespace blender::compositor { FlipOperation::FlipOperation() { - this->addInputSocket(DataType::Color); + this->addInputSocket(DataType::Color, ResizeMode::None); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); this->m_inputOperation = nullptr; @@ -75,6 +75,24 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void FlipOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + NodeOperation::determine_canvas(preferred_area, r_area); + if (execution_model_ == eExecutionModel::FullFrame) { + rcti input_area = r_area; + if (m_flipX) { + const int width = BLI_rcti_size_x(&input_area) - 1; + r_area.xmax = (width - input_area.xmin) + 1; + r_area.xmin = (width - input_area.xmax) + 1; + } + if (m_flipY) { + const int height = BLI_rcti_size_y(&input_area) - 1; + r_area.ymax = (height - input_area.ymin) + 1; + r_area.ymin = (height - input_area.ymax) + 1; + } + } +} + void FlipOperation::get_area_of_interest(const int input_idx, const rcti &output_area, rcti &r_input_area) @@ -84,7 +102,7 @@ void FlipOperation::get_area_of_interest(const int input_idx, if (this->m_flipX) { const int w = (int)this->getWidth() - 1; r_input_area.xmax = (w - output_area.xmin) + 1; - r_input_area.xmin = (w - output_area.xmax) - 1; + r_input_area.xmin = (w - output_area.xmax) + 1; } else { r_input_area.xmin = output_area.xmin; @@ -93,7 +111,7 @@ void FlipOperation::get_area_of_interest(const int input_idx, if (this->m_flipY) { const int h = (int)this->getHeight() - 1; r_input_area.ymax = (h - output_area.ymin) + 1; - r_input_area.ymin = (h - output_area.ymax) - 1; + r_input_area.ymin = (h - output_area.ymax) + 1; } else { r_input_area.ymin = output_area.ymin; @@ -106,10 +124,12 @@ void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { const MemoryBuffer *input_img = inputs[0]; + const int input_offset_x = input_img->get_rect().xmin; + const int input_offset_y = input_img->get_rect().ymin; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x; const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y; - input_img->read_elem(nx, ny, it.out); + input_img->read_elem(input_offset_x + nx, input_offset_y + ny, it.out); } } diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h index dba7f82c341..963996a5b0d 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.h +++ b/source/blender/compositor/operations/COM_FlipOperation.h @@ -46,6 +46,7 @@ class FlipOperation : public MultiThreadedOperation { this->m_flipY = flipY; } + void determine_canvas(const rcti &preferred_area, rcti &r_area) 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, From 66fe1c79f3ce225da5d237ac1371e26920c9cb56 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Thu, 30 Sep 2021 22:30:25 +0200 Subject: [PATCH 0410/1500] Compositor: Fix Composite node using alpha when "Use Alpha" is off Alpha input was not receiving the final composite canvas as preferred causing a Translate operation being inserted for centering. This resulted in a transparent background. The issue only affects Full Frame mode. --- .../compositor/operations/COM_CompositorOperation.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index 52bc9ed6c2f..f7466b5db34 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -256,8 +256,16 @@ void CompositorOperation::determine_canvas(const rcti &UNUSED(preferred_area), r rcti local_preferred; BLI_rcti_init(&local_preferred, 0, width, 0, height); - NodeOperation::determine_canvas(local_preferred, r_area); - r_area = local_preferred; + switch (execution_model_) { + case eExecutionModel::Tiled: + NodeOperation::determine_canvas(local_preferred, r_area); + r_area = local_preferred; + break; + case eExecutionModel::FullFrame: + set_determined_canvas_modifier([&](rcti &canvas) { canvas = local_preferred; }); + NodeOperation::determine_canvas(local_preferred, r_area); + break; + } } } // namespace blender::compositor From 3a59ddb2927cc5b507fd5f071c6c1831dce743c4 Mon Sep 17 00:00:00 2001 From: Vitor Boschi Date: Thu, 30 Sep 2021 18:27:58 -0500 Subject: [PATCH 0411/1500] Fix: Incorrect warning in curve to mesh node with instances The node was setting a warning when used with instances on input, even though it worked fine. Differential Revision: https://developer.blender.org/D12718 --- .../geometry/nodes/node_geo_curve_to_mesh.cc | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index b123820989f..9b4b6cdcd0c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -33,17 +33,8 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) } static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, - const GeometrySet &profile_set, - const GeoNodeExecParams ¶ms) + const GeometrySet &profile_set) { - if (!geometry_set.has_curve()) { - if (!geometry_set.is_empty()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("No curve data available in curve input")); - } - return; - } - const CurveEval *profile_curve = profile_set.get_curve_for_read(); if (profile_curve == nullptr) { @@ -73,11 +64,20 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) TIP_("No curve data available in the profile input")); } + bool has_curve = false; curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_curve_to_mesh(geometry_set, profile_set, params); + if (geometry_set.has_curve()) { + has_curve = true; + geometry_set_curve_to_mesh(geometry_set, profile_set); + } geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); }); + if (!has_curve && !curve_set.is_empty()) { + params.error_message_add(NodeWarningType::Warning, + TIP_("No curve data available in curve input")); + } + params.set_output("Mesh", std::move(curve_set)); } From 4485dc483cf0c0cea7f9c39e327ce1d036906fdf Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 1 Oct 2021 10:42:33 +1000 Subject: [PATCH 0412/1500] Cleanup: use C-style comments, nullptr for C++ Minor changes extracted from D6408 --- source/blender/blendthumb/src/Dll.cpp | 87 +++++++++++++++------------ 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/source/blender/blendthumb/src/Dll.cpp b/source/blender/blendthumb/src/Dll.cpp index 6516540034e..7f10777f884 100644 --- a/source/blender/blendthumb/src/Dll.cpp +++ b/source/blender/blendthumb/src/Dll.cpp @@ -14,11 +14,17 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/** \file + * \ingroup blendthumb + * + * Thumbnail from Blend file extraction for MS-Windows (DLL). + */ + #include #include -#include // For SHChangeNotify +#include /* For #SHChangeNotify */ #include -#include // For IThumbnailProvider. +#include /* For IThumbnailProvider */ extern HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv); @@ -33,16 +39,16 @@ struct CLASS_OBJECT_INIT { PFNCREATEINSTANCE pfnCreate; }; -// add classes supported by this module here +/* Add classes supported by this module here. */ const CLASS_OBJECT_INIT c_rgClassObjectInit[] = { {&CLSID_BlendThumbHandler, CBlendThumb_CreateInstance}}; long g_cRefModule = 0; -// Handle the DLL's module -HINSTANCE g_hInst = NULL; +/** Handle the DLL's module */ +HINSTANCE g_hInst = nullptr; -// Standard DLL functions +/** Standard DLL functions. */ STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *) { if (dwReason == DLL_PROCESS_ATTACH) { @@ -54,7 +60,7 @@ STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *) STDAPI DllCanUnloadNow() { - // Only allow the DLL to be unloaded after all outstanding references have been released + /* Only allow the DLL to be unloaded after all outstanding references have been released. */ return (g_cRefModule == 0) ? S_OK : S_FALSE; } @@ -76,7 +82,7 @@ class CClassFactory : public IClassFactory { REFIID riid, void **ppv) { - *ppv = NULL; + *ppv = nullptr; HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; for (size_t i = 0; i < cClassObjectInits; i++) { if (clsid == *pClassObjectInits[i].pClsid) { @@ -87,7 +93,8 @@ class CClassFactory : public IClassFactory { hr = pClassFactory->QueryInterface(riid, ppv); pClassFactory->Release(); } - break; // match found + /* Match found. */ + break; } } return hr; @@ -98,7 +105,7 @@ class CClassFactory : public IClassFactory { DllAddRef(); } - // IUnknown + /** #IUnknown */ IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = {QITABENT(CClassFactory, IClassFactory), {0}}; @@ -119,7 +126,7 @@ class CClassFactory : public IClassFactory { return cRef; } - // IClassFactory + /** #IClassFactory */ IFACEMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { return punkOuter ? CLASS_E_NOAGGREGATION : _pfnCreate(riid, ppv); @@ -152,33 +159,37 @@ STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **ppv) clsid, c_rgClassObjectInit, ARRAYSIZE(c_rgClassObjectInit), riid, ppv); } -// A struct to hold the information required for a registry entry - +/** + * A struct to hold the information required for a registry entry. + */ struct REGISTRY_ENTRY { HKEY hkeyRoot; PCWSTR pszKeyName; PCWSTR pszValueName; DWORD dwValueType; - PCWSTR pszData; // These two fields could/should have been a union, but C++ - DWORD dwData; // only lets you initialize the first field in a union. + /** These two fields could/should have been a union, but C++ */ + PCWSTR pszData; + /** Only lets you initialize the first field in a union. */ + DWORD dwData; }; -// Creates a registry key (if needed) and sets the default value of the key - +/** + * Creates a registry key (if needed) and sets the default value of the key. + */ HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry) { HKEY hKey; HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(pRegistryEntry->hkeyRoot, pRegistryEntry->pszKeyName, 0, - NULL, + nullptr, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, - NULL, + nullptr, &hKey, - NULL)); + nullptr)); if (SUCCEEDED(hr)) { - // All this just to support REG_DWORD... + /* All this just to support #REG_DWORD. */ DWORD size; DWORD data; BYTE *lpData = (LPBYTE)pRegistryEntry->pszData; @@ -202,9 +213,9 @@ HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry) return hr; } -// -// Registers this COM server -// +/** + * Registers this COM server. + */ STDAPI DllRegisterServer() { HRESULT hr; @@ -216,15 +227,15 @@ STDAPI DllRegisterServer() } else { const REGISTRY_ENTRY rgRegistryEntries[] = { - // RootKey KeyName ValueName ValueType Data + /* `RootKey KeyName ValueName ValueType Data` */ {HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER, - NULL, + nullptr, REG_SZ, SZ_BLENDTHUMBHANDLER}, {HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER L"\\InProcServer32", - NULL, + nullptr, REG_SZ, szModuleName}, {HKEY_CURRENT_USER, @@ -237,10 +248,10 @@ STDAPI DllRegisterServer() L"Treatment", REG_DWORD, 0, - 0}, // doesn't appear to do anything... + 0}, /* This doesn't appear to do anything. */ {HKEY_CURRENT_USER, L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}", - NULL, + nullptr, REG_SZ, SZ_CLSID_BLENDTHUMBHANDLER}, }; @@ -251,17 +262,17 @@ STDAPI DllRegisterServer() } } if (SUCCEEDED(hr)) { - // This tells the shell to invalidate the thumbnail cache. This is important because any - // .blend files viewed before registering this handler would otherwise show cached blank - // thumbnails. - SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); + /* This tells the shell to invalidate the thumbnail cache. + * This is important because any `.blend` files viewed before registering this handler + * would otherwise show cached blank thumbnails. */ + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); } return hr; } -// -// Unregisters this COM server -// +/** + * Unregisters this COM server + */ STDAPI DllUnregisterServer() { HRESULT hr = S_OK; @@ -270,11 +281,11 @@ STDAPI DllUnregisterServer() L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER, L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}"}; - // Delete the registry entries + /* Delete the registry entries. */ for (int i = 0; i < ARRAYSIZE(rgpszKeys) && SUCCEEDED(hr); i++) { hr = HRESULT_FROM_WIN32(RegDeleteTreeW(HKEY_CURRENT_USER, rgpszKeys[i])); if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { - // If the registry entry has already been deleted, say S_OK. + /* If the registry entry has already been deleted, say S_OK. */ hr = S_OK; } } From b559fb178e5063c3efd6be6b018ab50c9fd2e0d9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 1 Oct 2021 16:19:12 +1000 Subject: [PATCH 0413/1500] Gizmo: hide 2D gizmos while transforming Hide gizmos in the sequencer & UV editor while transforming. --- source/blender/editors/transform/transform_gizmo_2d.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 0d66db0d7e1..4f6556cd2a2 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -32,6 +32,7 @@ #include "DNA_view3d_types.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_layer.h" #include "RNA_access.h" @@ -70,6 +71,10 @@ static bool gizmo2d_generic_poll(const bContext *C, wmGizmoGroupType *gzgt) return false; } + if (G.moving) { + return false; + } + ScrArea *area = CTX_wm_area(C); switch (area->spacetype) { case SPACE_IMAGE: { From bdb7d262aadb2685f0cb7ef73840e479252e70d4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 1 Oct 2021 16:20:31 +1000 Subject: [PATCH 0414/1500] Cleanup: clang-tidy warnings --- source/blender/blenkernel/intern/asset_catalog_path.cc | 4 +--- source/blender/imbuf/intern/openexr/openexr_stub.cpp | 2 +- source/blender/sequencer/intern/modifier.c | 5 ++--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc index cc6aceef8e7..022bceb133e 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path.cc @@ -38,9 +38,7 @@ AssetCatalogPath::AssetCatalogPath(const char *path) : path_(path) { } -AssetCatalogPath::AssetCatalogPath(const AssetCatalogPath &other_path) : path_(other_path.path_) -{ -} +AssetCatalogPath::AssetCatalogPath(const AssetCatalogPath &other_path) = default; AssetCatalogPath::AssetCatalogPath(AssetCatalogPath &&other_path) noexcept : path_(std::move(other_path.path_)) diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index 639100ac6fe..9b4d6178613 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -49,7 +49,7 @@ bool IMB_exr_begin_read(void * /*handle*/, int * /*height*/, const bool /*add_channels*/) { - return 0; + return false; } bool IMB_exr_begin_write(void * /*handle*/, const char * /*filename*/, diff --git a/source/blender/sequencer/intern/modifier.c b/source/blender/sequencer/intern/modifier.c index 8414599b225..1a63f4c4655 100644 --- a/source/blender/sequencer/intern/modifier.c +++ b/source/blender/sequencer/intern/modifier.c @@ -302,9 +302,8 @@ static StripColorBalance calc_cb(StripColorBalance *cb_) if (cb_->method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { return calc_cb_lgg(cb_); } - else { /* cb_->method == SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER */ - return calc_cb_sop(cb_); - } + /* `cb_->method == SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER`. */ + return calc_cb_sop(cb_); } /* NOTE: lift is actually 2-lift. */ From 2e6c6426d3662302e7a9832d59f4d3bf20bf1ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 1 Oct 2021 10:58:16 +0200 Subject: [PATCH 0415/1500] Asset Catalogs: test that missing catalogs are created once Add asset catalogs test, to ensure missing catalogs are only created once, and not for every originally defined catalog. No functional changes to Blender (the code was already doing the right thing). --- .../blenkernel/intern/asset_catalog_test.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 836b681c950..f3e1f7984a4 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -62,6 +62,17 @@ class TestableAssetCatalogService : public AssetCatalogService { { AssetCatalogService::create_missing_catalogs(); } + + int64_t count_catalogs_with_path(const CatalogFilePath &path) + { + int64_t count = 0; + for (auto &catalog_uptr : catalogs_.values()) { + if (catalog_uptr->path == path) { + count++; + } + } + return count; + } }; class AssetCatalogTest : public testing::Test { @@ -887,6 +898,12 @@ TEST_F(AssetCatalogTest, create_missing_catalogs_after_loading) EXPECT_TRUE(cdf->contains(cat_ellie->catalog_id)) << "Missing parents should be saved to a CDF."; EXPECT_TRUE(cdf->contains(cat_ruzena->catalog_id)) << "Missing parents should be saved to a CDF."; + + /* Check that each missing parent is only created once. The CDF contains multiple paths that + * could trigger the creation of missing parents, so this test makes sense. */ + EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character")); + EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character/Ellie")); + EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character/Ružena")); } } // namespace blender::bke::tests From ae4b45145c8b61199e7fe3d3b862cf473fe5769e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 1 Oct 2021 10:58:33 +0200 Subject: [PATCH 0416/1500] Cleanup: Asset Catalog Paths, move default constructor to header file No functional changes. --- source/blender/blenkernel/BKE_asset_catalog_path.hh | 2 +- source/blender/blenkernel/intern/asset_catalog_path.cc | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog_path.hh b/source/blender/blenkernel/BKE_asset_catalog_path.hh index b150f805ed5..68b9e943e94 100644 --- a/source/blender/blenkernel/BKE_asset_catalog_path.hh +++ b/source/blender/blenkernel/BKE_asset_catalog_path.hh @@ -65,7 +65,7 @@ class AssetCatalogPath { AssetCatalogPath(StringRef path); AssetCatalogPath(const std::string &path); AssetCatalogPath(const char *path); - AssetCatalogPath(const AssetCatalogPath &other_path); + AssetCatalogPath(const AssetCatalogPath &other_path) = default; AssetCatalogPath(AssetCatalogPath &&other_path) noexcept; ~AssetCatalogPath() = default; diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc index 022bceb133e..689a572f80b 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path.cc @@ -38,8 +38,6 @@ AssetCatalogPath::AssetCatalogPath(const char *path) : path_(path) { } -AssetCatalogPath::AssetCatalogPath(const AssetCatalogPath &other_path) = default; - AssetCatalogPath::AssetCatalogPath(AssetCatalogPath &&other_path) noexcept : path_(std::move(other_path.path_)) { From 7843cd63d8bbf15b1c6c2e68bee34546966d4a6b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 1 Oct 2021 11:36:10 +0200 Subject: [PATCH 0417/1500] Fix T91839: incorrect active vertex group index Differential Revision: https://developer.blender.org/D12712 --- source/blender/blenloader/intern/versioning_300.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index e65fd3e6754..11b6ad9b8ea 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -376,6 +376,7 @@ static void move_vertex_group_names_to_object_data(Main *bmain) /* Clear the list in case the it was already assigned from another object. */ BLI_freelistN(new_defbase); *new_defbase = object->defbase; + BKE_object_defgroup_active_index_set(object, object->actdef); } } } @@ -628,6 +629,16 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) */ { /* Keep this block, even when empty. */ + + /* This was missing from #move_vertex_group_names_to_object_data. */ + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) { + /* This uses the fact that the active vertex group index starts counting at 1. */ + if (BKE_object_defgroup_active_index_get(object) == 0) { + BKE_object_defgroup_active_index_set(object, object->actdef); + } + } + } } } From af0b7925db517ebe4637cca90f9dfa8056656b3e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 1 Oct 2021 11:42:00 +0200 Subject: [PATCH 0418/1500] Fix T87536: incorrect socket types in reroute nodes This refactors and fixes the code that propagates socket types through reroute nodes. In my tests it is faster than the previous code. The difference becomes larger the more reroute nodes there are, because the old code had O(n^2) runtime, while the new code runs in linear time. Differential Revision: https://developer.blender.org/D12716 --- source/blender/nodes/intern/node_common.cc | 157 +++++++++++---------- 1 file changed, 82 insertions(+), 75 deletions(-) diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index 3a896aea69c..f5e6e7640ad 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -27,6 +27,10 @@ #include "DNA_node_types.h" #include "BLI_listbase.h" +#include "BLI_map.hh" +#include "BLI_multi_value_map.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -42,10 +46,10 @@ #include "node_common.h" #include "node_util.h" -enum { - REFINE_FORWARD = 1 << 0, - REFINE_BACKWARD = 1 << 1, -}; +using blender::Map; +using blender::MultiValueMap; +using blender::Set; +using blender::Stack; /* -------------------------------------------------------------------- */ /** \name Node Group @@ -289,76 +293,37 @@ void register_node_type_reroute(void) nodeRegisterType(ntype); } -static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, int flag) +static void propagate_reroute_type_from_start_socket( + bNodeSocket *start_socket, + const MultiValueMap &links_map, + Map &r_reroute_types) { - bNodeSocket *input = (bNodeSocket *)node->inputs.first; - bNodeSocket *output = (bNodeSocket *)node->outputs.first; - bNodeLink *link; - int type = SOCK_FLOAT; - const char *type_idname = nodeStaticSocketType(type, PROP_NONE); - - /* XXX it would be a little bit more efficient to restrict actual updates - * to reroute nodes connected to an updated node, but there's no reliable flag - * to indicate updated nodes (node->update is not set on linking). - */ - - node->done = 1; - - /* recursive update */ - for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { - bNode *fromnode = link->fromnode; - bNode *tonode = link->tonode; - if (!tonode || !fromnode) { - continue; + Stack nodes_to_check; + for (bNodeLink *link : links_map.lookup(start_socket)) { + if (link->tonode->type == NODE_REROUTE) { + nodes_to_check.push(link->tonode); } - if (nodeLinkIsHidden(link)) { - continue; + if (link->fromnode->type == NODE_REROUTE) { + nodes_to_check.push(link->fromnode); } - - if (flag & REFINE_FORWARD) { - if (tonode == node && fromnode->type == NODE_REROUTE && !fromnode->done) { - node_reroute_inherit_type_recursive(ntree, fromnode, REFINE_FORWARD); + } + const bNodeSocketType *current_type = start_socket->typeinfo; + while (!nodes_to_check.is_empty()) { + bNode *reroute_node = nodes_to_check.pop(); + BLI_assert(reroute_node->type == NODE_REROUTE); + if (r_reroute_types.add(reroute_node, current_type)) { + for (bNodeLink *link : links_map.lookup((bNodeSocket *)reroute_node->inputs.first)) { + if (link->fromnode->type == NODE_REROUTE) { + nodes_to_check.push(link->fromnode); + } } - } - if (flag & REFINE_BACKWARD) { - if (fromnode == node && tonode->type == NODE_REROUTE && !tonode->done) { - node_reroute_inherit_type_recursive(ntree, tonode, REFINE_BACKWARD); + for (bNodeLink *link : links_map.lookup((bNodeSocket *)reroute_node->outputs.first)) { + if (link->tonode->type == NODE_REROUTE) { + nodes_to_check.push(link->tonode); + } } } } - - /* determine socket type from unambiguous input/output connection if possible */ - if (nodeSocketLinkLimit(input) == 1 && input->link) { - type = input->link->fromsock->type; - type_idname = nodeStaticSocketType(type, PROP_NONE); - } - else if (nodeSocketLinkLimit(output) == 1 && output->link) { - type = output->link->tosock->type; - type_idname = nodeStaticSocketType(type, PROP_NONE); - } - - if (input->type != type) { - bNodeSocket *ninput = nodeAddSocket(ntree, node, SOCK_IN, type_idname, "input", "Input"); - for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { - if (link->tosock == input) { - link->tosock = ninput; - ninput->link = link; - } - } - nodeRemoveSocket(ntree, node, input); - } - - if (output->type != type) { - bNodeSocket *noutput = nodeAddSocket(ntree, node, SOCK_OUT, type_idname, "output", "Output"); - for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { - if (link->fromsock == output) { - link->fromsock = noutput; - } - } - nodeRemoveSocket(ntree, node, output); - } - - nodeUpdateInternalLinks(ntree, node); } /* Global update function for Reroute node types. @@ -366,16 +331,58 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i */ void ntree_update_reroute_nodes(bNodeTree *ntree) { - bNode *node; - - /* clear tags */ - for (node = (bNode *)ntree->nodes.first; node; node = node->next) { - node->done = 0; + /* Contains nodes that are linked to at least one reroute node. */ + Set nodes_linked_with_reroutes; + /* Contains all links that are linked to at least one reroute node. */ + MultiValueMap links_map; + /* Build acceleration data structures for the algorithm below. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->fromsock == nullptr || link->tosock == nullptr) { + continue; + } + if (link->fromnode->type != NODE_REROUTE && link->tonode->type != NODE_REROUTE) { + continue; + } + if (link->fromnode->type != NODE_REROUTE) { + nodes_linked_with_reroutes.add(link->fromnode); + } + if (link->tonode->type != NODE_REROUTE) { + nodes_linked_with_reroutes.add(link->tonode); + } + links_map.add(link->fromsock, link); + links_map.add(link->tosock, link); } - for (node = (bNode *)ntree->nodes.first; node; node = node->next) { - if (node->type == NODE_REROUTE && !node->done) { - node_reroute_inherit_type_recursive(ntree, node, REFINE_FORWARD | REFINE_BACKWARD); + /* Will contain the socket type for every linked reroute node. */ + Map reroute_types; + + /* Propagate socket types from left to right. */ + for (bNode *start_node : nodes_linked_with_reroutes) { + LISTBASE_FOREACH (bNodeSocket *, output_socket, &start_node->outputs) { + propagate_reroute_type_from_start_socket(output_socket, links_map, reroute_types); + } + } + + /* Propagate socket types from right to left. This affects reroute nodes that haven't been + * changed in the the loop above. */ + for (bNode *start_node : nodes_linked_with_reroutes) { + LISTBASE_FOREACH (bNodeSocket *, input_socket, &start_node->inputs) { + propagate_reroute_type_from_start_socket(input_socket, links_map, reroute_types); + } + } + + /* Actually update reroute nodes with changed types. */ + for (const auto &item : reroute_types.items()) { + bNode *reroute_node = item.key; + const bNodeSocketType *socket_type = item.value; + bNodeSocket *input_socket = (bNodeSocket *)reroute_node->inputs.first; + bNodeSocket *output_socket = (bNodeSocket *)reroute_node->outputs.first; + + if (input_socket->typeinfo != socket_type) { + nodeModifySocketType(ntree, reroute_node, input_socket, socket_type->idname); + } + if (output_socket->typeinfo != socket_type) { + nodeModifySocketType(ntree, reroute_node, output_socket, socket_type->idname); } } } From 928d644895887fd8864531e21570546c07d9c53f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 1 Oct 2021 12:13:29 +0200 Subject: [PATCH 0419/1500] Append: Fix appended objects potentially auto-instantiated in more than one collection. Related to T87189. --- source/blender/blenloader/intern/readfile.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index cdae043d01c..c7c5a8b8a0d 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4506,7 +4506,10 @@ static void add_loose_objects_to_scene(Main *mainvar, /* Give all objects which are LIB_TAG_INDIRECT a base, * or for a collection when *lib has been set. */ LISTBASE_FOREACH (Object *, ob, &mainvar->objects) { - bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0; + /* NOTE: Even if this is a directly linked object and is tagged for instantiation, it might + * have already been instantiated through one of its owner collections, in which case we do not + * want to re-instantiate it in the active collection here. */ + bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0 && !BKE_scene_object_find(scene, ob); if (do_it || ((ob->id.tag & LIB_TAG_INDIRECT) != 0 && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) { if (do_append) { From 21c29480c3e931ad062b454bd458360821a47cd7 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 1 Oct 2021 12:14:44 +0200 Subject: [PATCH 0420/1500] Fix paste code linking 'direct' IDs with 'INDIRECT' flag. No idea why this was done that way (it originate from initial paste commit, rB12b642062c6f). But the IDs 'selected' as direct paste in `BLO_library_link_copypaste` should be 'directly' linked, it's similar case to actual append of selected IDs by the user. Related to T87189. --- source/blender/blenloader/intern/readfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index c7c5a8b8a0d..85fc6c2ad70 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4779,7 +4779,7 @@ int BLO_library_link_copypaste(Main *mainl, BlendHandle *bh, const uint64_t id_t if (blo_bhead_is_id_valid_type(bhead) && BKE_idtype_idcode_is_linkable((short)bhead->code) && (id_types_mask == 0 || (BKE_idtype_idcode_to_idfilter((short)bhead->code) & id_types_mask) != 0)) { - read_libblock(fd, mainl, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT, false, &id); + read_libblock(fd, mainl, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_EXTERN, false, &id); num_directly_linked++; } From 798e59300280605b8886d9a1f52e1fcf8e5ac008 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 1 Oct 2021 12:18:56 +0200 Subject: [PATCH 0421/1500] Fix T87189: Copy/pasting IDs does not handle properly instantiation. Copy/Paste uses its own code path for ID linking, which was not setting `LIB_TAG_DOIT` for proper instantiation later on. Would be nice the make this logic closer to the rest of the link/append code at some point, but for now this fix will do. --- source/blender/blenloader/intern/readfile.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 85fc6c2ad70..3ee5e41bf1f 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4788,6 +4788,13 @@ int BLO_library_link_copypaste(Main *mainl, BlendHandle *bh, const uint64_t id_t ListBase *lb = which_libbase(mainl, GS(id->name)); id_sort_by_name(lb, id, NULL); + /* Tag as loose object (or data associated with objects) + * needing to be instantiated (see also #link_named_part and its usage of + * #BLO_LIBLINK_NEEDS_ID_TAG_DOIT above). */ + if (library_link_idcode_needs_tag_check(GS(id->name), BLO_LIBLINK_NEEDS_ID_TAG_DOIT)) { + id->tag |= LIB_TAG_DOIT; + } + if (bhead->code == ID_OB) { /* Instead of instancing Base's directly, postpone until after collections are loaded * otherwise the base's flag is set incorrectly when collections are used */ From eb3a8fb4e8b173edc2282f6462e219ab8b9c95e2 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 1 Oct 2021 13:20:46 +0200 Subject: [PATCH 0422/1500] Fix T91872: incorrect socket inspection on group nodes This bug was introduced in rBef45399f3be0955ba8. --- source/blender/editors/space_node/node_draw.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 499ccb8a889..9b243290566 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -1109,13 +1109,13 @@ static void node_socket_draw_nested(const bContext *C, if (!description.is_empty()) { output << TIP_(description.data()) << ".\n\n"; } - - if (socket_inspection_str.has_value()) { - output << *socket_inspection_str; - return BLI_strdup(output.str().c_str()); - } } - output << TIP_("The socket value has not been computed yet"); + if (socket_inspection_str.has_value()) { + output << *socket_inspection_str; + } + else { + output << TIP_("The socket value has not been computed yet"); + } return BLI_strdup(output.str().c_str()); }, data, From bdc66c9569eb244296bc1fad362f372ff8a939e2 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 30 Sep 2021 17:22:12 -0300 Subject: [PATCH 0423/1500] GPU: set 'GL_PACK_ALIGNMENT' 1 as default This fixes T91828. The current value of `GL_PACK_ALIGNMENT` may result in crash in the `gpu` module if the buffer is not aligned. Differential Revision: https://developer.blender.org/D12720 --- source/blender/gpu/opengl/gl_state.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc index 1106e3dab50..d737cf88a13 100644 --- a/source/blender/gpu/opengl/gl_state.cc +++ b/source/blender/gpu/opengl/gl_state.cc @@ -52,6 +52,7 @@ GLStateManager::GLStateManager() glDisable(GL_DITHER); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPrimitiveRestartIndex((GLuint)0xFFFFFFFF); From f9acf21063d32152a98876ecc83f93ca92df18a0 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 1 Oct 2021 15:43:25 +0300 Subject: [PATCH 0424/1500] Python API Docs: add an example of `Bone.convert_local_to_pose` usage. The use case for this method is quite obscure and difficult to understand without an example. Despite how big looks, this is actually the simplest example that makes sense. --- .../bpy.types.Bone.convert_local_to_pose.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py diff --git a/doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py b/doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py new file mode 100644 index 00000000000..f3cc95dec61 --- /dev/null +++ b/doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py @@ -0,0 +1,40 @@ +""" +This method enables conversions between Local and Pose space for bones in +the middle of updating the armature without having to update dependencies +after each change, by manually carrying updated matrices in a recursive walk. +""" + +def set_pose_matrices(obj, matrix_map): + "Assign pose space matrices of all bones at once, ignoring constraints." + + def rec(pbone, parent_matrix): + matrix = matrix_map[pbone.name] + + ## Instead of: + # pbone.matrix = matrix + # bpy.context.view_layer.update() + + # Compute and assign local matrix, using the new parent matrix + if pbone.parent: + pbone.matrix_basis = pbone.bone.convert_local_to_pose( + matrix, + pbone.bone.matrix_local, + parent_matrix=parent_matrix, + parent_matrix_local=pbone.parent.bone.matrix_local, + invert=True + ) + else: + pbone.matrix_basis = pbone.bone.convert_local_to_pose( + matrix, + pbone.bone.matrix_local, + invert=True + ) + + # Recursively process children, passing the new matrix through + for child in pbone.children: + rec(child, matrix) + + # Scan all bone trees from their roots + for pbone in obj.pose.bones: + if not pbone.parent: + rec(pbone, None) From 271210126e12713d70813937ddd4732993f7bb35 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 1 Oct 2021 14:22:15 +0200 Subject: [PATCH 0425/1500] Fix T91834: Appending objects with shape keys into new file is broken. Recent append refactor 'broke' this, we need special recursive care and handling of those nasty shpae keys... again. --- source/blender/windowmanager/intern/wm_files_link.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index ea0b0a9feaa..a73bea31669 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -597,6 +597,11 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) } if (!BKE_idtype_idcode_is_linkable(GS(id->name))) { + /* While we do not want to add non-linkable ID (shape keys...) to the list of linked items, + * unfortunately they can use fully linkable valid IDs too, like actions. Those need to be + * processed, so we need to recursively deal with them here. */ + BKE_library_foreach_ID_link( + cb_data->bmain, id, foreach_libblock_append_callback, data, IDWALK_NOP); return IDWALK_RET_NOP; } From 1c7ce7e0b45b6613b22cee1515187f5a251932e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 1 Oct 2021 15:04:10 +0200 Subject: [PATCH 0426/1500] Cleanup: asset catalogs, make function const Declare `AssetCatalogService::find_catalog()` as `const`, as it's not requiring modification off the service object. No functional changes. --- source/blender/blenkernel/BKE_asset_catalog.hh | 2 +- source/blender/blenkernel/intern/asset_catalog.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index a0bc1267826..052cf8e9d1e 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -94,7 +94,7 @@ class AssetCatalogService { void merge_from_disk_before_writing(); /** Return catalog with the given ID. Return nullptr if not found. */ - AssetCatalog *find_catalog(CatalogID catalog_id); + AssetCatalog *find_catalog(CatalogID catalog_id) const; /** Return first catalog with the given path. Return nullptr if not found. This is not an * efficient call as it's just a linear search over the catalogs. */ diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index a948c0f96ff..34bd1fd49db 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -65,9 +65,9 @@ bool AssetCatalogService::is_empty() const return catalogs_.is_empty(); } -AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) +AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) const { - std::unique_ptr *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id); + const std::unique_ptr *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id); if (catalog_uptr_ptr == nullptr) { return nullptr; } From 56ce51d1f75a5966977847aeafd25a60a9f44260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 1 Oct 2021 15:21:44 +0200 Subject: [PATCH 0427/1500] Asset Catalogs: add catalog filter for the asset browser Given an "active catalog" (i.e. the one selected in the UI), construct an `AssetCatalogFilter` instance. This filter can determine whether an asset should be shown or not. It returns `true` when The asset's catalog ID is: - the active catalog, - an alias of the active catalog (so different UUID that maps to the same path), - a sub-catalog of the active catalog. Not yet hooked up to the UI. --- .../blender/blenkernel/BKE_asset_catalog.hh | 26 +++++++++ .../blenkernel/intern/asset_catalog.cc | 37 ++++++++++++ .../blenkernel/intern/asset_catalog_test.cc | 56 +++++++++++++++++++ 3 files changed, 119 insertions(+) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 052cf8e9d1e..65082f06f3a 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -26,6 +26,7 @@ #include "BLI_function_ref.hh" #include "BLI_map.hh" +#include "BLI_set.hh" #include "BLI_string_ref.hh" #include "BLI_uuid.h" #include "BLI_vector.hh" @@ -48,6 +49,7 @@ using CatalogFilePath = std::string; class AssetCatalog; class AssetCatalogDefinitionFile; class AssetCatalogTree; +class AssetCatalogFilter; /* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single * directory hierarchy). */ @@ -100,6 +102,14 @@ class AssetCatalogService { * efficient call as it's just a linear search over the catalogs. */ AssetCatalog *find_catalog_by_path(const AssetCatalogPath &path) const; + /** + * Create a filter object that can be used to determine whether an asset belongs to the given + * catalog, or any of the catalogs in the sub-tree rooted at the given catalog. + * + * \see #AssetCatalogFilter + */ + AssetCatalogFilter create_catalog_filter(CatalogID active_catalog_id) const; + /** Create a catalog with some sensible auto-generated catalog ID. * The catalog will be saved to the default catalog file.*/ AssetCatalog *create_catalog(const AssetCatalogPath &catalog_path); @@ -331,4 +341,20 @@ struct AssetCatalogPathCmp { * Being a set, duplicates are removed. The catalog's simple name is ignored in this. */ using AssetCatalogOrderedSet = std::set; +/** + * Filter that can determine whether an asset should be visible or not, based on its catalog ID. + * + * \see AssetCatalogService::create_filter() + */ +class AssetCatalogFilter { + public: + bool contains(CatalogID asset_catalog_id) const; + + protected: + friend AssetCatalogService; + const Set matching_catalog_ids; + + explicit AssetCatalogFilter(Set &&matching_catalog_ids); +}; + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 34bd1fd49db..4973de20fb3 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -85,6 +85,33 @@ AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath & return nullptr; } +AssetCatalogFilter AssetCatalogService::create_catalog_filter( + const CatalogID active_catalog_id) const +{ + Set matching_catalog_ids; + matching_catalog_ids.add(active_catalog_id); + + const AssetCatalog *active_catalog = find_catalog(active_catalog_id); + if (!active_catalog) { + /* If the UUID is unknown (i.e. not mapped to an actual Catalog), it is impossible to determine + * its children. The filter can still work on the given UUID. */ + return AssetCatalogFilter(std::move(matching_catalog_ids)); + } + + /* This cannot just iterate over tree items to get all the required data, because tree items only + * represent single UUIDs. It could be used to get the main UUIDs of the children, though, and + * then only do an exact match on the path (instead of the more complex `is_contained_in()` + * call). Without an extra indexed-by-path acceleration structure, this is still going to require + * a linear search, though. */ + for (const auto &catalog_uptr : this->catalogs_.values()) { + if (catalog_uptr->path.is_contained_in(active_catalog->path)) { + matching_catalog_ids.add(catalog_uptr->catalog_id); + } + } + + return AssetCatalogFilter(std::move(matching_catalog_ids)); +} + void AssetCatalogService::delete_catalog(CatalogID catalog_id) { std::unique_ptr *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id); @@ -754,4 +781,14 @@ std::string AssetCatalog::sensible_simple_name_for_path(const AssetCatalogPath & return "..." + name.substr(name.length() - 60); } +AssetCatalogFilter::AssetCatalogFilter(Set &&matching_catalog_ids) + : matching_catalog_ids(std::move(matching_catalog_ids)) +{ +} + +bool AssetCatalogFilter::contains(const CatalogID asset_catalog_id) const +{ + return matching_catalog_ids.contains(asset_catalog_id); +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index f3e1f7984a4..e8d40771b89 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -906,4 +906,60 @@ TEST_F(AssetCatalogTest, create_missing_catalogs_after_loading) EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character/Ružena")); } +TEST_F(AssetCatalogTest, create_catalog_filter) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(); + + /* Alias for the same catalog as the main one. */ + AssetCatalog *alias_ruzena = service.create_catalog("character/Ružena/poselib"); + /* Alias for a sub-catalog. */ + AssetCatalog *alias_ruzena_hand = service.create_catalog("character/Ružena/poselib/hand"); + + AssetCatalogFilter filter = service.create_catalog_filter(UUID_POSES_RUZENA); + + /* Positive test for loaded-from-disk catalogs. */ + EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA)) + << "Main catalog should be included in the filter."; + EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA_HAND)) + << "Sub-catalog should be included in the filter."; + EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA_FACE)) + << "Sub-catalog should be included in the filter."; + + /* Positive test for newly-created catalogs. */ + EXPECT_TRUE(filter.contains(alias_ruzena->catalog_id)) + << "Alias of main catalog should be included in the filter."; + EXPECT_TRUE(filter.contains(alias_ruzena_hand->catalog_id)) + << "Alias of sub-catalog should be included in the filter."; + + /* Negative test for unrelated catalogs. */ + EXPECT_FALSE(filter.contains(BLI_uuid_nil())) << "Nil catalog should not be included."; + EXPECT_FALSE(filter.contains(UUID_ID_WITHOUT_PATH)); + EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE)); + EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_FALSE(filter.contains(UUID_WITHOUT_SIMPLENAME)); +} + +TEST_F(AssetCatalogTest, create_catalog_filter_for_unknown_uuid) +{ + AssetCatalogService service; + const bUUID unknown_uuid = BLI_uuid_generate_random(); + + AssetCatalogFilter filter = service.create_catalog_filter(unknown_uuid); + EXPECT_TRUE(filter.contains(unknown_uuid)); + + EXPECT_FALSE(filter.contains(BLI_uuid_nil())) << "Nil catalog should not be included."; + EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE)); +} + +TEST_F(AssetCatalogTest, create_catalog_filter_for_unassigned_assets) +{ + AssetCatalogService service; + + AssetCatalogFilter filter = service.create_catalog_filter(BLI_uuid_nil()); + EXPECT_TRUE(filter.contains(BLI_uuid_nil())); + EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE)); +} + } // namespace blender::bke::tests From 3be4cb5b279165bb09df53f24f654f688a2f2e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 1 Oct 2021 15:24:04 +0200 Subject: [PATCH 0428/1500] Cleanup: Asset Catalog Test, fix clang-tidy warnings No functional changes. --- source/blender/blenkernel/intern/asset_catalog_test.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index e8d40771b89..fb471a8ee7b 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -445,7 +445,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf) const AssetCatalog *cat = service.create_catalog("some/catalog/path"); const CatalogFilePath blendfilename = top_level_dir + "subdir/some_file.blend"; - ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str())); + ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename)); EXPECT_EQ(cdf_filename, service.get_catalog_definition_file()->file_path); /* Test that the CDF was created in the expected location. */ @@ -472,7 +472,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_empty_directory) const AssetCatalog *cat = service.create_catalog("some/catalog/path"); const CatalogFilePath blendfilename = target_dir + "some_file.blend"; - ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str())); + ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename)); /* Test that the CDF was created in the expected location. */ const CatalogFilePath expected_cdf_path = target_dir + @@ -505,7 +505,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_me /* Mock that the blend file is written to a subdirectory of the asset library. */ const CatalogFilePath blendfilename = target_dir + "some_file.blend"; - ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str())); + ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename)); /* Test that the CDF still exists in the expected location. */ const CatalogFilePath backup_filename = writable_cdf_file + "~"; @@ -550,7 +550,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_asset_lib) const AssetCatalog *cat = service.create_catalog("some/catalog/path"); /* Mock that the blend file is written to the directory already containing a CDF. */ - ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str())); + ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename)); /* Test that the CDF still exists in the expected location. */ EXPECT_TRUE(BLI_exists(writable_cdf_file.c_str())); From f497e471f8663498596725c09702b5b6da39b707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 1 Oct 2021 16:07:05 +0200 Subject: [PATCH 0429/1500] Asset Catalogs: always have an Asset Catalog Tree available Always create an `AssetCatalogTree` in the `AssetCatalogService`. This ensures that newly-created catalogs are immediately visible in the UI (because they insert themselves into an already-existing tree). --- source/blender/blenkernel/BKE_asset_catalog.hh | 2 +- source/blender/blenkernel/intern/asset_catalog.cc | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 65082f06f3a..8afc4fe2ad2 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -134,7 +134,7 @@ class AssetCatalogService { Map> catalogs_; Map> deleted_catalogs_; std::unique_ptr catalog_definition_file_; - std::unique_ptr catalog_tree_; + std::unique_ptr catalog_tree_ = std::make_unique(); CatalogFilePath asset_library_root_; void load_directory_recursive(const CatalogFilePath &directory_path); diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 4973de20fb3..2c7cf28d60d 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -171,11 +171,8 @@ AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalo catalog_definition_file_->add_new(catalog_ptr); } - /* The tree may not exist; this happens when no catalog definition file has been loaded yet. When - * the tree is created any in-memory catalogs will be added, so it doesn't need to happen now. */ - if (catalog_tree_) { - catalog_tree_->insert_item(*catalog_ptr); - } + BLI_assert_msg(catalog_tree_, "An Asset Catalog tree should always exist."); + catalog_tree_->insert_item(*catalog_ptr); return catalog_ptr; } From fb820496f5b00de8ccf6927c58d24b64bc099c4f Mon Sep 17 00:00:00 2001 From: Johan Walles Date: Fri, 1 Oct 2021 12:35:00 +0200 Subject: [PATCH 0430/1500] Tracking: Sort motion tracking tracks by start and end frames Enable sorting motion tracking tracks by start / end times. Help identifying what cases reconstructed camera jumps, based on information about whether any track starts/ends at the frame. Based on revision eb0eb54d9644c5139ef139fee1e14da35c4fab7e. {F10563305} Reviewed By: sergey Differential Revision: https://developer.blender.org/D12621 --- source/blender/blenkernel/intern/tracking.c | 102 ++++++++++++++++++ source/blender/makesdna/DNA_tracking_types.h | 4 + source/blender/makesrna/intern/rna_tracking.c | 2 + 3 files changed, 108 insertions(+) diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index 068d048fd08..3cdb8e927a6 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -3014,6 +3014,61 @@ static int channels_average_error_sort(const void *a, const void *b) return 0; } +static int compare_firstlast_putting_undefined_first( + bool inverse, bool a_markerless, int a_value, bool b_markerless, int b_value) +{ + if (a_markerless && b_markerless) { + /* Neither channel has not-disabled markers, return whatever. */ + return 0; + } + if (a_markerless) { + /* Put the markerless channel first. */ + return 0; + } + if (b_markerless) { + /* Put the markerless channel first. */ + return 1; + } + + /* Both channels have markers. */ + + if (inverse) { + if (a_value < b_value) { + return 1; + } + return 0; + } + + if (a_value > b_value) { + return 1; + } + return 0; +} + +static int channels_start_sort(const void *a, const void *b) +{ + const MovieTrackingDopesheetChannel *channel_a = a; + const MovieTrackingDopesheetChannel *channel_b = b; + + return compare_firstlast_putting_undefined_first(false, + channel_a->tot_segment == 0, + channel_a->first_not_disabled_marker_framenr, + channel_b->tot_segment == 0, + channel_b->first_not_disabled_marker_framenr); +} + +static int channels_end_sort(const void *a, const void *b) +{ + const MovieTrackingDopesheetChannel *channel_a = a; + const MovieTrackingDopesheetChannel *channel_b = b; + + return compare_firstlast_putting_undefined_first(false, + channel_a->tot_segment == 0, + channel_a->last_not_disabled_marker_framenr, + channel_b->tot_segment == 0, + channel_b->last_not_disabled_marker_framenr); +} + static int channels_alpha_inverse_sort(const void *a, const void *b) { if (channels_alpha_sort(a, b)) { @@ -3053,22 +3108,51 @@ static int channels_average_error_inverse_sort(const void *a, const void *b) return 0; } +static int channels_start_inverse_sort(const void *a, const void *b) +{ + const MovieTrackingDopesheetChannel *channel_a = a; + const MovieTrackingDopesheetChannel *channel_b = b; + + return compare_firstlast_putting_undefined_first(true, + channel_a->tot_segment == 0, + channel_a->first_not_disabled_marker_framenr, + channel_b->tot_segment == 0, + channel_b->first_not_disabled_marker_framenr); +} + +static int channels_end_inverse_sort(const void *a, const void *b) +{ + const MovieTrackingDopesheetChannel *channel_a = a; + const MovieTrackingDopesheetChannel *channel_b = b; + + return compare_firstlast_putting_undefined_first(true, + channel_a->tot_segment == 0, + channel_a->last_not_disabled_marker_framenr, + channel_b->tot_segment == 0, + channel_b->last_not_disabled_marker_framenr); +} + /* Calculate frames segments at which track is tracked continuously. */ static void tracking_dopesheet_channels_segments_calc(MovieTrackingDopesheetChannel *channel) { MovieTrackingTrack *track = channel->track; int i, segment; + bool first_not_disabled_marker_framenr_set; channel->tot_segment = 0; channel->max_segment = 0; channel->total_frames = 0; + channel->first_not_disabled_marker_framenr = 0; + channel->last_not_disabled_marker_framenr = 0; + /* TODO(sergey): looks a bit code-duplicated, need to look into * logic de-duplication here. */ /* count */ i = 0; + first_not_disabled_marker_framenr_set = false; while (i < track->markersnr) { MovieTrackingMarker *marker = &track->markers[i]; @@ -3086,6 +3170,12 @@ static void tracking_dopesheet_channels_segments_calc(MovieTrackingDopesheetChan break; } + if (!first_not_disabled_marker_framenr_set) { + channel->first_not_disabled_marker_framenr = marker->framenr; + first_not_disabled_marker_framenr_set = true; + } + channel->last_not_disabled_marker_framenr = marker->framenr; + prev_fra = marker->framenr; len++; i++; @@ -3203,6 +3293,12 @@ static void tracking_dopesheet_channels_sort(MovieTracking *tracking, else if (sort_method == TRACKING_DOPE_SORT_AVERAGE_ERROR) { BLI_listbase_sort(&dopesheet->channels, channels_average_error_inverse_sort); } + else if (sort_method == TRACKING_DOPE_SORT_START) { + BLI_listbase_sort(&dopesheet->channels, channels_start_inverse_sort); + } + else if (sort_method == TRACKING_DOPE_SORT_END) { + BLI_listbase_sort(&dopesheet->channels, channels_end_inverse_sort); + } } else { if (sort_method == TRACKING_DOPE_SORT_NAME) { @@ -3217,6 +3313,12 @@ static void tracking_dopesheet_channels_sort(MovieTracking *tracking, else if (sort_method == TRACKING_DOPE_SORT_AVERAGE_ERROR) { BLI_listbase_sort(&dopesheet->channels, channels_average_error_sort); } + else if (sort_method == TRACKING_DOPE_SORT_START) { + BLI_listbase_sort(&dopesheet->channels, channels_start_sort); + } + else if (sort_method == TRACKING_DOPE_SORT_END) { + BLI_listbase_sort(&dopesheet->channels, channels_end_sort); + } } } diff --git a/source/blender/makesdna/DNA_tracking_types.h b/source/blender/makesdna/DNA_tracking_types.h index fb2d985d353..0e313183300 100644 --- a/source/blender/makesdna/DNA_tracking_types.h +++ b/source/blender/makesdna/DNA_tracking_types.h @@ -398,6 +398,8 @@ typedef struct MovieTrackingDopesheetChannel { int *segments; /** Longest segment length and total number of tracked frames. */ int max_segment, total_frames; + /** These numbers are valid only if tot_segment > 0. */ + int first_not_disabled_marker_framenr, last_not_disabled_marker_framenr; } MovieTrackingDopesheetChannel; typedef struct MovieTrackingDopesheetCoverageSegment { @@ -592,6 +594,8 @@ enum { TRACKING_DOPE_SORT_LONGEST = 1, TRACKING_DOPE_SORT_TOTAL = 2, TRACKING_DOPE_SORT_AVERAGE_ERROR = 3, + TRACKING_DOPE_SORT_START = 4, + TRACKING_DOPE_SORT_END = 5, }; /* MovieTrackingDopesheet->flag */ diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index 336359a9dc0..c81588aa8b5 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -2437,6 +2437,8 @@ static void rna_def_trackingDopesheet(BlenderRNA *brna) 0, "Average Error", "Sort channels by average reprojection error of tracks after solve"}, + {TRACKING_DOPE_SORT_START, "START", 0, "Start Frame", "Sort channels by first frame number"}, + {TRACKING_DOPE_SORT_END, "END", 0, "End Frame", "Sort channels by last frame number"}, {0, NULL, 0, NULL, NULL}, }; From e1952c541af2fded4d1f92b55d94725be25914c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 1 Oct 2021 16:41:36 +0200 Subject: [PATCH 0431/1500] Cleanup: unused function declaration This should have been removed during the recent velocity attribute refactor. --- source/blender/io/alembic/intern/abc_customdata.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h index 2662ad733e0..03e6f697f0c 100644 --- a/source/blender/io/alembic/intern/abc_customdata.h +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -122,12 +122,6 @@ void read_custom_data(const std::string &iobject_full_name, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss); -void read_velocity(const Alembic::Abc::ICompoundProperty &prop, - const Alembic::Abc::PropertyHeader *prop_header, - const Alembic::Abc::ISampleSelector &selector, - const CDStreamConfig &config, - const char *velocity_name, - const float velocity_scale); typedef enum { ABC_UV_SCOPE_NONE, ABC_UV_SCOPE_LOOP, From aae96176e8c36c8bcc64e94499975ff3b0b6ec0c Mon Sep 17 00:00:00 2001 From: guitargeek Date: Fri, 1 Oct 2021 09:40:57 -0500 Subject: [PATCH 0432/1500] Geometry Nodes: Curve Subdivide Node with Fields The curve subdivide node can now take an int field to specify the number of subdivisions to make at each curve segment. Reviewed by: Hans Goudey Differential Revision: https://developer.blender.org/D12534 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesrna/intern/rna_nodetree.c | 2 +- source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 3 +- .../nodes/legacy/node_geo_curve_subdivide.cc | 2 +- .../nodes/node_geo_curve_subdivide.cc | 364 ++++++++++++++++++ 9 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 5e6f9b9a76a..d357e78d900 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -525,6 +525,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveFill"), NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), + NodeItem("GeometryNodeCurveSubdivide"), NodeItem("GeometryNodeCurveParameter"), NodeItem("GeometryNodeInputTangent"), NodeItem("GeometryNodeCurveSample"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a4fc4800fd2..bfce3718f82 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1509,6 +1509,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_REVERSE 1095 #define GEO_NODE_PROXIMITY 1096 +#define GEO_NODE_CURVE_SUBDIVIDE 1097 /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 66f917ffd96..2df9d2ab32c 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5714,6 +5714,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_material_assign(); register_node_type_geo_legacy_select_by_material(); register_node_type_geo_legacy_curve_reverse(); + register_node_type_geo_legacy_curve_subdivide(); register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 894a8bad6da..ad1331faf2f 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10280,7 +10280,7 @@ static void def_geo_curve_resample(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_curve_subdivide(StructRNA *srna) +static void def_geo_legacy_curve_subdivide(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 5b6a5ac5b33..7c243e8ce55 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -209,6 +209,7 @@ set(SRC geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc geometry/nodes/node_geo_curve_sample.cc + geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_distribute_points_on_faces.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index af8661d9b8d..d4cde483a12 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -34,6 +34,7 @@ void register_node_type_geo_legacy_attribute_randomize(void); void register_node_type_geo_legacy_material_assign(void); void register_node_type_geo_legacy_select_by_material(void); void register_node_type_geo_legacy_curve_reverse(void); +void register_node_type_geo_legacy_curve_subdivide(void); void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_capture(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 7972609aa27..a65ff73ab89 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -299,7 +299,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_legacy_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_TO_POINTS, def_geo_curve_to_points, "LEGACY_CURVE_TO_POINTS", LegacyCurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_LEGACY_EDGE_SPLIT, 0, "LEGACY_EDGE_SPLIT", LegacyEdgeSplit, "Edge Split", "") @@ -337,6 +337,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc index 0522f2b8981..f32a68bc042 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc @@ -373,7 +373,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_curve_subdivide() +void register_node_type_geo_legacy_curve_subdivide() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc new file mode 100644 index 00000000000..34997c66cbb --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -0,0 +1,364 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::fn::GVArray_For_GSpan; +using blender::fn::GVArray_For_Span; +using blender::fn::GVArray_Typed; + +namespace blender::nodes { + +static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Cuts").default_value(1).min(0).max(1000).supports_field(); + b.add_output("Geometry"); +} + +static Array get_subdivided_offsets(const Spline &spline, + const VArray &cuts, + const int spline_offset) +{ + Array offsets(spline.segments_size() + 1); + int offset = 0; + for (const int i : IndexRange(spline.segments_size())) { + offsets[i] = offset; + offset = offset + std::max(cuts[spline_offset + i], 0) + 1; + } + offsets.last() = offset; + return offsets; +} + +template +static void subdivide_attribute(Span src, + const Span offsets, + const bool is_cyclic, + MutableSpan dst) +{ + const int src_size = src.size(); + threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) { + for (const int i : range) { + const int cuts = offsets[i + 1] - offsets[i]; + dst[offsets[i]] = src[i]; + const float factor_delta = 1.0f / (cuts + 1.0f); + for (const int cut : IndexRange(cuts)) { + const float factor = (cut + 1) * factor_delta; + dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]); + } + } + }); + + if (is_cyclic) { + const int i = src_size - 1; + const int cuts = offsets[i + 1] - offsets[i]; + dst[offsets[i]] = src.last(); + const float factor_delta = 1.0f / (cuts + 1.0f); + for (const int cut : IndexRange(cuts)) { + const float factor = (cut + 1) * factor_delta; + dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first()); + } + } + else { + dst.last() = src.last(); + } +} + +/** + * In order to generate a Bezier spline with the same shape as the input spline, apply the + * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the + * previous result point's right handle and the left handle at the end of the segment. + * + * \note Non-vector segments in the result spline are given free handles. This could possibly be + * improved with another pass that sets handles to aligned where possible, but currently that does + * not provide much benefit for the increased complexity. + */ +static void subdivide_bezier_segment(const BezierSpline &src, + const int index, + const int offset, + const int result_size, + Span src_positions, + Span src_handles_left, + Span src_handles_right, + MutableSpan dst_positions, + MutableSpan dst_handles_left, + MutableSpan dst_handles_right, + MutableSpan dst_type_left, + MutableSpan dst_type_right) +{ + const bool is_last_cyclic_segment = index == (src.size() - 1); + const int next_index = is_last_cyclic_segment ? 0 : index + 1; + + /* The first point in the segment is always copied. */ + dst_positions[offset] = src_positions[index]; + + if (src.segment_is_vector(index)) { + if (is_last_cyclic_segment) { + dst_type_left.first() = BezierSpline::HandleType::Vector; + } + dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); + dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); + + const float factor_delta = 1.0f / result_size; + for (const int cut : IndexRange(result_size)) { + const float factor = cut * factor_delta; + dst_positions[offset + cut] = attribute_math::mix2( + factor, src_positions[index], src_positions[next_index]); + } + } + else { + if (is_last_cyclic_segment) { + dst_type_left.first() = BezierSpline::HandleType::Free; + } + dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free); + dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); + + const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; + + /* Create a Bezier segment to update iteratively for every subdivision + * and references to the meaningful values for ease of use. */ + BezierSpline temp; + temp.resize(2); + float3 &segment_start = temp.positions().first(); + float3 &segment_end = temp.positions().last(); + float3 &handle_prev = temp.handle_positions_right().first(); + float3 &handle_next = temp.handle_positions_left().last(); + segment_start = src_positions[index]; + segment_end = src_positions[next_index]; + handle_prev = src_handles_right[index]; + handle_next = src_handles_left[next_index]; + + for (const int cut : IndexRange(result_size - 1)) { + const float parameter = 1.0f / (result_size - cut); + const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); + + /* Copy relevant temporary data to the result. */ + dst_handles_right[offset + cut] = insert.handle_prev; + dst_handles_left[offset + cut + 1] = insert.left_handle; + dst_positions[offset + cut + 1] = insert.position; + + /* Update the segment to prepare it for the next subdivision. */ + segment_start = insert.position; + handle_prev = insert.right_handle; + handle_next = insert.handle_next; + } + + /* Copy the handles for the last segment from the temporary spline. */ + dst_handles_right[offset + result_size - 1] = handle_prev; + dst_handles_left[i_segment_last] = handle_next; + } +} + +static void subdivide_bezier_spline(const BezierSpline &src, + const Span offsets, + BezierSpline &dst) +{ + Span src_positions = src.positions(); + Span src_handles_left = src.handle_positions_left(); + Span src_handles_right = src.handle_positions_right(); + MutableSpan dst_positions = dst.positions(); + MutableSpan dst_handles_left = dst.handle_positions_left(); + MutableSpan dst_handles_right = dst.handle_positions_right(); + MutableSpan dst_type_left = dst.handle_types_left(); + MutableSpan dst_type_right = dst.handle_types_right(); + + threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { + for (const int i : range) { + subdivide_bezier_segment(src, + i, + offsets[i], + offsets[i + 1] - offsets[i], + src_positions, + src_handles_left, + src_handles_right, + dst_positions, + dst_handles_left, + dst_handles_right, + dst_type_left, + dst_type_right); + } + }); + + if (src.is_cyclic()) { + const int i_last = src.size() - 1; + subdivide_bezier_segment(src, + i_last, + offsets[i_last], + offsets.last() - offsets[i_last], + src_positions, + src_handles_left, + src_handles_right, + dst_positions, + dst_handles_left, + dst_handles_right, + dst_type_left, + dst_type_right); + } + else { + dst_positions.last() = src_positions.last(); + } +} + +static void subdivide_builtin_attributes(const Spline &src_spline, + const Span offsets, + Spline &dst_spline) +{ + const bool is_cyclic = src_spline.is_cyclic(); + subdivide_attribute(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); + subdivide_attribute(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); + switch (src_spline.type()) { + case Spline::Type::Poly: { + const PolySpline &src = static_cast(src_spline); + PolySpline &dst = static_cast(dst_spline); + subdivide_attribute(src.positions(), offsets, is_cyclic, dst.positions()); + break; + } + case Spline::Type::Bezier: { + const BezierSpline &src = static_cast(src_spline); + BezierSpline &dst = static_cast(dst_spline); + subdivide_bezier_spline(src, offsets, dst); + dst.mark_cache_invalid(); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src = static_cast(src_spline); + NURBSpline &dst = static_cast(dst_spline); + subdivide_attribute(src.positions(), offsets, is_cyclic, dst.positions()); + subdivide_attribute(src.weights(), offsets, is_cyclic, dst.weights()); + break; + } + } +} + +static void subdivide_dynamic_attributes(const Spline &src_spline, + const Span offsets, + Spline &dst_spline) +{ + const bool is_cyclic = src_spline.is_cyclic(); + src_spline.attributes.foreach_attribute( + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src = src_spline.attributes.get_for_read(attribute_id); + BLI_assert(src); + + if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) { + /* Since the source spline of the same type had the attribute, adding it should work. */ + BLI_assert_unreachable(); + } + + std::optional dst = dst_spline.attributes.get_for_write(attribute_id); + BLI_assert(dst); + + attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) { + using T = decltype(dummy); + subdivide_attribute(src->typed(), offsets, is_cyclic, dst->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr subdivide_spline(const Spline &spline, + const VArray &cuts, + const int spline_offset) +{ + /* Since we expect to access each value many times, it should be worth it to make sure the + * attribute is a real span (especially considering the note below). Using the offset at each + * point facilitates subdividing in parallel later. */ + Array offsets = get_subdivided_offsets(spline, cuts, spline_offset); + const int result_size = offsets.last() + int(!spline.is_cyclic()); + SplinePtr new_spline = spline.copy_only_settings(); + new_spline->resize(result_size); + subdivide_builtin_attributes(spline, offsets, *new_spline); + subdivide_dynamic_attributes(spline, offsets, *new_spline); + return new_spline; +} + +/** + * \note Passing the virtual array for the entire spline is possibly quite inefficient here when + * the attribute was on the point domain and stored separately for each spline already, and it + * prevents some other optimizations like skipping splines with a single attribute value of < 1. + * However, it allows the node to access builtin attribute easily, so it the makes most sense this + * way until the attribute API is refactored. + */ +static std::unique_ptr subdivide_curve(const CurveEval &input_curve, + const VArray &cuts) +{ + const Array control_point_offsets = input_curve.control_point_offsets(); + const Span input_splines = input_curve.splines(); + + std::unique_ptr output_curve = std::make_unique(); + output_curve->resize(input_splines.size()); + output_curve->attributes = input_curve.attributes; + MutableSpan output_splines = output_curve->splines(); + + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + output_splines[i] = subdivide_spline(*input_splines[i], cuts, control_point_offsets[i]); + } + }); + + return output_curve; +} + +static void geo_node_subdivide_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field cuts_field = params.extract_input>("Cuts"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; + } + + const CurveComponent &component = *geometry_set.get_component_for_read(); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(cuts_field); + evaluator.evaluate(); + const VArray &cuts = evaluator.get_evaluated(0); + + if (cuts.is_single() && cuts.get_internal_single() < 1) { + return; + } + + std::unique_ptr output_curve = subdivide_curve(*component.get_for_read(), cuts); + geometry_set.replace_curve(output_curve.release()); + }); + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_subdivide() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_subdivide_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec; + nodeRegisterType(&ntype); +} From 9e456ca69565f245c7b3d76b134a72ef5d252bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 1 Oct 2021 16:44:59 +0200 Subject: [PATCH 0433/1500] Asset Browser: expose current catalog ID in RNA Add read-only access to the active catalog ID via `context.space_data.params.catalog_id` in the Asset Browser context. The UUID is exposed as string to Python. --- source/blender/makesrna/intern/rna_space.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 78c26df7be6..652d2545e67 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -39,6 +39,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_uuid.h" #include "DNA_action_types.h" #include "DNA_gpencil_types.h" @@ -3179,6 +3180,17 @@ static void rna_SpaceSpreadsheet_context_path_guess(SpaceSpreadsheet *sspreadshe WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL); } +static void rna_FileAssetSelectParams_catalog_id_get(PointerRNA *ptr, char *value) +{ + const FileAssetSelectParams *params = ptr->data; + BLI_uuid_format(value, params->catalog_id); +} + +static int rna_FileAssetSelectParams_catalog_id_length(PointerRNA *UNUSED(ptr)) +{ + return UUID_STRING_LEN - 1; +} + #else static const EnumPropertyItem dt_uv_items[] = { @@ -6611,6 +6623,14 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Asset Library", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + prop = RNA_def_property(srna, "catalog_id", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_FileAssetSelectParams_catalog_id_get", + "rna_FileAssetSelectParams_catalog_id_length", + NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Catalog UUID", "The UUID of the catalog shown in the browser"); + prop = RNA_def_property(srna, "import_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, asset_import_type_items); RNA_def_property_ui_text(prop, "Import Type", "Determine how the asset will be imported"); From dc30a9087c37ede31e3b7167e08995ac163999ef Mon Sep 17 00:00:00 2001 From: guitargeek Date: Fri, 1 Oct 2021 09:58:49 -0500 Subject: [PATCH 0434/1500] Geometry Nodes: Spline Length Input Node The Spline Length Input node provides a field containing the length of the current evaluated spline to the Point and Spline domains. Differential Revision: https://developer.blender.org/D12706 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 3 +- source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_geo_input_spline_length.cc | 109 ++++++++++++++++++ 7 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index d357e78d900..15d49072518 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -525,6 +525,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveFill"), NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), + NodeItem("GeometryNodeSplineLength"), NodeItem("GeometryNodeCurveSubdivide"), NodeItem("GeometryNodeCurveParameter"), NodeItem("GeometryNodeInputTangent"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index bfce3718f82..2f9204e6705 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1508,8 +1508,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_POINTS_TO_VERTICES 1094 #define GEO_NODE_CURVE_REVERSE 1095 #define GEO_NODE_PROXIMITY 1096 - #define GEO_NODE_CURVE_SUBDIVIDE 1097 +#define GEO_NODE_INPUT_SPLINE_LENGTH 1098 + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 2df9d2ab32c..c2846b7f6c8 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5767,6 +5767,7 @@ static void registerGeometryNodes() register_node_type_geo_input_normal(); register_node_type_geo_input_position(); register_node_type_geo_input_tangent(); + register_node_type_geo_input_spline_length(); register_node_type_geo_instance_on_points(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 7c243e8ce55..bd8dd237d8c 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -217,6 +217,7 @@ set(SRC geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_normal.cc geometry/nodes/node_geo_input_position.cc + geometry/nodes/node_geo_input_spline_length.cc geometry/nodes/node_geo_input_tangent.cc geometry/nodes/node_geo_instance_on_points.cc geometry/nodes/node_geo_is_viewport.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d4cde483a12..61ab923fd22 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -86,6 +86,7 @@ void register_node_type_geo_input_index(void); void register_node_type_geo_input_material(void); void register_node_type_geo_input_normal(void); void register_node_type_geo_input_position(void); +void register_node_type_geo_input_spline_length(void); void register_node_type_geo_input_tangent(void); void register_node_type_geo_instance_on_points(void); void register_node_type_geo_is_viewport(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index a65ff73ab89..c454fe55f57 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -345,6 +345,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") +DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "") DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "") DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc new file mode 100644 index 00000000000..b5f3e1b0c28 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -0,0 +1,109 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "BKE_spline.hh" + +namespace blender::nodes { + +static void geo_node_input_spline_length_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Length").field_source(); +} + +static const GVArray *construct_spline_length_gvarray(const CurveComponent &component, + const AttributeDomain domain, + ResourceScope &scope) +{ + const CurveEval *curve = component.get_for_read(); + if (curve == nullptr) { + return nullptr; + } + + Span splines = curve->splines(); + auto length_fn = [splines](int i) { return splines[i]->length(); }; + + if (domain == ATTR_DOMAIN_CURVE) { + return &scope.construct< + fn::GVArray_For_EmbeddedVArray>>( + splines.size(), splines.size(), length_fn); + } + if (domain == ATTR_DOMAIN_POINT) { + GVArrayPtr length = std::make_unique< + fn::GVArray_For_EmbeddedVArray>>( + splines.size(), splines.size(), length_fn); + return scope + .add_value(component.attribute_try_adapt_domain( + std::move(length), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT)) + .get(); + } + + return nullptr; +} + +class SplineLengthFieldInput final : public fn::FieldInput { + public: + SplineLengthFieldInput() : fn::FieldInput(CPPType::get(), "Spline Length") + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask UNUSED(mask), + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast(component); + return construct_spline_length_gvarray(curve_component, domain, scope); + } + } + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 3549623580; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast(&other) != nullptr; + } +}; + +static void geo_node_input_spline_length_exec(GeoNodeExecParams params) +{ + Field length_field{std::make_shared()}; + params.set_output("Length", std::move(length_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_spline_length() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_SPLINE_LENGTH, "Spline Length", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_spline_length_exec; + ntype.declare = blender::nodes::geo_node_input_spline_length_declare; + nodeRegisterType(&ntype); +} From eacdc0ab4a936c930ba5ae65931acf625f6254ba Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Fri, 1 Oct 2021 18:03:18 +0200 Subject: [PATCH 0435/1500] VSE: Draw active strips with a different color in the preview window --- .../editors/space_sequencer/sequencer_draw.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index ae392980069..dc5e11b6998 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -108,9 +108,9 @@ static Sequence *special_seq_update = NULL; void color3ubv_from_seq(const Scene *curscene, - const Sequence *seq, - const bool show_strip_color_tag, - uchar r_col[3]) + const Sequence *seq, + const bool show_strip_color_tag, + uchar r_col[3]) { if (show_strip_color_tag && (uint)seq->color_tag < SEQUENCE_COLOR_TOT && seq->color_tag != SEQUENCE_COLOR_NONE) { @@ -2616,7 +2616,7 @@ static int sequencer_draw_get_transform_preview_frame(Scene *scene) return preview_frame; } -static void seq_draw_image_origin_and_outline(const bContext *C, Sequence *seq) +static void seq_draw_image_origin_and_outline(const bContext *C, Sequence *seq, bool is_active_seq) { SpaceSeq *sseq = CTX_wm_space_seq(C); if ((seq->flag & SELECT) == 0) { @@ -2659,7 +2659,12 @@ static void seq_draw_image_origin_and_outline(const bContext *C, Sequence *seq) immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); float col[3]; - UI_GetThemeColor3fv(TH_SEQ_SELECTED, col); + if (is_active_seq) { + UI_GetThemeColor3fv(TH_SEQ_ACTIVE, col); + } + else { + UI_GetThemeColor3fv(TH_SEQ_SELECTED, col); + } immUniformColor3fv(col); immUniform1f("lineWidth", U.pixelsize); immBegin(GPU_PRIM_LINE_LOOP, 4); @@ -2753,8 +2758,9 @@ void sequencer_draw_preview(const bContext *C, if (!draw_backdrop && scene->ed != NULL) { SeqCollection *collection = SEQ_query_rendered_strips(&scene->ed->seqbase, timeline_frame, 0); Sequence *seq; + Sequence *active_seq = SEQ_select_active_get(scene); SEQ_ITERATOR_FOREACH (seq, collection) { - seq_draw_image_origin_and_outline(C, seq); + seq_draw_image_origin_and_outline(C, seq, seq == active_seq); } SEQ_collection_free(collection); } From 1fb364491bad983a47665b58ec3ea471bfe7d207 Mon Sep 17 00:00:00 2001 From: guitargeek Date: Fri, 1 Oct 2021 11:59:29 -0500 Subject: [PATCH 0436/1500] Geometry Nodes: Set Spline Type Node Field Update This update of the Set Spline Type node allows for a bool field to be used as the selection of the affected splines. Differential Revision: https://developer.blender.org/D12522 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 2 +- source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../legacy/node_geo_curve_spline_type.cc | 22 +- .../nodes/node_geo_curve_spline_type.cc | 308 ++++++++++++++++++ 8 files changed, 325 insertions(+), 12 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 15d49072518..5c16f393041 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -525,6 +525,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveFill"), NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), + NodeItem("GeometryNodeCurveSplineType"), NodeItem("GeometryNodeSplineLength"), NodeItem("GeometryNodeCurveSubdivide"), NodeItem("GeometryNodeCurveParameter"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 2f9204e6705..8dbb6046e3b 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1510,7 +1510,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_PROXIMITY 1096 #define GEO_NODE_CURVE_SUBDIVIDE 1097 #define GEO_NODE_INPUT_SPLINE_LENGTH 1098 - +#define GEO_NODE_CURVE_SPLINE_TYPE 1099 /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index c2846b7f6c8..8da66a99e6b 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5713,6 +5713,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_attribute_randomize(); register_node_type_geo_legacy_material_assign(); register_node_type_geo_legacy_select_by_material(); + register_node_type_geo_legacy_curve_spline_type(); register_node_type_geo_legacy_curve_reverse(); register_node_type_geo_legacy_curve_subdivide(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index bd8dd237d8c..9bfbc807409 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -209,6 +209,7 @@ set(SRC geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc geometry/nodes/node_geo_curve_sample.cc + geometry/nodes/node_geo_curve_spline_type.cc geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_trim.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 61ab923fd22..a5f00fedc33 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -33,6 +33,7 @@ void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); void register_node_type_geo_legacy_material_assign(void); void register_node_type_geo_legacy_select_by_material(void); +void register_node_type_geo_legacy_curve_spline_type(void); void register_node_type_geo_legacy_curve_reverse(void); void register_node_type_geo_legacy_curve_subdivide(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index c454fe55f57..1ea1eb76983 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -335,6 +335,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_prim DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "") DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc index 0ef107fd8a4..44522e990d9 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc @@ -25,21 +25,21 @@ namespace blender::nodes { -static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b) +static void geo_node_legacy_curve_spline_type_declare(NodeDeclarationBuilder &b) { b.add_input("Curve"); b.add_input("Selection"); b.add_output("Curve"); } -static void geo_node_curve_spline_type_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void geo_node_legacy_curve_spline_type_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) { uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE); } -static void geo_node_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node) +static void geo_node_legacy_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN( sizeof(NodeGeometryCurveSplineType), __func__); @@ -238,7 +238,7 @@ static SplinePtr convert_to_nurbs(const Spline &input) return {}; } -static void geo_node_curve_spline_type_exec(GeoNodeExecParams params) +static void geo_node_legacy_curve_spline_type_exec(GeoNodeExecParams params) { const NodeGeometryCurveSplineType *storage = (const NodeGeometryCurveSplineType *)params.node().storage; @@ -284,19 +284,19 @@ static void geo_node_curve_spline_type_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_curve_spline_type() +void register_node_type_geo_legacy_curve_spline_type() { static bNodeType ntype; geo_node_type_base( &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_spline_type_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec; - node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init); + ntype.declare = blender::nodes::geo_node_legacy_curve_spline_type_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_curve_spline_type_exec; + node_type_init(&ntype, blender::nodes::geo_node_legacy_curve_spline_type_init); node_type_storage(&ntype, "NodeGeometryCurveSplineType", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = blender::nodes::geo_node_curve_spline_type_layout; + ntype.draw_buttons = blender::nodes::geo_node_legacy_curve_spline_type_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc new file mode 100644 index 00000000000..ec72154db13 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -0,0 +1,308 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Curve"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Curve"); +} + +static void geo_node_curve_spline_type_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE); +} + +static void geo_node_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN( + sizeof(NodeGeometryCurveSplineType), __func__); + + data->spline_type = GEO_NODE_SPLINE_TYPE_POLY; + node->storage = data; +} + +template +static void scale_input_assign(const Span input, + const int scale, + const int offset, + const MutableSpan r_output) +{ + for (const int i : IndexRange(r_output.size())) { + r_output[i] = input[i * scale + offset]; + } +} + +template +static void scale_output_assign(const Span input, + const int scale, + const int offset, + const MutableSpan &r_output) +{ + for (const int i : IndexRange(input.size())) { + r_output[i * scale + offset] = input[i]; + } +} + +template +static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn) +{ + input_spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src = input_spline.attributes.get_for_read(attribute_id); + BLI_assert(src); + if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) { + BLI_assert_unreachable(); + return false; + } + std::optional dst = output_spline.attributes.get_for_write(attribute_id); + if (!dst) { + BLI_assert_unreachable(); + return false; + } + + copy_fn(*src, *dst); + + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr convert_to_poly_spline(const Spline &input) +{ + std::unique_ptr output = std::make_unique(); + output->resize(input.positions().size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + Spline::copy_base_settings(input, *output); + output->attributes = input.attributes; + return output; +} + +static SplinePtr poly_to_nurbs(const Spline &input) +{ + std::unique_ptr output = std::make_unique(); + output->resize(input.positions().size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + output->weights().fill(1.0f); + output->set_resolution(12); + output->set_order(4); + Spline::copy_base_settings(input, *output); + output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->attributes = input.attributes; + return output; +} + +static SplinePtr bezier_to_nurbs(const Spline &input) +{ + const BezierSpline &bezier_spline = static_cast(input); + std::unique_ptr output = std::make_unique(); + output->resize(input.size() * 3); + + scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions()); + scale_output_assign(input.radii(), 3, 0, output->radii()); + scale_output_assign(input.tilts(), 3, 0, output->tilts()); + + scale_output_assign(bezier_spline.positions(), 3, 1, output->positions()); + scale_output_assign(input.radii(), 3, 1, output->radii()); + scale_output_assign(input.tilts(), 3, 1, output->tilts()); + + scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions()); + scale_output_assign(input.radii(), 3, 2, output->radii()); + scale_output_assign(input.tilts(), 3, 2, output->tilts()); + + Spline::copy_base_settings(input, *output); + output->weights().fill(1.0f); + output->set_resolution(12); + output->set_order(4); + output->set_cyclic(input.is_cyclic()); + output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->attributes.reallocate(output->size()); + copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + scale_output_assign(src.typed(), 3, 0, dst.typed()); + scale_output_assign(src.typed(), 3, 1, dst.typed()); + scale_output_assign(src.typed(), 3, 2, dst.typed()); + }); + }); + return output; +} + +static SplinePtr poly_to_bezier(const Spline &input) +{ + std::unique_ptr output = std::make_unique(); + output->resize(input.size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + output->handle_types_left().fill(BezierSpline::HandleType::Vector); + output->handle_types_right().fill(BezierSpline::HandleType::Vector); + output->set_resolution(12); + Spline::copy_base_settings(input, *output); + output->attributes = input.attributes; + return output; +} + +static SplinePtr nurbs_to_bezier(const Spline &input) +{ + const NURBSpline &nurbs_spline = static_cast(input); + std::unique_ptr output = std::make_unique(); + output->resize(input.size() / 3); + scale_input_assign(input.positions(), 3, 1, output->positions()); + scale_input_assign(input.positions(), 3, 0, output->handle_positions_left()); + scale_input_assign(input.positions(), 3, 2, output->handle_positions_right()); + scale_input_assign(input.radii(), 3, 2, output->radii()); + scale_input_assign(input.tilts(), 3, 2, output->tilts()); + output->handle_types_left().fill(BezierSpline::HandleType::Align); + output->handle_types_right().fill(BezierSpline::HandleType::Align); + output->set_resolution(nurbs_spline.resolution()); + Spline::copy_base_settings(input, *output); + output->attributes.reallocate(output->size()); + copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + scale_input_assign(src.typed(), 3, 1, dst.typed()); + }); + }); + return output; +} + +static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) +{ + switch (input.type()) { + case Spline::Type::Bezier: + return input.copy(); + case Spline::Type::Poly: + return poly_to_bezier(input); + case Spline::Type::NURBS: + if (input.size() < 6) { + params.error_message_add( + NodeWarningType::Info, + TIP_("NURBS must have minimum of 6 points for Bezier Conversion")); + return input.copy(); + } + else { + if (input.size() % 3 != 0) { + params.error_message_add(NodeWarningType::Info, + TIP_("NURBS must have multiples of 3 points for full Bezier " + "conversion, curve truncated")); + } + return nurbs_to_bezier(input); + } + } + BLI_assert_unreachable(); + return {}; +} + +static SplinePtr convert_to_nurbs(const Spline &input) +{ + switch (input.type()) { + case Spline::Type::NURBS: + return input.copy(); + case Spline::Type::Bezier: + return bezier_to_nurbs(input); + case Spline::Type::Poly: + return poly_to_nurbs(input); + } + BLI_assert_unreachable(); + return {}; +} + +static void geo_node_curve_spline_type_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSplineType *storage = + (const NodeGeometryCurveSplineType *)params.node().storage; + const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type; + + GeometrySet geometry_set = params.extract_input("Curve"); + Field selection_field = params.extract_input>("Selection"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; + } + + const CurveComponent *curve_component = geometry_set.get_component_for_read(); + const CurveEval &curve = *curve_component->get_for_read(); + GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE}; + const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray &selection = selection_evaluator.get_evaluated(0); + + std::unique_ptr new_curve = std::make_unique(); + for (const int i : curve.splines().index_range()) { + if (selection[i]) { + switch (output_type) { + case GEO_NODE_SPLINE_TYPE_POLY: + new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i])); + break; + case GEO_NODE_SPLINE_TYPE_BEZIER: + new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params)); + break; + case GEO_NODE_SPLINE_TYPE_NURBS: + new_curve->add_spline(convert_to_nurbs(*curve.splines()[i])); + break; + } + } + else { + new_curve->add_spline(curve.splines()[i]->copy()); + } + } + new_curve->attributes = curve.attributes; + geometry_set.replace_curve(new_curve.release()); + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_spline_type() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_spline_type_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec; + node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init); + node_type_storage(&ntype, + "NodeGeometryCurveSplineType", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_curve_spline_type_layout; + + nodeRegisterType(&ntype); +} From 1476d35870452e603584be808a25b60d5ddd1c67 Mon Sep 17 00:00:00 2001 From: guitargeek Date: Fri, 1 Oct 2021 14:22:24 -0500 Subject: [PATCH 0437/1500] Geometry Nodes: Set Handle Type Node Field Update This update of the Set Handle Type node allows for a bool field to be used as the selection of the affected control point handles for bezier splines. If no bezier splines are provided a info message is shown. Differential Revision: https://developer.blender.org/D12526 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesrna/intern/rna_nodetree.c | 17 ++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 3 +- .../legacy/node_geo_curve_set_handles.cc | 2 +- .../nodes/node_geo_curve_set_handles.cc | 150 ++++++++++++++++++ 9 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 5c16f393041..ab810b54a69 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -529,6 +529,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeSplineLength"), NodeItem("GeometryNodeCurveSubdivide"), NodeItem("GeometryNodeCurveParameter"), + NodeItem("GeometryNodeCurveSetHandles"), NodeItem("GeometryNodeInputTangent"), NodeItem("GeometryNodeCurveSample"), NodeItem("GeometryNodeCurveFillet"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 8dbb6046e3b..0ad92f8d190 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1511,6 +1511,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_SUBDIVIDE 1097 #define GEO_NODE_INPUT_SPLINE_LENGTH 1098 #define GEO_NODE_CURVE_SPLINE_TYPE 1099 +#define GEO_NODE_CURVE_SET_HANDLES 1100 /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 8da66a99e6b..aeb43b4a017 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5709,6 +5709,7 @@ static void registerGeometryNodes() { register_node_type_geo_group(); + register_node_type_geo_legacy_curve_set_handles(); register_node_type_geo_legacy_attribute_proximity(); register_node_type_geo_legacy_attribute_randomize(); register_node_type_geo_legacy_material_assign(); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index ad1331faf2f..f7ba32cf639 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9664,6 +9664,23 @@ static void def_geo_curve_set_handles(StructRNA *srna) RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); + RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_legacy_curve_set_handles(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSetHandles", "storage"); + + prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "handle_type"); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles"); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 9bfbc807409..78b6ee1d7a6 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -209,6 +209,7 @@ set(SRC geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc geometry/nodes/node_geo_curve_sample.cc + geometry/nodes/node_geo_curve_set_handles.cc geometry/nodes/node_geo_curve_spline_type.cc geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_curve_to_mesh.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index a5f00fedc33..bb90c7b6c51 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -29,6 +29,7 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); void register_node_type_geo_custom_group(bNodeType *ntype); +void register_node_type_geo_legacy_curve_set_handles(void); void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); void register_node_type_geo_legacy_material_assign(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 1ea1eb76983..d328efe9cba 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -297,7 +297,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_ENDPOINTS, 0, "LEGACY_CURVE_ENDPOINTS", LegacyCurveEndpoints, "Curve Endpoints", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_legacy_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_legacy_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_TO_POINTS, def_geo_curve_to_points, "LEGACY_CURVE_TO_POINTS", LegacyCurveToPoints, "Curve to Points", "") @@ -338,6 +338,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RE DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc index 31c13134f79..339029336d9 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc @@ -126,7 +126,7 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params) } } // namespace blender::nodes -void register_node_type_geo_curve_set_handles() +void register_node_type_geo_legacy_curve_set_handles() { static bNodeType ntype; geo_node_type_base( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc new file mode 100644 index 00000000000..9e7ac60c29d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -0,0 +1,150 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_set_handles_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Curve"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Curve"); +} + +static void geo_node_curve_set_handles_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); +} + +static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN( + sizeof(NodeGeometryCurveSetHandles), __func__); + + data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; + data->mode = GEO_NODE_CURVE_HANDLE_LEFT; + node->storage = data; +} + +static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) +{ + switch (type) { + case GEO_NODE_CURVE_HANDLE_AUTO: + return BezierSpline::HandleType::Auto; + case GEO_NODE_CURVE_HANDLE_ALIGN: + return BezierSpline::HandleType::Align; + case GEO_NODE_CURVE_HANDLE_FREE: + return BezierSpline::HandleType::Free; + case GEO_NODE_CURVE_HANDLE_VECTOR: + return BezierSpline::HandleType::Vector; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +static void geo_node_curve_set_handles_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSetHandles *node_storage = + (NodeGeometryCurveSetHandles *)params.node().storage; + const GeometryNodeCurveHandleType type = (GeometryNodeCurveHandleType)node_storage->handle_type; + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode; + + GeometrySet geometry_set = params.extract_input("Curve"); + Field selection_field = params.extract_input>("Selection"); + + bool has_bezier_spline = false; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; + } + + /* Retrieve data for write access so we can avoid new allocations for the handles data. */ + CurveComponent &curve_component = geometry_set.get_component_for_write(); + CurveEval &curve = *curve_component.get_for_write(); + MutableSpan splines = curve.splines(); + + GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT}; + const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray &selection = selection_evaluator.get_evaluated(0); + + const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + int point_index = 0; + + for (SplinePtr &spline : splines) { + if (spline->type() != Spline::Type::Bezier) { + point_index += spline->positions().size(); + continue; + } + + has_bezier_spline = true; + BezierSpline &bezier_spline = static_cast(*spline); + if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + /* In this case the automatically calculated handle types need to be "baked", because + * they're possibly changing from a type that is calculated automatically to a type that + * is positioned manually. */ + bezier_spline.ensure_auto_handles(); + } + + for (int i_point : IndexRange(bezier_spline.size())) { + if (selection[point_index]) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + bezier_spline.handle_types_left()[i_point] = new_handle_type; + } + if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) { + bezier_spline.handle_types_right()[i_point] = new_handle_type; + } + } + point_index++; + } + bezier_spline.mark_cache_invalid(); + } + }); + if (!has_bezier_spline) { + params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); + } + params.set_output("Curve", std::move(geometry_set)); +} +} // namespace blender::nodes + +void register_node_type_geo_curve_set_handles() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_set_handles_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec; + node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init); + node_type_storage(&ntype, + "NodeGeometryCurveSetHandles", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_curve_set_handles_layout; + + nodeRegisterType(&ntype); +} From 12e8c783535c60b0117a6d49a3ed378966db193e Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 1 Oct 2021 23:22:04 +0200 Subject: [PATCH 0438/1500] Fix T91888: Pivot point settings shown in timeline Added to timeline by accident in f9e09819766d. --- release/scripts/startup/bl_ui/space_sequencer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 455f5e2d841..197e3efebda 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -189,8 +189,12 @@ class SEQUENCER_HT_header(Header): if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: row = layout.row(align=True) row.prop(sequencer_tool_settings, "overlap_mode", text="") + + if st.view_type == 'SEQUENCER_PREVIEW': row = layout.row(align=True) row.prop(sequencer_tool_settings, "pivot_point", text="", icon_only=True) + + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: row = layout.row(align=True) row.prop(tool_settings, "use_snap_sequencer", text="") sub = row.row(align=True) From 54927caf4fcf214428e2fcb05d378d96fde54025 Mon Sep 17 00:00:00 2001 From: Leon Leno Date: Sat, 2 Oct 2021 17:29:25 -0500 Subject: [PATCH 0439/1500] Geometry Nodes: Add side and fill segments to Cone/Cylinder nodes This commit extends the 'Cone' and 'Cylinder' mesh primitive nodes, with two inputs to control the segments along the side and in the fill. This makes the nodes more flexible and brings them more in line with the improved cube node. Differential Revision: https://developer.blender.org/D12463 --- source/blender/makesrna/intern/rna_nodetree.c | 4 +- .../nodes/geometry/node_geometry_util.hh | 4 +- .../nodes/node_geo_mesh_primitive_cone.cc | 957 +++++++++++------- .../nodes/node_geo_mesh_primitive_cylinder.cc | 48 +- 4 files changed, 617 insertions(+), 396 deletions(-) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index f7ba32cf639..20fcff58990 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10152,7 +10152,7 @@ static void def_geo_mesh_cylinder(StructRNA *srna) prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items); RNA_def_property_ui_text(prop, "Fill Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } static void def_geo_mesh_cone(StructRNA *srna) @@ -10164,7 +10164,7 @@ static void def_geo_mesh_cone(StructRNA *srna) prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items); RNA_def_property_ui_text(prop, "Fill Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } static void def_geo_mesh_line(StructRNA *srna) diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 015ac0de002..5896b5bd6cc 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -65,7 +65,9 @@ Mesh *create_grid_mesh(const int verts_x, Mesh *create_cylinder_or_cone_mesh(const float radius_top, const float radius_bottom, const float depth, - const int verts_num, + const int circle_segments, + const int side_segments, + const int fill_segments, const GeometryNodeMeshCircleFillType fill_type); Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 0d58476fc58..a710f9d6190 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -29,22 +29,15 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b) { - b.add_input("Vertices").default_value(32).min(3); + b.add_input("Vertices").default_value(32).min(3).max(512); + b.add_input("Side Segments").default_value(1).min(1).max(512); + b.add_input("Fill Segments").default_value(1).min(1).max(512); b.add_input("Radius Top").min(0.0f).subtype(PROP_DISTANCE); b.add_input("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); b.add_input("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); b.add_output("Geometry"); } -static void geo_node_mesh_primitive_cone_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); -} - static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN( @@ -55,139 +48,486 @@ static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *n node->storage = node_storage; } -static int vert_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +static void geo_node_mesh_primitive_cone_update(bNodeTree *UNUSED(ntree), bNode *node) { - int vert_total = 0; - if (!top_is_point) { - vert_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - vert_total++; - } + bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *rings_socket = vertices_socket->next; + bNodeSocket *fill_subdiv_socket = rings_socket->next; + + const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node->storage; + const GeometryNodeMeshCircleFillType fill_type = + static_cast(storage.fill_type); + const bool has_fill = fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; + nodeSetSocketAvailability(fill_subdiv_socket, has_fill); +} + +static void geo_node_mesh_primitive_cone_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +struct ConeConfig { + float radius_top; + float radius_bottom; + float height; + int circle_segments; + int side_segments; + int fill_segments; + GeometryNodeMeshCircleFillType fill_type; + + bool top_is_point; + bool bottom_is_point; + /* The cone tip and a triangle fan filling are topologically identical. + * This simplifies the logic in some cases. */ + bool top_has_center_vert; + bool bottom_has_center_vert; + + /* Helpful quantitites. */ + int tot_quad_rings; + int tot_edge_rings; + int tot_verts; + int tot_edges; + + /* Helpful vertex indices. */ + int first_vert; + int first_ring_verts_start; + int last_ring_verts_start; + int last_vert; + + /* Helpful edge indices. */ + int first_ring_edges_start; + int last_ring_edges_start; + int last_fan_edges_start; + int last_edge; + + ConeConfig(float radius_top, + float radius_bottom, + float depth, + int circle_segments, + int side_segments, + int fill_segments, + GeometryNodeMeshCircleFillType fill_type) + : radius_top(radius_top), + radius_bottom(radius_bottom), + height(0.5f * depth), + circle_segments(circle_segments), + side_segments(side_segments), + fill_segments(fill_segments), + fill_type(fill_type) + { + this->top_is_point = this->radius_top == 0.0f; + this->bottom_is_point = this->radius_bottom == 0.0f; + this->top_has_center_vert = this->top_is_point || + this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN; + this->bottom_has_center_vert = this->bottom_is_point || + this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN; + + this->tot_quad_rings = this->calculate_total_quad_rings(); + this->tot_edge_rings = this->calculate_total_edge_rings(); + this->tot_verts = this->calculate_total_verts(); + this->tot_edges = this->calculate_total_edges(); + + this->first_vert = 0; + this->first_ring_verts_start = this->top_has_center_vert ? 1 : first_vert; + this->last_vert = this->tot_verts - 1; + this->last_ring_verts_start = this->last_vert - this->circle_segments; + + this->first_ring_edges_start = this->top_has_center_vert ? this->circle_segments : 0; + this->last_ring_edges_start = this->first_ring_edges_start + + this->tot_quad_rings * this->circle_segments * 2; + this->last_fan_edges_start = this->tot_edges - this->circle_segments; + this->last_edge = this->tot_edges - 1; } - else { + + private: + int calculate_total_quad_rings(); + int calculate_total_edge_rings(); + int calculate_total_verts(); + int calculate_total_edges(); + + public: + int get_tot_corners() const; + int get_tot_faces() const; +}; + +int ConeConfig::calculate_total_quad_rings() +{ + if (top_is_point && bottom_is_point) { + return 0; + } + + int quad_rings = 0; + + if (!top_is_point) { + quad_rings += fill_segments - 1; + } + + quad_rings += (!top_is_point && !bottom_is_point) ? side_segments : (side_segments - 1); + + if (!bottom_is_point) { + quad_rings += fill_segments - 1; + } + + return quad_rings; +} + +int ConeConfig::calculate_total_edge_rings() +{ + if (top_is_point && bottom_is_point) { + return 0; + } + + int edge_rings = 0; + + if (!top_is_point) { + edge_rings += fill_segments; + } + + edge_rings += side_segments - 1; + + if (!bottom_is_point) { + edge_rings += fill_segments; + } + + return edge_rings; +} + +int ConeConfig::calculate_total_verts() +{ + if (top_is_point && bottom_is_point) { + return side_segments + 1; + } + + int vert_total = 0; + + if (top_has_center_vert) { vert_total++; } - if (!bottom_is_point) { - vert_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - vert_total++; - } + + if (!top_is_point) { + vert_total += circle_segments * fill_segments; } - else { + + vert_total += circle_segments * (side_segments - 1); + + if (!bottom_is_point) { + vert_total += circle_segments * fill_segments; + } + + if (bottom_has_center_vert) { vert_total++; } return vert_total; } -static int edge_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +int ConeConfig::calculate_total_edges() { if (top_is_point && bottom_is_point) { - return 1; + return side_segments; } int edge_total = 0; - if (!top_is_point) { - edge_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - edge_total += verts_num; - } + if (top_has_center_vert) { + edge_total += circle_segments; } - edge_total += verts_num; + edge_total += circle_segments * (tot_quad_rings * 2 + 1); - if (!bottom_is_point) { - edge_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - edge_total += verts_num; - } + if (bottom_has_center_vert) { + edge_total += circle_segments; } return edge_total; } -static int corner_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +int ConeConfig::get_tot_corners() const { if (top_is_point && bottom_is_point) { return 0; } int corner_total = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - corner_total += verts_num; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - corner_total += verts_num * 3; - } + + if (top_has_center_vert) { + corner_total += (circle_segments * 3); + } + else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += circle_segments; } - if (!top_is_point && !bottom_is_point) { - corner_total += verts_num * 4; - } - else { - corner_total += verts_num * 3; - } + corner_total += tot_quad_rings * (circle_segments * 4); - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - corner_total += verts_num; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - corner_total += verts_num * 3; - } + if (bottom_has_center_vert) { + corner_total += (circle_segments * 3); + } + else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += circle_segments; } return corner_total; } -static int face_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +int ConeConfig::get_tot_faces() const { if (top_is_point && bottom_is_point) { return 0; } int face_total = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - face_total++; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - face_total += verts_num; - } + if (top_has_center_vert) { + face_total += circle_segments; + } + else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + face_total++; } - face_total += verts_num; + face_total += tot_quad_rings * circle_segments; - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - face_total++; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - face_total += verts_num; - } + if (bottom_has_center_vert) { + face_total += circle_segments; + } + else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + face_total++; } return face_total; } -static void calculate_uvs(Mesh *mesh, - const bool top_is_point, - const bool bottom_is_point, - const int verts_num, - const GeometryNodeMeshCircleFillType fill_type) +static void calculate_cone_vertices(const MutableSpan &verts, const ConeConfig &config) +{ + Array circle(config.circle_segments); + const float angle_delta = 2.0f * (M_PI / static_cast(config.circle_segments)); + float angle = 0.0f; + for (const int i : IndexRange(config.circle_segments)) { + circle[i].x = std::cos(angle); + circle[i].y = std::sin(angle); + angle += angle_delta; + } + + int vert_index = 0; + + /* Top cone tip or triangle fan center. */ + if (config.top_has_center_vert) { + copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, config.height); + } + + /* Top fill including the outer edge of the fill. */ + if (!config.top_is_point) { + const float top_fill_radius_delta = config.radius_top / + static_cast(config.fill_segments); + for (const int i : IndexRange(config.fill_segments)) { + const float top_fill_radius = top_fill_radius_delta * (i + 1); + for (const int j : IndexRange(config.circle_segments)) { + const float x = circle[j].x * top_fill_radius; + const float y = circle[j].y * top_fill_radius; + copy_v3_fl3(verts[vert_index++].co, x, y, config.height); + } + } + } + + /* Rings along the side. */ + const float side_radius_delta = (config.radius_bottom - config.radius_top) / + static_cast(config.side_segments); + const float height_delta = 2.0f * config.height / static_cast(config.side_segments); + for (const int i : IndexRange(config.side_segments - 1)) { + const float ring_radius = config.radius_top + (side_radius_delta * (i + 1)); + const float ring_height = config.height - (height_delta * (i + 1)); + for (const int j : IndexRange(config.circle_segments)) { + const float x = circle[j].x * ring_radius; + const float y = circle[j].y * ring_radius; + copy_v3_fl3(verts[vert_index++].co, x, y, ring_height); + } + } + + /* Bottom fill including the outer edge of the fill. */ + if (!config.bottom_is_point) { + const float bottom_fill_radius_delta = config.radius_bottom / + static_cast(config.fill_segments); + for (const int i : IndexRange(config.fill_segments)) { + const float bottom_fill_radius = config.radius_bottom - (i * bottom_fill_radius_delta); + for (const int j : IndexRange(config.circle_segments)) { + const float x = circle[j].x * bottom_fill_radius; + const float y = circle[j].y * bottom_fill_radius; + copy_v3_fl3(verts[vert_index++].co, x, y, -config.height); + } + } + } + + /* Bottom cone tip or triangle fan center. */ + if (config.bottom_has_center_vert) { + copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, -config.height); + } +} + +static void calculate_cone_edges(const MutableSpan &edges, const ConeConfig &config) +{ + int edge_index = 0; + + /* Edges for top cone tip or triangle fan */ + if (config.top_has_center_vert) { + for (const int i : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = config.first_vert; + edge.v2 = config.first_ring_verts_start + i; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Rings and connecting edges between the rings. */ + for (const int i : IndexRange(config.tot_edge_rings)) { + const int this_ring_vert_start = config.first_ring_verts_start + (i * config.circle_segments); + const int next_ring_vert_start = this_ring_vert_start + config.circle_segments; + /* Edge rings. */ + for (const int j : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = this_ring_vert_start + j; + edge.v2 = this_ring_vert_start + ((j + 1) % config.circle_segments); + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + if (i == config.tot_edge_rings - 1) { + /* There is one fewer ring of connecting edges. */ + break; + } + /* Connecting edges. */ + for (const int j : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = this_ring_vert_start + j; + edge.v2 = next_ring_vert_start + j; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Edges for bottom triangle fan or tip. */ + if (config.bottom_has_center_vert) { + for (const int i : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = config.last_ring_verts_start + i; + edge.v2 = config.last_vert; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } +} + +static void calculate_cone_faces(const MutableSpan &loops, + const MutableSpan &polys, + const ConeConfig &config) +{ + int loop_index = 0; + int poly_index = 0; + + if (config.top_has_center_vert) { + /* Top cone tip or center triangle fan in the fill. */ + const int top_center_vert = 0; + const int top_fan_edges_start = 0; + + for (const int i : IndexRange(config.circle_segments)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 3; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = config.first_ring_verts_start + i; + loop_a.e = config.first_ring_edges_start + i; + MLoop &loop_b = loops[loop_index++]; + loop_b.v = config.first_ring_verts_start + ((i + 1) % config.circle_segments); + loop_b.e = top_fan_edges_start + ((i + 1) % config.circle_segments); + MLoop &loop_c = loops[loop_index++]; + loop_c.v = top_center_vert; + loop_c.e = top_fan_edges_start + i; + } + } + else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* Center n-gon in the fill. */ + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = config.circle_segments; + for (const int i : IndexRange(config.circle_segments)) { + MLoop &loop = loops[loop_index++]; + loop.v = i; + loop.e = i; + } + } + + /* Quads connect one edge ring to the next one. */ + if (config.tot_quad_rings > 0) { + for (const int i : IndexRange(config.tot_quad_rings)) { + const int this_ring_vert_start = config.first_ring_verts_start + + (i * config.circle_segments); + const int next_ring_vert_start = this_ring_vert_start + config.circle_segments; + + const int this_ring_edges_start = config.first_ring_edges_start + + (i * 2 * config.circle_segments); + const int next_ring_edges_start = this_ring_edges_start + (2 * config.circle_segments); + const int ring_connections_start = this_ring_edges_start + config.circle_segments; + + for (const int j : IndexRange(config.circle_segments)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 4; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = this_ring_vert_start + j; + loop_a.e = ring_connections_start + j; + MLoop &loop_b = loops[loop_index++]; + loop_b.v = next_ring_vert_start + j; + loop_b.e = next_ring_edges_start + j; + MLoop &loop_c = loops[loop_index++]; + loop_c.v = next_ring_vert_start + ((j + 1) % config.circle_segments); + loop_c.e = ring_connections_start + ((j + 1) % config.circle_segments); + MLoop &loop_d = loops[loop_index++]; + loop_d.v = this_ring_vert_start + ((j + 1) % config.circle_segments); + loop_d.e = this_ring_edges_start + j; + } + } + } + + if (config.bottom_has_center_vert) { + /* Bottom cone tip or center triangle fan in the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 3; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = config.last_ring_verts_start + i; + loop_a.e = config.last_fan_edges_start + i; + MLoop &loop_b = loops[loop_index++]; + loop_b.v = config.last_vert; + loop_b.e = config.last_fan_edges_start + (i + 1) % config.circle_segments; + MLoop &loop_c = loops[loop_index++]; + loop_c.v = config.last_ring_verts_start + (i + 1) % config.circle_segments; + loop_c.e = config.last_ring_edges_start + i; + } + } + else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* Center n-gon in the fill. */ + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = config.circle_segments; + + for (const int i : IndexRange(config.circle_segments)) { + /* Go backwards to reverse surface normal. */ + MLoop &loop = loops[loop_index++]; + loop.v = config.last_vert - i; + loop.e = config.last_edge - ((i + 1) % config.circle_segments); + } + } +} + +/** + * If the top is the cone tip or has a fill, it is unwrapped into a circle in the + * lower left quadrant of the UV. + * Likewise, if the bottom is the cone tip or has a fill, it is unwrapped into a circle + * in the lower right quadrant of the UV. + * If the mesh is a truncated cone or a cylinder, the side faces are unwrapped into + * a rectangle that fills the top half of the UV (or the entire UV, if there are no fills). + */ +static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config) { MeshComponent mesh_component; mesh_component.replace(mesh, GeometryOwnershipType::Editable); @@ -195,75 +535,110 @@ static void calculate_uvs(Mesh *mesh, mesh_component.attribute_try_get_for_output_only("uv_map", ATTR_DOMAIN_CORNER); MutableSpan uvs = uv_attribute.as_span(); - Array circle(verts_num); + Array circle(config.circle_segments); float angle = 0.0f; - const float angle_delta = 2.0f * M_PI / static_cast(verts_num); - for (const int i : IndexRange(verts_num)) { - circle[i].x = std::cos(angle) * 0.225f + 0.25f; - circle[i].y = std::sin(angle) * 0.225f + 0.25f; + const float angle_delta = 2.0f * M_PI / static_cast(config.circle_segments); + for (const int i : IndexRange(config.circle_segments)) { + circle[i].x = std::cos(angle) * 0.225f; + circle[i].y = std::sin(angle) * 0.225f; angle += angle_delta; } int loop_index = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i]; + + /* Left circle of the UV representing the top fill or top cone tip. */ + if (config.top_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) { + const float2 center_left(0.25f, 0.25f); + const float radius_factor_delta = 1.0f / (config.top_is_point ? + static_cast(config.side_segments) : + static_cast(config.fill_segments)); + const int left_circle_segment_count = config.top_is_point ? config.side_segments : + config.fill_segments; + + if (config.top_has_center_vert) { + /* Cone tip itself or triangle fan center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + uvs[loop_index++] = radius_factor_delta * circle[i] + center_left; + uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] + + center_left; + uvs[loop_index++] = center_left; } } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i]; - uvs[loop_index++] = circle[(i + 1) % verts_num]; - uvs[loop_index++] = float2(0.25f, 0.25f); + else if (!config.top_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* N-gon at the center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + uvs[loop_index++] = radius_factor_delta * circle[i] + center_left; + } + } + /* The rest of the top fill is made out of quad rings. */ + for (const int i : IndexRange(1, left_circle_segment_count - 1)) { + const float inner_radius_factor = i * radius_factor_delta; + const float outer_radius_factor = (i + 1) * radius_factor_delta; + for (const int j : IndexRange(config.circle_segments)) { + uvs[loop_index++] = inner_radius_factor * circle[j] + center_left; + uvs[loop_index++] = outer_radius_factor * circle[j] + center_left; + uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] + + center_left; + uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] + + center_left; } } } - /* Create side corners and faces. */ - if (!top_is_point && !bottom_is_point) { - const float bottom = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f; - /* Quads connect the top and bottom. */ - for (const int i : IndexRange(verts_num)) { - const float vert = static_cast(i); - uvs[loop_index++] = float2(vert / verts_num, bottom); - uvs[loop_index++] = float2(vert / verts_num, 1.0f); - uvs[loop_index++] = float2((vert + 1.0f) / verts_num, 1.0f); - uvs[loop_index++] = float2((vert + 1.0f) / verts_num, bottom); - } - } - else { - /* Triangles connect the top and bottom section. */ - if (!top_is_point) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f); - uvs[loop_index++] = float2(0.75f, 0.25f); - uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f); - } - } - else { - BLI_assert(!bottom_is_point); - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i]; - uvs[loop_index++] = circle[(i + 1) % verts_num]; - uvs[loop_index++] = float2(0.25f, 0.25f); + if (!config.top_is_point && !config.bottom_is_point) { + /* Mesh is a truncated cone or cylinder. The sides are unwrapped into a rectangle. */ + const float bottom = (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f; + const float x_delta = 1.0f / static_cast(config.circle_segments); + const float y_delta = (1.0f - bottom) / static_cast(config.side_segments); + + for (const int i : IndexRange(config.side_segments)) { + for (const int j : IndexRange(config.circle_segments)) { + uvs[loop_index++] = float2(j * x_delta, i * y_delta + bottom); + uvs[loop_index++] = float2(j * x_delta, (i + 1) * y_delta + bottom); + uvs[loop_index++] = float2((j + 1) * x_delta, (i + 1) * y_delta + bottom); + uvs[loop_index++] = float2((j + 1) * x_delta, i * y_delta + bottom); } } } - /* Create bottom corners and faces. */ - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - for (const int i : IndexRange(verts_num)) { + /* Right circle of the UV representing the bottom fill or bottom cone tip. */ + if (config.bottom_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) { + const float2 center_right(0.75f, 0.25f); + const float radius_factor_delta = 1.0f / (config.bottom_is_point ? + static_cast(config.side_segments) : + static_cast(config.fill_segments)); + const int right_circle_segment_count = config.bottom_is_point ? config.side_segments : + config.fill_segments; + + /* The bottom circle has to be created outside in to match the loop order. */ + for (const int i : IndexRange(right_circle_segment_count - 1)) { + const float outer_radius_factor = 1.0f - i * radius_factor_delta; + const float inner_radius_factor = 1.0f - (i + 1) * radius_factor_delta; + for (const int j : IndexRange(config.circle_segments)) { + uvs[loop_index++] = outer_radius_factor * circle[j] + center_right; + uvs[loop_index++] = inner_radius_factor * circle[j] + center_right; + uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] + + center_right; + uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] + + center_right; + } + } + + if (config.bottom_has_center_vert) { + /* Cone tip itself or triangle fan center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + uvs[loop_index++] = radius_factor_delta * circle[i] + center_right; + uvs[loop_index++] = center_right; + uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] + + center_right; + } + } + else if (!config.bottom_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* N-gon at the center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { /* Go backwards because of reversed face normal. */ - uvs[loop_index++] = circle[verts_num - 1 - i] + float2(0.5f, 0.0f); - } - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f); - uvs[loop_index++] = float2(0.75f, 0.25f); - uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f); + uvs[loop_index++] = radius_factor_delta * circle[config.circle_segments - 1 - i] + + center_right; } } } @@ -271,267 +646,56 @@ static void calculate_uvs(Mesh *mesh, uv_attribute.save(); } +static Mesh *create_vertex_mesh() +{ + /* Returns a mesh with a single vertex at the origin. */ + Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0); + copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f); + const short up[3] = {0, 0, SHRT_MAX}; + copy_v3_v3_short(mesh->mvert[0].no, up); + return mesh; +} + Mesh *create_cylinder_or_cone_mesh(const float radius_top, const float radius_bottom, const float depth, - const int verts_num, + const int circle_segments, + const int side_segments, + const int fill_segments, const GeometryNodeMeshCircleFillType fill_type) { - const bool top_is_point = radius_top == 0.0f; - const bool bottom_is_point = radius_bottom == 0.0f; - const float height = depth * 0.5f; + const ConeConfig config( + radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type); + /* Handle the case of a line / single point before everything else to avoid * the need to check for it later. */ - if (top_is_point && bottom_is_point) { - const bool single_vertex = height == 0.0f; - Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0); - copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, height)); - if (single_vertex) { - const short up[3] = {0, 0, SHRT_MAX}; - copy_v3_v3_short(mesh->mvert[0].no, up); - return mesh; + if (config.top_is_point && config.bottom_is_point) { + if (config.height == 0.0f) { + return create_vertex_mesh(); } - copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -height)); - mesh->medge[0].v1 = 0; - mesh->medge[0].v2 = 1; - mesh->medge[0].flag |= ME_LOOSEEDGE; - BKE_mesh_normals_tag_dirty(mesh); - return mesh; + const float z_delta = -2.0f * config.height / static_cast(config.side_segments); + const float3 start(0.0f, 0.0f, config.height); + const float3 delta(0.0f, 0.0f, z_delta); + return create_line_mesh(start, delta, config.tot_verts); } Mesh *mesh = BKE_mesh_new_nomain( - vert_total(fill_type, verts_num, top_is_point, bottom_is_point), - edge_total(fill_type, verts_num, top_is_point, bottom_is_point), - 0, - corner_total(fill_type, verts_num, top_is_point, bottom_is_point), - face_total(fill_type, verts_num, top_is_point, bottom_is_point)); + config.tot_verts, config.tot_edges, 0, config.get_tot_corners(), config.get_tot_faces()); BKE_id_material_eval_ensure_default_slot(&mesh->id); + MutableSpan verts{mesh->mvert, mesh->totvert}; MutableSpan loops{mesh->mloop, mesh->totloop}; MutableSpan edges{mesh->medge, mesh->totedge}; MutableSpan polys{mesh->mpoly, mesh->totpoly}; - /* Calculate vertex positions. */ - const int top_verts_start = 0; - const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1); - const float angle_delta = 2.0f * (M_PI / static_cast(verts_num)); - for (const int i : IndexRange(verts_num)) { - const float angle = i * angle_delta; - const float x = std::cos(angle); - const float y = std::sin(angle); - if (!top_is_point) { - copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, height)); - } - if (!bottom_is_point) { - copy_v3_v3(verts[bottom_verts_start + i].co, - float3(x * radius_bottom, y * radius_bottom, -height)); - } - } - if (top_is_point) { - copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height)); - } - if (bottom_is_point) { - copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -height)); - } - - /* Add center vertices for the triangle fans at the end. */ - const int top_center_vert_index = bottom_verts_start + (bottom_is_point ? 1 : verts_num); - const int bottom_center_vert_index = top_center_vert_index + (top_is_point ? 0 : 1); - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - if (!top_is_point) { - copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, height)); - } - if (!bottom_is_point) { - copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -height)); - } - } - - /* Create top edges. */ - const int top_edges_start = 0; - const int top_fan_edges_start = (!top_is_point && - fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ? - top_edges_start + verts_num : - top_edges_start; - if (!top_is_point) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[top_edges_start + i]; - edge.v1 = top_verts_start + i; - edge.v2 = top_verts_start + (i + 1) % verts_num; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[top_fan_edges_start + i]; - edge.v1 = top_center_vert_index; - edge.v2 = top_verts_start + i; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - } - } - - /* Create connecting edges. */ - const int connecting_edges_start = top_fan_edges_start + (!top_is_point ? verts_num : 0); - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[connecting_edges_start + i]; - edge.v1 = top_verts_start + (!top_is_point ? i : 0); - edge.v2 = bottom_verts_start + (!bottom_is_point ? i : 0); - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - - /* Create bottom edges. */ - const int bottom_edges_start = connecting_edges_start + verts_num; - const int bottom_fan_edges_start = (!bottom_is_point && - fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ? - bottom_edges_start + verts_num : - bottom_edges_start; - if (!bottom_is_point) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[bottom_edges_start + i]; - edge.v1 = bottom_verts_start + i; - edge.v2 = bottom_verts_start + (i + 1) % verts_num; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[bottom_fan_edges_start + i]; - edge.v1 = bottom_center_vert_index; - edge.v2 = bottom_verts_start + i; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - } - } - - /* Create top corners and faces. */ - int loop_index = 0; - int poly_index = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = verts_num; - - for (const int i : IndexRange(verts_num)) { - MLoop &loop = loops[loop_index++]; - loop.v = top_verts_start + i; - loop.e = top_edges_start + i; - } - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = top_verts_start + i; - loop_a.e = top_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = top_verts_start + (i + 1) % verts_num; - loop_b.e = top_fan_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = top_center_vert_index; - loop_c.e = top_fan_edges_start + i; - } - } - } - - /* Create side corners and faces. */ - if (!top_is_point && !bottom_is_point) { - /* Quads connect the top and bottom. */ - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 4; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = top_verts_start + i; - loop_a.e = connecting_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_verts_start + i; - loop_b.e = bottom_edges_start + i; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = bottom_verts_start + (i + 1) % verts_num; - loop_c.e = connecting_edges_start + (i + 1) % verts_num; - MLoop &loop_d = loops[loop_index++]; - loop_d.v = top_verts_start + (i + 1) % verts_num; - loop_d.e = top_edges_start + i; - } - } - else { - /* Triangles connect the top and bottom section. */ - if (!top_is_point) { - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = top_verts_start + i; - loop_a.e = connecting_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_verts_start; - loop_b.e = connecting_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = top_verts_start + (i + 1) % verts_num; - loop_c.e = top_edges_start + i; - } - } - else { - BLI_assert(!bottom_is_point); - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = bottom_verts_start + i; - loop_a.e = bottom_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_verts_start + (i + 1) % verts_num; - loop_b.e = connecting_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = top_verts_start; - loop_c.e = connecting_edges_start + i; - } - } - } - - /* Create bottom corners and faces. */ - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = verts_num; - - for (const int i : IndexRange(verts_num)) { - /* Go backwards to reverse surface normal. */ - MLoop &loop = loops[loop_index++]; - loop.v = bottom_verts_start + verts_num - 1 - i; - loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num; - } - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = bottom_verts_start + i; - loop_a.e = bottom_fan_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_center_vert_index; - loop_b.e = bottom_fan_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = bottom_verts_start + (i + 1) % verts_num; - loop_c.e = bottom_edges_start + i; - } - } - } + calculate_cone_vertices(verts, config); + calculate_cone_edges(edges, config); + calculate_cone_faces(loops, polys, config); + calculate_cone_uvs(mesh, config); BKE_mesh_normals_tag_dirty(mesh); - calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type); + calculate_cone_uvs(mesh, config); return mesh; } @@ -540,23 +704,37 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) { const bNode &node = params.node(); const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node.storage; - const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) storage.fill_type; - const int verts_num = params.extract_input("Vertices"); - if (verts_num < 3) { + const int circle_segments = params.extract_input("Vertices"); + if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); params.set_output("Geometry", GeometrySet()); return; } + const int side_segments = params.extract_input("Side Segments"); + if (side_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); + params.set_output("Geometry", GeometrySet()); + return; + } + + const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE; + const int fill_segments = no_fill ? 1 : params.extract_input("Fill Segments"); + if (fill_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); + params.set_output("Geometry", GeometrySet()); + return; + } + const float radius_top = params.extract_input("Radius Top"); const float radius_bottom = params.extract_input("Radius Bottom"); const float depth = params.extract_input("Depth"); Mesh *mesh = create_cylinder_or_cone_mesh( - radius_top, radius_bottom, depth, verts_num, fill_type); + radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type); /* Transform the mesh so that the base of the cone is at the origin. */ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); @@ -572,6 +750,7 @@ void register_node_type_geo_mesh_primitive_cone() geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cone_init); + node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_cone_update); node_type_storage( &ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cone_exec; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index 8c4defc3ca3..287ea896ade 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -32,8 +32,18 @@ static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) b.add_input("Vertices") .default_value(32) .min(3) - .max(4096) + .max(512) .description("The number of vertices around the circumference"); + b.add_input("Side Segments") + .default_value(1) + .min(1) + .max(512) + .description("The number of segments along the side"); + b.add_input("Fill Segments") + .default_value(1) + .min(1) + .max(512) + .description("The number of concentric segments of the fill"); b.add_input("Radius") .default_value(1.0f) .min(0.0f) @@ -66,6 +76,19 @@ static void geo_node_mesh_primitive_cylinder_init(bNodeTree *UNUSED(ntree), bNod node->storage = node_storage; } +static void geo_node_mesh_primitive_cylinder_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *rings_socket = vertices_socket->next; + bNodeSocket *fill_subdiv_socket = rings_socket->next; + + const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node->storage; + const GeometryNodeMeshCircleFillType fill_type = + static_cast(storage.fill_type); + const bool has_fill = fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; + nodeSetSocketAvailability(fill_subdiv_socket, has_fill); +} + static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) { const bNode &node = params.node(); @@ -76,15 +99,31 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) const float radius = params.extract_input("Radius"); const float depth = params.extract_input("Depth"); - const int verts_num = params.extract_input("Vertices"); - if (verts_num < 3) { + const int circle_segments = params.extract_input("Vertices"); + if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); params.set_output("Geometry", GeometrySet()); return; } + const int side_segments = params.extract_input("Side Segments"); + if (side_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); + params.set_output("Geometry", GeometrySet()); + return; + } + + const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE; + const int fill_segments = no_fill ? 1 : params.extract_input("Fill Segments"); + if (fill_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); + params.set_output("Geometry", GeometrySet()); + return; + } + /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ - Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type); + Mesh *mesh = create_cylinder_or_cone_mesh( + radius, radius, depth, circle_segments, side_segments, fill_segments, fill_type); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } @@ -96,6 +135,7 @@ void register_node_type_geo_mesh_primitive_cylinder() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_init); + node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_update); node_type_storage( &ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage); ntype.declare = blender::nodes::geo_node_mesh_primitive_cylinder_declare; From 34cf33eb12099d3a1940de070d7dc7e88e3bebc7 Mon Sep 17 00:00:00 2001 From: guitargeek Date: Sat, 2 Oct 2021 17:33:25 -0500 Subject: [PATCH 0440/1500] Geometry Nodes: Switch Node Fields Update This update of the Switch node allows for field compatible types to be switched through the node. This includes the following: Float, Int, Bool, String, Vector, and Color The remaining types are processed with the orginal code: Geometry, Object, Collection, Texture, and Material Because the old types require a diffent "switch" socket than the field types, versioning for old files is included to move links of those types to a new switch socket. Once fields of other types are supported, this node can be updated to support them as well. Differential Revision: https://developer.blender.org/D12642 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_300.c | 26 +++ .../nodes/geometry/nodes/node_geo_switch.cc | 196 +++++++++++++----- 3 files changed, 170 insertions(+), 54 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index b04bbdfb187..31ce1b124e9 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 31 +#define BLENDER_FILE_SUBVERSION 32 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 11b6ad9b8ea..5b436d59213 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -617,6 +617,32 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) do_versions_idproperty_ui_data(bmain); } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 32)) { + /* Update Switch Node Non-Fields switch input to Switch_001. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->tonode->type == GEO_NODE_SWITCH) { + if (STREQ(link->tosock->identifier, "Switch")) { + bNode *to_node = link->tonode; + + uint8_t mode = ((NodeSwitch *)to_node->storage)->input_type; + if (ELEM(mode, + SOCK_GEOMETRY, + SOCK_OBJECT, + SOCK_COLLECTION, + SOCK_TEXTURE, + SOCK_MATERIAL)) { + link->tosock = link->tosock->next; + } + } + } + } + } + } /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index ca857c4d2e3..05df927fb39 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -19,24 +19,37 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_material.h" + +#include "FN_multi_function_signature.hh" + namespace blender::nodes { static void geo_node_switch_declare(NodeDeclarationBuilder &b) { - b.add_input("Switch"); + b.add_input("Switch").default_value(false).supports_field(); + b.add_input("Switch", "Switch_001").default_value(false); + + b.add_input("False").supports_field(); + b.add_input("True").supports_field(); + b.add_input("False", "False_001").min(-100000).max(100000).supports_field(); + b.add_input("True", "True_001").min(-100000).max(100000).supports_field(); + b.add_input("False", "False_002").default_value(false).hide_value().supports_field(); + b.add_input("True", "True_002").default_value(true).hide_value().supports_field(); + b.add_input("False", "False_003").supports_field(); + b.add_input("True", "True_003").supports_field(); + b.add_input("False", "False_004") + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .supports_field(); + b.add_input("True", "True_004") + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .supports_field(); + b.add_input("False", "False_005").supports_field(); + b.add_input("True", "True_005").supports_field(); - b.add_input("False"); - b.add_input("True"); - b.add_input("False", "False_001").min(-100000).max(100000); - b.add_input("True", "True_001").min(-100000).max(100000); - b.add_input("False", "False_002"); - b.add_input("True", "True_002"); - b.add_input("False", "False_003"); - b.add_input("True", "True_003"); - b.add_input("False", "False_004").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input("True", "True_004").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input("False", "False_005"); - b.add_input("True", "True_005"); b.add_input("False", "False_006"); b.add_input("True", "True_006"); b.add_input("False", "False_007"); @@ -48,12 +61,12 @@ static void geo_node_switch_declare(NodeDeclarationBuilder &b) b.add_input("False", "False_010"); b.add_input("True", "True_010"); - b.add_output("Output"); - b.add_output("Output", "Output_001"); - b.add_output("Output", "Output_002"); - b.add_output("Output", "Output_003"); - b.add_output("Output", "Output_004"); - b.add_output("Output", "Output_005"); + b.add_output("Output").dependent_field(); + b.add_output("Output", "Output_001").dependent_field(); + b.add_output("Output", "Output_002").dependent_field(); + b.add_output("Output", "Output_003").dependent_field(); + b.add_output("Output", "Output_004").dependent_field(); + b.add_output("Output", "Output_005").dependent_field(); b.add_output("Output", "Output_006"); b.add_output("Output", "Output_007"); b.add_output("Output", "Output_008"); @@ -77,91 +90,168 @@ static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeSwitch *node_storage = (NodeSwitch *)node->storage; int index = 0; - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - nodeSetSocketAvailability( - socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type); - index++; + bNodeSocket *field_switch = (bNodeSocket *)node->inputs.first; + bNodeSocket *non_field_switch = (bNodeSocket *)field_switch->next; + + const bool fields_type = ELEM((eNodeSocketDatatype)node_storage->input_type, + SOCK_FLOAT, + SOCK_INT, + SOCK_BOOLEAN, + SOCK_VECTOR, + SOCK_RGBA, + SOCK_STRING); + + nodeSetSocketAvailability(field_switch, fields_type); + nodeSetSocketAvailability(non_field_switch, !fields_type); + + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) { + if (index <= 1) { + continue; + } + nodeSetSocketAvailability(socket, + socket->type == (eNodeSocketDatatype)node_storage->input_type); } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { nodeSetSocketAvailability(socket, socket->type == (eNodeSocketDatatype)node_storage->input_type); } } -template -static void output_input(GeoNodeExecParams ¶ms, - const bool input, - const StringRef input_suffix, - const StringRef output_identifier) +template class SwitchFieldsFunction : public fn::MultiFunction { + public: + SwitchFieldsFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Switch"}; + signature.single_input("Switch"); + signature.single_input("False"); + signature.single_input("True"); + signature.single_output("Output"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &switches = params.readonly_single_input(0, "Switch"); + const VArray &falses = params.readonly_single_input(1, "False"); + const VArray &trues = params.readonly_single_input(2, "True"); + MutableSpan values = params.uninitialized_single_output_if_required(3, "Output"); + for (int64_t i : mask) { + new (&values[i]) T(switches[i] ? trues[i] : falses[i]); + } + } +}; + +template void switch_fields(GeoNodeExecParams ¶ms, const StringRef suffix) { - const std::string name_a = "False" + input_suffix; - const std::string name_b = "True" + input_suffix; - if (input) { - params.set_input_unused(name_a); - if (params.lazy_require_input(name_b)) { + if (params.lazy_require_input("Switch")) { + return; + } + + const std::string name_false = "False" + suffix; + const std::string name_true = "True" + suffix; + const std::string name_output = "Output" + suffix; + + /* TODO: Allow for Laziness when the switch field is constant. */ + const bool require_false = params.lazy_require_input(name_false); + const bool require_true = params.lazy_require_input(name_true); + if (require_false | require_true) { + return; + } + + Field switches_field = params.extract_input>("Switch"); + Field falses_field = params.extract_input>(name_false); + Field trues_field = params.extract_input>(name_true); + + auto switch_fn = std::make_unique>(); + auto switch_op = std::make_shared(FieldOperation( + std::move(switch_fn), + {std::move(switches_field), std::move(falses_field), std::move(trues_field)})); + + params.set_output(name_output, Field(switch_op, 0)); +} + +template void switch_no_fields(GeoNodeExecParams ¶ms, const StringRef suffix) +{ + if (params.lazy_require_input("Switch_001")) { + return; + } + bool switch_value = params.get_input("Switch_001"); + + const std::string name_false = "False" + suffix; + const std::string name_true = "True" + suffix; + const std::string name_output = "Output" + suffix; + + if (switch_value) { + params.set_input_unused(name_false); + if (params.lazy_require_input(name_true)) { return; } - params.set_output(output_identifier, params.extract_input(name_b)); + params.set_output(name_output, params.extract_input(name_true)); } else { - params.set_input_unused(name_b); - if (params.lazy_require_input(name_a)) { + params.set_input_unused(name_true); + if (params.lazy_require_input(name_false)) { return; } - params.set_output(output_identifier, params.extract_input(name_a)); + params.set_output(name_output, params.extract_input(name_false)); } } static void geo_node_switch_exec(GeoNodeExecParams params) { - if (params.lazy_require_input("Switch")) { - return; - } const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage; - const bool input = params.get_input("Switch"); - switch ((eNodeSocketDatatype)storage.input_type) { + const eNodeSocketDatatype data_type = static_cast(storage.input_type); + + switch (data_type) { + case SOCK_FLOAT: { - output_input(params, input, "", "Output"); + switch_fields(params, ""); break; } case SOCK_INT: { - output_input(params, input, "_001", "Output_001"); + switch_fields(params, "_001"); break; } case SOCK_BOOLEAN: { - output_input(params, input, "_002", "Output_002"); + switch_fields(params, "_002"); break; } case SOCK_VECTOR: { - output_input(params, input, "_003", "Output_003"); + switch_fields(params, "_003"); break; } case SOCK_RGBA: { - output_input(params, input, "_004", "Output_004"); + switch_fields(params, "_004"); break; } case SOCK_STRING: { - output_input(params, input, "_005", "Output_005"); + switch_fields(params, "_005"); break; } case SOCK_GEOMETRY: { - output_input(params, input, "_006", "Output_006"); + switch_no_fields(params, "_006"); break; } case SOCK_OBJECT: { - output_input(params, input, "_007", "Output_007"); + switch_no_fields(params, "_007"); break; } case SOCK_COLLECTION: { - output_input(params, input, "_008", "Output_008"); + switch_no_fields(params, "_008"); break; } case SOCK_TEXTURE: { - output_input(params, input, "_009", "Output_009"); + switch_no_fields(params, "_009"); break; } case SOCK_MATERIAL: { - output_input(params, input, "_010", "Output_010"); + switch_no_fields(params, "_010"); break; } default: From c5c94e3eae74a7023c84cf0906cfa814c39f84dd Mon Sep 17 00:00:00 2001 From: Jarrett Johnson Date: Sat, 2 Oct 2021 20:04:45 -0500 Subject: [PATCH 0441/1500] Geometry Nodes: Add Rotate Euler Node This commit introduces the Rotate Euler function node which modifies an input euler rotation. The node replaces the "Point Rotate" node. Addresses T91375. Differential Revision: https://developer.blender.org/D12531 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesdna/DNA_node_types.h | 10 ++ source/blender/makesrna/intern/rna_nodetree.c | 45 ++++++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_function.h | 1 + source/blender/nodes/NOD_static_types.h | 3 +- .../function/nodes/node_fn_rotate_euler.cc | 138 ++++++++++++++++++ 9 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 source/blender/nodes/function/nodes/node_fn_rotate_euler.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index ab810b54a69..37d5c5997ad 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -624,6 +624,7 @@ geometry_node_categories = [ NodeItem("ShaderNodeClamp"), NodeItem("ShaderNodeMath"), NodeItem("FunctionNodeBooleanMath"), + NodeItem("FunctionNodeRotateEuler"), NodeItem("FunctionNodeFloatCompare"), NodeItem("FunctionNodeFloatToInt"), NodeItem("GeometryNodeSwitch"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 0ad92f8d190..c4c3733f3a9 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1529,6 +1529,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_STRING_SUBSTRING 1212 #define FN_NODE_INPUT_SPECIAL_CHARACTERS 1213 #define FN_NODE_RANDOM_VALUE 1214 +#define FN_NODE_ROTATE_EULER 1215 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index aeb43b4a017..73060caa2f8 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5824,6 +5824,7 @@ static void registerFunctionNodes() register_node_type_fn_input_string(); register_node_type_fn_input_vector(); register_node_type_fn_random_value(); + register_node_type_fn_rotate_euler(); register_node_type_fn_string_length(); register_node_type_fn_string_substring(); register_node_type_fn_value_to_string(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 35be6a4b48e..ea87cef1118 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -2032,6 +2032,11 @@ typedef enum GeometryNodeRotatePointsType { GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1, } GeometryNodeRotatePointsType; +typedef enum FunctionNodeRotatePointsType { + FN_NODE_ROTATE_EULER_TYPE_EULER = 0, + FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE = 1, +} FunctionNodeRotatePointsType; + typedef enum GeometryNodeAttributeVectorRotateMode { GEO_NODE_VECTOR_ROTATE_TYPE_AXIS = 0, GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1, @@ -2052,6 +2057,11 @@ typedef enum GeometryNodeRotatePointsSpace { GEO_NODE_POINT_ROTATE_SPACE_POINT = 1, } GeometryNodeRotatePointsSpace; +typedef enum FunctionNodeRotateEulerSpace { + FN_NODE_ROTATE_EULER_SPACE_OBJECT = 0, + FN_NODE_ROTATE_EULER_SPACE_POINT = 1, +} FunctionNodeRotateEulerSpace; + typedef enum GeometryNodeAlignRotationToVectorAxis { GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X = 0, GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Y = 1, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 20fcff58990..df1be412bc4 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9818,6 +9818,51 @@ static void def_geo_point_rotate(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_fn_rotate_euler(StructRNA *srna) +{ + static const EnumPropertyItem type_items[] = { + {FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE, + "AXIS_ANGLE", + ICON_NONE, + "Axis Angle", + "Rotate around an axis by an angle"}, + {FN_NODE_ROTATE_EULER_TYPE_EULER, + "EULER", + ICON_NONE, + "Euler", + "Rotate around the X, Y, and Z axes"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem space_items[] = { + {FN_NODE_ROTATE_EULER_SPACE_OBJECT, + "OBJECT", + ICON_NONE, + "Object", + "Rotate the input rotation in the local space of the object"}, + {FN_NODE_ROTATE_EULER_SPACE_POINT, + "POINT", + ICON_NONE, + "Point", + "Rotate the input rotation in its local space"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, type_items); + RNA_def_property_ui_text(prop, "Type", "Method used to describe the rotation"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom2"); + RNA_def_property_enum_items(prop, space_items); + RNA_def_property_ui_text(prop, "Space", "Base orientation of the points"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_align_rotation_to_vector(StructRNA *srna) { static const EnumPropertyItem axis_items[] = { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 78b6ee1d7a6..903a30dd383 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -142,6 +142,7 @@ set(SRC function/nodes/node_fn_input_string.cc function/nodes/node_fn_input_vector.cc function/nodes/node_fn_random_value.cc + function/nodes/node_fn_rotate_euler.cc function/nodes/node_fn_string_length.cc function/nodes/node_fn_string_substring.cc function/nodes/node_fn_value_to_string.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 9aa4c04000e..450e999bea4 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -29,6 +29,7 @@ void register_node_type_fn_input_special_characters(void); void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); void register_node_type_fn_random_value(void); +void register_node_type_fn_rotate_euler(void); void register_node_type_fn_string_length(void); void register_node_type_fn_string_substring(void); void register_node_type_fn_value_to_string(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index d328efe9cba..c13ab199691 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -272,9 +272,10 @@ DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARAC DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "") -DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") +DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "") DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") DefNode(FunctionNode, FN_NODE_STRING_SUBSTRING, 0, "STRING_SUBSTRING", StringSubstring, "String Substring", "") +DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc new file mode 100644 index 00000000000..cbae1648663 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc @@ -0,0 +1,138 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_function_util.hh" + +namespace blender::nodes { +static void fn_node_rotate_euler_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input("Rotation").subtype(PROP_EULER).hide_value(); + b.add_input("Rotate By").subtype(PROP_EULER); + b.add_input("Axis").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); + b.add_input("Angle").subtype(PROP_ANGLE); + b.add_output("Rotation"); +}; + +static void fn_node_rotate_euler_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *rotate_by_socket = static_cast(BLI_findlink(&node->inputs, 1)); + bNodeSocket *axis_socket = static_cast(BLI_findlink(&node->inputs, 2)); + bNodeSocket *angle_socket = static_cast(BLI_findlink(&node->inputs, 3)); + + nodeSetSocketAvailability(rotate_by_socket, + ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_EULER)); + nodeSetSocketAvailability(axis_socket, + ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE)); + nodeSetSocketAvailability(angle_socket, + ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE)); +} + +static void fn_node_rotate_euler_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static const fn::MultiFunction *get_multi_function(bNode &bnode) +{ + static fn::CustomMF_SI_SI_SO obj_euler_rot{ + "Rotate Euler by Euler/Object", [](const float3 &input, const float3 &rotation) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + eul_to_mat3(rot_mat, rotation); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, rot_mat, input_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + static fn::CustomMF_SI_SI_SI_SO obj_AA_rot{ + "Rotate Euler by AxisAngle/Object", + [](const float3 &input, const float3 &axis, float angle) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + axis_angle_to_mat3(rot_mat, axis, angle); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, rot_mat, input_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + static fn::CustomMF_SI_SI_SO point_euler_rot{ + "Rotate Euler by Euler/Point", [](const float3 &input, const float3 &rotation) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + eul_to_mat3(rot_mat, rotation); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, input_mat, rot_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + static fn::CustomMF_SI_SI_SI_SO point_AA_rot{ + "Rotate Euler by AxisAngle/Point", [](const float3 &input, const float3 &axis, float angle) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + axis_angle_to_mat3(rot_mat, axis, angle); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, input_mat, rot_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + short type = bnode.custom1; + short space = bnode.custom2; + if (type == FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE) { + return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_AA_rot : &point_AA_rot; + } + if (type == FN_NODE_ROTATE_EULER_TYPE_EULER) { + return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_euler_rot : &point_euler_rot; + } + BLI_assert_unreachable(); + return nullptr; +} + +static void fn_node_rotate_euler_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const fn::MultiFunction *fn = get_multi_function(builder.node()); + builder.set_matching_fn(fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_rotate_euler() +{ + static bNodeType ntype; + fn_node_type_base(&ntype, FN_NODE_ROTATE_EULER, "Rotate Euler", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_rotate_euler_declare; + ntype.draw_buttons = blender::nodes::fn_node_rotate_euler_layout; + node_type_update(&ntype, blender::nodes::fn_node_rotate_euler_update); + ntype.build_multi_function = blender::nodes::fn_node_rotate_euler_build_multi_function; + nodeRegisterType(&ntype); +} From 74f45ed9c55aaecf6fd90a8076f486ad4302515d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 3 Oct 2021 12:06:06 +1100 Subject: [PATCH 0442/1500] Cleanup: spelling in comments --- intern/cycles/blender/blender_display_driver.cpp | 2 +- intern/cycles/device/hip/kernel.cpp | 2 +- intern/cycles/device/optix/device_impl.cpp | 2 +- intern/cycles/kernel/kernel_shader.h | 4 ++-- intern/ghost/intern/GHOST_DisplayManagerSDL.cpp | 3 +-- intern/ghost/intern/GHOST_SystemX11.cpp | 12 ++++++------ intern/ghost/intern/GHOST_WindowX11.cpp | 6 +++--- intern/guardedalloc/intern/mallocn_guarded_impl.c | 7 +++---- .../blender/blenkernel/BKE_asset_catalog_path.hh | 4 ++-- source/blender/blenkernel/BKE_asset_library.h | 1 + source/blender/blenkernel/BKE_mesh.h | 5 ++--- source/blender/blenkernel/BKE_node.h | 13 +++++++------ .../blenkernel/intern/asset_catalog_path.cc | 2 +- source/blender/blenkernel/intern/key.c | 10 +++++----- source/blender/blenkernel/intern/particle.c | 6 +++--- source/blender/blenkernel/intern/softbody.c | 2 +- source/blender/blenlib/intern/fileops.c | 2 +- source/blender/blenlib/intern/kdtree_impl.h | 2 +- source/blender/blenlib/intern/list_sort_impl.h | 2 +- source/blender/blenlib/intern/scanfill.c | 12 ++++++------ source/blender/blenloader/BLO_readfile.h | 2 +- source/blender/blenloader/intern/versioning_300.c | 2 +- source/blender/bmesh/intern/bmesh_structure.c | 3 ++- source/blender/bmesh/intern/bmesh_walkers.c | 3 +-- source/blender/bmesh/tools/bmesh_bevel.c | 2 +- .../blender/draw/engines/gpencil/gpencil_engine.h | 6 +++--- source/blender/editors/animation/anim_ipo_utils.c | 7 ++++--- source/blender/editors/animation/drivers.c | 2 +- source/blender/editors/armature/pose_transform.c | 2 +- source/blender/editors/space_info/info_ops.c | 2 +- .../editors/transform/transform_convert_armature.c | 8 +++++--- .../editors/transform/transform_convert_object.c | 14 +++++++------- source/blender/gpu/intern/gpu_select.c | 4 ++-- .../blender/gpu/intern/gpu_select_sample_query.cc | 4 ++-- source/blender/ikplugin/intern/itasc_plugin.cpp | 2 +- source/blender/makesrna/intern/rna_nla.c | 4 ++-- .../modifiers/intern/MOD_solidify_extrude.c | 4 ++-- .../blender/nodes/composite/node_composite_tree.cc | 4 ++-- .../blender/nodes/geometry/node_geometry_tree.cc | 2 +- .../geometry/nodes/node_geo_mesh_primitive_cone.cc | 2 +- source/blender/nodes/intern/node_socket.cc | 2 +- source/blender/nodes/shader/node_shader_tree.c | 2 +- source/blender/nodes/texture/node_texture_tree.c | 2 +- .../nodes/texture/nodes/node_texture_curves.c | 2 +- source/blender/windowmanager/WM_types.h | 2 +- 45 files changed, 95 insertions(+), 93 deletions(-) diff --git a/intern/cycles/blender/blender_display_driver.cpp b/intern/cycles/blender/blender_display_driver.cpp index 5267f41eef7..f55a8ce8c4e 100644 --- a/intern/cycles/blender/blender_display_driver.cpp +++ b/intern/cycles/blender/blender_display_driver.cpp @@ -460,7 +460,7 @@ void BlenderDisplayDriver::draw(const Params ¶ms) /* Texture is requested to be cleared and was not yet cleared. * * Do early return which should be equivalent of drawing all-zero texture. - * Watchout for the lock though so that the clear happening during update is properly + * Watch out for the lock though so that the clear happening during update is properly * synchronized here. */ gl_context_mutex_.unlock(); return; diff --git a/intern/cycles/device/hip/kernel.cpp b/intern/cycles/device/hip/kernel.cpp index e0acd6f17c6..9ede8507a0c 100644 --- a/intern/cycles/device/hip/kernel.cpp +++ b/intern/cycles/device/hip/kernel.cpp @@ -28,7 +28,7 @@ void HIPDeviceKernels::load(HIPDevice *device) for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) { HIPDeviceKernel &kernel = kernels_[i]; - /* No megakernel used for GPU. */ + /* No mega-kernel used for GPU. */ if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { continue; } diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index 5f5eff53063..49d4e22143f 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -1419,7 +1419,7 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) } else { /* Can disable __anyhit__kernel_optix_visibility_test by default (except for thick curves, - * since it needs to filter out endcaps there). + * since it needs to filter out end-caps there). * It is enabled where necessary (visibility mask exceeds 8 bits or the other any-hit * programs like __anyhit__kernel_optix_shadow_all_hit) via OPTIX_RAY_FLAG_ENFORCE_ANYHIT. */ diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 8bad9c34d74..e7133724c85 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -186,8 +186,8 @@ ccl_device_inline float _shader_bsdf_multi_eval(const KernelGlobals *kg, float sum_sample_weight, const uint light_shader_flags) { - /* this is the veach one-sample model with balance heuristic, some pdf - * factors drop out when using balance heuristic weighting */ + /* This is the veach one-sample model with balance heuristic, + * some PDF factors drop out when using balance heuristic weighting. */ for (int i = 0; i < sd->num_closure; i++) { const ShaderClosure *sc = &sd->closure[i]; diff --git a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp index 5b026eb1632..09b2e4dfe2b 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp +++ b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp @@ -101,8 +101,7 @@ GHOST_TSuccess GHOST_DisplayManagerSDL::setCurrentDisplaySetting( uint8_t display, const GHOST_DisplaySetting &setting) { /* - * Mode switching code ported from Quake 2 version 3.21 and bzflag version - * 2.4.0: + * Mode switching code ported from Quake 2 version 3.21 and BZFLAG version 2.4.0: * ftp://ftp.idsoftware.com/idstuff/source/q2source-3.21.zip * See linux/gl_glx.c:GLimp_SetMode * http://wiki.bzflag.org/BZFlag_Source diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index c87b745cf40..86b4245ca67 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -1528,13 +1528,13 @@ void GHOST_SystemX11::processEvent(XEvent *xe) window->GetTabletData().Pressure = axis_value / ((float)xtablet.PressureLevels); } - /* the (short) cast and the & 0xffff is bizarre and unexplained anywhere, - * but I got garbage data without it. Found it in the xidump.c source --matt + /* NOTE(@broken): the (short) cast and the & 0xffff is bizarre and unexplained anywhere, + * but I got garbage data without it. Found it in the `xidump.c` source. * - * The '& 0xffff' just truncates the value to its two lowest bytes, this probably means - * some drivers do not properly set the whole int value? Since we convert to float - * afterward, I don't think we need to cast to short here, but do not have a device to - * check this. --mont29 + * NOTE(@mont29): The '& 0xffff' just truncates the value to its two lowest bytes, + * this probably means some drivers do not properly set the whole int value? + * Since we convert to float afterward, + * I don't think we need to cast to short here, but do not have a device to check this. */ if (AXIS_VALUE_GET(3, axis_value)) { window->GetTabletData().Xtilt = (short)(axis_value & 0xffff) / diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index de389951613..8b44403c598 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -1092,9 +1092,9 @@ GHOST_TSuccess GHOST_WindowX11::setOrder(GHOST_TWindowOrder order) XWindowAttributes attr; Atom atom; - /* We use both XRaiseWindow and _NET_ACTIVE_WINDOW, since some - * window managers ignore the former (e.g. kwin from kde) and others - * don't implement the latter (e.g. fluxbox pre 0.9.9) */ + /* We use both #XRaiseWindow and #_NET_ACTIVE_WINDOW, since some + * window managers ignore the former (e.g. KWIN from KDE) and others + * don't implement the latter (e.g. FLUXBOX before 0.9.9). */ XRaiseWindow(m_display, m_window); diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c index 98a8553a3eb..bba72c907eb 100644 --- a/intern/guardedalloc/intern/mallocn_guarded_impl.c +++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c @@ -89,7 +89,7 @@ typedef struct localListBase { void *first, *last; } localListBase; -/* note: keep this struct aligned (e.g., irix/gcc) - Hos */ +/* NOTE(@hos): keep this struct aligned (e.g., IRIX/GCC). */ typedef struct MemHead { int tag1; size_t len; @@ -98,9 +98,8 @@ typedef struct MemHead { const char *nextname; int tag2; short pad1; - short alignment; /* if non-zero aligned alloc was used - * and alignment is stored here. - */ + /* if non-zero aligned allocation was used and alignment is stored here. */ + short alignment; #ifdef DEBUG_MEMCOUNTER int _count; #endif diff --git a/source/blender/blenkernel/BKE_asset_catalog_path.hh b/source/blender/blenkernel/BKE_asset_catalog_path.hh index 68b9e943e94..328625b1bfa 100644 --- a/source/blender/blenkernel/BKE_asset_catalog_path.hh +++ b/source/blender/blenkernel/BKE_asset_catalog_path.hh @@ -39,8 +39,8 @@ namespace blender::bke { * make things easy to save in the colon-delimited Catalog Definition File format. * * The path of a catalog determines where in the catalog hierarchy the catalog is shown. Examples - * are "Characters/Ellie/Poses/Hand" or "Kitbash/City/Skyscrapers". The path looks like a - * filesystem path, with a few differences: + * are "Characters/Ellie/Poses/Hand" or "Kit_bash/City/Skyscrapers". The path looks like a + * file-system path, with a few differences: * * - Only slashes are used as path component separators. * - All paths are absolute, so there is no need for a leading slash. diff --git a/source/blender/blenkernel/BKE_asset_library.h b/source/blender/blenkernel/BKE_asset_library.h index 04486a7c132..3ddfbb1415e 100644 --- a/source/blender/blenkernel/BKE_asset_library.h +++ b/source/blender/blenkernel/BKE_asset_library.h @@ -21,6 +21,7 @@ #pragma once struct Main; +// #ifdef __cplusplus extern "C" { diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index b0a8fee1178..f44d35c62a3 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -651,9 +651,8 @@ extern void (*BKE_mesh_batch_cache_free_cb)(struct Mesh *me); /* Inlines */ -/* Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.h, - * but I don't want to force every user of BKE_mesh.h to also include that file. - * ~~ Sybren */ +/* NOTE(@sybren): Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.h, + * but I don't want to force every user of BKE_mesh.h to also include that file. */ BLI_INLINE int BKE_mesh_origindex_mface_mpoly(const int *index_mf_to_mpoly, const int *index_mp_to_orig, const int i) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index c4c3733f3a9..a81a620ab12 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -417,7 +417,7 @@ typedef struct bNodeTreeType { void (*local_sync)(struct bNodeTree *localtree, struct bNodeTree *ntree); void (*local_merge)(struct Main *bmain, struct bNodeTree *localtree, struct bNodeTree *ntree); - /* Tree update. Overrides nodetype->updatetreefunc! */ + /* Tree update. Overrides `nodetype->updatetreefunc` ! */ void (*update)(struct bNodeTree *ntree); bool (*validate_link)(struct bNodeTree *ntree, struct bNodeLink *link); @@ -443,7 +443,7 @@ void ntreeTypeFreeLink(const struct bNodeTreeType *nt); bool ntreeIsRegistered(struct bNodeTree *ntree); struct GHashIterator *ntreeTypeGetIterator(void); -/* helper macros for iterating over tree types */ +/* Helper macros for iterating over tree types. */ #define NODE_TREE_TYPES_BEGIN(ntype) \ { \ GHashIterator *__node_tree_type_iter__ = ntreeTypeGetIterator(); \ @@ -548,7 +548,7 @@ void nodeUnregisterType(struct bNodeType *ntype); bool nodeTypeUndefined(struct bNode *node); struct GHashIterator *nodeTypeGetIterator(void); -/* helper macros for iterating over node types */ +/* Helper macros for iterating over node types. */ #define NODE_TYPES_BEGIN(ntype) \ { \ GHashIterator *__node_type_iter__ = nodeTypeGetIterator(); \ @@ -574,7 +574,7 @@ const char *nodeStaticSocketType(int type, int subtype); const char *nodeStaticSocketInterfaceType(int type, int subtype); const char *nodeStaticSocketLabel(int type, int subtype); -/* helper macros for iterating over node types */ +/* Helper macros for iterating over node types. */ #define NODE_SOCKET_TYPES_BEGIN(stype) \ { \ GHashIterator *__node_socket_type_iter__ = nodeSocketTypeGetIterator(); \ @@ -746,7 +746,8 @@ int BKE_node_clipboard_get_type(void); /* Node Instance Hash */ typedef struct bNodeInstanceHash { - GHash *ghash; /* XXX should be made a direct member, GHash allocation needs to support it */ + /** XXX should be made a direct member, #GHash allocation needs to support it */ + GHash *ghash; } bNodeInstanceHash; typedef void (*bNodeInstanceValueFP)(void *value); @@ -1347,7 +1348,7 @@ void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, const bNode *node, char *r_prefix, size_t prefix_len); -/* Update the runtime layer names with the cryptomatte layer names of the references +/* Update the runtime layer names with the crypto-matte layer names of the references * render layer or image. */ void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc index 689a572f80b..85b8969cb8c 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path.cc @@ -219,7 +219,7 @@ AssetCatalogPath AssetCatalogPath::rebase(const AssetCatalogPath &from_path, return to_path; } - /* When from_path = "abcd", we need to skip "abcd/" to get the rest of the path, hence the +1. */ + /* When from_path = "test", we need to skip "test/" to get the rest of the path, hence the +1. */ const StringRef suffix = StringRef(this->path_).substr(from_path.length() + 1); const AssetCatalogPath path_suffix(suffix); return to_path / path_suffix; diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 44fc86877a7..c09fcf0715e 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -1904,7 +1904,7 @@ KeyBlock *BKE_keyblock_add_ctime(Key *key, const char *name, const bool do_force return kb; } -/* only the active keyblock */ +/* Only the active key-block. */ KeyBlock *BKE_keyblock_from_object(Object *ob) { Key *key = BKE_key_from_object(ob); @@ -2247,7 +2247,7 @@ void BKE_keyblock_convert_to_mesh(KeyBlock *kb, Mesh *me) * Computes normals (vertices, polygons and/or loops ones) of given mesh for given shape key. * * \param kb: the KeyBlock to use to compute normals. - * \param mesh: the Mesh to apply keyblock to. + * \param mesh: the Mesh to apply key-block to. * \param r_vertnors: if non-NULL, an array of vectors, same length as number of vertices. * \param r_polynors: if non-NULL, an array of vectors, same length as number of polygons. * \param r_loopnors: if non-NULL, an array of vectors, same length as number of loops. @@ -2345,7 +2345,7 @@ void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, const float (*ve return; } - /* Copy coords to keyblock */ + /* Copy coords to key-block. */ if (ELEM(ob->type, OB_MESH, OB_LATTICE)) { for (a = 0; a < tot; a++, fp += 3, co++) { copy_v3_v3(fp, *co); @@ -2405,7 +2405,7 @@ void BKE_keyblock_convert_from_vertcos(Object *ob, KeyBlock *kb, const float (*v kb->data = MEM_mallocN(tot * elemsize, __func__); - /* Copy coords to keyblock */ + /* Copy coords to key-block. */ BKE_keyblock_update_from_vertcos(ob, kb, vertCos); } @@ -2594,7 +2594,7 @@ bool BKE_keyblock_move(Object *ob, int org_index, int new_index) } /** - * Check if given keyblock (as index) is used as basis by others in given key. + * Check if given key-block (as index) is used as basis by others in given key. */ bool BKE_keyblock_is_basis(Key *key, const int index) { diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 50b0fb1c9f5..7b2a1af7086 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -4619,11 +4619,11 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, pind.cache = cached ? psys->pointcache : NULL; pind.epoint = NULL; pind.bspline = (psys->part->flag & PART_HAIR_BSPLINE); - /* pind.dm disabled in editmode means we don't get effectors taken into - * account when subdividing for instance */ + /* `pind.dm` disabled in edit-mode means we don't get effectors taken into + * account when subdividing for instance. */ pind.mesh = psys_in_edit_mode(sim->depsgraph, psys) ? NULL : - psys->hair_out_mesh; /* XXX Sybren EEK */ + psys->hair_out_mesh; /* XXX(@sybren) EEK. */ init_particle_interpolation(sim->ob, psys, pa, &pind); do_particle_interpolation(psys, p, pa, t, &pind, state); diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index fbc781f5eb9..b7eb9d31b23 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -2295,7 +2295,7 @@ static void softbody_calc_forces( sb_sfesf_threads_run(depsgraph, scene, ob, timenow, sb->totspring, NULL); } - /* after spring scan because it uses Effoctors too */ + /* After spring scan because it uses effectors too. */ ListBase *effectors = BKE_effectors_create(depsgraph, ob, NULL, sb->effector_weights, false); if (do_deflector) { diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 88fb67c5502..2ef4d1093a8 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -761,7 +761,7 @@ static int recursive_operation(const char *startfrom, # endif if (is_dir) { - /* recursively dig into a subfolder */ + /* Recurse into sub-directories. */ ret = recursive_operation( from_path, to_path, callback_dir_pre, callback_file, callback_dir_post); } diff --git a/source/blender/blenlib/intern/kdtree_impl.h b/source/blender/blenlib/intern/kdtree_impl.h index 0c9de0aa128..0b47be1f7ea 100644 --- a/source/blender/blenlib/intern/kdtree_impl.h +++ b/source/blender/blenlib/intern/kdtree_impl.h @@ -190,7 +190,7 @@ static uint kdtree_balance(KDTreeNode *nodes, uint nodes_len, uint axis, const u } } - /* set node and sort subnodes */ + /* Set node and sort sub-nodes. */ node = &nodes[median]; node->d = axis; axis = (axis + 1) % KD_DIMS; diff --git a/source/blender/blenlib/intern/list_sort_impl.h b/source/blender/blenlib/intern/list_sort_impl.h index 680044f9ccb..71f7f0e29a8 100644 --- a/source/blender/blenlib/intern/list_sort_impl.h +++ b/source/blender/blenlib/intern/list_sort_impl.h @@ -202,7 +202,7 @@ BLI_INLINE list_node *sweep_up(struct SortInfo *si, list_node *list, unsigned in } /** - * The 'ranks' array essentially captures the recursion stack of a mergesort. + * The 'ranks' array essentially captures the recursion stack of a merge-sort. * The merge tree is built in a bottom-up manner. The control loop for * updating the 'ranks' array is analogous to incrementing a binary integer, * and the `O(n)` time for counting `upto` n translates to `O(n)` merges when diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c index f0cf19bf508..8845167f536 100644 --- a/source/blender/blenlib/intern/scanfill.c +++ b/source/blender/blenlib/intern/scanfill.c @@ -1040,13 +1040,13 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const } /* CURRENT STATUS: - * - eve->f :1 = available in edges - * - eve->poly_nr :polynumber - * - eve->edge_tot :amount of edges connected to vertex - * - eve->tmp.v :store! original vertex number + * - `eve->f`: 1 = available in edges. + * - `eve->poly_nr`: poly-number. + * - `eve->edge_tot`: amount of edges connected to vertex. + * - `eve->tmp.v`: store! original vertex number. * - * - eed->f :1 = boundary edge (optionally set by caller) - * - eed->poly_nr :poly number + * - `eed->f`: 1 = boundary edge (optionally set by caller). + * - `eed->poly_nr`: poly number. */ /* STEP 3: MAKE POLYFILL STRUCT */ diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index c6637b17d47..fa29b09af02 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -121,7 +121,7 @@ typedef struct BlendFileReadReport { int proxies_to_lib_overrides_success; /* Number of proxies that failed to convert to library overrides. */ int proxies_to_lib_overrides_failures; - /* Number of VSE strips that were not read because were in non-supported channels. */ + /* Number of sequencer strips that were not read because were in non-supported channels. */ int vse_strips_skipped; } count; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 5b436d59213..95fa3058931 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1695,7 +1695,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Show vse color tags by default. */ + /* Show sequencer color tags by default. */ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c index d5d72cd4ba3..1f1ad0bae5b 100644 --- a/source/blender/bmesh/intern/bmesh_structure.c +++ b/source/blender/bmesh/intern/bmesh_structure.c @@ -86,7 +86,8 @@ void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src) /** * \section bm_cycles BMesh Cycles - * (this is somewhat outdate, though bits of its API are still used) - joeedh + * + * NOTE(@joeedh): this is somewhat outdated, though bits of its API are still used. * * Cycles are circular doubly linked lists that form the basis of adjacency * information in the BME modeler. Full adjacency relations can be derived diff --git a/source/blender/bmesh/intern/bmesh_walkers.c b/source/blender/bmesh/intern/bmesh_walkers.c index 8bdf205babd..b8fdd534842 100644 --- a/source/blender/bmesh/intern/bmesh_walkers.c +++ b/source/blender/bmesh/intern/bmesh_walkers.c @@ -31,8 +31,7 @@ #include "bmesh_walkers_private.h" /** - * - joeedh - - * design notes: + * NOTE(@joeedh): Details on design. * * original design: walkers directly emulation recursive functions. * functions save their state onto a worklist, and also add new states diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 0fe687da44e..1f759e9ef43 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -111,7 +111,7 @@ typedef struct EdgeHalf { bool is_bev; /** Is e->v2 the vertex at this end? */ bool is_rev; - /** Is e a seam for custom loopdata (e.g., UVs)? */ + /** Is e a seam for custom loop-data (e.g., UV's). */ bool is_seam; /** Used during the custom profile orientation pass. */ bool visited_rpo; diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 34fe29055d6..674aca29662 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -298,14 +298,14 @@ typedef struct GPENCIL_PrivateData { /* Current frame */ int cfra; /* If we are rendering for final render (F12). - * NOTE: set to false for viewport and opengl rendering (including VSE scene rendering), but set - * to true when rendering in `OB_RENDER` shading mode (viewport or opengl rendering) */ + * NOTE: set to false for viewport and opengl rendering (including sequencer scene rendering), + * but set to true when rendering in #OB_RENDER shading mode (viewport or opengl rendering). */ bool is_render; /* If we are in viewport display (used for VFX). */ bool is_viewport; /* True in selection and auto_depth drawing */ bool draw_depth_only; - /* Is shading set to wireframe. */ + /* Is shading set to wire-frame. */ bool draw_wireframe; /* Used by the depth merge step. */ int is_stroke_order_3d; diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index 33b4882927a..6fe32699907 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -126,9 +126,10 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) structname = RNA_struct_ui_name(ptr.type); } - /* For the VSE, a strip's 'Transform' or 'Crop' is a nested (under Sequence) struct, but - * displaying the struct name alone is no meaningful information (and also cannot be - * filtered well), same for modifiers. So display strip name alongside as well. */ + /* For the sequencer, a strip's 'Transform' or 'Crop' is a nested (under Sequence) + * struct, but displaying the struct name alone is no meaningful information + * (and also cannot be filtered well), same for modifiers. + * So display strip name alongside as well. */ if (GS(ptr.owner_id->name) == ID_SCE) { char stripname[256]; if (BLI_str_quoted_substr( diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index bfaa76b3bf9..dbf379971fa 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -1115,7 +1115,7 @@ static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent * } /* 2) Show editing panel for setting up this driver */ - /* TODO: Use a different one from the editing popever, so we can have the single/all toggle? */ + /* TODO: Use a different one from the editing popover, so we can have the single/all toggle? */ UI_popover_panel_invoke(C, "GRAPH_PT_drivers_popover", true, op->reports); } diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 3798ca308ed..279f79ac44b 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -1184,7 +1184,7 @@ static int pose_clear_transform_generic_exec(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) { - /* XXX: UGLY HACK (for autokey + clear transforms) */ + /* XXX: UGLY HACK (for auto-key + clear transforms). */ Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter); ListBase dsources = {NULL, NULL}; bool changed = false; diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index a99396ecdf0..8e37e5fe9a8 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -564,7 +564,7 @@ void FILE_OT_find_missing_files(wmOperatorType *ot) /** \name Report Box Operator * \{ */ -/* NOTE(matt): Hard to decide whether to keep this as an operator, +/* NOTE(@broken): Hard to decide whether to keep this as an operator, * or turn it into a hard_coded UI control feature, * handling TIMER events for all regions in `interface_handlers.c`. * Not sure how good that is to be accessing UI data from diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 8f896512410..1bbbbc6294e 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -1496,8 +1496,10 @@ static void bone_children_clear_transflag(int mode, short around, ListBase *lb) } } -/* Sets transform flags in the bones. - * Returns total number of bones with `BONE_TRANSFORM`. */ +/** + * Sets transform flags in the bones. + * Returns total number of bones with #BONE_TRANSFORM. + */ int transform_convert_pose_transflags_update(Object *ob, const int mode, const short around, @@ -1730,7 +1732,7 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t) BKE_pose_where_is(t->depsgraph, t->scene, pose_ob); } - /* set BONE_TRANSFORM flags for autokey, gizmo draw might have changed them */ + /* Set BONE_TRANSFORM flags for auto-key, gizmo draw might have changed them. */ if (!canceled && (t->mode != TFM_DUMMY)) { transform_convert_pose_transflags_update(ob, t->mode, t->around, NULL); } diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index bcbac009948..1acd8787f51 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -958,25 +958,25 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t) } BLI_freelistN(&pidlist); - /* pointcache refresh */ + /* Point-cache refresh. */ if (BKE_ptcache_object_reset(t->scene, ob, PTCACHE_RESET_OUTDATED)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } - /* Needed for proper updating of "quick cached" dynamics. */ - /* Creates troubles for moving animated objects without */ - /* autokey though, probably needed is an anim sys override? */ - /* Please remove if some other solution is found. -jahka */ + /* Needed for proper updating of "quick cached" dynamics. + * Creates troubles for moving animated objects without + * auto-key though, probably needed is an animation-system override? + * NOTE(@jahka): Please remove if some other solution is found. */ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - /* Set autokey if necessary */ + /* Set auto-key if necessary. */ if (!canceled) { autokeyframe_object(C, t->scene, t->view_layer, ob, t->mode); } motionpath_update |= motionpath_need_update_object(t->scene, ob); - /* restore rigid body transform */ + /* Restore rigid body transform. */ if (ob->rigidbody_object && canceled) { float ctime = BKE_scene_ctime_get(t->scene); if (BKE_rigidbody_check_sim_running(t->scene->rigidbody_world, ctime)) { diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index 88b704a84a1..661c462f60d 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -20,8 +20,8 @@ /** \file * \ingroup gpu * - * Interface for accessing gpu-related methods for selection. The semantics are - * similar to glRenderMode(GL_SELECT) from older OpenGL versions. + * Interface for accessing GPU-related methods for selection. The semantics are + * similar to `glRenderMode(GL_SELECT)` from older OpenGL versions. */ #include #include diff --git a/source/blender/gpu/intern/gpu_select_sample_query.cc b/source/blender/gpu/intern/gpu_select_sample_query.cc index 7b9b3020639..047ce0cfb35 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.cc +++ b/source/blender/gpu/intern/gpu_select_sample_query.cc @@ -20,8 +20,8 @@ /** \file * \ingroup gpu * - * Interface for accessing gpu-related methods for selection. The semantics will be - * similar to glRenderMode(GL_SELECT) since the goal is to maintain compatibility. + * Interface for accessing GPU-related methods for selection. The semantics will be + * similar to `glRenderMode(GL_SELECT)` since the goal is to maintain compatibility. */ #include diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp index b9411f6dd2d..a9e1692ebf0 100644 --- a/source/blender/ikplugin/intern/itasc_plugin.cpp +++ b/source/blender/ikplugin/intern/itasc_plugin.cpp @@ -644,7 +644,7 @@ static bool base_callback(const iTaSC::Timestamp ×tamp, ikscene->baseFrame = iTaSC::F_identity; } next.setValue(&rootmat[0][0]); - /* if there is a polar target (only during solving otherwise we don't have end efffector) */ + /* If there is a polar target (only during solving otherwise we don't have end effector). */ if (ikscene->polarConstraint && timestamp.update) { /* compute additional rotation of base frame so that armature follows the polar target */ float imat[4][4]; /* IK tree base inverse matrix */ diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index 17c7b331c88..d0711f28a6e 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -751,14 +751,14 @@ static void rna_def_nlastrip(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Influence", "Amount the strip contributes to the current result"); /* XXX: Update temporarily disabled so that the property can be edited at all! - * Even autokey only applies after the curves have been re-evaluated, + * Even auto-key only applies after the curves have been re-evaluated, * causing the unkeyed values to be lost. */ RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, /*"rna_NlaStrip_update"*/ NULL); prop = RNA_def_property(srna, "strip_time", PROP_FLOAT, PROP_TIME); RNA_def_property_ui_text(prop, "Strip Time", "Frame of referenced Action to evaluate"); /* XXX: Update temporarily disabled so that the property can be edited at all! - * Even autokey only applies after the curves have been re-evaluated, + * Even auto-key only applies after the curves have been re-evaluated, * causing the unkeyed values to be lost. */ RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, /*"rna_NlaStrip_update"*/ NULL); diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 8f9aa86e561..54a508ff5e2 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -1043,8 +1043,8 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex #define SOLIDIFY_SIDE_NORMALS #ifdef SOLIDIFY_SIDE_NORMALS - /* Note that, due to the code setting cd_dirty_vert a few lines above, - * do_side_normals is always false. - Sybren */ + /* NOTE(@sybren): due to the code setting cd_dirty_vert a few lines above, + * do_side_normals is always false. */ const bool do_side_normals = !(result->runtime.cd_dirty_vert & CD_MASK_NORMAL); /* annoying to allocate these since we only need the edge verts, */ float(*edge_vert_nos)[3] = do_side_normals ? diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index ea54673faee..a596a85b748 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -104,7 +104,7 @@ static void localize(bNodeTree *localtree, bNodeTree *ntree) local_node->original = node; /* move over the compbufs */ - /* right after ntreeCopyTree() oldsock pointers are valid */ + /* right after #ntreeCopyTree() `oldsock` pointers are valid */ if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { if (node->id) { @@ -220,7 +220,7 @@ void register_node_tree_type_cmp(void) tt->type = NTREE_COMPOSIT; strcpy(tt->idname, "CompositorNodeTree"); strcpy(tt->ui_name, N_("Compositor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = 0; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Compositing nodes")); tt->free_cache = free_cache; diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index d6b23c38ee4..20b610a4db9 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -119,7 +119,7 @@ void register_node_tree_type_geo(void) tt->type = NTREE_GEOMETRY; strcpy(tt->idname, "GeometryNodeTree"); strcpy(tt->ui_name, N_("Geometry Node Editor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = 0; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Geometry nodes")); tt->rna_ext.srna = &RNA_GeometryNodeTree; tt->update = geometry_node_tree_update; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index a710f9d6190..059e6e8680c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -86,7 +86,7 @@ struct ConeConfig { bool top_has_center_vert; bool bottom_has_center_vert; - /* Helpful quantitites. */ + /* Helpful quantities. */ int tot_quad_rings; int tot_edge_rings; int tot_verts; diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 31260f95242..4334f1b5030 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -872,7 +872,7 @@ static bNodeSocketType *make_socket_type_material() void register_standard_node_socket_types(void) { - /* draw callbacks are set in drawnode.c to avoid bad-level calls */ + /* Draw callbacks are set in `drawnode.c` to avoid bad-level calls. */ nodeRegisterSocketType(make_socket_type_float(PROP_NONE)); nodeRegisterSocketType(make_socket_type_float(PROP_UNSIGNED)); diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index c3a675fcd20..83ee0c2f411 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -201,7 +201,7 @@ void register_node_tree_type_sh(void) tt->type = NTREE_SHADER; strcpy(tt->idname, "ShaderNodeTree"); strcpy(tt->ui_name, N_("Shader Editor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = 0; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Shader nodes")); tt->foreach_nodeclass = foreach_nodeclass; diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 7452007639c..14597050524 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -169,7 +169,7 @@ void register_node_tree_type_tex(void) tt->type = NTREE_TEXTURE; strcpy(tt->idname, "TextureNodeTree"); strcpy(tt->ui_name, N_("Texture Node Editor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = 0; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Texture nodes")); tt->foreach_nodeclass = foreach_nodeclass; diff --git a/source/blender/nodes/texture/nodes/node_texture_curves.c b/source/blender/nodes/texture/nodes/node_texture_curves.c index 70f7e731720..f61e3f36db5 100644 --- a/source/blender/nodes/texture/nodes/node_texture_curves.c +++ b/source/blender/nodes/texture/nodes/node_texture_curves.c @@ -26,7 +26,7 @@ /* **************** CURVE Time ******************** */ -/* custom1 = sfra, custom2 = efra */ +/* custom1 = start-frame, custom2 = end-frame. */ static bNodeSocketTemplate time_outputs[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; static void time_colorfn( diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index d0690cfd738..588c895c742 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -945,7 +945,7 @@ typedef struct wmDragAsset { int id_type; int import_type; /* eFileAssetImportType */ - /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the + /* FIXME: This is temporary evil solution to get scene/view-layer/etc in the copy callback of the * #wmDropBox. * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its * copy callback. From f49dff97d487f715175e95d7d6ca38d43c37e94a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 3 Oct 2021 12:06:09 +1100 Subject: [PATCH 0443/1500] Cleanup: spelling in strings --- release/scripts/modules/bl_i18n_utils/utils.py | 2 +- source/blender/depsgraph/intern/node/deg_node.cc | 2 +- source/blender/editors/interface/interface_widgets.c | 2 +- source/blender/editors/object/object_edit.c | 2 +- source/blender/editors/space_clip/tracking_ops.c | 2 +- source/blender/editors/space_file/fsmenu.c | 2 +- .../intern/python/StrokeShader/BPy_SmoothingShader.cpp | 2 +- source/blender/imbuf/intern/readimage.c | 2 +- source/blender/imbuf/intern/rotate.c | 2 +- source/blender/python/bmesh/bmesh_py_types.c | 4 ++-- source/blender/python/intern/bpy_props.c | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py index 95184853f73..13fb87d386a 100644 --- a/release/scripts/modules/bl_i18n_utils/utils.py +++ b/release/scripts/modules/bl_i18n_utils/utils.py @@ -219,7 +219,7 @@ def enable_addons(addons=None, support=None, disable=False, check_only=False): try: import bpy except ModuleNotFoundError: - print("Could not import bpy, enable_addons must be run from whithin Blender.") + print("Could not import bpy, enable_addons must be run from within Blender.") return if addons is None: diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc index fee5342df59..16089ba27dd 100644 --- a/source/blender/depsgraph/intern/node/deg_node.cc +++ b/source/blender/depsgraph/intern/node/deg_node.cc @@ -177,7 +177,7 @@ eDepsSceneComponentType nodeTypeToSceneComponent(NodeType type) case NodeType::SIMULATION: return DEG_SCENE_COMP_PARAMETERS; } - BLI_assert_msg(0, "Unhandled node type, not suppsed to happen."); + BLI_assert_msg(0, "Unhandled node type, not supposed to happen."); return DEG_SCENE_COMP_PARAMETERS; } diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 375206cab44..466deedf3bb 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -5371,7 +5371,7 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, } } else { - BLI_assert_msg(0, "Unknwon menu item separator type"); + BLI_assert_msg(0, "Unknown menu item separator type"); } } } diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 5697c2c973d..2bd0ae5f121 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1739,7 +1739,7 @@ void OBJECT_OT_mode_set_with_submode(wmOperatorType *ot) OBJECT_OT_mode_set(ot); /* identifiers */ - ot->name = "Set Object Mode with Submode"; + ot->name = "Set Object Mode with Sub-mode"; ot->idname = "OBJECT_OT_mode_set_with_submode"; /* properties */ diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index ff62bcf0cfa..4965099642b 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -417,7 +417,7 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, data->pos = marker->pos; data->offset = track->offset; data->old_markers = MEM_callocN(sizeof(*data->old_markers) * track->markersnr, - "slide marekrs"); + "slide markers"); for (int a = 0; a < track->markersnr; a++) { copy_v2_v2(data->old_markers[a], track->markers[a].pos); } diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 776bb0b3bb7..091c2d5f434 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -958,7 +958,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) found = 1; } if (endmntent(fp) == 0) { - fprintf(stderr, "could not close the list of mounted filesystems\n"); + fprintf(stderr, "could not close the list of mounted file-systems\n"); } } /* Check gvfs shares. */ diff --git a/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp b/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp index ab39b9ad883..bcb7af0e5a7 100644 --- a/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp +++ b/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp @@ -63,7 +63,7 @@ static char SmoothingShader___doc__[] = "\n" ".. method:: shade(stroke)\n" "\n" - " Smoothes the stroke by moving the vertices to make the stroke\n" + " Smooths the stroke by moving the vertices to make the stroke\n" " smoother. Uses curvature flow to converge towards a curve of\n" " constant curvature. The diffusion method we use is anisotropic to\n" " prevent the diffusion across corners.\n" diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index 50210650f05..c75bdfa375c 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -126,7 +126,7 @@ ImBuf *IMB_ibImageFromMemory(const unsigned char *mem, } if ((flags & IB_test) == 0) { - fprintf(stderr, "%s: unknown fileformat (%s)\n", __func__, descr); + fprintf(stderr, "%s: unknown file-format (%s)\n", __func__, descr); } return NULL; diff --git a/source/blender/imbuf/intern/rotate.c b/source/blender/imbuf/intern/rotate.c index c2fc2190ce5..83dc29aa107 100644 --- a/source/blender/imbuf/intern/rotate.c +++ b/source/blender/imbuf/intern/rotate.c @@ -69,7 +69,7 @@ void IMB_flipy(struct ImBuf *ibuf) topf = ibuf->rect_float; bottomf = topf + 4 * ((y - 1) * x); - linef = MEM_mallocN(4 * x * sizeof(float), "linebuff"); + linef = MEM_mallocN(4 * x * sizeof(float), "linebuf"); y >>= 1; diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index e5e601e0eb6..cbebe4746e9 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1950,7 +1950,7 @@ static PyObject *bpy_bmface_calc_tangent_edge_diagonal(BPy_BMFace *self) PyDoc_STRVAR(bpy_bmface_calc_tangent_vert_diagonal_doc, ".. method:: calc_tangent_vert_diagonal()\n" "\n" - " Return face tangent based on the two most distent vertices.\n" + " Return face tangent based on the two most distant vertices.\n" "\n" " :return: a normalized vector.\n" " :rtype: :class:`mathutils.Vector`\n"); @@ -3464,7 +3464,7 @@ PyDoc_STRVAR(bpy_bmelemseq_doc, ":class:`BMVert`, :class:`BMEdge`, :class:`BMFace`, :class:`BMLoop`.\n" "\n" "When accessed via :class:`BMesh.verts`, :class:`BMesh.edges`, :class:`BMesh.faces`\n" - "there are also functions to create/remomove items.\n"); + "there are also functions to create/remove items.\n"); PyDoc_STRVAR(bpy_bmiter_doc, "Internal BMesh type for looping over verts/faces/edges,\n" "used for iterating over :class:`BMElemSeq` types.\n"); diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index ac624d365fa..6d384ed9358 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -3750,7 +3750,7 @@ PyDoc_STRVAR( " (e.g. returned by :class:`bpy.types.UILayout.icon`)\n" " :number: Unique value used as the identifier for this item (stored in file data).\n" " Use when the identifier may need to change. If the *ENUM_FLAG* option is used,\n" - " the values are bitmasks and should be powers of two.\n" + " the values are bit-masks and should be powers of two.\n" "\n" " When an item only contains 4 items they define ``(identifier, name, description, " "number)``.\n" From b57b4dfab135f7f10c1d3fb4af1c7e7745de5914 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 3 Oct 2021 12:07:28 +1100 Subject: [PATCH 0444/1500] Cleanup: clang-format --- source/blender/editors/object/object_modifier.c | 4 ++-- .../blender/nodes/composite/nodes/node_composite_premulkey.cc | 2 +- .../nodes/composite/nodes/node_composite_sepcombHSVA.cc | 1 - .../nodes/composite/nodes/node_composite_sepcombRGBA.cc | 1 - .../blender/nodes/composite/nodes/node_composite_setalpha.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 3 +-- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 125cd65631a..efe19785f31 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -1927,8 +1927,8 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)( - RNA_enum_get(op->ptr, "mode")); + const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)(RNA_enum_get( + op->ptr, "mode")); multiresModifier_subdivide(object, mmd, subdivide_mode); ED_object_iter_other( diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc index 0abd7110aff..e557854c611 100644 --- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc +++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc @@ -41,6 +41,6 @@ void register_node_type_cmp_premulkey(void) cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::cmp_node_premulkey_declare; - + nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc index 06952d3b6d9..aa719a99b36 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc @@ -34,7 +34,6 @@ static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b) b.add_output("S"); b.add_output("V"); b.add_output("A"); - } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc index d119b637595..b29af1359f5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc @@ -33,7 +33,6 @@ static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b) b.add_output("G"); b.add_output("B"); b.add_output("A"); - } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc index 5d2490b0c81..07a7ffcb426 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -30,7 +30,7 @@ namespace blender::nodes { static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b) { b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Alpha").default_value(1.0f).min(0.0f).max(1.0f); + b.add_input("Alpha").default_value(1.0f).min(0.0f).max(1.0f); b.add_output("Image"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index 9b4b6cdcd0c..00451946af9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -32,8 +32,7 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) b.add_output("Mesh"); } -static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, - const GeometrySet &profile_set) +static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set) { const CurveEval *profile_curve = profile_set.get_curve_for_read(); From e863e056977d042fb3aa290e3d0537db225bda84 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sat, 2 Oct 2021 20:33:15 -0500 Subject: [PATCH 0445/1500] Data Transfer: Remove unnecessary noisy error message I've seen requests to remove this or complaints about this error message quite frequently. In lots of production files it's just always going off. It's not an actionable warning, and since "slow" is relative, it isn't always even correct. Differential Revision: https://developer.blender.org/D12694 --- source/blender/modifiers/intern/MOD_datatransfer.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index e2b2cc58d48..34bb93cbbbc 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -163,7 +163,6 @@ static bool isDisabled(const struct Scene *UNUSED(scene), return !dtmd->ob_source || dtmd->ob_source->type != OB_MESH; } -#define HIGH_POLY_WARNING 10000 #define DT_TYPES_AFFECT_MESH \ (DT_TYPE_BWEIGHT_VERT | DT_TYPE_BWEIGHT_EDGE | DT_TYPE_CREASE | DT_TYPE_SHARP_EDGE | \ DT_TYPE_LNOR | DT_TYPE_SHARP_FACE) @@ -239,13 +238,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * BKE_modifier_set_error( ctx->object, (ModifierData *)dtmd, "Enable 'Auto Smooth' in Object Data Properties"); } - else if (result->totvert > HIGH_POLY_WARNING || - ((Mesh *)(ob_source->data))->totvert > HIGH_POLY_WARNING) { - BKE_modifier_set_error( - ctx->object, - md, - "Source or destination object has a high polygon count, computation might be slow"); - } return result; } @@ -473,7 +465,6 @@ static void panelRegister(ARegionType *region_type) region_type, "advanced", "Topology Mapping", NULL, advanced_panel_draw, panel_type); } -#undef HIGH_POLY_WARNING #undef DT_TYPES_AFFECT_MESH ModifierTypeInfo modifierType_DataTransfer = { From d3afe0c1265c9ebb53053de68f176b30f0132281 Mon Sep 17 00:00:00 2001 From: "Johnny Matthews (guitargeek)" Date: Sat, 2 Oct 2021 21:45:51 -0500 Subject: [PATCH 0446/1500] Geometry Nodes: Resample Curve Fields Update This update of the Resample Curve node allows a field to populate the count or length input of the node depending on the current mode. The field is evaluated on the spline domain. Differential Revision: https://developer.blender.org/D12735 --- .../geometry/nodes/node_geo_curve_resample.cc | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index b8f62460069..e5be9b7a6f4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -35,8 +35,9 @@ namespace blender::nodes { static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Count").default_value(10).min(1).max(100000); - b.add_input("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_input("Count").default_value(10).min(1).max(100000).supports_field(); + b.add_input("Length").default_value(0.1f).min(0.001f).supports_field().subtype( + PROP_DISTANCE); b.add_output("Geometry"); } @@ -68,8 +69,8 @@ static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node struct SampleModeParam { GeometryNodeCurveResampleMode mode; - std::optional length; - std::optional count; + std::optional> length; + std::optional> count; }; static SplinePtr resample_spline(const Spline &src, const int count) @@ -163,28 +164,44 @@ static SplinePtr resample_spline_evaluated(const Spline &src) return dst; } -static std::unique_ptr resample_curve(const CurveEval &input_curve, +static std::unique_ptr resample_curve(const CurveComponent *component, const SampleModeParam &mode_param) { - Span input_splines = input_curve.splines(); + const CurveEval *input_curve = component->get_for_read(); + GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE}; + const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + + Span input_splines = input_curve->splines(); std::unique_ptr output_curve = std::make_unique(); output_curve->resize(input_splines.size()); MutableSpan output_splines = output_curve->splines(); if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { + evaluator.add(*mode_param.count); + evaluator.evaluate(); + const VArray &cuts = evaluator.get_evaluated(0); + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { BLI_assert(mode_param.count); - output_splines[i] = resample_spline(*input_splines[i], *mode_param.count); + output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1)); } }); } else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { + evaluator.add(*mode_param.length); + evaluator.evaluate(); + const VArray &lengths = evaluator.get_evaluated(0); + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - const float length = input_splines[i]->length(); - const int count = std::max(int(length / *mode_param.length) + 1, 1); + /* Don't allow asymptotic count increase for low resolution values. */ + const float divide_length = std::max(lengths[i], 0.0001f); + const float spline_length = input_splines[i]->length(); + const int count = std::max(int(spline_length / divide_length) + 1, 1); output_splines[i] = resample_spline(*input_splines[i], count); } }); @@ -197,7 +214,7 @@ static std::unique_ptr resample_curve(const CurveEval &input_curve, }); } - output_curve->attributes = input_curve.attributes; + output_curve->attributes = input_curve->attributes; return output_curve; } @@ -209,8 +226,8 @@ static void geometry_set_curve_resample(GeometrySet &geometry_set, return; } - const CurveEval &input_curve = *geometry_set.get_curve_for_read(); - std::unique_ptr output_curve = resample_curve(input_curve, mode_param); + std::unique_ptr output_curve = resample_curve( + geometry_set.get_component_for_read(), mode_param); geometry_set.replace_curve(output_curve.release()); } @@ -225,7 +242,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) SampleModeParam mode_param; mode_param.mode = mode; if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { - const int count = params.extract_input("Count"); + Field count = params.extract_input>("Count"); if (count < 1) { params.set_output("Geometry", GeometrySet()); return; @@ -233,8 +250,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) mode_param.count.emplace(count); } else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { - /* Don't allow asymptotic count increase for low resolution values. */ - const float resolution = std::max(params.extract_input("Length"), 0.0001f); + Field resolution = params.extract_input>("Length"); mode_param.length.emplace(resolution); } From f2da98d816e892ff3477bff2e443b37b03403522 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 13:44:44 +0200 Subject: [PATCH 0447/1500] Cleanup: remove unused functions --- source/blender/blenlib/BLI_span.hh | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index 29098fd79ce..5b7981e0302 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -729,29 +729,4 @@ template class MutableSpan { } }; -/** - * Utilities to check that arrays have the same size in debug builds. - */ -template constexpr void assert_same_size(const T1 &v1, const T2 &v2) -{ - UNUSED_VARS_NDEBUG(v1, v2); -#ifdef DEBUG - int64_t size = v1.size(); - BLI_assert(size == v1.size()); - BLI_assert(size == v2.size()); -#endif -} - -template -constexpr void assert_same_size(const T1 &v1, const T2 &v2, const T3 &v3) -{ - UNUSED_VARS_NDEBUG(v1, v2, v3); -#ifdef DEBUG - int64_t size = v1.size(); - BLI_assert(size == v1.size()); - BLI_assert(size == v2.size()); - BLI_assert(size == v3.size()); -#endif -} - } /* namespace blender */ From c206fa9627c40b91d07e1abd951f06dbcf502f6d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 14:03:53 +0200 Subject: [PATCH 0448/1500] Cleanup: add nolint comment to move semantic test --- source/blender/blenkernel/intern/asset_catalog_path_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc index af15cbf405a..6110217a0fb 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc @@ -91,7 +91,7 @@ TEST(AssetCatalogPathTest, move_semantics) EXPECT_TRUE(source_path); AssetCatalogPath dest_path = std::move(source_path); - EXPECT_FALSE(source_path); + EXPECT_FALSE(source_path); /* NOLINT: bugprone-use-after-move */ EXPECT_TRUE(dest_path); } From a8d6a86981b3bdcc8e9796a4465544523372c687 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 14:10:26 +0200 Subject: [PATCH 0449/1500] Cleanup: move StringRef method definitions out of class This makes the classes more appealing to look at and makes it easier to see what different methods are available. --- source/blender/blenlib/BLI_string_ref.hh | 872 ++++++++++++----------- 1 file changed, 460 insertions(+), 412 deletions(-) diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index dcf66bbf5ad..79d1f73bb93 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -64,135 +64,35 @@ class StringRefBase { const char *data_; int64_t size_; - constexpr StringRefBase(const char *data, const int64_t size) : data_(data), size_(size) - { - } + constexpr StringRefBase(const char *data, const int64_t size); public: /* Similar to string_view::npos, but signed. */ static constexpr int64_t not_found = -1; - /** - * Return the (byte-)length of the referenced string, without any null-terminator. - */ - constexpr int64_t size() const - { - return size_; - } + constexpr int64_t size() const; + constexpr bool is_empty() const; + constexpr const char *data() const; + constexpr operator Span() const; - constexpr bool is_empty() const - { - return size_ == 0; - } + operator std::string() const; + constexpr operator std::string_view() const; - /** - * Return a pointer to the start of the string. - */ - constexpr const char *data() const - { - return data_; - } + constexpr const char *begin() const; + constexpr const char *end() const; - constexpr operator Span() const - { - return Span(data_, size_); - } + constexpr IndexRange index_range() const; - /** - * Implicitly convert to std::string. This is convenient in most cases, but you have to be a bit - * careful not to convert to std::string accidentally. - */ - operator std::string() const - { - return std::string(data_, static_cast(size_)); - } + void unsafe_copy(char *dst) const; + void copy(char *dst, const int64_t dst_size) const; + template void copy(char (&dst)[N]) const; - constexpr operator std::string_view() const - { - return std::string_view(data_, static_cast(size_)); - } - - constexpr const char *begin() const - { - return data_; - } - - constexpr const char *end() const - { - return data_ + size_; - } - - constexpr IndexRange index_range() const - { - return IndexRange(size_); - } - - /** - * Copy the string into a buffer. The buffer has to be one byte larger than the size of the - * string, because the copied string will be null-terminated. Only use this when you are - * absolutely sure that the buffer is large enough. - */ - void unsafe_copy(char *dst) const - { - if (size_ > 0) { - memcpy(dst, data_, static_cast(size_)); - } - dst[size_] = '\0'; - } - - /** - * Copy the string into a buffer. The copied string will be null-terminated. This invokes - * undefined behavior when dst_size is too small. (Should we define the behavior?) - */ - void copy(char *dst, const int64_t dst_size) const - { - if (size_ < dst_size) { - this->unsafe_copy(dst); - } - else { - BLI_assert(false); - dst[0] = '\0'; - } - } - - /** - * Copy the string into a char array. The copied string will be null-terminated. This invokes - * undefined behavior when dst is too small. - */ - template void copy(char (&dst)[N]) const - { - this->copy(dst, N); - } - - /** - * Returns true when the string begins with the given prefix. Otherwise false. - */ constexpr bool startswith(StringRef prefix) const; - - /** - * Returns true when the string ends with the given suffix. Otherwise false. - */ constexpr bool endswith(StringRef suffix) const; - constexpr StringRef substr(int64_t start, const int64_t size) const; - /** - * Get the first char in the string. This invokes undefined behavior when the string is empty. - */ - constexpr const char &front() const - { - BLI_assert(size_ >= 1); - return data_[0]; - } - - /** - * Get the last char in the string. This invokes undefined behavior when the string is empty. - */ - constexpr const char &back() const - { - BLI_assert(size_ >= 1); - return data_[size_ - 1]; - } + constexpr const char &front() const; + constexpr const char &back() const; /** * The behavior of those functions matches the standard library implementation of @@ -211,15 +111,7 @@ class StringRefBase { constexpr int64_t find_last_not_of(StringRef chars, int64_t pos = INT64_MAX) const; constexpr int64_t find_last_not_of(char c, int64_t pos = INT64_MAX) const; - /** - * Return a new StringRef that does not contain leading and trailing whitespace. - */ constexpr StringRef trim() const; - - /** - * Return a new StringRef that removes all the leading and trailing characters - * that occur in `characters_to_remove`. - */ constexpr StringRef trim(StringRef characters_to_remove) const; constexpr StringRef trim(char character_to_remove) const; }; @@ -230,57 +122,13 @@ class StringRefBase { class StringRefNull : public StringRefBase { public: - constexpr StringRefNull() : StringRefBase("", 0) - { - } + constexpr StringRefNull(); + constexpr StringRefNull(const char *str, const int64_t size); + StringRefNull(const char *str); + StringRefNull(const std::string &str); - /** - * Construct a StringRefNull from a null terminated c-string. The pointer must not point to - * NULL. - */ - StringRefNull(const char *str) : StringRefBase(str, static_cast(strlen(str))) - { - BLI_assert(str != nullptr); - BLI_assert(data_[size_] == '\0'); - } - - /** - * Construct a StringRefNull from a null terminated c-string. This invokes undefined behavior - * when the given size is not the correct size of the string. - */ - constexpr StringRefNull(const char *str, const int64_t size) : StringRefBase(str, size) - { - BLI_assert(static_cast(strlen(str)) == size); - } - - /** - * Reference a std::string. Remember that when the std::string is destructed, the StringRefNull - * will point to uninitialized memory. - */ - StringRefNull(const std::string &str) : StringRefNull(str.c_str()) - { - } - - /** - * Get the char at the given index. - */ - constexpr char operator[](const int64_t index) const - { - BLI_assert(index >= 0); - /* Use '<=' instead of just '<', so that the null character can be accessed as well. */ - BLI_assert(index <= size_); - return data_[index]; - } - - /** - * Returns the beginning of a null-terminated char array. - * - * This is like ->data(), but can only be called on a StringRefNull. - */ - constexpr const char *c_str() const - { - return data_; - } + constexpr char operator[](const int64_t index) const; + constexpr const char *c_str() const; }; /** @@ -288,98 +136,443 @@ class StringRefNull : public StringRefBase { */ class StringRef : public StringRefBase { public: - constexpr StringRef() : StringRefBase(nullptr, 0) - { - } + constexpr StringRef(); + constexpr StringRef(StringRefNull other); + constexpr StringRef(const char *str); + constexpr StringRef(const char *str, const int64_t length); + constexpr StringRef(const char *begin, const char *one_after_end); + constexpr StringRef(std::string_view view); + StringRef(const std::string &str); - /** - * StringRefNull can be converted into StringRef, but not the other way around. - */ - constexpr StringRef(StringRefNull other) : StringRefBase(other.data(), other.size()) - { - } + constexpr StringRef drop_prefix(const int64_t n) const; + constexpr StringRef drop_known_prefix(StringRef prefix) const; + constexpr StringRef drop_suffix(const int64_t n) const; - /** - * Create a StringRef from a null-terminated c-string. - */ - constexpr StringRef(const char *str) - : StringRefBase(str, str ? static_cast(std::char_traits::length(str)) : 0) - { - } - - constexpr StringRef(const char *str, const int64_t length) : StringRefBase(str, length) - { - } - - /** - * Create a StringRef from a start and end pointer. This invokes undefined behavior when the - * second point points to a smaller address than the first one. - */ - constexpr StringRef(const char *begin, const char *one_after_end) - : StringRefBase(begin, static_cast(one_after_end - begin)) - { - BLI_assert(begin <= one_after_end); - } - - /** - * Reference a std::string. Remember that when the std::string is destructed, the StringRef - * will point to uninitialized memory. - */ - StringRef(const std::string &str) : StringRefBase(str.data(), static_cast(str.size())) - { - } - - constexpr StringRef(std::string_view view) - : StringRefBase(view.data(), static_cast(view.size())) - { - } - - /** - * Returns a new StringRef that does not contain the first n chars. This invokes undefined - * behavior when n is negative. - */ - constexpr StringRef drop_prefix(const int64_t n) const - { - BLI_assert(n >= 0); - const int64_t clamped_n = std::min(n, size_); - const int64_t new_size = size_ - clamped_n; - return StringRef(data_ + clamped_n, new_size); - } - - /** - * Return a new StringRef with the given prefix being skipped. This invokes undefined behavior if - * the string does not begin with the given prefix. - */ - constexpr StringRef drop_known_prefix(StringRef prefix) const - { - BLI_assert(this->startswith(prefix)); - return this->drop_prefix(prefix.size()); - } - - /** - * Return a new StringRef that does not contain the last n chars. This invokes undefined behavior - * when n is negative. - */ - constexpr StringRef drop_suffix(const int64_t n) const - { - BLI_assert(n >= 0); - const int64_t new_size = std::max(0, size_ - n); - return StringRef(data_, new_size); - } - - /** - * Get the char at the given index. - */ - constexpr char operator[](int64_t index) const - { - BLI_assert(index >= 0); - BLI_assert(index < size_); - return data_[index]; - } + constexpr char operator[](int64_t index) const; }; -/* More inline functions - ***************************************/ +/* -------------------------------------------------------------------- + * #StringRefBase inline methods. + */ + +constexpr StringRefBase::StringRefBase(const char *data, const int64_t size) + : data_(data), size_(size) +{ +} + +/** + * Return the (byte-)length of the referenced string, without any null-terminator. + */ +constexpr int64_t StringRefBase::size() const +{ + return size_; +} + +constexpr bool StringRefBase::is_empty() const +{ + return size_ == 0; +} + +/** + * Return a pointer to the start of the string. + */ +constexpr const char *StringRefBase::data() const +{ + return data_; +} + +constexpr StringRefBase::operator Span() const +{ + return Span(data_, size_); +} + +/** + * Implicitly convert to std::string. This is convenient in most cases, but you have to be a bit + * careful not to convert to std::string accidentally. + */ +inline StringRefBase::operator std::string() const +{ + return std::string(data_, static_cast(size_)); +} + +constexpr StringRefBase::operator std::string_view() const +{ + return std::string_view(data_, static_cast(size_)); +} + +constexpr const char *StringRefBase::begin() const +{ + return data_; +} + +constexpr const char *StringRefBase::end() const +{ + return data_ + size_; +} + +constexpr IndexRange StringRefBase::index_range() const +{ + return IndexRange(size_); +} + +/** + * Copy the string into a buffer. The buffer has to be one byte larger than the size of the + * string, because the copied string will be null-terminated. Only use this when you are + * absolutely sure that the buffer is large enough. + */ +inline void StringRefBase::unsafe_copy(char *dst) const +{ + if (size_ > 0) { + memcpy(dst, data_, static_cast(size_)); + } + dst[size_] = '\0'; +} + +/** + * Copy the string into a buffer. The copied string will be null-terminated. This invokes + * undefined behavior when dst_size is too small. (Should we define the behavior?) + */ +inline void StringRefBase::copy(char *dst, const int64_t dst_size) const +{ + if (size_ < dst_size) { + this->unsafe_copy(dst); + } + else { + BLI_assert(false); + dst[0] = '\0'; + } +} + +/** + * Copy the string into a char array. The copied string will be null-terminated. This invokes + * undefined behavior when dst is too small. + */ +template inline void StringRefBase::copy(char (&dst)[N]) const +{ + this->copy(dst, N); +} + +/** + * Return true when the string starts with the given prefix. + */ +constexpr bool StringRefBase::startswith(StringRef prefix) const +{ + if (size_ < prefix.size_) { + return false; + } + for (int64_t i = 0; i < prefix.size_; i++) { + if (data_[i] != prefix.data_[i]) { + return false; + } + } + return true; +} + +/** + * Return true when the string ends with the given suffix. + */ +constexpr bool StringRefBase::endswith(StringRef suffix) const +{ + if (size_ < suffix.size_) { + return false; + } + const int64_t offset = size_ - suffix.size_; + for (int64_t i = 0; i < suffix.size_; i++) { + if (data_[offset + i] != suffix.data_[i]) { + return false; + } + } + return true; +} + +/** + * Return a new #StringRef containing only a sub-string of the original string. This invokes + * undefined if the start or max_size is negative. + */ +constexpr StringRef StringRefBase::substr(const int64_t start, + const int64_t max_size = INT64_MAX) const +{ + BLI_assert(max_size >= 0); + BLI_assert(start >= 0); + const int64_t substr_size = std::min(max_size, size_ - start); + return StringRef(data_ + start, substr_size); +} + +/** + * Get the first char in the string. This invokes undefined behavior when the string is empty. + */ +constexpr const char &StringRefBase::front() const +{ + BLI_assert(size_ >= 1); + return data_[0]; +} + +/** + * Get the last char in the string. This invokes undefined behavior when the string is empty. + */ +constexpr const char &StringRefBase::back() const +{ + BLI_assert(size_ >= 1); + return data_[size_ - 1]; +} + +constexpr int64_t index_or_npos_to_int64(size_t index) +{ + /* The compiler will probably optimize this check away. */ + if (index == std::string_view::npos) { + return StringRef::not_found; + } + return static_cast(index); +} + +constexpr int64_t StringRefBase::find(char c, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64(std::string_view(*this).find(c, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find(StringRef str, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64(std::string_view(*this).find(str, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_first_of(chars, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find_first_of(char c, int64_t pos) const +{ + return this->find_first_of(StringRef(&c, 1), pos); +} + +constexpr int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_last_of(chars, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find_last_of(char c, int64_t pos) const +{ + return this->find_last_of(StringRef(&c, 1), pos); +} + +constexpr int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_first_not_of(chars, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const +{ + return this->find_first_not_of(StringRef(&c, 1), pos); +} + +constexpr int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_last_not_of(chars, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const +{ + return this->find_last_not_of(StringRef(&c, 1), pos); +} + +constexpr StringRef StringRefBase::trim() const +{ + return this->trim(" \t\r\n"); +} + +/** + * Return a new StringRef that does not contain leading and trailing whitespace. + */ +constexpr StringRef StringRefBase::trim(const char character_to_remove) const +{ + return this->trim(StringRef(&character_to_remove, 1)); +} + +/** + * Return a new StringRef that removes all the leading and trailing characters + * that occur in `characters_to_remove`. + */ +constexpr StringRef StringRefBase::trim(StringRef characters_to_remove) const +{ + const int64_t find_front = this->find_first_not_of(characters_to_remove); + if (find_front == not_found) { + return StringRef(); + } + const int64_t find_end = this->find_last_not_of(characters_to_remove); + /* `find_end` cannot be `not_found`, because that means the string is only + * `characters_to_remove`, in which case `find_front` would already have + * been `not_found`. */ + BLI_assert_msg(find_end != not_found, + "forward search found characters-to-not-remove, but backward search did not"); + const int64_t substr_len = find_end - find_front + 1; + return this->substr(find_front, substr_len); +} + +/* -------------------------------------------------------------------- + * #StringRefNull inline methods. + */ + +constexpr StringRefNull::StringRefNull() : StringRefBase("", 0) +{ +} + +/** + * Construct a StringRefNull from a null terminated c-string. This invokes undefined behavior + * when the given size is not the correct size of the string. + */ +constexpr StringRefNull::StringRefNull(const char *str, const int64_t size) + : StringRefBase(str, size) +{ + BLI_assert(static_cast(strlen(str)) == size); +} + +/** + * Construct a StringRefNull from a null terminated c-string. The pointer must not point to + * NULL. + */ +inline StringRefNull::StringRefNull(const char *str) + : StringRefBase(str, static_cast(strlen(str))) +{ + BLI_assert(str != nullptr); + BLI_assert(data_[size_] == '\0'); +} + +/** + * Reference a std::string. Remember that when the std::string is destructed, the StringRefNull + * will point to uninitialized memory. + */ +inline StringRefNull::StringRefNull(const std::string &str) : StringRefNull(str.c_str()) +{ +} + +/** + * Get the char at the given index. + */ +constexpr char StringRefNull::operator[](const int64_t index) const +{ + BLI_assert(index >= 0); + /* Use '<=' instead of just '<', so that the null character can be accessed as well. */ + BLI_assert(index <= size_); + return data_[index]; +} + +/** + * Returns the beginning of a null-terminated char array. + * + * This is like ->data(), but can only be called on a StringRefNull. + */ +constexpr const char *StringRefNull::c_str() const +{ + return data_; +} + +/* -------------------------------------------------------------------- + * #StringRef inline methods. + */ + +constexpr StringRef::StringRef() : StringRefBase(nullptr, 0) +{ +} + +/** + * StringRefNull can be converted into StringRef, but not the other way around. + */ +constexpr StringRef::StringRef(StringRefNull other) : StringRefBase(other.data(), other.size()) +{ +} + +/** + * Create a StringRef from a null-terminated c-string. + */ +constexpr StringRef::StringRef(const char *str) + : StringRefBase(str, str ? static_cast(std::char_traits::length(str)) : 0) +{ +} + +constexpr StringRef::StringRef(const char *str, const int64_t length) : StringRefBase(str, length) +{ +} + +/** + * Returns a new StringRef that does not contain the first n chars. This invokes undefined + * behavior when n is negative. + */ +constexpr StringRef StringRef::drop_prefix(const int64_t n) const +{ + BLI_assert(n >= 0); + const int64_t clamped_n = std::min(n, size_); + const int64_t new_size = size_ - clamped_n; + return StringRef(data_ + clamped_n, new_size); +} + +/** + * Return a new StringRef with the given prefix being skipped. This invokes undefined behavior if + * the string does not begin with the given prefix. + */ +constexpr StringRef StringRef::drop_known_prefix(StringRef prefix) const +{ + BLI_assert(this->startswith(prefix)); + return this->drop_prefix(prefix.size()); +} + +/** + * Return a new StringRef that does not contain the last n chars. This invokes undefined behavior + * when n is negative. + */ +constexpr StringRef StringRef::drop_suffix(const int64_t n) const +{ + BLI_assert(n >= 0); + const int64_t new_size = std::max(0, size_ - n); + return StringRef(data_, new_size); +} + +/** + * Get the char at the given index. + */ +constexpr char StringRef::operator[](int64_t index) const +{ + BLI_assert(index >= 0); + BLI_assert(index < size_); + return data_[index]; +} + +/** + * Create a StringRef from a start and end pointer. This invokes undefined behavior when the + * second point points to a smaller address than the first one. + */ +constexpr StringRef::StringRef(const char *begin, const char *one_after_end) + : StringRefBase(begin, static_cast(one_after_end - begin)) +{ + BLI_assert(begin <= one_after_end); +} + +/** + * Reference a std::string. Remember that when the std::string is destructed, the StringRef + * will point to uninitialized memory. + */ +inline StringRef::StringRef(const std::string &str) + : StringRefBase(str.data(), static_cast(str.size())) +{ +} + +constexpr StringRef::StringRef(std::string_view view) + : StringRefBase(view.data(), static_cast(view.size())) +{ +} + +/* -------------------------------------------------------------------- + * Operator overloads + */ inline std::ostream &operator<<(std::ostream &stream, StringRef ref) { @@ -406,7 +599,7 @@ inline std::string operator+(StringRef a, StringRef b) * not a problem when std::string_view is only used at api boundaries. To compare a StringRef and a * std::string_view, one should convert the std::string_view to StringRef (which is very cheap). * Ideally, we only use StringRef in our code to avoid this problem altogether. */ -constexpr inline bool operator==(StringRef a, StringRef b) +constexpr bool operator==(StringRef a, StringRef b) { if (a.size() != b.size()) { return false; @@ -414,174 +607,29 @@ constexpr inline bool operator==(StringRef a, StringRef b) return STREQLEN(a.data(), b.data(), (size_t)a.size()); } -constexpr inline bool operator!=(StringRef a, StringRef b) +constexpr bool operator!=(StringRef a, StringRef b) { return !(a == b); } -constexpr inline bool operator<(StringRef a, StringRef b) +constexpr bool operator<(StringRef a, StringRef b) { return std::string_view(a) < std::string_view(b); } -constexpr inline bool operator>(StringRef a, StringRef b) +constexpr bool operator>(StringRef a, StringRef b) { return std::string_view(a) > std::string_view(b); } -constexpr inline bool operator<=(StringRef a, StringRef b) +constexpr bool operator<=(StringRef a, StringRef b) { return std::string_view(a) <= std::string_view(b); } -constexpr inline bool operator>=(StringRef a, StringRef b) +constexpr bool operator>=(StringRef a, StringRef b) { return std::string_view(a) >= std::string_view(b); } -/** - * Return true when the string starts with the given prefix. - */ -constexpr inline bool StringRefBase::startswith(StringRef prefix) const -{ - if (size_ < prefix.size_) { - return false; - } - for (int64_t i = 0; i < prefix.size_; i++) { - if (data_[i] != prefix.data_[i]) { - return false; - } - } - return true; -} - -/** - * Return true when the string ends with the given suffix. - */ -constexpr inline bool StringRefBase::endswith(StringRef suffix) const -{ - if (size_ < suffix.size_) { - return false; - } - const int64_t offset = size_ - suffix.size_; - for (int64_t i = 0; i < suffix.size_; i++) { - if (data_[offset + i] != suffix.data_[i]) { - return false; - } - } - return true; -} - -/** - * Return a new #StringRef containing only a sub-string of the original string. This invokes - * undefined if the start or max_size is negative. - */ -constexpr inline StringRef StringRefBase::substr(const int64_t start, - const int64_t max_size = INT64_MAX) const -{ - BLI_assert(max_size >= 0); - BLI_assert(start >= 0); - const int64_t substr_size = std::min(max_size, size_ - start); - return StringRef(data_ + start, substr_size); -} - -constexpr inline int64_t index_or_npos_to_int64(size_t index) -{ - /* The compiler will probably optimize this check away. */ - if (index == std::string_view::npos) { - return StringRef::not_found; - } - return static_cast(index); -} - -constexpr inline int64_t StringRefBase::find(char c, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64(std::string_view(*this).find(c, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find(StringRef str, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64(std::string_view(*this).find(str, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64( - std::string_view(*this).find_first_of(chars, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find_first_of(char c, int64_t pos) const -{ - return this->find_first_of(StringRef(&c, 1), pos); -} - -constexpr inline int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64( - std::string_view(*this).find_last_of(chars, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find_last_of(char c, int64_t pos) const -{ - return this->find_last_of(StringRef(&c, 1), pos); -} - -constexpr inline int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64( - std::string_view(*this).find_first_not_of(chars, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const -{ - return this->find_first_not_of(StringRef(&c, 1), pos); -} - -constexpr inline int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64( - std::string_view(*this).find_last_not_of(chars, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const -{ - return this->find_last_not_of(StringRef(&c, 1), pos); -} - -constexpr StringRef StringRefBase::trim() const -{ - return this->trim(" \t\r\n"); -} - -constexpr StringRef StringRefBase::trim(const char character_to_remove) const -{ - return this->trim(StringRef(&character_to_remove, 1)); -} - -/** - * Return a new StringRef that removes all the leading and trailing characters - * that occur in `characters_to_remove`. - */ -constexpr StringRef StringRefBase::trim(StringRef characters_to_remove) const -{ - const int64_t find_front = this->find_first_not_of(characters_to_remove); - if (find_front == not_found) { - return StringRef(); - } - const int64_t find_end = this->find_last_not_of(characters_to_remove); - /* `find_end` cannot be `not_found`, because that means the string is only - * `characters_to_remove`, in which case `find_front` would already have - * been `not_found`. */ - BLI_assert_msg(find_end != not_found, - "forward search found characters-to-not-remove, but backward search did not"); - const int64_t substr_len = find_end - find_front + 1; - return this->substr(find_front, substr_len); -} - } // namespace blender From e1e75bd62c33395ca29e4ad456b416f8a6923d46 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 14:23:26 +0200 Subject: [PATCH 0450/1500] Cleanup: move resource scope method definitions out of class --- source/blender/blenlib/BLI_resource_scope.hh | 199 +++++++++--------- source/blender/blenlib/CMakeLists.txt | 1 + .../blender/blenlib/intern/resource_scope.cc | 32 +++ 3 files changed, 136 insertions(+), 96 deletions(-) create mode 100644 source/blender/blenlib/intern/resource_scope.cc diff --git a/source/blender/blenlib/BLI_resource_scope.hh b/source/blender/blenlib/BLI_resource_scope.hh index 761e1ef834c..8e88e251d30 100644 --- a/source/blender/blenlib/BLI_resource_scope.hh +++ b/source/blender/blenlib/BLI_resource_scope.hh @@ -56,106 +56,113 @@ class ResourceScope : NonCopyable, NonMovable { Vector resources_; public: - ResourceScope() = default; + ResourceScope(); + ~ResourceScope(); - ~ResourceScope() - { - /* Free in reversed order. */ - for (int64_t i = resources_.size(); i--;) { - ResourceData &data = resources_[i]; - data.free(data.data); - } - } + template T *add(std::unique_ptr resource); + template T *add(destruct_ptr resource); + void add(void *userdata, void (*free)(void *)); - /** - * Pass ownership of the resource to the ResourceScope. It will be destructed and freed when - * the collector is destructed. - */ - template T *add(std::unique_ptr resource) - { - T *ptr = resource.release(); - if (ptr == nullptr) { - return nullptr; - } - this->add(ptr, [](void *data) { - T *typed_data = reinterpret_cast(data); - delete typed_data; - }); - return ptr; - } + template T &add_value(T &&value); + template void add_destruct_call(Func func); - /** - * Pass ownership of the resource to the ResourceScope. It will be destructed when the - * collector is destructed. - */ - template T *add(destruct_ptr resource) - { - T *ptr = resource.release(); - if (ptr == nullptr) { - return nullptr; - } - /* There is no need to keep track of such types. */ - if (std::is_trivially_destructible_v) { - return ptr; - } + template T &construct(Args &&...args); - this->add(ptr, [](void *data) { - T *typed_data = reinterpret_cast(data); - typed_data->~T(); - }); - return ptr; - } - - /** - * Pass ownership of some resource to the ResourceScope. The given free function will be - * called when the collector is destructed. - */ - void add(void *userdata, void (*free)(void *)) - { - ResourceData data; - data.data = userdata; - data.free = free; - resources_.append(data); - } - - /** - * Construct an object with the same value in the ResourceScope and return a reference to the - * new value. - */ - template T &add_value(T &&value) - { - return this->construct(std::forward(value)); - } - - /** - * The passed in function will be called when the scope is destructed. - */ - template void add_destruct_call(Func func) - { - void *buffer = allocator_.allocate(sizeof(Func), alignof(Func)); - new (buffer) Func(std::move(func)); - this->add(buffer, [](void *data) { (*(Func *)data)(); }); - } - - /** - * Returns a reference to a linear allocator that is owned by the ResourcesCollector. Memory - * allocated through this allocator will be freed when the collector is destructed. - */ - LinearAllocator<> &linear_allocator() - { - return allocator_; - } - - /** - * Utility method to construct an instance of type T that will be owned by the ResourceScope. - */ - template T &construct(Args &&...args) - { - destruct_ptr value_ptr = allocator_.construct(std::forward(args)...); - T &value_ref = *value_ptr; - this->add(std::move(value_ptr)); - return value_ref; - } + LinearAllocator<> &linear_allocator(); }; +/* -------------------------------------------------------------------- + * #ResourceScope inline methods. + */ + +/** + * Pass ownership of the resource to the ResourceScope. It will be destructed and freed when + * the collector is destructed. + */ +template inline T *ResourceScope::add(std::unique_ptr resource) +{ + T *ptr = resource.release(); + if (ptr == nullptr) { + return nullptr; + } + this->add(ptr, [](void *data) { + T *typed_data = reinterpret_cast(data); + delete typed_data; + }); + return ptr; +} + +/** + * Pass ownership of the resource to the ResourceScope. It will be destructed when the + * collector is destructed. + */ +template inline T *ResourceScope::add(destruct_ptr resource) +{ + T *ptr = resource.release(); + if (ptr == nullptr) { + return nullptr; + } + /* There is no need to keep track of such types. */ + if constexpr (std::is_trivially_destructible_v) { + return ptr; + } + + this->add(ptr, [](void *data) { + T *typed_data = reinterpret_cast(data); + typed_data->~T(); + }); + return ptr; +} + +/** + * Pass ownership of some resource to the ResourceScope. The given free function will be + * called when the collector is destructed. + */ +inline void ResourceScope::add(void *userdata, void (*free)(void *)) +{ + ResourceData data; + data.data = userdata; + data.free = free; + resources_.append(data); +} + +/** + * Construct an object with the same value in the ResourceScope and return a reference to the + * new value. + */ +template inline T &ResourceScope::add_value(T &&value) +{ + return this->construct(std::forward(value)); +} + +/** + * The passed in function will be called when the scope is destructed. + */ +template inline void ResourceScope::add_destruct_call(Func func) +{ + void *buffer = allocator_.allocate(sizeof(Func), alignof(Func)); + new (buffer) Func(std::move(func)); + this->add(buffer, [](void *data) { (*(Func *)data)(); }); +} + +/** + * Utility method to construct an instance of type T that will be owned by the ResourceScope. + */ +template inline T &ResourceScope::construct(Args &&...args) +{ + destruct_ptr value_ptr = allocator_.construct(std::forward(args)...); + T &value_ref = *value_ptr; + this->add(std::move(value_ptr)); + return value_ref; +} + +/** + * Returns a reference to a linear allocator that is owned by the ResourcesCollector. Memory + * allocated through this allocator will be freed when the collector is destructed. + */ +inline LinearAllocator<> &ResourceScope::linear_allocator() +{ + return allocator_; +} + } // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 1eaf007e01b..c886732365e 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -124,6 +124,7 @@ set(SRC intern/quadric.c intern/rand.cc intern/rct.c + intern/resource_scope.cc intern/scanfill.c intern/scanfill_utils.c intern/session_uuid.c diff --git a/source/blender/blenlib/intern/resource_scope.cc b/source/blender/blenlib/intern/resource_scope.cc new file mode 100644 index 00000000000..8c8a03b8ce5 --- /dev/null +++ b/source/blender/blenlib/intern/resource_scope.cc @@ -0,0 +1,32 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_resource_scope.hh" + +namespace blender { + +ResourceScope::ResourceScope() = default; + +ResourceScope::~ResourceScope() +{ + /* Free in reversed order. */ + for (int64_t i = resources_.size(); i--;) { + ResourceData &data = resources_[i]; + data.free(data.data); + } +} + +} // namespace blender From 5d5a753d96350df46bf060628935e5e9e9f0c5a7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 14:39:17 +0200 Subject: [PATCH 0451/1500] Cleanup: move AttributeIDRef and OutputAttribute methods out of class This makes the classes easier to read and simplifies testing the compile time impact of defining these methods in the header. --- .../blenkernel/BKE_attribute_access.hh | 276 ++++++++++-------- 1 file changed, 159 insertions(+), 117 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index da3de2f08bd..3e9dfda7166 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -42,66 +42,21 @@ class AttributeIDRef { const AnonymousAttributeID *anonymous_id_ = nullptr; public: - AttributeIDRef() = default; + AttributeIDRef(); + AttributeIDRef(StringRef name); + AttributeIDRef(StringRefNull name); + AttributeIDRef(const char *name); + AttributeIDRef(const std::string &name); + AttributeIDRef(const AnonymousAttributeID *anonymous_id); - AttributeIDRef(StringRef name) : name_(name) - { - } - - AttributeIDRef(StringRefNull name) : name_(name) - { - } - - AttributeIDRef(const char *name) : name_(name) - { - } - - AttributeIDRef(const std::string &name) : name_(name) - { - } - - /* The anonymous id is only borrowed, the caller has to keep a reference to it. */ - AttributeIDRef(const AnonymousAttributeID *anonymous_id) : anonymous_id_(anonymous_id) - { - } - - operator bool() const - { - return this->is_named() || this->is_anonymous(); - } - - friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b) - { - return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_; - } - - uint64_t hash() const - { - return get_default_hash_2(name_, anonymous_id_); - } - - bool is_named() const - { - return !name_.is_empty(); - } - - bool is_anonymous() const - { - return anonymous_id_ != nullptr; - } - - StringRef name() const - { - BLI_assert(this->is_named()); - return name_; - } - - const AnonymousAttributeID &anonymous_id() const - { - BLI_assert(this->is_anonymous()); - return *anonymous_id_; - } + operator bool() const; + uint64_t hash() const; + bool is_named() const; + bool is_anonymous() const; + StringRef name() const; + const AnonymousAttributeID &anonymous_id() const; + friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b); friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id); }; @@ -259,73 +214,26 @@ class OutputAttribute { bool save_has_been_called_ = false; public: - OutputAttribute() = default; - + OutputAttribute(); + OutputAttribute(OutputAttribute &&other); OutputAttribute(GVMutableArrayPtr varray, AttributeDomain domain, SaveFn save, - const bool ignore_old_values) - : varray_(std::move(varray)), - domain_(domain), - save_(std::move(save)), - ignore_old_values_(ignore_old_values) - { - } - - OutputAttribute(OutputAttribute &&other) = default; + const bool ignore_old_values); ~OutputAttribute(); - operator bool() const - { - return varray_.get() != nullptr; - } + operator bool() const; - GVMutableArray &operator*() - { - return *varray_; - } + GVMutableArray &operator*(); + GVMutableArray *operator->(); + GVMutableArray &varray(); + AttributeDomain domain() const; + const CPPType &cpp_type() const; + CustomDataType custom_data_type() const; - GVMutableArray *operator->() - { - return varray_.get(); - } - - GVMutableArray &varray() - { - return *varray_; - } - - AttributeDomain domain() const - { - return domain_; - } - - const CPPType &cpp_type() const - { - return varray_->type(); - } - - CustomDataType custom_data_type() const - { - return cpp_type_to_custom_data_type(this->cpp_type()); - } - - fn::GMutableSpan as_span() - { - if (!optional_span_varray_) { - const bool materialize_old_values = !ignore_old_values_; - optional_span_varray_ = std::make_unique(*varray_, - materialize_old_values); - } - fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; - return span_varray; - } - - template MutableSpan as_span() - { - return this->as_span().typed(); - } + fn::GMutableSpan as_span(); + template MutableSpan as_span(); void save(); }; @@ -444,4 +352,138 @@ class CustomDataAttributes { const AttributeDomain domain) const; }; +/* -------------------------------------------------------------------- + * #AttributeIDRef inline methods. + */ + +inline AttributeIDRef::AttributeIDRef() = default; + +inline AttributeIDRef::AttributeIDRef(StringRef name) : name_(name) +{ +} + +inline AttributeIDRef::AttributeIDRef(StringRefNull name) : name_(name) +{ +} + +inline AttributeIDRef::AttributeIDRef(const char *name) : name_(name) +{ +} + +inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name) +{ +} + +/* The anonymous id is only borrowed, the caller has to keep a reference to it. */ +inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id) + : anonymous_id_(anonymous_id) +{ +} + +inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b) +{ + return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_; +} + +inline AttributeIDRef::operator bool() const +{ + return this->is_named() || this->is_anonymous(); +} + +inline uint64_t AttributeIDRef::hash() const +{ + return get_default_hash_2(name_, anonymous_id_); +} + +inline bool AttributeIDRef::is_named() const +{ + return !name_.is_empty(); +} + +inline bool AttributeIDRef::is_anonymous() const +{ + return anonymous_id_ != nullptr; +} + +inline StringRef AttributeIDRef::name() const +{ + BLI_assert(this->is_named()); + return name_; +} + +inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const +{ + BLI_assert(this->is_anonymous()); + return *anonymous_id_; +} + +/* -------------------------------------------------------------------- + * #OutputAttribute inline methods. + */ + +inline OutputAttribute::OutputAttribute() = default; +inline OutputAttribute::OutputAttribute(OutputAttribute &&other) = default; + +inline OutputAttribute::OutputAttribute(GVMutableArrayPtr varray, + AttributeDomain domain, + SaveFn save, + const bool ignore_old_values) + : varray_(std::move(varray)), + domain_(domain), + save_(std::move(save)), + ignore_old_values_(ignore_old_values) +{ +} + +inline OutputAttribute::operator bool() const +{ + return varray_.get() != nullptr; +} + +inline GVMutableArray &OutputAttribute::operator*() +{ + return *varray_; +} + +inline GVMutableArray *OutputAttribute::operator->() +{ + return varray_.get(); +} + +inline GVMutableArray &OutputAttribute::varray() +{ + return *varray_; +} + +inline AttributeDomain OutputAttribute::domain() const +{ + return domain_; +} + +inline const CPPType &OutputAttribute::cpp_type() const +{ + return varray_->type(); +} + +inline CustomDataType OutputAttribute::custom_data_type() const +{ + return cpp_type_to_custom_data_type(this->cpp_type()); +} + +inline fn::GMutableSpan OutputAttribute::as_span() +{ + if (!optional_span_varray_) { + const bool materialize_old_values = !ignore_old_values_; + optional_span_varray_ = std::make_unique(*varray_, + materialize_old_values); + } + fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; + return span_varray; +} + +template inline MutableSpan OutputAttribute::as_span() +{ + return this->as_span().typed(); +} + } // namespace blender::bke From 8fc97a871fa34a0413093bb12c2825e963482a45 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 14:49:15 +0200 Subject: [PATCH 0452/1500] Cleanup: make typed output attribute movable This comes at a small performance cost due to an additional memory allocation, but that is not significant currently. --- source/blender/blenkernel/BKE_attribute_access.hh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 3e9dfda7166..25ee4d3c132 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -244,18 +244,21 @@ class OutputAttribute { template class OutputAttribute_Typed { private: OutputAttribute attribute_; - std::optional> optional_varray_; + std::unique_ptr> optional_varray_; VMutableArray *varray_ = nullptr; public: OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute)) { if (attribute_) { - optional_varray_.emplace(attribute_.varray()); + optional_varray_ = std::make_unique>(attribute_.varray()); varray_ = &**optional_varray_; } } + OutputAttribute_Typed(OutputAttribute_Typed &&other) = default; + ~OutputAttribute_Typed() = default; + operator bool() const { return varray_ != nullptr; From 0998856c923f28b1cf2199b945e3eb70cbface96 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 15:01:02 +0200 Subject: [PATCH 0453/1500] Cleanup: use movable output attribute instead of optional This simplifies the code a bit and improves compile times a bit. --- .../blenkernel/BKE_attribute_access.hh | 11 +++++++ .../node_geo_distribute_points_on_faces.cc | 30 +++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 25ee4d3c132..ef43e21b739 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -248,6 +248,7 @@ template class OutputAttribute_Typed { VMutableArray *varray_ = nullptr; public: + OutputAttribute_Typed() = default; OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute)) { if (attribute_) { @@ -259,6 +260,16 @@ template class OutputAttribute_Typed { OutputAttribute_Typed(OutputAttribute_Typed &&other) = default; ~OutputAttribute_Typed() = default; + OutputAttribute_Typed &operator=(OutputAttribute_Typed &&other) + { + if (this == &other) { + return *this; + } + this->~OutputAttribute_Typed(); + new (this) OutputAttribute_Typed(std::move(other)); + return *this; + } + operator bool() const { return varray_ != nullptr; diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 1a4c5d84dbf..0c8db0d8912 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -331,28 +331,28 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com const Span looptri_indices, const AttributeOutputs &attribute_outputs) { - std::optional> id_attribute; - std::optional> normal_attribute; - std::optional> rotation_attribute; + OutputAttribute_Typed id_attribute; + OutputAttribute_Typed normal_attribute; + OutputAttribute_Typed rotation_attribute; MutableSpan ids; MutableSpan normals; MutableSpan rotations; if (attribute_outputs.stable_id_id) { - id_attribute.emplace(point_component.attribute_try_get_for_output_only( - attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT)); - ids = id_attribute->as_span(); + id_attribute = point_component.attribute_try_get_for_output_only( + attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT); + ids = id_attribute.as_span(); } if (attribute_outputs.normal_id) { - normal_attribute.emplace(point_component.attribute_try_get_for_output_only( - attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT)); - normals = normal_attribute->as_span(); + normal_attribute = point_component.attribute_try_get_for_output_only( + attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT); + normals = normal_attribute.as_span(); } if (attribute_outputs.rotation_id) { - rotation_attribute.emplace(point_component.attribute_try_get_for_output_only( - attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT)); - rotations = rotation_attribute->as_span(); + rotation_attribute = point_component.attribute_try_get_for_output_only( + attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT); + rotations = rotation_attribute.as_span(); } const Mesh &mesh = *mesh_component.get_for_read(); @@ -387,13 +387,13 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com } if (id_attribute) { - id_attribute->save(); + id_attribute.save(); } if (normal_attribute) { - normal_attribute->save(); + normal_attribute.save(); } if (rotation_attribute) { - rotation_attribute->save(); + rotation_attribute.save(); } } From fb34cdc7daa5bdefde561645def81405b9c6f26c Mon Sep 17 00:00:00 2001 From: "Johnny Matthews (guitargeek)" Date: Sun, 3 Oct 2021 07:54:05 -0500 Subject: [PATCH 0454/1500] Geometry Nodes: Points to Volume Fields Update This update of the Points to Volume node allows a field to populate the radius input of the node and removes the implicit realization of instances. Differential Revision: https://developer.blender.org/D12531 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesrna/intern/rna_nodetree.c | 28 +- source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 3 +- .../nodes/legacy/node_geo_points_to_volume.cc | 2 +- .../nodes/node_geo_points_to_volume.cc | 272 ++++++++++++++++++ 9 files changed, 307 insertions(+), 3 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 37d5c5997ad..409d4146706 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -646,6 +646,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodePointsToVolume"), NodeItem("GeometryNodeVolumeToMesh"), ]), GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a81a620ab12..c6d6a17bb13 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1513,6 +1513,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_INPUT_SPLINE_LENGTH 1098 #define GEO_NODE_CURVE_SPLINE_TYPE 1099 #define GEO_NODE_CURVE_SET_HANDLES 1100 +#define GEO_NODE_POINTS_TO_VOLUME 1101 /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 73060caa2f8..079ada6fe26 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5713,6 +5713,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_attribute_proximity(); register_node_type_geo_legacy_attribute_randomize(); register_node_type_geo_legacy_material_assign(); + register_node_type_geo_legacy_points_to_volume(); register_node_type_geo_legacy_select_by_material(); register_node_type_geo_legacy_curve_spline_type(); register_node_type_geo_legacy_curve_reverse(); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index df1be412bc4..7b3463ef241 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9987,7 +9987,7 @@ static void def_geo_object_info(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_points_to_volume(StructRNA *srna) +static void def_geo_legacy_points_to_volume(StructRNA *srna) { PropertyRNA *prop; @@ -10018,6 +10018,32 @@ static void def_geo_points_to_volume(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_points_to_volume(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem resolution_mode_items[] = { + {GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT, + "VOXEL_AMOUNT", + 0, + "Amount", + "Specify the approximate number of voxels along the diagonal"}, + {GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE, + "VOXEL_SIZE", + 0, + "Size", + "Specify the voxel side length"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryPointsToVolume", "storage"); + + prop = RNA_def_property(srna, "resolution_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, resolution_mode_items); + RNA_def_property_ui_text(prop, "Resolution Mode", "How the voxel size is specified"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_collection_info(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 903a30dd383..c487288bc75 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -240,6 +240,7 @@ set(SRC geometry/nodes/node_geo_mesh_to_points.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_points_to_vertices.cc + geometry/nodes/node_geo_points_to_volume.cc geometry/nodes/node_geo_proximity.cc geometry/nodes/node_geo_realize_instances.cc geometry/nodes/node_geo_separate_components.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index bb90c7b6c51..f0dc15996a2 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -33,6 +33,7 @@ void register_node_type_geo_legacy_curve_set_handles(void); void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); void register_node_type_geo_legacy_material_assign(void); +void register_node_type_geo_legacy_points_to_volume(void); void register_node_type_geo_legacy_select_by_material(void); void register_node_type_geo_legacy_curve_spline_type(void); void register_node_type_geo_legacy_curve_reverse(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index c13ab199691..88ccf28d6d3 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -312,7 +312,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_ROTATE, def_geo_point_rotate, "LEGAC DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SCALE, def_geo_point_scale, "LEGACY_POINT_SCALE", LegacyPointScale, "Point Scale", "") DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SEPARATE, 0, "LEGACY_POINT_SEPARATE", LegacyPointSeparate, "Point Separate", "") DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, "LEGACY_POINT_TRANSLATE", LegacyPointTranslate, "Point Translate", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") @@ -368,6 +368,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivid DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "") +DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "") DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc index d920c8de9f0..68d3f232ce1 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc @@ -258,7 +258,7 @@ static void geo_node_points_to_volume_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_points_to_volume() +void register_node_type_geo_legacy_points_to_volume() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc new file mode 100644 index 00000000000..4a51d485b85 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -0,0 +1,272 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifdef WITH_OPENVDB +# include +# include +# include +#endif + +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_volume.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Density").default_value(1.0f).min(0.0f); + b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); + b.add_input("Radius") + .default_value(0.5f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_output("Geometry"); +} + +static void geo_node_points_to_volume_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); +} + +static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN( + sizeof(NodeGeometryPointsToVolume), __func__); + data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; + node->storage = data; +} + +static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage; + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(voxel_amount_socket, + data->resolution_mode == + GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT); + nodeSetSocketAvailability( + voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE); +} + +#ifdef WITH_OPENVDB +namespace { +/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */ +struct ParticleList { + using PosType = openvdb::Vec3R; + + Span positions; + Span radii; + + size_t size() const + { + return (size_t)positions.size(); + } + + void getPos(size_t n, openvdb::Vec3R &xyz) const + { + xyz = &positions[n].x; + } + + void getPosRad(size_t n, openvdb::Vec3R &xyz, openvdb::Real &radius) const + { + xyz = &positions[n].x; + radius = radii[n]; + } +}; +} // namespace + +static openvdb::FloatGrid::Ptr generate_volume_from_points(const Span positions, + const Span radii, + const float density) +{ + /* Create a new grid that will be filled. #ParticlesToLevelSet requires the background value to + * be positive. It will be set to zero later on. */ + openvdb::FloatGrid::Ptr new_grid = openvdb::FloatGrid::create(1.0f); + + /* Create a narrow-band level set grid based on the positions and radii. */ + openvdb::tools::ParticlesToLevelSet op{*new_grid}; + /* Don't ignore particles based on their radius. */ + op.setRmin(0.0f); + op.setRmax(FLT_MAX); + ParticleList particles{positions, radii}; + op.rasterizeSpheres(particles); + op.finalize(); + + /* Convert the level set to a fog volume. This also sets the background value to zero. Inside the + * fog there will be a density of 1. */ + openvdb::tools::sdfToFogVolume(*new_grid); + + /* Take the desired density into account. */ + openvdb::tools::foreach (new_grid->beginValueOn(), + [&](const openvdb::FloatGrid::ValueOnIter &iter) { + iter.modifyValue([&](float &value) { value *= density; }); + }); + return new_grid; +} + +static float compute_voxel_size(const GeoNodeExecParams ¶ms, + Span positions, + const float radius) +{ + const NodeGeometryPointsToVolume &storage = + *(const NodeGeometryPointsToVolume *)params.node().storage; + + if (storage.resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE) { + return params.get_input("Voxel Size"); + } + + if (positions.is_empty()) { + return 0.0f; + } + + float3 min, max; + INIT_MINMAX(min, max); + minmax_v3v3_v3_array(min, max, (float(*)[3])positions.data(), positions.size()); + + const float voxel_amount = params.get_input("Voxel Amount"); + if (voxel_amount <= 1) { + return 0.0f; + } + + /* The voxel size adapts to the final size of the volume. */ + const float diagonal = float3::distance(min, max); + const float extended_diagonal = diagonal + 2.0f * radius; + const float voxel_size = extended_diagonal / voxel_amount; + return voxel_size; +} + +static void gather_point_data_from_component(GeoNodeExecParams ¶ms, + const GeometryComponent &component, + Vector &r_positions, + Vector &r_radii) +{ + GVArray_Typed positions = component.attribute_get_for_read( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + + Field radius_field = params.get_input>("Radius"); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + + r_positions.resize(r_positions.size() + domain_size); + positions->materialize(r_positions.as_mutable_span().take_back(domain_size)); + + r_radii.resize(r_radii.size() + domain_size); + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_size)); + evaluator.evaluate(); +} + +static void convert_to_grid_index_space(const float voxel_size, + MutableSpan positions, + MutableSpan radii) +{ + const float voxel_size_inv = 1.0f / voxel_size; + for (const int i : positions.index_range()) { + positions[i] *= voxel_size_inv; + /* Better align generated grid with source points. */ + positions[i] -= float3(0.5f); + radii[i] *= voxel_size_inv; + } +} + +static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, + GeometrySet &r_geometry_set) +{ + Vector positions; + Vector radii; + + if (r_geometry_set.has()) { + gather_point_data_from_component( + params, *r_geometry_set.get_component_for_read(), positions, radii); + } + if (r_geometry_set.has()) { + gather_point_data_from_component( + params, *r_geometry_set.get_component_for_read(), positions, radii); + } + if (r_geometry_set.has()) { + gather_point_data_from_component( + params, *r_geometry_set.get_component_for_read(), positions, radii); + } + + const float max_radius = *std::max_element(radii.begin(), radii.end()); + const float voxel_size = compute_voxel_size(params, positions, max_radius); + if (voxel_size == 0.0f || positions.is_empty()) { + return; + } + + Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); + BKE_volume_init_grids(volume); + + VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT); + openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast( + BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false)); + + const float density = params.get_input("Density"); + convert_to_grid_index_space(voxel_size, positions, radii); + openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii, density); + /* This merge is cheap, because the #density_grid is empty. */ + density_grid->merge(*new_grid); + density_grid->transform().postScale(voxel_size); + r_geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES}); + r_geometry_set.replace_volume(volume); +} +#endif + +static void geo_node_points_to_volume_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { +#ifdef WITH_OPENVDB + initialize_volume_component_from_points(params, geometry_set); +#endif + }); + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_points_to_volume() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0); + node_type_storage(&ntype, + "NodeGeometryPointsToVolume", + node_free_standard_storage, + node_copy_standard_storage); + node_type_size(&ntype, 170, 120, 700); + node_type_init(&ntype, blender::nodes::geo_node_points_to_volume_init); + node_type_update(&ntype, blender::nodes::geo_node_points_to_volume_update); + ntype.declare = blender::nodes::geo_node_points_to_volume_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_points_to_volume_exec; + ntype.draw_buttons = blender::nodes::geo_node_points_to_volume_layout; + nodeRegisterType(&ntype); +} From 06c3bac23b4aec2b2bacff561840a51d3dab718d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 15:14:03 +0200 Subject: [PATCH 0455/1500] Cleanup: move more methods out of classes --- source/blender/nodes/NOD_node_declaration.hh | 146 +++++++++++-------- 1 file changed, 82 insertions(+), 64 deletions(-) diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 07d4e05cda8..5b8003a03de 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -57,59 +57,15 @@ class OutputFieldDependency { Vector linked_input_indices_; public: - static OutputFieldDependency ForFieldSource() - { - OutputFieldDependency field_dependency; - field_dependency.type_ = OutputSocketFieldType::FieldSource; - return field_dependency; - } + static OutputFieldDependency ForFieldSource(); + static OutputFieldDependency ForDataSource(); + static OutputFieldDependency ForDependentField(); + static OutputFieldDependency ForPartiallyDependentField(Vector indices); - static OutputFieldDependency ForDataSource() - { - OutputFieldDependency field_dependency; - field_dependency.type_ = OutputSocketFieldType::None; - return field_dependency; - } + OutputSocketFieldType field_type() const; + Span linked_input_indices() const; - static OutputFieldDependency ForPartiallyDependentField(Vector indices) - { - OutputFieldDependency field_dependency; - if (indices.is_empty()) { - field_dependency.type_ = OutputSocketFieldType::None; - } - else { - field_dependency.type_ = OutputSocketFieldType::PartiallyDependent; - field_dependency.linked_input_indices_ = std::move(indices); - } - return field_dependency; - } - - static OutputFieldDependency ForDependentField() - { - OutputFieldDependency field_dependency; - field_dependency.type_ = OutputSocketFieldType::DependentField; - return field_dependency; - } - - OutputSocketFieldType field_type() const - { - return type_; - } - - Span linked_input_indices() const - { - return linked_input_indices_; - } - - friend bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b) - { - return a.type_ == b.type_ && a.linked_input_indices_ == b.linked_input_indices_; - } - - friend bool operator!=(const OutputFieldDependency &a, const OutputFieldDependency &b) - { - return !(a == b); - } + friend bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b); }; /** @@ -118,16 +74,6 @@ class OutputFieldDependency { struct FieldInferencingInterface { Vector inputs; Vector outputs; - - friend bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) - { - return a.inputs == b.inputs && a.outputs == b.outputs; - } - - friend bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b) - { - return !(a == b); - } }; /** @@ -309,7 +255,79 @@ class NodeDeclarationBuilder { }; /* -------------------------------------------------------------------- - * SocketDeclaration inline methods. + * #OutputFieldDependency inline methods. + */ + +inline OutputFieldDependency OutputFieldDependency::ForFieldSource() +{ + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::FieldSource; + return field_dependency; +} + +inline OutputFieldDependency OutputFieldDependency::ForDataSource() +{ + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::None; + return field_dependency; +} + +inline OutputFieldDependency OutputFieldDependency::ForDependentField() +{ + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::DependentField; + return field_dependency; +} + +inline OutputFieldDependency OutputFieldDependency::ForPartiallyDependentField(Vector indices) +{ + OutputFieldDependency field_dependency; + if (indices.is_empty()) { + field_dependency.type_ = OutputSocketFieldType::None; + } + else { + field_dependency.type_ = OutputSocketFieldType::PartiallyDependent; + field_dependency.linked_input_indices_ = std::move(indices); + } + return field_dependency; +} + +inline OutputSocketFieldType OutputFieldDependency::field_type() const +{ + return type_; +} + +inline Span OutputFieldDependency::linked_input_indices() const +{ + return linked_input_indices_; +} + +inline bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b) +{ + return a.type_ == b.type_ && a.linked_input_indices_ == b.linked_input_indices_; +} + +inline bool operator!=(const OutputFieldDependency &a, const OutputFieldDependency &b) +{ + return !(a == b); +} + +/* -------------------------------------------------------------------- + * #FieldInferencingInterface inline methods. + */ + +inline bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) +{ + return a.inputs == b.inputs && a.outputs == b.outputs; +} + +inline bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b) +{ + return !(a == b); +} + +/* -------------------------------------------------------------------- + * #SocketDeclaration inline methods. */ inline StringRefNull SocketDeclaration::name() const @@ -337,7 +355,7 @@ inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() } /* -------------------------------------------------------------------- - * NodeDeclarationBuilder inline methods. + * #NodeDeclarationBuilder inline methods. */ inline NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declaration) @@ -377,7 +395,7 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket( } /* -------------------------------------------------------------------- - * NodeDeclaration inline methods. + * #NodeDeclaration inline methods. */ inline Span NodeDeclaration::inputs() const From 2f52f5683ca8454a32c8f9f9e47c56092070e114 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 15:24:21 +0200 Subject: [PATCH 0456/1500] Cleanup: move more method definitions out of their class This makes it easier to test the impact of moving them out of the header later. --- .../blender/nodes/NOD_socket_declarations.hh | 241 +++++++++++------- 1 file changed, 147 insertions(+), 94 deletions(-) diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 3d0cfdb5d5d..3f7f9e0414e 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -46,29 +46,10 @@ class Float : public SocketDeclaration { class FloatBuilder : public SocketDeclarationBuilder { public: - FloatBuilder &min(const float value) - { - decl_->soft_min_value_ = value; - return *this; - } - - FloatBuilder &max(const float value) - { - decl_->soft_max_value_ = value; - return *this; - } - - FloatBuilder &default_value(const float value) - { - decl_->default_value_ = value; - return *this; - } - - FloatBuilder &subtype(PropertySubType subtype) - { - decl_->subtype_ = subtype; - return *this; - } + FloatBuilder &min(const float value); + FloatBuilder &max(const float value); + FloatBuilder &default_value(const float value); + FloatBuilder &subtype(PropertySubType subtype); }; class IntBuilder; @@ -92,29 +73,10 @@ class Int : public SocketDeclaration { class IntBuilder : public SocketDeclarationBuilder { public: - IntBuilder &min(const int value) - { - decl_->soft_min_value_ = value; - return *this; - } - - IntBuilder &max(const int value) - { - decl_->soft_max_value_ = value; - return *this; - } - - IntBuilder &default_value(const int value) - { - decl_->default_value_ = value; - return *this; - } - - IntBuilder &subtype(PropertySubType subtype) - { - decl_->subtype_ = subtype; - return *this; - } + IntBuilder &min(const int value); + IntBuilder &max(const int value); + IntBuilder &default_value(const int value); + IntBuilder &subtype(PropertySubType subtype); }; class VectorBuilder; @@ -138,29 +100,10 @@ class Vector : public SocketDeclaration { class VectorBuilder : public SocketDeclarationBuilder { public: - VectorBuilder &default_value(const float3 value) - { - decl_->default_value_ = value; - return *this; - } - - VectorBuilder &subtype(PropertySubType subtype) - { - decl_->subtype_ = subtype; - return *this; - } - - VectorBuilder &min(const float min) - { - decl_->soft_min_value_ = min; - return *this; - } - - VectorBuilder &max(const float max) - { - decl_->soft_max_value_ = max; - return *this; - } + VectorBuilder &default_value(const float3 value); + VectorBuilder &subtype(PropertySubType subtype); + VectorBuilder &min(const float min); + VectorBuilder &max(const float max); }; class BoolBuilder; @@ -179,11 +122,7 @@ class Bool : public SocketDeclaration { class BoolBuilder : public SocketDeclarationBuilder { public: - BoolBuilder &default_value(const bool value) - { - decl_->default_value_ = value; - return *this; - } + BoolBuilder &default_value(const bool value); }; class ColorBuilder; @@ -203,11 +142,7 @@ class Color : public SocketDeclaration { class ColorBuilder : public SocketDeclarationBuilder { public: - ColorBuilder &default_value(const ColorGeometry4f value) - { - decl_->default_value_ = value; - return *this; - } + ColorBuilder &default_value(const ColorGeometry4f value); }; class String : public SocketDeclaration { @@ -223,9 +158,7 @@ class IDSocketDeclaration : public SocketDeclaration { const char *idname_; public: - IDSocketDeclaration(const char *idname) : idname_(idname) - { - } + IDSocketDeclaration(const char *idname); bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; bool matches(const bNodeSocket &socket) const override; @@ -236,36 +169,28 @@ class Object : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder; - Object() : IDSocketDeclaration("NodeSocketObject") - { - } + Object(); }; class Material : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder; - Material() : IDSocketDeclaration("NodeSocketMaterial") - { - } + Material(); }; class Collection : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder; - Collection() : IDSocketDeclaration("NodeSocketCollection") - { - } + Collection(); }; class Texture : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder; - Texture() : IDSocketDeclaration("NodeSocketTexture") - { - } + Texture(); }; class Geometry : public SocketDeclaration { @@ -276,4 +201,132 @@ class Geometry : public SocketDeclaration { bool matches(const bNodeSocket &socket) const override; }; +/* -------------------------------------------------------------------- + * #FloatBuilder inline methods. + */ + +inline FloatBuilder &FloatBuilder::min(const float value) +{ + decl_->soft_min_value_ = value; + return *this; +} + +inline FloatBuilder &FloatBuilder::max(const float value) +{ + decl_->soft_max_value_ = value; + return *this; +} + +inline FloatBuilder &FloatBuilder::default_value(const float value) +{ + decl_->default_value_ = value; + return *this; +} + +inline FloatBuilder &FloatBuilder::subtype(PropertySubType subtype) +{ + decl_->subtype_ = subtype; + return *this; +} + +/* -------------------------------------------------------------------- + * #IntBuilder inline methods. + */ + +inline IntBuilder &IntBuilder::min(const int value) +{ + decl_->soft_min_value_ = value; + return *this; +} + +inline IntBuilder &IntBuilder::max(const int value) +{ + decl_->soft_max_value_ = value; + return *this; +} + +inline IntBuilder &IntBuilder::default_value(const int value) +{ + decl_->default_value_ = value; + return *this; +} + +inline IntBuilder &IntBuilder::subtype(PropertySubType subtype) +{ + decl_->subtype_ = subtype; + return *this; +} + +/* -------------------------------------------------------------------- + * #VectorBuilder inline methods. + */ + +inline VectorBuilder &VectorBuilder::default_value(const float3 value) +{ + decl_->default_value_ = value; + return *this; +} + +inline VectorBuilder &VectorBuilder::subtype(PropertySubType subtype) +{ + decl_->subtype_ = subtype; + return *this; +} + +inline VectorBuilder &VectorBuilder::min(const float min) +{ + decl_->soft_min_value_ = min; + return *this; +} + +inline VectorBuilder &VectorBuilder::max(const float max) +{ + decl_->soft_max_value_ = max; + return *this; +} + +/* -------------------------------------------------------------------- + * #BoolBuilder inline methods. + */ + +inline BoolBuilder &BoolBuilder::default_value(const bool value) +{ + decl_->default_value_ = value; + return *this; +} + +/* -------------------------------------------------------------------- + * #ColorBuilder inline methods. + */ + +inline ColorBuilder &ColorBuilder::default_value(const ColorGeometry4f value) +{ + decl_->default_value_ = value; + return *this; +} + +/* -------------------------------------------------------------------- + * #IDSocketDeclaration and children inline methods. + */ + +inline IDSocketDeclaration::IDSocketDeclaration(const char *idname) : idname_(idname) +{ +} + +inline Object::Object() : IDSocketDeclaration("NodeSocketObject") +{ +} + +inline Material::Material() : IDSocketDeclaration("NodeSocketMaterial") +{ +} + +inline Collection::Collection() : IDSocketDeclaration("NodeSocketCollection") +{ +} + +inline Texture::Texture() : IDSocketDeclaration("NodeSocketTexture") +{ +} + } // namespace blender::nodes::decl From a812fe8ceb75fd2befe44a151f1e214f357c24c2 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 16:05:19 +0200 Subject: [PATCH 0457/1500] Nodes: use extern templates for socket declarations The new socket declaration api generates a surprising amount of symbols in each translation unit where it is used. This resulted in a measurable compile time increase. This commit reduces the number of symbols that are generated in each translation unit significantly. For example, in `node_geo_distribute_points_on_faces.cc` the number of symbols decreased from 1930 to 1335. In my tests, this results in a 5-20% compile time speedup when this and similar files are compiled in isolation (measured by executing the command in `compile_commands.json`). Compiling the distribute points on faces node sped up from ~2.65s to ~2.4s. --- source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_node_declaration.hh | 1 + .../blender/nodes/NOD_socket_declarations.hh | 21 ++++++++++++ .../nodes/intern/extern_implementations.cc | 34 +++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 source/blender/nodes/intern/extern_implementations.cc diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index c487288bc75..276af617d1c 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -377,6 +377,7 @@ set(SRC texture/node_texture_util.c intern/derived_node_tree.cc + intern/extern_implementations.cc intern/geometry_nodes_eval_log.cc intern/math_functions.cc intern/node_common.cc diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 5b8003a03de..aa01afc6a14 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -197,6 +197,7 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { { decl_->output_field_dependency_ = OutputFieldDependency::ForPartiallyDependentField( std::move(input_dependencies)); + return *(Self *)this; } }; diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 3f7f9e0414e..6eac77b1d6b 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -330,3 +330,24 @@ inline Texture::Texture() : IDSocketDeclaration("NodeSocketTexture") } } // namespace blender::nodes::decl + +/* -------------------------------------------------------------------- + * Extern template instantiations that are defined in `intern/extern_implementations.cc`. + */ + +namespace blender::nodes { +#define MAKE_EXTERN_SOCKET_DECLARATION(TYPE) \ + extern template class SocketDeclarationBuilder; \ + extern template TYPE::Builder &NodeDeclarationBuilder::add_input(StringRef, StringRef); \ + extern template TYPE::Builder &NodeDeclarationBuilder::add_output(StringRef, StringRef); + +MAKE_EXTERN_SOCKET_DECLARATION(decl::Float) +MAKE_EXTERN_SOCKET_DECLARATION(decl::Int) +MAKE_EXTERN_SOCKET_DECLARATION(decl::Vector) +MAKE_EXTERN_SOCKET_DECLARATION(decl::Bool) +MAKE_EXTERN_SOCKET_DECLARATION(decl::Color) +MAKE_EXTERN_SOCKET_DECLARATION(decl::String) +MAKE_EXTERN_SOCKET_DECLARATION(decl::Geometry) + +#undef MAKE_EXTERN_SOCKET_DECLARATION +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/extern_implementations.cc b/source/blender/nodes/intern/extern_implementations.cc new file mode 100644 index 00000000000..42d4b2878bc --- /dev/null +++ b/source/blender/nodes/intern/extern_implementations.cc @@ -0,0 +1,34 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "NOD_socket_declarations.hh" + +namespace blender::nodes { +#define MAKE_EXTERN_SOCKET_IMPLEMENTATION(TYPE) \ + template class SocketDeclarationBuilder; \ + template TYPE::Builder &NodeDeclarationBuilder::add_input(StringRef, StringRef); \ + template TYPE::Builder &NodeDeclarationBuilder::add_output(StringRef, StringRef); + +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Float) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Int) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Vector) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Bool) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Color) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::String) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Geometry) + +#undef MAKE_EXTERN_SOCKET_IMPLEMENTATION +} // namespace blender::nodes From 17e2d54a3d06c699bc736b5aebcb56e37fb203d4 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 16:30:14 +0200 Subject: [PATCH 0458/1500] Cleanup: use extern templates for typed output attribute This is very similar to rBa812fe8ceb75fd2b. This time the number of symbols decreases further from 1335 to 928. Compile time of the distribute node decreases from ~2.4s to ~2.3s. --- .../blenkernel/BKE_attribute_access.hh | 25 ++++++++++++++--- source/blender/blenkernel/CMakeLists.txt | 1 + .../intern/extern_implementations.cc | 27 +++++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 source/blender/blenkernel/intern/extern_implementations.cc diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index ef43e21b739..baa918d9949 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -248,7 +248,7 @@ template class OutputAttribute_Typed { VMutableArray *varray_ = nullptr; public: - OutputAttribute_Typed() = default; + OutputAttribute_Typed(); OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute)) { if (attribute_) { @@ -257,8 +257,8 @@ template class OutputAttribute_Typed { } } - OutputAttribute_Typed(OutputAttribute_Typed &&other) = default; - ~OutputAttribute_Typed() = default; + OutputAttribute_Typed(OutputAttribute_Typed &&other); + ~OutputAttribute_Typed(); OutputAttribute_Typed &operator=(OutputAttribute_Typed &&other) { @@ -316,6 +316,13 @@ template class OutputAttribute_Typed { } }; +/* These are not defined in the class directly, because when defining them there, the external + * template instantiation does not work, resulting in longer compile times. */ +template inline OutputAttribute_Typed::OutputAttribute_Typed() = default; +template +inline OutputAttribute_Typed::OutputAttribute_Typed(OutputAttribute_Typed &&other) = default; +template inline OutputAttribute_Typed::~OutputAttribute_Typed() = default; + /** * A basic container around DNA CustomData so that its users * don't have to implement special copy and move constructors. @@ -501,3 +508,15 @@ template inline MutableSpan OutputAttribute::as_span() } } // namespace blender::bke + +/* -------------------------------------------------------------------- + * Extern template instantiations that are defined in `intern/extern_implementations.cc`. + */ + +namespace blender::bke { +extern template class OutputAttribute_Typed; +extern template class OutputAttribute_Typed; +extern template class OutputAttribute_Typed; +extern template class OutputAttribute_Typed; +extern template class OutputAttribute_Typed; +} // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index fb7fdd1ac21..5a4bc148c0a 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -134,6 +134,7 @@ set(SRC intern/editmesh_cache.c intern/editmesh_tangent.c intern/effect.c + intern/extern_implementations.cc intern/fcurve.c intern/fcurve_cache.c intern/fcurve_driver.c diff --git a/source/blender/blenkernel/intern/extern_implementations.cc b/source/blender/blenkernel/intern/extern_implementations.cc new file mode 100644 index 00000000000..07a4b6fc455 --- /dev/null +++ b/source/blender/blenkernel/intern/extern_implementations.cc @@ -0,0 +1,27 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_attribute_access.hh" + +namespace blender::bke { + +template class OutputAttribute_Typed; +template class OutputAttribute_Typed; +template class OutputAttribute_Typed; +template class OutputAttribute_Typed; +template class OutputAttribute_Typed; + +} // namespace blender::bke From 64d07ffcc32258c2a39800f1b930f7d262508d0d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 16:47:54 +0200 Subject: [PATCH 0459/1500] Cleanup: move methods out of field classes This makes it easier to scan through the classes and simplifies testing the compile time impact of having these methods in the header. --- source/blender/functions/FN_field.hh | 196 +++++++++++++++------------ 1 file changed, 113 insertions(+), 83 deletions(-) diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 3ce0993da59..eeb97946029 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -77,29 +77,15 @@ class FieldNode { bool depends_on_input_; public: - FieldNode(bool is_input, bool depends_on_input) - : is_input_(is_input), depends_on_input_(depends_on_input) - { - } + FieldNode(bool is_input, bool depends_on_input); virtual ~FieldNode() = default; virtual const CPPType &output_cpp_type(int output_index) const = 0; - bool is_input() const - { - return is_input_; - } - - bool is_operation() const - { - return !is_input_; - } - - bool depends_on_input() const - { - return depends_on_input_; - } + bool is_input() const; + bool is_operation() const; + bool depends_on_input() const; /** * Invoke callback for every field input. It might be called multiple times for the same input. @@ -107,25 +93,8 @@ class FieldNode { */ virtual void foreach_field_input(FunctionRef foreach_fn) const = 0; - virtual uint64_t hash() const - { - return get_default_hash(this); - } - - friend bool operator==(const FieldNode &a, const FieldNode &b) - { - return a.is_equal_to(b); - } - - friend bool operator!=(const FieldNode &a, const FieldNode &b) - { - return !(a == b); - } - - virtual bool is_equal_to(const FieldNode &other) const - { - return this == &other; - } + virtual uint64_t hash() const; + virtual bool is_equal_to(const FieldNode &other) const; }; /** @@ -245,32 +214,10 @@ class FieldOperation : public FieldNode { FieldOperation(std::unique_ptr function, Vector inputs = {}); FieldOperation(const MultiFunction &function, Vector inputs = {}); - Span inputs() const - { - return inputs_; - } - - const MultiFunction &multi_function() const - { - return *function_; - } - - const CPPType &output_cpp_type(int output_index) const override - { - int output_counter = 0; - for (const int param_index : function_->param_indices()) { - MFParamType param_type = function_->param_type(param_index); - if (param_type.is_output()) { - if (output_counter == output_index) { - return param_type.data_type().single_type(); - } - output_counter++; - } - } - BLI_assert_unreachable(); - return CPPType::get(); - } + Span inputs() const; + const MultiFunction &multi_function() const; + const CPPType &output_cpp_type(int output_index) const override; void foreach_field_input(FunctionRef foreach_fn) const override; }; @@ -295,28 +242,11 @@ class FieldInput : public FieldNode { IndexMask mask, ResourceScope &scope) const = 0; - virtual std::string socket_inspection_name() const - { - return debug_name_; - } - - blender::StringRef debug_name() const - { - return debug_name_; - } - - const CPPType &cpp_type() const - { - return *type_; - } - - const CPPType &output_cpp_type(int output_index) const override - { - BLI_assert(output_index == 0); - UNUSED_VARS_NDEBUG(output_index); - return *type_; - } + virtual std::string socket_inspection_name() const; + blender::StringRef debug_name() const; + const CPPType &cpp_type() const; + const CPPType &output_cpp_type(int output_index) const override; void foreach_field_input(FunctionRef foreach_fn) const override; }; @@ -493,4 +423,104 @@ class IndexFieldInput final : public FieldInput { ResourceScope &scope) const final; }; +/* -------------------------------------------------------------------- + * #FieldNode inline methods. + */ + +inline FieldNode::FieldNode(bool is_input, bool depends_on_input) + : is_input_(is_input), depends_on_input_(depends_on_input) +{ +} + +inline bool FieldNode::is_input() const +{ + return is_input_; +} + +inline bool FieldNode::is_operation() const +{ + return !is_input_; +} + +inline bool FieldNode::depends_on_input() const +{ + return depends_on_input_; +} + +inline uint64_t FieldNode::hash() const +{ + return get_default_hash(this); +} + +inline bool FieldNode::is_equal_to(const FieldNode &other) const +{ + return this == &other; +} + +inline bool operator==(const FieldNode &a, const FieldNode &b) +{ + return a.is_equal_to(b); +} + +inline bool operator!=(const FieldNode &a, const FieldNode &b) +{ + return !(a == b); +} + +/* -------------------------------------------------------------------- + * #FieldOperation inline methods. + */ + +inline Span FieldOperation::inputs() const +{ + return inputs_; +} + +inline const MultiFunction &FieldOperation::multi_function() const +{ + return *function_; +} + +inline const CPPType &FieldOperation::output_cpp_type(int output_index) const +{ + int output_counter = 0; + for (const int param_index : function_->param_indices()) { + MFParamType param_type = function_->param_type(param_index); + if (param_type.is_output()) { + if (output_counter == output_index) { + return param_type.data_type().single_type(); + } + output_counter++; + } + } + BLI_assert_unreachable(); + return CPPType::get(); +} + +/* -------------------------------------------------------------------- + * #FieldInput inline methods. + */ + +inline std::string FieldInput::socket_inspection_name() const +{ + return debug_name_; +} + +inline StringRef FieldInput::debug_name() const +{ + return debug_name_; +} + +inline const CPPType &FieldInput::cpp_type() const +{ + return *type_; +} + +inline const CPPType &FieldInput::output_cpp_type(int output_index) const +{ + BLI_assert(output_index == 0); + UNUSED_VARS_NDEBUG(output_index); + return *type_; +} + } // namespace blender::fn From bf354cde9691cd26ad5110bd275a581976b06911 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 16:58:33 +0200 Subject: [PATCH 0460/1500] Cleanup: move special methods of geometry set out of header This reduces the compile time, because fewer symbols have to be generated in translation units using geometry sets. --- source/blender/blenkernel/BKE_geometry_set.hh | 7 +++++++ source/blender/blenkernel/intern/geometry_set.cc | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index f182fb527e1..b36d15578a7 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -253,6 +253,13 @@ struct GeometrySet { blender::Map components_; public: + GeometrySet(); + GeometrySet(const GeometrySet &other); + GeometrySet(GeometrySet &&other); + ~GeometrySet(); + GeometrySet &operator=(const GeometrySet &other); + GeometrySet &operator=(GeometrySet &&other); + GeometryComponent &get_component_for_write(GeometryComponentType component_type); template Component &get_component_for_write() { diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 0aac6ae3adf..84daa06554a 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -105,6 +105,14 @@ bool GeometryComponent::is_empty() const /** \name Geometry Set * \{ */ +/* The methods are defaulted here so that they are not instantiated in every translation unit. */ +GeometrySet::GeometrySet() = default; +GeometrySet::GeometrySet(const GeometrySet &other) = default; +GeometrySet::GeometrySet(GeometrySet &&other) = default; +GeometrySet::~GeometrySet() = default; +GeometrySet &GeometrySet::operator=(const GeometrySet &other) = default; +GeometrySet &GeometrySet::operator=(GeometrySet &&other) = default; + /* This method can only be used when the geometry set is mutable. It returns a mutable geometry * component of the given type. */ From 1833ebea31a73ff8a76d4742c51bad4dfc318cff Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 3 Oct 2021 17:07:26 +0200 Subject: [PATCH 0461/1500] Cleanup: move as_span method out of header This method does not have to be in a header and results in a relatively large number of symbols to be generated (42). --- source/blender/blenkernel/BKE_attribute_access.hh | 11 ----------- source/blender/blenkernel/intern/attribute_access.cc | 11 +++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index baa918d9949..20b48160feb 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -491,17 +491,6 @@ inline CustomDataType OutputAttribute::custom_data_type() const return cpp_type_to_custom_data_type(this->cpp_type()); } -inline fn::GMutableSpan OutputAttribute::as_span() -{ - if (!optional_span_varray_) { - const bool materialize_old_values = !ignore_old_values_; - optional_span_varray_ = std::make_unique(*varray_, - materialize_old_values); - } - fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; - return span_varray; -} - template inline MutableSpan OutputAttribute::as_span() { return this->as_span().typed(); diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index c2837b522c4..d686cd8fa55 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -202,6 +202,17 @@ AttributeDomain attribute_domain_highest_priority(Span domains) return highest_priority_domain; } +fn::GMutableSpan OutputAttribute::as_span() +{ + if (!optional_span_varray_) { + const bool materialize_old_values = !ignore_old_values_; + optional_span_varray_ = std::make_unique(*varray_, + materialize_old_values); + } + fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; + return span_varray; +} + void OutputAttribute::save() { save_has_been_called_ = true; From 98fe05fb5b9e1e2ee184444ff56806fa66de2615 Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Sun, 3 Oct 2021 11:26:48 -0400 Subject: [PATCH 0462/1500] Fix T91810 Bevel not working well with certain unbeveled edge positions. A previous commit, c56526d8b68ab, which sometimes didn't drop offsets into 'in plane' faces, as a fix to T71329, was overly aggressive. If all the intermediate edges are in the same plane then it is fine to just put the meeting point on the plane of the start and end edges. --- source/blender/bmesh/tools/bmesh_bevel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 1f759e9ef43..8bd498a08bd 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -3048,7 +3048,9 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) } } else { - offset_meet(bp, e, e2, bv->v, e->fnext, true, co, eip); + /* Since all edges between e and e2 are in the same plane, it is OK + * to treat this like the case where there are no edges between. */ + offset_meet(bp, e, e2, bv->v, e->fnext, false, co, NULL); } } From ae865844040c084ca650376bbfdda3cc9998f2d2 Mon Sep 17 00:00:00 2001 From: "Johnny Matthews (guitargeek)" Date: Sun, 3 Oct 2021 13:43:51 -0500 Subject: [PATCH 0463/1500] Geometry Nodes: Points to Volume Fields fixes A few items when OpenVDB is not enabled. - Cleanup a compiler warning - Add a node warning message - Return an empty geometry set --- .../nodes/geometry/nodes/node_geo_points_to_volume.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 4a51d485b85..719523f64f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -241,13 +241,16 @@ static void geo_node_points_to_volume_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { #ifdef WITH_OPENVDB + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { initialize_volume_component_from_points(params, geometry_set); -#endif }); - params.set_output("Geometry", std::move(geometry_set)); +#else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); + params.set_output("Geometry", GeometrySet()); +#endif } } // namespace blender::nodes From adc084a3e9bb9b3a88a7b1b436859516671ce37c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 3 Oct 2021 14:40:08 -0500 Subject: [PATCH 0464/1500] Cleanup: Move curveprofile.c to C++ --- source/blender/blenkernel/CMakeLists.txt | 2 +- .../{curveprofile.c => curveprofile.cc} | 85 +++++++++---------- 2 files changed, 40 insertions(+), 47 deletions(-) rename source/blender/blenkernel/intern/{curveprofile.c => curveprofile.cc} (94%) diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 5a4bc148c0a..37581ad5c00 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -120,7 +120,7 @@ set(SRC intern/curve_deform.c intern/curve_eval.cc intern/curve_to_mesh_convert.cc - intern/curveprofile.c + intern/curveprofile.cc intern/customdata.c intern/customdata_file.c intern/data_transfer.c diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.cc similarity index 94% rename from source/blender/blenkernel/intern/curveprofile.c rename to source/blender/blenkernel/intern/curveprofile.cc index 00cdc7b3031..0adce991d0f 100644 --- a/source/blender/blenkernel/intern/curveprofile.c +++ b/source/blender/blenkernel/intern/curveprofile.cc @@ -21,25 +21,17 @@ * \ingroup bke */ -#include -#include -#include -#include - #include "MEM_guardedalloc.h" #include "DNA_curve_types.h" #include "DNA_curveprofile_types.h" -#include "BLI_blenlib.h" -#include "BLI_math.h" -#include "BLI_task.h" -#include "BLI_threads.h" +#include "BLI_math_vector.h" +#include "BLI_rect.h" #include "BLI_utildefines.h" #include "BKE_curve.h" #include "BKE_curveprofile.h" -#include "BKE_fcurve.h" #include "BLO_read_write.h" @@ -62,9 +54,9 @@ void BKE_curveprofile_copy_data(CurveProfile *target, const CurveProfile *profil { *target = *profile; - target->path = MEM_dupallocN(profile->path); - target->table = MEM_dupallocN(profile->table); - target->segments = MEM_dupallocN(profile->segments); + target->path = (CurveProfilePoint *)MEM_dupallocN(profile->path); + target->table = (CurveProfilePoint *)MEM_dupallocN(profile->table); + target->segments = (CurveProfilePoint *)MEM_dupallocN(profile->segments); /* Update the reference the points have to the profile. */ for (int i = 0; i < target->path_len; i++) { @@ -75,11 +67,11 @@ void BKE_curveprofile_copy_data(CurveProfile *target, const CurveProfile *profil CurveProfile *BKE_curveprofile_copy(const CurveProfile *profile) { if (profile) { - CurveProfile *new_prdgt = MEM_dupallocN(profile); + CurveProfile *new_prdgt = (CurveProfile *)MEM_dupallocN(profile); BKE_curveprofile_copy_data(new_prdgt, profile); return new_prdgt; } - return NULL; + return nullptr; } /** @@ -201,8 +193,8 @@ bool BKE_curveprofile_remove_point(CurveProfile *profile, CurveProfilePoint *poi return false; } - CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len, - "profile path"); + CurveProfilePoint *new_path = (CurveProfilePoint *)MEM_mallocN( + sizeof(CurveProfilePoint) * profile->path_len, __func__); int i_delete = (int)(point - profile->path); BLI_assert(i_delete > 0); @@ -229,8 +221,8 @@ bool BKE_curveprofile_remove_point(CurveProfile *profile, CurveProfilePoint *poi void BKE_curveprofile_remove_by_flag(CurveProfile *profile, const short flag) { /* Copy every point without the flag into the new path. */ - CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len, - "profile path"); + CurveProfilePoint *new_path = (CurveProfilePoint *)MEM_mallocN( + sizeof(CurveProfilePoint) * profile->path_len, __func__); /* Build the new list without any of the points with the flag. Keep the first and last points. */ int i_new = 1; @@ -278,7 +270,7 @@ CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float /* Don't add more control points than the maximum size of the higher resolution table. */ if (profile->path_len == PROF_TABLE_MAX - 1) { - return NULL; + return nullptr; } /* Find the index at the line segment that's closest to the new position. */ @@ -297,9 +289,9 @@ CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float /* Insert the new point at the location we found and copy all of the old points in as well. */ profile->path_len++; - CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len, - "profile path"); - CurveProfilePoint *new_pt = NULL; + CurveProfilePoint *new_path = (CurveProfilePoint *)MEM_mallocN( + sizeof(CurveProfilePoint) * profile->path_len, __func__); + CurveProfilePoint *new_pt = nullptr; for (int i_new = 0, i_old = 0; i_new < profile->path_len; i_new++) { if (i_new != i_insert) { /* Insert old points. */ @@ -341,7 +333,7 @@ void BKE_curveprofile_selected_handle_set(CurveProfile *profile, int type_1, int if (type_1 == HD_ALIGN && type_2 == HD_ALIGN) { /* Align the handles. */ - BKE_curveprofile_move_handle(&profile->path[i], true, false, NULL); + BKE_curveprofile_move_handle(&profile->path[i], true, false, nullptr); } } } @@ -365,8 +357,8 @@ void BKE_curveprofile_reverse(CurveProfile *profile) if (profile->path_len == 2) { return; } - CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len, - "profile path"); + CurveProfilePoint *new_path = (CurveProfilePoint *)MEM_mallocN( + sizeof(CurveProfilePoint) * profile->path_len, __func__); /* Mirror the new points across the y = x line */ for (int i = 0; i < profile->path_len; i++) { int i_reversed = profile->path_len - i - 1; @@ -452,7 +444,7 @@ void BKE_curveprofile_reset(CurveProfile *profile) { MEM_SAFE_FREE(profile->path); - eCurveProfilePresets preset = profile->preset; + eCurveProfilePresets preset = static_cast(profile->preset); switch (preset) { case PROF_PRESET_LINE: profile->path_len = 2; @@ -485,7 +477,8 @@ void BKE_curveprofile_reset(CurveProfile *profile) break; } - profile->path = MEM_callocN(sizeof(CurveProfilePoint) * profile->path_len, "profile path"); + profile->path = (CurveProfilePoint *)MEM_callocN(sizeof(CurveProfilePoint) * profile->path_len, + __func__); switch (preset) { case PROF_PRESET_LINE: @@ -536,7 +529,7 @@ void BKE_curveprofile_reset(CurveProfile *profile) } MEM_SAFE_FREE(profile->table); - profile->table = NULL; + profile->table = nullptr; } /** @@ -564,7 +557,7 @@ static void point_calculate_handle(CurveProfilePoint *point, float pt[2]; const float *prev_loc, *next_loc; - if (prev == NULL) { + if (prev == nullptr) { next_loc = &next->x; pt[0] = 2.0f * point_loc[0] - next_loc[0]; pt[1] = 2.0f * point_loc[1] - next_loc[1]; @@ -574,7 +567,7 @@ static void point_calculate_handle(CurveProfilePoint *point, prev_loc = &prev->x; } - if (next == NULL) { + if (next == nullptr) { prev_loc = &prev->x; pt[0] = 2.0f * point_loc[0] - prev_loc[0]; pt[1] = 2.0f * point_loc[1] - prev_loc[1]; @@ -625,11 +618,11 @@ static void point_calculate_handle(CurveProfilePoint *point, static void calculate_path_handles(CurveProfilePoint *path, int path_len) { - point_calculate_handle(&path[0], NULL, &path[1]); + point_calculate_handle(&path[0], nullptr, &path[1]); for (int i = 1; i < path_len - 1; i++) { point_calculate_handle(&path[i], &path[i - 1], &path[i + 1]); } - point_calculate_handle(&path[path_len - 1], &path[path_len - 2], NULL); + point_calculate_handle(&path[path_len - 1], &path[path_len - 2], nullptr); } /** @@ -651,12 +644,12 @@ static float bezt_edge_handle_angle(const CurveProfilePoint *path, int i_edge) } /** Struct to sort curvature of control point edges. */ -typedef struct { +struct CurvatureSortPoint { /** The index of the corresponding profile point. */ int point_index; /** The curvature of the edge with the above index. */ float point_curvature; -} CurvatureSortPoint; +}; /** * Helper function for 'BKE_curveprofile_create_samples' for sorting edges based on curvature. @@ -700,8 +693,8 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, calculate_path_handles(path, totpoints); /* Create a list of edge indices with the most curved at the start, least curved at the end. */ - CurvatureSortPoint *curve_sorted = MEM_callocN(sizeof(CurvatureSortPoint) * totedges, - "curve sorted"); + CurvatureSortPoint *curve_sorted = (CurvatureSortPoint *)MEM_callocN( + sizeof(CurvatureSortPoint) * totedges, __func__); for (int i = 0; i < totedges; i++) { curve_sorted[i].point_index = i; /* Calculate the curvature of each edge once for use when sorting for curvature. */ @@ -710,7 +703,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, qsort(curve_sorted, totedges, sizeof(CurvatureSortPoint), sort_points_curvature); /* Assign the number of sampled points for each edge. */ - int16_t *n_samples = MEM_callocN(sizeof(int16_t) * totedges, "samples numbers"); + int16_t *n_samples = (int16_t *)MEM_callocN(sizeof(int16_t) * totedges, "samples numbers"); int n_added = 0; int n_left; if (n_segments >= totedges) { @@ -819,8 +812,8 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, static void curveprofile_make_table(CurveProfile *profile) { int n_samples = PROF_TABLE_LEN(profile->path_len); - CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1), - __func__); + CurveProfilePoint *new_table = (CurveProfilePoint *)MEM_callocN( + sizeof(CurveProfilePoint) * (n_samples + 1), __func__); BKE_curveprofile_create_samples(profile, n_samples - 1, false, new_table); /* Manually add last point at the end of the profile */ @@ -841,8 +834,8 @@ static void curveprofile_make_segments_table(CurveProfile *profile) if (n_samples <= 0) { return; } - CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1), - __func__); + CurveProfilePoint *new_table = (CurveProfilePoint *)MEM_callocN( + sizeof(CurveProfilePoint) * (n_samples + 1), __func__); if (profile->flag & PROF_SAMPLE_EVEN_LENGTHS) { /* Even length sampling incompatible with only straight edge sampling for now. */ @@ -869,7 +862,7 @@ void BKE_curveprofile_set_defaults(CurveProfile *profile) profile->clip_rect = profile->view_rect; profile->path_len = 2; - profile->path = MEM_callocN(2 * sizeof(CurveProfilePoint), "path points"); + profile->path = (CurveProfilePoint *)MEM_callocN(2 * sizeof(CurveProfilePoint), __func__); profile->path[0].x = 1.0f; profile->path[0].y = 0.0f; @@ -886,7 +879,7 @@ void BKE_curveprofile_set_defaults(CurveProfile *profile) */ struct CurveProfile *BKE_curveprofile_add(eCurveProfilePresets preset) { - CurveProfile *profile = MEM_callocN(sizeof(CurveProfile), "curve profile"); + CurveProfile *profile = (CurveProfile *)MEM_callocN(sizeof(CurveProfile), __func__); BKE_curveprofile_set_defaults(profile); profile->preset = preset; @@ -1120,8 +1113,8 @@ void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct Curve void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile) { BLO_read_data_address(reader, &profile->path); - profile->table = NULL; - profile->segments = NULL; + profile->table = nullptr; + profile->segments = nullptr; /* Reset the points' pointers to the profile. */ for (int i = 0; i < profile->path_len; i++) { From 2305f270c582c7771c20d5f8b50ce8e13b48edd0 Mon Sep 17 00:00:00 2001 From: "Johnny Matthews (guitargeek)" Date: Sun, 3 Oct 2021 15:00:28 -0500 Subject: [PATCH 0465/1500] Geometry Nodes: Handle Type Selection Node Update This node creates a boolean field selection of bezier spline points that have a handle of the given type on the selected 'side' of the contol point. This is evaluated on the point domain. Differential Revision: https://developer.blender.org/D12559 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 4 +- source/blender/makesrna/intern/rna_nodetree.c | 18 ++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 2 + source/blender/nodes/NOD_static_types.h | 1 + .../node_geo_curve_select_by_handle_type.cc | 2 +- .../node_geo_curve_handle_type_selection.cc | 180 ++++++++++++++++++ 9 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 409d4146706..923ea22e1e3 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -532,6 +532,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveSetHandles"), NodeItem("GeometryNodeInputTangent"), NodeItem("GeometryNodeCurveSample"), + NodeItem("GeometryNodeCurveHandleTypeSelection"), NodeItem("GeometryNodeCurveFillet"), NodeItem("GeometryNodeCurveReverse"), ]), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index c6d6a17bb13..79ae9d71762 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1514,6 +1514,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_SPLINE_TYPE 1099 #define GEO_NODE_CURVE_SET_HANDLES 1100 #define GEO_NODE_POINTS_TO_VOLUME 1101 +#define GEO_NODE_CURVE_HANDLE_TYPE_SELECTION 1102 /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 079ada6fe26..75b9d07ca98 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5717,8 +5717,8 @@ static void registerGeometryNodes() register_node_type_geo_legacy_select_by_material(); register_node_type_geo_legacy_curve_spline_type(); register_node_type_geo_legacy_curve_reverse(); + register_node_type_geo_legacy_select_by_handle_type(); register_node_type_geo_legacy_curve_subdivide(); - register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); register_node_type_geo_attribute_clamp(); @@ -5744,6 +5744,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_endpoints(); register_node_type_geo_curve_fill(); register_node_type_geo_curve_fillet(); + register_node_type_geo_curve_handle_type_selection(); register_node_type_geo_curve_length(); register_node_type_geo_curve_parameter(); register_node_type_geo_curve_primitive_bezier_segment(); @@ -5801,7 +5802,6 @@ static void registerGeometryNodes() register_node_type_geo_raycast(); register_node_type_geo_realize_instances(); register_node_type_geo_sample_texture(); - register_node_type_geo_select_by_handle_type(); register_node_type_geo_separate_components(); register_node_type_geo_set_position(); register_node_type_geo_string_join(); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 7b3463ef241..625a5fb5606 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9706,6 +9706,24 @@ static void def_geo_curve_select_handles(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_handle_type_selection(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSelectHandles", "storage"); + + prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "handle_type"); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); + RNA_def_property_ui_text(prop, "Mode", "Whether to check the type of left and right handles"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_primitive_circle(StructRNA *srna) { static const EnumPropertyItem mode_items[] = { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 276af617d1c..dab7579d946 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -198,6 +198,7 @@ set(SRC geometry/nodes/node_geo_convex_hull.cc geometry/nodes/node_geo_curve_fill.cc geometry/nodes/node_geo_curve_fillet.cc + geometry/nodes/node_geo_curve_handle_type_selection.cc geometry/nodes/node_geo_curve_length.cc geometry/nodes/node_geo_curve_parameter.cc geometry/nodes/node_geo_curve_primitive_bezier_segment.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index f0dc15996a2..c5b0b8f5611 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -37,6 +37,7 @@ void register_node_type_geo_legacy_points_to_volume(void); void register_node_type_geo_legacy_select_by_material(void); void register_node_type_geo_legacy_curve_spline_type(void); void register_node_type_geo_legacy_curve_reverse(void); +void register_node_type_geo_legacy_select_by_handle_type(void); void register_node_type_geo_legacy_curve_subdivide(void); void register_node_type_geo_align_rotation_to_vector(void); @@ -64,6 +65,7 @@ void register_node_type_geo_convex_hull(void); void register_node_type_geo_curve_endpoints(void); void register_node_type_geo_curve_fill(void); void register_node_type_geo_curve_fillet(void); +void register_node_type_geo_curve_handle_type_selection(void); void register_node_type_geo_curve_length(void); void register_node_type_geo_curve_parameter(void); void register_node_type_geo_curve_primitive_bezier_segment(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 88ccf28d6d3..2a163c90ab5 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -326,6 +326,7 @@ DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLEC DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Curve Fill", "") DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Curve Fillet", "") +DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc index dfcae2e65b0..0d3de7ac5f5 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc @@ -123,7 +123,7 @@ static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_select_by_handle_type() +void register_node_type_geo_legacy_select_by_handle_type() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc new file mode 100644 index 00000000000..dc2070f20e6 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -0,0 +1,180 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_handle_type_selection_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Selection").field_source(); +} + +static void geo_node_curve_handle_type_selection_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); +} + +static void geo_node_curve_handle_type_selection_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSelectHandles *data = (NodeGeometryCurveSelectHandles *)MEM_callocN( + sizeof(NodeGeometryCurveSelectHandles), __func__); + + data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; + data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; + node->storage = data; +} + +static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +{ + switch (type) { + case GEO_NODE_CURVE_HANDLE_AUTO: + return BezierSpline::HandleType::Auto; + case GEO_NODE_CURVE_HANDLE_ALIGN: + return BezierSpline::HandleType::Align; + case GEO_NODE_CURVE_HANDLE_FREE: + return BezierSpline::HandleType::Free; + case GEO_NODE_CURVE_HANDLE_VECTOR: + return BezierSpline::HandleType::Vector; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +static void select_by_handle_type(const CurveEval &curve, + const BezierSpline::HandleType type, + const GeometryNodeCurveHandleMode mode, + const IndexMask mask, + const MutableSpan r_selection) +{ + int offset = 0; + for (const SplinePtr &spline : curve.splines()) { + if (spline->type() != Spline::Type::Bezier) { + r_selection.slice(offset, spline->size()).fill(false); + offset += spline->size(); + } + else { + BezierSpline *b = static_cast(spline.get()); + for (int i : IndexRange(b->size())) { + if (mode & GeometryNodeCurveHandleMode::GEO_NODE_CURVE_HANDLE_LEFT) { + r_selection[offset++] = b->handle_types_left()[i] == type; + } + else if (mode & GeometryNodeCurveHandleMode::GEO_NODE_CURVE_HANDLE_RIGHT) { + r_selection[offset++] = b->handle_types_right()[i] == type; + } + } + } + } +} + +class HandleTypeFieldInput final : public fn::FieldInput { + BezierSpline::HandleType type_; + GeometryNodeCurveHandleMode mode_; + + public: + HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode) + : FieldInput(CPPType::get(), "Selection"), type_(type), mode_(mode) + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + if (component.type() != GEO_COMPONENT_TYPE_CURVE) { + return nullptr; + } + + const CurveComponent &curve_component = static_cast(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve == nullptr) { + return nullptr; + } + + if (domain == ATTR_DOMAIN_POINT) { + Array selection(mask.min_array_size()); + select_by_handle_type(*curve, type_, mode_, mask, selection); + return &scope.construct>>(std::move(selection)); + } + } + return nullptr; + }; + + uint64_t hash() const override + { + return get_default_hash_2((int)mode_, (int)type_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast(&other) != nullptr; + if (const HandleTypeFieldInput *other_handle_selection = + dynamic_cast(&other)) { + return mode_ == other_handle_selection->mode_ && type_ == other_handle_selection->type_; + } + return false; + } +}; + +static void geo_node_curve_handle_type_selection_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSelectHandles *storage = + (const NodeGeometryCurveSelectHandles *)params.node().storage; + const BezierSpline::HandleType handle_type = handle_type_from_input_type( + (GeometryNodeCurveHandleType)storage->handle_type); + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode; + + Field selection_field{std::make_shared(handle_type, mode)}; + params.set_output("Selection", std::move(selection_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_handle_type_selection() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, + GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, + "Handle Type Selection", + NODE_CLASS_GEOMETRY, + 0); + ntype.declare = blender::nodes::geo_node_curve_handle_type_selection_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_handle_type_selection_exec; + node_type_init(&ntype, blender::nodes::geo_node_curve_handle_type_selection_init); + node_type_storage(&ntype, + "NodeGeometryCurveSelectHandles", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_curve_handle_type_selection_layout; + + nodeRegisterType(&ntype); +} From 3b1a2430391a6c1e0ea2744cfce2cf0749c72c85 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sun, 3 Oct 2021 23:18:04 +0200 Subject: [PATCH 0466/1500] Cleanup: Rename file-list function to better match what it's doing --- source/blender/editors/space_file/filelist.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index bdf61e792d3..0cb587c3b19 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -490,7 +490,7 @@ static void filelist_readjob_main_assets(struct FileListReadJob *job_params, static int groupname_to_code(const char *group); static uint64_t groupname_to_filter_id(const char *group); -static void filelist_filter_clear(FileList *filelist); +static void filelist_tag_needs_filtering(FileList *filelist); static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size); /* ********** Sort helpers ********** */ @@ -720,7 +720,7 @@ void filelist_sort(struct FileList *filelist) sort_cb, &(struct FileSortData){.inverted = (filelist->flags & FL_SORT_INVERT) != 0}); - filelist_filter_clear(filelist); + filelist_tag_needs_filtering(filelist); filelist->flags &= ~FL_NEED_SORTING; } } @@ -970,7 +970,7 @@ static bool is_filtered_main_assets(FileListInternEntry *file, is_filtered_asset(file, filter); } -static void filelist_filter_clear(FileList *filelist) +static void filelist_tag_needs_filtering(FileList *filelist) { filelist->flags |= FL_NEED_FILTERING; } @@ -1078,7 +1078,7 @@ void filelist_setfilter_options(FileList *filelist, if (update) { /* And now, free filtered data so that we know we have to filter again. */ - filelist_filter_clear(filelist); + filelist_tag_needs_filtering(filelist); } } @@ -1105,7 +1105,7 @@ void filelist_set_asset_catalog_filter_options( } if (update) { - filelist_filter_clear(filelist); + filelist_tag_needs_filtering(filelist); } } @@ -1831,7 +1831,7 @@ void filelist_clear_ex(struct FileList *filelist, return; } - filelist_filter_clear(filelist); + filelist_tag_needs_filtering(filelist); if (do_cache) { filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size); From c4dca6522812670ab9782e4f90553c5054109f3d Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sun, 3 Oct 2021 23:58:20 +0200 Subject: [PATCH 0467/1500] Asset Browser: Support dragging assets into catalogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this it is possible to select any number of assets in the Asset Browser and drag them into catalogs. The assets will be moved to that catalog then. However, this will only work in the "Current File" asset library, since that is the only library that allows changing assets, which is what's done here. While dragging assets over the tree row, a tooltip is shown explaining what's going to happen. In preparation to this, the new UI tree-view API was already extended with custom drop support, see 4ee2d9df428d. ---- Changes here to the `wmDrag` code were needed to support dragging multiple assets. Some of it is considered temporary because a) a proper #AssetHandle design should replace some ugly parts of this patch and b) the multi-item support in `wmDrag` isn't that great yet. The entire API will have to be written anyway (see D4071). Maniphest Tasks: T91573 Differential Revision: https://developer.blender.org/D12713 Reviewed by: Sybren Stüvel --- doc/python_api/sphinx_doc_gen.py | 1 + .../blender/blenkernel/BKE_asset_catalog.hh | 6 +- .../blenkernel/intern/asset_catalog.cc | 16 +++- source/blender/editors/interface/interface.c | 7 +- .../editors/interface/interface_handlers.c | 6 ++ .../space_file/asset_catalog_tree_view.cc | 83 +++++++++++++++- .../blender/editors/space_file/file_intern.h | 1 + .../blender/editors/space_file/file_panels.c | 2 +- source/blender/editors/space_file/filelist.c | 3 +- source/blender/editors/space_file/filelist.h | 1 + .../blender/editors/space_file/space_file.c | 19 ++++ source/blender/windowmanager/WM_api.h | 11 +++ source/blender/windowmanager/WM_types.h | 37 ++++++-- .../windowmanager/intern/wm_dragdrop.c | 94 ++++++++++++++++++- 14 files changed, 260 insertions(+), 27 deletions(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index aa0f79646e6..ec636036f95 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1101,6 +1101,7 @@ context_type_map = { "scene": ("Scene", False), "sculpt_object": ("Object", False), "selectable_objects": ("Object", True), + "selected_asset_files": ("FileSelectEntry", True), "selected_bones": ("EditBone", True), "selected_editable_bones": ("EditBone", True), "selected_editable_fcurves": ("FCurve", True), diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 8afc4fe2ad2..b6b90cff5fd 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -181,10 +181,12 @@ class AssetCatalogTreeItem { AssetCatalogTreeItem(StringRef name, CatalogID catalog_id, + StringRef simple_name, const AssetCatalogTreeItem *parent = nullptr); CatalogID get_catalog_id() const; - StringRef get_name() const; + StringRefNull get_simple_name() const; + StringRefNull get_name() const; /** Return the full catalog path, defined as the name of this catalog prefixed by the full * catalog path of its parent and a separator. */ AssetCatalogPath catalog_path() const; @@ -201,6 +203,8 @@ class AssetCatalogTreeItem { /** The user visible name of this component. */ CatalogPathComponent name_; CatalogID catalog_id_; + /** Copy of #AssetCatalog::simple_name. */ + std::string simple_name_; /** Pointer back to the parent item. Used to reconstruct the hierarchy from an item (e.g. to * build a path). */ diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 2c7cf28d60d..577d916288a 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -426,8 +426,9 @@ void AssetCatalogService::create_missing_catalogs() AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name, CatalogID catalog_id, + StringRef simple_name, const AssetCatalogTreeItem *parent) - : name_(name), catalog_id_(catalog_id), parent_(parent) + : name_(name), catalog_id_(catalog_id), simple_name_(simple_name), parent_(parent) { } @@ -436,11 +437,16 @@ CatalogID AssetCatalogTreeItem::get_catalog_id() const return catalog_id_; } -StringRef AssetCatalogTreeItem::get_name() const +StringRefNull AssetCatalogTreeItem::get_name() const { return name_; } +StringRefNull AssetCatalogTreeItem::get_simple_name() const +{ + return simple_name_; +} + AssetCatalogPath AssetCatalogTreeItem::catalog_path() const { AssetCatalogPath current_path = name_; @@ -482,8 +488,10 @@ void AssetCatalogTree::insert_item(const AssetCatalog &catalog) /* Insert new tree element - if no matching one is there yet! */ auto [key_and_item, was_inserted] = current_item_children->emplace( component_name, - AssetCatalogTreeItem( - component_name, is_last_component ? catalog.catalog_id : nil_id, parent)); + AssetCatalogTreeItem(component_name, + is_last_component ? catalog.catalog_id : nil_id, + is_last_component ? catalog.simple_name : "", + parent)); AssetCatalogTreeItem &item = key_and_item->second; /* If full path of this catalog already exists as parent path of a previously read catalog, diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index c53bffca778..39ad88c3368 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -6226,12 +6226,7 @@ void UI_but_drag_set_asset(uiBut *but, struct ImBuf *imb, float scale) { - wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset"); - - BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name)); - asset_drag->path = path; - asset_drag->id_type = ED_asset_handle_get_id_type(asset); - asset_drag->import_type = import_type; + wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, path, import_type); /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the * #wmDropBox. diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index aee66ec3a93..f0e3464a955 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2166,6 +2166,12 @@ static bool ui_but_drag_init(bContext *C, BLI_rctf_size_x(&but->rect), BLI_rctf_size_y(&but->rect)); } + + /* Special feature for assets: We add another drag item that supports multiple assets. It + * gets the assets from context. */ + if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { + WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, NULL, 0, WM_DRAG_NOP); + } } return true; } diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 7eea9af925b..92e4e668885 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -25,6 +25,7 @@ #include "DNA_space_types.h" +#include "BKE_asset.h" #include "BKE_asset_catalog.hh" #include "BKE_asset_library.hh" @@ -43,6 +44,7 @@ #include "WM_types.h" #include "file_intern.h" +#include "filelist.h" using namespace blender; using namespace blender::bke; @@ -53,11 +55,14 @@ class AssetCatalogTreeView : public ui::AbstractTreeView { /** The asset catalog tree this tree-view represents. */ bke::AssetCatalogTree *catalog_tree_; FileAssetSelectParams *params_; + SpaceFile &space_file_; friend class AssetCatalogTreeViewItem; public: - AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params); + AssetCatalogTreeView(::AssetLibrary *library, + FileAssetSelectParams *params, + SpaceFile &space_file); void build_tree() override; @@ -117,6 +122,70 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { RNA_string_set(props, "catalog_id", catalog_id_str_buffer); } } + + bool has_droppable_item(const wmDrag &drag) const + { + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + + /* There needs to be at least one asset from the current file. */ + LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) { + if (!asset_item->is_external) { + return true; + } + } + return false; + } + + bool can_drop(const wmDrag &drag) const override + { + if (drag.type != WM_DRAG_ASSET_LIST) { + return false; + } + return has_droppable_item(drag); + } + + std::string drop_tooltip(const bContext & /*C*/, + const wmDrag &drag, + const wmEvent & /*event*/) const override + { + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); + + /* Don't try to be smart by dynamically adding the 's' for the plural. Just makes translation + * harder, so use full literals. */ + std::string basic_tip = is_multiple_assets ? TIP_("Move assets to catalog") : + TIP_("Move asset to catalog"); + + return basic_tip + ": " + catalog_item_.get_name() + " (" + + catalog_item_.catalog_path().str() + ")"; + } + + bool on_drop(const wmDrag &drag) override + { + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + if (!asset_drags) { + return false; + } + + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + + LISTBASE_FOREACH (wmDragAssetListItem *, asset_item, asset_drags) { + if (asset_item->is_external) { + /* Only internal assets can be modified! */ + continue; + } + BKE_asset_metadata_catalog_id_set(asset_item->asset_data.local_id->asset_data, + catalog_item_.get_catalog_id(), + catalog_item_.get_simple_name().c_str()); + + /* Trigger re-run of filtering to update visible assets. */ + filelist_tag_needs_filtering(tree_view.space_file_.files); + file_select_deselect_all(&tree_view.space_file_, FILE_SEL_SELECTED | FILE_SEL_HIGHLIGHTED); + } + + return true; + } }; /** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level @@ -140,8 +209,12 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { } }; -AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params) - : catalog_tree_(BKE_asset_library_get_catalog_tree(library)), params_(params) +AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library, + FileAssetSelectParams *params, + SpaceFile &space_file) + : catalog_tree_(BKE_asset_library_get_catalog_tree(library)), + params_(params), + space_file_(space_file) { } @@ -216,6 +289,7 @@ bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library, uiLayout *layout, + SpaceFile *space_file, FileAssetSelectParams *params) { uiBlock *block = uiLayoutGetBlock(layout); @@ -223,7 +297,8 @@ void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library ui::AbstractTreeView *tree_view = UI_block_add_view( *block, "asset catalog tree view", - std::make_unique(asset_library, params)); + std::make_unique( + asset_library, params, *space_file)); ui::TreeViewBuilder builder(*block); builder.build_tree_view(*tree_view); diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index d39aefff691..c8609f48adb 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -165,6 +165,7 @@ void file_path_to_ui_path(const char *path, char *r_pathi, int max_size); void file_create_asset_catalog_tree_view_in_layout(struct AssetLibrary *asset_library, struct uiLayout *layout, + struct SpaceFile *space_file, struct FileAssetSelectParams *params); #ifdef __cplusplus diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index 95aad202f1a..b530f1d0aa7 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -238,7 +238,7 @@ static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *pane FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile); BLI_assert(params != NULL); - file_create_asset_catalog_tree_view_in_layout(asset_library, panel->layout, params); + file_create_asset_catalog_tree_view_in_layout(asset_library, panel->layout, sfile, params); } void file_tools_region_panels_register(ARegionType *art) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 0cb587c3b19..b58a04d6d4f 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -490,7 +490,6 @@ static void filelist_readjob_main_assets(struct FileListReadJob *job_params, static int groupname_to_code(const char *group); static uint64_t groupname_to_filter_id(const char *group); -static void filelist_tag_needs_filtering(FileList *filelist); static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size); /* ********** Sort helpers ********** */ @@ -970,7 +969,7 @@ static bool is_filtered_main_assets(FileListInternEntry *file, is_filtered_asset(file, filter); } -static void filelist_tag_needs_filtering(FileList *filelist) +void filelist_tag_needs_filtering(FileList *filelist) { filelist->flags |= FL_NEED_FILTERING; } diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index d1f37b5b365..c2c1211b81c 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -76,6 +76,7 @@ void filelist_set_asset_catalog_filter_options( struct FileList *filelist, eFileSel_Params_AssetCatalogVisibility catalog_visibility, const struct bUUID *catalog_id); +void filelist_tag_needs_filtering(struct FileList *filelist); void filelist_filter(struct FileList *filelist); void filelist_setlibrary(struct FileList *filelist, const struct AssetLibraryReference *asset_library_ref); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index a563f24e24e..c8bca22c166 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -894,6 +894,7 @@ const char *file_context_dir[] = { "active_file", "selected_files", "asset_library_ref", + "selected_asset_files", "id", NULL, }; @@ -951,6 +952,24 @@ static int /*eContextResult*/ file_context(const bContext *C, result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library_ref); return CTX_RESULT_OK; } + /** TODO temporary AssetHandle design: For now this returns the file entry. Would be better if it + * was `"selected_assets"` and returned the assets (e.g. as `AssetHandle`) directly. See comment + * for #AssetHandle for more info. */ + if (CTX_data_equals(member, "selected_asset_files")) { + const int num_files_filtered = filelist_files_ensure(sfile->files); + + for (int file_index = 0; file_index < num_files_filtered; file_index++) { + if (filelist_entry_is_selected(sfile->files, file_index)) { + FileDirEntry *entry = filelist_file(sfile->files, file_index); + if (entry->asset_data) { + CTX_data_list_add(result, &screen->id, &RNA_FileSelectEntry, entry); + } + } + } + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return CTX_RESULT_OK; + } if (CTX_data_equals(member, "id")) { const FileDirEntry *file = filelist_file(sfile->files, params->active_file); if (file == NULL) { diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 577561017c4..ebb0f803acf 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -41,6 +41,8 @@ extern "C" { #endif struct ARegion; +struct AssetHandle; +struct AssetLibraryReference; struct GHashIterator; struct GPUViewport; struct ID; @@ -741,6 +743,9 @@ struct ID *WM_drag_get_local_ID(const struct wmDrag *drag, short idcode); struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short idcode); bool WM_drag_is_ID_type(const struct wmDrag *drag, int idcode); +wmDragAsset *WM_drag_create_asset_data(const struct AssetHandle *asset, + const char *path, + int import_type); struct wmDragAsset *WM_drag_get_asset_data(const struct wmDrag *drag, int idcode); struct ID *WM_drag_get_local_ID_or_import_from_asset(const struct wmDrag *drag, int idcode); @@ -748,6 +753,12 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, struct wmDrag *drag, struct wmDropBox *drop); +void WM_drag_add_asset_list_item(wmDrag *drag, + const struct bContext *C, + const struct AssetLibraryReference *asset_library_ref, + const struct AssetHandle *asset); +const ListBase *WM_drag_asset_list_get(const wmDrag *drag); + const char *WM_drag_get_item_name(struct wmDrag *drag); /* Set OpenGL viewport and scissor */ diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 588c895c742..7f52bef3203 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -915,12 +915,16 @@ typedef void (*wmPaintCursorDraw)(struct bContext *C, int, int, void *customdata #define WM_DRAG_ID 0 #define WM_DRAG_ASSET 1 -#define WM_DRAG_RNA 2 -#define WM_DRAG_PATH 3 -#define WM_DRAG_NAME 4 -#define WM_DRAG_VALUE 5 -#define WM_DRAG_COLOR 6 -#define WM_DRAG_DATASTACK 7 +/** The user is dragging multiple assets. This is only supported in few specific cases, proper + * multi-item support for dragging isn't supported well yet. Therefore this is kept separate from + * #WM_DRAG_ASSET. */ +#define WM_DRAG_ASSET_LIST 2 +#define WM_DRAG_RNA 3 +#define WM_DRAG_PATH 4 +#define WM_DRAG_NAME 5 +#define WM_DRAG_VALUE 6 +#define WM_DRAG_COLOR 7 +#define WM_DRAG_DATASTACK 8 typedef enum wmDragFlags { WM_DRAG_NOP = 0, @@ -953,6 +957,25 @@ typedef struct wmDragAsset { struct bContext *evil_C; } wmDragAsset; +/** + * For some specific cases we support dragging multiple assets (#WM_DRAG_ASSET_LIST). There is no + * proper support for dragging multiple items in the `wmDrag`/`wmDrop` API yet, so this is really + * just to enable specific features for assets. + * + * This struct basically contains a tagged union to either store a local ID pointer, or information + * about an externally stored asset. + */ +typedef struct wmDragAssetListItem { + struct wmDragAssetListItem *next, *prev; + + union { + struct ID *local_id; + wmDragAsset *external_info; + } asset_data; + + bool is_external; +} wmDragAssetListItem; + typedef char *(*WMDropboxTooltipFunc)(struct bContext *, struct wmDrag *, const struct wmEvent *event, @@ -979,6 +1002,8 @@ typedef struct wmDrag { /** List of wmDragIDs, all are guaranteed to have the same ID type. */ ListBase ids; + /** List of `wmDragAssetListItem`s. */ + ListBase asset_items; } wmDrag; /** diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index c5a89e3ad9f..b76b1672543 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -46,6 +46,8 @@ #include "BLO_readfile.h" +#include "ED_asset.h" + #include "GPU_shader.h" #include "GPU_state.h" #include "GPU_viewport.h" @@ -66,6 +68,8 @@ static ListBase dropboxes = {NULL, NULL}; +static void wm_drag_free_asset_data(wmDragAsset **asset_data); + /* drop box maps are stored global for now */ /* these are part of blender's UI/space specs, and not like keymaps */ /* when editors become configurable, they can add own dropbox definitions */ @@ -176,6 +180,19 @@ wmDrag *WM_event_start_drag( drag->poin = poin; drag->flags |= WM_DRAG_FREE_DATA; break; + /* The asset-list case is special: We get multiple assets from context and attach them to the + * drag item. */ + case WM_DRAG_ASSET_LIST: { + const AssetLibraryReference *asset_library = CTX_wm_asset_library_ref(C); + ListBase asset_file_links = CTX_data_collection_get(C, "selected_asset_files"); + LISTBASE_FOREACH (const CollectionPointerLink *, link, &asset_file_links) { + const FileDirEntry *asset_file = link->ptr.data; + const AssetHandle asset_handle = {asset_file}; + WM_drag_add_asset_list_item(drag, C, asset_library, &asset_handle); + } + BLI_freelistN(&asset_file_links); + break; + } default: drag->poin = poin; break; @@ -202,10 +219,12 @@ void WM_drag_data_free(int dragtype, void *poin) /* Not too nice, could become a callback. */ if (dragtype == WM_DRAG_ASSET) { - wmDragAsset *asset_drag = poin; - MEM_freeN((void *)asset_drag->path); + wmDragAsset *asset_data = poin; + wm_drag_free_asset_data(&asset_data); + } + else { + MEM_freeN(poin); } - MEM_freeN(poin); } void WM_drag_free(wmDrag *drag) @@ -214,6 +233,12 @@ void WM_drag_free(wmDrag *drag) WM_drag_data_free(drag->type, drag->poin); } BLI_freelistN(&drag->ids); + LISTBASE_FOREACH_MUTABLE (wmDragAssetListItem *, asset_item, &drag->asset_items) { + if (asset_item->is_external) { + wm_drag_free_asset_data(&asset_item->asset_data.external_info); + } + BLI_freelinkN(&drag->asset_items, asset_item); + } MEM_freeN(drag); } @@ -378,6 +403,27 @@ bool WM_drag_is_ID_type(const wmDrag *drag, int idcode) return WM_drag_get_local_ID(drag, idcode) || WM_drag_get_asset_data(drag, idcode); } +/** + * \note: Does not store \a asset in any way, so it's fine to pass a temporary. + */ +wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset, const char *path, int import_type) +{ + wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset"); + + BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name)); + asset_drag->path = path; + asset_drag->id_type = ED_asset_handle_get_id_type(asset); + asset_drag->import_type = import_type; + + return asset_drag; +} + +static void wm_drag_free_asset_data(wmDragAsset **asset_data) +{ + MEM_freeN((char *)(*asset_data)->path); + MEM_SAFE_FREE(*asset_data); +} + wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) { if (drag->type != WM_DRAG_ASSET) { @@ -495,6 +541,48 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * } } +/** + * \note: Does not store \a asset in any way, so it's fine to pass a temporary. + */ +void WM_drag_add_asset_list_item( + wmDrag *drag, + /* Context only needed for the hack in #ED_asset_handle_get_full_library_path(). */ + const bContext *C, + const AssetLibraryReference *asset_library_ref, + const AssetHandle *asset) +{ + if (drag->type != WM_DRAG_ASSET_LIST) { + return; + } + + /* No guarantee that the same asset isn't added twice. */ + + /* Add to list. */ + wmDragAssetListItem *drag_asset = MEM_callocN(sizeof(*drag_asset), __func__); + ID *local_id = ED_asset_handle_get_local_id(asset); + if (local_id) { + drag_asset->is_external = false; + drag_asset->asset_data.local_id = local_id; + } + else { + char asset_blend_path[FILE_MAX_LIBEXTRA]; + ED_asset_handle_get_full_library_path(C, asset_library_ref, asset, asset_blend_path); + drag_asset->is_external = true; + drag_asset->asset_data.external_info = WM_drag_create_asset_data( + asset, BLI_strdup(asset_blend_path), FILE_ASSET_IMPORT_APPEND); + } + BLI_addtail(&drag->asset_items, drag_asset); +} + +const ListBase *WM_drag_asset_list_get(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_ASSET_LIST) { + return NULL; + } + + return &drag->asset_items; +} + /* ************** draw ***************** */ static void wm_drop_operator_draw(const char *name, int x, int y) From ee79bde54dfbaf9914224a32dae090c326b5998b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 3 Oct 2021 13:02:22 +1100 Subject: [PATCH 0468/1500] Keymap: print more verbose output for --debug-handlers Include the short-cut text and the operator properties to make it easier to track down the key-map item source that matched the event. --- .../windowmanager/intern/wm_event_system.c | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 537d5264ba9..cc0a13e96af 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -2665,7 +2665,34 @@ static int wm_handlers_do_keymap_with_keymap_handler( if (wm_eventmatch(event, kmi)) { struct wmEventHandler_KeymapPost keymap_post = handler->post; - PRINT("%s: item matched '%s'\n", __func__, kmi->idname); + if (do_debug_handler) { + /* Short representation of the key that was pressed, + * include this since it may differ from the event in minor details + * which can help looking up the key-map definition. */ + char kmi_buf[256]; + WM_keymap_item_to_string(kmi, false, kmi_buf, sizeof(kmi_buf)); + + /* The key-map item properties can further help distinguish this item from others. */ + char *kmi_props = NULL; + if (kmi->properties != NULL) { + wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); + if (ot) { + kmi_props = RNA_pointer_as_string_keywords(C, kmi->ptr, false, false, true, 512); + } + else { /* Fallback. */ + kmi_props = IDP_reprN(kmi->properties, NULL); + } + } + + printf("%s: item matched: \"%s\", %s(%s)\n", + __func__, + kmi_buf, + kmi->idname, + kmi_props ? kmi_props : ""); + if (kmi_props != NULL) { + MEM_freeN(kmi_props); + } + } action |= wm_handler_operator_call( C, handlers, &handler->head, event, kmi->ptr, kmi->idname); From 57272d598d2c64932b3ae26991063fce9fe52a90 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 3 Oct 2021 13:40:19 +1100 Subject: [PATCH 0469/1500] Cleanup: rename eRegionType -> eRegion_Type Match eSpace_Type. --- source/blender/editors/screen/screen_ops.c | 4 ++-- source/blender/makesdna/DNA_screen_types.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 674a2deb929..3a0192aef54 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4410,7 +4410,7 @@ static void SCREEN_OT_region_context_menu(wmOperatorType *ot) * Animation Step. * \{ */ static bool screen_animation_region_supports_time_follow(eSpace_Type spacetype, - eRegionType regiontype) + eRegion_Type regiontype) { return (regiontype == RGN_TYPE_WINDOW && ELEM(spacetype, SPACE_SEQ, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) || @@ -4418,7 +4418,7 @@ static bool screen_animation_region_supports_time_follow(eSpace_Type spacetype, } static bool match_region_with_redraws(const ScrArea *area, - eRegionType regiontype, + eRegion_Type regiontype, eScreen_Redraws_Flag redraws, bool from_anim_edit) { diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index d5b7458ae7b..f557890e3e8 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -650,7 +650,7 @@ enum { /* regiontype, first two are the default set */ /* Do NOT change order, append on end. Types are hardcoded needed */ -typedef enum eRegionType { +typedef enum eRegion_Type { RGN_TYPE_WINDOW = 0, RGN_TYPE_HEADER = 1, RGN_TYPE_CHANNELS = 2, @@ -668,7 +668,7 @@ typedef enum eRegionType { RGN_TYPE_TOOL_HEADER = 12, #define RGN_TYPE_LEN (RGN_TYPE_TOOL_HEADER + 1) -} eRegionType; +} eRegion_Type; /* use for function args */ #define RGN_TYPE_ANY -1 From 1e5cfebf660cdae0408bf36a895ef62499e4b13e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 3 Oct 2021 13:43:30 +1100 Subject: [PATCH 0470/1500] Fix key-map with fall-back tool on RMB select Regression in bffda4185dc7eee88e49818b72fa8c34dc2778e6. --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 35eb6490265..8bf5ac500b0 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -71,7 +71,7 @@ class Params: # (derived from other settings). # # This case needs to be checked often, - # Shorthand for: `(params.use_fallback_tool if params.select_mouse == 'RIGHT' else False)`. + # Shorthand for: `(params.use_fallback_tool if params.select_mouse == 'RIGHTMOUSE' else False)`. "use_fallback_tool_rmb", # Shorthand for: `('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value)`. "select_mouse_value_fallback", @@ -195,7 +195,7 @@ class Params: self.use_file_single_click = use_file_single_click # Convenience variables: - self.use_fallback_tool_rmb = self.use_fallback_tool if self.select_mouse == 'RIGHT' else False + self.use_fallback_tool_rmb = self.use_fallback_tool if select_mouse == 'RIGHT' else False self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS' self.tool_tweak_event = {"type": self.tool_tweak, "value": 'ANY'} From c9af025936e8c660d5dbad86a6554a13e72d0457 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 3 Oct 2021 18:23:58 -0500 Subject: [PATCH 0471/1500] Cleanup: Add doxygen sections, rearrange functions --- .../blender/blenkernel/intern/curveprofile.cc | 58 ++++++++++++------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/source/blender/blenkernel/intern/curveprofile.cc b/source/blender/blenkernel/intern/curveprofile.cc index 0adce991d0f..c16459afbb3 100644 --- a/source/blender/blenkernel/intern/curveprofile.cc +++ b/source/blender/blenkernel/intern/curveprofile.cc @@ -35,6 +35,10 @@ #include "BLO_read_write.h" +/* -------------------------------------------------------------------- */ +/** \name Data Handling + * \{ */ + void BKE_curveprofile_free_data(CurveProfile *profile) { MEM_SAFE_FREE(profile->path); @@ -74,6 +78,33 @@ CurveProfile *BKE_curveprofile_copy(const CurveProfile *profile) return nullptr; } +void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile) +{ + BLO_write_struct(writer, CurveProfile, profile); + BLO_write_struct_array(writer, CurveProfilePoint, profile->path_len, profile->path); +} + +/* Expects that the curve profile itself has been read already. */ +void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile) +{ + BLO_read_data_address(reader, &profile->path); + profile->table = nullptr; + profile->segments = nullptr; + + /* Reset the points' pointers to the profile. */ + for (int i = 0; i < profile->path_len; i++) { + profile->path[i].profile = profile; + } + + BKE_curveprofile_init(profile, profile->segments_len); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Editing + * \{ */ + /** * Move a point's handle, accounting for the alignment of handles with the #HD_ALIGN type. * @@ -532,6 +563,12 @@ void BKE_curveprofile_reset(CurveProfile *profile) profile->table = nullptr; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sampling and Evaluation + * \{ */ + /** * Helper for 'curve_profile_create' samples. * Returns whether both handles that make up the edge are vector handles. @@ -1103,23 +1140,4 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, *y_out = interpf(profile->table[i].y, profile->table[i + 1].y, lerp_factor); } -void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile) -{ - BLO_write_struct(writer, CurveProfile, profile); - BLO_write_struct_array(writer, CurveProfilePoint, profile->path_len, profile->path); -} - -/* Expects that the curve profile itself has been read already. */ -void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile) -{ - BLO_read_data_address(reader, &profile->path); - profile->table = nullptr; - profile->segments = nullptr; - - /* Reset the points' pointers to the profile. */ - for (int i = 0; i < profile->path_len; i++) { - profile->path[i].profile = profile; - } - - BKE_curveprofile_init(profile, profile->segments_len); -} +/** \} */ From 272a38e0c264911d6ebafcb440432d20c1495604 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 3 Oct 2021 18:31:56 -0500 Subject: [PATCH 0472/1500] Cleanup: Make function static --- source/blender/blenkernel/BKE_curveprofile.h | 3 --- source/blender/blenkernel/intern/curveprofile.cc | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/BKE_curveprofile.h b/source/blender/blenkernel/BKE_curveprofile.h index 501ae70ecdb..88bac220eb3 100644 --- a/source/blender/blenkernel/BKE_curveprofile.h +++ b/source/blender/blenkernel/BKE_curveprofile.h @@ -85,9 +85,6 @@ enum { }; void BKE_curveprofile_update(struct CurveProfile *profile, const int update_flags); -/* Need to find the total length of the curve to sample a portion of it */ -float BKE_curveprofile_total_length(const struct CurveProfile *profile); - void BKE_curveprofile_create_samples_even_spacing(struct CurveProfile *profile, int n_segments, struct CurveProfilePoint *r_samples); diff --git a/source/blender/blenkernel/intern/curveprofile.cc b/source/blender/blenkernel/intern/curveprofile.cc index c16459afbb3..282aa04e0fa 100644 --- a/source/blender/blenkernel/intern/curveprofile.cc +++ b/source/blender/blenkernel/intern/curveprofile.cc @@ -1022,7 +1022,7 @@ static float curveprofile_distance_to_next_table_point(const CurveProfile *profi * * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. */ -float BKE_curveprofile_total_length(const CurveProfile *profile) +static float curveprofile_total_length(const CurveProfile *profile) { float total_length = 0; for (int i = 0; i < PROF_TABLE_LEN(profile->path_len) - 1; i++) { @@ -1043,7 +1043,7 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, int n_segments, CurveProfilePoint *r_samples) { - const float total_length = BKE_curveprofile_total_length(profile); + const float total_length = curveprofile_total_length(profile); const float segment_length = total_length / n_segments; float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0); float distance_to_previous_table_point = 0.0f; @@ -1101,7 +1101,7 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, float *x_out, float *y_out) { - const float total_length = BKE_curveprofile_total_length(profile); + const float total_length = curveprofile_total_length(profile); const float requested_length = length_portion * total_length; /* Find the last point along the path with a lower length portion than the input. */ From b6195f66643be6604a4dc1072ead53d1567f6795 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 3 Oct 2021 18:54:52 -0500 Subject: [PATCH 0473/1500] Cleanup: Replace macro with function --- source/blender/blenkernel/BKE_curveprofile.h | 2 ++ .../blender/blenkernel/intern/curveprofile.cc | 21 +++++++++++++++---- .../editors/interface/interface_draw.c | 6 +++--- .../editors/interface/interface_handlers.c | 2 +- .../blender/makesdna/DNA_curveprofile_types.h | 7 ------- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/source/blender/blenkernel/BKE_curveprofile.h b/source/blender/blenkernel/BKE_curveprofile.h index 88bac220eb3..62074a30612 100644 --- a/source/blender/blenkernel/BKE_curveprofile.h +++ b/source/blender/blenkernel/BKE_curveprofile.h @@ -75,6 +75,8 @@ void BKE_curveprofile_create_samples(struct CurveProfile *profile, bool sample_straight_edges, struct CurveProfilePoint *r_samples); +int BKE_curveprofile_table_size(const struct CurveProfile *profile); + void BKE_curveprofile_init(struct CurveProfile *profile, short segments_len); /* Called for a complete update of the widget after modifications */ diff --git a/source/blender/blenkernel/intern/curveprofile.cc b/source/blender/blenkernel/intern/curveprofile.cc index 282aa04e0fa..9a7a8f7329f 100644 --- a/source/blender/blenkernel/intern/curveprofile.cc +++ b/source/blender/blenkernel/intern/curveprofile.cc @@ -21,6 +21,8 @@ * \ingroup bke */ +#include + #include "MEM_guardedalloc.h" #include "DNA_curve_types.h" @@ -35,6 +37,9 @@ #include "BLO_read_write.h" +/** Number of points in high resolution table is dynamic up to a maximum. */ +#define PROF_TABLE_MAX 512 + /* -------------------------------------------------------------------- */ /** \name Data Handling * \{ */ @@ -569,6 +574,14 @@ void BKE_curveprofile_reset(CurveProfile *profile) /** \name Sampling and Evaluation * \{ */ +int BKE_curveprofile_table_size(const CurveProfile *profile) +{ + /** Number of table points per control point. */ + const int resolution = 16; + + return std::clamp((profile->path_len - 1) * resolution + 1, 0, PROF_TABLE_MAX); +} + /** * Helper for 'curve_profile_create' samples. * Returns whether both handles that make up the edge are vector handles. @@ -848,7 +861,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, */ static void curveprofile_make_table(CurveProfile *profile) { - int n_samples = PROF_TABLE_LEN(profile->path_len); + int n_samples = BKE_curveprofile_table_size(profile); CurveProfilePoint *new_table = (CurveProfilePoint *)MEM_callocN( sizeof(CurveProfilePoint) * (n_samples + 1), __func__); @@ -1012,7 +1025,7 @@ void BKE_curveprofile_init(CurveProfile *profile, short segments_len) */ static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i) { - BLI_assert(i < PROF_TABLE_LEN(profile->path_len)); + BLI_assert(i < BKE_curveprofile_table_size(profile)); return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); } @@ -1025,7 +1038,7 @@ static float curveprofile_distance_to_next_table_point(const CurveProfile *profi static float curveprofile_total_length(const CurveProfile *profile) { float total_length = 0; - for (int i = 0; i < PROF_TABLE_LEN(profile->path_len) - 1; i++) { + for (int i = 0; i < BKE_curveprofile_table_size(profile) - 1; i++) { total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); } return total_length; @@ -1109,7 +1122,7 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, float length_travelled = 0.0f; while (length_travelled < requested_length) { /* Check if we reached the last point before the final one. */ - if (i == PROF_TABLE_LEN(profile->path_len) - 2) { + if (i == BKE_curveprofile_table_size(profile) - 2) { break; } float new_length = curveprofile_distance_to_next_table_point(profile, i); diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index ebebf69bc11..e9404b0273d 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -1860,13 +1860,13 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Also add the last points on the right and bottom edges to close off the fill polygon. */ const bool add_left_tri = profile->view_rect.xmin < 0.0f; const bool add_bottom_tri = profile->view_rect.ymin < 0.0f; - uint tot_points = (uint)PROF_TABLE_LEN(profile->path_len) + 1 + add_left_tri + add_bottom_tri; + uint tot_points = (uint)BKE_curveprofile_table_size(profile) + 1 + add_left_tri + add_bottom_tri; const uint tot_triangles = tot_points - 2; /* Create array of the positions of the table's points. */ float(*table_coords)[2] = MEM_mallocN(sizeof(*table_coords) * tot_points, "table x coords"); - for (uint i = 0; i < (uint)PROF_TABLE_LEN(profile->path_len); - i++) { /* Only add the points from the table here. */ + for (uint i = 0; i < (uint)BKE_curveprofile_table_size(profile); i++) { + /* Only add the points from the table here. */ table_coords[i][0] = pts[i].x; table_coords[i][1] = pts[i].y; } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index f0e3464a955..6ee563003ef 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -7587,7 +7587,7 @@ static int ui_do_but_CURVEPROFILE( dist_min_sq = square_f(U.dpi_fac * 8.0f); /* 8 pixel radius from each table point. */ /* Loop through the path's high resolution table and find what's near the click. */ - for (int i = 1; i <= PROF_TABLE_LEN(profile->path_len); i++) { + for (int i = 1; i <= BKE_curveprofile_table_size(profile); i++) { copy_v2_v2(f_xy_prev, f_xy); BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &table[i].x); diff --git a/source/blender/makesdna/DNA_curveprofile_types.h b/source/blender/makesdna/DNA_curveprofile_types.h index 7c76251adc3..447e94d2659 100644 --- a/source/blender/makesdna/DNA_curveprofile_types.h +++ b/source/blender/makesdna/DNA_curveprofile_types.h @@ -29,13 +29,6 @@ extern "C" { #endif -/** Number of points in high resolution table is dynamic up to a maximum. */ -#define PROF_TABLE_MAX 512 -/** Number of table points per control point. */ -#define PROF_RESOL 16 -/** Dynamic size of widget's high resolution table. Input should be profile->totpoint. */ -#define PROF_TABLE_LEN(n_pts) min_ii(PROF_TABLE_MAX, (((n_pts - 1)) * PROF_RESOL) + 1) - /** * Each control point that makes up the profile. * \note The flags use the same enum as Bezier curves, but they aren't guaranteed From dfdc9c62199ebb77007a59aa8e57aba1cb6988de Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 3 Oct 2021 19:52:59 -0500 Subject: [PATCH 0474/1500] Cleanup: Make more functions static This simplifies the surface of the API for a CurveProfile. --- source/blender/blenkernel/BKE_curveprofile.h | 9 - .../blender/blenkernel/intern/curveprofile.cc | 321 +++++++++--------- 2 files changed, 160 insertions(+), 170 deletions(-) diff --git a/source/blender/blenkernel/BKE_curveprofile.h b/source/blender/blenkernel/BKE_curveprofile.h index 62074a30612..5a948f0d844 100644 --- a/source/blender/blenkernel/BKE_curveprofile.h +++ b/source/blender/blenkernel/BKE_curveprofile.h @@ -70,11 +70,6 @@ void BKE_curveprofile_reset_view(struct CurveProfile *profile); void BKE_curveprofile_reset(struct CurveProfile *profile); -void BKE_curveprofile_create_samples(struct CurveProfile *profile, - int n_segments, - bool sample_straight_edges, - struct CurveProfilePoint *r_samples); - int BKE_curveprofile_table_size(const struct CurveProfile *profile); void BKE_curveprofile_init(struct CurveProfile *profile, short segments_len); @@ -87,10 +82,6 @@ enum { }; void BKE_curveprofile_update(struct CurveProfile *profile, const int update_flags); -void BKE_curveprofile_create_samples_even_spacing(struct CurveProfile *profile, - int n_segments, - struct CurveProfilePoint *r_samples); - /* Length portion is the fraction of the total path length where we want the location */ void BKE_curveprofile_evaluate_length_portion(const struct CurveProfile *profile, float length_portion, diff --git a/source/blender/blenkernel/intern/curveprofile.cc b/source/blender/blenkernel/intern/curveprofile.cc index 9a7a8f7329f..78ec05838c2 100644 --- a/source/blender/blenkernel/intern/curveprofile.cc +++ b/source/blender/blenkernel/intern/curveprofile.cc @@ -44,6 +44,21 @@ /** \name Data Handling * \{ */ +/** + * Returns a pointer to a newly allocated curve profile, using the given preset. + */ +struct CurveProfile *BKE_curveprofile_add(eCurveProfilePresets preset) +{ + CurveProfile *profile = (CurveProfile *)MEM_callocN(sizeof(CurveProfile), __func__); + + BKE_curveprofile_set_defaults(profile); + profile->preset = preset; + BKE_curveprofile_reset(profile); + BKE_curveprofile_update(profile, 0); + + return profile; +} + void BKE_curveprofile_free_data(CurveProfile *profile) { MEM_SAFE_FREE(profile->path); @@ -676,7 +691,7 @@ static void calculate_path_handles(CurveProfilePoint *path, int path_len) } /** - * Helper function for 'BKE_curveprofile_create_samples.' Calculates the angle between the + * Helper function for #create_samples. Calculates the angle between the * handles on the inside of the edge starting at index i. A larger angle means the edge is * more curved. * \param i_edge: The start index of the edge to calculate the angle for. @@ -702,7 +717,7 @@ struct CurvatureSortPoint { }; /** - * Helper function for 'BKE_curveprofile_create_samples' for sorting edges based on curvature. + * Helper function for #create_samples for sorting edges based on curvature. */ static int sort_points_curvature(const void *in_a, const void *in_b) { @@ -729,10 +744,10 @@ static int sort_points_curvature(const void *in_a, const void *in_b) * n_segments. Fill the array with the sampled locations and if the point corresponds to a * control point, its handle type. */ -void BKE_curveprofile_create_samples(CurveProfile *profile, - int n_segments, - bool sample_straight_edges, - CurveProfilePoint *r_samples) +static void create_samples(CurveProfile *profile, + int n_segments, + bool sample_straight_edges, + CurveProfilePoint *r_samples) { CurveProfilePoint *path = profile->path; int totpoints = profile->path_len; @@ -855,51 +870,6 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, MEM_freeN(n_samples); } -/** - * Creates a higher resolution table by sampling the curved points. - * This table is used for display and evenly spaced evaluation. - */ -static void curveprofile_make_table(CurveProfile *profile) -{ - int n_samples = BKE_curveprofile_table_size(profile); - CurveProfilePoint *new_table = (CurveProfilePoint *)MEM_callocN( - sizeof(CurveProfilePoint) * (n_samples + 1), __func__); - - BKE_curveprofile_create_samples(profile, n_samples - 1, false, new_table); - /* Manually add last point at the end of the profile */ - new_table[n_samples - 1].x = 0.0f; - new_table[n_samples - 1].y = 1.0f; - - MEM_SAFE_FREE(profile->table); - profile->table = new_table; -} - -/** - * Creates the table of points used for displaying a preview of the sampled segment locations on - * the widget itself. - */ -static void curveprofile_make_segments_table(CurveProfile *profile) -{ - int n_samples = profile->segments_len; - if (n_samples <= 0) { - return; - } - CurveProfilePoint *new_table = (CurveProfilePoint *)MEM_callocN( - sizeof(CurveProfilePoint) * (n_samples + 1), __func__); - - if (profile->flag & PROF_SAMPLE_EVEN_LENGTHS) { - /* Even length sampling incompatible with only straight edge sampling for now. */ - BKE_curveprofile_create_samples_even_spacing(profile, n_samples, new_table); - } - else { - BKE_curveprofile_create_samples( - profile, n_samples, profile->flag & PROF_SAMPLE_STRAIGHT_EDGES, new_table); - } - - MEM_SAFE_FREE(profile->segments); - profile->segments = new_table; -} - /** * Sets the default settings and clip range for the profile widget. * Does not generate either table. @@ -925,18 +895,149 @@ void BKE_curveprofile_set_defaults(CurveProfile *profile) } /** - * Returns a pointer to a newly allocated curve profile, using the given preset. + * Refreshes the higher resolution table sampled from the input points. A call to this or + * #BKE_curveprofile_update is needed before evaluation functions that use the table. + * Also sets the number of segments used for the display preview of the locations + * of the sampled points. */ -struct CurveProfile *BKE_curveprofile_add(eCurveProfilePresets preset) +void BKE_curveprofile_init(CurveProfile *profile, short segments_len) { - CurveProfile *profile = (CurveProfile *)MEM_callocN(sizeof(CurveProfile), __func__); + if (segments_len != profile->segments_len) { + profile->flag |= PROF_DIRTY_PRESET; + } + profile->segments_len = segments_len; - BKE_curveprofile_set_defaults(profile); - profile->preset = preset; - BKE_curveprofile_reset(profile); - curveprofile_make_table(profile); + /* Calculate the higher resolution / segments tables for display and evaluation. */ + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); +} - return profile; +/** + * Gives the distance to the next point in the widgets sampled table, in other words the length + * of the \a 'i' edge of the table. + * + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. + */ +static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i) +{ + BLI_assert(i < BKE_curveprofile_table_size(profile)); + + return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); +} + +/** + * Calculates the total length of the profile from the curves sampled in the table. + * + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. + */ +static float curveprofile_total_length(const CurveProfile *profile) +{ + float total_length = 0; + for (int i = 0; i < BKE_curveprofile_table_size(profile) - 1; i++) { + total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); + } + return total_length; +} + +/** + * Samples evenly spaced positions along the curve profile's table (generated from path). Fills + * an entire table at once for a speedup if all of the results are going to be used anyway. + * + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. + * \note Working, but would conflict with "Sample Straight Edges" option, so this is unused for + * now. + */ +static void create_samples_even_spacing(CurveProfile *profile, + int n_segments, + CurveProfilePoint *r_samples) +{ + const float total_length = curveprofile_total_length(profile); + const float segment_length = total_length / n_segments; + float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0); + float distance_to_previous_table_point = 0.0f; + int i_table = 0; + + /* Set the location for the first point. */ + r_samples[0].x = profile->table[0].x; + r_samples[0].y = profile->table[0].y; + + /* Travel along the path, recording the locations of segments as we pass them. */ + float segment_left = segment_length; + for (int i = 1; i < n_segments; i++) { + /* Travel over all of the points that fit inside this segment. */ + while (distance_to_next_table_point < segment_left) { + segment_left -= distance_to_next_table_point; + i_table++; + distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table); + distance_to_previous_table_point = 0.0f; + } + /* We're at the last table point that fits inside the current segment, use interpolation. */ + float factor = (distance_to_previous_table_point + segment_left) / + (distance_to_previous_table_point + distance_to_next_table_point); + r_samples[i].x = interpf(profile->table[i_table + 1].x, profile->table[i_table].x, factor); + r_samples[i].y = interpf(profile->table[i_table + 1].y, profile->table[i_table].y, factor); + BLI_assert(factor <= 1.0f && factor >= 0.0f); +#ifdef DEBUG_CURVEPROFILE_EVALUATE + printf("segment_left: %.3f\n", segment_left); + printf("i_table: %d\n", i_table); + printf("distance_to_previous_table_point: %.3f\n", distance_to_previous_table_point); + printf("distance_to_next_table_point: %.3f\n", distance_to_next_table_point); + printf("Interpolating with factor %.3f from (%.3f, %.3f) to (%.3f, %.3f)\n\n", + factor, + profile->table[i_table].x, + profile->table[i_table].y, + profile->table[i_table + 1].x, + profile->table[i_table + 1].y); +#endif + + /* We sampled in between this table point and the next, so the next travel step is smaller. */ + distance_to_next_table_point -= segment_left; + distance_to_previous_table_point += segment_left; + segment_left = segment_length; + } +} + +/** + * Creates a higher resolution table by sampling the curved points. + * This table is used for display and evenly spaced evaluation. + */ +static void curveprofile_make_table(CurveProfile *profile) +{ + int n_samples = BKE_curveprofile_table_size(profile); + CurveProfilePoint *new_table = (CurveProfilePoint *)MEM_callocN( + sizeof(CurveProfilePoint) * (n_samples + 1), __func__); + + create_samples(profile, n_samples - 1, false, new_table); + /* Manually add last point at the end of the profile */ + new_table[n_samples - 1].x = 0.0f; + new_table[n_samples - 1].y = 1.0f; + + MEM_SAFE_FREE(profile->table); + profile->table = new_table; +} + +/** + * Creates the table of points used for displaying a preview of the sampled segment locations on + * the widget itself. + */ +static void curveprofile_make_segments_table(CurveProfile *profile) +{ + int n_samples = profile->segments_len; + if (n_samples <= 0) { + return; + } + CurveProfilePoint *new_table = (CurveProfilePoint *)MEM_callocN( + sizeof(CurveProfilePoint) * (n_samples + 1), __func__); + + if (profile->flag & PROF_SAMPLE_EVEN_LENGTHS) { + /* Even length sampling incompatible with only straight edge sampling for now. */ + create_samples_even_spacing(profile, n_samples, new_table); + } + else { + create_samples(profile, n_samples, profile->flag & PROF_SAMPLE_STRAIGHT_EDGES, new_table); + } + + MEM_SAFE_FREE(profile->segments); + profile->segments = new_table; } /** @@ -1000,108 +1101,6 @@ void BKE_curveprofile_update(CurveProfile *profile, const int update_flags) } } -/** - * Refreshes the higher resolution table sampled from the input points. A call to this or - * #BKE_curveprofile_update is needed before evaluation functions that use the table. - * Also sets the number of segments used for the display preview of the locations - * of the sampled points. - */ -void BKE_curveprofile_init(CurveProfile *profile, short segments_len) -{ - if (segments_len != profile->segments_len) { - profile->flag |= PROF_DIRTY_PRESET; - } - profile->segments_len = segments_len; - - /* Calculate the higher resolution / segments tables for display and evaluation. */ - BKE_curveprofile_update(profile, PROF_UPDATE_NONE); -} - -/** - * Gives the distance to the next point in the widgets sampled table, in other words the length - * of the \a 'i' edge of the table. - * - * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. - */ -static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i) -{ - BLI_assert(i < BKE_curveprofile_table_size(profile)); - - return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); -} - -/** - * Calculates the total length of the profile from the curves sampled in the table. - * - * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. - */ -static float curveprofile_total_length(const CurveProfile *profile) -{ - float total_length = 0; - for (int i = 0; i < BKE_curveprofile_table_size(profile) - 1; i++) { - total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); - } - return total_length; -} - -/** - * Samples evenly spaced positions along the curve profile's table (generated from path). Fills - * an entire table at once for a speedup if all of the results are going to be used anyway. - * - * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. - * \note Working, but would conflict with "Sample Straight Edges" option, so this is unused for - * now. - */ -void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, - int n_segments, - CurveProfilePoint *r_samples) -{ - const float total_length = curveprofile_total_length(profile); - const float segment_length = total_length / n_segments; - float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0); - float distance_to_previous_table_point = 0.0f; - int i_table = 0; - - /* Set the location for the first point. */ - r_samples[0].x = profile->table[0].x; - r_samples[0].y = profile->table[0].y; - - /* Travel along the path, recording the locations of segments as we pass them. */ - float segment_left = segment_length; - for (int i = 1; i < n_segments; i++) { - /* Travel over all of the points that fit inside this segment. */ - while (distance_to_next_table_point < segment_left) { - segment_left -= distance_to_next_table_point; - i_table++; - distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table); - distance_to_previous_table_point = 0.0f; - } - /* We're at the last table point that fits inside the current segment, use interpolation. */ - float factor = (distance_to_previous_table_point + segment_left) / - (distance_to_previous_table_point + distance_to_next_table_point); - r_samples[i].x = interpf(profile->table[i_table + 1].x, profile->table[i_table].x, factor); - r_samples[i].y = interpf(profile->table[i_table + 1].y, profile->table[i_table].y, factor); - BLI_assert(factor <= 1.0f && factor >= 0.0f); -#ifdef DEBUG_CURVEPROFILE_EVALUATE - printf("segment_left: %.3f\n", segment_left); - printf("i_table: %d\n", i_table); - printf("distance_to_previous_table_point: %.3f\n", distance_to_previous_table_point); - printf("distance_to_next_table_point: %.3f\n", distance_to_next_table_point); - printf("Interpolating with factor %.3f from (%.3f, %.3f) to (%.3f, %.3f)\n\n", - factor, - profile->table[i_table].x, - profile->table[i_table].y, - profile->table[i_table + 1].x, - profile->table[i_table + 1].y); -#endif - - /* We sampled in between this table point and the next, so the next travel step is smaller. */ - distance_to_next_table_point -= segment_left; - distance_to_previous_table_point += segment_left; - segment_left = segment_length; - } -} - /** * Does a single evaluation along the profile's path. * Travels down (length_portion * path) length and returns the position at that point. From cc8fa3ee909927c817b881b39f806b0753c80b86 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 3 Oct 2021 20:28:31 -0500 Subject: [PATCH 0475/1500] Fix T91904: Assert when loading empty CurveProfile Somehow, the file from T71329 has an empty curve profile. While that may be a problem in itself, it's reasonable to avoid asserts or crashes when loading or drawing such a CurveProfile. This commit just makes sure the table always has a single vertex, and adds some checks in drawing code. --- .../blender/blenkernel/intern/curveprofile.cc | 8 +- .../editors/interface/interface_draw.c | 94 ++++++++++--------- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/source/blender/blenkernel/intern/curveprofile.cc b/source/blender/blenkernel/intern/curveprofile.cc index 78ec05838c2..7f2a2bc342d 100644 --- a/source/blender/blenkernel/intern/curveprofile.cc +++ b/source/blender/blenkernel/intern/curveprofile.cc @@ -594,7 +594,8 @@ int BKE_curveprofile_table_size(const CurveProfile *profile) /** Number of table points per control point. */ const int resolution = 16; - return std::clamp((profile->path_len - 1) * resolution + 1, 0, PROF_TABLE_MAX); + /* Make sure there is always one sample, even if there are no control points. */ + return std::clamp((profile->path_len - 1) * resolution + 1, 1, PROF_TABLE_MAX); } /** @@ -1006,7 +1007,10 @@ static void curveprofile_make_table(CurveProfile *profile) CurveProfilePoint *new_table = (CurveProfilePoint *)MEM_callocN( sizeof(CurveProfilePoint) * (n_samples + 1), __func__); - create_samples(profile, n_samples - 1, false, new_table); + if (n_samples > 1) { + create_samples(profile, n_samples - 1, false, new_table); + } + /* Manually add last point at the end of the profile */ new_table[n_samples - 1].x = 0.0f; new_table[n_samples - 1].y = 1.0f; diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index e9404b0273d..6cb0fcd499c 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -1860,7 +1860,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Also add the last points on the right and bottom edges to close off the fill polygon. */ const bool add_left_tri = profile->view_rect.xmin < 0.0f; const bool add_bottom_tri = profile->view_rect.ymin < 0.0f; - uint tot_points = (uint)BKE_curveprofile_table_size(profile) + 1 + add_left_tri + add_bottom_tri; + int tot_points = BKE_curveprofile_table_size(profile) + 1 + add_left_tri + add_bottom_tri; const uint tot_triangles = tot_points - 2; /* Create array of the positions of the table's points. */ @@ -1903,44 +1903,50 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, } /* Calculate the table point indices of the triangles for the profile's fill. */ - uint(*tri_indices)[3] = MEM_mallocN(sizeof(*tri_indices) * tot_triangles, "return tri indices"); - BLI_polyfill_calc(table_coords, tot_points, -1, tri_indices); + if (tot_triangles > 0) { + uint(*tri_indices)[3] = MEM_mallocN(sizeof(*tri_indices) * tot_triangles, __func__); + BLI_polyfill_calc(table_coords, tot_points, -1, tri_indices); - /* Draw the triangles for the profile fill. */ - immUniformColor3ubvAlpha((const uchar *)wcol->item, 128); - GPU_blend(GPU_BLEND_ALPHA); - GPU_polygon_smooth(false); - immBegin(GPU_PRIM_TRIS, 3 * tot_triangles); - for (uint i = 0; i < tot_triangles; i++) { - for (uint j = 0; j < 3; j++) { - uint *tri = tri_indices[i]; - fx = rect->xmin + zoomx * (table_coords[tri[j]][0] - offsx); - fy = rect->ymin + zoomy * (table_coords[tri[j]][1] - offsy); - immVertex2f(pos, fx, fy); + /* Draw the triangles for the profile fill. */ + immUniformColor3ubvAlpha((const uchar *)wcol->item, 128); + GPU_blend(GPU_BLEND_ALPHA); + GPU_polygon_smooth(false); + immBegin(GPU_PRIM_TRIS, 3 * tot_triangles); + for (uint i = 0; i < tot_triangles; i++) { + for (uint j = 0; j < 3; j++) { + uint *tri = tri_indices[i]; + fx = rect->xmin + zoomx * (table_coords[tri[j]][0] - offsx); + fy = rect->ymin + zoomy * (table_coords[tri[j]][1] - offsy); + immVertex2f(pos, fx, fy); + } } + immEnd(); + MEM_freeN(tri_indices); } - immEnd(); - MEM_freeN(tri_indices); /* Draw the profile's path so the edge stands out a bit. */ tot_points -= (add_left_tri + add_left_tri); - GPU_line_width(1.0f); - immUniformColor3ubvAlpha((const uchar *)wcol->item, 255); - GPU_line_smooth(true); - immBegin(GPU_PRIM_LINE_STRIP, tot_points - 1); - for (uint i = 0; i < tot_points - 1; i++) { - fx = rect->xmin + zoomx * (table_coords[i][0] - offsx); - fy = rect->ymin + zoomy * (table_coords[i][1] - offsy); - immVertex2f(pos, fx, fy); + const int edges_len = tot_points - 1; + if (edges_len > 0) { + GPU_line_width(1.0f); + immUniformColor3ubvAlpha((const uchar *)wcol->item, 255); + GPU_line_smooth(true); + immBegin(GPU_PRIM_LINE_STRIP, tot_points); + for (int i = 0; i < tot_points; i++) { + fx = rect->xmin + zoomx * (table_coords[i][0] - offsx); + fy = rect->ymin + zoomy * (table_coords[i][1] - offsy); + immVertex2f(pos, fx, fy); + } + immEnd(); } - immEnd(); - MEM_freeN(table_coords); + + MEM_SAFE_FREE(table_coords); /* Draw the handles for the selected control points. */ pts = profile->path; - tot_points = (uint)profile->path_len; + const int path_len = tot_points = (uint)profile->path_len; int selected_free_points = 0; - for (uint i = 0; i < tot_points; i++) { + for (int i = 0; i < path_len; i++) { if (point_draw_handles(&pts[i])) { selected_free_points++; } @@ -1952,7 +1958,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, GPU_line_smooth(true); immBegin(GPU_PRIM_LINES, selected_free_points * 4); float ptx, pty; - for (uint i = 0; i < tot_points; i++) { + for (int i = 0; i < path_len; i++) { if (point_draw_handles(&pts[i])) { ptx = rect->xmin + zoomx * (pts[i].x - offsx); pty = rect->ymin + zoomy * (pts[i].y - offsy); @@ -1996,16 +2002,18 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Draw the control points. */ GPU_line_smooth(false); - GPU_blend(GPU_BLEND_NONE); - GPU_point_size(max_ff(3.0f, min_ff(UI_DPI_FAC / but->block->aspect * 5.0f, 5.0f))); - immBegin(GPU_PRIM_POINTS, tot_points); - for (uint i = 0; i < tot_points; i++) { - fx = rect->xmin + zoomx * (pts[i].x - offsx); - fy = rect->ymin + zoomy * (pts[i].y - offsy); - immAttr4fv(col, (pts[i].flag & PROF_SELECT) ? color_vert_select : color_vert); - immVertex2f(pos, fx, fy); + if (path_len > 0) { + GPU_blend(GPU_BLEND_NONE); + GPU_point_size(max_ff(3.0f, min_ff(UI_DPI_FAC / but->block->aspect * 5.0f, 5.0f))); + immBegin(GPU_PRIM_POINTS, path_len); + for (int i = 0; i < path_len; i++) { + fx = rect->xmin + zoomx * (pts[i].x - offsx); + fy = rect->ymin + zoomy * (pts[i].y - offsy); + immAttr4fv(col, (pts[i].flag & PROF_SELECT) ? color_vert_select : color_vert); + immVertex2f(pos, fx, fy); + } + immEnd(); } - immEnd(); /* Draw the handle points. */ if (selected_free_points > 0) { @@ -2013,7 +2021,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, GPU_blend(GPU_BLEND_NONE); GPU_point_size(max_ff(2.0f, min_ff(UI_DPI_FAC / but->block->aspect * 4.0f, 4.0f))); immBegin(GPU_PRIM_POINTS, selected_free_points * 2); - for (uint i = 0; i < tot_points; i++) { + for (int i = 0; i < path_len; i++) { if (point_draw_handles(&pts[i])) { fx = rect->xmin + zoomx * (pts[i].h1_loc[0] - offsx); fy = rect->ymin + zoomy * (pts[i].h1_loc[1] - offsy); @@ -2031,11 +2039,11 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Draw the sampled points in addition to the control points if they have been created */ pts = profile->segments; - tot_points = (uint)profile->segments_len; - if (tot_points > 0 && pts) { + const int segments_len = (uint)profile->segments_len; + if (segments_len > 0 && pts) { GPU_point_size(max_ff(2.0f, min_ff(UI_DPI_FAC / but->block->aspect * 3.0f, 3.0f))); - immBegin(GPU_PRIM_POINTS, tot_points); - for (uint i = 0; i < tot_points; i++) { + immBegin(GPU_PRIM_POINTS, segments_len); + for (int i = 0; i < segments_len; i++) { fx = rect->xmin + zoomx * (pts[i].x - offsx); fy = rect->ymin + zoomy * (pts[i].y - offsy); immAttr4fv(col, color_sample); From e7274dedc4475e15cd06a7c83ec9e0d477f13314 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 4 Oct 2021 13:12:34 +1100 Subject: [PATCH 0476/1500] Fix possible NULL pointer deference The pointer was referenced before being checked. --- .../asset/intern/asset_library_reference_enum.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc index 5a8fed6fea1..c7c0f35c12d 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -80,14 +80,16 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value) /* Note that there is no check if the path exists here. If an invalid library path is used, the * Asset Browser can give a nice hint on what's wrong. */ - const bool is_valid = (user_library->name[0] && user_library->path[0]); if (!user_library) { library.type = ASSET_LIBRARY_LOCAL; library.custom_library_index = -1; } - else if (user_library && is_valid) { - library.custom_library_index = value - ASSET_LIBRARY_CUSTOM; - library.type = ASSET_LIBRARY_CUSTOM; + else { + const bool is_valid = (user_library->name[0] && user_library->path[0]); + if (is_valid) { + library.custom_library_index = value - ASSET_LIBRARY_CUSTOM; + library.type = ASSET_LIBRARY_CUSTOM; + } } return library; } From e0e7a5522f20699e4ffeeef793545f954db790da Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 4 Oct 2021 13:12:35 +1100 Subject: [PATCH 0477/1500] project_source_info: queue_processes() now waits for jobs to finish queue_processes() - used for some of the "make check_*" utilities, wasn't waiting for all jobs to finish before returning. This conflicted with running cleanup operations. --- build_files/cmake/project_source_info.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/build_files/cmake/project_source_info.py b/build_files/cmake/project_source_info.py index d2ed80022ca..c2ba7e5b11c 100644 --- a/build_files/cmake/project_source_info.py +++ b/build_files/cmake/project_source_info.py @@ -243,7 +243,9 @@ def build_defines_as_args() -> List[str]: # use this module. def queue_processes( process_funcs: Sequence[Tuple[Callable[..., subprocess.Popen[Any]], Tuple[Any, ...]]], + *, job_total: int =-1, + sleep: float = 0.1, ) -> None: """ Takes a list of function arg pairs, each function must return a process """ @@ -271,14 +273,20 @@ def queue_processes( if len(processes) <= job_total: break - else: - time.sleep(0.1) + time.sleep(sleep) sys.stdout.flush() sys.stderr.flush() processes.append(func(*args)) + # Don't return until all jobs have finished. + while 1: + processes[:] = [p for p in processes if p.poll() is None] + if not processes: + break + time.sleep(sleep) + def main() -> None: if not os.path.exists(join(CMAKE_DIR, "CMakeCache.txt")): From 606271966e1344958d43f5660be1ae46d2637738 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 4 Oct 2021 13:12:36 +1100 Subject: [PATCH 0478/1500] check_cppcheck: use '--cppcheck-build-dir' Use a temporary directory for faster performance. --- build_files/cmake/cmake_static_check_cppcheck.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/build_files/cmake/cmake_static_check_cppcheck.py b/build_files/cmake/cmake_static_check_cppcheck.py index 1eef2efe2b5..79b0e236ce5 100644 --- a/build_files/cmake/cmake_static_check_cppcheck.py +++ b/build_files/cmake/cmake_static_check_cppcheck.py @@ -24,6 +24,7 @@ import project_source_info import subprocess import sys import os +import tempfile from typing import ( Any, @@ -47,13 +48,15 @@ CHECKER_ARGS = [ "--max-configs=1", # speeds up execution # "--check-config", # when includes are missing "--enable=all", # if you want sixty hundred pedantic suggestions + + # NOTE: `--cppcheck-build-dir=` is added later as a temporary directory. ] if USE_QUIET: CHECKER_ARGS.append("--quiet") -def main() -> None: +def cppcheck() -> None: source_info = project_source_info.build_info(ignore_prefix_list=CHECKER_IGNORE_PREFIX) source_defines = project_source_info.build_defines_as_args() @@ -90,5 +93,11 @@ def main() -> None: print("Finished!") +def main() -> None: + with tempfile.TemporaryDirectory() as temp_dir: + CHECKER_ARGS.append("--cppcheck-build-dir=" + temp_dir) + cppcheck() + + if __name__ == "__main__": main() From 6e48a51af7925e84988cb83f986a7d5d415a2ae2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 4 Oct 2021 13:12:37 +1100 Subject: [PATCH 0479/1500] check_cppcheck: use quiet output Without this, each cppcheck invocation included all defines/includes flooding the console with unhelpful information. Also remove nonexistent directory to exclude. --- build_files/cmake/cmake_static_check_cppcheck.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/build_files/cmake/cmake_static_check_cppcheck.py b/build_files/cmake/cmake_static_check_cppcheck.py index 79b0e236ce5..0e37b9ba468 100644 --- a/build_files/cmake/cmake_static_check_cppcheck.py +++ b/build_files/cmake/cmake_static_check_cppcheck.py @@ -36,7 +36,6 @@ USE_QUIET = (os.environ.get("QUIET", None) is not None) CHECKER_IGNORE_PREFIX = [ "extern", - "intern/moto", ] CHECKER_BIN = "cppcheck" @@ -49,6 +48,10 @@ CHECKER_ARGS = [ # "--check-config", # when includes are missing "--enable=all", # if you want sixty hundred pedantic suggestions + # Quiet output, otherwise all defines/includes are printed (overly verbose). + # Only enable this for troubleshooting (if defines are not set as expected for example). + "--quiet", + # NOTE: `--cppcheck-build-dir=` is added later as a temporary directory. ] @@ -81,7 +84,10 @@ def cppcheck() -> None: percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:" sys.stdout.flush() - sys.stdout.write("%s " % percent_str) + sys.stdout.write("%s %s\n" % ( + percent_str, + os.path.relpath(c, project_source_info.SOURCE_DIR) + )) return subprocess.Popen(cmd) From 3c3669894f9d8b9e079605aee1a84796532fbe5b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 4 Oct 2021 13:12:38 +1100 Subject: [PATCH 0480/1500] Cleanup: use system includes --- source/blender/blenlib/BLI_task.hh | 2 +- source/blender/blenlib/intern/fileops.c | 4 ++-- source/blender/blenlib/intern/math_color_inline.c | 2 +- source/blender/blenlib/intern/mesh_boolean.cc | 4 ++-- source/blender/blenlib/intern/mesh_intersect.cc | 2 +- source/blender/blenlib/intern/path_util.c | 2 +- source/blender/datatoc/datatoc_icon.c | 2 +- source/blender/editors/io/io_usd.c | 2 +- source/blender/editors/util/select_utils.c | 4 ++-- source/blender/imbuf/intern/jpeg.c | 5 +++-- source/blender/imbuf/intern/png.c | 2 +- source/blender/imbuf/intern/tiff.c | 2 +- source/blender/io/avi/intern/avi_mjpeg.c | 4 ++-- 13 files changed, 19 insertions(+), 18 deletions(-) diff --git a/source/blender/blenlib/BLI_task.hh b/source/blender/blenlib/BLI_task.hh index e2446ad143e..da7309837c8 100644 --- a/source/blender/blenlib/BLI_task.hh +++ b/source/blender/blenlib/BLI_task.hh @@ -28,10 +28,10 @@ # define NOMINMAX # define TBB_MIN_MAX_CLEANUP # endif -# include "tbb/parallel_reduce.h" # include # include # include +# include # include # ifdef WIN32 /* We cannot keep this defined, since other parts of the code deal with this on their own, leading diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 2ef4d1093a8..7019acfbbdc 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -30,8 +30,8 @@ #include -#include "zlib.h" -#include "zstd.h" +#include +#include #ifdef WIN32 # include "BLI_fileops_types.h" diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c index ad4b844175f..8b3e3b11cff 100644 --- a/source/blender/blenlib/intern/math_color_inline.c +++ b/source/blender/blenlib/intern/math_color_inline.c @@ -27,7 +27,7 @@ #include "BLI_math_color.h" #include "BLI_utildefines.h" -#include "math.h" +#include #ifndef __MATH_COLOR_INLINE_C__ # define __MATH_COLOR_INLINE_C__ diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 90ffebdb422..8b029d11c3f 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -51,8 +51,8 @@ # include "BLI_mesh_boolean.hh" # ifdef WITH_TBB -# include "tbb/parallel_reduce.h" -# include "tbb/spin_mutex.h" +# include +# include # endif // # define PERFDEBUG diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 5651e52799e..526871c7b1f 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -53,7 +53,7 @@ # include "BLI_mesh_intersect.hh" # ifdef WITH_TBB -# include "tbb/parallel_sort.h" +# include # endif // # define PERFDEBUG diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 066749f3a94..2403d34e5d7 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -48,7 +48,7 @@ # include # include #else -# include "unistd.h" +# include #endif /* WIN32 */ #include "MEM_guardedalloc.h" diff --git a/source/blender/datatoc/datatoc_icon.c b/source/blender/datatoc/datatoc_icon.c index ba7120daa92..8a0c7175fee 100644 --- a/source/blender/datatoc/datatoc_icon.c +++ b/source/blender/datatoc/datatoc_icon.c @@ -32,7 +32,7 @@ # include #endif -#include "png.h" +#include /* for Win32 DIR functions */ #ifdef WIN32 diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index d0007d9e5be..4e2ccea36ab 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -57,7 +57,7 @@ # include "io_usd.h" # include "usd.h" -# include "stdio.h" +# include const EnumPropertyItem rna_enum_usd_export_evaluation_mode_items[] = { {DAG_EVAL_RENDER, diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c index 5681edd2f5c..99412079adf 100644 --- a/source/blender/editors/util/select_utils.c +++ b/source/blender/editors/util/select_utils.c @@ -18,14 +18,14 @@ * \ingroup edutil */ +#include + #include "BLI_kdtree.h" #include "BLI_math.h" #include "BLI_utildefines.h" #include "ED_select_utils.h" -#include "float.h" - /** 1: select, 0: deselect, -1: pass. */ int ED_select_op_action(const eSelectOp sel_op, const bool is_select, const bool is_inside) { diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 48b5b0c34db..c3a07d7face 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -40,8 +40,9 @@ #include "IMB_imbuf_types.h" #include "IMB_metadata.h" #include "imbuf.h" -#include "jerror.h" -#include "jpeglib.h" + +#include +#include #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c index 399fd487065..26f0f11a001 100644 --- a/source/blender/imbuf/intern/png.c +++ b/source/blender/imbuf/intern/png.c @@ -23,7 +23,7 @@ * \todo Save floats as 16 bits per channel, currently readonly. */ -#include "png.h" +#include #include "BLI_fileops.h" #include "BLI_math.h" diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index d9e1db27ef0..3625d7d1af2 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -52,7 +52,7 @@ #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" -#include "tiffio.h" +#include #ifdef WIN32 # include "utfconv.h" diff --git a/source/blender/io/avi/intern/avi_mjpeg.c b/source/blender/io/avi/intern/avi_mjpeg.c index 75059c202e5..8b132df7b8f 100644 --- a/source/blender/io/avi/intern/avi_mjpeg.c +++ b/source/blender/io/avi/intern/avi_mjpeg.c @@ -33,8 +33,8 @@ #include "BLI_math_base.h" #include "IMB_imbuf.h" -#include "jerror.h" -#include "jpeglib.h" +#include +#include #include "avi_mjpeg.h" From e43fcc014a288706818d1fa19918511802ff98d9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 4 Oct 2021 13:12:39 +1100 Subject: [PATCH 0481/1500] Cleanup: remove redundant assignment --- source/blender/io/common/intern/abstract_hierarchy_iterator.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index 3cda4d125d0..28d5eb59e5e 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -462,7 +462,6 @@ void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object, context->weak_export = false; context->export_path = ""; context->original_export_path = ""; - context->export_path = ""; context->animation_check_include_parent = false; copy_m4_m4(context->matrix_world, dupli_object->mat); From 357acd1d5053b2ff9c8a09b9b6ad2e170c10b875 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 4 Oct 2021 13:12:40 +1100 Subject: [PATCH 0482/1500] Cleanup: pass arguments as const --- intern/mikktspace/mikktspace.c | 4 ++-- source/blender/blenlib/BLI_math_geom.h | 2 +- source/blender/blenlib/BLI_math_vector.h | 2 +- source/blender/blenlib/intern/math_geom_inline.c | 2 +- source/blender/blenlib/intern/math_vector_inline.c | 2 +- source/blender/datatoc/datatoc_icon.c | 2 +- .../editors/space_outliner/tree/tree_display.hh | 4 ++-- .../space_outliner/tree/tree_display_libraries.cc | 2 +- .../tree/tree_display_override_library.cc | 2 +- .../editors/space_outliner/tree/tree_element.cc | 9 +++++---- source/blender/editors/transform/transform.h | 6 +++--- .../editors/transform/transform_constraints.c | 12 ++++++------ .../gpencil_modifiers/intern/MOD_gpencil_util.c | 2 +- .../gpencil_modifiers/intern/MOD_gpencil_util.h | 2 +- 14 files changed, 27 insertions(+), 26 deletions(-) diff --git a/intern/mikktspace/mikktspace.c b/intern/mikktspace/mikktspace.c index 96e8d433e30..b4596dc35ad 100644 --- a/intern/mikktspace/mikktspace.c +++ b/intern/mikktspace/mikktspace.c @@ -1112,7 +1112,7 @@ static tbool AssignRecur(const int piTriListIn[], static tbool CompareSubGroups(const SSubGroup *pg1, const SSubGroup *pg2); static void QuickSort(int *pSortBuffer, int iLeft, int iRight, unsigned int uSeed); -static STSpace EvalTspace(int face_indices[], +static STSpace EvalTspace(const int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], @@ -1292,7 +1292,7 @@ static tbool GenerateTSpaces(STSpace psTspace[], return TTRUE; } -static STSpace EvalTspace(int face_indices[], +static STSpace EvalTspace(const int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index bcda25ca533..fa9a30467ac 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -781,7 +781,7 @@ MINLINE void add_sh_shsh(float r[9], const float a[9], const float b[9]); MINLINE float dot_shsh(const float a[9], const float b[9]); MINLINE float eval_shv3(float r[9], const float v[3]); -MINLINE float diffuse_shv3(float r[9], const float v[3]); +MINLINE float diffuse_shv3(const float r[9], const float v[3]); MINLINE void vec_fac_to_sh(float r[9], const float v[3], const float f); MINLINE void madd_sh_shfl(float r[9], const float sh[9], const float f); diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 860ba14a3ed..62fd4a835ef 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -214,7 +214,7 @@ MINLINE void cross_v3_v3v3_db(double r[3], const double a[3], const double b[3]) MINLINE void add_newell_cross_v3_v3v3(float n[3], const float v_prev[3], const float v_curr[3]); -MINLINE void star_m3_v3(float rmat[3][3], float a[3]); +MINLINE void star_m3_v3(float rmat[3][3], const float a[3]); /*********************************** Length **********************************/ diff --git a/source/blender/blenlib/intern/math_geom_inline.c b/source/blender/blenlib/intern/math_geom_inline.c index 1757b0dd525..857cbfbde9c 100644 --- a/source/blender/blenlib/intern/math_geom_inline.c +++ b/source/blender/blenlib/intern/math_geom_inline.c @@ -99,7 +99,7 @@ MINLINE float dot_shsh(const float a[9], const float b[9]) return r; } -MINLINE float diffuse_shv3(float sh[9], const float v[3]) +MINLINE float diffuse_shv3(const float sh[9], const float v[3]) { /* See formula (13) in: * "An Efficient Representation for Irradiance Environment Maps" */ diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index ddfdaffb706..8be066bb0e9 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -991,7 +991,7 @@ MINLINE void add_newell_cross_v3_v3v3(float n[3], const float v_prev[3], const f n[2] += (v_prev[0] - v_curr[0]) * (v_prev[1] + v_curr[1]); } -MINLINE void star_m3_v3(float rmat[3][3], float a[3]) +MINLINE void star_m3_v3(float rmat[3][3], const float a[3]) { rmat[0][0] = rmat[1][1] = rmat[2][2] = 0.0; rmat[0][1] = -a[2]; diff --git a/source/blender/datatoc/datatoc_icon.c b/source/blender/datatoc/datatoc_icon.c index 8a0c7175fee..b4ee1c6d4c9 100644 --- a/source/blender/datatoc/datatoc_icon.c +++ b/source/blender/datatoc/datatoc_icon.c @@ -245,7 +245,7 @@ static struct IconInfo *icon_merge_context_info_for_icon_head(struct IconMergeCo static void icon_merge_context_register_icon(struct IconMergeContext *context, const char *file_name, - struct IconHead *icon_head) + const struct IconHead *icon_head) { context->read_icons = realloc(context->read_icons, sizeof(struct IconInfo) * (context->num_read_icons + 1)); diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh index 96af8258010..8aaf396888f 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.hh +++ b/source/blender/editors/space_outliner/tree/tree_display.hh @@ -106,7 +106,7 @@ class TreeDisplayLibraries final : public AbstractTreeDisplay { private: TreeElement *add_library_contents(Main &, ListBase &, Library *) const; - bool library_id_filter_poll(Library *lib, ID *id) const; + bool library_id_filter_poll(const Library *lib, ID *id) const; short id_filter_get() const; }; @@ -124,7 +124,7 @@ class TreeDisplayOverrideLibrary final : public AbstractTreeDisplay { private: TreeElement *add_library_contents(Main &, ListBase &, Library *) const; - bool override_library_id_filter_poll(Library *lib, ID *id) const; + bool override_library_id_filter_poll(const Library *lib, ID *id) const; short id_filter_get() const; }; diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc index c6b700318dd..371813cfb3f 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc @@ -186,7 +186,7 @@ short TreeDisplayLibraries::id_filter_get() const return 0; } -bool TreeDisplayLibraries::library_id_filter_poll(Library *lib, ID *id) const +bool TreeDisplayLibraries::library_id_filter_poll(const Library *lib, ID *id) const { if (id->lib != lib) { return false; diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc index a17bf174a74..0e4636db69d 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc @@ -186,7 +186,7 @@ short TreeDisplayOverrideLibrary::id_filter_get() const return 0; } -bool TreeDisplayOverrideLibrary::override_library_id_filter_poll(Library *lib, ID *id) const +bool TreeDisplayOverrideLibrary::override_library_id_filter_poll(const Library *lib, ID *id) const { if (id->lib != lib) { return false; diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index 113d421ed91..36da7fe1944 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -91,7 +91,8 @@ static void tree_element_free(AbstractTreeElement **tree_element) *tree_element = nullptr; } -static void tree_element_expand(AbstractTreeElement &tree_element, SpaceOutliner &space_outliner) +static void tree_element_expand(const AbstractTreeElement &tree_element, + SpaceOutliner &space_outliner) { /* Most types can just expand. IDs optionally expand (hence the poll) and do additional, common * expanding. Could be done nicer, we could request a small "expander" helper object from the @@ -107,7 +108,7 @@ static void tree_element_expand(AbstractTreeElement &tree_element, SpaceOutliner * Needed for types that still expand in C, but need to execute the same post-expand logic. Can be * removed once all ID types expand entirely using the new design. */ -static void tree_element_post_expand_only(AbstractTreeElement &tree_element, +static void tree_element_post_expand_only(const AbstractTreeElement &tree_element, SpaceOutliner &space_outliner) { tree_element.postExpand(space_outliner); @@ -116,8 +117,8 @@ static void tree_element_post_expand_only(AbstractTreeElement &tree_element, * Needed for types that still expand in C, to poll if they should expand in current context. Can * be removed once all ID types expand entirely using the new design. */ -static bool tree_element_expand_poll(AbstractTreeElement &tree_element, - SpaceOutliner &space_outliner) +static bool tree_element_expand_poll(const AbstractTreeElement &tree_element, + const SpaceOutliner &space_outliner) { return tree_element.expandPoll(space_outliner); } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 7f4e533ccd7..1be46c03f85 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -365,18 +365,18 @@ typedef struct TransCon { * The last three parameters are pointers to the in/out/printable vectors. */ void (*applyVec)(const struct TransInfo *t, const struct TransDataContainer *tc, - struct TransData *td, + const struct TransData *td, const float in[3], float r_out[3]); /** Apply function pointer for size transformation. */ void (*applySize)(const struct TransInfo *t, const struct TransDataContainer *tc, - struct TransData *td, + const struct TransData *td, float r_smat[3][3]); /** Apply function pointer for rotation transformation */ void (*applyRot)(const struct TransInfo *t, const struct TransDataContainer *tc, - struct TransData *td, + const struct TransData *td, float r_axis[3], float *r_angle); } TransCon; diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 7135395ee2d..e2ad89e0dbc 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -393,7 +393,7 @@ static void planeProjection(const TransInfo *t, const float in[3], float out[3]) */ static void applyAxisConstraintVec(const TransInfo *t, const TransDataContainer *UNUSED(tc), - TransData *td, + const TransData *td, const float in[3], float out[3]) { @@ -477,7 +477,7 @@ static void applyAxisConstraintVec(const TransInfo *t, */ static void applyObjectConstraintVec(const TransInfo *t, const TransDataContainer *tc, - TransData *td, + const TransData *td, const float in[3], float out[3]) { @@ -502,7 +502,7 @@ static void applyObjectConstraintVec(const TransInfo *t, */ static void applyAxisConstraintSize(const TransInfo *t, const TransDataContainer *UNUSED(tc), - TransData *td, + const TransData *td, float r_smat[3][3]) { if (!td && t->con.mode & CON_APPLY) { @@ -528,7 +528,7 @@ static void applyAxisConstraintSize(const TransInfo *t, */ static void applyObjectConstraintSize(const TransInfo *t, const TransDataContainer *tc, - TransData *td, + const TransData *td, float r_smat[3][3]) { if (td && t->con.mode & CON_APPLY) { @@ -603,7 +603,7 @@ static void constraints_rotation_impl(const TransInfo *t, */ static void applyAxisConstraintRot(const TransInfo *t, const TransDataContainer *UNUSED(tc), - TransData *td, + const TransData *td, float r_axis[3], float *r_angle) { @@ -627,7 +627,7 @@ static void applyAxisConstraintRot(const TransInfo *t, */ static void applyObjectConstraintRot(const TransInfo *t, const TransDataContainer *tc, - TransData *td, + const TransData *td, float r_axis[3], float *r_angle) { diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index df78ac8110e..595a0c1cc5e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -73,7 +73,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) /* verify if valid layer, material and pass index */ bool is_stroke_affected_by_modifier(Object *ob, char *mlayername, - Material *material, + const Material *material, const int mpassindex, const int gpl_passindex, const int minpoints, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h index 30e54f44499..2878ad4c73a 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h @@ -31,7 +31,7 @@ struct bGPDstroke; bool is_stroke_affected_by_modifier(struct Object *ob, char *mlayername, - struct Material *material, + const struct Material *material, const int mpassindex, const int gpl_passindex, const int minpoints, From 93e92ac1263505ea3a3bc5a42c1b2f55607ccc45 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 4 Oct 2021 13:12:41 +1100 Subject: [PATCH 0483/1500] Cleanup: remove unused assignments --- source/blender/blenkernel/intern/image_gen.c | 9 ++++----- source/blender/editors/space_outliner/outliner_utils.c | 4 +--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/image_gen.c b/source/blender/blenkernel/intern/image_gen.c index 1a0cc8c2924..943909cc90f 100644 --- a/source/blender/blenkernel/intern/image_gen.c +++ b/source/blender/blenkernel/intern/image_gen.c @@ -101,13 +101,12 @@ static void image_buf_fill_checker_slice( /* these two passes could be combined into one, but it's more readable and * easy to tweak like this, speed isn't really that much of an issue in this situation... */ - int checkerwidth = 32, dark = 1; + int checkerwidth = 32; int x, y; unsigned char *rect_orig = rect; float *rect_float_orig = rect_float; - float h = 0.0, hoffs = 0.0; float hsv[3] = {0.0f, 0.9f, 0.9f}; float rgb[3]; @@ -119,7 +118,7 @@ static void image_buf_fill_checker_slice( /* checkers */ for (y = offset; y < height + offset; y++) { - dark = powf(-1.0f, floorf(y / checkerwidth)); + int dark = powf(-1.0f, floorf(y / checkerwidth)); for (x = 0; x < width; x++) { if (x % checkerwidth == 0) { @@ -156,10 +155,10 @@ static void image_buf_fill_checker_slice( /* 2nd pass, colored + */ for (y = offset; y < height + offset; y++) { - hoffs = 0.125f * floorf(y / checkerwidth); + float hoffs = 0.125f * floorf(y / checkerwidth); for (x = 0; x < width; x++) { - h = 0.125f * floorf(x / checkerwidth); + float h = 0.125f * floorf(x / checkerwidth); if ((abs((x % checkerwidth) - (checkerwidth / 2)) < 4) && (abs((y % checkerwidth) - (checkerwidth / 2)) < 4)) { diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index 5feb157bfc8..c62ca468747 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -117,10 +117,8 @@ static TreeElement *outliner_find_item_at_x_in_row_recursive(const TreeElement * { TreeElement *child_te = parent_te->subtree.first; - bool over_element = false; - while (child_te) { - over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend); + const bool over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend); if ((child_te->flag & TE_ICONROW) && over_element) { return child_te; } From fc6228bd8528629da63cd9b5d012b36e0e5e9fee Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 4 Oct 2021 08:14:06 +0200 Subject: [PATCH 0484/1500] Fix T91873: Crash when opening properties panel This patch fixes a crash that was recently introduced by rB5cebcb415e76. The reason were missing poll functions in the UI and operator. Reviewed By: ISS Maniphest Tasks: T91873 Differential Revision: https://developer.blender.org/D12736 --- .../scripts/startup/bl_ui/space_sequencer.py | 23 +++++++++++-------- .../editors/space_sequencer/sequencer_edit.c | 18 ++++++++++++++- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 197e3efebda..6430d6bab9b 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1048,16 +1048,23 @@ class SequencerButtonsPanel_Output: return cls.has_preview(context) -class SEQUENCER_PT_color_tag_picker(Panel): - bl_label = "Color Tag" +class SequencerColorTagPicker: bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'UI' - bl_category = "Strip" - bl_options = {'HIDE_HEADER', 'INSTANCED'} + + @staticmethod + def has_sequencer(context): + return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}) @classmethod def poll(cls, context): - return context.active_sequence_strip is not None + return cls.has_sequencer(context) and context.active_sequence_strip is not None + + +class SEQUENCER_PT_color_tag_picker(SequencerColorTagPicker, Panel): + bl_label = "Color Tag" + bl_category = "Strip" + bl_options = {'HIDE_HEADER', 'INSTANCED'} def draw(self, context): layout = self.layout @@ -1069,13 +1076,9 @@ class SEQUENCER_PT_color_tag_picker(Panel): row.operator("sequencer.strip_color_tag_set", icon=icon).color = 'COLOR_%02d' % i -class SEQUENCER_MT_color_tag_picker(Menu): +class SEQUENCER_MT_color_tag_picker(SequencerColorTagPicker, Menu): bl_label = "Set Color Tag" - @classmethod - def poll(cls, context): - return context.active_sequence_strip is not None - def draw(self, context): layout = self.layout diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 9be947b9112..d55356eddec 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -3339,6 +3339,22 @@ static int sequencer_strip_color_tag_set_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool sequencer_strip_color_tag_set_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + if (scene == NULL) { + return false; + } + + Editing *ed = SEQ_editing_get(scene); + if (ed == NULL) { + return false; + } + + Sequence *act_seq = ed->act_seq; + return act_seq != NULL; +} + void SEQUENCER_OT_strip_color_tag_set(struct wmOperatorType *ot) { /* Identifiers. */ @@ -3348,7 +3364,7 @@ void SEQUENCER_OT_strip_color_tag_set(struct wmOperatorType *ot) /* Api callbacks. */ ot->exec = sequencer_strip_color_tag_set_exec; - ot->poll = sequencer_edit_poll; + ot->poll = sequencer_strip_color_tag_set_poll; /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; From 8c55333a8e8082ff6766b91d2cbfa74e510e9739 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 4 Oct 2021 09:43:40 +0200 Subject: [PATCH 0485/1500] Cleanup: tag unused parameters as such. --- .../geometry/nodes/node_geo_curve_handle_type_selection.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index dc2070f20e6..093146d563e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -67,7 +67,7 @@ static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCu static void select_by_handle_type(const CurveEval &curve, const BezierSpline::HandleType type, const GeometryNodeCurveHandleMode mode, - const IndexMask mask, + const IndexMask UNUSED(mask), const MutableSpan r_selection) { int offset = 0; From 87a3cb3bffd810f71c9af114a5b8fe8d4f029cf7 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 4 Oct 2021 11:08:33 +0200 Subject: [PATCH 0486/1500] Cleanup: `Neighbour` -> `Neighbor`. and other minor UI messages fixes. Blender English should use 'American' variants, not 'British' variants. --- .../bl_i18n_utils/utils_spell_check.py | 3 +++ .../keyconfig/keymap_data/blender_default.py | 2 +- release/scripts/startup/bl_ui/space_view3d.py | 4 ++-- .../blender/blenkernel/intern/gpencil_curve.c | 2 +- .../editors/animation/keyframes_keylist.cc | 20 +++++++++---------- .../editors/armature/armature_intern.h | 2 +- .../blender/editors/armature/armature_ops.c | 2 +- source/blender/editors/armature/pose_slide.c | 16 +++++++-------- .../makesrna/intern/rna_gpencil_modifier.c | 4 ++-- 9 files changed, 29 insertions(+), 26 deletions(-) diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index 0293d7143ec..c40b4593a19 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -176,6 +176,7 @@ class SpellChecker: "precalculate", "precomputing", "prefetch", + "prefilter", "prefiltering", "preload", "premultiply", "premultiplied", "prepass", @@ -225,6 +226,7 @@ class SpellChecker: "subpath", "subsize", "substep", "substeps", + "substring", "targetless", "textbox", "textboxes", "tilemode", @@ -731,6 +733,7 @@ class SpellChecker: "tma", "ui", "unix", + "uuid", "vbo", "vbos", "vr", "wxyz", diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 8bf5ac500b0..14520c10a2b 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4312,7 +4312,7 @@ def km_pose(params): ("pose.push", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), ("pose.relax", {"type": 'E', "value": 'PRESS', "alt": True}, None), ("pose.breakdown", {"type": 'E', "value": 'PRESS', "shift": True}, None), - ("pose.blend_to_neighbour", {"type": 'E', "value": 'PRESS', "shift": True, "alt": True}, None), + ("pose.blend_to_neighbor", {"type": 'E', "value": 'PRESS', "shift": True, "alt": True}, None), op_menu("VIEW3D_MT_pose_propagate", {"type": 'P', "value": 'PRESS', "alt": True}), *( (("object.hide_collection", diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 281c57b282f..59991eac92a 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -3443,7 +3443,7 @@ class VIEW3D_MT_pose_slide(Menu): layout.operator("pose.push") layout.operator("pose.relax") layout.operator("pose.breakdown") - layout.operator("pose.blend_to_neighbour") + layout.operator("pose.blend_to_neighbor") class VIEW3D_MT_pose_propagate(Menu): @@ -3596,7 +3596,7 @@ class VIEW3D_MT_pose_context_menu(Menu): layout.operator("pose.push") layout.operator("pose.relax") layout.operator("pose.breakdown") - layout.operator("pose.blend_to_neighbour") + layout.operator("pose.blend_to_neighbor") layout.separator() diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 3819c0699f4..98e481e6ea8 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -1167,7 +1167,7 @@ void BKE_gpencil_editcurve_recalculate_handles(bGPDstroke *gps) bGPDcurve_point *gpc_pt = &gpc->curve_points[i]; bGPDcurve_point *gpc_pt_prev = &gpc->curve_points[i - 1]; bGPDcurve_point *gpc_pt_next = &gpc->curve_points[i + 1]; - /* update handle if point or neighbour is selected */ + /* update handle if point or neighbor is selected */ if (gpc_pt->flag & GP_CURVE_POINT_SELECT || gpc_pt_prev->flag & GP_CURVE_POINT_SELECT || gpc_pt_next->flag & GP_CURVE_POINT_SELECT) { BezTriple *bezt = &gpc_pt->bezt; diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index c1a18196a3a..7d08e416d0d 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -564,9 +564,9 @@ static void nupdate_ak_masklayshape(ActKeyColumn *ak, void *data) using KeylistCreateColumnFunction = std::function; using KeylistUpdateColumnFunction = std::function; -/* `ED_keylist_find_neighbour_front_to_back` is called before the runtime can be initialized so we +/* `ED_keylist_find_neighbor_front_to_back` is called before the runtime can be initialized so we * cannot use bin searching. */ -static ActKeyColumn *ED_keylist_find_neighbour_front_to_back(ActKeyColumn *cursor, float cfra) +static ActKeyColumn *ED_keylist_find_neighbor_front_to_back(ActKeyColumn *cursor, float cfra) { while (cursor->next && cursor->next->cfra <= cfra) { cursor = cursor->next; @@ -574,9 +574,9 @@ static ActKeyColumn *ED_keylist_find_neighbour_front_to_back(ActKeyColumn *curso return cursor; } -/* `ED_keylist_find_neighbour_back_to_front` is called before the runtime can be initialized so we +/* `ED_keylist_find_neighbor_back_to_front` is called before the runtime can be initialized so we * cannot use bin searching. */ -static ActKeyColumn *ED_keylist_find_neighbour_back_to_front(ActKeyColumn *cursor, float cfra) +static ActKeyColumn *ED_keylist_find_neighbor_back_to_front(ActKeyColumn *cursor, float cfra) { while (cursor->prev && cursor->prev->cfra >= cfra) { cursor = cursor->prev; @@ -585,14 +585,14 @@ static ActKeyColumn *ED_keylist_find_neighbour_back_to_front(ActKeyColumn *curso } /* - * `ED_keylist_find_exact_or_neighbour_column` is called before the runtime can be initialized so + * `ED_keylist_find_exact_or_neighbor_column` is called before the runtime can be initialized so * we cannot use bin searching. * * This function is called to add or update columns in the keylist. * Typically columns are sorted by frame number so keeping track of the last_accessed_column * reduces searching. */ -static ActKeyColumn *ED_keylist_find_exact_or_neighbour_column(AnimKeylist *keylist, float cfra) +static ActKeyColumn *ED_keylist_find_exact_or_neighbor_column(AnimKeylist *keylist, float cfra) { BLI_assert(!keylist->is_runtime_initialized); if (ED_keylist_is_empty(keylist)) { @@ -604,10 +604,10 @@ static ActKeyColumn *ED_keylist_find_exact_or_neighbour_column(AnimKeylist *keyl if (!is_cfra_eq(cursor->cfra, cfra)) { const bool walking_direction_front_to_back = cursor->cfra <= cfra; if (walking_direction_front_to_back) { - cursor = ED_keylist_find_neighbour_front_to_back(cursor, cfra); + cursor = ED_keylist_find_neighbor_front_to_back(cursor, cfra); } else { - cursor = ED_keylist_find_neighbour_back_to_front(cursor, cfra); + cursor = ED_keylist_find_neighbor_back_to_front(cursor, cfra); } } @@ -633,7 +633,7 @@ static void ED_keylist_add_or_update_column(AnimKeylist *keylist, return; } - ActKeyColumn *nearest = ED_keylist_find_exact_or_neighbour_column(keylist, cfra); + ActKeyColumn *nearest = ED_keylist_find_exact_or_neighbor_column(keylist, cfra); if (is_cfra_eq(nearest->cfra, cfra)) { update_func(nearest, userdata); } @@ -774,7 +774,7 @@ static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, co if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) { /* Backtrack to find the right location. */ if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) { - ActKeyColumn *newcol = ED_keylist_find_exact_or_neighbour_column(keylist, col->cfra); + ActKeyColumn *newcol = ED_keylist_find_exact_or_neighbor_column(keylist, col->cfra); BLI_assert(newcol); BLI_assert(newcol->cfra == col->cfra); diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 696355324e6..3a6761ba915 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -216,7 +216,7 @@ void POSE_OT_relax(struct wmOperatorType *ot); void POSE_OT_push_rest(struct wmOperatorType *ot); void POSE_OT_relax_rest(struct wmOperatorType *ot); void POSE_OT_breakdown(struct wmOperatorType *ot); -void POSE_OT_blend_to_neighbours(struct wmOperatorType *ot); +void POSE_OT_blend_to_neighbors(struct wmOperatorType *ot); void POSE_OT_propagate(struct wmOperatorType *ot); diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index a1070a8823a..75b0455d026 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -150,7 +150,7 @@ void ED_operatortypes_armature(void) WM_operatortype_append(POSE_OT_push_rest); WM_operatortype_append(POSE_OT_relax_rest); WM_operatortype_append(POSE_OT_breakdown); - WM_operatortype_append(POSE_OT_blend_to_neighbours); + WM_operatortype_append(POSE_OT_blend_to_neighbors); } void ED_operatormacros_armature(void) diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index b273d3aac76..ed8a35f779b 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -914,7 +914,7 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) strcpy(mode_str, TIP_("Breakdown")); break; case POSESLIDE_BLEND: - strcpy(mode_str, TIP_("Blend To Neighbour")); + strcpy(mode_str, TIP_("Blend To Neighbor")); break; default: @@ -1709,7 +1709,7 @@ void POSE_OT_breakdown(wmOperatorType *ot) } /* ........................ */ -static int pose_slide_blend_to_neighbours_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int pose_slide_blend_to_neighbors_invoke(bContext *C, wmOperator *op, const wmEvent *event) { /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_BLEND) == 0) { @@ -1721,7 +1721,7 @@ static int pose_slide_blend_to_neighbours_invoke(bContext *C, wmOperator *op, co return pose_slide_invoke_common(C, op, event); } -static int pose_slide_blend_to_neighbours_exec(bContext *C, wmOperator *op) +static int pose_slide_blend_to_neighbors_exec(bContext *C, wmOperator *op) { tPoseSlideOp *pso; @@ -1737,16 +1737,16 @@ static int pose_slide_blend_to_neighbours_exec(bContext *C, wmOperator *op) return pose_slide_exec_common(C, op, pso); } -void POSE_OT_blend_to_neighbours(wmOperatorType *ot) +void POSE_OT_blend_to_neighbors(wmOperatorType *ot) { /* Identifiers. */ - ot->name = "Blend To Neighbour"; - ot->idname = "POSE_OT_blend_to_neighbour"; + ot->name = "Blend To Neighbor"; + ot->idname = "POSE_OT_blend_to_neighbor"; ot->description = "Blend from current position to previous or next keyframe"; /* Callbacks. */ - ot->exec = pose_slide_blend_to_neighbours_exec; - ot->invoke = pose_slide_blend_to_neighbours_invoke; + ot->exec = pose_slide_blend_to_neighbors_exec; + ot->invoke = pose_slide_blend_to_neighbors_invoke; ot->modal = pose_slide_modal; ot->cancel = pose_slide_cancel; ot->poll = ED_operator_posemode; diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 675cff3e58c..631d5822c5e 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -2943,7 +2943,7 @@ static void rna_def_modifier_gpencilweight_angle(BlenderRNA *brna) }; srna = RNA_def_struct(brna, "WeightAngleGpencilModifier", "GpencilModifier"); - RNA_def_struct_ui_text(srna, "Weight Modifier Amgle", "Calculate Vertex Weight dynamically"); + RNA_def_struct_ui_text(srna, "Weight Modifier Angle", "Calculate Vertex Weight dynamically"); RNA_def_struct_sdna(srna, "WeightAngleGpencilModifierData"); RNA_def_struct_ui_icon(srna, ICON_MOD_VERTEX_WEIGHT); @@ -3589,7 +3589,7 @@ static void rna_def_modifier_gpencildash(BlenderRNA *brna) prop = RNA_def_property(srna, "segment_active_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Active Dash Segement Index", "Active index in the segment list"); + RNA_def_property_ui_text(prop, "Active Dash Segment Index", "Active index in the segment list"); prop = RNA_def_property(srna, "dash_offset", PROP_INT, PROP_NONE); RNA_def_property_ui_text( From 37003cbbc12d30e6a5cf4bc425a34c232a24fe66 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 4 Oct 2021 11:53:46 +0200 Subject: [PATCH 0487/1500] Fix T91867: Error reading tiles with Persistent Data ON --- intern/cycles/blender/blender_session.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 3be7ff32bd8..3ba5258ad93 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -504,6 +504,10 @@ void BlenderSession::render_frame_finish() /* Clear driver. */ session->set_output_driver(nullptr); session->full_buffer_written_cb = function_null; + + /* All the files are handled. + * Clear the list so that this session can be re-used by Persistent Data. */ + full_buffer_files_.clear(); } static PassType bake_type_to_pass(const string &bake_type_str, const int bake_filter) From f2b86471eaa48f09f534195c7b1095f85e2b7ff8 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 4 Oct 2021 11:53:12 +0200 Subject: [PATCH 0488/1500] Fix session uuid ghash comparison return value Because of legacy reasons (C string compare function returning 0 when strings are equal), the ghash compare function is expected to return false when hashes are equal. --- source/blender/blenlib/intern/session_uuid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/intern/session_uuid.c b/source/blender/blenlib/intern/session_uuid.c index 8ed96f02149..ac15a400a92 100644 --- a/source/blender/blenlib/intern/session_uuid.c +++ b/source/blender/blenlib/intern/session_uuid.c @@ -74,5 +74,5 @@ bool BLI_session_uuid_ghash_compare(const void *lhs_v, const void *rhs_v) { const SessionUUID *lhs = (const SessionUUID *)lhs_v; const SessionUUID *rhs = (const SessionUUID *)rhs_v; - return BLI_session_uuid_is_equal(lhs, rhs); + return !BLI_session_uuid_is_equal(lhs, rhs); } From ce6a24976a38be5aa8fba346424e3caae2b2f9ae Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 4 Oct 2021 12:01:57 +0200 Subject: [PATCH 0489/1500] Cleanup: Redundant space at the end of comment --- intern/cycles/blender/blender_session.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 3ba5258ad93..0bee265557f 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -506,7 +506,7 @@ void BlenderSession::render_frame_finish() session->full_buffer_written_cb = function_null; /* All the files are handled. - * Clear the list so that this session can be re-used by Persistent Data. */ + * Clear the list so that this session can be re-used by Persistent Data. */ full_buffer_files_.clear(); } From 23d9953c807f3376212b1a0a31f7a7669e9eb1ac Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 4 Oct 2021 12:16:55 +0200 Subject: [PATCH 0490/1500] I18n tools: Fix issue when extracting messages on release builds. This was also affecting prototype of buildbot-driven UI messages extraction... --- .../modules/bl_i18n_utils/bl_extract_messages.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 3355e9075a0..6c9c212387e 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -378,7 +378,15 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): if cls in blacklist_rna_class: return cls.__name__ cls_id = "" - bl_rna = cls.bl_rna + bl_rna = getattr(cls, "bl_rna", None) + # It seems that py-defined 'wrappers' RNA classes (like `MeshEdge` in `bpy_types.py`) need to be accessed + # once from `bpy.types` before they have a valid `bl_rna` member. + # Weirdly enough, this is only triggered on release builds, debug builds somehow do not have that issue. + if bl_rna is None: + if getattr(bpy.types, cls.__name__, None) is not None: + bl_rna = getattr(cls, "bl_rna", None) + if bl_rna is None: + raise TypeError("Unknown RNA class") while bl_rna: cls_id = bl_rna.identifier + "." + cls_id bl_rna = bl_rna.base From e62ce9e08e919f25aad444f378947f6be932730f Mon Sep 17 00:00:00 2001 From: Simon Lenz Date: Mon, 4 Oct 2021 12:21:20 +0200 Subject: [PATCH 0491/1500] Fix camera border bug in passepartout render view {F10761402} With active viewport render from camera view, the camera border shows up, even when passepartout and overlays are disabled. By moving the line-drawing code to the passepartout section, it is effectively disabled when passepartout is off. Reviewed By: sebastian_k Differential Revision: https://developer.blender.org/D12745 --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- source/blender/editors/space_view3d/view3d_draw.c | 5 ++--- source/tools | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/release/datafiles/locale b/release/datafiles/locale index 4833954c0ac..8ce0741c51a 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 4833954c0ac85cc407e1d5a153aa11b1d1823ec0 +Subproject commit 8ce0741c51afec6a12b78c1ce21a7779e1f51c69 diff --git a/release/scripts/addons b/release/scripts/addons index f86f25e6221..c0942837ab5 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit f86f25e62217264495d05f116ccb09d575fe9841 +Subproject commit c0942837ab5a48482b1e901712f24cb1e1f04fac diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 5a82baad9f9..42da56aa737 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 5a82baad9f986722104280e8354a4427d8e9eab1 +Subproject commit 42da56aa73726710107031787af5eea186797984 diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 86f79718a68..79148035a25 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -608,11 +608,10 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, } GPU_blend(GPU_BLEND_NONE); + immUniformThemeColor3(TH_BACK); + imm_draw_box_wire_2d(shdr_pos, x1i, y1i, x2i, y2i); } - immUniformThemeColor3(TH_BACK); - imm_draw_box_wire_2d(shdr_pos, x1i, y1i, x2i, y2i); - #ifdef VIEW3D_CAMERA_BORDER_HACK if (view3d_camera_border_hack_test == true) { immUniformColor3ubv(view3d_camera_border_hack_col); diff --git a/source/tools b/source/tools index 01f51a0e551..3f12d0ae601 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 01f51a0e551ab730f0934dc6488613690ac4bf8f +Subproject commit 3f12d0ae601a5632d971f711c355e58bd28d21dc From dc4c2815a6f14eca60b2d5fdb3ec309a950bb0fc Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 4 Oct 2021 12:40:18 +0200 Subject: [PATCH 0492/1500] Cleanup: Reverting submodules hash This partially reverts commit e62ce9e08e919f25aad444f378947f6be932730f. --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- source/tools | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/release/datafiles/locale b/release/datafiles/locale index 8ce0741c51a..4833954c0ac 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 8ce0741c51afec6a12b78c1ce21a7779e1f51c69 +Subproject commit 4833954c0ac85cc407e1d5a153aa11b1d1823ec0 diff --git a/release/scripts/addons b/release/scripts/addons index c0942837ab5..f86f25e6221 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit c0942837ab5a48482b1e901712f24cb1e1f04fac +Subproject commit f86f25e62217264495d05f116ccb09d575fe9841 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 42da56aa737..5a82baad9f9 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 42da56aa73726710107031787af5eea186797984 +Subproject commit 5a82baad9f986722104280e8354a4427d8e9eab1 diff --git a/source/tools b/source/tools index 3f12d0ae601..01f51a0e551 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 3f12d0ae601a5632d971f711c355e58bd28d21dc +Subproject commit 01f51a0e551ab730f0934dc6488613690ac4bf8f From 8ca7250982087c137370f72c6b2de91c7ce09e91 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 4 Oct 2021 12:09:59 +0200 Subject: [PATCH 0493/1500] Fix T91911: error in image dithering code after recent changes Thanks to Patrik Olsson for spotting this. --- source/blender/blenlib/intern/math_color_inline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c index 8b3e3b11cff..24c4143e587 100644 --- a/source/blender/blenlib/intern/math_color_inline.c +++ b/source/blender/blenlib/intern/math_color_inline.c @@ -329,7 +329,7 @@ MINLINE float dither_random_value(float s, float t) hash0 -= floorf(hash0); hash1 -= floorf(hash1); /* Convert uniform distribution into triangle-shaped distribution. */ - return hash0 + hash0 - 0.5f; + return hash0 + hash1 - 0.5f; } MINLINE void float_to_byte_dither_v3( From 76238af213d44349821612db6a43f3a89d9be5f4 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 4 Oct 2021 12:07:55 +0200 Subject: [PATCH 0494/1500] Fix Cycles render time pass being available in UI, but it was removed This previously only work for CPU rendering, and isn't that practical to get working in the new architecture. --- intern/cycles/blender/addon/engine.py | 1 - intern/cycles/blender/addon/properties.py | 6 ------ intern/cycles/blender/addon/ui.py | 1 - intern/cycles/blender/blender_sync.cpp | 6 ------ intern/cycles/integrator/pass_accessor.cpp | 3 --- intern/cycles/kernel/kernel_types.h | 1 - intern/cycles/render/film.cpp | 2 -- intern/cycles/render/pass.cpp | 5 ----- 8 files changed, 25 deletions(-) diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index d729cb1ee69..c402df12ba9 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -211,7 +211,6 @@ def list_render_passes(scene, srl): if crl.use_pass_shadow_catcher: yield ("Shadow Catcher", "RGB", 'COLOR') # Debug passes. - if crl.pass_debug_render_time: yield ("Debug Render Time", "X", 'VALUE') if crl.pass_debug_sample_count: yield ("Debug Sample Count", "X", 'VALUE') # Cryptomatte passes. diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index cea70033784..8c1f26d7b9f 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1197,12 +1197,6 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup): class CyclesRenderLayerSettings(bpy.types.PropertyGroup): - pass_debug_render_time: BoolProperty( - name="Debug Render Time", - description="Render time in milliseconds per sample and pixel", - default=False, - update=update_render_passes, - ) pass_debug_sample_count: BoolProperty( name="Debug Sample Count", description="Number of samples/camera rays per pixel", diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index c4a1844480c..55782088444 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -792,7 +792,6 @@ class CYCLES_RENDER_PT_passes_data(CyclesButtonsPanel, Panel): col.prop(view_layer, "use_pass_material_index") col = layout.column(heading="Debug", align=True) - col.prop(cycles_view_layer, "pass_debug_render_time", text="Render Time") col.prop(cycles_view_layer, "pass_debug_sample_count", text="Sample Count") layout.prop(view_layer, "pass_alpha_threshold") diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 717f301b03e..9f5bbddbe77 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -545,8 +545,6 @@ static PassType get_blender_pass_type(BL::RenderPass &b_pass) MAP_PASS("Shadow Catcher", PASS_SHADOW_CATCHER); MAP_PASS("Noisy Shadow Catcher", PASS_SHADOW_CATCHER); - MAP_PASS("Debug Render Time", PASS_RENDER_TIME); - MAP_PASS("AdaptiveAuxBuffer", PASS_ADAPTIVE_AUX_BUFFER); MAP_PASS("Debug Sample Count", PASS_SAMPLE_COUNT); @@ -604,10 +602,6 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles"); /* Debug passes. */ - if (get_boolean(crl, "pass_debug_render_time")) { - b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str()); - pass_add(scene, PASS_RENDER_TIME, "Debug Render Time"); - } if (get_boolean(crl, "pass_debug_sample_count")) { b_engine.add_pass("Debug Sample Count", 1, "X", b_view_layer.name().c_str()); pass_add(scene, PASS_SAMPLE_COUNT, "Debug Sample Count"); diff --git a/intern/cycles/integrator/pass_accessor.cpp b/intern/cycles/integrator/pass_accessor.cpp index 87c048b1fa5..4f76f1fa9df 100644 --- a/intern/cycles/integrator/pass_accessor.cpp +++ b/intern/cycles/integrator/pass_accessor.cpp @@ -149,9 +149,6 @@ bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers, /* Denoised passes store their final pixels, no need in special calculation. */ get_pass_float(render_buffers, buffer_params, destination); } - else if (type == PASS_RENDER_TIME) { - /* TODO(sergey): Needs implementation. */ - } else if (type == PASS_DEPTH) { get_pass_depth(render_buffers, buffer_params, destination); } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 3cc42bf7a85..1a986c58878 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -360,7 +360,6 @@ typedef enum PassType { PASS_MATERIAL_ID, PASS_MOTION, PASS_MOTION_WEIGHT, - PASS_RENDER_TIME, PASS_CRYPTOMATTE, PASS_AOV_COLOR, PASS_AOV_VALUE, diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index ad3336ca089..48f87ea3bf7 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -326,8 +326,6 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) kfilm->pass_bake_differential = kfilm->pass_stride; break; - case PASS_RENDER_TIME: - break; case PASS_CRYPTOMATTE: kfilm->pass_cryptomatte = have_cryptomatte ? min(kfilm->pass_cryptomatte, kfilm->pass_stride) : diff --git a/intern/cycles/render/pass.cpp b/intern/cycles/render/pass.cpp index 27ad7c0db97..472c9fc0a82 100644 --- a/intern/cycles/render/pass.cpp +++ b/intern/cycles/render/pass.cpp @@ -89,7 +89,6 @@ const NodeEnum *Pass::get_type_enum() pass_type_enum.insert("material_id", PASS_MATERIAL_ID); pass_type_enum.insert("motion", PASS_MOTION); pass_type_enum.insert("motion_weight", PASS_MOTION_WEIGHT); - pass_type_enum.insert("render_time", PASS_RENDER_TIME); pass_type_enum.insert("cryptomatte", PASS_CRYPTOMATTE); pass_type_enum.insert("aov_color", PASS_AOV_COLOR); pass_type_enum.insert("aov_value", PASS_AOV_VALUE); @@ -217,10 +216,6 @@ PassInfo Pass::get_info(const PassType type, const bool include_albedo) pass_info.num_components = 3; pass_info.use_exposure = false; break; - case PASS_RENDER_TIME: - /* This pass is handled entirely on the host side. */ - pass_info.num_components = 0; - break; case PASS_DIFFUSE_COLOR: case PASS_GLOSSY_COLOR: From a80a2f07b7f824383b8df03f11055fc1bf6a6bd9 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 4 Oct 2021 12:19:18 +0200 Subject: [PATCH 0495/1500] Fix T90815: wrong Cycles OSL normal map render after recent optimization --- intern/cycles/kernel/osl/osl_services.cpp | 13 ++++++++++++- intern/cycles/kernel/osl/osl_services.h | 1 + intern/cycles/kernel/shaders/node_normal_map.osl | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 4fc46a255a8..2c7f5eb4948 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -110,6 +110,7 @@ ustring OSLRenderServices::u_curve_thickness("geom:curve_thickness"); ustring OSLRenderServices::u_curve_length("geom:curve_length"); ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal"); ustring OSLRenderServices::u_curve_random("geom:curve_random"); +ustring OSLRenderServices::u_normal_map_normal("geom:normal_map_normal"); ustring OSLRenderServices::u_path_ray_length("path:ray_length"); ustring OSLRenderServices::u_path_ray_depth("path:ray_depth"); ustring OSLRenderServices::u_path_diffuse_depth("path:diffuse_depth"); @@ -985,8 +986,18 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobals *kg, float3 f = curve_tangent_normal(kg, sd); return set_attribute_float3(f, type, derivatives, val); } - else + else if (name == u_normal_map_normal) { + if (sd->type & PRIMITIVE_ALL_TRIANGLE) { + float3 f = triangle_smooth_normal_unnormalized(kg, sd, sd->Ng, sd->prim, sd->u, sd->v); + return set_attribute_float3(f, type, derivatives, val); + } + else { + return false; + } + } + else { return false; + } } bool OSLRenderServices::get_background_attribute(const KernelGlobals *kg, diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/osl_services.h index 2a5400282b3..a9671485eda 100644 --- a/intern/cycles/kernel/osl/osl_services.h +++ b/intern/cycles/kernel/osl/osl_services.h @@ -297,6 +297,7 @@ class OSLRenderServices : public OSL::RendererServices { static ustring u_curve_length; static ustring u_curve_tangent_normal; static ustring u_curve_random; + static ustring u_normal_map_normal; static ustring u_path_ray_length; static ustring u_path_ray_depth; static ustring u_path_diffuse_depth; diff --git a/intern/cycles/kernel/shaders/node_normal_map.osl b/intern/cycles/kernel/shaders/node_normal_map.osl index 6d4780f6dae..7a94ad8ad1a 100644 --- a/intern/cycles/kernel/shaders/node_normal_map.osl +++ b/intern/cycles/kernel/shaders/node_normal_map.osl @@ -45,7 +45,7 @@ shader node_normal_map(normal NormalIn = N, // get _unnormalized_ interpolated normal and tangent if (getattribute(attr_name, tangent) && getattribute(attr_sign_name, tangent_sign) && - (!is_smooth || getattribute("geom:N", ninterp))) { + (!is_smooth || getattribute("geom:normal_map_normal", ninterp))) { // apply normal map vector B = tangent_sign * cross(ninterp, tangent); Normal = normalize(mcolor[0] * tangent + mcolor[1] * B + mcolor[2] * ninterp); From 326bd76d3b6d75d34fa3a6313ff0db374446d4ab Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 4 Oct 2021 13:50:29 +0200 Subject: [PATCH 0496/1500] Fix T89759: baking normals does not take into account mirror modifier --- source/blender/editors/object/object_bake_api.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 26f5b21a311..475e0e581fb 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -1532,22 +1532,22 @@ static int bake(const BakeAPIRender *bkr, if (md) { mode = md->mode; md->mode &= ~eModifierMode_Render; - } - /* Evaluate modifiers again. */ - me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false, false); - bake_targets_populate_pixels(bkr, &targets, ob_low, me_nores, pixel_array_low); + /* Evaluate modifiers again. */ + me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false, false); + bake_targets_populate_pixels(bkr, &targets, ob_low, me_nores, pixel_array_low); + } RE_bake_normal_world_to_tangent(pixel_array_low, targets.num_pixels, targets.num_channels, targets.result, - me_nores, + (me_nores) ? me_nores : me_low_eval, bkr->normal_swizzle, ob_low_eval->obmat); - BKE_id_free(NULL, &me_nores->id); if (md) { + BKE_id_free(NULL, &me_nores->id); md->mode = mode; } } From fc4886a31426b6fe40982f5288c8d129a1ce3223 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 4 Oct 2021 12:28:28 +0200 Subject: [PATCH 0497/1500] Fix T91894: Cycles baking normal maps of transformed objects not working --- intern/cycles/kernel/geom/geom_shader_data.h | 2 +- .../integrator/integrator_init_from_bake.h | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/intern/cycles/kernel/geom/geom_shader_data.h b/intern/cycles/kernel/geom/geom_shader_data.h index fb2cb5cb1ea..0e373c10086 100644 --- a/intern/cycles/kernel/geom/geom_shader_data.h +++ b/intern/cycles/kernel/geom/geom_shader_data.h @@ -103,7 +103,7 @@ ccl_device_inline void shader_setup_from_ray(const KernelGlobals *ccl_restrict k sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; - if (isect->object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { /* instance transform */ object_normal_transform_auto(kg, sd, &sd->N); object_normal_transform_auto(kg, sd, &sd->Ng); diff --git a/intern/cycles/kernel/integrator/integrator_init_from_bake.h b/intern/cycles/kernel/integrator/integrator_init_from_bake.h index 96db606cee1..6e4e1be55fa 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_bake.h +++ b/intern/cycles/kernel/integrator/integrator_init_from_bake.h @@ -109,9 +109,17 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, } /* Position and normal on triangle. */ + const int object = kernel_data.bake.object_index; float3 P, Ng; int shader; - triangle_point_normal(kg, kernel_data.bake.object_index, prim, u, v, &P, &Ng, &shader); + triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader); + + const int object_flag = kernel_tex_fetch(__object_flag, object); + if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); + P = transform_point_auto(&tfm, P); + } + if (kernel_data.film.pass_background != PASS_UNUSED) { /* Environment baking. */ @@ -130,8 +138,13 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, } else { /* Surface baking. */ - const float3 N = (shader & SHADER_SMOOTH_NORMAL) ? triangle_smooth_normal(kg, Ng, prim, u, v) : - Ng; + float3 N = (shader & SHADER_SMOOTH_NORMAL) ? triangle_smooth_normal(kg, Ng, prim, u, v) : Ng; + + if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + Transform itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); + N = normalize(transform_direction_transposed(&itfm, N)); + Ng = normalize(transform_direction_transposed(&itfm, Ng)); + } /* Setup ray. */ Ray ray ccl_optional_struct_init; @@ -143,6 +156,12 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, /* Setup differentials. */ float3 dPdu, dPdv; triangle_dPdudv(kg, prim, &dPdu, &dPdv); + if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); + dPdu = transform_direction(&tfm, dPdu); + dPdv = transform_direction(&tfm, dPdv); + } + differential3 dP; dP.dx = dPdu * dudx + dPdv * dvdx; dP.dy = dPdu * dudy + dPdv * dvdy; From f806bd8261092334035e9cf1aaf0174285c3344f Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 4 Oct 2021 12:57:21 +0200 Subject: [PATCH 0498/1500] Fix T91861: Black environment behind shadow catcher Always sample background pass behind shadow catcher (if the pass exists, of course), regardless of whether shadow catcher will be used as approximate or accurate. Allows to combine accurate shadows into an environment map. Differential Revision: https://developer.blender.org/D12747 --- intern/cycles/kernel/integrator/integrator_intersect_closest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/integrator_intersect_closest.h index 34ca6814534..4e581df1870 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_closest.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_closest.h @@ -123,7 +123,7 @@ ccl_device_forceinline void integrator_intersect_shader_next_kernel( #ifdef __SHADOW_CATCHER__ const int object_flags = intersection_get_object_flags(kg, isect); if (kernel_shadow_catcher_split(INTEGRATOR_STATE_PASS, object_flags)) { - if (kernel_data.film.use_approximate_shadow_catcher && !kernel_data.background.transparent) { + if (kernel_data.film.pass_background != PASS_UNUSED && !kernel_data.background.transparent) { INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_SHADOW_CATCHER_BACKGROUND; if (use_raytrace_kernel) { From cc636db8f2ff2e14b912e8c3998b5042f59c5a42 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 4 Oct 2021 15:21:25 +0200 Subject: [PATCH 0499/1500] Fix T91823: Regression not showing idblocks when recursion is set to `Blend file` Introduced by fc7beac8d6f4. During code review it wasn't clear why this branch was needed, so we removed it. Now it is clear why it is needed so we added it back and added a comment why the branch is needed. Patch provided by @Severin. --- source/blender/editors/space_file/filelist.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index b58a04d6d4f..a927b62fd6e 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -3295,6 +3295,7 @@ typedef struct FileListReadJob { } FileListReadJob; static bool filelist_readjob_should_recurse_into_entry(const int max_recursion, + const bool is_lib, const int current_recursion_level, FileListInternEntry *entry) { @@ -3302,10 +3303,16 @@ static bool filelist_readjob_should_recurse_into_entry(const int max_recursion, /* Recursive loading is disabled. */ return false; } - if (current_recursion_level >= max_recursion) { + if (!is_lib && current_recursion_level > max_recursion) { /* No more levels of recursion left. */ return false; } + /* Show entries when recursion is set to `Blend file` even when `current_recursion_level` exceeds + * `max_recursion`. */ + if (!is_lib && (current_recursion_level >= max_recursion) && + ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) { + return false; + } if (entry->typeflag & FILE_TYPE_BLENDERLIB) { /* Libraries are already loaded recursively when recursive loaded is used. No need to add * them another time. This loading is done with the `LIST_LIB_RECURSIVE` option. */ @@ -3421,7 +3428,8 @@ static void filelist_readjob_do(const bool do_lib, entry->name = fileentry_uiname(root, entry->relpath, entry->typeflag, dir); entry->free_name = true; - if (filelist_readjob_should_recurse_into_entry(max_recursion, recursion_level, entry)) { + if (filelist_readjob_should_recurse_into_entry( + max_recursion, is_lib, recursion_level, entry)) { /* We have a directory we want to list, add it to todo list! */ BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath); BLI_path_normalize_dir(job_params->main_name, dir); From 48822086331c1913a4980a87ee82f14904068213 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 4 Oct 2021 14:58:52 +0200 Subject: [PATCH 0500/1500] Cleanup: Separate interface & implementation for asset catalog tree-view Should make the code a bit more organized and help getting an overview of the interfaces more quickly. --- .../space_file/asset_catalog_tree_view.cc | 251 ++++++++++-------- 1 file changed, 137 insertions(+), 114 deletions(-) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 92e4e668885..35107340cdb 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -74,6 +74,7 @@ class AssetCatalogTreeView : public ui::AbstractTreeView { void add_unassigned_item(); bool is_active_catalog(CatalogID catalog_id) const; }; + /* ---------------------------------------------------------------------- */ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { @@ -81,111 +82,19 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { AssetCatalogTreeItem &catalog_item_; public: - AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item) - : BasicTreeViewItem(catalog_item->get_name()), catalog_item_(*catalog_item) - { - } + AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item); - void on_activate() override - { - const AssetCatalogTreeView &tree_view = static_cast( - get_tree_view()); - tree_view.params_->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG; - tree_view.params_->catalog_id = catalog_item_.get_catalog_id(); - WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); - } + void on_activate() override; - void build_row(uiLayout &row) override - { - ui::BasicTreeViewItem::build_row(row); + void build_row(uiLayout &row) override; - if (!is_active()) { - return; - } + bool has_droppable_item(const wmDrag &drag) const; - PointerRNA *props; - const CatalogID catalog_id = catalog_item_.get_catalog_id(); - - props = UI_but_extra_operator_icon_add( - button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); - RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str()); - - /* Tree items without a catalog ID represent components of catalog paths that are not - * associated with an actual catalog. They exist merely by the presence of a child catalog, and - * thus cannot be deleted themselves. */ - if (!BLI_uuid_is_nil(catalog_id)) { - char catalog_id_str_buffer[UUID_STRING_LEN] = ""; - BLI_uuid_format(catalog_id_str_buffer, catalog_id); - - props = UI_but_extra_operator_icon_add( - button(), "ASSET_OT_catalog_delete", WM_OP_INVOKE_DEFAULT, ICON_X); - RNA_string_set(props, "catalog_id", catalog_id_str_buffer); - } - } - - bool has_droppable_item(const wmDrag &drag) const - { - const ListBase *asset_drags = WM_drag_asset_list_get(&drag); - - /* There needs to be at least one asset from the current file. */ - LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) { - if (!asset_item->is_external) { - return true; - } - } - return false; - } - - bool can_drop(const wmDrag &drag) const override - { - if (drag.type != WM_DRAG_ASSET_LIST) { - return false; - } - return has_droppable_item(drag); - } - - std::string drop_tooltip(const bContext & /*C*/, + bool can_drop(const wmDrag &drag) const override; + std::string drop_tooltip(const bContext &C, const wmDrag &drag, - const wmEvent & /*event*/) const override - { - const ListBase *asset_drags = WM_drag_asset_list_get(&drag); - const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); - - /* Don't try to be smart by dynamically adding the 's' for the plural. Just makes translation - * harder, so use full literals. */ - std::string basic_tip = is_multiple_assets ? TIP_("Move assets to catalog") : - TIP_("Move asset to catalog"); - - return basic_tip + ": " + catalog_item_.get_name() + " (" + - catalog_item_.catalog_path().str() + ")"; - } - - bool on_drop(const wmDrag &drag) override - { - const ListBase *asset_drags = WM_drag_asset_list_get(&drag); - if (!asset_drags) { - return false; - } - - const AssetCatalogTreeView &tree_view = static_cast( - get_tree_view()); - - LISTBASE_FOREACH (wmDragAssetListItem *, asset_item, asset_drags) { - if (asset_item->is_external) { - /* Only internal assets can be modified! */ - continue; - } - BKE_asset_metadata_catalog_id_set(asset_item->asset_data.local_id->asset_data, - catalog_item_.get_catalog_id(), - catalog_item_.get_simple_name().c_str()); - - /* Trigger re-run of filtering to update visible assets. */ - filelist_tag_needs_filtering(tree_view.space_file_.files); - file_select_deselect_all(&tree_view.space_file_, FILE_SEL_SELECTED | FILE_SEL_HIGHLIGHTED); - } - - return true; - } + const wmEvent &event) const override; + bool on_drop(const wmDrag &drag) override; }; /** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level @@ -193,22 +102,11 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { using BasicTreeViewItem::BasicTreeViewItem; - void build_row(uiLayout &row) override - { - ui::BasicTreeViewItem::build_row(row); - - if (!is_active()) { - return; - } - - PointerRNA *props; - props = UI_but_extra_operator_icon_add( - button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); - /* No parent path to use the root level. */ - RNA_string_set(props, "parent_path", nullptr); - } + void build_row(uiLayout &row) override; }; +/* ---------------------------------------------------------------------- */ + AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params, SpaceFile &space_file) @@ -283,6 +181,131 @@ bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const (params_->catalog_id == catalog_id); } +/* ---------------------------------------------------------------------- */ + +AssetCatalogTreeViewItem::AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item) + : BasicTreeViewItem(catalog_item->get_name()), catalog_item_(*catalog_item) +{ +} + +void AssetCatalogTreeViewItem::on_activate() +{ + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + tree_view.params_->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG; + tree_view.params_->catalog_id = catalog_item_.get_catalog_id(); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); +} + +void AssetCatalogTreeViewItem::build_row(uiLayout &row) +{ + ui::BasicTreeViewItem::build_row(row); + + if (!is_active()) { + return; + } + + PointerRNA *props; + const CatalogID catalog_id = catalog_item_.get_catalog_id(); + + props = UI_but_extra_operator_icon_add( + button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str()); + + /* Tree items without a catalog ID represent components of catalog paths that are not + * associated with an actual catalog. They exist merely by the presence of a child catalog, and + * thus cannot be deleted themselves. */ + if (!BLI_uuid_is_nil(catalog_id)) { + char catalog_id_str_buffer[UUID_STRING_LEN] = ""; + BLI_uuid_format(catalog_id_str_buffer, catalog_id); + + props = UI_but_extra_operator_icon_add( + button(), "ASSET_OT_catalog_delete", WM_OP_INVOKE_DEFAULT, ICON_X); + RNA_string_set(props, "catalog_id", catalog_id_str_buffer); + } +} + +bool AssetCatalogTreeViewItem::has_droppable_item(const wmDrag &drag) const +{ + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + + /* There needs to be at least one asset from the current file. */ + LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) { + if (!asset_item->is_external) { + return true; + } + } + return false; +} + +bool AssetCatalogTreeViewItem::can_drop(const wmDrag &drag) const +{ + if (drag.type != WM_DRAG_ASSET_LIST) { + return false; + } + return has_droppable_item(drag); +} + +std::string AssetCatalogTreeViewItem::drop_tooltip(const bContext & /*C*/, + const wmDrag &drag, + const wmEvent & /*event*/) const +{ + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); + + /* Don't try to be smart by dynamically adding the 's' for the plural. Just makes translation + * harder, so use full literals. */ + std::string basic_tip = is_multiple_assets ? TIP_("Move assets to catalog") : + TIP_("Move asset to catalog"); + + return basic_tip + ": " + catalog_item_.get_name() + " (" + catalog_item_.catalog_path().str() + + ")"; +} + +bool AssetCatalogTreeViewItem::on_drop(const wmDrag &drag) +{ + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + if (!asset_drags) { + return false; + } + + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + + LISTBASE_FOREACH (wmDragAssetListItem *, asset_item, asset_drags) { + if (asset_item->is_external) { + /* Only internal assets can be modified! */ + continue; + } + BKE_asset_metadata_catalog_id_set(asset_item->asset_data.local_id->asset_data, + catalog_item_.get_catalog_id(), + catalog_item_.get_simple_name().c_str()); + + /* Trigger re-run of filtering to update visible assets. */ + filelist_tag_needs_filtering(tree_view.space_file_.files); + file_select_deselect_all(&tree_view.space_file_, FILE_SEL_SELECTED | FILE_SEL_HIGHLIGHTED); + } + + return true; +} + +/* ---------------------------------------------------------------------- */ + +void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) +{ + ui::BasicTreeViewItem::build_row(row); + + if (!is_active()) { + return; + } + + PointerRNA *props; + props = UI_but_extra_operator_icon_add( + button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + /* No parent path to use the root level. */ + RNA_string_set(props, "parent_path", nullptr); +} + } // namespace blender::ed::asset_browser /* ---------------------------------------------------------------------- */ From b536605e78eec10ad03c0eccd6cfd6b36b9216cf Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 4 Oct 2021 15:27:26 +0200 Subject: [PATCH 0501/1500] Cleanup: Use static function for asset catalog tree-view helper --- .../blender/editors/space_file/asset_catalog_tree_view.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 35107340cdb..629e06e5e71 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -84,12 +84,12 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { public: AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item); + static bool has_droppable_item(const wmDrag &drag); + void on_activate() override; void build_row(uiLayout &row) override; - bool has_droppable_item(const wmDrag &drag) const; - bool can_drop(const wmDrag &drag) const override; std::string drop_tooltip(const bContext &C, const wmDrag &drag, @@ -225,7 +225,7 @@ void AssetCatalogTreeViewItem::build_row(uiLayout &row) } } -bool AssetCatalogTreeViewItem::has_droppable_item(const wmDrag &drag) const +bool AssetCatalogTreeViewItem::has_droppable_item(const wmDrag &drag) { const ListBase *asset_drags = WM_drag_asset_list_get(&drag); From 4a3464050c4e83d446d47c946e17b9540f5a3862 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 4 Oct 2021 16:05:14 +0200 Subject: [PATCH 0502/1500] Assets: Support dragging assets on "Unassigned" catalog Dragging assets onto the "Unassigned" catalog tree item will effectively move the assets out of any catalog. Technically this means unsetting the Catalog-ID stored in the asset metadata, or more precisely setting the UUID to be all zeros. --- .../space_file/asset_catalog_tree_view.cc | 66 ++++++++++++++++--- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 629e06e5e71..883bc6d7890 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -85,6 +85,10 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item); static bool has_droppable_item(const wmDrag &drag); + static bool drop_into_catalog(const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name = ""); void on_activate() override; @@ -105,6 +109,16 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { void build_row(uiLayout &row) override; }; +class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { + using BasicTreeViewItem::BasicTreeViewItem; + + bool can_drop(const wmDrag &drag) const override; + std::string drop_tooltip(const bContext &C, + const wmDrag &drag, + const wmEvent &event) const override; + bool on_drop(const wmDrag &drag) override; +}; + /* ---------------------------------------------------------------------- */ AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library, @@ -165,7 +179,7 @@ void AssetCatalogTreeView::add_unassigned_item() { FileAssetSelectParams *params = params_; - ui::AbstractTreeViewItem &item = add_tree_item( + AssetCatalogTreeViewUnassignedItem &item = add_tree_item( IFACE_("Unassigned"), ICON_FILE_HIDDEN, [params](ui::BasicTreeViewItem & /*item*/) { params->asset_catalog_visibility = FILE_SHOW_ASSETS_WITHOUT_CATALOG; WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); @@ -262,24 +276,23 @@ std::string AssetCatalogTreeViewItem::drop_tooltip(const bContext & /*C*/, ")"; } -bool AssetCatalogTreeViewItem::on_drop(const wmDrag &drag) +bool AssetCatalogTreeViewItem::drop_into_catalog(const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name) { const ListBase *asset_drags = WM_drag_asset_list_get(&drag); if (!asset_drags) { return false; } - const AssetCatalogTreeView &tree_view = static_cast( - get_tree_view()); - LISTBASE_FOREACH (wmDragAssetListItem *, asset_item, asset_drags) { if (asset_item->is_external) { /* Only internal assets can be modified! */ continue; } - BKE_asset_metadata_catalog_id_set(asset_item->asset_data.local_id->asset_data, - catalog_item_.get_catalog_id(), - catalog_item_.get_simple_name().c_str()); + BKE_asset_metadata_catalog_id_set( + asset_item->asset_data.local_id->asset_data, catalog_id, simple_name.c_str()); /* Trigger re-run of filtering to update visible assets. */ filelist_tag_needs_filtering(tree_view.space_file_.files); @@ -289,6 +302,14 @@ bool AssetCatalogTreeViewItem::on_drop(const wmDrag &drag) return true; } +bool AssetCatalogTreeViewItem::on_drop(const wmDrag &drag) +{ + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + return drop_into_catalog( + tree_view, drag, catalog_item_.get_catalog_id(), catalog_item_.get_simple_name()); +} + /* ---------------------------------------------------------------------- */ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) @@ -306,6 +327,35 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) RNA_string_set(props, "parent_path", nullptr); } +/* ---------------------------------------------------------------------- */ + +bool AssetCatalogTreeViewUnassignedItem::can_drop(const wmDrag &drag) const +{ + if (drag.type != WM_DRAG_ASSET_LIST) { + return false; + } + return AssetCatalogTreeViewItem::has_droppable_item(drag); +} + +std::string AssetCatalogTreeViewUnassignedItem::drop_tooltip(const bContext & /*C*/, + const wmDrag &drag, + const wmEvent & /*event*/) const +{ + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); + + return is_multiple_assets ? TIP_("Move assets out of any catalog") : + TIP_("Move asset out of any catalog"); +} + +bool AssetCatalogTreeViewUnassignedItem::on_drop(const wmDrag &drag) +{ + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + /* Assign to nil catalog ID. */ + return AssetCatalogTreeViewItem::drop_into_catalog(tree_view, drag, CatalogID{}); +} + } // namespace blender::ed::asset_browser /* ---------------------------------------------------------------------- */ From 0bc40564558af74aaf9f1eb7c17d622c9b84a50d Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 4 Oct 2021 10:34:45 -0400 Subject: [PATCH 0503/1500] Nodes: Revert some socket declarations to previos API See T91826, there is a bug in the code where both `.` and `_` are used as a seperator for `BLI_uniquename_cb`. This resulted in some nodes becoming disconnected on file load. Until this is resolved, the chnages are reverted to prevent data loss. --- .../nodes/node_composite_alphaOver.cc | 25 ++++++++-------- .../composite/nodes/node_composite_math.cc | 19 +++++------- .../composite/nodes/node_composite_mixrgb.cc | 23 +++++++------- .../nodes/node_composite_splitViewer.cc | 17 ++++------- .../nodes/node_composite_zcombine.cc | 30 +++++++++---------- 5 files changed, 50 insertions(+), 64 deletions(-) diff --git a/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc index 6210d946bc7..edb451a3654 100644 --- a/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc +++ b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc @@ -20,22 +20,21 @@ /** \file * \ingroup cmpnodes */ - + #include "node_composite_util.hh" /* **************** ALPHAOVER ******************** */ -namespace blender::nodes { - -static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b) -{ - b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Image"); -} - -} // namespace blender::nodes +static bNodeSocketTemplate cmp_node_alphaover_in[] = { + {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, + {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, + {-1, ""}, +}; +static bNodeSocketTemplate cmp_node_alphaover_out[] = { + {SOCK_RGBA, N_("Image")}, + {-1, ""}, +}; static void node_alphaover_init(bNodeTree *UNUSED(ntree), bNode *node) { @@ -47,7 +46,7 @@ void register_node_type_cmp_alphaover(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_ALPHAOVER, "Alpha Over", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_alphaover_declare; + node_type_socket_templates(&ntype, cmp_node_alphaover_in, cmp_node_alphaover_out); node_type_init(&ntype, node_alphaover_init); node_type_storage( &ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_math.cc b/source/blender/nodes/composite/nodes/node_composite_math.cc index a9859425e28..ecddcc2ad32 100644 --- a/source/blender/nodes/composite/nodes/node_composite_math.cc +++ b/source/blender/nodes/composite/nodes/node_composite_math.cc @@ -24,25 +24,20 @@ #include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ +static bNodeSocketTemplate cmp_node_math_in[] = { + {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {-1, ""}}; -namespace blender::nodes { - -static void cmp_node_math_declare(NodeDeclarationBuilder &b) -{ - b.add_input("Value").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_input("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_input("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_output("Value"); -} - -} // namespace blender::nodes +static bNodeSocketTemplate cmp_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; void register_node_type_cmp_math(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::cmp_node_math_declare; + node_type_socket_templates(&ntype, cmp_node_math_in, cmp_node_math_out); node_type_label(&ntype, node_math_label); node_type_update(&ntype, node_math_update); diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc index 4f2a9c2717c..557116f5b7a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc @@ -25,17 +25,16 @@ /* **************** MIX RGB ******************** */ -namespace blender::nodes { - -static void cmp_node_mixrgb_declare(NodeDeclarationBuilder &b) -{ - b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Image"); -} - -} // namespace blender::nodes +static bNodeSocketTemplate cmp_node_mix_rgb_in[] = { + {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, + {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, + {-1, ""}, +}; +static bNodeSocketTemplate cmp_node_mix_rgb_out[] = { + {SOCK_RGBA, N_("Image")}, + {-1, ""}, +}; /* custom1 = mix type */ void register_node_type_cmp_mix_rgb(void) @@ -43,7 +42,7 @@ void register_node_type_cmp_mix_rgb(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, NODE_PREVIEW); - ntype.declare = blender::nodes::cmp_node_mixrgb_declare; + node_type_socket_templates(&ntype, cmp_node_mix_rgb_in, cmp_node_mix_rgb_out); node_type_label(&ntype, node_blend_label); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc index f64abe87116..54a1c59fca6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc @@ -27,16 +27,11 @@ #include "BKE_image.h" /* **************** SPLIT VIEWER ******************** */ - -namespace blender::nodes { - -static void cmp_node_splitviewer_declare(NodeDeclarationBuilder &b) -{ - b.add_input("Image"); - b.add_input("Image", "Image_001"); -} - -} // namespace blender::nodes +static bNodeSocketTemplate cmp_node_splitviewer_in[] = { + {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, + {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, + {-1, ""}, +}; static void node_composit_init_splitviewer(bNodeTree *UNUSED(ntree), bNode *node) { @@ -55,7 +50,7 @@ void register_node_type_cmp_splitviewer(void) cmp_node_type_base( &ntype, CMP_NODE_SPLITVIEWER, "Split Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW); - ntype.declare = blender::nodes::cmp_node_splitviewer_declare; + node_type_socket_templates(&ntype, cmp_node_splitviewer_in, nullptr); node_type_init(&ntype, node_composit_init_splitviewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc index 79e4d449159..990f9fcd366 100644 --- a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc +++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc @@ -24,28 +24,26 @@ #include "node_composite_util.hh" /* **************** Z COMBINE ******************** */ - -namespace blender::nodes { - -static void cmp_node_zcombine_declare(NodeDeclarationBuilder &b) -{ - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Z").default_value(1.0f).min(0.0f).max(10000.0f); - b.add_input("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Z", "Z_001").default_value(1.0f).min(0.0f).max(10000.0f); - b.add_output("Image"); - b.add_output("Z"); -} - -} // namespace blender::nodes - /* lazy coder NOTE: node->custom2 is abused to send signal. */ +static bNodeSocketTemplate cmp_node_zcombine_in[] = { + {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, + {SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_NONE}, + {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, + {SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_NONE}, + {-1, ""}, +}; +static bNodeSocketTemplate cmp_node_zcombine_out[] = { + {SOCK_RGBA, N_("Image")}, + {SOCK_FLOAT, N_("Z")}, + {-1, ""}, +}; + void register_node_type_cmp_zcombine(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_ZCOMBINE, "Z Combine", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_zcombine_declare; + node_type_socket_templates(&ntype, cmp_node_zcombine_in, cmp_node_zcombine_out); nodeRegisterType(&ntype); } From 4fd7ce321d7352570a5cbb08d66b1c635d234689 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 4 Oct 2021 16:42:56 +0200 Subject: [PATCH 0504/1500] GPencil: Remove unused flag This flag was used in older versions, but now is not used anymore. --- source/blender/editors/gpencil/gpencil_paint.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 957d8087bbd..bf49203f915 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -109,7 +109,6 @@ typedef enum eGP_StrokeAdd_Result { /* Runtime flags */ typedef enum eGPencil_PaintFlags { GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ - GP_PAINTFLAG_STROKEADDED = (1 << 1), GP_PAINTFLAG_SELECTMASK = (1 << 3), GP_PAINTFLAG_HARD_ERASER = (1 << 4), GP_PAINTFLAG_STROKE_ERASER = (1 << 5), @@ -285,7 +284,6 @@ static void gpencil_update_cache(bGPdata *gpd) static void gpencil_stroke_added_enable(tGPsdata *p) { BLI_assert(p->gpf->strokes.last != NULL); - p->flags |= GP_PAINTFLAG_STROKEADDED; /* drawing batch cache is dirty now */ gpencil_update_cache(p->gpd); From 2b6f2072f1eecd43080821bb7b55f5313f890ddb Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 4 Oct 2021 16:17:59 +0200 Subject: [PATCH 0505/1500] UI Tree-View API: Enforce active item to be un-collapsed Makes sure that the active item of a tree never has collapsed parent items, which can be confusing if it happens. E.g. for the asset catalogs UI, the active catalog decides which assets are visible. Having it hidden while being the main factor deciding which assets are visible is quite confusing. I think it makes sense to have this at the UI Tree-View level, rather than doing it manually in the asset catalog code for example. Seems like something you'd commonly want. We can make it optional in the API if needed. Renamed the `set_active()` function to make clear that it is more than a mere setter. --- .../blender/editors/include/UI_tree_view.hh | 6 +++- source/blender/editors/interface/tree_view.cc | 32 +++++++++++++++---- .../space_file/asset_catalog_tree_view.cc | 6 ++-- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index d36e688dd65..a82aae021f2 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -211,12 +211,16 @@ class AbstractTreeViewItem : public TreeViewItemContainer { const AbstractTreeView &get_tree_view() const; int count_parents() const; - void set_active(bool value = true); + /** Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate() + * function and ensures this item's parents are not collapsed (so the item is visible). */ + void activate(); + void deactivate(); bool is_active() const; void toggle_collapsed(); bool is_collapsed() const; void set_collapsed(bool collapsed); bool is_collapsible() const; + void ensure_parents_uncollapsed(); }; /** \} */ diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 0ea15a2a5bb..202e3dba2ab 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -184,14 +184,25 @@ int AbstractTreeViewItem::count_parents() const return i; } -void AbstractTreeViewItem::set_active(bool value) +void AbstractTreeViewItem::activate() { - if (value && !is_active()) { - /* Deactivate other items in the tree. */ - get_tree_view().foreach_item([](auto &item) { item.set_active(false); }); - on_activate(); + if (is_active()) { + return; } - is_active_ = value; + + /* Deactivate other items in the tree. */ + get_tree_view().foreach_item([](auto &item) { item.deactivate(); }); + + on_activate(); + /* Make sure the active item is always visible. */ + ensure_parents_uncollapsed(); + + is_active_ = true; +} + +void AbstractTreeViewItem::deactivate() +{ + is_active_ = false; } bool AbstractTreeViewItem::is_active() const @@ -219,6 +230,13 @@ bool AbstractTreeViewItem::is_collapsible() const return !children_.is_empty(); } +void AbstractTreeViewItem::ensure_parents_uncollapsed() +{ + for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) { + parent->set_collapsed(false); + } +} + /* ---------------------------------------------------------------------- */ TreeViewBuilder::TreeViewBuilder(uiBlock &block) : block_(block) @@ -277,7 +295,7 @@ static void tree_row_click_fn(struct bContext *UNUSED(C), void *but_arg1, void * if (tree_item.is_collapsed() || tree_item.is_active()) { tree_item.toggle_collapsed(); } - tree_item.set_active(); + tree_item.activate(); } void BasicTreeViewItem::build_row(uiLayout &row) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 883bc6d7890..657881bdb40 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -152,7 +152,7 @@ ui::BasicTreeViewItem &AssetCatalogTreeView::build_catalog_items_recursive( ui::BasicTreeViewItem &view_item = view_parent_item.add_tree_item( &catalog); if (is_active_catalog(catalog.get_catalog_id())) { - view_item.set_active(); + view_item.activate(); } catalog.foreach_child([&view_item, this](AssetCatalogTreeItem &child) { @@ -171,7 +171,7 @@ void AssetCatalogTreeView::add_all_item() WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); }); if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS) { - item.set_active(); + item.activate(); } } @@ -185,7 +185,7 @@ void AssetCatalogTreeView::add_unassigned_item() WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); }); if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG) { - item.set_active(); + item.activate(); } } From f2c896a9ad171e20dc291b60ed4c96a4e6a9456f Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 4 Oct 2021 17:08:00 +0200 Subject: [PATCH 0506/1500] GPencil: Simplify code removing extra function The function was not doing anything and only was calling another function. --- source/blender/editors/gpencil/gpencil_paint.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index bf49203f915..ffc50a86fc6 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -281,14 +281,6 @@ static void gpencil_update_cache(bGPdata *gpd) } } -static void gpencil_stroke_added_enable(tGPsdata *p) -{ - BLI_assert(p->gpf->strokes.last != NULL); - - /* drawing batch cache is dirty now */ - gpencil_update_cache(p->gpd); -} - /* ------ */ /* Forward defines for some functions... */ @@ -1322,7 +1314,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) BKE_gpencil_stroke_copy_to_keyframes(gpd, gpl, p->gpf, gps, tail); } - gpencil_stroke_added_enable(p); + gpencil_update_cache(p->gpd); } /* --- 'Eraser' for 'Paint' Tool ------ */ From 301ee97b935376027503338977b3e59d8a0103e2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 4 Oct 2021 11:10:08 -0500 Subject: [PATCH 0507/1500] Fix: Unable to select left and right in set handle type node The "enum" RNA flag was missing. --- source/blender/makesrna/intern/rna_nodetree.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 625a5fb5606..ddd2a3fcee0 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9667,6 +9667,7 @@ static void def_geo_curve_set_handles(StructRNA *srna) prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } From 076d797bda67efea6c977d424f0f89bec69fbadd Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Mon, 4 Oct 2021 12:01:29 -0500 Subject: [PATCH 0508/1500] Geometry Nodes: Curve Trim Node Update This update allows the Trim Curve node to use float field inputs for the start and end inputs. These fields are evaluated on the spline domain. Differential Revision: https://developer.blender.org/D12744 --- .../geometry/nodes/node_geo_curve_trim.cc | 54 +++++++++++++------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 97043980899..4303999f79c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -29,10 +29,19 @@ namespace blender::nodes { static void geo_node_curve_trim_declare(NodeDeclarationBuilder &b) { b.add_input("Curve"); - b.add_input("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("End").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); - b.add_input("Start", "Start_001").min(0.0f).subtype(PROP_DISTANCE); - b.add_input("End", "End_001").min(0.0f).default_value(1.0f).subtype(PROP_DISTANCE); + b.add_input("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); + b.add_input("End") + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR) + .supports_field(); + b.add_input("Start", "Start_001").min(0.0f).subtype(PROP_DISTANCE).supports_field(); + b.add_input("End", "End_001") + .min(0.0f) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .supports_field(); b.add_output("Curve"); } @@ -322,13 +331,24 @@ static void trim_bezier_spline(Spline &spline, static void geometry_set_curve_trim(GeometrySet &geometry_set, const GeometryNodeCurveSampleMode mode, - const float start, - const float end) + Field &start_field, + Field &end_field) { if (!geometry_set.has_curve()) { return; } + CurveComponent &component = geometry_set.get_component_for_write(); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(start_field); + evaluator.add(end_field); + evaluator.evaluate(); + const blender::VArray &starts = evaluator.get_evaluated(0); + const blender::VArray &ends = evaluator.get_evaluated(1); + CurveEval &curve = *geometry_set.get_curve_for_write(); MutableSpan splines = curve.splines(); @@ -343,19 +363,19 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, /* Return a spline with one point instead of implicitly * reversing the spline or switching the parameters. */ - if (end < start) { + if (ends[i] < starts[i]) { spline.resize(1); continue; } const Spline::LookupResult start_lookup = (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) ? - spline.lookup_evaluated_length(std::clamp(start, 0.0f, spline.length())) : - spline.lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)); + spline.lookup_evaluated_length(std::clamp(starts[i], 0.0f, spline.length())) : + spline.lookup_evaluated_factor(std::clamp(starts[i], 0.0f, 1.0f)); const Spline::LookupResult end_lookup = (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) ? - spline.lookup_evaluated_length(std::clamp(end, 0.0f, spline.length())) : - spline.lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f)); + spline.lookup_evaluated_length(std::clamp(ends[i], 0.0f, spline.length())) : + spline.lookup_evaluated_factor(std::clamp(ends[i], 0.0f, 1.0f)); switch (spline.type()) { case Spline::Type::Bezier: @@ -382,17 +402,17 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Curve"); if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) { - const float start = params.extract_input("Start"); - const float end = params.extract_input("End"); + Field start_field = params.extract_input>("Start"); + Field end_field = params.extract_input>("End"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_curve_trim(geometry_set, mode, start, end); + geometry_set_curve_trim(geometry_set, mode, start_field, end_field); }); } else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - const float start = params.extract_input("Start_001"); - const float end = params.extract_input("End_001"); + Field start_field = params.extract_input>("Start_001"); + Field end_field = params.extract_input>("End_001"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_curve_trim(geometry_set, mode, start, end); + geometry_set_curve_trim(geometry_set, mode, start_field, end_field); }); } From 65b5023df457d1277588c554a1abc4bdb7eeab6e Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Mon, 4 Oct 2021 12:47:35 -0500 Subject: [PATCH 0509/1500] Fix: Geometry Nodes Handle Type Selection Fix Fix the selection logic on the Handle Type Selection node to work as intended: (Left is Selected AND Left is ChosenType) OR (Right is Selected AND Right is ChosenType) --- .../nodes/node_geo_curve_handle_type_selection.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index 093146d563e..a40e839cc02 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -79,12 +79,10 @@ static void select_by_handle_type(const CurveEval &curve, else { BezierSpline *b = static_cast(spline.get()); for (int i : IndexRange(b->size())) { - if (mode & GeometryNodeCurveHandleMode::GEO_NODE_CURVE_HANDLE_LEFT) { - r_selection[offset++] = b->handle_types_left()[i] == type; - } - else if (mode & GeometryNodeCurveHandleMode::GEO_NODE_CURVE_HANDLE_RIGHT) { - r_selection[offset++] = b->handle_types_right()[i] == type; - } + r_selection[offset++] = (mode & GeometryNodeCurveHandleMode::GEO_NODE_CURVE_HANDLE_LEFT && + b->handle_types_left()[i] == type) || + (mode & GeometryNodeCurveHandleMode::GEO_NODE_CURVE_HANDLE_RIGHT && + b->handle_types_right()[i] == type); } } } From ffa20de0508591dae31fbb56abd55fd44e6deb4c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 4 Oct 2021 13:03:13 -0500 Subject: [PATCH 0510/1500] Cleanup: Remove unused variable and include --- .../nodes/node_geo_curve_handle_type_selection.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index a40e839cc02..b565b1e4602 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -14,8 +14,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BLI_task.hh" - #include "BKE_spline.hh" #include "UI_interface.h" @@ -67,7 +65,6 @@ static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCu static void select_by_handle_type(const CurveEval &curve, const BezierSpline::HandleType type, const GeometryNodeCurveHandleMode mode, - const IndexMask UNUSED(mask), const MutableSpan r_selection) { int offset = 0; @@ -79,9 +76,9 @@ static void select_by_handle_type(const CurveEval &curve, else { BezierSpline *b = static_cast(spline.get()); for (int i : IndexRange(b->size())) { - r_selection[offset++] = (mode & GeometryNodeCurveHandleMode::GEO_NODE_CURVE_HANDLE_LEFT && + r_selection[offset++] = (mode & GEO_NODE_CURVE_HANDLE_LEFT && b->handle_types_left()[i] == type) || - (mode & GeometryNodeCurveHandleMode::GEO_NODE_CURVE_HANDLE_RIGHT && + (mode & GEO_NODE_CURVE_HANDLE_RIGHT && b->handle_types_right()[i] == type); } } @@ -119,7 +116,7 @@ class HandleTypeFieldInput final : public fn::FieldInput { if (domain == ATTR_DOMAIN_POINT) { Array selection(mask.min_array_size()); - select_by_handle_type(*curve, type_, mode_, mask, selection); + select_by_handle_type(*curve, type_, mode_, selection); return &scope.construct>>(std::move(selection)); } } From 3391a2ef1d27dcf1ec1851eefc74e77114e6b493 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 4 Oct 2021 19:46:15 +0200 Subject: [PATCH 0511/1500] Assets: Show all assets indirectly nested inside the active catalog The asset catalog design was always that the active catalog would also display all assets of its child catalogs (or grand-childs, etc.). This is one of the main characteristics that differentiates catalogs from usual directories. Sybren prepared this on the asset catalog backend side with 56ce51d1f75a. This integrates it into the Asset Browser backend and the UI. --- .../space_file/asset_catalog_tree_view.cc | 91 +++++++++++++++++++ .../blender/editors/space_file/file_intern.h | 22 +++++ source/blender/editors/space_file/filelist.c | 66 +++++++------- 3 files changed, 148 insertions(+), 31 deletions(-) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 657881bdb40..9d6af5136d9 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -358,6 +358,97 @@ bool AssetCatalogTreeViewUnassignedItem::on_drop(const wmDrag &drag) } // namespace blender::ed::asset_browser +namespace blender::ed::asset_browser { + +class AssetCatalogFilterSettings { + public: + eFileSel_Params_AssetCatalogVisibility asset_catalog_visibility; + bUUID asset_catalog_id; + + std::unique_ptr catalog_filter; +}; + +} // namespace blender::ed::asset_browser + +using namespace blender::ed::asset_browser; + +FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings() +{ + AssetCatalogFilterSettings *filter_settings = OBJECT_GUARDED_NEW(AssetCatalogFilterSettings); + return reinterpret_cast(filter_settings); +} + +void file_delete_asset_catalog_filter_settings( + FileAssetCatalogFilterSettingsHandle **filter_settings_handle) +{ + AssetCatalogFilterSettings **filter_settings = reinterpret_cast( + filter_settings_handle); + OBJECT_GUARDED_SAFE_DELETE(*filter_settings, AssetCatalogFilterSettings); +} + +/** + * \return True if the file list should update its filtered results (e.g. because filtering + * parameters changed). + */ +bool file_set_asset_catalog_filter_settings( + FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + eFileSel_Params_AssetCatalogVisibility catalog_visibility, + ::bUUID catalog_id) +{ + AssetCatalogFilterSettings *filter_settings = reinterpret_cast( + filter_settings_handle); + bool needs_update = false; + + if (filter_settings->asset_catalog_visibility != catalog_visibility) { + filter_settings->asset_catalog_visibility = catalog_visibility; + needs_update = true; + } + + if (filter_settings->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG && + !BLI_uuid_equal(filter_settings->asset_catalog_id, catalog_id)) { + filter_settings->asset_catalog_id = catalog_id; + needs_update = true; + } + + return needs_update; +} + +void file_ensure_updated_catalog_filter_data( + FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + const ::AssetLibrary *asset_library) +{ + AssetCatalogFilterSettings *filter_settings = reinterpret_cast( + filter_settings_handle); + const AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service( + asset_library); + + if (filter_settings->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG) { + filter_settings->catalog_filter = std::make_unique( + catalog_service->create_catalog_filter(filter_settings->asset_catalog_id)); + } +} + +bool file_is_asset_visible_in_catalog_filter_settings( + const FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + const AssetMetaData *asset_data) +{ + const AssetCatalogFilterSettings *filter_settings = + reinterpret_cast(filter_settings_handle); + + switch (filter_settings->asset_catalog_visibility) { + case FILE_SHOW_ASSETS_WITHOUT_CATALOG: + return BLI_uuid_is_nil(asset_data->catalog_id); + case FILE_SHOW_ASSETS_FROM_CATALOG: + return filter_settings->catalog_filter->contains(asset_data->catalog_id); + case FILE_SHOW_ASSETS_ALL_CATALOGS: + /* All asset files should be visible. */ + return true; + } + + BLI_assert_unreachable(); + return false; +} + /* ---------------------------------------------------------------------- */ void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library, diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index c8609f48adb..ba08777e4e2 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -23,6 +23,8 @@ #pragma once +#include "DNA_space_types.h" + #ifdef __cplusplus extern "C" { #endif @@ -163,6 +165,26 @@ void file_path_to_ui_path(const char *path, char *r_pathi, int max_size); /* asset_catalog_tree_view.cc */ +/* C-handle for #ed::asset_browser::AssetCatalogFilterSettings. */ +typedef struct FileAssetCatalogFilterSettingsHandle FileAssetCatalogFilterSettingsHandle; + +FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings(void); +void file_delete_asset_catalog_filter_settings( + FileAssetCatalogFilterSettingsHandle **filter_settings_handle); +/** + * \return True if the stored filter settings were modified. + */ +bool file_set_asset_catalog_filter_settings( + FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + eFileSel_Params_AssetCatalogVisibility catalog_visibility, + bUUID catalog_id); +void file_ensure_updated_catalog_filter_data( + FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + const struct AssetLibrary *asset_library); +bool file_is_asset_visible_in_catalog_filter_settings( + const FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + const AssetMetaData *asset_data); + void file_create_asset_catalog_tree_view_in_layout(struct AssetLibrary *asset_library, struct uiLayout *layout, struct SpaceFile *space_file, diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index a927b62fd6e..773a321da5c 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -89,6 +89,7 @@ #include "atomic_ops.h" +#include "file_intern.h" #include "filelist.h" #define FILEDIR_NBR_ENTRIES_UNSET -1 @@ -371,8 +372,7 @@ typedef struct FileListFilter { char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */ short flags; - eFileSel_Params_AssetCatalogVisibility asset_catalog_visibility; - bUUID asset_catalog_id; + FileAssetCatalogFilterSettingsHandle *asset_catalog_filter; } FileListFilter; /* FileListFilter.flags */ @@ -426,6 +426,8 @@ typedef struct FileList { /* Filter an entry of current filelist. */ bool (*filter_fn)(struct FileListInternEntry *, const char *, FileListFilter *); + /* Executed before filtering individual items, to set up additional filter data. */ + void (*prepare_filter_fn)(const struct FileList *, FileListFilter *); short tags; /* FileListTags */ } FileList; @@ -906,27 +908,26 @@ static AssetMetaData *filelist_file_internal_get_asset_data(const FileListIntern return local_id ? local_id->asset_data : file->imported_asset_data; } -static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter) +static void prepare_filter_asset_library(const FileList *filelist, FileListFilter *filter) { - const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file); - bool is_visible = false; - - switch (filter->asset_catalog_visibility) { - case FILE_SHOW_ASSETS_WITHOUT_CATALOG: - is_visible = BLI_uuid_is_nil(asset_data->catalog_id); - break; - case FILE_SHOW_ASSETS_FROM_CATALOG: - /* TODO show all assets that are in child catalogs of the selected catalog. */ - is_visible = !BLI_uuid_is_nil(filter->asset_catalog_id) && - BLI_uuid_equal(filter->asset_catalog_id, asset_data->catalog_id); - break; - case FILE_SHOW_ASSETS_ALL_CATALOGS: - /* All asset files should be visible. */ - is_visible = true; - break; + /* Not used yet for the asset view template. */ + if (!filter->asset_catalog_filter) { + return; } - return is_visible; + file_ensure_updated_catalog_filter_data(filter->asset_catalog_filter, filelist->asset_library); +} + +static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter) +{ + /* Not used yet for the asset view template. */ + if (!filter->asset_catalog_filter) { + return true; + } + + const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file); + return file_is_asset_visible_in_catalog_filter_settings(filter->asset_catalog_filter, + asset_data); } static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) @@ -999,6 +1000,10 @@ void filelist_filter(FileList *filelist) } } + if (filelist->prepare_filter_fn) { + filelist->prepare_filter_fn(filelist, &filelist->filter_data); + } + filtered_tmp = MEM_mallocN(sizeof(*filtered_tmp) * (size_t)num_files, __func__); /* Filter remap & count how many files are left after filter in a single loop. */ @@ -1090,20 +1095,15 @@ void filelist_set_asset_catalog_filter_options( eFileSel_Params_AssetCatalogVisibility catalog_visibility, const bUUID *catalog_id) { - bool update = false; - - if (filelist->filter_data.asset_catalog_visibility != catalog_visibility) { - filelist->filter_data.asset_catalog_visibility = catalog_visibility; - update = true; + if (!filelist->filter_data.asset_catalog_filter) { + /* There's no filter data yet. */ + filelist->filter_data.asset_catalog_filter = file_create_asset_catalog_filter_settings(); } - if (filelist->filter_data.asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG && - catalog_id && !BLI_uuid_equal(filelist->filter_data.asset_catalog_id, *catalog_id)) { - filelist->filter_data.asset_catalog_id = *catalog_id; - update = true; - } + const bool needs_update = file_set_asset_catalog_filter_settings( + filelist->filter_data.asset_catalog_filter, catalog_visibility, *catalog_id); - if (update) { + if (needs_update) { filelist_tag_needs_filtering(filelist); } } @@ -1803,11 +1803,13 @@ void filelist_settype(FileList *filelist, short type) case FILE_ASSET_LIBRARY: filelist->check_dir_fn = filelist_checkdir_lib; filelist->read_job_fn = filelist_readjob_asset_library; + filelist->prepare_filter_fn = prepare_filter_asset_library; filelist->filter_fn = is_filtered_asset_library; break; case FILE_MAIN_ASSET: filelist->check_dir_fn = filelist_checkdir_main_assets; filelist->read_job_fn = filelist_readjob_main_assets; + filelist->prepare_filter_fn = prepare_filter_asset_library; filelist->filter_fn = is_filtered_main_assets; filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA | FILELIST_TAGS_NO_THREADS; break; @@ -1873,6 +1875,7 @@ void filelist_free(struct FileList *filelist) filelist->selection_state = NULL; } + file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter); MEM_SAFE_FREE(filelist->asset_library_ref); memset(&filelist->filter_data, 0, sizeof(filelist->filter_data)); @@ -3612,6 +3615,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache)); flrj->tmp_filelist->selection_state = NULL; flrj->tmp_filelist->asset_library_ref = NULL; + flrj->tmp_filelist->filter_data.asset_catalog_filter = NULL; BLI_mutex_unlock(&flrj->lock); From c8d59b60b5aa277a63277a39e794590eafa2e998 Mon Sep 17 00:00:00 2001 From: Gaia Clary Date: Wed, 29 Sep 2021 16:08:49 +0200 Subject: [PATCH 0512/1500] Make keyframe inserts/removals less verbose when called from python. Following operators now only report messages back when they are called via their invoke-methods: - ANIM_OT_keyframe_insert - ANIM_OT_keyframe_insert_by_name - ANIM_OT_keyframe_insert_menu - ANIM_OT_keyframe_delete - ANIM_OT_keyframe_clear_v3d - ANIM_OT_keyframe_delete_v3d Also removed the attribute confirm_success from the following operators: - ANIM_OT_keyframe_insert - ANIM_OT_keyframe_insert_by_name - ANIM_OT_keyframe_insert_menu - ANIM_OT_keyframe_delete - ANIM_OT_keyframe_delete_by_name Note: addons/scripts possibly need to be updated if they use the above operators AND set the "confirm_success" attribute Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D12697 --- .../scripts/startup/bl_operators/rigidbody.py | 2 +- source/blender/editors/animation/keyframing.c | 113 +++++------------- 2 files changed, 34 insertions(+), 81 deletions(-) diff --git a/release/scripts/startup/bl_operators/rigidbody.py b/release/scripts/startup/bl_operators/rigidbody.py index 7f5edac4dfb..1ef92a90754 100644 --- a/release/scripts/startup/bl_operators/rigidbody.py +++ b/release/scripts/startup/bl_operators/rigidbody.py @@ -176,7 +176,7 @@ class BakeToKeyframes(Operator): # NOTE: assume that on first frame, the starting rotation is appropriate obj.rotation_euler = mat.to_euler(rot_mode, obj.rotation_euler) - bpy.ops.anim.keyframe_insert(type='BUILTIN_KSI_LocRot', confirm_success=False) + bpy.ops.anim.keyframe_insert(type='BUILTIN_KSI_LocRot') # remove baked objects from simulation bpy.ops.rigidbody.objects_remove() diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 1ef7ee755ea..95df8b69cd4 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -1879,6 +1879,7 @@ static int insert_key_exec(bContext *C, wmOperator *op) float cfra = (float)CFRA; /* XXX for now, don't bother about all the yucky offset crap */ int num_channels; + const bool confirm = op->flag & OP_IS_INVOKE; KeyingSet *ks = keyingset_get_from_op_with_error(op, op->type->prop, scene); if (ks == NULL) { @@ -1915,20 +1916,22 @@ static int insert_key_exec(bContext *C, wmOperator *op) } if (num_channels > 0) { - /* if the appropriate properties have been set, make a note that we've inserted something */ - if (RNA_boolean_get(op->ptr, "confirm_success")) { + /* send notifiers that keyframes have been changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); + } + + if (confirm) { + /* if called by invoke (from the UI), make a note that we've inserted keyframes */ + if (num_channels > 0) { BKE_reportf(op->reports, RPT_INFO, "Successfully added %d keyframes for keying set '%s'", num_channels, ks->name); } - - /* send notifiers that keyframes have been changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); - } - else { - BKE_report(op->reports, RPT_WARNING, "Keying set failed to insert any keyframes"); + else { + BKE_report(op->reports, RPT_WARNING, "Keying set failed to insert any keyframes"); + } } return OPERATOR_FINISHED; @@ -1957,16 +1960,6 @@ void ANIM_OT_keyframe_insert(wmOperatorType *ot) RNA_def_enum_funcs(prop, ANIM_keying_sets_enum_itemf); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; - - /* confirm whether a keyframe was added by showing a popup - * - by default, this is enabled, since this operator is assumed to be called independently - */ - prop = RNA_def_boolean(ot->srna, - "confirm_success", - 1, - "Confirm Successful Insert", - "Show a popup when the keyframes get successfully added"); - RNA_def_property_flag(prop, PROP_HIDDEN); } /* Clone of 'ANIM_OT_keyframe_insert' which uses a name for the keying set instead of an enum. */ @@ -1990,16 +1983,6 @@ void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot) prop = RNA_def_string_file_path(ot->srna, "type", "Type", MAX_ID_NAME - 2, "", ""); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; - - /* confirm whether a keyframe was added by showing a popup - * - by default, this is enabled, since this operator is assumed to be called independently - */ - prop = RNA_def_boolean(ot->srna, - "confirm_success", - 1, - "Confirm Successful Insert", - "Show a popup when the keyframes get successfully added"); - RNA_def_property_flag(prop, PROP_HIDDEN); } /* Insert Key Operator (With Menu) ------------------------ */ @@ -2027,8 +2010,6 @@ static int insert_key_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UN /* just call the exec() on the active keyingset */ RNA_enum_set(op->ptr, "type", 0); - RNA_boolean_set(op->ptr, "confirm_success", true); - return op->type->exec(C, op); } @@ -2057,17 +2038,6 @@ void ANIM_OT_keyframe_insert_menu(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; - /* confirm whether a keyframe was added by showing a popup - * - by default, this is disabled so that if a menu is shown, this doesn't come up too - */ - /* XXX should this just be always on? */ - prop = RNA_def_boolean(ot->srna, - "confirm_success", - 0, - "Confirm Successful Insert", - "Show a popup when the keyframes get successfully added"); - RNA_def_property_flag(prop, PROP_HIDDEN); - /* whether the menu should always be shown * - by default, the menu should only be shown when there is no active Keying Set (2.5 behavior), * although in some cases it might be useful to always shown (pre 2.5 behavior) @@ -2094,6 +2064,7 @@ static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *k Scene *scene = CTX_data_scene(C); float cfra = (float)CFRA; /* XXX for now, don't bother about all the yucky offset crap */ int num_channels; + const bool confirm = op->flag & OP_IS_INVOKE; /* try to delete keyframes for the channels specified by KeyingSet */ num_channels = ANIM_apply_keyingset(C, NULL, NULL, ks, MODIFYKEY_MODE_DELETE, cfra); @@ -2107,26 +2078,23 @@ static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *k return OPERATOR_CANCELLED; } - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "confirm_success"); - bool confirm = (prop != NULL && RNA_property_boolean_get(op->ptr, prop)); - if (num_channels > 0) { - /* if the appropriate properties have been set, make a note that we've inserted something */ - if (confirm) { + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL); + } + + if (confirm) { + /* if called by invoke (from the UI), make a note that we've removed keyframes */ + if (num_channels > 0) { BKE_reportf(op->reports, RPT_INFO, "Successfully removed %d keyframes for keying set '%s'", num_channels, ks->name); } - - /* send notifiers that keyframes have been changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL); + else { + BKE_report(op->reports, RPT_WARNING, "Keying set failed to remove any keyframes"); + } } - else if (confirm) { - BKE_report(op->reports, RPT_WARNING, "Keying set failed to remove any keyframes"); - } - return OPERATOR_FINISHED; } @@ -2153,15 +2121,6 @@ void ANIM_OT_keyframe_delete(wmOperatorType *ot) RNA_def_enum_funcs(prop, ANIM_keying_sets_enum_itemf); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; - - /* confirm whether a keyframe was added by showing a popup - * - by default, this is enabled, since this operator is assumed to be called independently - */ - RNA_def_boolean(ot->srna, - "confirm_success", - 1, - "Confirm Successful Delete", - "Show a popup when the keyframes get successfully removed"); } void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot) @@ -2184,15 +2143,6 @@ void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot) prop = RNA_def_string_file_path(ot->srna, "type", "Type", MAX_ID_NAME - 2, "", ""); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; - - /* confirm whether a keyframe was added by showing a popup - * - by default, this is enabled, since this operator is assumed to be called independently - */ - RNA_def_boolean(ot->srna, - "confirm_success", - 1, - "Confirm Successful Delete", - "Show a popup when the keyframes get successfully removed"); } /* Delete Key Operator ------------------------ */ @@ -2291,7 +2241,7 @@ static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op) int selected_objects_success_len = 0; int success_multi = 0; - bool confirm = op->flag & OP_IS_INVOKE; + const bool confirm = op->flag & OP_IS_INVOKE; CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { ID *id = &ob->id; @@ -2372,21 +2322,24 @@ static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op) } CTX_DATA_END; - /* report success (or failure) */ if (selected_objects_success_len) { - if (confirm) { + /* send updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); + } + + if (confirm) { + /* if called by invoke (from the UI), make a note that we've removed keyframes */ + if (selected_objects_success_len) { BKE_reportf(op->reports, RPT_INFO, "%d object(s) successfully had %d keyframes removed", selected_objects_success_len, success_multi); } - /* send updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); - } - else if (confirm) { - BKE_reportf( - op->reports, RPT_ERROR, "No keyframes removed from %d object(s)", selected_objects_len); + else { + BKE_reportf( + op->reports, RPT_ERROR, "No keyframes removed from %d object(s)", selected_objects_len); + } } return OPERATOR_FINISHED; } From 655ce5dc3ed55358a72fc144a2095bb8ebdb053b Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 4 Oct 2021 21:57:29 +0200 Subject: [PATCH 0513/1500] Cleanup: Use LISTBASE_FOREACH macro --- source/blender/blenkernel/intern/gpencil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index ed84694a919..28598de69b8 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -1063,7 +1063,7 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src, /* copy frames */ BLI_listbase_clear(&gpl_dst->frames); if (dup_frames) { - for (gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { + LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { /* make a copy of source frame */ gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, dup_strokes); BLI_addtail(&gpl_dst->frames, gpf_dst); From d1ade756a965423398d462e28063f1fdcf98ba16 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 4 Oct 2021 21:59:20 +0200 Subject: [PATCH 0514/1500] Fix T91920: Missing decorate buttons in sound panel Add back decorate buttons, move mono and display waveforms to bottom as they were before. --- .../scripts/startup/bl_ui/space_sequencer.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 6430d6bab9b..4f76c645bcb 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1758,7 +1758,6 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): def draw(self, context): layout = self.layout - layout.use_property_split = False st = context.space_data overlay_settings = st.timeline_overlay @@ -1768,16 +1767,7 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): layout.active = not strip.mute if sound is not None: - col = layout.column() - - split = col.split(factor=0.4) - split.label(text="") - split.prop(sound, "use_mono") - if overlay_settings.waveform_display_type == 'DEFAULT_WAVEFORMS': - split = col.split(factor=0.4) - split.label(text="") - split.prop(strip, "show_waveform") - + layout.use_property_split = True col = layout.column() split = col.split(factor=0.4) @@ -1800,6 +1790,17 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): split.prop(strip, "pan", text=pan_text) split.enabled = sound.use_mono and audio_channels != 'MONO' + layout.use_property_split = False + col = layout.column() + + split = col.split(factor=0.4) + split.label(text="") + split.prop(sound, "use_mono") + if overlay_settings.waveform_display_type == 'DEFAULT_WAVEFORMS': + split = col.split(factor=0.4) + split.label(text="") + split.prop(strip, "show_waveform") + class SEQUENCER_PT_adjust_comp(SequencerButtonsPanel, Panel): From c38d2513c5db343314cf32f2ca2dc3396877df09 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 4 Oct 2021 22:17:05 +0200 Subject: [PATCH 0515/1500] Fix T91725: Waveforms are not displayed Use `sseq->timeline_overlay.flag` for `SEQ_TIMELINE_ALL_WAVEFORMS` instead of `sseq->flag`. --- source/blender/editors/space_sequencer/sequencer_draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index dc5e11b6998..d5cd5d11a35 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -350,7 +350,7 @@ static void draw_seq_waveform_overlay(View2D *v2d, float frames_per_pixel) { if (seq->sound && - ((sseq->flag & SEQ_TIMELINE_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { + ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid * flickering when moving around the strip. * To do this we figure out the fractional offset in pixel space by checking where the From 18959c502d62d3d51912bb3cf34ec6c8e8a3544a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 4 Oct 2021 15:40:09 -0500 Subject: [PATCH 0516/1500] Fix field type in curve resample node --- source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index e5be9b7a6f4..fe52d0e736c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -250,7 +250,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) mode_param.count.emplace(count); } else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { - Field resolution = params.extract_input>("Length"); + Field resolution = params.extract_input>("Length"); mode_param.length.emplace(resolution); } From 92c449776db8384c6555b21b135247ec4aa84e71 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 5 Oct 2021 10:59:48 +1100 Subject: [PATCH 0517/1500] Cleanup: quiet shadow warning, trailign space --- release/scripts/startup/bl_ui/space_sequencer.py | 2 +- source/blender/blenkernel/intern/gpencil.c | 1 - .../blender/nodes/composite/nodes/node_composite_alphaOver.cc | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 4f76c645bcb..b5904422beb 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1051,7 +1051,7 @@ class SequencerButtonsPanel_Output: class SequencerColorTagPicker: bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'UI' - + @staticmethod def has_sequencer(context): return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}) diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 28598de69b8..fa0741d3a2e 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -1044,7 +1044,6 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src, const bool dup_frames, const bool dup_strokes) { - const bGPDframe *gpf_src; bGPDframe *gpf_dst; bGPDlayer *gpl_dst; diff --git a/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc index edb451a3654..b6f64ed00c7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc +++ b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc @@ -20,7 +20,7 @@ /** \file * \ingroup cmpnodes */ - + #include "node_composite_util.hh" /* **************** ALPHAOVER ******************** */ From 2dace5f3ef54cc25ed31fe20fd33df727f10a9ac Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 5 Oct 2021 10:59:49 +1100 Subject: [PATCH 0518/1500] Cleanup: use 3D dot product (not 4D) when comparing pose-bone axes Also use more meaningful variable name. --- source/blender/blenkernel/intern/action_mirror.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c index 48472dfc9b3..cc3a15aa546 100644 --- a/source/blender/blenkernel/intern/action_mirror.c +++ b/source/blender/blenkernel/intern/action_mirror.c @@ -327,10 +327,10 @@ static void action_flip_pchan(Object *ob_arm, * the X-axis, it turns into a 180 degree rotation over the Y-axis. * This has only been observed with bones that can't be flipped, * hence the check for `pchan_flip`. */ - const float unit_x[4] = {1.0f, 0.0f, 0.0f, 0.0f}; - const bool is_problematic = pchan_flip == NULL && - fabsf(dot_v4v4(pchan->bone->arm_mat[0], unit_x)) <= 1e-6; - if (is_problematic) { + const float unit_x[3] = {1.0f, 0.0f, 0.0f}; + const bool is_x_axis_orthogonal = (pchan_flip == NULL) && + (fabsf(dot_v3v3(pchan->bone->arm_mat[0], unit_x)) <= 1e-6f); + if (is_x_axis_orthogonal) { /* Matrix needs to flip both the X and Z axes to come out right. */ float extra_mat[4][4] = { {-1.0f, 0.0f, 0.0f, 0.0f}, From 2b66b372bc39e12f938488a008f38b1945d86aa9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 5 Oct 2021 11:10:25 +1100 Subject: [PATCH 0519/1500] Cleanup: use doxygen sections --- .../blenkernel/BKE_attribute_access.hh | 26 ++++-- source/blender/blenlib/BLI_noise.hh | 18 ++-- source/blender/blenlib/BLI_resource_scope.hh | 8 +- source/blender/blenlib/BLI_string_ref.hh | 32 ++++--- source/blender/functions/FN_field.hh | 32 ++++--- .../functions/FN_multi_function_procedure.hh | 64 +++++++++----- .../functions/intern/generic_virtual_array.cc | 87 ++++++++++++------- source/blender/gpu/intern/gpu_select.c | 30 +++++-- source/blender/gpu/intern/gpu_select_pick.c | 46 ++++++---- source/blender/nodes/NOD_derived_node_tree.hh | 43 +++++---- source/blender/nodes/NOD_multi_function.hh | 16 ++-- source/blender/nodes/NOD_node_declaration.hh | 40 +++++---- source/blender/nodes/NOD_node_tree_ref.hh | 56 +++++++----- .../blender/nodes/NOD_socket_declarations.hh | 58 ++++++++----- .../nodes/intern/node_socket_declarations.cc | 64 +++++++++----- 15 files changed, 392 insertions(+), 228 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 20b48160feb..ff2aebc7d10 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -373,9 +373,9 @@ class CustomDataAttributes { const AttributeDomain domain) const; }; -/* -------------------------------------------------------------------- - * #AttributeIDRef inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #AttributeIDRef Inline Methods + * \{ */ inline AttributeIDRef::AttributeIDRef() = default; @@ -438,9 +438,11 @@ inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const return *anonymous_id_; } -/* -------------------------------------------------------------------- - * #OutputAttribute inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #OutputAttribute Inline Methods + * \{ */ inline OutputAttribute::OutputAttribute() = default; inline OutputAttribute::OutputAttribute(OutputAttribute &&other) = default; @@ -496,11 +498,15 @@ template inline MutableSpan OutputAttribute::as_span() return this->as_span().typed(); } +/** \} */ + } // namespace blender::bke -/* -------------------------------------------------------------------- - * Extern template instantiations that are defined in `intern/extern_implementations.cc`. - */ +/* -------------------------------------------------------------------- */ +/** \name External Template Instantiations + * + * Defined in `intern/extern_implementations.cc`. + * \{ */ namespace blender::bke { extern template class OutputAttribute_Typed; @@ -509,3 +515,5 @@ extern template class OutputAttribute_Typed; extern template class OutputAttribute_Typed; extern template class OutputAttribute_Typed; } // namespace blender::bke + +/** \} */ diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh index 7e1655f7864..839bee0f2f4 100644 --- a/source/blender/blenlib/BLI_noise.hh +++ b/source/blender/blenlib/BLI_noise.hh @@ -22,12 +22,12 @@ namespace blender::noise { -/* -------------------------------------------------------------------- - * Hash functions. - +/* -------------------------------------------------------------------- */ +/** \name Hash Functions + * * Create a randomized hash from the given inputs. Contrary to hash functions in `BLI_hash.hh` * these functions produce better randomness but are more expensive to compute. - */ + * \{ */ /* Hash integers to `uint32_t`. */ uint32_t hash(uint32_t kx); @@ -53,9 +53,11 @@ float hash_float_to_float(float2 k); float hash_float_to_float(float3 k); float hash_float_to_float(float4 k); -/* -------------------------------------------------------------------- - * Perlin noise. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Perlin Noise + * \{ */ /* Perlin noise in the range [-1, 1]. */ float perlin_signed(float position); @@ -99,4 +101,6 @@ float3 perlin_float3_fractal_distorted(float4 position, float roughness, float distortion); +/** \} */ + } // namespace blender::noise diff --git a/source/blender/blenlib/BLI_resource_scope.hh b/source/blender/blenlib/BLI_resource_scope.hh index 8e88e251d30..f0d8e71478e 100644 --- a/source/blender/blenlib/BLI_resource_scope.hh +++ b/source/blender/blenlib/BLI_resource_scope.hh @@ -71,9 +71,9 @@ class ResourceScope : NonCopyable, NonMovable { LinearAllocator<> &linear_allocator(); }; -/* -------------------------------------------------------------------- - * #ResourceScope inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #ResourceScope Inline Methods + * \{ */ /** * Pass ownership of the resource to the ResourceScope. It will be destructed and freed when @@ -165,4 +165,6 @@ inline LinearAllocator<> &ResourceScope::linear_allocator() return allocator_; } +/** \} */ + } // namespace blender diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index 79d1f73bb93..4c2b26fe316 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -151,9 +151,9 @@ class StringRef : public StringRefBase { constexpr char operator[](int64_t index) const; }; -/* -------------------------------------------------------------------- - * #StringRefBase inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #StringRefBase Inline Methods + * \{ */ constexpr StringRefBase::StringRefBase(const char *data, const int64_t size) : data_(data), size_(size) @@ -418,9 +418,11 @@ constexpr StringRef StringRefBase::trim(StringRef characters_to_remove) const return this->substr(find_front, substr_len); } -/* -------------------------------------------------------------------- - * #StringRefNull inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #StringRefNull Inline Methods + * \{ */ constexpr StringRefNull::StringRefNull() : StringRefBase("", 0) { @@ -476,9 +478,11 @@ constexpr const char *StringRefNull::c_str() const return data_; } -/* -------------------------------------------------------------------- - * #StringRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #StringRef Inline Methods + * \{ */ constexpr StringRef::StringRef() : StringRefBase(nullptr, 0) { @@ -570,9 +574,11 @@ constexpr StringRef::StringRef(std::string_view view) { } -/* -------------------------------------------------------------------- - * Operator overloads - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operator Overloads + * \{ */ inline std::ostream &operator<<(std::ostream &stream, StringRef ref) { @@ -632,4 +638,6 @@ constexpr bool operator>=(StringRef a, StringRef b) return std::string_view(a) >= std::string_view(b); } +/** \} */ + } // namespace blender diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index eeb97946029..f65c4e443f2 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -391,9 +391,9 @@ Vector evaluate_fields(ResourceScope &scope, const FieldContext &context, Span dst_varrays = {}); -/* -------------------------------------------------------------------- - * Utility functions for simple field creation and evaluation. - */ +/* -------------------------------------------------------------------- */ +/** \name Utility functions for simple field creation and evaluation + * \{ */ void evaluate_constant_field(const GField &field, void *r_value); @@ -423,9 +423,11 @@ class IndexFieldInput final : public FieldInput { ResourceScope &scope) const final; }; -/* -------------------------------------------------------------------- - * #FieldNode inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #FieldNode Inline Methods + * \{ */ inline FieldNode::FieldNode(bool is_input, bool depends_on_input) : is_input_(is_input), depends_on_input_(depends_on_input) @@ -467,9 +469,11 @@ inline bool operator!=(const FieldNode &a, const FieldNode &b) return !(a == b); } -/* -------------------------------------------------------------------- - * #FieldOperation inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #FieldOperation Inline Methods + * \{ */ inline Span FieldOperation::inputs() const { @@ -497,9 +501,11 @@ inline const CPPType &FieldOperation::output_cpp_type(int output_index) const return CPPType::get(); } -/* -------------------------------------------------------------------- - * #FieldInput inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #FieldInput Inline Methods + * \{ */ inline std::string FieldInput::socket_inspection_name() const { @@ -523,4 +529,6 @@ inline const CPPType &FieldInput::output_cpp_type(int output_index) const return *type_; } +/** \} */ + } // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_procedure.hh b/source/blender/functions/FN_multi_function_procedure.hh index 4c06ce98ee3..a26eb1045a7 100644 --- a/source/blender/functions/FN_multi_function_procedure.hh +++ b/source/blender/functions/FN_multi_function_procedure.hh @@ -323,9 +323,9 @@ using MFDestructInstruction = fn::MFDestructInstruction; using MFProcedure = fn::MFProcedure; } // namespace multi_function_procedure_types -/* -------------------------------------------------------------------- - * MFInstructionCursor inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #MFInstructionCursor Inline Methods + * \{ */ inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction) : type_(Call), instruction_(&instruction) @@ -367,9 +367,11 @@ inline MFInstructionCursor::Type MFInstructionCursor::type() const return type_; } -/* -------------------------------------------------------------------- - * MFVariable inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFVariable Inline Methods + * \{ */ inline MFDataType MFVariable::data_type() const { @@ -391,9 +393,11 @@ inline int MFVariable::id() const return id_; } -/* -------------------------------------------------------------------- - * MFInstruction inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFInstruction Inline Methods + * \{ */ inline MFInstructionType MFInstruction::type() const { @@ -405,9 +409,11 @@ inline Span MFInstruction::prev() const return prev_; } -/* -------------------------------------------------------------------- - * MFCallInstruction inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFCallInstruction Inline Methods + * \{ */ inline const MultiFunction &MFCallInstruction::fn() const { @@ -434,9 +440,11 @@ inline Span MFCallInstruction::params() const return params_; } -/* -------------------------------------------------------------------- - * MFBranchInstruction inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFBranchInstruction Inline Methods + * \{ */ inline MFVariable *MFBranchInstruction::condition() { @@ -468,9 +476,11 @@ inline const MFInstruction *MFBranchInstruction::branch_false() const return branch_false_; } -/* -------------------------------------------------------------------- - * MFDestructInstruction inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFDestructInstruction Inline Methods + * \{ */ inline MFVariable *MFDestructInstruction::variable() { @@ -492,9 +502,11 @@ inline const MFInstruction *MFDestructInstruction::next() const return next_; } -/* -------------------------------------------------------------------- - * MFDummyInstruction inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFDummyInstruction Inline Methods + * \{ */ inline MFInstruction *MFDummyInstruction::next() { @@ -506,9 +518,11 @@ inline const MFInstruction *MFDummyInstruction::next() const return next_; } -/* -------------------------------------------------------------------- - * MFProcedure inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFProcedure Inline Methods + * \{ */ inline Span MFProcedure::params() const { @@ -536,4 +550,6 @@ inline Span MFProcedure::variables() const return variables_; } +/** \} */ + } // namespace blender::fn diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc index 9a83d8cd497..ea54f1e7c00 100644 --- a/source/blender/functions/intern/generic_virtual_array.cc +++ b/source/blender/functions/intern/generic_virtual_array.cc @@ -18,9 +18,9 @@ namespace blender::fn { -/* -------------------------------------------------------------------- - * GVArray_For_ShallowCopy. - */ +/* -------------------------------------------------------------------- */ +/** \name #GVArray_For_ShallowCopy + * \{ */ class GVArray_For_ShallowCopy : public GVArray { private: @@ -48,10 +48,11 @@ class GVArray_For_ShallowCopy : public GVArray { varray_.materialize_to_uninitialized(mask, dst); } }; +/** \} */ -/* -------------------------------------------------------------------- - * GVArray. - */ +/* -------------------------------------------------------------------- */ +/** \name #GVArray + * \{ */ void GVArray::materialize(void *dst) const { @@ -142,9 +143,11 @@ GVArrayPtr GVArray::shallow_copy() const return std::make_unique(*this); } -/* -------------------------------------------------------------------- - * GVMutableArray. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVMutableArray + * \{ */ void GVMutableArray::set_by_copy_impl(const int64_t index, const void *value) { @@ -191,9 +194,11 @@ void GVMutableArray::fill(const void *value) } } -/* -------------------------------------------------------------------- - * GVArray_For_GSpan. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_For_GSpan + * \{ */ void GVArray_For_GSpan::get_impl(const int64_t index, void *r_value) const { @@ -215,9 +220,11 @@ GSpan GVArray_For_GSpan::get_internal_span_impl() const return GSpan(*type_, data_, size_); } -/* -------------------------------------------------------------------- - * GVMutableArray_For_GMutableSpan. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVMutableArray_For_GMutableSpan + * \{ */ void GVMutableArray_For_GMutableSpan::get_impl(const int64_t index, void *r_value) const { @@ -255,9 +262,11 @@ GSpan GVMutableArray_For_GMutableSpan::get_internal_span_impl() const return GSpan(*type_, data_, size_); } -/* -------------------------------------------------------------------- - * GVArray_For_SingleValueRef. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_For_SingleValueRef + * \{ */ void GVArray_For_SingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const { @@ -290,9 +299,11 @@ void GVArray_For_SingleValueRef::get_internal_single_impl(void *r_value) const type_->copy_assign(value_, r_value); } -/* -------------------------------------------------------------------- - * GVArray_For_SingleValue. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_For_SingleValue + * \{ */ GVArray_For_SingleValue::GVArray_For_SingleValue(const CPPType &type, const int64_t size, @@ -309,9 +320,11 @@ GVArray_For_SingleValue::~GVArray_For_SingleValue() MEM_freeN((void *)value_); } -/* -------------------------------------------------------------------- - * GVArray_GSpan. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_GSpan + * \{ */ GVArray_GSpan::GVArray_GSpan(const GVArray &varray) : GSpan(varray.type()), varray_(varray) { @@ -334,9 +347,11 @@ GVArray_GSpan::~GVArray_GSpan() } } -/* -------------------------------------------------------------------- - * GVMutableArray_GSpan. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVMutableArray_GSpan + * \{ */ GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray &varray, const bool copy_values_to_span) : GMutableSpan(varray.type()), varray_(varray) @@ -387,9 +402,11 @@ void GVMutableArray_GSpan::disable_not_applied_warning() show_not_saved_warning_ = false; } -/* -------------------------------------------------------------------- - * GVArray_For_SlicedGVArray. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_For_SlicedGVArray + * \{ */ void GVArray_For_SlicedGVArray::get_impl(const int64_t index, void *r_value) const { @@ -401,9 +418,11 @@ void GVArray_For_SlicedGVArray::get_to_uninitialized_impl(const int64_t index, v varray_.get_to_uninitialized(index + offset_, r_value); } -/* -------------------------------------------------------------------- - * GVArray_Slice. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_Slice + * \{ */ GVArray_Slice::GVArray_Slice(const GVArray &varray, const IndexRange slice) { @@ -430,4 +449,6 @@ GVArray_Slice::GVArray_Slice(const GVArray &varray, const IndexRange slice) } } +/** \} */ + } // namespace blender::fn diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index 661c462f60d..3c89f082e9b 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -38,6 +38,10 @@ #include "gpu_select_private.h" +/* -------------------------------------------------------------------- */ +/** \name Internal Types + * \{ */ + /* Internal algorithm used */ enum { /** glBegin/EndQuery(GL_SAMPLES_PASSED... ), `gpu_select_query.c` @@ -61,6 +65,12 @@ typedef struct GPUSelectState { static GPUSelectState g_select_state = {0}; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public API + * \{ */ + /** * initialize and provide buffer for results */ @@ -149,12 +159,14 @@ uint GPU_select_end(void) return hits; } -/* ---------------------------------------------------------------------------- - * Caching +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Caching * * Support multiple begin/end's as long as they are within the initial region. - * Currently only used by ALGO_GL_PICK. - */ + * Currently only used by #ALGO_GL_PICK. + * \{ */ void GPU_select_cache_begin(void) { @@ -187,9 +199,11 @@ bool GPU_select_is_cached(void) return g_select_state.use_cache && gpu_select_pick_is_cached(); } -/* ---------------------------------------------------------------------------- - * Utilities - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ /** * Helper function, nothing special but avoids doing inline since hits aren't sorted by depth @@ -269,3 +283,5 @@ void GPU_select_buffer_stride_realign(const rcti *src, const rcti *dst, uint *r_ } memset(r_buf, 0, (last_px_id + 1) * sizeof(*r_buf)); } + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index 7fb704c29dd..a8907859bf4 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -51,9 +51,9 @@ /* Z-depth of cleared depth buffer */ #define DEPTH_MAX 0xffffffff -/* ---------------------------------------------------------------------------- - * SubRectStride - */ +/* -------------------------------------------------------------------- */ +/** \name #SubRectStride + * \{ */ /* For looping over a sub-region of a rect, could be moved into 'rct.c'. */ typedef struct SubRectStride { @@ -99,14 +99,16 @@ BLI_INLINE bool depth_is_filled(const depth_t *prev, const depth_t *curr) return (*prev != *curr) && (*curr != DEPTH_MAX); } -/* ---------------------------------------------------------------------------- - * DepthBufCache - * - * Result of reading glReadPixels, - * use for both cache and non-cached storage. - */ +/** \} */ -/* store result of glReadPixels */ +/* -------------------------------------------------------------------- */ +/** \name #DepthBufCache + * + * Result of reading #glReadPixels, + * use for both cache and non-cached storage. + * \{ */ + +/** Store result of #glReadPixels. */ typedef struct DepthBufCache { struct DepthBufCache *next, *prev; uint id; @@ -188,11 +190,13 @@ static bool depth_buf_subrect_depth_any_filled(const DepthBufCache *rect_src, return false; } -/* ---------------------------------------------------------------------------- - * DepthID +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #DepthID * * Internal structure for storing hits. - */ + * \{ */ typedef struct DepthID { uint id; @@ -225,6 +229,12 @@ static int depth_cmp(const void *v1, const void *v2) return 0; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Selection Begin/End/Load API + * \{ */ + /* depth sorting */ typedef struct GPUPickState { /* cache on initialization */ @@ -691,11 +701,13 @@ uint gpu_select_pick_end(void) return hits; } -/* ---------------------------------------------------------------------------- - * Caching +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Caching * * Support multiple begin/end's reusing depth buffers. - */ + * \{ */ void gpu_select_pick_cache_begin(void) { @@ -749,3 +761,5 @@ void gpu_select_pick_cache_load_id(void) } } } + +/** \} */ diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index 60d84463a82..e903e3c9255 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -202,9 +202,9 @@ using nodes::DSocket; using nodes::DTreeContext; } // namespace derived_node_tree_types -/* -------------------------------------------------------------------- - * DTreeContext inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DTreeContext Inline Methods + * \{ */ inline const NodeTreeRef &DTreeContext::tree() const { @@ -235,10 +235,11 @@ inline bool DTreeContext::is_root() const { return parent_context_ == nullptr; } +/** \} */ -/* -------------------------------------------------------------------- - * DNode inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DNode Inline Methods + * \{ */ inline DNode::DNode(const DTreeContext *context, const NodeRef *node_ref) : context_(context), node_ref_(node_ref) @@ -300,10 +301,11 @@ inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const { return {context_, &node_ref_->output_by_identifier(identifier)}; } +/** \} */ -/* -------------------------------------------------------------------- - * DSocket inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DSocket Inline Methods + * \{ */ inline DSocket::DSocket(const DTreeContext *context, const SocketRef *socket_ref) : context_(context), socket_ref_(socket_ref) @@ -361,10 +363,11 @@ inline DNode DSocket::node() const BLI_assert(socket_ref_ != nullptr); return {context_, &socket_ref_->node()}; } +/** \} */ -/* -------------------------------------------------------------------- - * DInputSocket inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DInputSocket Inline Methods + * \{ */ inline DInputSocket::DInputSocket(const DTreeContext *context, const InputSocketRef *socket_ref) : DSocket(context, socket_ref) @@ -385,10 +388,11 @@ inline const InputSocketRef *DInputSocket::operator->() const { return (const InputSocketRef *)socket_ref_; } +/** \} */ -/* -------------------------------------------------------------------- - * DOutputSocket inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DOutputSocket Inline Methods + * \{ */ inline DOutputSocket::DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket_ref) : DSocket(context, socket_ref) @@ -409,10 +413,11 @@ inline const OutputSocketRef *DOutputSocket::operator->() const { return (const OutputSocketRef *)socket_ref_; } +/** \} */ -/* -------------------------------------------------------------------- - * DerivedNodeTree inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DerivedNodeTree Inline Methods + * \{ */ inline const DTreeContext &DerivedNodeTree::root_context() const { @@ -424,4 +429,6 @@ inline Span DerivedNodeTree::used_node_tree_refs() const return used_node_tree_refs_; } +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh index 58816544dc1..c1952cf8014 100644 --- a/source/blender/nodes/NOD_multi_function.hh +++ b/source/blender/nodes/NOD_multi_function.hh @@ -75,9 +75,9 @@ class NodeMultiFunctions { const MultiFunction *try_get(const DNode &node) const; }; -/* -------------------------------------------------------------------- - * NodeMultiFunctionBuilder inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #NodeMultiFunctionBuilder Inline Methods + * \{ */ inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, @@ -118,13 +118,17 @@ inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...ar this->set_matching_fn(&fn); } -/* -------------------------------------------------------------------- - * NodeMultiFunctions inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeMultiFunctions Inline Methods + * \{ */ inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const { return map_.lookup_default(node->bnode(), nullptr); } +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index aa01afc6a14..da0ce6a3907 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -255,9 +255,9 @@ class NodeDeclarationBuilder { Vector &r_decls); }; -/* -------------------------------------------------------------------- - * #OutputFieldDependency inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #OutputFieldDependency Inline Methods + * \{ */ inline OutputFieldDependency OutputFieldDependency::ForFieldSource() { @@ -313,9 +313,11 @@ inline bool operator!=(const OutputFieldDependency &a, const OutputFieldDependen return !(a == b); } -/* -------------------------------------------------------------------- - * #FieldInferencingInterface inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #FieldInferencingInterface Inline Methods + * \{ */ inline bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) { @@ -327,9 +329,11 @@ inline bool operator!=(const FieldInferencingInterface &a, const FieldInferencin return !(a == b); } -/* -------------------------------------------------------------------- - * #SocketDeclaration inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #SocketDeclaration Inline Methods + * \{ */ inline StringRefNull SocketDeclaration::name() const { @@ -355,9 +359,11 @@ inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() return output_field_dependency_; } -/* -------------------------------------------------------------------- - * #NodeDeclarationBuilder inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeDeclarationBuilder Inline Methods + * \{ */ inline NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declaration) : declaration_(declaration) @@ -395,9 +401,11 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket( return socket_decl_builder_ref; } -/* -------------------------------------------------------------------- - * #NodeDeclaration inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeDeclaration Inline Methods + * \{ */ inline Span NodeDeclaration::inputs() const { @@ -409,4 +417,6 @@ inline Span NodeDeclaration::outputs() const return outputs_; } +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index 1da42fb6425..5337f79536b 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -316,9 +316,9 @@ using nodes::OutputSocketRef; using nodes::SocketRef; } // namespace node_tree_ref_types -/* -------------------------------------------------------------------- - * SocketRef inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #SocketRef Inline Methods + * \{ */ inline Span SocketRef::logically_linked_sockets() const { @@ -457,9 +457,11 @@ template inline T *SocketRef::default_value() const return (T *)bsocket_->default_value; } -/* -------------------------------------------------------------------- - * InputSocketRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #InputSocketRef Inline Methods + * \{ */ inline Span InputSocketRef::logically_linked_sockets() const { @@ -476,9 +478,11 @@ inline bool InputSocketRef::is_multi_input_socket() const return bsocket_->flag & SOCK_MULTI_INPUT; } -/* -------------------------------------------------------------------- - * OutputSocketRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #OutputSocketRef Inline Methods + * \{ */ inline Span OutputSocketRef::logically_linked_sockets() const { @@ -490,9 +494,11 @@ inline Span OutputSocketRef::directly_linked_sockets() c return directly_linked_sockets_.cast(); } -/* -------------------------------------------------------------------- - * NodeRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeRef Inline Methods + * \{ */ inline const NodeTreeRef &NodeRef::tree() const { @@ -629,9 +635,11 @@ template inline T *NodeRef::storage() const return (T *)bnode_->storage; } -/* -------------------------------------------------------------------- - * LinkRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #LinkRef Inline Methods + * \{ */ inline const OutputSocketRef &LinkRef::from() const { @@ -653,9 +661,11 @@ inline bool LinkRef::is_muted() const return blink_->flag & NODE_LINK_MUTED; } -/* -------------------------------------------------------------------- - * InternalLinkRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #InternalLinkRef Inline Methods + * \{ */ inline const InputSocketRef &InternalLinkRef::from() const { @@ -672,9 +682,11 @@ inline bNodeLink *InternalLinkRef::blink() const return blink_; } -/* -------------------------------------------------------------------- - * NodeTreeRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeTreeRef Inline Methods + * \{ */ inline Span NodeTreeRef::nodes() const { @@ -722,4 +734,6 @@ inline StringRefNull NodeTreeRef::name() const return btree_->id.name + 2; } +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 6eac77b1d6b..00874cad766 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -201,9 +201,9 @@ class Geometry : public SocketDeclaration { bool matches(const bNodeSocket &socket) const override; }; -/* -------------------------------------------------------------------- - * #FloatBuilder inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #FloatBuilder Inline Methods + * \{ */ inline FloatBuilder &FloatBuilder::min(const float value) { @@ -229,9 +229,11 @@ inline FloatBuilder &FloatBuilder::subtype(PropertySubType subtype) return *this; } -/* -------------------------------------------------------------------- - * #IntBuilder inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #IntBuilder Inline Methods + * \{ */ inline IntBuilder &IntBuilder::min(const int value) { @@ -257,9 +259,11 @@ inline IntBuilder &IntBuilder::subtype(PropertySubType subtype) return *this; } -/* -------------------------------------------------------------------- - * #VectorBuilder inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #VectorBuilder Inline Methods + * \{ */ inline VectorBuilder &VectorBuilder::default_value(const float3 value) { @@ -285,9 +289,11 @@ inline VectorBuilder &VectorBuilder::max(const float max) return *this; } -/* -------------------------------------------------------------------- - * #BoolBuilder inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #BoolBuilder Inline Methods + * \{ */ inline BoolBuilder &BoolBuilder::default_value(const bool value) { @@ -295,9 +301,11 @@ inline BoolBuilder &BoolBuilder::default_value(const bool value) return *this; } -/* -------------------------------------------------------------------- - * #ColorBuilder inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #ColorBuilder Inline Methods + * \{ */ inline ColorBuilder &ColorBuilder::default_value(const ColorGeometry4f value) { @@ -305,9 +313,11 @@ inline ColorBuilder &ColorBuilder::default_value(const ColorGeometry4f value) return *this; } -/* -------------------------------------------------------------------- - * #IDSocketDeclaration and children inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #IDSocketDeclaration and Children Inline Methods + * \{ */ inline IDSocketDeclaration::IDSocketDeclaration(const char *idname) : idname_(idname) { @@ -329,11 +339,15 @@ inline Texture::Texture() : IDSocketDeclaration("NodeSocketTexture") { } +/** \} */ + } // namespace blender::nodes::decl -/* -------------------------------------------------------------------- - * Extern template instantiations that are defined in `intern/extern_implementations.cc`. - */ +/* -------------------------------------------------------------------- */ +/** \name External Template Instantiations + * + * Defined in `intern/extern_implementations.cc`. + * \{ */ namespace blender::nodes { #define MAKE_EXTERN_SOCKET_DECLARATION(TYPE) \ @@ -351,3 +365,5 @@ MAKE_EXTERN_SOCKET_DECLARATION(decl::Geometry) #undef MAKE_EXTERN_SOCKET_DECLARATION } // namespace blender::nodes + +/** \} */ diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index 4b0dbad3cff..f910679d492 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -30,9 +30,9 @@ static void modify_subtype_except_for_storage(bNodeSocket &socket, int new_subty socket.typeinfo = socktype; } -/* -------------------------------------------------------------------- - * Float. - */ +/* -------------------------------------------------------------------- */ +/** \name #Float + * \{ */ bNodeSocket &Float::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -83,9 +83,11 @@ bNodeSocket &Float::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket & return socket; } -/* -------------------------------------------------------------------- - * Int. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Int + * \{ */ bNodeSocket &Int::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -136,9 +138,11 @@ bNodeSocket &Int::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &so return socket; } -/* -------------------------------------------------------------------- - * Vector. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Vector + * \{ */ bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -181,9 +185,11 @@ bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket return socket; } -/* -------------------------------------------------------------------- - * Bool. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Bool + * \{ */ bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -206,9 +212,11 @@ bool Bool::matches(const bNodeSocket &socket) const return true; } -/* -------------------------------------------------------------------- - * Color. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Color + * \{ */ bNodeSocket &Color::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -236,9 +244,11 @@ bool Color::matches(const bNodeSocket &socket) const return true; } -/* -------------------------------------------------------------------- - * String. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #String + * \{ */ bNodeSocket &String::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -259,9 +269,11 @@ bool String::matches(const bNodeSocket &socket) const return true; } -/* -------------------------------------------------------------------- - * IDSocketDeclaration. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #IDSocketDeclaration + * \{ */ bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree, bNode &node, @@ -295,9 +307,11 @@ bNodeSocket &IDSocketDeclaration::update_or_build(bNodeTree &ntree, return socket; } -/* -------------------------------------------------------------------- - * Geometry. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Geometry + * \{ */ bNodeSocket &Geometry::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -318,4 +332,6 @@ bool Geometry::matches(const bNodeSocket &socket) const return true; } +/** \} */ + } // namespace blender::nodes::decl From 1b22650fbf0ce06b63795bfc6a21c382634e1632 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Mon, 4 Oct 2021 23:54:21 -0500 Subject: [PATCH 0520/1500] Geometry Nodes: Rename "String Join" node to "Join Strings" Rename the "String Join" node to "Join Strings" to go with the verb first naming convention. Differential Revision: https://developer.blender.org/D12678 --- source/blender/nodes/NOD_static_types.h | 2 +- source/blender/nodes/geometry/nodes/node_geo_string_join.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 2a163c90ab5..7e10b4055fd 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -374,7 +374,7 @@ DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proxim DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") -DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "String Join", "") +DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "") DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc index 1e4a4d1f68b..515f072e976 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc @@ -46,7 +46,7 @@ void register_node_type_geo_string_join() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_STRING_JOIN, "String Join", NODE_CLASS_CONVERTER, 0); + geo_node_type_base(&ntype, GEO_NODE_STRING_JOIN, "Join Strings", NODE_CLASS_CONVERTER, 0); ntype.geometry_node_execute = blender::nodes::geo_node_string_join_exec; ntype.declare = blender::nodes::geo_node_string_join_declare; nodeRegisterType(&ntype); From caac5325656294e7276fee60edbd575b123e8eb9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 5 Oct 2021 16:35:05 +1100 Subject: [PATCH 0521/1500] Cleanup: add Params.select_tweak_event Convenience, use for tool key-maps to avoid overly verbose expressions. --- .../keyconfig/keymap_data/blender_default.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 14520c10a2b..988ef3b2c21 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -75,6 +75,8 @@ class Params: "use_fallback_tool_rmb", # Shorthand for: `('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value)`. "select_mouse_value_fallback", + # Shorthand for: `{"type": params.select_tweak, "value": 'ANY'}`. + "select_tweak_event", # Shorthand for: `('CLICK_DRAG' if params.use_pie_click_drag else 'PRESS')` "pie_value", # Shorthand for: `{"type": params.tool_tweak, "value": 'ANY'}`. @@ -197,6 +199,7 @@ class Params: # Convenience variables: self.use_fallback_tool_rmb = self.use_fallback_tool if select_mouse == 'RIGHT' else False self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value + self.select_tweak_event = {"type": self.select_tweak, "value": 'ANY'} self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS' self.tool_tweak_event = {"type": self.tool_tweak, "value": 'ANY'} self.tool_maybe_tweak_event = {"type": self.tool_maybe_tweak, "value": self.tool_maybe_tweak_value} @@ -6172,7 +6175,7 @@ def km_image_editor_tool_uv_select_box(params, *, fallback): *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_box", # Don't use `tool_maybe_tweak_event`, see comment for this slot. - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if fallback else params.tool_tweak_event))), *_template_uv_select_for_fallback(params, fallback), ]}, ) @@ -6185,8 +6188,7 @@ def km_image_editor_tool_uv_select_circle(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_circle", - type=params.select_tweak if fallback else params.tool_mouse, - value='ANY' if fallback else 'PRESS', + **{params.select_tweak_event if fallback else {"type": params.tool_mouse, 'PRESS'}} properties=[("wait_for_input", False)])), # No selection fallback since this operates on press. ]}, @@ -6201,7 +6203,7 @@ def km_image_editor_tool_uv_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_lasso", - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if fallback else params.tool_tweak_event))), *_template_uv_select_for_fallback(params, fallback), ]}, ) @@ -6365,7 +6367,7 @@ def km_3d_view_tool_select_box(params, *, fallback): *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "view3d.select_box", # Don't use `tool_maybe_tweak_event`, see comment for this slot. - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if fallback else params.tool_tweak_event))), *_template_view3d_select_for_fallback(params, fallback), ]}, ) @@ -6395,7 +6397,7 @@ def km_3d_view_tool_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "view3d.select_lasso", - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if fallback else params.tool_tweak_event))), *_template_view3d_select_for_fallback(params, fallback), ]} ) @@ -7251,7 +7253,7 @@ def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback): *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "gpencil.select_box", # Don't use `tool_maybe_tweak_event`, see comment for this slot. - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if fallback else params.tool_tweak_event))), *_template_view3d_gpencil_select_for_fallback(params, fallback), ]}, ) @@ -7281,7 +7283,7 @@ def km_3d_view_tool_edit_gpencil_select_lasso(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "gpencil.select_lasso", - **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))), + **(params.select_tweak_event if fallback else params.tool_tweak_event))), *_template_view3d_gpencil_select_for_fallback(params, fallback), ]} ) From 300403a38b8ed7f0f84125ca9a82264757ae704a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 5 Oct 2021 17:41:23 +1100 Subject: [PATCH 0522/1500] Fix syntax error in caac5325656294e7276fee60edbd575b123e8eb9 --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 988ef3b2c21..650ade4d111 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6188,7 +6188,7 @@ def km_image_editor_tool_uv_select_circle(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_circle", - **{params.select_tweak_event if fallback else {"type": params.tool_mouse, 'PRESS'}} + **(params.select_tweak_event if fallback else {"type": params.tool_mouse, "value": 'PRESS'}), properties=[("wait_for_input", False)])), # No selection fallback since this operates on press. ]}, From 08511b1c3de0338314940397083adaba4e9cf492 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 5 Oct 2021 15:55:57 +0900 Subject: [PATCH 0523/1500] XR: Add runtime window area for XR events This adds an offscreen View3D window area for the VR view in order to execute XR events/operators in the proper context. The area is created as runtime data before XR events are dispatched and set as the active area during XR event handling. Since the area is runtime-only, it will not be saved in files and since the area is offscreen, it will not interfere with regular window areas. The area is removed with the rest of the XR runtime data on exit, file read, or when stopping the VR session. Note: This also adds internal types (EVT_DATA_XR, EVT_XR_ACTION) and structs (wmXrActionData) for XR events. Reviewed By: Severin Differential Revision: https://developer.blender.org/D12472 --- source/blender/editors/include/ED_screen.h | 6 ++ source/blender/editors/screen/area.c | 62 +++++++++++++ source/blender/editors/screen/screen_edit.c | 19 ++-- source/blender/editors/screen/screen_intern.h | 1 + source/blender/windowmanager/WM_api.h | 1 + source/blender/windowmanager/WM_types.h | 31 +++++++ .../windowmanager/intern/wm_event_system.c | 92 +++++++++++++++++++ .../blender/windowmanager/wm_event_system.h | 7 ++ source/blender/windowmanager/wm_event_types.h | 4 + .../blender/windowmanager/xr/intern/wm_xr.c | 17 +++- .../windowmanager/xr/intern/wm_xr_intern.h | 12 ++- .../windowmanager/xr/intern/wm_xr_session.c | 35 +++++-- 12 files changed, 268 insertions(+), 19 deletions(-) diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 60ef3e740c6..0de6907e677 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -178,6 +178,12 @@ void ED_area_update_region_sizes(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area); bool ED_area_has_shared_border(struct ScrArea *a, struct ScrArea *b); +ScrArea *ED_area_offscreen_create(struct wmWindowManager *wm, + struct wmWindow *win, + eSpace_Type space_type); +void ED_area_offscreen_free(struct wmWindowManager *wm, + struct wmWindow *win, + struct ScrArea *area); ScrArea *ED_screen_areas_iter_first(const struct wmWindow *win, const bScreen *screen); ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 833c9accf95..d5456482d67 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -2001,6 +2001,68 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) } } +static void area_offscreen_init(wmWindowManager *wm, ScrArea *area) +{ + area->type = BKE_spacetype_from_id(area->spacetype); + + if (area->type == NULL) { + area->spacetype = SPACE_VIEW3D; + area->type = BKE_spacetype_from_id(area->spacetype); + } + + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + region->type = BKE_regiontype_from_id_or_first(area->type, region->regiontype); + } +} + +ScrArea *ED_area_offscreen_create(wmWindowManager *wm, wmWindow *win, eSpace_Type space_type) +{ + ScrArea *area = MEM_callocN(sizeof(*area), __func__); + area->spacetype = space_type; + + screen_area_spacelink_add(WM_window_get_active_scene(win), area, space_type); + area_offscreen_init(wm, area); + + return area; +} + +static void area_offscreen_exit(wmWindowManager *wm, wmWindow *win, ScrArea *area) +{ + if (area->type && area->type->exit) { + area->type->exit(wm, area); + } + + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + if (region->type && region->type->exit) { + region->type->exit(wm, region); + } + + WM_event_modal_handler_region_replace(win, region, NULL); + WM_draw_region_free(region, true); + + MEM_SAFE_FREE(region->headerstr); + + if (region->regiontimer) { + WM_event_remove_timer(wm, win, region->regiontimer); + region->regiontimer = NULL; + } + + if (wm->message_bus) { + WM_msgbus_clear_by_owner(wm->message_bus, region); + } + } + + WM_event_modal_handler_area_replace(win, area, NULL); +} + +void ED_area_offscreen_free(wmWindowManager *wm, wmWindow *win, ScrArea *area) +{ + area_offscreen_exit(wm, win, area); + + BKE_screen_area_free(area); + MEM_freeN(area); +} + static void region_update_rect(ARegion *region) { region->winx = BLI_rcti_size_x(®ion->winrct) + 1; diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 1c068fdd6e4..02b1e002d86 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -574,6 +574,17 @@ bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area) return screen_area_join_ex(C, screen, sa2, area, true); } +void screen_area_spacelink_add(Scene *scene, ScrArea *area, eSpace_Type space_type) +{ + SpaceType *stype = BKE_spacetype_from_id(space_type); + SpaceLink *slink = stype->create(area, scene); + + area->regionbase = slink->regionbase; + + BLI_addhead(&area->spacedata, slink); + BLI_listbase_clear(&slink->regionbase); +} + /* ****************** EXPORTED API TO OTHER MODULES *************************** */ /* screen sets cursor based on active region */ @@ -1023,13 +1034,7 @@ static void screen_global_area_refresh(wmWindow *win, } else { area = screen_area_create_with_geometry(&win->global_areas, rect, space_type); - SpaceType *stype = BKE_spacetype_from_id(space_type); - SpaceLink *slink = stype->create(area, WM_window_get_active_scene(win)); - - area->regionbase = slink->regionbase; - - BLI_addhead(&area->spacedata, slink); - BLI_listbase_clear(&slink->regionbase); + screen_area_spacelink_add(WM_window_get_active_scene(win), area, space_type); /* Data specific to global areas. */ area->global = MEM_callocN(sizeof(*area->global), __func__); diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index 04ee62b1631..47229e5e2b5 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -94,6 +94,7 @@ eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b); void area_getoffsets( ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2); bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area); +void screen_area_spacelink_add(struct Scene *scene, ScrArea *area, eSpace_Type space_type); struct AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]); /* screen_geometry.c */ diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index ebb0f803acf..b79f5762955 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -989,6 +989,7 @@ bool WM_region_use_viewport(struct ScrArea *area, struct ARegion *region); bool WM_xr_session_exists(const wmXrData *xr); bool WM_xr_session_is_ready(const wmXrData *xr); struct wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr); +struct ScrArea *WM_xr_session_area_get(const wmXrData *xr); void WM_xr_session_base_pose_reset(wmXrData *xr); bool WM_xr_session_state_viewer_pose_location_get(const wmXrData *xr, float r_location[3]); bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_rotation[4]); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 7f52bef3203..14a69d9c435 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -120,6 +120,7 @@ struct wmWindowManager; #include "BLI_compiler_attrs.h" #include "DNA_listBase.h" #include "DNA_vec_types.h" +#include "DNA_xr_types.h" #include "RNA_types.h" /* exported types for WM */ @@ -721,6 +722,36 @@ typedef struct wmXrActionState { }; int type; /* eXrActionType */ } wmXrActionState; + +typedef struct wmXrActionData { + /** Action set name. */ + char action_set[64]; + /** Action name. */ + char action[64]; + /** Type. */ + eXrActionType type; + /** State. Set appropriately based on type. */ + float state[2]; + /** State of the other subaction path for bimanual actions. */ + float state_other[2]; + + /** Input threshold for float/vector2f actions. */ + float float_threshold; + + /** Controller aim pose corresponding to the action's subaction path. */ + float controller_loc[3]; + float controller_rot[4]; + /** Controller aim pose of the other subaction path for bimanual actions. */ + float controller_loc_other[3]; + float controller_rot_other[4]; + + /** Operator. */ + struct wmOperatorType *ot; + struct IDProperty *op_properties; + + /** Whether bimanual interaction is occuring. */ + bool bimanual; +} wmXrActionData; #endif /** Timer flags. */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index cc0a13e96af..c92208c4818 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3461,6 +3461,70 @@ static void wm_event_free_and_remove_from_queue_if_valid(wmEvent *event) * Handle events for all windows, run from the #WM_main event loop. * \{ */ +#ifdef WITH_XR_OPENXR +/** + * Special handling for XR events. + * + * Although XR events are added to regular window queues, they are handled in an "offscreen area" + * context that is owned entirely by XR runtime data and not tied to a window. + */ +static void wm_event_handle_xrevent(bContext *C, + wmWindowManager *wm, + wmWindow *win, + wmEvent *event) +{ + ScrArea *area = WM_xr_session_area_get(&wm->xr); + if (!area) { + return; + } + BLI_assert(area->spacetype == SPACE_VIEW3D && area->spacedata.first); + + /* Find a valid region for XR operator execution and modal handling. */ + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + if (!region) { + return; + } + BLI_assert(WM_region_use_viewport(area, region)); /* For operators using GPU-based selection. */ + + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + int action = wm_handlers_do(C, event, &win->modalhandlers); + + if ((action & WM_HANDLER_BREAK) == 0) { + wmXrActionData *actiondata = event->customdata; + if (actiondata->ot->modal && event->val == KM_RELEASE) { + /* Don't execute modal operators on release. */ + } + else { + PointerRNA properties = {.type = actiondata->ot->srna, .data = actiondata->op_properties}; + if (actiondata->ot->invoke) { + /* Invoke operator, either executing operator or transferring responsibility to window + * modal handlers. */ + wm_operator_invoke(C, + actiondata->ot, + event, + actiondata->op_properties ? &properties : NULL, + NULL, + false, + false); + } + else { + /* Execute operator. */ + wmOperator *op = wm_operator_create( + wm, actiondata->ot, actiondata->op_properties ? &properties : NULL, NULL); + if ((WM_operator_call(C, op) & OPERATOR_HANDLED) == 0) { + WM_operator_free(op); + } + } + } + } + + CTX_wm_region_set(C, NULL); + CTX_wm_area_set(C, NULL); +} +#endif /* WITH_XR_OPENXR */ + /* Called in main loop. */ /* Goes over entire hierarchy: events -> window -> screen -> area -> region. */ void wm_event_do_handlers(bContext *C) @@ -3558,6 +3622,16 @@ void wm_event_do_handlers(bContext *C) CTX_wm_window_set(C, win); +#ifdef WITH_XR_OPENXR + if (event->type == EVT_XR_ACTION) { + wm_event_handle_xrevent(C, wm, win, event); + BLI_remlink(&win->event_queue, event); + wm_event_free(event); + /* Skip mouse event handling below, which is unnecessary for XR events. */ + continue; + } +#endif + /* Clear tool-tip on mouse move. */ if (screen->tool_tip && screen->tool_tip->exit_on_event) { if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { @@ -5110,6 +5184,24 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void #endif } +#ifdef WITH_XR_OPENXR +void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val) +{ + BLI_assert(val == KM_PRESS || val == KM_RELEASE); + + wmEvent event = { + .type = EVT_XR_ACTION, + .val = val, + .is_repeat = false, + .custom = EVT_DATA_XR, + .customdata = actiondata, + .customdatafree = 1, + }; + + wm_event_add(win, &event); +} +#endif /* WITH_XR_OPENXR */ + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index 787c840de8a..d1eb10787e2 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -33,6 +33,10 @@ struct ARegion; struct GHOST_TabletData; struct ScrArea; +#ifdef WITH_XR_OPENXR +struct wmXrActionData; +#endif + #ifdef __cplusplus extern "C" { #endif @@ -147,6 +151,9 @@ void wm_event_free_handler(wmEventHandler *handler); void wm_event_do_handlers(bContext *C); void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void *customdata); +#ifdef WITH_XR_OPENXR +void wm_event_add_xrevent(wmWindow *win, struct wmXrActionData *actiondata, short val); +#endif void wm_event_do_depsgraph(bContext *C, bool is_after_open_file); void wm_event_do_refresh_wm_and_depsgraph(bContext *C); diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 905c57d901a..c175c211db6 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -34,6 +34,7 @@ enum { EVT_DATA_TIMER = 2, EVT_DATA_DRAGDROP = 3, EVT_DATA_NDOF_MOTION = 4, + EVT_DATA_XR = 5, }; /* tablet active, matches GHOST_TTabletMode */ @@ -341,6 +342,9 @@ enum { /* could become gizmo callback */ EVT_GIZMO_UPDATE = 0x5025, /* 20517 */ + + /* XR events: 0x503x */ + EVT_XR_ACTION = 0x5030, /* 20528 */ /* ********** End of Blender internal events. ********** */ }; diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 8891840cb75..36bd03ed3ea 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -24,19 +24,22 @@ #include "BKE_global.h" #include "BKE_idprop.h" +#include "BKE_main.h" #include "BKE_report.h" +#include "DEG_depsgraph.h" + #include "DNA_scene_types.h" #include "DNA_windowmanager_types.h" -#include "DEG_depsgraph.h" - -#include "MEM_guardedalloc.h" +#include "ED_screen.h" #include "GHOST_C-api.h" #include "GPU_platform.h" +#include "MEM_guardedalloc.h" + #include "WM_api.h" #include "wm_surface.h" @@ -137,7 +140,7 @@ bool wm_xr_events_handle(wmWindowManager *wm) /* Process OpenXR action events. */ if (WM_xr_session_is_ready(&wm->xr)) { - wm_xr_session_actions_update(&wm->xr); + wm_xr_session_actions_update(wm); } /* wm_window_process_events() uses the return value to determine if it can put the main thread @@ -172,6 +175,12 @@ void wm_xr_runtime_data_free(wmXrRuntimeData **runtime) * first call, see comment above. */ (*runtime)->context = NULL; + if ((*runtime)->area) { + wmWindowManager *wm = G_MAIN->wm.first; + wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, (*runtime)); + ED_area_offscreen_free(wm, win, (*runtime)->area); + (*runtime)->area = NULL; + } wm_xr_session_data_free(&(*runtime)->session_state); WM_xr_actionmaps_clear(*runtime); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index fc54e261f79..ee495755ac6 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -67,6 +67,9 @@ typedef struct wmXrRuntimeData { * be an invalid reference, i.e. the window may have been closed. */ wmWindow *session_root_win; + /** Offscreen area used for XR events. */ + struct ScrArea *area; + /** Although this struct is internal, RNA gets a handle to this for state information queries. */ wmXrSessionState session_state; wmXrSessionExitFn exit_fn; @@ -172,10 +175,14 @@ typedef struct wmXrActionSet { ListBase active_haptic_actions; } wmXrActionSet; +/* wm_xr.c */ wmXrRuntimeData *wm_xr_runtime_data_create(void); void wm_xr_runtime_data_free(wmXrRuntimeData **runtime); -void wm_xr_session_data_free(wmXrSessionState *state); +/* wm_xr_session.c */ +void wm_xr_session_data_free(wmXrSessionState *state); +wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm, + const wmXrRuntimeData *runtime_data); void wm_xr_session_draw_data_update(const wmXrSessionState *state, const XrSessionSettings *settings, const GHOST_XrDrawViewInfo *draw_view, @@ -190,12 +197,13 @@ void *wm_xr_session_gpu_binding_context_create(void); void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context); void wm_xr_session_actions_init(wmXrData *xr); -void wm_xr_session_actions_update(wmXrData *xr); +void wm_xr_session_actions_update(wmWindowManager *wm); void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, const wmXrAction *aim_action, wmXrData *xr); void wm_xr_session_controller_data_clear(wmXrSessionState *state); +/* wm_xr_draw.c */ void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]); void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 88bf3ff453c..1e8cda30121 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -30,9 +30,12 @@ #include "DEG_depsgraph.h" #include "DNA_camera_types.h" +#include "DNA_space_types.h" #include "DRW_engine.h" +#include "ED_screen.h" + #include "GHOST_C-api.h" #include "GPU_viewport.h" @@ -111,6 +114,7 @@ void wm_xr_session_toggle(wmWindowManager *wm, if (WM_xr_session_exists(xr_data)) { GHOST_XrSessionEnd(xr_data->runtime->context); + xr_data->runtime->session_state.is_started = false; } else { GHOST_XrSessionBeginInfo begin_info; @@ -197,8 +201,8 @@ static void wm_xr_session_draw_data_populate(wmXrData *xr_data, wm_xr_session_base_pose_calc(r_draw_data->scene, settings, &r_draw_data->base_pose); } -static wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm, - const wmXrRuntimeData *runtime_data) +wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm, + const wmXrRuntimeData *runtime_data) { if (runtime_data->session_root_win && BLI_findindex(&wm->windows, runtime_data->session_root_win) != -1) { @@ -373,6 +377,11 @@ wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr) return xr->runtime ? &xr->runtime->session_state : NULL; } +ScrArea *WM_xr_session_area_get(const wmXrData *xr) +{ + return xr->runtime ? xr->runtime->area : NULL; +} + bool WM_xr_session_state_viewer_pose_location_get(const wmXrData *xr, float r_location[3]) { if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { @@ -550,8 +559,9 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin } } -void wm_xr_session_actions_update(wmXrData *xr) +void wm_xr_session_actions_update(wmWindowManager *wm) { + wmXrData *xr = &wm->xr; if (!xr->runtime) { return; } @@ -565,14 +575,27 @@ void wm_xr_session_actions_update(wmXrData *xr) return; } - /* Only update controller data for active action set. */ + /* Only update controller data and dispatch events for active action set. */ if (active_action_set) { + const XrSessionSettings *settings = &xr->session_settings; + wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, xr->runtime); + if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) { - wm_xr_session_controller_data_update(&xr->session_settings, + wm_xr_session_controller_data_update(settings, active_action_set->controller_grip_action, active_action_set->controller_aim_action, state); } + + if (win) { + /* Ensure an XR area exists for events. */ + if (!xr->runtime->area) { + xr->runtime->area = ED_area_offscreen_create(wm, win, SPACE_VIEW3D); + } + + /* Implemented in D10944. */ + // wm_xr_session_events_dispatch(xr, settings, xr_context, active_action_set, state, win); + } } } @@ -628,7 +651,7 @@ static void wm_xr_session_surface_draw(bContext *C) Main *bmain = CTX_data_main(C); wmXrDrawData draw_data; - if (!GHOST_XrSessionIsRunning(wm->xr.runtime->context)) { + if (!WM_xr_session_is_ready(&wm->xr)) { return; } From 1d49293b80446b89b5b12fa0eeefaf14e5051e48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 5 Oct 2021 09:36:11 +0200 Subject: [PATCH 0524/1500] DRW: Move buffer & temp textures & framebuffer management to DrawManager This is a necessary step for EEVEE's new arch. This moves more data to the draw manager. This makes it easier to have the render or draw engines manage their own data. This makes more sense and cleans-up what the GPUViewport holds Also rewrites the Texture pool manager to be in C++. This also move the DefaultFramebuffer/TextureList and the engine related data to a new `DRWViewData` struct. This struct manages the per view (as in stereo view) engine data. There is a bit of cleanup in the way the draw manager is setup. We now use a temporary DRWData instead of creating a dummy viewport. Development: fclem, jbakker Differential Revision: https://developer.blender.org/D11966 --- .clang-format | 1 + source/blender/draw/CMakeLists.txt | 5 +- source/blender/draw/DRW_engine.h | 8 +- source/blender/draw/intern/DRW_render.h | 3 +- .../blender/draw/intern/draw_instance_data.c | 2 + source/blender/draw/intern/draw_manager.c | 759 +++++++++--------- source/blender/draw/intern/draw_manager.h | 59 +- .../blender/draw/intern/draw_manager_data.c | 18 +- .../draw/intern/draw_manager_profiling.c | 7 +- .../blender/draw/intern/draw_manager_text.h | 8 + .../draw/intern/draw_manager_texture.c | 3 +- .../blender/draw/intern/draw_texture_pool.cc | 140 ++++ .../draw_texture_pool.h} | 34 +- source/blender/draw/intern/draw_view_data.cc | 243 ++++++ source/blender/draw/intern/draw_view_data.h | 139 ++++ .../editors/space_view3d/view3d_draw.c | 21 +- source/blender/gpu/GPU_texture.h | 8 + source/blender/gpu/GPU_viewport.h | 95 +-- source/blender/gpu/intern/gpu_viewport.c | 632 +++------------ 19 files changed, 1117 insertions(+), 1068 deletions(-) create mode 100644 source/blender/draw/intern/draw_texture_pool.cc rename source/blender/draw/{DRW_engine_types.h => intern/draw_texture_pool.h} (54%) create mode 100644 source/blender/draw/intern/draw_view_data.cc create mode 100644 source/blender/draw/intern/draw_view_data.h diff --git a/.clang-format b/.clang-format index bf20a4e4c4a..91df22f4d5b 100644 --- a/.clang-format +++ b/.clang-format @@ -180,6 +180,7 @@ ForEachMacros: - CTX_DATA_BEGIN_WITH_ID - DEG_OBJECT_ITER_BEGIN - DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN + - DRW_ENABLED_ENGINE_ITER - DRIVER_TARGETS_LOOPER_BEGIN - DRIVER_TARGETS_USED_LOOPER_BEGIN - FOREACH_BASE_IN_EDIT_MODE_BEGIN diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 71115c5ceb9..dd4aa1747e5 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -94,6 +94,7 @@ set(SRC intern/draw_color_management.cc intern/draw_common.c intern/draw_debug.c + intern/draw_view_data.cc intern/draw_fluid.c intern/draw_hair.c intern/draw_instance_data.c @@ -105,6 +106,7 @@ set(SRC intern/draw_manager_text.c intern/draw_manager_texture.c intern/draw_select_buffer.c + intern/draw_texture_pool.cc intern/draw_shader.c intern/draw_view.c engines/basic/basic_engine.c @@ -188,7 +190,6 @@ set(SRC engines/overlay/overlay_wireframe.c DRW_engine.h - DRW_engine_types.h DRW_select_buffer.h intern/DRW_render.h intern/draw_cache.h @@ -198,12 +199,14 @@ set(SRC intern/draw_color_management.h intern/draw_common.h intern/draw_debug.h + intern/draw_view_data.h intern/draw_hair_private.h intern/draw_instance_data.h intern/draw_manager.h intern/draw_manager_profiling.h intern/draw_manager_testing.h intern/draw_manager_text.h + intern/draw_texture_pool.h intern/draw_shader.h intern/draw_view.h intern/mesh_extractors/extract_mesh.h diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 5e7b812c37b..927a29ed6b6 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -26,13 +26,12 @@ #include "DNA_object_enums.h" -#include "DRW_engine_types.h" - #ifdef __cplusplus extern "C" { #endif struct ARegion; +struct DRWData; struct DRWInstanceDataList; struct Depsgraph; struct DrawEngineType; @@ -57,8 +56,6 @@ void DRW_engines_free(void); bool DRW_engine_render_support(struct DrawEngineType *draw_engine_type); void DRW_engine_register(struct DrawEngineType *draw_engine_type); -void DRW_engine_viewport_data_size_get( - const void *engine_type, int *r_fbl_len, int *r_txl_len, int *r_psl_len, int *r_stl_len); typedef struct DRWUpdateContext { struct Main *bmain; @@ -176,6 +173,9 @@ void DRW_deferred_shader_remove(struct GPUMaterial *mat); struct DrawDataList *DRW_drawdatalist_from_id(struct ID *id); void DRW_drawdata_free(struct ID *id); +struct DRWData *DRW_viewport_data_create(void); +void DRW_viewport_data_free(struct DRWData *drw_data); + bool DRW_opengl_context_release(void); void DRW_opengl_context_activate(bool drw_state); diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index fb8b8536897..c1cab931f6a 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -24,8 +24,6 @@ #pragma once -#include "DRW_engine_types.h" - #include "BLI_listbase.h" #include "BLI_math_matrix.h" #include "BLI_math_vector.h" @@ -56,6 +54,7 @@ #include "draw_debug.h" #include "draw_manager_profiling.h" +#include "draw_view_data.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index e055192eb21..29112ee4788 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -364,6 +364,8 @@ void DRW_instance_data_list_free(DRWInstanceDataList *idatalist) BLI_memblock_destroy(idatalist->pool_batching, (MemblockValFreeFP)temp_batch_free); BLI_remlink(&g_idatalists, idatalist); + + MEM_freeN(idatalist); } void DRW_instance_data_list_reset(DRWInstanceDataList *idatalist) diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index e65fdce5f2e..7a41142b177 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -84,10 +84,12 @@ #include "wm_window.h" #include "draw_color_management.h" +#include "draw_manager.h" #include "draw_manager_profiling.h" #include "draw_manager_testing.h" #include "draw_manager_text.h" #include "draw_shader.h" +#include "draw_texture_pool.h" /* only for callbacks */ #include "draw_cache_impl.h" @@ -111,7 +113,10 @@ /** Render State: No persistent data between draw calls. */ DRWManager DST = {NULL}; -static ListBase DRW_engines = {NULL, NULL}; +static struct { + ListBase /*DRWRegisteredDrawEngine*/ engines; + int len; +} g_registered_engines = {{NULL}}; static void drw_state_prepare_clean_for_draw(DRWManager *dst) { @@ -315,35 +320,6 @@ struct DupliObject *DRW_object_get_dupli(const Object *UNUSED(ob)) /** \name Viewport (DRW_viewport) * \{ */ -void *drw_viewport_engine_data_ensure(void *engine_type) -{ - void *data = GPU_viewport_engine_data_get(DST.viewport, engine_type); - - if (data == NULL) { - data = GPU_viewport_engine_data_create(DST.viewport, engine_type); - } - return data; -} - -void DRW_engine_viewport_data_size_get( - const void *engine_type_v, int *r_fbl_len, int *r_txl_len, int *r_psl_len, int *r_stl_len) -{ - const DrawEngineType *engine_type = engine_type_v; - - if (r_fbl_len) { - *r_fbl_len = engine_type->vedata_size->fbl_len; - } - if (r_txl_len) { - *r_txl_len = engine_type->vedata_size->txl_len; - } - if (r_psl_len) { - *r_psl_len = engine_type->vedata_size->psl_len; - } - if (r_stl_len) { - *r_stl_len = engine_type->vedata_size->stl_len; - } -} - /* WARNING: only use for custom pipeline. 99% of the time, you don't want to use this. */ void DRW_render_viewport_size_set(const int size[2]) { @@ -373,39 +349,6 @@ const float *DRW_viewport_pixelsize_get(void) return &DST.pixsize; } -static void drw_viewport_cache_resize(void) -{ - /* Release the memiter before clearing the mempools that references them */ - GPU_viewport_cache_release(DST.viewport); - - if (DST.vmempool != NULL) { - /* Release Image textures. */ - BLI_memblock_iter iter; - GPUTexture **tex; - BLI_memblock_iternew(DST.vmempool->images, &iter); - while ((tex = BLI_memblock_iterstep(&iter))) { - GPU_texture_free(*tex); - } - - BLI_memblock_clear(DST.vmempool->commands, NULL); - BLI_memblock_clear(DST.vmempool->commands_small, NULL); - BLI_memblock_clear(DST.vmempool->callbuffers, NULL); - BLI_memblock_clear(DST.vmempool->obmats, NULL); - BLI_memblock_clear(DST.vmempool->obinfos, NULL); - BLI_memblock_clear(DST.vmempool->cullstates, NULL); - BLI_memblock_clear(DST.vmempool->shgroups, NULL); - BLI_memblock_clear(DST.vmempool->uniforms, NULL); - BLI_memblock_clear(DST.vmempool->passes, NULL); - BLI_memblock_clear(DST.vmempool->views, NULL); - BLI_memblock_clear(DST.vmempool->images, NULL); - - DRW_uniform_attrs_pool_clear_all(DST.vmempool->obattrs_ubo_pool); - } - - DRW_instance_data_list_free_unused(DST.idatalist); - DRW_instance_data_list_resize(DST.idatalist); -} - /* Not a viewport variable, we could split this out. */ static void drw_context_state_init(void) { @@ -465,107 +408,207 @@ static void draw_unit_state_create(void) DRW_handle_increment(&DST.resource_handle); } -/* It also stores viewport variable to an immutable place: DST - * This is because a cache uniform only store reference - * to its value. And we don't want to invalidate the cache - * if this value change per viewport */ -static void drw_viewport_var_init(void) +DRWData *DRW_viewport_data_create(void) { - RegionView3D *rv3d = DST.draw_ctx.rv3d; - ARegion *region = DST.draw_ctx.region; + DRWData *drw_data = MEM_callocN(sizeof(DRWData), "DRWData"); - /* Refresh DST.size */ - if (DST.viewport) { - int size[2]; - GPU_viewport_size_get(DST.viewport, size); - DST.size[0] = size[0]; - DST.size[1] = size[1]; - DST.inv_size[0] = 1.0f / size[0]; - DST.inv_size[1] = 1.0f / size[1]; + drw_data->texture_pool = DRW_texture_pool_create(); - DefaultFramebufferList *fbl = (DefaultFramebufferList *)GPU_viewport_framebuffer_list_get( - DST.viewport); - DST.default_framebuffer = fbl->default_fb; + drw_data->idatalist = DRW_instance_data_list_create(); - DST.vmempool = GPU_viewport_mempool_get(DST.viewport); + drw_data->commands = BLI_memblock_create(sizeof(DRWCommandChunk)); + drw_data->commands_small = BLI_memblock_create(sizeof(DRWCommandSmallChunk)); + drw_data->callbuffers = BLI_memblock_create(sizeof(DRWCallBuffer)); + drw_data->shgroups = BLI_memblock_create(sizeof(DRWShadingGroup)); + drw_data->uniforms = BLI_memblock_create(sizeof(DRWUniformChunk)); + drw_data->views = BLI_memblock_create(sizeof(DRWView)); + drw_data->images = BLI_memblock_create(sizeof(GPUTexture *)); + drw_data->obattrs_ubo_pool = DRW_uniform_attrs_pool_new(); + { + uint chunk_len = sizeof(DRWObjectMatrix) * DRW_RESOURCE_CHUNK_LEN; + drw_data->obmats = BLI_memblock_create_ex(sizeof(DRWObjectMatrix), chunk_len); + } + { + uint chunk_len = sizeof(DRWObjectInfos) * DRW_RESOURCE_CHUNK_LEN; + drw_data->obinfos = BLI_memblock_create_ex(sizeof(DRWObjectInfos), chunk_len); + } + { + uint chunk_len = sizeof(DRWCullingState) * DRW_RESOURCE_CHUNK_LEN; + drw_data->cullstates = BLI_memblock_create_ex(sizeof(DRWCullingState), chunk_len); + } + { + uint chunk_len = sizeof(DRWPass) * DRW_RESOURCE_CHUNK_LEN; + drw_data->passes = BLI_memblock_create_ex(sizeof(DRWPass), chunk_len); + } - if (DST.vmempool->commands == NULL) { - DST.vmempool->commands = BLI_memblock_create(sizeof(DRWCommandChunk)); - } - if (DST.vmempool->commands_small == NULL) { - DST.vmempool->commands_small = BLI_memblock_create(sizeof(DRWCommandSmallChunk)); - } - if (DST.vmempool->callbuffers == NULL) { - DST.vmempool->callbuffers = BLI_memblock_create(sizeof(DRWCallBuffer)); - } - if (DST.vmempool->obmats == NULL) { - uint chunk_len = sizeof(DRWObjectMatrix) * DRW_RESOURCE_CHUNK_LEN; - DST.vmempool->obmats = BLI_memblock_create_ex(sizeof(DRWObjectMatrix), chunk_len); - } - if (DST.vmempool->obinfos == NULL) { - uint chunk_len = sizeof(DRWObjectInfos) * DRW_RESOURCE_CHUNK_LEN; - DST.vmempool->obinfos = BLI_memblock_create_ex(sizeof(DRWObjectInfos), chunk_len); - } - if (DST.vmempool->cullstates == NULL) { - uint chunk_len = sizeof(DRWCullingState) * DRW_RESOURCE_CHUNK_LEN; - DST.vmempool->cullstates = BLI_memblock_create_ex(sizeof(DRWCullingState), chunk_len); - } - if (DST.vmempool->shgroups == NULL) { - DST.vmempool->shgroups = BLI_memblock_create(sizeof(DRWShadingGroup)); - } - if (DST.vmempool->uniforms == NULL) { - DST.vmempool->uniforms = BLI_memblock_create(sizeof(DRWUniformChunk)); - } - if (DST.vmempool->views == NULL) { - DST.vmempool->views = BLI_memblock_create(sizeof(DRWView)); - } - if (DST.vmempool->passes == NULL) { - uint chunk_len = sizeof(DRWPass) * DRW_RESOURCE_CHUNK_LEN; - DST.vmempool->passes = BLI_memblock_create_ex(sizeof(DRWPass), chunk_len); - } - if (DST.vmempool->images == NULL) { - DST.vmempool->images = BLI_memblock_create(sizeof(GPUTexture *)); - } - if (DST.vmempool->obattrs_ubo_pool == NULL) { - DST.vmempool->obattrs_ubo_pool = DRW_uniform_attrs_pool_new(); - } + for (int i = 0; i < 2; i++) { + drw_data->view_data[i] = DRW_view_data_create(&g_registered_engines.engines); + } + return drw_data; +} - DST.resource_handle = 0; - DST.pass_handle = 0; +/* Reduce ref count of the textures used by a viewport. */ +static void draw_texture_release(DRWData *drw_data) +{ + /* Release Image textures. */ + BLI_memblock_iter iter; + GPUTexture **tex; + BLI_memblock_iternew(drw_data->images, &iter); + while ((tex = BLI_memblock_iterstep(&iter))) { + GPU_texture_free(*tex); + } +} - draw_unit_state_create(); +static void drw_viewport_data_reset(DRWData *drw_data) +{ + draw_texture_release(drw_data); - DST.idatalist = GPU_viewport_instance_data_list_get(DST.viewport); - DRW_instance_data_list_reset(DST.idatalist); + BLI_memblock_clear(drw_data->commands, NULL); + BLI_memblock_clear(drw_data->commands_small, NULL); + BLI_memblock_clear(drw_data->callbuffers, NULL); + BLI_memblock_clear(drw_data->obmats, NULL); + BLI_memblock_clear(drw_data->obinfos, NULL); + BLI_memblock_clear(drw_data->cullstates, NULL); + BLI_memblock_clear(drw_data->shgroups, NULL); + BLI_memblock_clear(drw_data->uniforms, NULL); + BLI_memblock_clear(drw_data->passes, NULL); + BLI_memblock_clear(drw_data->views, NULL); + BLI_memblock_clear(drw_data->images, NULL); + DRW_uniform_attrs_pool_clear_all(drw_data->obattrs_ubo_pool); + DRW_instance_data_list_free_unused(drw_data->idatalist); + DRW_instance_data_list_resize(drw_data->idatalist); + DRW_instance_data_list_reset(drw_data->idatalist); + DRW_texture_pool_reset(drw_data->texture_pool); +} + +void DRW_viewport_data_free(DRWData *drw_data) +{ + draw_texture_release(drw_data); + + BLI_memblock_destroy(drw_data->commands, NULL); + BLI_memblock_destroy(drw_data->commands_small, NULL); + BLI_memblock_destroy(drw_data->callbuffers, NULL); + BLI_memblock_destroy(drw_data->obmats, NULL); + BLI_memblock_destroy(drw_data->obinfos, NULL); + BLI_memblock_destroy(drw_data->cullstates, NULL); + BLI_memblock_destroy(drw_data->shgroups, NULL); + BLI_memblock_destroy(drw_data->uniforms, NULL); + BLI_memblock_destroy(drw_data->views, NULL); + BLI_memblock_destroy(drw_data->passes, NULL); + BLI_memblock_destroy(drw_data->images, NULL); + DRW_uniform_attrs_pool_free(drw_data->obattrs_ubo_pool); + DRW_instance_data_list_free(drw_data->idatalist); + DRW_texture_pool_free(drw_data->texture_pool); + for (int i = 0; i < 2; i++) { + DRW_view_data_free(drw_data->view_data[i]); + } + if (drw_data->matrices_ubo != NULL) { + for (int i = 0; i < drw_data->ubo_len; i++) { + GPU_uniformbuf_free(drw_data->matrices_ubo[i]); + GPU_uniformbuf_free(drw_data->obinfos_ubo[i]); + } + MEM_freeN(drw_data->matrices_ubo); + MEM_freeN(drw_data->obinfos_ubo); + } + MEM_freeN(drw_data); +} + +static DRWData *drw_viewport_data_ensure(GPUViewport *viewport) +{ + DRWData **vmempool_p = GPU_viewport_data_get(viewport); + DRWData *vmempool = *vmempool_p; + + if (vmempool == NULL) { + *vmempool_p = vmempool = DRW_viewport_data_create(); + } + return vmempool; +} + +/** + * Sets DST.viewport, DST.size and a lot of other important variables. + * Needs to be called before enabling any draw engine. + * - viewport can be NULL. In this case the data will not be stored and will be free at + * drw_manager_exit(). + * - size can be NULL to get it from viewport. + * - if viewport and size are NULL, size is set to (1, 1). + * + * Important: drw_manager_init can be called multiple times before drw_manager_exit. + */ +static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int size[2]) +{ + RegionView3D *rv3d = dst->draw_ctx.rv3d; + ARegion *region = dst->draw_ctx.region; + + int view = (viewport) ? GPU_viewport_active_view_get(viewport) : 0; + + if (!dst->viewport && dst->vmempool) { + /* Manager was init first without a viewport, created DRWData, but is being re-init. + * In this case, keep the old data. */ + /* If it is being re-init with a valid viewport, it means there is something wrong. */ + BLI_assert(viewport == NULL); + } + else if (viewport) { + /* Use viewport's persistent DRWData. */ + dst->vmempool = drw_viewport_data_ensure(viewport); } else { - DST.size[0] = 0; - DST.size[1] = 0; - - DST.inv_size[0] = 0; - DST.inv_size[1] = 0; - - DST.default_framebuffer = NULL; - DST.vmempool = NULL; + /* Create temporary DRWData. Freed in drw_manager_exit(). */ + dst->vmempool = DRW_viewport_data_create(); } - DST.primary_view_ct = 0; + dst->viewport = viewport; + dst->view_data_active = dst->vmempool->view_data[view]; + dst->resource_handle = 0; + dst->pass_handle = 0; + dst->primary_view_ct = 0; + + drw_viewport_data_reset(dst->vmempool); + + if (size == NULL && viewport == NULL) { + /* Avoid division by 0. Engines will either overide this or not use it. */ + dst->size[0] = 1.0f; + dst->size[1] = 1.0f; + } + else if (size == NULL) { + BLI_assert(viewport); + GPUTexture *tex = GPU_viewport_color_texture(viewport, 0); + dst->size[0] = GPU_texture_width(tex); + dst->size[1] = GPU_texture_height(tex); + } + else { + BLI_assert(size); + dst->size[0] = size[0]; + dst->size[1] = size[1]; + } + dst->inv_size[0] = 1.0f / dst->size[0]; + dst->inv_size[1] = 1.0f / dst->size[1]; + + DRW_view_data_texture_list_size_validate(dst->view_data_active, (int[2]){UNPACK2(dst->size)}); + + if (viewport) { + DRW_view_data_default_lists_from_viewport(dst->view_data_active, viewport); + } + + DefaultFramebufferList *dfbl = DRW_view_data_default_framebuffer_list_get(dst->view_data_active); + dst->default_framebuffer = dfbl->default_fb; + + draw_unit_state_create(); if (rv3d != NULL) { - normalize_v3_v3(DST.screenvecs[0], rv3d->viewinv[0]); - normalize_v3_v3(DST.screenvecs[1], rv3d->viewinv[1]); + normalize_v3_v3(dst->screenvecs[0], rv3d->viewinv[0]); + normalize_v3_v3(dst->screenvecs[1], rv3d->viewinv[1]); - DST.pixsize = rv3d->pixsize; - DST.view_default = DRW_view_create(rv3d->viewmat, rv3d->winmat, NULL, NULL, NULL); - DRW_view_camtexco_set(DST.view_default, rv3d->viewcamtexcofac); + dst->pixsize = rv3d->pixsize; + dst->view_default = DRW_view_create(rv3d->viewmat, rv3d->winmat, NULL, NULL, NULL); + DRW_view_camtexco_set(dst->view_default, rv3d->viewcamtexcofac); - if (DST.draw_ctx.sh_cfg == GPU_SHADER_CFG_CLIPPED) { + if (dst->draw_ctx.sh_cfg == GPU_SHADER_CFG_CLIPPED) { int plane_len = (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXCLIP) ? 4 : 6; - DRW_view_clip_planes_set(DST.view_default, rv3d->clip, plane_len); + DRW_view_clip_planes_set(dst->view_default, rv3d->clip, plane_len); } - DST.view_active = DST.view_default; - DST.view_previous = NULL; + dst->view_active = dst->view_default; + dst->view_previous = NULL; } else if (region) { View2D *v2d = ®ion->v2d; @@ -581,49 +624,64 @@ static void drw_viewport_var_init(void) winmat[3][0] = -1.0f; winmat[3][1] = -1.0f; - DST.view_default = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); - DST.view_active = DST.view_default; - DST.view_previous = NULL; + dst->view_default = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); + dst->view_active = dst->view_default; + dst->view_previous = NULL; } else { - zero_v3(DST.screenvecs[0]); - zero_v3(DST.screenvecs[1]); + zero_v3(dst->screenvecs[0]); + zero_v3(dst->screenvecs[1]); - DST.pixsize = 1.0f; - DST.view_default = NULL; - DST.view_active = NULL; - DST.view_previous = NULL; + dst->pixsize = 1.0f; + dst->view_default = NULL; + dst->view_active = NULL; + dst->view_previous = NULL; } /* fclem: Is this still needed ? */ - if (DST.draw_ctx.object_edit && rv3d) { - ED_view3d_init_mats_rv3d(DST.draw_ctx.object_edit, rv3d); + if (dst->draw_ctx.object_edit && rv3d) { + ED_view3d_init_mats_rv3d(dst->draw_ctx.object_edit, rv3d); } if (G_draw.view_ubo == NULL) { G_draw.view_ubo = GPU_uniformbuf_create_ex(sizeof(DRWViewUboStorage), NULL, "G_draw.view_ubo"); } - if (DST.draw_list == NULL) { - DST.draw_list = GPU_draw_list_create(DRW_DRAWLIST_LEN); + if (dst->draw_list == NULL) { + dst->draw_list = GPU_draw_list_create(DRW_DRAWLIST_LEN); } - memset(DST.object_instance_data, 0x0, sizeof(DST.object_instance_data)); + memset(dst->object_instance_data, 0x0, sizeof(dst->object_instance_data)); +} + +static void drw_manager_exit(DRWManager *dst) +{ + if (dst->vmempool != NULL && dst->viewport == NULL) { + DRW_viewport_data_free(dst->vmempool); + } + dst->vmempool = NULL; + dst->viewport = NULL; +#ifdef DEBUG + /* Avoid accidental reuse. */ + drw_state_ensure_not_reused(dst); +#endif } DefaultFramebufferList *DRW_viewport_framebuffer_list_get(void) { - return GPU_viewport_framebuffer_list_get(DST.viewport); + return DRW_view_data_default_framebuffer_list_get(DST.view_data_active); } DefaultTextureList *DRW_viewport_texture_list_get(void) { - return GPU_viewport_texture_list_get(DST.viewport); + return DRW_view_data_default_texture_list_get(DST.view_data_active); } void DRW_viewport_request_redraw(void) { - GPU_viewport_tag_update(DST.viewport); + if (DST.viewport) { + GPU_viewport_tag_update(DST.viewport); + } } /** \} */ @@ -671,7 +729,7 @@ static void drw_duplidata_load(Object *ob) void **value; if (!BLI_ghash_ensure_p(DST.dupli_ghash, key, &value)) { - *value = MEM_callocN(sizeof(void *) * DST.enabled_engine_count, __func__); + *value = MEM_callocN(sizeof(void *) * g_registered_engines.len, __func__); /* TODO: Meh a bit out of place but this is nice as it is * only done once per instance type. */ @@ -686,7 +744,7 @@ static void drw_duplidata_load(Object *ob) static void duplidata_value_free(void *val) { void **dupli_datas = val; - for (int i = 0; i < DST.enabled_engine_count; i++) { + for (int i = 0; i < g_registered_engines.len; i++) { MEM_SAFE_FREE(dupli_datas[i]); } MEM_freeN(val); @@ -720,13 +778,9 @@ void **DRW_duplidata_get(void *vedata) if (DST.dupli_source == NULL) { return NULL; } - /* XXX Search engine index by using vedata array */ - for (int i = 0; i < DST.enabled_engine_count; i++) { - if (DST.vedata_array[i] == vedata) { - return &DST.dupli_datas[i]; - } - } - return NULL; + ViewportEngineData *ved = (ViewportEngineData *)vedata; + DRWRegisteredDrawEngine *engine_type = (DRWRegisteredDrawEngine *)ved->engine_type; + return &DST.dupli_datas[engine_type->index]; } /** \} */ @@ -873,7 +927,7 @@ DrawData *DRW_drawdata_ensure(ID *id, size_t fsize = size / sizeof(float); BLI_assert(fsize < MAX_INSTANCE_DATA_SIZE); if (DST.object_instance_data[fsize] == NULL) { - DST.object_instance_data[fsize] = DRW_instance_data_request(DST.idatalist, fsize); + DST.object_instance_data[fsize] = DRW_instance_data_request(DST.vmempool->idatalist, fsize); } dd = (DrawData *)DRW_instance_data_next(DST.object_instance_data[fsize]); memset(dd, 0, size); @@ -967,11 +1021,12 @@ void DRW_cache_free_old_batches(Main *bmain) static void drw_engines_init(void) { - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { PROFILE_START(stime); + const DrawEngineDataSize *data_size = engine->vedata_size; + memset(data->psl->passes, 0, sizeof(*data->psl->passes) * data_size->psl_len); + if (engine->engine_init) { engine->engine_init(data); } @@ -982,15 +1037,7 @@ static void drw_engines_init(void) static void drw_engines_cache_init(void) { - DST.enabled_engine_count = BLI_listbase_count(&DST.enabled_engines); - DST.vedata_array = MEM_mallocN(sizeof(void *) * DST.enabled_engine_count, __func__); - - int i = 0; - for (LinkData *link = DST.enabled_engines.first; link; link = link->next, i++) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); - DST.vedata_array[i] = data; - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (data->text_draw_cache) { DRW_text_cache_destroy(data->text_draw_cache); data->text_draw_cache = NULL; @@ -1011,10 +1058,7 @@ static void drw_engines_world_update(Scene *scene) return; } - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (engine->id_update) { engine->id_update(data, &scene->world->id); } @@ -1036,11 +1080,7 @@ static void drw_engines_cache_populate(Object *ob) drw_batch_cache_validate(ob); } - int i = 0; - for (LinkData *link = DST.enabled_engines.first; link; link = link->next, i++) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = DST.vedata_array[i]; - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (engine->id_update) { engine->id_update(data, &ob->id); } @@ -1063,25 +1103,17 @@ static void drw_engines_cache_populate(Object *ob) static void drw_engines_cache_finish(void) { - int i = 0; - for (LinkData *link = DST.enabled_engines.first; link; link = link->next, i++) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = DST.vedata_array[i]; - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (engine->cache_finish) { engine->cache_finish(data); } } - MEM_freeN(DST.vedata_array); } static void drw_engines_draw_scene(void) { - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { PROFILE_START(stime); - if (engine->draw_scene) { DRW_stats_group_start(engine->idname); engine->draw_scene(data); @@ -1091,7 +1123,6 @@ static void drw_engines_draw_scene(void) } DRW_stats_group_end(); } - PROFILE_END_UPDATE(data->render_time, stime); } /* Reset state after drawing */ @@ -1100,9 +1131,7 @@ static void drw_engines_draw_scene(void) static void drw_engines_draw_text(void) { - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { PROFILE_START(stime); if (data->text_draw_cache) { @@ -1116,10 +1145,7 @@ static void drw_engines_draw_text(void) /* Draw render engine info. */ void DRW_draw_region_engine_info(int xoffset, int *yoffset, int line_height) { - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (data->info[0] != '\0') { char *chr_current = data->info; char *chr_start = chr_current; @@ -1158,12 +1184,10 @@ void DRW_draw_region_engine_info(int xoffset, int *yoffset, int line_height) static void use_drw_engine(DrawEngineType *engine) { - LinkData *ld = MEM_callocN(sizeof(LinkData), "enabled engine link data"); - ld->data = engine; - BLI_addtail(&DST.enabled_engines, ld); + DRW_view_data_use_engine(DST.view_data_active, engine); } -/* Gather all draw engines needed and store them in DST.enabled_engines +/* Gather all draw engines needed and store them in DST.view_data_active * That also define the rendering order of engines */ static void drw_engines_enable_from_engine(const RenderEngineType *engine_type, eDrawType drawtype) { @@ -1252,22 +1276,12 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer), static void drw_engines_disable(void) { - BLI_freelistN(&DST.enabled_engines); + DRW_view_data_reset(DST.view_data_active); } static void drw_engines_data_validate(void) { - int enabled_engines = BLI_listbase_count(&DST.enabled_engines); - void **engine_handle_array = BLI_array_alloca(engine_handle_array, enabled_engines + 1); - int i = 0; - - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - engine_handle_array[i++] = engine; - } - engine_handle_array[i] = NULL; - - GPU_viewport_engines_data_validate(DST.viewport, engine_handle_array); + DRW_view_data_free_unused(DST.view_data_active); } /* Fast check to see if gpencil drawing engine is needed. @@ -1294,55 +1308,57 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx) Scene *scene = update_ctx->scene; ViewLayer *view_layer = update_ctx->view_layer; + GPUViewport *viewport = WM_draw_region_get_viewport(region); + if (!viewport) { + return; + } + const bool gpencil_engine_needed = drw_gpencil_engine_needed(depsgraph, v3d); + /* XXX Really nasty locking. But else this could + * be executed by the material previews thread + * while rendering a viewport. */ + BLI_ticket_mutex_lock(DST.gl_context_mutex); + + /* Reset before using it. */ + drw_state_prepare_clean_for_draw(&DST); + + DST.draw_ctx = (DRWContextState){ + .region = region, + .rv3d = rv3d, + .v3d = v3d, + .scene = scene, + .view_layer = view_layer, + .obact = OBACT(view_layer), + .engine_type = engine_type, + .depsgraph = depsgraph, + .object_mode = OB_MODE_OBJECT, + }; + + /* Custom lightweight init to avoid reseting the mempools. */ + DST.viewport = viewport; + DST.vmempool = drw_viewport_data_ensure(DST.viewport); + /* Separate update for each stereo view. */ - for (int view = 0; view < 2; view++) { - GPUViewport *viewport = WM_draw_region_get_viewport(region); - if (!viewport) { - continue; - } - - /* XXX Really nasty locking. But else this could - * be executed by the material previews thread - * while rendering a viewport. */ - BLI_ticket_mutex_lock(DST.gl_context_mutex); - - /* Reset before using it. */ - drw_state_prepare_clean_for_draw(&DST); - - DST.viewport = viewport; - GPU_viewport_active_view_set(viewport, view); - DST.draw_ctx = (DRWContextState){ - .region = region, - .rv3d = rv3d, - .v3d = v3d, - .scene = scene, - .view_layer = view_layer, - .obact = OBACT(view_layer), - .engine_type = engine_type, - .depsgraph = depsgraph, - .object_mode = OB_MODE_OBJECT, - }; + int view_count = GPU_viewport_is_stereo_get(viewport) ? 2 : 1; + for (int view = 0; view < view_count; view++) { + DST.view_data_active = DST.vmempool->view_data[view]; drw_engines_enable(view_layer, engine_type, gpencil_engine_needed); drw_engines_data_validate(); - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *draw_engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(draw_engine); - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, draw_engine, data) { if (draw_engine->view_update) { draw_engine->view_update(data); } } - DST.viewport = NULL; - drw_engines_disable(); - - BLI_ticket_mutex_unlock(DST.gl_context_mutex); } + + drw_manager_exit(&DST); + + BLI_ticket_mutex_unlock(DST.gl_context_mutex); } /** \} */ @@ -1513,8 +1529,6 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, RegionView3D *rv3d = region->regiondata; DST.draw_ctx.evil_C = evil_C; - DST.viewport = viewport; - /* Setup viewport */ DST.draw_ctx = (DRWContextState){ .region = region, .rv3d = rv3d, @@ -1531,8 +1545,8 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, drw_task_graph_init(); drw_context_state_init(); - drw_viewport_var_init(); - DRW_viewport_colormanagement_set(DST.viewport); + drw_manager_init(&DST, viewport, NULL); + DRW_viewport_colormanagement_set(viewport); const int object_type_exclude_viewport = v3d->object_type_exclude_viewport; /* Check if scene needs to perform the populate loop */ @@ -1591,7 +1605,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_render_instance_buffer_finish(); #ifdef USE_PROFILE - double *cache_time = GPU_viewport_cache_time_get(DST.viewport); + double *cache_time = DRW_view_data_cache_time_get(DST.view_data_active); PROFILE_END_UPDATE(*cache_time, stime); #endif } @@ -1631,12 +1645,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_state_reset(); drw_engines_disable(); - drw_viewport_cache_resize(); - -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); } void DRW_draw_render_loop(struct Depsgraph *depsgraph, @@ -1674,6 +1683,9 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph, GPU_viewport_bind_from_offscreen(render_viewport, ofs); + /* Just here to avoid an assert but shouldn't be required in practice. */ + GPU_framebuffer_restore(); + /* Reset before using it. */ drw_state_prepare_clean_for_draw(&DST); DST.options.is_image_render = is_image_render; @@ -1735,7 +1747,8 @@ static void DRW_render_gpencil_to_image(RenderEngine *engine, const rcti *rect) { if (draw_engine_gpencil_type.render_to_image) { - ViewportEngineData *gpdata = drw_viewport_engine_data_ensure(&draw_engine_gpencil_type); + ViewportEngineData *gpdata = DRW_view_data_engine_data_get_ensure(DST.view_data_active, + &draw_engine_gpencil_type); draw_engine_gpencil_type.render_to_image(gpdata, engine, render_layer, rect); } } @@ -1769,11 +1782,9 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph }; drw_context_state_init(); - DST.viewport = GPU_viewport_create(); const int size[2] = {engine->resolution_x, engine->resolution_y}; - GPU_viewport_size_set(DST.viewport, size); - drw_viewport_var_init(); + drw_manager_init(&DST, NULL, size); /* Main rendering. */ rctf view_rect; @@ -1793,13 +1804,12 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph DRW_render_gpencil_to_image(engine, render_layer, &render_rect); } - /* Force cache to reset. */ - drw_viewport_cache_resize(); - GPU_viewport_free(DST.viewport); DRW_state_reset(); GPU_depth_test(GPU_DEPTH_NONE); + drw_manager_exit(&DST); + /* Restore Drawing area. */ GPU_framebuffer_restore(); @@ -1848,13 +1858,12 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) }; drw_context_state_init(); - DST.viewport = GPU_viewport_create(); const int size[2] = {engine->resolution_x, engine->resolution_y}; - GPU_viewport_size_set(DST.viewport, size); - drw_viewport_var_init(); + drw_manager_init(&DST, NULL, size); - ViewportEngineData *data = drw_viewport_engine_data_ensure(draw_engine_type); + ViewportEngineData *data = DRW_view_data_engine_data_get_ensure(DST.view_data_active, + draw_engine_type); /* Main rendering. */ rctf view_rect; @@ -1898,16 +1907,9 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) engine_type->draw_engine->store_metadata(data, final_render_result); } - /* Force cache to reset. */ - drw_viewport_cache_resize(); - - GPU_viewport_free(DST.viewport); GPU_framebuffer_restore(); -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); /* Reset state after drawing */ DRW_state_reset(); @@ -1976,21 +1978,17 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, }; drw_context_state_init(); - DST.viewport = GPU_viewport_create(); - const int size[2] = {1, 1}; - GPU_viewport_size_set(DST.viewport, size); - - drw_viewport_var_init(); + drw_manager_init(&DST, NULL, NULL); DRW_hair_init(); - ViewportEngineData *data = drw_viewport_engine_data_ensure(draw_engine_type); + ViewportEngineData *data = DRW_view_data_engine_data_get_ensure(DST.view_data_active, + draw_engine_type); /* Execute the callback */ callback(data, user_data); DST.buffer_finish_called = false; - GPU_viewport_free(DST.viewport); GPU_framebuffer_restore(); /* The use of custom pipeline in other thread using the same @@ -1999,33 +1997,18 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, * GPU_finish to sync seems to fix the issue. (see T62997) */ GPU_finish(); -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); } /* Used when the render engine want to redo another cache populate inside the same render frame. */ void DRW_cache_restart(void) { - /* Save viewport size. */ - float size[2], inv_size[2]; - copy_v2_v2(size, DST.size); - copy_v2_v2(inv_size, DST.inv_size); - - /* Force cache to reset. */ - drw_viewport_cache_resize(); - - drw_viewport_var_init(); + drw_manager_init(&DST, DST.viewport, (int[2]){UNPACK2(DST.size)}); DST.buffer_finish_called = false; DRW_hair_init(); - - /* Restore. */ - copy_v2_v2(DST.size, size); - copy_v2_v2(DST.inv_size, inv_size); } void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, @@ -2037,9 +2020,6 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph); DST.draw_ctx.evil_C = evil_C; - DST.viewport = viewport; - - /* Setup viewport */ DST.draw_ctx = (DRWContextState){ .region = region, .scene = scene, @@ -2053,8 +2033,8 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, }; drw_context_state_init(); - drw_viewport_var_init(); - DRW_viewport_colormanagement_set(DST.viewport); + drw_manager_init(&DST, viewport, NULL); + DRW_viewport_colormanagement_set(viewport); /* TODO(jbakker): Only populate when editor needs to draw object. * for the image editor this is when showing UV's. */ @@ -2098,7 +2078,7 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, DRW_render_instance_buffer_finish(); #ifdef USE_PROFILE - double *cache_time = GPU_viewport_cache_time_get(DST.viewport); + double *cache_time = DRW_view_data_cache_time_get(DST.view_data_active); PROFILE_END_UPDATE(*cache_time, stime); #endif } @@ -2180,12 +2160,7 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, DRW_state_reset(); drw_engines_disable(); - drw_viewport_cache_resize(); - -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); } static struct DRWSelectBuffer { @@ -2222,7 +2197,7 @@ void DRW_render_instance_buffer_finish(void) { BLI_assert_msg(!DST.buffer_finish_called, "DRW_render_instance_buffer_finish called twice!"); DST.buffer_finish_called = true; - DRW_instance_buffer_finish(DST.idatalist); + DRW_instance_buffer_finish(DST.vmempool->idatalist); drw_resource_buffer_finish(DST.vmempool); } @@ -2305,11 +2280,22 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, } } - const int viewport_size[2] = {BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)}; - struct GPUViewport *viewport = GPU_viewport_create(); - GPU_viewport_size_set(viewport, viewport_size); + /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */ + DST.draw_ctx = (DRWContextState){ + .region = region, + .rv3d = rv3d, + .v3d = v3d, + .scene = scene, + .view_layer = view_layer, + .obact = obact, + .engine_type = engine_type, + .depsgraph = depsgraph, + }; + drw_context_state_init(); + + const int viewport_size[2] = {BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)}; + drw_manager_init(&DST, NULL, viewport_size); - DST.viewport = viewport; DST.options.is_select = true; DST.options.is_material_select = do_material_sub_selection; drw_task_graph_init(); @@ -2337,22 +2323,6 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, } drw_engines_data_validate(); - /* Setup viewport */ - - /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */ - DST.draw_ctx = (DRWContextState){ - .region = region, - .rv3d = rv3d, - .v3d = v3d, - .scene = scene, - .view_layer = view_layer, - .obact = obact, - .engine_type = engine_type, - .depsgraph = depsgraph, - }; - drw_context_state_init(); - drw_viewport_var_init(); - /* Update UBO's */ DRW_globals_update(); @@ -2456,14 +2426,10 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, DRW_state_reset(); drw_engines_disable(); -# ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -# endif + drw_manager_exit(&DST); + GPU_framebuffer_restore(); - /* Cleanup for selection state */ - GPU_viewport_free(viewport); #endif /* USE_GPU_SELECT */ } @@ -2485,7 +2451,6 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, DRW_opengl_context_enable(); } - DST.viewport = viewport; DST.options.is_depth = true; /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */ @@ -2499,18 +2464,21 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, .engine_type = engine_type, .depsgraph = depsgraph, }; + drw_context_state_init(); drw_task_graph_init(); - drw_engines_data_validate(); /* Setup frame-buffer. */ - DefaultFramebufferList *fbl = (DefaultFramebufferList *)GPU_viewport_framebuffer_list_get( - DST.viewport); - GPU_framebuffer_bind(fbl->depth_only_fb); - GPU_framebuffer_clear_depth(fbl->depth_only_fb, 1.0f); + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); - /* Setup viewport */ - drw_context_state_init(); - drw_viewport_var_init(); + GPUFrameBuffer *depth_fb = NULL; + GPU_framebuffer_ensure_config(&depth_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_NONE, + }); + + GPU_framebuffer_bind(depth_fb); + GPU_framebuffer_clear_depth(depth_fb, 1.0f); /* Update UBO's */ DRW_globals_update(); @@ -2559,15 +2527,11 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, /* TODO: Reading depth for operators should be done here. */ GPU_framebuffer_restore(); + GPU_framebuffer_free(depth_fb); drw_engines_disable(); - drw_viewport_cache_resize(); - -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); /* Changing context. */ if (use_opengl_context) { @@ -2586,6 +2550,11 @@ void DRW_draw_depth_loop(struct Depsgraph *depsgraph, /* Reset before using it. */ drw_state_prepare_clean_for_draw(&DST); + /* Required by `drw_manager_init()` */ + DST.draw_ctx.region = region; + DST.draw_ctx.rv3d = region->regiondata; + drw_manager_init(&DST, viewport, NULL); + /* Get list of enabled engines */ { /* Required by `DRW_state_draw_support()` */ @@ -2611,6 +2580,11 @@ void DRW_draw_depth_loop_gpencil(struct Depsgraph *depsgraph, /* Reset before using it. */ drw_state_prepare_clean_for_draw(&DST); + /* Required by `drw_manager_init()` */ + DST.draw_ctx.region = region; + DST.draw_ctx.rv3d = region->regiondata; + drw_manager_init(&DST, viewport, NULL); + use_drw_engine(&draw_engine_gpencil_type); drw_draw_depth_loop_impl(depsgraph, region, v3d, viewport, false); @@ -2648,9 +2622,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons drw_task_graph_init(); drw_context_state_init(); - /* Setup viewport */ - DST.viewport = viewport; - drw_viewport_var_init(); + drw_manager_init(&DST, viewport, NULL); /* Update UBO's */ UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW); @@ -2677,7 +2649,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons DRW_render_instance_buffer_finish(); #else DST.buffer_finish_called = true; - // DRW_instance_buffer_finish(DST.idatalist); + // DRW_instance_buffer_finish(DST.vmempool->idatalist); drw_resource_buffer_finish(DST.vmempool); #endif } @@ -2689,12 +2661,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons drw_engines_disable(); - drw_viewport_cache_resize(); - -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); } /** @@ -2710,10 +2677,17 @@ void DRW_draw_depth_object( GPU_matrix_mul(object->obmat); /* Setup frame-buffer. */ - DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport); + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); - GPU_framebuffer_bind(fbl->depth_only_fb); - GPU_framebuffer_clear_depth(fbl->depth_only_fb, 1.0f); + GPUFrameBuffer *depth_fb = NULL; + GPU_framebuffer_ensure_config(&depth_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_NONE, + }); + + GPU_framebuffer_bind(depth_fb); + GPU_framebuffer_clear_depth(depth_fb, 1.0f); GPU_depth_test(GPU_DEPTH_LESS_EQUAL); const float(*world_clip_planes)[4] = NULL; @@ -2763,6 +2737,9 @@ void DRW_draw_depth_object( GPU_matrix_set(rv3d->viewmat); GPU_depth_test(GPU_DEPTH_NONE); GPU_framebuffer_restore(); + + GPU_framebuffer_free(depth_fb); + DRW_opengl_context_disable(); } /** \} */ @@ -2895,7 +2872,12 @@ bool DRW_engine_render_support(DrawEngineType *draw_engine_type) void DRW_engine_register(DrawEngineType *draw_engine_type) { - BLI_addtail(&DRW_engines, draw_engine_type); + DRWRegisteredDrawEngine *draw_engine = MEM_mallocN(sizeof(DRWRegisteredDrawEngine), __func__); + draw_engine->draw_engine = draw_engine_type; + draw_engine->index = g_registered_engines.len; + + BLI_addtail(&g_registered_engines.engines, draw_engine); + g_registered_engines.len = BLI_listbase_count(&g_registered_engines.engines); } void DRW_engines_register(void) @@ -2966,14 +2948,15 @@ void DRW_engines_free(void) DRW_stats_free(); DRW_globals_free(); - DrawEngineType *next; - for (DrawEngineType *type = DRW_engines.first; type; type = next) { + DRWRegisteredDrawEngine *next; + for (DRWRegisteredDrawEngine *type = g_registered_engines.engines.first; type; type = next) { next = type->next; BLI_remlink(&R_engines, type); - if (type->engine_free) { - type->engine_free(); + if (type->draw_engine->engine_free) { + type->draw_engine->engine_free(); } + MEM_freeN(type); } DRW_UBO_FREE_SAFE(G_draw.block_ubo); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index c09126c98ef..1bb1ee06354 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -97,6 +97,17 @@ struct Object; #endif /* USE_PROFILE */ /* ------------ Data Structure --------------- */ +/** + * Data structure to for registered draw engines that can store draw manager + * specific data. + */ +typedef struct DRWRegisteredDrawEngine { + void /*DRWRegisteredDrawEngine*/ *next, *prev; + DrawEngineType *draw_engine; + /** Index of the type in the lists. Index is used for dupli data. */ + int index; +} DRWRegisteredDrawEngine; + /** * Data structure containing all drawcalls organized by passes and materials. * DRWPass > DRWShadingGroup > DRWCall > DRWCallState @@ -495,6 +506,35 @@ typedef struct DRWDebugSphere { float color[4]; } DRWDebugSphere; +/* ------------- Memory Pools ------------ */ + +/* Contains memory pools information */ +typedef struct DRWData { + /** Instance data. */ + DRWInstanceDataList *idatalist; + /** Mempools for drawcalls. */ + struct BLI_memblock *commands; + struct BLI_memblock *commands_small; + struct BLI_memblock *callbuffers; + struct BLI_memblock *obmats; + struct BLI_memblock *obinfos; + struct BLI_memblock *cullstates; + struct BLI_memblock *shgroups; + struct BLI_memblock *uniforms; + struct BLI_memblock *views; + struct BLI_memblock *passes; + struct BLI_memblock *images; + struct GPUUniformBuf **matrices_ubo; + struct GPUUniformBuf **obinfos_ubo; + struct GHash *obattrs_ubo_pool; + uint ubo_len; + /** Texture pool to reuse temp texture across engines. */ + /* TODO(fclem) the pool could be shared even between viewports. */ + struct DRWTexturePool *texture_pool; + /** Per stereo view data. Contains engine data and default framebuffers. */ + struct DRWViewData *view_data[2]; +} DRWData; + /* ------------- DRAW MANAGER ------------ */ typedef struct DupliKey { @@ -509,8 +549,10 @@ typedef struct DupliKey { typedef struct DRWManager { /* TODO: clean up this struct a bit. */ /* Cache generation */ - ViewportMemoryPool *vmempool; - DRWInstanceDataList *idatalist; + /* TODO(fclem) Rename to data. */ + DRWData *vmempool; + /** Active view data structure for one of the 2 stereo view. Not related to DRWView. */ + struct DRWViewData *view_data_active; /* State of the object being evaluated if already allocated. */ DRWResourceHandle ob_handle; /** True if current DST.ob_state has its matching DRWObjectInfos init. */ @@ -567,10 +609,6 @@ typedef struct DRWManager { /* Convenience pointer to text_store owned by the viewport */ struct DRWTextStore **text_store_p; - ListBase enabled_engines; /* RenderEngineType */ - void **vedata_array; /* ViewportEngineData */ - int enabled_engine_count; /* Length of enabled_engines list. */ - bool buffer_finish_called; /* Avoid bad usage of DRW_render_instance_buffer_finish */ DRWView *view_default; @@ -627,7 +665,7 @@ void drw_batch_cache_validate(Object *ob); void drw_batch_cache_generate_requested(struct Object *ob); void drw_batch_cache_generate_requested_delayed(Object *ob); -void drw_resource_buffer_finish(ViewportMemoryPool *vmempool); +void drw_resource_buffer_finish(DRWData *vmempool); /* Procedural Drawing */ GPUBatch *drw_cache_procedural_points_get(void); @@ -641,6 +679,13 @@ void drw_uniform_attrs_pool_update(struct GHash *table, struct Object *dupli_parent, struct DupliObject *dupli_source); +double *drw_engine_data_cache_time_get(GPUViewport *viewport); +void *drw_engine_data_engine_data_create(GPUViewport *viewport, void *engine_type); +void *drw_engine_data_engine_data_get(GPUViewport *viewport, void *engine_handle); +bool drw_engine_data_engines_data_validate(GPUViewport *viewport, void **engine_handle_array); +void drw_engine_data_cache_release(GPUViewport *viewport); +void drw_engine_data_free(GPUViewport *viewport); + #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index af331c86a8b..f96bd474aec 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -87,7 +87,7 @@ static void draw_call_sort(DRWCommand *array, DRWCommand *array_tmp, int array_l memcpy(array, array_tmp, sizeof(*array) * array_len); } -void drw_resource_buffer_finish(ViewportMemoryPool *vmempool) +void drw_resource_buffer_finish(DRWData *vmempool) { int chunk_id = DRW_handle_chunk_get(&DST.resource_handle); int elem_id = DRW_handle_id_get(&DST.resource_handle); @@ -908,7 +908,8 @@ void DRW_shgroup_call_instances_with_attrs(DRWShadingGroup *shgroup, drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - GPUBatch *batch = DRW_temp_batch_instance_request(DST.idatalist, NULL, inst_attributes, geom); + GPUBatch *batch = DRW_temp_batch_instance_request( + DST.vmempool->idatalist, NULL, inst_attributes, geom); drw_command_draw_instance(shgroup, batch, handle, 0, true); } @@ -1128,7 +1129,7 @@ DRWCallBuffer *DRW_shgroup_call_buffer(DRWShadingGroup *shgroup, BLI_assert(format != NULL); DRWCallBuffer *callbuf = BLI_memblock_alloc(DST.vmempool->callbuffers); - callbuf->buf = DRW_temp_buffer_request(DST.idatalist, format, &callbuf->count); + callbuf->buf = DRW_temp_buffer_request(DST.vmempool->idatalist, format, &callbuf->count); callbuf->buf_select = NULL; callbuf->count = 0; @@ -1138,12 +1139,12 @@ DRWCallBuffer *DRW_shgroup_call_buffer(DRWShadingGroup *shgroup, GPU_vertformat_attr_add(&inst_select_format, "selectId", GPU_COMP_I32, 1, GPU_FETCH_INT); } callbuf->buf_select = DRW_temp_buffer_request( - DST.idatalist, &inst_select_format, &callbuf->count); + DST.vmempool->idatalist, &inst_select_format, &callbuf->count); drw_command_set_select_id(shgroup, callbuf->buf_select, -1); } DRWResourceHandle handle = drw_resource_handle(shgroup, NULL, NULL); - GPUBatch *batch = DRW_temp_batch_request(DST.idatalist, callbuf->buf, prim_type); + GPUBatch *batch = DRW_temp_batch_request(DST.vmempool->idatalist, callbuf->buf, prim_type); drw_command_draw(shgroup, batch, handle); return callbuf; @@ -1157,7 +1158,7 @@ DRWCallBuffer *DRW_shgroup_call_buffer_instance(DRWShadingGroup *shgroup, BLI_assert(format != NULL); DRWCallBuffer *callbuf = BLI_memblock_alloc(DST.vmempool->callbuffers); - callbuf->buf = DRW_temp_buffer_request(DST.idatalist, format, &callbuf->count); + callbuf->buf = DRW_temp_buffer_request(DST.vmempool->idatalist, format, &callbuf->count); callbuf->buf_select = NULL; callbuf->count = 0; @@ -1167,12 +1168,13 @@ DRWCallBuffer *DRW_shgroup_call_buffer_instance(DRWShadingGroup *shgroup, GPU_vertformat_attr_add(&inst_select_format, "selectId", GPU_COMP_I32, 1, GPU_FETCH_INT); } callbuf->buf_select = DRW_temp_buffer_request( - DST.idatalist, &inst_select_format, &callbuf->count); + DST.vmempool->idatalist, &inst_select_format, &callbuf->count); drw_command_set_select_id(shgroup, callbuf->buf_select, -1); } DRWResourceHandle handle = drw_resource_handle(shgroup, NULL, NULL); - GPUBatch *batch = DRW_temp_batch_instance_request(DST.idatalist, callbuf->buf, NULL, geom); + GPUBatch *batch = DRW_temp_batch_instance_request( + DST.vmempool->idatalist, callbuf->buf, NULL, geom); drw_command_draw(shgroup, batch, handle); return callbuf; diff --git a/source/blender/draw/intern/draw_manager_profiling.c b/source/blender/draw/intern/draw_manager_profiling.c index d9ba2cbf932..70fe12270f5 100644 --- a/source/blender/draw/intern/draw_manager_profiling.c +++ b/source/blender/draw/intern/draw_manager_profiling.c @@ -257,10 +257,9 @@ void DRW_stats_draw(const rcti *rect) /* Engines rows */ char time_to_txt[16]; - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { + DRW_ENABLED_ENGINE_ITER(DST.view_data_active, engine, data) + { u = 0; - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); draw_stat_5row(rect, u++, v, engine->idname, sizeof(engine->idname)); @@ -297,7 +296,7 @@ void DRW_stats_draw(const rcti *rect) v += 2; u = 0; - double *cache_time = GPU_viewport_cache_time_get(DST.viewport); + double *cache_time = DRW_view_data_cache_time_get(DST.view_data_active); sprintf(col_label, "Cache Time"); draw_stat_5row(rect, u++, v, col_label, sizeof(col_label)); sprintf(time_to_txt, "%.2fms", *cache_time); diff --git a/source/blender/draw/intern/draw_manager_text.h b/source/blender/draw/intern/draw_manager_text.h index 760259018bb..4f3a6153775 100644 --- a/source/blender/draw/intern/draw_manager_text.h +++ b/source/blender/draw/intern/draw_manager_text.h @@ -22,6 +22,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + struct ARegion; struct DRWTextStore; struct Object; @@ -57,3 +61,7 @@ enum { /* draw_manager.c */ struct DRWTextStore *DRW_text_cache_ensure(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/source/blender/draw/intern/draw_manager_texture.c b/source/blender/draw/intern/draw_manager_texture.c index 99e8ba968a2..86242468e4a 100644 --- a/source/blender/draw/intern/draw_manager_texture.c +++ b/source/blender/draw/intern/draw_manager_texture.c @@ -21,6 +21,7 @@ */ #include "draw_manager.h" +#include "draw_texture_pool.h" #ifndef NDEBUG /* Maybe gpu_texture.c is a better place for this. */ @@ -147,7 +148,7 @@ GPUTexture *DRW_texture_pool_query_2d(int w, DrawEngineType *engine_type) { BLI_assert(drw_texture_format_supports_framebuffer(format)); - GPUTexture *tex = GPU_viewport_texture_pool_query(DST.viewport, engine_type, w, h, format); + GPUTexture *tex = DRW_texture_pool_query(DST.vmempool->texture_pool, w, h, format, engine_type); return tex; } diff --git a/source/blender/draw/intern/draw_texture_pool.cc b/source/blender/draw/intern/draw_texture_pool.cc new file mode 100644 index 00000000000..544a763ddb9 --- /dev/null +++ b/source/blender/draw/intern/draw_texture_pool.cc @@ -0,0 +1,140 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ + +#include "BKE_global.h" + +#include "BLI_vector.hh" + +#include "draw_texture_pool.h" + +using namespace blender; + +typedef struct DRWTexturePoolHandle { + uint64_t users_bits; + GPUTexture *texture; +} DRWTexturePoolHandle; + +struct DRWTexturePool { + Vector users; + Vector handles; + /* Cache last result to avoid linear search each time. */ + int last_user_id = -1; +}; + +DRWTexturePool *DRW_texture_pool_create(void) +{ + return new DRWTexturePool(); +} + +void DRW_texture_pool_free(DRWTexturePool *pool) +{ + /* Reseting the pool twice will effectively free all textures. */ + DRW_texture_pool_reset(pool); + DRW_texture_pool_reset(pool); + delete pool; +} + +/** + * Try to find a texture corresponding to params into the texture pool. + * If no texture was found, create one and add it to the pool. + */ +GPUTexture *DRW_texture_pool_query( + DRWTexturePool *pool, int width, int height, eGPUTextureFormat format, void *user) +{ + int user_id = pool->last_user_id; + /* Try cached value. */ + if (user_id != -1) { + if (pool->users[user_id] != user) { + user_id = -1; + } + } + /* Try to find inside previous users. */ + if (user_id == -1) { + user_id = pool->users.first_index_of_try(user); + } + /* No chance, needs to add it to the user list. */ + if (user_id == -1) { + user_id = pool->users.size(); + pool->users.append(user); + /* If there is more than 63 users, better refactor this system. */ + BLI_assert(user_id < 64); + } + pool->last_user_id = user_id; + + uint64_t user_bit = 1llu << user_id; + for (DRWTexturePoolHandle &handle : pool->handles) { + /* Skip if the user is already using this texture. */ + if (user_bit & handle.users_bits) { + continue; + } + /* If everthing matches reuse the texture. */ + if ((GPU_texture_format(handle.texture) == format) && + (GPU_texture_width(handle.texture) == width) && + (GPU_texture_height(handle.texture) == height)) { + handle.users_bits |= user_bit; + return handle.texture; + } + } + + char name[16] = "DRW_tex_pool"; + if (G.debug & G_DEBUG_GPU) { + int texture_id = pool->handles.size(); + SNPRINTF(name, "DRW_tex_pool_%d", texture_id); + } + + DRWTexturePoolHandle handle; + handle.users_bits = user_bit; + handle.texture = GPU_texture_create_2d(name, width, height, 1, format, nullptr); + pool->handles.append(handle); + /* Doing filtering for depth does not make sense when not doing shadow mapping, + * and enabling texture filtering on integer texture make them unreadable. */ + bool do_filter = !GPU_texture_depth(handle.texture) && !GPU_texture_integer(handle.texture); + GPU_texture_filter_mode(handle.texture, do_filter); + + return handle.texture; +} + +/* Resets the user bits for each texture in the pool and delete unused ones. */ +void DRW_texture_pool_reset(DRWTexturePool *pool) +{ + pool->last_user_id = -1; + + for (auto it = pool->handles.rbegin(); it != pool->handles.rend(); ++it) { + DRWTexturePoolHandle &handle = *it; + if (handle.users_bits == 0) { + if (handle.texture) { + GPU_texture_free(handle.texture); + handle.texture = nullptr; + } + } + else { + handle.users_bits = 0; + } + } + + /* Reverse iteration to make sure we only reorder with known good handles. */ + for (int i = pool->handles.size() - 1; i >= 0; i--) { + if (!pool->handles[i].texture) { + pool->handles.remove_and_reorder(i); + } + } +} \ No newline at end of file diff --git a/source/blender/draw/DRW_engine_types.h b/source/blender/draw/intern/draw_texture_pool.h similarity index 54% rename from source/blender/draw/DRW_engine_types.h rename to source/blender/draw/intern/draw_texture_pool.h index 807f654f559..f0b8f775c75 100644 --- a/source/blender/draw/DRW_engine_types.h +++ b/source/blender/draw/intern/draw_texture_pool.h @@ -13,38 +13,34 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * Copyright 2016, Blender Foundation. + * Copyright 2021, Blender Foundation. */ /** \file * \ingroup draw + * + * Texture pool + * A pool that gives temporary render targets that can be reused through other parts of the + * render pipeline. + * Expect texture data is garbage when acquiring it. */ #pragma once +#include "GPU_texture.h" + +typedef struct DRWTexturePool DRWTexturePool; + #ifdef __cplusplus extern "C" { #endif -/* Buffer and textures used by the viewport by default */ -typedef struct DefaultFramebufferList { - struct GPUFrameBuffer *default_fb; - struct GPUFrameBuffer *overlay_fb; - struct GPUFrameBuffer *in_front_fb; - struct GPUFrameBuffer *color_only_fb; - struct GPUFrameBuffer *depth_only_fb; - struct GPUFrameBuffer *overlay_only_fb; - struct GPUFrameBuffer *stereo_comp_fb; -} DefaultFramebufferList; +DRWTexturePool *DRW_texture_pool_create(void); +void DRW_texture_pool_free(DRWTexturePool *pool); -typedef struct DefaultTextureList { - struct GPUTexture *color; - struct GPUTexture *color_overlay; - struct GPUTexture *color_stereo; - struct GPUTexture *color_overlay_stereo; - struct GPUTexture *depth; - struct GPUTexture *depth_in_front; -} DefaultTextureList; +GPUTexture *DRW_texture_pool_query( + DRWTexturePool *pool, int width, int height, eGPUTextureFormat format, void *user); +void DRW_texture_pool_reset(DRWTexturePool *pool); #ifdef __cplusplus } diff --git a/source/blender/draw/intern/draw_view_data.cc b/source/blender/draw/intern/draw_view_data.cc new file mode 100644 index 00000000000..55ebbf82c29 --- /dev/null +++ b/source/blender/draw/intern/draw_view_data.cc @@ -0,0 +1,243 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ + +#include "BLI_vector.hh" + +#include "GPU_viewport.h" + +#include "DRW_render.h" + +#include "draw_instance_data.h" + +#include "draw_manager_text.h" + +#include "draw_manager.h" +#include "draw_view_data.h" + +using namespace blender; + +struct DRWViewData { + DefaultFramebufferList dfbl = {}; + DefaultTextureList dtxl = {}; + /** True indicates the textures inside dtxl are from the viewport and should not be freed. */ + bool from_viewport = false; + /** Common size for texture in the engines texture list. + * We free all texture lists if it changes. */ + int texture_list_size[2] = {0, 0}; + + double cache_time = 0.0; + + Vector engines; + Vector enabled_engines; +}; + +/** + * Creates a view data with all possible engines type for this view. + * + * `engine_types` contains `DRWRegisteredDrawEngine`. + * */ +DRWViewData *DRW_view_data_create(ListBase *engine_types) +{ + DRWViewData *view_data = new DRWViewData(); + LISTBASE_FOREACH (DRWRegisteredDrawEngine *, type, engine_types) { + ViewportEngineData engine = {}; + engine.engine_type = type; + view_data->engines.append(engine); + } + return view_data; +} + +void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, GPUViewport *viewport) +{ + int active_view = GPU_viewport_active_view_get(viewport); + view_data->from_viewport = true; + + DefaultFramebufferList *dfbl = &view_data->dfbl; + DefaultTextureList *dtxl = &view_data->dtxl; + /* Depth texture is shared between both stereo views. */ + dtxl->depth = GPU_viewport_depth_texture(viewport); + dtxl->color = GPU_viewport_color_texture(viewport, active_view); + dtxl->color_overlay = GPU_viewport_overlay_texture(viewport, active_view); + + GPU_framebuffer_ensure_config(&dfbl->default_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(dtxl->color), + }); + GPU_framebuffer_ensure_config(&dfbl->overlay_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), + }); + GPU_framebuffer_ensure_config(&dfbl->depth_only_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_NONE, + }); + GPU_framebuffer_ensure_config(&dfbl->color_only_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(dtxl->color), + }); + GPU_framebuffer_ensure_config(&dfbl->overlay_only_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), + }); +} + +static void draw_viewport_engines_data_clear(ViewportEngineData *data) +{ + DrawEngineType *engine_type = data->engine_type->draw_engine; + const DrawEngineDataSize *data_size = engine_type->vedata_size; + + for (int i = 0; data->fbl && i < data_size->fbl_len; i++) { + GPU_FRAMEBUFFER_FREE_SAFE(data->fbl->framebuffers[i]); + } + for (int i = 0; data->txl && i < data_size->txl_len; i++) { + GPU_TEXTURE_FREE_SAFE(data->txl->textures[i]); + } + for (int i = 0; data->stl && i < data_size->stl_len; i++) { + MEM_SAFE_FREE(data->stl->storage[i]); + } + + MEM_SAFE_FREE(data->fbl); + MEM_SAFE_FREE(data->txl); + MEM_SAFE_FREE(data->psl); + MEM_SAFE_FREE(data->stl); + + if (data->text_draw_cache) { + DRW_text_cache_destroy(data->text_draw_cache); + data->text_draw_cache = nullptr; + } +} + +static void draw_view_data_clear(DRWViewData *view_data) +{ + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.default_fb); + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.overlay_fb); + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.in_front_fb); + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.color_only_fb); + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.depth_only_fb); + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.overlay_only_fb); + + if (!view_data->from_viewport) { + GPU_TEXTURE_FREE_SAFE(view_data->dtxl.color); + GPU_TEXTURE_FREE_SAFE(view_data->dtxl.color_overlay); + GPU_TEXTURE_FREE_SAFE(view_data->dtxl.depth); + } + GPU_TEXTURE_FREE_SAFE(view_data->dtxl.depth_in_front); + + for (ViewportEngineData &engine : view_data->engines) { + draw_viewport_engines_data_clear(&engine); + } + + view_data->texture_list_size[0] = view_data->texture_list_size[1] = 0; + view_data->cache_time = 0.0f; +} + +void DRW_view_data_free(DRWViewData *view_data) +{ + draw_view_data_clear(view_data); + delete view_data; +} + +void DRW_view_data_texture_list_size_validate(DRWViewData *view_data, const int size[2]) +{ + if (!equals_v2v2_int(view_data->texture_list_size, size)) { + draw_view_data_clear(view_data); + copy_v2_v2_int(view_data->texture_list_size, size); + } +} + +ViewportEngineData *DRW_view_data_engine_data_get_ensure(DRWViewData *view_data, + DrawEngineType *engine_type) +{ + for (ViewportEngineData &engine : view_data->engines) { + if (engine.engine_type->draw_engine == engine_type) { + if (engine.fbl == nullptr) { + const DrawEngineDataSize *data_size = engine_type->vedata_size; + engine.fbl = (FramebufferList *)MEM_calloc_arrayN( + data_size->fbl_len, sizeof(GPUFrameBuffer *), "FramebufferList"); + engine.txl = (TextureList *)MEM_calloc_arrayN( + data_size->txl_len, sizeof(GPUTexture *), "TextureList"); + engine.psl = (PassList *)MEM_calloc_arrayN( + data_size->psl_len, sizeof(DRWPass *), "PassList"); + engine.stl = (StorageList *)MEM_calloc_arrayN( + data_size->stl_len, sizeof(void *), "StorageList"); + } + return &engine; + } + } + return nullptr; +} + +void DRW_view_data_use_engine(DRWViewData *view_data, DrawEngineType *engine_type) +{ + ViewportEngineData *engine = DRW_view_data_engine_data_get_ensure(view_data, engine_type); + view_data->enabled_engines.append(engine); +} + +void DRW_view_data_reset(DRWViewData *view_data) +{ + view_data->enabled_engines.clear(); +} + +void DRW_view_data_free_unused(DRWViewData *view_data) +{ + for (ViewportEngineData &engine : view_data->engines) { + if (view_data->enabled_engines.first_index_of_try(&engine) == -1) { + draw_viewport_engines_data_clear(&engine); + } + } +} + +double *DRW_view_data_cache_time_get(DRWViewData *view_data) +{ + return &view_data->cache_time; +} + +DefaultFramebufferList *DRW_view_data_default_framebuffer_list_get(DRWViewData *view_data) +{ + return &view_data->dfbl; +} + +DefaultTextureList *DRW_view_data_default_texture_list_get(DRWViewData *view_data) +{ + return &view_data->dtxl; +} + +void DRW_view_data_enabled_engine_iter_begin(DRWEngineIterator *iterator, DRWViewData *view_data) +{ + iterator->id = 0; + iterator->end = view_data->enabled_engines.size(); + iterator->engines = view_data->enabled_engines.data(); +} + +ViewportEngineData *DRW_view_data_enabled_engine_iter_step(DRWEngineIterator *iterator) +{ + if (iterator->id >= iterator->end) { + return nullptr; + } + ViewportEngineData *engine = iterator->engines[iterator->id++]; + return engine; +} diff --git a/source/blender/draw/intern/draw_view_data.h b/source/blender/draw/intern/draw_view_data.h new file mode 100644 index 00000000000..c8176170a61 --- /dev/null +++ b/source/blender/draw/intern/draw_view_data.h @@ -0,0 +1,139 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw + * + * Engine data + * Structure containing each draw engine instance data. + */ + +#pragma once + +#define GPU_INFO_SIZE 512 /* IMA_MAX_RENDER_TEXT */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct GPUViewport; +struct DrawEngineType; +struct DRWRegisteredDrawEngine; + +/* NOTE these structs are only here for reading the actual lists from the engine. + * The actual length of them is stored in a ViewportEngineData_Info. + * The length of 1 is just here to avoid compiler warning. */ +typedef struct FramebufferList { + struct GPUFrameBuffer *framebuffers[1]; +} FramebufferList; + +typedef struct TextureList { + struct GPUTexture *textures[1]; +} TextureList; + +typedef struct PassList { + struct DRWPass *passes[1]; +} PassList; + +/* Stores custom structs from the engine that have been MEM_(m/c)allocN'ed. */ +typedef struct StorageList { + void *storage[1]; +} StorageList; + +typedef struct ViewportEngineData { + /* Not owning pointer to the draw engine. */ + struct DRWRegisteredDrawEngine *engine_type; + + FramebufferList *fbl; + TextureList *txl; + PassList *psl; + StorageList *stl; + char info[GPU_INFO_SIZE]; + + /* we may want to put this elsewhere */ + struct DRWTextStore *text_draw_cache; + + /* Profiling data */ + double init_time; + double render_time; + double background_time; +} ViewportEngineData; + +typedef struct ViewportEngineData_Info { + int fbl_len; + int txl_len; + int psl_len; + int stl_len; +} ViewportEngineData_Info; + +/* Buffer and textures used by the viewport by default */ +typedef struct DefaultFramebufferList { + struct GPUFrameBuffer *default_fb; + struct GPUFrameBuffer *overlay_fb; + struct GPUFrameBuffer *in_front_fb; + struct GPUFrameBuffer *color_only_fb; + struct GPUFrameBuffer *depth_only_fb; + struct GPUFrameBuffer *overlay_only_fb; +} DefaultFramebufferList; + +typedef struct DefaultTextureList { + struct GPUTexture *color; + struct GPUTexture *color_overlay; + struct GPUTexture *depth; + struct GPUTexture *depth_in_front; +} DefaultTextureList; + +typedef struct DRWViewData DRWViewData; + +DRWViewData *DRW_view_data_create(ListBase *engine_types); +void DRW_view_data_free(DRWViewData *view_data); + +void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, + struct GPUViewport *viewport); +void DRW_view_data_texture_list_size_validate(DRWViewData *view_data, const int size[2]); +ViewportEngineData *DRW_view_data_engine_data_get_ensure(DRWViewData *view_data, + struct DrawEngineType *engine_type_); +void DRW_view_data_use_engine(DRWViewData *view_data, struct DrawEngineType *engine_type); +void DRW_view_data_reset(DRWViewData *view_data); +void DRW_view_data_free_unused(DRWViewData *view_data); +double *DRW_view_data_cache_time_get(DRWViewData *view_data); +DefaultFramebufferList *DRW_view_data_default_framebuffer_list_get(DRWViewData *view_data); +DefaultTextureList *DRW_view_data_default_texture_list_get(DRWViewData *view_data); + +typedef struct DRWEngineIterator { + int id, end; + ViewportEngineData **engines; +} DRWEngineIterator; + +/* Iterate over used engines of this view_data. */ +void DRW_view_data_enabled_engine_iter_begin(DRWEngineIterator *iterator, DRWViewData *view_data); +ViewportEngineData *DRW_view_data_enabled_engine_iter_step(DRWEngineIterator *iterator); + +#define DRW_ENABLED_ENGINE_ITER(view_data_, engine_, data_) \ + DRWEngineIterator iterator; \ + ViewportEngineData *data_; \ + struct DrawEngineType *engine_; \ + DRW_view_data_enabled_engine_iter_begin(&iterator, view_data_); \ + /* WATCH Comma operator trickery ahead! This tests engine_ == NULL. */ \ + while ((data_ = DRW_view_data_enabled_engine_iter_step(&iterator), \ + engine_ = (data_ != NULL) ? (struct DrawEngineType *)data_->engine_type->draw_engine : \ + NULL)) + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 79148035a25..1cfd83c503e 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -2180,13 +2180,17 @@ static void validate_object_select_id(struct Depsgraph *depsgraph, * Calling this function should be avoided during interactive drawing. */ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void *data) { - DefaultTextureList *dtxl = (DefaultTextureList *)GPU_viewport_texture_list_get(viewport); + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); - GPUFrameBuffer *tmp_fb = GPU_framebuffer_create(__func__); - GPU_framebuffer_texture_attach(tmp_fb, dtxl->depth, 0, 0); - GPU_framebuffer_bind(tmp_fb); + GPUFrameBuffer *depth_read_fb = NULL; + GPU_framebuffer_ensure_config(&depth_read_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_NONE, + }); - GPU_framebuffer_read_depth(tmp_fb, + GPU_framebuffer_bind(depth_read_fb); + GPU_framebuffer_read_depth(depth_read_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), @@ -2195,7 +2199,7 @@ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void data); GPU_framebuffer_restore(); - GPU_framebuffer_free(tmp_fb); + GPU_framebuffer_free(depth_read_fb); } void ED_view3d_select_id_validate(ViewContext *vc) @@ -2265,12 +2269,11 @@ static ViewDepths *view3d_depths_create(ARegion *region) ViewDepths *d = MEM_callocN(sizeof(ViewDepths), "ViewDepths"); d->w = region->winx; d->h = region->winy; - d->depths = MEM_mallocN(sizeof(float) * d->w * d->h, "View depths"); { GPUViewport *viewport = WM_draw_region_get_viewport(region); - DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport); - GPU_framebuffer_read_depth(fbl->depth_only_fb, 0, 0, d->w, d->h, GPU_DATA_FLOAT, d->depths); + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); + d->depths = GPU_texture_read(depth_tx, GPU_DATA_FLOAT, 0); /* Assumed to be this as they are never changed. */ d->depth_range[0] = 0.0; diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 9a1885160b6..deff9e47871 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -52,6 +52,14 @@ typedef enum eGPUSamplerState { GPU_SAMPLER_REPEAT = (GPU_SAMPLER_REPEAT_S | GPU_SAMPLER_REPEAT_T | GPU_SAMPLER_REPEAT_R), } eGPUSamplerState; +#define GPU_TEXTURE_FREE_SAFE(texture) \ + do { \ + if (texture != NULL) { \ + GPU_texture_free(texture); \ + texture = NULL; \ + } \ + } while (0) + /* `GPU_SAMPLER_MAX` is not a valid enum value, but only a limit. * It also creates a bad mask for the `NOT` operator in `ENUM_OPERATORS`. */ diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h index 0ecd5f3eb7b..4d9970dac90 100644 --- a/source/blender/gpu/GPU_viewport.h +++ b/source/blender/gpu/GPU_viewport.h @@ -35,76 +35,15 @@ extern "C" { #endif -#define GPU_INFO_SIZE 512 /* IMA_MAX_RENDER_TEXT */ #define GLA_PIXEL_OFS 0.375f typedef struct GHash GHash; typedef struct GPUViewport GPUViewport; struct GPUFrameBuffer; - -/* Contains memory pools information. */ -typedef struct ViewportMemoryPool { - struct BLI_memblock *commands; - struct BLI_memblock *commands_small; - struct BLI_memblock *callbuffers; - struct BLI_memblock *obmats; - struct BLI_memblock *obinfos; - struct BLI_memblock *cullstates; - struct BLI_memblock *shgroups; - struct BLI_memblock *uniforms; - struct BLI_memblock *views; - struct BLI_memblock *passes; - struct BLI_memblock *images; - struct GPUUniformBuf **matrices_ubo; - struct GPUUniformBuf **obinfos_ubo; - struct GHash *obattrs_ubo_pool; - uint ubo_len; -} ViewportMemoryPool; - -/* All FramebufferLists are just the same pointers with different names. */ -typedef struct FramebufferList { - struct GPUFrameBuffer *framebuffers[1]; -} FramebufferList; - -typedef struct TextureList { - struct GPUTexture *textures[1]; -} TextureList; - -typedef struct PassList { - struct DRWPass *passes[1]; -} PassList; - -typedef struct StorageList { - void *storage[1]; /* Custom structs from the engine. */ -} StorageList; - -typedef struct ViewportEngineData { - void *engine_type; - - FramebufferList *fbl; - TextureList *txl; - PassList *psl; - StorageList *stl; - char info[GPU_INFO_SIZE]; - - TextureList *txl_stereo; - StorageList *stl_stereo; - /* We may want to put this elsewhere. */ - struct DRWTextStore *text_draw_cache; - - /* Profiling data. */ - double init_time; - double render_time; - double background_time; -} ViewportEngineData; - -typedef struct ViewportEngineData_Info { - int fbl_len; - int txl_len; - int psl_len; - int stl_len; -} ViewportEngineData_Info; +struct DefaultFramebufferList; +struct DefaultTextureList; +struct DRWData; GPUViewport *GPU_viewport_create(void); GPUViewport *GPU_viewport_stereo_create(void); @@ -129,35 +68,21 @@ void GPU_viewport_unbind_from_offscreen(GPUViewport *viewport, bool display_colorspace, bool do_overlay_merge); -ViewportMemoryPool *GPU_viewport_mempool_get(GPUViewport *viewport); -struct DRWInstanceDataList *GPU_viewport_instance_data_list_get(GPUViewport *viewport); +struct DRWData **GPU_viewport_data_get(GPUViewport *viewport); -void *GPU_viewport_engine_data_create(GPUViewport *viewport, void *engine_type); -void *GPU_viewport_engine_data_get(GPUViewport *viewport, void *engine_handle); -void *GPU_viewport_framebuffer_list_get(GPUViewport *viewport); void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo_format); -void *GPU_viewport_texture_list_get(GPUViewport *viewport); -void GPU_viewport_size_get(const GPUViewport *viewport, int size[2]); -void GPU_viewport_size_set(GPUViewport *viewport, const int size[2]); -void GPU_viewport_active_view_set(GPUViewport *viewport, int view); - -/* Profiling. */ -double *GPU_viewport_cache_time_get(GPUViewport *viewport); void GPU_viewport_tag_update(GPUViewport *viewport); bool GPU_viewport_do_update(GPUViewport *viewport); +int GPU_viewport_active_view_get(GPUViewport *viewport); +bool GPU_viewport_is_stereo_get(GPUViewport *viewport); + GPUTexture *GPU_viewport_color_texture(GPUViewport *viewport, int view); +GPUTexture *GPU_viewport_overlay_texture(GPUViewport *viewport, int view); +GPUTexture *GPU_viewport_depth_texture(GPUViewport *viewport); -/* Texture pool. */ -GPUTexture *GPU_viewport_texture_pool_query( - GPUViewport *viewport, void *engine, int width, int height, int format); - -bool GPU_viewport_engines_data_validate(GPUViewport *viewport, void **engine_handle_array); -void GPU_viewport_cache_release(GPUViewport *viewport); - -struct GPUFrameBuffer *GPU_viewport_framebuffer_default_get(GPUViewport *viewport); -struct GPUFrameBuffer *GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport); +GPUFrameBuffer *GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport); #ifdef __cplusplus } diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index dd63edea0db..ccd9a4c061b 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -48,22 +48,6 @@ #include "MEM_guardedalloc.h" -static const int default_fbl_len = (sizeof(DefaultFramebufferList)) / sizeof(void *); -static const int default_txl_len = (sizeof(DefaultTextureList)) / sizeof(void *); - -#define MAX_ENABLE_ENGINE 8 - -/* Maximum number of simultaneous engine enabled at the same time. - * Setting it lower than the real number will do lead to - * higher VRAM usage due to sub-efficient buffer reuse. */ -#define MAX_ENGINE_BUFFER_SHARING 5 - -typedef struct ViewportTempTexture { - struct ViewportTempTexture *next, *prev; - void *user[MAX_ENGINE_BUFFER_SHARING]; - GPUTexture *texture; -} ViewportTempTexture; - /* Struct storing a viewport specific GPUBatch. * The end-goal is to have a single batch shared across viewport and use a model matrix to place * the batch. Due to OCIO and Image/UV editor we are not able to use an model matrix yet. */ @@ -89,23 +73,17 @@ struct GPUViewport { /* Set the active view (for stereoscopic viewport rendering). */ int active_view; - /* If engine_handles mismatch we free all #ViewportEngineData in this viewport. */ - struct { - void *handle; - ViewportEngineData *data; - } engine_data[MAX_ENABLE_ENGINE]; - - DefaultFramebufferList *fbl; - DefaultTextureList *txl; - - ViewportMemoryPool vmempool; /* Used for rendering data structure. */ - struct DRWInstanceDataList *idatalist; /* Used for rendering data structure. */ - - ListBase - tex_pool; /* ViewportTempTexture list : Temporary textures shared across draw engines. */ - - /* Profiling data. */ - double cache_time; + /* Viewport Resources. */ + struct DRWData *draw_data; + /** Color buffers, one for each stereo view. Only one if not stereo viewport. */ + GPUTexture *color_render_tx[2]; + GPUTexture *color_overlay_tx[2]; + /** Depth buffer. Can be shared with GPUOffscreen. */ + GPUTexture *depth_tx; + /** Compositing framebuffer for stereo viewport. */ + GPUFrameBuffer *stereo_comp_fb; + /** Overlay framebuffer for drawing outside of DRW module. */ + GPUFrameBuffer *overlay_fb; /* Color management. */ ColorManagedViewSettings view_settings; @@ -123,12 +101,6 @@ enum { GPU_VIEWPORT_STEREO = (1 << 1), }; -static void gpu_viewport_buffers_free( - FramebufferList *fbl, int fbl_len, TextureList *txl, TextureList *txl_stereo, int txl_len); -static void gpu_viewport_storage_free(StorageList *stl, int stl_len); -static void gpu_viewport_passes_free(PassList *psl, int psl_len); -static void gpu_viewport_texture_pool_free(GPUViewport *viewport); - void GPU_viewport_tag_update(GPUViewport *viewport) { viewport->flag |= DO_UPDATE; @@ -144,12 +116,9 @@ bool GPU_viewport_do_update(GPUViewport *viewport) GPUViewport *GPU_viewport_create(void) { GPUViewport *viewport = MEM_callocN(sizeof(GPUViewport), "GPUViewport"); - viewport->fbl = MEM_callocN(sizeof(DefaultFramebufferList), "FramebufferList"); - viewport->txl = MEM_callocN(sizeof(DefaultTextureList), "TextureList"); - viewport->idatalist = DRW_instance_data_list_create(); viewport->do_color_management = false; viewport->size[0] = viewport->size[1] = -1; - viewport->active_view = -1; + viewport->active_view = 0; return viewport; } @@ -160,343 +129,55 @@ GPUViewport *GPU_viewport_stereo_create(void) return viewport; } -static void gpu_viewport_framebuffer_view_set(GPUViewport *viewport, int view) +struct DRWData **GPU_viewport_data_get(GPUViewport *viewport) { - /* Early check if the view is the latest requested. */ - if (viewport->active_view == view) { - return; - } - DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; - - /* Only swap the texture when this is a Stereo Viewport. */ - if (((viewport->flag & GPU_VIEWPORT_STEREO) != 0)) { - SWAP(GPUTexture *, dtxl->color, dtxl->color_stereo); - SWAP(GPUTexture *, dtxl->color_overlay, dtxl->color_overlay_stereo); - - for (int i = 0; i < MAX_ENABLE_ENGINE; i++) { - if (viewport->engine_data[i].handle != NULL) { - ViewportEngineData *data = viewport->engine_data[i].data; - SWAP(StorageList *, data->stl, data->stl_stereo); - SWAP(TextureList *, data->txl, data->txl_stereo); - } - else { - break; - } - } - } - - GPU_framebuffer_ensure_config(&dfbl->default_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(dtxl->color), - }); - - GPU_framebuffer_ensure_config(&dfbl->overlay_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), - }); - - GPU_framebuffer_ensure_config(&dfbl->depth_only_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_NONE, - }); - - GPU_framebuffer_ensure_config(&dfbl->color_only_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(dtxl->color), - }); - - GPU_framebuffer_ensure_config(&dfbl->overlay_only_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), - }); - - viewport->active_view = view; + return &viewport->draw_data; } -void *GPU_viewport_engine_data_create(GPUViewport *viewport, void *engine_type) +static void gpu_viewport_textures_create(GPUViewport *viewport) { - ViewportEngineData *data = MEM_callocN(sizeof(ViewportEngineData), "ViewportEngineData"); - int fbl_len, txl_len, psl_len, stl_len; - - DRW_engine_viewport_data_size_get(engine_type, &fbl_len, &txl_len, &psl_len, &stl_len); - - data->engine_type = engine_type; - - data->fbl = MEM_callocN((sizeof(void *) * fbl_len) + sizeof(FramebufferList), "FramebufferList"); - data->txl = MEM_callocN((sizeof(void *) * txl_len) + sizeof(TextureList), "TextureList"); - data->psl = MEM_callocN((sizeof(void *) * psl_len) + sizeof(PassList), "PassList"); - data->stl = MEM_callocN((sizeof(void *) * stl_len) + sizeof(StorageList), "StorageList"); - - if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0) { - data->txl_stereo = MEM_callocN((sizeof(void *) * txl_len) + sizeof(TextureList), - "TextureList"); - data->stl_stereo = MEM_callocN((sizeof(void *) * stl_len) + sizeof(StorageList), - "StorageList"); - } - - for (int i = 0; i < MAX_ENABLE_ENGINE; i++) { - if (viewport->engine_data[i].handle == NULL) { - viewport->engine_data[i].handle = engine_type; - viewport->engine_data[i].data = data; - return data; - } - } - - BLI_assert_msg(0, "Too many draw engines enabled at the same time"); - return NULL; -} - -static void gpu_viewport_engines_data_free(GPUViewport *viewport) -{ - int fbl_len, txl_len, psl_len, stl_len; - - for (int i = 0; i < MAX_ENABLE_ENGINE && viewport->engine_data[i].handle; i++) { - ViewportEngineData *data = viewport->engine_data[i].data; - - DRW_engine_viewport_data_size_get(data->engine_type, &fbl_len, &txl_len, &psl_len, &stl_len); - - gpu_viewport_buffers_free(data->fbl, fbl_len, data->txl, data->txl_stereo, txl_len); - gpu_viewport_passes_free(data->psl, psl_len); - gpu_viewport_storage_free(data->stl, stl_len); - - MEM_freeN(data->fbl); - MEM_freeN(data->txl); - MEM_freeN(data->psl); - MEM_freeN(data->stl); - - if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0) { - gpu_viewport_storage_free(data->stl_stereo, stl_len); - MEM_freeN(data->txl_stereo); - MEM_freeN(data->stl_stereo); - } - /* We could handle this in the DRW module */ - if (data->text_draw_cache) { - extern void DRW_text_cache_destroy(struct DRWTextStore * dt); - DRW_text_cache_destroy(data->text_draw_cache); - data->text_draw_cache = NULL; - } - - MEM_freeN(data); - - /* Mark as unused. */ - viewport->engine_data[i].handle = NULL; - } - - gpu_viewport_texture_pool_free(viewport); -} - -void *GPU_viewport_engine_data_get(GPUViewport *viewport, void *engine_handle) -{ - BLI_assert(engine_handle != NULL); - - for (int i = 0; i < MAX_ENABLE_ENGINE; i++) { - if (viewport->engine_data[i].handle == engine_handle) { - return viewport->engine_data[i].data; - } - } - return NULL; -} - -ViewportMemoryPool *GPU_viewport_mempool_get(GPUViewport *viewport) -{ - return &viewport->vmempool; -} - -struct DRWInstanceDataList *GPU_viewport_instance_data_list_get(GPUViewport *viewport) -{ - return viewport->idatalist; -} - -/* Note this function is only allowed to be called from `DRW_notify_view_update`. The rest - * should bind the correct viewport. - * - * The reason is that DRW_notify_view_update can be called from a different thread, but needs - * access to the engine data. */ -void GPU_viewport_active_view_set(GPUViewport *viewport, int view) -{ - gpu_viewport_framebuffer_view_set(viewport, view); -} - -void *GPU_viewport_framebuffer_list_get(GPUViewport *viewport) -{ - return viewport->fbl; -} - -void *GPU_viewport_texture_list_get(GPUViewport *viewport) -{ - return viewport->txl; -} - -void GPU_viewport_size_get(const GPUViewport *viewport, int size[2]) -{ - copy_v2_v2_int(size, viewport->size); -} - -/** - * Special case, this is needed for when we have a viewport without a frame-buffer output - * (occlusion queries for eg) - * but still need to set the size since it may be used for other calculations. - */ -void GPU_viewport_size_set(GPUViewport *viewport, const int size[2]) -{ - copy_v2_v2_int(viewport->size, size); -} - -double *GPU_viewport_cache_time_get(GPUViewport *viewport) -{ - return &viewport->cache_time; -} - -/** - * Try to find a texture corresponding to params into the texture pool. - * If no texture was found, create one and add it to the pool. - */ -GPUTexture *GPU_viewport_texture_pool_query( - GPUViewport *viewport, void *engine, int width, int height, int format) -{ - GPUTexture *tex; - - LISTBASE_FOREACH (ViewportTempTexture *, tmp_tex, &viewport->tex_pool) { - if ((GPU_texture_format(tmp_tex->texture) == format) && - (GPU_texture_width(tmp_tex->texture) == width) && - (GPU_texture_height(tmp_tex->texture) == height)) { - /* Search if the engine is not already using this texture */ - for (int i = 0; i < MAX_ENGINE_BUFFER_SHARING; i++) { - if (tmp_tex->user[i] == engine) { - break; - } - - if (tmp_tex->user[i] == NULL) { - tmp_tex->user[i] = engine; - return tmp_tex->texture; - } - } - } - } - - tex = GPU_texture_create_2d("temp_from_pool", width, height, 1, format, NULL); - /* Doing filtering for depth does not make sense when not doing shadow mapping, - * and enabling texture filtering on integer texture make them unreadable. */ - bool do_filter = !GPU_texture_depth(tex) && !GPU_texture_integer(tex); - GPU_texture_filter_mode(tex, do_filter); - - ViewportTempTexture *tmp_tex = MEM_callocN(sizeof(ViewportTempTexture), "ViewportTempTexture"); - tmp_tex->texture = tex; - tmp_tex->user[0] = engine; - BLI_addtail(&viewport->tex_pool, tmp_tex); - - return tex; -} - -static void gpu_viewport_texture_pool_clear_users(GPUViewport *viewport) -{ - ViewportTempTexture *tmp_tex_next; - - for (ViewportTempTexture *tmp_tex = viewport->tex_pool.first; tmp_tex; tmp_tex = tmp_tex_next) { - tmp_tex_next = tmp_tex->next; - bool no_user = true; - for (int i = 0; i < MAX_ENGINE_BUFFER_SHARING; i++) { - if (tmp_tex->user[i] != NULL) { - tmp_tex->user[i] = NULL; - no_user = false; - } - } - - if (no_user) { - GPU_texture_free(tmp_tex->texture); - BLI_freelinkN(&viewport->tex_pool, tmp_tex); - } - } -} - -static void gpu_viewport_texture_pool_free(GPUViewport *viewport) -{ - LISTBASE_FOREACH (ViewportTempTexture *, tmp_tex, &viewport->tex_pool) { - GPU_texture_free(tmp_tex->texture); - } - - BLI_freelistN(&viewport->tex_pool); -} - -/* Takes an NULL terminated array of engine_handle. Returns true is data is still valid. */ -bool GPU_viewport_engines_data_validate(GPUViewport *viewport, void **engine_handle_array) -{ - for (int i = 0; i < MAX_ENABLE_ENGINE && engine_handle_array[i]; i++) { - if (viewport->engine_data[i].handle != engine_handle_array[i]) { - gpu_viewport_engines_data_free(viewport); - return false; - } - } - return true; -} - -void GPU_viewport_cache_release(GPUViewport *viewport) -{ - for (int i = 0; i < MAX_ENABLE_ENGINE && viewport->engine_data[i].handle; i++) { - ViewportEngineData *data = viewport->engine_data[i].data; - int psl_len; - DRW_engine_viewport_data_size_get(data->engine_type, NULL, NULL, &psl_len, NULL); - gpu_viewport_passes_free(data->psl, psl_len); - } -} - -static void gpu_viewport_default_fb_create(GPUViewport *viewport) -{ - DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; int *size = viewport->size; - bool ok = true; - dtxl->color = GPU_texture_create_2d("dtxl_color", UNPACK2(size), 1, GPU_RGBA16F, NULL); - dtxl->color_overlay = GPU_texture_create_2d( - "dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); + if (viewport->color_render_tx[0] == NULL) { + viewport->color_render_tx[0] = GPU_texture_create_2d( + "dtxl_color", UNPACK2(size), 1, GPU_RGBA16F, NULL); + viewport->color_overlay_tx[0] = GPU_texture_create_2d( + "dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); + } - if (viewport->flag & GPU_VIEWPORT_STEREO) { - dtxl->color_stereo = GPU_texture_create_2d( + if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0 && viewport->color_render_tx[1] == NULL) { + viewport->color_render_tx[1] = GPU_texture_create_2d( "dtxl_color_stereo", UNPACK2(size), 1, GPU_RGBA16F, NULL); - dtxl->color_overlay_stereo = GPU_texture_create_2d( + viewport->color_overlay_tx[1] = GPU_texture_create_2d( "dtxl_color_overlay_stereo", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); } /* Can be shared with GPUOffscreen. */ - if (dtxl->depth == NULL) { - dtxl->depth = GPU_texture_create_2d( + if (viewport->depth_tx == NULL) { + viewport->depth_tx = GPU_texture_create_2d( "dtxl_depth", UNPACK2(size), 1, GPU_DEPTH24_STENCIL8, NULL); } - if (!dtxl->depth || !dtxl->color) { - ok = false; - goto cleanup; - } - - gpu_viewport_framebuffer_view_set(viewport, 0); - - ok = ok && GPU_framebuffer_check_valid(dfbl->default_fb, NULL); - ok = ok && GPU_framebuffer_check_valid(dfbl->overlay_fb, NULL); - ok = ok && GPU_framebuffer_check_valid(dfbl->color_only_fb, NULL); - ok = ok && GPU_framebuffer_check_valid(dfbl->depth_only_fb, NULL); - ok = ok && GPU_framebuffer_check_valid(dfbl->overlay_only_fb, NULL); -cleanup: - if (!ok) { + if (!viewport->depth_tx || !viewport->color_render_tx[0] || !viewport->color_overlay_tx[0]) { GPU_viewport_free(viewport); - DRW_opengl_context_disable(); - return; + } +} + +static void gpu_viewport_textures_free(GPUViewport *viewport) +{ + GPU_FRAMEBUFFER_FREE_SAFE(viewport->stereo_comp_fb); + GPU_FRAMEBUFFER_FREE_SAFE(viewport->overlay_fb); + + for (int i = 0; i < 2; i++) { + GPU_TEXTURE_FREE_SAFE(viewport->color_render_tx[i]); + GPU_TEXTURE_FREE_SAFE(viewport->color_overlay_tx[i]); } - GPU_framebuffer_restore(); + GPU_TEXTURE_FREE_SAFE(viewport->depth_tx); } void GPU_viewport_bind(GPUViewport *viewport, int view, const rcti *rect) { - DefaultFramebufferList *dfbl = viewport->fbl; - int fbl_len, txl_len; - int rect_size[2]; /* add one pixel because of scissor test */ rect_size[0] = BLI_rcti_size_x(rect) + 1; @@ -504,39 +185,18 @@ void GPU_viewport_bind(GPUViewport *viewport, int view, const rcti *rect) DRW_opengl_context_enable(); - if (dfbl->default_fb) { - if (!equals_v2v2_int(viewport->size, rect_size)) { - gpu_viewport_buffers_free((FramebufferList *)viewport->fbl, - default_fbl_len, - (TextureList *)viewport->txl, - NULL, - default_txl_len); - - for (int i = 0; i < MAX_ENABLE_ENGINE && viewport->engine_data[i].handle; i++) { - ViewportEngineData *data = viewport->engine_data[i].data; - DRW_engine_viewport_data_size_get(data->engine_type, &fbl_len, &txl_len, NULL, NULL); - gpu_viewport_buffers_free(data->fbl, fbl_len, data->txl, data->txl_stereo, txl_len); - } - - gpu_viewport_texture_pool_free(viewport); - viewport->active_view = -1; - } + if (!equals_v2v2_int(viewport->size, rect_size)) { + copy_v2_v2_int(viewport->size, rect_size); + gpu_viewport_textures_free(viewport); + gpu_viewport_textures_create(viewport); } - copy_v2_v2_int(viewport->size, rect_size); - - gpu_viewport_texture_pool_clear_users(viewport); - - if (!dfbl->default_fb) { - gpu_viewport_default_fb_create(viewport); - } - gpu_viewport_framebuffer_view_set(viewport, view); + viewport->active_view = view; } +/* Should be called from DRW after DRW_opengl_context_enable. */ void GPU_viewport_bind_from_offscreen(GPUViewport *viewport, struct GPUOffScreen *ofs) { - DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; GPUTexture *color, *depth; GPUFrameBuffer *fb; viewport->size[0] = GPU_offscreen_width(ofs); @@ -544,14 +204,12 @@ void GPU_viewport_bind_from_offscreen(GPUViewport *viewport, struct GPUOffScreen GPU_offscreen_viewport_data_get(ofs, &fb, &color, &depth); + gpu_viewport_textures_free(viewport); + /* This is the only texture we can share. */ - dtxl->depth = depth; + viewport->depth_tx = depth; - gpu_viewport_texture_pool_clear_users(viewport); - - if (!dfbl->default_fb) { - gpu_viewport_default_fb_create(viewport); - } + gpu_viewport_textures_create(viewport); } void GPU_viewport_colorspace_set(GPUViewport *viewport, @@ -608,21 +266,17 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo * done from a single viewport. See `wm_stereo.c` */ return; } - gpu_viewport_framebuffer_view_set(viewport, 0); - DefaultTextureList *dtxl = viewport->txl; - DefaultFramebufferList *dfbl = viewport->fbl; - /* The composite framebuffer object needs to be created in the window context. */ - GPU_framebuffer_ensure_config(&dfbl->stereo_comp_fb, + GPU_framebuffer_ensure_config(&viewport->stereo_comp_fb, { GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), - GPU_ATTACHMENT_TEXTURE(dtxl->color), + GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]), + GPU_ATTACHMENT_TEXTURE(viewport->color_render_tx[0]), }); GPUVertFormat *vert_format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPU_framebuffer_bind(dfbl->stereo_comp_fb); + GPU_framebuffer_bind(viewport->stereo_comp_fb); GPU_matrix_push(); GPU_matrix_push_projection(); GPU_matrix_identity_set(); @@ -650,8 +304,8 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo } immUniform1i("stereoDisplaySettings", settings); - GPU_texture_bind(dtxl->color_stereo, 0); - GPU_texture_bind(dtxl->color_overlay_stereo, 1); + GPU_texture_bind(viewport->color_render_tx[1], 0); + GPU_texture_bind(viewport->color_overlay_tx[1], 1); immBegin(GPU_PRIM_TRI_STRIP, 4); @@ -662,8 +316,8 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo immEnd(); - GPU_texture_unbind(dtxl->color_stereo); - GPU_texture_unbind(dtxl->color_overlay_stereo); + GPU_texture_unbind(viewport->color_render_tx[1]); + GPU_texture_unbind(viewport->color_overlay_tx[1]); immUnbindProgram(); GPU_matrix_pop_projection(); @@ -747,14 +401,14 @@ static void gpu_viewport_batch_free(GPUViewport *viewport) /** \} */ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, + int view, const rctf *rect_pos, const rctf *rect_uv, bool display_colorspace, bool do_overlay_merge) { - DefaultTextureList *dtxl = viewport->txl; - GPUTexture *color = dtxl->color; - GPUTexture *color_overlay = dtxl->color_overlay; + GPUTexture *color = viewport->color_render_tx[view]; + GPUTexture *color_overlay = viewport->color_overlay_tx[view]; bool use_ocio = false; @@ -808,12 +462,9 @@ void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, bool display_colorspace, bool do_overlay_merge) { - gpu_viewport_framebuffer_view_set(viewport, view); - DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; - GPUTexture *color = dtxl->color; + GPUTexture *color = viewport->color_render_tx[view]; - if (dfbl->default_fb == NULL) { + if (color == NULL) { return; } @@ -854,7 +505,7 @@ void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, } gpu_viewport_draw_colormanaged( - viewport, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge); + viewport, view, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge); } /** @@ -877,10 +528,7 @@ void GPU_viewport_unbind_from_offscreen(GPUViewport *viewport, bool display_colorspace, bool do_overlay_merge) { - DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; - - if (dfbl->default_fb == NULL) { + if (viewport->color_render_tx == NULL) { return; } @@ -902,10 +550,10 @@ void GPU_viewport_unbind_from_offscreen(GPUViewport *viewport, }; gpu_viewport_draw_colormanaged( - viewport, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge); + viewport, 0, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge); /* This one is from the offscreen. Don't free it with the viewport. */ - dtxl->depth = NULL; + viewport->depth_tx = NULL; } void GPU_viewport_unbind(GPUViewport *UNUSED(viewport)) @@ -914,149 +562,53 @@ void GPU_viewport_unbind(GPUViewport *UNUSED(viewport)) DRW_opengl_context_disable(); } +int GPU_viewport_active_view_get(GPUViewport *viewport) +{ + return viewport->active_view; +} + +bool GPU_viewport_is_stereo_get(GPUViewport *viewport) +{ + return (viewport->flag & GPU_VIEWPORT_STEREO) != 0; +} + GPUTexture *GPU_viewport_color_texture(GPUViewport *viewport, int view) { - DefaultFramebufferList *dfbl = viewport->fbl; - - if (dfbl->default_fb) { - DefaultTextureList *dtxl = viewport->txl; - if (viewport->active_view == view) { - return dtxl->color; - } - - return dtxl->color_stereo; - } - - return NULL; + return viewport->color_render_tx[view]; } -static void gpu_viewport_buffers_free( - FramebufferList *fbl, int fbl_len, TextureList *txl, TextureList *txl_stereo, int txl_len) +GPUTexture *GPU_viewport_overlay_texture(GPUViewport *viewport, int view) { - for (int i = 0; i < fbl_len; i++) { - GPUFrameBuffer *fb = fbl->framebuffers[i]; - if (fb) { - GPU_framebuffer_free(fb); - fbl->framebuffers[i] = NULL; - } - } - for (int i = 0; i < txl_len; i++) { - GPUTexture *tex = txl->textures[i]; - if (tex) { - GPU_texture_free(tex); - txl->textures[i] = NULL; - } - } - if (txl_stereo != NULL) { - for (int i = 0; i < txl_len; i++) { - GPUTexture *tex = txl_stereo->textures[i]; - if (tex) { - GPU_texture_free(tex); - txl_stereo->textures[i] = NULL; - } - } - } + return viewport->color_overlay_tx[view]; } -static void gpu_viewport_storage_free(StorageList *stl, int stl_len) +GPUTexture *GPU_viewport_depth_texture(GPUViewport *viewport) { - for (int i = 0; i < stl_len; i++) { - void *storage = stl->storage[i]; - if (storage) { - MEM_freeN(storage); - stl->storage[i] = NULL; - } - } + return viewport->depth_tx; } -static void gpu_viewport_passes_free(PassList *psl, int psl_len) +/* Overlay framebuffer for drawing outside of DRW module. */ +GPUFrameBuffer *GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport) { - memset(psl->passes, 0, sizeof(*psl->passes) * psl_len); + GPU_framebuffer_ensure_config(&viewport->overlay_fb, + { + GPU_ATTACHMENT_TEXTURE(viewport->depth_tx), + GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]), + }); + return viewport->overlay_fb; } /* Must be executed inside Draw-manager OpenGL Context. */ void GPU_viewport_free(GPUViewport *viewport) { - gpu_viewport_engines_data_free(viewport); - - gpu_viewport_buffers_free((FramebufferList *)viewport->fbl, - default_fbl_len, - (TextureList *)viewport->txl, - NULL, - default_txl_len); - - gpu_viewport_texture_pool_free(viewport); - - MEM_freeN(viewport->fbl); - MEM_freeN(viewport->txl); - - if (viewport->vmempool.commands != NULL) { - BLI_memblock_destroy(viewport->vmempool.commands, NULL); - } - if (viewport->vmempool.commands_small != NULL) { - BLI_memblock_destroy(viewport->vmempool.commands_small, NULL); - } - if (viewport->vmempool.callbuffers != NULL) { - BLI_memblock_destroy(viewport->vmempool.callbuffers, NULL); - } - if (viewport->vmempool.obmats != NULL) { - BLI_memblock_destroy(viewport->vmempool.obmats, NULL); - } - if (viewport->vmempool.obinfos != NULL) { - BLI_memblock_destroy(viewport->vmempool.obinfos, NULL); - } - if (viewport->vmempool.cullstates != NULL) { - BLI_memblock_destroy(viewport->vmempool.cullstates, NULL); - } - if (viewport->vmempool.shgroups != NULL) { - BLI_memblock_destroy(viewport->vmempool.shgroups, NULL); - } - if (viewport->vmempool.uniforms != NULL) { - BLI_memblock_destroy(viewport->vmempool.uniforms, NULL); - } - if (viewport->vmempool.views != NULL) { - BLI_memblock_destroy(viewport->vmempool.views, NULL); - } - if (viewport->vmempool.passes != NULL) { - BLI_memblock_destroy(viewport->vmempool.passes, NULL); - } - if (viewport->vmempool.images != NULL) { - BLI_memblock_iter iter; - GPUTexture **tex; - BLI_memblock_iternew(viewport->vmempool.images, &iter); - while ((tex = BLI_memblock_iterstep(&iter))) { - GPU_texture_free(*tex); - } - BLI_memblock_destroy(viewport->vmempool.images, NULL); - } - if (viewport->vmempool.obattrs_ubo_pool != NULL) { - DRW_uniform_attrs_pool_free(viewport->vmempool.obattrs_ubo_pool); + if (viewport->draw_data) { + DRW_viewport_data_free(viewport->draw_data); } - for (int i = 0; i < viewport->vmempool.ubo_len; i++) { - GPU_uniformbuf_free(viewport->vmempool.matrices_ubo[i]); - GPU_uniformbuf_free(viewport->vmempool.obinfos_ubo[i]); - } - MEM_SAFE_FREE(viewport->vmempool.matrices_ubo); - MEM_SAFE_FREE(viewport->vmempool.obinfos_ubo); - - DRW_instance_data_list_free(viewport->idatalist); - MEM_freeN(viewport->idatalist); + gpu_viewport_textures_free(viewport); BKE_color_managed_view_settings_free(&viewport->view_settings); gpu_viewport_batch_free(viewport); MEM_freeN(viewport); } - -GPUFrameBuffer *GPU_viewport_framebuffer_default_get(GPUViewport *viewport) -{ - DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport); - return fbl->default_fb; -} - -GPUFrameBuffer *GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport) -{ - DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport); - return fbl->overlay_fb; -} From 7a66a9f22e6614b88ec262cceefa196906dd434d Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 5 Oct 2021 16:54:25 +0900 Subject: [PATCH 0525/1500] Cleanup: remove unused parameter --- source/blender/editors/include/ED_screen.h | 4 +--- source/blender/editors/screen/area.c | 6 +++--- source/blender/windowmanager/xr/intern/wm_xr_session.c | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 0de6907e677..b6936892803 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -178,9 +178,7 @@ void ED_area_update_region_sizes(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area); bool ED_area_has_shared_border(struct ScrArea *a, struct ScrArea *b); -ScrArea *ED_area_offscreen_create(struct wmWindowManager *wm, - struct wmWindow *win, - eSpace_Type space_type); +ScrArea *ED_area_offscreen_create(struct wmWindow *win, eSpace_Type space_type); void ED_area_offscreen_free(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index d5456482d67..9967d36fd40 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -2001,7 +2001,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) } } -static void area_offscreen_init(wmWindowManager *wm, ScrArea *area) +static void area_offscreen_init(ScrArea *area) { area->type = BKE_spacetype_from_id(area->spacetype); @@ -2015,13 +2015,13 @@ static void area_offscreen_init(wmWindowManager *wm, ScrArea *area) } } -ScrArea *ED_area_offscreen_create(wmWindowManager *wm, wmWindow *win, eSpace_Type space_type) +ScrArea *ED_area_offscreen_create(wmWindow *win, eSpace_Type space_type) { ScrArea *area = MEM_callocN(sizeof(*area), __func__); area->spacetype = space_type; screen_area_spacelink_add(WM_window_get_active_scene(win), area, space_type); - area_offscreen_init(wm, area); + area_offscreen_init(area); return area; } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 1e8cda30121..819057890fd 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -590,7 +590,7 @@ void wm_xr_session_actions_update(wmWindowManager *wm) if (win) { /* Ensure an XR area exists for events. */ if (!xr->runtime->area) { - xr->runtime->area = ED_area_offscreen_create(wm, win, SPACE_VIEW3D); + xr->runtime->area = ED_area_offscreen_create(win, SPACE_VIEW3D); } /* Implemented in D10944. */ From 9824df49c0694a67158598f232a55a80fe4ba2ce Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 5 Oct 2021 10:34:05 +0200 Subject: [PATCH 0526/1500] Fix memory leak when running test cases. Issue is that test cases re-uses draw manager. The new `DRWRegisteredDrawEngine` struct is only freed when a valid opengl context was found. what isn't the case when running test cases. Also made sure that re-using draw manager would use re-inited values. --- source/blender/draw/intern/draw_manager.c | 30 ++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 7a41142b177..8d8375556c7 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2928,8 +2928,27 @@ void DRW_engines_register(void) } } +static void drw_registered_engines_free(void) +{ + DRWRegisteredDrawEngine *next; + for (DRWRegisteredDrawEngine *type = g_registered_engines.engines.first; type; type = next) { + next = type->next; + BLI_remlink(&R_engines, type); + + if (type->draw_engine->engine_free) { + type->draw_engine->engine_free(); + } + MEM_freeN(type); + } + + BLI_listbase_clear(&g_registered_engines.engines); + g_registered_engines.len = 0; +} + void DRW_engines_free(void) { + drw_registered_engines_free(); + if (DST.gl_context == NULL) { /* Nothing has been setup. Nothing to clear. * Otherwise, DRW_opengl_context_enable can @@ -2948,17 +2967,6 @@ void DRW_engines_free(void) DRW_stats_free(); DRW_globals_free(); - DRWRegisteredDrawEngine *next; - for (DRWRegisteredDrawEngine *type = g_registered_engines.engines.first; type; type = next) { - next = type->next; - BLI_remlink(&R_engines, type); - - if (type->draw_engine->engine_free) { - type->draw_engine->engine_free(); - } - MEM_freeN(type); - } - DRW_UBO_FREE_SAFE(G_draw.block_ubo); DRW_UBO_FREE_SAFE(G_draw.view_ubo); DRW_TEXTURE_FREE_SAFE(G_draw.ramp); From 7df6f66ea202b93ae1abd9fe0b505bcd1b8511c2 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 5 Oct 2021 10:36:10 +0200 Subject: [PATCH 0527/1500] Silenced compilation warning when compiling using ASAN. --- extern/smaa_areatex/smaa_areatex.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extern/smaa_areatex/smaa_areatex.cpp b/extern/smaa_areatex/smaa_areatex.cpp index 7a4ff3a9831..c61543e10a0 100644 --- a/extern/smaa_areatex/smaa_areatex.cpp +++ b/extern/smaa_areatex/smaa_areatex.cpp @@ -574,6 +574,8 @@ Dbl2 AreaDiag::area(Dbl2 p1, Dbl2 p2, int left) Dbl2 d = p2 - p1; if (d.x == 0.0) return Dbl2(0.0, 1.0); + if (d.y == 0.0) + return Dbl2(1.0, 0.0); double x1 = (double)(1 + left); double x2 = x1 + 1.0; From b6ad0735a6b0a84aaee20d072bf218e262e8bb89 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 5 Oct 2021 10:45:28 +0200 Subject: [PATCH 0528/1500] Fix T86379: When using "Append" not handling properly RigidBody constraints This was simply never handled apparently. Also fixes a regression from recent append refactor that prevented RB objects to to properly handled too (since we instantiate loose objects in append step now, we need to handle RigidBody ones after that instantiation stage, otherwise nothing will happen since loose objects won't be in any scene). --- source/blender/blenkernel/intern/rigidbody.c | 49 ++++++++++++++++--- .../windowmanager/intern/wm_files_link.c | 13 +++-- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 1ea659b2d41..242bad163d8 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -1473,16 +1473,51 @@ static bool rigidbody_add_object_to_scene(Main *bmain, Scene *scene, Object *ob) return true; } -void BKE_rigidbody_ensure_local_object(Main *bmain, Object *ob) +static bool rigidbody_add_constraint_to_scene(Main *bmain, Scene *scene, Object *ob) { - if (ob->rigidbody_object == NULL) { - return; + /* Add rigid body world and group if they don't exist for convenience */ + RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); + if (rbw == NULL) { + rbw = BKE_rigidbody_create_world(scene); + if (rbw == NULL) { + return false; + } + + BKE_rigidbody_validate_sim_world(scene, rbw, false); + scene->rigidbody_world = rbw; } - /* Add newly local object to scene. */ - for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (BKE_scene_object_find(scene, ob)) { - rigidbody_add_object_to_scene(bmain, scene, ob); + if (rbw->constraints == NULL) { + rbw->constraints = BKE_collection_add(bmain, NULL, "RigidBodyConstraints"); + id_fake_user_set(&rbw->constraints->id); + } + + /* Add object to rigid body group. */ + BKE_collection_object_add(bmain, rbw->constraints, ob); + BKE_rigidbody_cache_reset(rbw); + + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&rbw->constraints->id, ID_RECALC_COPY_ON_WRITE); + + return true; +} + +void BKE_rigidbody_ensure_local_object(Main *bmain, Object *ob) +{ + if (ob->rigidbody_object != NULL) { + /* Add newly local object to scene. */ + for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + if (BKE_scene_object_find(scene, ob)) { + rigidbody_add_object_to_scene(bmain, scene, ob); + } + } + } + if (ob->rigidbody_constraint != NULL) { + /* Add newly local object to scene. */ + for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + if (BKE_scene_object_find(scene, ob)) { + rigidbody_add_constraint_to_scene(bmain, scene, ob); + } } } } diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index a73bea31669..cf3536213d2 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -579,6 +579,16 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, id->tag &= ~LIB_TAG_DOIT; } + + /* Finally, add rigid body objects and constraints to current RB world(s). */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = wm_append_loose_data_instantiate_process_check(item); + if (id == NULL || GS(id->name) != ID_OB) { + continue; + } + BKE_rigidbody_ensure_local_object(bmain, (Object *)id); + } } /** \} */ @@ -773,9 +783,6 @@ static void wm_append_do(WMLinkAppendData *lapp_data, local_appended_new_id); } - if (GS(local_appended_new_id->name) == ID_OB) { - BKE_rigidbody_ensure_local_object(bmain, (Object *)local_appended_new_id); - } if (set_fakeuser) { if (!ELEM(GS(local_appended_new_id->name), ID_OB, ID_GR)) { /* Do not set fake user on objects nor collections (instancing). */ From 71cf9f4b3f0b750325a0037c4ef5c43fea71248f Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 5 Oct 2021 10:50:40 +0200 Subject: [PATCH 0529/1500] Fix T91955: Cycles crash with denoising on non-available device For example, crash when attempting to use OptiX denoiser on systems without OptiX-capable device. Perform check that scene update happened without errors. Note that `et_error` makes progress to cancel, so the code was simplified a bit. --- intern/cycles/render/session.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 550188b196a..4f93c3a9054 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -157,6 +157,13 @@ void Session::run_main_render_loop() continue; } + /* Stop rendering if error happenned during scene update or other step of preparing scene + * for render. */ + if (device->have_error()) { + progress.set_error(device->error_message()); + break; + } + { /* buffers mutex is locked entirely while rendering each * sample, and released/reacquired on each iteration to allow @@ -172,10 +179,9 @@ void Session::run_main_render_loop() /* update status and timing */ update_status_time(); + /* Stop rendering if error happenned during path tracing. */ if (device->have_error()) { - const string &error_message = device->error_message(); - progress.set_error(error_message); - progress.set_cancel(error_message); + progress.set_error(device->error_message()); break; } } From 55b8fc718a378423cd4b6d93258779e201877b1d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 5 Oct 2021 13:08:44 +0200 Subject: [PATCH 0530/1500] Cycles: improve detection of HIP compiler for buildbot And fix various broken things in the HIP kernel compilation. --- CMakeLists.txt | 1 + build_files/cmake/Modules/FindHIP.cmake | 79 +++++++++++++++++++++++++ intern/cycles/cmake/external_libs.cmake | 12 +++- intern/cycles/kernel/CMakeLists.txt | 39 +++++------- 4 files changed, 106 insertions(+), 25 deletions(-) create mode 100644 build_files/cmake/Modules/FindHIP.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c4b8bf6dcd4..16842f3134b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,6 +406,7 @@ mark_as_advanced(WITH_CYCLES_CUDA_BUILD_SERIAL) set(CYCLES_TEST_DEVICES CPU CACHE STRING "Run regression tests on the specified device types (CPU CUDA OPTIX)" ) set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 compute_75 CACHE STRING "CUDA architectures to build binaries for") mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH) +option(WITH_CYCLES_HIP_BINARIES "Build Cycles HIP binaries" OFF) unset(PLATFORM_DEFAULT) option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON) option(WITH_CYCLES_DEBUG_NAN "Build Cycles with additional asserts for detecting NaNs and invalid values" OFF) diff --git a/build_files/cmake/Modules/FindHIP.cmake b/build_files/cmake/Modules/FindHIP.cmake new file mode 100644 index 00000000000..c68d78e5796 --- /dev/null +++ b/build_files/cmake/Modules/FindHIP.cmake @@ -0,0 +1,79 @@ +# - Find HIP compiler +# +# This module defines +# HIP_HIPCC_EXECUTABLE, the full path to the hipcc executable +# HIP_VERSION, the HIP compiler version +# +# HIP_FOUND, if the HIP toolkit is found. + +#============================================================================= +# Copyright 2021 Blender Foundation. +# +# Distributed under the OSI-approved BSD 3-Clause License, +# see accompanying file BSD-3-Clause-license.txt for details. +#============================================================================= + +# If HIP_ROOT_DIR was defined in the environment, use it. +if(NOT HIP_ROOT_DIR AND NOT $ENV{HIP_ROOT_DIR} STREQUAL "") + set(HIP_ROOT_DIR $ENV{HIP_ROOT_DIR}) +endif() + +set(_hip_SEARCH_DIRS + ${HIP_ROOT_DIR} +) + +find_program(HIP_HIPCC_EXECUTABLE + NAMES + hipcc + HINTS + ${_hip_SEARCH_DIRS} +) + +if(HIP_HIPCC_EXECUTABLE AND NOT EXISTS ${HIP_HIPCC_EXECUTABLE}) + message(WARNING "Cached or directly specified hipcc executable does not exist.") + set(HIP_FOUND FALSE) +elseif(HIP_HIPCC_EXECUTABLE) + set(HIP_FOUND TRUE) + + set(HIP_VERSION_MAJOR 0) + set(HIP_VERSION_MINOR 0) + set(HIP_VERSION_PATCH 0) + + # Get version from the output. + execute_process(COMMAND ${HIP_HIPCC_EXECUTABLE} --version + OUTPUT_VARIABLE HIP_VERSION_RAW + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Parse parts. + if(HIP_VERSION_RAW MATCHES "HIP version: .*") + # Strip the HIP prefix and get list of individual version components. + string(REGEX REPLACE + ".*HIP version: ([.0-9]+).*" "\\1" + HIP_SEMANTIC_VERSION "${HIP_VERSION_RAW}") + string(REPLACE "." ";" HIP_VERSION_PARTS "${HIP_SEMANTIC_VERSION}") + list(LENGTH HIP_VERSION_PARTS NUM_HIP_VERSION_PARTS) + + # Extract components into corresponding variables. + if(NUM_HIP_VERSION_PARTS GREATER 0) + list(GET HIP_VERSION_PARTS 0 HIP_VERSION_MAJOR) + endif() + if(NUM_HIP_VERSION_PARTS GREATER 1) + list(GET HIP_VERSION_PARTS 1 HIP_VERSION_MINOR) + endif() + if(NUM_HIP_VERSION_PARTS GREATER 2) + list(GET HIP_VERSION_PARTS 2 HIP_VERSION_PATCH) + endif() + + # Unset temp variables. + unset(NUM_HIP_VERSION_PARTS) + unset(HIP_SEMANTIC_VERSION) + unset(HIP_VERSION_PARTS) + endif() + + # Construct full semantic version. + set(HIP_VERSION "${HIP_VERSION_MAJOR}.${HIP_VERSION_MINOR}.${HIP_VERSION_PATCH}") + unset(HIP_VERSION_RAW) +else() + set(HIP_FOUND FALSE) +endif() diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake index b966edd4298..c1244ab740b 100644 --- a/intern/cycles/cmake/external_libs.cmake +++ b/intern/cycles/cmake/external_libs.cmake @@ -521,7 +521,7 @@ endif() if(WITH_CYCLES_CUDA_BINARIES OR NOT WITH_CUDA_DYNLOAD) find_package(CUDA) # Try to auto locate CUDA toolkit if(CUDA_FOUND) - message(STATUS "CUDA nvcc = ${CUDA_NVCC_EXECUTABLE}") + message(STATUS "Found CUDA ${CUDA_NVCC_EXECUTABLE} (${CUDA_VERSION})") else() message(STATUS "CUDA compiler not found, disabling WITH_CYCLES_CUDA_BINARIES") set(WITH_CYCLES_CUDA_BINARIES OFF) @@ -537,6 +537,16 @@ endif() # HIP ########################################################################### +if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP) + find_package(HIP) + if(HIP_FOUND) + message(STATUS "Found HIP ${HIP_HIPCC_EXECUTABLE} (${HIP_VERSION})") + else() + message(STATUS "HIP compiler not found, disabling WITH_CYCLES_HIP_BINARIES") + set(WITH_CYCLES_HIP_BINARIES OFF) + endif() +endif() + if(NOT WITH_HIP_DYNLOAD) set(WITH_HIP_DYNLOAD ON) endif() diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 7b56216e887..514b7f8263c 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -472,20 +472,10 @@ endif() # HIP module -if(WITH_CYCLES_HIP_BINARIES) +if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP) # 64 bit only set(HIP_BITS 64) - # HIP version - execute_process(COMMAND ${HIP_HIPCC_EXECUTABLE} "--version" OUTPUT_VARIABLE HIPCC_OUT) - string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\1" HIP_VERSION_MAJOR "${HIPCC_OUT}") - string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\2" HIP_VERSION_MINOR "${HIPCC_OUT}") - set(HIP_VERSION "${HIP_VERSION_MAJOR}${HIP_VERSION_MINOR}") - - - message(WARNING - "HIP version ${HIP_VERSION_MAJOR}.${HIP_VERSION_MINOR} detected") - # build for each arch set(hip_sources device/hip/kernel.cpp ${SRC_HEADERS} @@ -542,23 +532,24 @@ if(WITH_CYCLES_HIP_BINARIES) -D WITH_NANOVDB -I "${NANOVDB_INCLUDE_DIR}") endif() + + add_custom_command( + OUTPUT ${hip_file} + COMMAND ${HIP_HIPCC_EXECUTABLE} + -arch=${arch} + ${HIP_HIPCC_FLAGS} + --${format} + ${CMAKE_CURRENT_SOURCE_DIR}${hip_kernel_src} + ${hip_flags} + DEPENDS ${kernel_sources}) + delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${hip_file}" ${CYCLES_INSTALL_PATH}/lib) + list(APPEND hip_fatbins ${hip_file}) endmacro() set(prev_arch "none") foreach(arch ${CYCLES_HIP_BINARIES_ARCH}) - set(hip_hipcc_executable ${HIP_HIPCC_EXECUTABLE}) - set(hip_toolkit_root_dir ${HIP_TOOLKIT_ROOT_DIR}) - if(DEFINED hip_hipcc_executable AND DEFINED hip_toolkit_root_dir) - # Compile regular kernel - CYCLES_HIP_KERNEL_ADD(${arch} ${prev_arch} kernel "" "${hip_sources}" FALSE) - - if(WITH_CYCLES_HIP_BUILD_SERIAL) - set(prev_arch ${arch}) - endif() - - unset(hip_hipcc_executable) - unset(hip_toolkit_root_dir) - endif() + # Compile regular kernel + CYCLES_HIP_KERNEL_ADD(${arch} ${prev_arch} kernel "" "${hip_sources}" FALSE) endforeach() add_custom_target(cycles_kernel_hip ALL DEPENDS ${hip_fatbins}) From b1e6e63c22249edfb501a7579efa22810ea55aee Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Tue, 5 Oct 2021 15:37:19 +0200 Subject: [PATCH 0531/1500] Cleanup: Geometry Nodes dashed lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional change, just cleaning up the shader code a bit. Part of this is removing dead code (the discard was never called), and part is shuffling mix/max around based on feedback by Sybren Stüvel. --- .../blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl index 55d5d941290..402a07ad4e8 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl @@ -28,13 +28,10 @@ void main() float t = ANTIALIAS / DASH_WIDTH; float slope = 1.0 / (2.0 * t); - float alpha = min(1.0, max(0.0, slope * (normalized_distance_triangle - dashFactor + t))); + float unclamped_alpha = 1.0 - slope * (normalized_distance_triangle - dashFactor + t); + float alpha = max(0.0, min(unclamped_alpha, 1.0)); - if (alpha < 0.0) { - discard; - } - - fragColor.a *= 1.0 - alpha; + fragColor.a *= alpha; } fragColor.a *= smoothstep(1.0, 0.1, abs(colorGradient)); From 9a0850c8c25ea0c28f6ac313f076fd6a8563d0b4 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 5 Oct 2021 15:52:49 +0200 Subject: [PATCH 0532/1500] Cycles: Fix wrong GPU state calculation Currently was only used for logging, but better to fix the size so that it matches reality. The issue was caused by decoupling number of shadow intersections and using much higher number for CPU. This caused the total state on GPU to be logged as 10s of gigabytes instead of 100s of megabytes. Differential Revision: https://developer.blender.org/D12755 --- .../cycles/integrator/path_trace_work_gpu.cpp | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 17c49f244d2..7babc9d09fa 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -30,6 +30,31 @@ CCL_NAMESPACE_BEGIN +static size_t estimate_single_state_size() +{ + size_t state_size = 0; + +#define KERNEL_STRUCT_BEGIN(name) for (int array_index = 0;; array_index++) { +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) state_size += sizeof(type); +#define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) state_size += sizeof(type); +#define KERNEL_STRUCT_END(name) \ + break; \ + } +#define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \ + if (array_index == gpu_array_size - 1) { \ + break; \ + } \ + } +#include "kernel/integrator/integrator_state_template.h" +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_ARRAY_MEMBER +#undef KERNEL_STRUCT_END +#undef KERNEL_STRUCT_END_ARRAY + + return state_size; +} + PathTraceWorkGPU::PathTraceWorkGPU(Device *device, Film *film, DeviceScene *device_scene, @@ -47,7 +72,7 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device, num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE), work_tiles_(device, "work_tiles", MEM_READ_WRITE), display_rgba_half_(device, "display buffer half", MEM_READ_WRITE), - max_num_paths_(queue_->num_concurrent_states(sizeof(IntegratorStateCPU))), + max_num_paths_(queue_->num_concurrent_states(estimate_single_state_size())), min_num_active_paths_(queue_->num_concurrent_busy_states()), max_active_path_index_(0) { From dbe3981b0a805c1a40f42f57dc7ccc3d28270fda Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 5 Oct 2021 14:25:40 +0200 Subject: [PATCH 0533/1500] Cleanup: Better way to pass activate callbacks to Tree-View items The `ui::BasicTreeViewItem` took a function-like object to execute on item activation via the constructor. This was mainly intended to be used with lambdas. However, it's confusing to just have this lambda there, with no indication of what it's for (activation). Instead, assign the function-like object via an explicit `on_activate()` function. --- .../blender/editors/include/UI_tree_view.hh | 3 ++- source/blender/editors/interface/tree_view.cc | 8 +++++-- .../space_file/asset_catalog_tree_view.cc | 22 +++++++++++-------- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index a82aae021f2..4028bee9e15 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -239,10 +239,11 @@ class BasicTreeViewItem : public AbstractTreeViewItem { using ActivateFn = std::function; BIFIconID icon; - BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE, ActivateFn activate_fn = nullptr); + BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE); void build_row(uiLayout &row) override; void on_activate() override; + void on_activate(ActivateFn fn); protected: /** Created in the #build() function. */ diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 202e3dba2ab..63b12d4fc89 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -278,8 +278,7 @@ uiLayout *TreeViewLayoutBuilder::current_layout() const /* ---------------------------------------------------------------------- */ -BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_, ActivateFn activate_fn) - : icon(icon_), activate_fn_(activate_fn) +BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(icon_) { label_ = label; } @@ -330,6 +329,11 @@ void BasicTreeViewItem::on_activate() } } +void BasicTreeViewItem::on_activate(ActivateFn fn) +{ + activate_fn_ = fn; +} + BIFIconID BasicTreeViewItem::get_draw_icon() const { if (icon) { diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 9d6af5136d9..ff8775155c2 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -165,11 +165,12 @@ void AssetCatalogTreeView::add_all_item() { FileAssetSelectParams *params = params_; - ui::AbstractTreeViewItem &item = add_tree_item( - IFACE_("All"), ICON_HOME, [params](ui::BasicTreeViewItem & /*item*/) { - params->asset_catalog_visibility = FILE_SHOW_ASSETS_ALL_CATALOGS; - WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); - }); + AssetCatalogTreeViewAllItem &item = add_tree_item(IFACE_("All"), + ICON_HOME); + item.on_activate([params](ui::BasicTreeViewItem & /*item*/) { + params->asset_catalog_visibility = FILE_SHOW_ASSETS_ALL_CATALOGS; + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + }); if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS) { item.activate(); } @@ -180,10 +181,13 @@ void AssetCatalogTreeView::add_unassigned_item() FileAssetSelectParams *params = params_; AssetCatalogTreeViewUnassignedItem &item = add_tree_item( - IFACE_("Unassigned"), ICON_FILE_HIDDEN, [params](ui::BasicTreeViewItem & /*item*/) { - params->asset_catalog_visibility = FILE_SHOW_ASSETS_WITHOUT_CATALOG; - WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); - }); + IFACE_("Unassigned"), ICON_FILE_HIDDEN); + + item.on_activate([params](ui::BasicTreeViewItem & /*item*/) { + params->asset_catalog_visibility = FILE_SHOW_ASSETS_WITHOUT_CATALOG; + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + }); + if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG) { item.activate(); } From 758f3f7456ac1e31f411c4ac1b19760ad6e5539c Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 5 Oct 2021 16:01:01 +0200 Subject: [PATCH 0534/1500] Fix T91940: Asset Browser catalogs continuously redraw Issue was that the `on_activate()` callback of tree-items were continuously called, because the active-state was queried before we fully reconstructed the tree and its state from the previous redraw. Such issues could happen in more places, so I've refactored the API a bit to reflect the requirements for state queries, and include some sanity checks. The actual fix for the issue is to delay the state change until the tree is fully reconstructed, by letting the tree-items pass a callback to check if they should be active. --- .../blender/editors/include/UI_tree_view.hh | 42 +++++++++++++++-- source/blender/editors/interface/tree_view.cc | 47 ++++++++++++++++--- .../space_file/asset_catalog_tree_view.cc | 15 ++---- 3 files changed, 84 insertions(+), 20 deletions(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 4028bee9e15..46eaf56a3c0 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -139,11 +139,17 @@ class AbstractTreeView : public TreeViewItemContainer { friend TreeViewBuilder; friend TreeViewLayoutBuilder; + bool is_reconstructed_ = false; + public: virtual ~AbstractTreeView() = default; void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; + /** Check if the tree is fully (re-)constructed. That means, both #build_tree() and + * #update_from_old() have finished. */ + bool is_reconstructed() const; + protected: virtual void build_tree() = 0; @@ -156,6 +162,10 @@ class AbstractTreeView : public TreeViewItemContainer { const TreeViewItemContainer &old_items); static AbstractTreeViewItem *find_matching_child(const AbstractTreeViewItem &lookup_item, const TreeViewItemContainer &items); + /** Items may want to do additional work when state changes. But these state changes can only be + * reliably detected after the tree was reconstructed (see #is_reconstructed()). So this is done + * delayed. */ + void change_state_delayed(); void build_layout_from_tree(const TreeViewLayoutBuilder &builder); }; @@ -175,9 +185,15 @@ class AbstractTreeView : public TreeViewItemContainer { class AbstractTreeViewItem : public TreeViewItemContainer { friend class AbstractTreeView; + public: + using IsActiveFn = std::function; + + private: bool is_open_ = false; bool is_active_ = false; + IsActiveFn is_active_fn_; + protected: /** This label is used for identifying an item (together with its parent's labels). */ std::string label_{}; @@ -188,6 +204,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer { virtual void build_row(uiLayout &row) = 0; virtual void on_activate(); + virtual void is_active(IsActiveFn is_active_fn); virtual bool on_drop(const wmDrag &drag); virtual bool can_drop(const wmDrag &drag) const; /** Custom text to display when dragging over a tree item. Should explain what happens when @@ -211,16 +228,29 @@ class AbstractTreeViewItem : public TreeViewItemContainer { const AbstractTreeView &get_tree_view() const; int count_parents() const; - /** Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate() - * function and ensures this item's parents are not collapsed (so the item is visible). */ - void activate(); void deactivate(); + /** Must not be called before the tree was reconstructed (see #is_reconstructed()). Otherwise we + * can't be sure about the item state. */ bool is_active() const; void toggle_collapsed(); + /** Must not be called before the tree was reconstructed (see #is_reconstructed()). Otherwise we + * can't be sure about the item state. */ bool is_collapsed() const; void set_collapsed(bool collapsed); bool is_collapsible() const; void ensure_parents_uncollapsed(); + + protected: + /** Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate() + * function and ensures this item's parents are not collapsed (so the item is visible). + * Must not be called before the tree was reconstructed (see #is_reconstructed()). Otherwise we + * can't be sure about the current item state and may call state-change update functions + * incorrectly. */ + void activate(); + + private: + /** See #AbstractTreeView::change_state_delayed() */ + void change_state_delayed(); }; /** \} */ @@ -242,7 +272,6 @@ class BasicTreeViewItem : public AbstractTreeViewItem { BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE); void build_row(uiLayout &row) override; - void on_activate() override; void on_activate(ActivateFn fn); protected: @@ -255,6 +284,11 @@ class BasicTreeViewItem : public AbstractTreeViewItem { uiBut *button(); BIFIconID get_draw_icon() const; + + private: + static void tree_row_click_fn(struct bContext *C, void *arg1, void *arg2); + + void on_activate() override; }; /** \} */ diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 63b12d4fc89..28c757ddc79 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -92,17 +92,20 @@ void AbstractTreeView::update_from_old(uiBlock &new_block) { uiBlock *old_block = new_block.oldblock; if (!old_block) { + /* Initial construction, nothing to update. */ + is_reconstructed_ = true; return; } uiTreeViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block( &new_block, reinterpret_cast(this)); - if (!old_view_handle) { - return; - } + BLI_assert(old_view_handle); AbstractTreeView &old_view = reinterpret_cast(*old_view_handle); update_children_from_old_recursive(*this, old_view); + + /* Finished (re-)constructing the tree. */ + is_reconstructed_ = true; } void AbstractTreeView::update_children_from_old_recursive(const TreeViewItemContainer &new_items, @@ -134,6 +137,19 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child( return nullptr; } +bool AbstractTreeView::is_reconstructed() const +{ + return is_reconstructed_; +} + +void AbstractTreeView::change_state_delayed() +{ + BLI_assert_msg( + is_reconstructed(), + "These state changes are supposed to be delayed until reconstruction is completed"); + foreach_item([](AbstractTreeViewItem &item) { item.change_state_delayed(); }); +} + /* ---------------------------------------------------------------------- */ void AbstractTreeViewItem::on_activate() @@ -141,6 +157,11 @@ void AbstractTreeViewItem::on_activate() /* Do nothing by default. */ } +void AbstractTreeViewItem::is_active(IsActiveFn is_active_fn) +{ + is_active_fn_ = is_active_fn; +} + bool AbstractTreeViewItem::on_drop(const wmDrag & /*drag*/) { /* Do nothing by default. */ @@ -186,6 +207,9 @@ int AbstractTreeViewItem::count_parents() const void AbstractTreeViewItem::activate() { + BLI_assert_msg(get_tree_view().is_reconstructed(), + "Item activation can't be done until reconstruction is completed"); + if (is_active()) { return; } @@ -207,11 +231,15 @@ void AbstractTreeViewItem::deactivate() bool AbstractTreeViewItem::is_active() const { + BLI_assert_msg(get_tree_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); return is_active_; } bool AbstractTreeViewItem::is_collapsed() const { + BLI_assert_msg(get_tree_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); return is_collapsible() && !is_open_; } @@ -237,6 +265,13 @@ void AbstractTreeViewItem::ensure_parents_uncollapsed() } } +void AbstractTreeViewItem::change_state_delayed() +{ + if (is_active_fn_()) { + activate(); + } +} + /* ---------------------------------------------------------------------- */ TreeViewBuilder::TreeViewBuilder(uiBlock &block) : block_(block) @@ -247,6 +282,7 @@ void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view) { tree_view.build_tree(); tree_view.update_from_old(block_); + tree_view.change_state_delayed(); tree_view.build_layout_from_tree(TreeViewLayoutBuilder(block_)); } @@ -283,11 +319,10 @@ BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(ic label_ = label; } -static void tree_row_click_fn(struct bContext *UNUSED(C), void *but_arg1, void *UNUSED(arg2)) +void BasicTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, void *but_arg1, void * /*arg2*/) { uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1; - AbstractTreeViewItem &tree_item = reinterpret_cast( - *tree_row_but->tree_item); + BasicTreeViewItem &tree_item = reinterpret_cast(*tree_row_but->tree_item); /* Let a click on an opened item activate it, a second click will close it then. * TODO Should this be for asset catalogs only? */ diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index ff8775155c2..291582dac08 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -151,9 +151,7 @@ ui::BasicTreeViewItem &AssetCatalogTreeView::build_catalog_items_recursive( { ui::BasicTreeViewItem &view_item = view_parent_item.add_tree_item( &catalog); - if (is_active_catalog(catalog.get_catalog_id())) { - view_item.activate(); - } + view_item.is_active([this, &catalog]() { return is_active_catalog(catalog.get_catalog_id()); }); catalog.foreach_child([&view_item, this](AssetCatalogTreeItem &child) { build_catalog_items_recursive(view_item, child); @@ -171,9 +169,8 @@ void AssetCatalogTreeView::add_all_item() params->asset_catalog_visibility = FILE_SHOW_ASSETS_ALL_CATALOGS; WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); }); - if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS) { - item.activate(); - } + item.is_active( + [params]() { return params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS; }); } void AssetCatalogTreeView::add_unassigned_item() @@ -187,10 +184,8 @@ void AssetCatalogTreeView::add_unassigned_item() params->asset_catalog_visibility = FILE_SHOW_ASSETS_WITHOUT_CATALOG; WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); }); - - if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG) { - item.activate(); - } + item.is_active( + [params]() { return params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG; }); } bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const From 6e268a749fee16b442bcb3fba6cb6e08850d8389 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 21 Sep 2021 17:03:22 +0200 Subject: [PATCH 0535/1500] Fix adaptive sampling artifacts on tile boundaries Implement an overscan support for tiles, so that adaptive sampling can rely on the pixels neighbourhood. Differential Revision: https://developer.blender.org/D12599 --- intern/cycles/blender/blender_camera.cpp | 3 + intern/cycles/device/optix/device_impl.cpp | 8 ++- intern/cycles/integrator/denoiser_oidn.cpp | 8 ++- .../cycles/integrator/pass_accessor_cpu.cpp | 45 +++++++----- .../cycles/integrator/pass_accessor_gpu.cpp | 15 ++-- intern/cycles/integrator/path_trace.cpp | 14 ++-- intern/cycles/integrator/path_trace_work.cpp | 6 +- .../cycles/integrator/path_trace_work_gpu.cpp | 12 ++-- intern/cycles/kernel/device/gpu/kernel.h | 18 +++-- intern/cycles/render/buffers.cpp | 37 ++++++++-- intern/cycles/render/buffers.h | 9 +++ intern/cycles/render/session.cpp | 8 +++ intern/cycles/render/tile.cpp | 70 ++++++++++++++----- intern/cycles/render/tile.h | 6 ++ 14 files changed, 189 insertions(+), 70 deletions(-) diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index 4e8df5a99a6..93f19b73f53 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -927,6 +927,9 @@ BufferParams BlenderSync::get_buffer_params( params.height = height; } + params.window_width = params.width; + params.window_height = params.height; + return params; } diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index 49d4e22143f..f9a15553aa9 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -768,7 +768,13 @@ void OptiXDevice::denoise_color_read(DenoiseContext &context, const DenoisePass destination.num_components = 3; destination.pixel_stride = context.buffer_params.pass_stride; - pass_accessor.get_render_tile_pixels(context.render_buffers, context.buffer_params, destination); + BufferParams buffer_params = context.buffer_params; + buffer_params.window_x = 0; + buffer_params.window_y = 0; + buffer_params.window_width = buffer_params.width; + buffer_params.window_height = buffer_params.height; + + pass_accessor.get_render_tile_pixels(context.render_buffers, buffer_params, destination); } bool OptiXDevice::denoise_filter_color_preprocess(DenoiseContext &context, const DenoisePass &pass) diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp index 7fc2b2b1892..ee3b62668a7 100644 --- a/intern/cycles/integrator/denoiser_oidn.cpp +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -289,7 +289,13 @@ class OIDNDenoiseContext { * pixels. */ const PassAccessorCPU pass_accessor(pass_access_info, 1.0f, num_samples_); - pass_accessor.get_render_tile_pixels(render_buffers_, buffer_params_, destination); + BufferParams buffer_params = buffer_params_; + buffer_params.window_x = 0; + buffer_params.window_y = 0; + buffer_params.window_width = buffer_params.width; + buffer_params.window_height = buffer_params.height; + + pass_accessor.get_render_tile_pixels(render_buffers_, buffer_params, destination); } /* Read pass pixels using PassAccessor into a temporary buffer which is owned by the pass.. */ diff --git a/intern/cycles/integrator/pass_accessor_cpu.cpp b/intern/cycles/integrator/pass_accessor_cpu.cpp index 3c6691f6d43..80908271ff6 100644 --- a/intern/cycles/integrator/pass_accessor_cpu.cpp +++ b/intern/cycles/integrator/pass_accessor_cpu.cpp @@ -99,17 +99,22 @@ inline void PassAccessorCPU::run_get_pass_kernel_processor_float( { DCHECK_EQ(destination.stride, 0) << "Custom stride for float destination is not implemented."; - const float *buffer_data = render_buffers->buffer.data(); + const int64_t pass_stride = buffer_params.pass_stride; + const int64_t buffer_row_stride = buffer_params.stride * buffer_params.pass_stride; + + const float *window_data = render_buffers->buffer.data() + buffer_params.window_x * pass_stride + + buffer_params.window_y * buffer_row_stride; + const int pixel_stride = destination.pixel_stride ? destination.pixel_stride : destination.num_components; - tbb::parallel_for(0, buffer_params.height, [&](int64_t y) { - int64_t pixel_index = y * buffer_params.width; - for (int64_t x = 0; x < buffer_params.width; ++x, ++pixel_index) { - const int64_t input_pixel_offset = pixel_index * buffer_params.pass_stride; - const float *buffer = buffer_data + input_pixel_offset; - float *pixel = destination.pixels + (pixel_index + destination.offset) * pixel_stride; + tbb::parallel_for(0, buffer_params.window_height, [&](int64_t y) { + const float *buffer = window_data + y * buffer_row_stride; + float *pixel = destination.pixels + + (y * buffer_params.width + destination.offset) * pixel_stride; + for (int64_t x = 0; x < buffer_params.window_width; + ++x, buffer += pass_stride, pixel += pixel_stride) { processor(kfilm_convert, buffer, pixel); } }); @@ -123,26 +128,28 @@ inline void PassAccessorCPU::run_get_pass_kernel_processor_half_rgba( const Destination &destination, const Processor &processor) const { - const float *buffer_data = render_buffers->buffer.data(); + const int64_t pass_stride = buffer_params.pass_stride; + const int64_t buffer_row_stride = buffer_params.stride * buffer_params.pass_stride; + + const float *window_data = render_buffers->buffer.data() + buffer_params.window_x * pass_stride + + buffer_params.window_y * buffer_row_stride; half4 *dst_start = destination.pixels_half_rgba + destination.offset; const int destination_stride = destination.stride != 0 ? destination.stride : buffer_params.width; - tbb::parallel_for(0, buffer_params.height, [&](int64_t y) { - int64_t pixel_index = y * buffer_params.width; - half4 *dst_row_start = dst_start + y * destination_stride; - for (int64_t x = 0; x < buffer_params.width; ++x, ++pixel_index) { - const int64_t input_pixel_offset = pixel_index * buffer_params.pass_stride; - const float *buffer = buffer_data + input_pixel_offset; + tbb::parallel_for(0, buffer_params.window_height, [&](int64_t y) { + const float *buffer = window_data + y * buffer_row_stride; + half4 *pixel = dst_start + y * destination_stride; + for (int64_t x = 0; x < buffer_params.window_width; ++x, buffer += pass_stride, ++pixel) { - float pixel[4]; - processor(kfilm_convert, buffer, pixel); + float pixel_rgba[4]; + processor(kfilm_convert, buffer, pixel_rgba); - film_apply_pass_pixel_overlays_rgba(kfilm_convert, buffer, pixel); + film_apply_pass_pixel_overlays_rgba(kfilm_convert, buffer, pixel_rgba); - half4 *pixel_half_rgba = dst_row_start + x; - float4_store_half(&pixel_half_rgba->x, make_float4(pixel[0], pixel[1], pixel[2], pixel[3])); + float4_store_half(&pixel->x, + make_float4(pixel_rgba[0], pixel_rgba[1], pixel_rgba[2], pixel_rgba[3])); } }); } diff --git a/intern/cycles/integrator/pass_accessor_gpu.cpp b/intern/cycles/integrator/pass_accessor_gpu.cpp index eb80ba99655..7b01d061708 100644 --- a/intern/cycles/integrator/pass_accessor_gpu.cpp +++ b/intern/cycles/integrator/pass_accessor_gpu.cpp @@ -43,10 +43,13 @@ void PassAccessorGPU::run_film_convert_kernels(DeviceKernel kernel, KernelFilmConvert kfilm_convert; init_kernel_film_convert(&kfilm_convert, buffer_params, destination); - const int work_size = buffer_params.width * buffer_params.height; + const int work_size = buffer_params.window_width * buffer_params.window_height; const int destination_stride = destination.stride != 0 ? destination.stride : - buffer_params.width; + buffer_params.window_width; + + const int offset = buffer_params.window_x * buffer_params.pass_stride + + buffer_params.window_y * buffer_params.stride * buffer_params.pass_stride; if (destination.d_pixels) { DCHECK_EQ(destination.stride, 0) << "Custom stride for float destination is not implemented."; @@ -55,8 +58,8 @@ void PassAccessorGPU::run_film_convert_kernels(DeviceKernel kernel, const_cast(&destination.d_pixels), const_cast(&render_buffers->buffer.device_pointer), const_cast(&work_size), - const_cast(&buffer_params.width), - const_cast(&buffer_params.offset), + const_cast(&buffer_params.window_width), + const_cast(&offset), const_cast(&buffer_params.stride), const_cast(&destination.offset), const_cast(&destination_stride)}; @@ -70,8 +73,8 @@ void PassAccessorGPU::run_film_convert_kernels(DeviceKernel kernel, const_cast(&destination.d_pixels_half_rgba), const_cast(&render_buffers->buffer.device_pointer), const_cast(&work_size), - const_cast(&buffer_params.width), - const_cast(&buffer_params.offset), + const_cast(&buffer_params.window_width), + const_cast(&offset), const_cast(&buffer_params.stride), const_cast(&destination.offset), const_cast(&destination_stride)}; diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 7624b244175..3ea5c3c64b8 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -282,6 +282,12 @@ static BufferParams scale_buffer_params(const BufferParams ¶ms, int resoluti scaled_params.width = max(1, params.width / resolution_divider); scaled_params.height = max(1, params.height / resolution_divider); + + scaled_params.window_x = params.window_x / resolution_divider; + scaled_params.window_y = params.window_y / resolution_divider; + scaled_params.window_width = params.window_width / resolution_divider; + scaled_params.window_height = params.window_height / resolution_divider; + scaled_params.full_x = params.full_x / resolution_divider; scaled_params.full_y = params.full_y / resolution_divider; scaled_params.full_width = params.full_width / resolution_divider; @@ -1005,12 +1011,12 @@ bool PathTrace::set_render_tile_pixels(PassAccessor &pass_accessor, int2 PathTrace::get_render_tile_size() const { if (full_frame_state_.render_buffers) { - return make_int2(full_frame_state_.render_buffers->params.width, - full_frame_state_.render_buffers->params.height); + return make_int2(full_frame_state_.render_buffers->params.window_width, + full_frame_state_.render_buffers->params.window_height); } const Tile &tile = tile_manager_.get_current_tile(); - return make_int2(tile.width, tile.height); + return make_int2(tile.window_width, tile.window_height); } int2 PathTrace::get_render_tile_offset() const @@ -1020,7 +1026,7 @@ int2 PathTrace::get_render_tile_offset() const } const Tile &tile = tile_manager_.get_current_tile(); - return make_int2(tile.x, tile.y); + return make_int2(tile.x + tile.window_x, tile.y + tile.window_y); } int2 PathTrace::get_render_size() const diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp index c29177907c9..f626beb0aaa 100644 --- a/intern/cycles/integrator/path_trace_work.cpp +++ b/intern/cycles/integrator/path_trace_work.cpp @@ -191,8 +191,10 @@ PassAccessor::Destination PathTraceWork::get_display_destination_template( PassAccessor::Destination destination(film_->get_display_pass()); const int2 display_texture_size = display->get_texture_size(); - const int texture_x = effective_buffer_params_.full_x - effective_full_params_.full_x; - const int texture_y = effective_buffer_params_.full_y - effective_full_params_.full_y; + const int texture_x = effective_buffer_params_.full_x - effective_full_params_.full_x + + effective_buffer_params_.window_x; + const int texture_y = effective_buffer_params_.full_y - effective_full_params_.full_y + + effective_buffer_params_.window_y; destination.offset = texture_y * display_texture_size.x + texture_x; destination.stride = display_texture_size.x; diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 7babc9d09fa..c29b0fb039e 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -737,13 +737,13 @@ void PathTraceWorkGPU::copy_to_display_naive(PathTraceDisplay *display, { const int full_x = effective_buffer_params_.full_x; const int full_y = effective_buffer_params_.full_y; - const int width = effective_buffer_params_.width; - const int height = effective_buffer_params_.height; - const int final_width = buffers_->params.width; - const int final_height = buffers_->params.height; + const int width = effective_buffer_params_.window_width; + const int height = effective_buffer_params_.window_height; + const int final_width = buffers_->params.window_width; + const int final_height = buffers_->params.window_height; - const int texture_x = full_x - effective_full_params_.full_x; - const int texture_y = full_y - effective_full_params_.full_y; + const int texture_x = full_x - effective_full_params_.full_x + effective_buffer_params_.window_x; + const int texture_y = full_y - effective_full_params_.full_y + effective_buffer_params_.window_y; /* Re-allocate display memory if needed, and make sure the device pointer is allocated. * diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index 7b79c0aedfa..3379114fc62 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -424,8 +424,12 @@ ccl_device_inline void kernel_gpu_film_convert_common(const KernelFilmConvert *k return; } - const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kfilm_convert->pass_stride; - ccl_global const float *buffer = render_buffer + render_buffer_offset; + const int x = render_pixel_index % width; + const int y = render_pixel_index / width; + + ccl_global const float *buffer = render_buffer + offset + x * kfilm_convert->pass_stride + + y * stride * kfilm_convert->pass_stride; + ccl_global float *pixel = pixels + (render_pixel_index + dst_offset) * kfilm_convert->pixel_stride; @@ -451,17 +455,17 @@ ccl_device_inline void kernel_gpu_film_convert_half_rgba_common_rgba( return; } - const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kfilm_convert->pass_stride; - ccl_global const float *buffer = render_buffer + render_buffer_offset; + const int x = render_pixel_index % width; + const int y = render_pixel_index / width; + + ccl_global const float *buffer = render_buffer + offset + x * kfilm_convert->pass_stride + + y * stride * kfilm_convert->pass_stride; float pixel[4]; processor(kfilm_convert, buffer, pixel); film_apply_pass_pixel_overlays_rgba(kfilm_convert, buffer, pixel); - const int x = render_pixel_index % width; - const int y = render_pixel_index / width; - ccl_global half4 *out = ((ccl_global half4 *)rgba) + rgba_offset + y * rgba_stride + x; float4_store_half((ccl_global half *)out, make_float4(pixel[0], pixel[1], pixel[2], pixel[3])); } diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 3682b55049a..00b4284c22b 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -97,6 +97,11 @@ NODE_DEFINE(BufferParams) SOCKET_INT(width, "Width", 0); SOCKET_INT(height, "Height", 0); + SOCKET_INT(window_x, "Window X", 0); + SOCKET_INT(window_y, "Window Y", 0); + SOCKET_INT(window_width, "Window Width", 0); + SOCKET_INT(window_height, "Window Height", 0); + SOCKET_INT(full_x, "Full X", 0); SOCKET_INT(full_y, "Full Y", 0); SOCKET_INT(full_width, "Full Width", 0); @@ -233,13 +238,31 @@ void BufferParams::update_offset_stride() bool BufferParams::modified(const BufferParams &other) const { - if (!(width == other.width && height == other.height && full_x == other.full_x && - full_y == other.full_y && full_width == other.full_width && - full_height == other.full_height && offset == other.offset && stride == other.stride && - pass_stride == other.pass_stride && layer == other.layer && view == other.view && - exposure == other.exposure && - use_approximate_shadow_catcher == other.use_approximate_shadow_catcher && - use_transparent_background == other.use_transparent_background)) { + if (width != other.width || height != other.height) { + return true; + } + + if (full_x != other.full_x || full_y != other.full_y || full_width != other.full_width || + full_height != other.full_height) { + return true; + } + + if (window_x != other.window_x || window_y != other.window_y || + window_width != other.window_width || window_height != other.window_height) { + return true; + } + + if (offset != other.offset || stride != other.stride || pass_stride != other.pass_stride) { + return true; + } + + if (layer != other.layer || view != other.view) { + return false; + } + + if (exposure != other.exposure || + use_approximate_shadow_catcher != other.use_approximate_shadow_catcher || + use_transparent_background != other.use_transparent_background) { return true; } diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h index 184ac7197af..3cf826f14d6 100644 --- a/intern/cycles/render/buffers.h +++ b/intern/cycles/render/buffers.h @@ -82,6 +82,15 @@ class BufferParams : public Node { int width = 0; int height = 0; + /* Windows defines which part of the buffers is visible. The part outside of the window is + * considered an "overscan". + * + * Window X and Y are relative to the position of the buffer in the full buffer. */ + int window_x = 0; + int window_y = 0; + int window_width = 0; + int window_height = 0; + /* Offset into and width/height of the full buffer. */ int full_x = 0; int full_y = 0; diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 4f93c3a9054..8d2d950f661 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -286,12 +286,20 @@ RenderWork Session::run_update_for_next_iteration() BufferParams tile_params = buffer_params_; const Tile &tile = tile_manager_.get_current_tile(); + tile_params.width = tile.width; tile_params.height = tile.height; + + tile_params.window_x = tile.window_x; + tile_params.window_y = tile.window_y; + tile_params.window_width = tile.window_width; + tile_params.window_height = tile.window_height; + tile_params.full_x = tile.x + buffer_params_.full_x; tile_params.full_y = tile.y + buffer_params_.full_y; tile_params.full_width = buffer_params_.full_width; tile_params.full_height = buffer_params_.full_height; + tile_params.update_offset_stride(); path_trace_->reset(buffer_params_, tile_params); diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 7e53a9d0911..75c1f78982d 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -372,8 +372,17 @@ void TileManager::update(const BufferParams ¶ms, const Scene *scene) configure_image_spec_from_buffer(&write_state_.image_spec, buffer_params_, tile_size_); const DenoiseParams denoise_params = scene->integrator->get_denoise_params(); + const AdaptiveSampling adaptive_sampling = scene->integrator->get_adaptive_sampling(); + node_to_image_spec_atttributes( &write_state_.image_spec, &denoise_params, ATTR_DENOISE_SOCKET_PREFIX); + + if (adaptive_sampling.use) { + overscan_ = 4; + } + else { + overscan_ = 0; + } } bool TileManager::done() @@ -399,19 +408,29 @@ Tile TileManager::get_tile_for_index(int index) const /* TODO(sergey): Consider using hilbert spiral, or. maybe, even configurable. Not sure this * brings a lot of value since this is only applicable to BIG tiles. */ - const int tile_y = index / tile_state_.num_tiles_x; - const int tile_x = index - tile_y * tile_state_.num_tiles_x; + const int tile_index_y = index / tile_state_.num_tiles_x; + const int tile_index_x = index - tile_index_y * tile_state_.num_tiles_x; + + const int tile_x = tile_index_x * tile_size_.x; + const int tile_y = tile_index_y * tile_size_.y; Tile tile; - tile.x = tile_x * tile_size_.x; - tile.y = tile_y * tile_size_.y; - tile.width = tile_size_.x; - tile.height = tile_size_.y; + tile.x = tile_x - overscan_; + tile.y = tile_y - overscan_; + tile.width = tile_size_.x + 2 * overscan_; + tile.height = tile_size_.y + 2 * overscan_; + tile.x = max(tile.x, 0); + tile.y = max(tile.y, 0); tile.width = min(tile.width, buffer_params_.width - tile.x); tile.height = min(tile.height, buffer_params_.height - tile.y); + tile.window_x = tile_x - tile.x; + tile.window_y = tile_y - tile.y; + tile.window_width = min(tile_size_.x, buffer_params_.width - (tile.x + tile.window_x)); + tile.window_height = min(tile_size_.y, buffer_params_.height - (tile.y + tile.window_y)); + return tile; } @@ -483,11 +502,22 @@ bool TileManager::write_tile(const RenderBuffers &tile_buffers) DCHECK_EQ(tile_buffers.params.pass_stride, buffer_params_.pass_stride); + vector pixel_storage; + const BufferParams &tile_params = tile_buffers.params; - const float *pixels = tile_buffers.buffer.data(); - const int tile_x = tile_params.full_x - buffer_params_.full_x; - const int tile_y = tile_params.full_y - buffer_params_.full_y; + const int tile_x = tile_params.full_x - buffer_params_.full_x + tile_params.window_x; + const int tile_y = tile_params.full_y - buffer_params_.full_y + tile_params.window_y; + + const int64_t pass_stride = tile_params.pass_stride; + const int64_t tile_row_stride = tile_params.width * pass_stride; + + const int64_t xstride = pass_stride * sizeof(float); + const int64_t ystride = xstride * tile_params.width; + const int64_t zstride = ystride * tile_params.height; + + const float *pixels = tile_buffers.buffer.data() + tile_params.window_x * pass_stride + + tile_params.window_y * tile_row_stride; VLOG(3) << "Write tile at " << tile_x << ", " << tile_y; @@ -499,13 +529,16 @@ bool TileManager::write_tile(const RenderBuffers &tile_buffers) * The only thing we have to ensure is that the tile_x and tile_y are a multiple of the * image tile size, which happens in compute_render_tile_size. */ if (!write_state_.tile_out->write_tiles(tile_x, - tile_x + tile_params.width, + tile_x + tile_params.window_width, tile_y, - tile_y + tile_params.height, + tile_y + tile_params.window_height, 0, 1, TypeDesc::FLOAT, - pixels)) { + pixels, + xstride, + ystride, + zstride)) { LOG(ERROR) << "Error writing tile " << write_state_.tile_out->geterror(); return false; } @@ -531,12 +564,15 @@ void TileManager::finish_write_tiles() ++tile_index) { const Tile tile = get_tile_for_index(tile_index); - VLOG(3) << "Write dummy tile at " << tile.x << ", " << tile.y; + const int tile_x = tile.x + tile.window_x; + const int tile_y = tile.y + tile.window_y; - write_state_.tile_out->write_tiles(tile.x, - tile.x + tile.width, - tile.y, - tile.y + tile.height, + VLOG(3) << "Write dummy tile at " << tile_x << ", " << tile_y; + + write_state_.tile_out->write_tiles(tile_x, + tile_x + tile.window_width, + tile_y, + tile_y + tile.window_height, 0, 1, TypeDesc::FLOAT, diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h index 08eaa4034f0..a13afaea64e 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/render/tile.h @@ -35,6 +35,9 @@ class Tile { int x = 0, y = 0; int width = 0, height = 0; + int window_x = 0, window_y = 0; + int window_width = 0, window_height = 0; + Tile() { } @@ -128,6 +131,9 @@ class TileManager { int2 tile_size_ = make_int2(0, 0); + /* Number of extra pixels around the actual tile to render. */ + int overscan_ = 0; + BufferParams buffer_params_; /* Tile scheduling state. */ From c11585a82f97e51c01c4f4f309b85bdf7602ca08 Mon Sep 17 00:00:00 2001 From: Patrick Mours Date: Tue, 5 Oct 2021 16:36:33 +0200 Subject: [PATCH 0536/1500] Add missing "CUDA_ERROR_UNSUPPORTED_PTX_VERSION" to CUEW This is required for Cycles to report a meaningful error message when it fails to load a PTX module created with a newer CUDA toolkit version than the driver supports. Ref T91879 --- extern/cuew/include/cuew.h | 1 + extern/cuew/src/cuew.c | 1 + 2 files changed, 2 insertions(+) diff --git a/extern/cuew/include/cuew.h b/extern/cuew/include/cuew.h index a2142b8f2ba..5979f48e43d 100644 --- a/extern/cuew/include/cuew.h +++ b/extern/cuew/include/cuew.h @@ -609,6 +609,7 @@ typedef enum cudaError_enum { CUDA_ERROR_INVALID_GRAPHICS_CONTEXT = 219, CUDA_ERROR_NVLINK_UNCORRECTABLE = 220, CUDA_ERROR_JIT_COMPILER_NOT_FOUND = 221, + CUDA_ERROR_UNSUPPORTED_PTX_VERSION = 222, CUDA_ERROR_INVALID_SOURCE = 300, CUDA_ERROR_FILE_NOT_FOUND = 301, CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND = 302, diff --git a/extern/cuew/src/cuew.c b/extern/cuew/src/cuew.c index 7a1b0018a24..9eba9306323 100644 --- a/extern/cuew/src/cuew.c +++ b/extern/cuew/src/cuew.c @@ -736,6 +736,7 @@ const char *cuewErrorString(CUresult result) { case CUDA_ERROR_INVALID_GRAPHICS_CONTEXT: return "Invalid graphics context"; case CUDA_ERROR_NVLINK_UNCORRECTABLE: return "Nvlink uncorrectable"; case CUDA_ERROR_JIT_COMPILER_NOT_FOUND: return "Jit compiler not found"; + case CUDA_ERROR_UNSUPPORTED_PTX_VERSION: return "Unsupported PTX version"; case CUDA_ERROR_INVALID_SOURCE: return "Invalid source"; case CUDA_ERROR_FILE_NOT_FOUND: return "File not found"; case CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND: return "Link to a shared object failed to resolve"; From 11be9edae263194d0ce4fa0d43ad44f04db9a491 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 5 Oct 2021 17:04:35 +0200 Subject: [PATCH 0537/1500] Fix missing proper 'make local' call for liboverrides from outliner. Also includes minor improvements to `BKE_lib_override_library_make_local` itself. This is a complement to rB37458798fa02c. --- source/blender/blenkernel/intern/lib_override.c | 10 ++++++++++ source/blender/editors/space_outliner/outliner_tools.c | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 68675e5fc91..59e431b737c 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1918,6 +1918,16 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) */ void BKE_lib_override_library_make_local(ID *id) { + if (!ID_IS_OVERRIDE_LIBRARY(id)) { + return; + } + if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id)) { + /* We should never directly 'make local' virtual overrides (aka shape keys). */ + BLI_assert_unreachable(); + id->flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE; + return; + } + BKE_lib_override_library_free(&id->override_library, true); Key *shape_key = BKE_key_from_id(id); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 9e314701719..75bdc5dbac6 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -742,7 +742,7 @@ static void id_local_fn(bContext *C, } } else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) { - BKE_lib_override_library_free(&tselem->id->override_library, true); + BKE_lib_override_library_make_local(tselem->id); } } From 6eefcd7d78389fbcb92f6d482024224ceea5d1fa Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Tue, 5 Oct 2021 17:09:44 +0200 Subject: [PATCH 0538/1500] GPencil: Remove unused spacetype check in Paint operator The Paint operator only works in SPACE_VIEW3D and this is checked in the poll mtehod, so it's not logic check again. These checkings were part of the old grease pencil but it was not removed. --- .../blender/editors/gpencil/gpencil_paint.c | 117 +++++++----------- 1 file changed, 44 insertions(+), 73 deletions(-) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index ffc50a86fc6..2aa4b3b3282 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -869,7 +869,7 @@ static short gpencil_stroke_addpoint(tGPsdata *p, pt->time = (float)(curtime - p->inittime); /* point uv (only 3d view) */ - if ((p->area->spacetype == SPACE_VIEW3D) && (gpd->runtime.sbuffer_used > 0)) { + if (gpd->runtime.sbuffer_used > 0) { tGPspoint *ptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1; bGPDspoint spt, spt2; @@ -1335,8 +1335,7 @@ static bool gpencil_stroke_eraser_is_occluded( gp_settings = eraser->gpencil_settings; } - if ((gp_settings != NULL) && (p->area->spacetype == SPACE_VIEW3D) && - (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { + if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { RegionView3D *rv3d = p->region->regiondata; const int mval_i[2] = {x, y}; @@ -1729,12 +1728,10 @@ static void gpencil_stroke_doeraser(tGPsdata *p) rect.xmax = p->mval[0] + calc_radius; rect.ymax = p->mval[1] + calc_radius; - if (p->area->spacetype == SPACE_VIEW3D) { - if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { - View3D *v3d = p->area->spacedata.first; - view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); - } + if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { + View3D *v3d = p->area->spacedata.first; + view3d_region_operator_needs_opengl(p->win, p->region); + ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); } /* loop over all layers too, since while it's easy to restrict editing to @@ -1961,48 +1958,35 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) unit_m4(p->imat); unit_m4(p->mat); - switch (curarea->spacetype) { - /* supported views first */ - case SPACE_VIEW3D: { - /* set current area - * - must verify that region data is 3D-view (and not something else) - */ - /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ - p->area = curarea; - p->region = region; - p->align_flag = &ts->gpencil_v3d_align; + /* set current area + * - must verify that region data is 3D-view (and not something else) + */ + /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ + p->area = curarea; + p->region = region; + p->align_flag = &ts->gpencil_v3d_align; - if (region->regiondata == NULL) { - p->status = GP_STATUS_ERROR; - return 0; - } - - if ((!obact) || (obact->type != OB_GPENCIL)) { - View3D *v3d = p->area->spacedata.first; - /* if active object doesn't exist or isn't a GP Object, create one */ - const float *cur = p->scene->cursor.location; - - ushort local_view_bits = 0; - if (v3d->localvd) { - local_view_bits = v3d->local_view_uuid; - } - /* create new default object */ - obact = ED_gpencil_add_object(C, cur, local_view_bits); - } - /* assign object after all checks to be sure we have one active */ - p->ob = obact; - p->ob_eval = (Object *)DEG_get_evaluated_object(p->depsgraph, p->ob); - - break; - } - - /* unsupported views */ - default: { - p->status = GP_STATUS_ERROR; - return 0; - } + if (region->regiondata == NULL) { + p->status = GP_STATUS_ERROR; + return 0; } + if ((!obact) || (obact->type != OB_GPENCIL)) { + View3D *v3d = p->area->spacedata.first; + /* if active object doesn't exist or isn't a GP Object, create one */ + const float *cur = p->scene->cursor.location; + + ushort local_view_bits = 0; + if (v3d->localvd) { + local_view_bits = v3d->local_view_uuid; + } + /* create new default object */ + obact = ED_gpencil_add_object(C, cur, local_view_bits); + } + /* assign object after all checks to be sure we have one active */ + p->ob = obact; + p->ob_eval = (Object *)DEG_get_evaluated_object(p->depsgraph, p->ob); + /* get gp-data */ gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); if ((gpd_ptr == NULL) || ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { @@ -2238,17 +2222,15 @@ static void gpencil_paint_initstroke(tGPsdata *p, /* when drawing in the camera view, in 2D space, set the subrect */ p->subrect = NULL; if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { - if (p->area->spacetype == SPACE_VIEW3D) { - View3D *v3d = p->area->spacedata.first; - RegionView3D *rv3d = p->region->regiondata; + View3D *v3d = p->area->spacedata.first; + RegionView3D *rv3d = p->region->regiondata; - /* for camera view set the subrect */ - if (rv3d->persp == RV3D_CAMOB) { - /* no shift */ - ED_view3d_calc_camera_border( - p->scene, depsgraph, p->region, v3d, rv3d, &p->subrect_data, true); - p->subrect = &p->subrect_data; - } + /* for camera view set the subrect */ + if (rv3d->persp == RV3D_CAMOB) { + /* no shift */ + ED_view3d_calc_camera_border( + p->scene, depsgraph, p->region, v3d, rv3d, &p->subrect_data, true); + p->subrect = &p->subrect_data; } } @@ -2267,12 +2249,7 @@ static void gpencil_paint_initstroke(tGPsdata *p, /* check if points will need to be made in view-aligned space */ if (*p->align_flag & GP_PROJECT_VIEWSPACE) { - switch (p->area->spacetype) { - case SPACE_VIEW3D: { - p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; - break; - } - } + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; } if (!changed) { /* Copy the brush to avoid a full tag (very slow). */ @@ -2425,15 +2402,9 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op) p->eraser->size = p->radius; } - /* restore cursor to indicate end of drawing */ - if (p->area->spacetype != SPACE_VIEW3D) { - WM_cursor_modal_restore(CTX_wm_window(C)); - } - else { - /* drawing batch cache is dirty now */ - bGPdata *gpd = CTX_data_gpencil_data(C); - gpencil_update_cache(gpd); - } + /* drawing batch cache is dirty now */ + bGPdata *gpd = CTX_data_gpencil_data(C); + gpencil_update_cache(gpd); /* clear undo stack */ gpencil_undo_finish(); From 0a1a173e57d0f9e797dbb4972adda2993fccd6d7 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Tue, 5 Oct 2021 18:45:47 +0200 Subject: [PATCH 0539/1500] Cleanup: Make anim_getnew in the VSE less confusing It was using dummy image buffers to indicate if an animation container could be initialized or not. Use booleans instead. --- source/blender/imbuf/intern/anim_movie.c | 44 ++++++++++-------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 13f9356751e..4eb078113dd 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -1496,16 +1496,15 @@ static void free_anim_ffmpeg(struct anim *anim) #endif -/* Try next picture to read */ -/* No picture, try to open next animation */ -/* Succeed, remove first image from animation */ +/* Try to init the anim struct. + * Returns true on success.*/ -static ImBuf *anim_getnew(struct anim *anim) +static bool anim_getnew(struct anim *anim) { - struct ImBuf *ibuf = NULL; - + BLI_assert(anim->curtype == ANIM_NONE); if (anim == NULL) { - return NULL; + /* Nothing to init. */ + return false; } free_anim_movie(anim); @@ -1518,44 +1517,43 @@ static ImBuf *anim_getnew(struct anim *anim) free_anim_ffmpeg(anim); #endif - if (anim->curtype != 0) { - return NULL; - } anim->curtype = imb_get_anim_type(anim->name); switch (anim->curtype) { - case ANIM_SEQUENCE: - ibuf = IMB_loadiffname(anim->name, anim->ib_flags, anim->colorspace); + case ANIM_SEQUENCE: { + ImBuf *ibuf = IMB_loadiffname(anim->name, anim->ib_flags, anim->colorspace); if (ibuf) { BLI_strncpy(anim->first, anim->name, sizeof(anim->first)); anim->duration_in_frames = 1; + IMB_freeImBuf(ibuf); + } + else { + return false; } break; + } case ANIM_MOVIE: if (startmovie(anim)) { - return NULL; + return false; } - ibuf = IMB_allocImBuf(anim->x, anim->y, 24, 0); /* fake */ break; #ifdef WITH_AVI case ANIM_AVI: if (startavi(anim)) { printf("couldn't start avi\n"); - return NULL; + return false; } - ibuf = IMB_allocImBuf(anim->x, anim->y, 24, 0); break; #endif #ifdef WITH_FFMPEG case ANIM_FFMPEG: if (startffmpeg(anim)) { - return 0; + return false; } - ibuf = IMB_allocImBuf(anim->x, anim->y, 24, 0); break; #endif } - return ibuf; + return true; } struct ImBuf *IMB_anim_previewframe(struct anim *anim) @@ -1589,14 +1587,10 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, filter_y = (anim->ib_flags & IB_animdeinterlace); if (preview_size == IMB_PROXY_NONE) { - if (anim->curtype == 0) { - ibuf = anim_getnew(anim); - if (ibuf == NULL) { + if (anim->curtype == ANIM_NONE) { + if (!anim_getnew(anim)) { return NULL; } - - IMB_freeImBuf(ibuf); /* ???? */ - ibuf = NULL; } if (position < 0) { From 88c02bf826df371be89af326515a3216fb449673 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Tue, 5 Oct 2021 18:49:45 +0200 Subject: [PATCH 0540/1500] VSE: Free animation strip data if they are not visible Previously we would only free animation strip data when doing final renders. If not doing a final render or simply just playing back videos in the VSE, we would not free decoders or non VSE cache data from the strips. This would lead to memory usage exploding in complex VSE scenes. Now we instead use the dumb apporach of freeing everything that is not currently visible. --- source/blender/render/intern/pipeline.c | 2 +- source/blender/sequencer/intern/render.c | 3 +++ source/blender/sequencer/intern/strip_relations.c | 3 --- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 7c5259a1c5c..1bf0dfe079c 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -1440,7 +1440,7 @@ static void do_render_full_pipeline(Render *re) /* ensure no images are in memory from previous animated sequences */ BKE_image_all_free_anim_ibufs(re->main, re->r.cfra); - SEQ_relations_free_all_anim_ibufs(re->scene, re->r.cfra); + SEQ_cache_cleanup(re->scene); if (RE_engine_render(re, true)) { /* in this case external render overrides all */ diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index cf3f9d5cba5..6b233cd20fb 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -69,6 +69,7 @@ #include "SEQ_iterator.h" #include "SEQ_modifier.h" #include "SEQ_proxy.h" +#include "SEQ_relations.h" #include "SEQ_render.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" @@ -1882,6 +1883,8 @@ ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame, } seq_cache_free_temp_cache(context->scene, context->task_id, timeline_frame); + /* Make sure we only keep the `anim` data for strips that are in view. */ + SEQ_relations_free_all_anim_ibufs(context->scene, timeline_frame); if (count && !out) { BLI_mutex_lock(&seq_render_mutex); diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c index 9822bfe38f9..444d3581b3d 100644 --- a/source/blender/sequencer/intern/strip_relations.c +++ b/source/blender/sequencer/intern/strip_relations.c @@ -354,7 +354,6 @@ void SEQ_relations_update_changed_seq_and_deps(Scene *scene, } } -/* Unused */ static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int timeline_frame) { for (Sequence *seq = seqbase->first; seq != NULL; seq = seq->next) { @@ -367,7 +366,6 @@ static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int timeline_frame) } } -/* Unused */ void SEQ_relations_free_all_anim_ibufs(Scene *scene, int timeline_frame) { Editing *ed = SEQ_editing_get(scene); @@ -375,7 +373,6 @@ void SEQ_relations_free_all_anim_ibufs(Scene *scene, int timeline_frame) return; } sequencer_all_free_anim_ibufs(&ed->seqbase, timeline_frame); - SEQ_cache_cleanup(scene); } static Sequence *sequencer_check_scene_recursion(Scene *scene, ListBase *seqbase) From 16e7a7b5b192fe19f271a28cfb55c1b19118ccb1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 5 Oct 2021 13:15:31 -0500 Subject: [PATCH 0541/1500] Cleanup: Clang tidy --- source/blender/draw/intern/draw_texture_pool.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/intern/draw_texture_pool.cc b/source/blender/draw/intern/draw_texture_pool.cc index 544a763ddb9..61697465784 100644 --- a/source/blender/draw/intern/draw_texture_pool.cc +++ b/source/blender/draw/intern/draw_texture_pool.cc @@ -28,10 +28,10 @@ using namespace blender; -typedef struct DRWTexturePoolHandle { +struct DRWTexturePoolHandle { uint64_t users_bits; GPUTexture *texture; -} DRWTexturePoolHandle; +}; struct DRWTexturePool { Vector users; From 432d5bc692e229430e09d429184958b2de18e504 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 5 Oct 2021 13:16:50 -0500 Subject: [PATCH 0542/1500] Cleanup: Remove unused DerivedMesh functions The long term goal is completely removing DerivedMesh, and these functions are making some refactoring of mesh normals (T91186) more complicated. They are not used anywhere. --- source/blender/blenkernel/BKE_DerivedMesh.h | 9 -- .../blender/blenkernel/intern/cdderivedmesh.c | 22 --- .../blender/blenkernel/intern/subsurf_ccg.c | 141 ------------------ 3 files changed, 172 deletions(-) diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index e3954e134da..c95190d2c83 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -157,15 +157,6 @@ struct DerivedMesh { int (*getNumLoops)(DerivedMesh *dm); int (*getNumPolys)(DerivedMesh *dm); - /** Copy a single vert/edge/tessellated face from the derived mesh into - * `*r_{vert/edge/face}`. note that the current implementation - * of this function can be quite slow, iterating over all - * elements (editmesh) - */ - void (*getVert)(DerivedMesh *dm, int index, struct MVert *r_vert); - void (*getEdge)(DerivedMesh *dm, int index, struct MEdge *r_edge); - void (*getTessFace)(DerivedMesh *dm, int index, struct MFace *r_face); - /** Return a pointer to the entire array of verts/edges/face from the * derived mesh. if such an array does not exist yet, it will be created, * and freed on the next ->release(). consider using getVert/Edge/Face if diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 039a971fe2c..c93d320787a 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -103,24 +103,6 @@ static int cdDM_getNumPolys(DerivedMesh *dm) return dm->numPolyData; } -static void cdDM_getVert(DerivedMesh *dm, int index, MVert *r_vert) -{ - CDDerivedMesh *cddm = (CDDerivedMesh *)dm; - *r_vert = cddm->mvert[index]; -} - -static void cdDM_getEdge(DerivedMesh *dm, int index, MEdge *r_edge) -{ - CDDerivedMesh *cddm = (CDDerivedMesh *)dm; - *r_edge = cddm->medge[index]; -} - -static void cdDM_getTessFace(DerivedMesh *dm, int index, MFace *r_face) -{ - CDDerivedMesh *cddm = (CDDerivedMesh *)dm; - *r_face = cddm->mface[index]; -} - static void cdDM_copyVertArray(DerivedMesh *dm, MVert *r_vert) { CDDerivedMesh *cddm = (CDDerivedMesh *)dm; @@ -231,10 +213,6 @@ static CDDerivedMesh *cdDM_create(const char *desc) dm->getNumLoops = cdDM_getNumLoops; dm->getNumPolys = cdDM_getNumPolys; - dm->getVert = cdDM_getVert; - dm->getEdge = cdDM_getEdge; - dm->getTessFace = cdDM_getTessFace; - dm->copyVertArray = cdDM_copyVertArray; dm->copyEdgeArray = cdDM_copyEdgeArray; dm->copyTessFaceArray = cdDM_copyTessFaceArray; diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index a1b45c2ac7d..0c58c8e8a5a 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -912,141 +912,6 @@ static void ccgDM_getFinalVertNo(DerivedMesh *dm, int vertNum, float r_no[3]) normal_short_to_float_v3(r_no, mvert.no); } -static void ccgDM_getFinalEdge(DerivedMesh *dm, int edgeNum, MEdge *med) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - CCGSubSurf *ss = ccgdm->ss; - int i; - - memset(med, 0, sizeof(*med)); - - if (edgeNum < ccgdm->edgeMap[0].startEdge) { - /* this edge comes from face data */ - int lastface = ccgSubSurf_getNumFaces(ss) - 1; - CCGFace *f; - int x, y, grid /*, numVerts*/; - int offset; - int gridSize = ccgSubSurf_getGridSize(ss); - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int gridSideEdges; - int gridInternalEdges; - - i = 0; - while (i < lastface && edgeNum >= ccgdm->faceMap[i + 1].startEdge) { - i++; - } - - f = ccgdm->faceMap[i].face; - /* numVerts = ccgSubSurf_getFaceNumVerts(f); */ /*UNUSED*/ - - gridSideEdges = gridSize - 1; - gridInternalEdges = (gridSideEdges - 1) * gridSideEdges * 2; - - offset = edgeNum - ccgdm->faceMap[i].startEdge; - grid = offset / (gridSideEdges + gridInternalEdges); - offset %= (gridSideEdges + gridInternalEdges); - - if (offset < gridSideEdges) { - x = offset; - med->v1 = getFaceIndex(ss, f, grid, x, 0, edgeSize, gridSize); - med->v2 = getFaceIndex(ss, f, grid, x + 1, 0, edgeSize, gridSize); - } - else { - offset -= gridSideEdges; - x = (offset / 2) / gridSideEdges + 1; - y = (offset / 2) % gridSideEdges; - if (offset % 2 == 0) { - med->v1 = getFaceIndex(ss, f, grid, x, y, edgeSize, gridSize); - med->v2 = getFaceIndex(ss, f, grid, x, y + 1, edgeSize, gridSize); - } - else { - med->v1 = getFaceIndex(ss, f, grid, y, x, edgeSize, gridSize); - med->v2 = getFaceIndex(ss, f, grid, y + 1, x, edgeSize, gridSize); - } - } - } - else { - /* this vert comes from edge data */ - CCGEdge *e; - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int x; - short *edgeFlag; - unsigned int flags = 0; - - i = (edgeNum - ccgdm->edgeMap[0].startEdge) / (edgeSize - 1); - - e = ccgdm->edgeMap[i].edge; - - if (!ccgSubSurf_getEdgeNumFaces(e)) { - flags |= ME_LOOSEEDGE; - } - - x = edgeNum - ccgdm->edgeMap[i].startEdge; - - med->v1 = getEdgeIndex(ss, e, x, edgeSize); - med->v2 = getEdgeIndex(ss, e, x + 1, edgeSize); - - edgeFlag = (ccgdm->edgeFlags) ? &ccgdm->edgeFlags[i] : NULL; - if (edgeFlag) { - flags |= (*edgeFlag & (ME_SEAM | ME_SHARP)) | ME_EDGEDRAW | ME_EDGERENDER; - } - else { - flags |= ME_EDGEDRAW | ME_EDGERENDER; - } - - med->flag = flags; - } -} - -static void ccgDM_getFinalFace(DerivedMesh *dm, int faceNum, MFace *mf) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - CCGSubSurf *ss = ccgdm->ss; - int gridSize = ccgSubSurf_getGridSize(ss); - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int gridSideEdges = gridSize - 1; - int gridFaces = gridSideEdges * gridSideEdges; - int i; - CCGFace *f; - // int numVerts; - int offset; - int grid; - int x, y; - // int lastface = ccgSubSurf_getNumFaces(ss) - 1; /* UNUSED */ - DMFlagMat *faceFlags = ccgdm->faceFlags; - - memset(mf, 0, sizeof(*mf)); - if (faceNum >= ccgdm->dm.numTessFaceData) { - return; - } - - i = ccgdm->reverseFaceMap[faceNum]; - - f = ccgdm->faceMap[i].face; - // numVerts = ccgSubSurf_getFaceNumVerts(f); /* UNUSED */ - - offset = faceNum - ccgdm->faceMap[i].startFace; - grid = offset / gridFaces; - offset %= gridFaces; - y = offset / gridSideEdges; - x = offset % gridSideEdges; - - mf->v1 = getFaceIndex(ss, f, grid, x + 0, y + 0, edgeSize, gridSize); - mf->v2 = getFaceIndex(ss, f, grid, x + 0, y + 1, edgeSize, gridSize); - mf->v3 = getFaceIndex(ss, f, grid, x + 1, y + 1, edgeSize, gridSize); - mf->v4 = getFaceIndex(ss, f, grid, x + 1, y + 0, edgeSize, gridSize); - - if (faceFlags) { - mf->flag = faceFlags[i].flag; - mf->mat_nr = faceFlags[i].mat_nr; - } - else { - mf->flag = ME_SMOOTH; - } - - mf->edcode = 0; -} - /* Translate GridHidden into the ME_HIDE flag for MVerts. Assumes * vertices are in the order output by ccgDM_copyFinalVertArray. */ void subsurf_copy_grid_hidden(DerivedMesh *dm, @@ -1920,10 +1785,6 @@ static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm) ccgdm->dm.getNumPolys = ccgDM_getNumPolys; ccgdm->dm.getNumTessFaces = ccgDM_getNumTessFaces; - ccgdm->dm.getVert = ccgDM_getFinalVert; - ccgdm->dm.getEdge = ccgDM_getFinalEdge; - ccgdm->dm.getTessFace = ccgDM_getFinalFace; - ccgdm->dm.getVertCo = ccgDM_getFinalVertCo; ccgdm->dm.getVertNo = ccgDM_getFinalVertNo; @@ -2032,9 +1893,7 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, gridSideEdges = gridSize - 1; gridInternalEdges = (gridSideEdges - 1) * gridSideEdges * 2; - /* mvert = dm->getVertArray(dm); */ /* UNUSED */ medge = dm->getEdgeArray(dm); - /* mface = dm->getTessFaceArray(dm); */ /* UNUSED */ mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); base_polyOrigIndex = CustomData_get_layer(&dm->polyData, CD_ORIGINDEX); From 7b5835c793fbc5459ad64be4e8c7ceabd45cd668 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Tue, 5 Oct 2021 22:38:41 +0100 Subject: [PATCH 0543/1500] Fix: Add missing function node declaration for RGB and Float Curve nodes --- source/blender/nodes/shader/nodes/node_shader_curves.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index 8657d9e517d..875e6fa0c35 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -174,6 +174,7 @@ namespace blender::nodes { static void sh_node_curve_rgb_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); b.add_input("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_output("Color"); @@ -350,6 +351,7 @@ namespace blender::nodes { static void sh_node_curve_float_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Factor").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); b.add_input("Value").default_value(1.0f); b.add_output("Value"); From 6d2b486e431dae57536a5a7d9b64c62144754363 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 09:28:00 +1100 Subject: [PATCH 0544/1500] Cleanup: spelling in comments --- intern/cycles/bvh/bvh_embree.cpp | 4 ++-- intern/cycles/render/session.cpp | 4 ++-- source/blender/blenkernel/BKE_image.h | 22 +++++++++---------- source/blender/draw/intern/draw_manager.c | 4 ++-- source/blender/draw/intern/draw_manager.h | 2 +- .../blender/draw/intern/draw_texture_pool.cc | 6 ++--- .../editors/sculpt_paint/paint_image_proj.c | 2 +- source/blender/imbuf/intern/anim_movie.c | 9 ++++---- source/blender/windowmanager/WM_types.h | 10 ++++----- .../windowmanager/intern/wm_event_system.c | 2 +- .../windowmanager/xr/intern/wm_xr_intern.h | 10 ++++----- 11 files changed, 38 insertions(+), 37 deletions(-) diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index 20430cb164c..9250af419cb 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -249,7 +249,7 @@ static void rtc_filter_func_thick_curve(const RTCFilterFunctionNArguments *args) const RTCRay *ray = (RTCRay *)args->ray; RTCHit *hit = (RTCHit *)args->hit; - /* Always ignore backfacing intersections. */ + /* Always ignore back-facing intersections. */ if (dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z), make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) { *args->valid = 0; @@ -262,7 +262,7 @@ static void rtc_filter_occluded_func_thick_curve(const RTCFilterFunctionNArgumen const RTCRay *ray = (RTCRay *)args->ray; RTCHit *hit = (RTCHit *)args->hit; - /* Always ignore backfacing intersections. */ + /* Always ignore back-facing intersections. */ if (dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z), make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) { *args->valid = 0; diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 8d2d950f661..a18c61599c2 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -157,7 +157,7 @@ void Session::run_main_render_loop() continue; } - /* Stop rendering if error happenned during scene update or other step of preparing scene + /* Stop rendering if error happened during scene update or other step of preparing scene * for render. */ if (device->have_error()) { progress.set_error(device->error_message()); @@ -179,7 +179,7 @@ void Session::run_main_render_loop() /* update status and timing */ update_status_time(); - /* Stop rendering if error happenned during path tracing. */ + /* Stop rendering if error happened during path tracing. */ if (device->have_error()) { progress.set_error(device->error_message()); break; diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index b62ad3ad24a..09463246e27 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -253,13 +253,13 @@ bool BKE_image_is_stereo(struct Image *ima); struct RenderResult *BKE_image_acquire_renderresult(struct Scene *scene, struct Image *ima); void BKE_image_release_renderresult(struct Scene *scene, struct Image *ima); -/* for multilayer images as well as for singlelayer */ +/* For multi-layer images as well as for single-layer. */ bool BKE_image_is_openexr(struct Image *ima); -/* for multiple slot render, call this before render */ +/* For multiple slot render, call this before render. */ void BKE_image_backup_render(struct Scene *scene, struct Image *ima, bool free_current_slot); -/* for singlelayer openexr saving */ +/* For single-layer OpenEXR saving */ bool BKE_image_save_openexr_multiview(struct Image *ima, struct ImBuf *ibuf, const char *filepath, @@ -285,22 +285,22 @@ void BKE_image_packfiles_from_mem(struct ReportList *reports, char *data, const size_t data_len); -/* prints memory statistics for images */ +/* Prints memory statistics for images. */ void BKE_image_print_memlist(struct Main *bmain); -/* merge source into dest, and free source */ +/* Merge source into dest, and free source. */ void BKE_image_merge(struct Main *bmain, struct Image *dest, struct Image *source); -/* scale the image */ +/* Scale the image. */ bool BKE_image_scale(struct Image *image, int width, int height); -/* check if texture has alpha (depth=32) */ +/* Check if texture has alpha (depth=32). */ bool BKE_image_has_alpha(struct Image *image); -/* check if texture has gpu texture code */ +/* Check if texture has GPU texture code. */ bool BKE_image_has_opengl_texture(struct Image *ima); -/* get tile index for tiled images */ +/* Get tile index for tiled images. */ void BKE_image_get_tile_label(struct Image *ima, struct ImageTile *tile, char *label, @@ -369,10 +369,10 @@ struct ImBuf *BKE_image_get_first_ibuf(struct Image *image); /* Not to be use directly. */ struct GPUTexture *BKE_image_create_gpu_texture_from_ibuf(struct Image *image, struct ImBuf *ibuf); -/* Get the GPUTexture for a given `Image`. +/* Get the #GPUTexture for a given `Image`. * * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already - * available. It is also required when requesting the GPUTexture for a render result. */ + * available. It is also required when requesting the #GPUTexture for a render result. */ struct GPUTexture *BKE_image_get_gpu_texture(struct Image *image, struct ImageUser *iuser, struct ImBuf *ibuf); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 8d8375556c7..b8de92dea7f 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -565,7 +565,7 @@ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int s drw_viewport_data_reset(dst->vmempool); if (size == NULL && viewport == NULL) { - /* Avoid division by 0. Engines will either overide this or not use it. */ + /* Avoid division by 0. Engines will either override this or not use it. */ dst->size[0] = 1.0f; dst->size[1] = 1.0f; } @@ -1335,7 +1335,7 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx) .object_mode = OB_MODE_OBJECT, }; - /* Custom lightweight init to avoid reseting the mempools. */ + /* Custom lightweight initialize to avoid resetting the memory-pools. */ DST.viewport = viewport; DST.vmempool = drw_viewport_data_ensure(DST.viewport); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 1bb1ee06354..162fe9b5fd1 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -512,7 +512,7 @@ typedef struct DRWDebugSphere { typedef struct DRWData { /** Instance data. */ DRWInstanceDataList *idatalist; - /** Mempools for drawcalls. */ + /** Memory-pools for draw-calls. */ struct BLI_memblock *commands; struct BLI_memblock *commands_small; struct BLI_memblock *callbuffers; diff --git a/source/blender/draw/intern/draw_texture_pool.cc b/source/blender/draw/intern/draw_texture_pool.cc index 61697465784..709bf808874 100644 --- a/source/blender/draw/intern/draw_texture_pool.cc +++ b/source/blender/draw/intern/draw_texture_pool.cc @@ -47,7 +47,7 @@ DRWTexturePool *DRW_texture_pool_create(void) void DRW_texture_pool_free(DRWTexturePool *pool) { - /* Reseting the pool twice will effectively free all textures. */ + /* Resetting the pool twice will effectively free all textures. */ DRW_texture_pool_reset(pool); DRW_texture_pool_reset(pool); delete pool; @@ -86,7 +86,7 @@ GPUTexture *DRW_texture_pool_query( if (user_bit & handle.users_bits) { continue; } - /* If everthing matches reuse the texture. */ + /* If everything matches reuse the texture. */ if ((GPU_texture_format(handle.texture) == format) && (GPU_texture_width(handle.texture) == width) && (GPU_texture_height(handle.texture) == height)) { @@ -137,4 +137,4 @@ void DRW_texture_pool_reset(DRWTexturePool *pool) pool->handles.remove_and_reorder(i); } } -} \ No newline at end of file +} diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 0176b4a1b13..89263bec7b1 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -5262,7 +5262,7 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v color_f[3] *= ((float)projPixel->mask) * (1.0f / 65535.0f) * brush_alpha; if (is_floatbuf) { - /* convert to premultipied */ + /* Convert to premutliplied. */ mul_v3_fl(color_f, color_f[3]); IMB_blend_color_float( projPixel->pixel.f_pt, projPixel->origColor.f_pt, color_f, ps->blend); diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 4eb078113dd..c11c6d778a1 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -1496,14 +1496,15 @@ static void free_anim_ffmpeg(struct anim *anim) #endif -/* Try to init the anim struct. - * Returns true on success.*/ - +/** + * Try to initialize the #anim struct. + * Returns true on success. + */ static bool anim_getnew(struct anim *anim) { BLI_assert(anim->curtype == ANIM_NONE); if (anim == NULL) { - /* Nothing to init. */ + /* Nothing to initialize. */ return false; } diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 14a69d9c435..28c7a270554 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -732,16 +732,16 @@ typedef struct wmXrActionData { eXrActionType type; /** State. Set appropriately based on type. */ float state[2]; - /** State of the other subaction path for bimanual actions. */ + /** State of the other sub-action path for bimanual actions. */ float state_other[2]; /** Input threshold for float/vector2f actions. */ float float_threshold; - /** Controller aim pose corresponding to the action's subaction path. */ + /** Controller aim pose corresponding to the action's sub-action path. */ float controller_loc[3]; float controller_rot[4]; - /** Controller aim pose of the other subaction path for bimanual actions. */ + /** Controller aim pose of the other sub-action path for bimanual actions. */ float controller_loc_other[3]; float controller_rot_other[4]; @@ -749,14 +749,14 @@ typedef struct wmXrActionData { struct wmOperatorType *ot; struct IDProperty *op_properties; - /** Whether bimanual interaction is occuring. */ + /** Whether bimanual interaction is occurring. */ bool bimanual; } wmXrActionData; #endif /** Timer flags. */ typedef enum { - /** Do not attempt to free customdata pointer even if non-NULL. */ + /** Do not attempt to free custom-data pointer even if non-NULL. */ WM_TIMER_NO_FREE_CUSTOM_DATA = 1 << 0, } wmTimerFlags; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index c92208c4818..b4abc4f2fde 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3465,7 +3465,7 @@ static void wm_event_free_and_remove_from_queue_if_valid(wmEvent *event) /** * Special handling for XR events. * - * Although XR events are added to regular window queues, they are handled in an "offscreen area" + * Although XR events are added to regular window queues, they are handled in an "off-screen area" * context that is owned entirely by XR runtime data and not tied to a window. */ static void wm_event_handle_xrevent(bContext *C, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index ee495755ac6..8fb5424b8c2 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -37,7 +37,7 @@ typedef struct wmXrSessionState { /** Copy of XrSessionSettings.base_pose_ data to detect changes that need * resetting to base pose. */ - char prev_base_pose_type; /* eXRSessionBasePoseType */ + char prev_base_pose_type; /* #eXRSessionBasePoseType */ Object *prev_base_pose_object; /** Copy of XrSessionSettings.flag created on the last draw call, stored to detect changes. */ int prev_settings_flag; @@ -52,7 +52,7 @@ typedef struct wmXrSessionState { bool is_view_data_set; /** Last known controller data. */ - ListBase controllers; /* wmXrController */ + ListBase controllers; /* #wmXrController */ /** The currently active action set that will be updated on calls to * wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and @@ -67,14 +67,14 @@ typedef struct wmXrRuntimeData { * be an invalid reference, i.e. the window may have been closed. */ wmWindow *session_root_win; - /** Offscreen area used for XR events. */ + /** Off-screen area used for XR events. */ struct ScrArea *area; /** Although this struct is internal, RNA gets a handle to this for state information queries. */ wmXrSessionState session_state; wmXrSessionExitFn exit_fn; - ListBase actionmaps; /* XrActionMap */ + ListBase actionmaps; /* #XrActionMap */ short actactionmap; short selactionmap; } wmXrRuntimeData; @@ -87,7 +87,7 @@ typedef struct wmXrViewportPair { typedef struct { /** Off-screen buffers/viewports for each view. */ - ListBase viewports; /* wmXrViewportPair */ + ListBase viewports; /* #wmXrViewportPair */ } wmXrSurfaceData; typedef struct wmXrDrawData { From 26dac33ce18f8a5655883b759d271da4a9b94982 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 09:29:09 +1100 Subject: [PATCH 0545/1500] Cleanup: simplify ED_imbuf_sample_poll Access the space data directly from the area. Also remove redundant NULL check. --- source/blender/editors/util/ed_util_imbuf.c | 55 +++++++++++---------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index fcbc0807893..d57640e16dc 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -535,37 +535,38 @@ void ED_imbuf_sample_cancel(bContext *C, wmOperator *op) bool ED_imbuf_sample_poll(bContext *C) { - ScrArea *sa = CTX_wm_area(C); - - if (sa && sa->spacetype == SPACE_IMAGE) { - SpaceImage *sima = CTX_wm_space_image(C); - if (sima == NULL) { - return false; - } - - Object *obedit = CTX_data_edit_object(C); - if (obedit) { - /* Disable when UV editing so it doesn't swallow all click events - * (use for setting cursor). */ - if (ED_space_image_show_uvedit(sima, obedit)) { - return false; - } - } - else if (sima->mode != SI_MODE_VIEW) { - return false; - } - - return true; + ScrArea *area = CTX_wm_area(C); + if (area == NULL) { + return false; } - if (sa && sa->spacetype == SPACE_SEQ) { - SpaceSeq *sseq = CTX_wm_space_seq(C); - - if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { - return false; + switch (area->spacetype) { + case SPACE_IMAGE: { + SpaceImage *sima = area->spacedata.first; + Object *obedit = CTX_data_edit_object(C); + if (obedit) { + /* Disable when UV editing so it doesn't swallow all click events + * (use for setting cursor). */ + if (ED_space_image_show_uvedit(sima, obedit)) { + return false; + } + } + else if (sima->mode != SI_MODE_VIEW) { + return false; + } + return true; } + case SPACE_SEQ: { + SpaceSeq *sseq = area->spacedata.first; - return sseq && SEQ_editing_get(CTX_data_scene(C)) != NULL; + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return false; + } + if (SEQ_editing_get(CTX_data_scene(C)) == NULL) { + return false; + } + return true; + } } return false; From fd592538d983bec51e331f1d073c39582d43520f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 10:44:11 +1100 Subject: [PATCH 0546/1500] Cleanup: move BLI_vfontdata.h to BKE_vfontdata.h This didn't belong on blenlib since it uses DNA data types and included a bad-level call to BKE_curve.h. It also meant linking in blenlib would depend on the freetype library, noticeable for thumbnail extraction (see D6408). --- .../BKE_vfontdata.h} | 10 +++++----- source/blender/blenkernel/CMakeLists.txt | 8 ++++++++ source/blender/blenkernel/intern/font.c | 12 ++++++------ .../intern/vfontdata_freetype.c} | 19 ++++++++----------- source/blender/blenlib/CMakeLists.txt | 4 ---- 5 files changed, 27 insertions(+), 26 deletions(-) rename source/blender/{blenlib/BLI_vfontdata.h => blenkernel/BKE_vfontdata.h} (81%) rename source/blender/{blenlib/intern/freetypefont.c => blenkernel/intern/vfontdata_freetype.c} (96%) diff --git a/source/blender/blenlib/BLI_vfontdata.h b/source/blender/blenkernel/BKE_vfontdata.h similarity index 81% rename from source/blender/blenlib/BLI_vfontdata.h rename to source/blender/blenkernel/BKE_vfontdata.h index 0bb32ca24b7..b162958f69d 100644 --- a/source/blender/blenlib/BLI_vfontdata.h +++ b/source/blender/blenkernel/BKE_vfontdata.h @@ -20,7 +20,7 @@ #pragma once /** \file - * \ingroup bli + * \ingroup bke * \brief A structure to represent vector fonts, * and to load them from PostScript fonts. */ @@ -49,11 +49,11 @@ typedef struct VChar { float width; } VChar; -VFontData *BLI_vfontdata_from_freetypefont(struct PackedFile *pf); -VFontData *BLI_vfontdata_copy(const VFontData *vfont_src, const int flag); +VFontData *BKE_vfontdata_from_freetypefont(struct PackedFile *pf); +VFontData *BKE_vfontdata_copy(const VFontData *vfont_src, const int flag); -VChar *BLI_vfontchar_from_freetypefont(struct VFont *vfont, unsigned long character); -VChar *BLI_vfontchar_copy(const VChar *vchar_src, const int flag); +VChar *BKE_vfontdata_char_from_freetypefont(struct VFont *vfont, unsigned long character); +VChar *BKE_vfontdata_char_copy(const VChar *vchar_src, const int flag); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 37581ad5c00..e727730770c 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -60,6 +60,9 @@ set(INC set(INC_SYS ${ZLIB_INCLUDE_DIRS} + + # For `vfontdata_freetype.c`. + ${FREETYPE_INCLUDE_DIRS} ) set(SRC @@ -286,6 +289,7 @@ set(SRC intern/tracking_util.c intern/undo_system.c intern/unit.c + intern/vfontdata_freetype.c intern/volume.cc intern/volume_render.cc intern/volume_to_mesh.cc @@ -453,6 +457,7 @@ set(SRC BKE_tracking.h BKE_undo_system.h BKE_unit.h + BKE_vfontdata.h BKE_volume.h BKE_volume_render.h BKE_volume_to_mesh.hh @@ -502,6 +507,9 @@ set(LIB bf_rna bf_shader_fx bf_simulation + + # For `vfontdata_freetype.c`. + ${FREETYPE_LIBRARY} ) if(WITH_BINRELOC) diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 0e159418724..d749237971a 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -40,7 +40,6 @@ #include "BLI_string_utf8.h" #include "BLI_threads.h" #include "BLI_utildefines.h" -#include "BLI_vfontdata.h" #include "BLT_translation.h" @@ -57,6 +56,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_packedFile.h" +#include "BKE_vfontdata.h" #include "BLO_read_write.h" @@ -77,7 +77,7 @@ static void vfont_init_data(ID *id) if (pf) { VFontData *vfd; - vfd = BLI_vfontdata_from_freetypefont(pf); + vfd = BKE_vfontdata_from_freetypefont(pf); if (vfd) { vfont->data = vfd; @@ -107,7 +107,7 @@ static void vfont_copy_data(Main *UNUSED(bmain), } if (vfont_dst->data) { - vfont_dst->data = BLI_vfontdata_copy(vfont_dst->data, flag_subdata); + vfont_dst->data = BKE_vfontdata_copy(vfont_dst->data, flag_subdata); } } @@ -300,7 +300,7 @@ static VFontData *vfont_get_data(VFont *vfont) } if (pf) { - vfont->data = BLI_vfontdata_from_freetypefont(pf); + vfont->data = BKE_vfontdata_from_freetypefont(pf); if (pf != vfont->packedfile) { BKE_packedfile_free(pf); } @@ -335,7 +335,7 @@ VFont *BKE_vfont_load(Main *bmain, const char *filepath) if (pf) { VFontData *vfd; - vfd = BLI_vfontdata_from_freetypefont(pf); + vfd = BKE_vfontdata_from_freetypefont(pf); if (vfd) { /* If there's a font name, use it for the ID name. */ vfont = BKE_libblock_alloc(bmain, ID_VF, vfd->name[0] ? vfd->name : filename, 0); @@ -954,7 +954,7 @@ static bool vfont_to_curve(Object *ob, * happen often once all the chars are load. */ if ((che = find_vfont_char(vfd, ascii)) == NULL) { - che = BLI_vfontchar_from_freetypefont(vfont, ascii); + che = BKE_vfontdata_char_from_freetypefont(vfont, ascii); } BLI_rw_mutex_unlock(&vfont_rwlock); } diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenkernel/intern/vfontdata_freetype.c similarity index 96% rename from source/blender/blenlib/intern/freetypefont.c rename to source/blender/blenkernel/intern/vfontdata_freetype.c index 34de8fe7f6d..bba6768017d 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenkernel/intern/vfontdata_freetype.c @@ -42,7 +42,9 @@ #include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_utildefines.h" -#include "BLI_vfontdata.h" + +#include "BKE_curve.h" +#include "BKE_vfontdata.h" #include "DNA_curve_types.h" #include "DNA_packedFile_types.h" @@ -402,7 +404,7 @@ static bool check_freetypefont(PackedFile *pf) * \retval A new VFontData structure, or NULL * if unable to load. */ -VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf) +VFontData *BKE_vfontdata_from_freetypefont(PackedFile *pf) { VFontData *vfd = NULL; @@ -425,10 +427,10 @@ VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf) static void *vfontdata_copy_characters_value_cb(const void *src) { - return BLI_vfontchar_copy(src, 0); + return BKE_vfontdata_char_copy(src, 0); } -VFontData *BLI_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag)) +VFontData *BKE_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag)) { VFontData *vfont_dst = MEM_dupallocN(vfont_src); @@ -440,7 +442,7 @@ VFontData *BLI_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag) return vfont_dst; } -VChar *BLI_vfontchar_from_freetypefont(VFont *vfont, unsigned long character) +VChar *BKE_vfontdata_char_from_freetypefont(VFont *vfont, unsigned long character) { VChar *che = NULL; @@ -464,12 +466,7 @@ VChar *BLI_vfontchar_from_freetypefont(VFont *vfont, unsigned long character) return che; } -/* Yeah, this is very bad... But why is this in BLI in the first place, since it uses Nurb data? - * Anyway, do not feel like duplicating whole Nurb copy code here, - * so unless someone has a better idea... */ -#include "../../blenkernel/BKE_curve.h" - -VChar *BLI_vfontchar_copy(const VChar *vchar_src, const int UNUSED(flag)) +VChar *BKE_vfontdata_char_copy(const VChar *vchar_src, const int UNUSED(flag)) { VChar *vchar_dst = MEM_dupallocN(vchar_src); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index c886732365e..c01052f0111 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -32,7 +32,6 @@ set(INC set(INC_SYS ${ZLIB_INCLUDE_DIRS} ${ZSTD_INCLUDE_DIRS} - ${FREETYPE_INCLUDE_DIRS} ${GMP_INCLUDE_DIRS} ) @@ -81,7 +80,6 @@ set(SRC intern/filereader_memory.c intern/filereader_zstd.c intern/fnmatch.c - intern/freetypefont.c intern/gsqueue.c intern/hash_md5.c intern/hash_mm2a.c @@ -318,7 +316,6 @@ set(SRC BLI_vector_adaptor.hh BLI_vector_set.hh BLI_vector_set_slots.hh - BLI_vfontdata.h BLI_virtual_array.hh BLI_virtual_vector_array.hh BLI_voronoi_2d.h @@ -334,7 +331,6 @@ set(LIB bf_intern_numaapi extern_wcwidth - ${FREETYPE_LIBRARY} ${ZLIB_LIBRARIES} ${ZSTD_LIBRARIES} ) From dcac86f4f11fb4e464a8f2dc3f2ef69eff4f4f21 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 10:54:30 +1100 Subject: [PATCH 0547/1500] Cleanup: remove unused flag argument --- source/blender/blenkernel/BKE_vfontdata.h | 2 +- source/blender/blenkernel/intern/vfontdata_freetype.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_vfontdata.h b/source/blender/blenkernel/BKE_vfontdata.h index b162958f69d..b6e57dad934 100644 --- a/source/blender/blenkernel/BKE_vfontdata.h +++ b/source/blender/blenkernel/BKE_vfontdata.h @@ -53,7 +53,7 @@ VFontData *BKE_vfontdata_from_freetypefont(struct PackedFile *pf); VFontData *BKE_vfontdata_copy(const VFontData *vfont_src, const int flag); VChar *BKE_vfontdata_char_from_freetypefont(struct VFont *vfont, unsigned long character); -VChar *BKE_vfontdata_char_copy(const VChar *vchar_src, const int flag); +VChar *BKE_vfontdata_char_copy(const VChar *vchar_src); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/intern/vfontdata_freetype.c b/source/blender/blenkernel/intern/vfontdata_freetype.c index bba6768017d..db9fdef75c0 100644 --- a/source/blender/blenkernel/intern/vfontdata_freetype.c +++ b/source/blender/blenkernel/intern/vfontdata_freetype.c @@ -427,7 +427,7 @@ VFontData *BKE_vfontdata_from_freetypefont(PackedFile *pf) static void *vfontdata_copy_characters_value_cb(const void *src) { - return BKE_vfontdata_char_copy(src, 0); + return BKE_vfontdata_char_copy(src); } VFontData *BKE_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag)) @@ -466,7 +466,7 @@ VChar *BKE_vfontdata_char_from_freetypefont(VFont *vfont, unsigned long characte return che; } -VChar *BKE_vfontdata_char_copy(const VChar *vchar_src, const int UNUSED(flag)) +VChar *BKE_vfontdata_char_copy(const VChar *vchar_src) { VChar *vchar_dst = MEM_dupallocN(vchar_src); From b93e947306517a5b6ab879aab35c8fdccff8887e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 11:15:53 +1100 Subject: [PATCH 0548/1500] Cleanup: rename BKE_font.h -> BKE_vfont.h Match API naming prefix (BKE_vfont_*) and DNA_vfont_types.h. --- source/blender/blenkernel/{BKE_font.h => BKE_vfont.h} | 0 source/blender/blenkernel/CMakeLists.txt | 4 ++-- source/blender/blenkernel/intern/bpath.c | 2 +- source/blender/blenkernel/intern/curve.c | 2 +- source/blender/blenkernel/intern/curve_convert.c | 2 +- source/blender/blenkernel/intern/displist.cc | 2 +- source/blender/blenkernel/intern/lib_override.c | 2 +- source/blender/blenkernel/intern/material.c | 2 +- source/blender/blenkernel/intern/object.c | 2 +- source/blender/blenkernel/intern/object_dupli.cc | 2 +- source/blender/blenkernel/intern/packedFile.c | 2 +- source/blender/blenkernel/intern/{font.c => vfont.c} | 2 +- source/blender/blenlib/BLI_string_utf8.h | 1 - source/blender/draw/engines/overlay/overlay_edit_text.c | 2 +- source/blender/draw/intern/draw_cache_impl_curve.cc | 2 +- source/blender/draw/intern/draw_manager_profiling.c | 3 +-- source/blender/editors/curve/editfont.c | 2 +- source/blender/editors/curve/editfont_undo.c | 2 +- source/blender/editors/object/object_add.c | 2 +- source/blender/editors/render/render_shading.c | 2 +- source/blender/editors/space_view3d/view3d_edit.c | 2 +- source/blender/makesrna/intern/rna_ID.c | 2 +- source/blender/makesrna/intern/rna_curve.c | 2 +- source/blender/makesrna/intern/rna_main_api.c | 2 +- source/blender/makesrna/intern/rna_object_api.c | 2 +- source/blender/makesrna/intern/rna_vfont.c | 2 +- .../blender/nodes/geometry/nodes/node_geo_string_to_curves.cc | 2 +- source/blender/windowmanager/intern/wm_init_exit.c | 2 +- source/creator/creator.c | 2 +- 29 files changed, 28 insertions(+), 30 deletions(-) rename source/blender/blenkernel/{BKE_font.h => BKE_vfont.h} (100%) rename source/blender/blenkernel/intern/{font.c => vfont.c} (99%) diff --git a/source/blender/blenkernel/BKE_font.h b/source/blender/blenkernel/BKE_vfont.h similarity index 100% rename from source/blender/blenkernel/BKE_font.h rename to source/blender/blenkernel/BKE_vfont.h diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index e727730770c..47c1d698360 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -143,7 +143,6 @@ set(SRC intern/fcurve_driver.c intern/fluid.c intern/fmodifier.c - intern/font.c intern/freestyle.c intern/geometry_component_curve.cc intern/geometry_component_instances.cc @@ -289,6 +288,7 @@ set(SRC intern/tracking_util.c intern/undo_system.c intern/unit.c + intern/vfont.c intern/vfontdata_freetype.c intern/volume.cc intern/volume_render.cc @@ -364,7 +364,6 @@ set(SRC BKE_fcurve.h BKE_fcurve_driver.h BKE_fluid.h - BKE_font.h BKE_freestyle.h BKE_geometry_set.h BKE_geometry_set.hh @@ -457,6 +456,7 @@ set(SRC BKE_tracking.h BKE_undo_system.h BKE_unit.h + BKE_vfont.h BKE_vfontdata.h BKE_volume.h BKE_volume_render.h diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 371ec14876b..07ac0086f0d 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -66,13 +66,13 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" -#include "BKE_font.h" #include "BKE_image.h" #include "BKE_lib_id.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_report.h" +#include "BKE_vfont.h" #include "BKE_bpath.h" /* own include */ diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 0dcfea78ca5..b0fc1426093 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -52,13 +52,13 @@ #include "BKE_curve.h" #include "BKE_curveprofile.h" #include "BKE_displist.h" -#include "BKE_font.h" #include "BKE_idtype.h" #include "BKE_key.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_object.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/blenkernel/intern/curve_convert.c b/source/blender/blenkernel/intern/curve_convert.c index 5bcce9c339e..98a9cbc2bcf 100644 --- a/source/blender/blenkernel/intern/curve_convert.c +++ b/source/blender/blenkernel/intern/curve_convert.c @@ -26,9 +26,9 @@ #include "BKE_curve.h" #include "BKE_displist.h" -#include "BKE_font.h" #include "BKE_lib_id.h" #include "BKE_modifier.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 0776f3b9a68..ebe00531e65 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -47,7 +47,6 @@ #include "BKE_anim_path.h" #include "BKE_curve.h" #include "BKE_displist.h" -#include "BKE_font.h" #include "BKE_geometry_set.hh" #include "BKE_key.h" #include "BKE_lattice.h" @@ -58,6 +57,7 @@ #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_spline.hh" +#include "BKE_vfont.h" #include "BLI_sys_types.h" /* For #intptr_t support. */ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 59e431b737c..5117c8bd64c 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1918,7 +1918,7 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) */ void BKE_lib_override_library_make_local(ID *id) { - if (!ID_IS_OVERRIDE_LIBRARY(id)) { + if (!ID_IS_OVERRIDE_LIBRARY(id)) { return; } if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id)) { diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index fa3fbd457d1..6c57d3139bb 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -63,7 +63,6 @@ #include "BKE_curve.h" #include "BKE_displist.h" #include "BKE_editmesh.h" -#include "BKE_font.h" #include "BKE_gpencil.h" #include "BKE_icons.h" #include "BKE_idtype.h" @@ -76,6 +75,7 @@ #include "BKE_node.h" #include "BKE_object.h" #include "BKE_scene.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index ec39c5b45c4..274f0394618 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -94,7 +94,6 @@ #include "BKE_effect.h" #include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" -#include "BKE_font.h" #include "BKE_geometry_set.h" #include "BKE_global.h" #include "BKE_gpencil.h" @@ -136,6 +135,7 @@ #include "BKE_speaker.h" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" +#include "BKE_vfont.h" #include "BKE_volume.h" #include "DEG_depsgraph.h" diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 04739ec19d3..58b19805407 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -50,7 +50,6 @@ #include "BKE_duplilist.h" #include "BKE_editmesh.h" #include "BKE_editmesh_cache.h" -#include "BKE_font.h" #include "BKE_geometry_set.h" #include "BKE_geometry_set.hh" #include "BKE_global.h" @@ -63,6 +62,7 @@ #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_scene.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index baff1bb47cc..95763b0da54 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -43,12 +43,12 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" -#include "BKE_font.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_packedFile.h" #include "BKE_report.h" #include "BKE_sound.h" +#include "BKE_vfont.h" #include "BKE_volume.h" #include "IMB_imbuf.h" diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/vfont.c similarity index 99% rename from source/blender/blenkernel/intern/font.c rename to source/blender/blenkernel/intern/vfont.c index d749237971a..e650ae696b5 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -50,7 +50,7 @@ #include "BKE_anim_path.h" #include "BKE_curve.h" -#include "BKE_font.h" +#include "BKE_vfont.h" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h index 3b7463affc0..bf7547cc90b 100644 --- a/source/blender/blenlib/BLI_string_utf8.h +++ b/source/blender/blenlib/BLI_string_utf8.h @@ -64,7 +64,6 @@ const char *BLI_str_find_prev_char_utf8(const char *p, const char *str_start) const char *BLI_str_find_next_char_utf8(const char *p, const char *str_end) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1, 2); -/* wchar_t functions, copied from blenders own font.c originally */ size_t BLI_wstrlen_utf8(const wchar_t *src) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; size_t BLI_strlen_utf8_ex(const char *strc, size_t *r_len_bytes) ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/draw/engines/overlay/overlay_edit_text.c b/source/blender/draw/engines/overlay/overlay_edit_text.c index 5356700f156..b899767acc4 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_text.c +++ b/source/blender/draw/engines/overlay/overlay_edit_text.c @@ -22,7 +22,7 @@ #include "DRW_render.h" -#include "BKE_font.h" +#include "BKE_vfont.h" #include "DNA_curve_types.h" diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index dc8f382b7f8..1108d40125b 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -36,9 +36,9 @@ #include "BKE_curve.h" #include "BKE_displist.h" -#include "BKE_font.h" #include "BKE_geometry_set.hh" #include "BKE_spline.hh" +#include "BKE_vfont.h" #include "GPU_batch.h" #include "GPU_capabilities.h" diff --git a/source/blender/draw/intern/draw_manager_profiling.c b/source/blender/draw/intern/draw_manager_profiling.c index 70fe12270f5..87d4c34b3ed 100644 --- a/source/blender/draw/intern/draw_manager_profiling.c +++ b/source/blender/draw/intern/draw_manager_profiling.c @@ -257,8 +257,7 @@ void DRW_stats_draw(const rcti *rect) /* Engines rows */ char time_to_txt[16]; - DRW_ENABLED_ENGINE_ITER(DST.view_data_active, engine, data) - { + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { u = 0; draw_stat_5row(rect, u++, v, engine->idname, sizeof(engine->idname)); diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index d029bb539ba..1b44cf88db1 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -42,11 +42,11 @@ #include "BKE_context.h" #include "BKE_curve.h" -#include "BKE_font.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_object.h" #include "BKE_report.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/editors/curve/editfont_undo.c b/source/blender/editors/curve/editfont_undo.c index 6eaf8971eb0..21a6564edf4 100644 --- a/source/blender/editors/curve/editfont_undo.c +++ b/source/blender/editors/curve/editfont_undo.c @@ -33,9 +33,9 @@ #include "DNA_scene_types.h" #include "BKE_context.h" -#include "BKE_font.h" #include "BKE_main.h" #include "BKE_undo_system.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index cc4f2acc346..4b2315c0552 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -65,7 +65,6 @@ #include "BKE_displist.h" #include "BKE_duplilist.h" #include "BKE_effect.h" -#include "BKE_font.h" #include "BKE_geometry_set.h" #include "BKE_gpencil_curve.h" #include "BKE_gpencil_geom.h" @@ -92,6 +91,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_speaker.h" +#include "BKE_vfont.h" #include "BKE_volume.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 7b2667905ff..b6e6869f4e2 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -48,7 +48,6 @@ #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_editmesh.h" -#include "BKE_font.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_layer.h" @@ -61,6 +60,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_texture.h" +#include "BKE_vfont.h" #include "BKE_workspace.h" #include "BKE_world.h" diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 15ccf5891d4..6f51d740e55 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -47,7 +47,6 @@ #include "BKE_armature.h" #include "BKE_camera.h" #include "BKE_context.h" -#include "BKE_font.h" #include "BKE_gpencil_geom.h" #include "BKE_layer.h" #include "BKE_lib_id.h" @@ -57,6 +56,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 1113ac0eb45..1776da7e62d 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -225,7 +225,6 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { # include "BLI_math_base.h" # include "BKE_anim_data.h" -# include "BKE_font.h" # include "BKE_global.h" /* XXX, remove me */ # include "BKE_idprop.h" # include "BKE_idtype.h" @@ -234,6 +233,7 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { # include "BKE_lib_remap.h" # include "BKE_library.h" # include "BKE_material.h" +# include "BKE_vfont.h" # include "DEG_depsgraph.h" # include "DEG_depsgraph_build.h" diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 0bfb1200f49..8c89162f571 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -30,7 +30,7 @@ #include "BLT_translation.h" -#include "BKE_font.h" +#include "BKE_vfont.h" #include "RNA_access.h" #include "RNA_define.h" diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 9a33849b645..0276c8a3f8a 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -47,7 +47,6 @@ # include "BKE_collection.h" # include "BKE_curve.h" # include "BKE_displist.h" -# include "BKE_font.h" # include "BKE_gpencil.h" # include "BKE_hair.h" # include "BKE_icons.h" @@ -74,6 +73,7 @@ # include "BKE_speaker.h" # include "BKE_text.h" # include "BKE_texture.h" +# include "BKE_vfont.h" # include "BKE_volume.h" # include "BKE_workspace.h" # include "BKE_world.h" diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 10ba2b9acb1..10094ade711 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -69,7 +69,6 @@ static const EnumPropertyItem space_items[] = { # include "BKE_constraint.h" # include "BKE_context.h" # include "BKE_customdata.h" -# include "BKE_font.h" # include "BKE_global.h" # include "BKE_layer.h" # include "BKE_main.h" @@ -78,6 +77,7 @@ static const EnumPropertyItem space_items[] = { # include "BKE_modifier.h" # include "BKE_object.h" # include "BKE_report.h" +# include "BKE_vfont.h" # include "ED_object.h" # include "ED_screen.h" diff --git a/source/blender/makesrna/intern/rna_vfont.c b/source/blender/makesrna/intern/rna_vfont.c index 214a32372dd..f5f7742d753 100644 --- a/source/blender/makesrna/intern/rna_vfont.c +++ b/source/blender/makesrna/intern/rna_vfont.c @@ -30,7 +30,7 @@ #ifdef RNA_RUNTIME -# include "BKE_font.h" +# include "BKE_vfont.h" # include "DNA_object_types.h" # include "DEG_depsgraph.h" diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 5e2f03806c3..3eec2279f24 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -18,8 +18,8 @@ #include "DNA_vfont_types.h" #include "BKE_curve.h" -#include "BKE_font.h" #include "BKE_spline.hh" +#include "BKE_vfont.h" #include "BLI_hash.h" #include "BLI_string_utf8.h" diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index ad04416613a..d4ef14bbf5d 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -56,7 +56,6 @@ #include "BKE_blendfile.h" #include "BKE_callbacks.h" #include "BKE_context.h" -#include "BKE_font.h" #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_image.h" @@ -69,6 +68,7 @@ #include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_sound.h" +#include "BKE_vfont.h" #include "BKE_addon.h" #include "BKE_appdir.h" diff --git a/source/creator/creator.c b/source/creator/creator.c index 2ec4a2aa616..7c99f954bfc 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -54,7 +54,6 @@ #include "BKE_cachefile.h" #include "BKE_callbacks.h" #include "BKE_context.h" -#include "BKE_font.h" #include "BKE_global.h" #include "BKE_gpencil_modifier.h" #include "BKE_idtype.h" @@ -65,6 +64,7 @@ #include "BKE_particle.h" #include "BKE_shader_fx.h" #include "BKE_sound.h" +#include "BKE_vfont.h" #include "BKE_volume.h" #include "DEG_depsgraph.h" From c148eba16fbe1e37b2e0cfb4ef1b60bf43523a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Wed, 6 Oct 2021 02:32:54 +0200 Subject: [PATCH 0549/1500] Fix crash when reading non standard Alembic velocity attribute type Some software may export velocity as a different type than 3D vectors (e.g. as colors or flat arrays or floats), so we need to explicitely check for this. A more robust attribute handling system allowing us to cope with other software idiosyncrasies is on the way, so this fix will do for now. --- source/blender/io/alembic/intern/abc_reader_mesh.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index eab94139f55..adf1a3e241c 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -435,6 +435,13 @@ static V3fArraySamplePtr get_velocity_prop(const ICompoundProperty &schema, const ICompoundProperty &prop = ICompoundProperty(schema, header.getName()); if (has_property(prop, name)) { + /* Header cannot be null here, as its presence is checked via has_property, so it is safe + * to dereference. */ + const PropertyHeader *header = prop.getPropertyHeader(name); + if (!IV3fArrayProperty::matches(*header)) { + continue; + } + const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0); if (velocity_prop) { return velocity_prop.getValue(selector); @@ -442,7 +449,7 @@ static V3fArraySamplePtr get_velocity_prop(const ICompoundProperty &schema, } } else if (header.isArray()) { - if (header.getName() == name) { + if (header.getName() == name && IV3fArrayProperty::matches(header)) { const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0); return velocity_prop.getValue(selector); } From 11d31addf89680eb5adad9ce7268e234dc25f8c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Wed, 6 Oct 2021 02:41:34 +0200 Subject: [PATCH 0550/1500] Cleanup: missing verb in comment --- source/blender/io/alembic/intern/alembic_capi.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index 63565b902f3..d7b176eea50 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -460,7 +460,7 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa /* Decrement the ID ref-count because it is going to be incremented for each * modifier and constraint that it will be attached to, so since currently - * it is not used by anyone, its use count will off by one. */ + * it is not used by anyone, its use count will be off by one. */ id_us_min(&cache_file->id); cache_file->is_sequence = data->settings.is_sequence; From 76de3ac4ce436dac4e3354fb7edbb67281ca9b54 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Tue, 5 Oct 2021 19:09:01 -0700 Subject: [PATCH 0551/1500] Cleanup: Remove data duplication from various lookup tables in Cycles This effectively undoes some of the following commit: rB4537e8558468c71a03bf53f59c60f888b3412de2 The tables in question were duplicated 5-6 times into the blender executable due to the headers being used in multiple translation units. This contributes ~6.3kb worth of duplicate data into the binary. Some further details are in the below revision. Differential Revision: https://developer.blender.org/D12724 --- intern/cycles/kernel/svm/svm_math_util.h | 66 ++++++++++----------- intern/cycles/kernel/svm/svm_wavelength.h | 70 +++++++++++------------ 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/svm_math_util.h index 9e654f2247f..11b1e8f57f8 100644 --- a/intern/cycles/kernel/svm/svm_math_util.h +++ b/intern/cycles/kernel/svm/svm_math_util.h @@ -200,43 +200,43 @@ ccl_device float svm_math(NodeMathType type, float a, float b, float c) } } -/* Calculate color in range 800..12000 using an approximation - * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B - * Max absolute error for RGB is (0.00095, 0.00077, 0.00057), - * which is enough to get the same 8 bit/channel color. - */ - -ccl_static_constant float blackbody_table_r[6][3] = { - {2.52432244e+03f, -1.06185848e-03f, 3.11067539e+00f}, - {3.37763626e+03f, -4.34581697e-04f, 1.64843306e+00f}, - {4.10671449e+03f, -8.61949938e-05f, 6.41423749e-01f}, - {4.66849800e+03f, 2.85655028e-05f, 1.29075375e-01f}, - {4.60124770e+03f, 2.89727618e-05f, 1.48001316e-01f}, - {3.78765709e+03f, 9.36026367e-06f, 3.98995841e-01f}, -}; - -ccl_static_constant float blackbody_table_g[6][3] = { - {-7.50343014e+02f, 3.15679613e-04f, 4.73464526e-01f}, - {-1.00402363e+03f, 1.29189794e-04f, 9.08181524e-01f}, - {-1.22075471e+03f, 2.56245413e-05f, 1.20753416e+00f}, - {-1.42546105e+03f, -4.01730887e-05f, 1.44002695e+00f}, - {-1.18134453e+03f, -2.18913373e-05f, 1.30656109e+00f}, - {-5.00279505e+02f, -4.59745390e-06f, 1.09090465e+00f}, -}; - -ccl_static_constant float blackbody_table_b[6][4] = { - {0.0f, 0.0f, 0.0f, 0.0f}, /* zeros should be optimized by compiler */ - {0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, - {-2.02524603e-11f, 1.79435860e-07f, -2.60561875e-04f, -1.41761141e-02f}, - {-2.22463426e-13f, -1.55078698e-08f, 3.81675160e-04f, -7.30646033e-01f}, - {6.72595954e-13f, -2.73059993e-08f, 4.24068546e-04f, -7.52204323e-01f}, -}; - ccl_device float3 svm_math_blackbody_color(float t) { /* TODO(lukas): Reimplement in XYZ. */ + /* Calculate color in range 800..12000 using an approximation + * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B + * Max absolute error for RGB is (0.00095, 0.00077, 0.00057), + * which is enough to get the same 8 bit/channel color. + */ + + const float blackbody_table_r[6][3] = { + {2.52432244e+03f, -1.06185848e-03f, 3.11067539e+00f}, + {3.37763626e+03f, -4.34581697e-04f, 1.64843306e+00f}, + {4.10671449e+03f, -8.61949938e-05f, 6.41423749e-01f}, + {4.66849800e+03f, 2.85655028e-05f, 1.29075375e-01f}, + {4.60124770e+03f, 2.89727618e-05f, 1.48001316e-01f}, + {3.78765709e+03f, 9.36026367e-06f, 3.98995841e-01f}, + }; + + const float blackbody_table_g[6][3] = { + {-7.50343014e+02f, 3.15679613e-04f, 4.73464526e-01f}, + {-1.00402363e+03f, 1.29189794e-04f, 9.08181524e-01f}, + {-1.22075471e+03f, 2.56245413e-05f, 1.20753416e+00f}, + {-1.42546105e+03f, -4.01730887e-05f, 1.44002695e+00f}, + {-1.18134453e+03f, -2.18913373e-05f, 1.30656109e+00f}, + {-5.00279505e+02f, -4.59745390e-06f, 1.09090465e+00f}, + }; + + const float blackbody_table_b[6][4] = { + {0.0f, 0.0f, 0.0f, 0.0f}, /* zeros should be optimized by compiler */ + {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, + {-2.02524603e-11f, 1.79435860e-07f, -2.60561875e-04f, -1.41761141e-02f}, + {-2.22463426e-13f, -1.55078698e-08f, 3.81675160e-04f, -7.30646033e-01f}, + {6.72595954e-13f, -2.73059993e-08f, 4.24068546e-04f, -7.52204323e-01f}, + }; + if (t >= 12000.0f) { return make_float3(0.826270103f, 0.994478524f, 1.56626022f); } diff --git a/intern/cycles/kernel/svm/svm_wavelength.h b/intern/cycles/kernel/svm/svm_wavelength.h index fba8aa63d31..aa291fd2741 100644 --- a/intern/cycles/kernel/svm/svm_wavelength.h +++ b/intern/cycles/kernel/svm/svm_wavelength.h @@ -34,44 +34,44 @@ CCL_NAMESPACE_BEGIN /* Wavelength to RGB */ -// CIE colour matching functions xBar, yBar, and zBar for -// wavelengths from 380 through 780 nanometers, every 5 -// nanometers. For a wavelength lambda in this range: -// cie_colour_match[(lambda - 380) / 5][0] = xBar -// cie_colour_match[(lambda - 380) / 5][1] = yBar -// cie_colour_match[(lambda - 380) / 5][2] = zBar -ccl_static_constant float cie_colour_match[81][3] = { - {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f}, - {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f}, - {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f}, - {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f}, - {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f}, - {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f}, - {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f}, - {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f}, - {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f}, - {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f}, - {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f}, - {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f}, - {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f}, - {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f}, - {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f}, - {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f}, - {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f}, - {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f}, - {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f}, - {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f}, - {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f}, - {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f}, - {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f}, - {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f}, - {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f}, - {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, - {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}}; - ccl_device_noinline void svm_node_wavelength( const KernelGlobals *kg, ShaderData *sd, float *stack, uint wavelength, uint color_out) { + // CIE colour matching functions xBar, yBar, and zBar for + // wavelengths from 380 through 780 nanometers, every 5 + // nanometers. For a wavelength lambda in this range: + // cie_colour_match[(lambda - 380) / 5][0] = xBar + // cie_colour_match[(lambda - 380) / 5][1] = yBar + // cie_colour_match[(lambda - 380) / 5][2] = zBar + const float cie_colour_match[81][3] = { + {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f}, + {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f}, + {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f}, + {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f}, + {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f}, + {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f}, + {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f}, + {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f}, + {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f}, + {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f}, + {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f}, + {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f}, + {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f}, + {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f}, + {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f}, + {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f}, + {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f}, + {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f}, + {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f}, + {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f}, + {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f}, + {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f}, + {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f}, + {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f}, + {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f}, + {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, + {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}}; + float lambda_nm = stack_load_float(stack, wavelength); float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); // scaled 0..80 int i = float_to_int(ii); From 8113b8391ae0f4d1f1612fb446f2268d093b5240 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 6 Oct 2021 04:16:50 +0200 Subject: [PATCH 0552/1500] Cleanup: VSE: Move thumbnail drawing to own file No functional changes. --- .../editors/space_sequencer/CMakeLists.txt | 1 + .../editors/space_sequencer/sequencer_draw.c | 527 +--------------- .../space_sequencer/sequencer_intern.h | 13 + .../space_sequencer/sequencer_thumbnails.c | 568 ++++++++++++++++++ 4 files changed, 584 insertions(+), 525 deletions(-) create mode 100644 source/blender/editors/space_sequencer/sequencer_thumbnails.c diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index e1c193b0c15..1471929defb 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -45,6 +45,7 @@ set(SRC sequencer_proxy.c sequencer_scopes.c sequencer_select.c + sequencer_thumbnails.c sequencer_view.c space_sequencer.c diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index d5cd5d11a35..bbe2def8081 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -25,7 +25,6 @@ #include #include "BLI_blenlib.h" -#include "BLI_ghash.h" #include "BLI_math.h" #include "BLI_string_utils.h" #include "BLI_threads.h" @@ -45,7 +44,6 @@ #include "BKE_context.h" #include "BKE_fcurve.h" #include "BKE_global.h" -#include "BKE_main.h" #include "BKE_scene.h" #include "BKE_sound.h" @@ -103,7 +101,6 @@ #define SEQ_HANDLE_SIZE 8.0f #define SEQ_SCROLLER_TEXT_OFFSET 8 #define MUTE_ALPHA 120 -#define OVERLAP_ALPHA 180 static Sequence *special_seq_update = NULL; @@ -349,8 +346,8 @@ static void draw_seq_waveform_overlay(View2D *v2d, float y2, float frames_per_pixel) { - if (seq->sound && - ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { + if (seq->sound && ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) || + (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid * flickering when moving around the strip. * To do this we figure out the fractional offset in pixel space by checking where the @@ -1312,526 +1309,6 @@ static void draw_seq_fcurve_overlay( } } -typedef struct ThumbnailDrawJob { - SeqRenderData context; - GHash *sequences_ghash; - Scene *scene; - rctf *view_area; - float pixelx; - float pixely; -} ThumbnailDrawJob; - -typedef struct ThumbDataItem { - Sequence *seq_dupli; - Scene *scene; -} ThumbDataItem; - -static void thumbnail_hash_data_free(void *val) -{ - ThumbDataItem *item = val; - SEQ_sequence_free(item->scene, item->seq_dupli, 0); - MEM_freeN(val); -} - -static void thumbnail_freejob(void *data) -{ - ThumbnailDrawJob *tj = data; - BLI_ghash_free(tj->sequences_ghash, NULL, thumbnail_hash_data_free); - MEM_freeN(tj->view_area); - MEM_freeN(tj); -} - -static void thumbnail_endjob(void *data) -{ - ThumbnailDrawJob *tj = data; - WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, tj->scene); -} - -static bool check_seq_need_thumbnails(Sequence *seq, rctf *view_area) -{ - if (seq->type != SEQ_TYPE_MOVIE && seq->type != SEQ_TYPE_IMAGE) { - return false; - } - if (min_ii(seq->startdisp, seq->start) > view_area->xmax) { - return false; - } - if (max_ii(seq->enddisp, seq->start + seq->len) < view_area->xmin) { - return false; - } - if (seq->machine + 1.0f < view_area->ymin) { - return false; - } - if (seq->machine > view_area->ymax) { - return false; - } - - return true; -} - -static void seq_get_thumb_image_dimensions(Sequence *seq, - float pixelx, - float pixely, - float *r_thumb_width, - float *r_thumb_height, - float *r_image_width, - float *r_image_height) -{ - float image_width = seq->strip->stripdata->orig_width; - float image_height = seq->strip->stripdata->orig_height; - - /* Fix the dimensions to be max SEQ_RENDER_THUMB_SIZE (256) for x or y. */ - float aspect_ratio = (float)image_width / image_height; - if (image_width > image_height) { - image_width = SEQ_RENDER_THUMB_SIZE; - image_height = round_fl_to_int(image_width / aspect_ratio); - } - else { - image_height = SEQ_RENDER_THUMB_SIZE; - image_width = round_fl_to_int(image_height * aspect_ratio); - } - - /* Calculate thumb dimensions. */ - float thumb_height = (SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM) - (20 * U.dpi_fac * pixely); - aspect_ratio = ((float)image_width) / image_height; - float thumb_h_px = thumb_height / pixely; - float thumb_width = aspect_ratio * thumb_h_px * pixelx; - - if (r_thumb_height == NULL) { - *r_thumb_width = thumb_width; - return; - } - - *r_thumb_height = thumb_height; - *r_image_width = image_width; - *r_image_height = image_height; - *r_thumb_width = thumb_width; -} - -static float seq_thumbnail_get_start_frame(Sequence *seq, float frame_step, rctf *view_area) -{ - if (seq->start > view_area->xmin && seq->start < view_area->xmax) { - return seq->start; - } - - /* Drawing and caching both check to see if strip is in view area or not before calling this - * function so assuming strip/part of strip in view. */ - - int no_invisible_thumbs = (view_area->xmin - seq->start) / frame_step; - return ((no_invisible_thumbs - 1) * frame_step) + seq->start; -} - -static void thumbnail_start_job(void *data, - short *stop, - short *UNUSED(do_update), - float *UNUSED(progress)) -{ - ThumbnailDrawJob *tj = data; - float start_frame, frame_step; - - GHashIterator gh_iter; - BLI_ghashIterator_init(&gh_iter, tj->sequences_ghash); - while (!BLI_ghashIterator_done(&gh_iter) & !*stop) { - Sequence *seq_orig = BLI_ghashIterator_getKey(&gh_iter); - ThumbDataItem *val = BLI_ghash_lookup(tj->sequences_ghash, seq_orig); - - if (check_seq_need_thumbnails(seq_orig, tj->view_area)) { - seq_get_thumb_image_dimensions( - val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, NULL, NULL, NULL); - start_frame = seq_thumbnail_get_start_frame(seq_orig, frame_step, tj->view_area); - SEQ_render_thumbnails( - &tj->context, val->seq_dupli, seq_orig, start_frame, frame_step, tj->view_area, stop); - SEQ_render_thumbnails_base_set(&tj->context, val->seq_dupli, seq_orig, tj->view_area, stop); - } - BLI_ghashIterator_step(&gh_iter); - } -} - -static SeqRenderData sequencer_thumbnail_context_init(const bContext *C) -{ - struct Main *bmain = CTX_data_main(C); - struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - Scene *scene = CTX_data_scene(C); - SpaceSeq *sseq = CTX_wm_space_seq(C); - SeqRenderData context = {0}; - - /* Taking rectx and recty as 0 as dimensions not known here, and context is used to calculate - * hash key but not necessary as other variables of SeqRenderData are unique enough. */ - SEQ_render_new_render_data(bmain, depsgraph, scene, 0, 0, sseq->render_size, false, &context); - context.view_id = BKE_scene_multiview_view_id_get(&scene->r, STEREO_LEFT_NAME); - context.use_proxies = false; - - return context; -} - -static GHash *sequencer_thumbnail_ghash_init(const bContext *C, View2D *v2d, Editing *ed) -{ - Scene *scene = CTX_data_scene(C); - - /* Set the data for thumbnail caching job. */ - GHash *thumb_data_hash = BLI_ghash_ptr_new("seq_duplicates_and_origs"); - - LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { - ThumbDataItem *val_need_update = BLI_ghash_lookup(thumb_data_hash, seq); - if (val_need_update == NULL && check_seq_need_thumbnails(seq, &v2d->cur)) { - ThumbDataItem *val = MEM_callocN(sizeof(ThumbDataItem), "Thumbnail Hash Values"); - val->seq_dupli = SEQ_sequence_dupli_recursive(scene, scene, NULL, seq, 0); - val->scene = scene; - BLI_ghash_insert(thumb_data_hash, seq, val); - } - else { - if (val_need_update != NULL) { - val_need_update->seq_dupli->start = seq->start; - val_need_update->seq_dupli->startdisp = seq->startdisp; - } - } - } - - return thumb_data_hash; -} - -static void sequencer_thumbnail_init_job(const bContext *C, View2D *v2d, Editing *ed) -{ - wmJob *wm_job; - ThumbnailDrawJob *tj = NULL; - ScrArea *area = CTX_wm_area(C); - wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - CTX_data_scene(C), - "Draw Thumbnails", - 0, - WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL); - - /* Get the thumbnail job if it exists. */ - tj = WM_jobs_customdata_get(wm_job); - if (!tj) { - tj = MEM_callocN(sizeof(ThumbnailDrawJob), "Thumbnail cache job"); - - /* Duplicate value of v2d->cur and v2d->tot to have module separation. */ - rctf *view_area = MEM_callocN(sizeof(struct rctf), "viewport area"); - view_area->xmax = v2d->cur.xmax; - view_area->xmin = v2d->cur.xmin; - view_area->ymax = v2d->cur.ymax; - view_area->ymin = v2d->cur.ymin; - - tj->scene = CTX_data_scene(C); - tj->view_area = view_area; - tj->context = sequencer_thumbnail_context_init(C); - tj->sequences_ghash = sequencer_thumbnail_ghash_init(C, v2d, ed); - tj->pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask); - tj->pixely = BLI_rctf_size_y(&v2d->cur) / BLI_rcti_size_y(&v2d->mask); - WM_jobs_customdata_set(wm_job, tj, thumbnail_freejob); - WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_SEQUENCER, NC_SCENE | ND_SEQUENCER); - WM_jobs_callbacks(wm_job, thumbnail_start_job, NULL, NULL, thumbnail_endjob); - } - - if (!WM_jobs_is_running(wm_job)) { - G.is_break = false; - WM_jobs_start(CTX_wm_manager(C), wm_job); - } - else { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL); - } - - ED_area_tag_redraw(area); -} - -static bool sequencer_thumbnail_v2d_is_navigating(const bContext *C) -{ - ARegion *region = CTX_wm_region(C); - View2D *v2d = ®ion->v2d; - return (v2d->flag & V2D_IS_NAVIGATING) != 0; -} - -static void sequencer_thumbnail_start_job_if_necessary(const bContext *C, - Editing *ed, - View2D *v2d, - bool thumbnail_is_missing) -{ - SpaceSeq *sseq = CTX_wm_space_seq(C); - - if (sequencer_thumbnail_v2d_is_navigating(C)) { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL); - return; - } - - /* `thumbnail_is_missing` should be set to true if missing image in strip. False when normal call - * to all strips done. */ - if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || - v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax || thumbnail_is_missing) { - - /* Stop the job first as view has changed. Pointless to continue old job. */ - if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || - v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax) { - WM_jobs_stop(CTX_wm_manager(C), NULL, thumbnail_start_job); - } - - sequencer_thumbnail_init_job(C, v2d, ed); - sseq->runtime.last_thumbnail_area = v2d->cur; - } -} - -void last_displayed_thumbnails_list_free(void *val) -{ - BLI_gset_free(val, NULL); -} - -static GSet *last_displayed_thumbnails_list_ensure(const bContext *C, Sequence *seq) -{ - SpaceSeq *sseq = CTX_wm_space_seq(C); - if (sseq->runtime.last_displayed_thumbnails == NULL) { - sseq->runtime.last_displayed_thumbnails = BLI_ghash_ptr_new(__func__); - } - - GSet *displayed_thumbnails = BLI_ghash_lookup(sseq->runtime.last_displayed_thumbnails, seq); - if (displayed_thumbnails == NULL) { - displayed_thumbnails = BLI_gset_int_new(__func__); - BLI_ghash_insert(sseq->runtime.last_displayed_thumbnails, seq, displayed_thumbnails); - } - - return displayed_thumbnails; -} - -static void last_displayed_thumbnails_list_cleanup(GSet *previously_displayed, - float range_start, - float range_end) -{ - GSetIterator gset_iter; - BLI_gsetIterator_init(&gset_iter, previously_displayed); - while (!BLI_gsetIterator_done(&gset_iter)) { - int frame = (float)POINTER_AS_INT(BLI_gsetIterator_getKey(&gset_iter)); - BLI_gsetIterator_step(&gset_iter); - - if (frame > range_start && frame < range_end) { - BLI_gset_remove(previously_displayed, POINTER_FROM_INT(frame), NULL); - } - } -} - -static int sequencer_thumbnail_closest_previous_frame_get(int timeline_frame, - GSet *previously_displayed) -{ - int best_diff = INT_MAX; - int best_frame = timeline_frame; - - /* Previously displayed thumbnails. */ - GSetIterator gset_iter; - BLI_gsetIterator_init(&gset_iter, previously_displayed); - while (!BLI_gsetIterator_done(&gset_iter)) { - int frame = POINTER_AS_INT(BLI_gsetIterator_getKey(&gset_iter)); - int diff = abs(frame - timeline_frame); - if (diff < best_diff) { - best_diff = diff; - best_frame = frame; - } - BLI_gsetIterator_step(&gset_iter); - } - return best_frame; -} - -static int sequencer_thumbnail_closest_guaranteed_frame_get(Sequence *seq, int timeline_frame) -{ - if (timeline_frame <= seq->startdisp) { - return seq->startdisp; - } - - /* Set of "guaranteed" thumbnails. */ - const int frame_index = timeline_frame - seq->startdisp; - const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(seq); - const int relative_base_frame = round_fl_to_int((frame_index / (float)frame_step)) * frame_step; - const int nearest_guaranted_absolute_frame = relative_base_frame + seq->startdisp; - return nearest_guaranted_absolute_frame; -} - -static ImBuf *sequencer_thumbnail_closest_from_memory(const SeqRenderData *context, - Sequence *seq, - int timeline_frame, - GSet *previously_displayed, - rcti *crop, - bool clipped) -{ - int frame_previous = sequencer_thumbnail_closest_previous_frame_get(timeline_frame, - previously_displayed); - ImBuf *ibuf_previous = SEQ_get_thumbnail(context, seq, frame_previous, crop, clipped); - - int frame_guaranteed = sequencer_thumbnail_closest_guaranteed_frame_get(seq, timeline_frame); - ImBuf *ibuf_guaranteed = SEQ_get_thumbnail(context, seq, frame_guaranteed, crop, clipped); - - ImBuf *closest_in_memory = NULL; - - if (ibuf_previous && ibuf_guaranteed) { - if (abs(frame_previous - timeline_frame) < abs(frame_guaranteed - timeline_frame)) { - IMB_freeImBuf(ibuf_guaranteed); - closest_in_memory = ibuf_previous; - } - else { - IMB_freeImBuf(ibuf_previous); - closest_in_memory = ibuf_guaranteed; - } - } - - if (ibuf_previous == NULL) { - closest_in_memory = ibuf_guaranteed; - } - - if (ibuf_guaranteed == NULL) { - closest_in_memory = ibuf_previous; - } - - return closest_in_memory; -} - -static void draw_seq_strip_thumbnail(View2D *v2d, - const bContext *C, - Scene *scene, - Sequence *seq, - float y1, - float y2, - float pixelx, - float pixely) -{ - bool clipped = false; - float image_height, image_width, thumb_width, thumb_height; - rcti crop; - - /* If width of the strip too small ignore drawing thumbnails. */ - if ((y2 - y1) / pixely <= 40 * U.dpi_fac) { - return; - } - - SeqRenderData context = sequencer_thumbnail_context_init(C); - - if ((seq->flag & SEQ_FLAG_SKIP_THUMBNAILS) != 0) { - return; - } - - seq_get_thumb_image_dimensions( - seq, pixelx, pixely, &thumb_width, &thumb_height, &image_width, &image_height); - - float thumb_y_end = y1 + thumb_height - pixely; - - float cut_off = 0; - float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; - if (seq->type == SEQ_TYPE_IMAGE) { - upper_thumb_bound = seq->enddisp; - } - - float thumb_x_start = seq_thumbnail_get_start_frame(seq, thumb_width, &v2d->cur); - float thumb_x_end; - - while (thumb_x_start + thumb_width < v2d->cur.xmin) { - thumb_x_start += thumb_width; - } - - /* Ignore thumbs to the left of strip. */ - while (thumb_x_start + thumb_width < seq->startdisp) { - thumb_x_start += thumb_width; - } - - GSet *last_displayed_thumbnails = last_displayed_thumbnails_list_ensure(C, seq); - /* Cleanup thumbnail list outside of rendered range, which is cleaned up one by one to prevent - * flickering after zooming. */ - if (!sequencer_thumbnail_v2d_is_navigating(C)) { - last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, -FLT_MAX, thumb_x_start); - } - - /* Start drawing. */ - while (thumb_x_start < upper_thumb_bound) { - thumb_x_end = thumb_x_start + thumb_width; - clipped = false; - - /* Checks to make sure that thumbs are loaded only when in view and within the confines of the - * strip. Some may not be required but better to have conditions for safety as x1 here is - * point to start caching from and not drawing. */ - if (thumb_x_start > v2d->cur.xmax) { - break; - } - - /* Set the clipping bound to show the left handle moving over thumbs and not shift thumbs. */ - if (IN_RANGE_INCL(seq->startdisp, thumb_x_start, thumb_x_end)) { - cut_off = seq->startdisp - thumb_x_start; - clipped = true; - } - - /* Clip if full thumbnail cannot be displayed. */ - if (thumb_x_end > (upper_thumb_bound)) { - thumb_x_end = upper_thumb_bound; - clipped = true; - if (thumb_x_end - thumb_x_start < 1) { - break; - } - } - - float zoom_x = thumb_width / image_width; - float zoom_y = thumb_height / image_height; - - float cropx_min = (cut_off / pixelx) / (zoom_y / pixely); - float cropx_max = ((thumb_x_end - thumb_x_start) / pixelx) / (zoom_y / pixely); - if (cropx_max == (thumb_x_end - thumb_x_start)) { - cropx_max = cropx_max + 1; - } - BLI_rcti_init(&crop, (int)(cropx_min), (int)cropx_max, 0, (int)(image_height)-1); - - int timeline_frame = round_fl_to_int(thumb_x_start); - - /* Get the image. */ - ImBuf *ibuf = SEQ_get_thumbnail(&context, seq, timeline_frame, &crop, clipped); - - if (!ibuf) { - sequencer_thumbnail_start_job_if_necessary(C, scene->ed, v2d, true); - - ibuf = sequencer_thumbnail_closest_from_memory( - &context, seq, timeline_frame, last_displayed_thumbnails, &crop, clipped); - } - /* Store recently rendered frames, so they can be reused when zooming. */ - else if (!sequencer_thumbnail_v2d_is_navigating(C)) { - /* Clear images in frame range occupied by new thumbnail. */ - last_displayed_thumbnails_list_cleanup( - last_displayed_thumbnails, thumb_x_start, thumb_x_end); - /* Insert new thumbnail frame to list. */ - BLI_gset_add(last_displayed_thumbnails, POINTER_FROM_INT(timeline_frame)); - } - - /* If there is no image still, abort. */ - if (!ibuf) { - break; - } - - /* Transparency on overlap. */ - if (seq->flag & SEQ_OVERLAP) { - GPU_blend(GPU_BLEND_ALPHA); - if (ibuf->rect) { - unsigned char *buf = (unsigned char *)ibuf->rect; - for (int pixel = ibuf->x * ibuf->y; pixel--; buf += 4) { - buf[3] = OVERLAP_ALPHA; - } - } - else if (ibuf->rect_float) { - float *buf = (float *)ibuf->rect_float; - for (int pixel = ibuf->x * ibuf->y; pixel--; buf += ibuf->channels) { - buf[3] = (OVERLAP_ALPHA / 255.0f); - } - } - } - - ED_draw_imbuf_ctx_clipping(C, - ibuf, - thumb_x_start + cut_off, - y1, - true, - thumb_x_start + cut_off, - y1, - thumb_x_end, - thumb_y_end, - zoom_x, - zoom_y); - IMB_freeImBuf(ibuf); - GPU_blend(GPU_BLEND_NONE); - cut_off = 0; - thumb_x_start += thumb_width; - } - last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, thumb_x_start, FLT_MAX); -} - /* Draw visible strips. Bounds check are already made. */ static void draw_seq_strip(const bContext *C, SpaceSeq *sseq, diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 202eda85dca..730d4d11e78 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -38,8 +38,11 @@ struct SpaceSeq; struct StripElem; struct bContext; struct rctf; +struct View2D; struct wmOperator; +#define OVERLAP_ALPHA 180 + /* sequencer_draw.c */ void draw_timeline_seq(const struct bContext *C, struct ARegion *region); void draw_timeline_seq_display(const struct bContext *C, struct ARegion *region); @@ -70,7 +73,17 @@ struct ImBuf *sequencer_ibuf_get(struct Main *bmain, int timeline_frame, int frame_ofs, const char *viewname); + +/* sequencer_thumbnails.c */ void last_displayed_thumbnails_list_free(void *val); +void draw_seq_strip_thumbnail(struct View2D *v2d, + const struct bContext *C, + struct Scene *scene, + struct Sequence *seq, + float y1, + float y2, + float pixelx, + float pixely); /* sequencer_edit.c */ struct View2D; diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.c b/source/blender/editors/space_sequencer/sequencer_thumbnails.c new file mode 100644 index 00000000000..f67b40b9674 --- /dev/null +++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.c @@ -0,0 +1,568 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spseq + */ + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_scene.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "ED_screen.h" + +#include "BIF_glutil.h" + +#include "SEQ_render.h" +#include "SEQ_sequencer.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "MEM_guardedalloc.h" + +/* Own include. */ +#include "sequencer_intern.h" + +typedef struct ThumbnailDrawJob { + SeqRenderData context; + GHash *sequences_ghash; + Scene *scene; + rctf *view_area; + float pixelx; + float pixely; +} ThumbnailDrawJob; + +typedef struct ThumbDataItem { + Sequence *seq_dupli; + Scene *scene; +} ThumbDataItem; + +static void thumbnail_hash_data_free(void *val) +{ + ThumbDataItem *item = val; + SEQ_sequence_free(item->scene, item->seq_dupli, 0); + MEM_freeN(val); +} + +static void thumbnail_freejob(void *data) +{ + ThumbnailDrawJob *tj = data; + BLI_ghash_free(tj->sequences_ghash, NULL, thumbnail_hash_data_free); + MEM_freeN(tj->view_area); + MEM_freeN(tj); +} + +static void thumbnail_endjob(void *data) +{ + ThumbnailDrawJob *tj = data; + WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, tj->scene); +} + +static bool check_seq_need_thumbnails(Sequence *seq, rctf *view_area) +{ + if (seq->type != SEQ_TYPE_MOVIE && seq->type != SEQ_TYPE_IMAGE) { + return false; + } + if (min_ii(seq->startdisp, seq->start) > view_area->xmax) { + return false; + } + if (max_ii(seq->enddisp, seq->start + seq->len) < view_area->xmin) { + return false; + } + if (seq->machine + 1.0f < view_area->ymin) { + return false; + } + if (seq->machine > view_area->ymax) { + return false; + } + + return true; +} + +static void seq_get_thumb_image_dimensions(Sequence *seq, + float pixelx, + float pixely, + float *r_thumb_width, + float *r_thumb_height, + float *r_image_width, + float *r_image_height) +{ + float image_width = seq->strip->stripdata->orig_width; + float image_height = seq->strip->stripdata->orig_height; + + /* Fix the dimensions to be max SEQ_RENDER_THUMB_SIZE (256) for x or y. */ + float aspect_ratio = (float)image_width / image_height; + if (image_width > image_height) { + image_width = SEQ_RENDER_THUMB_SIZE; + image_height = round_fl_to_int(image_width / aspect_ratio); + } + else { + image_height = SEQ_RENDER_THUMB_SIZE; + image_width = round_fl_to_int(image_height * aspect_ratio); + } + + /* Calculate thumb dimensions. */ + float thumb_height = (SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM) - (20 * U.dpi_fac * pixely); + aspect_ratio = ((float)image_width) / image_height; + float thumb_h_px = thumb_height / pixely; + float thumb_width = aspect_ratio * thumb_h_px * pixelx; + + if (r_thumb_height == NULL) { + *r_thumb_width = thumb_width; + return; + } + + *r_thumb_height = thumb_height; + *r_image_width = image_width; + *r_image_height = image_height; + *r_thumb_width = thumb_width; +} + +static float seq_thumbnail_get_start_frame(Sequence *seq, float frame_step, rctf *view_area) +{ + if (seq->start > view_area->xmin && seq->start < view_area->xmax) { + return seq->start; + } + + /* Drawing and caching both check to see if strip is in view area or not before calling this + * function so assuming strip/part of strip in view. */ + + int no_invisible_thumbs = (view_area->xmin - seq->start) / frame_step; + return ((no_invisible_thumbs - 1) * frame_step) + seq->start; +} + +static void thumbnail_start_job(void *data, + short *stop, + short *UNUSED(do_update), + float *UNUSED(progress)) +{ + ThumbnailDrawJob *tj = data; + float start_frame, frame_step; + + GHashIterator gh_iter; + BLI_ghashIterator_init(&gh_iter, tj->sequences_ghash); + while (!BLI_ghashIterator_done(&gh_iter) & !*stop) { + Sequence *seq_orig = BLI_ghashIterator_getKey(&gh_iter); + ThumbDataItem *val = BLI_ghash_lookup(tj->sequences_ghash, seq_orig); + + if (check_seq_need_thumbnails(seq_orig, tj->view_area)) { + seq_get_thumb_image_dimensions( + val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, NULL, NULL, NULL); + start_frame = seq_thumbnail_get_start_frame(seq_orig, frame_step, tj->view_area); + SEQ_render_thumbnails( + &tj->context, val->seq_dupli, seq_orig, start_frame, frame_step, tj->view_area, stop); + SEQ_render_thumbnails_base_set(&tj->context, val->seq_dupli, seq_orig, tj->view_area, stop); + } + BLI_ghashIterator_step(&gh_iter); + } +} + +static SeqRenderData sequencer_thumbnail_context_init(const bContext *C) +{ + struct Main *bmain = CTX_data_main(C); + struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Scene *scene = CTX_data_scene(C); + SpaceSeq *sseq = CTX_wm_space_seq(C); + SeqRenderData context = {0}; + + /* Taking rectx and recty as 0 as dimensions not known here, and context is used to calculate + * hash key but not necessary as other variables of SeqRenderData are unique enough. */ + SEQ_render_new_render_data(bmain, depsgraph, scene, 0, 0, sseq->render_size, false, &context); + context.view_id = BKE_scene_multiview_view_id_get(&scene->r, STEREO_LEFT_NAME); + context.use_proxies = false; + + return context; +} + +static GHash *sequencer_thumbnail_ghash_init(const bContext *C, View2D *v2d, Editing *ed) +{ + Scene *scene = CTX_data_scene(C); + + /* Set the data for thumbnail caching job. */ + GHash *thumb_data_hash = BLI_ghash_ptr_new("seq_duplicates_and_origs"); + + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { + ThumbDataItem *val_need_update = BLI_ghash_lookup(thumb_data_hash, seq); + if (val_need_update == NULL && check_seq_need_thumbnails(seq, &v2d->cur)) { + ThumbDataItem *val = MEM_callocN(sizeof(ThumbDataItem), "Thumbnail Hash Values"); + val->seq_dupli = SEQ_sequence_dupli_recursive(scene, scene, NULL, seq, 0); + val->scene = scene; + BLI_ghash_insert(thumb_data_hash, seq, val); + } + else { + if (val_need_update != NULL) { + val_need_update->seq_dupli->start = seq->start; + val_need_update->seq_dupli->startdisp = seq->startdisp; + } + } + } + + return thumb_data_hash; +} + +static void sequencer_thumbnail_init_job(const bContext *C, View2D *v2d, Editing *ed) +{ + wmJob *wm_job; + ThumbnailDrawJob *tj = NULL; + ScrArea *area = CTX_wm_area(C); + wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + CTX_data_scene(C), + "Draw Thumbnails", + 0, + WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL); + + /* Get the thumbnail job if it exists. */ + tj = WM_jobs_customdata_get(wm_job); + if (!tj) { + tj = MEM_callocN(sizeof(ThumbnailDrawJob), "Thumbnail cache job"); + + /* Duplicate value of v2d->cur and v2d->tot to have module separation. */ + rctf *view_area = MEM_callocN(sizeof(struct rctf), "viewport area"); + view_area->xmax = v2d->cur.xmax; + view_area->xmin = v2d->cur.xmin; + view_area->ymax = v2d->cur.ymax; + view_area->ymin = v2d->cur.ymin; + + tj->scene = CTX_data_scene(C); + tj->view_area = view_area; + tj->context = sequencer_thumbnail_context_init(C); + tj->sequences_ghash = sequencer_thumbnail_ghash_init(C, v2d, ed); + tj->pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask); + tj->pixely = BLI_rctf_size_y(&v2d->cur) / BLI_rcti_size_y(&v2d->mask); + WM_jobs_customdata_set(wm_job, tj, thumbnail_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_SEQUENCER, NC_SCENE | ND_SEQUENCER); + WM_jobs_callbacks(wm_job, thumbnail_start_job, NULL, NULL, thumbnail_endjob); + } + + if (!WM_jobs_is_running(wm_job)) { + G.is_break = false; + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL); + } + + ED_area_tag_redraw(area); +} + +static bool sequencer_thumbnail_v2d_is_navigating(const bContext *C) +{ + ARegion *region = CTX_wm_region(C); + View2D *v2d = ®ion->v2d; + return (v2d->flag & V2D_IS_NAVIGATING) != 0; +} + +static void sequencer_thumbnail_start_job_if_necessary(const bContext *C, + Editing *ed, + View2D *v2d, + bool thumbnail_is_missing) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + + if (sequencer_thumbnail_v2d_is_navigating(C)) { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL); + return; + } + + /* `thumbnail_is_missing` should be set to true if missing image in strip. False when normal call + * to all strips done. */ + if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || + v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax || thumbnail_is_missing) { + + /* Stop the job first as view has changed. Pointless to continue old job. */ + if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || + v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax) { + WM_jobs_stop(CTX_wm_manager(C), NULL, thumbnail_start_job); + } + + sequencer_thumbnail_init_job(C, v2d, ed); + sseq->runtime.last_thumbnail_area = v2d->cur; + } +} + +void last_displayed_thumbnails_list_free(void *val) +{ + BLI_gset_free(val, NULL); +} + +static GSet *last_displayed_thumbnails_list_ensure(const bContext *C, Sequence *seq) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + if (sseq->runtime.last_displayed_thumbnails == NULL) { + sseq->runtime.last_displayed_thumbnails = BLI_ghash_ptr_new(__func__); + } + + GSet *displayed_thumbnails = BLI_ghash_lookup(sseq->runtime.last_displayed_thumbnails, seq); + if (displayed_thumbnails == NULL) { + displayed_thumbnails = BLI_gset_int_new(__func__); + BLI_ghash_insert(sseq->runtime.last_displayed_thumbnails, seq, displayed_thumbnails); + } + + return displayed_thumbnails; +} + +static void last_displayed_thumbnails_list_cleanup(GSet *previously_displayed, + float range_start, + float range_end) +{ + GSetIterator gset_iter; + BLI_gsetIterator_init(&gset_iter, previously_displayed); + while (!BLI_gsetIterator_done(&gset_iter)) { + int frame = (float)POINTER_AS_INT(BLI_gsetIterator_getKey(&gset_iter)); + BLI_gsetIterator_step(&gset_iter); + + if (frame > range_start && frame < range_end) { + BLI_gset_remove(previously_displayed, POINTER_FROM_INT(frame), NULL); + } + } +} + +static int sequencer_thumbnail_closest_previous_frame_get(int timeline_frame, + GSet *previously_displayed) +{ + int best_diff = INT_MAX; + int best_frame = timeline_frame; + + /* Previously displayed thumbnails. */ + GSetIterator gset_iter; + BLI_gsetIterator_init(&gset_iter, previously_displayed); + while (!BLI_gsetIterator_done(&gset_iter)) { + int frame = POINTER_AS_INT(BLI_gsetIterator_getKey(&gset_iter)); + int diff = abs(frame - timeline_frame); + if (diff < best_diff) { + best_diff = diff; + best_frame = frame; + } + BLI_gsetIterator_step(&gset_iter); + } + return best_frame; +} + +static int sequencer_thumbnail_closest_guaranteed_frame_get(Sequence *seq, int timeline_frame) +{ + if (timeline_frame <= seq->startdisp) { + return seq->startdisp; + } + + /* Set of "guaranteed" thumbnails. */ + const int frame_index = timeline_frame - seq->startdisp; + const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(seq); + const int relative_base_frame = round_fl_to_int((frame_index / (float)frame_step)) * frame_step; + const int nearest_guaranted_absolute_frame = relative_base_frame + seq->startdisp; + return nearest_guaranted_absolute_frame; +} + +static ImBuf *sequencer_thumbnail_closest_from_memory(const SeqRenderData *context, + Sequence *seq, + int timeline_frame, + GSet *previously_displayed, + rcti *crop, + bool clipped) +{ + int frame_previous = sequencer_thumbnail_closest_previous_frame_get(timeline_frame, + previously_displayed); + ImBuf *ibuf_previous = SEQ_get_thumbnail(context, seq, frame_previous, crop, clipped); + + int frame_guaranteed = sequencer_thumbnail_closest_guaranteed_frame_get(seq, timeline_frame); + ImBuf *ibuf_guaranteed = SEQ_get_thumbnail(context, seq, frame_guaranteed, crop, clipped); + + ImBuf *closest_in_memory = NULL; + + if (ibuf_previous && ibuf_guaranteed) { + if (abs(frame_previous - timeline_frame) < abs(frame_guaranteed - timeline_frame)) { + IMB_freeImBuf(ibuf_guaranteed); + closest_in_memory = ibuf_previous; + } + else { + IMB_freeImBuf(ibuf_previous); + closest_in_memory = ibuf_guaranteed; + } + } + + if (ibuf_previous == NULL) { + closest_in_memory = ibuf_guaranteed; + } + + if (ibuf_guaranteed == NULL) { + closest_in_memory = ibuf_previous; + } + + return closest_in_memory; +} + +void draw_seq_strip_thumbnail(View2D *v2d, + const bContext *C, + Scene *scene, + Sequence *seq, + float y1, + float y2, + float pixelx, + float pixely) +{ + bool clipped = false; + float image_height, image_width, thumb_width, thumb_height; + rcti crop; + + /* If width of the strip too small ignore drawing thumbnails. */ + if ((y2 - y1) / pixely <= 40 * U.dpi_fac) { + return; + } + + SeqRenderData context = sequencer_thumbnail_context_init(C); + + if ((seq->flag & SEQ_FLAG_SKIP_THUMBNAILS) != 0) { + return; + } + + seq_get_thumb_image_dimensions( + seq, pixelx, pixely, &thumb_width, &thumb_height, &image_width, &image_height); + + float thumb_y_end = y1 + thumb_height - pixely; + + float cut_off = 0; + float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; + if (seq->type == SEQ_TYPE_IMAGE) { + upper_thumb_bound = seq->enddisp; + } + + float thumb_x_start = seq_thumbnail_get_start_frame(seq, thumb_width, &v2d->cur); + float thumb_x_end; + + while (thumb_x_start + thumb_width < v2d->cur.xmin) { + thumb_x_start += thumb_width; + } + + /* Ignore thumbs to the left of strip. */ + while (thumb_x_start + thumb_width < seq->startdisp) { + thumb_x_start += thumb_width; + } + + GSet *last_displayed_thumbnails = last_displayed_thumbnails_list_ensure(C, seq); + /* Cleanup thumbnail list outside of rendered range, which is cleaned up one by one to prevent + * flickering after zooming. */ + if (!sequencer_thumbnail_v2d_is_navigating(C)) { + last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, -FLT_MAX, thumb_x_start); + } + + /* Start drawing. */ + while (thumb_x_start < upper_thumb_bound) { + thumb_x_end = thumb_x_start + thumb_width; + clipped = false; + + /* Checks to make sure that thumbs are loaded only when in view and within the confines of the + * strip. Some may not be required but better to have conditions for safety as x1 here is + * point to start caching from and not drawing. */ + if (thumb_x_start > v2d->cur.xmax) { + break; + } + + /* Set the clipping bound to show the left handle moving over thumbs and not shift thumbs. */ + if (IN_RANGE_INCL(seq->startdisp, thumb_x_start, thumb_x_end)) { + cut_off = seq->startdisp - thumb_x_start; + clipped = true; + } + + /* Clip if full thumbnail cannot be displayed. */ + if (thumb_x_end > (upper_thumb_bound)) { + thumb_x_end = upper_thumb_bound; + clipped = true; + if (thumb_x_end - thumb_x_start < 1) { + break; + } + } + + float zoom_x = thumb_width / image_width; + float zoom_y = thumb_height / image_height; + + float cropx_min = (cut_off / pixelx) / (zoom_y / pixely); + float cropx_max = ((thumb_x_end - thumb_x_start) / pixelx) / (zoom_y / pixely); + if (cropx_max == (thumb_x_end - thumb_x_start)) { + cropx_max = cropx_max + 1; + } + BLI_rcti_init(&crop, (int)(cropx_min), (int)cropx_max, 0, (int)(image_height)-1); + + int timeline_frame = round_fl_to_int(thumb_x_start); + + /* Get the image. */ + ImBuf *ibuf = SEQ_get_thumbnail(&context, seq, timeline_frame, &crop, clipped); + + if (!ibuf) { + sequencer_thumbnail_start_job_if_necessary(C, scene->ed, v2d, true); + + ibuf = sequencer_thumbnail_closest_from_memory( + &context, seq, timeline_frame, last_displayed_thumbnails, &crop, clipped); + } + /* Store recently rendered frames, so they can be reused when zooming. */ + else if (!sequencer_thumbnail_v2d_is_navigating(C)) { + /* Clear images in frame range occupied by new thumbnail. */ + last_displayed_thumbnails_list_cleanup( + last_displayed_thumbnails, thumb_x_start, thumb_x_end); + /* Insert new thumbnail frame to list. */ + BLI_gset_add(last_displayed_thumbnails, POINTER_FROM_INT(timeline_frame)); + } + + /* If there is no image still, abort. */ + if (!ibuf) { + break; + } + + /* Transparency on overlap. */ + if (seq->flag & SEQ_OVERLAP) { + GPU_blend(GPU_BLEND_ALPHA); + if (ibuf->rect) { + unsigned char *buf = (unsigned char *)ibuf->rect; + for (int pixel = ibuf->x * ibuf->y; pixel--; buf += 4) { + buf[3] = OVERLAP_ALPHA; + } + } + else if (ibuf->rect_float) { + float *buf = (float *)ibuf->rect_float; + for (int pixel = ibuf->x * ibuf->y; pixel--; buf += ibuf->channels) { + buf[3] = (OVERLAP_ALPHA / 255.0f); + } + } + } + + ED_draw_imbuf_ctx_clipping(C, + ibuf, + thumb_x_start + cut_off, + y1, + true, + thumb_x_start + cut_off, + y1, + thumb_x_end, + thumb_y_end, + zoom_x, + zoom_y); + IMB_freeImBuf(ibuf); + GPU_blend(GPU_BLEND_NONE); + cut_off = 0; + thumb_x_start += thumb_width; + } + last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, thumb_x_start, FLT_MAX); +} From 0d68d7baa3c55971ce6640738716690ff819a052 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 11:37:50 +1100 Subject: [PATCH 0553/1500] Cleanup: clang-format, correct doxy groups --- source/blender/blenkernel/BKE_appdir.h | 2 +- source/blender/blenkernel/BKE_curve_to_mesh.hh | 2 +- source/blender/blenkernel/BKE_ocean.h | 2 +- source/blender/blenkernel/BKE_rigidbody.h | 2 +- source/blender/blenkernel/intern/bpath.c | 2 +- source/blender/blenkernel/intern/ocean_intern.h | 2 +- source/blender/blenkernel/intern/pbvh.c | 2 +- source/blender/blenkernel/intern/pbvh_bmesh.c | 2 +- source/blender/blenkernel/intern/pbvh_intern.h | 2 +- source/blender/blenkernel/intern/rigidbody.c | 2 +- source/blender/blenkernel/intern/vfont.c | 2 +- source/blender/blenkernel/intern/vfontdata_freetype.c | 2 +- source/blender/blenlib/BLI_timecode.h | 2 +- source/blender/blenlib/BLI_timer.h | 2 +- source/blender/blenlib/intern/timecode.c | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h index fee52479cd0..0f00d391973 100644 --- a/source/blender/blenkernel/BKE_appdir.h +++ b/source/blender/blenkernel/BKE_appdir.h @@ -16,7 +16,7 @@ #pragma once /** \file - * \ingroup bli + * \ingroup bke */ #include diff --git a/source/blender/blenkernel/BKE_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh index cc1ef08908d..87bec6203a9 100644 --- a/source/blender/blenkernel/BKE_curve_to_mesh.hh +++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh @@ -20,7 +20,7 @@ struct Mesh; struct CurveEval; /** \file - * \ingroup geo + * \ingroup bke */ namespace blender::bke { diff --git a/source/blender/blenkernel/BKE_ocean.h b/source/blender/blenkernel/BKE_ocean.h index 380f9045520..186e0ec174b 100644 --- a/source/blender/blenkernel/BKE_ocean.h +++ b/source/blender/blenkernel/BKE_ocean.h @@ -19,7 +19,7 @@ #include /** \file - * \ingroup bli + * \ingroup bke */ #ifdef __cplusplus diff --git a/source/blender/blenkernel/BKE_rigidbody.h b/source/blender/blenkernel/BKE_rigidbody.h index ae1e437cd60..e28f668d189 100644 --- a/source/blender/blenkernel/BKE_rigidbody.h +++ b/source/blender/blenkernel/BKE_rigidbody.h @@ -18,7 +18,7 @@ */ /** \file - * \ingroup blenkernel + * \ingroup bke * \brief API for Blender-side Rigid Body stuff */ diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 07ac0086f0d..9ce58d8129b 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -15,7 +15,7 @@ */ /** \file - * \ingroup bli + * \ingroup bke */ /* TODO: diff --git a/source/blender/blenkernel/intern/ocean_intern.h b/source/blender/blenkernel/intern/ocean_intern.h index 4ebd03789af..df9dcd7e2f5 100644 --- a/source/blender/blenkernel/intern/ocean_intern.h +++ b/source/blender/blenkernel/intern/ocean_intern.h @@ -17,7 +17,7 @@ #pragma once /** \file - * \ingroup bli + * \ingroup bke */ #ifdef __cplusplus diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index ca1fada8c76..a24b53e48b7 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -15,7 +15,7 @@ */ /** \file - * \ingroup bli + * \ingroup bke */ #include "MEM_guardedalloc.h" diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index c30f94a4cf6..679a8b378b9 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -15,7 +15,7 @@ */ /** \file - * \ingroup bli + * \ingroup bke */ #include "MEM_guardedalloc.h" diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index 84c4ae4dead..79b25c027ba 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -17,7 +17,7 @@ #pragma once /** \file - * \ingroup bli + * \ingroup bke */ /* Axis-aligned bounding box */ diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 242bad163d8..4482285c271 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -18,7 +18,7 @@ */ /** \file - * \ingroup blenkernel + * \ingroup bke * \brief Blender-side interface and methods for dealing with Rigid Body simulations */ diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c index e650ae696b5..01c232e5a70 100644 --- a/source/blender/blenkernel/intern/vfont.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -50,12 +50,12 @@ #include "BKE_anim_path.h" #include "BKE_curve.h" -#include "BKE_vfont.h" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_packedFile.h" +#include "BKE_vfont.h" #include "BKE_vfontdata.h" #include "BLO_read_write.h" diff --git a/source/blender/blenkernel/intern/vfontdata_freetype.c b/source/blender/blenkernel/intern/vfontdata_freetype.c index db9fdef75c0..caeb016aaa3 100644 --- a/source/blender/blenkernel/intern/vfontdata_freetype.c +++ b/source/blender/blenkernel/intern/vfontdata_freetype.c @@ -23,7 +23,7 @@ */ /** \file - * \ingroup bli + * \ingroup bke */ #include diff --git a/source/blender/blenlib/BLI_timecode.h b/source/blender/blenlib/BLI_timecode.h index 12f4f93f700..5dfd9089598 100644 --- a/source/blender/blenlib/BLI_timecode.h +++ b/source/blender/blenlib/BLI_timecode.h @@ -20,7 +20,7 @@ #pragma once /** \file - * \ingroup BLI + * \ingroup bli */ #include "BLI_compiler_attrs.h" diff --git a/source/blender/blenlib/BLI_timer.h b/source/blender/blenlib/BLI_timer.h index 19275ff5b9a..c219b5502f3 100644 --- a/source/blender/blenlib/BLI_timer.h +++ b/source/blender/blenlib/BLI_timer.h @@ -22,7 +22,7 @@ #include "BLI_sys_types.h" /** \file - * \ingroup BLI + * \ingroup bli */ #ifdef __cplusplus diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c index 69c383061fc..13f95ddf264 100644 --- a/source/blender/blenlib/intern/timecode.c +++ b/source/blender/blenlib/intern/timecode.c @@ -18,7 +18,7 @@ */ /** \file - * \ingroup blendlib + * \ingroup bli * * Time-Code string formatting */ From b534806ecb9847c6929d5ef107a2cb729fb636c9 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 6 Oct 2021 04:43:26 +0200 Subject: [PATCH 0554/1500] VSE: Reduce memory footprint when using thumbnails Free strip `anim` data immediately after rendering. This doesn't affect rendering performance, because each new loop would have to seek to start of strip. Also strips are now freed anyway, but after rendering loop ends. With SF edit file, thumbnail rendering used around 60GB of memory. Now it uses few hundreds MB (depends on movie file resolution, codec, etc.) Freeing of strips caused UI to be unresponsive for brief period. This issue is not removed, but is more spread out so it is less noticable. --- source/blender/editors/space_sequencer/sequencer_thumbnails.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.c b/source/blender/editors/space_sequencer/sequencer_thumbnails.c index f67b40b9674..0ea952d0999 100644 --- a/source/blender/editors/space_sequencer/sequencer_thumbnails.c +++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.c @@ -36,6 +36,7 @@ #include "BIF_glutil.h" +#include "SEQ_relations.h" #include "SEQ_render.h" #include "SEQ_sequencer.h" @@ -176,6 +177,7 @@ static void thumbnail_start_job(void *data, SEQ_render_thumbnails( &tj->context, val->seq_dupli, seq_orig, start_frame, frame_step, tj->view_area, stop); SEQ_render_thumbnails_base_set(&tj->context, val->seq_dupli, seq_orig, tj->view_area, stop); + SEQ_relations_sequence_free_anim(val->seq_dupli); } BLI_ghashIterator_step(&gh_iter); } From bf35dba7fbab568e857b85e9a74ae0f1eb5838f6 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Tue, 5 Oct 2021 23:28:12 -0400 Subject: [PATCH 0555/1500] Nodes: Composite: Fix wrong socket type for color ramp node --- source/blender/nodes/composite/nodes/node_composite_valToRgb.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc index ba98ee12f30..9e4f1329fbd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc @@ -31,7 +31,7 @@ static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b) { b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); b.add_output("Image"); - b.add_output("Alpha"); + b.add_output("Alpha"); } } // namespace blender::nodes From df8f507f411fd71e649e9a896f53c2e574558525 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 14:44:27 +1100 Subject: [PATCH 0556/1500] Cleanup: spelling in comments --- intern/cycles/kernel/geom/geom_shader_data.h | 2 +- intern/cycles/kernel/kernel_shader.h | 4 ++-- intern/cycles/kernel/kernel_types.h | 2 +- intern/ghost/intern/GHOST_SystemPathsUnix.cpp | 4 ++-- intern/ghost/intern/GHOST_SystemX11.cpp | 4 ++-- source/blender/blenfont/intern/blf_internal_types.h | 2 +- source/blender/blenkernel/intern/editmesh_bvh.c | 2 +- source/blender/blenkernel/intern/image.c | 11 +++++------ source/blender/blenkernel/intern/mask_rasterize.c | 2 +- source/blender/blenkernel/intern/vfont.c | 12 ++++++------ .../blender/blenkernel/intern/vfontdata_freetype.c | 2 +- source/blender/blenlib/BLI_array.hh | 2 +- source/blender/editors/interface/interface.c | 2 +- source/blender/editors/object/object_bake_api.c | 2 +- source/blender/editors/render/render_preview.c | 2 +- source/blender/editors/space_view3d/view3d_draw.c | 6 +++--- source/blender/imbuf/intern/jp2.c | 2 +- source/blender/imbuf/intern/oiio/openimageio_api.cpp | 2 +- source/blender/imbuf/intern/png.c | 2 +- source/blender/imbuf/intern/tiff.c | 4 +--- source/blender/io/avi/intern/avi_rgb.c | 2 +- source/blender/makesdna/DNA_gpencil_modifier_types.h | 2 +- .../nodes/shader/nodes/node_shader_valToRgb.cc | 4 ++-- source/blender/python/intern/bpy_rna.c | 2 +- source/blender/render/intern/texture_procedural.c | 2 +- .../simulation/intern/ConstrainedConjugateGradient.h | 4 ++-- 26 files changed, 42 insertions(+), 45 deletions(-) diff --git a/intern/cycles/kernel/geom/geom_shader_data.h b/intern/cycles/kernel/geom/geom_shader_data.h index 0e373c10086..5dc03940238 100644 --- a/intern/cycles/kernel/geom/geom_shader_data.h +++ b/intern/cycles/kernel/geom/geom_shader_data.h @@ -37,7 +37,7 @@ ccl_device void shader_setup_object_transforms(const KernelGlobals *ccl_restrict #endif /* TODO: break this up if it helps reduce register pressure to load data from - * global memory as we write it to shaderdata. */ + * global memory as we write it to shader-data. */ ccl_device_inline void shader_setup_from_ray(const KernelGlobals *ccl_restrict kg, ShaderData *ccl_restrict sd, const Ray *ccl_restrict ray, diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index e7133724c85..a50f3fb214b 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -780,8 +780,8 @@ ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS, break; } - /* setup shaderdata from stack. it's mostly setup already in - * shader_setup_from_volume, this switching should be quick */ + /* Setup shader-data from stack. it's mostly setup already in + * shader_setup_from_volume, this switching should be quick. */ sd->object = entry.object; sd->lamp = LAMP_NONE; sd->shader = entry.shader; diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 1a986c58878..6107e1028ba 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -190,7 +190,7 @@ enum SamplingPattern { SAMPLING_NUM_PATTERNS, }; -/* these flags values correspond to raytypes in osl.cpp, so keep them in sync! */ +/* These flags values correspond to `raytypes` in `osl.cpp`, so keep them in sync! */ enum PathRayFlag { /* -------------------------------------------------------------------- diff --git a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp index b58799e9c2a..8bc2ff9227d 100644 --- a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp +++ b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp @@ -142,7 +142,7 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes } static string path = ""; - /* Pipe stderr to /dev/null to avoid error prints. We will fail gracefully still. */ + /* Pipe `stderr` to `/dev/null` to avoid error prints. We will fail gracefully still. */ string command = string("xdg-user-dir ") + type_str + " 2> /dev/null"; FILE *fstream = popen(command.c_str(), "r"); @@ -152,7 +152,7 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes std::stringstream path_stream; while (!feof(fstream)) { char c = fgetc(fstream); - /* xdg-user-dir ends the path with '\n'. */ + /* `xdg-user-dir` ends the path with '\n'. */ if (c == '\n') { break; } diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 86b4245ca67..ab8039ea95d 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -1044,7 +1044,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) #ifdef USE_NON_LATIN_KB_WORKAROUND /* XXX: Code below is kinda awfully convoluted... Issues are: - * - In keyboards like latin ones, numbers need a 'Shift' to be accessed but key_sym + * - In keyboards like Latin ones, numbers need a 'Shift' to be accessed but key_sym * is unmodified (or anyone swapping the keys with `xmodmap`). * - #XLookupKeysym seems to always use first defined key-map (see T47228), which generates * key-codes unusable by ghost_key_from_keysym for non-Latin-compatible key-maps. @@ -1131,7 +1131,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) } } #else - /* In keyboards like latin ones, + /* In keyboards like Latin ones, * numbers needs a 'Shift' to be accessed but key_sym * is unmodified (or anyone swapping the keys with xmodmap). * diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 38d7d7b6e21..e90f82da7f3 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -152,7 +152,7 @@ typedef struct FontBufInfoBLF { struct ColorManagedDisplay *display; /* and the color, the alphas is get from the glyph! - * color is srgb space */ + * color is sRGB space */ float col_init[4]; /* cached conversion from 'col_init' */ unsigned char col_char[4]; diff --git a/source/blender/blenkernel/intern/editmesh_bvh.c b/source/blender/blenkernel/intern/editmesh_bvh.c index 087481b1b5d..5058863912f 100644 --- a/source/blender/blenkernel/intern/editmesh_bvh.c +++ b/source/blender/blenkernel/intern/editmesh_bvh.c @@ -221,7 +221,7 @@ static void bmbvh_tri_from_face(const float *cos[3], } } -/* taken from bvhutils.c */ +/* Taken from `bvhutils.c`. */ /* -------------------------------------------------------------------- */ /* BKE_bmbvh_ray_cast */ diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index b993d743044..2a0f8a597d1 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -1560,9 +1560,9 @@ bool BKE_imtype_requires_linear_float(const char imtype) char BKE_imtype_valid_channels(const char imtype, bool write_file) { - char chan_flag = IMA_CHAN_FLAG_RGB; /* assume all support rgb */ + char chan_flag = IMA_CHAN_FLAG_RGB; /* Assume all support RGB. */ - /* alpha */ + /* Alpha. */ switch (imtype) { case R_IMF_IMTYPE_BMP: if (write_file) { @@ -1583,7 +1583,7 @@ char BKE_imtype_valid_channels(const char imtype, bool write_file) break; } - /* bw */ + /* BW. */ switch (imtype) { case R_IMF_IMTYPE_BMP: case R_IMF_IMTYPE_PNG: @@ -3078,8 +3078,7 @@ int BKE_imbuf_write_as(ImBuf *ibuf, const char *name, ImageFormatData *imf, cons ImBuf ibuf_back = *ibuf; int ok; - /* all data is rgba anyway, - * this just controls how to save for some formats */ + /* All data is RGBA anyway, this just controls how to save for some formats. */ ibuf->planes = imf->planes; ok = BKE_imbuf_write(ibuf, name, imf); @@ -4611,7 +4610,7 @@ static ImBuf *load_image_single(Image *ima, image_init_after_load(ima, iuser, ibuf); *r_assign = true; - /* make packed file for autopack */ + /* Make packed file for auto-pack. */ if ((has_packed == false) && (G.fileflags & G_FILE_AUTOPACK)) { ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Pack-file"); BLI_addtail(&ima->packedfiles, imapf); diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c index e04e5fceec6..0c40a1b5078 100644 --- a/source/blender/blenkernel/intern/mask_rasterize.c +++ b/source/blender/blenkernel/intern/mask_rasterize.c @@ -151,7 +151,7 @@ BLI_INLINE unsigned int clampis_uint(const unsigned int v, } /* --------------------------------------------------------------------- */ -/* local structs for mask rasterizeing */ +/* local structs for mask rasterizing */ /* --------------------------------------------------------------------- */ /** diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c index 01c232e5a70..43c8a59baad 100644 --- a/source/blender/blenkernel/intern/vfont.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -342,12 +342,12 @@ VFont *BKE_vfont_load(Main *bmain, const char *filepath) vfont->data = vfd; BLI_strncpy(vfont->filepath, filepath, sizeof(vfont->filepath)); - /* if autopack is on store the packedfile in de font structure */ + /* if auto-pack is on store the packed-file in de font structure */ if (!is_builtin && (G.fileflags & G_FILE_AUTOPACK)) { vfont->packedfile = pf; } - /* Do not add FO_BUILTIN_NAME to temporary listbase */ + /* Do not add #FO_BUILTIN_NAME to temporary list-base. */ if (!STREQ(filename, FO_BUILTIN_NAME)) { vfont->temp_pf = BKE_packedfile_new(NULL, filepath, BKE_main_blendfile_path(bmain)); } @@ -694,7 +694,7 @@ struct TempLineInfo { float x_min; /* left margin */ float x_max; /* right margin */ int char_nr; /* number of characters */ - int wspace_nr; /* number of whitespaces of line */ + int wspace_nr; /* number of white-spaces of line */ }; /* -------------------------------------------------------------------- */ @@ -803,7 +803,7 @@ static bool vfont_to_curve(Object *ob, float longest_line_length = 0.0f; /* Text at the beginning of the last used text-box (use for y-axis alignment). - * We overallocate by one to simplify logic of getting last char. */ + * We over-allocate by one to simplify logic of getting last char. */ int *i_textbox_array = MEM_callocN(sizeof(*i_textbox_array) * (cu->totbox + 1), "TextBox initial char index"); @@ -1136,7 +1136,7 @@ static bool vfont_to_curve(Object *ob, } } - /* linedata is now: width of line */ + /* Line-data is now: width of line. */ if (cu->spacemode != CU_ALIGN_X_LEFT) { ct = chartransdata; @@ -1500,7 +1500,7 @@ static bool vfont_to_curve(Object *ob, chartransdata = NULL; } else if (mode == FO_EDIT) { - /* make nurbdata */ + /* Make NURBS-data. */ BKE_nurbList_free(r_nubase); ct = chartransdata; diff --git a/source/blender/blenkernel/intern/vfontdata_freetype.c b/source/blender/blenkernel/intern/vfontdata_freetype.c index caeb016aaa3..bd58d156d06 100644 --- a/source/blender/blenkernel/intern/vfontdata_freetype.c +++ b/source/blender/blenkernel/intern/vfontdata_freetype.c @@ -100,7 +100,7 @@ static VChar *freetypechar_to_vchar(FT_Face face, FT_ULong charcode, VFontData * /* Start converting the FT data */ onpoints = (int *)MEM_callocN((ftoutline.n_contours) * sizeof(int), "onpoints"); - /* get number of on-curve points for beziertriples (including conic virtual on-points) */ + /* Get number of on-curve points for bezier-triples (including conic virtual on-points). */ for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) { const int n = ftoutline.contours[j] - contour_prev; contour_prev = ftoutline.contours[j]; diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index 352bf379d4d..32588b7450d 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -31,7 +31,7 @@ * * A main benefit of using Array over Vector is that it expresses the intent of the developer * better. It indicates that the size of the data structure is not expected to change. Furthermore, - * you can be more certain that an array does not overallocate. + * you can be more certain that an array does not over-allocate. * * blender::Array supports small object optimization to improve performance when the size turns out * to be small at run-time. diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 39ad88c3368..9a294162f34 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -2557,7 +2557,7 @@ double ui_but_value_get(uiBut *but) void ui_but_value_set(uiBut *but, double value) { - /* value is a hsv value: convert to rgb */ + /* Value is a HSV value: convert to RGB. */ if (but->rnaprop) { PropertyRNA *prop = but->rnaprop; diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 475e0e581fb..98f85823bb3 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -697,7 +697,7 @@ static bool bake_targets_init_image_textures(const BakeAPIRender *bkr, } } - /* Overallocate in case there is more materials than images. */ + /* Over-allocate in case there is more materials than images. */ targets->num_materials = num_materials; targets->images = MEM_callocN(sizeof(BakeImage) * targets->num_materials, "BakeTargets.images"); targets->material_to_image = MEM_callocN(sizeof(int) * targets->num_materials, diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 81aecfdf788..6f49b03f07f 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -1343,7 +1343,7 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush) BLI_strncpy(path, brush->icon_filepath, sizeof(brush->icon_filepath)); BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); - /* use default colorspaces for brushes */ + /* Use default color-spaces for brushes. */ brush->icon_imbuf = IMB_loadiffname(path, flags, NULL); /* otherwise lets try to find it in other directories */ diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 1cfd83c503e..733ec7e81cd 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -780,9 +780,9 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, immUniformThemeColorShadeAlpha(TH_VIEW_OVERLAY, 100, 255); /* TODO: Was using: - * UI_draw_roundbox_4fv(false, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 2.0f, color); - * We'll probably need a new imm_draw_line_roundbox_dashed dor that - though in practice the - * 2.0f round corner effect was nearly not visible anyway... */ + * `UI_draw_roundbox_4fv(false, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 2.0f, color);` + * We'll probably need a new imm_draw_line_roundbox_dashed or that - though in practice the + * 2.0f round corner effect was nearly not visible anyway. */ imm_draw_box_wire_2d(shdr_pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax); } diff --git a/source/blender/imbuf/intern/jp2.c b/source/blender/imbuf/intern/jp2.c index 117e0d97b2e..83e93f16f1b 100644 --- a/source/blender/imbuf/intern/jp2.c +++ b/source/blender/imbuf/intern/jp2.c @@ -848,7 +848,7 @@ static opj_image_t *ibuftoimage(ImBuf *ibuf, opj_cparameters_t *parameters) chanel_colormanage_cb = channel_colormanage_noop; } else { - /* standard linear-to-srgb conversion if float buffer wasn't managed */ + /* standard linear-to-SRGB conversion if float buffer wasn't managed */ chanel_colormanage_cb = linearrgb_to_srgb; } diff --git a/source/blender/imbuf/intern/oiio/openimageio_api.cpp b/source/blender/imbuf/intern/oiio/openimageio_api.cpp index 3ad902a241d..22533b04b58 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_api.cpp +++ b/source/blender/imbuf/intern/oiio/openimageio_api.cpp @@ -221,7 +221,7 @@ struct ImBuf *imb_load_photoshop(const char *filename, int flags, char colorspac string ics = spec.get_string_attribute("oiio:ColorSpace"); BLI_strncpy(file_colorspace, ics.c_str(), IM_MAX_SPACE); - /* only use colorspaces exis */ + /* Only use color-spaces exist. */ if (colormanage_colorspace_get_named(file_colorspace)) { strcpy(colorspace, file_colorspace); } diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c index 26f0f11a001..aaf56b1daa0 100644 --- a/source/blender/imbuf/intern/png.c +++ b/source/blender/imbuf/intern/png.c @@ -153,7 +153,7 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags) chanel_colormanage_cb = channel_colormanage_noop; } else { - /* standard linear-to-srgb conversion if float buffer wasn't managed */ + /* Standard linear-to-SRGB conversion if float buffer wasn't managed. */ chanel_colormanage_cb = linearrgb_to_srgb; } diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index 3625d7d1af2..7b68e3f3bea 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -895,9 +895,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) copy_v3_v3(rgb, &fromf[from_i]); } else { - /* Standard linear-to-srgb conversion if float buffer - * wasn't managed. - */ + /* Standard linear-to-SRGB conversion if float buffer wasn't managed. */ linearrgb_to_srgb_v3_v3(rgb, &fromf[from_i]); } if (channels_in_float == 4) { diff --git a/source/blender/io/avi/intern/avi_rgb.c b/source/blender/io/avi/intern/avi_rgb.c index 8af728f0737..f1d83b9857b 100644 --- a/source/blender/io/avi/intern/avi_rgb.c +++ b/source/blender/io/avi/intern/avi_rgb.c @@ -20,7 +20,7 @@ /** \file * \ingroup avi * - * This is external code. Converts rgb-type avi-s. + * This is external code. Converts RGB-type AVI files. */ #include diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index ea5c81761c6..c7a93080f7c 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -260,7 +260,7 @@ typedef struct ColorGpencilModifierData { int pass_index; /** Flags. */ int flag; - /** Hsv factors. */ + /** HSV factors. */ float hsv[3]; /** Modify stroke, fill or both. */ char modify_color; diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index d4d08be5d49..2544ea1921c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -205,8 +205,8 @@ static void node_shader_exec_rgbtobw(void *UNUSED(data), bNodeStack **in, bNodeStack **out) { - /* stack order out: bw */ - /* stack order in: col */ + /* Stack order out: BW. */ + /* Stack order in: COL. */ float col[3]; nodestack_get_vec(col, SOCK_VECTOR, in[0]); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 35acb56e66a..92499d3c0ff 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -7238,7 +7238,7 @@ static PyObject *pyrna_srna_ExternalType(StructRNA *srna) /* Sanity check, could skip this unless in debug mode. */ if (newclass) { PyObject *base_compare = pyrna_srna_PyBase(srna); - /* Can't do this because it gets superclasses values! */ + /* Can't do this because it gets super-classes values! */ // PyObject *slots = PyObject_GetAttrString(newclass, "__slots__"); /* Can do this, but faster not to. */ // PyObject *bases = PyObject_GetAttrString(newclass, "__bases__"); diff --git a/source/blender/render/intern/texture_procedural.c b/source/blender/render/intern/texture_procedural.c index bea9dfbb0ed..5a94bc09b93 100644 --- a/source/blender/render/intern/texture_procedural.c +++ b/source/blender/render/intern/texture_procedural.c @@ -745,7 +745,7 @@ static int mg_distNoiseTex(const Tex *tex, const float texvec[3], TexResult *tex /* ------------------------------------------------------------------------- */ /* newnoise: Voronoi texture type * - * probably the slowest, especially with minkovsky, bumpmapping, could be done another way. + * probably the slowest, especially with minkovsky, bump-mapping, could be done another way. */ static int voronoiTex(const Tex *tex, const float texvec[3], TexResult *texres) diff --git a/source/blender/simulation/intern/ConstrainedConjugateGradient.h b/source/blender/simulation/intern/ConstrainedConjugateGradient.h index c5a2827a09f..c231d511733 100644 --- a/source/blender/simulation/intern/ConstrainedConjugateGradient.h +++ b/source/blender/simulation/intern/ConstrainedConjugateGradient.h @@ -164,13 +164,13 @@ struct traits< * \brief A conjugate gradient solver for sparse self-adjoint problems with additional constraints * * This class allows to solve for A.x = b sparse linear problems using a conjugate gradient - * algorithm. The sparse matrix A must be selfadjoint. The vectors x and b can be either dense or + * algorithm. The sparse matrix A must be self-adjoint. The vectors x and b can be either dense or * sparse. * * \tparam _MatrixType the type of the sparse matrix A, can be a dense or a sparse matrix. * \tparam _UpLo the triangular part that will be used for the computations. It can be Lower * or Upper. Default is Lower. - * \tparam _Preconditioner the type of the preconditioner. Default is DiagonalPreconditioner + * \tparam _Preconditioner the type of the pre-conditioner. Default is #DiagonalPreconditioner * * The maximal number of iterations and tolerance value can be controlled via the * setMaxIterations() and setTolerance() methods. The defaults are the size of the problem for the From 0e590f90784a4c7b2f427ba133c0b0f749d4d6b4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 14:07:20 +1100 Subject: [PATCH 0557/1500] Fix sequencer sample poll function Sequencer poll was succeeding outside of a preview region. This meant it couldn't be used in tool key maps which are currently shared between preview & sequencer regions. --- source/blender/editors/util/ed_util_imbuf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index d57640e16dc..ca21f88b230 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -565,6 +565,10 @@ bool ED_imbuf_sample_poll(bContext *C) if (SEQ_editing_get(CTX_data_scene(C)) == NULL) { return false; } + ARegion *region = CTX_wm_region(C); + if (!(region && (region->regiontype == RGN_TYPE_PREVIEW))) { + return false; + } return true; } } From 9161993e0214c1d4c44db1a26e251d9d4d3008b2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 14:45:27 +1100 Subject: [PATCH 0558/1500] Sequencer: use pass-through for preview select Needed for select-drag action as done in the 3D view and UV editor. --- .../editors/space_sequencer/sequencer_select.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index aa6599a7c53..c7d97ea16a7 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -801,6 +801,16 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) return ret_value; } +static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const int retval = WM_generic_select_invoke(C, op, event); + ARegion *region = CTX_wm_region(C); + if (region && (region->regiontype == RGN_TYPE_PREVIEW)) { + return WM_operator_flag_only_pass_through_on_press(retval, event); + } + return retval; +} + void SEQUENCER_OT_select(wmOperatorType *ot) { PropertyRNA *prop; @@ -812,7 +822,7 @@ void SEQUENCER_OT_select(wmOperatorType *ot) /* Api callbacks. */ ot->exec = sequencer_select_exec; - ot->invoke = WM_generic_select_invoke; + ot->invoke = sequencer_select_invoke; ot->modal = WM_generic_select_modal; ot->poll = ED_operator_sequencer_active; From 8e2a21e8624df8df5ac42e8c5f6d196068d30d73 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 14:45:28 +1100 Subject: [PATCH 0559/1500] Keymap: show sequencer tools in key-map editor Re-order common sequencer key-map to be at the top level (shared by preview and sequence view). Without this sequencer tools would be displayed at different levels in the hierarchy which is confusing and doesn't represent the separation between "Sequencer" and "SequencerPreview" key-maps. --- .../modules/bl_keymap_utils/keymap_hierarchy.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/release/scripts/modules/bl_keymap_utils/keymap_hierarchy.py b/release/scripts/modules/bl_keymap_utils/keymap_hierarchy.py index 0784a91d174..c3bc0f429c1 100644 --- a/release/scripts/modules/bl_keymap_utils/keymap_hierarchy.py +++ b/release/scripts/modules/bl_keymap_utils/keymap_hierarchy.py @@ -168,9 +168,13 @@ _km_hierarchy = [ ('Node Editor', 'NODE_EDITOR', 'WINDOW', [ ('Node Generic', 'NODE_EDITOR', 'WINDOW', []), ]), - ('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW', [ - ('SequencerCommon', 'SEQUENCE_EDITOR', 'WINDOW', []), - ('SequencerPreview', 'SEQUENCE_EDITOR', 'WINDOW', []), + ('SequencerCommon', 'SEQUENCE_EDITOR', 'WINDOW', [ + ('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW', [ + _km_expand_from_toolsystem('SEQUENCE_EDITOR', 'SEQUENCER'), + ]), + ('SequencerPreview', 'SEQUENCE_EDITOR', 'WINDOW', [ + _km_expand_from_toolsystem('SEQUENCE_EDITOR', 'PREVIEW'), + ]), ]), ('File Browser', 'FILE_BROWSER', 'WINDOW', [ From ba95cf6234af1255a794e02f831e0924ddfc1f46 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 14:45:30 +1100 Subject: [PATCH 0560/1500] Keymap: remove selection from the common sequencer map Needed for further changes as selection behaves differently in the preview region. --- .../keyconfig/keymap_data/blender_default.py | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 650ade4d111..c1db5128151 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2685,9 +2685,6 @@ def km_sequencercommon(params): ("wm.context_toggle_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, {"properties": [("data_path", 'space_data.view_type'), ("value_1", 'SEQUENCER'), ("value_2", 'PREVIEW')]}), ("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, None), - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, - {"properties": [("extend", True)]}), ]) if params.select_mouse == 'LEFTMOUSE' and not params.legacy: @@ -2770,6 +2767,11 @@ def km_sequencer(params): for i in range(10) ) ), + *_template_sequencer_select( + type=params.select_mouse, + value=params.select_mouse_value_fallback, + legacy=params.legacy, + ), ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "alt": True}, {"properties": [("linked_handle", True)]}), ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "alt": True}, @@ -2828,6 +2830,12 @@ def km_sequencerpreview(params): ) items.extend([ + # Selection. + *_template_sequencer_select( + type=params.select_mouse, + value=params.select_mouse_value_fallback, + legacy=params.legacy, + ), ("sequencer.view_all_preview", {"type": 'HOME', "value": 'PRESS'}, None), ("sequencer.view_all_preview", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None), ("sequencer.view_ghost_border", {"type": 'O', "value": 'PRESS'}, None), @@ -4637,6 +4645,26 @@ def _template_uv_select_for_fallback(params, fallback): return [] +def _template_sequencer_select(*, type, value, legacy): + # FIXME. + legacy = True + return [( + "sequencer.select", + {"type": type, "value": value, **{m: True for m in mods}}, + {"properties": [(c, True) for c in props]}, + ) for props, mods in ( + (("deselect_all",) if not legacy else (), ()), + (("extend",), ("shift",)), + # TODO: + # (("center", "object"), ("ctrl",)), + # (("enumerate",), ("alt",)), + # (("toggle", "center"), ("shift", "ctrl")), + # (("center", "enumerate"), ("ctrl", "alt")), + # (("toggle", "enumerate"), ("shift", "alt")), + # (("toggle", "center", "enumerate"), ("shift", "ctrl", "alt")), + )] + + def km_image_paint(params): items = [] keymap = ( From 876b2504997e296bc926feb8b530ff1898f35cbc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 14:45:32 +1100 Subject: [PATCH 0561/1500] Keymap: fallback tool support for the sequencer Note that sample is no longer in the sequencer preview keymap, it is still accessible as a tool. This conflicted with click-drag to transform. --- .../keyconfig/keymap_data/blender_default.py | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index c1db5128151..81a8a621594 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2853,7 +2853,7 @@ def km_sequencerpreview(params): {"properties": [("ratio", 0.25)]}), ("sequencer.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS'}, {"properties": [("ratio", 0.125)]}), - ("sequencer.sample", {"type": params.action_mouse, "value": 'PRESS'}, None), + ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), op_tool_optional( ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), (op_tool_cycle, "builtin.move"), params), @@ -4665,6 +4665,17 @@ def _template_sequencer_select(*, type, value, legacy): )] +def _template_sequencer_select_for_fallback(params, fallback): + if (not fallback) and params.use_fallback_tool_rmb: + # Needed so we have immediate select+tweak when the default select tool is active. + return _template_sequencer_select( + type=params.select_mouse, + value=params.select_mouse_value, + legacy=params.legacy, + ) + return [] + + def km_image_paint(params): items = [] keymap = ( @@ -7435,11 +7446,16 @@ def km_3d_view_tool_sculpt_gpencil_select_lasso(params): def km_sequencer_editor_tool_select(params, *, fallback): return ( - # TODO, fall-back tool support. _fallback_id("Sequencer Tool: Select", fallback), {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, None), + # TODO: Use 2D cursor for preview region (currently `sequencer.sample`). + *([] if fallback else + _template_items_tool_select(params, "sequencer.select", "sequencer.sample", extend="extend") + ), + *([] if (not params.use_fallback_tool_rmb) else _template_sequencer_select( + type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), + *_template_items_change_frame(params), ]}, ) @@ -7447,15 +7463,16 @@ def km_sequencer_editor_tool_select(params, *, fallback): def km_sequencer_editor_tool_select_box(params, *, fallback): return ( - # TODO, fall-back tool support. _fallback_id("Sequencer Tool: Select Box", fallback), {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ # Don't use `tool_maybe_tweak_event`, see comment for this slot. - *_template_items_tool_select_actions_simple( - "sequencer.select_box", **params.tool_tweak_event, - properties=[("tweak", params.select_mouse == 'LEFTMOUSE')], - ), + *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( + "sequencer.select_box", + **(params.select_tweak_event if fallback else params.tool_tweak_event), + properties=[("tweak", params.select_mouse == 'LEFTMOUSE')])), + *_template_sequencer_select_for_fallback(params, fallback), + # RMB select can already set the frame, match the tweak tool. *(_template_items_change_frame(params) if params.select_mouse == 'LEFTMOUSE' else []), From c73a550e904086b06b5a02e246609222270a847d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 14:45:33 +1100 Subject: [PATCH 0562/1500] UI: rename sequencer "Select" to "Tweak" This matches the tweak tool elsewhere. Match names since this name is shown prominently in the fall back tool selector. --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 2 +- release/scripts/startup/bl_ui/space_toolsystem_toolbar.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 81a8a621594..c89f858602f 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -7446,7 +7446,7 @@ def km_3d_view_tool_sculpt_gpencil_select_lasso(params): def km_sequencer_editor_tool_select(params, *, fallback): return ( - _fallback_id("Sequencer Tool: Select", fallback), + _fallback_id("Sequencer Tool: Tweak", fallback), {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ # TODO: Use 2D cursor for preview region (currently `sequencer.sample`). diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 5970d6fdf2b..6478cff3597 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2508,10 +2508,10 @@ class _defs_sequencer_select: def select(): return dict( idname="builtin.select", - label="Select", + label="Tweak", icon="ops.generic.select", widget=None, - keymap="Sequencer Tool: Select", + keymap="Sequencer Tool: Tweak", ) @ToolDef.from_fn From ce66075d0027d21044fe46591cd10d211443162a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 14:45:34 +1100 Subject: [PATCH 0563/1500] UI: add sequencer preview context menu This is mostly a place-holder since many items have not yet been implemented. --- .../keyconfig/keymap_data/blender_default.py | 1 + .../scripts/startup/bl_ui/space_sequencer.py | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index c89f858602f..80ec9e0edfe 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2869,6 +2869,7 @@ def km_sequencerpreview(params): {"properties": [("property", 'SCALE')]}), ("sequencer.strip_transform_clear", {"type": 'R', "alt": True, "value": 'PRESS'}, {"properties": [("property", 'ROTATION')]}), + *_template_items_context_menu("SEQUENCER_MT_preview_context_menu", params.context_menu_event), ]) return keymap diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index b5904422beb..dec754b8747 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1021,6 +1021,27 @@ class SEQUENCER_MT_context_menu(Menu): layout.menu("SEQUENCER_MT_strip_lock_mute") +class SEQUENCER_MT_preview_context_menu(Menu): + bl_label = "Sequencer Preview Context Menu" + + def draw(self, context): + layout = self.layout + + layout.operator_context = 'INVOKE_REGION_WIN' + + props = layout.operator("wm.call_panel", text="Rename...") + props.name = "TOPBAR_PT_name" + props.keep_open = False + + # TODO: support in preview. + # layout.operator("sequencer.delete", text="Delete") + + strip = context.active_sequence_strip + + if strip: + pass + + class SequencerButtonsPanel: bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'UI' @@ -2431,6 +2452,7 @@ classes = ( SEQUENCER_MT_strip_lock_mute, SEQUENCER_MT_color_tag_picker, SEQUENCER_MT_context_menu, + SEQUENCER_MT_preview_context_menu, SEQUENCER_PT_color_tag_picker, From 82f0e4948c36a4d69d0c3dc3e14d9b4376e0be37 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 6 Oct 2021 00:00:00 -0400 Subject: [PATCH 0564/1500] UI: Boolean rename "Self" to "Self Intersection" Better to be more explicit here, also this matches the recent Boolean Node. --- source/blender/editors/mesh/editmesh_intersect.c | 2 +- source/blender/makesrna/intern/rna_modifier.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index f2691580a9d..5dfa804ad85 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -480,7 +480,7 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot) false, "Swap", "Use with difference intersection to swap which side is kept"); - RNA_def_boolean(ot->srna, "use_self", false, "Self", "Do self-union or self-intersection"); + RNA_def_boolean(ot->srna, "use_self", false, "Self Intersection", "Do self-union or self-intersection"); RNA_def_float_distance( ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge Threshold", "", 0.0, 0.001); RNA_def_enum(ot->srna, diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index c99dfea524f..028b6b6e11f 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -2746,7 +2746,7 @@ static void rna_def_modifier_boolean(BlenderRNA *brna) prop = RNA_def_property(srna, "use_self", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", eBooleanModifierFlag_Self); - RNA_def_property_ui_text(prop, "Self", "Allow self-intersection in operands"); + RNA_def_property_ui_text(prop, "Self Intersection", "Allow self-intersection in operands"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "use_hole_tolerant", PROP_BOOLEAN, PROP_NONE); From 68dc970219ef7a559b48bd1b3e45d033367b4172 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 17:27:52 +1100 Subject: [PATCH 0565/1500] Sequencer: improvements to preview selection - Support toggle/deselect/deselect_all options (matching 3D viewport object selection). - Support legacy selection behavior. - Support selecting by the center in preview views (holding Ctrl). --- .../keyconfig/keymap_data/blender_default.py | 60 +++++--- .../space_sequencer/sequencer_select.c | 128 +++++++++++++++--- 2 files changed, 144 insertions(+), 44 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 80ec9e0edfe..265db782eff 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2767,21 +2767,11 @@ def km_sequencer(params): for i in range(10) ) ), - *_template_sequencer_select( + *_template_sequencer_timeline_select( type=params.select_mouse, value=params.select_mouse_value_fallback, legacy=params.legacy, ), - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "alt": True}, - {"properties": [("linked_handle", True)]}), - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "alt": True}, - {"properties": [("extend", True), ("linked_handle", True)]}), - ("sequencer.select", - {"type": params.select_mouse, "value": 'PRESS' if params.legacy else 'CLICK', "ctrl": True}, - {"properties": [("side_of_frame", True), ("linked_time", True)]}), - ("sequencer.select", - {"type": params.select_mouse, "value": 'PRESS' if params.legacy else 'CLICK', "ctrl": True, "shift": True}, - {"properties": [("side_of_frame", True), ("linked_time", True), ("extend", True)]}), ("sequencer.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("sequencer.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("sequencer.select_linked_pick", {"type": 'L', "value": 'PRESS'}, None), @@ -2831,7 +2821,7 @@ def km_sequencerpreview(params): items.extend([ # Selection. - *_template_sequencer_select( + *_template_sequencer_preview_select( type=params.select_mouse, value=params.select_mouse_value_fallback, legacy=params.legacy, @@ -4646,32 +4636,58 @@ def _template_uv_select_for_fallback(params, fallback): return [] -def _template_sequencer_select(*, type, value, legacy): - # FIXME. - legacy = True +def _template_sequencer_generic_select(*, type, value, legacy): return [( "sequencer.select", {"type": type, "value": value, **{m: True for m in mods}}, {"properties": [(c, True) for c in props]}, ) for props, mods in ( (("deselect_all",) if not legacy else (), ()), - (("extend",), ("shift",)), + (("toggle",), ("shift",)), + )] + + +def _template_sequencer_timeline_select(*, type, value, legacy): + return _template_sequencer_generic_select( + type=type, value=value, legacy=legacy, + ) + [( + "sequencer.select", + {"type": type, "value": value, **{m: True for m in mods}}, + {"properties": [(c, True) for c in props]}, + ) for props, mods in ( + (("center",), ("ctrl",)), # TODO: - # (("center", "object"), ("ctrl",)), # (("enumerate",), ("alt",)), - # (("toggle", "center"), ("shift", "ctrl")), + (("toggle", "center"), ("shift", "ctrl")), # (("center", "enumerate"), ("ctrl", "alt")), # (("toggle", "enumerate"), ("shift", "alt")), # (("toggle", "center", "enumerate"), ("shift", "ctrl", "alt")), )] +def _template_sequencer_preview_select(*, type, value, legacy): + return _template_sequencer_generic_select( + type=type, value=value, legacy=legacy, + ) + [( + "sequencer.select", + {"type": type, "value": value, **{m: True for m in mods}}, + {"properties": [(c, True) for c in props]}, + ) for props, mods in ( + (("linked_handle",), ("alt",)), + (("linked_handle", "extend"), ("shift", "alt",)), + + (("side_of_frame", "linked_time"), ("ctrl",)), + (("side_of_frame", "linked_time", "extend"), ("ctrl", "shift")), + )] + + def _template_sequencer_select_for_fallback(params, fallback): if (not fallback) and params.use_fallback_tool_rmb: # Needed so we have immediate select+tweak when the default select tool is active. - return _template_sequencer_select( + return _template_sequencer_generic_select( type=params.select_mouse, value=params.select_mouse_value, + preview=False, legacy=params.legacy, ) return [] @@ -7452,10 +7468,10 @@ def km_sequencer_editor_tool_select(params, *, fallback): {"items": [ # TODO: Use 2D cursor for preview region (currently `sequencer.sample`). *([] if fallback else - _template_items_tool_select(params, "sequencer.select", "sequencer.sample", extend="extend") + _template_items_tool_select(params, "sequencer.select", "sequencer.sample", extend="toggle") ), - *([] if (not params.use_fallback_tool_rmb) else _template_sequencer_select( - type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), + *([] if (not params.use_fallback_tool_rmb) else _template_sequencer_generic_select( + type=params.select_mouse, value=params.select_mouse_value, preview=False, legacy=params.legacy)), *_template_items_change_frame(params), ]}, diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index c7d97ea16a7..5b5b5e002f6 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -637,7 +637,9 @@ static void sequencer_select_linked_handle(const bContext *C, /* Check if click happened on image which belongs to strip. If multiple strips are found, loop * through them in order. */ -static Sequence *seq_select_seq_from_preview(const bContext *C, const int mval[2]) +static Sequence *seq_select_seq_from_preview(const bContext *C, + const int mval[2], + const bool center) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); @@ -649,10 +651,53 @@ static Sequence *seq_select_seq_from_preview(const bContext *C, const int mval[2 UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseco_view[0], &mouseco_view[1]); SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown); + + /* Allow strips this far from the closest center to be included. + * This allows cycling over center points which are near enough + * to overlapping from the users perspective. */ + const float center_threshold_cycle_px = 5.0f; + const float center_dist_sq_eps = square_f(center_threshold_cycle_px * U.pixelsize); + const float center_scale_px[2] = { + UI_view2d_scale_get_x(v2d), + UI_view2d_scale_get_y(v2d), + }; + float center_co_best[2] = {0.0f}; + + if (center) { + Sequence *seq_best = NULL; + float center_dist_sq_best = 0.0f; + + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + float co[2]; + SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co); + sub_v2_v2(co, mouseco_view); + const float center_dist_sq_test = len_squared_v2(co); + if ((seq_best == NULL) || (center_dist_sq_test < center_dist_sq_best)) { + seq_best = seq; + center_dist_sq_best = center_dist_sq_test; + copy_v2_v2(center_co_best, co); + } + } + } + ListBase strips_ordered = {NULL}; Sequence *seq; SEQ_ITERATOR_FOREACH (seq, strips) { - if (seq_point_image_isect(scene, seq, mouseco_view)) { + bool isect = false; + if (center) { + /* Detect overlapping center points (scaled by the zoom level). */ + float co[2]; + SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co); + sub_v2_v2(co, center_co_best); + mul_v2_v2(co, center_scale_px); + isect = len_squared_v2(co) <= center_dist_sq_eps; + } + else { + isect = seq_point_image_isect(scene, seq, mouseco_view); + } + + if (isect) { BLI_remlink(seqbase, seq); BLI_addtail(&strips_ordered, seq); } @@ -686,14 +731,15 @@ static bool element_already_selected(const Sequence *seq, const int handle_click static void sequencer_select_strip_impl(const Editing *ed, Sequence *seq, const int handle_clicked, - const bool extend) + const bool extend, + const bool deselect, + const bool toggle) { - /* Deselect strip. */ - if (extend && (seq->flag & SELECT) && ed->act_seq == seq) { + const bool is_active = (ed->act_seq == seq); + + /* Exception for active strip handles. */ + if ((handle_clicked != SEQ_SIDE_NONE) && (seq->flag & SELECT) && is_active && toggle) { switch (handle_clicked) { - case SEQ_SIDE_NONE: - seq->flag &= ~SEQ_ALLSEL; - break; case SEQ_SIDE_LEFT: seq->flag ^= SEQ_LEFTSEL; break; @@ -701,8 +747,28 @@ static void sequencer_select_strip_impl(const Editing *ed, seq->flag ^= SEQ_RIGHTSEL; break; } + return; } - else { /* Select strip. */ + + /* Select strip. */ + /* Match object selection behavior. */ + int action = -1; + if (extend) { + action = 1; + } + else if (deselect) { + action = 0; + } + else { + if ((seq->flag & SELECT) == 0 || is_active) { + action = 1; + } + else if (toggle) { + action = 0; + } + } + + if (action == 1) { seq->flag |= SELECT; if (handle_clicked == SEQ_SIDE_LEFT) { seq->flag |= SEQ_LEFTSEL; @@ -711,6 +777,9 @@ static void sequencer_select_strip_impl(const Editing *ed, seq->flag |= SEQ_RIGHTSEL; } } + else if (action == 0) { + seq->flag &= ~SEQ_ALLSEL; + } } static int sequencer_select_exec(bContext *C, wmOperator *op) @@ -718,12 +787,17 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) View2D *v2d = UI_view2d_fromcontext(C); Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); - const bool extend = RNA_boolean_get(op->ptr, "extend"); if (ed == NULL) { return OPERATOR_CANCELLED; } + bool extend = RNA_boolean_get(op->ptr, "extend"); + bool deselect = RNA_boolean_get(op->ptr, "deselect"); + bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + bool center = RNA_boolean_get(op->ptr, "center"); + int mval[2]; mval[0] = RNA_int_get(op->ptr, "mouse_x"); mval[1] = RNA_int_get(op->ptr, "mouse_y"); @@ -732,7 +806,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) int handle_clicked = SEQ_SIDE_NONE; Sequence *seq = NULL; if (region->regiontype == RGN_TYPE_PREVIEW) { - seq = seq_select_seq_from_preview(C, mval); + seq = seq_select_seq_from_preview(C, mval, center); } else { seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); @@ -744,7 +818,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) if (!extend) { ED_sequencer_deselect_all(scene); } - sequencer_select_strip_impl(ed, seq, handle_clicked, extend); + sequencer_select_strip_impl(ed, seq, handle_clicked, extend, deselect, toggle); select_linked_time(ed->seqbasep, seq); sequencer_select_do_updates(C, scene); sequencer_select_set_active(scene, seq); @@ -776,29 +850,32 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* Clicking on already selected element falls on modal operation. * All strips are deselected on mouse button release unless extend mode is used. */ - if (seq && element_already_selected(seq, handle_clicked) && wait_to_deselect_others && !extend) { + if (seq && element_already_selected(seq, handle_clicked) && wait_to_deselect_others && !toggle) { return OPERATOR_RUNNING_MODAL; } - int ret_value = OPERATOR_CANCELLED; + bool changed = false; - if (!extend) { + /* Deselect everything */ + if (deselect_all || (seq && ((extend == false && deselect == false && toggle == false)))) { ED_sequencer_deselect_all(scene); - ret_value = OPERATOR_FINISHED; + changed = true; } /* Nothing to select, but strips could be deselected. */ if (!seq) { - sequencer_select_do_updates(C, scene); - return ret_value; + if (changed) { + sequencer_select_do_updates(C, scene); + } + return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } /* Do actual selection. */ - sequencer_select_strip_impl(ed, seq, handle_clicked, extend); - ret_value = OPERATOR_FINISHED; + sequencer_select_strip_impl(ed, seq, handle_clicked, extend, deselect, toggle); + sequencer_select_do_updates(C, scene); sequencer_select_set_active(scene, seq); - return ret_value; + return OPERATOR_FINISHED; } static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -832,7 +909,14 @@ void SEQUENCER_OT_select(wmOperatorType *ot) /* Properties. */ WM_operator_properties_generic_select(ot); - prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); + WM_operator_properties_mouse_select(ot); + + prop = RNA_def_boolean( + ot->srna, + "center", + 0, + "Center", + "Use the object center when selecting, in edit mode used to extend object selection"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, From 85267ec1ae9edba1167b2f6248fbf7307ee73544 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 18:12:01 +1100 Subject: [PATCH 0566/1500] Correct error in 68dc970219ef7a559b48bd1b3e45d033367b4172 Included invalid keyword argument. --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 265db782eff..1ab0e25b59d 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4687,7 +4687,6 @@ def _template_sequencer_select_for_fallback(params, fallback): return _template_sequencer_generic_select( type=params.select_mouse, value=params.select_mouse_value, - preview=False, legacy=params.legacy, ) return [] @@ -7471,7 +7470,7 @@ def km_sequencer_editor_tool_select(params, *, fallback): _template_items_tool_select(params, "sequencer.select", "sequencer.sample", extend="toggle") ), *([] if (not params.use_fallback_tool_rmb) else _template_sequencer_generic_select( - type=params.select_mouse, value=params.select_mouse_value, preview=False, legacy=params.legacy)), + type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), *_template_items_change_frame(params), ]}, From ca0450feeffdb07eea11c17aa617bfd7b9a04913 Mon Sep 17 00:00:00 2001 From: Mikhail Matrosov Date: Wed, 6 Oct 2021 10:16:56 +0200 Subject: [PATCH 0567/1500] Fix T91064: Cycles low poly meshes having black edges when shade smoothed Fixes:{T91064} Caused by {rBcd118c5581f482afc8554ff88b5b6f3b552b1682} - Applies `ensure_valid_reflection()` to the normal input on all BSDFs for CPU and GPU. - This doesn't affect hair. - Removes `ensure_valid_reflection()` from the output of Bump Map and Normal Map nodes for CPU/GPU as it is not needed. - The fix doesn't touch OSL. Reviewed By: brecht, leesonw Maniphest Tasks: T91064 Differential Revision: https://developer.blender.org/D12403 --- .../kernel/closure/bsdf_principled_diffuse.h | 25 ++++++++++--------- intern/cycles/kernel/svm/svm_closure.h | 6 +++++ intern/cycles/kernel/svm/svm_tex_coord.h | 2 -- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index a72af519482..55fe364db70 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -35,21 +35,25 @@ static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledDiffuseBsdf), "PrincipledDiffuseBsdf is too large!"); ccl_device float3 calculate_principled_diffuse_brdf( - const PrincipledDiffuseBsdf *bsdf, float3 N, float3 V, float3 L, float3 H, float *pdf) + const PrincipledDiffuseBsdf *bsdf, float3 N, float3 V, float3 L, float *pdf) { float NdotL = dot(N, L); - float NdotV = dot(N, V); - if (NdotL <= 0 || NdotV <= 0) { - *pdf = 0.0f; + if (NdotL <= 0) { return make_float3(0.0f, 0.0f, 0.0f); } - float LdotH = dot(L, H); + float NdotV = dot(N, V); + + /* H = normalize(L + V); // Bissector of an angle between L and V + * LH2 = 2 * dot(L, H)^2 = 2cos(x)^2 = cos(2x) + 1 = dot(L, V) + 1, + * half-angle x between L and V is at most 90 deg + */ + float LH2 = dot(L, V) + 1; float FL = schlick_fresnel(NdotL), FV = schlick_fresnel(NdotV); - const float Fd90 = 0.5f + 2.0f * LdotH * LdotH * bsdf->roughness; - float Fd = (1.0f * (1.0f - FL) + Fd90 * FL) * (1.0f * (1.0f - FV) + Fd90 * FV); + const float Fd90 = 0.5f + LH2 * bsdf->roughness; + float Fd = (1.0f - FL + Fd90 * FL) * (1.0f - FV + Fd90 * FV); float value = M_1_PI_F * NdotL * Fd; @@ -72,11 +76,10 @@ ccl_device float3 bsdf_principled_diffuse_eval_reflect(const ShaderClosure *sc, float3 N = bsdf->N; float3 V = I; // outgoing float3 L = omega_in; // incoming - float3 H = normalize(L + V); if (dot(N, omega_in) > 0.0f) { *pdf = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F; - return calculate_principled_diffuse_brdf(bsdf, N, V, L, H, pdf); + return calculate_principled_diffuse_brdf(bsdf, N, V, L, pdf); } else { *pdf = 0.0f; @@ -112,9 +115,7 @@ ccl_device int bsdf_principled_diffuse_sample(const ShaderClosure *sc, sample_cos_hemisphere(N, randu, randv, omega_in, pdf); if (dot(Ng, *omega_in) > 0) { - float3 H = normalize(I + *omega_in); - - *eval = calculate_principled_diffuse_brdf(bsdf, N, I, *omega_in, H, pdf); + *eval = calculate_principled_diffuse_brdf(bsdf, N, I, *omega_in, pdf); #ifdef __RAY_DIFFERENTIALS__ // TODO: find a better approximation for the diffuse bounce diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h index e2f6dde4ace..3e0cbe3a483 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/svm_closure.h @@ -85,6 +85,9 @@ ccl_device_noinline int svm_node_closure_bsdf( } float3 N = stack_valid(data_node.x) ? stack_load_float3(stack, data_node.x) : sd->N; + if (!(sd->type & PRIMITIVE_ALL_CURVE)) { + N = ensure_valid_reflection(sd->Ng, sd->I, N); + } float param1 = (stack_valid(param1_offset)) ? stack_load_float(stack, param1_offset) : __uint_as_float(node.z); @@ -166,6 +169,9 @@ ccl_device_noinline int svm_node_closure_bsdf( float3 clearcoat_normal = stack_valid(data_cn_ssr.x) ? stack_load_float3(stack, data_cn_ssr.x) : sd->N; + if (!(sd->type & PRIMITIVE_ALL_CURVE)) { + clearcoat_normal = ensure_valid_reflection(sd->Ng, sd->I, clearcoat_normal); + } float3 subsurface_radius = stack_valid(data_cn_ssr.y) ? stack_load_float3(stack, data_cn_ssr.y) : make_float3(1.0f, 1.0f, 1.0f); diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index a35253080da..8869001015b 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -347,8 +347,6 @@ ccl_device_noinline void svm_node_normal_map(const KernelGlobals *kg, N = safe_normalize(sd->N + (N - sd->N) * strength); } - N = ensure_valid_reflection(sd->Ng, sd->I, N); - if (is_zero(N)) { N = sd->N; } From b5ea3d2c09c5df49ed783dabdc83820c55effdd2 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 6 Oct 2021 11:20:15 +0200 Subject: [PATCH 0568/1500] Fix possible use-after-free when cancelling temporary rename button If a renaming button was removed via `UI_but_active_only_ex()` and that button was placed using the layout system, the button was still in the layout. So far this didn't cause issues, because all cases where the button may be removed were not using the layout system. --- source/blender/editors/interface/interface.c | 3 ++ .../editors/interface/interface_intern.h | 1 + .../editors/interface/interface_layout.c | 42 +++++++++++++++---- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 9a294162f34..88b23e07f54 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1014,6 +1014,9 @@ bool UI_but_active_only_ex( else if ((found == true) && (isactive == false)) { if (remove_on_failure) { BLI_remlink(&block->buttons, but); + if (but->layout) { + ui_layout_remove_but(but->layout, but); + } ui_but_free(C, but); } return false; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 8b45d9faae6..8e69ac40a34 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1107,6 +1107,7 @@ void ui_resources_free(void); /* interface_layout.c */ void ui_layout_add_but(uiLayout *layout, uiBut *but); +void ui_layout_remove_but(uiLayout *layout, const uiBut *but); bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but); uiBut *ui_but_add_search(uiBut *but, PointerRNA *ptr, diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 64c16e57f56..e54b261facd 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -5605,28 +5605,52 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but) ui_button_group_add_but(uiLayoutGetBlock(layout), but); } -bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but) +static uiButtonItem *ui_layout_find_button_item(const uiLayout *layout, const uiBut *but) { - ListBase *child_list = layout->child_items_layout ? &layout->child_items_layout->items : - &layout->items; + const ListBase *child_list = layout->child_items_layout ? &layout->child_items_layout->items : + &layout->items; LISTBASE_FOREACH (uiItem *, item, child_list) { if (item->type == ITEM_BUTTON) { uiButtonItem *bitem = (uiButtonItem *)item; - if (bitem->but == old_but_ptr) { - bitem->but = new_but; - return true; + if (bitem->but == but) { + return bitem; } } else { - if (ui_layout_replace_but_ptr((uiLayout *)item, old_but_ptr, new_but)) { - return true; + uiButtonItem *nested_item = ui_layout_find_button_item((uiLayout *)item, but); + if (nested_item) { + return nested_item; } } } - return false; + return NULL; +} + +void ui_layout_remove_but(uiLayout *layout, const uiBut *but) +{ + uiButtonItem *bitem = ui_layout_find_button_item(layout, but); + if (!bitem) { + return; + } + + BLI_freelinkN(&layout->items, bitem); +} + +/** + * \return true if the button was successfully replaced. + */ +bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but) +{ + uiButtonItem *bitem = ui_layout_find_button_item(layout, old_but_ptr); + if (!bitem) { + return false; + } + + bitem->but = new_but; + return true; } void uiLayoutSetFixedSize(uiLayout *layout, bool fixed_size) From 9ed19db5392d76bc810afabeb93fcd34a23fd88d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 6 Oct 2021 11:38:11 +0200 Subject: [PATCH 0569/1500] Fix handling of overrides during append. Liboverride references need a special handling during append, since those pointers should never be made local, nor reampped to newly localized data. And liboverride references should never be directly made local either, to ensure their liboverride usages remain pointing to linked data and not local one. Issue was reported by the studio, and also probably as part of T91892. --- source/blender/blenkernel/intern/lib_remap.c | 8 ++++++-- .../blender/windowmanager/intern/wm_files_link.c | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 48396c5e6d9..b5c45c0902b 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -719,7 +719,7 @@ void BKE_libblock_relink_to_newid(ID *id) static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) { const int cb_flag = cb_data->cb_flag; - if (cb_flag & IDWALK_CB_EMBEDDED) { + if (cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { return IDWALK_RET_NOP; } @@ -730,7 +730,11 @@ static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) if (id) { /* See: NEW_ID macro */ if (id->newid != NULL) { - BKE_libblock_relink_ex(bmain, id_owner, id, id->newid, ID_REMAP_SKIP_INDIRECT_USAGE); + BKE_libblock_relink_ex(bmain, + id_owner, + id, + id->newid, + ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); id = id->newid; } if (id->tag & LIB_TAG_NEW) { diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index cf3536213d2..12ab10e3a70 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -595,7 +595,10 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) { - if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK)) { + /* NOTE: It is important to also skip liboverride references here, as those should never be made + * local. */ + if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | + IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { return IDWALK_RET_NOP; } @@ -652,7 +655,8 @@ static void wm_append_do(WMLinkAppendData *lapp_data, LinkNode *itemlink; - /* Generate a mapping between newly linked IDs and their items. */ + /* Generate a mapping between newly linked IDs and their items, and tag linked IDs used as + * liboverride references as already existing. */ lapp_data->new_id_to_item = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { WMLinkAppendDataItem *item = itemlink->link; @@ -661,6 +665,13 @@ static void wm_append_do(WMLinkAppendData *lapp_data, continue; } BLI_ghash_insert(lapp_data->new_id_to_item, id, item); + + /* This ensures that if a liboverride reference is also linked/used by some other appended + * data, it gets a local copy instead of being made directly local, so that the liboverride + * references remain valid (i.e. linked data). */ + if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + id->override_library->reference->tag |= LIB_TAG_PRE_EXISTING; + } } lapp_data->library_weak_reference_mapping = BKE_main_library_weak_reference_create(bmain); @@ -704,7 +715,6 @@ static void wm_append_do(WMLinkAppendData *lapp_data, item->append_action = WM_APPEND_ACT_COPY_LOCAL; } else { - /* In future we could search for already existing matching local ID etc. */ CLOG_INFO(&LOG, 3, "Appended ID '%s' will be made local...", id->name); item->append_action = WM_APPEND_ACT_MAKE_LOCAL; } From 4ab8212e1a0fbeff0ab7e33a37b6f2f97182566b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 21:38:42 +1100 Subject: [PATCH 0570/1500] Fix errors in 68dc970219ef7a559b48bd1b3e45d033367b4172 Swapped preview/timeline keymap and incorrect center measurement. --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 4 ++-- source/blender/editors/space_sequencer/sequencer_select.c | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 1ab0e25b59d..b8b27b0e8ba 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4647,7 +4647,7 @@ def _template_sequencer_generic_select(*, type, value, legacy): )] -def _template_sequencer_timeline_select(*, type, value, legacy): +def _template_sequencer_preview_select(*, type, value, legacy): return _template_sequencer_generic_select( type=type, value=value, legacy=legacy, ) + [( @@ -4665,7 +4665,7 @@ def _template_sequencer_timeline_select(*, type, value, legacy): )] -def _template_sequencer_preview_select(*, type, value, legacy): +def _template_sequencer_timeline_select(*, type, value, legacy): return _template_sequencer_generic_select( type=type, value=value, legacy=legacy, ) + [( diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 5b5b5e002f6..7722909190f 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -671,8 +671,7 @@ static Sequence *seq_select_seq_from_preview(const bContext *C, SEQ_ITERATOR_FOREACH (seq, strips) { float co[2]; SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co); - sub_v2_v2(co, mouseco_view); - const float center_dist_sq_test = len_squared_v2(co); + const float center_dist_sq_test = len_squared_v2v2(co, mouseco_view); if ((seq_best == NULL) || (center_dist_sq_test < center_dist_sq_best)) { seq_best = seq; center_dist_sq_best = center_dist_sq_test; From ac9ec52e9e26544f15ed1f8596d46b82f2577a61 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 6 Oct 2021 11:42:47 +0200 Subject: [PATCH 0571/1500] UI: Draw tree-views (e.g. asset catalogs) in a box Makes things look more appealing visually. Plus it's a way to visually group the tree rows together, which can be important if there are more widgets surrounding the tree. --- source/blender/editors/interface/tree_view.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 28c757ddc79..8bd2be7dc77 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -80,7 +80,8 @@ void AbstractTreeView::build_layout_from_tree(const TreeViewLayoutBuilder &build { uiLayout *prev_layout = builder.current_layout(); - uiLayoutColumn(prev_layout, true); + uiLayout *box = uiLayoutBox(prev_layout); + uiLayoutColumn(box, true); foreach_item([&builder](AbstractTreeViewItem &item) { builder.build_row(item); }, IterOptions::SkipCollapsed); From 3c4537cd38f715485826d5db040afe776c958861 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 6 Oct 2021 13:06:20 +0200 Subject: [PATCH 0572/1500] Cleanup: Improve readability & comments in UI tree-view header --- .../blender/editors/include/UI_tree_view.hh | 117 ++++++++++++------ 1 file changed, 80 insertions(+), 37 deletions(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 46eaf56a3c0..dbafd1b3a2b 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -16,6 +16,9 @@ /** \file * \ingroup editorui + * + * API for simple creation of tree UIs supporting advanced features. + * https://wiki.blender.org/wiki/Source/Interface/Views */ #pragma once @@ -78,18 +81,23 @@ class TreeViewItemContainer { using ItemIterFn = FunctionRef; /** - * Convenience wrapper taking the arguments needed to construct an item of type \a ItemT. Calls - * the version just below. + * Convenience wrapper constructing the item by forwarding given arguments to the constructor of + * the type (\a ItemT). + * + * E.g. if your tree-item type has the following constructor: + * \code{.cpp} + * MyTreeItem(std::string str, int i); + * \endcode + * You can add an item like this: + * \code + * add_tree_item("blabla", 42); + * \endcode + */ + template inline ItemT &add_tree_item(Args &&...args); + /** + * Add an already constructed tree item to this parent. Ownership is moved to it. + * All tree items must be added through this, it handles important invariants! */ - template ItemT &add_tree_item(Args &&...args) - { - static_assert(std::is_base_of::value, - "Type must derive from and implement the AbstractTreeViewItem interface"); - - return dynamic_cast( - add_tree_item(std::make_unique(std::forward(args)...))); - } - AbstractTreeViewItem &add_tree_item(std::unique_ptr item); protected: @@ -146,25 +154,31 @@ class AbstractTreeView : public TreeViewItemContainer { void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; - /** Check if the tree is fully (re-)constructed. That means, both #build_tree() and - * #update_from_old() have finished. */ + /** + * Check if the tree is fully (re-)constructed. That means, both #build_tree() and + * #update_from_old() have finished. + */ bool is_reconstructed() const; protected: virtual void build_tree() = 0; private: - /** Match the tree-view against an earlier version of itself (if any) and copy the old UI state - * (e.g. collapsed, active, selected) to the new one. See - * #AbstractTreeViewItem.update_from_old(). */ + /** + * Match the tree-view against an earlier version of itself (if any) and copy the old UI state + * (e.g. collapsed, active, selected, renaming, etc.) to the new one. See + * #AbstractTreeViewItem.update_from_old(). + */ void update_from_old(uiBlock &new_block); static void update_children_from_old_recursive(const TreeViewItemContainer &new_items, const TreeViewItemContainer &old_items); static AbstractTreeViewItem *find_matching_child(const AbstractTreeViewItem &lookup_item, const TreeViewItemContainer &items); - /** Items may want to do additional work when state changes. But these state changes can only be - * reliably detected after the tree was reconstructed (see #is_reconstructed()). So this is done - * delayed. */ + /** + * Items may want to do additional work when state changes. But these state changes can only be + * reliably detected after the tree has completed reconstruction (see #is_reconstructed()). So + * the actual state changes are done in a delayed manner through this function. + */ void change_state_delayed(); void build_layout_from_tree(const TreeViewLayoutBuilder &builder); }; @@ -204,48 +218,63 @@ class AbstractTreeViewItem : public TreeViewItemContainer { virtual void build_row(uiLayout &row) = 0; virtual void on_activate(); + /** + * Set a custom callback to check if this item should be active. There's a version without + * arguments for checking if the item is currently in an active state. + */ virtual void is_active(IsActiveFn is_active_fn); virtual bool on_drop(const wmDrag &drag); virtual bool can_drop(const wmDrag &drag) const; - /** Custom text to display when dragging over a tree item. Should explain what happens when + /** + * Custom text to display when dragging over a tree item. Should explain what happens when * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() * returns true, so the implementing override doesn't have to check that again. - * The returned value must be a translated string. */ + * The returned value must be a translated string. + */ virtual std::string drop_tooltip(const bContext &C, const wmDrag &drag, const wmEvent &event) const; - /** Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of + /** + * Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of * the last redraw to this item. If sub-classes introduce more advanced state they should - * override this and make it update their state accordingly. */ + * override this and make it update their state accordingly. + */ virtual void update_from_old(const AbstractTreeViewItem &old); - /** Compare this item to \a other to check if they represent the same data. This is critical for - * being able to recognize an item from a previous redraw, to be able to keep its state (e.g. + /** + * Compare this item to \a other to check if they represent the same data. + * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. * open/closed, active, etc.). Items are only matched if their parents also match. - * By default this just matches the items names/labels (if their parents match!). If that isn't - * good enough for a sub-class, that can override it. */ + * By default this just matches the item's label (if the parents match!). If that isn't + * good enough for a sub-class, that can override it. + */ virtual bool matches(const AbstractTreeViewItem &other) const; const AbstractTreeView &get_tree_view() const; int count_parents() const; void deactivate(); - /** Must not be called before the tree was reconstructed (see #is_reconstructed()). Otherwise we - * can't be sure about the item state. */ + /** + * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we + * can't be sure about the item state. + */ bool is_active() const; void toggle_collapsed(); - /** Must not be called before the tree was reconstructed (see #is_reconstructed()). Otherwise we - * can't be sure about the item state. */ + /** + * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we + * can't be sure about the item state. + */ bool is_collapsed() const; void set_collapsed(bool collapsed); bool is_collapsible() const; void ensure_parents_uncollapsed(); protected: - /** Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate() + /** + * Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate() * function and ensures this item's parents are not collapsed (so the item is visible). - * Must not be called before the tree was reconstructed (see #is_reconstructed()). Otherwise we - * can't be sure about the current item state and may call state-change update functions - * incorrectly. */ + * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise the + * actual item state is unknown, possibly calling state-change update functions incorrectly. + */ void activate(); private: @@ -277,9 +306,11 @@ class BasicTreeViewItem : public AbstractTreeViewItem { protected: /** Created in the #build() function. */ uiButTreeRow *tree_row_but_ = nullptr; - /** Optionally passed to the #BasicTreeViewItem constructor. Called when activating this tree + /** + * Optionally passed to the #BasicTreeViewItem constructor. Called when activating this tree * view item. This way users don't have to sub-class #BasicTreeViewItem, just to implement - * custom activation behavior (a common thing to do). */ + * custom activation behavior (a common thing to do). + */ ActivateFn activate_fn_; uiBut *button(); @@ -293,4 +324,16 @@ class BasicTreeViewItem : public AbstractTreeViewItem { /** \} */ +/* ---------------------------------------------------------------------- */ + +template +inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args) +{ + static_assert(std::is_base_of::value, + "Type must derive from and implement the AbstractTreeViewItem interface"); + + return dynamic_cast( + add_tree_item(std::make_unique(std::forward(args)...))); +} + } // namespace blender::ui From 18c6314e2660820778c555143b234dff3ba35ffa Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 5 Oct 2021 16:33:29 +0200 Subject: [PATCH 0573/1500] Cleanup: don't detect duplicate intersections in Embree It's unclear why this code was added in the first place, but it seems unnecessary, it can be restored if we find this breaks something. The Embree docs mention that the same primitive may be hit multiple times, but my understanding is that about e.g. curves where both the frontside and backside may be hit. However those hits would be at different distances. The context for this change is that we want to add an optimization where we can immediately update throughput for transparent shadows instead of recording intersections, and avoid duplicate would require extra work. However there is an Embree example that does something similar without worrying about duplicate hits either. --- intern/cycles/bvh/bvh_embree.cpp | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index 9250af419cb..eebc1e1e547 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -89,20 +89,9 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) /* Test if we need to record this transparent intersection. */ if (ctx->num_hits < ctx->max_hits || ray->tfar < ctx->max_t) { - /* Skip already recorded intersections. */ - int num_recorded_hits = min(ctx->num_hits, ctx->max_hits); - - for (int i = 0; i < num_recorded_hits; ++i) { - if (current_isect.object == ctx->isect_s[i].object && - current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) { - /* This intersection was already recorded, skip it. */ - *args->valid = 0; - return; - } - } - /* If maximum number of hits was reached, replace the intersection with the * highest distance. We want to find the N closest intersections. */ + const int num_recorded_hits = min(ctx->num_hits, ctx->max_hits); int isect_index = num_recorded_hits; if (num_recorded_hits + 1 >= ctx->max_hits) { float max_t = ctx->isect_s[0].t; @@ -213,14 +202,6 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) if (ctx->num_hits < ctx->max_hits) { Intersection current_isect; kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - for (size_t i = 0; i < ctx->num_hits; ++i) { - if (current_isect.object == ctx->isect_s[i].object && - current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) { - /* This intersection was already recorded, skip it. */ - *args->valid = 0; - break; - } - } Intersection *isect = &ctx->isect_s[ctx->num_hits]; ++ctx->num_hits; *isect = current_isect; From 03f8c1abd06e204f5a46046e71f29054a6fc0ed0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 5 Oct 2021 14:53:27 +0200 Subject: [PATCH 0574/1500] Build: add ccache support for CUDA kernels on Linux --- intern/cycles/kernel/CMakeLists.txt | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 514b7f8263c..c53d3d4b962 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -404,16 +404,27 @@ if(WITH_CYCLES_CUDA_BINARIES) -cuda-toolkit-dir "${cuda_toolkit_root_dir}" DEPENDS ${kernel_sources} cycles_cubin_cc) else() - add_custom_command( - OUTPUT ${cuda_file} - COMMAND ${cuda_nvcc_executable} + set(_cuda_nvcc_args -arch=${arch} ${CUDA_NVCC_FLAGS} --${format} ${CMAKE_CURRENT_SOURCE_DIR}${cuda_kernel_src} --ptxas-options="-v" - ${cuda_flags} - DEPENDS ${kernel_sources}) + ${cuda_flags}) + + if(WITH_COMPILER_CCACHE AND CCACHE_PROGRAM) + add_custom_command( + OUTPUT ${cuda_file} + COMMAND ${CCACHE_PROGRAM} ${cuda_nvcc_executable} ${_cuda_nvcc_args} + DEPENDS ${kernel_sources}) + else() + add_custom_command( + OUTPUT ${cuda_file} + COMMAND ${cuda_nvcc_executable} ${_cuda_nvcc_args} + DEPENDS ${kernel_sources}) + endif() + + unset(_cuda_nvcc_args) endif() delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${cuda_file}" ${CYCLES_INSTALL_PATH}/lib) list(APPEND cuda_cubins ${cuda_file}) From 335f40ebfa28d36d0e4c3c562eb572554282a3ff Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 5 Oct 2021 17:23:06 +0200 Subject: [PATCH 0575/1500] Tests: include device type in benchmark graph labels --- tests/performance/api/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/api/graph.py b/tests/performance/api/graph.py index e54adc194de..c7d3c5043ec 100644 --- a/tests/performance/api/graph.py +++ b/tests/performance/api/graph.py @@ -22,7 +22,7 @@ class TestGraph: for entry in queue.entries: if entry.status in ('done', 'outdated'): - device_name = entry.device_name + device_name = entry.device_name + " (" + entry.device_type + ")" if device_name in devices.keys(): devices[device_name].append(entry) else: From 539575b585a018eabda9137d724967692433165a Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 6 Oct 2021 14:18:12 +0200 Subject: [PATCH 0576/1500] Assets: Support Renaming Catalogs in the UI Catalogs can now be renamed by double clicking them in the Asset Browser. This is mostly done through the tree-view API, the asset specific code is very little. There is some polish left to be done here, e.g. the double click currently also collapses/uncollapses and activates the clicked item. And the rename button takes the full width of the row. But addressing these is better done as part of some other behavioral changes that are planned anyway. --- source/blender/editors/include/UI_interface.h | 2 + .../blender/editors/include/UI_tree_view.hh | 44 +++- .../editors/interface/interface_handlers.c | 22 +- source/blender/editors/interface/tree_view.cc | 225 +++++++++++++++--- .../space_file/asset_catalog_tree_view.cc | 26 +- 5 files changed, 278 insertions(+), 41 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index f642895f64e..e8b71a41439 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2770,6 +2770,8 @@ char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct bContext *C, const struct wmDrag *drag, const struct wmEvent *event); +bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle); +void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle); uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, int x, int y); diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index dbafd1b3a2b..51737067648 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -23,10 +23,13 @@ #pragma once +#include #include #include #include +#include "DNA_defs.h" + #include "BLI_function_ref.hh" #include "BLI_vector.hh" @@ -144,8 +147,14 @@ class TreeViewLayoutBuilder { * \{ */ class AbstractTreeView : public TreeViewItemContainer { + friend AbstractTreeViewItem; friend TreeViewBuilder; - friend TreeViewLayoutBuilder; + + /** + * Only one item can be renamed at a time. So the tree is informed about the renaming state to + * enforce that. + */ + std::unique_ptr> rename_buffer_; bool is_reconstructed_ = false; @@ -154,6 +163,8 @@ class AbstractTreeView : public TreeViewItemContainer { void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; + /** Only one item can be renamed at a time. */ + bool is_renaming() const; /** * Check if the tree is fully (re-)constructed. That means, both #build_tree() and * #update_from_old() have finished. @@ -198,6 +209,7 @@ class AbstractTreeView : public TreeViewItemContainer { */ class AbstractTreeViewItem : public TreeViewItemContainer { friend class AbstractTreeView; + friend class TreeViewLayoutBuilder; public: using IsActiveFn = std::function; @@ -205,12 +217,15 @@ class AbstractTreeViewItem : public TreeViewItemContainer { private: bool is_open_ = false; bool is_active_ = false; + bool is_renaming_ = false; IsActiveFn is_active_fn_; protected: /** This label is used for identifying an item (together with its parent's labels). */ std::string label_{}; + /** Every item gets a button of type during the layout building #UI_BTYPE_TREEROW. */ + uiButTreeRow *tree_row_but_ = nullptr; public: virtual ~AbstractTreeViewItem() = default; @@ -234,6 +249,19 @@ class AbstractTreeViewItem : public TreeViewItemContainer { virtual std::string drop_tooltip(const bContext &C, const wmDrag &drag, const wmEvent &event) const; + /** + * Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if + * another item is already being renamed. + */ + virtual bool can_rename() const; + /** + * Try renaming the item, or the data it represents. Can assume + * #AbstractTreeViewItem::can_rename() returned true. Sub-classes that override this should + * usually call this, unless they have a custom #AbstractTreeViewItem.matches(). + * + * \return True if the renaming was successful. + */ + virtual bool rename(StringRefNull new_name); /** * Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of @@ -250,7 +278,11 @@ class AbstractTreeViewItem : public TreeViewItemContainer { */ virtual bool matches(const AbstractTreeViewItem &other) const; + void begin_renaming(); + void end_renaming(); + const AbstractTreeView &get_tree_view() const; + AbstractTreeView &get_tree_view(); int count_parents() const; void deactivate(); /** @@ -266,6 +298,8 @@ class AbstractTreeViewItem : public TreeViewItemContainer { bool is_collapsed() const; void set_collapsed(bool collapsed); bool is_collapsible() const; + bool is_renaming() const; + void ensure_parents_uncollapsed(); protected: @@ -278,8 +312,14 @@ class AbstractTreeViewItem : public TreeViewItemContainer { void activate(); private: + static void rename_button_fn(bContext *, void *, char *); + static AbstractTreeViewItem *find_tree_item_from_rename_button(const uiBut &but); + static void tree_row_click_fn(struct bContext *, void *, void *); + /** See #AbstractTreeView::change_state_delayed() */ void change_state_delayed(); + void add_treerow_button(uiBlock &block); + void add_rename_button(uiBlock &block); }; /** \} */ @@ -304,8 +344,6 @@ class BasicTreeViewItem : public AbstractTreeViewItem { void on_activate(ActivateFn fn); protected: - /** Created in the #build() function. */ - uiButTreeRow *tree_row_but_ = nullptr; /** * Optionally passed to the #BasicTreeViewItem constructor. Called when activating this tree * view item. This way users don't have to sub-class #BasicTreeViewItem, just to implement diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 6ee563003ef..f73420b3668 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -4822,6 +4822,24 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons return WM_UI_HANDLER_CONTINUE; } +static int ui_do_but_TREEROW(bContext *C, + uiBut *but, + uiHandleButtonData *data, + const wmEvent *event) +{ + uiButTreeRow *tree_row_but = (uiButTreeRow *)but; + BLI_assert(tree_row_but->but.type == UI_BTYPE_TREEROW); + + if ((event->type == LEFTMOUSE) && (event->val == KM_DBL_CLICK)) { + button_activate_state(C, but, BUTTON_STATE_EXIT); + + UI_tree_view_item_begin_rename(tree_row_but->tree_item); + return WM_UI_HANDLER_BREAK; + } + + return ui_do_but_TOG(C, but, data, event); +} + static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7989,10 +8007,12 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_CHECKBOX: case UI_BTYPE_CHECKBOX_N: case UI_BTYPE_ROW: - case UI_BTYPE_TREEROW: case UI_BTYPE_DATASETROW: retval = ui_do_but_TOG(C, but, data, event); break; + case UI_BTYPE_TREEROW: + retval = ui_do_but_TREEROW(C, but, data, event); + break; case UI_BTYPE_SCROLL: retval = ui_do_but_SCROLL(C, block, but, data, event); break; diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 8bd2be7dc77..8f272143b2c 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -20,6 +20,8 @@ #include "DNA_userdef_types.h" +#include "BKE_context.h" + #include "BLT_translation.h" #include "interface_intern.h" @@ -76,6 +78,11 @@ void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) con foreach_item_recursive(iter_fn, options); } +bool AbstractTreeView::is_renaming() const +{ + return rename_buffer_ != nullptr; +} + void AbstractTreeView::build_layout_from_tree(const TreeViewLayoutBuilder &builder) { uiLayout *prev_layout = builder.current_layout(); @@ -103,6 +110,13 @@ void AbstractTreeView::update_from_old(uiBlock &new_block) BLI_assert(old_view_handle); AbstractTreeView &old_view = reinterpret_cast(*old_view_handle); + + /* Update own persistent data. */ + /* Keep the rename buffer persistent while renaming! The rename button uses the buffer's + * pointer to identify itself over redraws. */ + rename_buffer_ = std::move(old_view.rename_buffer_); + old_view.rename_buffer_ = nullptr; + update_children_from_old_recursive(*this, old_view); /* Finished (re-)constructing the tree. */ @@ -153,6 +167,95 @@ void AbstractTreeView::change_state_delayed() /* ---------------------------------------------------------------------- */ +void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, + void *but_arg1, + void * /*arg2*/) +{ + uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1; + BasicTreeViewItem &tree_item = reinterpret_cast(*tree_row_but->tree_item); + + /* Let a click on an opened item activate it, a second click will close it then. + * TODO Should this be for asset catalogs only? */ + if (tree_item.is_collapsed() || tree_item.is_active()) { + tree_item.toggle_collapsed(); + } + tree_item.activate(); +} + +void AbstractTreeViewItem::add_treerow_button(uiBlock &block) +{ + tree_row_but_ = (uiButTreeRow *)uiDefBut( + &block, UI_BTYPE_TREEROW, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + + tree_row_but_->tree_item = reinterpret_cast(this); + UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr); + UI_but_treerow_indentation_set(&tree_row_but_->but, count_parents()); +} + +AbstractTreeViewItem *AbstractTreeViewItem::find_tree_item_from_rename_button( + const uiBut &rename_but) +{ + /* A minimal sanity check, can't do much more here. */ + BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); + + LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { + if (but->type != UI_BTYPE_TREEROW) { + continue; + } + + uiButTreeRow *tree_row_but = (uiButTreeRow *)but; + AbstractTreeViewItem *item = reinterpret_cast(tree_row_but->tree_item); + const AbstractTreeView &tree_view = item->get_tree_view(); + + if (item->is_renaming() && (tree_view.rename_buffer_->data() == rename_but.poin)) { + return item; + } + } + + return nullptr; +} + +void AbstractTreeViewItem::rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr)) +{ + const uiBut *rename_but = static_cast(arg); + AbstractTreeViewItem *item = find_tree_item_from_rename_button(*rename_but); + BLI_assert(item); + + const AbstractTreeView &tree_view = item->get_tree_view(); + item->rename(tree_view.rename_buffer_->data()); + item->end_renaming(); +} + +void AbstractTreeViewItem::add_rename_button(uiBlock &block) +{ + AbstractTreeView &tree_view = get_tree_view(); + uiBut *rename_but = uiDefBut(&block, + UI_BTYPE_TEXT, + 1, + "", + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + tree_view.rename_buffer_->data(), + 1.0f, + tree_view.rename_buffer_->max_size(), + 0, + 0, + ""); + + /* Gotta be careful with what's passed to the `arg1` here. Any tree data will be freed once the + * callback is executed. */ + UI_but_func_rename_set(rename_but, AbstractTreeViewItem::rename_button_fn, rename_but); + + const bContext *evil_C = static_cast(block.evil_C); + ARegion *region = CTX_wm_region(evil_C); + /* Returns false if the button was removed. */ + if (UI_but_active_only(evil_C, region, &block, rename_but) == false) { + end_renaming(); + } +} + void AbstractTreeViewItem::on_activate() { /* Do nothing by default. */ @@ -181,10 +284,25 @@ std::string AbstractTreeViewItem::drop_tooltip(const bContext & /*C*/, return TIP_("Drop into/onto tree item"); } +bool AbstractTreeViewItem::can_rename() const +{ + /* No renaming by default. */ + return false; +} + +bool AbstractTreeViewItem::rename(StringRefNull new_name) +{ + /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches() + * recognizes the item. (It only compares labels by default.) */ + label_ = new_name; + return true; +} + void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) { is_open_ = old.is_open_; is_active_ = old.is_active_; + is_renaming_ = old.is_renaming_; } bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const @@ -192,11 +310,41 @@ bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const return label_ == other.label_; } +void AbstractTreeViewItem::begin_renaming() +{ + AbstractTreeView &tree_view = get_tree_view(); + if (tree_view.is_renaming() || !can_rename()) { + return; + } + + is_renaming_ = true; + + tree_view.rename_buffer_ = std::make_unique(); + std::copy(std::begin(label_), std::end(label_), std::begin(*tree_view.rename_buffer_)); +} + +void AbstractTreeViewItem::end_renaming() +{ + if (!is_renaming()) { + return; + } + + is_renaming_ = false; + + AbstractTreeView &tree_view = get_tree_view(); + tree_view.rename_buffer_ = nullptr; +} + const AbstractTreeView &AbstractTreeViewItem::get_tree_view() const { return static_cast(*root_); } +AbstractTreeView &AbstractTreeViewItem::get_tree_view() +{ + return static_cast(*root_); +} + int AbstractTreeViewItem::count_parents() const { int i = 0; @@ -259,6 +407,11 @@ bool AbstractTreeViewItem::is_collapsible() const return !children_.is_empty(); } +bool AbstractTreeViewItem::is_renaming() const +{ + return is_renaming_; +} + void AbstractTreeViewItem::ensure_parents_uncollapsed() { for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) { @@ -298,9 +451,21 @@ void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const uiLayout *prev_layout = current_layout(); uiLayout *row = uiLayoutRow(prev_layout, false); - item.build_row(*row); + uiLayoutOverlap(row); - UI_block_layout_set_current(&block(), prev_layout); + uiBlock &block_ = block(); + + /* Every item gets one! Other buttons can be overlapped on top. */ + item.add_treerow_button(block_); + + if (item.is_renaming()) { + item.add_rename_button(block_); + } + else { + item.build_row(*row); + } + + UI_block_layout_set_current(&block_, prev_layout); } uiBlock &TreeViewLayoutBuilder::block() const @@ -320,42 +485,12 @@ BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(ic label_ = label; } -void BasicTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, void *but_arg1, void * /*arg2*/) +void BasicTreeViewItem::build_row(uiLayout & /*row*/) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1; - BasicTreeViewItem &tree_item = reinterpret_cast(*tree_row_but->tree_item); - - /* Let a click on an opened item activate it, a second click will close it then. - * TODO Should this be for asset catalogs only? */ - if (tree_item.is_collapsed() || tree_item.is_active()) { - tree_item.toggle_collapsed(); + if (BIFIconID icon = get_draw_icon()) { + ui_def_but_icon(&tree_row_but_->but, icon, UI_HAS_ICON); } - tree_item.activate(); -} - -void BasicTreeViewItem::build_row(uiLayout &row) -{ - uiBlock *block = uiLayoutGetBlock(&row); - tree_row_but_ = (uiButTreeRow *)uiDefIconTextBut(block, - UI_BTYPE_TREEROW, - 0, - /* TODO allow icon besides the chevron icon? */ - get_draw_icon(), - label_.data(), - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0, - 0, - 0, - 0, - nullptr); - - tree_row_but_->tree_item = reinterpret_cast(this); - UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr); - UI_but_treerow_indentation_set(&tree_row_but_->but, count_parents()); + tree_row_but_->but.str = BLI_strdupn(label_.c_str(), label_.length()); } void BasicTreeViewItem::on_activate() @@ -437,3 +572,21 @@ bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const ListBase * return false; } + +/** + * Can \a item_handle be renamed right now? Not that this isn't just a mere wrapper around + * #AbstractTreeViewItem::can_rename(). This also checks if there is another item being renamed, + * and returns false if so. + */ +bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_handle); + const AbstractTreeView &tree_view = item.get_tree_view(); + return !tree_view.is_renaming() && item.can_rename(); +} + +void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle) +{ + AbstractTreeViewItem &item = reinterpret_cast(*item_handle); + item.begin_renaming(); +} diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 291582dac08..85912268286 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -52,6 +52,7 @@ using namespace blender::bke; namespace blender::ed::asset_browser { class AssetCatalogTreeView : public ui::AbstractTreeView { + bke::AssetCatalogService *catalog_service_; /** The asset catalog tree this tree-view represents. */ bke::AssetCatalogTree *catalog_tree_; FileAssetSelectParams *params_; @@ -99,6 +100,9 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { const wmDrag &drag, const wmEvent &event) const override; bool on_drop(const wmDrag &drag) override; + + bool can_rename() const override; + bool rename(StringRefNull new_name) override; }; /** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level @@ -124,7 +128,8 @@ class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params, SpaceFile &space_file) - : catalog_tree_(BKE_asset_library_get_catalog_tree(library)), + : catalog_service_(BKE_asset_library_get_catalog_service(library)), + catalog_tree_(BKE_asset_library_get_catalog_tree(library)), params_(params), space_file_(space_file) { @@ -309,6 +314,25 @@ bool AssetCatalogTreeViewItem::on_drop(const wmDrag &drag) tree_view, drag, catalog_item_.get_catalog_id(), catalog_item_.get_simple_name()); } +bool AssetCatalogTreeViewItem::can_rename() const +{ + return true; +} + +bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) +{ + /* Important to keep state. */ + BasicTreeViewItem::rename(new_name); + + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + + AssetCatalogPath new_path = catalog_item_.catalog_path().parent(); + new_path = new_path / StringRef(new_name); + tree_view.catalog_service_->update_catalog_path(catalog_item_.get_catalog_id(), new_path); + return true; +} + /* ---------------------------------------------------------------------- */ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) From cda20a7af89c7da76a5016667f24f06eaa8afdb1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 6 Oct 2021 23:43:34 +1100 Subject: [PATCH 0577/1500] Keymap: ignore the fallback keymap when "Active Tool" is set Resolve regression in c9d9bfa84ad5cb985e3feccffa702b2f3cc2adf8, which added support for tools to be tagged as using a fallback too. In these cases the "Active Tool" setting was ignored and the fallback tool would be used (the spin tool would box select for example). --- source/blender/windowmanager/intern/wm_event_system.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index b4abc4f2fde..a2625f233b5 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -4025,6 +4025,7 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, const char *keymap_id_list[ARRAY_SIZE(km_result->keymaps)]; int keymap_id_list_len = 0; + const Scene *scene = wm->winactive->scene; ScrArea *area = handler->dynamic.user_data; handler->keymap_tool = NULL; bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL; @@ -4036,7 +4037,8 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, bool is_gizmo_visible = false; bool is_gizmo_highlight = false; - if (tref_rt && tref_rt->keymap_fallback[0]) { + if ((tref_rt && tref_rt->keymap_fallback[0]) && + (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK)) { bool add_keymap = false; /* Support for the gizmo owning the tool keymap. */ From e41dddd29a17a77e60bde6a2336fcd3937819bec Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 7 Oct 2021 00:01:46 +1100 Subject: [PATCH 0578/1500] Gizmo: remove wmGizmoGroup.use_fallback_keymap This ended up being a copy of: `toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK` requiring boiler plate assignment in gizmos refresh callbacks. Remove this struct member and check `toolsettings->workspace_tool_type` directly, since so far there has been no advantage in gizmo-groups being able to control this themselves. --- .../space_view3d/view3d_gizmo_tool_generic.c | 3 -- .../editors/transform/transform_gizmo_2d.c | 30 ------------------- .../editors/transform/transform_gizmo_3d.c | 21 ------------- .../transform/transform_gizmo_extrude_3d.c | 2 -- .../windowmanager/gizmo/WM_gizmo_types.h | 2 -- .../gizmo/intern/wm_gizmo_group.c | 1 - .../windowmanager/intern/wm_event_system.c | 12 ++++---- 7 files changed, 5 insertions(+), 66 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c index 0e0d59764e5..1de08e75f80 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c @@ -140,13 +140,10 @@ static void WIDGETGROUP_tool_generic_refresh(const bContext *C, wmGizmoGroup *gz ToolSettings *ts = CTX_data_tool_settings(C); if (ts->workspace_tool_type != SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = false; WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true); return; } - gzgroup->use_fallback_keymap = true; - /* skip, we don't draw anything anyway */ { int orientation; diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 4f6556cd2a2..aa4d5c03d74 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -465,16 +465,6 @@ static void gizmo2d_xform_refresh(const bContext *C, wmGizmoGroup *gzgroup) copy_v2_v2(ggd->origin, origin); bool show_cage = !ggd->no_cage && !equals_v2v2(ggd->min, ggd->max); - if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { - Scene *scene = CTX_data_scene(C); - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - } - if (has_select == false) { for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { ggd->translate_xy[i]->flag |= WM_GIZMO_HIDDEN; @@ -641,16 +631,6 @@ static void gizmo2d_resize_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup GizmoGroup_Resize2D *ggd = gzgroup->customdata; float origin[3] = {UNPACK2(ggd->origin), 0.0f}; - if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { - Scene *scene = CTX_data_scene(C); - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - } - gizmo2d_origin_to_region(region, origin); for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) { @@ -793,16 +773,6 @@ static void gizmo2d_rotate_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup GizmoGroup_Rotate2D *ggd = gzgroup->customdata; float origin[3] = {UNPACK2(ggd->origin), 0.0f}; - if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { - Scene *scene = CTX_data_scene(C); - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - } - gizmo2d_origin_to_region(region, origin); wmGizmo *gz = ggd->gizmo; diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 0fa179c4f74..e4c20fa0be1 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -1670,13 +1670,6 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup) RegionView3D *rv3d = region->regiondata; struct TransformBounds tbounds; - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - if (ggd->use_twtype_refresh) { ggd->twtype = v3d->gizmo_show_object & ggd->twtype_init; if (ggd->twtype != ggd->twtype_prev) { @@ -2105,13 +2098,6 @@ static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgr struct TransformBounds tbounds; - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, SCE_ORIENT_SCALE); if ((ED_transform_calc_gizmo_stats(C, @@ -2316,13 +2302,6 @@ static void WIDGETGROUP_xform_shear_refresh(const bContext *C, wmGizmoGroup *gzg struct XFormShearWidgetGroup *xgzgroup = gzgroup->customdata; struct TransformBounds tbounds; - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - /* Needed to test view orientation changes. */ copy_m3_m4(xgzgroup->prev.viewinv_m3, rv3d->viewinv); diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c index ca4ed01c0f6..6e89c3de197 100644 --- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c +++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c @@ -381,11 +381,9 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup) if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { WM_gizmo_set_flag(ggd->invoke_view, WM_GIZMO_HIDDEN, false); - gzgroup->use_fallback_keymap = true; } else { WM_gizmo_set_flag(ggd->invoke_view, WM_GIZMO_HIDDEN, true); - gzgroup->use_fallback_keymap = false; } } diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index a1edc4196dc..b667872a914 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -502,8 +502,6 @@ typedef struct wmGizmoGroup { bool tag_remove; - bool use_fallback_keymap; - void *customdata; /** For freeing customdata from above. */ void (*customdata_free)(void *); diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index 22bdf65a169..7772c87f71c 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -266,7 +266,6 @@ void WM_gizmogroup_ensure_init(const bContext *C, wmGizmoGroup *gzgroup) /* prepare for first draw */ if (UNLIKELY((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0)) { - gzgroup->use_fallback_keymap = true; gzgroup->type->setup(C, gzgroup); /* Not ideal, initialize keymap here, needed for RNA runtime generated gizmos. */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index a2625f233b5..f4753c7c190 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -4060,14 +4060,12 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, if (gzgroup != NULL) { if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { /* If all are hidden, don't override. */ - if (gzgroup->use_fallback_keymap) { - is_gizmo_visible = true; - wmGizmo *highlight = wm_gizmomap_highlight_get(gzmap); - if (highlight) { - is_gizmo_highlight = true; - } - add_keymap = true; + is_gizmo_visible = true; + wmGizmo *highlight = wm_gizmomap_highlight_get(gzmap); + if (highlight) { + is_gizmo_highlight = true; } + add_keymap = true; } } } From c6275da852eab77e2cea1ae601a43a2dbaad6c27 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 5 Oct 2021 15:05:12 +0200 Subject: [PATCH 0579/1500] Fix T91922: Cycles artifacts with high volume nested level Make volume stack allocated conditionally, potentially based on the actual nested level of objects in the scene. Currently the nested level is estimated by number of volume objects. This is a non-expensive check which is probably enough in practice to get almost perfect memory usage and performance. The conditional allocation is a bit tricky. For the CPU we declare and define maximum possible volume stack, because there are only that many integrator states on the CPU. On the GPU we declare outer SoA to have all volume stack elements, but only allocate actually needed ones. The actually used volume stack size is passed as a pre-processor, which seems to be easiest and fastest for the GPU state copy. There seems to be no speed regression in the demo files on RTX6000. Note that scenes with high nested level of volume will now be slower but correct. Differential Revision: https://developer.blender.org/D12759 --- .../cycles/integrator/path_trace_work_gpu.cpp | 18 ++++++++++-- .../integrator_intersect_volume_stack.h | 27 ++++++++++------- .../kernel/integrator/integrator_state.h | 6 ++-- .../integrator/integrator_state_template.h | 8 +++-- .../kernel/integrator/integrator_state_util.h | 5 +++- .../integrator/integrator_volume_stack.h | 2 +- intern/cycles/kernel/kernel_types.h | 10 +++++-- intern/cycles/render/graph.cpp | 4 ++- intern/cycles/render/object.cpp | 16 ++++++++++ intern/cycles/render/object.h | 7 +++++ intern/cycles/render/scene.cpp | 29 +++++++++++++++++++ intern/cycles/render/scene.h | 3 ++ 12 files changed, 112 insertions(+), 23 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index c29b0fb039e..8af8f9a02e2 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -23,6 +23,7 @@ #include "render/buffers.h" #include "render/scene.h" #include "util/util_logging.h" +#include "util/util_string.h" #include "util/util_tbb.h" #include "util/util_time.h" @@ -30,7 +31,7 @@ CCL_NAMESPACE_BEGIN -static size_t estimate_single_state_size() +static size_t estimate_single_state_size(DeviceScene *device_scene) { size_t state_size = 0; @@ -45,12 +46,14 @@ static size_t estimate_single_state_size() break; \ } \ } +#define KERNEL_STRUCT_VOLUME_STACK_SIZE (device_scene->data.volume_stack_size) #include "kernel/integrator/integrator_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER #undef KERNEL_STRUCT_ARRAY_MEMBER #undef KERNEL_STRUCT_END #undef KERNEL_STRUCT_END_ARRAY +#undef KERNEL_STRUCT_VOLUME_STACK_SIZE return state_size; } @@ -72,7 +75,7 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device, num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE), work_tiles_(device, "work_tiles", MEM_READ_WRITE), display_rgba_half_(device, "display buffer half", MEM_READ_WRITE), - max_num_paths_(queue_->num_concurrent_states(estimate_single_state_size())), + max_num_paths_(queue_->num_concurrent_states(estimate_single_state_size(device_scene))), min_num_active_paths_(queue_->num_concurrent_busy_states()), max_active_path_index_(0) { @@ -125,12 +128,23 @@ void PathTraceWorkGPU::alloc_integrator_soa() break; \ } \ } +#define KERNEL_STRUCT_VOLUME_STACK_SIZE (device_scene_->data.volume_stack_size) #include "kernel/integrator/integrator_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER #undef KERNEL_STRUCT_ARRAY_MEMBER #undef KERNEL_STRUCT_END #undef KERNEL_STRUCT_END_ARRAY +#undef KERNEL_STRUCT_VOLUME_STACK_SIZE + + if (VLOG_IS_ON(3)) { + size_t total_soa_size = 0; + for (auto &&soa_memory : integrator_state_soa_) { + total_soa_size += soa_memory->memory_size(); + } + + VLOG(3) << "GPU SoA state size: " << string_human_readable_size(total_soa_size); + } } void PathTraceWorkGPU::alloc_integrator_queue() diff --git a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h index 60d8a8e3e54..99f6cf35e9e 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h @@ -38,10 +38,13 @@ ccl_device void integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_A volume_ray.P = from_P; volume_ray.D = normalize_len(to_P - from_P, &volume_ray.t); + /* Store to avoid global fetches on every intersection step. */ + const uint volume_stack_size = kernel_data.volume_stack_size; + #ifdef __VOLUME_RECORD_ALL__ - Intersection hits[2 * VOLUME_STACK_SIZE + 1]; + Intersection hits[2 * volume_stack_size + 1]; uint num_hits = scene_intersect_volume_all( - kg, &volume_ray, hits, 2 * VOLUME_STACK_SIZE, PATH_RAY_ALL_VISIBILITY); + kg, &volume_ray, hits, 2 * volume_stack_size, PATH_RAY_ALL_VISIBILITY); if (num_hits > 0) { Intersection *isect = hits; @@ -55,7 +58,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_A #else Intersection isect; int step = 0; - while (step < 2 * VOLUME_STACK_SIZE && + while (step < 2 * volume_stack_size && scene_intersect_volume(kg, &volume_ray, &isect, PATH_RAY_ALL_VISIBILITY)) { shader_setup_from_ray(kg, stack_sd, &volume_ray, &isect); volume_stack_enter_exit(INTEGRATOR_STATE_PASS, stack_sd); @@ -91,12 +94,15 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) stack_index++; } + /* Store to avoid global fetches on every intersection step. */ + const uint volume_stack_size = kernel_data.volume_stack_size; + #ifdef __VOLUME_RECORD_ALL__ - Intersection hits[2 * VOLUME_STACK_SIZE + 1]; + Intersection hits[2 * volume_stack_size + 1]; uint num_hits = scene_intersect_volume_all( - kg, &volume_ray, hits, 2 * VOLUME_STACK_SIZE, visibility); + kg, &volume_ray, hits, 2 * volume_stack_size, visibility); if (num_hits > 0) { - int enclosed_volumes[VOLUME_STACK_SIZE]; + int enclosed_volumes[volume_stack_size]; Intersection *isect = hits; qsort(hits, num_hits, sizeof(Intersection), intersections_compare); @@ -121,7 +127,7 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) break; } } - if (need_add && stack_index < VOLUME_STACK_SIZE - 1) { + if (need_add && stack_index < volume_stack_size - 1) { const VolumeStack new_entry = {stack_sd->object, stack_sd->shader}; integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, stack_index, new_entry); ++stack_index; @@ -136,11 +142,12 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) } } #else - int enclosed_volumes[VOLUME_STACK_SIZE]; + /* CUDA does not support defintion of a variable size arrays, so use the maximum possible. */ + int enclosed_volumes[MAX_VOLUME_STACK_SIZE]; int step = 0; - while (stack_index < VOLUME_STACK_SIZE - 1 && enclosed_index < VOLUME_STACK_SIZE - 1 && - step < 2 * VOLUME_STACK_SIZE) { + while (stack_index < volume_stack_size - 1 && enclosed_index < volume_stack_size - 1 && + step < 2 * volume_stack_size) { Intersection isect; if (!scene_intersect_volume(kg, &volume_ray, &isect, visibility)) { break; diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h index f745ad3f4b9..efc7576d95b 100644 --- a/intern/cycles/kernel/integrator/integrator_state.h +++ b/intern/cycles/kernel/integrator/integrator_state.h @@ -59,8 +59,6 @@ CCL_NAMESPACE_BEGIN * * TODO: these could be made dynamic depending on the features used in the scene. */ -#define INTEGRATOR_VOLUME_STACK_SIZE VOLUME_STACK_SIZE - #define INTEGRATOR_SHADOW_ISECT_SIZE_CPU 1024 #define INTEGRATOR_SHADOW_ISECT_SIZE_GPU 4 @@ -85,12 +83,14 @@ typedef struct IntegratorStateCPU { #define KERNEL_STRUCT_END_ARRAY(name, cpu_size, gpu_size) \ } \ name[cpu_size]; +#define KERNEL_STRUCT_VOLUME_STACK_SIZE MAX_VOLUME_STACK_SIZE #include "kernel/integrator/integrator_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER #undef KERNEL_STRUCT_ARRAY_MEMBER #undef KERNEL_STRUCT_END #undef KERNEL_STRUCT_END_ARRAY +#undef KERNEL_STRUCT_VOLUME_STACK_SIZE } IntegratorStateCPU; /* Path Queue @@ -114,12 +114,14 @@ typedef struct IntegratorStateGPU { #define KERNEL_STRUCT_END_ARRAY(name, cpu_size, gpu_size) \ } \ name[gpu_size]; +#define KERNEL_STRUCT_VOLUME_STACK_SIZE MAX_VOLUME_STACK_SIZE #include "kernel/integrator/integrator_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER #undef KERNEL_STRUCT_ARRAY_MEMBER #undef KERNEL_STRUCT_END #undef KERNEL_STRUCT_END_ARRAY +#undef KERNEL_STRUCT_VOLUME_STACK_SIZE /* Count number of queued kernels. */ IntegratorQueueCounter *queue_counter; diff --git a/intern/cycles/kernel/integrator/integrator_state_template.h b/intern/cycles/kernel/integrator/integrator_state_template.h index 0d8126c64aa..15998ee6edf 100644 --- a/intern/cycles/kernel/integrator/integrator_state_template.h +++ b/intern/cycles/kernel/integrator/integrator_state_template.h @@ -107,7 +107,9 @@ KERNEL_STRUCT_END(subsurface) KERNEL_STRUCT_BEGIN(volume_stack) KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, object, KERNEL_FEATURE_VOLUME) KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, shader, KERNEL_FEATURE_VOLUME) -KERNEL_STRUCT_END_ARRAY(volume_stack, INTEGRATOR_VOLUME_STACK_SIZE, INTEGRATOR_VOLUME_STACK_SIZE) +KERNEL_STRUCT_END_ARRAY(volume_stack, + KERNEL_STRUCT_VOLUME_STACK_SIZE, + KERNEL_STRUCT_VOLUME_STACK_SIZE) /********************************* Shadow Path State **************************/ @@ -163,5 +165,5 @@ KERNEL_STRUCT_BEGIN(shadow_volume_stack) KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, object, KERNEL_FEATURE_VOLUME) KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, shader, KERNEL_FEATURE_VOLUME) KERNEL_STRUCT_END_ARRAY(shadow_volume_stack, - INTEGRATOR_VOLUME_STACK_SIZE, - INTEGRATOR_VOLUME_STACK_SIZE) + KERNEL_STRUCT_VOLUME_STACK_SIZE, + KERNEL_STRUCT_VOLUME_STACK_SIZE) diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index 08d6cb00114..453ec49c7b0 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -155,7 +155,7 @@ ccl_device_forceinline void integrator_state_read_shadow_isect(INTEGRATOR_STATE_ ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(INTEGRATOR_STATE_ARGS) { if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { - for (int i = 0; i < INTEGRATOR_VOLUME_STACK_SIZE; i++) { + for (int i = 0; i < kernel_data.volume_stack_size; i++) { INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, i, object) = INTEGRATOR_STATE_ARRAY( volume_stack, i, object); INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, i, shader) = INTEGRATOR_STATE_ARRAY( @@ -223,6 +223,8 @@ ccl_device_inline void integrator_state_copy_only(const IntegratorState to_state while (index < gpu_array_size) \ ; +# define KERNEL_STRUCT_VOLUME_STACK_SIZE kernel_data.volume_stack_size + # include "kernel/integrator/integrator_state_template.h" # undef KERNEL_STRUCT_BEGIN @@ -230,6 +232,7 @@ ccl_device_inline void integrator_state_copy_only(const IntegratorState to_state # undef KERNEL_STRUCT_ARRAY_MEMBER # undef KERNEL_STRUCT_END # undef KERNEL_STRUCT_END_ARRAY +# undef KERNEL_STRUCT_VOLUME_STACK_SIZE } ccl_device_inline void integrator_state_move(const IntegratorState to_state, diff --git a/intern/cycles/kernel/integrator/integrator_volume_stack.h b/intern/cycles/kernel/integrator/integrator_volume_stack.h index d53070095f0..01ebf8376b1 100644 --- a/intern/cycles/kernel/integrator/integrator_volume_stack.h +++ b/intern/cycles/kernel/integrator/integrator_volume_stack.h @@ -72,7 +72,7 @@ ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, } /* If we exceed the stack limit, ignore. */ - if (i >= VOLUME_STACK_SIZE - 1) { + if (i >= kernel_data.volume_stack_size - 1) { return; } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 6107e1028ba..22dde3537eb 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -61,8 +61,6 @@ CCL_NAMESPACE_BEGIN #define ID_NONE (0.0f) #define PASS_UNUSED (~0) -#define VOLUME_STACK_SIZE 4 - /* Kernel features */ #define __SOBOL__ #define __DPDU__ @@ -608,6 +606,12 @@ typedef struct AttributeDescriptor { # define MAX_CLOSURE __MAX_CLOSURE__ #endif +#ifndef __MAX_VOLUME_STACK_SIZE__ +# define MAX_VOLUME_STACK_SIZE 32 +#else +# define MAX_VOLUME_STACK_SIZE __MAX_VOLUME_STACK_SIZE__ +#endif + #define MAX_VOLUME_CLOSURE 8 /* This struct is the base class for all closures. The common members are @@ -1223,7 +1227,7 @@ typedef struct KernelData { uint kernel_features; uint max_closures; uint max_shaders; - uint pad; + uint volume_stack_size; KernelCamera cam; KernelFilm film; diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index e9da48b624d..ee1a6e68bcf 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -1149,7 +1149,9 @@ int ShaderGraph::get_num_closures() num_closures += 8; } else if (CLOSURE_IS_VOLUME(closure_type)) { - num_closures += VOLUME_STACK_SIZE; + /* TODO(sergey): Verify this is still needed, since we have special minimized volume storage + * for the volume steps. */ + num_closures += MAX_VOLUME_STACK_SIZE; } else if (closure_type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) { num_closures += 4; diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 4637f8fe989..1320a5eb7a6 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -366,6 +366,22 @@ float Object::compute_volume_step_size() const return step_size; } +bool Object::check_is_volume() const +{ + if (geometry->geometry_type == Geometry::VOLUME) { + return true; + } + + for (Node *node : get_geometry()->get_used_shaders()) { + const Shader *shader = static_cast(node); + if (shader->has_volume_connected) { + return true; + } + } + + return false; +} + int Object::get_device_index() const { return index; diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index c52ddce48da..84e2dfffebb 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -109,6 +109,13 @@ class Object : public Node { /* Compute step size from attributes, shaders, transforms. */ float compute_volume_step_size() const; + /* Check whether this object requires volume sampling (and hence might require space in the + * volume stack). + * + * Note that this is a naive iteration over sharders, which allows to access information prior + * to `scene_update()`. */ + bool check_is_volume() const; + protected: /* Specifies the position of the object in scene->objects and * in the device vectors. Gets set in device_update. */ diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index a4b030190dc..ecd6946bbf8 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -527,6 +527,8 @@ void Scene::update_kernel_features() const uint max_closures = (params.background) ? get_max_closure_count() : MAX_CLOSURE; dscene.data.max_closures = max_closures; dscene.data.max_shaders = shaders.size(); + + dscene.data.volume_stack_size = get_volume_stack_size(); } bool Scene::update(Progress &progress) @@ -642,6 +644,33 @@ int Scene::get_max_closure_count() return max_closure_global; } +int Scene::get_volume_stack_size() const +{ + /* Quick non-expensive check. Can over-estimate maximum possible nested level, but does not + * require expensive calculation during pre-processing. */ + int num_volume_objects = 0; + for (const Object *object : objects) { + if (object->check_is_volume()) { + ++num_volume_objects; + } + + if (num_volume_objects == MAX_VOLUME_STACK_SIZE) { + break; + } + } + + /* Count background world for the stack. */ + const Shader *background_shader = background->get_shader(this); + if (background_shader && background_shader->has_volume_connected) { + ++num_volume_objects; + } + + /* Space for terminator. */ + ++num_volume_objects; + + return min(num_volume_objects, MAX_VOLUME_STACK_SIZE); +} + bool Scene::has_shadow_catcher() { if (shadow_catcher_modified_) { diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index cf4a3ba6b12..8076d0dc09c 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -344,6 +344,9 @@ class Scene : public NodeOwner { /* Get maximum number of closures to be used in kernel. */ int get_max_closure_count(); + /* Get size of a volume stack needed to render this scene. */ + int get_volume_stack_size() const; + template void delete_node_impl(T *node) { delete node; From 12c66854bd02c27595d41a83301638f4e46fa29a Mon Sep 17 00:00:00 2001 From: Jacob Lewallen Date: Wed, 6 Oct 2021 10:23:10 -0400 Subject: [PATCH 0580/1500] Pass correct array size to BKE_object_material_remap_calc This was patch D12460 from jlewallen and fixes T91339 and T90818. --- source/blender/modifiers/intern/MOD_boolean.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index c5d6902e1bc..95167b5c82e 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -386,7 +386,7 @@ static void BMD_mesh_intersection(BMesh *bm, * Caller owns the returned array. */ static Array get_material_remap(Object *dest_ob, Object *src_ob) { - int n = dest_ob->totcol; + int n = src_ob->totcol; if (n <= 0) { n = 1; } From 2012d541ae3b9fb4ef099e7c490083b463e60d4f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 6 Oct 2021 14:50:15 +0200 Subject: [PATCH 0581/1500] Asset Browser: Always show icon to add catalog next to "All" item Feedback was that it's unclear sometimes how to add a new item and that some people expect a button to add a new item next to the "All" item. --- source/blender/editors/space_file/asset_catalog_tree_view.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 85912268286..552a0a7acd0 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -339,10 +339,6 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) { ui::BasicTreeViewItem::build_row(row); - if (!is_active()) { - return; - } - PointerRNA *props; props = UI_but_extra_operator_icon_add( button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); From 536109b4ec336e86de5a7e22e51804584bca74f5 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 6 Oct 2021 16:15:12 +0200 Subject: [PATCH 0582/1500] Fix possibly wrong matching of tree-view item buttons The UI code to ensure consistent button state over redraws was just comparing the name of the item, ignoring the parent names. So with multiple items of the same name, there might have been glitches (didn't see any myself though). There's a leftover to-do though, we don't check yet if the matched buttons are actually from the same tree. Added TODO comment. --- .../blender/editors/include/UI_tree_view.hh | 1 + source/blender/editors/interface/tree_view.cc | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 51737067648..8f8681896fe 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -301,6 +301,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer { bool is_renaming() const; void ensure_parents_uncollapsed(); + bool matches_including_parents(const AbstractTreeViewItem &other) const; protected: /** diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 8f272143b2c..d2971f791c2 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -419,6 +419,23 @@ void AbstractTreeViewItem::ensure_parents_uncollapsed() } } +bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem &other) const +{ + if (!matches(other)) { + return false; + } + + for (AbstractTreeViewItem *parent = parent_, *other_parent = other.parent_; + parent && other_parent; + parent = parent->parent_, other_parent = other_parent->parent_) { + if (!parent->matches(*other_parent)) { + return false; + } + } + + return true; +} + void AbstractTreeViewItem::change_state_delayed() { if (is_active_fn_()) { @@ -538,7 +555,8 @@ bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, { const AbstractTreeViewItem &a = reinterpret_cast(*a_handle); const AbstractTreeViewItem &b = reinterpret_cast(*b_handle); - return a.matches(b); + /* TODO should match the tree-view as well. */ + return a.matches_including_parents(b); } bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const wmDrag *drag) From 75fbf6f17e69ee9c6487173ae5957cfff5193d1f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 6 Oct 2021 16:29:10 +0200 Subject: [PATCH 0583/1500] Asset Browser: Show catalog add & delete icons on mouse hover (only) Now the icons to add or delete catalogs are only shown when mouse hovering a catalog item in the tree. This is convenient for quick creation of catalogs, and doesn't require activating a catalog to edit it first. Determining if a tree item is hovered isn't trivial actually. The UI tree-view code has to find the matching tree-row button in the previous layout to do so, since the new layout isn't calculated yet. --- .../blender/editors/include/UI_tree_view.hh | 7 +++ .../editors/interface/interface_intern.h | 2 + .../editors/interface/interface_view.cc | 63 ++++++++++++++++--- source/blender/editors/interface/tree_view.cc | 15 +++++ .../space_file/asset_catalog_tree_view.cc | 2 +- 5 files changed, 79 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 8f8681896fe..7693a833210 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -185,6 +185,7 @@ class AbstractTreeView : public TreeViewItemContainer { const TreeViewItemContainer &old_items); static AbstractTreeViewItem *find_matching_child(const AbstractTreeViewItem &lookup_item, const TreeViewItemContainer &items); + /** * Items may want to do additional work when state changes. But these state changes can only be * reliably detected after the tree has completed reconstruction (see #is_reconstructed()). So @@ -290,6 +291,12 @@ class AbstractTreeViewItem : public TreeViewItemContainer { * can't be sure about the item state. */ bool is_active() const; + /** + * Can be called from the #AbstractTreeViewItem::build_row() implementation, but not earlier. The + * hovered state can't be queried reliably otherwise. + * Note that this does a linear lookup in the old block, so isn't too great performance-wise. + */ + bool is_hovered() const; void toggle_collapsed(); /** * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 8e69ac40a34..5c06f8cfd13 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1293,6 +1293,8 @@ void ui_interface_tag_script_reload_queries(void); void ui_block_free_views(struct uiBlock *block); uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, const uiTreeViewHandle *new_view); +uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, + const uiTreeViewItemHandle *new_item_handle); #ifdef __cplusplus } diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc index b199ce9562e..8122b965892 100644 --- a/source/blender/editors/interface/interface_view.cc +++ b/source/blender/editors/interface/interface_view.cc @@ -106,26 +106,71 @@ static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractT return {}; } -uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, - const uiTreeViewHandle *new_view_handle) +static AbstractTreeView *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, + const AbstractTreeView &new_view) { - const AbstractTreeView &needle_view = reinterpret_cast( - *new_view_handle); - - uiBlock *old_block = new_block->oldblock; + uiBlock *old_block = new_block.oldblock; if (!old_block) { return nullptr; } - StringRef idname = ui_block_view_find_idname(*new_block, needle_view); + StringRef idname = ui_block_view_find_idname(new_block, new_view); if (idname.is_empty()) { return nullptr; } LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) { if (old_view_link->idname == idname) { - return reinterpret_cast( - get_view_from_link(*old_view_link)); + return get_view_from_link(*old_view_link); + } + } + + return nullptr; +} + +uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, + const uiTreeViewHandle *new_view_handle) +{ + BLI_assert(new_block && new_view_handle); + const AbstractTreeView &new_view = reinterpret_cast(*new_view_handle); + + AbstractTreeView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view); + return reinterpret_cast(old_view); +} + +uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, + const uiTreeViewItemHandle *new_item_handle) +{ + uiBlock *old_block = new_block->oldblock; + if (!old_block) { + return nullptr; + } + + const AbstractTreeViewItem &new_item = *reinterpret_cast( + new_item_handle); + const AbstractTreeView *old_tree_view = ui_block_view_find_matching_in_old_block( + *new_block, new_item.get_tree_view()); + if (!old_tree_view) { + return nullptr; + } + + LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) { + if (old_but->type != UI_BTYPE_TREEROW) { + continue; + } + uiButTreeRow *old_treerow_but = (uiButTreeRow *)old_but; + if (!old_treerow_but->tree_item) { + continue; + } + AbstractTreeViewItem &old_item = *reinterpret_cast( + old_treerow_but->tree_item); + /* Check if the row is from the expected tree-view. */ + if (&old_item.get_tree_view() != old_tree_view) { + continue; + } + + if (UI_tree_view_item_matches(new_item_handle, old_treerow_but->tree_item)) { + return old_treerow_but; } } diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index d2971f791c2..7bcf679a5ea 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -385,6 +385,21 @@ bool AbstractTreeViewItem::is_active() const return is_active_; } +bool AbstractTreeViewItem::is_hovered() const +{ + BLI_assert_msg(get_tree_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + BLI_assert_msg(tree_row_but_ != nullptr, + "Hovered state can't be queried before the tree row is being built"); + + const uiTreeViewItemHandle *this_handle = reinterpret_cast(this); + /* The new layout hasn't finished construction yet, so the final state of the button is unknown. + * Get the matching button from the previous redraw instead. */ + uiButTreeRow *old_treerow_but = ui_block_view_find_treerow_in_old_block(tree_row_but_->but.block, + this_handle); + return old_treerow_but && (old_treerow_but->but.flag & UI_ACTIVE); +} + bool AbstractTreeViewItem::is_collapsed() const { BLI_assert_msg(get_tree_view().is_reconstructed(), diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 552a0a7acd0..fac38e71220 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -219,7 +219,7 @@ void AssetCatalogTreeViewItem::build_row(uiLayout &row) { ui::BasicTreeViewItem::build_row(row); - if (!is_active()) { + if (!is_hovered()) { return; } From 0194e54fd3276a722ea5c2a8cdff6486c30dcbad Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 6 Oct 2021 16:49:20 +0200 Subject: [PATCH 0584/1500] Fix compilation error with MSVC MSVC does not support variable size array definition. Use maximum possible stack, similar to the GPU case. Not expected to have user-measurable difference. --- .../kernel/integrator/integrator_intersect_volume_stack.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h index 99f6cf35e9e..00e90701d19 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h @@ -42,7 +42,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_A const uint volume_stack_size = kernel_data.volume_stack_size; #ifdef __VOLUME_RECORD_ALL__ - Intersection hits[2 * volume_stack_size + 1]; + Intersection hits[2 * MAX_VOLUME_STACK_SIZE + 1]; uint num_hits = scene_intersect_volume_all( kg, &volume_ray, hits, 2 * volume_stack_size, PATH_RAY_ALL_VISIBILITY); if (num_hits > 0) { @@ -98,11 +98,11 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) const uint volume_stack_size = kernel_data.volume_stack_size; #ifdef __VOLUME_RECORD_ALL__ - Intersection hits[2 * volume_stack_size + 1]; + Intersection hits[2 * MAX_VOLUME_STACK_SIZE + 1]; uint num_hits = scene_intersect_volume_all( kg, &volume_ray, hits, 2 * volume_stack_size, visibility); if (num_hits > 0) { - int enclosed_volumes[volume_stack_size]; + int enclosed_volumes[MAX_VOLUME_STACK_SIZE]; Intersection *isect = hits; qsort(hits, num_hits, sizeof(Intersection), intersections_compare); From b7dc0346aae849d2ba3f81839a6fd682ff24114a Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 6 Oct 2021 16:41:08 +0200 Subject: [PATCH 0585/1500] BMain: Add utils to check if a Main is empty or not. Mostly intended for debug code (asserts). --- source/blender/blenkernel/BKE_main.h | 2 ++ source/blender/blenkernel/intern/main.c | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 93d5b5c5aa6..3108dfd5d8f 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -201,6 +201,8 @@ typedef struct Main { struct Main *BKE_main_new(void); void BKE_main_free(struct Main *mainvar); +bool BKE_main_is_empty(struct Main *bmain); + void BKE_main_lock(struct Main *bmain); void BKE_main_unlock(struct Main *bmain); diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 9c3291edbcc..583feacc04b 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -205,6 +205,17 @@ void BKE_main_free(Main *mainvar) MEM_freeN(mainvar); } +/* Check whether given `bmain` is empty or contains some IDs. */ +bool BKE_main_is_empty(struct Main *bmain) +{ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + return false; + } + FOREACH_MAIN_ID_END; + return true; +} + void BKE_main_lock(struct Main *bmain) { BLI_spin_lock((SpinLock *)bmain->lock); From bbfa6a92cf1c7581a09712401f50c0f0fc02240d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 6 Oct 2021 16:41:47 +0200 Subject: [PATCH 0586/1500] Fix T91987: Linking overrides does not apply overrides rules. Just a matter of calling `BKE_lib_override_library_main_update` in `library_link_end`. --- source/blender/blenloader/intern/readfile.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 3ee5e41bf1f..491322e06df 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -5040,7 +5040,6 @@ static void library_link_end(Main *mainl, add_main_to_main(mainvar, main_newid); } - BKE_main_free(main_newid); blo_join_main((*fd)->mainlist); mainvar = (*fd)->mainlist->first; MEM_freeN((*fd)->mainlist); @@ -5054,6 +5053,15 @@ static void library_link_end(Main *mainl, placeholders_ensure_valid(mainvar); + /* Apply overrides of newly linked data if needed. Already existing IDs need to split out, to + * avoid re-applying their own overrides. */ + BLI_assert(BKE_main_is_empty(main_newid)); + split_main_newid(mainvar, main_newid); + BKE_lib_override_library_main_validate(main_newid, (*fd)->reports->reports); + BKE_lib_override_library_main_update(main_newid); + add_main_to_main(mainvar, main_newid); + BKE_main_free(main_newid); + BKE_main_id_tag_all(mainvar, LIB_TAG_NEW, false); /* Make all relative paths, relative to the open blend file. */ From 8a6f224e260b2ec7e39d2a02fca62f9614049091 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 6 Oct 2021 16:52:16 +0200 Subject: [PATCH 0587/1500] Fix logic error when trying to find hovered item Was just comparing this item's and the parent item's names. But if an item has no parents, only its own name has to match for the check to return true. Make sure that the number of parents also matches. --- source/blender/editors/interface/tree_view.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 7bcf679a5ea..f3070481da2 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -439,6 +439,9 @@ bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem if (!matches(other)) { return false; } + if (count_parents() != other.count_parents()) { + return false; + } for (AbstractTreeViewItem *parent = parent_, *other_parent = other.parent_; parent && other_parent; From 0fd0b0643a7a1c0334f39bddba4067d8fa8eede6 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 6 Oct 2021 16:13:21 +0200 Subject: [PATCH 0588/1500] Build: search for hipcc in HIP_ROOT_DIR/bin --- build_files/cmake/Modules/FindHIP.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build_files/cmake/Modules/FindHIP.cmake b/build_files/cmake/Modules/FindHIP.cmake index c68d78e5796..c331a19eb33 100644 --- a/build_files/cmake/Modules/FindHIP.cmake +++ b/build_files/cmake/Modules/FindHIP.cmake @@ -27,6 +27,8 @@ find_program(HIP_HIPCC_EXECUTABLE hipcc HINTS ${_hip_SEARCH_DIRS} + PATH_SUFFIXES + bin ) if(HIP_HIPCC_EXECUTABLE AND NOT EXISTS ${HIP_HIPCC_EXECUTABLE}) From 04857cc8efb385af5d8f40b655eeca41e2b73494 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 28 Feb 2021 23:23:24 +0100 Subject: [PATCH 0589/1500] Cycles: fully decouple triangle and curve primitive storage from BVH2 Previously the storage here was optimized to avoid indirections in BVH2 traversal. This helps improve performance a bit, but makes performance and memory usage of Embree and OptiX BVHs a bit worse also. It also adds code complexity in other parts of the code. Now decouple triangle and curve primitive storage from BVH2. * Reduced peak memory usage on all devices * Bit better performance for OptiX and Embree * Bit worse performance for CUDA * Simplified code: ** Intersection.prim/object now matches ShaderData.prim/object ** No more offset manipulation for mesh displacement before a BVH is built ** Remove primitive packing code and flags for Embree and OptiX ** Curve segments are now stored in a KernelCurve struct * Also happens to fix a bug in baking with incorrect prim/object Fixes T91968, T91770, T91902 Differential Revision: https://developer.blender.org/D12766 --- intern/cycles/bvh/bvh.h | 4 - intern/cycles/bvh/bvh2.cpp | 61 ----- intern/cycles/bvh/bvh_build.cpp | 36 +-- intern/cycles/bvh/bvh_embree.cpp | 17 +- intern/cycles/device/optix/device_impl.cpp | 13 +- intern/cycles/kernel/bvh/bvh_embree.h | 33 ++- intern/cycles/kernel/bvh/bvh_shadow_all.h | 31 ++- intern/cycles/kernel/bvh/bvh_traversal.h | 24 +- intern/cycles/kernel/bvh/bvh_util.h | 31 +-- intern/cycles/kernel/device/optix/kernel.cu | 86 +++--- intern/cycles/kernel/geom/geom_curve.h | 24 +- .../cycles/kernel/geom/geom_curve_intersect.h | 55 ++-- .../cycles/kernel/geom/geom_motion_triangle.h | 6 +- .../geom/geom_motion_triangle_intersect.h | 17 +- intern/cycles/kernel/geom/geom_shader_data.h | 5 +- intern/cycles/kernel/geom/geom_triangle.h | 30 +-- .../kernel/geom/geom_triangle_intersect.h | 61 ++--- .../integrator/integrator_intersect_closest.h | 5 +- .../integrator/integrator_shade_background.h | 3 +- .../kernel/integrator/integrator_subsurface.h | 2 +- intern/cycles/kernel/kernel_textures.h | 8 +- intern/cycles/kernel/kernel_types.h | 17 +- intern/cycles/kernel/svm/svm_bevel.h | 9 +- intern/cycles/render/geometry.cpp | 244 ++++-------------- intern/cycles/render/geometry.h | 30 +-- intern/cycles/render/hair.cpp | 70 ++--- intern/cycles/render/hair.h | 15 +- intern/cycles/render/mesh.cpp | 69 +---- intern/cycles/render/mesh.h | 14 +- intern/cycles/render/object.cpp | 4 + intern/cycles/render/scene.cpp | 4 +- intern/cycles/render/scene.h | 6 +- 32 files changed, 364 insertions(+), 670 deletions(-) diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h index 94935c26f10..d9e2ad9526c 100644 --- a/intern/cycles/bvh/bvh.h +++ b/intern/cycles/bvh/bvh.h @@ -50,10 +50,6 @@ struct PackedBVH { array leaf_nodes; /* object index to BVH node index mapping for instances */ array object_node; - /* Mapping from primitive index to index in triangle array. */ - array prim_tri_index; - /* Continuous storage of triangle vertices. */ - array prim_tri_verts; /* primitive type - triangle or strand */ array prim_type; /* visibility visibilitys for primitives */ diff --git a/intern/cycles/bvh/bvh2.cpp b/intern/cycles/bvh/bvh2.cpp index 379ae9b25ff..4a90a1e8796 100644 --- a/intern/cycles/bvh/bvh2.cpp +++ b/intern/cycles/bvh/bvh2.cpp @@ -439,61 +439,20 @@ void BVH2::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility /* Triangles */ -void BVH2::pack_triangle(int idx, float4 tri_verts[3]) -{ - int tob = pack.prim_object[idx]; - assert(tob >= 0 && tob < objects.size()); - const Mesh *mesh = static_cast(objects[tob]->get_geometry()); - - int tidx = pack.prim_index[idx]; - Mesh::Triangle t = mesh->get_triangle(tidx); - const float3 *vpos = &mesh->verts[0]; - float3 v0 = vpos[t.v[0]]; - float3 v1 = vpos[t.v[1]]; - float3 v2 = vpos[t.v[2]]; - - tri_verts[0] = float3_to_float4(v0); - tri_verts[1] = float3_to_float4(v1); - tri_verts[2] = float3_to_float4(v2); -} - void BVH2::pack_primitives() { const size_t tidx_size = pack.prim_index.size(); - size_t num_prim_triangles = 0; - /* Count number of triangles primitives in BVH. */ - for (unsigned int i = 0; i < tidx_size; i++) { - if ((pack.prim_index[i] != -1)) { - if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { - ++num_prim_triangles; - } - } - } /* Reserve size for arrays. */ - pack.prim_tri_index.clear(); - pack.prim_tri_index.resize(tidx_size); - pack.prim_tri_verts.clear(); - pack.prim_tri_verts.resize(num_prim_triangles * 3); pack.prim_visibility.clear(); pack.prim_visibility.resize(tidx_size); /* Fill in all the arrays. */ - size_t prim_triangle_index = 0; for (unsigned int i = 0; i < tidx_size; i++) { if (pack.prim_index[i] != -1) { int tob = pack.prim_object[i]; Object *ob = objects[tob]; - if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { - pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]); - pack.prim_tri_index[i] = 3 * prim_triangle_index; - ++prim_triangle_index; - } - else { - pack.prim_tri_index[i] = -1; - } pack.prim_visibility[i] = ob->visibility_for_tracing(); } else { - pack.prim_tri_index[i] = -1; pack.prim_visibility[i] = 0; } } @@ -522,10 +481,8 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) /* reserve */ size_t prim_index_size = pack.prim_index.size(); - size_t prim_tri_verts_size = pack.prim_tri_verts.size(); size_t pack_prim_index_offset = prim_index_size; - size_t pack_prim_tri_verts_offset = prim_tri_verts_size; size_t pack_nodes_offset = nodes_size; size_t pack_leaf_nodes_offset = leaf_nodes_size; size_t object_offset = 0; @@ -535,7 +492,6 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) if (geom->need_build_bvh(params.bvh_layout)) { prim_index_size += bvh->pack.prim_index.size(); - prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); nodes_size += bvh->pack.nodes.size(); leaf_nodes_size += bvh->pack.leaf_nodes.size(); } @@ -545,8 +501,6 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) pack.prim_type.resize(prim_index_size); pack.prim_object.resize(prim_index_size); pack.prim_visibility.resize(prim_index_size); - pack.prim_tri_verts.resize(prim_tri_verts_size); - pack.prim_tri_index.resize(prim_index_size); pack.nodes.resize(nodes_size); pack.leaf_nodes.resize(leaf_nodes_size); pack.object_node.resize(objects.size()); @@ -559,8 +513,6 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL; int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL; uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL; - float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL; - uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL; int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL; int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL; float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL; @@ -609,18 +561,14 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) int *bvh_prim_index = &bvh->pack.prim_index[0]; int *bvh_prim_type = &bvh->pack.prim_type[0]; uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0]; - uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0]; float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL; for (size_t i = 0; i < bvh_prim_index_size; i++) { if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_prim_index_offset] = -1; } else { pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] + - pack_prim_tri_verts_offset; } pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i]; @@ -633,15 +581,6 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) } } - /* Merge triangle vertices data. */ - if (bvh->pack.prim_tri_verts.size()) { - const size_t prim_tri_size = bvh->pack.prim_tri_verts.size(); - memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset, - &bvh->pack.prim_tri_verts[0], - prim_tri_size * sizeof(float4)); - pack_prim_tri_verts_offset += prim_tri_size; - } - /* merge nodes */ if (bvh->pack.leaf_nodes.size()) { int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0]; diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp index d3497f3a8d8..025a103d6f8 100644 --- a/intern/cycles/bvh/bvh_build.cpp +++ b/intern/cycles/bvh/bvh_build.cpp @@ -67,8 +67,12 @@ BVHBuild::~BVHBuild() /* Adding References */ -void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *mesh, int i) +void BVHBuild::add_reference_triangles(BoundBox &root, + BoundBox ¢er, + Mesh *mesh, + int object_index) { + const PrimitiveType primitive_type = mesh->primitive_type(); const Attribute *attr_mP = NULL; if (mesh->has_motion_blur()) { attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); @@ -81,7 +85,7 @@ void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *m BoundBox bounds = BoundBox::empty; t.bounds_grow(verts, bounds); if (bounds.valid() && t.valid(verts)) { - references.push_back(BVHReference(bounds, j, i, PRIMITIVE_TRIANGLE)); + references.push_back(BVHReference(bounds, j, object_index, primitive_type)); root.grow(bounds); center.grow(bounds.center2()); } @@ -101,7 +105,7 @@ void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *m t.bounds_grow(vert_steps + step * num_verts, bounds); } if (bounds.valid()) { - references.push_back(BVHReference(bounds, j, i, PRIMITIVE_MOTION_TRIANGLE)); + references.push_back(BVHReference(bounds, j, object_index, primitive_type)); root.grow(bounds); center.grow(bounds.center2()); } @@ -140,7 +144,7 @@ void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *m if (bounds.valid()) { const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1; references.push_back( - BVHReference(bounds, j, i, PRIMITIVE_MOTION_TRIANGLE, prev_time, curr_time)); + BVHReference(bounds, j, object_index, primitive_type, prev_time, curr_time)); root.grow(bounds); center.grow(bounds.center2()); } @@ -153,18 +157,14 @@ void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *m } } -void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair, int i) +void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair, int object_index) { const Attribute *curve_attr_mP = NULL; if (hair->has_motion_blur()) { curve_attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); } - const PrimitiveType primitive_type = - (curve_attr_mP != NULL) ? - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : - PRIMITIVE_MOTION_CURVE_THICK) : - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK); + const PrimitiveType primitive_type = hair->primitive_type(); const size_t num_curves = hair->num_curves(); for (uint j = 0; j < num_curves; j++) { @@ -177,7 +177,7 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair curve.bounds_grow(k, &hair->get_curve_keys()[0], curve_radius, bounds); if (bounds.valid()) { int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k); - references.push_back(BVHReference(bounds, j, i, packed_type)); + references.push_back(BVHReference(bounds, j, object_index, packed_type)); root.grow(bounds); center.grow(bounds.center2()); } @@ -198,7 +198,7 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair } if (bounds.valid()) { int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k); - references.push_back(BVHReference(bounds, j, i, packed_type)); + references.push_back(BVHReference(bounds, j, object_index, packed_type)); root.grow(bounds); center.grow(bounds.center2()); } @@ -254,7 +254,8 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair if (bounds.valid()) { const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1; int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k); - references.push_back(BVHReference(bounds, j, i, packed_type, prev_time, curr_time)); + references.push_back( + BVHReference(bounds, j, object_index, packed_type, prev_time, curr_time)); root.grow(bounds); center.grow(bounds.center2()); } @@ -268,15 +269,18 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair } } -void BVHBuild::add_reference_geometry(BoundBox &root, BoundBox ¢er, Geometry *geom, int i) +void BVHBuild::add_reference_geometry(BoundBox &root, + BoundBox ¢er, + Geometry *geom, + int object_index) { if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { Mesh *mesh = static_cast(geom); - add_reference_triangles(root, center, mesh, i); + add_reference_triangles(root, center, mesh, object_index); } else if (geom->geometry_type == Geometry::HAIR) { Hair *hair = static_cast(geom); - add_reference_curves(root, center, hair, i); + add_reference_curves(root, center, hair, object_index); } } diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index eebc1e1e547..8c1ca1f5b38 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -136,10 +136,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) } else { kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - int object = (current_isect.object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, current_isect.prim) : - current_isect.object; - if (ctx->local_object_id != object) { + if (ctx->local_object_id != current_isect.object) { /* This tells Embree to continue tracing. */ *args->valid = 0; break; @@ -206,9 +203,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) ++ctx->num_hits; *isect = current_isect; /* Only primitives from volume object. */ - uint tri_object = (isect->object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, isect->prim) : - isect->object; + uint tri_object = isect->object; int object_flag = kernel_tex_fetch(__object_flag, tri_object); if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { --ctx->num_hits; @@ -437,7 +432,7 @@ void BVHEmbree::add_instance(Object *ob, int i) void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i) { - size_t prim_offset = mesh->optix_prim_offset; + size_t prim_offset = mesh->prim_offset; const Attribute *attr_mP = NULL; size_t num_motion_steps = 1; @@ -606,7 +601,7 @@ void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, c void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) { - size_t prim_offset = hair->optix_prim_offset; + size_t prim_offset = hair->curve_segment_offset; const Attribute *attr_mP = NULL; size_t num_motion_steps = 1; @@ -683,7 +678,7 @@ void BVHEmbree::refit(Progress &progress) if (mesh->num_triangles() > 0) { RTCGeometry geom = rtcGetGeometry(scene, geom_id); set_tri_vertex_buffer(geom, mesh, true); - rtcSetGeometryUserData(geom, (void *)mesh->optix_prim_offset); + rtcSetGeometryUserData(geom, (void *)mesh->prim_offset); rtcCommitGeometry(geom); } } @@ -692,7 +687,7 @@ void BVHEmbree::refit(Progress &progress) if (hair->num_curves() > 0) { RTCGeometry geom = rtcGetGeometry(scene, geom_id + 1); set_curve_vertex_buffer(geom, hair, true); - rtcSetGeometryUserData(geom, (void *)hair->optix_prim_offset); + rtcSetGeometryUserData(geom, (void *)hair->curve_segment_offset); rtcCommitGeometry(geom); } } diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index f9a15553aa9..89f4b696b4c 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -1252,7 +1252,7 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) build_input.curveArray.indexBuffer = (CUdeviceptr)index_data.device_pointer; build_input.curveArray.indexStrideInBytes = sizeof(int); build_input.curveArray.flag = build_flags; - build_input.curveArray.primitiveIndexOffset = hair->optix_prim_offset; + build_input.curveArray.primitiveIndexOffset = hair->curve_segment_offset; } else { /* Disable visibility test any-hit program, since it is already checked during @@ -1265,7 +1265,7 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) build_input.customPrimitiveArray.strideInBytes = sizeof(OptixAabb); build_input.customPrimitiveArray.flags = &build_flags; build_input.customPrimitiveArray.numSbtRecords = 1; - build_input.customPrimitiveArray.primitiveIndexOffset = hair->optix_prim_offset; + build_input.customPrimitiveArray.primitiveIndexOffset = hair->curve_segment_offset; } if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { @@ -1334,7 +1334,7 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) * buffers for that purpose. OptiX does not allow this to be zero though, so just pass in * one and rely on that having the same meaning in this case. */ build_input.triangleArray.numSbtRecords = 1; - build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset; + build_input.triangleArray.primitiveIndexOffset = mesh->prim_offset; if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { progress.set_error("Failed to build OptiX acceleration structure"); @@ -1401,8 +1401,8 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) instance.transform[5] = 1.0f; instance.transform[10] = 1.0f; - /* Set user instance ID to object index (but leave low bit blank). */ - instance.instanceId = ob->get_device_index() << 1; + /* Set user instance ID to object index. */ + instance.instanceId = ob->get_device_index(); /* Add some of the object visibility bits to the mask. * __prim_visibility contains the combined visibility bits of all instances, so is not @@ -1514,9 +1514,6 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) else { /* Disable instance transform if geometry already has it applied to vertex data. */ instance.flags |= OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; - /* Non-instanced objects read ID from 'prim_object', so distinguish - * them from instanced objects with the low bit set. */ - instance.instanceId |= 1; } } } diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/bvh_embree.h index 092d770dcac..d3db6295ea5 100644 --- a/intern/cycles/kernel/bvh/bvh_embree.h +++ b/intern/cycles/kernel/bvh/bvh_embree.h @@ -106,9 +106,6 @@ ccl_device_inline void kernel_embree_convert_hit(const KernelGlobals *kg, const RTCHit *hit, Intersection *isect) { - bool is_hair = hit->geomID & 1; - isect->u = is_hair ? hit->u : 1.0f - hit->v - hit->u; - isect->v = is_hair ? hit->v : hit->u; isect->t = ray->tfar; isect->Ng = make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z); if (hit->instID[0] != RTC_INVALID_GEOMETRY_ID) { @@ -121,27 +118,37 @@ ccl_device_inline void kernel_embree_convert_hit(const KernelGlobals *kg, else { isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData( rtcGetGeometry(kernel_data.bvh.scene, hit->geomID)); - isect->object = OBJECT_NONE; + isect->object = hit->geomID / 2; + } + + const bool is_hair = hit->geomID & 1; + if (is_hair) { + const KernelCurveSegment segment = kernel_tex_fetch(__curve_segments, isect->prim); + isect->type = segment.type; + isect->prim = segment.prim; + isect->u = hit->u; + isect->v = hit->v; + } + else { + isect->type = kernel_tex_fetch(__objects, isect->object).primitive_type; + isect->u = 1.0f - hit->v - hit->u; + isect->v = hit->u; } - isect->type = kernel_tex_fetch(__prim_type, isect->prim); } -ccl_device_inline void kernel_embree_convert_sss_hit(const KernelGlobals *kg, - const RTCRay *ray, - const RTCHit *hit, - Intersection *isect, - int local_object_id) +ccl_device_inline void kernel_embree_convert_sss_hit( + const KernelGlobals *kg, const RTCRay *ray, const RTCHit *hit, Intersection *isect, int object) { isect->u = 1.0f - hit->v - hit->u; isect->v = hit->u; isect->t = ray->tfar; isect->Ng = make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z); RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, local_object_id * 2)); + rtcGetGeometry(kernel_data.bvh.scene, object * 2)); isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); - isect->object = local_object_id; - isect->type = kernel_tex_fetch(__prim_type, isect->prim); + isect->object = object; + isect->type = kernel_tex_fetch(__objects, object).primitive_type; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index 0ae36fccf9b..82c7c1a8a6c 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -130,7 +130,6 @@ ccl_device_inline if (prim_addr >= 0) { const int prim_addr2 = __float_as_int(leaf.y); const uint type = __float_as_int(leaf.w); - const uint p_type = type & PRIMITIVE_ALL; /* pop */ node_addr = traversal_stack[stack_ptr]; @@ -138,14 +137,15 @@ ccl_device_inline /* primitive intersection */ while (prim_addr < prim_addr2) { - kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) == p_type); + kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) == + (type & PRIMITIVE_ALL)); bool hit; /* todo: specialized intersect functions which don't fill in * isect unless needed and check SD_HAS_TRANSPARENT_SHADOW? * might give a few % performance improvement */ - switch (p_type) { + switch (type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { hit = triangle_intersect( kg, isect, P, dir, isect_t, visibility, object, prim_addr); @@ -163,17 +163,20 @@ ccl_device_inline case PRIMITIVE_MOTION_CURVE_THICK: case PRIMITIVE_CURVE_RIBBON: case PRIMITIVE_MOTION_CURVE_RIBBON: { - const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); - hit = curve_intersect(kg, - isect, - P, - dir, - isect_t, - visibility, - object, - prim_addr, - ray->time, - curve_type); + if ((type & PRIMITIVE_ALL_MOTION) && kernel_data.bvh.use_bvh_steps) { + const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr); + if (ray->time < prim_time.x || ray->time > prim_time.y) { + hit = false; + break; + } + } + + const int curve_object = kernel_tex_fetch(__prim_object, prim_addr); + const int curve_type = kernel_tex_fetch(__prim_type, prim_addr); + const int curve_prim = kernel_tex_fetch(__prim_index, prim_addr); + hit = curve_intersect( + kg, isect, P, dir, isect_t, curve_object, curve_prim, ray->time, curve_type); + break; } #endif diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/bvh_traversal.h index a26d8c514f3..2feff593c10 100644 --- a/intern/cycles/kernel/bvh/bvh_traversal.h +++ b/intern/cycles/kernel/bvh/bvh_traversal.h @@ -165,18 +165,18 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, case PRIMITIVE_CURVE_RIBBON: case PRIMITIVE_MOTION_CURVE_RIBBON: { for (; prim_addr < prim_addr2; prim_addr++) { - const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); - kernel_assert((curve_type & PRIMITIVE_ALL) == (type & PRIMITIVE_ALL)); - const bool hit = curve_intersect(kg, - isect, - P, - dir, - isect->t, - visibility, - object, - prim_addr, - ray->time, - curve_type); + if ((type & PRIMITIVE_ALL_MOTION) && kernel_data.bvh.use_bvh_steps) { + const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr); + if (ray->time < prim_time.x || ray->time > prim_time.y) { + continue; + } + } + + const int curve_object = kernel_tex_fetch(__prim_object, prim_addr); + const int curve_prim = kernel_tex_fetch(__prim_index, prim_addr); + const int curve_type = kernel_tex_fetch(__prim_type, prim_addr); + const bool hit = curve_intersect( + kg, isect, P, dir, isect->t, curve_object, curve_prim, ray->time, curve_type); if (hit) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index 21384457b16..9f188a93e2c 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -118,19 +118,18 @@ ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits) ccl_device_forceinline int intersection_get_shader_flags(const KernelGlobals *ccl_restrict kg, const Intersection *ccl_restrict isect) { - const int prim = kernel_tex_fetch(__prim_index, isect->prim); + const int prim = isect->prim; int shader = 0; #ifdef __HAIR__ - if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) + if (isect->type & PRIMITIVE_ALL_TRIANGLE) #endif { shader = kernel_tex_fetch(__tri_shader, prim); } #ifdef __HAIR__ else { - float4 str = kernel_tex_fetch(__curves, prim); - shader = __float_as_int(str.z); + shader = kernel_tex_fetch(__curves, prim).shader_id; } #endif @@ -138,21 +137,19 @@ ccl_device_forceinline int intersection_get_shader_flags(const KernelGlobals *cc } ccl_device_forceinline int intersection_get_shader_from_isect_prim( - const KernelGlobals *ccl_restrict kg, const int isect_prim) + const KernelGlobals *ccl_restrict kg, const int prim, const int isect_type) { - const int prim = kernel_tex_fetch(__prim_index, isect_prim); int shader = 0; #ifdef __HAIR__ - if (kernel_tex_fetch(__prim_type, isect_prim) & PRIMITIVE_ALL_TRIANGLE) + if (isect_type & PRIMITIVE_ALL_TRIANGLE) #endif { shader = kernel_tex_fetch(__tri_shader, prim); } #ifdef __HAIR__ else { - float4 str = kernel_tex_fetch(__curves, prim); - shader = __float_as_int(str.z); + shader = kernel_tex_fetch(__curves, prim).shader_id; } #endif @@ -162,25 +159,13 @@ ccl_device_forceinline int intersection_get_shader_from_isect_prim( ccl_device_forceinline int intersection_get_shader(const KernelGlobals *ccl_restrict kg, const Intersection *ccl_restrict isect) { - return intersection_get_shader_from_isect_prim(kg, isect->prim); -} - -ccl_device_forceinline int intersection_get_object(const KernelGlobals *ccl_restrict kg, - const Intersection *ccl_restrict isect) -{ - if (isect->object != OBJECT_NONE) { - return isect->object; - } - - return kernel_tex_fetch(__prim_object, isect->prim); + return intersection_get_shader_from_isect_prim(kg, isect->prim, isect->type); } ccl_device_forceinline int intersection_get_object_flags(const KernelGlobals *ccl_restrict kg, const Intersection *ccl_restrict isect) { - const int object = intersection_get_object(kg, isect); - - return kernel_tex_fetch(__object_flag, object); + return kernel_tex_fetch(__object_flag, isect->object); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu index 7a79e0c4823..736f30d93ef 100644 --- a/intern/cycles/kernel/device/optix/kernel.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -41,22 +41,15 @@ template ccl_device_forceinline T *get_payload_ptr_2() return (T *)(((uint64_t)optixGetPayload_3() << 32) | optixGetPayload_2()); } -template ccl_device_forceinline uint get_object_id() +ccl_device_forceinline int get_object_id() { #ifdef __OBJECT_MOTION__ - /* Always get the the instance ID from the TLAS. + /* Always get the the instance ID from the TLAS * There might be a motion transform node between TLAS and BLAS which does not have one. */ - uint object = optixGetInstanceIdFromHandle(optixGetTransformListHandle(0)); + return optixGetInstanceIdFromHandle(optixGetTransformListHandle(0)); #else - uint object = optixGetInstanceId(); + return optixGetInstanceId(); #endif - /* Choose between always returning object ID or only for instances. */ - if (always || (object & 1) == 0) - /* Can just remove the low bit since instance always contains object ID. */ - return object >> 1; - else - /* Set to OBJECT_NONE if this is not an instanced object. */ - return OBJECT_NONE; } extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_closest() @@ -108,7 +101,7 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit() #endif #ifdef __BVH_LOCAL__ - const uint object = get_object_id(); + const int object = get_object_id(); if (object != optixGetPayload_4() /* local_object */) { /* Only intersect with matching object. */ return optixIgnoreIntersection(); @@ -152,21 +145,23 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit() local_isect->num_hits = 1; } + const int prim = optixGetPrimitiveIndex(); + Intersection *isect = &local_isect->hits[hit]; isect->t = optixGetRayTmax(); - isect->prim = optixGetPrimitiveIndex(); + isect->prim = prim; isect->object = get_object_id(); - isect->type = kernel_tex_fetch(__prim_type, isect->prim); + isect->type = kernel_tex_fetch(__objects, isect->object).primitive_type; const float2 barycentrics = optixGetTriangleBarycentrics(); isect->u = 1.0f - barycentrics.y - barycentrics.x; isect->v = barycentrics.x; /* Record geometric normal. */ - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect->prim); - const float3 tri_a = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0)); - const float3 tri_b = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1)); - const float3 tri_c = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2)); + const uint tri_vindex = kernel_tex_fetch(__tri_vindex, prim).w; + const float3 tri_a = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 0)); + const float3 tri_b = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 1)); + const float3 tri_c = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 2)); local_isect->Ng[hit] = normalize(cross(tri_b - tri_a, tri_c - tri_a)); /* Continue tracing (without this the trace call would return after the first hit). */ @@ -179,25 +174,32 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() #ifdef __SHADOW_RECORD_ALL__ bool ignore_intersection = false; - const uint prim = optixGetPrimitiveIndex(); + int prim = optixGetPrimitiveIndex(); + const uint object = get_object_id(); # ifdef __VISIBILITY_FLAG__ const uint visibility = optixGetPayload_4(); - if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) { + if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { ignore_intersection = true; } # endif float u = 0.0f, v = 0.0f; + int type = 0; if (optixIsTriangleHit()) { const float2 barycentrics = optixGetTriangleBarycentrics(); u = 1.0f - barycentrics.y - barycentrics.x; v = barycentrics.x; + type = kernel_tex_fetch(__objects, object).primitive_type; } # ifdef __HAIR__ else { u = __uint_as_float(optixGetAttribute_0()); v = __uint_as_float(optixGetAttribute_1()); + const KernelCurveSegment segment = kernel_tex_fetch(__curve_segments, prim); + type = segment.type; + prim = segment.prim; + /* Filter out curve endcaps. */ if (u == 0.0f || u == 1.0f) { ignore_intersection = true; @@ -245,8 +247,8 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() isect->v = v; isect->t = optixGetRayTmax(); isect->prim = prim; - isect->object = get_object_id(); - isect->type = kernel_tex_fetch(__prim_type, prim); + isect->object = object; + isect->type = type; # ifdef __TRANSPARENT_SHADOWS__ /* Detect if this surface has a shader with transparent shadows. */ @@ -274,15 +276,14 @@ extern "C" __global__ void __anyhit__kernel_optix_volume_test() } #endif + const uint object = get_object_id(); #ifdef __VISIBILITY_FLAG__ - const uint prim = optixGetPrimitiveIndex(); const uint visibility = optixGetPayload_4(); - if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) { + if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { return optixIgnoreIntersection(); } #endif - const uint object = get_object_id(); if ((kernel_tex_fetch(__object_flag, object) & SD_OBJECT_HAS_VOLUME) == 0) { return optixIgnoreIntersection(); } @@ -301,9 +302,9 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test() #endif #ifdef __VISIBILITY_FLAG__ - const uint prim = optixGetPrimitiveIndex(); + const uint object = get_object_id(); const uint visibility = optixGetPayload_4(); - if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) { + if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { return optixIgnoreIntersection(); } @@ -316,28 +317,39 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test() extern "C" __global__ void __closesthit__kernel_optix_hit() { + const int object = get_object_id(); + const int prim = optixGetPrimitiveIndex(); + optixSetPayload_0(__float_as_uint(optixGetRayTmax())); /* Intersection distance */ - optixSetPayload_3(optixGetPrimitiveIndex()); - optixSetPayload_4(get_object_id()); - /* Can be PRIMITIVE_TRIANGLE and PRIMITIVE_MOTION_TRIANGLE or curve type and segment index. */ - optixSetPayload_5(kernel_tex_fetch(__prim_type, optixGetPrimitiveIndex())); + optixSetPayload_4(object); if (optixIsTriangleHit()) { const float2 barycentrics = optixGetTriangleBarycentrics(); optixSetPayload_1(__float_as_uint(1.0f - barycentrics.y - barycentrics.x)); optixSetPayload_2(__float_as_uint(barycentrics.x)); + optixSetPayload_3(prim); + optixSetPayload_5(kernel_tex_fetch(__objects, object).primitive_type); } else { + const KernelCurveSegment segment = kernel_tex_fetch(__curve_segments, prim); optixSetPayload_1(optixGetAttribute_0()); /* Same as 'optixGetCurveParameter()' */ optixSetPayload_2(optixGetAttribute_1()); + optixSetPayload_3(segment.prim); + optixSetPayload_5(segment.type); } } #ifdef __HAIR__ -ccl_device_inline void optix_intersection_curve(const uint prim, const uint type) +ccl_device_inline void optix_intersection_curve(const int prim, const int type) { - const uint object = get_object_id(); + const int object = get_object_id(); + +# ifdef __VISIBILITY_FLAG__ const uint visibility = optixGetPayload_4(); + if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { + return; + } +# endif float3 P = optixGetObjectRayOrigin(); float3 dir = optixGetObjectRayDirection(); @@ -358,7 +370,7 @@ ccl_device_inline void optix_intersection_curve(const uint prim, const uint type if (isect.t != FLT_MAX) isect.t *= len; - if (curve_intersect(NULL, &isect, P, dir, isect.t, visibility, object, prim, time, type)) { + if (curve_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { optixReportIntersection(isect.t / len, type & PRIMITIVE_ALL, __float_as_int(isect.u), /* Attribute_0 */ @@ -368,9 +380,9 @@ ccl_device_inline void optix_intersection_curve(const uint prim, const uint type extern "C" __global__ void __intersection__curve_ribbon() { - const uint prim = optixGetPrimitiveIndex(); - const uint type = kernel_tex_fetch(__prim_type, prim); - + const KernelCurveSegment segment = kernel_tex_fetch(__curve_segments, optixGetPrimitiveIndex()); + const int prim = segment.prim; + const int type = segment.type; if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) { optix_intersection_curve(prim, type); } diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h index a827a67ce7a..811558edae9 100644 --- a/intern/cycles/kernel/geom/geom_curve.h +++ b/intern/cycles/kernel/geom/geom_curve.h @@ -34,8 +34,8 @@ ccl_device float curve_attribute_float(const KernelGlobals *kg, float *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float f0 = kernel_tex_fetch(__attributes_float, desc.offset + k0); @@ -76,8 +76,8 @@ ccl_device float2 curve_attribute_float2(const KernelGlobals *kg, float2 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float2 f0 = kernel_tex_fetch(__attributes_float2, desc.offset + k0); @@ -122,8 +122,8 @@ ccl_device float3 curve_attribute_float3(const KernelGlobals *kg, float3 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + k0)); @@ -164,8 +164,8 @@ ccl_device float4 curve_attribute_float4(const KernelGlobals *kg, float4 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float4 f0 = kernel_tex_fetch(__attributes_float3, desc.offset + k0); @@ -206,8 +206,8 @@ ccl_device float curve_thickness(const KernelGlobals *kg, const ShaderData *sd) float r = 0.0f; if (sd->type & PRIMITIVE_ALL_CURVE) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float4 P_curve[2]; @@ -231,8 +231,8 @@ ccl_device float curve_thickness(const KernelGlobals *kg, const ShaderData *sd) ccl_device float3 curve_motion_center_location(const KernelGlobals *kg, const ShaderData *sd) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float4 P_curve[2]; diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h index a068e93790a..30addb9616d 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/geom_curve_intersect.h @@ -630,33 +630,19 @@ ccl_device_forceinline bool curve_intersect(const KernelGlobals *kg, const float3 P, const float3 dir, const float tmax, - uint visibility, int object, - int curveAddr, + int prim, float time, int type) { const bool is_motion = (type & PRIMITIVE_ALL_MOTION); -# ifndef __KERNEL_OPTIX__ /* See OptiX motion flag OPTIX_MOTION_FLAG_[START|END]_VANISH */ - if (is_motion && kernel_data.bvh.use_bvh_steps) { - const float2 prim_time = kernel_tex_fetch(__prim_time, curveAddr); - if (time < prim_time.x || time > prim_time.y) { - return false; - } - } -# endif + KernelCurve kcurve = kernel_tex_fetch(__curves, prim); - int segment = PRIMITIVE_UNPACK_SEGMENT(type); - int prim = kernel_tex_fetch(__prim_index, curveAddr); - - float4 v00 = kernel_tex_fetch(__curves, prim); - - int k0 = __float_as_int(v00.x) + segment; + int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(type); int k1 = k0 + 1; - - int ka = max(k0 - 1, __float_as_int(v00.x)); - int kb = min(k1 + 1, __float_as_int(v00.x) + __float_as_int(v00.y) - 1); + int ka = max(k0 - 1, kcurve.first_key); + int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1); float4 curve[4]; if (!is_motion) { @@ -666,21 +652,14 @@ ccl_device_forceinline bool curve_intersect(const KernelGlobals *kg, curve[3] = kernel_tex_fetch(__curve_keys, kb); } else { - int fobject = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, curveAddr) : object; - motion_curve_keys(kg, fobject, prim, time, ka, k0, k1, kb, curve); + motion_curve_keys(kg, object, prim, time, ka, k0, k1, kb, curve); } -# ifdef __VISIBILITY_FLAG__ - if (!(kernel_tex_fetch(__prim_visibility, curveAddr) & visibility)) { - return false; - } -# endif - if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) { /* todo: adaptive number of subdivisions could help performance here. */ const int subdivisions = kernel_data.bvh.curve_subdivisions; if (ribbon_intersect(P, dir, tmax, subdivisions, curve, isect)) { - isect->prim = curveAddr; + isect->prim = prim; isect->object = object; isect->type = type; return true; @@ -690,7 +669,7 @@ ccl_device_forceinline bool curve_intersect(const KernelGlobals *kg, } else { if (curve_intersect_recursive(P, dir, tmax, curve, isect)) { - isect->prim = curveAddr; + isect->prim = prim; isect->object = object; isect->type = type; return true; @@ -708,7 +687,7 @@ ccl_device_inline void curve_shader_setup(const KernelGlobals *kg, const int isect_object, const int isect_prim) { - if (isect_object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); @@ -716,14 +695,12 @@ ccl_device_inline void curve_shader_setup(const KernelGlobals *kg, D = safe_normalize_len(D, &t); } - int prim = kernel_tex_fetch(__prim_index, isect_prim); - float4 v00 = kernel_tex_fetch(__curves, prim); + KernelCurve kcurve = kernel_tex_fetch(__curves, isect_prim); - int k0 = __float_as_int(v00.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; - - int ka = max(k0 - 1, __float_as_int(v00.x)); - int kb = min(k1 + 1, __float_as_int(v00.x) + __float_as_int(v00.y) - 1); + int ka = max(k0 - 1, kcurve.first_key); + int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1); float4 P_curve[4]; @@ -780,15 +757,13 @@ ccl_device_inline void curve_shader_setup(const KernelGlobals *kg, sd->dPdv = cross(dPdu, sd->Ng); # endif - if (isect_object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } sd->P = P; - - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - sd->shader = __float_as_int(curvedata.z); + sd->shader = kernel_tex_fetch(__curves, sd->prim).shader_id; } #endif diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/geom_motion_triangle.h index 239bd0a37b2..b7f182090aa 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle.h @@ -72,9 +72,9 @@ ccl_device_inline void motion_triangle_verts_for_step(const KernelGlobals *kg, { if (step == numsteps) { /* center step: regular vertex location */ - verts[0] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - verts[1] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - verts[2] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + verts[0] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + verts[1] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + verts[2] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); } else { /* center step not store in this array */ diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h index ec7e4b07d76..6fb9756ff92 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h @@ -44,7 +44,7 @@ ccl_device_inline float3 motion_triangle_refine(const KernelGlobals *kg, float3 verts[3]) { #ifdef __INTERSECTION_REFINE__ - if (isect_object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { if (UNLIKELY(t == 0.0f)) { return P; } @@ -70,7 +70,7 @@ ccl_device_inline float3 motion_triangle_refine(const KernelGlobals *kg, /* Compute refined position. */ P = P + D * rt; - if (isect_object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } @@ -106,7 +106,7 @@ ccl_device_inline return motion_triangle_refine(kg, sd, P, D, t, isect_object, isect_prim, verts); # else # ifdef __INTERSECTION_REFINE__ - if (isect_object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); @@ -128,7 +128,7 @@ ccl_device_inline P = P + D * rt; - if (isect_object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } @@ -186,8 +186,9 @@ ccl_device_inline bool motion_triangle_intersect(const KernelGlobals *kg, isect->t = t; isect->u = u; isect->v = v; - isect->prim = prim_addr; - isect->object = object; + isect->prim = prim; + isect->object = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, prim_addr) : + object; isect->type = PRIMITIVE_MOTION_TRIANGLE; return true; } @@ -288,8 +289,8 @@ ccl_device_inline bool motion_triangle_intersect_local(const KernelGlobals *kg, isect->t = t; isect->u = u; isect->v = v; - isect->prim = prim_addr; - isect->object = object; + isect->prim = prim; + isect->object = local_object; isect->type = PRIMITIVE_MOTION_TRIANGLE; /* Record geometric normal. */ diff --git a/intern/cycles/kernel/geom/geom_shader_data.h b/intern/cycles/kernel/geom/geom_shader_data.h index 5dc03940238..f78d194359d 100644 --- a/intern/cycles/kernel/geom/geom_shader_data.h +++ b/intern/cycles/kernel/geom/geom_shader_data.h @@ -52,10 +52,9 @@ ccl_device_inline void shader_setup_from_ray(const KernelGlobals *ccl_restrict k sd->v = isect->v; sd->ray_length = isect->t; sd->type = isect->type; - sd->object = (isect->object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, isect->prim) : - isect->object; + sd->object = isect->object; sd->object_flag = kernel_tex_fetch(__object_flag, sd->object); - sd->prim = kernel_tex_fetch(__prim_index, isect->prim); + sd->prim = isect->prim; sd->lamp = LAMP_NONE; sd->flag = 0; diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/geom_triangle.h index 910fb122c6d..8edba46fd39 100644 --- a/intern/cycles/kernel/geom/geom_triangle.h +++ b/intern/cycles/kernel/geom/geom_triangle.h @@ -29,9 +29,9 @@ ccl_device_inline float3 triangle_normal(const KernelGlobals *kg, ShaderData *sd { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, sd->prim); - const float3 v0 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - const float3 v1 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - const float3 v2 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + const float3 v0 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + const float3 v1 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + const float3 v2 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); /* return normal */ if (sd->object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { @@ -54,9 +54,9 @@ ccl_device_inline void triangle_point_normal(const KernelGlobals *kg, { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); - float3 v0 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - float3 v1 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - float3 v2 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + float3 v0 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + float3 v1 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + float3 v2 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); /* compute point */ float t = 1.0f - u - v; *P = (u * v0 + v * v1 + t * v2); @@ -78,9 +78,9 @@ ccl_device_inline void triangle_point_normal(const KernelGlobals *kg, ccl_device_inline void triangle_vertices(const KernelGlobals *kg, int prim, float3 P[3]) { const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); - P[0] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - P[1] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - P[2] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + P[0] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + P[1] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + P[2] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); } /* Triangle vertex locations and vertex normals */ @@ -91,9 +91,9 @@ ccl_device_inline void triangle_vertices_and_normals(const KernelGlobals *kg, float3 N[3]) { const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); - P[0] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - P[1] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - P[2] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + P[0] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + P[1] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + P[2] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); N[0] = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.x)); N[1] = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.y)); N[2] = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.z)); @@ -145,9 +145,9 @@ ccl_device_inline void triangle_dPdudv(const KernelGlobals *kg, { /* fetch triangle vertex coordinates */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); - const float3 p0 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - const float3 p1 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - const float3 p2 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + const float3 p0 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + const float3 p1 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + const float3 p2 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); /* compute derivatives of P w.r.t. uv */ *dPdu = (p0 - p2); diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h index 30b77ebd2eb..b784cc75d08 100644 --- a/intern/cycles/kernel/geom/geom_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h @@ -35,13 +35,14 @@ ccl_device_inline bool triangle_intersect(const KernelGlobals *kg, int object, int prim_addr) { - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, prim_addr); + const int prim = kernel_tex_fetch(__prim_index, prim_addr); + const uint tri_vindex = kernel_tex_fetch(__tri_vindex, prim).w; #if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) - const ssef *ssef_verts = (ssef *)&kg->__prim_tri_verts.data[tri_vindex]; + const ssef *ssef_verts = (ssef *)&kg->__tri_verts.data[tri_vindex]; #else - const float4 tri_a = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0), - tri_b = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1), - tri_c = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2); + const float4 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0), + tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1), + tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2); #endif float t, u, v; if (ray_triangle_intersect(P, @@ -64,8 +65,9 @@ ccl_device_inline bool triangle_intersect(const KernelGlobals *kg, if (kernel_tex_fetch(__prim_visibility, prim_addr) & visibility) #endif { - isect->prim = prim_addr; - isect->object = object; + isect->object = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, prim_addr) : + object; + isect->prim = prim; isect->type = PRIMITIVE_TRIANGLE; isect->u = u; isect->v = v; @@ -102,13 +104,14 @@ ccl_device_inline bool triangle_intersect_local(const KernelGlobals *kg, } } - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, prim_addr); + const int prim = kernel_tex_fetch(__prim_index, prim_addr); + const uint tri_vindex = kernel_tex_fetch(__tri_vindex, prim).w; # if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) - const ssef *ssef_verts = (ssef *)&kg->__prim_tri_verts.data[tri_vindex]; + const ssef *ssef_verts = (ssef *)&kg->__tri_verts.data[tri_vindex]; # else - const float3 tri_a = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0)), - tri_b = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1)), - tri_c = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2)); + const float3 tri_a = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 0)), + tri_b = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 1)), + tri_c = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 2)); # endif float t, u, v; if (!ray_triangle_intersect(P, @@ -167,8 +170,8 @@ ccl_device_inline bool triangle_intersect_local(const KernelGlobals *kg, /* Record intersection. */ Intersection *isect = &local_isect->hits[hit]; - isect->prim = prim_addr; - isect->object = object; + isect->prim = prim; + isect->object = local_object; isect->type = PRIMITIVE_TRIANGLE; isect->u = u; isect->v = v; @@ -176,9 +179,9 @@ ccl_device_inline bool triangle_intersect_local(const KernelGlobals *kg, /* Record geometric normal. */ # if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) - const float3 tri_a = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0)), - tri_b = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1)), - tri_c = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2)); + const float3 tri_a = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 0)), + tri_b = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 1)), + tri_c = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 2)); # endif local_isect->Ng[hit] = normalize(cross(tri_b - tri_a, tri_c - tri_a)); @@ -206,7 +209,7 @@ ccl_device_inline float3 triangle_refine(const KernelGlobals *kg, const int isect_prim) { #ifdef __INTERSECTION_REFINE__ - if (isect_object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { if (UNLIKELY(t == 0.0f)) { return P; } @@ -219,10 +222,10 @@ ccl_device_inline float3 triangle_refine(const KernelGlobals *kg, P = P + D * t; - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect_prim); - const float4 tri_a = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0), - tri_b = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1), - tri_c = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2); + const uint tri_vindex = kernel_tex_fetch(__tri_vindex, isect_prim).w; + const float4 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0), + tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1), + tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2); float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z); float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z); float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z); @@ -239,7 +242,7 @@ ccl_device_inline float3 triangle_refine(const KernelGlobals *kg, P = P + D * rt; } - if (isect_object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } @@ -265,7 +268,7 @@ ccl_device_inline float3 triangle_refine_local(const KernelGlobals *kg, /* t is always in world space with OptiX. */ return triangle_refine(kg, sd, P, D, t, isect_object, isect_prim); #else - if (isect_object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); @@ -276,10 +279,10 @@ ccl_device_inline float3 triangle_refine_local(const KernelGlobals *kg, P = P + D * t; # ifdef __INTERSECTION_REFINE__ - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect_prim); - const float4 tri_a = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0), - tri_b = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1), - tri_c = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2); + const uint tri_vindex = kernel_tex_fetch(__tri_vindex, isect_prim).w; + const float4 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0), + tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1), + tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2); float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z); float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z); float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z); @@ -297,7 +300,7 @@ ccl_device_inline float3 triangle_refine_local(const KernelGlobals *kg, } # endif /* __INTERSECTION_REFINE__ */ - if (isect_object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/integrator_intersect_closest.h index 4e581df1870..579a9c4d200 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_closest.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_closest.h @@ -160,10 +160,7 @@ ccl_device void integrator_intersect_closest(INTEGRATOR_STATE_ARGS) if (path_state_ao_bounce(INTEGRATOR_STATE_PASS)) { ray.t = kernel_data.integrator.ao_bounces_distance; - const int last_object = last_isect_object != OBJECT_NONE ? - last_isect_object : - kernel_tex_fetch(__prim_object, last_isect_prim); - const float object_ao_distance = kernel_tex_fetch(__objects, last_object).ao_distance; + const float object_ao_distance = kernel_tex_fetch(__objects, last_isect_object).ao_distance; if (object_ao_distance != 0.0f) { ray.t = object_ao_distance; } diff --git a/intern/cycles/kernel/integrator/integrator_shade_background.h b/intern/cycles/kernel/integrator/integrator_shade_background.h index 3e4cc837e9b..234aa7cae63 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_background.h +++ b/intern/cycles/kernel/integrator/integrator_shade_background.h @@ -192,7 +192,8 @@ ccl_device void integrator_shade_background(INTEGRATOR_STATE_ARGS, INTEGRATOR_STATE_WRITE(path, flag) &= ~PATH_RAY_SHADOW_CATCHER_BACKGROUND; const int isect_prim = INTEGRATOR_STATE(isect, prim); - const int shader = intersection_get_shader_from_isect_prim(kg, isect_prim); + const int isect_type = INTEGRATOR_STATE(isect, type); + const int shader = intersection_get_shader_from_isect_prim(kg, isect_prim, isect_type); const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; if ((shader_flags & SD_HAS_RAYTRACE) || (kernel_data.film.pass_ao != PASS_UNUSED)) { diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/integrator_subsurface.h index 9490738404e..c309d20a046 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface.h @@ -577,7 +577,7 @@ ccl_device_inline bool subsurface_scatter(INTEGRATOR_STATE_ARGS) # ifdef __VOLUME__ /* Update volume stack if needed. */ if (kernel_data.integrator.use_volumes) { - const int object = intersection_get_object(kg, &ss_isect.hits[0]); + const int object = ss_isect.hits[0].object; const int object_flag = kernel_tex_fetch(__object_flag, object); if (object_flag & SD_OBJECT_INTERSECTS_VOLUME) { diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index bf9b94c1753..464ecb183cb 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -18,11 +18,9 @@ # define KERNEL_TEX(type, name) #endif -/* bvh */ +/* BVH2, not used for OptiX or Embree. */ KERNEL_TEX(float4, __bvh_nodes) KERNEL_TEX(float4, __bvh_leaf_nodes) -KERNEL_TEX(float4, __prim_tri_verts) -KERNEL_TEX(uint, __prim_tri_index) KERNEL_TEX(uint, __prim_type) KERNEL_TEX(uint, __prim_visibility) KERNEL_TEX(uint, __prim_index) @@ -46,10 +44,12 @@ KERNEL_TEX(float4, __tri_vnormal) KERNEL_TEX(uint4, __tri_vindex) KERNEL_TEX(uint, __tri_patch) KERNEL_TEX(float2, __tri_patch_uv) +KERNEL_TEX(float4, __tri_verts) /* curves */ -KERNEL_TEX(float4, __curves) +KERNEL_TEX(KernelCurve, __curves) KERNEL_TEX(float4, __curve_keys) +KERNEL_TEX(KernelCurveSegment, __curve_segments) /* patches */ KERNEL_TEX(uint, __patches) diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 22dde3537eb..4a72f45f1a2 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1270,10 +1270,25 @@ typedef struct KernelObject { float ao_distance; - float pad1, pad2; + uint visibility; + int primitive_type; } KernelObject; static_assert_align(KernelObject, 16); +typedef struct KernelCurve { + int shader_id; + int first_key; + int num_keys; + int type; +} KernelCurve; +static_assert_align(KernelCurve, 16); + +typedef struct KernelCurveSegment { + int prim; + int type; +} KernelCurveSegment; +static_assert_align(KernelCurveSegment, 8); + typedef struct KernelSpotLight { float radius; float invarea; diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/svm_bevel.h index 9d7ce202d49..19176087180 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/svm_bevel.h @@ -206,8 +206,7 @@ ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, # ifdef __OBJECT_MOTION__ else if (sd->type & PRIMITIVE_MOTION_TRIANGLE) { float3 verts[3]; - motion_triangle_vertices( - kg, sd->object, kernel_tex_fetch(__prim_index, isect.hits[hit].prim), sd->time, verts); + motion_triangle_vertices(kg, sd->object, isect.hits[hit].prim, sd->time, verts); hit_P = motion_triangle_refine_local( kg, sd, ray->P, ray->D, ray->t, isect.hits[hit].object, isect.hits[hit].prim, verts); } @@ -215,9 +214,7 @@ ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, /* Get geometric normal. */ float3 hit_Ng = isect.Ng[hit]; - int object = (isect.hits[hit].object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, isect.hits[hit].prim) : - isect.hits[hit].object; + int object = isect.hits[hit].object; int object_flag = kernel_tex_fetch(__object_flag, object); if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { hit_Ng = -hit_Ng; @@ -225,7 +222,7 @@ ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, /* Compute smooth normal. */ float3 N = hit_Ng; - int prim = kernel_tex_fetch(__prim_index, isect.hits[hit].prim); + int prim = isect.hits[hit].prim; int shader = kernel_tex_fetch(__tri_shader, prim); if (shader & SHADER_SMOOTH_NORMAL) { diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 4de458de271..49b5f9e27ee 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -46,12 +46,6 @@ CCL_NAMESPACE_BEGIN /* Geometry */ -PackFlags operator|=(PackFlags &pack_flags, uint32_t value) -{ - pack_flags = (PackFlags)((uint32_t)pack_flags | value); - return pack_flags; -} - NODE_ABSTRACT_DEFINE(Geometry) { NodeType *type = NodeType::add("geometry_base", NULL); @@ -79,7 +73,6 @@ Geometry::Geometry(const NodeType *node_type, const Type type) bvh = NULL; attr_map_offset = 0; - optix_prim_offset = 0; prim_offset = 0; } @@ -707,9 +700,9 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom, if (element == ATTR_ELEMENT_CURVE) offset -= hair->prim_offset; else if (element == ATTR_ELEMENT_CURVE_KEY) - offset -= hair->curvekey_offset; + offset -= hair->curve_key_offset; else if (element == ATTR_ELEMENT_CURVE_KEY_MOTION) - offset -= hair->curvekey_offset; + offset -= hair->curve_key_offset; } } else { @@ -972,28 +965,22 @@ void GeometryManager::mesh_calc_offset(Scene *scene, BVHLayout bvh_layout) size_t vert_size = 0; size_t tri_size = 0; - size_t curve_key_size = 0; size_t curve_size = 0; + size_t curve_key_size = 0; + size_t curve_segment_size = 0; size_t patch_size = 0; size_t face_size = 0; size_t corner_size = 0; - size_t optix_prim_size = 0; - foreach (Geometry *geom, scene->geometry) { - if (geom->optix_prim_offset != optix_prim_size) { - /* Need to rebuild BVH in OptiX, since refit only allows modified mesh data there */ - const bool has_optix_bvh = bvh_layout == BVH_LAYOUT_OPTIX || - bvh_layout == BVH_LAYOUT_MULTI_OPTIX || - bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE; - geom->need_update_rebuild |= has_optix_bvh; - geom->need_update_bvh_for_offset = true; - } + bool prim_offset_changed = false; if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { Mesh *mesh = static_cast(geom); + prim_offset_changed = (mesh->prim_offset != tri_size); + mesh->vert_offset = vert_size; mesh->prim_offset = tri_size; @@ -1017,27 +1004,35 @@ void GeometryManager::mesh_calc_offset(Scene *scene, BVHLayout bvh_layout) face_size += mesh->get_num_subd_faces(); corner_size += mesh->subd_face_corners.size(); - - mesh->optix_prim_offset = optix_prim_size; - optix_prim_size += mesh->num_triangles(); } else if (geom->is_hair()) { Hair *hair = static_cast(geom); - hair->curvekey_offset = curve_key_size; + prim_offset_changed = (hair->curve_segment_offset != curve_segment_size); + hair->curve_key_offset = curve_key_size; + hair->curve_segment_offset = curve_segment_size; hair->prim_offset = curve_size; - curve_key_size += hair->get_curve_keys().size(); curve_size += hair->num_curves(); + curve_key_size += hair->get_curve_keys().size(); + curve_segment_size += hair->num_segments(); + } - hair->optix_prim_offset = optix_prim_size; - optix_prim_size += hair->num_segments(); + if (prim_offset_changed) { + /* Need to rebuild BVH in OptiX, since refit only allows modified mesh data there */ + const bool has_optix_bvh = bvh_layout == BVH_LAYOUT_OPTIX || + bvh_layout == BVH_LAYOUT_MULTI_OPTIX || + bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE; + geom->need_update_rebuild |= has_optix_bvh; + geom->need_update_bvh_for_offset = true; } } } -void GeometryManager::device_update_mesh( - Device *, DeviceScene *dscene, Scene *scene, bool for_displacement, Progress &progress) +void GeometryManager::device_update_mesh(Device *, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { /* Count. */ size_t vert_size = 0; @@ -1045,6 +1040,7 @@ void GeometryManager::device_update_mesh( size_t curve_key_size = 0; size_t curve_size = 0; + size_t curve_segment_size = 0; size_t patch_size = 0; @@ -1071,31 +1067,7 @@ void GeometryManager::device_update_mesh( curve_key_size += hair->get_curve_keys().size(); curve_size += hair->num_curves(); - } - } - - /* Create mapping from triangle to primitive triangle array. */ - vector tri_prim_index(tri_size); - if (for_displacement) { - /* For displacement kernels we do some trickery to make them believe - * we've got all required data ready. However, that data is different - * from final render kernels since we don't have BVH yet, so can't - * really use same semantic of arrays. - */ - foreach (Geometry *geom, scene->geometry) { - if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - Mesh *mesh = static_cast(geom); - for (size_t i = 0; i < mesh->num_triangles(); ++i) { - tri_prim_index[i + mesh->prim_offset] = 3 * (i + mesh->prim_offset); - } - } - } - } - else { - for (size_t i = 0; i < dscene->prim_index.size(); ++i) { - if ((dscene->prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { - tri_prim_index[dscene->prim_index[i]] = dscene->prim_tri_index[i]; - } + curve_segment_size += hair->num_segments(); } } @@ -1104,6 +1076,7 @@ void GeometryManager::device_update_mesh( /* normals */ progress.set_status("Updating Mesh", "Computing normals"); + float4 *tri_verts = dscene->tri_verts.alloc(tri_size * 3); uint *tri_shader = dscene->tri_shader.alloc(tri_size); float4 *vnormal = dscene->tri_vnormal.alloc(vert_size); uint4 *tri_vindex = dscene->tri_vindex.alloc(tri_size); @@ -1129,13 +1102,12 @@ void GeometryManager::device_update_mesh( mesh->pack_normals(&vnormal[mesh->vert_offset]); } - if (mesh->triangles_is_modified() || mesh->vert_patch_uv_is_modified() || copy_all_data) { - mesh->pack_verts(tri_prim_index, + if (mesh->verts_is_modified() || mesh->triangles_is_modified() || + mesh->vert_patch_uv_is_modified() || copy_all_data) { + mesh->pack_verts(&tri_verts[mesh->prim_offset * 3], &tri_vindex[mesh->prim_offset], &tri_patch[mesh->prim_offset], - &tri_patch_uv[mesh->vert_offset], - mesh->vert_offset, - mesh->prim_offset); + &tri_patch_uv[mesh->vert_offset]); } if (progress.get_cancel()) @@ -1146,6 +1118,7 @@ void GeometryManager::device_update_mesh( /* vertex coordinates */ progress.set_status("Updating Mesh", "Copying Mesh to device"); + dscene->tri_verts.copy_to_device_if_modified(); dscene->tri_shader.copy_to_device_if_modified(); dscene->tri_vnormal.copy_to_device_if_modified(); dscene->tri_vindex.copy_to_device_if_modified(); @@ -1153,13 +1126,16 @@ void GeometryManager::device_update_mesh( dscene->tri_patch_uv.copy_to_device_if_modified(); } - if (curve_size != 0) { - progress.set_status("Updating Mesh", "Copying Strands to device"); + if (curve_segment_size != 0) { + progress.set_status("Updating Mesh", "Copying Curves to device"); float4 *curve_keys = dscene->curve_keys.alloc(curve_key_size); - float4 *curves = dscene->curves.alloc(curve_size); + KernelCurve *curves = dscene->curves.alloc(curve_size); + KernelCurveSegment *curve_segments = dscene->curve_segments.alloc(curve_segment_size); - const bool copy_all_data = dscene->curve_keys.need_realloc() || dscene->curves.need_realloc(); + const bool copy_all_data = dscene->curve_keys.need_realloc() || + dscene->curves.need_realloc() || + dscene->curve_segments.need_realloc(); foreach (Geometry *geom, scene->geometry) { if (geom->is_hair()) { @@ -1175,9 +1151,9 @@ void GeometryManager::device_update_mesh( } hair->pack_curves(scene, - &curve_keys[hair->curvekey_offset], + &curve_keys[hair->curve_key_offset], &curves[hair->prim_offset], - hair->curvekey_offset); + &curve_segments[hair->curve_segment_offset]); if (progress.get_cancel()) return; } @@ -1185,6 +1161,7 @@ void GeometryManager::device_update_mesh( dscene->curve_keys.copy_to_device_if_modified(); dscene->curves.copy_to_device_if_modified(); + dscene->curve_segments.copy_to_device_if_modified(); } if (patch_size != 0 && dscene->patches.need_realloc()) { @@ -1195,10 +1172,7 @@ void GeometryManager::device_update_mesh( foreach (Geometry *geom, scene->geometry) { if (geom->is_mesh()) { Mesh *mesh = static_cast(geom); - mesh->pack_patches(&patch_data[mesh->patch_offset], - mesh->vert_offset, - mesh->face_offset, - mesh->corner_offset); + mesh->pack_patches(&patch_data[mesh->patch_offset]); if (mesh->patch_table) { mesh->patch_table->copy_adjusting_offsets(&patch_data[mesh->patch_table_offset], @@ -1212,23 +1186,6 @@ void GeometryManager::device_update_mesh( dscene->patches.copy_to_device(); } - - if (for_displacement) { - float4 *prim_tri_verts = dscene->prim_tri_verts.alloc(tri_size * 3); - foreach (Geometry *geom, scene->geometry) { - if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - Mesh *mesh = static_cast(geom); - for (size_t i = 0; i < mesh->num_triangles(); ++i) { - Mesh::Triangle t = mesh->get_triangle(i); - size_t offset = 3 * (i + mesh->prim_offset); - prim_tri_verts[offset + 0] = float3_to_float4(mesh->verts[t.v[0]]); - prim_tri_verts[offset + 1] = float3_to_float4(mesh->verts[t.v[1]]); - prim_tri_verts[offset + 2] = float3_to_float4(mesh->verts[t.v[2]]); - } - } - } - dscene->prim_tri_verts.copy_to_device(); - } } void GeometryManager::device_update_bvh(Device *device, @@ -1256,16 +1213,6 @@ void GeometryManager::device_update_bvh(Device *device, const bool can_refit = scene->bvh != nullptr && (bparams.bvh_layout == BVHLayout::BVH_LAYOUT_OPTIX); - PackFlags pack_flags = PackFlags::PACK_NONE; - - if (scene->bvh == nullptr) { - pack_flags |= PackFlags::PACK_ALL; - } - - if (dscene->prim_visibility.is_modified()) { - pack_flags |= PackFlags::PACK_VISIBILITY; - } - BVH *bvh = scene->bvh; if (!scene->bvh) { bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device); @@ -1284,77 +1231,7 @@ void GeometryManager::device_update_bvh(Device *device, pack = std::move(static_cast(bvh)->pack); } else { - progress.set_status("Updating Scene BVH", "Packing BVH primitives"); - - size_t num_prims = 0; - size_t num_tri_verts = 0; - foreach (Geometry *geom, scene->geometry) { - if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - Mesh *mesh = static_cast(geom); - num_prims += mesh->num_triangles(); - num_tri_verts += 3 * mesh->num_triangles(); - } - else if (geom->is_hair()) { - Hair *hair = static_cast(geom); - num_prims += hair->num_segments(); - } - } - pack.root_index = -1; - - if (pack_flags != PackFlags::PACK_ALL) { - /* if we do not need to recreate the BVH, then only the vertices are updated, so we can - * safely retake the memory */ - dscene->prim_tri_verts.give_data(pack.prim_tri_verts); - - if ((pack_flags & PackFlags::PACK_VISIBILITY) != 0) { - dscene->prim_visibility.give_data(pack.prim_visibility); - } - } - else { - /* It is not strictly necessary to skip those resizes we if do not have to repack, as the OS - * will not allocate pages if we do not touch them, however it does help catching bugs. */ - pack.prim_tri_index.resize(num_prims); - pack.prim_tri_verts.resize(num_tri_verts); - pack.prim_type.resize(num_prims); - pack.prim_index.resize(num_prims); - pack.prim_object.resize(num_prims); - pack.prim_visibility.resize(num_prims); - } - - // Merge visibility flags of all objects and find object index for non-instanced geometry - unordered_map> geometry_to_object_info; - geometry_to_object_info.reserve(scene->geometry.size()); - foreach (Object *ob, scene->objects) { - const Geometry *const geom = ob->get_geometry(); - pair &info = geometry_to_object_info[geom]; - info.second |= ob->visibility_for_tracing(); - if (!geom->is_instanced()) { - info.first = ob->get_device_index(); - } - } - - TaskPool pool; - // Iterate over scene mesh list instead of objects, since 'optix_prim_offset' was calculated - // based on that list, which may be ordered differently from the object list. - foreach (Geometry *geom, scene->geometry) { - /* Make a copy of the pack_flags so the current geometry's flags do not pollute the others'. - */ - PackFlags geom_pack_flags = pack_flags; - - if (geom->is_modified()) { - geom_pack_flags |= PackFlags::PACK_VERTICES; - } - - if (geom_pack_flags == PACK_NONE) { - continue; - } - - const pair &info = geometry_to_object_info[geom]; - pool.push(function_bind( - &Geometry::pack_primitives, geom, &pack, info.first, info.second, geom_pack_flags)); - } - pool.wait_work(); } /* copy to device */ @@ -1375,31 +1252,23 @@ void GeometryManager::device_update_bvh(Device *device, dscene->object_node.steal_data(pack.object_node); dscene->object_node.copy_to_device(); } - if (pack.prim_tri_index.size() && (dscene->prim_tri_index.need_realloc() || has_bvh2_layout)) { - dscene->prim_tri_index.steal_data(pack.prim_tri_index); - dscene->prim_tri_index.copy_to_device(); - } - if (pack.prim_tri_verts.size()) { - dscene->prim_tri_verts.steal_data(pack.prim_tri_verts); - dscene->prim_tri_verts.copy_to_device(); - } - if (pack.prim_type.size() && (dscene->prim_type.need_realloc() || has_bvh2_layout)) { + if (pack.prim_type.size()) { dscene->prim_type.steal_data(pack.prim_type); dscene->prim_type.copy_to_device(); } - if (pack.prim_visibility.size() && (dscene->prim_visibility.is_modified() || has_bvh2_layout)) { + if (pack.prim_visibility.size()) { dscene->prim_visibility.steal_data(pack.prim_visibility); dscene->prim_visibility.copy_to_device(); } - if (pack.prim_index.size() && (dscene->prim_index.need_realloc() || has_bvh2_layout)) { + if (pack.prim_index.size()) { dscene->prim_index.steal_data(pack.prim_index); dscene->prim_index.copy_to_device(); } - if (pack.prim_object.size() && (dscene->prim_object.need_realloc() || has_bvh2_layout)) { + if (pack.prim_object.size()) { dscene->prim_object.steal_data(pack.prim_object); dscene->prim_object.copy_to_device(); } - if (pack.prim_time.size() && (dscene->prim_time.need_realloc() || has_bvh2_layout)) { + if (pack.prim_time.size()) { dscene->prim_time.steal_data(pack.prim_time); dscene->prim_time.copy_to_device(); } @@ -1629,8 +1498,6 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro dscene->bvh_nodes.tag_realloc(); dscene->bvh_leaf_nodes.tag_realloc(); dscene->object_node.tag_realloc(); - dscene->prim_tri_verts.tag_realloc(); - dscene->prim_tri_index.tag_realloc(); dscene->prim_type.tag_realloc(); dscene->prim_visibility.tag_realloc(); dscene->prim_index.tag_realloc(); @@ -1649,6 +1516,7 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro if (device_update_flags & DEVICE_CURVE_DATA_NEEDS_REALLOC) { dscene->curves.tag_realloc(); dscene->curve_keys.tag_realloc(); + dscene->curve_segments.tag_realloc(); } } @@ -1691,6 +1559,7 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro if (device_update_flags & DEVICE_MESH_DATA_MODIFIED) { /* if anything else than vertices or shaders are modified, we would need to reallocate, so * these are the only arrays that can be updated */ + dscene->tri_verts.tag_modified(); dscene->tri_vnormal.tag_modified(); dscene->tri_shader.tag_modified(); } @@ -1698,6 +1567,7 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro if (device_update_flags & DEVICE_CURVE_DATA_MODIFIED) { dscene->curve_keys.tag_modified(); dscene->curves.tag_modified(); + dscene->curve_segments.tag_modified(); } need_flags_update = false; @@ -1906,7 +1776,7 @@ void GeometryManager::device_update(Device *device, {"device_update (displacement: copy meshes to device)", time}); } }); - device_update_mesh(device, dscene, scene, true, progress); + device_update_mesh(device, dscene, scene, progress); } if (progress.get_cancel()) { return; @@ -2058,7 +1928,7 @@ void GeometryManager::device_update(Device *device, {"device_update (copy meshes to device)", time}); } }); - device_update_mesh(device, dscene, scene, false, progress); + device_update_mesh(device, dscene, scene, progress); if (progress.get_cancel()) { return; } @@ -2091,13 +1961,12 @@ void GeometryManager::device_update(Device *device, dscene->bvh_nodes.clear_modified(); dscene->bvh_leaf_nodes.clear_modified(); dscene->object_node.clear_modified(); - dscene->prim_tri_verts.clear_modified(); - dscene->prim_tri_index.clear_modified(); dscene->prim_type.clear_modified(); dscene->prim_visibility.clear_modified(); dscene->prim_index.clear_modified(); dscene->prim_object.clear_modified(); dscene->prim_time.clear_modified(); + dscene->tri_verts.clear_modified(); dscene->tri_shader.clear_modified(); dscene->tri_vindex.clear_modified(); dscene->tri_patch.clear_modified(); @@ -2105,6 +1974,7 @@ void GeometryManager::device_update(Device *device, dscene->tri_patch_uv.clear_modified(); dscene->curves.clear_modified(); dscene->curve_keys.clear_modified(); + dscene->curve_segments.clear_modified(); dscene->patches.clear_modified(); dscene->attributes_map.clear_modified(); dscene->attributes_float.clear_modified(); @@ -2118,13 +1988,12 @@ void GeometryManager::device_free(Device *device, DeviceScene *dscene, bool forc dscene->bvh_nodes.free_if_need_realloc(force_free); dscene->bvh_leaf_nodes.free_if_need_realloc(force_free); dscene->object_node.free_if_need_realloc(force_free); - dscene->prim_tri_verts.free_if_need_realloc(force_free); - dscene->prim_tri_index.free_if_need_realloc(force_free); dscene->prim_type.free_if_need_realloc(force_free); dscene->prim_visibility.free_if_need_realloc(force_free); dscene->prim_index.free_if_need_realloc(force_free); dscene->prim_object.free_if_need_realloc(force_free); dscene->prim_time.free_if_need_realloc(force_free); + dscene->tri_verts.free_if_need_realloc(force_free); dscene->tri_shader.free_if_need_realloc(force_free); dscene->tri_vnormal.free_if_need_realloc(force_free); dscene->tri_vindex.free_if_need_realloc(force_free); @@ -2132,6 +2001,7 @@ void GeometryManager::device_free(Device *device, DeviceScene *dscene, bool forc dscene->tri_patch_uv.free_if_need_realloc(force_free); dscene->curves.free_if_need_realloc(force_free); dscene->curve_keys.free_if_need_realloc(force_free); + dscene->curve_segments.free_if_need_realloc(force_free); dscene->patches.free_if_need_realloc(force_free); dscene->attributes_map.free_if_need_realloc(force_free); dscene->attributes_float.free_if_need_realloc(force_free); diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h index 7db122f69cb..cd42f62c669 100644 --- a/intern/cycles/render/geometry.h +++ b/intern/cycles/render/geometry.h @@ -43,24 +43,6 @@ class Shader; class Volume; struct PackedBVH; -/* Flags used to determine which geometry data need to be packed. */ -enum PackFlags : uint32_t { - PACK_NONE = 0u, - - /* Pack the geometry information (e.g. triangle or curve keys indices). */ - PACK_GEOMETRY = (1u << 0), - - /* Pack the vertices, for Meshes and Volumes' bounding meshes. */ - PACK_VERTICES = (1u << 1), - - /* Pack the visibility flags for each triangle or curve. */ - PACK_VISIBILITY = (1u << 2), - - PACK_ALL = (PACK_GEOMETRY | PACK_VERTICES | PACK_VISIBILITY), -}; - -PackFlags operator|=(PackFlags &pack_flags, uint32_t value); - /* Geometry * * Base class for geometric types like Mesh and Hair. */ @@ -100,7 +82,6 @@ class Geometry : public Node { BVH *bvh; size_t attr_map_offset; size_t prim_offset; - size_t optix_prim_offset; /* Shader Properties */ bool has_volume; /* Set in the device_update_flags(). */ @@ -144,10 +125,7 @@ class Geometry : public Node { int n, int total); - virtual void pack_primitives(PackedBVH *pack, - int object, - uint visibility, - PackFlags pack_flags) = 0; + virtual PrimitiveType primitive_type() const = 0; /* Check whether the geometry should have own BVH built separately. Briefly, * own BVH is needed for geometry, if: @@ -260,11 +238,7 @@ class GeometryManager { void device_update_object(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); - void device_update_mesh(Device *device, - DeviceScene *dscene, - Scene *scene, - bool for_displacement, - Progress &progress); + void device_update_mesh(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); void device_update_attributes(Device *device, DeviceScene *dscene, diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp index 72fc612c0c0..e104455f7dd 100644 --- a/intern/cycles/render/hair.cpp +++ b/intern/cycles/render/hair.cpp @@ -295,7 +295,8 @@ NODE_DEFINE(Hair) Hair::Hair() : Geometry(get_node_type(), Geometry::HAIR) { - curvekey_offset = 0; + curve_key_offset = 0; + curve_segment_offset = 0; curve_shape = CURVE_RIBBON; } @@ -462,8 +463,8 @@ void Hair::apply_transform(const Transform &tfm, const bool apply_to_motion) void Hair::pack_curves(Scene *scene, float4 *curve_key_co, - float4 *curve_data, - size_t curvekey_offset) + KernelCurve *curves, + KernelCurveSegment *curve_segments) { size_t curve_keys_size = curve_keys.size(); @@ -477,7 +478,10 @@ void Hair::pack_curves(Scene *scene, } /* pack curve segments */ + const PrimitiveType type = primitive_type(); + size_t curve_num = num_curves(); + size_t index = 0; for (size_t i = 0; i < curve_num; i++) { Curve curve = get_curve(i); @@ -487,56 +491,24 @@ void Hair::pack_curves(Scene *scene, scene->default_surface; shader_id = scene->shader_manager->get_shader_id(shader, false); - curve_data[i] = make_float4(__int_as_float(curve.first_key + curvekey_offset), - __int_as_float(curve.num_keys), - __int_as_float(shader_id), - 0.0f); + curves[i].shader_id = shader_id; + curves[i].first_key = curve_key_offset + curve.first_key; + curves[i].num_keys = curve.num_keys; + curves[i].type = type; + + for (int k = 0; k < curve.num_segments(); ++k, ++index) { + curve_segments[index].prim = prim_offset + i; + curve_segments[index].type = PRIMITIVE_PACK_SEGMENT(type, k); + } } } -void Hair::pack_primitives(PackedBVH *pack, int object, uint visibility, PackFlags pack_flags) +PrimitiveType Hair::primitive_type() const { - if (curve_first_key.empty()) - return; - - /* Separate loop as other arrays are not initialized if their packing is not required. */ - if ((pack_flags & PACK_VISIBILITY) != 0) { - unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset]; - - size_t index = 0; - for (size_t j = 0; j < num_curves(); ++j) { - Curve curve = get_curve(j); - for (size_t k = 0; k < curve.num_segments(); ++k, ++index) { - prim_visibility[index] = visibility; - } - } - } - - if ((pack_flags & PACK_GEOMETRY) != 0) { - unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset]; - int *prim_type = &pack->prim_type[optix_prim_offset]; - int *prim_index = &pack->prim_index[optix_prim_offset]; - int *prim_object = &pack->prim_object[optix_prim_offset]; - // 'pack->prim_time' is unused by Embree and OptiX - - uint type = has_motion_blur() ? - ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : - PRIMITIVE_MOTION_CURVE_THICK) : - ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : - PRIMITIVE_CURVE_THICK); - - size_t index = 0; - for (size_t j = 0; j < num_curves(); ++j) { - Curve curve = get_curve(j); - for (size_t k = 0; k < curve.num_segments(); ++k, ++index) { - prim_tri_index[index] = -1; - prim_type[index] = PRIMITIVE_PACK_SEGMENT(type, k); - // Each curve segment points back to its curve index - prim_index[index] = j + prim_offset; - prim_object[index] = object; - } - } - } + return has_motion_blur() ? + ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : + PRIMITIVE_MOTION_CURVE_THICK) : + ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK); } CCL_NAMESPACE_END diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h index e4451d70767..920e9601b35 100644 --- a/intern/cycles/render/hair.h +++ b/intern/cycles/render/hair.h @@ -21,6 +21,8 @@ CCL_NAMESPACE_BEGIN +struct KernelCurveSegment; + class Hair : public Geometry { public: NODE_DECLARE @@ -95,7 +97,8 @@ class Hair : public Geometry { NODE_SOCKET_API_ARRAY(array, curve_shader) /* BVH */ - size_t curvekey_offset; + size_t curve_key_offset; + size_t curve_segment_offset; CurveShapeType curve_shape; /* Constructor/Destructor */ @@ -144,12 +147,12 @@ class Hair : public Geometry { void get_uv_tiles(ustring map, unordered_set &tiles) override; /* BVH */ - void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset); + void pack_curves(Scene *scene, + float4 *curve_key_co, + KernelCurve *curve, + KernelCurveSegment *curve_segments); - void pack_primitives(PackedBVH *pack, - int object, - uint visibility, - PackFlags pack_flags) override; + PrimitiveType primitive_type() const override; }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index fd9879dd5dd..2ecea3101db 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -729,12 +729,7 @@ void Mesh::pack_normals(float4 *vnormal) } } -void Mesh::pack_verts(const vector &tri_prim_index, - uint4 *tri_vindex, - uint *tri_patch, - float2 *tri_patch_uv, - size_t vert_offset, - size_t tri_offset) +void Mesh::pack_verts(float4 *tri_verts, uint4 *tri_vindex, uint *tri_patch, float2 *tri_patch_uv) { size_t verts_size = verts.size(); @@ -749,17 +744,19 @@ void Mesh::pack_verts(const vector &tri_prim_index, size_t triangles_size = num_triangles(); for (size_t i = 0; i < triangles_size; i++) { - Triangle t = get_triangle(i); - tri_vindex[i] = make_uint4(t.v[0] + vert_offset, - t.v[1] + vert_offset, - t.v[2] + vert_offset, - tri_prim_index[i + tri_offset]); + const Triangle t = get_triangle(i); + tri_vindex[i] = make_uint4( + t.v[0] + vert_offset, t.v[1] + vert_offset, t.v[2] + vert_offset, 3 * (prim_offset + i)); tri_patch[i] = (!get_num_subd_faces()) ? -1 : (triangle_patch[i] * 8 + patch_offset); + + tri_verts[i * 3] = float3_to_float4(verts[t.v[0]]); + tri_verts[i * 3 + 1] = float3_to_float4(verts[t.v[1]]); + tri_verts[i * 3 + 2] = float3_to_float4(verts[t.v[2]]); } } -void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset) +void Mesh::pack_patches(uint *patch_data) { size_t num_faces = get_num_subd_faces(); int ngons = 0; @@ -805,53 +802,9 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui } } -void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, PackFlags pack_flags) +PrimitiveType Mesh::primitive_type() const { - if (triangles.empty()) - return; - - const size_t num_prims = num_triangles(); - - /* Use prim_offset for indexing as it is computed per geometry type, and prim_tri_verts does not - * contain data for Hair geometries. */ - float4 *prim_tri_verts = &pack->prim_tri_verts[prim_offset * 3]; - // 'pack->prim_time' is unused by Embree and OptiX - - uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE; - - /* Separate loop as other arrays are not initialized if their packing is not required. */ - if ((pack_flags & PackFlags::PACK_VISIBILITY) != 0) { - unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset]; - for (size_t k = 0; k < num_prims; ++k) { - prim_visibility[k] = visibility; - } - } - - if ((pack_flags & PackFlags::PACK_GEOMETRY) != 0) { - /* Use optix_prim_offset for indexing as those arrays also contain data for Hair geometries. */ - unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset]; - int *prim_type = &pack->prim_type[optix_prim_offset]; - int *prim_index = &pack->prim_index[optix_prim_offset]; - int *prim_object = &pack->prim_object[optix_prim_offset]; - - for (size_t k = 0; k < num_prims; ++k) { - if ((pack_flags & PackFlags::PACK_GEOMETRY) != 0) { - prim_tri_index[k] = (prim_offset + k) * 3; - prim_type[k] = type; - prim_index[k] = prim_offset + k; - prim_object[k] = object; - } - } - } - - if ((pack_flags & PackFlags::PACK_VERTICES) != 0) { - for (size_t k = 0; k < num_prims; ++k) { - const Mesh::Triangle t = get_triangle(k); - prim_tri_verts[k * 3] = float3_to_float4(verts[t.v[0]]); - prim_tri_verts[k * 3 + 1] = float3_to_float4(verts[t.v[1]]); - prim_tri_verts[k * 3 + 2] = float3_to_float4(verts[t.v[2]]); - } - } + return has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE; } CCL_NAMESPACE_END diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index e9e79f7f20d..8258c18ddd1 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -224,18 +224,10 @@ class Mesh : public Geometry { void pack_shaders(Scene *scene, uint *shader); void pack_normals(float4 *vnormal); - void pack_verts(const vector &tri_prim_index, - uint4 *tri_vindex, - uint *tri_patch, - float2 *tri_patch_uv, - size_t vert_offset, - size_t tri_offset); - void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset); + void pack_verts(float4 *tri_verts, uint4 *tri_vindex, uint *tri_patch, float2 *tri_patch_uv); + void pack_patches(uint *patch_data); - void pack_primitives(PackedBVH *pack, - int object, - uint visibility, - PackFlags pack_flags) override; + PrimitiveType primitive_type() const override; void tessellate(DiagSplit *split); diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 1320a5eb7a6..d3ea93ca8a5 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -60,6 +60,7 @@ struct UpdateObjectTransformState { /* Packed object arrays. Those will be filled in. */ uint *object_flag; + uint *object_visibility; KernelObject *objects; Transform *object_motion_pass; DecomposedTransform *object_motion; @@ -528,6 +529,9 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s (1.0f - 0.5f * ob->shadow_terminator_shading_offset); kobject.shadow_terminator_geometry_offset = ob->shadow_terminator_geometry_offset; + kobject.visibility = ob->visibility_for_tracing(); + kobject.primitive_type = geom->primitive_type(); + /* Object flag. */ if (ob->use_holdout) { flag |= SD_OBJECT_HOLDOUT_MASK; diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index ecd6946bbf8..eeb92122825 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -49,13 +49,12 @@ DeviceScene::DeviceScene(Device *device) : bvh_nodes(device, "__bvh_nodes", MEM_GLOBAL), bvh_leaf_nodes(device, "__bvh_leaf_nodes", MEM_GLOBAL), object_node(device, "__object_node", MEM_GLOBAL), - prim_tri_index(device, "__prim_tri_index", MEM_GLOBAL), - prim_tri_verts(device, "__prim_tri_verts", MEM_GLOBAL), prim_type(device, "__prim_type", MEM_GLOBAL), prim_visibility(device, "__prim_visibility", MEM_GLOBAL), prim_index(device, "__prim_index", MEM_GLOBAL), prim_object(device, "__prim_object", MEM_GLOBAL), prim_time(device, "__prim_time", MEM_GLOBAL), + tri_verts(device, "__tri_verts", MEM_GLOBAL), tri_shader(device, "__tri_shader", MEM_GLOBAL), tri_vnormal(device, "__tri_vnormal", MEM_GLOBAL), tri_vindex(device, "__tri_vindex", MEM_GLOBAL), @@ -63,6 +62,7 @@ DeviceScene::DeviceScene(Device *device) tri_patch_uv(device, "__tri_patch_uv", MEM_GLOBAL), curves(device, "__curves", MEM_GLOBAL), curve_keys(device, "__curve_keys", MEM_GLOBAL), + curve_segments(device, "__curve_segments", MEM_GLOBAL), patches(device, "__patches", MEM_GLOBAL), objects(device, "__objects", MEM_GLOBAL), object_motion_pass(device, "__object_motion_pass", MEM_GLOBAL), diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 8076d0dc09c..001da31e893 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -74,8 +74,6 @@ class DeviceScene { device_vector bvh_nodes; device_vector bvh_leaf_nodes; device_vector object_node; - device_vector prim_tri_index; - device_vector prim_tri_verts; device_vector prim_type; device_vector prim_visibility; device_vector prim_index; @@ -83,14 +81,16 @@ class DeviceScene { device_vector prim_time; /* mesh */ + device_vector tri_verts; device_vector tri_shader; device_vector tri_vnormal; device_vector tri_vindex; device_vector tri_patch; device_vector tri_patch_uv; - device_vector curves; + device_vector curves; device_vector curve_keys; + device_vector curve_segments; device_vector patches; From 306e9bff46ad721c1d3203bf7d83c1bef0d957f3 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 7 Oct 2021 00:04:26 +0200 Subject: [PATCH 0590/1500] Fix VSE pan property text printing Move text into separate label. --- .../scripts/startup/bl_ui/space_sequencer.py | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index dec754b8747..03a090255f6 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1801,15 +1801,26 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): split.label(text="Pitch") split.prop(strip, "pitch", text="") + audio_channels = context.scene.render.ffmpeg.audio_channels + pan_enabled = sound.use_mono and audio_channels != 'MONO' + pan_text = "%.2f°" % (strip.pan * 90) + split = col.split(factor=0.4) split.alignment = 'RIGHT' split.label(text="Pan") - audio_channels = context.scene.render.ffmpeg.audio_channels - pan_text = "" + split.prop(strip, "pan", text="") + split.enabled = pan_enabled + if audio_channels != 'MONO' and audio_channels != 'STEREO': - pan_text = "%.2f°" % (strip.pan * 90) - split.prop(strip, "pan", text=pan_text) - split.enabled = sound.use_mono and audio_channels != 'MONO' + split = col.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Pan Angle") + split.enabled = pan_enabled + subsplit = split.row() + subsplit.alignment = 'CENTER' + subsplit.label(text=pan_text) + subsplit.label(text=" ") # Compensate for no decorate. + subsplit.enabled = pan_enabled layout.use_property_split = False col = layout.column() From 877ba6b251bb5af673d023b243fd6da03e4a0fab Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 7 Oct 2021 00:10:37 +0200 Subject: [PATCH 0591/1500] Fix T91972: Meta changes length when adding strip `SequencesMeta.new_movie()` API function caused meta strip to change length. Similar issue has been fixed in transform code by checking if `MetaStack` exists. `MetaStack` is not used when changing data in python. Provide `seqbase` to `SEQ_time_update_sequence()` so the function can check if change happens inside of meta strip. This patch also merges `seq_time_update_sequence_bounds()` into `SEQ_time_update_sequence()`. This is because same issue applies for both functions and it is confusing to have more time update functions.re if this will lead anywhere. Reviewed By: sergey Differential Revision: https://developer.blender.org/D12763 --- .../editors/space_sequencer/sequencer_add.c | 2 +- .../editors/space_sequencer/sequencer_edit.c | 63 ++++++++++--------- .../transform/transform_convert_sequencer.c | 19 +++--- .../blender/makesrna/intern/rna_sequencer.c | 13 ++-- .../makesrna/intern/rna_sequencer_api.c | 18 ++++-- source/blender/sequencer/SEQ_time.h | 3 +- source/blender/sequencer/intern/sound.c | 4 +- source/blender/sequencer/intern/strip_add.c | 11 ++-- source/blender/sequencer/intern/strip_edit.c | 11 ++-- .../sequencer/intern/strip_relations.c | 3 +- source/blender/sequencer/intern/strip_time.c | 33 +++++----- .../sequencer/intern/strip_transform.c | 13 ++-- source/blender/sequencer/intern/utils.c | 15 +++++ source/blender/sequencer/intern/utils.h | 3 + 14 files changed, 132 insertions(+), 79 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index bdfa639b327..8d6d81d7115 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -1111,7 +1111,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) /* Adjust length. */ if (load_data.image.len == 1) { SEQ_transform_set_right_handle_frame(seq, load_data.image.end_frame); - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, SEQ_active_seqbase_get(ed), seq); } seq_load_apply_generic_options(C, op, seq); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index d55356eddec..b0273fe1e25 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -329,6 +329,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); Sequence *seq; int snap_frame; @@ -352,7 +353,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) SEQ_transform_handle_xlimits(seq, seq->flag & SEQ_LEFTSEL, seq->flag & SEQ_RIGHTSEL); SEQ_transform_fix_single_image_seq_offsets(seq); } - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); } } @@ -375,19 +376,19 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) if (!either_handle_selected) { SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); } - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); } else if (seq->seq2 && (seq->seq2->flag & SELECT)) { if (!either_handle_selected) { SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); } - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); } else if (seq->seq3 && (seq->seq3->flag & SELECT)) { if (!either_handle_selected) { SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); } - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); } } } @@ -629,7 +630,8 @@ static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) * we can skip calculating for effects. * This way we can avoid an extra loop just for effects. */ if (!(seq->type & SEQ_TYPE_EFFECT)) { - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq); } } if (changed) { @@ -810,7 +812,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even for (int i = 0; i < data->num_seq; i++) { Sequence *seq = data->seq_array[i]; SEQ_add_reload_new_file(bmain, scene, seq, false); - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq); } MEM_freeN(data->seq_array); @@ -1763,7 +1766,8 @@ static int sequencer_offset_clear_exec(bContext *C, wmOperator *UNUSED(op)) /* Update lengths, etc. */ seq = ed->seqbasep->first; while (seq) { - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SEQ_time_update_sequence(scene, seqbase, seq); seq = seq->next; } @@ -1806,6 +1810,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); Sequence *seq, *seq_new; Strip *strip_new; @@ -1813,7 +1818,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) int start_ofs, timeline_frame, frame_end; int step = RNA_int_get(op->ptr, "length"); - seq = ed->seqbasep->first; /* Poll checks this is valid. */ + seq = seqbase->first; /* Poll checks this is valid. */ SEQ_prefetch_stop(scene); @@ -1823,7 +1828,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) /* Remove seq so overlap tests don't conflict, * see seq_free_sequence below for the real freeing. */ - BLI_remlink(ed->seqbasep, seq); + BLI_remlink(seqbase, seq); /* TODO: remove f-curve and assign to split image strips. * The old animation system would remove the user of `seq->ipo`. */ @@ -1834,8 +1839,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) /* New seq. */ se = SEQ_render_give_stripelem(seq, timeline_frame); - seq_new = SEQ_sequence_dupli_recursive( - scene, scene, ed->seqbasep, seq, SEQ_DUPE_UNIQUE_NAME); + seq_new = SEQ_sequence_dupli_recursive(scene, scene, seqbase, seq, SEQ_DUPE_UNIQUE_NAME); seq_new->start = start_ofs; seq_new->type = SEQ_TYPE_IMAGE; @@ -1853,12 +1857,12 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) BLI_strncpy(se_new->name, se->name, sizeof(se_new->name)); strip_new->stripdata = se_new; - SEQ_time_update_sequence(scene, seq_new); + SEQ_time_update_sequence(scene, seqbase, seq_new); if (step > 1) { seq_new->flag &= ~SEQ_OVERLAP; - if (SEQ_transform_test_overlap(ed->seqbasep, seq_new)) { - SEQ_transform_seqbase_shuffle(ed->seqbasep, seq_new, scene); + if (SEQ_transform_test_overlap(seqbase, seq_new)) { + SEQ_transform_seqbase_shuffle(seqbase, seq_new, scene); } } @@ -1877,7 +1881,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) } } - SEQ_sort(SEQ_active_seqbase_get(ed)); + SEQ_sort(seqbase); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1994,7 +1998,7 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op) SEQ_sequence_base_unique_name_recursive(scene, &ed->seqbase, seqm); seqm->start = meta_start_frame; seqm->len = meta_end_frame - meta_start_frame; - SEQ_time_update_sequence(scene, seqm); + SEQ_time_update_sequence(scene, active_seqbase, seqm); SEQ_select_active_set(scene, seqm); if (SEQ_transform_test_overlap(active_seqbase, seqm)) { SEQ_transform_seqbase_shuffle(active_seqbase, seqm, scene); @@ -2167,17 +2171,18 @@ static const EnumPropertyItem prop_side_lr_types[] = { static void swap_sequence(Scene *scene, Sequence *seqa, Sequence *seqb) { + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); int gap = seqb->startdisp - seqa->enddisp; int seq_a_start; int seq_b_start; seq_b_start = (seqb->start - seqb->startdisp) + seqa->startdisp; SEQ_transform_translate_sequence(scene, seqb, seq_b_start - seqb->start); - SEQ_time_update_sequence(scene, seqb); + SEQ_time_update_sequence(scene, seqbase, seqb); seq_a_start = (seqa->start - seqa->startdisp) + seqb->enddisp + gap; SEQ_transform_translate_sequence(scene, seqa, seq_a_start - seqa->start); - SEQ_time_update_sequence(scene, seqa); + SEQ_time_update_sequence(scene, seqbase, seqa); } static Sequence *find_next_prev_sequence(Scene *scene, Sequence *test, int lr, int sel) @@ -2236,6 +2241,7 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); Sequence *active_seq = SEQ_select_active_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); Sequence *seq, *iseq; int side = RNA_enum_get(op->ptr, "side"); @@ -2267,20 +2273,20 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op) } /* XXX: Should be a generic function. */ - for (iseq = scene->ed->seqbasep->first; iseq; iseq = iseq->next) { + for (iseq = seqbase->first; iseq; iseq = iseq->next) { if ((iseq->type & SEQ_TYPE_EFFECT) && (seq_is_parent(iseq, active_seq) || seq_is_parent(iseq, seq))) { - SEQ_time_update_sequence(scene, iseq); + SEQ_time_update_sequence(scene, seqbase, iseq); } } /* Do this in a new loop since both effects need to be calculated first. */ - for (iseq = scene->ed->seqbasep->first; iseq; iseq = iseq->next) { + for (iseq = seqbase->first; iseq; iseq = iseq->next) { if ((iseq->type & SEQ_TYPE_EFFECT) && (seq_is_parent(iseq, active_seq) || seq_is_parent(iseq, seq))) { /* This may now overlap. */ - if (SEQ_transform_test_overlap(ed->seqbasep, iseq)) { - SEQ_transform_seqbase_shuffle(ed->seqbasep, iseq, scene); + if (SEQ_transform_test_overlap(seqbase, iseq)) { + SEQ_transform_seqbase_shuffle(seqbase, iseq, scene); } } } @@ -2594,8 +2600,9 @@ static int sequencer_swap_data_exec(bContext *C, wmOperator *op) seq_act->scene_sound = NULL; seq_other->scene_sound = NULL; - SEQ_time_update_sequence(scene, seq_act); - SEQ_time_update_sequence(scene, seq_other); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq_act); + SEQ_time_update_sequence(scene, seqbase, seq_other); if (seq_act->sound) { BKE_sound_add_scene_sound_defaults(scene, seq_act); @@ -2793,7 +2800,6 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene); Sequence *seq = SEQ_select_active_get(scene); const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path"); const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders"); @@ -2849,10 +2855,11 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) * Important not to set seq->len = len; allow the function to handle it. */ SEQ_add_reload_new_file(bmain, scene, seq, true); - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq); /* Invalidate cache. */ - SEQ_relations_free_imbuf(scene, &ed->seqbase, false); + SEQ_relations_free_imbuf(scene, seqbase, false); } else if (ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD)) { bSound *sound = seq->sound; diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index a2698b342d0..bf320595d6c 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -275,7 +275,7 @@ static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips SEQ_transform_seqbase_shuffle(seqbase, seq, t->scene); } - SEQ_time_update_sequence_bounds(t->scene, seq); + SEQ_time_update_sequence(t->scene, seqbase, seq); } } @@ -327,7 +327,8 @@ static void seq_transform_update_effects(TransInfo *t, SeqCollection *collection Sequence *seq; SEQ_ITERATOR_FOREACH (seq, collection) { if ((seq->type & SEQ_TYPE_EFFECT) && (seq->seq1 || seq->seq2 || seq->seq3)) { - SEQ_time_update_sequence(t->scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene)); + SEQ_time_update_sequence(t->scene, seqbase, seq); } } } @@ -487,7 +488,9 @@ static void seq_transform_handle_overwrite_trim(const TransInfo *t, BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE); SEQ_transform_set_right_handle_frame(seq, transformed->startdisp); } - SEQ_time_update_sequence(t->scene, seq); + + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene)); + SEQ_time_update_sequence(t->scene, seqbase, seq); } SEQ_collection_free(targets); } @@ -704,7 +707,9 @@ BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int s /* Calculate this strip and all nested strips. * Children are ALWAYS transformed first so we don't need to do this in another loop. */ - SEQ_time_update_sequence(sce, seq); + + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(sce)); + SEQ_time_update_sequence(sce, seqbase, seq); if (sel_flag == SELECT) { SEQ_offset_animdata(sce, seq, seq->start - old_start); @@ -744,13 +749,13 @@ static void flushTransSeq(TransInfo *t) SEQ_transform_set_left_handle_frame(seq, new_frame); SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); SEQ_transform_fix_single_image_seq_offsets(seq); - SEQ_time_update_sequence(t->scene, seq); + SEQ_time_update_sequence(t->scene, seqbasep, seq); break; case SEQ_RIGHTSEL: /* No vertical transform. */ SEQ_transform_set_right_handle_frame(seq, new_frame); SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); SEQ_transform_fix_single_image_seq_offsets(seq); - SEQ_time_update_sequence(t->scene, seq); + SEQ_time_update_sequence(t->scene, seqbasep, seq); break; } } @@ -759,7 +764,7 @@ static void flushTransSeq(TransInfo *t) if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) { for (seq = seqbasep->first; seq; seq = seq->next) { if (seq->seq1 || seq->seq2 || seq->seq3) { - SEQ_time_update_sequence(t->scene, seq); + SEQ_time_update_sequence(t->scene, seqbasep, seq); } } } diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index e519740259c..08dc01ff45b 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -303,7 +303,7 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq) Editing *ed = SEQ_editing_get(scene); ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); Sequence *tseq; - SEQ_time_update_sequence_bounds(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); /* ensure effects are always fit in length to their input */ @@ -312,7 +312,7 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq) */ for (tseq = seqbase->first; tseq; tseq = tseq->next) { if (tseq->seq1 || tseq->seq2 || tseq->seq3) { - SEQ_time_update_sequence(scene, tseq); + SEQ_time_update_sequence(scene, seqbase, tseq); } } @@ -756,13 +756,16 @@ static IDProperty **rna_Sequence_idprops(PointerRNA *ptr) static bool rna_MovieSequence_reload_if_needed(ID *scene_id, Sequence *seq, Main *bmain) { Scene *scene = (Scene *)scene_id; + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + bool has_reloaded; bool can_produce_frames; SEQ_add_movie_reload_if_needed(bmain, scene, seq, &has_reloaded, &can_produce_frames); if (has_reloaded && can_produce_frames) { - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); SEQ_relations_invalidate_cache_raw(scene, seq); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -950,7 +953,9 @@ static void rna_Sequence_filepath_update(Main *bmain, Scene *UNUSED(scene), Poin Scene *scene = (Scene *)ptr->owner_id; Sequence *seq = (Sequence *)(ptr->data); SEQ_add_reload_new_file(bmain, scene, seq, true); - SEQ_time_update_sequence(scene, seq); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + SEQ_time_update_sequence(scene, seqbase, seq); rna_Sequence_invalidate_raw_update(bmain, scene, ptr); } diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index b43b57a35be..ec6c4c2f32f 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -64,12 +64,16 @@ static void rna_Sequence_update_rnafunc(ID *id, Sequence *self, bool do_data) { + Scene *scene = (Scene *)id; + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, self); + if (do_data) { - SEQ_relations_update_changed_seq_and_deps((Scene *)id, self, true, true); + SEQ_relations_update_changed_seq_and_deps(scene, self, true, true); // new_tstripdata(self); /* need 2.6x version of this. */ } - SEQ_time_update_sequence((Scene *)id, self); - SEQ_time_update_sequence_bounds((Scene *)id, self); + + SEQ_time_update_sequence(scene, seqbase, self); } static void rna_Sequence_swap_internal(Sequence *seq_self, @@ -586,6 +590,8 @@ static void rna_Sequences_meta_remove( static StripElem *rna_SequenceElements_append(ID *id, Sequence *seq, const char *filename) { Scene *scene = (Scene *)id; + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); StripElem *se; seq->strip->stripdata = se = MEM_reallocN(seq->strip->stripdata, @@ -594,7 +600,7 @@ static StripElem *rna_SequenceElements_append(ID *id, Sequence *seq, const char BLI_strncpy(se->name, filename, sizeof(se->name)); seq->len++; - SEQ_time_update_sequence_bounds(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); return se; @@ -603,6 +609,8 @@ static StripElem *rna_SequenceElements_append(ID *id, Sequence *seq, const char static void rna_SequenceElements_pop(ID *id, Sequence *seq, ReportList *reports, int index) { Scene *scene = (Scene *)id; + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); StripElem *new_seq, *se; if (seq->len == 1) { @@ -635,7 +643,7 @@ static void rna_SequenceElements_pop(ID *id, Sequence *seq, ReportList *reports, MEM_freeN(seq->strip->stripdata); seq->strip->stripdata = new_seq; - SEQ_time_update_sequence_bounds(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); } diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h index 732e9bb985a..c9024614dfd 100644 --- a/source/blender/sequencer/SEQ_time.h +++ b/source/blender/sequencer/SEQ_time.h @@ -42,8 +42,7 @@ int SEQ_time_find_next_prev_edit(struct Scene *scene, const bool do_skip_mute, const bool do_center, const bool do_unselected); -void SEQ_time_update_sequence(struct Scene *scene, struct Sequence *seq); -void SEQ_time_update_sequence_bounds(struct Scene *scene, struct Sequence *seq); +void SEQ_time_update_sequence(struct Scene *scene, struct ListBase *seqbase, struct Sequence *seq); bool SEQ_time_strip_intersects_frame(const struct Sequence *seq, const int timeline_frame); void SEQ_time_update_meta_strip_range(struct Scene *scene, struct Sequence *seq_meta); diff --git a/source/blender/sequencer/intern/sound.c b/source/blender/sequencer/intern/sound.c index c53aacddcfe..9fe9e644a74 100644 --- a/source/blender/sequencer/intern/sound.c +++ b/source/blender/sequencer/intern/sound.c @@ -51,7 +51,7 @@ static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene, for (seq = seqbase->first; seq; seq = seq->next) { if (seq->type == SEQ_TYPE_META) { if (sequencer_refresh_sound_length_recursive(bmain, scene, &seq->seqbase)) { - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); changed = true; } } @@ -67,7 +67,7 @@ static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene, seq->endofs *= fac; seq->start += (old - seq->startofs); /* So that visual/"real" start frame does not change! */ - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); changed = true; } } diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index aa3f7c92dd8..6f635b5db5f 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -100,7 +100,7 @@ void SEQ_add_load_data_init(SeqLoadData *load_data, static void seq_add_generic_update(Scene *scene, ListBase *seqbase, Sequence *seq) { SEQ_sequence_base_unique_name_recursive(scene, &scene->ed->seqbase, seq); - SEQ_time_update_sequence_bounds(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); SEQ_sort(seqbase); SEQ_relations_invalidate_cache_composite(scene, seq); } @@ -472,7 +472,7 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_ /* Set frames start and length. */ seqm->start = load_data->start_frame; seqm->len = 1; - SEQ_time_update_sequence(scene, seqm); + SEQ_time_update_sequence(scene, seqbase, seqm); return seqm; } @@ -644,7 +644,9 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo if (lock_range) { /* keep so we don't have to move the actual start and end points (only the data) */ - SEQ_time_update_sequence_bounds(scene, seq); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + SEQ_time_update_sequence(scene, seqbase, seq); prev_startdisp = seq->startdisp; prev_enddisp = seq->enddisp; } @@ -794,7 +796,8 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo SEQ_transform_fix_single_image_seq_offsets(seq); } - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq); } void SEQ_add_movie_reload_if_needed(struct Main *bmain, diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index cfac243e68f..747f0eb3deb 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -267,8 +267,9 @@ bool SEQ_edit_move_strip_to_meta(Scene *scene, SEQ_relations_invalidate_cache_preprocessed(scene, seq); /* Update meta. */ + ListBase *meta_seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, dst_seqm); SEQ_time_update_meta_strip_range(scene, dst_seqm); - SEQ_time_update_sequence(scene, dst_seqm); + SEQ_time_update_sequence(scene, meta_seqbase, dst_seqm); if (SEQ_transform_test_overlap(&dst_seqm->seqbase, seq)) { SEQ_transform_seqbase_shuffle(&dst_seqm->seqbase, seq, scene); } @@ -359,6 +360,7 @@ static bool seq_edit_split_effect_intersect_check(const Sequence *seq, const int static void seq_edit_split_handle_strip_offsets(Main *bmain, Scene *scene, + ListBase *seqbase, Sequence *left_seq, Sequence *right_seq, const int timeline_frame, @@ -374,7 +376,7 @@ static void seq_edit_split_handle_strip_offsets(Main *bmain, SEQ_add_reload_new_file(bmain, scene, right_seq, false); break; } - SEQ_time_update_sequence(scene, right_seq); + SEQ_time_update_sequence(scene, seqbase, right_seq); } if (seq_edit_split_effect_intersect_check(left_seq, timeline_frame)) { @@ -387,7 +389,7 @@ static void seq_edit_split_handle_strip_offsets(Main *bmain, SEQ_add_reload_new_file(bmain, scene, left_seq, false); break; } - SEQ_time_update_sequence(scene, left_seq); + SEQ_time_update_sequence(scene, seqbase, left_seq); } } @@ -509,7 +511,8 @@ Sequence *SEQ_edit_strip_split(Main *bmain, SEQ_collection_append_strip(right_seq, strips_to_delete); } - seq_edit_split_handle_strip_offsets(bmain, scene, left_seq, right_seq, timeline_frame, method); + seq_edit_split_handle_strip_offsets( + bmain, scene, seqbase, left_seq, right_seq, timeline_frame, method); left_seq = left_seq->next; right_seq = right_seq->next; } diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c index 444d3581b3d..d17a37cb9d8 100644 --- a/source/blender/sequencer/intern/strip_relations.c +++ b/source/blender/sequencer/intern/strip_relations.c @@ -330,7 +330,8 @@ static bool update_changed_seq_recurs( } if (len_change) { - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq); } } diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index fd6c0805c23..3c80e1dba27 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -148,7 +148,7 @@ void seq_update_sound_bounds_recursive(Scene *scene, Sequence *metaseq) scene, metaseq, metaseq_start(metaseq), metaseq_end(metaseq)); } -void SEQ_time_update_sequence_bounds(Scene *scene, Sequence *seq) +static void seq_time_update_sequence_bounds(Scene *scene, Sequence *seq) { if (seq->startofs && seq->startstill) { seq->startstill = 0; @@ -195,7 +195,7 @@ void SEQ_time_update_meta_strip_range(Scene *scene, Sequence *seq_meta) SEQ_transform_set_right_handle_frame(seq_meta, seq_meta->enddisp); } -void SEQ_time_update_sequence(Scene *scene, Sequence *seq) +void SEQ_time_update_sequence(Scene *scene, ListBase *seqbase, Sequence *seq) { Sequence *seqm; @@ -203,7 +203,7 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq) seqm = seq->seqbase.first; while (seqm) { if (seqm->seqbase.first) { - SEQ_time_update_sequence(scene, seqm); + SEQ_time_update_sequence(scene, &seqm->seqbase, seqm); } seqm = seqm->next; } @@ -241,22 +241,25 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq) seq->len = seq->enddisp - seq->startdisp; } else { - SEQ_time_update_sequence_bounds(scene, seq); + seq_time_update_sequence_bounds(scene, seq); } } + else if (seq->type == SEQ_TYPE_META) { + seq_time_update_meta_strip(scene, seq); + } else { - if (seq->type == SEQ_TYPE_META) { - seq_time_update_meta_strip(scene, seq); - } - - Editing *ed = SEQ_editing_get(scene); - MetaStack *ms = SEQ_meta_stack_active_get(ed); - if (ms != NULL) { - SEQ_time_update_meta_strip_range(scene, ms->parseq); - } - - SEQ_time_update_sequence_bounds(scene, seq); + seq_time_update_sequence_bounds(scene, seq); } + + Editing *ed = SEQ_editing_get(scene); + + /* Strip is inside meta strip */ + if (seqbase != &ed->seqbase) { + Sequence *meta = SEQ_get_meta_by_seqbase(&ed->seqbase, seqbase); + SEQ_time_update_meta_strip_range(scene, meta); + } + + seq_time_update_sequence_bounds(scene, seq); } int SEQ_time_find_next_prev_edit(Scene *scene, diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 54ca4ef487f..8c214f66b96 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -253,7 +253,8 @@ void SEQ_transform_translate_sequence(Scene *evil_scene, Sequence *seq, int delt SEQ_transform_set_right_handle_frame(seq, seq->enddisp + delta); } - SEQ_time_update_sequence(evil_scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(evil_scene)); + SEQ_time_update_sequence(evil_scene, seqbase, seq); } /* return 0 if there weren't enough space */ @@ -266,7 +267,7 @@ bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep, BLI_assert(ELEM(channel_delta, -1, 1)); test->machine += channel_delta; - SEQ_time_update_sequence(evil_scene, test); + SEQ_time_update_sequence(evil_scene, seqbasep, test); while (SEQ_transform_test_overlap(seqbasep, test)) { if ((channel_delta > 0) ? (test->machine >= MAXSEQ) : (test->machine < 1)) { break; @@ -275,7 +276,7 @@ bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep, test->machine += channel_delta; /* XXX: I don't think this is needed since were only moving vertically, Campbell. */ - SEQ_time_update_sequence(evil_scene, test); + SEQ_time_update_sequence(evil_scene, seqbasep, test); } if (!SEQ_valid_strip_channel(test)) { @@ -295,7 +296,7 @@ bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep, new_frame = new_frame + (test->start - test->startdisp); /* adjust by the startdisp */ SEQ_transform_translate_sequence(evil_scene, test, new_frame - test->start); - SEQ_time_update_sequence(evil_scene, test); + SEQ_time_update_sequence(evil_scene, seqbasep, test); return false; } @@ -355,7 +356,7 @@ static int shuffle_seq_time_offset(SeqCollection *strips_to_shuffle, } SEQ_ITERATOR_FOREACH (seq, strips_to_shuffle) { - SEQ_time_update_sequence_bounds(scene, seq); /* corrects dummy startdisp/enddisp values */ + SEQ_time_update_sequence(scene, seqbasep, seq); /* corrects dummy startdisp/enddisp values */ } return tot_ofs; @@ -408,7 +409,7 @@ void SEQ_transform_offset_after_frame(Scene *scene, LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (seq->startdisp >= timeline_frame) { SEQ_transform_translate_sequence(scene, seq, delta); - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); SEQ_relations_invalidate_cache_preprocessed(scene, seq); } } diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index 8421aab5217..71686065882 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -450,6 +450,21 @@ ListBase *SEQ_get_seqbase_by_seq(ListBase *seqbase, Sequence *seq) return NULL; } +Sequence *SEQ_get_meta_by_seqbase(ListBase *seqbase_main, ListBase *meta_seqbase) +{ + SeqCollection *strips = SEQ_query_all_strips_recursive(seqbase_main); + + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + if (seq->type == SEQ_TYPE_META && &seq->seqbase == meta_seqbase) { + break; + } + } + + SEQ_collection_free(strips); + return seq; +} + /** * Only use as last resort when the StripElem is available but no the Sequence. * (needed for RNA) diff --git a/source/blender/sequencer/intern/utils.h b/source/blender/sequencer/intern/utils.h index 7aee7d229c9..512647ed2e2 100644 --- a/source/blender/sequencer/intern/utils.h +++ b/source/blender/sequencer/intern/utils.h @@ -28,9 +28,12 @@ extern "C" { #endif struct Scene; +struct ListBase; bool sequencer_seq_generates_image(struct Sequence *seq); void seq_open_anim_file(struct Scene *scene, struct Sequence *seq, bool openfile); +Sequence *SEQ_get_meta_by_seqbase(struct ListBase *seqbase_main, struct ListBase *meta_seqbase); + #ifdef __cplusplus } #endif From 439c9b0b8478336f987b532212650c59b5f9f30f Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 7 Oct 2021 00:35:57 +0200 Subject: [PATCH 0592/1500] Cleanup: VSE iterator semantics Use `BLI_gset_ensure_p_ex()` instead of `BLI_gset_insert()` after checking `BLI_gset_lookup()`. --- source/blender/sequencer/intern/iterator.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index 2429405350b..e83d158881e 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -189,10 +189,12 @@ SeqCollection *SEQ_query_by_reference(Sequence *seq_reference, */ bool SEQ_collection_append_strip(Sequence *seq, SeqCollection *collection) { - if (BLI_gset_lookup(collection->set, seq) != NULL) { + void **key; + if (BLI_gset_ensure_p_ex(collection->set, seq, &key)) { return false; } - BLI_gset_insert(collection->set, seq); + + *key = (void *)seq; return true; } From 70cc80ea1c7f2678762c8ec31b924f9624172810 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 7 Oct 2021 03:04:34 +0200 Subject: [PATCH 0593/1500] Cleanup: Move VSE disk cache code into own file No functional changes. --- source/blender/sequencer/CMakeLists.txt | 2 + source/blender/sequencer/intern/disk_cache.c | 698 ++++++++++++++++++ source/blender/sequencer/intern/disk_cache.h | 56 ++ source/blender/sequencer/intern/image_cache.c | 667 +---------------- source/blender/sequencer/intern/image_cache.h | 21 +- 5 files changed, 780 insertions(+), 664 deletions(-) create mode 100644 source/blender/sequencer/intern/disk_cache.c create mode 100644 source/blender/sequencer/intern/disk_cache.h diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt index f060e6ad69b..eccc336141a 100644 --- a/source/blender/sequencer/CMakeLists.txt +++ b/source/blender/sequencer/CMakeLists.txt @@ -63,6 +63,8 @@ set(SRC SEQ_utils.h intern/clipboard.c + intern/disk_cache.c + intern/disk_cache.h intern/effects.c intern/effects.h intern/image_cache.c diff --git a/source/blender/sequencer/intern/disk_cache.c b/source/blender/sequencer/intern/disk_cache.c new file mode 100644 index 00000000000..543c23b184b --- /dev/null +++ b/source/blender/sequencer/intern/disk_cache.c @@ -0,0 +1,698 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup sequencer + */ + +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" +#include "DNA_sequence_types.h" +#include "DNA_space_types.h" /* for FILE_MAX. */ + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "BLI_blenlib.h" +#include "BLI_endian_defines.h" +#include "BLI_endian_switch.h" +#include "BLI_fileops.h" +#include "BLI_fileops_types.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_mempool.h" +#include "BLI_path_util.h" +#include "BLI_threads.h" + +#include "BKE_main.h" +#include "BKE_scene.h" + +#include "SEQ_prefetch.h" +#include "SEQ_relations.h" +#include "SEQ_render.h" +#include "SEQ_sequencer.h" + +#include "disk_cache.h" +#include "image_cache.h" +#include "prefetch.h" +#include "strip_time.h" + +/** + * Disk Cache Design Notes + * ======================= + * + * Disk cache uses directory specified in user preferences + * For each cached non-temp image, image data and supplementary info are written to HDD. + * Multiple(DCACHE_IMAGES_PER_FILE) images share the same file. + * Each of these files contains header DiskCacheHeader followed by image data. + * Zlib compression with user definable level can be used to compress image data(per image) + * Images are written in order in which they are rendered. + * Overwriting of individual entry is not possible. + * Stored images are deleted by invalidation, or when size of all files exceeds maximum + * size specified in user preferences. + * To distinguish 2 blend files with same name, scene->ed->disk_cache_timestamp + * is used as UID. Blend file can still be copied manually which may cause conflict. + */ + +/* -x-%()-.dcf */ +#define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf" +#define DCACHE_IMAGES_PER_FILE 100 +#define DCACHE_CURRENT_VERSION 2 +#define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */ + +typedef struct DiskCacheHeaderEntry { + unsigned char encoding; + uint64_t frameno; + uint64_t size_compressed; + uint64_t size_raw; + uint64_t offset; + char colorspace_name[COLORSPACE_NAME_MAX]; +} DiskCacheHeaderEntry; + +typedef struct DiskCacheHeader { + DiskCacheHeaderEntry entry[DCACHE_IMAGES_PER_FILE]; +} DiskCacheHeader; + +typedef struct SeqDiskCache { + Main *bmain; + int64_t timestamp; + ListBase files; + ThreadMutex read_write_mutex; + size_t size_total; +} SeqDiskCache; + +typedef struct DiskCacheFile { + struct DiskCacheFile *next, *prev; + char path[FILE_MAX]; + char dir[FILE_MAXDIR]; + char file[FILE_MAX]; + BLI_stat_t fstat; + int cache_type; + int rectx; + int recty; + int render_size; + int view_id; + int start_frame; +} DiskCacheFile; + +static ThreadMutex cache_create_lock = BLI_MUTEX_INITIALIZER; + +static char *seq_disk_cache_base_dir(void) +{ + return U.sequencer_disk_cache_dir; +} + +static int seq_disk_cache_compression_level(void) +{ + switch (U.sequencer_disk_cache_compression) { + case USER_SEQ_DISK_CACHE_COMPRESSION_NONE: + return 0; + case USER_SEQ_DISK_CACHE_COMPRESSION_LOW: + return 1; + case USER_SEQ_DISK_CACHE_COMPRESSION_HIGH: + return 9; + } + + return U.sequencer_disk_cache_compression; +} + +static size_t seq_disk_cache_size_limit(void) +{ + return (size_t)U.sequencer_disk_cache_size_limit * (1024 * 1024 * 1024); +} + +bool seq_disk_cache_is_enabled(Main *bmain) +{ + return (U.sequencer_disk_cache_dir[0] != '\0' && U.sequencer_disk_cache_size_limit != 0 && + (U.sequencer_disk_cache_flag & SEQ_CACHE_DISK_CACHE_ENABLE) != 0 && + bmain->name[0] != '\0'); +} + +static DiskCacheFile *seq_disk_cache_add_file_to_list(SeqDiskCache *disk_cache, const char *path) +{ + + DiskCacheFile *cache_file = MEM_callocN(sizeof(DiskCacheFile), "SeqDiskCacheFile"); + char dir[FILE_MAXDIR], file[FILE_MAX]; + BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file)); + BLI_strncpy(cache_file->path, path, sizeof(cache_file->path)); + BLI_strncpy(cache_file->dir, dir, sizeof(cache_file->dir)); + BLI_strncpy(cache_file->file, file, sizeof(cache_file->file)); + sscanf(file, + DCACHE_FNAME_FORMAT, + &cache_file->cache_type, + &cache_file->rectx, + &cache_file->recty, + &cache_file->render_size, + &cache_file->view_id, + &cache_file->start_frame); + cache_file->start_frame *= DCACHE_IMAGES_PER_FILE; + BLI_addtail(&disk_cache->files, cache_file); + return cache_file; +} + +static void seq_disk_cache_get_files(SeqDiskCache *disk_cache, char *path) +{ + struct direntry *filelist, *fl; + uint nbr, i; + disk_cache->size_total = 0; + + i = nbr = BLI_filelist_dir_contents(path, &filelist); + fl = filelist; + while (i--) { + /* Don't follow links. */ + const eFileAttributes file_attrs = BLI_file_attributes(fl->path); + if (file_attrs & FILE_ATTR_ANY_LINK) { + fl++; + continue; + } + + char file[FILE_MAX]; + BLI_split_dirfile(fl->path, NULL, file, 0, sizeof(file)); + + bool is_dir = BLI_is_dir(fl->path); + if (is_dir && !FILENAME_IS_CURRPAR(file)) { + char subpath[FILE_MAX]; + BLI_strncpy(subpath, fl->path, sizeof(subpath)); + BLI_path_slash_ensure(subpath); + seq_disk_cache_get_files(disk_cache, subpath); + } + + if (!is_dir) { + const char *ext = BLI_path_extension(fl->path); + if (ext && ext[1] == 'd' && ext[2] == 'c' && ext[3] == 'f') { + DiskCacheFile *cache_file = seq_disk_cache_add_file_to_list(disk_cache, fl->path); + cache_file->fstat = fl->s; + disk_cache->size_total += cache_file->fstat.st_size; + } + } + fl++; + } + BLI_filelist_free(filelist, nbr); +} + +static DiskCacheFile *seq_disk_cache_get_oldest_file(SeqDiskCache *disk_cache) +{ + DiskCacheFile *oldest_file = disk_cache->files.first; + if (oldest_file == NULL) { + return NULL; + } + for (DiskCacheFile *cache_file = oldest_file->next; cache_file; cache_file = cache_file->next) { + if (cache_file->fstat.st_mtime < oldest_file->fstat.st_mtime) { + oldest_file = cache_file; + } + } + + return oldest_file; +} + +static void seq_disk_cache_delete_file(SeqDiskCache *disk_cache, DiskCacheFile *file) +{ + disk_cache->size_total -= file->fstat.st_size; + BLI_delete(file->path, false, false); + BLI_remlink(&disk_cache->files, file); + MEM_freeN(file); +} + +bool seq_disk_cache_enforce_limits(SeqDiskCache *disk_cache) +{ + BLI_mutex_lock(&disk_cache->read_write_mutex); + while (disk_cache->size_total > seq_disk_cache_size_limit()) { + DiskCacheFile *oldest_file = seq_disk_cache_get_oldest_file(disk_cache); + + if (!oldest_file) { + /* We shouldn't enforce limits with no files, do re-scan. */ + seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); + continue; + } + + if (BLI_exists(oldest_file->path) == 0) { + /* File may have been manually deleted during runtime, do re-scan. */ + BLI_freelistN(&disk_cache->files); + seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); + continue; + } + + seq_disk_cache_delete_file(disk_cache, oldest_file); + } + BLI_mutex_unlock(&disk_cache->read_write_mutex); + + return true; +} + +static DiskCacheFile *seq_disk_cache_get_file_entry_by_path(SeqDiskCache *disk_cache, char *path) +{ + DiskCacheFile *cache_file = disk_cache->files.first; + + for (; cache_file; cache_file = cache_file->next) { + if (BLI_strcasecmp(cache_file->path, path) == 0) { + return cache_file; + } + } + + return NULL; +} + +/* Update file size and timestamp. */ +static void seq_disk_cache_update_file(SeqDiskCache *disk_cache, char *path) +{ + DiskCacheFile *cache_file; + int64_t size_before; + int64_t size_after; + + cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path); + size_before = cache_file->fstat.st_size; + + if (BLI_stat(path, &cache_file->fstat) == -1) { + BLI_assert(false); + memset(&cache_file->fstat, 0, sizeof(BLI_stat_t)); + } + + size_after = cache_file->fstat.st_size; + disk_cache->size_total += size_after - size_before; +} + +/* Path format: + * /_seq_cache/-//DCACHE_FNAME_FORMAT + */ + +static void seq_disk_cache_get_project_dir(SeqDiskCache *disk_cache, char *path, size_t path_len) +{ + char cache_dir[FILE_MAX]; + BLI_split_file_part(BKE_main_blendfile_path(disk_cache->bmain), cache_dir, sizeof(cache_dir)); + /* Use suffix, so that the cache directory name does not conflict with the bmain's blend file. */ + const char *suffix = "_seq_cache"; + strncat(cache_dir, suffix, sizeof(cache_dir) - strlen(cache_dir) - 1); + BLI_strncpy(path, seq_disk_cache_base_dir(), path_len); + BLI_path_append(path, path_len, cache_dir); +} + +static void seq_disk_cache_get_dir( + SeqDiskCache *disk_cache, Scene *scene, Sequence *seq, char *path, size_t path_len) +{ + char scene_name[MAX_ID_NAME + 22]; /* + -%PRId64 */ + char seq_name[SEQ_NAME_MAXSTR]; + char project_dir[FILE_MAX]; + + seq_disk_cache_get_project_dir(disk_cache, project_dir, sizeof(project_dir)); + sprintf(scene_name, "%s-%" PRId64, scene->id.name, disk_cache->timestamp); + BLI_strncpy(seq_name, seq->name, sizeof(seq_name)); + BLI_filename_make_safe(scene_name); + BLI_filename_make_safe(seq_name); + BLI_strncpy(path, project_dir, path_len); + BLI_path_append(path, path_len, scene_name); + BLI_path_append(path, path_len, seq_name); +} + +static void seq_disk_cache_get_file_path(SeqDiskCache *disk_cache, + SeqCacheKey *key, + char *path, + size_t path_len) +{ + seq_disk_cache_get_dir(disk_cache, key->context.scene, key->seq, path, path_len); + int frameno = (int)key->frame_index / DCACHE_IMAGES_PER_FILE; + char cache_filename[FILE_MAXFILE]; + sprintf(cache_filename, + DCACHE_FNAME_FORMAT, + key->type, + key->context.rectx, + key->context.recty, + key->context.preview_render_size, + key->context.view_id, + frameno); + + BLI_path_append(path, path_len, cache_filename); +} + +static void seq_disk_cache_create_version_file(char *path) +{ + BLI_make_existing_file(path); + + FILE *file = BLI_fopen(path, "w"); + if (file) { + fprintf(file, "%d", DCACHE_CURRENT_VERSION); + fclose(file); + } +} + +static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache) +{ + char path[FILE_MAX]; + char path_version_file[FILE_MAX]; + int version = 0; + + seq_disk_cache_get_project_dir(disk_cache, path, sizeof(path)); + BLI_strncpy(path_version_file, path, sizeof(path_version_file)); + BLI_path_append(path_version_file, sizeof(path_version_file), "cache_version"); + + if (BLI_exists(path) && BLI_is_dir(path)) { + FILE *file = BLI_fopen(path_version_file, "r"); + + if (file) { + const int num_items_read = fscanf(file, "%d", &version); + if (num_items_read == 0) { + version = -1; + } + fclose(file); + } + + if (version != DCACHE_CURRENT_VERSION) { + BLI_delete(path, false, true); + seq_disk_cache_create_version_file(path_version_file); + } + } + else { + seq_disk_cache_create_version_file(path_version_file); + } +} + +static void seq_disk_cache_delete_invalid_files(SeqDiskCache *disk_cache, + Scene *scene, + Sequence *seq, + int invalidate_types, + int range_start, + int range_end) +{ + DiskCacheFile *next_file, *cache_file = disk_cache->files.first; + char cache_dir[FILE_MAX]; + seq_disk_cache_get_dir(disk_cache, scene, seq, cache_dir, sizeof(cache_dir)); + BLI_path_slash_ensure(cache_dir); + + while (cache_file) { + next_file = cache_file->next; + if (cache_file->cache_type & invalidate_types) { + if (STREQ(cache_dir, cache_file->dir)) { + int timeline_frame_start = seq_cache_frame_index_to_timeline_frame( + seq, cache_file->start_frame); + if (timeline_frame_start > range_start && timeline_frame_start <= range_end) { + seq_disk_cache_delete_file(disk_cache, cache_file); + } + } + } + cache_file = next_file; + } +} + +void seq_disk_cache_invalidate(SeqDiskCache *disk_cache, + Scene *scene, + Sequence *seq, + Sequence *seq_changed, + int invalidate_types) +{ + int start; + int end; + + BLI_mutex_lock(&disk_cache->read_write_mutex); + + start = seq_changed->startdisp - DCACHE_IMAGES_PER_FILE; + end = seq_changed->enddisp; + + seq_disk_cache_delete_invalid_files(disk_cache, scene, seq, invalidate_types, start, end); + + BLI_mutex_unlock(&disk_cache->read_write_mutex); +} + +static size_t deflate_imbuf_to_file(ImBuf *ibuf, + FILE *file, + int level, + DiskCacheHeaderEntry *header_entry) +{ + void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; + + /* Apply compression if wanted, otherwise just write directly to the file. */ + if (level > 0) { + return BLI_file_zstd_from_mem_at_pos( + data, header_entry->size_raw, file, header_entry->offset, level); + } + + fseek(file, header_entry->offset, SEEK_SET); + return fwrite(data, 1, header_entry->size_raw, file); +} + +static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry) +{ + void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; + char header[4]; + fseek(file, header_entry->offset, SEEK_SET); + if (fread(header, 1, sizeof(header), file) != sizeof(header)) { + return 0; + } + + /* Check if the data is compressed or raw. */ + if (BLI_file_magic_is_zstd(header)) { + return BLI_file_unzstd_to_mem_at_pos(data, header_entry->size_raw, file, header_entry->offset); + } + + fseek(file, header_entry->offset, SEEK_SET); + return fread(data, 1, header_entry->size_raw, file); +} + +static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) +{ + BLI_fseek(file, 0LL, SEEK_SET); + const size_t num_items_read = fread(header, sizeof(*header), 1, file); + if (num_items_read < 1) { + BLI_assert_msg(0, "unable to read disk cache header"); + perror("unable to read disk cache header"); + return false; + } + + for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { + if ((ENDIAN_ORDER == B_ENDIAN) && header->entry[i].encoding == 0) { + BLI_endian_switch_uint64(&header->entry[i].frameno); + BLI_endian_switch_uint64(&header->entry[i].offset); + BLI_endian_switch_uint64(&header->entry[i].size_compressed); + BLI_endian_switch_uint64(&header->entry[i].size_raw); + } + } + + return true; +} + +static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header) +{ + BLI_fseek(file, 0LL, SEEK_SET); + return fwrite(header, sizeof(*header), 1, file); +} + +static int seq_disk_cache_add_header_entry(SeqCacheKey *key, ImBuf *ibuf, DiskCacheHeader *header) +{ + int i; + uint64_t offset = sizeof(*header); + + /* Lookup free entry, get offset for new data. */ + for (i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { + if (header->entry[i].size_compressed == 0) { + break; + } + } + + /* Attempt to write beyond set entry limit. + * Reset file header and start writing from beginning. + */ + if (i == DCACHE_IMAGES_PER_FILE) { + i = 0; + memset(header, 0, sizeof(*header)); + } + + /* Calculate offset for image data. */ + if (i > 0) { + offset = header->entry[i - 1].offset + header->entry[i - 1].size_compressed; + } + + if (ENDIAN_ORDER == B_ENDIAN) { + header->entry[i].encoding = 255; + } + else { + header->entry[i].encoding = 0; + } + + header->entry[i].offset = offset; + header->entry[i].frameno = key->frame_index; + + /* Store colorspace name of ibuf. */ + const char *colorspace_name; + if (ibuf->rect) { + header->entry[i].size_raw = ibuf->x * ibuf->y * ibuf->channels; + colorspace_name = IMB_colormanagement_get_rect_colorspace(ibuf); + } + else { + header->entry[i].size_raw = ibuf->x * ibuf->y * ibuf->channels * 4; + colorspace_name = IMB_colormanagement_get_float_colorspace(ibuf); + } + BLI_strncpy( + header->entry[i].colorspace_name, colorspace_name, sizeof(header->entry[i].colorspace_name)); + + return i; +} + +static int seq_disk_cache_get_header_entry(SeqCacheKey *key, DiskCacheHeader *header) +{ + for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { + if (header->entry[i].frameno == key->frame_index) { + return i; + } + } + + return -1; +} + +bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf *ibuf) +{ + BLI_mutex_lock(&disk_cache->read_write_mutex); + + char path[FILE_MAX]; + + seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); + BLI_make_existing_file(path); + + FILE *file = BLI_fopen(path, "rb+"); + if (!file) { + file = BLI_fopen(path, "wb+"); + if (!file) { + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return false; + } + seq_disk_cache_add_file_to_list(disk_cache, path); + } + + DiskCacheFile *cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path); + DiskCacheHeader header; + memset(&header, 0, sizeof(header)); + /* #BLI_make_existing_file() above may create an empty file. This is fine, don't attempt reading + * the header in that case. */ + if (cache_file->fstat.st_size != 0 && !seq_disk_cache_read_header(file, &header)) { + fclose(file); + seq_disk_cache_delete_file(disk_cache, cache_file); + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return false; + } + int entry_index = seq_disk_cache_add_header_entry(key, ibuf, &header); + + size_t bytes_written = deflate_imbuf_to_file( + ibuf, file, seq_disk_cache_compression_level(), &header.entry[entry_index]); + + if (bytes_written != 0) { + /* Last step is writing header, as image data can be overwritten, + * but missing data would cause problems. + */ + header.entry[entry_index].size_compressed = bytes_written; + seq_disk_cache_write_header(file, &header); + seq_disk_cache_update_file(disk_cache, path); + fclose(file); + + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return true; + } + + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return false; +} + +ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key) +{ + BLI_mutex_lock(&disk_cache->read_write_mutex); + + char path[FILE_MAX]; + DiskCacheHeader header; + + seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); + BLI_make_existing_file(path); + + FILE *file = BLI_fopen(path, "rb"); + if (!file) { + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return NULL; + } + + if (!seq_disk_cache_read_header(file, &header)) { + fclose(file); + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return NULL; + } + int entry_index = seq_disk_cache_get_header_entry(key, &header); + + /* Item not found. */ + if (entry_index < 0) { + fclose(file); + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return NULL; + } + + ImBuf *ibuf; + uint64_t size_char = (uint64_t)key->context.rectx * key->context.recty * 4; + uint64_t size_float = (uint64_t)key->context.rectx * key->context.recty * 16; + size_t expected_size; + + if (header.entry[entry_index].size_raw == size_char) { + expected_size = size_char; + ibuf = IMB_allocImBuf(key->context.rectx, key->context.recty, 32, IB_rect); + IMB_colormanagement_assign_rect_colorspace(ibuf, header.entry[entry_index].colorspace_name); + } + else if (header.entry[entry_index].size_raw == size_float) { + expected_size = size_float; + ibuf = IMB_allocImBuf(key->context.rectx, key->context.recty, 32, IB_rectfloat); + IMB_colormanagement_assign_float_colorspace(ibuf, header.entry[entry_index].colorspace_name); + } + else { + fclose(file); + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return NULL; + } + + size_t bytes_read = inflate_file_to_imbuf(ibuf, file, &header.entry[entry_index]); + + /* Sanity check. */ + if (bytes_read != expected_size) { + fclose(file); + IMB_freeImBuf(ibuf); + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return NULL; + } + BLI_file_touch(path); + seq_disk_cache_update_file(disk_cache, path); + fclose(file); + + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return ibuf; +} + +SeqDiskCache *seq_disk_cache_create(Main *bmain, Scene *scene) +{ + SeqDiskCache *disk_cache = MEM_callocN(sizeof(SeqDiskCache), "SeqDiskCache"); + disk_cache->bmain = bmain; + BLI_mutex_init(&disk_cache->read_write_mutex); + seq_disk_cache_handle_versioning(disk_cache); + seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); + disk_cache->timestamp = scene->ed->disk_cache_timestamp; + BLI_mutex_unlock(&cache_create_lock); + return disk_cache; +} + +void seq_disk_cache_free(SeqDiskCache *disk_cache) +{ + BLI_freelistN(&disk_cache->files); + BLI_mutex_end(&disk_cache->read_write_mutex); + MEM_freeN(disk_cache); +} diff --git a/source/blender/sequencer/intern/disk_cache.h b/source/blender/sequencer/intern/disk_cache.h new file mode 100644 index 00000000000..a84bfaf5ea0 --- /dev/null +++ b/source/blender/sequencer/intern/disk_cache.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup sequencer + */ + +#pragma once + +/** \file + * \ingroup sequencer + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct ImBuf; +struct Main; +struct Scene; +struct SeqCacheKey; +struct SeqDiskCache; +struct Sequence; + +struct SeqDiskCache *seq_disk_cache_create(struct Main *bmain, struct Scene *scene); +void seq_disk_cache_free(struct SeqDiskCache *disk_cache); +bool seq_disk_cache_is_enabled(struct Main *bmain); +struct ImBuf *seq_disk_cache_read_file(struct SeqDiskCache *disk_cache, struct SeqCacheKey *key); +bool seq_disk_cache_write_file(struct SeqDiskCache *disk_cache, + struct SeqCacheKey *key, + struct ImBuf *ibuf); +bool seq_disk_cache_enforce_limits(struct SeqDiskCache *disk_cache); +void seq_disk_cache_invalidate(struct SeqDiskCache *disk_cache, + struct Scene *scene, + struct Sequence *seq, + struct Sequence *seq_changed, + int invalidate_types); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index 86c198075e9..7b3d342b2e4 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -50,9 +50,9 @@ #include "SEQ_prefetch.h" #include "SEQ_relations.h" -#include "SEQ_render.h" #include "SEQ_sequencer.h" +#include "disk_cache.h" #include "image_cache.h" #include "prefetch.h" #include "strip_time.h" @@ -80,67 +80,10 @@ * entries one by one in reverse order to their creation. * * User can exclude caching of some images. Such entries will have is_temp_cache set. - * - * - * Disk Cache Design Notes - * ======================= - * - * Disk cache uses directory specified in user preferences - * For each cached non-temp image, image data and supplementary info are written to HDD. - * Multiple(DCACHE_IMAGES_PER_FILE) images share the same file. - * Each of these files contains header DiskCacheHeader followed by image data. - * Zlib compression with user definable level can be used to compress image data(per image) - * Images are written in order in which they are rendered. - * Overwriting of individual entry is not possible. - * Stored images are deleted by invalidation, or when size of all files exceeds maximum - * size specified in user preferences. - * To distinguish 2 blend files with same name, scene->ed->disk_cache_timestamp - * is used as UID. Blend file can still be copied manually which may cause conflict. - * */ -/* -x-%()-.dcf */ -#define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf" -#define DCACHE_IMAGES_PER_FILE 100 -#define DCACHE_CURRENT_VERSION 2 -#define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */ #define THUMB_CACHE_LIMIT 5000 -typedef struct DiskCacheHeaderEntry { - unsigned char encoding; - uint64_t frameno; - uint64_t size_compressed; - uint64_t size_raw; - uint64_t offset; - char colorspace_name[COLORSPACE_NAME_MAX]; -} DiskCacheHeaderEntry; - -typedef struct DiskCacheHeader { - DiskCacheHeaderEntry entry[DCACHE_IMAGES_PER_FILE]; -} DiskCacheHeader; - -typedef struct SeqDiskCache { - Main *bmain; - int64_t timestamp; - ListBase files; - ThreadMutex read_write_mutex; - size_t size_total; -} SeqDiskCache; - -typedef struct DiskCacheFile { - struct DiskCacheFile *next, *prev; - char path[FILE_MAX]; - char dir[FILE_MAXDIR]; - char file[FILE_MAX]; - BLI_stat_t fstat; - int cache_type; - int rectx; - int recty; - int render_size; - int view_id; - int start_frame; -} DiskCacheFile; - typedef struct SeqCache { Main *bmain; struct GHash *hash; @@ -148,7 +91,7 @@ typedef struct SeqCache { struct BLI_mempool *keys_pool; struct BLI_mempool *items_pool; struct SeqCacheKey *last_key; - SeqDiskCache *disk_cache; + struct SeqDiskCache *disk_cache; int thumbnail_count; } SeqCache; @@ -157,577 +100,7 @@ typedef struct SeqCacheItem { struct ImBuf *ibuf; } SeqCacheItem; -typedef struct SeqCacheKey { - struct SeqCache *cache_owner; - void *userkey; - struct SeqCacheKey *link_prev; /* Used for linking intermediate items to final frame. */ - struct SeqCacheKey *link_next; /* Used for linking intermediate items to final frame. */ - struct Sequence *seq; - SeqRenderData context; - float frame_index; /* Usually same as timeline_frame. Mapped to media for RAW entries. */ - float timeline_frame; /* Only for reference - used for freeing when cache is full. */ - float cost; /* In short: render time(s) divided by playback frame duration(s) */ - bool is_temp_cache; /* this cache entry will be freed before rendering next frame */ - /* ID of task for assigning temp cache entries to particular task(thread, etc.) */ - eSeqTaskId task_id; - int type; -} SeqCacheKey; - static ThreadMutex cache_create_lock = BLI_MUTEX_INITIALIZER; -static float seq_cache_timeline_frame_to_frame_index(Sequence *seq, - float timeline_frame, - int type); -static float seq_cache_frame_index_to_timeline_frame(Sequence *seq, float frame_index); - -static char *seq_disk_cache_base_dir(void) -{ - return U.sequencer_disk_cache_dir; -} - -static int seq_disk_cache_compression_level(void) -{ - switch (U.sequencer_disk_cache_compression) { - case USER_SEQ_DISK_CACHE_COMPRESSION_NONE: - return 0; - case USER_SEQ_DISK_CACHE_COMPRESSION_LOW: - return 1; - case USER_SEQ_DISK_CACHE_COMPRESSION_HIGH: - return 9; - } - - return U.sequencer_disk_cache_compression; -} - -static size_t seq_disk_cache_size_limit(void) -{ - return (size_t)U.sequencer_disk_cache_size_limit * (1024 * 1024 * 1024); -} - -static bool seq_disk_cache_is_enabled(Main *bmain) -{ - return (U.sequencer_disk_cache_dir[0] != '\0' && U.sequencer_disk_cache_size_limit != 0 && - (U.sequencer_disk_cache_flag & SEQ_CACHE_DISK_CACHE_ENABLE) != 0 && - bmain->name[0] != '\0'); -} - -static DiskCacheFile *seq_disk_cache_add_file_to_list(SeqDiskCache *disk_cache, const char *path) -{ - - DiskCacheFile *cache_file = MEM_callocN(sizeof(DiskCacheFile), "SeqDiskCacheFile"); - char dir[FILE_MAXDIR], file[FILE_MAX]; - BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file)); - BLI_strncpy(cache_file->path, path, sizeof(cache_file->path)); - BLI_strncpy(cache_file->dir, dir, sizeof(cache_file->dir)); - BLI_strncpy(cache_file->file, file, sizeof(cache_file->file)); - sscanf(file, - DCACHE_FNAME_FORMAT, - &cache_file->cache_type, - &cache_file->rectx, - &cache_file->recty, - &cache_file->render_size, - &cache_file->view_id, - &cache_file->start_frame); - cache_file->start_frame *= DCACHE_IMAGES_PER_FILE; - BLI_addtail(&disk_cache->files, cache_file); - return cache_file; -} - -static void seq_disk_cache_get_files(SeqDiskCache *disk_cache, char *path) -{ - struct direntry *filelist, *fl; - uint nbr, i; - disk_cache->size_total = 0; - - i = nbr = BLI_filelist_dir_contents(path, &filelist); - fl = filelist; - while (i--) { - /* Don't follow links. */ - const eFileAttributes file_attrs = BLI_file_attributes(fl->path); - if (file_attrs & FILE_ATTR_ANY_LINK) { - fl++; - continue; - } - - char file[FILE_MAX]; - BLI_split_dirfile(fl->path, NULL, file, 0, sizeof(file)); - - bool is_dir = BLI_is_dir(fl->path); - if (is_dir && !FILENAME_IS_CURRPAR(file)) { - char subpath[FILE_MAX]; - BLI_strncpy(subpath, fl->path, sizeof(subpath)); - BLI_path_slash_ensure(subpath); - seq_disk_cache_get_files(disk_cache, subpath); - } - - if (!is_dir) { - const char *ext = BLI_path_extension(fl->path); - if (ext && ext[1] == 'd' && ext[2] == 'c' && ext[3] == 'f') { - DiskCacheFile *cache_file = seq_disk_cache_add_file_to_list(disk_cache, fl->path); - cache_file->fstat = fl->s; - disk_cache->size_total += cache_file->fstat.st_size; - } - } - fl++; - } - BLI_filelist_free(filelist, nbr); -} - -static DiskCacheFile *seq_disk_cache_get_oldest_file(SeqDiskCache *disk_cache) -{ - DiskCacheFile *oldest_file = disk_cache->files.first; - if (oldest_file == NULL) { - return NULL; - } - for (DiskCacheFile *cache_file = oldest_file->next; cache_file; cache_file = cache_file->next) { - if (cache_file->fstat.st_mtime < oldest_file->fstat.st_mtime) { - oldest_file = cache_file; - } - } - - return oldest_file; -} - -static void seq_disk_cache_delete_file(SeqDiskCache *disk_cache, DiskCacheFile *file) -{ - disk_cache->size_total -= file->fstat.st_size; - BLI_delete(file->path, false, false); - BLI_remlink(&disk_cache->files, file); - MEM_freeN(file); -} - -static bool seq_disk_cache_enforce_limits(SeqDiskCache *disk_cache) -{ - BLI_mutex_lock(&disk_cache->read_write_mutex); - while (disk_cache->size_total > seq_disk_cache_size_limit()) { - DiskCacheFile *oldest_file = seq_disk_cache_get_oldest_file(disk_cache); - - if (!oldest_file) { - /* We shouldn't enforce limits with no files, do re-scan. */ - seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); - continue; - } - - if (BLI_exists(oldest_file->path) == 0) { - /* File may have been manually deleted during runtime, do re-scan. */ - BLI_freelistN(&disk_cache->files); - seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); - continue; - } - - seq_disk_cache_delete_file(disk_cache, oldest_file); - } - BLI_mutex_unlock(&disk_cache->read_write_mutex); - - return true; -} - -static DiskCacheFile *seq_disk_cache_get_file_entry_by_path(SeqDiskCache *disk_cache, char *path) -{ - DiskCacheFile *cache_file = disk_cache->files.first; - - for (; cache_file; cache_file = cache_file->next) { - if (BLI_strcasecmp(cache_file->path, path) == 0) { - return cache_file; - } - } - - return NULL; -} - -/* Update file size and timestamp. */ -static void seq_disk_cache_update_file(SeqDiskCache *disk_cache, char *path) -{ - DiskCacheFile *cache_file; - int64_t size_before; - int64_t size_after; - - cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path); - size_before = cache_file->fstat.st_size; - - if (BLI_stat(path, &cache_file->fstat) == -1) { - BLI_assert(false); - memset(&cache_file->fstat, 0, sizeof(BLI_stat_t)); - } - - size_after = cache_file->fstat.st_size; - disk_cache->size_total += size_after - size_before; -} - -/* Path format: - * /_seq_cache/-//DCACHE_FNAME_FORMAT - */ - -static void seq_disk_cache_get_project_dir(SeqDiskCache *disk_cache, char *path, size_t path_len) -{ - char cache_dir[FILE_MAX]; - BLI_split_file_part(BKE_main_blendfile_path(disk_cache->bmain), cache_dir, sizeof(cache_dir)); - /* Use suffix, so that the cache directory name does not conflict with the bmain's blend file. */ - const char *suffix = "_seq_cache"; - strncat(cache_dir, suffix, sizeof(cache_dir) - strlen(cache_dir) - 1); - BLI_strncpy(path, seq_disk_cache_base_dir(), path_len); - BLI_path_append(path, path_len, cache_dir); -} - -static void seq_disk_cache_get_dir( - SeqDiskCache *disk_cache, Scene *scene, Sequence *seq, char *path, size_t path_len) -{ - char scene_name[MAX_ID_NAME + 22]; /* + -%PRId64 */ - char seq_name[SEQ_NAME_MAXSTR]; - char project_dir[FILE_MAX]; - - seq_disk_cache_get_project_dir(disk_cache, project_dir, sizeof(project_dir)); - sprintf(scene_name, "%s-%" PRId64, scene->id.name, disk_cache->timestamp); - BLI_strncpy(seq_name, seq->name, sizeof(seq_name)); - BLI_filename_make_safe(scene_name); - BLI_filename_make_safe(seq_name); - BLI_strncpy(path, project_dir, path_len); - BLI_path_append(path, path_len, scene_name); - BLI_path_append(path, path_len, seq_name); -} - -static void seq_disk_cache_get_file_path(SeqDiskCache *disk_cache, - SeqCacheKey *key, - char *path, - size_t path_len) -{ - seq_disk_cache_get_dir(disk_cache, key->context.scene, key->seq, path, path_len); - int frameno = (int)key->frame_index / DCACHE_IMAGES_PER_FILE; - char cache_filename[FILE_MAXFILE]; - sprintf(cache_filename, - DCACHE_FNAME_FORMAT, - key->type, - key->context.rectx, - key->context.recty, - key->context.preview_render_size, - key->context.view_id, - frameno); - - BLI_path_append(path, path_len, cache_filename); -} - -static void seq_disk_cache_create_version_file(char *path) -{ - BLI_make_existing_file(path); - - FILE *file = BLI_fopen(path, "w"); - if (file) { - fprintf(file, "%d", DCACHE_CURRENT_VERSION); - fclose(file); - } -} - -static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache) -{ - char path[FILE_MAX]; - char path_version_file[FILE_MAX]; - int version = 0; - - seq_disk_cache_get_project_dir(disk_cache, path, sizeof(path)); - BLI_strncpy(path_version_file, path, sizeof(path_version_file)); - BLI_path_append(path_version_file, sizeof(path_version_file), "cache_version"); - - if (BLI_exists(path) && BLI_is_dir(path)) { - FILE *file = BLI_fopen(path_version_file, "r"); - - if (file) { - const int num_items_read = fscanf(file, "%d", &version); - if (num_items_read == 0) { - version = -1; - } - fclose(file); - } - - if (version != DCACHE_CURRENT_VERSION) { - BLI_delete(path, false, true); - seq_disk_cache_create_version_file(path_version_file); - } - } - else { - seq_disk_cache_create_version_file(path_version_file); - } -} - -static void seq_disk_cache_delete_invalid_files(SeqDiskCache *disk_cache, - Scene *scene, - Sequence *seq, - int invalidate_types, - int range_start, - int range_end) -{ - DiskCacheFile *next_file, *cache_file = disk_cache->files.first; - char cache_dir[FILE_MAX]; - seq_disk_cache_get_dir(disk_cache, scene, seq, cache_dir, sizeof(cache_dir)); - BLI_path_slash_ensure(cache_dir); - - while (cache_file) { - next_file = cache_file->next; - if (cache_file->cache_type & invalidate_types) { - if (STREQ(cache_dir, cache_file->dir)) { - int timeline_frame_start = seq_cache_frame_index_to_timeline_frame( - seq, cache_file->start_frame); - if (timeline_frame_start > range_start && timeline_frame_start <= range_end) { - seq_disk_cache_delete_file(disk_cache, cache_file); - } - } - } - cache_file = next_file; - } -} - -static void seq_disk_cache_invalidate(Scene *scene, - Sequence *seq, - Sequence *seq_changed, - int invalidate_types) -{ - int start; - int end; - SeqDiskCache *disk_cache = scene->ed->cache->disk_cache; - - BLI_mutex_lock(&disk_cache->read_write_mutex); - - start = seq_changed->startdisp - DCACHE_IMAGES_PER_FILE; - end = seq_changed->enddisp; - - seq_disk_cache_delete_invalid_files(disk_cache, scene, seq, invalidate_types, start, end); - - BLI_mutex_unlock(&disk_cache->read_write_mutex); -} - -static size_t deflate_imbuf_to_file(ImBuf *ibuf, - FILE *file, - int level, - DiskCacheHeaderEntry *header_entry) -{ - void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; - - /* Apply compression if wanted, otherwise just write directly to the file. */ - if (level > 0) { - return BLI_file_zstd_from_mem_at_pos( - data, header_entry->size_raw, file, header_entry->offset, level); - } - - fseek(file, header_entry->offset, SEEK_SET); - return fwrite(data, 1, header_entry->size_raw, file); -} - -static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry) -{ - void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; - char header[4]; - fseek(file, header_entry->offset, SEEK_SET); - if (fread(header, 1, sizeof(header), file) != sizeof(header)) { - return 0; - } - - /* Check if the data is compressed or raw. */ - if (BLI_file_magic_is_zstd(header)) { - return BLI_file_unzstd_to_mem_at_pos(data, header_entry->size_raw, file, header_entry->offset); - } - - fseek(file, header_entry->offset, SEEK_SET); - return fread(data, 1, header_entry->size_raw, file); -} - -static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) -{ - BLI_fseek(file, 0LL, SEEK_SET); - const size_t num_items_read = fread(header, sizeof(*header), 1, file); - if (num_items_read < 1) { - BLI_assert_msg(0, "unable to read disk cache header"); - perror("unable to read disk cache header"); - return false; - } - - for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { - if ((ENDIAN_ORDER == B_ENDIAN) && header->entry[i].encoding == 0) { - BLI_endian_switch_uint64(&header->entry[i].frameno); - BLI_endian_switch_uint64(&header->entry[i].offset); - BLI_endian_switch_uint64(&header->entry[i].size_compressed); - BLI_endian_switch_uint64(&header->entry[i].size_raw); - } - } - - return true; -} - -static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header) -{ - BLI_fseek(file, 0LL, SEEK_SET); - return fwrite(header, sizeof(*header), 1, file); -} - -static int seq_disk_cache_add_header_entry(SeqCacheKey *key, ImBuf *ibuf, DiskCacheHeader *header) -{ - int i; - uint64_t offset = sizeof(*header); - - /* Lookup free entry, get offset for new data. */ - for (i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { - if (header->entry[i].size_compressed == 0) { - break; - } - } - - /* Attempt to write beyond set entry limit. - * Reset file header and start writing from beginning. - */ - if (i == DCACHE_IMAGES_PER_FILE) { - i = 0; - memset(header, 0, sizeof(*header)); - } - - /* Calculate offset for image data. */ - if (i > 0) { - offset = header->entry[i - 1].offset + header->entry[i - 1].size_compressed; - } - - if (ENDIAN_ORDER == B_ENDIAN) { - header->entry[i].encoding = 255; - } - else { - header->entry[i].encoding = 0; - } - - header->entry[i].offset = offset; - header->entry[i].frameno = key->frame_index; - - /* Store colorspace name of ibuf. */ - const char *colorspace_name; - if (ibuf->rect) { - header->entry[i].size_raw = ibuf->x * ibuf->y * ibuf->channels; - colorspace_name = IMB_colormanagement_get_rect_colorspace(ibuf); - } - else { - header->entry[i].size_raw = ibuf->x * ibuf->y * ibuf->channels * 4; - colorspace_name = IMB_colormanagement_get_float_colorspace(ibuf); - } - BLI_strncpy( - header->entry[i].colorspace_name, colorspace_name, sizeof(header->entry[i].colorspace_name)); - - return i; -} - -static int seq_disk_cache_get_header_entry(SeqCacheKey *key, DiskCacheHeader *header) -{ - for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { - if (header->entry[i].frameno == key->frame_index) { - return i; - } - } - - return -1; -} - -static bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf *ibuf) -{ - char path[FILE_MAX]; - - seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); - BLI_make_existing_file(path); - - FILE *file = BLI_fopen(path, "rb+"); - if (!file) { - file = BLI_fopen(path, "wb+"); - if (!file) { - return false; - } - seq_disk_cache_add_file_to_list(disk_cache, path); - } - - DiskCacheFile *cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path); - DiskCacheHeader header; - memset(&header, 0, sizeof(header)); - /* #BLI_make_existing_file() above may create an empty file. This is fine, don't attempt reading - * the header in that case. */ - if (cache_file->fstat.st_size != 0 && !seq_disk_cache_read_header(file, &header)) { - fclose(file); - seq_disk_cache_delete_file(disk_cache, cache_file); - return false; - } - int entry_index = seq_disk_cache_add_header_entry(key, ibuf, &header); - - size_t bytes_written = deflate_imbuf_to_file( - ibuf, file, seq_disk_cache_compression_level(), &header.entry[entry_index]); - - if (bytes_written != 0) { - /* Last step is writing header, as image data can be overwritten, - * but missing data would cause problems. - */ - header.entry[entry_index].size_compressed = bytes_written; - seq_disk_cache_write_header(file, &header); - seq_disk_cache_update_file(disk_cache, path); - fclose(file); - - return true; - } - - return false; -} - -static ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key) -{ - char path[FILE_MAX]; - DiskCacheHeader header; - - seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); - BLI_make_existing_file(path); - - FILE *file = BLI_fopen(path, "rb"); - if (!file) { - return NULL; - } - - if (!seq_disk_cache_read_header(file, &header)) { - fclose(file); - return NULL; - } - int entry_index = seq_disk_cache_get_header_entry(key, &header); - - /* Item not found. */ - if (entry_index < 0) { - fclose(file); - return NULL; - } - - ImBuf *ibuf; - uint64_t size_char = (uint64_t)key->context.rectx * key->context.recty * 4; - uint64_t size_float = (uint64_t)key->context.rectx * key->context.recty * 16; - size_t expected_size; - - if (header.entry[entry_index].size_raw == size_char) { - expected_size = size_char; - ibuf = IMB_allocImBuf(key->context.rectx, key->context.recty, 32, IB_rect); - IMB_colormanagement_assign_rect_colorspace(ibuf, header.entry[entry_index].colorspace_name); - } - else if (header.entry[entry_index].size_raw == size_float) { - expected_size = size_float; - ibuf = IMB_allocImBuf(key->context.rectx, key->context.recty, 32, IB_rectfloat); - IMB_colormanagement_assign_float_colorspace(ibuf, header.entry[entry_index].colorspace_name); - } - else { - fclose(file); - return NULL; - } - - size_t bytes_read = inflate_file_to_imbuf(ibuf, file, &header.entry[entry_index]); - - /* Sanity check. */ - if (bytes_read != expected_size) { - fclose(file); - IMB_freeImBuf(ibuf); - return NULL; - } - BLI_file_touch(path); - seq_disk_cache_update_file(disk_cache, path); - fclose(file); - - return ibuf; -} - -#undef DCACHE_FNAME_FORMAT -#undef DCACHE_IMAGES_PER_FILE -#undef COLORSPACE_NAME_MAX -#undef DCACHE_CURRENT_VERSION static bool seq_cmp_render_data(const SeqRenderData *a, const SeqRenderData *b) { @@ -785,7 +158,7 @@ static float seq_cache_timeline_frame_to_frame_index(Sequence *seq, float timeli return timeline_frame - seq->start; } -static float seq_cache_frame_index_to_timeline_frame(Sequence *seq, float frame_index) +float seq_cache_frame_index_to_timeline_frame(Sequence *seq, float frame_index) { return frame_index + seq->start; } @@ -1131,28 +504,6 @@ static void seq_cache_set_temp_cache_linked(Scene *scene, SeqCacheKey *base) } } -static void seq_disk_cache_create(Main *bmain, Scene *scene) -{ - BLI_mutex_lock(&cache_create_lock); - SeqCache *cache = seq_cache_get_from_scene(scene); - - if (cache == NULL) { - return; - } - - if (cache->disk_cache != NULL) { - return; - } - - cache->disk_cache = MEM_callocN(sizeof(SeqDiskCache), "SeqDiskCache"); - cache->disk_cache->bmain = bmain; - BLI_mutex_init(&cache->disk_cache->read_write_mutex); - seq_disk_cache_handle_versioning(cache->disk_cache); - seq_disk_cache_get_files(cache->disk_cache, seq_disk_cache_base_dir()); - cache->disk_cache->timestamp = scene->ed->disk_cache_timestamp; - BLI_mutex_unlock(&cache_create_lock); -} - static void seq_cache_create(Main *bmain, Scene *scene) { BLI_mutex_lock(&cache_create_lock); @@ -1246,9 +597,7 @@ void seq_cache_destruct(Scene *scene) BLI_mutex_end(&cache->iterator_mutex); if (cache->disk_cache != NULL) { - BLI_freelistN(&cache->disk_cache->files); - BLI_mutex_end(&cache->disk_cache->read_write_mutex); - MEM_freeN(cache->disk_cache); + seq_disk_cache_free(cache->disk_cache); } MEM_freeN(cache); @@ -1297,7 +646,7 @@ void seq_cache_cleanup_sequence(Scene *scene, } if (seq_disk_cache_is_enabled(cache->bmain) && cache->disk_cache != NULL) { - seq_disk_cache_invalidate(scene, seq, seq_changed, invalidate_types); + seq_disk_cache_invalidate(cache->disk_cache, scene, seq, seq_changed, invalidate_types); } seq_cache_lock(scene); @@ -1434,12 +783,10 @@ struct ImBuf *seq_cache_get(const SeqRenderData *context, /* Try disk cache: */ if (seq_disk_cache_is_enabled(context->bmain)) { if (cache->disk_cache == NULL) { - seq_disk_cache_create(context->bmain, context->scene); + cache->disk_cache = seq_disk_cache_create(context->bmain, context->scene); } - BLI_mutex_lock(&cache->disk_cache->read_write_mutex); ibuf = seq_disk_cache_read_file(cache->disk_cache, &key); - BLI_mutex_unlock(&cache->disk_cache->read_write_mutex); if (ibuf == NULL) { return NULL; @@ -1550,9 +897,7 @@ void seq_cache_put( seq_disk_cache_create(context->bmain, context->scene); } - BLI_mutex_lock(&cache->disk_cache->read_write_mutex); seq_disk_cache_write_file(cache->disk_cache, key, i); - BLI_mutex_unlock(&cache->disk_cache->read_write_mutex); seq_disk_cache_enforce_limits(cache->disk_cache); } } diff --git a/source/blender/sequencer/intern/image_cache.h b/source/blender/sequencer/intern/image_cache.h index 60031311985..e7827c15305 100644 --- a/source/blender/sequencer/intern/image_cache.h +++ b/source/blender/sequencer/intern/image_cache.h @@ -27,15 +27,29 @@ extern "C" { #endif +#include "SEQ_render.h" /* Needed for #eSeqTaskId. */ + struct ImBuf; struct Main; struct Scene; struct SeqRenderData; struct Sequence; -#ifdef __cplusplus -} -#endif +typedef struct SeqCacheKey { + struct SeqCache *cache_owner; + void *userkey; + struct SeqCacheKey *link_prev; /* Used for linking intermediate items to final frame. */ + struct SeqCacheKey *link_next; /* Used for linking intermediate items to final frame. */ + struct Sequence *seq; + struct SeqRenderData context; + float frame_index; /* Usually same as timeline_frame. Mapped to media for RAW entries. */ + float timeline_frame; /* Only for reference - used for freeing when cache is full. */ + float cost; /* In short: render time(s) divided by playback frame duration(s) */ + bool is_temp_cache; /* this cache entry will be freed before rendering next frame */ + /* ID of task for assigning temp cache entries to particular task(thread, etc.) */ + eSeqTaskId task_id; + int type; +} SeqCacheKey; struct ImBuf *seq_cache_get(const struct SeqRenderData *context, struct Sequence *seq, @@ -67,6 +81,7 @@ void seq_cache_cleanup_sequence(struct Scene *scene, bool force_seq_changed_range); void seq_cache_thumbnail_cleanup(Scene *scene, rctf *view_area); bool seq_cache_is_full(void); +float seq_cache_frame_index_to_timeline_frame(struct Sequence *seq, float frame_index); #ifdef __cplusplus } From eadbacdbb06ab1ea4b0c744c31959bdf2f41bc98 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 7 Oct 2021 06:35:16 +0200 Subject: [PATCH 0594/1500] Fix T91670: Strip text position is incorrect Use `sseq->timeline_overlay.flag` instead of `sseq->flag`. Caused oversight in 7cb65e45814d. --- source/blender/editors/space_sequencer/sequencer_draw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index bbe2def8081..e5e241c8b7e 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1342,9 +1342,9 @@ static void draw_seq_strip(const bContext *C, float text_margin_y; bool y_threshold; - if ((sseq->flag & SEQ_TIMELINE_SHOW_STRIP_NAME) || - (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE) || - (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_DURATION)) { + if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_NAME) || + (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE) || + (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_DURATION)) { /* Calculate height needed for drawing text on strip. */ text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely); From 00bd631c7ccef79462f2904ecfb751d7b34ef91e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 7 Oct 2021 02:27:09 -0700 Subject: [PATCH 0595/1500] Fix mask expand not properly supporting both inverse and keep mask modes being on at the same time. --- source/blender/editors/sculpt_paint/sculpt_expand.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 40874375772..34da7daed65 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -1243,7 +1243,12 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, } if (expand_cache->preserve) { - new_mask = max_ff(new_mask, expand_cache->original_mask[vd.index]); + if (expand_cache->invert) { + new_mask = min_ff(new_mask, expand_cache->original_mask[vd.index]); + } + else { + new_mask = max_ff(new_mask, expand_cache->original_mask[vd.index]); + } } if (new_mask == initial_mask) { From 87a36cba1a5ad9e8e8279e89d104184c8bb84593 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Thu, 7 Oct 2021 11:44:04 +0200 Subject: [PATCH 0596/1500] UI: Fix alignment of buttons in Grease Pencil tool settings Small fix reviewed on blender.chat by the Grease Pencil team. --- release/scripts/startup/bl_ui/properties_paint_common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index c038f5f906a..14aacf3a47a 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -1151,7 +1151,8 @@ def brush_basic__draw_color_selector(context, layout, brush, gp_settings, props) if len(txt_ma) > maxw: txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:] - sub = row.row() + sub = row.row(align=True) + sub.enabled = not gp_settings.use_material_pin sub.ui_units_x = 8 sub.popover( panel="TOPBAR_PT_gpencil_materials", From 7fc11744e69aac2b87575c46132900eb95336088 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 7 Oct 2021 04:11:49 -0700 Subject: [PATCH 0597/1500] Revert commit, turns out this isn't a bug? --- source/blender/editors/sculpt_paint/sculpt_expand.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 34da7daed65..40874375772 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -1243,12 +1243,7 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, } if (expand_cache->preserve) { - if (expand_cache->invert) { - new_mask = min_ff(new_mask, expand_cache->original_mask[vd.index]); - } - else { - new_mask = max_ff(new_mask, expand_cache->original_mask[vd.index]); - } + new_mask = max_ff(new_mask, expand_cache->original_mask[vd.index]); } if (new_mask == initial_mask) { From 123255be6b109963bc00fe5b3ea434ecedbde231 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 7 Oct 2021 15:06:26 +0200 Subject: [PATCH 0598/1500] Fix T91994: Cycles crash when rendering on multiple devices The overscan change from D12599 lacked proper handling of window when slicing buffer for multiple devices. --- intern/cycles/integrator/path_trace.cpp | 34 ++++++++++++++------ intern/cycles/integrator/path_trace_work.cpp | 3 +- intern/cycles/render/tile.h | 5 +++ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 3ea5c3c64b8..9f3049c6484 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -234,42 +234,53 @@ template static void foreach_sliced_buffer_params(const vector> &path_trace_works, const vector &work_balance_infos, const BufferParams &buffer_params, + const int overscan, const Callback &callback) { const int num_works = path_trace_works.size(); - const int height = buffer_params.height; + const int window_height = buffer_params.window_height; int current_y = 0; for (int i = 0; i < num_works; ++i) { const double weight = work_balance_infos[i].weight; - const int slice_height = max(lround(height * weight), 1); + const int slice_window_full_y = buffer_params.full_y + buffer_params.window_y + current_y; + const int slice_window_height = max(lround(window_height * weight), 1); /* Disallow negative values to deal with situations when there are more compute devices than * scan-lines. */ - const int remaining_height = max(0, height - current_y); + const int remaining_window_height = max(0, window_height - current_y); + + BufferParams slice_params = buffer_params; + + slice_params.full_y = max(slice_window_full_y - overscan, buffer_params.full_y); + slice_params.window_y = slice_window_full_y - slice_params.full_y; - BufferParams slide_params = buffer_params; - slide_params.full_y = buffer_params.full_y + current_y; if (i < num_works - 1) { - slide_params.height = min(slice_height, remaining_height); + slice_params.window_height = min(slice_window_height, remaining_window_height); } else { - slide_params.height = remaining_height; + slice_params.window_height = remaining_window_height; } - slide_params.update_offset_stride(); + slice_params.height = slice_params.window_y + slice_params.window_height + overscan; + slice_params.height = min(slice_params.height, + buffer_params.height + buffer_params.full_y - slice_params.full_y); - callback(path_trace_works[i].get(), slide_params); + slice_params.update_offset_stride(); - current_y += slide_params.height; + callback(path_trace_works[i].get(), slice_params); + + current_y += slice_params.window_height; } } void PathTrace::update_allocated_work_buffer_params() { + const int overscan = tile_manager_.get_tile_overscan(); foreach_sliced_buffer_params(path_trace_works_, work_balance_infos_, big_tile_params_, + overscan, [](PathTraceWork *path_trace_work, const BufferParams ¶ms) { RenderBuffers *buffers = path_trace_work->get_render_buffers(); buffers->reset(params); @@ -306,9 +317,12 @@ void PathTrace::update_effective_work_buffer_params(const RenderWork &render_wor const BufferParams scaled_big_tile_params = scale_buffer_params(big_tile_params_, resolution_divider); + const int overscan = tile_manager_.get_tile_overscan(); + foreach_sliced_buffer_params(path_trace_works_, work_balance_infos_, scaled_big_tile_params, + overscan, [&](PathTraceWork *path_trace_work, const BufferParams params) { path_trace_work->set_effective_buffer_params( scaled_full_params, scaled_big_tile_params, params); diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp index f626beb0aaa..14e6f03aedc 100644 --- a/intern/cycles/integrator/path_trace_work.cpp +++ b/intern/cycles/integrator/path_trace_work.cpp @@ -134,7 +134,8 @@ void PathTraceWork::copy_from_denoised_render_buffers(const RenderBuffers *rende bool PathTraceWork::get_render_tile_pixels(const PassAccessor &pass_accessor, const PassAccessor::Destination &destination) { - const int offset_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y; + const int offset_y = (effective_buffer_params_.full_y + effective_buffer_params_.window_y) - + (effective_big_tile_params_.full_y + effective_big_tile_params_.window_y); const int width = effective_buffer_params_.width; PassAccessor::Destination slice_destination = destination; diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h index a13afaea64e..5872a9a8609 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/render/tile.h @@ -81,6 +81,11 @@ class TileManager { return tile_state_.num_tiles > 1; } + inline int get_tile_overscan() const + { + return overscan_; + } + bool next(); bool done(); From 13a28d9e6f12dcba3f97deb20efb0b0761e93db4 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 7 Oct 2021 15:20:54 +0200 Subject: [PATCH 0599/1500] Fix proxy to override code being called on undos. --- source/blender/blenkernel/intern/blendfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 6957f9b5a69..1213ecc20ee 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -347,7 +347,7 @@ static void setup_app_data(bContext *C, /* FIXME: Same as above, readfile's `do_version` do not allow to create new IDs. */ /* TODO: Once this is definitively validated for 3.0 and option to not do it is removed, add a * version bump and check here. */ - if (!USER_EXPERIMENTAL_TEST(&U, no_proxy_to_override_conversion)) { + if (mode != LOAD_UNDO && !USER_EXPERIMENTAL_TEST(&U, no_proxy_to_override_conversion)) { BKE_lib_override_library_main_proxy_convert(bmain, reports); } From c0a5b13b5ed3d1477afdbae48653acf87c6a0d08 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 7 Oct 2021 14:59:43 +0200 Subject: [PATCH 0600/1500] Asset Browser: Rework layout & behavior of catalog tree-view This reworks how tree rows are constructed in the layout and how they behave in return. * To open or collapse a row, the triangle/chevron icon has to be clicked now. The previous behavior of allowing to do it on the entire row, but only if the item was active already, was just too unusual and felt weird. * Reduce margin between chevron icon and the row label. * Indent child items without chevron some more, otherwise they feel like a row on the same level as their parent, just without chevron. * Fix renaming button taking entire row width. Respect indentation now. * Fix double-clicking to rename toggling collapsed state on each click. Some hacks/special-handling was needed so tree-rows always highlight while the mouse is hovering them, even if the mouse is actually hovering another button inside the row. --- .../blender/editors/include/UI_tree_view.hh | 17 +- source/blender/editors/interface/interface.c | 8 +- .../editors/interface/interface_handlers.c | 55 +++++- source/blender/editors/interface/tree_view.cc | 177 +++++++++++++----- .../space_file/asset_catalog_tree_view.cc | 8 +- 5 files changed, 201 insertions(+), 64 deletions(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 7693a833210..272439a2ae9 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -138,6 +138,8 @@ class TreeViewLayoutBuilder { private: /* Created through #TreeViewBuilder. */ TreeViewLayoutBuilder(uiBlock &block); + + static void polish_layout(const uiBlock &block); }; /** \} */ @@ -282,8 +284,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer { void begin_renaming(); void end_renaming(); - const AbstractTreeView &get_tree_view() const; - AbstractTreeView &get_tree_view(); + AbstractTreeView &get_tree_view() const; int count_parents() const; void deactivate(); /** @@ -310,6 +311,8 @@ class AbstractTreeViewItem : public TreeViewItemContainer { void ensure_parents_uncollapsed(); bool matches_including_parents(const AbstractTreeViewItem &other) const; + uiButTreeRow *tree_row_button(); + protected: /** * Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate() @@ -323,11 +326,16 @@ class AbstractTreeViewItem : public TreeViewItemContainer { static void rename_button_fn(bContext *, void *, char *); static AbstractTreeViewItem *find_tree_item_from_rename_button(const uiBut &but); static void tree_row_click_fn(struct bContext *, void *, void *); + static void collapse_chevron_click_fn(bContext *, void *but_arg1, void *); + static bool is_collapse_chevron_but(const uiBut *but); /** See #AbstractTreeView::change_state_delayed() */ void change_state_delayed(); + void add_treerow_button(uiBlock &block); - void add_rename_button(uiBlock &block); + void add_indent(uiLayout &row) const; + void add_collapse_chevron(uiBlock &block) const; + void add_rename_button(uiLayout &row); }; /** \} */ @@ -359,9 +367,6 @@ class BasicTreeViewItem : public AbstractTreeViewItem { */ ActivateFn activate_fn_; - uiBut *button(); - BIFIconID get_draw_icon() const; - private: static void tree_row_click_fn(struct bContext *C, void *arg1, void *arg2); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 88b23e07f54..92391a703ef 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -965,7 +965,13 @@ static bool ui_but_update_from_old_block(const bContext *C, found_active = true; } else { - const int flag_copy = UI_BUT_DRAG_MULTI; + int flag_copy = UI_BUT_DRAG_MULTI; + + /* Stupid special case: The active button may be inside (as in, overlapped on top) a tree-row + * button which we also want to keep highlighted then. */ + if (but->type == UI_BTYPE_TREEROW) { + flag_copy |= UI_ACTIVE; + } but->flag = (but->flag & ~flag_copy) | (oldbut->flag & flag_copy); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index f73420b3668..8fdc055a13b 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -4830,14 +4830,23 @@ static int ui_do_but_TREEROW(bContext *C, uiButTreeRow *tree_row_but = (uiButTreeRow *)but; BLI_assert(tree_row_but->but.type == UI_BTYPE_TREEROW); - if ((event->type == LEFTMOUSE) && (event->val == KM_DBL_CLICK)) { - button_activate_state(C, but, BUTTON_STATE_EXIT); + if (data->state == BUTTON_STATE_HIGHLIGHT) { + if (event->type == LEFTMOUSE) { + if (event->val == KM_CLICK) { + button_activate_state(C, but, BUTTON_STATE_EXIT); + return WM_UI_HANDLER_BREAK; + } + else if (event->val == KM_DBL_CLICK) { + data->cancel = true; - UI_tree_view_item_begin_rename(tree_row_but->tree_item); - return WM_UI_HANDLER_BREAK; + UI_tree_view_item_begin_rename(tree_row_but->tree_item); + ED_region_tag_redraw(CTX_wm_region(C)); + return WM_UI_HANDLER_BREAK; + } + } } - return ui_do_but_TOG(C, but, data, event); + return WM_UI_HANDLER_CONTINUE; } static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) @@ -9683,6 +9692,38 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi return retval; } +static int ui_handle_tree_hover(const wmEvent *event, const ARegion *region) +{ + bool has_treerows = false; + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + /* Avoid unnecessary work: Tree-rows are assumed to be inside tree-views. */ + if (BLI_listbase_is_empty(&block->views)) { + continue; + } + + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->type == UI_BTYPE_TREEROW) { + but->flag &= ~UI_ACTIVE; + has_treerows = true; + } + } + } + + if (!has_treerows) { + /* Avoid unnecessary lookup. */ + return WM_UI_HANDLER_CONTINUE; + } + + /* Always highlight the hovered tree-row, even if the mouse hovers another button inside of it. + */ + uiBut *hovered_row_but = ui_tree_row_find_mouse_over(region, event->x, event->y); + if (hovered_row_but) { + hovered_row_but->flag |= UI_ACTIVE; + } + + return WM_UI_HANDLER_CONTINUE; +} + static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but) { uiHandleButtonData *data = but->active; @@ -11286,6 +11327,10 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use ui_blocks_set_tooltips(region, true); } + /* Always do this, to reliably update tree-row highlighting, even if the mouse hovers a button + * inside the row (it's an overlapping layout). */ + ui_handle_tree_hover(event, region); + /* delayed apply callbacks */ ui_apply_but_funcs_after(C); diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index f3070481da2..5df9c21de4f 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -19,6 +19,7 @@ */ #include "DNA_userdef_types.h" +#include "DNA_windowmanager_types.h" #include "BKE_context.h" @@ -28,6 +29,8 @@ #include "UI_interface.h" +#include "WM_types.h" + #include "UI_tree_view.hh" namespace blender::ui { @@ -88,7 +91,7 @@ void AbstractTreeView::build_layout_from_tree(const TreeViewLayoutBuilder &build uiLayout *prev_layout = builder.current_layout(); uiLayout *box = uiLayoutBox(prev_layout); - uiLayoutColumn(box, true); + uiLayoutColumn(box, false); foreach_item([&builder](AbstractTreeViewItem &item) { builder.build_row(item); }, IterOptions::SkipCollapsed); @@ -172,24 +175,80 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, void * /*arg2*/) { uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1; - BasicTreeViewItem &tree_item = reinterpret_cast(*tree_row_but->tree_item); + AbstractTreeViewItem &tree_item = reinterpret_cast( + *tree_row_but->tree_item); - /* Let a click on an opened item activate it, a second click will close it then. - * TODO Should this be for asset catalogs only? */ - if (tree_item.is_collapsed() || tree_item.is_active()) { - tree_item.toggle_collapsed(); - } tree_item.activate(); } void AbstractTreeViewItem::add_treerow_button(uiBlock &block) { + /* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */ tree_row_but_ = (uiButTreeRow *)uiDefBut( - &block, UI_BTYPE_TREEROW, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + &block, UI_BTYPE_TREEROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); tree_row_but_->tree_item = reinterpret_cast(this); UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr); - UI_but_treerow_indentation_set(&tree_row_but_->but, count_parents()); +} + +void AbstractTreeViewItem::add_indent(uiLayout &row) const +{ + uiBlock *block = uiLayoutGetBlock(&row); + uiLayout *subrow = uiLayoutRow(&row, true); + uiLayoutSetFixedSize(subrow, true); + + const float indent_size = count_parents() * UI_DPI_ICON_SIZE; + uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, indent_size, 0, NULL, 0.0, 0.0, 0, 0, ""); + + /* Indent items without collapsing icon some more within their parent. Makes it clear that they + * are actually nested and not just a row at the same level without a chevron. */ + if (!is_collapsible() && parent_) { + uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, 0.2f * UI_UNIT_X, 0, NULL, 0.0, 0.0, 0, 0, ""); + } + + /* Restore. */ + UI_block_layout_set_current(block, &row); +} + +void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, + void * /*but_arg1*/, + void * /*arg2*/) +{ + /* There's no data we could pass to this callback. It must be either the button itself or a + * consistent address to match buttons over redraws. So instead of passing it somehow, just + * lookup the hovered item via context here. */ + + const wmWindow *win = CTX_wm_window(C); + const ARegion *region = CTX_wm_region(C); + uiTreeViewItemHandle *hovered_item_handle = UI_block_tree_view_find_item_at( + region, win->eventstate->x, win->eventstate->y); + AbstractTreeViewItem *hovered_item = reinterpret_cast( + hovered_item_handle); + BLI_assert(hovered_item != nullptr); + + hovered_item->toggle_collapsed(); +} + +bool AbstractTreeViewItem::is_collapse_chevron_but(const uiBut *but) +{ + return but->type == UI_BTYPE_BUT_TOGGLE && ELEM(but->icon, ICON_TRIA_RIGHT, ICON_TRIA_DOWN) && + (but->func == collapse_chevron_click_fn); +} + +void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const +{ + if (!is_collapsible()) { + return; + } + + const BIFIconID icon = is_collapsed() ? ICON_TRIA_RIGHT : ICON_TRIA_DOWN; + uiBut *but = uiDefIconBut( + &block, UI_BTYPE_BUT_TOGGLE, 0, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + /* Note that we're passing the tree-row button here, not the chevron one. */ + UI_but_func_set(but, collapse_chevron_click_fn, nullptr, nullptr); + + /* Check if the query for the button matches the created button. */ + BLI_assert(is_collapse_chevron_but(but)); } AbstractTreeViewItem *AbstractTreeViewItem::find_tree_item_from_rename_button( @@ -226,16 +285,23 @@ void AbstractTreeViewItem::rename_button_fn(bContext *UNUSED(C), void *arg, char item->end_renaming(); } -void AbstractTreeViewItem::add_rename_button(uiBlock &block) +void AbstractTreeViewItem::add_rename_button(uiLayout &row) { + uiBlock *block = uiLayoutGetBlock(&row); + eUIEmbossType previous_emboss = UI_block_emboss_get(block); + + uiLayoutRow(&row, false); + /* Enable emboss for the text button. */ + UI_block_emboss_set(block, UI_EMBOSS); + AbstractTreeView &tree_view = get_tree_view(); - uiBut *rename_but = uiDefBut(&block, + uiBut *rename_but = uiDefBut(block, UI_BTYPE_TEXT, 1, "", 0, 0, - UI_UNIT_X, + UI_UNIT_X * 10, UI_UNIT_Y, tree_view.rename_buffer_->data(), 1.0f, @@ -248,12 +314,15 @@ void AbstractTreeViewItem::add_rename_button(uiBlock &block) * callback is executed. */ UI_but_func_rename_set(rename_but, AbstractTreeViewItem::rename_button_fn, rename_but); - const bContext *evil_C = static_cast(block.evil_C); + const bContext *evil_C = static_cast(block->evil_C); ARegion *region = CTX_wm_region(evil_C); /* Returns false if the button was removed. */ - if (UI_but_active_only(evil_C, region, &block, rename_but) == false) { + if (UI_but_active_only(evil_C, region, block, rename_but) == false) { end_renaming(); } + + UI_block_emboss_set(block, previous_emboss); + UI_block_layout_set_current(block, &row); } void AbstractTreeViewItem::on_activate() @@ -335,12 +404,7 @@ void AbstractTreeViewItem::end_renaming() tree_view.rename_buffer_ = nullptr; } -const AbstractTreeView &AbstractTreeViewItem::get_tree_view() const -{ - return static_cast(*root_); -} - -AbstractTreeView &AbstractTreeViewItem::get_tree_view() +AbstractTreeView &AbstractTreeViewItem::get_tree_view() const { return static_cast(*root_); } @@ -454,6 +518,11 @@ bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem return true; } +uiButTreeRow *AbstractTreeViewItem::tree_row_button() +{ + return tree_row_but_; +} + void AbstractTreeViewItem::change_state_delayed() { if (is_active_fn_()) { @@ -481,25 +550,56 @@ TreeViewLayoutBuilder::TreeViewLayoutBuilder(uiBlock &block) : block_(block) { } +/** + * Moves the button following the last added chevron closer to the list item. + * + * Iterates backwards over buttons until finding the tree-row button, which is assumed to be the + * first button added for the row, and can act as a delimiter that way. + */ +void TreeViewLayoutBuilder::polish_layout(const uiBlock &block) +{ + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block.buttons) { + if (AbstractTreeViewItem::is_collapse_chevron_but(but) && but->next && + /* Embossed buttons with padding-less text padding look weird, so don't touch them. */ + ELEM(but->next->emboss, UI_EMBOSS_NONE, UI_EMBOSS_NONE_OR_STATUS)) { + UI_but_drawflag_enable(static_cast(but->next), UI_BUT_NO_TEXT_PADDING); + } + + if (but->type == UI_BTYPE_TREEROW) { + break; + } + } +} + void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const { - uiLayout *prev_layout = current_layout(); - uiLayout *row = uiLayoutRow(prev_layout, false); - - uiLayoutOverlap(row); - uiBlock &block_ = block(); + uiLayout *prev_layout = current_layout(); + eUIEmbossType previous_emboss = UI_block_emboss_get(&block_); + + uiLayout *overlap = uiLayoutOverlap(prev_layout); + + uiLayoutRow(overlap, false); /* Every item gets one! Other buttons can be overlapped on top. */ item.add_treerow_button(block_); + /* After adding tree-row button (would disable hover highlighting). */ + UI_block_emboss_set(&block_, UI_EMBOSS_NONE); + + uiLayout *row = uiLayoutRow(overlap, true); + item.add_indent(*row); + item.add_collapse_chevron(block_); + if (item.is_renaming()) { - item.add_rename_button(block_); + item.add_rename_button(*row); } else { item.build_row(*row); } + polish_layout(block_); + UI_block_emboss_set(&block_, previous_emboss); UI_block_layout_set_current(&block_, prev_layout); } @@ -520,12 +620,9 @@ BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(ic label_ = label; } -void BasicTreeViewItem::build_row(uiLayout & /*row*/) +void BasicTreeViewItem::build_row(uiLayout &row) { - if (BIFIconID icon = get_draw_icon()) { - ui_def_but_icon(&tree_row_but_->but, icon, UI_HAS_ICON); - } - tree_row_but_->but.str = BLI_strdupn(label_.c_str(), label_.length()); + uiItemL(&row, label_.c_str(), icon); } void BasicTreeViewItem::on_activate() @@ -540,24 +637,6 @@ void BasicTreeViewItem::on_activate(ActivateFn fn) activate_fn_ = fn; } -BIFIconID BasicTreeViewItem::get_draw_icon() const -{ - if (icon) { - return icon; - } - - if (is_collapsible()) { - return is_collapsed() ? ICON_TRIA_RIGHT : ICON_TRIA_DOWN; - } - - return ICON_NONE; -} - -uiBut *BasicTreeViewItem::button() -{ - return &tree_row_but_->but; -} - } // namespace blender::ui using namespace blender::ui; diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index fac38e71220..84bfa58be85 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -223,11 +223,12 @@ void AssetCatalogTreeViewItem::build_row(uiLayout &row) return; } + uiButTreeRow *tree_row_but = tree_row_button(); PointerRNA *props; const CatalogID catalog_id = catalog_item_.get_catalog_id(); props = UI_but_extra_operator_icon_add( - button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + (uiBut *)tree_row_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str()); /* Tree items without a catalog ID represent components of catalog paths that are not @@ -238,7 +239,7 @@ void AssetCatalogTreeViewItem::build_row(uiLayout &row) BLI_uuid_format(catalog_id_str_buffer, catalog_id); props = UI_but_extra_operator_icon_add( - button(), "ASSET_OT_catalog_delete", WM_OP_INVOKE_DEFAULT, ICON_X); + (uiBut *)tree_row_but, "ASSET_OT_catalog_delete", WM_OP_INVOKE_DEFAULT, ICON_X); RNA_string_set(props, "catalog_id", catalog_id_str_buffer); } } @@ -301,6 +302,7 @@ bool AssetCatalogTreeViewItem::drop_into_catalog(const AssetCatalogTreeView &tre /* Trigger re-run of filtering to update visible assets. */ filelist_tag_needs_filtering(tree_view.space_file_.files); file_select_deselect_all(&tree_view.space_file_, FILE_SEL_SELECTED | FILE_SEL_HIGHLIGHTED); + WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_LIST, nullptr); } return true; @@ -341,7 +343,7 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) PointerRNA *props; props = UI_but_extra_operator_icon_add( - button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + (uiBut *)tree_row_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); /* No parent path to use the root level. */ RNA_string_set(props, "parent_path", nullptr); } From 719c319055ad30467ada26f72ca7b0f56c0bd40e Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 7 Oct 2021 15:51:31 +0200 Subject: [PATCH 0601/1500] Fix Cycles long start on scene without volumes The state template iteration had difficult time dealing with 0-sized arrays, causing iteration for until integer overflows. --- intern/cycles/integrator/path_trace_work_gpu.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 8af8f9a02e2..706fc5799d0 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -31,7 +31,7 @@ CCL_NAMESPACE_BEGIN -static size_t estimate_single_state_size(DeviceScene *device_scene) +static size_t estimate_single_state_size() { size_t state_size = 0; @@ -42,11 +42,16 @@ static size_t estimate_single_state_size(DeviceScene *device_scene) break; \ } #define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \ - if (array_index == gpu_array_size - 1) { \ + if (array_index >= gpu_array_size - 1) { \ break; \ } \ } -#define KERNEL_STRUCT_VOLUME_STACK_SIZE (device_scene->data.volume_stack_size) +/* TODO(sergey): Look into better estimation for fields which depend on scene features. Maybe + * maximum state calculation should happen as `alloc_work_memory()`, so that we can react to an + * updated scene state here. + * For until then use common value. Currently this size is only used for logging, but is weak to + * rely on this. */ +#define KERNEL_STRUCT_VOLUME_STACK_SIZE 4 #include "kernel/integrator/integrator_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER @@ -75,7 +80,7 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device, num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE), work_tiles_(device, "work_tiles", MEM_READ_WRITE), display_rgba_half_(device, "display buffer half", MEM_READ_WRITE), - max_num_paths_(queue_->num_concurrent_states(estimate_single_state_size(device_scene))), + max_num_paths_(queue_->num_concurrent_states(estimate_single_state_size())), min_num_active_paths_(queue_->num_concurrent_busy_states()), max_active_path_index_(0) { @@ -124,7 +129,7 @@ void PathTraceWorkGPU::alloc_integrator_soa() break; \ } #define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \ - if (array_index == gpu_array_size - 1) { \ + if (array_index >= gpu_array_size - 1) { \ break; \ } \ } From 0f58cc15943e0cd852628e3cac915fda930510bc Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Thu, 7 Oct 2021 16:11:22 +0200 Subject: [PATCH 0602/1500] Cleanup: make format --- source/blender/editors/mesh/editmesh_intersect.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 5dfa804ad85..ee227c58fe7 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -480,7 +480,8 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot) false, "Swap", "Use with difference intersection to swap which side is kept"); - RNA_def_boolean(ot->srna, "use_self", false, "Self Intersection", "Do self-union or self-intersection"); + RNA_def_boolean( + ot->srna, "use_self", false, "Self Intersection", "Do self-union or self-intersection"); RNA_def_float_distance( ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge Threshold", "", 0.0, 0.001); RNA_def_enum(ot->srna, From 0d4c53ecfebb49a8c4e00f984756e0d243968abe Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 7 Oct 2021 16:19:50 +0200 Subject: [PATCH 0603/1500] Fix wrong tile size calculated in Cycles Was causing extra overscan pixels, and was confusing multiple workers check after fix T91994. --- intern/cycles/render/tile.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 75c1f78982d..3070e93eff3 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -411,25 +411,22 @@ Tile TileManager::get_tile_for_index(int index) const const int tile_index_y = index / tile_state_.num_tiles_x; const int tile_index_x = index - tile_index_y * tile_state_.num_tiles_x; - const int tile_x = tile_index_x * tile_size_.x; - const int tile_y = tile_index_y * tile_size_.y; + const int tile_window_x = tile_index_x * tile_size_.x; + const int tile_window_y = tile_index_y * tile_size_.y; Tile tile; - tile.x = tile_x - overscan_; - tile.y = tile_y - overscan_; - tile.width = tile_size_.x + 2 * overscan_; - tile.height = tile_size_.y + 2 * overscan_; + tile.x = max(0, tile_window_x - overscan_); + tile.y = max(0, tile_window_y - overscan_); - tile.x = max(tile.x, 0); - tile.y = max(tile.y, 0); - tile.width = min(tile.width, buffer_params_.width - tile.x); - tile.height = min(tile.height, buffer_params_.height - tile.y); + tile.window_x = tile_window_x - tile.x; + tile.window_y = tile_window_y - tile.y; + tile.window_width = min(tile_size_.x, buffer_params_.width - tile_window_x); + tile.window_height = min(tile_size_.y, buffer_params_.height - tile_window_y); - tile.window_x = tile_x - tile.x; - tile.window_y = tile_y - tile.y; - tile.window_width = min(tile_size_.x, buffer_params_.width - (tile.x + tile.window_x)); - tile.window_height = min(tile_size_.y, buffer_params_.height - (tile.y + tile.window_y)); + tile.width = min(buffer_params_.width - tile.x, tile.window_x + tile.window_width + overscan_); + tile.height = min(buffer_params_.height - tile.y, + tile.window_y + tile.window_height + overscan_); return tile; } From 9f9e2dd25d70d81a8ea9cd9164c1b221398cbaee Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 01:24:12 +1100 Subject: [PATCH 0604/1500] Cleanup: clang-tidy --- source/blender/editors/interface/interface_handlers.c | 2 +- source/blender/editors/interface/tree_view.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 8fdc055a13b..c508cf2f36c 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -4836,7 +4836,7 @@ static int ui_do_but_TREEROW(bContext *C, button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } - else if (event->val == KM_DBL_CLICK) { + if (event->val == KM_DBL_CLICK) { data->cancel = true; UI_tree_view_item_begin_rename(tree_row_but->tree_item); diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 5df9c21de4f..8ac69f862c8 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -198,12 +198,12 @@ void AbstractTreeViewItem::add_indent(uiLayout &row) const uiLayoutSetFixedSize(subrow, true); const float indent_size = count_parents() * UI_DPI_ICON_SIZE; - uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, indent_size, 0, NULL, 0.0, 0.0, 0, 0, ""); + uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, indent_size, 0, nullptr, 0.0, 0.0, 0, 0, ""); /* Indent items without collapsing icon some more within their parent. Makes it clear that they * are actually nested and not just a row at the same level without a chevron. */ if (!is_collapsible() && parent_) { - uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, 0.2f * UI_UNIT_X, 0, NULL, 0.0, 0.0, 0, 0, ""); + uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, 0.2f * UI_UNIT_X, 0, nullptr, 0.0, 0.0, 0, 0, ""); } /* Restore. */ From c7b237e7d1a4c2adf03d5b7dc97b87691e6e224b Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 7 Oct 2021 16:15:39 +0200 Subject: [PATCH 0605/1500] Asset Browser: Move Asset Library selector to navigation bar The menu to select the active Asset Library is now in the left bar (so called "Source List", although I'd prefer "Navigation-Bar"). This has some benefits: * All Asset Library navigation is in the left sidebar now, giving nice grouping and a top-to-bottom & left-to-right flow of the layout. The header is focused on view set-up now. * Catalogs are stored inside the asset library. Makes sense to have them right under that. * Less content in the header allows for less wide Asset Browsers without extensive scrolling. * This location gives more space to add options or operators for Asset Libraries. Main downside I see is that the side-bar needs to be opened to change libraries, which takes quite some space. In practice there shouldn't be need to do this often though. --- .../scripts/startup/bl_ui/space_filebrowser.py | 6 ------ .../space_file/asset_catalog_tree_view.cc | 2 ++ source/blender/editors/space_file/file_panels.c | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 5dd8c69f3d5..f54a2446c7e 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -36,12 +36,6 @@ class FILEBROWSER_HT_header(Header): space_data = context.space_data params = space_data.params - row = layout.row(align=True) - row.prop(params, "asset_library_ref", text="") - # External libraries don't auto-refresh, add refresh button. - if params.asset_library_ref != 'LOCAL': - row.operator("file.refresh", text="", icon='FILE_REFRESH') - layout.separator_spacer() layout.prop(params, "import_type", text="") diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 84bfa58be85..28d64cfca60 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -479,6 +479,8 @@ void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library { uiBlock *block = uiLayoutGetBlock(layout); + UI_block_layout_set_current(block, layout); + ui::AbstractTreeView *tree_view = UI_block_add_view( *block, "asset catalog tree view", diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index b530f1d0aa7..51d0581d6a4 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -232,13 +232,27 @@ void file_execute_region_panels_register(ARegionType *art) static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *panel) { + bScreen *screen = CTX_wm_screen(C); SpaceFile *sfile = CTX_wm_space_file(C); /* May be null if the library wasn't loaded yet. */ struct AssetLibrary *asset_library = filelist_asset_library(sfile->files); FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile); BLI_assert(params != NULL); - file_create_asset_catalog_tree_view_in_layout(asset_library, panel->layout, sfile, params); + uiLayout *col = uiLayoutColumn(panel->layout, false); + uiLayout *row = uiLayoutRow(col, true); + + PointerRNA params_ptr; + RNA_pointer_create(&screen->id, &RNA_FileAssetSelectParams, params, ¶ms_ptr); + + uiItemR(row, ¶ms_ptr, "asset_library_ref", 0, "", ICON_NONE); + if (params->asset_library_ref.type != ASSET_LIBRARY_LOCAL) { + uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_refresh"); + } + + uiItemS(col); + + file_create_asset_catalog_tree_view_in_layout(asset_library, col, sfile, params); } void file_tools_region_panels_register(ARegionType *art) From 1b79b4dd30b6900717458707b0ddd656b384413f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 01:54:48 +1100 Subject: [PATCH 0606/1500] Fix sequencer preview poll function sequencer_view_preview_poll returned true even when in the "Sequence" view. Now check the preview is visible, also check the region is expected type so preview actions aren't possible for mixed sequence/preview display. --- .../editors/space_sequencer/sequencer_edit.c | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index b0273fe1e25..655cfb9375c 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -214,22 +214,38 @@ bool sequencer_strip_has_path_poll(bContext *C) bool sequencer_view_preview_poll(bContext *C) { SpaceSeq *sseq = CTX_wm_space_seq(C); - Editing *ed = SEQ_editing_get(CTX_data_scene(C)); - if (ed && sseq && (sseq->mainb == SEQ_DRAW_IMG_IMBUF)) { - return 1; + if (sseq == NULL) { + return false; + } + if (SEQ_editing_get(CTX_data_scene(C)) == NULL) { + return false; + } + if (!(ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW) && + (sseq->mainb == SEQ_DRAW_IMG_IMBUF))) { + return false; + } + ARegion *region = CTX_wm_region(C); + if (!(region && region->regiontype == RGN_TYPE_PREVIEW)) { + return false; } - return 0; + return true; } bool sequencer_view_strips_poll(bContext *C) { SpaceSeq *sseq = CTX_wm_space_seq(C); - if (sseq && ED_space_sequencer_check_show_strip(sseq)) { - return 1; + if (sseq == NULL) { + return false; } - - return 0; + if (!ED_space_sequencer_check_show_strip(sseq)) { + return false; + } + ARegion *region = CTX_wm_region(C); + if (!(region && region->regiontype == RGN_TYPE_WINDOW)) { + return false; + } + return true; } /** \} */ From 1de922f88c91402b3f8083431e11401892485b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 7 Oct 2021 16:23:08 +0200 Subject: [PATCH 0607/1500] Cleanup: asset catalog tests, move teardown function Move `AssetCatalogTest::TearDown` close to the corresponding `SetUp` function, so that it's easier to find. No functional changes. --- .../blenkernel/intern/asset_catalog_test.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index fb471a8ee7b..90488611946 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -91,6 +91,14 @@ class AssetCatalogTest : public testing::Test { temp_library_path_ = ""; } + void TearDown() override + { + if (!temp_library_path_.empty()) { + BLI_delete(temp_library_path_.c_str(), true, true); + temp_library_path_ = ""; + } + } + /* Register a temporary path, which will be removed at the end of the test. * The returned path ends in a slash. */ CatalogFilePath use_temp_path() @@ -176,14 +184,6 @@ class AssetCatalogTest : public testing::Test { i++; }); } - - void TearDown() override - { - if (!temp_library_path_.empty()) { - BLI_delete(temp_library_path_.c_str(), true, true); - temp_library_path_ = ""; - } - } }; TEST_F(AssetCatalogTest, load_single_file) From cc6a3509a05b227f554fdb9a3cfef90a1de273b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 7 Oct 2021 16:44:29 +0200 Subject: [PATCH 0608/1500] Asset Catalogs: change rules for saving catalog definition files Change the rules for determining where to save a new catalog definition file (CDF). Old situation (T91681): if a `blender_assets.cats.txt` file already exists in the same directory as the blend file, write to that. If not, see if the blend file is contained in an asset library, and write to its top-level CDF. The new situation swaps the rules: first see if the blend file is contained in an asset library, and if so write to its top-level CDF. If not, write a CDF next to the blend file. As before, any pre-existing CDF is not just bluntly overwritten, but merged with the in-memory catalogs. --- .../blenkernel/intern/asset_catalog.cc | 34 +++-- .../blenkernel/intern/asset_catalog_test.cc | 126 ++++++++++++------ 2 files changed, 98 insertions(+), 62 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 577d916288a..d50942bd9fa 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -327,30 +327,26 @@ CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing( "A non-empty .blend file path is required to be able to determine where the " "catalog definition file should be put"); + /* Ask the asset library API for an appropriate location. */ + char suitable_root_path[PATH_MAX]; + const bool asset_lib_root_found = BKE_asset_library_find_suitable_root_path_from_path( + blend_file_path.c_str(), suitable_root_path); + if (asset_lib_root_found) { + char asset_lib_cdf_path[PATH_MAX]; + BLI_path_join(asset_lib_cdf_path, + sizeof(asset_lib_cdf_path), + suitable_root_path, + DEFAULT_CATALOG_FILENAME.c_str(), + NULL); + return asset_lib_cdf_path; + } + /* Determine the default CDF path in the same directory of the blend file. */ char blend_dir_path[PATH_MAX]; BLI_split_dir_part(blend_file_path.c_str(), blend_dir_path, sizeof(blend_dir_path)); const CatalogFilePath cdf_path_next_to_blend = asset_definition_default_file_path_from_dir( blend_dir_path); - - if (BLI_exists(cdf_path_next_to_blend.c_str())) { - /* - The directory containing the blend file has a blender_assets.cats.txt file? - * -> Merge with & write to that file. */ - return cdf_path_next_to_blend; - } - - /* - There's no definition file next to the .blend file. - * -> Ask the asset library API for an appropriate location. */ - char suitable_root_path[PATH_MAX]; - BKE_asset_library_find_suitable_root_path_from_path(blend_file_path.c_str(), suitable_root_path); - char asset_lib_cdf_path[PATH_MAX]; - BLI_path_join(asset_lib_cdf_path, - sizeof(asset_lib_cdf_path), - suitable_root_path, - DEFAULT_CATALOG_FILENAME.c_str(), - NULL); - - return asset_lib_cdf_path; + return cdf_path_next_to_blend; } std::unique_ptr AssetCatalogService::construct_cdf_in_memory( diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 90488611946..fe7222920ab 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -184,6 +184,76 @@ class AssetCatalogTest : public testing::Test { i++; }); } + + /* Used by on_blendfile_save__from_memory_into_existing_asset_lib* test functions. */ + void save_from_memory_into_existing_asset_lib(const bool should_top_level_cdf_exist) + { + const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath registered_asset_lib = target_dir + "my_asset_library/"; + const CatalogFilePath asset_lib_subdir = registered_asset_lib + "subdir/"; + CatalogFilePath cdf_toplevel = registered_asset_lib + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + CatalogFilePath cdf_in_subdir = asset_lib_subdir + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + BLI_path_slash_native(cdf_toplevel.data()); + BLI_path_slash_native(cdf_in_subdir.data()); + + /* Set up a temporary asset library for testing. */ + bUserAssetLibrary *asset_lib_pref = BKE_preferences_asset_library_add( + &U, "Test", registered_asset_lib.c_str()); + ASSERT_NE(nullptr, asset_lib_pref); + ASSERT_TRUE(BLI_dir_create_recursive(asset_lib_subdir.c_str())); + + if (should_top_level_cdf_exist) { + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), cdf_toplevel.c_str())); + } + + /* Create an empty CDF to add complexity. It should not save to this, but to the top-level + * one.*/ + ASSERT_TRUE(BLI_file_touch(cdf_in_subdir.c_str())); + ASSERT_EQ(0, BLI_file_size(cdf_in_subdir.c_str())); + + /* Create the catalog service without loading the already-existing CDF. */ + TestableAssetCatalogService service; + const CatalogFilePath blendfilename = asset_lib_subdir + "some_file.blend"; + const AssetCatalog *cat = service.create_catalog("some/catalog/path"); + + /* Mock that the blend file is written to the directory already containing a CDF. */ + ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename)); + + /* Test that the CDF still exists in the expected location. */ + EXPECT_TRUE(BLI_exists(cdf_toplevel.c_str())); + const CatalogFilePath backup_filename = cdf_toplevel + "~"; + const bool backup_exists = BLI_exists(backup_filename.c_str()); + EXPECT_EQ(should_top_level_cdf_exist, backup_exists) + << "Overwritten CDF should have been backed up."; + + /* Test that the in-memory CDF has the expected file path. */ + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + BLI_path_slash_native(cdf->file_path.data()); + EXPECT_EQ(cdf_toplevel, cdf->file_path); + + /* Test that the in-memory catalogs have been merged with the on-disk one. */ + AssetCatalogService loaded_service(cdf_toplevel); + loaded_service.load_from_disk(); + EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id)); + + /* This catalog comes from a pre-existing CDF that should have been merged. + * However, if the file doesn't exist, so does the catalog. */ + AssetCatalog *poses_ellie_catalog = loaded_service.find_catalog(UUID_POSES_ELLIE); + if (should_top_level_cdf_exist) { + EXPECT_NE(nullptr, poses_ellie_catalog); + } + else { + EXPECT_EQ(nullptr, poses_ellie_catalog); + } + + /* Test that the "red herring" CDF has not been touched. */ + EXPECT_EQ(0, BLI_file_size(cdf_in_subdir.c_str())); + + BKE_preferences_asset_library_remove(&U, asset_lib_pref); + } }; TEST_F(AssetCatalogTest, load_single_file) @@ -525,51 +595,21 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_me EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); } -/* Create some catalogs in memory, save to subdirectory of a registered asset library. */ +/* Create some catalogs in memory, save to subdirectory of a registered asset library, where the + * subdirectory also contains a CDF. This should still write to the top-level dir of the asset + * library. */ +TEST_F(AssetCatalogTest, + on_blendfile_save__from_memory_into_existing_asset_lib_without_top_level_cdf) +{ + save_from_memory_into_existing_asset_lib(true); +} + +/* Create some catalogs in memory, save to subdirectory of a registered asset library, where the + * subdirectory contains a CDF, but the top-level directory does not. This should still write to + * the top-level dir of the asset library. */ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_asset_lib) { - const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ - const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; - const CatalogFilePath registered_asset_lib = target_dir + "my_asset_library/"; - CatalogFilePath writable_cdf_file = registered_asset_lib + - AssetCatalogService::DEFAULT_CATALOG_FILENAME; - BLI_path_slash_native(writable_cdf_file.data()); - - /* Set up a temporary asset library for testing. */ - bUserAssetLibrary *asset_lib_pref = BKE_preferences_asset_library_add( - &U, "Test", registered_asset_lib.c_str()); - ASSERT_NE(nullptr, asset_lib_pref); - ASSERT_TRUE(BLI_dir_create_recursive(registered_asset_lib.c_str())); - ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); - - /* Create the catalog service without loading the already-existing CDF. */ - TestableAssetCatalogService service; - const CatalogFilePath blenddirname = registered_asset_lib + "subdirectory/"; - const CatalogFilePath blendfilename = blenddirname + "some_file.blend"; - ASSERT_TRUE(BLI_dir_create_recursive(blenddirname.c_str())); - const AssetCatalog *cat = service.create_catalog("some/catalog/path"); - - /* Mock that the blend file is written to the directory already containing a CDF. */ - ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename)); - - /* Test that the CDF still exists in the expected location. */ - EXPECT_TRUE(BLI_exists(writable_cdf_file.c_str())); - const CatalogFilePath backup_filename = writable_cdf_file + "~"; - EXPECT_TRUE(BLI_exists(backup_filename.c_str())) - << "Overwritten CDF should have been backed up."; - - /* Test that the in-memory CDF has the expected file path. */ - AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); - BLI_path_slash_native(cdf->file_path.data()); - EXPECT_EQ(writable_cdf_file, cdf->file_path); - - /* Test that the in-memory catalogs have been merged with the on-disk one. */ - AssetCatalogService loaded_service(writable_cdf_file); - loaded_service.load_from_disk(); - EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id)); - EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); - - BKE_preferences_asset_library_remove(&U, asset_lib_pref); + save_from_memory_into_existing_asset_lib(false); } TEST_F(AssetCatalogTest, create_first_catalog_from_scratch) From 919e513fa8f9fb4f1304ea4b752869b6d63b1608 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Thu, 7 Oct 2021 16:14:02 +0200 Subject: [PATCH 0609/1500] User Interface: Remove the green background when inside a node group The current background color and parent nodetrees is too distracting and noisy. It drastically affect the readability of the nested node-trees. Other techniques (better bread crumbs) can be used instead to indicate to users that they are inside a node group. --- The background drawing was introduced in 4638e5f99a9ba as part of the Python Nodes branch merge. This made its debut in Blender 2.67 (30/May/2021). At the time the color used for the background was a light gray. Over the years the color changed to the current dark green, aggravating the problem further. Before that, the (expanded) nodegroup already had the partially transparent background, mingling with the other nodes. The Python Nodes branch brought this concept with its changes, and would always draw up to two levels up in the background (the parent nodetree, and its parent nodetree). To read the original inspiration for all the changes introduced then: https://code.blender.org/2012/01/improving-node-group-interface-editing/ Differential Revision: https://developer.blender.org/D12780 --- .../blender/editors/space_node/node_draw.cc | 50 +------------------ 1 file changed, 2 insertions(+), 48 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 9b243290566..b5e2434f3d8 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2176,28 +2176,6 @@ static void draw_nodetree(const bContext *C, node_draw_nodetree(C, region, snode, ntree, parent_key); } -/* Shade the parent node group and add a `uiBlock` to clip mouse events. */ -static void draw_group_overlay(const bContext *C, ARegion *region) -{ - const View2D *v2d = ®ion->v2d; - const rctf rect = v2d->cur; - float color[4]; - - /* Shade node groups to separate them visually. */ - GPU_blend(GPU_BLEND_ALPHA); - - UI_GetThemeColorShadeAlpha4fv(TH_NODE_GROUP, 0, 0, color); - UI_draw_roundbox_corner_set(UI_CNR_NONE); - UI_draw_roundbox_4fv(&rect, true, 0, color); - GPU_blend(GPU_BLEND_NONE); - - /* Set the block bounds to clip mouse events from underlying nodes. */ - uiBlock *block = UI_block_begin(C, region, "node tree bounds block", UI_EMBOSS); - UI_block_bounds_set_explicit(block, rect.xmin, rect.ymin, rect.xmax, rect.ymax); - UI_block_flag_enable(block, UI_BLOCK_CLIP_EVENTS); - UI_block_end(C, block); -} - void node_draw_space(const bContext *C, ARegion *region) { wmWindow *win = CTX_wm_window(C); @@ -2237,8 +2215,6 @@ void node_draw_space(const bContext *C, ARegion *region) /* Draw parent node trees. */ if (snode->treepath.last) { - static const int max_depth = 2; - bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last; /* Update tree path name (drawn in the bottom left). */ @@ -2259,35 +2235,13 @@ void node_draw_space(const bContext *C, ARegion *region) copy_v2_v2(snode->edittree->view_center, center); } - int depth = 0; - while (path->prev && depth < max_depth) { - path = path->prev; - depth++; - } - - /* Parent node trees in the background. */ - for (int curdepth = depth; curdepth > 0; path = path->next, curdepth--) { - bNodeTree *ntree = path->nodetree; - if (ntree) { - snode_setup_v2d(snode, region, path->view_center); - - draw_nodetree(C, region, ntree, path->parent_key); - - draw_group_overlay(C, region); - } - } - /* Top-level edit tree. */ bNodeTree *ntree = path->nodetree; if (ntree) { snode_setup_v2d(snode, region, center); - /* Grid, uses theme color based on node path depth. */ - UI_view2d_multi_grid_draw(v2d, - (depth > 0 ? TH_NODE_GROUP : TH_GRID), - ED_node_grid_size(), - NODE_GRID_STEPS, - grid_levels); + /* Grid. */ + UI_view2d_multi_grid_draw(v2d, TH_GRID, ED_node_grid_size(), NODE_GRID_STEPS, grid_levels); /* Backdrop. */ draw_nodespace_back_pix(C, region, snode, path->parent_key); From d04d27b406b856396102452cab0eedf315e94a54 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 7 Oct 2021 12:32:04 +1100 Subject: [PATCH 0610/1500] Sequencer: 2D cursor for the preview & transform - Use 2D cursor in the preview space using shortcuts matching the UV editor and 3D view. - Add Cursor tool, cursor transform. - Support for cursor and bound-box pivot. - Add pivot pie menu. --- .../keyconfig/keymap_data/blender_default.py | 33 ++++++- .../scripts/startup/bl_ui/space_sequencer.py | 34 +++++++ .../startup/bl_ui/space_toolsystem_toolbar.py | 17 ++++ source/blender/draw/DRW_engine.h | 3 + source/blender/draw/intern/draw_view.c | 70 ++++++++------ source/blender/draw/intern/draw_view.h | 2 + source/blender/editors/animation/anim_ops.c | 10 +- source/blender/editors/include/ED_screen.h | 2 +- source/blender/editors/include/ED_util.h | 1 + source/blender/editors/screen/area.c | 2 +- .../editors/space_sequencer/CMakeLists.txt | 1 + .../editors/space_sequencer/sequencer_edit.c | 61 ++++++++++++ .../space_sequencer/sequencer_intern.h | 1 + .../editors/space_sequencer/sequencer_ops.c | 1 + .../editors/space_sequencer/space_sequencer.c | 17 ++++ source/blender/editors/transform/transform.c | 7 ++ source/blender/editors/transform/transform.h | 1 + .../editors/transform/transform_convert.c | 15 ++- .../editors/transform/transform_convert.h | 4 +- .../transform/transform_convert_cursor.c | 93 ++++++++++++------- .../editors/transform/transform_generics.c | 10 +- .../editors/transform/transform_gizmo_2d.c | 24 ++--- source/blender/makesdna/DNA_space_types.h | 3 + source/blender/makesrna/intern/rna_scene.c | 2 + source/blender/makesrna/intern/rna_space.c | 7 ++ source/blender/sequencer/SEQ_transform.h | 7 ++ .../sequencer/intern/strip_transform.c | 12 +++ 27 files changed, 363 insertions(+), 77 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index b8b27b0e8ba..7152069972d 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2826,6 +2826,8 @@ def km_sequencerpreview(params): value=params.select_mouse_value_fallback, legacy=params.legacy, ), + op_menu_pie("SEQUENCER_MT_pivot_pie", {"type": 'PERIOD', "value": 'PRESS'}), + ("sequencer.view_all_preview", {"type": 'HOME', "value": 'PRESS'}, None), ("sequencer.view_all_preview", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None), ("sequencer.view_ghost_border", {"type": 'O', "value": 'PRESS'}, None), @@ -2862,6 +2864,18 @@ def km_sequencerpreview(params): *_template_items_context_menu("SEQUENCER_MT_preview_context_menu", params.context_menu_event), ]) + # 2D cursor. + if params.cursor_tweak_event: + items.extend([ + ("sequencer.cursor_set", params.cursor_set_event, None), + ("transform.translate", params.cursor_tweak_event, + {"properties": [("release_confirm", True), ("cursor_transform", True)]}), + ]) + else: + items.extend([ + ("sequencer.cursor_set", params.cursor_set_event, None), + ]) + return keymap @@ -7465,13 +7479,13 @@ def km_sequencer_editor_tool_select(params, *, fallback): _fallback_id("Sequencer Tool: Tweak", fallback), {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - # TODO: Use 2D cursor for preview region (currently `sequencer.sample`). *([] if fallback else - _template_items_tool_select(params, "sequencer.select", "sequencer.sample", extend="toggle") + _template_items_tool_select(params, "sequencer.select", "sequencer.cursor_set", extend="toggle") ), *([] if (not params.use_fallback_tool_rmb) else _template_sequencer_generic_select( type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), + # Ignored for preview. *_template_items_change_frame(params), ]}, ) @@ -7490,6 +7504,7 @@ def km_sequencer_editor_tool_select_box(params, *, fallback): *_template_sequencer_select_for_fallback(params, fallback), # RMB select can already set the frame, match the tweak tool. + # Ignored for preview. *(_template_items_change_frame(params) if params.select_mouse == 'LEFTMOUSE' else []), ]}, @@ -7506,6 +7521,19 @@ def km_sequencer_editor_tool_generic_sample(params): ) +def km_sequencer_editor_tool_cursor(params): + return ( + "Sequencer Tool: Cursor", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("sequencer.cursor_set", {"type": params.tool_mouse, "value": 'PRESS'}, None), + # Don't use `tool_maybe_tweak_event` since it conflicts with `PRESS` that places the cursor. + ("transform.translate", params.tool_tweak_event, + {"properties": [("release_confirm", True), ("cursor_transform", True)]}), + ]}, + ) + + def km_sequencer_editor_tool_blade(_params): return ( "Sequencer Tool: Blade", @@ -7808,6 +7836,7 @@ def generate_keymaps(params=None): *(km_sequencer_editor_tool_select_box(params, fallback=fallback) for fallback in (False, True)), km_sequencer_editor_tool_blade(params), km_sequencer_editor_tool_generic_sample(params), + km_sequencer_editor_tool_cursor(params), km_sequencer_editor_tool_scale(params), km_sequencer_editor_tool_rotate(params), km_sequencer_editor_tool_move(params), diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 03a090255f6..58afb6d9203 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1042,6 +1042,22 @@ class SEQUENCER_MT_preview_context_menu(Menu): pass +class SEQUENCER_MT_pivot_pie(Menu): + bl_label = "Pivot Point" + + def draw(self, context): + layout = self.layout + pie = layout.menu_pie() + + tool_settings = context.tool_settings + sequencer_tool_settings = context.tool_settings.sequencer_tool_settings + + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='CENTER') + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='CURSOR') + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='INDIVIDUAL_ORIGINS') + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='MEDIAN') + + class SequencerButtonsPanel: bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'UI' @@ -2185,6 +2201,22 @@ class SEQUENCER_PT_view(SequencerButtonsPanel_Output, Panel): col.prop(st, "show_separate_color") +class SEQUENCER_PT_view_cursor(SequencerButtonsPanel_Output, Panel): + bl_category = "View" + bl_label = "2D Cursor" + + def draw(self, context): + layout = self.layout + + st = context.space_data + + layout.use_property_split = True + layout.use_property_decorate = False + + col = layout.column() + col.prop(st, "cursor_location", text="Location") + + class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel): bl_label = "Frame Overlay" bl_category = "View" @@ -2464,6 +2496,7 @@ classes = ( SEQUENCER_MT_color_tag_picker, SEQUENCER_MT_context_menu, SEQUENCER_MT_preview_context_menu, + SEQUENCER_MT_pivot_pie, SEQUENCER_PT_color_tag_picker, @@ -2500,6 +2533,7 @@ classes = ( SEQUENCER_PT_custom_props, SEQUENCER_PT_view, + SEQUENCER_PT_view_cursor, SEQUENCER_PT_frame_overlay, SEQUENCER_PT_view_safe_areas, SEQUENCER_PT_view_safe_areas_center_cut, diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 6478cff3597..008129cd389 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2440,6 +2440,19 @@ class _defs_node_edit: class _defs_sequencer_generic: + @ToolDef.from_fn + def cursor(): + return dict( + idname="builtin.cursor", + label="Cursor", + description=( + "Set the cursor location, drag to transform" + ), + icon="ops.generic.cursor", + keymap="Sequencer Tool: Cursor", + options={'KEYMAP_FALLBACK'}, + ) + @ToolDef.from_fn def blade(): def draw_settings(_context, layout, tool): @@ -3094,6 +3107,8 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): ], 'PREVIEW': [ *_tools_select, + _defs_sequencer_generic.cursor, + None, _defs_sequencer_generic.translate, _defs_sequencer_generic.rotate, _defs_sequencer_generic.scale, @@ -3106,6 +3121,8 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): ], 'SEQUENCER_PREVIEW': [ *_tools_select, + _defs_sequencer_generic.cursor, + None, _defs_sequencer_generic.translate, _defs_sequencer_generic.rotate, _defs_sequencer_generic.scale, diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 927a29ed6b6..af6c8ea62b2 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -179,6 +179,9 @@ void DRW_viewport_data_free(struct DRWData *drw_data); bool DRW_opengl_context_release(void); void DRW_opengl_context_activate(bool drw_state); +/* We may want to move this into a more general location. */ +void DRW_draw_cursor_2d_ex(const struct ARegion *region, const float cursor[2]); + #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c index 90bb3762473..bbd345271b1 100644 --- a/source/blender/draw/intern/draw_view.c +++ b/source/blender/draw/intern/draw_view.c @@ -28,6 +28,7 @@ #include "DNA_view3d_types.h" #include "ED_screen.h" +#include "ED_util.h" #include "ED_view3d.h" #include "GPU_immediate.h" @@ -226,6 +227,46 @@ static bool is_cursor_visible_2d(const DRWContextState *draw_ctx) return (sima->overlay.flag & SI_OVERLAY_SHOW_OVERLAYS) != 0; } +/* -------------------------------------------------------------------- */ +/** \name Generic Cursor + * \{ */ + +/** + * \note This doesn't require the draw context to be in use. + */ +void DRW_draw_cursor_2d_ex(const ARegion *region, const float cursor[2]) +{ + int co[2]; + UI_view2d_view_to_region(®ion->v2d, cursor[0], cursor[1], &co[0], &co[1]); + + /* Draw nice Anti Aliased cursor. */ + GPU_line_width(1.0f); + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_smooth(true); + + /* Draw lines */ + float original_proj[4][4]; + GPU_matrix_projection_get(original_proj); + GPU_matrix_push(); + ED_region_pixelspace(region); + GPU_matrix_translate_2f(co[0] + 0.5f, co[1] + 0.5f); + GPU_matrix_scale_2f(U.widget_unit, U.widget_unit); + + GPUBatch *cursor_batch = DRW_cache_cursor_get(true); + + GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR); + GPU_batch_set_shader(cursor_batch, shader); + + GPU_batch_draw(cursor_batch); + + GPU_blend(GPU_BLEND_NONE); + GPU_line_smooth(false); + GPU_matrix_pop(); + GPU_matrix_projection_set(original_proj); +} + +/** \} */ + void DRW_draw_cursor_2d(void) { const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -236,33 +277,8 @@ void DRW_draw_cursor_2d(void) GPU_depth_test(GPU_DEPTH_NONE); if (is_cursor_visible_2d(draw_ctx)) { - SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; - int co[2]; - UI_view2d_view_to_region(®ion->v2d, sima->cursor[0], sima->cursor[1], &co[0], &co[1]); - - /* Draw nice Anti Aliased cursor. */ - GPU_line_width(1.0f); - GPU_blend(GPU_BLEND_ALPHA); - GPU_line_smooth(true); - - /* Draw lines */ - float original_proj[4][4]; - GPU_matrix_projection_get(original_proj); - GPU_matrix_push(); - ED_region_pixelspace(region); - GPU_matrix_translate_2f(co[0] + 0.5f, co[1] + 0.5f); - GPU_matrix_scale_2f(U.widget_unit, U.widget_unit); - - GPUBatch *cursor_batch = DRW_cache_cursor_get(true); - GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR); - GPU_batch_set_shader(cursor_batch, shader); - - GPU_batch_draw(cursor_batch); - - GPU_blend(GPU_BLEND_NONE); - GPU_line_smooth(false); - GPU_matrix_pop(); - GPU_matrix_projection_set(original_proj); + const SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; + DRW_draw_cursor_2d_ex(region, sima->cursor); } } /** \} */ diff --git a/source/blender/draw/intern/draw_view.h b/source/blender/draw/intern/draw_view.h index 24fabaae05e..2646918cd3e 100644 --- a/source/blender/draw/intern/draw_view.h +++ b/source/blender/draw/intern/draw_view.h @@ -22,6 +22,8 @@ #pragma once +struct ARegion; + void DRW_draw_region_info(void); void DRW_clear_background(void); void DRW_draw_cursor(void); diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 3958c7f9e34..8232df968ed 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -74,9 +74,17 @@ static bool change_frame_poll(bContext *C) * this shouldn't show up in 3D editor (or others without 2D timeline view) via search */ if (area) { - if (ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP)) { + if (ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_CLIP)) { return true; } + if (area->spacetype == SPACE_SEQ) { + /* Check the region type so tools (which are shared between preview/strip view) + * don't conflict with actions which can have the same key bound (2D cursor for example). */ + const ARegion *region = CTX_wm_region(C); + if (region && region->regiontype == RGN_TYPE_WINDOW) { + return true; + } + } if (area->spacetype == SPACE_GRAPH) { const SpaceGraph *sipo = area->spacedata.first; /* Driver Editor's X axis is not time. */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index b6936892803..92aeaf03329 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -68,7 +68,7 @@ void ED_region_do_layout(struct bContext *C, struct ARegion *region); void ED_region_do_draw(struct bContext *C, struct ARegion *region); void ED_region_exit(struct bContext *C, struct ARegion *region); void ED_region_remove(struct bContext *C, struct ScrArea *area, struct ARegion *region); -void ED_region_pixelspace(struct ARegion *region); +void ED_region_pixelspace(const struct ARegion *region); void ED_region_update_rect(struct ARegion *region); void ED_region_floating_init(struct ARegion *region); void ED_region_tag_redraw(struct ARegion *region); diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 0105af843bb..df132e6ae80 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -30,6 +30,7 @@ extern "C" { #endif +struct GPUBatch; struct Main; struct bContext; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 9967d36fd40..473674f1059 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -134,7 +134,7 @@ static void region_draw_emboss(const ARegion *region, const rcti *scirct, int si GPU_blend(GPU_BLEND_NONE); } -void ED_region_pixelspace(ARegion *region) +void ED_region_pixelspace(const ARegion *region) { wmOrtho2_region_pixelspace(region); GPU_matrix_identity_set(); diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index 1471929defb..bf8cf89699d 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -22,6 +22,7 @@ set(INC ../../blenlib ../../blentranslation ../../depsgraph + ../../draw ../../gpu ../../imbuf ../../makesdna diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 655cfb9375c..50a74d9e636 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -3397,3 +3397,64 @@ void SEQUENCER_OT_strip_color_tag_set(struct wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Set 2D Cursor Operator + * \{ */ + +static int sequencer_set_2d_cursor_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + SpaceSeq *sseq = CTX_wm_space_seq(C); + + float cursor_pixel[2]; + RNA_float_get_array(op->ptr, "location", cursor_pixel); + + SEQ_image_preview_unit_from_px(scene, cursor_pixel, sseq->cursor); + + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + return OPERATOR_FINISHED; +} + +static int sequencer_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + float cursor_pixel[2]; + UI_view2d_region_to_view( + ®ion->v2d, event->mval[0], event->mval[1], &cursor_pixel[0], &cursor_pixel[1]); + + RNA_float_set_array(op->ptr, "location", cursor_pixel); + + return sequencer_set_2d_cursor_exec(C, op); +} + +void SEQUENCER_OT_cursor_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set 2D Cursor"; + ot->description = "Set 2D cursor location"; + ot->idname = "SEQUENCER_OT_cursor_set"; + + /* api callbacks */ + ot->exec = sequencer_set_2d_cursor_exec; + ot->invoke = sequencer_set_2d_cursor_invoke; + ot->poll = sequencer_view_preview_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_float_vector(ot->srna, + "location", + 2, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Cursor location in normalized preview coordinates", + -10.0f, + 10.0f); +} + +/** \} */ diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 730d4d11e78..5982a0a8993 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -165,6 +165,7 @@ void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot); void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot); void SEQUENCER_OT_strip_color_tag_set(struct wmOperatorType *ot); +void SEQUENCER_OT_cursor_set(struct wmOperatorType *ot); /* sequencer_select.c */ void SEQUENCER_OT_select_all(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 95f7b44264c..2c5f211b0e4 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -80,6 +80,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_strip_transform_fit); WM_operatortype_append(SEQUENCER_OT_strip_color_tag_set); + WM_operatortype_append(SEQUENCER_OT_cursor_set); /* sequencer_select.c */ WM_operatortype_append(SEQUENCER_OT_select_all); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index ad0ceb82709..c1f853270e9 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -41,6 +41,8 @@ #include "BKE_screen.h" #include "BKE_sequencer_offscreen.h" +#include "GPU_state.h" + #include "ED_screen.h" #include "ED_space_api.h" #include "ED_transform.h" @@ -53,6 +55,7 @@ #include "RNA_access.h" +#include "SEQ_transform.h" #include "SEQ_utils.h" #include "UI_interface.h" @@ -61,6 +64,9 @@ #include "IMB_imbuf.h" +/* Only for cursor drawing. */ +#include "DRW_engine.h" + /* Own include. */ #include "sequencer_intern.h" @@ -803,6 +809,17 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) } } + { + GPU_color_mask(true, true, true, true); + GPU_depth_mask(false); + GPU_depth_test(GPU_DEPTH_NONE); + + float cursor_pixel[2]; + SEQ_image_preview_unit_to_px(scene, sseq->cursor, cursor_pixel); + + DRW_draw_cursor_2d_ex(region, cursor_pixel); + } + WM_gizmomap_draw(region->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(wm)) { diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 6ed2c28a7eb..9dcd3a3e510 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -48,6 +48,8 @@ #include "ED_screen.h" #include "ED_space_api.h" +#include "SEQ_transform.h" + #include "WM_api.h" #include "WM_message.h" #include "WM_types.h" @@ -128,6 +130,11 @@ void setTransformViewAspect(TransInfo *t, float r_aspect[3]) ED_space_image_get_uv_aspect(sima, &r_aspect[0], &r_aspect[1]); } } + else if (t->spacetype == SPACE_SEQ) { + if (t->options & CTX_CURSOR) { + SEQ_image_preview_unit_to_px(t->scene, r_aspect, r_aspect); + } + } else if (t->spacetype == SPACE_CLIP) { SpaceClip *sclip = t->area->spacedata.first; diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 1be46c03f85..3076b3e207f 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -222,6 +222,7 @@ typedef enum { TC_POSE, TC_ARMATURE_VERTS, TC_CURSOR_IMAGE, + TC_CURSOR_SEQUENCER, TC_CURSOR_VIEW3D, TC_CURVE_VERTS, TC_GRAPH_EDIT_DATA, diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 557fa79e7ac..00be756878b 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -945,6 +945,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) break; case TC_ARMATURE_VERTS: case TC_CURSOR_IMAGE: + case TC_CURSOR_SEQUENCER: case TC_CURSOR_VIEW3D: case TC_CURVE_VERTS: case TC_GPENCIL: @@ -1037,6 +1038,7 @@ static void init_proportional_edit(TransInfo *t) case TC_POSE: /* Disable PET, its not usable in pose mode yet T32444. */ case TC_ARMATURE_VERTS: case TC_CURSOR_IMAGE: + case TC_CURSOR_SEQUENCER: case TC_CURSOR_VIEW3D: case TC_NLA_DATA: case TC_OBJECT_TEXSPACE: @@ -1112,6 +1114,7 @@ static void init_TransDataContainers(TransInfo *t, case TC_ACTION_DATA: case TC_GRAPH_EDIT_DATA: case TC_CURSOR_IMAGE: + case TC_CURSOR_SEQUENCER: case TC_CURSOR_VIEW3D: case TC_MASKING_DATA: case TC_NLA_DATA: @@ -1223,6 +1226,7 @@ static eTFlag flags_from_data_type(eTConvertType data_type) case TC_MESH_UV: return T_EDIT | T_POINTS | T_2D_EDIT; case TC_CURSOR_IMAGE: + case TC_CURSOR_SEQUENCER: return T_2D_EDIT; case TC_PARTICLE_VERTS: return T_POINTS; @@ -1249,6 +1253,9 @@ static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armatur if (t->spacetype == SPACE_IMAGE) { convert_type = TC_CURSOR_IMAGE; } + else if (t->spacetype == SPACE_SEQ) { + convert_type = TC_CURSOR_SEQUENCER; + } else { convert_type = TC_CURSOR_VIEW3D; } @@ -1396,6 +1403,9 @@ void createTransData(bContext *C, TransInfo *t) case TC_CURSOR_IMAGE: createTransCursor_image(t); break; + case TC_CURSOR_SEQUENCER: + createTransCursor_sequencer(t); + break; case TC_CURSOR_VIEW3D: createTransCursor_view3d(t); break; @@ -1714,8 +1724,11 @@ void recalcData(TransInfo *t) case TC_CURSOR_IMAGE: recalcData_cursor_image(t); break; + case TC_CURSOR_SEQUENCER: + recalcData_cursor_sequencer(t); + break; case TC_CURSOR_VIEW3D: - recalcData_cursor(t); + recalcData_cursor_view3d(t); break; case TC_GRAPH_EDIT_DATA: recalcData_graphedit(t); diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 66d84bca2d2..e4f2ab05bec 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -85,9 +85,11 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t); /* transform_convert_cursor.c */ void createTransCursor_image(TransInfo *t); +void createTransCursor_sequencer(TransInfo *t); void createTransCursor_view3d(TransInfo *t); void recalcData_cursor_image(TransInfo *t); -void recalcData_cursor(TransInfo *t); +void recalcData_cursor_sequencer(TransInfo *t); +void recalcData_cursor_view3d(TransInfo *t); /* transform_convert_curve.c */ void createTransCurveVerts(TransInfo *t); diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c index 1f3eff31205..8a4a13eb4db 100644 --- a/source/blender/editors/transform/transform_convert_cursor.c +++ b/source/blender/editors/transform/transform_convert_cursor.c @@ -19,6 +19,8 @@ /** \file * \ingroup edtransform + * + * Instead of transforming the selection, move the 2D/3D cursor. */ #include "DNA_space_types.h" @@ -35,18 +37,12 @@ #include "transform_convert.h" /* -------------------------------------------------------------------- */ -/** \name Cursor Transform Creation - * - * Instead of transforming the selection, move the 2D/3D cursor. - * +/** \name Shared 2D Cursor Utilities * \{ */ -void createTransCursor_image(TransInfo *t) +static void createTransCursor_2D_impl(TransInfo *t, float cursor_location[2]) { TransData *td; - SpaceImage *sima = t->area->spacedata.first; - float *cursor_location = sima->cursor; - { BLI_assert(t->data_container_len == 1); TransDataContainer *tc = t->data_container; @@ -57,7 +53,7 @@ void createTransCursor_image(TransInfo *t) td->flag = TD_SELECTED; - /* UV coords are scaled by aspects (see UVsToTransData). This also applies for the Cursor in the + /* UV coords are scaled by aspects (see #UVsToTransData). This also applies for the Cursor in the * UV Editor which also means that for display and when the cursor coords are flushed * (recalcData_cursor_image), these are converted each time. */ cursor_location[0] = cursor_location[0] * t->aspect[0]; @@ -74,6 +70,62 @@ void createTransCursor_image(TransInfo *t) copy_v3_v3(td->iloc, cursor_location); } +static void recalcData_cursor_2D_impl(TransInfo *t) +{ + TransDataContainer *tc = t->data_container; + TransData *td = tc->data; + float aspect_inv[2]; + + aspect_inv[0] = 1.0f / t->aspect[0]; + aspect_inv[1] = 1.0f / t->aspect[1]; + + td->loc[0] = td->loc[0] * aspect_inv[0]; + td->loc[1] = td->loc[1] * aspect_inv[1]; + + DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Cursor + * \{ */ + +void createTransCursor_image(TransInfo *t) +{ + SpaceImage *sima = t->area->spacedata.first; + createTransCursor_2D_impl(t, sima->cursor); +} + +void recalcData_cursor_image(TransInfo *t) +{ + recalcData_cursor_2D_impl(t); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sequencer Cursor + * \{ */ + +void createTransCursor_sequencer(TransInfo *t) +{ + SpaceSeq *sseq = t->area->spacedata.first; + + createTransCursor_2D_impl(t, sseq->cursor); +} + +void recalcData_cursor_sequencer(TransInfo *t) +{ + recalcData_cursor_2D_impl(t); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View 3D Cursor + * \{ */ + void createTransCursor_view3d(TransInfo *t) { TransData *td; @@ -133,28 +185,7 @@ void createTransCursor_view3d(TransInfo *t) td->ext->rotOrder = cursor->rotation_mode; } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Recalc Cursor - * \{ */ - -void recalcData_cursor_image(TransInfo *t) -{ - TransDataContainer *tc = t->data_container; - TransData *td = tc->data; - float aspect_inv[2]; - - aspect_inv[0] = 1.0f / t->aspect[0]; - aspect_inv[1] = 1.0f / t->aspect[1]; - - td->loc[0] = td->loc[0] * aspect_inv[0]; - td->loc[1] = td->loc[1] * aspect_inv[1]; - - DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); -} - -void recalcData_cursor(TransInfo *t) +void recalcData_cursor_view3d(TransInfo *t) { DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index fa323f0c1f7..09c338046ed 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -46,6 +46,8 @@ #include "BKE_modifier.h" #include "BKE_paint.h" +#include "SEQ_transform.h" + #include "ED_clip.h" #include "ED_image.h" #include "ED_object.h" @@ -885,12 +887,18 @@ void calculateCenterCursor(TransInfo *t, float r_center[3]) void calculateCenterCursor2D(TransInfo *t, float r_center[2]) { + float cursor_local_buf[2]; const float *cursor = NULL; if (t->spacetype == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)t->area->spacedata.first; cursor = sima->cursor; } + if (t->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)t->area->spacedata.first; + SEQ_image_preview_unit_to_px(t->scene, sseq->cursor, cursor_local_buf); + cursor = cursor_local_buf; + } else if (t->spacetype == SPACE_CLIP) { SpaceClip *space_clip = (SpaceClip *)t->area->spacedata.first; cursor = space_clip->cursor; @@ -1069,7 +1077,7 @@ static void calculateCenter_FromAround(TransInfo *t, int around, float r_center[ calculateCenterMedian(t, r_center); break; case V3D_AROUND_CURSOR: - if (ELEM(t->spacetype, SPACE_IMAGE, SPACE_CLIP)) { + if (ELEM(t->spacetype, SPACE_IMAGE, SPACE_SEQ, SPACE_CLIP)) { calculateCenterCursor2D(t, r_center); } else if (t->spacetype == SPACE_GRAPH) { diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index aa4d5c03d74..b25a182926e 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -284,23 +284,25 @@ static bool gizmo2d_calc_center(const bContext *C, float r_center[2]) ED_uvedit_center_from_pivot_ex(sima, scene, view_layer, r_center, sima->around, &has_select); } else if (area->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = area->spacedata.first; + const int pivot_point = scene->toolsettings->sequencer_tool_settings->pivot_point; ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); SEQ_filter_selected_strips(strips); + has_select = SEQ_collection_len(strips) != 0; - if (SEQ_collection_len(strips) <= 0) { - SEQ_collection_free(strips); - return false; + if (pivot_point == V3D_AROUND_CURSOR) { + SEQ_image_preview_unit_to_px(scene, sseq->cursor, r_center); } - - has_select = true; - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, strips) { - float origin[2]; - SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin); - add_v2_v2(r_center, origin); + else if (has_select) { + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + float origin[2]; + SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin); + add_v2_v2(r_center, origin); + } + mul_v2_fl(r_center, 1.0f / SEQ_collection_len(strips)); } - mul_v2_fl(r_center, 1.0f / SEQ_collection_len(strips)); SEQ_collection_free(strips); } diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index aa74e7712c0..bace73b88ba 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -647,6 +647,9 @@ typedef struct SpaceSeq { int draw_flag; char _pad[4]; + /** 2D cursor for transform. */ + float cursor[2]; + /** Grease-pencil data. */ struct bGPdata *gpd; diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 5b46dfa8210..3468cab9eea 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3539,7 +3539,9 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) }; static const EnumPropertyItem pivot_points[] = { + {V3D_AROUND_CENTER_BOUNDS, "CENTER", ICON_PIVOT_BOUNDBOX, "Bounding Box Center", ""}, {V3D_AROUND_CENTER_MEDIAN, "MEDIAN", ICON_PIVOT_MEDIAN, "Median Point", ""}, + {V3D_AROUND_CURSOR, "CURSOR", ICON_PIVOT_CURSOR, "2D Cursor", "Pivot around the 2D cursor"}, {V3D_AROUND_LOCAL_ORIGINS, "INDIVIDUAL_ORIGINS", ICON_PIVOT_INDIVIDUAL, diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 652d2545e67..9d8e06b3c56 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5690,6 +5690,13 @@ static void rna_def_space_sequencer(BlenderRNA *brna) rna_def_space_sequencer_preview_overlay(brna); rna_def_space_sequencer_timeline_overlay(brna); + + /* transform */ + prop = RNA_def_property(srna, "cursor_location", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "cursor"); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_text(prop, "2D Cursor Location", "2D cursor location for this view"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL); } static void rna_def_space_text(BlenderRNA *brna) diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index 328efb9424a..69db21d4c63 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -70,6 +70,13 @@ void SEQ_image_transform_final_quad_get(const struct Scene *scene, const struct Sequence *seq, float r_quad[4][2]); +void SEQ_image_preview_unit_to_px(const struct Scene *scene, + const float co_src[2], + float co_dst[2]); +void SEQ_image_preview_unit_from_px(const struct Scene *scene, + const float co_src[2], + float co_dst[2]); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 8c214f66b96..f014c7a312a 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -520,3 +520,15 @@ void SEQ_image_transform_final_quad_get(const Scene *scene, mul_v2_v2(r_quad[2], mirror); mul_v2_v2(r_quad[3], mirror); } + +void SEQ_image_preview_unit_to_px(const Scene *scene, const float co_src[2], float co_dst[2]) +{ + co_dst[0] = co_src[0] * scene->r.xsch; + co_dst[1] = co_src[1] * scene->r.ysch; +} + +void SEQ_image_preview_unit_from_px(const Scene *scene, const float co_src[2], float co_dst[2]) +{ + co_dst[0] = co_src[0] / scene->r.xsch; + co_dst[1] = co_src[1] / scene->r.ysch; +} From ba4e5399fc85e318c41380b0b1c81b23a8334786 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 03:59:20 +1100 Subject: [PATCH 0611/1500] Fix screenshot editor showing status text in the editor This caused problems calling screenshot from menu-search which included the status text in the screenshot. Now the status text is shown in the global status bar for any operators called from a screen context. --- release/scripts/startup/bl_ui/space_topbar.py | 10 ++++++++++ source/blender/windowmanager/WM_types.h | 4 ++++ source/blender/windowmanager/intern/wm_event_system.c | 5 ++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 1d75ad8ff0a..9d5e2a78631 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -634,6 +634,8 @@ class TOPBAR_MT_window(Menu): layout = self.layout + operator_context_default = layout.operator_context + layout.operator("wm.window_new") layout.operator("wm.window_new_main") @@ -655,7 +657,15 @@ class TOPBAR_MT_window(Menu): layout.separator() layout.operator("screen.screenshot") + + # Showing the status in the area doesn't work well in this case. + # - From the top-bar, the text replaces the file-menu (not so bad but strange). + # - From menu-search it replaces the area that the user may want to screen-shot. + # Setting the context to screen causes the status to show in the global status-bar. + print(layout.operator_context) + layout.operator_context = 'INVOKE_SCREEN' layout.operator("screen.screenshot_area") + layout.operator_context = operator_context_default if sys.platform[:3] == "win": layout.separator() diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 28c7a270554..c4612485e5a 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -227,6 +227,10 @@ enum { WM_OP_EXEC_SCREEN, }; +#define WM_OP_CONTEXT_HAS_AREA(type) (!ELEM(type, WM_OP_INVOKE_SCREEN, WM_OP_EXEC_SCREEN)) +#define WM_OP_CONTEXT_HAS_REGION(type) \ + (WM_OP_CONTEXT_HAS_AREA(type) && !ELEM(type, WM_OP_INVOKE_AREA, WM_OP_EXEC_AREA)) + /* property tags for RNA_OperatorProperties */ typedef enum eOperatorPropTags { OP_PROP_TAG_ADVANCED = (1 << 0), diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index f4753c7c190..785434c301c 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1790,7 +1790,10 @@ void WM_operator_name_call_ptr_with_depends_on_cursor( } wmWindow *win = CTX_wm_window(C); - ScrArea *area = CTX_wm_area(C); + /* The operator context is applied when the operator is called, + * the check for the area needs to be explicitly limited here. + * Useful so it's possible to screen-shot an area without drawing into it's header. */ + ScrArea *area = WM_OP_CONTEXT_HAS_AREA(opcontext) ? CTX_wm_area(C) : NULL; { char header_text[UI_MAX_DRAW_STR]; From 0cd3d462466c0b746611d1e552109de3a3632467 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Thu, 7 Oct 2021 15:04:20 +0100 Subject: [PATCH 0612/1500] Nodes: Move texture nodes to C++ Move texture nodes to C++ and use new socket declaration Brick, Checker, Image, Magic and Wave Differential Revision: https://developer.blender.org/D12778 --- source/blender/nodes/CMakeLists.txt | 10 +- ...r_tex_brick.c => node_shader_tex_brick.cc} | 136 +++++------------- ...x_checker.c => node_shader_tex_checker.cc} | 49 +++---- ...r_tex_image.c => node_shader_tex_image.cc} | 38 ++--- ...r_tex_magic.c => node_shader_tex_magic.cc} | 34 ++--- ...der_tex_wave.c => node_shader_tex_wave.cc} | 46 +++--- 6 files changed, 103 insertions(+), 210 deletions(-) rename source/blender/nodes/shader/nodes/{node_shader_tex_brick.c => node_shader_tex_brick.cc} (58%) rename source/blender/nodes/shader/nodes/{node_shader_tex_checker.c => node_shader_tex_checker.cc} (67%) rename source/blender/nodes/shader/nodes/{node_shader_tex_image.c => node_shader_tex_image.cc} (88%) rename source/blender/nodes/shader/nodes/{node_shader_tex_magic.c => node_shader_tex_magic.cc} (71%) rename source/blender/nodes/shader/nodes/{node_shader_tex_wave.c => node_shader_tex_wave.cc} (70%) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index dab7579d946..a4350c10087 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -319,19 +319,19 @@ set(SRC shader/nodes/node_shader_squeeze.c shader/nodes/node_shader_subsurface_scattering.c shader/nodes/node_shader_tangent.c - shader/nodes/node_shader_tex_brick.c - shader/nodes/node_shader_tex_checker.c + shader/nodes/node_shader_tex_brick.cc + shader/nodes/node_shader_tex_checker.cc shader/nodes/node_shader_tex_coord.c shader/nodes/node_shader_tex_environment.c shader/nodes/node_shader_tex_gradient.cc - shader/nodes/node_shader_tex_image.c - shader/nodes/node_shader_tex_magic.c + shader/nodes/node_shader_tex_image.cc + shader/nodes/node_shader_tex_magic.cc shader/nodes/node_shader_tex_musgrave.cc shader/nodes/node_shader_tex_noise.cc shader/nodes/node_shader_tex_pointdensity.c shader/nodes/node_shader_tex_sky.c shader/nodes/node_shader_tex_voronoi.cc - shader/nodes/node_shader_tex_wave.c + shader/nodes/node_shader_tex_wave.cc shader/nodes/node_shader_tex_white_noise.cc shader/nodes/node_shader_uvAlongStroke.c shader/nodes/node_shader_uvmap.c diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc similarity index 58% rename from source/blender/nodes/shader/nodes/node_shader_tex_brick.c rename to source/blender/nodes/shader/nodes/node_shader_tex_brick.cc index 1b802f1dfd7..e90dae60189 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc @@ -19,112 +19,46 @@ #include "../node_shader_util.h" -/* **************** OUTPUT ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_brick_in[] = { - {SOCK_VECTOR, - N_("Vector"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_HIDE_VALUE | SOCK_NO_INTERNAL_LINK}, - {SOCK_RGBA, N_("Color1"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("Color2"), 0.2f, 0.2f, 0.2f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, - N_("Mortar"), - 0.0f, - 0.0f, - 0.0f, - 1.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Scale"), - 5.0f, - 0.0f, - 0.0f, - 0.0f, - -1000.0f, - 1000.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Mortar Size"), - 0.02f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.125f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Mortar Smooth"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Bias"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - -1.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Brick Width"), - 0.5f, - 0.0f, - 0.0f, - 0.0f, - 0.01f, - 100.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Row Height"), - 0.25f, - 0.0f, - 0.0f, - 0.0f, - 0.01f, - 100.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, +static void sh_node_tex_brick_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input("Vector").min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input("Color1").default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input("Color2").default_value({0.2f, 0.2f, 0.2f, 1.0f}); + b.add_input("Mortar").default_value({0.0f, 0.0f, 0.0f, 1.0f}).no_muted_links(); + b.add_input("Scale") + .min(-1000.0f) + .max(1000.0f) + .default_value(5.0f) + .no_muted_links(); + b.add_input("Mortar Size") + .min(0.0f) + .max(0.125f) + .default_value(0.02f) + .no_muted_links(); + b.add_input("Mortar Smooth").min(0.0f).max(1.0f).no_muted_links(); + b.add_input("Bias").min(-1.0f).max(1.0f).no_muted_links(); + b.add_input("Brick Width") + .min(0.01f) + .max(100.0f) + .default_value(0.5f) + .no_muted_links(); + b.add_input("Row Height") + .min(0.01f) + .max(100.0f) + .default_value(0.25f) + .no_muted_links(); + b.add_output("Color"); + b.add_output("Fac"); }; -static bNodeSocketTemplate sh_node_tex_brick_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_brick(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexBrick *tex = MEM_callocN(sizeof(NodeTexBrick), "NodeTexBrick"); + NodeTexBrick *tex = (NodeTexBrick *)MEM_callocN(sizeof(NodeTexBrick), "NodeTexBrick"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); @@ -170,7 +104,7 @@ void register_node_type_sh_tex_brick(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_brick_in, sh_node_tex_brick_out); + ntype.declare = blender::nodes::sh_node_tex_brick_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, node_shader_init_tex_brick); node_type_storage( diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.c b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc similarity index 67% rename from source/blender/nodes/shader/nodes/node_shader_tex_checker.c rename to source/blender/nodes/shader/nodes/node_shader_tex_checker.cc index 75219f4c3f9..26abc66fc3d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_checker.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc @@ -19,43 +19,28 @@ #include "../node_shader_util.h" -/* **************** OUTPUT ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_checker_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_RGBA, N_("Color1"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("Color2"), 0.2f, 0.2f, 0.2f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, - N_("Scale"), - 5.0f, - 0.0f, - 0.0f, - 0.0f, - -1000.0f, - 1000.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, +static void sh_node_tex_checker_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input("Vector").min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input("Color1").default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input("Color2").default_value({0.2f, 0.2f, 0.2f, 1.0f}); + b.add_input("Scale") + .min(-10000.0f) + .max(10000.0f) + .default_value(5.0f) + .no_muted_links(); + b.add_output("Color"); + b.add_output("Fac"); }; -static bNodeSocketTemplate sh_node_tex_checker_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_checker(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexChecker *tex = MEM_callocN(sizeof(NodeTexChecker), "NodeTexChecker"); + NodeTexChecker *tex = (NodeTexChecker *)MEM_callocN(sizeof(NodeTexChecker), "NodeTexChecker"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); @@ -80,7 +65,7 @@ void register_node_type_sh_tex_checker(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_TEX_CHECKER, "Checker Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_checker_in, sh_node_tex_checker_out); + ntype.declare = blender::nodes::sh_node_tex_checker_declare; node_type_init(&ntype, node_shader_init_tex_checker); node_type_storage( &ntype, "NodeTexChecker", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc similarity index 88% rename from source/blender/nodes/shader/nodes/node_shader_tex_image.c rename to source/blender/nodes/shader/nodes/node_shader_tex_image.cc index 09d06c35a5f..df1051c07b4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc @@ -19,31 +19,21 @@ #include "../node_shader_util.h" -/* **************** OUTPUT ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_image_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, +static void sh_node_tex_image_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input("Vector").implicit_field(); + b.add_output("Color").no_muted_links(); + b.add_output("Alpha").no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_image_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Alpha"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +}; // namespace blender::nodes static void node_shader_init_tex_image(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexImage *tex = MEM_callocN(sizeof(NodeTexImage), "NodeTexImage"); + NodeTexImage *tex = (NodeTexImage *)MEM_callocN(sizeof(NodeTexImage), "NodeTexImage"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); BKE_imageuser_default(&tex->iuser); @@ -58,12 +48,12 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, GPUNodeStack *out) { Image *ima = (Image *)node->id; - NodeTexImage *tex = node->storage; + NodeTexImage *tex = (NodeTexImage *)node->storage; /* We get the image user from the original node, since GPU image keeps * a pointer to it and the dependency refreshes the original. */ bNode *node_original = node->original ? node->original : node; - NodeTexImage *tex_original = node_original->storage; + NodeTexImage *tex_original = (NodeTexImage *)node_original->storage; ImageUser *iuser = &tex_original->iuser; if (!ima) { @@ -78,7 +68,7 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, node_shader_gpu_tex_mapping(mat, node, in, out); - eGPUSamplerState sampler_state = 0; + eGPUSamplerState sampler_state = GPU_SAMPLER_DEFAULT; switch (tex->extension) { case SHD_IMAGE_EXTENSION_REPEAT: @@ -94,7 +84,7 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, if (tex->interpolation != SHD_INTERP_CLOSEST) { sampler_state |= GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; /* TODO(fclem): For now assume mipmap is always enabled. */ - sampler_state |= true ? GPU_SAMPLER_MIPMAP : 0; + sampler_state |= GPU_SAMPLER_MIPMAP; } const bool use_cubic = ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART); @@ -189,7 +179,7 @@ void register_node_type_sh_tex_image(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_TEX_IMAGE, "Image Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_image_in, sh_node_tex_image_out); + ntype.declare = blender::nodes::sh_node_tex_image_declare; node_type_init(&ntype, node_shader_init_tex_image); node_type_storage( &ntype, "NodeTexImage", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.c b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc similarity index 71% rename from source/blender/nodes/shader/nodes/node_shader_tex_magic.c rename to source/blender/nodes/shader/nodes/node_shader_tex_magic.cc index 51721f8bb09..0bd15005816 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc @@ -19,33 +19,23 @@ #include "../node_shader_util.h" -/* **************** OUTPUT ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_magic_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Distortion"), 1.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {-1, ""}, +static void sh_node_tex_magic_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input("Vector").implicit_field(); + b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input("Distortion").min(-1000.0f).max(1000.0f).default_value(1.0f); + b.add_output("Color").no_muted_links(); + b.add_output("Fac").no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_magic_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_magic(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexMagic *tex = MEM_callocN(sizeof(NodeTexMagic), "NodeTexMagic"); + NodeTexMagic *tex = (NodeTexMagic *)MEM_callocN(sizeof(NodeTexMagic), "NodeTexMagic"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->depth = 2; @@ -74,7 +64,7 @@ void register_node_type_sh_tex_magic(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_TEX_MAGIC, "Magic Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_magic_in, sh_node_tex_magic_out); + ntype.declare = blender::nodes::sh_node_tex_magic_declare; node_type_init(&ntype, node_shader_init_tex_magic); node_type_storage( &ntype, "NodeTexMagic", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc similarity index 70% rename from source/blender/nodes/shader/nodes/node_shader_tex_wave.c rename to source/blender/nodes/shader/nodes/node_shader_tex_wave.cc index bba568ed5b7..79fec5f6e0e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc @@ -19,37 +19,31 @@ #include "../node_shader_util.h" -/* **************** WAVE ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_wave_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, - {SOCK_FLOAT, N_("Detail Scale"), 1.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Phase Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {-1, ""}, +static void sh_node_tex_wave_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input("Vector").implicit_field(); + b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input("Distortion").min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_input("Detail").min(0.0f).max(16.0f).default_value(2.0f); + b.add_input("Detail Scale").min(-1000.0f).max(1000.0f).default_value(1.0f); + b.add_input("Detail Roughness") + .min(0.0f) + .max(1.0f) + .default_value(0.5f) + .subtype(PROP_FACTOR); + b.add_input("Phase Offset").min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_output("Color").no_muted_links(); + b.add_output("Fac").no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_wave_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_wave(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexWave *tex = MEM_callocN(sizeof(NodeTexWave), "NodeTexWave"); + NodeTexWave *tex = (NodeTexWave *)MEM_callocN(sizeof(NodeTexWave), "NodeTexWave"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->wave_type = SHD_WAVE_BANDS; @@ -91,7 +85,7 @@ void register_node_type_sh_tex_wave(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_TEX_WAVE, "Wave Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_wave_in, sh_node_tex_wave_out); + ntype.declare = blender::nodes::sh_node_tex_wave_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, node_shader_init_tex_wave); node_type_storage(&ntype, "NodeTexWave", node_free_standard_storage, node_copy_standard_storage); From e9daca77d69f70b32dca57f5ea181fd219a02d3b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 7 Oct 2021 13:44:06 -0500 Subject: [PATCH 0613/1500] Fix: Missing field markers for curve fillet node inputs --- source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 67ce20efd9d..78ab4119c8b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -30,12 +30,13 @@ namespace blender::nodes { static void geo_node_curve_fillet_declare(NodeDeclarationBuilder &b) { b.add_input("Curve"); - b.add_input("Count").default_value(1).min(1).max(1000); + b.add_input("Count").default_value(1).min(1).max(1000).supports_field(); b.add_input("Radius") .min(0.0f) .max(FLT_MAX) .subtype(PropertySubType::PROP_DISTANCE) - .default_value(0.25f); + .default_value(0.25f) + .supports_field(); b.add_input("Limit Radius"); b.add_output("Curve"); } From 9708b1f317a16f329bbd249c73003330c935d4f7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 7 Oct 2021 14:00:11 -0500 Subject: [PATCH 0614/1500] Cleanup: Rename enum values This makes the diff for adding a new version of the attribute transfer node slightly smaller. --- source/blender/makesdna/DNA_node_types.h | 4 ++-- source/blender/makesrna/intern/rna_nodetree.c | 4 ++-- .../geometry/nodes/legacy/node_geo_attribute_transfer.cc | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index ea87cef1118..edbb0070462 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -2136,8 +2136,8 @@ typedef enum GeometryNodeCurveFilletMode { } GeometryNodeCurveFilletMode; typedef enum GeometryNodeAttributeTransferMapMode { - GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, - GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1, + GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, + GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST = 1, } GeometryNodeAttributeTransferMapMode; typedef enum GeometryNodeRaycastMapMode { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index ddd2a3fcee0..5e997f81753 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10522,13 +10522,13 @@ static void def_geo_curve_trim(StructRNA *srna) static void def_geo_attribute_transfer(StructRNA *srna) { static EnumPropertyItem mapping_items[] = { - {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED, + {GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED, "NEAREST_FACE_INTERPOLATED", 0, "Nearest Face Interpolated", "Transfer the attribute from the nearest face on a surface (loose points and edges are " "ignored)"}, - {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST, + {GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST, "NEAREST", 0, "Nearest", diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc index f187ee39b94..0006905a445 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc @@ -463,12 +463,12 @@ static void transfer_attribute(const GeoNodeExecParams ¶ms, "position", dst_domain, {0, 0, 0}); switch (mapping) { - case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { + case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { transfer_attribute_nearest_face_interpolated( src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); break; } - case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { + case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST: { transfer_attribute_nearest( src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); break; From 4ee97f129a7ffbab5c5d544b661b8147369d1fd2 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 7 Oct 2021 17:05:25 +0200 Subject: [PATCH 0615/1500] Cleanup: remove unnecessary data from LocalIntersection --- intern/cycles/kernel/kernel_types.h | 3 --- intern/cycles/kernel/svm/svm_bevel.h | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 4a72f45f1a2..5000a96c331 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -866,9 +866,6 @@ typedef struct VolumeStack { /* Struct to gather multiple nearby intersections. */ typedef struct LocalIntersection { - Ray ray; - float3 weight[LOCAL_MAX_HITS]; - int num_hits; struct Intersection hits[LOCAL_MAX_HITS]; float3 Ng[LOCAL_MAX_HITS]; diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/svm_bevel.h index 19176087180..60302b8e3d7 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/svm_bevel.h @@ -182,17 +182,17 @@ ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, float3 disk_P = (disk_r * cosf(phi)) * disk_T + (disk_r * sinf(phi)) * disk_B; /* Create ray. */ - Ray *ray = &isect.ray; - ray->P = sd->P + disk_N * disk_height + disk_P; - ray->D = -disk_N; - ray->t = 2.0f * disk_height; - ray->dP = differential_zero_compact(); - ray->dD = differential_zero_compact(); - ray->time = sd->time; + Ray ray ccl_optional_struct_init; + ray.P = sd->P + disk_N * disk_height + disk_P; + ray.D = -disk_N; + ray.t = 2.0f * disk_height; + ray.dP = differential_zero_compact(); + ray.dD = differential_zero_compact(); + ray.time = sd->time; /* Intersect with the same object. if multiple intersections are found it * will use at most LOCAL_MAX_HITS hits, a random subset of all hits. */ - scene_intersect_local(kg, ray, &isect, sd->object, &lcg_state, LOCAL_MAX_HITS); + scene_intersect_local(kg, &ray, &isect, sd->object, &lcg_state, LOCAL_MAX_HITS); int num_eval_hits = min(isect.num_hits, LOCAL_MAX_HITS); @@ -201,14 +201,14 @@ ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, float3 hit_P; if (sd->type & PRIMITIVE_TRIANGLE) { hit_P = triangle_refine_local( - kg, sd, ray->P, ray->D, ray->t, isect.hits[hit].object, isect.hits[hit].prim); + kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim); } # ifdef __OBJECT_MOTION__ else if (sd->type & PRIMITIVE_MOTION_TRIANGLE) { float3 verts[3]; motion_triangle_vertices(kg, sd->object, isect.hits[hit].prim, sd->time, verts); hit_P = motion_triangle_refine_local( - kg, sd, ray->P, ray->D, ray->t, isect.hits[hit].object, isect.hits[hit].prim, verts); + kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim, verts); } # endif /* __OBJECT_MOTION__ */ From 23791db1456332ee83b16156dc117eaa873a765c Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 7 Oct 2021 21:23:02 +0200 Subject: [PATCH 0616/1500] Fix Cycles random walk SSS differences between CPU and GPU The Embree logic did not match the GPU. --- intern/cycles/bvh/bvh_embree.cpp | 42 +++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index 8c1ca1f5b38..ae5b7dd426a 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -155,41 +155,49 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) break; } - /* See triangle_intersect_subsurface() for the native equivalent. */ - for (int i = min(ctx->max_hits, ctx->local_isect->num_hits) - 1; i >= 0; --i) { - if (ctx->local_isect->hits[i].t == ray->tfar) { - /* This tells Embree to continue tracing. */ - *args->valid = 0; - break; - } - } - + LocalIntersection *local_isect = ctx->local_isect; int hit_idx = 0; if (ctx->lcg_state) { + /* See triangle_intersect_subsurface() for the native equivalent. */ + for (int i = min(ctx->max_hits, local_isect->num_hits) - 1; i >= 0; --i) { + if (local_isect->hits[i].t == ray->tfar) { + /* This tells Embree to continue tracing. */ + *args->valid = 0; + return; + } + } - ++ctx->local_isect->num_hits; - if (ctx->local_isect->num_hits <= ctx->max_hits) { - hit_idx = ctx->local_isect->num_hits - 1; + local_isect->num_hits++; + + if (local_isect->num_hits <= ctx->max_hits) { + hit_idx = local_isect->num_hits - 1; } else { /* reservoir sampling: if we are at the maximum number of * hits, randomly replace element or skip it */ - hit_idx = lcg_step_uint(ctx->lcg_state) % ctx->local_isect->num_hits; + hit_idx = lcg_step_uint(ctx->lcg_state) % local_isect->num_hits; if (hit_idx >= ctx->max_hits) { /* This tells Embree to continue tracing. */ *args->valid = 0; - break; + return; } } } else { - ctx->local_isect->num_hits = 1; + /* Record closest intersection only. */ + if (local_isect->num_hits && current_isect.t > local_isect->hits[0].t) { + *args->valid = 0; + return; + } + + local_isect->num_hits = 1; } + /* record intersection */ - ctx->local_isect->hits[hit_idx] = current_isect; - ctx->local_isect->Ng[hit_idx] = normalize(make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)); + local_isect->hits[hit_idx] = current_isect; + local_isect->Ng[hit_idx] = normalize(make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)); /* This tells Embree to continue tracing. */ *args->valid = 0; break; From 29e496e768cd850f731521c9bb95e017e09885be Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 11:57:42 +1100 Subject: [PATCH 0617/1500] Cleanup: remove redundant cursor copy, print --- release/scripts/startup/bl_ui/space_topbar.py | 1 - source/blender/editors/uvedit/uvedit_unwrap_ops.c | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 9d5e2a78631..518979a5ef3 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -662,7 +662,6 @@ class TOPBAR_MT_window(Menu): # - From the top-bar, the text replaces the file-menu (not so bad but strange). # - From menu-search it replaces the area that the user may want to screen-shot. # Setting the context to screen causes the status to show in the global status-bar. - print(layout.operator_context) layout.operator_context = 'INVOKE_SCREEN' layout.operator("screen.screenshot_area") layout.operator_context = operator_context_default diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 38233b55d15..89490e59bd8 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -171,7 +171,7 @@ bool ED_uvedit_udim_params_from_image_space(const SpaceImage *sima, int active_udim = 1001; /* NOTE: Presently, when UDIM grid and tiled image are present together, only active tile for * the tiled image is considered. */ - Image *image = sima->image; + const Image *image = sima->image; if (image && image->source == IMA_SRC_TILED) { ImageTile *active_tile = BLI_findlink(&image->tiles, image->active_tile_index); if (active_tile) { @@ -181,11 +181,10 @@ bool ED_uvedit_udim_params_from_image_space(const SpaceImage *sima, else { /* TODO: Support storing an active UDIM when there are no tiles present. * Until then, use 2D cursor to find the active tile index for the UDIM grid. */ - const float cursor_loc[2] = {sima->cursor[0], sima->cursor[1]}; - if (uv_coords_isect_udim(sima->image, sima->tile_grid_shape, cursor_loc)) { + if (uv_coords_isect_udim(sima->image, sima->tile_grid_shape, sima->cursor)) { int tile_number = 1001; - tile_number += floorf(cursor_loc[1]) * 10; - tile_number += floorf(cursor_loc[0]); + tile_number += floorf(sima->cursor[1]) * 10; + tile_number += floorf(sima->cursor[0]); active_udim = tile_number; } } From f9f88f50cb5c362dd613f58a5312eceb69269282 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 12:55:26 +1100 Subject: [PATCH 0618/1500] Fix sequencer preview/strip checks - Drawing annotations used a deprecated value to detect if drawing was supported. - ED_space_sequencer_check_show_strip was checking the preview image type (which doesn't impact strip display). Signed-off-by: Campbell Barton --- source/blender/editors/gpencil/annotate_paint.c | 2 +- source/blender/editors/space_sequencer/sequencer_edit.c | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 59ea105fbbb..8924865f5e1 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1311,7 +1311,7 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) p->align_flag = &ts->gpencil_v2d_align; /* check that gpencil data is allowed to be drawn */ - if (sseq->mainb == SEQ_DRAW_SEQUENCE) { + if (!((sseq->mainb == SEQ_DRAW_IMG_IMBUF) && (region->regiontype == RGN_TYPE_PREVIEW))) { p->status = GP_STATUS_ERROR; return 0; } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 50a74d9e636..2a29125af19 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -130,14 +130,13 @@ bool ED_space_sequencer_maskedit_poll(bContext *C) /* Are we displaying the seq output (not channels or histogram). */ bool ED_space_sequencer_check_show_imbuf(SpaceSeq *sseq) { - return (ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW) && - ELEM(sseq->mainb, SEQ_DRAW_SEQUENCE, SEQ_DRAW_IMG_IMBUF)); + return (sseq->mainb == SEQ_DRAW_IMG_IMBUF) && + (ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW)); } bool ED_space_sequencer_check_show_strip(SpaceSeq *sseq) { - return (ELEM(sseq->view, SEQ_VIEW_SEQUENCE, SEQ_VIEW_SEQUENCE_PREVIEW) && - ELEM(sseq->mainb, SEQ_DRAW_SEQUENCE, SEQ_DRAW_IMG_IMBUF)); + return ELEM(sseq->view, SEQ_VIEW_SEQUENCE, SEQ_VIEW_SEQUENCE_PREVIEW); } static bool sequencer_fcurves_targets_color_strip(const FCurve *fcurve) From 2f9fab716dbd53c9b8520272d3745994415dc819 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 13:03:39 +1100 Subject: [PATCH 0619/1500] Cleanup: remove deprecated SEQ_DRAW_SEQUENCE value While drawing cleared this value (as part of temporary fix from 2009), this was still being checked until recently. Remove this value in versioning code. Also clear unused text space flag. --- .../blenloader/intern/versioning_300.c | 22 +++++++++++++++++++ .../editors/space_sequencer/space_sequencer.c | 5 ----- source/blender/makesdna/DNA_space_types.h | 3 +-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 95fa3058931..34712caf584 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1719,5 +1719,27 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + switch (sl->spacetype) { + case SPACE_SEQ: { + SpaceSeq *sseq = (SpaceSeq *)sl; + enum { SEQ_DRAW_SEQUENCE = 0 }; + if (sseq->mainb == SEQ_DRAW_SEQUENCE) { + sseq->mainb = SEQ_DRAW_IMG_IMBUF; + } + break; + } + case SPACE_TEXT: { + SpaceText *st = (SpaceText *)sl; + st->flags &= ~ST_FLAG_UNUSED_4; + break; + } + } + } + } + } } } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index c1f853270e9..508669abd04 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -784,11 +784,6 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && (sseq->flag & SEQ_SHOW_OVERLAY)); - /* XXX temp fix for wrong setting in sseq->mainb */ - if (sseq->mainb == SEQ_DRAW_SEQUENCE) { - sseq->mainb = SEQ_DRAW_IMG_IMBUF; - } - if (!draw_overlay || sseq->overlay_type != SEQ_DRAW_OVERLAY_REFERENCE) { sequencer_draw_preview(C, scene, region, sseq, scene->r.cfra, 0, false, false); } diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index bace73b88ba..f6ce71dad54 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -667,7 +667,6 @@ typedef struct SpaceSeq { /* SpaceSeq.mainb */ typedef enum eSpaceSeq_RegionType { - SEQ_DRAW_SEQUENCE = 0, SEQ_DRAW_IMG_IMBUF = 1, SEQ_DRAW_IMG_WAVEFORM = 2, SEQ_DRAW_IMG_VECTORSCOPE = 3, @@ -1417,7 +1416,7 @@ typedef enum eSpaceText_Flags { /* scrollable */ ST_SCROLL_SELECT = (1 << 0), - ST_FLAG_UNUSED_4 = (1 << 4), /* dirty */ + ST_FLAG_UNUSED_4 = (1 << 4), /* Cleared. */ ST_FIND_WRAP = (1 << 5), ST_FIND_ALL = (1 << 6), From 8f4697e570d1e74e5121534051ebbfa7a3c3f27a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 13:20:19 +1100 Subject: [PATCH 0620/1500] Sequencer: only show the 2D cursor with overlays enabled Also hide when displaying scopes. --- source/blender/editors/space_sequencer/space_sequencer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 508669abd04..4c4c908ea3c 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -804,7 +804,8 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) } } - { + /* No need to show the cursor for scopes. */ + if (draw_overlay && (sseq->mainb == SEQ_DRAW_IMG_IMBUF)) { GPU_color_mask(true, true, true, true); GPU_depth_mask(false); GPU_depth_test(GPU_DEPTH_NONE); From de07bf2b13e8239b5175e9c4b79fc09f096b1b86 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 13:23:19 +1100 Subject: [PATCH 0621/1500] Cleanup: spelling --- .../kernel/closure/bsdf_principled_diffuse.h | 2 +- .../integrator_intersect_volume_stack.h | 2 +- intern/cycles/render/object.h | 2 +- source/blender/blenlib/intern/scanfill.c | 2 +- .../blender/blenloader/BLO_blend_validate.h | 2 +- source/blender/blenloader/BLO_read_write.h | 24 +++++++++---------- source/blender/sequencer/intern/disk_cache.c | 5 ++-- 7 files changed, 20 insertions(+), 19 deletions(-) diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index 55fe364db70..0d611f40096 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -45,7 +45,7 @@ ccl_device float3 calculate_principled_diffuse_brdf( float NdotV = dot(N, V); - /* H = normalize(L + V); // Bissector of an angle between L and V + /* H = normalize(L + V); // Bisector of an angle between L and V. * LH2 = 2 * dot(L, H)^2 = 2cos(x)^2 = cos(2x) + 1 = dot(L, V) + 1, * half-angle x between L and V is at most 90 deg */ diff --git a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h index 00e90701d19..33a77d0fe29 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h @@ -142,7 +142,7 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) } } #else - /* CUDA does not support defintion of a variable size arrays, so use the maximum possible. */ + /* CUDA does not support definition of a variable size arrays, so use the maximum possible. */ int enclosed_volumes[MAX_VOLUME_STACK_SIZE]; int step = 0; diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 84e2dfffebb..6920f2c1f1c 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -112,7 +112,7 @@ class Object : public Node { /* Check whether this object requires volume sampling (and hence might require space in the * volume stack). * - * Note that this is a naive iteration over sharders, which allows to access information prior + * Note that this is a naive iteration over shaders, which allows to access information prior * to `scene_update()`. */ bool check_is_volume() const; diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c index 8845167f536..26ecc7552dc 100644 --- a/source/blender/blenlib/intern/scanfill.c +++ b/source/blender/blenlib/intern/scanfill.c @@ -448,7 +448,7 @@ static void splitlist(ScanFillContext *sf_ctx, ListBase *temped, unsigned short nr) { - /* everything is in templist, write only poly nr to fillist */ + /* Everything is in temp-list, write only poly nr to fill-list. */ ScanFillVert *eve, *eve_next; ScanFillEdge *eed, *eed_next; diff --git a/source/blender/blenloader/BLO_blend_validate.h b/source/blender/blenloader/BLO_blend_validate.h index 78aa481d4b1..cdbf4bdd952 100644 --- a/source/blender/blenloader/BLO_blend_validate.h +++ b/source/blender/blenloader/BLO_blend_validate.h @@ -21,7 +21,7 @@ /** \file * \ingroup blenloader - * \brief Utils ensuring .blend file (i.e. Main) + * \brief Utilities ensuring `.blend` file (i.e. Main) * is in valid state during write and/or read process. */ diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index 86c7c367816..be5d28c7716 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -69,28 +69,28 @@ struct ReportList; * DNA Struct Writing * ------------------ * - * Functions dealing with DNA structs begin with BLO_write_struct_*. + * Functions dealing with DNA structs begin with `BLO_write_struct_*`. * * DNA struct types can be identified in different ways: - * - Run-time Name: The name is provided as const char *. - * - Compile-time Name: The name is provided at compile time. This is more efficient. - * - Struct ID: Every DNA struct type has an integer ID that can be queried with - * BLO_get_struct_id_by_name. Providing this ID can be a useful optimization when many structs - * of the same type are stored AND if those structs are not in a continuous array. + * - Run-time Name: The name is provided as `const char *`. + * - Compile-time Name: The name is provided at compile time. This is more efficient. + * - Struct ID: Every DNA struct type has an integer ID that can be queried with + * #BLO_get_struct_id_by_name. Providing this ID can be a useful optimization when many + * structs of the same type are stored AND if those structs are not in a continuous array. * * Often only a single instance of a struct is written at once. However, sometimes it is necessary * to write arrays or linked lists. Separate functions for that are provided as well. * - * There is a special macro for writing id structs: BLO_write_id_struct. Those are handled - * differently from other structs. + * There is a special macro for writing id structs: #BLO_write_id_struct. + * Those are handled differently from other structs. * * Raw Data Writing * ---------------- * - * At the core there is BLO_write_raw, which can write arbitrary memory buffers to the file. The - * code that reads this data might have to correct its byte-order. For the common cases there are - * convenience functions that write and read arrays of simple types such as int32. Those will - * correct endianness automatically. + * At the core there is #BLO_write_raw, which can write arbitrary memory buffers to the file. + * The code that reads this data might have to correct its byte-order. For the common cases + * there are convenience functions that write and read arrays of simple types such as `int32`. + * Those will correct endianness automatically. */ /* Mapping between names and ids. */ diff --git a/source/blender/sequencer/intern/disk_cache.c b/source/blender/sequencer/intern/disk_cache.c index 543c23b184b..06d27717bdd 100644 --- a/source/blender/sequencer/intern/disk_cache.c +++ b/source/blender/sequencer/intern/disk_cache.c @@ -76,11 +76,12 @@ * is used as UID. Blend file can still be copied manually which may cause conflict. */ -/* -x-%()-.dcf */ +/* Format string: + * `-x-%()-.dcf`. */ #define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf" #define DCACHE_IMAGES_PER_FILE 100 #define DCACHE_CURRENT_VERSION 2 -#define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */ +#define COLORSPACE_NAME_MAX 64 /* XXX: defined in IMB intern. */ typedef struct DiskCacheHeaderEntry { unsigned char encoding; From ebe216f532845069bc5bb7b45e2eda03756b81cc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 17:07:56 +1100 Subject: [PATCH 0622/1500] Sequencer: add option to toggle gizmos Use shortcut matching the 3D view & popover in the header --- .../keyconfig/keymap_data/blender_default.py | 11 +++++- .../scripts/startup/bl_ui/space_sequencer.py | 35 +++++++++++++++++++ source/blender/editors/include/ED_image.h | 6 ++-- .../editors/interface/view2d_gizmo_navigate.c | 15 +++++++- .../blender/editors/space_image/image_edit.c | 6 ++-- .../editors/space_sequencer/space_sequencer.c | 4 ++- .../editors/transform/transform_gizmo_2d.c | 16 ++++++++- source/blender/makesdna/DNA_space_types.h | 16 +++++++-- source/blender/makesrna/intern/rna_space.c | 21 +++++++++++ 9 files changed, 117 insertions(+), 13 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 7152069972d..16cfb1e4760 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2826,7 +2826,6 @@ def km_sequencerpreview(params): value=params.select_mouse_value_fallback, legacy=params.legacy, ), - op_menu_pie("SEQUENCER_MT_pivot_pie", {"type": 'PERIOD', "value": 'PRESS'}), ("sequencer.view_all_preview", {"type": 'HOME', "value": 'PRESS'}, None), ("sequencer.view_all_preview", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None), @@ -2864,6 +2863,16 @@ def km_sequencerpreview(params): *_template_items_context_menu("SEQUENCER_MT_preview_context_menu", params.context_menu_event), ]) + if not params.legacy: + # New pie menus. + items.extend([ + ("wm.context_toggle", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True}, + {"properties": [("data_path", 'space_data.show_gizmo')]}), + op_menu_pie("SEQUENCER_MT_pivot_pie", {"type": 'PERIOD', "value": 'PRESS'}), + ("wm.context_toggle", {"type": 'Z', "value": 'PRESS', "alt": True, "shift": True}, + {"properties": [("data_path", "space_data.overlay.show_overlays")]}), + ]) + # 2D cursor. if params.cursor_tweak_event: items.extend([ diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 58afb6d9203..665e7c54fa7 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -205,6 +205,17 @@ class SEQUENCER_HT_header(Header): layout.prop(st, "display_mode", text="", icon_only=True) layout.prop(st, "preview_channels", text="", icon_only=True) + # Gizmo toggle & popover. + row = layout.row(align=True) + # FIXME: place-holder icon. + row.prop(st, "show_gizmo", text="", toggle=True, icon='GIZMO') + sub = row.row(align=True) + sub.active = st.show_gizmo + sub.popover( + panel="SEQUENCER_PT_gizmo_display", + text="", + ) + row = layout.row(align=True) row.prop(st, "show_strip_overlay", text="", icon='OVERLAY') sub = row.row(align=True) @@ -230,6 +241,29 @@ class SEQUENCER_MT_editor_menus(Menu): layout.menu("SEQUENCER_MT_strip") +class SEQUENCER_PT_gizmo_display(Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'HEADER' + bl_label = "Gizmo" + bl_ui_units_x = 8 + + def draw(self, context): + layout = self.layout + + scene = context.scene + st = context.space_data + + col = layout.column() + col.label(text="Viewport Gizmos") + col.separator() + + col.active = st.show_gizmo + colsub = col.column() + colsub.prop(st, "show_gizmo_navigate", text="Navigate") + colsub.prop(st, "show_gizmo_tool", text="Active Tools") + # colsub.prop(st, "show_gizmo_context", text="Active Object") # Currently unused. + + class SEQUENCER_PT_overlay(Panel): bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'HEADER' @@ -2503,6 +2537,7 @@ classes = ( SEQUENCER_PT_active_tool, SEQUENCER_PT_strip, + SEQUENCER_PT_gizmo_display, SEQUENCER_PT_overlay, SEQUENCER_PT_preview_overlay, SEQUENCER_PT_sequencer_overlay, diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index 9532035a1cd..f0e8f7f0a39 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -112,9 +112,9 @@ void ED_image_point_pos__reverse(struct SpaceImage *sima, float r_co[2]); bool ED_image_slot_cycle(struct Image *image, int direction); -bool ED_space_image_show_render(struct SpaceImage *sima); -bool ED_space_image_show_paint(struct SpaceImage *sima); -bool ED_space_image_show_uvedit(struct SpaceImage *sima, struct Object *obedit); +bool ED_space_image_show_render(const struct SpaceImage *sima); +bool ED_space_image_show_paint(const struct SpaceImage *sima); +bool ED_space_image_show_uvedit(const struct SpaceImage *sima, struct Object *obedit); bool ED_space_image_paint_curve(const struct bContext *C); diff --git a/source/blender/editors/interface/view2d_gizmo_navigate.c b/source/blender/editors/interface/view2d_gizmo_navigate.c index 30b4a7c097a..3ff5b471731 100644 --- a/source/blender/editors/interface/view2d_gizmo_navigate.c +++ b/source/blender/editors/interface/view2d_gizmo_navigate.c @@ -127,11 +127,24 @@ struct NavigateWidgetGroup { int region_size[2]; }; -static bool WIDGETGROUP_navigate_poll(const bContext *UNUSED(C), wmGizmoGroupType *UNUSED(gzgt)) +static bool WIDGETGROUP_navigate_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt)) { if ((U.uiflag & USER_SHOW_GIZMO_NAVIGATE) == 0) { return false; } + ScrArea *area = CTX_wm_area(C); + if (area == NULL) { + return false; + } + switch (area->spacetype) { + case SPACE_SEQ: { + const SpaceSeq *sseq = area->spacedata.first; + if (sseq->gizmo_flag & (SEQ_GIZMO_HIDE | SEQ_GIZMO_HIDE_NAVIGATE)) { + return false; + } + break; + } + } return true; } diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 2174a4b9dc1..9081f0dfcf3 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -445,12 +445,12 @@ void ED_space_image_scopes_update(const struct bContext *C, &scene->display_settings); } -bool ED_space_image_show_render(SpaceImage *sima) +bool ED_space_image_show_render(const SpaceImage *sima) { return (sima->image && ELEM(sima->image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)); } -bool ED_space_image_show_paint(SpaceImage *sima) +bool ED_space_image_show_paint(const SpaceImage *sima) { if (ED_space_image_show_render(sima)) { return false; @@ -459,7 +459,7 @@ bool ED_space_image_show_paint(SpaceImage *sima) return (sima->mode == SI_MODE_PAINT); } -bool ED_space_image_show_uvedit(SpaceImage *sima, Object *obedit) +bool ED_space_image_show_uvedit(const SpaceImage *sima, Object *obedit) { if (sima) { if (ED_space_image_show_render(sima)) { diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 4c4c908ea3c..978ac9a3404 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -816,7 +816,9 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) DRW_draw_cursor_2d_ex(region, cursor_pixel); } - WM_gizmomap_draw(region->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); + if ((sseq->gizmo_flag & SEQ_GIZMO_HIDE) == 0) { + WM_gizmomap_draw(region->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); + } if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(wm)) { const rcti *rect = ED_region_visible_rect(region); diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index b25a182926e..a0ca7b84c48 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -76,13 +76,27 @@ static bool gizmo2d_generic_poll(const bContext *C, wmGizmoGroupType *gzgt) } ScrArea *area = CTX_wm_area(C); + if (area == NULL) { + return false; + } + + /* NOTE: below this is assumed to be a tool gizmo. + * If there are cases that need to check other flags - this function could be split. */ switch (area->spacetype) { case SPACE_IMAGE: { - SpaceImage *sima = area->spacedata.first; + const SpaceImage *sima = area->spacedata.first; Object *obedit = CTX_data_edit_object(C); if (!ED_space_image_show_uvedit(sima, obedit)) { return false; } + break; + } + case SPACE_SEQ: { + const SpaceSeq *sseq = area->spacedata.first; + if (sseq->gizmo_flag & (SEQ_GIZMO_HIDE | SEQ_GIZMO_HIDE_TOOL)) { + return false; + } + break; } } diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index f6ce71dad54..ac55fa0df01 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -641,10 +641,11 @@ typedef struct SpaceSeq { /** Deprecated, handled by View2D now. */ float zoom DNA_DEPRECATED; /** See SEQ_VIEW_* below. */ - int view; - int overlay_type; + char view; + char overlay_type; /** Overlay an image of the editing on below the strips. */ - int draw_flag; + char draw_flag; + char gizmo_flag; char _pad[4]; /** 2D cursor for transform. */ @@ -729,6 +730,15 @@ typedef struct MaskSpaceInfo { char _pad3[5]; } MaskSpaceInfo; +/** #SpaceSeq.gizmo_flag */ +enum { + /** All gizmos. */ + SEQ_GIZMO_HIDE = (1 << 0), + SEQ_GIZMO_HIDE_NAVIGATE = (1 << 1), + SEQ_GIZMO_HIDE_CONTEXT = (1 << 2), + SEQ_GIZMO_HIDE_TOOL = (1 << 3), +}; + /* SpaceSeq.mainb */ typedef enum eSpaceSeq_OverlayType { SEQ_DRAW_OVERLAY_RECT = 0, diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 9d8e06b3c56..fcaf53da81b 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5670,6 +5670,27 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Transform Preview", "Show preview of the transformed frames"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + /* Gizmo toggles. */ + prop = RNA_def_property(srna, "show_gizmo", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SEQ_GIZMO_HIDE); + RNA_def_property_ui_text(prop, "Show Gizmo", "Show gizmos of all types"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_gizmo_navigate", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SEQ_GIZMO_HIDE_NAVIGATE); + RNA_def_property_ui_text(prop, "Navigate Gizmo", "Viewport navigation gizmo"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_gizmo_context", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SEQ_GIZMO_HIDE_CONTEXT); + RNA_def_property_ui_text(prop, "Context Gizmo", "Context sensitive gizmos for the active item"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_gizmo_tool", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SEQ_GIZMO_HIDE_TOOL); + RNA_def_property_ui_text(prop, "Tool Gizmo", "Active tool gizmo"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + /* Overlay settings. */ prop = RNA_def_property(srna, "show_strip_overlay", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_OVERLAY); From 415098abc353947ed7d87eab3825113c5d0f8118 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 17:22:10 +1100 Subject: [PATCH 0623/1500] Fix crash switching scenes with sequencer gizmos displayed --- source/blender/editors/transform/transform_gizmo_2d.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index a0ca7b84c48..61119c72ad6 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -96,6 +96,11 @@ static bool gizmo2d_generic_poll(const bContext *C, wmGizmoGroupType *gzgt) if (sseq->gizmo_flag & (SEQ_GIZMO_HIDE | SEQ_GIZMO_HIDE_TOOL)) { return false; } + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + if (ed == NULL) { + return false; + } break; } } From 741fb0d6c9a790b13c622e10ff6ef7ece48784eb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 18:06:50 +1100 Subject: [PATCH 0624/1500] Sequencer: hide gizmos & cursor during scrubbing & playback This was distracting prevented easily viewing an animation. --- source/blender/editors/space_sequencer/space_sequencer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 978ac9a3404..87344a38c26 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -783,6 +783,7 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) wmWindowManager *wm = CTX_wm_manager(C); const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && (sseq->flag & SEQ_SHOW_OVERLAY)); + const bool is_playing = ED_screen_animation_playing(wm); if (!draw_overlay || sseq->overlay_type != SEQ_DRAW_OVERLAY_REFERENCE) { sequencer_draw_preview(C, scene, region, sseq, scene->r.cfra, 0, false, false); @@ -805,7 +806,7 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) } /* No need to show the cursor for scopes. */ - if (draw_overlay && (sseq->mainb == SEQ_DRAW_IMG_IMBUF)) { + if (draw_overlay && (is_playing == false) && (sseq->mainb == SEQ_DRAW_IMG_IMBUF)) { GPU_color_mask(true, true, true, true); GPU_depth_mask(false); GPU_depth_test(GPU_DEPTH_NONE); @@ -816,7 +817,7 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) DRW_draw_cursor_2d_ex(region, cursor_pixel); } - if ((sseq->gizmo_flag & SEQ_GIZMO_HIDE) == 0) { + if ((is_playing == false) && (sseq->gizmo_flag & SEQ_GIZMO_HIDE) == 0) { WM_gizmomap_draw(region->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); } From 6c11733dfbce897fa390465caa5de4c5b0e1904d Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 8 Oct 2021 09:29:15 +0200 Subject: [PATCH 0625/1500] Fix T91190: Remove gaps operator not working Caused by mistake in f49d438ced7c - `SEQ_query_all_strips()` is not meant to be recursive. --- source/blender/sequencer/intern/iterator.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index e83d158881e..a12a5cbdc61 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -322,7 +322,9 @@ SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase) SeqCollection *SEQ_query_all_strips(ListBase *seqbase) { SeqCollection *collection = SEQ_collection_create(__func__); - query_all_strips_recursive(seqbase, collection); + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + SEQ_collection_append_strip(seq, collection); + } return collection; } From 3284b5bbde3fb203a1071da117e7796a0443d97e Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 8 Oct 2021 09:55:29 +0200 Subject: [PATCH 0626/1500] Cleanup: Else after return in Cycles --- intern/cycles/blender/blender_output_driver.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/intern/cycles/blender/blender_output_driver.cpp b/intern/cycles/blender/blender_output_driver.cpp index f380b7b3bb1..2f2844d4820 100644 --- a/intern/cycles/blender/blender_output_driver.cpp +++ b/intern/cycles/blender/blender_output_driver.cpp @@ -72,15 +72,14 @@ bool BlenderOutputDriver::update_render_tile(const Tile &tile) write_render_tile(tile); return true; } - else { - /* Don't highlight full-frame tile. */ - if (!(tile.size == tile.full_size)) { - b_engine_.tile_highlight_clear_all(); - b_engine_.tile_highlight_set(tile.offset.x, tile.offset.y, tile.size.x, tile.size.y, true); - } - return false; + /* Don't highlight full-frame tile. */ + if (!(tile.size == tile.full_size)) { + b_engine_.tile_highlight_clear_all(); + b_engine_.tile_highlight_set(tile.offset.x, tile.offset.y, tile.size.x, tile.size.y, true); } + + return false; } void BlenderOutputDriver::write_render_tile(const Tile &tile) From bd65d3ce97e075fe12aec9f31721c68377d31a9e Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 8 Oct 2021 10:00:45 +0200 Subject: [PATCH 0627/1500] Cleanup: Explicit specifier for single argument constructor --- intern/cycles/blender/blender_output_driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/blender_output_driver.h b/intern/cycles/blender/blender_output_driver.h index 8a1cf92d7c7..0852cba1b34 100644 --- a/intern/cycles/blender/blender_output_driver.h +++ b/intern/cycles/blender/blender_output_driver.h @@ -26,7 +26,7 @@ CCL_NAMESPACE_BEGIN class BlenderOutputDriver : public OutputDriver { public: - BlenderOutputDriver(BL::RenderEngine &b_engine); + explicit BlenderOutputDriver(BL::RenderEngine &b_engine); ~BlenderOutputDriver(); virtual void write_render_tile(const Tile &tile) override; From a3e2cc0bb7fe47fa1122cd19c4fa8a5aa59f761c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 8 Oct 2021 11:32:22 +0200 Subject: [PATCH 0628/1500] Asset Catalogs: Fix unit test on Windows Force native slashes before comparing paths; on Windows mixing separators caused a unit test to fail. No functional changes. --- source/blender/blenkernel/intern/asset_catalog_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index fe7222920ab..dc39cefed5a 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -565,8 +565,8 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_me { const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; - const CatalogFilePath writable_cdf_file = target_dir + - AssetCatalogService::DEFAULT_CATALOG_FILENAME; + CatalogFilePath writable_cdf_file = target_dir + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + BLI_path_slash_native(writable_cdf_file.data()); ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); /* Create the catalog service without loading the already-existing CDF. */ From 482806c81678e351ff171c68a757386a5b2d4676 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Fri, 8 Oct 2021 12:09:27 +0200 Subject: [PATCH 0629/1500] VSE: Implement the bounding box (xform) tool in the seq preview window Make the "xform" tool/gizmo available for strip transformations in the sequencer preview window. Because of the amount of hacks needed to make the gizmo work nicely with multiple strips at the same time, it was decided to only show the translate gizmo when multiple strips are selected. This is because the transforms with multiple strips would appear buggy because of our lack of shearing support in the transform system. There is also currently no way to properly sync the gizmo drawing with the transform when using multiple strips. Reviewed By: Richard Antalik, Campbell Barton Differential Revision: http://developer.blender.org/D12729 --- .../startup/bl_ui/space_toolsystem_toolbar.py | 19 +- .../gizmo_library/gizmo_types/cage2d_gizmo.c | 227 +----------- .../editors/space_sequencer/sequencer_draw.c | 5 + source/blender/editors/transform/transform.c | 19 + .../editors/transform/transform_constraints.c | 12 +- .../transform_convert_sequencer_image.c | 30 +- .../editors/transform/transform_generics.c | 62 ++-- .../editors/transform/transform_gizmo_2d.c | 328 +++++++++++++----- .../editors/transform/transform_mode.c | 12 +- .../editors/transform/transform_mode.h | 2 +- .../editors/transform/transform_mode_resize.c | 35 +- .../transform/transform_mode_shrink_fatten.c | 4 +- .../blender/editors/transform/transform_ops.c | 14 + .../transform/transform_orientations.c | 13 + source/blender/sequencer/SEQ_transform.h | 4 + .../sequencer/intern/strip_transform.c | 54 ++- 16 files changed, 475 insertions(+), 365 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 008129cd389..9b1b401bc5b 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2515,6 +2515,18 @@ class _defs_sequencer_generic: keymap="Sequencer Tool: Scale", ) + @ToolDef.from_fn + def transform(): + return dict( + idname="builtin.transform", + label="Transform", + description=( + "Supports any combination of grab, rotate, and scale at once" + ), + icon="ops.transform.transform", + widget="SEQUENCER_GGT_gizmo2d", + # No keymap default action, only for gizmo! + ) class _defs_sequencer_select: @ToolDef.from_fn @@ -3112,6 +3124,8 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_sequencer_generic.translate, _defs_sequencer_generic.rotate, _defs_sequencer_generic.scale, + _defs_sequencer_generic.transform, + None, _defs_sequencer_generic.sample, *_tools_annotate, ], @@ -3126,9 +3140,12 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_sequencer_generic.translate, _defs_sequencer_generic.rotate, _defs_sequencer_generic.scale, - _defs_sequencer_generic.blade, + _defs_sequencer_generic.transform, + None, _defs_sequencer_generic.sample, *_tools_annotate, + None, + _defs_sequencer_generic.blade, ], } diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c index 6fd06b47656..08dbdd021d3 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -98,64 +98,12 @@ static bool gizmo_calc_rect_view_margin(const wmGizmo *gz, const float dims[2], zero_v2(margin); return false; } + margin[0] = ((handle_size * scale_xy[0])); margin[1] = ((handle_size * scale_xy[1])); return true; } -/* -------------------------------------------------------------------- */ - -static void gizmo_rect_pivot_from_scale_part(int part, float r_pt[2], bool r_constrain_axis[2]) -{ - bool x = true, y = true; - switch (part) { - case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X: { - ARRAY_SET_ITEMS(r_pt, 0.5, 0.0); - x = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X: { - ARRAY_SET_ITEMS(r_pt, -0.5, 0.0); - x = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y: { - ARRAY_SET_ITEMS(r_pt, 0.0, 0.5); - y = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y: { - ARRAY_SET_ITEMS(r_pt, 0.0, -0.5); - y = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y: { - ARRAY_SET_ITEMS(r_pt, 0.5, 0.5); - x = y = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y: { - ARRAY_SET_ITEMS(r_pt, 0.5, -0.5); - x = y = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y: { - ARRAY_SET_ITEMS(r_pt, -0.5, 0.5); - x = y = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y: { - ARRAY_SET_ITEMS(r_pt, -0.5, -0.5); - x = y = false; - break; - } - default: - BLI_assert(0); - } - r_constrain_axis[0] = x; - r_constrain_axis[1] = y; -} - /* -------------------------------------------------------------------- */ /** \name Box Draw Style * @@ -400,6 +348,7 @@ static void cage2d_draw_box_interaction(const float color[4], ARRAY_SET_ITEMS(verts[1], r_rotate.xmin, r_rotate.ymax); ARRAY_SET_ITEMS(verts[2], r_rotate.xmax, r_rotate.ymax); ARRAY_SET_ITEMS(verts[3], r_rotate.xmax, r_rotate.ymin); + verts_len = 4; if (is_solid) { prim_type = GPU_PRIM_TRI_FAN; @@ -769,10 +718,10 @@ static int gizmo_cage2d_get_cursor(wmGizmo *gz) return WM_CURSOR_NSEW_SCROLL; case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X: case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X: - return WM_CURSOR_X_MOVE; + return WM_CURSOR_NSEW_SCROLL; case ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y: case ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y: - return WM_CURSOR_Y_MOVE; + return WM_CURSOR_NSEW_SCROLL; /* TODO: diagonal cursor. */ case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y: @@ -937,173 +886,11 @@ static int gizmo_cage2d_invoke(bContext *C, wmGizmo *gz, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -static int gizmo_cage2d_modal(bContext *C, - wmGizmo *gz, - const wmEvent *event, +static int gizmo_cage2d_modal(bContext *UNUSED(C), + wmGizmo *UNUSED(gz), + const wmEvent *UNUSED(event), eWM_GizmoFlagTweak UNUSED(tweak_flag)) { - if (event->type != MOUSEMOVE) { - return OPERATOR_RUNNING_MODAL; - } - /* For transform logic to be manageable we operate in -0.5..0.5 2D space, - * no matter the size of the rectangle, mouse coords are scaled to unit space. - * The mouse coords have been projected into the matrix - * so we don't need to worry about axis alignment. - * - * - The cursor offset are multiplied by 'dims'. - * - Matrix translation is also multiplied by 'dims'. - */ - RectTransformInteraction *data = gz->interaction_data; - float point_local[2]; - - float dims[2]; - RNA_float_get_array(gz->ptr, "dimensions", dims); - - { - float matrix_back[4][4]; - copy_m4_m4(matrix_back, gz->matrix_offset); - copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset); - - bool ok = gizmo_window_project_2d( - C, gz, (const float[2]){UNPACK2(event->mval)}, 2, false, point_local); - copy_m4_m4(gz->matrix_offset, matrix_back); - if (!ok) { - return OPERATOR_RUNNING_MODAL; - } - } - - const int transform_flag = RNA_enum_get(gz->ptr, "transform"); - wmGizmoProperty *gz_prop; - - gz_prop = WM_gizmo_target_property_find(gz, "matrix"); - if (gz_prop->type != NULL) { - WM_gizmo_target_property_float_get_array(gz, gz_prop, &gz->matrix_offset[0][0]); - } - - if (gz->highlight_part == ED_GIZMO_CAGE2D_PART_TRANSLATE) { - /* do this to prevent clamping from changing size */ - copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset); - gz->matrix_offset[3][0] = data->orig_matrix_offset[3][0] + - (point_local[0] - data->orig_mouse[0]); - gz->matrix_offset[3][1] = data->orig_matrix_offset[3][1] + - (point_local[1] - data->orig_mouse[1]); - } - else if (gz->highlight_part == ED_GIZMO_CAGE2D_PART_ROTATE) { - -#define MUL_V2_V3_M4_FINAL(test_co, mouse_co) \ - mul_v3_m4v3( \ - test_co, data->orig_matrix_final_no_offset, ((const float[3]){UNPACK2(mouse_co), 0.0})) - - float test_co[3]; - - if (data->dial == NULL) { - MUL_V2_V3_M4_FINAL(test_co, data->orig_matrix_offset[3]); - - data->dial = BLI_dial_init(test_co, FLT_EPSILON); - - MUL_V2_V3_M4_FINAL(test_co, data->orig_mouse); - BLI_dial_angle(data->dial, test_co); - } - - /* rotate */ - MUL_V2_V3_M4_FINAL(test_co, point_local); - const float angle = BLI_dial_angle(data->dial, test_co); - - float matrix_space_inv[4][4]; - float matrix_rotate[4][4]; - float pivot[3]; - - copy_v3_v3(pivot, data->orig_matrix_offset[3]); - - invert_m4_m4(matrix_space_inv, gz->matrix_space); - - unit_m4(matrix_rotate); - mul_m4_m4m4(matrix_rotate, matrix_rotate, matrix_space_inv); - rotate_m4(matrix_rotate, 'Z', -angle); - mul_m4_m4m4(matrix_rotate, matrix_rotate, gz->matrix_space); - - zero_v3(matrix_rotate[3]); - transform_pivot_set_m4(matrix_rotate, pivot); - - mul_m4_m4m4(gz->matrix_offset, matrix_rotate, data->orig_matrix_offset); - -#undef MUL_V2_V3_M4_FINAL - } - else { - /* scale */ - copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset); - float pivot[2]; - bool constrain_axis[2] = {false}; - - if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) { - gizmo_rect_pivot_from_scale_part(gz->highlight_part, pivot, constrain_axis); - } - else { - zero_v2(pivot); - } - - /* Cursor deltas scaled to (-0.5..0.5). */ - float delta_orig[2], delta_curr[2]; - for (int i = 0; i < 2; i++) { - delta_orig[i] = ((data->orig_mouse[i] - data->orig_matrix_offset[3][i]) / dims[i]) - - pivot[i]; - delta_curr[i] = ((point_local[i] - data->orig_matrix_offset[3][i]) / dims[i]) - pivot[i]; - } - - float scale[2] = {1.0f, 1.0f}; - for (int i = 0; i < 2; i++) { - if (constrain_axis[i] == false) { - if (delta_orig[i] < 0.0f) { - delta_orig[i] *= -1.0f; - delta_curr[i] *= -1.0f; - } - const int sign = signum_i(scale[i]); - - scale[i] = 1.0f + ((delta_curr[i] - delta_orig[i]) / len_v3(data->orig_matrix_offset[i])); - - if ((transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_SIGNED) == 0) { - if (sign != signum_i(scale[i])) { - scale[i] = 0.0f; - } - } - } - } - - if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_UNIFORM) { - if (constrain_axis[0] == false && constrain_axis[1] == false) { - scale[1] = scale[0] = (scale[1] + scale[0]) / 2.0f; - } - else if (constrain_axis[0] == false) { - scale[1] = scale[0]; - } - else if (constrain_axis[1] == false) { - scale[0] = scale[1]; - } - else { - BLI_assert(0); - } - } - - /* scale around pivot */ - float matrix_scale[4][4]; - unit_m4(matrix_scale); - - mul_v3_fl(matrix_scale[0], scale[0]); - mul_v3_fl(matrix_scale[1], scale[1]); - - transform_pivot_set_m4(matrix_scale, - (const float[3]){pivot[0] * dims[0], pivot[1] * dims[1], 0.0f}); - mul_m4_m4m4(gz->matrix_offset, data->orig_matrix_offset, matrix_scale); - } - - if (gz_prop->type != NULL) { - WM_gizmo_target_property_float_set_array(C, gz, gz_prop, &gz->matrix_offset[0][0]); - } - - /* tag the region for redraw */ - ED_region_tag_redraw_editor_overlays(CTX_wm_region(C)); - WM_event_add_mousemove(CTX_wm_window(C)); - return OPERATOR_RUNNING_MODAL; } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index e5e241c8b7e..6e4c3c774a2 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -2250,6 +2250,11 @@ void sequencer_draw_preview(const bContext *C, sequencer_draw_maskedit(C, scene, region, sseq); #endif + /* Draw registered callbacks. */ + GPU_framebuffer_bind(framebuffer_overlay); + ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW); + GPU_framebuffer_bind_no_srgb(framebuffer_overlay); + /* Scope is freed in sequencer_check_scopes when `ibuf` changes and redraw is needed. */ if (ibuf) { IMB_freeImBuf(ibuf); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 9dcd3a3e510..ec3847acbce 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -797,6 +797,25 @@ static bool transform_event_modal_constraint(TransInfo *t, short modal_type) if (constraint_new == CON_AXIS2) { return false; } + + if (t->data_type == TC_SEQ_IMAGE_DATA) { + /* Setup the 2d msg string so it writes out the transform space. */ + msg_2d = msg_3d; + + short orient_index = 1; + if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) { + /* Successive presses on existing axis, cycle orientation modes. */ + orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient)); + } + + transform_orientations_current_set(t, orient_index); + if (orient_index != 0) { + /* Make sure that we don't stop the constraint unless we are looped back around to + * "no constraint". */ + constraint_curr = -1; + } + } + if (constraint_curr == constraint_new) { stopConstraint(t); } diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index e2ad89e0dbc..23ba335476c 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -755,7 +755,7 @@ void drawConstraint(TransInfo *t) { TransCon *tc = &(t->con); - if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) { + if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE, SPACE_SEQ)) { return; } if (!(tc->mode & CON_APPLY)) { @@ -921,6 +921,16 @@ static void drawObjectConstraint(TransInfo *t) } } + if (t->options & CTX_SEQUENCER_IMAGE) { + /* Because we construct an "L" shape to deform the sequence, we should skip + * all points except the first vertex. Otherwise we will draw the same axis constraint line + * 3 times for each strip. + */ + if (i % 3 != 0) { + continue; + } + } + if (t->flag & T_EDIT) { mul_v3_m4v3(co, tc->mat, td->center); diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index 6e3f12de472..2b5ed268504 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -64,12 +64,16 @@ static TransData *SeqToTransData(const Scene *scene, SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin); float vertex[2] = {origin[0], origin[1]}; - /* Add control vertex, so rotation and scale can be calculated. */ + /* Add control vertex, so rotation and scale can be calculated. + * All three vertices will form a "L" shape that is aligned to the local strip axis. + */ if (vert_index == 1) { - vertex[0] += 1.0f; + vertex[0] += cosf(transform->rotation); + vertex[1] += sinf(transform->rotation); } else if (vert_index == 2) { - vertex[1] += 1.0f; + vertex[0] -= sinf(transform->rotation); + vertex[1] += cosf(transform->rotation); } td2d->loc[0] = vertex[0]; @@ -81,10 +85,12 @@ static TransData *SeqToTransData(const Scene *scene, td->center[0] = origin[0]; td->center[1] = origin[1]; - memset(td->axismtx, 0, sizeof(td->axismtx)); - td->axismtx[2][2] = 1.0f; unit_m3(td->mtx); unit_m3(td->smtx); + unit_m3(td->axismtx); + + rotate_m3(td->axismtx, transform->rotation); + normalize_m3(td->axismtx); tdseq->seq = seq; copy_v2_v2(tdseq->orig_origin_position, origin); @@ -159,18 +165,18 @@ void recalcData_sequencer_image(TransInfo *t) for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) { /* Origin. */ - float loc[2]; - copy_v2_v2(loc, td2d->loc); + float origin[2]; + copy_v2_v2(origin, td2d->loc); i++, td++, td2d++; /* X and Y control points used to read scale and rotation. */ float handle_x[2]; copy_v2_v2(handle_x, td2d->loc); - sub_v2_v2(handle_x, loc); + sub_v2_v2(handle_x, origin); i++, td++, td2d++; float handle_y[2]; copy_v2_v2(handle_y, td2d->loc); - sub_v2_v2(handle_y, loc); + sub_v2_v2(handle_y, origin); TransDataSeq *tdseq = td->extra; Sequence *seq = tdseq->seq; @@ -181,8 +187,9 @@ void recalcData_sequencer_image(TransInfo *t) /* Calculate translation. */ float translation[2]; copy_v2_v2(translation, tdseq->orig_origin_position); - sub_v2_v2(translation, loc); + sub_v2_v2(translation, origin); mul_v2_v2(translation, mirror); + transform->xofs = tdseq->orig_translation[0] - translation[0]; transform->yofs = tdseq->orig_translation[1] - translation[1]; @@ -192,7 +199,8 @@ void recalcData_sequencer_image(TransInfo *t) /* Rotation. Scaling can cause negative rotation. */ if (t->mode == TFM_ROTATION) { - float rotation = angle_signed_v2v2(handle_x, (float[]){1, 0}) * mirror[0] * mirror[1]; + const float orig_dir[2] = {cosf(tdseq->orig_rotation), sinf(tdseq->orig_rotation)}; + float rotation = angle_signed_v2v2(handle_x, orig_dir) * mirror[0] * mirror[1]; transform->rotation = tdseq->orig_rotation + rotation; transform->rotation += DEG2RAD(360.0); transform->rotation = fmod(transform->rotation, DEG2RAD(360.0)); diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 09c338046ed..8effb82173b 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -73,42 +73,56 @@ void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis, short options) { + if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_SEQ)) { + return; + } + float v1[3], v2[3], v3[3]; uchar col[3], col2[3]; if (t->spacetype == SPACE_VIEW3D) { View3D *v3d = t->view; - GPU_matrix_push(); - copy_v3_v3(v3, dir); mul_v3_fl(v3, v3d->clip_end); sub_v3_v3v3(v2, center, v3); add_v3_v3v3(v1, center, v3); - - if (options & DRAWLIGHT) { - col[0] = col[1] = col[2] = 220; - } - else { - UI_GetThemeColor3ubv(TH_GRID, col); - } - UI_make_axis_color(col, col2, axis); - - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformColor3ubv(col2); - - immBegin(GPU_PRIM_LINES, 2); - immVertex3fv(pos, v1); - immVertex3fv(pos, v2); - immEnd(); - - immUnbindProgram(); - - GPU_matrix_pop(); } + else if (t->spacetype == SPACE_SEQ) { + View2D *v2d = t->view; + + copy_v3_v3(v3, dir); + float max_dist = max_ff(BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)); + mul_v3_fl(v3, max_dist); + + sub_v3_v3v3(v2, center, v3); + add_v3_v3v3(v1, center, v3); + } + + GPU_matrix_push(); + + if (options & DRAWLIGHT) { + col[0] = col[1] = col[2] = 220; + } + else { + UI_GetThemeColor3ubv(TH_GRID, col); + } + UI_make_axis_color(col, col2, axis); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor3ubv(col2); + + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + immEnd(); + + immUnbindProgram(); + + GPU_matrix_pop(); } /** diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 61119c72ad6..7ba52ec823d 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -173,6 +173,7 @@ typedef struct GizmoGroup2D { float origin[2]; float min[2]; float max[2]; + float rotation; bool no_cage; @@ -241,7 +242,7 @@ static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min } ScrArea *area = CTX_wm_area(C); - bool changed = false; + bool has_select = false; if (area->spacetype == SPACE_IMAGE) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -249,18 +250,75 @@ static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, NULL, &objects_len); if (ED_uvedit_minmax_multi(scene, objects, objects_len, r_min, r_max)) { - changed = true; + has_select = true; } MEM_freeN(objects); } + else if (area->spacetype == SPACE_SEQ) { + Scene *scene = CTX_data_scene(C); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + int selected_strips = SEQ_collection_len(strips); + if (selected_strips > 0) { + INIT_MINMAX2(r_min, r_max); + has_select = true; - if (changed == false) { + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + float quad[4][2]; + SEQ_image_transform_quad_get(scene, seq, selected_strips != 1, quad); + for (int i = 0; i < 4; i++) { + minmax_v2v2_v2(r_min, r_max, quad[i]); + } + } + } + SEQ_collection_free(strips); + if (selected_strips > 1) { + /* Don't draw the cage as transforming multiple strips isn't currently very useful as it + * doesn't behave as one would expect. + * + * This is because our current transform system doesn't support shearing which would make the + * scaling transforms of the bounding box behave weirdly. + * In addition to this, the rotation of the bounding box can not currently be hooked up + * properly to read the result from the transform system (when transforming multiple strips). + */ + mid_v2_v2v2(r_center, r_min, r_max); + zero_v2(r_min); + zero_v2(r_max); + return has_select; + } + } + + if (has_select == false) { zero_v2(r_min); zero_v2(r_max); } mid_v2_v2v2(r_center, r_min, r_max); - return changed; + return has_select; +} + +static int gizmo2d_calc_transform_orientation(const bContext *C) +{ + ScrArea *area = CTX_wm_area(C); + if (area->spacetype != SPACE_SEQ) { + return V3D_ORIENT_GLOBAL; + } + + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + + bool use_local_orient = SEQ_collection_len(strips) == 1; + SEQ_collection_free(strips); + + if (use_local_orient) { + return V3D_ORIENT_LOCAL; + } + return V3D_ORIENT_GLOBAL; } static float gizmo2d_calc_rotation(const bContext *C) @@ -276,9 +334,10 @@ static float gizmo2d_calc_rotation(const bContext *C) SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); SEQ_filter_selected_strips(strips); - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, strips) { - if (seq == ed->act_seq) { + if (SEQ_collection_len(strips) == 1) { + /* Only return the strip rotation if only one is selected. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { StripTransform *transform = seq->strip->transform; float mirror[2]; SEQ_image_transform_mirror_factor_get(seq, mirror); @@ -436,17 +495,17 @@ static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X, ot_resize, NULL); PropertyRNA *prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm"); PropertyRNA *prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis"); - RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x); RNA_property_boolean_set(ptr, prop_release_confirm, true); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x); ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X, ot_resize, NULL); + RNA_property_boolean_set(ptr, prop_release_confirm, true); RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x); - RNA_property_boolean_set(ptr, prop_release_confirm, true); ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y, ot_resize, NULL); - RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y); RNA_property_boolean_set(ptr, prop_release_confirm, true); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y); ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y, ot_resize, NULL); - RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y); RNA_property_boolean_set(ptr, prop_release_confirm, true); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y); ptr = WM_gizmo_operator_set( ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y, ot_resize, NULL); @@ -465,87 +524,51 @@ static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup } } -static void gizmo2d_xform_setup_no_cage(const bContext *C, wmGizmoGroup *gzgroup) +static void rotate_around_center_v2(float point[2], const float center[2], const float angle) { - gizmo2d_xform_setup(C, gzgroup); - GizmoGroup2D *ggd = gzgroup->customdata; - ggd->no_cage = true; + float tmp[2]; + + sub_v2_v2v2(tmp, point, center); + rotate_v2_v2fl(point, tmp, angle); + add_v2_v2(point, center); } static void gizmo2d_xform_refresh(const bContext *C, wmGizmoGroup *gzgroup) { GizmoGroup2D *ggd = gzgroup->customdata; - float origin[3]; bool has_select; if (ggd->no_cage) { - has_select = gizmo2d_calc_center(C, origin); + has_select = gizmo2d_calc_center(C, ggd->origin); } else { - has_select = gizmo2d_calc_bounds(C, origin, ggd->min, ggd->max); + has_select = gizmo2d_calc_bounds(C, ggd->origin, ggd->min, ggd->max); + ggd->rotation = gizmo2d_calc_rotation(C); } - copy_v2_v2(ggd->origin, origin); + bool show_cage = !ggd->no_cage && !equals_v2v2(ggd->min, ggd->max); if (has_select == false) { + /* Nothing selected. Disable gizmo drawing and return. */ + ggd->cage->flag |= WM_GIZMO_HIDDEN; for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { ggd->translate_xy[i]->flag |= WM_GIZMO_HIDDEN; } - ggd->cage->flag |= WM_GIZMO_HIDDEN; + return; } - else { - if (show_cage) { - ggd->cage->flag &= ~WM_GIZMO_HIDDEN; - for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { - wmGizmo *gz = ggd->translate_xy[i]; - gz->flag |= WM_GIZMO_HIDDEN; - } - } - else { - ggd->cage->flag |= WM_GIZMO_HIDDEN; - for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { - wmGizmo *gz = ggd->translate_xy[i]; - gz->flag &= ~WM_GIZMO_HIDDEN; - } + + if (!show_cage) { + /* Disable cage gizmo drawing and return. */ + ggd->cage->flag |= WM_GIZMO_HIDDEN; + for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { + ggd->translate_xy[i]->flag &= ~WM_GIZMO_HIDDEN; } + return; + } - if (show_cage) { - wmGizmoOpElem *gzop; - float mid[2]; - const float *min = ggd->min; - const float *max = ggd->max; - mid_v2_v2v2(mid, min, max); - - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X); - PropertyRNA *prop_center_override = RNA_struct_find_property(&gzop->ptr, "center_override"); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){max[0], mid[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){min[0], mid[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){mid[0], max[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){mid[0], min[1], 0.0f}); - - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){max[0], max[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){max[0], min[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){min[0], max[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){min[0], min[1], 0.0f}); - - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){mid[0], mid[1], 0.0f}); - } + /* We will show the cage gizmo! Setup all necessary data. */ + ggd->cage->flag &= ~WM_GIZMO_HIDDEN; + for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { + ggd->translate_xy[i]->flag |= WM_GIZMO_HIDDEN; } } @@ -554,7 +577,6 @@ static void gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) ARegion *region = CTX_wm_region(C); GizmoGroup2D *ggd = gzgroup->customdata; float origin[3] = {UNPACK2(ggd->origin), 0.0f}; - const float origin_aa[3] = {UNPACK2(ggd->origin), 0.0f}; gizmo2d_origin_to_region(region, origin); @@ -564,9 +586,145 @@ static void gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) } UI_view2d_view_to_region_m4(®ion->v2d, ggd->cage->matrix_space); - WM_gizmo_set_matrix_offset_location(ggd->cage, origin_aa); + /* Define the bounding box of the gizmo in the offset transform matrix. */ + unit_m4(ggd->cage->matrix_offset); ggd->cage->matrix_offset[0][0] = (ggd->max[0] - ggd->min[0]); ggd->cage->matrix_offset[1][1] = (ggd->max[1] - ggd->min[1]); + + ScrArea *area = CTX_wm_area(C); + + if (area->spacetype == SPACE_SEQ) { + gizmo2d_calc_center(C, origin); + + float matrix_rotate[4][4]; + unit_m4(matrix_rotate); + copy_v3_v3(matrix_rotate[3], origin); + rotate_m4(matrix_rotate, 'Z', ggd->rotation); + unit_m4(ggd->cage->matrix_basis); + mul_m4_m4m4(ggd->cage->matrix_basis, matrix_rotate, ggd->cage->matrix_basis); + + float mid[2]; + sub_v2_v2v2(mid, origin, ggd->origin); + mul_v2_fl(mid, -1.0f); + copy_v2_v2(ggd->cage->matrix_offset[3], mid); + } + else { + const float origin_aa[3] = {UNPACK2(ggd->origin), 0.0f}; + WM_gizmo_set_matrix_offset_location(ggd->cage, origin_aa); + } +} + +static void gizmo2d_xform_invoke_prepare(const bContext *C, + wmGizmoGroup *gzgroup, + wmGizmo *UNUSED(gz), + const wmEvent *UNUSED(event)) +{ + GizmoGroup2D *ggd = gzgroup->customdata; + wmGizmoOpElem *gzop; + const float *mid = ggd->origin; + const float *min = ggd->min; + const float *max = ggd->max; + + /* Define the different transform center points that will be used when grabbing the corners or + * rotating with the gizmo. + * + * The coordinates are referred to as their cardinal directions: + * N + * o + *NW | NE + * x-----------x + * | | + *W| C |E + * | | + * x-----------x + *SW S SE + */ + float n[3] = {mid[0], max[1], 0.0f}; + float w[3] = {min[0], mid[1], 0.0f}; + float e[3] = {max[0], mid[1], 0.0f}; + float s[3] = {mid[0], min[1], 0.0f}; + + float nw[3] = {min[0], max[1], 0.0f}; + float ne[3] = {max[0], max[1], 0.0f}; + float sw[3] = {min[0], min[1], 0.0f}; + float se[3] = {max[0], min[1], 0.0f}; + + float c[3] = {mid[0], mid[1], 0.0f}; + + float orient_matrix[3][3]; + unit_m3(orient_matrix); + + ScrArea *area = CTX_wm_area(C); + + if (ggd->rotation != 0.0f && area->spacetype == SPACE_SEQ) { + float origin[3]; + gizmo2d_calc_center(C, origin); + /* We need to rotate the cardinal points so they align with the rotated bounding box. */ + + rotate_around_center_v2(n, origin, ggd->rotation); + rotate_around_center_v2(w, origin, ggd->rotation); + rotate_around_center_v2(e, origin, ggd->rotation); + rotate_around_center_v2(s, origin, ggd->rotation); + + rotate_around_center_v2(nw, origin, ggd->rotation); + rotate_around_center_v2(ne, origin, ggd->rotation); + rotate_around_center_v2(sw, origin, ggd->rotation); + rotate_around_center_v2(se, origin, ggd->rotation); + + rotate_around_center_v2(c, origin, ggd->rotation); + + rotate_m3(orient_matrix, ggd->rotation); + } + + int orient_type = gizmo2d_calc_transform_orientation(C); + + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X); + PropertyRNA *prop_center_override = RNA_struct_find_property(&gzop->ptr, "center_override"); + PropertyRNA *prop_mouse_dir = RNA_struct_find_property(&gzop->ptr, "mouse_dir_constraint"); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, e); + RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[0]); + RNA_enum_set(&gzop->ptr, "orient_type", orient_type); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, w); + RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[0]); + RNA_enum_set(&gzop->ptr, "orient_type", orient_type); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, n); + RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[1]); + RNA_enum_set(&gzop->ptr, "orient_type", orient_type); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, s); + RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[1]); + RNA_enum_set(&gzop->ptr, "orient_type", orient_type); + + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, ne); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, se); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, nw); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, sw); + + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, c); +} + +void ED_widgetgroup_gizmo2d_xform_callbacks_set(wmGizmoGroupType *gzgt) +{ + gzgt->poll = gizmo2d_generic_poll; + gzgt->setup = gizmo2d_xform_setup; + gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; + gzgt->refresh = gizmo2d_xform_refresh; + gzgt->draw_prepare = gizmo2d_xform_draw_prepare; + gzgt->invoke_prepare = gizmo2d_xform_invoke_prepare; +} + +static void gizmo2d_xform_setup_no_cage(const bContext *C, wmGizmoGroup *gzgroup) +{ + gizmo2d_xform_setup(C, gzgroup); + GizmoGroup2D *ggd = gzgroup->customdata; + ggd->no_cage = true; } static void gizmo2d_xform_no_cage_message_subscribe(const struct bContext *C, @@ -579,15 +737,6 @@ static void gizmo2d_xform_no_cage_message_subscribe(const struct bContext *C, gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, area, region); } -void ED_widgetgroup_gizmo2d_xform_callbacks_set(wmGizmoGroupType *gzgt) -{ - gzgt->poll = gizmo2d_generic_poll; - gzgt->setup = gizmo2d_xform_setup; - gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; - gzgt->refresh = gizmo2d_xform_refresh; - gzgt->draw_prepare = gizmo2d_xform_draw_prepare; -} - void ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(wmGizmoGroupType *gzgt) { ED_widgetgroup_gizmo2d_xform_callbacks_set(gzgt); @@ -726,6 +875,18 @@ static void gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgrou } } +static void gizmo2d_resize_invoke_prepare(const bContext *C, + wmGizmoGroup *UNUSED(gzgroup), + wmGizmo *gz, + const wmEvent *UNUSED(event)) +{ + wmGizmoOpElem *gzop; + int orient_type = gizmo2d_calc_transform_orientation(C); + + gzop = WM_gizmo_operator_get(gz, 0); + RNA_enum_set(&gzop->ptr, "orient_type", orient_type); +} + static void gizmo2d_resize_message_subscribe(const struct bContext *C, struct wmGizmoGroup *gzgroup, struct wmMsgBus *mbus) @@ -743,6 +904,7 @@ void ED_widgetgroup_gizmo2d_resize_callbacks_set(wmGizmoGroupType *gzgt) gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; gzgt->refresh = gizmo2d_resize_refresh; gzgt->draw_prepare = gizmo2d_resize_draw_prepare; + gzgt->invoke_prepare = gizmo2d_resize_invoke_prepare; gzgt->message_subscribe = gizmo2d_resize_message_subscribe; } diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index b14d499cb66..7f92c96d25f 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -1082,9 +1082,17 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) case TFM_ROTATION: initRotation(t); break; - case TFM_RESIZE: - initResize(t); + case TFM_RESIZE: { + float mouse_dir_constraint[3]; + if (op) { + RNA_float_get_array(op->ptr, "mouse_dir_constraint", mouse_dir_constraint); + } + else { + zero_v3(mouse_dir_constraint); + } + initResize(t, mouse_dir_constraint); break; + } case TFM_SKIN_RESIZE: initSkinResize(t); break; diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h index d8601000ddb..c561d1c8a4f 100644 --- a/source/blender/editors/transform/transform_mode.h +++ b/source/blender/editors/transform/transform_mode.h @@ -121,7 +121,7 @@ void initMirror(TransInfo *t); void initPushPull(TransInfo *t); /* transform_mode_resize.c */ -void initResize(TransInfo *t); +void initResize(TransInfo *t, float mouse_dir_constraint[3]); /* transform_mode_rotate.c */ void initRotation(TransInfo *t); diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 65f4623b3be..28323460626 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -201,14 +201,45 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) ED_area_status_text(t->area, str); } -void initResize(TransInfo *t) +void initResize(TransInfo *t, float mouse_dir_constraint[3]) { t->mode = TFM_RESIZE; t->transform = applyResize; t->tsnap.applySnap = ApplySnapResize; t->tsnap.distance = ResizeBetween; - initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP); + if (is_zero_v3(mouse_dir_constraint)) { + initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP); + } + else { + int mval_start[2], mval_end[2]; + float mval_dir[3], t_mval[2]; + float viewmat[3][3]; + + copy_m3_m4(viewmat, t->viewmat); + mul_v3_m3v3(mval_dir, viewmat, mouse_dir_constraint); + normalize_v2(mval_dir); + if (is_zero_v2(mval_dir)) { + /* The screen space direction is orthogonal to the view. + * Fall back to constraining on the Y axis. */ + mval_dir[0] = 0; + mval_dir[1] = 1; + } + + mval_start[0] = t->center2d[0]; + mval_start[1] = t->center2d[1]; + + t_mval[0] = t->mval[0] - mval_start[0]; + t_mval[1] = t->mval[1] - mval_start[1]; + project_v2_v2v2(mval_dir, t_mval, mval_dir); + + mval_end[0] = t->center2d[0] + mval_dir[0]; + mval_end[1] = t->center2d[1] + mval_dir[1]; + + setCustomPoints(t, &t->mouse, mval_end, mval_start); + + initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO); + } t->flag |= T_NULL_ONE; t->num.val_flag[0] |= NUM_NULL_ONE; diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c index b96b8103392..b18e0aa0c7f 100644 --- a/source/blender/editors/transform/transform_mode_shrink_fatten.c +++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c @@ -188,7 +188,9 @@ void initShrinkFatten(TransInfo *t) { /* If not in mesh edit mode, fallback to Resize. */ if ((t->flag & T_EDIT) == 0 || (t->obedit_type != OB_MESH)) { - initResize(t); + float no_mouse_dir_constraint[3]; + zero_v3(no_mouse_dir_constraint); + initResize(t, no_mouse_dir_constraint); } else { t->mode = TFM_SHRINKFATTEN; diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 3a4a9342e18..5ed340abf97 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -59,6 +59,7 @@ typedef struct TransformModeItem { void (*opfunc)(wmOperatorType *); } TransformModeItem; +static const float VecZero[3] = {0, 0, 0}; static const float VecOne[3] = {1, 1, 1}; static const char OP_TRANSLATION[] = "TRANSFORM_OT_translate"; @@ -783,6 +784,19 @@ static void TRANSFORM_OT_resize(struct wmOperatorType *ot) RNA_def_float_vector( ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Scale", "", -FLT_MAX, FLT_MAX); + PropertyRNA *prop; + prop = RNA_def_float_vector(ot->srna, + "mouse_dir_constraint", + 3, + VecZero, + -FLT_MAX, + FLT_MAX, + "Mouse Directional Constraint", + "", + -FLT_MAX, + FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + WM_operatortype_props_advanced_begin(ot); Transform_Properties(ot, diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 1e3acdf1071..298cd00bb46 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -30,6 +30,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" @@ -52,6 +53,8 @@ #include "ED_armature.h" +#include "SEQ_select.h" + #include "transform.h" #include "transform_orientations.h" @@ -602,6 +605,16 @@ short transform_orientation_matrix_get(bContext *C, return V3D_ORIENT_CUSTOM_MATRIX; } + if (t->spacetype == SPACE_SEQ && t->options & CTX_SEQUENCER_IMAGE) { + Scene *scene = t->scene; + Sequence *seq = SEQ_select_active_get(scene); + if (seq && seq->strip->transform && orient_index == V3D_ORIENT_LOCAL) { + unit_m3(r_spacemtx); + rotate_m3(r_spacemtx, seq->strip->transform->rotation); + return orient_index; + } + } + Object *ob = CTX_data_active_object(C); Object *obedit = CTX_data_edit_object(C); Scene *scene = t->scene; diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index 69db21d4c63..18437680731 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -66,6 +66,10 @@ void SEQ_image_transform_mirror_factor_get(const struct Sequence *seq, float r_m void SEQ_image_transform_origin_offset_pixelspace_get(const struct Scene *scene, const struct Sequence *seq, float r_origin[2]); +void SEQ_image_transform_quad_get(const struct Scene *scene, + const struct Sequence *seq, + bool apply_rotation, + float r_quad[4][2]); void SEQ_image_transform_final_quad_get(const struct Scene *scene, const struct Sequence *seq, float r_quad[4][2]); diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index f014c7a312a..ec504c0c9b6 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -473,40 +473,41 @@ void SEQ_image_transform_origin_offset_pixelspace_get(const Scene *scene, * * \param scene: Scene in which strips are located * \param seq: Sequence to calculate image transform origin + * \param apply_rotation: Apply sequence rotation transform to the quad * \param r_origin: return value */ - -void SEQ_image_transform_final_quad_get(const Scene *scene, - const Sequence *seq, - float r_quad[4][2]) +static void seq_image_transform_quad_get_ex(const Scene *scene, + const Sequence *seq, + bool apply_rotation, + float r_quad[4][2]) { StripTransform *transform = seq->strip->transform; StripCrop *crop = seq->strip->crop; - int imgage_size[2] = {scene->r.xsch, scene->r.ysch}; + int image_size[2] = {scene->r.xsch, scene->r.ysch}; if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) { - imgage_size[0] = seq->strip->stripdata->orig_width; - imgage_size[1] = seq->strip->stripdata->orig_height; + image_size[0] = seq->strip->stripdata->orig_width; + image_size[1] = seq->strip->stripdata->orig_height; } float transform_matrix[3][3]; loc_rot_size_to_mat3(transform_matrix, (const float[]){transform->xofs, transform->yofs}, - transform->rotation, + apply_rotation ? transform->rotation : 0.0f, (const float[]){transform->scale_x, transform->scale_y}); - const float origin[2] = {imgage_size[0] * transform->origin[0], - imgage_size[1] * transform->origin[1]}; - const float pivot[2] = {origin[0] - (imgage_size[0] / 2), origin[1] - (imgage_size[1] / 2)}; + const float origin[2] = {image_size[0] * transform->origin[0], + image_size[1] * transform->origin[1]}; + const float pivot[2] = {origin[0] - (image_size[0] / 2), origin[1] - (image_size[1] / 2)}; transform_pivot_set_m3(transform_matrix, pivot); - r_quad[0][0] = (imgage_size[0] / 2) - crop->right; - r_quad[0][1] = (imgage_size[1] / 2) - crop->top; - r_quad[1][0] = (imgage_size[0] / 2) - crop->right; - r_quad[1][1] = (-imgage_size[1] / 2) + crop->bottom; - r_quad[2][0] = (-imgage_size[0] / 2) + crop->left; - r_quad[2][1] = (-imgage_size[1] / 2) + crop->bottom; - r_quad[3][0] = (-imgage_size[0] / 2) + crop->left; - r_quad[3][1] = (imgage_size[1] / 2) - crop->top; + r_quad[0][0] = (image_size[0] / 2) - crop->right; + r_quad[0][1] = (image_size[1] / 2) - crop->top; + r_quad[1][0] = (image_size[0] / 2) - crop->right; + r_quad[1][1] = (-image_size[1] / 2) + crop->bottom; + r_quad[2][0] = (-image_size[0] / 2) + crop->left; + r_quad[2][1] = (-image_size[1] / 2) + crop->bottom; + r_quad[3][0] = (-image_size[0] / 2) + crop->left; + r_quad[3][1] = (image_size[1] / 2) - crop->top; mul_m3_v2(transform_matrix, r_quad[0]); mul_m3_v2(transform_matrix, r_quad[1]); @@ -521,6 +522,21 @@ void SEQ_image_transform_final_quad_get(const Scene *scene, mul_v2_v2(r_quad[3], mirror); } +void SEQ_image_transform_quad_get(const Scene *scene, + const Sequence *seq, + bool apply_rotation, + float r_quad[4][2]) +{ + seq_image_transform_quad_get_ex(scene, seq, apply_rotation, r_quad); +} + +void SEQ_image_transform_final_quad_get(const Scene *scene, + const Sequence *seq, + float r_quad[4][2]) +{ + seq_image_transform_quad_get_ex(scene, seq, true, r_quad); +} + void SEQ_image_preview_unit_to_px(const Scene *scene, const float co_src[2], float co_dst[2]) { co_dst[0] = co_src[0] * scene->r.xsch; From 5da58f48ae0a429eb2aa5745362f8c7ee9c165a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 8 Oct 2021 12:34:45 +0200 Subject: [PATCH 0630/1500] Cleanup: asset catalogs, move functions to their siblings Moved function definitions around so that all members of a class are next to each other. Previously some functions of one class were sitting between functions of another class. No functional changes. --- .../blenkernel/intern/asset_catalog.cc | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index d50942bd9fa..1d25bda480d 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -362,6 +362,11 @@ std::unique_ptr AssetCatalogService::construct_cdf_i return cdf; } +AssetCatalogTree *AssetCatalogService::get_catalog_tree() +{ + return catalog_tree_.get(); +} + std::unique_ptr AssetCatalogService::read_into_tree() { auto tree = std::make_unique(); @@ -466,6 +471,22 @@ bool AssetCatalogTreeItem::has_children() const return !children_.empty(); } +void AssetCatalogTreeItem::foreach_item_recursive(AssetCatalogTreeItem::ChildMap &children, + const ItemIterFn callback) +{ + for (auto &[key, item] : children) { + callback(item); + foreach_item_recursive(item.children_, callback); + } +} + +void AssetCatalogTreeItem::foreach_child(const ItemIterFn callback) +{ + for (auto &[key, item] : children_) { + callback(item); + } +} + /* ---------------------------------------------------------------------- */ void AssetCatalogTree::insert_item(const AssetCatalog &catalog) @@ -507,15 +528,6 @@ void AssetCatalogTree::foreach_item(AssetCatalogTreeItem::ItemIterFn callback) AssetCatalogTreeItem::foreach_item_recursive(root_items_, callback); } -void AssetCatalogTreeItem::foreach_item_recursive(AssetCatalogTreeItem::ChildMap &children, - const ItemIterFn callback) -{ - for (auto &[key, item] : children) { - callback(item); - foreach_item_recursive(item.children_, callback); - } -} - void AssetCatalogTree::foreach_root_item(const ItemIterFn callback) { for (auto &[key, item] : root_items_) { @@ -523,18 +535,6 @@ void AssetCatalogTree::foreach_root_item(const ItemIterFn callback) } } -void AssetCatalogTreeItem::foreach_child(const ItemIterFn callback) -{ - for (auto &[key, item] : children_) { - callback(item); - } -} - -AssetCatalogTree *AssetCatalogService::get_catalog_tree() -{ - return catalog_tree_.get(); -} - bool AssetCatalogDefinitionFile::contains(const CatalogID catalog_id) const { return catalogs_.contains(catalog_id); From 04ad42d83bf74f679a4de2e29ee98eb6e97ce996 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 8 Oct 2021 12:27:46 +0200 Subject: [PATCH 0631/1500] UI/Assets: Disable undo for tree item buttons Disables undo for: * The tree row collapsing - which doesn't make sense to undo, isn't supported by the undo system, and just triggers the confirmation prompt when closing the file. * Renaming items - While this may make sense in some cases, users of the tree-view API can explicitly do an undo push. For asset catalogs it's not supported. --- source/blender/editors/interface/tree_view.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 8ac69f862c8..3f66810b7f6 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -246,6 +246,7 @@ void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const &block, UI_BTYPE_BUT_TOGGLE, 0, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); /* Note that we're passing the tree-row button here, not the chevron one. */ UI_but_func_set(but, collapse_chevron_click_fn, nullptr, nullptr); + UI_but_flag_disable(but, UI_BUT_UNDO); /* Check if the query for the button matches the created button. */ BLI_assert(is_collapse_chevron_but(but)); @@ -313,6 +314,7 @@ void AbstractTreeViewItem::add_rename_button(uiLayout &row) /* Gotta be careful with what's passed to the `arg1` here. Any tree data will be freed once the * callback is executed. */ UI_but_func_rename_set(rename_but, AbstractTreeViewItem::rename_button_fn, rename_but); + UI_but_flag_disable(rename_but, UI_BUT_UNDO); const bContext *evil_C = static_cast(block->evil_C); ARegion *region = CTX_wm_region(evil_C); From 86643a4e73fc23055cf346213b0569200bee795d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 8 Oct 2021 14:06:20 +0200 Subject: [PATCH 0632/1500] Py doc: document msgbus subscriptions clearing on blendfile load Document that `bpy.msgbus.subscribe_rna()`-registered messagebus subscriptions will be cleared whenever a new blend file is loaded. Passing `options={'PERSISTENT'}` has no influence on this behaviour. --- source/blender/python/intern/bpy_msgbus.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c index 75a5f6f72ae..bf70f8158d2 100644 --- a/source/blender/python/intern/bpy_msgbus.c +++ b/source/blender/python/intern/bpy_msgbus.c @@ -208,6 +208,9 @@ static void bpy_msgbus_subscribe_value_free_data(struct wmMsgSubscribeKey *UNUSE PyDoc_STRVAR( bpy_msgbus_subscribe_rna_doc, ".. function:: subscribe_rna(key, owner, args, notify, options=set())\n" + "\n" + " Register a message bus subscription. It will be cleared when another blend file is\n" + " loaded, or can be cleared explicitly via :func:`bpy.msgbus.clear_by_owner`.\n" "\n" BPY_MSGBUS_RNA_MSGKEY_DOC " :arg owner: Handle for this subscription (compared by identity).\n" " :type owner: Any type.\n" From 2aca08fc1cb6448b593afde7b00ba34ce590551b Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 8 Oct 2021 14:17:03 +0200 Subject: [PATCH 0633/1500] UI: Support tooltips for superimposed icons In a couple of places in the UI, we show superimposed icons on buttons to execute an operation (called "Extra Icons" internally). Hovering them would show the tooltip of the underlying button, which is misleading and confusing. There are cases where it's not obvious what an icon does, so a tooltip would be quite useful here. It's likely we are going to use superimposed icons in more places in the future, e.g. see D11890. The extra icon basically acts as an override for the button in the tooltip code. Differential Revision: https://developer.blender.org/D11894 Reviewed by: Campbell Barton --- source/blender/editors/include/UI_interface.h | 10 +++ source/blender/editors/interface/interface.c | 82 +++++++++++++++-- .../editors/interface/interface_handlers.c | 6 +- .../interface/interface_region_tooltip.c | 89 +++++++++++-------- 4 files changed, 145 insertions(+), 42 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index e8b71a41439..c536eff771d 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -82,6 +82,7 @@ struct wmWindow; typedef struct uiBlock uiBlock; typedef struct uiBut uiBut; +typedef struct uiButExtraOpIcon uiButExtraOpIcon; typedef struct uiLayout uiLayout; typedef struct uiPopupBlockHandle uiPopupBlockHandle; /* C handle for C++ #ui::AbstractTreeView type. */ @@ -1432,6 +1433,8 @@ typedef struct uiStringInfo { * Will fill them with translated strings, when possible. * Strings in uiStringInfo must be MEM_freeN'ed by caller. */ void UI_but_string_info_get(struct bContext *C, uiBut *but, ...) ATTR_SENTINEL(0); +void UI_but_extra_icon_string_info_get(struct bContext *C, uiButExtraOpIcon *extra_icon, ...) + ATTR_SENTINEL(0); /* Edit i18n stuff. */ /* Name of the main py op from i18n addon. */ @@ -1724,6 +1727,8 @@ struct PointerRNA *UI_but_extra_operator_icon_add(uiBut *but, const char *opname, short opcontext, int icon); +struct wmOperatorType *UI_but_extra_operator_icon_optype_get(struct uiButExtraOpIcon *extra_icon); +struct PointerRNA *UI_but_extra_operator_icon_opptr_get(struct uiButExtraOpIcon *extra_icon); /* Autocomplete * @@ -2713,6 +2718,11 @@ struct ARegion *UI_tooltip_create_from_button(struct bContext *C, struct ARegion *butregion, uiBut *but, bool is_label); +struct ARegion *UI_tooltip_create_from_button_or_extra_icon(struct bContext *C, + struct ARegion *butregion, + uiBut *but, + uiButExtraOpIcon *extra_icon, + bool is_label); struct ARegion *UI_tooltip_create_from_gizmo(struct bContext *C, struct wmGizmo *gz); void UI_tooltip_free(struct bContext *C, struct bScreen *screen, struct ARegion *region); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 92391a703ef..68595e49871 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1224,16 +1224,21 @@ void ui_but_add_shortcut(uiBut *but, const char *shortcut_str, const bool do_str * \{ */ static bool ui_but_event_operator_string_from_operator(const bContext *C, - uiBut *but, + wmOperatorCallParams *op_call_params, char *buf, const size_t buf_len) { - BLI_assert(but->optype != NULL); + BLI_assert(op_call_params->optype != NULL); bool found = false; - IDProperty *prop = (but->opptr) ? but->opptr->data : NULL; + IDProperty *prop = (op_call_params->opptr) ? op_call_params->opptr->data : NULL; - if (WM_key_event_operator_string( - C, but->optype->idname, but->opcontext, prop, true, buf, buf_len)) { + if (WM_key_event_operator_string(C, + op_call_params->optype->idname, + op_call_params->opcontext, + prop, + true, + buf, + buf_len)) { found = true; } return found; @@ -1318,7 +1323,12 @@ static bool ui_but_event_operator_string(const bContext *C, bool found = false; if (but->optype != NULL) { - found = ui_but_event_operator_string_from_operator(C, but, buf, buf_len); + found = ui_but_event_operator_string_from_operator( + C, + &(wmOperatorCallParams){ + .optype = but->optype, .opptr = but->opptr, .opcontext = but->opcontext}, + buf, + buf_len); } else if (UI_but_menutype_get(but) != NULL) { found = ui_but_event_operator_string_from_menu(C, but, buf, buf_len); @@ -1330,6 +1340,20 @@ static bool ui_but_event_operator_string(const bContext *C, return found; } +static bool ui_but_extra_icon_event_operator_string(const bContext *C, + uiButExtraOpIcon *extra_icon, + char *buf, + const size_t buf_len) +{ + wmOperatorType *extra_icon_optype = UI_but_extra_operator_icon_optype_get(extra_icon); + + if (extra_icon_optype) { + return ui_but_event_operator_string_from_operator(C, extra_icon->optype_params, buf, buf_len); + } + + return false; +} + static bool ui_but_event_property_operator_string(const bContext *C, uiBut *but, char *buf, @@ -1713,6 +1737,16 @@ PointerRNA *UI_but_extra_operator_icon_add(uiBut *but, return NULL; } +wmOperatorType *UI_but_extra_operator_icon_optype_get(uiButExtraOpIcon *extra_icon) +{ + return extra_icon ? extra_icon->optype_params->optype : NULL; +} + +PointerRNA *UI_but_extra_operator_icon_opptr_get(uiButExtraOpIcon *extra_icon) +{ + return extra_icon->optype_params->opptr; +} + static bool ui_but_icon_extra_is_visible_text_clear(const uiBut *but) { BLI_assert(but->type == UI_BTYPE_TEXT); @@ -7262,6 +7296,42 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) } } +void UI_but_extra_icon_string_info_get(struct bContext *C, uiButExtraOpIcon *extra_icon, ...) +{ + va_list args; + uiStringInfo *si; + + wmOperatorType *optype = UI_but_extra_operator_icon_optype_get(extra_icon); + PointerRNA *opptr = UI_but_extra_operator_icon_opptr_get(extra_icon); + + va_start(args, extra_icon); + while ((si = (uiStringInfo *)va_arg(args, void *))) { + char *tmp = NULL; + + switch (si->type) { + case BUT_GET_LABEL: + tmp = BLI_strdup(WM_operatortype_name(optype, opptr)); + break; + case BUT_GET_TIP: + tmp = WM_operatortype_description(C, optype, opptr); + break; + case BUT_GET_OP_KEYMAP: { + char buf[128]; + if (ui_but_extra_icon_event_operator_string(C, extra_icon, buf, sizeof(buf))) { + tmp = BLI_strdup(buf); + } + } + /* Other types not supported. The caller should expect that outcome, no need to message or + * assert here. */ + default: + break; + } + + si->strinfo = tmp; + } + va_end(args); +} + /* Program Init/Exit */ void UI_init(void) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index c508cf2f36c..bf9b37c00fa 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -8235,7 +8235,11 @@ static ARegion *ui_but_tooltip_init( uiBut *but = UI_region_active_but_get(region); *r_exit_on_event = false; if (but) { - return UI_tooltip_create_from_button(C, region, but, is_label); + const wmWindow *win = CTX_wm_window(C); + uiButExtraOpIcon *extra_icon = ui_but_extra_operator_icon_mouse_over_get( + but, but->active, win->eventstate); + + return UI_tooltip_create_from_button_or_extra_icon(C, region, but, extra_icon, is_label); } return NULL; } diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index a8f289702f8..6071e14d153 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -761,7 +761,9 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is return data; } -static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) +static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, + uiBut *but, + uiButExtraOpIcon *extra_icon) { uiStringInfo but_label = {BUT_GET_LABEL, NULL}; uiStringInfo but_tip = {BUT_GET_TIP, NULL}; @@ -774,20 +776,29 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) char buf[512]; + wmOperatorType *optype = extra_icon ? UI_but_extra_operator_icon_optype_get(extra_icon) : + but->optype; + PropertyRNA *rnaprop = extra_icon ? NULL : but->rnaprop; + /* create tooltip data */ uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData"); - UI_but_string_info_get(C, - but, - &but_label, - &but_tip, - &enum_label, - &enum_tip, - &op_keymap, - &prop_keymap, - &rna_struct, - &rna_prop, - NULL); + if (extra_icon) { + UI_but_extra_icon_string_info_get(C, extra_icon, &but_label, &but_tip, &op_keymap, NULL); + } + else { + UI_but_string_info_get(C, + but, + &but_label, + &but_tip, + &enum_label, + &enum_tip, + &op_keymap, + &prop_keymap, + &rna_struct, + &rna_prop, + NULL); + } /* Tip Label (only for buttons not already showing the label). * Check prefix instead of comparing because the button may include the shortcut. */ @@ -818,8 +829,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } /* special case enum rna buttons */ - if ((but->type & UI_BTYPE_ROW) && but->rnaprop && - RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG) { + if ((but->type & UI_BTYPE_ROW) && rnaprop && RNA_property_flag(rnaprop) & PROP_ENUM_FLAG) { uiTooltipField *field = text_field_add(data, &(uiTooltipFormat){ .style = UI_TIP_STYLE_NORMAL, @@ -863,7 +873,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { /* better not show the value of a password */ - if ((but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) == 0) { + if ((rnaprop && (RNA_property_subtype(rnaprop) == PROP_PASSWORD)) == 0) { /* full string */ ui_but_string_get(but, buf, sizeof(buf)); if (buf[0]) { @@ -878,15 +888,14 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } } - if (but->rnaprop) { + if (rnaprop) { const int unit_type = UI_but_unit_type_get(but); if (unit_type == PROP_UNIT_ROTATION) { - if (RNA_property_type(but->rnaprop) == PROP_FLOAT) { - float value = RNA_property_array_check(but->rnaprop) ? - RNA_property_float_get_index( - &but->rnapoin, but->rnaprop, but->rnaindex) : - RNA_property_float_get(&but->rnapoin, but->rnaprop); + if (RNA_property_type(rnaprop) == PROP_FLOAT) { + float value = RNA_property_array_check(rnaprop) ? + RNA_property_float_get_index(&but->rnapoin, rnaprop, but->rnaindex) : + RNA_property_float_get(&but->rnapoin, rnaprop); uiTooltipField *field = text_field_add(data, &(uiTooltipFormat){ @@ -920,15 +929,15 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } } } - else if (but->optype) { - PointerRNA *opptr; - char *str; - opptr = UI_but_operator_ptr_get(but); /* allocated when needed, the button owns it */ + else if (optype) { + PointerRNA *opptr = extra_icon ? UI_but_extra_operator_icon_opptr_get(extra_icon) : + /* allocated when needed, the button owns it */ + UI_but_operator_ptr_get(but); /* so the context is passed to fieldf functions (some py fieldf functions use it) */ WM_operator_properties_sanitize(opptr, false); - str = ui_tooltip_text_python_from_op(C, but->optype, opptr); + char *str = ui_tooltip_text_python_from_op(C, optype, opptr); /* operator info */ if (U.flag & USER_TOOLTIPS_PYTHON) { @@ -973,7 +982,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } } - if ((U.flag & USER_TOOLTIPS_PYTHON) && !but->optype && rna_struct.strinfo) { + if ((U.flag & USER_TOOLTIPS_PYTHON) && !optype && rna_struct.strinfo) { { uiTooltipField *field = text_field_add(data, &(uiTooltipFormat){ @@ -1002,9 +1011,9 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) /* never fails */ /* move ownership (no need for re-alloc) */ - if (but->rnaprop) { + if (rnaprop) { field->text = RNA_path_full_property_py_ex( - CTX_data_main(C), &but->rnapoin, but->rnaprop, but->rnaindex, true); + CTX_data_main(C), &but->rnapoin, rnaprop, but->rnaindex, true); } else { field->text = RNA_path_full_struct_py(CTX_data_main(C), &but->rnapoin); @@ -1394,11 +1403,8 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, /** \name ToolTip Public API * \{ */ -/** - * \param is_label: When true, show a small tip that only shows the name, - * otherwise show the full tooltip. - */ -ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_label) +ARegion *UI_tooltip_create_from_button_or_extra_icon( + bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_label) { wmWindow *win = CTX_wm_window(C); /* aspect values that shrink text are likely unreadable */ @@ -1415,7 +1421,11 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b } if (data == NULL) { - data = ui_tooltip_data_from_button(C, but); + data = ui_tooltip_data_from_button_or_extra_icon(C, but, extra_icon); + } + + if (data == NULL) { + data = ui_tooltip_data_from_button_or_extra_icon(C, but, NULL); } if (data == NULL) { @@ -1453,6 +1463,15 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b return region; } +/** + * \param is_label: When true, show a small tip that only shows the name, otherwise show the full + * tooltip. + */ +ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_label) +{ + return UI_tooltip_create_from_button_or_extra_icon(C, butregion, but, NULL, is_label); +} + ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz) { wmWindow *win = CTX_wm_window(C); From 092424dae3da4c45f944294825d680a97a0f11ac Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 8 Oct 2021 14:51:39 +0200 Subject: [PATCH 0634/1500] install_deps: Fix OIIO depending on (system...) openVDB. --- build_files/build_environment/install_deps.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index ecaff307885..faabde2af6e 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -2013,7 +2013,7 @@ compile_OIIO() { fi # To be changed each time we make edits that would modify the compiled result! - oiio_magic=17 + oiio_magic=18 _init_oiio # Force having own builds for the dependencies. @@ -2088,6 +2088,7 @@ compile_OIIO() { cmake_d="$cmake_d -D USE_PYTHON=OFF" cmake_d="$cmake_d -D USE_FFMPEG=OFF" cmake_d="$cmake_d -D USE_OPENCV=OFF" + cmake_d="$cmake_d -D USE_OPENVDB=OFF" cmake_d="$cmake_d -D BUILD_TESTING=OFF" cmake_d="$cmake_d -D OIIO_BUILD_TESTS=OFF" cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=OFF" From 7afde7cd22822993fa1a6f709ff0676fc71a0aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 8 Oct 2021 15:14:45 +0200 Subject: [PATCH 0635/1500] Cleanup: asset catalogs, alphabetically order forward declarations No functional changes. --- source/blender/blenkernel/BKE_asset_catalog.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index b6b90cff5fd..e9f0aa6311c 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -48,8 +48,8 @@ using CatalogFilePath = std::string; class AssetCatalog; class AssetCatalogDefinitionFile; -class AssetCatalogTree; class AssetCatalogFilter; +class AssetCatalogTree; /* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single * directory hierarchy). */ From 596446dbc6beeed4d371242e21cd02ef85c4426b Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 7 Oct 2021 16:54:26 +0200 Subject: [PATCH 0636/1500] Fix wrong Cycles tile highlight with region render In previous Blender version the tile highlight was stored in the full frame (un-cropped) space. This was changed with the Cycles X development and now the tiles and render result are always measured relative to the cropped region. Differential Revision: https://developer.blender.org/D12779 --- source/blender/editors/space_image/image_draw.c | 7 ------- source/blender/render/intern/engine.c | 3 +-- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index fc04ec1fe02..fb87c54c1db 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -112,13 +112,6 @@ static void draw_render_info( GPU_matrix_translate_2f(x, y); GPU_matrix_scale_2f(zoomx, zoomy); - RenderData *rd = RE_engine_get_render_data(re); - if (rd->mode & R_BORDER) { - /* TODO: round or floor instead of casting to int */ - GPU_matrix_translate_2f((int)(-rd->border.xmin * rd->xsch * rd->size * 0.01f), - (int)(-rd->border.ymin * rd->ysch * rd->size * 0.01f)); - } - uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 790c46dad0f..5f8679b572d 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -281,11 +281,10 @@ static void render_result_to_bake(RenderEngine *engine, RenderResult *rr) /* Render Results */ -static HighlightedTile highlighted_tile_from_result_get(Render *re, RenderResult *result) +static HighlightedTile highlighted_tile_from_result_get(Render *UNUSED(re), RenderResult *result) { HighlightedTile tile; tile.rect = result->tilerect; - BLI_rcti_translate(&tile.rect, re->disprect.xmin, re->disprect.ymin); return tile; } From 601a6a7dc66616e971169c157e67b70684e6ccfe Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Fri, 8 Oct 2021 15:17:48 +0200 Subject: [PATCH 0637/1500] Theme: Fix Blender Light wire color When using the Blender Light theme the wires seemed too thick. This is a left over from 4a0ddeb62bb4a438. --- release/scripts/presets/interface_theme/Blender_Light.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/presets/interface_theme/Blender_Light.xml b/release/scripts/presets/interface_theme/Blender_Light.xml index 132295316eb..de6a5fe205c 100644 --- a/release/scripts/presets/interface_theme/Blender_Light.xml +++ b/release/scripts/presets/interface_theme/Blender_Light.xml @@ -941,7 +941,7 @@ grid="#353535" node_selected="#f15800" node_active="#f15800" - wire="#a7a7a7" + wire="#191919" wire_inner="#999999" wire_select="#ffa733" selected_text="#7f7070" From ebe23745281e8675a6b45c39f79441a4f896963e Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Fri, 8 Oct 2021 11:43:52 +0200 Subject: [PATCH 0638/1500] User Interface: Make the background inside a node group brighter The breadcrumbs alone may not be enough to indicate that a user is inside a nodegroup. The original dark green color was a bit overwhelming but having a different background helps. This is a follow up to 919e513fa8f9f. --- .../blender/editors/space_node/node_draw.cc | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index b5e2434f3d8..bcebd73e9a7 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2176,6 +2176,28 @@ static void draw_nodetree(const bContext *C, node_draw_nodetree(C, region, snode, ntree, parent_key); } +/** + * Make the background slightly brighter to indicate that users are inside a nodegroup. + **/ +static void draw_background_color(const SpaceNode *snode) +{ + const int max_depth = 2; + const float bright_factor = 0.25f; + + float color[3]; + UI_GetThemeColor3fv(TH_BACK, color); + + int depth = 0; + bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last; + while (path->prev && depth < max_depth) { + path = path->prev; + depth++; + } + + mul_v3_fl(color, 1.0f + bright_factor * depth); + GPU_clear_color(color[0], color[1], color[2], 1.0); +} + void node_draw_space(const bContext *C, ARegion *region) { wmWindow *win = CTX_wm_window(C); @@ -2189,7 +2211,7 @@ void node_draw_space(const bContext *C, ARegion *region) GPU_framebuffer_bind_no_srgb(framebuffer_overlay); UI_view2d_view_ortho(v2d); - UI_ThemeClearColor(TH_BACK); + draw_background_color(snode); GPU_depth_test(GPU_DEPTH_NONE); GPU_scissor_test(true); From ff9587d28eab0339e8341673ed90db29a2932a95 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Fri, 8 Oct 2021 14:30:08 +0200 Subject: [PATCH 0639/1500] User Interface: Use theme alpha for the nodes frames background This bump subversion. --- .../datafiles/userdef/userdef_default_theme.c | 2 +- .../presets/interface_theme/Blender_Light.xml | 2 +- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_300.c | 49 ++++++++++--------- .../blenloader/intern/versioning_userdef.c | 5 ++ source/blender/editors/space_node/drawnode.cc | 2 +- 6 files changed, 36 insertions(+), 26 deletions(-) diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 6753dc8563e..1f9316cacfd 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -845,7 +845,7 @@ const bTheme U_theme_default = { .nodeclass_layout = RGBA(0x6c696fff), .nodeclass_geometry = RGBA(0x00d7a4ff), .nodeclass_attribute = RGBA(0x3f5980ff), - .movie = RGBA(0x1a1a1acc), + .movie = RGBA(0x1a1a1a7d), .gp_vertex_size = 3, .gp_vertex = RGBA(0x97979700), .gp_vertex_select = RGBA(0xff8500ff), diff --git a/release/scripts/presets/interface_theme/Blender_Light.xml b/release/scripts/presets/interface_theme/Blender_Light.xml index de6a5fe205c..834139458d5 100644 --- a/release/scripts/presets/interface_theme/Blender_Light.xml +++ b/release/scripts/presets/interface_theme/Blender_Light.xml @@ -950,7 +950,7 @@ color_node="#ffcb4d" group_node="#59b36ab9" group_socket_node="#dfc300" - frame_node="#9b9b9ba0" + frame_node="#9b9b9b60" matte_node="#977474" distor_node="#749797" noodle_curving="4" diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 31ce1b124e9..ceb19e87b40 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 32 +#define BLENDER_FILE_SUBVERSION 33 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 34712caf584..e1100fa40c4 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -643,6 +643,19 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 33)) { + /* This was missing from #move_vertex_group_names_to_object_data. */ + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) { + /* This uses the fact that the active vertex group index starts counting at 1. */ + if (BKE_object_defgroup_active_index_get(object) == 0) { + BKE_object_defgroup_active_index_set(object, object->actdef); + } + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -655,16 +668,6 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) */ { /* Keep this block, even when empty. */ - - /* This was missing from #move_vertex_group_names_to_object_data. */ - LISTBASE_FOREACH (Object *, object, &bmain->objects) { - if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) { - /* This uses the fact that the active vertex group index starts counting at 1. */ - if (BKE_object_defgroup_active_index_get(object) == 0) { - BKE_object_defgroup_active_index_set(object, object->actdef); - } - } - } } } @@ -1708,18 +1711,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 300, 33)) { for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { @@ -1742,4 +1734,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index cd365b6be78..60e202746ff 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -301,6 +301,11 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) } } + if (!USER_VERSION_ATLEAST(300, 33)) { + /* Adjust the frame node alpha now that it is used differently. */ + btheme->space_node.movie[3] = U_theme_default.space_node.movie[3]; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 8d6d56fc383..f7231df85a0 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -451,7 +451,7 @@ static void node_draw_frame(const bContext *C, const rctf *rct = &node->totr; UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(rct, true, BASIS_RAD, color); + UI_draw_roundbox_4fv(rct, true, BASIS_RAD, color); /* outline active and selected emphasis */ if (node->flag & SELECT) { From f01c4f27f978d3c70ca01515e338d7edd6e59b32 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 8 Oct 2021 11:17:42 +0200 Subject: [PATCH 0640/1500] Fix Cycles speed regression after dynamic volume stack change Only copy required part of volume stack instead of entire stack. Solves time regression introduced by D12759 and avoids need in implementing volume stack calculation to exactly match what the path tracing will do (as well as potentially makes scenes with a lot of volumes ans a tiny bit of deeply nested ones render faster). Still need to look into memory aspect of the regression, but that is for separate patch. Ref T92014 Maniphest Tasks: T92014 Differential Revision: https://developer.blender.org/D12790 --- .../kernel/integrator/integrator_state_util.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index 453ec49c7b0..01d596b690a 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -155,12 +155,17 @@ ccl_device_forceinline void integrator_state_read_shadow_isect(INTEGRATOR_STATE_ ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(INTEGRATOR_STATE_ARGS) { if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { - for (int i = 0; i < kernel_data.volume_stack_size; i++) { - INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, i, object) = INTEGRATOR_STATE_ARRAY( - volume_stack, i, object); - INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, i, shader) = INTEGRATOR_STATE_ARRAY( - volume_stack, i, shader); - } + int index = 0; + int shader; + do { + shader = INTEGRATOR_STATE_ARRAY(volume_stack, index, shader); + + INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, index, object) = INTEGRATOR_STATE_ARRAY( + volume_stack, index, object); + INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, index, shader) = shader; + + ++index; + } while (shader != OBJECT_NONE); } } From bff3dcf3307a475916781994027dbd7d36f31b98 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 23:41:47 +1100 Subject: [PATCH 0641/1500] WM: always reset message-bus on file load Previously this was only happening when "Load UI" was enabled, making it difficult for Python script authors to know when re-registering subscribers was needed. --- source/blender/windowmanager/intern/wm_files.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index a5ebf988edd..564f869581a 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -204,6 +204,16 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist) WM_event_remove_handlers(C, &win->modalhandlers); ED_screen_exit(C, win, WM_window_get_active_screen(win)); } + + /* NOTE(@campbellbarton): Clear the message bus so it's always cleared on file load. + * Otherwise it's cleared when "Load UI" is set (see #USER_FILENOUI & #wm_close_and_free). + * However it's _not_ cleared when the UI is kept. This complicates use from add-ons + * which can re-register subscribers on file-load. To support this use case, + * it's best to have predictable behavior - always clear. */ + if (wm->message_bus != NULL) { + WM_msgbus_destroy(wm->message_bus); + wm->message_bus = NULL; + } } /* reset active window */ From 4d71138738ca8ba7e4908328d81d1f6c95fc84cd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 8 Oct 2021 23:43:01 +1100 Subject: [PATCH 0642/1500] Docs: note that message-bus subscribers are reset on file load --- source/blender/python/intern/bpy_msgbus.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c index bf70f8158d2..9e165bbf1c0 100644 --- a/source/blender/python/intern/bpy_msgbus.c +++ b/source/blender/python/intern/bpy_msgbus.c @@ -218,7 +218,12 @@ PyDoc_STRVAR( "\n" " - ``PERSISTENT`` when set, the subscriber will be kept when remapping ID data.\n" "\n" - " :type options: set of str.\n"); + " :type options: set of str.\n" + "\n" + ".. note::\n" + "\n" + " All subscribers will be cleared on file-load. Subscribers can be re-registered on load,\n" + " see :mod:`bpy.app.handlers.load_post`.\n"); static PyObject *bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { const char *error_prefix = "subscribe_rna"; From 736be7cf5899047167e6b377414f35b2043191e9 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 8 Oct 2021 15:48:55 +0200 Subject: [PATCH 0643/1500] Fix T91997: Cycles glass + SSS not rendering correctly --- intern/cycles/kernel/integrator/integrator_subsurface.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/integrator_subsurface.h index c309d20a046..9026de1c064 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface.h @@ -59,6 +59,9 @@ ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, ShaderData *sd, const Sh INTEGRATOR_STATE_WRITE(path, flag) = (path_flag & ~PATH_RAY_CAMERA) | PATH_RAY_SUBSURFACE; INTEGRATOR_STATE_WRITE(path, throughput) *= shader_bssrdf_sample_weight(sd, sc); + /* Advance random number offset for bounce. */ + INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { if (INTEGRATOR_STATE(path, bounce) == 0) { INTEGRATOR_STATE_WRITE(path, diffuse_glossy_ratio) = one_float3(); @@ -599,7 +602,7 @@ ccl_device_inline bool subsurface_scatter(INTEGRATOR_STATE_ARGS) integrator_state_write_isect(INTEGRATOR_STATE_PASS, &ss_isect.hits[0]); integrator_state_write_ray(INTEGRATOR_STATE_PASS, &ray); - /* Advanced random number offset for bounce. */ + /* Advance random number offset for bounce. */ INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; const int shader = intersection_get_shader(kg, &ss_isect.hits[0]); From 0c684a704641fdd3cf46f263464b8f84060f79e6 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 8 Oct 2021 16:08:04 +0200 Subject: [PATCH 0644/1500] Fix T91999: wrong Cycles updates with mesh deformation, after recent changes --- intern/cycles/render/geometry.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 49b5f9e27ee..39681ef3d66 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -1505,6 +1505,7 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro dscene->prim_time.tag_realloc(); if (device_update_flags & DEVICE_MESH_DATA_NEEDS_REALLOC) { + dscene->tri_verts.tag_realloc(); dscene->tri_vnormal.tag_realloc(); dscene->tri_vindex.tag_realloc(); dscene->tri_patch.tag_realloc(); From 38c4888f0999ee5361dc76d2b9a7cd45e8ae2896 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Fri, 8 Oct 2021 16:01:49 +0200 Subject: [PATCH 0645/1500] Fix crash in the node editor in cases where nodetree was empty This seems to happen only in a few files, and not so trivial to reproduce from scratch. The crash is real though, and this fixes it. It also fix a wrong comment style that was introduced in the same faulty commit. Bug introduced on ebe23745281e86. Differential Revision: https://developer.blender.org/D12794 --- source/blender/editors/space_node/node_draw.cc | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index bcebd73e9a7..c864f6c34d6 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2178,22 +2178,16 @@ static void draw_nodetree(const bContext *C, /** * Make the background slightly brighter to indicate that users are inside a nodegroup. - **/ + */ static void draw_background_color(const SpaceNode *snode) { const int max_depth = 2; const float bright_factor = 0.25f; + const int depth = BLI_listbase_count_at_most(&snode->treepath, max_depth); + float color[3]; UI_GetThemeColor3fv(TH_BACK, color); - - int depth = 0; - bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last; - while (path->prev && depth < max_depth) { - path = path->prev; - depth++; - } - mul_v3_fl(color, 1.0f + bright_factor * depth); GPU_clear_color(color[0], color[1], color[2], 1.0); } From ff57ce86170d2e440d2e0566bf3c4a74bb745b32 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 8 Oct 2021 16:17:29 +0200 Subject: [PATCH 0646/1500] UI: Support showing superimposed icons as disabled (with disabled hint) If the operator poll of a superimposed icon returned `false`, the superimposed icon would just draw normally and fail silently. Instead it will now be drawn grayed out, plus the tooltip of the icon can show the usual "disabled hint" (a hint explaining why the button is disabled). --- source/blender/editors/interface/interface.c | 23 +++++++++++++++---- .../editors/interface/interface_intern.h | 4 ++++ .../interface/interface_region_tooltip.c | 9 ++++---- .../editors/interface/interface_widgets.c | 5 +++- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 68595e49871..07bb9040da8 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1701,6 +1701,7 @@ static PointerRNA *ui_but_extra_operator_icon_add_ptr(uiBut *but, extra_op_icon->optype_params->optype); extra_op_icon->optype_params->opcontext = opcontext; extra_op_icon->highlighted = false; + extra_op_icon->disabled = false; BLI_addtail(&but->extra_op_icons, extra_op_icon); @@ -1905,18 +1906,19 @@ static void ui_but_validate(const uiBut *but) /** * Check if the operator \a ot poll is successful with the context given by \a but (optionally). * \param but: The button that might store context. Can be NULL for convenience (e.g. if there is - * no button to take context from, but we still want to poll the operator). + * no button to take context from, but we still want to poll the operator). */ -bool ui_but_context_poll_operator(bContext *C, wmOperatorType *ot, const uiBut *but) +bool ui_but_context_poll_operator_ex(bContext *C, + const uiBut *but, + const wmOperatorCallParams *optype_params) { bool result; - int opcontext = but ? but->opcontext : WM_OP_INVOKE_DEFAULT; if (but && but->context) { CTX_store_set(C, but->context); } - result = WM_operator_poll_context(C, ot, opcontext); + result = WM_operator_poll_context(C, optype_params->optype, optype_params->opcontext); if (but && but->context) { CTX_store_set(C, NULL); @@ -1925,6 +1927,13 @@ bool ui_but_context_poll_operator(bContext *C, wmOperatorType *ot, const uiBut * return result; } +bool ui_but_context_poll_operator(bContext *C, wmOperatorType *ot, const uiBut *but) +{ + const int opcontext = but ? but->opcontext : WM_OP_INVOKE_DEFAULT; + return ui_but_context_poll_operator_ex( + C, but, &(wmOperatorCallParams){.optype = ot, .opcontext = opcontext}); +} + void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_xy[2]) { wmWindow *window = CTX_wm_window(C); @@ -1955,6 +1964,12 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x } } + LISTBASE_FOREACH (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) { + if (!ui_but_context_poll_operator_ex((bContext *)C, but, op_icon->optype_params)) { + op_icon->disabled = true; + } + } + const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( depsgraph, (scene) ? scene->r.cfra : 0.0f); ui_but_anim_flag(but, &anim_eval_context); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 5c06f8cfd13..e7a728efce1 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -407,6 +407,7 @@ typedef struct uiButExtraOpIcon { struct wmOperatorCallParams *optype_params; bool highlighted; + bool disabled; } uiButExtraOpIcon; typedef struct ColorPicker { @@ -694,6 +695,9 @@ void ui_but_range_set_hard(uiBut *but); void ui_but_range_set_soft(uiBut *but); bool ui_but_context_poll_operator(struct bContext *C, struct wmOperatorType *ot, const uiBut *but); +bool ui_but_context_poll_operator_ex(struct bContext *C, + const uiBut *but, + const struct wmOperatorCallParams *optype_params); extern void ui_but_update(uiBut *but); extern void ui_but_update_edited(uiBut *but); diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 6071e14d153..9aa65bd5b55 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -954,18 +954,19 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, } /* button is disabled, we may be able to tell user why */ - if (but->flag & UI_BUT_DISABLED) { + if ((but->flag & UI_BUT_DISABLED) || extra_icon) { const char *disabled_msg = NULL; bool disabled_msg_free = false; /* if operator poll check failed, it can give pretty precise info why */ - if (but->optype) { + if (optype) { CTX_wm_operator_poll_msg_clear(C); - WM_operator_poll_context(C, but->optype, but->opcontext); + WM_operator_poll_context( + C, optype, extra_icon ? extra_icon->optype_params->opcontext : but->opcontext); disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free); } /* alternatively, buttons can store some reasoning too */ - else if (but->disabled_info) { + else if (!extra_icon && but->disabled_info) { disabled_msg = TIP_(but->disabled_info); } diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 466deedf3bb..a1534ab4b7f 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2263,7 +2263,10 @@ static void widget_draw_extra_icons(const uiWidgetColors *wcol, temp.xmin = temp.xmax - icon_size; - if (!op_icon->highlighted) { + if (op_icon->disabled) { + alpha_this *= 0.4f; + } + else if (!op_icon->highlighted) { alpha_this *= 0.75f; } From 94d2736dfb33c20a49781eec00168f174ff8b767 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 8 Oct 2021 16:49:28 +0200 Subject: [PATCH 0647/1500] Fix T92046: Mesh to GPencil fails because materials are not created This bug was introduced in D12190 because the list of types that support materials did not include GPencil and this caused all materials to be removed after they were created during conversion. --- source/blender/blenkernel/intern/object.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 274f0394618..e3bb384ffcc 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -5772,6 +5772,14 @@ void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data) bool BKE_object_supports_material_slots(struct Object *ob) { - return ELEM( - ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_HAIR, OB_POINTCLOUD, OB_VOLUME); + return ELEM(ob->type, + OB_MESH, + OB_CURVE, + OB_SURF, + OB_FONT, + OB_MBALL, + OB_HAIR, + OB_POINTCLOUD, + OB_VOLUME, + OB_GPENCIL); } From 3a65571195524ea50682611306ab4d103807112a Mon Sep 17 00:00:00 2001 From: Patrick Mours Date: Fri, 8 Oct 2021 13:45:34 +0200 Subject: [PATCH 0648/1500] Fix T90666: Toggling motion blur while persistent data is enabled results in artifacts Enabling or disabling motion blur requires rebuilding the BVH of affected geometry and uploading modified vertices to the device (since without motion blur the transform is applied to the vertex positions, whereas with motion blur this is done during traversal). Previously neither was happening when persistent data was enabled, since the relevant node sockets were not tagged as modified after toggling motion blur. The change to blender_object.cpp makes it so `geom->set_use_motion_blur()` is always called (regardless of motion blur being toggled on or off), which will tag the geometry as modified if that value changed and ensures the BVH is updated. The change to hair.cpp/mesh.cpp was necessary since after motion blur is disabled, the transform is applied to the vertex positions of a mesh, but those changes were not uploaded to the device. This is fixed now that they are tagged as modified. Maniphest Tasks: T90666 Differential Revision: https://developer.blender.org/D12781 --- intern/cycles/blender/blender_object.cpp | 9 ++++----- intern/cycles/render/hair.cpp | 3 +++ intern/cycles/render/mesh.cpp | 2 ++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 95da4a2df84..4b1c4edef7e 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -104,23 +104,22 @@ void BlenderSync::sync_object_motion_init(BL::Object &b_parent, BL::Object &b_ob array motion; object->set_motion(motion); - Scene::MotionType need_motion = scene->need_motion(); - if (need_motion == Scene::MOTION_NONE || !object->get_geometry()) { + Geometry *geom = object->get_geometry(); + if (!geom) { return; } - Geometry *geom = object->get_geometry(); - int motion_steps = 0; bool use_motion_blur = false; + Scene::MotionType need_motion = scene->need_motion(); if (need_motion == Scene::MOTION_BLUR) { motion_steps = object_motion_steps(b_parent, b_ob, Object::MAX_MOTION_STEPS); if (motion_steps && object_use_deform_motion(b_parent, b_ob)) { use_motion_blur = true; } } - else { + else if (need_motion != Scene::MOTION_NONE) { motion_steps = 3; } diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp index e104455f7dd..e757e3fd3e0 100644 --- a/intern/cycles/render/hair.cpp +++ b/intern/cycles/render/hair.cpp @@ -441,6 +441,9 @@ void Hair::apply_transform(const Transform &tfm, const bool apply_to_motion) curve_radius[i] = radius; } + tag_curve_keys_modified(); + tag_curve_radius_modified(); + if (apply_to_motion) { Attribute *curve_attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 2ecea3101db..9c93f6f881c 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -508,6 +508,8 @@ void Mesh::apply_transform(const Transform &tfm, const bool apply_to_motion) for (size_t i = 0; i < verts.size(); i++) verts[i] = transform_point(&tfm, verts[i]); + tag_verts_modified(); + if (apply_to_motion) { Attribute *attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); From 7bd0de924080571f4c8308dc651b83539d6d22b8 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 8 Oct 2021 19:48:14 +0200 Subject: [PATCH 0649/1500] Asset Browser: Avoid per-asset context menu on right click in sidebar The right-click keymap item to display the context menu was added for the entire area, not just the main region. --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 2 +- .../presets/keyconfig/keymap_data/industry_compatible_data.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 16cfb1e4760..8fdf7148613 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2144,7 +2144,6 @@ def km_file_browser(params): ("only_activate_if_selected", params.select_mouse == 'LEFTMOUSE'), ("pass_through", True), ]}), *_template_items_context_menu("FILEBROWSER_MT_context_menu", params.context_menu_event), - *_template_items_context_menu("ASSETBROWSER_MT_context_menu", params.context_menu_event), ]) return keymap @@ -2212,6 +2211,7 @@ def km_file_browser_main(params): ("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None), ("file.sort_column_ui_context", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None), ("file.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None), + *_template_items_context_menu("ASSETBROWSER_MT_context_menu", params.context_menu_event), ]) return keymap diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 886abae3602..dedaba2f56c 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1254,7 +1254,6 @@ def km_file_browser(params): ("file.select", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, {"properties": [("open", False), ("only_activate_if_selected", True), ("pass_through", True)]}), *_template_items_context_menu("FILEBROWSER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), - *_template_items_context_menu("ASSETBROWSER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) return keymap @@ -1316,6 +1315,7 @@ def km_file_browser_main(params): ("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None), ("file.sort_column_ui_context", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None), ("file.view_selected", {"type": 'F', "value": 'PRESS'}, None), + *_template_items_context_menu("ASSETBROWSER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) return keymap From 17c928e9752372b698a1ed27e243181873aa411e Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 8 Oct 2021 19:56:24 +0200 Subject: [PATCH 0650/1500] UI: Add context menu support for tree-view items Tree-view items can now easily define their own context menu. This works by overriding the `ui::AbstractTreeViewItem::build_context_menu()` function. See the documentation: https://wiki.blender.org/wiki/Source/Interface/Views#Context_Menus Consistently with the Outliner and File Browser, the right-clicked item also gets activated. This makes sure the correct context is set for the operators and makes it clear to the user which item is operated on. An operator to rename the active item is also added, which is something you'd typically want to put in the context menu as well. --- source/blender/editors/include/UI_interface.h | 5 +++ .../blender/editors/include/UI_tree_view.hh | 1 + .../interface/interface_context_menu.c | 12 +++++ .../editors/interface/interface_handlers.c | 8 ++++ .../editors/interface/interface_intern.h | 1 + .../blender/editors/interface/interface_ops.c | 44 +++++++++++++++++++ .../editors/interface/interface_query.c | 15 +++++++ .../editors/interface/interface_view.cc | 10 +++++ source/blender/editors/interface/tree_view.cc | 13 ++++++ 9 files changed, 109 insertions(+) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index c536eff771d..ddd5e77cbb6 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2783,7 +2783,12 @@ char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle); void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle); +void UI_tree_view_item_context_menu_build(struct bContext *C, + const uiTreeViewItemHandle *item, + uiLayout *column); + uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, int x, int y); +uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region); #ifdef __cplusplus } diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 272439a2ae9..ae85375ed2f 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -234,6 +234,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer { virtual ~AbstractTreeViewItem() = default; virtual void build_row(uiLayout &row) = 0; + virtual void build_context_menu(bContext &C, uiLayout &column) const; virtual void on_activate(); /** diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index bb9e813ea50..d327124484b 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -925,6 +925,18 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev } } + { + const ARegion *region = CTX_wm_region(C); + uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over( + region, event->x, event->y); + if (treerow_but) { + BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW); + UI_tree_view_item_context_menu_build( + C, treerow_but->tree_item, uiLayoutColumn(layout, false)); + uiItemS(layout); + } + } + /* If the button represents an id, it can set the "id" context pointer. */ if (U.experimental.use_extended_asset_browser && ED_asset_can_mark_single_from_context(C)) { ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index bf9b37c00fa..e1f8d18ce35 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -7961,6 +7961,14 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* handle menu */ if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) && (event->val == KM_PRESS)) { + /* For some button types that are typically representing entire sets of data, right-clicking + * to spawn the context menu should also activate the item. This makes it clear which item + * will be operated on. + * Apply the button immediately, so context menu polls get the right active item. */ + if (ELEM(but->type, UI_BTYPE_TREEROW)) { + ui_apply_but(C, but->block, but, but->active, true); + } + /* RMB has two options now */ if (ui_popup_context_menu_for_button(C, but, event)) { return WM_UI_HANDLER_BREAK; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index e7a728efce1..c7781d65058 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1177,6 +1177,7 @@ uiBut *ui_list_row_find_from_index(const struct ARegion *region, const int index, uiBut *listbox) ATTR_WARN_UNUSED_RESULT; uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int x, const int y); +uiBut *ui_tree_row_find_active(const struct ARegion *region); typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region, diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 1fc07bce341..423950d4dbd 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1962,6 +1962,49 @@ static void UI_OT_tree_view_drop(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name UI Tree-View Item Rename Operator + * + * General purpose renaming operator for tree-views. Thanks to this, to add a rename button to + * context menus for example, tree-view API users don't have to implement own renaming operators + * with the same logic as they already have for their #ui::AbstractTreeViewItem::rename() override. + * + * \{ */ + +static bool ui_tree_view_item_rename_poll(bContext *C) +{ + const ARegion *region = CTX_wm_region(C); + const uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region); + return active_item != NULL && UI_tree_view_item_can_rename(active_item); +} + +static int ui_tree_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ARegion *region = CTX_wm_region(C); + uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region); + + UI_tree_view_item_begin_rename(active_item); + ED_region_tag_redraw(region); + + return OPERATOR_FINISHED; +} + +static void UI_OT_tree_view_item_rename(wmOperatorType *ot) +{ + ot->name = "Rename Tree-View Item"; + ot->idname = "UI_OT_tree_view_item_rename"; + ot->description = "Rename the active item in the tree"; + + ot->exec = ui_tree_view_item_rename_exec; + ot->poll = ui_tree_view_item_rename_poll; + /* Could get a custom tooltip via the `get_description()` callback and another overridable + * function of the tree-view. */ + + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Operator & Keymap Registration * \{ */ @@ -1990,6 +2033,7 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_list_start_filter); WM_operatortype_append(UI_OT_tree_view_drop); + WM_operatortype_append(UI_OT_tree_view_item_rename); /* external */ WM_operatortype_append(UI_OT_eyedropper_color); diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 2f6bda3252d..8674f1e435a 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -473,6 +473,21 @@ uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int x, const int return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_treerow, NULL); } +static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata) +{ + if (!ui_but_is_treerow(but, customdata)) { + return false; + } + + const uiButTreeRow *treerow_but = (const uiButTreeRow *)but; + return UI_tree_view_item_is_active(treerow_but->tree_item); +} + +uiBut *ui_tree_row_find_active(const ARegion *region) +{ + return ui_but_find(region, ui_but_is_active_treerow, NULL); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc index 8122b965892..fdd8eb0cc71 100644 --- a/source/blender/editors/interface/interface_view.cc +++ b/source/blender/editors/interface/interface_view.cc @@ -94,6 +94,16 @@ uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, return tree_row_but->tree_item; } +uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region) +{ + uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_active(region); + if (!tree_row_but) { + return nullptr; + } + + return tree_row_but->tree_item; +} + static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view) { /* First get the idname the of the view we're looking for. */ diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 3f66810b7f6..3f3a8c5bce5 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -369,6 +369,11 @@ bool AbstractTreeViewItem::rename(StringRefNull new_name) return true; } +void AbstractTreeViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const +{ + /* No context menu by default. */ +} + void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) { is_open_ = old.is_open_; @@ -707,3 +712,11 @@ void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle) AbstractTreeViewItem &item = reinterpret_cast(*item_handle); item.begin_renaming(); } + +void UI_tree_view_item_context_menu_build(bContext *C, + const uiTreeViewItemHandle *item_handle, + uiLayout *column) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_handle); + item.build_context_menu(*C, *column); +} From 8f8982d57c7a022a4040169e19d1943da62c42f9 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 8 Oct 2021 20:09:42 +0200 Subject: [PATCH 0651/1500] Asset Browser: Context menu for catalogs The context menu is a standard way to expose operations of the clicked item to the user. They expect it to be there, and we can make use of it as a place to put more advanced operations in. The menu contains: * New Catalog * Delete Catalog * Rename Also removes the 'x' icon to delete a catalog from the right side of a row. This was just placed there temporarily until the context menu is there. It's too easy to accidentally delete catalogs with this. --- .../space_file/asset_catalog_tree_view.cc | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 28d64cfca60..3407be9d8cd 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -94,6 +94,7 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { void on_activate() override; void build_row(uiLayout &row) override; + void build_context_menu(bContext &C, uiLayout &column) const override; bool can_drop(const wmDrag &drag) const override; std::string drop_tooltip(const bContext &C, @@ -225,23 +226,47 @@ void AssetCatalogTreeViewItem::build_row(uiLayout &row) uiButTreeRow *tree_row_but = tree_row_button(); PointerRNA *props; - const CatalogID catalog_id = catalog_item_.get_catalog_id(); props = UI_but_extra_operator_icon_add( (uiBut *)tree_row_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str()); +} - /* Tree items without a catalog ID represent components of catalog paths that are not - * associated with an actual catalog. They exist merely by the presence of a child catalog, and - * thus cannot be deleted themselves. */ - if (!BLI_uuid_is_nil(catalog_id)) { - char catalog_id_str_buffer[UUID_STRING_LEN] = ""; - BLI_uuid_format(catalog_id_str_buffer, catalog_id); +void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column) const +{ + PointerRNA props; - props = UI_but_extra_operator_icon_add( - (uiBut *)tree_row_but, "ASSET_OT_catalog_delete", WM_OP_INVOKE_DEFAULT, ICON_X); - RNA_string_set(props, "catalog_id", catalog_id_str_buffer); + uiItemFullO(&column, + "ASSET_OT_catalog_new", + "New Catalog", + ICON_NONE, + nullptr, + WM_OP_INVOKE_DEFAULT, + 0, + &props); + RNA_string_set(&props, "parent_path", catalog_item_.catalog_path().c_str()); + + char catalog_id_str_buffer[UUID_STRING_LEN] = ""; + BLI_uuid_format(catalog_id_str_buffer, catalog_item_.get_catalog_id()); + uiItemFullO(&column, + "ASSET_OT_catalog_delete", + "Delete Catalog", + ICON_NONE, + nullptr, + WM_OP_INVOKE_DEFAULT, + 0, + &props); + RNA_string_set(&props, "catalog_id", catalog_id_str_buffer); + uiItemO(&column, "Rename", ICON_NONE, "UI_OT_tree_view_item_rename"); + + /* Doesn't actually exist right now, but could be defined in Python. Reason that this isn't done + * in Python yet is that catalogs are not exposed in BPY, and we'd somehow pass the clicked on + * catalog to the menu draw callback (via context probably).*/ + MenuType *mt = WM_menutype_find("ASSETBROWSER_MT_catalog_context_menu", true); + if (!mt) { + return; } + UI_menutype_draw(&C, mt, &column); } bool AssetCatalogTreeViewItem::has_droppable_item(const wmDrag &drag) From 886196b88817d1c6c2931d48b835377cf590e9ab Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 8 Oct 2021 15:02:23 -0500 Subject: [PATCH 0652/1500] Fix T92037: Custom property edit issues with integer soft range - Fix a typo that used the max instead of the min for the soft max - Assign the correct "last property type" when the operator starts - Only check values for the soft range when use soft range is turned on --- release/scripts/startup/bl_operators/wm.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 6bf45cc5a15..1ce2beca509 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1582,7 +1582,7 @@ class WM_OT_properties_edit(Operator): min=self.min_int, max=self.max_int, soft_min=self.soft_min_int if self.use_soft_limits else self.min_int, - soft_max=self.soft_max_int if self.use_soft_limits else self.min_int, + soft_max=self.soft_max_int if self.use_soft_limits else self.max_int, step=self.step_int, default=self.default_int[0] if prop_type_new == 'INT' else self.default_int[:self.array_length], description=self.description, @@ -1702,7 +1702,6 @@ class WM_OT_properties_edit(Operator): name = self.property_name self._old_prop_name = [name] - self.last_property_type = self.property_type item = eval("context.%s" % data_path) if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference): @@ -1712,6 +1711,7 @@ class WM_OT_properties_edit(Operator): # Set operator's property type with the type of the existing property, to display the right settings. old_type = self._get_property_type(item, name) self.property_type = old_type + self.last_property_type = old_type # So that the operator can do something for unsupported properties, change the property into # a string, just for editing in the dialog. When the operator executes, it will be converted back @@ -1738,10 +1738,10 @@ class WM_OT_properties_edit(Operator): if self.min_float > self.max_float: self.min_float, self.max_float = self.max_float, self.min_float changed = True - if self.soft_min_float > self.soft_max_float: - self.soft_min_float, self.soft_max_float = self.soft_max_float, self.soft_min_float - changed = True if self.use_soft_limits: + if self.soft_min_float > self.soft_max_float: + self.soft_min_float, self.soft_max_float = self.soft_max_float, self.soft_min_float + changed = True if self.soft_max_float > self.max_float: self.soft_max_float = self.max_float changed = True @@ -1752,10 +1752,10 @@ class WM_OT_properties_edit(Operator): if self.min_int > self.max_int: self.min_int, self.max_int = self.max_int, self.min_int changed = True - if self.soft_min_int > self.soft_max_int: - self.soft_min_int, self.soft_max_int = self.soft_max_int, self.soft_min_int - changed = True if self.use_soft_limits: + if self.soft_min_int > self.soft_max_int: + self.soft_min_int, self.soft_max_int = self.soft_max_int, self.soft_min_int + changed = True if self.soft_max_int > self.max_int: self.soft_max_int = self.max_int changed = True From 728e31e18a1c7075ff9114dc9965d6916c87a2df Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sat, 9 Oct 2021 00:55:11 -0500 Subject: [PATCH 0653/1500] Fix: Typo in UI error message --- source/blender/editors/interface/interface_templates.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 320371ad9ea..458ffd3f053 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2364,7 +2364,7 @@ static eAutoPropButsReturn template_operator_property_buts_draw_single( /* poll() on this operator may still fail, * at the moment there is no nice feedback when this happens just fails silently. */ if (!WM_operator_repeat_check(C, op)) { - UI_block_lock_set(block, true, "Operator can't' redo"); + UI_block_lock_set(block, true, "Operator can't redo"); return return_info; } From 63919496010d4a4ee3b12545da51818a6f5fdc9b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sat, 9 Oct 2021 01:01:44 -0500 Subject: [PATCH 0654/1500] Cleanup: Change variable name, comment formatting --- release/scripts/modules/rna_prop_ui.py | 33 +++++++++++++------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 6d92c94a85c..7da7ccdeddd 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -135,12 +135,12 @@ def rna_idprop_ui_create( def draw(layout, context, context_member, property_type, *, use_edit=True): - def assign_props(prop, val, key): + def assign_props(prop, value, key): prop.data_path = context_member prop.property_name = key try: - prop.value = str(val) + prop.value = str(value) except: pass @@ -176,25 +176,24 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True) - for key, val in items: + for key, value in items: is_rna = (key in rna_properties) - # only show API defined for developers + # Only show API defined properties to developers. if is_rna and not show_developer_ui: continue - to_dict = getattr(val, "to_dict", None) - to_list = getattr(val, "to_list", None) + to_dict = getattr(value, "to_dict", None) + to_list = getattr(value, "to_list", None) - # val_orig = val # UNUSED if to_dict: - val = to_dict() - val_draw = str(val) + value = to_dict() + val_draw = str(value) elif to_list: - val = to_list() - val_draw = str(val) + value = to_list() + val_draw = str(value) else: - val_draw = val + val_draw = value row = layout.row(align=True) box = row.box() @@ -210,10 +209,10 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): row.label(text=key, translate=False) - # explicit exception for arrays. - show_array_ui = to_list and not is_rna and 0 < len(val) <= MAX_DISPLAY_ROWS + # Explicit exception for arrays. + show_array_ui = to_list and not is_rna and 0 < len(value) <= MAX_DISPLAY_ROWS - if show_array_ui and isinstance(val[0], (int, float)): + if show_array_ui and isinstance(value[0], (int, float)): row.prop(rna_item, '["%s"]' % escape_identifier(key), text="") elif to_dict or to_list: row.label(text=val_draw, translate=False) @@ -225,8 +224,8 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): if use_edit: row = split.row(align=True) - # Do not allow editing of overridden properties (we cannot use a poll function of the operators here - # since they's have no access to the specific property...). + # Do not allow editing of overridden properties (we cannot use a poll function + # of the operators here since they's have no access to the specific property). row.enabled = not(is_lib_override and key in rna_item.id_data.override_library.reference) if is_rna: row.label(text="API Defined") From 13df8616ce64a883aa9d44283ca0bfcb2c5c3d13 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sat, 9 Oct 2021 08:32:07 +0200 Subject: [PATCH 0655/1500] VSE: Fix 2D cursor not visible This was caused by confusing naming of frame overlay feature. Correct flag to use is `sseq->flag & SEQ_SHOW_OVERLAY`, not `ed->over_flag & SEQ_EDIT_OVERLAY_SHOW`. --- source/blender/editors/space_sequencer/space_sequencer.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 87344a38c26..25a08abdabc 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -781,15 +781,16 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) SpaceSeq *sseq = area->spacedata.first; Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); - const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && - (sseq->flag & SEQ_SHOW_OVERLAY)); + const bool draw_overlay = sseq->flag & SEQ_SHOW_OVERLAY; + const bool draw_frame_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && + draw_overlay); const bool is_playing = ED_screen_animation_playing(wm); - if (!draw_overlay || sseq->overlay_type != SEQ_DRAW_OVERLAY_REFERENCE) { + if (!draw_frame_overlay || sseq->overlay_type != SEQ_DRAW_OVERLAY_REFERENCE) { sequencer_draw_preview(C, scene, region, sseq, scene->r.cfra, 0, false, false); } - if (draw_overlay && sseq->overlay_type != SEQ_DRAW_OVERLAY_CURRENT) { + if (draw_frame_overlay && sseq->overlay_type != SEQ_DRAW_OVERLAY_CURRENT) { int over_cfra; if (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) { From 27ac80f068a94acbd1c04df45db00787da7299d3 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sat, 9 Oct 2021 08:37:14 +0200 Subject: [PATCH 0656/1500] VSE: rename flag for Frame Overlay feature Rename `SEQ_EDIT_OVERLAY_SHOW` to `SEQ_EDIT_USE_FRAME_OVERLAY` to avoid confusion between `SEQ_SHOW_OVERLAY` of `SpaceSeq.flag` --- source/blender/editors/space_sequencer/sequencer_buttons.c | 2 +- source/blender/editors/space_sequencer/sequencer_draw.c | 2 +- source/blender/editors/space_sequencer/space_sequencer.c | 2 +- source/blender/makesdna/DNA_sequence_types.h | 2 +- source/blender/makesrna/intern/rna_sequencer.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index 1e0ecfd890e..f0940cd9f55 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -78,7 +78,7 @@ static void metadata_panel_context_draw(const bContext *C, Panel *panel) SpaceSeq *space_sequencer = CTX_wm_space_seq(C); /* NOTE: We can only reliably show metadata for the original (current) * frame when split view is used. */ - const bool show_split = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && + const bool show_split = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_USE_FRAME_OVERLAY) && (space_sequencer->mainb == SEQ_DRAW_IMG_IMBUF)); if (show_split && space_sequencer->overlay_type == SEQ_DRAW_OVERLAY_REFERENCE) { return; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 6e4c3c774a2..922b6b251ed 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -2807,7 +2807,7 @@ void draw_timeline_seq_display(const bContext *C, ARegion *region) if (scene->ed != NULL) { UI_view2d_view_ortho(v2d); draw_cache_view(C); - if (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) { + if (scene->ed->over_flag & SEQ_EDIT_USE_FRAME_OVERLAY) { draw_overlap_frame_indicator(scene, v2d); } UI_view2d_view_restore(C); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 25a08abdabc..72db47c0a76 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -782,7 +782,7 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); const bool draw_overlay = sseq->flag & SEQ_SHOW_OVERLAY; - const bool draw_frame_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && + const bool draw_frame_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_USE_FRAME_OVERLAY) && draw_overlay); const bool is_playing = ED_screen_animation_playing(wm); diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index a71f86eae9f..e363ed5ddfd 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -503,7 +503,7 @@ typedef struct SequencerScopes { #define SELECT 1 /* Editor->over_flag */ -#define SEQ_EDIT_OVERLAY_SHOW 1 +#define SEQ_EDIT_USE_FRAME_OVERLAY 1 #define SEQ_EDIT_OVERLAY_ABS 2 #define SEQ_STRIP_OFSBOTTOM 0.05f diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 08dc01ff45b..c9bcd5e0a0d 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2114,7 +2114,7 @@ static void rna_def_editor(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Active Strip", "Sequencer's active strip"); prop = RNA_def_property(srna, "show_overlay", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "over_flag", SEQ_EDIT_OVERLAY_SHOW); + RNA_def_property_boolean_sdna(prop, NULL, "over_flag", SEQ_EDIT_USE_FRAME_OVERLAY); RNA_def_property_ui_text( prop, "Show Overlay", "Partial overlay on top of the sequencer with a frame offset"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); From e541f37529ac2e9fb2ae7ae2f920f304f70dc1ae Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sat, 9 Oct 2021 09:53:42 +0200 Subject: [PATCH 0657/1500] Fix T91978: VSE box select substract doesn't work Substract and add modes were not implemented. Add logic to handle these modes. --- .../editors/space_sequencer/sequencer_select.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 7722909190f..e193cde4535 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -1482,7 +1482,7 @@ static bool seq_box_select_rect_image_isect(const Scene *scene, const Sequence * seq_image_quad[3], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]); } -static void seq_box_select_seq_from_preview(const bContext *C, rctf *rect) +static void seq_box_select_seq_from_preview(const bContext *C, rctf *rect, const eSelectOp mode) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); @@ -1492,9 +1492,17 @@ static void seq_box_select_seq_from_preview(const bContext *C, rctf *rect) SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown); Sequence *seq; SEQ_ITERATOR_FOREACH (seq, strips) { - if (seq_box_select_rect_image_isect(scene, seq, rect)) { + if (!seq_box_select_rect_image_isect(scene, seq, rect)) { + continue; + } + + if (ELEM(mode, SEL_OP_ADD, SEL_OP_SET)) { seq->flag |= SELECT; } + else { + BLI_assert(mode == SEL_OP_SUB); + seq->flag &= ~SELECT; + } } SEQ_collection_free(strips); @@ -1524,7 +1532,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) ARegion *region = CTX_wm_region(C); if (region->regiontype == RGN_TYPE_PREVIEW) { - seq_box_select_seq_from_preview(C, &rectf); + seq_box_select_seq_from_preview(C, &rectf, sel_op); sequencer_select_do_updates(C, scene); return OPERATOR_FINISHED; } @@ -1664,7 +1672,8 @@ static const EnumPropertyItem sequencer_prop_select_grouped_types[] = { "EFFECT_LINK", 0, "Effect/Linked", - "Other strips affected by the active one (sharing some time, and below or effect-assigned)"}, + "Other strips affected by the active one (sharing some time, and below or " + "effect-assigned)"}, {SEQ_SELECT_GROUP_OVERLAP, "OVERLAP", 0, "Overlap", "Overlapping time"}, {0, NULL, 0, NULL, NULL}, }; From 2561145da8d1e6db6617fa67c5a306d6e07e34e5 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sat, 9 Oct 2021 10:11:10 +0200 Subject: [PATCH 0658/1500] Fix T91096: VSE use after free with overwrite enabled Strip was flagged for deletion in `seq_transform_handle_overwrite()` on `STRIP_OVERLAP_IS_FULL`. Then it is removed in `SEQ_edit_strip_split()` before it should be. Handle `STRIP_OVERLAP_IS_FULL` in separate loop. This may not be complete solution, because in example file overlap is caused between 2 transformed strips and one that is "static". Such operation should not be possible in first place. This fixes the crash at lest, so improvement in behavior can be handled separately. Differential Revision: https://developer.blender.org/D12751 --- .../transform/transform_convert_sequencer.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index bf320595d6c..70089164d8a 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -458,6 +458,7 @@ static void seq_transform_handle_overwrite_split(const TransInfo *t, SEQ_edit_strip_split( bmain, scene, seqbase, split_strip, transformed->enddisp, SEQ_SPLIT_SOFT, NULL); SEQ_edit_flag_for_removal(scene, seqbase_active_get(t), split_strip); + SEQ_edit_remove_flagged_sequences(t->scene, seqbase_active_get(t)); } /* Trim strips by adjusting handle position. @@ -498,8 +499,8 @@ static void seq_transform_handle_overwrite_trim(const TransInfo *t, static void seq_transform_handle_overwrite(const TransInfo *t, SeqCollection *transformed_strips) { SeqCollection *targets = query_overwrite_targets(t, transformed_strips); + SeqCollection *strips_to_delete = SEQ_collection_create(__func__); - bool strips_delete = false; Sequence *target; Sequence *transformed; SEQ_ITERATOR_FOREACH (target, targets) { @@ -511,13 +512,10 @@ static void seq_transform_handle_overwrite(const TransInfo *t, SeqCollection *tr const eOvelapDescrition overlap = overlap_description_get(transformed, target); if (overlap == STRIP_OVERLAP_IS_FULL) { - /* Remove covered strip. */ - SEQ_edit_flag_for_removal(t->scene, seqbase_active_get(t), target); - strips_delete = true; + SEQ_collection_append_strip(target, strips_to_delete); } else if (overlap == STRIP_OVERLAP_IS_INSIDE) { seq_transform_handle_overwrite_split(t, transformed, target); - strips_delete = true; } else if (ELEM(overlap, STRIP_OVERLAP_LEFT_SIDE, STRIP_OVERLAP_RIGHT_SIDE)) { seq_transform_handle_overwrite_trim(t, transformed, target, overlap); @@ -527,9 +525,16 @@ static void seq_transform_handle_overwrite(const TransInfo *t, SeqCollection *tr SEQ_collection_free(targets); - if (strips_delete) { + /* Remove covered strips. This must be done in separate loop, because `SEQ_edit_strip_split()` + * also uses `SEQ_edit_remove_flagged_sequences()`. See T91096. */ + if (SEQ_collection_len(strips_to_delete) > 0) { + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips_to_delete) { + SEQ_edit_flag_for_removal(t->scene, seqbase_active_get(t), seq); + } SEQ_edit_remove_flagged_sequences(t->scene, seqbase_active_get(t)); } + SEQ_collection_free(strips_to_delete); } static void seq_transform_handle_overlap_shuffle(const TransInfo *t, From 79425ed3267663ee482ee3ffcc542d86888103af Mon Sep 17 00:00:00 2001 From: Jarrett Johnson Date: Sat, 9 Oct 2021 14:40:37 -0500 Subject: [PATCH 0659/1500] Geometry Nodes: Align Euler to Vector Node This commit introduces the Align Euler to Vector function node which rotates to a body into a given direction. The node replaces the legacy "Align Rotation to Vector" node, which only worked on an attribute named `rotation` internally. The "Euler" in the name is meant to make it clearer that the rotation isn't interchangeable with a regular vector. Addresses T91374. Differential Revision: https://developer.blender.org/D12726 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesdna/DNA_node_types.h | 13 ++ source/blender/makesrna/intern/rna_nodetree.c | 60 +++++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_function.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_fn_align_euler_to_vector.cc | 215 ++++++++++++++++++ 9 files changed, 294 insertions(+) create mode 100644 source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 923ea22e1e3..155fa59c315 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -630,6 +630,7 @@ geometry_node_categories = [ NodeItem("FunctionNodeFloatToInt"), NodeItem("GeometryNodeSwitch"), NodeItem("FunctionNodeRandomValue"), + NodeItem("FunctionNodeAlignEulerToVector"), ]), GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ NodeItem("ShaderNodeTexNoise"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 79ae9d71762..447e0268701 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1533,6 +1533,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_INPUT_SPECIAL_CHARACTERS 1213 #define FN_NODE_RANDOM_VALUE 1214 #define FN_NODE_ROTATE_EULER 1215 +#define FN_NODE_ALIGN_EULER_TO_VECTOR 1216 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 75b9d07ca98..cad5d4eff41 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5818,6 +5818,7 @@ static void registerFunctionNodes() { register_node_type_fn_legacy_random_float(); + register_node_type_fn_align_euler_to_vector(); register_node_type_fn_boolean_math(); register_node_type_fn_float_compare(); register_node_type_fn_float_to_int(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index edbb0070462..cbfa4e702ea 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -2075,6 +2075,19 @@ typedef enum GeometryNodeAlignRotationToVectorPivotAxis { GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Z = 3, } GeometryNodeAlignRotationToVectorPivotAxis; +typedef enum NodeAlignEulerToVectorAxis { + FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_X = 0, + FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Y = 1, + FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Z = 2, +} NodeAlignEulerToVectorAxis; + +typedef enum NodeAlignEulerToVectorPivotAxis { + FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO = 0, + FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_X = 1, + FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Y = 2, + FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Z = 3, +} NodeAlignEulerToVectorPivotAxis; + typedef enum GeometryNodeTransformSpace { GEO_NODE_TRANSFORM_SPACE_ORIGINAL = 0, GEO_NODE_TRANSFORM_SPACE_RELATIVE = 1, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 5e997f81753..cab420ba990 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9952,6 +9952,66 @@ static void def_geo_align_rotation_to_vector(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_fn_align_euler_to_vector(StructRNA *srna) +{ + static const EnumPropertyItem axis_items[] = { + {FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_X, + "X", + ICON_NONE, + "X", + "Align the X axis with the vector"}, + {FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Y, + "Y", + ICON_NONE, + "Y", + "Align the Y axis with the vector"}, + {FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Z, + "Z", + ICON_NONE, + "Z", + "Align the Z axis with the vector"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem pivot_axis_items[] = { + {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO, + "AUTO", + ICON_NONE, + "Auto", + "Automatically detect the best rotation axis to rotate towards the vector"}, + {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_X, + "X", + ICON_NONE, + "X", + "Rotate around the local X axis"}, + {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Y, + "Y", + ICON_NONE, + "Y", + "Rotate around the local Y axis"}, + {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Z, + "Z", + ICON_NONE, + "Z", + "Rotate around the local Z axis"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, axis_items); + RNA_def_property_ui_text(prop, "Axis", "Axis to align to the vector"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "pivot_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom2"); + RNA_def_property_enum_items(prop, pivot_axis_items); + RNA_def_property_ui_text(prop, "Pivot Axis", "Axis to rotate around"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_point_scale(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index a4350c10087..78a9bb72e26 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -135,6 +135,7 @@ set(SRC function/nodes/legacy/node_fn_random_float.cc + function/nodes/node_fn_align_euler_to_vector.cc function/nodes/node_fn_boolean_math.cc function/nodes/node_fn_float_compare.cc function/nodes/node_fn_float_to_int.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 450e999bea4..395a2d68b53 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -22,6 +22,7 @@ extern "C" { void register_node_type_fn_legacy_random_float(void); +void register_node_type_fn_align_euler_to_vector(void); void register_node_type_fn_boolean_math(void); void register_node_type_fn_float_compare(void); void register_node_type_fn_float_to_int(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 7e10b4055fd..80468adf3bc 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -265,6 +265,7 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", LegacyRandomFloat, "Random Float", "") +DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc new file mode 100644 index 00000000000..4c741c96bb8 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc @@ -0,0 +1,215 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_align_euler_to_vector_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input("Rotation").subtype(PROP_EULER).hide_value(); + b.add_input("Factor").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Vector").default_value({0.0, 0.0, 1.0}); + b.add_output("Rotation").subtype(PROP_EULER); +} + +static void fn_node_align_euler_to_vector_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "pivot_axis", 0, IFACE_("Pivot"), ICON_NONE); +} + +static void align_rotations_auto_pivot(IndexMask mask, + const VArray &input_rotations, + const VArray &vectors, + const VArray &factors, + const float3 local_main_axis, + const MutableSpan output_rotations) +{ + threading::parallel_for(mask.index_range(), 512, [&](IndexRange mask_range) { + for (const int maski : mask_range) { + const int64_t i = mask[maski]; + const float3 vector = vectors[i]; + if (is_zero_v3(vector)) { + output_rotations[i] = input_rotations[i]; + } + + float old_rotation[3][3]; + eul_to_mat3(old_rotation, input_rotations[i]); + float3 old_axis; + mul_v3_m3v3(old_axis, old_rotation, local_main_axis); + + const float3 new_axis = vector.normalized(); + float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis); + if (is_zero_v3(rotation_axis)) { + /* The vectors are linearly dependent, so we fall back to another axis. */ + rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0)); + if (is_zero_v3(rotation_axis)) { + /* This is now guaranteed to not be zero. */ + rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0)); + } + } + + const float full_angle = angle_normalized_v3v3(old_axis, new_axis); + const float angle = factors[i] * full_angle; + + float rotation[3][3]; + axis_angle_to_mat3(rotation, rotation_axis, angle); + + float new_rotation_matrix[3][3]; + mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); + + float3 new_rotation; + mat3_to_eul(new_rotation, new_rotation_matrix); + + output_rotations[i] = new_rotation; + } + }); +} + +static void align_rotations_fixed_pivot(IndexMask mask, + const VArray &input_rotations, + const VArray &vectors, + const VArray &factors, + const float3 local_main_axis, + const float3 local_pivot_axis, + const MutableSpan output_rotations) +{ + threading::parallel_for(mask.index_range(), 512, [&](IndexRange mask_range) { + for (const int64_t maski : mask_range) { + const int64_t i = mask[maski]; + if (local_main_axis == local_pivot_axis) { + /* Can't compute any meaningful rotation angle in this case. */ + output_rotations[i] = input_rotations[i]; + } + + const float3 vector = vectors[i]; + if (is_zero_v3(vector)) { + continue; + } + + float old_rotation[3][3]; + eul_to_mat3(old_rotation, input_rotations[i]); + float3 old_axis; + mul_v3_m3v3(old_axis, old_rotation, local_main_axis); + float3 pivot_axis; + mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis); + + float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis); + if (full_angle > M_PI) { + /* Make sure the point is rotated as little as possible. */ + full_angle -= 2.0f * M_PI; + } + const float angle = factors[i] * full_angle; + + float rotation[3][3]; + axis_angle_to_mat3(rotation, pivot_axis, angle); + + float new_rotation_matrix[3][3]; + mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); + + float3 new_rotation; + mat3_to_eul(new_rotation, new_rotation_matrix); + + output_rotations[i] = new_rotation; + } + }); +} + +class MF_AlignEulerToVector : public fn::MultiFunction { + private: + int main_axis_mode_; + int pivot_axis_mode_; + + public: + MF_AlignEulerToVector(int main_axis_mode, int pivot_axis_mode) + : main_axis_mode_(main_axis_mode), pivot_axis_mode_(pivot_axis_mode) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Align Euler To Vector"}; + signature.single_input("Rotation"); + signature.single_input("Factor"); + signature.single_input("Vector"); + + signature.single_output("Rotation"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &input_rotations = params.readonly_single_input(0, "Rotation"); + const VArray &factors = params.readonly_single_input(1, "Factor"); + const VArray &vectors = params.readonly_single_input(2, "Vector"); + + auto output_rotations = params.uninitialized_single_output(3, "Rotation"); + + float3 local_main_axis = {0.0f, 0.0f, 0.0f}; + local_main_axis[main_axis_mode_] = 1; + + if (pivot_axis_mode_ == FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO) { + align_rotations_auto_pivot( + mask, input_rotations, vectors, factors, local_main_axis, output_rotations); + } + else { + float3 local_pivot_axis = {0.0f, 0.0f, 0.0f}; + local_pivot_axis[main_axis_mode_] = 1; + align_rotations_fixed_pivot(mask, + input_rotations, + vectors, + factors, + local_main_axis, + local_pivot_axis, + output_rotations); + } + } +}; + +static void fn_node_align_euler_to_vector_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + builder.construct_and_set_matching_fn(node.custom1, node.custom2); +} + +} // namespace blender::nodes + +void register_node_type_fn_align_euler_to_vector() +{ + static bNodeType ntype; + + fn_node_type_base( + &ntype, FN_NODE_ALIGN_EULER_TO_VECTOR, "Align Euler to Vector", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_align_euler_to_vector_declare; + ntype.draw_buttons = blender::nodes::fn_node_align_euler_to_vector_layout; + ntype.build_multi_function = blender::nodes::fn_node_align_euler_to_vector_build_multi_function; + nodeRegisterType(&ntype); +} From 1bfa9539d3b50da2530c6273d2f692fb57f9c267 Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Sun, 10 Oct 2021 19:54:02 -0400 Subject: [PATCH 0660/1500] Fix T91889 Exact boolean sometimes drops triangles. The problem is that the fast triangulator (based on polyfill) sometimes makes degenerate triangles. Commit 8115f0c5bd91f had a check for degenerate triangles but it wasn't thorough enough. This commit uses a more thorough (and pessimistic) test for degenerate triangles, using the exact triangulator in those cases. --- .../blender/blenlib/intern/mesh_intersect.cc | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 526871c7b1f..8276890eec1 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -2225,8 +2225,7 @@ static bool face_is_degenerate(const Face *f) return false; } -/** Fast check for degenerate tris. Only tests for when verts are identical, - * not cases where there are zero-length edges. */ +/** Fast check for degenerate tris. It is OK if it returns true for nearly degenerate triangles. */ static bool any_degenerate_tris_fast(const Array triangulation) { for (const Face *f : triangulation) { @@ -2236,6 +2235,23 @@ static bool any_degenerate_tris_fast(const Array triangulation) if (v0 == v1 || v0 == v2 || v1 == v2) { return true; } + double3 da = v2->co - v0->co; + double3 db = v2->co - v1->co; + double da_length_squared = da.length_squared(); + double db_length_squared = db.length_squared(); + if (da_length_squared == 0.0 || db_length_squared == 0.0) { + return true; + } + /* |da x db| = |da| |db| sin t, where t is angle between them. + * The triangle is almost degenerate if sin t is almost 0. + * sin^2 t = |da x db|^2 / (|da|^2 |db|^2) + */ + double3 dab = double3::cross_high_precision(da, db); + double dab_length_squared = dab.length_squared(); + double sin_squared_t = dab_length_squared / (da_length_squared * db_length_squared); + if (sin_squared_t < 1e-8) { + return true; + } } return false; } From d2454487d1ee97caa270478aa0ef0bc8e5471ea1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 10 Oct 2021 20:53:56 -0500 Subject: [PATCH 0661/1500] Fix: Incorrect custom property edit string to int change This fixed an error in a corner case, and is a reasonable check anyway. --- release/scripts/startup/bl_operators/wm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 1ce2beca509..17552b6e013 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1419,7 +1419,7 @@ class WM_OT_properties_edit(Operator): # Helper method to avoid repetative code to retrieve a single value from sequences and non-sequences. @staticmethod def _convert_new_value_single(old_value, new_type): - if hasattr(old_value, "__len__"): + if hasattr(old_value, "__len__") and len(old_value) > 0: return new_type(old_value[0]) return new_type(old_value) From fef4dc7269099eeac452a14e68c475357d12ee6e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 11 Oct 2021 12:53:52 +1100 Subject: [PATCH 0662/1500] Fix starting Blender with Python 3.10 URL presets weren't working, raising a Python exception when the splash screen was displayed. --- release/scripts/startup/bl_operators/wm.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 17552b6e013..280df736c18 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -979,6 +979,12 @@ class WM_OT_url_open(Operator): return {'FINISHED'} +# NOTE: needed for Python 3.10 since there are name-space issues with annotations. +# This can be moved into the class as a static-method once Python 3.9x is dropped. +def _wm_url_open_preset_type_items(_self, _context): + return [item for (item, _) in WM_OT_url_open_preset.preset_items] + + class WM_OT_url_open_preset(Operator): """Open a preset website in the web browser""" bl_idname = "wm.url_open_preset" @@ -987,9 +993,7 @@ class WM_OT_url_open_preset(Operator): type: EnumProperty( name="Site", - items=lambda self, _context: ( - item for (item, _) in WM_OT_url_open_preset.preset_items - ), + items=_wm_url_open_preset_type_items, ) id: StringProperty( From bdd2a7f46646e266bfa7d926fbbffbf938c781fc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 11 Oct 2021 15:33:42 +1100 Subject: [PATCH 0663/1500] Doc: expand on docstring for PyC_Long_AsBool --- source/blender/python/generic/py_capi_utils.c | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 9dac5a4f21f..e847a5a5b5c 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -1649,7 +1649,30 @@ bool PyC_RunString_AsString(const char *imports[], #endif /** - * Don't use `bool` return type, so -1 can be used as an error value. + * + * Comparison with #PyObject_IsTrue + * ================================ + * + * Even though Python provides a way to retrieve the boolean value for an object, + * in many cases it's far too relaxed, with the following examples coercing values. + * + * \code{.py} + * data.value = "Text" # True. + * data.value = "" # False. + * data.value = {1, 2} # True + * data.value = {} # False. + * data.value = None # False. + * \endcode + * + * In practice this is often a mistake by the script author that doesn't behave as they expect. + * So it's better to be more strict for attribute assignment and function arguments, + * only accepting True/False 0/1. + * + * If coercing a value is desired, it can be done explicitly: `data.value = bool(value)` + * + * \see #PyC_ParseBool for use with #PyArg_ParseTuple and related functions. + * + * \note Don't use `bool` return type, so -1 can be used as an error value. */ int PyC_Long_AsBool(PyObject *value) { From a282efecbc1487fdb3156fb829ea3170c39e14a7 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 11 Oct 2021 09:40:22 +0200 Subject: [PATCH 0664/1500] Cleanup: Add const keyword to `BKE_packedfile_id_check`. --- source/blender/blenkernel/BKE_image.h | 2 +- source/blender/blenkernel/BKE_packedFile.h | 4 ++-- source/blender/blenkernel/intern/image.c | 2 +- source/blender/blenkernel/intern/packedFile.c | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 09463246e27..82d1f299b4b 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -355,7 +355,7 @@ bool BKE_image_is_dirty_writable(struct Image *image, bool *is_format_writable); /* Guess offset for the first frame in the sequence */ int BKE_image_sequence_guess_offset(struct Image *image); bool BKE_image_has_anim(struct Image *image); -bool BKE_image_has_packedfile(struct Image *image); +bool BKE_image_has_packedfile(const struct Image *image); bool BKE_image_has_filepath(struct Image *ima); bool BKE_image_is_animated(struct Image *image); bool BKE_image_has_multiple_ibufs(struct Image *image); diff --git a/source/blender/blenkernel/BKE_packedFile.h b/source/blender/blenkernel/BKE_packedFile.h index 8cb0c78d9aa..8ddf77e3d49 100644 --- a/source/blender/blenkernel/BKE_packedFile.h +++ b/source/blender/blenkernel/BKE_packedFile.h @@ -121,8 +121,8 @@ int BKE_packedfile_seek(struct PackedFile *pf, int offset, int whence); void BKE_packedfile_rewind(struct PackedFile *pf); int BKE_packedfile_read(struct PackedFile *pf, void *data, int size); -/* ID should be not NULL, return 1 if there's a packed file */ -bool BKE_packedfile_id_check(struct ID *id); +/* ID should be not NULL, return true if there's a packed file */ +bool BKE_packedfile_id_check(const struct ID *id); /* ID should be not NULL, throws error when ID is Library */ void BKE_packedfile_id_unpack(struct Main *bmain, struct ID *id, diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 2a0f8a597d1..5ae338aaaeb 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -5749,7 +5749,7 @@ bool BKE_image_has_anim(Image *ima) return (BLI_listbase_is_empty(&ima->anims) == false); } -bool BKE_image_has_packedfile(Image *ima) +bool BKE_image_has_packedfile(const Image *ima) { return (BLI_listbase_is_empty(&ima->packedfiles) == false); } diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index 95763b0da54..f0f8343420d 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -805,27 +805,27 @@ void BKE_packedfile_unpack_all(Main *bmain, ReportList *reports, enum ePF_FileSt } /* ID should be not NULL, return 1 if there's a packed file */ -bool BKE_packedfile_id_check(ID *id) +bool BKE_packedfile_id_check(const ID *id) { switch (GS(id->name)) { case ID_IM: { - Image *ima = (Image *)id; + const Image *ima = (const Image *)id; return BKE_image_has_packedfile(ima); } case ID_VF: { - VFont *vf = (VFont *)id; + const VFont *vf = (const VFont *)id; return vf->packedfile != NULL; } case ID_SO: { - bSound *snd = (bSound *)id; + const bSound *snd = (const bSound *)id; return snd->packedfile != NULL; } case ID_VO: { - Volume *volume = (Volume *)id; + const Volume *volume = (const Volume *)id; return volume->packedfile != NULL; } case ID_LI: { - Library *li = (Library *)id; + const Library *li = (const Library *)id; return li->packedfile != NULL; } default: From 813ca82f1ec00af6c570be9504f5ffeea56fdfef Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 11 Oct 2021 09:32:29 +0200 Subject: [PATCH 0665/1500] Fix T92080: Background of Node editors appear brighter than before In the original code depth=0 meant that there was no parents. But with BLI_listbase_count we have depth 1 in those cases. Differential Revision: https://developer.blender.org/D12817 --- source/blender/editors/space_node/node_draw.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index c864f6c34d6..4332302159c 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2181,10 +2181,15 @@ static void draw_nodetree(const bContext *C, */ static void draw_background_color(const SpaceNode *snode) { - const int max_depth = 2; + const int max_tree_length = 3; const float bright_factor = 0.25f; - const int depth = BLI_listbase_count_at_most(&snode->treepath, max_depth); + /* We ignore the first element of the path since it is the top-most tree and it doesn't need to + * be brighter. We also set a cap to how many levels we want to set apart, to avoid the + * background from getting too bright. */ + const int clamped_tree_path_length = BLI_listbase_count_at_most(&snode->treepath, + max_tree_length); + const int depth = max_ii(0, clamped_tree_path_length - 1); float color[3]; UI_GetThemeColor3fv(TH_BACK, color); From 6e92a2d5914d3f198ec16236638dfc04496da209 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 11 Oct 2021 10:10:54 +0200 Subject: [PATCH 0666/1500] Cleanup: make format (VSE) --- source/blender/editors/space_sequencer/space_sequencer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 72db47c0a76..3ed8b35c9bf 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -782,7 +782,8 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); const bool draw_overlay = sseq->flag & SEQ_SHOW_OVERLAY; - const bool draw_frame_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_USE_FRAME_OVERLAY) && + const bool draw_frame_overlay = (scene->ed && + (scene->ed->over_flag & SEQ_EDIT_USE_FRAME_OVERLAY) && draw_overlay); const bool is_playing = ED_screen_animation_playing(wm); From a6d34f4c3f2a86af1cabf75c89e85a35d38073c3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 11 Oct 2021 19:59:06 +1100 Subject: [PATCH 0667/1500] Cleanup: add utility functions to parse gizmos and target properties --- source/blender/python/intern/bpy_rna_gizmo.c | 209 +++++++++++++------ 1 file changed, 141 insertions(+), 68 deletions(-) diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c index f121bfd6e36..5294f56d5c6 100644 --- a/source/blender/python/intern/bpy_rna_gizmo.c +++ b/source/blender/python/intern/bpy_rna_gizmo.c @@ -17,7 +17,7 @@ /** \file * \ingroup pythonintern * - * . + * This file defines utility methods for `bpy.types.Gizmo`. */ #include @@ -43,6 +43,84 @@ #include "bpy_rna.h" +/* -------------------------------------------------------------------- */ +/** \name Parsing Utility Functions + * + * Functions used as callbacks for #PyArg_ParseTuple `O&` format string. + * \{ */ + +struct BPyGizmoWithTarget { + wmGizmo *gz; /* Must be first. */ + wmGizmoProperty *gz_prop; +}; + +struct BPyGizmoWithTargetType { + wmGizmo *gz; /* Must be first. */ + const wmGizmoPropertyType *gz_prop_type; +}; + +static int py_rna_gizmo_parse(PyObject *o, void *p) +{ + /* No type checking (this is `self` not a user defined argument). */ + BLI_assert(BPy_StructRNA_Check(o)); + BLI_assert(((const BPy_StructRNA *)o)->ptr.type == &RNA_Gizmo); + + wmGizmo **gz_p = p; + *gz_p = ((const BPy_StructRNA *)o)->ptr.data; + return 1; +} + +static int py_rna_gizmo_target_id_parse(PyObject *o, void *p) +{ + struct BPyGizmoWithTarget *gizmo_with_target = p; + /* Must be set by `py_rna_gizmo_parse`. */ + wmGizmo *gz = gizmo_with_target->gz; + BLI_assert(gz != NULL); + + if (!PyUnicode_Check(o)) { + PyErr_Format(PyExc_TypeError, "expected a string (got %.200s)", Py_TYPE(o)->tp_name); + return 0; + } + const char *gz_prop_id = PyUnicode_AsUTF8(o); + wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, gz_prop_id); + if (gz_prop == NULL) { + PyErr_Format(PyExc_ValueError, + "Gizmo target property '%s.%s' not found!", + gz->type->idname, + gz_prop_id); + return 0; + } + gizmo_with_target->gz_prop = gz_prop; + return 1; +} + +static int py_rna_gizmo_target_type_id_parse(PyObject *o, void *p) +{ + struct BPyGizmoWithTargetType *gizmo_with_target = p; + /* Must be set first. */ + wmGizmo *gz = gizmo_with_target->gz; + BLI_assert(gz != NULL); + + if (!PyUnicode_Check(o)) { + PyErr_Format(PyExc_TypeError, "expected a string (got %.200s)", Py_TYPE(o)->tp_name); + return 0; + } + const char *gz_prop_id = PyUnicode_AsUTF8(o); + const wmGizmoPropertyType *gz_prop_type = WM_gizmotype_target_property_find(gz->type, + gz_prop_id); + if (gz_prop_type == NULL) { + PyErr_Format(PyExc_ValueError, + "Gizmo target property '%s.%s' not found!", + gz->type->idname, + gz_prop_id); + return 0; + } + gizmo_with_target->gz_prop_type = gz_prop_type; + return 1; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Gizmo Target Property Define API * \{ */ @@ -240,12 +318,10 @@ static PyObject *bpy_gizmo_target_set_handler(PyObject *UNUSED(self), PyObject * const PyGILState_STATE gilstate = PyGILState_Ensure(); struct { - PyObject *self; - char *target; + struct BPyGizmoWithTargetType gz_with_target_type; PyObject *py_fn_slots[BPY_GIZMO_FN_SLOT_LEN]; } params = { - .self = NULL, - .target = NULL, + .gz_with_target_type = {NULL, NULL}, .py_fn_slots = {NULL}, }; @@ -253,29 +329,25 @@ static PyObject *bpy_gizmo_target_set_handler(PyObject *UNUSED(self), PyObject * * 'Gizmo.target_set_prop & target_set_operator' * (see: rna_wm_gizmo_api.c). conventions should match. */ static const char *const _keywords[] = {"self", "target", "get", "set", "range", NULL}; - static _PyArg_Parser _parser = {"Os|$OOO:target_set_handler", _keywords, 0}; + static _PyArg_Parser _parser = {"O&O&|$OOO:target_set_handler", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, - ¶ms.self, - ¶ms.target, + /* `self` */ + py_rna_gizmo_parse, + ¶ms.gz_with_target_type.gz, + /* `target` */ + py_rna_gizmo_target_type_id_parse, + ¶ms.gz_with_target_type, + /* `get/set/range` */ ¶ms.py_fn_slots[BPY_GIZMO_FN_SLOT_GET], ¶ms.py_fn_slots[BPY_GIZMO_FN_SLOT_SET], ¶ms.py_fn_slots[BPY_GIZMO_FN_SLOT_RANGE_GET])) { goto fail; } - wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data; - - const wmGizmoPropertyType *gz_prop_type = WM_gizmotype_target_property_find(gz->type, - params.target); - if (gz_prop_type == NULL) { - PyErr_Format(PyExc_ValueError, - "Gizmo target property '%s.%s' not found", - gz->type->idname, - params.target); - goto fail; - } + wmGizmo *gz = params.gz_with_target_type.gz; + const wmGizmoPropertyType *gz_prop_type = params.gz_with_target_type.gz_prop_type; { const int slots_required = 2; @@ -338,29 +410,27 @@ PyDoc_STRVAR(bpy_gizmo_target_get_value_doc, static PyObject *bpy_gizmo_target_get_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { struct { - PyObject *self; - char *target; + struct BPyGizmoWithTarget gz_with_target; } params = { - .self = NULL, - .target = NULL, + .gz_with_target = {NULL, NULL}, }; static const char *const _keywords[] = {"self", "target", NULL}; - static _PyArg_Parser _parser = {"Os:target_get_value", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, ¶ms.self, ¶ms.target)) { + static _PyArg_Parser _parser = {"O&O&:target_get_value", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + /* `self` */ + py_rna_gizmo_parse, + ¶ms.gz_with_target.gz, + /* `target` */ + py_rna_gizmo_target_id_parse, + ¶ms.gz_with_target)) { goto fail; } - wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data; - - wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, params.target); - if (gz_prop == NULL) { - PyErr_Format(PyExc_ValueError, - "Gizmo target property '%s.%s' not found", - gz->type->idname, - params.target); - goto fail; - } + wmGizmo *gz = params.gz_with_target.gz; + wmGizmoProperty *gz_prop = params.gz_with_target.gz_prop; const int array_len = WM_gizmo_target_property_array_length(gz, gz_prop); switch (gz_prop->type->data_type) { @@ -396,32 +466,31 @@ PyDoc_STRVAR(bpy_gizmo_target_set_value_doc, static PyObject *bpy_gizmo_target_set_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { struct { - PyObject *self; - char *target; + struct BPyGizmoWithTarget gz_with_target; PyObject *value; } params = { - .self = NULL, - .target = NULL, + .gz_with_target = {NULL, NULL}, .value = NULL, }; static const char *const _keywords[] = {"self", "target", "value", NULL}; - static _PyArg_Parser _parser = {"OsO:target_set_value", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast( - args, kw, &_parser, ¶ms.self, ¶ms.target, ¶ms.value)) { + static _PyArg_Parser _parser = {"O&O&O:target_set_value", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + /* `self` */ + py_rna_gizmo_parse, + ¶ms.gz_with_target.gz, + /* `target` */ + py_rna_gizmo_target_id_parse, + ¶ms.gz_with_target, + /* `value` */ + ¶ms.value)) { goto fail; } - wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data; - - wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, params.target); - if (gz_prop == NULL) { - PyErr_Format(PyExc_ValueError, - "Gizmo target property '%s.%s' not found", - gz->type->idname, - params.target); - goto fail; - } + wmGizmo *gz = params.gz_with_target.gz; + wmGizmoProperty *gz_prop = params.gz_with_target.gz_prop; const int array_len = WM_gizmo_target_property_array_length(gz, gz_prop); switch (gz_prop->type->data_type) { @@ -468,29 +537,27 @@ PyDoc_STRVAR(bpy_gizmo_target_get_range_doc, static PyObject *bpy_gizmo_target_get_range(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { struct { - PyObject *self; - char *target; + struct BPyGizmoWithTarget gz_with_target; } params = { - .self = NULL, - .target = NULL, + .gz_with_target = {NULL, NULL}, }; static const char *const _keywords[] = {"self", "target", NULL}; - static _PyArg_Parser _parser = {"Os:target_get_range", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, ¶ms.self, ¶ms.target)) { + static _PyArg_Parser _parser = {"O&O&:target_get_range", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + /* `self` */ + py_rna_gizmo_parse, + ¶ms.gz_with_target.gz, + /* `target` */ + py_rna_gizmo_target_id_parse, + ¶ms.gz_with_target)) { goto fail; } - wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data; - - wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, params.target); - if (gz_prop == NULL) { - PyErr_Format(PyExc_ValueError, - "Gizmo target property '%s.%s' not found", - gz->type->idname, - params.target); - goto fail; - } + wmGizmo *gz = params.gz_with_target.gz; + wmGizmoProperty *gz_prop = params.gz_with_target.gz_prop; switch (gz_prop->type->data_type) { case PROP_FLOAT: { @@ -510,6 +577,10 @@ fail: /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Gizmo Module + * \{ */ + bool BPY_rna_gizmo_module(PyObject *mod_par) { static PyMethodDef method_def_array[] = { @@ -546,3 +617,5 @@ bool BPY_rna_gizmo_module(PyObject *mod_par) return false; } + +/** \} */ From cc6ca1385279f4bd8817c8f23db40b3b43fa402b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 11 Oct 2021 20:06:09 +1100 Subject: [PATCH 0668/1500] Fix T90634: Gizmo.target_set_value() crash without a valid property Raise an exception when target properties have not been set. --- source/blender/python/intern/bpy_rna_gizmo.c | 26 +++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c index 5294f56d5c6..adae68bd7b4 100644 --- a/source/blender/python/intern/bpy_rna_gizmo.c +++ b/source/blender/python/intern/bpy_rna_gizmo.c @@ -94,6 +94,26 @@ static int py_rna_gizmo_target_id_parse(PyObject *o, void *p) return 1; } +static int py_rna_gizmo_target_id_parse_and_ensure_is_valid(PyObject *o, void *p) +{ + if (py_rna_gizmo_target_id_parse(o, p) == 0) { + return 0; + } + struct BPyGizmoWithTarget *gizmo_with_target = p; + wmGizmo *gz = gizmo_with_target->gz; + wmGizmoProperty *gz_prop = gizmo_with_target->gz_prop; + if (!WM_gizmo_target_property_is_valid(gz_prop)) { + const char *gz_prop_id = PyUnicode_AsUTF8(o); + PyErr_Format(PyExc_ValueError, + "Gizmo target property '%s.%s' has not been initialized, " + "Call \"target_set_prop\" first!", + gz->type->idname, + gz_prop_id); + return 0; + } + return 1; +} + static int py_rna_gizmo_target_type_id_parse(PyObject *o, void *p) { struct BPyGizmoWithTargetType *gizmo_with_target = p; @@ -424,7 +444,7 @@ static PyObject *bpy_gizmo_target_get_value(PyObject *UNUSED(self), PyObject *ar py_rna_gizmo_parse, ¶ms.gz_with_target.gz, /* `target` */ - py_rna_gizmo_target_id_parse, + py_rna_gizmo_target_id_parse_and_ensure_is_valid, ¶ms.gz_with_target)) { goto fail; } @@ -482,7 +502,7 @@ static PyObject *bpy_gizmo_target_set_value(PyObject *UNUSED(self), PyObject *ar py_rna_gizmo_parse, ¶ms.gz_with_target.gz, /* `target` */ - py_rna_gizmo_target_id_parse, + py_rna_gizmo_target_id_parse_and_ensure_is_valid, ¶ms.gz_with_target, /* `value` */ ¶ms.value)) { @@ -551,7 +571,7 @@ static PyObject *bpy_gizmo_target_get_range(PyObject *UNUSED(self), PyObject *ar py_rna_gizmo_parse, ¶ms.gz_with_target.gz, /* `target` */ - py_rna_gizmo_target_id_parse, + py_rna_gizmo_target_id_parse_and_ensure_is_valid, ¶ms.gz_with_target)) { goto fail; } From f9755add6544667e04b2b0d39c7c3bc794b6bdb8 Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Mon, 11 Oct 2021 11:09:00 +0100 Subject: [PATCH 0669/1500] Fix T91785: Change max input limit for knife tool angle snapping Patch changes the Knife Tool angle snapping input limit to 180. Differential Revision: https://developer.blender.org/D12728 --- source/blender/editors/mesh/editmesh_knife.c | 24 +++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 64c008acf8e..036a2d9582f 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -97,7 +97,7 @@ #define KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT 30.0f #define KNIFE_MIN_ANGLE_SNAPPING_INCREMENT 0.0f -#define KNIFE_MAX_ANGLE_SNAPPING_INCREMENT 90.0f +#define KNIFE_MAX_ANGLE_SNAPPING_INCREMENT 180.0f typedef struct KnifeColors { uchar line[3]; @@ -1124,7 +1124,7 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k WM_MODALKEY(KNF_MODAL_ANGLE_SNAP_TOGGLE), (kcd->angle >= 0.0f) ? RAD2DEGF(kcd->angle) : 360.0f + RAD2DEGF(kcd->angle), (kcd->angle_snapping_increment > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && - kcd->angle_snapping_increment < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) ? + kcd->angle_snapping_increment <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) ? kcd->angle_snapping_increment : KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT, kcd->angle_snapping ? @@ -3532,9 +3532,9 @@ static bool knife_snap_angle_screen(KnifeTool_OpData *kcd) float dvec[2], dvec_snap[2]; float snap_step; - /* Currently user can input any float between 0 and 90. */ + /* Currently user can input any float between 0 and 180. */ if (kcd->angle_snapping_increment > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && - kcd->angle_snapping_increment < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + kcd->angle_snapping_increment <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { snap_step = DEG2RADF(kcd->angle_snapping_increment); } else { @@ -3688,7 +3688,7 @@ static bool knife_snap_angle_relative(KnifeTool_OpData *kcd) /* Calculate snap step. */ float snap_step; if (kcd->angle_snapping_increment > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && - kcd->angle_snapping_increment < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + kcd->angle_snapping_increment <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { snap_step = DEG2RADF(kcd->angle_snapping_increment); } else { @@ -4363,7 +4363,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) float snapping_increment_temp; if (kcd->angle_snapping) { - if (kcd->num.str_cur >= 2) { + if (kcd->num.str_cur >= 3 || + kcd->angle_snapping_increment > KNIFE_MAX_ANGLE_SNAPPING_INCREMENT / 10) { knife_reset_snap_angle_input(kcd); } knife_update_header(C, op, kcd); /* Update the angle multiple. */ @@ -4371,9 +4372,9 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->val == KM_PRESS && hasNumInput(&kcd->num) && handleNumInput(C, &kcd->num, event)) { handled = true; applyNumInput(&kcd->num, &snapping_increment_temp); - /* Restrict number key input to 0 - 90 degree range. */ + /* Restrict number key input to 0 - 180 degree range. */ if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && - snapping_increment_temp < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { kcd->angle_snapping_increment = snapping_increment_temp; } knife_update_active(C, kcd); @@ -4617,16 +4618,17 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (kcd->angle_snapping) { - if (kcd->num.str_cur >= 2) { + if (kcd->num.str_cur >= 3 || + kcd->angle_snapping_increment > KNIFE_MAX_ANGLE_SNAPPING_INCREMENT / 10) { knife_reset_snap_angle_input(kcd); } if (event->type != EVT_MODAL_MAP) { /* Modal number-input inactive, try to handle numeric inputs last. */ if (!handled && event->val == KM_PRESS && handleNumInput(C, &kcd->num, event)) { applyNumInput(&kcd->num, &snapping_increment_temp); - /* Restrict number key input to 0 - 90 degree range. */ + /* Restrict number key input to 0 - 180 degree range. */ if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && - snapping_increment_temp < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { kcd->angle_snapping_increment = snapping_increment_temp; } knife_update_active(C, kcd); From 0ceded7bc97852564c4d26951b41853e8289925e Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 6 Oct 2021 11:43:58 +0200 Subject: [PATCH 0670/1500] Cycles: Introduce scene host_update function The longer-term goal is to separate host-only scene update from device update: make it possible to make kernel features depend on actual scene state and flags. This change makes it so shaders are compiled before kernel load, making checks like "has_volume" available at the kernel features calculation state. No functional changes are expected at this point. Differential Revision: https://developer.blender.org/D12795 --- intern/cycles/render/osl.cpp | 75 +++++++++++++++++++++------------ intern/cycles/render/osl.h | 2 + intern/cycles/render/scene.cpp | 32 +++++++++++--- intern/cycles/render/scene.h | 4 ++ intern/cycles/render/shader.cpp | 17 ++++++-- intern/cycles/render/shader.h | 3 ++ intern/cycles/render/svm.cpp | 59 ++++++++++++++++---------- intern/cycles/render/svm.h | 11 +++-- 8 files changed, 138 insertions(+), 65 deletions(-) diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 5a43b641872..b6c743ac295 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -91,24 +91,20 @@ void OSLShaderManager::reset(Scene * /*scene*/) shading_system_init(); } -void OSLShaderManager::device_update_specific(Device *device, - DeviceScene *dscene, - Scene *scene, - Progress &progress) +void OSLShaderManager::host_update_specific(Device *device, Scene *scene, Progress &progress) { - if (!need_update()) + if (!need_update()) { return; + } scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { - scene->update_stats->osl.times.add_entry({"device_update", time}); + scene->update_stats->osl.times.add_entry({"host_update", time}); } }); VLOG(1) << "Total " << scene->shaders.size() << " shaders."; - device_free(device, dscene, scene); - /* set texture system */ scene->image_manager->set_osl_texture_system((void *)ts); @@ -116,13 +112,14 @@ void OSLShaderManager::device_update_specific(Device *device, OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); Shader *background_shader = scene->background->get_shader(scene); - foreach (Shader *shader, scene->shaders) { + for (Shader *shader : scene->shaders) { assert(shader->graph); - if (progress.get_cancel()) + if (progress.get_cancel()) { return; + } - /* we can only compile one shader at the time as the OSL ShadingSytem + /* we can only compile one shader at the time as the OSL ShadingSystem * has a single state, but we put the lock here so different renders can * compile shaders alternating */ thread_scoped_lock lock(ss_mutex); @@ -131,30 +128,15 @@ void OSLShaderManager::device_update_specific(Device *device, compiler.background = (shader == background_shader); compiler.compile(og, shader); - if (shader->get_use_mis() && shader->has_surface_emission) + if (shader->get_use_mis() && shader->has_surface_emission) { scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED); + } } - /* setup shader engine */ - og->ss = ss; - og->ts = ts; - og->services = services; - - int background_id = scene->shader_manager->get_shader_id(background_shader); - og->background_state = og->surface_state[background_id & SHADER_MASK]; - og->use = true; - - foreach (Shader *shader, scene->shaders) - shader->clear_modified(); - - update_flags = UPDATE_NONE; - /* add special builtin texture types */ services->textures.insert(ustring("@ao"), new OSLTextureHandle(OSLTextureHandle::AO)); services->textures.insert(ustring("@bevel"), new OSLTextureHandle(OSLTextureHandle::BEVEL)); - device_update_common(device, dscene, scene, progress); - { /* Perform greedyjit optimization. * @@ -172,6 +154,43 @@ void OSLShaderManager::device_update_specific(Device *device, } } +void OSLShaderManager::device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) +{ + if (!need_update()) + return; + + scoped_callback_timer timer([scene](double time) { + if (scene->update_stats) { + scene->update_stats->osl.times.add_entry({"device_update", time}); + } + }); + + device_free(device, dscene, scene); + + OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); + Shader *background_shader = scene->background->get_shader(scene); + + /* Setup shader engine. */ + og->ss = ss; + og->ts = ts; + og->services = services; + + const int background_id = scene->shader_manager->get_shader_id(background_shader); + og->background_state = og->surface_state[background_id & SHADER_MASK]; + og->use = true; + + foreach (Shader *shader, scene->shaders) { + shader->clear_modified(); + } + + update_flags = UPDATE_NONE; + + device_update_common(device, dscene, scene, progress); +} + void OSLShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *scene) { OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h index dfeec54d915..019dca16df7 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/render/osl.h @@ -79,6 +79,8 @@ class OSLShaderManager : public ShaderManager { return true; } + void host_update_specific(Device *device, Scene *scene, Progress &progress) override; + void device_update_specific(Device *device, DeviceScene *dscene, Scene *scene, diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index eeb92122825..e65f542bd2e 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -228,6 +228,22 @@ void Scene::free_memory(bool final) } } +void Scene::host_update(Device *device, Progress &progress) +{ + if (update_stats) { + update_stats->clear(); + } + + scoped_callback_timer timer([this](double time) { + if (update_stats) { + update_stats->scene.times.add_entry({"host_update", time}); + } + }); + + progress.set_status("Updating Shaders"); + shader_manager->host_update(device, this, progress); +} + void Scene::device_update(Device *device_, Progress &progress) { if (!device) @@ -235,10 +251,6 @@ void Scene::device_update(Device *device_, Progress &progress) bool print_stats = need_data_update(); - if (update_stats) { - update_stats->clear(); - } - scoped_callback_timer timer([this, print_stats](double time) { if (update_stats) { update_stats->scene.times.add_entry({"device_update", time}); @@ -537,11 +549,17 @@ bool Scene::update(Progress &progress) return false; } - /* Load render kernels, before device update where we upload data to the GPU. */ + /* Update scene data on the host side. + * Only updates which do not depend on the kernel (including kernel features). */ + progress.set_status("Updating Scene"); + MEM_GUARDED_CALL(&progress, host_update, device, progress); + + /* Load render kernels. After host scene update so that the required kernel features are known. + */ load_kernels(progress, false); - /* Upload scene data to the GPU. */ - progress.set_status("Updating Scene"); + /* Upload scene data to the device. */ + progress.set_status("Updating Scene Device"); MEM_GUARDED_CALL(&progress, device_update, device, progress); return true; diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 001da31e893..83abb4c28db 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -251,6 +251,10 @@ class Scene : public NodeOwner { Scene(const SceneParams ¶ms, Device *device); ~Scene(); + /* NOTE: Device can only use used to access invariant data. For example, OSL globals is valid + * but anything what is related on kernel and kernel features is not. */ + void host_update(Device *device, Progress &progress); + void device_update(Device *device, Progress &progress); bool need_global_attribute(AttributeStandard std); diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index 23786a3390f..cf18b269d4c 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -462,10 +462,7 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth) return id; } -void ShaderManager::device_update(Device *device, - DeviceScene *dscene, - Scene *scene, - Progress &progress) +void ShaderManager::host_update(Device *device, Scene *scene, Progress &progress) { if (!need_update()) { return; @@ -483,6 +480,18 @@ void ShaderManager::device_update(Device *device, assert(scene->default_background->reference_count() != 0); assert(scene->default_empty->reference_count() != 0); + host_update_specific(device, scene, progress); +} + +void ShaderManager::device_update(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) +{ + if (!need_update()) { + return; + } + device_update_specific(device, dscene, scene, progress); } diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index 5f9adea3949..db5e06a715e 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -193,6 +193,9 @@ class ShaderManager { return false; } + void host_update(Device *device, Scene *scene, Progress &progress); + virtual void host_update_specific(Device *device, Scene *scene, Progress &progress) = 0; + /* device update */ void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); virtual void device_update_specific(Device *device, diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index 2379eb775a0..efbc446fc49 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -47,10 +47,10 @@ void SVMShaderManager::reset(Scene * /*scene*/) { } -void SVMShaderManager::device_update_shader(Scene *scene, - Shader *shader, - Progress *progress, - array *svm_nodes) +static void host_compile_shader(Scene *scene, + Shader *shader, + Progress *progress, + array *svm_nodes) { if (progress->get_cancel()) { return; @@ -69,6 +69,32 @@ void SVMShaderManager::device_update_shader(Scene *scene, << summary.full_report(); } +void SVMShaderManager::host_update_specific(Device * /*device*/, Scene *scene, Progress &progress) +{ + if (!need_update()) { + return; + } + + scoped_callback_timer timer([scene](double time) { + if (scene->update_stats) { + scene->update_stats->svm.times.add_entry({"host_update", time}); + } + }); + + const int num_shaders = scene->shaders.size(); + + VLOG(1) << "Total " << num_shaders << " shaders."; + + /* Build all shaders. */ + TaskPool task_pool; + shader_svm_nodes_.resize(num_shaders); + for (int i = 0; i < num_shaders; i++) { + task_pool.push(function_bind( + host_compile_shader, scene, scene->shaders[i], &progress, &shader_svm_nodes_[i])); + } + task_pool.wait_work(); +} + void SVMShaderManager::device_update_specific(Device *device, DeviceScene *dscene, Scene *scene, @@ -92,19 +118,6 @@ void SVMShaderManager::device_update_specific(Device *device, /* test if we need to update */ device_free(device, dscene, scene); - /* Build all shaders. */ - TaskPool task_pool; - vector> shader_svm_nodes(num_shaders); - for (int i = 0; i < num_shaders; i++) { - task_pool.push(function_bind(&SVMShaderManager::device_update_shader, - this, - scene, - scene->shaders[i], - &progress, - &shader_svm_nodes[i])); - } - task_pool.wait_work(); - if (progress.get_cancel()) { return; } @@ -114,7 +127,7 @@ void SVMShaderManager::device_update_specific(Device *device, int svm_nodes_size = num_shaders; for (int i = 0; i < num_shaders; i++) { /* Since we're not copying the local jump node, the size ends up being one node lower. */ - svm_nodes_size += shader_svm_nodes[i].size() - 1; + svm_nodes_size += shader_svm_nodes_[i].size() - 1; } int4 *svm_nodes = dscene->svm_nodes.alloc(svm_nodes_size); @@ -132,22 +145,22 @@ void SVMShaderManager::device_update_specific(Device *device, * Each compiled shader starts with a jump node that has offsets local * to the shader, so copy those and add the offset into the global node list. */ int4 &global_jump_node = svm_nodes[shader->id]; - int4 &local_jump_node = shader_svm_nodes[i][0]; + int4 &local_jump_node = shader_svm_nodes_[i][0]; global_jump_node.x = NODE_SHADER_JUMP; global_jump_node.y = local_jump_node.y - 1 + node_offset; global_jump_node.z = local_jump_node.z - 1 + node_offset; global_jump_node.w = local_jump_node.w - 1 + node_offset; - node_offset += shader_svm_nodes[i].size() - 1; + node_offset += shader_svm_nodes_[i].size() - 1; } /* Copy the nodes of each shader into the correct location. */ svm_nodes += num_shaders; for (int i = 0; i < num_shaders; i++) { - int shader_size = shader_svm_nodes[i].size() - 1; + int shader_size = shader_svm_nodes_[i].size() - 1; - memcpy(svm_nodes, &shader_svm_nodes[i][1], sizeof(int4) * shader_size); + memcpy(svm_nodes, &shader_svm_nodes_[i][1], sizeof(int4) * shader_size); svm_nodes += shader_size; } @@ -161,6 +174,8 @@ void SVMShaderManager::device_update_specific(Device *device, update_flags = UPDATE_NONE; + shader_svm_nodes_.clear(); + VLOG(1) << "Shader manager updated " << num_shaders << " shaders in " << time_dt() - start_time << " seconds."; } diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h index 0353c393ae4..e3c42b5a45a 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/render/svm.h @@ -46,6 +46,8 @@ class SVMShaderManager : public ShaderManager { void reset(Scene *scene) override; + void host_update_specific(Device *device, Scene *scene, Progress &progress) override; + void device_update_specific(Device *device, DeviceScene *dscene, Scene *scene, @@ -53,10 +55,11 @@ class SVMShaderManager : public ShaderManager { void device_free(Device *device, DeviceScene *dscene, Scene *scene) override; protected: - void device_update_shader(Scene *scene, - Shader *shader, - Progress *progress, - array *svm_nodes); + /* Compiled shader nodes. + * + * The compilation happens in the `host_update_specific()`, and the `device_update_specific()` + * moves these nodes to the device. */ + vector> shader_svm_nodes_; }; /* Graph Compiler */ From a82c9e1e405c84b9ab8b5c1f31d7e135ab41c101 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 11 Oct 2021 21:28:06 +1100 Subject: [PATCH 0671/1500] Fix T91169: bpy_extras.io_utils.create_derived_objects -> duplis error This function now takes a depsgraph and a list of objects to avoid inefficient O(n^2) iteration when extracting instances from all objects in the scene. Returning an object -> instance map. Note that keeping compatibility with the existing API wasn't practical in this case since instances can no longer be generated from the scene and it's objects. --- .../scripts/modules/bpy_extras/io_utils.py | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py index 9e3c5bb64e0..a3b39853b3a 100644 --- a/release/scripts/modules/bpy_extras/io_utils.py +++ b/release/scripts/modules/bpy_extras/io_utils.py @@ -25,7 +25,6 @@ __all__ = ( "axis_conversion", "axis_conversion_ensure", "create_derived_objects", - "free_derived_objects", "unpack_list", "unpack_face_list", "path_reference", @@ -348,21 +347,40 @@ def axis_conversion_ensure(operator, forward_attr, up_attr): return False -# return a tuple (free, object list), free is True if memory should be freed -# later with free_derived_objects() -def create_derived_objects(scene, ob): - if ob.parent and ob.parent.instance_type in {'VERTS', 'FACES'}: - return False, None +def create_derived_objects(depsgraph, objects): + """ + This function takes a sequence of objects, returning their instances. - if ob.instance_type != 'NONE': - ob.dupli_list_create(scene) - return True, [(dob.object, dob.matrix) for dob in ob.dupli_list] - else: - return False, [(ob, ob.matrix_world)] + :arg depsgraph: The evaluated depsgraph. + :type depsgraph: :class:`bpy.types.Depsgraph` + :arg objects: A sequencer of objects. + :type objects: sequence of :class:`bpy.types.Object` + :return: A dictionary where each key is an object from `objects`, + values are lists of (:class:`bpy.types.Object`, :class:`mathutils.Matrix`) tuples representing instances. + :rtype: dict + """ + result = {} + has_instancer = False + for ob in objects: + ob_parent = ob.parent + if ob_parent and ob_parent.instance_type in {'VERTS', 'FACES'}: + continue + result[ob] = [] if ob.is_instancer else [(ob, ob.matrix_world.copy())] - -def free_derived_objects(ob): - ob.dupli_list_clear() + if result: + for dup in depsgraph.object_instances: + dup_parent = dup.parent + if dup_parent is None: + continue + dup_parent_original = dup_parent.original + if not dup_parent_original.is_instancer: + # The instance has already been added (on assignment). + continue + instance_list = result.get(dup_parent_original) + if instance_list is None: + continue + instance_list.append((dup.instance_object.original, dup.matrix_world.copy())) + return result def unpack_list(list_of_tuples): From eca2a419648a9e4288f56b921dabafed0cb97526 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 8 Oct 2021 16:38:40 +0200 Subject: [PATCH 0672/1500] Cycles: Improve volume stack size calculation Only count volume objects after shader optimization. Allows to discard objects which don't have effective volume BSDF connected to the shader output (i.e. constant folded, or non-volume BSDF used by mistake). Solves memory regression reported in T92014. There is still possibility to improve memory even further for cases when there are a lot of non-intersecting volume objects, but that requires a deeper refactor of update process. Will happen as a followup development. Differential Revision: https://developer.blender.org/D12797 --- intern/cycles/render/object.cpp | 2 +- intern/cycles/render/scene.cpp | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index d3ea93ca8a5..6d5c537e33d 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -375,7 +375,7 @@ bool Object::check_is_volume() const for (Node *node : get_geometry()->get_used_shaders()) { const Shader *shader = static_cast(node); - if (shader->has_volume_connected) { + if (shader->has_volume) { return true; } } diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index e65f542bd2e..17dc99dd589 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -664,29 +664,29 @@ int Scene::get_max_closure_count() int Scene::get_volume_stack_size() const { + int volume_stack_size = 0; + + /* Space for background volume and terminator. + * Don't do optional here because camera ray initialization expects that there is space for + * at least those elements (avoiding extra condition to check if there is actual volume or not). + */ + volume_stack_size += 2; + /* Quick non-expensive check. Can over-estimate maximum possible nested level, but does not * require expensive calculation during pre-processing. */ - int num_volume_objects = 0; for (const Object *object : objects) { if (object->check_is_volume()) { - ++num_volume_objects; + ++volume_stack_size; } - if (num_volume_objects == MAX_VOLUME_STACK_SIZE) { + if (volume_stack_size == MAX_VOLUME_STACK_SIZE) { break; } } - /* Count background world for the stack. */ - const Shader *background_shader = background->get_shader(this); - if (background_shader && background_shader->has_volume_connected) { - ++num_volume_objects; - } + volume_stack_size = min(volume_stack_size, MAX_VOLUME_STACK_SIZE); - /* Space for terminator. */ - ++num_volume_objects; - - return min(num_volume_objects, MAX_VOLUME_STACK_SIZE); + return volume_stack_size; } bool Scene::has_shadow_catcher() From 275d0d33971872b356a6356a4996604aac98b554 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 11 Oct 2021 12:29:05 +0200 Subject: [PATCH 0673/1500] Cleanup: Spelling in comment --- intern/cycles/util/util_string.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/util/util_string.cpp b/intern/cycles/util/util_string.cpp index 9c0b2ca50bb..0fc9cb4ae77 100644 --- a/intern/cycles/util/util_string.cpp +++ b/intern/cycles/util/util_string.cpp @@ -153,7 +153,7 @@ string string_remove_trademark(const string &s) { string result = s; - /* Special case, so we don;t leave sequential spaces behind. */ + /* Special case, so we don't leave sequential spaces behind. */ /* TODO(sergey): Consider using regex perhaps? */ string_replace(result, " (TM)", ""); string_replace(result, " (R)", ""); From 9c004864511f80e48a48a4d8e459d8f05c40f64d Mon Sep 17 00:00:00 2001 From: Wannes Malfait Date: Mon, 11 Oct 2021 08:38:02 -0500 Subject: [PATCH 0674/1500] Geometry Nodes: Separate and Delete Geometry for fields Delete Geometry: This adds a copy of the old node in the legacy folder and updates the node to work with fields. The invert option is removed, because it is something that should be very easy with fields, and to be consistent with other nodes which have a selection. There is also a dropdown to select the domain, because the domain can't be determined from the field input. When the domain does not belong on any of the components an info message is displayed. Separate Geometry: A more general version of the old Point Separate node. The "inverted" output is the same as using the delete geometry node. Differential Revision: https://developer.blender.org/D12574 --- release/scripts/startup/nodeitems_builtins.py | 2 + source/blender/blenkernel/BKE_node.h | 3 + source/blender/blenkernel/intern/node.cc | 2 + source/blender/makesdna/DNA_node_types.h | 18 + source/blender/makesrna/RNA_enum_items.h | 1 + .../blender/makesrna/intern/rna_attribute.c | 8 + source/blender/makesrna/intern/rna_nodetree.c | 38 + source/blender/nodes/CMakeLists.txt | 2 + source/blender/nodes/NOD_geometry.h | 2 + source/blender/nodes/NOD_static_types.h | 2 + .../nodes/geometry/node_geometry_util.hh | 11 + .../nodes/legacy/node_geo_delete_geometry.cc | 2 +- .../nodes/node_geo_delete_geometry.cc | 1226 +++++++++++++++++ .../nodes/node_geo_separate_geometry.cc | 118 ++ 14 files changed, 1434 insertions(+), 1 deletion(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 155fa59c315..d73fbe5dc76 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -552,9 +552,11 @@ geometry_node_categories = [ NodeItem("GeometryNodeProximity"), NodeItem("GeometryNodeBoundBox"), NodeItem("GeometryNodeConvexHull"), + NodeItem("GeometryNodeDeleteGeometry"), NodeItem("GeometryNodeTransform"), NodeItem("GeometryNodeJoinGeometry"), NodeItem("GeometryNodeSeparateComponents"), + NodeItem("GeometryNodeSeparateGeometry"), NodeItem("GeometryNodeSetPosition"), NodeItem("GeometryNodeRealizeInstances"), ]), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 447e0268701..ef9021fd465 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1515,6 +1515,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_SET_HANDLES 1100 #define GEO_NODE_POINTS_TO_VOLUME 1101 #define GEO_NODE_CURVE_HANDLE_TYPE_SELECTION 1102 +#define GEO_NODE_DELETE_GEOMETRY 1103 +#define GEO_NODE_SEPARATE_GEOMETRY 1104 + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index cad5d4eff41..bff2ed936d9 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5712,6 +5712,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_curve_set_handles(); register_node_type_geo_legacy_attribute_proximity(); register_node_type_geo_legacy_attribute_randomize(); + register_node_type_geo_legacy_delete_geometry(); register_node_type_geo_legacy_material_assign(); register_node_type_geo_legacy_points_to_volume(); register_node_type_geo_legacy_select_by_material(); @@ -5803,6 +5804,7 @@ static void registerGeometryNodes() register_node_type_geo_realize_instances(); register_node_type_geo_sample_texture(); register_node_type_geo_separate_components(); + register_node_type_geo_separate_geometry(); register_node_type_geo_set_position(); register_node_type_geo_string_join(); register_node_type_geo_string_to_curves(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index cbfa4e702ea..52a3755a959 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1538,6 +1538,18 @@ typedef struct NodeGeometryStringToCurves { char _pad[1]; } NodeGeometryStringToCurves; +typedef struct NodeGeometryDeleteGeometry { + /* AttributeDomain. */ + int8_t domain; + /* GeometryNodeDeleteGeometryMode. */ + int8_t mode; +} NodeGeometryDeleteGeometry; + +typedef struct NodeGeometrySeparateGeometry { + /* AttributeDomain. */ + int8_t domain; +} NodeGeometrySeparateGeometry; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -2192,6 +2204,12 @@ typedef enum GeometryNodeStringToCurvesAlignYMode { GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM = 4, } GeometryNodeStringToCurvesAlignYMode; +typedef enum GeometryNodeDeleteGeometryMode { + GEO_NODE_DELETE_GEOMETRY_MODE_ALL = 0, + GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE = 1, + GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE = 2, +} GeometryNodeDeleteGeometryMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index 03d371be1f7..f3e15d08fa3 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -208,6 +208,7 @@ DEF_ENUM(rna_enum_preference_section_items) DEF_ENUM(rna_enum_attribute_type_items) DEF_ENUM(rna_enum_attribute_type_with_auto_items) DEF_ENUM(rna_enum_attribute_domain_items) +DEF_ENUM(rna_enum_attribute_domain_without_corner_items) DEF_ENUM(rna_enum_attribute_domain_with_auto_items) DEF_ENUM(rna_enum_collection_color_items) diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 49e813e6a6c..f1831bca0fe 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -75,6 +75,14 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_attribute_domain_without_corner_items[] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"}, + {ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"}, + {ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"}, + {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = { {ATTR_DOMAIN_AUTO, "AUTO", 0, "Auto", ""}, {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"}, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index cab420ba990..9c1ce0d7bc4 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10700,6 +10700,31 @@ static void def_geo_attribute_capture(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_delete_geometry(StructRNA *srna) +{ + PropertyRNA *prop; + + static const EnumPropertyItem mode_items[] = { + {GEO_NODE_DELETE_GEOMETRY_MODE_ALL, "ALL", 0, "All", ""}, + {GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE, "EDGE_FACE", 0, "Only Edges & Faces", ""}, + {GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE, "ONLY_FACE", 0, "Only Faces", ""}, + {0, NULL, 0, NULL, NULL}, + }; + RNA_def_struct_sdna_from(srna, "NodeGeometryDeleteGeometry", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_enum_default(prop, GEO_NODE_DELETE_GEOMETRY_MODE_ALL); + RNA_def_property_ui_text(prop, "Mode", "Which parts of the mesh component to delete"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_without_corner_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Which domain to delete in"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_string_to_curves(StructRNA *srna) { static const EnumPropertyItem rna_node_geometry_string_to_curves_overflow_items[] = { @@ -10814,6 +10839,19 @@ static void def_geo_string_to_curves(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_separate_geometry(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometrySeparateGeometry", "storage"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_without_corner_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Which domain to separate on"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 78a9bb72e26..b29568a5c9f 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -217,6 +217,7 @@ set(SRC geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_trim.cc + geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_distribute_points_on_faces.cc geometry/nodes/node_geo_input_index.cc geometry/nodes/node_geo_input_material.cc @@ -246,6 +247,7 @@ set(SRC geometry/nodes/node_geo_proximity.cc geometry/nodes/node_geo_realize_instances.cc geometry/nodes/node_geo_separate_components.cc + geometry/nodes/node_geo_separate_geometry.cc geometry/nodes/node_geo_set_position.cc geometry/nodes/node_geo_string_join.cc geometry/nodes/node_geo_string_to_curves.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index c5b0b8f5611..baa841460e9 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -32,6 +32,7 @@ void register_node_type_geo_custom_group(bNodeType *ntype); void register_node_type_geo_legacy_curve_set_handles(void); void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); +void register_node_type_geo_legacy_delete_geometry(void); void register_node_type_geo_legacy_material_assign(void); void register_node_type_geo_legacy_points_to_volume(void); void register_node_type_geo_legacy_select_by_material(void); @@ -125,6 +126,7 @@ void register_node_type_geo_realize_instances(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_separate_components(void); +void register_node_type_geo_separate_geometry(void); void register_node_type_geo_set_position(void); void register_node_type_geo_string_join(void); void register_node_type_geo_string_to_curves(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 80468adf3bc..4cf3179f481 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -345,6 +345,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CU DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") +DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") @@ -374,6 +375,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "") DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") +DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "") DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 5896b5bd6cc..875308ac116 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -79,6 +79,17 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, GeometryComponent &result_component, Span masks, const bool invert); +/** + * Returns the parts of the geometry that are on the selection for the given domain. If the domain + * is not applicable for the component, e.g. face domain for point cloud, nothing happens to that + * component. If no component can work with the domain, then `error_message` is set to true. + */ +void separate_geometry(GeometrySet &geometry_set, + const AttributeDomain domain, + const GeometryNodeDeleteGeometryMode mode, + const Field &selection_field, + const bool invert, + bool &r_error_message); struct CurveToPointsResults { int result_size; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc index 1e2f652cd78..2d9b4da4c83 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc @@ -664,7 +664,7 @@ static void geo_node_delete_geometry_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_delete_geometry() +void register_node_type_geo_legacy_delete_geometry() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc new file mode 100644 index 00000000000..fc9cba73b01 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -0,0 +1,1226 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BLI_array.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +using blender::bke::CustomDataAttributes; + +/* Code from the mask modifier in MOD_mask.cc. */ +extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span vertex_map); +extern void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span vertex_map, + blender::Span edge_map); +extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span vertex_map, + blender::Span edge_map, + blender::Span masked_poly_indices, + blender::Span new_loop_starts); + +namespace blender::nodes { + +static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Selection") + .default_value(true) + .hide_value() + .supports_field() + .description("The parts of the geometry to be deleted"); + b.add_output("Geometry"); +} + +static void geo_node_delete_geometry_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + const bNode *node = static_cast(ptr->data); + const NodeGeometryDeleteGeometry &storage = *(const NodeGeometryDeleteGeometry *)node->storage; + const AttributeDomain domain = static_cast(storage.domain); + + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + /* Only show the mode when it is relevant. */ + if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE)) { + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); + } +} + +static void geo_node_delete_geometry_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryDeleteGeometry *data = (NodeGeometryDeleteGeometry *)MEM_callocN( + sizeof(NodeGeometryDeleteGeometry), __func__); + data->domain = ATTR_DOMAIN_POINT; + data->mode = GEO_NODE_DELETE_GEOMETRY_MODE_ALL; + + node->storage = data; +} + +template static void copy_data(Span data, MutableSpan r_data, IndexMask mask) +{ + for (const int i_out : mask.index_range()) { + r_data[i_out] = data[mask[i_out]]; + } +} + +/** Utility function for making an IndexMask from a boolean selection. The indices vector should + * live at least as long as the returned IndexMask. + */ +static IndexMask index_mask_indices(Span mask, const bool invert, Vector &indices) +{ + for (const int i : mask.index_range()) { + if (mask[i] != invert) { + indices.append(i); + } + } + return IndexMask(indices); +} + +/** Utility function for making an IndexMask from an array of integers, where the negative integers + * are seen as false. The indices vector should live at least as long as the returned IndexMask. + */ +static IndexMask index_mask_indices(Span mask, + const int num_indices, + Vector &indices) +{ + indices.clear(); + indices.reserve(num_indices); + for (const int i : mask.index_range()) { + if (mask[i] >= 0) { + indices.append_unchecked(i); + } + } + return IndexMask(indices); +} + +/** + * Copies the attributes with a domain in `domains` to `result_component`. + */ +static void copy_attributes(const Map &attributes, + const GeometryComponent &in_component, + GeometryComponent &result_component, + const Span domains) +{ + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); + if (!attribute) { + continue; + } + + /* Only copy if it is on a domain we want. */ + if (!domains.contains(attribute.domain)) { + continue; + } + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type()); + + OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( + attribute_id, attribute.domain, data_type); + + if (!result_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + GVArray_Span span{*attribute.varray}; + MutableSpan out_span = result_attribute.as_span(); + out_span.copy_from(span); + }); + result_attribute.save(); + } +} + +/** + * For each attribute with a domain in `domains` it copies the parts of that attribute which lie in + * the mask to `result_component`. + */ +static void copy_attributes_based_on_mask(const Map &attributes, + const GeometryComponent &in_component, + GeometryComponent &result_component, + const AttributeDomain domain, + const IndexMask mask) +{ + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); + if (!attribute) { + continue; + } + + /* Only copy if it is on a domain we want. */ + if (domain != attribute.domain) { + continue; + } + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type()); + + OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( + attribute_id, attribute.domain, data_type); + + if (!result_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + GVArray_Span span{*attribute.varray}; + MutableSpan out_span = result_attribute.as_span(); + copy_data(span, out_span, mask); + }); + result_attribute.save(); + } +} + +static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span edge_map) +{ + BLI_assert(src_mesh.totedge == edge_map.size()); + for (const int i_src : IndexRange(src_mesh.totedge)) { + const int i_dst = edge_map[i_src]; + if (i_dst == -1 || i_dst == -2) { + continue; + } + + const MEdge &e_src = src_mesh.medge[i_src]; + MEdge &e_dst = dst_mesh.medge[i_dst]; + + e_dst = e_src; + e_dst.v1 = e_src.v1; + e_dst.v2 = e_src.v2; + } +} + +/* Faces and edges changed but vertices are the same. */ +static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span edge_map, + Span masked_poly_indices, + Span new_loop_starts) +{ + for (const int i_dst : masked_poly_indices.index_range()) { + const int i_src = masked_poly_indices[i_dst]; + + const MPoly &mp_src = src_mesh.mpoly[i_src]; + MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const int i_ml_src = mp_src.loopstart; + const int i_ml_dst = new_loop_starts[i_dst]; + + const MLoop *ml_src = src_mesh.mloop + i_ml_src; + MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + + mp_dst = mp_src; + mp_dst.loopstart = i_ml_dst; + for (int i : IndexRange(mp_src.totloop)) { + ml_dst[i].v = ml_src[i].v; + ml_dst[i].e = edge_map[ml_src[i].e]; + } + } +} + +/* Only faces changed. */ +static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span masked_poly_indices, + Span new_loop_starts) +{ + for (const int i_dst : masked_poly_indices.index_range()) { + const int i_src = masked_poly_indices[i_dst]; + + const MPoly &mp_src = src_mesh.mpoly[i_src]; + MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const int i_ml_src = mp_src.loopstart; + const int i_ml_dst = new_loop_starts[i_dst]; + + const MLoop *ml_src = src_mesh.mloop + i_ml_src; + MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + + mp_dst = mp_src; + mp_dst.loopstart = i_ml_dst; + for (int i : IndexRange(mp_src.totloop)) { + ml_dst[i].v = ml_src[i].v; + ml_dst[i].e = ml_src[i].e; + } + } +} + +static void spline_copy_builtin_attributes(const Spline &spline, + Spline &r_spline, + const IndexMask mask) +{ + copy_data(spline.positions(), r_spline.positions(), mask); + copy_data(spline.radii(), r_spline.radii(), mask); + copy_data(spline.tilts(), r_spline.tilts(), mask); + switch (spline.type()) { + case Spline::Type::Poly: + break; + case Spline::Type::Bezier: { + const BezierSpline &src = static_cast(spline); + BezierSpline &dst = static_cast(r_spline); + copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask); + copy_data(src.handle_positions_right(), dst.handle_positions_right(), mask); + copy_data(src.handle_types_left(), dst.handle_types_left(), mask); + copy_data(src.handle_types_right(), dst.handle_types_right(), mask); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src = static_cast(spline); + NURBSpline &dst = static_cast(r_spline); + copy_data(src.weights(), dst.weights(), mask); + break; + } + } +} + +static void copy_dynamic_attributes(const CustomDataAttributes &src, + CustomDataAttributes &dst, + const IndexMask mask) +{ + src.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src_attribute = src.get_for_read(attribute_id); + BLI_assert(src_attribute); + + if (!dst.create(attribute_id, meta_data.data_type)) { + /* Since the source spline of the same type had the attribute, adding it should work. + */ + BLI_assert_unreachable(); + } + + std::optional new_attribute = dst.get_for_write(attribute_id); + BLI_assert(new_attribute); + + attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_data(src_attribute->typed(), new_attribute->typed(), mask); + }); + return true; + }, + ATTR_DOMAIN_POINT); +} + +/** + * Deletes points in the spline. Those not in the mask are deleted. The spline is not split into + * multiple newer splines. + */ +static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) +{ + SplinePtr new_spline = spline.copy_only_settings(); + new_spline->resize(mask.size()); + + spline_copy_builtin_attributes(spline, *new_spline, mask); + copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask); + + return new_spline; +} + +static std::unique_ptr curve_separate(const CurveEval &input_curve, + const Span selection, + const AttributeDomain selection_domain, + const bool invert) +{ + Span input_splines = input_curve.splines(); + std::unique_ptr output_curve = std::make_unique(); + + /* Keep track of which splines were copied to the result to copy spline domain attributes. */ + Vector copied_splines; + + if (selection_domain == ATTR_DOMAIN_CURVE) { + /* Operates on each of the splines as a whole, i.e. not on the points in the splines + * themselves. */ + for (const int i : selection.index_range()) { + if (selection[i] != invert) { + output_curve->add_spline(input_splines[i]->copy()); + copied_splines.append(i); + } + } + } + else { + /* Operates on the points in the splines themselves. */ + + /* Reuse index vector for each spline. */ + Vector indices_to_copy; + + int selection_index = 0; + for (const int i : input_splines.index_range()) { + const Spline &spline = *input_splines[i]; + + indices_to_copy.clear(); + for (const int i_point : IndexRange(spline.size())) { + if (selection[selection_index] == invert) { + /* Append i_point instead of selection_index because we need indices local to the spline + * for copying. */ + indices_to_copy.append(i_point); + } + selection_index++; + } + + /* Avoid creating an empty spline. */ + if (indices_to_copy.is_empty()) { + continue; + } + + SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy)); + output_curve->add_spline(std::move(new_spline)); + copied_splines.append(i); + } + } + + if (copied_splines.is_empty()) { + return {}; + } + + output_curve->attributes.reallocate(output_curve->splines().size()); + copy_dynamic_attributes( + input_curve.attributes, output_curve->attributes, IndexMask(copied_splines)); + + return output_curve; +} + +static void separate_curve_selection(GeometrySet &geometry_set, + const Field &selection_field, + const AttributeDomain selection_domain, + const bool invert) +{ + const CurveComponent &src_component = *geometry_set.get_component_for_read(); + GeometryComponentFieldContext field_context{src_component, selection_domain}; + + fn::FieldEvaluator selection_evaluator{field_context, + src_component.attribute_domain_size(selection_domain)}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray_Span &selection = selection_evaluator.get_evaluated(0); + std::unique_ptr r_curve = curve_separate( + *src_component.get_for_read(), selection, selection_domain, invert); + if (r_curve) { + geometry_set.replace_curve(r_curve.release()); + } + else { + geometry_set.replace_curve(nullptr); + } +} + +static void separate_point_cloud_selection(GeometrySet &geometry_set, + const Field &selection_field, + const bool invert) +{ + const PointCloudComponent &src_points = + *geometry_set.get_component_for_read(); + GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; + + fn::FieldEvaluator selection_evaluator{field_context, + src_points.attribute_domain_size(ATTR_DOMAIN_POINT)}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray_Span &selection = selection_evaluator.get_evaluated(0); + + Vector indices; + const IndexMask mask = index_mask_indices(selection, invert, indices); + const int total = mask.size(); + PointCloud *pointcloud = BKE_pointcloud_new_nomain(total); + + if (total == 0) { + geometry_set.replace_pointcloud(pointcloud); + return; + } + + PointCloudComponent dst_points; + dst_points.replace(pointcloud, GeometryOwnershipType::Editable); + + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); + + copy_attributes_based_on_mask(attributes, src_points, dst_points, ATTR_DOMAIN_POINT, mask); + geometry_set.replace_pointcloud(pointcloud); +} + +static void compute_selected_vertices_from_vertex_selection(const Span vertex_selection, + const bool invert, + MutableSpan r_vertex_map, + int *r_num_selected_vertices) +{ + BLI_assert(vertex_selection.size() == r_vertex_map.size()); + + int num_selected_vertices = 0; + for (const int i : r_vertex_map.index_range()) { + if (vertex_selection[i] != invert) { + r_vertex_map[i] = num_selected_vertices; + num_selected_vertices++; + } + else { + r_vertex_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; +} + +static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, + const Span vertex_selection, + const bool invert, + MutableSpan r_edge_map, + int *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == r_edge_map.size()); + + int num_selected_edges = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + + /* Only add the edge if both vertices will be in the new mesh. */ + if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_edges = num_selected_edges; +} + +static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, + const Span vertex_selection, + const bool invert, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + BLI_assert(mesh.totvert == vertex_selection.size()); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_verts_in_selection = true; + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (vertex_selection[loop.v] == invert) { + all_verts_in_selection = false; + break; + } + } + + if (all_verts_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the + * edge are kept along with the edge. + */ +static void compute_selected_vertices_and_edges_from_edge_selection( + const Mesh &mesh, + const Span edge_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + int *r_num_selected_vertices, + int *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == edge_selection.size()); + + int num_selected_edges = 0; + int num_selected_vertices = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + if (edge_selection[i] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + if (r_vertex_map[edge.v1] == -1) { + r_vertex_map[edge.v1] = num_selected_vertices; + num_selected_vertices++; + } + if (r_vertex_map[edge.v2] == -1) { + r_vertex_map[edge.v2] = num_selected_vertices; + num_selected_vertices++; + } + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; +} + +/** + * Checks for every edge if it is in `edge_selection`. + */ +static void compute_selected_edges_from_edge_selection(const Mesh &mesh, + const Span edge_selection, + const bool invert, + MutableSpan r_edge_map, + int *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == edge_selection.size()); + + int num_selected_edges = 0; + for (const int i : IndexRange(mesh.totedge)) { + if (edge_selection[i] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_edges = num_selected_edges; +} + +/** + * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that + * polygon is kept. + */ +static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, + const Span edge_selection, + const bool invert, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_edges_in_selection = true; + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (edge_selection[loop.e] == invert) { + all_edges_in_selection = false; + break; + } + } + + if (all_edges_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every edge and polygon if all its vertices are in `vertex_selection`. + */ +static void compute_selected_mesh_data_from_vertex_selection_edge_face( + const Mesh &mesh, + const Span vertex_selection, + const bool invert, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + + compute_selected_edges_from_vertex_selection( + mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + + compute_selected_polygons_from_vertex_selection(mesh, + vertex_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all + * vertices of that polygon or edge are in the selection. + */ +static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, + const Span vertex_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_vertices, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + compute_selected_vertices_from_vertex_selection( + vertex_selection, invert, r_vertex_map, r_num_selected_vertices); + + compute_selected_edges_from_vertex_selection( + mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + + compute_selected_polygons_from_vertex_selection(mesh, + vertex_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every edge if it is in `edge_selection`. The polygons are kept if all edges are in + * the selection. + */ +static void compute_selected_mesh_data_from_edge_selection_edge_face( + const Mesh &mesh, + const Span edge_selection, + const bool invert, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + compute_selected_edges_from_edge_selection( + mesh, edge_selection, invert, r_edge_map, r_num_selected_edges); + compute_selected_polygons_from_edge_selection(mesh, + edge_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to + * that edge are kept as well. The polygons are kept if all edges are in the selection. + */ +static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, + const Span edge_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_vertices, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + r_vertex_map.fill(-1); + compute_selected_vertices_and_edges_from_edge_selection(mesh, + edge_selection, + invert, + r_vertex_map, + r_edge_map, + r_num_selected_vertices, + r_num_selected_edges); + compute_selected_polygons_from_edge_selection(mesh, + edge_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every polygon if it is in `poly_selection`. + */ +static void compute_selected_polygons_from_poly_selection(const Mesh &mesh, + const Span poly_selection, + const bool invert, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} +/** + * Checks for every polygon if it is in `poly_selection`. If it is, the edges + * belonging to that polygon are kept as well. + */ +static void compute_selected_mesh_data_from_poly_selection_edge_face( + const Mesh &mesh, + const Span poly_selection, + const bool invert, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + BLI_assert(mesh.totedge == r_edge_map.size()); + r_edge_map.fill(-1); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + int num_selected_edges = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + + /* Add the vertices and the edges. */ + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + /* Check first if it has not yet been added. */ + if (r_edge_map[loop.e] == -1) { + r_edge_map[loop.e] = num_selected_edges; + num_selected_edges++; + } + } + } + } + *r_num_selected_edges = num_selected_edges; + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices + * belonging to that polygon are kept as well. + */ +static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, + const Span poly_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_vertices, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + BLI_assert(mesh.totedge == r_edge_map.size()); + r_vertex_map.fill(-1); + r_edge_map.fill(-1); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + int num_selected_vertices = 0; + int num_selected_edges = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + + /* Add the vertices and the edges. */ + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + /* Check first if it has not yet been added. */ + if (r_vertex_map[loop.v] == -1) { + r_vertex_map[loop.v] = num_selected_vertices; + num_selected_vertices++; + } + if (r_edge_map[loop.e] == -1) { + r_edge_map[loop.e] = num_selected_edges; + num_selected_edges++; + } + } + } + } + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Keep the parts of the mesh that are in the selection. + */ +static void do_mesh_separation(GeometrySet &geometry_set, + const MeshComponent &in_component, + const VArray_Span &selection, + const bool invert, + const AttributeDomain domain, + const GeometryNodeDeleteGeometryMode mode) +{ + /* Needed in all cases. */ + Vector selected_poly_indices; + Vector new_loop_starts; + int num_selected_polys; + int num_selected_loops; + + const Mesh &mesh_in = *in_component.get_for_read(); + Mesh *mesh_out; + MeshComponent out_component; + + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, attributes); + + switch (mode) { + case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: { + Array vertex_map(mesh_in.totvert); + int num_selected_vertices; + + Array edge_map(mesh_in.totedge); + int num_selected_edges; + + /* Fill all the maps based on the selection. */ + switch (domain) { + case ATTR_DOMAIN_POINT: + compute_selected_mesh_data_from_vertex_selection(mesh_in, + selection, + invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_EDGE: + compute_selected_mesh_data_from_edge_selection(mesh_in, + selection, + invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_FACE: + compute_selected_mesh_data_from_poly_selection(mesh_in, + selection, + invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + default: + BLI_assert_unreachable(); + break; + } + mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, + num_selected_vertices, + num_selected_edges, + 0, + num_selected_loops, + num_selected_polys); + out_component.replace(mesh_out, GeometryOwnershipType::Editable); + + /* Copy the selected parts of the mesh over to the new mesh. */ + copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map); + copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map); + copy_masked_polys_to_new_mesh( + mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts); + break; + } + case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: { + Array edge_map(mesh_in.totedge); + int num_selected_edges; + + /* Fill all the maps based on the selection. */ + switch (domain) { + case ATTR_DOMAIN_POINT: + compute_selected_mesh_data_from_vertex_selection_edge_face(mesh_in, + selection, + invert, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_EDGE: + compute_selected_mesh_data_from_edge_selection_edge_face(mesh_in, + selection, + invert, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_FACE: + compute_selected_mesh_data_from_poly_selection_edge_face(mesh_in, + selection, + invert, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + default: + BLI_assert_unreachable(); + break; + } + mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, + mesh_in.totvert, + num_selected_edges, + 0, + num_selected_loops, + num_selected_polys); + out_component.replace(mesh_out, GeometryOwnershipType::Editable); + + /* Copy the selected parts of the mesh over to the new mesh. */ + memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); + copy_attributes(attributes, in_component, out_component, {ATTR_DOMAIN_POINT}); + copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map); + copy_masked_polys_to_new_mesh( + mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts); + Vector indices; + copy_attributes_based_on_mask(attributes, + in_component, + out_component, + ATTR_DOMAIN_EDGE, + index_mask_indices(edge_map, num_selected_edges, indices)); + copy_attributes_based_on_mask( + attributes, + in_component, + out_component, + ATTR_DOMAIN_FACE, + index_mask_indices(selected_poly_indices, num_selected_polys, indices)); + break; + } + case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE: { + /* Fill all the maps based on the selection. */ + switch (domain) { + case ATTR_DOMAIN_POINT: + compute_selected_polygons_from_vertex_selection(mesh_in, + selection, + invert, + selected_poly_indices, + new_loop_starts, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_EDGE: + compute_selected_polygons_from_edge_selection(mesh_in, + selection, + invert, + selected_poly_indices, + new_loop_starts, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_FACE: + compute_selected_polygons_from_poly_selection(mesh_in, + selection, + invert, + selected_poly_indices, + new_loop_starts, + &num_selected_polys, + &num_selected_loops); + break; + default: + BLI_assert_unreachable(); + break; + } + mesh_out = BKE_mesh_new_nomain_from_template( + &mesh_in, mesh_in.totvert, mesh_in.totedge, 0, num_selected_loops, num_selected_polys); + out_component.replace(mesh_out, GeometryOwnershipType::Editable); + + /* Copy the selected parts of the mesh over to the new mesh. */ + memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); + memcpy(mesh_out->medge, mesh_in.medge, mesh_in.totedge * sizeof(MEdge)); + copy_attributes( + attributes, in_component, out_component, {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE}); + copy_masked_polys_to_new_mesh(mesh_in, *mesh_out, selected_poly_indices, new_loop_starts); + Vector indices; + const IndexMask mask = index_mask_indices( + selected_poly_indices, num_selected_polys, indices); + copy_attributes_based_on_mask( + attributes, in_component, out_component, ATTR_DOMAIN_FACE, mask); + break; + } + } + + BKE_mesh_calc_edges_loose(mesh_out); + /* Tag to recalculate normals later. */ + BKE_mesh_normals_tag_dirty(mesh_out); + geometry_set.replace_mesh(mesh_out); +} + +static void separate_mesh_selection(GeometrySet &geometry_set, + const Field &selection_field, + const AttributeDomain selection_domain, + const GeometryNodeDeleteGeometryMode mode, + const bool invert) +{ + const MeshComponent &src_component = *geometry_set.get_component_for_read(); + GeometryComponentFieldContext field_context{src_component, selection_domain}; + + fn::FieldEvaluator selection_evaluator{field_context, + src_component.attribute_domain_size(selection_domain)}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray_Span &selection = selection_evaluator.get_evaluated(0); + + /* Check if there is anything to delete. */ + bool delete_nothing = true; + for (const int i : selection.index_range()) { + if (selection[i] == invert) { + delete_nothing = false; + break; + } + } + if (delete_nothing) { + return; + } + + do_mesh_separation(geometry_set, src_component, selection, invert, selection_domain, mode); +} + +void separate_geometry(GeometrySet &geometry_set, + const AttributeDomain domain, + const GeometryNodeDeleteGeometryMode mode, + const Field &selection_field, + const bool invert, + bool &r_is_error) +{ + bool some_valid_domain = false; + if (geometry_set.has()) { + if (domain == ATTR_DOMAIN_POINT) { + separate_point_cloud_selection(geometry_set, selection_field, invert); + some_valid_domain = true; + } + } + if (geometry_set.has()) { + if (domain != ATTR_DOMAIN_CURVE) { + separate_mesh_selection(geometry_set, selection_field, domain, mode, invert); + some_valid_domain = true; + } + } + if (geometry_set.has()) { + if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + separate_curve_selection(geometry_set, selection_field, domain, invert); + some_valid_domain = true; + } + } + r_is_error = !some_valid_domain && geometry_set.has_realized_data(); +} + +static void geo_node_delete_geometry_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + const Field selection_field = params.extract_input>("Selection"); + + const bNode &node = params.node(); + const NodeGeometryDeleteGeometry &storage = *(const NodeGeometryDeleteGeometry *)node.storage; + const AttributeDomain domain = static_cast(storage.domain); + const GeometryNodeDeleteGeometryMode mode = static_cast( + storage.mode); + + bool all_is_error = false; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + bool this_is_error = false; + /* Invert here because we want to keep the things not in the selection. */ + separate_geometry(geometry_set, domain, mode, selection_field, true, this_is_error); + all_is_error &= this_is_error; + }); + if (all_is_error) { + /* Only show this if none of the instances/components actually changed. */ + params.error_message_add(NodeWarningType::Info, TIP_("No geometry with given domain")); + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_delete_geometry() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); + + node_type_storage(&ntype, + "NodeGeometryDeleteGeometry", + node_free_standard_storage, + node_copy_standard_storage); + + node_type_init(&ntype, blender::nodes::geo_node_delete_geometry_init); + + ntype.declare = blender::nodes::geo_node_delete_geometry_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec; + ntype.draw_buttons = blender::nodes::geo_node_delete_geometry_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc new file mode 100644 index 00000000000..970d49e0626 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc @@ -0,0 +1,118 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_separate_geometry_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Selection") + .default_value(true) + .hide_value() + .supports_field() + .description("The parts of the geometry that go into the first output"); + b.add_output("Selection") + .description("The parts of the geometry in the selection"); + b.add_output("Inverted") + .description("The parts of the geometry not in the selection"); +} + +static void geo_node_separate_geometry_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void geo_node_separate_geometry_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometrySeparateGeometry *data = (NodeGeometrySeparateGeometry *)MEM_callocN( + sizeof(NodeGeometrySeparateGeometry), __func__); + data->domain = ATTR_DOMAIN_POINT; + + node->storage = data; +} + +static void geo_node_separate_geometry_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + const Field selection_field = params.extract_input>("Selection"); + + const bNode &node = params.node(); + const NodeGeometryDeleteGeometry &storage = *(const NodeGeometryDeleteGeometry *)node.storage; + const AttributeDomain domain = static_cast(storage.domain); + + bool all_is_error = false; + GeometrySet second_set(geometry_set); + if (params.output_is_required("Selection")) { + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + bool this_is_error = false; + separate_geometry(geometry_set, + domain, + GEO_NODE_DELETE_GEOMETRY_MODE_ALL, + selection_field, + false, + this_is_error); + all_is_error &= this_is_error; + }); + params.set_output("Selection", std::move(geometry_set)); + } + if (params.output_is_required("Inverted")) { + second_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + bool this_is_error = false; + separate_geometry(geometry_set, + domain, + GEO_NODE_DELETE_GEOMETRY_MODE_ALL, + selection_field, + true, + this_is_error); + all_is_error &= this_is_error; + }); + params.set_output("Inverted", std::move(second_set)); + } + if (all_is_error) { + /* Only show this if none of the instances/components actually changed. */ + params.error_message_add(NodeWarningType::Info, TIP_("No geometry with given domain")); + } +} + +} // namespace blender::nodes + +void register_node_type_geo_separate_geometry() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SEPARATE_GEOMETRY, "Separate Geometry", NODE_CLASS_GEOMETRY, 0); + + node_type_storage(&ntype, + "NodeGeometrySeparateGeometry", + node_free_standard_storage, + node_copy_standard_storage); + + node_type_init(&ntype, blender::nodes::geo_node_separate_geometry_init); + + ntype.declare = blender::nodes::geo_node_separate_geometry_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_separate_geometry_exec; + ntype.draw_buttons = blender::nodes::geo_node_separate_geometry_layout; + nodeRegisterType(&ntype); +} From 4703e125bf4bb2b604aa78131e9b1a5a7c01c7af Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 11 Oct 2021 15:55:38 +0200 Subject: [PATCH 0675/1500] Fix active pixels overlay for Cycles viewport It got missed in some of previous development. Can not see a reason why the line needed to be removed, maybe just some accident. --- intern/cycles/integrator/path_trace_work.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp index 14e6f03aedc..d46f095d0d7 100644 --- a/intern/cycles/integrator/path_trace_work.cpp +++ b/intern/cycles/integrator/path_trace_work.cpp @@ -183,6 +183,8 @@ PassAccessor::PassAccessInfo PathTraceWork::get_display_pass_access_info(PassMod pass_access_info.use_approximate_shadow_catcher_background = kfilm.use_approximate_shadow_catcher && !kbackground.transparent; + pass_access_info.show_active_pixels = film_->get_show_active_pixels(); + return pass_access_info; } From cae4d8637cde13d8cba9dfd0db246f6cd0ecfc21 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 11 Oct 2021 16:07:57 +0200 Subject: [PATCH 0676/1500] Cleanup: match parameter name in function declaration and implementation. --- source/blender/nodes/geometry/node_geometry_util.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 875308ac116..21404525748 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -89,7 +89,7 @@ void separate_geometry(GeometrySet &geometry_set, const GeometryNodeDeleteGeometryMode mode, const Field &selection_field, const bool invert, - bool &r_error_message); + bool &r_is_error); struct CurveToPointsResults { int result_size; From ecedef09e7fde9c032b746137211f02acfb6160a Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Mon, 11 Oct 2021 10:38:45 -0500 Subject: [PATCH 0677/1500] Geometry Nodes: Rename 12 Nodes to be "Verb First" Attribute Capture => Capture Attribute Curve Fill => Fill Curve Curve Fillet => Fillet Curve Curve Reverse => Reverse Curve Curve Sample => Sample Curve Curve Subdivide => Subdivide Curve Curve Trim => Trim Curve Material Assign => Assign Material Material Replace => Replace Material Mesh Subdivide => Subdivide Mesh Float Compare => Compare Float Boolean => Mesh Boolean Differential Revision: https://developer.blender.org/D12798 Task: https://developer.blender.org/T91682 --- source/blender/nodes/NOD_static_types.h | 24 +++++++++---------- .../function/nodes/node_fn_float_compare.cc | 2 +- .../nodes/node_geo_attribute_capture.cc | 2 +- .../nodes/geometry/nodes/node_geo_boolean.cc | 2 +- .../geometry/nodes/node_geo_curve_fill.cc | 2 +- .../geometry/nodes/node_geo_curve_fillet.cc | 2 +- .../geometry/nodes/node_geo_curve_reverse.cc | 2 +- .../geometry/nodes/node_geo_curve_sample.cc | 2 +- .../nodes/node_geo_curve_subdivide.cc | 2 +- .../geometry/nodes/node_geo_curve_trim.cc | 2 +- .../nodes/node_geo_material_assign.cc | 2 +- .../nodes/node_geo_material_replace.cc | 2 +- .../geometry/nodes/node_geo_mesh_subdivide.cc | 2 +- 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 4cf3179f481..1acd082377f 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -267,7 +267,7 @@ DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", Leg DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") -DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") +DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Compare Floats", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "") DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") @@ -318,15 +318,15 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST" DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Capture Attribute", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "") -DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") +DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Mesh Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") -DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Curve Fill", "") -DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Curve Fillet", "") +DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Fill Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Fillet Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") @@ -339,12 +339,12 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRA DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "") +DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Reverse Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Sample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Subdivide Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") -DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") +DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Trim Curve", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") @@ -356,8 +356,8 @@ DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") -DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") -DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "") +DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Assign Material", "") +DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Replace Material", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") @@ -367,7 +367,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") -DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "") +DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Subdivide Mesh", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "") diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 9736c52e895..72c85de455a 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -110,7 +110,7 @@ void register_node_type_fn_float_compare() { static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", NODE_CLASS_CONVERTER, 0); + fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Compare Floats", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_float_compare_declare; node_type_label(&ntype, node_float_compare_label); node_type_update(&ntype, node_float_compare_update); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 43fb00a482c..8cb7d81f432 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -194,7 +194,7 @@ void register_node_type_geo_attribute_capture() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_CAPTURE, "Attribute Capture", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_ATTRIBUTE_CAPTURE, "Capture Attribute", NODE_CLASS_ATTRIBUTE, 0); node_type_storage(&ntype, "NodeGeometryAttributeCapture", node_free_standard_storage, diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 21b425c0ed4..ee72d27a87b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -128,7 +128,7 @@ void register_node_type_geo_boolean() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Mesh Boolean", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_boolean_declare; ntype.draw_buttons = blender::nodes::geo_node_boolean_layout; ntype.updatefunc = blender::nodes::geo_node_boolean_update; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index c30741cf786..0c1fe1fa6a2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -166,7 +166,7 @@ void register_node_type_geo_curve_fill() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_FILL, "Curve Fill", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_CURVE_FILL, "Fill Curve", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_curve_fill_init); node_type_storage( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 78ab4119c8b..bdf122aff29 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -635,7 +635,7 @@ void register_node_type_geo_curve_fillet() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_FILLET, "Curve Fillet", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_CURVE_FILLET, "Fillet Curve", NODE_CLASS_GEOMETRY, 0); ntype.draw_buttons = blender::nodes::geo_node_curve_fillet_layout; node_type_storage( &ntype, "NodeGeometryCurveFillet", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index b644faabedb..adeaa67e1b6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -65,7 +65,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) void register_node_type_geo_curve_reverse() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Reverse Curve", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_reverse_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 1266f525861..e4043d9408c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -275,7 +275,7 @@ void register_node_type_geo_curve_sample() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_SAMPLE, "Curve Sample", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_CURVE_SAMPLE, " Sample Curve", NODE_CLASS_GEOMETRY, 0); ntype.geometry_node_execute = blender::nodes::geo_node_curve_sample_exec; ntype.declare = blender::nodes::geo_node_curve_sample_declare; node_type_init(&ntype, blender::nodes::geo_node_curve_sample_type_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 34997c66cbb..99379ab7259 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -357,7 +357,7 @@ void register_node_type_geo_curve_subdivide() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Subdivide Curve", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_subdivide_declare; ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 4303999f79c..fd340afabbb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -424,7 +424,7 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params) void register_node_type_geo_curve_trim() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Curve Trim", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Trim Curve", NODE_CLASS_GEOMETRY, 0); ntype.geometry_node_execute = blender::nodes::geo_node_curve_trim_exec; ntype.draw_buttons = blender::nodes::geo_node_curve_trim_layout; ntype.declare = blender::nodes::geo_node_curve_trim_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc index 780994996ae..26c1aabf39f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -90,7 +90,7 @@ void register_node_type_geo_material_assign() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Assign Material", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_material_assign_declare; ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc index a917434fa00..e2a9510c3cb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc @@ -62,7 +62,7 @@ void register_node_type_geo_material_replace() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_MATERIAL_REPLACE, "Material Replace", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_MATERIAL_REPLACE, "Replace Material", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_material_replace_declare; ntype.geometry_node_execute = blender::nodes::geo_node_material_replace_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index c436f5bd480..79324b38241 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -103,7 +103,7 @@ void register_node_type_geo_mesh_subdivide() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_SUBDIVIDE, "Mesh Subdivide", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_MESH_SUBDIVIDE, "Subdivide Mesh", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_mesh_subdivide_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_subdivide_exec; nodeRegisterType(&ntype); From 3de76a067a157b8fafc18809e629fdd4aa1816c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 11 Oct 2021 17:59:42 +0200 Subject: [PATCH 0678/1500] Cleanup: Asset Catalogs, add type alias for asset catalog maps Add alias for `Map>` to make the rest of the code a bit simpler. No functional changes. --- source/blender/blenkernel/BKE_asset_catalog.hh | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index e9f0aa6311c..bd18fdf1d6e 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -40,16 +40,18 @@ namespace blender::bke { +class AssetCatalog; +class AssetCatalogCollection; +class AssetCatalogDefinitionFile; +class AssetCatalogFilter; +class AssetCatalogTree; + using CatalogID = bUUID; using CatalogPathComponent = std::string; /* Would be nice to be able to use `std::filesystem::path` for this, but it's currently not * available on the minimum macOS target version. */ using CatalogFilePath = std::string; - -class AssetCatalog; -class AssetCatalogDefinitionFile; -class AssetCatalogFilter; -class AssetCatalogTree; +using OwningAssetCatalogMap = Map>; /* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single * directory hierarchy). */ @@ -131,8 +133,8 @@ class AssetCatalogService { protected: /* These pointers are owned by this AssetCatalogService. */ - Map> catalogs_; - Map> deleted_catalogs_; + OwningAssetCatalogMap catalogs_; + OwningAssetCatalogMap deleted_catalogs_; std::unique_ptr catalog_definition_file_; std::unique_ptr catalog_tree_ = std::make_unique(); CatalogFilePath asset_library_root_; From 83f87d9f21fada613a70e7296120752e1e876dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 11 Oct 2021 17:59:57 +0200 Subject: [PATCH 0679/1500] Cleanup: Asset Catalogs, add divider between sections in code No functional changes. --- source/blender/blenkernel/intern/asset_catalog.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 1d25bda480d..ab7d8eafb8b 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -535,6 +535,8 @@ void AssetCatalogTree::foreach_root_item(const ItemIterFn callback) } } +/* ---------------------------------------------------------------------- */ + bool AssetCatalogDefinitionFile::contains(const CatalogID catalog_id) const { return catalogs_.contains(catalog_id); From c1b4abf527f102ca20114e8179eca511d90784b3 Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Mon, 11 Oct 2021 11:03:57 -0500 Subject: [PATCH 0680/1500] Geometry Nodes: Add Nodes to Get/Set Built-in Attributes This commit implements T91780, adding nodes to get and set builtin attributes. Individual set nodes are used so that the values can be exposed for direct editing, which is useful for attributes like shade smooth and spline resolution. Individual input nodes are used to allow reusing nodes for multiple components, and to allow grouping multiple outputs conceptually in the same node in the future. Input Nodes - Radius - Curve Tilt - Curve Handle Positions - Is Shade Smooth - Spline Resolution - Is Spline Cyclic 'Set' Nodes - Curve Radius - Point Radius - Curve Tilt - Curve Handle Positions - Is Shade Smooth - Spline Resolution - Is Spline Cyclic Using hardcoded categories is necessary to add separators to the node menu. Differential Revision: https://developer.blender.org/D12687 --- release/scripts/startup/nodeitems_builtins.py | 152 ++++++++++------ source/blender/blenkernel/BKE_node.h | 13 ++ source/blender/blenkernel/intern/node.cc | 16 +- source/blender/makesdna/DNA_node_types.h | 5 + source/blender/makesrna/intern/rna_nodetree.c | 12 ++ .../modifiers/intern/MOD_nodes_evaluator.cc | 8 + source/blender/nodes/CMakeLists.txt | 13 ++ source/blender/nodes/NOD_geometry.h | 13 ++ source/blender/nodes/NOD_static_types.h | 15 +- .../nodes/node_geo_input_curve_handles.cc | 46 +++++ .../nodes/node_geo_input_curve_tilt.cc | 42 +++++ .../geometry/nodes/node_geo_input_radius.cc | 42 +++++ .../nodes/node_geo_input_shade_smooth.cc | 42 +++++ .../nodes/node_geo_input_spline_cyclic.cc | 43 +++++ .../nodes/node_geo_input_spline_resolution.cc | 43 +++++ .../nodes/node_geo_set_curve_handles.cc | 164 ++++++++++++++++++ .../nodes/node_geo_set_curve_radius.cc | 79 +++++++++ .../geometry/nodes/node_geo_set_curve_tilt.cc | 78 +++++++++ .../nodes/node_geo_set_point_radius.cc | 80 +++++++++ .../nodes/node_geo_set_shade_smooth.cc | 78 +++++++++ .../nodes/node_geo_set_spline_cyclic.cc | 79 +++++++++ .../nodes/node_geo_set_spline_resolution.cc | 95 ++++++++++ 22 files changed, 1107 insertions(+), 51 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_radius.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index d73fbe5dc76..b6bc70e36fc 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -80,6 +80,104 @@ node_tree_group_type = { 'GeometryNodeTree': 'GeometryNodeGroup', } +# Custom Menu for Geometry Node Curves +def curve_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + if not space.edit_tree: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("GeometryNodeLegacyCurveEndpoints") + yield NodeItem("GeometryNodeLegacyCurveReverse") + yield NodeItem("GeometryNodeLegacyCurveSubdivide") + yield NodeItem("GeometryNodeLegacyCurveToPoints") + yield NodeItem("GeometryNodeLegacyMeshToCurve") + yield NodeItem("GeometryNodeLegacyCurveSelectHandles") + yield NodeItem("GeometryNodeLegacyCurveSetHandles") + yield NodeItem("GeometryNodeLegacyCurveSplineType") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeCurveFill") + yield NodeItem("GeometryNodeCurveFillet") + yield NodeItem("GeometryNodeCurveLength") + yield NodeItem("GeometryNodeCurveReverse") + yield NodeItem("GeometryNodeCurveSample") + yield NodeItem("GeometryNodeCurveSubdivide") + yield NodeItem("GeometryNodeCurveToMesh") + yield NodeItem("GeometryNodeCurveTrim") + yield NodeItem("GeometryNodeCurveResample") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeInputCurveHandlePositions") + yield NodeItem("GeometryNodeCurveParameter") + yield NodeItem("GeometryNodeInputTangent") + yield NodeItem("GeometryNodeInputCurveTilt") + yield NodeItem("GeometryNodeCurveHandleTypeSelection") + yield NodeItem("GeometryNodeInputSplineCyclic") + yield NodeItem("GeometryNodeSplineLength") + yield NodeItem("GeometryNodeInputSplineResolution") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeSetCurveRadius") + yield NodeItem("GeometryNodeSetCurveTilt") + yield NodeItem("GeometryNodeSetCurveHandlePositions") + yield NodeItem("GeometryNodeCurveSetHandles") + yield NodeItem("GeometryNodeSetSplineCyclic") + yield NodeItem("GeometryNodeSetSplineResolution") + yield NodeItem("GeometryNodeCurveSplineType") + +# Custom Menu for Geometry Node Curves +def mesh_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + if not space.edit_tree: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("GeometryNodeLegacyEdgeSplit", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_legacy_poll) + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeBoolean") + yield NodeItem("GeometryNodeMeshSubdivide") + yield NodeItem("GeometryNodePointsToVertices") + yield NodeItem("GeometryNodeTriangulate") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeInputShadeSmooth") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeSetShadeSmooth") + +# Custom Menu for Geometry Node Curves +def point_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + if not space.edit_tree: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyPointScale", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_legacy_poll) + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeDistributePointsOnFaces") + yield NodeItem("GeometryNodeInstanceOnPoints") + yield NodeItem("GeometryNodeMeshToPoints") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeSetPointRadius") # generic node group items generator for shader, compositor, geometry and texture node groups def node_group_items(context): @@ -510,32 +608,7 @@ geometry_node_categories = [ NodeItem("ShaderNodeSeparateRGB"), NodeItem("ShaderNodeCombineRGB"), ]), - GeometryNodeCategory("GEO_CURVE", "Curve", items=[ - NodeItem("GeometryNodeLegacyCurveSubdivide", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyCurveReverse", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyCurveSplineType", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyCurveSetHandles", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyCurveSelectHandles", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyMeshToCurve", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyCurveToPoints", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyCurveEndpoints", poll=geometry_nodes_legacy_poll), - - NodeItem("GeometryNodeCurveToMesh"), - NodeItem("GeometryNodeCurveResample"), - NodeItem("GeometryNodeCurveFill"), - NodeItem("GeometryNodeCurveTrim"), - NodeItem("GeometryNodeCurveLength"), - NodeItem("GeometryNodeCurveSplineType"), - NodeItem("GeometryNodeSplineLength"), - NodeItem("GeometryNodeCurveSubdivide"), - NodeItem("GeometryNodeCurveParameter"), - NodeItem("GeometryNodeCurveSetHandles"), - NodeItem("GeometryNodeInputTangent"), - NodeItem("GeometryNodeCurveSample"), - NodeItem("GeometryNodeCurveHandleTypeSelection"), - NodeItem("GeometryNodeCurveFillet"), - NodeItem("GeometryNodeCurveReverse"), - ]), + GeometryNodeCategory("GEO_CURVE", "Curve", items=curve_node_items), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ NodeItem("GeometryNodeCurvePrimitiveLine"), NodeItem("GeometryNodeCurvePrimitiveCircle"), @@ -562,7 +635,6 @@ geometry_node_categories = [ ]), GeometryNodeCategory("GEO_INPUT", "Input", items=[ NodeItem("FunctionNodeLegacyRandomFloat", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeObjectInfo"), NodeItem("GeometryNodeCollectionInfo"), NodeItem("ShaderNodeValue"), @@ -570,8 +642,9 @@ geometry_node_categories = [ NodeItem("FunctionNodeInputVector"), NodeItem("GeometryNodeInputMaterial"), NodeItem("GeometryNodeIsViewport"), - NodeItem("GeometryNodeInputPosition"), NodeItem("GeometryNodeInputIndex"), + NodeItem("GeometryNodeInputPosition"), + NodeItem("GeometryNodeInputRadius"), NodeItem("GeometryNodeInputNormal"), ]), GeometryNodeCategory("GEO_MATERIAL", "Material", items=[ @@ -582,15 +655,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeMaterialSelection"), NodeItem("GeometryNodeMaterialReplace"), ]), - GeometryNodeCategory("GEO_MESH", "Mesh", items=[ - NodeItem("GeometryNodeLegacyEdgeSplit", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_legacy_poll), - - NodeItem("GeometryNodeBoolean"), - NodeItem("GeometryNodeTriangulate"), - NodeItem("GeometryNodeMeshSubdivide"), - NodeItem("GeometryNodePointsToVertices"), - ]), + GeometryNodeCategory("GEO_MESH", "Mesh", items=mesh_node_items), GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[ NodeItem("GeometryNodeMeshCircle"), NodeItem("GeometryNodeMeshCone"), @@ -601,18 +666,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshLine"), NodeItem("GeometryNodeMeshUVSphere"), ]), - GeometryNodeCategory("GEO_POINT", "Point", items=[ - NodeItem("GeometryNodeMeshToPoints"), - NodeItem("GeometryNodeInstanceOnPoints"), - NodeItem("GeometryNodeDistributePointsOnFaces"), - NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyPointScale", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_legacy_poll), - ]), + GeometryNodeCategory("GEO_POINT", "Point", items=point_node_items), GeometryNodeCategory("GEO_TEXT", "Text", items=[ NodeItem("FunctionNodeStringLength"), NodeItem("FunctionNodeStringSubstring"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index ef9021fd465..e0aeb6e875f 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1517,6 +1517,19 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_HANDLE_TYPE_SELECTION 1102 #define GEO_NODE_DELETE_GEOMETRY 1103 #define GEO_NODE_SEPARATE_GEOMETRY 1104 +#define GEO_NODE_INPUT_RADIUS 1105 +#define GEO_NODE_INPUT_CURVE_TILT 1106 +#define GEO_NODE_INPUT_CURVE_HANDLES 1107 +#define GEO_NODE_INPUT_SHADE_SMOOTH 1108 +#define GEO_NODE_INPUT_SPLINE_RESOLUTION 1109 +#define GEO_NODE_INPUT_SPLINE_CYCLIC 1110 +#define GEO_NODE_SET_CURVE_RADIUS 1111 +#define GEO_NODE_SET_CURVE_TILT 1112 +#define GEO_NODE_SET_CURVE_HANDLES 1113 +#define GEO_NODE_SET_SHADE_SMOOTH 1114 +#define GEO_NODE_SET_SPLINE_RESOLUTION 1115 +#define GEO_NODE_SET_SPLINE_CYCLIC 1116 +#define GEO_NODE_SET_POINT_RADIUS 1117 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index bff2ed936d9..41f9bf46b81 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5720,6 +5720,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_curve_reverse(); register_node_type_geo_legacy_select_by_handle_type(); register_node_type_geo_legacy_curve_subdivide(); + register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); register_node_type_geo_attribute_clamp(); @@ -5767,12 +5768,18 @@ static void registerGeometryNodes() register_node_type_geo_delete_geometry(); register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_edge_split(); + register_node_type_geo_input_curve_handles(); + register_node_type_geo_input_curve_tilt(); register_node_type_geo_input_index(); register_node_type_geo_input_material(); register_node_type_geo_input_normal(); register_node_type_geo_input_position(); - register_node_type_geo_input_tangent(); + register_node_type_geo_input_radius(); + register_node_type_geo_input_shade_smooth(); + register_node_type_geo_input_spline_cyclic(); register_node_type_geo_input_spline_length(); + register_node_type_geo_input_spline_resolution(); + register_node_type_geo_input_tangent(); register_node_type_geo_instance_on_points(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); @@ -5805,7 +5812,14 @@ static void registerGeometryNodes() register_node_type_geo_sample_texture(); register_node_type_geo_separate_components(); register_node_type_geo_separate_geometry(); + register_node_type_geo_set_curve_handles(); + register_node_type_geo_set_curve_radius(); + register_node_type_geo_set_curve_tilt(); + register_node_type_geo_set_point_radius(); register_node_type_geo_set_position(); + register_node_type_geo_set_shade_smooth(); + register_node_type_geo_set_spline_cyclic(); + register_node_type_geo_set_spline_resolution(); register_node_type_geo_string_join(); register_node_type_geo_string_to_curves(); register_node_type_geo_subdivision_surface(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 52a3755a959..05adcc3b922 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1432,6 +1432,11 @@ typedef struct NodeGeometryCurveSplineType { uint8_t spline_type; } NodeGeometryCurveSplineType; +typedef struct NodeGeometrySetCurveHandlePositions { + /* GeometryNodeCurveHandleMode. */ + uint8_t mode; +} NodeGeometrySetCurveHandlePositions; + typedef struct NodeGeometryCurveSetHandles { /* GeometryNodeCurveHandleType. */ uint8_t handle_type; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 9c1ce0d7bc4..47b2c7ea5c6 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9689,6 +9689,18 @@ static void def_geo_legacy_curve_set_handles(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_set_handle_positions(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometrySetCurveHandlePositions", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); + RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_select_handles(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index c5213dc304b..8209d46ec24 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -337,6 +337,14 @@ static void get_socket_value(const SocketRef &socket, void *r_value) new (r_value) Field(bke::AttributeFieldInput::Create("position")); return; } + if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) { + StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode == + GEO_NODE_CURVE_HANDLE_LEFT ? + "handle_left" : + "handle_right"; + new (r_value) Field(bke::AttributeFieldInput::Create(side)); + return; + } } else if (bsocket.type == SOCK_INT) { if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index b29568a5c9f..a4e8df3164c 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -219,11 +219,17 @@ set(SRC geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_distribute_points_on_faces.cc + geometry/nodes/node_geo_input_curve_handles.cc + geometry/nodes/node_geo_input_curve_tilt.cc geometry/nodes/node_geo_input_index.cc geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_normal.cc geometry/nodes/node_geo_input_position.cc + geometry/nodes/node_geo_input_radius.cc + geometry/nodes/node_geo_input_shade_smooth.cc + geometry/nodes/node_geo_input_spline_cyclic.cc geometry/nodes/node_geo_input_spline_length.cc + geometry/nodes/node_geo_input_spline_resolution.cc geometry/nodes/node_geo_input_tangent.cc geometry/nodes/node_geo_instance_on_points.cc geometry/nodes/node_geo_is_viewport.cc @@ -248,7 +254,14 @@ set(SRC geometry/nodes/node_geo_realize_instances.cc geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_separate_geometry.cc + geometry/nodes/node_geo_set_curve_handles.cc + geometry/nodes/node_geo_set_curve_radius.cc + geometry/nodes/node_geo_set_curve_tilt.cc + geometry/nodes/node_geo_set_point_radius.cc geometry/nodes/node_geo_set_position.cc + geometry/nodes/node_geo_set_shade_smooth.cc + geometry/nodes/node_geo_set_spline_cyclic.cc + geometry/nodes/node_geo_set_spline_resolution.cc geometry/nodes/node_geo_string_join.cc geometry/nodes/node_geo_string_to_curves.cc geometry/nodes/node_geo_switch.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index baa841460e9..2d409d6d80b 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -88,11 +88,17 @@ void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_edge_split(void); +void register_node_type_geo_input_curve_handles(void); +void register_node_type_geo_input_curve_tilt(void); void register_node_type_geo_input_index(void); void register_node_type_geo_input_material(void); void register_node_type_geo_input_normal(void); void register_node_type_geo_input_position(void); +void register_node_type_geo_input_radius(void); +void register_node_type_geo_input_shade_smooth(void); +void register_node_type_geo_input_spline_cyclic(void); void register_node_type_geo_input_spline_length(void); +void register_node_type_geo_input_spline_resolution(void); void register_node_type_geo_input_tangent(void); void register_node_type_geo_instance_on_points(void); void register_node_type_geo_is_viewport(void); @@ -127,7 +133,14 @@ void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_separate_components(void); void register_node_type_geo_separate_geometry(void); +void register_node_type_geo_set_curve_handles(void); +void register_node_type_geo_set_curve_radius(void); +void register_node_type_geo_set_curve_tilt(void); +void register_node_type_geo_set_point_radius(void); void register_node_type_geo_set_position(void); +void register_node_type_geo_set_shade_smooth(void); +void register_node_type_geo_set_spline_cyclic(void); +void register_node_type_geo_set_spline_resolution(void); void register_node_type_geo_string_join(void); void register_node_type_geo_string_to_curves(void); void register_node_type_geo_subdivision_surface(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 1acd082377f..e43f471d1e3 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -338,20 +338,26 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_prim DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Reverse Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Sample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Subdivide Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Trim Curve", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") +DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "") +DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") +DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "") +DefNode(GeometryNode, GEO_NODE_INPUT_SHADE_SMOOTH, 0, "INPUT_SHADE_SMOOTH", InputShadeSmooth, "Is Shade Smooth", "") +DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_CYCLIC, 0, "INPUT_SPLINE_CYCLIC", InputSplineCyclic, "Is Spline Cyclic", "") DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "") +DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_RESOLUTION, 0, "INPUT_SPLINE_RESOLUTION", InputSplineResolution, "Spline Resolution", "") DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "") DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") @@ -376,7 +382,14 @@ DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proxim DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "") +DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "") +DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "") +DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "") +DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") +DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "") +DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "") +DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "") DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "") DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc new file mode 100644 index 00000000000..604b181918d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -0,0 +1,46 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_curve_handles_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Left").field_source(); + b.add_output("Right").field_source(); +} + +static void geo_node_input_curve_handles_exec(GeoNodeExecParams params) +{ + Field left_field = AttributeFieldInput::Create("handle_left"); + Field right_field = AttributeFieldInput::Create("handle_right"); + params.set_output("Left", std::move(left_field)); + params.set_output("Right", std::move(right_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_curve_handles() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_INPUT_CURVE_HANDLES, "Curve Handle Positions", NODE_CLASS_INPUT, 0); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + ntype.geometry_node_execute = blender::nodes::geo_node_input_curve_handles_exec; + ntype.declare = blender::nodes::geo_node_input_curve_handles_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc new file mode 100644 index 00000000000..5a24b7f3f07 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_curve_tilt_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Tilt").field_source(); +} + +static void geo_node_input_curve_tilt_exec(GeoNodeExecParams params) +{ + Field tilt_field = AttributeFieldInput::Create("tilt"); + params.set_output("Tilt", std::move(tilt_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_curve_tilt() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_CURVE_TILT, "Curve Tilt", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_curve_tilt_exec; + ntype.declare = blender::nodes::geo_node_input_curve_tilt_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc new file mode 100644 index 00000000000..586005511ad --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_radius_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Radius").default_value(1.0f).min(0.0f).field_source(); +} + +static void geo_node_input_radius_exec(GeoNodeExecParams params) +{ + Field radius_field = AttributeFieldInput::Create("radius"); + params.set_output("Radius", std::move(radius_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_radius() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_RADIUS, "Radius", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_radius_exec; + ntype.declare = blender::nodes::geo_node_input_radius_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc new file mode 100644 index 00000000000..de520787e78 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_shade_smooth_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Smooth").field_source(); +} + +static void geo_node_input_shade_smooth_exec(GeoNodeExecParams params) +{ + Field shade_smooth_field = AttributeFieldInput::Create("shade_smooth"); + params.set_output("Smooth", std::move(shade_smooth_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_shade_smooth() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_SHADE_SMOOTH, "Is Shade Smooth", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_shade_smooth_exec; + ntype.declare = blender::nodes::geo_node_input_shade_smooth_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc new file mode 100644 index 00000000000..44a1bb62de8 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc @@ -0,0 +1,43 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_spline_cyclic_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Cyclic").field_source(); +} + +static void geo_node_input_spline_cyclic_exec(GeoNodeExecParams params) +{ + Field cyclic_field = AttributeFieldInput::Create("cyclic"); + params.set_output("Cyclic", std::move(cyclic_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_spline_cyclic() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_SPLINE_CYCLIC, "Is Spline Cyclic", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_spline_cyclic_exec; + ntype.declare = blender::nodes::geo_node_input_spline_cyclic_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc new file mode 100644 index 00000000000..eab95ebc46e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc @@ -0,0 +1,43 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_spline_resolution_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Resolution").field_source(); +} + +static void geo_node_input_spline_resolution_exec(GeoNodeExecParams params) +{ + Field resolution_field = AttributeFieldInput::Create("resolution"); + params.set_output("Resolution", std::move(resolution_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_spline_resolution() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_SPLINE_RESOLUTION, "Spline Resolution", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_spline_resolution_exec; + ntype.declare = blender::nodes::geo_node_input_spline_resolution_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc new file mode 100644 index 00000000000..3e106a92b14 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -0,0 +1,164 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_curve_handles_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Position").implicit_field(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Geometry"); +} + +static void geo_node_set_curve_handles_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_set_curve_handles_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometrySetCurveHandlePositions *data = (NodeGeometrySetCurveHandlePositions *)MEM_callocN( + sizeof(NodeGeometrySetCurveHandlePositions), __func__); + + data->mode = GEO_NODE_CURVE_HANDLE_LEFT; + node->storage = data; +} + +static void set_position_in_component(const GeometryNodeCurveHandleMode mode, + GeometryComponent &component, + const Field &selection_field, + const Field &position_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + CurveComponent *curve_component = static_cast(&component); + CurveEval *curve = curve_component->get_for_write(); + + StringRef side = mode & GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : "handle_right"; + + int current_point = 0; + int current_mask = 0; + + for (const SplinePtr &spline : curve->splines()) { + if (spline->type() == Spline::Type::Bezier) { + BezierSpline &bezier = static_cast(*spline); + for (int i : bezier.positions().index_range()) { + if (selection[current_mask] == current_point) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Vector) { + bezier.handle_types_left()[i] = BezierSpline::HandleType::Free; + } + else if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Auto) { + bezier.handle_types_left()[i] = BezierSpline::HandleType::Align; + } + } + else { + if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Vector) { + bezier.handle_types_right()[i] = BezierSpline::HandleType::Free; + } + else if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Auto) { + bezier.handle_types_right()[i] = BezierSpline::HandleType::Align; + } + } + current_mask++; + } + current_point++; + } + } + else { + for (int UNUSED(i) : spline->positions().index_range()) { + if (selection[current_mask] == current_point) { + current_mask++; + } + current_point++; + } + } + } + + OutputAttribute_Typed positions = component.attribute_try_get_for_output_only( + side, ATTR_DOMAIN_POINT); + fn::FieldEvaluator position_evaluator{field_context, &selection}; + position_evaluator.add_with_destination(position_field, positions.varray()); + position_evaluator.evaluate(); + positions.save(); +} + +static void geo_node_set_curve_handles_exec(GeoNodeExecParams params) +{ + const NodeGeometrySetCurveHandlePositions *node_storage = + (NodeGeometrySetCurveHandlePositions *)params.node().storage; + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode; + + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field position_field = params.extract_input>("Position"); + + bool has_bezier = false; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve() && + geometry_set.get_curve_for_read()->has_spline_with_type(Spline::Type::Bezier)) { + has_bezier = true; + set_position_in_component(mode, + geometry_set.get_component_for_write(), + selection_field, + position_field); + } + }); + if (!has_bezier) { + params.error_message_add(NodeWarningType::Info, + TIP_("The input geometry does not contain a Bezier spline")); + } + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_curve_handles() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_CURVE_HANDLES, "Set Handle Positions", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_curve_handles_exec; + ntype.declare = blender::nodes::geo_node_set_curve_handles_declare; + ntype.minwidth = 100.0f; + node_type_init(&ntype, blender::nodes::geo_node_set_curve_handles_init); + node_type_storage(&ntype, + "NodeGeometrySetCurveHandlePositions", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_set_curve_handles_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc new file mode 100644 index 00000000000..446e63d0471 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_curve_radius_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Radius").min(0.0f).default_value(1.0f).supports_field(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Geometry"); +} + +static void set_radius_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &radius_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed radii = component.attribute_try_get_for_output_only( + "radius", ATTR_DOMAIN_POINT); + fn::FieldEvaluator radii_evaluator{field_context, &selection}; + radii_evaluator.add_with_destination(radius_field, radii.varray()); + radii_evaluator.evaluate(); + radii.save(); +} + +static void geo_node_set_curve_radius_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field radii_field = params.extract_input>("Radius"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + set_radius_in_component( + geometry_set.get_component_for_write(), selection_field, radii_field); + } + }); + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_curve_radius() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_CURVE_RADIUS, "Set Curve Radius", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_curve_radius_exec; + ntype.declare = blender::nodes::geo_node_set_curve_radius_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc new file mode 100644 index 00000000000..ee88b24fb04 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_curve_tilt_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Tilt").subtype(PROP_ANGLE).supports_field(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Geometry"); +} + +static void set_tilt_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &tilt_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed tilts = component.attribute_try_get_for_output_only( + "tilt", ATTR_DOMAIN_POINT); + fn::FieldEvaluator tilt_evaluator{field_context, &selection}; + tilt_evaluator.add_with_destination(tilt_field, tilts.varray()); + tilt_evaluator.evaluate(); + tilts.save(); +} + +static void geo_node_set_curve_tilt_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field tilt_field = params.extract_input>("Tilt"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + set_tilt_in_component( + geometry_set.get_component_for_write(), selection_field, tilt_field); + } + }); + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_curve_tilt() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_CURVE_TILT, "Set Tilt", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_curve_tilt_exec; + ntype.declare = blender::nodes::geo_node_set_curve_tilt_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc new file mode 100644 index 00000000000..18646c789b4 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -0,0 +1,80 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_point_radius_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Radius").default_value(0.05f).min(0.0f).supports_field(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Geometry"); +} + +static void set_radius_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &radius_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed radii = component.attribute_try_get_for_output_only( + "radius", ATTR_DOMAIN_POINT); + fn::FieldEvaluator radii_evaluator{field_context, &selection}; + radii_evaluator.add_with_destination(radius_field, radii.varray()); + radii_evaluator.evaluate(); + radii.save(); +} + +static void geo_node_set_point_radius_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field radii_field = params.extract_input>("Radius"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_pointcloud()) { + set_radius_in_component(geometry_set.get_component_for_write(), + selection_field, + radii_field); + } + }); + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_point_radius() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_POINT_RADIUS, "Set Point Radius", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_point_radius_exec; + ntype.declare = blender::nodes::geo_node_set_point_radius_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc new file mode 100644 index 00000000000..1d7bab4a6bb --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_shade_smooth_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Shade Smooth").supports_field(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Geometry"); +} + +static void set_smooth_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &shade_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed shades = component.attribute_try_get_for_output_only( + "shade_smooth", ATTR_DOMAIN_FACE); + fn::FieldEvaluator shade_evaluator{field_context, &selection}; + shade_evaluator.add_with_destination(shade_field, shades.varray()); + shade_evaluator.evaluate(); + shades.save(); +} + +static void geo_node_set_shade_smooth_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field shade_field = params.extract_input>("Shade Smooth"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_mesh()) { + set_smooth_in_component( + geometry_set.get_component_for_write(), selection_field, shade_field); + } + }); + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_shade_smooth() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_SHADE_SMOOTH, "Set Shade Smooth", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_shade_smooth_exec; + ntype.declare = blender::nodes::geo_node_set_shade_smooth_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc new file mode 100644 index 00000000000..c013e6f0ba4 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_spline_cyclic_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Cyclic").supports_field(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Geometry"); +} + +static void set_cyclic_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &cyclic_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed cyclics = component.attribute_try_get_for_output_only( + "cyclic", ATTR_DOMAIN_CURVE); + fn::FieldEvaluator cyclic_evaluator{field_context, &selection}; + cyclic_evaluator.add_with_destination(cyclic_field, cyclics.varray()); + cyclic_evaluator.evaluate(); + cyclics.save(); +} + +static void geo_node_set_spline_cyclic_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field cyclic_field = params.extract_input>("Cyclic"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + set_cyclic_in_component( + geometry_set.get_component_for_write(), selection_field, cyclic_field); + } + }); + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_spline_cyclic() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_SPLINE_CYCLIC, "Set Spline Cyclic", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_spline_cyclic_exec; + ntype.declare = blender::nodes::geo_node_set_spline_cyclic_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc new file mode 100644 index 00000000000..fc8706f3223 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -0,0 +1,95 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_spline_resolution_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Resolution").default_value(12).supports_field(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Geometry"); +} + +static void set_resolution_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &resolution_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed resolutions = component.attribute_try_get_for_output_only( + "resolution", ATTR_DOMAIN_CURVE); + fn::FieldEvaluator resolution_evaluator{field_context, &selection}; + resolution_evaluator.add_with_destination(resolution_field, resolutions.varray()); + resolution_evaluator.evaluate(); + resolutions.save(); +} + +static void geo_node_set_spline_resolution_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field resolution_field = params.extract_input>("Resolution"); + + bool only_poly = true; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + if (!only_poly) { + for (const SplinePtr &spline : geometry_set.get_curve_for_read()->splines()) { + if (ELEM(spline->type(), Spline::Type::Bezier, Spline::Type::NURBS)) { + only_poly = false; + break; + } + } + } + set_resolution_in_component(geometry_set.get_component_for_write(), + selection_field, + resolution_field); + } + }); + + if (only_poly) { + params.error_message_add(NodeWarningType::Warning, + TIP_("Input geometry does not contain a Bezier or NURB spline")); + } + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_spline_resolution() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_SPLINE_RESOLUTION, "Set Spline Resolution", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_spline_resolution_exec; + ntype.declare = blender::nodes::geo_node_set_spline_resolution_declare; + nodeRegisterType(&ntype); +} From bb6cc67d0511bed3e106c88103826d46fbde13ee Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 11 Oct 2021 11:10:39 -0500 Subject: [PATCH 0681/1500] UI: Reduce whitespace in custom node categories This makes the long "Curve" category take up less space. --- release/scripts/modules/nodeitems_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py index a5c18cee463..f6b6aaf03cf 100644 --- a/release/scripts/modules/nodeitems_utils.py +++ b/release/scripts/modules/nodeitems_utils.py @@ -108,7 +108,7 @@ def register_node_categories(identifier, cat_list): # works as draw function for menus def draw_node_item(self, context): layout = self.layout - col = layout.column() + col = layout.column(align=True) for item in self.category.items(context): item.draw(item, col, context) From 40360253aefd1f3451d5b413595bcbb143425b84 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 11 Oct 2021 18:18:51 +0200 Subject: [PATCH 0682/1500] Fix T86671: Background Scene Dupliface Not Instanced. Use depsgraph's objects iterator to find sources of dupliobjects, instead of looping over bases of a viewlayer. --- .../blender/blenkernel/intern/object_dupli.cc | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 58b19805407..666a31a9e3f 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -88,7 +88,6 @@ struct DupliContext { Object *obedit; Scene *scene; - ViewLayer *view_layer; Object *object; float space_mat[4][4]; @@ -127,7 +126,6 @@ static void init_context(DupliContext *r_ctx, { r_ctx->depsgraph = depsgraph; r_ctx->scene = scene; - r_ctx->view_layer = DEG_get_evaluated_view_layer(depsgraph); r_ctx->collection = nullptr; r_ctx->object = ob; @@ -311,13 +309,18 @@ static void make_child_duplis(const DupliContext *ctx, FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; } else { - int baseid; - ViewLayer *view_layer = ctx->view_layer; - LISTBASE_FOREACH_INDEX (Base *, base, &view_layer->object_bases, baseid) { - Object *ob = base->object; + /* FIXME: using a mere counter to generate a 'persistent' dupli id is very weak. One possible + * better solution could be to use `session_uuid` of ID's instead? */ + int persistent_dupli_id = 0; + /* NOTE: this set of flags ensure we only iterate over objects that have a base in either the + * current scene, or the set (background) scene. */ + int deg_objects_visibility_flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | + DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET; + + DEG_OBJECT_ITER_BEGIN (ctx->depsgraph, ob, deg_objects_visibility_flags) { if ((ob != ctx->obedit) && is_child(ob, parent)) { DupliContext pctx; - copy_dupli_context(&pctx, ctx, ctx->object, nullptr, baseid); + copy_dupli_context(&pctx, ctx, ctx->object, nullptr, persistent_dupli_id); /* Meta-balls have a different dupli-handling. */ if (ob->type != OB_MBALL) { @@ -326,7 +329,9 @@ static void make_child_duplis(const DupliContext *ctx, make_child_duplis_cb(&pctx, userdata, ob); } + persistent_dupli_id++; } + DEG_OBJECT_ITER_END; } } From 73a05ff9e83a31be34d32a92cd5fb4d17994e342 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 7 Oct 2021 17:27:22 +0200 Subject: [PATCH 0683/1500] Cycles: restore Christensen-Burley SSS There is not enough time before the release to improve Random Walk to handle all cases this was used for, so restore it for now. Since there is no more path splitting in cycles-x, this can increase noise in non-flat areas for the sample number of samples, though fewer rays will be traced also. This is fundamentally a trade-off we made in the new design and why Random Walk is a better fit. However the importance resampling we do now does help to reduce noise. Differential Revision: https://developer.blender.org/D12800 --- intern/cycles/blender/blender_shader.cpp | 6 + intern/cycles/kernel/CMakeLists.txt | 2 + intern/cycles/kernel/bvh/bvh_util.h | 24 + intern/cycles/kernel/closure/bssrdf.h | 174 ++++++- .../kernel/integrator/integrator_subsurface.h | 460 +---------------- .../integrator/integrator_subsurface_disk.h | 195 ++++++++ .../integrator_subsurface_random_walk.h | 465 ++++++++++++++++++ intern/cycles/kernel/kernel_types.h | 20 +- intern/cycles/kernel/osl/osl_bssrdf.cpp | 6 +- intern/cycles/kernel/svm/svm_closure.h | 1 + intern/cycles/kernel/svm/svm_types.h | 3 +- intern/cycles/render/nodes.cpp | 2 + .../blenloader/intern/versioning_280.c | 2 +- .../blenloader/intern/versioning_300.c | 4 +- .../blenloader/intern/versioning_cycles.c | 4 +- source/blender/makesdna/DNA_node_types.h | 2 +- source/blender/makesrna/intern/rna_nodetree.c | 5 + .../nodes/node_shader_bsdf_principled.c | 10 + 18 files changed, 922 insertions(+), 463 deletions(-) create mode 100644 intern/cycles/kernel/integrator/integrator_subsurface_disk.h create mode 100644 intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 0b8aea15d6c..db5eadeed56 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -489,6 +489,9 @@ static ShaderNode *add_node(Scene *scene, SubsurfaceScatteringNode *subsurface = graph->create_node(); switch (b_subsurface_node.falloff()) { + case BL::ShaderNodeSubsurfaceScattering::falloff_BURLEY: + subsurface->set_method(CLOSURE_BSSRDF_BURLEY_ID); + break; case BL::ShaderNodeSubsurfaceScattering::falloff_RANDOM_WALK_FIXED_RADIUS: subsurface->set_method(CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); break; @@ -605,6 +608,9 @@ static ShaderNode *add_node(Scene *scene, break; } switch (b_principled_node.subsurface_method()) { + case BL::ShaderNodeBsdfPrincipled::subsurface_method_BURLEY: + principled->set_subsurface_method(CLOSURE_BSSRDF_BURLEY_ID); + break; case BL::ShaderNodeBsdfPrincipled::subsurface_method_RANDOM_WALK_FIXED_RADIUS: principled->set_subsurface_method(CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); break; diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index c53d3d4b962..e0d48361650 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -241,6 +241,8 @@ set(SRC_INTEGRATOR_HEADERS integrator/integrator_state_template.h integrator/integrator_state_util.h integrator/integrator_subsurface.h + integrator/integrator_subsurface_disk.h + integrator/integrator_subsurface_random_walk.h integrator/integrator_volume_stack.h ) diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index 9f188a93e2c..e16da9755f2 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -113,6 +113,30 @@ ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits) } #endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */ +/* For subsurface scattering, only sorting a small amount of intersections + * so bubble sort is fine for CPU and GPU. */ +ccl_device_inline void sort_intersections_and_normals(Intersection *hits, + float3 *Ng, + uint num_hits) +{ + bool swapped; + do { + swapped = false; + for (int j = 0; j < num_hits - 1; ++j) { + if (hits[j].t > hits[j + 1].t) { + struct Intersection tmp_hit = hits[j]; + struct float3 tmp_Ng = Ng[j]; + hits[j] = hits[j + 1]; + Ng[j] = Ng[j + 1]; + hits[j + 1] = tmp_hit; + Ng[j + 1] = tmp_Ng; + swapped = true; + } + } + --num_hits; + } while (swapped); +} + /* Utility to quickly get flags from an intersection. */ ccl_device_forceinline int intersection_get_shader_flags(const KernelGlobals *ccl_restrict kg, diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index e095314678a..db183887018 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -29,6 +29,8 @@ typedef ccl_addr_space struct Bssrdf { static_assert(sizeof(ShaderClosure) >= sizeof(Bssrdf), "Bssrdf is too large!"); +/* Random Walk BSSRDF */ + ccl_device float bssrdf_dipole_compute_Rd(float alpha_prime, float fourthirdA) { float s = sqrtf(3.0f * (1.0f - alpha_prime)); @@ -66,7 +68,7 @@ ccl_device float bssrdf_dipole_compute_alpha_prime(float rd, float fourthirdA) ccl_device void bssrdf_setup_radius(Bssrdf *bssrdf, const ClosureType type, const float eta) { - if (type == CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) { + if (type == CLOSURE_BSSRDF_BURLEY_ID || type == CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) { /* Scale mean free path length so it gives similar looking result to older * Cubic, Gaussian and Burley models. */ bssrdf->radius *= 0.25f * M_1_PI_F; @@ -87,6 +89,176 @@ ccl_device void bssrdf_setup_radius(Bssrdf *bssrdf, const ClosureType type, cons } } +/* Christensen-Burley BSSRDF. + * + * Approximate Reflectance Profiles from + * http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf + */ + +/* This is a bit arbitrary, just need big enough radius so it matches + * the mean free length, but still not too big so sampling is still + * effective. */ +#define BURLEY_TRUNCATE 16.0f +#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) + +ccl_device_inline float bssrdf_burley_fitting(float A) +{ + /* Diffuse surface transmission, equation (6). */ + return 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f); +} + +/* Scale mean free path length so it gives similar looking result + * to Cubic and Gaussian models. */ +ccl_device_inline float3 bssrdf_burley_compatible_mfp(float3 r) +{ + return 0.25f * M_1_PI_F * r; +} + +ccl_device void bssrdf_burley_setup(Bssrdf *bssrdf) +{ + /* Mean free path length. */ + const float3 l = bssrdf_burley_compatible_mfp(bssrdf->radius); + /* Surface albedo. */ + const float3 A = bssrdf->albedo; + const float3 s = make_float3( + bssrdf_burley_fitting(A.x), bssrdf_burley_fitting(A.y), bssrdf_burley_fitting(A.z)); + + bssrdf->radius = l / s; +} + +ccl_device float bssrdf_burley_eval(const float d, float r) +{ + const float Rm = BURLEY_TRUNCATE * d; + + if (r >= Rm) + return 0.0f; + + /* Burley reflectance profile, equation (3). + * + * NOTES: + * - Surface albedo is already included into sc->weight, no need to + * multiply by this term here. + * - This is normalized diffuse model, so the equation is multiplied + * by 2*pi, which also matches cdf(). + */ + float exp_r_3_d = expf(-r / (3.0f * d)); + float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; + return (exp_r_d + exp_r_3_d) / (4.0f * d); +} + +ccl_device float bssrdf_burley_pdf(const float d, float r) +{ + if (r == 0.0f) { + return 0.0f; + } + + return bssrdf_burley_eval(d, r) * (1.0f / BURLEY_TRUNCATE_CDF); +} + +/* Find the radius for desired CDF value. + * Returns scaled radius, meaning the result is to be scaled up by d. + * Since there's no closed form solution we do Newton-Raphson method to find it. + */ +ccl_device_forceinline float bssrdf_burley_root_find(float xi) +{ + const float tolerance = 1e-6f; + const int max_iteration_count = 10; + /* Do initial guess based on manual curve fitting, this allows us to reduce + * number of iterations to maximum 4 across the [0..1] range. We keep maximum + * number of iteration higher just to be sure we didn't miss root in some + * corner case. + */ + float r; + if (xi <= 0.9f) { + r = expf(xi * xi * 2.4f) - 1.0f; + } + else { + /* TODO(sergey): Some nicer curve fit is possible here. */ + r = 15.0f; + } + /* Solve against scaled radius. */ + for (int i = 0; i < max_iteration_count; i++) { + float exp_r_3 = expf(-r / 3.0f); + float exp_r = exp_r_3 * exp_r_3 * exp_r_3; + float f = 1.0f - 0.25f * exp_r - 0.75f * exp_r_3 - xi; + float f_ = 0.25f * exp_r + 0.25f * exp_r_3; + + if (fabsf(f) < tolerance || f_ == 0.0f) { + break; + } + + r = r - f / f_; + if (r < 0.0f) { + r = 0.0f; + } + } + return r; +} + +ccl_device void bssrdf_burley_sample(const float d, float xi, float *r, float *h) +{ + const float Rm = BURLEY_TRUNCATE * d; + const float r_ = bssrdf_burley_root_find(xi * BURLEY_TRUNCATE_CDF) * d; + + *r = r_; + + /* h^2 + r^2 = Rm^2 */ + *h = safe_sqrtf(Rm * Rm - r_ * r_); +} + +ccl_device float bssrdf_num_channels(const float3 radius) +{ + float channels = 0; + if (radius.x > 0.0f) { + channels += 1.0f; + } + if (radius.y > 0.0f) { + channels += 1.0f; + } + if (radius.z > 0.0f) { + channels += 1.0f; + } + return channels; +} + +ccl_device void bssrdf_sample(const float3 radius, float xi, float *r, float *h) +{ + const float num_channels = bssrdf_num_channels(radius); + float sampled_radius; + + /* Sample color channel and reuse random number. Only a subset of channels + * may be used if their radius was too small to handle as BSSRDF. */ + xi *= num_channels; + + if (xi < 1.0f) { + sampled_radius = (radius.x > 0.0f) ? radius.x : (radius.y > 0.0f) ? radius.y : radius.z; + } + else if (xi < 2.0f) { + xi -= 1.0f; + sampled_radius = (radius.x > 0.0f && radius.y > 0.0f) ? radius.y : radius.z; + } + else { + xi -= 2.0f; + sampled_radius = radius.z; + } + + /* Sample BSSRDF. */ + bssrdf_burley_sample(sampled_radius, xi, r, h); +} + +ccl_device_forceinline float3 bssrdf_eval(const float3 radius, float r) +{ + return make_float3(bssrdf_burley_pdf(radius.x, r), + bssrdf_burley_pdf(radius.y, r), + bssrdf_burley_pdf(radius.z, r)); +} + +ccl_device_forceinline float bssrdf_pdf(const float3 radius, float r) +{ + float3 pdf = bssrdf_eval(radius, r); + return (pdf.x + pdf.y + pdf.z) / bssrdf_num_channels(radius); +} + /* Setup */ ccl_device_inline Bssrdf *bssrdf_alloc(ShaderData *sd, float3 weight) diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/integrator_subsurface.h index 9026de1c064..7ca676351db 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface.h @@ -29,6 +29,8 @@ #include "kernel/closure/volume.h" #include "kernel/integrator/integrator_intersect_volume_stack.h" +#include "kernel/integrator/integrator_subsurface_disk.h" +#include "kernel/integrator/integrator_subsurface_random_walk.h" CCL_NAMESPACE_BEGIN @@ -56,7 +58,10 @@ ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, ShaderData *sd, const Sh /* Pass BSSRDF parameters. */ const uint32_t path_flag = INTEGRATOR_STATE_WRITE(path, flag); - INTEGRATOR_STATE_WRITE(path, flag) = (path_flag & ~PATH_RAY_CAMERA) | PATH_RAY_SUBSURFACE; + INTEGRATOR_STATE_WRITE(path, flag) = (path_flag & ~PATH_RAY_CAMERA) | + ((sc->type == CLOSURE_BSSRDF_BURLEY_ID) ? + PATH_RAY_SUBSURFACE_DISK : + PATH_RAY_SUBSURFACE_RANDOM_WALK); INTEGRATOR_STATE_WRITE(path, throughput) *= shader_bssrdf_sample_weight(sd, sc); /* Advance random number offset for bounce. */ @@ -123,448 +128,6 @@ ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, ShaderData * } } -/* Random walk subsurface scattering. - * - * "Practical and Controllable Subsurface Scattering for Production Path - * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ - -/* Support for anisotropy from: - * "Path Traced Subsurface Scattering using Anisotropic Phase Functions - * and Non-Exponential Free Flights". - * Magnus Wrenninge, Ryusuke Villemin, Christophe Hery. - * https://graphics.pixar.com/library/PathTracedSubsurface/ */ - -ccl_device void subsurface_random_walk_remap( - const float albedo, const float d, float g, float *sigma_t, float *alpha) -{ - /* Compute attenuation and scattering coefficients from albedo. */ - const float g2 = g * g; - const float g3 = g2 * g; - const float g4 = g3 * g; - const float g5 = g4 * g; - const float g6 = g5 * g; - const float g7 = g6 * g; - - const float A = 1.8260523782f + -1.28451056436f * g + -1.79904629312f * g2 + - 9.19393289202f * g3 + -22.8215585862f * g4 + 32.0234874259f * g5 + - -23.6264803333f * g6 + 7.21067002658f * g7; - const float B = 4.98511194385f + - 0.127355959438f * - expf(31.1491581433f * g + -201.847017512f * g2 + 841.576016723f * g3 + - -2018.09288505f * g4 + 2731.71560286f * g5 + -1935.41424244f * g6 + - 559.009054474f * g7); - const float C = 1.09686102424f + -0.394704063468f * g + 1.05258115941f * g2 + - -8.83963712726f * g3 + 28.8643230661f * g4 + -46.8802913581f * g5 + - 38.5402837518f * g6 + -12.7181042538f * g7; - const float D = 0.496310210422f + 0.360146581622f * g + -2.15139309747f * g2 + - 17.8896899217f * g3 + -55.2984010333f * g4 + 82.065982243f * g5 + - -58.5106008578f * g6 + 15.8478295021f * g7; - const float E = 4.23190299701f + - 0.00310603949088f * - expf(76.7316253952f * g + -594.356773233f * g2 + 2448.8834203f * g3 + - -5576.68528998f * g4 + 7116.60171912f * g5 + -4763.54467887f * g6 + - 1303.5318055f * g7); - const float F = 2.40602999408f + -2.51814844609f * g + 9.18494908356f * g2 + - -79.2191708682f * g3 + 259.082868209f * g4 + -403.613804597f * g5 + - 302.85712436f * g6 + -87.4370473567f * g7; - - const float blend = powf(albedo, 0.25f); - - *alpha = (1.0f - blend) * A * powf(atanf(B * albedo), C) + - blend * D * powf(atanf(E * albedo), F); - *alpha = clamp(*alpha, 0.0f, 0.999999f); // because of numerical precision - - float sigma_t_prime = 1.0f / fmaxf(d, 1e-16f); - *sigma_t = sigma_t_prime / (1.0f - g); -} - -ccl_device void subsurface_random_walk_coefficients(const float3 albedo, - const float3 radius, - const float anisotropy, - float3 *sigma_t, - float3 *alpha, - float3 *throughput) -{ - float sigma_t_x, sigma_t_y, sigma_t_z; - float alpha_x, alpha_y, alpha_z; - - subsurface_random_walk_remap(albedo.x, radius.x, anisotropy, &sigma_t_x, &alpha_x); - subsurface_random_walk_remap(albedo.y, radius.y, anisotropy, &sigma_t_y, &alpha_y); - subsurface_random_walk_remap(albedo.z, radius.z, anisotropy, &sigma_t_z, &alpha_z); - - /* Throughput already contains closure weight at this point, which includes the - * albedo, as well as closure mixing and Fresnel weights. Divide out the albedo - * which will be added through scattering. */ - *throughput = safe_divide_color(*throughput, albedo); - - /* With low albedo values (like 0.025) we get diffusion_length 1.0 and - * infinite phase functions. To avoid a sharp discontinuity as we go from - * such values to 0.0, increase alpha and reduce the throughput to compensate. */ - const float min_alpha = 0.2f; - if (alpha_x < min_alpha) { - (*throughput).x *= alpha_x / min_alpha; - alpha_x = min_alpha; - } - if (alpha_y < min_alpha) { - (*throughput).y *= alpha_y / min_alpha; - alpha_y = min_alpha; - } - if (alpha_z < min_alpha) { - (*throughput).z *= alpha_z / min_alpha; - alpha_z = min_alpha; - } - - *sigma_t = make_float3(sigma_t_x, sigma_t_y, sigma_t_z); - *alpha = make_float3(alpha_x, alpha_y, alpha_z); -} - -/* References for Dwivedi sampling: - * - * [1] "A Zero-variance-based Sampling Scheme for Monte Carlo Subsurface Scattering" - * by Jaroslav Křivánek and Eugene d'Eon (SIGGRAPH 2014) - * https://cgg.mff.cuni.cz/~jaroslav/papers/2014-zerovar/ - * - * [2] "Improving the Dwivedi Sampling Scheme" - * by Johannes Meng, Johannes Hanika, and Carsten Dachsbacher (EGSR 2016) - * https://cg.ivd.kit.edu/1951.php - * - * [3] "Zero-Variance Theory for Efficient Subsurface Scattering" - * by Eugene d'Eon and Jaroslav Křivánek (SIGGRAPH 2020) - * https://iliyan.com/publications/RenderingCourse2020 - */ - -ccl_device_forceinline float eval_phase_dwivedi(float v, float phase_log, float cos_theta) -{ - /* Eq. 9 from [2] using precomputed log((v + 1) / (v - 1)) */ - return 1.0f / ((v - cos_theta) * phase_log); -} - -ccl_device_forceinline float sample_phase_dwivedi(float v, float phase_log, float rand) -{ - /* Based on Eq. 10 from [2]: `v - (v + 1) * pow((v - 1) / (v + 1), rand)` - * Since we're already pre-computing `phase_log = log((v + 1) / (v - 1))` for the evaluation, - * we can implement the power function like this. */ - return v - (v + 1.0f) * expf(-rand * phase_log); -} - -ccl_device_forceinline float diffusion_length_dwivedi(float alpha) -{ - /* Eq. 67 from [3] */ - return 1.0f / sqrtf(1.0f - powf(alpha, 2.44294f - 0.0215813f * alpha + 0.578637f / alpha)); -} - -ccl_device_forceinline float3 direction_from_cosine(float3 D, float cos_theta, float randv) -{ - float sin_theta = safe_sqrtf(1.0f - cos_theta * cos_theta); - float phi = M_2PI_F * randv; - float3 dir = make_float3(sin_theta * cosf(phi), sin_theta * sinf(phi), cos_theta); - - float3 T, B; - make_orthonormals(D, &T, &B); - return dir.x * T + dir.y * B + dir.z * D; -} - -ccl_device_forceinline float3 subsurface_random_walk_pdf(float3 sigma_t, - float t, - bool hit, - float3 *transmittance) -{ - float3 T = volume_color_transmittance(sigma_t, t); - if (transmittance) { - *transmittance = T; - } - return hit ? T : sigma_t * T; -} - -/* Define the below variable to get the similarity code active, - * and the value represents the cutoff level */ -# define SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL 9 - -ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, - RNGState rng_state, - Ray &ray, - LocalIntersection &ss_isect) -{ - float bssrdf_u, bssrdf_v; - path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); - - const float3 P = INTEGRATOR_STATE(ray, P); - const float3 N = INTEGRATOR_STATE(ray, D); - const float ray_dP = INTEGRATOR_STATE(ray, dP); - const float time = INTEGRATOR_STATE(ray, time); - const float3 Ng = INTEGRATOR_STATE(isect, Ng); - const int object = INTEGRATOR_STATE(isect, object); - - /* Sample diffuse surface scatter into the object. */ - float3 D; - float pdf; - sample_cos_hemisphere(-N, bssrdf_u, bssrdf_v, &D, &pdf); - if (dot(-Ng, D) <= 0.0f) { - return false; - } - - /* Setup ray. */ - ray.P = ray_offset(P, -Ng); - ray.D = D; - ray.t = FLT_MAX; - ray.time = time; - ray.dP = ray_dP; - ray.dD = differential_zero_compact(); - -# ifndef __KERNEL_OPTIX__ - /* Compute or fetch object transforms. */ - Transform ob_itfm ccl_optional_struct_init; - Transform ob_tfm = object_fetch_transform_motion_test(kg, object, time, &ob_itfm); -# endif - - /* Convert subsurface to volume coefficients. - * The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */ - const float3 albedo = INTEGRATOR_STATE(subsurface, albedo); - const float3 radius = INTEGRATOR_STATE(subsurface, radius); - const float anisotropy = INTEGRATOR_STATE(subsurface, anisotropy); - - float3 sigma_t, alpha; - float3 throughput = INTEGRATOR_STATE_WRITE(path, throughput); - subsurface_random_walk_coefficients(albedo, radius, anisotropy, &sigma_t, &alpha, &throughput); - float3 sigma_s = sigma_t * alpha; - - /* Theoretically it should be better to use the exact alpha for the channel we're sampling at - * each bounce, but in practice there doesn't seem to be a noticeable difference in exchange - * for making the code significantly more complex and slower (if direction sampling depends on - * the sampled channel, we need to compute its PDF per-channel and consider it for MIS later on). - * - * Since the strength of the guided sampling increases as alpha gets lower, using a value that - * is too low results in fireflies while one that's too high just gives a bit more noise. - * Therefore, the code here uses the highest of the three albedos to be safe. */ - const float diffusion_length = diffusion_length_dwivedi(max3(alpha)); - - if (diffusion_length == 1.0f) { - /* With specific values of alpha the length might become 1, which in asymptotic makes phase to - * be infinite. After first bounce it will cause throughput to be 0. Do early output, avoiding - * numerical issues and extra unneeded work. */ - return false; - } - - /* Precompute term for phase sampling. */ - const float phase_log = logf((diffusion_length + 1.0f) / (diffusion_length - 1.0f)); - - /* Modify state for RNGs, decorrelated from other paths. */ - rng_state.rng_hash = cmj_hash(rng_state.rng_hash + rng_state.rng_offset, 0xdeadbeef); - - /* Random walk until we hit the surface again. */ - bool hit = false; - bool have_opposite_interface = false; - float opposite_distance = 0.0f; - - /* Todo: Disable for alpha>0.999 or so? */ - /* Our heuristic, a compromise between guiding and classic. */ - const float guided_fraction = 1.0f - fmaxf(0.5f, powf(fabsf(anisotropy), 0.125f)); - -# ifdef SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL - float3 sigma_s_star = sigma_s * (1.0f - anisotropy); - float3 sigma_t_star = sigma_t - sigma_s + sigma_s_star; - float3 sigma_t_org = sigma_t; - float3 sigma_s_org = sigma_s; - const float anisotropy_org = anisotropy; - const float guided_fraction_org = guided_fraction; -# endif - - for (int bounce = 0; bounce < BSSRDF_MAX_BOUNCES; bounce++) { - /* Advance random number offset. */ - rng_state.rng_offset += PRNG_BOUNCE_NUM; - -# ifdef SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL - // shadow with local variables according to depth - float anisotropy, guided_fraction; - float3 sigma_s, sigma_t; - if (bounce <= SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL) { - anisotropy = anisotropy_org; - guided_fraction = guided_fraction_org; - sigma_t = sigma_t_org; - sigma_s = sigma_s_org; - } - else { - anisotropy = 0.0f; - guided_fraction = 0.75f; // back to isotropic heuristic from Blender - sigma_t = sigma_t_star; - sigma_s = sigma_s_star; - } -# endif - - /* Sample color channel, use MIS with balance heuristic. */ - float rphase = path_state_rng_1D(kg, &rng_state, PRNG_PHASE_CHANNEL); - float3 channel_pdf; - int channel = volume_sample_channel(alpha, throughput, rphase, &channel_pdf); - float sample_sigma_t = volume_channel_get(sigma_t, channel); - float randt = path_state_rng_1D(kg, &rng_state, PRNG_SCATTER_DISTANCE); - - /* We need the result of the raycast to compute the full guided PDF, so just remember the - * relevant terms to avoid recomputing them later. */ - float backward_fraction = 0.0f; - float forward_pdf_factor = 0.0f; - float forward_stretching = 1.0f; - float backward_pdf_factor = 0.0f; - float backward_stretching = 1.0f; - - /* For the initial ray, we already know the direction, so just do classic distance sampling. */ - if (bounce > 0) { - /* Decide whether we should use guided or classic sampling. */ - bool guided = (path_state_rng_1D(kg, &rng_state, PRNG_LIGHT_TERMINATE) < guided_fraction); - - /* Determine if we want to sample away from the incoming interface. - * This only happens if we found a nearby opposite interface, and the probability for it - * depends on how close we are to it already. - * This probability term comes from the recorded presentation of [3]. */ - bool guide_backward = false; - if (have_opposite_interface) { - /* Compute distance of the random walk between the tangent plane at the starting point - * and the assumed opposite interface (the parallel plane that contains the point we - * found in our ray query for the opposite side). */ - float x = clamp(dot(ray.P - P, -N), 0.0f, opposite_distance); - backward_fraction = 1.0f / - (1.0f + expf((opposite_distance - 2.0f * x) / diffusion_length)); - guide_backward = path_state_rng_1D(kg, &rng_state, PRNG_TERMINATE) < backward_fraction; - } - - /* Sample scattering direction. */ - float scatter_u, scatter_v; - path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &scatter_u, &scatter_v); - float cos_theta; - float hg_pdf; - if (guided) { - cos_theta = sample_phase_dwivedi(diffusion_length, phase_log, scatter_u); - /* The backwards guiding distribution is just mirrored along sd->N, so swapping the - * sign here is enough to sample from that instead. */ - if (guide_backward) { - cos_theta = -cos_theta; - } - float3 newD = direction_from_cosine(N, cos_theta, scatter_v); - hg_pdf = single_peaked_henyey_greenstein(dot(ray.D, newD), anisotropy); - ray.D = newD; - } - else { - float3 newD = henyey_greenstrein_sample(ray.D, anisotropy, scatter_u, scatter_v, &hg_pdf); - cos_theta = dot(newD, N); - ray.D = newD; - } - - /* Compute PDF factor caused by phase sampling (as the ratio of guided / classic). - * Since phase sampling is channel-independent, we can get away with applying a factor - * to the guided PDF, which implicitly means pulling out the classic PDF term and letting - * it cancel with an equivalent term in the numerator of the full estimator. - * For the backward PDF, we again reuse the same probability distribution with a sign swap. - */ - forward_pdf_factor = M_1_2PI_F * eval_phase_dwivedi(diffusion_length, phase_log, cos_theta) / - hg_pdf; - backward_pdf_factor = M_1_2PI_F * - eval_phase_dwivedi(diffusion_length, phase_log, -cos_theta) / hg_pdf; - - /* Prepare distance sampling. - * For the backwards case, this also needs the sign swapped since now directions against - * sd->N (and therefore with negative cos_theta) are preferred. */ - forward_stretching = (1.0f - cos_theta / diffusion_length); - backward_stretching = (1.0f + cos_theta / diffusion_length); - if (guided) { - sample_sigma_t *= guide_backward ? backward_stretching : forward_stretching; - } - } - - /* Sample direction along ray. */ - float t = -logf(1.0f - randt) / sample_sigma_t; - - /* On the first bounce, we use the raycast to check if the opposite side is nearby. - * If yes, we will later use backwards guided sampling in order to have a decent - * chance of connecting to it. - * Todo: Maybe use less than 10 times the mean free path? */ - ray.t = (bounce == 0) ? max(t, 10.0f / (min3(sigma_t))) : t; - scene_intersect_local(kg, &ray, &ss_isect, object, NULL, 1); - hit = (ss_isect.num_hits > 0); - - if (hit) { -# ifdef __KERNEL_OPTIX__ - /* t is always in world space with OptiX. */ - ray.t = ss_isect.hits[0].t; -# else - /* Compute world space distance to surface hit. */ - float3 D = transform_direction(&ob_itfm, ray.D); - D = normalize(D) * ss_isect.hits[0].t; - ray.t = len(transform_direction(&ob_tfm, D)); -# endif - } - - if (bounce == 0) { - /* Check if we hit the opposite side. */ - if (hit) { - have_opposite_interface = true; - opposite_distance = dot(ray.P + ray.t * ray.D - P, -N); - } - /* Apart from the opposite side check, we were supposed to only trace up to distance t, - * so check if there would have been a hit in that case. */ - hit = ray.t < t; - } - - /* Use the distance to the exit point for the throughput update if we found one. */ - if (hit) { - t = ray.t; - } - else if (bounce == 0) { - /* Restore original position if nothing was hit after the first bounce, - * without the ray_offset() that was added to avoid self-intersection. - * Otherwise if that offset is relatively large compared to the scattering - * radius, we never go back up high enough to exit the surface. */ - ray.P = P; - } - - /* Advance to new scatter location. */ - ray.P += t * ray.D; - - float3 transmittance; - float3 pdf = subsurface_random_walk_pdf(sigma_t, t, hit, &transmittance); - if (bounce > 0) { - /* Compute PDF just like we do for classic sampling, but with the stretched sigma_t. */ - float3 guided_pdf = subsurface_random_walk_pdf(forward_stretching * sigma_t, t, hit, NULL); - - if (have_opposite_interface) { - /* First step of MIS: Depending on geometry we might have two methods for guided - * sampling, so perform MIS between them. */ - float3 back_pdf = subsurface_random_walk_pdf(backward_stretching * sigma_t, t, hit, NULL); - guided_pdf = mix( - guided_pdf * forward_pdf_factor, back_pdf * backward_pdf_factor, backward_fraction); - } - else { - /* Just include phase sampling factor otherwise. */ - guided_pdf *= forward_pdf_factor; - } - - /* Now we apply the MIS balance heuristic between the classic and guided sampling. */ - pdf = mix(pdf, guided_pdf, guided_fraction); - } - - /* Finally, we're applying MIS again to combine the three color channels. - * Altogether, the MIS computation combines up to nine different estimators: - * {classic, guided, backward_guided} x {r, g, b} */ - throughput *= (hit ? transmittance : sigma_s * transmittance) / dot(channel_pdf, pdf); - - if (hit) { - /* If we hit the surface, we are done. */ - break; - } - else if (throughput.x < VOLUME_THROUGHPUT_EPSILON && - throughput.y < VOLUME_THROUGHPUT_EPSILON && - throughput.z < VOLUME_THROUGHPUT_EPSILON) { - /* Avoid unnecessary work and precision issue when throughput gets really small. */ - break; - } - } - - if (hit) { - kernel_assert(isfinite3_safe(throughput)); - INTEGRATOR_STATE_WRITE(path, throughput) = throughput; - } - - return hit; -} - ccl_device_inline bool subsurface_scatter(INTEGRATOR_STATE_ARGS) { RNGState rng_state; @@ -573,8 +136,15 @@ ccl_device_inline bool subsurface_scatter(INTEGRATOR_STATE_ARGS) Ray ray ccl_optional_struct_init; LocalIntersection ss_isect ccl_optional_struct_init; - if (!subsurface_random_walk(INTEGRATOR_STATE_PASS, rng_state, ray, ss_isect)) { - return false; + if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SUBSURFACE_RANDOM_WALK) { + if (!subsurface_random_walk(INTEGRATOR_STATE_PASS, rng_state, ray, ss_isect)) { + return false; + } + } + else { + if (!subsurface_disk(INTEGRATOR_STATE_PASS, rng_state, ray, ss_isect)) { + return false; + } } # ifdef __VOLUME__ diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_disk.h b/intern/cycles/kernel/integrator/integrator_subsurface_disk.h new file mode 100644 index 00000000000..3f685e3a2e9 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_subsurface_disk.h @@ -0,0 +1,195 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CCL_NAMESPACE_BEGIN + +/* BSSRDF using disk based importance sampling. + * + * BSSRDF Importance Sampling, SIGGRAPH 2013 + * http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf + */ + +ccl_device_inline float3 subsurface_disk_eval(const float3 radius, float disk_r, float r) +{ + const float3 eval = bssrdf_eval(radius, r); + const float pdf = bssrdf_pdf(radius, disk_r); + return (pdf > 0.0f) ? eval / pdf : zero_float3(); +} + +/* Subsurface scattering step, from a point on the surface to other + * nearby points on the same object. */ +ccl_device_inline bool subsurface_disk(INTEGRATOR_STATE_ARGS, + RNGState rng_state, + Ray &ray, + LocalIntersection &ss_isect) + +{ + float disk_u, disk_v; + path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &disk_u, &disk_v); + + /* Read shading point info from integrator state. */ + const float3 P = INTEGRATOR_STATE(ray, P); + const float ray_dP = INTEGRATOR_STATE(ray, dP); + const float time = INTEGRATOR_STATE(ray, time); + const float3 Ng = INTEGRATOR_STATE(isect, Ng); + const int object = INTEGRATOR_STATE(isect, object); + + /* Read subsurface scattering parameters. */ + const float3 radius = INTEGRATOR_STATE(subsurface, radius); + + /* Pick random axis in local frame and point on disk. */ + float3 disk_N, disk_T, disk_B; + float pick_pdf_N, pick_pdf_T, pick_pdf_B; + + disk_N = Ng; + make_orthonormals(disk_N, &disk_T, &disk_B); + + if (disk_v < 0.5f) { + pick_pdf_N = 0.5f; + pick_pdf_T = 0.25f; + pick_pdf_B = 0.25f; + disk_v *= 2.0f; + } + else if (disk_v < 0.75f) { + float3 tmp = disk_N; + disk_N = disk_T; + disk_T = tmp; + pick_pdf_N = 0.25f; + pick_pdf_T = 0.5f; + pick_pdf_B = 0.25f; + disk_v = (disk_v - 0.5f) * 4.0f; + } + else { + float3 tmp = disk_N; + disk_N = disk_B; + disk_B = tmp; + pick_pdf_N = 0.25f; + pick_pdf_T = 0.25f; + pick_pdf_B = 0.5f; + disk_v = (disk_v - 0.75f) * 4.0f; + } + + /* Sample point on disk. */ + float phi = M_2PI_F * disk_v; + float disk_height, disk_r; + + bssrdf_sample(radius, disk_u, &disk_r, &disk_height); + + float3 disk_P = (disk_r * cosf(phi)) * disk_T + (disk_r * sinf(phi)) * disk_B; + + /* Create ray. */ + ray.P = P + disk_N * disk_height + disk_P; + ray.D = -disk_N; + ray.t = 2.0f * disk_height; + ray.dP = ray_dP; + ray.dD = differential_zero_compact(); + ray.time = time; + + /* Intersect with the same object. if multiple intersections are found it + * will use at most BSSRDF_MAX_HITS hits, a random subset of all hits. */ + uint lcg_state = lcg_state_init( + rng_state.rng_hash, rng_state.rng_offset, rng_state.sample, 0x68bc21eb); + const int max_hits = BSSRDF_MAX_HITS; + + scene_intersect_local(kg, &ray, &ss_isect, object, &lcg_state, max_hits); + const int num_eval_hits = min(ss_isect.num_hits, max_hits); + if (num_eval_hits == 0) { + return false; + } + + /* Sort for consistent renders between CPU and GPU, independent of the BVH + * traversal algorithm. */ + sort_intersections_and_normals(ss_isect.hits, ss_isect.Ng, num_eval_hits); + + float3 weights[BSSRDF_MAX_HITS]; /* TODO: zero? */ + float sum_weights = 0.0f; + + for (int hit = 0; hit < num_eval_hits; hit++) { + /* Quickly retrieve P and Ng without setting up ShaderData. */ + const float3 hit_P = ray.P + ray.D * ss_isect.hits[hit].t; + + /* Get geometric normal. */ + const int object = ss_isect.hits[hit].object; + const int object_flag = kernel_tex_fetch(__object_flag, object); + float3 hit_Ng = ss_isect.Ng[hit]; + if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { + hit_Ng = -hit_Ng; + } + + if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + Transform itfm; + object_fetch_transform_motion_test(kg, object, time, &itfm); + hit_Ng = normalize(transform_direction_transposed(&itfm, hit_Ng)); + } + + /* Probability densities for local frame axes. */ + const float pdf_N = pick_pdf_N * fabsf(dot(disk_N, hit_Ng)); + const float pdf_T = pick_pdf_T * fabsf(dot(disk_T, hit_Ng)); + const float pdf_B = pick_pdf_B * fabsf(dot(disk_B, hit_Ng)); + + /* Multiple importance sample between 3 axes, power heuristic + * found to be slightly better than balance heuristic. pdf_N + * in the MIS weight and denominator cancelled out. */ + float w = pdf_N / (sqr(pdf_N) + sqr(pdf_T) + sqr(pdf_B)); + if (ss_isect.num_hits > max_hits) { + w *= ss_isect.num_hits / (float)max_hits; + } + + /* Real distance to sampled point. */ + const float r = len(hit_P - P); + + /* Evaluate profiles. */ + const float3 weight = subsurface_disk_eval(radius, disk_r, r) * w; + + /* Store result. */ + ss_isect.Ng[hit] = hit_Ng; + weights[hit] = weight; + sum_weights += average(fabs(weight)); + } + + if (sum_weights == 0.0f) { + return false; + } + + /* Use importance resampling, sampling one of the hits proportional to weight. */ + const float r = lcg_step_float(&lcg_state) * sum_weights; + float partial_sum = 0.0f; + + for (int hit = 0; hit < num_eval_hits; hit++) { + const float3 weight = weights[hit]; + const float sample_weight = average(fabs(weight)); + float next_sum = partial_sum + sample_weight; + + if (r < next_sum) { + /* Return exit point. */ + INTEGRATOR_STATE_WRITE(path, throughput) *= weight * sum_weights / sample_weight; + + ss_isect.hits[0] = ss_isect.hits[hit]; + ss_isect.Ng[0] = ss_isect.Ng[hit]; + + ray.P = ray.P + ray.D * ss_isect.hits[hit].t; + ray.D = ss_isect.Ng[hit]; + ray.t = 1.0f; + return true; + } + + partial_sum = next_sum; + } + + return false; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h new file mode 100644 index 00000000000..cffc53bf270 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h @@ -0,0 +1,465 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kernel/kernel_projection.h" + +#include "kernel/bvh/bvh.h" + +CCL_NAMESPACE_BEGIN + +/* Random walk subsurface scattering. + * + * "Practical and Controllable Subsurface Scattering for Production Path + * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ + +/* Support for anisotropy from: + * "Path Traced Subsurface Scattering using Anisotropic Phase Functions + * and Non-Exponential Free Flights". + * Magnus Wrenninge, Ryusuke Villemin, Christophe Hery. + * https://graphics.pixar.com/library/PathTracedSubsurface/ */ + +ccl_device void subsurface_random_walk_remap( + const float albedo, const float d, float g, float *sigma_t, float *alpha) +{ + /* Compute attenuation and scattering coefficients from albedo. */ + const float g2 = g * g; + const float g3 = g2 * g; + const float g4 = g3 * g; + const float g5 = g4 * g; + const float g6 = g5 * g; + const float g7 = g6 * g; + + const float A = 1.8260523782f + -1.28451056436f * g + -1.79904629312f * g2 + + 9.19393289202f * g3 + -22.8215585862f * g4 + 32.0234874259f * g5 + + -23.6264803333f * g6 + 7.21067002658f * g7; + const float B = 4.98511194385f + + 0.127355959438f * + expf(31.1491581433f * g + -201.847017512f * g2 + 841.576016723f * g3 + + -2018.09288505f * g4 + 2731.71560286f * g5 + -1935.41424244f * g6 + + 559.009054474f * g7); + const float C = 1.09686102424f + -0.394704063468f * g + 1.05258115941f * g2 + + -8.83963712726f * g3 + 28.8643230661f * g4 + -46.8802913581f * g5 + + 38.5402837518f * g6 + -12.7181042538f * g7; + const float D = 0.496310210422f + 0.360146581622f * g + -2.15139309747f * g2 + + 17.8896899217f * g3 + -55.2984010333f * g4 + 82.065982243f * g5 + + -58.5106008578f * g6 + 15.8478295021f * g7; + const float E = 4.23190299701f + + 0.00310603949088f * + expf(76.7316253952f * g + -594.356773233f * g2 + 2448.8834203f * g3 + + -5576.68528998f * g4 + 7116.60171912f * g5 + -4763.54467887f * g6 + + 1303.5318055f * g7); + const float F = 2.40602999408f + -2.51814844609f * g + 9.18494908356f * g2 + + -79.2191708682f * g3 + 259.082868209f * g4 + -403.613804597f * g5 + + 302.85712436f * g6 + -87.4370473567f * g7; + + const float blend = powf(albedo, 0.25f); + + *alpha = (1.0f - blend) * A * powf(atanf(B * albedo), C) + + blend * D * powf(atanf(E * albedo), F); + *alpha = clamp(*alpha, 0.0f, 0.999999f); // because of numerical precision + + float sigma_t_prime = 1.0f / fmaxf(d, 1e-16f); + *sigma_t = sigma_t_prime / (1.0f - g); +} + +ccl_device void subsurface_random_walk_coefficients(const float3 albedo, + const float3 radius, + const float anisotropy, + float3 *sigma_t, + float3 *alpha, + float3 *throughput) +{ + float sigma_t_x, sigma_t_y, sigma_t_z; + float alpha_x, alpha_y, alpha_z; + + subsurface_random_walk_remap(albedo.x, radius.x, anisotropy, &sigma_t_x, &alpha_x); + subsurface_random_walk_remap(albedo.y, radius.y, anisotropy, &sigma_t_y, &alpha_y); + subsurface_random_walk_remap(albedo.z, radius.z, anisotropy, &sigma_t_z, &alpha_z); + + /* Throughput already contains closure weight at this point, which includes the + * albedo, as well as closure mixing and Fresnel weights. Divide out the albedo + * which will be added through scattering. */ + *throughput = safe_divide_color(*throughput, albedo); + + /* With low albedo values (like 0.025) we get diffusion_length 1.0 and + * infinite phase functions. To avoid a sharp discontinuity as we go from + * such values to 0.0, increase alpha and reduce the throughput to compensate. */ + const float min_alpha = 0.2f; + if (alpha_x < min_alpha) { + (*throughput).x *= alpha_x / min_alpha; + alpha_x = min_alpha; + } + if (alpha_y < min_alpha) { + (*throughput).y *= alpha_y / min_alpha; + alpha_y = min_alpha; + } + if (alpha_z < min_alpha) { + (*throughput).z *= alpha_z / min_alpha; + alpha_z = min_alpha; + } + + *sigma_t = make_float3(sigma_t_x, sigma_t_y, sigma_t_z); + *alpha = make_float3(alpha_x, alpha_y, alpha_z); +} + +/* References for Dwivedi sampling: + * + * [1] "A Zero-variance-based Sampling Scheme for Monte Carlo Subsurface Scattering" + * by Jaroslav Křivánek and Eugene d'Eon (SIGGRAPH 2014) + * https://cgg.mff.cuni.cz/~jaroslav/papers/2014-zerovar/ + * + * [2] "Improving the Dwivedi Sampling Scheme" + * by Johannes Meng, Johannes Hanika, and Carsten Dachsbacher (EGSR 2016) + * https://cg.ivd.kit.edu/1951.php + * + * [3] "Zero-Variance Theory for Efficient Subsurface Scattering" + * by Eugene d'Eon and Jaroslav Křivánek (SIGGRAPH 2020) + * https://iliyan.com/publications/RenderingCourse2020 + */ + +ccl_device_forceinline float eval_phase_dwivedi(float v, float phase_log, float cos_theta) +{ + /* Eq. 9 from [2] using precomputed log((v + 1) / (v - 1)) */ + return 1.0f / ((v - cos_theta) * phase_log); +} + +ccl_device_forceinline float sample_phase_dwivedi(float v, float phase_log, float rand) +{ + /* Based on Eq. 10 from [2]: `v - (v + 1) * pow((v - 1) / (v + 1), rand)` + * Since we're already pre-computing `phase_log = log((v + 1) / (v - 1))` for the evaluation, + * we can implement the power function like this. */ + return v - (v + 1.0f) * expf(-rand * phase_log); +} + +ccl_device_forceinline float diffusion_length_dwivedi(float alpha) +{ + /* Eq. 67 from [3] */ + return 1.0f / sqrtf(1.0f - powf(alpha, 2.44294f - 0.0215813f * alpha + 0.578637f / alpha)); +} + +ccl_device_forceinline float3 direction_from_cosine(float3 D, float cos_theta, float randv) +{ + float sin_theta = safe_sqrtf(1.0f - cos_theta * cos_theta); + float phi = M_2PI_F * randv; + float3 dir = make_float3(sin_theta * cosf(phi), sin_theta * sinf(phi), cos_theta); + + float3 T, B; + make_orthonormals(D, &T, &B); + return dir.x * T + dir.y * B + dir.z * D; +} + +ccl_device_forceinline float3 subsurface_random_walk_pdf(float3 sigma_t, + float t, + bool hit, + float3 *transmittance) +{ + float3 T = volume_color_transmittance(sigma_t, t); + if (transmittance) { + *transmittance = T; + } + return hit ? T : sigma_t * T; +} + +/* Define the below variable to get the similarity code active, + * and the value represents the cutoff level */ +#define SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL 9 + +ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, + RNGState rng_state, + Ray &ray, + LocalIntersection &ss_isect) +{ + float bssrdf_u, bssrdf_v; + path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); + + const float3 P = INTEGRATOR_STATE(ray, P); + const float3 N = INTEGRATOR_STATE(ray, D); + const float ray_dP = INTEGRATOR_STATE(ray, dP); + const float time = INTEGRATOR_STATE(ray, time); + const float3 Ng = INTEGRATOR_STATE(isect, Ng); + const int object = INTEGRATOR_STATE(isect, object); + + /* Sample diffuse surface scatter into the object. */ + float3 D; + float pdf; + sample_cos_hemisphere(-N, bssrdf_u, bssrdf_v, &D, &pdf); + if (dot(-Ng, D) <= 0.0f) { + return false; + } + + /* Setup ray. */ + ray.P = ray_offset(P, -Ng); + ray.D = D; + ray.t = FLT_MAX; + ray.time = time; + ray.dP = ray_dP; + ray.dD = differential_zero_compact(); + +#ifndef __KERNEL_OPTIX__ + /* Compute or fetch object transforms. */ + Transform ob_itfm ccl_optional_struct_init; + Transform ob_tfm = object_fetch_transform_motion_test(kg, object, time, &ob_itfm); +#endif + + /* Convert subsurface to volume coefficients. + * The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */ + const float3 albedo = INTEGRATOR_STATE(subsurface, albedo); + const float3 radius = INTEGRATOR_STATE(subsurface, radius); + const float anisotropy = INTEGRATOR_STATE(subsurface, anisotropy); + + float3 sigma_t, alpha; + float3 throughput = INTEGRATOR_STATE_WRITE(path, throughput); + subsurface_random_walk_coefficients(albedo, radius, anisotropy, &sigma_t, &alpha, &throughput); + float3 sigma_s = sigma_t * alpha; + + /* Theoretically it should be better to use the exact alpha for the channel we're sampling at + * each bounce, but in practice there doesn't seem to be a noticeable difference in exchange + * for making the code significantly more complex and slower (if direction sampling depends on + * the sampled channel, we need to compute its PDF per-channel and consider it for MIS later on). + * + * Since the strength of the guided sampling increases as alpha gets lower, using a value that + * is too low results in fireflies while one that's too high just gives a bit more noise. + * Therefore, the code here uses the highest of the three albedos to be safe. */ + const float diffusion_length = diffusion_length_dwivedi(max3(alpha)); + + if (diffusion_length == 1.0f) { + /* With specific values of alpha the length might become 1, which in asymptotic makes phase to + * be infinite. After first bounce it will cause throughput to be 0. Do early output, avoiding + * numerical issues and extra unneeded work. */ + return false; + } + + /* Precompute term for phase sampling. */ + const float phase_log = logf((diffusion_length + 1.0f) / (diffusion_length - 1.0f)); + + /* Modify state for RNGs, decorrelated from other paths. */ + rng_state.rng_hash = cmj_hash(rng_state.rng_hash + rng_state.rng_offset, 0xdeadbeef); + + /* Random walk until we hit the surface again. */ + bool hit = false; + bool have_opposite_interface = false; + float opposite_distance = 0.0f; + + /* Todo: Disable for alpha>0.999 or so? */ + /* Our heuristic, a compromise between guiding and classic. */ + const float guided_fraction = 1.0f - fmaxf(0.5f, powf(fabsf(anisotropy), 0.125f)); + +#ifdef SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL + float3 sigma_s_star = sigma_s * (1.0f - anisotropy); + float3 sigma_t_star = sigma_t - sigma_s + sigma_s_star; + float3 sigma_t_org = sigma_t; + float3 sigma_s_org = sigma_s; + const float anisotropy_org = anisotropy; + const float guided_fraction_org = guided_fraction; +#endif + + for (int bounce = 0; bounce < BSSRDF_MAX_BOUNCES; bounce++) { + /* Advance random number offset. */ + rng_state.rng_offset += PRNG_BOUNCE_NUM; + +#ifdef SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL + // shadow with local variables according to depth + float anisotropy, guided_fraction; + float3 sigma_s, sigma_t; + if (bounce <= SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL) { + anisotropy = anisotropy_org; + guided_fraction = guided_fraction_org; + sigma_t = sigma_t_org; + sigma_s = sigma_s_org; + } + else { + anisotropy = 0.0f; + guided_fraction = 0.75f; // back to isotropic heuristic from Blender + sigma_t = sigma_t_star; + sigma_s = sigma_s_star; + } +#endif + + /* Sample color channel, use MIS with balance heuristic. */ + float rphase = path_state_rng_1D(kg, &rng_state, PRNG_PHASE_CHANNEL); + float3 channel_pdf; + int channel = volume_sample_channel(alpha, throughput, rphase, &channel_pdf); + float sample_sigma_t = volume_channel_get(sigma_t, channel); + float randt = path_state_rng_1D(kg, &rng_state, PRNG_SCATTER_DISTANCE); + + /* We need the result of the raycast to compute the full guided PDF, so just remember the + * relevant terms to avoid recomputing them later. */ + float backward_fraction = 0.0f; + float forward_pdf_factor = 0.0f; + float forward_stretching = 1.0f; + float backward_pdf_factor = 0.0f; + float backward_stretching = 1.0f; + + /* For the initial ray, we already know the direction, so just do classic distance sampling. */ + if (bounce > 0) { + /* Decide whether we should use guided or classic sampling. */ + bool guided = (path_state_rng_1D(kg, &rng_state, PRNG_LIGHT_TERMINATE) < guided_fraction); + + /* Determine if we want to sample away from the incoming interface. + * This only happens if we found a nearby opposite interface, and the probability for it + * depends on how close we are to it already. + * This probability term comes from the recorded presentation of [3]. */ + bool guide_backward = false; + if (have_opposite_interface) { + /* Compute distance of the random walk between the tangent plane at the starting point + * and the assumed opposite interface (the parallel plane that contains the point we + * found in our ray query for the opposite side). */ + float x = clamp(dot(ray.P - P, -N), 0.0f, opposite_distance); + backward_fraction = 1.0f / + (1.0f + expf((opposite_distance - 2.0f * x) / diffusion_length)); + guide_backward = path_state_rng_1D(kg, &rng_state, PRNG_TERMINATE) < backward_fraction; + } + + /* Sample scattering direction. */ + float scatter_u, scatter_v; + path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &scatter_u, &scatter_v); + float cos_theta; + float hg_pdf; + if (guided) { + cos_theta = sample_phase_dwivedi(diffusion_length, phase_log, scatter_u); + /* The backwards guiding distribution is just mirrored along sd->N, so swapping the + * sign here is enough to sample from that instead. */ + if (guide_backward) { + cos_theta = -cos_theta; + } + float3 newD = direction_from_cosine(N, cos_theta, scatter_v); + hg_pdf = single_peaked_henyey_greenstein(dot(ray.D, newD), anisotropy); + ray.D = newD; + } + else { + float3 newD = henyey_greenstrein_sample(ray.D, anisotropy, scatter_u, scatter_v, &hg_pdf); + cos_theta = dot(newD, N); + ray.D = newD; + } + + /* Compute PDF factor caused by phase sampling (as the ratio of guided / classic). + * Since phase sampling is channel-independent, we can get away with applying a factor + * to the guided PDF, which implicitly means pulling out the classic PDF term and letting + * it cancel with an equivalent term in the numerator of the full estimator. + * For the backward PDF, we again reuse the same probability distribution with a sign swap. + */ + forward_pdf_factor = M_1_2PI_F * eval_phase_dwivedi(diffusion_length, phase_log, cos_theta) / + hg_pdf; + backward_pdf_factor = M_1_2PI_F * + eval_phase_dwivedi(diffusion_length, phase_log, -cos_theta) / hg_pdf; + + /* Prepare distance sampling. + * For the backwards case, this also needs the sign swapped since now directions against + * sd->N (and therefore with negative cos_theta) are preferred. */ + forward_stretching = (1.0f - cos_theta / diffusion_length); + backward_stretching = (1.0f + cos_theta / diffusion_length); + if (guided) { + sample_sigma_t *= guide_backward ? backward_stretching : forward_stretching; + } + } + + /* Sample direction along ray. */ + float t = -logf(1.0f - randt) / sample_sigma_t; + + /* On the first bounce, we use the raycast to check if the opposite side is nearby. + * If yes, we will later use backwards guided sampling in order to have a decent + * chance of connecting to it. + * Todo: Maybe use less than 10 times the mean free path? */ + ray.t = (bounce == 0) ? max(t, 10.0f / (min3(sigma_t))) : t; + scene_intersect_local(kg, &ray, &ss_isect, object, NULL, 1); + hit = (ss_isect.num_hits > 0); + + if (hit) { +#ifdef __KERNEL_OPTIX__ + /* t is always in world space with OptiX. */ + ray.t = ss_isect.hits[0].t; +#else + /* Compute world space distance to surface hit. */ + float3 D = transform_direction(&ob_itfm, ray.D); + D = normalize(D) * ss_isect.hits[0].t; + ray.t = len(transform_direction(&ob_tfm, D)); +#endif + } + + if (bounce == 0) { + /* Check if we hit the opposite side. */ + if (hit) { + have_opposite_interface = true; + opposite_distance = dot(ray.P + ray.t * ray.D - P, -N); + } + /* Apart from the opposite side check, we were supposed to only trace up to distance t, + * so check if there would have been a hit in that case. */ + hit = ray.t < t; + } + + /* Use the distance to the exit point for the throughput update if we found one. */ + if (hit) { + t = ray.t; + } + else if (bounce == 0) { + /* Restore original position if nothing was hit after the first bounce, + * without the ray_offset() that was added to avoid self-intersection. + * Otherwise if that offset is relatively large compared to the scattering + * radius, we never go back up high enough to exit the surface. */ + ray.P = P; + } + + /* Advance to new scatter location. */ + ray.P += t * ray.D; + + float3 transmittance; + float3 pdf = subsurface_random_walk_pdf(sigma_t, t, hit, &transmittance); + if (bounce > 0) { + /* Compute PDF just like we do for classic sampling, but with the stretched sigma_t. */ + float3 guided_pdf = subsurface_random_walk_pdf(forward_stretching * sigma_t, t, hit, NULL); + + if (have_opposite_interface) { + /* First step of MIS: Depending on geometry we might have two methods for guided + * sampling, so perform MIS between them. */ + float3 back_pdf = subsurface_random_walk_pdf(backward_stretching * sigma_t, t, hit, NULL); + guided_pdf = mix( + guided_pdf * forward_pdf_factor, back_pdf * backward_pdf_factor, backward_fraction); + } + else { + /* Just include phase sampling factor otherwise. */ + guided_pdf *= forward_pdf_factor; + } + + /* Now we apply the MIS balance heuristic between the classic and guided sampling. */ + pdf = mix(pdf, guided_pdf, guided_fraction); + } + + /* Finally, we're applying MIS again to combine the three color channels. + * Altogether, the MIS computation combines up to nine different estimators: + * {classic, guided, backward_guided} x {r, g, b} */ + throughput *= (hit ? transmittance : sigma_s * transmittance) / dot(channel_pdf, pdf); + + if (hit) { + /* If we hit the surface, we are done. */ + break; + } + else if (throughput.x < VOLUME_THROUGHPUT_EPSILON && + throughput.y < VOLUME_THROUGHPUT_EPSILON && + throughput.z < VOLUME_THROUGHPUT_EPSILON) { + /* Avoid unnecessary work and precision issue when throughput gets really small. */ + break; + } + } + + if (hit) { + kernel_assert(isfinite3_safe(throughput)); + INTEGRATOR_STATE_WRITE(path, throughput) = throughput; + } + + return hit; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 5000a96c331..eb681683d6a 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -261,32 +261,34 @@ enum PathRayFlag { PATH_RAY_EMISSION = (1 << 19), /* Perform subsurface scattering. */ - PATH_RAY_SUBSURFACE = (1 << 20), + PATH_RAY_SUBSURFACE_RANDOM_WALK = (1 << 20), + PATH_RAY_SUBSURFACE_DISK = (1 << 21), + PATH_RAY_SUBSURFACE = (PATH_RAY_SUBSURFACE_RANDOM_WALK | PATH_RAY_SUBSURFACE_DISK), /* Contribute to denoising features. */ - PATH_RAY_DENOISING_FEATURES = (1 << 21), + PATH_RAY_DENOISING_FEATURES = (1 << 22), /* Render pass categories. */ - PATH_RAY_REFLECT_PASS = (1 << 22), - PATH_RAY_TRANSMISSION_PASS = (1 << 23), - PATH_RAY_VOLUME_PASS = (1 << 24), + PATH_RAY_REFLECT_PASS = (1 << 23), + PATH_RAY_TRANSMISSION_PASS = (1 << 24), + PATH_RAY_VOLUME_PASS = (1 << 25), PATH_RAY_ANY_PASS = (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS | PATH_RAY_VOLUME_PASS), /* Shadow ray is for a light or surface. */ - PATH_RAY_SHADOW_FOR_LIGHT = (1 << 25), + PATH_RAY_SHADOW_FOR_LIGHT = (1 << 26), /* A shadow catcher object was hit and the path was split into two. */ - PATH_RAY_SHADOW_CATCHER_HIT = (1 << 26), + PATH_RAY_SHADOW_CATCHER_HIT = (1 << 27), /* A shadow catcher object was hit and this path traces only shadow catchers, writing them into * their dedicated pass for later division. * * NOTE: Is not covered with `PATH_RAY_ANY_PASS` because shadow catcher does special handling * which is separate from the light passes. */ - PATH_RAY_SHADOW_CATCHER_PASS = (1 << 27), + PATH_RAY_SHADOW_CATCHER_PASS = (1 << 28), /* Path is evaluating background for an approximate shadow catcher with non-transparent film. */ - PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1 << 28), + PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1 << 29), }; /* Configure ray visibility bits for rays and objects respectively, diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/osl_bssrdf.cpp index 5d968ed85e0..b6b0d72103a 100644 --- a/intern/cycles/kernel/osl/osl_bssrdf.cpp +++ b/intern/cycles/kernel/osl/osl_bssrdf.cpp @@ -50,6 +50,7 @@ CCL_NAMESPACE_BEGIN using namespace OSL; +static ustring u_burley("burley"); static ustring u_random_walk_fixed_radius("random_walk_fixed_radius"); static ustring u_random_walk("random_walk"); @@ -68,7 +69,10 @@ class CBSSRDFClosure : public CClosurePrimitive { void setup(ShaderData *sd, int path_flag, float3 weight) { - if (method == u_random_walk_fixed_radius) { + if (method == u_burley) { + alloc(sd, path_flag, weight, CLOSURE_BSSRDF_BURLEY_ID); + } + else if (method == u_random_walk_fixed_radius) { alloc(sd, path_flag, weight, CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); } else if (method == u_random_walk) { diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h index 3e0cbe3a483..b3f7fee8a63 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/svm_closure.h @@ -885,6 +885,7 @@ ccl_device_noinline int svm_node_closure_bsdf( #endif /* __HAIR__ */ #ifdef __SUBSURFACE__ + case CLOSURE_BSSRDF_BURLEY_ID: case CLOSURE_BSSRDF_RANDOM_WALK_ID: case CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID: { float3 weight = sd->svm_closure_weight * mix_weight; diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 59a0e33acbc..e846e4af259 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -543,6 +543,7 @@ typedef enum ClosureType { CLOSURE_BSDF_TRANSPARENT_ID, /* BSSRDF */ + CLOSURE_BSSRDF_BURLEY_ID, CLOSURE_BSSRDF_RANDOM_WALK_ID, CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID, @@ -589,7 +590,7 @@ typedef enum ClosureType { type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) #define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) #define CLOSURE_IS_BSSRDF(type) \ - (type >= CLOSURE_BSSRDF_RANDOM_WALK_ID && type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) + (type >= CLOSURE_BSSRDF_BURLEY_ID && type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) #define CLOSURE_IS_VOLUME(type) \ (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) #define CLOSURE_IS_VOLUME_SCATTER(type) (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 1629895ff6e..d775859d24d 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -2736,6 +2736,7 @@ NODE_DEFINE(PrincipledBsdfNode) distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); static NodeEnum subsurface_method_enum; + subsurface_method_enum.insert("burley", CLOSURE_BSSRDF_BURLEY_ID); subsurface_method_enum.insert("random_walk_fixed_radius", CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); subsurface_method_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID); @@ -3060,6 +3061,7 @@ NODE_DEFINE(SubsurfaceScatteringNode) SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum method_enum; + method_enum.insert("burley", CLOSURE_BSSRDF_BURLEY_ID); method_enum.insert("random_walk_fixed_radius", CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); method_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID); SOCKET_ENUM(method, "Method", method_enum, CLOSURE_BSSRDF_RANDOM_WALK_ID); diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index d8f4d01a2e9..e6247750759 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3718,7 +3718,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) STRNCPY(node->idname, "ShaderNodeOutputLight"); } if (node->type == SH_NODE_BSDF_PRINCIPLED && node->custom2 == 0) { - node->custom2 = SHD_SUBSURFACE_DIFFUSION; + node->custom2 = SHD_SUBSURFACE_BURLEY; } } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index e1100fa40c4..617cd8b6c58 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -948,12 +948,12 @@ static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data)) static void do_version_subsurface_methods(bNode *node) { if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { - if (node->custom1 != SHD_SUBSURFACE_RANDOM_WALK) { + if (!ELEM(node->custom1, SHD_SUBSURFACE_BURLEY, SHD_SUBSURFACE_RANDOM_WALK)) { node->custom1 = SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS; } } else if (node->type == SH_NODE_BSDF_PRINCIPLED) { - if (node->custom2 != SHD_SUBSURFACE_RANDOM_WALK) { + if (!ELEM(node->custom2, SHD_SUBSURFACE_BURLEY, SHD_SUBSURFACE_RANDOM_WALK)) { node->custom2 = SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS; } } diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index da57f27af4e..3df6af86618 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -182,8 +182,8 @@ static void displacement_principled_nodes(bNode *node) } } else if (node->type == SH_NODE_BSDF_PRINCIPLED) { - if (node->custom2 != SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS) { - node->custom2 = SHD_SUBSURFACE_DIFFUSION; + if (node->custom2 != SHD_SUBSURFACE_RANDOM_WALK) { + node->custom2 = SHD_SUBSURFACE_BURLEY; } } } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 05adcc3b922..74465c4bbe0 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1877,7 +1877,7 @@ enum { SHD_SUBSURFACE_CUBIC = 1, SHD_SUBSURFACE_GAUSSIAN = 2, #endif - SHD_SUBSURFACE_DIFFUSION = 3, + SHD_SUBSURFACE_BURLEY = 3, SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS = 4, SHD_SUBSURFACE_RANDOM_WALK = 5, }; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 47b2c7ea5c6..79fae204e34 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4678,6 +4678,11 @@ static const EnumPropertyItem node_principled_distribution_items[] = { }; static const EnumPropertyItem node_subsurface_method_items[] = { + {SHD_SUBSURFACE_BURLEY, + "BURLEY", + 0, + "Christensen-Burley", + "Approximation to physically based volume scattering"}, {SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS, "RANDOM_WALK_FIXED_RADIUS", 0, diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c index 06f4d1f1b79..2d3957d159e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c @@ -171,6 +171,7 @@ static void node_shader_update_principled(bNodeTree *UNUSED(ntree), bNode *node) { bNodeSocket *sock; int distribution = node->custom1; + int sss_method = node->custom2; for (sock = node->inputs.first; sock; sock = sock->next) { if (STREQ(sock->name, "Transmission Roughness")) { @@ -181,6 +182,15 @@ static void node_shader_update_principled(bNodeTree *UNUSED(ntree), bNode *node) sock->flag |= SOCK_UNAVAIL; } } + + if (STREQ(sock->name, "Subsurface IOR") || STREQ(sock->name, "Subsurface Anisotropy")) { + if (sss_method == SHD_SUBSURFACE_BURLEY) { + sock->flag |= SOCK_UNAVAIL; + } + else { + sock->flag &= ~SOCK_UNAVAIL; + } + } } } From a94343a8afcac5d6db09c8461e67ad1ba5a85d35 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 8 Oct 2021 19:44:56 +0200 Subject: [PATCH 0684/1500] Cycles: improve SSS Fresnel and retro-reflection in Principled BSDF For details see the "Extending the Disney BRDF to a BSDF with Integrated Subsurface Scattering" paper. We split the diffuse BSDF into a lambertian and retro-reflection component. The retro-reflection component is always handled as a BSDF, while the lambertian component can be replaced by a BSSRDF. For the BSSRDF case, we compute Fresnel separately at the entry and exit points, which may have different normals. As the scattering radius decreases this converges to the BSDF case. A downside is that this increases noise for subsurface scattering in the Principled BSDF, due to some samples going to the retro-reflection component. However the previous logic (also in 2.93) was simple wrong, using a non-sensical view direction vector at the exit point. We use an importance sampling weight estimate for the retro-reflection to try to better balance samples between the BSDF and BSSRDF. Differential Revision: https://developer.blender.org/D12801 --- intern/cycles/kernel/closure/bsdf.h | 6 -- .../kernel/closure/bsdf_principled_diffuse.h | 78 +++++++++++++++---- intern/cycles/kernel/closure/bssrdf.h | 30 ++++--- .../integrator/integrator_shade_surface.h | 4 +- .../integrator/integrator_state_template.h | 1 - .../kernel/integrator/integrator_subsurface.h | 44 +++++------ intern/cycles/kernel/kernel_shader.h | 11 +-- intern/cycles/kernel/kernel_types.h | 20 ++--- intern/cycles/kernel/svm/svm_closure.h | 4 +- intern/cycles/kernel/svm/svm_types.h | 4 - 10 files changed, 125 insertions(+), 77 deletions(-) diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index 4eb8bcae997..87aa6339f80 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -128,7 +128,6 @@ ccl_device_inline int bsdf_sample(const KernelGlobals *kg, switch (sc->type) { case CLOSURE_BSDF_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_ID: label = bsdf_diffuse_sample(sc, Ng, sd->I, @@ -401,7 +400,6 @@ ccl_device_inline int bsdf_sample(const KernelGlobals *kg, break; # ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: label = bsdf_principled_diffuse_sample(sc, Ng, sd->I, @@ -481,7 +479,6 @@ ccl_device_inline if (!is_transmission) { switch (sc->type) { case CLOSURE_BSDF_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_ID: eval = bsdf_diffuse_eval_reflect(sc, sd->I, omega_in, pdf); break; #ifdef __SVM__ @@ -550,7 +547,6 @@ ccl_device_inline break; # ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: eval = bsdf_principled_diffuse_eval_reflect(sc, sd->I, omega_in, pdf); break; case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: @@ -576,7 +572,6 @@ ccl_device_inline else { switch (sc->type) { case CLOSURE_BSDF_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_ID: eval = bsdf_diffuse_eval_transmit(sc, sd->I, omega_in, pdf); break; #ifdef __SVM__ @@ -637,7 +632,6 @@ ccl_device_inline break; # ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: eval = bsdf_principled_diffuse_eval_transmit(sc, sd->I, omega_in, pdf); break; case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index 0d611f40096..04963ca1dc5 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -19,50 +19,98 @@ /* DISNEY PRINCIPLED DIFFUSE BRDF * * Shading model by Brent Burley (Disney): "Physically Based Shading at Disney" (2012) + * + * "Extending the Disney BRDF to a BSDF with Integrated Subsurface Scattering" (2015) + * For the separation of retro-reflection, "2.3 Dielectric BRDF with integrated + * subsurface scattering" */ #include "kernel/closure/bsdf_util.h" CCL_NAMESPACE_BEGIN +enum PrincipledDiffuseBsdfComponents { + PRINCIPLED_DIFFUSE_FULL = 1, + PRINCIPLED_DIFFUSE_LAMBERT = 2, + PRINCIPLED_DIFFUSE_LAMBERT_EXIT = 4, + PRINCIPLED_DIFFUSE_RETRO_REFLECTION = 8, +}; + typedef ccl_addr_space struct PrincipledDiffuseBsdf { SHADER_CLOSURE_BASE; float roughness; + int components; } PrincipledDiffuseBsdf; static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledDiffuseBsdf), "PrincipledDiffuseBsdf is too large!"); -ccl_device float3 calculate_principled_diffuse_brdf( +ccl_device int bsdf_principled_diffuse_setup(PrincipledDiffuseBsdf *bsdf) +{ + bsdf->type = CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID; + return SD_BSDF | SD_BSDF_HAS_EVAL; +} + +ccl_device float3 bsdf_principled_diffuse_compute_brdf( const PrincipledDiffuseBsdf *bsdf, float3 N, float3 V, float3 L, float *pdf) { - float NdotL = dot(N, L); + const float NdotL = dot(N, L); if (NdotL <= 0) { return make_float3(0.0f, 0.0f, 0.0f); } - float NdotV = dot(N, V); + const float NdotV = dot(N, V); - /* H = normalize(L + V); // Bisector of an angle between L and V. - * LH2 = 2 * dot(L, H)^2 = 2cos(x)^2 = cos(2x) + 1 = dot(L, V) + 1, - * half-angle x between L and V is at most 90 deg - */ - float LH2 = dot(L, V) + 1; + const float FV = schlick_fresnel(NdotV); + const float FL = schlick_fresnel(NdotL); - float FL = schlick_fresnel(NdotL), FV = schlick_fresnel(NdotV); - const float Fd90 = 0.5f + LH2 * bsdf->roughness; - float Fd = (1.0f - FL + Fd90 * FL) * (1.0f - FV + Fd90 * FV); + float f = 0.0f; - float value = M_1_PI_F * NdotL * Fd; + /* Lambertian component. */ + if (bsdf->components & (PRINCIPLED_DIFFUSE_FULL | PRINCIPLED_DIFFUSE_LAMBERT)) { + f += (1.0f - 0.5f * FV) * (1.0f - 0.5f * FL); + } + else if (bsdf->components & PRINCIPLED_DIFFUSE_LAMBERT_EXIT) { + f += (1.0f - 0.5f * FL); + } + + /* Retro-reflection component. */ + if (bsdf->components & (PRINCIPLED_DIFFUSE_FULL | PRINCIPLED_DIFFUSE_RETRO_REFLECTION)) { + /* H = normalize(L + V); // Bisector of an angle between L and V + * LH2 = 2 * dot(L, H)^2 = 2cos(x)^2 = cos(2x) + 1 = dot(L, V) + 1, + * half-angle x between L and V is at most 90 deg. */ + const float LH2 = dot(L, V) + 1; + const float RR = bsdf->roughness * LH2; + f += RR * (FL + FV + FL * FV * (RR - 1.0f)); + } + + float value = M_1_PI_F * NdotL * f; return make_float3(value, value, value); } -ccl_device int bsdf_principled_diffuse_setup(PrincipledDiffuseBsdf *bsdf) +/* Compute Fresnel at entry point, to be compbined with PRINCIPLED_DIFFUSE_LAMBERT_EXIT + * at the exit point to get the complete BSDF. */ +ccl_device_inline float bsdf_principled_diffuse_compute_entry_fresnel(const float NdotV) +{ + const float FV = schlick_fresnel(NdotV); + return (1.0f - 0.5f * FV); +} + +/* Ad-hoc weight adjusment to avoid retro-reflection taking away half the + * samples from BSSRDF. */ +ccl_device_inline float bsdf_principled_diffuse_retro_reflection_sample_weight( + PrincipledDiffuseBsdf *bsdf, const float3 I) +{ + return bsdf->roughness * schlick_fresnel(dot(bsdf->N, I)); +} + +ccl_device int bsdf_principled_diffuse_setup(PrincipledDiffuseBsdf *bsdf, int components) { bsdf->type = CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID; + bsdf->components = components; return SD_BSDF | SD_BSDF_HAS_EVAL; } @@ -79,7 +127,7 @@ ccl_device float3 bsdf_principled_diffuse_eval_reflect(const ShaderClosure *sc, if (dot(N, omega_in) > 0.0f) { *pdf = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F; - return calculate_principled_diffuse_brdf(bsdf, N, V, L, pdf); + return bsdf_principled_diffuse_compute_brdf(bsdf, N, V, L, pdf); } else { *pdf = 0.0f; @@ -115,7 +163,7 @@ ccl_device int bsdf_principled_diffuse_sample(const ShaderClosure *sc, sample_cos_hemisphere(N, randu, randv, omega_in, pdf); if (dot(Ng, *omega_in) > 0) { - *eval = calculate_principled_diffuse_brdf(bsdf, N, I, *omega_in, pdf); + *eval = bsdf_principled_diffuse_compute_brdf(bsdf, N, I, *omega_in, pdf); #ifdef __RAY_DIFFERENTIALS__ // TODO: find a better approximation for the diffuse bounce diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index db183887018..d2f8af7910c 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -277,10 +277,27 @@ ccl_device_inline Bssrdf *bssrdf_alloc(ShaderData *sd, float3 weight) ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type, const float ior) { int flag = 0; + + /* Add retro-reflection component as separate diffuse BSDF. */ + if (bssrdf->roughness != FLT_MAX) { + PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( + sd, sizeof(PrincipledDiffuseBsdf), bssrdf->weight); + + if (bsdf) { + bsdf->N = bssrdf->N; + bsdf->roughness = bssrdf->roughness; + flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_RETRO_REFLECTION); + + /* Ad-hoc weight adjusment to avoid retro-reflection taking away half the + * samples from BSSRDF. */ + bsdf->sample_weight *= bsdf_principled_diffuse_retro_reflection_sample_weight(bsdf, sd->I); + } + } + + /* Verify if the radii are large enough to sample without precision issues. */ int bssrdf_channels = 3; float3 diffuse_weight = make_float3(0.0f, 0.0f, 0.0f); - /* Verify if the radii are large enough to sample without precision issues. */ if (bssrdf->radius.x < BSSRDF_MIN_RADIUS) { diffuse_weight.x = bssrdf->weight.x; bssrdf->weight.x = 0.0f; @@ -304,17 +321,13 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type, co /* Add diffuse BSDF if any radius too small. */ #ifdef __PRINCIPLED__ if (bssrdf->roughness != FLT_MAX) { - float roughness = bssrdf->roughness; - float3 N = bssrdf->N; - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), diffuse_weight); if (bsdf) { - bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID; - bsdf->N = N; - bsdf->roughness = roughness; - flag |= bsdf_principled_diffuse_setup(bsdf); + bsdf->N = bssrdf->N; + bsdf->roughness = bssrdf->roughness; + flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_LAMBERT); } } else @@ -323,7 +336,6 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type, co DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc(sd, sizeof(DiffuseBsdf), diffuse_weight); if (bsdf) { - bsdf->type = CLOSURE_BSDF_BSSRDF_ID; bsdf->N = bssrdf->N; flag |= bsdf_diffuse_setup(bsdf); } diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index a24473addcc..27338f824c0 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -378,11 +378,11 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, } #ifdef __SUBSURFACE__ - if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SUBSURFACE) { + if (path_flag & PATH_RAY_SUBSURFACE) { /* When coming from inside subsurface scattering, setup a diffuse * closure to perform lighting at the exit point. */ + subsurface_shader_data_setup(INTEGRATOR_STATE_PASS, &sd, path_flag); INTEGRATOR_STATE_WRITE(path, flag) &= ~PATH_RAY_SUBSURFACE; - subsurface_shader_data_setup(INTEGRATOR_STATE_PASS, &sd); } #endif diff --git a/intern/cycles/kernel/integrator/integrator_state_template.h b/intern/cycles/kernel/integrator/integrator_state_template.h index 15998ee6edf..0fe47cf13bc 100644 --- a/intern/cycles/kernel/integrator/integrator_state_template.h +++ b/intern/cycles/kernel/integrator/integrator_state_template.h @@ -99,7 +99,6 @@ KERNEL_STRUCT_BEGIN(subsurface) KERNEL_STRUCT_MEMBER(subsurface, float3, albedo, KERNEL_FEATURE_SUBSURFACE) KERNEL_STRUCT_MEMBER(subsurface, float3, radius, KERNEL_FEATURE_SUBSURFACE) KERNEL_STRUCT_MEMBER(subsurface, float, anisotropy, KERNEL_FEATURE_SUBSURFACE) -KERNEL_STRUCT_MEMBER(subsurface, float, roughness, KERNEL_FEATURE_SUBSURFACE) KERNEL_STRUCT_END(subsurface) /********************************** Volume Stack ******************************/ diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/integrator_subsurface.h index 7ca676351db..2d15c82322a 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface.h @@ -47,7 +47,7 @@ ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, ShaderData *sd, const Sh /* Setup ray into surface. */ INTEGRATOR_STATE_WRITE(ray, P) = sd->P; - INTEGRATOR_STATE_WRITE(ray, D) = sd->N; + INTEGRATOR_STATE_WRITE(ray, D) = bssrdf->N; INTEGRATOR_STATE_WRITE(ray, t) = FLT_MAX; INTEGRATOR_STATE_WRITE(ray, dP) = differential_make_compact(sd->dP); INTEGRATOR_STATE_WRITE(ray, dD) = differential_zero_compact(); @@ -56,13 +56,20 @@ ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, ShaderData *sd, const Sh INTEGRATOR_STATE_WRITE(isect, Ng) = sd->Ng; INTEGRATOR_STATE_WRITE(isect, object) = sd->object; - /* Pass BSSRDF parameters. */ - const uint32_t path_flag = INTEGRATOR_STATE_WRITE(path, flag); - INTEGRATOR_STATE_WRITE(path, flag) = (path_flag & ~PATH_RAY_CAMERA) | - ((sc->type == CLOSURE_BSSRDF_BURLEY_ID) ? - PATH_RAY_SUBSURFACE_DISK : - PATH_RAY_SUBSURFACE_RANDOM_WALK); - INTEGRATOR_STATE_WRITE(path, throughput) *= shader_bssrdf_sample_weight(sd, sc); + uint32_t path_flag = (INTEGRATOR_STATE(path, flag) & ~PATH_RAY_CAMERA) | + ((sc->type == CLOSURE_BSSRDF_BURLEY_ID) ? PATH_RAY_SUBSURFACE_DISK : + PATH_RAY_SUBSURFACE_RANDOM_WALK); + + /* Compute weight, optionally including Fresnel from entry point. */ + float3 weight = shader_bssrdf_sample_weight(sd, sc); +# ifdef __PRINCIPLED__ + if (bssrdf->roughness != FLT_MAX) { + path_flag |= PATH_RAY_SUBSURFACE_USE_FRESNEL; + } +# endif + + INTEGRATOR_STATE_WRITE(path, throughput) *= weight; + INTEGRATOR_STATE_WRITE(path, flag) = path_flag; /* Advance random number offset for bounce. */ INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; @@ -73,15 +80,17 @@ ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, ShaderData *sd, const Sh } } + /* Pass BSSRDF parameters. */ INTEGRATOR_STATE_WRITE(subsurface, albedo) = bssrdf->albedo; INTEGRATOR_STATE_WRITE(subsurface, radius) = bssrdf->radius; - INTEGRATOR_STATE_WRITE(subsurface, roughness) = bssrdf->roughness; INTEGRATOR_STATE_WRITE(subsurface, anisotropy) = bssrdf->anisotropy; return LABEL_SUBSURFACE_SCATTER; } -ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, ShaderData *sd) +ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, + ShaderData *sd, + const uint32_t path_flag) { /* Get bump mapped normal from shader evaluation at exit point. */ float3 N = sd->N; @@ -95,21 +104,16 @@ ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, ShaderData * sd->num_closure_left = kernel_data.max_closures; const float3 weight = one_float3(); - const float roughness = INTEGRATOR_STATE(subsurface, roughness); # ifdef __PRINCIPLED__ - if (roughness != FLT_MAX) { + if (path_flag & PATH_RAY_SUBSURFACE_USE_FRESNEL) { PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), weight); if (bsdf) { bsdf->N = N; - bsdf->roughness = roughness; - sd->flag |= bsdf_principled_diffuse_setup(bsdf); - - /* replace CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID with this special ID so render passes - * can recognize it as not being a regular Disney principled diffuse closure */ - bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID; + bsdf->roughness = FLT_MAX; + sd->flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_LAMBERT_EXIT); } } else @@ -120,10 +124,6 @@ ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, ShaderData * if (bsdf) { bsdf->N = N; sd->flag |= bsdf_diffuse_setup(bsdf); - - /* replace CLOSURE_BSDF_DIFFUSE_ID with this special ID so render passes - * can recognize it as not being a regular diffuse closure */ - bsdf->type = CLOSURE_BSDF_BSSRDF_ID; } } } diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index a50f3fb214b..d1b53832793 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -159,7 +159,7 @@ ccl_device_forceinline bool _shader_bsdf_exclude(ClosureType type, uint light_sh return false; } if (light_shader_flags & SHADER_EXCLUDE_DIFFUSE) { - if (CLOSURE_IS_BSDF_DIFFUSE(type) || CLOSURE_IS_BSDF_BSSRDF(type)) { + if (CLOSURE_IS_BSDF_DIFFUSE(type)) { return true; } } @@ -201,8 +201,7 @@ ccl_device_inline float _shader_bsdf_multi_eval(const KernelGlobals *kg, float3 eval = bsdf_eval(kg, sd, sc, omega_in, is_transmission, &bsdf_pdf); if (bsdf_pdf != 0.0f) { - const bool is_diffuse = (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || - CLOSURE_IS_BSDF_BSSRDF(sc->type)); + const bool is_diffuse = CLOSURE_IS_BSDF_DIFFUSE(sc->type); bsdf_eval_accum(result_eval, is_diffuse, eval * sc->weight, 1.0f); sum_pdf += bsdf_pdf * sc->sample_weight; } @@ -320,8 +319,7 @@ ccl_device int shader_bsdf_sample_closure(const KernelGlobals *kg, label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); if (*pdf != 0.0f) { - const bool is_diffuse = (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || - CLOSURE_IS_BSDF_BSSRDF(sc->type)); + const bool is_diffuse = CLOSURE_IS_BSDF_DIFFUSE(sc->type); bsdf_eval_init(bsdf_eval, is_diffuse, eval * sc->weight); if (sd->num_closure > 1) { @@ -401,8 +399,7 @@ ccl_device float3 shader_bsdf_diffuse(const KernelGlobals *kg, const ShaderData for (int i = 0; i < sd->num_closure; i++) { const ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type) || - CLOSURE_IS_BSDF_BSSRDF(sc->type)) + if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type)) eval += sc->weight; } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index eb681683d6a..00457695e53 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -263,32 +263,34 @@ enum PathRayFlag { /* Perform subsurface scattering. */ PATH_RAY_SUBSURFACE_RANDOM_WALK = (1 << 20), PATH_RAY_SUBSURFACE_DISK = (1 << 21), - PATH_RAY_SUBSURFACE = (PATH_RAY_SUBSURFACE_RANDOM_WALK | PATH_RAY_SUBSURFACE_DISK), + PATH_RAY_SUBSURFACE_USE_FRESNEL = (1 << 22), + PATH_RAY_SUBSURFACE = (PATH_RAY_SUBSURFACE_RANDOM_WALK | PATH_RAY_SUBSURFACE_DISK | + PATH_RAY_SUBSURFACE_USE_FRESNEL), /* Contribute to denoising features. */ - PATH_RAY_DENOISING_FEATURES = (1 << 22), + PATH_RAY_DENOISING_FEATURES = (1 << 23), /* Render pass categories. */ - PATH_RAY_REFLECT_PASS = (1 << 23), - PATH_RAY_TRANSMISSION_PASS = (1 << 24), - PATH_RAY_VOLUME_PASS = (1 << 25), + PATH_RAY_REFLECT_PASS = (1 << 24), + PATH_RAY_TRANSMISSION_PASS = (1 << 25), + PATH_RAY_VOLUME_PASS = (1 << 26), PATH_RAY_ANY_PASS = (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS | PATH_RAY_VOLUME_PASS), /* Shadow ray is for a light or surface. */ - PATH_RAY_SHADOW_FOR_LIGHT = (1 << 26), + PATH_RAY_SHADOW_FOR_LIGHT = (1 << 27), /* A shadow catcher object was hit and the path was split into two. */ - PATH_RAY_SHADOW_CATCHER_HIT = (1 << 27), + PATH_RAY_SHADOW_CATCHER_HIT = (1 << 28), /* A shadow catcher object was hit and this path traces only shadow catchers, writing them into * their dedicated pass for later division. * * NOTE: Is not covered with `PATH_RAY_ANY_PASS` because shadow catcher does special handling * which is separate from the light passes. */ - PATH_RAY_SHADOW_CATCHER_PASS = (1 << 28), + PATH_RAY_SHADOW_CATCHER_PASS = (1 << 29), /* Path is evaluating background for an approximate shadow catcher with non-transparent film. */ - PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1 << 29), + PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1 << 30), }; /* Configure ray visibility bits for rays and objects respectively, diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h index b3f7fee8a63..e55f76a4400 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/svm_closure.h @@ -221,7 +221,7 @@ ccl_device_noinline int svm_node_closure_bsdf( bsdf->roughness = roughness; /* setup bsdf */ - sd->flag |= bsdf_principled_diffuse_setup(bsdf); + sd->flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_FULL); } } else if (subsurface > CLOSURE_WEIGHT_CUTOFF) { @@ -255,7 +255,7 @@ ccl_device_noinline int svm_node_closure_bsdf( bsdf->roughness = roughness; /* setup bsdf */ - sd->flag |= bsdf_principled_diffuse_setup(bsdf); + sd->flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_FULL); } } # endif diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index e846e4af259..6f6c101fb69 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -538,8 +538,6 @@ typedef enum ClosureType { CLOSURE_BSDF_HAIR_TRANSMISSION_ID, /* Special cases */ - CLOSURE_BSDF_BSSRDF_ID, - CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID, CLOSURE_BSDF_TRANSPARENT_ID, /* BSSRDF */ @@ -569,8 +567,6 @@ typedef enum ClosureType { (type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID)) #define CLOSURE_IS_BSDF_TRANSMISSION(type) \ (type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID) -#define CLOSURE_IS_BSDF_BSSRDF(type) \ - (type == CLOSURE_BSDF_BSSRDF_ID || type == CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID) #define CLOSURE_IS_BSDF_SINGULAR(type) \ (type == CLOSURE_BSDF_REFLECTION_ID || type == CLOSURE_BSDF_REFRACTION_ID || \ type == CLOSURE_BSDF_TRANSPARENT_ID) From db851ccd2a896f3f058dba56a5380fb2bf1ae0f9 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 11 Oct 2021 17:56:18 +0200 Subject: [PATCH 0685/1500] Fix T92056: empty sampling pattern in Cycles when opening some existing files --- intern/cycles/blender/addon/properties.py | 4 ++-- intern/cycles/blender/addon/version_update.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 8c1f26d7b9f..faa0aaec8ae 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -86,8 +86,8 @@ enum_use_layer_samples = ( ) enum_sampling_pattern = ( - ('SOBOL', "Sobol", "Use Sobol random sampling pattern"), - ('PROGRESSIVE_MUTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern"), + ('SOBOL', "Sobol", "Use Sobol random sampling pattern", 0), + ('PROGRESSIVE_MUTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern", 1), ) enum_volume_sampling = ( diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py index 57da7d7995c..b3e8e755903 100644 --- a/intern/cycles/blender/addon/version_update.py +++ b/intern/cycles/blender/addon/version_update.py @@ -235,7 +235,8 @@ def do_versions(self): cscene.use_denoising = False if not cscene.is_property_set("use_preview_denoising"): cscene.use_preview_denoising = False - if not cscene.is_property_set("sampling_pattern"): + if not cscene.is_property_set("sampling_pattern") or \ + cscene.get('sampling_pattern') >= 2: cscene.sampling_pattern = 'PROGRESSIVE_MUTI_JITTER' # Removal of square samples. From d993c7b50300a588eb909e9e02b0735f2cb8f11b Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 11 Oct 2021 18:26:31 +0200 Subject: [PATCH 0686/1500] Fix recently added File Browser name/path getters not allowing unicode The `FileSelectEntry.name` and `FileSelectEntry.relative_path` members should support unicode strings like any file names & paths, but didn't. --- source/blender/makesrna/intern/rna_space.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index fcaf53da81b..8a2f48ba991 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2652,7 +2652,7 @@ static int rna_FileBrowser_FileSelectEntry_name_editable(PointerRNA *ptr, const static void rna_FileBrowser_FileSelectEntry_name_get(PointerRNA *ptr, char *value) { const FileDirEntry *entry = ptr->data; - strcpy(value, entry->name); + BLI_strncpy_utf8(value, entry->name, strlen(entry->name) + 1); } static int rna_FileBrowser_FileSelectEntry_name_length(PointerRNA *ptr) @@ -2664,7 +2664,7 @@ static int rna_FileBrowser_FileSelectEntry_name_length(PointerRNA *ptr) static void rna_FileBrowser_FileSelectEntry_relative_path_get(PointerRNA *ptr, char *value) { const FileDirEntry *entry = ptr->data; - strcpy(value, entry->relpath); + BLI_strncpy_utf8(value, entry->relpath, strlen(entry->relpath) + 1); } static int rna_FileBrowser_FileSelectEntry_relative_path_length(PointerRNA *ptr) @@ -6344,7 +6344,7 @@ static void rna_def_fileselect_entry(BlenderRNA *brna) RNA_def_struct_sdna(srna, "FileDirEntry"); RNA_def_struct_ui_text(srna, "File Select Entry", "A file viewable in the File Browser"); - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_FILENAME); RNA_def_property_editable_func(prop, "rna_FileBrowser_FileSelectEntry_name_editable"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_string_funcs(prop, @@ -6354,7 +6354,7 @@ static void rna_def_fileselect_entry(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Name", ""); RNA_def_struct_name_property(srna, prop); - prop = RNA_def_property(srna, "relative_path", PROP_STRING, PROP_NONE); + prop = RNA_def_property(srna, "relative_path", PROP_STRING, PROP_FILEPATH); RNA_def_property_string_funcs(prop, "rna_FileBrowser_FileSelectEntry_relative_path_get", "rna_FileBrowser_FileSelectEntry_relative_path_length", From ca8e8fd8d4747943bbff5f37b4858c7cd32daef3 Mon Sep 17 00:00:00 2001 From: Jarrett Johnson Date: Mon, 11 Oct 2021 13:26:31 -0500 Subject: [PATCH 0687/1500] Fix T92102: Issues with align euler to vector node For fixed pivots, make sure the correct pivot axis is being used. Also add continues or invalid rotations. Differential Revision: https://developer.blender.org/D12824 --- .../nodes/function/nodes/node_fn_align_euler_to_vector.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc index 4c741c96bb8..ae41cdfca5a 100644 --- a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc @@ -57,6 +57,7 @@ static void align_rotations_auto_pivot(IndexMask mask, const float3 vector = vectors[i]; if (is_zero_v3(vector)) { output_rotations[i] = input_rotations[i]; + continue; } float old_rotation[3][3]; @@ -106,10 +107,12 @@ static void align_rotations_fixed_pivot(IndexMask mask, if (local_main_axis == local_pivot_axis) { /* Can't compute any meaningful rotation angle in this case. */ output_rotations[i] = input_rotations[i]; + continue; } const float3 vector = vectors[i]; if (is_zero_v3(vector)) { + output_rotations[i] = input_rotations[i]; continue; } @@ -182,7 +185,7 @@ class MF_AlignEulerToVector : public fn::MultiFunction { } else { float3 local_pivot_axis = {0.0f, 0.0f, 0.0f}; - local_pivot_axis[main_axis_mode_] = 1; + local_pivot_axis[pivot_axis_mode_ - 1] = 1; align_rotations_fixed_pivot(mask, input_rotations, vectors, From e005ad5b547627914a87469b58bcd6a249c95c55 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Mon, 11 Oct 2021 21:59:04 +0100 Subject: [PATCH 0688/1500] Fix T92103: Update BLI hash_float_to_float functions to be shader compatible Previously the functions called `hash_float` instead of `uint_to_float_01`. This meant that the float was hashed twice instead of once. The new functions are also compatible with Cycles/Eevee. Differential Revision: https://developer.blender.org/D12832 --- source/blender/blenlib/intern/noise.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index e80975f618c..6ed1fae71ad 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -222,22 +222,22 @@ float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) float hash_float_to_float(float k) { - return hash_to_float(hash_float(k)); + return uint_to_float_01(hash_float(k)); } float hash_float_to_float(float2 k) { - return hash_to_float(hash_float(k)); + return uint_to_float_01(hash_float(k)); } float hash_float_to_float(float3 k) { - return hash_to_float(hash_float(k)); + return uint_to_float_01(hash_float(k)); } float hash_float_to_float(float4 k) { - return hash_to_float(hash_float(k)); + return uint_to_float_01(hash_float(k)); } /* ------------ From f7ef68514bc2ee1abf902807702effe04f2e0dff Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Mon, 11 Oct 2021 23:02:17 +0100 Subject: [PATCH 0689/1500] Geometry Nodes: Add Color input node Adds a color input node with picker. Differential Revision: https://developer.blender.org/D12793 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesdna/DNA_node_types.h | 4 ++ source/blender/makesrna/intern/rna_nodetree.c | 13 ++++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_function.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../function/nodes/node_fn_input_color.cc | 65 +++++++++++++++++++ 9 files changed, 88 insertions(+) create mode 100644 source/blender/nodes/function/nodes/node_fn_input_color.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index b6bc70e36fc..bceebe031cf 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -640,6 +640,7 @@ geometry_node_categories = [ NodeItem("ShaderNodeValue"), NodeItem("FunctionNodeInputString"), NodeItem("FunctionNodeInputVector"), + NodeItem("FunctionNodeInputColor"), NodeItem("GeometryNodeInputMaterial"), NodeItem("GeometryNodeIsViewport"), NodeItem("GeometryNodeInputIndex"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index e0aeb6e875f..81cf1ed180f 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1550,6 +1550,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_RANDOM_VALUE 1214 #define FN_NODE_ROTATE_EULER 1215 #define FN_NODE_ALIGN_EULER_TO_VECTOR 1216 +#define FN_NODE_INPUT_COLOR 1217 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 41f9bf46b81..3e577bc29a3 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5841,6 +5841,7 @@ static void registerFunctionNodes() register_node_type_fn_input_special_characters(); register_node_type_fn_input_string(); register_node_type_fn_input_vector(); + register_node_type_fn_input_color(); register_node_type_fn_random_value(); register_node_type_fn_rotate_euler(); register_node_type_fn_string_length(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 74465c4bbe0..affe017feed 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1295,6 +1295,10 @@ typedef struct NodeInputVector { float vector[3]; } NodeInputVector; +typedef struct NodeInputColor { + float color[4]; +} NodeInputColor; + typedef struct NodeInputString { char *string; } NodeInputString; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 79fae204e34..f52173b45d4 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4988,6 +4988,19 @@ static void def_texture(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_fn_input_color(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeInputColor", "storage"); + + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_array(prop, 4); + RNA_def_property_float_sdna(prop, NULL, "color"); + RNA_def_property_ui_text(prop, "Color", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_fn_input_vector(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index a4e8df3164c..f22b890b243 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -142,6 +142,7 @@ set(SRC function/nodes/node_fn_input_special_characters.cc function/nodes/node_fn_input_string.cc function/nodes/node_fn_input_vector.cc + function/nodes/node_fn_input_color.cc function/nodes/node_fn_random_value.cc function/nodes/node_fn_rotate_euler.cc function/nodes/node_fn_string_length.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 395a2d68b53..999162b1803 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -29,6 +29,7 @@ void register_node_type_fn_float_to_int(void); void register_node_type_fn_input_special_characters(void); void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); +void register_node_type_fn_input_color(void); void register_node_type_fn_random_value(void); void register_node_type_fn_rotate_euler(void); void register_node_type_fn_string_length(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index e43f471d1e3..d4cc7b42292 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -269,6 +269,7 @@ DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vecto DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Compare Floats", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") +DefNode(FunctionNode, FN_NODE_INPUT_COLOR, def_fn_input_color, "INPUT_COLOR", InputColor, "Color", "") DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "") DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc new file mode 100644 index 00000000000..5dc211da1b2 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void fn_node_input_color_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Color"); +}; + +static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiTemplateColorPicker(layout, ptr, "color", true, false, false, true); + uiItemR(layout, ptr, "color", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void fn_node_color_input_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &bnode = builder.node(); + NodeInputColor *node_storage = static_cast(bnode.storage); + blender::ColorGeometry4f color = (ColorGeometry4f)node_storage->color; + builder.construct_and_set_matching_fn>(color); +} + +static void fn_node_input_color_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeInputColor *data = (NodeInputColor *)MEM_callocN(sizeof(NodeInputColor), __func__); + copy_v4_fl4(data->color, 0.5f, 0.5f, 0.5f, 1.0f); + node->storage = data; +} + +} // namespace blender::nodes + +void register_node_type_fn_input_color() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_INPUT_COLOR, "Color", NODE_CLASS_INPUT, 0); + ntype.declare = blender::nodes::fn_node_input_color_declare; + node_type_init(&ntype, blender::nodes::fn_node_input_color_init); + node_type_storage( + &ntype, "NodeInputColor", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_color_input_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_input_color_layout; + nodeRegisterType(&ntype); +} From 4b31a21bcd19b5005cb46895186ca7ecf314c4db Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 11 Oct 2021 18:14:03 -0500 Subject: [PATCH 0690/1500] Geometry Nodes: Use a separator in the add menu input category This can help separate the field inputs from the other nodes, like some other categories. --- release/scripts/startup/nodeitems_builtins.py | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index bceebe031cf..f63e41b0c28 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -152,6 +152,34 @@ def mesh_node_items(context): yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeSetShadeSmooth") +# Custom Menu for Geometry Node Input Nodes +def geometry_input_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + if not space.edit_tree: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("FunctionNodeLegacyRandomFloat"), + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeCollectionInfo") + yield NodeItem("FunctionNodeInputColor") + yield NodeItem("GeometryNodeIsViewport") + yield NodeItem("GeometryNodeInputMaterial") + yield NodeItem("GeometryNodeObjectInfo") + yield NodeItem("FunctionNodeInputString") + yield NodeItem("ShaderNodeValue") + yield NodeItem("FunctionNodeInputVector") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeInputIndex") + yield NodeItem("GeometryNodeInputNormal") + yield NodeItem("GeometryNodeInputPosition") + yield NodeItem("GeometryNodeInputRadius") + # Custom Menu for Geometry Node Curves def point_node_items(context): if context is None: @@ -633,21 +661,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeSetPosition"), NodeItem("GeometryNodeRealizeInstances"), ]), - GeometryNodeCategory("GEO_INPUT", "Input", items=[ - NodeItem("FunctionNodeLegacyRandomFloat", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeObjectInfo"), - NodeItem("GeometryNodeCollectionInfo"), - NodeItem("ShaderNodeValue"), - NodeItem("FunctionNodeInputString"), - NodeItem("FunctionNodeInputVector"), - NodeItem("FunctionNodeInputColor"), - NodeItem("GeometryNodeInputMaterial"), - NodeItem("GeometryNodeIsViewport"), - NodeItem("GeometryNodeInputIndex"), - NodeItem("GeometryNodeInputPosition"), - NodeItem("GeometryNodeInputRadius"), - NodeItem("GeometryNodeInputNormal"), - ]), + GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items), GeometryNodeCategory("GEO_MATERIAL", "Material", items=[ NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_legacy_poll), NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_legacy_poll), From cdeb506008e9fc6d8d9a48ce90204ec10101cdd0 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 12 Oct 2021 13:09:01 +0900 Subject: [PATCH 0691/1500] XR Controller Support Step 3: XR Events Integrates XR input actions with the WM event system. With this commit, all VR action functionality (operator execution, pose querying, haptic application), with the exception of custom drawing, is enabled. By itself, this does not bring about any changes for regular users, however it is necessary for the upcoming VR add-on update that will expose default controller actions to users. For add-on developers, this updates the Python API with access to XR event data (input states, controller poses, etc.), which can be obtained via the "xr" property added to the bpy.types.Event struct. For XR events, this property will be non-null and the event will have the type XR_ACTION. Further details: XR-type window events are queued to the regular window queues after updating and interpreting VR action states. An appropriate window is found by either using the window the VR session was started in or a fallback option. When handling XR events, mouse-specific processing is skipped and instead a dedicated XR offscreen area and region (see 08511b1c3de0) is used to execute XR event operators in the proper context. Reviewed By: Severin Differential Revision: https://developer.blender.org/D10944 --- source/blender/makesrna/intern/rna_wm.c | 21 + source/blender/makesrna/intern/rna_xr.c | 238 ++++++++ source/blender/windowmanager/WM_api.h | 4 + .../windowmanager/intern/wm_event_query.c | 13 + .../windowmanager/xr/intern/wm_xr_session.c | 510 +++++++++++++++++- 5 files changed, 784 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index c2d1ac67675..b45cfb04bc1 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -362,6 +362,8 @@ const EnumPropertyItem rna_enum_event_type_items[] = { 0, "ActionZone Fullscreen", "AZone FullScr"}, + /* xr */ + {EVT_XR_ACTION, "XR_ACTION", 0, "XR Action", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -700,6 +702,18 @@ static void rna_Event_tilt_get(PointerRNA *ptr, float *values) WM_event_tablet_data(event, NULL, values); } +static PointerRNA rna_Event_xr_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmEvent *event = ptr->data; + wmXrActionData *actiondata = WM_event_is_xr(event) ? event->customdata : NULL; + return rna_pointer_inherit_refine(ptr, &RNA_XrEventData, actiondata); +# else + UNUSED_VARS(ptr); + return PointerRNA_NULL; +# endif +} + static PointerRNA rna_PopupMenu_layout_get(PointerRNA *ptr) { struct uiPopupMenu *pup = ptr->data; @@ -2228,6 +2242,13 @@ static void rna_def_event(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Absolute Motion", "The last motion event was an absolute input"); + /* xr */ + prop = RNA_def_property(srna, "xr", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "XrEventData"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, "rna_Event_xr_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "XR", "XR event data"); + /* modifiers */ prop = RNA_def_property(srna, "shift", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "shift", 1); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index f24d28d1209..a433a93e403 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -947,6 +947,155 @@ static void rna_XrSessionState_selected_actionmap_set(PointerRNA *ptr, int value /** \} */ +/* -------------------------------------------------------------------- */ +/** \name XR Event Data + * \{ */ + +static void rna_XrEventData_action_set_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->action_set); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_action_set_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->action_set); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static void rna_XrEventData_action_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->action); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_action_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->action); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static int rna_XrEventData_type_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return data->type; +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static void rna_XrEventData_state_get(PointerRNA *ptr, float r_values[2]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_v2_v2(r_values, data->state); +# else + UNUSED_VARS(ptr); + zero_v2(r_values); +# endif +} + +static void rna_XrEventData_state_other_get(PointerRNA *ptr, float r_values[2]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_v2_v2(r_values, data->state_other); +# else + UNUSED_VARS(ptr); + zero_v2(r_values); +# endif +} + +static float rna_XrEventData_float_threshold_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return data->float_threshold; +# else + UNUSED_VARS(ptr); + return 0.0f; +# endif +} + +static void rna_XrEventData_controller_location_get(PointerRNA *ptr, float r_values[3]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_v3_v3(r_values, data->controller_loc); +# else + UNUSED_VARS(ptr); + zero_v3(r_values); +# endif +} + +static void rna_XrEventData_controller_rotation_get(PointerRNA *ptr, float r_values[4]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_qt_qt(r_values, data->controller_rot); +# else + UNUSED_VARS(ptr); + unit_qt(r_values); +# endif +} + +static void rna_XrEventData_controller_location_other_get(PointerRNA *ptr, float r_values[3]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_v3_v3(r_values, data->controller_loc_other); +# else + UNUSED_VARS(ptr); + zero_v3(r_values); +# endif +} + +static void rna_XrEventData_controller_rotation_other_get(PointerRNA *ptr, float r_values[4]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_qt_qt(r_values, data->controller_rot_other); +# else + UNUSED_VARS(ptr); + unit_qt(r_values); +# endif +} + +static bool rna_XrEventData_bimanual_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return data->bimanual; +# else + UNUSED_VARS(ptr); + return false; +# endif +} + +/** \} */ + #else /* RNA_RUNTIME */ /* -------------------------------------------------------------------- */ @@ -1824,6 +1973,94 @@ static void rna_def_xr_session_state(BlenderRNA *brna) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name XR Event Data + * \{ */ + +static void rna_def_xr_eventdata(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "XrEventData", NULL); + RNA_def_struct_ui_text(srna, "XrEventData", "XR Data for Window Manager Event"); + + prop = RNA_def_property(srna, "action_set", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_action_set_get", "rna_XrEventData_action_set_length", NULL); + RNA_def_property_ui_text(prop, "Action Set", "XR action set name"); + + prop = RNA_def_property(srna, "action", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_action_get", "rna_XrEventData_action_length", NULL); + RNA_def_property_ui_text(prop, "Action", "XR action name"); + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_enum_items(prop, rna_enum_xr_action_types); + RNA_def_property_enum_funcs(prop, "rna_XrEventData_type_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Type", "XR action type"); + + prop = RNA_def_property(srna, "state", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 2); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_state_get", NULL, NULL); + RNA_def_property_ui_text(prop, "State", "XR action values corresponding to type"); + + prop = RNA_def_property(srna, "state_other", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 2); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_state_other_get", NULL, NULL); + RNA_def_property_ui_text( + prop, "State Other", "State of the other user path for bimanual actions"); + + prop = RNA_def_property(srna, "float_threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_float_threshold_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Float Threshold", "Input threshold for float/2D vector actions"); + + prop = RNA_def_property(srna, "controller_location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_controller_location_get", NULL, NULL); + RNA_def_property_ui_text(prop, + "Controller Location", + "Location of the action's corresponding controller aim in world space"); + + prop = RNA_def_property(srna, "controller_rotation", PROP_FLOAT, PROP_QUATERNION); + RNA_def_property_array(prop, 4); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_controller_rotation_get", NULL, NULL); + RNA_def_property_ui_text(prop, + "Controller Rotation", + "Rotation of the action's corresponding controller aim in world space"); + + prop = RNA_def_property(srna, "controller_location_other", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_controller_location_other_get", NULL, NULL); + RNA_def_property_ui_text(prop, + "Controller Location Other", + "Controller aim location of the other user path for bimanual actions"); + + prop = RNA_def_property(srna, "controller_rotation_other", PROP_FLOAT, PROP_QUATERNION); + RNA_def_property_array(prop, 4); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_controller_rotation_other_get", NULL, NULL); + RNA_def_property_ui_text(prop, + "Controller Rotation Other", + "Controller aim rotation of the other user path for bimanual actions"); + + prop = RNA_def_property(srna, "bimanual", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs(prop, "rna_XrEventData_bimanual_get", NULL); + RNA_def_property_ui_text(prop, "Bimanual", "Whether bimanual interaction is occurring"); +} + +/** \} */ + void RNA_def_xr(BlenderRNA *brna) { RNA_define_animate_sdna(false); @@ -1831,6 +2068,7 @@ void RNA_def_xr(BlenderRNA *brna) rna_def_xr_actionmap(brna); rna_def_xr_session_settings(brna); rna_def_xr_session_state(brna); + rna_def_xr_eventdata(brna); RNA_define_animate_sdna(true); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index b79f5762955..531c5aa19bf 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -937,6 +937,10 @@ float WM_event_ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axi void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]); #endif /* WITH_INPUT_NDOF */ +#ifdef WITH_XR_OPENXR +bool WM_event_is_xr(const struct wmEvent *event); +#endif + float WM_event_tablet_data(const struct wmEvent *event, int *pen_flip, float tilt[2]); bool WM_event_is_tablet(const struct wmEvent *event); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 7b5691b99a0..8b77c460be3 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -410,6 +410,19 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Event XR Input Access + * \{ */ + +#ifdef WITH_XR_OPENXR +bool WM_event_is_xr(const struct wmEvent *event) +{ + return (event->type == EVT_XR_ACTION && event->custom == EVT_DATA_XR); +} +#endif + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Event Tablet Input Access * \{ */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 819057890fd..2a59a48f152 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -21,6 +21,7 @@ #include "BKE_callbacks.h" #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_idprop.h" #include "BKE_main.h" #include "BKE_scene.h" @@ -42,9 +43,12 @@ #include "MEM_guardedalloc.h" +#include "PIL_time.h" + #include "WM_api.h" #include "WM_types.h" +#include "wm_event_system.h" #include "wm_surface.h" #include "wm_window.h" #include "wm_xr_intern.h" @@ -559,6 +563,509 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin } } +static const GHOST_XrPose *wm_xr_session_controller_aim_pose_find(const wmXrSessionState *state, + const char *subaction_path) +{ + const wmXrController *controller = BLI_findstring( + &state->controllers, subaction_path, offsetof(wmXrController, subaction_path)); + return controller ? &controller->aim_pose : NULL; +} + +BLI_INLINE bool test_float_state(const float *state, float threshold, eXrAxisFlag flag) +{ + if ((flag & XR_AXIS0_POS) != 0) { + if (*state > threshold) { + return true; + } + } + else if ((flag & XR_AXIS0_NEG) != 0) { + if (*state < -threshold) { + return true; + } + } + else { + if (fabsf(*state) > threshold) { + return true; + } + } + return false; +} + +BLI_INLINE bool test_vec2f_state(const float state[2], float threshold, eXrAxisFlag flag) +{ + if ((flag & XR_AXIS0_POS) != 0) { + if (state[0] < 0.0f) { + return false; + } + } + else if ((flag & XR_AXIS0_NEG) != 0) { + if (state[0] > 0.0f) { + return false; + } + } + if ((flag & XR_AXIS1_POS) != 0) { + if (state[1] < 0.0f) { + return false; + } + } + else if ((flag & XR_AXIS1_NEG) != 0) { + if (state[1] > 0.0f) { + return false; + } + } + return (len_v2(state) > threshold); +} + +static bool wm_xr_session_modal_action_test(const ListBase *active_modal_actions, + const wmXrAction *action, + bool *r_found) +{ + if (r_found) { + *r_found = false; + } + + LISTBASE_FOREACH (LinkData *, ld, active_modal_actions) { + wmXrAction *active_modal_action = ld->data; + if (action == active_modal_action) { + if (r_found) { + *r_found = true; + } + return true; + } + if (action->ot == active_modal_action->ot && + IDP_EqualsProperties(action->op_properties, active_modal_action->op_properties)) { + /* Don't allow duplicate modal operators since this can lead to unwanted modal handler + * behavior. */ + return false; + } + } + + return true; +} + +static void wm_xr_session_modal_action_test_add(ListBase *active_modal_actions, + const wmXrAction *action) +{ + bool found; + if (wm_xr_session_modal_action_test(active_modal_actions, action, &found) && !found) { + LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); + ld->data = (void *)action; + BLI_addtail(active_modal_actions, ld); + } +} + +static void wm_xr_session_modal_action_remove(ListBase *active_modal_actions, + const wmXrAction *action) +{ + LISTBASE_FOREACH (LinkData *, ld, active_modal_actions) { + if (action == ld->data) { + BLI_freelinkN(active_modal_actions, ld); + return; + } + } +} + +static wmXrHapticAction *wm_xr_session_haptic_action_find(ListBase *active_haptic_actions, + const wmXrAction *action, + const char *subaction_path) +{ + LISTBASE_FOREACH (wmXrHapticAction *, ha, active_haptic_actions) { + if ((action == ha->action) && (subaction_path == ha->subaction_path)) { + return ha; + } + } + return NULL; +} + +static void wm_xr_session_haptic_action_add(ListBase *active_haptic_actions, + const wmXrAction *action, + const char *subaction_path, + int64_t time_now) +{ + wmXrHapticAction *ha = wm_xr_session_haptic_action_find( + active_haptic_actions, action, subaction_path); + if (ha) { + /* Reset start time since OpenXR restarts haptics if they are already active. */ + ha->time_start = time_now; + } + else { + ha = MEM_callocN(sizeof(wmXrHapticAction), __func__); + ha->action = (wmXrAction *)action; + ha->subaction_path = subaction_path; + ha->time_start = time_now; + BLI_addtail(active_haptic_actions, ha); + } +} + +static void wm_xr_session_haptic_action_remove(ListBase *active_haptic_actions, + const wmXrAction *action) +{ + LISTBASE_FOREACH (wmXrHapticAction *, ha, active_haptic_actions) { + if (action == ha->action) { + BLI_freelinkN(active_haptic_actions, ha); + return; + } + } +} + +static void wm_xr_session_haptic_timers_check(ListBase *active_haptic_actions, int64_t time_now) +{ + LISTBASE_FOREACH_MUTABLE (wmXrHapticAction *, ha, active_haptic_actions) { + if (time_now - ha->time_start >= ha->action->haptic_duration) { + BLI_freelinkN(active_haptic_actions, ha); + } + } +} + +static void wm_xr_session_action_states_interpret(wmXrData *xr, + const char *action_set_name, + wmXrAction *action, + unsigned int subaction_idx, + ListBase *active_modal_actions, + ListBase *active_haptic_actions, + int64_t time_now, + bool modal, + bool haptic, + short *r_val) +{ + const char *haptic_subaction_path = ((action->haptic_flag & XR_HAPTIC_MATCHUSERPATHS) != 0) ? + action->subaction_paths[subaction_idx] : + NULL; + bool curr = false; + bool prev = false; + + switch (action->type) { + case XR_BOOLEAN_INPUT: { + const bool *state = &((bool *)action->states)[subaction_idx]; + bool *state_prev = &((bool *)action->states_prev)[subaction_idx]; + if (*state) { + curr = true; + } + if (*state_prev) { + prev = true; + } + *state_prev = *state; + break; + } + case XR_FLOAT_INPUT: { + const float *state = &((float *)action->states)[subaction_idx]; + float *state_prev = &((float *)action->states_prev)[subaction_idx]; + if (test_float_state( + state, action->float_thresholds[subaction_idx], action->axis_flags[subaction_idx])) { + curr = true; + } + if (test_float_state(state_prev, + action->float_thresholds[subaction_idx], + action->axis_flags[subaction_idx])) { + prev = true; + } + *state_prev = *state; + break; + } + case XR_VECTOR2F_INPUT: { + const float(*state)[2] = &((float(*)[2])action->states)[subaction_idx]; + float(*state_prev)[2] = &((float(*)[2])action->states_prev)[subaction_idx]; + if (test_vec2f_state(*state, + action->float_thresholds[subaction_idx], + action->axis_flags[subaction_idx])) { + curr = true; + } + if (test_vec2f_state(*state_prev, + action->float_thresholds[subaction_idx], + action->axis_flags[subaction_idx])) { + prev = true; + } + copy_v2_v2(*state_prev, *state); + break; + } + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + + if (curr) { + if (!prev) { + if (modal || (action->op_flag == XR_OP_PRESS)) { + *r_val = KM_PRESS; + } + if (haptic && (action->haptic_flag & (XR_HAPTIC_PRESS | XR_HAPTIC_REPEAT)) != 0) { + /* Apply haptics. */ + if (WM_xr_haptic_action_apply(xr, + action_set_name, + action->haptic_name, + haptic_subaction_path, + &action->haptic_duration, + &action->haptic_frequency, + &action->haptic_amplitude)) { + wm_xr_session_haptic_action_add( + active_haptic_actions, action, haptic_subaction_path, time_now); + } + } + } + else if (modal) { + *r_val = KM_PRESS; + } + if (modal && !action->active_modal_path) { + /* Set active modal path. */ + action->active_modal_path = action->subaction_paths[subaction_idx]; + /* Add to active modal actions. */ + wm_xr_session_modal_action_test_add(active_modal_actions, action); + } + if (haptic && ((action->haptic_flag & XR_HAPTIC_REPEAT) != 0)) { + if (!wm_xr_session_haptic_action_find( + active_haptic_actions, action, haptic_subaction_path)) { + /* Apply haptics. */ + if (WM_xr_haptic_action_apply(xr, + action_set_name, + action->haptic_name, + haptic_subaction_path, + &action->haptic_duration, + &action->haptic_frequency, + &action->haptic_amplitude)) { + wm_xr_session_haptic_action_add( + active_haptic_actions, action, haptic_subaction_path, time_now); + } + } + } + } + else if (prev) { + if (modal || (action->op_flag == XR_OP_RELEASE)) { + *r_val = KM_RELEASE; + if (modal && (action->subaction_paths[subaction_idx] == action->active_modal_path)) { + /* Unset active modal path. */ + action->active_modal_path = NULL; + /* Remove from active modal actions. */ + wm_xr_session_modal_action_remove(active_modal_actions, action); + } + } + if (haptic) { + if ((action->haptic_flag & XR_HAPTIC_RELEASE) != 0) { + /* Apply haptics. */ + if (WM_xr_haptic_action_apply(xr, + action_set_name, + action->haptic_name, + haptic_subaction_path, + &action->haptic_duration, + &action->haptic_frequency, + &action->haptic_amplitude)) { + wm_xr_session_haptic_action_add( + active_haptic_actions, action, haptic_subaction_path, time_now); + } + } + else if ((action->haptic_flag & XR_HAPTIC_REPEAT) != 0) { + /* Stop any active haptics. */ + WM_xr_haptic_action_stop(xr, action_set_name, action->haptic_name, haptic_subaction_path); + wm_xr_session_haptic_action_remove(active_haptic_actions, action); + } + } + } +} + +static bool wm_xr_session_action_test_bimanual(const wmXrSessionState *session_state, + wmXrAction *action, + unsigned int subaction_idx, + unsigned int *r_subaction_idx_other, + const GHOST_XrPose **r_aim_pose_other) +{ + if ((action->action_flag & XR_ACTION_BIMANUAL) == 0) { + return false; + } + + bool bimanual = false; + + *r_subaction_idx_other = (subaction_idx == 0) ? + (unsigned int)min_ii(1, action->count_subaction_paths - 1) : + 0; + + switch (action->type) { + case XR_BOOLEAN_INPUT: { + const bool *state = &((bool *)action->states)[*r_subaction_idx_other]; + if (*state) { + bimanual = true; + } + break; + } + case XR_FLOAT_INPUT: { + const float *state = &((float *)action->states)[*r_subaction_idx_other]; + if (test_float_state(state, + action->float_thresholds[*r_subaction_idx_other], + action->axis_flags[*r_subaction_idx_other])) { + bimanual = true; + } + break; + } + case XR_VECTOR2F_INPUT: { + const float(*state)[2] = &((float(*)[2])action->states)[*r_subaction_idx_other]; + if (test_vec2f_state(*state, + action->float_thresholds[*r_subaction_idx_other], + action->axis_flags[*r_subaction_idx_other])) { + bimanual = true; + } + break; + } + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + + if (bimanual) { + *r_aim_pose_other = wm_xr_session_controller_aim_pose_find( + session_state, action->subaction_paths[*r_subaction_idx_other]); + } + + return bimanual; +} + +static wmXrActionData *wm_xr_session_event_create(const char *action_set_name, + const wmXrAction *action, + const GHOST_XrPose *controller_aim_pose, + const GHOST_XrPose *controller_aim_pose_other, + unsigned int subaction_idx, + unsigned int subaction_idx_other, + bool bimanual) +{ + wmXrActionData *data = MEM_callocN(sizeof(wmXrActionData), __func__); + strcpy(data->action_set, action_set_name); + strcpy(data->action, action->name); + data->type = action->type; + + switch (action->type) { + case XR_BOOLEAN_INPUT: + data->state[0] = ((bool *)action->states)[subaction_idx] ? 1.0f : 0.0f; + if (bimanual) { + data->state_other[0] = ((bool *)action->states)[subaction_idx_other] ? 1.0f : 0.0f; + } + break; + case XR_FLOAT_INPUT: + data->state[0] = ((float *)action->states)[subaction_idx]; + if (bimanual) { + data->state_other[0] = ((float *)action->states)[subaction_idx_other]; + } + data->float_threshold = action->float_thresholds[subaction_idx]; + break; + case XR_VECTOR2F_INPUT: + copy_v2_v2(data->state, ((float(*)[2])action->states)[subaction_idx]); + if (bimanual) { + copy_v2_v2(data->state_other, ((float(*)[2])action->states)[subaction_idx_other]); + } + data->float_threshold = action->float_thresholds[subaction_idx]; + break; + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + + if (controller_aim_pose) { + copy_v3_v3(data->controller_loc, controller_aim_pose->position); + copy_qt_qt(data->controller_rot, controller_aim_pose->orientation_quat); + + if (bimanual && controller_aim_pose_other) { + copy_v3_v3(data->controller_loc_other, controller_aim_pose_other->position); + copy_qt_qt(data->controller_rot_other, controller_aim_pose_other->orientation_quat); + } + else { + data->controller_rot_other[0] = 1.0f; + } + } + else { + data->controller_rot[0] = 1.0f; + data->controller_rot_other[0] = 1.0f; + } + + data->ot = action->ot; + data->op_properties = action->op_properties; + + data->bimanual = bimanual; + + return data; +} + +/* Dispatch events to window queues. */ +static void wm_xr_session_events_dispatch(wmXrData *xr, + const XrSessionSettings *settings, + GHOST_XrContextHandle xr_context, + wmXrActionSet *action_set, + wmXrSessionState *session_state, + wmWindow *win) +{ + const char *action_set_name = action_set->name; + + const unsigned int count = GHOST_XrGetActionCount(xr_context, action_set_name); + if (count < 1) { + return; + } + + const int64_t time_now = (int64_t)(PIL_check_seconds_timer() * 1000); + + ListBase *active_modal_actions = &action_set->active_modal_actions; + ListBase *active_haptic_actions = &action_set->active_haptic_actions; + + wmXrAction **actions = MEM_calloc_arrayN(count, sizeof(*actions), __func__); + + GHOST_XrGetActionCustomdataArray(xr_context, action_set_name, (void **)actions); + + /* Check haptic action timers. */ + wm_xr_session_haptic_timers_check(active_haptic_actions, time_now); + + for (unsigned int action_idx = 0; action_idx < count; ++action_idx) { + wmXrAction *action = actions[action_idx]; + if (action && action->ot) { + const bool modal = action->ot->modal; + const bool haptic = (GHOST_XrGetActionCustomdata( + xr_context, action_set_name, action->haptic_name) != NULL); + + for (unsigned int subaction_idx = 0; subaction_idx < action->count_subaction_paths; + ++subaction_idx) { + short val = KM_NOTHING; + + /* Interpret action states (update modal/haptic action lists, apply haptics, etc). */ + wm_xr_session_action_states_interpret(xr, + action_set_name, + action, + subaction_idx, + active_modal_actions, + active_haptic_actions, + time_now, + modal, + haptic, + &val); + + const bool is_active_modal_action = wm_xr_session_modal_action_test( + active_modal_actions, action, NULL); + const bool is_active_modal_subaction = (!action->active_modal_path || + (action->subaction_paths[subaction_idx] == + action->active_modal_path)); + + if ((val != KM_NOTHING) && + (!modal || (is_active_modal_action && is_active_modal_subaction))) { + const GHOST_XrPose *aim_pose = wm_xr_session_controller_aim_pose_find( + session_state, action->subaction_paths[subaction_idx]); + const GHOST_XrPose *aim_pose_other = NULL; + unsigned int subaction_idx_other = 0; + + /* Test for bimanual interaction. */ + const bool bimanual = wm_xr_session_action_test_bimanual( + session_state, action, subaction_idx, &subaction_idx_other, &aim_pose_other); + + wmXrActionData *actiondata = wm_xr_session_event_create(action_set_name, + action, + aim_pose, + aim_pose_other, + subaction_idx, + subaction_idx_other, + bimanual); + wm_event_add_xrevent(win, actiondata, val); + } + } + } + } + + MEM_freeN(actions); +} + void wm_xr_session_actions_update(wmWindowManager *wm) { wmXrData *xr = &wm->xr; @@ -593,8 +1100,7 @@ void wm_xr_session_actions_update(wmWindowManager *wm) xr->runtime->area = ED_area_offscreen_create(win, SPACE_VIEW3D); } - /* Implemented in D10944. */ - // wm_xr_session_events_dispatch(xr, settings, xr_context, active_action_set, state, win); + wm_xr_session_events_dispatch(xr, settings, xr_context, active_action_set, state, win); } } } From 8f66f4031855d1414f1d7475ca7dbc551fc8b0bc Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 12 Oct 2021 08:41:12 +0200 Subject: [PATCH 0692/1500] Silence compilation warning in wm_xr_session. --- source/blender/windowmanager/xr/intern/wm_xr_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 2a59a48f152..4c37253f457 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -985,7 +985,7 @@ static wmXrActionData *wm_xr_session_event_create(const char *action_set_name, /* Dispatch events to window queues. */ static void wm_xr_session_events_dispatch(wmXrData *xr, - const XrSessionSettings *settings, + const XrSessionSettings *UNUSED(settings), GHOST_XrContextHandle xr_context, wmXrActionSet *action_set, wmXrSessionState *session_state, From 70fd6a313e70ead89f94090d1de3d90032f778d5 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 12 Oct 2021 08:42:08 +0200 Subject: [PATCH 0693/1500] GHOST: Add option to request (user) cache folder. Introduces `BKE_appdir_folder_caches` to get the folder that can be used to store caches. On different OS's different folders are used. - Linux: `~/.cache/blender/`. - MacOS: `Library/Caches/Blender/`. - Windows: `(%USERPROFILE%\AppData\Local)\Blender Foundation\Blender\Cache\`. Reviewed By: Severin Differential Revision: https://developer.blender.org/D12822 --- intern/ghost/GHOST_Types.h | 1 + intern/ghost/intern/GHOST_SystemPathsCocoa.mm | 3 ++ intern/ghost/intern/GHOST_SystemPathsUnix.cpp | 17 ++++++++++ .../ghost/intern/GHOST_SystemPathsWin32.cpp | 3 ++ source/blender/blenkernel/BKE_appdir.h | 1 + source/blender/blenkernel/intern/appdir.c | 32 +++++++++++++++++++ 6 files changed, 57 insertions(+) diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 221fa140f70..898ee451baf 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -569,6 +569,7 @@ typedef enum { GHOST_kUserSpecialDirMusic, GHOST_kUserSpecialDirPictures, GHOST_kUserSpecialDirVideos, + GHOST_kUserSpecialDirCaches, /* Can be extended as needed. */ } GHOST_TUserSpecialDirTypes; diff --git a/intern/ghost/intern/GHOST_SystemPathsCocoa.mm b/intern/ghost/intern/GHOST_SystemPathsCocoa.mm index 3b29d5106f6..43ce0bb0533 100644 --- a/intern/ghost/intern/GHOST_SystemPathsCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemPathsCocoa.mm @@ -117,6 +117,9 @@ const char *GHOST_SystemPathsCocoa::getUserSpecialDir(GHOST_TUserSpecialDirTypes case GHOST_kUserSpecialDirVideos: ns_directory = NSMoviesDirectory; break; + case GHOST_kUserSpecialDirCaches: + ns_directory = NSCachesDirectory; + break; default: GHOST_ASSERT( false, diff --git a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp index 8bc2ff9227d..72396782752 100644 --- a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp +++ b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp @@ -114,6 +114,7 @@ const char *GHOST_SystemPathsUnix::getUserDir(int version, const char *versionst const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const { const char *type_str; + std::string add_path = ""; switch (type) { case GHOST_kUserSpecialDirDesktop: @@ -134,6 +135,18 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes case GHOST_kUserSpecialDirVideos: type_str = "VIDEOS"; break; + case GHOST_kUserSpecialDirCaches: { + const char *cache_dir = getenv("XDG_CACHE_HOME"); + if (cache_dir) { + return cache_dir; + } + /* Fallback to ~home/.cache/. + * When invoking `xdg-user-dir` without parameters the user folder + * will be read. `.cache` will be appended. */ + type_str = ""; + add_path = ".cache"; + break; + } default: GHOST_ASSERT( false, @@ -163,6 +176,10 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes return NULL; } + if (!add_path.empty()) { + path_stream << '/' << add_path; + } + path = path_stream.str(); return path[0] ? path.c_str() : NULL; } diff --git a/intern/ghost/intern/GHOST_SystemPathsWin32.cpp b/intern/ghost/intern/GHOST_SystemPathsWin32.cpp index 580cfcac7ba..bced552921f 100644 --- a/intern/ghost/intern/GHOST_SystemPathsWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemPathsWin32.cpp @@ -100,6 +100,9 @@ const char *GHOST_SystemPathsWin32::getUserSpecialDir(GHOST_TUserSpecialDirTypes case GHOST_kUserSpecialDirVideos: folderid = FOLDERID_Videos; break; + case GHOST_kUserSpecialDirCaches: + folderid = FOLDERID_LocalAppData; + break; default: GHOST_ASSERT( false, diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h index 0f00d391973..07132201e87 100644 --- a/source/blender/blenkernel/BKE_appdir.h +++ b/source/blender/blenkernel/BKE_appdir.h @@ -35,6 +35,7 @@ void BKE_appdir_exit(void); const char *BKE_appdir_folder_default(void); const char *BKE_appdir_folder_home(void); bool BKE_appdir_folder_documents(char *dir); +bool BKE_appdir_folder_caches(char *r_path, size_t path_len); bool BKE_appdir_folder_id_ex(const int folder_id, const char *subfolder, char *path, diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index eae331fc7d1..0bc8d17fdaf 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -217,6 +217,38 @@ bool BKE_appdir_folder_documents(char *dir) return true; } +/** + * Get the user's cache directory, i.e. $HOME/.cache/blender/ on Linux, + * %USERPROFILE%\AppData\Local\blender\ on Windows. + * + * \returns True if the path is valid. It doesn't create or checks format + * if the `blender` folder exists. It does check if the parent of the + * path exists. + */ +bool BKE_appdir_folder_caches(char *r_path, const size_t path_len) +{ + r_path[0] = '\0'; + + const char *caches_root_path = GHOST_getUserSpecialDir(GHOST_kUserSpecialDirCaches); + if (caches_root_path == NULL || !BLI_is_dir(caches_root_path)) { + caches_root_path = BKE_tempdir_base(); + } + if (caches_root_path == NULL || !BLI_is_dir(caches_root_path)) { + return false; + } + +#ifdef WIN32 + BLI_path_join( + r_path, path_len, caches_root_path, "Blender Foundation", "Blender", "Cache", SEP_STR, NULL); +#elif __APPLE__ + BLI_path_join(r_path, path_len, caches_root_path, "Blender", SEP_STR, NULL); +#else /* __linux__ */ + BLI_path_join(r_path, path_len, caches_root_path, "blender", SEP_STR, NULL); +#endif + + return true; +} + /** * Gets a good default directory for fonts. */ From a91c6f1804e9ce07e9f751bc1c3c05751da7607a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 12 Oct 2021 17:52:33 +1100 Subject: [PATCH 0694/1500] Cleanup: quiet undefined warning --- source/blender/blenkernel/intern/appdir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 0bc8d17fdaf..5779c873222 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -240,7 +240,7 @@ bool BKE_appdir_folder_caches(char *r_path, const size_t path_len) #ifdef WIN32 BLI_path_join( r_path, path_len, caches_root_path, "Blender Foundation", "Blender", "Cache", SEP_STR, NULL); -#elif __APPLE__ +#elif defined(__APPLE__) BLI_path_join(r_path, path_len, caches_root_path, "Blender", SEP_STR, NULL); #else /* __linux__ */ BLI_path_join(r_path, path_len, caches_root_path, "blender", SEP_STR, NULL); From c1c6c11ca6c5f4cd775b787910fe69119b054af2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 12 Oct 2021 17:52:35 +1100 Subject: [PATCH 0695/1500] Cleanup: spelling in comments --- intern/cycles/bvh/bvh.h | 2 +- intern/cycles/kernel/bvh/bvh_util.h | 2 +- intern/cycles/kernel/closure/bsdf.h | 2 +- .../kernel/closure/bsdf_principled_diffuse.h | 4 +- intern/cycles/kernel/closure/bssrdf.h | 6 +- .../integrator/integrator_shade_volume.h | 2 +- .../integrator_subsurface_random_walk.h | 12 +-- .../kernel/shaders/node_image_texture.osl | 4 +- intern/cycles/kernel/svm/svm_image.h | 4 +- source/blender/blenkernel/BKE_bvhutils.h | 8 +- source/blender/blenkernel/intern/bvhutils.cc | 88 +++++++++++++------ .../blender/blenkernel/intern/curve_deform.c | 4 +- source/blender/blenkernel/intern/subdiv_ccg.c | 2 +- source/blender/editors/armature/pose_slide.c | 4 +- .../interface/interface_button_group.c | 2 +- source/blender/editors/render/render_view.c | 12 +-- .../blender/editors/space_graph/graph_edit.c | 2 +- .../blender/editors/space_image/image_edit.c | 2 +- .../blender/editors/space_node/node_draw.cc | 2 +- .../blender_interface/FRS_freestyle.cpp | 4 +- source/blender/imbuf/intern/imageprocess.c | 9 +- source/blender/imbuf/intern/iris.c | 2 +- .../blender/io/collada/ArmatureImporter.cpp | 2 +- 23 files changed, 105 insertions(+), 76 deletions(-) diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h index d9e2ad9526c..b222dfb14ed 100644 --- a/intern/cycles/bvh/bvh.h +++ b/intern/cycles/bvh/bvh.h @@ -52,7 +52,7 @@ struct PackedBVH { array object_node; /* primitive type - triangle or strand */ array prim_type; - /* visibility visibilitys for primitives */ + /* Visibility visibilities for primitives. */ array prim_visibility; /* mapping from BVH primitive index to true primitive index, as primitives * may be duplicated due to spatial splits. -1 for instances. */ diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index e16da9755f2..d143fe4aeab 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -72,7 +72,7 @@ ccl_device_inline float3 ray_offset(float3 P, float3 Ng) } #if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__)) -/* ToDo: Move to another file? */ +/* TODO: Move to another file? */ ccl_device int intersections_compare(const void *a, const void *b) { const Intersection *isect_a = (const Intersection *)a; diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index 87aa6339f80..bb80b9636bb 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -654,7 +654,7 @@ ccl_device_inline ccl_device void bsdf_blur(const KernelGlobals *kg, ShaderClosure *sc, float roughness) { - /* ToDo: do we want to blur volume closures? */ + /* TODO: do we want to blur volume closures? */ #ifdef __SVM__ switch (sc->type) { case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index 04963ca1dc5..52a37eafd9f 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -91,7 +91,7 @@ ccl_device float3 bsdf_principled_diffuse_compute_brdf( return make_float3(value, value, value); } -/* Compute Fresnel at entry point, to be compbined with PRINCIPLED_DIFFUSE_LAMBERT_EXIT +/* Compute Fresnel at entry point, to be combined with #PRINCIPLED_DIFFUSE_LAMBERT_EXIT * at the exit point to get the complete BSDF. */ ccl_device_inline float bsdf_principled_diffuse_compute_entry_fresnel(const float NdotV) { @@ -99,7 +99,7 @@ ccl_device_inline float bsdf_principled_diffuse_compute_entry_fresnel(const floa return (1.0f - 0.5f * FV); } -/* Ad-hoc weight adjusment to avoid retro-reflection taking away half the +/* Ad-hoc weight adjustment to avoid retro-reflection taking away half the * samples from BSSRDF. */ ccl_device_inline float bsdf_principled_diffuse_retro_reflection_sample_weight( PrincipledDiffuseBsdf *bsdf, const float3 I) diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index d2f8af7910c..07415c53ec5 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -136,10 +136,10 @@ ccl_device float bssrdf_burley_eval(const float d, float r) /* Burley reflectance profile, equation (3). * * NOTES: - * - Surface albedo is already included into sc->weight, no need to + * - Surface albedo is already included into `sc->weight`, no need to * multiply by this term here. * - This is normalized diffuse model, so the equation is multiplied - * by 2*pi, which also matches cdf(). + * by `2*pi`, which also matches `cdf()`. */ float exp_r_3_d = expf(-r / (3.0f * d)); float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; @@ -288,7 +288,7 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type, co bsdf->roughness = bssrdf->roughness; flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_RETRO_REFLECTION); - /* Ad-hoc weight adjusment to avoid retro-reflection taking away half the + /* Ad-hoc weight adjustment to avoid retro-reflection taking away half the * samples from BSSRDF. */ bsdf->sample_weight *= bsdf_principled_diffuse_retro_reflection_sample_weight(bsdf, sd->I); } diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index dac3efb3996..aa4c652c037 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -232,7 +232,7 @@ ccl_device void volume_shadow_heterogeneous(INTEGRATOR_STATE_ARGS, * because `exp(a)*exp(b) = exp(a+b)`, also do a quick #VOLUME_THROUGHPUT_EPSILON * check then. */ sum += (-sigma_t * dt); - if ((i & 0x07) == 0) { /* ToDo: Other interval? */ + if ((i & 0x07) == 0) { /* TODO: Other interval? */ tp = *throughput * exp3(sum); /* stop if nearly all light is blocked */ diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h index cffc53bf270..d4935b0ce4a 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h @@ -253,7 +253,7 @@ ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, bool have_opposite_interface = false; float opposite_distance = 0.0f; - /* Todo: Disable for alpha>0.999 or so? */ + /* TODO: Disable for `alpha > 0.999` or so? */ /* Our heuristic, a compromise between guiding and classic. */ const float guided_fraction = 1.0f - fmaxf(0.5f, powf(fabsf(anisotropy), 0.125f)); @@ -295,7 +295,7 @@ ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, float sample_sigma_t = volume_channel_get(sigma_t, channel); float randt = path_state_rng_1D(kg, &rng_state, PRNG_SCATTER_DISTANCE); - /* We need the result of the raycast to compute the full guided PDF, so just remember the + /* We need the result of the ray-cast to compute the full guided PDF, so just remember the * relevant terms to avoid recomputing them later. */ float backward_fraction = 0.0f; float forward_pdf_factor = 0.0f; @@ -330,7 +330,7 @@ ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, float hg_pdf; if (guided) { cos_theta = sample_phase_dwivedi(diffusion_length, phase_log, scatter_u); - /* The backwards guiding distribution is just mirrored along sd->N, so swapping the + /* The backwards guiding distribution is just mirrored along `sd->N`, so swapping the * sign here is enough to sample from that instead. */ if (guide_backward) { cos_theta = -cos_theta; @@ -358,7 +358,7 @@ ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, /* Prepare distance sampling. * For the backwards case, this also needs the sign swapped since now directions against - * sd->N (and therefore with negative cos_theta) are preferred. */ + * `sd->N` (and therefore with negative cos_theta) are preferred. */ forward_stretching = (1.0f - cos_theta / diffusion_length); backward_stretching = (1.0f + cos_theta / diffusion_length); if (guided) { @@ -369,10 +369,10 @@ ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, /* Sample direction along ray. */ float t = -logf(1.0f - randt) / sample_sigma_t; - /* On the first bounce, we use the raycast to check if the opposite side is nearby. + /* On the first bounce, we use the ray-cast to check if the opposite side is nearby. * If yes, we will later use backwards guided sampling in order to have a decent * chance of connecting to it. - * Todo: Maybe use less than 10 times the mean free path? */ + * TODO: Maybe use less than 10 times the mean free path? */ ray.t = (bounce == 0) ? max(t, 10.0f / (min3(sigma_t))) : t; scene_intersect_local(kg, &ray, &ss_isect, object, NULL, 1); hit = (ss_isect.num_hits > 0); diff --git a/intern/cycles/kernel/shaders/node_image_texture.osl b/intern/cycles/kernel/shaders/node_image_texture.osl index 9e2ef84c872..56fcc47a011 100644 --- a/intern/cycles/kernel/shaders/node_image_texture.osl +++ b/intern/cycles/kernel/shaders/node_image_texture.osl @@ -143,10 +143,10 @@ shader node_image_texture(int use_mapping = 0, * in between we blend between two textures, and in the middle we a blend * between three textures. * - * the Nxyz values are the barycentric coordinates in an equilateral + * the `Nxyz` values are the barycentric coordinates in an equilateral * triangle, which in case of blending, in the middle has a smaller * equilateral triangle where 3 textures blend. this divides things into - * 7 zones, with an if () test for each zone */ + * 7 zones, with an if () test for each zone. */ vector weight = vector(0.0, 0.0, 0.0); float blend = projection_blend; diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index a344f36977a..ce70109392b 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -142,10 +142,10 @@ ccl_device_noinline void svm_node_tex_image_box(const KernelGlobals *kg, * in between we blend between two textures, and in the middle we a blend * between three textures. * - * the Nxyz values are the barycentric coordinates in an equilateral + * The `Nxyz` values are the barycentric coordinates in an equilateral * triangle, which in case of blending, in the middle has a smaller * equilateral triangle where 3 textures blend. this divides things into - * 7 zones, with an if() test for each zone */ + * 7 zones, with an if() test for each zone. */ float3 weight = make_float3(0.0f, 0.0f, 0.0f); float blend = __int_as_float(node.w); diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index 06be8ec80fc..bb95985ef4c 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -48,7 +48,7 @@ struct BVHCache; typedef struct BVHTreeFromEditMesh { struct BVHTree *tree; - /* default callbacks to bvh nearest and raycast */ + /** Default callbacks to bvh nearest and ray-cast. */ BVHTree_NearestPointCallback nearest_callback; BVHTree_RayCastCallback raycast_callback; @@ -60,18 +60,18 @@ typedef struct BVHTreeFromEditMesh { } BVHTreeFromEditMesh; /** - * Struct that stores basic information about a BVHTree built from a mesh. + * Struct that stores basic information about a #BVHTree built from a mesh. */ typedef struct BVHTreeFromMesh { struct BVHTree *tree; - /* default callbacks to bvh nearest and raycast */ + /** Default callbacks to bvh nearest and ray-cast. */ BVHTree_NearestPointCallback nearest_callback; BVHTree_RayCastCallback raycast_callback; /* Vertex array, so that callbacks have instant access to data. */ const struct MVert *vert; - const struct MEdge *edge; /* only used for BVHTreeFromMeshEdges */ + const struct MEdge *edge; /* only used for #BVHTreeFromMeshEdges */ const struct MFace *face; const struct MLoop *loop; const struct MLoopTri *looptri; diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 707201207d9..1f92f834972 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -161,9 +161,11 @@ void bvhcache_free(BVHCache *bvh_cache) MEM_freeN(bvh_cache); } -/* BVH tree balancing inside a mutex lock must be run in isolation. Balancing +/** + * BVH-tree balancing inside a mutex lock must be run in isolation. Balancing * is multithreaded, and we do not want the current thread to start another task - * that may involve acquiring the same mutex lock that it is waiting for. */ + * that may involve acquiring the same mutex lock that it is waiting for. + */ static void bvhtree_balance_isolated(void *userdata) { BLI_bvhtree_balance((BVHTree *)userdata); @@ -233,8 +235,12 @@ float bvhtree_sphereray_tri_intersection(const BVHTreeRay *ray, * BVH from meshes callbacks */ -/* Callback to bvh tree nearest point. The tree must have been built using bvhtree_from_mesh_faces. - * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ +/** + * Callback to BVH-tree nearest point. + * The tree must have been built using #bvhtree_from_mesh_faces. + * + * \param userdata: Must be a #BVHMeshCallbackUserdata built from the same mesh as the tree. + */ static void mesh_faces_nearest_point(void *userdata, int index, const float co[3], @@ -325,8 +331,12 @@ static void editmesh_looptri_nearest_point(void *userdata, } } -/* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_faces. - * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ +/** + * Callback to BVH-tree ray-cast. + * The tree must have been built using bvhtree_from_mesh_faces. + * + * \param userdata: Must be a #BVHMeshCallbackUserdata built from the same mesh as the tree. + */ static void mesh_faces_spherecast(void *userdata, int index, const BVHTreeRay *ray, @@ -430,8 +440,12 @@ static void editmesh_looptri_spherecast(void *userdata, } } -/* Callback to bvh tree nearest point. The tree must have been built using bvhtree_from_mesh_edges. - * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ +/** + * Callback to BVH-tree nearest point. + * The tree must have been built using #bvhtree_from_mesh_edges. + * + * \param userdata: Must be a #BVHMeshCallbackUserdata built from the same mesh as the tree. + */ static void mesh_edges_nearest_point(void *userdata, int index, const float co[3], @@ -491,8 +505,12 @@ static void editmesh_verts_spherecast(void *userdata, mesh_verts_spherecast_do(index, eve->co, ray, hit); } -/* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_verts. - * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ +/** + * Callback to BVH-tree ray-cast. + * The tree must have been built using bvhtree_from_mesh_verts. + * + * \param userdata: Must be a #BVHMeshCallbackUserdata built from the same mesh as the tree. + */ static void mesh_verts_spherecast(void *userdata, int index, const BVHTreeRay *ray, @@ -504,8 +522,12 @@ static void mesh_verts_spherecast(void *userdata, mesh_verts_spherecast_do(index, v, ray, hit); } -/* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_edges. - * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ +/** + * Callback to BVH-tree ray-cast. + * The tree must have been built using bvhtree_from_mesh_edges. + * + * \param userdata: Must be a #BVHMeshCallbackUserdata built from the same mesh as the tree. + */ static void mesh_edges_spherecast(void *userdata, int index, const BVHTreeRay *ray, @@ -647,7 +669,9 @@ static void bvhtree_from_mesh_verts_setup_data(BVHTreeFromMesh *data, data->vert_allocated = vert_allocated; } -/* Builds a bvh tree where nodes are the vertices of the given em */ +/** + * Builds a BVH-tree where nodes are the vertices of the given `em`. + */ BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data, BMEditMesh *em, const BLI_bitmap *verts_mask, @@ -704,10 +728,10 @@ BVHTree *bvhtree_from_editmesh_verts( } /** - * Builds a bvh tree where nodes are the given vertices (NOTE: does not copy given `vert`!). + * Builds a BVH-tree where nodes are the given vertices (NOTE: does not copy given `vert`!). * \param vert_allocated: if true, vert freeing will be done when freeing data. - * \param verts_mask: if not null, true elements give which vert to add to BVH tree. - * \param verts_num_active: if >= 0, number of active verts to add to BVH tree + * \param verts_mask: if not null, true elements give which vert to add to BVH-tree. + * \param verts_num_active: if >= 0, number of active verts to add to BVH-tree * (else will be computed from mask). */ BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data, @@ -860,7 +884,9 @@ static void bvhtree_from_mesh_edges_setup_data(BVHTreeFromMesh *data, data->edge_allocated = edge_allocated; } -/* Builds a bvh tree where nodes are the edges of the given em */ +/** + * Builds a BVH-tree where nodes are the edges of the given `em`. + */ BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data, BMEditMesh *em, const BLI_bitmap *edges_mask, @@ -916,11 +942,11 @@ BVHTree *bvhtree_from_editmesh_edges( } /** - * Builds a bvh tree where nodes are the given edges . + * Builds a BVH-tree where nodes are the given edges. * \param vert, vert_allocated: if true, elem freeing will be done when freeing data. * \param edge, edge_allocated: if true, elem freeing will be done when freeing data. - * \param edges_mask: if not null, true elements give which vert to add to BVH tree. - * \param edges_num_active: if >= 0, number of active edges to add to BVH tree + * \param edges_mask: if not null, true elements give which vert to add to BVH-tree. + * \param edges_num_active: if >= 0, number of active edges to add to BVH-tree * (else will be computed from mask). */ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data, @@ -1050,12 +1076,12 @@ static void bvhtree_from_mesh_faces_setup_data(BVHTreeFromMesh *data, } /** - * Builds a bvh tree where nodes are the given tessellated faces + * Builds a BVH-tree where nodes are the given tessellated faces * (NOTE: does not copy given mfaces!). * \param vert_allocated: if true, vert freeing will be done when freeing data. * \param face_allocated: if true, face freeing will be done when freeing data. - * \param faces_mask: if not null, true elements give which faces to add to BVH tree. - * \param faces_num_active: if >= 0, number of active faces to add to BVH tree + * \param faces_mask: if not null, true elements give which faces to add to BVH-tree. + * \param faces_num_active: if >= 0, number of active faces to add to BVH-tree * (else will be computed from mask). */ BVHTree *bvhtree_from_mesh_faces_ex(BVHTreeFromMesh *data, @@ -1135,7 +1161,7 @@ static BVHTree *bvhtree_from_editmesh_looptri_create_tree(float epsilon, if (tree) { const BMLoop *(*looptris)[3] = (const BMLoop *(*)[3])em->looptris; - /* Insert BMesh-tessellation triangles into the bvh tree, unless they are hidden + /* Insert BMesh-tessellation triangles into the BVH-tree, unless they are hidden * and/or selected. Even if the faces themselves are not selected for the snapped * transform, having a vertex selected means the face (and thus it's tessellated * triangles) will be moving and will not be a good snap targets. */ @@ -1232,7 +1258,7 @@ static void bvhtree_from_mesh_looptri_setup_data(BVHTreeFromMesh *data, } /** - * Builds a bvh tree where nodes are the looptri faces of the given bm + * Builds a BVH-tree where nodes are the `looptri` faces of the given `bm`. */ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data, BMEditMesh *em, @@ -1684,7 +1710,7 @@ BVHTree *BKE_bvhtree_from_editmesh_get(BVHTreeFromEditMesh *data, mesh_eval_mutex); } else { - /* Setup BVHTreeFromMesh */ + /* Setup #BVHTreeFromMesh */ data->nearest_callback = nullptr; /* TODO */ data->raycast_callback = nullptr; /* TODO */ } @@ -1704,7 +1730,7 @@ BVHTree *BKE_bvhtree_from_editmesh_get(BVHTreeFromEditMesh *data, mesh_eval_mutex); } else { - /* Setup BVHTreeFromMesh */ + /* Setup #BVHTreeFromMesh */ data->nearest_callback = editmesh_looptri_nearest_point; data->raycast_callback = editmesh_looptri_spherecast; } @@ -1741,7 +1767,9 @@ BVHTree *BKE_bvhtree_from_editmesh_get(BVHTreeFromEditMesh *data, /** \} */ -/* Frees data allocated by a call to bvhtree_from_editmesh_*. */ +/** + * Frees data allocated by a call to `bvhtree_from_editmesh_*`. + */ void free_bvhtree_from_editmesh(struct BVHTreeFromEditMesh *data) { if (data->tree) { @@ -1752,7 +1780,9 @@ void free_bvhtree_from_editmesh(struct BVHTreeFromEditMesh *data) } } -/* Frees data allocated by a call to bvhtree_from_mesh_*. */ +/** + * Frees data allocated by a call to `bvhtree_from_mesh_*`. + */ void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data) { if (data->tree && !data->cached) { diff --git a/source/blender/blenkernel/intern/curve_deform.c b/source/blender/blenkernel/intern/curve_deform.c index 28b7c2dfba0..b8b8506d681 100644 --- a/source/blender/blenkernel/intern/curve_deform.c +++ b/source/blender/blenkernel/intern/curve_deform.c @@ -311,7 +311,7 @@ static void curve_deform_coords_impl(const Object *ob_curve, } \ ((void)0) - /* already in 'cd.curvespace', prev for loop */ + /* Already in 'cd.curvespace', previous for loop. */ #define DEFORM_OP_CLAMPED(dvert) \ { \ const float weight = invert_vgroup ? 1.0f - BKE_defvert_find_weight(dvert, defgrp_index) : \ @@ -369,7 +369,7 @@ static void curve_deform_coords_impl(const Object *ob_curve, } for (a = 0; a < vert_coords_len; a++) { - /* already in 'cd.curvespace', prev for loop */ + /* Already in 'cd.curvespace', previous for loop. */ calc_curve_deform(ob_curve, vert_coords[a], defaxis, &cd, NULL); mul_m4_v3(cd.objectspace, vert_coords[a]); } diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index 95f51a72b70..07c4e8c2316 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -1797,7 +1797,7 @@ static void neighbor_coords_edge_get(const SubdivCCG *subdiv_ccg, r_neighbors->coords[i + 2] = coord_step_inside_from_boundary(subdiv_ccg, &grid_coord); if (grid_coord.grid_index == coord->grid_index) { - /* Prev and next along the edge for the current grid. */ + /* Previous and next along the edge for the current grid. */ r_neighbors->coords[0] = boundary_coords[prev_point_index]; r_neighbors->coords[1] = boundary_coords[next_point_index]; } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index ed8a35f779b..1079985346c 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -1042,7 +1042,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent * const ActKeyColumn *nk = ED_keylist_find_next(pso->keylist, cframe); /* New set the frames. */ - /* Prev frame. */ + /* Previous frame. */ pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1); RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); /* Next frame. */ @@ -1051,7 +1051,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent * } else { /* Current frame itself is a keyframe, so just take keyframes on either side. */ - /* Prev frame. */ + /* Previous frame. */ pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1); RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); /* Next frame. */ diff --git a/source/blender/editors/interface/interface_button_group.c b/source/blender/editors/interface/interface_button_group.c index 4e7da4ada33..7054498d469 100644 --- a/source/blender/editors/interface/interface_button_group.c +++ b/source/blender/editors/interface/interface_button_group.c @@ -57,7 +57,7 @@ void ui_button_group_add_but(uiBlock *block, uiBut *but) uiButtonGroup *current_button_group = block->button_groups.last; /* We can't use the button directly because adding it to - * this list would mess with its prev and next pointers. */ + * this list would mess with its `prev` and `next` pointers. */ LinkData *button_link = BLI_genericNodeN(but); BLI_addtail(¤t_button_group->buttons, button_link); } diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index 97ecb67d6cc..0e5acc0a108 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -183,8 +183,8 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) else if (U.render_display_type == USER_RENDER_DISPLAY_SCREEN) { area = CTX_wm_area(C); - /* if the active screen is already in fullscreen mode, skip this and - * unset the area, so that the fullscreen area is just changed later */ + /* If the active screen is already in full-screen mode, skip this and + * unset the area, so that the full-screen area is just changed later. */ if (area && area->full) { area = NULL; } @@ -216,10 +216,10 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) ED_area_newspace(C, area, SPACE_IMAGE, true); sima = area->spacedata.first; - /* makes ESC go back to prev space */ + /* Makes "Escape" go back to previous space. */ sima->flag |= SI_PREVSPACE; - /* we already had a fullscreen here -> mark new space as a stacked fullscreen */ + /* We already had a full-screen here -> mark new space as a stacked full-screen. */ if (area->full) { area->flag |= AREA_FLAG_STACKED_FULLSCREEN; } @@ -231,7 +231,7 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) // XXX newspace(area, SPACE_IMAGE); sima = area->spacedata.first; - /* makes ESC go back to prev space */ + /* Makes "Escape" go back to previous space. */ sima->flag |= SI_PREVSPACE; } } @@ -275,7 +275,7 @@ static int render_view_cancel_exec(bContext *C, wmOperator *UNUSED(op)) ScrArea *area = CTX_wm_area(C); SpaceImage *sima = area->spacedata.first; - /* ensure image editor fullscreen and area fullscreen states are in sync */ + /* ensure image editor full-screen and area full-screen states are in sync */ if ((sima->flag & SI_FULLWINDOW) && !area->full) { sima->flag &= ~SI_FULLWINDOW; } diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 872b17372de..0b74c801399 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -1880,7 +1880,7 @@ static bool euler_filter_single_channel(FCurve *fcu) return false; } - /* Prev follows bezt, bezt = "current" point to be fixed. */ + /* `prev` follows bezt, bezt = "current" point to be fixed. */ /* Our method depends on determining a "difference" from the previous vert. */ bool is_modified = false; for (i = 1, prev = fcu->bezt, bezt = fcu->bezt + 1; i < fcu->totvert; i++, prev = bezt++) { diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 9081f0dfcf3..594baf67aaf 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -378,7 +378,7 @@ void ED_image_point_pos__reverse(SpaceImage *sima, } /** - * This is more a user-level functionality, for going to next/prev used slot, + * This is more a user-level functionality, for going to `next/prev` used slot, * Stepping onto the last unused slot too. */ bool ED_image_slot_cycle(struct Image *image, int direction) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 4332302159c..97e5bdd93c1 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2177,7 +2177,7 @@ static void draw_nodetree(const bContext *C, } /** - * Make the background slightly brighter to indicate that users are inside a nodegroup. + * Make the background slightly brighter to indicate that users are inside a node-group. */ static void draw_background_color(const SpaceNode *snode) { diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index 405deaf00b0..b31f4fd2303 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -86,8 +86,8 @@ static void load_post_callback(struct Main * /*main*/, } static bCallbackFuncStore load_post_callback_funcstore = { - nullptr, - nullptr, /* next, prev */ + nullptr, /* next */ + nullptr, /* prev */ load_post_callback, /* func */ nullptr, /* arg */ 0 /* alloc */ diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 804c9c3eb89..e7ad6153cd2 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -20,11 +20,10 @@ /** \file * \ingroup imbuf * - * This file was moved here from the src/ directory. It is meant to - * deal with endianness. It resided in a general blending lib. The - * other functions were only used during rendering. This single - * function remained. It should probably move to imbuf/intern/util.c, - * but we'll keep it here for the time being. (nzc) + * This file was moved here from the `src/` directory. + * It is meant to deal with endianness. It resided in a general blending lib. + * The other functions were only used during rendering. This single function remained. + * It should probably move to `imbuf/intern/util.c`, but we'll keep it here for the time being. */ #include diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c index df516d2a5c4..6a7ad87d53d 100644 --- a/source/blender/imbuf/intern/iris.c +++ b/source/blender/imbuf/intern/iris.c @@ -274,7 +274,7 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors return NULL; } - /* Could pe part of the magic check above, + /* Could be part of the magic check above, * by convention this check only requests the size needed to read it's magic though. */ if (size < HEADER_SIZE) { return NULL; diff --git a/source/blender/io/collada/ArmatureImporter.cpp b/source/blender/io/collada/ArmatureImporter.cpp index f36b9aacd8b..72fb8820be5 100644 --- a/source/blender/io/collada/ArmatureImporter.cpp +++ b/source/blender/io/collada/ArmatureImporter.cpp @@ -955,7 +955,7 @@ void ArmatureImporter::make_shape_keys(bContext *C) COLLADAFW::UniqueIdArray &morphTargetIds = (*mc)->getMorphTargets(); COLLADAFW::FloatOrDoubleArray &morphWeights = (*mc)->getMorphWeights(); - /* Prereq: all the geometries must be imported and mesh objects must be made */ + /* Prerequisite: all the geometries must be imported and mesh objects must be made. */ Object *source_ob = this->mesh_importer->get_object_by_geom_uid((*mc)->getSource()); if (source_ob) { From fe958d7d993e849527839486ef37c6239e3c5f5c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 12 Oct 2021 17:52:36 +1100 Subject: [PATCH 0696/1500] Fix leak on exit when WITH_PYTHON_SAFETY is enabled The lead only occurred when Python references were leaking as well. --- source/blender/python/intern/bpy_rna.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 92499d3c0ff..8c83f611b5c 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -180,6 +180,15 @@ static PyObject *id_free_weakref_cb(PyObject *weakinfo_pair, PyObject *weakref); static PyMethodDef id_free_weakref_cb_def = { "id_free_weakref_cb", (PyCFunction)id_free_weakref_cb, METH_O, NULL}; +/** + * Only used when there are values left on exit (causing memory leaks). + */ +static void id_weakref_pool_free_value_fn(void *p) +{ + GHash *weakinfo_hash = p; + BLI_ghash_free(weakinfo_hash, NULL, NULL); +} + /* Adds a reference to the list, remember to decref. */ static GHash *id_weakref_pool_get(ID *id) { @@ -7633,7 +7642,7 @@ void BPY_rna_exit(void) printf("ID: %s\n", id->name); } } - BLI_ghash_free(id_weakref_pool, NULL, NULL); + BLI_ghash_free(id_weakref_pool, NULL, id_weakref_pool_free_value_fn); id_weakref_pool = NULL; #endif } From 7b0b050dd5a837f52eb0801918427e2854fa64f0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 12 Oct 2021 17:57:37 +1100 Subject: [PATCH 0697/1500] Tests: quiet warning for UIList class name --- tests/python/bl_pyapi_idprop_datablock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py index ca52c1b01fe..63070dc2cb3 100644 --- a/tests/python/bl_pyapi_idprop_datablock.py +++ b/tests/python/bl_pyapi_idprop_datablock.py @@ -308,14 +308,14 @@ def test_restrictions2(): bpy.types.Addon.a = bpy.props.PointerProperty(type=bpy.types.Object) - class TestUIList(UIList): + class TEST_UL_list(UIList): test: bpy.props.PointerProperty(type=bpy.types.Object) def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): layout.prop(item, "name", text="", emboss=False, icon_value=icon) check_crash(bpy.utils.register_class, TestPrefs) - check_crash(bpy.utils.register_class, TestUIList) + check_crash(bpy.utils.register_class, TEST_UL_list) bpy.utils.unregister_class(TestClassCollection) From 3d35d4a9e5ca39523d8ce13f8470e2b3ba59d958 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 12 Oct 2021 17:57:38 +1100 Subject: [PATCH 0698/1500] Tests: support running ID-property test without numpy --- tests/python/bl_pyapi_idprop.py | 75 ++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py index bdf625c1c2e..4cee39fafb0 100644 --- a/tests/python/bl_pyapi_idprop.py +++ b/tests/python/bl_pyapi_idprop.py @@ -4,9 +4,13 @@ import bpy import idprop import unittest -import numpy as np from array import array +# Run if `numpy` is installed. +try: + import numpy as np +except ImportError: + np = None class TestHelper: @@ -189,38 +193,6 @@ class TestIdPropertyGroupView(TestHelper, unittest.TestCase): class TestBufferProtocol(TestHelper, unittest.TestCase): - def test_int(self): - self.id["a"] = array("i", [1, 2, 3, 4, 5]) - a = np.frombuffer(self.id["a"], self.id["a"].typecode) - self.assertEqual(len(a), 5) - a[2] = 10 - self.assertEqual(self.id["a"].to_list(), [1, 2, 10, 4, 5]) - - def test_float(self): - self.id["a"] = array("f", [1.0, 2.0, 3.0, 4.0]) - a = np.frombuffer(self.id["a"], self.id["a"].typecode) - self.assertEqual(len(a), 4) - a[-1] = 10 - self.assertEqual(self.id["a"].to_list(), [1.0, 2.0, 3.0, 10.0]) - - def test_double(self): - self.id["a"] = array("d", [1.0, 2.0, 3.0, 4.0]) - a = np.frombuffer(self.id["a"], self.id["a"].typecode) - a[1] = 10 - self.assertEqual(self.id["a"].to_list(), [1.0, 10.0, 3.0, 4.0]) - - def test_full_update(self): - self.id["a"] = array("i", [1, 2, 3, 4, 5, 6]) - a = np.frombuffer(self.id["a"], self.id["a"].typecode) - a[:] = [10, 20, 30, 40, 50, 60] - self.assertEqual(self.id["a"].to_list(), [10, 20, 30, 40, 50, 60]) - - def test_partial_update(self): - self.id["a"] = array("i", [1, 2, 3, 4, 5, 6, 7, 8]) - a = np.frombuffer(self.id["a"], self.id["a"].typecode) - a[1:5] = [10, 20, 30, 40] - self.assertEqual(self.id["a"].to_list(), [1, 10, 20, 30, 40, 6, 7, 8]) - def test_copy(self): self.id["a"] = array("i", [1, 2, 3, 4, 5]) self.id["b"] = self.id["a"] @@ -246,6 +218,42 @@ class TestBufferProtocol(TestHelper, unittest.TestCase): self.assertEqual(list(view1), list(view2)) self.assertEqual(view1.tobytes(), view2.tobytes()) + +if np is not None: + class TestBufferProtocol_Numpy(TestHelper, unittest.TestCase): + def test_int(self): + self.id["a"] = array("i", [1, 2, 3, 4, 5]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + self.assertEqual(len(a), 5) + a[2] = 10 + self.assertEqual(self.id["a"].to_list(), [1, 2, 10, 4, 5]) + + def test_float(self): + self.id["a"] = array("f", [1.0, 2.0, 3.0, 4.0]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + self.assertEqual(len(a), 4) + a[-1] = 10 + self.assertEqual(self.id["a"].to_list(), [1.0, 2.0, 3.0, 10.0]) + + def test_double(self): + self.id["a"] = array("d", [1.0, 2.0, 3.0, 4.0]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + a[1] = 10 + self.assertEqual(self.id["a"].to_list(), [1.0, 10.0, 3.0, 4.0]) + + def test_full_update(self): + self.id["a"] = array("i", [1, 2, 3, 4, 5, 6]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + a[:] = [10, 20, 30, 40, 50, 60] + self.assertEqual(self.id["a"].to_list(), [10, 20, 30, 40, 50, 60]) + + def test_partial_update(self): + self.id["a"] = array("i", [1, 2, 3, 4, 5, 6, 7, 8]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + a[1:5] = [10, 20, 30, 40] + self.assertEqual(self.id["a"].to_list(), [1, 10, 20, 30, 40, 6, 7, 8]) + + class TestRNAData(TestHelper, unittest.TestCase): def test_custom_properties_none(self): @@ -309,7 +317,6 @@ class TestRNAData(TestHelper, unittest.TestCase): self.assertEqual(rna_data["default"], [1, 2]) - if __name__ == '__main__': import sys sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) From 6139782d8116054cba0857db862c66c6cb0b4094 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 12 Oct 2021 17:57:40 +1100 Subject: [PATCH 0699/1500] Tests: script_pyapi_idprop now cleans up after it's self This test left blend files in the users temporary directory. --- tests/python/bl_pyapi_idprop_datablock.py | 26 ++++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py index 63070dc2cb3..6cc99154cfe 100644 --- a/tests/python/bl_pyapi_idprop_datablock.py +++ b/tests/python/bl_pyapi_idprop_datablock.py @@ -16,6 +16,8 @@ # # ##### END GPL LICENSE BLOCK ##### +# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_idprop_datablock.py -- --verbose + import bpy import sys import os @@ -25,8 +27,10 @@ from bpy.types import UIList arr_len = 100 ob_cp_count = 100 -lib_path = os.path.join(tempfile.gettempdir(), "lib.blend") -test_path = os.path.join(tempfile.gettempdir(), "test.blend") + +# Set before execution. +lib_path = None +test_path = None def print_fail_msg_and_exit(msg): @@ -321,12 +325,18 @@ def test_restrictions2(): def main(): - init() - test_users_counting() - test_linking() - test_restrictions1() - check_crash(test_regressions) - test_restrictions2() + global lib_path + global test_path + with tempfile.TemporaryDirectory() as temp_dir: + lib_path = os.path.join(temp_dir, "lib.blend") + test_path = os.path.join(temp_dir, "test.blend") + + init() + test_users_counting() + test_linking() + test_restrictions1() + check_crash(test_regressions) + test_restrictions2() if __name__ == "__main__": From eb8afc39f858ca397e53fc8cd36b3ebc0024476e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 12 Oct 2021 17:57:41 +1100 Subject: [PATCH 0700/1500] Tests: remove noisy output from bl_pyapi_idprop_datablock As part of expected behavior this printed an exception, making it seem as if there was an error in the test. Now the exception is suppressed from the output, ensuring it matches an the expected output. --- tests/python/bl_pyapi_idprop_datablock.py | 123 +++++++++++++++------- 1 file changed, 85 insertions(+), 38 deletions(-) diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py index 6cc99154cfe..d4253dc4665 100644 --- a/tests/python/bl_pyapi_idprop_datablock.py +++ b/tests/python/bl_pyapi_idprop_datablock.py @@ -18,13 +18,17 @@ # ./blender.bin --background -noaudio --python tests/python/bl_pyapi_idprop_datablock.py -- --verbose -import bpy -import sys -import os -import tempfile +import contextlib import inspect -from bpy.types import UIList +import io +import os +import re +import sys +import tempfile +import bpy + +from bpy.types import UIList arr_len = 100 ob_cp_count = 100 @@ -49,13 +53,40 @@ def print_fail_msg_and_exit(msg): os._exit(1) -def abort_if_false(expr, msg=None): +def expect_false_or_abort(expr, msg=None): if not expr: if not msg: msg = "test failed" print_fail_msg_and_exit(msg) +def expect_exception_or_abort(*, fn, ex): + try: + fn() + exception = False + except ex: + exception = True + if exception: + return # OK + print_fail_msg_and_exit("test failed") + + +def expect_ouput_or_abort(*, fn, match_stderr=None, match_stdout=None): + + stdout, stderr = io.StringIO(), io.StringIO() + + with (contextlib.redirect_stderr(stderr), contextlib.redirect_stdout(stdout)): + fn() + + for (handle, match) in ((stdout, match_stdout), (stderr, match_stderr)): + if not match: + continue + handle.seek(0) + output = handle.read() + if not re.match(match, output): + print_fail_msg_and_exit("%r not found in %r" % (match, output)) + + class TestClass(bpy.types.PropertyGroup): test_prop: bpy.props.PointerProperty(type=bpy.types.Object) name: bpy.props.StringProperty() @@ -71,14 +102,6 @@ def get_scene(lib_name, sce_name): return s -def check_crash(fnc, args=None): - try: - fnc(args) if args else fnc() - except: - return - print_fail_msg_and_exit("test failed") - - def init(): bpy.utils.register_class(TestClass) bpy.types.Object.prop_array = bpy.props.CollectionProperty( @@ -122,12 +145,13 @@ def make_lib(): def check_lib(): # check pointer - abort_if_false(bpy.data.objects["Cube"].prop == bpy.data.objects['Camera']) + expect_false_or_abort(bpy.data.objects["Cube"].prop == bpy.data.objects['Camera']) # check array of pointers in duplicated object for i in range(0, arr_len): - abort_if_false(bpy.data.objects["Cube.001"].prop_array[i].test_prop == - bpy.data.objects['Light']) + expect_false_or_abort( + bpy.data.objects["Cube.001"].prop_array[i].test_prop == + bpy.data.objects['Light']) def check_lib_linking(): @@ -140,9 +164,9 @@ def check_lib_linking(): o = bpy.data.scenes["Scene_lib"].objects['Unique_Cube'] - abort_if_false(o.prop_array[0].test_prop == bpy.data.scenes["Scene_lib"].objects['Light']) - abort_if_false(o.prop == bpy.data.scenes["Scene_lib"].objects['Camera']) - abort_if_false(o.prop.library == o.library) + expect_false_or_abort(o.prop_array[0].test_prop == bpy.data.scenes["Scene_lib"].objects['Light']) + expect_false_or_abort(o.prop == bpy.data.scenes["Scene_lib"].objects['Camera']) + expect_false_or_abort(o.prop.library == o.library) bpy.ops.wm.save_as_mainfile(filepath=test_path) @@ -162,9 +186,10 @@ def check_linked_scene_copying(): # check node's props # must point to own scene camera - abort_if_false(intern_sce.node_tree.nodes['Render Layers']["prop"] and - not (intern_sce.node_tree.nodes['Render Layers']["prop"] == - extern_sce.node_tree.nodes['Render Layers']["prop"])) + expect_false_or_abort( + intern_sce.node_tree.nodes['Render Layers']["prop"] and + not (intern_sce.node_tree.nodes['Render Layers']["prop"] == + extern_sce.node_tree.nodes['Render Layers']["prop"])) def check_scene_copying(): @@ -183,8 +208,9 @@ def check_scene_copying(): # check node's props # must point to own scene camera - abort_if_false(not (first_sce.node_tree.nodes['Render Layers']["prop"] == - second_sce.node_tree.nodes['Render Layers']["prop"])) + expect_false_or_abort( + not (first_sce.node_tree.nodes['Render Layers']["prop"] == + second_sce.node_tree.nodes['Render Layers']["prop"])) # count users @@ -194,11 +220,11 @@ def test_users_counting(): n = 1000 for i in range(0, n): bpy.data.objects["Cube"]["a%s" % i] = bpy.data.objects["Light"].data - abort_if_false(bpy.data.objects["Light"].data.users == Light_us + n) + expect_false_or_abort(bpy.data.objects["Light"].data.users == Light_us + n) for i in range(0, int(n / 2)): bpy.data.objects["Cube"]["a%s" % i] = 1 - abort_if_false(bpy.data.objects["Light"].data.users == Light_us + int(n / 2)) + expect_false_or_abort(bpy.data.objects["Light"].data.users == Light_us + int(n / 2)) # linking @@ -240,16 +266,22 @@ def test_restrictions1(): self.layout.template_ID(context.scene, "prop1") self.layout.prop_search(context.scene, "prop2", bpy.data, "node_groups") - op = self.layout.operator("scene.test_op") + op = self.layout.operator(TEST_Op.bl_idname) op.str_prop = "test string" - def test_fnc(op): + def test_fn(op): op["ob"] = bpy.data.objects['Unique_Cube'] - check_crash(test_fnc, op) - abort_if_false(not hasattr(op, "id_prop")) + expect_exception_or_abort( + fn=lambda: test_fn(op), + ex=ImportError, + ) + expect_false_or_abort(not hasattr(op, "id_prop")) bpy.utils.register_class(TEST_PT_DatablockProp) - bpy.utils.register_class(TEST_Op) + expect_ouput_or_abort( + fn=lambda: bpy.utils.register_class(TEST_Op), + match_stderr="^ValueError: bpy_struct \"SCENE_OT_test_op\" registration error:", + ) def poll(self, value): return value.name in bpy.data.scenes["Scene_lib"].objects @@ -270,12 +302,18 @@ def test_restrictions1(): # NodeTree id_prop bpy.context.scene.prop2 = bpy.data.objects["Light.001"] - check_crash(sub_test) + expect_exception_or_abort( + fn=sub_test, + ex=TypeError, + ) bpy.context.scene.prop2 = bpy.data.node_groups.new("Shader", "ShaderNodeTree") - print("Please, test GUI performance manually on the Render tab, '%s' panel" % - TEST_PT_DatablockProp.bl_label, file=sys.stderr) + # NOTE: keep since the author thought this useful information. + # print( + # "Please, test GUI performance manually on the Render tab, '%s' panel" % + # TEST_PT_DatablockProp.bl_label, file=sys.stderr, + # ) sys.stderr.flush() @@ -318,8 +356,14 @@ def test_restrictions2(): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): layout.prop(item, "name", text="", emboss=False, icon_value=icon) - check_crash(bpy.utils.register_class, TestPrefs) - check_crash(bpy.utils.register_class, TEST_UL_list) + expect_exception_or_abort( + fn=lambda: bpy.utils.register_class(TestPrefs), + ex=ValueError, + ) + expect_exception_or_abort( + fn=lambda: bpy.utils.register_class(TEST_UL_list), + ex=ValueError, + ) bpy.utils.unregister_class(TestClassCollection) @@ -335,7 +379,10 @@ def main(): test_users_counting() test_linking() test_restrictions1() - check_crash(test_regressions) + expect_exception_or_abort( + fn=test_regressions, + ex=AttributeError, + ) test_restrictions2() From cfa59b3fabe729e49a57154ce21c5a4b88aa5812 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 12 Oct 2021 16:13:24 +0900 Subject: [PATCH 0701/1500] Cleanup: remove unused parameter --- source/blender/windowmanager/xr/intern/wm_xr_session.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 4c37253f457..88bc5c45c91 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -985,7 +985,6 @@ static wmXrActionData *wm_xr_session_event_create(const char *action_set_name, /* Dispatch events to window queues. */ static void wm_xr_session_events_dispatch(wmXrData *xr, - const XrSessionSettings *UNUSED(settings), GHOST_XrContextHandle xr_context, wmXrActionSet *action_set, wmXrSessionState *session_state, @@ -1084,11 +1083,10 @@ void wm_xr_session_actions_update(wmWindowManager *wm) /* Only update controller data and dispatch events for active action set. */ if (active_action_set) { - const XrSessionSettings *settings = &xr->session_settings; wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, xr->runtime); if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) { - wm_xr_session_controller_data_update(settings, + wm_xr_session_controller_data_update(&xr->session_settings, active_action_set->controller_grip_action, active_action_set->controller_aim_action, state); @@ -1100,7 +1098,7 @@ void wm_xr_session_actions_update(wmWindowManager *wm) xr->runtime->area = ED_area_offscreen_create(win, SPACE_VIEW3D); } - wm_xr_session_events_dispatch(xr, settings, xr_context, active_action_set, state, win); + wm_xr_session_events_dispatch(xr, xr_context, active_action_set, state, win); } } } From 9dda65455b54336fe3efef91eba9e41866dac1c1 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 12 Oct 2021 16:18:05 +0900 Subject: [PATCH 0702/1500] XR Controller Support Step 4: Controller Drawing Addresses T77127 (Controller Drawing). Adds VR controller visualization and custom drawing via draw handlers. Add-ons can draw to the XR surface (headset display) and mirror window by adding a View3D draw handler of region type 'XR' and draw type 'POST_VIEW'. Controller drawing and custom overlays can be toggled individually as XR session options, which will be added in a future update to the VR Scene Inspection add-on. For the actual drawing, the OpenXR XR_MSFT_controller_model extension is used to load a glTF model provided by the XR runtime. The model's vertex data is then used to create a GPUBatch in the XR session state. Finally, this batch is drawn via the XR surface draw handler mentioned above. For runtimes that do not support the controller model extension, a a simple fallback shape (sphere) is drawn instead. Reviewed By: Severin, fclem Differential Revision: https://developer.blender.org/D10948 --- intern/ghost/CMakeLists.txt | 7 + intern/ghost/GHOST_C-api.h | 24 + intern/ghost/GHOST_Types.h | 25 +- intern/ghost/intern/GHOST_C-api.cpp | 35 + intern/ghost/intern/GHOST_XrContext.cpp | 5 +- .../ghost/intern/GHOST_XrControllerModel.cpp | 629 ++++++++++++++++++ intern/ghost/intern/GHOST_XrControllerModel.h | 59 ++ intern/ghost/intern/GHOST_XrSession.cpp | 71 ++ intern/ghost/intern/GHOST_XrSession.h | 6 + source/blender/draw/intern/draw_manager.c | 62 ++ source/blender/editors/include/ED_space_api.h | 5 +- .../editors/include/ED_view3d_offscreen.h | 3 +- source/blender/editors/space_api/spacetypes.c | 14 +- .../editors/space_view3d/space_view3d.c | 5 + .../editors/space_view3d/view3d_draw.c | 48 +- source/blender/makesdna/DNA_screen_types.h | 5 +- source/blender/makesdna/DNA_view3d_enums.h | 3 + source/blender/makesdna/DNA_view3d_types.h | 3 + source/blender/makesdna/DNA_xr_types.h | 11 +- source/blender/makesrna/intern/rna_screen.c | 1 + source/blender/makesrna/intern/rna_xr.c | 39 ++ source/blender/windowmanager/WM_api.h | 2 + .../windowmanager/xr/intern/wm_xr_draw.c | 203 ++++++ .../windowmanager/xr/intern/wm_xr_intern.h | 14 +- .../windowmanager/xr/intern/wm_xr_session.c | 64 +- 25 files changed, 1319 insertions(+), 24 deletions(-) create mode 100644 intern/ghost/intern/GHOST_XrControllerModel.cpp create mode 100644 intern/ghost/intern/GHOST_XrControllerModel.h diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 76cac1049fb..05423209c71 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -473,6 +473,7 @@ if(WITH_XR_OPENXR) intern/GHOST_Xr.cpp intern/GHOST_XrAction.cpp intern/GHOST_XrContext.cpp + intern/GHOST_XrControllerModel.cpp intern/GHOST_XrEvent.cpp intern/GHOST_XrGraphicsBinding.cpp intern/GHOST_XrSession.cpp @@ -482,13 +483,19 @@ if(WITH_XR_OPENXR) intern/GHOST_IXrGraphicsBinding.h intern/GHOST_XrAction.h intern/GHOST_XrContext.h + intern/GHOST_XrControllerModel.h intern/GHOST_XrException.h intern/GHOST_XrSession.h intern/GHOST_XrSwapchain.h intern/GHOST_Xr_intern.h intern/GHOST_Xr_openxr_includes.h ) + list(APPEND INC + ../../extern/json/include + ../../extern/tinygltf + ) list(APPEND INC_SYS + ${EIGEN3_INCLUDE_DIRS} ${XR_OPENXR_SDK_INCLUDE_DIR} ) list(APPEND LIB diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index b78aac6f5eb..784febe8581 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1140,6 +1140,30 @@ void GHOST_XrGetActionCustomdataArray(GHOST_XrContextHandle xr_context, const char *action_set_name, void **r_customdata_array); +/* controller model */ +/** + * Load the OpenXR controller model. + */ +int GHOST_XrLoadControllerModel(GHOST_XrContextHandle xr_context, const char *subaction_path); + +/** + * Unload the OpenXR controller model. + */ +void GHOST_XrUnloadControllerModel(GHOST_XrContextHandle xr_context, const char *subaction_path); + +/** + * Update component transforms for the OpenXR controller model. + */ +int GHOST_XrUpdateControllerModelComponents(GHOST_XrContextHandle xr_context, + const char *subaction_path); + +/** + * Get vertex data for the OpenXR controller model. + */ +int GHOST_XrGetControllerModelData(GHOST_XrContextHandle xr_context, + const char *subaction_path, + GHOST_XrControllerModelData *r_data); + #endif /* WITH_XR_OPENXR */ #ifdef __cplusplus diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 898ee451baf..2c8014a08cc 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -754,8 +754,31 @@ typedef struct GHOST_XrActionProfileInfo { const char *profile_path; uint32_t count_subaction_paths; const char **subaction_paths; - /* Bindings for each subaction path. */ + /** Bindings for each subaction path. */ const GHOST_XrActionBindingInfo *bindings; } GHOST_XrActionProfileInfo; +typedef struct GHOST_XrControllerModelVertex { + float position[3]; + float normal[3]; +} GHOST_XrControllerModelVertex; + +typedef struct GHOST_XrControllerModelComponent { + /** World space transform. */ + float transform[4][4]; + uint32_t vertex_offset; + uint32_t vertex_count; + uint32_t index_offset; + uint32_t index_count; +} GHOST_XrControllerModelComponent; + +typedef struct GHOST_XrControllerModelData { + uint32_t count_vertices; + const GHOST_XrControllerModelVertex *vertices; + uint32_t count_indices; + const uint32_t *indices; + uint32_t count_components; + const GHOST_XrControllerModelComponent *components; +} GHOST_XrControllerModelData; + #endif /* WITH_XR_OPENXR */ diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index a2871b46222..a21c3a90c06 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -1069,4 +1069,39 @@ void GHOST_XrGetActionCustomdataArray(GHOST_XrContextHandle xr_contexthandle, xr_context); } +int GHOST_XrLoadControllerModel(GHOST_XrContextHandle xr_contexthandle, const char *subaction_path) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL_RET(xr_session->loadControllerModel(subaction_path), xr_context); + return 0; +} + +void GHOST_XrUnloadControllerModel(GHOST_XrContextHandle xr_contexthandle, + const char *subaction_path) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL(xr_session->unloadControllerModel(subaction_path), xr_context); +} + +int GHOST_XrUpdateControllerModelComponents(GHOST_XrContextHandle xr_contexthandle, + const char *subaction_path) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL_RET(xr_session->updateControllerModelComponents(subaction_path), xr_context); + return 0; +} + +int GHOST_XrGetControllerModelData(GHOST_XrContextHandle xr_contexthandle, + const char *subaction_path, + GHOST_XrControllerModelData *r_data) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL_RET(xr_session->getControllerModelData(subaction_path, *r_data), xr_context); + return 0; +} + #endif /* WITH_XR_OPENXR */ diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index fe8fec052fe..15b40690d83 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -412,11 +412,14 @@ void GHOST_XrContext::getExtensionsToEnable( try_ext.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME); } - /* Try enabling interaction profile extensions. */ + /* Interaction profile extensions. */ try_ext.push_back(XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME); try_ext.push_back(XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME); try_ext.push_back(XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME); + /* Controller model extension. */ + try_ext.push_back(XR_MSFT_CONTROLLER_MODEL_EXTENSION_NAME); + /* Varjo quad view extension. */ try_ext.push_back(XR_VARJO_QUAD_VIEWS_EXTENSION_NAME); diff --git a/intern/ghost/intern/GHOST_XrControllerModel.cpp b/intern/ghost/intern/GHOST_XrControllerModel.cpp new file mode 100644 index 00000000000..ae15bf11aa0 --- /dev/null +++ b/intern/ghost/intern/GHOST_XrControllerModel.cpp @@ -0,0 +1,629 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup GHOST + */ + +#include + +#include +#include + +#include "GHOST_Types.h" +#include "GHOST_XrException.h" +#include "GHOST_Xr_intern.h" + +#include "GHOST_XrControllerModel.h" + +#define TINYGLTF_IMPLEMENTATION +#define TINYGLTF_NO_STB_IMAGE +#define TINYGLTF_NO_STB_IMAGE_WRITE +#define STBIWDEF static inline +#include "tiny_gltf.h" + +struct GHOST_XrControllerModelNode { + int32_t parent_idx = -1; + int32_t component_idx = -1; + float local_transform[4][4]; +}; + +/* -------------------------------------------------------------------- */ +/** \name glTF Utilities + * + * Adapted from Microsoft OpenXR-Mixed Reality Samples (MIT License): + * https://github.com/microsoft/OpenXR-MixedReality + * \{ */ + +struct GHOST_XrPrimitive { + std::vector vertices; + std::vector indices; +}; + +/** + * Validate that an accessor does not go out of bounds of the buffer view that it references and + * that the buffer view does not exceed the bounds of the buffer that it references + */ +static void validate_accessor(const tinygltf::Accessor &accessor, + const tinygltf::BufferView &buffer_view, + const tinygltf::Buffer &buffer, + size_t byte_stride, + size_t element_size) +{ + /* Make sure the accessor does not go out of range of the buffer view. */ + if (accessor.byteOffset + (accessor.count - 1) * byte_stride + element_size > + buffer_view.byteLength) { + throw GHOST_XrException("glTF: Accessor goes out of range of bufferview."); + } + + /* Make sure the buffer view does not go out of range of the buffer. */ + if (buffer_view.byteOffset + buffer_view.byteLength > buffer.data.size()) { + throw GHOST_XrException("glTF: BufferView goes out of range of buffer."); + } +} + +template +static void read_vertices(const tinygltf::Accessor &accessor, + const tinygltf::BufferView &buffer_view, + const tinygltf::Buffer &buffer, + GHOST_XrPrimitive &primitive) +{ + if (accessor.type != TINYGLTF_TYPE_VEC3) { + throw GHOST_XrException( + "glTF: Accessor for primitive attribute has incorrect type (VEC3 expected)."); + } + + if (accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) { + throw GHOST_XrException( + "glTF: Accessor for primitive attribute has incorrect component type (FLOAT expected)."); + } + + /* If stride is not specified, it is tightly packed. */ + constexpr size_t packed_size = sizeof(float) * 3; + const size_t stride = buffer_view.byteStride == 0 ? packed_size : buffer_view.byteStride; + validate_accessor(accessor, buffer_view, buffer, stride, packed_size); + + /* Resize the vertices vector, if necessary, to include room for the attribute data. + If there are multiple attributes for a primitive, the first one will resize, and the + subsequent will not need to. */ + primitive.vertices.resize(accessor.count); + + /* Copy the attribute value over from the glTF buffer into the appropriate vertex field. */ + const uint8_t *buffer_ptr = buffer.data.data() + buffer_view.byteOffset + accessor.byteOffset; + for (size_t i = 0; i < accessor.count; i++, buffer_ptr += stride) { + memcpy(primitive.vertices[i].*field, buffer_ptr, stride); + } +} + +static void load_attribute_accessor(const tinygltf::Model &gltf_model, + const std::string &attribute_name, + int accessor_id, + GHOST_XrPrimitive &primitive) +{ + const auto &accessor = gltf_model.accessors.at(accessor_id); + + if (accessor.bufferView == -1) { + throw GHOST_XrException("glTF: Accessor for primitive attribute specifies no bufferview."); + } + + const tinygltf::BufferView &buffer_view = gltf_model.bufferViews.at(accessor.bufferView); + if (buffer_view.target != TINYGLTF_TARGET_ARRAY_BUFFER && buffer_view.target != 0) { + throw GHOST_XrException( + "glTF: Accessor for primitive attribute uses bufferview with invalid 'target' type."); + } + + const tinygltf::Buffer &buffer = gltf_model.buffers.at(buffer_view.buffer); + + if (attribute_name.compare("POSITION") == 0) { + read_vertices<&GHOST_XrControllerModelVertex::position>( + accessor, buffer_view, buffer, primitive); + } + else if (attribute_name.compare("NORMAL") == 0) { + read_vertices<&GHOST_XrControllerModelVertex::normal>( + accessor, buffer_view, buffer, primitive); + } +} + +/** + * Reads index data from a glTF primitive into a GHOST_XrPrimitive. glTF indices may be 8bit, 16bit + * or 32bit integers. This will coalesce indices from the source type(s) into a 32bit integer. + */ +template +static void read_indices(const tinygltf::Accessor &accessor, + const tinygltf::BufferView &buffer_view, + const tinygltf::Buffer &buffer, + GHOST_XrPrimitive &primitive) +{ + if (buffer_view.target != TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER && + buffer_view.target != 0) { /* Allow 0 (not specified) even though spec doesn't seem to allow + this (BoomBox GLB fails). */ + throw GHOST_XrException( + "glTF: Accessor for indices uses bufferview with invalid 'target' type."); + } + + constexpr size_t component_size_bytes = sizeof(TSrcIndex); + if (buffer_view.byteStride != 0 && + buffer_view.byteStride != + component_size_bytes) { /* Index buffer must be packed per glTF spec. */ + throw GHOST_XrException( + "glTF: Accessor for indices uses bufferview with invalid 'byteStride'."); + } + + validate_accessor(accessor, buffer_view, buffer, component_size_bytes, component_size_bytes); + + if ((accessor.count % 3) != 0) { /* Since only triangles are supported, enforce that the number + of indices is divisible by 3. */ + throw GHOST_XrException("glTF: Unexpected number of indices for triangle primitive"); + } + + const TSrcIndex *index_buffer = reinterpret_cast( + buffer.data.data() + buffer_view.byteOffset + accessor.byteOffset); + for (uint32_t i = 0; i < accessor.count; i++) { + primitive.indices.push_back(*(index_buffer + i)); + } +} + +/** + * Reads index data from a glTF primitive into a GHOST_XrPrimitive. + */ +static void load_index_accessor(const tinygltf::Model &gltf_model, + const tinygltf::Accessor &accessor, + GHOST_XrPrimitive &primitive) +{ + if (accessor.type != TINYGLTF_TYPE_SCALAR) { + throw GHOST_XrException("glTF: Accessor for indices specifies invalid 'type'."); + } + + if (accessor.bufferView == -1) { + throw GHOST_XrException("glTF: Index accessor without bufferView is currently not supported."); + } + + const tinygltf::BufferView &buffer_view = gltf_model.bufferViews.at(accessor.bufferView); + const tinygltf::Buffer &buffer = gltf_model.buffers.at(buffer_view.buffer); + + if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { + read_indices(accessor, buffer_view, buffer, primitive); + } + else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { + read_indices(accessor, buffer_view, buffer, primitive); + } + else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) { + read_indices(accessor, buffer_view, buffer, primitive); + } + else { + throw GHOST_XrException("glTF: Accessor for indices specifies invalid 'componentType'."); + } +} + +static GHOST_XrPrimitive read_primitive(const tinygltf::Model &gltf_model, + const tinygltf::Primitive &gltf_primitive) +{ + if (gltf_primitive.mode != TINYGLTF_MODE_TRIANGLES) { + throw GHOST_XrException( + "glTF: Unsupported primitive mode. Only TINYGLTF_MODE_TRIANGLES is supported."); + } + + GHOST_XrPrimitive primitive; + + /* glTF vertex data is stored in an attribute dictionary.Loop through each attribute and insert + * it into the GHOST_XrPrimitive. */ + for (const auto &[attr_name, accessor_idx] : gltf_primitive.attributes) { + load_attribute_accessor(gltf_model, attr_name, accessor_idx, primitive); + } + + if (gltf_primitive.indices != -1) { + /* If indices are specified for the glTF primitive, read them into the GHOST_XrPrimitive. */ + load_index_accessor(gltf_model, gltf_model.accessors.at(gltf_primitive.indices), primitive); + } + + return primitive; +} + +/** + * Calculate node local and world transforms. + */ +static void calc_node_transforms(const tinygltf::Node &gltf_node, + const float parent_transform[4][4], + float r_local_transform[4][4], + float r_world_transform[4][4]) +{ + /* A node may specify either a 4x4 matrix or TRS (Translation - Rotation - Scale) values, but not + * both. */ + if (gltf_node.matrix.size() == 16) { + const std::vector &dm = gltf_node.matrix; + float m[4][4] = {(float)dm[0], + (float)dm[1], + (float)dm[2], + (float)dm[3], + (float)dm[4], + (float)dm[5], + (float)dm[6], + (float)dm[7], + (float)dm[8], + (float)dm[9], + (float)dm[10], + (float)dm[11], + (float)dm[12], + (float)dm[13], + (float)dm[14], + (float)dm[15]}; + memcpy(r_local_transform, m, sizeof(float) * 16); + } + else { + /* No matrix is present, so construct a matrix from the TRS values (each one is optional). */ + std::vector translation = gltf_node.translation; + std::vector rotation = gltf_node.rotation; + std::vector scale = gltf_node.scale; + Eigen::Matrix4f &m = *(Eigen::Matrix4f *)r_local_transform; + Eigen::Quaternionf q; + Eigen::Matrix3f scalemat; + + if (translation.size() != 3) { + translation.resize(3); + translation[0] = translation[1] = translation[2] = 0.0; + } + if (rotation.size() != 4) { + rotation.resize(4); + rotation[0] = rotation[1] = rotation[2] = 0.0; + rotation[3] = 1.0; + } + if (scale.size() != 3) { + scale.resize(3); + scale[0] = scale[1] = scale[2] = 1.0; + } + + q.w() = (float)rotation[3]; + q.x() = (float)rotation[0]; + q.y() = (float)rotation[1]; + q.z() = (float)rotation[2]; + q.normalize(); + + scalemat.setIdentity(); + scalemat(0, 0) = (float)scale[0]; + scalemat(1, 1) = (float)scale[1]; + scalemat(2, 2) = (float)scale[2]; + + m.setIdentity(); + m.block<3, 3>(0, 0) = q.toRotationMatrix() * scalemat; + m.block<3, 1>(0, 3) = Eigen::Vector3f( + (float)translation[0], (float)translation[1], (float)translation[2]); + } + + *(Eigen::Matrix4f *)r_world_transform = *(Eigen::Matrix4f *)parent_transform * + *(Eigen::Matrix4f *)r_local_transform; +} + +static void load_node(const tinygltf::Model &gltf_model, + int gltf_node_id, + int32_t parent_idx, + const float parent_transform[4][4], + const std::string &parent_name, + const std::vector &node_properties, + std::vector &vertices, + std::vector &indices, + std::vector &components, + std::vector &nodes, + std::vector &node_state_indices) +{ + const tinygltf::Node &gltf_node = gltf_model.nodes.at(gltf_node_id); + float world_transform[4][4]; + + GHOST_XrControllerModelNode &node = nodes.emplace_back(); + const int32_t node_idx = (int32_t)(nodes.size() - 1); + node.parent_idx = parent_idx; + calc_node_transforms(gltf_node, parent_transform, node.local_transform, world_transform); + + for (size_t i = 0; i < node_properties.size(); ++i) { + if ((node_state_indices[i] < 0) && (parent_name == node_properties[i].parentNodeName) && + (gltf_node.name == node_properties[i].nodeName)) { + node_state_indices[i] = node_idx; + break; + } + } + + if (gltf_node.mesh != -1) { + const tinygltf::Mesh &gltf_mesh = gltf_model.meshes.at(gltf_node.mesh); + + GHOST_XrControllerModelComponent &component = components.emplace_back(); + node.component_idx = components.size() - 1; + memcpy(component.transform, world_transform, sizeof(component.transform)); + component.vertex_offset = vertices.size(); + component.index_offset = indices.size(); + + for (const tinygltf::Primitive &gltf_primitive : gltf_mesh.primitives) { + /* Read the primitive data from the glTF buffers. */ + const GHOST_XrPrimitive primitive = read_primitive(gltf_model, gltf_primitive); + + const size_t start_vertex = vertices.size(); + size_t offset = start_vertex; + size_t count = primitive.vertices.size(); + vertices.resize(offset + count); + memcpy(vertices.data() + offset, + primitive.vertices.data(), + count * sizeof(decltype(primitive.vertices)::value_type)); + + offset = indices.size(); + count = primitive.indices.size(); + indices.resize(offset + count); + for (size_t i = 0; i < count; i += 3) { + indices[offset + i + 0] = start_vertex + primitive.indices[i + 0]; + indices[offset + i + 1] = start_vertex + primitive.indices[i + 2]; + indices[offset + i + 2] = start_vertex + primitive.indices[i + 1]; + } + } + + component.vertex_count = vertices.size() - component.vertex_offset; + component.index_count = indices.size() - component.index_offset; + } + + /* Recursively load all children. */ + for (const int child_node_id : gltf_node.children) { + load_node(gltf_model, + child_node_id, + node_idx, + world_transform, + gltf_node.name, + node_properties, + vertices, + indices, + components, + nodes, + node_state_indices); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name OpenXR Extension Functions + * + * \{ */ + +static PFN_xrGetControllerModelKeyMSFT g_xrGetControllerModelKeyMSFT = nullptr; +static PFN_xrLoadControllerModelMSFT g_xrLoadControllerModelMSFT = nullptr; +static PFN_xrGetControllerModelPropertiesMSFT g_xrGetControllerModelPropertiesMSFT = nullptr; +static PFN_xrGetControllerModelStateMSFT g_xrGetControllerModelStateMSFT = nullptr; +static XrInstance g_instance = XR_NULL_HANDLE; + +#define INIT_EXTENSION_FUNCTION(name) \ + CHECK_XR( \ + xrGetInstanceProcAddr(instance, #name, reinterpret_cast(&g_##name)), \ + "Failed to get pointer to extension function: " #name); + +static void init_controller_model_extension_functions(XrInstance instance) +{ + if (instance != g_instance) { + g_instance = instance; + g_xrGetControllerModelKeyMSFT = nullptr; + g_xrLoadControllerModelMSFT = nullptr; + g_xrGetControllerModelPropertiesMSFT = nullptr; + g_xrGetControllerModelStateMSFT = nullptr; + } + + if (g_xrGetControllerModelKeyMSFT == nullptr) { + INIT_EXTENSION_FUNCTION(xrGetControllerModelKeyMSFT); + } + if (g_xrLoadControllerModelMSFT == nullptr) { + INIT_EXTENSION_FUNCTION(xrLoadControllerModelMSFT); + } + if (g_xrGetControllerModelPropertiesMSFT == nullptr) { + INIT_EXTENSION_FUNCTION(xrGetControllerModelPropertiesMSFT); + } + if (g_xrGetControllerModelStateMSFT == nullptr) { + INIT_EXTENSION_FUNCTION(xrGetControllerModelStateMSFT); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GHOST_XrControllerModel + * + * \{ */ + +GHOST_XrControllerModel::GHOST_XrControllerModel(XrInstance instance, + const char *subaction_path_str) +{ + init_controller_model_extension_functions(instance); + + CHECK_XR(xrStringToPath(instance, subaction_path_str, &m_subaction_path), + (std::string("Failed to get user path \"") + subaction_path_str + "\".").data()); +} + +GHOST_XrControllerModel::~GHOST_XrControllerModel() +{ + if (m_load_task.valid()) { + m_load_task.wait(); + } +} + +void GHOST_XrControllerModel::load(XrSession session) +{ + if (m_data_loaded || m_load_task.valid()) { + return; + } + + /* Get model key. */ + XrControllerModelKeyStateMSFT key_state{XR_TYPE_CONTROLLER_MODEL_KEY_STATE_MSFT}; + CHECK_XR(g_xrGetControllerModelKeyMSFT(session, m_subaction_path, &key_state), + "Failed to get controller model key state."); + + if (key_state.modelKey != XR_NULL_CONTROLLER_MODEL_KEY_MSFT) { + m_model_key = key_state.modelKey; + /* Load asynchronously. */ + m_load_task = std::async(std::launch::async, + [&, session = session]() { return loadControllerModel(session); }); + } +} + +void GHOST_XrControllerModel::loadControllerModel(XrSession session) +{ + /* Load binary buffers. */ + uint32_t buf_size = 0; + CHECK_XR(g_xrLoadControllerModelMSFT(session, m_model_key, 0, &buf_size, nullptr), + "Failed to get controller model buffer size."); + + std::vector buf((size_t)buf_size); + CHECK_XR(g_xrLoadControllerModelMSFT(session, m_model_key, buf_size, &buf_size, buf.data()), + "Failed to load controller model binary buffers."); + + /* Convert to glTF model. */ + tinygltf::TinyGLTF gltf_loader; + tinygltf::Model gltf_model; + std::string err_msg; + { + /* Workaround for TINYGLTF_NO_STB_IMAGE define. Set custom image loader to prevent failure when + * parsing image data. */ + auto load_img_func = [](tinygltf::Image *img, + const int p0, + std::string *p1, + std::string *p2, + int p3, + int p4, + const unsigned char *p5, + int p6, + void *user_pointer) -> bool { + (void)img; + (void)p0; + (void)p1; + (void)p2; + (void)p3; + (void)p4; + (void)p5; + (void)p6; + (void)user_pointer; + return true; + }; + gltf_loader.SetImageLoader(load_img_func, nullptr); + } + + if (!gltf_loader.LoadBinaryFromMemory(&gltf_model, &err_msg, nullptr, buf.data(), buf_size)) { + throw GHOST_XrException(("Failed to load glTF controller model: " + err_msg).c_str()); + } + + /* Get node properties. */ + XrControllerModelPropertiesMSFT model_properties{XR_TYPE_CONTROLLER_MODEL_PROPERTIES_MSFT}; + model_properties.nodeCapacityInput = 0; + CHECK_XR(g_xrGetControllerModelPropertiesMSFT(session, m_model_key, &model_properties), + "Failed to get controller model node properties count."); + + std::vector node_properties( + model_properties.nodeCountOutput, {XR_TYPE_CONTROLLER_MODEL_NODE_PROPERTIES_MSFT}); + model_properties.nodeCapacityInput = (uint32_t)node_properties.size(); + model_properties.nodeProperties = node_properties.data(); + CHECK_XR(g_xrGetControllerModelPropertiesMSFT(session, m_model_key, &model_properties), + "Failed to get controller model node properties."); + + m_node_state_indices.resize(node_properties.size(), -1); + + /* Get mesh vertex data. */ + const tinygltf::Scene &default_scene = gltf_model.scenes.at( + (gltf_model.defaultScene == -1) ? 0 : gltf_model.defaultScene); + const int32_t root_idx = -1; + const std::string root_name = ""; + float root_transform[4][4] = {0}; + root_transform[0][0] = root_transform[1][1] = root_transform[2][2] = root_transform[3][3] = 1.0f; + + for (const int node_id : default_scene.nodes) { + load_node(gltf_model, + node_id, + root_idx, + root_transform, + root_name, + node_properties, + m_vertices, + m_indices, + m_components, + m_nodes, + m_node_state_indices); + } + + m_data_loaded = true; +} + +void GHOST_XrControllerModel::updateComponents(XrSession session) +{ + if (!m_data_loaded) { + return; + } + + /* Get node states. */ + XrControllerModelStateMSFT model_state{XR_TYPE_CONTROLLER_MODEL_STATE_MSFT}; + model_state.nodeCapacityInput = 0; + CHECK_XR(g_xrGetControllerModelStateMSFT(session, m_model_key, &model_state), + "Failed to get controller model node state count."); + + const uint32_t count = model_state.nodeCountOutput; + std::vector node_states( + count, {XR_TYPE_CONTROLLER_MODEL_NODE_STATE_MSFT}); + model_state.nodeCapacityInput = count; + model_state.nodeStates = node_states.data(); + CHECK_XR(g_xrGetControllerModelStateMSFT(session, m_model_key, &model_state), + "Failed to get controller model node states."); + + /* Update node local transforms. */ + assert(m_node_state_indices.size() == count); + + for (uint32_t state_idx = 0; state_idx < count; ++state_idx) { + const int32_t &node_idx = m_node_state_indices[state_idx]; + if (node_idx >= 0) { + const XrPosef &pose = node_states[state_idx].nodePose; + Eigen::Matrix4f &m = *(Eigen::Matrix4f *)m_nodes[node_idx].local_transform; + Eigen::Quaternionf q( + pose.orientation.w, pose.orientation.x, pose.orientation.y, pose.orientation.z); + m.setIdentity(); + m.block<3, 3>(0, 0) = q.toRotationMatrix(); + m.block<3, 1>(0, 3) = Eigen::Vector3f(pose.position.x, pose.position.y, pose.position.z); + } + } + + /* Calculate component transforms (in world space). */ + std::vector world_transforms(m_nodes.size()); + uint32_t i = 0; + for (const GHOST_XrControllerModelNode &node : m_nodes) { + world_transforms[i] = (node.parent_idx >= 0) ? world_transforms[node.parent_idx] * + *(Eigen::Matrix4f *)node.local_transform : + *(Eigen::Matrix4f *)node.local_transform; + if (node.component_idx >= 0) { + memcpy(m_components[node.component_idx].transform, + world_transforms[i].data(), + sizeof(m_components[node.component_idx].transform)); + } + ++i; + } +} + +void GHOST_XrControllerModel::getData(GHOST_XrControllerModelData &r_data) +{ + if (m_data_loaded) { + r_data.count_vertices = (uint32_t)m_vertices.size(); + r_data.vertices = m_vertices.data(); + r_data.count_indices = (uint32_t)m_indices.size(); + r_data.indices = m_indices.data(); + r_data.count_components = (uint32_t)m_components.size(); + r_data.components = m_components.data(); + } + else { + r_data.count_vertices = 0; + r_data.vertices = nullptr; + r_data.count_indices = 0; + r_data.indices = nullptr; + r_data.count_components = 0; + r_data.components = nullptr; + } +} + +/** \} */ diff --git a/intern/ghost/intern/GHOST_XrControllerModel.h b/intern/ghost/intern/GHOST_XrControllerModel.h new file mode 100644 index 00000000000..5ff72957b24 --- /dev/null +++ b/intern/ghost/intern/GHOST_XrControllerModel.h @@ -0,0 +1,59 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup GHOST + */ + +/* Note: Requires OpenXR headers to be included before this one for OpenXR types (XrInstance, + * XrSession, etc.). */ + +#pragma once + +#include +#include +#include + +struct GHOST_XrControllerModelNode; + +/** + * OpenXR glTF controller model. + */ +class GHOST_XrControllerModel { + public: + GHOST_XrControllerModel(XrInstance instance, const char *subaction_path); + ~GHOST_XrControllerModel(); + + void load(XrSession session); + void updateComponents(XrSession session); + void getData(GHOST_XrControllerModelData &r_data); + + private: + XrPath m_subaction_path = XR_NULL_PATH; + XrControllerModelKeyMSFT m_model_key = XR_NULL_CONTROLLER_MODEL_KEY_MSFT; + + std::future m_load_task; + std::atomic m_data_loaded = false; + + std::vector m_vertices; + std::vector m_indices; + std::vector m_components; + std::vector m_nodes; + /** Maps node states to nodes. */ + std::vector m_node_state_indices; + + void loadControllerModel(XrSession session); +}; diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index cd930c8328b..808f3a26be7 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -30,6 +30,7 @@ #include "GHOST_IXrGraphicsBinding.h" #include "GHOST_XrAction.h" #include "GHOST_XrContext.h" +#include "GHOST_XrControllerModel.h" #include "GHOST_XrException.h" #include "GHOST_XrSwapchain.h" #include "GHOST_Xr_intern.h" @@ -52,6 +53,8 @@ struct OpenXRSessionData { std::vector swapchains; std::map action_sets; + /* Controller models identified by subaction path. */ + std::map controller_models; }; struct GHOST_XrDrawInfo { @@ -916,3 +919,71 @@ void GHOST_XrSession::getActionCustomdataArray(const char *action_set_name, } /** \} */ /* Actions */ + +/* -------------------------------------------------------------------- */ +/** \name Controller Model + * + * \{ */ + +bool GHOST_XrSession::loadControllerModel(const char *subaction_path) +{ + if (!m_context->isExtensionEnabled(XR_MSFT_CONTROLLER_MODEL_EXTENSION_NAME)) { + return false; + } + + XrSession session = m_oxr->session; + std::map &controller_models = m_oxr->controller_models; + std::map::iterator it = controller_models.find( + subaction_path); + + if (it == controller_models.end()) { + XrInstance instance = m_context->getInstance(); + it = controller_models + .emplace(std::piecewise_construct, + std::make_tuple(subaction_path), + std::make_tuple(instance, subaction_path)) + .first; + } + + it->second.load(session); + + return true; +} + +void GHOST_XrSession::unloadControllerModel(const char *subaction_path) +{ + std::map &controller_models = m_oxr->controller_models; + if (controller_models.find(subaction_path) != controller_models.end()) { + controller_models.erase(subaction_path); + } +} + +bool GHOST_XrSession::updateControllerModelComponents(const char *subaction_path) +{ + XrSession session = m_oxr->session; + std::map::iterator it = m_oxr->controller_models.find( + subaction_path); + if (it == m_oxr->controller_models.end()) { + return false; + } + + it->second.updateComponents(session); + + return true; +} + +bool GHOST_XrSession::getControllerModelData(const char *subaction_path, + GHOST_XrControllerModelData &r_data) +{ + std::map::iterator it = m_oxr->controller_models.find( + subaction_path); + if (it == m_oxr->controller_models.end()) { + return false; + } + + it->second.getData(r_data); + + return true; +} + +/** \} */ /* Controller Model */ diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index a76e11aede1..83de44c8d8e 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -90,6 +90,12 @@ class GHOST_XrSession { uint32_t getActionCount(const char *action_set_name); void getActionCustomdataArray(const char *action_set_name, void **r_customdata_array); + /** Controller model functions. */ + bool loadControllerModel(const char *subaction_path); + void unloadControllerModel(const char *subaction_path); + bool updateControllerModelComponents(const char *subaction_path); + bool getControllerModelData(const char *subaction_path, GHOST_XrControllerModelData &r_data); + private: /** Pointer back to context managing this session. Would be nice to avoid, but needed to access * custom callbacks set before session start. */ diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index b8de92dea7f..c8900d64935 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -51,6 +51,7 @@ #include "BKE_pbvh.h" #include "BKE_pointcache.h" #include "BKE_pointcloud.h" +#include "BKE_screen.h" #include "BKE_volume.h" #include "DNA_camera_types.h" @@ -1418,6 +1419,27 @@ void DRW_draw_callbacks_post_scene(void) ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW); +#ifdef WITH_XR_OPENXR + /* XR callbacks (controllers, custom draw functions) for session mirror. */ + if ((v3d->flag & V3D_XR_SESSION_MIRROR) != 0) { + if ((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) { + ARegionType *art = WM_xr_surface_controller_region_type_get(); + if (art) { + ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW); + } + } + if ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0) { + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + if (st) { + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR); + if (art) { + ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW); + } + } + } + } +#endif + /* Callback can be nasty and do whatever they want with the state. * Don't trust them! */ DRW_state_reset(); @@ -1464,6 +1486,46 @@ void DRW_draw_callbacks_post_scene(void) ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true); GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } + +#ifdef WITH_XR_OPENXR + if ((v3d->flag & V3D_XR_SESSION_SURFACE) != 0) { + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + DRW_state_reset(); + + GPU_framebuffer_bind(dfbl->overlay_fb); + + GPU_matrix_projection_set(rv3d->winmat); + GPU_matrix_set(rv3d->viewmat); + + /* XR callbacks (controllers, custom draw functions) for session surface. */ + if (((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) || + ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0)) { + GPU_depth_test(GPU_DEPTH_NONE); + GPU_apply_state(); + + if ((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) { + ARegionType *art = WM_xr_surface_controller_region_type_get(); + if (art) { + ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW); + } + } + if ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0) { + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + if (st) { + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR); + if (art) { + ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW); + } + } + } + + DRW_state_reset(); + } + + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + } +#endif } } diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h index 1a3aa7e5496..958df8f7707 100644 --- a/source/blender/editors/include/ED_space_api.h +++ b/source/blender/editors/include/ED_space_api.h @@ -72,8 +72,9 @@ void *ED_region_draw_cb_activate(struct ARegionType *art, void (*draw)(const struct bContext *, struct ARegion *, void *), void *customdata, int type); -void ED_region_draw_cb_draw(const struct bContext *, struct ARegion *, int); -void ED_region_draw_cb_exit(struct ARegionType *, void *); +void ED_region_draw_cb_draw(const struct bContext *C, struct ARegion *region, int type); +void ED_region_surface_draw_cb_draw(struct ARegionType *art, int type); +void ED_region_draw_cb_exit(struct ARegionType *art, void *handle); void ED_region_draw_cb_remove_by_type(struct ARegionType *art, void *draw_fn, void (*free)(void *)); diff --git a/source/blender/editors/include/ED_view3d_offscreen.h b/source/blender/editors/include/ED_view3d_offscreen.h index c490e96031f..8b695e61a35 100644 --- a/source/blender/editors/include/ED_view3d_offscreen.h +++ b/source/blender/editors/include/ED_view3d_offscreen.h @@ -60,7 +60,7 @@ void ED_view3d_draw_offscreen(struct Depsgraph *depsgraph, void ED_view3d_draw_offscreen_simple(struct Depsgraph *depsgraph, struct Scene *scene, struct View3DShading *shading_override, - int drawtype, + eDrawType drawtype, int winx, int winy, unsigned int draw_flags, @@ -68,6 +68,7 @@ void ED_view3d_draw_offscreen_simple(struct Depsgraph *depsgraph, const float winmat[4][4], float clip_start, float clip_end, + bool is_xr_surface, bool is_image_render, bool draw_background, const char *viewname, diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 3da283089c5..149067a94fe 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -264,9 +264,9 @@ void ED_region_draw_cb_exit(ARegionType *art, void *handle) } } -void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type) +static void ed_region_draw_cb_draw(const bContext *C, ARegion *region, ARegionType *art, int type) { - LISTBASE_FOREACH_MUTABLE (RegionDrawCB *, rdc, ®ion->type->drawcalls) { + LISTBASE_FOREACH_MUTABLE (RegionDrawCB *, rdc, &art->drawcalls) { if (rdc->type == type) { rdc->draw(C, region, rdc->customdata); @@ -276,6 +276,16 @@ void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type) } } +void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type) +{ + ed_region_draw_cb_draw(C, region, region->type, type); +} + +void ED_region_surface_draw_cb_draw(ARegionType *art, int type) +{ + ed_region_draw_cb_draw(NULL, NULL, art, type); +} + void ED_region_draw_cb_remove_by_type(ARegionType *art, void *draw_fn, void (*free)(void *)) { LISTBASE_FOREACH_MUTABLE (RegionDrawCB *, rdc, &art->drawcalls) { diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index f68a4d78a00..bedc24a6287 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1798,5 +1798,10 @@ void ED_spacetype_view3d(void) art = ED_area_type_hud(st->spaceid); BLI_addhead(&st->regiontypes, art); + /* regions: xr */ + art = MEM_callocN(sizeof(ARegionType), "spacetype view3d xr region"); + art->regionid = RGN_TYPE_XR; + BLI_addhead(&st->regiontypes, art); + BKE_spacetype_register(st); } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 733ec7e81cd..4da24888e1d 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -339,7 +339,16 @@ static void view3d_xr_mirror_setup(const wmWindowManager *wm, } view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, NULL, rect); - /* Reset overridden View3D data */ + /* Set draw flags. */ + SET_FLAG_FROM_TEST(v3d->flag2, + (wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CONTROLLERS) != 0, + V3D_XR_SHOW_CONTROLLERS); + SET_FLAG_FROM_TEST(v3d->flag2, + (wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) != + 0, + V3D_XR_SHOW_CUSTOM_OVERLAYS); + + /* Reset overridden View3D data. */ v3d->lens = lens_old; } #endif /* WITH_XR_OPENXR */ @@ -1757,6 +1766,7 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph, const float winmat[4][4], float clip_start, float clip_end, + bool is_xr_surface, bool is_image_render, bool draw_background, const char *viewname, @@ -1786,23 +1796,37 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph, v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS; } - if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) { - v3d.flag2 |= V3D_SHOW_ANNOTATION; + if ((draw_flags & ~V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS) == V3D_OFSDRAW_NONE) { + v3d.flag2 = V3D_HIDE_OVERLAYS; } - if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) { - v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y; - v3d.grid = 1.0f; - v3d.gridlines = 16; - v3d.gridsubdiv = 10; - - /* Show grid, disable other overlays (set all available _HIDE_ flags). */ + else { + if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) { + v3d.flag2 |= V3D_SHOW_ANNOTATION; + } + if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) { + v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y; + v3d.grid = 1.0f; + v3d.gridlines = 16; + v3d.gridsubdiv = 10; + } + if (draw_flags & V3D_OFSDRAW_SHOW_SELECTION) { + v3d.flag |= V3D_SELECT_OUTLINE; + } + if (draw_flags & V3D_OFSDRAW_XR_SHOW_CONTROLLERS) { + v3d.flag2 |= V3D_XR_SHOW_CONTROLLERS; + } + if (draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) { + v3d.flag2 |= V3D_XR_SHOW_CUSTOM_OVERLAYS; + } + /* Disable other overlays (set all available _HIDE_ flags). */ v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT | V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_BONES | V3D_OVERLAY_HIDE_OBJECT_XTRAS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS; v3d.flag |= V3D_HIDE_HELPLINES; } - else { - v3d.flag2 = V3D_HIDE_OVERLAYS; + + if (is_xr_surface) { + v3d.flag |= V3D_XR_SESSION_SURFACE; } rv3d.persp = RV3D_PERSP; diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index f557890e3e8..86fd6b9744a 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -666,8 +666,11 @@ typedef enum eRegion_Type { RGN_TYPE_EXECUTE = 10, RGN_TYPE_FOOTER = 11, RGN_TYPE_TOOL_HEADER = 12, + /* Region type used exclusively by internal code and add-ons to register draw callbacks to the XR + context (surface, mirror view). Does not represent any real region. */ + RGN_TYPE_XR = 13, -#define RGN_TYPE_LEN (RGN_TYPE_TOOL_HEADER + 1) +#define RGN_TYPE_LEN (RGN_TYPE_XR + 1) } eRegion_Type; /* use for function args */ diff --git a/source/blender/makesdna/DNA_view3d_enums.h b/source/blender/makesdna/DNA_view3d_enums.h index aec52da1bf9..59012a0cd8d 100644 --- a/source/blender/makesdna/DNA_view3d_enums.h +++ b/source/blender/makesdna/DNA_view3d_enums.h @@ -30,6 +30,9 @@ typedef enum eV3DOffscreenDrawFlag { V3D_OFSDRAW_SHOW_ANNOTATION = (1 << 0), V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS = (1 << 1), V3D_OFSDRAW_SHOW_GRIDFLOOR = (1 << 2), + V3D_OFSDRAW_SHOW_SELECTION = (1 << 3), + V3D_OFSDRAW_XR_SHOW_CONTROLLERS = (1 << 4), + V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS = (1 << 5), } eV3DOffscreenDrawFlag; /** #View3DShading.light */ diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 4d88f6f0c15..fafc470b95a 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -373,6 +373,7 @@ typedef struct View3D { #define V3D_HIDE_HELPLINES (1 << 2) #define V3D_FLAG_UNUSED_2 (1 << 3) /* cleared */ #define V3D_XR_SESSION_MIRROR (1 << 4) +#define V3D_XR_SESSION_SURFACE (1 << 5) #define V3D_FLAG_UNUSED_10 (1 << 10) /* cleared */ #define V3D_SELECT_OUTLINE (1 << 11) @@ -465,6 +466,8 @@ enum { #define V3D_FLAG2_UNUSED_13 (1 << 13) /* cleared */ #define V3D_FLAG2_UNUSED_14 (1 << 14) /* cleared */ #define V3D_FLAG2_UNUSED_15 (1 << 15) /* cleared */ +#define V3D_XR_SHOW_CONTROLLERS (1 << 16) +#define V3D_XR_SHOW_CUSTOM_OVERLAYS (1 << 17) /** #View3D.gp_flag (short) */ #define V3D_GP_FADE_OBJECTS (1 << 0) /* Fade all non GP objects */ diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index a9d427777f7..6f4f7e3e8ae 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -42,7 +42,9 @@ typedef struct XrSessionSettings { /** View3D draw flags (V3D_OFSDRAW_NONE, V3D_OFSDRAW_SHOW_ANNOTATION, ...). */ char draw_flags; - char _pad2[3]; + /** Draw style for controller visualization. */ + char controller_draw_style; + char _pad2[2]; /** Clipping distance. */ float clip_start, clip_end; @@ -61,6 +63,13 @@ typedef enum eXRSessionBasePoseType { XR_BASE_POSE_CUSTOM = 2, } eXRSessionBasePoseType; +typedef enum eXrSessionControllerDrawStyle { + XR_CONTROLLER_DRAW_DARK = 0, + XR_CONTROLLER_DRAW_LIGHT = 1, + XR_CONTROLLER_DRAW_DARK_RAY = 2, + XR_CONTROLLER_DRAW_LIGHT_RAY = 3, +} eXrSessionControllerDrawStyle; + /** XR action type. Enum values match those in GHOST_XrActionType enum for consistency. */ typedef enum eXrActionType { XR_BOOLEAN_INPUT = 1, diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index 912f47ba9aa..a8f28f6091c 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -46,6 +46,7 @@ const EnumPropertyItem rna_enum_region_type_items[] = { {RGN_TYPE_EXECUTE, "EXECUTE", 0, "Execute Buttons", ""}, {RGN_TYPE_FOOTER, "FOOTER", 0, "Footer", ""}, {RGN_TYPE_TOOL_HEADER, "TOOL_HEADER", 0, "Tool Header", ""}, + {RGN_TYPE_XR, "XR", 0, "XR", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index a433a93e403..3705284ca66 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -1559,6 +1559,22 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem controller_draw_styles[] = { + {XR_CONTROLLER_DRAW_DARK, "DARK", 0, "Dark", "Draw dark controller"}, + {XR_CONTROLLER_DRAW_LIGHT, "LIGHT", 0, "Light", "Draw light controller"}, + {XR_CONTROLLER_DRAW_DARK_RAY, + "DARK_RAY", + 0, + "Dark + Ray", + "Draw dark controller with aiming axis ray"}, + {XR_CONTROLLER_DRAW_LIGHT_RAY, + "LIGHT_RAY", + 0, + "Light + Ray", + "Draw light controller with aiming axis ray"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "XrSessionSettings", NULL); RNA_def_struct_ui_text(srna, "XR Session Settings", ""); @@ -1609,6 +1625,29 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "show_selection", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_SELECTION); + RNA_def_property_ui_text(prop, "Show Selection", "Show selection outlines"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "show_controllers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_XR_SHOW_CONTROLLERS); + RNA_def_property_ui_text( + prop, "Show Controllers", "Show VR controllers (requires VR actions for controller poses)"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "show_custom_overlays", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS); + RNA_def_property_ui_text(prop, "Show Custom Overlays", "Show custom VR overlays"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "controller_draw_style", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_items(prop, controller_draw_styles); + RNA_def_property_ui_text( + prop, "Controller Draw Style", "Style to use when drawing VR controllers"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "clip_start", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 1e-6f, FLT_MAX); RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 531c5aa19bf..68e2c6b38f0 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1013,6 +1013,8 @@ bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, unsigned int subaction_idx, float r_rotation[4]); +struct ARegionType *WM_xr_surface_controller_region_type_get(void); + /* wm_xr_actions.c */ /* XR action functions to be called pre-XR session start. * NOTE: The "destroy" functions can also be called post-session start. */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index bbb73fc2007..6b3756c8178 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -24,12 +24,17 @@ #include +#include "BKE_context.h" + #include "BLI_listbase.h" #include "BLI_math.h" #include "ED_view3d_offscreen.h" #include "GHOST_C-api.h" +#include "GPU_batch_presets.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" #include "GPU_viewport.h" @@ -153,6 +158,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) winmat, settings->clip_start, settings->clip_end, + true, false, true, NULL, @@ -172,3 +178,200 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) wm_xr_draw_viewport_buffers_to_active_framebuffer(xr_data->runtime, surface_data, draw_view); } + +static GPUBatch *wm_xr_controller_model_batch_create(GHOST_XrContextHandle xr_context, + const char *subaction_path) +{ + GHOST_XrControllerModelData model_data; + + if (!GHOST_XrGetControllerModelData(xr_context, subaction_path, &model_data) || + model_data.count_vertices < 1) { + return NULL; + } + + GPUVertFormat format = {0}; + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo, model_data.count_vertices); + void *vbo_data = GPU_vertbuf_get_data(vbo); + memcpy( + vbo_data, model_data.vertices, model_data.count_vertices * sizeof(model_data.vertices[0])); + + GPUIndexBuf *ibo = NULL; + if (model_data.count_indices > 0 && ((model_data.count_indices % 3) == 0)) { + GPUIndexBufBuilder ibo_builder; + const unsigned int prim_len = model_data.count_indices / 3; + GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, prim_len, model_data.count_vertices); + for (unsigned int i = 0; i < prim_len; ++i) { + const uint32_t *idx = &model_data.indices[i * 3]; + GPU_indexbuf_add_tri_verts(&ibo_builder, idx[0], idx[1], idx[2]); + } + ibo = GPU_indexbuf_build(&ibo_builder); + } + + return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, ibo, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX); +} + +static void wm_xr_controller_model_draw(const XrSessionSettings *settings, + GHOST_XrContextHandle xr_context, + wmXrSessionState *state) +{ + GHOST_XrControllerModelData model_data; + + float color[4]; + switch (settings->controller_draw_style) { + case XR_CONTROLLER_DRAW_DARK: + case XR_CONTROLLER_DRAW_DARK_RAY: + color[0] = color[1] = color[2] = 0.0f, color[3] = 0.4f; + break; + case XR_CONTROLLER_DRAW_LIGHT: + case XR_CONTROLLER_DRAW_LIGHT_RAY: + color[0] = 0.422f, color[1] = 0.438f, color[2] = 0.446f, color[3] = 0.4f; + break; + } + + GPU_depth_test(GPU_DEPTH_NONE); + GPU_blend(GPU_BLEND_ALPHA); + + LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) { + GPUBatch *model = controller->model; + if (!model) { + model = controller->model = wm_xr_controller_model_batch_create(xr_context, + controller->subaction_path); + } + + if (model && + GHOST_XrGetControllerModelData(xr_context, controller->subaction_path, &model_data) && + model_data.count_components > 0) { + GPU_batch_program_set_builtin(model, GPU_SHADER_3D_UNIFORM_COLOR); + GPU_batch_uniform_4fv(model, "color", color); + + GPU_matrix_push(); + GPU_matrix_mul(controller->grip_mat); + for (unsigned int component_idx = 0; component_idx < model_data.count_components; + ++component_idx) { + const GHOST_XrControllerModelComponent *component = &model_data.components[component_idx]; + GPU_matrix_push(); + GPU_matrix_mul(component->transform); + GPU_batch_draw_range(model, + model->elem ? component->index_offset : component->vertex_offset, + model->elem ? component->index_count : component->vertex_count); + GPU_matrix_pop(); + } + GPU_matrix_pop(); + } + else { + /* Fallback. */ + const float scale = 0.05f; + GPUBatch *sphere = GPU_batch_preset_sphere(2); + GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR); + GPU_batch_uniform_4fv(sphere, "color", color); + + GPU_matrix_push(); + GPU_matrix_mul(controller->grip_mat); + GPU_matrix_scale_1f(scale); + GPU_batch_draw(sphere); + GPU_matrix_pop(); + } + } +} + +static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSessionState *state) +{ + bool draw_ray; + switch (settings->controller_draw_style) { + case XR_CONTROLLER_DRAW_DARK: + case XR_CONTROLLER_DRAW_LIGHT: + draw_ray = false; + break; + case XR_CONTROLLER_DRAW_DARK_RAY: + case XR_CONTROLLER_DRAW_LIGHT_RAY: + draw_ray = true; + break; + } + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR); + + float viewport[4]; + GPU_viewport_size_get_f(viewport); + immUniform2fv("viewportSize", &viewport[2]); + + immUniform1f("lineWidth", 3.0f * U.pixelsize); + + if (draw_ray) { + const uchar color[4] = {89, 89, 255, 127}; + const float scale = settings->clip_end; + float ray[3]; + + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + GPU_blend(GPU_BLEND_ALPHA); + + immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(&state->controllers) * 2); + + LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) { + const float(*mat)[4] = controller->aim_mat; + madd_v3_v3v3fl(ray, mat[3], mat[2], -scale); + + immAttrSkip(col); + immVertex3fv(pos, mat[3]); + immAttr4ubv(col, color); + immVertex3fv(pos, ray); + } + + immEnd(); + } + else { + const uchar r[4] = {255, 51, 82, 255}; + const uchar g[4] = {139, 220, 0, 255}; + const uchar b[4] = {40, 144, 255, 255}; + const float scale = 0.01f; + float x_axis[3], y_axis[3], z_axis[3]; + + GPU_depth_test(GPU_DEPTH_NONE); + GPU_blend(GPU_BLEND_NONE); + + immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(&state->controllers) * 6); + + LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) { + const float(*mat)[4] = controller->aim_mat; + madd_v3_v3v3fl(x_axis, mat[3], mat[0], scale); + madd_v3_v3v3fl(y_axis, mat[3], mat[1], scale); + madd_v3_v3v3fl(z_axis, mat[3], mat[2], scale); + + immAttrSkip(col); + immVertex3fv(pos, mat[3]); + immAttr4ubv(col, r); + immVertex3fv(pos, x_axis); + + immAttrSkip(col); + immVertex3fv(pos, mat[3]); + immAttr4ubv(col, g); + immVertex3fv(pos, y_axis); + + immAttrSkip(col); + immVertex3fv(pos, mat[3]); + immAttr4ubv(col, b); + immVertex3fv(pos, z_axis); + } + + immEnd(); + } + + immUnbindProgram(); +} + +void wm_xr_draw_controllers(const bContext *UNUSED(C), ARegion *UNUSED(region), void *customdata) +{ + wmXrData *xr = customdata; + const XrSessionSettings *settings = &xr->session_settings; + GHOST_XrContextHandle xr_context = xr->runtime->context; + wmXrSessionState *state = &xr->runtime->session_state; + + wm_xr_controller_model_draw(settings, xr_context, state); + wm_xr_controller_aim_draw(settings, state); +} diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 8fb5424b8c2..2cd0ba5c056 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -88,6 +88,11 @@ typedef struct wmXrViewportPair { typedef struct { /** Off-screen buffers/viewports for each view. */ ListBase viewports; /* #wmXrViewportPair */ + + /** Dummy region type for controller draw callback. */ + struct ARegionType *controller_art; + /** Controller draw callback handle. */ + void *controller_draw_handle; } wmXrSurfaceData; typedef struct wmXrDrawData { @@ -114,12 +119,16 @@ typedef struct wmXrController { /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). */ char subaction_path[64]; - /* Pose (in world space) that represents the user's hand when holding the controller.*/ + + /** Pose (in world space) that represents the user's hand when holding the controller. */ GHOST_XrPose grip_pose; float grip_mat[4][4]; - /* Pose (in world space) that represents the controller's aiming source. */ + /** Pose (in world space) that represents the controller's aiming source. */ GHOST_XrPose aim_pose; float aim_mat[4][4]; + + /** Controller model. */ + struct GPUBatch *model; } wmXrController; typedef struct wmXrAction { @@ -207,3 +216,4 @@ void wm_xr_session_controller_data_clear(wmXrSessionState *state); void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]); void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata); +void wm_xr_draw_controllers(const struct bContext *C, struct ARegion *region, void *customdata); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 88bc5c45c91..9f53db1347c 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -24,6 +24,7 @@ #include "BKE_idprop.h" #include "BKE_main.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -36,9 +37,11 @@ #include "DRW_engine.h" #include "ED_screen.h" +#include "ED_space_api.h" #include "GHOST_C-api.h" +#include "GPU_batch.h" #include "GPU_viewport.h" #include "MEM_guardedalloc.h" @@ -72,7 +75,15 @@ static void wm_xr_session_create_cb(void) static void wm_xr_session_controller_data_free(wmXrSessionState *state) { - BLI_freelistN(&state->controllers); + ListBase *lb = &state->controllers; + wmXrController *c; + + while ((c = BLI_pophead(lb))) { + if (c->model) { + GPU_batch_discard(c->model); + } + BLI_freelinkN(lb, c); + } } void wm_xr_session_data_free(wmXrSessionState *state) @@ -529,6 +540,7 @@ static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose, static void wm_xr_session_controller_data_update(const XrSessionSettings *settings, const wmXrAction *grip_action, const wmXrAction *aim_action, + GHOST_XrContextHandle xr_context, wmXrSessionState *state) { BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths); @@ -560,6 +572,16 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin base_mat, &controller->aim_pose, controller->aim_mat); + + if (!controller->model) { + /* Notify GHOST to load/continue loading the controller model data. This can be called more + * than once since the model may not be available from the runtime yet. The batch itself will + * be created in wm_xr_draw_controllers(). */ + GHOST_XrLoadControllerModel(xr_context, controller->subaction_path); + } + else { + GHOST_XrUpdateControllerModelComponents(xr_context, controller->subaction_path); + } } } @@ -1089,6 +1111,7 @@ void wm_xr_session_actions_update(wmWindowManager *wm) wm_xr_session_controller_data_update(&xr->session_settings, active_action_set->controller_grip_action, active_action_set->controller_aim_action, + xr_context, state); } @@ -1125,11 +1148,33 @@ void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, BLI_addtail(controllers, controller); } + + /* Activate draw callback. */ + if (g_xr_surface) { + wmXrSurfaceData *surface_data = g_xr_surface->customdata; + if (surface_data && !surface_data->controller_draw_handle) { + if (surface_data->controller_art) { + surface_data->controller_draw_handle = ED_region_draw_cb_activate( + surface_data->controller_art, wm_xr_draw_controllers, xr, REGION_DRAW_POST_VIEW); + } + } + } } void wm_xr_session_controller_data_clear(wmXrSessionState *state) { wm_xr_session_controller_data_free(state); + + /* Deactivate draw callback. */ + if (g_xr_surface) { + wmXrSurfaceData *surface_data = g_xr_surface->customdata; + if (surface_data && surface_data->controller_draw_handle) { + if (surface_data->controller_art) { + ED_region_draw_cb_exit(surface_data->controller_art, surface_data->controller_draw_handle); + } + surface_data->controller_draw_handle = NULL; + } + } } /** \} */ /* XR-Session Actions */ @@ -1255,6 +1300,11 @@ static void wm_xr_session_surface_free_data(wmSurface *surface) BLI_freelinkN(lb, vp); } + if (data->controller_art) { + BLI_freelistN(&data->controller_art->drawcalls); + MEM_freeN(data->controller_art); + } + MEM_freeN(surface->customdata); g_xr_surface = NULL; @@ -1269,6 +1319,7 @@ static wmSurface *wm_xr_session_surface_create(void) wmSurface *surface = MEM_callocN(sizeof(*surface), __func__); wmXrSurfaceData *data = MEM_callocN(sizeof(*data), "XrSurfaceData"); + data->controller_art = MEM_callocN(sizeof(*(data->controller_art)), "XrControllerRegionType"); surface->draw = wm_xr_session_surface_draw; surface->free_data = wm_xr_session_surface_free_data; @@ -1278,6 +1329,7 @@ static wmSurface *wm_xr_session_surface_create(void) surface->ghost_ctx = DRW_xr_opengl_context_get(); surface->gpu_ctx = DRW_xr_gpu_context_get(); + data->controller_art->regionid = RGN_TYPE_XR; surface->customdata = data; g_xr_surface = surface; @@ -1311,4 +1363,14 @@ void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle UNUSED(contex WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL); } +ARegionType *WM_xr_surface_controller_region_type_get(void) +{ + if (g_xr_surface) { + wmXrSurfaceData *data = g_xr_surface->customdata; + return data->controller_art; + } + + return NULL; +} + /** \} */ /* XR-Session Surface */ From e8dc02aa3f219db634a2359619b6581a3879792d Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 12 Oct 2021 16:36:56 +0900 Subject: [PATCH 0703/1500] Fix build error due to conflicting types --- source/blender/editors/space_view3d/view3d_draw.c | 2 +- source/blender/windowmanager/xr/intern/wm_xr_draw.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 4da24888e1d..151fa02cd5c 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1758,7 +1758,7 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph, Scene *scene, View3DShading *shading_override, - int drawtype, + eDrawType drawtype, int winx, int winy, uint draw_flags, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index 6b3756c8178..628d50f05bd 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -150,7 +150,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) ED_view3d_draw_offscreen_simple(draw_data->depsgraph, draw_data->scene, &settings->shading, - settings->shading.type, + (eDrawType)settings->shading.type, draw_view->width, draw_view->height, display_flags, From 690382bef5c38d9279d72b36660c466114afc774 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 12 Oct 2021 09:46:46 +0200 Subject: [PATCH 0704/1500] Fix incorrect commet in appdir.c about cache locations. --- source/blender/blenkernel/intern/appdir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 5779c873222..0ecf6238d3d 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -219,7 +219,8 @@ bool BKE_appdir_folder_documents(char *dir) /** * Get the user's cache directory, i.e. $HOME/.cache/blender/ on Linux, - * %USERPROFILE%\AppData\Local\blender\ on Windows. + * %USERPROFILE%\AppData\Local\Blender Foundation\Blender\ on Windows and + * /Library/Caches/Blender on MacOS. * * \returns True if the path is valid. It doesn't create or checks format * if the `blender` folder exists. It does check if the parent of the From 29e5dc1b197908e09d0872267500b79c8f41c317 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 12 Oct 2021 09:49:06 +0200 Subject: [PATCH 0705/1500] WindowManager: Keep track or the source library when appending. Keep track of the source library allowing other parts of the code to to make better decisions. This is needed to localize external files. In this case the file paths are updated when `making local`. But we should decide based on the source library if we want to copy the file relative to the new blend file. See D12423. Reviewed By: mont29, Severin Differential Revision: https://developer.blender.org/D12765 --- source/blender/windowmanager/intern/wm_files_link.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 12ab10e3a70..50ab4f338a6 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -177,6 +177,7 @@ typedef struct WMLinkAppendDataItem { char append_tag; ID *new_id; + Library *source_library; void *customdata; } WMLinkAppendDataItem; @@ -622,6 +623,7 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) if (item == NULL) { item = wm_link_append_data_item_add(data->lapp_data, id->name, GS(id->name), NULL); item->new_id = id; + item->source_library = id->lib; /* Since we did not have an item for that ID yet, we now user did not selected it explicitly, * it was rather linked indirectly. This info is important for instantiation of collections. */ item->append_tag |= WM_APPEND_TAG_INDIRECT; @@ -1004,6 +1006,7 @@ static void wm_link_do(WMLinkAppendData *lapp_data, * This avoids trying to link same item with other libraries to come. */ BLI_bitmap_set_all(item->libraries, false, lapp_data->num_libraries); item->new_id = new_id; + item->source_library = new_id->lib; } } From ac657bee0142f96fcd3fa5d56455658834a19b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 12 Oct 2021 11:22:48 +0200 Subject: [PATCH 0706/1500] Tests: simplify asset catalog tree testing code Simplify the testing code that verifies the asset catalog tree. It now prints clearer error messages when things go wrong, and it gets simpler data to test (instead of having to explicitly pass the parent count, it just counts the number of separators in the expected path). No functional changes to Blender. --- .../blenkernel/BKE_asset_catalog_path.hh | 3 + .../blenkernel/intern/asset_catalog_path.cc | 10 ++ .../intern/asset_catalog_path_test.cc | 9 ++ .../blenkernel/intern/asset_catalog_test.cc | 124 +++++++++--------- 4 files changed, 84 insertions(+), 62 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog_path.hh b/source/blender/blenkernel/BKE_asset_catalog_path.hh index 328625b1bfa..054b7853140 100644 --- a/source/blender/blenkernel/BKE_asset_catalog_path.hh +++ b/source/blender/blenkernel/BKE_asset_catalog_path.hh @@ -76,6 +76,9 @@ class AssetCatalogPath { const char *c_str() const; const std::string &str() const; + /* The last path component, used as label in the tree view. */ + StringRefNull name() const; + /* In-class operators, because of the implicit `AssetCatalogPath(StringRef)` constructor. * Otherwise `string == string` could cast both sides to `AssetCatalogPath`. */ bool operator==(const AssetCatalogPath &other_path) const; diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc index 85b8969cb8c..fec2b76e7a1 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path.cc @@ -64,6 +64,16 @@ const std::string &AssetCatalogPath::str() const return this->path_; } +StringRefNull AssetCatalogPath::name() const +{ + const size_t last_sep_index = this->path_.rfind(SEPARATOR); + if (last_sep_index == std::string::npos) { + return StringRefNull(this->path_); + } + + return StringRefNull(this->path_.c_str() + last_sep_index + 1); +} + /* In-class operators, because of the implicit `AssetCatalogPath(StringRef)` constructor. * Otherwise `string == string` could cast both sides to `AssetCatalogPath`. */ bool AssetCatalogPath::operator==(const AssetCatalogPath &other_path) const diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc index 6110217a0fb..d8da91d5d18 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc @@ -58,6 +58,15 @@ TEST(AssetCatalogPathTest, length) EXPECT_EQ(21, utf8.length()) << "13 characters should be 21 bytes."; } +TEST(AssetCatalogPathTest, name) +{ + EXPECT_EQ(StringRefNull(""), AssetCatalogPath("").name()); + EXPECT_EQ(StringRefNull("word"), AssetCatalogPath("word").name()); + EXPECT_EQ(StringRefNull("Пермь"), AssetCatalogPath("дорога/в/Пермь").name()); + EXPECT_EQ(StringRefNull("windows\\paths"), + AssetCatalogPath("these/are/not/windows\\paths").name()); +} + TEST(AssetCatalogPathTest, comparison_operators) { const AssetCatalogPath empty(""); diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index dc39cefed5a..69efab76b43 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -116,21 +116,23 @@ class AssetCatalogTest : public testing::Test { return path; } - struct CatalogPathInfo { - StringRef name; - int parent_count; - }; - - void assert_expected_item(const CatalogPathInfo &expected_path, + void assert_expected_item(const AssetCatalogPath &expected_path, const AssetCatalogTreeItem &actual_item) { - char expected_filename[FILE_MAXFILE]; + if (expected_path != actual_item.catalog_path().str()) { + /* This will fail, but with a nicer error message than just calling FAIL(). */ + EXPECT_EQ(expected_path, actual_item.catalog_path()); + return; + } + /* Is the catalog name as expected? "character", "Ellie", ... */ - BLI_split_file_part(expected_path.name.data(), expected_filename, sizeof(expected_filename)); - EXPECT_EQ(expected_filename, actual_item.get_name()); + EXPECT_EQ(expected_path.name(), actual_item.get_name()); + /* Does the computed number of parents match? */ - EXPECT_EQ(expected_path.parent_count, actual_item.count_parents()); - EXPECT_EQ(expected_path.name, actual_item.catalog_path().str()); + const std::string expected_path_str = expected_path.str(); + const size_t expected_parent_count = std::count( + expected_path_str.begin(), expected_path_str.end(), AssetCatalogPath::SEPARATOR); + EXPECT_EQ(expected_parent_count, actual_item.count_parents()); } /** @@ -138,7 +140,7 @@ class AssetCatalogTest : public testing::Test { * the items map exactly to \a expected_paths. */ void assert_expected_tree_items(AssetCatalogTree *tree, - const std::vector &expected_paths) + const std::vector &expected_paths) { int i = 0; tree->foreach_item([&](const AssetCatalogTreeItem &actual_item) { @@ -155,7 +157,7 @@ class AssetCatalogTest : public testing::Test { * #AssetCatalogTree::foreach_root_item() instead of #AssetCatalogTree::foreach_item(). */ void assert_expected_tree_root_items(AssetCatalogTree *tree, - const std::vector &expected_paths) + const std::vector &expected_paths) { int i = 0; tree->foreach_root_item([&](const AssetCatalogTreeItem &actual_item) { @@ -173,7 +175,7 @@ class AssetCatalogTest : public testing::Test { * #AssetCatalogTreeItem::foreach_child() instead of #AssetCatalogTree::foreach_item(). */ void assert_expected_tree_item_child_items(AssetCatalogTreeItem *parent_item, - const std::vector &expected_paths) + const std::vector &expected_paths) { int i = 0; parent_item->foreach_child([&](const AssetCatalogTreeItem &actual_item) { @@ -305,32 +307,30 @@ TEST_F(AssetCatalogTest, insert_item_into_tree) std::unique_ptr catalog = AssetCatalog::from_path("item"); tree.insert_item(*catalog); - assert_expected_tree_items(&tree, {{"item", 0}}); + assert_expected_tree_items(&tree, {"item"}); /* Insert child after parent already exists. */ std::unique_ptr child_catalog = AssetCatalog::from_path("item/child"); tree.insert_item(*catalog); - assert_expected_tree_items(&tree, {{"item", 0}, {"item/child", 1}}); + assert_expected_tree_items(&tree, {"item", "item/child"}); - std::vector expected_paths; + std::vector expected_paths; /* Test inserting multi-component sub-path. */ std::unique_ptr grandgrandchild_catalog = AssetCatalog::from_path( "item/child/grandchild/grandgrandchild"); tree.insert_item(*catalog); - expected_paths = {{"item", 0}, - {"item/child", 1}, - {"item/child/grandchild", 2}, - {"item/child/grandchild/grandgrandchild", 3}}; + expected_paths = { + "item", "item/child", "item/child/grandchild", "item/child/grandchild/grandgrandchild"}; assert_expected_tree_items(&tree, expected_paths); std::unique_ptr root_level_catalog = AssetCatalog::from_path("root level"); tree.insert_item(*catalog); - expected_paths = {{"item", 0}, - {"item/child", 1}, - {"item/child/grandchild", 2}, - {"item/child/grandchild/grandgrandchild", 3}, - {"root level", 0}}; + expected_paths = {"item", + "item/child", + "item/child/grandchild", + "item/child/grandchild/grandgrandchild", + "root level"}; assert_expected_tree_items(&tree, expected_paths); } @@ -339,7 +339,7 @@ TEST_F(AssetCatalogTest, insert_item_into_tree) std::unique_ptr catalog = AssetCatalog::from_path("item/child"); tree.insert_item(*catalog); - assert_expected_tree_items(&tree, {{"item", 0}, {"item/child", 1}}); + assert_expected_tree_items(&tree, {"item", "item/child"}); } { @@ -347,7 +347,7 @@ TEST_F(AssetCatalogTest, insert_item_into_tree) std::unique_ptr catalog = AssetCatalog::from_path("white space"); tree.insert_item(*catalog); - assert_expected_tree_items(&tree, {{"white space", 0}}); + assert_expected_tree_items(&tree, {"white space"}); } { @@ -355,7 +355,7 @@ TEST_F(AssetCatalogTest, insert_item_into_tree) std::unique_ptr catalog = AssetCatalog::from_path("/item/white space"); tree.insert_item(*catalog); - assert_expected_tree_items(&tree, {{"item", 0}, {"item/white space", 1}}); + assert_expected_tree_items(&tree, {"item", "item/white space"}); } { @@ -363,11 +363,11 @@ TEST_F(AssetCatalogTest, insert_item_into_tree) std::unique_ptr catalog_unicode_path = AssetCatalog::from_path("Ružena"); tree.insert_item(*catalog_unicode_path); - assert_expected_tree_items(&tree, {{"Ružena", 0}}); + assert_expected_tree_items(&tree, {"Ružena"}); catalog_unicode_path = AssetCatalog::from_path("Ružena/Ružena"); tree.insert_item(*catalog_unicode_path); - assert_expected_tree_items(&tree, {{"Ružena", 0}, {"Ružena/Ružena", 1}}); + assert_expected_tree_items(&tree, {"Ružena", "Ružena/Ružena"}); } } @@ -378,19 +378,19 @@ TEST_F(AssetCatalogTest, load_single_file_into_tree) /* Contains not only paths from the CDF but also the missing parents (implicitly defined * catalogs). */ - std::vector expected_paths{ - {"character", 0}, - {"character/Ellie", 1}, - {"character/Ellie/poselib", 2}, - {"character/Ellie/poselib/tailslash", 3}, - {"character/Ellie/poselib/white space", 3}, - {"character/Ružena", 1}, - {"character/Ružena/poselib", 2}, - {"character/Ružena/poselib/face", 3}, - {"character/Ružena/poselib/hand", 3}, - {"path", 0}, /* Implicit. */ - {"path/without", 1}, /* Implicit. */ - {"path/without/simplename", 2}, /* From CDF. */ + std::vector expected_paths{ + "character", + "character/Ellie", + "character/Ellie/poselib", + "character/Ellie/poselib/tailslash", + "character/Ellie/poselib/white space", + "character/Ružena", + "character/Ružena/poselib", + "character/Ružena/poselib/face", + "character/Ružena/poselib/hand", + "path", /* Implicit. */ + "path/without", /* Implicit. */ + "path/without/simplename", /* From CDF. */ }; AssetCatalogTree *tree = service.get_catalog_tree(); @@ -401,7 +401,7 @@ TEST_F(AssetCatalogTest, foreach_in_tree) { { AssetCatalogTree tree{}; - const std::vector no_catalogs{}; + const std::vector no_catalogs{}; assert_expected_tree_items(&tree, no_catalogs); assert_expected_tree_root_items(&tree, no_catalogs); @@ -416,16 +416,16 @@ TEST_F(AssetCatalogTest, foreach_in_tree) AssetCatalogService service(asset_library_root_); service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); - std::vector expected_root_items{{"character", 0}, {"path", 0}}; + std::vector expected_root_items{{"character", "path"}}; AssetCatalogTree *tree = service.get_catalog_tree(); assert_expected_tree_root_items(tree, expected_root_items); /* Test if the direct children of the root item are what's expected. */ - std::vector> expected_root_child_items = { + std::vector> expected_root_child_items = { /* Children of the "character" root item. */ - {{"character/Ellie", 1}, {"character/Ružena", 1}}, + {"character/Ellie", "character/Ružena"}, /* Children of the "path" root item. */ - {{"path/without", 1}}, + {"path/without"}, }; int i = 0; tree->foreach_root_item([&expected_root_child_items, &i, this](AssetCatalogTreeItem &item) { @@ -724,19 +724,19 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf) /* Contains not only paths from the CDF but also the missing parents (implicitly defined * catalogs). This is why a leaf catalog was deleted. */ - std::vector expected_paths{ - {"character", 0}, - {"character/Ellie", 1}, - {"character/Ellie/poselib", 2}, - {"character/Ellie/poselib/tailslash", 3}, - {"character/Ellie/poselib/white space", 3}, - {"character/Ružena", 1}, - {"character/Ružena/poselib", 2}, - {"character/Ružena/poselib/face", 3}, - // {"character/Ružena/poselib/hand", 3}, /* This is the deleted one. */ - {"path", 0}, - {"path/without", 1}, - {"path/without/simplename", 2}, + std::vector expected_paths{ + "character", + "character/Ellie", + "character/Ellie/poselib", + "character/Ellie/poselib/tailslash", + "character/Ellie/poselib/white space", + "character/Ružena", + "character/Ružena/poselib", + "character/Ružena/poselib/face", + // "character/Ružena/poselib/hand", /* This is the deleted one. */ + "path", + "path/without", + "path/without/simplename", }; AssetCatalogTree *tree = service.get_catalog_tree(); From ad1735f8ede966b7d49423928ad05cca25119949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 12 Oct 2021 11:28:16 +0200 Subject: [PATCH 0707/1500] Asset Catalogs: recursive deletion of catalogs & children Recursively delete asset catalogs with `AssetCatalogService:prune_...` functions. This deletes the catalog and all of its children. The old `delete_catalog` function has been renamed to `delete_catalog_by_id()`, and is now a lower-level function (no deletion of children, no rebuilding of the tree). The `prune_catalogs_by_path()` and `prune_catalogs_by_id()` do delete children and do rebuild the catalog tree. Manifest task: T91634 --- .../blender/blenkernel/BKE_asset_catalog.hh | 18 +++++- .../blenkernel/intern/asset_catalog.cc | 31 +++++++++- .../blenkernel/intern/asset_catalog_test.cc | 58 ++++++++++++++++++- .../editors/asset/intern/asset_catalog.cc | 2 +- 4 files changed, 100 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index bd18fdf1d6e..4ea6abd65e0 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -117,9 +117,21 @@ class AssetCatalogService { AssetCatalog *create_catalog(const AssetCatalogPath &catalog_path); /** - * Soft-delete the catalog, ensuring it actually gets deleted when the catalog definition file is - * written. */ - void delete_catalog(CatalogID catalog_id); + * Delete all catalogs with the given path, and their children. + */ + void prune_catalogs_by_path(const AssetCatalogPath &path); + + /** + * Delete all catalogs with the same path as the identified catalog, and their children. + * This call is the same as calling `prune_catalogs_by_path(find_catalog(catalog_id)->path)`. + */ + void prune_catalogs_by_id(CatalogID catalog_id); + + /** + * Delete a catalog, without deleting any of its children and without rebuilding the catalog + * tree. This is a lower-level function than #prune_catalogs_by_path. + */ + void delete_catalog_by_id(CatalogID catalog_id); /** * Update the catalog path, also updating the catalog path of all sub-catalogs. diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index ab7d8eafb8b..4531dabf0cf 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -112,7 +112,7 @@ AssetCatalogFilter AssetCatalogService::create_catalog_filter( return AssetCatalogFilter(std::move(matching_catalog_ids)); } -void AssetCatalogService::delete_catalog(CatalogID catalog_id) +void AssetCatalogService::delete_catalog_by_id(const CatalogID catalog_id) { std::unique_ptr *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id); if (catalog_uptr_ptr == nullptr) { @@ -129,11 +129,38 @@ void AssetCatalogService::delete_catalog(CatalogID catalog_id) /* The catalog can now be removed from the map without freeing the actual AssetCatalog. */ this->catalogs_.remove(catalog_id); +} + +void AssetCatalogService::prune_catalogs_by_path(const AssetCatalogPath &path) +{ + /* Build a collection of catalog IDs to delete. */ + Set catalogs_to_delete; + for (const auto &catalog_uptr : this->catalogs_.values()) { + const AssetCatalog *cat = catalog_uptr.get(); + if (cat->path.is_contained_in(path)) { + catalogs_to_delete.add(cat->catalog_id); + } + } + + /* Delete the catalogs. */ + for (const CatalogID cat_id : catalogs_to_delete) { + this->delete_catalog_by_id(cat_id); + } this->rebuild_tree(); } -void AssetCatalogService::update_catalog_path(CatalogID catalog_id, +void AssetCatalogService::prune_catalogs_by_id(const CatalogID catalog_id) +{ + const AssetCatalog *catalog = find_catalog(catalog_id); + BLI_assert_msg(catalog, "trying to prune asset catalogs by the path of a non-existent catalog"); + if (!catalog) { + return; + } + this->prune_catalogs_by_path(catalog->path); +} + +void AssetCatalogService::update_catalog_path(const CatalogID catalog_id, const AssetCatalogPath &new_catalog_path) { AssetCatalog *renamed_cat = this->find_catalog(catalog_id); diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 69efab76b43..cf06638bcdd 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -719,7 +719,7 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf) /* Delete a leaf catalog, i.e. one that is not a parent of another catalog. * This keeps this particular test easy. */ - service.delete_catalog(UUID_POSES_RUZENA_HAND); + service.prune_catalogs_by_id(UUID_POSES_RUZENA_HAND); EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_HAND)); /* Contains not only paths from the CDF but also the missing parents (implicitly defined @@ -743,13 +743,65 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf) assert_expected_tree_items(tree, expected_paths); } +TEST_F(AssetCatalogTest, delete_catalog_parent_by_id) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + /* Delete a parent catalog. */ + service.delete_catalog_by_id(UUID_POSES_RUZENA); + + /* The catalog should have been deleted, but its children should still be there. */ + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_RUZENA_FACE)); + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_RUZENA_HAND)); +} + +TEST_F(AssetCatalogTest, delete_catalog_parent_by_path) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + /* Create an extra catalog with the to-be-deleted path, and one with a child of that. + * This creates some duplicates that are bound to occur in production asset libraries as well. */ + const bUUID cat1_uuid = service.create_catalog("character/Ružena/poselib")->catalog_id; + const bUUID cat2_uuid = service.create_catalog("character/Ružena/poselib/body")->catalog_id; + + /* Delete a parent catalog. */ + service.prune_catalogs_by_path("character/Ružena/poselib"); + + /* The catalogs and their children should have been deleted. */ + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_FACE)); + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_EQ(nullptr, service.find_catalog(cat1_uuid)); + EXPECT_EQ(nullptr, service.find_catalog(cat2_uuid)); + + /* Contains not only paths from the CDF but also the missing parents (implicitly defined + * catalogs). This is why a leaf catalog was deleted. */ + std::vector expected_paths{ + "character", + "character/Ellie", + "character/Ellie/poselib", + "character/Ellie/poselib/tailslash", + "character/Ellie/poselib/white space", + "character/Ružena", + "path", + "path/without", + "path/without/simplename", + }; + + AssetCatalogTree *tree = service.get_catalog_tree(); + assert_expected_tree_items(tree, expected_paths); +} + TEST_F(AssetCatalogTest, delete_catalog_write_to_disk) { TestableAssetCatalogService service(asset_library_root_); service.load_from_disk(asset_library_root_ + "/" + AssetCatalogService::DEFAULT_CATALOG_FILENAME); - service.delete_catalog(UUID_POSES_ELLIE); + service.delete_catalog_by_id(UUID_POSES_ELLIE); const CatalogFilePath save_to_path = use_temp_path(); AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); @@ -844,7 +896,7 @@ TEST_F(AssetCatalogTest, backups) /* Read a CDF, modify, and write it. */ AssetCatalogService service(cdf_dir); service.load_from_disk(); - service.delete_catalog(UUID_POSES_ELLIE); + service.delete_catalog_by_id(UUID_POSES_ELLIE); service.write_to_disk_on_blendfile_save(cdf_dir + "phony.blend"); const CatalogFilePath backup_path = writable_cdf_file + "~"; diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index 6e49ca2dd5c..eb1865ee9cc 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -77,5 +77,5 @@ void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_i return; } - catalog_service->delete_catalog(catalog_id); + catalog_service->prune_catalogs_by_id(catalog_id); } From cc043999378dbe04e1cc601c01b43dc0b727e553 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 12 Oct 2021 11:54:33 +0200 Subject: [PATCH 0708/1500] Fix missing Cycles volume stack re-allocation Need to check allocation size, as the features do not change with volume stack depth detection. --- intern/cycles/integrator/path_trace_work_gpu.cpp | 8 ++++++-- intern/cycles/integrator/path_trace_work_gpu.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 706fc5799d0..df393bac0de 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -97,11 +97,15 @@ void PathTraceWorkGPU::alloc_integrator_soa() /* IntegrateState allocated as structure of arrays. */ /* Check if we already allocated memory for the required features. */ + const int requested_volume_stack_size = device_scene_->data.volume_stack_size; const uint kernel_features = device_scene_->data.kernel_features; - if ((integrator_state_soa_kernel_features_ & kernel_features) == kernel_features) { + if ((integrator_state_soa_kernel_features_ & kernel_features) == kernel_features && + integrator_state_soa_volume_stack_size_ >= requested_volume_stack_size) { return; } integrator_state_soa_kernel_features_ = kernel_features; + integrator_state_soa_volume_stack_size_ = max(integrator_state_soa_volume_stack_size_, + requested_volume_stack_size); /* Allocate a device only memory buffer before for each struct member, and then * write the pointers into a struct that resides in constant memory. @@ -133,7 +137,7 @@ void PathTraceWorkGPU::alloc_integrator_soa() break; \ } \ } -#define KERNEL_STRUCT_VOLUME_STACK_SIZE (device_scene_->data.volume_stack_size) +#define KERNEL_STRUCT_VOLUME_STACK_SIZE (integrator_state_soa_volume_stack_size_) #include "kernel/integrator/integrator_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h index 9212537d2fd..e66851cc8d8 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.h +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -124,6 +124,7 @@ class PathTraceWorkGPU : public PathTraceWork { /* SoA arrays for integrator state. */ vector> integrator_state_soa_; uint integrator_state_soa_kernel_features_; + int integrator_state_soa_volume_stack_size_ = 0; /* Keep track of number of queued kernels. */ device_vector integrator_queue_counter_; /* Shader sorting. */ From b67a9373945d07700b378aaa8be06aa2fd6d2cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 12 Oct 2021 12:17:42 +0200 Subject: [PATCH 0709/1500] Assets: hide low-level catalog info behind "developer extra's" Hide the catalog UUID and the catalog simple name from the Asset Browser side-panel, unless "Developer Extra's" is enabled in the user prefs. --- .../startup/bl_ui/space_filebrowser.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index f54a2446c7e..3f2b11ee3a8 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -658,25 +658,29 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): asset_library_ref = context.asset_library_ref asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref) + show_developer_ui = context.preferences.view.show_developer_ui + if asset_file_handle.local_id: # If the active file is an ID, use its name directly so renaming is possible from right here. layout.prop(asset_file_handle.local_id, "name", text="") - col = layout.column(align=True) - col.label(text="Asset Catalog:") - col.prop(asset_file_handle.local_id.asset_data, "catalog_id", text="UUID") - col.prop(asset_file_handle.local_id.asset_data, "catalog_simple_name", text="Simple Name") + if show_developer_ui: + col = layout.column(align=True) + col.label(text="Asset Catalog:") + col.prop(asset_file_handle.local_id.asset_data, "catalog_id", text="UUID") + col.prop(asset_file_handle.local_id.asset_data, "catalog_simple_name", text="Simple Name") row = layout.row() row.label(text="Source: Current File") else: layout.prop(asset_file_handle, "name", text="") - col = layout.column(align=True) - col.enabled = False - col.label(text="Asset Catalog:") - col.prop(asset_file_handle.asset_data, "catalog_id", text="UUID") - col.prop(asset_file_handle.asset_data, "catalog_simple_name", text="Simple Name") + if show_developer_ui: + col = layout.column(align=True) + col.enabled = False + col.label(text="Asset Catalog:") + col.prop(asset_file_handle.asset_data, "catalog_id", text="UUID") + col.prop(asset_file_handle.asset_data, "catalog_simple_name", text="Simple Name") col = layout.column(align=True) # Just to reduce margin. col.label(text="Source:") From a06435e43a6516207c617c2b4ad9961f865b66d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 12 Oct 2021 12:39:24 +0200 Subject: [PATCH 0710/1500] Asset Catalogs: undo stack for catalog edits Add an undo stack for catalog edits. This only implements the backend, no operators or UI yet. A bunch of `this->xxx` has been replaced by `catalog_collection_->xxx`. Things are getting a bit long, and the class is turning into a god object; refactoring the class is tracked in T92114. Reviewed By: Severin Maniphest Tasks: T92047 Differential Revision: https://developer.blender.org/D12825 --- .../blender/blenkernel/BKE_asset_catalog.hh | 64 +++++- .../blenkernel/intern/asset_catalog.cc | 181 +++++++++++++---- .../blenkernel/intern/asset_catalog_test.cc | 187 +++++++++++++++++- 3 files changed, 390 insertions(+), 42 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 4ea6abd65e0..b677adefb46 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -60,7 +60,7 @@ class AssetCatalogService { static const CatalogFilePath DEFAULT_CATALOG_FILENAME; public: - AssetCatalogService() = default; + AssetCatalogService(); explicit AssetCatalogService(const CatalogFilePath &asset_library_root); /** Load asset catalog definitions from the files found in the asset library. */ @@ -143,14 +143,30 @@ class AssetCatalogService { /** Return true only if there are no catalogs known. */ bool is_empty() const; + /** + * Store the current catalogs in the undo stack. + * This snapshots everything in the #AssetCatalogCollection. */ + void store_undo_snapshot(); + /** + * Restore the last-saved undo snapshot, pushing the current state onto the redo stack. + * The caller is responsible for first checking that undoing is possible. + */ + void undo(); + bool is_undo_possbile() const; + /** + * Restore the last-saved redo snapshot, pushing the current state onto the undo stack. + * The caller is responsible for first checking that undoing is possible. */ + void redo(); + bool is_redo_possbile() const; + protected: - /* These pointers are owned by this AssetCatalogService. */ - OwningAssetCatalogMap catalogs_; - OwningAssetCatalogMap deleted_catalogs_; - std::unique_ptr catalog_definition_file_; + std::unique_ptr catalog_collection_; std::unique_ptr catalog_tree_ = std::make_unique(); CatalogFilePath asset_library_root_; + Vector> undo_snapshots_; + Vector> redo_snapshots_; + void load_directory_recursive(const CatalogFilePath &directory_path); void load_single_file(const CatalogFilePath &catalog_definition_file_path); @@ -179,6 +195,41 @@ class AssetCatalogService { * For every catalog, ensure that its parent path also has a known catalog. */ void create_missing_catalogs(); + + /* For access by subclasses, as those will not be marked as friend by #AssetCatalogCollection. */ + AssetCatalogDefinitionFile *get_catalog_definition_file(); + OwningAssetCatalogMap &get_catalogs(); +}; + +/** + * All catalogs that are owned by a single asset library, and managed by a single instance of + * #AssetCatalogService. The undo system for asset catalog edits contains historical copies of this + * struct. + */ +class AssetCatalogCollection { + friend AssetCatalogService; + + public: + AssetCatalogCollection() = default; + AssetCatalogCollection(const AssetCatalogCollection &other) = delete; + AssetCatalogCollection(AssetCatalogCollection &&other) noexcept = default; + + std::unique_ptr deep_copy() const; + + protected: + /** All catalogs known, except the known-but-deleted ones. */ + OwningAssetCatalogMap catalogs_; + + /** Catalogs that have been deleted. They are kept around so that the load-merge-save of catalog + * definition files can actually delete them if they already existed on disk (instead of the + * merge operation resurrecting them). */ + OwningAssetCatalogMap deleted_catalogs_; + + /* For now only a single catalog definition file is supported. + * The aim is to support an arbitrary number of such files per asset library in the future. */ + std::unique_ptr catalog_definition_file_; + + static OwningAssetCatalogMap copy_catalog_map(const OwningAssetCatalogMap &orig); }; /** @@ -292,6 +343,9 @@ class AssetCatalogDefinitionFile { void parse_catalog_file(const CatalogFilePath &catalog_definition_file_path, AssetCatalogParsedFn callback); + std::unique_ptr copy_and_remap( + const OwningAssetCatalogMap &catalogs, const OwningAssetCatalogMap &deleted_catalogs) const; + protected: /* Catalogs stored in this file. They are mapped by ID to make it possible to query whether a * catalog is already known, without having to find the corresponding `AssetCatalog*`. */ diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 4531dabf0cf..1b8ab6f7196 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -55,19 +55,36 @@ const std::string AssetCatalogDefinitionFile::HEADER = "# The first non-ignored line should be the version indicator.\n" "# Other lines are of the format \"UUID:catalog/path/for/assets:simple catalog name\"\n"; +AssetCatalogService::AssetCatalogService() + : catalog_collection_(std::make_unique()) +{ +} + AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_root) - : asset_library_root_(asset_library_root) + : catalog_collection_(std::make_unique()), + asset_library_root_(asset_library_root) { } bool AssetCatalogService::is_empty() const { - return catalogs_.is_empty(); + return catalog_collection_->catalogs_.is_empty(); +} + +OwningAssetCatalogMap &AssetCatalogService::get_catalogs() +{ + return catalog_collection_->catalogs_; +} + +AssetCatalogDefinitionFile *AssetCatalogService::get_catalog_definition_file() +{ + return catalog_collection_->catalog_definition_file_.get(); } AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) const { - const std::unique_ptr *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id); + const std::unique_ptr *catalog_uptr_ptr = + catalog_collection_->catalogs_.lookup_ptr(catalog_id); if (catalog_uptr_ptr == nullptr) { return nullptr; } @@ -76,7 +93,7 @@ AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) const AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath &path) const { - for (const auto &catalog : catalogs_.values()) { + for (const auto &catalog : catalog_collection_->catalogs_.values()) { if (catalog->path == path) { return catalog.get(); } @@ -103,7 +120,7 @@ AssetCatalogFilter AssetCatalogService::create_catalog_filter( * then only do an exact match on the path (instead of the more complex `is_contained_in()` * call). Without an extra indexed-by-path acceleration structure, this is still going to require * a linear search, though. */ - for (const auto &catalog_uptr : this->catalogs_.values()) { + for (const auto &catalog_uptr : catalog_collection_->catalogs_.values()) { if (catalog_uptr->path.is_contained_in(active_catalog->path)) { matching_catalog_ids.add(catalog_uptr->catalog_id); } @@ -114,7 +131,8 @@ AssetCatalogFilter AssetCatalogService::create_catalog_filter( void AssetCatalogService::delete_catalog_by_id(const CatalogID catalog_id) { - std::unique_ptr *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id); + std::unique_ptr *catalog_uptr_ptr = catalog_collection_->catalogs_.lookup_ptr( + catalog_id); if (catalog_uptr_ptr == nullptr) { /* Catalog cannot be found, which is fine. */ return; @@ -124,18 +142,19 @@ void AssetCatalogService::delete_catalog_by_id(const CatalogID catalog_id) AssetCatalog *catalog = catalog_uptr_ptr->get(); catalog->flags.is_deleted = true; - /* Move ownership from this->catalogs_ to this->deleted_catalogs_. */ - this->deleted_catalogs_.add(catalog_id, std::move(*catalog_uptr_ptr)); + /* Move ownership from catalog_collection_->catalogs_ to catalog_collection_->deleted_catalogs_. + */ + catalog_collection_->deleted_catalogs_.add(catalog_id, std::move(*catalog_uptr_ptr)); /* The catalog can now be removed from the map without freeing the actual AssetCatalog. */ - this->catalogs_.remove(catalog_id); + catalog_collection_->catalogs_.remove(catalog_id); } void AssetCatalogService::prune_catalogs_by_path(const AssetCatalogPath &path) { /* Build a collection of catalog IDs to delete. */ Set catalogs_to_delete; - for (const auto &catalog_uptr : this->catalogs_.values()) { + for (const auto &catalog_uptr : catalog_collection_->catalogs_.values()) { const AssetCatalog *cat = catalog_uptr.get(); if (cat->path.is_contained_in(path)) { catalogs_to_delete.add(cat->catalog_id); @@ -166,7 +185,7 @@ void AssetCatalogService::update_catalog_path(const CatalogID catalog_id, AssetCatalog *renamed_cat = this->find_catalog(catalog_id); const AssetCatalogPath old_cat_path = renamed_cat->path; - for (auto &catalog_uptr : catalogs_.values()) { + for (auto &catalog_uptr : catalog_collection_->catalogs_.values()) { AssetCatalog *cat = catalog_uptr.get(); const AssetCatalogPath new_path = cat->path.rebase(old_cat_path, new_catalog_path); @@ -189,13 +208,14 @@ AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalo /* TODO(@sybren): move the `AssetCatalog::from_path()` function to another place, that can reuse * catalogs when a catalog with the given path is already known, and avoid duplicate catalog IDs. */ - BLI_assert_msg(!catalogs_.contains(catalog->catalog_id), "duplicate catalog ID not supported"); - catalogs_.add_new(catalog->catalog_id, std::move(catalog)); + BLI_assert_msg(!catalog_collection_->catalogs_.contains(catalog->catalog_id), + "duplicate catalog ID not supported"); + catalog_collection_->catalogs_.add_new(catalog->catalog_id, std::move(catalog)); - if (catalog_definition_file_) { + if (catalog_collection_->catalog_definition_file_) { /* Ensure the new catalog gets written to disk at some point. If there is no CDF in memory yet, * it's enough to have the catalog known to the service as it'll be saved to a new file. */ - catalog_definition_file_->add_new(catalog_ptr); + catalog_collection_->catalog_definition_file_->add_new(catalog_ptr); } BLI_assert_msg(catalog_tree_, "An Asset Catalog tree should always exist."); @@ -263,9 +283,9 @@ void AssetCatalogService::load_single_file(const CatalogFilePath &catalog_defini std::unique_ptr cdf = parse_catalog_file( catalog_definition_file_path); - BLI_assert_msg(!this->catalog_definition_file_, + BLI_assert_msg(!catalog_collection_->catalog_definition_file_, "Only loading of a single catalog definition file is supported."); - this->catalog_definition_file_ = std::move(cdf); + catalog_collection_->catalog_definition_file_ = std::move(cdf); } std::unique_ptr AssetCatalogService::parse_catalog_file( @@ -276,7 +296,7 @@ std::unique_ptr AssetCatalogService::parse_catalog_f auto catalog_parsed_callback = [this, catalog_definition_file_path]( std::unique_ptr catalog) { - if (this->catalogs_.contains(catalog->catalog_id)) { + if (catalog_collection_->catalogs_.contains(catalog->catalog_id)) { // TODO(@sybren): apparently another CDF was already loaded. This is not supported yet. std::cerr << catalog_definition_file_path << ": multiple definitions of catalog " << catalog->catalog_id << " in multiple files, ignoring this one." << std::endl; @@ -285,7 +305,7 @@ std::unique_ptr AssetCatalogService::parse_catalog_f } /* The AssetCatalog pointer is now owned by the AssetCatalogService. */ - this->catalogs_.add_new(catalog->catalog_id, std::move(catalog)); + catalog_collection_->catalogs_.add_new(catalog->catalog_id, std::move(catalog)); return true; }; @@ -297,9 +317,8 @@ std::unique_ptr AssetCatalogService::parse_catalog_f void AssetCatalogService::merge_from_disk_before_writing() { /* TODO(Sybren): expand to support multiple CDFs. */ - - if (!catalog_definition_file_ || catalog_definition_file_->file_path.empty() || - !BLI_is_file(catalog_definition_file_->file_path.c_str())) { + AssetCatalogDefinitionFile *const cdf = catalog_collection_->catalog_definition_file_.get(); + if (!cdf || cdf->file_path.empty() || !BLI_is_file(cdf->file_path.c_str())) { return; } @@ -308,22 +327,21 @@ void AssetCatalogService::merge_from_disk_before_writing() /* The following two conditions could be or'ed together. Keeping them separated helps when * adding debug prints, breakpoints, etc. */ - if (this->catalogs_.contains(catalog_id)) { + if (catalog_collection_->catalogs_.contains(catalog_id)) { /* This catalog was already seen, so just ignore it. */ return false; } - if (this->deleted_catalogs_.contains(catalog_id)) { + if (catalog_collection_->deleted_catalogs_.contains(catalog_id)) { /* This catalog was already seen and subsequently deleted, so just ignore it. */ return false; } /* This is a new catalog, so let's keep it around. */ - this->catalogs_.add_new(catalog_id, std::move(catalog)); + catalog_collection_->catalogs_.add_new(catalog_id, std::move(catalog)); return true; }; - catalog_definition_file_->parse_catalog_file(catalog_definition_file_->file_path, - catalog_parsed_callback); + cdf->parse_catalog_file(cdf->file_path, catalog_parsed_callback); } bool AssetCatalogService::write_to_disk_on_blendfile_save(const CatalogFilePath &blend_file_path) @@ -331,20 +349,21 @@ bool AssetCatalogService::write_to_disk_on_blendfile_save(const CatalogFilePath /* TODO(Sybren): expand to support multiple CDFs. */ /* - Already loaded a CDF from disk? -> Always write to that file. */ - if (this->catalog_definition_file_) { + if (catalog_collection_->catalog_definition_file_) { merge_from_disk_before_writing(); - return catalog_definition_file_->write_to_disk(); + return catalog_collection_->catalog_definition_file_->write_to_disk(); } - if (catalogs_.is_empty() && deleted_catalogs_.is_empty()) { + if (catalog_collection_->catalogs_.is_empty() && + catalog_collection_->deleted_catalogs_.is_empty()) { /* Avoid saving anything, when there is nothing to save. */ return true; /* Writing nothing when there is nothing to write is still a success. */ } const CatalogFilePath cdf_path_to_write = find_suitable_cdf_path_for_writing(blend_file_path); - this->catalog_definition_file_ = construct_cdf_in_memory(cdf_path_to_write); + catalog_collection_->catalog_definition_file_ = construct_cdf_in_memory(cdf_path_to_write); merge_from_disk_before_writing(); - return catalog_definition_file_->write_to_disk(); + return catalog_collection_->catalog_definition_file_->write_to_disk(); } CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing( @@ -382,7 +401,7 @@ std::unique_ptr AssetCatalogService::construct_cdf_i auto cdf = std::make_unique(); cdf->file_path = file_path; - for (auto &catalog : catalogs_.values()) { + for (auto &catalog : catalog_collection_->catalogs_.values()) { cdf->add_new(catalog.get()); } @@ -399,7 +418,7 @@ std::unique_ptr AssetCatalogService::read_into_tree() auto tree = std::make_unique(); /* Go through the catalogs, insert each path component into the tree where needed. */ - for (auto &catalog : catalogs_.values()) { + for (auto &catalog : catalog_collection_->catalogs_.values()) { tree->insert_item(*catalog); } @@ -416,7 +435,7 @@ void AssetCatalogService::create_missing_catalogs() { /* Construct an ordered set of paths to check, so that parents are ordered before children. */ std::set paths_to_check; - for (auto &catalog : catalogs_.values()) { + for (auto &catalog : catalog_collection_->catalogs_.values()) { paths_to_check.insert(catalog->path); } @@ -450,6 +469,68 @@ void AssetCatalogService::create_missing_catalogs() /* TODO(Sybren): bind the newly created catalogs to a CDF, if we know about it. */ } +bool AssetCatalogService::is_undo_possbile() const +{ + return !undo_snapshots_.is_empty(); +} + +bool AssetCatalogService::is_redo_possbile() const +{ + return !redo_snapshots_.is_empty(); +} + +void AssetCatalogService::undo() +{ + BLI_assert_msg(is_undo_possbile(), "Undo stack is empty"); + + redo_snapshots_.append(std::move(catalog_collection_)); + catalog_collection_ = std::move(undo_snapshots_.pop_last()); +} + +void AssetCatalogService::redo() +{ + BLI_assert_msg(is_redo_possbile(), "Redo stack is empty"); + + undo_snapshots_.append(std::move(catalog_collection_)); + catalog_collection_ = std::move(redo_snapshots_.pop_last()); +} + +void AssetCatalogService::store_undo_snapshot() +{ + std::unique_ptr snapshot = catalog_collection_->deep_copy(); + undo_snapshots_.append(std::move(snapshot)); + redo_snapshots_.clear(); +} + +/* ---------------------------------------------------------------------- */ + +std::unique_ptr AssetCatalogCollection::deep_copy() const +{ + auto copy = std::make_unique(); + + copy->catalogs_ = std::move(copy_catalog_map(this->catalogs_)); + copy->deleted_catalogs_ = std::move(copy_catalog_map(this->deleted_catalogs_)); + + if (catalog_definition_file_) { + copy->catalog_definition_file_ = std::move( + catalog_definition_file_->copy_and_remap(copy->catalogs_, copy->deleted_catalogs_)); + } + + return copy; +} + +OwningAssetCatalogMap AssetCatalogCollection::copy_catalog_map(const OwningAssetCatalogMap &orig) +{ + OwningAssetCatalogMap copy; + + for (const auto &orig_catalog_uptr : orig.values()) { + auto copy_catalog_uptr = std::make_unique(*orig_catalog_uptr); + copy.add_new(copy_catalog_uptr->catalog_id, std::move(copy_catalog_uptr)); + } + + return copy; +} + /* ---------------------------------------------------------------------- */ AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name, @@ -564,6 +645,8 @@ void AssetCatalogTree::foreach_root_item(const ItemIterFn callback) /* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + bool AssetCatalogDefinitionFile::contains(const CatalogID catalog_id) const { return catalogs_.contains(catalog_id); @@ -782,6 +865,34 @@ bool AssetCatalogDefinitionFile::ensure_directory_exists( return true; } +std::unique_ptr AssetCatalogDefinitionFile::copy_and_remap( + const OwningAssetCatalogMap &catalogs, const OwningAssetCatalogMap &deleted_catalogs) const +{ + auto copy = std::make_unique(*this); + copy->catalogs_.clear(); + + /* Remap pointers of the copy from the original AssetCatalogCollection to the given one. */ + for (CatalogID catalog_id : catalogs_.keys()) { + /* The catalog can be in the regular or the deleted map. */ + const std::unique_ptr *remapped_catalog_uptr_ptr = catalogs.lookup_ptr( + catalog_id); + if (remapped_catalog_uptr_ptr) { + copy->catalogs_.add_new(catalog_id, remapped_catalog_uptr_ptr->get()); + continue; + } + + remapped_catalog_uptr_ptr = deleted_catalogs.lookup_ptr(catalog_id); + if (remapped_catalog_uptr_ptr) { + copy->catalogs_.add_new(catalog_id, remapped_catalog_uptr_ptr->get()); + continue; + } + + BLI_assert(!"A CDF should only reference known catalogs."); + } + + return copy; +} + AssetCatalog::AssetCatalog(const CatalogID catalog_id, const AssetCatalogPath &path, const std::string &simple_name) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index cf06638bcdd..3522ad80bdd 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -55,7 +55,7 @@ class TestableAssetCatalogService : public AssetCatalogService { AssetCatalogDefinitionFile *get_catalog_definition_file() { - return catalog_definition_file_.get(); + return AssetCatalogService::get_catalog_definition_file(); } void create_missing_catalogs() @@ -66,7 +66,7 @@ class TestableAssetCatalogService : public AssetCatalogService { int64_t count_catalogs_with_path(const CatalogFilePath &path) { int64_t count = 0; - for (auto &catalog_uptr : catalogs_.values()) { + for (auto &catalog_uptr : get_catalogs().values()) { if (catalog_uptr->path == path) { count++; } @@ -1054,4 +1054,187 @@ TEST_F(AssetCatalogTest, create_catalog_filter_for_unassigned_assets) EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE)); } +TEST_F(AssetCatalogTest, cat_collection_deep_copy__empty) +{ + const AssetCatalogCollection empty; + auto copy = empty.deep_copy(); + EXPECT_NE(&empty, copy.get()); +} + +class TestableAssetCatalogCollection : public AssetCatalogCollection { + public: + OwningAssetCatalogMap &get_catalogs() + { + return catalogs_; + } + OwningAssetCatalogMap &get_deleted_catalogs() + { + return deleted_catalogs_; + } + AssetCatalogDefinitionFile *get_catalog_definition_file() + { + return catalog_definition_file_.get(); + } + AssetCatalogDefinitionFile *allocate_catalog_definition_file() + { + catalog_definition_file_ = std::make_unique(); + return get_catalog_definition_file(); + } +}; + +TEST_F(AssetCatalogTest, cat_collection_deep_copy__nonempty_nocdf) +{ + TestableAssetCatalogCollection catcoll; + auto cat1 = std::make_unique(UUID_POSES_RUZENA, "poses/Henrik", ""); + auto cat2 = std::make_unique(UUID_POSES_RUZENA_FACE, "poses/Henrik/face", ""); + auto cat3 = std::make_unique(UUID_POSES_RUZENA_HAND, "poses/Henrik/hands", ""); + cat3->flags.is_deleted = true; + + AssetCatalog *cat1_ptr = cat1.get(); + AssetCatalog *cat3_ptr = cat3.get(); + + catcoll.get_catalogs().add_new(cat1->catalog_id, std::move(cat1)); + catcoll.get_catalogs().add_new(cat2->catalog_id, std::move(cat2)); + catcoll.get_deleted_catalogs().add_new(cat3->catalog_id, std::move(cat3)); + + auto copy = catcoll.deep_copy(); + EXPECT_NE(&catcoll, copy.get()); + + TestableAssetCatalogCollection *testcopy = reinterpret_cast( + copy.get()); + + /* Test catalogs & deleted catalogs. */ + EXPECT_EQ(2, testcopy->get_catalogs().size()); + EXPECT_EQ(1, testcopy->get_deleted_catalogs().size()); + + ASSERT_TRUE(testcopy->get_catalogs().contains(UUID_POSES_RUZENA)); + ASSERT_TRUE(testcopy->get_catalogs().contains(UUID_POSES_RUZENA_FACE)); + ASSERT_TRUE(testcopy->get_deleted_catalogs().contains(UUID_POSES_RUZENA_HAND)); + + EXPECT_NE(nullptr, testcopy->get_catalogs().lookup(UUID_POSES_RUZENA)); + EXPECT_NE(cat1_ptr, testcopy->get_catalogs().lookup(UUID_POSES_RUZENA).get()) + << "AssetCatalogs should be actual copies."; + + EXPECT_NE(nullptr, testcopy->get_deleted_catalogs().lookup(UUID_POSES_RUZENA_HAND)); + EXPECT_NE(cat3_ptr, testcopy->get_deleted_catalogs().lookup(UUID_POSES_RUZENA_HAND).get()) + << "AssetCatalogs should be actual copies."; +} + +class TestableAssetCatalogDefinitionFile : public AssetCatalogDefinitionFile { + public: + Map get_catalogs() + { + return catalogs_; + } +}; + +TEST_F(AssetCatalogTest, cat_collection_deep_copy__nonempty_cdf) +{ + TestableAssetCatalogCollection catcoll; + auto cat1 = std::make_unique(UUID_POSES_RUZENA, "poses/Henrik", ""); + auto cat2 = std::make_unique(UUID_POSES_RUZENA_FACE, "poses/Henrik/face", ""); + auto cat3 = std::make_unique(UUID_POSES_RUZENA_HAND, "poses/Henrik/hands", ""); + cat3->flags.is_deleted = true; + + AssetCatalog *cat1_ptr = cat1.get(); + AssetCatalog *cat2_ptr = cat2.get(); + AssetCatalog *cat3_ptr = cat3.get(); + + catcoll.get_catalogs().add_new(cat1->catalog_id, std::move(cat1)); + catcoll.get_catalogs().add_new(cat2->catalog_id, std::move(cat2)); + catcoll.get_deleted_catalogs().add_new(cat3->catalog_id, std::move(cat3)); + + AssetCatalogDefinitionFile *cdf = catcoll.allocate_catalog_definition_file(); + cdf->file_path = "path/to/somewhere.cats.txt"; + cdf->add_new(cat1_ptr); + cdf->add_new(cat2_ptr); + cdf->add_new(cat3_ptr); + + /* Test CDF remapping. */ + auto copy = catcoll.deep_copy(); + TestableAssetCatalogCollection *testable_copy = static_cast( + copy.get()); + + TestableAssetCatalogDefinitionFile *cdf_copy = static_cast( + testable_copy->get_catalog_definition_file()); + EXPECT_EQ(testable_copy->get_catalogs().lookup(UUID_POSES_RUZENA).get(), + cdf_copy->get_catalogs().lookup(UUID_POSES_RUZENA)) + << "AssetCatalog pointers should have been remapped to the copy."; + + EXPECT_EQ(testable_copy->get_deleted_catalogs().lookup(UUID_POSES_RUZENA_HAND).get(), + cdf_copy->get_catalogs().lookup(UUID_POSES_RUZENA_HAND)) + << "Deleted AssetCatalog pointers should have been remapped to the copy."; +} + +TEST_F(AssetCatalogTest, undo_redo_one_step) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(); + + EXPECT_FALSE(service.is_undo_possbile()); + EXPECT_FALSE(service.is_redo_possbile()); + + service.create_catalog("some/catalog/path"); + EXPECT_FALSE(service.is_undo_possbile()) + << "Undo steps should be created explicitly, and not after creating any catalog."; + + service.store_undo_snapshot(); + const bUUID other_catalog_id = service.create_catalog("other/catalog/path")->catalog_id; + EXPECT_TRUE(service.is_undo_possbile()) + << "Undo should be possible after creating an undo snapshot."; + + // Undo the creation of the catalog. + service.undo(); + EXPECT_FALSE(service.is_undo_possbile()) + << "Undoing the only stored step should make it impossible to undo further."; + EXPECT_TRUE(service.is_redo_possbile()) << "Undoing a step should make redo possible."; + EXPECT_EQ(nullptr, service.find_catalog_by_path("other/catalog/path")) + << "Undone catalog should not exist after undo."; + EXPECT_NE(nullptr, service.find_catalog_by_path("some/catalog/path")) + << "First catalog should still exist after undo."; + EXPECT_FALSE(service.get_catalog_definition_file()->contains(other_catalog_id)) + << "The CDF should also not contain the undone catalog."; + + // Redo the creation of the catalog. + service.redo(); + EXPECT_TRUE(service.is_undo_possbile()) + << "Undoing and then redoing a step should make it possible to undo again."; + EXPECT_FALSE(service.is_redo_possbile()) + << "Undoing and then redoing a step should make redo impossible."; + EXPECT_NE(nullptr, service.find_catalog_by_path("other/catalog/path")) + << "Redone catalog should exist after redo."; + EXPECT_NE(nullptr, service.find_catalog_by_path("some/catalog/path")) + << "First catalog should still exist after redo."; + EXPECT_TRUE(service.get_catalog_definition_file()->contains(other_catalog_id)) + << "The CDF should contain the redone catalog."; +} + +TEST_F(AssetCatalogTest, undo_redo_more_complex) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(); + + service.store_undo_snapshot(); + service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)->simple_name = "Edited simple name"; + + service.store_undo_snapshot(); + service.find_catalog(UUID_POSES_ELLIE)->path = "poselib/EllieWithEditedPath"; + + service.undo(); + service.undo(); + + service.store_undo_snapshot(); + service.find_catalog(UUID_POSES_ELLIE)->simple_name = "Ellie Simple"; + + EXPECT_FALSE(service.is_redo_possbile()) + << "After storing an undo snapshot, the redo buffer should be empty."; + EXPECT_TRUE(service.is_undo_possbile()) + << "After storing an undo snapshot, undoing should be possible"; + + EXPECT_EQ(service.find_catalog(UUID_POSES_ELLIE)->simple_name, "Ellie Simple"); /* Not undone. */ + EXPECT_EQ(service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)->simple_name, + "POSES_ELLIE WHITESPACE"); /* Undone. */ + EXPECT_EQ(service.find_catalog(UUID_POSES_ELLIE)->path, "character/Ellie/poselib"); /* Undone. */ +} + } // namespace blender::bke::tests From 3a03d6c8519e3b12d5f3a359e3b8eccfe01ec6af Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 12 Oct 2021 07:24:25 -0500 Subject: [PATCH 0711/1500] Fix: Incorrect error message in set spline resolution node Caused by my own refactoring before committing the patch. --- .../nodes/geometry/nodes/node_geo_set_spline_resolution.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index fc8706f3223..ce50f1a29c6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -60,7 +60,7 @@ static void geo_node_set_spline_resolution_exec(GeoNodeExecParams params) bool only_poly = true; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_curve()) { - if (!only_poly) { + if (only_poly) { for (const SplinePtr &spline : geometry_set.get_curve_for_read()->splines()) { if (ELEM(spline->type(), Spline::Type::Bezier, Spline::Type::NURBS)) { only_poly = false; From f1d97a308d4a588694cdd41db57197576f1432c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 12 Oct 2021 14:42:27 +0200 Subject: [PATCH 0712/1500] Cleanup: asset catalogs, rename store_undo_snapshot to undo_push Rename `bke::AssetCatalogService::store_undo_snapshot` to `undo_push`. This makes the function named the same way as the global Blender "undo push" function. No functional changes. --- source/blender/blenkernel/BKE_asset_catalog.hh | 2 +- source/blender/blenkernel/intern/asset_catalog.cc | 2 +- source/blender/blenkernel/intern/asset_catalog_test.cc | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index b677adefb46..d5c8acff960 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -146,7 +146,7 @@ class AssetCatalogService { /** * Store the current catalogs in the undo stack. * This snapshots everything in the #AssetCatalogCollection. */ - void store_undo_snapshot(); + void undo_push(); /** * Restore the last-saved undo snapshot, pushing the current state onto the redo stack. * The caller is responsible for first checking that undoing is possible. diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 1b8ab6f7196..2d4956e7e25 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -495,7 +495,7 @@ void AssetCatalogService::redo() catalog_collection_ = std::move(redo_snapshots_.pop_last()); } -void AssetCatalogService::store_undo_snapshot() +void AssetCatalogService::undo_push() { std::unique_ptr snapshot = catalog_collection_->deep_copy(); undo_snapshots_.append(std::move(snapshot)); diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 3522ad80bdd..d1413d521a1 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -1178,7 +1178,7 @@ TEST_F(AssetCatalogTest, undo_redo_one_step) EXPECT_FALSE(service.is_undo_possbile()) << "Undo steps should be created explicitly, and not after creating any catalog."; - service.store_undo_snapshot(); + service.undo_push(); const bUUID other_catalog_id = service.create_catalog("other/catalog/path")->catalog_id; EXPECT_TRUE(service.is_undo_possbile()) << "Undo should be possible after creating an undo snapshot."; @@ -1214,16 +1214,16 @@ TEST_F(AssetCatalogTest, undo_redo_more_complex) TestableAssetCatalogService service(asset_library_root_); service.load_from_disk(); - service.store_undo_snapshot(); + service.undo_push(); service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)->simple_name = "Edited simple name"; - service.store_undo_snapshot(); + service.undo_push(); service.find_catalog(UUID_POSES_ELLIE)->path = "poselib/EllieWithEditedPath"; service.undo(); service.undo(); - service.store_undo_snapshot(); + service.undo_push(); service.find_catalog(UUID_POSES_ELLIE)->simple_name = "Ellie Simple"; EXPECT_FALSE(service.is_redo_possbile()) From 74ea21ec9dfdff4c5b1c00092427c49044343ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 12 Oct 2021 14:45:11 +0200 Subject: [PATCH 0713/1500] Asset Catalogs: expose undo/redo operators to UI Ensure that catalog operations create an undo snapshot, and show undo/redo operators in the asset browser. A hidden operator `ASSET_OT_catalog_undo_push` is also added such that add-ons can also set undo snapshots if they need. --- .../startup/bl_ui/space_filebrowser.py | 12 +++ .../blenkernel/intern/asset_catalog.cc | 2 + .../editors/asset/intern/asset_catalog.cc | 2 + .../blender/editors/asset/intern/asset_ops.cc | 101 ++++++++++++++++++ .../space_file/asset_catalog_tree_view.cc | 2 + 5 files changed, 119 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 3f2b11ee3a8..a087361780c 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -604,6 +604,7 @@ class ASSETBROWSER_MT_editor_menus(AssetBrowserMenu, Menu): layout.menu("ASSETBROWSER_MT_view") layout.menu("ASSETBROWSER_MT_select") + layout.menu("ASSETBROWSER_MT_edit") class ASSETBROWSER_MT_view(AssetBrowserMenu, Menu): @@ -642,6 +643,16 @@ class ASSETBROWSER_MT_select(AssetBrowserMenu, Menu): layout.operator("file.select_box") +class ASSETBROWSER_MT_edit(AssetBrowserMenu, Menu): + bl_label = "Edit" + + def draw(self, _context): + layout = self.layout + + layout.operator("asset.catalog_undo", text="Undo") + layout.operator("asset.catalog_redo", text="Redo") + + class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): bl_region_type = 'TOOL_PROPS' bl_label = "Asset Metadata" @@ -795,6 +806,7 @@ classes = ( ASSETBROWSER_MT_editor_menus, ASSETBROWSER_MT_view, ASSETBROWSER_MT_select, + ASSETBROWSER_MT_edit, ASSETBROWSER_PT_metadata, ASSETBROWSER_PT_metadata_preview, ASSETBROWSER_PT_metadata_details, diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 2d4956e7e25..2e01d3cdcea 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -485,6 +485,7 @@ void AssetCatalogService::undo() redo_snapshots_.append(std::move(catalog_collection_)); catalog_collection_ = std::move(undo_snapshots_.pop_last()); + rebuild_tree(); } void AssetCatalogService::redo() @@ -493,6 +494,7 @@ void AssetCatalogService::redo() undo_snapshots_.append(std::move(catalog_collection_)); catalog_collection_ = std::move(redo_snapshots_.pop_last()); + rebuild_tree(); } void AssetCatalogService::undo_push() diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index eb1865ee9cc..056fda63bd7 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -66,6 +66,7 @@ AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library, std::string unique_name = catalog_name_ensure_unique(*catalog_service, name, parent_path); AssetCatalogPath fullpath = AssetCatalogPath(parent_path) / unique_name; + catalog_service->undo_push(); return catalog_service->create_catalog(fullpath); } @@ -77,5 +78,6 @@ void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_i return; } + catalog_service->undo_push(); catalog_service->prune_catalogs_by_id(catalog_id); } diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 5424bae77b4..28cc16a9376 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -19,6 +19,7 @@ */ #include "BKE_asset_catalog.hh" +#include "BKE_asset_library.hh" #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_report.h" @@ -455,6 +456,103 @@ static void ASSET_OT_catalog_delete(struct wmOperatorType *ot) RNA_def_string(ot->srna, "catalog_id", nullptr, 0, "Catalog ID", "ID of the catalog to delete"); } +static bke::AssetCatalogService *get_catalog_service(bContext *C) +{ + const SpaceFile *sfile = CTX_wm_space_file(C); + if (!asset_operation_poll(C) || !sfile) { + return nullptr; + } + + AssetLibrary *asset_lib = ED_fileselect_active_asset_library_get(sfile); + return BKE_asset_library_get_catalog_service(asset_lib); +} + +static int asset_catalog_undo_exec(bContext *C, wmOperator * /*op*/) +{ + bke::AssetCatalogService *catalog_service = get_catalog_service(C); + if (!catalog_service) { + return OPERATOR_CANCELLED; + } + + catalog_service->undo(); + return OPERATOR_FINISHED; +} + +static bool asset_catalog_undo_poll(bContext *C) +{ + const bke::AssetCatalogService *catalog_service = get_catalog_service(C); + return catalog_service && catalog_service->is_undo_possbile(); +} + +static void ASSET_OT_catalog_undo(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Undo Catalog Edits"; + ot->description = "Undo the last edit to the asset catalogs"; + ot->idname = "ASSET_OT_catalog_undo"; + + /* api callbacks */ + ot->exec = asset_catalog_undo_exec; + ot->poll = asset_catalog_undo_poll; +} + +static int asset_catalog_redo_exec(bContext *C, wmOperator * /*op*/) +{ + bke::AssetCatalogService *catalog_service = get_catalog_service(C); + if (!catalog_service) { + return OPERATOR_CANCELLED; + } + + catalog_service->redo(); + return OPERATOR_FINISHED; +} + +static bool asset_catalog_redo_poll(bContext *C) +{ + const bke::AssetCatalogService *catalog_service = get_catalog_service(C); + return catalog_service && catalog_service->is_redo_possbile(); +} + +static void ASSET_OT_catalog_redo(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "redo Catalog Edits"; + ot->description = "Redo the last undone edit to the asset catalogs"; + ot->idname = "ASSET_OT_catalog_redo"; + + /* api callbacks */ + ot->exec = asset_catalog_redo_exec; + ot->poll = asset_catalog_redo_poll; +} + +static int asset_catalog_undo_push_exec(bContext *C, wmOperator * /*op*/) +{ + bke::AssetCatalogService *catalog_service = get_catalog_service(C); + if (!catalog_service) { + return OPERATOR_CANCELLED; + } + + catalog_service->undo_push(); + return OPERATOR_FINISHED; +} + +static bool asset_catalog_undo_push_poll(bContext *C) +{ + return get_catalog_service(C) != nullptr; +} + +static void ASSET_OT_catalog_undo_push(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Store undo snapshot for asset catalog edits"; + ot->description = "Store the current state of the asset catalogs in the undo buffer"; + ot->idname = "ASSET_OT_catalog_undo_push"; + + /* api callbacks */ + ot->exec = asset_catalog_undo_push_exec; + ot->poll = asset_catalog_undo_push_poll; +} + /* -------------------------------------------------------------------- */ void ED_operatortypes_asset(void) @@ -464,6 +562,9 @@ void ED_operatortypes_asset(void) WM_operatortype_append(ASSET_OT_catalog_new); WM_operatortype_append(ASSET_OT_catalog_delete); + WM_operatortype_append(ASSET_OT_catalog_undo); + WM_operatortype_append(ASSET_OT_catalog_redo); + WM_operatortype_append(ASSET_OT_catalog_undo_push); WM_operatortype_append(ASSET_OT_list_refresh); } diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 3407be9d8cd..8906cf34288 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -356,6 +356,8 @@ bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) AssetCatalogPath new_path = catalog_item_.catalog_path().parent(); new_path = new_path / StringRef(new_name); + + tree_view.catalog_service_->undo_push(); tree_view.catalog_service_->update_catalog_path(catalog_item_.get_catalog_id(), new_path); return true; } From a7300a217d14c7f566d0a947bbf24102df00b174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 12 Oct 2021 15:10:50 +0200 Subject: [PATCH 0714/1500] Asset Catalog Undo: send notifiers to redraw the catalog tree Send notifiers to redraw the catalog tree after undo/redo. --- source/blender/editors/asset/intern/asset_ops.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 28cc16a9376..9bf5c243e9a 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -475,6 +475,7 @@ static int asset_catalog_undo_exec(bContext *C, wmOperator * /*op*/) } catalog_service->undo(); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL); return OPERATOR_FINISHED; } @@ -504,6 +505,7 @@ static int asset_catalog_redo_exec(bContext *C, wmOperator * /*op*/) } catalog_service->redo(); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL); return OPERATOR_FINISHED; } From 7867feae56b679d5d2e2584a579b0977ddf19433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 12 Oct 2021 15:11:13 +0200 Subject: [PATCH 0715/1500] Asset Catalogs: mark 'undo push' operator as internal Mark the `ASSET_OT_catalog_undo_push` operator as internal, as it's not meant for artists to use directly. --- source/blender/editors/asset/intern/asset_ops.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 9bf5c243e9a..3d035396961 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -553,6 +553,9 @@ static void ASSET_OT_catalog_undo_push(struct wmOperatorType *ot) /* api callbacks */ ot->exec = asset_catalog_undo_push_exec; ot->poll = asset_catalog_undo_push_poll; + + /* Generally artists don't need to find & use this operator, it's meant for scripts only. */ + ot->flag = OPTYPE_INTERNAL; } /* -------------------------------------------------------------------- */ From b7b2103cf731d4645f19fdb06ed511056594fda8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 12 Oct 2021 15:12:48 +0200 Subject: [PATCH 0716/1500] Asset Catalogs Undo: add Ctrl+Z and Ctrl+Shift+Z as hotkeys for undo/redo Allow undo/redo of asset catalog edits with Ctrl+Z/Ctrl+Shift+Z. These keys are registered in the 'screen' keymap, so that they can take priority over the global undo/redo operators. Updated both Blender Default and Industry Compatible keymaps. --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 3 +++ .../presets/keyconfig/keymap_data/industry_compatible_data.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 8fdf7148613..07f4a40e118 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -627,6 +627,9 @@ def km_screen(params): ("file.execute", {"type": 'RET', "value": 'PRESS'}, None), ("file.execute", {"type": 'NUMPAD_ENTER', "value": 'PRESS'}, None), ("file.cancel", {"type": 'ESC', "value": 'PRESS'}, None), + # Asset Catalog undo is only available in the asset browser, and should take priority over `ed.undo`. + ("asset.catalog_undo", {"type": 'Z', "value": 'PRESS', "ctrl": True, "repeat": True}, None), + ("asset.catalog_redo", {"type": 'Z', "value": 'PRESS', "ctrl": True, "shift": True, "repeat": True}, None), # Undo ("ed.undo", {"type": 'Z', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("ed.redo", {"type": 'Z', "value": 'PRESS', "shift": True, "ctrl": True, "repeat": True}, None), diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index dedaba2f56c..6baf0d569d6 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -255,6 +255,9 @@ def km_screen(params): ("file.execute", {"type": 'RET', "value": 'PRESS'}, None), ("file.execute", {"type": 'NUMPAD_ENTER', "value": 'PRESS'}, None), ("file.cancel", {"type": 'ESC', "value": 'PRESS'}, None), + # Asset Catalog undo is only available in the asset browser, and should take priority over `ed.undo`. + ("asset.catalog_undo", {"type": 'Z', "value": 'PRESS', "ctrl": True, "repeat": True}, None), + ("asset.catalog_redo", {"type": 'Z', "value": 'PRESS', "ctrl": True, "shift": True, "repeat": True}, None), # Undo ("ed.undo", {"type": 'Z', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("ed.redo", {"type": 'Z', "value": 'PRESS', "shift": True, "ctrl": True, "repeat": True}, None), From 30cd1d10a95a5e32b9e0daf1a24c41123f262c96 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 12 Oct 2021 15:49:57 +0200 Subject: [PATCH 0717/1500] Asset Browser: Remove catalog deletion confirm prompt This confirmation popup was added when deletion was a destructive action that would be written to disk immediately, with no way to undo. Now we only write such changes to disk on .blend file save, plus there's undo/redo support for catalog edits now. In such cases confirmation popups should be avoided. --- source/blender/editors/asset/intern/asset_ops.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 3d035396961..090cc53eb3b 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -450,7 +450,6 @@ static void ASSET_OT_catalog_delete(struct wmOperatorType *ot) /* api callbacks */ ot->exec = asset_catalog_delete_exec; - ot->invoke = WM_operator_confirm; ot->poll = asset_catalog_operator_poll; RNA_def_string(ot->srna, "catalog_id", nullptr, 0, "Catalog ID", "ID of the catalog to delete"); From c1a1644db7b13425675819eeea9ef96bab4f5c97 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 12 Oct 2021 09:13:21 -0500 Subject: [PATCH 0718/1500] Cleanup: Attempt to fix benign macOS compile warnings --- source/blender/modifiers/intern/MOD_nodes_evaluator.cc | 2 +- .../nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc | 4 ++-- .../nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc | 4 ++-- .../nodes/geometry/nodes/node_geo_delete_geometry.cc | 6 +++--- .../blender/nodes/geometry/nodes/node_geo_join_geometry.cc | 4 ++-- source/blender/nodes/geometry/nodes/node_geo_proximity.cc | 2 +- .../nodes/geometry/nodes/node_geo_string_to_curves.cc | 4 ++-- source/blender/nodes/intern/node_common.cc | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 8209d46ec24..a85fc29430f 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -1108,7 +1108,7 @@ class GeometryNodesEvaluator { return; } bool will_be_triggered_by_other_node = false; - for (const DSocket origin_socket : origin_sockets) { + for (const DSocket &origin_socket : origin_sockets) { if (origin_socket->is_input()) { /* Load the value directly from the origin socket. In most cases this is an unlinked * group input. */ diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc index 65d22eca39c..b226cc2d3be 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc @@ -124,7 +124,7 @@ static void copy_endpoint_attributes(Span splines, end_data.tilts[i] = spline.tilts().last(); /* Copy the point attribute data over. */ - for (const auto &item : start_data.point_attributes.items()) { + for (const auto item : start_data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; @@ -133,7 +133,7 @@ static void copy_endpoint_attributes(Span splines, blender::fn::GVArray_For_GSpan(spline_span).get(0, point_span[i]); } - for (const auto &item : end_data.point_attributes.items()) { + for (const auto item : end_data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc index 0c435d69991..b6409290f31 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc @@ -180,7 +180,7 @@ static void copy_evaluated_point_attributes(Span splines, spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size)); spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size)); - for (const Map::Item &item : data.point_attributes.items()) { + for (const Map::Item item : data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; @@ -223,7 +223,7 @@ static void copy_uniform_sample_point_attributes(Span splines, uniform_samples, data.tilts.slice(offset, size)); - for (const Map::Item &item : data.point_attributes.items()) { + for (const Map::Item item : data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index fc9cba73b01..33f8c53e343 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -923,10 +923,10 @@ static void do_mesh_separation(GeometrySet &geometry_set, switch (mode) { case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: { Array vertex_map(mesh_in.totvert); - int num_selected_vertices; + int num_selected_vertices = 0; Array edge_map(mesh_in.totedge); - int num_selected_edges; + int num_selected_edges = 0; /* Fill all the maps based on the selection. */ switch (domain) { @@ -990,7 +990,7 @@ static void do_mesh_separation(GeometrySet &geometry_set, } case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: { Array edge_map(mesh_in.totedge); - int num_selected_edges; + int num_selected_edges = 0; /* Fill all the maps based on the selection. */ switch (domain) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 3e9b615f478..b628c5cbab8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -209,7 +209,7 @@ static void join_attributes(Span src_components, const Map info = get_final_attribute_info(src_components, ignored_attributes); - for (const Map::Item &item : info.items()) { + for (const Map::Item item : info.items()) { const AttributeIDRef attribute_id = item.key; const AttributeMetaData &meta_data = item.value; @@ -399,7 +399,7 @@ static void join_curve_attributes(const Map & Span src_components, CurveEval &result) { - for (const Map::Item &item : info.items()) { + for (const Map::Item item : info.items()) { const AttributeIDRef attribute_id = item.key; const AttributeMetaData meta_data = item.value; diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index 7062deff2f1..9f357ce2b1c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -201,7 +201,7 @@ static void geo_node_proximity_exec(GeoNodeExecParams params) if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) { params.set_output("Position", fn::make_constant_field({0.0f, 0.0f, 0.0f})); - params.set_output("Distance", fn::make_constant_field({0.0f})); + params.set_output("Distance", fn::make_constant_field(0.0f)); return; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 3eec2279f24..1cb6d43f685 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -136,7 +136,7 @@ static TextLayout get_text_layout(GeoNodeExecParams ¶ms) params.extract_input("Text Box Height"); VFont *vfont = (VFont *)params.node().id; - Curve cu = {nullptr}; + Curve cu = {{nullptr}}; cu.type = OB_FONT; /* Set defaults */ cu.resolu = 12; @@ -214,7 +214,7 @@ static Map create_curve_instances(GeoNodeExecParams ¶ms, if (handles.contains(charcodes[i])) { continue; } - Curve cu = {nullptr}; + Curve cu = {{nullptr}}; cu.type = OB_FONT; cu.resolu = 12; cu.vfont = vfont; diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index f5e6e7640ad..e5ec50858d9 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -372,7 +372,7 @@ void ntree_update_reroute_nodes(bNodeTree *ntree) } /* Actually update reroute nodes with changed types. */ - for (const auto &item : reroute_types.items()) { + for (const auto item : reroute_types.items()) { bNode *reroute_node = item.key; const bNodeSocketType *socket_type = item.value; bNodeSocket *input_socket = (bNodeSocket *)reroute_node->inputs.first; From aaf3a63dca54d154711753455d9f1ef3957e99e7 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 12 Oct 2021 12:25:19 +0200 Subject: [PATCH 0719/1500] Cleanup: Typo in comment. --- source/blender/windowmanager/intern/wm_files_link.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 50ab4f338a6..260ea73cc3b 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -624,7 +624,7 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) item = wm_link_append_data_item_add(data->lapp_data, id->name, GS(id->name), NULL); item->new_id = id; item->source_library = id->lib; - /* Since we did not have an item for that ID yet, we now user did not selected it explicitly, + /* Since we did not have an item for that ID yet, we know user did not selected it explicitly, * it was rather linked indirectly. This info is important for instantiation of collections. */ item->append_tag |= WM_APPEND_TAG_INDIRECT; BLI_ghash_insert(data->lapp_data->new_id_to_item, id, item); From 1db42c9b7967507d5d3ac688fb1a4a36bfac5f95 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 12 Oct 2021 16:23:57 +0200 Subject: [PATCH 0720/1500] Address warning about breaking copy elision of temporary object Using `std::move()` on temporary objects prevents copy elision done by compilers. Apple Clang warns about this. Generally `std::move()` should be used sparingly: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines --- source/blender/blenkernel/intern/asset_catalog.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 2e01d3cdcea..f66fda1a0bc 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -484,7 +484,7 @@ void AssetCatalogService::undo() BLI_assert_msg(is_undo_possbile(), "Undo stack is empty"); redo_snapshots_.append(std::move(catalog_collection_)); - catalog_collection_ = std::move(undo_snapshots_.pop_last()); + catalog_collection_ = undo_snapshots_.pop_last(); rebuild_tree(); } @@ -493,7 +493,7 @@ void AssetCatalogService::redo() BLI_assert_msg(is_redo_possbile(), "Redo stack is empty"); undo_snapshots_.append(std::move(catalog_collection_)); - catalog_collection_ = std::move(redo_snapshots_.pop_last()); + catalog_collection_ = redo_snapshots_.pop_last(); rebuild_tree(); } @@ -510,12 +510,12 @@ std::unique_ptr AssetCatalogCollection::deep_copy() cons { auto copy = std::make_unique(); - copy->catalogs_ = std::move(copy_catalog_map(this->catalogs_)); - copy->deleted_catalogs_ = std::move(copy_catalog_map(this->deleted_catalogs_)); + copy->catalogs_ = copy_catalog_map(this->catalogs_); + copy->deleted_catalogs_ = copy_catalog_map(this->deleted_catalogs_); if (catalog_definition_file_) { - copy->catalog_definition_file_ = std::move( - catalog_definition_file_->copy_and_remap(copy->catalogs_, copy->deleted_catalogs_)); + copy->catalog_definition_file_ = catalog_definition_file_->copy_and_remap( + copy->catalogs_, copy->deleted_catalogs_); } return copy; From a2daf92a57e45039fe928a9bc6251e8f8fc2bd6d Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 12 Oct 2021 16:40:02 +0200 Subject: [PATCH 0721/1500] Fix warning about deleted default constructor declared as default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since `AssetCatalogPath` isn't default constructible (unlike the previous `CatalogPath`, alias `std::string`), `AssetCatalog` isn't default constructible either. But its default constructor is declared with `= default` which Apple Clang was warning about. Differential Revision: https://developer.blender.org/D12714 Reviewed by: Sybren Stüvel --- source/blender/blenkernel/BKE_asset_catalog_path.hh | 4 ++-- .../blenkernel/intern/asset_catalog_path_test.cc | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog_path.hh b/source/blender/blenkernel/BKE_asset_catalog_path.hh index 054b7853140..f51232334f2 100644 --- a/source/blender/blenkernel/BKE_asset_catalog_path.hh +++ b/source/blender/blenkernel/BKE_asset_catalog_path.hh @@ -56,12 +56,12 @@ class AssetCatalogPath { /** * The path itself, such as "Agents/Secret/327". */ - std::string path_; + std::string path_ = ""; public: static const char SEPARATOR; - AssetCatalogPath() = delete; + AssetCatalogPath() = default; AssetCatalogPath(StringRef path); AssetCatalogPath(const std::string &path); AssetCatalogPath(const char *path); diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc index d8da91d5d18..be50f2fc001 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc @@ -31,6 +31,16 @@ namespace blender::bke::tests { TEST(AssetCatalogPathTest, construction) { + AssetCatalogPath default_constructed; + /* Use `.str()` to use `std:string`'s comparison operators here, not our own (which are tested + * later). */ + EXPECT_EQ(default_constructed.str(), ""); + + /* C++ considers this construction special, it doesn't call the default constructor but does + * recursive, member-wise value initialization. See https://stackoverflow.com/a/4982720. */ + AssetCatalogPath value_initialized = AssetCatalogPath(); + EXPECT_EQ(value_initialized.str(), ""); + AssetCatalogPath from_char_literal("the/path"); const std::string str_const = "the/path"; From 6535779c92b90035870047f178cf3eff95f0bdf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 10 Sep 2021 21:53:33 +0200 Subject: [PATCH 0722/1500] GHOST: Unify behavior of offscreen context creation This makes sure the previously bound context is restored after creating a new context. This follows what is already happening on windows. All system backend are patched. This also removes the goto and some code duplication. Differential Revision: https://developer.blender.org/D12455 --- intern/ghost/intern/GHOST_SystemCocoa.mm | 17 ++++-- intern/ghost/intern/GHOST_SystemSDL.cpp | 17 ++++-- intern/ghost/intern/GHOST_SystemWayland.cpp | 50 ++++++++-------- intern/ghost/intern/GHOST_SystemWin32.cpp | 59 +++++-------------- intern/ghost/intern/GHOST_SystemX11.cpp | 65 ++++++++++----------- 5 files changed, 94 insertions(+), 114 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 189e663f91a..5a7d6ef2c01 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -767,13 +767,18 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title, */ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSettings) { - GHOST_Context *context = new GHOST_ContextCGL(false, NULL, NULL, NULL); - if (context->initializeDrawingContext()) - return context; - else - delete context; + NSOpenGLContext *prevContext = [NSOpenGLContext currentContext]; - return NULL; + GHOST_Context *context = new GHOST_ContextCGL(false, NULL, NULL, NULL); + if (context->initializeDrawingContext() == false) { + delete context; + context = nullptr; + } + /* Restore previously bound context. This is just to follow the win32 behavior. */ + if (prevContext) { + [prevContext makeCurrentContext]; + } + return context; } /** diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 5370d4df857..77410407e21 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -141,6 +141,9 @@ uint8_t GHOST_SystemSDL::getNumDisplays() const GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GLSettings glSettings) { + SDL_GLContext prev_context = SDL_GL_GetCurrentContext(); + SDL_Window *prev_window = SDL_GL_GetCurrentWindow(); + GHOST_Context *context = new GHOST_ContextSDL(0, NULL, 0, /* Profile bit. */ @@ -149,12 +152,18 @@ GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GLSettings glSetti GHOST_OPENGL_SDL_CONTEXT_FLAGS, GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY); - if (context->initializeDrawingContext()) - return context; - else + if (context->initializeDrawingContext()) { + /* Pass */ + } + else { delete context; + context = nullptr; + } - return NULL; + if (m_context) { + SDL_GL_MakeCurrent(prev_window, prev_context); + } + return context; } GHOST_TSuccess GHOST_SystemSDL::disposeContext(GHOST_IContext *context) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 38700845405..8cc84641d2e 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -1599,45 +1599,47 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*g GHOST_Context *context; - for (int minor = 6; minor >= 0; --minor) { + EGLDisplay prev_display = eglGetCurrentDisplay(); + /* It doesn't matter which one we query since we use the same surface for both read and write. */ + EGLSurface prev_surface = eglGetCurrentSurface(EGL_DRAW); + EGLContext prev_context = eglGetCurrentContext(); + + const int versions[][2] = {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}}; + const int versions_len = sizeof(versions) / sizeof(versions[0]); + + for (int i = 0; i < versions_len; i++) { + int major = versions[i][0]; + int minor = versions[i][1]; + context = new GHOST_ContextEGL(this, false, EGLNativeWindowType(os_egl_window), EGLNativeDisplayType(d->display), EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, - 4, + major, minor, GHOST_OPENGL_EGL_CONTEXT_FLAGS, GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, EGL_OPENGL_API); - if (context->initializeDrawingContext()) - return context; - else + if (context->initializeDrawingContext()) { + break; + } + else { delete context; + context = nullptr; + } } - context = new GHOST_ContextEGL(this, - false, - EGLNativeWindowType(os_egl_window), - EGLNativeDisplayType(d->display), - EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, - 3, - 3, - GHOST_OPENGL_EGL_CONTEXT_FLAGS, - GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, - EGL_OPENGL_API); - - if (context->initializeDrawingContext()) { - return context; - } - else { - delete context; + if (context == nullptr) { + GHOST_PRINT("Cannot create off-screen EGL context" << std::endl); } - GHOST_PRINT("Cannot create off-screen EGL context" << std::endl); - - return nullptr; + if (prev_context) { + /* Restore previously bound context. This is just to follow the win32 behavior. */ + eglMakeCurrent(prev_display, prev_surface, prev_surface, prev_context); + } + return context; } GHOST_TSuccess GHOST_SystemWayland::disposeContext(GHOST_IContext *context) diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 482f20f5cd1..e0765ad85d5 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -284,69 +284,36 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GLSettings glSet HDC mHDC = GetDC(wnd); HDC prev_hdc = wglGetCurrentDC(); HGLRC prev_context = wglGetCurrentContext(); -#if defined(WITH_GL_PROFILE_CORE) - for (int minor = 5; minor >= 0; --minor) { + + const int versions[][2] = {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}}; + const int versions_len = sizeof(versions) / sizeof(versions[0]); + + for (int i = 0; i < versions_len; i++) { + int major = versions[i][0]; + int minor = versions[i][1]; + context = new GHOST_ContextWGL(false, true, wnd, mHDC, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, - 4, + major, minor, (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); if (context->initializeDrawingContext()) { - goto finished; + break; } else { delete context; + context = nullptr; } } - context = new GHOST_ContextWGL(false, - true, - wnd, - mHDC, - WGL_CONTEXT_CORE_PROFILE_BIT_ARB, - 3, - 3, - (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); - - if (context->initializeDrawingContext()) { - goto finished; + if (prev_context) { + wglMakeCurrent(prev_hdc, prev_context); } - else { - delete context; - return NULL; - } - -#elif defined(WITH_GL_PROFILE_COMPAT) - // ask for 2.1 context, driver gives any GL version >= 2.1 - // (hopefully the latest compatibility profile) - // 2.1 ignores the profile bit & is incompatible with core profile - context = new GHOST_ContextWGL(false, - true, - NULL, - NULL, - 0, // no profile bit - 2, - 1, - (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); - - if (context->initializeDrawingContext()) { - return context; - } - else { - delete context; - } -#else -# error // must specify either core or compat at build time -#endif -finished: - wglMakeCurrent(prev_hdc, prev_context); return context; } diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index ab8039ea95d..98f5f41d5e8 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -437,16 +437,32 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSetti # endif #endif +#if defined(WITH_GL_EGL) + EGLDisplay prev_display = eglGetCurrentDisplay(); + /* It doesn't matter which one we query since we use the same surface for both read and write. */ + EGLSurface prev_surface = eglGetCurrentSurface(EGL_DRAW); + EGLContext prev_context = eglGetCurrentContext(); +#else + Display *prev_display = glXGetCurrentDisplay(); + GLXDrawable prev_drawable = glXGetCurrentDrawable(); + GLXContext prev_context = glXGetCurrentContext(); +#endif + GHOST_Context *context; - for (int minor = 5; minor >= 0; --minor) { + const int versions[][2] = {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}}; + const int versions_len = sizeof(versions) / sizeof(versions[0]); + + for (int i = 0; i < versions_len; i++) { + int major = versions[i][0]; + int minor = versions[i][1]; #if defined(WITH_GL_EGL) context = new GHOST_ContextEGL(this, false, EGLNativeWindowType(nullptr), EGLNativeDisplayType(m_display), profile_mask, - 4, + major, minor, GHOST_OPENGL_EGL_CONTEXT_FLAGS | (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0), @@ -458,50 +474,31 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSetti m_display, (GLXFBConfig)NULL, profile_mask, - 4, + major, minor, GHOST_OPENGL_GLX_CONTEXT_FLAGS | (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY); #endif - if (context->initializeDrawingContext()) - return context; - else + if (context->initializeDrawingContext()) { + break; + } + else { delete context; + context = nullptr; + } } + if (prev_context) { + /* Restore previously bound context. This is just to follow the win32 behavior. */ #if defined(WITH_GL_EGL) - context = new GHOST_ContextEGL(this, - false, - EGLNativeWindowType(nullptr), - EGLNativeDisplayType(m_display), - profile_mask, - 3, - 3, - GHOST_OPENGL_EGL_CONTEXT_FLAGS | - (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0), - GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, - EGL_OPENGL_API); + eglMakeCurrent(prev_display, prev_surface, prev_surface, prev_context); #else - context = new GHOST_ContextGLX(false, - (Window)NULL, - m_display, - (GLXFBConfig)NULL, - profile_mask, - 3, - 3, - GHOST_OPENGL_GLX_CONTEXT_FLAGS | - (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY); + glXMakeCurrent(prev_display, prev_drawable, prev_context); #endif - - if (context->initializeDrawingContext()) - return context; - else - delete context; - - return NULL; + } + return context; } /** From 45f167237f0cbebfed95e8b58b405367070ac305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 12 Oct 2021 14:43:41 +0200 Subject: [PATCH 0723/1500] Fix T91981: Crash when using operators that needs scene depth There was a double lock in the object depth drawing function. Also the texture read was not reading the texture with the right format. Now it needs a conversion. Fix T91981 Particle Edit make Blender Crash Fix T92006 Light spot interactively point can't use --- source/blender/draw/intern/draw_manager.c | 1 - source/blender/editors/space_view3d/view3d_draw.c | 9 +++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index c8900d64935..4761e8b755f 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2801,7 +2801,6 @@ void DRW_draw_depth_object( GPU_framebuffer_restore(); GPU_framebuffer_free(depth_fb); - DRW_opengl_context_disable(); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 151fa02cd5c..fe347e89600 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -2297,8 +2297,13 @@ static ViewDepths *view3d_depths_create(ARegion *region) { GPUViewport *viewport = WM_draw_region_get_viewport(region); GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); - d->depths = GPU_texture_read(depth_tx, GPU_DATA_FLOAT, 0); - + uint32_t *int_depths = GPU_texture_read(depth_tx, GPU_DATA_UINT_24_8, 0); + d->depths = (float *)int_depths; + /* Convert in-place. */ + int pixel_count = GPU_texture_width(depth_tx) * GPU_texture_height(depth_tx); + for (int i = 0; i < pixel_count; i++) { + d->depths[i] = (int_depths[i] >> 8u) / (float)0xFFFFFF; + } /* Assumed to be this as they are never changed. */ d->depth_range[0] = 0.0; d->depth_range[1] = 1.0; From c63fb657c8991dbffaf6d6bb2bdf3d4fc3894f53 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 12 Oct 2021 17:13:48 +0200 Subject: [PATCH 0724/1500] Cleanup: use `nullptr` instead of `NULL` in C++ code. --- source/blender/editors/asset/intern/asset_ops.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 090cc53eb3b..bf532903c7c 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -474,7 +474,7 @@ static int asset_catalog_undo_exec(bContext *C, wmOperator * /*op*/) } catalog_service->undo(); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); return OPERATOR_FINISHED; } @@ -504,7 +504,7 @@ static int asset_catalog_redo_exec(bContext *C, wmOperator * /*op*/) } catalog_service->redo(); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); return OPERATOR_FINISHED; } From 72a47fea5d30d168e0de90a20187d63f4fe23e94 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 12 Oct 2021 17:06:04 +0200 Subject: [PATCH 0725/1500] Fix dragging objects from Outliner to 3D View broken A dragged & dropped wouldn't be duplicated anymore, it would just be moved to the drop position. Caused by c8fcea0c33ef. --- source/blender/editors/space_view3d/space_view3d.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index bedc24a6287..bda6fa05a63 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -630,7 +630,9 @@ static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB); RNA_string_set(drop->ptr, "name", id->name + 2); - RNA_boolean_set(drop->ptr, "duplicate", false); + /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ + const bool is_imported_id = drag->type == WM_DRAG_ASSET; + RNA_boolean_set(drop->ptr, "duplicate", !is_imported_id); } static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop) From ad802488756b4a8e79d94ce77e09d39e9c6b3b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 12 Oct 2021 17:54:16 +0200 Subject: [PATCH 0726/1500] Revert "GHOST: Unify behavior of offscreen context creation" Commited by mistake This reverts commit 6535779c92b90035870047f178cf3eff95f0bdf0. --- intern/ghost/intern/GHOST_SystemCocoa.mm | 15 ++--- intern/ghost/intern/GHOST_SystemSDL.cpp | 17 ++---- intern/ghost/intern/GHOST_SystemWayland.cpp | 50 ++++++++-------- intern/ghost/intern/GHOST_SystemWin32.cpp | 59 ++++++++++++++----- intern/ghost/intern/GHOST_SystemX11.cpp | 65 +++++++++++---------- 5 files changed, 113 insertions(+), 93 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 5a7d6ef2c01..189e663f91a 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -767,18 +767,13 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title, */ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSettings) { - NSOpenGLContext *prevContext = [NSOpenGLContext currentContext]; - GHOST_Context *context = new GHOST_ContextCGL(false, NULL, NULL, NULL); - if (context->initializeDrawingContext() == false) { + if (context->initializeDrawingContext()) + return context; + else delete context; - context = nullptr; - } - /* Restore previously bound context. This is just to follow the win32 behavior. */ - if (prevContext) { - [prevContext makeCurrentContext]; - } - return context; + + return NULL; } /** diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 77410407e21..5370d4df857 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -141,9 +141,6 @@ uint8_t GHOST_SystemSDL::getNumDisplays() const GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GLSettings glSettings) { - SDL_GLContext prev_context = SDL_GL_GetCurrentContext(); - SDL_Window *prev_window = SDL_GL_GetCurrentWindow(); - GHOST_Context *context = new GHOST_ContextSDL(0, NULL, 0, /* Profile bit. */ @@ -152,18 +149,12 @@ GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GLSettings glSetti GHOST_OPENGL_SDL_CONTEXT_FLAGS, GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY); - if (context->initializeDrawingContext()) { - /* Pass */ - } - else { + if (context->initializeDrawingContext()) + return context; + else delete context; - context = nullptr; - } - if (m_context) { - SDL_GL_MakeCurrent(prev_window, prev_context); - } - return context; + return NULL; } GHOST_TSuccess GHOST_SystemSDL::disposeContext(GHOST_IContext *context) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 8cc84641d2e..38700845405 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -1599,47 +1599,45 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*g GHOST_Context *context; - EGLDisplay prev_display = eglGetCurrentDisplay(); - /* It doesn't matter which one we query since we use the same surface for both read and write. */ - EGLSurface prev_surface = eglGetCurrentSurface(EGL_DRAW); - EGLContext prev_context = eglGetCurrentContext(); - - const int versions[][2] = {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}}; - const int versions_len = sizeof(versions) / sizeof(versions[0]); - - for (int i = 0; i < versions_len; i++) { - int major = versions[i][0]; - int minor = versions[i][1]; - + for (int minor = 6; minor >= 0; --minor) { context = new GHOST_ContextEGL(this, false, EGLNativeWindowType(os_egl_window), EGLNativeDisplayType(d->display), EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, - major, + 4, minor, GHOST_OPENGL_EGL_CONTEXT_FLAGS, GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, EGL_OPENGL_API); - if (context->initializeDrawingContext()) { - break; - } - else { + if (context->initializeDrawingContext()) + return context; + else delete context; - context = nullptr; - } } - if (context == nullptr) { - GHOST_PRINT("Cannot create off-screen EGL context" << std::endl); + context = new GHOST_ContextEGL(this, + false, + EGLNativeWindowType(os_egl_window), + EGLNativeDisplayType(d->display), + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + 3, + 3, + GHOST_OPENGL_EGL_CONTEXT_FLAGS, + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); + + if (context->initializeDrawingContext()) { + return context; + } + else { + delete context; } - if (prev_context) { - /* Restore previously bound context. This is just to follow the win32 behavior. */ - eglMakeCurrent(prev_display, prev_surface, prev_surface, prev_context); - } - return context; + GHOST_PRINT("Cannot create off-screen EGL context" << std::endl); + + return nullptr; } GHOST_TSuccess GHOST_SystemWayland::disposeContext(GHOST_IContext *context) diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index e0765ad85d5..482f20f5cd1 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -284,36 +284,69 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GLSettings glSet HDC mHDC = GetDC(wnd); HDC prev_hdc = wglGetCurrentDC(); HGLRC prev_context = wglGetCurrentContext(); - - const int versions[][2] = {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}}; - const int versions_len = sizeof(versions) / sizeof(versions[0]); - - for (int i = 0; i < versions_len; i++) { - int major = versions[i][0]; - int minor = versions[i][1]; - +#if defined(WITH_GL_PROFILE_CORE) + for (int minor = 5; minor >= 0; --minor) { context = new GHOST_ContextWGL(false, true, wnd, mHDC, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, - major, + 4, minor, (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); if (context->initializeDrawingContext()) { - break; + goto finished; } else { delete context; - context = nullptr; } } - if (prev_context) { - wglMakeCurrent(prev_hdc, prev_context); + context = new GHOST_ContextWGL(false, + true, + wnd, + mHDC, + WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 3, + 3, + (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), + GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); + + if (context->initializeDrawingContext()) { + goto finished; } + else { + delete context; + return NULL; + } + +#elif defined(WITH_GL_PROFILE_COMPAT) + // ask for 2.1 context, driver gives any GL version >= 2.1 + // (hopefully the latest compatibility profile) + // 2.1 ignores the profile bit & is incompatible with core profile + context = new GHOST_ContextWGL(false, + true, + NULL, + NULL, + 0, // no profile bit + 2, + 1, + (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), + GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); + + if (context->initializeDrawingContext()) { + return context; + } + else { + delete context; + } +#else +# error // must specify either core or compat at build time +#endif +finished: + wglMakeCurrent(prev_hdc, prev_context); return context; } diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 98f5f41d5e8..ab8039ea95d 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -437,32 +437,16 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSetti # endif #endif -#if defined(WITH_GL_EGL) - EGLDisplay prev_display = eglGetCurrentDisplay(); - /* It doesn't matter which one we query since we use the same surface for both read and write. */ - EGLSurface prev_surface = eglGetCurrentSurface(EGL_DRAW); - EGLContext prev_context = eglGetCurrentContext(); -#else - Display *prev_display = glXGetCurrentDisplay(); - GLXDrawable prev_drawable = glXGetCurrentDrawable(); - GLXContext prev_context = glXGetCurrentContext(); -#endif - GHOST_Context *context; - const int versions[][2] = {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}}; - const int versions_len = sizeof(versions) / sizeof(versions[0]); - - for (int i = 0; i < versions_len; i++) { - int major = versions[i][0]; - int minor = versions[i][1]; + for (int minor = 5; minor >= 0; --minor) { #if defined(WITH_GL_EGL) context = new GHOST_ContextEGL(this, false, EGLNativeWindowType(nullptr), EGLNativeDisplayType(m_display), profile_mask, - major, + 4, minor, GHOST_OPENGL_EGL_CONTEXT_FLAGS | (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0), @@ -474,31 +458,50 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSetti m_display, (GLXFBConfig)NULL, profile_mask, - major, + 4, minor, GHOST_OPENGL_GLX_CONTEXT_FLAGS | (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY); #endif - if (context->initializeDrawingContext()) { - break; - } - else { + if (context->initializeDrawingContext()) + return context; + else delete context; - context = nullptr; - } } - if (prev_context) { - /* Restore previously bound context. This is just to follow the win32 behavior. */ #if defined(WITH_GL_EGL) - eglMakeCurrent(prev_display, prev_surface, prev_surface, prev_context); + context = new GHOST_ContextEGL(this, + false, + EGLNativeWindowType(nullptr), + EGLNativeDisplayType(m_display), + profile_mask, + 3, + 3, + GHOST_OPENGL_EGL_CONTEXT_FLAGS | + (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0), + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); #else - glXMakeCurrent(prev_display, prev_drawable, prev_context); + context = new GHOST_ContextGLX(false, + (Window)NULL, + m_display, + (GLXFBConfig)NULL, + profile_mask, + 3, + 3, + GHOST_OPENGL_GLX_CONTEXT_FLAGS | + (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), + GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY); #endif - } - return context; + + if (context->initializeDrawingContext()) + return context; + else + delete context; + + return NULL; } /** From 0c7e836a1da0e637187d1d0c2cde9d6d89a6d0df Mon Sep 17 00:00:00 2001 From: Wannes Malfait Date: Tue, 12 Oct 2021 10:57:12 -0500 Subject: [PATCH 0727/1500] Fix T92150: Incorrect invert in Delete Geometry node The selection was inverted when deleting points from a spline. Differential Revision: https://developer.blender.org/D12840 --- source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 33f8c53e343..e4f6d3d766e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -371,7 +371,7 @@ static std::unique_ptr curve_separate(const CurveEval &input_curve, indices_to_copy.clear(); for (const int i_point : IndexRange(spline.size())) { - if (selection[selection_index] == invert) { + if (selection[selection_index] != invert) { /* Append i_point instead of selection_index because we need indices local to the spline * for copying. */ indices_to_copy.append(i_point); From 5e3877e0c8560f27a5cd7b303666b235fb148165 Mon Sep 17 00:00:00 2001 From: Wannes Malfait Date: Tue, 12 Oct 2021 10:58:38 -0500 Subject: [PATCH 0728/1500] Fix T92149: Crash in delete geometry node after curve fill node There was only a check for the component but not for if it was empty. Because the curve fill node produces an empty curve component, a nullptr was read, causing a crash. Generally nodes shouldn't produce empty components, but currently we cannot rely on that fact. Differential Revision: https://developer.blender.org/D12838 --- .../nodes/geometry/nodes/node_geo_delete_geometry.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index e4f6d3d766e..25f0d355c9e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -1156,19 +1156,19 @@ void separate_geometry(GeometrySet &geometry_set, bool &r_is_error) { bool some_valid_domain = false; - if (geometry_set.has()) { + if (geometry_set.has_pointcloud()) { if (domain == ATTR_DOMAIN_POINT) { separate_point_cloud_selection(geometry_set, selection_field, invert); some_valid_domain = true; } } - if (geometry_set.has()) { + if (geometry_set.has_mesh()) { if (domain != ATTR_DOMAIN_CURVE) { separate_mesh_selection(geometry_set, selection_field, domain, mode, invert); some_valid_domain = true; } } - if (geometry_set.has()) { + if (geometry_set.has_curve()) { if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { separate_curve_selection(geometry_set, selection_field, domain, invert); some_valid_domain = true; From a6da1884ba9f4d3d37c98b48bc18a8d26dd489fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 12 Oct 2021 17:53:05 +0200 Subject: [PATCH 0729/1500] Asset Catalogs: Refresh catalog simple name when assigning catalog ID When assigning a new catalog ID to an asset, also refresh the "catalog simple name". This "simple name" is stored on the asset metadata next to the catalog UUID, to allow some emergency data recovery when the catalog definition file is somehow lost. --- source/blender/blenkernel/BKE_asset_library.h | 4 +++ .../blender/blenkernel/BKE_asset_library.hh | 8 ++++++ .../blenkernel/intern/asset_library.cc | 26 +++++++++++++++++++ source/blender/makesrna/intern/rna_asset.c | 20 ++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/source/blender/blenkernel/BKE_asset_library.h b/source/blender/blenkernel/BKE_asset_library.h index 3ddfbb1415e..70949f40430 100644 --- a/source/blender/blenkernel/BKE_asset_library.h +++ b/source/blender/blenkernel/BKE_asset_library.h @@ -69,6 +69,10 @@ bool BKE_asset_library_find_suitable_root_path_from_path( bool BKE_asset_library_find_suitable_root_path_from_main( const struct Main *bmain, char r_library_path[768 /* FILE_MAXDIR */]); +/** Look up the asset's catalog and copy its simple name into #asset_data. */ +void BKE_asset_library_refresh_catalog_simplename(struct AssetLibrary *asset_library, + struct AssetMetaData *asset_data); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh index 1dc02f7aa9b..419df2a1061 100644 --- a/source/blender/blenkernel/BKE_asset_library.hh +++ b/source/blender/blenkernel/BKE_asset_library.hh @@ -38,6 +38,14 @@ struct AssetLibrary { void load(StringRefNull library_root_directory); + /** + * Update `catalog_simple_name` by looking up the asset's catalog by its ID. + * + * No-op if the catalog cannot be found. This could be the kind of "the + * catalog definition file is corrupt/lost" scenario that the simple name is + * meant to help recover from. */ + void refresh_catalog_simplename(struct AssetMetaData *asset_data); + void on_save_handler_register(); void on_save_handler_unregister(); diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index 27e66ee5725..5956a4af0cb 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -18,6 +18,7 @@ * \ingroup bke */ +#include "BKE_asset_catalog.hh" #include "BKE_asset_library.hh" #include "BKE_callbacks.h" #include "BKE_main.h" @@ -25,6 +26,7 @@ #include "BLI_path_util.h" +#include "DNA_asset_types.h" #include "DNA_userdef_types.h" #include "MEM_guardedalloc.h" @@ -91,6 +93,13 @@ blender::bke::AssetCatalogTree *BKE_asset_library_get_catalog_tree(const ::Asset return catalog_service->get_catalog_tree(); } +void BKE_asset_library_refresh_catalog_simplename(struct AssetLibrary *asset_library, + struct AssetMetaData *asset_data) +{ + blender::bke::AssetLibrary *lib = reinterpret_cast(asset_library); + lib->refresh_catalog_simplename(asset_data); +} + namespace blender::bke { void AssetLibrary::load(StringRefNull library_root_directory) @@ -138,4 +147,21 @@ void AssetLibrary::on_save_post(struct Main *main, this->catalog_service->write_to_disk_on_blendfile_save(main->name); } +void AssetLibrary::refresh_catalog_simplename(struct AssetMetaData *asset_data) +{ + if (BLI_uuid_is_nil(asset_data->catalog_id)) { + asset_data->catalog_simple_name[0] = '\0'; + return; + } + + const AssetCatalog *catalog = this->catalog_service->find_catalog(asset_data->catalog_id); + if (catalog == nullptr) { + /* No-op if the catalog cannot be found. This could be the kind of "the catalog definition file + * is corrupt/lost" scenario that the simple name is meant to help recover from. */ + return; + } + + STRNCPY(asset_data->catalog_simple_name, catalog->simple_name.c_str()); +} + } // namespace blender::bke diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 80824df1bc8..979d0882dd5 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -215,6 +215,25 @@ static void rna_AssetMetaData_catalog_id_set(PointerRNA *ptr, const char *value) BKE_asset_metadata_catalog_id_set(asset_data, new_uuid, ""); } +void rna_AssetMetaData_catalog_id_update(struct bContext *C, struct PointerRNA *ptr) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + if (sfile == NULL) { + /* Until there is a proper Asset Service available, it's only possible to get the asset library + * from within the asset browser context. */ + return; + } + + AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); + if (asset_library == NULL) { + /* The SpaceFile may not be an asset browser but a regular file browser. */ + return; + } + + AssetMetaData *asset_data = ptr->data; + BKE_asset_library_refresh_catalog_simplename(asset_library, asset_data); +} + static PointerRNA rna_AssetHandle_file_data_get(PointerRNA *ptr) { AssetHandle *asset_handle = ptr->data; @@ -356,6 +375,7 @@ static void rna_def_asset_data(BlenderRNA *brna) "rna_AssetMetaData_catalog_id_length", "rna_AssetMetaData_catalog_id_set"); RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, 0, "rna_AssetMetaData_catalog_id_update"); RNA_def_property_ui_text(prop, "Catalog UUID", "Identifier for the asset's catalog, used by Blender to look up the " From eb56e8cd788dd9711ec013a5460fdb1d510511c1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 12 Oct 2021 14:14:09 -0500 Subject: [PATCH 0730/1500] Cleanup: Fix comment formatting and grammar --- source/blender/makesrna/intern/rna_access.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index f1980eed811..40b5f3ed1da 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -1445,8 +1445,9 @@ int RNA_property_int_clamp(PointerRNA *ptr, PropertyRNA *prop, int *value) return 0; } -/* this is the max length including \0 terminator. - * '0' used when their is no maximum */ +/** + * \return the maximum length including the \0 terminator. '0' is used when there is no maximum. + */ int RNA_property_string_maxlength(PropertyRNA *prop) { StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop); From 9d03990e32ac36a4b9c6f958e9b1bea7288b0bbc Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 12 Oct 2021 15:28:11 -0500 Subject: [PATCH 0731/1500] Fix T92160: Geometry Proximity node can produce invalid values Check when the node fails to create BVH trees, and fill the result with zero in that case, which is most likely the expected value when the node encounters an error. Warnings will be added with a separate patch. --- .../geometry/nodes/node_geo_proximity.cc | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index 9f357ce2b1c..30d025953af 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -50,7 +50,7 @@ static void geo_proximity_init(bNodeTree *UNUSED(ntree), bNode *node) node->storage = node_storage; } -static void calculate_mesh_proximity(const VArray &positions, +static bool calculate_mesh_proximity(const VArray &positions, const IndexMask mask, const Mesh &mesh, const GeometryNodeProximityTargetType type, @@ -71,7 +71,7 @@ static void calculate_mesh_proximity(const VArray &positions, } if (bvh_data.tree == nullptr) { - return; + return false; } threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) { @@ -97,18 +97,19 @@ static void calculate_mesh_proximity(const VArray &positions, }); free_bvhtree_from_mesh(&bvh_data); + return true; } -static void calculate_pointcloud_proximity(const VArray &positions, +static bool calculate_pointcloud_proximity(const VArray &positions, const IndexMask mask, const PointCloud &pointcloud, - const MutableSpan r_distances, - const MutableSpan r_locations) + MutableSpan r_distances, + MutableSpan r_locations) { BVHTreeFromPointCloud bvh_data; BKE_bvhtree_from_pointcloud_get(&bvh_data, &pointcloud, 2); if (bvh_data.tree == nullptr) { - return; + return false; } threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) { @@ -136,6 +137,7 @@ static void calculate_pointcloud_proximity(const VArray &positions, }); free_bvhtree_from_pointcloud(&bvh_data); + return true; } class ProximityFunction : public fn::MultiFunction { @@ -174,16 +176,23 @@ class ProximityFunction : public fn::MultiFunction { distances.fill(FLT_MAX); + bool success = false; if (target_.has_mesh()) { - calculate_mesh_proximity( + success |= calculate_mesh_proximity( src_positions, mask, *target_.get_mesh_for_read(), type_, distances, positions); } if (target_.has_pointcloud() && type_ == GEO_NODE_PROX_TARGET_POINTS) { - calculate_pointcloud_proximity( + success |= calculate_pointcloud_proximity( src_positions, mask, *target_.get_pointcloud_for_read(), distances, positions); } + if (!success) { + positions.fill(float3(0)); + distances.fill(0.0f); + return; + } + if (params.single_output_is_required(2, "Distance")) { threading::parallel_for(mask.index_range(), 2048, [&](IndexRange range) { for (const int i : range) { @@ -199,10 +208,13 @@ static void geo_node_proximity_exec(GeoNodeExecParams params) { GeometrySet geometry_set_target = params.extract_input("Target"); - if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) { + auto return_default = [&]() { params.set_output("Position", fn::make_constant_field({0.0f, 0.0f, 0.0f})); params.set_output("Distance", fn::make_constant_field(0.0f)); - return; + }; + + if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) { + return return_default(); } const NodeGeometryProximity &storage = *(const NodeGeometryProximity *)params.node().storage; From f18ab3470f3da32d561781e0794e5983221d5a2d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 12 Oct 2021 15:38:56 -0500 Subject: [PATCH 0732/1500] Fix T91809: Crash on undo with empty field inferencing Some runtime data that stores which sockets can be fields and which can't is not stored in the file, but only calculated when necessary. When opening a file, the node tree update function was called, which recalculated this data, but that was explicily turned off for undo. This exposes a fundamental issue with undo, the ID caching system for undo, and how it relates to node trees in particular. Ideally this call couldn't be necessary at all. In the future it could be removed by adding a runtime struct to node trees, and calculating its contents on-demand instead of preemtively. Differential Revision: https://developer.blender.org/D12699 --- source/blender/blenloader/intern/readfile.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 491322e06df..a41b0641fc7 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4027,11 +4027,13 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) * does not always properly handle user counts, and/or that function does not take into * account old, deprecated data. */ BKE_main_id_refcount_recompute(bfd->main, false); - - /* After all data has been read and versioned, uses LIB_TAG_NEW. */ - ntreeUpdateAllNew(bfd->main); } + /* After all data has been read and versioned, uses LIB_TAG_NEW. Theoretically this should + * not be calculated in the undo case, but it is currently needed even on undo to recalculate + * a cache. */ + ntreeUpdateAllNew(bfd->main); + placeholders_ensure_valid(bfd->main); BKE_main_id_tag_all(bfd->main, LIB_TAG_NEW, false); From 351721d0eaa5e9bb216ac3ef232eec68d3491710 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Tue, 12 Oct 2021 16:59:06 -0500 Subject: [PATCH 0733/1500] BLI: Overload float4x4 multiplication-assignment operator This looks a lot nicer than writing `mul_m4_m4_post` instead. Differential Revision: https://developer.blender.org/D12844 --- source/blender/blenlib/BLI_float4x4.hh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index 14e61d53845..b7f839f4ddf 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -124,6 +124,11 @@ struct float4x4 { return result; } + void operator*=(const float4x4 &other) + { + mul_m4_m4_post(values, other.values); + } + /** * This also applies the translation on the vector. Use `m.ref_3x3() * v` if that is not * intended. From 9e3c84a5d68d139614998cc8f9b30d6251fa335c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 12 Oct 2021 17:33:59 -0500 Subject: [PATCH 0734/1500] Geometry Nodes: Add units to set radius node inputs --- .../blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc | 3 ++- .../blender/nodes/geometry/nodes/node_geo_set_point_radius.cc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index 446e63d0471..73599ed4f50 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -21,7 +21,8 @@ namespace blender::nodes { static void geo_node_set_curve_radius_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Radius").min(0.0f).default_value(1.0f).supports_field(); + b.add_input("Radius").min(0.0f).default_value(1.0f).supports_field().subtype( + PROP_DISTANCE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index 18646c789b4..c98976dd490 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -21,7 +21,8 @@ namespace blender::nodes { static void geo_node_set_point_radius_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Radius").default_value(0.05f).min(0.0f).supports_field(); + b.add_input("Radius").default_value(0.05f).min(0.0f).supports_field().subtype( + PROP_DISTANCE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Geometry"); } From 53af51ad50ec43ee960eb9125f0adf5c37e2a5cd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 12 Oct 2021 17:43:41 -0500 Subject: [PATCH 0735/1500] Geometry Nodes: Add "XYZ" label to instance on points scale --- .../blender/nodes/geometry/nodes/node_geo_instance_on_points.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 8c0c0763be8..047fdd0cd57 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -44,6 +44,7 @@ static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b) .description("Rotation of the instances"); b.add_input("Scale") .default_value({1.0f, 1.0f, 1.0f}) + .subtype(PROP_XYZ) .supports_field() .description("Scale of the instances"); b.add_input("Stable ID") From 3021babf38f88a7e8099189ffa84addf84430dfe Mon Sep 17 00:00:00 2001 From: William Leeson Date: Wed, 13 Oct 2021 11:08:11 +0200 Subject: [PATCH 0736/1500] Fix: Stops assert when baking in debug mode. When baking in a debug build running gdb it kept asserting because a GL context was being created outside the main thread. To fix this the patch only creates the GL context is only created for rendering (when it is actually used). Reviewed By: sergey Differential Revision: https://developer.blender.org/D12767 --- intern/cycles/blender/blender_session.cpp | 41 +++++++++++++++++------ intern/cycles/blender/blender_session.h | 2 ++ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 0bee265557f..38dbb6ab8b1 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -158,15 +158,6 @@ void BlenderSession::create_session() b_v3d, b_rv3d, scene->camera, width, height); session->reset(session_params, buffer_params); - /* Create GPU display. - * TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */ - if (!b_engine.is_preview() && !headless) { - unique_ptr display_driver = make_unique(b_engine, - b_scene); - display_driver_ = display_driver.get(); - session->set_display_driver(move(display_driver)); - } - /* Viewport and preview (as in, material preview) does not do tiled rendering, so can inform * engine that no tracking of the tiles state is needed. * The offline rendering will make a decision when tile is being written. The penalty of asking @@ -355,6 +346,7 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) } /* Create driver to write out render results. */ + ensure_display_driver_if_needed(); session->set_output_driver(make_unique(b_engine)); session->full_buffer_written_cb = [&](string_view filename) { full_buffer_written(filename); }; @@ -727,6 +719,8 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) /* unlock */ session->scene->mutex.unlock(); + ensure_display_driver_if_needed(); + /* Start rendering thread, if it's not running already. Do this * after all scene data has been synced at least once. */ session->start(); @@ -763,8 +757,10 @@ void BlenderSession::draw(BL::SpaceImageEditor &space_image) draw_state_.last_pass_index = pass_index; } - BL::Array zoom = space_image.zoom(); - display_driver_->set_zoom(zoom[0], zoom[1]); + if (display_driver_) { + BL::Array zoom = space_image.zoom(); + display_driver_->set_zoom(zoom[0], zoom[1]); + } session->draw(); } @@ -979,4 +975,27 @@ void BlenderSession::free_blender_memory_if_possible() b_engine.free_blender_memory(); } +void BlenderSession::ensure_display_driver_if_needed() +{ + if (display_driver_) { + /* Driver is already created. */ + return; + } + + if (headless) { + /* No display needed for headless. */ + return; + } + + if (b_engine.is_preview()) { + /* TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */ + return; + } + + unique_ptr display_driver = make_unique(b_engine, + b_scene); + display_driver_ = display_driver.get(); + session->set_display_driver(move(display_driver)); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index fef6ad1adfc..7d3be5f8054 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -146,6 +146,8 @@ class BlenderSession { */ void free_blender_memory_if_possible(); + void ensure_display_driver_if_needed(); + struct { thread_mutex mutex; int last_pass_index = -1; From 9c412b6e2d4d5ae3c0cbd4a8d0249a3c12c6bd65 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 13 Oct 2021 10:06:30 +0200 Subject: [PATCH 0737/1500] Fix possible integer overflow in Cycles baking Ensure math happens on size_t type instead of int followed by a cast to the size_t. --- intern/cycles/integrator/pass_accessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/integrator/pass_accessor.cpp b/intern/cycles/integrator/pass_accessor.cpp index 4f76f1fa9df..4a0b1ed6ece 100644 --- a/intern/cycles/integrator/pass_accessor.cpp +++ b/intern/cycles/integrator/pass_accessor.cpp @@ -96,7 +96,7 @@ static void pad_pixels(const BufferParams &buffer_params, return; } - const size_t size = buffer_params.width * buffer_params.height; + const size_t size = static_cast(buffer_params.width) * buffer_params.height; if (destination.pixels) { float *pixel = destination.pixels; From f12513a21ccef5133baa4144074a8a20db2cc5b4 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 13 Oct 2021 10:34:17 +0200 Subject: [PATCH 0738/1500] Fix Cycles backing issues when using multiple devices The pixel accessor was not aware of possible offset in the pixel padding causing some slices of the result not being properly padded. --- intern/cycles/integrator/pass_accessor.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/intern/cycles/integrator/pass_accessor.cpp b/intern/cycles/integrator/pass_accessor.cpp index 4a0b1ed6ece..4ef9ce7ef42 100644 --- a/intern/cycles/integrator/pass_accessor.cpp +++ b/intern/cycles/integrator/pass_accessor.cpp @@ -98,7 +98,10 @@ static void pad_pixels(const BufferParams &buffer_params, const size_t size = static_cast(buffer_params.width) * buffer_params.height; if (destination.pixels) { - float *pixel = destination.pixels; + const size_t pixel_stride = destination.pixel_stride ? destination.pixel_stride : + destination.num_components; + + float *pixel = destination.pixels + pixel_stride * destination.offset; for (size_t i = 0; i < size; i++, pixel += dest_num_components) { if (dest_num_components >= 3 && src_num_components == 1) { @@ -113,7 +116,7 @@ static void pad_pixels(const BufferParams &buffer_params, if (destination.pixels_half_rgba) { const half one = float_to_half(1.0f); - half4 *pixel = destination.pixels_half_rgba; + half4 *pixel = destination.pixels_half_rgba + destination.offset; for (size_t i = 0; i < size; i++, pixel++) { if (dest_num_components >= 3 && src_num_components == 1) { From 86536e785902c35a197bfef099693c00e8fc1bee Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 13 Oct 2021 11:46:38 +0200 Subject: [PATCH 0739/1500] Fix Cycles assert in viewport after recent change Create display early on, so that ready_to_reset() passes assert test for use for display actually configured. --- intern/cycles/blender/blender_session.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 38dbb6ab8b1..8ecd7ee93d6 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -657,6 +657,8 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) if (!b_v3d) return; + ensure_display_driver_if_needed(); + /* on session/scene parameter changes, we recreate session entirely */ const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); @@ -719,8 +721,6 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) /* unlock */ session->scene->mutex.unlock(); - ensure_display_driver_if_needed(); - /* Start rendering thread, if it's not running already. Do this * after all scene data has been synced at least once. */ session->start(); From 0558907ae674ebe81dc8910a6615fc32ce675d70 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 13 Oct 2021 20:59:56 +1100 Subject: [PATCH 0740/1500] Fix T92136: Leak accessing evaluated depsgraph data from Python --- .../depsgraph/intern/eval/deg_eval_copy_on_write.cc | 11 +++++++++++ .../depsgraph/intern/eval/deg_eval_runtime_backup.cc | 2 ++ 2 files changed, 13 insertions(+) diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index a844d23b558..610e4860108 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -853,6 +853,10 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, if (!deg_copy_on_write_is_needed(id_orig)) { return id_cow; } + /* Avoid removing & re-creating the reference if it exists. */ + void *py_instance = id_cow->py_instance; + id_cow->py_instance = nullptr; + DEG_COW_PRINT( "Expanding datablock for %s: id_orig=%p id_cow=%p\n", id_orig->name, id_orig, id_cow); /* Sanity checks. */ @@ -925,6 +929,7 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, * from above. */ update_id_after_copy(depsgraph, id_node, id_orig, id_cow); id_cow->recalc = id_cow_recalc; + id_cow->py_instance = py_instance; return id_cow; } @@ -1042,6 +1047,11 @@ void discard_edit_mode_pointers(ID *id_cow) * - Does not free data-block itself. */ void deg_free_copy_on_write_datablock(ID *id_cow) { + /* There may be Python references to to shallow copies, see: T92136. */ + if (id_cow->py_instance) { + BKE_libblock_free_data_py(id_cow); + } + if (!check_datablock_expanded(id_cow)) { /* Actual content was never copied on top of CoW block, we have * nothing to free. */ @@ -1103,6 +1113,7 @@ void deg_tag_copy_on_write_id(ID *id_cow, const ID *id_orig) /* This ID is no longer localized, is a self-sustaining copy now. */ id_cow->tag &= ~LIB_TAG_LOCALIZED; id_cow->orig_id = (ID *)id_orig; + BLI_assert(id_cow->py_instance == nullptr); } bool deg_copy_on_write_is_expanded(const ID *id_cow) diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc index 7893e8c64c1..c6e3532aca9 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc @@ -52,7 +52,9 @@ void RuntimeBackup::init_from_id(ID *id) } have_backup = true; + /* Clear, so freeing the expanded data doesn't remove this Python reference. */ id_data.py_instance = id->py_instance; + id->py_instance = nullptr; animation_backup.init_from_id(id); From f71d479556b23ac012cfcf90530a1d9740955349 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 13 Oct 2021 11:57:47 +0200 Subject: [PATCH 0741/1500] Fix Cycles viewport after session reset Happens i.e. when changing compute device. A more proper follow-up to the on-demand display driver creation change. --- intern/cycles/blender/blender_session.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 8ecd7ee93d6..0b4b5c60def 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -270,6 +270,8 @@ void BlenderSession::free_session() delete session; session = nullptr; + + display_driver_ = nullptr; } void BlenderSession::full_buffer_written(string_view filename) @@ -657,8 +659,6 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) if (!b_v3d) return; - ensure_display_driver_if_needed(); - /* on session/scene parameter changes, we recreate session entirely */ const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); @@ -670,6 +670,8 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) create_session(); } + ensure_display_driver_if_needed(); + /* increase samples and render time, but never decrease */ session->set_samples(session_params.samples); session->set_time_limit(session_params.time_limit); From 8c0698460be9e435560b0b00474900492a614ac0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 13 Oct 2021 21:29:27 +1100 Subject: [PATCH 0742/1500] Revert "Fix T92136: Leak accessing evaluated depsgraph data from Python" This reverts commit 0558907ae674ebe81dc8910a6615fc32ce675d70. Based on discussion with Sergey, having Python references to un-expanded data should not happen - this change needs to be reconsidered. --- .../depsgraph/intern/eval/deg_eval_copy_on_write.cc | 11 ----------- .../depsgraph/intern/eval/deg_eval_runtime_backup.cc | 2 -- 2 files changed, 13 deletions(-) diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index 610e4860108..a844d23b558 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -853,10 +853,6 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, if (!deg_copy_on_write_is_needed(id_orig)) { return id_cow; } - /* Avoid removing & re-creating the reference if it exists. */ - void *py_instance = id_cow->py_instance; - id_cow->py_instance = nullptr; - DEG_COW_PRINT( "Expanding datablock for %s: id_orig=%p id_cow=%p\n", id_orig->name, id_orig, id_cow); /* Sanity checks. */ @@ -929,7 +925,6 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, * from above. */ update_id_after_copy(depsgraph, id_node, id_orig, id_cow); id_cow->recalc = id_cow_recalc; - id_cow->py_instance = py_instance; return id_cow; } @@ -1047,11 +1042,6 @@ void discard_edit_mode_pointers(ID *id_cow) * - Does not free data-block itself. */ void deg_free_copy_on_write_datablock(ID *id_cow) { - /* There may be Python references to to shallow copies, see: T92136. */ - if (id_cow->py_instance) { - BKE_libblock_free_data_py(id_cow); - } - if (!check_datablock_expanded(id_cow)) { /* Actual content was never copied on top of CoW block, we have * nothing to free. */ @@ -1113,7 +1103,6 @@ void deg_tag_copy_on_write_id(ID *id_cow, const ID *id_orig) /* This ID is no longer localized, is a self-sustaining copy now. */ id_cow->tag &= ~LIB_TAG_LOCALIZED; id_cow->orig_id = (ID *)id_orig; - BLI_assert(id_cow->py_instance == nullptr); } bool deg_copy_on_write_is_expanded(const ID *id_cow) diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc index c6e3532aca9..7893e8c64c1 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc @@ -52,9 +52,7 @@ void RuntimeBackup::init_from_id(ID *id) } have_backup = true; - /* Clear, so freeing the expanded data doesn't remove this Python reference. */ id_data.py_instance = id->py_instance; - id->py_instance = nullptr; animation_backup.init_from_id(id); From 59113df8ec3c90c123c6c215083365aca4ea4732 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 13 Oct 2021 13:33:58 +0200 Subject: [PATCH 0743/1500] Fix T92113: On assets, replace "Fake User" button with "Clear Asset" button. Change is simple enough, but we abuse a bit the UI code here to get a similar 'look' as the fake user button for the new Asset one, while still being able to call an operator instead of editing directly a RNA value. Reviewed By: Severin, sybren Maniphest Tasks: T92113 Differential Revision: https://developer.blender.org/D12839 --- .../editors/interface/interface_templates.c | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 458ffd3f053..755a0fce7bc 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1112,24 +1112,41 @@ static void template_ID(const bContext *C, UI_but_flag_enable(but, UI_BUT_REDALERT); } - if (!ID_IS_LINKED(id) && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_OB, ID_WS)) && - (hide_buttons == false)) { - uiDefIconButR(block, - UI_BTYPE_ICON_TOGGLE, - 0, - ICON_FAKE_USER_OFF, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - &idptr, - "use_fake_user", - -1, - 0, - 0, - -1, - -1, - NULL); + if (!ID_IS_LINKED(id)) { + if (ID_IS_ASSET(id)) { + uiDefIconButO(block, + /* Using `_N` version allows us to get the 'active' state by default. */ + UI_BTYPE_ICON_TOGGLE_N, + "ASSET_OT_clear", + WM_OP_INVOKE_DEFAULT, + /* 'active' state of a toggle button uses icon + 1, so to get proper asset + * icon we need to pass its value - 1 here. */ + ICON_ASSET_MANAGER - 1, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + NULL); + } + else if (!(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_OB, ID_WS)) && + (hide_buttons == false)) { + uiDefIconButR(block, + UI_BTYPE_ICON_TOGGLE, + 0, + ICON_FAKE_USER_OFF, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + &idptr, + "use_fake_user", + -1, + 0, + 0, + -1, + -1, + NULL); + } } } From 72e81a45c4ce3963a5aa929adb00d762db7e07ef Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Wed, 13 Oct 2021 20:56:27 +0900 Subject: [PATCH 0744/1500] Extern: Add modifications diff for TinyGLTF This should have been added in ee49991999c9. --- extern/tinygltf/patches/TinyGLTF.diff | Bin 0 -> 896 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 extern/tinygltf/patches/TinyGLTF.diff diff --git a/extern/tinygltf/patches/TinyGLTF.diff b/extern/tinygltf/patches/TinyGLTF.diff new file mode 100644 index 0000000000000000000000000000000000000000..411bd5621f3691c42467d9ec41f220d243711445 GIT binary patch literal 896 zcmbu7OHaa35QWd$#Q$(tMub9rfyGB~>&lf|(ieypY5ODqu=M03)1?%X+N z9-YsROcRZOQvk-Cme{d6O4aAR)KpLYJJC#U8WL0B9buK46K|Xuj6!YQ!%ep0PGMKM zjkT*CuaYU(D;kG7(=LyE#A2n^nAm+WY*Vw@9-9S#=E~} zI~@?4qtb$z`e@U2dRTFt_V70Fy!$yhtQs#~)#(!7dWMq8kG<{%6EDe|kUiHG*&E>e z7p=rs66HUFWv^WVM-d_O{ZfJKa{pc67lc e?H|~7W*bVT*#XYqkBGFYtpnSjI-^-XXYvLA5Qy^t literal 0 HcmV?d00001 From 518365395152e516af235366f4be908a40343b87 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Wed, 13 Oct 2021 14:28:55 +0200 Subject: [PATCH 0745/1500] UI: Make menu item use theme roundness Menu items ignore the roundness setting since they spread left to right. This patch makes it so menu items use the theme preference instead of hardcoded square corners. Providing more flexibility to themes. All built-in and included themes already have this set so no need to update them. For the default themes (Dark/Light) roundness is 0.4. {F10950727, size=full} The motivations behind this change are: * To be more consistent with other widgets. * Improve themes flexibility. * Match padding with other elements that have like the Search field: {F10950746, size=full} Reviewed By: #user_interface, Severin Differential Revision: https://developer.blender.org/D12813 --- source/blender/editors/interface/interface_widgets.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index a1534ab4b7f..6727d812e1e 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -4052,9 +4052,15 @@ static void widget_menu_itembut(uiWidgetColors *wcol, uiWidgetBase wtb; widget_init(&wtb); - /* not rounded, no outline */ + /* Padding on the sides. */ + const float padding = 0.125f * BLI_rcti_size_y(rect); + rect->xmin += padding; + rect->xmax -= padding; + + /* No outline. */ wtb.draw_outline = false; - round_box_edges(&wtb, 0, rect, 0.0f); + const float rad = wcol->roundness * BLI_rcti_size_y(rect); + round_box_edges(&wtb, UI_CNR_ALL, rect, rad); widgetbase_draw(&wtb, wcol); } From d4e8390e95d9663a32012dcc39a9bbe08330e75b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 13 Oct 2021 15:36:52 +0200 Subject: [PATCH 0746/1500] Fix T92153: use-after-free with anonymous attributes Differential Revision: https://developer.blender.org/D12851 --- source/blender/blenkernel/intern/anonymous_attribute.cc | 1 + source/blender/blenkernel/intern/customdata.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/source/blender/blenkernel/intern/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc index 67611053d83..22c2f83e8be 100644 --- a/source/blender/blenkernel/intern/anonymous_attribute.cc +++ b/source/blender/blenkernel/intern/anonymous_attribute.cc @@ -97,6 +97,7 @@ void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anony { const int new_refcount = anonymous_id->refcount_tot.fetch_sub(1) - 1; if (new_refcount == 0) { + BLI_assert(anonymous_id->refcount_strong == 0); delete anonymous_id; } } diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 3bb02e1856b..d86b8163ebc 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -2595,6 +2595,11 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, data->layers[index] = data->layers[index - 1]; } + /* Clear remaining data on the layer. The original data on the layer has been moved to another + * index. Without this, it can happen that information from the previous layer at that index + * leaks into the new layer. */ + memset(data->layers + index, 0, sizeof(CustomDataLayer)); + data->layers[index].type = type; data->layers[index].flag = flag; data->layers[index].data = newlayerdata; From 24cc552cf48694b5ed07d885c0cc69220cbbe34b Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Wed, 13 Oct 2021 08:39:54 -0500 Subject: [PATCH 0747/1500] Geometry Nodes: Material Index, Set Material Add Get/Set Nodes for Material Index Rename Assign Material to Set Material Differential Revision: https://developer.blender.org/D12837 --- release/scripts/startup/nodeitems_builtins.py | 32 ++++++-- source/blender/blenkernel/BKE_node.h | 4 +- source/blender/blenkernel/intern/node.cc | 4 +- .../blenloader/intern/versioning_300.c | 15 ++++ source/blender/nodes/CMakeLists.txt | 4 +- source/blender/nodes/NOD_geometry.h | 4 +- source/blender/nodes/NOD_static_types.h | 4 +- .../nodes/node_geo_input_material_index.cc | 42 ++++++++++ ...ial_assign.cc => node_geo_set_material.cc} | 12 +-- .../nodes/node_geo_set_material_index.cc | 78 +++++++++++++++++++ 10 files changed, 180 insertions(+), 19 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc rename source/blender/nodes/geometry/nodes/{node_geo_material_assign.cc => node_geo_set_material.cc} (87%) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index f63e41b0c28..40afcc69bd2 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -180,6 +180,29 @@ def geometry_input_node_items(context): yield NodeItem("GeometryNodeInputPosition") yield NodeItem("GeometryNodeInputRadius") +# Custom Menu for Material Node Input Nodes +def geometry_material_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + if not space.edit_tree: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("GeometryNodeLegacyMaterialAssign") + yield NodeItem("GeometryNodeLegacySelectByMaterial") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeMaterialReplace") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeInputMaterialIndex") + yield NodeItem("GeometryNodeMaterialSelection") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeSetMaterial") + yield NodeItem("GeometryNodeSetMaterialIndex") + # Custom Menu for Geometry Node Curves def point_node_items(context): if context is None: @@ -662,14 +685,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeRealizeInstances"), ]), GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items), - GeometryNodeCategory("GEO_MATERIAL", "Material", items=[ - NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_legacy_poll), - - NodeItem("GeometryNodeMaterialAssign"), - NodeItem("GeometryNodeMaterialSelection"), - NodeItem("GeometryNodeMaterialReplace"), - ]), + GeometryNodeCategory("GEO_MATERIAL", "Material", items=geometry_material_node_items), GeometryNodeCategory("GEO_MESH", "Mesh", items=mesh_node_items), GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[ NodeItem("GeometryNodeMeshCircle"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 81cf1ed180f..026b6574374 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1494,7 +1494,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_INPUT_NORMAL 1079 #define GEO_NODE_ATTRIBUTE_CAPTURE 1080 #define GEO_NODE_MATERIAL_SELECTION 1081 -#define GEO_NODE_MATERIAL_ASSIGN 1082 +#define GEO_NODE_SET_MATERIAL 1082 #define GEO_NODE_REALIZE_INSTANCES 1083 #define GEO_NODE_ATTRIBUTE_STATISTIC 1084 #define GEO_NODE_CURVE_SAMPLE 1085 @@ -1530,6 +1530,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_SET_SPLINE_RESOLUTION 1115 #define GEO_NODE_SET_SPLINE_CYCLIC 1116 #define GEO_NODE_SET_POINT_RADIUS 1117 +#define GEO_NODE_INPUT_MATERIAL_INDEX 1118 +#define GEO_NODE_SET_MATERIAL_INDEX 1119 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3e577bc29a3..46022b4f4c1 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5772,6 +5772,7 @@ static void registerGeometryNodes() register_node_type_geo_input_curve_tilt(); register_node_type_geo_input_index(); register_node_type_geo_input_material(); + register_node_type_geo_input_material_index(); register_node_type_geo_input_normal(); register_node_type_geo_input_position(); register_node_type_geo_input_radius(); @@ -5783,7 +5784,6 @@ static void registerGeometryNodes() register_node_type_geo_instance_on_points(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); - register_node_type_geo_material_assign(); register_node_type_geo_material_replace(); register_node_type_geo_material_selection(); register_node_type_geo_mesh_primitive_circle(); @@ -5815,6 +5815,8 @@ static void registerGeometryNodes() register_node_type_geo_set_curve_handles(); register_node_type_geo_set_curve_radius(); register_node_type_geo_set_curve_tilt(); + register_node_type_geo_set_material(); + register_node_type_geo_set_material_index(); register_node_type_geo_set_point_radius(); register_node_type_geo_set_position(); register_node_type_geo_set_shade_smooth(); diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 617cd8b6c58..db2bb73108b 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1746,5 +1746,20 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != GEO_NODE_SET_MATERIAL) { + continue; + } + if (strstr(node->idname, "SetMaterial")) { + /* Make sure we haven't changed this idname already. */ + continue; + } + strcpy(node->idname, "GeometryNodeSetMaterial"); + } + } } } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index f22b890b243..aad61113e4c 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -224,6 +224,7 @@ set(SRC geometry/nodes/node_geo_input_curve_tilt.cc geometry/nodes/node_geo_input_index.cc geometry/nodes/node_geo_input_material.cc + geometry/nodes/node_geo_input_material_index.cc geometry/nodes/node_geo_input_normal.cc geometry/nodes/node_geo_input_position.cc geometry/nodes/node_geo_input_radius.cc @@ -235,7 +236,6 @@ set(SRC geometry/nodes/node_geo_instance_on_points.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc - geometry/nodes/node_geo_material_assign.cc geometry/nodes/node_geo_material_replace.cc geometry/nodes/node_geo_material_selection.cc geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -258,6 +258,8 @@ set(SRC geometry/nodes/node_geo_set_curve_handles.cc geometry/nodes/node_geo_set_curve_radius.cc geometry/nodes/node_geo_set_curve_tilt.cc + geometry/nodes/node_geo_set_material.cc + geometry/nodes/node_geo_set_material_index.cc geometry/nodes/node_geo_set_point_radius.cc geometry/nodes/node_geo_set_position.cc geometry/nodes/node_geo_set_shade_smooth.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 2d409d6d80b..50142eeb559 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -92,6 +92,7 @@ void register_node_type_geo_input_curve_handles(void); void register_node_type_geo_input_curve_tilt(void); void register_node_type_geo_input_index(void); void register_node_type_geo_input_material(void); +void register_node_type_geo_input_material_index(void); void register_node_type_geo_input_normal(void); void register_node_type_geo_input_position(void); void register_node_type_geo_input_radius(void); @@ -103,7 +104,6 @@ void register_node_type_geo_input_tangent(void); void register_node_type_geo_instance_on_points(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); -void register_node_type_geo_material_assign(void); void register_node_type_geo_material_replace(void); void register_node_type_geo_material_selection(void); void register_node_type_geo_mesh_primitive_circle(void); @@ -136,6 +136,8 @@ void register_node_type_geo_separate_geometry(void); void register_node_type_geo_set_curve_handles(void); void register_node_type_geo_set_curve_radius(void); void register_node_type_geo_set_curve_tilt(void); +void register_node_type_geo_set_material(void); +void register_node_type_geo_set_material_index(void); void register_node_type_geo_set_point_radius(void); void register_node_type_geo_set_position(void); void register_node_type_geo_set_shade_smooth(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index d4cc7b42292..30807073e06 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -352,6 +352,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", In DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") +DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "") @@ -363,7 +364,6 @@ DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") -DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Assign Material", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Replace Material", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") @@ -386,6 +386,8 @@ DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SE DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "") +DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "") +DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "") DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc new file mode 100644 index 00000000000..702c83daea0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_material_index_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Material Index").field_source(); +} + +static void geo_node_input_material_index_exec(GeoNodeExecParams params) +{ + Field material_index_field = AttributeFieldInput::Create("material_index"); + params.set_output("Material Index", std::move(material_index_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_material_index() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL_INDEX, "Material Index", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_material_index_exec; + ntype.declare = blender::nodes::geo_node_input_material_index_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc similarity index 87% rename from source/blender/nodes/geometry/nodes/node_geo_material_assign.cc rename to source/blender/nodes/geometry/nodes/node_geo_set_material.cc index 26c1aabf39f..ab7d2ab250d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -26,7 +26,7 @@ namespace blender::nodes { -static void geo_node_material_assign_declare(NodeDeclarationBuilder &b) +static void geo_node_set_material_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_input("Material").hide_label(); @@ -57,7 +57,7 @@ static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Mate } } -static void geo_node_material_assign_exec(GeoNodeExecParams params) +static void geo_node_set_material_exec(GeoNodeExecParams params) { Material *material = params.extract_input("Material"); const Field selection_field = params.extract_input>("Selection"); @@ -86,12 +86,12 @@ static void geo_node_material_assign_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_material_assign() +void register_node_type_geo_set_material() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Assign Material", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_material_assign_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec; + geo_node_type_base(&ntype, GEO_NODE_SET_MATERIAL, "Set Material", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_set_material_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_set_material_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc new file mode 100644 index 00000000000..ebf6b43ae30 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_material_index_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Material Index").supports_field().min(0); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Geometry"); +} + +static void set_material_index_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &index_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed indexes = component.attribute_try_get_for_output_only( + "material_index", ATTR_DOMAIN_FACE); + fn::FieldEvaluator material_evaluator{field_context, &selection}; + material_evaluator.add_with_destination(index_field, indexes.varray()); + material_evaluator.evaluate(); + indexes.save(); +} + +static void geo_node_set_material_index_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field index_field = params.extract_input>("Material Index"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_mesh()) { + set_material_index_in_component( + geometry_set.get_component_for_write(), selection_field, index_field); + } + }); + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_material_index() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_MATERIAL_INDEX, "Set Material Index", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_material_index_exec; + ntype.declare = blender::nodes::geo_node_set_material_index_declare; + nodeRegisterType(&ntype); +} From e659f78d3e5892f98c4855907a9cf91af12638e8 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 13 Oct 2021 15:42:48 +0200 Subject: [PATCH 0748/1500] Cleanup: use typedef struct for BLODataBlockInfo. --- source/blender/blenloader/BLO_readfile.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index fa29b09af02..4656bc64a1f 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -163,10 +163,10 @@ void BLO_blendfiledata_free(BlendFileData *bfd); /** \name BLO Blend File Handle API * \{ */ -struct BLODataBlockInfo { +typedef struct BLODataBlockInfo { char name[64]; /* MAX_NAME */ struct AssetMetaData *asset_data; -}; +} BLODataBlockInfo; BlendHandle *BLO_blendhandle_from_file(const char *filepath, struct BlendFileReadReport *reports); BlendHandle *BLO_blendhandle_from_memory(const void *mem, From c29f20a52151b90734edb7b4ebbd9b7b49e65d73 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 13 Oct 2021 12:57:49 +0200 Subject: [PATCH 0749/1500] Cleanup: Remove unused code paths in the depsgraph copy-on-write Seems to be residue from an early 2.80 days: the placeholder code path is no longer used. Remove all the tricky code for this, and make it clear that the `deg_expand_copy_on_write_datablock` is used on an non-expanded datablock. Should be no functional changes. And should help simplify D12850. Differential Revision: https://developer.blender.org/D12852 --- .../intern/eval/deg_eval_copy_on_write.cc | 66 +++---------------- .../intern/eval/deg_eval_copy_on_write.h | 12 ---- 2 files changed, 8 insertions(+), 70 deletions(-) diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index a844d23b558..b4a91944b65 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -511,12 +511,6 @@ inline bool check_datablock_expanded(const ID *id_cow) struct RemapCallbackUserData { /* Dependency graph for which remapping is happening. */ const Depsgraph *depsgraph; - /* Create placeholder for ID nodes for cases when we need to remap original - * ID to it[s CoW version but we don't have required ID node yet. - * - * This happens when expansion happens a ta construction time. */ - DepsgraphNodeBuilder *node_builder; - bool create_placeholders; }; int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) @@ -526,38 +520,11 @@ int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) return IDWALK_RET_NOP; } - ID *id_self = cb_data->id_self; RemapCallbackUserData *user_data = (RemapCallbackUserData *)cb_data->user_data; const Depsgraph *depsgraph = user_data->depsgraph; ID *id_orig = *id_p; if (deg_copy_on_write_is_needed(id_orig)) { - ID *id_cow; - if (user_data->create_placeholders) { - /* Special workaround to stop creating temp datablocks for - * objects which are coming from scene's collection and which - * are never linked to any of layers. - * - * TODO(sergey): Ideally we need to tell ID looper to ignore - * those or at least make it more reliable check where the - * pointer is coming from. */ - const ID_Type id_type = GS(id_orig->name); - const ID_Type id_type_self = GS(id_self->name); - if (id_type == ID_OB && id_type_self == ID_SCE) { - IDNode *id_node = depsgraph->find_id_node(id_orig); - if (id_node == nullptr) { - id_cow = id_orig; - } - else { - id_cow = id_node->id_cow; - } - } - else { - id_cow = user_data->node_builder->ensure_cow_id(id_orig); - } - } - else { - id_cow = depsgraph->get_cow_id(id_orig); - } + ID *id_cow = depsgraph->get_cow_id(id_orig); BLI_assert(id_cow != nullptr); DEG_COW_PRINT( " Remapping datablock for %s: id_orig=%p id_cow=%p\n", id_orig->name, id_orig, id_cow); @@ -834,34 +801,28 @@ int foreach_libblock_validate_callback(LibraryIDLinkCallbackData *cb_data) return IDWALK_RET_NOP; } -} // namespace - /* Actual implementation of logic which "expands" all the data which was not * yet copied-on-write. * * NOTE: Expects that CoW datablock is empty. */ -ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, - const IDNode *id_node, - DepsgraphNodeBuilder *node_builder, - bool create_placeholders) +ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode *id_node) { const ID *id_orig = id_node->id_orig; ID *id_cow = id_node->id_cow; const int id_cow_recalc = id_cow->recalc; + /* No need to expand such datablocks, their copied ID is same as original * one already. */ if (!deg_copy_on_write_is_needed(id_orig)) { return id_cow; } + DEG_COW_PRINT( "Expanding datablock for %s: id_orig=%p id_cow=%p\n", id_orig->name, id_orig, id_cow); + /* Sanity checks. */ - /* NOTE: Disabled for now, conflicts when re-using evaluated datablock when - * rebuilding dependencies. */ - if (check_datablock_expanded(id_cow) && create_placeholders) { - deg_free_copy_on_write_datablock(id_cow); - } - // BLI_assert(check_datablock_expanded(id_cow) == false); + BLI_assert(check_datablock_expanded(id_cow) == false); + /* Copy data from original ID to a copied version. */ /* TODO(sergey): Avoid doing full ID copy somehow, make Mesh to reference * original geometry arrays for until those are modified. */ @@ -914,8 +875,6 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, /* Perform remapping of the nodes. */ RemapCallbackUserData user_data = {nullptr}; user_data.depsgraph = depsgraph; - user_data.node_builder = node_builder; - user_data.create_placeholders = create_placeholders; BKE_library_foreach_ID_link(nullptr, id_cow, foreach_libblock_remap_callback, @@ -928,16 +887,7 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, return id_cow; } -/* NOTE: Depsgraph is supposed to have ID node already. */ -ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, - ID *id_orig, - DepsgraphNodeBuilder *node_builder, - bool create_placeholders) -{ - IDNode *id_node = depsgraph->find_id_node(id_orig); - BLI_assert(id_node != nullptr); - return deg_expand_copy_on_write_datablock(depsgraph, id_node, node_builder, create_placeholders); -} +} // namespace ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode *id_node) { diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h index d0bb841caab..70e510b5ef9 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h @@ -50,18 +50,6 @@ struct Depsgraph; class DepsgraphNodeBuilder; struct IDNode; -/* Get fully expanded (ready for use) copy-on-write data-block for the given - * original data-block. - */ -ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, - const IDNode *id_node, - DepsgraphNodeBuilder *node_builder = nullptr, - bool create_placeholders = false); -ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, - struct ID *id_orig, - DepsgraphNodeBuilder *node_builder = nullptr, - bool create_placeholders = false); - /* Makes sure given CoW data-block is brought back to state of the original * data-block. */ From 356dce13f05faed5303ac1aacf68dc6c1ff51d80 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Oct 2021 00:49:54 +1100 Subject: [PATCH 0750/1500] Fix T92136: Leak accessing evaluated depsgraph data from Python Copy-on-write data blocks could be referenced from python but were not properly managing python reference counting. This would leak memory for any evaluated data-blocks accessed by Python. Reviewed By: sergey Ref D12850 --- source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc | 2 ++ source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc | 2 ++ 2 files changed, 4 insertions(+) diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index b4a91944b65..68a72638c7d 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -822,6 +822,7 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode /* Sanity checks. */ BLI_assert(check_datablock_expanded(id_cow) == false); + BLI_assert(id_cow->py_instance == nullptr); /* Copy data from original ID to a copied version. */ /* TODO(sergey): Avoid doing full ID copy somehow, make Mesh to reference @@ -1015,6 +1016,7 @@ void deg_free_copy_on_write_datablock(ID *id_cow) break; } discard_edit_mode_pointers(id_cow); + BKE_libblock_free_data_py(id_cow); BKE_libblock_free_datablock(id_cow, 0); BKE_libblock_free_data(id_cow, false); /* Signal datablock as not being expanded. */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc index 7893e8c64c1..8bf64af7d5d 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc @@ -52,7 +52,9 @@ void RuntimeBackup::init_from_id(ID *id) } have_backup = true; + /* Clear, so freeing the expanded data doesn't touch this Python reference. */ id_data.py_instance = id->py_instance; + id->py_instance = nullptr; animation_backup.init_from_id(id); From 9d49fc2ba0ecd02a7b5ba187f6e0ad29882db4e0 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Wed, 13 Oct 2021 09:02:05 -0500 Subject: [PATCH 0751/1500] Geometry Nodes: Translate Instances Node Adds a node that can translate instances in the transform space of the modifier object, or the local space of their original transform. One reason to have a special node for instances is that they always have the existing transform, unlike mesh or point cloud points. Differential Revision: https://developer.blender.org/D12679 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_geo_translate_instances.cc | 84 +++++++++++++++++++ 7 files changed, 90 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 40afcc69bd2..0bc4f6af997 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -683,6 +683,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeSeparateGeometry"), NodeItem("GeometryNodeSetPosition"), NodeItem("GeometryNodeRealizeInstances"), + NodeItem("GeometryNodeTranslateInstances"), ]), GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items), GeometryNodeCategory("GEO_MATERIAL", "Material", items=geometry_material_node_items), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 026b6574374..45001ba6fcb 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1532,6 +1532,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_SET_POINT_RADIUS 1117 #define GEO_NODE_INPUT_MATERIAL_INDEX 1118 #define GEO_NODE_SET_MATERIAL_INDEX 1119 +#define GEO_NODE_TRANSLATE_INSTANCES 1120 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 46022b4f4c1..5dc8909a45f 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5827,6 +5827,7 @@ static void registerGeometryNodes() register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); register_node_type_geo_transform(); + register_node_type_geo_translate_instances(); register_node_type_geo_triangulate(); register_node_type_geo_viewer(); register_node_type_geo_volume_to_mesh(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index aad61113e4c..e4153b7381b 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -269,6 +269,7 @@ set(SRC geometry/nodes/node_geo_string_to_curves.cc geometry/nodes/node_geo_switch.cc geometry/nodes/node_geo_transform.cc + geometry/nodes/node_geo_translate_instances.cc geometry/nodes/node_geo_triangulate.cc geometry/nodes/node_geo_viewer.cc geometry/nodes/node_geo_volume_to_mesh.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 50142eeb559..2da9466ae16 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -148,6 +148,7 @@ void register_node_type_geo_string_to_curves(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); void register_node_type_geo_transform(void); +void register_node_type_geo_translate_instances(void); void register_node_type_geo_triangulate(void); void register_node_type_geo_viewer(void); void register_node_type_geo_volume_to_mesh(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 30807073e06..aca018b5fe9 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -397,6 +397,7 @@ DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") +DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") DefNode(GeometryNode, GEO_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc new file mode 100644 index 00000000000..8fc2843fd8a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_translate_instances_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Translation").subtype(PROP_TRANSLATION).supports_field(); + b.add_input("Local Space").default_value(true).supports_field(); + b.add_output("Geometry"); +}; + +static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +{ + GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_POINT}; + + fn::FieldEvaluator selection_evaluator{field_context, instances_component.instances_amount()}; + selection_evaluator.add(params.extract_input>("Selection")); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + fn::FieldEvaluator transforms_evaluator{field_context, &selection}; + transforms_evaluator.add(params.extract_input>("Translation")); + transforms_evaluator.add(params.extract_input>("Local Space")); + transforms_evaluator.evaluate(); + const VArray &translations = transforms_evaluator.get_evaluated(0); + const VArray &local_spaces = transforms_evaluator.get_evaluated(1); + + MutableSpan instance_transforms = instances_component.instance_transforms(); + + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { + for (const int i_selection : range) { + const int i = selection[i_selection]; + if (local_spaces[i]) { + instance_transforms[i] *= float4x4::from_location(translations[i]); + } + else { + add_v3_v3(instance_transforms[i].values[3], translations[i]); + } + } + }); +} + +static void geo_node_translate_instances_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + if (geometry_set.has_instances()) { + InstancesComponent &instances = geometry_set.get_component_for_write(); + translate_instances(params, instances); + } + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_translate_instances() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_TRANSLATE_INSTANCES, "Translate Instances", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_translate_instances_exec; + ntype.declare = blender::nodes::geo_node_translate_instances_declare; + nodeRegisterType(&ntype); +} From d0a4a41b5d2014ec72f0ac5a149875d1c927ddf3 Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Wed, 13 Oct 2021 09:04:05 -0500 Subject: [PATCH 0752/1500] Geometry Nodes: Add Selection to Instance on Points Add a boolean selection field to the Instance on Points node. This will select which points from the source geometry will be used to create the instances. Differential Revision: https://developer.blender.org/D12847 --- .../nodes/node_geo_instance_on_points.cc | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 047fdd0cd57..c7235fb2c29 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -29,6 +29,7 @@ namespace blender::nodes { static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b) { b.add_input("Points").description("Points to instance on"); + b.add_input("Selection").default_value(true).supports_field().hide_value(); b.add_input("Instance").description("Geometry that is instanced on the points"); b.add_input("Pick Instance") .supports_field() @@ -64,28 +65,38 @@ static void add_instances_from_component(InstancesComponent &dst_component, const AttributeDomain domain = ATTR_DOMAIN_POINT; const int domain_size = src_component.attribute_domain_size(domain); + GeometryComponentFieldContext field_context{src_component, domain}; + const Field selection_field = params.get_input>("Selection"); + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + /* The initial size of the component might be non-zero when this function is called for multiple * component types. */ const int start_len = dst_component.instances_amount(); - dst_component.resize(start_len + domain_size); + const int select_len = selection.index_range().size(); + dst_component.resize(start_len + select_len); + MutableSpan dst_handles = dst_component.instance_reference_handles().slice(start_len, - domain_size); + select_len); MutableSpan dst_transforms = dst_component.instance_transforms().slice(start_len, - domain_size); - MutableSpan dst_stable_ids = dst_component.instance_ids().slice(start_len, domain_size); + select_len); + MutableSpan dst_stable_ids = dst_component.instance_ids().slice(start_len, select_len); - GeometryComponentFieldContext field_context{src_component, domain}; FieldEvaluator field_evaluator{field_context, domain_size}; - const VArray *pick_instance = nullptr; const VArray *indices = nullptr; const VArray *rotations = nullptr; const VArray *scales = nullptr; + /* The evaluator could use the component's stable IDs as a destination directly, but only the + * selected indices should be copied. */ + const VArray *stable_ids = nullptr; field_evaluator.add(params.get_input>("Pick Instance"), &pick_instance); field_evaluator.add(params.get_input>("Instance Index"), &indices); field_evaluator.add(params.get_input>("Rotation"), &rotations); field_evaluator.add(params.get_input>("Scale"), &scales); - field_evaluator.add_with_destination(params.get_input>("Stable ID"), dst_stable_ids); + field_evaluator.add(params.get_input>("Stable ID"), &stable_ids); field_evaluator.evaluate(); GVArray_Typed positions = src_component.attribute_get_for_read( @@ -111,10 +122,13 @@ static void add_instances_from_component(InstancesComponent &dst_component, /* Add this reference last, because it is the most likely one to be removed later on. */ const int empty_reference_handle = dst_component.add_reference(InstanceReference()); - threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { - for (const int i : range) { + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { + for (const int range_i : selection_range) { + const int64_t i = selection[range_i]; + dst_stable_ids[range_i] = (*stable_ids)[i]; + /* Compute base transform for every instances. */ - float4x4 &dst_transform = dst_transforms[i]; + float4x4 &dst_transform = dst_transforms[range_i]; dst_transform = float4x4::from_loc_eul_scale( positions[i], rotations->get(i), scales->get(i)); @@ -126,8 +140,8 @@ static void add_instances_from_component(InstancesComponent &dst_component, if (src_instances != nullptr) { const int src_instances_amount = src_instances->instances_amount(); const int original_index = indices->get(i); - /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 refers to - * the last element. */ + /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 + * refers to the last element. */ const int index = mod_i(original_index, std::max(src_instances_amount, 1)); if (index < src_instances_amount) { /* Get the reference to the source instance. */ @@ -145,7 +159,7 @@ static void add_instances_from_component(InstancesComponent &dst_component, dst_handle = full_instance_handle; } /* Set properties of new instance. */ - dst_handles[i] = dst_handle; + dst_handles[range_i] = dst_handle; } }); From 6c11b320c410255bbad358595af2044da016b9c5 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Wed, 13 Oct 2021 09:08:02 -0500 Subject: [PATCH 0753/1500] Geometry Nodes: Scale Instances Node Adds a node that can scale a geometry's instances. With "Local" turned on, the instance is scaled individually from the center point input, while when local space is turned off, it's more like the transform node, except it scales outward from the center point instead of only from the origin. Differential Revision: https://developer.blender.org/D12681 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_geo_scale_instances.cc | 95 +++++++++++++++++++ 7 files changed, 101 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 0bc4f6af997..dfe33199f28 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -683,6 +683,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeSeparateGeometry"), NodeItem("GeometryNodeSetPosition"), NodeItem("GeometryNodeRealizeInstances"), + NodeItem("GeometryNodeScaleInstances"), NodeItem("GeometryNodeTranslateInstances"), ]), GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 45001ba6fcb..4c1c49555b8 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1533,6 +1533,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_INPUT_MATERIAL_INDEX 1118 #define GEO_NODE_SET_MATERIAL_INDEX 1119 #define GEO_NODE_TRANSLATE_INSTANCES 1120 +#define GEO_NODE_SCALE_INSTANCES 1121 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 5dc8909a45f..283e28ed54f 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5810,6 +5810,7 @@ static void registerGeometryNodes() register_node_type_geo_raycast(); register_node_type_geo_realize_instances(); register_node_type_geo_sample_texture(); + register_node_type_geo_scale_instances(); register_node_type_geo_separate_components(); register_node_type_geo_separate_geometry(); register_node_type_geo_set_curve_handles(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index e4153b7381b..e543db92b9f 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -253,6 +253,7 @@ set(SRC geometry/nodes/node_geo_points_to_volume.cc geometry/nodes/node_geo_proximity.cc geometry/nodes/node_geo_realize_instances.cc + geometry/nodes/node_geo_scale_instances.cc geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_separate_geometry.cc geometry/nodes/node_geo_set_curve_handles.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 2da9466ae16..3b0816300f5 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -130,6 +130,7 @@ void register_node_type_geo_proximity(void); void register_node_type_geo_raycast(void); void register_node_type_geo_realize_instances(void); void register_node_type_geo_sample_texture(void); +void register_node_type_geo_scale_instances(void); void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_separate_components(void); void register_node_type_geo_separate_geometry(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index aca018b5fe9..010327a1ac1 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -381,6 +381,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", Poin DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "") DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") +DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc new file mode 100644 index 00000000000..33897ef354d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -0,0 +1,95 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_scale_instances_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Scale").subtype(PROP_XYZ).default_value({1, 1, 1}).supports_field(); + b.add_input("Center").subtype(PROP_TRANSLATION).supports_field(); + b.add_input("Local Space").default_value(true).supports_field(); + b.add_output("Geometry"); +}; + +static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +{ + GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_POINT}; + + fn::FieldEvaluator selection_evaluator{field_context, instances_component.instances_amount()}; + selection_evaluator.add(params.extract_input>("Selection")); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + fn::FieldEvaluator transforms_evaluator{field_context, &selection}; + transforms_evaluator.add(params.extract_input>("Scale")); + transforms_evaluator.add(params.extract_input>("Center")); + transforms_evaluator.add(params.extract_input>("Local Space")); + transforms_evaluator.evaluate(); + const VArray &scales = transforms_evaluator.get_evaluated(0); + const VArray &pivots = transforms_evaluator.get_evaluated(1); + const VArray &local_spaces = transforms_evaluator.get_evaluated(2); + + MutableSpan instance_transforms = instances_component.instance_transforms(); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i = selection[i_selection]; + const float3 pivot = pivots[i]; + float4x4 &instance_transform = instance_transforms[i]; + + if (local_spaces[i]) { + instance_transform *= float4x4::from_location(pivot); + rescale_m4(instance_transform.values, scales[i]); + instance_transform *= float4x4::from_location(-pivot); + } + else { + const float4x4 original_transform = instance_transform; + instance_transform = float4x4::from_location(pivot); + rescale_m4(instance_transform.values, scales[i]); + instance_transform *= float4x4::from_location(-pivot); + instance_transform *= original_transform; + } + } + }); +} + +static void geo_node_scale_instances_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + if (geometry_set.has_instances()) { + InstancesComponent &instances = geometry_set.get_component_for_write(); + scale_instances(params, instances); + } + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_scale_instances() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SCALE_INSTANCES, "Scale Instances", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_scale_instances_exec; + ntype.declare = blender::nodes::geo_node_scale_instances_declare; + nodeRegisterType(&ntype); +} From 92b7bf4856022bc27f21c5c60be717605d47930e Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Wed, 13 Oct 2021 16:12:53 +0200 Subject: [PATCH 0754/1500] Cleanup: Remove data duplication from SMAA lookup tables These 2 large tables, `areaTexBytes` and `searchTexBytes`, contributed ~176kb worth of duplicate data into the `blender` executable due to the header being used in multiple places. We were lucky that only 2 translation units had included this header so only 1 duplicate copy of each was wasted. Define the tables as `extern` to address this. Reviewed By: fclem Differential Revision: https://developer.blender.org/D12723 --- release/scripts/addons | 2 +- source/blender/draw/CMakeLists.txt | 1 + source/blender/draw/intern/smaa_textures.c | 15068 +++++++++++++++++++ source/blender/draw/intern/smaa_textures.h | 15036 +----------------- 4 files changed, 15072 insertions(+), 15035 deletions(-) create mode 100644 source/blender/draw/intern/smaa_textures.c diff --git a/release/scripts/addons b/release/scripts/addons index f86f25e6221..67f1fbca148 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit f86f25e62217264495d05f116ccb09d575fe9841 +Subproject commit 67f1fbca1482d9d9362a4001332e785c3fd5d230 diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index dd4aa1747e5..62dcf438471 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -108,6 +108,7 @@ set(SRC intern/draw_select_buffer.c intern/draw_texture_pool.cc intern/draw_shader.c + intern/smaa_textures.c intern/draw_view.c engines/basic/basic_engine.c engines/image/image_engine.c diff --git a/source/blender/draw/intern/smaa_textures.c b/source/blender/draw/intern/smaa_textures.c new file mode 100644 index 00000000000..b34a641d838 --- /dev/null +++ b/source/blender/draw/intern/smaa_textures.c @@ -0,0 +1,15068 @@ +/** + * Copyright (C) 2013 Jorge Jimenez + * Copyright (C) 2013 Jose I. Echevarria + * Copyright (C) 2013 Belen Masia + * Copyright (C) 2013 Fernando Navarro + * Copyright (C) 2013 Diego Gutierrez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to + * do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "smaa_textures.h" + +/* Don't re-wrap large data definitions. */ +/* clang-format off */ + +/** + * Stored in R8G8 format. Load it in the following format: + * - DX10: DXGI_FORMAT_R8G8_UNORM + */ +const unsigned char areaTexBytes[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x44, 0x7b, 0x41, 0x5d, + 0x42, 0x54, 0x41, 0x4f, 0x42, 0x4b, 0x42, 0x49, 0x42, 0x48, 0x41, 0x47, + 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, + 0x42, 0x43, 0x41, 0x43, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, + 0x04, 0x7f, 0x22, 0x3d, 0x2b, 0x3d, 0x30, 0x3d, 0x33, 0x3d, 0x35, 0x3d, + 0x37, 0x3d, 0x38, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, + 0x3c, 0x3d, 0x3c, 0x3d, 0x40, 0x7f, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x62, 0x20, 0x4d, 0x2a, 0x48, 0x30, 0x44, 0x33, + 0x44, 0x35, 0x44, 0x36, 0x43, 0x37, 0x42, 0x38, 0x43, 0x39, 0x42, 0x39, + 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x41, 0x3b, + 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x00, 0x5d, 0x0c, 0x49, + 0x16, 0x43, 0x1d, 0x41, 0x22, 0x40, 0x26, 0x3f, 0x29, 0x3f, 0x2c, 0x3f, + 0x2e, 0x3e, 0x2f, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, 0x33, 0x3e, + 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x00, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x6d, 0x0b, 0x57, 0x16, 0x4f, 0x1d, 0x4a, 0x22, 0x48, 0x26, 0x47, 0x29, + 0x46, 0x2b, 0x44, 0x2d, 0x44, 0x2f, 0x44, 0x30, 0x44, 0x31, 0x44, 0x32, + 0x43, 0x33, 0x43, 0x34, 0x43, 0x34, 0x42, 0x35, 0x43, 0x36, 0x42, 0x36, + 0x42, 0x37, 0x42, 0x37, 0x00, 0x68, 0x06, 0x54, 0x0d, 0x4b, 0x14, 0x47, + 0x19, 0x44, 0x1d, 0x43, 0x20, 0x41, 0x23, 0x41, 0x26, 0x40, 0x27, 0x40, + 0x29, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x2f, 0x3f, + 0x30, 0x3f, 0x30, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, + 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x72, 0x06, 0x5f, 0x0d, + 0x56, 0x14, 0x4f, 0x19, 0x4d, 0x1d, 0x4a, 0x20, 0x49, 0x23, 0x47, 0x25, + 0x46, 0x27, 0x46, 0x29, 0x45, 0x2a, 0x45, 0x2c, 0x44, 0x2d, 0x44, 0x2e, + 0x44, 0x2f, 0x43, 0x30, 0x44, 0x30, 0x44, 0x31, 0x43, 0x32, 0x43, 0x33, + 0x00, 0x6d, 0x04, 0x5b, 0x09, 0x51, 0x0e, 0x4c, 0x13, 0x48, 0x17, 0x46, + 0x1a, 0x44, 0x1d, 0x43, 0x1f, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, + 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, + 0x2d, 0x3f, 0x2e, 0x3f, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, + 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x75, 0x03, 0x64, 0x09, 0x5a, 0x0e, 0x54, 0x13, + 0x51, 0x17, 0x4e, 0x1a, 0x4c, 0x1d, 0x49, 0x1f, 0x49, 0x22, 0x48, 0x23, + 0x47, 0x25, 0x46, 0x26, 0x46, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, + 0x44, 0x2c, 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2e, 0x00, 0x70, 0x02, 0x60, + 0x07, 0x56, 0x0b, 0x50, 0x0f, 0x4c, 0x12, 0x49, 0x16, 0x47, 0x18, 0x46, + 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x43, 0x21, 0x42, 0x22, 0x42, 0x24, 0x41, + 0x25, 0x41, 0x26, 0x40, 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x77, 0x02, 0x68, 0x06, 0x5f, 0x0b, 0x58, 0x0f, 0x54, 0x12, 0x51, 0x15, + 0x4f, 0x18, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1f, 0x49, 0x21, 0x48, 0x22, + 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x45, 0x27, 0x45, 0x28, 0x45, 0x29, + 0x45, 0x2a, 0x44, 0x2b, 0x00, 0x72, 0x02, 0x64, 0x05, 0x5b, 0x08, 0x54, + 0x0c, 0x50, 0x0f, 0x4d, 0x12, 0x4a, 0x14, 0x48, 0x17, 0x47, 0x19, 0x46, + 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x44, 0x20, 0x42, 0x21, 0x42, 0x22, 0x42, + 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x40, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, + 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x78, 0x02, 0x6b, 0x05, + 0x62, 0x08, 0x5b, 0x0c, 0x57, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4e, 0x17, + 0x4d, 0x19, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, 0x49, 0x20, 0x48, 0x21, + 0x48, 0x22, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26, 0x46, 0x27, 0x45, 0x27, + 0x00, 0x74, 0x01, 0x66, 0x04, 0x5e, 0x07, 0x57, 0x0a, 0x53, 0x0d, 0x4f, + 0x10, 0x4d, 0x12, 0x4b, 0x14, 0x49, 0x16, 0x48, 0x18, 0x46, 0x1a, 0x45, + 0x1b, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x20, 0x43, 0x21, 0x42, 0x22, 0x42, + 0x23, 0x42, 0x24, 0x41, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, + 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, + 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x79, 0x01, 0x6d, 0x04, 0x65, 0x07, 0x5e, 0x0a, + 0x5a, 0x0d, 0x56, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4f, 0x16, 0x4e, 0x18, + 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, 0x49, 0x1f, 0x48, 0x21, + 0x48, 0x22, 0x48, 0x23, 0x47, 0x23, 0x46, 0x25, 0x00, 0x75, 0x01, 0x69, + 0x03, 0x61, 0x06, 0x5a, 0x08, 0x56, 0x0b, 0x52, 0x0d, 0x4f, 0x10, 0x4d, + 0x12, 0x4b, 0x14, 0x4a, 0x16, 0x48, 0x17, 0x47, 0x19, 0x46, 0x1a, 0x45, + 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x20, 0x43, 0x21, 0x42, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, + 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, + 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7a, 0x01, 0x6f, 0x03, 0x67, 0x06, 0x60, 0x08, 0x5d, 0x0b, 0x59, 0x0d, + 0x56, 0x10, 0x53, 0x11, 0x51, 0x14, 0x50, 0x15, 0x4e, 0x17, 0x4d, 0x19, + 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x48, 0x1e, 0x49, 0x1f, 0x49, 0x20, + 0x48, 0x21, 0x48, 0x22, 0x00, 0x76, 0x01, 0x6b, 0x03, 0x63, 0x05, 0x5d, + 0x07, 0x58, 0x09, 0x54, 0x0c, 0x52, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, + 0x13, 0x4a, 0x15, 0x49, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x45, + 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, + 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, + 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x01, 0x71, 0x02, + 0x69, 0x05, 0x63, 0x07, 0x5f, 0x09, 0x5b, 0x0c, 0x58, 0x0e, 0x55, 0x10, + 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4e, 0x16, 0x4e, 0x18, 0x4c, 0x19, + 0x4c, 0x1a, 0x4a, 0x1c, 0x4b, 0x1d, 0x49, 0x1e, 0x49, 0x1f, 0x49, 0x20, + 0x00, 0x77, 0x00, 0x6c, 0x02, 0x65, 0x04, 0x5f, 0x06, 0x5a, 0x08, 0x57, + 0x0a, 0x54, 0x0c, 0x51, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, + 0x15, 0x49, 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x1a, 0x46, 0x1b, 0x45, + 0x1c, 0x45, 0x1d, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, + 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, + 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x00, 0x72, 0x02, 0x6b, 0x04, 0x64, 0x06, + 0x61, 0x08, 0x5d, 0x0a, 0x5a, 0x0c, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, + 0x52, 0x13, 0x50, 0x15, 0x4f, 0x16, 0x4e, 0x17, 0x4d, 0x18, 0x4b, 0x1a, + 0x4b, 0x1a, 0x4b, 0x1c, 0x4b, 0x1d, 0x49, 0x1d, 0x00, 0x77, 0x00, 0x6e, + 0x02, 0x66, 0x04, 0x61, 0x05, 0x5c, 0x07, 0x59, 0x09, 0x56, 0x0b, 0x53, + 0x0d, 0x51, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, + 0x15, 0x48, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x46, 0x1b, 0x45, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, + 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7c, 0x00, 0x73, 0x02, 0x6c, 0x03, 0x66, 0x05, 0x63, 0x07, 0x5e, 0x09, + 0x5c, 0x0b, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, + 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x17, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, + 0x4b, 0x1a, 0x4b, 0x1c, 0x00, 0x77, 0x00, 0x6f, 0x02, 0x68, 0x03, 0x63, + 0x05, 0x5e, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x55, 0x0b, 0x53, 0x0d, 0x50, + 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x49, + 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x19, 0x46, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, + 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, + 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x74, 0x02, + 0x6e, 0x03, 0x68, 0x05, 0x64, 0x06, 0x60, 0x08, 0x5d, 0x0a, 0x5a, 0x0b, + 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x14, + 0x50, 0x15, 0x4d, 0x16, 0x4e, 0x18, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, + 0x00, 0x78, 0x00, 0x70, 0x01, 0x69, 0x03, 0x64, 0x04, 0x60, 0x06, 0x5c, + 0x07, 0x59, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x52, 0x0d, 0x50, 0x0f, 0x4f, + 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x48, + 0x17, 0x48, 0x18, 0x47, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, + 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, + 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, + 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x75, 0x01, 0x6f, 0x02, 0x69, 0x04, + 0x65, 0x06, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0b, 0x5a, 0x0c, 0x58, 0x0d, + 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x13, 0x4f, 0x15, + 0x4e, 0x15, 0x4e, 0x17, 0x4d, 0x18, 0x4c, 0x18, 0x00, 0x78, 0x00, 0x71, + 0x01, 0x6a, 0x03, 0x66, 0x04, 0x61, 0x05, 0x5d, 0x06, 0x5a, 0x08, 0x58, + 0x09, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0e, 0x50, 0x0f, 0x4e, 0x10, 0x4e, + 0x11, 0x4c, 0x13, 0x4c, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x49, 0x16, 0x48, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, + 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, + 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, + 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, + 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7d, 0x00, 0x76, 0x01, 0x70, 0x02, 0x6a, 0x03, 0x67, 0x05, 0x63, 0x06, + 0x60, 0x08, 0x5d, 0x09, 0x5b, 0x0b, 0x59, 0x0c, 0x57, 0x0d, 0x55, 0x0e, + 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x4f, 0x13, 0x50, 0x15, 0x4f, 0x15, + 0x4e, 0x16, 0x4e, 0x18, 0x00, 0x79, 0x00, 0x71, 0x01, 0x6c, 0x02, 0x66, + 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5c, 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, + 0x0b, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, + 0x13, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x4a, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, + 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, + 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, + 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, + 0x71, 0x02, 0x6c, 0x03, 0x68, 0x05, 0x64, 0x06, 0x61, 0x07, 0x5e, 0x09, + 0x5c, 0x0a, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0e, 0x55, 0x0e, 0x55, 0x11, + 0x52, 0x11, 0x51, 0x12, 0x51, 0x13, 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x15, + 0x00, 0x79, 0x00, 0x72, 0x01, 0x6d, 0x02, 0x68, 0x03, 0x63, 0x04, 0x60, + 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x53, + 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, + 0x13, 0x4b, 0x14, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x1f, 0x00, 0x3f, + 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x58, 0x00, 0x70, 0x00, 0x77, + 0x00, 0x79, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x3f, + 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x7e, 0x00, 0x7e, 0x1f, 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, + 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x77, 0x01, 0x72, 0x02, 0x6d, 0x03, + 0x69, 0x04, 0x66, 0x06, 0x63, 0x07, 0x5f, 0x08, 0x5e, 0x09, 0x5c, 0x0b, + 0x5a, 0x0c, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x51, 0x11, + 0x52, 0x12, 0x51, 0x13, 0x50, 0x14, 0x50, 0x15, 0x00, 0x79, 0x00, 0x73, + 0x01, 0x6d, 0x02, 0x69, 0x03, 0x65, 0x04, 0x61, 0x05, 0x5e, 0x06, 0x5c, + 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0d, 0x53, 0x0d, 0x51, + 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x3f, 0x00, 0x5c, + 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x58, 0x00, 0x44, 0x00, 0x55, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x72, + 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, + 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, + 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, + 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, + 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7d, 0x00, 0x78, 0x01, 0x72, 0x02, 0x6e, 0x02, 0x6a, 0x03, 0x67, 0x05, + 0x64, 0x06, 0x60, 0x07, 0x5f, 0x09, 0x5c, 0x0a, 0x5b, 0x0b, 0x5a, 0x0c, + 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0f, 0x53, 0x11, 0x52, 0x11, 0x52, 0x12, + 0x51, 0x13, 0x50, 0x13, 0x00, 0x79, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x69, + 0x03, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, + 0x09, 0x56, 0x0b, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, + 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x12, 0x4c, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, + 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, + 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x70, 0x00, 0x55, + 0x00, 0x20, 0x00, 0x3e, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x6a, + 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, + 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, + 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, + 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x66, 0x00, 0x3f, 0x00, + 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, + 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, + 0x00, 0x79, 0x00, 0x7a, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x01, + 0x73, 0x02, 0x6e, 0x02, 0x6b, 0x03, 0x68, 0x05, 0x65, 0x06, 0x61, 0x07, + 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0d, + 0x56, 0x0e, 0x54, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, + 0x00, 0x79, 0x00, 0x74, 0x00, 0x6f, 0x02, 0x6a, 0x03, 0x66, 0x04, 0x63, + 0x05, 0x60, 0x05, 0x5e, 0x06, 0x5b, 0x07, 0x59, 0x09, 0x58, 0x09, 0x55, + 0x0b, 0x55, 0x0c, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, 0x0f, 0x4e, + 0x11, 0x4e, 0x11, 0x4d, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, + 0x00, 0x73, 0x00, 0x75, 0x00, 0x77, 0x00, 0x67, 0x00, 0x3e, 0x00, 0x0d, + 0x00, 0x28, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, + 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, + 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, + 0x00, 0x73, 0x00, 0x75, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x01, + 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, + 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x00, 0x74, 0x01, 0x6f, 0x02, + 0x6c, 0x03, 0x68, 0x04, 0x65, 0x05, 0x62, 0x06, 0x61, 0x07, 0x5f, 0x09, + 0x5c, 0x09, 0x5b, 0x0b, 0x5a, 0x0b, 0x58, 0x0c, 0x57, 0x0d, 0x55, 0x0e, + 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, 0x00, 0x79, 0x00, 0x74, + 0x00, 0x70, 0x01, 0x6b, 0x02, 0x67, 0x03, 0x64, 0x04, 0x61, 0x05, 0x5f, + 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, + 0x0c, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, + 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x00, 0x79, 0x00, 0x6e, 0x00, 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x1b, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, + 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, + 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x1b, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, + 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x21, 0x5d, 0x0c, 0x68, + 0x06, 0x6d, 0x04, 0x71, 0x02, 0x72, 0x02, 0x74, 0x01, 0x75, 0x01, 0x76, + 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, + 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, + 0x42, 0x40, 0x17, 0x55, 0x0c, 0x60, 0x08, 0x66, 0x05, 0x6a, 0x04, 0x6d, + 0x03, 0x6f, 0x02, 0x71, 0x02, 0x73, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, + 0x01, 0x76, 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, + 0x00, 0x78, 0x00, 0x78, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x21, 0x5d, 0x0c, 0x68, + 0x06, 0x6d, 0x04, 0x71, 0x02, 0x72, 0x02, 0x74, 0x01, 0x75, 0x01, 0x76, + 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, + 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, + 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x7b, 0x00, 0x72, + 0x00, 0x5a, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x16, 0x00, 0x28, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, + 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, + 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x7a, 0x00, 0x71, 0x00, + 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, + 0x00, 0x62, 0x00, 0x65, 0x2b, 0x49, 0x16, 0x53, 0x0d, 0x5b, 0x09, 0x60, + 0x07, 0x64, 0x05, 0x67, 0x04, 0x69, 0x03, 0x6b, 0x03, 0x6c, 0x02, 0x6e, + 0x02, 0x6f, 0x02, 0x70, 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, + 0x01, 0x73, 0x01, 0x74, 0x00, 0x74, 0x00, 0x75, 0x57, 0x16, 0x2d, 0x2c, + 0x1b, 0x3b, 0x12, 0x45, 0x0d, 0x4d, 0x0b, 0x53, 0x08, 0x57, 0x07, 0x5b, + 0x05, 0x5e, 0x05, 0x61, 0x04, 0x63, 0x04, 0x65, 0x03, 0x67, 0x02, 0x68, + 0x02, 0x69, 0x02, 0x6b, 0x02, 0x6c, 0x01, 0x6d, 0x01, 0x6e, 0x01, 0x6f, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x2b, 0x49, 0x16, 0x53, 0x0d, 0x5b, 0x09, 0x60, + 0x07, 0x64, 0x05, 0x67, 0x04, 0x69, 0x03, 0x6b, 0x03, 0x6c, 0x02, 0x6e, + 0x02, 0x6f, 0x02, 0x70, 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, + 0x01, 0x73, 0x01, 0x74, 0x00, 0x74, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, + 0x00, 0x57, 0x00, 0x5b, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, + 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, + 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, + 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, + 0x00, 0x57, 0x00, 0x5b, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, + 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, + 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, + 0x31, 0x43, 0x1d, 0x4b, 0x14, 0x51, 0x0e, 0x56, 0x0b, 0x5b, 0x08, 0x5e, + 0x07, 0x61, 0x06, 0x63, 0x05, 0x65, 0x04, 0x67, 0x04, 0x68, 0x03, 0x69, + 0x03, 0x6a, 0x03, 0x6c, 0x02, 0x6d, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x6f, + 0x02, 0x70, 0x01, 0x70, 0x61, 0x0c, 0x3b, 0x1b, 0x28, 0x28, 0x1d, 0x32, + 0x16, 0x3a, 0x11, 0x41, 0x0e, 0x47, 0x0c, 0x4b, 0x0a, 0x4f, 0x09, 0x53, + 0x07, 0x55, 0x06, 0x58, 0x05, 0x5a, 0x05, 0x5c, 0x05, 0x5e, 0x04, 0x60, + 0x04, 0x61, 0x04, 0x63, 0x03, 0x64, 0x02, 0x66, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x31, 0x43, 0x1d, 0x4b, 0x14, 0x51, 0x0e, 0x56, 0x0b, 0x5b, 0x08, 0x5e, + 0x07, 0x61, 0x06, 0x63, 0x05, 0x65, 0x04, 0x67, 0x04, 0x68, 0x03, 0x69, + 0x03, 0x6a, 0x03, 0x6c, 0x02, 0x6d, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x6f, + 0x02, 0x70, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, + 0x00, 0x13, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, + 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, + 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, + 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x33, 0x41, 0x22, 0x46, + 0x19, 0x4c, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5d, + 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x66, 0x04, 0x66, + 0x04, 0x68, 0x03, 0x69, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x02, 0x6c, + 0x67, 0x07, 0x45, 0x12, 0x32, 0x1d, 0x26, 0x26, 0x1e, 0x2e, 0x18, 0x34, + 0x14, 0x3a, 0x11, 0x3f, 0x0f, 0x44, 0x0c, 0x47, 0x0b, 0x4b, 0x0a, 0x4d, + 0x09, 0x51, 0x07, 0x52, 0x07, 0x55, 0x06, 0x57, 0x05, 0x58, 0x05, 0x5a, + 0x05, 0x5c, 0x04, 0x5d, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x33, 0x41, 0x22, 0x46, + 0x19, 0x4c, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5d, + 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x66, 0x04, 0x66, + 0x04, 0x68, 0x03, 0x69, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x02, 0x6c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, + 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x7d, 0x00, 0x79, + 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, + 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, + 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, + 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x7d, 0x00, 0x79, 0x00, + 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, + 0x00, 0x40, 0x00, 0x46, 0x35, 0x40, 0x27, 0x44, 0x1d, 0x48, 0x17, 0x4c, + 0x12, 0x50, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5e, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x65, + 0x04, 0x66, 0x04, 0x66, 0x04, 0x67, 0x03, 0x68, 0x6b, 0x05, 0x4d, 0x0d, + 0x3b, 0x16, 0x2e, 0x1e, 0x25, 0x25, 0x1f, 0x2b, 0x1a, 0x31, 0x16, 0x36, + 0x13, 0x3a, 0x10, 0x3e, 0x0f, 0x42, 0x0d, 0x45, 0x0c, 0x47, 0x0a, 0x4a, + 0x0a, 0x4c, 0x09, 0x4f, 0x07, 0x51, 0x07, 0x52, 0x07, 0x54, 0x06, 0x56, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x35, 0x40, 0x27, 0x44, 0x1d, 0x48, 0x17, 0x4c, + 0x12, 0x50, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5e, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x65, + 0x04, 0x66, 0x04, 0x66, 0x04, 0x67, 0x03, 0x68, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, + 0x00, 0x34, 0x00, 0x3b, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, + 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, + 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, + 0x00, 0x34, 0x00, 0x3b, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, + 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, + 0x37, 0x3f, 0x29, 0x43, 0x21, 0x46, 0x1a, 0x49, 0x16, 0x4d, 0x12, 0x50, + 0x10, 0x52, 0x0d, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5d, 0x06, 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, + 0x04, 0x64, 0x04, 0x65, 0x6e, 0x04, 0x53, 0x0a, 0x41, 0x11, 0x34, 0x18, + 0x2b, 0x1f, 0x24, 0x24, 0x1f, 0x29, 0x1b, 0x2e, 0x17, 0x33, 0x15, 0x37, + 0x12, 0x3a, 0x10, 0x3d, 0x0f, 0x40, 0x0d, 0x43, 0x0c, 0x45, 0x0c, 0x48, + 0x0a, 0x4a, 0x0a, 0x4c, 0x09, 0x4e, 0x07, 0x4f, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x37, 0x3f, 0x29, 0x43, 0x21, 0x46, 0x1a, 0x49, 0x16, 0x4d, 0x12, 0x50, + 0x10, 0x52, 0x0d, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5d, 0x06, 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, + 0x04, 0x64, 0x04, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, + 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, + 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, + 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x38, 0x3f, 0x2c, 0x41, + 0x23, 0x44, 0x1d, 0x47, 0x19, 0x4a, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x52, + 0x0e, 0x54, 0x0c, 0x56, 0x0b, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x61, 0x05, 0x61, 0x05, 0x62, + 0x70, 0x03, 0x58, 0x08, 0x47, 0x0e, 0x3a, 0x14, 0x31, 0x1a, 0x29, 0x1f, + 0x24, 0x24, 0x20, 0x28, 0x1c, 0x2d, 0x19, 0x31, 0x16, 0x34, 0x14, 0x37, + 0x12, 0x3a, 0x10, 0x3d, 0x0f, 0x3f, 0x0e, 0x42, 0x0c, 0x44, 0x0c, 0x46, + 0x0b, 0x47, 0x0a, 0x4a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x38, 0x3f, 0x2c, 0x41, + 0x23, 0x44, 0x1d, 0x47, 0x19, 0x4a, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x52, + 0x0e, 0x54, 0x0c, 0x56, 0x0b, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x61, 0x05, 0x61, 0x05, 0x62, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x7e, 0x00, 0x7c, + 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, + 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, + 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, + 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x7e, 0x00, 0x7c, 0x00, + 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, + 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, + 0x00, 0x1e, 0x00, 0x26, 0x39, 0x3f, 0x2e, 0x41, 0x26, 0x43, 0x20, 0x46, + 0x1b, 0x48, 0x17, 0x4b, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x51, 0x0e, 0x53, + 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x60, 0x71, 0x02, 0x5b, 0x07, + 0x4c, 0x0c, 0x3f, 0x11, 0x36, 0x16, 0x2e, 0x1b, 0x29, 0x20, 0x24, 0x23, + 0x20, 0x28, 0x1d, 0x2b, 0x19, 0x2f, 0x17, 0x32, 0x16, 0x35, 0x12, 0x37, + 0x12, 0x3a, 0x10, 0x3c, 0x0f, 0x3f, 0x0e, 0x41, 0x0c, 0x42, 0x0c, 0x45, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x39, 0x3f, 0x2e, 0x41, 0x26, 0x43, 0x20, 0x46, + 0x1b, 0x48, 0x17, 0x4b, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x51, 0x0e, 0x53, + 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x13, 0x00, 0x1b, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, + 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, + 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, + 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, + 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x13, 0x00, 0x1b, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, + 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, + 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, + 0x39, 0x3e, 0x2f, 0x40, 0x28, 0x42, 0x22, 0x45, 0x1d, 0x47, 0x19, 0x49, + 0x16, 0x4b, 0x14, 0x4d, 0x11, 0x4f, 0x10, 0x51, 0x0f, 0x53, 0x0d, 0x54, + 0x0c, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x07, 0x5c, 0x07, 0x5e, 0x73, 0x02, 0x5e, 0x05, 0x4f, 0x0a, 0x44, 0x0f, + 0x3a, 0x13, 0x33, 0x18, 0x2d, 0x1c, 0x27, 0x20, 0x23, 0x23, 0x20, 0x27, + 0x1d, 0x2a, 0x1a, 0x2d, 0x18, 0x30, 0x16, 0x33, 0x14, 0x35, 0x12, 0x38, + 0x12, 0x3a, 0x10, 0x3c, 0x0f, 0x3e, 0x0f, 0x41, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x39, 0x3e, 0x2f, 0x40, 0x28, 0x42, 0x22, 0x45, 0x1d, 0x47, 0x19, 0x49, + 0x16, 0x4b, 0x14, 0x4d, 0x11, 0x4f, 0x10, 0x51, 0x0f, 0x53, 0x0d, 0x54, + 0x0c, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x07, 0x5c, 0x07, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, + 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, + 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, + 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, + 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x3a, 0x3e, 0x31, 0x40, + 0x29, 0x42, 0x23, 0x44, 0x1f, 0x46, 0x1b, 0x48, 0x18, 0x4a, 0x15, 0x4c, + 0x13, 0x4d, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x55, + 0x0b, 0x56, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x74, 0x01, 0x62, 0x05, 0x53, 0x08, 0x47, 0x0c, 0x3e, 0x11, 0x37, 0x15, + 0x31, 0x19, 0x2b, 0x1d, 0x27, 0x20, 0x23, 0x23, 0x20, 0x26, 0x1d, 0x2a, + 0x1b, 0x2c, 0x19, 0x2f, 0x16, 0x31, 0x16, 0x34, 0x13, 0x35, 0x12, 0x38, + 0x12, 0x3b, 0x0f, 0x3b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3a, 0x3e, 0x31, 0x40, + 0x29, 0x42, 0x23, 0x44, 0x1f, 0x46, 0x1b, 0x48, 0x18, 0x4a, 0x15, 0x4c, + 0x13, 0x4d, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x55, + 0x0b, 0x56, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x7e, 0x00, 0x7d, + 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, + 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, + 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, 0x7e, 0x00, 0x7d, 0x00, + 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, + 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x3a, 0x3e, 0x32, 0x3f, 0x2b, 0x41, 0x25, 0x43, + 0x21, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, + 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, + 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x58, 0x09, 0x59, 0x75, 0x01, 0x64, 0x04, + 0x56, 0x07, 0x4b, 0x0b, 0x42, 0x0f, 0x3a, 0x12, 0x34, 0x16, 0x2f, 0x19, + 0x2a, 0x1d, 0x26, 0x20, 0x23, 0x23, 0x21, 0x26, 0x1d, 0x29, 0x1b, 0x2b, + 0x19, 0x2e, 0x18, 0x30, 0x16, 0x32, 0x15, 0x35, 0x12, 0x35, 0x12, 0x38, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x3a, 0x3e, 0x32, 0x3f, 0x2b, 0x41, 0x25, 0x43, + 0x21, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, + 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, + 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x58, 0x09, 0x59, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, + 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, + 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, + 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, + 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x3b, 0x3e, 0x33, 0x3f, 0x2c, 0x41, 0x27, 0x42, 0x22, 0x44, 0x1f, 0x45, + 0x1c, 0x47, 0x19, 0x49, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, + 0x10, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x55, + 0x0b, 0x57, 0x0a, 0x58, 0x76, 0x01, 0x66, 0x04, 0x58, 0x06, 0x4d, 0x0a, + 0x45, 0x0d, 0x3e, 0x10, 0x37, 0x14, 0x32, 0x17, 0x2d, 0x1a, 0x2a, 0x1d, + 0x26, 0x21, 0x22, 0x22, 0x21, 0x26, 0x1d, 0x28, 0x1c, 0x2b, 0x19, 0x2c, + 0x19, 0x30, 0x16, 0x30, 0x16, 0x33, 0x14, 0x35, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x3b, 0x3e, 0x33, 0x3f, 0x2c, 0x41, 0x27, 0x42, 0x22, 0x44, 0x1f, 0x45, + 0x1c, 0x47, 0x19, 0x49, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, + 0x10, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x55, + 0x0b, 0x57, 0x0a, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3e, 0x33, 0x3f, + 0x2d, 0x40, 0x28, 0x42, 0x24, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1a, 0x48, + 0x18, 0x49, 0x16, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x10, 0x50, + 0x0f, 0x51, 0x0f, 0x53, 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x55, 0x0b, 0x56, + 0x77, 0x01, 0x67, 0x03, 0x5a, 0x05, 0x51, 0x09, 0x48, 0x0c, 0x40, 0x0f, + 0x3a, 0x12, 0x35, 0x16, 0x30, 0x18, 0x2c, 0x1b, 0x29, 0x1d, 0x26, 0x21, + 0x22, 0x22, 0x21, 0x26, 0x1d, 0x27, 0x1d, 0x2b, 0x1a, 0x2b, 0x19, 0x2e, + 0x17, 0x30, 0x16, 0x31, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x33, 0x3f, + 0x2d, 0x40, 0x28, 0x42, 0x24, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1a, 0x48, + 0x18, 0x49, 0x16, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x10, 0x50, + 0x0f, 0x51, 0x0f, 0x53, 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x55, 0x0b, 0x56, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3e, 0x34, 0x3f, 0x2e, 0x40, 0x29, 0x41, + 0x25, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x47, 0x19, 0x48, 0x18, 0x4a, + 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, + 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x77, 0x01, 0x68, 0x02, + 0x5d, 0x05, 0x52, 0x07, 0x4a, 0x0a, 0x43, 0x0d, 0x3d, 0x10, 0x38, 0x12, + 0x33, 0x16, 0x2f, 0x19, 0x2b, 0x1b, 0x28, 0x1d, 0x26, 0x21, 0x22, 0x22, + 0x21, 0x26, 0x1e, 0x26, 0x1d, 0x2a, 0x1a, 0x2b, 0x19, 0x2d, 0x18, 0x30, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x34, 0x3f, 0x2e, 0x40, 0x29, 0x41, + 0x25, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x47, 0x19, 0x48, 0x18, 0x4a, + 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, + 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x35, 0x3f, 0x2f, 0x40, 0x2a, 0x41, 0x26, 0x42, 0x22, 0x44, + 0x20, 0x45, 0x1d, 0x46, 0x1b, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4b, + 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x52, + 0x0d, 0x53, 0x0d, 0x53, 0x77, 0x00, 0x6a, 0x02, 0x5e, 0x04, 0x55, 0x07, + 0x4c, 0x0a, 0x45, 0x0c, 0x40, 0x0f, 0x3a, 0x12, 0x35, 0x14, 0x31, 0x16, + 0x2e, 0x19, 0x2b, 0x1c, 0x27, 0x1d, 0x26, 0x22, 0x21, 0x22, 0x21, 0x25, + 0x1e, 0x26, 0x1d, 0x29, 0x1b, 0x2b, 0x19, 0x2b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x3c, 0x3e, 0x35, 0x3f, 0x2f, 0x40, 0x2a, 0x41, 0x26, 0x42, 0x22, 0x44, + 0x20, 0x45, 0x1d, 0x46, 0x1b, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4b, + 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x52, + 0x0d, 0x53, 0x0d, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x35, 0x3f, + 0x30, 0x3f, 0x2b, 0x40, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1e, 0x45, + 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4c, 0x13, 0x4c, + 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x0e, 0x53, + 0x78, 0x00, 0x6b, 0x02, 0x60, 0x04, 0x57, 0x06, 0x4f, 0x09, 0x48, 0x0c, + 0x42, 0x0e, 0x3c, 0x10, 0x38, 0x12, 0x34, 0x16, 0x30, 0x18, 0x2d, 0x19, + 0x2b, 0x1d, 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, + 0x1d, 0x28, 0x1c, 0x2b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x35, 0x3f, + 0x30, 0x3f, 0x2b, 0x40, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1e, 0x45, + 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4c, 0x13, 0x4c, + 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x0e, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x36, 0x3f, 0x30, 0x3f, 0x2c, 0x40, + 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, + 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, + 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x78, 0x00, 0x6d, 0x02, + 0x61, 0x04, 0x58, 0x05, 0x51, 0x07, 0x4a, 0x0a, 0x44, 0x0c, 0x3f, 0x0f, + 0x3a, 0x12, 0x35, 0x14, 0x32, 0x16, 0x30, 0x19, 0x2b, 0x19, 0x2a, 0x1d, + 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, 0x1d, 0x27, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x36, 0x3f, 0x30, 0x3f, 0x2c, 0x40, + 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, + 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, + 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x37, 0x3e, 0x31, 0x3f, 0x2d, 0x40, 0x29, 0x41, 0x26, 0x42, + 0x23, 0x44, 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x48, + 0x17, 0x4a, 0x15, 0x4a, 0x14, 0x4c, 0x13, 0x4c, 0x12, 0x4e, 0x11, 0x4e, + 0x11, 0x50, 0x0f, 0x50, 0x78, 0x00, 0x6d, 0x02, 0x63, 0x04, 0x5a, 0x05, + 0x53, 0x07, 0x4c, 0x0a, 0x46, 0x0c, 0x41, 0x0f, 0x3c, 0x0f, 0x38, 0x12, + 0x35, 0x16, 0x30, 0x16, 0x2e, 0x19, 0x2b, 0x1a, 0x29, 0x1d, 0x26, 0x1e, + 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1f, 0x26, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x3c, 0x3e, 0x37, 0x3e, 0x31, 0x3f, 0x2d, 0x40, 0x29, 0x41, 0x26, 0x42, + 0x23, 0x44, 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x48, + 0x17, 0x4a, 0x15, 0x4a, 0x14, 0x4c, 0x13, 0x4c, 0x12, 0x4e, 0x11, 0x4e, + 0x11, 0x50, 0x0f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3e, + 0x32, 0x3f, 0x2e, 0x40, 0x2a, 0x41, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, + 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, + 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, + 0x78, 0x00, 0x6e, 0x01, 0x65, 0x03, 0x5c, 0x05, 0x54, 0x07, 0x4e, 0x09, + 0x48, 0x0b, 0x43, 0x0c, 0x3e, 0x0f, 0x3b, 0x12, 0x36, 0x12, 0x33, 0x16, + 0x30, 0x17, 0x2d, 0x19, 0x2b, 0x1b, 0x28, 0x1d, 0x26, 0x1e, 0x25, 0x22, + 0x21, 0x22, 0x21, 0x24, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, + 0x32, 0x3f, 0x2e, 0x40, 0x2a, 0x41, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, + 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, + 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3e, 0x33, 0x3f, 0x2e, 0x3f, + 0x2b, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1e, 0x45, + 0x1c, 0x46, 0x1b, 0x48, 0x19, 0x48, 0x18, 0x4a, 0x16, 0x4a, 0x15, 0x4b, + 0x13, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x79, 0x00, 0x6f, 0x01, + 0x66, 0x02, 0x5d, 0x04, 0x56, 0x06, 0x4f, 0x07, 0x4a, 0x0a, 0x45, 0x0c, + 0x41, 0x0f, 0x3b, 0x0f, 0x38, 0x12, 0x35, 0x14, 0x31, 0x16, 0x30, 0x18, + 0x2b, 0x19, 0x2b, 0x1c, 0x27, 0x1d, 0x26, 0x1f, 0x24, 0x22, 0x21, 0x22, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, 0x33, 0x3f, 0x2e, 0x3f, + 0x2b, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1e, 0x45, + 0x1c, 0x46, 0x1b, 0x48, 0x19, 0x48, 0x18, 0x4a, 0x16, 0x4a, 0x15, 0x4b, + 0x13, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x74, 0x00, 0x77, 0x00, 0x79, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x08, 0x00, 0x44, 0x00, + 0x56, 0x00, 0x61, 0x00, 0x67, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x70, 0x00, + 0x71, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x76, 0x00, + 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, + 0x44, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x71, 0x00, 0x75, 0x00, 0x77, 0x00, + 0x79, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7c, 0x00, + 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0b, 0x57, 0x06, + 0x5f, 0x03, 0x63, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6d, 0x01, 0x6e, 0x01, + 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, + 0x76, 0x00, 0x76, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, 0x00, + 0x82, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x40, 0x17, 0x17, 0x2c, 0x0c, 0x3b, 0x07, + 0x45, 0x05, 0x4d, 0x04, 0x53, 0x03, 0x57, 0x02, 0x5c, 0x02, 0x5e, 0x01, + 0x61, 0x01, 0x63, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x00, 0x69, 0x00, + 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x41, 0x20, 0x4d, 0x0b, + 0x58, 0x06, 0x5e, 0x03, 0x64, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6c, 0x01, + 0x6f, 0x01, 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, + 0x76, 0x00, 0x75, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x15, 0x4f, 0x0d, 0x56, 0x09, 0x59, 0x06, + 0x5f, 0x05, 0x62, 0x04, 0x65, 0x03, 0x67, 0x02, 0x69, 0x02, 0x6b, 0x02, + 0x6c, 0x02, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x70, 0x01, + 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x74, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x55, 0x0c, 0x2c, 0x1b, 0x1b, 0x27, 0x12, 0x32, 0x0d, 0x3a, 0x0b, + 0x41, 0x08, 0x47, 0x07, 0x4c, 0x05, 0x4f, 0x05, 0x53, 0x04, 0x56, 0x04, + 0x58, 0x03, 0x5a, 0x02, 0x5d, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x61, 0x01, + 0x63, 0x01, 0x64, 0x01, 0x41, 0x2a, 0x47, 0x16, 0x4f, 0x0d, 0x54, 0x09, + 0x5b, 0x06, 0x5f, 0x05, 0x62, 0x04, 0x64, 0x03, 0x67, 0x02, 0x69, 0x02, + 0x6b, 0x02, 0x6c, 0x02, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x70, 0x01, + 0x71, 0x01, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x1d, 0x4a, 0x14, 0x50, 0x0e, 0x54, 0x0b, 0x59, 0x08, 0x5c, 0x07, + 0x5f, 0x06, 0x60, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, 0x68, 0x02, + 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6c, 0x02, 0x6e, 0x02, 0x6f, 0x01, + 0x70, 0x01, 0x70, 0x01, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x60, 0x08, 0x3b, + 0x12, 0x28, 0x1d, 0x1d, 0x26, 0x16, 0x2e, 0x11, 0x34, 0x0e, 0x3a, 0x0c, + 0x3f, 0x0a, 0x44, 0x08, 0x47, 0x07, 0x4b, 0x06, 0x4d, 0x05, 0x51, 0x05, + 0x52, 0x04, 0x55, 0x04, 0x57, 0x04, 0x58, 0x04, 0x5a, 0x03, 0x5c, 0x02, + 0x42, 0x30, 0x45, 0x1d, 0x4b, 0x14, 0x4f, 0x0e, 0x55, 0x0b, 0x59, 0x08, + 0x5c, 0x07, 0x5e, 0x06, 0x61, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, + 0x68, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6b, 0x02, 0x6d, 0x02, 0x6e, 0x02, + 0x6f, 0x01, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x22, 0x48, 0x19, + 0x4d, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5c, 0x07, + 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, + 0x68, 0x03, 0x68, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6d, 0x02, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x66, 0x05, 0x45, 0x0d, 0x32, 0x16, 0x26, + 0x1e, 0x1e, 0x25, 0x18, 0x2b, 0x14, 0x31, 0x11, 0x36, 0x0f, 0x3a, 0x0c, + 0x3e, 0x0b, 0x42, 0x0a, 0x45, 0x09, 0x47, 0x07, 0x4a, 0x07, 0x4c, 0x06, + 0x4f, 0x05, 0x51, 0x05, 0x52, 0x05, 0x54, 0x04, 0x41, 0x33, 0x44, 0x22, + 0x48, 0x19, 0x4c, 0x13, 0x51, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x08, + 0x5d, 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, + 0x67, 0x03, 0x67, 0x03, 0x69, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x43, 0x26, 0x47, 0x1d, 0x4a, 0x17, 0x4d, 0x12, + 0x51, 0x0f, 0x54, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5b, 0x08, 0x5d, 0x07, + 0x5f, 0x06, 0x60, 0x06, 0x62, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, + 0x67, 0x03, 0x67, 0x03, 0x68, 0x03, 0x69, 0x02, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x6a, 0x04, 0x4d, 0x0b, 0x3a, 0x11, 0x2e, 0x18, 0x25, 0x1f, 0x1f, + 0x24, 0x1a, 0x29, 0x16, 0x2e, 0x13, 0x33, 0x11, 0x37, 0x0f, 0x3a, 0x0d, + 0x3d, 0x0c, 0x40, 0x0a, 0x43, 0x0a, 0x45, 0x09, 0x48, 0x07, 0x4a, 0x07, + 0x4c, 0x07, 0x4e, 0x06, 0x41, 0x35, 0x43, 0x26, 0x47, 0x1d, 0x49, 0x17, + 0x4e, 0x12, 0x51, 0x0f, 0x54, 0x0d, 0x55, 0x0b, 0x59, 0x09, 0x5b, 0x08, + 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x62, 0x05, 0x63, 0x05, 0x63, 0x04, + 0x65, 0x03, 0x67, 0x03, 0x67, 0x03, 0x68, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x29, 0x45, 0x20, 0x49, 0x1a, 0x4b, 0x15, 0x4f, 0x12, 0x51, 0x0f, + 0x54, 0x0d, 0x55, 0x0b, 0x58, 0x0a, 0x5a, 0x09, 0x5b, 0x08, 0x5d, 0x07, + 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x64, 0x05, 0x65, 0x04, + 0x65, 0x03, 0x66, 0x03, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x6d, 0x03, 0x52, + 0x08, 0x41, 0x0e, 0x34, 0x14, 0x2b, 0x1a, 0x24, 0x1f, 0x1f, 0x24, 0x1b, + 0x28, 0x17, 0x2d, 0x15, 0x31, 0x12, 0x34, 0x10, 0x37, 0x0f, 0x3a, 0x0d, + 0x3d, 0x0c, 0x40, 0x0b, 0x42, 0x0a, 0x44, 0x0a, 0x46, 0x09, 0x47, 0x07, + 0x41, 0x36, 0x43, 0x29, 0x46, 0x20, 0x48, 0x1a, 0x4c, 0x15, 0x4f, 0x12, + 0x51, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x0a, 0x5a, 0x09, 0x5b, 0x08, + 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x60, 0x05, 0x62, 0x05, 0x64, 0x05, + 0x65, 0x04, 0x65, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2b, 0x45, 0x23, + 0x48, 0x1d, 0x49, 0x18, 0x4d, 0x14, 0x4f, 0x12, 0x51, 0x10, 0x53, 0x0e, + 0x55, 0x0c, 0x57, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5c, 0x08, 0x5d, 0x07, + 0x5f, 0x07, 0x5f, 0x06, 0x61, 0x06, 0x62, 0x05, 0x63, 0x05, 0x64, 0x05, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x6f, 0x02, 0x57, 0x07, 0x47, 0x0c, 0x3a, + 0x11, 0x31, 0x16, 0x29, 0x1b, 0x24, 0x20, 0x20, 0x23, 0x1c, 0x28, 0x19, + 0x2b, 0x16, 0x2f, 0x14, 0x32, 0x12, 0x35, 0x10, 0x38, 0x0f, 0x3a, 0x0e, + 0x3c, 0x0c, 0x3f, 0x0c, 0x41, 0x0b, 0x42, 0x0a, 0x41, 0x37, 0x43, 0x2b, + 0x45, 0x23, 0x47, 0x1d, 0x4a, 0x18, 0x4d, 0x14, 0x4f, 0x12, 0x51, 0x10, + 0x53, 0x0e, 0x55, 0x0c, 0x57, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5c, 0x08, + 0x5d, 0x07, 0x5e, 0x07, 0x60, 0x06, 0x61, 0x06, 0x62, 0x05, 0x63, 0x05, + 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, + 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x1f, 0x1f, 0x3f, 0x00, + 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x58, 0x00, 0x70, 0x00, 0x77, 0x00, 0x79, 0x00, 0x7b, 0x00, + 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x1f, 0x1f, 0x3f, 0x00, + 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x43, 0x2d, 0x44, 0x25, 0x46, 0x1f, 0x48, 0x1b, + 0x4b, 0x17, 0x4d, 0x14, 0x50, 0x12, 0x51, 0x10, 0x53, 0x0e, 0x55, 0x0c, + 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x08, 0x5c, 0x07, + 0x5f, 0x07, 0x60, 0x06, 0x61, 0x06, 0x62, 0x06, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x71, 0x02, 0x5b, 0x05, 0x4b, 0x0a, 0x3f, 0x0f, 0x36, 0x13, 0x2e, + 0x18, 0x29, 0x1c, 0x23, 0x20, 0x20, 0x23, 0x1d, 0x27, 0x19, 0x2a, 0x17, + 0x2d, 0x16, 0x30, 0x12, 0x33, 0x12, 0x35, 0x10, 0x38, 0x0f, 0x3b, 0x0e, + 0x3c, 0x0c, 0x3e, 0x0c, 0x41, 0x38, 0x43, 0x2d, 0x44, 0x25, 0x45, 0x1f, + 0x49, 0x1b, 0x4b, 0x17, 0x4d, 0x14, 0x4f, 0x11, 0x51, 0x10, 0x53, 0x0e, + 0x55, 0x0c, 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5b, 0x08, + 0x5e, 0x07, 0x5f, 0x07, 0x60, 0x06, 0x61, 0x06, 0x00, 0x00, 0x0a, 0x00, + 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, + 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x44, 0x00, + 0x55, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x75, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, + 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x42, 0x2f, 0x44, 0x27, 0x46, 0x22, 0x47, 0x1d, 0x4a, 0x19, 0x4c, 0x16, + 0x4e, 0x13, 0x4f, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, + 0x58, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x07, 0x5e, 0x07, + 0x5f, 0x07, 0x5f, 0x06, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x73, 0x01, 0x5e, + 0x05, 0x4f, 0x08, 0x44, 0x0c, 0x3a, 0x10, 0x33, 0x15, 0x2d, 0x19, 0x28, + 0x1d, 0x23, 0x20, 0x20, 0x23, 0x1d, 0x26, 0x1a, 0x2a, 0x18, 0x2c, 0x16, + 0x2f, 0x14, 0x31, 0x12, 0x34, 0x12, 0x35, 0x0f, 0x38, 0x0f, 0x3b, 0x0f, + 0x41, 0x39, 0x42, 0x2f, 0x44, 0x27, 0x45, 0x22, 0x48, 0x1d, 0x4a, 0x19, + 0x4c, 0x16, 0x4d, 0x14, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, + 0x56, 0x0c, 0x58, 0x0b, 0x59, 0x0a, 0x59, 0x09, 0x5c, 0x09, 0x5c, 0x07, + 0x5e, 0x07, 0x5f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x2d, 0x00, + 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, + 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, + 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, + 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x55, 0x00, 0x20, 0x00, 0x3e, 0x00, + 0x50, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, + 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, + 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, + 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x42, 0x30, 0x44, 0x29, + 0x45, 0x23, 0x46, 0x1f, 0x49, 0x1b, 0x4b, 0x18, 0x4c, 0x15, 0x4d, 0x13, + 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0b, + 0x59, 0x0b, 0x59, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x5c, 0x07, 0x5e, 0x07, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x73, 0x01, 0x61, 0x04, 0x52, 0x07, 0x47, + 0x0b, 0x3e, 0x0f, 0x37, 0x12, 0x31, 0x16, 0x2b, 0x19, 0x27, 0x1d, 0x23, + 0x20, 0x20, 0x23, 0x1d, 0x26, 0x1b, 0x29, 0x19, 0x2b, 0x16, 0x2e, 0x16, + 0x30, 0x13, 0x32, 0x12, 0x35, 0x12, 0x36, 0x0f, 0x41, 0x39, 0x42, 0x30, + 0x44, 0x29, 0x44, 0x23, 0x47, 0x1f, 0x49, 0x1b, 0x4b, 0x18, 0x4c, 0x15, + 0x4e, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, + 0x57, 0x0b, 0x57, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x5c, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, + 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, + 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x72, 0x00, 0x5c, + 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, + 0x73, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x67, 0x00, 0x3e, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x3a, 0x00, + 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, + 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x72, 0x00, 0x5c, + 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, + 0x73, 0x00, 0x75, 0x00, 0x42, 0x32, 0x44, 0x2a, 0x45, 0x25, 0x46, 0x21, + 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x17, 0x4c, 0x15, 0x4e, 0x13, 0x50, 0x11, + 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x55, 0x0c, 0x57, 0x0c, 0x57, 0x0b, + 0x5a, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x74, 0x01, 0x64, 0x04, 0x55, 0x06, 0x4a, 0x0a, 0x42, 0x0d, 0x3a, + 0x10, 0x34, 0x14, 0x2f, 0x17, 0x2a, 0x1a, 0x26, 0x1d, 0x23, 0x21, 0x21, + 0x22, 0x1d, 0x26, 0x1b, 0x28, 0x19, 0x2b, 0x18, 0x2c, 0x16, 0x30, 0x15, + 0x30, 0x12, 0x33, 0x12, 0x41, 0x3a, 0x42, 0x32, 0x44, 0x2a, 0x44, 0x25, + 0x46, 0x21, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x17, 0x4d, 0x15, 0x4e, 0x13, + 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x55, 0x0c, 0x56, 0x0c, + 0x58, 0x0b, 0x5a, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, + 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, + 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, + 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x6e, 0x00, + 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, + 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, + 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, + 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, + 0x42, 0x32, 0x43, 0x2c, 0x44, 0x26, 0x45, 0x22, 0x48, 0x1f, 0x49, 0x1b, + 0x4b, 0x19, 0x4c, 0x16, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, + 0x53, 0x0e, 0x55, 0x0e, 0x55, 0x0c, 0x56, 0x0c, 0x57, 0x0b, 0x59, 0x0b, + 0x5a, 0x0a, 0x5a, 0x09, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x75, 0x01, 0x65, + 0x03, 0x58, 0x05, 0x4d, 0x09, 0x45, 0x0c, 0x3e, 0x0f, 0x37, 0x12, 0x32, + 0x16, 0x2d, 0x18, 0x2a, 0x1b, 0x26, 0x1d, 0x22, 0x21, 0x21, 0x22, 0x1d, + 0x26, 0x1c, 0x27, 0x19, 0x2b, 0x19, 0x2b, 0x16, 0x2e, 0x16, 0x30, 0x14, + 0x41, 0x3a, 0x42, 0x32, 0x43, 0x2c, 0x44, 0x26, 0x46, 0x22, 0x48, 0x1f, + 0x49, 0x1b, 0x4a, 0x19, 0x4c, 0x16, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x11, + 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0e, 0x54, 0x0c, 0x57, 0x0c, 0x57, 0x0b, + 0x59, 0x0b, 0x5a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, + 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, + 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, + 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x72, 0x00, 0x5a, 0x00, 0x3a, 0x00, + 0x1b, 0x00, 0x01, 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, + 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, + 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, + 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x42, 0x33, 0x43, 0x2d, + 0x44, 0x28, 0x45, 0x23, 0x47, 0x20, 0x48, 0x1d, 0x4a, 0x1a, 0x4a, 0x18, + 0x4c, 0x16, 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, + 0x55, 0x0e, 0x54, 0x0d, 0x57, 0x0c, 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x0b, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x76, 0x01, 0x67, 0x02, 0x5a, 0x05, 0x50, + 0x07, 0x47, 0x0a, 0x40, 0x0d, 0x3a, 0x10, 0x35, 0x12, 0x30, 0x16, 0x2c, + 0x19, 0x29, 0x1b, 0x26, 0x1d, 0x22, 0x21, 0x21, 0x22, 0x1d, 0x26, 0x1d, + 0x26, 0x1a, 0x2a, 0x19, 0x2b, 0x17, 0x2d, 0x16, 0x41, 0x3b, 0x42, 0x33, + 0x43, 0x2d, 0x44, 0x28, 0x45, 0x23, 0x47, 0x20, 0x48, 0x1d, 0x49, 0x1a, + 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, + 0x53, 0x0e, 0x54, 0x0e, 0x55, 0x0d, 0x57, 0x0c, 0x57, 0x0b, 0x58, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, + 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x7c, 0x00, 0x75, + 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, + 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, + 0x57, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, + 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x7c, 0x00, 0x75, + 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, + 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, + 0x57, 0x00, 0x5b, 0x00, 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, 0x45, 0x25, + 0x46, 0x21, 0x48, 0x1e, 0x49, 0x1b, 0x4a, 0x19, 0x4c, 0x17, 0x4d, 0x15, + 0x4e, 0x14, 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x53, 0x0e, + 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, 0x57, 0x0b, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x77, 0x00, 0x68, 0x02, 0x5c, 0x05, 0x52, 0x07, 0x4a, 0x0a, 0x43, + 0x0c, 0x3d, 0x0f, 0x37, 0x12, 0x33, 0x14, 0x2f, 0x16, 0x2b, 0x19, 0x28, + 0x1c, 0x26, 0x1d, 0x22, 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1a, + 0x29, 0x19, 0x2b, 0x18, 0x41, 0x3b, 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, + 0x45, 0x25, 0x46, 0x21, 0x48, 0x1e, 0x48, 0x1b, 0x4b, 0x19, 0x4c, 0x17, + 0x4d, 0x15, 0x4e, 0x14, 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x51, 0x0f, + 0x54, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, + 0x4c, 0x00, 0x51, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, + 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, + 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, + 0x4c, 0x00, 0x51, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, + 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, + 0x42, 0x34, 0x43, 0x2f, 0x44, 0x2a, 0x44, 0x26, 0x46, 0x22, 0x47, 0x1f, + 0x48, 0x1d, 0x49, 0x1a, 0x4b, 0x18, 0x4c, 0x17, 0x4e, 0x15, 0x4e, 0x13, + 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x51, 0x0f, 0x54, 0x0e, 0x55, 0x0d, + 0x56, 0x0c, 0x57, 0x0c, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x77, 0x00, 0x69, + 0x02, 0x5e, 0x04, 0x55, 0x06, 0x4c, 0x09, 0x45, 0x0c, 0x3f, 0x0e, 0x3a, + 0x10, 0x35, 0x12, 0x31, 0x16, 0x2e, 0x18, 0x2b, 0x19, 0x27, 0x1d, 0x26, + 0x1e, 0x22, 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1b, 0x28, 0x19, + 0x41, 0x3b, 0x42, 0x34, 0x43, 0x2f, 0x43, 0x2a, 0x45, 0x26, 0x46, 0x22, + 0x47, 0x1f, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x18, 0x4c, 0x17, 0x4e, 0x15, + 0x4e, 0x13, 0x50, 0x13, 0x50, 0x11, 0x51, 0x11, 0x52, 0x0f, 0x54, 0x0e, + 0x55, 0x0d, 0x56, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, + 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, + 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, + 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, + 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, + 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x42, 0x35, 0x43, 0x30, + 0x44, 0x2b, 0x44, 0x27, 0x45, 0x24, 0x46, 0x21, 0x48, 0x1e, 0x48, 0x1c, + 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4e, 0x13, 0x50, 0x12, + 0x50, 0x11, 0x51, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, 0x55, 0x0c, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x77, 0x00, 0x6b, 0x02, 0x60, 0x04, 0x57, + 0x05, 0x4f, 0x07, 0x48, 0x0a, 0x42, 0x0c, 0x3c, 0x0f, 0x38, 0x12, 0x34, + 0x14, 0x30, 0x16, 0x2c, 0x19, 0x2b, 0x19, 0x26, 0x1d, 0x25, 0x1e, 0x22, + 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1c, 0x41, 0x3b, 0x42, 0x35, + 0x43, 0x30, 0x43, 0x2b, 0x44, 0x27, 0x45, 0x24, 0x46, 0x21, 0x47, 0x1e, + 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4e, 0x13, + 0x50, 0x12, 0x50, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, + 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x7d, 0x00, 0x7a, + 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, + 0x34, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, + 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, + 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x7d, 0x00, 0x7a, + 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, + 0x34, 0x00, 0x3b, 0x00, 0x42, 0x36, 0x43, 0x30, 0x44, 0x2c, 0x44, 0x28, + 0x45, 0x25, 0x46, 0x22, 0x48, 0x1f, 0x48, 0x1d, 0x49, 0x1a, 0x4b, 0x19, + 0x4c, 0x18, 0x4d, 0x15, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x12, 0x50, 0x11, + 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x77, 0x00, 0x6c, 0x02, 0x61, 0x04, 0x58, 0x05, 0x51, 0x07, 0x4a, + 0x0a, 0x44, 0x0c, 0x3f, 0x0f, 0x3a, 0x10, 0x35, 0x12, 0x32, 0x16, 0x30, + 0x16, 0x2b, 0x19, 0x2a, 0x1a, 0x26, 0x1d, 0x25, 0x1e, 0x22, 0x21, 0x22, + 0x21, 0x1e, 0x25, 0x1d, 0x41, 0x3b, 0x42, 0x36, 0x43, 0x30, 0x43, 0x2c, + 0x44, 0x28, 0x45, 0x25, 0x46, 0x22, 0x47, 0x1f, 0x49, 0x1d, 0x49, 0x1a, + 0x4b, 0x19, 0x4c, 0x18, 0x4d, 0x15, 0x4e, 0x15, 0x4f, 0x13, 0x4f, 0x12, + 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, + 0x29, 0x00, 0x30, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, + 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, + 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, + 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, + 0x29, 0x00, 0x30, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, + 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, + 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, + 0x42, 0x36, 0x42, 0x31, 0x43, 0x2d, 0x44, 0x29, 0x45, 0x26, 0x46, 0x23, + 0x47, 0x20, 0x47, 0x1e, 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x17, + 0x4d, 0x15, 0x4e, 0x14, 0x4f, 0x13, 0x4f, 0x12, 0x51, 0x11, 0x52, 0x11, + 0x52, 0x0f, 0x54, 0x0e, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x78, 0x00, 0x6d, + 0x01, 0x62, 0x03, 0x5a, 0x05, 0x52, 0x07, 0x4c, 0x09, 0x46, 0x0b, 0x41, + 0x0c, 0x3c, 0x0f, 0x38, 0x12, 0x35, 0x12, 0x30, 0x16, 0x2e, 0x17, 0x2b, + 0x19, 0x29, 0x1b, 0x26, 0x1d, 0x25, 0x1e, 0x22, 0x21, 0x22, 0x21, 0x1f, + 0x41, 0x3c, 0x42, 0x36, 0x42, 0x31, 0x43, 0x2d, 0x44, 0x29, 0x45, 0x26, + 0x46, 0x23, 0x46, 0x20, 0x48, 0x1e, 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, + 0x4c, 0x17, 0x4d, 0x15, 0x4e, 0x14, 0x4e, 0x13, 0x50, 0x12, 0x51, 0x11, + 0x52, 0x11, 0x52, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, + 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, + 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, + 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, + 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x42, 0x37, 0x42, 0x32, + 0x43, 0x2d, 0x44, 0x2a, 0x45, 0x27, 0x45, 0x23, 0x46, 0x21, 0x47, 0x1f, + 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, 0x4c, 0x16, 0x4e, 0x15, + 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x78, 0x00, 0x6e, 0x01, 0x64, 0x02, 0x5b, + 0x04, 0x54, 0x06, 0x4e, 0x07, 0x47, 0x0a, 0x42, 0x0c, 0x3e, 0x0f, 0x3b, + 0x0f, 0x35, 0x12, 0x33, 0x14, 0x30, 0x16, 0x2d, 0x19, 0x2b, 0x19, 0x28, + 0x1c, 0x26, 0x1d, 0x25, 0x1f, 0x22, 0x21, 0x22, 0x41, 0x3c, 0x42, 0x37, + 0x42, 0x32, 0x43, 0x2d, 0x44, 0x2a, 0x45, 0x27, 0x45, 0x23, 0x46, 0x21, + 0x48, 0x1f, 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, 0x4c, 0x16, + 0x4e, 0x15, 0x4d, 0x14, 0x50, 0x13, 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x7e, 0x00, 0x7c, + 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, + 0x13, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, + 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x7e, 0x00, 0x7c, + 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, + 0x13, 0x00, 0x1b, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x61, 0x20, 0x4d, 0x2a, + 0x48, 0x30, 0x44, 0x33, 0x44, 0x35, 0x44, 0x36, 0x43, 0x37, 0x42, 0x38, + 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3b, + 0x42, 0x3b, 0x41, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, + 0x04, 0x3d, 0x22, 0x3d, 0x2b, 0x3d, 0x30, 0x3d, 0x33, 0x3d, 0x35, 0x3d, + 0x37, 0x3d, 0x38, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, + 0x3c, 0x3d, 0x3c, 0x3d, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x12, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, + 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, + 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, + 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x12, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, + 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, + 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x6c, 0x0b, 0x57, 0x16, 0x4f, 0x1d, 0x4a, 0x22, + 0x48, 0x26, 0x47, 0x29, 0x46, 0x2b, 0x44, 0x2d, 0x44, 0x2f, 0x44, 0x30, + 0x44, 0x31, 0x44, 0x32, 0x43, 0x33, 0x43, 0x34, 0x43, 0x34, 0x42, 0x35, + 0x43, 0x36, 0x42, 0x36, 0x42, 0x37, 0x42, 0x37, 0x00, 0x5d, 0x0c, 0x49, + 0x16, 0x43, 0x1d, 0x41, 0x22, 0x40, 0x26, 0x3f, 0x29, 0x3f, 0x2c, 0x3f, + 0x2e, 0x3e, 0x2f, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, 0x33, 0x3e, + 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, + 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, + 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, + 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, + 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, + 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, + 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x72, 0x06, 0x5f, 0x0d, 0x56, 0x14, 0x4f, 0x19, 0x4d, 0x1d, 0x4a, 0x20, + 0x49, 0x23, 0x47, 0x25, 0x46, 0x27, 0x46, 0x29, 0x45, 0x2a, 0x45, 0x2c, + 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2f, 0x43, 0x30, 0x44, 0x30, 0x44, 0x31, + 0x43, 0x32, 0x43, 0x33, 0x00, 0x68, 0x06, 0x54, 0x0d, 0x4b, 0x14, 0x47, + 0x19, 0x44, 0x1d, 0x43, 0x20, 0x41, 0x23, 0x41, 0x26, 0x40, 0x27, 0x40, + 0x29, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x2f, 0x3f, + 0x30, 0x3f, 0x30, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, + 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, + 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, + 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, + 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, + 0x00, 0x08, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x75, 0x03, 0x64, 0x09, + 0x5a, 0x0e, 0x54, 0x13, 0x51, 0x17, 0x4e, 0x1a, 0x4c, 0x1d, 0x49, 0x1f, + 0x49, 0x22, 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x46, 0x28, 0x45, 0x29, + 0x45, 0x2a, 0x44, 0x2b, 0x44, 0x2c, 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2e, + 0x00, 0x6d, 0x04, 0x5b, 0x09, 0x51, 0x0e, 0x4c, 0x13, 0x48, 0x17, 0x46, + 0x1a, 0x44, 0x1d, 0x43, 0x1f, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, + 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, + 0x2d, 0x3f, 0x2e, 0x3f, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, + 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x3f, + 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x77, 0x02, 0x68, 0x06, 0x5f, 0x0b, 0x58, 0x0f, + 0x54, 0x12, 0x51, 0x15, 0x4f, 0x18, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1f, + 0x49, 0x21, 0x48, 0x22, 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x45, 0x27, + 0x45, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, 0x00, 0x70, 0x02, 0x60, + 0x07, 0x56, 0x0b, 0x50, 0x0f, 0x4c, 0x12, 0x49, 0x16, 0x47, 0x18, 0x46, + 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x43, 0x21, 0x42, 0x22, 0x42, 0x24, 0x41, + 0x25, 0x41, 0x26, 0x40, 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, + 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, + 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, + 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x78, 0x02, 0x6b, 0x05, 0x62, 0x08, 0x5b, 0x0c, 0x57, 0x0f, 0x54, 0x12, + 0x51, 0x14, 0x4e, 0x17, 0x4d, 0x19, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, + 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26, + 0x46, 0x27, 0x45, 0x27, 0x00, 0x72, 0x02, 0x64, 0x05, 0x5b, 0x08, 0x54, + 0x0c, 0x50, 0x0f, 0x4d, 0x12, 0x4a, 0x14, 0x48, 0x17, 0x47, 0x19, 0x46, + 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x44, 0x20, 0x42, 0x21, 0x42, 0x22, 0x42, + 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x40, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x3f, + 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, + 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, + 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, + 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x79, 0x01, 0x6d, 0x04, + 0x65, 0x07, 0x5e, 0x0a, 0x5a, 0x0d, 0x56, 0x0f, 0x54, 0x12, 0x51, 0x14, + 0x4f, 0x16, 0x4e, 0x18, 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, + 0x49, 0x1f, 0x48, 0x21, 0x48, 0x22, 0x48, 0x23, 0x47, 0x23, 0x46, 0x25, + 0x00, 0x74, 0x01, 0x66, 0x04, 0x5e, 0x07, 0x57, 0x0a, 0x53, 0x0d, 0x4f, + 0x10, 0x4d, 0x12, 0x4b, 0x14, 0x49, 0x16, 0x48, 0x18, 0x46, 0x1a, 0x45, + 0x1b, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x20, 0x43, 0x21, 0x42, 0x22, 0x42, + 0x23, 0x42, 0x24, 0x41, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x01, 0x01, + 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, + 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, + 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, + 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7a, 0x01, 0x6f, 0x03, 0x67, 0x06, 0x60, 0x08, + 0x5d, 0x0b, 0x59, 0x0d, 0x56, 0x10, 0x53, 0x11, 0x51, 0x14, 0x50, 0x15, + 0x4e, 0x17, 0x4d, 0x19, 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x48, 0x1e, + 0x49, 0x1f, 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, 0x00, 0x75, 0x01, 0x69, + 0x03, 0x61, 0x06, 0x5a, 0x08, 0x56, 0x0b, 0x52, 0x0d, 0x4f, 0x10, 0x4d, + 0x12, 0x4b, 0x14, 0x4a, 0x16, 0x48, 0x17, 0x47, 0x19, 0x46, 0x1a, 0x45, + 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x20, 0x43, 0x21, 0x42, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x1b, 0x00, + 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, + 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, + 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7b, 0x01, 0x71, 0x02, 0x69, 0x05, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0c, + 0x58, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4e, 0x16, + 0x4e, 0x18, 0x4c, 0x19, 0x4c, 0x1a, 0x4a, 0x1c, 0x4b, 0x1d, 0x49, 0x1e, + 0x49, 0x1f, 0x49, 0x20, 0x00, 0x76, 0x01, 0x6b, 0x03, 0x63, 0x05, 0x5d, + 0x07, 0x58, 0x09, 0x54, 0x0c, 0x52, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, + 0x13, 0x4a, 0x15, 0x49, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x45, + 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x71, + 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, + 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, + 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, + 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x00, 0x72, 0x02, + 0x6b, 0x04, 0x64, 0x06, 0x61, 0x08, 0x5d, 0x0a, 0x5a, 0x0c, 0x56, 0x0e, + 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4f, 0x16, 0x4e, 0x17, + 0x4d, 0x18, 0x4b, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, 0x4b, 0x1d, 0x49, 0x1d, + 0x00, 0x77, 0x00, 0x6c, 0x02, 0x65, 0x04, 0x5f, 0x06, 0x5a, 0x08, 0x57, + 0x0a, 0x54, 0x0c, 0x51, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, + 0x15, 0x49, 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x1a, 0x46, 0x1b, 0x45, + 0x1c, 0x45, 0x1d, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, + 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, + 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, + 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x73, 0x02, 0x6c, 0x03, 0x66, 0x05, + 0x63, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x10, + 0x53, 0x11, 0x52, 0x13, 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x17, 0x4c, 0x18, + 0x4c, 0x19, 0x4c, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, 0x00, 0x77, 0x00, 0x6e, + 0x02, 0x66, 0x04, 0x61, 0x05, 0x5c, 0x07, 0x59, 0x09, 0x56, 0x0b, 0x53, + 0x0d, 0x51, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, + 0x15, 0x48, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x46, 0x1b, 0x45, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, + 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, + 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, + 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7c, 0x00, 0x74, 0x02, 0x6e, 0x03, 0x68, 0x05, 0x64, 0x06, 0x60, 0x08, + 0x5d, 0x0a, 0x5a, 0x0b, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, + 0x52, 0x13, 0x50, 0x14, 0x50, 0x15, 0x4d, 0x16, 0x4e, 0x18, 0x4c, 0x18, + 0x4c, 0x19, 0x4c, 0x1a, 0x00, 0x77, 0x00, 0x6f, 0x02, 0x68, 0x03, 0x63, + 0x05, 0x5e, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x55, 0x0b, 0x53, 0x0d, 0x50, + 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x49, + 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x19, 0x46, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, + 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, + 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, + 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, + 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, + 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x75, 0x01, + 0x6f, 0x02, 0x69, 0x04, 0x65, 0x06, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0b, + 0x5a, 0x0c, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, + 0x50, 0x13, 0x4f, 0x15, 0x4e, 0x15, 0x4e, 0x17, 0x4d, 0x18, 0x4c, 0x18, + 0x00, 0x78, 0x00, 0x70, 0x01, 0x69, 0x03, 0x64, 0x04, 0x60, 0x06, 0x5c, + 0x07, 0x59, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x52, 0x0d, 0x50, 0x0f, 0x4f, + 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x48, + 0x17, 0x48, 0x18, 0x47, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, + 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, + 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, + 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, + 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, 0x70, 0x02, 0x6a, 0x03, + 0x67, 0x05, 0x63, 0x06, 0x60, 0x08, 0x5d, 0x09, 0x5b, 0x0b, 0x59, 0x0c, + 0x57, 0x0d, 0x55, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x4f, 0x13, + 0x50, 0x15, 0x4f, 0x15, 0x4e, 0x16, 0x4e, 0x18, 0x00, 0x78, 0x00, 0x71, + 0x01, 0x6a, 0x03, 0x66, 0x04, 0x61, 0x05, 0x5d, 0x06, 0x5a, 0x08, 0x58, + 0x09, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0e, 0x50, 0x0f, 0x4e, 0x10, 0x4e, + 0x11, 0x4c, 0x13, 0x4c, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x49, 0x16, 0x48, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, + 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, + 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, + 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7d, 0x00, 0x76, 0x01, 0x71, 0x02, 0x6c, 0x03, 0x68, 0x05, 0x64, 0x06, + 0x61, 0x07, 0x5e, 0x09, 0x5c, 0x0a, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0e, + 0x55, 0x0e, 0x55, 0x11, 0x52, 0x11, 0x51, 0x12, 0x51, 0x13, 0x50, 0x14, + 0x4f, 0x15, 0x4e, 0x15, 0x00, 0x79, 0x00, 0x71, 0x01, 0x6c, 0x02, 0x66, + 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5c, 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, + 0x0b, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, + 0x13, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x4a, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, + 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, + 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, + 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, + 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x77, 0x01, + 0x72, 0x02, 0x6d, 0x03, 0x69, 0x04, 0x66, 0x06, 0x63, 0x07, 0x5f, 0x08, + 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0c, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x0f, + 0x54, 0x11, 0x51, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x14, 0x50, 0x15, + 0x00, 0x79, 0x00, 0x72, 0x01, 0x6d, 0x02, 0x68, 0x03, 0x63, 0x04, 0x60, + 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x53, + 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, + 0x13, 0x4b, 0x14, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, + 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, + 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, + 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, + 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x78, 0x01, 0x72, 0x02, 0x6e, 0x02, + 0x6a, 0x03, 0x67, 0x05, 0x64, 0x06, 0x60, 0x07, 0x5f, 0x09, 0x5c, 0x0a, + 0x5b, 0x0b, 0x5a, 0x0c, 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0f, 0x53, 0x11, + 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x13, 0x00, 0x79, 0x00, 0x73, + 0x01, 0x6d, 0x02, 0x69, 0x03, 0x65, 0x04, 0x61, 0x05, 0x5e, 0x06, 0x5c, + 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0d, 0x53, 0x0d, 0x51, + 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, + 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, + 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, + 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, + 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7e, 0x00, 0x78, 0x01, 0x73, 0x02, 0x6e, 0x02, 0x6b, 0x03, 0x68, 0x05, + 0x65, 0x06, 0x62, 0x07, 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0b, + 0x59, 0x0c, 0x57, 0x0d, 0x56, 0x0e, 0x54, 0x0f, 0x54, 0x11, 0x52, 0x11, + 0x52, 0x12, 0x51, 0x13, 0x00, 0x79, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x69, + 0x03, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, + 0x09, 0x56, 0x0b, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, + 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x12, 0x4c, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, + 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, + 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, + 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x00, + 0x74, 0x01, 0x6f, 0x02, 0x6c, 0x03, 0x68, 0x04, 0x65, 0x05, 0x62, 0x06, + 0x61, 0x07, 0x5f, 0x09, 0x5c, 0x09, 0x5b, 0x0b, 0x5a, 0x0b, 0x58, 0x0c, + 0x57, 0x0d, 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, + 0x00, 0x79, 0x00, 0x74, 0x00, 0x6f, 0x02, 0x6a, 0x03, 0x66, 0x04, 0x63, + 0x05, 0x60, 0x05, 0x5e, 0x06, 0x5b, 0x07, 0x59, 0x09, 0x58, 0x09, 0x55, + 0x0b, 0x55, 0x0c, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, 0x0f, 0x4e, + 0x11, 0x4e, 0x11, 0x4d, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, + 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, + 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, + 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x79, 0x00, 0x74, 0x01, 0x70, 0x02, + 0x6c, 0x03, 0x69, 0x03, 0x66, 0x05, 0x63, 0x06, 0x62, 0x07, 0x5f, 0x07, + 0x5e, 0x09, 0x5c, 0x0a, 0x5a, 0x0b, 0x5a, 0x0c, 0x57, 0x0c, 0x56, 0x0e, + 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x00, 0x79, 0x00, 0x74, + 0x00, 0x70, 0x01, 0x6b, 0x02, 0x67, 0x03, 0x64, 0x04, 0x61, 0x05, 0x5f, + 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, + 0x0c, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x66, 0x7f, 0x4d, 0x68, 0x49, 0x5c, 0x47, 0x55, 0x45, 0x51, 0x45, 0x4e, + 0x44, 0x4c, 0x43, 0x4a, 0x43, 0x49, 0x43, 0x48, 0x43, 0x47, 0x42, 0x47, + 0x42, 0x46, 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, + 0x42, 0x44, 0x42, 0x44, 0x57, 0x7f, 0x2e, 0x58, 0x34, 0x4d, 0x37, 0x49, + 0x39, 0x47, 0x3a, 0x45, 0x3b, 0x44, 0x3b, 0x44, 0x3c, 0x43, 0x3c, 0x43, + 0x3c, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x41, + 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x99, 0x7f, 0x4d, 0x58, + 0x49, 0x4d, 0x47, 0x49, 0x45, 0x47, 0x45, 0x45, 0x44, 0x44, 0x43, 0x44, + 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa5, 0x16, 0x74, 0x23, + 0x62, 0x29, 0x59, 0x2d, 0x53, 0x30, 0x50, 0x32, 0x4e, 0x34, 0x4c, 0x35, + 0x4a, 0x36, 0x49, 0x37, 0x48, 0x38, 0x48, 0x38, 0x47, 0x39, 0x46, 0x39, + 0x46, 0x39, 0x46, 0x3a, 0x45, 0x3a, 0x45, 0x3a, 0x45, 0x3a, 0x44, 0x3b, + 0x23, 0x2f, 0x25, 0x35, 0x2a, 0x37, 0x2e, 0x39, 0x31, 0x3a, 0x33, 0x3b, + 0x34, 0x3b, 0x35, 0x3c, 0x37, 0x3c, 0x37, 0x3c, 0x38, 0x3d, 0x38, 0x3d, + 0x39, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x77, 0x27, 0x5f, 0x31, 0x56, 0x35, 0x52, 0x38, + 0x4f, 0x39, 0x4c, 0x3a, 0x4b, 0x3b, 0x49, 0x3b, 0x48, 0x3c, 0x48, 0x3c, + 0x47, 0x3c, 0x47, 0x3c, 0x46, 0x3d, 0x46, 0x3d, 0x45, 0x3d, 0x45, 0x3d, + 0x45, 0x3d, 0x44, 0x3d, 0x44, 0x3d, 0x44, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x5f, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xbb, 0x01, 0x91, 0x09, 0x78, 0x11, 0x6b, 0x17, + 0x62, 0x1c, 0x5c, 0x20, 0x58, 0x23, 0x55, 0x25, 0x52, 0x28, 0x51, 0x2a, + 0x4f, 0x2b, 0x4e, 0x2c, 0x4d, 0x2e, 0x4c, 0x2f, 0x4b, 0x30, 0x4a, 0x31, + 0x49, 0x31, 0x49, 0x32, 0x48, 0x33, 0x47, 0x33, 0x22, 0x25, 0x23, 0x2a, + 0x26, 0x2e, 0x2a, 0x31, 0x2c, 0x33, 0x2e, 0x34, 0x30, 0x36, 0x31, 0x37, + 0x32, 0x37, 0x33, 0x38, 0x34, 0x38, 0x35, 0x39, 0x36, 0x39, 0x36, 0x3a, + 0x37, 0x3a, 0x37, 0x3a, 0x38, 0x3a, 0x38, 0x3b, 0x39, 0x3b, 0x39, 0x3b, + 0x7a, 0x1a, 0x68, 0x24, 0x5f, 0x2a, 0x5a, 0x2e, 0x55, 0x31, 0x53, 0x33, + 0x51, 0x34, 0x4f, 0x35, 0x4d, 0x36, 0x4c, 0x37, 0x4b, 0x38, 0x4b, 0x38, + 0x4a, 0x39, 0x49, 0x39, 0x49, 0x39, 0x48, 0x3a, 0x47, 0x3a, 0x47, 0x3a, + 0x46, 0x3b, 0x46, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x39, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x8b, 0x00, 0x44, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xc6, 0x00, 0xa3, 0x02, 0x8a, 0x07, 0x7a, 0x0c, 0x6f, 0x11, 0x68, 0x15, + 0x62, 0x18, 0x5e, 0x1b, 0x5b, 0x1d, 0x58, 0x20, 0x56, 0x22, 0x54, 0x23, + 0x53, 0x25, 0x51, 0x26, 0x50, 0x28, 0x4e, 0x29, 0x4d, 0x2a, 0x4d, 0x2b, + 0x4d, 0x2c, 0x4c, 0x2c, 0x22, 0x23, 0x22, 0x27, 0x25, 0x2a, 0x27, 0x2c, + 0x2a, 0x2e, 0x2b, 0x30, 0x2d, 0x32, 0x2e, 0x33, 0x30, 0x34, 0x31, 0x34, + 0x32, 0x35, 0x33, 0x36, 0x33, 0x36, 0x34, 0x37, 0x34, 0x37, 0x35, 0x38, + 0x36, 0x38, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x7b, 0x16, 0x6d, 0x1f, + 0x65, 0x24, 0x5f, 0x28, 0x5b, 0x2b, 0x58, 0x2d, 0x55, 0x2f, 0x54, 0x31, + 0x52, 0x32, 0x50, 0x33, 0x4f, 0x34, 0x4e, 0x35, 0x4d, 0x35, 0x4c, 0x36, + 0x4b, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x4a, 0x38, 0x4a, 0x39, 0x49, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, + 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb3, 0x00, 0x9f, 0x00, 0x6d, 0x00, 0x33, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xcc, 0x00, 0xaf, 0x00, + 0x98, 0x03, 0x87, 0x06, 0x7b, 0x0a, 0x73, 0x0e, 0x6c, 0x11, 0x67, 0x13, + 0x63, 0x16, 0x5f, 0x18, 0x5c, 0x1a, 0x5a, 0x1c, 0x58, 0x1e, 0x56, 0x20, + 0x55, 0x21, 0x54, 0x22, 0x52, 0x24, 0x51, 0x25, 0x50, 0x26, 0x4f, 0x27, + 0x22, 0x22, 0x22, 0x25, 0x24, 0x27, 0x26, 0x2a, 0x28, 0x2b, 0x29, 0x2d, + 0x2b, 0x2f, 0x2c, 0x30, 0x2d, 0x31, 0x2f, 0x32, 0x30, 0x33, 0x30, 0x33, + 0x31, 0x34, 0x32, 0x35, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, 0x34, 0x36, + 0x35, 0x36, 0x35, 0x37, 0x7c, 0x15, 0x71, 0x1b, 0x68, 0x20, 0x63, 0x24, + 0x5f, 0x27, 0x5c, 0x29, 0x59, 0x2c, 0x57, 0x2d, 0x55, 0x2f, 0x54, 0x30, + 0x53, 0x31, 0x51, 0x32, 0x50, 0x32, 0x4f, 0x33, 0x4f, 0x34, 0x4e, 0x35, + 0x4d, 0x35, 0x4c, 0x35, 0x4b, 0x35, 0x4a, 0x36, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1d, + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, 0x00, + 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xcf, 0x00, 0xb7, 0x00, 0xa2, 0x00, 0x92, 0x03, + 0x85, 0x06, 0x7c, 0x09, 0x75, 0x0b, 0x6f, 0x0e, 0x6a, 0x11, 0x66, 0x13, + 0x63, 0x15, 0x5f, 0x17, 0x5e, 0x19, 0x5c, 0x1a, 0x5a, 0x1c, 0x57, 0x1d, + 0x56, 0x1f, 0x55, 0x1f, 0x55, 0x21, 0x54, 0x22, 0x22, 0x22, 0x22, 0x24, + 0x23, 0x26, 0x25, 0x28, 0x26, 0x29, 0x28, 0x2b, 0x29, 0x2c, 0x2b, 0x2d, + 0x2c, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x31, 0x30, 0x32, 0x30, 0x33, + 0x30, 0x33, 0x32, 0x33, 0x32, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, + 0x7c, 0x14, 0x73, 0x19, 0x6c, 0x1e, 0x67, 0x22, 0x63, 0x24, 0x5f, 0x26, + 0x5c, 0x28, 0x5a, 0x2a, 0x58, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2f, + 0x53, 0x30, 0x52, 0x31, 0x51, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4f, 0x33, + 0x4f, 0x34, 0x4e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, + 0x48, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xd2, 0x00, 0xbd, 0x00, 0xaa, 0x00, 0x9b, 0x01, 0x8e, 0x03, 0x84, 0x05, + 0x7c, 0x08, 0x76, 0x0a, 0x71, 0x0c, 0x6c, 0x0f, 0x69, 0x11, 0x66, 0x12, + 0x63, 0x14, 0x60, 0x16, 0x5e, 0x17, 0x5d, 0x19, 0x5b, 0x1a, 0x59, 0x1b, + 0x57, 0x1c, 0x56, 0x1e, 0x22, 0x22, 0x22, 0x23, 0x23, 0x25, 0x24, 0x27, + 0x25, 0x28, 0x27, 0x29, 0x28, 0x2b, 0x29, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, + 0x2d, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x30, 0x30, 0x32, 0x30, 0x33, + 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x7d, 0x13, 0x75, 0x18, + 0x6e, 0x1c, 0x69, 0x1f, 0x65, 0x22, 0x62, 0x24, 0x60, 0x26, 0x5c, 0x28, + 0x5b, 0x29, 0x59, 0x2a, 0x57, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2e, + 0x53, 0x2f, 0x53, 0x31, 0x51, 0x31, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd3, 0x00, 0xc2, 0x00, + 0xb1, 0x00, 0xa2, 0x00, 0x96, 0x02, 0x8c, 0x03, 0x83, 0x05, 0x7d, 0x07, + 0x77, 0x09, 0x73, 0x0b, 0x6f, 0x0d, 0x6a, 0x0f, 0x68, 0x11, 0x66, 0x12, + 0x63, 0x14, 0x60, 0x15, 0x5f, 0x16, 0x5e, 0x18, 0x5c, 0x19, 0x5a, 0x1a, + 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, 0x26, 0x29, + 0x27, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2e, + 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x30, 0x31, 0x30, 0x32, + 0x30, 0x33, 0x30, 0x33, 0x7d, 0x13, 0x76, 0x17, 0x70, 0x1b, 0x6b, 0x1e, + 0x68, 0x20, 0x65, 0x22, 0x61, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x29, + 0x5a, 0x2a, 0x58, 0x2b, 0x57, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2e, + 0x53, 0x2f, 0x53, 0x30, 0x53, 0x31, 0x51, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, + 0x00, 0x25, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, 0x00, + 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xd4, 0x00, 0xc5, 0x00, 0xb6, 0x00, 0xa8, 0x00, + 0x9c, 0x00, 0x92, 0x02, 0x8a, 0x03, 0x82, 0x05, 0x7d, 0x07, 0x78, 0x09, + 0x74, 0x0a, 0x71, 0x0c, 0x6c, 0x0e, 0x69, 0x0f, 0x68, 0x11, 0x65, 0x12, + 0x63, 0x13, 0x60, 0x14, 0x5f, 0x16, 0x5e, 0x17, 0x21, 0x22, 0x22, 0x23, + 0x22, 0x24, 0x23, 0x25, 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, 0x27, 0x29, + 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2e, + 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x31, + 0x7d, 0x12, 0x77, 0x16, 0x71, 0x19, 0x6d, 0x1c, 0x69, 0x1f, 0x66, 0x20, + 0x64, 0x23, 0x61, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x28, 0x5b, 0x2a, + 0x58, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x54, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, + 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, + 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xd5, 0x00, 0xc7, 0x00, 0xba, 0x00, 0xad, 0x00, 0xa2, 0x00, 0x98, 0x01, + 0x8f, 0x02, 0x88, 0x03, 0x82, 0x05, 0x7e, 0x07, 0x78, 0x08, 0x74, 0x0a, + 0x72, 0x0b, 0x6e, 0x0c, 0x6b, 0x0e, 0x69, 0x0f, 0x67, 0x11, 0x65, 0x12, + 0x63, 0x13, 0x60, 0x14, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, + 0x24, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, 0x27, 0x29, 0x28, 0x2b, + 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, 0x2a, 0x2d, 0x2c, 0x2d, 0x2d, 0x2e, + 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x7d, 0x12, 0x77, 0x15, + 0x72, 0x18, 0x6e, 0x1b, 0x6b, 0x1d, 0x68, 0x20, 0x65, 0x21, 0x64, 0x23, + 0x60, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x59, 0x2a, + 0x57, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x54, 0x2e, 0x53, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, + 0x00, 0x3a, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, + 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, + 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd6, 0x00, 0xc9, 0x00, + 0xbd, 0x00, 0xb2, 0x00, 0xa7, 0x00, 0x9d, 0x00, 0x95, 0x01, 0x8e, 0x02, + 0x87, 0x03, 0x82, 0x05, 0x7e, 0x06, 0x79, 0x08, 0x75, 0x09, 0x73, 0x0b, + 0x70, 0x0c, 0x6c, 0x0d, 0x6a, 0x0e, 0x68, 0x0f, 0x67, 0x11, 0x65, 0x12, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x26, + 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, + 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, + 0x2d, 0x2f, 0x2d, 0x30, 0x7d, 0x12, 0x78, 0x15, 0x73, 0x18, 0x70, 0x1a, + 0x6d, 0x1d, 0x69, 0x1e, 0x67, 0x20, 0x65, 0x21, 0x63, 0x23, 0x60, 0x24, + 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x5a, 0x2a, 0x58, 0x2a, + 0x57, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, + 0x00, 0x30, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, + 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, + 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xd7, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb5, 0x00, + 0xab, 0x00, 0xa2, 0x00, 0x9a, 0x00, 0x92, 0x02, 0x8d, 0x02, 0x87, 0x03, + 0x81, 0x05, 0x7e, 0x06, 0x79, 0x07, 0x76, 0x09, 0x74, 0x0a, 0x71, 0x0b, + 0x6e, 0x0c, 0x6b, 0x0d, 0x6a, 0x0e, 0x68, 0x10, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x23, 0x23, 0x24, 0x23, 0x25, 0x24, 0x25, 0x25, 0x27, 0x25, 0x27, + 0x27, 0x28, 0x27, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, + 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, + 0x7e, 0x12, 0x79, 0x15, 0x75, 0x17, 0x71, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, + 0x69, 0x1f, 0x65, 0x20, 0x65, 0x22, 0x62, 0x23, 0x60, 0x23, 0x60, 0x25, + 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x5c, 0x29, 0x59, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, + 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, + 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xd7, 0x00, 0xcc, 0x00, 0xc2, 0x00, 0xb9, 0x00, 0xae, 0x00, 0xa6, 0x00, + 0x9e, 0x00, 0x97, 0x01, 0x90, 0x02, 0x8b, 0x02, 0x85, 0x04, 0x81, 0x05, + 0x7e, 0x06, 0x7a, 0x07, 0x76, 0x08, 0x74, 0x09, 0x72, 0x0b, 0x6f, 0x0c, + 0x6c, 0x0c, 0x6a, 0x0e, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, + 0x26, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x7e, 0x12, 0x79, 0x15, + 0x75, 0x17, 0x72, 0x19, 0x6e, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x68, 0x20, + 0x65, 0x20, 0x65, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x5a, 0x2a, 0x58, 0x2a, 0x57, 0x2a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3c, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, + 0x00, 0x1c, 0x00, 0x16, 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, + 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xce, 0x00, + 0xc5, 0x00, 0xbb, 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa1, 0x00, 0x9b, 0x00, + 0x94, 0x01, 0x8f, 0x02, 0x8a, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, + 0x7b, 0x07, 0x77, 0x08, 0x75, 0x09, 0x73, 0x0a, 0x71, 0x0b, 0x6e, 0x0c, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, + 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, + 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, + 0x2b, 0x2d, 0x2b, 0x2d, 0x7e, 0x12, 0x7a, 0x14, 0x76, 0x16, 0x72, 0x18, + 0x6f, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, + 0x64, 0x23, 0x61, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, + 0x5c, 0x27, 0x5c, 0x28, 0x5b, 0x2a, 0x59, 0x2a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, + 0x00, 0x35, 0x00, 0x31, 0x00, 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, + 0x00, 0x14, 0x00, 0x0f, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, + 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, + 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbd, 0x00, + 0xb5, 0x00, 0xad, 0x00, 0xa6, 0x00, 0x9e, 0x00, 0x99, 0x00, 0x92, 0x01, + 0x8e, 0x02, 0x89, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, 0x7b, 0x07, + 0x77, 0x07, 0x75, 0x09, 0x73, 0x09, 0x72, 0x0b, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, + 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, + 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, + 0x7e, 0x12, 0x7a, 0x14, 0x76, 0x15, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, + 0x6c, 0x1d, 0x69, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x65, 0x21, 0x64, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x27, 0x5c, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, + 0x00, 0x2d, 0x00, 0x28, 0x00, 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, + 0x00, 0x0d, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, + 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, + 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbf, 0x00, 0xb8, 0x00, 0xaf, 0x00, + 0xa9, 0x00, 0xa1, 0x00, 0x9c, 0x00, 0x96, 0x01, 0x90, 0x02, 0x8d, 0x02, + 0x88, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7b, 0x07, 0x77, 0x07, + 0x76, 0x09, 0x74, 0x09, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, + 0x26, 0x27, 0x27, 0x27, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, + 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x7e, 0x11, 0x7a, 0x13, + 0x76, 0x15, 0x74, 0x18, 0x72, 0x19, 0x6e, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, + 0x69, 0x1e, 0x67, 0x20, 0x65, 0x20, 0x65, 0x21, 0x63, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x58, 0x00, 0x3d, + 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x42, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x0b, 0x07, 0x17, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3a, 0x00, 0x3c, + 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x47, 0x07, 0x0f, 0x0f, + 0x00, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, + 0x00, 0x3f, 0x00, 0x3f, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd1, 0x00, + 0xc9, 0x00, 0xc1, 0x00, 0xba, 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, + 0x9e, 0x00, 0x9a, 0x00, 0x93, 0x01, 0x8f, 0x02, 0x8c, 0x02, 0x88, 0x03, + 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7b, 0x06, 0x78, 0x07, 0x76, 0x08, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, + 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, + 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, + 0x2a, 0x2b, 0x2b, 0x2b, 0x7e, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x75, 0x17, + 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x69, 0x1f, + 0x65, 0x20, 0x65, 0x20, 0x65, 0x22, 0x62, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x25, 0x5f, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x30, 0x00, 0x39, 0x00, 0x42, + 0x00, 0x41, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x27, 0x02, + 0x02, 0x12, 0x00, 0x25, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, + 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x3e, 0x7f, 0x00, 0x3f, 0x00, 0x05, 0x05, 0x00, 0x1c, + 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3b, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc3, 0x00, + 0xbb, 0x00, 0xb5, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x9d, 0x00, + 0x98, 0x00, 0x92, 0x01, 0x8e, 0x02, 0x8b, 0x02, 0x87, 0x03, 0x83, 0x04, + 0x81, 0x05, 0x7f, 0x06, 0x7c, 0x06, 0x78, 0x07, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, + 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x27, 0x28, + 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, + 0x7e, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x76, 0x17, 0x72, 0x18, 0x71, 0x1a, + 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x68, 0x20, 0x65, 0x20, + 0x65, 0x20, 0x65, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, + 0x5f, 0x27, 0x5c, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, + 0x00, 0x24, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, + 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x4c, 0x00, 0x39, 0x00, 0x16, 0x00, 0x28, 0x00, 0x2f, 0x00, 0x2e, + 0x00, 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, + 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x6f, 0x00, 0x22, 0x00, 0x09, 0x0b, + 0x00, 0x16, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x35, + 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, + 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x00, 0x00, 0x09, 0x00, 0x19, + 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, + 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xbd, 0x00, 0xb7, 0x00, + 0xb0, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9f, 0x00, 0x9b, 0x00, 0x95, 0x01, + 0x91, 0x01, 0x8e, 0x02, 0x8a, 0x02, 0x86, 0x03, 0x83, 0x04, 0x81, 0x05, + 0x7f, 0x06, 0x7c, 0x06, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, + 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x7f, 0x11, 0x7b, 0x13, + 0x77, 0x15, 0x76, 0x16, 0x72, 0x18, 0x72, 0x19, 0x6d, 0x1a, 0x6d, 0x1b, + 0x6b, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, 0x65, 0x20, + 0x64, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x24, 0x5f, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1d, + 0x00, 0x25, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, + 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x42, + 0x00, 0x28, 0x00, 0x09, 0x00, 0x16, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, + 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, + 0x00, 0x39, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaf, 0x00, 0x93, 0x00, 0x58, 0x00, 0x21, 0x00, 0x0e, 0x08, 0x02, 0x0e, + 0x00, 0x18, 0x00, 0x20, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x31, + 0x00, 0x33, 0x00, 0x35, 0x00, 0x36, 0x00, 0x38, 0xb2, 0x00, 0x9c, 0x00, + 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x16, + 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, + 0x00, 0x33, 0x00, 0x35, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xda, 0x00, 0xd2, 0x00, + 0xcb, 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xac, 0x00, + 0xa8, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x01, 0x90, 0x02, + 0x8d, 0x02, 0x89, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, + 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, + 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x29, 0x27, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x29, 0x7f, 0x11, 0x7b, 0x13, 0x78, 0x15, 0x76, 0x15, + 0x73, 0x18, 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, + 0x69, 0x1d, 0x69, 0x1f, 0x65, 0x20, 0x65, 0x20, 0x65, 0x21, 0x64, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, + 0x00, 0x25, 0x00, 0x2a, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, + 0x00, 0x35, 0x00, 0x37, 0x00, 0x48, 0x00, 0x41, 0x00, 0x2f, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, + 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x00, 0xa4, 0x00, + 0x7a, 0x00, 0x4a, 0x00, 0x20, 0x00, 0x12, 0x06, 0x07, 0x0c, 0x00, 0x10, + 0x00, 0x18, 0x00, 0x1f, 0x00, 0x24, 0x00, 0x28, 0x00, 0x2c, 0x00, 0x2e, + 0x00, 0x30, 0x00, 0x32, 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, + 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, + 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, + 0x33, 0x22, 0x28, 0x22, 0x25, 0x22, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x22, 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, + 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, + 0x22, 0x21, 0x22, 0x21, 0x9d, 0x05, 0x6b, 0x0d, 0x51, 0x12, 0x45, 0x15, + 0x3d, 0x17, 0x38, 0x18, 0x35, 0x1a, 0x32, 0x1b, 0x30, 0x1b, 0x2f, 0x1c, + 0x2d, 0x1c, 0x2d, 0x1c, 0x2c, 0x1d, 0x2b, 0x1d, 0x2a, 0x1e, 0x2a, 0x1e, + 0x29, 0x1e, 0x29, 0x1e, 0x28, 0x1e, 0x28, 0x1e, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x5f, 0x13, 0x46, 0x17, 0x39, 0x1a, 0x33, 0x1b, 0x2f, 0x1c, 0x2d, 0x1d, + 0x2b, 0x1e, 0x2a, 0x1e, 0x29, 0x1e, 0x28, 0x1f, 0x27, 0x1f, 0x27, 0x1f, + 0x27, 0x1f, 0x26, 0x1f, 0x26, 0x20, 0x26, 0x20, 0x25, 0x20, 0x25, 0x20, + 0x25, 0x20, 0x25, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, + 0x00, 0x25, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, + 0x00, 0x42, 0x00, 0x3d, 0x00, 0x2e, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, + 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb9, 0x00, 0xad, 0x00, 0x8f, 0x00, 0x68, 0x00, + 0x42, 0x00, 0x20, 0x00, 0x14, 0x05, 0x0b, 0x0a, 0x04, 0x0d, 0x00, 0x12, + 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2c, + 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, + 0x29, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x13, + 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, 0x37, 0x28, 0x2d, 0x25, + 0x28, 0x23, 0x26, 0x23, 0x25, 0x23, 0x24, 0x22, 0x24, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0xbc, 0x00, 0x8e, 0x01, 0x73, 0x04, 0x60, 0x08, 0x55, 0x0b, 0x4c, 0x0d, + 0x47, 0x0f, 0x42, 0x11, 0x3e, 0x12, 0x3b, 0x13, 0x39, 0x14, 0x37, 0x15, + 0x36, 0x16, 0x34, 0x16, 0x33, 0x17, 0x32, 0x18, 0x31, 0x18, 0x30, 0x19, + 0x2f, 0x19, 0x2f, 0x19, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x6f, 0x11, 0x58, 0x11, + 0x4a, 0x13, 0x41, 0x15, 0x3b, 0x16, 0x37, 0x17, 0x34, 0x18, 0x32, 0x19, + 0x30, 0x1a, 0x2e, 0x1a, 0x2d, 0x1b, 0x2c, 0x1b, 0x2c, 0x1c, 0x2b, 0x1c, + 0x2a, 0x1c, 0x2a, 0x1d, 0x29, 0x1d, 0x29, 0x1d, 0x28, 0x1d, 0x28, 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, + 0x00, 0x26, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2d, 0x00, 0x3e, 0x00, 0x3a, + 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, + 0x00, 0x2b, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xba, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x7d, 0x00, 0x5c, 0x00, 0x3c, 0x00, + 0x20, 0x00, 0x16, 0x04, 0x0e, 0x08, 0x07, 0x0c, 0x02, 0x0e, 0x00, 0x13, + 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0xbb, 0x00, 0xb5, 0x00, + 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, 0x00, + 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x12, + 0x00, 0x17, 0x00, 0x1c, 0x38, 0x2c, 0x30, 0x28, 0x2b, 0x26, 0x29, 0x25, + 0x27, 0x24, 0x26, 0x24, 0x25, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x22, + 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xc6, 0x00, 0xa4, 0x00, + 0x89, 0x00, 0x77, 0x02, 0x67, 0x04, 0x5e, 0x06, 0x55, 0x08, 0x50, 0x0a, + 0x4b, 0x0c, 0x47, 0x0d, 0x44, 0x0e, 0x41, 0x0f, 0x3e, 0x10, 0x3d, 0x11, + 0x3b, 0x12, 0x39, 0x12, 0x38, 0x13, 0x37, 0x14, 0x35, 0x15, 0x35, 0x16, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x74, 0x11, 0x63, 0x11, 0x55, 0x11, 0x4c, 0x12, + 0x44, 0x13, 0x40, 0x14, 0x3b, 0x15, 0x39, 0x16, 0x36, 0x17, 0x34, 0x17, + 0x33, 0x18, 0x31, 0x18, 0x30, 0x19, 0x2f, 0x19, 0x2e, 0x1a, 0x2d, 0x1a, + 0x2d, 0x1a, 0x2c, 0x1b, 0x2b, 0x1b, 0x2b, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, + 0x00, 0x26, 0x00, 0x28, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, + 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, + 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb5, 0x00, + 0xa4, 0x00, 0x8b, 0x00, 0x6f, 0x00, 0x52, 0x00, 0x37, 0x00, 0x20, 0x00, + 0x17, 0x04, 0x10, 0x07, 0x0a, 0x0a, 0x05, 0x0d, 0x00, 0x0f, 0x00, 0x14, + 0x00, 0x19, 0x00, 0x1d, 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, + 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x20, 0x00, + 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, + 0x3a, 0x2f, 0x32, 0x2b, 0x2e, 0x28, 0x2b, 0x27, 0x28, 0x25, 0x27, 0x25, + 0x27, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, + 0x24, 0x23, 0x23, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x23, 0x22, 0xcd, 0x00, 0xb0, 0x00, 0x9a, 0x00, 0x87, 0x00, + 0x78, 0x01, 0x6c, 0x03, 0x64, 0x04, 0x5c, 0x05, 0x57, 0x07, 0x51, 0x09, + 0x4e, 0x0a, 0x4a, 0x0b, 0x48, 0x0c, 0x45, 0x0c, 0x43, 0x0e, 0x40, 0x0f, + 0x3f, 0x0f, 0x3d, 0x0f, 0x3c, 0x11, 0x3b, 0x12, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x77, 0x11, 0x69, 0x11, 0x5e, 0x11, 0x54, 0x11, 0x4d, 0x11, 0x47, 0x12, + 0x43, 0x13, 0x3f, 0x13, 0x3c, 0x14, 0x39, 0x15, 0x38, 0x16, 0x36, 0x16, + 0x35, 0x17, 0x33, 0x17, 0x32, 0x18, 0x31, 0x18, 0x30, 0x18, 0x2f, 0x18, + 0x2f, 0x19, 0x2e, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x0d, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, + 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, + 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xaa, 0x00, 0x96, 0x00, + 0x7e, 0x00, 0x64, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x20, 0x00, 0x18, 0x03, + 0x11, 0x06, 0x0c, 0x09, 0x07, 0x0c, 0x03, 0x0e, 0x00, 0x10, 0x00, 0x15, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, + 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, + 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3b, 0x32, 0x34, 0x2d, + 0x2f, 0x2a, 0x2c, 0x28, 0x2a, 0x27, 0x29, 0x26, 0x27, 0x25, 0x27, 0x25, + 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x23, 0x24, 0x23, + 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x22, + 0xcf, 0x00, 0xb9, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x85, 0x00, 0x7a, 0x01, + 0x70, 0x02, 0x68, 0x03, 0x61, 0x04, 0x5c, 0x05, 0x57, 0x07, 0x53, 0x07, + 0x50, 0x09, 0x4c, 0x0a, 0x4a, 0x0a, 0x48, 0x0c, 0x45, 0x0c, 0x44, 0x0c, + 0x42, 0x0d, 0x41, 0x0f, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x78, 0x11, 0x6d, 0x11, + 0x63, 0x11, 0x5b, 0x11, 0x53, 0x11, 0x4e, 0x11, 0x49, 0x12, 0x45, 0x12, + 0x41, 0x13, 0x3f, 0x13, 0x3c, 0x14, 0x3a, 0x14, 0x39, 0x15, 0x37, 0x16, + 0x36, 0x16, 0x35, 0x17, 0x33, 0x17, 0x33, 0x17, 0x32, 0x17, 0x31, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, + 0x00, 0x11, 0x00, 0x16, 0x00, 0x1a, 0x00, 0x1d, 0x00, 0x3e, 0x00, 0x3d, + 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, + 0x00, 0x1a, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9d, 0x00, 0x89, 0x00, 0x73, 0x00, + 0x5c, 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, 0x00, 0x19, 0x03, 0x13, 0x06, + 0x0e, 0x08, 0x09, 0x0b, 0x05, 0x0d, 0x01, 0x0e, 0xbd, 0x00, 0xba, 0x00, + 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, 0x00, + 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, + 0x0b, 0x00, 0x03, 0x00, 0x3c, 0x34, 0x35, 0x2f, 0x31, 0x2c, 0x2e, 0x2a, + 0x2c, 0x28, 0x2a, 0x27, 0x28, 0x27, 0x28, 0x26, 0x27, 0x25, 0x27, 0x25, + 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x23, + 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0xd2, 0x00, 0xbe, 0x00, + 0xad, 0x00, 0x9d, 0x00, 0x90, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, 0x01, + 0x6b, 0x02, 0x65, 0x04, 0x60, 0x04, 0x5b, 0x05, 0x58, 0x06, 0x54, 0x07, + 0x51, 0x07, 0x4f, 0x09, 0x4c, 0x0a, 0x4a, 0x0a, 0x48, 0x0b, 0x46, 0x0c, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x7a, 0x11, 0x70, 0x11, 0x67, 0x11, 0x5f, 0x11, + 0x59, 0x11, 0x53, 0x11, 0x4e, 0x11, 0x4a, 0x11, 0x46, 0x12, 0x43, 0x13, + 0x41, 0x13, 0x3e, 0x13, 0x3d, 0x14, 0x3b, 0x14, 0x39, 0x14, 0x38, 0x15, + 0x37, 0x16, 0x36, 0x16, 0x35, 0x16, 0x34, 0x17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, + 0x00, 0x14, 0x00, 0x18, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, + 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, + 0xb1, 0x00, 0xa3, 0x00, 0x92, 0x00, 0x7e, 0x00, 0x6a, 0x00, 0x56, 0x00, + 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x19, 0x03, 0x14, 0x05, 0x0f, 0x08, + 0x0b, 0x0a, 0x07, 0x0c, 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, + 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4d, 0x00, + 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, 0x00, + 0x3c, 0x35, 0x36, 0x31, 0x32, 0x2e, 0x2f, 0x2b, 0x2d, 0x2a, 0x2b, 0x28, + 0x2a, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, + 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x23, 0x24, 0x23, 0xd3, 0x00, 0xc2, 0x00, 0xb2, 0x00, 0xa4, 0x00, + 0x98, 0x00, 0x8d, 0x00, 0x83, 0x00, 0x7b, 0x00, 0x73, 0x01, 0x6e, 0x02, + 0x67, 0x02, 0x63, 0x04, 0x5f, 0x04, 0x5b, 0x05, 0x58, 0x05, 0x55, 0x07, + 0x52, 0x07, 0x50, 0x07, 0x4e, 0x09, 0x4c, 0x0a, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x7a, 0x11, 0x72, 0x11, 0x6a, 0x11, 0x63, 0x11, 0x5d, 0x11, 0x57, 0x11, + 0x52, 0x11, 0x4e, 0x11, 0x4a, 0x11, 0x48, 0x12, 0x44, 0x12, 0x42, 0x13, + 0x40, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x14, 0x3a, 0x14, 0x39, 0x14, + 0x38, 0x15, 0x37, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, + 0x00, 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa8, 0x00, + 0x99, 0x00, 0x87, 0x00, 0x75, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, + 0x2e, 0x00, 0x1f, 0x00, 0x1a, 0x02, 0x15, 0x05, 0x10, 0x07, 0x0c, 0x09, + 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, + 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, + 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, 0x00, 0x3c, 0x36, 0x37, 0x32, + 0x33, 0x2f, 0x30, 0x2d, 0x2e, 0x2b, 0x2d, 0x2a, 0x2b, 0x28, 0x2a, 0x28, + 0x28, 0x27, 0x28, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, + 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, + 0xd5, 0x00, 0xc5, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x9f, 0x00, 0x95, 0x00, + 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, 0x01, 0x6f, 0x01, 0x69, 0x02, + 0x66, 0x02, 0x61, 0x04, 0x5e, 0x04, 0x5a, 0x05, 0x58, 0x05, 0x55, 0x06, + 0x53, 0x07, 0x51, 0x07, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7b, 0x11, 0x73, 0x11, + 0x6c, 0x11, 0x66, 0x11, 0x60, 0x11, 0x5b, 0x11, 0x56, 0x11, 0x52, 0x11, + 0x4f, 0x11, 0x4b, 0x11, 0x48, 0x11, 0x45, 0x12, 0x44, 0x12, 0x41, 0x13, + 0x40, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x14, 0x3a, 0x14, 0x39, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, + 0x00, 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x09, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x8f, 0x00, + 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2d, 0x00, + 0x1f, 0x00, 0x1a, 0x02, 0x16, 0x04, 0x11, 0x06, 0xbe, 0x00, 0xbc, 0x00, + 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, 0x00, + 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, + 0x2c, 0x00, 0x23, 0x00, 0x3c, 0x37, 0x38, 0x33, 0x34, 0x30, 0x31, 0x2e, + 0x2f, 0x2c, 0x2d, 0x2a, 0x2c, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x27, + 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0xd5, 0x00, 0xc8, 0x00, + 0xbb, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x92, 0x00, 0x8a, 0x00, + 0x82, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x70, 0x01, 0x6c, 0x02, 0x67, 0x02, + 0x64, 0x03, 0x60, 0x04, 0x5d, 0x04, 0x5b, 0x05, 0x58, 0x05, 0x55, 0x05, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x7b, 0x11, 0x75, 0x11, 0x6e, 0x11, 0x68, 0x11, + 0x63, 0x11, 0x5e, 0x11, 0x5a, 0x11, 0x56, 0x11, 0x52, 0x11, 0x4f, 0x11, + 0x4c, 0x11, 0x49, 0x11, 0x47, 0x12, 0x44, 0x12, 0x43, 0x12, 0x41, 0x13, + 0x3f, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x09, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, + 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, + 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, + 0xb7, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x86, 0x00, 0x77, 0x00, + 0x67, 0x00, 0x57, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2c, 0x00, 0x1f, 0x00, + 0x1b, 0x02, 0x16, 0x04, 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, + 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6c, 0x00, + 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, 0x00, + 0x3d, 0x38, 0x38, 0x34, 0x35, 0x31, 0x33, 0x2f, 0x30, 0x2d, 0x2e, 0x2c, + 0x2d, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x27, 0x28, 0x27, + 0x28, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x24, 0xd6, 0x00, 0xca, 0x00, 0xbe, 0x00, 0xb3, 0x00, + 0xaa, 0x00, 0xa0, 0x00, 0x98, 0x00, 0x90, 0x00, 0x88, 0x00, 0x82, 0x00, + 0x7d, 0x00, 0x77, 0x00, 0x71, 0x01, 0x6d, 0x01, 0x69, 0x02, 0x66, 0x02, + 0x63, 0x04, 0x60, 0x04, 0x5d, 0x04, 0x5b, 0x05, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x7c, 0x11, 0x76, 0x11, 0x70, 0x11, 0x6a, 0x11, 0x66, 0x11, 0x61, 0x11, + 0x5d, 0x11, 0x59, 0x11, 0x55, 0x11, 0x52, 0x11, 0x4f, 0x11, 0x4c, 0x11, + 0x49, 0x11, 0x47, 0x11, 0x45, 0x12, 0x44, 0x12, 0x42, 0x13, 0x41, 0x13, + 0x3f, 0x13, 0x3e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, + 0x00, 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, + 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xb0, 0x00, + 0xa6, 0x00, 0x9a, 0x00, 0x8d, 0x00, 0x7f, 0x00, 0x70, 0x00, 0x61, 0x00, + 0x53, 0x00, 0x45, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x1b, 0x02, + 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, + 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, + 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x3d, 0x38, 0x39, 0x35, + 0x36, 0x32, 0x33, 0x30, 0x31, 0x2e, 0x30, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, + 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, + 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, + 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb7, 0x00, 0xad, 0x00, 0xa5, 0x00, + 0x9d, 0x00, 0x96, 0x00, 0x8f, 0x00, 0x88, 0x00, 0x81, 0x00, 0x7d, 0x00, + 0x77, 0x00, 0x73, 0x01, 0x6f, 0x01, 0x6b, 0x02, 0x67, 0x02, 0x64, 0x02, + 0x62, 0x04, 0x5f, 0x04, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x77, 0x11, + 0x71, 0x11, 0x6c, 0x11, 0x67, 0x11, 0x63, 0x11, 0x5f, 0x11, 0x5c, 0x11, + 0x58, 0x11, 0x55, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4c, 0x11, 0x4a, 0x11, + 0x48, 0x11, 0x46, 0x12, 0x44, 0x12, 0x43, 0x12, 0x42, 0x13, 0x40, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, + 0x00, 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xa9, 0x00, 0x9e, 0x00, + 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x5c, 0x00, 0x4f, 0x00, + 0x42, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0xbe, 0x00, 0xbd, 0x00, + 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, 0x00, + 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x48, 0x00, 0x3f, 0x00, 0x3d, 0x39, 0x39, 0x36, 0x36, 0x33, 0x33, 0x30, + 0x32, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x29, + 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0xd7, 0x00, 0xcc, 0x00, + 0xc3, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x99, 0x00, + 0x93, 0x00, 0x8d, 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, + 0x74, 0x00, 0x70, 0x01, 0x6c, 0x01, 0x69, 0x02, 0x66, 0x02, 0x63, 0x02, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x77, 0x11, 0x72, 0x11, 0x6e, 0x11, + 0x69, 0x11, 0x65, 0x11, 0x61, 0x11, 0x5d, 0x11, 0x5a, 0x11, 0x57, 0x11, + 0x54, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, + 0x47, 0x11, 0x45, 0x12, 0x44, 0x12, 0x42, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x39, 0x39, 0x36, 0x36, 0x33, 0x34, 0x32, 0x33, 0x30, 0x30, 0x2e, + 0x30, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, + 0x29, 0x28, 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x25, 0xd7, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbc, 0x00, + 0xb4, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x91, 0x00, + 0x8c, 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x75, 0x00, + 0x71, 0x01, 0x6d, 0x01, 0x6b, 0x02, 0x68, 0x02, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x7c, 0x11, 0x78, 0x11, 0x73, 0x11, 0x6f, 0x11, 0x6b, 0x11, 0x67, 0x11, + 0x63, 0x11, 0x60, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x57, 0x11, 0x54, 0x11, + 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, + 0x46, 0x12, 0x45, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x3a, 0x36, + 0x37, 0x34, 0x35, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2d, 0x2e, 0x2d, + 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, + 0x28, 0x28, 0x28, 0x27, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, + 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb6, 0x00, 0xaf, 0x00, + 0xa8, 0x00, 0xa1, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, + 0x86, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x75, 0x00, 0x72, 0x01, + 0x6f, 0x01, 0x6b, 0x01, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x78, 0x11, + 0x74, 0x11, 0x70, 0x11, 0x6c, 0x11, 0x68, 0x11, 0x65, 0x11, 0x61, 0x11, + 0x5f, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x56, 0x11, 0x54, 0x11, 0x51, 0x11, + 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x35, 0x36, 0x33, + 0x33, 0x31, 0x32, 0x30, 0x30, 0x2e, 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, + 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, + 0x28, 0x27, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0xd8, 0x00, 0xd0, 0x00, + 0xc8, 0x00, 0xc0, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa5, 0x00, + 0x9f, 0x00, 0x99, 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8a, 0x00, 0x86, 0x00, + 0x81, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x76, 0x00, 0x73, 0x00, 0x70, 0x01, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, 0x75, 0x11, 0x71, 0x11, + 0x6d, 0x11, 0x6a, 0x11, 0x66, 0x11, 0x63, 0x11, 0x60, 0x11, 0x5d, 0x11, + 0x5b, 0x11, 0x58, 0x11, 0x56, 0x11, 0x54, 0x11, 0x51, 0x11, 0x4f, 0x11, + 0x4d, 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x49, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x36, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, + 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2c, 0x2b, + 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x27, 0x29, 0x27, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc2, 0x00, + 0xbb, 0x00, 0xb4, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9c, 0x00, + 0x97, 0x00, 0x92, 0x00, 0x8e, 0x00, 0x89, 0x00, 0x85, 0x00, 0x81, 0x00, + 0x7d, 0x00, 0x7a, 0x00, 0x76, 0x00, 0x73, 0x00, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x7d, 0x11, 0x79, 0x11, 0x75, 0x11, 0x72, 0x11, 0x6e, 0x11, 0x6b, 0x11, + 0x68, 0x11, 0x65, 0x11, 0x62, 0x11, 0x5f, 0x11, 0x5c, 0x11, 0x5a, 0x11, + 0x58, 0x11, 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, + 0x4c, 0x11, 0x4a, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3b, 0x38, + 0x39, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x30, 0x32, 0x30, 0x30, 0x2e, + 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, + 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbc, 0x00, 0xb6, 0x00, + 0xb0, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9a, 0x00, 0x96, 0x00, + 0x91, 0x00, 0x8d, 0x00, 0x89, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, + 0x7a, 0x00, 0x77, 0x00, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, + 0x76, 0x11, 0x72, 0x11, 0x6f, 0x11, 0x6c, 0x11, 0x69, 0x11, 0x66, 0x11, + 0x63, 0x11, 0x61, 0x11, 0x5e, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x57, 0x11, + 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3b, 0x39, 0x39, 0x36, 0x36, 0x34, + 0x35, 0x33, 0x33, 0x31, 0x33, 0x30, 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, + 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd2, 0x00, + 0xcb, 0x00, 0xc4, 0x00, 0xbe, 0x00, 0xb8, 0x00, 0xb2, 0x00, 0xad, 0x00, + 0xa8, 0x00, 0xa2, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x94, 0x00, 0x90, 0x00, + 0x8b, 0x00, 0x88, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7b, 0x00, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, 0x76, 0x11, 0x73, 0x11, + 0x70, 0x11, 0x6d, 0x11, 0x6a, 0x11, 0x67, 0x11, 0x65, 0x11, 0x62, 0x11, + 0x5f, 0x11, 0x5d, 0x11, 0x5b, 0x11, 0x59, 0x11, 0x56, 0x11, 0x55, 0x11, + 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3b, 0x3b, 0x39, 0x3a, 0x36, 0x36, 0x35, 0x36, 0x33, 0x33, 0x32, + 0x33, 0x30, 0x31, 0x30, 0x30, 0x2f, 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x2b, 0x28, + 0x28, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd3, 0x00, 0xcc, 0x00, 0xc6, 0x00, + 0xc0, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa5, 0x00, + 0xa0, 0x00, 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x00, 0x8b, 0x00, + 0x88, 0x00, 0x83, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x7d, 0x11, 0x7a, 0x11, 0x77, 0x11, 0x74, 0x11, 0x71, 0x11, 0x6e, 0x11, + 0x6b, 0x11, 0x68, 0x11, 0x66, 0x11, 0x63, 0x11, 0x61, 0x11, 0x5e, 0x11, + 0x5c, 0x11, 0x5a, 0x11, 0x58, 0x11, 0x56, 0x11, 0x55, 0x11, 0x52, 0x11, + 0x51, 0x11, 0x4f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3b, 0x3b, 0x39, + 0x39, 0x36, 0x37, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x31, 0x32, 0x30, + 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, + 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc7, 0x00, 0xc1, 0x00, 0xbc, 0x00, + 0xb7, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa2, 0x00, 0x9e, 0x00, + 0x9a, 0x00, 0x95, 0x00, 0x93, 0x00, 0x8e, 0x00, 0x8b, 0x00, 0x87, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, + 0x77, 0x11, 0x74, 0x11, 0x71, 0x11, 0x6f, 0x11, 0x6c, 0x11, 0x69, 0x11, + 0x67, 0x11, 0x64, 0x11, 0x62, 0x11, 0x60, 0x11, 0x5e, 0x11, 0x5b, 0x11, + 0x5a, 0x11, 0x58, 0x11, 0x56, 0x11, 0x54, 0x11, 0x52, 0x11, 0x51, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0xb8, 0x00, 0xc5, 0x00, 0xcb, 0x00, + 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, + 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, 0x00, + 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x44, 0x00, 0x9d, 0x00, 0xbc, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xcf, 0x00, + 0xd2, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, + 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, + 0xd9, 0x00, 0xd9, 0x00, 0x90, 0x00, 0xbd, 0x00, 0xcc, 0x00, 0xd1, 0x00, + 0xd5, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, + 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x01, 0x8b, 0x00, 0xa0, 0x00, 0xad, 0x00, 0xb6, 0x00, 0xbc, 0x00, + 0xc1, 0x00, 0xc4, 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, + 0xcd, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd2, 0x00, + 0xd2, 0x00, 0xd3, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x25, 0x05, 0x6b, 0x00, + 0x8e, 0x00, 0xa4, 0x00, 0xb0, 0x00, 0xb9, 0x00, 0xbe, 0x00, 0xc2, 0x00, + 0xc5, 0x00, 0xc8, 0x00, 0xca, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xce, 0x00, + 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, + 0x81, 0x02, 0xa4, 0x00, 0xb5, 0x00, 0xc0, 0x00, 0xc6, 0x00, 0xcb, 0x00, + 0xcd, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, + 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, + 0xd7, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x09, 0x72, 0x02, + 0x87, 0x00, 0x96, 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb0, 0x00, 0xb5, 0x00, + 0xb9, 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, + 0xc7, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x23, 0x0d, 0x51, 0x01, 0x73, 0x00, 0x89, 0x00, + 0x9a, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbb, 0x00, + 0xbe, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc8, 0x00, + 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0x80, 0x06, 0x97, 0x00, + 0xa8, 0x00, 0xb3, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc7, 0x00, + 0xca, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd1, 0x00, + 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4f, 0x11, 0x65, 0x07, 0x76, 0x03, 0x84, 0x01, + 0x8f, 0x00, 0x99, 0x00, 0xa1, 0x00, 0xa7, 0x00, 0xad, 0x00, 0xb1, 0x00, + 0xb4, 0x00, 0xb8, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc0, 0x00, + 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x12, 0x45, 0x04, 0x60, 0x00, 0x77, 0x00, 0x87, 0x00, 0x94, 0x00, + 0x9d, 0x00, 0xa4, 0x00, 0xaa, 0x00, 0xaf, 0x00, 0xb3, 0x00, 0xb7, 0x00, + 0xba, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc3, 0x00, + 0xc4, 0x00, 0xc6, 0x00, 0x7f, 0x09, 0x91, 0x02, 0x9e, 0x00, 0xaa, 0x00, + 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc6, 0x00, + 0xc8, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, + 0xcf, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4b, 0x17, 0x5d, 0x0c, 0x6c, 0x06, 0x78, 0x03, 0x83, 0x01, 0x8c, 0x00, + 0x94, 0x00, 0x9b, 0x00, 0xa1, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xae, 0x00, + 0xb1, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, 0x00, + 0xbe, 0x00, 0xbf, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x15, 0x3d, 0x08, + 0x55, 0x02, 0x67, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x98, 0x00, + 0x9f, 0x00, 0xa5, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xb1, 0x00, 0xb4, 0x00, + 0xb6, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, + 0x7f, 0x0a, 0x8d, 0x04, 0x99, 0x01, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, + 0xb6, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc3, 0x00, 0xc5, 0x00, + 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcc, 0x00, + 0xcd, 0x00, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x1c, 0x57, 0x11, + 0x64, 0x0a, 0x70, 0x06, 0x79, 0x03, 0x82, 0x02, 0x8a, 0x00, 0x91, 0x00, + 0x97, 0x00, 0x9c, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xad, 0x00, + 0xaf, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x17, 0x38, 0x0b, 0x4c, 0x04, 0x5e, 0x01, + 0x6c, 0x00, 0x7a, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x95, 0x00, 0x9b, 0x00, + 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, + 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0x7f, 0x0b, 0x8a, 0x05, + 0x94, 0x02, 0x9d, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb5, 0x00, + 0xb9, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, + 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x20, 0x54, 0x15, 0x5f, 0x0e, 0x69, 0x09, + 0x72, 0x05, 0x7a, 0x03, 0x82, 0x02, 0x88, 0x01, 0x8f, 0x00, 0x94, 0x00, + 0x99, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa8, 0x00, 0xac, 0x00, + 0xae, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x18, 0x35, 0x0d, 0x47, 0x06, 0x55, 0x03, 0x64, 0x01, 0x70, 0x00, + 0x7a, 0x00, 0x83, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, + 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, + 0xb2, 0x00, 0xb4, 0x00, 0x7f, 0x0c, 0x89, 0x06, 0x92, 0x03, 0x99, 0x01, + 0xa0, 0x00, 0xa6, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb4, 0x00, 0xb7, 0x00, + 0xba, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, + 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x23, 0x50, 0x18, 0x5b, 0x11, 0x64, 0x0b, 0x6c, 0x08, 0x74, 0x05, + 0x7b, 0x03, 0x82, 0x02, 0x87, 0x01, 0x8c, 0x00, 0x91, 0x00, 0x95, 0x00, + 0x9a, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xab, 0x00, + 0xac, 0x00, 0xaf, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1a, 0x32, 0x0f, + 0x42, 0x08, 0x50, 0x04, 0x5c, 0x02, 0x68, 0x00, 0x72, 0x00, 0x7b, 0x00, + 0x83, 0x00, 0x8a, 0x00, 0x90, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9e, 0x00, + 0xa1, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0x7f, 0x0d, 0x88, 0x07, 0x8f, 0x04, 0x96, 0x02, 0x9c, 0x01, 0xa2, 0x00, + 0xa7, 0x00, 0xac, 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb9, 0x00, + 0xbb, 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, + 0xc5, 0x00, 0xc6, 0x00, 0x2f, 0x00, 0x5f, 0x00, 0x99, 0x00, 0xac, 0x00, + 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, + 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0x3b, 0x0b, 0x6f, 0x00, 0x9f, 0x00, 0xaf, 0x00, 0xb5, 0x00, 0xb9, 0x00, + 0xba, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, + 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x6d, 0x00, 0x8d, 0x00, 0x9b, 0x00, + 0xa6, 0x00, 0xb2, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, + 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0x47, 0x07, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, + 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, + 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x45, 0x25, 0x4e, 0x1b, + 0x57, 0x13, 0x60, 0x0e, 0x68, 0x0a, 0x6f, 0x07, 0x76, 0x05, 0x7b, 0x03, + 0x82, 0x02, 0x86, 0x02, 0x8b, 0x01, 0x90, 0x00, 0x93, 0x00, 0x97, 0x00, + 0x9b, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1b, 0x30, 0x11, 0x3e, 0x0a, 0x4b, 0x05, + 0x57, 0x03, 0x61, 0x01, 0x6b, 0x00, 0x73, 0x00, 0x7c, 0x00, 0x82, 0x00, + 0x88, 0x00, 0x8f, 0x00, 0x93, 0x00, 0x97, 0x00, 0x9c, 0x00, 0x9f, 0x00, + 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0x7f, 0x0d, 0x86, 0x08, + 0x8d, 0x05, 0x94, 0x02, 0x9a, 0x01, 0x9f, 0x00, 0xa4, 0x00, 0xa8, 0x00, + 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, + 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, + 0x00, 0x00, 0x0f, 0x00, 0x5f, 0x00, 0x8b, 0x00, 0x9f, 0x00, 0xaa, 0x00, + 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x07, 0x17, 0x27, 0x02, + 0x6f, 0x00, 0x93, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb5, 0x00, + 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, + 0xbc, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x54, 0x00, 0x6b, 0x00, 0x87, 0x00, 0x98, 0x00, 0xa6, 0x00, + 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x0f, 0x0f, 0x3f, 0x00, + 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, 0x00, + 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, + 0xbd, 0x00, 0xbd, 0x00, 0x45, 0x28, 0x4d, 0x1d, 0x55, 0x16, 0x5d, 0x11, + 0x64, 0x0c, 0x6b, 0x09, 0x71, 0x07, 0x77, 0x05, 0x7b, 0x03, 0x81, 0x02, + 0x85, 0x02, 0x8a, 0x01, 0x8e, 0x00, 0x92, 0x00, 0x95, 0x00, 0x98, 0x00, + 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1b, 0x2f, 0x12, 0x3b, 0x0c, 0x47, 0x07, 0x51, 0x04, 0x5c, 0x02, + 0x65, 0x01, 0x6e, 0x00, 0x75, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, + 0x8d, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9c, 0x00, 0xa0, 0x00, + 0xa2, 0x00, 0xa5, 0x00, 0x7f, 0x0d, 0x86, 0x09, 0x8c, 0x06, 0x92, 0x03, + 0x97, 0x02, 0x9c, 0x01, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xac, 0x00, + 0xaf, 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, + 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x44, 0x00, 0x6d, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, + 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, + 0xb6, 0x00, 0xb7, 0x00, 0x00, 0x2c, 0x02, 0x12, 0x22, 0x00, 0x58, 0x00, + 0x7a, 0x00, 0x8f, 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xaa, 0x00, 0xae, 0x00, + 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x6b, 0x00, + 0x28, 0x00, 0x52, 0x00, 0x70, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, + 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, + 0xb6, 0x00, 0xb7, 0x00, 0x00, 0x26, 0x05, 0x05, 0x3f, 0x00, 0x6d, 0x00, + 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, + 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0x44, 0x2a, 0x4b, 0x20, 0x53, 0x18, 0x5a, 0x13, 0x61, 0x0f, 0x67, 0x0b, + 0x6d, 0x09, 0x73, 0x07, 0x77, 0x05, 0x7c, 0x03, 0x81, 0x02, 0x85, 0x02, + 0x88, 0x01, 0x8d, 0x01, 0x90, 0x00, 0x93, 0x00, 0x96, 0x00, 0x9a, 0x00, + 0x9d, 0x00, 0x9f, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1c, 0x2d, 0x13, + 0x39, 0x0d, 0x44, 0x09, 0x4e, 0x05, 0x57, 0x04, 0x60, 0x02, 0x67, 0x01, + 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8c, 0x00, + 0x90, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0xa0, 0x00, + 0x7f, 0x0e, 0x85, 0x09, 0x8b, 0x06, 0x90, 0x04, 0x95, 0x02, 0x9a, 0x02, + 0x9e, 0x01, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0xb2, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, + 0xbd, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x33, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, + 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0x00, 0x36, 0x00, 0x25, 0x09, 0x0b, 0x21, 0x00, 0x4a, 0x00, 0x68, 0x00, + 0x7d, 0x00, 0x8b, 0x00, 0x96, 0x00, 0x9d, 0x00, 0xa3, 0x00, 0xa8, 0x00, + 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x87, 0x00, 0x52, 0x00, 0x11, 0x00, + 0x39, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, + 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0x00, 0x33, 0x00, 0x1c, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, + 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, + 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0x43, 0x2b, 0x4a, 0x22, + 0x51, 0x1a, 0x57, 0x15, 0x5e, 0x11, 0x63, 0x0d, 0x69, 0x0a, 0x6e, 0x08, + 0x74, 0x06, 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, + 0x8c, 0x01, 0x8f, 0x00, 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9b, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1c, 0x2d, 0x14, 0x37, 0x0e, 0x41, 0x0a, + 0x4a, 0x07, 0x53, 0x04, 0x5b, 0x02, 0x63, 0x01, 0x69, 0x00, 0x70, 0x00, + 0x77, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8a, 0x00, 0x8f, 0x00, + 0x92, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x7f, 0x0e, 0x85, 0x0a, + 0x8a, 0x07, 0x8f, 0x05, 0x93, 0x03, 0x98, 0x02, 0x9c, 0x01, 0xa0, 0x00, + 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, 0x00, + 0xb3, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x28, 0x00, + 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, + 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x3a, 0x00, 0x2f, + 0x00, 0x16, 0x0e, 0x08, 0x20, 0x00, 0x42, 0x00, 0x5c, 0x00, 0x6f, 0x00, + 0x7e, 0x00, 0x89, 0x00, 0x92, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa2, 0x00, + 0xa6, 0x00, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa6, 0x00, 0x98, 0x00, 0x70, 0x00, 0x39, 0x00, 0x02, 0x00, 0x28, 0x00, + 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, + 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x38, 0x00, 0x2a, + 0x00, 0x09, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, 0x00, + 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, + 0xab, 0x00, 0xad, 0x00, 0x43, 0x2c, 0x49, 0x23, 0x50, 0x1c, 0x56, 0x17, + 0x5b, 0x12, 0x61, 0x0f, 0x66, 0x0c, 0x6b, 0x0a, 0x6f, 0x08, 0x75, 0x06, + 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x8b, 0x01, + 0x8e, 0x01, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x1c, 0x2c, 0x15, 0x36, 0x0f, 0x3e, 0x0b, 0x48, 0x07, 0x50, 0x05, + 0x58, 0x04, 0x5f, 0x02, 0x66, 0x01, 0x6c, 0x00, 0x71, 0x00, 0x77, 0x00, + 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, + 0x94, 0x00, 0x97, 0x00, 0x7f, 0x0e, 0x84, 0x0a, 0x89, 0x07, 0x8d, 0x05, + 0x92, 0x03, 0x96, 0x02, 0x9a, 0x02, 0x9e, 0x01, 0xa1, 0x00, 0xa4, 0x00, + 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, + 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x3d, 0x00, + 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, + 0x93, 0x00, 0x98, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x02, 0x0e, + 0x12, 0x06, 0x20, 0x00, 0x3c, 0x00, 0x52, 0x00, 0x64, 0x00, 0x73, 0x00, + 0x7e, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x00, 0xa6, 0x00, + 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, + 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, + 0x93, 0x00, 0x98, 0x00, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x05, 0x00, + 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, 0x00, + 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, + 0x43, 0x2e, 0x48, 0x25, 0x4e, 0x1e, 0x54, 0x19, 0x59, 0x14, 0x5f, 0x11, + 0x63, 0x0e, 0x69, 0x0b, 0x6c, 0x09, 0x71, 0x07, 0x76, 0x06, 0x78, 0x05, + 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x86, 0x02, 0x8a, 0x01, 0x8e, 0x01, + 0x90, 0x00, 0x92, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1d, 0x2b, 0x16, + 0x34, 0x10, 0x3d, 0x0c, 0x45, 0x09, 0x4c, 0x06, 0x54, 0x04, 0x5b, 0x02, + 0x61, 0x02, 0x67, 0x01, 0x6d, 0x00, 0x73, 0x00, 0x78, 0x00, 0x7d, 0x00, + 0x81, 0x00, 0x86, 0x00, 0x89, 0x00, 0x8d, 0x00, 0x90, 0x00, 0x93, 0x00, + 0x7f, 0x0e, 0x84, 0x0b, 0x88, 0x08, 0x8d, 0x06, 0x91, 0x04, 0x94, 0x03, + 0x98, 0x02, 0x9c, 0x01, 0x9f, 0x01, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, + 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, + 0xb6, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, + 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, + 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x18, 0x07, 0x0c, 0x14, 0x05, + 0x20, 0x00, 0x37, 0x00, 0x4c, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x75, 0x00, + 0x7e, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, + 0x48, 0x00, 0x21, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, + 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, + 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x0f, 0x00, 0x29, 0x00, + 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, + 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, 0x00, 0x42, 0x2f, 0x48, 0x26, + 0x4d, 0x20, 0x52, 0x1a, 0x58, 0x16, 0x5c, 0x12, 0x61, 0x0f, 0x66, 0x0c, + 0x6b, 0x0b, 0x6d, 0x09, 0x73, 0x07, 0x76, 0x06, 0x79, 0x05, 0x7d, 0x04, + 0x81, 0x03, 0x83, 0x02, 0x86, 0x02, 0x89, 0x02, 0x8d, 0x01, 0x8f, 0x01, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1d, 0x2a, 0x16, 0x33, 0x11, 0x3b, 0x0c, + 0x43, 0x0a, 0x4a, 0x07, 0x51, 0x05, 0x58, 0x04, 0x5e, 0x02, 0x64, 0x01, + 0x69, 0x01, 0x6f, 0x00, 0x74, 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, + 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, 0x8f, 0x00, 0x7f, 0x0e, 0x83, 0x0b, + 0x88, 0x08, 0x8c, 0x06, 0x90, 0x05, 0x93, 0x03, 0x97, 0x02, 0x9a, 0x02, + 0x9d, 0x01, 0xa0, 0x00, 0xa3, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, + 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, + 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x3d, 0x00, 0x3a, + 0x00, 0x2f, 0x00, 0x20, 0x00, 0x10, 0x0b, 0x0a, 0x16, 0x04, 0x20, 0x00, + 0x34, 0x00, 0x46, 0x00, 0x56, 0x00, 0x62, 0x00, 0x6d, 0x00, 0x77, 0x00, + 0x7f, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, + 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x3d, 0x00, 0x38, + 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, 0x00, + 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, + 0x8b, 0x00, 0x91, 0x00, 0x42, 0x30, 0x47, 0x28, 0x4c, 0x21, 0x51, 0x1c, + 0x56, 0x17, 0x5a, 0x14, 0x60, 0x11, 0x63, 0x0e, 0x68, 0x0c, 0x6c, 0x0a, + 0x6f, 0x08, 0x74, 0x07, 0x77, 0x06, 0x79, 0x05, 0x7d, 0x04, 0x81, 0x03, + 0x83, 0x02, 0x85, 0x02, 0x88, 0x02, 0x8c, 0x01, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1e, 0x2a, 0x17, 0x32, 0x12, 0x39, 0x0e, 0x40, 0x0a, 0x48, 0x07, + 0x4f, 0x05, 0x55, 0x04, 0x5a, 0x03, 0x60, 0x02, 0x66, 0x01, 0x6b, 0x00, + 0x70, 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, + 0x88, 0x00, 0x8b, 0x00, 0x7f, 0x0f, 0x83, 0x0b, 0x87, 0x09, 0x8b, 0x07, + 0x8e, 0x05, 0x92, 0x03, 0x96, 0x02, 0x99, 0x02, 0x9b, 0x01, 0x9e, 0x01, + 0xa1, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, + 0xaf, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, + 0x60, 0x00, 0x6a, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x33, 0x00, 0x27, + 0x00, 0x18, 0x04, 0x0d, 0x0e, 0x08, 0x17, 0x04, 0x20, 0x00, 0x32, 0x00, + 0x42, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x67, 0x00, 0x70, 0x00, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, 0x00, + 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, + 0x60, 0x00, 0x6a, 0x00, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, + 0x00, 0x0b, 0x08, 0x00, 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, 0x00, + 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, 0x00, + 0x42, 0x31, 0x47, 0x29, 0x4b, 0x22, 0x50, 0x1d, 0x55, 0x19, 0x58, 0x15, + 0x5e, 0x12, 0x61, 0x0f, 0x65, 0x0d, 0x6a, 0x0b, 0x6c, 0x09, 0x70, 0x08, + 0x75, 0x07, 0x77, 0x06, 0x79, 0x05, 0x7d, 0x04, 0x81, 0x03, 0x83, 0x02, + 0x85, 0x02, 0x87, 0x02, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x29, 0x18, + 0x31, 0x12, 0x38, 0x0f, 0x3f, 0x0c, 0x45, 0x09, 0x4c, 0x07, 0x52, 0x05, + 0x58, 0x04, 0x5d, 0x02, 0x63, 0x02, 0x67, 0x01, 0x6c, 0x00, 0x71, 0x00, + 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, + 0x7f, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8a, 0x07, 0x8e, 0x06, 0x91, 0x04, + 0x94, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x01, 0xa0, 0x01, 0xa2, 0x00, + 0xa4, 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0xb0, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x12, + 0x07, 0x0c, 0x10, 0x07, 0x18, 0x03, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4c, 0x00, 0x57, 0x00, 0x61, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, + 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, + 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, + 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, + 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x42, 0x31, 0x46, 0x2a, + 0x4a, 0x24, 0x4f, 0x1f, 0x53, 0x1a, 0x58, 0x16, 0x5c, 0x13, 0x60, 0x11, + 0x63, 0x0e, 0x67, 0x0c, 0x6b, 0x0b, 0x6d, 0x09, 0x72, 0x07, 0x75, 0x07, + 0x77, 0x06, 0x79, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x29, 0x18, 0x30, 0x13, 0x37, 0x0f, + 0x3d, 0x0c, 0x44, 0x0a, 0x4a, 0x07, 0x50, 0x05, 0x55, 0x04, 0x5b, 0x04, + 0x60, 0x02, 0x64, 0x01, 0x69, 0x01, 0x6d, 0x00, 0x72, 0x00, 0x76, 0x00, + 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, 0x00, 0x7f, 0x0f, 0x83, 0x0c, + 0x86, 0x09, 0x8a, 0x07, 0x8d, 0x06, 0x90, 0x05, 0x93, 0x03, 0x96, 0x02, + 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, 0x00, 0xa3, 0x00, 0xa5, 0x00, + 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x37, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x02, 0x0e, 0x0a, 0x0a, + 0x11, 0x06, 0x19, 0x03, 0x1f, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, + 0x53, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, + 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x04, 0x00, 0x14, 0x00, + 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, + 0x68, 0x00, 0x70, 0x00, 0x42, 0x32, 0x46, 0x2b, 0x49, 0x25, 0x4f, 0x1f, + 0x52, 0x1b, 0x57, 0x18, 0x59, 0x14, 0x5f, 0x12, 0x62, 0x0f, 0x65, 0x0d, + 0x6a, 0x0c, 0x6c, 0x0a, 0x6e, 0x09, 0x73, 0x07, 0x76, 0x06, 0x78, 0x06, + 0x7a, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1e, 0x28, 0x19, 0x2f, 0x14, 0x35, 0x0f, 0x3c, 0x0c, 0x42, 0x0a, + 0x48, 0x07, 0x4e, 0x06, 0x53, 0x05, 0x58, 0x04, 0x5d, 0x02, 0x62, 0x02, + 0x66, 0x01, 0x6b, 0x01, 0x6f, 0x00, 0x73, 0x00, 0x76, 0x00, 0x7a, 0x00, + 0x7d, 0x00, 0x81, 0x00, 0x7f, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x07, + 0x8c, 0x06, 0x8f, 0x05, 0x92, 0x03, 0x95, 0x03, 0x98, 0x02, 0x9a, 0x02, + 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, + 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2d, 0x00, 0x39, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x31, + 0x00, 0x28, 0x00, 0x1e, 0x00, 0x13, 0x05, 0x0d, 0x0c, 0x09, 0x13, 0x06, + 0x19, 0x03, 0x1f, 0x00, 0x2d, 0x00, 0x3a, 0x00, 0x45, 0x00, 0x4f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, + 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, + 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2d, 0x00, 0x39, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, + 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x0a, 0x00, 0x18, 0x00, 0x26, 0x00, + 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, 0x00, + 0x42, 0x33, 0x45, 0x2c, 0x48, 0x26, 0x4e, 0x21, 0x50, 0x1c, 0x56, 0x19, + 0x58, 0x16, 0x5c, 0x13, 0x60, 0x11, 0x62, 0x0e, 0x67, 0x0c, 0x6a, 0x0b, + 0x6d, 0x0a, 0x70, 0x09, 0x74, 0x07, 0x76, 0x06, 0x78, 0x06, 0x7a, 0x05, + 0x7e, 0x04, 0x81, 0x03, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x28, 0x19, + 0x2f, 0x15, 0x35, 0x11, 0x3b, 0x0d, 0x41, 0x0b, 0x46, 0x09, 0x4c, 0x07, + 0x51, 0x05, 0x55, 0x04, 0x5b, 0x04, 0x5f, 0x02, 0x63, 0x02, 0x68, 0x01, + 0x6b, 0x00, 0x70, 0x00, 0x73, 0x00, 0x77, 0x00, 0x7b, 0x00, 0x7d, 0x00, + 0x7f, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x08, 0x8c, 0x06, 0x8f, 0x05, + 0x91, 0x04, 0x94, 0x03, 0x97, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, + 0xa0, 0x01, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, + 0xac, 0x00, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x23, + 0x00, 0x19, 0x00, 0x0f, 0x07, 0x0c, 0x0e, 0x08, 0x14, 0x05, 0x1a, 0x02, + 0x1f, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, + 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, + 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, + 0x00, 0x0c, 0x01, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, + 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x00, 0x5d, 0x1a, 0x6d, 0x16, + 0x72, 0x15, 0x75, 0x14, 0x77, 0x13, 0x78, 0x13, 0x79, 0x13, 0x7a, 0x12, + 0x7a, 0x12, 0x7b, 0x12, 0x7b, 0x12, 0x7b, 0x12, 0x7c, 0x12, 0x7c, 0x11, + 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7d, 0x11, + 0xbd, 0x02, 0xa4, 0x06, 0x97, 0x09, 0x91, 0x0a, 0x8d, 0x0b, 0x8a, 0x0c, + 0x89, 0x0d, 0x88, 0x0d, 0x86, 0x0d, 0x86, 0x0e, 0x85, 0x0e, 0x85, 0x0e, + 0x84, 0x0e, 0x84, 0x0e, 0x83, 0x0f, 0x83, 0x0f, 0x83, 0x0f, 0x83, 0x0f, + 0x82, 0x0f, 0x82, 0x0f, 0x33, 0x11, 0x5f, 0x11, 0x6f, 0x11, 0x74, 0x11, + 0x77, 0x11, 0x78, 0x11, 0x7a, 0x11, 0x7a, 0x11, 0x7b, 0x11, 0x7b, 0x11, + 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7d, 0x11, 0x7d, 0x11, + 0x7d, 0x11, 0x7d, 0x11, 0x7d, 0x11, 0x7d, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1d, 0x00, 0x14, + 0x03, 0x0e, 0x09, 0x0b, 0x0f, 0x08, 0x15, 0x05, 0x1a, 0x02, 0x1f, 0x00, + 0x2b, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, + 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x3f, 0x00, 0x3d, + 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, + 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, + 0x49, 0x00, 0x51, 0x00, 0x50, 0x24, 0x5f, 0x1f, 0x66, 0x1b, 0x6b, 0x19, + 0x6e, 0x18, 0x70, 0x17, 0x73, 0x16, 0x73, 0x15, 0x75, 0x15, 0x76, 0x15, + 0x76, 0x15, 0x77, 0x14, 0x77, 0x14, 0x78, 0x13, 0x78, 0x13, 0x79, 0x13, + 0x79, 0x13, 0x7a, 0x13, 0x7a, 0x13, 0x7b, 0x13, 0xcc, 0x00, 0xb5, 0x00, + 0xa8, 0x02, 0x9e, 0x04, 0x99, 0x05, 0x94, 0x06, 0x92, 0x07, 0x8f, 0x08, + 0x8d, 0x09, 0x8c, 0x09, 0x8b, 0x0a, 0x8a, 0x0a, 0x89, 0x0b, 0x88, 0x0b, + 0x88, 0x0b, 0x87, 0x0c, 0x87, 0x0c, 0x86, 0x0c, 0x86, 0x0c, 0x86, 0x0c, + 0x23, 0x13, 0x46, 0x11, 0x58, 0x11, 0x63, 0x11, 0x69, 0x11, 0x6d, 0x11, + 0x70, 0x11, 0x72, 0x11, 0x73, 0x11, 0x75, 0x11, 0x76, 0x11, 0x77, 0x11, + 0x77, 0x11, 0x78, 0x11, 0x78, 0x11, 0x79, 0x11, 0x79, 0x11, 0x79, 0x11, + 0x7a, 0x11, 0x7a, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, + 0x00, 0x30, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x10, 0x05, 0x0d, + 0x0b, 0x0a, 0x10, 0x07, 0x16, 0x04, 0x1b, 0x02, 0x1f, 0x00, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, + 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, + 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, + 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, + 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x00, + 0x4a, 0x2a, 0x57, 0x24, 0x5f, 0x20, 0x64, 0x1e, 0x68, 0x1c, 0x6a, 0x1b, + 0x6d, 0x19, 0x6e, 0x18, 0x70, 0x18, 0x72, 0x17, 0x72, 0x17, 0x73, 0x16, + 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, + 0x76, 0x15, 0x77, 0x14, 0xd1, 0x00, 0xc0, 0x00, 0xb3, 0x00, 0xaa, 0x01, + 0xa2, 0x02, 0x9d, 0x03, 0x99, 0x04, 0x96, 0x05, 0x94, 0x06, 0x92, 0x06, + 0x90, 0x07, 0x8f, 0x07, 0x8d, 0x08, 0x8d, 0x08, 0x8c, 0x09, 0x8b, 0x09, + 0x8a, 0x09, 0x8a, 0x0a, 0x89, 0x0a, 0x89, 0x0b, 0x22, 0x17, 0x39, 0x11, + 0x4a, 0x11, 0x55, 0x11, 0x5e, 0x11, 0x63, 0x11, 0x67, 0x11, 0x6a, 0x11, + 0x6c, 0x11, 0x6e, 0x11, 0x70, 0x11, 0x71, 0x11, 0x72, 0x11, 0x73, 0x11, + 0x74, 0x11, 0x75, 0x11, 0x75, 0x11, 0x76, 0x11, 0x76, 0x11, 0x77, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, 0x32, 0x00, 0x2c, + 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x01, 0x0e, 0x07, 0x0c, 0x0c, 0x09, + 0x11, 0x06, 0x16, 0x04, 0x1b, 0x02, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, + 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, + 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, + 0x00, 0x1c, 0x00, 0x11, 0x00, 0x07, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, + 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x2e, 0x53, 0x28, + 0x5a, 0x24, 0x5f, 0x22, 0x63, 0x1f, 0x66, 0x1e, 0x69, 0x1c, 0x6a, 0x1b, + 0x6c, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x70, 0x18, 0x71, 0x18, 0x72, 0x18, + 0x72, 0x17, 0x72, 0x17, 0x73, 0x16, 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, + 0xd5, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x01, + 0xa0, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x97, 0x04, 0x95, 0x05, 0x93, 0x05, + 0x92, 0x06, 0x91, 0x06, 0x90, 0x07, 0x8e, 0x07, 0x8e, 0x07, 0x8d, 0x07, + 0x8c, 0x08, 0x8c, 0x09, 0x22, 0x1a, 0x33, 0x13, 0x41, 0x11, 0x4c, 0x11, + 0x54, 0x11, 0x5b, 0x11, 0x5f, 0x11, 0x63, 0x11, 0x66, 0x11, 0x68, 0x11, + 0x6a, 0x11, 0x6c, 0x11, 0x6e, 0x11, 0x6f, 0x11, 0x70, 0x11, 0x71, 0x11, + 0x72, 0x11, 0x72, 0x11, 0x73, 0x11, 0x74, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x07, 0x7f, 0x00, + 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, + 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x07, 0x0f, 0x0f, 0x00, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, + 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x31, 0x50, 0x2b, 0x56, 0x27, 0x5b, 0x24, + 0x5f, 0x22, 0x62, 0x20, 0x65, 0x1f, 0x67, 0x1d, 0x69, 0x1d, 0x6a, 0x1b, + 0x6c, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x6f, 0x18, 0x71, 0x18, + 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x72, 0x17, 0xd6, 0x00, 0xcb, 0x00, + 0xc0, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xab, 0x00, 0xa6, 0x01, 0xa2, 0x01, + 0x9f, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x98, 0x03, 0x96, 0x04, 0x94, 0x05, + 0x93, 0x05, 0x92, 0x06, 0x91, 0x06, 0x90, 0x06, 0x8f, 0x06, 0x8f, 0x07, + 0x22, 0x1b, 0x2f, 0x15, 0x3b, 0x12, 0x44, 0x11, 0x4d, 0x11, 0x53, 0x11, + 0x59, 0x11, 0x5d, 0x11, 0x60, 0x11, 0x63, 0x11, 0x66, 0x11, 0x67, 0x11, + 0x69, 0x11, 0x6b, 0x11, 0x6c, 0x11, 0x6d, 0x11, 0x6e, 0x11, 0x6f, 0x11, + 0x70, 0x11, 0x71, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, + 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0x00, + 0x05, 0x05, 0x00, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, + 0x00, 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, + 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x33, 0x4d, 0x2e, 0x53, 0x29, 0x58, 0x26, 0x5c, 0x24, 0x5f, 0x22, + 0x61, 0x21, 0x65, 0x20, 0x65, 0x1e, 0x68, 0x1d, 0x69, 0x1d, 0x6a, 0x1b, + 0x6b, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x6f, 0x18, + 0x71, 0x18, 0x72, 0x18, 0xd7, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbd, 0x00, + 0xb6, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa7, 0x00, 0xa4, 0x01, 0xa1, 0x02, + 0x9e, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x98, 0x03, 0x97, 0x03, 0x96, 0x04, + 0x94, 0x05, 0x93, 0x05, 0x92, 0x05, 0x91, 0x06, 0x21, 0x1c, 0x2d, 0x16, + 0x37, 0x13, 0x40, 0x11, 0x47, 0x11, 0x4e, 0x11, 0x53, 0x11, 0x57, 0x11, + 0x5b, 0x11, 0x5e, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x67, 0x11, + 0x68, 0x11, 0x6a, 0x11, 0x6b, 0x11, 0x6c, 0x11, 0x6d, 0x11, 0x6e, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x26, 0x05, 0x05, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, + 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, + 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x00, + 0x00, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, + 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x34, 0x4c, 0x2f, + 0x51, 0x2c, 0x56, 0x28, 0x5a, 0x26, 0x5c, 0x24, 0x60, 0x23, 0x61, 0x21, + 0x64, 0x20, 0x65, 0x1f, 0x66, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, + 0x6b, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, + 0xd8, 0x00, 0xcf, 0x00, 0xc7, 0x00, 0xc0, 0x00, 0xba, 0x00, 0xb5, 0x00, + 0xb0, 0x00, 0xac, 0x00, 0xa8, 0x00, 0xa5, 0x01, 0xa2, 0x01, 0xa0, 0x02, + 0x9e, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x03, 0x97, 0x03, 0x96, 0x03, + 0x95, 0x04, 0x94, 0x05, 0x22, 0x1d, 0x2b, 0x17, 0x34, 0x14, 0x3b, 0x12, + 0x43, 0x11, 0x49, 0x11, 0x4e, 0x11, 0x52, 0x11, 0x56, 0x11, 0x5a, 0x11, + 0x5d, 0x11, 0x5f, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, + 0x68, 0x11, 0x69, 0x11, 0x6a, 0x11, 0x6b, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1c, + 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, 0x00, + 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, + 0xb3, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x00, + 0x00, 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, + 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x36, 0x4a, 0x31, 0x50, 0x2d, 0x54, 0x2a, + 0x57, 0x28, 0x5b, 0x26, 0x5c, 0x24, 0x60, 0x23, 0x61, 0x21, 0x63, 0x20, + 0x65, 0x20, 0x65, 0x1e, 0x67, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, + 0x6a, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0xd9, 0x00, 0xd1, 0x00, + 0xca, 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb4, 0x00, 0xb0, 0x00, + 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0x9f, 0x02, + 0x9d, 0x02, 0x9b, 0x02, 0x9a, 0x02, 0x99, 0x03, 0x98, 0x03, 0x97, 0x03, + 0x21, 0x1e, 0x2a, 0x18, 0x32, 0x15, 0x39, 0x13, 0x3f, 0x12, 0x45, 0x11, + 0x4a, 0x11, 0x4e, 0x11, 0x52, 0x11, 0x56, 0x11, 0x59, 0x11, 0x5c, 0x11, + 0x5d, 0x11, 0x60, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, + 0x67, 0x11, 0x68, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x09, 0x1d, 0x00, + 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, 0x00, 0x8b, 0x00, 0x94, 0x00, + 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, 0x00, + 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, + 0x00, 0x2b, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x36, 0x49, 0x32, 0x4e, 0x2f, 0x52, 0x2c, 0x56, 0x29, 0x58, 0x27, + 0x5b, 0x26, 0x5d, 0x24, 0x60, 0x23, 0x60, 0x22, 0x62, 0x20, 0x65, 0x20, + 0x65, 0x1f, 0x66, 0x1e, 0x68, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, + 0x6a, 0x1b, 0x6c, 0x1a, 0xd9, 0x00, 0xd2, 0x00, 0xcc, 0x00, 0xc6, 0x00, + 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb3, 0x00, 0xaf, 0x00, 0xac, 0x00, + 0xa9, 0x00, 0xa6, 0x00, 0xa4, 0x01, 0xa2, 0x01, 0xa0, 0x01, 0x9e, 0x02, + 0x9d, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x02, 0x22, 0x1e, 0x29, 0x19, + 0x30, 0x16, 0x36, 0x13, 0x3c, 0x12, 0x41, 0x11, 0x46, 0x11, 0x4a, 0x11, + 0x4f, 0x11, 0x52, 0x11, 0x55, 0x11, 0x58, 0x11, 0x5a, 0x11, 0x5c, 0x11, + 0x5f, 0x11, 0x60, 0x11, 0x62, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, + 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, + 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, + 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x37, 0x48, 0x33, + 0x4d, 0x30, 0x51, 0x2d, 0x54, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x26, + 0x5d, 0x24, 0x60, 0x23, 0x60, 0x22, 0x62, 0x20, 0x65, 0x20, 0x65, 0x20, + 0x65, 0x1f, 0x66, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, + 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc8, 0x00, 0xc3, 0x00, 0xbe, 0x00, + 0xba, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, + 0xa7, 0x00, 0xa5, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0xa0, 0x02, 0x9e, 0x02, + 0x9d, 0x02, 0x9c, 0x02, 0x21, 0x1e, 0x28, 0x1a, 0x2e, 0x17, 0x34, 0x14, + 0x39, 0x13, 0x3f, 0x12, 0x43, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4f, 0x11, + 0x52, 0x11, 0x55, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5c, 0x11, 0x5d, 0x11, + 0x5f, 0x11, 0x61, 0x11, 0x62, 0x11, 0x63, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, + 0x00, 0x23, 0x00, 0x0b, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, 0x00, + 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, + 0x97, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, + 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x43, 0x38, 0x48, 0x34, 0x4c, 0x31, 0x4f, 0x2e, + 0x53, 0x2c, 0x56, 0x2a, 0x57, 0x28, 0x5a, 0x27, 0x5c, 0x26, 0x5d, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, 0x65, 0x20, + 0x65, 0x1e, 0x67, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0xd9, 0x00, 0xd4, 0x00, + 0xce, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xb9, 0x00, + 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, + 0xa6, 0x00, 0xa4, 0x01, 0xa2, 0x01, 0xa0, 0x01, 0x9f, 0x02, 0x9e, 0x02, + 0x22, 0x1f, 0x27, 0x1a, 0x2d, 0x17, 0x33, 0x15, 0x38, 0x13, 0x3c, 0x13, + 0x41, 0x12, 0x44, 0x11, 0x48, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, + 0x54, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5b, 0x11, 0x5c, 0x11, 0x5e, 0x11, + 0x5f, 0x11, 0x61, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, + 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, 0x00, 0x50, 0x00, 0x5e, 0x00, + 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb7, 0x00, + 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, 0x00, + 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, + 0x00, 0x0c, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x38, 0x47, 0x35, 0x4b, 0x32, 0x4e, 0x2f, 0x52, 0x2d, 0x53, 0x2b, + 0x57, 0x2a, 0x58, 0x27, 0x5b, 0x27, 0x5c, 0x25, 0x5d, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, + 0x66, 0x1e, 0x68, 0x1d, 0xda, 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcb, 0x00, + 0xc7, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb5, 0x00, + 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, + 0xa4, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0xa0, 0x01, 0x21, 0x1f, 0x27, 0x1b, + 0x2c, 0x18, 0x31, 0x16, 0x36, 0x14, 0x3a, 0x13, 0x3e, 0x12, 0x42, 0x11, + 0x45, 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, + 0x56, 0x11, 0x58, 0x11, 0x5a, 0x11, 0x5c, 0x11, 0x5d, 0x11, 0x5e, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x08, 0x00, + 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, + 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, + 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, 0x00, + 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x46, 0x35, + 0x4a, 0x32, 0x4e, 0x30, 0x50, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x29, + 0x58, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x22, 0x63, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, + 0xda, 0x00, 0xd5, 0x00, 0xd1, 0x00, 0xcc, 0x00, 0xc8, 0x00, 0xc4, 0x00, + 0xc1, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb4, 0x00, 0xb2, 0x00, + 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa5, 0x00, + 0xa4, 0x01, 0xa2, 0x01, 0x22, 0x1f, 0x27, 0x1b, 0x2c, 0x18, 0x30, 0x16, + 0x35, 0x14, 0x39, 0x13, 0x3d, 0x13, 0x40, 0x12, 0x44, 0x11, 0x47, 0x11, + 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x56, 0x11, + 0x58, 0x11, 0x59, 0x11, 0x5b, 0x11, 0x5c, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, + 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x0f, 0x00, 0x20, 0x00, + 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, + 0x74, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, + 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, + 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x46, 0x36, 0x4a, 0x33, 0x4d, 0x31, + 0x4f, 0x2e, 0x52, 0x2d, 0x53, 0x2b, 0x57, 0x2a, 0x57, 0x28, 0x5a, 0x27, + 0x5c, 0x27, 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, + 0x62, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0xda, 0x00, 0xd6, 0x00, + 0xd1, 0x00, 0xcd, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc2, 0x00, 0xbf, 0x00, + 0xbc, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, + 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa6, 0x00, 0xa4, 0x00, + 0x21, 0x1f, 0x26, 0x1c, 0x2b, 0x19, 0x2f, 0x17, 0x33, 0x15, 0x37, 0x14, + 0x3b, 0x13, 0x3e, 0x12, 0x41, 0x12, 0x44, 0x11, 0x47, 0x11, 0x4a, 0x11, + 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x55, 0x11, 0x57, 0x11, + 0x59, 0x11, 0x5a, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, + 0x00, 0x1b, 0x00, 0x0c, 0x04, 0x00, 0x14, 0x00, 0x23, 0x00, 0x32, 0x00, + 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, + 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, 0x00, + 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, + 0x16, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x39, 0x45, 0x36, 0x49, 0x34, 0x4c, 0x31, 0x4f, 0x30, 0x51, 0x2e, + 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x28, 0x5b, 0x27, 0x5c, 0x27, + 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, 0x61, 0x21, + 0x64, 0x20, 0x65, 0x20, 0xda, 0x00, 0xd6, 0x00, 0xd2, 0x00, 0xce, 0x00, + 0xcb, 0x00, 0xc7, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, + 0xb8, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, + 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0x22, 0x1f, 0x26, 0x1c, + 0x2a, 0x19, 0x2e, 0x17, 0x32, 0x16, 0x36, 0x14, 0x39, 0x13, 0x3d, 0x13, + 0x40, 0x12, 0x43, 0x11, 0x45, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4d, 0x11, + 0x4f, 0x11, 0x51, 0x11, 0x53, 0x11, 0x55, 0x11, 0x56, 0x11, 0x58, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, + 0x00, 0x04, 0x0a, 0x00, 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, + 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, + 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, 0x00, + 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3a, 0x45, 0x37, + 0x48, 0x35, 0x4a, 0x32, 0x4e, 0x31, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2b, + 0x57, 0x2a, 0x57, 0x29, 0x58, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x25, + 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, + 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xc8, 0x00, + 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb7, 0x00, + 0xb5, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, + 0xa9, 0x00, 0xa8, 0x00, 0x21, 0x20, 0x26, 0x1c, 0x2a, 0x1a, 0x2d, 0x18, + 0x31, 0x16, 0x35, 0x14, 0x38, 0x13, 0x3b, 0x13, 0x3e, 0x12, 0x41, 0x12, + 0x44, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, + 0x51, 0x11, 0x53, 0x11, 0x55, 0x11, 0x56, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, + 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x01, 0x00, + 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, + 0x53, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, + 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, + 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3a, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x33, + 0x4d, 0x31, 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, + 0x57, 0x29, 0x59, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5f, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, 0xdb, 0x00, 0xd7, 0x00, + 0xd3, 0x00, 0xd0, 0x00, 0xcc, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc3, 0x00, + 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb9, 0x00, 0xb7, 0x00, 0xb5, 0x00, + 0xb3, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xaa, 0x00, + 0x21, 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2d, 0x18, 0x30, 0x17, 0x33, 0x15, + 0x37, 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x12, 0x42, 0x12, 0x44, 0x11, + 0x47, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x11, + 0x53, 0x11, 0x55, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, + 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, 0x06, 0x00, 0x13, 0x00, + 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, + 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, 0x00, + 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, + 0x36, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3a, 0x45, 0x38, 0x47, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x30, + 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2b, 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x28, + 0x5a, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5f, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd7, 0x00, 0xd4, 0x00, 0xd0, 0x00, + 0xcd, 0x00, 0xca, 0x00, 0xc7, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, + 0xbd, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, 0x00, + 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xac, 0x00, 0x21, 0x20, 0x25, 0x1d, + 0x29, 0x1a, 0x2c, 0x18, 0x2f, 0x17, 0x33, 0x16, 0x36, 0x14, 0x39, 0x13, + 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x43, 0x11, 0x45, 0x11, 0x47, 0x11, + 0x4a, 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x52, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, + 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, + 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, + 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, 0x00, + 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, + 0x46, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x2e, 0x53, 0x2e, + 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x27, 0x5b, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x24, 0x5f, 0x23, 0x60, 0x23, 0x60, 0x23, + 0xdb, 0x00, 0xd8, 0x00, 0xd4, 0x00, 0xd1, 0x00, 0xce, 0x00, 0xcb, 0x00, + 0xc8, 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbc, 0x00, + 0xba, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, + 0xaf, 0x00, 0xad, 0x00, 0x21, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x18, + 0x2f, 0x17, 0x32, 0x16, 0x35, 0x14, 0x38, 0x14, 0x3a, 0x13, 0x3d, 0x13, + 0x3f, 0x12, 0x42, 0x12, 0x44, 0x11, 0x46, 0x11, 0x48, 0x11, 0x4a, 0x11, + 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, + 0x00, 0x07, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, + 0x36, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, + 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, + 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x36, 0x4a, 0x35, + 0x4a, 0x32, 0x4e, 0x31, 0x4f, 0x2f, 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2b, + 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x26, 0x5c, 0x24, 0x5f, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd8, 0x00, + 0xd5, 0x00, 0xd2, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xca, 0x00, 0xc6, 0x00, + 0xc4, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb9, 0x00, + 0xb8, 0x00, 0xb5, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, + 0x21, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x19, 0x2e, 0x17, 0x31, 0x16, + 0x34, 0x15, 0x37, 0x14, 0x39, 0x13, 0x3b, 0x13, 0x3e, 0x13, 0x40, 0x12, + 0x42, 0x12, 0x45, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4a, 0x11, 0x4c, 0x11, + 0x4e, 0x11, 0x4f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x27, 0x68, 0x32, 0x51, 0x36, 0x4a, 0x38, 0x47, + 0x3a, 0x46, 0x3a, 0x45, 0x3b, 0x44, 0x3c, 0x43, 0x3c, 0x43, 0x3c, 0x43, + 0x3c, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x41, + 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x41, 0x0e, 0x16, 0x27, + 0x23, 0x31, 0x2a, 0x35, 0x2e, 0x38, 0x31, 0x39, 0x33, 0x3a, 0x34, 0x3b, + 0x35, 0x3b, 0x36, 0x3c, 0x37, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x39, 0x3d, + 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3b, 0x3d, + 0x68, 0x00, 0x32, 0x27, 0x36, 0x31, 0x38, 0x35, 0x3a, 0x38, 0x3a, 0x39, + 0x3b, 0x3a, 0x3c, 0x3b, 0x3c, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3c, + 0x3d, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x22, 0x32, 0x28, 0x36, 0x2c, 0x38, 0x2f, 0x3a, 0x32, 0x3a, 0x34, 0x3b, + 0x35, 0x3c, 0x36, 0x3c, 0x37, 0x3c, 0x37, 0x3c, 0x38, 0x3d, 0x39, 0x3d, + 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x00, 0x97, 0x01, 0x67, 0x09, 0x56, 0x11, 0x4f, + 0x17, 0x4b, 0x1c, 0x49, 0x20, 0x47, 0x23, 0x46, 0x25, 0x45, 0x28, 0x44, + 0x2a, 0x44, 0x2b, 0x43, 0x2c, 0x43, 0x2e, 0x43, 0x2f, 0x42, 0x30, 0x42, + 0x31, 0x42, 0x31, 0x42, 0x32, 0x42, 0x33, 0x42, 0x1a, 0x5c, 0x25, 0x4f, + 0x2a, 0x4a, 0x2e, 0x48, 0x31, 0x46, 0x33, 0x45, 0x34, 0x44, 0x36, 0x44, + 0x37, 0x43, 0x37, 0x43, 0x38, 0x43, 0x38, 0x42, 0x39, 0x42, 0x39, 0x42, + 0x39, 0x41, 0x3a, 0x41, 0x3a, 0x41, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x5f, + 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x28, 0x25, 0x2d, + 0x28, 0x30, 0x2b, 0x32, 0x2e, 0x34, 0x2f, 0x35, 0x31, 0x36, 0x32, 0x37, + 0x33, 0x38, 0x34, 0x38, 0x35, 0x39, 0x36, 0x39, 0x36, 0x39, 0x36, 0x3a, + 0x37, 0x3a, 0x37, 0x3a, 0x38, 0x3b, 0x38, 0x3b, 0x39, 0x3b, 0x39, 0x3b, + 0x00, 0xb7, 0x00, 0x8b, 0x02, 0x72, 0x07, 0x65, 0x0c, 0x5c, 0x11, 0x57, + 0x15, 0x53, 0x18, 0x50, 0x1b, 0x4e, 0x1d, 0x4d, 0x20, 0x4b, 0x22, 0x4a, + 0x23, 0x49, 0x25, 0x48, 0x26, 0x48, 0x28, 0x47, 0x29, 0x47, 0x2a, 0x46, + 0x2b, 0x46, 0x2c, 0x45, 0x16, 0x6d, 0x1f, 0x5f, 0x24, 0x57, 0x28, 0x53, + 0x2b, 0x50, 0x2d, 0x4d, 0x2f, 0x4c, 0x31, 0x4a, 0x32, 0x49, 0x33, 0x48, + 0x34, 0x48, 0x35, 0x47, 0x35, 0x46, 0x36, 0x46, 0x36, 0x45, 0x37, 0x45, + 0x38, 0x45, 0x38, 0x45, 0x39, 0x44, 0x39, 0x44, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x8b, 0x00, 0x44, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x2e, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x25, 0x23, 0x28, 0x26, 0x2b, 0x28, 0x2e, + 0x2a, 0x2f, 0x2c, 0x31, 0x2e, 0x32, 0x2f, 0x33, 0x30, 0x34, 0x31, 0x35, + 0x32, 0x36, 0x33, 0x36, 0x33, 0x36, 0x34, 0x37, 0x35, 0x38, 0x36, 0x38, + 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x00, 0xc5, 0x00, 0xa0, + 0x00, 0x87, 0x03, 0x76, 0x06, 0x6b, 0x0a, 0x64, 0x0e, 0x5f, 0x11, 0x5b, + 0x13, 0x57, 0x16, 0x55, 0x18, 0x53, 0x1a, 0x51, 0x1c, 0x50, 0x1e, 0x4e, + 0x20, 0x4d, 0x21, 0x4c, 0x22, 0x4b, 0x24, 0x4a, 0x25, 0x49, 0x26, 0x48, + 0x15, 0x72, 0x1b, 0x66, 0x20, 0x5f, 0x24, 0x5a, 0x27, 0x56, 0x29, 0x53, + 0x2c, 0x51, 0x2d, 0x50, 0x2f, 0x4e, 0x30, 0x4d, 0x31, 0x4c, 0x32, 0x4b, + 0x32, 0x4a, 0x33, 0x49, 0x34, 0x49, 0x35, 0x48, 0x35, 0x48, 0x35, 0x47, + 0x35, 0x46, 0x36, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x6d, 0x00, 0x33, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x24, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x22, 0x24, 0x23, 0x26, 0x25, 0x29, 0x27, 0x2b, 0x28, 0x2c, 0x2a, 0x2e, + 0x2c, 0x2f, 0x2d, 0x30, 0x2e, 0x31, 0x2f, 0x32, 0x30, 0x33, 0x30, 0x33, + 0x32, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, 0x33, 0x36, 0x34, 0x36, + 0x35, 0x36, 0x36, 0x37, 0x00, 0xcb, 0x00, 0xad, 0x00, 0x96, 0x01, 0x84, + 0x03, 0x78, 0x06, 0x70, 0x09, 0x69, 0x0b, 0x64, 0x0e, 0x60, 0x10, 0x5d, + 0x13, 0x59, 0x15, 0x57, 0x17, 0x56, 0x19, 0x54, 0x1a, 0x52, 0x1c, 0x51, + 0x1d, 0x50, 0x1f, 0x4f, 0x1f, 0x4f, 0x21, 0x4e, 0x14, 0x75, 0x19, 0x6b, + 0x1e, 0x64, 0x22, 0x5f, 0x24, 0x5b, 0x27, 0x58, 0x29, 0x56, 0x2a, 0x54, + 0x2c, 0x52, 0x2d, 0x51, 0x2e, 0x4f, 0x2f, 0x4e, 0x30, 0x4e, 0x31, 0x4d, + 0x31, 0x4c, 0x32, 0x4a, 0x33, 0x4a, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, + 0x00, 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x23, 0x23, 0x25, + 0x24, 0x27, 0x25, 0x28, 0x27, 0x2a, 0x28, 0x2c, 0x2a, 0x2d, 0x2b, 0x2e, + 0x2c, 0x2f, 0x2d, 0x30, 0x2e, 0x31, 0x2f, 0x32, 0x30, 0x32, 0x30, 0x33, + 0x31, 0x33, 0x32, 0x33, 0x33, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, + 0x00, 0xcf, 0x00, 0xb6, 0x00, 0xa0, 0x00, 0x8f, 0x01, 0x83, 0x03, 0x79, + 0x05, 0x72, 0x08, 0x6c, 0x0a, 0x68, 0x0c, 0x64, 0x0f, 0x61, 0x10, 0x5e, + 0x12, 0x5b, 0x14, 0x59, 0x16, 0x58, 0x17, 0x56, 0x19, 0x55, 0x1a, 0x53, + 0x1b, 0x52, 0x1c, 0x50, 0x13, 0x77, 0x18, 0x6e, 0x1c, 0x68, 0x1f, 0x63, + 0x22, 0x5f, 0x24, 0x5c, 0x26, 0x59, 0x28, 0x57, 0x29, 0x56, 0x2a, 0x54, + 0x2c, 0x53, 0x2d, 0x52, 0x2e, 0x50, 0x2e, 0x4f, 0x2f, 0x4f, 0x31, 0x4e, + 0x31, 0x4d, 0x31, 0x4d, 0x31, 0x4c, 0x32, 0x4a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, + 0x00, 0x48, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3a, 0x00, + 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x23, 0x22, 0x24, 0x24, 0x26, 0x25, 0x27, + 0x26, 0x29, 0x27, 0x2a, 0x28, 0x2b, 0x2a, 0x2d, 0x2a, 0x2d, 0x2c, 0x2e, + 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, + 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x34, 0x00, 0xd1, 0x00, 0xbc, + 0x00, 0xa9, 0x00, 0x99, 0x00, 0x8c, 0x02, 0x82, 0x03, 0x7a, 0x05, 0x74, + 0x07, 0x6f, 0x09, 0x6b, 0x0b, 0x67, 0x0d, 0x63, 0x0f, 0x61, 0x10, 0x5f, + 0x12, 0x5c, 0x14, 0x5a, 0x15, 0x58, 0x16, 0x58, 0x18, 0x57, 0x19, 0x56, + 0x13, 0x78, 0x17, 0x70, 0x1b, 0x6a, 0x1e, 0x66, 0x20, 0x62, 0x22, 0x5f, + 0x24, 0x5c, 0x26, 0x5b, 0x27, 0x58, 0x29, 0x57, 0x2a, 0x56, 0x2b, 0x53, + 0x2c, 0x53, 0x2d, 0x52, 0x2e, 0x51, 0x2e, 0x50, 0x2f, 0x4f, 0x30, 0x4f, + 0x31, 0x4f, 0x31, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, + 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, + 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x22, 0x22, 0x22, 0x24, 0x23, 0x25, 0x24, 0x27, 0x25, 0x27, 0x27, 0x28, + 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2e, + 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x33, + 0x30, 0x33, 0x31, 0x33, 0x00, 0xd3, 0x00, 0xc1, 0x00, 0xaf, 0x00, 0xa1, + 0x00, 0x94, 0x00, 0x8a, 0x02, 0x82, 0x03, 0x7b, 0x05, 0x76, 0x07, 0x71, + 0x09, 0x6d, 0x0a, 0x69, 0x0c, 0x66, 0x0e, 0x63, 0x0f, 0x61, 0x10, 0x60, + 0x12, 0x5d, 0x13, 0x5c, 0x14, 0x59, 0x16, 0x58, 0x13, 0x79, 0x16, 0x72, + 0x19, 0x6d, 0x1c, 0x69, 0x1f, 0x65, 0x20, 0x61, 0x23, 0x60, 0x24, 0x5c, + 0x26, 0x5b, 0x27, 0x59, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, + 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2e, 0x51, 0x2e, 0x4f, 0x2f, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, + 0x00, 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, + 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x23, + 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, 0x26, 0x28, 0x26, 0x28, 0x28, 0x2a, + 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2d, 0x2a, 0x2d, 0x2c, 0x2d, 0x2d, 0x2e, + 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, + 0x00, 0xd4, 0x00, 0xc4, 0x00, 0xb5, 0x00, 0xa7, 0x00, 0x9b, 0x00, 0x91, + 0x01, 0x88, 0x02, 0x82, 0x03, 0x7b, 0x05, 0x77, 0x07, 0x73, 0x08, 0x6e, + 0x0a, 0x6b, 0x0b, 0x69, 0x0c, 0x66, 0x0e, 0x63, 0x0f, 0x61, 0x10, 0x60, + 0x12, 0x5f, 0x13, 0x5c, 0x12, 0x79, 0x15, 0x73, 0x18, 0x6e, 0x1b, 0x6a, + 0x1d, 0x67, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x24, 0x5d, 0x26, 0x5c, + 0x27, 0x5a, 0x27, 0x58, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, + 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, + 0x00, 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, + 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, + 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, + 0x2a, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, + 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x30, 0x30, 0x30, 0x00, 0xd5, 0x00, 0xc7, + 0x00, 0xb9, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8f, 0x01, 0x87, + 0x02, 0x82, 0x03, 0x7b, 0x05, 0x77, 0x06, 0x74, 0x08, 0x6f, 0x09, 0x6c, + 0x0b, 0x6b, 0x0c, 0x68, 0x0d, 0x65, 0x0e, 0x63, 0x0f, 0x62, 0x10, 0x60, + 0x12, 0x7a, 0x15, 0x75, 0x18, 0x70, 0x1a, 0x6c, 0x1d, 0x69, 0x1e, 0x65, + 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x24, 0x5d, 0x26, 0x5c, 0x27, 0x5b, + 0x27, 0x58, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, + 0x2c, 0x53, 0x2e, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, + 0x00, 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, + 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, + 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, + 0x2d, 0x30, 0x2d, 0x30, 0x00, 0xd6, 0x00, 0xc9, 0x00, 0xbc, 0x00, 0xb1, + 0x00, 0xa6, 0x00, 0x9c, 0x00, 0x94, 0x00, 0x8c, 0x02, 0x86, 0x02, 0x81, + 0x03, 0x7c, 0x05, 0x78, 0x06, 0x75, 0x07, 0x71, 0x09, 0x6d, 0x0a, 0x6c, + 0x0b, 0x6a, 0x0c, 0x67, 0x0d, 0x65, 0x0e, 0x62, 0x12, 0x7b, 0x15, 0x76, + 0x18, 0x72, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x63, + 0x22, 0x60, 0x23, 0x60, 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5a, + 0x28, 0x57, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2a, 0x55, 0x2b, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, + 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, + 0x00, 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, + 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x21, 0x22, 0x22, 0x23, + 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x27, 0x26, 0x27, + 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2c, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, + 0x00, 0xd6, 0x00, 0xcb, 0x00, 0xbf, 0x00, 0xb4, 0x00, 0xab, 0x00, 0xa1, + 0x00, 0x99, 0x00, 0x91, 0x01, 0x8b, 0x02, 0x85, 0x02, 0x81, 0x04, 0x7c, + 0x05, 0x78, 0x06, 0x76, 0x07, 0x73, 0x08, 0x6f, 0x09, 0x6c, 0x0b, 0x6b, + 0x0c, 0x6a, 0x0c, 0x67, 0x12, 0x7b, 0x15, 0x76, 0x17, 0x72, 0x19, 0x6e, + 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x66, 0x20, 0x65, 0x20, 0x62, 0x22, 0x60, + 0x23, 0x60, 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x27, 0x58, + 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, + 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, + 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, + 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, 0x00, + 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, + 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, + 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, + 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x00, 0xd7, 0x00, 0xcc, + 0x00, 0xc2, 0x00, 0xb8, 0x00, 0xae, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x95, + 0x00, 0x90, 0x01, 0x8a, 0x02, 0x85, 0x03, 0x81, 0x04, 0x7c, 0x05, 0x78, + 0x06, 0x76, 0x07, 0x74, 0x08, 0x70, 0x09, 0x6d, 0x0a, 0x6c, 0x0b, 0x6a, + 0x12, 0x7b, 0x14, 0x77, 0x16, 0x73, 0x18, 0x70, 0x1a, 0x6d, 0x1c, 0x6a, + 0x1d, 0x69, 0x1e, 0x65, 0x20, 0x65, 0x20, 0x62, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x59, 0x28, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, + 0x00, 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, + 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, 0x00, + 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, + 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, 0x26, 0x28, 0x28, 0x28, + 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, + 0x2b, 0x2d, 0x2c, 0x2d, 0x00, 0xd7, 0x00, 0xcd, 0x00, 0xc4, 0x00, 0xbb, + 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, 0x00, 0x93, 0x00, 0x8e, + 0x01, 0x88, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7c, 0x05, 0x79, 0x06, 0x77, + 0x07, 0x75, 0x07, 0x72, 0x09, 0x6e, 0x09, 0x6d, 0x12, 0x7b, 0x14, 0x77, + 0x15, 0x73, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6b, 0x1d, 0x69, 0x1d, 0x67, + 0x1f, 0x65, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, + 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5a, 0x27, 0x58, 0x29, 0x57, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, + 0x00, 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, + 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, + 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, + 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, + 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x26, 0x28, 0x28, 0x28, 0x28, 0x29, + 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, + 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbd, 0x00, 0xb4, 0x00, 0xad, + 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x92, 0x01, 0x8d, 0x02, 0x88, + 0x02, 0x84, 0x03, 0x81, 0x04, 0x7d, 0x05, 0x79, 0x06, 0x77, 0x07, 0x75, + 0x07, 0x73, 0x09, 0x70, 0x12, 0x7c, 0x13, 0x78, 0x15, 0x75, 0x18, 0x72, + 0x19, 0x6e, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, + 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x27, 0x59, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, + 0x00, 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, + 0x00, 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, + 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, 0x00, + 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, + 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, + 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, + 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x00, 0xd8, 0x00, 0xd0, + 0x00, 0xc7, 0x00, 0xbe, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, + 0x00, 0x9b, 0x00, 0x95, 0x00, 0x90, 0x01, 0x8c, 0x02, 0x87, 0x02, 0x84, + 0x03, 0x81, 0x04, 0x7d, 0x05, 0x79, 0x06, 0x77, 0x06, 0x76, 0x07, 0x74, + 0x11, 0x7c, 0x13, 0x78, 0x15, 0x76, 0x17, 0x72, 0x18, 0x6f, 0x1a, 0x6d, + 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x65, 0x20, 0x64, + 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x00, 0x2f, 0x00, 0x5f, 0x00, 0x99, 0x00, 0xac, + 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, + 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x00, 0x9a, 0x00, 0x6d, 0x00, 0x8d, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0xb2, + 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, + 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x3b, 0x00, 0x6f, 0x00, 0x9f, 0x00, 0xaf, + 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, + 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x07, 0x47, 0x00, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, + 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, + 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, + 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, + 0x27, 0x29, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc0, + 0x00, 0xba, 0x00, 0xb1, 0x00, 0xac, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x98, + 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, + 0x04, 0x7d, 0x05, 0x79, 0x06, 0x78, 0x06, 0x76, 0x11, 0x7c, 0x13, 0x79, + 0x15, 0x76, 0x17, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, + 0x1d, 0x69, 0x1d, 0x66, 0x20, 0x65, 0x20, 0x65, 0x20, 0x63, 0x22, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x5f, 0x00, 0x8b, 0x00, 0x9f, 0x00, 0xaa, + 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, + 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x6d, 0x00, 0x54, + 0x00, 0x6b, 0x00, 0x87, 0x00, 0x98, 0x00, 0xa6, 0x00, 0xb0, 0x00, 0xb3, + 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, + 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x07, 0x02, 0x27, 0x00, 0x6f, 0x00, 0x93, 0x00, 0xa4, 0x00, 0xad, + 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, + 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x0f, 0x0f, 0x00, 0x3f, + 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, + 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, + 0x00, 0xbd, 0x00, 0xbd, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, + 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, + 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, + 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xc9, 0x00, 0xc2, 0x00, 0xbb, 0x00, 0xb4, + 0x00, 0xae, 0x00, 0xa7, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x96, 0x00, 0x92, + 0x01, 0x8e, 0x01, 0x89, 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, + 0x05, 0x7a, 0x06, 0x78, 0x11, 0x7c, 0x13, 0x79, 0x15, 0x76, 0x16, 0x73, + 0x18, 0x72, 0x19, 0x6e, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x69, + 0x1e, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x62, 0x22, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x5f, 0x24, 0x5c, 0x26, 0x5c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x44, 0x00, 0x6d, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, + 0x00, 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, + 0x00, 0xb6, 0x00, 0xb7, 0x00, 0x8d, 0x00, 0x6b, 0x00, 0x28, 0x00, 0x52, + 0x00, 0x70, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, 0xa6, 0x00, 0xab, + 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x12, 0x02, + 0x00, 0x22, 0x00, 0x58, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9c, 0x00, 0xa4, + 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, + 0x00, 0xb8, 0x00, 0xb9, 0x26, 0x00, 0x05, 0x05, 0x00, 0x3f, 0x00, 0x6d, + 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, + 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, + 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x00, 0xd9, 0x00, 0xd2, + 0x00, 0xca, 0x00, 0xc4, 0x00, 0xbd, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, + 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x91, 0x01, 0x8e, + 0x02, 0x89, 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, 0x05, 0x7a, + 0x11, 0x7c, 0x13, 0x7a, 0x15, 0x76, 0x15, 0x74, 0x18, 0x72, 0x18, 0x6f, + 0x1a, 0x6d, 0x1a, 0x6c, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x67, 0x1f, 0x65, + 0x20, 0x65, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x5f, 0x24, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x33, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, + 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, + 0x00, 0x9b, 0x00, 0x87, 0x00, 0x52, 0x00, 0x11, 0x00, 0x39, 0x00, 0x57, + 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa3, + 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x25, 0x00, 0x0b, 0x09, 0x00, 0x21, + 0x00, 0x4a, 0x00, 0x68, 0x00, 0x7d, 0x00, 0x8b, 0x00, 0x96, 0x00, 0x9d, + 0x00, 0xa3, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, + 0x33, 0x00, 0x1c, 0x00, 0x00, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, + 0x00, 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, + 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, + 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x2a, 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc6, + 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb1, 0x00, 0xac, 0x00, 0xa6, 0x00, 0xa1, + 0x00, 0x9d, 0x00, 0x97, 0x00, 0x93, 0x00, 0x90, 0x01, 0x8d, 0x02, 0x88, + 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, 0x11, 0x7c, 0x13, 0x7a, + 0x15, 0x76, 0x15, 0x75, 0x18, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, + 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, 0x65, + 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x28, + 0x00, 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, + 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0x98, + 0x00, 0x70, 0x00, 0x39, 0x00, 0x02, 0x00, 0x28, 0x00, 0x48, 0x00, 0x5f, + 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, 0x98, 0x00, 0x9d, + 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x2f, 0x00, 0x16, 0x00, 0x08, 0x0e, 0x00, 0x20, 0x00, 0x42, + 0x00, 0x5c, 0x00, 0x6f, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x92, 0x00, 0x99, + 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x38, 0x00, 0x2a, 0x00, + 0x09, 0x00, 0x00, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, + 0x00, 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xab, 0x00, 0xad, 0x16, 0xa4, 0x01, 0xbb, 0x00, 0xc6, 0x00, 0xcc, + 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, + 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, + 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x05, 0x9d, 0x00, 0xbc, + 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xcf, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd5, + 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, + 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x02, 0xbd, 0x00, 0xcc, 0x00, 0xd1, 0x00, 0xd5, + 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, + 0x00, 0xd9, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xdb, + 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x3d, + 0x00, 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, + 0x00, 0x93, 0x00, 0x98, 0x00, 0xb2, 0x00, 0xa6, 0x00, 0x85, 0x00, 0x57, + 0x00, 0x28, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, 0x52, 0x00, 0x64, + 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, 0x93, 0x00, 0x98, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, + 0x23, 0x00, 0x0e, 0x02, 0x06, 0x12, 0x00, 0x20, 0x00, 0x3c, 0x00, 0x52, + 0x00, 0x64, 0x00, 0x73, 0x00, 0x7e, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x95, + 0x00, 0x9a, 0x00, 0x9e, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x00, 0x00, 0x05, + 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, + 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, + 0x23, 0x74, 0x09, 0x91, 0x02, 0xa3, 0x00, 0xaf, 0x00, 0xb7, 0x00, 0xbd, + 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, + 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd2, + 0x00, 0xd2, 0x00, 0xd3, 0x0d, 0x6b, 0x01, 0x8e, 0x00, 0xa4, 0x00, 0xb0, + 0x00, 0xb9, 0x00, 0xbe, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc8, 0x00, 0xca, + 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, + 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x06, 0xa4, 0x00, 0xb5, 0x00, 0xc0, 0x00, 0xc6, 0x00, 0xcb, 0x00, 0xcd, + 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, + 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd7, + 0x00, 0xd8, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, + 0x00, 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, + 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, 0x48, 0x00, 0x21, + 0x00, 0x01, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, 0x59, 0x00, 0x66, + 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x18, 0x00, + 0x0c, 0x07, 0x05, 0x14, 0x00, 0x20, 0x00, 0x37, 0x00, 0x4c, 0x00, 0x5c, + 0x00, 0x6a, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x92, + 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x00, 0x00, 0x0f, 0x00, 0x29, + 0x00, 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, + 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, 0x2a, 0x61, 0x11, 0x78, + 0x07, 0x8a, 0x03, 0x98, 0x01, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, + 0x00, 0xba, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc6, + 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, + 0x12, 0x51, 0x04, 0x73, 0x00, 0x89, 0x00, 0x9a, 0x00, 0xa4, 0x00, 0xad, + 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbb, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc3, + 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, + 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x09, 0x97, 0x02, 0xa8, + 0x00, 0xb3, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc7, 0x00, 0xca, + 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd1, + 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, 0x00, 0xd5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, + 0x00, 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0xbb, 0x00, 0xb3, + 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x1c, 0x00, 0x00, + 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, 0x5d, 0x00, 0x68, + 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x20, 0x00, 0x10, 0x00, 0x0a, 0x0b, + 0x04, 0x16, 0x00, 0x20, 0x00, 0x34, 0x00, 0x46, 0x00, 0x56, 0x00, 0x62, + 0x00, 0x6d, 0x00, 0x77, 0x00, 0x7f, 0x00, 0x85, 0x3d, 0x00, 0x38, 0x00, + 0x2a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, + 0x00, 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, + 0x00, 0x8b, 0x00, 0x91, 0x2e, 0x59, 0x17, 0x6b, 0x0c, 0x7a, 0x06, 0x87, + 0x03, 0x92, 0x01, 0x9b, 0x00, 0xa2, 0x00, 0xa8, 0x00, 0xad, 0x00, 0xb1, + 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, + 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x15, 0x45, 0x08, 0x60, + 0x02, 0x77, 0x00, 0x87, 0x00, 0x94, 0x00, 0x9d, 0x00, 0xa4, 0x00, 0xaa, + 0x00, 0xaf, 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, + 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0a, 0x91, 0x04, 0x9e, 0x01, 0xaa, 0x00, 0xb2, + 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc6, 0x00, 0xc8, + 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, + 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, + 0x00, 0x60, 0x00, 0x6a, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xa6, 0x00, 0x8e, + 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, 0x00, 0x00, 0x16, + 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, 0x60, 0x00, 0x6a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, + 0x33, 0x00, 0x27, 0x00, 0x18, 0x00, 0x0d, 0x04, 0x08, 0x0e, 0x04, 0x17, + 0x00, 0x20, 0x00, 0x32, 0x00, 0x42, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x67, + 0x00, 0x70, 0x00, 0x78, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, + 0x0b, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, + 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, + 0x31, 0x53, 0x1c, 0x62, 0x11, 0x70, 0x0a, 0x7b, 0x06, 0x85, 0x03, 0x8e, + 0x02, 0x96, 0x00, 0x9c, 0x00, 0xa2, 0x00, 0xa7, 0x00, 0xab, 0x00, 0xae, + 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, + 0x00, 0xbe, 0x00, 0xbf, 0x17, 0x3d, 0x0b, 0x55, 0x04, 0x67, 0x01, 0x78, + 0x00, 0x85, 0x00, 0x90, 0x00, 0x98, 0x00, 0x9f, 0x00, 0xa5, 0x00, 0xaa, + 0x00, 0xad, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xbb, + 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0b, 0x8d, 0x05, 0x99, 0x02, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, + 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc7, + 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcd, + 0x00, 0xce, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, + 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, 0x7e, 0x00, 0x64, + 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x25, + 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, + 0x1f, 0x00, 0x12, 0x00, 0x0c, 0x07, 0x07, 0x10, 0x03, 0x18, 0x00, 0x20, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4c, 0x00, 0x57, 0x00, 0x61, 0x00, 0x6a, + 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x00, + 0x00, 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, + 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, 0x33, 0x50, 0x20, 0x5c, + 0x15, 0x68, 0x0e, 0x72, 0x09, 0x7c, 0x05, 0x84, 0x03, 0x8c, 0x02, 0x92, + 0x01, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, + 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, + 0x18, 0x38, 0x0d, 0x4c, 0x06, 0x5e, 0x03, 0x6c, 0x01, 0x7a, 0x00, 0x84, + 0x00, 0x8d, 0x00, 0x95, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, + 0x00, 0xba, 0x00, 0xbc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0c, 0x8a, 0x06, 0x94, + 0x03, 0x9d, 0x01, 0xa4, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb5, 0x00, 0xb9, + 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, + 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0xbd, 0x00, 0xb9, + 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, 0x59, 0x00, 0x41, + 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, + 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, + 0x0e, 0x02, 0x0a, 0x0a, 0x06, 0x11, 0x03, 0x19, 0x00, 0x1f, 0x00, 0x2e, + 0x00, 0x3c, 0x00, 0x48, 0x00, 0x53, 0x00, 0x5c, 0x3e, 0x00, 0x3c, 0x00, + 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x14, + 0x00, 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, + 0x00, 0x68, 0x00, 0x70, 0x34, 0x4e, 0x23, 0x58, 0x18, 0x62, 0x10, 0x6c, + 0x0b, 0x75, 0x08, 0x7c, 0x05, 0x83, 0x03, 0x8a, 0x02, 0x8f, 0x01, 0x95, + 0x00, 0x9a, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xac, + 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb5, 0x1a, 0x35, 0x0f, 0x47, + 0x08, 0x55, 0x04, 0x64, 0x02, 0x70, 0x00, 0x7a, 0x00, 0x83, 0x00, 0x8b, + 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb7, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x89, 0x07, 0x92, 0x04, 0x99, 0x02, 0xa0, + 0x01, 0xa6, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, + 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc5, + 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xca, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2d, 0x00, 0x39, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, + 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, 0x3a, 0x00, 0x25, + 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, + 0x38, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x0d, 0x05, + 0x09, 0x0c, 0x06, 0x13, 0x03, 0x19, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x3a, + 0x00, 0x45, 0x00, 0x4f, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, + 0x21, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, 0x0a, 0x00, 0x18, 0x00, 0x26, + 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, + 0x35, 0x4c, 0x26, 0x55, 0x1b, 0x5e, 0x13, 0x67, 0x0e, 0x6f, 0x0a, 0x76, + 0x07, 0x7d, 0x05, 0x82, 0x03, 0x88, 0x02, 0x8e, 0x02, 0x92, 0x01, 0x97, + 0x00, 0x9b, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, + 0x00, 0xac, 0x00, 0xaf, 0x1b, 0x33, 0x10, 0x42, 0x0a, 0x50, 0x05, 0x5c, + 0x03, 0x68, 0x01, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x8a, 0x00, 0x90, + 0x00, 0x96, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0d, 0x88, 0x08, 0x8f, 0x05, 0x96, 0x02, 0x9c, 0x01, 0xa2, 0x00, 0xa7, + 0x00, 0xac, 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xbb, + 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc5, + 0x00, 0xc6, 0x00, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, + 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, 0x98, 0x00, 0x85, + 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, 0x22, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x33, 0x00, + 0x2c, 0x00, 0x23, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x0c, 0x07, 0x08, 0x0e, + 0x05, 0x14, 0x02, 0x1a, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x42, + 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, + 0x0c, 0x00, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, + 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x36, 0x4a, 0x28, 0x52, + 0x1d, 0x5b, 0x16, 0x63, 0x10, 0x6a, 0x0c, 0x71, 0x09, 0x77, 0x07, 0x7d, + 0x05, 0x82, 0x03, 0x87, 0x02, 0x8c, 0x02, 0x90, 0x01, 0x94, 0x00, 0x99, + 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xaa, + 0x1b, 0x30, 0x12, 0x3e, 0x0c, 0x4b, 0x07, 0x57, 0x04, 0x61, 0x02, 0x6b, + 0x01, 0x73, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8f, 0x00, 0x93, + 0x00, 0x97, 0x00, 0x9c, 0x00, 0x9f, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xaa, 0x00, 0xac, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x86, 0x09, 0x8d, + 0x06, 0x94, 0x03, 0x9a, 0x02, 0x9f, 0x01, 0xa4, 0x00, 0xa8, 0x00, 0xac, + 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, + 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0xbe, 0x00, 0xbb, + 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, 0x7b, 0x00, 0x68, + 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x26, 0x00, + 0x1d, 0x00, 0x14, 0x00, 0x0e, 0x03, 0x0b, 0x09, 0x08, 0x0f, 0x05, 0x15, + 0x02, 0x1a, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x3f, 0x00, 0x3d, 0x00, + 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, + 0x00, 0x49, 0x00, 0x51, 0x37, 0x49, 0x2a, 0x51, 0x20, 0x58, 0x18, 0x5f, + 0x13, 0x66, 0x0f, 0x6c, 0x0b, 0x73, 0x09, 0x78, 0x07, 0x7e, 0x05, 0x82, + 0x03, 0x86, 0x02, 0x8b, 0x02, 0x8f, 0x01, 0x92, 0x01, 0x96, 0x00, 0x9a, + 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa4, 0x1c, 0x2f, 0x13, 0x3b, + 0x0d, 0x47, 0x09, 0x51, 0x05, 0x5c, 0x04, 0x65, 0x02, 0x6e, 0x01, 0x75, + 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8d, 0x00, 0x91, 0x00, 0x96, + 0x00, 0x99, 0x00, 0x9c, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0xa7, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x86, 0x09, 0x8c, 0x06, 0x92, 0x04, 0x97, + 0x02, 0x9c, 0x02, 0xa1, 0x01, 0xa5, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, + 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, + 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0d, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xad, + 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, 0x60, 0x00, 0x4f, + 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, + 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, + 0x10, 0x00, 0x0d, 0x05, 0x0a, 0x0b, 0x07, 0x10, 0x04, 0x16, 0x02, 0x1b, + 0x00, 0x1f, 0x00, 0x2a, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, + 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, + 0x38, 0x48, 0x2b, 0x4f, 0x22, 0x56, 0x1a, 0x5c, 0x15, 0x63, 0x11, 0x69, + 0x0d, 0x6e, 0x0a, 0x74, 0x08, 0x78, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x85, + 0x03, 0x8a, 0x02, 0x8e, 0x02, 0x90, 0x01, 0x93, 0x00, 0x98, 0x00, 0x9b, + 0x00, 0x9d, 0x00, 0x9f, 0x1c, 0x2d, 0x14, 0x39, 0x0e, 0x44, 0x0a, 0x4e, + 0x07, 0x57, 0x04, 0x60, 0x02, 0x67, 0x01, 0x6f, 0x00, 0x76, 0x00, 0x7d, + 0x00, 0x81, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, 0x00, 0x94, 0x00, 0x97, + 0x00, 0x9a, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0e, 0x85, 0x0a, 0x8b, 0x07, 0x90, 0x05, 0x95, 0x03, 0x9a, 0x02, 0x9e, + 0x01, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, + 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, + 0x00, 0xbe, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x98, + 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, 0x49, 0x00, 0x39, + 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, + 0x32, 0x00, 0x2c, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0e, 0x01, + 0x0c, 0x07, 0x09, 0x0c, 0x06, 0x11, 0x04, 0x16, 0x02, 0x1b, 0x00, 0x1f, + 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, + 0x1c, 0x00, 0x11, 0x00, 0x07, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x19, + 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, 0x38, 0x48, 0x2d, 0x4e, + 0x23, 0x54, 0x1c, 0x5a, 0x17, 0x5f, 0x12, 0x66, 0x0f, 0x6a, 0x0c, 0x71, + 0x0a, 0x74, 0x08, 0x79, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x89, + 0x02, 0x8d, 0x02, 0x8f, 0x01, 0x92, 0x01, 0x95, 0x00, 0x99, 0x00, 0x9b, + 0x1c, 0x2d, 0x15, 0x37, 0x0f, 0x41, 0x0b, 0x4a, 0x07, 0x53, 0x05, 0x5b, + 0x04, 0x63, 0x02, 0x69, 0x01, 0x70, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, + 0x00, 0x87, 0x00, 0x8a, 0x00, 0x8f, 0x00, 0x92, 0x00, 0x96, 0x00, 0x99, + 0x00, 0x9b, 0x00, 0x9e, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x85, 0x0a, 0x8a, + 0x07, 0x8f, 0x05, 0x93, 0x03, 0x98, 0x02, 0x9c, 0x02, 0xa0, 0x01, 0xa3, + 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb3, + 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x39, 0x47, 0x2e, 0x4d, 0x25, 0x53, 0x1e, 0x58, + 0x19, 0x5e, 0x14, 0x63, 0x11, 0x68, 0x0e, 0x6c, 0x0b, 0x72, 0x09, 0x75, + 0x07, 0x79, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x88, 0x02, 0x8c, + 0x02, 0x8e, 0x01, 0x91, 0x01, 0x93, 0x00, 0x97, 0x1d, 0x2c, 0x16, 0x36, + 0x10, 0x3e, 0x0c, 0x48, 0x09, 0x50, 0x06, 0x58, 0x04, 0x5f, 0x02, 0x66, + 0x02, 0x6c, 0x01, 0x71, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, + 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x84, 0x0b, 0x89, 0x08, 0x8d, 0x06, 0x92, + 0x04, 0x96, 0x03, 0x9a, 0x02, 0x9e, 0x01, 0xa1, 0x01, 0xa4, 0x00, 0xa7, + 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, + 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x39, 0x46, 0x2f, 0x4c, 0x26, 0x51, 0x20, 0x56, 0x1a, 0x5c, 0x16, 0x60, + 0x12, 0x66, 0x0f, 0x69, 0x0c, 0x6e, 0x0b, 0x73, 0x09, 0x76, 0x07, 0x7a, + 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x88, 0x02, 0x8b, 0x02, 0x8e, + 0x02, 0x90, 0x01, 0x92, 0x1d, 0x2b, 0x16, 0x34, 0x11, 0x3d, 0x0c, 0x45, + 0x0a, 0x4c, 0x07, 0x54, 0x05, 0x5b, 0x04, 0x61, 0x02, 0x67, 0x01, 0x6d, + 0x01, 0x73, 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, 0x89, + 0x00, 0x8d, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0e, 0x84, 0x0b, 0x88, 0x08, 0x8d, 0x06, 0x91, 0x05, 0x94, 0x03, 0x98, + 0x02, 0x9c, 0x02, 0x9f, 0x01, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, + 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, + 0x00, 0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x46, 0x30, 0x4b, + 0x28, 0x50, 0x21, 0x55, 0x1c, 0x59, 0x17, 0x5e, 0x14, 0x63, 0x10, 0x68, + 0x0e, 0x6b, 0x0c, 0x70, 0x0a, 0x74, 0x08, 0x76, 0x07, 0x7b, 0x06, 0x7e, + 0x05, 0x81, 0x04, 0x83, 0x03, 0x87, 0x02, 0x8a, 0x02, 0x8d, 0x02, 0x8f, + 0x1e, 0x2a, 0x17, 0x33, 0x12, 0x3b, 0x0e, 0x43, 0x0a, 0x4a, 0x07, 0x51, + 0x05, 0x58, 0x04, 0x5e, 0x03, 0x64, 0x02, 0x69, 0x01, 0x6f, 0x00, 0x74, + 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, + 0x00, 0x8f, 0x00, 0x93, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0b, 0x88, + 0x09, 0x8c, 0x07, 0x90, 0x05, 0x93, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, + 0x01, 0xa0, 0x01, 0xa3, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xad, + 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x46, 0x31, 0x4a, 0x29, 0x4e, 0x22, 0x54, + 0x1d, 0x57, 0x19, 0x5d, 0x15, 0x60, 0x12, 0x65, 0x0f, 0x69, 0x0d, 0x6c, + 0x0b, 0x71, 0x09, 0x74, 0x08, 0x77, 0x07, 0x7b, 0x06, 0x7f, 0x05, 0x81, + 0x04, 0x83, 0x03, 0x86, 0x02, 0x8a, 0x02, 0x8c, 0x1e, 0x2a, 0x18, 0x32, + 0x12, 0x39, 0x0f, 0x40, 0x0c, 0x48, 0x09, 0x4f, 0x07, 0x55, 0x05, 0x5a, + 0x04, 0x60, 0x02, 0x66, 0x02, 0x6b, 0x01, 0x70, 0x00, 0x75, 0x00, 0x79, + 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8b, 0x07, 0x8e, + 0x06, 0x92, 0x04, 0x96, 0x03, 0x99, 0x02, 0x9b, 0x02, 0x9e, 0x01, 0xa1, + 0x01, 0xa4, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, + 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb5, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x45, 0x31, 0x49, 0x2a, 0x4d, 0x24, 0x52, 0x1f, 0x56, 0x1a, 0x5b, + 0x16, 0x5f, 0x13, 0x63, 0x10, 0x67, 0x0e, 0x6a, 0x0c, 0x6e, 0x0b, 0x72, + 0x09, 0x75, 0x07, 0x77, 0x07, 0x7b, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, + 0x03, 0x85, 0x02, 0x89, 0x1e, 0x29, 0x18, 0x31, 0x13, 0x38, 0x0f, 0x3f, + 0x0c, 0x45, 0x0a, 0x4c, 0x07, 0x52, 0x05, 0x58, 0x04, 0x5d, 0x04, 0x63, + 0x02, 0x67, 0x01, 0x6c, 0x01, 0x71, 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, + 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8a, 0x07, 0x8e, 0x06, 0x91, 0x05, 0x94, + 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x02, 0xa0, 0x01, 0xa2, 0x00, 0xa4, + 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, + 0x00, 0xb2, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x45, 0x32, 0x48, + 0x2b, 0x4d, 0x25, 0x51, 0x20, 0x55, 0x1b, 0x59, 0x18, 0x5e, 0x14, 0x60, + 0x12, 0x65, 0x0f, 0x68, 0x0d, 0x6b, 0x0c, 0x6f, 0x0a, 0x73, 0x09, 0x75, + 0x07, 0x77, 0x06, 0x7b, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, + 0x1e, 0x29, 0x19, 0x30, 0x14, 0x37, 0x0f, 0x3d, 0x0c, 0x44, 0x0a, 0x4a, + 0x07, 0x50, 0x06, 0x55, 0x05, 0x5b, 0x04, 0x60, 0x02, 0x64, 0x02, 0x69, + 0x01, 0x6d, 0x01, 0x72, 0x00, 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, + 0x00, 0x83, 0x00, 0x87, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x86, + 0x0a, 0x8a, 0x07, 0x8d, 0x06, 0x90, 0x05, 0x93, 0x03, 0x96, 0x03, 0x99, + 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, 0x01, 0xa3, 0x00, 0xa5, 0x00, 0xa7, + 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3b, 0x45, 0x33, 0x48, 0x2c, 0x4d, 0x26, 0x4f, + 0x21, 0x55, 0x1c, 0x57, 0x19, 0x5c, 0x16, 0x5f, 0x13, 0x63, 0x10, 0x67, + 0x0e, 0x6a, 0x0c, 0x6c, 0x0b, 0x71, 0x0a, 0x73, 0x09, 0x76, 0x07, 0x78, + 0x06, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x1e, 0x28, 0x19, 0x2f, + 0x15, 0x35, 0x10, 0x3c, 0x0d, 0x42, 0x0b, 0x48, 0x09, 0x4e, 0x07, 0x53, + 0x05, 0x58, 0x04, 0x5d, 0x04, 0x62, 0x02, 0x66, 0x02, 0x6b, 0x01, 0x6f, + 0x00, 0x73, 0x00, 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x08, 0x8c, + 0x06, 0x8f, 0x05, 0x92, 0x04, 0x95, 0x03, 0x98, 0x02, 0x9a, 0x02, 0x9d, + 0x02, 0x9f, 0x01, 0xa1, 0x01, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xa9, + 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x44, 0x33, 0x47, 0x2c, 0x4c, 0x27, 0x4f, 0x22, 0x54, 0x1e, 0x56, + 0x1a, 0x5a, 0x17, 0x5e, 0x14, 0x60, 0x12, 0x65, 0x10, 0x68, 0x0e, 0x6a, + 0x0c, 0x6e, 0x0b, 0x72, 0x09, 0x74, 0x08, 0x76, 0x07, 0x78, 0x06, 0x7c, + 0x06, 0x7f, 0x05, 0x81, 0x1e, 0x28, 0x19, 0x2f, 0x16, 0x35, 0x12, 0x3b, + 0x0f, 0x41, 0x0c, 0x46, 0x0a, 0x4c, 0x07, 0x51, 0x05, 0x55, 0x05, 0x5b, + 0x04, 0x5f, 0x02, 0x63, 0x02, 0x68, 0x01, 0x6b, 0x01, 0x70, 0x00, 0x73, + 0x00, 0x77, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x81, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0f, 0x82, 0x0c, 0x86, 0x0b, 0x89, 0x09, 0x8c, 0x07, 0x8f, 0x06, 0x91, + 0x05, 0x94, 0x03, 0x97, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, + 0x01, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xac, + 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x23, 0x25, 0x22, + 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0x44, 0x05, 0x25, 0x0d, 0x23, 0x12, 0x22, + 0x15, 0x22, 0x17, 0x22, 0x18, 0x22, 0x1a, 0x22, 0x1b, 0x22, 0x1b, 0x22, + 0x1c, 0x22, 0x1c, 0x22, 0x1c, 0x22, 0x1d, 0x22, 0x1d, 0x22, 0x1e, 0x22, + 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x10, 0x33, 0x13, 0x23, + 0x17, 0x22, 0x1a, 0x22, 0x1b, 0x22, 0x1c, 0x22, 0x1d, 0x22, 0x1e, 0x22, + 0x1e, 0x22, 0x1e, 0x22, 0x1f, 0x22, 0x1f, 0x22, 0x1f, 0x22, 0x1f, 0x22, + 0x1f, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x35, 0x25, 0x2b, 0x23, 0x27, 0x22, 0x25, 0x22, + 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0x9d, 0x00, 0x6b, 0x01, 0x51, 0x04, 0x45, 0x08, 0x3d, 0x0b, 0x38, + 0x0d, 0x35, 0x0f, 0x33, 0x10, 0x30, 0x12, 0x2f, 0x13, 0x2d, 0x14, 0x2d, + 0x15, 0x2c, 0x16, 0x2b, 0x16, 0x2a, 0x17, 0x2a, 0x18, 0x29, 0x18, 0x29, + 0x19, 0x28, 0x19, 0x28, 0x10, 0x5f, 0x10, 0x46, 0x11, 0x39, 0x13, 0x33, + 0x15, 0x2f, 0x16, 0x2d, 0x17, 0x2b, 0x18, 0x2a, 0x19, 0x29, 0x1a, 0x28, + 0x1a, 0x27, 0x1b, 0x27, 0x1b, 0x27, 0x1c, 0x26, 0x1c, 0x26, 0x1c, 0x26, + 0x1d, 0x25, 0x1d, 0x25, 0x1d, 0x25, 0x1d, 0x25, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x2a, 0x2e, 0x26, 0x2a, 0x25, 0x27, 0x24, 0x26, 0x23, 0x25, 0x23, + 0x24, 0x23, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xbc, 0x00, 0x8e, + 0x00, 0x73, 0x00, 0x60, 0x02, 0x55, 0x04, 0x4c, 0x06, 0x47, 0x08, 0x42, + 0x0a, 0x3e, 0x0c, 0x3b, 0x0d, 0x39, 0x0e, 0x37, 0x0f, 0x36, 0x10, 0x34, + 0x11, 0x33, 0x12, 0x32, 0x12, 0x31, 0x13, 0x30, 0x14, 0x2f, 0x15, 0x2f, + 0x10, 0x6f, 0x10, 0x58, 0x10, 0x4a, 0x11, 0x41, 0x12, 0x3b, 0x13, 0x37, + 0x14, 0x34, 0x15, 0x32, 0x16, 0x30, 0x17, 0x2e, 0x17, 0x2d, 0x18, 0x2c, + 0x18, 0x2c, 0x19, 0x2b, 0x19, 0x2a, 0x1a, 0x2a, 0x1a, 0x29, 0x1a, 0x29, + 0x1b, 0x28, 0x1b, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x2e, 0x31, 0x29, + 0x2c, 0x27, 0x2a, 0x26, 0x28, 0x25, 0x27, 0x24, 0x25, 0x24, 0x25, 0x23, + 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xc6, 0x00, 0xa4, 0x00, 0x89, 0x00, 0x77, + 0x00, 0x67, 0x01, 0x5e, 0x03, 0x55, 0x04, 0x50, 0x05, 0x4b, 0x07, 0x47, + 0x09, 0x44, 0x0a, 0x41, 0x0b, 0x3e, 0x0c, 0x3d, 0x0c, 0x3b, 0x0e, 0x39, + 0x0f, 0x38, 0x0f, 0x37, 0x0f, 0x35, 0x10, 0x35, 0x10, 0x74, 0x10, 0x63, + 0x10, 0x55, 0x10, 0x4c, 0x11, 0x44, 0x11, 0x40, 0x12, 0x3b, 0x13, 0x39, + 0x13, 0x36, 0x14, 0x34, 0x15, 0x33, 0x16, 0x31, 0x16, 0x30, 0x17, 0x2f, + 0x17, 0x2e, 0x18, 0x2d, 0x18, 0x2d, 0x18, 0x2c, 0x18, 0x2b, 0x19, 0x2b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x31, 0x33, 0x2c, 0x2f, 0x29, 0x2b, 0x28, + 0x29, 0x26, 0x28, 0x25, 0x27, 0x25, 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xcd, 0x00, 0xb0, 0x00, 0x9a, 0x00, 0x87, 0x00, 0x78, 0x00, 0x6c, + 0x01, 0x64, 0x02, 0x5c, 0x03, 0x57, 0x04, 0x51, 0x05, 0x4e, 0x07, 0x4a, + 0x07, 0x48, 0x09, 0x45, 0x0a, 0x43, 0x0a, 0x40, 0x0c, 0x3f, 0x0c, 0x3d, + 0x0c, 0x3c, 0x0d, 0x3b, 0x10, 0x77, 0x10, 0x69, 0x10, 0x5e, 0x10, 0x54, + 0x10, 0x4d, 0x11, 0x47, 0x11, 0x43, 0x12, 0x3f, 0x12, 0x3c, 0x13, 0x39, + 0x13, 0x38, 0x14, 0x36, 0x14, 0x35, 0x15, 0x33, 0x16, 0x32, 0x16, 0x31, + 0x17, 0x30, 0x17, 0x2f, 0x17, 0x2f, 0x17, 0x2e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x33, 0x34, 0x2e, 0x30, 0x2b, 0x2d, 0x29, 0x2b, 0x28, 0x29, 0x27, + 0x29, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x24, 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xcf, 0x00, 0xb9, + 0x00, 0xa4, 0x00, 0x94, 0x00, 0x85, 0x00, 0x7a, 0x00, 0x70, 0x00, 0x68, + 0x01, 0x61, 0x02, 0x5c, 0x04, 0x57, 0x04, 0x53, 0x05, 0x50, 0x06, 0x4c, + 0x07, 0x4a, 0x07, 0x48, 0x09, 0x45, 0x0a, 0x44, 0x0a, 0x42, 0x0b, 0x41, + 0x10, 0x78, 0x10, 0x6d, 0x10, 0x63, 0x10, 0x5b, 0x10, 0x53, 0x10, 0x4e, + 0x11, 0x49, 0x11, 0x45, 0x11, 0x41, 0x12, 0x3f, 0x13, 0x3c, 0x13, 0x3a, + 0x13, 0x39, 0x14, 0x37, 0x14, 0x36, 0x14, 0x35, 0x15, 0x33, 0x16, 0x33, + 0x16, 0x32, 0x16, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x34, 0x36, 0x30, + 0x32, 0x2d, 0x2f, 0x2b, 0x2d, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, + 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xd2, 0x00, 0xbe, 0x00, 0xad, 0x00, 0x9d, + 0x00, 0x90, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x6b, 0x01, 0x65, + 0x02, 0x60, 0x02, 0x5b, 0x04, 0x58, 0x04, 0x54, 0x05, 0x51, 0x05, 0x4f, + 0x07, 0x4c, 0x07, 0x4a, 0x07, 0x48, 0x09, 0x46, 0x10, 0x7a, 0x10, 0x70, + 0x10, 0x67, 0x10, 0x5f, 0x10, 0x59, 0x10, 0x53, 0x10, 0x4e, 0x11, 0x4a, + 0x11, 0x46, 0x11, 0x43, 0x12, 0x41, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, + 0x13, 0x39, 0x13, 0x38, 0x14, 0x37, 0x14, 0x36, 0x14, 0x35, 0x15, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x35, 0x37, 0x31, 0x33, 0x2e, 0x30, 0x2c, + 0x2d, 0x2a, 0x2c, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, 0x28, 0x27, + 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd3, 0x00, 0xc2, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x98, 0x00, 0x8d, + 0x00, 0x83, 0x00, 0x7b, 0x00, 0x73, 0x00, 0x6e, 0x01, 0x67, 0x01, 0x63, + 0x02, 0x5f, 0x02, 0x5b, 0x04, 0x58, 0x04, 0x55, 0x05, 0x52, 0x05, 0x50, + 0x06, 0x4e, 0x07, 0x4c, 0x10, 0x7a, 0x10, 0x72, 0x10, 0x6a, 0x10, 0x63, + 0x10, 0x5d, 0x10, 0x57, 0x10, 0x52, 0x10, 0x4e, 0x11, 0x4a, 0x11, 0x48, + 0x11, 0x44, 0x11, 0x42, 0x12, 0x40, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, + 0x13, 0x3a, 0x13, 0x39, 0x14, 0x38, 0x14, 0x37, 0x0f, 0x00, 0x1f, 0x00, + 0x33, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x0b, 0x3b, 0x17, 0x07, 0x2c, 0x00, 0x36, 0x00, + 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x3d, 0x00, + 0x4c, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x42, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x07, 0x47, 0x0f, 0x0f, 0x26, 0x00, 0x33, 0x00, + 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3c, 0x37, 0x37, 0x32, 0x34, 0x30, 0x31, 0x2d, 0x2f, 0x2c, 0x2d, 0x2b, + 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, 0x28, 0x27, 0x27, 0x26, + 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x24, 0x24, 0x24, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd5, 0x00, 0xc5, + 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x9f, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x83, + 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6f, 0x00, 0x69, 0x01, 0x66, 0x02, 0x61, + 0x02, 0x5e, 0x03, 0x5a, 0x04, 0x58, 0x04, 0x55, 0x05, 0x53, 0x05, 0x51, + 0x10, 0x7b, 0x10, 0x73, 0x10, 0x6c, 0x10, 0x66, 0x10, 0x60, 0x10, 0x5b, + 0x10, 0x56, 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4b, 0x11, 0x48, 0x11, 0x45, + 0x11, 0x44, 0x12, 0x41, 0x12, 0x40, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, + 0x13, 0x3a, 0x13, 0x39, 0x00, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x2e, 0x00, + 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, + 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x00, 0x6f, 0x02, 0x27, 0x12, 0x02, 0x25, 0x00, 0x2f, 0x00, 0x35, 0x00, + 0x38, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, + 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x30, 0x00, 0x39, 0x00, 0x42, 0x00, + 0x41, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, + 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x00, 0x7f, 0x00, 0x3f, 0x05, 0x05, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, + 0x35, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3c, 0x37, 0x38, 0x33, + 0x34, 0x31, 0x32, 0x2f, 0x30, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x29, + 0x2b, 0x28, 0x29, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x25, + 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xd5, 0x00, 0xc8, 0x00, 0xbb, 0x00, 0xaf, + 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x92, 0x00, 0x8a, 0x00, 0x82, 0x00, 0x7c, + 0x00, 0x76, 0x00, 0x70, 0x00, 0x6c, 0x01, 0x67, 0x01, 0x64, 0x02, 0x60, + 0x02, 0x5d, 0x04, 0x5b, 0x04, 0x58, 0x04, 0x55, 0x10, 0x7b, 0x10, 0x75, + 0x10, 0x6e, 0x10, 0x68, 0x10, 0x63, 0x10, 0x5e, 0x10, 0x5a, 0x10, 0x56, + 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4c, 0x11, 0x49, 0x11, 0x47, 0x11, 0x44, + 0x11, 0x43, 0x12, 0x41, 0x12, 0x3f, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, 0x00, 0x24, 0x00, 0x2c, 0x00, + 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, + 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0x9f, 0x00, 0x6f, + 0x00, 0x22, 0x0b, 0x09, 0x16, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, + 0x33, 0x00, 0x35, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, + 0x3b, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x39, 0x00, 0x16, 0x00, 0x28, 0x00, 0x2f, 0x00, 0x2e, 0x00, + 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, + 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0xa5, 0x00, 0x7f, + 0x00, 0x3f, 0x00, 0x12, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, + 0x3a, 0x00, 0x3a, 0x00, 0x3d, 0x38, 0x39, 0x34, 0x35, 0x32, 0x33, 0x30, + 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x29, 0x2b, 0x28, + 0x29, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, + 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x25, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd6, 0x00, 0xca, 0x00, 0xbe, 0x00, 0xb3, 0x00, 0xaa, 0x00, 0xa0, + 0x00, 0x98, 0x00, 0x90, 0x00, 0x88, 0x00, 0x82, 0x00, 0x7d, 0x00, 0x77, + 0x00, 0x71, 0x00, 0x6d, 0x01, 0x69, 0x01, 0x66, 0x02, 0x63, 0x02, 0x60, + 0x02, 0x5d, 0x04, 0x5b, 0x10, 0x7c, 0x10, 0x76, 0x10, 0x70, 0x10, 0x6a, + 0x10, 0x65, 0x10, 0x61, 0x10, 0x5d, 0x10, 0x59, 0x10, 0x55, 0x10, 0x52, + 0x10, 0x4f, 0x11, 0x4c, 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x11, 0x44, + 0x12, 0x42, 0x12, 0x41, 0x12, 0x3f, 0x13, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, + 0x39, 0x00, 0x3a, 0x00, 0x00, 0xaf, 0x00, 0x93, 0x00, 0x58, 0x00, 0x21, + 0x08, 0x0e, 0x0e, 0x02, 0x18, 0x00, 0x20, 0x00, 0x27, 0x00, 0x2b, 0x00, + 0x2f, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00, 0x36, 0x00, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x42, 0x00, + 0x28, 0x00, 0x09, 0x00, 0x16, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, + 0x39, 0x00, 0x3a, 0x00, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, + 0x00, 0x1d, 0x00, 0x05, 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, + 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x3d, 0x38, 0x39, 0x35, 0x36, 0x33, 0x33, 0x30, 0x31, 0x2f, 0x30, 0x2d, + 0x2e, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x2b, 0x28, 0x28, 0x28, + 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, + 0x27, 0x25, 0x25, 0x25, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd6, 0x00, 0xcc, + 0x00, 0xc0, 0x00, 0xb7, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x96, + 0x00, 0x8f, 0x00, 0x88, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x77, 0x00, 0x73, + 0x00, 0x6f, 0x00, 0x6b, 0x01, 0x67, 0x01, 0x64, 0x02, 0x62, 0x02, 0x5f, + 0x10, 0x7c, 0x10, 0x77, 0x10, 0x71, 0x10, 0x6c, 0x10, 0x67, 0x10, 0x63, + 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x58, 0x10, 0x55, 0x10, 0x51, 0x10, 0x4f, + 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x11, 0x44, 0x11, 0x43, + 0x12, 0x42, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, 0x00, + 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, 0x00, + 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x7a, 0x00, 0x4a, 0x00, 0x20, 0x06, 0x12, + 0x0c, 0x07, 0x10, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x24, 0x00, 0x28, 0x00, + 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x41, 0x00, 0x2f, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, 0x00, + 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, 0x00, + 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, + 0x00, 0x0f, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, + 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, 0x00, 0x3d, 0x39, 0x3a, 0x36, + 0x36, 0x33, 0x34, 0x31, 0x32, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2b, + 0x2d, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x27, + 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xd7, 0x00, 0xcc, 0x00, 0xc3, 0x00, 0xba, + 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8d, + 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x74, 0x00, 0x70, + 0x00, 0x6c, 0x01, 0x69, 0x01, 0x66, 0x02, 0x63, 0x10, 0x7c, 0x10, 0x77, + 0x10, 0x72, 0x10, 0x6e, 0x10, 0x69, 0x10, 0x65, 0x10, 0x61, 0x10, 0x5d, + 0x10, 0x5a, 0x10, 0x57, 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, + 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x11, 0x44, 0x12, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, + 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0xb9, 0x00, 0xad, + 0x00, 0x8f, 0x00, 0x68, 0x00, 0x42, 0x00, 0x20, 0x05, 0x14, 0x0a, 0x0b, + 0x0d, 0x04, 0x12, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x26, 0x00, + 0x29, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x3d, 0x00, 0x2e, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, + 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0xba, 0x00, 0xb1, + 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, + 0x00, 0x08, 0x02, 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, + 0x22, 0x00, 0x25, 0x00, 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x32, + 0x33, 0x30, 0x31, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2a, 0x2c, 0x2b, + 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, + 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbc, 0x00, 0xb4, 0x00, 0xac, + 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x91, 0x00, 0x8c, 0x00, 0x87, + 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x75, 0x00, 0x71, 0x00, 0x6d, + 0x01, 0x6b, 0x01, 0x68, 0x10, 0x7c, 0x10, 0x78, 0x10, 0x73, 0x10, 0x6f, + 0x10, 0x6b, 0x10, 0x67, 0x10, 0x63, 0x10, 0x60, 0x10, 0x5c, 0x10, 0x59, + 0x10, 0x57, 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, + 0x11, 0x49, 0x11, 0x47, 0x11, 0x46, 0x11, 0x45, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, + 0x2b, 0x00, 0x2d, 0x00, 0x00, 0xba, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x7d, + 0x00, 0x5c, 0x00, 0x3c, 0x00, 0x20, 0x04, 0x16, 0x08, 0x0e, 0x0c, 0x07, + 0x0e, 0x02, 0x13, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3a, 0x00, + 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, + 0x2b, 0x00, 0x2d, 0x00, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, + 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, + 0x00, 0x04, 0x04, 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, + 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x33, 0x33, 0x30, 0x32, 0x30, + 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2c, 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x2a, + 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd8, 0x00, 0xcf, + 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb6, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, + 0x00, 0x9c, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, 0x86, 0x00, 0x81, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x75, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6b, + 0x10, 0x7d, 0x10, 0x78, 0x10, 0x74, 0x10, 0x70, 0x10, 0x6c, 0x10, 0x68, + 0x10, 0x65, 0x10, 0x61, 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x59, 0x10, 0x56, + 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x4a, + 0x11, 0x48, 0x11, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, + 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, 0x00, + 0x00, 0xbc, 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x6f, 0x00, 0x52, + 0x00, 0x37, 0x00, 0x20, 0x04, 0x17, 0x07, 0x10, 0x0a, 0x0a, 0x0d, 0x05, + 0x0f, 0x00, 0x14, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, + 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, + 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, 0x00, + 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, + 0x00, 0x53, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, + 0x00, 0x01, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x3d, 0x3a, 0x3a, 0x37, + 0x38, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x2f, 0x30, 0x2d, + 0x2e, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, + 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, 0x27, 0x27, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc8, 0x00, 0xc0, + 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa5, 0x00, 0x9f, 0x00, 0x99, + 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8a, 0x00, 0x86, 0x00, 0x81, 0x00, 0x7d, + 0x00, 0x79, 0x00, 0x76, 0x00, 0x73, 0x00, 0x70, 0x10, 0x7d, 0x10, 0x79, + 0x10, 0x75, 0x10, 0x71, 0x10, 0x6d, 0x10, 0x6a, 0x10, 0x66, 0x10, 0x63, + 0x10, 0x60, 0x10, 0x5d, 0x10, 0x5b, 0x10, 0x58, 0x10, 0x56, 0x10, 0x54, + 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x49, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, + 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0xbc, 0x00, 0xb8, + 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7e, 0x00, 0x64, 0x00, 0x4c, 0x00, 0x34, + 0x00, 0x20, 0x03, 0x18, 0x06, 0x11, 0x09, 0x0c, 0x0c, 0x07, 0x0e, 0x03, + 0x10, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, + 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, + 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0xbd, 0x00, 0xb9, + 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, + 0x00, 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, + 0x00, 0x00, 0x07, 0x00, 0x3d, 0x3a, 0x3a, 0x38, 0x38, 0x36, 0x36, 0x33, + 0x34, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x28, 0x27, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc2, 0x00, 0xbb, 0x00, 0xb4, + 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x92, + 0x00, 0x8e, 0x00, 0x89, 0x00, 0x85, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, + 0x00, 0x76, 0x00, 0x73, 0x10, 0x7d, 0x10, 0x79, 0x10, 0x75, 0x10, 0x72, + 0x10, 0x6e, 0x10, 0x6b, 0x10, 0x68, 0x10, 0x65, 0x10, 0x62, 0x10, 0x5f, + 0x10, 0x5c, 0x10, 0x5a, 0x10, 0x58, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, + 0x10, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, 0x4a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, + 0x1a, 0x00, 0x1d, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9d, + 0x00, 0x89, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, + 0x03, 0x19, 0x06, 0x13, 0x08, 0x0e, 0x0b, 0x09, 0x0d, 0x05, 0x0e, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, + 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, + 0x1a, 0x00, 0x1d, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, + 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, + 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, + 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x36, 0x34, 0x35, 0x33, 0x33, 0x31, + 0x32, 0x30, 0x30, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2c, 0x2d, 0x2a, + 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd9, 0x00, 0xd1, + 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, + 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8d, + 0x00, 0x89, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x77, + 0x10, 0x7d, 0x10, 0x79, 0x10, 0x76, 0x10, 0x72, 0x10, 0x6f, 0x10, 0x6c, + 0x10, 0x69, 0x10, 0x66, 0x10, 0x63, 0x10, 0x61, 0x10, 0x5e, 0x10, 0x5c, + 0x10, 0x59, 0x10, 0x57, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, + 0x11, 0x4e, 0x11, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, + 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x92, 0x00, 0x7e, + 0x00, 0x6a, 0x00, 0x56, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x03, 0x19, + 0x05, 0x14, 0x08, 0x0f, 0x0a, 0x0b, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, + 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, + 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, + 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, + 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, 0x3d, 0x3b, 0x3b, 0x39, + 0x3a, 0x36, 0x36, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x30, + 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc4, + 0x00, 0xbe, 0x00, 0xb8, 0x00, 0xb2, 0x00, 0xad, 0x00, 0xa8, 0x00, 0xa2, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x94, 0x00, 0x90, 0x00, 0x8b, 0x00, 0x88, + 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7b, 0x10, 0x7d, 0x10, 0x7a, + 0x10, 0x76, 0x10, 0x73, 0x10, 0x70, 0x10, 0x6d, 0x10, 0x6a, 0x10, 0x67, + 0x10, 0x65, 0x10, 0x62, 0x10, 0x5f, 0x10, 0x5d, 0x10, 0x5b, 0x10, 0x59, + 0x10, 0x56, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0xbd, 0x00, 0xbb, + 0x00, 0xb3, 0x00, 0xa8, 0x00, 0x99, 0x00, 0x87, 0x00, 0x75, 0x00, 0x62, + 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x02, 0x1a, 0x05, 0x15, + 0x07, 0x10, 0x09, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, + 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0xbe, 0x00, 0xbc, + 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, + 0x00, 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, + 0x00, 0x21, 0x00, 0x19, 0x3d, 0x3b, 0x3b, 0x39, 0x39, 0x36, 0x37, 0x35, + 0x36, 0x33, 0x33, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2f, 0x30, 0x2d, + 0x2e, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcc, 0x00, 0xc6, 0x00, 0xc0, 0x00, 0xba, + 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9b, + 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x00, 0x8b, 0x00, 0x88, 0x00, 0x83, + 0x00, 0x81, 0x00, 0x7d, 0x10, 0x7d, 0x10, 0x7a, 0x10, 0x77, 0x10, 0x74, + 0x10, 0x71, 0x10, 0x6e, 0x10, 0x6b, 0x10, 0x68, 0x10, 0x65, 0x10, 0x63, + 0x10, 0x61, 0x10, 0x5e, 0x10, 0x5c, 0x10, 0x5a, 0x10, 0x58, 0x10, 0x56, + 0x10, 0x55, 0x10, 0x52, 0x10, 0x51, 0x10, 0x4f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x0d, 0x00, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xab, + 0x00, 0x9e, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, + 0x00, 0x3c, 0x00, 0x2d, 0x00, 0x1f, 0x02, 0x1a, 0x04, 0x16, 0x06, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, + 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, 0x00, + 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x0d, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, + 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, + 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, + 0x27, 0x76, 0x1a, 0x79, 0x16, 0x7b, 0x15, 0x7c, 0x14, 0x7c, 0x13, 0x7d, + 0x13, 0x7d, 0x13, 0x7d, 0x12, 0x7d, 0x12, 0x7d, 0x12, 0x7e, 0x12, 0x7e, + 0x12, 0x7e, 0x12, 0x7e, 0x12, 0x7e, 0x11, 0x7e, 0x11, 0x7e, 0x11, 0x7e, + 0x11, 0x7f, 0x11, 0x7f, 0x13, 0x5f, 0x10, 0x6f, 0x10, 0x74, 0x10, 0x77, + 0x10, 0x78, 0x10, 0x7a, 0x10, 0x7a, 0x10, 0x7b, 0x10, 0x7b, 0x10, 0x7c, + 0x10, 0x7c, 0x10, 0x7c, 0x10, 0x7c, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, + 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x00, 0x90, 0x02, 0x81, + 0x06, 0x80, 0x09, 0x7f, 0x0a, 0x7f, 0x0b, 0x7f, 0x0c, 0x7f, 0x0d, 0x7f, + 0x0d, 0x7f, 0x0d, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, + 0x0e, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x95, + 0x00, 0x86, 0x00, 0x77, 0x00, 0x67, 0x00, 0x57, 0x00, 0x48, 0x00, 0x3a, + 0x00, 0x2c, 0x00, 0x1f, 0x02, 0x1b, 0x04, 0x16, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, + 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, 0x00, + 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, + 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, + 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, 0x32, 0x5f, 0x25, 0x68, + 0x1f, 0x6d, 0x1c, 0x71, 0x19, 0x72, 0x18, 0x74, 0x17, 0x76, 0x16, 0x77, + 0x16, 0x77, 0x15, 0x78, 0x15, 0x79, 0x15, 0x79, 0x14, 0x7a, 0x14, 0x7a, + 0x13, 0x7a, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, + 0x17, 0x46, 0x11, 0x58, 0x10, 0x63, 0x10, 0x69, 0x10, 0x6d, 0x10, 0x70, + 0x10, 0x72, 0x10, 0x73, 0x10, 0x75, 0x10, 0x76, 0x10, 0x77, 0x10, 0x77, + 0x10, 0x78, 0x10, 0x78, 0x10, 0x79, 0x10, 0x79, 0x10, 0x79, 0x10, 0x7a, + 0x10, 0x7a, 0x10, 0x7a, 0x00, 0xbd, 0x00, 0xa4, 0x00, 0x97, 0x02, 0x91, + 0x04, 0x8d, 0x05, 0x8a, 0x06, 0x89, 0x07, 0x88, 0x08, 0x86, 0x09, 0x86, + 0x09, 0x85, 0x0a, 0x85, 0x0a, 0x84, 0x0b, 0x84, 0x0b, 0x83, 0x0b, 0x83, + 0x0c, 0x83, 0x0c, 0x83, 0x0c, 0x82, 0x0c, 0x82, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x00, 0xbc, + 0x00, 0xb8, 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9a, 0x00, 0x8d, 0x00, 0x7f, + 0x00, 0x70, 0x00, 0x61, 0x00, 0x53, 0x00, 0x45, 0x00, 0x38, 0x00, 0x2b, + 0x00, 0x1f, 0x02, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, + 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, + 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x00, 0xbd, + 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, + 0x00, 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, + 0x00, 0x3f, 0x00, 0x36, 0x36, 0x56, 0x2a, 0x5f, 0x24, 0x65, 0x20, 0x68, + 0x1e, 0x6c, 0x1c, 0x6e, 0x1b, 0x70, 0x19, 0x71, 0x18, 0x72, 0x18, 0x73, + 0x18, 0x75, 0x17, 0x75, 0x16, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, 0x77, + 0x15, 0x77, 0x15, 0x77, 0x15, 0x78, 0x15, 0x79, 0x1a, 0x39, 0x13, 0x4a, + 0x11, 0x55, 0x10, 0x5e, 0x10, 0x63, 0x10, 0x67, 0x10, 0x6a, 0x10, 0x6c, + 0x10, 0x6e, 0x10, 0x70, 0x10, 0x71, 0x10, 0x72, 0x10, 0x73, 0x10, 0x74, + 0x10, 0x75, 0x10, 0x75, 0x10, 0x76, 0x10, 0x76, 0x10, 0x77, 0x10, 0x77, + 0x00, 0xcc, 0x00, 0xb5, 0x00, 0xa8, 0x00, 0x9e, 0x01, 0x99, 0x02, 0x94, + 0x03, 0x92, 0x04, 0x8f, 0x05, 0x8d, 0x06, 0x8c, 0x06, 0x8b, 0x07, 0x8a, + 0x07, 0x89, 0x08, 0x88, 0x08, 0x88, 0x09, 0x87, 0x09, 0x87, 0x09, 0x86, + 0x0a, 0x86, 0x0a, 0x86, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb2, + 0x00, 0xa9, 0x00, 0x9e, 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6a, + 0x00, 0x5c, 0x00, 0x4f, 0x00, 0x42, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, + 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, 0x00, + 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, + 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, + 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, + 0x38, 0x52, 0x2e, 0x59, 0x28, 0x5f, 0x24, 0x63, 0x22, 0x67, 0x1f, 0x69, + 0x1e, 0x6b, 0x1c, 0x6d, 0x1b, 0x6e, 0x1a, 0x70, 0x1a, 0x71, 0x19, 0x72, + 0x18, 0x72, 0x18, 0x72, 0x18, 0x73, 0x17, 0x75, 0x17, 0x76, 0x16, 0x76, + 0x15, 0x76, 0x15, 0x76, 0x1b, 0x33, 0x15, 0x41, 0x12, 0x4c, 0x11, 0x54, + 0x10, 0x5b, 0x10, 0x5f, 0x10, 0x63, 0x10, 0x66, 0x10, 0x68, 0x10, 0x6a, + 0x10, 0x6c, 0x10, 0x6e, 0x10, 0x6f, 0x10, 0x70, 0x10, 0x71, 0x10, 0x72, + 0x10, 0x72, 0x10, 0x73, 0x10, 0x74, 0x10, 0x74, 0x00, 0xd1, 0x00, 0xc0, + 0x00, 0xb3, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9d, 0x01, 0x99, 0x02, 0x96, + 0x02, 0x94, 0x03, 0x92, 0x04, 0x90, 0x05, 0x8f, 0x05, 0x8d, 0x06, 0x8d, + 0x06, 0x8c, 0x07, 0x8b, 0x07, 0x8a, 0x07, 0x8a, 0x07, 0x89, 0x08, 0x89, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x47, 0x0f, 0x0f, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, + 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x00, 0x7f, 0x00, 0xa5, 0x00, 0xb2, + 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, + 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x4f, 0x31, 0x55, + 0x2b, 0x5b, 0x27, 0x5f, 0x24, 0x63, 0x22, 0x65, 0x20, 0x68, 0x1f, 0x69, + 0x1d, 0x6b, 0x1d, 0x6d, 0x1b, 0x6d, 0x1a, 0x6e, 0x1a, 0x6f, 0x1a, 0x71, + 0x19, 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x73, 0x18, 0x74, + 0x1c, 0x2f, 0x16, 0x3b, 0x13, 0x44, 0x11, 0x4d, 0x11, 0x53, 0x10, 0x59, + 0x10, 0x5d, 0x10, 0x60, 0x10, 0x63, 0x10, 0x65, 0x10, 0x67, 0x10, 0x69, + 0x10, 0x6b, 0x10, 0x6c, 0x10, 0x6d, 0x10, 0x6e, 0x10, 0x6f, 0x10, 0x70, + 0x10, 0x71, 0x10, 0x71, 0x00, 0xd5, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xb2, + 0x00, 0xaa, 0x00, 0xa4, 0x00, 0xa0, 0x01, 0x9c, 0x01, 0x9a, 0x02, 0x97, + 0x02, 0x95, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, 0x05, 0x90, 0x05, 0x8e, + 0x06, 0x8e, 0x06, 0x8d, 0x06, 0x8c, 0x06, 0x8c, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x3f, + 0x05, 0x05, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, 0x00, + 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, + 0x3d, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0f, 0x00, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, + 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, + 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x4c, 0x33, 0x53, 0x2e, 0x58, 0x29, 0x5c, + 0x27, 0x5f, 0x24, 0x62, 0x22, 0x64, 0x20, 0x66, 0x20, 0x68, 0x1e, 0x69, + 0x1d, 0x6a, 0x1d, 0x6c, 0x1c, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x1a, 0x6f, + 0x1a, 0x71, 0x19, 0x72, 0x18, 0x72, 0x18, 0x72, 0x1d, 0x2d, 0x17, 0x37, + 0x14, 0x40, 0x12, 0x47, 0x11, 0x4e, 0x11, 0x53, 0x10, 0x57, 0x10, 0x5b, + 0x10, 0x5e, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x67, 0x10, 0x68, + 0x10, 0x6a, 0x10, 0x6b, 0x10, 0x6c, 0x10, 0x6d, 0x10, 0x6e, 0x10, 0x6f, + 0x00, 0xd6, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xab, + 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9f, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x98, + 0x02, 0x96, 0x03, 0x94, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, 0x05, 0x90, + 0x05, 0x8f, 0x05, 0x8f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, + 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, 0x00, + 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x05, 0x05, + 0x00, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, + 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, + 0x00, 0xb9, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x4b, 0x34, 0x51, 0x2f, 0x55, 0x2c, 0x59, 0x29, 0x5c, 0x26, 0x60, + 0x24, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x67, 0x1f, 0x69, 0x1d, 0x69, + 0x1d, 0x6a, 0x1d, 0x6c, 0x1c, 0x6d, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, + 0x1a, 0x6f, 0x1a, 0x71, 0x1e, 0x2b, 0x18, 0x34, 0x15, 0x3b, 0x13, 0x43, + 0x12, 0x49, 0x11, 0x4e, 0x11, 0x52, 0x10, 0x56, 0x10, 0x5a, 0x10, 0x5d, + 0x10, 0x5f, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x66, 0x10, 0x68, + 0x10, 0x69, 0x10, 0x6a, 0x10, 0x6b, 0x10, 0x6c, 0x00, 0xd7, 0x00, 0xcd, + 0x00, 0xc5, 0x00, 0xbd, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa7, + 0x00, 0xa4, 0x00, 0xa1, 0x01, 0x9e, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x98, + 0x02, 0x97, 0x02, 0x96, 0x03, 0x94, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, + 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1c, 0x00, 0x00, 0x12, 0x00, 0x3f, + 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, + 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x49, 0x36, 0x4f, + 0x31, 0x54, 0x2d, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x26, 0x60, 0x24, 0x61, + 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x69, + 0x1d, 0x69, 0x1d, 0x6b, 0x1c, 0x6d, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, + 0x1e, 0x2a, 0x19, 0x32, 0x16, 0x39, 0x13, 0x3f, 0x12, 0x45, 0x11, 0x4a, + 0x11, 0x4e, 0x11, 0x52, 0x10, 0x56, 0x10, 0x59, 0x10, 0x5c, 0x10, 0x5d, + 0x10, 0x60, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x66, 0x10, 0x67, + 0x10, 0x68, 0x10, 0x69, 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc7, 0x00, 0xc0, + 0x00, 0xba, 0x00, 0xb5, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa8, 0x00, 0xa5, + 0x00, 0xa2, 0x00, 0xa0, 0x01, 0x9e, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x99, + 0x02, 0x97, 0x02, 0x96, 0x03, 0x95, 0x03, 0x94, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, + 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, + 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, + 0x2b, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x2a, 0x00, 0x09, 0x00, 0x00, 0x1d, 0x00, 0x3f, 0x00, 0x5b, + 0x00, 0x6f, 0x00, 0x7f, 0x00, 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, + 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x48, 0x37, 0x4d, 0x32, 0x51, 0x2f, 0x55, + 0x2c, 0x58, 0x29, 0x5b, 0x27, 0x5d, 0x26, 0x60, 0x24, 0x60, 0x23, 0x63, + 0x22, 0x65, 0x20, 0x65, 0x20, 0x66, 0x1f, 0x68, 0x1e, 0x69, 0x1d, 0x69, + 0x1d, 0x69, 0x1d, 0x6b, 0x1c, 0x6d, 0x1b, 0x6d, 0x1e, 0x29, 0x1a, 0x30, + 0x17, 0x36, 0x14, 0x3c, 0x13, 0x41, 0x12, 0x46, 0x11, 0x4a, 0x11, 0x4f, + 0x11, 0x52, 0x10, 0x55, 0x10, 0x58, 0x10, 0x5a, 0x10, 0x5c, 0x10, 0x5f, + 0x10, 0x60, 0x10, 0x62, 0x10, 0x63, 0x10, 0x65, 0x10, 0x65, 0x10, 0x67, + 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xb9, + 0x00, 0xb4, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa3, + 0x00, 0xa1, 0x01, 0x9f, 0x01, 0x9d, 0x01, 0x9b, 0x02, 0x9a, 0x02, 0x99, + 0x02, 0x98, 0x02, 0x97, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, + 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, 0x00, 0x08, 0x02, 0x00, + 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x31, 0x00, + 0x19, 0x00, 0x00, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, + 0x00, 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, + 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x48, 0x37, 0x4c, 0x33, 0x50, 0x30, 0x54, 0x2d, 0x57, 0x2a, 0x59, + 0x29, 0x5c, 0x27, 0x5d, 0x26, 0x60, 0x24, 0x60, 0x23, 0x62, 0x22, 0x65, + 0x20, 0x65, 0x20, 0x65, 0x20, 0x67, 0x1f, 0x69, 0x1d, 0x69, 0x1d, 0x69, + 0x1d, 0x69, 0x1d, 0x6a, 0x1f, 0x28, 0x1a, 0x2e, 0x17, 0x34, 0x15, 0x39, + 0x13, 0x3f, 0x13, 0x43, 0x12, 0x48, 0x11, 0x4b, 0x11, 0x4f, 0x11, 0x52, + 0x10, 0x55, 0x10, 0x57, 0x10, 0x59, 0x10, 0x5c, 0x10, 0x5d, 0x10, 0x5f, + 0x10, 0x61, 0x10, 0x62, 0x10, 0x63, 0x10, 0x64, 0x00, 0xd9, 0x00, 0xd2, + 0x00, 0xcc, 0x00, 0xc6, 0x00, 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb3, + 0x00, 0xaf, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa2, + 0x00, 0xa0, 0x01, 0x9e, 0x01, 0x9d, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, + 0x00, 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x04, 0x00, + 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x00, + 0x00, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, + 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x47, 0x38, 0x4b, + 0x34, 0x4f, 0x31, 0x53, 0x2e, 0x55, 0x2c, 0x57, 0x2a, 0x5a, 0x28, 0x5c, + 0x27, 0x5d, 0x26, 0x60, 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, + 0x20, 0x65, 0x20, 0x65, 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x69, 0x1d, 0x69, + 0x1f, 0x27, 0x1b, 0x2d, 0x18, 0x33, 0x16, 0x38, 0x14, 0x3c, 0x13, 0x41, + 0x12, 0x44, 0x11, 0x48, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, + 0x10, 0x57, 0x10, 0x59, 0x10, 0x5b, 0x10, 0x5c, 0x10, 0x5e, 0x10, 0x5f, + 0x10, 0x61, 0x10, 0x62, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc8, + 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xba, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, + 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa3, 0x00, 0xa1, + 0x01, 0xa0, 0x01, 0x9e, 0x01, 0x9d, 0x02, 0x9c, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb7, + 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, + 0x00, 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, + 0x0c, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, + 0x00, 0x2c, 0x00, 0x3f, 0x00, 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, + 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x47, 0x38, 0x4b, 0x35, 0x4e, 0x32, 0x51, + 0x2f, 0x53, 0x2d, 0x57, 0x2b, 0x58, 0x2a, 0x5b, 0x27, 0x5c, 0x27, 0x5d, + 0x25, 0x60, 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, + 0x20, 0x65, 0x20, 0x66, 0x1f, 0x69, 0x1e, 0x69, 0x1f, 0x27, 0x1b, 0x2c, + 0x18, 0x31, 0x16, 0x36, 0x14, 0x3a, 0x13, 0x3e, 0x13, 0x42, 0x12, 0x45, + 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x56, + 0x10, 0x58, 0x10, 0x5a, 0x10, 0x5c, 0x10, 0x5d, 0x10, 0x5e, 0x10, 0x60, + 0x00, 0xd9, 0x00, 0xd4, 0x00, 0xce, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xc1, + 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, + 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa2, 0x00, 0xa0, + 0x01, 0x9f, 0x01, 0x9e, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, + 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, + 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x39, 0x00, + 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x2e, + 0x00, 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, + 0x00, 0x80, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x46, 0x39, 0x4a, 0x35, 0x4d, 0x32, 0x50, 0x30, 0x53, 0x2e, 0x55, + 0x2c, 0x57, 0x2a, 0x58, 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5d, 0x25, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x63, 0x22, 0x65, 0x20, 0x65, 0x20, 0x65, + 0x20, 0x65, 0x20, 0x67, 0x1f, 0x27, 0x1c, 0x2c, 0x19, 0x30, 0x17, 0x35, + 0x15, 0x39, 0x14, 0x3d, 0x13, 0x40, 0x12, 0x44, 0x12, 0x47, 0x11, 0x49, + 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x56, 0x10, 0x58, + 0x10, 0x59, 0x10, 0x5b, 0x10, 0x5c, 0x10, 0x5e, 0x00, 0xda, 0x00, 0xd4, + 0x00, 0xd0, 0x00, 0xcb, 0x00, 0xc7, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xbb, + 0x00, 0xb8, 0x00, 0xb5, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, + 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa3, 0x00, 0xa1, 0x01, 0xa0, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, + 0x00, 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, + 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, + 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x46, 0x39, 0x49, + 0x36, 0x4c, 0x33, 0x4f, 0x31, 0x52, 0x2e, 0x53, 0x2d, 0x57, 0x2b, 0x57, + 0x2a, 0x59, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x62, 0x22, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, + 0x1f, 0x26, 0x1c, 0x2b, 0x19, 0x2f, 0x17, 0x33, 0x16, 0x37, 0x14, 0x3b, + 0x13, 0x3e, 0x13, 0x41, 0x12, 0x44, 0x11, 0x47, 0x11, 0x4a, 0x11, 0x4d, + 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x55, 0x10, 0x57, 0x10, 0x59, + 0x10, 0x5a, 0x10, 0x5b, 0x00, 0xda, 0x00, 0xd5, 0x00, 0xd1, 0x00, 0xcc, + 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb7, + 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa9, + 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa2, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, + 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, + 0x00, 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, + 0x00, 0x16, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x00, + 0x00, 0x04, 0x00, 0x14, 0x00, 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, + 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x39, 0x48, 0x36, 0x4b, 0x34, 0x4f, + 0x31, 0x50, 0x30, 0x53, 0x2e, 0x55, 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x5a, + 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x61, 0x22, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x26, 0x1c, 0x2a, + 0x1a, 0x2e, 0x18, 0x32, 0x16, 0x36, 0x14, 0x39, 0x13, 0x3d, 0x13, 0x40, + 0x12, 0x43, 0x12, 0x45, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, + 0x11, 0x51, 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, 0x10, 0x58, 0x10, 0x5a, + 0x00, 0xda, 0x00, 0xd6, 0x00, 0xd1, 0x00, 0xcd, 0x00, 0xc9, 0x00, 0xc6, + 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb3, + 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa7, + 0x00, 0xa6, 0x00, 0xa4, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, + 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, + 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, + 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, 0x0a, + 0x00, 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, + 0x00, 0x5d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x45, 0x3a, 0x48, 0x37, 0x4a, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x53, + 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, 0x2a, 0x58, 0x29, 0x5b, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x61, + 0x23, 0x64, 0x21, 0x65, 0x20, 0x26, 0x1d, 0x2a, 0x1a, 0x2d, 0x18, 0x31, + 0x17, 0x35, 0x15, 0x38, 0x14, 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x44, + 0x12, 0x46, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, + 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, 0x10, 0x58, 0x00, 0xda, 0x00, 0xd6, + 0x00, 0xd2, 0x00, 0xce, 0x00, 0xcb, 0x00, 0xc7, 0x00, 0xc4, 0x00, 0xc1, + 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, + 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0xa6, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, + 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, + 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, + 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x1c, + 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x3a, 0x47, + 0x38, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x54, + 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x59, 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x63, + 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2d, 0x18, 0x30, 0x17, 0x33, 0x16, 0x37, + 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x13, 0x42, 0x12, 0x44, 0x11, 0x47, + 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x53, + 0x10, 0x55, 0x10, 0x56, 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xcf, + 0x00, 0xcc, 0x00, 0xc8, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, + 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, + 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, + 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, + 0x00, 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, + 0x00, 0x36, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, + 0x12, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, + 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4c, + 0x33, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, + 0x2a, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5f, + 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x29, + 0x1b, 0x2c, 0x18, 0x2f, 0x17, 0x33, 0x16, 0x36, 0x14, 0x39, 0x14, 0x3b, + 0x13, 0x3e, 0x13, 0x41, 0x12, 0x43, 0x12, 0x45, 0x11, 0x47, 0x11, 0x4a, + 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x52, 0x10, 0x54, + 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xd0, 0x00, 0xcc, 0x00, 0xc9, + 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb9, + 0x00, 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, + 0x00, 0xab, 0x00, 0xaa, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, + 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, + 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, + 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, + 0x00, 0x3f, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x4a, 0x35, 0x4b, 0x34, 0x4f, 0x31, 0x4f, + 0x31, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x58, + 0x2a, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5f, 0x24, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x19, 0x2f, + 0x17, 0x32, 0x16, 0x35, 0x15, 0x38, 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, + 0x13, 0x42, 0x12, 0x44, 0x12, 0x46, 0x11, 0x48, 0x11, 0x4a, 0x11, 0x4c, + 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x52, 0x00, 0xdb, 0x00, 0xd7, + 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcd, 0x00, 0xca, 0x00, 0xc7, 0x00, 0xc5, + 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, + 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xac, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, + 0x00, 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, + 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, + 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, 0x00, 0x07, 0x00, 0x00, 0x03, + 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3b, 0x46, + 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x51, 0x2f, 0x53, + 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x29, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x26, 0x5f, 0x24, 0x60, 0x23, 0x60, + 0x20, 0x25, 0x1d, 0x28, 0x1c, 0x2b, 0x1a, 0x2e, 0x18, 0x31, 0x17, 0x34, + 0x16, 0x37, 0x14, 0x39, 0x13, 0x3b, 0x13, 0x3e, 0x13, 0x40, 0x12, 0x42, + 0x12, 0x45, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4a, 0x11, 0x4c, 0x11, 0x4e, + 0x11, 0x4f, 0x11, 0x51, 0x00, 0xdb, 0x00, 0xd8, 0x00, 0xd4, 0x00, 0xd1, + 0x00, 0xce, 0x00, 0xcb, 0x00, 0xc8, 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, + 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x56, 0x7f, 0x47, 0x63, + 0x44, 0x57, 0x43, 0x52, 0x43, 0x4e, 0x42, 0x4c, 0x42, 0x4a, 0x42, 0x49, + 0x42, 0x48, 0x42, 0x47, 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x45, + 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, + 0x4c, 0x7f, 0x27, 0x4d, 0x2f, 0x46, 0x33, 0x44, 0x36, 0x43, 0x37, 0x43, + 0x39, 0x42, 0x3a, 0x41, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, 0x3b, 0x41, + 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3d, 0x41, + 0x3d, 0x41, 0x3d, 0x41, 0x8e, 0x7f, 0x47, 0x4d, 0x44, 0x46, 0x43, 0x44, + 0x43, 0x43, 0x42, 0x43, 0x42, 0x42, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, + 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, + 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x85, 0x1b, 0x60, 0x27, 0x54, 0x2d, 0x4e, 0x30, + 0x4b, 0x33, 0x49, 0x34, 0x47, 0x35, 0x46, 0x37, 0x45, 0x38, 0x45, 0x38, + 0x44, 0x39, 0x43, 0x39, 0x43, 0x3a, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, + 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x09, 0x43, 0x16, 0x3f, + 0x1f, 0x3e, 0x25, 0x3e, 0x29, 0x3e, 0x2c, 0x3e, 0x2f, 0x3e, 0x30, 0x3e, + 0x32, 0x3e, 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x36, 0x3e, + 0x37, 0x3e, 0x37, 0x3e, 0x38, 0x3e, 0x38, 0x3e, 0x38, 0x3e, 0x39, 0x3e, + 0x59, 0x31, 0x4f, 0x38, 0x4b, 0x3a, 0x48, 0x3b, 0x47, 0x3c, 0x46, 0x3c, + 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x4f, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x96, 0x05, 0x74, 0x0f, 0x63, 0x17, 0x5a, 0x1d, 0x54, 0x21, 0x51, 0x24, + 0x4e, 0x27, 0x4c, 0x29, 0x4a, 0x2b, 0x49, 0x2d, 0x49, 0x2e, 0x48, 0x2f, + 0x48, 0x31, 0x47, 0x31, 0x46, 0x32, 0x46, 0x33, 0x44, 0x33, 0x44, 0x34, + 0x43, 0x34, 0x43, 0x35, 0x09, 0x43, 0x11, 0x3e, 0x18, 0x3c, 0x1d, 0x3c, + 0x22, 0x3c, 0x25, 0x3c, 0x27, 0x3c, 0x2a, 0x3c, 0x2c, 0x3d, 0x2d, 0x3d, + 0x2f, 0x3d, 0x30, 0x3e, 0x31, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, + 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x5b, 0x2a, 0x53, 0x31, + 0x4f, 0x34, 0x4c, 0x36, 0x4b, 0x38, 0x4a, 0x39, 0x48, 0x39, 0x47, 0x3a, + 0x46, 0x3b, 0x46, 0x3b, 0x46, 0x3c, 0x46, 0x3c, 0x46, 0x3d, 0x45, 0x3d, + 0x44, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x45, + 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8f, 0x00, 0x73, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x9e, 0x01, 0x81, 0x06, + 0x6f, 0x0d, 0x64, 0x12, 0x5d, 0x16, 0x58, 0x1a, 0x54, 0x1d, 0x53, 0x20, + 0x50, 0x22, 0x4e, 0x24, 0x4c, 0x26, 0x4b, 0x27, 0x4a, 0x29, 0x49, 0x2a, + 0x49, 0x2b, 0x49, 0x2c, 0x48, 0x2d, 0x48, 0x2e, 0x48, 0x2f, 0x47, 0x2f, + 0x09, 0x44, 0x0f, 0x3e, 0x14, 0x3c, 0x19, 0x3c, 0x1d, 0x3b, 0x20, 0x3b, + 0x23, 0x3b, 0x25, 0x3c, 0x27, 0x3c, 0x28, 0x3c, 0x2a, 0x3b, 0x2c, 0x3b, + 0x2c, 0x3b, 0x2d, 0x3b, 0x2f, 0x3c, 0x2f, 0x3c, 0x30, 0x3d, 0x31, 0x3e, + 0x32, 0x3e, 0x32, 0x3e, 0x5c, 0x28, 0x56, 0x2d, 0x52, 0x31, 0x4f, 0x33, + 0x4e, 0x35, 0x4b, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x49, 0x39, 0x47, 0x39, + 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x3a, 0x46, 0x3a, 0x46, 0x3b, + 0x46, 0x3c, 0x46, 0x3c, 0x46, 0x3d, 0x45, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x84, 0x00, + 0x5b, 0x00, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xa4, 0x00, 0x8a, 0x03, 0x78, 0x07, 0x6d, 0x0c, + 0x65, 0x10, 0x60, 0x13, 0x5b, 0x16, 0x57, 0x19, 0x55, 0x1b, 0x53, 0x1e, + 0x52, 0x1f, 0x4f, 0x22, 0x4d, 0x23, 0x4c, 0x24, 0x4b, 0x25, 0x4b, 0x27, + 0x4a, 0x28, 0x49, 0x29, 0x49, 0x29, 0x49, 0x2b, 0x09, 0x45, 0x0d, 0x40, + 0x11, 0x3d, 0x16, 0x3c, 0x19, 0x3b, 0x1d, 0x3b, 0x1f, 0x3b, 0x21, 0x3a, + 0x23, 0x3b, 0x25, 0x3b, 0x26, 0x3c, 0x28, 0x3c, 0x29, 0x3c, 0x2a, 0x3b, + 0x2b, 0x3b, 0x2c, 0x3b, 0x2d, 0x3b, 0x2e, 0x3b, 0x2e, 0x3b, 0x2f, 0x3b, + 0x5d, 0x27, 0x57, 0x2c, 0x54, 0x2f, 0x52, 0x31, 0x4f, 0x32, 0x4e, 0x34, + 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x48, 0x39, + 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, + 0x46, 0x39, 0x46, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5b, 0x00, 0x55, 0x00, 0x42, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x8d, 0x00, 0x6f, 0x00, 0x48, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xa7, 0x00, 0x91, 0x01, 0x80, 0x04, 0x75, 0x08, 0x6c, 0x0b, 0x65, 0x0e, + 0x61, 0x11, 0x5d, 0x14, 0x59, 0x16, 0x56, 0x19, 0x55, 0x1a, 0x54, 0x1c, + 0x53, 0x1e, 0x51, 0x1f, 0x4f, 0x21, 0x4d, 0x22, 0x4c, 0x23, 0x4b, 0x24, + 0x4b, 0x25, 0x4b, 0x26, 0x09, 0x45, 0x0d, 0x41, 0x10, 0x3e, 0x14, 0x3d, + 0x17, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, 0x3b, 0x20, 0x3b, 0x22, 0x3a, + 0x23, 0x3a, 0x25, 0x3b, 0x26, 0x3c, 0x28, 0x3c, 0x28, 0x3c, 0x29, 0x3c, + 0x2a, 0x3c, 0x2b, 0x3b, 0x2c, 0x3b, 0x2d, 0x3b, 0x5d, 0x26, 0x59, 0x2a, + 0x56, 0x2d, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, + 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x37, 0x4a, 0x38, 0x49, 0x39, + 0x48, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, + 0x00, 0x4a, 0x00, 0x38, 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa9, 0x00, 0x96, 0x00, + 0x86, 0x02, 0x7a, 0x05, 0x72, 0x08, 0x6b, 0x0b, 0x65, 0x0e, 0x62, 0x10, + 0x5f, 0x12, 0x5b, 0x14, 0x58, 0x16, 0x56, 0x18, 0x55, 0x1a, 0x54, 0x1b, + 0x53, 0x1d, 0x52, 0x1e, 0x50, 0x1f, 0x4f, 0x20, 0x4d, 0x21, 0x4c, 0x22, + 0x09, 0x46, 0x0c, 0x41, 0x0f, 0x3f, 0x12, 0x3d, 0x15, 0x3c, 0x17, 0x3c, + 0x1a, 0x3b, 0x1c, 0x3a, 0x1e, 0x3b, 0x1f, 0x3b, 0x21, 0x3a, 0x22, 0x3a, + 0x24, 0x3a, 0x25, 0x3a, 0x26, 0x3b, 0x27, 0x3c, 0x28, 0x3c, 0x29, 0x3c, + 0x2a, 0x3c, 0x2a, 0x3c, 0x5e, 0x26, 0x5a, 0x29, 0x57, 0x2c, 0x53, 0x2e, + 0x53, 0x30, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, 0x4a, 0x38, + 0x48, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, + 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, 0x00, + 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xab, 0x00, 0x9a, 0x00, 0x8b, 0x01, 0x7f, 0x03, + 0x77, 0x06, 0x71, 0x08, 0x6a, 0x0b, 0x66, 0x0d, 0x63, 0x0f, 0x60, 0x11, + 0x5d, 0x13, 0x59, 0x14, 0x57, 0x16, 0x56, 0x18, 0x55, 0x19, 0x54, 0x1b, + 0x53, 0x1c, 0x53, 0x1d, 0x51, 0x1e, 0x50, 0x1f, 0x0a, 0x46, 0x0c, 0x42, + 0x0e, 0x3f, 0x11, 0x3f, 0x13, 0x3c, 0x16, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, + 0x1c, 0x3a, 0x1e, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3a, 0x23, 0x3a, + 0x24, 0x3a, 0x25, 0x39, 0x26, 0x3a, 0x27, 0x3c, 0x27, 0x3c, 0x28, 0x3c, + 0x5e, 0x26, 0x5a, 0x29, 0x57, 0x2b, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x30, + 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x34, 0x4d, 0x35, 0x4b, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x38, + 0x4a, 0x39, 0x48, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, + 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, + 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xac, 0x00, 0x9d, 0x00, 0x8f, 0x01, 0x85, 0x02, 0x7b, 0x04, 0x75, 0x06, + 0x6f, 0x08, 0x69, 0x0b, 0x66, 0x0d, 0x63, 0x0e, 0x61, 0x10, 0x5e, 0x12, + 0x5b, 0x13, 0x58, 0x15, 0x57, 0x16, 0x56, 0x18, 0x55, 0x19, 0x55, 0x1a, + 0x54, 0x1b, 0x53, 0x1c, 0x0a, 0x47, 0x0b, 0x42, 0x0e, 0x40, 0x10, 0x3f, + 0x12, 0x3d, 0x15, 0x3c, 0x17, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, + 0x1d, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x23, 0x3a, + 0x24, 0x3a, 0x25, 0x39, 0x26, 0x3a, 0x27, 0x3b, 0x5e, 0x26, 0x5b, 0x28, + 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x35, 0x4b, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, + 0x00, 0x55, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, + 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xad, 0x00, 0xa0, 0x00, + 0x92, 0x00, 0x89, 0x02, 0x7f, 0x03, 0x78, 0x05, 0x73, 0x07, 0x6e, 0x09, + 0x69, 0x0b, 0x66, 0x0c, 0x64, 0x0e, 0x62, 0x0f, 0x60, 0x11, 0x5c, 0x12, + 0x59, 0x14, 0x58, 0x15, 0x57, 0x16, 0x56, 0x18, 0x55, 0x18, 0x55, 0x1a, + 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x41, 0x0f, 0x3f, 0x11, 0x3f, 0x13, 0x3c, + 0x15, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x3a, + 0x1f, 0x3b, 0x1f, 0x3b, 0x21, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x24, 0x3a, + 0x24, 0x3a, 0x24, 0x39, 0x5e, 0x26, 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2b, + 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2f, 0x52, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x32, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, + 0x00, 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, + 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x8b, 0x01, + 0x83, 0x02, 0x7b, 0x04, 0x76, 0x05, 0x73, 0x07, 0x6e, 0x09, 0x69, 0x0a, + 0x66, 0x0c, 0x64, 0x0d, 0x62, 0x0f, 0x61, 0x10, 0x5e, 0x11, 0x5b, 0x13, + 0x58, 0x14, 0x58, 0x15, 0x56, 0x16, 0x56, 0x18, 0x0a, 0x47, 0x0b, 0x43, + 0x0d, 0x42, 0x0f, 0x3f, 0x11, 0x3f, 0x12, 0x3e, 0x14, 0x3c, 0x16, 0x3c, + 0x17, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x3b, + 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3b, 0x21, 0x3a, 0x22, 0x3a, 0x24, 0x3a, + 0x5e, 0x26, 0x5c, 0x27, 0x59, 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, + 0x00, 0x33, 0x00, 0x28, 0x00, 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, + 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, 0x87, 0x02, 0x7f, 0x03, + 0x79, 0x04, 0x75, 0x06, 0x71, 0x07, 0x6c, 0x09, 0x68, 0x0a, 0x66, 0x0c, + 0x64, 0x0d, 0x63, 0x0e, 0x61, 0x10, 0x5f, 0x11, 0x5c, 0x12, 0x59, 0x13, + 0x58, 0x14, 0x58, 0x15, 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0e, 0x40, + 0x10, 0x3e, 0x11, 0x3f, 0x13, 0x3c, 0x15, 0x3b, 0x17, 0x3c, 0x17, 0x3c, + 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x3a, 0x1f, 0x3b, + 0x1f, 0x3b, 0x21, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x5e, 0x25, 0x5c, 0x27, + 0x5a, 0x29, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, + 0x00, 0x59, 0x00, 0x53, 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, + 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, + 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xaf, 0x00, 0xa5, 0x00, + 0x9a, 0x00, 0x90, 0x00, 0x8a, 0x01, 0x83, 0x02, 0x7c, 0x03, 0x77, 0x05, + 0x74, 0x06, 0x70, 0x07, 0x6c, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x64, 0x0c, + 0x63, 0x0e, 0x61, 0x0f, 0x60, 0x11, 0x5d, 0x11, 0x5b, 0x13, 0x59, 0x13, + 0x0a, 0x47, 0x0a, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3e, 0x11, 0x3f, + 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, + 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x3a, 0x1f, 0x3b, 0x1f, 0x3b, + 0x20, 0x3b, 0x21, 0x3b, 0x5e, 0x25, 0x5c, 0x27, 0x5a, 0x29, 0x57, 0x2a, + 0x57, 0x2a, 0x56, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x30, + 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, + 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, + 0x00, 0x4e, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, + 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, + 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, + 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x92, 0x00, + 0x8c, 0x01, 0x86, 0x02, 0x7e, 0x02, 0x7a, 0x04, 0x76, 0x05, 0x73, 0x06, + 0x70, 0x07, 0x6b, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0e, + 0x62, 0x0e, 0x61, 0x10, 0x5e, 0x11, 0x5c, 0x12, 0x0a, 0x47, 0x0a, 0x44, + 0x0c, 0x42, 0x0d, 0x41, 0x0f, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x13, 0x3d, + 0x15, 0x3c, 0x16, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, + 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x39, 0x1f, 0x3b, 0x1f, 0x3b, 0x1f, 0x3b, + 0x5f, 0x25, 0x5c, 0x27, 0x5b, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, + 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, + 0x4e, 0x34, 0x4c, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, + 0x00, 0x41, 0x00, 0x39, 0x00, 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, + 0x00, 0x0e, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, + 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, + 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xb0, 0x00, 0xa7, 0x00, 0x9f, 0x00, 0x95, 0x00, 0x8e, 0x00, 0x88, 0x01, + 0x82, 0x02, 0x7c, 0x03, 0x78, 0x05, 0x75, 0x06, 0x72, 0x07, 0x6f, 0x08, + 0x6a, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0d, 0x62, 0x0e, + 0x61, 0x0f, 0x60, 0x11, 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, + 0x0e, 0x40, 0x10, 0x3d, 0x11, 0x3f, 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3b, + 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, + 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x5f, 0x25, 0x5c, 0x27, + 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2d, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, + 0x00, 0x5b, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, + 0x00, 0x35, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, + 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, + 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa7, 0x00, + 0xa0, 0x00, 0x97, 0x00, 0x90, 0x00, 0x8a, 0x01, 0x85, 0x02, 0x7e, 0x02, + 0x7a, 0x03, 0x77, 0x05, 0x74, 0x06, 0x72, 0x07, 0x6e, 0x08, 0x6a, 0x09, + 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0d, 0x63, 0x0e, 0x61, 0x0f, + 0x0a, 0x47, 0x0a, 0x45, 0x0b, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3e, + 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3d, 0x15, 0x3c, 0x15, 0x3a, 0x17, 0x3c, + 0x17, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3a, + 0x1d, 0x39, 0x1f, 0x39, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x59, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x17, 0x00, 0x2f, + 0x00, 0x4c, 0x00, 0x56, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x5d, + 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, + 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x62, + 0x00, 0x62, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, + 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x14, 0x01, 0x29, + 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, + 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, + 0x00, 0x5f, 0x00, 0x5f, 0x31, 0x11, 0x03, 0x23, 0x00, 0x46, 0x00, 0x52, + 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, + 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x99, 0x00, + 0x91, 0x00, 0x8c, 0x00, 0x88, 0x01, 0x82, 0x02, 0x7c, 0x03, 0x79, 0x04, + 0x76, 0x05, 0x74, 0x06, 0x71, 0x07, 0x6d, 0x08, 0x6a, 0x09, 0x68, 0x0a, + 0x66, 0x0b, 0x65, 0x0c, 0x64, 0x0c, 0x63, 0x0e, 0x0a, 0x47, 0x0a, 0x45, + 0x0b, 0x42, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3e, 0x11, 0x3f, + 0x13, 0x3e, 0x13, 0x3c, 0x15, 0x3c, 0x16, 0x3a, 0x17, 0x3c, 0x17, 0x3c, + 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, + 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5a, 0x29, 0x57, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x45, + 0x00, 0x4f, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, + 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, + 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x48, 0x00, 0x55, 0x00, 0x58, 0x00, 0x57, + 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, + 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x16, 0x04, 0x00, 0x27, 0x00, 0x41, + 0x00, 0x4d, 0x00, 0x53, 0x00, 0x56, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, + 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, + 0x5f, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, + 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, + 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xb1, 0x00, 0xa9, 0x00, 0xa2, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x8e, 0x00, + 0x89, 0x01, 0x84, 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x78, 0x05, 0x75, 0x05, + 0x73, 0x06, 0x71, 0x07, 0x6c, 0x08, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, + 0x65, 0x0c, 0x64, 0x0c, 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x42, + 0x0e, 0x42, 0x0f, 0x40, 0x10, 0x3d, 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3d, + 0x15, 0x3c, 0x15, 0x3b, 0x17, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, + 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x5f, 0x25, 0x5c, 0x27, + 0x5c, 0x27, 0x5b, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x30, + 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x22, 0x00, 0x36, 0x00, 0x42, + 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, + 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5f, 0x00, 0x48, + 0x00, 0x1b, 0x00, 0x34, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4f, + 0x00, 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, + 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x57, 0x00, 0x12, 0x01, 0x00, 0x17, 0x00, 0x2f, 0x00, 0x3e, + 0x00, 0x47, 0x00, 0x4d, 0x00, 0x51, 0x00, 0x53, 0x00, 0x56, 0x00, 0x57, + 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x85, 0x00, 0x5f, 0x00, + 0x1f, 0x00, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, + 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, + 0x00, 0x59, 0x00, 0x5a, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb2, 0x00, 0xa9, 0x00, + 0xa3, 0x00, 0x9d, 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8b, 0x01, 0x87, 0x02, + 0x81, 0x02, 0x7c, 0x03, 0x79, 0x03, 0x77, 0x05, 0x75, 0x06, 0x72, 0x06, + 0x70, 0x07, 0x6c, 0x08, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, + 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, + 0x0f, 0x3e, 0x11, 0x3e, 0x11, 0x3f, 0x13, 0x3f, 0x13, 0x3c, 0x15, 0x3c, + 0x15, 0x3a, 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x19, 0x3c, + 0x1a, 0x3b, 0x1c, 0x3b, 0x5f, 0x24, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x28, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x50, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x19, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, + 0x00, 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, + 0x00, 0x56, 0x00, 0x57, 0x00, 0x62, 0x00, 0x55, 0x00, 0x34, 0x00, 0x0b, + 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, + 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x78, 0x00, + 0x43, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x23, 0x00, 0x31, 0x00, 0x3b, + 0x00, 0x42, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, + 0x00, 0x55, 0x00, 0x56, 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x00, + 0x00, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x45, + 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0x9e, 0x00, + 0x96, 0x00, 0x90, 0x00, 0x8c, 0x00, 0x88, 0x01, 0x84, 0x02, 0x7e, 0x02, + 0x7b, 0x03, 0x78, 0x04, 0x76, 0x05, 0x74, 0x06, 0x72, 0x07, 0x6f, 0x07, + 0x6b, 0x09, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x0a, 0x47, 0x0a, 0x46, + 0x0b, 0x44, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3f, 0x10, 0x3d, + 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3c, 0x16, 0x3a, + 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, + 0x5f, 0x24, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, + 0x00, 0x44, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, + 0x00, 0x62, 0x00, 0x58, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x14, + 0x00, 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, + 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x87, 0x00, 0x61, 0x00, 0x36, 0x00, + 0x10, 0x00, 0x02, 0x0a, 0x00, 0x1a, 0x00, 0x27, 0x00, 0x32, 0x00, 0x3a, + 0x00, 0x40, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4b, 0x00, 0x4e, 0x00, 0x50, + 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, + 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, 0x28, 0x3c, 0x16, 0x41, + 0x11, 0x43, 0x0f, 0x44, 0x0e, 0x45, 0x0d, 0x46, 0x0c, 0x46, 0x0c, 0x46, + 0x0b, 0x47, 0x0b, 0x47, 0x0b, 0x47, 0x0b, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x6e, 0x1c, 0x3a, 0x29, 0x27, 0x31, 0x1e, 0x36, 0x19, 0x3a, 0x17, 0x3b, + 0x14, 0x3d, 0x13, 0x3f, 0x11, 0x40, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x42, + 0x0f, 0x43, 0x0f, 0x43, 0x0e, 0x43, 0x0e, 0x44, 0x0d, 0x44, 0x0d, 0x44, + 0x0c, 0x44, 0x0c, 0x44, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x3c, 0x32, 0x22, 0x38, + 0x18, 0x3c, 0x14, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x42, 0x0e, 0x43, + 0x0e, 0x44, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x44, 0x0c, 0x45, 0x0c, 0x45, + 0x0c, 0x45, 0x0c, 0x45, 0x0b, 0x45, 0x0b, 0x45, 0x0b, 0x46, 0x0b, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x1e, 0x00, 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, + 0x00, 0x42, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x57, + 0x00, 0x44, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, + 0x00, 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, + 0x00, 0x49, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x8f, 0x00, 0x74, 0x00, 0x51, 0x00, 0x2e, 0x00, 0x10, 0x00, + 0x04, 0x08, 0x00, 0x13, 0x00, 0x20, 0x00, 0x2a, 0x00, 0x32, 0x00, 0x38, + 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x48, 0x9a, 0x00, 0x91, 0x00, + 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x00, 0x00, 0x08, + 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, + 0x00, 0x42, 0x00, 0x45, 0x30, 0x38, 0x20, 0x3a, 0x18, 0x3d, 0x14, 0x3e, + 0x12, 0x40, 0x10, 0x41, 0x0f, 0x42, 0x0e, 0x42, 0x0e, 0x42, 0x0d, 0x43, + 0x0d, 0x43, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x45, 0x0c, 0x45, 0x0b, 0x45, + 0x0b, 0x46, 0x0b, 0x46, 0x0b, 0x46, 0x0b, 0x47, 0x8a, 0x03, 0x59, 0x0f, + 0x40, 0x18, 0x32, 0x1f, 0x2a, 0x24, 0x24, 0x28, 0x20, 0x2c, 0x1c, 0x2f, + 0x1b, 0x31, 0x18, 0x33, 0x17, 0x34, 0x16, 0x36, 0x15, 0x37, 0x14, 0x38, + 0x13, 0x39, 0x12, 0x3a, 0x12, 0x3a, 0x12, 0x3b, 0x11, 0x3c, 0x10, 0x3c, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x4a, 0x25, 0x31, 0x2b, 0x25, 0x30, 0x1e, 0x33, + 0x1a, 0x36, 0x17, 0x38, 0x15, 0x39, 0x13, 0x3b, 0x12, 0x3c, 0x11, 0x3d, + 0x10, 0x3e, 0x10, 0x3f, 0x0f, 0x3f, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x40, + 0x0e, 0x41, 0x0e, 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, + 0x00, 0x41, 0x00, 0x44, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, + 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, + 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x94, 0x00, + 0x7f, 0x00, 0x64, 0x00, 0x46, 0x00, 0x29, 0x00, 0x10, 0x00, 0x06, 0x07, + 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x38, + 0x00, 0x3c, 0x00, 0x40, 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, + 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x10, + 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, + 0x34, 0x38, 0x25, 0x39, 0x1e, 0x3b, 0x19, 0x3c, 0x16, 0x3d, 0x14, 0x3d, + 0x13, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x41, 0x0f, 0x42, 0x0e, 0x42, + 0x0e, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x43, 0x0d, 0x43, + 0x0d, 0x44, 0x0d, 0x44, 0x97, 0x00, 0x6c, 0x06, 0x52, 0x0d, 0x43, 0x12, + 0x38, 0x18, 0x30, 0x1c, 0x2a, 0x20, 0x27, 0x23, 0x23, 0x26, 0x20, 0x28, + 0x1e, 0x2a, 0x1c, 0x2c, 0x1b, 0x2e, 0x19, 0x2f, 0x18, 0x31, 0x17, 0x32, + 0x16, 0x33, 0x16, 0x34, 0x16, 0x35, 0x15, 0x35, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x50, 0x24, 0x3b, 0x27, 0x2e, 0x2a, 0x26, 0x2d, 0x21, 0x2f, 0x1d, 0x32, + 0x1a, 0x33, 0x18, 0x35, 0x16, 0x36, 0x15, 0x38, 0x14, 0x39, 0x13, 0x3a, + 0x12, 0x3a, 0x11, 0x3b, 0x11, 0x3c, 0x11, 0x3c, 0x10, 0x3d, 0x10, 0x3e, + 0x10, 0x3e, 0x0f, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, + 0x00, 0x20, 0x00, 0x28, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, + 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, + 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x87, 0x00, 0x71, 0x00, + 0x57, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x10, 0x00, 0x07, 0x06, 0x00, 0x0b, + 0x00, 0x15, 0x00, 0x1f, 0x00, 0x26, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37, + 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, + 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, + 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, 0x36, 0x39, 0x2a, 0x39, + 0x22, 0x39, 0x1d, 0x3a, 0x1a, 0x3b, 0x17, 0x3d, 0x15, 0x3d, 0x13, 0x3d, + 0x13, 0x3e, 0x12, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x41, 0x0f, 0x42, + 0x0e, 0x41, 0x0e, 0x42, 0x0e, 0x42, 0x0e, 0x41, 0x0e, 0x42, 0x0d, 0x42, + 0x9e, 0x00, 0x79, 0x02, 0x61, 0x07, 0x50, 0x0b, 0x44, 0x10, 0x3b, 0x14, + 0x34, 0x18, 0x2f, 0x1b, 0x2b, 0x1e, 0x28, 0x20, 0x25, 0x23, 0x23, 0x25, + 0x21, 0x27, 0x1f, 0x28, 0x1d, 0x2a, 0x1c, 0x2b, 0x1b, 0x2c, 0x19, 0x2e, + 0x19, 0x2f, 0x19, 0x30, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x54, 0x23, 0x42, 0x25, + 0x35, 0x27, 0x2d, 0x29, 0x27, 0x2c, 0x22, 0x2e, 0x1f, 0x2f, 0x1c, 0x31, + 0x1a, 0x33, 0x19, 0x34, 0x17, 0x35, 0x16, 0x36, 0x15, 0x37, 0x14, 0x38, + 0x13, 0x38, 0x13, 0x39, 0x12, 0x3a, 0x11, 0x3a, 0x11, 0x3b, 0x11, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, + 0x00, 0x24, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, 0x00, 0x5e, 0x00, 0x5b, + 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, + 0x00, 0x30, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, + 0x37, 0x00, 0x22, 0x00, 0x10, 0x00, 0x08, 0x05, 0x02, 0x0a, 0x00, 0x12, + 0x00, 0x1a, 0x00, 0x22, 0x00, 0x28, 0x00, 0x2d, 0x9d, 0x00, 0x99, 0x00, + 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, 0x00, + 0x1f, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x07, 0x00, 0x10, 0x00, 0x19, + 0x00, 0x20, 0x00, 0x26, 0x38, 0x39, 0x2d, 0x39, 0x25, 0x39, 0x20, 0x3a, + 0x1d, 0x3b, 0x1a, 0x3b, 0x18, 0x3c, 0x16, 0x3d, 0x14, 0x3d, 0x13, 0x3d, + 0x13, 0x3d, 0x12, 0x3e, 0x11, 0x3f, 0x11, 0x40, 0x10, 0x40, 0x0f, 0x41, + 0x0f, 0x42, 0x0f, 0x42, 0x0e, 0x42, 0x0e, 0x42, 0xa2, 0x00, 0x84, 0x00, + 0x6c, 0x04, 0x5a, 0x07, 0x4e, 0x0b, 0x45, 0x0f, 0x3e, 0x12, 0x38, 0x15, + 0x32, 0x18, 0x2f, 0x1a, 0x2c, 0x1d, 0x29, 0x1f, 0x26, 0x21, 0x24, 0x22, + 0x22, 0x24, 0x21, 0x26, 0x1f, 0x27, 0x1e, 0x28, 0x1d, 0x2a, 0x1c, 0x2b, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x56, 0x23, 0x47, 0x24, 0x3b, 0x25, 0x32, 0x27, + 0x2c, 0x29, 0x27, 0x2b, 0x24, 0x2d, 0x21, 0x2e, 0x1e, 0x30, 0x1c, 0x31, + 0x1b, 0x32, 0x19, 0x33, 0x18, 0x34, 0x17, 0x35, 0x16, 0x36, 0x15, 0x37, + 0x14, 0x37, 0x14, 0x38, 0x13, 0x38, 0x13, 0x39, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, + 0x00, 0x27, 0x00, 0x2c, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, + 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, + 0x90, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x5b, 0x00, 0x46, 0x00, 0x32, 0x00, + 0x20, 0x00, 0x10, 0x00, 0x09, 0x04, 0x03, 0x09, 0x00, 0x0f, 0x00, 0x17, + 0x00, 0x1e, 0x00, 0x24, 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, + 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x00, + 0x12, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, + 0x39, 0x3a, 0x2f, 0x39, 0x28, 0x39, 0x23, 0x39, 0x20, 0x3a, 0x1c, 0x3b, + 0x1a, 0x3a, 0x18, 0x3b, 0x17, 0x3c, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, + 0x13, 0x3d, 0x12, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x11, 0x40, 0x10, 0x40, + 0x0f, 0x40, 0x0f, 0x41, 0xa6, 0x00, 0x8b, 0x00, 0x75, 0x02, 0x63, 0x05, + 0x57, 0x07, 0x4d, 0x0b, 0x45, 0x0e, 0x3e, 0x11, 0x39, 0x13, 0x35, 0x16, + 0x32, 0x18, 0x2e, 0x1a, 0x2c, 0x1c, 0x29, 0x1d, 0x27, 0x1f, 0x26, 0x22, + 0x24, 0x22, 0x21, 0x23, 0x21, 0x25, 0x20, 0x26, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x58, 0x23, 0x4a, 0x23, 0x3f, 0x24, 0x36, 0x26, 0x30, 0x27, 0x2b, 0x29, + 0x27, 0x2a, 0x24, 0x2c, 0x22, 0x2d, 0x20, 0x2e, 0x1e, 0x30, 0x1c, 0x30, + 0x1b, 0x32, 0x1a, 0x32, 0x18, 0x33, 0x18, 0x34, 0x17, 0x34, 0x16, 0x35, + 0x16, 0x36, 0x15, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, + 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x86, 0x00, + 0x76, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, + 0x10, 0x00, 0x09, 0x04, 0x04, 0x08, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1a, + 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, + 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x3a, 0x3a, 0x31, 0x3a, + 0x2a, 0x39, 0x25, 0x3a, 0x22, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1a, 0x3a, + 0x19, 0x3a, 0x17, 0x3c, 0x16, 0x3c, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, + 0x13, 0x3d, 0x12, 0x3d, 0x11, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x10, 0x40, + 0xa8, 0x00, 0x90, 0x00, 0x7c, 0x01, 0x6b, 0x02, 0x5f, 0x05, 0x55, 0x07, + 0x4c, 0x0a, 0x45, 0x0d, 0x40, 0x0f, 0x3c, 0x12, 0x37, 0x14, 0x34, 0x16, + 0x31, 0x18, 0x2e, 0x19, 0x2b, 0x1b, 0x2a, 0x1d, 0x28, 0x1e, 0x26, 0x20, + 0x25, 0x22, 0x23, 0x22, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x59, 0x23, 0x4d, 0x23, + 0x43, 0x24, 0x3b, 0x25, 0x34, 0x26, 0x2f, 0x27, 0x2b, 0x29, 0x28, 0x2a, + 0x25, 0x2b, 0x23, 0x2d, 0x21, 0x2e, 0x1f, 0x2e, 0x1d, 0x30, 0x1c, 0x30, + 0x1b, 0x31, 0x1a, 0x32, 0x19, 0x32, 0x18, 0x33, 0x17, 0x34, 0x16, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x0f, 0x00, 0x16, 0x00, 0x1c, 0x00, 0x5e, 0x00, 0x5d, + 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, + 0x00, 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, + 0x00, 0x16, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9b, 0x00, 0x95, 0x00, 0x8a, 0x00, 0x7d, 0x00, 0x6d, 0x00, + 0x5d, 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x10, 0x00, + 0x0a, 0x04, 0x05, 0x07, 0x00, 0x0b, 0x00, 0x11, 0x9e, 0x00, 0x9c, 0x00, + 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, 0x00, + 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, + 0x01, 0x00, 0x00, 0x06, 0x3a, 0x3b, 0x32, 0x3a, 0x2c, 0x39, 0x27, 0x3a, + 0x23, 0x39, 0x21, 0x39, 0x1e, 0x3b, 0x1c, 0x3c, 0x1a, 0x3a, 0x19, 0x3a, + 0x17, 0x3b, 0x17, 0x3c, 0x15, 0x3d, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, + 0x13, 0x3d, 0x13, 0x3d, 0x11, 0x3d, 0x11, 0x3e, 0xaa, 0x00, 0x94, 0x00, + 0x82, 0x00, 0x72, 0x01, 0x65, 0x04, 0x5b, 0x05, 0x53, 0x07, 0x4b, 0x0a, + 0x45, 0x0c, 0x41, 0x0f, 0x3d, 0x11, 0x39, 0x12, 0x36, 0x15, 0x33, 0x16, + 0x30, 0x18, 0x2e, 0x19, 0x2b, 0x1a, 0x2b, 0x1d, 0x29, 0x1d, 0x26, 0x1e, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x5a, 0x23, 0x4f, 0x23, 0x46, 0x24, 0x3e, 0x24, + 0x37, 0x25, 0x32, 0x26, 0x2e, 0x27, 0x2b, 0x28, 0x28, 0x2a, 0x25, 0x2b, + 0x23, 0x2c, 0x21, 0x2d, 0x20, 0x2e, 0x1e, 0x2e, 0x1d, 0x30, 0x1c, 0x30, + 0x1b, 0x31, 0x1a, 0x32, 0x19, 0x32, 0x18, 0x33, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x0e, 0x00, 0x14, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, + 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, + 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, + 0x96, 0x00, 0x8d, 0x00, 0x81, 0x00, 0x74, 0x00, 0x65, 0x00, 0x56, 0x00, + 0x46, 0x00, 0x37, 0x00, 0x29, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x0a, 0x03, + 0x06, 0x07, 0x01, 0x0a, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, + 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x43, 0x00, + 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, 0x00, + 0x3b, 0x3b, 0x33, 0x3b, 0x2d, 0x39, 0x29, 0x39, 0x25, 0x3a, 0x22, 0x39, + 0x20, 0x39, 0x1e, 0x3b, 0x1c, 0x3c, 0x1a, 0x3a, 0x19, 0x3a, 0x18, 0x3a, + 0x17, 0x3c, 0x16, 0x3c, 0x15, 0x3d, 0x15, 0x3e, 0x13, 0x3f, 0x13, 0x3d, + 0x13, 0x3d, 0x13, 0x3d, 0xaa, 0x00, 0x98, 0x00, 0x87, 0x00, 0x78, 0x01, + 0x6b, 0x02, 0x61, 0x04, 0x58, 0x06, 0x51, 0x08, 0x4b, 0x0a, 0x46, 0x0c, + 0x42, 0x0e, 0x3e, 0x0f, 0x3a, 0x12, 0x37, 0x12, 0x35, 0x16, 0x32, 0x16, + 0x30, 0x18, 0x2e, 0x19, 0x2b, 0x1a, 0x2b, 0x1c, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x5a, 0x23, 0x51, 0x23, 0x48, 0x23, 0x41, 0x24, 0x3a, 0x25, 0x35, 0x25, + 0x31, 0x26, 0x2e, 0x27, 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2b, 0x24, 0x2b, + 0x22, 0x2d, 0x21, 0x2d, 0x1f, 0x2e, 0x1e, 0x2e, 0x1d, 0x30, 0x1c, 0x30, + 0x1b, 0x30, 0x1a, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, + 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, + 0x00, 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x90, 0x00, + 0x85, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x42, 0x00, + 0x34, 0x00, 0x27, 0x00, 0x1b, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x06, 0x06, + 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, + 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x3b, 0x3c, 0x34, 0x3a, + 0x2f, 0x3a, 0x2a, 0x39, 0x26, 0x3a, 0x23, 0x3a, 0x21, 0x39, 0x1f, 0x3a, + 0x1d, 0x3b, 0x1c, 0x3c, 0x1a, 0x3b, 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3b, + 0x17, 0x3c, 0x15, 0x3c, 0x15, 0x3d, 0x15, 0x3f, 0x13, 0x3f, 0x13, 0x3e, + 0xac, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7c, 0x00, 0x71, 0x01, 0x67, 0x03, + 0x5e, 0x05, 0x57, 0x07, 0x51, 0x08, 0x4b, 0x0a, 0x46, 0x0c, 0x42, 0x0d, + 0x3f, 0x0f, 0x3b, 0x11, 0x39, 0x12, 0x35, 0x14, 0x34, 0x16, 0x31, 0x16, + 0x30, 0x18, 0x2e, 0x19, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x52, 0x23, + 0x4a, 0x23, 0x43, 0x24, 0x3d, 0x24, 0x38, 0x25, 0x34, 0x26, 0x30, 0x27, + 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x24, 0x2b, 0x22, 0x2c, + 0x21, 0x2d, 0x20, 0x2d, 0x1f, 0x2e, 0x1d, 0x2e, 0x1d, 0x30, 0x1c, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x5f, 0x00, 0x5e, + 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, + 0x00, 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3e, 0x00, 0x31, 0x00, + 0x25, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x9e, 0x00, 0x9d, 0x00, + 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, 0x00, + 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, + 0x1f, 0x00, 0x17, 0x00, 0x3b, 0x3c, 0x35, 0x3a, 0x30, 0x3b, 0x2c, 0x39, + 0x28, 0x3a, 0x25, 0x3b, 0x22, 0x39, 0x21, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, + 0x1c, 0x3c, 0x1a, 0x3b, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, 0x17, 0x3c, + 0x16, 0x3c, 0x15, 0x3c, 0x15, 0x3e, 0x14, 0x3f, 0xad, 0x00, 0x9d, 0x00, + 0x8e, 0x00, 0x81, 0x00, 0x76, 0x01, 0x6b, 0x02, 0x63, 0x04, 0x5c, 0x05, + 0x56, 0x07, 0x50, 0x08, 0x4b, 0x0a, 0x47, 0x0c, 0x43, 0x0c, 0x40, 0x0f, + 0x3c, 0x0f, 0x3a, 0x12, 0x37, 0x12, 0x35, 0x14, 0x33, 0x16, 0x30, 0x16, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x53, 0x23, 0x4c, 0x23, 0x45, 0x23, + 0x40, 0x24, 0x3b, 0x25, 0x36, 0x25, 0x33, 0x26, 0x30, 0x27, 0x2d, 0x28, + 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x25, 0x2b, 0x23, 0x2b, 0x22, 0x2d, + 0x20, 0x2d, 0x20, 0x2e, 0x1e, 0x2e, 0x1d, 0x2e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, + 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, + 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, + 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6b, 0x00, + 0x5f, 0x00, 0x53, 0x00, 0x46, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x24, 0x00, + 0x19, 0x00, 0x0f, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, + 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x66, 0x00, 0x5b, 0x00, + 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, 0x00, + 0x3c, 0x3d, 0x36, 0x3a, 0x31, 0x3b, 0x2c, 0x3a, 0x29, 0x39, 0x26, 0x3a, + 0x24, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3c, + 0x1a, 0x3c, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, 0x17, 0x3b, 0x17, 0x3c, + 0x15, 0x3c, 0x15, 0x3c, 0xad, 0x00, 0x9f, 0x00, 0x91, 0x00, 0x85, 0x00, + 0x79, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x60, 0x04, 0x59, 0x05, 0x54, 0x07, + 0x4f, 0x08, 0x4b, 0x0a, 0x47, 0x0c, 0x43, 0x0c, 0x41, 0x0f, 0x3d, 0x0f, + 0x3b, 0x11, 0x38, 0x12, 0x35, 0x12, 0x35, 0x15, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x5b, 0x23, 0x54, 0x23, 0x4e, 0x23, 0x47, 0x23, 0x42, 0x24, 0x3c, 0x24, + 0x38, 0x25, 0x35, 0x25, 0x32, 0x26, 0x2f, 0x27, 0x2d, 0x28, 0x2a, 0x28, + 0x28, 0x2a, 0x26, 0x2a, 0x25, 0x2b, 0x23, 0x2b, 0x22, 0x2c, 0x21, 0x2d, + 0x20, 0x2d, 0x20, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3d, 0x36, 0x3a, + 0x32, 0x3b, 0x2d, 0x3b, 0x2b, 0x39, 0x28, 0x3a, 0x25, 0x3a, 0x23, 0x3a, + 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, + 0x19, 0x3a, 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3a, 0x17, 0x3c, 0x16, 0x3c, + 0xad, 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x74, 0x01, + 0x6c, 0x02, 0x65, 0x03, 0x5e, 0x04, 0x58, 0x05, 0x53, 0x07, 0x4f, 0x08, + 0x4b, 0x0a, 0x47, 0x0b, 0x44, 0x0c, 0x41, 0x0e, 0x3e, 0x0f, 0x3b, 0x0f, + 0x3a, 0x12, 0x37, 0x12, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x55, 0x23, + 0x4f, 0x23, 0x49, 0x23, 0x44, 0x24, 0x3f, 0x24, 0x3b, 0x25, 0x37, 0x25, + 0x34, 0x26, 0x31, 0x26, 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, + 0x27, 0x2a, 0x25, 0x2b, 0x24, 0x2b, 0x22, 0x2b, 0x22, 0x2d, 0x20, 0x2d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3b, 0x33, 0x3b, 0x2f, 0x3b, + 0x2b, 0x39, 0x29, 0x39, 0x26, 0x3a, 0x24, 0x3b, 0x22, 0x3a, 0x21, 0x39, + 0x1f, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, + 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3a, 0x17, 0x3b, 0xae, 0x00, 0xa2, 0x00, + 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x77, 0x00, 0x6f, 0x01, 0x67, 0x02, + 0x61, 0x04, 0x5c, 0x05, 0x57, 0x05, 0x52, 0x07, 0x4e, 0x08, 0x4b, 0x0a, + 0x47, 0x0b, 0x44, 0x0c, 0x41, 0x0d, 0x3f, 0x0f, 0x3c, 0x0f, 0x3b, 0x11, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x56, 0x23, 0x50, 0x23, 0x4a, 0x23, + 0x45, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x35, 0x25, 0x33, 0x26, + 0x30, 0x26, 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, + 0x25, 0x2a, 0x24, 0x2b, 0x23, 0x2b, 0x22, 0x2c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x37, 0x3b, 0x33, 0x3a, 0x2f, 0x3b, 0x2c, 0x3a, 0x2a, 0x39, + 0x27, 0x3a, 0x25, 0x3a, 0x23, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, + 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, 0x19, 0x3a, + 0x19, 0x3a, 0x17, 0x3a, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, + 0x84, 0x00, 0x7a, 0x00, 0x72, 0x01, 0x6c, 0x02, 0x65, 0x02, 0x5f, 0x04, + 0x5a, 0x05, 0x56, 0x05, 0x52, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0b, + 0x45, 0x0c, 0x41, 0x0c, 0x40, 0x0f, 0x3d, 0x0f, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x5c, 0x23, 0x56, 0x23, 0x51, 0x23, 0x4c, 0x23, 0x47, 0x23, 0x42, 0x24, + 0x3e, 0x24, 0x3b, 0x25, 0x37, 0x25, 0x34, 0x25, 0x32, 0x26, 0x30, 0x26, + 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, + 0x25, 0x2b, 0x23, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x38, 0x3c, + 0x34, 0x3a, 0x30, 0x3b, 0x2d, 0x3b, 0x2a, 0x39, 0x28, 0x39, 0x26, 0x3a, + 0x24, 0x3b, 0x22, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, 0x1e, 0x3b, + 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, 0x19, 0x3a, 0x19, 0x3a, + 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, 0x00, 0x87, 0x00, 0x7e, 0x00, + 0x76, 0x00, 0x6f, 0x01, 0x68, 0x02, 0x62, 0x04, 0x5e, 0x04, 0x59, 0x05, + 0x55, 0x06, 0x51, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x45, 0x0c, + 0x41, 0x0c, 0x41, 0x0f, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x57, 0x23, + 0x52, 0x23, 0x4d, 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, 0x3c, 0x24, + 0x39, 0x25, 0x36, 0x25, 0x34, 0x25, 0x31, 0x26, 0x2f, 0x27, 0x2d, 0x27, + 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, 0x25, 0x2b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x38, 0x3c, 0x34, 0x3a, 0x31, 0x3b, + 0x2e, 0x3c, 0x2b, 0x39, 0x29, 0x39, 0x27, 0x3a, 0x25, 0x3a, 0x24, 0x3b, + 0x21, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, + 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3a, 0xaf, 0x00, 0xa6, 0x00, + 0x9b, 0x00, 0x91, 0x00, 0x88, 0x00, 0x80, 0x00, 0x78, 0x00, 0x71, 0x01, + 0x6b, 0x02, 0x66, 0x02, 0x61, 0x04, 0x5c, 0x04, 0x58, 0x05, 0x55, 0x07, + 0x50, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0c, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4d, 0x23, + 0x49, 0x23, 0x45, 0x23, 0x41, 0x24, 0x3d, 0x24, 0x3b, 0x24, 0x38, 0x25, + 0x35, 0x25, 0x33, 0x26, 0x31, 0x26, 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, + 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3e, 0x38, 0x3d, 0x35, 0x3a, 0x32, 0x3b, 0x2e, 0x3b, 0x2c, 0x3a, + 0x2a, 0x39, 0x27, 0x39, 0x26, 0x3a, 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x39, + 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, + 0x1a, 0x3c, 0x19, 0x3c, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x93, 0x00, + 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6e, 0x01, 0x69, 0x02, + 0x63, 0x03, 0x5f, 0x04, 0x5b, 0x05, 0x56, 0x05, 0x54, 0x07, 0x4f, 0x07, + 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x46, 0x0c, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x5d, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4f, 0x23, 0x4a, 0x23, 0x46, 0x23, + 0x43, 0x24, 0x3f, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x36, 0x25, 0x34, 0x25, + 0x33, 0x26, 0x30, 0x26, 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, 0x2a, 0x28, + 0x28, 0x28, 0x28, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x39, 0x3e, + 0x35, 0x3a, 0x32, 0x3a, 0x2f, 0x3b, 0x2d, 0x3c, 0x2a, 0x39, 0x28, 0x39, + 0x27, 0x3a, 0x24, 0x3a, 0x24, 0x3b, 0x22, 0x3b, 0x21, 0x39, 0x20, 0x39, + 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, + 0xb0, 0x00, 0xa7, 0x00, 0x9e, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x86, 0x00, + 0x7e, 0x00, 0x77, 0x00, 0x71, 0x01, 0x6b, 0x01, 0x66, 0x02, 0x62, 0x04, + 0x5d, 0x04, 0x5a, 0x05, 0x55, 0x05, 0x53, 0x07, 0x4f, 0x07, 0x4e, 0x09, + 0x4a, 0x0a, 0x47, 0x0a, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, + 0x54, 0x23, 0x50, 0x23, 0x4c, 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, + 0x3d, 0x24, 0x3a, 0x24, 0x38, 0x25, 0x36, 0x25, 0x33, 0x25, 0x32, 0x26, + 0x2f, 0x26, 0x2f, 0x27, 0x2c, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x39, 0x3e, 0x35, 0x3b, 0x32, 0x3a, + 0x30, 0x3b, 0x2e, 0x3c, 0x2b, 0x3a, 0x2a, 0x39, 0x27, 0x39, 0x26, 0x3a, + 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x39, + 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0xb1, 0x00, 0xa7, 0x00, + 0x9f, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x87, 0x00, 0x80, 0x00, 0x79, 0x00, + 0x73, 0x00, 0x6e, 0x01, 0x69, 0x02, 0x64, 0x02, 0x60, 0x04, 0x5c, 0x04, + 0x59, 0x05, 0x55, 0x05, 0x53, 0x07, 0x4e, 0x07, 0x4e, 0x09, 0x4a, 0x0a, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, 0x23, + 0x4c, 0x23, 0x48, 0x23, 0x45, 0x23, 0x42, 0x24, 0x3f, 0x24, 0x3c, 0x24, + 0x39, 0x25, 0x37, 0x25, 0x35, 0x25, 0x33, 0x25, 0x31, 0x26, 0x2f, 0x26, + 0x2e, 0x27, 0x2c, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x94, 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xa9, 0x00, + 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, + 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb1, 0x00, + 0xb2, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x21, 0x00, 0x6e, 0x00, + 0x8a, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa8, 0x00, + 0xaa, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xad, 0x00, 0xad, 0x00, 0xad, 0x00, + 0xae, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, + 0x6c, 0x00, 0x92, 0x00, 0xa1, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xac, 0x00, + 0xae, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, + 0xb2, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, + 0xb3, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x04, 0x70, 0x01, + 0x7f, 0x00, 0x88, 0x00, 0x90, 0x00, 0x95, 0x00, 0x99, 0x00, 0x9c, 0x00, + 0x9f, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00, + 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x1c, 0x3a, 0x03, 0x59, 0x00, 0x6c, 0x00, + 0x79, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00, + 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, + 0xa5, 0x00, 0xa6, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x60, 0x0e, 0x79, 0x01, + 0x88, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa3, 0x00, + 0xa5, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00, 0xac, 0x00, + 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4e, 0x0f, 0x60, 0x07, 0x6d, 0x03, 0x77, 0x01, + 0x7f, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x8f, 0x00, 0x92, 0x00, 0x94, 0x00, + 0x97, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0, 0x00, 0xa1, 0x00, + 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa4, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x29, 0x27, 0x0f, 0x40, 0x06, 0x52, 0x02, 0x61, 0x00, 0x6c, 0x00, + 0x75, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x87, 0x00, 0x8b, 0x00, 0x8e, 0x00, + 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9b, 0x00, + 0x9d, 0x00, 0x9e, 0x00, 0x60, 0x14, 0x6f, 0x07, 0x7b, 0x03, 0x84, 0x01, + 0x8c, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9d, 0x00, 0x9f, 0x00, + 0xa1, 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa7, 0x00, + 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x17, 0x56, 0x0d, 0x62, 0x07, 0x6b, 0x04, 0x73, 0x02, 0x79, 0x01, + 0x7e, 0x01, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, + 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, + 0x9e, 0x00, 0x9f, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x31, 0x1e, 0x18, + 0x32, 0x0d, 0x43, 0x07, 0x50, 0x04, 0x5a, 0x02, 0x63, 0x01, 0x6b, 0x00, + 0x72, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x85, 0x00, 0x88, 0x00, + 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, + 0x60, 0x18, 0x6b, 0x0c, 0x74, 0x06, 0x7d, 0x03, 0x83, 0x02, 0x89, 0x01, + 0x8d, 0x00, 0x91, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, + 0x9e, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xa4, 0x00, + 0xa5, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x1d, 0x51, 0x12, + 0x5b, 0x0c, 0x63, 0x08, 0x6a, 0x05, 0x70, 0x03, 0x77, 0x02, 0x7b, 0x02, + 0x7e, 0x01, 0x82, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8c, 0x00, 0x8e, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x97, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x36, 0x19, 0x1f, 0x2a, 0x12, 0x38, 0x0b, + 0x44, 0x07, 0x4e, 0x05, 0x57, 0x02, 0x5f, 0x01, 0x65, 0x01, 0x6b, 0x00, + 0x71, 0x00, 0x76, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x84, 0x00, + 0x87, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x60, 0x1b, 0x68, 0x0f, + 0x70, 0x09, 0x77, 0x05, 0x7d, 0x03, 0x82, 0x02, 0x87, 0x01, 0x8b, 0x00, + 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, + 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x21, 0x4e, 0x16, 0x56, 0x10, 0x5d, 0x0b, + 0x65, 0x08, 0x69, 0x06, 0x6f, 0x04, 0x74, 0x03, 0x78, 0x02, 0x7b, 0x02, + 0x7e, 0x01, 0x81, 0x01, 0x85, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c, 0x00, + 0x8e, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x92, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x3a, 0x17, 0x24, 0x24, 0x18, 0x30, 0x10, 0x3b, 0x0b, 0x45, 0x07, + 0x4d, 0x05, 0x55, 0x04, 0x5b, 0x02, 0x61, 0x01, 0x67, 0x01, 0x6b, 0x00, + 0x6f, 0x00, 0x74, 0x00, 0x77, 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x80, 0x00, + 0x83, 0x00, 0x86, 0x00, 0x60, 0x1d, 0x67, 0x12, 0x6d, 0x0c, 0x73, 0x08, + 0x79, 0x05, 0x7e, 0x03, 0x82, 0x02, 0x86, 0x02, 0x89, 0x01, 0x8c, 0x00, + 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0x99, 0x00, + 0x9a, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x24, 0x4c, 0x1a, 0x53, 0x13, 0x59, 0x0e, 0x5f, 0x0b, 0x65, 0x08, + 0x69, 0x06, 0x6d, 0x05, 0x73, 0x04, 0x76, 0x03, 0x79, 0x02, 0x7c, 0x02, + 0x7e, 0x01, 0x81, 0x01, 0x84, 0x00, 0x87, 0x00, 0x89, 0x00, 0x8b, 0x00, + 0x8c, 0x00, 0x8d, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x3b, 0x14, 0x28, + 0x20, 0x1c, 0x2a, 0x14, 0x34, 0x0f, 0x3e, 0x0b, 0x45, 0x07, 0x4c, 0x05, + 0x53, 0x04, 0x58, 0x03, 0x5e, 0x02, 0x63, 0x01, 0x67, 0x01, 0x6c, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x76, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x7e, 0x00, + 0x60, 0x1d, 0x66, 0x14, 0x6b, 0x0e, 0x71, 0x0a, 0x76, 0x07, 0x7a, 0x05, + 0x7e, 0x03, 0x81, 0x02, 0x85, 0x02, 0x88, 0x01, 0x8b, 0x01, 0x8d, 0x00, + 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x98, 0x00, + 0x99, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x27, 0x4a, 0x1d, + 0x4f, 0x16, 0x57, 0x11, 0x5b, 0x0e, 0x61, 0x0b, 0x66, 0x08, 0x69, 0x07, + 0x6d, 0x05, 0x71, 0x04, 0x75, 0x03, 0x77, 0x02, 0x7a, 0x02, 0x7c, 0x02, + 0x7e, 0x01, 0x80, 0x01, 0x83, 0x01, 0x86, 0x00, 0x88, 0x00, 0x89, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x3d, 0x13, 0x2c, 0x1c, 0x20, 0x27, 0x18, + 0x2f, 0x12, 0x38, 0x0e, 0x3e, 0x0a, 0x45, 0x07, 0x4b, 0x06, 0x51, 0x05, + 0x57, 0x04, 0x5c, 0x02, 0x60, 0x02, 0x65, 0x01, 0x67, 0x01, 0x6c, 0x00, + 0x6f, 0x00, 0x71, 0x00, 0x75, 0x00, 0x77, 0x00, 0x60, 0x1e, 0x65, 0x16, + 0x6a, 0x10, 0x6f, 0x0c, 0x73, 0x09, 0x77, 0x07, 0x7b, 0x05, 0x7e, 0x03, + 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8e, 0x00, + 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, + 0x27, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x8f, 0x00, 0x96, 0x00, 0x99, 0x00, + 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, + 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x2c, 0x14, 0x57, 0x00, + 0x82, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8c, 0x00, 0x63, 0x00, 0x7f, 0x00, 0x89, 0x00, 0x90, 0x00, 0x96, 0x00, + 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, + 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x31, 0x11, 0x5f, 0x00, + 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x9e, 0x00, 0x9e, 0x00, 0x43, 0x29, 0x48, 0x20, 0x4d, 0x19, 0x54, 0x14, + 0x58, 0x10, 0x5c, 0x0d, 0x62, 0x0b, 0x66, 0x09, 0x69, 0x07, 0x6c, 0x06, + 0x70, 0x05, 0x74, 0x04, 0x76, 0x03, 0x78, 0x02, 0x7a, 0x02, 0x7c, 0x02, + 0x7e, 0x02, 0x80, 0x01, 0x82, 0x01, 0x85, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x3f, 0x11, 0x2f, 0x1b, 0x23, 0x23, 0x1b, 0x2b, 0x15, 0x32, 0x11, + 0x39, 0x0d, 0x40, 0x0a, 0x45, 0x08, 0x4b, 0x07, 0x51, 0x05, 0x56, 0x04, + 0x59, 0x03, 0x5e, 0x02, 0x61, 0x02, 0x65, 0x01, 0x68, 0x01, 0x6b, 0x00, + 0x6e, 0x00, 0x71, 0x00, 0x60, 0x1f, 0x64, 0x17, 0x69, 0x11, 0x6d, 0x0d, + 0x71, 0x0a, 0x75, 0x08, 0x78, 0x06, 0x7b, 0x05, 0x7e, 0x04, 0x81, 0x03, + 0x84, 0x02, 0x86, 0x02, 0x88, 0x01, 0x8a, 0x01, 0x8c, 0x01, 0x8e, 0x00, + 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x4f, 0x00, 0x73, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x95, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, + 0x9c, 0x00, 0x9d, 0x00, 0x01, 0x29, 0x16, 0x04, 0x57, 0x00, 0x78, 0x00, + 0x87, 0x00, 0x8f, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, + 0x9b, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x4d, 0x00, + 0x60, 0x00, 0x77, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x92, 0x00, 0x95, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, + 0x9c, 0x00, 0x9d, 0x00, 0x03, 0x23, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, + 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, 0x00, + 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x43, 0x2b, 0x47, 0x22, 0x4c, 0x1b, 0x51, 0x16, 0x56, 0x12, 0x59, 0x0f, + 0x5e, 0x0d, 0x63, 0x0b, 0x66, 0x09, 0x69, 0x07, 0x6b, 0x06, 0x6f, 0x05, + 0x73, 0x05, 0x75, 0x03, 0x77, 0x03, 0x79, 0x02, 0x7a, 0x02, 0x7c, 0x02, + 0x7e, 0x02, 0x7f, 0x01, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x40, 0x11, 0x31, + 0x18, 0x26, 0x20, 0x1e, 0x28, 0x18, 0x2f, 0x13, 0x35, 0x0f, 0x3c, 0x0c, + 0x41, 0x0a, 0x46, 0x08, 0x4b, 0x07, 0x50, 0x05, 0x54, 0x04, 0x58, 0x04, + 0x5c, 0x02, 0x5f, 0x02, 0x62, 0x02, 0x66, 0x01, 0x69, 0x01, 0x6b, 0x00, + 0x60, 0x20, 0x64, 0x18, 0x68, 0x13, 0x6b, 0x0f, 0x6f, 0x0c, 0x73, 0x09, + 0x76, 0x07, 0x79, 0x06, 0x7c, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, + 0x85, 0x02, 0x88, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8e, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x38, 0x00, + 0x5b, 0x00, 0x6f, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, + 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, + 0x00, 0x49, 0x00, 0x27, 0x12, 0x01, 0x43, 0x00, 0x61, 0x00, 0x74, 0x00, + 0x7f, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, 0x00, + 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x60, 0x00, 0x24, 0x00, 0x48, 0x00, + 0x60, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, + 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, + 0x00, 0x46, 0x00, 0x1f, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, + 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, + 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x43, 0x2d, 0x46, 0x24, + 0x4b, 0x1e, 0x4f, 0x19, 0x55, 0x14, 0x58, 0x11, 0x5b, 0x0e, 0x5f, 0x0c, + 0x64, 0x0a, 0x66, 0x09, 0x68, 0x07, 0x6a, 0x06, 0x6e, 0x06, 0x72, 0x05, + 0x74, 0x04, 0x76, 0x03, 0x78, 0x03, 0x79, 0x02, 0x7b, 0x02, 0x7c, 0x02, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x40, 0x10, 0x33, 0x17, 0x28, 0x1e, 0x20, + 0x25, 0x1a, 0x2c, 0x16, 0x32, 0x12, 0x37, 0x0f, 0x3d, 0x0c, 0x42, 0x0a, + 0x46, 0x08, 0x4b, 0x07, 0x4f, 0x05, 0x53, 0x05, 0x57, 0x04, 0x5a, 0x04, + 0x5e, 0x02, 0x61, 0x02, 0x63, 0x01, 0x66, 0x01, 0x60, 0x20, 0x63, 0x19, + 0x67, 0x14, 0x6a, 0x10, 0x6e, 0x0d, 0x71, 0x0b, 0x74, 0x09, 0x77, 0x07, + 0x7a, 0x06, 0x7c, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, + 0x87, 0x02, 0x88, 0x02, 0x8a, 0x01, 0x8c, 0x01, 0x8d, 0x00, 0x8e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x48, 0x00, + 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, + 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x54, 0x00, 0x41, + 0x00, 0x17, 0x10, 0x00, 0x36, 0x00, 0x51, 0x00, 0x64, 0x00, 0x71, 0x00, + 0x7a, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8d, 0x00, 0x90, 0x00, + 0x92, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x89, 0x00, 0x77, 0x00, 0x48, 0x00, 0x0f, 0x00, 0x30, 0x00, 0x48, 0x00, + 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, + 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x52, 0x00, 0x3c, + 0x00, 0x0d, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, 0x00, + 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, + 0x93, 0x00, 0x95, 0x00, 0x42, 0x2e, 0x45, 0x26, 0x4b, 0x1f, 0x4d, 0x1a, + 0x52, 0x16, 0x56, 0x13, 0x59, 0x10, 0x5c, 0x0e, 0x61, 0x0c, 0x64, 0x0a, + 0x66, 0x09, 0x68, 0x07, 0x6a, 0x07, 0x6d, 0x06, 0x71, 0x05, 0x74, 0x05, + 0x75, 0x03, 0x77, 0x03, 0x78, 0x02, 0x7a, 0x02, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x41, 0x0f, 0x34, 0x16, 0x2a, 0x1c, 0x23, 0x23, 0x1d, 0x29, 0x18, + 0x2e, 0x14, 0x34, 0x11, 0x39, 0x0e, 0x3e, 0x0c, 0x42, 0x0a, 0x47, 0x08, + 0x4b, 0x07, 0x4f, 0x05, 0x52, 0x05, 0x56, 0x04, 0x59, 0x04, 0x5c, 0x03, + 0x5f, 0x02, 0x62, 0x02, 0x60, 0x20, 0x63, 0x1a, 0x66, 0x15, 0x6a, 0x11, + 0x6d, 0x0e, 0x70, 0x0c, 0x73, 0x0a, 0x75, 0x08, 0x78, 0x07, 0x7a, 0x06, + 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, 0x86, 0x02, + 0x88, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, + 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, + 0x86, 0x00, 0x89, 0x00, 0x00, 0x59, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x0e, + 0x10, 0x00, 0x2e, 0x00, 0x46, 0x00, 0x57, 0x00, 0x64, 0x00, 0x6f, 0x00, + 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x83, 0x00, + 0x60, 0x00, 0x30, 0x00, 0x02, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, + 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, + 0x86, 0x00, 0x89, 0x00, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, + 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, 0x00, + 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, + 0x42, 0x2f, 0x44, 0x27, 0x4a, 0x22, 0x4c, 0x1c, 0x50, 0x18, 0x55, 0x14, + 0x57, 0x12, 0x5a, 0x0f, 0x5d, 0x0d, 0x62, 0x0c, 0x64, 0x0a, 0x66, 0x09, + 0x68, 0x08, 0x6a, 0x07, 0x6c, 0x06, 0x70, 0x05, 0x73, 0x05, 0x75, 0x04, + 0x76, 0x03, 0x78, 0x03, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x42, 0x0f, 0x36, + 0x15, 0x2c, 0x1b, 0x25, 0x21, 0x1f, 0x26, 0x1a, 0x2c, 0x16, 0x31, 0x12, + 0x36, 0x0f, 0x3a, 0x0d, 0x3f, 0x0c, 0x43, 0x0a, 0x47, 0x08, 0x4b, 0x07, + 0x4e, 0x05, 0x52, 0x05, 0x55, 0x04, 0x58, 0x04, 0x5b, 0x04, 0x5d, 0x02, + 0x60, 0x21, 0x63, 0x1b, 0x66, 0x16, 0x69, 0x12, 0x6c, 0x0f, 0x6e, 0x0d, + 0x71, 0x0b, 0x74, 0x09, 0x76, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7d, 0x05, + 0x7f, 0x04, 0x81, 0x03, 0x82, 0x02, 0x84, 0x02, 0x86, 0x02, 0x87, 0x02, + 0x89, 0x02, 0x8a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, + 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, + 0x00, 0x5b, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x23, 0x02, 0x0a, 0x10, 0x00, + 0x29, 0x00, 0x3d, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x65, 0x00, 0x6d, 0x00, + 0x74, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x70, 0x00, 0x48, 0x00, + 0x22, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, + 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, + 0x00, 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x04, 0x00, 0x1f, 0x00, + 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, + 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, 0x00, 0x42, 0x31, 0x44, 0x29, + 0x49, 0x23, 0x4c, 0x1e, 0x4e, 0x1a, 0x53, 0x16, 0x56, 0x13, 0x58, 0x11, + 0x5a, 0x0f, 0x5f, 0x0d, 0x63, 0x0b, 0x64, 0x0a, 0x66, 0x09, 0x68, 0x08, + 0x6a, 0x07, 0x6b, 0x06, 0x6f, 0x06, 0x72, 0x05, 0x74, 0x05, 0x75, 0x03, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x43, 0x0f, 0x37, 0x14, 0x2e, 0x19, 0x27, + 0x1f, 0x21, 0x24, 0x1c, 0x29, 0x18, 0x2e, 0x15, 0x33, 0x12, 0x37, 0x0f, + 0x3b, 0x0c, 0x40, 0x0c, 0x43, 0x0a, 0x47, 0x08, 0x4b, 0x07, 0x4e, 0x06, + 0x51, 0x05, 0x55, 0x05, 0x56, 0x04, 0x5a, 0x04, 0x60, 0x21, 0x63, 0x1b, + 0x65, 0x17, 0x68, 0x13, 0x6b, 0x10, 0x6e, 0x0e, 0x70, 0x0c, 0x73, 0x0a, + 0x75, 0x09, 0x77, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7d, 0x05, 0x7f, 0x04, + 0x81, 0x03, 0x82, 0x03, 0x84, 0x02, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, + 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x5c, 0x00, 0x56, + 0x00, 0x47, 0x00, 0x31, 0x00, 0x1a, 0x04, 0x08, 0x10, 0x00, 0x25, 0x00, + 0x37, 0x00, 0x46, 0x00, 0x52, 0x00, 0x5d, 0x00, 0x65, 0x00, 0x6c, 0x00, + 0x72, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, + 0x01, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, + 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x5c, 0x00, 0x55, + 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x09, 0x00, 0x1f, 0x00, 0x33, 0x00, + 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, + 0x77, 0x00, 0x7b, 0x00, 0x42, 0x31, 0x44, 0x2a, 0x48, 0x24, 0x4b, 0x1f, + 0x4d, 0x1b, 0x51, 0x18, 0x55, 0x15, 0x57, 0x12, 0x59, 0x10, 0x5b, 0x0e, + 0x5f, 0x0c, 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x6a, 0x07, + 0x6b, 0x06, 0x6e, 0x06, 0x71, 0x05, 0x73, 0x05, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x43, 0x0e, 0x38, 0x13, 0x2f, 0x18, 0x28, 0x1d, 0x22, 0x22, 0x1d, + 0x27, 0x19, 0x2b, 0x16, 0x30, 0x12, 0x35, 0x11, 0x39, 0x0f, 0x3c, 0x0c, + 0x41, 0x0b, 0x44, 0x0a, 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x50, 0x05, + 0x54, 0x05, 0x55, 0x04, 0x60, 0x21, 0x63, 0x1c, 0x65, 0x17, 0x68, 0x14, + 0x6a, 0x11, 0x6c, 0x0e, 0x6f, 0x0c, 0x71, 0x0b, 0x73, 0x09, 0x76, 0x08, + 0x78, 0x07, 0x79, 0x06, 0x7c, 0x05, 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, + 0x82, 0x03, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, + 0x5f, 0x00, 0x66, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4d, 0x00, 0x3b, + 0x00, 0x27, 0x00, 0x13, 0x06, 0x07, 0x10, 0x00, 0x22, 0x00, 0x32, 0x00, + 0x40, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, 0x00, + 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, + 0x5f, 0x00, 0x66, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, + 0x00, 0x1f, 0x00, 0x08, 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, 0x00, + 0x42, 0x32, 0x44, 0x2b, 0x47, 0x25, 0x4b, 0x21, 0x4c, 0x1d, 0x4f, 0x19, + 0x54, 0x16, 0x56, 0x14, 0x58, 0x11, 0x5a, 0x10, 0x5d, 0x0e, 0x61, 0x0c, + 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x69, 0x07, 0x6b, 0x07, + 0x6d, 0x06, 0x71, 0x05, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x43, 0x0e, 0x39, + 0x12, 0x31, 0x17, 0x2a, 0x1c, 0x24, 0x21, 0x1f, 0x26, 0x1b, 0x2a, 0x18, + 0x2e, 0x16, 0x32, 0x12, 0x35, 0x0f, 0x3a, 0x0f, 0x3d, 0x0c, 0x41, 0x0b, + 0x44, 0x0a, 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x4f, 0x05, 0x53, 0x05, + 0x60, 0x21, 0x62, 0x1c, 0x65, 0x18, 0x67, 0x15, 0x6a, 0x12, 0x6c, 0x0f, + 0x6e, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x09, 0x76, 0x07, 0x79, 0x07, + 0x7a, 0x06, 0x7c, 0x05, 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x82, 0x03, + 0x83, 0x02, 0x85, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, + 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x42, 0x00, 0x32, 0x00, 0x20, + 0x00, 0x0e, 0x07, 0x06, 0x10, 0x00, 0x20, 0x00, 0x2f, 0x00, 0x3b, 0x00, + 0x46, 0x00, 0x50, 0x00, 0x58, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, + 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, + 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, + 0x00, 0x03, 0x0f, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, + 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, 0x00, 0x42, 0x33, 0x43, 0x2c, + 0x46, 0x27, 0x4a, 0x22, 0x4c, 0x1e, 0x4d, 0x1b, 0x52, 0x18, 0x55, 0x15, + 0x57, 0x13, 0x58, 0x11, 0x5a, 0x0f, 0x5e, 0x0e, 0x62, 0x0c, 0x63, 0x0b, + 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x69, 0x07, 0x6b, 0x07, 0x6c, 0x06, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0d, 0x3a, 0x12, 0x32, 0x16, 0x2b, + 0x1b, 0x26, 0x1f, 0x22, 0x24, 0x1d, 0x28, 0x19, 0x2b, 0x16, 0x30, 0x14, + 0x34, 0x12, 0x37, 0x0f, 0x3b, 0x0e, 0x3e, 0x0c, 0x41, 0x0b, 0x45, 0x0a, + 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x4f, 0x05, 0x60, 0x22, 0x62, 0x1d, + 0x65, 0x19, 0x66, 0x15, 0x69, 0x13, 0x6b, 0x11, 0x6d, 0x0e, 0x6f, 0x0c, + 0x71, 0x0b, 0x73, 0x0a, 0x76, 0x09, 0x77, 0x07, 0x79, 0x07, 0x7a, 0x06, + 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, 0x82, 0x03, 0x83, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x5e, 0x00, 0x5b, + 0x00, 0x53, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2a, 0x00, 0x1a, 0x00, 0x0b, + 0x08, 0x05, 0x10, 0x00, 0x1e, 0x00, 0x2c, 0x00, 0x37, 0x00, 0x42, 0x00, + 0x4b, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, + 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x5e, 0x00, 0x5b, + 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, + 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, + 0x54, 0x00, 0x5b, 0x00, 0x42, 0x33, 0x43, 0x2d, 0x45, 0x28, 0x49, 0x23, + 0x4b, 0x1f, 0x4d, 0x1c, 0x50, 0x19, 0x55, 0x16, 0x56, 0x14, 0x58, 0x12, + 0x59, 0x11, 0x5b, 0x0e, 0x5f, 0x0d, 0x62, 0x0c, 0x63, 0x0b, 0x65, 0x0a, + 0x66, 0x09, 0x68, 0x09, 0x69, 0x07, 0x6b, 0x07, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x44, 0x0d, 0x3a, 0x12, 0x33, 0x16, 0x2c, 0x19, 0x27, 0x1e, 0x22, + 0x21, 0x1e, 0x26, 0x1a, 0x2b, 0x18, 0x2e, 0x16, 0x31, 0x12, 0x35, 0x11, + 0x38, 0x0f, 0x3b, 0x0d, 0x3f, 0x0c, 0x41, 0x0a, 0x45, 0x0a, 0x47, 0x09, + 0x4a, 0x07, 0x4e, 0x07, 0x60, 0x22, 0x62, 0x1d, 0x64, 0x19, 0x66, 0x16, + 0x68, 0x13, 0x6a, 0x11, 0x6c, 0x0f, 0x6e, 0x0d, 0x71, 0x0c, 0x73, 0x0b, + 0x74, 0x09, 0x76, 0x08, 0x78, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7c, 0x05, + 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, 0x82, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, + 0x33, 0x00, 0x3d, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x32, 0x00, 0x24, 0x00, 0x15, 0x02, 0x0a, 0x09, 0x04, + 0x10, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x34, 0x00, 0x3e, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, + 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, + 0x33, 0x00, 0x3d, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, + 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x04, 0x00, 0x12, 0x00, + 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, 0x00, + 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, 0x49, 0x24, 0x4b, 0x20, 0x4c, 0x1d, + 0x4e, 0x1a, 0x53, 0x18, 0x55, 0x15, 0x56, 0x13, 0x58, 0x11, 0x5a, 0x10, + 0x5c, 0x0e, 0x60, 0x0d, 0x63, 0x0c, 0x64, 0x0b, 0x65, 0x0a, 0x66, 0x09, + 0x68, 0x09, 0x69, 0x07, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0c, 0x3b, + 0x11, 0x34, 0x16, 0x2e, 0x19, 0x28, 0x1d, 0x23, 0x21, 0x20, 0x25, 0x1d, + 0x29, 0x19, 0x2b, 0x16, 0x30, 0x14, 0x33, 0x12, 0x35, 0x0f, 0x3a, 0x0f, + 0x3c, 0x0c, 0x40, 0x0c, 0x41, 0x0a, 0x45, 0x0a, 0x47, 0x09, 0x4a, 0x07, + 0x60, 0x22, 0x62, 0x1d, 0x64, 0x1a, 0x66, 0x17, 0x68, 0x14, 0x6a, 0x11, + 0x6c, 0x10, 0x6e, 0x0e, 0x70, 0x0c, 0x71, 0x0b, 0x73, 0x0a, 0x75, 0x09, + 0x76, 0x07, 0x78, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7c, 0x05, 0x7e, 0x05, + 0x7f, 0x04, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, + 0x00, 0x2c, 0x00, 0x1f, 0x00, 0x12, 0x03, 0x09, 0x09, 0x04, 0x10, 0x00, + 0x1c, 0x00, 0x27, 0x00, 0x31, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, + 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, + 0x00, 0x24, 0x00, 0x15, 0x00, 0x07, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, + 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, 0x00, 0x42, 0x34, 0x43, 0x2f, + 0x44, 0x29, 0x49, 0x25, 0x4b, 0x21, 0x4c, 0x1e, 0x4d, 0x1b, 0x51, 0x18, + 0x55, 0x16, 0x56, 0x14, 0x58, 0x13, 0x59, 0x11, 0x5a, 0x0f, 0x5d, 0x0e, + 0x61, 0x0c, 0x63, 0x0c, 0x64, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x09, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0c, 0x3c, 0x10, 0x35, 0x15, 0x2f, + 0x19, 0x2a, 0x1c, 0x25, 0x20, 0x22, 0x23, 0x1d, 0x26, 0x1a, 0x2b, 0x18, + 0x2e, 0x16, 0x30, 0x12, 0x35, 0x12, 0x37, 0x0f, 0x3b, 0x0f, 0x3d, 0x0c, + 0x41, 0x0c, 0x42, 0x0a, 0x46, 0x0a, 0x47, 0x09, 0x60, 0x22, 0x62, 0x1e, + 0x64, 0x1a, 0x66, 0x17, 0x68, 0x15, 0x6a, 0x12, 0x6b, 0x11, 0x6d, 0x0e, + 0x6e, 0x0d, 0x71, 0x0c, 0x72, 0x0b, 0x74, 0x09, 0x76, 0x09, 0x77, 0x07, + 0x79, 0x07, 0x7a, 0x06, 0x7c, 0x06, 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x5e, 0x00, 0x5d, + 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x26, + 0x00, 0x1a, 0x00, 0x0f, 0x04, 0x08, 0x0a, 0x04, 0x10, 0x00, 0x1b, 0x00, + 0x25, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, + 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x5e, 0x00, 0x5d, + 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, + 0x00, 0x10, 0x00, 0x03, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, + 0x33, 0x00, 0x3b, 0x00, 0x4f, 0x2a, 0x56, 0x28, 0x59, 0x27, 0x5a, 0x26, + 0x5c, 0x26, 0x5c, 0x26, 0x5c, 0x26, 0x5d, 0x26, 0x5d, 0x26, 0x5d, 0x25, + 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5e, 0x25, 0x5e, 0x25, + 0x5e, 0x24, 0x5e, 0x24, 0x5e, 0x24, 0x5e, 0x24, 0x92, 0x0e, 0x79, 0x14, + 0x6f, 0x18, 0x6b, 0x1b, 0x68, 0x1d, 0x67, 0x1d, 0x66, 0x1e, 0x65, 0x1f, + 0x64, 0x20, 0x64, 0x20, 0x63, 0x20, 0x63, 0x21, 0x63, 0x21, 0x63, 0x21, + 0x63, 0x21, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, + 0x16, 0x23, 0x3c, 0x23, 0x4a, 0x23, 0x50, 0x23, 0x54, 0x23, 0x56, 0x23, + 0x58, 0x23, 0x59, 0x23, 0x5a, 0x23, 0x5a, 0x23, 0x5b, 0x23, 0x5b, 0x23, + 0x5b, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, + 0x5d, 0x23, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x16, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, + 0x00, 0x4b, 0x00, 0x42, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x22, 0x00, 0x17, + 0x00, 0x0c, 0x05, 0x07, 0x0a, 0x03, 0x0f, 0x00, 0x1a, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, + 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, + 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x16, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, + 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, + 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, 0x00, + 0x48, 0x31, 0x4f, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x29, 0x58, 0x29, + 0x59, 0x28, 0x59, 0x27, 0x5a, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0xa1, 0x01, 0x88, 0x07, 0x7b, 0x0c, 0x74, 0x0f, + 0x70, 0x12, 0x6d, 0x14, 0x6b, 0x16, 0x6a, 0x17, 0x69, 0x18, 0x68, 0x19, + 0x67, 0x1a, 0x66, 0x1b, 0x66, 0x1b, 0x65, 0x1c, 0x65, 0x1c, 0x65, 0x1d, + 0x65, 0x1d, 0x64, 0x1d, 0x64, 0x1e, 0x64, 0x1e, 0x0a, 0x32, 0x22, 0x25, + 0x31, 0x24, 0x3b, 0x23, 0x42, 0x23, 0x47, 0x23, 0x4a, 0x23, 0x4d, 0x23, + 0x4f, 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, 0x23, 0x55, 0x23, + 0x56, 0x23, 0x56, 0x23, 0x57, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, + 0x00, 0x3c, 0x00, 0x32, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x0b, + 0x06, 0x07, 0x0b, 0x03, 0x0f, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, + 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, + 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, + 0x00, 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x01, 0x00, + 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, 0x00, 0x45, 0x34, 0x4b, 0x31, + 0x4f, 0x2f, 0x52, 0x2d, 0x54, 0x2c, 0x55, 0x2b, 0x57, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x29, 0x58, 0x29, 0x59, 0x28, 0x59, 0x27, 0x5a, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0xa7, 0x00, 0x92, 0x03, 0x84, 0x06, 0x7d, 0x09, 0x77, 0x0c, 0x73, 0x0e, + 0x71, 0x10, 0x6f, 0x11, 0x6d, 0x13, 0x6b, 0x14, 0x6a, 0x15, 0x6a, 0x16, + 0x69, 0x17, 0x68, 0x17, 0x68, 0x18, 0x67, 0x19, 0x66, 0x19, 0x66, 0x1a, + 0x66, 0x1a, 0x66, 0x1a, 0x0a, 0x38, 0x18, 0x2b, 0x25, 0x27, 0x2e, 0x25, + 0x35, 0x24, 0x3b, 0x23, 0x3f, 0x23, 0x43, 0x23, 0x46, 0x23, 0x48, 0x23, + 0x4a, 0x23, 0x4c, 0x23, 0x4e, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x51, 0x23, + 0x52, 0x23, 0x53, 0x23, 0x53, 0x23, 0x54, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, + 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x48, 0x00, 0x40, 0x00, 0x37, + 0x00, 0x2d, 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x01, 0x0a, 0x06, 0x06, + 0x0b, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, + 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, + 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, + 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, + 0x00, 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x03, 0x00, 0x0d, 0x00, + 0x17, 0x00, 0x1f, 0x00, 0x44, 0x36, 0x49, 0x33, 0x4d, 0x31, 0x4f, 0x2f, + 0x51, 0x2e, 0x53, 0x2d, 0x53, 0x2c, 0x55, 0x2b, 0x56, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x58, 0x29, + 0x59, 0x28, 0x5a, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0xaa, 0x00, 0x98, 0x01, + 0x8c, 0x03, 0x83, 0x05, 0x7d, 0x08, 0x79, 0x0a, 0x76, 0x0c, 0x73, 0x0d, + 0x71, 0x0f, 0x6f, 0x10, 0x6e, 0x11, 0x6d, 0x12, 0x6c, 0x13, 0x6b, 0x14, + 0x6a, 0x15, 0x6a, 0x15, 0x69, 0x16, 0x68, 0x17, 0x68, 0x17, 0x68, 0x18, + 0x0a, 0x3c, 0x14, 0x30, 0x1e, 0x2a, 0x26, 0x27, 0x2d, 0x25, 0x32, 0x24, + 0x36, 0x24, 0x3b, 0x24, 0x3e, 0x23, 0x41, 0x23, 0x43, 0x23, 0x45, 0x23, + 0x47, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4d, 0x23, 0x4d, 0x23, + 0x4f, 0x23, 0x50, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x31, 0x11, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, + 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x11, 0x03, 0x23, + 0x00, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, + 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, + 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x38, 0x48, 0x35, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x30, 0x50, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2c, 0x54, 0x2b, 0x56, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x58, 0x29, 0xac, 0x00, 0x9d, 0x00, 0x91, 0x02, 0x89, 0x03, + 0x82, 0x05, 0x7e, 0x07, 0x7a, 0x09, 0x77, 0x0a, 0x75, 0x0c, 0x73, 0x0d, + 0x71, 0x0e, 0x70, 0x0f, 0x6e, 0x10, 0x6e, 0x11, 0x6c, 0x12, 0x6c, 0x13, + 0x6b, 0x13, 0x6a, 0x14, 0x6a, 0x15, 0x6a, 0x15, 0x09, 0x3f, 0x11, 0x33, + 0x1a, 0x2d, 0x21, 0x29, 0x27, 0x27, 0x2c, 0x26, 0x30, 0x25, 0x34, 0x24, + 0x37, 0x24, 0x3a, 0x24, 0x3d, 0x23, 0x40, 0x23, 0x42, 0x23, 0x44, 0x23, + 0x45, 0x23, 0x47, 0x23, 0x48, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x23, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, + 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x3c, + 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, + 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x39, 0x46, 0x36, + 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x54, 0x2b, 0x55, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, + 0xae, 0x00, 0xa1, 0x00, 0x96, 0x01, 0x8d, 0x02, 0x87, 0x03, 0x82, 0x05, + 0x7e, 0x07, 0x7b, 0x08, 0x78, 0x09, 0x76, 0x0b, 0x74, 0x0c, 0x73, 0x0d, + 0x71, 0x0e, 0x70, 0x0e, 0x6f, 0x0f, 0x6e, 0x11, 0x6d, 0x11, 0x6c, 0x11, + 0x6c, 0x12, 0x6b, 0x13, 0x09, 0x40, 0x10, 0x36, 0x17, 0x2f, 0x1d, 0x2c, + 0x22, 0x29, 0x27, 0x27, 0x2b, 0x26, 0x2f, 0x25, 0x32, 0x25, 0x35, 0x24, + 0x38, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3f, 0x23, 0x40, 0x23, 0x42, 0x23, + 0x44, 0x23, 0x45, 0x23, 0x46, 0x23, 0x48, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x1f, + 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, 0x00, + 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, + 0x99, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x39, + 0x00, 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, + 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x33, + 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x52, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x54, 0x2c, 0x55, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0xaf, 0x00, 0xa3, 0x00, + 0x99, 0x00, 0x91, 0x01, 0x8b, 0x02, 0x86, 0x03, 0x81, 0x05, 0x7e, 0x06, + 0x7b, 0x07, 0x79, 0x09, 0x77, 0x0a, 0x75, 0x0b, 0x74, 0x0c, 0x73, 0x0c, + 0x71, 0x0d, 0x71, 0x0e, 0x6f, 0x0f, 0x6e, 0x10, 0x6e, 0x11, 0x6d, 0x11, + 0x09, 0x41, 0x0f, 0x38, 0x15, 0x32, 0x1a, 0x2e, 0x1f, 0x2b, 0x24, 0x29, + 0x27, 0x27, 0x2b, 0x26, 0x2e, 0x25, 0x31, 0x25, 0x34, 0x25, 0x36, 0x24, + 0x38, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3e, 0x23, 0x40, 0x23, 0x41, 0x23, + 0x43, 0x23, 0x44, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x1f, 0x00, + 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x84, 0x00, + 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x7c, 0x00, + 0x4d, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, + 0x00, 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, + 0x00, 0x53, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3a, 0x45, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x53, 0x2c, 0x55, 0x2b, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0xb0, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x95, 0x00, + 0x8e, 0x02, 0x89, 0x02, 0x85, 0x03, 0x81, 0x05, 0x7e, 0x06, 0x7c, 0x07, + 0x7a, 0x08, 0x78, 0x09, 0x76, 0x0a, 0x75, 0x0b, 0x73, 0x0c, 0x73, 0x0c, + 0x71, 0x0d, 0x71, 0x0e, 0x70, 0x0e, 0x6e, 0x0f, 0x09, 0x42, 0x0e, 0x39, + 0x13, 0x33, 0x18, 0x2f, 0x1c, 0x2d, 0x21, 0x2a, 0x24, 0x29, 0x28, 0x27, + 0x2b, 0x26, 0x2e, 0x26, 0x30, 0x25, 0x33, 0x25, 0x35, 0x25, 0x37, 0x24, + 0x39, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x23, 0x40, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x1f, 0x00, 0x3b, 0x00, + 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, + 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, + 0x1f, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, + 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, + 0x46, 0x36, 0x49, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x2f, 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2c, 0x54, 0x2b, 0x56, 0x2a, + 0xb1, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x98, 0x00, 0x91, 0x01, 0x8c, 0x02, + 0x88, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, 0x7c, 0x07, 0x7a, 0x07, + 0x79, 0x09, 0x77, 0x09, 0x76, 0x0b, 0x74, 0x0b, 0x73, 0x0c, 0x73, 0x0c, + 0x71, 0x0d, 0x71, 0x0e, 0x09, 0x43, 0x0e, 0x3b, 0x12, 0x35, 0x16, 0x31, + 0x1a, 0x2e, 0x1e, 0x2c, 0x22, 0x2a, 0x25, 0x28, 0x28, 0x27, 0x2a, 0x27, + 0x2d, 0x26, 0x30, 0x25, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x37, 0x24, + 0x39, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x51, + 0x00, 0x39, 0x00, 0x1a, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, 0x00, + 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, + 0x82, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, + 0x09, 0x00, 0x00, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, + 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x37, 0x48, 0x35, + 0x4a, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0xb1, 0x00, 0xa9, 0x00, + 0xa1, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x87, 0x03, + 0x84, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7d, 0x06, 0x7b, 0x07, 0x79, 0x08, + 0x78, 0x09, 0x76, 0x0a, 0x76, 0x0b, 0x74, 0x0b, 0x73, 0x0c, 0x72, 0x0c, + 0x09, 0x44, 0x0d, 0x3c, 0x11, 0x36, 0x15, 0x33, 0x19, 0x30, 0x1c, 0x2d, + 0x20, 0x2b, 0x23, 0x2a, 0x25, 0x28, 0x28, 0x27, 0x2a, 0x27, 0x2d, 0x26, + 0x2f, 0x26, 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, 0x36, 0x24, 0x38, 0x24, + 0x39, 0x24, 0x3a, 0x24, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, + 0x00, 0x10, 0x09, 0x00, 0x1f, 0x00, 0x33, 0x00, 0x43, 0x00, 0x50, 0x00, + 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x95, 0x00, + 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, 0x00, + 0x00, 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, + 0x00, 0x37, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3c, 0x43, 0x39, 0x46, 0x38, 0x46, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, + 0x50, 0x2e, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9c, 0x00, + 0x96, 0x00, 0x91, 0x01, 0x8d, 0x02, 0x89, 0x02, 0x86, 0x03, 0x83, 0x04, + 0x81, 0x05, 0x7f, 0x06, 0x7d, 0x06, 0x7b, 0x07, 0x79, 0x07, 0x79, 0x09, + 0x77, 0x09, 0x76, 0x0a, 0x75, 0x0b, 0x74, 0x0b, 0x0a, 0x44, 0x0d, 0x3d, + 0x10, 0x38, 0x14, 0x34, 0x17, 0x31, 0x1b, 0x2e, 0x1e, 0x2d, 0x21, 0x2b, + 0x23, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2d, 0x26, 0x2e, 0x26, + 0x30, 0x25, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x36, 0x24, 0x38, 0x24, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, + 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, + 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, + 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x43, 0x39, + 0x46, 0x39, 0x46, 0x37, 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x32, + 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, + 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0xb2, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x98, 0x00, 0x93, 0x00, + 0x8f, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, + 0x7f, 0x06, 0x7d, 0x06, 0x7c, 0x07, 0x7a, 0x07, 0x79, 0x08, 0x78, 0x09, + 0x76, 0x09, 0x76, 0x0a, 0x0a, 0x44, 0x0c, 0x3e, 0x10, 0x39, 0x13, 0x35, + 0x16, 0x32, 0x19, 0x30, 0x1c, 0x2e, 0x1f, 0x2c, 0x21, 0x2b, 0x24, 0x2a, + 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x26, 0x2e, 0x26, 0x30, 0x25, + 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, + 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, 0x03, 0x0f, 0x00, + 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, + 0x60, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, + 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x07, + 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x39, 0x46, 0x39, 0x46, 0x38, + 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0x51, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xac, 0x00, + 0xa5, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x91, 0x01, 0x8e, 0x01, + 0x8a, 0x02, 0x88, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x05, + 0x7d, 0x06, 0x7c, 0x07, 0x7a, 0x07, 0x79, 0x07, 0x78, 0x09, 0x77, 0x09, + 0x0a, 0x44, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x36, 0x15, 0x33, 0x18, 0x30, + 0x1b, 0x2e, 0x1d, 0x2d, 0x20, 0x2b, 0x22, 0x2a, 0x24, 0x2a, 0x26, 0x28, + 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x26, 0x2e, 0x26, 0x2f, 0x26, 0x31, 0x25, + 0x33, 0x25, 0x33, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, + 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x1f, 0x00, + 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, + 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, + 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0c, + 0x00, 0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3a, 0x45, 0x39, 0x46, 0x39, 0x46, 0x36, 0x49, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0xb3, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa1, 0x00, + 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8c, 0x02, 0x89, 0x02, + 0x87, 0x02, 0x85, 0x03, 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7d, 0x06, + 0x7c, 0x06, 0x7b, 0x07, 0x79, 0x07, 0x79, 0x08, 0x0a, 0x45, 0x0c, 0x3f, + 0x0f, 0x3a, 0x11, 0x37, 0x14, 0x34, 0x17, 0x32, 0x1a, 0x30, 0x1c, 0x2e, + 0x1e, 0x2d, 0x21, 0x2b, 0x22, 0x2a, 0x25, 0x2a, 0x26, 0x28, 0x28, 0x28, + 0x2a, 0x27, 0x2c, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x30, 0x25, 0x32, 0x25, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, + 0x00, 0x1b, 0x00, 0x0b, 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, + 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, + 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, 0x00, + 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3a, + 0x44, 0x39, 0x46, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0xb3, 0x00, 0xad, 0x00, 0xa7, 0x00, 0xa3, 0x00, 0x9d, 0x00, 0x99, 0x00, + 0x95, 0x00, 0x91, 0x01, 0x8e, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x86, 0x02, + 0x84, 0x03, 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, + 0x7b, 0x07, 0x7a, 0x07, 0x0a, 0x45, 0x0c, 0x40, 0x0e, 0x3b, 0x11, 0x38, + 0x13, 0x35, 0x16, 0x32, 0x18, 0x30, 0x1b, 0x2e, 0x1d, 0x2d, 0x1f, 0x2c, + 0x21, 0x2b, 0x23, 0x2a, 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, + 0x2c, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x2f, 0x25, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, + 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, + 0x00, 0x07, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, + 0x3d, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, + 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, + 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x39, + 0x46, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, + 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2f, 0x52, 0x2e, 0xb3, 0x00, 0xae, 0x00, + 0xa8, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x93, 0x00, + 0x8f, 0x01, 0x8d, 0x02, 0x8a, 0x02, 0x88, 0x02, 0x86, 0x03, 0x84, 0x03, + 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, 0x7c, 0x07, + 0x0a, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x38, 0x13, 0x36, 0x15, 0x33, + 0x18, 0x31, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, 0x2d, 0x20, 0x2b, 0x22, 0x2b, + 0x23, 0x2a, 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, + 0x2d, 0x26, 0x2f, 0x26, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, + 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, 0x00, 0x10, 0x00, 0x03, + 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, + 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, 0x00, + 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, + 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3c, 0x43, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x36, + 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4c, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x2f, 0xb3, 0x00, 0xae, 0x00, 0xa9, 0x00, 0xa4, 0x00, + 0xa0, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x94, 0x00, 0x91, 0x01, 0x8e, 0x01, + 0x8c, 0x02, 0x89, 0x02, 0x87, 0x02, 0x86, 0x03, 0x84, 0x03, 0x82, 0x04, + 0x80, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, 0x0a, 0x45, 0x0b, 0x40, + 0x0e, 0x3c, 0x10, 0x39, 0x12, 0x37, 0x14, 0x34, 0x17, 0x32, 0x19, 0x30, + 0x1b, 0x2e, 0x1d, 0x2d, 0x1f, 0x2d, 0x20, 0x2b, 0x22, 0x2b, 0x24, 0x2a, + 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, 0x2c, 0x26, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, + 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0a, 0x00, + 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, + 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, + 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3c, + 0x43, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0xb3, 0x00, 0xae, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa1, 0x00, 0x9d, 0x00, + 0x99, 0x00, 0x96, 0x00, 0x93, 0x00, 0x90, 0x01, 0x8d, 0x01, 0x8b, 0x02, + 0x89, 0x02, 0x87, 0x02, 0x85, 0x03, 0x83, 0x03, 0x82, 0x04, 0x80, 0x05, + 0x7f, 0x05, 0x7e, 0x06, 0x0a, 0x45, 0x0b, 0x41, 0x0e, 0x3d, 0x10, 0x3a, + 0x11, 0x37, 0x14, 0x34, 0x16, 0x32, 0x18, 0x31, 0x1a, 0x30, 0x1c, 0x2e, + 0x1d, 0x2d, 0x20, 0x2c, 0x21, 0x2b, 0x22, 0x2a, 0x24, 0x2a, 0x25, 0x29, + 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, + 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, + 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, + 0x1f, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, + 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, + 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x39, 0x46, 0x39, + 0x46, 0x39, 0x46, 0x39, 0x46, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0xb4, 0x00, 0xaf, 0x00, + 0xaa, 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x9a, 0x00, 0x97, 0x00, + 0x94, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x02, 0x8a, 0x02, 0x88, 0x02, + 0x86, 0x02, 0x85, 0x03, 0x83, 0x03, 0x82, 0x04, 0x80, 0x05, 0x7f, 0x05, + 0x0a, 0x45, 0x0b, 0x41, 0x0d, 0x3e, 0x10, 0x3a, 0x11, 0x38, 0x13, 0x35, + 0x16, 0x33, 0x17, 0x32, 0x19, 0x30, 0x1b, 0x2e, 0x1d, 0x2e, 0x1e, 0x2d, + 0x20, 0x2b, 0x22, 0x2b, 0x23, 0x2a, 0x25, 0x2a, 0x25, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x2a, 0x27, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, + 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, 0x00, 0x26, 0x00, 0x1b, + 0x00, 0x11, 0x00, 0x06, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, + 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, + 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, + 0x28, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3a, 0x45, 0x39, 0x46, 0x39, 0x46, 0x39, + 0x46, 0x37, 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0xb4, 0x00, 0xaf, 0x00, 0xab, 0x00, 0xa7, 0x00, + 0xa3, 0x00, 0x9f, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x95, 0x00, 0x92, 0x00, + 0x90, 0x01, 0x8d, 0x01, 0x8b, 0x02, 0x89, 0x02, 0x88, 0x02, 0x86, 0x02, + 0x85, 0x03, 0x82, 0x03, 0x82, 0x04, 0x80, 0x05, 0x0a, 0x46, 0x0b, 0x42, + 0x0d, 0x3e, 0x0f, 0x3b, 0x11, 0x38, 0x13, 0x36, 0x15, 0x34, 0x16, 0x32, + 0x18, 0x30, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2d, 0x20, 0x2d, 0x20, 0x2b, + 0x22, 0x2b, 0x23, 0x2a, 0x25, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x32, 0x74, 0x38, 0x57, 0x3a, 0x4f, 0x3c, 0x4b, 0x3c, 0x49, 0x3d, 0x47, + 0x3d, 0x46, 0x3d, 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, + 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, + 0x3d, 0x42, 0x3d, 0x42, 0x41, 0x09, 0x1b, 0x31, 0x27, 0x38, 0x2d, 0x3a, + 0x31, 0x3b, 0x33, 0x3c, 0x35, 0x3c, 0x36, 0x3d, 0x37, 0x3d, 0x38, 0x3d, + 0x38, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x73, 0x00, 0x38, 0x31, + 0x3a, 0x38, 0x3c, 0x3a, 0x3c, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x3d, 0x28, 0x38, 0x30, + 0x38, 0x34, 0x39, 0x36, 0x39, 0x38, 0x3a, 0x39, 0x3a, 0x39, 0x3b, 0x3a, + 0x3b, 0x3b, 0x3c, 0x3b, 0x3c, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3e, 0x3c, + 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3d, 0x3e, 0x3d, 0x3e, 0x3d, + 0x00, 0x7e, 0x05, 0x58, 0x0f, 0x4e, 0x17, 0x49, 0x1d, 0x47, 0x21, 0x45, + 0x24, 0x44, 0x27, 0x43, 0x29, 0x43, 0x2b, 0x43, 0x2d, 0x43, 0x2e, 0x42, + 0x2f, 0x42, 0x31, 0x42, 0x31, 0x42, 0x32, 0x42, 0x33, 0x42, 0x33, 0x42, + 0x34, 0x42, 0x34, 0x42, 0x2b, 0x4e, 0x31, 0x47, 0x34, 0x45, 0x36, 0x44, + 0x38, 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, + 0x3c, 0x41, 0x3c, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, + 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x4f, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x2f, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x16, 0x3a, 0x20, 0x39, 0x25, 0x39, 0x29, + 0x39, 0x2d, 0x39, 0x2f, 0x3a, 0x31, 0x3a, 0x32, 0x3b, 0x33, 0x3a, 0x34, + 0x3a, 0x35, 0x3a, 0x36, 0x3a, 0x36, 0x3b, 0x37, 0x3b, 0x37, 0x3c, 0x38, + 0x3c, 0x38, 0x3d, 0x38, 0x3e, 0x39, 0x3e, 0x39, 0x00, 0x93, 0x01, 0x70, + 0x06, 0x60, 0x0d, 0x56, 0x12, 0x52, 0x16, 0x4e, 0x1a, 0x4c, 0x1d, 0x4a, + 0x20, 0x48, 0x22, 0x47, 0x24, 0x46, 0x26, 0x45, 0x27, 0x44, 0x29, 0x44, + 0x2a, 0x44, 0x2b, 0x44, 0x2c, 0x43, 0x2d, 0x43, 0x2e, 0x43, 0x2f, 0x43, + 0x28, 0x56, 0x2e, 0x4f, 0x31, 0x4b, 0x33, 0x49, 0x35, 0x48, 0x36, 0x46, + 0x37, 0x45, 0x38, 0x45, 0x39, 0x44, 0x39, 0x44, 0x39, 0x43, 0x39, 0x43, + 0x39, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, + 0x3d, 0x42, 0x3d, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x8f, 0x00, 0x73, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x45, 0x00, 0x22, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x43, 0x11, 0x3d, 0x18, 0x3b, 0x1e, 0x3a, 0x22, 0x39, 0x25, 0x39, 0x28, + 0x39, 0x2a, 0x39, 0x2c, 0x39, 0x2d, 0x3a, 0x2f, 0x3b, 0x30, 0x3b, 0x31, + 0x3b, 0x32, 0x3b, 0x33, 0x3a, 0x33, 0x3a, 0x34, 0x3a, 0x34, 0x3a, 0x35, + 0x3a, 0x35, 0x3b, 0x35, 0x00, 0x9d, 0x00, 0x7f, 0x03, 0x6d, 0x07, 0x62, + 0x0c, 0x5b, 0x10, 0x56, 0x13, 0x52, 0x16, 0x50, 0x19, 0x4d, 0x1c, 0x4c, + 0x1e, 0x4b, 0x1f, 0x4b, 0x22, 0x4a, 0x23, 0x49, 0x24, 0x48, 0x25, 0x47, + 0x27, 0x46, 0x28, 0x45, 0x29, 0x44, 0x29, 0x44, 0x27, 0x59, 0x2c, 0x53, + 0x2f, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x34, 0x4a, 0x35, 0x48, 0x35, 0x47, + 0x36, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x45, + 0x39, 0x44, 0x39, 0x44, 0x39, 0x43, 0x39, 0x43, 0x39, 0x42, 0x3a, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x84, + 0x00, 0x5b, 0x00, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x44, 0x0f, 0x3e, 0x14, + 0x3c, 0x19, 0x3a, 0x1d, 0x3a, 0x20, 0x39, 0x23, 0x3a, 0x25, 0x3a, 0x27, + 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2c, 0x3a, 0x2c, 0x3b, 0x2d, 0x3b, 0x2f, + 0x3b, 0x2f, 0x3b, 0x30, 0x3b, 0x31, 0x3b, 0x32, 0x3a, 0x32, 0x3a, 0x33, + 0x00, 0xa3, 0x00, 0x88, 0x01, 0x77, 0x04, 0x6b, 0x08, 0x63, 0x0b, 0x5d, + 0x0e, 0x59, 0x11, 0x57, 0x14, 0x54, 0x16, 0x51, 0x19, 0x4f, 0x1a, 0x4d, + 0x1c, 0x4c, 0x1e, 0x4c, 0x1f, 0x4b, 0x21, 0x4b, 0x22, 0x4a, 0x23, 0x49, + 0x24, 0x49, 0x25, 0x49, 0x27, 0x5a, 0x2a, 0x55, 0x2d, 0x52, 0x2f, 0x4f, + 0x31, 0x4e, 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x49, 0x35, 0x48, + 0x35, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, + 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x45, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x8d, 0x00, 0x6f, 0x00, 0x48, + 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x55, 0x00, + 0x42, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x45, 0x0e, 0x40, 0x12, 0x3d, 0x16, 0x3b, 0x1a, + 0x3b, 0x1d, 0x3a, 0x20, 0x39, 0x22, 0x39, 0x23, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2b, 0x3a, 0x2c, 0x3b, 0x2d, + 0x3c, 0x2e, 0x3b, 0x2e, 0x3b, 0x2f, 0x3b, 0x30, 0x00, 0xa7, 0x00, 0x90, + 0x00, 0x7e, 0x02, 0x73, 0x05, 0x6a, 0x08, 0x65, 0x0b, 0x5f, 0x0d, 0x5b, + 0x10, 0x58, 0x12, 0x56, 0x14, 0x55, 0x16, 0x52, 0x18, 0x50, 0x1a, 0x4e, + 0x1b, 0x4d, 0x1d, 0x4c, 0x1e, 0x4c, 0x1f, 0x4b, 0x20, 0x4b, 0x21, 0x4b, + 0x26, 0x5b, 0x2a, 0x57, 0x2c, 0x54, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4e, + 0x31, 0x4c, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, + 0x35, 0x47, 0x36, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, + 0x39, 0x46, 0x39, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, + 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x46, 0x0d, 0x41, 0x11, 0x3d, 0x14, 0x3d, 0x17, 0x3b, 0x1a, 0x3b, 0x1c, + 0x3b, 0x1e, 0x39, 0x21, 0x39, 0x22, 0x3a, 0x23, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2a, 0x39, 0x2b, 0x3b, 0x2c, + 0x3c, 0x2d, 0x3c, 0x2e, 0x00, 0xa9, 0x00, 0x95, 0x00, 0x85, 0x01, 0x79, + 0x03, 0x70, 0x06, 0x69, 0x08, 0x65, 0x0b, 0x61, 0x0d, 0x5c, 0x0f, 0x59, + 0x11, 0x58, 0x13, 0x56, 0x14, 0x55, 0x16, 0x53, 0x18, 0x51, 0x19, 0x4f, + 0x1b, 0x4d, 0x1c, 0x4d, 0x1d, 0x4c, 0x1e, 0x4c, 0x26, 0x5c, 0x29, 0x58, + 0x2b, 0x55, 0x2d, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4d, + 0x32, 0x4b, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, + 0x35, 0x48, 0x35, 0x47, 0x36, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, + 0x00, 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0c, 0x42, 0x0f, + 0x3f, 0x13, 0x3d, 0x15, 0x3c, 0x18, 0x3a, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, + 0x39, 0x20, 0x39, 0x21, 0x39, 0x22, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x27, 0x39, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2a, 0x3a, 0x2b, + 0x00, 0xab, 0x00, 0x99, 0x00, 0x8b, 0x01, 0x7e, 0x02, 0x77, 0x04, 0x6f, + 0x06, 0x69, 0x08, 0x66, 0x0b, 0x62, 0x0d, 0x5e, 0x0e, 0x5b, 0x10, 0x59, + 0x12, 0x57, 0x13, 0x56, 0x15, 0x55, 0x16, 0x54, 0x18, 0x52, 0x19, 0x50, + 0x1a, 0x4e, 0x1b, 0x4d, 0x26, 0x5c, 0x28, 0x59, 0x2a, 0x57, 0x2c, 0x53, + 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, + 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x49, 0x35, 0x48, 0x36, 0x46, 0x37, 0x46, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, + 0x00, 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, + 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0c, 0x42, 0x0e, 0x40, 0x11, 0x3d, 0x13, + 0x3d, 0x16, 0x3b, 0x18, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1e, 0x3a, 0x1f, + 0x39, 0x21, 0x39, 0x21, 0x3a, 0x23, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x27, 0x39, 0x27, 0x39, 0x28, 0x39, 0x2a, 0x00, 0xac, 0x00, 0x9c, + 0x00, 0x8f, 0x00, 0x83, 0x02, 0x7b, 0x03, 0x74, 0x05, 0x6d, 0x07, 0x69, + 0x09, 0x66, 0x0b, 0x63, 0x0c, 0x5f, 0x0e, 0x5c, 0x0f, 0x5a, 0x11, 0x58, + 0x12, 0x57, 0x14, 0x56, 0x15, 0x55, 0x16, 0x55, 0x18, 0x53, 0x18, 0x51, + 0x26, 0x5d, 0x27, 0x59, 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, 0x53, + 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, + 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, + 0x00, 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, + 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x47, 0x0b, 0x42, 0x0d, 0x41, 0x10, 0x3e, 0x13, 0x3d, 0x15, 0x3c, 0x17, + 0x3a, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x20, + 0x39, 0x21, 0x3a, 0x22, 0x3b, 0x23, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x27, 0x39, 0x27, 0x00, 0xad, 0x00, 0x9f, 0x00, 0x92, 0x00, 0x88, + 0x01, 0x7e, 0x02, 0x78, 0x04, 0x72, 0x05, 0x6d, 0x07, 0x69, 0x09, 0x66, + 0x0a, 0x64, 0x0c, 0x61, 0x0d, 0x5d, 0x0f, 0x5a, 0x10, 0x59, 0x11, 0x58, + 0x13, 0x57, 0x14, 0x56, 0x15, 0x55, 0x16, 0x55, 0x26, 0x5d, 0x27, 0x5a, + 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x32, 0x4b, 0x33, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, + 0x00, 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, + 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, + 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x43, 0x0d, + 0x41, 0x0f, 0x3f, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x17, 0x3a, 0x19, + 0x3a, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x1f, 0x39, 0x21, + 0x39, 0x21, 0x3b, 0x22, 0x3b, 0x24, 0x3a, 0x24, 0x3a, 0x24, 0x3a, 0x26, + 0x00, 0xae, 0x00, 0xa2, 0x00, 0x94, 0x00, 0x8b, 0x00, 0x82, 0x02, 0x7b, + 0x03, 0x76, 0x05, 0x71, 0x06, 0x6c, 0x07, 0x69, 0x09, 0x66, 0x0a, 0x64, + 0x0c, 0x62, 0x0d, 0x5f, 0x0e, 0x5b, 0x10, 0x5a, 0x10, 0x58, 0x12, 0x58, + 0x13, 0x56, 0x14, 0x56, 0x25, 0x5d, 0x27, 0x5b, 0x29, 0x57, 0x2a, 0x57, + 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4d, 0x33, 0x4a, 0x34, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, + 0x00, 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, + 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, 0x00, + 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0f, 0x40, 0x11, + 0x3d, 0x13, 0x3e, 0x14, 0x3c, 0x16, 0x3b, 0x17, 0x3a, 0x19, 0x3b, 0x1a, + 0x3c, 0x1c, 0x3b, 0x1d, 0x3b, 0x1f, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, + 0x3a, 0x21, 0x3b, 0x22, 0x3b, 0x24, 0x3a, 0x24, 0x00, 0xae, 0x00, 0xa3, + 0x00, 0x97, 0x00, 0x8e, 0x00, 0x86, 0x01, 0x7e, 0x02, 0x79, 0x03, 0x75, + 0x05, 0x70, 0x06, 0x6b, 0x07, 0x68, 0x09, 0x66, 0x0a, 0x64, 0x0b, 0x63, + 0x0c, 0x5f, 0x0e, 0x5d, 0x0f, 0x5a, 0x10, 0x59, 0x11, 0x58, 0x13, 0x58, + 0x25, 0x5d, 0x27, 0x5c, 0x29, 0x58, 0x2a, 0x57, 0x2a, 0x56, 0x2d, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, + 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, + 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, 0x00, + 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x47, 0x0b, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x10, 0x3e, 0x12, 0x3d, 0x13, + 0x3e, 0x15, 0x3c, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, + 0x3b, 0x1c, 0x3b, 0x1e, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, 0x39, 0x21, + 0x3b, 0x22, 0x3b, 0x23, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, + 0x00, 0x8a, 0x01, 0x81, 0x02, 0x7c, 0x02, 0x77, 0x04, 0x74, 0x05, 0x6f, + 0x06, 0x6a, 0x07, 0x68, 0x09, 0x66, 0x0a, 0x64, 0x0b, 0x63, 0x0c, 0x61, + 0x0e, 0x5e, 0x0e, 0x5b, 0x10, 0x5a, 0x10, 0x59, 0x25, 0x5d, 0x27, 0x5c, + 0x28, 0x59, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, + 0x00, 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, + 0x00, 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, + 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x44, 0x0d, + 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3d, 0x13, 0x3e, 0x14, 0x3d, 0x15, + 0x3c, 0x17, 0x3a, 0x18, 0x3a, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, + 0x3b, 0x1e, 0x39, 0x1f, 0x39, 0x1f, 0x39, 0x21, 0x39, 0x21, 0x3a, 0x21, + 0x00, 0xaf, 0x00, 0xa6, 0x00, 0x9c, 0x00, 0x92, 0x00, 0x8c, 0x00, 0x85, + 0x01, 0x7e, 0x02, 0x7a, 0x03, 0x76, 0x05, 0x73, 0x06, 0x6e, 0x07, 0x6a, + 0x08, 0x68, 0x09, 0x66, 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x62, 0x0d, 0x5f, + 0x0e, 0x5c, 0x0f, 0x5a, 0x25, 0x5d, 0x27, 0x5c, 0x27, 0x59, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x34, 0x4a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, + 0x00, 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, + 0x00, 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, + 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, 0x00, + 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0f, + 0x40, 0x11, 0x3d, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x16, 0x3b, 0x17, + 0x3a, 0x19, 0x3a, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1e, + 0x3a, 0x1f, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, 0x00, 0xb0, 0x00, 0xa7, + 0x00, 0x9e, 0x00, 0x94, 0x00, 0x8e, 0x00, 0x88, 0x01, 0x81, 0x02, 0x7c, + 0x02, 0x78, 0x03, 0x75, 0x05, 0x72, 0x06, 0x6d, 0x07, 0x6a, 0x08, 0x68, + 0x09, 0x66, 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x62, 0x0d, 0x60, 0x0e, 0x5d, + 0x25, 0x5d, 0x27, 0x5c, 0x27, 0x5a, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x2c, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4e, 0x31, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, + 0x00, 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, + 0x00, 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, + 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, 0x00, + 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x10, 0x3e, 0x11, + 0x3d, 0x13, 0x3e, 0x14, 0x3d, 0x15, 0x3c, 0x17, 0x3a, 0x17, 0x3a, 0x19, + 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, + 0x39, 0x1f, 0x39, 0x1f, 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xa0, 0x00, 0x96, + 0x00, 0x90, 0x00, 0x8a, 0x00, 0x84, 0x01, 0x7e, 0x02, 0x7a, 0x03, 0x77, + 0x04, 0x74, 0x05, 0x71, 0x06, 0x6c, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, + 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x63, 0x0c, 0x61, 0x25, 0x5e, 0x27, 0x5c, + 0x27, 0x5c, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x00, 0x27, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x8f, 0x00, 0x96, 0x00, 0x99, + 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, + 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x8c, 0x00, 0x63, + 0x00, 0x7f, 0x00, 0x89, 0x00, 0x90, 0x00, 0x96, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, + 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x2c, 0x00, 0x57, 0x00, 0x82, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, + 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, + 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x11, 0x31, 0x00, 0x5f, + 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, + 0x00, 0x9e, 0x00, 0x9e, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0b, + 0x42, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3d, 0x12, 0x3d, 0x13, + 0x3e, 0x15, 0x3c, 0x15, 0x3c, 0x17, 0x3a, 0x17, 0x3a, 0x19, 0x3b, 0x19, + 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x1f, + 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x98, 0x00, 0x91, 0x00, 0x8c, + 0x00, 0x87, 0x01, 0x80, 0x02, 0x7c, 0x02, 0x79, 0x03, 0x76, 0x05, 0x74, + 0x05, 0x70, 0x06, 0x6b, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, 0x0a, 0x65, + 0x0b, 0x64, 0x0c, 0x63, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x58, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x4f, 0x00, 0x73, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x95, + 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, + 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x63, 0x00, 0x4d, 0x00, 0x60, 0x00, 0x77, + 0x00, 0x83, 0x00, 0x8c, 0x00, 0x92, 0x00, 0x95, 0x00, 0x98, 0x00, 0x99, + 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x01, 0x04, 0x16, + 0x00, 0x57, 0x00, 0x78, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x94, 0x00, 0x96, + 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, + 0x00, 0x9d, 0x00, 0x9d, 0x23, 0x03, 0x00, 0x20, 0x00, 0x5f, 0x00, 0x7c, + 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, + 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x41, 0x0e, + 0x42, 0x0f, 0x40, 0x11, 0x3d, 0x11, 0x3d, 0x13, 0x3f, 0x13, 0x3d, 0x15, + 0x3c, 0x16, 0x3b, 0x17, 0x3a, 0x18, 0x3a, 0x19, 0x3b, 0x19, 0x3c, 0x1a, + 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3b, 0x1e, 0x00, 0xb1, 0x00, 0xa9, + 0x00, 0xa2, 0x00, 0x9a, 0x00, 0x93, 0x00, 0x8d, 0x00, 0x89, 0x01, 0x83, + 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x78, 0x03, 0x75, 0x05, 0x73, 0x06, 0x6f, + 0x06, 0x6b, 0x07, 0x69, 0x08, 0x68, 0x09, 0x66, 0x0a, 0x66, 0x0b, 0x64, + 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x59, 0x2a, 0x57, 0x2a, 0x57, + 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x38, + 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, + 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, + 0x00, 0x7f, 0x00, 0x60, 0x00, 0x24, 0x00, 0x48, 0x00, 0x60, 0x00, 0x70, + 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x93, + 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x27, 0x00, 0x01, 0x12, 0x00, 0x43, + 0x00, 0x61, 0x00, 0x74, 0x00, 0x7f, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, + 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, + 0x46, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, + 0x00, 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, + 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x41, 0x0e, 0x42, 0x0f, 0x40, 0x10, + 0x3e, 0x11, 0x3d, 0x13, 0x3d, 0x13, 0x3f, 0x15, 0x3c, 0x15, 0x3c, 0x17, + 0x3a, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, + 0x3b, 0x1c, 0x3b, 0x1c, 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa3, 0x00, 0x9c, + 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8b, 0x00, 0x86, 0x01, 0x80, 0x02, 0x7c, + 0x02, 0x79, 0x03, 0x77, 0x04, 0x75, 0x05, 0x72, 0x06, 0x6e, 0x07, 0x6b, + 0x07, 0x69, 0x09, 0x68, 0x09, 0x66, 0x0a, 0x66, 0x24, 0x5e, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5a, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x48, + 0x00, 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, + 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x89, 0x00, 0x77, + 0x00, 0x48, 0x00, 0x0f, 0x00, 0x30, 0x00, 0x48, 0x00, 0x5d, 0x00, 0x6c, + 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, + 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x41, 0x00, 0x17, 0x00, 0x00, 0x10, 0x00, 0x36, 0x00, 0x51, + 0x00, 0x64, 0x00, 0x71, 0x00, 0x7a, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, + 0x00, 0x8d, 0x00, 0x90, 0x00, 0x92, 0x00, 0x93, 0x52, 0x00, 0x3c, 0x00, + 0x0d, 0x00, 0x00, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, + 0x00, 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, + 0x00, 0x93, 0x00, 0x95, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, + 0x44, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x40, 0x11, 0x3d, 0x11, + 0x3d, 0x13, 0x3f, 0x13, 0x3e, 0x15, 0x3c, 0x15, 0x3c, 0x17, 0x3a, 0x17, + 0x3a, 0x18, 0x3b, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, + 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x95, 0x00, 0x90, + 0x00, 0x8c, 0x00, 0x88, 0x01, 0x82, 0x02, 0x7e, 0x02, 0x7b, 0x02, 0x78, + 0x03, 0x76, 0x05, 0x74, 0x05, 0x71, 0x06, 0x6d, 0x07, 0x6b, 0x07, 0x69, + 0x09, 0x68, 0x09, 0x66, 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5b, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x50, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, + 0x00, 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, + 0x00, 0x86, 0x00, 0x89, 0x00, 0x90, 0x00, 0x83, 0x00, 0x60, 0x00, 0x30, + 0x00, 0x02, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x5e, 0x00, 0x69, + 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x86, 0x00, 0x89, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x4d, 0x00, + 0x2f, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x2e, 0x00, 0x46, 0x00, 0x57, + 0x00, 0x64, 0x00, 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, + 0x00, 0x89, 0x00, 0x8b, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x00, + 0x00, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, + 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, + 0x1b, 0x85, 0x05, 0x96, 0x01, 0x9e, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xa9, + 0x00, 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, + 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, + 0x00, 0xb2, 0x00, 0xb2, 0x1c, 0x6e, 0x03, 0x8a, 0x00, 0x97, 0x00, 0x9e, + 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xac, + 0x00, 0xad, 0x00, 0xad, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, + 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x0e, 0x92, 0x01, 0xa1, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xae, + 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb2, + 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, + 0x00, 0xb4, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, + 0x00, 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, + 0x00, 0x96, 0x00, 0x8c, 0x00, 0x70, 0x00, 0x48, 0x00, 0x22, 0x00, 0x01, + 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, 0x5e, 0x00, 0x68, + 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x23, 0x00, + 0x0a, 0x02, 0x00, 0x10, 0x00, 0x29, 0x00, 0x3d, 0x00, 0x4e, 0x00, 0x5b, + 0x00, 0x65, 0x00, 0x6d, 0x00, 0x74, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, + 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x00, 0x00, 0x04, 0x00, 0x1f, + 0x00, 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, + 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, 0x27, 0x60, 0x0f, 0x73, + 0x07, 0x81, 0x03, 0x8a, 0x01, 0x91, 0x00, 0x95, 0x00, 0x99, 0x00, 0x9d, + 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, + 0x00, 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, + 0x29, 0x3a, 0x0f, 0x59, 0x06, 0x6c, 0x02, 0x79, 0x00, 0x84, 0x00, 0x8b, + 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9f, + 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa6, + 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x14, 0x79, 0x07, 0x88, + 0x03, 0x92, 0x01, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa5, + 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00, 0xac, 0x00, 0xac, + 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, + 0x00, 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x9b, 0x00, 0x92, + 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x18, + 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x66, + 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x56, 0x00, 0x47, 0x00, 0x31, 0x00, 0x1a, 0x00, 0x08, 0x04, + 0x00, 0x10, 0x00, 0x25, 0x00, 0x37, 0x00, 0x46, 0x00, 0x52, 0x00, 0x5d, + 0x00, 0x65, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x77, 0x5c, 0x00, 0x55, 0x00, + 0x43, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x33, + 0x00, 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, + 0x00, 0x77, 0x00, 0x7b, 0x2d, 0x54, 0x17, 0x63, 0x0d, 0x6f, 0x07, 0x78, + 0x04, 0x80, 0x02, 0x86, 0x01, 0x8b, 0x01, 0x8f, 0x00, 0x92, 0x00, 0x95, + 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, + 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa4, 0x31, 0x27, 0x18, 0x40, + 0x0d, 0x52, 0x07, 0x61, 0x04, 0x6c, 0x02, 0x75, 0x01, 0x7c, 0x00, 0x82, + 0x00, 0x87, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, + 0x00, 0x98, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x18, 0x6f, 0x0c, 0x7b, 0x06, 0x84, 0x03, 0x8c, + 0x02, 0x91, 0x01, 0x96, 0x00, 0x99, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, + 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xa8, + 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xab, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, + 0x00, 0x5f, 0x00, 0x66, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x84, 0x00, 0x6c, + 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, 0x14, 0x00, 0x26, + 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, 0x5f, 0x00, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, + 0x4d, 0x00, 0x3b, 0x00, 0x27, 0x00, 0x13, 0x00, 0x07, 0x06, 0x00, 0x10, + 0x00, 0x22, 0x00, 0x32, 0x00, 0x40, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5e, + 0x00, 0x65, 0x00, 0x6b, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, + 0x1f, 0x00, 0x08, 0x00, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, + 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, + 0x30, 0x4e, 0x1d, 0x5a, 0x12, 0x64, 0x0c, 0x6d, 0x08, 0x75, 0x05, 0x7a, + 0x03, 0x7f, 0x02, 0x85, 0x02, 0x89, 0x01, 0x8b, 0x00, 0x8e, 0x00, 0x90, + 0x00, 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9e, 0x00, 0x9f, 0x36, 0x1e, 0x1f, 0x32, 0x12, 0x43, 0x0b, 0x50, + 0x07, 0x5a, 0x05, 0x63, 0x02, 0x6b, 0x01, 0x72, 0x01, 0x78, 0x00, 0x7c, + 0x00, 0x81, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, + 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x1b, 0x6b, 0x0f, 0x74, 0x09, 0x7d, 0x05, 0x83, 0x03, 0x89, 0x02, 0x8d, + 0x01, 0x91, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, + 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, + 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, + 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5e, 0x00, 0x45, + 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, + 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x42, 0x00, + 0x32, 0x00, 0x20, 0x00, 0x0e, 0x00, 0x06, 0x07, 0x00, 0x10, 0x00, 0x20, + 0x00, 0x2f, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x58, 0x00, 0x5f, + 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, + 0x03, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, + 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, 0x33, 0x4b, 0x21, 0x54, + 0x16, 0x5d, 0x10, 0x65, 0x0b, 0x6c, 0x08, 0x72, 0x06, 0x77, 0x04, 0x7b, + 0x03, 0x7f, 0x02, 0x83, 0x02, 0x87, 0x01, 0x8a, 0x01, 0x8c, 0x00, 0x8e, + 0x00, 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x98, + 0x3a, 0x19, 0x24, 0x2a, 0x18, 0x38, 0x10, 0x44, 0x0b, 0x4e, 0x07, 0x57, + 0x05, 0x5f, 0x04, 0x65, 0x02, 0x6b, 0x01, 0x71, 0x01, 0x76, 0x00, 0x79, + 0x00, 0x7e, 0x00, 0x80, 0x00, 0x84, 0x00, 0x87, 0x00, 0x88, 0x00, 0x8b, + 0x00, 0x8d, 0x00, 0x8f, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1d, 0x68, 0x12, 0x70, + 0x0c, 0x77, 0x08, 0x7d, 0x05, 0x82, 0x03, 0x87, 0x02, 0x8b, 0x02, 0x8e, + 0x01, 0x91, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9b, + 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x9d, 0x00, 0x99, + 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, 0x3c, 0x00, 0x26, + 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x37, + 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2a, 0x00, + 0x1a, 0x00, 0x0b, 0x00, 0x05, 0x08, 0x00, 0x10, 0x00, 0x1e, 0x00, 0x2c, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x5e, 0x00, 0x5b, 0x00, + 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, + 0x00, 0x54, 0x00, 0x5b, 0x34, 0x49, 0x25, 0x51, 0x1a, 0x58, 0x13, 0x60, + 0x0e, 0x65, 0x0b, 0x6b, 0x08, 0x71, 0x06, 0x75, 0x05, 0x78, 0x04, 0x7b, + 0x03, 0x7f, 0x02, 0x83, 0x02, 0x86, 0x01, 0x88, 0x01, 0x8a, 0x00, 0x8c, + 0x00, 0x8d, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x92, 0x3b, 0x17, 0x28, 0x24, + 0x1c, 0x30, 0x14, 0x3b, 0x0f, 0x45, 0x0b, 0x4d, 0x07, 0x55, 0x05, 0x5b, + 0x04, 0x61, 0x03, 0x67, 0x02, 0x6b, 0x01, 0x6f, 0x01, 0x74, 0x00, 0x77, + 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x83, 0x00, 0x86, 0x00, 0x87, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x1d, 0x67, 0x14, 0x6d, 0x0e, 0x73, 0x0a, 0x79, + 0x07, 0x7e, 0x05, 0x82, 0x03, 0x86, 0x02, 0x89, 0x02, 0x8c, 0x01, 0x8f, + 0x01, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, + 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, + 0x00, 0x33, 0x00, 0x3d, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x83, + 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x22, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x33, 0x00, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, + 0x56, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x32, 0x00, 0x24, 0x00, 0x15, 0x00, + 0x0a, 0x02, 0x04, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x34, + 0x00, 0x3e, 0x00, 0x46, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, + 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x12, + 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, + 0x36, 0x47, 0x27, 0x4e, 0x1d, 0x54, 0x16, 0x5b, 0x11, 0x61, 0x0e, 0x65, + 0x0b, 0x6a, 0x08, 0x6f, 0x07, 0x73, 0x05, 0x76, 0x05, 0x79, 0x03, 0x7c, + 0x02, 0x7e, 0x02, 0x82, 0x02, 0x85, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8b, + 0x00, 0x8c, 0x00, 0x8d, 0x3d, 0x14, 0x2c, 0x20, 0x20, 0x2a, 0x18, 0x34, + 0x12, 0x3e, 0x0e, 0x45, 0x0a, 0x4c, 0x07, 0x53, 0x06, 0x58, 0x05, 0x5e, + 0x04, 0x63, 0x02, 0x67, 0x02, 0x6c, 0x01, 0x6f, 0x01, 0x72, 0x00, 0x76, + 0x00, 0x78, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x1e, 0x66, 0x16, 0x6b, 0x10, 0x71, 0x0c, 0x76, 0x09, 0x7a, 0x07, 0x7e, + 0x05, 0x81, 0x03, 0x85, 0x03, 0x88, 0x02, 0x8b, 0x02, 0x8d, 0x01, 0x8f, + 0x01, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x98, 0x00, 0x99, + 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, + 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, 0x79, 0x00, 0x68, + 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, + 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x09, 0x03, + 0x04, 0x09, 0x00, 0x10, 0x00, 0x1c, 0x00, 0x27, 0x00, 0x31, 0x00, 0x3a, + 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, + 0x24, 0x00, 0x15, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, + 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, 0x37, 0x46, 0x29, 0x4c, + 0x20, 0x52, 0x19, 0x57, 0x14, 0x5d, 0x10, 0x62, 0x0d, 0x66, 0x0b, 0x69, + 0x09, 0x6e, 0x07, 0x73, 0x06, 0x75, 0x05, 0x77, 0x04, 0x7a, 0x03, 0x7c, + 0x02, 0x7e, 0x02, 0x82, 0x02, 0x84, 0x02, 0x87, 0x01, 0x88, 0x01, 0x89, + 0x3f, 0x13, 0x2f, 0x1c, 0x23, 0x27, 0x1b, 0x2f, 0x15, 0x38, 0x10, 0x3e, + 0x0d, 0x45, 0x0a, 0x4b, 0x08, 0x51, 0x07, 0x57, 0x05, 0x5c, 0x04, 0x60, + 0x03, 0x65, 0x02, 0x67, 0x02, 0x6c, 0x01, 0x6f, 0x01, 0x71, 0x00, 0x75, + 0x00, 0x77, 0x00, 0x79, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1f, 0x65, 0x17, 0x6a, + 0x11, 0x6f, 0x0d, 0x73, 0x0a, 0x77, 0x08, 0x7b, 0x06, 0x7e, 0x05, 0x81, + 0x04, 0x84, 0x03, 0x87, 0x02, 0x89, 0x02, 0x8b, 0x01, 0x8e, 0x01, 0x8f, + 0x01, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x9e, 0x00, 0x9c, + 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x4e, + 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, + 0x32, 0x00, 0x26, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x08, 0x04, 0x04, 0x0a, + 0x00, 0x10, 0x00, 0x1b, 0x00, 0x25, 0x00, 0x2f, 0x5e, 0x00, 0x5d, 0x00, + 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, 0x00, + 0x10, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, + 0x00, 0x33, 0x00, 0x3b, 0x38, 0x45, 0x2b, 0x4a, 0x22, 0x50, 0x1c, 0x55, + 0x16, 0x59, 0x12, 0x5f, 0x0f, 0x63, 0x0d, 0x66, 0x0b, 0x69, 0x09, 0x6e, + 0x07, 0x71, 0x06, 0x74, 0x05, 0x76, 0x05, 0x78, 0x03, 0x7a, 0x03, 0x7c, + 0x02, 0x7e, 0x02, 0x81, 0x02, 0x84, 0x02, 0x86, 0x40, 0x11, 0x31, 0x1b, + 0x26, 0x23, 0x1e, 0x2b, 0x18, 0x33, 0x13, 0x39, 0x0f, 0x40, 0x0c, 0x45, + 0x0a, 0x4b, 0x08, 0x51, 0x07, 0x56, 0x05, 0x59, 0x04, 0x5e, 0x04, 0x61, + 0x02, 0x65, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6e, 0x01, 0x71, 0x00, 0x73, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x64, 0x18, 0x69, 0x13, 0x6d, 0x0f, 0x71, + 0x0c, 0x75, 0x09, 0x78, 0x07, 0x7b, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, + 0x03, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, 0x01, 0x8e, 0x01, 0x8f, + 0x01, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x16, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8e, + 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, 0x47, 0x00, 0x37, + 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, + 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x38, 0x00, 0x2d, 0x00, + 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x07, 0x05, 0x03, 0x0a, 0x00, 0x0f, + 0x00, 0x1a, 0x00, 0x24, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, + 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, + 0x38, 0x45, 0x2d, 0x49, 0x24, 0x4e, 0x1e, 0x53, 0x19, 0x56, 0x14, 0x5b, + 0x11, 0x60, 0x0e, 0x63, 0x0c, 0x66, 0x0a, 0x69, 0x09, 0x6c, 0x07, 0x70, + 0x06, 0x73, 0x06, 0x75, 0x05, 0x77, 0x04, 0x79, 0x03, 0x7a, 0x03, 0x7c, + 0x02, 0x7e, 0x02, 0x80, 0x40, 0x11, 0x32, 0x18, 0x28, 0x20, 0x20, 0x28, + 0x1a, 0x2f, 0x16, 0x35, 0x12, 0x3c, 0x0f, 0x41, 0x0c, 0x46, 0x0a, 0x4b, + 0x08, 0x50, 0x07, 0x54, 0x05, 0x58, 0x05, 0x5c, 0x04, 0x5f, 0x04, 0x62, + 0x02, 0x66, 0x02, 0x69, 0x01, 0x6b, 0x01, 0x6e, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x20, 0x64, 0x19, 0x68, 0x14, 0x6b, 0x10, 0x6f, 0x0d, 0x73, 0x0b, 0x76, + 0x09, 0x79, 0x07, 0x7c, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, + 0x02, 0x88, 0x02, 0x89, 0x02, 0x8b, 0x02, 0x8d, 0x01, 0x8e, 0x01, 0x90, + 0x00, 0x91, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, 0x86, 0x00, 0x7a, + 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, 0x33, 0x00, 0x25, + 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, + 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x28, 0x00, 0x1e, 0x00, + 0x14, 0x00, 0x0b, 0x00, 0x07, 0x06, 0x03, 0x0b, 0x00, 0x0f, 0x00, 0x19, + 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, + 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x01, + 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, 0x39, 0x44, 0x2e, 0x49, + 0x26, 0x4c, 0x1f, 0x52, 0x1a, 0x55, 0x16, 0x58, 0x13, 0x5d, 0x10, 0x61, + 0x0e, 0x64, 0x0c, 0x66, 0x0a, 0x68, 0x09, 0x6c, 0x07, 0x70, 0x07, 0x72, + 0x06, 0x74, 0x05, 0x76, 0x05, 0x78, 0x03, 0x79, 0x03, 0x7b, 0x02, 0x7c, + 0x41, 0x10, 0x34, 0x17, 0x2a, 0x1e, 0x23, 0x25, 0x1d, 0x2c, 0x18, 0x32, + 0x14, 0x37, 0x10, 0x3d, 0x0e, 0x42, 0x0c, 0x46, 0x0a, 0x4b, 0x08, 0x4f, + 0x07, 0x53, 0x05, 0x57, 0x05, 0x5a, 0x04, 0x5e, 0x04, 0x61, 0x03, 0x63, + 0x02, 0x66, 0x02, 0x69, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x63, 0x1a, 0x67, + 0x15, 0x6a, 0x11, 0x6e, 0x0e, 0x71, 0x0c, 0x74, 0x0a, 0x77, 0x08, 0x7a, + 0x07, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, 0x02, 0x87, + 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x90, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, + 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x66, + 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, 0x22, 0x00, 0x16, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x48, 0x00, + 0x40, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, + 0x0a, 0x01, 0x06, 0x06, 0x03, 0x0b, 0x00, 0x0f, 0x5f, 0x00, 0x5d, 0x00, + 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, 0x00, + 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0d, + 0x00, 0x17, 0x00, 0x1f, 0x39, 0x43, 0x2f, 0x48, 0x27, 0x4b, 0x22, 0x4f, + 0x1c, 0x54, 0x18, 0x56, 0x14, 0x59, 0x12, 0x5e, 0x0f, 0x62, 0x0d, 0x64, + 0x0c, 0x66, 0x0a, 0x68, 0x09, 0x6b, 0x08, 0x6f, 0x07, 0x72, 0x06, 0x74, + 0x05, 0x75, 0x05, 0x77, 0x04, 0x78, 0x03, 0x7a, 0x42, 0x0f, 0x36, 0x16, + 0x2c, 0x1c, 0x25, 0x23, 0x1f, 0x29, 0x1a, 0x2e, 0x16, 0x34, 0x12, 0x39, + 0x0f, 0x3e, 0x0d, 0x42, 0x0c, 0x47, 0x0a, 0x4b, 0x08, 0x4f, 0x07, 0x52, + 0x05, 0x56, 0x05, 0x59, 0x04, 0x5c, 0x04, 0x5f, 0x04, 0x62, 0x02, 0x64, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1b, 0x66, 0x16, 0x6a, 0x12, 0x6d, + 0x0f, 0x70, 0x0d, 0x73, 0x0b, 0x75, 0x09, 0x78, 0x07, 0x7a, 0x06, 0x7d, + 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, 0x02, 0x86, 0x02, 0x88, + 0x02, 0x89, 0x02, 0x8b, 0x02, 0x8c, 0x01, 0x8d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x43, 0x31, 0x48, 0x29, 0x4a, 0x23, 0x4d, 0x1e, 0x53, 0x1a, 0x55, + 0x16, 0x57, 0x13, 0x5b, 0x11, 0x60, 0x0f, 0x62, 0x0d, 0x64, 0x0b, 0x66, + 0x0a, 0x68, 0x09, 0x6a, 0x08, 0x6e, 0x07, 0x71, 0x06, 0x73, 0x06, 0x75, + 0x05, 0x76, 0x05, 0x78, 0x43, 0x0f, 0x37, 0x15, 0x2e, 0x1b, 0x27, 0x21, + 0x21, 0x26, 0x1c, 0x2c, 0x18, 0x31, 0x15, 0x36, 0x12, 0x3a, 0x0f, 0x3f, + 0x0c, 0x43, 0x0c, 0x47, 0x0a, 0x4b, 0x08, 0x4e, 0x07, 0x52, 0x06, 0x55, + 0x05, 0x58, 0x05, 0x5b, 0x04, 0x5d, 0x04, 0x60, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x21, 0x63, 0x1b, 0x66, 0x17, 0x69, 0x13, 0x6c, 0x10, 0x6e, 0x0e, 0x71, + 0x0c, 0x74, 0x0a, 0x76, 0x09, 0x79, 0x07, 0x7b, 0x06, 0x7d, 0x06, 0x7f, + 0x05, 0x81, 0x04, 0x82, 0x03, 0x84, 0x03, 0x86, 0x02, 0x87, 0x02, 0x89, + 0x02, 0x8a, 0x02, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x42, 0x31, 0x47, + 0x2a, 0x49, 0x24, 0x4c, 0x1f, 0x51, 0x1b, 0x54, 0x18, 0x56, 0x15, 0x58, + 0x12, 0x5c, 0x10, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0b, 0x66, 0x0a, 0x68, + 0x09, 0x6a, 0x08, 0x6d, 0x07, 0x71, 0x06, 0x72, 0x06, 0x74, 0x05, 0x75, + 0x43, 0x0f, 0x38, 0x14, 0x2f, 0x19, 0x28, 0x1f, 0x22, 0x24, 0x1d, 0x29, + 0x19, 0x2e, 0x16, 0x33, 0x12, 0x37, 0x11, 0x3b, 0x0f, 0x40, 0x0c, 0x43, + 0x0b, 0x47, 0x0a, 0x4b, 0x09, 0x4e, 0x07, 0x51, 0x07, 0x55, 0x05, 0x56, + 0x05, 0x5a, 0x04, 0x5c, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1c, 0x65, + 0x17, 0x68, 0x14, 0x6b, 0x11, 0x6e, 0x0e, 0x70, 0x0c, 0x73, 0x0b, 0x75, + 0x09, 0x77, 0x08, 0x79, 0x07, 0x7b, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, + 0x04, 0x82, 0x03, 0x84, 0x03, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, 0x89, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x42, 0x32, 0x46, 0x2b, 0x49, 0x25, 0x4b, + 0x21, 0x4f, 0x1d, 0x53, 0x19, 0x55, 0x16, 0x57, 0x14, 0x59, 0x11, 0x5e, + 0x10, 0x61, 0x0e, 0x63, 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x6a, + 0x08, 0x6c, 0x07, 0x70, 0x07, 0x72, 0x06, 0x73, 0x43, 0x0e, 0x39, 0x13, + 0x31, 0x18, 0x2a, 0x1d, 0x24, 0x22, 0x1f, 0x27, 0x1b, 0x2b, 0x18, 0x30, + 0x16, 0x35, 0x12, 0x39, 0x0f, 0x3c, 0x0f, 0x41, 0x0c, 0x44, 0x0b, 0x47, + 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x50, 0x07, 0x54, 0x05, 0x55, 0x05, 0x59, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1c, 0x65, 0x18, 0x68, 0x15, 0x6a, + 0x12, 0x6c, 0x0f, 0x6f, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x76, 0x09, 0x78, + 0x07, 0x79, 0x07, 0x7c, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, + 0x03, 0x84, 0x03, 0x85, 0x02, 0x86, 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x42, 0x33, 0x45, 0x2c, 0x49, 0x27, 0x4b, 0x22, 0x4d, 0x1e, 0x52, + 0x1b, 0x54, 0x18, 0x56, 0x15, 0x58, 0x13, 0x5b, 0x10, 0x5f, 0x0f, 0x61, + 0x0e, 0x63, 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x08, 0x6c, + 0x07, 0x6f, 0x07, 0x72, 0x44, 0x0e, 0x3a, 0x12, 0x32, 0x17, 0x2b, 0x1c, + 0x26, 0x21, 0x21, 0x26, 0x1d, 0x2a, 0x19, 0x2e, 0x16, 0x32, 0x14, 0x35, + 0x12, 0x3a, 0x0f, 0x3d, 0x0e, 0x41, 0x0c, 0x44, 0x0b, 0x47, 0x0a, 0x4a, + 0x09, 0x4e, 0x07, 0x4f, 0x07, 0x53, 0x05, 0x55, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x22, 0x62, 0x1d, 0x65, 0x19, 0x67, 0x15, 0x6a, 0x13, 0x6c, 0x10, 0x6e, + 0x0e, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x0a, 0x76, 0x09, 0x79, 0x07, 0x7a, + 0x07, 0x7c, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, 0x03, 0x83, + 0x03, 0x85, 0x02, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x33, 0x44, + 0x2d, 0x48, 0x28, 0x4a, 0x23, 0x4c, 0x1f, 0x50, 0x1c, 0x53, 0x19, 0x55, + 0x16, 0x57, 0x14, 0x58, 0x12, 0x5c, 0x10, 0x60, 0x0e, 0x62, 0x0d, 0x63, + 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x09, 0x6b, 0x07, 0x6e, + 0x44, 0x0d, 0x3a, 0x12, 0x33, 0x16, 0x2c, 0x1b, 0x27, 0x1f, 0x21, 0x24, + 0x1e, 0x28, 0x1a, 0x2b, 0x18, 0x30, 0x16, 0x34, 0x12, 0x37, 0x11, 0x3b, + 0x0f, 0x3e, 0x0d, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, + 0x07, 0x4f, 0x07, 0x53, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x65, + 0x19, 0x66, 0x16, 0x69, 0x13, 0x6b, 0x10, 0x6d, 0x0f, 0x6f, 0x0d, 0x71, + 0x0c, 0x73, 0x0b, 0x76, 0x09, 0x77, 0x08, 0x79, 0x07, 0x7a, 0x06, 0x7c, + 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, 0x03, 0x83, 0x03, 0x85, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x34, 0x44, 0x2e, 0x48, 0x29, 0x49, + 0x24, 0x4b, 0x20, 0x4f, 0x1d, 0x53, 0x1a, 0x55, 0x18, 0x56, 0x15, 0x58, + 0x13, 0x59, 0x11, 0x5d, 0x10, 0x61, 0x0e, 0x62, 0x0d, 0x63, 0x0c, 0x65, + 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x09, 0x6b, 0x44, 0x0d, 0x3b, 0x12, + 0x34, 0x16, 0x2e, 0x19, 0x28, 0x1e, 0x23, 0x22, 0x20, 0x26, 0x1d, 0x2b, + 0x19, 0x2e, 0x16, 0x31, 0x14, 0x35, 0x12, 0x38, 0x0f, 0x3b, 0x0f, 0x3f, + 0x0c, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x4e, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x64, 0x1a, 0x66, 0x17, 0x68, + 0x14, 0x6a, 0x11, 0x6c, 0x10, 0x6e, 0x0e, 0x71, 0x0c, 0x73, 0x0b, 0x74, + 0x0a, 0x76, 0x09, 0x78, 0x07, 0x79, 0x07, 0x7b, 0x06, 0x7c, 0x06, 0x7e, + 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, 0x03, 0x82, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x42, 0x34, 0x43, 0x2f, 0x48, 0x29, 0x49, 0x25, 0x4b, 0x21, 0x4d, + 0x1e, 0x51, 0x1b, 0x54, 0x18, 0x55, 0x16, 0x56, 0x14, 0x58, 0x13, 0x5b, + 0x10, 0x5e, 0x0f, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0c, 0x66, 0x0b, 0x66, + 0x0a, 0x68, 0x09, 0x69, 0x44, 0x0c, 0x3c, 0x11, 0x35, 0x16, 0x2f, 0x19, + 0x2a, 0x1d, 0x25, 0x22, 0x21, 0x25, 0x1d, 0x29, 0x1a, 0x2b, 0x18, 0x30, + 0x16, 0x33, 0x12, 0x35, 0x12, 0x3a, 0x0f, 0x3c, 0x0f, 0x40, 0x0c, 0x41, + 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x22, 0x62, 0x1e, 0x64, 0x1a, 0x66, 0x17, 0x68, 0x15, 0x6a, 0x12, 0x6c, + 0x10, 0x6e, 0x0e, 0x70, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x75, 0x09, 0x76, + 0x09, 0x78, 0x07, 0x79, 0x07, 0x7b, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, + 0x05, 0x80, 0x04, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x35, 0x43, + 0x2f, 0x47, 0x2b, 0x49, 0x26, 0x4b, 0x22, 0x4c, 0x1f, 0x4f, 0x1c, 0x53, + 0x1a, 0x55, 0x18, 0x56, 0x15, 0x58, 0x13, 0x59, 0x12, 0x5c, 0x10, 0x60, + 0x0f, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0c, 0x66, 0x0b, 0x66, 0x0a, 0x68, + 0x44, 0x0c, 0x3c, 0x10, 0x35, 0x15, 0x30, 0x19, 0x2b, 0x1c, 0x26, 0x20, + 0x21, 0x23, 0x1e, 0x26, 0x1c, 0x2b, 0x19, 0x2e, 0x16, 0x30, 0x15, 0x35, + 0x12, 0x37, 0x11, 0x3b, 0x0f, 0x3d, 0x0f, 0x41, 0x0c, 0x42, 0x0c, 0x46, + 0x0a, 0x47, 0x0a, 0x4a, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1e, 0x64, + 0x1a, 0x66, 0x18, 0x68, 0x15, 0x6a, 0x13, 0x6b, 0x10, 0x6d, 0x0f, 0x6e, + 0x0e, 0x71, 0x0c, 0x72, 0x0b, 0x74, 0x0a, 0x76, 0x09, 0x77, 0x08, 0x79, + 0x07, 0x7a, 0x07, 0x7c, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x09, 0x43, 0x09, 0x44, 0x09, 0x45, 0x09, + 0x45, 0x09, 0x46, 0x09, 0x46, 0x09, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0x22, 0x1c, 0x0a, 0x29, 0x0a, 0x31, 0x09, 0x36, 0x09, 0x3a, 0x09, + 0x3b, 0x09, 0x3d, 0x09, 0x3f, 0x09, 0x40, 0x09, 0x40, 0x09, 0x41, 0x09, + 0x42, 0x09, 0x43, 0x09, 0x43, 0x0a, 0x43, 0x0a, 0x44, 0x0a, 0x44, 0x0a, + 0x44, 0x0a, 0x44, 0x0a, 0x23, 0x16, 0x32, 0x0a, 0x38, 0x0a, 0x3c, 0x0a, + 0x3f, 0x09, 0x40, 0x09, 0x41, 0x09, 0x42, 0x09, 0x43, 0x09, 0x44, 0x09, + 0x44, 0x0a, 0x44, 0x0a, 0x44, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, + 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x46, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x16, 0x3e, 0x11, 0x3f, 0x0f, 0x40, 0x0d, 0x41, 0x0d, 0x41, 0x0c, + 0x42, 0x0c, 0x43, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x44, 0x0a, + 0x44, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x46, 0x0a, 0x46, 0x0a, + 0x46, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x6e, 0x03, 0x3a, + 0x0f, 0x27, 0x18, 0x1e, 0x1f, 0x19, 0x24, 0x17, 0x28, 0x14, 0x2c, 0x13, + 0x2f, 0x11, 0x31, 0x11, 0x32, 0x10, 0x34, 0x0f, 0x36, 0x0f, 0x37, 0x0f, + 0x38, 0x0e, 0x39, 0x0e, 0x3a, 0x0d, 0x3a, 0x0d, 0x3b, 0x0c, 0x3c, 0x0c, + 0x23, 0x3c, 0x25, 0x22, 0x2b, 0x18, 0x30, 0x14, 0x33, 0x11, 0x36, 0x10, + 0x38, 0x0f, 0x39, 0x0e, 0x3b, 0x0e, 0x3c, 0x0d, 0x3d, 0x0d, 0x3e, 0x0c, + 0x3f, 0x0c, 0x3f, 0x0c, 0x40, 0x0c, 0x40, 0x0c, 0x40, 0x0b, 0x41, 0x0b, + 0x41, 0x0b, 0x42, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1f, 0x3c, 0x18, + 0x3c, 0x14, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x0f, 0x3f, 0x0e, 0x40, 0x0e, + 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0c, 0x42, 0x0c, + 0x42, 0x0b, 0x42, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x44, 0x0b, 0x44, 0x0b, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x8a, 0x00, 0x59, 0x06, 0x40, 0x0d, 0x32, + 0x12, 0x2a, 0x18, 0x24, 0x1c, 0x20, 0x20, 0x1c, 0x23, 0x1b, 0x26, 0x18, + 0x28, 0x17, 0x2a, 0x16, 0x2c, 0x15, 0x2e, 0x14, 0x2f, 0x13, 0x31, 0x12, + 0x32, 0x12, 0x33, 0x12, 0x34, 0x11, 0x35, 0x10, 0x23, 0x4a, 0x24, 0x31, + 0x27, 0x25, 0x2a, 0x1e, 0x2d, 0x1a, 0x2f, 0x17, 0x32, 0x15, 0x33, 0x13, + 0x35, 0x12, 0x36, 0x11, 0x38, 0x10, 0x39, 0x10, 0x3a, 0x0f, 0x3a, 0x0f, + 0x3b, 0x0e, 0x3c, 0x0e, 0x3c, 0x0e, 0x3d, 0x0e, 0x3e, 0x0d, 0x3e, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x25, 0x3c, 0x1d, 0x3c, 0x19, 0x3c, 0x16, + 0x3d, 0x14, 0x3d, 0x12, 0x3f, 0x11, 0x3f, 0x10, 0x3f, 0x0f, 0x3f, 0x0f, + 0x40, 0x0e, 0x41, 0x0e, 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, + 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0c, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0x97, 0x00, 0x6c, 0x02, 0x52, 0x07, 0x43, 0x0b, 0x38, 0x10, 0x30, + 0x14, 0x2a, 0x18, 0x27, 0x1b, 0x23, 0x1e, 0x20, 0x20, 0x1e, 0x23, 0x1c, + 0x25, 0x1b, 0x27, 0x19, 0x28, 0x18, 0x2a, 0x17, 0x2b, 0x16, 0x2c, 0x16, + 0x2e, 0x16, 0x2f, 0x15, 0x23, 0x50, 0x23, 0x3b, 0x25, 0x2e, 0x27, 0x26, + 0x29, 0x21, 0x2c, 0x1d, 0x2e, 0x1a, 0x2f, 0x18, 0x31, 0x16, 0x33, 0x15, + 0x34, 0x14, 0x35, 0x13, 0x36, 0x12, 0x37, 0x11, 0x38, 0x11, 0x38, 0x11, + 0x39, 0x10, 0x3a, 0x10, 0x3a, 0x10, 0x3b, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x29, 0x3c, 0x22, 0x3b, 0x1d, 0x3b, 0x19, 0x3c, 0x17, 0x3c, 0x15, + 0x3c, 0x13, 0x3d, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x10, 0x3e, 0x0f, + 0x3f, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, 0x42, 0x0e, 0x42, 0x0d, + 0x42, 0x0d, 0x42, 0x0d, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x9e, 0x00, 0x79, + 0x00, 0x61, 0x04, 0x50, 0x07, 0x44, 0x0b, 0x3b, 0x0f, 0x34, 0x12, 0x2f, + 0x15, 0x2b, 0x18, 0x28, 0x1a, 0x25, 0x1d, 0x23, 0x1f, 0x21, 0x21, 0x1f, + 0x22, 0x1d, 0x24, 0x1c, 0x26, 0x1b, 0x27, 0x19, 0x28, 0x19, 0x2a, 0x19, + 0x23, 0x54, 0x23, 0x42, 0x24, 0x35, 0x25, 0x2d, 0x27, 0x27, 0x29, 0x22, + 0x2b, 0x1f, 0x2d, 0x1c, 0x2e, 0x1a, 0x30, 0x19, 0x31, 0x17, 0x32, 0x16, + 0x33, 0x15, 0x34, 0x14, 0x35, 0x13, 0x36, 0x13, 0x37, 0x12, 0x37, 0x11, + 0x38, 0x11, 0x38, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x2c, 0x3c, 0x25, + 0x3b, 0x20, 0x3b, 0x1d, 0x3b, 0x1a, 0x3c, 0x17, 0x3c, 0x16, 0x3c, 0x14, + 0x3c, 0x13, 0x3e, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x11, 0x3d, 0x10, + 0x3e, 0x0f, 0x3f, 0x0f, 0x40, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xa2, 0x00, 0x84, 0x00, 0x6c, 0x02, 0x5a, + 0x05, 0x4e, 0x07, 0x45, 0x0b, 0x3e, 0x0e, 0x38, 0x10, 0x33, 0x13, 0x2f, + 0x16, 0x2c, 0x18, 0x29, 0x1a, 0x26, 0x1c, 0x24, 0x1d, 0x22, 0x1f, 0x21, + 0x21, 0x1f, 0x21, 0x1e, 0x23, 0x1d, 0x25, 0x1c, 0x23, 0x56, 0x23, 0x47, + 0x23, 0x3b, 0x24, 0x32, 0x26, 0x2c, 0x27, 0x27, 0x29, 0x24, 0x2a, 0x21, + 0x2c, 0x1e, 0x2d, 0x1c, 0x2e, 0x1b, 0x30, 0x19, 0x30, 0x18, 0x32, 0x17, + 0x32, 0x16, 0x33, 0x15, 0x34, 0x14, 0x34, 0x14, 0x35, 0x13, 0x36, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x2e, 0x3c, 0x27, 0x3b, 0x23, 0x3b, 0x1f, + 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x18, 0x3c, 0x17, 0x3b, 0x15, 0x3c, 0x14, + 0x3c, 0x13, 0x3e, 0x13, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, + 0x3d, 0x10, 0x3e, 0x0f, 0x3f, 0x0f, 0x40, 0x0f, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xa6, 0x00, 0x8b, 0x00, 0x75, 0x01, 0x63, 0x02, 0x57, 0x05, 0x4d, + 0x07, 0x45, 0x0a, 0x3e, 0x0d, 0x39, 0x0f, 0x35, 0x12, 0x32, 0x14, 0x2e, + 0x16, 0x2c, 0x18, 0x29, 0x19, 0x27, 0x1b, 0x26, 0x1d, 0x24, 0x1e, 0x22, + 0x20, 0x22, 0x21, 0x20, 0x23, 0x58, 0x23, 0x4a, 0x23, 0x3f, 0x24, 0x36, + 0x25, 0x30, 0x26, 0x2b, 0x27, 0x27, 0x29, 0x24, 0x2a, 0x22, 0x2b, 0x20, + 0x2d, 0x1e, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x18, 0x31, 0x18, + 0x32, 0x17, 0x32, 0x16, 0x33, 0x16, 0x34, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x30, 0x3c, 0x2a, 0x3c, 0x25, 0x3a, 0x21, 0x3b, 0x1e, 0x3a, 0x1c, + 0x3b, 0x1a, 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x16, 0x3b, 0x15, 0x3c, 0x14, + 0x3d, 0x13, 0x3e, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, + 0x3d, 0x10, 0x3e, 0x0f, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xa8, 0x00, 0x90, + 0x00, 0x7c, 0x00, 0x6b, 0x01, 0x5f, 0x04, 0x55, 0x05, 0x4c, 0x07, 0x45, + 0x0a, 0x40, 0x0c, 0x3c, 0x0f, 0x37, 0x10, 0x34, 0x12, 0x31, 0x15, 0x2e, + 0x16, 0x2b, 0x18, 0x2a, 0x19, 0x28, 0x1a, 0x26, 0x1d, 0x25, 0x1d, 0x23, + 0x23, 0x59, 0x23, 0x4d, 0x23, 0x43, 0x24, 0x3b, 0x24, 0x34, 0x25, 0x2f, + 0x26, 0x2b, 0x27, 0x28, 0x28, 0x25, 0x2a, 0x23, 0x2b, 0x21, 0x2c, 0x1f, + 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x19, 0x31, 0x18, + 0x32, 0x17, 0x32, 0x16, 0x17, 0x00, 0x2f, 0x00, 0x4c, 0x00, 0x56, 0x00, + 0x59, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, + 0x14, 0x2c, 0x29, 0x01, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5b, 0x00, + 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x62, 0x00, + 0x62, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, + 0x11, 0x31, 0x23, 0x03, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, + 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x3e, 0x32, 0x3d, 0x2c, + 0x3c, 0x26, 0x3b, 0x23, 0x3b, 0x20, 0x3b, 0x1e, 0x3a, 0x1c, 0x3b, 0x1a, + 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x14, + 0x3d, 0x13, 0x3e, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaa, 0x00, 0x94, 0x00, 0x82, 0x00, 0x72, + 0x01, 0x65, 0x02, 0x5b, 0x04, 0x53, 0x06, 0x4b, 0x08, 0x45, 0x0a, 0x41, + 0x0c, 0x3d, 0x0e, 0x39, 0x0f, 0x36, 0x12, 0x33, 0x12, 0x30, 0x16, 0x2e, + 0x16, 0x2b, 0x18, 0x2b, 0x19, 0x29, 0x1a, 0x26, 0x23, 0x5a, 0x23, 0x4f, + 0x23, 0x46, 0x23, 0x3e, 0x24, 0x37, 0x25, 0x32, 0x25, 0x2e, 0x26, 0x2b, + 0x27, 0x28, 0x28, 0x25, 0x2a, 0x23, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x1e, + 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x19, 0x30, 0x18, + 0x00, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x45, 0x00, 0x4f, 0x00, 0x55, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x57, 0x04, 0x16, + 0x27, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x53, 0x00, 0x56, 0x00, 0x59, 0x00, + 0x5a, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x3b, 0x00, 0x48, 0x00, 0x55, 0x00, 0x58, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x20, + 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, + 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x3e, 0x33, 0x3d, 0x2d, 0x3c, 0x28, 0x3b, 0x25, + 0x3a, 0x22, 0x3b, 0x1f, 0x3b, 0x1e, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, + 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x16, 0x3b, 0x15, 0x3c, 0x15, 0x3c, 0x13, + 0x3d, 0x13, 0x3f, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xaa, 0x00, 0x98, 0x00, 0x87, 0x00, 0x78, 0x00, 0x6b, 0x01, 0x61, + 0x03, 0x58, 0x05, 0x51, 0x07, 0x4b, 0x08, 0x46, 0x0a, 0x42, 0x0c, 0x3e, + 0x0d, 0x3a, 0x0f, 0x37, 0x11, 0x35, 0x12, 0x32, 0x14, 0x30, 0x16, 0x2e, + 0x16, 0x2b, 0x18, 0x2b, 0x23, 0x5a, 0x23, 0x51, 0x23, 0x48, 0x23, 0x41, + 0x24, 0x3a, 0x24, 0x35, 0x25, 0x31, 0x26, 0x2e, 0x27, 0x2a, 0x27, 0x28, + 0x28, 0x26, 0x2a, 0x24, 0x2a, 0x22, 0x2b, 0x21, 0x2c, 0x1f, 0x2d, 0x1e, + 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x22, 0x00, 0x36, 0x00, 0x42, 0x00, 0x4a, 0x00, 0x4f, 0x00, + 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, + 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x82, 0x00, 0x57, 0x01, 0x12, 0x17, 0x00, + 0x2f, 0x00, 0x3e, 0x00, 0x47, 0x00, 0x4d, 0x00, 0x51, 0x00, 0x53, 0x00, + 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x48, 0x00, + 0x1b, 0x00, 0x34, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4f, 0x00, + 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, + 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x0d, 0x00, + 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, + 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, + 0x3e, 0x34, 0x3d, 0x2f, 0x3b, 0x2a, 0x3c, 0x26, 0x3a, 0x23, 0x3b, 0x21, + 0x3b, 0x1f, 0x3b, 0x1d, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x17, + 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x15, 0x3c, 0x13, + 0x3e, 0x13, 0x3f, 0x13, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xac, 0x00, 0x9b, + 0x00, 0x8b, 0x00, 0x7c, 0x00, 0x71, 0x01, 0x67, 0x02, 0x5e, 0x04, 0x57, + 0x05, 0x51, 0x07, 0x4b, 0x08, 0x46, 0x0a, 0x42, 0x0c, 0x3f, 0x0c, 0x3b, + 0x0f, 0x39, 0x0f, 0x35, 0x12, 0x34, 0x12, 0x31, 0x14, 0x30, 0x16, 0x2e, + 0x23, 0x5b, 0x23, 0x52, 0x23, 0x4a, 0x23, 0x43, 0x23, 0x3d, 0x24, 0x38, + 0x25, 0x34, 0x25, 0x30, 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, + 0x2a, 0x24, 0x2a, 0x22, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x1f, 0x2d, 0x1d, + 0x2e, 0x1d, 0x2e, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x19, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, 0x00, + 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x00, 0x91, 0x00, 0x78, 0x00, 0x43, 0x00, 0x10, 0x0e, 0x00, 0x23, 0x00, + 0x31, 0x00, 0x3b, 0x00, 0x42, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4f, 0x00, + 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x55, 0x00, 0x34, 0x00, 0x0b, 0x00, + 0x1f, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, 0x00, + 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x00, 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x02, 0x00, 0x1a, 0x00, + 0x2a, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, + 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, 0x00, 0x3e, 0x35, 0x3e, 0x30, + 0x3b, 0x2c, 0x3c, 0x28, 0x3b, 0x25, 0x3a, 0x22, 0x3b, 0x20, 0x3b, 0x1f, + 0x3a, 0x1d, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, + 0x3c, 0x17, 0x3a, 0x16, 0x3b, 0x15, 0x3c, 0x15, 0x3c, 0x14, 0x3c, 0x13, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xad, 0x00, 0x9d, 0x00, 0x8e, 0x00, 0x81, + 0x00, 0x76, 0x00, 0x6b, 0x01, 0x63, 0x02, 0x5c, 0x04, 0x56, 0x05, 0x50, + 0x07, 0x4b, 0x08, 0x47, 0x0a, 0x43, 0x0c, 0x40, 0x0c, 0x3c, 0x0f, 0x3a, + 0x0f, 0x37, 0x11, 0x35, 0x12, 0x33, 0x12, 0x30, 0x23, 0x5b, 0x23, 0x53, + 0x23, 0x4c, 0x23, 0x45, 0x23, 0x40, 0x24, 0x3b, 0x24, 0x36, 0x25, 0x33, + 0x25, 0x30, 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, 0x2a, 0x25, + 0x2a, 0x23, 0x2b, 0x22, 0x2b, 0x20, 0x2c, 0x20, 0x2d, 0x1e, 0x2d, 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, + 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, + 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x96, 0x00, 0x87, + 0x00, 0x61, 0x00, 0x36, 0x00, 0x10, 0x0a, 0x02, 0x1a, 0x00, 0x27, 0x00, + 0x32, 0x00, 0x3a, 0x00, 0x40, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4b, 0x00, + 0x4e, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x58, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x14, 0x00, + 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, + 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x97, 0x00, 0x8a, + 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x1f, 0x00, + 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, + 0x4b, 0x00, 0x4e, 0x00, 0x3e, 0x36, 0x3e, 0x31, 0x3b, 0x2c, 0x3c, 0x29, + 0x3c, 0x26, 0x3a, 0x24, 0x3a, 0x21, 0x3b, 0x20, 0x3b, 0x1f, 0x39, 0x1d, + 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, + 0x3a, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x15, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xad, 0x00, 0x9f, 0x00, 0x91, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6f, + 0x01, 0x67, 0x02, 0x60, 0x03, 0x59, 0x04, 0x54, 0x05, 0x4f, 0x07, 0x4b, + 0x08, 0x47, 0x0a, 0x43, 0x0b, 0x41, 0x0c, 0x3d, 0x0e, 0x3b, 0x0f, 0x38, + 0x0f, 0x35, 0x12, 0x35, 0x23, 0x5b, 0x23, 0x54, 0x23, 0x4e, 0x23, 0x47, + 0x23, 0x42, 0x24, 0x3c, 0x24, 0x38, 0x25, 0x35, 0x25, 0x32, 0x26, 0x2f, + 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, 0x29, 0x25, 0x2a, 0x23, + 0x2b, 0x22, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, 0x00, + 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, + 0x49, 0x00, 0x4c, 0x00, 0x00, 0x99, 0x00, 0x8f, 0x00, 0x74, 0x00, 0x51, + 0x00, 0x2e, 0x00, 0x10, 0x08, 0x04, 0x13, 0x00, 0x20, 0x00, 0x2a, 0x00, + 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x48, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x57, 0x00, + 0x44, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, 0x00, + 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, + 0x49, 0x00, 0x4c, 0x00, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, + 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, + 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, + 0x3e, 0x36, 0x3e, 0x31, 0x3b, 0x2d, 0x3b, 0x2a, 0x3c, 0x27, 0x3a, 0x25, + 0x3a, 0x23, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1c, 0x3b, 0x1c, + 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x17, + 0x3a, 0x16, 0x3b, 0x15, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xad, 0x00, 0xa0, + 0x00, 0x94, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x74, 0x00, 0x6c, 0x01, 0x65, + 0x02, 0x5e, 0x04, 0x58, 0x05, 0x53, 0x05, 0x4f, 0x07, 0x4b, 0x08, 0x47, + 0x0a, 0x44, 0x0b, 0x41, 0x0c, 0x3e, 0x0d, 0x3b, 0x0f, 0x3a, 0x0f, 0x37, + 0x23, 0x5c, 0x23, 0x55, 0x23, 0x4f, 0x23, 0x49, 0x23, 0x44, 0x23, 0x3f, + 0x24, 0x3b, 0x24, 0x37, 0x25, 0x34, 0x25, 0x31, 0x26, 0x2e, 0x26, 0x2c, + 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x24, 0x2a, 0x22, + 0x2b, 0x22, 0x2b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, + 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, 0x00, + 0x00, 0x9b, 0x00, 0x94, 0x00, 0x7f, 0x00, 0x64, 0x00, 0x46, 0x00, 0x29, + 0x00, 0x10, 0x07, 0x06, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, + 0x32, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, + 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, + 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, 0x00, + 0x00, 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, + 0x00, 0x1f, 0x00, 0x0c, 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, + 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, 0x00, 0x3e, 0x37, 0x3e, 0x32, + 0x3c, 0x2f, 0x3b, 0x2b, 0x3c, 0x28, 0x3b, 0x26, 0x3a, 0x24, 0x3a, 0x22, + 0x3b, 0x21, 0x3b, 0x1f, 0x3a, 0x1e, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, + 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x17, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x97, 0x00, 0x8b, + 0x00, 0x80, 0x00, 0x77, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x61, 0x02, 0x5c, + 0x04, 0x57, 0x05, 0x52, 0x05, 0x4e, 0x07, 0x4b, 0x09, 0x47, 0x0a, 0x44, + 0x0b, 0x41, 0x0c, 0x3f, 0x0c, 0x3c, 0x0f, 0x3b, 0x23, 0x5c, 0x23, 0x56, + 0x23, 0x50, 0x23, 0x4a, 0x23, 0x45, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, + 0x25, 0x35, 0x25, 0x33, 0x25, 0x30, 0x26, 0x2e, 0x26, 0x2c, 0x27, 0x2a, + 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x24, 0x2a, 0x23, 0x2b, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, + 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x9c, 0x00, 0x96, + 0x00, 0x87, 0x00, 0x71, 0x00, 0x57, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x10, + 0x06, 0x07, 0x0b, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x26, 0x00, 0x2d, 0x00, + 0x32, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, + 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x9c, 0x00, 0x97, + 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, + 0x00, 0x0f, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, + 0x2c, 0x00, 0x31, 0x00, 0x3e, 0x37, 0x3e, 0x33, 0x3c, 0x2f, 0x3b, 0x2c, + 0x3c, 0x29, 0x3c, 0x27, 0x39, 0x25, 0x3a, 0x23, 0x3b, 0x21, 0x3b, 0x20, + 0x3b, 0x1f, 0x3a, 0x1e, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, + 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3b, 0x17, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, 0x84, 0x00, 0x7a, + 0x00, 0x72, 0x00, 0x6c, 0x01, 0x65, 0x02, 0x5f, 0x04, 0x5a, 0x04, 0x56, + 0x05, 0x52, 0x06, 0x4e, 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x41, + 0x0c, 0x40, 0x0c, 0x3d, 0x23, 0x5c, 0x23, 0x56, 0x23, 0x51, 0x23, 0x4c, + 0x23, 0x47, 0x23, 0x42, 0x23, 0x3e, 0x24, 0x3b, 0x24, 0x37, 0x25, 0x34, + 0x25, 0x32, 0x25, 0x30, 0x26, 0x2e, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, + 0x28, 0x27, 0x29, 0x25, 0x2a, 0x25, 0x2a, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, + 0x30, 0x00, 0x35, 0x00, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x7a, + 0x00, 0x64, 0x00, 0x4e, 0x00, 0x37, 0x00, 0x22, 0x00, 0x10, 0x05, 0x08, + 0x0a, 0x02, 0x12, 0x00, 0x1a, 0x00, 0x22, 0x00, 0x28, 0x00, 0x2d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, + 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, + 0x30, 0x00, 0x35, 0x00, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, + 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, + 0x00, 0x04, 0x07, 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, + 0x3e, 0x38, 0x3e, 0x33, 0x3d, 0x30, 0x3b, 0x2d, 0x3c, 0x2a, 0x3c, 0x27, + 0x3a, 0x26, 0x3a, 0x24, 0x3a, 0x22, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, + 0x39, 0x1e, 0x3a, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, + 0x3c, 0x18, 0x3c, 0x17, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaf, 0x00, 0xa5, + 0x00, 0x99, 0x00, 0x90, 0x00, 0x87, 0x00, 0x7e, 0x00, 0x76, 0x00, 0x6f, + 0x01, 0x68, 0x02, 0x62, 0x02, 0x5e, 0x04, 0x59, 0x04, 0x55, 0x05, 0x51, + 0x07, 0x4e, 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x41, 0x0c, 0x41, + 0x23, 0x5c, 0x23, 0x57, 0x23, 0x52, 0x23, 0x4d, 0x23, 0x48, 0x23, 0x44, + 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x24, 0x36, 0x25, 0x34, 0x25, 0x31, + 0x26, 0x2f, 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, + 0x28, 0x25, 0x2a, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, 0x00, + 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x90, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x5b, + 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, 0x00, 0x10, 0x04, 0x09, 0x09, 0x03, + 0x0f, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, + 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, 0x00, + 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, + 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, + 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x3e, 0x38, 0x3e, 0x34, + 0x3e, 0x31, 0x3b, 0x2e, 0x3b, 0x2b, 0x3c, 0x29, 0x3c, 0x27, 0x39, 0x25, + 0x3a, 0x24, 0x3a, 0x21, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1d, + 0x3a, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x19, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaf, 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x91, + 0x00, 0x88, 0x00, 0x80, 0x00, 0x78, 0x00, 0x71, 0x00, 0x6b, 0x01, 0x66, + 0x02, 0x61, 0x03, 0x5c, 0x04, 0x58, 0x05, 0x55, 0x05, 0x50, 0x07, 0x4e, + 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x42, 0x23, 0x5c, 0x23, 0x58, + 0x23, 0x53, 0x23, 0x4d, 0x23, 0x49, 0x23, 0x45, 0x23, 0x41, 0x24, 0x3d, + 0x24, 0x3b, 0x24, 0x38, 0x25, 0x35, 0x25, 0x33, 0x25, 0x31, 0x26, 0x2f, + 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x9d, 0x00, 0x9b, + 0x00, 0x93, 0x00, 0x86, 0x00, 0x76, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, + 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x10, 0x04, 0x09, 0x08, 0x04, 0x0c, 0x00, + 0x14, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, + 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x9e, 0x00, 0x9b, + 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, + 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, + 0x09, 0x00, 0x11, 0x00, 0x3e, 0x38, 0x3e, 0x35, 0x3e, 0x32, 0x3b, 0x2e, + 0x3b, 0x2c, 0x3c, 0x2a, 0x3c, 0x27, 0x3a, 0x26, 0x3a, 0x24, 0x3a, 0x23, + 0x3b, 0x21, 0x3b, 0x20, 0x3b, 0x1f, 0x3a, 0x1f, 0x39, 0x1d, 0x3a, 0x1c, + 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x83, + 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6e, 0x01, 0x69, 0x01, 0x63, 0x02, 0x5f, + 0x04, 0x5b, 0x04, 0x56, 0x05, 0x54, 0x05, 0x4f, 0x07, 0x4e, 0x07, 0x4a, + 0x09, 0x47, 0x0a, 0x46, 0x23, 0x5d, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4f, + 0x23, 0x4a, 0x23, 0x46, 0x23, 0x43, 0x23, 0x3f, 0x24, 0x3c, 0x24, 0x39, + 0x24, 0x36, 0x25, 0x34, 0x25, 0x32, 0x25, 0x30, 0x26, 0x2f, 0x26, 0x2d, + 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, + 0x16, 0x00, 0x1c, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x95, 0x00, 0x8a, + 0x00, 0x7d, 0x00, 0x6d, 0x00, 0x5d, 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x2c, + 0x00, 0x1d, 0x00, 0x10, 0x04, 0x0a, 0x07, 0x05, 0x0b, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, + 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, 0x00, + 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, + 0x16, 0x00, 0x1c, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, + 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, + 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, + 0x3e, 0x39, 0x3e, 0x35, 0x3e, 0x32, 0x3b, 0x2f, 0x3b, 0x2d, 0x3c, 0x2a, + 0x3c, 0x28, 0x3b, 0x27, 0x39, 0x24, 0x3a, 0x24, 0x3a, 0x22, 0x3b, 0x21, + 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1f, 0x39, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, + 0x3b, 0x1a, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xb0, 0x00, 0xa7, + 0x00, 0x9e, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x86, 0x00, 0x7e, 0x00, 0x77, + 0x00, 0x71, 0x00, 0x6b, 0x01, 0x66, 0x02, 0x62, 0x02, 0x5d, 0x04, 0x5a, + 0x04, 0x55, 0x05, 0x53, 0x05, 0x4f, 0x07, 0x4e, 0x07, 0x4a, 0x09, 0x47, + 0x23, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, 0x23, 0x4c, 0x23, 0x48, + 0x23, 0x44, 0x23, 0x40, 0x24, 0x3d, 0x24, 0x3a, 0x24, 0x38, 0x25, 0x36, + 0x25, 0x33, 0x25, 0x32, 0x25, 0x2f, 0x26, 0x2f, 0x26, 0x2c, 0x27, 0x2c, + 0x27, 0x2a, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x81, 0x00, 0x74, + 0x00, 0x65, 0x00, 0x56, 0x00, 0x46, 0x00, 0x37, 0x00, 0x29, 0x00, 0x1c, + 0x00, 0x10, 0x03, 0x0a, 0x07, 0x06, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, + 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, 0x00, + 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, + 0x00, 0x6b, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, + 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, 0x32, 0x59, 0x2a, 0x5b, + 0x28, 0x5c, 0x27, 0x5d, 0x27, 0x5d, 0x26, 0x5e, 0x26, 0x5e, 0x26, 0x5e, + 0x26, 0x5e, 0x26, 0x5e, 0x25, 0x5e, 0x25, 0x5e, 0x25, 0x5f, 0x25, 0x5f, + 0x25, 0x5f, 0x25, 0x5f, 0x25, 0x5f, 0x24, 0x5f, 0x24, 0x5f, 0x24, 0x60, + 0x32, 0x3c, 0x25, 0x4a, 0x24, 0x50, 0x23, 0x54, 0x23, 0x56, 0x23, 0x58, + 0x23, 0x59, 0x23, 0x5a, 0x23, 0x5a, 0x23, 0x5b, 0x23, 0x5b, 0x23, 0x5b, + 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5d, + 0x23, 0x5d, 0x23, 0x5d, 0x00, 0x6c, 0x0e, 0x60, 0x14, 0x60, 0x18, 0x60, + 0x1b, 0x60, 0x1d, 0x60, 0x1d, 0x60, 0x1e, 0x60, 0x1f, 0x60, 0x20, 0x60, + 0x20, 0x60, 0x20, 0x60, 0x21, 0x60, 0x21, 0x60, 0x21, 0x60, 0x21, 0x60, + 0x22, 0x60, 0x22, 0x60, 0x22, 0x60, 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x9e, 0x00, 0x9c, + 0x00, 0x97, 0x00, 0x90, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x5e, + 0x00, 0x50, 0x00, 0x42, 0x00, 0x34, 0x00, 0x27, 0x00, 0x1b, 0x00, 0x0f, + 0x03, 0x0b, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, + 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x9e, 0x00, 0x9d, + 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, + 0x00, 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, + 0x00, 0x16, 0x00, 0x0d, 0x38, 0x4f, 0x31, 0x53, 0x2e, 0x56, 0x2c, 0x57, + 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5a, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x38, 0x22, 0x2b, 0x31, + 0x27, 0x3b, 0x25, 0x42, 0x24, 0x47, 0x23, 0x4a, 0x23, 0x4d, 0x23, 0x4f, + 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, 0x23, 0x55, 0x23, 0x56, + 0x23, 0x56, 0x23, 0x57, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, + 0x00, 0x92, 0x01, 0x79, 0x07, 0x6f, 0x0c, 0x6b, 0x0f, 0x68, 0x12, 0x67, + 0x14, 0x66, 0x16, 0x65, 0x17, 0x64, 0x18, 0x64, 0x19, 0x63, 0x1a, 0x63, + 0x1b, 0x63, 0x1b, 0x63, 0x1c, 0x63, 0x1c, 0x62, 0x1d, 0x62, 0x1d, 0x62, + 0x1d, 0x62, 0x1e, 0x62, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, + 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x58, 0x00, 0x4b, + 0x00, 0x3e, 0x00, 0x31, 0x00, 0x25, 0x00, 0x1a, 0x00, 0x0f, 0x03, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, + 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, 0x00, + 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, + 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, + 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, + 0x3a, 0x4b, 0x34, 0x4f, 0x31, 0x52, 0x2f, 0x54, 0x2d, 0x56, 0x2c, 0x57, + 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5a, + 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x3c, 0x18, 0x30, 0x25, 0x2a, 0x2e, 0x27, 0x35, + 0x25, 0x3b, 0x24, 0x3f, 0x24, 0x43, 0x24, 0x46, 0x23, 0x48, 0x23, 0x4a, + 0x23, 0x4c, 0x23, 0x4e, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x51, 0x23, 0x52, + 0x23, 0x53, 0x23, 0x53, 0x23, 0x54, 0x23, 0x54, 0x00, 0xa1, 0x00, 0x88, + 0x03, 0x7b, 0x06, 0x74, 0x09, 0x70, 0x0c, 0x6d, 0x0e, 0x6b, 0x10, 0x6a, + 0x11, 0x69, 0x13, 0x68, 0x14, 0x67, 0x15, 0x66, 0x16, 0x66, 0x17, 0x65, + 0x17, 0x65, 0x18, 0x65, 0x19, 0x65, 0x19, 0x64, 0x1a, 0x64, 0x1a, 0x64, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, + 0x00, 0x77, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x46, 0x00, 0x3a, + 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, + 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, 0x00, + 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, + 0x00, 0x7b, 0x00, 0x71, 0x00, 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, + 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, 0x3b, 0x48, 0x36, 0x4c, + 0x33, 0x4f, 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, 0x2d, 0x55, 0x2c, 0x57, + 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, + 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5b, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, + 0x3f, 0x14, 0x33, 0x1e, 0x2d, 0x26, 0x29, 0x2d, 0x27, 0x32, 0x26, 0x36, + 0x25, 0x3b, 0x24, 0x3e, 0x24, 0x41, 0x24, 0x43, 0x23, 0x45, 0x23, 0x47, + 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4d, 0x23, 0x4d, 0x23, 0x4f, + 0x23, 0x50, 0x23, 0x50, 0x00, 0xa7, 0x00, 0x92, 0x01, 0x84, 0x03, 0x7d, + 0x05, 0x77, 0x08, 0x73, 0x0a, 0x71, 0x0c, 0x6f, 0x0d, 0x6d, 0x0f, 0x6b, + 0x10, 0x6a, 0x11, 0x6a, 0x12, 0x69, 0x13, 0x68, 0x14, 0x68, 0x15, 0x67, + 0x15, 0x66, 0x16, 0x66, 0x17, 0x66, 0x17, 0x66, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x31, 0x23, 0x03, + 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x5e, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x31, 0x00, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, + 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, + 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x47, 0x38, 0x4b, 0x35, 0x4e, 0x32, 0x4f, + 0x31, 0x51, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, 0x2c, 0x56, + 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x2a, 0x59, 0x40, 0x11, 0x36, 0x1a, + 0x2f, 0x21, 0x2c, 0x27, 0x29, 0x2c, 0x27, 0x30, 0x26, 0x34, 0x25, 0x37, + 0x25, 0x3a, 0x24, 0x3d, 0x24, 0x40, 0x24, 0x42, 0x24, 0x44, 0x23, 0x45, + 0x23, 0x47, 0x23, 0x48, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4c, + 0x00, 0xaa, 0x00, 0x98, 0x00, 0x8c, 0x02, 0x83, 0x03, 0x7d, 0x05, 0x79, + 0x07, 0x76, 0x09, 0x73, 0x0a, 0x71, 0x0c, 0x6f, 0x0d, 0x6e, 0x0e, 0x6d, + 0x0f, 0x6c, 0x10, 0x6b, 0x11, 0x6a, 0x12, 0x6a, 0x13, 0x69, 0x13, 0x68, + 0x14, 0x68, 0x15, 0x68, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x20, 0x1f, 0x00, 0x3c, 0x00, + 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, + 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x03, 0x00, 0x20, + 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, + 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, + 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x46, 0x39, 0x4a, 0x36, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x50, + 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2d, 0x56, + 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x41, 0x10, 0x38, 0x17, 0x32, 0x1d, 0x2e, 0x22, + 0x2b, 0x27, 0x29, 0x2b, 0x27, 0x2f, 0x26, 0x32, 0x25, 0x35, 0x25, 0x38, + 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3f, 0x24, 0x40, 0x24, 0x42, 0x23, 0x44, + 0x23, 0x45, 0x23, 0x46, 0x23, 0x48, 0x23, 0x48, 0x00, 0xac, 0x00, 0x9d, + 0x00, 0x91, 0x01, 0x89, 0x02, 0x82, 0x03, 0x7e, 0x05, 0x7a, 0x07, 0x77, + 0x08, 0x75, 0x09, 0x73, 0x0b, 0x71, 0x0c, 0x70, 0x0d, 0x6e, 0x0e, 0x6e, + 0x0e, 0x6c, 0x0f, 0x6c, 0x10, 0x6b, 0x10, 0x6a, 0x11, 0x6a, 0x12, 0x6a, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, + 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, + 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x4d, + 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, + 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x39, 0x48, + 0x37, 0x4a, 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2d, 0x56, + 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x42, 0x0f, 0x39, 0x15, 0x33, 0x1a, 0x2f, 0x1f, 0x2d, 0x24, 0x2a, 0x27, + 0x29, 0x2b, 0x27, 0x2e, 0x26, 0x31, 0x26, 0x34, 0x25, 0x36, 0x25, 0x38, + 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3e, 0x24, 0x40, 0x24, 0x41, 0x24, 0x43, + 0x23, 0x44, 0x23, 0x45, 0x00, 0xae, 0x00, 0xa1, 0x00, 0x96, 0x00, 0x8d, + 0x01, 0x87, 0x02, 0x82, 0x03, 0x7e, 0x05, 0x7b, 0x06, 0x78, 0x07, 0x76, + 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x71, 0x0c, 0x70, 0x0c, 0x6f, 0x0d, 0x6e, + 0x0e, 0x6d, 0x0f, 0x6c, 0x10, 0x6c, 0x10, 0x6b, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x7c, + 0x00, 0x4d, 0x00, 0x1f, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, 0x00, + 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, + 0x53, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x00, 0x00, 0x1f, 0x00, 0x42, 0x00, 0x5a, + 0x00, 0x6a, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, + 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4a, + 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x52, 0x2f, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x55, + 0x2c, 0x57, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x43, 0x0e, 0x3b, 0x13, + 0x35, 0x18, 0x31, 0x1c, 0x2e, 0x21, 0x2c, 0x24, 0x2a, 0x28, 0x28, 0x2b, + 0x27, 0x2e, 0x27, 0x30, 0x26, 0x33, 0x25, 0x35, 0x25, 0x37, 0x25, 0x39, + 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x24, 0x40, 0x24, 0x42, + 0x00, 0xaf, 0x00, 0xa3, 0x00, 0x99, 0x00, 0x91, 0x00, 0x8b, 0x02, 0x86, + 0x02, 0x81, 0x03, 0x7e, 0x05, 0x7b, 0x06, 0x79, 0x07, 0x77, 0x08, 0x75, + 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x0c, 0x6f, 0x0d, 0x6e, + 0x0e, 0x6e, 0x0e, 0x6d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, + 0x00, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, 0x00, + 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x4a, 0x00, + 0x28, 0x00, 0x02, 0x00, 0x00, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, + 0x00, 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, + 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4b, 0x34, 0x4e, + 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, + 0x2c, 0x56, 0x2b, 0x57, 0x44, 0x0e, 0x3c, 0x12, 0x36, 0x16, 0x33, 0x1a, + 0x30, 0x1e, 0x2d, 0x22, 0x2b, 0x25, 0x2a, 0x28, 0x28, 0x2a, 0x27, 0x2d, + 0x27, 0x30, 0x26, 0x32, 0x26, 0x34, 0x25, 0x35, 0x25, 0x37, 0x25, 0x39, + 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x00, 0xb0, 0x00, 0xa5, + 0x00, 0x9d, 0x00, 0x95, 0x00, 0x8e, 0x01, 0x89, 0x02, 0x85, 0x03, 0x81, + 0x04, 0x7e, 0x05, 0x7c, 0x06, 0x7a, 0x07, 0x78, 0x07, 0x76, 0x09, 0x75, + 0x09, 0x73, 0x0b, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x0c, 0x70, 0x0d, 0x6e, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, + 0x00, 0x09, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, + 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x00, + 0x00, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, + 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3b, 0x46, + 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x34, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, + 0x44, 0x0d, 0x3d, 0x11, 0x38, 0x15, 0x34, 0x19, 0x31, 0x1c, 0x2e, 0x20, + 0x2d, 0x23, 0x2b, 0x25, 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2d, 0x27, 0x2f, + 0x26, 0x31, 0x26, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x38, 0x25, 0x39, + 0x24, 0x3a, 0x24, 0x3c, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x98, + 0x00, 0x91, 0x00, 0x8c, 0x01, 0x88, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7e, + 0x05, 0x7c, 0x06, 0x7a, 0x06, 0x79, 0x07, 0x77, 0x08, 0x76, 0x09, 0x74, + 0x0a, 0x73, 0x0b, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x95, + 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, + 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x09, + 0x00, 0x1f, 0x00, 0x33, 0x00, 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, + 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x44, 0x0d, 0x3e, 0x10, + 0x39, 0x14, 0x35, 0x17, 0x32, 0x1b, 0x30, 0x1e, 0x2e, 0x21, 0x2c, 0x23, + 0x2b, 0x26, 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2d, 0x27, 0x2e, 0x26, 0x30, + 0x26, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x36, 0x25, 0x38, 0x25, 0x39, + 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x8f, + 0x01, 0x8b, 0x02, 0x87, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, + 0x06, 0x7b, 0x06, 0x79, 0x07, 0x78, 0x07, 0x76, 0x09, 0x76, 0x09, 0x74, + 0x0a, 0x73, 0x0b, 0x72, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, + 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, + 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, 0x00, 0x00, 0x0c, 0x00, 0x1f, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, + 0x00, 0x6c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, + 0x35, 0x4b, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x44, 0x0c, 0x3f, 0x10, 0x3a, 0x13, 0x36, 0x16, + 0x33, 0x19, 0x30, 0x1c, 0x2e, 0x1f, 0x2d, 0x21, 0x2b, 0x24, 0x2a, 0x26, + 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2e, 0x26, 0x30, 0x26, 0x31, + 0x26, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x37, 0x00, 0xb2, 0x00, 0xaa, + 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8d, 0x01, 0x89, + 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x06, 0x7b, + 0x06, 0x79, 0x07, 0x79, 0x07, 0x77, 0x08, 0x76, 0x09, 0x75, 0x09, 0x74, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, + 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, 0x00, 0x04, 0x07, 0x00, + 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, + 0x2b, 0x00, 0x17, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2e, + 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x46, + 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, + 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x45, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x37, 0x15, 0x34, 0x18, 0x32, 0x1b, + 0x30, 0x1d, 0x2e, 0x20, 0x2d, 0x22, 0x2b, 0x24, 0x2a, 0x26, 0x2a, 0x28, + 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2e, 0x27, 0x2f, 0x26, 0x31, 0x26, 0x32, + 0x25, 0x33, 0x25, 0x35, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9e, + 0x00, 0x98, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, 0x01, 0x88, 0x02, 0x85, + 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7a, + 0x07, 0x79, 0x07, 0x78, 0x07, 0x76, 0x09, 0x76, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, + 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, + 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x03, 0x00, 0x0c, 0x00, + 0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, + 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x45, 0x3a, 0x46, 0x39, 0x46, + 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x33, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x51, + 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x40, 0x0f, + 0x3b, 0x11, 0x38, 0x14, 0x35, 0x17, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, + 0x2d, 0x21, 0x2c, 0x22, 0x2b, 0x25, 0x2a, 0x26, 0x29, 0x28, 0x28, 0x2a, + 0x28, 0x2c, 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x30, 0x26, 0x32, 0x25, 0x33, + 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, + 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8a, 0x02, 0x88, 0x02, 0x85, 0x02, 0x83, + 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7a, 0x06, 0x79, + 0x07, 0x78, 0x07, 0x77, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, + 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, + 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, + 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x00, + 0x00, 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, + 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x44, 0x3a, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x38, 0x13, + 0x36, 0x16, 0x33, 0x18, 0x31, 0x1b, 0x30, 0x1d, 0x2e, 0x1f, 0x2d, 0x21, + 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, + 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x2f, 0x26, 0x31, 0x00, 0xb3, 0x00, 0xac, + 0x00, 0xa7, 0x00, 0xa1, 0x00, 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, + 0x01, 0x8c, 0x01, 0x89, 0x02, 0x87, 0x02, 0x85, 0x02, 0x82, 0x03, 0x81, + 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7b, 0x06, 0x79, 0x07, 0x79, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, + 0x00, 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, + 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, + 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, 0x00, 0x07, 0x00, 0x00, 0x06, + 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x44, + 0x3b, 0x46, 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, 0x2f, 0x53, + 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x39, 0x13, 0x37, 0x15, 0x34, 0x18, + 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, 0x2d, 0x20, 0x2d, 0x22, 0x2b, 0x23, + 0x2b, 0x25, 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2d, + 0x27, 0x2f, 0x26, 0x2f, 0x00, 0xb3, 0x00, 0xad, 0x00, 0xa7, 0x00, 0xa3, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x95, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8b, + 0x02, 0x88, 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, 0x03, 0x81, 0x04, 0x7f, + 0x05, 0x7e, 0x05, 0x7c, 0x06, 0x7b, 0x06, 0x7a, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, + 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, + 0x00, 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, + 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, + 0x2c, 0x00, 0x1e, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x14, + 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, + 0x39, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x52, 0x45, 0x0b, 0x41, 0x0e, + 0x3d, 0x10, 0x3a, 0x12, 0x37, 0x14, 0x34, 0x17, 0x32, 0x19, 0x31, 0x1b, + 0x30, 0x1d, 0x2e, 0x1f, 0x2d, 0x20, 0x2c, 0x22, 0x2b, 0x24, 0x2a, 0x25, + 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2c, 0x27, 0x2e, + 0x00, 0xb3, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, + 0x00, 0x96, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8d, 0x01, 0x8a, 0x02, 0x88, + 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7e, + 0x05, 0x7c, 0x06, 0x7c, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, + 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, + 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, + 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, + 0x00, 0x29, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x47, + 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, + 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x45, 0x0b, 0x41, 0x0e, 0x3e, 0x10, 0x3a, 0x11, + 0x38, 0x14, 0x35, 0x16, 0x33, 0x18, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, + 0x2e, 0x20, 0x2d, 0x21, 0x2b, 0x22, 0x2b, 0x24, 0x2a, 0x25, 0x2a, 0x27, + 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2c, 0x00, 0xb3, 0x00, 0xae, + 0x00, 0xa9, 0x00, 0xa4, 0x00, 0xa0, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x94, + 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x01, 0x89, 0x02, 0x87, 0x02, 0x86, + 0x02, 0x84, 0x03, 0x82, 0x03, 0x80, 0x04, 0x7f, 0x05, 0x7e, 0x05, 0x7c, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, + 0x00, 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, + 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, + 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, + 0x09, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x42, + 0x3d, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x4a, 0x36, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x46, 0x0b, 0x42, 0x0d, 0x3e, 0x10, 0x3b, 0x11, 0x38, 0x13, 0x36, 0x16, + 0x34, 0x17, 0x32, 0x19, 0x30, 0x1b, 0x30, 0x1d, 0x2e, 0x1e, 0x2d, 0x20, + 0x2d, 0x22, 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x25, 0x2a, 0x28, 0x28, 0x28, + 0x28, 0x2a, 0x28, 0x2c, 0x00, 0xb3, 0x00, 0xae, 0x00, 0xaa, 0x00, 0xa5, + 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x96, 0x00, 0x93, 0x00, 0x90, + 0x00, 0x8d, 0x01, 0x8b, 0x02, 0x89, 0x02, 0x87, 0x02, 0x85, 0x02, 0x83, + 0x03, 0x82, 0x03, 0x80, 0x04, 0x7f, 0x05, 0x7e, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, + 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, + 0x00, 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, + 0x00, 0x28, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, + 0x3c, 0x00, 0x31, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x00, + 0x00, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x45, 0x3a, 0x46, + 0x39, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x34, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x46, 0x0b, 0x42, 0x0d, + 0x3e, 0x0f, 0x3c, 0x11, 0x39, 0x13, 0x37, 0x15, 0x34, 0x16, 0x33, 0x18, + 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2e, 0x20, 0x2d, 0x20, 0x2c, 0x22, + 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x26, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, + 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9e, + 0x00, 0x9a, 0x00, 0x97, 0x00, 0x94, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, + 0x01, 0x8a, 0x02, 0x88, 0x02, 0x86, 0x02, 0x85, 0x02, 0x83, 0x03, 0x82, + 0x03, 0x80, 0x04, 0x7f, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb2, 0x00, 0x6f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0xa2, 0x00, + 0x4f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x7f, 0x00, 0x3c, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x16, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd6, 0x00, 0xc6, 0x00, 0x9c, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, + 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, 0x00, + 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, + 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, + 0x00, 0x1b, 0x00, 0x17, 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, + 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, + 0x00, 0x15, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, + 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, + 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, + 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, + 0x00, 0x09, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, + 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, + 0x00, 0x19, 0x00, 0x16, 0x00, 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, + 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, + 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, + 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, + 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, + 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, + 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, + 0x00, 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, + 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, + 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, + 0x00, 0x0c, 0x00, 0x09, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, + 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, + 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1c, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x24, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x04, 0x11, 0x09, 0x01, 0x11, 0x00, 0x17, + 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x61, 0x01, 0x23, 0x03, 0x03, 0x09, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1a, + 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x1c, + 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x21, + 0x00, 0x27, 0x00, 0x2b, 0x00, 0x28, 0x00, 0x21, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x39, 0x01, 0x0f, 0x07, 0x02, 0x0c, 0x00, 0x12, 0x00, 0x17, + 0x00, 0x19, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x9f, 0x00, 0x5f, 0x00, + 0x1f, 0x00, 0x04, 0x01, 0x00, 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, + 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, + 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, + 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x34, 0x00, 0x27, 0x00, 0x0f, 0x00, 0x1a, + 0x00, 0x1c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, + 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x87, 0x00, + 0x32, 0x00, 0x19, 0x05, 0x0b, 0x09, 0x03, 0x0b, 0x00, 0x0e, 0x00, 0x12, + 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, + 0x00, 0x1c, 0x00, 0x1c, 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, + 0x16, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x12, + 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x0e, 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, + 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, + 0x00, 0x32, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0e, + 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, + 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xcd, 0x00, 0xaf, 0x00, 0x6e, 0x00, 0x31, 0x00, + 0x1e, 0x04, 0x12, 0x07, 0x0a, 0x09, 0x04, 0x0a, 0x00, 0x0b, 0x00, 0x0f, + 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, + 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, + 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, + 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x0c, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, + 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x2c, 0x00, 0x28, + 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, + 0x00, 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, + 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd4, 0x00, 0xc1, 0x00, 0x93, 0x00, 0x5f, 0x00, 0x30, 0x00, 0x22, 0x03, + 0x17, 0x06, 0x0f, 0x07, 0x0a, 0x09, 0x05, 0x0a, 0x02, 0x0b, 0x00, 0x0c, + 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x15, 0xd7, 0x00, 0xc9, 0x00, + 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, 0x00, + 0x14, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x09, + 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, + 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, + 0x00, 0x18, 0x00, 0x19, 0x00, 0x24, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0e, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, + 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0xcb, 0x00, + 0xaa, 0x00, 0x7f, 0x00, 0x55, 0x00, 0x30, 0x00, 0x24, 0x02, 0x1b, 0x05, + 0x14, 0x06, 0x0e, 0x08, 0x09, 0x09, 0x06, 0x0a, 0x03, 0x0b, 0x00, 0x0b, + 0x00, 0x0d, 0x00, 0x0f, 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, + 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, 0x00, 0x28, 0x00, 0x1c, 0x00, + 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, + 0x00, 0x0e, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, + 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xb8, 0x00, 0x96, 0x00, + 0x71, 0x00, 0x4e, 0x00, 0x30, 0x00, 0x26, 0x02, 0x1e, 0x04, 0x17, 0x06, + 0x12, 0x07, 0x0d, 0x08, 0x09, 0x09, 0x06, 0x0a, 0x04, 0x0a, 0x01, 0x0b, + 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, + 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, + 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, + 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x1e, + 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, + 0x00, 0x13, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0xa6, 0x00, 0x87, 0x00, 0x67, 0x00, + 0x4a, 0x00, 0x30, 0x00, 0x27, 0x02, 0x20, 0x03, 0x1a, 0x05, 0x14, 0x06, + 0x10, 0x07, 0x0c, 0x08, 0x09, 0x09, 0x07, 0x0a, 0xdc, 0x00, 0xd7, 0x00, + 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, 0x00, + 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, + 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, + 0x00, 0x10, 0x00, 0x11, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, + 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, + 0xc8, 0x00, 0xb2, 0x00, 0x97, 0x00, 0x7b, 0x00, 0x60, 0x00, 0x46, 0x00, + 0x30, 0x00, 0x28, 0x01, 0x21, 0x03, 0x1c, 0x04, 0x17, 0x06, 0x13, 0x07, + 0x0f, 0x08, 0x0c, 0x08, 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, + 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x50, 0x00, + 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, + 0x00, 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, + 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xba, 0x00, + 0xa4, 0x00, 0x8b, 0x00, 0x72, 0x00, 0x5a, 0x00, 0x44, 0x00, 0x2f, 0x00, + 0x29, 0x01, 0x23, 0x03, 0x1e, 0x04, 0x19, 0x05, 0x15, 0x06, 0x11, 0x07, + 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, + 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, + 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x05, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x1e, + 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, + 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, + 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xd9, 0x00, 0xd0, 0x00, 0xc0, 0x00, 0xad, 0x00, 0x98, 0x00, + 0x81, 0x00, 0x6b, 0x00, 0x56, 0x00, 0x42, 0x00, 0x2f, 0x00, 0x29, 0x01, + 0x24, 0x02, 0x1f, 0x04, 0x1b, 0x05, 0x17, 0x06, 0xdd, 0x00, 0xdb, 0x00, + 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, 0x00, + 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, + 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, + 0x00, 0x07, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, + 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, + 0xd2, 0x00, 0xc5, 0x00, 0xb5, 0x00, 0xa2, 0x00, 0x8e, 0x00, 0x79, 0x00, + 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x2a, 0x01, 0x25, 0x02, + 0x20, 0x03, 0x1c, 0x04, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, + 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, 0x00, 0x86, 0x00, 0x78, 0x00, + 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, + 0x00, 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, + 0xbb, 0x00, 0xaa, 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x60, 0x00, + 0x4f, 0x00, 0x3e, 0x00, 0x2f, 0x00, 0x2a, 0x01, 0x26, 0x02, 0x21, 0x03, + 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, + 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, + 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, + 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb1, 0x00, + 0xa0, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, 0x00, + 0x3d, 0x00, 0x2f, 0x00, 0x2b, 0x01, 0x26, 0x02, 0xde, 0x00, 0xdc, 0x00, + 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, 0x00, + 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, + 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, + 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, + 0xd7, 0x00, 0xce, 0x00, 0xc3, 0x00, 0xb6, 0x00, 0xa8, 0x00, 0x98, 0x00, + 0x88, 0x00, 0x78, 0x00, 0x68, 0x00, 0x59, 0x00, 0x4a, 0x00, 0x3c, 0x00, + 0x2f, 0x00, 0x2b, 0x01, 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, + 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, 0x00, 0xa0, 0x00, 0x94, 0x00, + 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, + 0x00, 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, + 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd0, 0x00, + 0xc7, 0x00, 0xbb, 0x00, 0xae, 0x00, 0xa0, 0x00, 0x91, 0x00, 0x82, 0x00, + 0x73, 0x00, 0x64, 0x00, 0x56, 0x00, 0x48, 0x00, 0x3b, 0x00, 0x2f, 0x00, + 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, + 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, + 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x6f, 0x00, + 0xb2, 0x00, 0xc8, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xde, 0x00, 0x4c, 0x04, 0x87, 0x00, 0xbc, 0x00, 0xcd, 0x00, + 0xd4, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x76, 0x00, + 0x9b, 0x00, 0xac, 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xde, 0x00, 0x61, 0x01, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, + 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x6f, 0x00, 0xa2, 0x00, + 0xb9, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, + 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, + 0x11, 0x09, 0x39, 0x01, 0x87, 0x00, 0xaf, 0x00, 0xc1, 0x00, 0xcb, 0x00, + 0xd1, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x5b, 0x00, 0x75, 0x00, 0x96, 0x00, + 0xab, 0x00, 0xc0, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, + 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, + 0x23, 0x03, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, + 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, + 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x9c, 0x00, + 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, + 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x01, 0x11, 0x0f, 0x07, + 0x32, 0x00, 0x6e, 0x00, 0x93, 0x00, 0xaa, 0x00, 0xb8, 0x00, 0xc1, 0x00, + 0xc8, 0x00, 0xcc, 0x00, 0xd0, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd6, 0x00, + 0xd7, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x75, 0x00, 0x2b, 0x00, 0x5b, 0x00, 0x7f, 0x00, 0x9b, 0x00, + 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, + 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x03, 0x09, 0x1f, 0x00, + 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, 0x00, + 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, + 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, + 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, + 0xca, 0x00, 0xcd, 0x00, 0x00, 0x17, 0x02, 0x0c, 0x19, 0x05, 0x31, 0x00, + 0x5f, 0x00, 0x7f, 0x00, 0x96, 0x00, 0xa6, 0x00, 0xb2, 0x00, 0xba, 0x00, + 0xc0, 0x00, 0xc5, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x96, 0x00, + 0x5b, 0x00, 0x13, 0x00, 0x41, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, + 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, + 0xca, 0x00, 0xcd, 0x00, 0x00, 0x13, 0x04, 0x01, 0x32, 0x00, 0x5f, 0x00, + 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, 0x00, + 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, + 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, + 0x00, 0x1b, 0x00, 0x12, 0x0b, 0x09, 0x1e, 0x04, 0x30, 0x00, 0x55, 0x00, + 0x71, 0x00, 0x87, 0x00, 0x97, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb5, 0x00, + 0xbb, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x7f, 0x00, 0x41, 0x00, + 0x03, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, + 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, + 0x00, 0x18, 0x00, 0x0a, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, + 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, + 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, + 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x1c, 0x00, 0x17, + 0x03, 0x0b, 0x12, 0x07, 0x22, 0x03, 0x30, 0x00, 0x4e, 0x00, 0x67, 0x00, + 0x7b, 0x00, 0x8b, 0x00, 0x98, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, + 0xb6, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcd, 0x00, 0xc0, 0x00, 0x9b, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x02, 0x00, + 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, + 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x1a, 0x00, 0x11, + 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, 0x00, + 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, + 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, + 0x99, 0x00, 0xa0, 0x00, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x0e, 0x0a, 0x09, + 0x17, 0x06, 0x24, 0x02, 0x30, 0x00, 0x4a, 0x00, 0x60, 0x00, 0x72, 0x00, + 0x81, 0x00, 0x8e, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, 0x00, + 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x01, 0x00, 0x21, 0x00, + 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, + 0x99, 0x00, 0xa0, 0x00, 0x00, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x14, 0x00, + 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, 0x00, + 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x36, 0x00, + 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, + 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x04, 0x0a, 0x0f, 0x07, 0x1b, 0x05, + 0x26, 0x02, 0x30, 0x00, 0x46, 0x00, 0x5a, 0x00, 0x6b, 0x00, 0x79, 0x00, + 0x85, 0x00, 0x8f, 0x00, 0x98, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, + 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x36, 0x00, + 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, + 0x00, 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, + 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, + 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, + 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x15, 0x00, 0x0b, 0x0a, 0x09, 0x14, 0x06, 0x1e, 0x04, 0x27, 0x02, + 0x30, 0x00, 0x44, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x7e, 0x00, + 0x88, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, + 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, + 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x1d, 0x00, 0x1a, + 0x00, 0x0f, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, 0x00, + 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, + 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, + 0x5c, 0x00, 0x68, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0f, + 0x05, 0x0a, 0x0e, 0x08, 0x17, 0x06, 0x20, 0x03, 0x28, 0x01, 0x2f, 0x00, + 0x42, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6d, 0x00, 0x78, 0x00, 0x82, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, + 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, + 0x5c, 0x00, 0x68, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, + 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, 0x00, + 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x02, 0x0b, 0x09, 0x09, + 0x12, 0x07, 0x1a, 0x05, 0x21, 0x03, 0x29, 0x01, 0x2f, 0x00, 0x40, 0x00, + 0x4f, 0x00, 0x5c, 0x00, 0x68, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, + 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, + 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x04, 0x00, 0x13, 0x00, + 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, + 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x1f, 0x00, 0x1d, + 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x06, 0x0a, 0x0d, 0x08, 0x14, 0x06, + 0x1c, 0x04, 0x23, 0x03, 0x29, 0x01, 0x2f, 0x00, 0x3e, 0x00, 0x4c, 0x00, + 0x59, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, + 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x0c, 0x00, 0x1b, 0x00, 0x29, 0x00, + 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, + 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x16, + 0x00, 0x0f, 0x03, 0x0b, 0x09, 0x09, 0x10, 0x07, 0x17, 0x06, 0x1e, 0x04, + 0x24, 0x02, 0x2a, 0x01, 0x2f, 0x00, 0x3d, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, + 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, + 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x11, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, + 0x00, 0x05, 0x06, 0x00, 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, 0x00, + 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x11, 0x00, 0x0b, + 0x06, 0x0a, 0x0c, 0x08, 0x13, 0x07, 0x19, 0x05, 0x1f, 0x04, 0x25, 0x02, + 0x2a, 0x01, 0x2f, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, + 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, + 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x01, 0x00, + 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, + 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0x00, 0x1e, + 0x00, 0x1c, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x04, 0x0a, 0x09, 0x09, + 0x0f, 0x08, 0x15, 0x06, 0x1b, 0x05, 0x20, 0x03, 0x26, 0x02, 0x2b, 0x01, + 0x2f, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, + 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0x00, 0x1d, + 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x08, 0x00, 0x13, 0x00, + 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, + 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, + 0x00, 0x15, 0x00, 0x0f, 0x01, 0x0b, 0x07, 0x0a, 0x0c, 0x08, 0x11, 0x07, + 0x17, 0x06, 0x1c, 0x04, 0x21, 0x03, 0x26, 0x02, 0x2b, 0x01, 0x2f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, + 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, + 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, + 0x00, 0x0e, 0x00, 0x05, 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, 0x00, + 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x01, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, + 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x01, 0x23, 0x03, 0x03, 0x09, 0x00, 0x13, + 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x03, 0x5f, 0x00, + 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, 0x00, + 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x04, 0x01, 0x00, 0x0a, 0x00, 0x11, + 0x00, 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, + 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x09, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, + 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, 0x00, 0xce, 0x00, 0xd1, 0x00, + 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x9f, 0x00, + 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0a, + 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, + 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x04, 0x01, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, + 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, + 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, + 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x0a, + 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, 0x00, + 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, + 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, + 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x01, + 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x11, 0x06, 0x00, 0x25, 0x00, + 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, 0x00, 0x96, 0x00, 0xa2, 0x00, + 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd0, 0x00, + 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, 0x00, + 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, + 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, + 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, + 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, 0x00, + 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x18, + 0x00, 0x0a, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, 0x00, + 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, + 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, + 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, + 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, + 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, 0x00, 0x5f, 0x00, 0x6e, 0x00, + 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xd9, 0x00, + 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, 0x00, + 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, + 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x0b, 0x00, 0x1c, 0x00, + 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, + 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, + 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, 0x00, + 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x14, 0x00, 0x09, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, + 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, + 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, + 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, + 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, + 0x00, 0x01, 0x0c, 0x00, 0x1b, 0x00, 0x29, 0x00, 0x38, 0x00, 0x46, 0x00, + 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, + 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, 0x00, + 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, + 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, 0x05, 0x06, 0x00, + 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, + 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, + 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, 0x00, + 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, + 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x01, 0x00, 0x0d, 0x00, 0x19, 0x00, + 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, + 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, + 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, + 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, + 0x00, 0x0c, 0x00, 0x02, 0x08, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, + 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, + 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, 0x00, + 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, + 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, + 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, + 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, + 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, 0x00, + 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb2, 0x00, 0x6f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0xa2, + 0x00, 0x4f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x7f, 0x00, 0x3c, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1a, 0x00, + 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd6, 0x00, 0xc6, 0x00, 0x9c, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0e, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, + 0x00, 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, + 0x00, 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, + 0x00, 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, + 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, + 0x00, 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, + 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, + 0x00, 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, 0x00, + 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, + 0x00, 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, + 0x00, 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, + 0x00, 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, + 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, + 0x00, 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, + 0x00, 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, 0x00, + 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, + 0x00, 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, + 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, + 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, + 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, + 0x00, 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, + 0x00, 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, + 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x6f, + 0x00, 0xb2, 0x00, 0xc8, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xd9, 0x00, 0xda, + 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xde, 0x00, 0xa6, 0x00, 0x76, 0x00, 0x9b, 0x00, 0xac, + 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x4c, 0x00, 0x87, + 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd4, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdb, + 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, + 0x00, 0xde, 0x00, 0xde, 0x01, 0x61, 0x00, 0x9f, 0x00, 0xc5, 0x00, 0xd2, + 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x6f, 0x00, 0xa2, + 0x00, 0xb9, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, + 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0x76, 0x00, 0x5b, 0x00, 0x75, 0x00, 0x96, 0x00, 0xab, 0x00, 0xc0, + 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, + 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x11, 0x01, 0x39, 0x00, 0x87, 0x00, 0xaf, + 0x00, 0xc1, 0x00, 0xcb, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, + 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, + 0x03, 0x23, 0x00, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, + 0x00, 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x9c, + 0x00, 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, + 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x9b, 0x00, 0x75, + 0x00, 0x2b, 0x00, 0x5b, 0x00, 0x7f, 0x00, 0x9b, 0x00, 0xae, 0x00, 0xb9, + 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, + 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x01, 0x07, 0x0f, 0x00, 0x32, 0x00, 0x6e, 0x00, 0x93, 0x00, 0xaa, + 0x00, 0xb8, 0x00, 0xc1, 0x00, 0xc8, 0x00, 0xcc, 0x00, 0xd0, 0x00, 0xd2, + 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x09, 0x03, 0x00, 0x1f, + 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, + 0x00, 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, + 0x00, 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, + 0x00, 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, + 0x00, 0xca, 0x00, 0xcd, 0x00, 0xac, 0x00, 0x96, 0x00, 0x5b, 0x00, 0x13, + 0x00, 0x41, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, 0xa5, 0x00, 0xb0, + 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0xca, 0x00, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x0c, 0x02, + 0x05, 0x19, 0x00, 0x31, 0x00, 0x5f, 0x00, 0x7f, 0x00, 0x96, 0x00, 0xa6, + 0x00, 0xb2, 0x00, 0xba, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc9, 0x00, 0xcc, + 0x00, 0xce, 0x00, 0xd0, 0x13, 0x00, 0x01, 0x04, 0x00, 0x32, 0x00, 0x5f, + 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, + 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, + 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, + 0x00, 0xbc, 0x00, 0xab, 0x00, 0x7f, 0x00, 0x41, 0x00, 0x03, 0x00, 0x2f, + 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, 0xa0, 0x00, 0xa9, + 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x09, 0x0b, 0x04, 0x1e, + 0x00, 0x30, 0x00, 0x55, 0x00, 0x71, 0x00, 0x87, 0x00, 0x97, 0x00, 0xa4, + 0x00, 0xad, 0x00, 0xb5, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc7, + 0x18, 0x00, 0x0a, 0x00, 0x00, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, + 0x00, 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, + 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, + 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xcd, 0x00, 0xc0, + 0x00, 0x9b, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x27, 0x00, 0x47, + 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, 0x9c, 0x00, 0xa4, + 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x03, 0x07, 0x12, 0x03, 0x22, 0x00, 0x30, + 0x00, 0x4e, 0x00, 0x67, 0x00, 0x7b, 0x00, 0x8b, 0x00, 0x98, 0x00, 0xa2, + 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, 0x00, 0xbb, 0x1a, 0x00, 0x11, 0x00, + 0x00, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, + 0x00, 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, + 0x00, 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, + 0x00, 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, + 0x00, 0x99, 0x00, 0xa0, 0x00, 0xd9, 0x00, 0xcd, 0x00, 0xae, 0x00, 0x82, + 0x00, 0x54, 0x00, 0x27, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, 0x55, + 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x99, 0x00, 0xa0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x19, 0x00, + 0x0e, 0x00, 0x09, 0x0a, 0x06, 0x17, 0x02, 0x24, 0x00, 0x30, 0x00, 0x4a, + 0x00, 0x60, 0x00, 0x72, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x98, 0x00, 0xa0, + 0x00, 0xa8, 0x00, 0xae, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x00, 0x00, 0x14, + 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, + 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x36, + 0x00, 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, + 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, 0x6f, 0x00, 0x47, + 0x00, 0x21, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x36, 0x00, 0x4b, 0x00, 0x5d, + 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x0a, 0x04, + 0x07, 0x0f, 0x05, 0x1b, 0x02, 0x26, 0x00, 0x30, 0x00, 0x46, 0x00, 0x5a, + 0x00, 0x6b, 0x00, 0x79, 0x00, 0x85, 0x00, 0x8f, 0x00, 0x98, 0x00, 0xa0, + 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x36, + 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, + 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, + 0x00, 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0xdb, 0x00, 0xd4, + 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, 0x3d, 0x00, 0x1d, + 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, 0x55, 0x00, 0x64, + 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x15, 0x00, 0x0b, 0x00, 0x09, 0x0a, 0x06, 0x14, + 0x04, 0x1e, 0x02, 0x27, 0x00, 0x30, 0x00, 0x44, 0x00, 0x56, 0x00, 0x65, + 0x00, 0x72, 0x00, 0x7e, 0x00, 0x88, 0x00, 0x91, 0x1d, 0x00, 0x1a, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, + 0x00, 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, + 0x00, 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, + 0x00, 0x5c, 0x00, 0x68, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc7, 0x00, 0xb0, + 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, 0x19, 0x00, 0x00, + 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, 0x5c, 0x00, 0x68, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, + 0x17, 0x00, 0x0f, 0x00, 0x0a, 0x05, 0x08, 0x0e, 0x06, 0x17, 0x03, 0x20, + 0x01, 0x28, 0x00, 0x2f, 0x00, 0x42, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6d, + 0x00, 0x78, 0x00, 0x82, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x00, + 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, + 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, + 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, 0xa0, 0x00, 0x84, + 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, 0x00, 0x00, 0x15, + 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, + 0x0b, 0x02, 0x09, 0x09, 0x07, 0x12, 0x05, 0x1a, 0x03, 0x21, 0x01, 0x29, + 0x00, 0x2f, 0x00, 0x40, 0x00, 0x4f, 0x00, 0x5c, 0x00, 0x68, 0x00, 0x73, + 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x04, 0x00, 0x13, + 0x00, 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, + 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0xdd, 0x00, 0xd9, + 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, 0x78, 0x00, 0x5d, + 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x24, + 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x0a, 0x06, + 0x08, 0x0d, 0x06, 0x14, 0x04, 0x1c, 0x03, 0x23, 0x01, 0x29, 0x00, 0x2f, + 0x00, 0x3e, 0x00, 0x4c, 0x00, 0x59, 0x00, 0x64, 0x1e, 0x00, 0x1c, 0x00, + 0x16, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x29, + 0x00, 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, + 0x00, 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x00, 0x22, 0x00, 0x30, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc3, + 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, 0x55, 0x00, 0x3e, + 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x11, 0x00, 0x22, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1b, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x09, 0x09, 0x07, 0x10, + 0x06, 0x17, 0x04, 0x1e, 0x02, 0x24, 0x01, 0x2a, 0x00, 0x2f, 0x00, 0x3d, + 0x00, 0x4a, 0x00, 0x56, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, + 0x05, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, + 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, 0xb7, 0x00, 0xa4, + 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, 0x39, 0x00, 0x24, + 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, + 0x11, 0x00, 0x0b, 0x00, 0x0a, 0x06, 0x08, 0x0c, 0x07, 0x13, 0x05, 0x19, + 0x04, 0x1f, 0x02, 0x25, 0x01, 0x2a, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x48, + 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x01, + 0x00, 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, + 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xdd, 0x00, 0xdb, + 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x99, 0x00, 0x85, + 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, + 0x0a, 0x04, 0x09, 0x09, 0x08, 0x0f, 0x06, 0x15, 0x05, 0x1b, 0x03, 0x20, + 0x02, 0x26, 0x01, 0x2b, 0x00, 0x2f, 0x00, 0x3b, 0x1f, 0x00, 0x1d, 0x00, + 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x13, + 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, + 0x00, 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcd, + 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, 0x7b, 0x00, 0x68, + 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0b, 0x01, 0x0a, 0x07, + 0x08, 0x0c, 0x07, 0x11, 0x06, 0x17, 0x04, 0x1c, 0x03, 0x21, 0x02, 0x26, + 0x01, 0x2b, 0x00, 0x2f, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, + 0x0e, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, + 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1c, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x04, 0x4c, 0x09, 0x11, + 0x11, 0x01, 0x17, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x24, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x01, 0x61, 0x03, 0x23, + 0x09, 0x03, 0x13, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, + 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x0f, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x87, 0x01, 0x39, 0x07, 0x0f, 0x0c, 0x02, + 0x12, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, + 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x21, 0x00, + 0x27, 0x00, 0x2b, 0x00, 0x28, 0x00, 0x21, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x01, 0x04, + 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, + 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, + 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, + 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x00, 0xbc, 0x00, 0x87, 0x00, 0x32, 0x05, 0x19, 0x09, 0x0b, 0x0b, 0x03, + 0x0e, 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, + 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x27, 0x00, 0x0f, 0x00, 0x1a, 0x00, + 0x1c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, + 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x00, 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, + 0x03, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, + 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, + 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, + 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0xcd, 0x00, 0xaf, + 0x00, 0x6e, 0x00, 0x31, 0x04, 0x1e, 0x07, 0x12, 0x09, 0x0a, 0x0a, 0x04, + 0x0b, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, + 0x18, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0e, 0x00, + 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, + 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0xd2, 0x00, 0xbc, + 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, + 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, + 0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, 0x00, + 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, + 0x1a, 0x00, 0x1b, 0x00, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0x93, 0x00, 0x5f, + 0x00, 0x30, 0x03, 0x22, 0x06, 0x17, 0x07, 0x0f, 0x09, 0x0a, 0x0a, 0x05, + 0x0b, 0x02, 0x0c, 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x28, 0x00, + 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, 0x00, + 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, + 0x1a, 0x00, 0x1b, 0x00, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, + 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, + 0x00, 0x04, 0x01, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, + 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x00, 0xd8, 0x00, 0xcb, 0x00, 0xaa, 0x00, 0x7f, 0x00, 0x55, 0x00, 0x30, + 0x02, 0x24, 0x05, 0x1b, 0x06, 0x14, 0x08, 0x0e, 0x09, 0x09, 0x0a, 0x06, + 0x0b, 0x03, 0x0b, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0e, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, + 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x00, 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, + 0x00, 0x49, 0x00, 0x36, 0x00, 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, + 0x00, 0x06, 0x00, 0x01, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, + 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0xda, 0x00, 0xd1, + 0x00, 0xb8, 0x00, 0x96, 0x00, 0x71, 0x00, 0x4e, 0x00, 0x30, 0x02, 0x26, + 0x04, 0x1e, 0x06, 0x17, 0x07, 0x12, 0x08, 0x0d, 0x09, 0x09, 0x0a, 0x06, + 0x0a, 0x04, 0x0b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, + 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0xdb, 0x00, 0xd5, + 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, + 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, + 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, + 0x13, 0x00, 0x14, 0x00, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0xa6, + 0x00, 0x87, 0x00, 0x67, 0x00, 0x4a, 0x00, 0x30, 0x02, 0x27, 0x03, 0x20, + 0x05, 0x1a, 0x06, 0x14, 0x07, 0x10, 0x08, 0x0c, 0x09, 0x09, 0x0a, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, + 0x13, 0x00, 0x14, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, + 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, + 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, 0x00, + 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc8, 0x00, 0xb2, 0x00, 0x97, 0x00, 0x7b, + 0x00, 0x60, 0x00, 0x46, 0x00, 0x30, 0x01, 0x28, 0x03, 0x21, 0x04, 0x1c, + 0x06, 0x17, 0x07, 0x13, 0x08, 0x0f, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, + 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, 0x00, + 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, + 0x00, 0x82, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, + 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0xdc, 0x00, 0xd8, + 0x00, 0xcc, 0x00, 0xba, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x72, 0x00, 0x5a, + 0x00, 0x44, 0x00, 0x2f, 0x01, 0x29, 0x03, 0x23, 0x04, 0x1e, 0x05, 0x19, + 0x06, 0x15, 0x07, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0xdd, 0x00, 0xda, + 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, + 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, + 0x00, 0x2a, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x0a, 0x00, 0x0c, 0x00, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd0, 0x00, 0xc0, + 0x00, 0xad, 0x00, 0x98, 0x00, 0x81, 0x00, 0x6b, 0x00, 0x56, 0x00, 0x42, + 0x00, 0x2f, 0x01, 0x29, 0x02, 0x24, 0x04, 0x1f, 0x05, 0x1b, 0x06, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, 0x00, + 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x0a, 0x00, 0x0c, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, + 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, + 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, + 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xc5, 0x00, 0xb5, 0x00, 0xa2, + 0x00, 0x8e, 0x00, 0x79, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, + 0x01, 0x2a, 0x02, 0x25, 0x03, 0x20, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, + 0x00, 0xa4, 0x00, 0x95, 0x00, 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, + 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0xdd, 0x00, 0xdb, + 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xaa, 0x00, 0x98, 0x00, 0x85, + 0x00, 0x72, 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x2f, 0x01, 0x2a, + 0x02, 0x26, 0x03, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, + 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0xde, 0x00, 0xdc, + 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, + 0x00, 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, + 0x00, 0x4c, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x04, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, + 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, + 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3d, 0x00, 0x2f, 0x01, 0x2b, 0x02, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, 0x00, + 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x04, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, + 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, + 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc3, 0x00, 0xb6, + 0x00, 0xa8, 0x00, 0x98, 0x00, 0x88, 0x00, 0x78, 0x00, 0x68, 0x00, 0x59, + 0x00, 0x4a, 0x00, 0x3c, 0x00, 0x2f, 0x01, 0x2b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, + 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, + 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, + 0x00, 0xb7, 0x00, 0xab, 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, + 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, + 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbb, 0x00, 0xae, 0x00, 0xa0, + 0x00, 0x91, 0x00, 0x82, 0x00, 0x73, 0x00, 0x64, 0x00, 0x56, 0x00, 0x48, + 0x00, 0x3b, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, + 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, + 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, + 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, + 0x00, 0x68, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x61, 0x03, 0x23, 0x09, 0x03, 0x13, 0x00, + 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x61, 0x00, 0x9f, + 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, + 0x00, 0xde, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x01, 0x04, 0x0a, 0x00, 0x11, 0x00, + 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, + 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x5f, 0x00, 0x9f, 0x00, 0xbc, + 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, + 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x9f, + 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x03, 0x00, 0x0a, 0x00, + 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x03, 0x00, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, + 0x00, 0xc3, 0x00, 0xc9, 0x00, 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, + 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, + 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x04, + 0x00, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, + 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, + 0x00, 0xd3, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, + 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, 0x00, 0x04, 0x01, 0x00, + 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x16, 0x00, 0x3d, + 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, + 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd0, + 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, + 0x00, 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, + 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x11, 0x00, 0x00, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, + 0x00, 0x76, 0x00, 0x88, 0x00, 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, + 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, + 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, + 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x15, 0x00, + 0x03, 0x00, 0x00, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, + 0x00, 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, + 0x00, 0xb7, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, + 0x00, 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, + 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x09, + 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, + 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xd9, + 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, + 0x00, 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, + 0x00, 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x28, + 0x00, 0x3c, 0x00, 0x4e, 0x00, 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, + 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, + 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, + 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, + 0x12, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, + 0x00, 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, + 0x00, 0x94, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, + 0x00, 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, + 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x00, + 0x00, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, + 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, + 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, + 0x00, 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, + 0x00, 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x0c, + 0x00, 0x1b, 0x00, 0x29, 0x00, 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, + 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, + 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, + 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1d, 0x00, + 0x18, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x21, + 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, + 0x00, 0x73, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, + 0x00, 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, + 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, + 0x09, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, + 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, + 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, + 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, + 0x00, 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x00, + 0x00, 0x08, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, + 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, + 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, + 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x0e, + 0x00, 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, + 0x00, 0x56, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +/** + * Stored in R8 format. Load it in the following format: + * - DX10: DXGI_FORMAT_R8_UNORM + */ +const unsigned char searchTexBytes[] = { + 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, + 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, + 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, + 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, + 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, + 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, + 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +/* clang-format on */ diff --git a/source/blender/draw/intern/smaa_textures.h b/source/blender/draw/intern/smaa_textures.h index 8f150c6cd7d..bb3e3bdfb2f 100644 --- a/source/blender/draw/intern/smaa_textures.h +++ b/source/blender/draw/intern/smaa_textures.h @@ -33,15051 +33,19 @@ #define AREATEX_PITCH (AREATEX_WIDTH * 2) #define AREATEX_SIZE (AREATEX_HEIGHT * AREATEX_PITCH) -/* Don't re-wrap large data definitions. */ -/* clang-format off */ - /** * Stored in R8G8 format. Load it in the following format: * - DX10: DXGI_FORMAT_R8G8_UNORM */ -static const unsigned char areaTexBytes[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x44, 0x7b, 0x41, 0x5d, - 0x42, 0x54, 0x41, 0x4f, 0x42, 0x4b, 0x42, 0x49, 0x42, 0x48, 0x41, 0x47, - 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, - 0x42, 0x43, 0x41, 0x43, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, - 0x04, 0x7f, 0x22, 0x3d, 0x2b, 0x3d, 0x30, 0x3d, 0x33, 0x3d, 0x35, 0x3d, - 0x37, 0x3d, 0x38, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, - 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, - 0x3c, 0x3d, 0x3c, 0x3d, 0x40, 0x7f, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x62, 0x20, 0x4d, 0x2a, 0x48, 0x30, 0x44, 0x33, - 0x44, 0x35, 0x44, 0x36, 0x43, 0x37, 0x42, 0x38, 0x43, 0x39, 0x42, 0x39, - 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x41, 0x3b, - 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x00, 0x5d, 0x0c, 0x49, - 0x16, 0x43, 0x1d, 0x41, 0x22, 0x40, 0x26, 0x3f, 0x29, 0x3f, 0x2c, 0x3f, - 0x2e, 0x3e, 0x2f, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, 0x33, 0x3e, - 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x66, 0x00, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x6d, 0x0b, 0x57, 0x16, 0x4f, 0x1d, 0x4a, 0x22, 0x48, 0x26, 0x47, 0x29, - 0x46, 0x2b, 0x44, 0x2d, 0x44, 0x2f, 0x44, 0x30, 0x44, 0x31, 0x44, 0x32, - 0x43, 0x33, 0x43, 0x34, 0x43, 0x34, 0x42, 0x35, 0x43, 0x36, 0x42, 0x36, - 0x42, 0x37, 0x42, 0x37, 0x00, 0x68, 0x06, 0x54, 0x0d, 0x4b, 0x14, 0x47, - 0x19, 0x44, 0x1d, 0x43, 0x20, 0x41, 0x23, 0x41, 0x26, 0x40, 0x27, 0x40, - 0x29, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x2f, 0x3f, - 0x30, 0x3f, 0x30, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, - 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x72, 0x06, 0x5f, 0x0d, - 0x56, 0x14, 0x4f, 0x19, 0x4d, 0x1d, 0x4a, 0x20, 0x49, 0x23, 0x47, 0x25, - 0x46, 0x27, 0x46, 0x29, 0x45, 0x2a, 0x45, 0x2c, 0x44, 0x2d, 0x44, 0x2e, - 0x44, 0x2f, 0x43, 0x30, 0x44, 0x30, 0x44, 0x31, 0x43, 0x32, 0x43, 0x33, - 0x00, 0x6d, 0x04, 0x5b, 0x09, 0x51, 0x0e, 0x4c, 0x13, 0x48, 0x17, 0x46, - 0x1a, 0x44, 0x1d, 0x43, 0x1f, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, - 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, - 0x2d, 0x3f, 0x2e, 0x3f, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, - 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x75, 0x03, 0x64, 0x09, 0x5a, 0x0e, 0x54, 0x13, - 0x51, 0x17, 0x4e, 0x1a, 0x4c, 0x1d, 0x49, 0x1f, 0x49, 0x22, 0x48, 0x23, - 0x47, 0x25, 0x46, 0x26, 0x46, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, - 0x44, 0x2c, 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2e, 0x00, 0x70, 0x02, 0x60, - 0x07, 0x56, 0x0b, 0x50, 0x0f, 0x4c, 0x12, 0x49, 0x16, 0x47, 0x18, 0x46, - 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x43, 0x21, 0x42, 0x22, 0x42, 0x24, 0x41, - 0x25, 0x41, 0x26, 0x40, 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x77, 0x02, 0x68, 0x06, 0x5f, 0x0b, 0x58, 0x0f, 0x54, 0x12, 0x51, 0x15, - 0x4f, 0x18, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1f, 0x49, 0x21, 0x48, 0x22, - 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x45, 0x27, 0x45, 0x28, 0x45, 0x29, - 0x45, 0x2a, 0x44, 0x2b, 0x00, 0x72, 0x02, 0x64, 0x05, 0x5b, 0x08, 0x54, - 0x0c, 0x50, 0x0f, 0x4d, 0x12, 0x4a, 0x14, 0x48, 0x17, 0x47, 0x19, 0x46, - 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x44, 0x20, 0x42, 0x21, 0x42, 0x22, 0x42, - 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x40, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, - 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x78, 0x02, 0x6b, 0x05, - 0x62, 0x08, 0x5b, 0x0c, 0x57, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4e, 0x17, - 0x4d, 0x19, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, 0x49, 0x20, 0x48, 0x21, - 0x48, 0x22, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26, 0x46, 0x27, 0x45, 0x27, - 0x00, 0x74, 0x01, 0x66, 0x04, 0x5e, 0x07, 0x57, 0x0a, 0x53, 0x0d, 0x4f, - 0x10, 0x4d, 0x12, 0x4b, 0x14, 0x49, 0x16, 0x48, 0x18, 0x46, 0x1a, 0x45, - 0x1b, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x20, 0x43, 0x21, 0x42, 0x22, 0x42, - 0x23, 0x42, 0x24, 0x41, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, - 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, - 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x79, 0x01, 0x6d, 0x04, 0x65, 0x07, 0x5e, 0x0a, - 0x5a, 0x0d, 0x56, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4f, 0x16, 0x4e, 0x18, - 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, 0x49, 0x1f, 0x48, 0x21, - 0x48, 0x22, 0x48, 0x23, 0x47, 0x23, 0x46, 0x25, 0x00, 0x75, 0x01, 0x69, - 0x03, 0x61, 0x06, 0x5a, 0x08, 0x56, 0x0b, 0x52, 0x0d, 0x4f, 0x10, 0x4d, - 0x12, 0x4b, 0x14, 0x4a, 0x16, 0x48, 0x17, 0x47, 0x19, 0x46, 0x1a, 0x45, - 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x20, 0x43, 0x21, 0x42, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, - 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, - 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7a, 0x01, 0x6f, 0x03, 0x67, 0x06, 0x60, 0x08, 0x5d, 0x0b, 0x59, 0x0d, - 0x56, 0x10, 0x53, 0x11, 0x51, 0x14, 0x50, 0x15, 0x4e, 0x17, 0x4d, 0x19, - 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x48, 0x1e, 0x49, 0x1f, 0x49, 0x20, - 0x48, 0x21, 0x48, 0x22, 0x00, 0x76, 0x01, 0x6b, 0x03, 0x63, 0x05, 0x5d, - 0x07, 0x58, 0x09, 0x54, 0x0c, 0x52, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, - 0x13, 0x4a, 0x15, 0x49, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x45, - 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, - 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, - 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x01, 0x71, 0x02, - 0x69, 0x05, 0x63, 0x07, 0x5f, 0x09, 0x5b, 0x0c, 0x58, 0x0e, 0x55, 0x10, - 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4e, 0x16, 0x4e, 0x18, 0x4c, 0x19, - 0x4c, 0x1a, 0x4a, 0x1c, 0x4b, 0x1d, 0x49, 0x1e, 0x49, 0x1f, 0x49, 0x20, - 0x00, 0x77, 0x00, 0x6c, 0x02, 0x65, 0x04, 0x5f, 0x06, 0x5a, 0x08, 0x57, - 0x0a, 0x54, 0x0c, 0x51, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, - 0x15, 0x49, 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x1a, 0x46, 0x1b, 0x45, - 0x1c, 0x45, 0x1d, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, - 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, - 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, - 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x00, 0x72, 0x02, 0x6b, 0x04, 0x64, 0x06, - 0x61, 0x08, 0x5d, 0x0a, 0x5a, 0x0c, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, - 0x52, 0x13, 0x50, 0x15, 0x4f, 0x16, 0x4e, 0x17, 0x4d, 0x18, 0x4b, 0x1a, - 0x4b, 0x1a, 0x4b, 0x1c, 0x4b, 0x1d, 0x49, 0x1d, 0x00, 0x77, 0x00, 0x6e, - 0x02, 0x66, 0x04, 0x61, 0x05, 0x5c, 0x07, 0x59, 0x09, 0x56, 0x0b, 0x53, - 0x0d, 0x51, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, - 0x15, 0x48, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x46, 0x1b, 0x45, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, - 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, - 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7c, 0x00, 0x73, 0x02, 0x6c, 0x03, 0x66, 0x05, 0x63, 0x07, 0x5e, 0x09, - 0x5c, 0x0b, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, - 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x17, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, - 0x4b, 0x1a, 0x4b, 0x1c, 0x00, 0x77, 0x00, 0x6f, 0x02, 0x68, 0x03, 0x63, - 0x05, 0x5e, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x55, 0x0b, 0x53, 0x0d, 0x50, - 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x49, - 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x19, 0x46, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, - 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, - 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, - 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x74, 0x02, - 0x6e, 0x03, 0x68, 0x05, 0x64, 0x06, 0x60, 0x08, 0x5d, 0x0a, 0x5a, 0x0b, - 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x14, - 0x50, 0x15, 0x4d, 0x16, 0x4e, 0x18, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, - 0x00, 0x78, 0x00, 0x70, 0x01, 0x69, 0x03, 0x64, 0x04, 0x60, 0x06, 0x5c, - 0x07, 0x59, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x52, 0x0d, 0x50, 0x0f, 0x4f, - 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x48, - 0x17, 0x48, 0x18, 0x47, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, - 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, - 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, - 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, - 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x75, 0x01, 0x6f, 0x02, 0x69, 0x04, - 0x65, 0x06, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0b, 0x5a, 0x0c, 0x58, 0x0d, - 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x13, 0x4f, 0x15, - 0x4e, 0x15, 0x4e, 0x17, 0x4d, 0x18, 0x4c, 0x18, 0x00, 0x78, 0x00, 0x71, - 0x01, 0x6a, 0x03, 0x66, 0x04, 0x61, 0x05, 0x5d, 0x06, 0x5a, 0x08, 0x58, - 0x09, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0e, 0x50, 0x0f, 0x4e, 0x10, 0x4e, - 0x11, 0x4c, 0x13, 0x4c, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x49, 0x16, 0x48, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, - 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, - 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, - 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, - 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7d, 0x00, 0x76, 0x01, 0x70, 0x02, 0x6a, 0x03, 0x67, 0x05, 0x63, 0x06, - 0x60, 0x08, 0x5d, 0x09, 0x5b, 0x0b, 0x59, 0x0c, 0x57, 0x0d, 0x55, 0x0e, - 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x4f, 0x13, 0x50, 0x15, 0x4f, 0x15, - 0x4e, 0x16, 0x4e, 0x18, 0x00, 0x79, 0x00, 0x71, 0x01, 0x6c, 0x02, 0x66, - 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5c, 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, - 0x0b, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, - 0x13, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x4a, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, - 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, - 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, - 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, - 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, - 0x71, 0x02, 0x6c, 0x03, 0x68, 0x05, 0x64, 0x06, 0x61, 0x07, 0x5e, 0x09, - 0x5c, 0x0a, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0e, 0x55, 0x0e, 0x55, 0x11, - 0x52, 0x11, 0x51, 0x12, 0x51, 0x13, 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x15, - 0x00, 0x79, 0x00, 0x72, 0x01, 0x6d, 0x02, 0x68, 0x03, 0x63, 0x04, 0x60, - 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x53, - 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, - 0x13, 0x4b, 0x14, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x1f, 0x00, 0x3f, - 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, - 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, - 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x58, 0x00, 0x70, 0x00, 0x77, - 0x00, 0x79, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, - 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x3f, - 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, - 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, - 0x00, 0x7e, 0x00, 0x7e, 0x1f, 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, - 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, - 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x77, 0x01, 0x72, 0x02, 0x6d, 0x03, - 0x69, 0x04, 0x66, 0x06, 0x63, 0x07, 0x5f, 0x08, 0x5e, 0x09, 0x5c, 0x0b, - 0x5a, 0x0c, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x51, 0x11, - 0x52, 0x12, 0x51, 0x13, 0x50, 0x14, 0x50, 0x15, 0x00, 0x79, 0x00, 0x73, - 0x01, 0x6d, 0x02, 0x69, 0x03, 0x65, 0x04, 0x61, 0x05, 0x5e, 0x06, 0x5c, - 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0d, 0x53, 0x0d, 0x51, - 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x3f, 0x00, 0x5c, - 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, - 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, - 0x00, 0x58, 0x00, 0x44, 0x00, 0x55, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x72, - 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, - 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, - 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, - 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, - 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, - 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, - 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7d, 0x00, 0x78, 0x01, 0x72, 0x02, 0x6e, 0x02, 0x6a, 0x03, 0x67, 0x05, - 0x64, 0x06, 0x60, 0x07, 0x5f, 0x09, 0x5c, 0x0a, 0x5b, 0x0b, 0x5a, 0x0c, - 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0f, 0x53, 0x11, 0x52, 0x11, 0x52, 0x12, - 0x51, 0x13, 0x50, 0x13, 0x00, 0x79, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x69, - 0x03, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, - 0x09, 0x56, 0x0b, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, - 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x12, 0x4c, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, - 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, - 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x70, 0x00, 0x55, - 0x00, 0x20, 0x00, 0x3e, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x6a, - 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, - 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, - 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, - 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x66, 0x00, 0x3f, 0x00, - 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, - 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, - 0x00, 0x79, 0x00, 0x7a, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x01, - 0x73, 0x02, 0x6e, 0x02, 0x6b, 0x03, 0x68, 0x05, 0x65, 0x06, 0x61, 0x07, - 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0d, - 0x56, 0x0e, 0x54, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, - 0x00, 0x79, 0x00, 0x74, 0x00, 0x6f, 0x02, 0x6a, 0x03, 0x66, 0x04, 0x63, - 0x05, 0x60, 0x05, 0x5e, 0x06, 0x5b, 0x07, 0x59, 0x09, 0x58, 0x09, 0x55, - 0x0b, 0x55, 0x0c, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, 0x0f, 0x4e, - 0x11, 0x4e, 0x11, 0x4d, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, - 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, - 0x00, 0x73, 0x00, 0x75, 0x00, 0x77, 0x00, 0x67, 0x00, 0x3e, 0x00, 0x0d, - 0x00, 0x28, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, - 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, - 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, - 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, - 0x00, 0x73, 0x00, 0x75, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x01, - 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, - 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x00, 0x74, 0x01, 0x6f, 0x02, - 0x6c, 0x03, 0x68, 0x04, 0x65, 0x05, 0x62, 0x06, 0x61, 0x07, 0x5f, 0x09, - 0x5c, 0x09, 0x5b, 0x0b, 0x5a, 0x0b, 0x58, 0x0c, 0x57, 0x0d, 0x55, 0x0e, - 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, 0x00, 0x79, 0x00, 0x74, - 0x00, 0x70, 0x01, 0x6b, 0x02, 0x67, 0x03, 0x64, 0x04, 0x61, 0x05, 0x5f, - 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, - 0x0c, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, - 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, - 0x00, 0x79, 0x00, 0x6e, 0x00, 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x1b, - 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, - 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, - 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, - 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, - 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x1b, - 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, - 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x21, 0x5d, 0x0c, 0x68, - 0x06, 0x6d, 0x04, 0x71, 0x02, 0x72, 0x02, 0x74, 0x01, 0x75, 0x01, 0x76, - 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, - 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, - 0x42, 0x40, 0x17, 0x55, 0x0c, 0x60, 0x08, 0x66, 0x05, 0x6a, 0x04, 0x6d, - 0x03, 0x6f, 0x02, 0x71, 0x02, 0x73, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, - 0x01, 0x76, 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, - 0x00, 0x78, 0x00, 0x78, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x21, 0x5d, 0x0c, 0x68, - 0x06, 0x6d, 0x04, 0x71, 0x02, 0x72, 0x02, 0x74, 0x01, 0x75, 0x01, 0x76, - 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, - 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, - 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x7b, 0x00, 0x72, - 0x00, 0x5a, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x16, 0x00, 0x28, - 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, - 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, - 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x7a, 0x00, 0x71, 0x00, - 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, - 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, - 0x00, 0x62, 0x00, 0x65, 0x2b, 0x49, 0x16, 0x53, 0x0d, 0x5b, 0x09, 0x60, - 0x07, 0x64, 0x05, 0x67, 0x04, 0x69, 0x03, 0x6b, 0x03, 0x6c, 0x02, 0x6e, - 0x02, 0x6f, 0x02, 0x70, 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, - 0x01, 0x73, 0x01, 0x74, 0x00, 0x74, 0x00, 0x75, 0x57, 0x16, 0x2d, 0x2c, - 0x1b, 0x3b, 0x12, 0x45, 0x0d, 0x4d, 0x0b, 0x53, 0x08, 0x57, 0x07, 0x5b, - 0x05, 0x5e, 0x05, 0x61, 0x04, 0x63, 0x04, 0x65, 0x03, 0x67, 0x02, 0x68, - 0x02, 0x69, 0x02, 0x6b, 0x02, 0x6c, 0x01, 0x6d, 0x01, 0x6e, 0x01, 0x6f, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x2b, 0x49, 0x16, 0x53, 0x0d, 0x5b, 0x09, 0x60, - 0x07, 0x64, 0x05, 0x67, 0x04, 0x69, 0x03, 0x6b, 0x03, 0x6c, 0x02, 0x6e, - 0x02, 0x6f, 0x02, 0x70, 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, - 0x01, 0x73, 0x01, 0x74, 0x00, 0x74, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, - 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, - 0x00, 0x57, 0x00, 0x5b, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, - 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, - 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, - 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, - 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, - 0x00, 0x57, 0x00, 0x5b, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, - 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, - 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, - 0x31, 0x43, 0x1d, 0x4b, 0x14, 0x51, 0x0e, 0x56, 0x0b, 0x5b, 0x08, 0x5e, - 0x07, 0x61, 0x06, 0x63, 0x05, 0x65, 0x04, 0x67, 0x04, 0x68, 0x03, 0x69, - 0x03, 0x6a, 0x03, 0x6c, 0x02, 0x6d, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x6f, - 0x02, 0x70, 0x01, 0x70, 0x61, 0x0c, 0x3b, 0x1b, 0x28, 0x28, 0x1d, 0x32, - 0x16, 0x3a, 0x11, 0x41, 0x0e, 0x47, 0x0c, 0x4b, 0x0a, 0x4f, 0x09, 0x53, - 0x07, 0x55, 0x06, 0x58, 0x05, 0x5a, 0x05, 0x5c, 0x05, 0x5e, 0x04, 0x60, - 0x04, 0x61, 0x04, 0x63, 0x03, 0x64, 0x02, 0x66, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x31, 0x43, 0x1d, 0x4b, 0x14, 0x51, 0x0e, 0x56, 0x0b, 0x5b, 0x08, 0x5e, - 0x07, 0x61, 0x06, 0x63, 0x05, 0x65, 0x04, 0x67, 0x04, 0x68, 0x03, 0x69, - 0x03, 0x6a, 0x03, 0x6c, 0x02, 0x6d, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x6f, - 0x02, 0x70, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, - 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, - 0x00, 0x13, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, - 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, - 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, - 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, - 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x33, 0x41, 0x22, 0x46, - 0x19, 0x4c, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5d, - 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x66, 0x04, 0x66, - 0x04, 0x68, 0x03, 0x69, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x02, 0x6c, - 0x67, 0x07, 0x45, 0x12, 0x32, 0x1d, 0x26, 0x26, 0x1e, 0x2e, 0x18, 0x34, - 0x14, 0x3a, 0x11, 0x3f, 0x0f, 0x44, 0x0c, 0x47, 0x0b, 0x4b, 0x0a, 0x4d, - 0x09, 0x51, 0x07, 0x52, 0x07, 0x55, 0x06, 0x57, 0x05, 0x58, 0x05, 0x5a, - 0x05, 0x5c, 0x04, 0x5d, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x33, 0x41, 0x22, 0x46, - 0x19, 0x4c, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5d, - 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x66, 0x04, 0x66, - 0x04, 0x68, 0x03, 0x69, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x02, 0x6c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, - 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x7d, 0x00, 0x79, - 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, - 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, - 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, - 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x7d, 0x00, 0x79, 0x00, - 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, - 0x00, 0x40, 0x00, 0x46, 0x35, 0x40, 0x27, 0x44, 0x1d, 0x48, 0x17, 0x4c, - 0x12, 0x50, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5e, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x65, - 0x04, 0x66, 0x04, 0x66, 0x04, 0x67, 0x03, 0x68, 0x6b, 0x05, 0x4d, 0x0d, - 0x3b, 0x16, 0x2e, 0x1e, 0x25, 0x25, 0x1f, 0x2b, 0x1a, 0x31, 0x16, 0x36, - 0x13, 0x3a, 0x10, 0x3e, 0x0f, 0x42, 0x0d, 0x45, 0x0c, 0x47, 0x0a, 0x4a, - 0x0a, 0x4c, 0x09, 0x4f, 0x07, 0x51, 0x07, 0x52, 0x07, 0x54, 0x06, 0x56, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x35, 0x40, 0x27, 0x44, 0x1d, 0x48, 0x17, 0x4c, - 0x12, 0x50, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5e, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x65, - 0x04, 0x66, 0x04, 0x66, 0x04, 0x67, 0x03, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, - 0x00, 0x34, 0x00, 0x3b, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, - 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, - 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, - 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, - 0x00, 0x34, 0x00, 0x3b, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, - 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, - 0x37, 0x3f, 0x29, 0x43, 0x21, 0x46, 0x1a, 0x49, 0x16, 0x4d, 0x12, 0x50, - 0x10, 0x52, 0x0d, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5d, 0x06, 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, - 0x04, 0x64, 0x04, 0x65, 0x6e, 0x04, 0x53, 0x0a, 0x41, 0x11, 0x34, 0x18, - 0x2b, 0x1f, 0x24, 0x24, 0x1f, 0x29, 0x1b, 0x2e, 0x17, 0x33, 0x15, 0x37, - 0x12, 0x3a, 0x10, 0x3d, 0x0f, 0x40, 0x0d, 0x43, 0x0c, 0x45, 0x0c, 0x48, - 0x0a, 0x4a, 0x0a, 0x4c, 0x09, 0x4e, 0x07, 0x4f, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x37, 0x3f, 0x29, 0x43, 0x21, 0x46, 0x1a, 0x49, 0x16, 0x4d, 0x12, 0x50, - 0x10, 0x52, 0x0d, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5d, 0x06, 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, - 0x04, 0x64, 0x04, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, - 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, - 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, - 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, - 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, - 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x38, 0x3f, 0x2c, 0x41, - 0x23, 0x44, 0x1d, 0x47, 0x19, 0x4a, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x52, - 0x0e, 0x54, 0x0c, 0x56, 0x0b, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x61, 0x05, 0x61, 0x05, 0x62, - 0x70, 0x03, 0x58, 0x08, 0x47, 0x0e, 0x3a, 0x14, 0x31, 0x1a, 0x29, 0x1f, - 0x24, 0x24, 0x20, 0x28, 0x1c, 0x2d, 0x19, 0x31, 0x16, 0x34, 0x14, 0x37, - 0x12, 0x3a, 0x10, 0x3d, 0x0f, 0x3f, 0x0e, 0x42, 0x0c, 0x44, 0x0c, 0x46, - 0x0b, 0x47, 0x0a, 0x4a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x38, 0x3f, 0x2c, 0x41, - 0x23, 0x44, 0x1d, 0x47, 0x19, 0x4a, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x52, - 0x0e, 0x54, 0x0c, 0x56, 0x0b, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x61, 0x05, 0x61, 0x05, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x7e, 0x00, 0x7c, - 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, - 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, - 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, - 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x7e, 0x00, 0x7c, 0x00, - 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, - 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, - 0x00, 0x1e, 0x00, 0x26, 0x39, 0x3f, 0x2e, 0x41, 0x26, 0x43, 0x20, 0x46, - 0x1b, 0x48, 0x17, 0x4b, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x51, 0x0e, 0x53, - 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x60, 0x71, 0x02, 0x5b, 0x07, - 0x4c, 0x0c, 0x3f, 0x11, 0x36, 0x16, 0x2e, 0x1b, 0x29, 0x20, 0x24, 0x23, - 0x20, 0x28, 0x1d, 0x2b, 0x19, 0x2f, 0x17, 0x32, 0x16, 0x35, 0x12, 0x37, - 0x12, 0x3a, 0x10, 0x3c, 0x0f, 0x3f, 0x0e, 0x41, 0x0c, 0x42, 0x0c, 0x45, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x39, 0x3f, 0x2e, 0x41, 0x26, 0x43, 0x20, 0x46, - 0x1b, 0x48, 0x17, 0x4b, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x51, 0x0e, 0x53, - 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, - 0x00, 0x13, 0x00, 0x1b, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, - 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, - 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, - 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, - 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, - 0x00, 0x13, 0x00, 0x1b, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, - 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, - 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, - 0x39, 0x3e, 0x2f, 0x40, 0x28, 0x42, 0x22, 0x45, 0x1d, 0x47, 0x19, 0x49, - 0x16, 0x4b, 0x14, 0x4d, 0x11, 0x4f, 0x10, 0x51, 0x0f, 0x53, 0x0d, 0x54, - 0x0c, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x07, 0x5c, 0x07, 0x5e, 0x73, 0x02, 0x5e, 0x05, 0x4f, 0x0a, 0x44, 0x0f, - 0x3a, 0x13, 0x33, 0x18, 0x2d, 0x1c, 0x27, 0x20, 0x23, 0x23, 0x20, 0x27, - 0x1d, 0x2a, 0x1a, 0x2d, 0x18, 0x30, 0x16, 0x33, 0x14, 0x35, 0x12, 0x38, - 0x12, 0x3a, 0x10, 0x3c, 0x0f, 0x3e, 0x0f, 0x41, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x39, 0x3e, 0x2f, 0x40, 0x28, 0x42, 0x22, 0x45, 0x1d, 0x47, 0x19, 0x49, - 0x16, 0x4b, 0x14, 0x4d, 0x11, 0x4f, 0x10, 0x51, 0x0f, 0x53, 0x0d, 0x54, - 0x0c, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x07, 0x5c, 0x07, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, - 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, - 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, - 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, - 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, - 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, - 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, - 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x3a, 0x3e, 0x31, 0x40, - 0x29, 0x42, 0x23, 0x44, 0x1f, 0x46, 0x1b, 0x48, 0x18, 0x4a, 0x15, 0x4c, - 0x13, 0x4d, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x55, - 0x0b, 0x56, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x74, 0x01, 0x62, 0x05, 0x53, 0x08, 0x47, 0x0c, 0x3e, 0x11, 0x37, 0x15, - 0x31, 0x19, 0x2b, 0x1d, 0x27, 0x20, 0x23, 0x23, 0x20, 0x26, 0x1d, 0x2a, - 0x1b, 0x2c, 0x19, 0x2f, 0x16, 0x31, 0x16, 0x34, 0x13, 0x35, 0x12, 0x38, - 0x12, 0x3b, 0x0f, 0x3b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3a, 0x3e, 0x31, 0x40, - 0x29, 0x42, 0x23, 0x44, 0x1f, 0x46, 0x1b, 0x48, 0x18, 0x4a, 0x15, 0x4c, - 0x13, 0x4d, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x55, - 0x0b, 0x56, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x7e, 0x00, 0x7d, - 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, - 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, - 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, - 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, 0x7e, 0x00, 0x7d, 0x00, - 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, - 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x3a, 0x3e, 0x32, 0x3f, 0x2b, 0x41, 0x25, 0x43, - 0x21, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, - 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, - 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x58, 0x09, 0x59, 0x75, 0x01, 0x64, 0x04, - 0x56, 0x07, 0x4b, 0x0b, 0x42, 0x0f, 0x3a, 0x12, 0x34, 0x16, 0x2f, 0x19, - 0x2a, 0x1d, 0x26, 0x20, 0x23, 0x23, 0x21, 0x26, 0x1d, 0x29, 0x1b, 0x2b, - 0x19, 0x2e, 0x18, 0x30, 0x16, 0x32, 0x15, 0x35, 0x12, 0x35, 0x12, 0x38, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x3a, 0x3e, 0x32, 0x3f, 0x2b, 0x41, 0x25, 0x43, - 0x21, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, - 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, - 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x58, 0x09, 0x59, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, - 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, - 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, - 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, - 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, - 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, - 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x3b, 0x3e, 0x33, 0x3f, 0x2c, 0x41, 0x27, 0x42, 0x22, 0x44, 0x1f, 0x45, - 0x1c, 0x47, 0x19, 0x49, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, - 0x10, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x55, - 0x0b, 0x57, 0x0a, 0x58, 0x76, 0x01, 0x66, 0x04, 0x58, 0x06, 0x4d, 0x0a, - 0x45, 0x0d, 0x3e, 0x10, 0x37, 0x14, 0x32, 0x17, 0x2d, 0x1a, 0x2a, 0x1d, - 0x26, 0x21, 0x22, 0x22, 0x21, 0x26, 0x1d, 0x28, 0x1c, 0x2b, 0x19, 0x2c, - 0x19, 0x30, 0x16, 0x30, 0x16, 0x33, 0x14, 0x35, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x3b, 0x3e, 0x33, 0x3f, 0x2c, 0x41, 0x27, 0x42, 0x22, 0x44, 0x1f, 0x45, - 0x1c, 0x47, 0x19, 0x49, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, - 0x10, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x55, - 0x0b, 0x57, 0x0a, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3e, 0x33, 0x3f, - 0x2d, 0x40, 0x28, 0x42, 0x24, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1a, 0x48, - 0x18, 0x49, 0x16, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x10, 0x50, - 0x0f, 0x51, 0x0f, 0x53, 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x55, 0x0b, 0x56, - 0x77, 0x01, 0x67, 0x03, 0x5a, 0x05, 0x51, 0x09, 0x48, 0x0c, 0x40, 0x0f, - 0x3a, 0x12, 0x35, 0x16, 0x30, 0x18, 0x2c, 0x1b, 0x29, 0x1d, 0x26, 0x21, - 0x22, 0x22, 0x21, 0x26, 0x1d, 0x27, 0x1d, 0x2b, 0x1a, 0x2b, 0x19, 0x2e, - 0x17, 0x30, 0x16, 0x31, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x33, 0x3f, - 0x2d, 0x40, 0x28, 0x42, 0x24, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1a, 0x48, - 0x18, 0x49, 0x16, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x10, 0x50, - 0x0f, 0x51, 0x0f, 0x53, 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x55, 0x0b, 0x56, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3e, 0x34, 0x3f, 0x2e, 0x40, 0x29, 0x41, - 0x25, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x47, 0x19, 0x48, 0x18, 0x4a, - 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, - 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x77, 0x01, 0x68, 0x02, - 0x5d, 0x05, 0x52, 0x07, 0x4a, 0x0a, 0x43, 0x0d, 0x3d, 0x10, 0x38, 0x12, - 0x33, 0x16, 0x2f, 0x19, 0x2b, 0x1b, 0x28, 0x1d, 0x26, 0x21, 0x22, 0x22, - 0x21, 0x26, 0x1e, 0x26, 0x1d, 0x2a, 0x1a, 0x2b, 0x19, 0x2d, 0x18, 0x30, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x34, 0x3f, 0x2e, 0x40, 0x29, 0x41, - 0x25, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x47, 0x19, 0x48, 0x18, 0x4a, - 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, - 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x3e, 0x35, 0x3f, 0x2f, 0x40, 0x2a, 0x41, 0x26, 0x42, 0x22, 0x44, - 0x20, 0x45, 0x1d, 0x46, 0x1b, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4b, - 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x52, - 0x0d, 0x53, 0x0d, 0x53, 0x77, 0x00, 0x6a, 0x02, 0x5e, 0x04, 0x55, 0x07, - 0x4c, 0x0a, 0x45, 0x0c, 0x40, 0x0f, 0x3a, 0x12, 0x35, 0x14, 0x31, 0x16, - 0x2e, 0x19, 0x2b, 0x1c, 0x27, 0x1d, 0x26, 0x22, 0x21, 0x22, 0x21, 0x25, - 0x1e, 0x26, 0x1d, 0x29, 0x1b, 0x2b, 0x19, 0x2b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x3c, 0x3e, 0x35, 0x3f, 0x2f, 0x40, 0x2a, 0x41, 0x26, 0x42, 0x22, 0x44, - 0x20, 0x45, 0x1d, 0x46, 0x1b, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4b, - 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x52, - 0x0d, 0x53, 0x0d, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x35, 0x3f, - 0x30, 0x3f, 0x2b, 0x40, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1e, 0x45, - 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4c, 0x13, 0x4c, - 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x0e, 0x53, - 0x78, 0x00, 0x6b, 0x02, 0x60, 0x04, 0x57, 0x06, 0x4f, 0x09, 0x48, 0x0c, - 0x42, 0x0e, 0x3c, 0x10, 0x38, 0x12, 0x34, 0x16, 0x30, 0x18, 0x2d, 0x19, - 0x2b, 0x1d, 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, - 0x1d, 0x28, 0x1c, 0x2b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x35, 0x3f, - 0x30, 0x3f, 0x2b, 0x40, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1e, 0x45, - 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4c, 0x13, 0x4c, - 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x0e, 0x53, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x36, 0x3f, 0x30, 0x3f, 0x2c, 0x40, - 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, - 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, - 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x78, 0x00, 0x6d, 0x02, - 0x61, 0x04, 0x58, 0x05, 0x51, 0x07, 0x4a, 0x0a, 0x44, 0x0c, 0x3f, 0x0f, - 0x3a, 0x12, 0x35, 0x14, 0x32, 0x16, 0x30, 0x19, 0x2b, 0x19, 0x2a, 0x1d, - 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, 0x1d, 0x27, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x36, 0x3f, 0x30, 0x3f, 0x2c, 0x40, - 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, - 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, - 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x3e, 0x37, 0x3e, 0x31, 0x3f, 0x2d, 0x40, 0x29, 0x41, 0x26, 0x42, - 0x23, 0x44, 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x48, - 0x17, 0x4a, 0x15, 0x4a, 0x14, 0x4c, 0x13, 0x4c, 0x12, 0x4e, 0x11, 0x4e, - 0x11, 0x50, 0x0f, 0x50, 0x78, 0x00, 0x6d, 0x02, 0x63, 0x04, 0x5a, 0x05, - 0x53, 0x07, 0x4c, 0x0a, 0x46, 0x0c, 0x41, 0x0f, 0x3c, 0x0f, 0x38, 0x12, - 0x35, 0x16, 0x30, 0x16, 0x2e, 0x19, 0x2b, 0x1a, 0x29, 0x1d, 0x26, 0x1e, - 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1f, 0x26, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x3c, 0x3e, 0x37, 0x3e, 0x31, 0x3f, 0x2d, 0x40, 0x29, 0x41, 0x26, 0x42, - 0x23, 0x44, 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x48, - 0x17, 0x4a, 0x15, 0x4a, 0x14, 0x4c, 0x13, 0x4c, 0x12, 0x4e, 0x11, 0x4e, - 0x11, 0x50, 0x0f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3e, - 0x32, 0x3f, 0x2e, 0x40, 0x2a, 0x41, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, - 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, - 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, - 0x78, 0x00, 0x6e, 0x01, 0x65, 0x03, 0x5c, 0x05, 0x54, 0x07, 0x4e, 0x09, - 0x48, 0x0b, 0x43, 0x0c, 0x3e, 0x0f, 0x3b, 0x12, 0x36, 0x12, 0x33, 0x16, - 0x30, 0x17, 0x2d, 0x19, 0x2b, 0x1b, 0x28, 0x1d, 0x26, 0x1e, 0x25, 0x22, - 0x21, 0x22, 0x21, 0x24, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, - 0x32, 0x3f, 0x2e, 0x40, 0x2a, 0x41, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, - 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, - 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3e, 0x33, 0x3f, 0x2e, 0x3f, - 0x2b, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1e, 0x45, - 0x1c, 0x46, 0x1b, 0x48, 0x19, 0x48, 0x18, 0x4a, 0x16, 0x4a, 0x15, 0x4b, - 0x13, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x79, 0x00, 0x6f, 0x01, - 0x66, 0x02, 0x5d, 0x04, 0x56, 0x06, 0x4f, 0x07, 0x4a, 0x0a, 0x45, 0x0c, - 0x41, 0x0f, 0x3b, 0x0f, 0x38, 0x12, 0x35, 0x14, 0x31, 0x16, 0x30, 0x18, - 0x2b, 0x19, 0x2b, 0x1c, 0x27, 0x1d, 0x26, 0x1f, 0x24, 0x22, 0x21, 0x22, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, 0x33, 0x3f, 0x2e, 0x3f, - 0x2b, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1e, 0x45, - 0x1c, 0x46, 0x1b, 0x48, 0x19, 0x48, 0x18, 0x4a, 0x16, 0x4a, 0x15, 0x4b, - 0x13, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x74, 0x00, 0x77, 0x00, 0x79, 0x00, - 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, - 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x08, 0x00, 0x44, 0x00, - 0x56, 0x00, 0x61, 0x00, 0x67, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x70, 0x00, - 0x71, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x76, 0x00, - 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, - 0x44, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x71, 0x00, 0x75, 0x00, 0x77, 0x00, - 0x79, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7c, 0x00, - 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0b, 0x57, 0x06, - 0x5f, 0x03, 0x63, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6d, 0x01, 0x6e, 0x01, - 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, - 0x76, 0x00, 0x76, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, 0x00, - 0x82, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x40, 0x17, 0x17, 0x2c, 0x0c, 0x3b, 0x07, - 0x45, 0x05, 0x4d, 0x04, 0x53, 0x03, 0x57, 0x02, 0x5c, 0x02, 0x5e, 0x01, - 0x61, 0x01, 0x63, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x00, 0x69, 0x00, - 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x41, 0x20, 0x4d, 0x0b, - 0x58, 0x06, 0x5e, 0x03, 0x64, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6c, 0x01, - 0x6f, 0x01, 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, - 0x76, 0x00, 0x75, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x15, 0x4f, 0x0d, 0x56, 0x09, 0x59, 0x06, - 0x5f, 0x05, 0x62, 0x04, 0x65, 0x03, 0x67, 0x02, 0x69, 0x02, 0x6b, 0x02, - 0x6c, 0x02, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x70, 0x01, - 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x74, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x55, 0x0c, 0x2c, 0x1b, 0x1b, 0x27, 0x12, 0x32, 0x0d, 0x3a, 0x0b, - 0x41, 0x08, 0x47, 0x07, 0x4c, 0x05, 0x4f, 0x05, 0x53, 0x04, 0x56, 0x04, - 0x58, 0x03, 0x5a, 0x02, 0x5d, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x61, 0x01, - 0x63, 0x01, 0x64, 0x01, 0x41, 0x2a, 0x47, 0x16, 0x4f, 0x0d, 0x54, 0x09, - 0x5b, 0x06, 0x5f, 0x05, 0x62, 0x04, 0x64, 0x03, 0x67, 0x02, 0x69, 0x02, - 0x6b, 0x02, 0x6c, 0x02, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x70, 0x01, - 0x71, 0x01, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x1d, 0x4a, 0x14, 0x50, 0x0e, 0x54, 0x0b, 0x59, 0x08, 0x5c, 0x07, - 0x5f, 0x06, 0x60, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, 0x68, 0x02, - 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6c, 0x02, 0x6e, 0x02, 0x6f, 0x01, - 0x70, 0x01, 0x70, 0x01, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x60, 0x08, 0x3b, - 0x12, 0x28, 0x1d, 0x1d, 0x26, 0x16, 0x2e, 0x11, 0x34, 0x0e, 0x3a, 0x0c, - 0x3f, 0x0a, 0x44, 0x08, 0x47, 0x07, 0x4b, 0x06, 0x4d, 0x05, 0x51, 0x05, - 0x52, 0x04, 0x55, 0x04, 0x57, 0x04, 0x58, 0x04, 0x5a, 0x03, 0x5c, 0x02, - 0x42, 0x30, 0x45, 0x1d, 0x4b, 0x14, 0x4f, 0x0e, 0x55, 0x0b, 0x59, 0x08, - 0x5c, 0x07, 0x5e, 0x06, 0x61, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, - 0x68, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6b, 0x02, 0x6d, 0x02, 0x6e, 0x02, - 0x6f, 0x01, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x22, 0x48, 0x19, - 0x4d, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5c, 0x07, - 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, - 0x68, 0x03, 0x68, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6d, 0x02, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x66, 0x05, 0x45, 0x0d, 0x32, 0x16, 0x26, - 0x1e, 0x1e, 0x25, 0x18, 0x2b, 0x14, 0x31, 0x11, 0x36, 0x0f, 0x3a, 0x0c, - 0x3e, 0x0b, 0x42, 0x0a, 0x45, 0x09, 0x47, 0x07, 0x4a, 0x07, 0x4c, 0x06, - 0x4f, 0x05, 0x51, 0x05, 0x52, 0x05, 0x54, 0x04, 0x41, 0x33, 0x44, 0x22, - 0x48, 0x19, 0x4c, 0x13, 0x51, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x08, - 0x5d, 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, - 0x67, 0x03, 0x67, 0x03, 0x69, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x26, 0x47, 0x1d, 0x4a, 0x17, 0x4d, 0x12, - 0x51, 0x0f, 0x54, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5b, 0x08, 0x5d, 0x07, - 0x5f, 0x06, 0x60, 0x06, 0x62, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, - 0x67, 0x03, 0x67, 0x03, 0x68, 0x03, 0x69, 0x02, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x6a, 0x04, 0x4d, 0x0b, 0x3a, 0x11, 0x2e, 0x18, 0x25, 0x1f, 0x1f, - 0x24, 0x1a, 0x29, 0x16, 0x2e, 0x13, 0x33, 0x11, 0x37, 0x0f, 0x3a, 0x0d, - 0x3d, 0x0c, 0x40, 0x0a, 0x43, 0x0a, 0x45, 0x09, 0x48, 0x07, 0x4a, 0x07, - 0x4c, 0x07, 0x4e, 0x06, 0x41, 0x35, 0x43, 0x26, 0x47, 0x1d, 0x49, 0x17, - 0x4e, 0x12, 0x51, 0x0f, 0x54, 0x0d, 0x55, 0x0b, 0x59, 0x09, 0x5b, 0x08, - 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x62, 0x05, 0x63, 0x05, 0x63, 0x04, - 0x65, 0x03, 0x67, 0x03, 0x67, 0x03, 0x68, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x29, 0x45, 0x20, 0x49, 0x1a, 0x4b, 0x15, 0x4f, 0x12, 0x51, 0x0f, - 0x54, 0x0d, 0x55, 0x0b, 0x58, 0x0a, 0x5a, 0x09, 0x5b, 0x08, 0x5d, 0x07, - 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x64, 0x05, 0x65, 0x04, - 0x65, 0x03, 0x66, 0x03, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x6d, 0x03, 0x52, - 0x08, 0x41, 0x0e, 0x34, 0x14, 0x2b, 0x1a, 0x24, 0x1f, 0x1f, 0x24, 0x1b, - 0x28, 0x17, 0x2d, 0x15, 0x31, 0x12, 0x34, 0x10, 0x37, 0x0f, 0x3a, 0x0d, - 0x3d, 0x0c, 0x40, 0x0b, 0x42, 0x0a, 0x44, 0x0a, 0x46, 0x09, 0x47, 0x07, - 0x41, 0x36, 0x43, 0x29, 0x46, 0x20, 0x48, 0x1a, 0x4c, 0x15, 0x4f, 0x12, - 0x51, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x0a, 0x5a, 0x09, 0x5b, 0x08, - 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x60, 0x05, 0x62, 0x05, 0x64, 0x05, - 0x65, 0x04, 0x65, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2b, 0x45, 0x23, - 0x48, 0x1d, 0x49, 0x18, 0x4d, 0x14, 0x4f, 0x12, 0x51, 0x10, 0x53, 0x0e, - 0x55, 0x0c, 0x57, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5c, 0x08, 0x5d, 0x07, - 0x5f, 0x07, 0x5f, 0x06, 0x61, 0x06, 0x62, 0x05, 0x63, 0x05, 0x64, 0x05, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x6f, 0x02, 0x57, 0x07, 0x47, 0x0c, 0x3a, - 0x11, 0x31, 0x16, 0x29, 0x1b, 0x24, 0x20, 0x20, 0x23, 0x1c, 0x28, 0x19, - 0x2b, 0x16, 0x2f, 0x14, 0x32, 0x12, 0x35, 0x10, 0x38, 0x0f, 0x3a, 0x0e, - 0x3c, 0x0c, 0x3f, 0x0c, 0x41, 0x0b, 0x42, 0x0a, 0x41, 0x37, 0x43, 0x2b, - 0x45, 0x23, 0x47, 0x1d, 0x4a, 0x18, 0x4d, 0x14, 0x4f, 0x12, 0x51, 0x10, - 0x53, 0x0e, 0x55, 0x0c, 0x57, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5c, 0x08, - 0x5d, 0x07, 0x5e, 0x07, 0x60, 0x06, 0x61, 0x06, 0x62, 0x05, 0x63, 0x05, - 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, - 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x1f, 0x1f, 0x3f, 0x00, - 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, - 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x58, 0x00, 0x70, 0x00, 0x77, 0x00, 0x79, 0x00, 0x7b, 0x00, - 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x1f, 0x1f, 0x3f, 0x00, - 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, - 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x43, 0x2d, 0x44, 0x25, 0x46, 0x1f, 0x48, 0x1b, - 0x4b, 0x17, 0x4d, 0x14, 0x50, 0x12, 0x51, 0x10, 0x53, 0x0e, 0x55, 0x0c, - 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x08, 0x5c, 0x07, - 0x5f, 0x07, 0x60, 0x06, 0x61, 0x06, 0x62, 0x06, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x71, 0x02, 0x5b, 0x05, 0x4b, 0x0a, 0x3f, 0x0f, 0x36, 0x13, 0x2e, - 0x18, 0x29, 0x1c, 0x23, 0x20, 0x20, 0x23, 0x1d, 0x27, 0x19, 0x2a, 0x17, - 0x2d, 0x16, 0x30, 0x12, 0x33, 0x12, 0x35, 0x10, 0x38, 0x0f, 0x3b, 0x0e, - 0x3c, 0x0c, 0x3e, 0x0c, 0x41, 0x38, 0x43, 0x2d, 0x44, 0x25, 0x45, 0x1f, - 0x49, 0x1b, 0x4b, 0x17, 0x4d, 0x14, 0x4f, 0x11, 0x51, 0x10, 0x53, 0x0e, - 0x55, 0x0c, 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5b, 0x08, - 0x5e, 0x07, 0x5f, 0x07, 0x60, 0x06, 0x61, 0x06, 0x00, 0x00, 0x0a, 0x00, - 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, - 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, - 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, - 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, - 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x44, 0x00, - 0x55, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x75, 0x00, 0x78, 0x00, - 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, - 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, - 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, - 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, - 0x42, 0x2f, 0x44, 0x27, 0x46, 0x22, 0x47, 0x1d, 0x4a, 0x19, 0x4c, 0x16, - 0x4e, 0x13, 0x4f, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, - 0x58, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x07, 0x5e, 0x07, - 0x5f, 0x07, 0x5f, 0x06, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x73, 0x01, 0x5e, - 0x05, 0x4f, 0x08, 0x44, 0x0c, 0x3a, 0x10, 0x33, 0x15, 0x2d, 0x19, 0x28, - 0x1d, 0x23, 0x20, 0x20, 0x23, 0x1d, 0x26, 0x1a, 0x2a, 0x18, 0x2c, 0x16, - 0x2f, 0x14, 0x31, 0x12, 0x34, 0x12, 0x35, 0x0f, 0x38, 0x0f, 0x3b, 0x0f, - 0x41, 0x39, 0x42, 0x2f, 0x44, 0x27, 0x45, 0x22, 0x48, 0x1d, 0x4a, 0x19, - 0x4c, 0x16, 0x4d, 0x14, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, - 0x56, 0x0c, 0x58, 0x0b, 0x59, 0x0a, 0x59, 0x09, 0x5c, 0x09, 0x5c, 0x07, - 0x5e, 0x07, 0x5f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x2d, 0x00, - 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, - 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, - 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, - 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, - 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x55, 0x00, 0x20, 0x00, 0x3e, 0x00, - 0x50, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, - 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, - 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, - 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, - 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x42, 0x30, 0x44, 0x29, - 0x45, 0x23, 0x46, 0x1f, 0x49, 0x1b, 0x4b, 0x18, 0x4c, 0x15, 0x4d, 0x13, - 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0b, - 0x59, 0x0b, 0x59, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x5c, 0x07, 0x5e, 0x07, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x73, 0x01, 0x61, 0x04, 0x52, 0x07, 0x47, - 0x0b, 0x3e, 0x0f, 0x37, 0x12, 0x31, 0x16, 0x2b, 0x19, 0x27, 0x1d, 0x23, - 0x20, 0x20, 0x23, 0x1d, 0x26, 0x1b, 0x29, 0x19, 0x2b, 0x16, 0x2e, 0x16, - 0x30, 0x13, 0x32, 0x12, 0x35, 0x12, 0x36, 0x0f, 0x41, 0x39, 0x42, 0x30, - 0x44, 0x29, 0x44, 0x23, 0x47, 0x1f, 0x49, 0x1b, 0x4b, 0x18, 0x4c, 0x15, - 0x4e, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, - 0x57, 0x0b, 0x57, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x5c, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, - 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, - 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x72, 0x00, 0x5c, - 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, - 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, - 0x73, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x67, 0x00, 0x3e, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x3a, 0x00, - 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, - 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x72, 0x00, 0x5c, - 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, - 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, - 0x73, 0x00, 0x75, 0x00, 0x42, 0x32, 0x44, 0x2a, 0x45, 0x25, 0x46, 0x21, - 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x17, 0x4c, 0x15, 0x4e, 0x13, 0x50, 0x11, - 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x55, 0x0c, 0x57, 0x0c, 0x57, 0x0b, - 0x5a, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x74, 0x01, 0x64, 0x04, 0x55, 0x06, 0x4a, 0x0a, 0x42, 0x0d, 0x3a, - 0x10, 0x34, 0x14, 0x2f, 0x17, 0x2a, 0x1a, 0x26, 0x1d, 0x23, 0x21, 0x21, - 0x22, 0x1d, 0x26, 0x1b, 0x28, 0x19, 0x2b, 0x18, 0x2c, 0x16, 0x30, 0x15, - 0x30, 0x12, 0x33, 0x12, 0x41, 0x3a, 0x42, 0x32, 0x44, 0x2a, 0x44, 0x25, - 0x46, 0x21, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x17, 0x4d, 0x15, 0x4e, 0x13, - 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x55, 0x0c, 0x56, 0x0c, - 0x58, 0x0b, 0x5a, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, - 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, - 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, - 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, - 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x6e, 0x00, - 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, - 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, - 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, - 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, - 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, - 0x42, 0x32, 0x43, 0x2c, 0x44, 0x26, 0x45, 0x22, 0x48, 0x1f, 0x49, 0x1b, - 0x4b, 0x19, 0x4c, 0x16, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, - 0x53, 0x0e, 0x55, 0x0e, 0x55, 0x0c, 0x56, 0x0c, 0x57, 0x0b, 0x59, 0x0b, - 0x5a, 0x0a, 0x5a, 0x09, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x75, 0x01, 0x65, - 0x03, 0x58, 0x05, 0x4d, 0x09, 0x45, 0x0c, 0x3e, 0x0f, 0x37, 0x12, 0x32, - 0x16, 0x2d, 0x18, 0x2a, 0x1b, 0x26, 0x1d, 0x22, 0x21, 0x21, 0x22, 0x1d, - 0x26, 0x1c, 0x27, 0x19, 0x2b, 0x19, 0x2b, 0x16, 0x2e, 0x16, 0x30, 0x14, - 0x41, 0x3a, 0x42, 0x32, 0x43, 0x2c, 0x44, 0x26, 0x46, 0x22, 0x48, 0x1f, - 0x49, 0x1b, 0x4a, 0x19, 0x4c, 0x16, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x11, - 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0e, 0x54, 0x0c, 0x57, 0x0c, 0x57, 0x0b, - 0x59, 0x0b, 0x5a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, - 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, - 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, - 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, - 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x72, 0x00, 0x5a, 0x00, 0x3a, 0x00, - 0x1b, 0x00, 0x01, 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, - 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, - 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, - 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, - 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x42, 0x33, 0x43, 0x2d, - 0x44, 0x28, 0x45, 0x23, 0x47, 0x20, 0x48, 0x1d, 0x4a, 0x1a, 0x4a, 0x18, - 0x4c, 0x16, 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, - 0x55, 0x0e, 0x54, 0x0d, 0x57, 0x0c, 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x0b, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x76, 0x01, 0x67, 0x02, 0x5a, 0x05, 0x50, - 0x07, 0x47, 0x0a, 0x40, 0x0d, 0x3a, 0x10, 0x35, 0x12, 0x30, 0x16, 0x2c, - 0x19, 0x29, 0x1b, 0x26, 0x1d, 0x22, 0x21, 0x21, 0x22, 0x1d, 0x26, 0x1d, - 0x26, 0x1a, 0x2a, 0x19, 0x2b, 0x17, 0x2d, 0x16, 0x41, 0x3b, 0x42, 0x33, - 0x43, 0x2d, 0x44, 0x28, 0x45, 0x23, 0x47, 0x20, 0x48, 0x1d, 0x49, 0x1a, - 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, - 0x53, 0x0e, 0x54, 0x0e, 0x55, 0x0d, 0x57, 0x0c, 0x57, 0x0b, 0x58, 0x0b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, - 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x7c, 0x00, 0x75, - 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, - 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, - 0x57, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, - 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, - 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x7c, 0x00, 0x75, - 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, - 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, - 0x57, 0x00, 0x5b, 0x00, 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, 0x45, 0x25, - 0x46, 0x21, 0x48, 0x1e, 0x49, 0x1b, 0x4a, 0x19, 0x4c, 0x17, 0x4d, 0x15, - 0x4e, 0x14, 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x53, 0x0e, - 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, 0x57, 0x0b, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x77, 0x00, 0x68, 0x02, 0x5c, 0x05, 0x52, 0x07, 0x4a, 0x0a, 0x43, - 0x0c, 0x3d, 0x0f, 0x37, 0x12, 0x33, 0x14, 0x2f, 0x16, 0x2b, 0x19, 0x28, - 0x1c, 0x26, 0x1d, 0x22, 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1a, - 0x29, 0x19, 0x2b, 0x18, 0x41, 0x3b, 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, - 0x45, 0x25, 0x46, 0x21, 0x48, 0x1e, 0x48, 0x1b, 0x4b, 0x19, 0x4c, 0x17, - 0x4d, 0x15, 0x4e, 0x14, 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x51, 0x0f, - 0x54, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, - 0x4c, 0x00, 0x51, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, - 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, - 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, - 0x4c, 0x00, 0x51, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, - 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, - 0x42, 0x34, 0x43, 0x2f, 0x44, 0x2a, 0x44, 0x26, 0x46, 0x22, 0x47, 0x1f, - 0x48, 0x1d, 0x49, 0x1a, 0x4b, 0x18, 0x4c, 0x17, 0x4e, 0x15, 0x4e, 0x13, - 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x51, 0x0f, 0x54, 0x0e, 0x55, 0x0d, - 0x56, 0x0c, 0x57, 0x0c, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x77, 0x00, 0x69, - 0x02, 0x5e, 0x04, 0x55, 0x06, 0x4c, 0x09, 0x45, 0x0c, 0x3f, 0x0e, 0x3a, - 0x10, 0x35, 0x12, 0x31, 0x16, 0x2e, 0x18, 0x2b, 0x19, 0x27, 0x1d, 0x26, - 0x1e, 0x22, 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1b, 0x28, 0x19, - 0x41, 0x3b, 0x42, 0x34, 0x43, 0x2f, 0x43, 0x2a, 0x45, 0x26, 0x46, 0x22, - 0x47, 0x1f, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x18, 0x4c, 0x17, 0x4e, 0x15, - 0x4e, 0x13, 0x50, 0x13, 0x50, 0x11, 0x51, 0x11, 0x52, 0x0f, 0x54, 0x0e, - 0x55, 0x0d, 0x56, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, - 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, - 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, - 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, - 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, - 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, - 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, - 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, - 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, - 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x42, 0x35, 0x43, 0x30, - 0x44, 0x2b, 0x44, 0x27, 0x45, 0x24, 0x46, 0x21, 0x48, 0x1e, 0x48, 0x1c, - 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4e, 0x13, 0x50, 0x12, - 0x50, 0x11, 0x51, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, 0x55, 0x0c, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x77, 0x00, 0x6b, 0x02, 0x60, 0x04, 0x57, - 0x05, 0x4f, 0x07, 0x48, 0x0a, 0x42, 0x0c, 0x3c, 0x0f, 0x38, 0x12, 0x34, - 0x14, 0x30, 0x16, 0x2c, 0x19, 0x2b, 0x19, 0x26, 0x1d, 0x25, 0x1e, 0x22, - 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1c, 0x41, 0x3b, 0x42, 0x35, - 0x43, 0x30, 0x43, 0x2b, 0x44, 0x27, 0x45, 0x24, 0x46, 0x21, 0x47, 0x1e, - 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4e, 0x13, - 0x50, 0x12, 0x50, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, - 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x7d, 0x00, 0x7a, - 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, - 0x34, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, - 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, - 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x7d, 0x00, 0x7a, - 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, - 0x34, 0x00, 0x3b, 0x00, 0x42, 0x36, 0x43, 0x30, 0x44, 0x2c, 0x44, 0x28, - 0x45, 0x25, 0x46, 0x22, 0x48, 0x1f, 0x48, 0x1d, 0x49, 0x1a, 0x4b, 0x19, - 0x4c, 0x18, 0x4d, 0x15, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x12, 0x50, 0x11, - 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x77, 0x00, 0x6c, 0x02, 0x61, 0x04, 0x58, 0x05, 0x51, 0x07, 0x4a, - 0x0a, 0x44, 0x0c, 0x3f, 0x0f, 0x3a, 0x10, 0x35, 0x12, 0x32, 0x16, 0x30, - 0x16, 0x2b, 0x19, 0x2a, 0x1a, 0x26, 0x1d, 0x25, 0x1e, 0x22, 0x21, 0x22, - 0x21, 0x1e, 0x25, 0x1d, 0x41, 0x3b, 0x42, 0x36, 0x43, 0x30, 0x43, 0x2c, - 0x44, 0x28, 0x45, 0x25, 0x46, 0x22, 0x47, 0x1f, 0x49, 0x1d, 0x49, 0x1a, - 0x4b, 0x19, 0x4c, 0x18, 0x4d, 0x15, 0x4e, 0x15, 0x4f, 0x13, 0x4f, 0x12, - 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, - 0x29, 0x00, 0x30, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, - 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, - 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, - 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, - 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, - 0x29, 0x00, 0x30, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, - 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, - 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, - 0x42, 0x36, 0x42, 0x31, 0x43, 0x2d, 0x44, 0x29, 0x45, 0x26, 0x46, 0x23, - 0x47, 0x20, 0x47, 0x1e, 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x17, - 0x4d, 0x15, 0x4e, 0x14, 0x4f, 0x13, 0x4f, 0x12, 0x51, 0x11, 0x52, 0x11, - 0x52, 0x0f, 0x54, 0x0e, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x78, 0x00, 0x6d, - 0x01, 0x62, 0x03, 0x5a, 0x05, 0x52, 0x07, 0x4c, 0x09, 0x46, 0x0b, 0x41, - 0x0c, 0x3c, 0x0f, 0x38, 0x12, 0x35, 0x12, 0x30, 0x16, 0x2e, 0x17, 0x2b, - 0x19, 0x29, 0x1b, 0x26, 0x1d, 0x25, 0x1e, 0x22, 0x21, 0x22, 0x21, 0x1f, - 0x41, 0x3c, 0x42, 0x36, 0x42, 0x31, 0x43, 0x2d, 0x44, 0x29, 0x45, 0x26, - 0x46, 0x23, 0x46, 0x20, 0x48, 0x1e, 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, - 0x4c, 0x17, 0x4d, 0x15, 0x4e, 0x14, 0x4e, 0x13, 0x50, 0x12, 0x51, 0x11, - 0x52, 0x11, 0x52, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, - 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, - 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, - 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, - 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, - 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, - 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, - 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x42, 0x37, 0x42, 0x32, - 0x43, 0x2d, 0x44, 0x2a, 0x45, 0x27, 0x45, 0x23, 0x46, 0x21, 0x47, 0x1f, - 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, 0x4c, 0x16, 0x4e, 0x15, - 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x78, 0x00, 0x6e, 0x01, 0x64, 0x02, 0x5b, - 0x04, 0x54, 0x06, 0x4e, 0x07, 0x47, 0x0a, 0x42, 0x0c, 0x3e, 0x0f, 0x3b, - 0x0f, 0x35, 0x12, 0x33, 0x14, 0x30, 0x16, 0x2d, 0x19, 0x2b, 0x19, 0x28, - 0x1c, 0x26, 0x1d, 0x25, 0x1f, 0x22, 0x21, 0x22, 0x41, 0x3c, 0x42, 0x37, - 0x42, 0x32, 0x43, 0x2d, 0x44, 0x2a, 0x45, 0x27, 0x45, 0x23, 0x46, 0x21, - 0x48, 0x1f, 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, 0x4c, 0x16, - 0x4e, 0x15, 0x4d, 0x14, 0x50, 0x13, 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x7e, 0x00, 0x7c, - 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, - 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, - 0x13, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, - 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, - 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x7e, 0x00, 0x7c, - 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, - 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, - 0x13, 0x00, 0x1b, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x61, 0x20, 0x4d, 0x2a, - 0x48, 0x30, 0x44, 0x33, 0x44, 0x35, 0x44, 0x36, 0x43, 0x37, 0x42, 0x38, - 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3b, - 0x42, 0x3b, 0x41, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, - 0x04, 0x3d, 0x22, 0x3d, 0x2b, 0x3d, 0x30, 0x3d, 0x33, 0x3d, 0x35, 0x3d, - 0x37, 0x3d, 0x38, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, - 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, - 0x3c, 0x3d, 0x3c, 0x3d, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x12, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, - 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, - 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, - 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, - 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x12, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, - 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, - 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x6c, 0x0b, 0x57, 0x16, 0x4f, 0x1d, 0x4a, 0x22, - 0x48, 0x26, 0x47, 0x29, 0x46, 0x2b, 0x44, 0x2d, 0x44, 0x2f, 0x44, 0x30, - 0x44, 0x31, 0x44, 0x32, 0x43, 0x33, 0x43, 0x34, 0x43, 0x34, 0x42, 0x35, - 0x43, 0x36, 0x42, 0x36, 0x42, 0x37, 0x42, 0x37, 0x00, 0x5d, 0x0c, 0x49, - 0x16, 0x43, 0x1d, 0x41, 0x22, 0x40, 0x26, 0x3f, 0x29, 0x3f, 0x2c, 0x3f, - 0x2e, 0x3e, 0x2f, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, 0x33, 0x3e, - 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, - 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, - 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, - 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, - 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, - 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, - 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x72, 0x06, 0x5f, 0x0d, 0x56, 0x14, 0x4f, 0x19, 0x4d, 0x1d, 0x4a, 0x20, - 0x49, 0x23, 0x47, 0x25, 0x46, 0x27, 0x46, 0x29, 0x45, 0x2a, 0x45, 0x2c, - 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2f, 0x43, 0x30, 0x44, 0x30, 0x44, 0x31, - 0x43, 0x32, 0x43, 0x33, 0x00, 0x68, 0x06, 0x54, 0x0d, 0x4b, 0x14, 0x47, - 0x19, 0x44, 0x1d, 0x43, 0x20, 0x41, 0x23, 0x41, 0x26, 0x40, 0x27, 0x40, - 0x29, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x2f, 0x3f, - 0x30, 0x3f, 0x30, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, - 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, - 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, - 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, - 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, - 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, - 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, - 0x00, 0x08, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x75, 0x03, 0x64, 0x09, - 0x5a, 0x0e, 0x54, 0x13, 0x51, 0x17, 0x4e, 0x1a, 0x4c, 0x1d, 0x49, 0x1f, - 0x49, 0x22, 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x46, 0x28, 0x45, 0x29, - 0x45, 0x2a, 0x44, 0x2b, 0x44, 0x2c, 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2e, - 0x00, 0x6d, 0x04, 0x5b, 0x09, 0x51, 0x0e, 0x4c, 0x13, 0x48, 0x17, 0x46, - 0x1a, 0x44, 0x1d, 0x43, 0x1f, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, - 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, - 0x2d, 0x3f, 0x2e, 0x3f, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, - 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x3f, - 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, - 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, - 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x77, 0x02, 0x68, 0x06, 0x5f, 0x0b, 0x58, 0x0f, - 0x54, 0x12, 0x51, 0x15, 0x4f, 0x18, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1f, - 0x49, 0x21, 0x48, 0x22, 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x45, 0x27, - 0x45, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, 0x00, 0x70, 0x02, 0x60, - 0x07, 0x56, 0x0b, 0x50, 0x0f, 0x4c, 0x12, 0x49, 0x16, 0x47, 0x18, 0x46, - 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x43, 0x21, 0x42, 0x22, 0x42, 0x24, 0x41, - 0x25, 0x41, 0x26, 0x40, 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, - 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, - 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, - 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, - 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x78, 0x02, 0x6b, 0x05, 0x62, 0x08, 0x5b, 0x0c, 0x57, 0x0f, 0x54, 0x12, - 0x51, 0x14, 0x4e, 0x17, 0x4d, 0x19, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, - 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26, - 0x46, 0x27, 0x45, 0x27, 0x00, 0x72, 0x02, 0x64, 0x05, 0x5b, 0x08, 0x54, - 0x0c, 0x50, 0x0f, 0x4d, 0x12, 0x4a, 0x14, 0x48, 0x17, 0x47, 0x19, 0x46, - 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x44, 0x20, 0x42, 0x21, 0x42, 0x22, 0x42, - 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x40, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x3f, - 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, - 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, - 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, - 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, - 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x79, 0x01, 0x6d, 0x04, - 0x65, 0x07, 0x5e, 0x0a, 0x5a, 0x0d, 0x56, 0x0f, 0x54, 0x12, 0x51, 0x14, - 0x4f, 0x16, 0x4e, 0x18, 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, - 0x49, 0x1f, 0x48, 0x21, 0x48, 0x22, 0x48, 0x23, 0x47, 0x23, 0x46, 0x25, - 0x00, 0x74, 0x01, 0x66, 0x04, 0x5e, 0x07, 0x57, 0x0a, 0x53, 0x0d, 0x4f, - 0x10, 0x4d, 0x12, 0x4b, 0x14, 0x49, 0x16, 0x48, 0x18, 0x46, 0x1a, 0x45, - 0x1b, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x20, 0x43, 0x21, 0x42, 0x22, 0x42, - 0x23, 0x42, 0x24, 0x41, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x01, 0x01, - 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, - 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, - 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, - 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, - 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7a, 0x01, 0x6f, 0x03, 0x67, 0x06, 0x60, 0x08, - 0x5d, 0x0b, 0x59, 0x0d, 0x56, 0x10, 0x53, 0x11, 0x51, 0x14, 0x50, 0x15, - 0x4e, 0x17, 0x4d, 0x19, 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x48, 0x1e, - 0x49, 0x1f, 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, 0x00, 0x75, 0x01, 0x69, - 0x03, 0x61, 0x06, 0x5a, 0x08, 0x56, 0x0b, 0x52, 0x0d, 0x4f, 0x10, 0x4d, - 0x12, 0x4b, 0x14, 0x4a, 0x16, 0x48, 0x17, 0x47, 0x19, 0x46, 0x1a, 0x45, - 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x20, 0x43, 0x21, 0x42, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x1b, 0x00, - 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, - 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, - 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, - 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7b, 0x01, 0x71, 0x02, 0x69, 0x05, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0c, - 0x58, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4e, 0x16, - 0x4e, 0x18, 0x4c, 0x19, 0x4c, 0x1a, 0x4a, 0x1c, 0x4b, 0x1d, 0x49, 0x1e, - 0x49, 0x1f, 0x49, 0x20, 0x00, 0x76, 0x01, 0x6b, 0x03, 0x63, 0x05, 0x5d, - 0x07, 0x58, 0x09, 0x54, 0x0c, 0x52, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, - 0x13, 0x4a, 0x15, 0x49, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x45, - 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x71, - 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, - 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, - 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, - 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x00, 0x72, 0x02, - 0x6b, 0x04, 0x64, 0x06, 0x61, 0x08, 0x5d, 0x0a, 0x5a, 0x0c, 0x56, 0x0e, - 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4f, 0x16, 0x4e, 0x17, - 0x4d, 0x18, 0x4b, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, 0x4b, 0x1d, 0x49, 0x1d, - 0x00, 0x77, 0x00, 0x6c, 0x02, 0x65, 0x04, 0x5f, 0x06, 0x5a, 0x08, 0x57, - 0x0a, 0x54, 0x0c, 0x51, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, - 0x15, 0x49, 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x1a, 0x46, 0x1b, 0x45, - 0x1c, 0x45, 0x1d, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, - 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, - 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, - 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, - 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, - 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x73, 0x02, 0x6c, 0x03, 0x66, 0x05, - 0x63, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x10, - 0x53, 0x11, 0x52, 0x13, 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x17, 0x4c, 0x18, - 0x4c, 0x19, 0x4c, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, 0x00, 0x77, 0x00, 0x6e, - 0x02, 0x66, 0x04, 0x61, 0x05, 0x5c, 0x07, 0x59, 0x09, 0x56, 0x0b, 0x53, - 0x0d, 0x51, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, - 0x15, 0x48, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x46, 0x1b, 0x45, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, - 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, - 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, - 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7c, 0x00, 0x74, 0x02, 0x6e, 0x03, 0x68, 0x05, 0x64, 0x06, 0x60, 0x08, - 0x5d, 0x0a, 0x5a, 0x0b, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, - 0x52, 0x13, 0x50, 0x14, 0x50, 0x15, 0x4d, 0x16, 0x4e, 0x18, 0x4c, 0x18, - 0x4c, 0x19, 0x4c, 0x1a, 0x00, 0x77, 0x00, 0x6f, 0x02, 0x68, 0x03, 0x63, - 0x05, 0x5e, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x55, 0x0b, 0x53, 0x0d, 0x50, - 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x49, - 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x19, 0x46, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, - 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, - 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, - 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, - 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, - 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x75, 0x01, - 0x6f, 0x02, 0x69, 0x04, 0x65, 0x06, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0b, - 0x5a, 0x0c, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, - 0x50, 0x13, 0x4f, 0x15, 0x4e, 0x15, 0x4e, 0x17, 0x4d, 0x18, 0x4c, 0x18, - 0x00, 0x78, 0x00, 0x70, 0x01, 0x69, 0x03, 0x64, 0x04, 0x60, 0x06, 0x5c, - 0x07, 0x59, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x52, 0x0d, 0x50, 0x0f, 0x4f, - 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x48, - 0x17, 0x48, 0x18, 0x47, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, - 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, - 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, - 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, - 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, 0x70, 0x02, 0x6a, 0x03, - 0x67, 0x05, 0x63, 0x06, 0x60, 0x08, 0x5d, 0x09, 0x5b, 0x0b, 0x59, 0x0c, - 0x57, 0x0d, 0x55, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x4f, 0x13, - 0x50, 0x15, 0x4f, 0x15, 0x4e, 0x16, 0x4e, 0x18, 0x00, 0x78, 0x00, 0x71, - 0x01, 0x6a, 0x03, 0x66, 0x04, 0x61, 0x05, 0x5d, 0x06, 0x5a, 0x08, 0x58, - 0x09, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0e, 0x50, 0x0f, 0x4e, 0x10, 0x4e, - 0x11, 0x4c, 0x13, 0x4c, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x49, 0x16, 0x48, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, - 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, - 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, - 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7d, 0x00, 0x76, 0x01, 0x71, 0x02, 0x6c, 0x03, 0x68, 0x05, 0x64, 0x06, - 0x61, 0x07, 0x5e, 0x09, 0x5c, 0x0a, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0e, - 0x55, 0x0e, 0x55, 0x11, 0x52, 0x11, 0x51, 0x12, 0x51, 0x13, 0x50, 0x14, - 0x4f, 0x15, 0x4e, 0x15, 0x00, 0x79, 0x00, 0x71, 0x01, 0x6c, 0x02, 0x66, - 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5c, 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, - 0x0b, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, - 0x13, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x4a, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, - 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, - 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, - 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, - 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x77, 0x01, - 0x72, 0x02, 0x6d, 0x03, 0x69, 0x04, 0x66, 0x06, 0x63, 0x07, 0x5f, 0x08, - 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0c, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x0f, - 0x54, 0x11, 0x51, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x14, 0x50, 0x15, - 0x00, 0x79, 0x00, 0x72, 0x01, 0x6d, 0x02, 0x68, 0x03, 0x63, 0x04, 0x60, - 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x53, - 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, - 0x13, 0x4b, 0x14, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, - 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, - 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, - 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, - 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, - 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x78, 0x01, 0x72, 0x02, 0x6e, 0x02, - 0x6a, 0x03, 0x67, 0x05, 0x64, 0x06, 0x60, 0x07, 0x5f, 0x09, 0x5c, 0x0a, - 0x5b, 0x0b, 0x5a, 0x0c, 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0f, 0x53, 0x11, - 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x13, 0x00, 0x79, 0x00, 0x73, - 0x01, 0x6d, 0x02, 0x69, 0x03, 0x65, 0x04, 0x61, 0x05, 0x5e, 0x06, 0x5c, - 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0d, 0x53, 0x0d, 0x51, - 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, - 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, - 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, - 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, - 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7e, 0x00, 0x78, 0x01, 0x73, 0x02, 0x6e, 0x02, 0x6b, 0x03, 0x68, 0x05, - 0x65, 0x06, 0x62, 0x07, 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0b, - 0x59, 0x0c, 0x57, 0x0d, 0x56, 0x0e, 0x54, 0x0f, 0x54, 0x11, 0x52, 0x11, - 0x52, 0x12, 0x51, 0x13, 0x00, 0x79, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x69, - 0x03, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, - 0x09, 0x56, 0x0b, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, - 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x12, 0x4c, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, - 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, - 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, - 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, - 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x00, - 0x74, 0x01, 0x6f, 0x02, 0x6c, 0x03, 0x68, 0x04, 0x65, 0x05, 0x62, 0x06, - 0x61, 0x07, 0x5f, 0x09, 0x5c, 0x09, 0x5b, 0x0b, 0x5a, 0x0b, 0x58, 0x0c, - 0x57, 0x0d, 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, - 0x00, 0x79, 0x00, 0x74, 0x00, 0x6f, 0x02, 0x6a, 0x03, 0x66, 0x04, 0x63, - 0x05, 0x60, 0x05, 0x5e, 0x06, 0x5b, 0x07, 0x59, 0x09, 0x58, 0x09, 0x55, - 0x0b, 0x55, 0x0c, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, 0x0f, 0x4e, - 0x11, 0x4e, 0x11, 0x4d, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, - 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, - 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, - 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, - 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x79, 0x00, 0x74, 0x01, 0x70, 0x02, - 0x6c, 0x03, 0x69, 0x03, 0x66, 0x05, 0x63, 0x06, 0x62, 0x07, 0x5f, 0x07, - 0x5e, 0x09, 0x5c, 0x0a, 0x5a, 0x0b, 0x5a, 0x0c, 0x57, 0x0c, 0x56, 0x0e, - 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x00, 0x79, 0x00, 0x74, - 0x00, 0x70, 0x01, 0x6b, 0x02, 0x67, 0x03, 0x64, 0x04, 0x61, 0x05, 0x5f, - 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, - 0x0c, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x66, 0x7f, 0x4d, 0x68, 0x49, 0x5c, 0x47, 0x55, 0x45, 0x51, 0x45, 0x4e, - 0x44, 0x4c, 0x43, 0x4a, 0x43, 0x49, 0x43, 0x48, 0x43, 0x47, 0x42, 0x47, - 0x42, 0x46, 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, - 0x42, 0x44, 0x42, 0x44, 0x57, 0x7f, 0x2e, 0x58, 0x34, 0x4d, 0x37, 0x49, - 0x39, 0x47, 0x3a, 0x45, 0x3b, 0x44, 0x3b, 0x44, 0x3c, 0x43, 0x3c, 0x43, - 0x3c, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x41, - 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x99, 0x7f, 0x4d, 0x58, - 0x49, 0x4d, 0x47, 0x49, 0x45, 0x47, 0x45, 0x45, 0x44, 0x44, 0x43, 0x44, - 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa5, 0x16, 0x74, 0x23, - 0x62, 0x29, 0x59, 0x2d, 0x53, 0x30, 0x50, 0x32, 0x4e, 0x34, 0x4c, 0x35, - 0x4a, 0x36, 0x49, 0x37, 0x48, 0x38, 0x48, 0x38, 0x47, 0x39, 0x46, 0x39, - 0x46, 0x39, 0x46, 0x3a, 0x45, 0x3a, 0x45, 0x3a, 0x45, 0x3a, 0x44, 0x3b, - 0x23, 0x2f, 0x25, 0x35, 0x2a, 0x37, 0x2e, 0x39, 0x31, 0x3a, 0x33, 0x3b, - 0x34, 0x3b, 0x35, 0x3c, 0x37, 0x3c, 0x37, 0x3c, 0x38, 0x3d, 0x38, 0x3d, - 0x39, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, - 0x3b, 0x3d, 0x3b, 0x3d, 0x77, 0x27, 0x5f, 0x31, 0x56, 0x35, 0x52, 0x38, - 0x4f, 0x39, 0x4c, 0x3a, 0x4b, 0x3b, 0x49, 0x3b, 0x48, 0x3c, 0x48, 0x3c, - 0x47, 0x3c, 0x47, 0x3c, 0x46, 0x3d, 0x46, 0x3d, 0x45, 0x3d, 0x45, 0x3d, - 0x45, 0x3d, 0x44, 0x3d, 0x44, 0x3d, 0x44, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x5f, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xbb, 0x01, 0x91, 0x09, 0x78, 0x11, 0x6b, 0x17, - 0x62, 0x1c, 0x5c, 0x20, 0x58, 0x23, 0x55, 0x25, 0x52, 0x28, 0x51, 0x2a, - 0x4f, 0x2b, 0x4e, 0x2c, 0x4d, 0x2e, 0x4c, 0x2f, 0x4b, 0x30, 0x4a, 0x31, - 0x49, 0x31, 0x49, 0x32, 0x48, 0x33, 0x47, 0x33, 0x22, 0x25, 0x23, 0x2a, - 0x26, 0x2e, 0x2a, 0x31, 0x2c, 0x33, 0x2e, 0x34, 0x30, 0x36, 0x31, 0x37, - 0x32, 0x37, 0x33, 0x38, 0x34, 0x38, 0x35, 0x39, 0x36, 0x39, 0x36, 0x3a, - 0x37, 0x3a, 0x37, 0x3a, 0x38, 0x3a, 0x38, 0x3b, 0x39, 0x3b, 0x39, 0x3b, - 0x7a, 0x1a, 0x68, 0x24, 0x5f, 0x2a, 0x5a, 0x2e, 0x55, 0x31, 0x53, 0x33, - 0x51, 0x34, 0x4f, 0x35, 0x4d, 0x36, 0x4c, 0x37, 0x4b, 0x38, 0x4b, 0x38, - 0x4a, 0x39, 0x49, 0x39, 0x49, 0x39, 0x48, 0x3a, 0x47, 0x3a, 0x47, 0x3a, - 0x46, 0x3b, 0x46, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x39, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x8b, 0x00, 0x44, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xc6, 0x00, 0xa3, 0x02, 0x8a, 0x07, 0x7a, 0x0c, 0x6f, 0x11, 0x68, 0x15, - 0x62, 0x18, 0x5e, 0x1b, 0x5b, 0x1d, 0x58, 0x20, 0x56, 0x22, 0x54, 0x23, - 0x53, 0x25, 0x51, 0x26, 0x50, 0x28, 0x4e, 0x29, 0x4d, 0x2a, 0x4d, 0x2b, - 0x4d, 0x2c, 0x4c, 0x2c, 0x22, 0x23, 0x22, 0x27, 0x25, 0x2a, 0x27, 0x2c, - 0x2a, 0x2e, 0x2b, 0x30, 0x2d, 0x32, 0x2e, 0x33, 0x30, 0x34, 0x31, 0x34, - 0x32, 0x35, 0x33, 0x36, 0x33, 0x36, 0x34, 0x37, 0x34, 0x37, 0x35, 0x38, - 0x36, 0x38, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x7b, 0x16, 0x6d, 0x1f, - 0x65, 0x24, 0x5f, 0x28, 0x5b, 0x2b, 0x58, 0x2d, 0x55, 0x2f, 0x54, 0x31, - 0x52, 0x32, 0x50, 0x33, 0x4f, 0x34, 0x4e, 0x35, 0x4d, 0x35, 0x4c, 0x36, - 0x4b, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x4a, 0x38, 0x4a, 0x39, 0x49, 0x39, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, - 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb3, 0x00, 0x9f, 0x00, 0x6d, 0x00, 0x33, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xcc, 0x00, 0xaf, 0x00, - 0x98, 0x03, 0x87, 0x06, 0x7b, 0x0a, 0x73, 0x0e, 0x6c, 0x11, 0x67, 0x13, - 0x63, 0x16, 0x5f, 0x18, 0x5c, 0x1a, 0x5a, 0x1c, 0x58, 0x1e, 0x56, 0x20, - 0x55, 0x21, 0x54, 0x22, 0x52, 0x24, 0x51, 0x25, 0x50, 0x26, 0x4f, 0x27, - 0x22, 0x22, 0x22, 0x25, 0x24, 0x27, 0x26, 0x2a, 0x28, 0x2b, 0x29, 0x2d, - 0x2b, 0x2f, 0x2c, 0x30, 0x2d, 0x31, 0x2f, 0x32, 0x30, 0x33, 0x30, 0x33, - 0x31, 0x34, 0x32, 0x35, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, 0x34, 0x36, - 0x35, 0x36, 0x35, 0x37, 0x7c, 0x15, 0x71, 0x1b, 0x68, 0x20, 0x63, 0x24, - 0x5f, 0x27, 0x5c, 0x29, 0x59, 0x2c, 0x57, 0x2d, 0x55, 0x2f, 0x54, 0x30, - 0x53, 0x31, 0x51, 0x32, 0x50, 0x32, 0x4f, 0x33, 0x4f, 0x34, 0x4e, 0x35, - 0x4d, 0x35, 0x4c, 0x35, 0x4b, 0x35, 0x4a, 0x36, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1d, - 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, 0x00, - 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xcf, 0x00, 0xb7, 0x00, 0xa2, 0x00, 0x92, 0x03, - 0x85, 0x06, 0x7c, 0x09, 0x75, 0x0b, 0x6f, 0x0e, 0x6a, 0x11, 0x66, 0x13, - 0x63, 0x15, 0x5f, 0x17, 0x5e, 0x19, 0x5c, 0x1a, 0x5a, 0x1c, 0x57, 0x1d, - 0x56, 0x1f, 0x55, 0x1f, 0x55, 0x21, 0x54, 0x22, 0x22, 0x22, 0x22, 0x24, - 0x23, 0x26, 0x25, 0x28, 0x26, 0x29, 0x28, 0x2b, 0x29, 0x2c, 0x2b, 0x2d, - 0x2c, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x31, 0x30, 0x32, 0x30, 0x33, - 0x30, 0x33, 0x32, 0x33, 0x32, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, - 0x7c, 0x14, 0x73, 0x19, 0x6c, 0x1e, 0x67, 0x22, 0x63, 0x24, 0x5f, 0x26, - 0x5c, 0x28, 0x5a, 0x2a, 0x58, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2f, - 0x53, 0x30, 0x52, 0x31, 0x51, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4f, 0x33, - 0x4f, 0x34, 0x4e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, - 0x48, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xd2, 0x00, 0xbd, 0x00, 0xaa, 0x00, 0x9b, 0x01, 0x8e, 0x03, 0x84, 0x05, - 0x7c, 0x08, 0x76, 0x0a, 0x71, 0x0c, 0x6c, 0x0f, 0x69, 0x11, 0x66, 0x12, - 0x63, 0x14, 0x60, 0x16, 0x5e, 0x17, 0x5d, 0x19, 0x5b, 0x1a, 0x59, 0x1b, - 0x57, 0x1c, 0x56, 0x1e, 0x22, 0x22, 0x22, 0x23, 0x23, 0x25, 0x24, 0x27, - 0x25, 0x28, 0x27, 0x29, 0x28, 0x2b, 0x29, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, - 0x2d, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x30, 0x30, 0x32, 0x30, 0x33, - 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x7d, 0x13, 0x75, 0x18, - 0x6e, 0x1c, 0x69, 0x1f, 0x65, 0x22, 0x62, 0x24, 0x60, 0x26, 0x5c, 0x28, - 0x5b, 0x29, 0x59, 0x2a, 0x57, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2e, - 0x53, 0x2f, 0x53, 0x31, 0x51, 0x31, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x32, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, - 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd3, 0x00, 0xc2, 0x00, - 0xb1, 0x00, 0xa2, 0x00, 0x96, 0x02, 0x8c, 0x03, 0x83, 0x05, 0x7d, 0x07, - 0x77, 0x09, 0x73, 0x0b, 0x6f, 0x0d, 0x6a, 0x0f, 0x68, 0x11, 0x66, 0x12, - 0x63, 0x14, 0x60, 0x15, 0x5f, 0x16, 0x5e, 0x18, 0x5c, 0x19, 0x5a, 0x1a, - 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, 0x26, 0x29, - 0x27, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2e, - 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x30, 0x31, 0x30, 0x32, - 0x30, 0x33, 0x30, 0x33, 0x7d, 0x13, 0x76, 0x17, 0x70, 0x1b, 0x6b, 0x1e, - 0x68, 0x20, 0x65, 0x22, 0x61, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x29, - 0x5a, 0x2a, 0x58, 0x2b, 0x57, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2e, - 0x53, 0x2f, 0x53, 0x30, 0x53, 0x31, 0x51, 0x31, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, - 0x00, 0x25, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, 0x00, - 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xd4, 0x00, 0xc5, 0x00, 0xb6, 0x00, 0xa8, 0x00, - 0x9c, 0x00, 0x92, 0x02, 0x8a, 0x03, 0x82, 0x05, 0x7d, 0x07, 0x78, 0x09, - 0x74, 0x0a, 0x71, 0x0c, 0x6c, 0x0e, 0x69, 0x0f, 0x68, 0x11, 0x65, 0x12, - 0x63, 0x13, 0x60, 0x14, 0x5f, 0x16, 0x5e, 0x17, 0x21, 0x22, 0x22, 0x23, - 0x22, 0x24, 0x23, 0x25, 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, 0x27, 0x29, - 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2e, - 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x31, - 0x7d, 0x12, 0x77, 0x16, 0x71, 0x19, 0x6d, 0x1c, 0x69, 0x1f, 0x66, 0x20, - 0x64, 0x23, 0x61, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x28, 0x5b, 0x2a, - 0x58, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x54, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, - 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, - 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xd5, 0x00, 0xc7, 0x00, 0xba, 0x00, 0xad, 0x00, 0xa2, 0x00, 0x98, 0x01, - 0x8f, 0x02, 0x88, 0x03, 0x82, 0x05, 0x7e, 0x07, 0x78, 0x08, 0x74, 0x0a, - 0x72, 0x0b, 0x6e, 0x0c, 0x6b, 0x0e, 0x69, 0x0f, 0x67, 0x11, 0x65, 0x12, - 0x63, 0x13, 0x60, 0x14, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, - 0x24, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, 0x27, 0x29, 0x28, 0x2b, - 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, 0x2a, 0x2d, 0x2c, 0x2d, 0x2d, 0x2e, - 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x7d, 0x12, 0x77, 0x15, - 0x72, 0x18, 0x6e, 0x1b, 0x6b, 0x1d, 0x68, 0x20, 0x65, 0x21, 0x64, 0x23, - 0x60, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x59, 0x2a, - 0x57, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x54, 0x2e, 0x53, 0x2e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, - 0x00, 0x3a, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, - 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, - 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd6, 0x00, 0xc9, 0x00, - 0xbd, 0x00, 0xb2, 0x00, 0xa7, 0x00, 0x9d, 0x00, 0x95, 0x01, 0x8e, 0x02, - 0x87, 0x03, 0x82, 0x05, 0x7e, 0x06, 0x79, 0x08, 0x75, 0x09, 0x73, 0x0b, - 0x70, 0x0c, 0x6c, 0x0d, 0x6a, 0x0e, 0x68, 0x0f, 0x67, 0x11, 0x65, 0x12, - 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x26, - 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, - 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, - 0x2d, 0x2f, 0x2d, 0x30, 0x7d, 0x12, 0x78, 0x15, 0x73, 0x18, 0x70, 0x1a, - 0x6d, 0x1d, 0x69, 0x1e, 0x67, 0x20, 0x65, 0x21, 0x63, 0x23, 0x60, 0x24, - 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x5a, 0x2a, 0x58, 0x2a, - 0x57, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, - 0x00, 0x30, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, - 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, - 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, - 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xd7, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb5, 0x00, - 0xab, 0x00, 0xa2, 0x00, 0x9a, 0x00, 0x92, 0x02, 0x8d, 0x02, 0x87, 0x03, - 0x81, 0x05, 0x7e, 0x06, 0x79, 0x07, 0x76, 0x09, 0x74, 0x0a, 0x71, 0x0b, - 0x6e, 0x0c, 0x6b, 0x0d, 0x6a, 0x0e, 0x68, 0x10, 0x21, 0x22, 0x22, 0x22, - 0x22, 0x23, 0x23, 0x24, 0x23, 0x25, 0x24, 0x25, 0x25, 0x27, 0x25, 0x27, - 0x27, 0x28, 0x27, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, - 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, - 0x7e, 0x12, 0x79, 0x15, 0x75, 0x17, 0x71, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, - 0x69, 0x1f, 0x65, 0x20, 0x65, 0x22, 0x62, 0x23, 0x60, 0x23, 0x60, 0x25, - 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x5c, 0x29, 0x59, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, - 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, - 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xd7, 0x00, 0xcc, 0x00, 0xc2, 0x00, 0xb9, 0x00, 0xae, 0x00, 0xa6, 0x00, - 0x9e, 0x00, 0x97, 0x01, 0x90, 0x02, 0x8b, 0x02, 0x85, 0x04, 0x81, 0x05, - 0x7e, 0x06, 0x7a, 0x07, 0x76, 0x08, 0x74, 0x09, 0x72, 0x0b, 0x6f, 0x0c, - 0x6c, 0x0c, 0x6a, 0x0e, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, - 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, - 0x26, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x7e, 0x12, 0x79, 0x15, - 0x75, 0x17, 0x72, 0x19, 0x6e, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x68, 0x20, - 0x65, 0x20, 0x65, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, - 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x5a, 0x2a, 0x58, 0x2a, 0x57, 0x2a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3c, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, - 0x00, 0x1c, 0x00, 0x16, 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, - 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xce, 0x00, - 0xc5, 0x00, 0xbb, 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa1, 0x00, 0x9b, 0x00, - 0x94, 0x01, 0x8f, 0x02, 0x8a, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, - 0x7b, 0x07, 0x77, 0x08, 0x75, 0x09, 0x73, 0x0a, 0x71, 0x0b, 0x6e, 0x0c, - 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, - 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, - 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, - 0x2b, 0x2d, 0x2b, 0x2d, 0x7e, 0x12, 0x7a, 0x14, 0x76, 0x16, 0x72, 0x18, - 0x6f, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, - 0x64, 0x23, 0x61, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, - 0x5c, 0x27, 0x5c, 0x28, 0x5b, 0x2a, 0x59, 0x2a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, - 0x00, 0x35, 0x00, 0x31, 0x00, 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, - 0x00, 0x14, 0x00, 0x0f, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, - 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, - 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbd, 0x00, - 0xb5, 0x00, 0xad, 0x00, 0xa6, 0x00, 0x9e, 0x00, 0x99, 0x00, 0x92, 0x01, - 0x8e, 0x02, 0x89, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, 0x7b, 0x07, - 0x77, 0x07, 0x75, 0x09, 0x73, 0x09, 0x72, 0x0b, 0x21, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, - 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, - 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, - 0x7e, 0x12, 0x7a, 0x14, 0x76, 0x15, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, - 0x6c, 0x1d, 0x69, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x65, 0x21, 0x64, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0x5c, 0x27, 0x5c, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, - 0x00, 0x2d, 0x00, 0x28, 0x00, 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, - 0x00, 0x0d, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, - 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, - 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbf, 0x00, 0xb8, 0x00, 0xaf, 0x00, - 0xa9, 0x00, 0xa1, 0x00, 0x9c, 0x00, 0x96, 0x01, 0x90, 0x02, 0x8d, 0x02, - 0x88, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7b, 0x07, 0x77, 0x07, - 0x76, 0x09, 0x74, 0x09, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, - 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, - 0x26, 0x27, 0x27, 0x27, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, - 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x7e, 0x11, 0x7a, 0x13, - 0x76, 0x15, 0x74, 0x18, 0x72, 0x19, 0x6e, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, - 0x69, 0x1e, 0x67, 0x20, 0x65, 0x20, 0x65, 0x21, 0x63, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x3d, - 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, - 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x58, 0x00, 0x3d, - 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x42, 0x00, 0x3e, 0x00, 0x3e, - 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, - 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x0b, 0x07, 0x17, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3a, 0x00, 0x3c, - 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, - 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x47, 0x07, 0x0f, 0x0f, - 0x00, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, - 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, - 0x00, 0x3f, 0x00, 0x3f, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd1, 0x00, - 0xc9, 0x00, 0xc1, 0x00, 0xba, 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, - 0x9e, 0x00, 0x9a, 0x00, 0x93, 0x01, 0x8f, 0x02, 0x8c, 0x02, 0x88, 0x03, - 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7b, 0x06, 0x78, 0x07, 0x76, 0x08, - 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, - 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, - 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, - 0x2a, 0x2b, 0x2b, 0x2b, 0x7e, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x75, 0x17, - 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x69, 0x1f, - 0x65, 0x20, 0x65, 0x20, 0x65, 0x22, 0x62, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x25, 0x5f, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, - 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, - 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x30, 0x00, 0x39, 0x00, 0x42, - 0x00, 0x41, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, - 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x27, 0x02, - 0x02, 0x12, 0x00, 0x25, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, - 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, - 0x00, 0x3e, 0x00, 0x3e, 0x7f, 0x00, 0x3f, 0x00, 0x05, 0x05, 0x00, 0x1c, - 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3b, - 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc3, 0x00, - 0xbb, 0x00, 0xb5, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x9d, 0x00, - 0x98, 0x00, 0x92, 0x01, 0x8e, 0x02, 0x8b, 0x02, 0x87, 0x03, 0x83, 0x04, - 0x81, 0x05, 0x7f, 0x06, 0x7c, 0x06, 0x78, 0x07, 0x21, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, - 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x27, 0x28, - 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, - 0x7e, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x76, 0x17, 0x72, 0x18, 0x71, 0x1a, - 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x68, 0x20, 0x65, 0x20, - 0x65, 0x20, 0x65, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, - 0x5f, 0x27, 0x5c, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, - 0x00, 0x24, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, - 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, - 0x00, 0x4c, 0x00, 0x39, 0x00, 0x16, 0x00, 0x28, 0x00, 0x2f, 0x00, 0x2e, - 0x00, 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, - 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x6f, 0x00, 0x22, 0x00, 0x09, 0x0b, - 0x00, 0x16, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x35, - 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, - 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x00, 0x00, 0x09, 0x00, 0x19, - 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, - 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xbd, 0x00, 0xb7, 0x00, - 0xb0, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9f, 0x00, 0x9b, 0x00, 0x95, 0x01, - 0x91, 0x01, 0x8e, 0x02, 0x8a, 0x02, 0x86, 0x03, 0x83, 0x04, 0x81, 0x05, - 0x7f, 0x06, 0x7c, 0x06, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, - 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, - 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x7f, 0x11, 0x7b, 0x13, - 0x77, 0x15, 0x76, 0x16, 0x72, 0x18, 0x72, 0x19, 0x6d, 0x1a, 0x6d, 0x1b, - 0x6b, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, 0x65, 0x20, - 0x64, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x24, 0x5f, 0x26, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1d, - 0x00, 0x25, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, - 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x42, - 0x00, 0x28, 0x00, 0x09, 0x00, 0x16, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, - 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, - 0x00, 0x39, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xaf, 0x00, 0x93, 0x00, 0x58, 0x00, 0x21, 0x00, 0x0e, 0x08, 0x02, 0x0e, - 0x00, 0x18, 0x00, 0x20, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x31, - 0x00, 0x33, 0x00, 0x35, 0x00, 0x36, 0x00, 0x38, 0xb2, 0x00, 0x9c, 0x00, - 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x16, - 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, - 0x00, 0x33, 0x00, 0x35, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xda, 0x00, 0xd2, 0x00, - 0xcb, 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xac, 0x00, - 0xa8, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x01, 0x90, 0x02, - 0x8d, 0x02, 0x89, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, - 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, - 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, - 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x29, 0x27, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x29, 0x7f, 0x11, 0x7b, 0x13, 0x78, 0x15, 0x76, 0x15, - 0x73, 0x18, 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, - 0x69, 0x1d, 0x69, 0x1f, 0x65, 0x20, 0x65, 0x20, 0x65, 0x21, 0x64, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x24, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, - 0x00, 0x25, 0x00, 0x2a, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, - 0x00, 0x35, 0x00, 0x37, 0x00, 0x48, 0x00, 0x41, 0x00, 0x2f, 0x00, 0x16, - 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, - 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x00, 0xa4, 0x00, - 0x7a, 0x00, 0x4a, 0x00, 0x20, 0x00, 0x12, 0x06, 0x07, 0x0c, 0x00, 0x10, - 0x00, 0x18, 0x00, 0x1f, 0x00, 0x24, 0x00, 0x28, 0x00, 0x2c, 0x00, 0x2e, - 0x00, 0x30, 0x00, 0x32, 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, - 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, - 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, - 0x33, 0x22, 0x28, 0x22, 0x25, 0x22, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, - 0x22, 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, - 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, - 0x22, 0x21, 0x22, 0x21, 0x9d, 0x05, 0x6b, 0x0d, 0x51, 0x12, 0x45, 0x15, - 0x3d, 0x17, 0x38, 0x18, 0x35, 0x1a, 0x32, 0x1b, 0x30, 0x1b, 0x2f, 0x1c, - 0x2d, 0x1c, 0x2d, 0x1c, 0x2c, 0x1d, 0x2b, 0x1d, 0x2a, 0x1e, 0x2a, 0x1e, - 0x29, 0x1e, 0x29, 0x1e, 0x28, 0x1e, 0x28, 0x1e, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x5f, 0x13, 0x46, 0x17, 0x39, 0x1a, 0x33, 0x1b, 0x2f, 0x1c, 0x2d, 0x1d, - 0x2b, 0x1e, 0x2a, 0x1e, 0x29, 0x1e, 0x28, 0x1f, 0x27, 0x1f, 0x27, 0x1f, - 0x27, 0x1f, 0x26, 0x1f, 0x26, 0x20, 0x26, 0x20, 0x25, 0x20, 0x25, 0x20, - 0x25, 0x20, 0x25, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, - 0x00, 0x25, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, - 0x00, 0x42, 0x00, 0x3d, 0x00, 0x2e, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, - 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xb9, 0x00, 0xad, 0x00, 0x8f, 0x00, 0x68, 0x00, - 0x42, 0x00, 0x20, 0x00, 0x14, 0x05, 0x0b, 0x0a, 0x04, 0x0d, 0x00, 0x12, - 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2c, - 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, - 0x29, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x13, - 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, 0x37, 0x28, 0x2d, 0x25, - 0x28, 0x23, 0x26, 0x23, 0x25, 0x23, 0x24, 0x22, 0x24, 0x22, 0x23, 0x22, - 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0xbc, 0x00, 0x8e, 0x01, 0x73, 0x04, 0x60, 0x08, 0x55, 0x0b, 0x4c, 0x0d, - 0x47, 0x0f, 0x42, 0x11, 0x3e, 0x12, 0x3b, 0x13, 0x39, 0x14, 0x37, 0x15, - 0x36, 0x16, 0x34, 0x16, 0x33, 0x17, 0x32, 0x18, 0x31, 0x18, 0x30, 0x19, - 0x2f, 0x19, 0x2f, 0x19, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x6f, 0x11, 0x58, 0x11, - 0x4a, 0x13, 0x41, 0x15, 0x3b, 0x16, 0x37, 0x17, 0x34, 0x18, 0x32, 0x19, - 0x30, 0x1a, 0x2e, 0x1a, 0x2d, 0x1b, 0x2c, 0x1b, 0x2c, 0x1c, 0x2b, 0x1c, - 0x2a, 0x1c, 0x2a, 0x1d, 0x29, 0x1d, 0x29, 0x1d, 0x28, 0x1d, 0x28, 0x1d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, - 0x00, 0x26, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2d, 0x00, 0x3e, 0x00, 0x3a, - 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, - 0x00, 0x2b, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xba, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x7d, 0x00, 0x5c, 0x00, 0x3c, 0x00, - 0x20, 0x00, 0x16, 0x04, 0x0e, 0x08, 0x07, 0x0c, 0x02, 0x0e, 0x00, 0x13, - 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0xbb, 0x00, 0xb5, 0x00, - 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, 0x00, - 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x12, - 0x00, 0x17, 0x00, 0x1c, 0x38, 0x2c, 0x30, 0x28, 0x2b, 0x26, 0x29, 0x25, - 0x27, 0x24, 0x26, 0x24, 0x25, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x22, - 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xc6, 0x00, 0xa4, 0x00, - 0x89, 0x00, 0x77, 0x02, 0x67, 0x04, 0x5e, 0x06, 0x55, 0x08, 0x50, 0x0a, - 0x4b, 0x0c, 0x47, 0x0d, 0x44, 0x0e, 0x41, 0x0f, 0x3e, 0x10, 0x3d, 0x11, - 0x3b, 0x12, 0x39, 0x12, 0x38, 0x13, 0x37, 0x14, 0x35, 0x15, 0x35, 0x16, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x74, 0x11, 0x63, 0x11, 0x55, 0x11, 0x4c, 0x12, - 0x44, 0x13, 0x40, 0x14, 0x3b, 0x15, 0x39, 0x16, 0x36, 0x17, 0x34, 0x17, - 0x33, 0x18, 0x31, 0x18, 0x30, 0x19, 0x2f, 0x19, 0x2e, 0x1a, 0x2d, 0x1a, - 0x2d, 0x1a, 0x2c, 0x1b, 0x2b, 0x1b, 0x2b, 0x1c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, - 0x00, 0x26, 0x00, 0x28, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, - 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, - 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb5, 0x00, - 0xa4, 0x00, 0x8b, 0x00, 0x6f, 0x00, 0x52, 0x00, 0x37, 0x00, 0x20, 0x00, - 0x17, 0x04, 0x10, 0x07, 0x0a, 0x0a, 0x05, 0x0d, 0x00, 0x0f, 0x00, 0x14, - 0x00, 0x19, 0x00, 0x1d, 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, - 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x20, 0x00, - 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, - 0x3a, 0x2f, 0x32, 0x2b, 0x2e, 0x28, 0x2b, 0x27, 0x28, 0x25, 0x27, 0x25, - 0x27, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, - 0x24, 0x23, 0x23, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, - 0x23, 0x22, 0x23, 0x22, 0xcd, 0x00, 0xb0, 0x00, 0x9a, 0x00, 0x87, 0x00, - 0x78, 0x01, 0x6c, 0x03, 0x64, 0x04, 0x5c, 0x05, 0x57, 0x07, 0x51, 0x09, - 0x4e, 0x0a, 0x4a, 0x0b, 0x48, 0x0c, 0x45, 0x0c, 0x43, 0x0e, 0x40, 0x0f, - 0x3f, 0x0f, 0x3d, 0x0f, 0x3c, 0x11, 0x3b, 0x12, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x77, 0x11, 0x69, 0x11, 0x5e, 0x11, 0x54, 0x11, 0x4d, 0x11, 0x47, 0x12, - 0x43, 0x13, 0x3f, 0x13, 0x3c, 0x14, 0x39, 0x15, 0x38, 0x16, 0x36, 0x16, - 0x35, 0x17, 0x33, 0x17, 0x32, 0x18, 0x31, 0x18, 0x30, 0x18, 0x2f, 0x18, - 0x2f, 0x19, 0x2e, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x00, 0x0d, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, - 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, - 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, - 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xaa, 0x00, 0x96, 0x00, - 0x7e, 0x00, 0x64, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x20, 0x00, 0x18, 0x03, - 0x11, 0x06, 0x0c, 0x09, 0x07, 0x0c, 0x03, 0x0e, 0x00, 0x10, 0x00, 0x15, - 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, - 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, - 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3b, 0x32, 0x34, 0x2d, - 0x2f, 0x2a, 0x2c, 0x28, 0x2a, 0x27, 0x29, 0x26, 0x27, 0x25, 0x27, 0x25, - 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x23, 0x24, 0x23, - 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x22, - 0xcf, 0x00, 0xb9, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x85, 0x00, 0x7a, 0x01, - 0x70, 0x02, 0x68, 0x03, 0x61, 0x04, 0x5c, 0x05, 0x57, 0x07, 0x53, 0x07, - 0x50, 0x09, 0x4c, 0x0a, 0x4a, 0x0a, 0x48, 0x0c, 0x45, 0x0c, 0x44, 0x0c, - 0x42, 0x0d, 0x41, 0x0f, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x78, 0x11, 0x6d, 0x11, - 0x63, 0x11, 0x5b, 0x11, 0x53, 0x11, 0x4e, 0x11, 0x49, 0x12, 0x45, 0x12, - 0x41, 0x13, 0x3f, 0x13, 0x3c, 0x14, 0x3a, 0x14, 0x39, 0x15, 0x37, 0x16, - 0x36, 0x16, 0x35, 0x17, 0x33, 0x17, 0x33, 0x17, 0x32, 0x17, 0x31, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, - 0x00, 0x11, 0x00, 0x16, 0x00, 0x1a, 0x00, 0x1d, 0x00, 0x3e, 0x00, 0x3d, - 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, - 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, - 0x00, 0x1a, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9d, 0x00, 0x89, 0x00, 0x73, 0x00, - 0x5c, 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, 0x00, 0x19, 0x03, 0x13, 0x06, - 0x0e, 0x08, 0x09, 0x0b, 0x05, 0x0d, 0x01, 0x0e, 0xbd, 0x00, 0xba, 0x00, - 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, 0x00, - 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, - 0x0b, 0x00, 0x03, 0x00, 0x3c, 0x34, 0x35, 0x2f, 0x31, 0x2c, 0x2e, 0x2a, - 0x2c, 0x28, 0x2a, 0x27, 0x28, 0x27, 0x28, 0x26, 0x27, 0x25, 0x27, 0x25, - 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x23, - 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0xd2, 0x00, 0xbe, 0x00, - 0xad, 0x00, 0x9d, 0x00, 0x90, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, 0x01, - 0x6b, 0x02, 0x65, 0x04, 0x60, 0x04, 0x5b, 0x05, 0x58, 0x06, 0x54, 0x07, - 0x51, 0x07, 0x4f, 0x09, 0x4c, 0x0a, 0x4a, 0x0a, 0x48, 0x0b, 0x46, 0x0c, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x7a, 0x11, 0x70, 0x11, 0x67, 0x11, 0x5f, 0x11, - 0x59, 0x11, 0x53, 0x11, 0x4e, 0x11, 0x4a, 0x11, 0x46, 0x12, 0x43, 0x13, - 0x41, 0x13, 0x3e, 0x13, 0x3d, 0x14, 0x3b, 0x14, 0x39, 0x14, 0x38, 0x15, - 0x37, 0x16, 0x36, 0x16, 0x35, 0x16, 0x34, 0x17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, - 0x00, 0x14, 0x00, 0x18, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, - 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, - 0xb1, 0x00, 0xa3, 0x00, 0x92, 0x00, 0x7e, 0x00, 0x6a, 0x00, 0x56, 0x00, - 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x19, 0x03, 0x14, 0x05, 0x0f, 0x08, - 0x0b, 0x0a, 0x07, 0x0c, 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, - 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4d, 0x00, - 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, 0x00, - 0x3c, 0x35, 0x36, 0x31, 0x32, 0x2e, 0x2f, 0x2b, 0x2d, 0x2a, 0x2b, 0x28, - 0x2a, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, - 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x23, 0x24, 0x23, 0xd3, 0x00, 0xc2, 0x00, 0xb2, 0x00, 0xa4, 0x00, - 0x98, 0x00, 0x8d, 0x00, 0x83, 0x00, 0x7b, 0x00, 0x73, 0x01, 0x6e, 0x02, - 0x67, 0x02, 0x63, 0x04, 0x5f, 0x04, 0x5b, 0x05, 0x58, 0x05, 0x55, 0x07, - 0x52, 0x07, 0x50, 0x07, 0x4e, 0x09, 0x4c, 0x0a, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x7a, 0x11, 0x72, 0x11, 0x6a, 0x11, 0x63, 0x11, 0x5d, 0x11, 0x57, 0x11, - 0x52, 0x11, 0x4e, 0x11, 0x4a, 0x11, 0x48, 0x12, 0x44, 0x12, 0x42, 0x13, - 0x40, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x14, 0x3a, 0x14, 0x39, 0x14, - 0x38, 0x15, 0x37, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, - 0x00, 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, - 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa8, 0x00, - 0x99, 0x00, 0x87, 0x00, 0x75, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, - 0x2e, 0x00, 0x1f, 0x00, 0x1a, 0x02, 0x15, 0x05, 0x10, 0x07, 0x0c, 0x09, - 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, - 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, - 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, 0x00, 0x3c, 0x36, 0x37, 0x32, - 0x33, 0x2f, 0x30, 0x2d, 0x2e, 0x2b, 0x2d, 0x2a, 0x2b, 0x28, 0x2a, 0x28, - 0x28, 0x27, 0x28, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, - 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, - 0xd5, 0x00, 0xc5, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x9f, 0x00, 0x95, 0x00, - 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, 0x01, 0x6f, 0x01, 0x69, 0x02, - 0x66, 0x02, 0x61, 0x04, 0x5e, 0x04, 0x5a, 0x05, 0x58, 0x05, 0x55, 0x06, - 0x53, 0x07, 0x51, 0x07, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7b, 0x11, 0x73, 0x11, - 0x6c, 0x11, 0x66, 0x11, 0x60, 0x11, 0x5b, 0x11, 0x56, 0x11, 0x52, 0x11, - 0x4f, 0x11, 0x4b, 0x11, 0x48, 0x11, 0x45, 0x12, 0x44, 0x12, 0x41, 0x13, - 0x40, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x14, 0x3a, 0x14, 0x39, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, - 0x00, 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x09, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x8f, 0x00, - 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2d, 0x00, - 0x1f, 0x00, 0x1a, 0x02, 0x16, 0x04, 0x11, 0x06, 0xbe, 0x00, 0xbc, 0x00, - 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, 0x00, - 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, - 0x2c, 0x00, 0x23, 0x00, 0x3c, 0x37, 0x38, 0x33, 0x34, 0x30, 0x31, 0x2e, - 0x2f, 0x2c, 0x2d, 0x2a, 0x2c, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x27, - 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0xd5, 0x00, 0xc8, 0x00, - 0xbb, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x92, 0x00, 0x8a, 0x00, - 0x82, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x70, 0x01, 0x6c, 0x02, 0x67, 0x02, - 0x64, 0x03, 0x60, 0x04, 0x5d, 0x04, 0x5b, 0x05, 0x58, 0x05, 0x55, 0x05, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x7b, 0x11, 0x75, 0x11, 0x6e, 0x11, 0x68, 0x11, - 0x63, 0x11, 0x5e, 0x11, 0x5a, 0x11, 0x56, 0x11, 0x52, 0x11, 0x4f, 0x11, - 0x4c, 0x11, 0x49, 0x11, 0x47, 0x12, 0x44, 0x12, 0x43, 0x12, 0x41, 0x13, - 0x3f, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x09, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, - 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, - 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, - 0xb7, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x86, 0x00, 0x77, 0x00, - 0x67, 0x00, 0x57, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2c, 0x00, 0x1f, 0x00, - 0x1b, 0x02, 0x16, 0x04, 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, - 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6c, 0x00, - 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, 0x00, - 0x3d, 0x38, 0x38, 0x34, 0x35, 0x31, 0x33, 0x2f, 0x30, 0x2d, 0x2e, 0x2c, - 0x2d, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x27, 0x28, 0x27, - 0x28, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x24, 0xd6, 0x00, 0xca, 0x00, 0xbe, 0x00, 0xb3, 0x00, - 0xaa, 0x00, 0xa0, 0x00, 0x98, 0x00, 0x90, 0x00, 0x88, 0x00, 0x82, 0x00, - 0x7d, 0x00, 0x77, 0x00, 0x71, 0x01, 0x6d, 0x01, 0x69, 0x02, 0x66, 0x02, - 0x63, 0x04, 0x60, 0x04, 0x5d, 0x04, 0x5b, 0x05, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x7c, 0x11, 0x76, 0x11, 0x70, 0x11, 0x6a, 0x11, 0x66, 0x11, 0x61, 0x11, - 0x5d, 0x11, 0x59, 0x11, 0x55, 0x11, 0x52, 0x11, 0x4f, 0x11, 0x4c, 0x11, - 0x49, 0x11, 0x47, 0x11, 0x45, 0x12, 0x44, 0x12, 0x42, 0x13, 0x41, 0x13, - 0x3f, 0x13, 0x3e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, - 0x00, 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, - 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xb0, 0x00, - 0xa6, 0x00, 0x9a, 0x00, 0x8d, 0x00, 0x7f, 0x00, 0x70, 0x00, 0x61, 0x00, - 0x53, 0x00, 0x45, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x1b, 0x02, - 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, - 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, - 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x3d, 0x38, 0x39, 0x35, - 0x36, 0x32, 0x33, 0x30, 0x31, 0x2e, 0x30, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, - 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, - 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, - 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb7, 0x00, 0xad, 0x00, 0xa5, 0x00, - 0x9d, 0x00, 0x96, 0x00, 0x8f, 0x00, 0x88, 0x00, 0x81, 0x00, 0x7d, 0x00, - 0x77, 0x00, 0x73, 0x01, 0x6f, 0x01, 0x6b, 0x02, 0x67, 0x02, 0x64, 0x02, - 0x62, 0x04, 0x5f, 0x04, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x77, 0x11, - 0x71, 0x11, 0x6c, 0x11, 0x67, 0x11, 0x63, 0x11, 0x5f, 0x11, 0x5c, 0x11, - 0x58, 0x11, 0x55, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4c, 0x11, 0x4a, 0x11, - 0x48, 0x11, 0x46, 0x12, 0x44, 0x12, 0x43, 0x12, 0x42, 0x13, 0x40, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, - 0x00, 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xa9, 0x00, 0x9e, 0x00, - 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x5c, 0x00, 0x4f, 0x00, - 0x42, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0xbe, 0x00, 0xbd, 0x00, - 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, 0x00, - 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, - 0x48, 0x00, 0x3f, 0x00, 0x3d, 0x39, 0x39, 0x36, 0x36, 0x33, 0x33, 0x30, - 0x32, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x29, - 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0xd7, 0x00, 0xcc, 0x00, - 0xc3, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x99, 0x00, - 0x93, 0x00, 0x8d, 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, - 0x74, 0x00, 0x70, 0x01, 0x6c, 0x01, 0x69, 0x02, 0x66, 0x02, 0x63, 0x02, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x77, 0x11, 0x72, 0x11, 0x6e, 0x11, - 0x69, 0x11, 0x65, 0x11, 0x61, 0x11, 0x5d, 0x11, 0x5a, 0x11, 0x57, 0x11, - 0x54, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, - 0x47, 0x11, 0x45, 0x12, 0x44, 0x12, 0x42, 0x12, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x39, 0x39, 0x36, 0x36, 0x33, 0x34, 0x32, 0x33, 0x30, 0x30, 0x2e, - 0x30, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, - 0x29, 0x28, 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x25, 0xd7, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbc, 0x00, - 0xb4, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x91, 0x00, - 0x8c, 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x75, 0x00, - 0x71, 0x01, 0x6d, 0x01, 0x6b, 0x02, 0x68, 0x02, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x7c, 0x11, 0x78, 0x11, 0x73, 0x11, 0x6f, 0x11, 0x6b, 0x11, 0x67, 0x11, - 0x63, 0x11, 0x60, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x57, 0x11, 0x54, 0x11, - 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, - 0x46, 0x12, 0x45, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x3a, 0x36, - 0x37, 0x34, 0x35, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2d, 0x2e, 0x2d, - 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, - 0x28, 0x28, 0x28, 0x27, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, - 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb6, 0x00, 0xaf, 0x00, - 0xa8, 0x00, 0xa1, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, - 0x86, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x75, 0x00, 0x72, 0x01, - 0x6f, 0x01, 0x6b, 0x01, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x78, 0x11, - 0x74, 0x11, 0x70, 0x11, 0x6c, 0x11, 0x68, 0x11, 0x65, 0x11, 0x61, 0x11, - 0x5f, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x56, 0x11, 0x54, 0x11, 0x51, 0x11, - 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x35, 0x36, 0x33, - 0x33, 0x31, 0x32, 0x30, 0x30, 0x2e, 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, - 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, - 0x28, 0x27, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0xd8, 0x00, 0xd0, 0x00, - 0xc8, 0x00, 0xc0, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa5, 0x00, - 0x9f, 0x00, 0x99, 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8a, 0x00, 0x86, 0x00, - 0x81, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x76, 0x00, 0x73, 0x00, 0x70, 0x01, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, 0x75, 0x11, 0x71, 0x11, - 0x6d, 0x11, 0x6a, 0x11, 0x66, 0x11, 0x63, 0x11, 0x60, 0x11, 0x5d, 0x11, - 0x5b, 0x11, 0x58, 0x11, 0x56, 0x11, 0x54, 0x11, 0x51, 0x11, 0x4f, 0x11, - 0x4d, 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x49, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x36, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, - 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2c, 0x2b, - 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x27, 0x29, 0x27, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc2, 0x00, - 0xbb, 0x00, 0xb4, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9c, 0x00, - 0x97, 0x00, 0x92, 0x00, 0x8e, 0x00, 0x89, 0x00, 0x85, 0x00, 0x81, 0x00, - 0x7d, 0x00, 0x7a, 0x00, 0x76, 0x00, 0x73, 0x00, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x7d, 0x11, 0x79, 0x11, 0x75, 0x11, 0x72, 0x11, 0x6e, 0x11, 0x6b, 0x11, - 0x68, 0x11, 0x65, 0x11, 0x62, 0x11, 0x5f, 0x11, 0x5c, 0x11, 0x5a, 0x11, - 0x58, 0x11, 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, - 0x4c, 0x11, 0x4a, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3b, 0x38, - 0x39, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x30, 0x32, 0x30, 0x30, 0x2e, - 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, - 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbc, 0x00, 0xb6, 0x00, - 0xb0, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9a, 0x00, 0x96, 0x00, - 0x91, 0x00, 0x8d, 0x00, 0x89, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, - 0x7a, 0x00, 0x77, 0x00, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, - 0x76, 0x11, 0x72, 0x11, 0x6f, 0x11, 0x6c, 0x11, 0x69, 0x11, 0x66, 0x11, - 0x63, 0x11, 0x61, 0x11, 0x5e, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x57, 0x11, - 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3b, 0x39, 0x39, 0x36, 0x36, 0x34, - 0x35, 0x33, 0x33, 0x31, 0x33, 0x30, 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, - 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd2, 0x00, - 0xcb, 0x00, 0xc4, 0x00, 0xbe, 0x00, 0xb8, 0x00, 0xb2, 0x00, 0xad, 0x00, - 0xa8, 0x00, 0xa2, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x94, 0x00, 0x90, 0x00, - 0x8b, 0x00, 0x88, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7b, 0x00, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, 0x76, 0x11, 0x73, 0x11, - 0x70, 0x11, 0x6d, 0x11, 0x6a, 0x11, 0x67, 0x11, 0x65, 0x11, 0x62, 0x11, - 0x5f, 0x11, 0x5d, 0x11, 0x5b, 0x11, 0x59, 0x11, 0x56, 0x11, 0x55, 0x11, - 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x3b, 0x3b, 0x39, 0x3a, 0x36, 0x36, 0x35, 0x36, 0x33, 0x33, 0x32, - 0x33, 0x30, 0x31, 0x30, 0x30, 0x2f, 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x2b, 0x28, - 0x28, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd3, 0x00, 0xcc, 0x00, 0xc6, 0x00, - 0xc0, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa5, 0x00, - 0xa0, 0x00, 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x00, 0x8b, 0x00, - 0x88, 0x00, 0x83, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x7d, 0x11, 0x7a, 0x11, 0x77, 0x11, 0x74, 0x11, 0x71, 0x11, 0x6e, 0x11, - 0x6b, 0x11, 0x68, 0x11, 0x66, 0x11, 0x63, 0x11, 0x61, 0x11, 0x5e, 0x11, - 0x5c, 0x11, 0x5a, 0x11, 0x58, 0x11, 0x56, 0x11, 0x55, 0x11, 0x52, 0x11, - 0x51, 0x11, 0x4f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3b, 0x3b, 0x39, - 0x39, 0x36, 0x37, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x31, 0x32, 0x30, - 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, - 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc7, 0x00, 0xc1, 0x00, 0xbc, 0x00, - 0xb7, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa2, 0x00, 0x9e, 0x00, - 0x9a, 0x00, 0x95, 0x00, 0x93, 0x00, 0x8e, 0x00, 0x8b, 0x00, 0x87, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, - 0x77, 0x11, 0x74, 0x11, 0x71, 0x11, 0x6f, 0x11, 0x6c, 0x11, 0x69, 0x11, - 0x67, 0x11, 0x64, 0x11, 0x62, 0x11, 0x60, 0x11, 0x5e, 0x11, 0x5b, 0x11, - 0x5a, 0x11, 0x58, 0x11, 0x56, 0x11, 0x54, 0x11, 0x52, 0x11, 0x51, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0xb8, 0x00, 0xc5, 0x00, 0xcb, 0x00, - 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, - 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, 0x00, - 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x44, 0x00, 0x9d, 0x00, 0xbc, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xcf, 0x00, - 0xd2, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, - 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, - 0xd9, 0x00, 0xd9, 0x00, 0x90, 0x00, 0xbd, 0x00, 0xcc, 0x00, 0xd1, 0x00, - 0xd5, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, - 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, - 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x68, 0x01, 0x8b, 0x00, 0xa0, 0x00, 0xad, 0x00, 0xb6, 0x00, 0xbc, 0x00, - 0xc1, 0x00, 0xc4, 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, - 0xcd, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd2, 0x00, - 0xd2, 0x00, 0xd3, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x25, 0x05, 0x6b, 0x00, - 0x8e, 0x00, 0xa4, 0x00, 0xb0, 0x00, 0xb9, 0x00, 0xbe, 0x00, 0xc2, 0x00, - 0xc5, 0x00, 0xc8, 0x00, 0xca, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xce, 0x00, - 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, - 0x81, 0x02, 0xa4, 0x00, 0xb5, 0x00, 0xc0, 0x00, 0xc6, 0x00, 0xcb, 0x00, - 0xcd, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, - 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, - 0xd7, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x09, 0x72, 0x02, - 0x87, 0x00, 0x96, 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb0, 0x00, 0xb5, 0x00, - 0xb9, 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, - 0xc7, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x23, 0x0d, 0x51, 0x01, 0x73, 0x00, 0x89, 0x00, - 0x9a, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbb, 0x00, - 0xbe, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc8, 0x00, - 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0x80, 0x06, 0x97, 0x00, - 0xa8, 0x00, 0xb3, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc7, 0x00, - 0xca, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd1, 0x00, - 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x11, 0x65, 0x07, 0x76, 0x03, 0x84, 0x01, - 0x8f, 0x00, 0x99, 0x00, 0xa1, 0x00, 0xa7, 0x00, 0xad, 0x00, 0xb1, 0x00, - 0xb4, 0x00, 0xb8, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc0, 0x00, - 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x22, 0x12, 0x45, 0x04, 0x60, 0x00, 0x77, 0x00, 0x87, 0x00, 0x94, 0x00, - 0x9d, 0x00, 0xa4, 0x00, 0xaa, 0x00, 0xaf, 0x00, 0xb3, 0x00, 0xb7, 0x00, - 0xba, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc3, 0x00, - 0xc4, 0x00, 0xc6, 0x00, 0x7f, 0x09, 0x91, 0x02, 0x9e, 0x00, 0xaa, 0x00, - 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc6, 0x00, - 0xc8, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, - 0xcf, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x4b, 0x17, 0x5d, 0x0c, 0x6c, 0x06, 0x78, 0x03, 0x83, 0x01, 0x8c, 0x00, - 0x94, 0x00, 0x9b, 0x00, 0xa1, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xae, 0x00, - 0xb1, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, 0x00, - 0xbe, 0x00, 0xbf, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x15, 0x3d, 0x08, - 0x55, 0x02, 0x67, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x98, 0x00, - 0x9f, 0x00, 0xa5, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xb1, 0x00, 0xb4, 0x00, - 0xb6, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, - 0x7f, 0x0a, 0x8d, 0x04, 0x99, 0x01, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, - 0xb6, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc3, 0x00, 0xc5, 0x00, - 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcc, 0x00, - 0xcd, 0x00, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x1c, 0x57, 0x11, - 0x64, 0x0a, 0x70, 0x06, 0x79, 0x03, 0x82, 0x02, 0x8a, 0x00, 0x91, 0x00, - 0x97, 0x00, 0x9c, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xad, 0x00, - 0xaf, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xba, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x17, 0x38, 0x0b, 0x4c, 0x04, 0x5e, 0x01, - 0x6c, 0x00, 0x7a, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x95, 0x00, 0x9b, 0x00, - 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, - 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0x7f, 0x0b, 0x8a, 0x05, - 0x94, 0x02, 0x9d, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb5, 0x00, - 0xb9, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, - 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x20, 0x54, 0x15, 0x5f, 0x0e, 0x69, 0x09, - 0x72, 0x05, 0x7a, 0x03, 0x82, 0x02, 0x88, 0x01, 0x8f, 0x00, 0x94, 0x00, - 0x99, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa8, 0x00, 0xac, 0x00, - 0xae, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x22, 0x18, 0x35, 0x0d, 0x47, 0x06, 0x55, 0x03, 0x64, 0x01, 0x70, 0x00, - 0x7a, 0x00, 0x83, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, - 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, - 0xb2, 0x00, 0xb4, 0x00, 0x7f, 0x0c, 0x89, 0x06, 0x92, 0x03, 0x99, 0x01, - 0xa0, 0x00, 0xa6, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb4, 0x00, 0xb7, 0x00, - 0xba, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, - 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x23, 0x50, 0x18, 0x5b, 0x11, 0x64, 0x0b, 0x6c, 0x08, 0x74, 0x05, - 0x7b, 0x03, 0x82, 0x02, 0x87, 0x01, 0x8c, 0x00, 0x91, 0x00, 0x95, 0x00, - 0x9a, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xab, 0x00, - 0xac, 0x00, 0xaf, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1a, 0x32, 0x0f, - 0x42, 0x08, 0x50, 0x04, 0x5c, 0x02, 0x68, 0x00, 0x72, 0x00, 0x7b, 0x00, - 0x83, 0x00, 0x8a, 0x00, 0x90, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9e, 0x00, - 0xa1, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, - 0x7f, 0x0d, 0x88, 0x07, 0x8f, 0x04, 0x96, 0x02, 0x9c, 0x01, 0xa2, 0x00, - 0xa7, 0x00, 0xac, 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb9, 0x00, - 0xbb, 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, - 0xc5, 0x00, 0xc6, 0x00, 0x2f, 0x00, 0x5f, 0x00, 0x99, 0x00, 0xac, 0x00, - 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, - 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, - 0x3b, 0x0b, 0x6f, 0x00, 0x9f, 0x00, 0xaf, 0x00, 0xb5, 0x00, 0xb9, 0x00, - 0xba, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, - 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x6d, 0x00, 0x8d, 0x00, 0x9b, 0x00, - 0xa6, 0x00, 0xb2, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, - 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, - 0x47, 0x07, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, - 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, - 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x45, 0x25, 0x4e, 0x1b, - 0x57, 0x13, 0x60, 0x0e, 0x68, 0x0a, 0x6f, 0x07, 0x76, 0x05, 0x7b, 0x03, - 0x82, 0x02, 0x86, 0x02, 0x8b, 0x01, 0x90, 0x00, 0x93, 0x00, 0x97, 0x00, - 0x9b, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1b, 0x30, 0x11, 0x3e, 0x0a, 0x4b, 0x05, - 0x57, 0x03, 0x61, 0x01, 0x6b, 0x00, 0x73, 0x00, 0x7c, 0x00, 0x82, 0x00, - 0x88, 0x00, 0x8f, 0x00, 0x93, 0x00, 0x97, 0x00, 0x9c, 0x00, 0x9f, 0x00, - 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0x7f, 0x0d, 0x86, 0x08, - 0x8d, 0x05, 0x94, 0x02, 0x9a, 0x01, 0x9f, 0x00, 0xa4, 0x00, 0xa8, 0x00, - 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, - 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, - 0x00, 0x00, 0x0f, 0x00, 0x5f, 0x00, 0x8b, 0x00, 0x9f, 0x00, 0xaa, 0x00, - 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, - 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x07, 0x17, 0x27, 0x02, - 0x6f, 0x00, 0x93, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb5, 0x00, - 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, - 0xbc, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x6d, 0x00, 0x54, 0x00, 0x6b, 0x00, 0x87, 0x00, 0x98, 0x00, 0xa6, 0x00, - 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, - 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x0f, 0x0f, 0x3f, 0x00, - 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, 0x00, - 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, - 0xbd, 0x00, 0xbd, 0x00, 0x45, 0x28, 0x4d, 0x1d, 0x55, 0x16, 0x5d, 0x11, - 0x64, 0x0c, 0x6b, 0x09, 0x71, 0x07, 0x77, 0x05, 0x7b, 0x03, 0x81, 0x02, - 0x85, 0x02, 0x8a, 0x01, 0x8e, 0x00, 0x92, 0x00, 0x95, 0x00, 0x98, 0x00, - 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x21, 0x1b, 0x2f, 0x12, 0x3b, 0x0c, 0x47, 0x07, 0x51, 0x04, 0x5c, 0x02, - 0x65, 0x01, 0x6e, 0x00, 0x75, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, - 0x8d, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9c, 0x00, 0xa0, 0x00, - 0xa2, 0x00, 0xa5, 0x00, 0x7f, 0x0d, 0x86, 0x09, 0x8c, 0x06, 0x92, 0x03, - 0x97, 0x02, 0x9c, 0x01, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xac, 0x00, - 0xaf, 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, - 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x44, 0x00, 0x6d, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, - 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, - 0xb6, 0x00, 0xb7, 0x00, 0x00, 0x2c, 0x02, 0x12, 0x22, 0x00, 0x58, 0x00, - 0x7a, 0x00, 0x8f, 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xaa, 0x00, 0xae, 0x00, - 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x6b, 0x00, - 0x28, 0x00, 0x52, 0x00, 0x70, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, - 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, - 0xb6, 0x00, 0xb7, 0x00, 0x00, 0x26, 0x05, 0x05, 0x3f, 0x00, 0x6d, 0x00, - 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, - 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, - 0x44, 0x2a, 0x4b, 0x20, 0x53, 0x18, 0x5a, 0x13, 0x61, 0x0f, 0x67, 0x0b, - 0x6d, 0x09, 0x73, 0x07, 0x77, 0x05, 0x7c, 0x03, 0x81, 0x02, 0x85, 0x02, - 0x88, 0x01, 0x8d, 0x01, 0x90, 0x00, 0x93, 0x00, 0x96, 0x00, 0x9a, 0x00, - 0x9d, 0x00, 0x9f, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1c, 0x2d, 0x13, - 0x39, 0x0d, 0x44, 0x09, 0x4e, 0x05, 0x57, 0x04, 0x60, 0x02, 0x67, 0x01, - 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8c, 0x00, - 0x90, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0xa0, 0x00, - 0x7f, 0x0e, 0x85, 0x09, 0x8b, 0x06, 0x90, 0x04, 0x95, 0x02, 0x9a, 0x02, - 0x9e, 0x01, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, - 0xb2, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, - 0xbd, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x33, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, - 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, - 0x00, 0x36, 0x00, 0x25, 0x09, 0x0b, 0x21, 0x00, 0x4a, 0x00, 0x68, 0x00, - 0x7d, 0x00, 0x8b, 0x00, 0x96, 0x00, 0x9d, 0x00, 0xa3, 0x00, 0xa8, 0x00, - 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x87, 0x00, 0x52, 0x00, 0x11, 0x00, - 0x39, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, - 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, - 0x00, 0x33, 0x00, 0x1c, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, - 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, - 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0x43, 0x2b, 0x4a, 0x22, - 0x51, 0x1a, 0x57, 0x15, 0x5e, 0x11, 0x63, 0x0d, 0x69, 0x0a, 0x6e, 0x08, - 0x74, 0x06, 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, - 0x8c, 0x01, 0x8f, 0x00, 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9b, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1c, 0x2d, 0x14, 0x37, 0x0e, 0x41, 0x0a, - 0x4a, 0x07, 0x53, 0x04, 0x5b, 0x02, 0x63, 0x01, 0x69, 0x00, 0x70, 0x00, - 0x77, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8a, 0x00, 0x8f, 0x00, - 0x92, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x7f, 0x0e, 0x85, 0x0a, - 0x8a, 0x07, 0x8f, 0x05, 0x93, 0x03, 0x98, 0x02, 0x9c, 0x01, 0xa0, 0x00, - 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, 0x00, - 0xb3, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x28, 0x00, - 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, - 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x3a, 0x00, 0x2f, - 0x00, 0x16, 0x0e, 0x08, 0x20, 0x00, 0x42, 0x00, 0x5c, 0x00, 0x6f, 0x00, - 0x7e, 0x00, 0x89, 0x00, 0x92, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa2, 0x00, - 0xa6, 0x00, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xa6, 0x00, 0x98, 0x00, 0x70, 0x00, 0x39, 0x00, 0x02, 0x00, 0x28, 0x00, - 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, - 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x38, 0x00, 0x2a, - 0x00, 0x09, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, 0x00, - 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, - 0xab, 0x00, 0xad, 0x00, 0x43, 0x2c, 0x49, 0x23, 0x50, 0x1c, 0x56, 0x17, - 0x5b, 0x12, 0x61, 0x0f, 0x66, 0x0c, 0x6b, 0x0a, 0x6f, 0x08, 0x75, 0x06, - 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x8b, 0x01, - 0x8e, 0x01, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x22, 0x1c, 0x2c, 0x15, 0x36, 0x0f, 0x3e, 0x0b, 0x48, 0x07, 0x50, 0x05, - 0x58, 0x04, 0x5f, 0x02, 0x66, 0x01, 0x6c, 0x00, 0x71, 0x00, 0x77, 0x00, - 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, - 0x94, 0x00, 0x97, 0x00, 0x7f, 0x0e, 0x84, 0x0a, 0x89, 0x07, 0x8d, 0x05, - 0x92, 0x03, 0x96, 0x02, 0x9a, 0x02, 0x9e, 0x01, 0xa1, 0x00, 0xa4, 0x00, - 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, - 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x3d, 0x00, - 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, - 0x93, 0x00, 0x98, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x02, 0x0e, - 0x12, 0x06, 0x20, 0x00, 0x3c, 0x00, 0x52, 0x00, 0x64, 0x00, 0x73, 0x00, - 0x7e, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x00, 0xa6, 0x00, - 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, - 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, - 0x93, 0x00, 0x98, 0x00, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x05, 0x00, - 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, 0x00, - 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, - 0x43, 0x2e, 0x48, 0x25, 0x4e, 0x1e, 0x54, 0x19, 0x59, 0x14, 0x5f, 0x11, - 0x63, 0x0e, 0x69, 0x0b, 0x6c, 0x09, 0x71, 0x07, 0x76, 0x06, 0x78, 0x05, - 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x86, 0x02, 0x8a, 0x01, 0x8e, 0x01, - 0x90, 0x00, 0x92, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1d, 0x2b, 0x16, - 0x34, 0x10, 0x3d, 0x0c, 0x45, 0x09, 0x4c, 0x06, 0x54, 0x04, 0x5b, 0x02, - 0x61, 0x02, 0x67, 0x01, 0x6d, 0x00, 0x73, 0x00, 0x78, 0x00, 0x7d, 0x00, - 0x81, 0x00, 0x86, 0x00, 0x89, 0x00, 0x8d, 0x00, 0x90, 0x00, 0x93, 0x00, - 0x7f, 0x0e, 0x84, 0x0b, 0x88, 0x08, 0x8d, 0x06, 0x91, 0x04, 0x94, 0x03, - 0x98, 0x02, 0x9c, 0x01, 0x9f, 0x01, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, - 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, - 0xb6, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, - 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, - 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x18, 0x07, 0x0c, 0x14, 0x05, - 0x20, 0x00, 0x37, 0x00, 0x4c, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x75, 0x00, - 0x7e, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, - 0x48, 0x00, 0x21, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, - 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, - 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x0f, 0x00, 0x29, 0x00, - 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, - 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, 0x00, 0x42, 0x2f, 0x48, 0x26, - 0x4d, 0x20, 0x52, 0x1a, 0x58, 0x16, 0x5c, 0x12, 0x61, 0x0f, 0x66, 0x0c, - 0x6b, 0x0b, 0x6d, 0x09, 0x73, 0x07, 0x76, 0x06, 0x79, 0x05, 0x7d, 0x04, - 0x81, 0x03, 0x83, 0x02, 0x86, 0x02, 0x89, 0x02, 0x8d, 0x01, 0x8f, 0x01, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1d, 0x2a, 0x16, 0x33, 0x11, 0x3b, 0x0c, - 0x43, 0x0a, 0x4a, 0x07, 0x51, 0x05, 0x58, 0x04, 0x5e, 0x02, 0x64, 0x01, - 0x69, 0x01, 0x6f, 0x00, 0x74, 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, - 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, 0x8f, 0x00, 0x7f, 0x0e, 0x83, 0x0b, - 0x88, 0x08, 0x8c, 0x06, 0x90, 0x05, 0x93, 0x03, 0x97, 0x02, 0x9a, 0x02, - 0x9d, 0x01, 0xa0, 0x00, 0xa3, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, - 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, - 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x3d, 0x00, 0x3a, - 0x00, 0x2f, 0x00, 0x20, 0x00, 0x10, 0x0b, 0x0a, 0x16, 0x04, 0x20, 0x00, - 0x34, 0x00, 0x46, 0x00, 0x56, 0x00, 0x62, 0x00, 0x6d, 0x00, 0x77, 0x00, - 0x7f, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, - 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x3d, 0x00, 0x38, - 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, 0x00, - 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, - 0x8b, 0x00, 0x91, 0x00, 0x42, 0x30, 0x47, 0x28, 0x4c, 0x21, 0x51, 0x1c, - 0x56, 0x17, 0x5a, 0x14, 0x60, 0x11, 0x63, 0x0e, 0x68, 0x0c, 0x6c, 0x0a, - 0x6f, 0x08, 0x74, 0x07, 0x77, 0x06, 0x79, 0x05, 0x7d, 0x04, 0x81, 0x03, - 0x83, 0x02, 0x85, 0x02, 0x88, 0x02, 0x8c, 0x01, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x21, 0x1e, 0x2a, 0x17, 0x32, 0x12, 0x39, 0x0e, 0x40, 0x0a, 0x48, 0x07, - 0x4f, 0x05, 0x55, 0x04, 0x5a, 0x03, 0x60, 0x02, 0x66, 0x01, 0x6b, 0x00, - 0x70, 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, - 0x88, 0x00, 0x8b, 0x00, 0x7f, 0x0f, 0x83, 0x0b, 0x87, 0x09, 0x8b, 0x07, - 0x8e, 0x05, 0x92, 0x03, 0x96, 0x02, 0x99, 0x02, 0x9b, 0x01, 0x9e, 0x01, - 0xa1, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, - 0xaf, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, - 0x60, 0x00, 0x6a, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x33, 0x00, 0x27, - 0x00, 0x18, 0x04, 0x0d, 0x0e, 0x08, 0x17, 0x04, 0x20, 0x00, 0x32, 0x00, - 0x42, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x67, 0x00, 0x70, 0x00, 0x78, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, 0x00, - 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, - 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, - 0x60, 0x00, 0x6a, 0x00, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, - 0x00, 0x0b, 0x08, 0x00, 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, 0x00, - 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, 0x00, - 0x42, 0x31, 0x47, 0x29, 0x4b, 0x22, 0x50, 0x1d, 0x55, 0x19, 0x58, 0x15, - 0x5e, 0x12, 0x61, 0x0f, 0x65, 0x0d, 0x6a, 0x0b, 0x6c, 0x09, 0x70, 0x08, - 0x75, 0x07, 0x77, 0x06, 0x79, 0x05, 0x7d, 0x04, 0x81, 0x03, 0x83, 0x02, - 0x85, 0x02, 0x87, 0x02, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x29, 0x18, - 0x31, 0x12, 0x38, 0x0f, 0x3f, 0x0c, 0x45, 0x09, 0x4c, 0x07, 0x52, 0x05, - 0x58, 0x04, 0x5d, 0x02, 0x63, 0x02, 0x67, 0x01, 0x6c, 0x00, 0x71, 0x00, - 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, - 0x7f, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8a, 0x07, 0x8e, 0x06, 0x91, 0x04, - 0x94, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x01, 0xa0, 0x01, 0xa2, 0x00, - 0xa4, 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, - 0xb0, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, - 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x12, - 0x07, 0x0c, 0x10, 0x07, 0x18, 0x03, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, - 0x4c, 0x00, 0x57, 0x00, 0x61, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, - 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, - 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, - 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, - 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x42, 0x31, 0x46, 0x2a, - 0x4a, 0x24, 0x4f, 0x1f, 0x53, 0x1a, 0x58, 0x16, 0x5c, 0x13, 0x60, 0x11, - 0x63, 0x0e, 0x67, 0x0c, 0x6b, 0x0b, 0x6d, 0x09, 0x72, 0x07, 0x75, 0x07, - 0x77, 0x06, 0x79, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x29, 0x18, 0x30, 0x13, 0x37, 0x0f, - 0x3d, 0x0c, 0x44, 0x0a, 0x4a, 0x07, 0x50, 0x05, 0x55, 0x04, 0x5b, 0x04, - 0x60, 0x02, 0x64, 0x01, 0x69, 0x01, 0x6d, 0x00, 0x72, 0x00, 0x76, 0x00, - 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, 0x00, 0x7f, 0x0f, 0x83, 0x0c, - 0x86, 0x09, 0x8a, 0x07, 0x8d, 0x06, 0x90, 0x05, 0x93, 0x03, 0x96, 0x02, - 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, 0x00, 0xa3, 0x00, 0xa5, 0x00, - 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x3e, 0x00, 0x3c, - 0x00, 0x37, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x02, 0x0e, 0x0a, 0x0a, - 0x11, 0x06, 0x19, 0x03, 0x1f, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, - 0x53, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, - 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x3e, 0x00, 0x3c, - 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x04, 0x00, 0x14, 0x00, - 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, - 0x68, 0x00, 0x70, 0x00, 0x42, 0x32, 0x46, 0x2b, 0x49, 0x25, 0x4f, 0x1f, - 0x52, 0x1b, 0x57, 0x18, 0x59, 0x14, 0x5f, 0x12, 0x62, 0x0f, 0x65, 0x0d, - 0x6a, 0x0c, 0x6c, 0x0a, 0x6e, 0x09, 0x73, 0x07, 0x76, 0x06, 0x78, 0x06, - 0x7a, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x21, 0x1e, 0x28, 0x19, 0x2f, 0x14, 0x35, 0x0f, 0x3c, 0x0c, 0x42, 0x0a, - 0x48, 0x07, 0x4e, 0x06, 0x53, 0x05, 0x58, 0x04, 0x5d, 0x02, 0x62, 0x02, - 0x66, 0x01, 0x6b, 0x01, 0x6f, 0x00, 0x73, 0x00, 0x76, 0x00, 0x7a, 0x00, - 0x7d, 0x00, 0x81, 0x00, 0x7f, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x07, - 0x8c, 0x06, 0x8f, 0x05, 0x92, 0x03, 0x95, 0x03, 0x98, 0x02, 0x9a, 0x02, - 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, - 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2d, 0x00, 0x39, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x31, - 0x00, 0x28, 0x00, 0x1e, 0x00, 0x13, 0x05, 0x0d, 0x0c, 0x09, 0x13, 0x06, - 0x19, 0x03, 0x1f, 0x00, 0x2d, 0x00, 0x3a, 0x00, 0x45, 0x00, 0x4f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, - 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, - 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2d, 0x00, 0x39, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, - 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x0a, 0x00, 0x18, 0x00, 0x26, 0x00, - 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, 0x00, - 0x42, 0x33, 0x45, 0x2c, 0x48, 0x26, 0x4e, 0x21, 0x50, 0x1c, 0x56, 0x19, - 0x58, 0x16, 0x5c, 0x13, 0x60, 0x11, 0x62, 0x0e, 0x67, 0x0c, 0x6a, 0x0b, - 0x6d, 0x0a, 0x70, 0x09, 0x74, 0x07, 0x76, 0x06, 0x78, 0x06, 0x7a, 0x05, - 0x7e, 0x04, 0x81, 0x03, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x28, 0x19, - 0x2f, 0x15, 0x35, 0x11, 0x3b, 0x0d, 0x41, 0x0b, 0x46, 0x09, 0x4c, 0x07, - 0x51, 0x05, 0x55, 0x04, 0x5b, 0x04, 0x5f, 0x02, 0x63, 0x02, 0x68, 0x01, - 0x6b, 0x00, 0x70, 0x00, 0x73, 0x00, 0x77, 0x00, 0x7b, 0x00, 0x7d, 0x00, - 0x7f, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x08, 0x8c, 0x06, 0x8f, 0x05, - 0x91, 0x04, 0x94, 0x03, 0x97, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, - 0xa0, 0x01, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, - 0xac, 0x00, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, - 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x23, - 0x00, 0x19, 0x00, 0x0f, 0x07, 0x0c, 0x0e, 0x08, 0x14, 0x05, 0x1a, 0x02, - 0x1f, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, - 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, - 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, - 0x00, 0x0c, 0x01, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, - 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x00, 0x5d, 0x1a, 0x6d, 0x16, - 0x72, 0x15, 0x75, 0x14, 0x77, 0x13, 0x78, 0x13, 0x79, 0x13, 0x7a, 0x12, - 0x7a, 0x12, 0x7b, 0x12, 0x7b, 0x12, 0x7b, 0x12, 0x7c, 0x12, 0x7c, 0x11, - 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7d, 0x11, - 0xbd, 0x02, 0xa4, 0x06, 0x97, 0x09, 0x91, 0x0a, 0x8d, 0x0b, 0x8a, 0x0c, - 0x89, 0x0d, 0x88, 0x0d, 0x86, 0x0d, 0x86, 0x0e, 0x85, 0x0e, 0x85, 0x0e, - 0x84, 0x0e, 0x84, 0x0e, 0x83, 0x0f, 0x83, 0x0f, 0x83, 0x0f, 0x83, 0x0f, - 0x82, 0x0f, 0x82, 0x0f, 0x33, 0x11, 0x5f, 0x11, 0x6f, 0x11, 0x74, 0x11, - 0x77, 0x11, 0x78, 0x11, 0x7a, 0x11, 0x7a, 0x11, 0x7b, 0x11, 0x7b, 0x11, - 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7d, 0x11, 0x7d, 0x11, - 0x7d, 0x11, 0x7d, 0x11, 0x7d, 0x11, 0x7d, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1d, 0x00, 0x14, - 0x03, 0x0e, 0x09, 0x0b, 0x0f, 0x08, 0x15, 0x05, 0x1a, 0x02, 0x1f, 0x00, - 0x2b, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, - 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x3f, 0x00, 0x3d, - 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, - 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, - 0x49, 0x00, 0x51, 0x00, 0x50, 0x24, 0x5f, 0x1f, 0x66, 0x1b, 0x6b, 0x19, - 0x6e, 0x18, 0x70, 0x17, 0x73, 0x16, 0x73, 0x15, 0x75, 0x15, 0x76, 0x15, - 0x76, 0x15, 0x77, 0x14, 0x77, 0x14, 0x78, 0x13, 0x78, 0x13, 0x79, 0x13, - 0x79, 0x13, 0x7a, 0x13, 0x7a, 0x13, 0x7b, 0x13, 0xcc, 0x00, 0xb5, 0x00, - 0xa8, 0x02, 0x9e, 0x04, 0x99, 0x05, 0x94, 0x06, 0x92, 0x07, 0x8f, 0x08, - 0x8d, 0x09, 0x8c, 0x09, 0x8b, 0x0a, 0x8a, 0x0a, 0x89, 0x0b, 0x88, 0x0b, - 0x88, 0x0b, 0x87, 0x0c, 0x87, 0x0c, 0x86, 0x0c, 0x86, 0x0c, 0x86, 0x0c, - 0x23, 0x13, 0x46, 0x11, 0x58, 0x11, 0x63, 0x11, 0x69, 0x11, 0x6d, 0x11, - 0x70, 0x11, 0x72, 0x11, 0x73, 0x11, 0x75, 0x11, 0x76, 0x11, 0x77, 0x11, - 0x77, 0x11, 0x78, 0x11, 0x78, 0x11, 0x79, 0x11, 0x79, 0x11, 0x79, 0x11, - 0x7a, 0x11, 0x7a, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0d, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, - 0x00, 0x30, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x10, 0x05, 0x0d, - 0x0b, 0x0a, 0x10, 0x07, 0x16, 0x04, 0x1b, 0x02, 0x1f, 0x00, 0x2a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, - 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, - 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, - 0x00, 0x00, 0x0d, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, - 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, - 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x00, - 0x4a, 0x2a, 0x57, 0x24, 0x5f, 0x20, 0x64, 0x1e, 0x68, 0x1c, 0x6a, 0x1b, - 0x6d, 0x19, 0x6e, 0x18, 0x70, 0x18, 0x72, 0x17, 0x72, 0x17, 0x73, 0x16, - 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, - 0x76, 0x15, 0x77, 0x14, 0xd1, 0x00, 0xc0, 0x00, 0xb3, 0x00, 0xaa, 0x01, - 0xa2, 0x02, 0x9d, 0x03, 0x99, 0x04, 0x96, 0x05, 0x94, 0x06, 0x92, 0x06, - 0x90, 0x07, 0x8f, 0x07, 0x8d, 0x08, 0x8d, 0x08, 0x8c, 0x09, 0x8b, 0x09, - 0x8a, 0x09, 0x8a, 0x0a, 0x89, 0x0a, 0x89, 0x0b, 0x22, 0x17, 0x39, 0x11, - 0x4a, 0x11, 0x55, 0x11, 0x5e, 0x11, 0x63, 0x11, 0x67, 0x11, 0x6a, 0x11, - 0x6c, 0x11, 0x6e, 0x11, 0x70, 0x11, 0x71, 0x11, 0x72, 0x11, 0x73, 0x11, - 0x74, 0x11, 0x75, 0x11, 0x75, 0x11, 0x76, 0x11, 0x76, 0x11, 0x77, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, 0x32, 0x00, 0x2c, - 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x01, 0x0e, 0x07, 0x0c, 0x0c, 0x09, - 0x11, 0x06, 0x16, 0x04, 0x1b, 0x02, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, - 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, - 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, - 0x00, 0x1c, 0x00, 0x11, 0x00, 0x07, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, - 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x2e, 0x53, 0x28, - 0x5a, 0x24, 0x5f, 0x22, 0x63, 0x1f, 0x66, 0x1e, 0x69, 0x1c, 0x6a, 0x1b, - 0x6c, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x70, 0x18, 0x71, 0x18, 0x72, 0x18, - 0x72, 0x17, 0x72, 0x17, 0x73, 0x16, 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, - 0xd5, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x01, - 0xa0, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x97, 0x04, 0x95, 0x05, 0x93, 0x05, - 0x92, 0x06, 0x91, 0x06, 0x90, 0x07, 0x8e, 0x07, 0x8e, 0x07, 0x8d, 0x07, - 0x8c, 0x08, 0x8c, 0x09, 0x22, 0x1a, 0x33, 0x13, 0x41, 0x11, 0x4c, 0x11, - 0x54, 0x11, 0x5b, 0x11, 0x5f, 0x11, 0x63, 0x11, 0x66, 0x11, 0x68, 0x11, - 0x6a, 0x11, 0x6c, 0x11, 0x6e, 0x11, 0x6f, 0x11, 0x70, 0x11, 0x71, 0x11, - 0x72, 0x11, 0x72, 0x11, 0x73, 0x11, 0x74, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x07, 0x7f, 0x00, - 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, - 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, - 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x07, 0x0f, 0x0f, 0x00, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, - 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, - 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x31, 0x50, 0x2b, 0x56, 0x27, 0x5b, 0x24, - 0x5f, 0x22, 0x62, 0x20, 0x65, 0x1f, 0x67, 0x1d, 0x69, 0x1d, 0x6a, 0x1b, - 0x6c, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x6f, 0x18, 0x71, 0x18, - 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x72, 0x17, 0xd6, 0x00, 0xcb, 0x00, - 0xc0, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xab, 0x00, 0xa6, 0x01, 0xa2, 0x01, - 0x9f, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x98, 0x03, 0x96, 0x04, 0x94, 0x05, - 0x93, 0x05, 0x92, 0x06, 0x91, 0x06, 0x90, 0x06, 0x8f, 0x06, 0x8f, 0x07, - 0x22, 0x1b, 0x2f, 0x15, 0x3b, 0x12, 0x44, 0x11, 0x4d, 0x11, 0x53, 0x11, - 0x59, 0x11, 0x5d, 0x11, 0x60, 0x11, 0x63, 0x11, 0x66, 0x11, 0x67, 0x11, - 0x69, 0x11, 0x6b, 0x11, 0x6c, 0x11, 0x6d, 0x11, 0x6e, 0x11, 0x6f, 0x11, - 0x70, 0x11, 0x71, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, - 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, 0x00, - 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0x00, - 0x05, 0x05, 0x00, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, - 0x00, 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, - 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x33, 0x4d, 0x2e, 0x53, 0x29, 0x58, 0x26, 0x5c, 0x24, 0x5f, 0x22, - 0x61, 0x21, 0x65, 0x20, 0x65, 0x1e, 0x68, 0x1d, 0x69, 0x1d, 0x6a, 0x1b, - 0x6b, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x6f, 0x18, - 0x71, 0x18, 0x72, 0x18, 0xd7, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbd, 0x00, - 0xb6, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa7, 0x00, 0xa4, 0x01, 0xa1, 0x02, - 0x9e, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x98, 0x03, 0x97, 0x03, 0x96, 0x04, - 0x94, 0x05, 0x93, 0x05, 0x92, 0x05, 0x91, 0x06, 0x21, 0x1c, 0x2d, 0x16, - 0x37, 0x13, 0x40, 0x11, 0x47, 0x11, 0x4e, 0x11, 0x53, 0x11, 0x57, 0x11, - 0x5b, 0x11, 0x5e, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x67, 0x11, - 0x68, 0x11, 0x6a, 0x11, 0x6b, 0x11, 0x6c, 0x11, 0x6d, 0x11, 0x6e, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x26, 0x05, 0x05, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, - 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, - 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x00, - 0x00, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, - 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x34, 0x4c, 0x2f, - 0x51, 0x2c, 0x56, 0x28, 0x5a, 0x26, 0x5c, 0x24, 0x60, 0x23, 0x61, 0x21, - 0x64, 0x20, 0x65, 0x1f, 0x66, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, - 0x6b, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, - 0xd8, 0x00, 0xcf, 0x00, 0xc7, 0x00, 0xc0, 0x00, 0xba, 0x00, 0xb5, 0x00, - 0xb0, 0x00, 0xac, 0x00, 0xa8, 0x00, 0xa5, 0x01, 0xa2, 0x01, 0xa0, 0x02, - 0x9e, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x03, 0x97, 0x03, 0x96, 0x03, - 0x95, 0x04, 0x94, 0x05, 0x22, 0x1d, 0x2b, 0x17, 0x34, 0x14, 0x3b, 0x12, - 0x43, 0x11, 0x49, 0x11, 0x4e, 0x11, 0x52, 0x11, 0x56, 0x11, 0x5a, 0x11, - 0x5d, 0x11, 0x5f, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, - 0x68, 0x11, 0x69, 0x11, 0x6a, 0x11, 0x6b, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1c, - 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, 0x00, - 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, - 0xb3, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x00, - 0x00, 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, - 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x44, 0x36, 0x4a, 0x31, 0x50, 0x2d, 0x54, 0x2a, - 0x57, 0x28, 0x5b, 0x26, 0x5c, 0x24, 0x60, 0x23, 0x61, 0x21, 0x63, 0x20, - 0x65, 0x20, 0x65, 0x1e, 0x67, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, - 0x6a, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0xd9, 0x00, 0xd1, 0x00, - 0xca, 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb4, 0x00, 0xb0, 0x00, - 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0x9f, 0x02, - 0x9d, 0x02, 0x9b, 0x02, 0x9a, 0x02, 0x99, 0x03, 0x98, 0x03, 0x97, 0x03, - 0x21, 0x1e, 0x2a, 0x18, 0x32, 0x15, 0x39, 0x13, 0x3f, 0x12, 0x45, 0x11, - 0x4a, 0x11, 0x4e, 0x11, 0x52, 0x11, 0x56, 0x11, 0x59, 0x11, 0x5c, 0x11, - 0x5d, 0x11, 0x60, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, - 0x67, 0x11, 0x68, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x09, 0x1d, 0x00, - 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, 0x00, 0x8b, 0x00, 0x94, 0x00, - 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, 0x00, - 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, - 0x00, 0x2b, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x36, 0x49, 0x32, 0x4e, 0x2f, 0x52, 0x2c, 0x56, 0x29, 0x58, 0x27, - 0x5b, 0x26, 0x5d, 0x24, 0x60, 0x23, 0x60, 0x22, 0x62, 0x20, 0x65, 0x20, - 0x65, 0x1f, 0x66, 0x1e, 0x68, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, - 0x6a, 0x1b, 0x6c, 0x1a, 0xd9, 0x00, 0xd2, 0x00, 0xcc, 0x00, 0xc6, 0x00, - 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb3, 0x00, 0xaf, 0x00, 0xac, 0x00, - 0xa9, 0x00, 0xa6, 0x00, 0xa4, 0x01, 0xa2, 0x01, 0xa0, 0x01, 0x9e, 0x02, - 0x9d, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x02, 0x22, 0x1e, 0x29, 0x19, - 0x30, 0x16, 0x36, 0x13, 0x3c, 0x12, 0x41, 0x11, 0x46, 0x11, 0x4a, 0x11, - 0x4f, 0x11, 0x52, 0x11, 0x55, 0x11, 0x58, 0x11, 0x5a, 0x11, 0x5c, 0x11, - 0x5f, 0x11, 0x60, 0x11, 0x62, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, - 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, - 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, - 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x02, - 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x37, 0x48, 0x33, - 0x4d, 0x30, 0x51, 0x2d, 0x54, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x26, - 0x5d, 0x24, 0x60, 0x23, 0x60, 0x22, 0x62, 0x20, 0x65, 0x20, 0x65, 0x20, - 0x65, 0x1f, 0x66, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, - 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc8, 0x00, 0xc3, 0x00, 0xbe, 0x00, - 0xba, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, - 0xa7, 0x00, 0xa5, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0xa0, 0x02, 0x9e, 0x02, - 0x9d, 0x02, 0x9c, 0x02, 0x21, 0x1e, 0x28, 0x1a, 0x2e, 0x17, 0x34, 0x14, - 0x39, 0x13, 0x3f, 0x12, 0x43, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4f, 0x11, - 0x52, 0x11, 0x55, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5c, 0x11, 0x5d, 0x11, - 0x5f, 0x11, 0x61, 0x11, 0x62, 0x11, 0x63, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, - 0x00, 0x23, 0x00, 0x0b, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, 0x00, - 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, - 0x97, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, - 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x38, 0x48, 0x34, 0x4c, 0x31, 0x4f, 0x2e, - 0x53, 0x2c, 0x56, 0x2a, 0x57, 0x28, 0x5a, 0x27, 0x5c, 0x26, 0x5d, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, 0x65, 0x20, - 0x65, 0x1e, 0x67, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0xd9, 0x00, 0xd4, 0x00, - 0xce, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xb9, 0x00, - 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, - 0xa6, 0x00, 0xa4, 0x01, 0xa2, 0x01, 0xa0, 0x01, 0x9f, 0x02, 0x9e, 0x02, - 0x22, 0x1f, 0x27, 0x1a, 0x2d, 0x17, 0x33, 0x15, 0x38, 0x13, 0x3c, 0x13, - 0x41, 0x12, 0x44, 0x11, 0x48, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, - 0x54, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5b, 0x11, 0x5c, 0x11, 0x5e, 0x11, - 0x5f, 0x11, 0x61, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, - 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, 0x00, 0x50, 0x00, 0x5e, 0x00, - 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb7, 0x00, - 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, 0x00, - 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, - 0x00, 0x0c, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x38, 0x47, 0x35, 0x4b, 0x32, 0x4e, 0x2f, 0x52, 0x2d, 0x53, 0x2b, - 0x57, 0x2a, 0x58, 0x27, 0x5b, 0x27, 0x5c, 0x25, 0x5d, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, - 0x66, 0x1e, 0x68, 0x1d, 0xda, 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcb, 0x00, - 0xc7, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb5, 0x00, - 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, - 0xa4, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0xa0, 0x01, 0x21, 0x1f, 0x27, 0x1b, - 0x2c, 0x18, 0x31, 0x16, 0x36, 0x14, 0x3a, 0x13, 0x3e, 0x12, 0x42, 0x11, - 0x45, 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, - 0x56, 0x11, 0x58, 0x11, 0x5a, 0x11, 0x5c, 0x11, 0x5d, 0x11, 0x5e, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x08, 0x00, - 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, - 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, - 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, 0x00, - 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x46, 0x35, - 0x4a, 0x32, 0x4e, 0x30, 0x50, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x29, - 0x58, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x22, 0x63, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, - 0xda, 0x00, 0xd5, 0x00, 0xd1, 0x00, 0xcc, 0x00, 0xc8, 0x00, 0xc4, 0x00, - 0xc1, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb4, 0x00, 0xb2, 0x00, - 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa5, 0x00, - 0xa4, 0x01, 0xa2, 0x01, 0x22, 0x1f, 0x27, 0x1b, 0x2c, 0x18, 0x30, 0x16, - 0x35, 0x14, 0x39, 0x13, 0x3d, 0x13, 0x40, 0x12, 0x44, 0x11, 0x47, 0x11, - 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x56, 0x11, - 0x58, 0x11, 0x59, 0x11, 0x5b, 0x11, 0x5c, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, - 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x0f, 0x00, 0x20, 0x00, - 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, - 0x74, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, - 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, - 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x46, 0x36, 0x4a, 0x33, 0x4d, 0x31, - 0x4f, 0x2e, 0x52, 0x2d, 0x53, 0x2b, 0x57, 0x2a, 0x57, 0x28, 0x5a, 0x27, - 0x5c, 0x27, 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, - 0x62, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0xda, 0x00, 0xd6, 0x00, - 0xd1, 0x00, 0xcd, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc2, 0x00, 0xbf, 0x00, - 0xbc, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, - 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa6, 0x00, 0xa4, 0x00, - 0x21, 0x1f, 0x26, 0x1c, 0x2b, 0x19, 0x2f, 0x17, 0x33, 0x15, 0x37, 0x14, - 0x3b, 0x13, 0x3e, 0x12, 0x41, 0x12, 0x44, 0x11, 0x47, 0x11, 0x4a, 0x11, - 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x55, 0x11, 0x57, 0x11, - 0x59, 0x11, 0x5a, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, - 0x00, 0x1b, 0x00, 0x0c, 0x04, 0x00, 0x14, 0x00, 0x23, 0x00, 0x32, 0x00, - 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, - 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, 0x00, - 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, - 0x16, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x39, 0x45, 0x36, 0x49, 0x34, 0x4c, 0x31, 0x4f, 0x30, 0x51, 0x2e, - 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x28, 0x5b, 0x27, 0x5c, 0x27, - 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, 0x61, 0x21, - 0x64, 0x20, 0x65, 0x20, 0xda, 0x00, 0xd6, 0x00, 0xd2, 0x00, 0xce, 0x00, - 0xcb, 0x00, 0xc7, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, - 0xb8, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, - 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0x22, 0x1f, 0x26, 0x1c, - 0x2a, 0x19, 0x2e, 0x17, 0x32, 0x16, 0x36, 0x14, 0x39, 0x13, 0x3d, 0x13, - 0x40, 0x12, 0x43, 0x11, 0x45, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4d, 0x11, - 0x4f, 0x11, 0x51, 0x11, 0x53, 0x11, 0x55, 0x11, 0x56, 0x11, 0x58, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, - 0x00, 0x04, 0x0a, 0x00, 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, - 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, - 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, 0x00, - 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3a, 0x45, 0x37, - 0x48, 0x35, 0x4a, 0x32, 0x4e, 0x31, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2b, - 0x57, 0x2a, 0x57, 0x29, 0x58, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x25, - 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, - 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xc8, 0x00, - 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb7, 0x00, - 0xb5, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, - 0xa9, 0x00, 0xa8, 0x00, 0x21, 0x20, 0x26, 0x1c, 0x2a, 0x1a, 0x2d, 0x18, - 0x31, 0x16, 0x35, 0x14, 0x38, 0x13, 0x3b, 0x13, 0x3e, 0x12, 0x41, 0x12, - 0x44, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, - 0x51, 0x11, 0x53, 0x11, 0x55, 0x11, 0x56, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, - 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x01, 0x00, - 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, - 0x53, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, - 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, - 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3a, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x33, - 0x4d, 0x31, 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, - 0x57, 0x29, 0x59, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5f, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, 0xdb, 0x00, 0xd7, 0x00, - 0xd3, 0x00, 0xd0, 0x00, 0xcc, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc3, 0x00, - 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb9, 0x00, 0xb7, 0x00, 0xb5, 0x00, - 0xb3, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xaa, 0x00, - 0x21, 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2d, 0x18, 0x30, 0x17, 0x33, 0x15, - 0x37, 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x12, 0x42, 0x12, 0x44, 0x11, - 0x47, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x11, - 0x53, 0x11, 0x55, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, - 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, 0x06, 0x00, 0x13, 0x00, - 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, - 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, 0x00, - 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, - 0x36, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3a, 0x45, 0x38, 0x47, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x30, - 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2b, 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x28, - 0x5a, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5f, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd7, 0x00, 0xd4, 0x00, 0xd0, 0x00, - 0xcd, 0x00, 0xca, 0x00, 0xc7, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, - 0xbd, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, 0x00, - 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xac, 0x00, 0x21, 0x20, 0x25, 0x1d, - 0x29, 0x1a, 0x2c, 0x18, 0x2f, 0x17, 0x33, 0x16, 0x36, 0x14, 0x39, 0x13, - 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x43, 0x11, 0x45, 0x11, 0x47, 0x11, - 0x4a, 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x52, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, - 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, - 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, - 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, 0x00, - 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, - 0x46, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x2e, 0x53, 0x2e, - 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x27, 0x5b, 0x27, - 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x24, 0x5f, 0x23, 0x60, 0x23, 0x60, 0x23, - 0xdb, 0x00, 0xd8, 0x00, 0xd4, 0x00, 0xd1, 0x00, 0xce, 0x00, 0xcb, 0x00, - 0xc8, 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbc, 0x00, - 0xba, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, - 0xaf, 0x00, 0xad, 0x00, 0x21, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x18, - 0x2f, 0x17, 0x32, 0x16, 0x35, 0x14, 0x38, 0x14, 0x3a, 0x13, 0x3d, 0x13, - 0x3f, 0x12, 0x42, 0x12, 0x44, 0x11, 0x46, 0x11, 0x48, 0x11, 0x4a, 0x11, - 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, - 0x00, 0x07, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, - 0x36, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, - 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, - 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x36, 0x4a, 0x35, - 0x4a, 0x32, 0x4e, 0x31, 0x4f, 0x2f, 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2b, - 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0x5c, 0x26, 0x5c, 0x24, 0x5f, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd8, 0x00, - 0xd5, 0x00, 0xd2, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xca, 0x00, 0xc6, 0x00, - 0xc4, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb9, 0x00, - 0xb8, 0x00, 0xb5, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, - 0x21, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x19, 0x2e, 0x17, 0x31, 0x16, - 0x34, 0x15, 0x37, 0x14, 0x39, 0x13, 0x3b, 0x13, 0x3e, 0x13, 0x40, 0x12, - 0x42, 0x12, 0x45, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4a, 0x11, 0x4c, 0x11, - 0x4e, 0x11, 0x4f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x27, 0x68, 0x32, 0x51, 0x36, 0x4a, 0x38, 0x47, - 0x3a, 0x46, 0x3a, 0x45, 0x3b, 0x44, 0x3c, 0x43, 0x3c, 0x43, 0x3c, 0x43, - 0x3c, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x41, - 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x41, 0x0e, 0x16, 0x27, - 0x23, 0x31, 0x2a, 0x35, 0x2e, 0x38, 0x31, 0x39, 0x33, 0x3a, 0x34, 0x3b, - 0x35, 0x3b, 0x36, 0x3c, 0x37, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x39, 0x3d, - 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3b, 0x3d, - 0x68, 0x00, 0x32, 0x27, 0x36, 0x31, 0x38, 0x35, 0x3a, 0x38, 0x3a, 0x39, - 0x3b, 0x3a, 0x3c, 0x3b, 0x3c, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3c, - 0x3d, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x22, 0x32, 0x28, 0x36, 0x2c, 0x38, 0x2f, 0x3a, 0x32, 0x3a, 0x34, 0x3b, - 0x35, 0x3c, 0x36, 0x3c, 0x37, 0x3c, 0x37, 0x3c, 0x38, 0x3d, 0x39, 0x3d, - 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, - 0x3b, 0x3d, 0x3b, 0x3d, 0x00, 0x97, 0x01, 0x67, 0x09, 0x56, 0x11, 0x4f, - 0x17, 0x4b, 0x1c, 0x49, 0x20, 0x47, 0x23, 0x46, 0x25, 0x45, 0x28, 0x44, - 0x2a, 0x44, 0x2b, 0x43, 0x2c, 0x43, 0x2e, 0x43, 0x2f, 0x42, 0x30, 0x42, - 0x31, 0x42, 0x31, 0x42, 0x32, 0x42, 0x33, 0x42, 0x1a, 0x5c, 0x25, 0x4f, - 0x2a, 0x4a, 0x2e, 0x48, 0x31, 0x46, 0x33, 0x45, 0x34, 0x44, 0x36, 0x44, - 0x37, 0x43, 0x37, 0x43, 0x38, 0x43, 0x38, 0x42, 0x39, 0x42, 0x39, 0x42, - 0x39, 0x41, 0x3a, 0x41, 0x3a, 0x41, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x5f, - 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x33, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x28, 0x25, 0x2d, - 0x28, 0x30, 0x2b, 0x32, 0x2e, 0x34, 0x2f, 0x35, 0x31, 0x36, 0x32, 0x37, - 0x33, 0x38, 0x34, 0x38, 0x35, 0x39, 0x36, 0x39, 0x36, 0x39, 0x36, 0x3a, - 0x37, 0x3a, 0x37, 0x3a, 0x38, 0x3b, 0x38, 0x3b, 0x39, 0x3b, 0x39, 0x3b, - 0x00, 0xb7, 0x00, 0x8b, 0x02, 0x72, 0x07, 0x65, 0x0c, 0x5c, 0x11, 0x57, - 0x15, 0x53, 0x18, 0x50, 0x1b, 0x4e, 0x1d, 0x4d, 0x20, 0x4b, 0x22, 0x4a, - 0x23, 0x49, 0x25, 0x48, 0x26, 0x48, 0x28, 0x47, 0x29, 0x47, 0x2a, 0x46, - 0x2b, 0x46, 0x2c, 0x45, 0x16, 0x6d, 0x1f, 0x5f, 0x24, 0x57, 0x28, 0x53, - 0x2b, 0x50, 0x2d, 0x4d, 0x2f, 0x4c, 0x31, 0x4a, 0x32, 0x49, 0x33, 0x48, - 0x34, 0x48, 0x35, 0x47, 0x35, 0x46, 0x36, 0x46, 0x36, 0x45, 0x37, 0x45, - 0x38, 0x45, 0x38, 0x45, 0x39, 0x44, 0x39, 0x44, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x8b, 0x00, 0x44, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x2e, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x25, 0x23, 0x28, 0x26, 0x2b, 0x28, 0x2e, - 0x2a, 0x2f, 0x2c, 0x31, 0x2e, 0x32, 0x2f, 0x33, 0x30, 0x34, 0x31, 0x35, - 0x32, 0x36, 0x33, 0x36, 0x33, 0x36, 0x34, 0x37, 0x35, 0x38, 0x36, 0x38, - 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x00, 0xc5, 0x00, 0xa0, - 0x00, 0x87, 0x03, 0x76, 0x06, 0x6b, 0x0a, 0x64, 0x0e, 0x5f, 0x11, 0x5b, - 0x13, 0x57, 0x16, 0x55, 0x18, 0x53, 0x1a, 0x51, 0x1c, 0x50, 0x1e, 0x4e, - 0x20, 0x4d, 0x21, 0x4c, 0x22, 0x4b, 0x24, 0x4a, 0x25, 0x49, 0x26, 0x48, - 0x15, 0x72, 0x1b, 0x66, 0x20, 0x5f, 0x24, 0x5a, 0x27, 0x56, 0x29, 0x53, - 0x2c, 0x51, 0x2d, 0x50, 0x2f, 0x4e, 0x30, 0x4d, 0x31, 0x4c, 0x32, 0x4b, - 0x32, 0x4a, 0x33, 0x49, 0x34, 0x49, 0x35, 0x48, 0x35, 0x48, 0x35, 0x47, - 0x35, 0x46, 0x36, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x6d, 0x00, 0x33, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x24, 0x00, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x22, 0x24, 0x23, 0x26, 0x25, 0x29, 0x27, 0x2b, 0x28, 0x2c, 0x2a, 0x2e, - 0x2c, 0x2f, 0x2d, 0x30, 0x2e, 0x31, 0x2f, 0x32, 0x30, 0x33, 0x30, 0x33, - 0x32, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, 0x33, 0x36, 0x34, 0x36, - 0x35, 0x36, 0x36, 0x37, 0x00, 0xcb, 0x00, 0xad, 0x00, 0x96, 0x01, 0x84, - 0x03, 0x78, 0x06, 0x70, 0x09, 0x69, 0x0b, 0x64, 0x0e, 0x60, 0x10, 0x5d, - 0x13, 0x59, 0x15, 0x57, 0x17, 0x56, 0x19, 0x54, 0x1a, 0x52, 0x1c, 0x51, - 0x1d, 0x50, 0x1f, 0x4f, 0x1f, 0x4f, 0x21, 0x4e, 0x14, 0x75, 0x19, 0x6b, - 0x1e, 0x64, 0x22, 0x5f, 0x24, 0x5b, 0x27, 0x58, 0x29, 0x56, 0x2a, 0x54, - 0x2c, 0x52, 0x2d, 0x51, 0x2e, 0x4f, 0x2f, 0x4e, 0x30, 0x4e, 0x31, 0x4d, - 0x31, 0x4c, 0x32, 0x4a, 0x33, 0x4a, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, - 0x00, 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x23, 0x23, 0x25, - 0x24, 0x27, 0x25, 0x28, 0x27, 0x2a, 0x28, 0x2c, 0x2a, 0x2d, 0x2b, 0x2e, - 0x2c, 0x2f, 0x2d, 0x30, 0x2e, 0x31, 0x2f, 0x32, 0x30, 0x32, 0x30, 0x33, - 0x31, 0x33, 0x32, 0x33, 0x33, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, - 0x00, 0xcf, 0x00, 0xb6, 0x00, 0xa0, 0x00, 0x8f, 0x01, 0x83, 0x03, 0x79, - 0x05, 0x72, 0x08, 0x6c, 0x0a, 0x68, 0x0c, 0x64, 0x0f, 0x61, 0x10, 0x5e, - 0x12, 0x5b, 0x14, 0x59, 0x16, 0x58, 0x17, 0x56, 0x19, 0x55, 0x1a, 0x53, - 0x1b, 0x52, 0x1c, 0x50, 0x13, 0x77, 0x18, 0x6e, 0x1c, 0x68, 0x1f, 0x63, - 0x22, 0x5f, 0x24, 0x5c, 0x26, 0x59, 0x28, 0x57, 0x29, 0x56, 0x2a, 0x54, - 0x2c, 0x53, 0x2d, 0x52, 0x2e, 0x50, 0x2e, 0x4f, 0x2f, 0x4f, 0x31, 0x4e, - 0x31, 0x4d, 0x31, 0x4d, 0x31, 0x4c, 0x32, 0x4a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, - 0x00, 0x48, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3a, 0x00, - 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x23, 0x22, 0x24, 0x24, 0x26, 0x25, 0x27, - 0x26, 0x29, 0x27, 0x2a, 0x28, 0x2b, 0x2a, 0x2d, 0x2a, 0x2d, 0x2c, 0x2e, - 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, - 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x34, 0x00, 0xd1, 0x00, 0xbc, - 0x00, 0xa9, 0x00, 0x99, 0x00, 0x8c, 0x02, 0x82, 0x03, 0x7a, 0x05, 0x74, - 0x07, 0x6f, 0x09, 0x6b, 0x0b, 0x67, 0x0d, 0x63, 0x0f, 0x61, 0x10, 0x5f, - 0x12, 0x5c, 0x14, 0x5a, 0x15, 0x58, 0x16, 0x58, 0x18, 0x57, 0x19, 0x56, - 0x13, 0x78, 0x17, 0x70, 0x1b, 0x6a, 0x1e, 0x66, 0x20, 0x62, 0x22, 0x5f, - 0x24, 0x5c, 0x26, 0x5b, 0x27, 0x58, 0x29, 0x57, 0x2a, 0x56, 0x2b, 0x53, - 0x2c, 0x53, 0x2d, 0x52, 0x2e, 0x51, 0x2e, 0x50, 0x2f, 0x4f, 0x30, 0x4f, - 0x31, 0x4f, 0x31, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, - 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, - 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x22, 0x22, 0x22, 0x24, 0x23, 0x25, 0x24, 0x27, 0x25, 0x27, 0x27, 0x28, - 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2e, - 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x33, - 0x30, 0x33, 0x31, 0x33, 0x00, 0xd3, 0x00, 0xc1, 0x00, 0xaf, 0x00, 0xa1, - 0x00, 0x94, 0x00, 0x8a, 0x02, 0x82, 0x03, 0x7b, 0x05, 0x76, 0x07, 0x71, - 0x09, 0x6d, 0x0a, 0x69, 0x0c, 0x66, 0x0e, 0x63, 0x0f, 0x61, 0x10, 0x60, - 0x12, 0x5d, 0x13, 0x5c, 0x14, 0x59, 0x16, 0x58, 0x13, 0x79, 0x16, 0x72, - 0x19, 0x6d, 0x1c, 0x69, 0x1f, 0x65, 0x20, 0x61, 0x23, 0x60, 0x24, 0x5c, - 0x26, 0x5b, 0x27, 0x59, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, - 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2e, 0x51, 0x2e, 0x4f, 0x2f, 0x4f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, - 0x00, 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, - 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x23, - 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, 0x26, 0x28, 0x26, 0x28, 0x28, 0x2a, - 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2d, 0x2a, 0x2d, 0x2c, 0x2d, 0x2d, 0x2e, - 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, - 0x00, 0xd4, 0x00, 0xc4, 0x00, 0xb5, 0x00, 0xa7, 0x00, 0x9b, 0x00, 0x91, - 0x01, 0x88, 0x02, 0x82, 0x03, 0x7b, 0x05, 0x77, 0x07, 0x73, 0x08, 0x6e, - 0x0a, 0x6b, 0x0b, 0x69, 0x0c, 0x66, 0x0e, 0x63, 0x0f, 0x61, 0x10, 0x60, - 0x12, 0x5f, 0x13, 0x5c, 0x12, 0x79, 0x15, 0x73, 0x18, 0x6e, 0x1b, 0x6a, - 0x1d, 0x67, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x24, 0x5d, 0x26, 0x5c, - 0x27, 0x5a, 0x27, 0x58, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, - 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, - 0x00, 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, - 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, - 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, - 0x2a, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, - 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x30, 0x30, 0x30, 0x00, 0xd5, 0x00, 0xc7, - 0x00, 0xb9, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8f, 0x01, 0x87, - 0x02, 0x82, 0x03, 0x7b, 0x05, 0x77, 0x06, 0x74, 0x08, 0x6f, 0x09, 0x6c, - 0x0b, 0x6b, 0x0c, 0x68, 0x0d, 0x65, 0x0e, 0x63, 0x0f, 0x62, 0x10, 0x60, - 0x12, 0x7a, 0x15, 0x75, 0x18, 0x70, 0x1a, 0x6c, 0x1d, 0x69, 0x1e, 0x65, - 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x24, 0x5d, 0x26, 0x5c, 0x27, 0x5b, - 0x27, 0x58, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, - 0x2c, 0x53, 0x2e, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, - 0x00, 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, - 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, - 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, - 0x2d, 0x30, 0x2d, 0x30, 0x00, 0xd6, 0x00, 0xc9, 0x00, 0xbc, 0x00, 0xb1, - 0x00, 0xa6, 0x00, 0x9c, 0x00, 0x94, 0x00, 0x8c, 0x02, 0x86, 0x02, 0x81, - 0x03, 0x7c, 0x05, 0x78, 0x06, 0x75, 0x07, 0x71, 0x09, 0x6d, 0x0a, 0x6c, - 0x0b, 0x6a, 0x0c, 0x67, 0x0d, 0x65, 0x0e, 0x62, 0x12, 0x7b, 0x15, 0x76, - 0x18, 0x72, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x63, - 0x22, 0x60, 0x23, 0x60, 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5a, - 0x28, 0x57, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2a, 0x55, 0x2b, 0x53, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, - 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, - 0x00, 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, - 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x21, 0x22, 0x22, 0x23, - 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x27, 0x26, 0x27, - 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2c, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, - 0x00, 0xd6, 0x00, 0xcb, 0x00, 0xbf, 0x00, 0xb4, 0x00, 0xab, 0x00, 0xa1, - 0x00, 0x99, 0x00, 0x91, 0x01, 0x8b, 0x02, 0x85, 0x02, 0x81, 0x04, 0x7c, - 0x05, 0x78, 0x06, 0x76, 0x07, 0x73, 0x08, 0x6f, 0x09, 0x6c, 0x0b, 0x6b, - 0x0c, 0x6a, 0x0c, 0x67, 0x12, 0x7b, 0x15, 0x76, 0x17, 0x72, 0x19, 0x6e, - 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x66, 0x20, 0x65, 0x20, 0x62, 0x22, 0x60, - 0x23, 0x60, 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x27, 0x58, - 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, - 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, - 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, - 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, 0x00, - 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, - 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, - 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, - 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x00, 0xd7, 0x00, 0xcc, - 0x00, 0xc2, 0x00, 0xb8, 0x00, 0xae, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x95, - 0x00, 0x90, 0x01, 0x8a, 0x02, 0x85, 0x03, 0x81, 0x04, 0x7c, 0x05, 0x78, - 0x06, 0x76, 0x07, 0x74, 0x08, 0x70, 0x09, 0x6d, 0x0a, 0x6c, 0x0b, 0x6a, - 0x12, 0x7b, 0x14, 0x77, 0x16, 0x73, 0x18, 0x70, 0x1a, 0x6d, 0x1c, 0x6a, - 0x1d, 0x69, 0x1e, 0x65, 0x20, 0x65, 0x20, 0x62, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x59, 0x28, 0x57, - 0x2a, 0x57, 0x2a, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, - 0x00, 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, - 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, 0x00, - 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, - 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, 0x26, 0x28, 0x28, 0x28, - 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, - 0x2b, 0x2d, 0x2c, 0x2d, 0x00, 0xd7, 0x00, 0xcd, 0x00, 0xc4, 0x00, 0xbb, - 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, 0x00, 0x93, 0x00, 0x8e, - 0x01, 0x88, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7c, 0x05, 0x79, 0x06, 0x77, - 0x07, 0x75, 0x07, 0x72, 0x09, 0x6e, 0x09, 0x6d, 0x12, 0x7b, 0x14, 0x77, - 0x15, 0x73, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6b, 0x1d, 0x69, 0x1d, 0x67, - 0x1f, 0x65, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, - 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5a, 0x27, 0x58, 0x29, 0x57, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, - 0x00, 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, - 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, - 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, - 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, - 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x26, 0x28, 0x28, 0x28, 0x28, 0x29, - 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, - 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbd, 0x00, 0xb4, 0x00, 0xad, - 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x92, 0x01, 0x8d, 0x02, 0x88, - 0x02, 0x84, 0x03, 0x81, 0x04, 0x7d, 0x05, 0x79, 0x06, 0x77, 0x07, 0x75, - 0x07, 0x73, 0x09, 0x70, 0x12, 0x7c, 0x13, 0x78, 0x15, 0x75, 0x18, 0x72, - 0x19, 0x6e, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, - 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x27, 0x59, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, - 0x00, 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, - 0x00, 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, - 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, 0x00, - 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, - 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, - 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, - 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x00, 0xd8, 0x00, 0xd0, - 0x00, 0xc7, 0x00, 0xbe, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, - 0x00, 0x9b, 0x00, 0x95, 0x00, 0x90, 0x01, 0x8c, 0x02, 0x87, 0x02, 0x84, - 0x03, 0x81, 0x04, 0x7d, 0x05, 0x79, 0x06, 0x77, 0x06, 0x76, 0x07, 0x74, - 0x11, 0x7c, 0x13, 0x78, 0x15, 0x76, 0x17, 0x72, 0x18, 0x6f, 0x1a, 0x6d, - 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x65, 0x20, 0x64, - 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x00, 0x2f, 0x00, 0x5f, 0x00, 0x99, 0x00, 0xac, - 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, - 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, - 0x00, 0x9a, 0x00, 0x6d, 0x00, 0x8d, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0xb2, - 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, - 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x3b, 0x00, 0x6f, 0x00, 0x9f, 0x00, 0xaf, - 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, - 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, - 0x07, 0x47, 0x00, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, - 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, - 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, - 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, - 0x27, 0x29, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc0, - 0x00, 0xba, 0x00, 0xb1, 0x00, 0xac, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x98, - 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, - 0x04, 0x7d, 0x05, 0x79, 0x06, 0x78, 0x06, 0x76, 0x11, 0x7c, 0x13, 0x79, - 0x15, 0x76, 0x17, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, - 0x1d, 0x69, 0x1d, 0x66, 0x20, 0x65, 0x20, 0x65, 0x20, 0x63, 0x22, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x5f, 0x00, 0x8b, 0x00, 0x9f, 0x00, 0xaa, - 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, - 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x6d, 0x00, 0x54, - 0x00, 0x6b, 0x00, 0x87, 0x00, 0x98, 0x00, 0xa6, 0x00, 0xb0, 0x00, 0xb3, - 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, - 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x17, 0x07, 0x02, 0x27, 0x00, 0x6f, 0x00, 0x93, 0x00, 0xa4, 0x00, 0xad, - 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, - 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x0f, 0x0f, 0x00, 0x3f, - 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, - 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, - 0x00, 0xbd, 0x00, 0xbd, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, - 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, - 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, - 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xc9, 0x00, 0xc2, 0x00, 0xbb, 0x00, 0xb4, - 0x00, 0xae, 0x00, 0xa7, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x96, 0x00, 0x92, - 0x01, 0x8e, 0x01, 0x89, 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, - 0x05, 0x7a, 0x06, 0x78, 0x11, 0x7c, 0x13, 0x79, 0x15, 0x76, 0x16, 0x73, - 0x18, 0x72, 0x19, 0x6e, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x69, - 0x1e, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x62, 0x22, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x5f, 0x24, 0x5c, 0x26, 0x5c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x00, 0x44, 0x00, 0x6d, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, - 0x00, 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, - 0x00, 0xb6, 0x00, 0xb7, 0x00, 0x8d, 0x00, 0x6b, 0x00, 0x28, 0x00, 0x52, - 0x00, 0x70, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, 0xa6, 0x00, 0xab, - 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x12, 0x02, - 0x00, 0x22, 0x00, 0x58, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9c, 0x00, 0xa4, - 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, - 0x00, 0xb8, 0x00, 0xb9, 0x26, 0x00, 0x05, 0x05, 0x00, 0x3f, 0x00, 0x6d, - 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, - 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, - 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, - 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x00, 0xd9, 0x00, 0xd2, - 0x00, 0xca, 0x00, 0xc4, 0x00, 0xbd, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, - 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x91, 0x01, 0x8e, - 0x02, 0x89, 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, 0x05, 0x7a, - 0x11, 0x7c, 0x13, 0x7a, 0x15, 0x76, 0x15, 0x74, 0x18, 0x72, 0x18, 0x6f, - 0x1a, 0x6d, 0x1a, 0x6c, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x67, 0x1f, 0x65, - 0x20, 0x65, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x5f, 0x24, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x33, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, - 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, - 0x00, 0x9b, 0x00, 0x87, 0x00, 0x52, 0x00, 0x11, 0x00, 0x39, 0x00, 0x57, - 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa3, - 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x25, 0x00, 0x0b, 0x09, 0x00, 0x21, - 0x00, 0x4a, 0x00, 0x68, 0x00, 0x7d, 0x00, 0x8b, 0x00, 0x96, 0x00, 0x9d, - 0x00, 0xa3, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, - 0x33, 0x00, 0x1c, 0x00, 0x00, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, - 0x00, 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, - 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, - 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x2a, 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc6, - 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb1, 0x00, 0xac, 0x00, 0xa6, 0x00, 0xa1, - 0x00, 0x9d, 0x00, 0x97, 0x00, 0x93, 0x00, 0x90, 0x01, 0x8d, 0x02, 0x88, - 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, 0x11, 0x7c, 0x13, 0x7a, - 0x15, 0x76, 0x15, 0x75, 0x18, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, - 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, 0x65, - 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x28, - 0x00, 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, - 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0x98, - 0x00, 0x70, 0x00, 0x39, 0x00, 0x02, 0x00, 0x28, 0x00, 0x48, 0x00, 0x5f, - 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, 0x98, 0x00, 0x9d, - 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3a, 0x00, 0x2f, 0x00, 0x16, 0x00, 0x08, 0x0e, 0x00, 0x20, 0x00, 0x42, - 0x00, 0x5c, 0x00, 0x6f, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x92, 0x00, 0x99, - 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x38, 0x00, 0x2a, 0x00, - 0x09, 0x00, 0x00, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, - 0x00, 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, - 0x00, 0xab, 0x00, 0xad, 0x16, 0xa4, 0x01, 0xbb, 0x00, 0xc6, 0x00, 0xcc, - 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, - 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, - 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x05, 0x9d, 0x00, 0xbc, - 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xcf, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd5, - 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, - 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x02, 0xbd, 0x00, 0xcc, 0x00, 0xd1, 0x00, 0xd5, - 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, - 0x00, 0xd9, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xdb, - 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x3d, - 0x00, 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, - 0x00, 0x93, 0x00, 0x98, 0x00, 0xb2, 0x00, 0xa6, 0x00, 0x85, 0x00, 0x57, - 0x00, 0x28, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, 0x52, 0x00, 0x64, - 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, 0x93, 0x00, 0x98, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, - 0x23, 0x00, 0x0e, 0x02, 0x06, 0x12, 0x00, 0x20, 0x00, 0x3c, 0x00, 0x52, - 0x00, 0x64, 0x00, 0x73, 0x00, 0x7e, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x95, - 0x00, 0x9a, 0x00, 0x9e, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x00, 0x00, 0x05, - 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, - 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, - 0x23, 0x74, 0x09, 0x91, 0x02, 0xa3, 0x00, 0xaf, 0x00, 0xb7, 0x00, 0xbd, - 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, - 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd2, - 0x00, 0xd2, 0x00, 0xd3, 0x0d, 0x6b, 0x01, 0x8e, 0x00, 0xa4, 0x00, 0xb0, - 0x00, 0xb9, 0x00, 0xbe, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc8, 0x00, 0xca, - 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, - 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x06, 0xa4, 0x00, 0xb5, 0x00, 0xc0, 0x00, 0xc6, 0x00, 0xcb, 0x00, 0xcd, - 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, - 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd7, - 0x00, 0xd8, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, - 0x00, 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, - 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, 0x48, 0x00, 0x21, - 0x00, 0x01, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, 0x59, 0x00, 0x66, - 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x18, 0x00, - 0x0c, 0x07, 0x05, 0x14, 0x00, 0x20, 0x00, 0x37, 0x00, 0x4c, 0x00, 0x5c, - 0x00, 0x6a, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x92, - 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x00, 0x00, 0x0f, 0x00, 0x29, - 0x00, 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, - 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, 0x2a, 0x61, 0x11, 0x78, - 0x07, 0x8a, 0x03, 0x98, 0x01, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, - 0x00, 0xba, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc6, - 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, - 0x12, 0x51, 0x04, 0x73, 0x00, 0x89, 0x00, 0x9a, 0x00, 0xa4, 0x00, 0xad, - 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbb, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc3, - 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, - 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x09, 0x97, 0x02, 0xa8, - 0x00, 0xb3, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc7, 0x00, 0xca, - 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd1, - 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, 0x00, 0xd5, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, - 0x00, 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0xbb, 0x00, 0xb3, - 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x1c, 0x00, 0x00, - 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, 0x5d, 0x00, 0x68, - 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x20, 0x00, 0x10, 0x00, 0x0a, 0x0b, - 0x04, 0x16, 0x00, 0x20, 0x00, 0x34, 0x00, 0x46, 0x00, 0x56, 0x00, 0x62, - 0x00, 0x6d, 0x00, 0x77, 0x00, 0x7f, 0x00, 0x85, 0x3d, 0x00, 0x38, 0x00, - 0x2a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, - 0x00, 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, - 0x00, 0x8b, 0x00, 0x91, 0x2e, 0x59, 0x17, 0x6b, 0x0c, 0x7a, 0x06, 0x87, - 0x03, 0x92, 0x01, 0x9b, 0x00, 0xa2, 0x00, 0xa8, 0x00, 0xad, 0x00, 0xb1, - 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, - 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x15, 0x45, 0x08, 0x60, - 0x02, 0x77, 0x00, 0x87, 0x00, 0x94, 0x00, 0x9d, 0x00, 0xa4, 0x00, 0xaa, - 0x00, 0xaf, 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, - 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0a, 0x91, 0x04, 0x9e, 0x01, 0xaa, 0x00, 0xb2, - 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc6, 0x00, 0xc8, - 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, - 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, - 0x00, 0x60, 0x00, 0x6a, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xa6, 0x00, 0x8e, - 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, 0x00, 0x00, 0x16, - 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, 0x60, 0x00, 0x6a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, - 0x33, 0x00, 0x27, 0x00, 0x18, 0x00, 0x0d, 0x04, 0x08, 0x0e, 0x04, 0x17, - 0x00, 0x20, 0x00, 0x32, 0x00, 0x42, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x67, - 0x00, 0x70, 0x00, 0x78, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, - 0x0b, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, - 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, - 0x31, 0x53, 0x1c, 0x62, 0x11, 0x70, 0x0a, 0x7b, 0x06, 0x85, 0x03, 0x8e, - 0x02, 0x96, 0x00, 0x9c, 0x00, 0xa2, 0x00, 0xa7, 0x00, 0xab, 0x00, 0xae, - 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, - 0x00, 0xbe, 0x00, 0xbf, 0x17, 0x3d, 0x0b, 0x55, 0x04, 0x67, 0x01, 0x78, - 0x00, 0x85, 0x00, 0x90, 0x00, 0x98, 0x00, 0x9f, 0x00, 0xa5, 0x00, 0xaa, - 0x00, 0xad, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xbb, - 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0b, 0x8d, 0x05, 0x99, 0x02, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, - 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc7, - 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcd, - 0x00, 0xce, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, - 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, 0x7e, 0x00, 0x64, - 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x25, - 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, - 0x1f, 0x00, 0x12, 0x00, 0x0c, 0x07, 0x07, 0x10, 0x03, 0x18, 0x00, 0x20, - 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4c, 0x00, 0x57, 0x00, 0x61, 0x00, 0x6a, - 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x00, - 0x00, 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, - 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, 0x33, 0x50, 0x20, 0x5c, - 0x15, 0x68, 0x0e, 0x72, 0x09, 0x7c, 0x05, 0x84, 0x03, 0x8c, 0x02, 0x92, - 0x01, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, - 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, - 0x18, 0x38, 0x0d, 0x4c, 0x06, 0x5e, 0x03, 0x6c, 0x01, 0x7a, 0x00, 0x84, - 0x00, 0x8d, 0x00, 0x95, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, - 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, - 0x00, 0xba, 0x00, 0xbc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0c, 0x8a, 0x06, 0x94, - 0x03, 0x9d, 0x01, 0xa4, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb5, 0x00, 0xb9, - 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, - 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, - 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0xbd, 0x00, 0xb9, - 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, 0x59, 0x00, 0x41, - 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, - 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, - 0x0e, 0x02, 0x0a, 0x0a, 0x06, 0x11, 0x03, 0x19, 0x00, 0x1f, 0x00, 0x2e, - 0x00, 0x3c, 0x00, 0x48, 0x00, 0x53, 0x00, 0x5c, 0x3e, 0x00, 0x3c, 0x00, - 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x14, - 0x00, 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, - 0x00, 0x68, 0x00, 0x70, 0x34, 0x4e, 0x23, 0x58, 0x18, 0x62, 0x10, 0x6c, - 0x0b, 0x75, 0x08, 0x7c, 0x05, 0x83, 0x03, 0x8a, 0x02, 0x8f, 0x01, 0x95, - 0x00, 0x9a, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xac, - 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb5, 0x1a, 0x35, 0x0f, 0x47, - 0x08, 0x55, 0x04, 0x64, 0x02, 0x70, 0x00, 0x7a, 0x00, 0x83, 0x00, 0x8b, - 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, - 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb7, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x89, 0x07, 0x92, 0x04, 0x99, 0x02, 0xa0, - 0x01, 0xa6, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, - 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc5, - 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xca, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0x2d, 0x00, 0x39, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, - 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, 0x3a, 0x00, 0x25, - 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x39, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, - 0x38, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x0d, 0x05, - 0x09, 0x0c, 0x06, 0x13, 0x03, 0x19, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x3a, - 0x00, 0x45, 0x00, 0x4f, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, - 0x21, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, 0x0a, 0x00, 0x18, 0x00, 0x26, - 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, - 0x35, 0x4c, 0x26, 0x55, 0x1b, 0x5e, 0x13, 0x67, 0x0e, 0x6f, 0x0a, 0x76, - 0x07, 0x7d, 0x05, 0x82, 0x03, 0x88, 0x02, 0x8e, 0x02, 0x92, 0x01, 0x97, - 0x00, 0x9b, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, - 0x00, 0xac, 0x00, 0xaf, 0x1b, 0x33, 0x10, 0x42, 0x0a, 0x50, 0x05, 0x5c, - 0x03, 0x68, 0x01, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x8a, 0x00, 0x90, - 0x00, 0x96, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa8, - 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0d, 0x88, 0x08, 0x8f, 0x05, 0x96, 0x02, 0x9c, 0x01, 0xa2, 0x00, 0xa7, - 0x00, 0xac, 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xbb, - 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc5, - 0x00, 0xc6, 0x00, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, - 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, 0x98, 0x00, 0x85, - 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, 0x22, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x33, 0x00, - 0x2c, 0x00, 0x23, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x0c, 0x07, 0x08, 0x0e, - 0x05, 0x14, 0x02, 0x1a, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x42, - 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, - 0x0c, 0x00, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, - 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x36, 0x4a, 0x28, 0x52, - 0x1d, 0x5b, 0x16, 0x63, 0x10, 0x6a, 0x0c, 0x71, 0x09, 0x77, 0x07, 0x7d, - 0x05, 0x82, 0x03, 0x87, 0x02, 0x8c, 0x02, 0x90, 0x01, 0x94, 0x00, 0x99, - 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xaa, - 0x1b, 0x30, 0x12, 0x3e, 0x0c, 0x4b, 0x07, 0x57, 0x04, 0x61, 0x02, 0x6b, - 0x01, 0x73, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8f, 0x00, 0x93, - 0x00, 0x97, 0x00, 0x9c, 0x00, 0x9f, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0xa8, - 0x00, 0xaa, 0x00, 0xac, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x86, 0x09, 0x8d, - 0x06, 0x94, 0x03, 0x9a, 0x02, 0x9f, 0x01, 0xa4, 0x00, 0xa8, 0x00, 0xac, - 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, - 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0xbe, 0x00, 0xbb, - 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, 0x7b, 0x00, 0x68, - 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, - 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x26, 0x00, - 0x1d, 0x00, 0x14, 0x00, 0x0e, 0x03, 0x0b, 0x09, 0x08, 0x0f, 0x05, 0x15, - 0x02, 0x1a, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x3f, 0x00, 0x3d, 0x00, - 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, 0x00, - 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, - 0x00, 0x49, 0x00, 0x51, 0x37, 0x49, 0x2a, 0x51, 0x20, 0x58, 0x18, 0x5f, - 0x13, 0x66, 0x0f, 0x6c, 0x0b, 0x73, 0x09, 0x78, 0x07, 0x7e, 0x05, 0x82, - 0x03, 0x86, 0x02, 0x8b, 0x02, 0x8f, 0x01, 0x92, 0x01, 0x96, 0x00, 0x9a, - 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa4, 0x1c, 0x2f, 0x13, 0x3b, - 0x0d, 0x47, 0x09, 0x51, 0x05, 0x5c, 0x04, 0x65, 0x02, 0x6e, 0x01, 0x75, - 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8d, 0x00, 0x91, 0x00, 0x96, - 0x00, 0x99, 0x00, 0x9c, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0xa7, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x86, 0x09, 0x8c, 0x06, 0x92, 0x04, 0x97, - 0x02, 0x9c, 0x02, 0xa1, 0x01, 0xa5, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, - 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, - 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0d, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xad, - 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, 0x60, 0x00, 0x4f, - 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, - 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, - 0x10, 0x00, 0x0d, 0x05, 0x0a, 0x0b, 0x07, 0x10, 0x04, 0x16, 0x02, 0x1b, - 0x00, 0x1f, 0x00, 0x2a, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, - 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, - 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, - 0x38, 0x48, 0x2b, 0x4f, 0x22, 0x56, 0x1a, 0x5c, 0x15, 0x63, 0x11, 0x69, - 0x0d, 0x6e, 0x0a, 0x74, 0x08, 0x78, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x85, - 0x03, 0x8a, 0x02, 0x8e, 0x02, 0x90, 0x01, 0x93, 0x00, 0x98, 0x00, 0x9b, - 0x00, 0x9d, 0x00, 0x9f, 0x1c, 0x2d, 0x14, 0x39, 0x0e, 0x44, 0x0a, 0x4e, - 0x07, 0x57, 0x04, 0x60, 0x02, 0x67, 0x01, 0x6f, 0x00, 0x76, 0x00, 0x7d, - 0x00, 0x81, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, 0x00, 0x94, 0x00, 0x97, - 0x00, 0x9a, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0e, 0x85, 0x0a, 0x8b, 0x07, 0x90, 0x05, 0x95, 0x03, 0x9a, 0x02, 0x9e, - 0x01, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, - 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, - 0x00, 0xbe, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x98, - 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, 0x49, 0x00, 0x39, - 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, - 0x32, 0x00, 0x2c, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0e, 0x01, - 0x0c, 0x07, 0x09, 0x0c, 0x06, 0x11, 0x04, 0x16, 0x02, 0x1b, 0x00, 0x1f, - 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, - 0x1c, 0x00, 0x11, 0x00, 0x07, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x19, - 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, 0x38, 0x48, 0x2d, 0x4e, - 0x23, 0x54, 0x1c, 0x5a, 0x17, 0x5f, 0x12, 0x66, 0x0f, 0x6a, 0x0c, 0x71, - 0x0a, 0x74, 0x08, 0x79, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x89, - 0x02, 0x8d, 0x02, 0x8f, 0x01, 0x92, 0x01, 0x95, 0x00, 0x99, 0x00, 0x9b, - 0x1c, 0x2d, 0x15, 0x37, 0x0f, 0x41, 0x0b, 0x4a, 0x07, 0x53, 0x05, 0x5b, - 0x04, 0x63, 0x02, 0x69, 0x01, 0x70, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, - 0x00, 0x87, 0x00, 0x8a, 0x00, 0x8f, 0x00, 0x92, 0x00, 0x96, 0x00, 0x99, - 0x00, 0x9b, 0x00, 0x9e, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x85, 0x0a, 0x8a, - 0x07, 0x8f, 0x05, 0x93, 0x03, 0x98, 0x02, 0x9c, 0x02, 0xa0, 0x01, 0xa3, - 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb3, - 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x39, 0x47, 0x2e, 0x4d, 0x25, 0x53, 0x1e, 0x58, - 0x19, 0x5e, 0x14, 0x63, 0x11, 0x68, 0x0e, 0x6c, 0x0b, 0x72, 0x09, 0x75, - 0x07, 0x79, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x88, 0x02, 0x8c, - 0x02, 0x8e, 0x01, 0x91, 0x01, 0x93, 0x00, 0x97, 0x1d, 0x2c, 0x16, 0x36, - 0x10, 0x3e, 0x0c, 0x48, 0x09, 0x50, 0x06, 0x58, 0x04, 0x5f, 0x02, 0x66, - 0x02, 0x6c, 0x01, 0x71, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, - 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x84, 0x0b, 0x89, 0x08, 0x8d, 0x06, 0x92, - 0x04, 0x96, 0x03, 0x9a, 0x02, 0x9e, 0x01, 0xa1, 0x01, 0xa4, 0x00, 0xa7, - 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, - 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x39, 0x46, 0x2f, 0x4c, 0x26, 0x51, 0x20, 0x56, 0x1a, 0x5c, 0x16, 0x60, - 0x12, 0x66, 0x0f, 0x69, 0x0c, 0x6e, 0x0b, 0x73, 0x09, 0x76, 0x07, 0x7a, - 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x88, 0x02, 0x8b, 0x02, 0x8e, - 0x02, 0x90, 0x01, 0x92, 0x1d, 0x2b, 0x16, 0x34, 0x11, 0x3d, 0x0c, 0x45, - 0x0a, 0x4c, 0x07, 0x54, 0x05, 0x5b, 0x04, 0x61, 0x02, 0x67, 0x01, 0x6d, - 0x01, 0x73, 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, 0x89, - 0x00, 0x8d, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0e, 0x84, 0x0b, 0x88, 0x08, 0x8d, 0x06, 0x91, 0x05, 0x94, 0x03, 0x98, - 0x02, 0x9c, 0x02, 0x9f, 0x01, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, - 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, - 0x00, 0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x46, 0x30, 0x4b, - 0x28, 0x50, 0x21, 0x55, 0x1c, 0x59, 0x17, 0x5e, 0x14, 0x63, 0x10, 0x68, - 0x0e, 0x6b, 0x0c, 0x70, 0x0a, 0x74, 0x08, 0x76, 0x07, 0x7b, 0x06, 0x7e, - 0x05, 0x81, 0x04, 0x83, 0x03, 0x87, 0x02, 0x8a, 0x02, 0x8d, 0x02, 0x8f, - 0x1e, 0x2a, 0x17, 0x33, 0x12, 0x3b, 0x0e, 0x43, 0x0a, 0x4a, 0x07, 0x51, - 0x05, 0x58, 0x04, 0x5e, 0x03, 0x64, 0x02, 0x69, 0x01, 0x6f, 0x00, 0x74, - 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, - 0x00, 0x8f, 0x00, 0x93, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0b, 0x88, - 0x09, 0x8c, 0x07, 0x90, 0x05, 0x93, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, - 0x01, 0xa0, 0x01, 0xa3, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xad, - 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0x46, 0x31, 0x4a, 0x29, 0x4e, 0x22, 0x54, - 0x1d, 0x57, 0x19, 0x5d, 0x15, 0x60, 0x12, 0x65, 0x0f, 0x69, 0x0d, 0x6c, - 0x0b, 0x71, 0x09, 0x74, 0x08, 0x77, 0x07, 0x7b, 0x06, 0x7f, 0x05, 0x81, - 0x04, 0x83, 0x03, 0x86, 0x02, 0x8a, 0x02, 0x8c, 0x1e, 0x2a, 0x18, 0x32, - 0x12, 0x39, 0x0f, 0x40, 0x0c, 0x48, 0x09, 0x4f, 0x07, 0x55, 0x05, 0x5a, - 0x04, 0x60, 0x02, 0x66, 0x02, 0x6b, 0x01, 0x70, 0x00, 0x75, 0x00, 0x79, - 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8b, 0x07, 0x8e, - 0x06, 0x92, 0x04, 0x96, 0x03, 0x99, 0x02, 0x9b, 0x02, 0x9e, 0x01, 0xa1, - 0x01, 0xa4, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, - 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb5, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3a, 0x45, 0x31, 0x49, 0x2a, 0x4d, 0x24, 0x52, 0x1f, 0x56, 0x1a, 0x5b, - 0x16, 0x5f, 0x13, 0x63, 0x10, 0x67, 0x0e, 0x6a, 0x0c, 0x6e, 0x0b, 0x72, - 0x09, 0x75, 0x07, 0x77, 0x07, 0x7b, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, - 0x03, 0x85, 0x02, 0x89, 0x1e, 0x29, 0x18, 0x31, 0x13, 0x38, 0x0f, 0x3f, - 0x0c, 0x45, 0x0a, 0x4c, 0x07, 0x52, 0x05, 0x58, 0x04, 0x5d, 0x04, 0x63, - 0x02, 0x67, 0x01, 0x6c, 0x01, 0x71, 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, - 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8a, 0x07, 0x8e, 0x06, 0x91, 0x05, 0x94, - 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x02, 0xa0, 0x01, 0xa2, 0x00, 0xa4, - 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, - 0x00, 0xb2, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x45, 0x32, 0x48, - 0x2b, 0x4d, 0x25, 0x51, 0x20, 0x55, 0x1b, 0x59, 0x18, 0x5e, 0x14, 0x60, - 0x12, 0x65, 0x0f, 0x68, 0x0d, 0x6b, 0x0c, 0x6f, 0x0a, 0x73, 0x09, 0x75, - 0x07, 0x77, 0x06, 0x7b, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, - 0x1e, 0x29, 0x19, 0x30, 0x14, 0x37, 0x0f, 0x3d, 0x0c, 0x44, 0x0a, 0x4a, - 0x07, 0x50, 0x06, 0x55, 0x05, 0x5b, 0x04, 0x60, 0x02, 0x64, 0x02, 0x69, - 0x01, 0x6d, 0x01, 0x72, 0x00, 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, - 0x00, 0x83, 0x00, 0x87, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x86, - 0x0a, 0x8a, 0x07, 0x8d, 0x06, 0x90, 0x05, 0x93, 0x03, 0x96, 0x03, 0x99, - 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, 0x01, 0xa3, 0x00, 0xa5, 0x00, 0xa7, - 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb2, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3b, 0x45, 0x33, 0x48, 0x2c, 0x4d, 0x26, 0x4f, - 0x21, 0x55, 0x1c, 0x57, 0x19, 0x5c, 0x16, 0x5f, 0x13, 0x63, 0x10, 0x67, - 0x0e, 0x6a, 0x0c, 0x6c, 0x0b, 0x71, 0x0a, 0x73, 0x09, 0x76, 0x07, 0x78, - 0x06, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x1e, 0x28, 0x19, 0x2f, - 0x15, 0x35, 0x10, 0x3c, 0x0d, 0x42, 0x0b, 0x48, 0x09, 0x4e, 0x07, 0x53, - 0x05, 0x58, 0x04, 0x5d, 0x04, 0x62, 0x02, 0x66, 0x02, 0x6b, 0x01, 0x6f, - 0x00, 0x73, 0x00, 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x08, 0x8c, - 0x06, 0x8f, 0x05, 0x92, 0x04, 0x95, 0x03, 0x98, 0x02, 0x9a, 0x02, 0x9d, - 0x02, 0x9f, 0x01, 0xa1, 0x01, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xa9, - 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x44, 0x33, 0x47, 0x2c, 0x4c, 0x27, 0x4f, 0x22, 0x54, 0x1e, 0x56, - 0x1a, 0x5a, 0x17, 0x5e, 0x14, 0x60, 0x12, 0x65, 0x10, 0x68, 0x0e, 0x6a, - 0x0c, 0x6e, 0x0b, 0x72, 0x09, 0x74, 0x08, 0x76, 0x07, 0x78, 0x06, 0x7c, - 0x06, 0x7f, 0x05, 0x81, 0x1e, 0x28, 0x19, 0x2f, 0x16, 0x35, 0x12, 0x3b, - 0x0f, 0x41, 0x0c, 0x46, 0x0a, 0x4c, 0x07, 0x51, 0x05, 0x55, 0x05, 0x5b, - 0x04, 0x5f, 0x02, 0x63, 0x02, 0x68, 0x01, 0x6b, 0x01, 0x70, 0x00, 0x73, - 0x00, 0x77, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x81, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0f, 0x82, 0x0c, 0x86, 0x0b, 0x89, 0x09, 0x8c, 0x07, 0x8f, 0x06, 0x91, - 0x05, 0x94, 0x03, 0x97, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, - 0x01, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xac, - 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x23, 0x25, 0x22, - 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0x44, 0x05, 0x25, 0x0d, 0x23, 0x12, 0x22, - 0x15, 0x22, 0x17, 0x22, 0x18, 0x22, 0x1a, 0x22, 0x1b, 0x22, 0x1b, 0x22, - 0x1c, 0x22, 0x1c, 0x22, 0x1c, 0x22, 0x1d, 0x22, 0x1d, 0x22, 0x1e, 0x22, - 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x10, 0x33, 0x13, 0x23, - 0x17, 0x22, 0x1a, 0x22, 0x1b, 0x22, 0x1c, 0x22, 0x1d, 0x22, 0x1e, 0x22, - 0x1e, 0x22, 0x1e, 0x22, 0x1f, 0x22, 0x1f, 0x22, 0x1f, 0x22, 0x1f, 0x22, - 0x1f, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x35, 0x25, 0x2b, 0x23, 0x27, 0x22, 0x25, 0x22, - 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0x9d, 0x00, 0x6b, 0x01, 0x51, 0x04, 0x45, 0x08, 0x3d, 0x0b, 0x38, - 0x0d, 0x35, 0x0f, 0x33, 0x10, 0x30, 0x12, 0x2f, 0x13, 0x2d, 0x14, 0x2d, - 0x15, 0x2c, 0x16, 0x2b, 0x16, 0x2a, 0x17, 0x2a, 0x18, 0x29, 0x18, 0x29, - 0x19, 0x28, 0x19, 0x28, 0x10, 0x5f, 0x10, 0x46, 0x11, 0x39, 0x13, 0x33, - 0x15, 0x2f, 0x16, 0x2d, 0x17, 0x2b, 0x18, 0x2a, 0x19, 0x29, 0x1a, 0x28, - 0x1a, 0x27, 0x1b, 0x27, 0x1b, 0x27, 0x1c, 0x26, 0x1c, 0x26, 0x1c, 0x26, - 0x1d, 0x25, 0x1d, 0x25, 0x1d, 0x25, 0x1d, 0x25, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x2a, 0x2e, 0x26, 0x2a, 0x25, 0x27, 0x24, 0x26, 0x23, 0x25, 0x23, - 0x24, 0x23, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, - 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xbc, 0x00, 0x8e, - 0x00, 0x73, 0x00, 0x60, 0x02, 0x55, 0x04, 0x4c, 0x06, 0x47, 0x08, 0x42, - 0x0a, 0x3e, 0x0c, 0x3b, 0x0d, 0x39, 0x0e, 0x37, 0x0f, 0x36, 0x10, 0x34, - 0x11, 0x33, 0x12, 0x32, 0x12, 0x31, 0x13, 0x30, 0x14, 0x2f, 0x15, 0x2f, - 0x10, 0x6f, 0x10, 0x58, 0x10, 0x4a, 0x11, 0x41, 0x12, 0x3b, 0x13, 0x37, - 0x14, 0x34, 0x15, 0x32, 0x16, 0x30, 0x17, 0x2e, 0x17, 0x2d, 0x18, 0x2c, - 0x18, 0x2c, 0x19, 0x2b, 0x19, 0x2a, 0x1a, 0x2a, 0x1a, 0x29, 0x1a, 0x29, - 0x1b, 0x28, 0x1b, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x2e, 0x31, 0x29, - 0x2c, 0x27, 0x2a, 0x26, 0x28, 0x25, 0x27, 0x24, 0x25, 0x24, 0x25, 0x23, - 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x22, 0x23, 0x22, - 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xc6, 0x00, 0xa4, 0x00, 0x89, 0x00, 0x77, - 0x00, 0x67, 0x01, 0x5e, 0x03, 0x55, 0x04, 0x50, 0x05, 0x4b, 0x07, 0x47, - 0x09, 0x44, 0x0a, 0x41, 0x0b, 0x3e, 0x0c, 0x3d, 0x0c, 0x3b, 0x0e, 0x39, - 0x0f, 0x38, 0x0f, 0x37, 0x0f, 0x35, 0x10, 0x35, 0x10, 0x74, 0x10, 0x63, - 0x10, 0x55, 0x10, 0x4c, 0x11, 0x44, 0x11, 0x40, 0x12, 0x3b, 0x13, 0x39, - 0x13, 0x36, 0x14, 0x34, 0x15, 0x33, 0x16, 0x31, 0x16, 0x30, 0x17, 0x2f, - 0x17, 0x2e, 0x18, 0x2d, 0x18, 0x2d, 0x18, 0x2c, 0x18, 0x2b, 0x19, 0x2b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0x31, 0x33, 0x2c, 0x2f, 0x29, 0x2b, 0x28, - 0x29, 0x26, 0x28, 0x25, 0x27, 0x25, 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, - 0x25, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xcd, 0x00, 0xb0, 0x00, 0x9a, 0x00, 0x87, 0x00, 0x78, 0x00, 0x6c, - 0x01, 0x64, 0x02, 0x5c, 0x03, 0x57, 0x04, 0x51, 0x05, 0x4e, 0x07, 0x4a, - 0x07, 0x48, 0x09, 0x45, 0x0a, 0x43, 0x0a, 0x40, 0x0c, 0x3f, 0x0c, 0x3d, - 0x0c, 0x3c, 0x0d, 0x3b, 0x10, 0x77, 0x10, 0x69, 0x10, 0x5e, 0x10, 0x54, - 0x10, 0x4d, 0x11, 0x47, 0x11, 0x43, 0x12, 0x3f, 0x12, 0x3c, 0x13, 0x39, - 0x13, 0x38, 0x14, 0x36, 0x14, 0x35, 0x15, 0x33, 0x16, 0x32, 0x16, 0x31, - 0x17, 0x30, 0x17, 0x2f, 0x17, 0x2f, 0x17, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x33, 0x34, 0x2e, 0x30, 0x2b, 0x2d, 0x29, 0x2b, 0x28, 0x29, 0x27, - 0x29, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, - 0x25, 0x24, 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xcf, 0x00, 0xb9, - 0x00, 0xa4, 0x00, 0x94, 0x00, 0x85, 0x00, 0x7a, 0x00, 0x70, 0x00, 0x68, - 0x01, 0x61, 0x02, 0x5c, 0x04, 0x57, 0x04, 0x53, 0x05, 0x50, 0x06, 0x4c, - 0x07, 0x4a, 0x07, 0x48, 0x09, 0x45, 0x0a, 0x44, 0x0a, 0x42, 0x0b, 0x41, - 0x10, 0x78, 0x10, 0x6d, 0x10, 0x63, 0x10, 0x5b, 0x10, 0x53, 0x10, 0x4e, - 0x11, 0x49, 0x11, 0x45, 0x11, 0x41, 0x12, 0x3f, 0x13, 0x3c, 0x13, 0x3a, - 0x13, 0x39, 0x14, 0x37, 0x14, 0x36, 0x14, 0x35, 0x15, 0x33, 0x16, 0x33, - 0x16, 0x32, 0x16, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x34, 0x36, 0x30, - 0x32, 0x2d, 0x2f, 0x2b, 0x2d, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, - 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, - 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xd2, 0x00, 0xbe, 0x00, 0xad, 0x00, 0x9d, - 0x00, 0x90, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x6b, 0x01, 0x65, - 0x02, 0x60, 0x02, 0x5b, 0x04, 0x58, 0x04, 0x54, 0x05, 0x51, 0x05, 0x4f, - 0x07, 0x4c, 0x07, 0x4a, 0x07, 0x48, 0x09, 0x46, 0x10, 0x7a, 0x10, 0x70, - 0x10, 0x67, 0x10, 0x5f, 0x10, 0x59, 0x10, 0x53, 0x10, 0x4e, 0x11, 0x4a, - 0x11, 0x46, 0x11, 0x43, 0x12, 0x41, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, - 0x13, 0x39, 0x13, 0x38, 0x14, 0x37, 0x14, 0x36, 0x14, 0x35, 0x15, 0x34, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x35, 0x37, 0x31, 0x33, 0x2e, 0x30, 0x2c, - 0x2d, 0x2a, 0x2c, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, 0x28, 0x27, - 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, - 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xd3, 0x00, 0xc2, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x98, 0x00, 0x8d, - 0x00, 0x83, 0x00, 0x7b, 0x00, 0x73, 0x00, 0x6e, 0x01, 0x67, 0x01, 0x63, - 0x02, 0x5f, 0x02, 0x5b, 0x04, 0x58, 0x04, 0x55, 0x05, 0x52, 0x05, 0x50, - 0x06, 0x4e, 0x07, 0x4c, 0x10, 0x7a, 0x10, 0x72, 0x10, 0x6a, 0x10, 0x63, - 0x10, 0x5d, 0x10, 0x57, 0x10, 0x52, 0x10, 0x4e, 0x11, 0x4a, 0x11, 0x48, - 0x11, 0x44, 0x11, 0x42, 0x12, 0x40, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, - 0x13, 0x3a, 0x13, 0x39, 0x14, 0x38, 0x14, 0x37, 0x0f, 0x00, 0x1f, 0x00, - 0x33, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, - 0x3f, 0x00, 0x3f, 0x00, 0x0b, 0x3b, 0x17, 0x07, 0x2c, 0x00, 0x36, 0x00, - 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x3d, 0x00, - 0x4c, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x42, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, - 0x3f, 0x00, 0x3f, 0x00, 0x07, 0x47, 0x0f, 0x0f, 0x26, 0x00, 0x33, 0x00, - 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, - 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, - 0x3c, 0x37, 0x37, 0x32, 0x34, 0x30, 0x31, 0x2d, 0x2f, 0x2c, 0x2d, 0x2b, - 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, 0x28, 0x27, 0x27, 0x26, - 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, - 0x25, 0x24, 0x24, 0x24, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd5, 0x00, 0xc5, - 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x9f, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x83, - 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6f, 0x00, 0x69, 0x01, 0x66, 0x02, 0x61, - 0x02, 0x5e, 0x03, 0x5a, 0x04, 0x58, 0x04, 0x55, 0x05, 0x53, 0x05, 0x51, - 0x10, 0x7b, 0x10, 0x73, 0x10, 0x6c, 0x10, 0x66, 0x10, 0x60, 0x10, 0x5b, - 0x10, 0x56, 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4b, 0x11, 0x48, 0x11, 0x45, - 0x11, 0x44, 0x12, 0x41, 0x12, 0x40, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, - 0x13, 0x3a, 0x13, 0x39, 0x00, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x2e, 0x00, - 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, - 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x00, 0x6f, 0x02, 0x27, 0x12, 0x02, 0x25, 0x00, 0x2f, 0x00, 0x35, 0x00, - 0x38, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, - 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x30, 0x00, 0x39, 0x00, 0x42, 0x00, - 0x41, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, - 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x00, 0x7f, 0x00, 0x3f, 0x05, 0x05, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, - 0x35, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, - 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3c, 0x37, 0x38, 0x33, - 0x34, 0x31, 0x32, 0x2f, 0x30, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x29, - 0x2b, 0x28, 0x29, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x25, - 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xd5, 0x00, 0xc8, 0x00, 0xbb, 0x00, 0xaf, - 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x92, 0x00, 0x8a, 0x00, 0x82, 0x00, 0x7c, - 0x00, 0x76, 0x00, 0x70, 0x00, 0x6c, 0x01, 0x67, 0x01, 0x64, 0x02, 0x60, - 0x02, 0x5d, 0x04, 0x5b, 0x04, 0x58, 0x04, 0x55, 0x10, 0x7b, 0x10, 0x75, - 0x10, 0x6e, 0x10, 0x68, 0x10, 0x63, 0x10, 0x5e, 0x10, 0x5a, 0x10, 0x56, - 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4c, 0x11, 0x49, 0x11, 0x47, 0x11, 0x44, - 0x11, 0x43, 0x12, 0x41, 0x12, 0x3f, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, 0x00, 0x24, 0x00, 0x2c, 0x00, - 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, - 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0x9f, 0x00, 0x6f, - 0x00, 0x22, 0x0b, 0x09, 0x16, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, - 0x33, 0x00, 0x35, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, - 0x3b, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x39, 0x00, 0x16, 0x00, 0x28, 0x00, 0x2f, 0x00, 0x2e, 0x00, - 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, - 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0xa5, 0x00, 0x7f, - 0x00, 0x3f, 0x00, 0x12, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, - 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, - 0x3a, 0x00, 0x3a, 0x00, 0x3d, 0x38, 0x39, 0x34, 0x35, 0x32, 0x33, 0x30, - 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x29, 0x2b, 0x28, - 0x29, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, - 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x25, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xd6, 0x00, 0xca, 0x00, 0xbe, 0x00, 0xb3, 0x00, 0xaa, 0x00, 0xa0, - 0x00, 0x98, 0x00, 0x90, 0x00, 0x88, 0x00, 0x82, 0x00, 0x7d, 0x00, 0x77, - 0x00, 0x71, 0x00, 0x6d, 0x01, 0x69, 0x01, 0x66, 0x02, 0x63, 0x02, 0x60, - 0x02, 0x5d, 0x04, 0x5b, 0x10, 0x7c, 0x10, 0x76, 0x10, 0x70, 0x10, 0x6a, - 0x10, 0x65, 0x10, 0x61, 0x10, 0x5d, 0x10, 0x59, 0x10, 0x55, 0x10, 0x52, - 0x10, 0x4f, 0x11, 0x4c, 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x11, 0x44, - 0x12, 0x42, 0x12, 0x41, 0x12, 0x3f, 0x13, 0x3e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, - 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, - 0x39, 0x00, 0x3a, 0x00, 0x00, 0xaf, 0x00, 0x93, 0x00, 0x58, 0x00, 0x21, - 0x08, 0x0e, 0x0e, 0x02, 0x18, 0x00, 0x20, 0x00, 0x27, 0x00, 0x2b, 0x00, - 0x2f, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00, 0x36, 0x00, 0x38, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x42, 0x00, - 0x28, 0x00, 0x09, 0x00, 0x16, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, - 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, - 0x39, 0x00, 0x3a, 0x00, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, - 0x00, 0x1d, 0x00, 0x05, 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, - 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, - 0x3d, 0x38, 0x39, 0x35, 0x36, 0x33, 0x33, 0x30, 0x31, 0x2f, 0x30, 0x2d, - 0x2e, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x2b, 0x28, 0x28, 0x28, - 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, - 0x27, 0x25, 0x25, 0x25, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd6, 0x00, 0xcc, - 0x00, 0xc0, 0x00, 0xb7, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x96, - 0x00, 0x8f, 0x00, 0x88, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x77, 0x00, 0x73, - 0x00, 0x6f, 0x00, 0x6b, 0x01, 0x67, 0x01, 0x64, 0x02, 0x62, 0x02, 0x5f, - 0x10, 0x7c, 0x10, 0x77, 0x10, 0x71, 0x10, 0x6c, 0x10, 0x67, 0x10, 0x63, - 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x58, 0x10, 0x55, 0x10, 0x51, 0x10, 0x4f, - 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x11, 0x44, 0x11, 0x43, - 0x12, 0x42, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, 0x00, - 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, 0x00, - 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x7a, 0x00, 0x4a, 0x00, 0x20, 0x06, 0x12, - 0x0c, 0x07, 0x10, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x24, 0x00, 0x28, 0x00, - 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x41, 0x00, 0x2f, 0x00, 0x16, 0x00, - 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, 0x00, - 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, 0x00, - 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, - 0x00, 0x0f, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, - 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, 0x00, 0x3d, 0x39, 0x3a, 0x36, - 0x36, 0x33, 0x34, 0x31, 0x32, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2b, - 0x2d, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x27, - 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xd7, 0x00, 0xcc, 0x00, 0xc3, 0x00, 0xba, - 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8d, - 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x74, 0x00, 0x70, - 0x00, 0x6c, 0x01, 0x69, 0x01, 0x66, 0x02, 0x63, 0x10, 0x7c, 0x10, 0x77, - 0x10, 0x72, 0x10, 0x6e, 0x10, 0x69, 0x10, 0x65, 0x10, 0x61, 0x10, 0x5d, - 0x10, 0x5a, 0x10, 0x57, 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, - 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x11, 0x44, 0x12, 0x42, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, - 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0xb9, 0x00, 0xad, - 0x00, 0x8f, 0x00, 0x68, 0x00, 0x42, 0x00, 0x20, 0x05, 0x14, 0x0a, 0x0b, - 0x0d, 0x04, 0x12, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x26, 0x00, - 0x29, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x3d, 0x00, 0x2e, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, - 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0xba, 0x00, 0xb1, - 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, - 0x00, 0x08, 0x02, 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, - 0x22, 0x00, 0x25, 0x00, 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x32, - 0x33, 0x30, 0x31, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2a, 0x2c, 0x2b, - 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, - 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbc, 0x00, 0xb4, 0x00, 0xac, - 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x91, 0x00, 0x8c, 0x00, 0x87, - 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x75, 0x00, 0x71, 0x00, 0x6d, - 0x01, 0x6b, 0x01, 0x68, 0x10, 0x7c, 0x10, 0x78, 0x10, 0x73, 0x10, 0x6f, - 0x10, 0x6b, 0x10, 0x67, 0x10, 0x63, 0x10, 0x60, 0x10, 0x5c, 0x10, 0x59, - 0x10, 0x57, 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, - 0x11, 0x49, 0x11, 0x47, 0x11, 0x46, 0x11, 0x45, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, - 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, - 0x2b, 0x00, 0x2d, 0x00, 0x00, 0xba, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x7d, - 0x00, 0x5c, 0x00, 0x3c, 0x00, 0x20, 0x04, 0x16, 0x08, 0x0e, 0x0c, 0x07, - 0x0e, 0x02, 0x13, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3a, 0x00, - 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, 0x00, - 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, - 0x2b, 0x00, 0x2d, 0x00, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, - 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, - 0x00, 0x04, 0x04, 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, - 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x33, 0x33, 0x30, 0x32, 0x30, - 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2c, 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x2a, - 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd8, 0x00, 0xcf, - 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb6, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, - 0x00, 0x9c, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, 0x86, 0x00, 0x81, - 0x00, 0x7d, 0x00, 0x79, 0x00, 0x75, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6b, - 0x10, 0x7d, 0x10, 0x78, 0x10, 0x74, 0x10, 0x70, 0x10, 0x6c, 0x10, 0x68, - 0x10, 0x65, 0x10, 0x61, 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x59, 0x10, 0x56, - 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x4a, - 0x11, 0x48, 0x11, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, - 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, 0x00, - 0x00, 0xbc, 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x6f, 0x00, 0x52, - 0x00, 0x37, 0x00, 0x20, 0x04, 0x17, 0x07, 0x10, 0x0a, 0x0a, 0x0d, 0x05, - 0x0f, 0x00, 0x14, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, - 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, - 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, 0x00, - 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, - 0x00, 0x53, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, - 0x00, 0x01, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x3d, 0x3a, 0x3a, 0x37, - 0x38, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x2f, 0x30, 0x2d, - 0x2e, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, - 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, 0x27, 0x27, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc8, 0x00, 0xc0, - 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa5, 0x00, 0x9f, 0x00, 0x99, - 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8a, 0x00, 0x86, 0x00, 0x81, 0x00, 0x7d, - 0x00, 0x79, 0x00, 0x76, 0x00, 0x73, 0x00, 0x70, 0x10, 0x7d, 0x10, 0x79, - 0x10, 0x75, 0x10, 0x71, 0x10, 0x6d, 0x10, 0x6a, 0x10, 0x66, 0x10, 0x63, - 0x10, 0x60, 0x10, 0x5d, 0x10, 0x5b, 0x10, 0x58, 0x10, 0x56, 0x10, 0x54, - 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x49, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, - 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0xbc, 0x00, 0xb8, - 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7e, 0x00, 0x64, 0x00, 0x4c, 0x00, 0x34, - 0x00, 0x20, 0x03, 0x18, 0x06, 0x11, 0x09, 0x0c, 0x0c, 0x07, 0x0e, 0x03, - 0x10, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, - 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, - 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0xbd, 0x00, 0xb9, - 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, - 0x00, 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, - 0x00, 0x00, 0x07, 0x00, 0x3d, 0x3a, 0x3a, 0x38, 0x38, 0x36, 0x36, 0x33, - 0x34, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x28, 0x27, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc2, 0x00, 0xbb, 0x00, 0xb4, - 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x92, - 0x00, 0x8e, 0x00, 0x89, 0x00, 0x85, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, - 0x00, 0x76, 0x00, 0x73, 0x10, 0x7d, 0x10, 0x79, 0x10, 0x75, 0x10, 0x72, - 0x10, 0x6e, 0x10, 0x6b, 0x10, 0x68, 0x10, 0x65, 0x10, 0x62, 0x10, 0x5f, - 0x10, 0x5c, 0x10, 0x5a, 0x10, 0x58, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, - 0x10, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, 0x4a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, - 0x1a, 0x00, 0x1d, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9d, - 0x00, 0x89, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, - 0x03, 0x19, 0x06, 0x13, 0x08, 0x0e, 0x0b, 0x09, 0x0d, 0x05, 0x0e, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, - 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, - 0x1a, 0x00, 0x1d, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, - 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, - 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, - 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x36, 0x34, 0x35, 0x33, 0x33, 0x31, - 0x32, 0x30, 0x30, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2c, 0x2d, 0x2a, - 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd9, 0x00, 0xd1, - 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, - 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8d, - 0x00, 0x89, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x77, - 0x10, 0x7d, 0x10, 0x79, 0x10, 0x76, 0x10, 0x72, 0x10, 0x6f, 0x10, 0x6c, - 0x10, 0x69, 0x10, 0x66, 0x10, 0x63, 0x10, 0x61, 0x10, 0x5e, 0x10, 0x5c, - 0x10, 0x59, 0x10, 0x57, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, - 0x11, 0x4e, 0x11, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, - 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x92, 0x00, 0x7e, - 0x00, 0x6a, 0x00, 0x56, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x03, 0x19, - 0x05, 0x14, 0x08, 0x0f, 0x0a, 0x0b, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, - 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, - 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, - 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, - 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, 0x3d, 0x3b, 0x3b, 0x39, - 0x3a, 0x36, 0x36, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x30, - 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc4, - 0x00, 0xbe, 0x00, 0xb8, 0x00, 0xb2, 0x00, 0xad, 0x00, 0xa8, 0x00, 0xa2, - 0x00, 0x9d, 0x00, 0x99, 0x00, 0x94, 0x00, 0x90, 0x00, 0x8b, 0x00, 0x88, - 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7b, 0x10, 0x7d, 0x10, 0x7a, - 0x10, 0x76, 0x10, 0x73, 0x10, 0x70, 0x10, 0x6d, 0x10, 0x6a, 0x10, 0x67, - 0x10, 0x65, 0x10, 0x62, 0x10, 0x5f, 0x10, 0x5d, 0x10, 0x5b, 0x10, 0x59, - 0x10, 0x56, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0xbd, 0x00, 0xbb, - 0x00, 0xb3, 0x00, 0xa8, 0x00, 0x99, 0x00, 0x87, 0x00, 0x75, 0x00, 0x62, - 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x02, 0x1a, 0x05, 0x15, - 0x07, 0x10, 0x09, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, - 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0xbe, 0x00, 0xbc, - 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, - 0x00, 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, - 0x00, 0x21, 0x00, 0x19, 0x3d, 0x3b, 0x3b, 0x39, 0x39, 0x36, 0x37, 0x35, - 0x36, 0x33, 0x33, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2f, 0x30, 0x2d, - 0x2e, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcc, 0x00, 0xc6, 0x00, 0xc0, 0x00, 0xba, - 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9b, - 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x00, 0x8b, 0x00, 0x88, 0x00, 0x83, - 0x00, 0x81, 0x00, 0x7d, 0x10, 0x7d, 0x10, 0x7a, 0x10, 0x77, 0x10, 0x74, - 0x10, 0x71, 0x10, 0x6e, 0x10, 0x6b, 0x10, 0x68, 0x10, 0x65, 0x10, 0x63, - 0x10, 0x61, 0x10, 0x5e, 0x10, 0x5c, 0x10, 0x5a, 0x10, 0x58, 0x10, 0x56, - 0x10, 0x55, 0x10, 0x52, 0x10, 0x51, 0x10, 0x4f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, - 0x09, 0x00, 0x0d, 0x00, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xab, - 0x00, 0x9e, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, - 0x00, 0x3c, 0x00, 0x2d, 0x00, 0x1f, 0x02, 0x1a, 0x04, 0x16, 0x06, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, - 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, 0x00, - 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, - 0x09, 0x00, 0x0d, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, - 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, - 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, - 0x27, 0x76, 0x1a, 0x79, 0x16, 0x7b, 0x15, 0x7c, 0x14, 0x7c, 0x13, 0x7d, - 0x13, 0x7d, 0x13, 0x7d, 0x12, 0x7d, 0x12, 0x7d, 0x12, 0x7e, 0x12, 0x7e, - 0x12, 0x7e, 0x12, 0x7e, 0x12, 0x7e, 0x11, 0x7e, 0x11, 0x7e, 0x11, 0x7e, - 0x11, 0x7f, 0x11, 0x7f, 0x13, 0x5f, 0x10, 0x6f, 0x10, 0x74, 0x10, 0x77, - 0x10, 0x78, 0x10, 0x7a, 0x10, 0x7a, 0x10, 0x7b, 0x10, 0x7b, 0x10, 0x7c, - 0x10, 0x7c, 0x10, 0x7c, 0x10, 0x7c, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, - 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x00, 0x90, 0x02, 0x81, - 0x06, 0x80, 0x09, 0x7f, 0x0a, 0x7f, 0x0b, 0x7f, 0x0c, 0x7f, 0x0d, 0x7f, - 0x0d, 0x7f, 0x0d, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, - 0x0e, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, - 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x95, - 0x00, 0x86, 0x00, 0x77, 0x00, 0x67, 0x00, 0x57, 0x00, 0x48, 0x00, 0x3a, - 0x00, 0x2c, 0x00, 0x1f, 0x02, 0x1b, 0x04, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, - 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, 0x00, - 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, - 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, - 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, - 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, 0x32, 0x5f, 0x25, 0x68, - 0x1f, 0x6d, 0x1c, 0x71, 0x19, 0x72, 0x18, 0x74, 0x17, 0x76, 0x16, 0x77, - 0x16, 0x77, 0x15, 0x78, 0x15, 0x79, 0x15, 0x79, 0x14, 0x7a, 0x14, 0x7a, - 0x13, 0x7a, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, - 0x17, 0x46, 0x11, 0x58, 0x10, 0x63, 0x10, 0x69, 0x10, 0x6d, 0x10, 0x70, - 0x10, 0x72, 0x10, 0x73, 0x10, 0x75, 0x10, 0x76, 0x10, 0x77, 0x10, 0x77, - 0x10, 0x78, 0x10, 0x78, 0x10, 0x79, 0x10, 0x79, 0x10, 0x79, 0x10, 0x7a, - 0x10, 0x7a, 0x10, 0x7a, 0x00, 0xbd, 0x00, 0xa4, 0x00, 0x97, 0x02, 0x91, - 0x04, 0x8d, 0x05, 0x8a, 0x06, 0x89, 0x07, 0x88, 0x08, 0x86, 0x09, 0x86, - 0x09, 0x85, 0x0a, 0x85, 0x0a, 0x84, 0x0b, 0x84, 0x0b, 0x83, 0x0b, 0x83, - 0x0c, 0x83, 0x0c, 0x83, 0x0c, 0x82, 0x0c, 0x82, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x00, 0xbc, - 0x00, 0xb8, 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9a, 0x00, 0x8d, 0x00, 0x7f, - 0x00, 0x70, 0x00, 0x61, 0x00, 0x53, 0x00, 0x45, 0x00, 0x38, 0x00, 0x2b, - 0x00, 0x1f, 0x02, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, - 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, - 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x00, 0xbd, - 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, - 0x00, 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, - 0x00, 0x3f, 0x00, 0x36, 0x36, 0x56, 0x2a, 0x5f, 0x24, 0x65, 0x20, 0x68, - 0x1e, 0x6c, 0x1c, 0x6e, 0x1b, 0x70, 0x19, 0x71, 0x18, 0x72, 0x18, 0x73, - 0x18, 0x75, 0x17, 0x75, 0x16, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, 0x77, - 0x15, 0x77, 0x15, 0x77, 0x15, 0x78, 0x15, 0x79, 0x1a, 0x39, 0x13, 0x4a, - 0x11, 0x55, 0x10, 0x5e, 0x10, 0x63, 0x10, 0x67, 0x10, 0x6a, 0x10, 0x6c, - 0x10, 0x6e, 0x10, 0x70, 0x10, 0x71, 0x10, 0x72, 0x10, 0x73, 0x10, 0x74, - 0x10, 0x75, 0x10, 0x75, 0x10, 0x76, 0x10, 0x76, 0x10, 0x77, 0x10, 0x77, - 0x00, 0xcc, 0x00, 0xb5, 0x00, 0xa8, 0x00, 0x9e, 0x01, 0x99, 0x02, 0x94, - 0x03, 0x92, 0x04, 0x8f, 0x05, 0x8d, 0x06, 0x8c, 0x06, 0x8b, 0x07, 0x8a, - 0x07, 0x89, 0x08, 0x88, 0x08, 0x88, 0x09, 0x87, 0x09, 0x87, 0x09, 0x86, - 0x0a, 0x86, 0x0a, 0x86, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb2, - 0x00, 0xa9, 0x00, 0x9e, 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6a, - 0x00, 0x5c, 0x00, 0x4f, 0x00, 0x42, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, - 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, 0x00, - 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, - 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, - 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, - 0x38, 0x52, 0x2e, 0x59, 0x28, 0x5f, 0x24, 0x63, 0x22, 0x67, 0x1f, 0x69, - 0x1e, 0x6b, 0x1c, 0x6d, 0x1b, 0x6e, 0x1a, 0x70, 0x1a, 0x71, 0x19, 0x72, - 0x18, 0x72, 0x18, 0x72, 0x18, 0x73, 0x17, 0x75, 0x17, 0x76, 0x16, 0x76, - 0x15, 0x76, 0x15, 0x76, 0x1b, 0x33, 0x15, 0x41, 0x12, 0x4c, 0x11, 0x54, - 0x10, 0x5b, 0x10, 0x5f, 0x10, 0x63, 0x10, 0x66, 0x10, 0x68, 0x10, 0x6a, - 0x10, 0x6c, 0x10, 0x6e, 0x10, 0x6f, 0x10, 0x70, 0x10, 0x71, 0x10, 0x72, - 0x10, 0x72, 0x10, 0x73, 0x10, 0x74, 0x10, 0x74, 0x00, 0xd1, 0x00, 0xc0, - 0x00, 0xb3, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9d, 0x01, 0x99, 0x02, 0x96, - 0x02, 0x94, 0x03, 0x92, 0x04, 0x90, 0x05, 0x8f, 0x05, 0x8d, 0x06, 0x8d, - 0x06, 0x8c, 0x07, 0x8b, 0x07, 0x8a, 0x07, 0x8a, 0x07, 0x89, 0x08, 0x89, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x47, 0x0f, 0x0f, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, - 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x00, 0x7f, 0x00, 0xa5, 0x00, 0xb2, - 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, - 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x4f, 0x31, 0x55, - 0x2b, 0x5b, 0x27, 0x5f, 0x24, 0x63, 0x22, 0x65, 0x20, 0x68, 0x1f, 0x69, - 0x1d, 0x6b, 0x1d, 0x6d, 0x1b, 0x6d, 0x1a, 0x6e, 0x1a, 0x6f, 0x1a, 0x71, - 0x19, 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x73, 0x18, 0x74, - 0x1c, 0x2f, 0x16, 0x3b, 0x13, 0x44, 0x11, 0x4d, 0x11, 0x53, 0x10, 0x59, - 0x10, 0x5d, 0x10, 0x60, 0x10, 0x63, 0x10, 0x65, 0x10, 0x67, 0x10, 0x69, - 0x10, 0x6b, 0x10, 0x6c, 0x10, 0x6d, 0x10, 0x6e, 0x10, 0x6f, 0x10, 0x70, - 0x10, 0x71, 0x10, 0x71, 0x00, 0xd5, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xb2, - 0x00, 0xaa, 0x00, 0xa4, 0x00, 0xa0, 0x01, 0x9c, 0x01, 0x9a, 0x02, 0x97, - 0x02, 0x95, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, 0x05, 0x90, 0x05, 0x8e, - 0x06, 0x8e, 0x06, 0x8d, 0x06, 0x8c, 0x06, 0x8c, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x3f, - 0x05, 0x05, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, 0x00, - 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, - 0x3d, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0f, 0x0f, 0x00, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, - 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, - 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0x4c, 0x33, 0x53, 0x2e, 0x58, 0x29, 0x5c, - 0x27, 0x5f, 0x24, 0x62, 0x22, 0x64, 0x20, 0x66, 0x20, 0x68, 0x1e, 0x69, - 0x1d, 0x6a, 0x1d, 0x6c, 0x1c, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x1a, 0x6f, - 0x1a, 0x71, 0x19, 0x72, 0x18, 0x72, 0x18, 0x72, 0x1d, 0x2d, 0x17, 0x37, - 0x14, 0x40, 0x12, 0x47, 0x11, 0x4e, 0x11, 0x53, 0x10, 0x57, 0x10, 0x5b, - 0x10, 0x5e, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x67, 0x10, 0x68, - 0x10, 0x6a, 0x10, 0x6b, 0x10, 0x6c, 0x10, 0x6d, 0x10, 0x6e, 0x10, 0x6f, - 0x00, 0xd6, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xab, - 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9f, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x98, - 0x02, 0x96, 0x03, 0x94, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, 0x05, 0x90, - 0x05, 0x8f, 0x05, 0x8f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, - 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, 0x00, - 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x05, 0x05, - 0x00, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, - 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, - 0x00, 0xb9, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x4b, 0x34, 0x51, 0x2f, 0x55, 0x2c, 0x59, 0x29, 0x5c, 0x26, 0x60, - 0x24, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x67, 0x1f, 0x69, 0x1d, 0x69, - 0x1d, 0x6a, 0x1d, 0x6c, 0x1c, 0x6d, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, - 0x1a, 0x6f, 0x1a, 0x71, 0x1e, 0x2b, 0x18, 0x34, 0x15, 0x3b, 0x13, 0x43, - 0x12, 0x49, 0x11, 0x4e, 0x11, 0x52, 0x10, 0x56, 0x10, 0x5a, 0x10, 0x5d, - 0x10, 0x5f, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x66, 0x10, 0x68, - 0x10, 0x69, 0x10, 0x6a, 0x10, 0x6b, 0x10, 0x6c, 0x00, 0xd7, 0x00, 0xcd, - 0x00, 0xc5, 0x00, 0xbd, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa7, - 0x00, 0xa4, 0x00, 0xa1, 0x01, 0x9e, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x98, - 0x02, 0x97, 0x02, 0x96, 0x03, 0x94, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, - 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, - 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1c, 0x00, 0x00, 0x12, 0x00, 0x3f, - 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, - 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x49, 0x36, 0x4f, - 0x31, 0x54, 0x2d, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x26, 0x60, 0x24, 0x61, - 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x69, - 0x1d, 0x69, 0x1d, 0x6b, 0x1c, 0x6d, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, - 0x1e, 0x2a, 0x19, 0x32, 0x16, 0x39, 0x13, 0x3f, 0x12, 0x45, 0x11, 0x4a, - 0x11, 0x4e, 0x11, 0x52, 0x10, 0x56, 0x10, 0x59, 0x10, 0x5c, 0x10, 0x5d, - 0x10, 0x60, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x66, 0x10, 0x67, - 0x10, 0x68, 0x10, 0x69, 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc7, 0x00, 0xc0, - 0x00, 0xba, 0x00, 0xb5, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa8, 0x00, 0xa5, - 0x00, 0xa2, 0x00, 0xa0, 0x01, 0x9e, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x99, - 0x02, 0x97, 0x02, 0x96, 0x03, 0x95, 0x03, 0x94, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, - 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, - 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, - 0x2b, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x2a, 0x00, 0x09, 0x00, 0x00, 0x1d, 0x00, 0x3f, 0x00, 0x5b, - 0x00, 0x6f, 0x00, 0x7f, 0x00, 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, - 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x48, 0x37, 0x4d, 0x32, 0x51, 0x2f, 0x55, - 0x2c, 0x58, 0x29, 0x5b, 0x27, 0x5d, 0x26, 0x60, 0x24, 0x60, 0x23, 0x63, - 0x22, 0x65, 0x20, 0x65, 0x20, 0x66, 0x1f, 0x68, 0x1e, 0x69, 0x1d, 0x69, - 0x1d, 0x69, 0x1d, 0x6b, 0x1c, 0x6d, 0x1b, 0x6d, 0x1e, 0x29, 0x1a, 0x30, - 0x17, 0x36, 0x14, 0x3c, 0x13, 0x41, 0x12, 0x46, 0x11, 0x4a, 0x11, 0x4f, - 0x11, 0x52, 0x10, 0x55, 0x10, 0x58, 0x10, 0x5a, 0x10, 0x5c, 0x10, 0x5f, - 0x10, 0x60, 0x10, 0x62, 0x10, 0x63, 0x10, 0x65, 0x10, 0x65, 0x10, 0x67, - 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xb9, - 0x00, 0xb4, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa3, - 0x00, 0xa1, 0x01, 0x9f, 0x01, 0x9d, 0x01, 0x9b, 0x02, 0x9a, 0x02, 0x99, - 0x02, 0x98, 0x02, 0x97, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, - 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, 0x00, 0x08, 0x02, 0x00, - 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x31, 0x00, - 0x19, 0x00, 0x00, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, - 0x00, 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, - 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x48, 0x37, 0x4c, 0x33, 0x50, 0x30, 0x54, 0x2d, 0x57, 0x2a, 0x59, - 0x29, 0x5c, 0x27, 0x5d, 0x26, 0x60, 0x24, 0x60, 0x23, 0x62, 0x22, 0x65, - 0x20, 0x65, 0x20, 0x65, 0x20, 0x67, 0x1f, 0x69, 0x1d, 0x69, 0x1d, 0x69, - 0x1d, 0x69, 0x1d, 0x6a, 0x1f, 0x28, 0x1a, 0x2e, 0x17, 0x34, 0x15, 0x39, - 0x13, 0x3f, 0x13, 0x43, 0x12, 0x48, 0x11, 0x4b, 0x11, 0x4f, 0x11, 0x52, - 0x10, 0x55, 0x10, 0x57, 0x10, 0x59, 0x10, 0x5c, 0x10, 0x5d, 0x10, 0x5f, - 0x10, 0x61, 0x10, 0x62, 0x10, 0x63, 0x10, 0x64, 0x00, 0xd9, 0x00, 0xd2, - 0x00, 0xcc, 0x00, 0xc6, 0x00, 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb3, - 0x00, 0xaf, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa2, - 0x00, 0xa0, 0x01, 0x9e, 0x01, 0x9d, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, - 0x00, 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x04, 0x00, - 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x00, - 0x00, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, - 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x47, 0x38, 0x4b, - 0x34, 0x4f, 0x31, 0x53, 0x2e, 0x55, 0x2c, 0x57, 0x2a, 0x5a, 0x28, 0x5c, - 0x27, 0x5d, 0x26, 0x60, 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, - 0x20, 0x65, 0x20, 0x65, 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x69, 0x1d, 0x69, - 0x1f, 0x27, 0x1b, 0x2d, 0x18, 0x33, 0x16, 0x38, 0x14, 0x3c, 0x13, 0x41, - 0x12, 0x44, 0x11, 0x48, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, - 0x10, 0x57, 0x10, 0x59, 0x10, 0x5b, 0x10, 0x5c, 0x10, 0x5e, 0x10, 0x5f, - 0x10, 0x61, 0x10, 0x62, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc8, - 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xba, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, - 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa3, 0x00, 0xa1, - 0x01, 0xa0, 0x01, 0x9e, 0x01, 0x9d, 0x02, 0x9c, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb7, - 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, - 0x00, 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, - 0x0c, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, - 0x00, 0x2c, 0x00, 0x3f, 0x00, 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, - 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x47, 0x38, 0x4b, 0x35, 0x4e, 0x32, 0x51, - 0x2f, 0x53, 0x2d, 0x57, 0x2b, 0x58, 0x2a, 0x5b, 0x27, 0x5c, 0x27, 0x5d, - 0x25, 0x60, 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, - 0x20, 0x65, 0x20, 0x66, 0x1f, 0x69, 0x1e, 0x69, 0x1f, 0x27, 0x1b, 0x2c, - 0x18, 0x31, 0x16, 0x36, 0x14, 0x3a, 0x13, 0x3e, 0x13, 0x42, 0x12, 0x45, - 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x56, - 0x10, 0x58, 0x10, 0x5a, 0x10, 0x5c, 0x10, 0x5d, 0x10, 0x5e, 0x10, 0x60, - 0x00, 0xd9, 0x00, 0xd4, 0x00, 0xce, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xc1, - 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, - 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa2, 0x00, 0xa0, - 0x01, 0x9f, 0x01, 0x9e, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, - 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, - 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x39, 0x00, - 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x2e, - 0x00, 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, - 0x00, 0x80, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x46, 0x39, 0x4a, 0x35, 0x4d, 0x32, 0x50, 0x30, 0x53, 0x2e, 0x55, - 0x2c, 0x57, 0x2a, 0x58, 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5d, 0x25, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x63, 0x22, 0x65, 0x20, 0x65, 0x20, 0x65, - 0x20, 0x65, 0x20, 0x67, 0x1f, 0x27, 0x1c, 0x2c, 0x19, 0x30, 0x17, 0x35, - 0x15, 0x39, 0x14, 0x3d, 0x13, 0x40, 0x12, 0x44, 0x12, 0x47, 0x11, 0x49, - 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x56, 0x10, 0x58, - 0x10, 0x59, 0x10, 0x5b, 0x10, 0x5c, 0x10, 0x5e, 0x00, 0xda, 0x00, 0xd4, - 0x00, 0xd0, 0x00, 0xcb, 0x00, 0xc7, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xbb, - 0x00, 0xb8, 0x00, 0xb5, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, - 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa3, 0x00, 0xa1, 0x01, 0xa0, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, - 0x00, 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, - 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, - 0x14, 0x00, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, - 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x46, 0x39, 0x49, - 0x36, 0x4c, 0x33, 0x4f, 0x31, 0x52, 0x2e, 0x53, 0x2d, 0x57, 0x2b, 0x57, - 0x2a, 0x59, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x62, 0x22, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, - 0x1f, 0x26, 0x1c, 0x2b, 0x19, 0x2f, 0x17, 0x33, 0x16, 0x37, 0x14, 0x3b, - 0x13, 0x3e, 0x13, 0x41, 0x12, 0x44, 0x11, 0x47, 0x11, 0x4a, 0x11, 0x4d, - 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x55, 0x10, 0x57, 0x10, 0x59, - 0x10, 0x5a, 0x10, 0x5b, 0x00, 0xda, 0x00, 0xd5, 0x00, 0xd1, 0x00, 0xcc, - 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb7, - 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa9, - 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa2, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, - 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, - 0x00, 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, - 0x00, 0x16, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x00, - 0x00, 0x04, 0x00, 0x14, 0x00, 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, - 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x39, 0x48, 0x36, 0x4b, 0x34, 0x4f, - 0x31, 0x50, 0x30, 0x53, 0x2e, 0x55, 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x5a, - 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x61, 0x22, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x26, 0x1c, 0x2a, - 0x1a, 0x2e, 0x18, 0x32, 0x16, 0x36, 0x14, 0x39, 0x13, 0x3d, 0x13, 0x40, - 0x12, 0x43, 0x12, 0x45, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, - 0x11, 0x51, 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, 0x10, 0x58, 0x10, 0x5a, - 0x00, 0xda, 0x00, 0xd6, 0x00, 0xd1, 0x00, 0xcd, 0x00, 0xc9, 0x00, 0xc6, - 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb3, - 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa7, - 0x00, 0xa6, 0x00, 0xa4, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, - 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, - 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, - 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, 0x0a, - 0x00, 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, - 0x00, 0x5d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x45, 0x3a, 0x48, 0x37, 0x4a, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x53, - 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, 0x2a, 0x58, 0x29, 0x5b, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x61, - 0x23, 0x64, 0x21, 0x65, 0x20, 0x26, 0x1d, 0x2a, 0x1a, 0x2d, 0x18, 0x31, - 0x17, 0x35, 0x15, 0x38, 0x14, 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x44, - 0x12, 0x46, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, - 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, 0x10, 0x58, 0x00, 0xda, 0x00, 0xd6, - 0x00, 0xd2, 0x00, 0xce, 0x00, 0xcb, 0x00, 0xc7, 0x00, 0xc4, 0x00, 0xc1, - 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, - 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0xa6, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, - 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, - 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, - 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x1c, - 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x3a, 0x47, - 0x38, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x54, - 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x59, 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5c, - 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x63, - 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2d, 0x18, 0x30, 0x17, 0x33, 0x16, 0x37, - 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x13, 0x42, 0x12, 0x44, 0x11, 0x47, - 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x53, - 0x10, 0x55, 0x10, 0x56, 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xcf, - 0x00, 0xcc, 0x00, 0xc8, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, - 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, - 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, - 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, - 0x00, 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, - 0x00, 0x36, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, - 0x12, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, - 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4c, - 0x33, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, - 0x2a, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5f, - 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x29, - 0x1b, 0x2c, 0x18, 0x2f, 0x17, 0x33, 0x16, 0x36, 0x14, 0x39, 0x14, 0x3b, - 0x13, 0x3e, 0x13, 0x41, 0x12, 0x43, 0x12, 0x45, 0x11, 0x47, 0x11, 0x4a, - 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x52, 0x10, 0x54, - 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xd0, 0x00, 0xcc, 0x00, 0xc9, - 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb9, - 0x00, 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, - 0x00, 0xab, 0x00, 0xaa, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, - 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, - 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, - 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, - 0x00, 0x3f, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x4a, 0x35, 0x4b, 0x34, 0x4f, 0x31, 0x4f, - 0x31, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x58, - 0x2a, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5f, 0x24, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x19, 0x2f, - 0x17, 0x32, 0x16, 0x35, 0x15, 0x38, 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, - 0x13, 0x42, 0x12, 0x44, 0x12, 0x46, 0x11, 0x48, 0x11, 0x4a, 0x11, 0x4c, - 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x52, 0x00, 0xdb, 0x00, 0xd7, - 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcd, 0x00, 0xca, 0x00, 0xc7, 0x00, 0xc5, - 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, - 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xac, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, - 0x00, 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, - 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, - 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, 0x00, 0x07, 0x00, 0x00, 0x03, - 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3b, 0x46, - 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x51, 0x2f, 0x53, - 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x29, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x26, 0x5f, 0x24, 0x60, 0x23, 0x60, - 0x20, 0x25, 0x1d, 0x28, 0x1c, 0x2b, 0x1a, 0x2e, 0x18, 0x31, 0x17, 0x34, - 0x16, 0x37, 0x14, 0x39, 0x13, 0x3b, 0x13, 0x3e, 0x13, 0x40, 0x12, 0x42, - 0x12, 0x45, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4a, 0x11, 0x4c, 0x11, 0x4e, - 0x11, 0x4f, 0x11, 0x51, 0x00, 0xdb, 0x00, 0xd8, 0x00, 0xd4, 0x00, 0xd1, - 0x00, 0xce, 0x00, 0xcb, 0x00, 0xc8, 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, - 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, - 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x56, 0x7f, 0x47, 0x63, - 0x44, 0x57, 0x43, 0x52, 0x43, 0x4e, 0x42, 0x4c, 0x42, 0x4a, 0x42, 0x49, - 0x42, 0x48, 0x42, 0x47, 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x45, - 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, - 0x4c, 0x7f, 0x27, 0x4d, 0x2f, 0x46, 0x33, 0x44, 0x36, 0x43, 0x37, 0x43, - 0x39, 0x42, 0x3a, 0x41, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, 0x3b, 0x41, - 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3d, 0x41, - 0x3d, 0x41, 0x3d, 0x41, 0x8e, 0x7f, 0x47, 0x4d, 0x44, 0x46, 0x43, 0x44, - 0x43, 0x43, 0x42, 0x43, 0x42, 0x42, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, - 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, - 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x85, 0x1b, 0x60, 0x27, 0x54, 0x2d, 0x4e, 0x30, - 0x4b, 0x33, 0x49, 0x34, 0x47, 0x35, 0x46, 0x37, 0x45, 0x38, 0x45, 0x38, - 0x44, 0x39, 0x43, 0x39, 0x43, 0x3a, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, - 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x09, 0x43, 0x16, 0x3f, - 0x1f, 0x3e, 0x25, 0x3e, 0x29, 0x3e, 0x2c, 0x3e, 0x2f, 0x3e, 0x30, 0x3e, - 0x32, 0x3e, 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x36, 0x3e, - 0x37, 0x3e, 0x37, 0x3e, 0x38, 0x3e, 0x38, 0x3e, 0x38, 0x3e, 0x39, 0x3e, - 0x59, 0x31, 0x4f, 0x38, 0x4b, 0x3a, 0x48, 0x3b, 0x47, 0x3c, 0x46, 0x3c, - 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x4c, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x4f, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x96, 0x05, 0x74, 0x0f, 0x63, 0x17, 0x5a, 0x1d, 0x54, 0x21, 0x51, 0x24, - 0x4e, 0x27, 0x4c, 0x29, 0x4a, 0x2b, 0x49, 0x2d, 0x49, 0x2e, 0x48, 0x2f, - 0x48, 0x31, 0x47, 0x31, 0x46, 0x32, 0x46, 0x33, 0x44, 0x33, 0x44, 0x34, - 0x43, 0x34, 0x43, 0x35, 0x09, 0x43, 0x11, 0x3e, 0x18, 0x3c, 0x1d, 0x3c, - 0x22, 0x3c, 0x25, 0x3c, 0x27, 0x3c, 0x2a, 0x3c, 0x2c, 0x3d, 0x2d, 0x3d, - 0x2f, 0x3d, 0x30, 0x3e, 0x31, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, - 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x5b, 0x2a, 0x53, 0x31, - 0x4f, 0x34, 0x4c, 0x36, 0x4b, 0x38, 0x4a, 0x39, 0x48, 0x39, 0x47, 0x3a, - 0x46, 0x3b, 0x46, 0x3b, 0x46, 0x3c, 0x46, 0x3c, 0x46, 0x3d, 0x45, 0x3d, - 0x44, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x45, - 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x8f, 0x00, 0x73, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x9e, 0x01, 0x81, 0x06, - 0x6f, 0x0d, 0x64, 0x12, 0x5d, 0x16, 0x58, 0x1a, 0x54, 0x1d, 0x53, 0x20, - 0x50, 0x22, 0x4e, 0x24, 0x4c, 0x26, 0x4b, 0x27, 0x4a, 0x29, 0x49, 0x2a, - 0x49, 0x2b, 0x49, 0x2c, 0x48, 0x2d, 0x48, 0x2e, 0x48, 0x2f, 0x47, 0x2f, - 0x09, 0x44, 0x0f, 0x3e, 0x14, 0x3c, 0x19, 0x3c, 0x1d, 0x3b, 0x20, 0x3b, - 0x23, 0x3b, 0x25, 0x3c, 0x27, 0x3c, 0x28, 0x3c, 0x2a, 0x3b, 0x2c, 0x3b, - 0x2c, 0x3b, 0x2d, 0x3b, 0x2f, 0x3c, 0x2f, 0x3c, 0x30, 0x3d, 0x31, 0x3e, - 0x32, 0x3e, 0x32, 0x3e, 0x5c, 0x28, 0x56, 0x2d, 0x52, 0x31, 0x4f, 0x33, - 0x4e, 0x35, 0x4b, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x49, 0x39, 0x47, 0x39, - 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x3a, 0x46, 0x3a, 0x46, 0x3b, - 0x46, 0x3c, 0x46, 0x3c, 0x46, 0x3d, 0x45, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x19, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x84, 0x00, - 0x5b, 0x00, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xa4, 0x00, 0x8a, 0x03, 0x78, 0x07, 0x6d, 0x0c, - 0x65, 0x10, 0x60, 0x13, 0x5b, 0x16, 0x57, 0x19, 0x55, 0x1b, 0x53, 0x1e, - 0x52, 0x1f, 0x4f, 0x22, 0x4d, 0x23, 0x4c, 0x24, 0x4b, 0x25, 0x4b, 0x27, - 0x4a, 0x28, 0x49, 0x29, 0x49, 0x29, 0x49, 0x2b, 0x09, 0x45, 0x0d, 0x40, - 0x11, 0x3d, 0x16, 0x3c, 0x19, 0x3b, 0x1d, 0x3b, 0x1f, 0x3b, 0x21, 0x3a, - 0x23, 0x3b, 0x25, 0x3b, 0x26, 0x3c, 0x28, 0x3c, 0x29, 0x3c, 0x2a, 0x3b, - 0x2b, 0x3b, 0x2c, 0x3b, 0x2d, 0x3b, 0x2e, 0x3b, 0x2e, 0x3b, 0x2f, 0x3b, - 0x5d, 0x27, 0x57, 0x2c, 0x54, 0x2f, 0x52, 0x31, 0x4f, 0x32, 0x4e, 0x34, - 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x48, 0x39, - 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, - 0x46, 0x39, 0x46, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5b, 0x00, 0x55, 0x00, 0x42, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x8d, 0x00, 0x6f, 0x00, 0x48, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xa7, 0x00, 0x91, 0x01, 0x80, 0x04, 0x75, 0x08, 0x6c, 0x0b, 0x65, 0x0e, - 0x61, 0x11, 0x5d, 0x14, 0x59, 0x16, 0x56, 0x19, 0x55, 0x1a, 0x54, 0x1c, - 0x53, 0x1e, 0x51, 0x1f, 0x4f, 0x21, 0x4d, 0x22, 0x4c, 0x23, 0x4b, 0x24, - 0x4b, 0x25, 0x4b, 0x26, 0x09, 0x45, 0x0d, 0x41, 0x10, 0x3e, 0x14, 0x3d, - 0x17, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, 0x3b, 0x20, 0x3b, 0x22, 0x3a, - 0x23, 0x3a, 0x25, 0x3b, 0x26, 0x3c, 0x28, 0x3c, 0x28, 0x3c, 0x29, 0x3c, - 0x2a, 0x3c, 0x2b, 0x3b, 0x2c, 0x3b, 0x2d, 0x3b, 0x5d, 0x26, 0x59, 0x2a, - 0x56, 0x2d, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, - 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x37, 0x4a, 0x38, 0x49, 0x39, - 0x48, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, - 0x00, 0x4a, 0x00, 0x38, 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa9, 0x00, 0x96, 0x00, - 0x86, 0x02, 0x7a, 0x05, 0x72, 0x08, 0x6b, 0x0b, 0x65, 0x0e, 0x62, 0x10, - 0x5f, 0x12, 0x5b, 0x14, 0x58, 0x16, 0x56, 0x18, 0x55, 0x1a, 0x54, 0x1b, - 0x53, 0x1d, 0x52, 0x1e, 0x50, 0x1f, 0x4f, 0x20, 0x4d, 0x21, 0x4c, 0x22, - 0x09, 0x46, 0x0c, 0x41, 0x0f, 0x3f, 0x12, 0x3d, 0x15, 0x3c, 0x17, 0x3c, - 0x1a, 0x3b, 0x1c, 0x3a, 0x1e, 0x3b, 0x1f, 0x3b, 0x21, 0x3a, 0x22, 0x3a, - 0x24, 0x3a, 0x25, 0x3a, 0x26, 0x3b, 0x27, 0x3c, 0x28, 0x3c, 0x29, 0x3c, - 0x2a, 0x3c, 0x2a, 0x3c, 0x5e, 0x26, 0x5a, 0x29, 0x57, 0x2c, 0x53, 0x2e, - 0x53, 0x30, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, 0x4a, 0x38, - 0x48, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, - 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, 0x00, - 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xab, 0x00, 0x9a, 0x00, 0x8b, 0x01, 0x7f, 0x03, - 0x77, 0x06, 0x71, 0x08, 0x6a, 0x0b, 0x66, 0x0d, 0x63, 0x0f, 0x60, 0x11, - 0x5d, 0x13, 0x59, 0x14, 0x57, 0x16, 0x56, 0x18, 0x55, 0x19, 0x54, 0x1b, - 0x53, 0x1c, 0x53, 0x1d, 0x51, 0x1e, 0x50, 0x1f, 0x0a, 0x46, 0x0c, 0x42, - 0x0e, 0x3f, 0x11, 0x3f, 0x13, 0x3c, 0x16, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, - 0x1c, 0x3a, 0x1e, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3a, 0x23, 0x3a, - 0x24, 0x3a, 0x25, 0x39, 0x26, 0x3a, 0x27, 0x3c, 0x27, 0x3c, 0x28, 0x3c, - 0x5e, 0x26, 0x5a, 0x29, 0x57, 0x2b, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x30, - 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x34, 0x4d, 0x35, 0x4b, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x38, - 0x4a, 0x39, 0x48, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, - 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, - 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xac, 0x00, 0x9d, 0x00, 0x8f, 0x01, 0x85, 0x02, 0x7b, 0x04, 0x75, 0x06, - 0x6f, 0x08, 0x69, 0x0b, 0x66, 0x0d, 0x63, 0x0e, 0x61, 0x10, 0x5e, 0x12, - 0x5b, 0x13, 0x58, 0x15, 0x57, 0x16, 0x56, 0x18, 0x55, 0x19, 0x55, 0x1a, - 0x54, 0x1b, 0x53, 0x1c, 0x0a, 0x47, 0x0b, 0x42, 0x0e, 0x40, 0x10, 0x3f, - 0x12, 0x3d, 0x15, 0x3c, 0x17, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, - 0x1d, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x23, 0x3a, - 0x24, 0x3a, 0x25, 0x39, 0x26, 0x3a, 0x27, 0x3b, 0x5e, 0x26, 0x5b, 0x28, - 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x35, 0x4b, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, - 0x00, 0x55, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, - 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, - 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xad, 0x00, 0xa0, 0x00, - 0x92, 0x00, 0x89, 0x02, 0x7f, 0x03, 0x78, 0x05, 0x73, 0x07, 0x6e, 0x09, - 0x69, 0x0b, 0x66, 0x0c, 0x64, 0x0e, 0x62, 0x0f, 0x60, 0x11, 0x5c, 0x12, - 0x59, 0x14, 0x58, 0x15, 0x57, 0x16, 0x56, 0x18, 0x55, 0x18, 0x55, 0x1a, - 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x41, 0x0f, 0x3f, 0x11, 0x3f, 0x13, 0x3c, - 0x15, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x3a, - 0x1f, 0x3b, 0x1f, 0x3b, 0x21, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x24, 0x3a, - 0x24, 0x3a, 0x24, 0x39, 0x5e, 0x26, 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2b, - 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2f, 0x52, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x32, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, - 0x00, 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, - 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x8b, 0x01, - 0x83, 0x02, 0x7b, 0x04, 0x76, 0x05, 0x73, 0x07, 0x6e, 0x09, 0x69, 0x0a, - 0x66, 0x0c, 0x64, 0x0d, 0x62, 0x0f, 0x61, 0x10, 0x5e, 0x11, 0x5b, 0x13, - 0x58, 0x14, 0x58, 0x15, 0x56, 0x16, 0x56, 0x18, 0x0a, 0x47, 0x0b, 0x43, - 0x0d, 0x42, 0x0f, 0x3f, 0x11, 0x3f, 0x12, 0x3e, 0x14, 0x3c, 0x16, 0x3c, - 0x17, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x3b, - 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3b, 0x21, 0x3a, 0x22, 0x3a, 0x24, 0x3a, - 0x5e, 0x26, 0x5c, 0x27, 0x59, 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, - 0x00, 0x33, 0x00, 0x28, 0x00, 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, - 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, 0x87, 0x02, 0x7f, 0x03, - 0x79, 0x04, 0x75, 0x06, 0x71, 0x07, 0x6c, 0x09, 0x68, 0x0a, 0x66, 0x0c, - 0x64, 0x0d, 0x63, 0x0e, 0x61, 0x10, 0x5f, 0x11, 0x5c, 0x12, 0x59, 0x13, - 0x58, 0x14, 0x58, 0x15, 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0e, 0x40, - 0x10, 0x3e, 0x11, 0x3f, 0x13, 0x3c, 0x15, 0x3b, 0x17, 0x3c, 0x17, 0x3c, - 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x3a, 0x1f, 0x3b, - 0x1f, 0x3b, 0x21, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x5e, 0x25, 0x5c, 0x27, - 0x5a, 0x29, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, - 0x00, 0x59, 0x00, 0x53, 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, - 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, - 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xaf, 0x00, 0xa5, 0x00, - 0x9a, 0x00, 0x90, 0x00, 0x8a, 0x01, 0x83, 0x02, 0x7c, 0x03, 0x77, 0x05, - 0x74, 0x06, 0x70, 0x07, 0x6c, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x64, 0x0c, - 0x63, 0x0e, 0x61, 0x0f, 0x60, 0x11, 0x5d, 0x11, 0x5b, 0x13, 0x59, 0x13, - 0x0a, 0x47, 0x0a, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3e, 0x11, 0x3f, - 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, - 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x3a, 0x1f, 0x3b, 0x1f, 0x3b, - 0x20, 0x3b, 0x21, 0x3b, 0x5e, 0x25, 0x5c, 0x27, 0x5a, 0x29, 0x57, 0x2a, - 0x57, 0x2a, 0x56, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x30, - 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, - 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, - 0x00, 0x4e, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, - 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, - 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, - 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x92, 0x00, - 0x8c, 0x01, 0x86, 0x02, 0x7e, 0x02, 0x7a, 0x04, 0x76, 0x05, 0x73, 0x06, - 0x70, 0x07, 0x6b, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0e, - 0x62, 0x0e, 0x61, 0x10, 0x5e, 0x11, 0x5c, 0x12, 0x0a, 0x47, 0x0a, 0x44, - 0x0c, 0x42, 0x0d, 0x41, 0x0f, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x13, 0x3d, - 0x15, 0x3c, 0x16, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, - 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x39, 0x1f, 0x3b, 0x1f, 0x3b, 0x1f, 0x3b, - 0x5f, 0x25, 0x5c, 0x27, 0x5b, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, - 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, - 0x4e, 0x34, 0x4c, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, - 0x00, 0x41, 0x00, 0x39, 0x00, 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, - 0x00, 0x0e, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, - 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, - 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xb0, 0x00, 0xa7, 0x00, 0x9f, 0x00, 0x95, 0x00, 0x8e, 0x00, 0x88, 0x01, - 0x82, 0x02, 0x7c, 0x03, 0x78, 0x05, 0x75, 0x06, 0x72, 0x07, 0x6f, 0x08, - 0x6a, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0d, 0x62, 0x0e, - 0x61, 0x0f, 0x60, 0x11, 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, - 0x0e, 0x40, 0x10, 0x3d, 0x11, 0x3f, 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3b, - 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, - 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x5f, 0x25, 0x5c, 0x27, - 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2d, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x34, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, - 0x00, 0x5b, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, - 0x00, 0x35, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, - 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, - 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, - 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa7, 0x00, - 0xa0, 0x00, 0x97, 0x00, 0x90, 0x00, 0x8a, 0x01, 0x85, 0x02, 0x7e, 0x02, - 0x7a, 0x03, 0x77, 0x05, 0x74, 0x06, 0x72, 0x07, 0x6e, 0x08, 0x6a, 0x09, - 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0d, 0x63, 0x0e, 0x61, 0x0f, - 0x0a, 0x47, 0x0a, 0x45, 0x0b, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3e, - 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3d, 0x15, 0x3c, 0x15, 0x3a, 0x17, 0x3c, - 0x17, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3a, - 0x1d, 0x39, 0x1f, 0x39, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x59, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x17, 0x00, 0x2f, - 0x00, 0x4c, 0x00, 0x56, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x5d, - 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, - 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x62, - 0x00, 0x62, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, - 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x14, 0x01, 0x29, - 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, - 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, - 0x00, 0x5f, 0x00, 0x5f, 0x31, 0x11, 0x03, 0x23, 0x00, 0x46, 0x00, 0x52, - 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, - 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x99, 0x00, - 0x91, 0x00, 0x8c, 0x00, 0x88, 0x01, 0x82, 0x02, 0x7c, 0x03, 0x79, 0x04, - 0x76, 0x05, 0x74, 0x06, 0x71, 0x07, 0x6d, 0x08, 0x6a, 0x09, 0x68, 0x0a, - 0x66, 0x0b, 0x65, 0x0c, 0x64, 0x0c, 0x63, 0x0e, 0x0a, 0x47, 0x0a, 0x45, - 0x0b, 0x42, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3e, 0x11, 0x3f, - 0x13, 0x3e, 0x13, 0x3c, 0x15, 0x3c, 0x16, 0x3a, 0x17, 0x3c, 0x17, 0x3c, - 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, - 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5a, 0x29, 0x57, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x45, - 0x00, 0x4f, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, - 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, - 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x48, 0x00, 0x55, 0x00, 0x58, 0x00, 0x57, - 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, - 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x16, 0x04, 0x00, 0x27, 0x00, 0x41, - 0x00, 0x4d, 0x00, 0x53, 0x00, 0x56, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, - 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, - 0x5f, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, - 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, - 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xb1, 0x00, 0xa9, 0x00, 0xa2, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x8e, 0x00, - 0x89, 0x01, 0x84, 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x78, 0x05, 0x75, 0x05, - 0x73, 0x06, 0x71, 0x07, 0x6c, 0x08, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, - 0x65, 0x0c, 0x64, 0x0c, 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x42, - 0x0e, 0x42, 0x0f, 0x40, 0x10, 0x3d, 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3d, - 0x15, 0x3c, 0x15, 0x3b, 0x17, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, - 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x5f, 0x25, 0x5c, 0x27, - 0x5c, 0x27, 0x5b, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x30, - 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x22, 0x00, 0x36, 0x00, 0x42, - 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, - 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5f, 0x00, 0x48, - 0x00, 0x1b, 0x00, 0x34, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4f, - 0x00, 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, - 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x57, 0x00, 0x12, 0x01, 0x00, 0x17, 0x00, 0x2f, 0x00, 0x3e, - 0x00, 0x47, 0x00, 0x4d, 0x00, 0x51, 0x00, 0x53, 0x00, 0x56, 0x00, 0x57, - 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x85, 0x00, 0x5f, 0x00, - 0x1f, 0x00, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, - 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, - 0x00, 0x59, 0x00, 0x5a, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb2, 0x00, 0xa9, 0x00, - 0xa3, 0x00, 0x9d, 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8b, 0x01, 0x87, 0x02, - 0x81, 0x02, 0x7c, 0x03, 0x79, 0x03, 0x77, 0x05, 0x75, 0x06, 0x72, 0x06, - 0x70, 0x07, 0x6c, 0x08, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, - 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, - 0x0f, 0x3e, 0x11, 0x3e, 0x11, 0x3f, 0x13, 0x3f, 0x13, 0x3c, 0x15, 0x3c, - 0x15, 0x3a, 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x19, 0x3c, - 0x1a, 0x3b, 0x1c, 0x3b, 0x5f, 0x24, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x28, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x50, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x19, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, - 0x00, 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, - 0x00, 0x56, 0x00, 0x57, 0x00, 0x62, 0x00, 0x55, 0x00, 0x34, 0x00, 0x0b, - 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, - 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x78, 0x00, - 0x43, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x23, 0x00, 0x31, 0x00, 0x3b, - 0x00, 0x42, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, - 0x00, 0x55, 0x00, 0x56, 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x00, - 0x00, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x45, - 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0x9e, 0x00, - 0x96, 0x00, 0x90, 0x00, 0x8c, 0x00, 0x88, 0x01, 0x84, 0x02, 0x7e, 0x02, - 0x7b, 0x03, 0x78, 0x04, 0x76, 0x05, 0x74, 0x06, 0x72, 0x07, 0x6f, 0x07, - 0x6b, 0x09, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x0a, 0x47, 0x0a, 0x46, - 0x0b, 0x44, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3f, 0x10, 0x3d, - 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3c, 0x16, 0x3a, - 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, - 0x5f, 0x24, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x14, 0x00, 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, - 0x00, 0x44, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, - 0x00, 0x62, 0x00, 0x58, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x14, - 0x00, 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, - 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x87, 0x00, 0x61, 0x00, 0x36, 0x00, - 0x10, 0x00, 0x02, 0x0a, 0x00, 0x1a, 0x00, 0x27, 0x00, 0x32, 0x00, 0x3a, - 0x00, 0x40, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4b, 0x00, 0x4e, 0x00, 0x50, - 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, - 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, 0x28, 0x3c, 0x16, 0x41, - 0x11, 0x43, 0x0f, 0x44, 0x0e, 0x45, 0x0d, 0x46, 0x0c, 0x46, 0x0c, 0x46, - 0x0b, 0x47, 0x0b, 0x47, 0x0b, 0x47, 0x0b, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x6e, 0x1c, 0x3a, 0x29, 0x27, 0x31, 0x1e, 0x36, 0x19, 0x3a, 0x17, 0x3b, - 0x14, 0x3d, 0x13, 0x3f, 0x11, 0x40, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x42, - 0x0f, 0x43, 0x0f, 0x43, 0x0e, 0x43, 0x0e, 0x44, 0x0d, 0x44, 0x0d, 0x44, - 0x0c, 0x44, 0x0c, 0x44, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x3c, 0x32, 0x22, 0x38, - 0x18, 0x3c, 0x14, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x42, 0x0e, 0x43, - 0x0e, 0x44, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x44, 0x0c, 0x45, 0x0c, 0x45, - 0x0c, 0x45, 0x0c, 0x45, 0x0b, 0x45, 0x0b, 0x45, 0x0b, 0x46, 0x0b, 0x46, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x10, 0x00, 0x1e, 0x00, 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, - 0x00, 0x42, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x57, - 0x00, 0x44, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, - 0x00, 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, - 0x00, 0x49, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x8f, 0x00, 0x74, 0x00, 0x51, 0x00, 0x2e, 0x00, 0x10, 0x00, - 0x04, 0x08, 0x00, 0x13, 0x00, 0x20, 0x00, 0x2a, 0x00, 0x32, 0x00, 0x38, - 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x48, 0x9a, 0x00, 0x91, 0x00, - 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x00, 0x00, 0x08, - 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, - 0x00, 0x42, 0x00, 0x45, 0x30, 0x38, 0x20, 0x3a, 0x18, 0x3d, 0x14, 0x3e, - 0x12, 0x40, 0x10, 0x41, 0x0f, 0x42, 0x0e, 0x42, 0x0e, 0x42, 0x0d, 0x43, - 0x0d, 0x43, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x45, 0x0c, 0x45, 0x0b, 0x45, - 0x0b, 0x46, 0x0b, 0x46, 0x0b, 0x46, 0x0b, 0x47, 0x8a, 0x03, 0x59, 0x0f, - 0x40, 0x18, 0x32, 0x1f, 0x2a, 0x24, 0x24, 0x28, 0x20, 0x2c, 0x1c, 0x2f, - 0x1b, 0x31, 0x18, 0x33, 0x17, 0x34, 0x16, 0x36, 0x15, 0x37, 0x14, 0x38, - 0x13, 0x39, 0x12, 0x3a, 0x12, 0x3a, 0x12, 0x3b, 0x11, 0x3c, 0x10, 0x3c, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x4a, 0x25, 0x31, 0x2b, 0x25, 0x30, 0x1e, 0x33, - 0x1a, 0x36, 0x17, 0x38, 0x15, 0x39, 0x13, 0x3b, 0x12, 0x3c, 0x11, 0x3d, - 0x10, 0x3e, 0x10, 0x3f, 0x0f, 0x3f, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x40, - 0x0e, 0x41, 0x0e, 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, - 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, - 0x00, 0x41, 0x00, 0x44, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, - 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, - 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x94, 0x00, - 0x7f, 0x00, 0x64, 0x00, 0x46, 0x00, 0x29, 0x00, 0x10, 0x00, 0x06, 0x07, - 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x38, - 0x00, 0x3c, 0x00, 0x40, 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, - 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x10, - 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, - 0x34, 0x38, 0x25, 0x39, 0x1e, 0x3b, 0x19, 0x3c, 0x16, 0x3d, 0x14, 0x3d, - 0x13, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x41, 0x0f, 0x42, 0x0e, 0x42, - 0x0e, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x43, 0x0d, 0x43, - 0x0d, 0x44, 0x0d, 0x44, 0x97, 0x00, 0x6c, 0x06, 0x52, 0x0d, 0x43, 0x12, - 0x38, 0x18, 0x30, 0x1c, 0x2a, 0x20, 0x27, 0x23, 0x23, 0x26, 0x20, 0x28, - 0x1e, 0x2a, 0x1c, 0x2c, 0x1b, 0x2e, 0x19, 0x2f, 0x18, 0x31, 0x17, 0x32, - 0x16, 0x33, 0x16, 0x34, 0x16, 0x35, 0x15, 0x35, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x50, 0x24, 0x3b, 0x27, 0x2e, 0x2a, 0x26, 0x2d, 0x21, 0x2f, 0x1d, 0x32, - 0x1a, 0x33, 0x18, 0x35, 0x16, 0x36, 0x15, 0x38, 0x14, 0x39, 0x13, 0x3a, - 0x12, 0x3a, 0x11, 0x3b, 0x11, 0x3c, 0x11, 0x3c, 0x10, 0x3d, 0x10, 0x3e, - 0x10, 0x3e, 0x0f, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, - 0x00, 0x20, 0x00, 0x28, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, - 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, - 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, - 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x87, 0x00, 0x71, 0x00, - 0x57, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x10, 0x00, 0x07, 0x06, 0x00, 0x0b, - 0x00, 0x15, 0x00, 0x1f, 0x00, 0x26, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37, - 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, - 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, - 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, 0x36, 0x39, 0x2a, 0x39, - 0x22, 0x39, 0x1d, 0x3a, 0x1a, 0x3b, 0x17, 0x3d, 0x15, 0x3d, 0x13, 0x3d, - 0x13, 0x3e, 0x12, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x41, 0x0f, 0x42, - 0x0e, 0x41, 0x0e, 0x42, 0x0e, 0x42, 0x0e, 0x41, 0x0e, 0x42, 0x0d, 0x42, - 0x9e, 0x00, 0x79, 0x02, 0x61, 0x07, 0x50, 0x0b, 0x44, 0x10, 0x3b, 0x14, - 0x34, 0x18, 0x2f, 0x1b, 0x2b, 0x1e, 0x28, 0x20, 0x25, 0x23, 0x23, 0x25, - 0x21, 0x27, 0x1f, 0x28, 0x1d, 0x2a, 0x1c, 0x2b, 0x1b, 0x2c, 0x19, 0x2e, - 0x19, 0x2f, 0x19, 0x30, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x54, 0x23, 0x42, 0x25, - 0x35, 0x27, 0x2d, 0x29, 0x27, 0x2c, 0x22, 0x2e, 0x1f, 0x2f, 0x1c, 0x31, - 0x1a, 0x33, 0x19, 0x34, 0x17, 0x35, 0x16, 0x36, 0x15, 0x37, 0x14, 0x38, - 0x13, 0x38, 0x13, 0x39, 0x12, 0x3a, 0x11, 0x3a, 0x11, 0x3b, 0x11, 0x3c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, - 0x00, 0x24, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, 0x00, 0x5e, 0x00, 0x5b, - 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, - 0x00, 0x30, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9d, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, - 0x37, 0x00, 0x22, 0x00, 0x10, 0x00, 0x08, 0x05, 0x02, 0x0a, 0x00, 0x12, - 0x00, 0x1a, 0x00, 0x22, 0x00, 0x28, 0x00, 0x2d, 0x9d, 0x00, 0x99, 0x00, - 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, 0x00, - 0x1f, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x07, 0x00, 0x10, 0x00, 0x19, - 0x00, 0x20, 0x00, 0x26, 0x38, 0x39, 0x2d, 0x39, 0x25, 0x39, 0x20, 0x3a, - 0x1d, 0x3b, 0x1a, 0x3b, 0x18, 0x3c, 0x16, 0x3d, 0x14, 0x3d, 0x13, 0x3d, - 0x13, 0x3d, 0x12, 0x3e, 0x11, 0x3f, 0x11, 0x40, 0x10, 0x40, 0x0f, 0x41, - 0x0f, 0x42, 0x0f, 0x42, 0x0e, 0x42, 0x0e, 0x42, 0xa2, 0x00, 0x84, 0x00, - 0x6c, 0x04, 0x5a, 0x07, 0x4e, 0x0b, 0x45, 0x0f, 0x3e, 0x12, 0x38, 0x15, - 0x32, 0x18, 0x2f, 0x1a, 0x2c, 0x1d, 0x29, 0x1f, 0x26, 0x21, 0x24, 0x22, - 0x22, 0x24, 0x21, 0x26, 0x1f, 0x27, 0x1e, 0x28, 0x1d, 0x2a, 0x1c, 0x2b, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x56, 0x23, 0x47, 0x24, 0x3b, 0x25, 0x32, 0x27, - 0x2c, 0x29, 0x27, 0x2b, 0x24, 0x2d, 0x21, 0x2e, 0x1e, 0x30, 0x1c, 0x31, - 0x1b, 0x32, 0x19, 0x33, 0x18, 0x34, 0x17, 0x35, 0x16, 0x36, 0x15, 0x37, - 0x14, 0x37, 0x14, 0x38, 0x13, 0x38, 0x13, 0x39, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, - 0x00, 0x27, 0x00, 0x2c, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, - 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, - 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, - 0x90, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x5b, 0x00, 0x46, 0x00, 0x32, 0x00, - 0x20, 0x00, 0x10, 0x00, 0x09, 0x04, 0x03, 0x09, 0x00, 0x0f, 0x00, 0x17, - 0x00, 0x1e, 0x00, 0x24, 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, - 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x00, - 0x12, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, - 0x39, 0x3a, 0x2f, 0x39, 0x28, 0x39, 0x23, 0x39, 0x20, 0x3a, 0x1c, 0x3b, - 0x1a, 0x3a, 0x18, 0x3b, 0x17, 0x3c, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, - 0x13, 0x3d, 0x12, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x11, 0x40, 0x10, 0x40, - 0x0f, 0x40, 0x0f, 0x41, 0xa6, 0x00, 0x8b, 0x00, 0x75, 0x02, 0x63, 0x05, - 0x57, 0x07, 0x4d, 0x0b, 0x45, 0x0e, 0x3e, 0x11, 0x39, 0x13, 0x35, 0x16, - 0x32, 0x18, 0x2e, 0x1a, 0x2c, 0x1c, 0x29, 0x1d, 0x27, 0x1f, 0x26, 0x22, - 0x24, 0x22, 0x21, 0x23, 0x21, 0x25, 0x20, 0x26, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x58, 0x23, 0x4a, 0x23, 0x3f, 0x24, 0x36, 0x26, 0x30, 0x27, 0x2b, 0x29, - 0x27, 0x2a, 0x24, 0x2c, 0x22, 0x2d, 0x20, 0x2e, 0x1e, 0x30, 0x1c, 0x30, - 0x1b, 0x32, 0x1a, 0x32, 0x18, 0x33, 0x18, 0x34, 0x17, 0x34, 0x16, 0x35, - 0x16, 0x36, 0x15, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, - 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, - 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x86, 0x00, - 0x76, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, - 0x10, 0x00, 0x09, 0x04, 0x04, 0x08, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1a, - 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, - 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x3a, 0x3a, 0x31, 0x3a, - 0x2a, 0x39, 0x25, 0x3a, 0x22, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1a, 0x3a, - 0x19, 0x3a, 0x17, 0x3c, 0x16, 0x3c, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, - 0x13, 0x3d, 0x12, 0x3d, 0x11, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x10, 0x40, - 0xa8, 0x00, 0x90, 0x00, 0x7c, 0x01, 0x6b, 0x02, 0x5f, 0x05, 0x55, 0x07, - 0x4c, 0x0a, 0x45, 0x0d, 0x40, 0x0f, 0x3c, 0x12, 0x37, 0x14, 0x34, 0x16, - 0x31, 0x18, 0x2e, 0x19, 0x2b, 0x1b, 0x2a, 0x1d, 0x28, 0x1e, 0x26, 0x20, - 0x25, 0x22, 0x23, 0x22, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x59, 0x23, 0x4d, 0x23, - 0x43, 0x24, 0x3b, 0x25, 0x34, 0x26, 0x2f, 0x27, 0x2b, 0x29, 0x28, 0x2a, - 0x25, 0x2b, 0x23, 0x2d, 0x21, 0x2e, 0x1f, 0x2e, 0x1d, 0x30, 0x1c, 0x30, - 0x1b, 0x31, 0x1a, 0x32, 0x19, 0x32, 0x18, 0x33, 0x17, 0x34, 0x16, 0x34, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x0f, 0x00, 0x16, 0x00, 0x1c, 0x00, 0x5e, 0x00, 0x5d, - 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, - 0x00, 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, - 0x00, 0x16, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9b, 0x00, 0x95, 0x00, 0x8a, 0x00, 0x7d, 0x00, 0x6d, 0x00, - 0x5d, 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x10, 0x00, - 0x0a, 0x04, 0x05, 0x07, 0x00, 0x0b, 0x00, 0x11, 0x9e, 0x00, 0x9c, 0x00, - 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, 0x00, - 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, - 0x01, 0x00, 0x00, 0x06, 0x3a, 0x3b, 0x32, 0x3a, 0x2c, 0x39, 0x27, 0x3a, - 0x23, 0x39, 0x21, 0x39, 0x1e, 0x3b, 0x1c, 0x3c, 0x1a, 0x3a, 0x19, 0x3a, - 0x17, 0x3b, 0x17, 0x3c, 0x15, 0x3d, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, - 0x13, 0x3d, 0x13, 0x3d, 0x11, 0x3d, 0x11, 0x3e, 0xaa, 0x00, 0x94, 0x00, - 0x82, 0x00, 0x72, 0x01, 0x65, 0x04, 0x5b, 0x05, 0x53, 0x07, 0x4b, 0x0a, - 0x45, 0x0c, 0x41, 0x0f, 0x3d, 0x11, 0x39, 0x12, 0x36, 0x15, 0x33, 0x16, - 0x30, 0x18, 0x2e, 0x19, 0x2b, 0x1a, 0x2b, 0x1d, 0x29, 0x1d, 0x26, 0x1e, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x5a, 0x23, 0x4f, 0x23, 0x46, 0x24, 0x3e, 0x24, - 0x37, 0x25, 0x32, 0x26, 0x2e, 0x27, 0x2b, 0x28, 0x28, 0x2a, 0x25, 0x2b, - 0x23, 0x2c, 0x21, 0x2d, 0x20, 0x2e, 0x1e, 0x2e, 0x1d, 0x30, 0x1c, 0x30, - 0x1b, 0x31, 0x1a, 0x32, 0x19, 0x32, 0x18, 0x33, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x00, 0x0e, 0x00, 0x14, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, - 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, - 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, - 0x96, 0x00, 0x8d, 0x00, 0x81, 0x00, 0x74, 0x00, 0x65, 0x00, 0x56, 0x00, - 0x46, 0x00, 0x37, 0x00, 0x29, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x0a, 0x03, - 0x06, 0x07, 0x01, 0x0a, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, - 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x43, 0x00, - 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, 0x00, - 0x3b, 0x3b, 0x33, 0x3b, 0x2d, 0x39, 0x29, 0x39, 0x25, 0x3a, 0x22, 0x39, - 0x20, 0x39, 0x1e, 0x3b, 0x1c, 0x3c, 0x1a, 0x3a, 0x19, 0x3a, 0x18, 0x3a, - 0x17, 0x3c, 0x16, 0x3c, 0x15, 0x3d, 0x15, 0x3e, 0x13, 0x3f, 0x13, 0x3d, - 0x13, 0x3d, 0x13, 0x3d, 0xaa, 0x00, 0x98, 0x00, 0x87, 0x00, 0x78, 0x01, - 0x6b, 0x02, 0x61, 0x04, 0x58, 0x06, 0x51, 0x08, 0x4b, 0x0a, 0x46, 0x0c, - 0x42, 0x0e, 0x3e, 0x0f, 0x3a, 0x12, 0x37, 0x12, 0x35, 0x16, 0x32, 0x16, - 0x30, 0x18, 0x2e, 0x19, 0x2b, 0x1a, 0x2b, 0x1c, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x5a, 0x23, 0x51, 0x23, 0x48, 0x23, 0x41, 0x24, 0x3a, 0x25, 0x35, 0x25, - 0x31, 0x26, 0x2e, 0x27, 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2b, 0x24, 0x2b, - 0x22, 0x2d, 0x21, 0x2d, 0x1f, 0x2e, 0x1e, 0x2e, 0x1d, 0x30, 0x1c, 0x30, - 0x1b, 0x30, 0x1a, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, - 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, - 0x00, 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, - 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x90, 0x00, - 0x85, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x42, 0x00, - 0x34, 0x00, 0x27, 0x00, 0x1b, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x06, 0x06, - 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, - 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, - 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x3b, 0x3c, 0x34, 0x3a, - 0x2f, 0x3a, 0x2a, 0x39, 0x26, 0x3a, 0x23, 0x3a, 0x21, 0x39, 0x1f, 0x3a, - 0x1d, 0x3b, 0x1c, 0x3c, 0x1a, 0x3b, 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3b, - 0x17, 0x3c, 0x15, 0x3c, 0x15, 0x3d, 0x15, 0x3f, 0x13, 0x3f, 0x13, 0x3e, - 0xac, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7c, 0x00, 0x71, 0x01, 0x67, 0x03, - 0x5e, 0x05, 0x57, 0x07, 0x51, 0x08, 0x4b, 0x0a, 0x46, 0x0c, 0x42, 0x0d, - 0x3f, 0x0f, 0x3b, 0x11, 0x39, 0x12, 0x35, 0x14, 0x34, 0x16, 0x31, 0x16, - 0x30, 0x18, 0x2e, 0x19, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x52, 0x23, - 0x4a, 0x23, 0x43, 0x24, 0x3d, 0x24, 0x38, 0x25, 0x34, 0x26, 0x30, 0x27, - 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x24, 0x2b, 0x22, 0x2c, - 0x21, 0x2d, 0x20, 0x2d, 0x1f, 0x2e, 0x1d, 0x2e, 0x1d, 0x30, 0x1c, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x5f, 0x00, 0x5e, - 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, - 0x00, 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, - 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, - 0x72, 0x00, 0x65, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3e, 0x00, 0x31, 0x00, - 0x25, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x9e, 0x00, 0x9d, 0x00, - 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, 0x00, - 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, - 0x1f, 0x00, 0x17, 0x00, 0x3b, 0x3c, 0x35, 0x3a, 0x30, 0x3b, 0x2c, 0x39, - 0x28, 0x3a, 0x25, 0x3b, 0x22, 0x39, 0x21, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, - 0x1c, 0x3c, 0x1a, 0x3b, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, 0x17, 0x3c, - 0x16, 0x3c, 0x15, 0x3c, 0x15, 0x3e, 0x14, 0x3f, 0xad, 0x00, 0x9d, 0x00, - 0x8e, 0x00, 0x81, 0x00, 0x76, 0x01, 0x6b, 0x02, 0x63, 0x04, 0x5c, 0x05, - 0x56, 0x07, 0x50, 0x08, 0x4b, 0x0a, 0x47, 0x0c, 0x43, 0x0c, 0x40, 0x0f, - 0x3c, 0x0f, 0x3a, 0x12, 0x37, 0x12, 0x35, 0x14, 0x33, 0x16, 0x30, 0x16, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x53, 0x23, 0x4c, 0x23, 0x45, 0x23, - 0x40, 0x24, 0x3b, 0x25, 0x36, 0x25, 0x33, 0x26, 0x30, 0x27, 0x2d, 0x28, - 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x25, 0x2b, 0x23, 0x2b, 0x22, 0x2d, - 0x20, 0x2d, 0x20, 0x2e, 0x1e, 0x2e, 0x1d, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, - 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, - 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, - 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6b, 0x00, - 0x5f, 0x00, 0x53, 0x00, 0x46, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x24, 0x00, - 0x19, 0x00, 0x0f, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, - 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x66, 0x00, 0x5b, 0x00, - 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, 0x00, - 0x3c, 0x3d, 0x36, 0x3a, 0x31, 0x3b, 0x2c, 0x3a, 0x29, 0x39, 0x26, 0x3a, - 0x24, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3c, - 0x1a, 0x3c, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, 0x17, 0x3b, 0x17, 0x3c, - 0x15, 0x3c, 0x15, 0x3c, 0xad, 0x00, 0x9f, 0x00, 0x91, 0x00, 0x85, 0x00, - 0x79, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x60, 0x04, 0x59, 0x05, 0x54, 0x07, - 0x4f, 0x08, 0x4b, 0x0a, 0x47, 0x0c, 0x43, 0x0c, 0x41, 0x0f, 0x3d, 0x0f, - 0x3b, 0x11, 0x38, 0x12, 0x35, 0x12, 0x35, 0x15, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x5b, 0x23, 0x54, 0x23, 0x4e, 0x23, 0x47, 0x23, 0x42, 0x24, 0x3c, 0x24, - 0x38, 0x25, 0x35, 0x25, 0x32, 0x26, 0x2f, 0x27, 0x2d, 0x28, 0x2a, 0x28, - 0x28, 0x2a, 0x26, 0x2a, 0x25, 0x2b, 0x23, 0x2b, 0x22, 0x2c, 0x21, 0x2d, - 0x20, 0x2d, 0x20, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3d, 0x36, 0x3a, - 0x32, 0x3b, 0x2d, 0x3b, 0x2b, 0x39, 0x28, 0x3a, 0x25, 0x3a, 0x23, 0x3a, - 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, - 0x19, 0x3a, 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3a, 0x17, 0x3c, 0x16, 0x3c, - 0xad, 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x74, 0x01, - 0x6c, 0x02, 0x65, 0x03, 0x5e, 0x04, 0x58, 0x05, 0x53, 0x07, 0x4f, 0x08, - 0x4b, 0x0a, 0x47, 0x0b, 0x44, 0x0c, 0x41, 0x0e, 0x3e, 0x0f, 0x3b, 0x0f, - 0x3a, 0x12, 0x37, 0x12, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x55, 0x23, - 0x4f, 0x23, 0x49, 0x23, 0x44, 0x24, 0x3f, 0x24, 0x3b, 0x25, 0x37, 0x25, - 0x34, 0x26, 0x31, 0x26, 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, - 0x27, 0x2a, 0x25, 0x2b, 0x24, 0x2b, 0x22, 0x2b, 0x22, 0x2d, 0x20, 0x2d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3b, 0x33, 0x3b, 0x2f, 0x3b, - 0x2b, 0x39, 0x29, 0x39, 0x26, 0x3a, 0x24, 0x3b, 0x22, 0x3a, 0x21, 0x39, - 0x1f, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, - 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3a, 0x17, 0x3b, 0xae, 0x00, 0xa2, 0x00, - 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x77, 0x00, 0x6f, 0x01, 0x67, 0x02, - 0x61, 0x04, 0x5c, 0x05, 0x57, 0x05, 0x52, 0x07, 0x4e, 0x08, 0x4b, 0x0a, - 0x47, 0x0b, 0x44, 0x0c, 0x41, 0x0d, 0x3f, 0x0f, 0x3c, 0x0f, 0x3b, 0x11, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x56, 0x23, 0x50, 0x23, 0x4a, 0x23, - 0x45, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x35, 0x25, 0x33, 0x26, - 0x30, 0x26, 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, - 0x25, 0x2a, 0x24, 0x2b, 0x23, 0x2b, 0x22, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x3e, 0x37, 0x3b, 0x33, 0x3a, 0x2f, 0x3b, 0x2c, 0x3a, 0x2a, 0x39, - 0x27, 0x3a, 0x25, 0x3a, 0x23, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, - 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, 0x19, 0x3a, - 0x19, 0x3a, 0x17, 0x3a, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, - 0x84, 0x00, 0x7a, 0x00, 0x72, 0x01, 0x6c, 0x02, 0x65, 0x02, 0x5f, 0x04, - 0x5a, 0x05, 0x56, 0x05, 0x52, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0b, - 0x45, 0x0c, 0x41, 0x0c, 0x40, 0x0f, 0x3d, 0x0f, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x5c, 0x23, 0x56, 0x23, 0x51, 0x23, 0x4c, 0x23, 0x47, 0x23, 0x42, 0x24, - 0x3e, 0x24, 0x3b, 0x25, 0x37, 0x25, 0x34, 0x25, 0x32, 0x26, 0x30, 0x26, - 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, - 0x25, 0x2b, 0x23, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x38, 0x3c, - 0x34, 0x3a, 0x30, 0x3b, 0x2d, 0x3b, 0x2a, 0x39, 0x28, 0x39, 0x26, 0x3a, - 0x24, 0x3b, 0x22, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, 0x1e, 0x3b, - 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, 0x19, 0x3a, 0x19, 0x3a, - 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, 0x00, 0x87, 0x00, 0x7e, 0x00, - 0x76, 0x00, 0x6f, 0x01, 0x68, 0x02, 0x62, 0x04, 0x5e, 0x04, 0x59, 0x05, - 0x55, 0x06, 0x51, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x45, 0x0c, - 0x41, 0x0c, 0x41, 0x0f, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x57, 0x23, - 0x52, 0x23, 0x4d, 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, 0x3c, 0x24, - 0x39, 0x25, 0x36, 0x25, 0x34, 0x25, 0x31, 0x26, 0x2f, 0x27, 0x2d, 0x27, - 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, 0x25, 0x2b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x38, 0x3c, 0x34, 0x3a, 0x31, 0x3b, - 0x2e, 0x3c, 0x2b, 0x39, 0x29, 0x39, 0x27, 0x3a, 0x25, 0x3a, 0x24, 0x3b, - 0x21, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, - 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3a, 0xaf, 0x00, 0xa6, 0x00, - 0x9b, 0x00, 0x91, 0x00, 0x88, 0x00, 0x80, 0x00, 0x78, 0x00, 0x71, 0x01, - 0x6b, 0x02, 0x66, 0x02, 0x61, 0x04, 0x5c, 0x04, 0x58, 0x05, 0x55, 0x07, - 0x50, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0c, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4d, 0x23, - 0x49, 0x23, 0x45, 0x23, 0x41, 0x24, 0x3d, 0x24, 0x3b, 0x24, 0x38, 0x25, - 0x35, 0x25, 0x33, 0x26, 0x31, 0x26, 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, - 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x3e, 0x38, 0x3d, 0x35, 0x3a, 0x32, 0x3b, 0x2e, 0x3b, 0x2c, 0x3a, - 0x2a, 0x39, 0x27, 0x39, 0x26, 0x3a, 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x39, - 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, - 0x1a, 0x3c, 0x19, 0x3c, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x93, 0x00, - 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6e, 0x01, 0x69, 0x02, - 0x63, 0x03, 0x5f, 0x04, 0x5b, 0x05, 0x56, 0x05, 0x54, 0x07, 0x4f, 0x07, - 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x46, 0x0c, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x5d, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4f, 0x23, 0x4a, 0x23, 0x46, 0x23, - 0x43, 0x24, 0x3f, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x36, 0x25, 0x34, 0x25, - 0x33, 0x26, 0x30, 0x26, 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, 0x2a, 0x28, - 0x28, 0x28, 0x28, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x39, 0x3e, - 0x35, 0x3a, 0x32, 0x3a, 0x2f, 0x3b, 0x2d, 0x3c, 0x2a, 0x39, 0x28, 0x39, - 0x27, 0x3a, 0x24, 0x3a, 0x24, 0x3b, 0x22, 0x3b, 0x21, 0x39, 0x20, 0x39, - 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, - 0xb0, 0x00, 0xa7, 0x00, 0x9e, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x86, 0x00, - 0x7e, 0x00, 0x77, 0x00, 0x71, 0x01, 0x6b, 0x01, 0x66, 0x02, 0x62, 0x04, - 0x5d, 0x04, 0x5a, 0x05, 0x55, 0x05, 0x53, 0x07, 0x4f, 0x07, 0x4e, 0x09, - 0x4a, 0x0a, 0x47, 0x0a, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, - 0x54, 0x23, 0x50, 0x23, 0x4c, 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, - 0x3d, 0x24, 0x3a, 0x24, 0x38, 0x25, 0x36, 0x25, 0x33, 0x25, 0x32, 0x26, - 0x2f, 0x26, 0x2f, 0x27, 0x2c, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x39, 0x3e, 0x35, 0x3b, 0x32, 0x3a, - 0x30, 0x3b, 0x2e, 0x3c, 0x2b, 0x3a, 0x2a, 0x39, 0x27, 0x39, 0x26, 0x3a, - 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x39, - 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0xb1, 0x00, 0xa7, 0x00, - 0x9f, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x87, 0x00, 0x80, 0x00, 0x79, 0x00, - 0x73, 0x00, 0x6e, 0x01, 0x69, 0x02, 0x64, 0x02, 0x60, 0x04, 0x5c, 0x04, - 0x59, 0x05, 0x55, 0x05, 0x53, 0x07, 0x4e, 0x07, 0x4e, 0x09, 0x4a, 0x0a, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, 0x23, - 0x4c, 0x23, 0x48, 0x23, 0x45, 0x23, 0x42, 0x24, 0x3f, 0x24, 0x3c, 0x24, - 0x39, 0x25, 0x37, 0x25, 0x35, 0x25, 0x33, 0x25, 0x31, 0x26, 0x2f, 0x26, - 0x2e, 0x27, 0x2c, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x94, 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xa9, 0x00, - 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, - 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb1, 0x00, - 0xb2, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x21, 0x00, 0x6e, 0x00, - 0x8a, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa8, 0x00, - 0xaa, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xad, 0x00, 0xad, 0x00, 0xad, 0x00, - 0xae, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, - 0x6c, 0x00, 0x92, 0x00, 0xa1, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xac, 0x00, - 0xae, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, - 0xb2, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, - 0xb3, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x04, 0x70, 0x01, - 0x7f, 0x00, 0x88, 0x00, 0x90, 0x00, 0x95, 0x00, 0x99, 0x00, 0x9c, 0x00, - 0x9f, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00, - 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x1c, 0x3a, 0x03, 0x59, 0x00, 0x6c, 0x00, - 0x79, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00, - 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, - 0xa5, 0x00, 0xa6, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x60, 0x0e, 0x79, 0x01, - 0x88, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa3, 0x00, - 0xa5, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00, 0xac, 0x00, - 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4e, 0x0f, 0x60, 0x07, 0x6d, 0x03, 0x77, 0x01, - 0x7f, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x8f, 0x00, 0x92, 0x00, 0x94, 0x00, - 0x97, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0, 0x00, 0xa1, 0x00, - 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa4, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x0a, 0x29, 0x27, 0x0f, 0x40, 0x06, 0x52, 0x02, 0x61, 0x00, 0x6c, 0x00, - 0x75, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x87, 0x00, 0x8b, 0x00, 0x8e, 0x00, - 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9b, 0x00, - 0x9d, 0x00, 0x9e, 0x00, 0x60, 0x14, 0x6f, 0x07, 0x7b, 0x03, 0x84, 0x01, - 0x8c, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9d, 0x00, 0x9f, 0x00, - 0xa1, 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa7, 0x00, - 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x49, 0x17, 0x56, 0x0d, 0x62, 0x07, 0x6b, 0x04, 0x73, 0x02, 0x79, 0x01, - 0x7e, 0x01, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, - 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, - 0x9e, 0x00, 0x9f, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x31, 0x1e, 0x18, - 0x32, 0x0d, 0x43, 0x07, 0x50, 0x04, 0x5a, 0x02, 0x63, 0x01, 0x6b, 0x00, - 0x72, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x85, 0x00, 0x88, 0x00, - 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, - 0x60, 0x18, 0x6b, 0x0c, 0x74, 0x06, 0x7d, 0x03, 0x83, 0x02, 0x89, 0x01, - 0x8d, 0x00, 0x91, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, - 0x9e, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xa4, 0x00, - 0xa5, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x1d, 0x51, 0x12, - 0x5b, 0x0c, 0x63, 0x08, 0x6a, 0x05, 0x70, 0x03, 0x77, 0x02, 0x7b, 0x02, - 0x7e, 0x01, 0x82, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8c, 0x00, 0x8e, 0x00, - 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x97, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x36, 0x19, 0x1f, 0x2a, 0x12, 0x38, 0x0b, - 0x44, 0x07, 0x4e, 0x05, 0x57, 0x02, 0x5f, 0x01, 0x65, 0x01, 0x6b, 0x00, - 0x71, 0x00, 0x76, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x84, 0x00, - 0x87, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x60, 0x1b, 0x68, 0x0f, - 0x70, 0x09, 0x77, 0x05, 0x7d, 0x03, 0x82, 0x02, 0x87, 0x01, 0x8b, 0x00, - 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, - 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x21, 0x4e, 0x16, 0x56, 0x10, 0x5d, 0x0b, - 0x65, 0x08, 0x69, 0x06, 0x6f, 0x04, 0x74, 0x03, 0x78, 0x02, 0x7b, 0x02, - 0x7e, 0x01, 0x81, 0x01, 0x85, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c, 0x00, - 0x8e, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x92, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x09, 0x3a, 0x17, 0x24, 0x24, 0x18, 0x30, 0x10, 0x3b, 0x0b, 0x45, 0x07, - 0x4d, 0x05, 0x55, 0x04, 0x5b, 0x02, 0x61, 0x01, 0x67, 0x01, 0x6b, 0x00, - 0x6f, 0x00, 0x74, 0x00, 0x77, 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x80, 0x00, - 0x83, 0x00, 0x86, 0x00, 0x60, 0x1d, 0x67, 0x12, 0x6d, 0x0c, 0x73, 0x08, - 0x79, 0x05, 0x7e, 0x03, 0x82, 0x02, 0x86, 0x02, 0x89, 0x01, 0x8c, 0x00, - 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0x99, 0x00, - 0x9a, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x44, 0x24, 0x4c, 0x1a, 0x53, 0x13, 0x59, 0x0e, 0x5f, 0x0b, 0x65, 0x08, - 0x69, 0x06, 0x6d, 0x05, 0x73, 0x04, 0x76, 0x03, 0x79, 0x02, 0x7c, 0x02, - 0x7e, 0x01, 0x81, 0x01, 0x84, 0x00, 0x87, 0x00, 0x89, 0x00, 0x8b, 0x00, - 0x8c, 0x00, 0x8d, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x3b, 0x14, 0x28, - 0x20, 0x1c, 0x2a, 0x14, 0x34, 0x0f, 0x3e, 0x0b, 0x45, 0x07, 0x4c, 0x05, - 0x53, 0x04, 0x58, 0x03, 0x5e, 0x02, 0x63, 0x01, 0x67, 0x01, 0x6c, 0x00, - 0x6f, 0x00, 0x72, 0x00, 0x76, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x7e, 0x00, - 0x60, 0x1d, 0x66, 0x14, 0x6b, 0x0e, 0x71, 0x0a, 0x76, 0x07, 0x7a, 0x05, - 0x7e, 0x03, 0x81, 0x02, 0x85, 0x02, 0x88, 0x01, 0x8b, 0x01, 0x8d, 0x00, - 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x98, 0x00, - 0x99, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x27, 0x4a, 0x1d, - 0x4f, 0x16, 0x57, 0x11, 0x5b, 0x0e, 0x61, 0x0b, 0x66, 0x08, 0x69, 0x07, - 0x6d, 0x05, 0x71, 0x04, 0x75, 0x03, 0x77, 0x02, 0x7a, 0x02, 0x7c, 0x02, - 0x7e, 0x01, 0x80, 0x01, 0x83, 0x01, 0x86, 0x00, 0x88, 0x00, 0x89, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x3d, 0x13, 0x2c, 0x1c, 0x20, 0x27, 0x18, - 0x2f, 0x12, 0x38, 0x0e, 0x3e, 0x0a, 0x45, 0x07, 0x4b, 0x06, 0x51, 0x05, - 0x57, 0x04, 0x5c, 0x02, 0x60, 0x02, 0x65, 0x01, 0x67, 0x01, 0x6c, 0x00, - 0x6f, 0x00, 0x71, 0x00, 0x75, 0x00, 0x77, 0x00, 0x60, 0x1e, 0x65, 0x16, - 0x6a, 0x10, 0x6f, 0x0c, 0x73, 0x09, 0x77, 0x07, 0x7b, 0x05, 0x7e, 0x03, - 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8e, 0x00, - 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, - 0x27, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x8f, 0x00, 0x96, 0x00, 0x99, 0x00, - 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, - 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x2c, 0x14, 0x57, 0x00, - 0x82, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, 0x00, - 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, - 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x8c, 0x00, 0x63, 0x00, 0x7f, 0x00, 0x89, 0x00, 0x90, 0x00, 0x96, 0x00, - 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, - 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x31, 0x11, 0x5f, 0x00, - 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, - 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, - 0x9e, 0x00, 0x9e, 0x00, 0x43, 0x29, 0x48, 0x20, 0x4d, 0x19, 0x54, 0x14, - 0x58, 0x10, 0x5c, 0x0d, 0x62, 0x0b, 0x66, 0x09, 0x69, 0x07, 0x6c, 0x06, - 0x70, 0x05, 0x74, 0x04, 0x76, 0x03, 0x78, 0x02, 0x7a, 0x02, 0x7c, 0x02, - 0x7e, 0x02, 0x80, 0x01, 0x82, 0x01, 0x85, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x09, 0x3f, 0x11, 0x2f, 0x1b, 0x23, 0x23, 0x1b, 0x2b, 0x15, 0x32, 0x11, - 0x39, 0x0d, 0x40, 0x0a, 0x45, 0x08, 0x4b, 0x07, 0x51, 0x05, 0x56, 0x04, - 0x59, 0x03, 0x5e, 0x02, 0x61, 0x02, 0x65, 0x01, 0x68, 0x01, 0x6b, 0x00, - 0x6e, 0x00, 0x71, 0x00, 0x60, 0x1f, 0x64, 0x17, 0x69, 0x11, 0x6d, 0x0d, - 0x71, 0x0a, 0x75, 0x08, 0x78, 0x06, 0x7b, 0x05, 0x7e, 0x04, 0x81, 0x03, - 0x84, 0x02, 0x86, 0x02, 0x88, 0x01, 0x8a, 0x01, 0x8c, 0x01, 0x8e, 0x00, - 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x00, 0x00, 0x0d, 0x00, - 0x4f, 0x00, 0x73, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x95, 0x00, - 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, - 0x9c, 0x00, 0x9d, 0x00, 0x01, 0x29, 0x16, 0x04, 0x57, 0x00, 0x78, 0x00, - 0x87, 0x00, 0x8f, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, - 0x9b, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x4d, 0x00, - 0x60, 0x00, 0x77, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x92, 0x00, 0x95, 0x00, - 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, - 0x9c, 0x00, 0x9d, 0x00, 0x03, 0x23, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, - 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, 0x00, - 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, - 0x43, 0x2b, 0x47, 0x22, 0x4c, 0x1b, 0x51, 0x16, 0x56, 0x12, 0x59, 0x0f, - 0x5e, 0x0d, 0x63, 0x0b, 0x66, 0x09, 0x69, 0x07, 0x6b, 0x06, 0x6f, 0x05, - 0x73, 0x05, 0x75, 0x03, 0x77, 0x03, 0x79, 0x02, 0x7a, 0x02, 0x7c, 0x02, - 0x7e, 0x02, 0x7f, 0x01, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x40, 0x11, 0x31, - 0x18, 0x26, 0x20, 0x1e, 0x28, 0x18, 0x2f, 0x13, 0x35, 0x0f, 0x3c, 0x0c, - 0x41, 0x0a, 0x46, 0x08, 0x4b, 0x07, 0x50, 0x05, 0x54, 0x04, 0x58, 0x04, - 0x5c, 0x02, 0x5f, 0x02, 0x62, 0x02, 0x66, 0x01, 0x69, 0x01, 0x6b, 0x00, - 0x60, 0x20, 0x64, 0x18, 0x68, 0x13, 0x6b, 0x0f, 0x6f, 0x0c, 0x73, 0x09, - 0x76, 0x07, 0x79, 0x06, 0x7c, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, - 0x85, 0x02, 0x88, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8e, 0x00, - 0x90, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x38, 0x00, - 0x5b, 0x00, 0x6f, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, - 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, - 0x00, 0x49, 0x00, 0x27, 0x12, 0x01, 0x43, 0x00, 0x61, 0x00, 0x74, 0x00, - 0x7f, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, 0x00, - 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x60, 0x00, 0x24, 0x00, 0x48, 0x00, - 0x60, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, - 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, - 0x00, 0x46, 0x00, 0x1f, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, - 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, - 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x43, 0x2d, 0x46, 0x24, - 0x4b, 0x1e, 0x4f, 0x19, 0x55, 0x14, 0x58, 0x11, 0x5b, 0x0e, 0x5f, 0x0c, - 0x64, 0x0a, 0x66, 0x09, 0x68, 0x07, 0x6a, 0x06, 0x6e, 0x06, 0x72, 0x05, - 0x74, 0x04, 0x76, 0x03, 0x78, 0x03, 0x79, 0x02, 0x7b, 0x02, 0x7c, 0x02, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x40, 0x10, 0x33, 0x17, 0x28, 0x1e, 0x20, - 0x25, 0x1a, 0x2c, 0x16, 0x32, 0x12, 0x37, 0x0f, 0x3d, 0x0c, 0x42, 0x0a, - 0x46, 0x08, 0x4b, 0x07, 0x4f, 0x05, 0x53, 0x05, 0x57, 0x04, 0x5a, 0x04, - 0x5e, 0x02, 0x61, 0x02, 0x63, 0x01, 0x66, 0x01, 0x60, 0x20, 0x63, 0x19, - 0x67, 0x14, 0x6a, 0x10, 0x6e, 0x0d, 0x71, 0x0b, 0x74, 0x09, 0x77, 0x07, - 0x7a, 0x06, 0x7c, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, - 0x87, 0x02, 0x88, 0x02, 0x8a, 0x01, 0x8c, 0x01, 0x8d, 0x00, 0x8e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x48, 0x00, - 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, - 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x54, 0x00, 0x41, - 0x00, 0x17, 0x10, 0x00, 0x36, 0x00, 0x51, 0x00, 0x64, 0x00, 0x71, 0x00, - 0x7a, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8d, 0x00, 0x90, 0x00, - 0x92, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x89, 0x00, 0x77, 0x00, 0x48, 0x00, 0x0f, 0x00, 0x30, 0x00, 0x48, 0x00, - 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, - 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x52, 0x00, 0x3c, - 0x00, 0x0d, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, 0x00, - 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, - 0x93, 0x00, 0x95, 0x00, 0x42, 0x2e, 0x45, 0x26, 0x4b, 0x1f, 0x4d, 0x1a, - 0x52, 0x16, 0x56, 0x13, 0x59, 0x10, 0x5c, 0x0e, 0x61, 0x0c, 0x64, 0x0a, - 0x66, 0x09, 0x68, 0x07, 0x6a, 0x07, 0x6d, 0x06, 0x71, 0x05, 0x74, 0x05, - 0x75, 0x03, 0x77, 0x03, 0x78, 0x02, 0x7a, 0x02, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x09, 0x41, 0x0f, 0x34, 0x16, 0x2a, 0x1c, 0x23, 0x23, 0x1d, 0x29, 0x18, - 0x2e, 0x14, 0x34, 0x11, 0x39, 0x0e, 0x3e, 0x0c, 0x42, 0x0a, 0x47, 0x08, - 0x4b, 0x07, 0x4f, 0x05, 0x52, 0x05, 0x56, 0x04, 0x59, 0x04, 0x5c, 0x03, - 0x5f, 0x02, 0x62, 0x02, 0x60, 0x20, 0x63, 0x1a, 0x66, 0x15, 0x6a, 0x11, - 0x6d, 0x0e, 0x70, 0x0c, 0x73, 0x0a, 0x75, 0x08, 0x78, 0x07, 0x7a, 0x06, - 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, 0x86, 0x02, - 0x88, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, - 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, - 0x86, 0x00, 0x89, 0x00, 0x00, 0x59, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x0e, - 0x10, 0x00, 0x2e, 0x00, 0x46, 0x00, 0x57, 0x00, 0x64, 0x00, 0x6f, 0x00, - 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x83, 0x00, - 0x60, 0x00, 0x30, 0x00, 0x02, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, - 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, - 0x86, 0x00, 0x89, 0x00, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, - 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, 0x00, - 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, - 0x42, 0x2f, 0x44, 0x27, 0x4a, 0x22, 0x4c, 0x1c, 0x50, 0x18, 0x55, 0x14, - 0x57, 0x12, 0x5a, 0x0f, 0x5d, 0x0d, 0x62, 0x0c, 0x64, 0x0a, 0x66, 0x09, - 0x68, 0x08, 0x6a, 0x07, 0x6c, 0x06, 0x70, 0x05, 0x73, 0x05, 0x75, 0x04, - 0x76, 0x03, 0x78, 0x03, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x42, 0x0f, 0x36, - 0x15, 0x2c, 0x1b, 0x25, 0x21, 0x1f, 0x26, 0x1a, 0x2c, 0x16, 0x31, 0x12, - 0x36, 0x0f, 0x3a, 0x0d, 0x3f, 0x0c, 0x43, 0x0a, 0x47, 0x08, 0x4b, 0x07, - 0x4e, 0x05, 0x52, 0x05, 0x55, 0x04, 0x58, 0x04, 0x5b, 0x04, 0x5d, 0x02, - 0x60, 0x21, 0x63, 0x1b, 0x66, 0x16, 0x69, 0x12, 0x6c, 0x0f, 0x6e, 0x0d, - 0x71, 0x0b, 0x74, 0x09, 0x76, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7d, 0x05, - 0x7f, 0x04, 0x81, 0x03, 0x82, 0x02, 0x84, 0x02, 0x86, 0x02, 0x87, 0x02, - 0x89, 0x02, 0x8a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, - 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, - 0x00, 0x5b, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x23, 0x02, 0x0a, 0x10, 0x00, - 0x29, 0x00, 0x3d, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x65, 0x00, 0x6d, 0x00, - 0x74, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x70, 0x00, 0x48, 0x00, - 0x22, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, - 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, - 0x00, 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x04, 0x00, 0x1f, 0x00, - 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, - 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, 0x00, 0x42, 0x31, 0x44, 0x29, - 0x49, 0x23, 0x4c, 0x1e, 0x4e, 0x1a, 0x53, 0x16, 0x56, 0x13, 0x58, 0x11, - 0x5a, 0x0f, 0x5f, 0x0d, 0x63, 0x0b, 0x64, 0x0a, 0x66, 0x09, 0x68, 0x08, - 0x6a, 0x07, 0x6b, 0x06, 0x6f, 0x06, 0x72, 0x05, 0x74, 0x05, 0x75, 0x03, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x43, 0x0f, 0x37, 0x14, 0x2e, 0x19, 0x27, - 0x1f, 0x21, 0x24, 0x1c, 0x29, 0x18, 0x2e, 0x15, 0x33, 0x12, 0x37, 0x0f, - 0x3b, 0x0c, 0x40, 0x0c, 0x43, 0x0a, 0x47, 0x08, 0x4b, 0x07, 0x4e, 0x06, - 0x51, 0x05, 0x55, 0x05, 0x56, 0x04, 0x5a, 0x04, 0x60, 0x21, 0x63, 0x1b, - 0x65, 0x17, 0x68, 0x13, 0x6b, 0x10, 0x6e, 0x0e, 0x70, 0x0c, 0x73, 0x0a, - 0x75, 0x09, 0x77, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7d, 0x05, 0x7f, 0x04, - 0x81, 0x03, 0x82, 0x03, 0x84, 0x02, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, - 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x5c, 0x00, 0x56, - 0x00, 0x47, 0x00, 0x31, 0x00, 0x1a, 0x04, 0x08, 0x10, 0x00, 0x25, 0x00, - 0x37, 0x00, 0x46, 0x00, 0x52, 0x00, 0x5d, 0x00, 0x65, 0x00, 0x6c, 0x00, - 0x72, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, - 0x01, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, - 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x5c, 0x00, 0x55, - 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x09, 0x00, 0x1f, 0x00, 0x33, 0x00, - 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, - 0x77, 0x00, 0x7b, 0x00, 0x42, 0x31, 0x44, 0x2a, 0x48, 0x24, 0x4b, 0x1f, - 0x4d, 0x1b, 0x51, 0x18, 0x55, 0x15, 0x57, 0x12, 0x59, 0x10, 0x5b, 0x0e, - 0x5f, 0x0c, 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x6a, 0x07, - 0x6b, 0x06, 0x6e, 0x06, 0x71, 0x05, 0x73, 0x05, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x0a, 0x43, 0x0e, 0x38, 0x13, 0x2f, 0x18, 0x28, 0x1d, 0x22, 0x22, 0x1d, - 0x27, 0x19, 0x2b, 0x16, 0x30, 0x12, 0x35, 0x11, 0x39, 0x0f, 0x3c, 0x0c, - 0x41, 0x0b, 0x44, 0x0a, 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x50, 0x05, - 0x54, 0x05, 0x55, 0x04, 0x60, 0x21, 0x63, 0x1c, 0x65, 0x17, 0x68, 0x14, - 0x6a, 0x11, 0x6c, 0x0e, 0x6f, 0x0c, 0x71, 0x0b, 0x73, 0x09, 0x76, 0x08, - 0x78, 0x07, 0x79, 0x06, 0x7c, 0x05, 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, - 0x82, 0x03, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, - 0x5f, 0x00, 0x66, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4d, 0x00, 0x3b, - 0x00, 0x27, 0x00, 0x13, 0x06, 0x07, 0x10, 0x00, 0x22, 0x00, 0x32, 0x00, - 0x40, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, 0x00, - 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, - 0x5f, 0x00, 0x66, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, - 0x00, 0x1f, 0x00, 0x08, 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, 0x00, - 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, 0x00, - 0x42, 0x32, 0x44, 0x2b, 0x47, 0x25, 0x4b, 0x21, 0x4c, 0x1d, 0x4f, 0x19, - 0x54, 0x16, 0x56, 0x14, 0x58, 0x11, 0x5a, 0x10, 0x5d, 0x0e, 0x61, 0x0c, - 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x69, 0x07, 0x6b, 0x07, - 0x6d, 0x06, 0x71, 0x05, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x43, 0x0e, 0x39, - 0x12, 0x31, 0x17, 0x2a, 0x1c, 0x24, 0x21, 0x1f, 0x26, 0x1b, 0x2a, 0x18, - 0x2e, 0x16, 0x32, 0x12, 0x35, 0x0f, 0x3a, 0x0f, 0x3d, 0x0c, 0x41, 0x0b, - 0x44, 0x0a, 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x4f, 0x05, 0x53, 0x05, - 0x60, 0x21, 0x62, 0x1c, 0x65, 0x18, 0x67, 0x15, 0x6a, 0x12, 0x6c, 0x0f, - 0x6e, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x09, 0x76, 0x07, 0x79, 0x07, - 0x7a, 0x06, 0x7c, 0x05, 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x82, 0x03, - 0x83, 0x02, 0x85, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, - 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x42, 0x00, 0x32, 0x00, 0x20, - 0x00, 0x0e, 0x07, 0x06, 0x10, 0x00, 0x20, 0x00, 0x2f, 0x00, 0x3b, 0x00, - 0x46, 0x00, 0x50, 0x00, 0x58, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, - 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, - 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, - 0x00, 0x03, 0x0f, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, - 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, 0x00, 0x42, 0x33, 0x43, 0x2c, - 0x46, 0x27, 0x4a, 0x22, 0x4c, 0x1e, 0x4d, 0x1b, 0x52, 0x18, 0x55, 0x15, - 0x57, 0x13, 0x58, 0x11, 0x5a, 0x0f, 0x5e, 0x0e, 0x62, 0x0c, 0x63, 0x0b, - 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x69, 0x07, 0x6b, 0x07, 0x6c, 0x06, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0d, 0x3a, 0x12, 0x32, 0x16, 0x2b, - 0x1b, 0x26, 0x1f, 0x22, 0x24, 0x1d, 0x28, 0x19, 0x2b, 0x16, 0x30, 0x14, - 0x34, 0x12, 0x37, 0x0f, 0x3b, 0x0e, 0x3e, 0x0c, 0x41, 0x0b, 0x45, 0x0a, - 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x4f, 0x05, 0x60, 0x22, 0x62, 0x1d, - 0x65, 0x19, 0x66, 0x15, 0x69, 0x13, 0x6b, 0x11, 0x6d, 0x0e, 0x6f, 0x0c, - 0x71, 0x0b, 0x73, 0x0a, 0x76, 0x09, 0x77, 0x07, 0x79, 0x07, 0x7a, 0x06, - 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, 0x82, 0x03, 0x83, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x5e, 0x00, 0x5b, - 0x00, 0x53, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2a, 0x00, 0x1a, 0x00, 0x0b, - 0x08, 0x05, 0x10, 0x00, 0x1e, 0x00, 0x2c, 0x00, 0x37, 0x00, 0x42, 0x00, - 0x4b, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, - 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x5e, 0x00, 0x5b, - 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, - 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, - 0x54, 0x00, 0x5b, 0x00, 0x42, 0x33, 0x43, 0x2d, 0x45, 0x28, 0x49, 0x23, - 0x4b, 0x1f, 0x4d, 0x1c, 0x50, 0x19, 0x55, 0x16, 0x56, 0x14, 0x58, 0x12, - 0x59, 0x11, 0x5b, 0x0e, 0x5f, 0x0d, 0x62, 0x0c, 0x63, 0x0b, 0x65, 0x0a, - 0x66, 0x09, 0x68, 0x09, 0x69, 0x07, 0x6b, 0x07, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x0a, 0x44, 0x0d, 0x3a, 0x12, 0x33, 0x16, 0x2c, 0x19, 0x27, 0x1e, 0x22, - 0x21, 0x1e, 0x26, 0x1a, 0x2b, 0x18, 0x2e, 0x16, 0x31, 0x12, 0x35, 0x11, - 0x38, 0x0f, 0x3b, 0x0d, 0x3f, 0x0c, 0x41, 0x0a, 0x45, 0x0a, 0x47, 0x09, - 0x4a, 0x07, 0x4e, 0x07, 0x60, 0x22, 0x62, 0x1d, 0x64, 0x19, 0x66, 0x16, - 0x68, 0x13, 0x6a, 0x11, 0x6c, 0x0f, 0x6e, 0x0d, 0x71, 0x0c, 0x73, 0x0b, - 0x74, 0x09, 0x76, 0x08, 0x78, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7c, 0x05, - 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, 0x82, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, - 0x33, 0x00, 0x3d, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4c, - 0x00, 0x40, 0x00, 0x32, 0x00, 0x24, 0x00, 0x15, 0x02, 0x0a, 0x09, 0x04, - 0x10, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x34, 0x00, 0x3e, 0x00, 0x46, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, - 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, - 0x33, 0x00, 0x3d, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, - 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x04, 0x00, 0x12, 0x00, - 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, 0x00, - 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, 0x49, 0x24, 0x4b, 0x20, 0x4c, 0x1d, - 0x4e, 0x1a, 0x53, 0x18, 0x55, 0x15, 0x56, 0x13, 0x58, 0x11, 0x5a, 0x10, - 0x5c, 0x0e, 0x60, 0x0d, 0x63, 0x0c, 0x64, 0x0b, 0x65, 0x0a, 0x66, 0x09, - 0x68, 0x09, 0x69, 0x07, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0c, 0x3b, - 0x11, 0x34, 0x16, 0x2e, 0x19, 0x28, 0x1d, 0x23, 0x21, 0x20, 0x25, 0x1d, - 0x29, 0x19, 0x2b, 0x16, 0x30, 0x14, 0x33, 0x12, 0x35, 0x0f, 0x3a, 0x0f, - 0x3c, 0x0c, 0x40, 0x0c, 0x41, 0x0a, 0x45, 0x0a, 0x47, 0x09, 0x4a, 0x07, - 0x60, 0x22, 0x62, 0x1d, 0x64, 0x1a, 0x66, 0x17, 0x68, 0x14, 0x6a, 0x11, - 0x6c, 0x10, 0x6e, 0x0e, 0x70, 0x0c, 0x71, 0x0b, 0x73, 0x0a, 0x75, 0x09, - 0x76, 0x07, 0x78, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7c, 0x05, 0x7e, 0x05, - 0x7f, 0x04, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, - 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, - 0x00, 0x2c, 0x00, 0x1f, 0x00, 0x12, 0x03, 0x09, 0x09, 0x04, 0x10, 0x00, - 0x1c, 0x00, 0x27, 0x00, 0x31, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, - 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, - 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, - 0x00, 0x24, 0x00, 0x15, 0x00, 0x07, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, - 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, 0x00, 0x42, 0x34, 0x43, 0x2f, - 0x44, 0x29, 0x49, 0x25, 0x4b, 0x21, 0x4c, 0x1e, 0x4d, 0x1b, 0x51, 0x18, - 0x55, 0x16, 0x56, 0x14, 0x58, 0x13, 0x59, 0x11, 0x5a, 0x0f, 0x5d, 0x0e, - 0x61, 0x0c, 0x63, 0x0c, 0x64, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x09, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0c, 0x3c, 0x10, 0x35, 0x15, 0x2f, - 0x19, 0x2a, 0x1c, 0x25, 0x20, 0x22, 0x23, 0x1d, 0x26, 0x1a, 0x2b, 0x18, - 0x2e, 0x16, 0x30, 0x12, 0x35, 0x12, 0x37, 0x0f, 0x3b, 0x0f, 0x3d, 0x0c, - 0x41, 0x0c, 0x42, 0x0a, 0x46, 0x0a, 0x47, 0x09, 0x60, 0x22, 0x62, 0x1e, - 0x64, 0x1a, 0x66, 0x17, 0x68, 0x15, 0x6a, 0x12, 0x6b, 0x11, 0x6d, 0x0e, - 0x6e, 0x0d, 0x71, 0x0c, 0x72, 0x0b, 0x74, 0x09, 0x76, 0x09, 0x77, 0x07, - 0x79, 0x07, 0x7a, 0x06, 0x7c, 0x06, 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x5e, 0x00, 0x5d, - 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x26, - 0x00, 0x1a, 0x00, 0x0f, 0x04, 0x08, 0x0a, 0x04, 0x10, 0x00, 0x1b, 0x00, - 0x25, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, - 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x5e, 0x00, 0x5d, - 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, - 0x00, 0x10, 0x00, 0x03, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, - 0x33, 0x00, 0x3b, 0x00, 0x4f, 0x2a, 0x56, 0x28, 0x59, 0x27, 0x5a, 0x26, - 0x5c, 0x26, 0x5c, 0x26, 0x5c, 0x26, 0x5d, 0x26, 0x5d, 0x26, 0x5d, 0x25, - 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5e, 0x25, 0x5e, 0x25, - 0x5e, 0x24, 0x5e, 0x24, 0x5e, 0x24, 0x5e, 0x24, 0x92, 0x0e, 0x79, 0x14, - 0x6f, 0x18, 0x6b, 0x1b, 0x68, 0x1d, 0x67, 0x1d, 0x66, 0x1e, 0x65, 0x1f, - 0x64, 0x20, 0x64, 0x20, 0x63, 0x20, 0x63, 0x21, 0x63, 0x21, 0x63, 0x21, - 0x63, 0x21, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, - 0x16, 0x23, 0x3c, 0x23, 0x4a, 0x23, 0x50, 0x23, 0x54, 0x23, 0x56, 0x23, - 0x58, 0x23, 0x59, 0x23, 0x5a, 0x23, 0x5a, 0x23, 0x5b, 0x23, 0x5b, 0x23, - 0x5b, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, - 0x5d, 0x23, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x16, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, - 0x00, 0x4b, 0x00, 0x42, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x22, 0x00, 0x17, - 0x00, 0x0c, 0x05, 0x07, 0x0a, 0x03, 0x0f, 0x00, 0x1a, 0x00, 0x24, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, - 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, - 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x16, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, - 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, - 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, 0x00, - 0x48, 0x31, 0x4f, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x29, 0x58, 0x29, - 0x59, 0x28, 0x59, 0x27, 0x5a, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0x5c, 0x27, 0x5c, 0x27, 0xa1, 0x01, 0x88, 0x07, 0x7b, 0x0c, 0x74, 0x0f, - 0x70, 0x12, 0x6d, 0x14, 0x6b, 0x16, 0x6a, 0x17, 0x69, 0x18, 0x68, 0x19, - 0x67, 0x1a, 0x66, 0x1b, 0x66, 0x1b, 0x65, 0x1c, 0x65, 0x1c, 0x65, 0x1d, - 0x65, 0x1d, 0x64, 0x1d, 0x64, 0x1e, 0x64, 0x1e, 0x0a, 0x32, 0x22, 0x25, - 0x31, 0x24, 0x3b, 0x23, 0x42, 0x23, 0x47, 0x23, 0x4a, 0x23, 0x4d, 0x23, - 0x4f, 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, 0x23, 0x55, 0x23, - 0x56, 0x23, 0x56, 0x23, 0x57, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, - 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, - 0x00, 0x3c, 0x00, 0x32, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x0b, - 0x06, 0x07, 0x0b, 0x03, 0x0f, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, - 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, - 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, - 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, - 0x00, 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x01, 0x00, - 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, 0x00, 0x45, 0x34, 0x4b, 0x31, - 0x4f, 0x2f, 0x52, 0x2d, 0x54, 0x2c, 0x55, 0x2b, 0x57, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x29, 0x58, 0x29, 0x59, 0x28, 0x59, 0x27, 0x5a, 0x27, - 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0xa7, 0x00, 0x92, 0x03, 0x84, 0x06, 0x7d, 0x09, 0x77, 0x0c, 0x73, 0x0e, - 0x71, 0x10, 0x6f, 0x11, 0x6d, 0x13, 0x6b, 0x14, 0x6a, 0x15, 0x6a, 0x16, - 0x69, 0x17, 0x68, 0x17, 0x68, 0x18, 0x67, 0x19, 0x66, 0x19, 0x66, 0x1a, - 0x66, 0x1a, 0x66, 0x1a, 0x0a, 0x38, 0x18, 0x2b, 0x25, 0x27, 0x2e, 0x25, - 0x35, 0x24, 0x3b, 0x23, 0x3f, 0x23, 0x43, 0x23, 0x46, 0x23, 0x48, 0x23, - 0x4a, 0x23, 0x4c, 0x23, 0x4e, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x51, 0x23, - 0x52, 0x23, 0x53, 0x23, 0x53, 0x23, 0x54, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, - 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x48, 0x00, 0x40, 0x00, 0x37, - 0x00, 0x2d, 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x01, 0x0a, 0x06, 0x06, - 0x0b, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, - 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, - 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, - 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, - 0x00, 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x03, 0x00, 0x0d, 0x00, - 0x17, 0x00, 0x1f, 0x00, 0x44, 0x36, 0x49, 0x33, 0x4d, 0x31, 0x4f, 0x2f, - 0x51, 0x2e, 0x53, 0x2d, 0x53, 0x2c, 0x55, 0x2b, 0x56, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x58, 0x29, - 0x59, 0x28, 0x5a, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0xaa, 0x00, 0x98, 0x01, - 0x8c, 0x03, 0x83, 0x05, 0x7d, 0x08, 0x79, 0x0a, 0x76, 0x0c, 0x73, 0x0d, - 0x71, 0x0f, 0x6f, 0x10, 0x6e, 0x11, 0x6d, 0x12, 0x6c, 0x13, 0x6b, 0x14, - 0x6a, 0x15, 0x6a, 0x15, 0x69, 0x16, 0x68, 0x17, 0x68, 0x17, 0x68, 0x18, - 0x0a, 0x3c, 0x14, 0x30, 0x1e, 0x2a, 0x26, 0x27, 0x2d, 0x25, 0x32, 0x24, - 0x36, 0x24, 0x3b, 0x24, 0x3e, 0x23, 0x41, 0x23, 0x43, 0x23, 0x45, 0x23, - 0x47, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4d, 0x23, 0x4d, 0x23, - 0x4f, 0x23, 0x50, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x31, 0x11, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, - 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, - 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x11, 0x03, 0x23, - 0x00, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, - 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, - 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x38, 0x48, 0x35, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x30, 0x50, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2c, 0x54, 0x2b, 0x56, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x58, 0x29, 0xac, 0x00, 0x9d, 0x00, 0x91, 0x02, 0x89, 0x03, - 0x82, 0x05, 0x7e, 0x07, 0x7a, 0x09, 0x77, 0x0a, 0x75, 0x0c, 0x73, 0x0d, - 0x71, 0x0e, 0x70, 0x0f, 0x6e, 0x10, 0x6e, 0x11, 0x6c, 0x12, 0x6c, 0x13, - 0x6b, 0x13, 0x6a, 0x14, 0x6a, 0x15, 0x6a, 0x15, 0x09, 0x3f, 0x11, 0x33, - 0x1a, 0x2d, 0x21, 0x29, 0x27, 0x27, 0x2c, 0x26, 0x30, 0x25, 0x34, 0x24, - 0x37, 0x24, 0x3a, 0x24, 0x3d, 0x23, 0x40, 0x23, 0x42, 0x23, 0x44, 0x23, - 0x45, 0x23, 0x47, 0x23, 0x48, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x23, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, - 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, - 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x3c, - 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, - 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x39, 0x46, 0x36, - 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x54, 0x2b, 0x55, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, - 0xae, 0x00, 0xa1, 0x00, 0x96, 0x01, 0x8d, 0x02, 0x87, 0x03, 0x82, 0x05, - 0x7e, 0x07, 0x7b, 0x08, 0x78, 0x09, 0x76, 0x0b, 0x74, 0x0c, 0x73, 0x0d, - 0x71, 0x0e, 0x70, 0x0e, 0x6f, 0x0f, 0x6e, 0x11, 0x6d, 0x11, 0x6c, 0x11, - 0x6c, 0x12, 0x6b, 0x13, 0x09, 0x40, 0x10, 0x36, 0x17, 0x2f, 0x1d, 0x2c, - 0x22, 0x29, 0x27, 0x27, 0x2b, 0x26, 0x2f, 0x25, 0x32, 0x25, 0x35, 0x24, - 0x38, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3f, 0x23, 0x40, 0x23, 0x42, 0x23, - 0x44, 0x23, 0x45, 0x23, 0x46, 0x23, 0x48, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x1f, - 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, 0x00, - 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, - 0x99, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x39, - 0x00, 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, - 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x33, - 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x52, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x54, 0x2c, 0x55, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0xaf, 0x00, 0xa3, 0x00, - 0x99, 0x00, 0x91, 0x01, 0x8b, 0x02, 0x86, 0x03, 0x81, 0x05, 0x7e, 0x06, - 0x7b, 0x07, 0x79, 0x09, 0x77, 0x0a, 0x75, 0x0b, 0x74, 0x0c, 0x73, 0x0c, - 0x71, 0x0d, 0x71, 0x0e, 0x6f, 0x0f, 0x6e, 0x10, 0x6e, 0x11, 0x6d, 0x11, - 0x09, 0x41, 0x0f, 0x38, 0x15, 0x32, 0x1a, 0x2e, 0x1f, 0x2b, 0x24, 0x29, - 0x27, 0x27, 0x2b, 0x26, 0x2e, 0x25, 0x31, 0x25, 0x34, 0x25, 0x36, 0x24, - 0x38, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3e, 0x23, 0x40, 0x23, 0x41, 0x23, - 0x43, 0x23, 0x44, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x1f, 0x00, - 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x84, 0x00, - 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x7c, 0x00, - 0x4d, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, - 0x00, 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, - 0x00, 0x53, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3a, 0x45, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x53, 0x2c, 0x55, 0x2b, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0xb0, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x95, 0x00, - 0x8e, 0x02, 0x89, 0x02, 0x85, 0x03, 0x81, 0x05, 0x7e, 0x06, 0x7c, 0x07, - 0x7a, 0x08, 0x78, 0x09, 0x76, 0x0a, 0x75, 0x0b, 0x73, 0x0c, 0x73, 0x0c, - 0x71, 0x0d, 0x71, 0x0e, 0x70, 0x0e, 0x6e, 0x0f, 0x09, 0x42, 0x0e, 0x39, - 0x13, 0x33, 0x18, 0x2f, 0x1c, 0x2d, 0x21, 0x2a, 0x24, 0x29, 0x28, 0x27, - 0x2b, 0x26, 0x2e, 0x26, 0x30, 0x25, 0x33, 0x25, 0x35, 0x25, 0x37, 0x24, - 0x39, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x23, 0x40, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x1f, 0x00, 0x3b, 0x00, - 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, - 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, - 0x1f, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, - 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, - 0x46, 0x36, 0x49, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x2f, 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2c, 0x54, 0x2b, 0x56, 0x2a, - 0xb1, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x98, 0x00, 0x91, 0x01, 0x8c, 0x02, - 0x88, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, 0x7c, 0x07, 0x7a, 0x07, - 0x79, 0x09, 0x77, 0x09, 0x76, 0x0b, 0x74, 0x0b, 0x73, 0x0c, 0x73, 0x0c, - 0x71, 0x0d, 0x71, 0x0e, 0x09, 0x43, 0x0e, 0x3b, 0x12, 0x35, 0x16, 0x31, - 0x1a, 0x2e, 0x1e, 0x2c, 0x22, 0x2a, 0x25, 0x28, 0x28, 0x27, 0x2a, 0x27, - 0x2d, 0x26, 0x30, 0x25, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x37, 0x24, - 0x39, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x51, - 0x00, 0x39, 0x00, 0x1a, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, 0x00, - 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, - 0x82, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, - 0x09, 0x00, 0x00, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, - 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x37, 0x48, 0x35, - 0x4a, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0xb1, 0x00, 0xa9, 0x00, - 0xa1, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x87, 0x03, - 0x84, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7d, 0x06, 0x7b, 0x07, 0x79, 0x08, - 0x78, 0x09, 0x76, 0x0a, 0x76, 0x0b, 0x74, 0x0b, 0x73, 0x0c, 0x72, 0x0c, - 0x09, 0x44, 0x0d, 0x3c, 0x11, 0x36, 0x15, 0x33, 0x19, 0x30, 0x1c, 0x2d, - 0x20, 0x2b, 0x23, 0x2a, 0x25, 0x28, 0x28, 0x27, 0x2a, 0x27, 0x2d, 0x26, - 0x2f, 0x26, 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, 0x36, 0x24, 0x38, 0x24, - 0x39, 0x24, 0x3a, 0x24, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, - 0x00, 0x10, 0x09, 0x00, 0x1f, 0x00, 0x33, 0x00, 0x43, 0x00, 0x50, 0x00, - 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x95, 0x00, - 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, 0x00, - 0x00, 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, - 0x00, 0x37, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3c, 0x43, 0x39, 0x46, 0x38, 0x46, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, - 0x50, 0x2e, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9c, 0x00, - 0x96, 0x00, 0x91, 0x01, 0x8d, 0x02, 0x89, 0x02, 0x86, 0x03, 0x83, 0x04, - 0x81, 0x05, 0x7f, 0x06, 0x7d, 0x06, 0x7b, 0x07, 0x79, 0x07, 0x79, 0x09, - 0x77, 0x09, 0x76, 0x0a, 0x75, 0x0b, 0x74, 0x0b, 0x0a, 0x44, 0x0d, 0x3d, - 0x10, 0x38, 0x14, 0x34, 0x17, 0x31, 0x1b, 0x2e, 0x1e, 0x2d, 0x21, 0x2b, - 0x23, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2d, 0x26, 0x2e, 0x26, - 0x30, 0x25, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x36, 0x24, 0x38, 0x24, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, - 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, - 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, - 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x43, 0x39, - 0x46, 0x39, 0x46, 0x37, 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x32, - 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, - 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0xb2, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x98, 0x00, 0x93, 0x00, - 0x8f, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, - 0x7f, 0x06, 0x7d, 0x06, 0x7c, 0x07, 0x7a, 0x07, 0x79, 0x08, 0x78, 0x09, - 0x76, 0x09, 0x76, 0x0a, 0x0a, 0x44, 0x0c, 0x3e, 0x10, 0x39, 0x13, 0x35, - 0x16, 0x32, 0x19, 0x30, 0x1c, 0x2e, 0x1f, 0x2c, 0x21, 0x2b, 0x24, 0x2a, - 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x26, 0x2e, 0x26, 0x30, 0x25, - 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, - 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, 0x03, 0x0f, 0x00, - 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, - 0x60, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, - 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x07, - 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x39, 0x46, 0x39, 0x46, 0x38, - 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0x51, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xac, 0x00, - 0xa5, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x91, 0x01, 0x8e, 0x01, - 0x8a, 0x02, 0x88, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x05, - 0x7d, 0x06, 0x7c, 0x07, 0x7a, 0x07, 0x79, 0x07, 0x78, 0x09, 0x77, 0x09, - 0x0a, 0x44, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x36, 0x15, 0x33, 0x18, 0x30, - 0x1b, 0x2e, 0x1d, 0x2d, 0x20, 0x2b, 0x22, 0x2a, 0x24, 0x2a, 0x26, 0x28, - 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x26, 0x2e, 0x26, 0x2f, 0x26, 0x31, 0x25, - 0x33, 0x25, 0x33, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, - 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x1f, 0x00, - 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, - 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, - 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0c, - 0x00, 0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3d, 0x42, 0x3a, 0x45, 0x39, 0x46, 0x39, 0x46, 0x36, 0x49, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0xb3, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa1, 0x00, - 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8c, 0x02, 0x89, 0x02, - 0x87, 0x02, 0x85, 0x03, 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7d, 0x06, - 0x7c, 0x06, 0x7b, 0x07, 0x79, 0x07, 0x79, 0x08, 0x0a, 0x45, 0x0c, 0x3f, - 0x0f, 0x3a, 0x11, 0x37, 0x14, 0x34, 0x17, 0x32, 0x1a, 0x30, 0x1c, 0x2e, - 0x1e, 0x2d, 0x21, 0x2b, 0x22, 0x2a, 0x25, 0x2a, 0x26, 0x28, 0x28, 0x28, - 0x2a, 0x27, 0x2c, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x30, 0x25, 0x32, 0x25, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, - 0x00, 0x1b, 0x00, 0x0b, 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, - 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, - 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, 0x00, - 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3a, - 0x44, 0x39, 0x46, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0xb3, 0x00, 0xad, 0x00, 0xa7, 0x00, 0xa3, 0x00, 0x9d, 0x00, 0x99, 0x00, - 0x95, 0x00, 0x91, 0x01, 0x8e, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x86, 0x02, - 0x84, 0x03, 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, - 0x7b, 0x07, 0x7a, 0x07, 0x0a, 0x45, 0x0c, 0x40, 0x0e, 0x3b, 0x11, 0x38, - 0x13, 0x35, 0x16, 0x32, 0x18, 0x30, 0x1b, 0x2e, 0x1d, 0x2d, 0x1f, 0x2c, - 0x21, 0x2b, 0x23, 0x2a, 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, - 0x2c, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x2f, 0x25, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, - 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, - 0x00, 0x07, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, - 0x3d, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, - 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, - 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x39, - 0x46, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, - 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2f, 0x52, 0x2e, 0xb3, 0x00, 0xae, 0x00, - 0xa8, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x93, 0x00, - 0x8f, 0x01, 0x8d, 0x02, 0x8a, 0x02, 0x88, 0x02, 0x86, 0x03, 0x84, 0x03, - 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, 0x7c, 0x07, - 0x0a, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x38, 0x13, 0x36, 0x15, 0x33, - 0x18, 0x31, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, 0x2d, 0x20, 0x2b, 0x22, 0x2b, - 0x23, 0x2a, 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, - 0x2d, 0x26, 0x2f, 0x26, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, - 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, 0x00, 0x10, 0x00, 0x03, - 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, - 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, 0x00, - 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, - 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3d, 0x42, 0x3c, 0x43, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x36, - 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4c, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x2f, 0xb3, 0x00, 0xae, 0x00, 0xa9, 0x00, 0xa4, 0x00, - 0xa0, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x94, 0x00, 0x91, 0x01, 0x8e, 0x01, - 0x8c, 0x02, 0x89, 0x02, 0x87, 0x02, 0x86, 0x03, 0x84, 0x03, 0x82, 0x04, - 0x80, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, 0x0a, 0x45, 0x0b, 0x40, - 0x0e, 0x3c, 0x10, 0x39, 0x12, 0x37, 0x14, 0x34, 0x17, 0x32, 0x19, 0x30, - 0x1b, 0x2e, 0x1d, 0x2d, 0x1f, 0x2d, 0x20, 0x2b, 0x22, 0x2b, 0x24, 0x2a, - 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, 0x2c, 0x26, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, - 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0a, 0x00, - 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, - 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, - 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3c, - 0x43, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0xb3, 0x00, 0xae, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa1, 0x00, 0x9d, 0x00, - 0x99, 0x00, 0x96, 0x00, 0x93, 0x00, 0x90, 0x01, 0x8d, 0x01, 0x8b, 0x02, - 0x89, 0x02, 0x87, 0x02, 0x85, 0x03, 0x83, 0x03, 0x82, 0x04, 0x80, 0x05, - 0x7f, 0x05, 0x7e, 0x06, 0x0a, 0x45, 0x0b, 0x41, 0x0e, 0x3d, 0x10, 0x3a, - 0x11, 0x37, 0x14, 0x34, 0x16, 0x32, 0x18, 0x31, 0x1a, 0x30, 0x1c, 0x2e, - 0x1d, 0x2d, 0x20, 0x2c, 0x21, 0x2b, 0x22, 0x2a, 0x24, 0x2a, 0x25, 0x29, - 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, - 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, - 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, - 0x1f, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, - 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, - 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x39, 0x46, 0x39, - 0x46, 0x39, 0x46, 0x39, 0x46, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0xb4, 0x00, 0xaf, 0x00, - 0xaa, 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x9a, 0x00, 0x97, 0x00, - 0x94, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x02, 0x8a, 0x02, 0x88, 0x02, - 0x86, 0x02, 0x85, 0x03, 0x83, 0x03, 0x82, 0x04, 0x80, 0x05, 0x7f, 0x05, - 0x0a, 0x45, 0x0b, 0x41, 0x0d, 0x3e, 0x10, 0x3a, 0x11, 0x38, 0x13, 0x35, - 0x16, 0x33, 0x17, 0x32, 0x19, 0x30, 0x1b, 0x2e, 0x1d, 0x2e, 0x1e, 0x2d, - 0x20, 0x2b, 0x22, 0x2b, 0x23, 0x2a, 0x25, 0x2a, 0x25, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x2a, 0x27, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, - 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, 0x00, 0x26, 0x00, 0x1b, - 0x00, 0x11, 0x00, 0x06, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, - 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, - 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, - 0x28, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3a, 0x45, 0x39, 0x46, 0x39, 0x46, 0x39, - 0x46, 0x37, 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0xb4, 0x00, 0xaf, 0x00, 0xab, 0x00, 0xa7, 0x00, - 0xa3, 0x00, 0x9f, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x95, 0x00, 0x92, 0x00, - 0x90, 0x01, 0x8d, 0x01, 0x8b, 0x02, 0x89, 0x02, 0x88, 0x02, 0x86, 0x02, - 0x85, 0x03, 0x82, 0x03, 0x82, 0x04, 0x80, 0x05, 0x0a, 0x46, 0x0b, 0x42, - 0x0d, 0x3e, 0x0f, 0x3b, 0x11, 0x38, 0x13, 0x36, 0x15, 0x34, 0x16, 0x32, - 0x18, 0x30, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2d, 0x20, 0x2d, 0x20, 0x2b, - 0x22, 0x2b, 0x23, 0x2a, 0x25, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x32, 0x74, 0x38, 0x57, 0x3a, 0x4f, 0x3c, 0x4b, 0x3c, 0x49, 0x3d, 0x47, - 0x3d, 0x46, 0x3d, 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, - 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, - 0x3d, 0x42, 0x3d, 0x42, 0x41, 0x09, 0x1b, 0x31, 0x27, 0x38, 0x2d, 0x3a, - 0x31, 0x3b, 0x33, 0x3c, 0x35, 0x3c, 0x36, 0x3d, 0x37, 0x3d, 0x38, 0x3d, - 0x38, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, - 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x73, 0x00, 0x38, 0x31, - 0x3a, 0x38, 0x3c, 0x3a, 0x3c, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x3d, 0x28, 0x38, 0x30, - 0x38, 0x34, 0x39, 0x36, 0x39, 0x38, 0x3a, 0x39, 0x3a, 0x39, 0x3b, 0x3a, - 0x3b, 0x3b, 0x3c, 0x3b, 0x3c, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3e, 0x3c, - 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3d, 0x3e, 0x3d, 0x3e, 0x3d, - 0x00, 0x7e, 0x05, 0x58, 0x0f, 0x4e, 0x17, 0x49, 0x1d, 0x47, 0x21, 0x45, - 0x24, 0x44, 0x27, 0x43, 0x29, 0x43, 0x2b, 0x43, 0x2d, 0x43, 0x2e, 0x42, - 0x2f, 0x42, 0x31, 0x42, 0x31, 0x42, 0x32, 0x42, 0x33, 0x42, 0x33, 0x42, - 0x34, 0x42, 0x34, 0x42, 0x2b, 0x4e, 0x31, 0x47, 0x34, 0x45, 0x36, 0x44, - 0x38, 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, - 0x3c, 0x41, 0x3c, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, - 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x4f, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x2f, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x16, 0x3a, 0x20, 0x39, 0x25, 0x39, 0x29, - 0x39, 0x2d, 0x39, 0x2f, 0x3a, 0x31, 0x3a, 0x32, 0x3b, 0x33, 0x3a, 0x34, - 0x3a, 0x35, 0x3a, 0x36, 0x3a, 0x36, 0x3b, 0x37, 0x3b, 0x37, 0x3c, 0x38, - 0x3c, 0x38, 0x3d, 0x38, 0x3e, 0x39, 0x3e, 0x39, 0x00, 0x93, 0x01, 0x70, - 0x06, 0x60, 0x0d, 0x56, 0x12, 0x52, 0x16, 0x4e, 0x1a, 0x4c, 0x1d, 0x4a, - 0x20, 0x48, 0x22, 0x47, 0x24, 0x46, 0x26, 0x45, 0x27, 0x44, 0x29, 0x44, - 0x2a, 0x44, 0x2b, 0x44, 0x2c, 0x43, 0x2d, 0x43, 0x2e, 0x43, 0x2f, 0x43, - 0x28, 0x56, 0x2e, 0x4f, 0x31, 0x4b, 0x33, 0x49, 0x35, 0x48, 0x36, 0x46, - 0x37, 0x45, 0x38, 0x45, 0x39, 0x44, 0x39, 0x44, 0x39, 0x43, 0x39, 0x43, - 0x39, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, - 0x3d, 0x42, 0x3d, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x8f, 0x00, 0x73, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x45, 0x00, 0x22, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x43, 0x11, 0x3d, 0x18, 0x3b, 0x1e, 0x3a, 0x22, 0x39, 0x25, 0x39, 0x28, - 0x39, 0x2a, 0x39, 0x2c, 0x39, 0x2d, 0x3a, 0x2f, 0x3b, 0x30, 0x3b, 0x31, - 0x3b, 0x32, 0x3b, 0x33, 0x3a, 0x33, 0x3a, 0x34, 0x3a, 0x34, 0x3a, 0x35, - 0x3a, 0x35, 0x3b, 0x35, 0x00, 0x9d, 0x00, 0x7f, 0x03, 0x6d, 0x07, 0x62, - 0x0c, 0x5b, 0x10, 0x56, 0x13, 0x52, 0x16, 0x50, 0x19, 0x4d, 0x1c, 0x4c, - 0x1e, 0x4b, 0x1f, 0x4b, 0x22, 0x4a, 0x23, 0x49, 0x24, 0x48, 0x25, 0x47, - 0x27, 0x46, 0x28, 0x45, 0x29, 0x44, 0x29, 0x44, 0x27, 0x59, 0x2c, 0x53, - 0x2f, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x34, 0x4a, 0x35, 0x48, 0x35, 0x47, - 0x36, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x45, - 0x39, 0x44, 0x39, 0x44, 0x39, 0x43, 0x39, 0x43, 0x39, 0x42, 0x3a, 0x42, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x84, - 0x00, 0x5b, 0x00, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x59, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x44, 0x0f, 0x3e, 0x14, - 0x3c, 0x19, 0x3a, 0x1d, 0x3a, 0x20, 0x39, 0x23, 0x3a, 0x25, 0x3a, 0x27, - 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2c, 0x3a, 0x2c, 0x3b, 0x2d, 0x3b, 0x2f, - 0x3b, 0x2f, 0x3b, 0x30, 0x3b, 0x31, 0x3b, 0x32, 0x3a, 0x32, 0x3a, 0x33, - 0x00, 0xa3, 0x00, 0x88, 0x01, 0x77, 0x04, 0x6b, 0x08, 0x63, 0x0b, 0x5d, - 0x0e, 0x59, 0x11, 0x57, 0x14, 0x54, 0x16, 0x51, 0x19, 0x4f, 0x1a, 0x4d, - 0x1c, 0x4c, 0x1e, 0x4c, 0x1f, 0x4b, 0x21, 0x4b, 0x22, 0x4a, 0x23, 0x49, - 0x24, 0x49, 0x25, 0x49, 0x27, 0x5a, 0x2a, 0x55, 0x2d, 0x52, 0x2f, 0x4f, - 0x31, 0x4e, 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x49, 0x35, 0x48, - 0x35, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, - 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x45, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x8d, 0x00, 0x6f, 0x00, 0x48, - 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x55, 0x00, - 0x42, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x45, 0x0e, 0x40, 0x12, 0x3d, 0x16, 0x3b, 0x1a, - 0x3b, 0x1d, 0x3a, 0x20, 0x39, 0x22, 0x39, 0x23, 0x3a, 0x25, 0x3a, 0x26, - 0x3a, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2b, 0x3a, 0x2c, 0x3b, 0x2d, - 0x3c, 0x2e, 0x3b, 0x2e, 0x3b, 0x2f, 0x3b, 0x30, 0x00, 0xa7, 0x00, 0x90, - 0x00, 0x7e, 0x02, 0x73, 0x05, 0x6a, 0x08, 0x65, 0x0b, 0x5f, 0x0d, 0x5b, - 0x10, 0x58, 0x12, 0x56, 0x14, 0x55, 0x16, 0x52, 0x18, 0x50, 0x1a, 0x4e, - 0x1b, 0x4d, 0x1d, 0x4c, 0x1e, 0x4c, 0x1f, 0x4b, 0x20, 0x4b, 0x21, 0x4b, - 0x26, 0x5b, 0x2a, 0x57, 0x2c, 0x54, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4e, - 0x31, 0x4c, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, - 0x35, 0x47, 0x36, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, - 0x39, 0x46, 0x39, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, - 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x46, 0x0d, 0x41, 0x11, 0x3d, 0x14, 0x3d, 0x17, 0x3b, 0x1a, 0x3b, 0x1c, - 0x3b, 0x1e, 0x39, 0x21, 0x39, 0x22, 0x3a, 0x23, 0x3a, 0x25, 0x3a, 0x26, - 0x3a, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2a, 0x39, 0x2b, 0x3b, 0x2c, - 0x3c, 0x2d, 0x3c, 0x2e, 0x00, 0xa9, 0x00, 0x95, 0x00, 0x85, 0x01, 0x79, - 0x03, 0x70, 0x06, 0x69, 0x08, 0x65, 0x0b, 0x61, 0x0d, 0x5c, 0x0f, 0x59, - 0x11, 0x58, 0x13, 0x56, 0x14, 0x55, 0x16, 0x53, 0x18, 0x51, 0x19, 0x4f, - 0x1b, 0x4d, 0x1c, 0x4d, 0x1d, 0x4c, 0x1e, 0x4c, 0x26, 0x5c, 0x29, 0x58, - 0x2b, 0x55, 0x2d, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4d, - 0x32, 0x4b, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, - 0x35, 0x48, 0x35, 0x47, 0x36, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, - 0x00, 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0c, 0x42, 0x0f, - 0x3f, 0x13, 0x3d, 0x15, 0x3c, 0x18, 0x3a, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, - 0x39, 0x20, 0x39, 0x21, 0x39, 0x22, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, - 0x3a, 0x27, 0x39, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2a, 0x3a, 0x2b, - 0x00, 0xab, 0x00, 0x99, 0x00, 0x8b, 0x01, 0x7e, 0x02, 0x77, 0x04, 0x6f, - 0x06, 0x69, 0x08, 0x66, 0x0b, 0x62, 0x0d, 0x5e, 0x0e, 0x5b, 0x10, 0x59, - 0x12, 0x57, 0x13, 0x56, 0x15, 0x55, 0x16, 0x54, 0x18, 0x52, 0x19, 0x50, - 0x1a, 0x4e, 0x1b, 0x4d, 0x26, 0x5c, 0x28, 0x59, 0x2a, 0x57, 0x2c, 0x53, - 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, - 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x35, 0x49, 0x35, 0x48, 0x36, 0x46, 0x37, 0x46, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, - 0x00, 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, - 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0c, 0x42, 0x0e, 0x40, 0x11, 0x3d, 0x13, - 0x3d, 0x16, 0x3b, 0x18, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1e, 0x3a, 0x1f, - 0x39, 0x21, 0x39, 0x21, 0x3a, 0x23, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, - 0x3a, 0x27, 0x39, 0x27, 0x39, 0x28, 0x39, 0x2a, 0x00, 0xac, 0x00, 0x9c, - 0x00, 0x8f, 0x00, 0x83, 0x02, 0x7b, 0x03, 0x74, 0x05, 0x6d, 0x07, 0x69, - 0x09, 0x66, 0x0b, 0x63, 0x0c, 0x5f, 0x0e, 0x5c, 0x0f, 0x5a, 0x11, 0x58, - 0x12, 0x57, 0x14, 0x56, 0x15, 0x55, 0x16, 0x55, 0x18, 0x53, 0x18, 0x51, - 0x26, 0x5d, 0x27, 0x59, 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, 0x53, - 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, - 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x35, 0x4a, 0x35, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, - 0x00, 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, - 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x47, 0x0b, 0x42, 0x0d, 0x41, 0x10, 0x3e, 0x13, 0x3d, 0x15, 0x3c, 0x17, - 0x3a, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x20, - 0x39, 0x21, 0x3a, 0x22, 0x3b, 0x23, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, - 0x3a, 0x27, 0x39, 0x27, 0x00, 0xad, 0x00, 0x9f, 0x00, 0x92, 0x00, 0x88, - 0x01, 0x7e, 0x02, 0x78, 0x04, 0x72, 0x05, 0x6d, 0x07, 0x69, 0x09, 0x66, - 0x0a, 0x64, 0x0c, 0x61, 0x0d, 0x5d, 0x0f, 0x5a, 0x10, 0x59, 0x11, 0x58, - 0x13, 0x57, 0x14, 0x56, 0x15, 0x55, 0x16, 0x55, 0x26, 0x5d, 0x27, 0x5a, - 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x32, 0x4b, 0x33, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, - 0x00, 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, - 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, - 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x43, 0x0d, - 0x41, 0x0f, 0x3f, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x17, 0x3a, 0x19, - 0x3a, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x1f, 0x39, 0x21, - 0x39, 0x21, 0x3b, 0x22, 0x3b, 0x24, 0x3a, 0x24, 0x3a, 0x24, 0x3a, 0x26, - 0x00, 0xae, 0x00, 0xa2, 0x00, 0x94, 0x00, 0x8b, 0x00, 0x82, 0x02, 0x7b, - 0x03, 0x76, 0x05, 0x71, 0x06, 0x6c, 0x07, 0x69, 0x09, 0x66, 0x0a, 0x64, - 0x0c, 0x62, 0x0d, 0x5f, 0x0e, 0x5b, 0x10, 0x5a, 0x10, 0x58, 0x12, 0x58, - 0x13, 0x56, 0x14, 0x56, 0x25, 0x5d, 0x27, 0x5b, 0x29, 0x57, 0x2a, 0x57, - 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4d, 0x33, 0x4a, 0x34, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, - 0x00, 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, - 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, 0x00, - 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0f, 0x40, 0x11, - 0x3d, 0x13, 0x3e, 0x14, 0x3c, 0x16, 0x3b, 0x17, 0x3a, 0x19, 0x3b, 0x1a, - 0x3c, 0x1c, 0x3b, 0x1d, 0x3b, 0x1f, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, - 0x3a, 0x21, 0x3b, 0x22, 0x3b, 0x24, 0x3a, 0x24, 0x00, 0xae, 0x00, 0xa3, - 0x00, 0x97, 0x00, 0x8e, 0x00, 0x86, 0x01, 0x7e, 0x02, 0x79, 0x03, 0x75, - 0x05, 0x70, 0x06, 0x6b, 0x07, 0x68, 0x09, 0x66, 0x0a, 0x64, 0x0b, 0x63, - 0x0c, 0x5f, 0x0e, 0x5d, 0x0f, 0x5a, 0x10, 0x59, 0x11, 0x58, 0x13, 0x58, - 0x25, 0x5d, 0x27, 0x5c, 0x29, 0x58, 0x2a, 0x57, 0x2a, 0x56, 0x2d, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, - 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, - 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, 0x00, - 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x47, 0x0b, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x10, 0x3e, 0x12, 0x3d, 0x13, - 0x3e, 0x15, 0x3c, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, - 0x3b, 0x1c, 0x3b, 0x1e, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, 0x39, 0x21, - 0x3b, 0x22, 0x3b, 0x23, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, - 0x00, 0x8a, 0x01, 0x81, 0x02, 0x7c, 0x02, 0x77, 0x04, 0x74, 0x05, 0x6f, - 0x06, 0x6a, 0x07, 0x68, 0x09, 0x66, 0x0a, 0x64, 0x0b, 0x63, 0x0c, 0x61, - 0x0e, 0x5e, 0x0e, 0x5b, 0x10, 0x5a, 0x10, 0x59, 0x25, 0x5d, 0x27, 0x5c, - 0x28, 0x59, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, - 0x00, 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, - 0x00, 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, - 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x44, 0x0d, - 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3d, 0x13, 0x3e, 0x14, 0x3d, 0x15, - 0x3c, 0x17, 0x3a, 0x18, 0x3a, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, - 0x3b, 0x1e, 0x39, 0x1f, 0x39, 0x1f, 0x39, 0x21, 0x39, 0x21, 0x3a, 0x21, - 0x00, 0xaf, 0x00, 0xa6, 0x00, 0x9c, 0x00, 0x92, 0x00, 0x8c, 0x00, 0x85, - 0x01, 0x7e, 0x02, 0x7a, 0x03, 0x76, 0x05, 0x73, 0x06, 0x6e, 0x07, 0x6a, - 0x08, 0x68, 0x09, 0x66, 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x62, 0x0d, 0x5f, - 0x0e, 0x5c, 0x0f, 0x5a, 0x25, 0x5d, 0x27, 0x5c, 0x27, 0x59, 0x2a, 0x57, - 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x34, 0x4a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, - 0x00, 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, - 0x00, 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, - 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, 0x00, - 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0f, - 0x40, 0x11, 0x3d, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x16, 0x3b, 0x17, - 0x3a, 0x19, 0x3a, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1e, - 0x3a, 0x1f, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, 0x00, 0xb0, 0x00, 0xa7, - 0x00, 0x9e, 0x00, 0x94, 0x00, 0x8e, 0x00, 0x88, 0x01, 0x81, 0x02, 0x7c, - 0x02, 0x78, 0x03, 0x75, 0x05, 0x72, 0x06, 0x6d, 0x07, 0x6a, 0x08, 0x68, - 0x09, 0x66, 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x62, 0x0d, 0x60, 0x0e, 0x5d, - 0x25, 0x5d, 0x27, 0x5c, 0x27, 0x5a, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, - 0x2c, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4e, 0x31, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, - 0x00, 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, - 0x00, 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, - 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, 0x00, - 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x10, 0x3e, 0x11, - 0x3d, 0x13, 0x3e, 0x14, 0x3d, 0x15, 0x3c, 0x17, 0x3a, 0x17, 0x3a, 0x19, - 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, - 0x39, 0x1f, 0x39, 0x1f, 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xa0, 0x00, 0x96, - 0x00, 0x90, 0x00, 0x8a, 0x00, 0x84, 0x01, 0x7e, 0x02, 0x7a, 0x03, 0x77, - 0x04, 0x74, 0x05, 0x71, 0x06, 0x6c, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, - 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x63, 0x0c, 0x61, 0x25, 0x5e, 0x27, 0x5c, - 0x27, 0x5c, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x00, 0x27, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x8f, 0x00, 0x96, 0x00, 0x99, - 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, - 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x8c, 0x00, 0x63, - 0x00, 0x7f, 0x00, 0x89, 0x00, 0x90, 0x00, 0x96, 0x00, 0x9b, 0x00, 0x9c, - 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, - 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x2c, 0x00, 0x57, 0x00, 0x82, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, - 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, - 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x11, 0x31, 0x00, 0x5f, - 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, - 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, - 0x00, 0x9e, 0x00, 0x9e, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0b, - 0x42, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3d, 0x12, 0x3d, 0x13, - 0x3e, 0x15, 0x3c, 0x15, 0x3c, 0x17, 0x3a, 0x17, 0x3a, 0x19, 0x3b, 0x19, - 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x1f, - 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x98, 0x00, 0x91, 0x00, 0x8c, - 0x00, 0x87, 0x01, 0x80, 0x02, 0x7c, 0x02, 0x79, 0x03, 0x76, 0x05, 0x74, - 0x05, 0x70, 0x06, 0x6b, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, 0x0a, 0x65, - 0x0b, 0x64, 0x0c, 0x63, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x58, - 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x0d, - 0x00, 0x4f, 0x00, 0x73, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x95, - 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, - 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x63, 0x00, 0x4d, 0x00, 0x60, 0x00, 0x77, - 0x00, 0x83, 0x00, 0x8c, 0x00, 0x92, 0x00, 0x95, 0x00, 0x98, 0x00, 0x99, - 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x01, 0x04, 0x16, - 0x00, 0x57, 0x00, 0x78, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x94, 0x00, 0x96, - 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, - 0x00, 0x9d, 0x00, 0x9d, 0x23, 0x03, 0x00, 0x20, 0x00, 0x5f, 0x00, 0x7c, - 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, - 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x41, 0x0e, - 0x42, 0x0f, 0x40, 0x11, 0x3d, 0x11, 0x3d, 0x13, 0x3f, 0x13, 0x3d, 0x15, - 0x3c, 0x16, 0x3b, 0x17, 0x3a, 0x18, 0x3a, 0x19, 0x3b, 0x19, 0x3c, 0x1a, - 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3b, 0x1e, 0x00, 0xb1, 0x00, 0xa9, - 0x00, 0xa2, 0x00, 0x9a, 0x00, 0x93, 0x00, 0x8d, 0x00, 0x89, 0x01, 0x83, - 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x78, 0x03, 0x75, 0x05, 0x73, 0x06, 0x6f, - 0x06, 0x6b, 0x07, 0x69, 0x08, 0x68, 0x09, 0x66, 0x0a, 0x66, 0x0b, 0x64, - 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x59, 0x2a, 0x57, 0x2a, 0x57, - 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x38, - 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, - 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, - 0x00, 0x7f, 0x00, 0x60, 0x00, 0x24, 0x00, 0x48, 0x00, 0x60, 0x00, 0x70, - 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x93, - 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x27, 0x00, 0x01, 0x12, 0x00, 0x43, - 0x00, 0x61, 0x00, 0x74, 0x00, 0x7f, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, - 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, - 0x46, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, - 0x00, 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, - 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x41, 0x0e, 0x42, 0x0f, 0x40, 0x10, - 0x3e, 0x11, 0x3d, 0x13, 0x3d, 0x13, 0x3f, 0x15, 0x3c, 0x15, 0x3c, 0x17, - 0x3a, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, - 0x3b, 0x1c, 0x3b, 0x1c, 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa3, 0x00, 0x9c, - 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8b, 0x00, 0x86, 0x01, 0x80, 0x02, 0x7c, - 0x02, 0x79, 0x03, 0x77, 0x04, 0x75, 0x05, 0x72, 0x06, 0x6e, 0x07, 0x6b, - 0x07, 0x69, 0x09, 0x68, 0x09, 0x66, 0x0a, 0x66, 0x24, 0x5e, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5a, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, - 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x48, - 0x00, 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, - 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x89, 0x00, 0x77, - 0x00, 0x48, 0x00, 0x0f, 0x00, 0x30, 0x00, 0x48, 0x00, 0x5d, 0x00, 0x6c, - 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, - 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x41, 0x00, 0x17, 0x00, 0x00, 0x10, 0x00, 0x36, 0x00, 0x51, - 0x00, 0x64, 0x00, 0x71, 0x00, 0x7a, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, - 0x00, 0x8d, 0x00, 0x90, 0x00, 0x92, 0x00, 0x93, 0x52, 0x00, 0x3c, 0x00, - 0x0d, 0x00, 0x00, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, - 0x00, 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, - 0x00, 0x93, 0x00, 0x95, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, - 0x44, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x40, 0x11, 0x3d, 0x11, - 0x3d, 0x13, 0x3f, 0x13, 0x3e, 0x15, 0x3c, 0x15, 0x3c, 0x17, 0x3a, 0x17, - 0x3a, 0x18, 0x3b, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, - 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x95, 0x00, 0x90, - 0x00, 0x8c, 0x00, 0x88, 0x01, 0x82, 0x02, 0x7e, 0x02, 0x7b, 0x02, 0x78, - 0x03, 0x76, 0x05, 0x74, 0x05, 0x71, 0x06, 0x6d, 0x07, 0x6b, 0x07, 0x69, - 0x09, 0x68, 0x09, 0x66, 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5b, - 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x50, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, - 0x00, 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, - 0x00, 0x86, 0x00, 0x89, 0x00, 0x90, 0x00, 0x83, 0x00, 0x60, 0x00, 0x30, - 0x00, 0x02, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x5e, 0x00, 0x69, - 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x86, 0x00, 0x89, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x4d, 0x00, - 0x2f, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x2e, 0x00, 0x46, 0x00, 0x57, - 0x00, 0x64, 0x00, 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, - 0x00, 0x89, 0x00, 0x8b, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x00, - 0x00, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, - 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, - 0x1b, 0x85, 0x05, 0x96, 0x01, 0x9e, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xa9, - 0x00, 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, - 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, - 0x00, 0xb2, 0x00, 0xb2, 0x1c, 0x6e, 0x03, 0x8a, 0x00, 0x97, 0x00, 0x9e, - 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xac, - 0x00, 0xad, 0x00, 0xad, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, - 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x0e, 0x92, 0x01, 0xa1, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xae, - 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb2, - 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, - 0x00, 0xb4, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, - 0x00, 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, - 0x00, 0x96, 0x00, 0x8c, 0x00, 0x70, 0x00, 0x48, 0x00, 0x22, 0x00, 0x01, - 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, 0x5e, 0x00, 0x68, - 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x23, 0x00, - 0x0a, 0x02, 0x00, 0x10, 0x00, 0x29, 0x00, 0x3d, 0x00, 0x4e, 0x00, 0x5b, - 0x00, 0x65, 0x00, 0x6d, 0x00, 0x74, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, - 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x00, 0x00, 0x04, 0x00, 0x1f, - 0x00, 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, - 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, 0x27, 0x60, 0x0f, 0x73, - 0x07, 0x81, 0x03, 0x8a, 0x01, 0x91, 0x00, 0x95, 0x00, 0x99, 0x00, 0x9d, - 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, - 0x00, 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, - 0x29, 0x3a, 0x0f, 0x59, 0x06, 0x6c, 0x02, 0x79, 0x00, 0x84, 0x00, 0x8b, - 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9f, - 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa6, - 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x14, 0x79, 0x07, 0x88, - 0x03, 0x92, 0x01, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa5, - 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00, 0xac, 0x00, 0xac, - 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, - 0x00, 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x9b, 0x00, 0x92, - 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x18, - 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x66, - 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5c, 0x00, 0x56, 0x00, 0x47, 0x00, 0x31, 0x00, 0x1a, 0x00, 0x08, 0x04, - 0x00, 0x10, 0x00, 0x25, 0x00, 0x37, 0x00, 0x46, 0x00, 0x52, 0x00, 0x5d, - 0x00, 0x65, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x77, 0x5c, 0x00, 0x55, 0x00, - 0x43, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x33, - 0x00, 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, - 0x00, 0x77, 0x00, 0x7b, 0x2d, 0x54, 0x17, 0x63, 0x0d, 0x6f, 0x07, 0x78, - 0x04, 0x80, 0x02, 0x86, 0x01, 0x8b, 0x01, 0x8f, 0x00, 0x92, 0x00, 0x95, - 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, - 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa4, 0x31, 0x27, 0x18, 0x40, - 0x0d, 0x52, 0x07, 0x61, 0x04, 0x6c, 0x02, 0x75, 0x01, 0x7c, 0x00, 0x82, - 0x00, 0x87, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, - 0x00, 0x98, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x18, 0x6f, 0x0c, 0x7b, 0x06, 0x84, 0x03, 0x8c, - 0x02, 0x91, 0x01, 0x96, 0x00, 0x99, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, - 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xa8, - 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xab, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, - 0x00, 0x5f, 0x00, 0x66, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x84, 0x00, 0x6c, - 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, 0x14, 0x00, 0x26, - 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, 0x5f, 0x00, 0x66, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, - 0x4d, 0x00, 0x3b, 0x00, 0x27, 0x00, 0x13, 0x00, 0x07, 0x06, 0x00, 0x10, - 0x00, 0x22, 0x00, 0x32, 0x00, 0x40, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5e, - 0x00, 0x65, 0x00, 0x6b, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, - 0x1f, 0x00, 0x08, 0x00, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, - 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, - 0x30, 0x4e, 0x1d, 0x5a, 0x12, 0x64, 0x0c, 0x6d, 0x08, 0x75, 0x05, 0x7a, - 0x03, 0x7f, 0x02, 0x85, 0x02, 0x89, 0x01, 0x8b, 0x00, 0x8e, 0x00, 0x90, - 0x00, 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, - 0x00, 0x9e, 0x00, 0x9f, 0x36, 0x1e, 0x1f, 0x32, 0x12, 0x43, 0x0b, 0x50, - 0x07, 0x5a, 0x05, 0x63, 0x02, 0x6b, 0x01, 0x72, 0x01, 0x78, 0x00, 0x7c, - 0x00, 0x81, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, - 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x1b, 0x6b, 0x0f, 0x74, 0x09, 0x7d, 0x05, 0x83, 0x03, 0x89, 0x02, 0x8d, - 0x01, 0x91, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, - 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, - 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, - 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, - 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5e, 0x00, 0x45, - 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, - 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x42, 0x00, - 0x32, 0x00, 0x20, 0x00, 0x0e, 0x00, 0x06, 0x07, 0x00, 0x10, 0x00, 0x20, - 0x00, 0x2f, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x58, 0x00, 0x5f, - 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, - 0x03, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, - 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, 0x33, 0x4b, 0x21, 0x54, - 0x16, 0x5d, 0x10, 0x65, 0x0b, 0x6c, 0x08, 0x72, 0x06, 0x77, 0x04, 0x7b, - 0x03, 0x7f, 0x02, 0x83, 0x02, 0x87, 0x01, 0x8a, 0x01, 0x8c, 0x00, 0x8e, - 0x00, 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x98, - 0x3a, 0x19, 0x24, 0x2a, 0x18, 0x38, 0x10, 0x44, 0x0b, 0x4e, 0x07, 0x57, - 0x05, 0x5f, 0x04, 0x65, 0x02, 0x6b, 0x01, 0x71, 0x01, 0x76, 0x00, 0x79, - 0x00, 0x7e, 0x00, 0x80, 0x00, 0x84, 0x00, 0x87, 0x00, 0x88, 0x00, 0x8b, - 0x00, 0x8d, 0x00, 0x8f, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1d, 0x68, 0x12, 0x70, - 0x0c, 0x77, 0x08, 0x7d, 0x05, 0x82, 0x03, 0x87, 0x02, 0x8b, 0x02, 0x8e, - 0x01, 0x91, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9b, - 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x9d, 0x00, 0x99, - 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, 0x3c, 0x00, 0x26, - 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x37, - 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2a, 0x00, - 0x1a, 0x00, 0x0b, 0x00, 0x05, 0x08, 0x00, 0x10, 0x00, 0x1e, 0x00, 0x2c, - 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x5e, 0x00, 0x5b, 0x00, - 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, - 0x00, 0x54, 0x00, 0x5b, 0x34, 0x49, 0x25, 0x51, 0x1a, 0x58, 0x13, 0x60, - 0x0e, 0x65, 0x0b, 0x6b, 0x08, 0x71, 0x06, 0x75, 0x05, 0x78, 0x04, 0x7b, - 0x03, 0x7f, 0x02, 0x83, 0x02, 0x86, 0x01, 0x88, 0x01, 0x8a, 0x00, 0x8c, - 0x00, 0x8d, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x92, 0x3b, 0x17, 0x28, 0x24, - 0x1c, 0x30, 0x14, 0x3b, 0x0f, 0x45, 0x0b, 0x4d, 0x07, 0x55, 0x05, 0x5b, - 0x04, 0x61, 0x03, 0x67, 0x02, 0x6b, 0x01, 0x6f, 0x01, 0x74, 0x00, 0x77, - 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x83, 0x00, 0x86, 0x00, 0x87, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x1d, 0x67, 0x14, 0x6d, 0x0e, 0x73, 0x0a, 0x79, - 0x07, 0x7e, 0x05, 0x82, 0x03, 0x86, 0x02, 0x89, 0x02, 0x8c, 0x01, 0x8f, - 0x01, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, - 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, - 0x00, 0x33, 0x00, 0x3d, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x83, - 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x22, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x33, 0x00, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, - 0x56, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x32, 0x00, 0x24, 0x00, 0x15, 0x00, - 0x0a, 0x02, 0x04, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x34, - 0x00, 0x3e, 0x00, 0x46, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, - 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x12, - 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, - 0x36, 0x47, 0x27, 0x4e, 0x1d, 0x54, 0x16, 0x5b, 0x11, 0x61, 0x0e, 0x65, - 0x0b, 0x6a, 0x08, 0x6f, 0x07, 0x73, 0x05, 0x76, 0x05, 0x79, 0x03, 0x7c, - 0x02, 0x7e, 0x02, 0x82, 0x02, 0x85, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8b, - 0x00, 0x8c, 0x00, 0x8d, 0x3d, 0x14, 0x2c, 0x20, 0x20, 0x2a, 0x18, 0x34, - 0x12, 0x3e, 0x0e, 0x45, 0x0a, 0x4c, 0x07, 0x53, 0x06, 0x58, 0x05, 0x5e, - 0x04, 0x63, 0x02, 0x67, 0x02, 0x6c, 0x01, 0x6f, 0x01, 0x72, 0x00, 0x76, - 0x00, 0x78, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x1e, 0x66, 0x16, 0x6b, 0x10, 0x71, 0x0c, 0x76, 0x09, 0x7a, 0x07, 0x7e, - 0x05, 0x81, 0x03, 0x85, 0x03, 0x88, 0x02, 0x8b, 0x02, 0x8d, 0x01, 0x8f, - 0x01, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x98, 0x00, 0x99, - 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, - 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, 0x79, 0x00, 0x68, - 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, - 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, - 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x09, 0x03, - 0x04, 0x09, 0x00, 0x10, 0x00, 0x1c, 0x00, 0x27, 0x00, 0x31, 0x00, 0x3a, - 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, - 0x24, 0x00, 0x15, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, - 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, 0x37, 0x46, 0x29, 0x4c, - 0x20, 0x52, 0x19, 0x57, 0x14, 0x5d, 0x10, 0x62, 0x0d, 0x66, 0x0b, 0x69, - 0x09, 0x6e, 0x07, 0x73, 0x06, 0x75, 0x05, 0x77, 0x04, 0x7a, 0x03, 0x7c, - 0x02, 0x7e, 0x02, 0x82, 0x02, 0x84, 0x02, 0x87, 0x01, 0x88, 0x01, 0x89, - 0x3f, 0x13, 0x2f, 0x1c, 0x23, 0x27, 0x1b, 0x2f, 0x15, 0x38, 0x10, 0x3e, - 0x0d, 0x45, 0x0a, 0x4b, 0x08, 0x51, 0x07, 0x57, 0x05, 0x5c, 0x04, 0x60, - 0x03, 0x65, 0x02, 0x67, 0x02, 0x6c, 0x01, 0x6f, 0x01, 0x71, 0x00, 0x75, - 0x00, 0x77, 0x00, 0x79, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1f, 0x65, 0x17, 0x6a, - 0x11, 0x6f, 0x0d, 0x73, 0x0a, 0x77, 0x08, 0x7b, 0x06, 0x7e, 0x05, 0x81, - 0x04, 0x84, 0x03, 0x87, 0x02, 0x89, 0x02, 0x8b, 0x01, 0x8e, 0x01, 0x8f, - 0x01, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x9e, 0x00, 0x9c, - 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x4e, - 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, - 0x32, 0x00, 0x26, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x08, 0x04, 0x04, 0x0a, - 0x00, 0x10, 0x00, 0x1b, 0x00, 0x25, 0x00, 0x2f, 0x5e, 0x00, 0x5d, 0x00, - 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, 0x00, - 0x10, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, - 0x00, 0x33, 0x00, 0x3b, 0x38, 0x45, 0x2b, 0x4a, 0x22, 0x50, 0x1c, 0x55, - 0x16, 0x59, 0x12, 0x5f, 0x0f, 0x63, 0x0d, 0x66, 0x0b, 0x69, 0x09, 0x6e, - 0x07, 0x71, 0x06, 0x74, 0x05, 0x76, 0x05, 0x78, 0x03, 0x7a, 0x03, 0x7c, - 0x02, 0x7e, 0x02, 0x81, 0x02, 0x84, 0x02, 0x86, 0x40, 0x11, 0x31, 0x1b, - 0x26, 0x23, 0x1e, 0x2b, 0x18, 0x33, 0x13, 0x39, 0x0f, 0x40, 0x0c, 0x45, - 0x0a, 0x4b, 0x08, 0x51, 0x07, 0x56, 0x05, 0x59, 0x04, 0x5e, 0x04, 0x61, - 0x02, 0x65, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6e, 0x01, 0x71, 0x00, 0x73, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x64, 0x18, 0x69, 0x13, 0x6d, 0x0f, 0x71, - 0x0c, 0x75, 0x09, 0x78, 0x07, 0x7b, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, - 0x03, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, 0x01, 0x8e, 0x01, 0x8f, - 0x01, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x16, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8e, - 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, 0x47, 0x00, 0x37, - 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, - 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x38, 0x00, 0x2d, 0x00, - 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x07, 0x05, 0x03, 0x0a, 0x00, 0x0f, - 0x00, 0x1a, 0x00, 0x24, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, - 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, - 0x38, 0x45, 0x2d, 0x49, 0x24, 0x4e, 0x1e, 0x53, 0x19, 0x56, 0x14, 0x5b, - 0x11, 0x60, 0x0e, 0x63, 0x0c, 0x66, 0x0a, 0x69, 0x09, 0x6c, 0x07, 0x70, - 0x06, 0x73, 0x06, 0x75, 0x05, 0x77, 0x04, 0x79, 0x03, 0x7a, 0x03, 0x7c, - 0x02, 0x7e, 0x02, 0x80, 0x40, 0x11, 0x32, 0x18, 0x28, 0x20, 0x20, 0x28, - 0x1a, 0x2f, 0x16, 0x35, 0x12, 0x3c, 0x0f, 0x41, 0x0c, 0x46, 0x0a, 0x4b, - 0x08, 0x50, 0x07, 0x54, 0x05, 0x58, 0x05, 0x5c, 0x04, 0x5f, 0x04, 0x62, - 0x02, 0x66, 0x02, 0x69, 0x01, 0x6b, 0x01, 0x6e, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x20, 0x64, 0x19, 0x68, 0x14, 0x6b, 0x10, 0x6f, 0x0d, 0x73, 0x0b, 0x76, - 0x09, 0x79, 0x07, 0x7c, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, - 0x02, 0x88, 0x02, 0x89, 0x02, 0x8b, 0x02, 0x8d, 0x01, 0x8e, 0x01, 0x90, - 0x00, 0x91, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, - 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, 0x86, 0x00, 0x7a, - 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, 0x33, 0x00, 0x25, - 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, - 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x28, 0x00, 0x1e, 0x00, - 0x14, 0x00, 0x0b, 0x00, 0x07, 0x06, 0x03, 0x0b, 0x00, 0x0f, 0x00, 0x19, - 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, - 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x01, - 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, 0x39, 0x44, 0x2e, 0x49, - 0x26, 0x4c, 0x1f, 0x52, 0x1a, 0x55, 0x16, 0x58, 0x13, 0x5d, 0x10, 0x61, - 0x0e, 0x64, 0x0c, 0x66, 0x0a, 0x68, 0x09, 0x6c, 0x07, 0x70, 0x07, 0x72, - 0x06, 0x74, 0x05, 0x76, 0x05, 0x78, 0x03, 0x79, 0x03, 0x7b, 0x02, 0x7c, - 0x41, 0x10, 0x34, 0x17, 0x2a, 0x1e, 0x23, 0x25, 0x1d, 0x2c, 0x18, 0x32, - 0x14, 0x37, 0x10, 0x3d, 0x0e, 0x42, 0x0c, 0x46, 0x0a, 0x4b, 0x08, 0x4f, - 0x07, 0x53, 0x05, 0x57, 0x05, 0x5a, 0x04, 0x5e, 0x04, 0x61, 0x03, 0x63, - 0x02, 0x66, 0x02, 0x69, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x63, 0x1a, 0x67, - 0x15, 0x6a, 0x11, 0x6e, 0x0e, 0x71, 0x0c, 0x74, 0x0a, 0x77, 0x08, 0x7a, - 0x07, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, 0x02, 0x87, - 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x90, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, - 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x66, - 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, 0x22, 0x00, 0x16, - 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x48, 0x00, - 0x40, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, - 0x0a, 0x01, 0x06, 0x06, 0x03, 0x0b, 0x00, 0x0f, 0x5f, 0x00, 0x5d, 0x00, - 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, 0x00, - 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0d, - 0x00, 0x17, 0x00, 0x1f, 0x39, 0x43, 0x2f, 0x48, 0x27, 0x4b, 0x22, 0x4f, - 0x1c, 0x54, 0x18, 0x56, 0x14, 0x59, 0x12, 0x5e, 0x0f, 0x62, 0x0d, 0x64, - 0x0c, 0x66, 0x0a, 0x68, 0x09, 0x6b, 0x08, 0x6f, 0x07, 0x72, 0x06, 0x74, - 0x05, 0x75, 0x05, 0x77, 0x04, 0x78, 0x03, 0x7a, 0x42, 0x0f, 0x36, 0x16, - 0x2c, 0x1c, 0x25, 0x23, 0x1f, 0x29, 0x1a, 0x2e, 0x16, 0x34, 0x12, 0x39, - 0x0f, 0x3e, 0x0d, 0x42, 0x0c, 0x47, 0x0a, 0x4b, 0x08, 0x4f, 0x07, 0x52, - 0x05, 0x56, 0x05, 0x59, 0x04, 0x5c, 0x04, 0x5f, 0x04, 0x62, 0x02, 0x64, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1b, 0x66, 0x16, 0x6a, 0x12, 0x6d, - 0x0f, 0x70, 0x0d, 0x73, 0x0b, 0x75, 0x09, 0x78, 0x07, 0x7a, 0x06, 0x7d, - 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, 0x02, 0x86, 0x02, 0x88, - 0x02, 0x89, 0x02, 0x8b, 0x02, 0x8c, 0x01, 0x8d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3a, 0x43, 0x31, 0x48, 0x29, 0x4a, 0x23, 0x4d, 0x1e, 0x53, 0x1a, 0x55, - 0x16, 0x57, 0x13, 0x5b, 0x11, 0x60, 0x0f, 0x62, 0x0d, 0x64, 0x0b, 0x66, - 0x0a, 0x68, 0x09, 0x6a, 0x08, 0x6e, 0x07, 0x71, 0x06, 0x73, 0x06, 0x75, - 0x05, 0x76, 0x05, 0x78, 0x43, 0x0f, 0x37, 0x15, 0x2e, 0x1b, 0x27, 0x21, - 0x21, 0x26, 0x1c, 0x2c, 0x18, 0x31, 0x15, 0x36, 0x12, 0x3a, 0x0f, 0x3f, - 0x0c, 0x43, 0x0c, 0x47, 0x0a, 0x4b, 0x08, 0x4e, 0x07, 0x52, 0x06, 0x55, - 0x05, 0x58, 0x05, 0x5b, 0x04, 0x5d, 0x04, 0x60, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x21, 0x63, 0x1b, 0x66, 0x17, 0x69, 0x13, 0x6c, 0x10, 0x6e, 0x0e, 0x71, - 0x0c, 0x74, 0x0a, 0x76, 0x09, 0x79, 0x07, 0x7b, 0x06, 0x7d, 0x06, 0x7f, - 0x05, 0x81, 0x04, 0x82, 0x03, 0x84, 0x03, 0x86, 0x02, 0x87, 0x02, 0x89, - 0x02, 0x8a, 0x02, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x42, 0x31, 0x47, - 0x2a, 0x49, 0x24, 0x4c, 0x1f, 0x51, 0x1b, 0x54, 0x18, 0x56, 0x15, 0x58, - 0x12, 0x5c, 0x10, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0b, 0x66, 0x0a, 0x68, - 0x09, 0x6a, 0x08, 0x6d, 0x07, 0x71, 0x06, 0x72, 0x06, 0x74, 0x05, 0x75, - 0x43, 0x0f, 0x38, 0x14, 0x2f, 0x19, 0x28, 0x1f, 0x22, 0x24, 0x1d, 0x29, - 0x19, 0x2e, 0x16, 0x33, 0x12, 0x37, 0x11, 0x3b, 0x0f, 0x40, 0x0c, 0x43, - 0x0b, 0x47, 0x0a, 0x4b, 0x09, 0x4e, 0x07, 0x51, 0x07, 0x55, 0x05, 0x56, - 0x05, 0x5a, 0x04, 0x5c, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1c, 0x65, - 0x17, 0x68, 0x14, 0x6b, 0x11, 0x6e, 0x0e, 0x70, 0x0c, 0x73, 0x0b, 0x75, - 0x09, 0x77, 0x08, 0x79, 0x07, 0x7b, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, - 0x04, 0x82, 0x03, 0x84, 0x03, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, 0x89, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0x42, 0x32, 0x46, 0x2b, 0x49, 0x25, 0x4b, - 0x21, 0x4f, 0x1d, 0x53, 0x19, 0x55, 0x16, 0x57, 0x14, 0x59, 0x11, 0x5e, - 0x10, 0x61, 0x0e, 0x63, 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x6a, - 0x08, 0x6c, 0x07, 0x70, 0x07, 0x72, 0x06, 0x73, 0x43, 0x0e, 0x39, 0x13, - 0x31, 0x18, 0x2a, 0x1d, 0x24, 0x22, 0x1f, 0x27, 0x1b, 0x2b, 0x18, 0x30, - 0x16, 0x35, 0x12, 0x39, 0x0f, 0x3c, 0x0f, 0x41, 0x0c, 0x44, 0x0b, 0x47, - 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x50, 0x07, 0x54, 0x05, 0x55, 0x05, 0x59, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1c, 0x65, 0x18, 0x68, 0x15, 0x6a, - 0x12, 0x6c, 0x0f, 0x6f, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x76, 0x09, 0x78, - 0x07, 0x79, 0x07, 0x7c, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, - 0x03, 0x84, 0x03, 0x85, 0x02, 0x86, 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x42, 0x33, 0x45, 0x2c, 0x49, 0x27, 0x4b, 0x22, 0x4d, 0x1e, 0x52, - 0x1b, 0x54, 0x18, 0x56, 0x15, 0x58, 0x13, 0x5b, 0x10, 0x5f, 0x0f, 0x61, - 0x0e, 0x63, 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x08, 0x6c, - 0x07, 0x6f, 0x07, 0x72, 0x44, 0x0e, 0x3a, 0x12, 0x32, 0x17, 0x2b, 0x1c, - 0x26, 0x21, 0x21, 0x26, 0x1d, 0x2a, 0x19, 0x2e, 0x16, 0x32, 0x14, 0x35, - 0x12, 0x3a, 0x0f, 0x3d, 0x0e, 0x41, 0x0c, 0x44, 0x0b, 0x47, 0x0a, 0x4a, - 0x09, 0x4e, 0x07, 0x4f, 0x07, 0x53, 0x05, 0x55, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x22, 0x62, 0x1d, 0x65, 0x19, 0x67, 0x15, 0x6a, 0x13, 0x6c, 0x10, 0x6e, - 0x0e, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x0a, 0x76, 0x09, 0x79, 0x07, 0x7a, - 0x07, 0x7c, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, 0x03, 0x83, - 0x03, 0x85, 0x02, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x33, 0x44, - 0x2d, 0x48, 0x28, 0x4a, 0x23, 0x4c, 0x1f, 0x50, 0x1c, 0x53, 0x19, 0x55, - 0x16, 0x57, 0x14, 0x58, 0x12, 0x5c, 0x10, 0x60, 0x0e, 0x62, 0x0d, 0x63, - 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x09, 0x6b, 0x07, 0x6e, - 0x44, 0x0d, 0x3a, 0x12, 0x33, 0x16, 0x2c, 0x1b, 0x27, 0x1f, 0x21, 0x24, - 0x1e, 0x28, 0x1a, 0x2b, 0x18, 0x30, 0x16, 0x34, 0x12, 0x37, 0x11, 0x3b, - 0x0f, 0x3e, 0x0d, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, - 0x07, 0x4f, 0x07, 0x53, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x65, - 0x19, 0x66, 0x16, 0x69, 0x13, 0x6b, 0x10, 0x6d, 0x0f, 0x6f, 0x0d, 0x71, - 0x0c, 0x73, 0x0b, 0x76, 0x09, 0x77, 0x08, 0x79, 0x07, 0x7a, 0x06, 0x7c, - 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, 0x03, 0x83, 0x03, 0x85, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x34, 0x44, 0x2e, 0x48, 0x29, 0x49, - 0x24, 0x4b, 0x20, 0x4f, 0x1d, 0x53, 0x1a, 0x55, 0x18, 0x56, 0x15, 0x58, - 0x13, 0x59, 0x11, 0x5d, 0x10, 0x61, 0x0e, 0x62, 0x0d, 0x63, 0x0c, 0x65, - 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x09, 0x6b, 0x44, 0x0d, 0x3b, 0x12, - 0x34, 0x16, 0x2e, 0x19, 0x28, 0x1e, 0x23, 0x22, 0x20, 0x26, 0x1d, 0x2b, - 0x19, 0x2e, 0x16, 0x31, 0x14, 0x35, 0x12, 0x38, 0x0f, 0x3b, 0x0f, 0x3f, - 0x0c, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x4e, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x64, 0x1a, 0x66, 0x17, 0x68, - 0x14, 0x6a, 0x11, 0x6c, 0x10, 0x6e, 0x0e, 0x71, 0x0c, 0x73, 0x0b, 0x74, - 0x0a, 0x76, 0x09, 0x78, 0x07, 0x79, 0x07, 0x7b, 0x06, 0x7c, 0x06, 0x7e, - 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, 0x03, 0x82, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x42, 0x34, 0x43, 0x2f, 0x48, 0x29, 0x49, 0x25, 0x4b, 0x21, 0x4d, - 0x1e, 0x51, 0x1b, 0x54, 0x18, 0x55, 0x16, 0x56, 0x14, 0x58, 0x13, 0x5b, - 0x10, 0x5e, 0x0f, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0c, 0x66, 0x0b, 0x66, - 0x0a, 0x68, 0x09, 0x69, 0x44, 0x0c, 0x3c, 0x11, 0x35, 0x16, 0x2f, 0x19, - 0x2a, 0x1d, 0x25, 0x22, 0x21, 0x25, 0x1d, 0x29, 0x1a, 0x2b, 0x18, 0x30, - 0x16, 0x33, 0x12, 0x35, 0x12, 0x3a, 0x0f, 0x3c, 0x0f, 0x40, 0x0c, 0x41, - 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x22, 0x62, 0x1e, 0x64, 0x1a, 0x66, 0x17, 0x68, 0x15, 0x6a, 0x12, 0x6c, - 0x10, 0x6e, 0x0e, 0x70, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x75, 0x09, 0x76, - 0x09, 0x78, 0x07, 0x79, 0x07, 0x7b, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, - 0x05, 0x80, 0x04, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x35, 0x43, - 0x2f, 0x47, 0x2b, 0x49, 0x26, 0x4b, 0x22, 0x4c, 0x1f, 0x4f, 0x1c, 0x53, - 0x1a, 0x55, 0x18, 0x56, 0x15, 0x58, 0x13, 0x59, 0x12, 0x5c, 0x10, 0x60, - 0x0f, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0c, 0x66, 0x0b, 0x66, 0x0a, 0x68, - 0x44, 0x0c, 0x3c, 0x10, 0x35, 0x15, 0x30, 0x19, 0x2b, 0x1c, 0x26, 0x20, - 0x21, 0x23, 0x1e, 0x26, 0x1c, 0x2b, 0x19, 0x2e, 0x16, 0x30, 0x15, 0x35, - 0x12, 0x37, 0x11, 0x3b, 0x0f, 0x3d, 0x0f, 0x41, 0x0c, 0x42, 0x0c, 0x46, - 0x0a, 0x47, 0x0a, 0x4a, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1e, 0x64, - 0x1a, 0x66, 0x18, 0x68, 0x15, 0x6a, 0x13, 0x6b, 0x10, 0x6d, 0x0f, 0x6e, - 0x0e, 0x71, 0x0c, 0x72, 0x0b, 0x74, 0x0a, 0x76, 0x09, 0x77, 0x08, 0x79, - 0x07, 0x7a, 0x07, 0x7c, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x44, 0x09, 0x43, 0x09, 0x44, 0x09, 0x45, 0x09, - 0x45, 0x09, 0x46, 0x09, 0x46, 0x09, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0x22, 0x1c, 0x0a, 0x29, 0x0a, 0x31, 0x09, 0x36, 0x09, 0x3a, 0x09, - 0x3b, 0x09, 0x3d, 0x09, 0x3f, 0x09, 0x40, 0x09, 0x40, 0x09, 0x41, 0x09, - 0x42, 0x09, 0x43, 0x09, 0x43, 0x0a, 0x43, 0x0a, 0x44, 0x0a, 0x44, 0x0a, - 0x44, 0x0a, 0x44, 0x0a, 0x23, 0x16, 0x32, 0x0a, 0x38, 0x0a, 0x3c, 0x0a, - 0x3f, 0x09, 0x40, 0x09, 0x41, 0x09, 0x42, 0x09, 0x43, 0x09, 0x44, 0x09, - 0x44, 0x0a, 0x44, 0x0a, 0x44, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, - 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x46, 0x0a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x16, 0x3e, 0x11, 0x3f, 0x0f, 0x40, 0x0d, 0x41, 0x0d, 0x41, 0x0c, - 0x42, 0x0c, 0x43, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x44, 0x0a, - 0x44, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x46, 0x0a, 0x46, 0x0a, - 0x46, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x6e, 0x03, 0x3a, - 0x0f, 0x27, 0x18, 0x1e, 0x1f, 0x19, 0x24, 0x17, 0x28, 0x14, 0x2c, 0x13, - 0x2f, 0x11, 0x31, 0x11, 0x32, 0x10, 0x34, 0x0f, 0x36, 0x0f, 0x37, 0x0f, - 0x38, 0x0e, 0x39, 0x0e, 0x3a, 0x0d, 0x3a, 0x0d, 0x3b, 0x0c, 0x3c, 0x0c, - 0x23, 0x3c, 0x25, 0x22, 0x2b, 0x18, 0x30, 0x14, 0x33, 0x11, 0x36, 0x10, - 0x38, 0x0f, 0x39, 0x0e, 0x3b, 0x0e, 0x3c, 0x0d, 0x3d, 0x0d, 0x3e, 0x0c, - 0x3f, 0x0c, 0x3f, 0x0c, 0x40, 0x0c, 0x40, 0x0c, 0x40, 0x0b, 0x41, 0x0b, - 0x41, 0x0b, 0x42, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1f, 0x3c, 0x18, - 0x3c, 0x14, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x0f, 0x3f, 0x0e, 0x40, 0x0e, - 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0c, 0x42, 0x0c, - 0x42, 0x0b, 0x42, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x44, 0x0b, 0x44, 0x0b, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x8a, 0x00, 0x59, 0x06, 0x40, 0x0d, 0x32, - 0x12, 0x2a, 0x18, 0x24, 0x1c, 0x20, 0x20, 0x1c, 0x23, 0x1b, 0x26, 0x18, - 0x28, 0x17, 0x2a, 0x16, 0x2c, 0x15, 0x2e, 0x14, 0x2f, 0x13, 0x31, 0x12, - 0x32, 0x12, 0x33, 0x12, 0x34, 0x11, 0x35, 0x10, 0x23, 0x4a, 0x24, 0x31, - 0x27, 0x25, 0x2a, 0x1e, 0x2d, 0x1a, 0x2f, 0x17, 0x32, 0x15, 0x33, 0x13, - 0x35, 0x12, 0x36, 0x11, 0x38, 0x10, 0x39, 0x10, 0x3a, 0x0f, 0x3a, 0x0f, - 0x3b, 0x0e, 0x3c, 0x0e, 0x3c, 0x0e, 0x3d, 0x0e, 0x3e, 0x0d, 0x3e, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x25, 0x3c, 0x1d, 0x3c, 0x19, 0x3c, 0x16, - 0x3d, 0x14, 0x3d, 0x12, 0x3f, 0x11, 0x3f, 0x10, 0x3f, 0x0f, 0x3f, 0x0f, - 0x40, 0x0e, 0x41, 0x0e, 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, - 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0c, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0x97, 0x00, 0x6c, 0x02, 0x52, 0x07, 0x43, 0x0b, 0x38, 0x10, 0x30, - 0x14, 0x2a, 0x18, 0x27, 0x1b, 0x23, 0x1e, 0x20, 0x20, 0x1e, 0x23, 0x1c, - 0x25, 0x1b, 0x27, 0x19, 0x28, 0x18, 0x2a, 0x17, 0x2b, 0x16, 0x2c, 0x16, - 0x2e, 0x16, 0x2f, 0x15, 0x23, 0x50, 0x23, 0x3b, 0x25, 0x2e, 0x27, 0x26, - 0x29, 0x21, 0x2c, 0x1d, 0x2e, 0x1a, 0x2f, 0x18, 0x31, 0x16, 0x33, 0x15, - 0x34, 0x14, 0x35, 0x13, 0x36, 0x12, 0x37, 0x11, 0x38, 0x11, 0x38, 0x11, - 0x39, 0x10, 0x3a, 0x10, 0x3a, 0x10, 0x3b, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x29, 0x3c, 0x22, 0x3b, 0x1d, 0x3b, 0x19, 0x3c, 0x17, 0x3c, 0x15, - 0x3c, 0x13, 0x3d, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x10, 0x3e, 0x0f, - 0x3f, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, 0x42, 0x0e, 0x42, 0x0d, - 0x42, 0x0d, 0x42, 0x0d, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x9e, 0x00, 0x79, - 0x00, 0x61, 0x04, 0x50, 0x07, 0x44, 0x0b, 0x3b, 0x0f, 0x34, 0x12, 0x2f, - 0x15, 0x2b, 0x18, 0x28, 0x1a, 0x25, 0x1d, 0x23, 0x1f, 0x21, 0x21, 0x1f, - 0x22, 0x1d, 0x24, 0x1c, 0x26, 0x1b, 0x27, 0x19, 0x28, 0x19, 0x2a, 0x19, - 0x23, 0x54, 0x23, 0x42, 0x24, 0x35, 0x25, 0x2d, 0x27, 0x27, 0x29, 0x22, - 0x2b, 0x1f, 0x2d, 0x1c, 0x2e, 0x1a, 0x30, 0x19, 0x31, 0x17, 0x32, 0x16, - 0x33, 0x15, 0x34, 0x14, 0x35, 0x13, 0x36, 0x13, 0x37, 0x12, 0x37, 0x11, - 0x38, 0x11, 0x38, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x2c, 0x3c, 0x25, - 0x3b, 0x20, 0x3b, 0x1d, 0x3b, 0x1a, 0x3c, 0x17, 0x3c, 0x16, 0x3c, 0x14, - 0x3c, 0x13, 0x3e, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x11, 0x3d, 0x10, - 0x3e, 0x0f, 0x3f, 0x0f, 0x40, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xa2, 0x00, 0x84, 0x00, 0x6c, 0x02, 0x5a, - 0x05, 0x4e, 0x07, 0x45, 0x0b, 0x3e, 0x0e, 0x38, 0x10, 0x33, 0x13, 0x2f, - 0x16, 0x2c, 0x18, 0x29, 0x1a, 0x26, 0x1c, 0x24, 0x1d, 0x22, 0x1f, 0x21, - 0x21, 0x1f, 0x21, 0x1e, 0x23, 0x1d, 0x25, 0x1c, 0x23, 0x56, 0x23, 0x47, - 0x23, 0x3b, 0x24, 0x32, 0x26, 0x2c, 0x27, 0x27, 0x29, 0x24, 0x2a, 0x21, - 0x2c, 0x1e, 0x2d, 0x1c, 0x2e, 0x1b, 0x30, 0x19, 0x30, 0x18, 0x32, 0x17, - 0x32, 0x16, 0x33, 0x15, 0x34, 0x14, 0x34, 0x14, 0x35, 0x13, 0x36, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x2e, 0x3c, 0x27, 0x3b, 0x23, 0x3b, 0x1f, - 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x18, 0x3c, 0x17, 0x3b, 0x15, 0x3c, 0x14, - 0x3c, 0x13, 0x3e, 0x13, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, - 0x3d, 0x10, 0x3e, 0x0f, 0x3f, 0x0f, 0x40, 0x0f, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0xa6, 0x00, 0x8b, 0x00, 0x75, 0x01, 0x63, 0x02, 0x57, 0x05, 0x4d, - 0x07, 0x45, 0x0a, 0x3e, 0x0d, 0x39, 0x0f, 0x35, 0x12, 0x32, 0x14, 0x2e, - 0x16, 0x2c, 0x18, 0x29, 0x19, 0x27, 0x1b, 0x26, 0x1d, 0x24, 0x1e, 0x22, - 0x20, 0x22, 0x21, 0x20, 0x23, 0x58, 0x23, 0x4a, 0x23, 0x3f, 0x24, 0x36, - 0x25, 0x30, 0x26, 0x2b, 0x27, 0x27, 0x29, 0x24, 0x2a, 0x22, 0x2b, 0x20, - 0x2d, 0x1e, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x18, 0x31, 0x18, - 0x32, 0x17, 0x32, 0x16, 0x33, 0x16, 0x34, 0x15, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x30, 0x3c, 0x2a, 0x3c, 0x25, 0x3a, 0x21, 0x3b, 0x1e, 0x3a, 0x1c, - 0x3b, 0x1a, 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x16, 0x3b, 0x15, 0x3c, 0x14, - 0x3d, 0x13, 0x3e, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, - 0x3d, 0x10, 0x3e, 0x0f, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xa8, 0x00, 0x90, - 0x00, 0x7c, 0x00, 0x6b, 0x01, 0x5f, 0x04, 0x55, 0x05, 0x4c, 0x07, 0x45, - 0x0a, 0x40, 0x0c, 0x3c, 0x0f, 0x37, 0x10, 0x34, 0x12, 0x31, 0x15, 0x2e, - 0x16, 0x2b, 0x18, 0x2a, 0x19, 0x28, 0x1a, 0x26, 0x1d, 0x25, 0x1d, 0x23, - 0x23, 0x59, 0x23, 0x4d, 0x23, 0x43, 0x24, 0x3b, 0x24, 0x34, 0x25, 0x2f, - 0x26, 0x2b, 0x27, 0x28, 0x28, 0x25, 0x2a, 0x23, 0x2b, 0x21, 0x2c, 0x1f, - 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x19, 0x31, 0x18, - 0x32, 0x17, 0x32, 0x16, 0x17, 0x00, 0x2f, 0x00, 0x4c, 0x00, 0x56, 0x00, - 0x59, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, - 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, - 0x14, 0x2c, 0x29, 0x01, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5b, 0x00, - 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, - 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x62, 0x00, - 0x62, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, - 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, - 0x11, 0x31, 0x23, 0x03, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, - 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, - 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x3e, 0x32, 0x3d, 0x2c, - 0x3c, 0x26, 0x3b, 0x23, 0x3b, 0x20, 0x3b, 0x1e, 0x3a, 0x1c, 0x3b, 0x1a, - 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x14, - 0x3d, 0x13, 0x3e, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaa, 0x00, 0x94, 0x00, 0x82, 0x00, 0x72, - 0x01, 0x65, 0x02, 0x5b, 0x04, 0x53, 0x06, 0x4b, 0x08, 0x45, 0x0a, 0x41, - 0x0c, 0x3d, 0x0e, 0x39, 0x0f, 0x36, 0x12, 0x33, 0x12, 0x30, 0x16, 0x2e, - 0x16, 0x2b, 0x18, 0x2b, 0x19, 0x29, 0x1a, 0x26, 0x23, 0x5a, 0x23, 0x4f, - 0x23, 0x46, 0x23, 0x3e, 0x24, 0x37, 0x25, 0x32, 0x25, 0x2e, 0x26, 0x2b, - 0x27, 0x28, 0x28, 0x25, 0x2a, 0x23, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x1e, - 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x19, 0x30, 0x18, - 0x00, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x45, 0x00, 0x4f, 0x00, 0x55, 0x00, - 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, - 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x57, 0x04, 0x16, - 0x27, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x53, 0x00, 0x56, 0x00, 0x59, 0x00, - 0x5a, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, - 0x5d, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x3b, 0x00, 0x48, 0x00, 0x55, 0x00, 0x58, 0x00, 0x57, 0x00, - 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, - 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x20, - 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, - 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, - 0x5d, 0x00, 0x5d, 0x00, 0x3e, 0x33, 0x3d, 0x2d, 0x3c, 0x28, 0x3b, 0x25, - 0x3a, 0x22, 0x3b, 0x1f, 0x3b, 0x1e, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, - 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x16, 0x3b, 0x15, 0x3c, 0x15, 0x3c, 0x13, - 0x3d, 0x13, 0x3f, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0xaa, 0x00, 0x98, 0x00, 0x87, 0x00, 0x78, 0x00, 0x6b, 0x01, 0x61, - 0x03, 0x58, 0x05, 0x51, 0x07, 0x4b, 0x08, 0x46, 0x0a, 0x42, 0x0c, 0x3e, - 0x0d, 0x3a, 0x0f, 0x37, 0x11, 0x35, 0x12, 0x32, 0x14, 0x30, 0x16, 0x2e, - 0x16, 0x2b, 0x18, 0x2b, 0x23, 0x5a, 0x23, 0x51, 0x23, 0x48, 0x23, 0x41, - 0x24, 0x3a, 0x24, 0x35, 0x25, 0x31, 0x26, 0x2e, 0x27, 0x2a, 0x27, 0x28, - 0x28, 0x26, 0x2a, 0x24, 0x2a, 0x22, 0x2b, 0x21, 0x2c, 0x1f, 0x2d, 0x1e, - 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x22, 0x00, 0x36, 0x00, 0x42, 0x00, 0x4a, 0x00, 0x4f, 0x00, - 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, - 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x82, 0x00, 0x57, 0x01, 0x12, 0x17, 0x00, - 0x2f, 0x00, 0x3e, 0x00, 0x47, 0x00, 0x4d, 0x00, 0x51, 0x00, 0x53, 0x00, - 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x48, 0x00, - 0x1b, 0x00, 0x34, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4f, 0x00, - 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, - 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x0d, 0x00, - 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, - 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, - 0x3e, 0x34, 0x3d, 0x2f, 0x3b, 0x2a, 0x3c, 0x26, 0x3a, 0x23, 0x3b, 0x21, - 0x3b, 0x1f, 0x3b, 0x1d, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x17, - 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x15, 0x3c, 0x13, - 0x3e, 0x13, 0x3f, 0x13, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xac, 0x00, 0x9b, - 0x00, 0x8b, 0x00, 0x7c, 0x00, 0x71, 0x01, 0x67, 0x02, 0x5e, 0x04, 0x57, - 0x05, 0x51, 0x07, 0x4b, 0x08, 0x46, 0x0a, 0x42, 0x0c, 0x3f, 0x0c, 0x3b, - 0x0f, 0x39, 0x0f, 0x35, 0x12, 0x34, 0x12, 0x31, 0x14, 0x30, 0x16, 0x2e, - 0x23, 0x5b, 0x23, 0x52, 0x23, 0x4a, 0x23, 0x43, 0x23, 0x3d, 0x24, 0x38, - 0x25, 0x34, 0x25, 0x30, 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, - 0x2a, 0x24, 0x2a, 0x22, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x1f, 0x2d, 0x1d, - 0x2e, 0x1d, 0x2e, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x19, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, 0x00, - 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, - 0x00, 0x91, 0x00, 0x78, 0x00, 0x43, 0x00, 0x10, 0x0e, 0x00, 0x23, 0x00, - 0x31, 0x00, 0x3b, 0x00, 0x42, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4f, 0x00, - 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x55, 0x00, 0x34, 0x00, 0x0b, 0x00, - 0x1f, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, 0x00, - 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, - 0x00, 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x02, 0x00, 0x1a, 0x00, - 0x2a, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, - 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, 0x00, 0x3e, 0x35, 0x3e, 0x30, - 0x3b, 0x2c, 0x3c, 0x28, 0x3b, 0x25, 0x3a, 0x22, 0x3b, 0x20, 0x3b, 0x1f, - 0x3a, 0x1d, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, - 0x3c, 0x17, 0x3a, 0x16, 0x3b, 0x15, 0x3c, 0x15, 0x3c, 0x14, 0x3c, 0x13, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xad, 0x00, 0x9d, 0x00, 0x8e, 0x00, 0x81, - 0x00, 0x76, 0x00, 0x6b, 0x01, 0x63, 0x02, 0x5c, 0x04, 0x56, 0x05, 0x50, - 0x07, 0x4b, 0x08, 0x47, 0x0a, 0x43, 0x0c, 0x40, 0x0c, 0x3c, 0x0f, 0x3a, - 0x0f, 0x37, 0x11, 0x35, 0x12, 0x33, 0x12, 0x30, 0x23, 0x5b, 0x23, 0x53, - 0x23, 0x4c, 0x23, 0x45, 0x23, 0x40, 0x24, 0x3b, 0x24, 0x36, 0x25, 0x33, - 0x25, 0x30, 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, 0x2a, 0x25, - 0x2a, 0x23, 0x2b, 0x22, 0x2b, 0x20, 0x2c, 0x20, 0x2d, 0x1e, 0x2d, 0x1d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, - 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, - 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x96, 0x00, 0x87, - 0x00, 0x61, 0x00, 0x36, 0x00, 0x10, 0x0a, 0x02, 0x1a, 0x00, 0x27, 0x00, - 0x32, 0x00, 0x3a, 0x00, 0x40, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4b, 0x00, - 0x4e, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x58, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x14, 0x00, - 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, - 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x97, 0x00, 0x8a, - 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x1f, 0x00, - 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, - 0x4b, 0x00, 0x4e, 0x00, 0x3e, 0x36, 0x3e, 0x31, 0x3b, 0x2c, 0x3c, 0x29, - 0x3c, 0x26, 0x3a, 0x24, 0x3a, 0x21, 0x3b, 0x20, 0x3b, 0x1f, 0x39, 0x1d, - 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, - 0x3a, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x15, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0xad, 0x00, 0x9f, 0x00, 0x91, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6f, - 0x01, 0x67, 0x02, 0x60, 0x03, 0x59, 0x04, 0x54, 0x05, 0x4f, 0x07, 0x4b, - 0x08, 0x47, 0x0a, 0x43, 0x0b, 0x41, 0x0c, 0x3d, 0x0e, 0x3b, 0x0f, 0x38, - 0x0f, 0x35, 0x12, 0x35, 0x23, 0x5b, 0x23, 0x54, 0x23, 0x4e, 0x23, 0x47, - 0x23, 0x42, 0x24, 0x3c, 0x24, 0x38, 0x25, 0x35, 0x25, 0x32, 0x26, 0x2f, - 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, 0x29, 0x25, 0x2a, 0x23, - 0x2b, 0x22, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, 0x00, - 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, - 0x49, 0x00, 0x4c, 0x00, 0x00, 0x99, 0x00, 0x8f, 0x00, 0x74, 0x00, 0x51, - 0x00, 0x2e, 0x00, 0x10, 0x08, 0x04, 0x13, 0x00, 0x20, 0x00, 0x2a, 0x00, - 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x48, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x57, 0x00, - 0x44, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, 0x00, - 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, - 0x49, 0x00, 0x4c, 0x00, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, - 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, - 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, - 0x3e, 0x36, 0x3e, 0x31, 0x3b, 0x2d, 0x3b, 0x2a, 0x3c, 0x27, 0x3a, 0x25, - 0x3a, 0x23, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1c, 0x3b, 0x1c, - 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x17, - 0x3a, 0x16, 0x3b, 0x15, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xad, 0x00, 0xa0, - 0x00, 0x94, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x74, 0x00, 0x6c, 0x01, 0x65, - 0x02, 0x5e, 0x04, 0x58, 0x05, 0x53, 0x05, 0x4f, 0x07, 0x4b, 0x08, 0x47, - 0x0a, 0x44, 0x0b, 0x41, 0x0c, 0x3e, 0x0d, 0x3b, 0x0f, 0x3a, 0x0f, 0x37, - 0x23, 0x5c, 0x23, 0x55, 0x23, 0x4f, 0x23, 0x49, 0x23, 0x44, 0x23, 0x3f, - 0x24, 0x3b, 0x24, 0x37, 0x25, 0x34, 0x25, 0x31, 0x26, 0x2e, 0x26, 0x2c, - 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x24, 0x2a, 0x22, - 0x2b, 0x22, 0x2b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, - 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, 0x00, - 0x00, 0x9b, 0x00, 0x94, 0x00, 0x7f, 0x00, 0x64, 0x00, 0x46, 0x00, 0x29, - 0x00, 0x10, 0x07, 0x06, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, - 0x32, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, - 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, - 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, 0x00, - 0x00, 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, - 0x00, 0x1f, 0x00, 0x0c, 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, - 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, 0x00, 0x3e, 0x37, 0x3e, 0x32, - 0x3c, 0x2f, 0x3b, 0x2b, 0x3c, 0x28, 0x3b, 0x26, 0x3a, 0x24, 0x3a, 0x22, - 0x3b, 0x21, 0x3b, 0x1f, 0x3a, 0x1e, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, - 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x17, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x97, 0x00, 0x8b, - 0x00, 0x80, 0x00, 0x77, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x61, 0x02, 0x5c, - 0x04, 0x57, 0x05, 0x52, 0x05, 0x4e, 0x07, 0x4b, 0x09, 0x47, 0x0a, 0x44, - 0x0b, 0x41, 0x0c, 0x3f, 0x0c, 0x3c, 0x0f, 0x3b, 0x23, 0x5c, 0x23, 0x56, - 0x23, 0x50, 0x23, 0x4a, 0x23, 0x45, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, - 0x25, 0x35, 0x25, 0x33, 0x25, 0x30, 0x26, 0x2e, 0x26, 0x2c, 0x27, 0x2a, - 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x24, 0x2a, 0x23, 0x2b, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, - 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x9c, 0x00, 0x96, - 0x00, 0x87, 0x00, 0x71, 0x00, 0x57, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x10, - 0x06, 0x07, 0x0b, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x26, 0x00, 0x2d, 0x00, - 0x32, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, - 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x9c, 0x00, 0x97, - 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, - 0x00, 0x0f, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, - 0x2c, 0x00, 0x31, 0x00, 0x3e, 0x37, 0x3e, 0x33, 0x3c, 0x2f, 0x3b, 0x2c, - 0x3c, 0x29, 0x3c, 0x27, 0x39, 0x25, 0x3a, 0x23, 0x3b, 0x21, 0x3b, 0x20, - 0x3b, 0x1f, 0x3a, 0x1e, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, - 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3b, 0x17, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, 0x84, 0x00, 0x7a, - 0x00, 0x72, 0x00, 0x6c, 0x01, 0x65, 0x02, 0x5f, 0x04, 0x5a, 0x04, 0x56, - 0x05, 0x52, 0x06, 0x4e, 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x41, - 0x0c, 0x40, 0x0c, 0x3d, 0x23, 0x5c, 0x23, 0x56, 0x23, 0x51, 0x23, 0x4c, - 0x23, 0x47, 0x23, 0x42, 0x23, 0x3e, 0x24, 0x3b, 0x24, 0x37, 0x25, 0x34, - 0x25, 0x32, 0x25, 0x30, 0x26, 0x2e, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, - 0x28, 0x27, 0x29, 0x25, 0x2a, 0x25, 0x2a, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, - 0x30, 0x00, 0x35, 0x00, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x7a, - 0x00, 0x64, 0x00, 0x4e, 0x00, 0x37, 0x00, 0x22, 0x00, 0x10, 0x05, 0x08, - 0x0a, 0x02, 0x12, 0x00, 0x1a, 0x00, 0x22, 0x00, 0x28, 0x00, 0x2d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, - 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, - 0x30, 0x00, 0x35, 0x00, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, - 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, - 0x00, 0x04, 0x07, 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, - 0x3e, 0x38, 0x3e, 0x33, 0x3d, 0x30, 0x3b, 0x2d, 0x3c, 0x2a, 0x3c, 0x27, - 0x3a, 0x26, 0x3a, 0x24, 0x3a, 0x22, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, - 0x39, 0x1e, 0x3a, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, - 0x3c, 0x18, 0x3c, 0x17, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaf, 0x00, 0xa5, - 0x00, 0x99, 0x00, 0x90, 0x00, 0x87, 0x00, 0x7e, 0x00, 0x76, 0x00, 0x6f, - 0x01, 0x68, 0x02, 0x62, 0x02, 0x5e, 0x04, 0x59, 0x04, 0x55, 0x05, 0x51, - 0x07, 0x4e, 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x41, 0x0c, 0x41, - 0x23, 0x5c, 0x23, 0x57, 0x23, 0x52, 0x23, 0x4d, 0x23, 0x48, 0x23, 0x44, - 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x24, 0x36, 0x25, 0x34, 0x25, 0x31, - 0x26, 0x2f, 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, - 0x28, 0x25, 0x2a, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, 0x00, - 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x90, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x5b, - 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, 0x00, 0x10, 0x04, 0x09, 0x09, 0x03, - 0x0f, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, - 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, 0x00, - 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, - 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, - 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x3e, 0x38, 0x3e, 0x34, - 0x3e, 0x31, 0x3b, 0x2e, 0x3b, 0x2b, 0x3c, 0x29, 0x3c, 0x27, 0x39, 0x25, - 0x3a, 0x24, 0x3a, 0x21, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1d, - 0x3a, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x19, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaf, 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x91, - 0x00, 0x88, 0x00, 0x80, 0x00, 0x78, 0x00, 0x71, 0x00, 0x6b, 0x01, 0x66, - 0x02, 0x61, 0x03, 0x5c, 0x04, 0x58, 0x05, 0x55, 0x05, 0x50, 0x07, 0x4e, - 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x42, 0x23, 0x5c, 0x23, 0x58, - 0x23, 0x53, 0x23, 0x4d, 0x23, 0x49, 0x23, 0x45, 0x23, 0x41, 0x24, 0x3d, - 0x24, 0x3b, 0x24, 0x38, 0x25, 0x35, 0x25, 0x33, 0x25, 0x31, 0x26, 0x2f, - 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x26, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, - 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x9d, 0x00, 0x9b, - 0x00, 0x93, 0x00, 0x86, 0x00, 0x76, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, - 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x10, 0x04, 0x09, 0x08, 0x04, 0x0c, 0x00, - 0x14, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, - 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, - 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x9e, 0x00, 0x9b, - 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, - 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, - 0x09, 0x00, 0x11, 0x00, 0x3e, 0x38, 0x3e, 0x35, 0x3e, 0x32, 0x3b, 0x2e, - 0x3b, 0x2c, 0x3c, 0x2a, 0x3c, 0x27, 0x3a, 0x26, 0x3a, 0x24, 0x3a, 0x23, - 0x3b, 0x21, 0x3b, 0x20, 0x3b, 0x1f, 0x3a, 0x1f, 0x39, 0x1d, 0x3a, 0x1c, - 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x83, - 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6e, 0x01, 0x69, 0x01, 0x63, 0x02, 0x5f, - 0x04, 0x5b, 0x04, 0x56, 0x05, 0x54, 0x05, 0x4f, 0x07, 0x4e, 0x07, 0x4a, - 0x09, 0x47, 0x0a, 0x46, 0x23, 0x5d, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4f, - 0x23, 0x4a, 0x23, 0x46, 0x23, 0x43, 0x23, 0x3f, 0x24, 0x3c, 0x24, 0x39, - 0x24, 0x36, 0x25, 0x34, 0x25, 0x32, 0x25, 0x30, 0x26, 0x2f, 0x26, 0x2d, - 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, - 0x16, 0x00, 0x1c, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x95, 0x00, 0x8a, - 0x00, 0x7d, 0x00, 0x6d, 0x00, 0x5d, 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x2c, - 0x00, 0x1d, 0x00, 0x10, 0x04, 0x0a, 0x07, 0x05, 0x0b, 0x00, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, - 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, 0x00, - 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, - 0x16, 0x00, 0x1c, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, - 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, - 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, - 0x3e, 0x39, 0x3e, 0x35, 0x3e, 0x32, 0x3b, 0x2f, 0x3b, 0x2d, 0x3c, 0x2a, - 0x3c, 0x28, 0x3b, 0x27, 0x39, 0x24, 0x3a, 0x24, 0x3a, 0x22, 0x3b, 0x21, - 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1f, 0x39, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, - 0x3b, 0x1a, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xb0, 0x00, 0xa7, - 0x00, 0x9e, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x86, 0x00, 0x7e, 0x00, 0x77, - 0x00, 0x71, 0x00, 0x6b, 0x01, 0x66, 0x02, 0x62, 0x02, 0x5d, 0x04, 0x5a, - 0x04, 0x55, 0x05, 0x53, 0x05, 0x4f, 0x07, 0x4e, 0x07, 0x4a, 0x09, 0x47, - 0x23, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, 0x23, 0x4c, 0x23, 0x48, - 0x23, 0x44, 0x23, 0x40, 0x24, 0x3d, 0x24, 0x3a, 0x24, 0x38, 0x25, 0x36, - 0x25, 0x33, 0x25, 0x32, 0x25, 0x2f, 0x26, 0x2f, 0x26, 0x2c, 0x27, 0x2c, - 0x27, 0x2a, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, 0x00, - 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x81, 0x00, 0x74, - 0x00, 0x65, 0x00, 0x56, 0x00, 0x46, 0x00, 0x37, 0x00, 0x29, 0x00, 0x1c, - 0x00, 0x10, 0x03, 0x0a, 0x07, 0x06, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, - 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, 0x00, - 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, 0x00, - 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, - 0x00, 0x6b, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, - 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, 0x32, 0x59, 0x2a, 0x5b, - 0x28, 0x5c, 0x27, 0x5d, 0x27, 0x5d, 0x26, 0x5e, 0x26, 0x5e, 0x26, 0x5e, - 0x26, 0x5e, 0x26, 0x5e, 0x25, 0x5e, 0x25, 0x5e, 0x25, 0x5f, 0x25, 0x5f, - 0x25, 0x5f, 0x25, 0x5f, 0x25, 0x5f, 0x24, 0x5f, 0x24, 0x5f, 0x24, 0x60, - 0x32, 0x3c, 0x25, 0x4a, 0x24, 0x50, 0x23, 0x54, 0x23, 0x56, 0x23, 0x58, - 0x23, 0x59, 0x23, 0x5a, 0x23, 0x5a, 0x23, 0x5b, 0x23, 0x5b, 0x23, 0x5b, - 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5d, - 0x23, 0x5d, 0x23, 0x5d, 0x00, 0x6c, 0x0e, 0x60, 0x14, 0x60, 0x18, 0x60, - 0x1b, 0x60, 0x1d, 0x60, 0x1d, 0x60, 0x1e, 0x60, 0x1f, 0x60, 0x20, 0x60, - 0x20, 0x60, 0x20, 0x60, 0x21, 0x60, 0x21, 0x60, 0x21, 0x60, 0x21, 0x60, - 0x22, 0x60, 0x22, 0x60, 0x22, 0x60, 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x9e, 0x00, 0x9c, - 0x00, 0x97, 0x00, 0x90, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x5e, - 0x00, 0x50, 0x00, 0x42, 0x00, 0x34, 0x00, 0x27, 0x00, 0x1b, 0x00, 0x0f, - 0x03, 0x0b, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, - 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x9e, 0x00, 0x9d, - 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, - 0x00, 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, - 0x00, 0x16, 0x00, 0x0d, 0x38, 0x4f, 0x31, 0x53, 0x2e, 0x56, 0x2c, 0x57, - 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5a, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x38, 0x22, 0x2b, 0x31, - 0x27, 0x3b, 0x25, 0x42, 0x24, 0x47, 0x23, 0x4a, 0x23, 0x4d, 0x23, 0x4f, - 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, 0x23, 0x55, 0x23, 0x56, - 0x23, 0x56, 0x23, 0x57, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, - 0x00, 0x92, 0x01, 0x79, 0x07, 0x6f, 0x0c, 0x6b, 0x0f, 0x68, 0x12, 0x67, - 0x14, 0x66, 0x16, 0x65, 0x17, 0x64, 0x18, 0x64, 0x19, 0x63, 0x1a, 0x63, - 0x1b, 0x63, 0x1b, 0x63, 0x1c, 0x63, 0x1c, 0x62, 0x1d, 0x62, 0x1d, 0x62, - 0x1d, 0x62, 0x1e, 0x62, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, - 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x58, 0x00, 0x4b, - 0x00, 0x3e, 0x00, 0x31, 0x00, 0x25, 0x00, 0x1a, 0x00, 0x0f, 0x03, 0x0b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, - 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, 0x00, - 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, - 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, - 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, - 0x3a, 0x4b, 0x34, 0x4f, 0x31, 0x52, 0x2f, 0x54, 0x2d, 0x56, 0x2c, 0x57, - 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5a, - 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x3c, 0x18, 0x30, 0x25, 0x2a, 0x2e, 0x27, 0x35, - 0x25, 0x3b, 0x24, 0x3f, 0x24, 0x43, 0x24, 0x46, 0x23, 0x48, 0x23, 0x4a, - 0x23, 0x4c, 0x23, 0x4e, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x51, 0x23, 0x52, - 0x23, 0x53, 0x23, 0x53, 0x23, 0x54, 0x23, 0x54, 0x00, 0xa1, 0x00, 0x88, - 0x03, 0x7b, 0x06, 0x74, 0x09, 0x70, 0x0c, 0x6d, 0x0e, 0x6b, 0x10, 0x6a, - 0x11, 0x69, 0x13, 0x68, 0x14, 0x67, 0x15, 0x66, 0x16, 0x66, 0x17, 0x65, - 0x17, 0x65, 0x18, 0x65, 0x19, 0x65, 0x19, 0x64, 0x1a, 0x64, 0x1a, 0x64, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, - 0x00, 0x77, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x46, 0x00, 0x3a, - 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, - 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, 0x00, - 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, - 0x00, 0x7b, 0x00, 0x71, 0x00, 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, - 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, 0x3b, 0x48, 0x36, 0x4c, - 0x33, 0x4f, 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, 0x2d, 0x55, 0x2c, 0x57, - 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, - 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5b, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, - 0x3f, 0x14, 0x33, 0x1e, 0x2d, 0x26, 0x29, 0x2d, 0x27, 0x32, 0x26, 0x36, - 0x25, 0x3b, 0x24, 0x3e, 0x24, 0x41, 0x24, 0x43, 0x23, 0x45, 0x23, 0x47, - 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4d, 0x23, 0x4d, 0x23, 0x4f, - 0x23, 0x50, 0x23, 0x50, 0x00, 0xa7, 0x00, 0x92, 0x01, 0x84, 0x03, 0x7d, - 0x05, 0x77, 0x08, 0x73, 0x0a, 0x71, 0x0c, 0x6f, 0x0d, 0x6d, 0x0f, 0x6b, - 0x10, 0x6a, 0x11, 0x6a, 0x12, 0x69, 0x13, 0x68, 0x14, 0x68, 0x15, 0x67, - 0x15, 0x66, 0x16, 0x66, 0x17, 0x66, 0x17, 0x66, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x31, 0x23, 0x03, - 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, 0x00, - 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, - 0x5e, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x11, 0x31, 0x00, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, - 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, - 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x47, 0x38, 0x4b, 0x35, 0x4e, 0x32, 0x4f, - 0x31, 0x51, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, 0x2c, 0x56, - 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, - 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x2a, 0x59, 0x40, 0x11, 0x36, 0x1a, - 0x2f, 0x21, 0x2c, 0x27, 0x29, 0x2c, 0x27, 0x30, 0x26, 0x34, 0x25, 0x37, - 0x25, 0x3a, 0x24, 0x3d, 0x24, 0x40, 0x24, 0x42, 0x24, 0x44, 0x23, 0x45, - 0x23, 0x47, 0x23, 0x48, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4c, - 0x00, 0xaa, 0x00, 0x98, 0x00, 0x8c, 0x02, 0x83, 0x03, 0x7d, 0x05, 0x79, - 0x07, 0x76, 0x09, 0x73, 0x0a, 0x71, 0x0c, 0x6f, 0x0d, 0x6e, 0x0e, 0x6d, - 0x0f, 0x6c, 0x10, 0x6b, 0x11, 0x6a, 0x12, 0x6a, 0x13, 0x69, 0x13, 0x68, - 0x14, 0x68, 0x15, 0x68, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x20, 0x1f, 0x00, 0x3c, 0x00, - 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, - 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x03, 0x00, 0x20, - 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, - 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, - 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x46, 0x39, 0x4a, 0x36, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x50, - 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2d, 0x56, - 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, - 0x2a, 0x57, 0x2a, 0x57, 0x41, 0x10, 0x38, 0x17, 0x32, 0x1d, 0x2e, 0x22, - 0x2b, 0x27, 0x29, 0x2b, 0x27, 0x2f, 0x26, 0x32, 0x25, 0x35, 0x25, 0x38, - 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3f, 0x24, 0x40, 0x24, 0x42, 0x23, 0x44, - 0x23, 0x45, 0x23, 0x46, 0x23, 0x48, 0x23, 0x48, 0x00, 0xac, 0x00, 0x9d, - 0x00, 0x91, 0x01, 0x89, 0x02, 0x82, 0x03, 0x7e, 0x05, 0x7a, 0x07, 0x77, - 0x08, 0x75, 0x09, 0x73, 0x0b, 0x71, 0x0c, 0x70, 0x0d, 0x6e, 0x0e, 0x6e, - 0x0e, 0x6c, 0x0f, 0x6c, 0x10, 0x6b, 0x10, 0x6a, 0x11, 0x6a, 0x12, 0x6a, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, - 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, - 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x4d, - 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, - 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x39, 0x48, - 0x37, 0x4a, 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2d, 0x56, - 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, - 0x42, 0x0f, 0x39, 0x15, 0x33, 0x1a, 0x2f, 0x1f, 0x2d, 0x24, 0x2a, 0x27, - 0x29, 0x2b, 0x27, 0x2e, 0x26, 0x31, 0x26, 0x34, 0x25, 0x36, 0x25, 0x38, - 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3e, 0x24, 0x40, 0x24, 0x41, 0x24, 0x43, - 0x23, 0x44, 0x23, 0x45, 0x00, 0xae, 0x00, 0xa1, 0x00, 0x96, 0x00, 0x8d, - 0x01, 0x87, 0x02, 0x82, 0x03, 0x7e, 0x05, 0x7b, 0x06, 0x78, 0x07, 0x76, - 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x71, 0x0c, 0x70, 0x0c, 0x6f, 0x0d, 0x6e, - 0x0e, 0x6d, 0x0f, 0x6c, 0x10, 0x6c, 0x10, 0x6b, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x7c, - 0x00, 0x4d, 0x00, 0x1f, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, 0x00, - 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, - 0x53, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x00, 0x00, 0x1f, 0x00, 0x42, 0x00, 0x5a, - 0x00, 0x6a, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, - 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4a, - 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x52, 0x2f, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x55, - 0x2c, 0x57, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x43, 0x0e, 0x3b, 0x13, - 0x35, 0x18, 0x31, 0x1c, 0x2e, 0x21, 0x2c, 0x24, 0x2a, 0x28, 0x28, 0x2b, - 0x27, 0x2e, 0x27, 0x30, 0x26, 0x33, 0x25, 0x35, 0x25, 0x37, 0x25, 0x39, - 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x24, 0x40, 0x24, 0x42, - 0x00, 0xaf, 0x00, 0xa3, 0x00, 0x99, 0x00, 0x91, 0x00, 0x8b, 0x02, 0x86, - 0x02, 0x81, 0x03, 0x7e, 0x05, 0x7b, 0x06, 0x79, 0x07, 0x77, 0x08, 0x75, - 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x0c, 0x6f, 0x0d, 0x6e, - 0x0e, 0x6e, 0x0e, 0x6d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, - 0x00, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, 0x00, - 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x4a, 0x00, - 0x28, 0x00, 0x02, 0x00, 0x00, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, - 0x00, 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, - 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4b, 0x34, 0x4e, - 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, - 0x2c, 0x56, 0x2b, 0x57, 0x44, 0x0e, 0x3c, 0x12, 0x36, 0x16, 0x33, 0x1a, - 0x30, 0x1e, 0x2d, 0x22, 0x2b, 0x25, 0x2a, 0x28, 0x28, 0x2a, 0x27, 0x2d, - 0x27, 0x30, 0x26, 0x32, 0x26, 0x34, 0x25, 0x35, 0x25, 0x37, 0x25, 0x39, - 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x00, 0xb0, 0x00, 0xa5, - 0x00, 0x9d, 0x00, 0x95, 0x00, 0x8e, 0x01, 0x89, 0x02, 0x85, 0x03, 0x81, - 0x04, 0x7e, 0x05, 0x7c, 0x06, 0x7a, 0x07, 0x78, 0x07, 0x76, 0x09, 0x75, - 0x09, 0x73, 0x0b, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x0c, 0x70, 0x0d, 0x6e, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, - 0x00, 0x09, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, - 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x00, - 0x00, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, - 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3b, 0x46, - 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x34, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, - 0x44, 0x0d, 0x3d, 0x11, 0x38, 0x15, 0x34, 0x19, 0x31, 0x1c, 0x2e, 0x20, - 0x2d, 0x23, 0x2b, 0x25, 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2d, 0x27, 0x2f, - 0x26, 0x31, 0x26, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x38, 0x25, 0x39, - 0x24, 0x3a, 0x24, 0x3c, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x98, - 0x00, 0x91, 0x00, 0x8c, 0x01, 0x88, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7e, - 0x05, 0x7c, 0x06, 0x7a, 0x06, 0x79, 0x07, 0x77, 0x08, 0x76, 0x09, 0x74, - 0x0a, 0x73, 0x0b, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x95, - 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, - 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, - 0x37, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x09, - 0x00, 0x1f, 0x00, 0x33, 0x00, 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, - 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x44, 0x0d, 0x3e, 0x10, - 0x39, 0x14, 0x35, 0x17, 0x32, 0x1b, 0x30, 0x1e, 0x2e, 0x21, 0x2c, 0x23, - 0x2b, 0x26, 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2d, 0x27, 0x2e, 0x26, 0x30, - 0x26, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x36, 0x25, 0x38, 0x25, 0x39, - 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x8f, - 0x01, 0x8b, 0x02, 0x87, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, - 0x06, 0x7b, 0x06, 0x79, 0x07, 0x78, 0x07, 0x76, 0x09, 0x76, 0x09, 0x74, - 0x0a, 0x73, 0x0b, 0x72, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, - 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, - 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, - 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, 0x00, 0x00, 0x0c, 0x00, 0x1f, - 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, - 0x00, 0x6c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x42, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, - 0x35, 0x4b, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x44, 0x0c, 0x3f, 0x10, 0x3a, 0x13, 0x36, 0x16, - 0x33, 0x19, 0x30, 0x1c, 0x2e, 0x1f, 0x2d, 0x21, 0x2b, 0x24, 0x2a, 0x26, - 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2e, 0x26, 0x30, 0x26, 0x31, - 0x26, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x37, 0x00, 0xb2, 0x00, 0xaa, - 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8d, 0x01, 0x89, - 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x06, 0x7b, - 0x06, 0x79, 0x07, 0x79, 0x07, 0x77, 0x08, 0x76, 0x09, 0x75, 0x09, 0x74, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, - 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, 0x00, 0x04, 0x07, 0x00, - 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, - 0x2b, 0x00, 0x17, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2e, - 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x46, - 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, - 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x45, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x37, 0x15, 0x34, 0x18, 0x32, 0x1b, - 0x30, 0x1d, 0x2e, 0x20, 0x2d, 0x22, 0x2b, 0x24, 0x2a, 0x26, 0x2a, 0x28, - 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2e, 0x27, 0x2f, 0x26, 0x31, 0x26, 0x32, - 0x25, 0x33, 0x25, 0x35, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9e, - 0x00, 0x98, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, 0x01, 0x88, 0x02, 0x85, - 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7a, - 0x07, 0x79, 0x07, 0x78, 0x07, 0x76, 0x09, 0x76, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, - 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, - 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x03, 0x00, 0x0c, 0x00, - 0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, - 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x45, 0x3a, 0x46, 0x39, 0x46, - 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x33, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x51, - 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x40, 0x0f, - 0x3b, 0x11, 0x38, 0x14, 0x35, 0x17, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, - 0x2d, 0x21, 0x2c, 0x22, 0x2b, 0x25, 0x2a, 0x26, 0x29, 0x28, 0x28, 0x2a, - 0x28, 0x2c, 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x30, 0x26, 0x32, 0x25, 0x33, - 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, - 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8a, 0x02, 0x88, 0x02, 0x85, 0x02, 0x83, - 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7a, 0x06, 0x79, - 0x07, 0x78, 0x07, 0x77, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, - 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, - 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, - 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x00, - 0x00, 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, - 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x42, 0x3d, 0x44, 0x3a, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x38, 0x13, - 0x36, 0x16, 0x33, 0x18, 0x31, 0x1b, 0x30, 0x1d, 0x2e, 0x1f, 0x2d, 0x21, - 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, - 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x2f, 0x26, 0x31, 0x00, 0xb3, 0x00, 0xac, - 0x00, 0xa7, 0x00, 0xa1, 0x00, 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, - 0x01, 0x8c, 0x01, 0x89, 0x02, 0x87, 0x02, 0x85, 0x02, 0x82, 0x03, 0x81, - 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7b, 0x06, 0x79, 0x07, 0x79, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, - 0x00, 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, - 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, - 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, 0x00, 0x07, 0x00, 0x00, 0x06, - 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x44, - 0x3b, 0x46, 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x35, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, 0x2f, 0x53, - 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x39, 0x13, 0x37, 0x15, 0x34, 0x18, - 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, 0x2d, 0x20, 0x2d, 0x22, 0x2b, 0x23, - 0x2b, 0x25, 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2d, - 0x27, 0x2f, 0x26, 0x2f, 0x00, 0xb3, 0x00, 0xad, 0x00, 0xa7, 0x00, 0xa3, - 0x00, 0x9d, 0x00, 0x99, 0x00, 0x95, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8b, - 0x02, 0x88, 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, 0x03, 0x81, 0x04, 0x7f, - 0x05, 0x7e, 0x05, 0x7c, 0x06, 0x7b, 0x06, 0x7a, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, - 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, - 0x00, 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, - 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, - 0x2c, 0x00, 0x1e, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x14, - 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, - 0x39, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x52, 0x45, 0x0b, 0x41, 0x0e, - 0x3d, 0x10, 0x3a, 0x12, 0x37, 0x14, 0x34, 0x17, 0x32, 0x19, 0x31, 0x1b, - 0x30, 0x1d, 0x2e, 0x1f, 0x2d, 0x20, 0x2c, 0x22, 0x2b, 0x24, 0x2a, 0x25, - 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2c, 0x27, 0x2e, - 0x00, 0xb3, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, - 0x00, 0x96, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8d, 0x01, 0x8a, 0x02, 0x88, - 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7e, - 0x05, 0x7c, 0x06, 0x7c, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, - 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, - 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, - 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, - 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, - 0x00, 0x29, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x42, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x47, - 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, - 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x45, 0x0b, 0x41, 0x0e, 0x3e, 0x10, 0x3a, 0x11, - 0x38, 0x14, 0x35, 0x16, 0x33, 0x18, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, - 0x2e, 0x20, 0x2d, 0x21, 0x2b, 0x22, 0x2b, 0x24, 0x2a, 0x25, 0x2a, 0x27, - 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2c, 0x00, 0xb3, 0x00, 0xae, - 0x00, 0xa9, 0x00, 0xa4, 0x00, 0xa0, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x94, - 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x01, 0x89, 0x02, 0x87, 0x02, 0x86, - 0x02, 0x84, 0x03, 0x82, 0x03, 0x80, 0x04, 0x7f, 0x05, 0x7e, 0x05, 0x7c, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, - 0x00, 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, - 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, - 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, - 0x09, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x42, - 0x3d, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x4a, 0x36, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x46, 0x0b, 0x42, 0x0d, 0x3e, 0x10, 0x3b, 0x11, 0x38, 0x13, 0x36, 0x16, - 0x34, 0x17, 0x32, 0x19, 0x30, 0x1b, 0x30, 0x1d, 0x2e, 0x1e, 0x2d, 0x20, - 0x2d, 0x22, 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x25, 0x2a, 0x28, 0x28, 0x28, - 0x28, 0x2a, 0x28, 0x2c, 0x00, 0xb3, 0x00, 0xae, 0x00, 0xaa, 0x00, 0xa5, - 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x96, 0x00, 0x93, 0x00, 0x90, - 0x00, 0x8d, 0x01, 0x8b, 0x02, 0x89, 0x02, 0x87, 0x02, 0x85, 0x02, 0x83, - 0x03, 0x82, 0x03, 0x80, 0x04, 0x7f, 0x05, 0x7e, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, - 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, - 0x00, 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, - 0x00, 0x28, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, - 0x3c, 0x00, 0x31, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x00, - 0x00, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x45, 0x3a, 0x46, - 0x39, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x34, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x46, 0x0b, 0x42, 0x0d, - 0x3e, 0x0f, 0x3c, 0x11, 0x39, 0x13, 0x37, 0x15, 0x34, 0x16, 0x33, 0x18, - 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2e, 0x20, 0x2d, 0x20, 0x2c, 0x22, - 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x26, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, - 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9e, - 0x00, 0x9a, 0x00, 0x97, 0x00, 0x94, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, - 0x01, 0x8a, 0x02, 0x88, 0x02, 0x86, 0x02, 0x85, 0x02, 0x83, 0x03, 0x82, - 0x03, 0x80, 0x04, 0x7f, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb2, 0x00, 0x6f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0xa2, 0x00, - 0x4f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x7f, 0x00, 0x3c, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x16, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xd6, 0x00, 0xc6, 0x00, 0x9c, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, - 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, 0x00, - 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, - 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, - 0x00, 0x1b, 0x00, 0x17, 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, - 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, - 0x00, 0x15, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, - 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, - 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, - 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, - 0x00, 0x09, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, - 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, - 0x00, 0x19, 0x00, 0x16, 0x00, 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, - 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, - 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, - 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, - 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, - 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, - 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, - 0x00, 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, - 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, - 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, - 0x00, 0x0c, 0x00, 0x09, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, - 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, - 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1c, - 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x24, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x04, 0x11, 0x09, 0x01, 0x11, 0x00, 0x17, - 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x61, 0x01, 0x23, 0x03, 0x03, 0x09, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1a, - 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, - 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x1c, - 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x21, - 0x00, 0x27, 0x00, 0x2b, 0x00, 0x28, 0x00, 0x21, 0x00, 0x1d, 0x00, 0x1e, - 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x39, 0x01, 0x0f, 0x07, 0x02, 0x0c, 0x00, 0x12, 0x00, 0x17, - 0x00, 0x19, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, - 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x9f, 0x00, 0x5f, 0x00, - 0x1f, 0x00, 0x04, 0x01, 0x00, 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, - 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, - 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, - 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, - 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x34, 0x00, 0x27, 0x00, 0x0f, 0x00, 0x1a, - 0x00, 0x1c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, - 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x87, 0x00, - 0x32, 0x00, 0x19, 0x05, 0x0b, 0x09, 0x03, 0x0b, 0x00, 0x0e, 0x00, 0x12, - 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, - 0x00, 0x1c, 0x00, 0x1c, 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, - 0x16, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x12, - 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x0e, 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, - 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, - 0x00, 0x32, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0e, - 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, - 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xcd, 0x00, 0xaf, 0x00, 0x6e, 0x00, 0x31, 0x00, - 0x1e, 0x04, 0x12, 0x07, 0x0a, 0x09, 0x04, 0x0a, 0x00, 0x0b, 0x00, 0x0f, - 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, - 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, - 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, - 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x0c, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, - 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x2c, 0x00, 0x28, - 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, - 0x00, 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, - 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xd4, 0x00, 0xc1, 0x00, 0x93, 0x00, 0x5f, 0x00, 0x30, 0x00, 0x22, 0x03, - 0x17, 0x06, 0x0f, 0x07, 0x0a, 0x09, 0x05, 0x0a, 0x02, 0x0b, 0x00, 0x0c, - 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x15, 0xd7, 0x00, 0xc9, 0x00, - 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, 0x00, - 0x14, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x09, - 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, - 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, - 0x00, 0x18, 0x00, 0x19, 0x00, 0x24, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0e, - 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, - 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0xcb, 0x00, - 0xaa, 0x00, 0x7f, 0x00, 0x55, 0x00, 0x30, 0x00, 0x24, 0x02, 0x1b, 0x05, - 0x14, 0x06, 0x0e, 0x08, 0x09, 0x09, 0x06, 0x0a, 0x03, 0x0b, 0x00, 0x0b, - 0x00, 0x0d, 0x00, 0x0f, 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, - 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, 0x00, 0x28, 0x00, 0x1c, 0x00, - 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, - 0x00, 0x0e, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, - 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, - 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xb8, 0x00, 0x96, 0x00, - 0x71, 0x00, 0x4e, 0x00, 0x30, 0x00, 0x26, 0x02, 0x1e, 0x04, 0x17, 0x06, - 0x12, 0x07, 0x0d, 0x08, 0x09, 0x09, 0x06, 0x0a, 0x04, 0x0a, 0x01, 0x0b, - 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, - 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, - 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, - 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x1e, - 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, - 0x00, 0x13, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdb, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0xa6, 0x00, 0x87, 0x00, 0x67, 0x00, - 0x4a, 0x00, 0x30, 0x00, 0x27, 0x02, 0x20, 0x03, 0x1a, 0x05, 0x14, 0x06, - 0x10, 0x07, 0x0c, 0x08, 0x09, 0x09, 0x07, 0x0a, 0xdc, 0x00, 0xd7, 0x00, - 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, 0x00, - 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, - 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, - 0x00, 0x10, 0x00, 0x11, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, - 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, - 0xc8, 0x00, 0xb2, 0x00, 0x97, 0x00, 0x7b, 0x00, 0x60, 0x00, 0x46, 0x00, - 0x30, 0x00, 0x28, 0x01, 0x21, 0x03, 0x1c, 0x04, 0x17, 0x06, 0x13, 0x07, - 0x0f, 0x08, 0x0c, 0x08, 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, - 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x50, 0x00, - 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, - 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, - 0x00, 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, - 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xba, 0x00, - 0xa4, 0x00, 0x8b, 0x00, 0x72, 0x00, 0x5a, 0x00, 0x44, 0x00, 0x2f, 0x00, - 0x29, 0x01, 0x23, 0x03, 0x1e, 0x04, 0x19, 0x05, 0x15, 0x06, 0x11, 0x07, - 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, - 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, - 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x05, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x1e, - 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, - 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, - 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xd9, 0x00, 0xd0, 0x00, 0xc0, 0x00, 0xad, 0x00, 0x98, 0x00, - 0x81, 0x00, 0x6b, 0x00, 0x56, 0x00, 0x42, 0x00, 0x2f, 0x00, 0x29, 0x01, - 0x24, 0x02, 0x1f, 0x04, 0x1b, 0x05, 0x17, 0x06, 0xdd, 0x00, 0xdb, 0x00, - 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, 0x00, - 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, - 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, - 0x00, 0x07, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, - 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, - 0xd2, 0x00, 0xc5, 0x00, 0xb5, 0x00, 0xa2, 0x00, 0x8e, 0x00, 0x79, 0x00, - 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x2a, 0x01, 0x25, 0x02, - 0x20, 0x03, 0x1c, 0x04, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, - 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, 0x00, 0x86, 0x00, 0x78, 0x00, - 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, - 0x00, 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, - 0xbb, 0x00, 0xaa, 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x60, 0x00, - 0x4f, 0x00, 0x3e, 0x00, 0x2f, 0x00, 0x2a, 0x01, 0x26, 0x02, 0x21, 0x03, - 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, - 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, - 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, - 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb1, 0x00, - 0xa0, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, 0x00, - 0x3d, 0x00, 0x2f, 0x00, 0x2b, 0x01, 0x26, 0x02, 0xde, 0x00, 0xdc, 0x00, - 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, 0x00, - 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, - 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, - 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, - 0xd7, 0x00, 0xce, 0x00, 0xc3, 0x00, 0xb6, 0x00, 0xa8, 0x00, 0x98, 0x00, - 0x88, 0x00, 0x78, 0x00, 0x68, 0x00, 0x59, 0x00, 0x4a, 0x00, 0x3c, 0x00, - 0x2f, 0x00, 0x2b, 0x01, 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, - 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, 0x00, 0xa0, 0x00, 0x94, 0x00, - 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, - 0x00, 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, - 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd0, 0x00, - 0xc7, 0x00, 0xbb, 0x00, 0xae, 0x00, 0xa0, 0x00, 0x91, 0x00, 0x82, 0x00, - 0x73, 0x00, 0x64, 0x00, 0x56, 0x00, 0x48, 0x00, 0x3b, 0x00, 0x2f, 0x00, - 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, - 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, - 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x6f, 0x00, - 0xb2, 0x00, 0xc8, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xd9, 0x00, 0xda, 0x00, - 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xde, 0x00, 0x4c, 0x04, 0x87, 0x00, 0xbc, 0x00, 0xcd, 0x00, - 0xd4, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x76, 0x00, - 0x9b, 0x00, 0xac, 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xda, 0x00, - 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xde, 0x00, 0x61, 0x01, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, - 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x6f, 0x00, 0xa2, 0x00, - 0xb9, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, - 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, - 0x11, 0x09, 0x39, 0x01, 0x87, 0x00, 0xaf, 0x00, 0xc1, 0x00, 0xcb, 0x00, - 0xd1, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, - 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x5b, 0x00, 0x75, 0x00, 0x96, 0x00, - 0xab, 0x00, 0xc0, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, - 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, - 0x23, 0x03, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, - 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, - 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x9c, 0x00, - 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, - 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x01, 0x11, 0x0f, 0x07, - 0x32, 0x00, 0x6e, 0x00, 0x93, 0x00, 0xaa, 0x00, 0xb8, 0x00, 0xc1, 0x00, - 0xc8, 0x00, 0xcc, 0x00, 0xd0, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd6, 0x00, - 0xd7, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9b, 0x00, 0x75, 0x00, 0x2b, 0x00, 0x5b, 0x00, 0x7f, 0x00, 0x9b, 0x00, - 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, - 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x03, 0x09, 0x1f, 0x00, - 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, 0x00, - 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, - 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, - 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, - 0xca, 0x00, 0xcd, 0x00, 0x00, 0x17, 0x02, 0x0c, 0x19, 0x05, 0x31, 0x00, - 0x5f, 0x00, 0x7f, 0x00, 0x96, 0x00, 0xa6, 0x00, 0xb2, 0x00, 0xba, 0x00, - 0xc0, 0x00, 0xc5, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x96, 0x00, - 0x5b, 0x00, 0x13, 0x00, 0x41, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, - 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, - 0xca, 0x00, 0xcd, 0x00, 0x00, 0x13, 0x04, 0x01, 0x32, 0x00, 0x5f, 0x00, - 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, 0x00, - 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, - 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, - 0x00, 0x1b, 0x00, 0x12, 0x0b, 0x09, 0x1e, 0x04, 0x30, 0x00, 0x55, 0x00, - 0x71, 0x00, 0x87, 0x00, 0x97, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb5, 0x00, - 0xbb, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x7f, 0x00, 0x41, 0x00, - 0x03, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, - 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, - 0x00, 0x18, 0x00, 0x0a, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, - 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, - 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, - 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x1c, 0x00, 0x17, - 0x03, 0x0b, 0x12, 0x07, 0x22, 0x03, 0x30, 0x00, 0x4e, 0x00, 0x67, 0x00, - 0x7b, 0x00, 0x8b, 0x00, 0x98, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, - 0xb6, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xcd, 0x00, 0xc0, 0x00, 0x9b, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x02, 0x00, - 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, - 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x1a, 0x00, 0x11, - 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, 0x00, - 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, - 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, - 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, - 0x99, 0x00, 0xa0, 0x00, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x0e, 0x0a, 0x09, - 0x17, 0x06, 0x24, 0x02, 0x30, 0x00, 0x4a, 0x00, 0x60, 0x00, 0x72, 0x00, - 0x81, 0x00, 0x8e, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, 0x00, - 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x01, 0x00, 0x21, 0x00, - 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, - 0x99, 0x00, 0xa0, 0x00, 0x00, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x14, 0x00, - 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, 0x00, - 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x36, 0x00, - 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, - 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x04, 0x0a, 0x0f, 0x07, 0x1b, 0x05, - 0x26, 0x02, 0x30, 0x00, 0x46, 0x00, 0x5a, 0x00, 0x6b, 0x00, 0x79, 0x00, - 0x85, 0x00, 0x8f, 0x00, 0x98, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, - 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x36, 0x00, - 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, - 0x00, 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, - 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, - 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, - 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x15, 0x00, 0x0b, 0x0a, 0x09, 0x14, 0x06, 0x1e, 0x04, 0x27, 0x02, - 0x30, 0x00, 0x44, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x7e, 0x00, - 0x88, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, - 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, - 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x1d, 0x00, 0x1a, - 0x00, 0x0f, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, 0x00, - 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, - 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, - 0x5c, 0x00, 0x68, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0f, - 0x05, 0x0a, 0x0e, 0x08, 0x17, 0x06, 0x20, 0x03, 0x28, 0x01, 0x2f, 0x00, - 0x42, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6d, 0x00, 0x78, 0x00, 0x82, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, - 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, - 0x5c, 0x00, 0x68, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, - 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, 0x00, - 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, - 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x02, 0x0b, 0x09, 0x09, - 0x12, 0x07, 0x1a, 0x05, 0x21, 0x03, 0x29, 0x01, 0x2f, 0x00, 0x40, 0x00, - 0x4f, 0x00, 0x5c, 0x00, 0x68, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, - 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, - 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, - 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x04, 0x00, 0x13, 0x00, - 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, - 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x1f, 0x00, 0x1d, - 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x06, 0x0a, 0x0d, 0x08, 0x14, 0x06, - 0x1c, 0x04, 0x23, 0x03, 0x29, 0x01, 0x2f, 0x00, 0x3e, 0x00, 0x4c, 0x00, - 0x59, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, - 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x0c, 0x00, 0x1b, 0x00, 0x29, 0x00, - 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, - 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x16, - 0x00, 0x0f, 0x03, 0x0b, 0x09, 0x09, 0x10, 0x07, 0x17, 0x06, 0x1e, 0x04, - 0x24, 0x02, 0x2a, 0x01, 0x2f, 0x00, 0x3d, 0x00, 0x4a, 0x00, 0x56, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, - 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, - 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x11, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, - 0x00, 0x05, 0x06, 0x00, 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, 0x00, - 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x11, 0x00, 0x0b, - 0x06, 0x0a, 0x0c, 0x08, 0x13, 0x07, 0x19, 0x05, 0x1f, 0x04, 0x25, 0x02, - 0x2a, 0x01, 0x2f, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, - 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, - 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x01, 0x00, - 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, - 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0x00, 0x1e, - 0x00, 0x1c, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x04, 0x0a, 0x09, 0x09, - 0x0f, 0x08, 0x15, 0x06, 0x1b, 0x05, 0x20, 0x03, 0x26, 0x02, 0x2b, 0x01, - 0x2f, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, - 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0x00, 0x1d, - 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x08, 0x00, 0x13, 0x00, - 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, - 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, - 0x00, 0x15, 0x00, 0x0f, 0x01, 0x0b, 0x07, 0x0a, 0x0c, 0x08, 0x11, 0x07, - 0x17, 0x06, 0x1c, 0x04, 0x21, 0x03, 0x26, 0x02, 0x2b, 0x01, 0x2f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, - 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, - 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, - 0x00, 0x0e, 0x00, 0x05, 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, 0x00, - 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x01, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, - 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, - 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x61, 0x01, 0x23, 0x03, 0x03, 0x09, 0x00, 0x13, - 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, - 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x03, 0x5f, 0x00, - 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, 0x00, - 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x04, 0x01, 0x00, 0x0a, 0x00, 0x11, - 0x00, 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, - 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x09, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, - 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, 0x00, 0xce, 0x00, 0xd1, 0x00, - 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x9f, 0x00, - 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0a, - 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, - 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x13, 0x04, 0x01, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, - 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, - 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, - 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x0a, - 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, 0x00, - 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, - 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, - 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x01, - 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x11, 0x06, 0x00, 0x25, 0x00, - 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, 0x00, 0x96, 0x00, 0xa2, 0x00, - 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd0, 0x00, - 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, 0x00, - 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x00, - 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, - 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, - 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, - 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, 0x00, - 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x18, - 0x00, 0x0a, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, 0x00, - 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, - 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, - 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, - 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, - 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, 0x00, 0x5f, 0x00, 0x6e, 0x00, - 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xd9, 0x00, - 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, 0x00, - 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, - 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x0b, 0x00, 0x1c, 0x00, - 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, - 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, - 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, 0x00, - 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x14, 0x00, 0x09, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, - 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, - 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, - 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, - 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, - 0x00, 0x01, 0x0c, 0x00, 0x1b, 0x00, 0x29, 0x00, 0x38, 0x00, 0x46, 0x00, - 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, - 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, 0x00, - 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, - 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, 0x05, 0x06, 0x00, - 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, - 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, - 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, 0x00, - 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, - 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x01, 0x00, 0x0d, 0x00, 0x19, 0x00, - 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, - 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, - 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, - 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, - 0x00, 0x0c, 0x00, 0x02, 0x08, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, - 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, - 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, 0x00, - 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, - 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, - 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, - 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, - 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, 0x00, - 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xb2, 0x00, 0x6f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0xa2, - 0x00, 0x4f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x7f, 0x00, 0x3c, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1a, 0x00, - 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xd6, 0x00, 0xc6, 0x00, 0x9c, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0e, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, - 0x00, 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, - 0x00, 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, - 0x00, 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, - 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, - 0x00, 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, - 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, - 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, - 0x00, 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, 0x00, - 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, - 0x00, 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, - 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, - 0x00, 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, - 0x00, 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, - 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, - 0x00, 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, - 0x00, 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, 0x00, - 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, - 0x00, 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, - 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, - 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, - 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, - 0x00, 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, - 0x00, 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, - 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x6f, - 0x00, 0xb2, 0x00, 0xc8, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xd9, 0x00, 0xda, - 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xde, 0x00, 0xa6, 0x00, 0x76, 0x00, 0x9b, 0x00, 0xac, - 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, - 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x4c, 0x00, 0x87, - 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd4, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdb, - 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, - 0x00, 0xde, 0x00, 0xde, 0x01, 0x61, 0x00, 0x9f, 0x00, 0xc5, 0x00, 0xd2, - 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x6f, 0x00, 0xa2, - 0x00, 0xb9, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, - 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, - 0x00, 0x76, 0x00, 0x5b, 0x00, 0x75, 0x00, 0x96, 0x00, 0xab, 0x00, 0xc0, - 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, - 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x11, 0x01, 0x39, 0x00, 0x87, 0x00, 0xaf, - 0x00, 0xc1, 0x00, 0xcb, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, - 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, - 0x03, 0x23, 0x00, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, - 0x00, 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, - 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x9c, - 0x00, 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, - 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x9b, 0x00, 0x75, - 0x00, 0x2b, 0x00, 0x5b, 0x00, 0x7f, 0x00, 0x9b, 0x00, 0xae, 0x00, 0xb9, - 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, - 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x11, 0x01, 0x07, 0x0f, 0x00, 0x32, 0x00, 0x6e, 0x00, 0x93, 0x00, 0xaa, - 0x00, 0xb8, 0x00, 0xc1, 0x00, 0xc8, 0x00, 0xcc, 0x00, 0xd0, 0x00, 0xd2, - 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x09, 0x03, 0x00, 0x1f, - 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, - 0x00, 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, - 0x00, 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, - 0x00, 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, - 0x00, 0xca, 0x00, 0xcd, 0x00, 0xac, 0x00, 0x96, 0x00, 0x5b, 0x00, 0x13, - 0x00, 0x41, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, 0xa5, 0x00, 0xb0, - 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0xca, 0x00, 0xcd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x0c, 0x02, - 0x05, 0x19, 0x00, 0x31, 0x00, 0x5f, 0x00, 0x7f, 0x00, 0x96, 0x00, 0xa6, - 0x00, 0xb2, 0x00, 0xba, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc9, 0x00, 0xcc, - 0x00, 0xce, 0x00, 0xd0, 0x13, 0x00, 0x01, 0x04, 0x00, 0x32, 0x00, 0x5f, - 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, - 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, - 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, - 0x00, 0xbc, 0x00, 0xab, 0x00, 0x7f, 0x00, 0x41, 0x00, 0x03, 0x00, 0x2f, - 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, 0xa0, 0x00, 0xa9, - 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x09, 0x0b, 0x04, 0x1e, - 0x00, 0x30, 0x00, 0x55, 0x00, 0x71, 0x00, 0x87, 0x00, 0x97, 0x00, 0xa4, - 0x00, 0xad, 0x00, 0xb5, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc7, - 0x18, 0x00, 0x0a, 0x00, 0x00, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, - 0x00, 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, - 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, - 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xcd, 0x00, 0xc0, - 0x00, 0x9b, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x27, 0x00, 0x47, - 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, 0x9c, 0x00, 0xa4, - 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x03, 0x07, 0x12, 0x03, 0x22, 0x00, 0x30, - 0x00, 0x4e, 0x00, 0x67, 0x00, 0x7b, 0x00, 0x8b, 0x00, 0x98, 0x00, 0xa2, - 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, 0x00, 0xbb, 0x1a, 0x00, 0x11, 0x00, - 0x00, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, - 0x00, 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, - 0x00, 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, - 0x00, 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, - 0x00, 0x99, 0x00, 0xa0, 0x00, 0xd9, 0x00, 0xcd, 0x00, 0xae, 0x00, 0x82, - 0x00, 0x54, 0x00, 0x27, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, 0x55, - 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x99, 0x00, 0xa0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x19, 0x00, - 0x0e, 0x00, 0x09, 0x0a, 0x06, 0x17, 0x02, 0x24, 0x00, 0x30, 0x00, 0x4a, - 0x00, 0x60, 0x00, 0x72, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x98, 0x00, 0xa0, - 0x00, 0xa8, 0x00, 0xae, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x00, 0x00, 0x14, - 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, - 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x36, - 0x00, 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, - 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, 0x6f, 0x00, 0x47, - 0x00, 0x21, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x36, 0x00, 0x4b, 0x00, 0x5d, - 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x0a, 0x04, - 0x07, 0x0f, 0x05, 0x1b, 0x02, 0x26, 0x00, 0x30, 0x00, 0x46, 0x00, 0x5a, - 0x00, 0x6b, 0x00, 0x79, 0x00, 0x85, 0x00, 0x8f, 0x00, 0x98, 0x00, 0xa0, - 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x36, - 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, - 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, - 0x00, 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0xdb, 0x00, 0xd4, - 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, 0x3d, 0x00, 0x1d, - 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, 0x55, 0x00, 0x64, - 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x1c, 0x00, 0x15, 0x00, 0x0b, 0x00, 0x09, 0x0a, 0x06, 0x14, - 0x04, 0x1e, 0x02, 0x27, 0x00, 0x30, 0x00, 0x44, 0x00, 0x56, 0x00, 0x65, - 0x00, 0x72, 0x00, 0x7e, 0x00, 0x88, 0x00, 0x91, 0x1d, 0x00, 0x1a, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, - 0x00, 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, - 0x00, 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, - 0x00, 0x5c, 0x00, 0x68, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc7, 0x00, 0xb0, - 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, 0x19, 0x00, 0x00, - 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, 0x5c, 0x00, 0x68, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, - 0x17, 0x00, 0x0f, 0x00, 0x0a, 0x05, 0x08, 0x0e, 0x06, 0x17, 0x03, 0x20, - 0x01, 0x28, 0x00, 0x2f, 0x00, 0x42, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6d, - 0x00, 0x78, 0x00, 0x82, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x00, - 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, - 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, - 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, 0xa0, 0x00, 0x84, - 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, 0x00, 0x00, 0x15, - 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, - 0x0b, 0x02, 0x09, 0x09, 0x07, 0x12, 0x05, 0x1a, 0x03, 0x21, 0x01, 0x29, - 0x00, 0x2f, 0x00, 0x40, 0x00, 0x4f, 0x00, 0x5c, 0x00, 0x68, 0x00, 0x73, - 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x04, 0x00, 0x13, - 0x00, 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, - 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0xdd, 0x00, 0xd9, - 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, 0x78, 0x00, 0x5d, - 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x24, - 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x0a, 0x06, - 0x08, 0x0d, 0x06, 0x14, 0x04, 0x1c, 0x03, 0x23, 0x01, 0x29, 0x00, 0x2f, - 0x00, 0x3e, 0x00, 0x4c, 0x00, 0x59, 0x00, 0x64, 0x1e, 0x00, 0x1c, 0x00, - 0x16, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x29, - 0x00, 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, - 0x00, 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, - 0x00, 0x22, 0x00, 0x30, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc3, - 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, 0x55, 0x00, 0x3e, - 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x11, 0x00, 0x22, 0x00, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1b, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x09, 0x09, 0x07, 0x10, - 0x06, 0x17, 0x04, 0x1e, 0x02, 0x24, 0x01, 0x2a, 0x00, 0x2f, 0x00, 0x3d, - 0x00, 0x4a, 0x00, 0x56, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, - 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, 0xb7, 0x00, 0xa4, - 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, 0x39, 0x00, 0x24, - 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, - 0x11, 0x00, 0x0b, 0x00, 0x0a, 0x06, 0x08, 0x0c, 0x07, 0x13, 0x05, 0x19, - 0x04, 0x1f, 0x02, 0x25, 0x01, 0x2a, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x48, - 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x01, - 0x00, 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, - 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xdd, 0x00, 0xdb, - 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x99, 0x00, 0x85, - 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, - 0x0a, 0x04, 0x09, 0x09, 0x08, 0x0f, 0x06, 0x15, 0x05, 0x1b, 0x03, 0x20, - 0x02, 0x26, 0x01, 0x2b, 0x00, 0x2f, 0x00, 0x3b, 0x1f, 0x00, 0x1d, 0x00, - 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x13, - 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, - 0x00, 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcd, - 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, 0x7b, 0x00, 0x68, - 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0b, 0x01, 0x0a, 0x07, - 0x08, 0x0c, 0x07, 0x11, 0x06, 0x17, 0x04, 0x1c, 0x03, 0x21, 0x02, 0x26, - 0x01, 0x2b, 0x00, 0x2f, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, - 0x0e, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, - 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1c, 0x00, 0x1e, 0x00, 0x1e, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x04, 0x4c, 0x09, 0x11, - 0x11, 0x01, 0x17, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, - 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x24, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x01, 0x61, 0x03, 0x23, - 0x09, 0x03, 0x13, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, - 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x0f, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, - 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x87, 0x01, 0x39, 0x07, 0x0f, 0x0c, 0x02, - 0x12, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, - 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x21, 0x00, - 0x27, 0x00, 0x2b, 0x00, 0x28, 0x00, 0x21, 0x00, 0x1d, 0x00, 0x1e, 0x00, - 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x01, 0x04, - 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, - 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, - 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, - 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, - 0x00, 0xbc, 0x00, 0x87, 0x00, 0x32, 0x05, 0x19, 0x09, 0x0b, 0x0b, 0x03, - 0x0e, 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, - 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x27, 0x00, 0x0f, 0x00, 0x1a, 0x00, - 0x1c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, - 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, - 0x00, 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, - 0x03, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, - 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, - 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, - 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0xcd, 0x00, 0xaf, - 0x00, 0x6e, 0x00, 0x31, 0x04, 0x1e, 0x07, 0x12, 0x09, 0x0a, 0x0a, 0x04, - 0x0b, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, - 0x18, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0e, 0x00, - 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, - 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0xd2, 0x00, 0xbc, - 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, - 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, - 0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, 0x00, - 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, - 0x1a, 0x00, 0x1b, 0x00, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0x93, 0x00, 0x5f, - 0x00, 0x30, 0x03, 0x22, 0x06, 0x17, 0x07, 0x0f, 0x09, 0x0a, 0x0a, 0x05, - 0x0b, 0x02, 0x0c, 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x15, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x28, 0x00, - 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, 0x00, - 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, - 0x1a, 0x00, 0x1b, 0x00, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, - 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, - 0x00, 0x04, 0x01, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, - 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, - 0x00, 0xd8, 0x00, 0xcb, 0x00, 0xaa, 0x00, 0x7f, 0x00, 0x55, 0x00, 0x30, - 0x02, 0x24, 0x05, 0x1b, 0x06, 0x14, 0x08, 0x0e, 0x09, 0x09, 0x0a, 0x06, - 0x0b, 0x03, 0x0b, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0e, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, - 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, - 0x00, 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, - 0x00, 0x49, 0x00, 0x36, 0x00, 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, - 0x00, 0x06, 0x00, 0x01, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, - 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0xda, 0x00, 0xd1, - 0x00, 0xb8, 0x00, 0x96, 0x00, 0x71, 0x00, 0x4e, 0x00, 0x30, 0x02, 0x26, - 0x04, 0x1e, 0x06, 0x17, 0x07, 0x12, 0x08, 0x0d, 0x09, 0x09, 0x0a, 0x06, - 0x0a, 0x04, 0x0b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, - 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0xdb, 0x00, 0xd5, - 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, - 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, - 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, - 0x13, 0x00, 0x14, 0x00, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0xa6, - 0x00, 0x87, 0x00, 0x67, 0x00, 0x4a, 0x00, 0x30, 0x02, 0x27, 0x03, 0x20, - 0x05, 0x1a, 0x06, 0x14, 0x07, 0x10, 0x08, 0x0c, 0x09, 0x09, 0x0a, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, - 0x13, 0x00, 0x14, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, - 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, - 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, 0x00, - 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc8, 0x00, 0xb2, 0x00, 0x97, 0x00, 0x7b, - 0x00, 0x60, 0x00, 0x46, 0x00, 0x30, 0x01, 0x28, 0x03, 0x21, 0x04, 0x1c, - 0x06, 0x17, 0x07, 0x13, 0x08, 0x0f, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, - 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, 0x00, - 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, - 0x00, 0x82, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, - 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, - 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0xdc, 0x00, 0xd8, - 0x00, 0xcc, 0x00, 0xba, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x72, 0x00, 0x5a, - 0x00, 0x44, 0x00, 0x2f, 0x01, 0x29, 0x03, 0x23, 0x04, 0x1e, 0x05, 0x19, - 0x06, 0x15, 0x07, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, - 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, - 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0xdd, 0x00, 0xda, - 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, - 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, - 0x00, 0x2a, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x0a, 0x00, 0x0c, 0x00, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd0, 0x00, 0xc0, - 0x00, 0xad, 0x00, 0x98, 0x00, 0x81, 0x00, 0x6b, 0x00, 0x56, 0x00, 0x42, - 0x00, 0x2f, 0x01, 0x29, 0x02, 0x24, 0x04, 0x1f, 0x05, 0x1b, 0x06, 0x17, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, 0x00, - 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x0a, 0x00, 0x0c, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, - 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, - 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, - 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xc5, 0x00, 0xb5, 0x00, 0xa2, - 0x00, 0x8e, 0x00, 0x79, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, - 0x01, 0x2a, 0x02, 0x25, 0x03, 0x20, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, - 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, - 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, - 0x00, 0xa4, 0x00, 0x95, 0x00, 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, - 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0xdd, 0x00, 0xdb, - 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xaa, 0x00, 0x98, 0x00, 0x85, - 0x00, 0x72, 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x2f, 0x01, 0x2a, - 0x02, 0x26, 0x03, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, - 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0xde, 0x00, 0xdc, - 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, - 0x00, 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, - 0x00, 0x4c, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x04, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, - 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, - 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3d, 0x00, 0x2f, 0x01, 0x2b, 0x02, 0x26, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, 0x00, - 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x04, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, - 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, - 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc3, 0x00, 0xb6, - 0x00, 0xa8, 0x00, 0x98, 0x00, 0x88, 0x00, 0x78, 0x00, 0x68, 0x00, 0x59, - 0x00, 0x4a, 0x00, 0x3c, 0x00, 0x2f, 0x01, 0x2b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, - 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, - 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, - 0x00, 0xb7, 0x00, 0xab, 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, - 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, - 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbb, 0x00, 0xae, 0x00, 0xa0, - 0x00, 0x91, 0x00, 0x82, 0x00, 0x73, 0x00, 0x64, 0x00, 0x56, 0x00, 0x48, - 0x00, 0x3b, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, - 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, - 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, - 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, - 0x00, 0x68, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x61, 0x03, 0x23, 0x09, 0x03, 0x13, 0x00, - 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, - 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x61, 0x00, 0x9f, - 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, - 0x00, 0xde, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x01, 0x04, 0x0a, 0x00, 0x11, 0x00, - 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, - 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x5f, 0x00, 0x9f, 0x00, 0xbc, - 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, - 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x9f, - 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x03, 0x00, 0x0a, 0x00, - 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, - 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x03, 0x00, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, - 0x00, 0xc3, 0x00, 0xc9, 0x00, 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, - 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, - 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x05, 0x00, - 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x04, - 0x00, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, - 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, - 0x00, 0xd3, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, - 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, 0x00, 0x04, 0x01, 0x00, - 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x16, 0x00, 0x3d, - 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, - 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd0, - 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, - 0x00, 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, - 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1a, 0x00, 0x11, 0x00, 0x00, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, - 0x00, 0x76, 0x00, 0x88, 0x00, 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, - 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, - 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, - 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x15, 0x00, - 0x03, 0x00, 0x00, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, - 0x00, 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, - 0x00, 0xb7, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, - 0x00, 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, - 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x09, - 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, - 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xd9, - 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, - 0x00, 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, - 0x00, 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x28, - 0x00, 0x3c, 0x00, 0x4e, 0x00, 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, - 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, - 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, - 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, - 0x12, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, - 0x00, 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, - 0x00, 0x94, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, - 0x00, 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, - 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x00, - 0x00, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, - 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, - 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, - 0x00, 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, - 0x00, 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x0c, - 0x00, 0x1b, 0x00, 0x29, 0x00, 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, - 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, - 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, - 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1d, 0x00, - 0x18, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x21, - 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, - 0x00, 0x73, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, - 0x00, 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, - 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, - 0x09, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, - 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, - 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, - 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, - 0x00, 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x00, - 0x00, 0x08, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, - 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, - 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, - 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x0e, - 0x00, 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, - 0x00, 0x56, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; - -/* clang-format on */ +extern const unsigned char areaTexBytes[]; #define SEARCHTEX_WIDTH 64 #define SEARCHTEX_HEIGHT 16 #define SEARCHTEX_PITCH SEARCHTEX_WIDTH #define SEARCHTEX_SIZE (SEARCHTEX_HEIGHT * SEARCHTEX_PITCH) -/* Don't re-wrap large data definitions. */ -/* clang-format off */ - /** * Stored in R8 format. Load it in the following format: * - DX10: DXGI_FORMAT_R8_UNORM */ -static const unsigned char searchTexBytes[] = { - 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, - 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, - 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, - 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, - 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, - 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, - 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; - -/* clang-format off */ +extern const unsigned char searchTexBytes[]; From 51ea08487bf8debab4163f1bfe3f2495c50bc403 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 13 Oct 2021 16:22:17 +0200 Subject: [PATCH 0755/1500] Fix T92185: GPencil memory leak removing stroke from python The API was not removing the weights, points and traingulation data. Only it was removing the stroke itself. --- source/blender/makesrna/intern/rna_gpencil.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 2bce5f3f4b3..3f380cd1830 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -906,7 +906,8 @@ static void rna_GPencil_stroke_remove(ID *id, return; } - BLI_freelinkN(&frame->strokes, stroke); + BLI_remlink(&frame->strokes, stroke); + BKE_gpencil_free_stroke(stroke); RNA_POINTER_INVALIDATE(stroke_ptr); DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); From 78445ebd5fd99444741abee1406aa8d318b5a269 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Wed, 13 Oct 2021 09:42:10 -0500 Subject: [PATCH 0756/1500] Geometry Nodes: Rotate Instances Node Adds a node that can rotate each of a geometry's instances in global (to the modifier object) or local space (of each point) by a specified angle around a pivot point. In the future, separating the local-global choice for the pivot and the rotation might be useful. However, for now the node is kept simple. Differential Revision: https://developer.blender.org/D12682 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_geo_rotate_instances.cc | 99 +++++++++++++++++++ 7 files changed, 105 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index dfe33199f28..11d1b1b2686 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -683,6 +683,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeSeparateGeometry"), NodeItem("GeometryNodeSetPosition"), NodeItem("GeometryNodeRealizeInstances"), + NodeItem("GeometryNodeRotateInstances"), NodeItem("GeometryNodeScaleInstances"), NodeItem("GeometryNodeTranslateInstances"), ]), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 4c1c49555b8..b83d8d0e5e6 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1534,6 +1534,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_SET_MATERIAL_INDEX 1119 #define GEO_NODE_TRANSLATE_INSTANCES 1120 #define GEO_NODE_SCALE_INSTANCES 1121 +#define GEO_NODE_ROTATE_INSTANCES 1122 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 283e28ed54f..16435ea6c58 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5809,6 +5809,7 @@ static void registerGeometryNodes() register_node_type_geo_proximity(); register_node_type_geo_raycast(); register_node_type_geo_realize_instances(); + register_node_type_geo_rotate_instances(); register_node_type_geo_sample_texture(); register_node_type_geo_scale_instances(); register_node_type_geo_separate_components(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index e543db92b9f..19740a973a2 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -253,6 +253,7 @@ set(SRC geometry/nodes/node_geo_points_to_volume.cc geometry/nodes/node_geo_proximity.cc geometry/nodes/node_geo_realize_instances.cc + geometry/nodes/node_geo_rotate_instances.cc geometry/nodes/node_geo_scale_instances.cc geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_separate_geometry.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 3b0816300f5..f9b3c5ee09a 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -129,6 +129,7 @@ void register_node_type_geo_points_to_volume(void); void register_node_type_geo_proximity(void); void register_node_type_geo_raycast(void); void register_node_type_geo_realize_instances(void); +void register_node_type_geo_rotate_instances(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_scale_instances(void); void register_node_type_geo_select_by_handle_type(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 010327a1ac1..813e97be044 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -381,6 +381,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", Poin DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "") DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") +DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "") DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc new file mode 100644 index 00000000000..09d92d7fa1e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -0,0 +1,99 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_rotate_instances_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Rotation").subtype(PROP_EULER).supports_field(); + b.add_input("Pivot Point").subtype(PROP_TRANSLATION).supports_field(); + b.add_input("Local Space").default_value(true).supports_field(); + b.add_output("Geometry"); +}; + +static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +{ + GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_POINT}; + const int domain_size = instances_component.instances_amount(); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(params.extract_input>("Selection")); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + fn::FieldEvaluator transforms_evaluator{field_context, &selection}; + transforms_evaluator.add(params.extract_input>("Rotation")); + transforms_evaluator.add(params.extract_input>("Pivot Point")); + transforms_evaluator.add(params.extract_input>("Local Space")); + transforms_evaluator.evaluate(); + const VArray &rotations = transforms_evaluator.get_evaluated(0); + const VArray &pivots = transforms_evaluator.get_evaluated(1); + const VArray &local_spaces = transforms_evaluator.get_evaluated(2); + + MutableSpan instance_transforms = instances_component.instance_transforms(); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i = selection[i_selection]; + const float3 pivot = pivots[i]; + float4x4 &instance_transform = instance_transforms[i]; + const float4x4 rotation_matrix = float4x4::from_loc_eul_scale( + {0, 0, 0}, rotations[i], {1, 1, 1}); + + if (local_spaces[i]) { + instance_transform *= float4x4::from_location(pivot); + instance_transform *= rotation_matrix; + instance_transform *= float4x4::from_location(-pivot); + } + else { + const float4x4 orgiginal_transform = instance_transform; + instance_transform = float4x4::from_location(pivot); + instance_transform *= rotation_matrix; + instance_transform *= float4x4::from_location(-pivot); + instance_transform *= orgiginal_transform; + } + } + }); +} + +static void geo_node_rotate_instances_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + if (geometry_set.has_instances()) { + InstancesComponent &instances = geometry_set.get_component_for_write(); + rotate_instances(params, instances); + } + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_rotate_instances() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ROTATE_INSTANCES, "Rotate Instances", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_rotate_instances_exec; + ntype.declare = blender::nodes::geo_node_rotate_instances_declare; + nodeRegisterType(&ntype); +} From 96876305e18a9206a1d266b11e3a7d61c32be41f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 13 Oct 2021 11:26:23 -0500 Subject: [PATCH 0757/1500] Geometry Nodes: Make attribute capture work on instances Previously the attribute capture node only worked on realized geometry, which was very confusing when other nodes worked on each individual instance. The realize instances node is the way to explicitly change between the two behaviors. Addresses T92155. Differential Revision: https://developer.blender.org/D12841 --- .../geometry/nodes/node_geo_attribute_capture.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 8cb7d81f432..7f12b816023 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -149,12 +149,14 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params) static const Array types = { GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; - for (const GeometryComponentType type : types) { - if (geometry_set.has(type)) { - GeometryComponent &component = geometry_set.get_component_for_write(type); - try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { + GeometryComponent &component = geometry_set.get_component_for_write(type); + try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + } } - } + }); GField output_field{ std::make_shared(std::move(anonymous_id), type)}; From 366cea95c5d95dccdf30fe15216a3b30174406d3 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 13 Oct 2021 11:34:29 -0500 Subject: [PATCH 0758/1500] Geometry Nodes: Fields version of edge split node This changes the edge split node to have a selection input, which is more aligned with the other design changes. This loses the ability to split edges based on an angle, but the edge angle can be added as a field input node in the future, which will make for a much more flexible system. Differential Revision: https://developer.blender.org/D12829 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/legacy/node_geo_edge_split.cc | 2 +- .../geometry/nodes/node_geo_edge_split.cc | 94 +++++++++++++++++++ 8 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_edge_split.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 11d1b1b2686..784a7b1eb56 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -143,6 +143,7 @@ def mesh_node_items(context): yield NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_legacy_poll) yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeEdgeSplit") yield NodeItem("GeometryNodeBoolean") yield NodeItem("GeometryNodeMeshSubdivide") yield NodeItem("GeometryNodePointsToVertices") diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index b83d8d0e5e6..19215e75d95 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1535,6 +1535,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_TRANSLATE_INSTANCES 1120 #define GEO_NODE_SCALE_INSTANCES 1121 #define GEO_NODE_ROTATE_INSTANCES 1122 +#define GEO_NODE_EDGE_SPLIT 1123 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 16435ea6c58..cb51249ac27 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5720,6 +5720,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_curve_reverse(); register_node_type_geo_legacy_select_by_handle_type(); register_node_type_geo_legacy_curve_subdivide(); + register_node_type_geo_legacy_edge_split(); register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 19740a973a2..f748c89c005 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -220,6 +220,7 @@ set(SRC geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_distribute_points_on_faces.cc + geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_curve_handles.cc geometry/nodes/node_geo_input_curve_tilt.cc geometry/nodes/node_geo_input_index.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index f9b3c5ee09a..d34acd6e9aa 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -40,6 +40,7 @@ void register_node_type_geo_legacy_curve_spline_type(void); void register_node_type_geo_legacy_curve_reverse(void); void register_node_type_geo_legacy_select_by_handle_type(void); void register_node_type_geo_legacy_curve_subdivide(void); +void register_node_type_geo_legacy_edge_split(void); void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_capture(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 813e97be044..b5640207d04 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -348,6 +348,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, " DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Trim Curve", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") +DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc index 2ea6516996d..d7e908edf61 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc @@ -78,7 +78,7 @@ static void geo_node_edge_split_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_edge_split() +void register_node_type_geo_legacy_edge_split() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc new file mode 100644 index 00000000000..fcc95d6430c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -0,0 +1,94 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_edge_split_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Mesh"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Mesh"); +} + +static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection) +{ + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = {0, 0, 0, 0}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + BMeshFromMeshParams params{}; + BM_mesh_bm_from_me(bm, &mesh, ¶ms); + + BM_mesh_elem_table_ensure(bm, BM_EDGE); + for (const int i : selection) { + BMEdge *edge = BM_edge_at_index(bm, i); + BM_elem_flag_enable(edge, BM_ELEM_TAG); + } + + BM_mesh_edgesplit(bm, false, true, false); + + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, &mesh); + BM_mesh_free(bm); + + BKE_mesh_normals_tag_dirty(result); + + return result; +} + +static void geo_node_edge_split_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Mesh"); + + const Field selection_field = params.extract_input>("Selection"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + return; + } + + const MeshComponent &mesh_component = *geometry_set.get_component_for_read(); + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; + const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + geometry_set.replace_mesh(mesh_edge_split(*mesh_component.get_for_read(), selection)); + }); + + params.set_output("Mesh", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_edge_split() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec; + ntype.declare = blender::nodes::geo_node_edge_split_declare; + nodeRegisterType(&ntype); +} From 98a62a5c088b18d5dd7d60e733c618d9c3db3a46 Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Wed, 13 Oct 2021 12:32:28 -0500 Subject: [PATCH 0759/1500] Geometry Nodes: Material Index - minor cleanup - Add a versioning comment - Indexes to Indices --- source/blender/blenloader/intern/versioning_300.c | 2 ++ .../nodes/geometry/nodes/node_geo_set_material_index.cc | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index db2bb73108b..e5b2536f123 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1746,6 +1746,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Update the idname for the Assign Material Node to SetMaterial */ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type != NTREE_GEOMETRY) { continue; diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index ebf6b43ae30..66ca0d5b979 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -41,12 +41,12 @@ static void set_material_index_in_component(GeometryComponent &component, selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - OutputAttribute_Typed indexes = component.attribute_try_get_for_output_only( + OutputAttribute_Typed indices = component.attribute_try_get_for_output_only( "material_index", ATTR_DOMAIN_FACE); fn::FieldEvaluator material_evaluator{field_context, &selection}; - material_evaluator.add_with_destination(index_field, indexes.varray()); + material_evaluator.add_with_destination(index_field, indices.varray()); material_evaluator.evaluate(); - indexes.save(); + indices.save(); } static void geo_node_set_material_index_exec(GeoNodeExecParams params) From d39cd851c0ca67dff975b1c447089d319ef0816f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 13 Oct 2021 18:48:28 +0200 Subject: [PATCH 0760/1500] Fix T89777 EEVEE: Contact Shadows causes wrong shading in Reflection Plane The planar reflections being rendered at the same resolution as the HiZ max buffer, do not need any uv correction during raytracing. However, the GTAO horizon buffer being at output resolution do need the uv factors in order to match the pixels visible on screen. To avoid many complication, we increase the size of the GTAO texture up to the hiz buffer size. This way, if planar reflections need GTAO the texture is big enough. We change the viewport of the GTAO framebuffer for the main view in order to not have to modify Uvs in many places. --- .../blender/draw/engines/eevee/eevee_effects.c | 7 ++++--- .../draw/engines/eevee/eevee_lightprobes.c | 6 ++++++ .../draw/engines/eevee/eevee_occlusion.c | 18 +++++++++++++++--- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index d5960ea57d5..87df5f11c80 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -94,6 +94,10 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, effects = stl->effects; + int div = 1 << MAX_SCREEN_BUFFERS_LOD_LEVEL; + effects->hiz_size[0] = divide_ceil_u(size_fs[0], div) * div; + effects->hiz_size[1] = divide_ceil_u(size_fs[1], div) * div; + effects->enabled_effects = 0; effects->enabled_effects |= (G.debug_value == 9) ? EFFECT_VELOCITY_BUFFER : 0; effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata); @@ -118,9 +122,6 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, /** * MinMax Pyramid */ - int div = 1 << MAX_SCREEN_BUFFERS_LOD_LEVEL; - effects->hiz_size[0] = divide_ceil_u(size_fs[0], div) * div; - effects->hiz_size[1] = divide_ceil_u(size_fs[1], div) * div; if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { /* Intel gpu seems to have problem rendering to only depth hiz_format */ diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 8598655477f..56227bc95ec 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -1203,6 +1203,8 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v return; } + float hiz_uv_scale_prev[2] = {UNPACK2(common_data->hiz_uv_scale)}; + /* Temporary Remove all planar reflections (avoid lag effect). */ common_data->prb_num_planar = 0; /* Turn off ssr to avoid black specular */ @@ -1212,6 +1214,9 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v common_data->ray_type = EEVEE_RAY_GLOSSY; common_data->ray_depth = 1.0f; + /* Planar reflections are rendered at the hiz resolution, so no need to scalling. */ + copy_v2_fl(common_data->hiz_uv_scale, 1.0f); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); /* Rendering happens here! */ @@ -1227,6 +1232,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v common_data->ssr_toggle = true; common_data->ssrefract_toggle = true; common_data->sss_toggle = true; + copy_v2_v2(common_data->hiz_uv_scale, hiz_uv_scale_prev); /* Prefilter for SSR */ if ((vedata->stl->effects->enabled_effects & EFFECT_SSR) != 0) { diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c index 4c2024a6f65..955cfd990ef 100644 --- a/source/blender/draw/engines/eevee/eevee_occlusion.c +++ b/source/blender/draw/engines/eevee/eevee_occlusion.c @@ -77,14 +77,14 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) common_data->ao_bounce_fac = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) ? 1.0f : 0.0f; effects->gtao_horizons_renderpass = DRW_texture_pool_query_2d( - fs_size[0], fs_size[1], GPU_RGBA8, &draw_engine_eevee_type); + UNPACK2(effects->hiz_size), GPU_RGBA8, &draw_engine_eevee_type); GPU_framebuffer_ensure_config( &fbl->gtao_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_renderpass)}); if (G.debug_value == 6) { effects->gtao_horizons_debug = DRW_texture_pool_query_2d( - fs_size[0], fs_size[1], GPU_RGBA8, &draw_engine_eevee_type); + UNPACK2(fs_size), GPU_RGBA8, &draw_engine_eevee_type); GPU_framebuffer_ensure_config( &fbl->gtao_debug_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_debug)}); @@ -188,20 +188,32 @@ void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) } } -void EEVEE_occlusion_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) +void EEVEE_occlusion_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_PassList *psl = vedata->psl; EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; + EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; if ((effects->enabled_effects & EFFECT_GTAO) != 0) { DRW_stats_group_start("GTAO Horizon Scan"); + /** NOTE(fclem): Kind of fragile. We need this to make sure everything lines up + * nicely during planar reflection. */ + if (common_data->ray_type != EEVEE_RAY_GLOSSY) { + const float *viewport_size = DRW_viewport_size_get(); + GPU_framebuffer_viewport_set(fbl->gtao_fb, 0, 0, UNPACK2(viewport_size)); + } + GPU_framebuffer_bind(fbl->gtao_fb); DRW_draw_pass(psl->ao_horizon_search); + if (common_data->ray_type != EEVEE_RAY_GLOSSY) { + GPU_framebuffer_viewport_reset(fbl->gtao_fb); + } + if (GPU_mip_render_workaround() || GPU_type_matches(GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_ANY)) { /* Fix dot corruption on intel HD5XX/HD6XX series. */ From 10abaf3ddf08fc891cb7b7ce98bb2166bb1b9f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 13 Oct 2021 20:51:15 +0200 Subject: [PATCH 0761/1500] Fix T88766 EEVEE: Missing glossy reflections with Shader to RGB & SSR is active. This was due to the shading evaluation being outdated inside the ShaderToRGBA glsl code. --- .../gpu_shader_material_shader_to_rgba.glsl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl index a5fdc7a2337..f0f2f79c60e 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl @@ -1,14 +1,22 @@ #ifndef VOLUMETRICS + +CLOSURE_EVAL_FUNCTION_DECLARE_1(node_shader_to_rgba, Glossy) + void node_shader_to_rgba(Closure cl, out vec4 outcol, out float outalpha) { vec4 spec_accum = vec4(0.0); if (ssrToggle && FLAG_TEST(cl.flag, CLOSURE_SSR_FLAG)) { - vec3 V = cameraVec(worldPosition); + CLOSURE_VARS_DECLARE_1(Glossy); + vec3 vN = normal_decode(cl.ssr_normal, viewCameraVec(viewPosition)); vec3 N = transform_direction(ViewMatrixInverse, vN); - float roughness = cl.ssr_data.a; - float roughnessSquared = max(1e-3, roughness * roughness); - fallback_cubemap(N, V, worldPosition, viewPosition, roughness, roughnessSquared, spec_accum); + + in_Glossy_0.N = N; /* Normalized during eval. */ + in_Glossy_0.roughness = cl.ssr_data.a; + + CLOSURE_EVAL_FUNCTION_1(node_shader_to_rgba, Glossy); + + spec_accum.rgb = out_Glossy_0.radiance; } outalpha = saturate(1.0 - avg(cl.transmittance)); From 1ae79b704a6f38adb1b5dfa35ed3f1e338a05f33 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 13 Oct 2021 14:28:05 -0500 Subject: [PATCH 0762/1500] Fix T92180: Curve subdivide incorrect result for poly splines The node shifted all new points forward in the spline, so the first point would appear to be removed. --- .../nodes/geometry/nodes/node_geo_curve_subdivide.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 99379ab7259..0d934108738 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -63,9 +63,9 @@ static void subdivide_attribute(Span src, for (const int i : range) { const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src[i]; - const float factor_delta = 1.0f / (cuts + 1.0f); + const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; for (const int cut : IndexRange(cuts)) { - const float factor = (cut + 1) * factor_delta; + const float factor = cut * factor_delta; dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]); } } @@ -75,9 +75,9 @@ static void subdivide_attribute(Span src, const int i = src_size - 1; const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src.last(); - const float factor_delta = 1.0f / (cuts + 1.0f); + const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; for (const int cut : IndexRange(cuts)) { - const float factor = (cut + 1) * factor_delta; + const float factor = cut * factor_delta; dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first()); } } From 988b9bc40ce846cdce05961315dc0e2c1dd95e4b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 13 Oct 2021 14:34:14 -0500 Subject: [PATCH 0763/1500] Fix T92192: Inconsistent curve circle primitive direction Switch sin and cosine so that the points in the circle have the same direction in both radius and points modes. --- .../nodes/geometry/nodes/node_geo_curve_primitive_circle.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index f5eb83ea4fd..efc2dff48c1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -132,7 +132,7 @@ static std::unique_ptr create_point_circle_curve( */ const float theta = theta_step * i; - positions[i] = center + r * cos(theta) * v1 + r * sin(theta) * v4; + positions[i] = center + r * sin(theta) * v1 + r * cos(theta) * v4; } spline->radii().fill(1.0f); From 91c33c8b99520b6b094265a826cd391929a81716 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 12 Oct 2021 17:05:56 -0300 Subject: [PATCH 0764/1500] Cleanup: Snap Context Refactor Move runtime parameters out of context creation. Not being able to choose another region and v3d limits the use of the snap API. --- source/blender/editors/curve/editcurve.c | 6 +- .../gizmo_library/gizmo_types/move3d_gizmo.c | 5 +- .../gizmo_library/gizmo_types/snap3d_gizmo.c | 18 +- .../editors/gpencil/gpencil_bake_animation.c | 3 +- source/blender/editors/gpencil/gpencil_edit.c | 3 +- source/blender/editors/gpencil/gpencil_mesh.c | 3 +- .../blender/editors/gpencil/gpencil_utils.c | 1 + .../editors/include/ED_gizmo_library.h | 2 - .../ED_transform_snap_object_context.h | 14 +- source/blender/editors/mesh/editmesh_utils.c | 6 +- .../editors/space_view3d/view3d_edit.c | 5 +- .../editors/space_view3d/view3d_gizmo_ruler.c | 6 +- .../space_view3d/view3d_navigate_walk.c | 5 +- .../editors/space_view3d/view3d_placement.c | 14 +- .../editors/transform/transform_snap.c | 9 +- .../editors/transform/transform_snap_object.c | 440 +++++++++--------- .../blender/makesrna/intern/rna_scene_api.c | 1 + 17 files changed, 278 insertions(+), 263 deletions(-) diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 9b43e23bd32..d4fdf46d8f4 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -5561,12 +5561,14 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (use_proj) { const float mval[2] = {UNPACK2(event->mval)}; - struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( - vc.scene, 0, vc.region, vc.v3d); + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create(vc.scene, + 0); ED_transform_snap_object_project_view3d( snap_context, vc.depsgraph, + vc.region, + vc.v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL, diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c index 68322ed56af..ec4837aec3c 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -289,6 +289,8 @@ static int gizmo_move_modal(bContext *C, if (ED_transform_snap_object_project_view3d( inter->snap_context_v3d, CTX_data_ensure_evaluated_depsgraph(C), + region, + CTX_wm_view3d(C), (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE), &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, @@ -381,8 +383,7 @@ static int gizmo_move_invoke(bContext *C, wmGizmo *gz, const wmEvent *event) if (area) { switch (area->spacetype) { case SPACE_VIEW3D: { - inter->snap_context_v3d = ED_transform_snap_object_context_create_view3d( - CTX_data_scene(C), 0, CTX_wm_region(C), CTX_wm_view3d(C)); + inter->snap_context_v3d = ED_transform_snap_object_context_create(CTX_data_scene(C), 0); break; } default: diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index ae2cc05c01b..f673d3e85ef 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -278,15 +278,11 @@ void ED_gizmotypes_snap_3d_draw_util(RegionView3D *rv3d, immUnbindProgram(); } -SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(Scene *scene, - const ARegion *region, - const View3D *v3d, - wmGizmo *gz) +SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(Scene *scene, wmGizmo *gz) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; if (snap_gizmo->snap_context_v3d == NULL) { - snap_gizmo->snap_context_v3d = ED_transform_snap_object_context_create_view3d( - scene, 0, region, v3d); + snap_gizmo->snap_context_v3d = ED_transform_snap_object_context_create(scene, 0); } return snap_gizmo->snap_context_v3d; } @@ -387,6 +383,8 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz, snap_elem = ED_transform_snap_object_project_view3d_ex( snap_gizmo->snap_context_v3d, depsgraph, + region, + v3d, snap_elements, &(const struct SnapObjectParams){ .snap_select = snap_select, @@ -576,8 +574,12 @@ static void snap_gizmo_setup(wmGizmo *gz) #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - snap_gizmo->keymap = WM_modalkeymap_find(gz->parent_gzgroup->type->keyconf, - "Generic Gizmo Tweak Modal Map"); + wmKeyConfig *keyconf = gz->parent_gzgroup->type->keyconf; + if (!keyconf) { + /* It can happen when gizmogrouptype is not linked at startup. */ + keyconf = ((wmWindowManager *)G.main->wm.first)->defaultconf; + } + snap_gizmo->keymap = WM_modalkeymap_find(keyconf, "Generic Gizmo Tweak Modal Map"); RNA_enum_value_from_id(snap_gizmo->keymap->modal_items, "SNAP_ON", &snap_gizmo->snap_on); #endif } diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c index 2d299230124..960f228edd0 100644 --- a/source/blender/editors/gpencil/gpencil_bake_animation.c +++ b/source/blender/editors/gpencil/gpencil_bake_animation.c @@ -213,7 +213,6 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); - ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); ListBase ob_selected_list = {NULL, NULL}; @@ -256,7 +255,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op gsc.ob = ob_gpencil; /* Init snap context for geometry projection. */ - sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); + sctx = ED_transform_snap_object_context_create(scene, 0); } /* Loop all frame range. */ diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 1f31c60367e..90b6db82838 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3758,7 +3758,6 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) bGPdata *gpd = ED_gpencil_data_get_active(C); Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ARegion *region = CTX_wm_region(C); int oldframe = (int)DEG_get_ctime(depsgraph); const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); const bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); @@ -3767,7 +3766,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) /* Init snap context for geometry projection. */ SnapObjectContext *sctx = NULL; - sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); + sctx = ED_transform_snap_object_context_create(scene, 0); bool changed = false; /* Init space conversion stuff. */ diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index 079089786d0..11e02b9832f 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -188,7 +188,6 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); - ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); ListBase ob_selected_list = {NULL, NULL}; @@ -262,7 +261,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) gsc.ob = ob_gpencil; /* Init snap context for geometry projection. */ - sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); + sctx = ED_transform_snap_object_context_create(scene, 0); /* Tag all existing strokes to avoid reprojections. */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index bb05b93ad81..d3640c6eebd 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -1292,6 +1292,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, depsgraph, region, v3d, xy, &ray_start[0], &ray_normal[0], true); if (ED_transform_snap_object_project_ray(sctx, depsgraph, + v3d, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, }, diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h index 357d5e10fa7..9bef5a17d12 100644 --- a/source/blender/editors/include/ED_gizmo_library.h +++ b/source/blender/editors/include/ED_gizmo_library.h @@ -257,8 +257,6 @@ void ED_gizmotypes_snap_3d_draw_util(struct RegionView3D *rv3d, const uchar color_point[4], const short snap_elem_type); struct SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(struct Scene *scene, - const struct ARegion *region, - const struct View3D *v3d, struct wmGizmo *gz); typedef enum { diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index 42e73bbf744..c8e8513d104 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -83,11 +83,6 @@ struct SnapObjectParams { typedef struct SnapObjectContext SnapObjectContext; SnapObjectContext *ED_transform_snap_object_context_create(struct Scene *scene, int flag); -SnapObjectContext *ED_transform_snap_object_context_create_view3d(struct Scene *scene, - int flag, - /* extra args for view3d */ - const struct ARegion *region, - const struct View3D *v3d); void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx); /* callbacks to filter how snap works */ @@ -100,6 +95,7 @@ void ED_transform_snap_object_context_set_editmesh_callbacks( bool ED_transform_snap_object_project_ray_ex(struct SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], @@ -112,6 +108,7 @@ bool ED_transform_snap_object_project_ray_ex(struct SnapObjectContext *sctx, float r_obmat[4][4]); bool ED_transform_snap_object_project_ray(SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_origin[3], const float ray_direction[3], @@ -121,6 +118,7 @@ bool ED_transform_snap_object_project_ray(SnapObjectContext *sctx, bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], @@ -130,6 +128,8 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], @@ -142,6 +142,8 @@ short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, float r_obmat[4][4]); bool ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], @@ -153,6 +155,8 @@ bool ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, bool ED_transform_snap_object_project_all_view3d_ex(SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const struct SnapObjectParams *params, const float mval[2], float ray_depth, diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index c6a8e771362..97303117dd3 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -1701,8 +1701,8 @@ void EDBM_project_snap_verts( ED_view3d_init_mats_rv3d(obedit, region->regiondata); - struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( - CTX_data_scene(C), 0, region, CTX_wm_view3d(C)); + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create( + CTX_data_scene(C), 0); BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { @@ -1711,6 +1711,8 @@ void EDBM_project_snap_verts( V3D_PROJ_RET_OK) { if (ED_transform_snap_object_project_view3d(snap_context, depsgraph, + region, + CTX_wm_view3d(C), SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_NOT_ACTIVE, diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 6f51d740e55..302862b1a8f 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -5099,14 +5099,15 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, float ray_no[3]; float ray_co[3]; - struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( - scene, 0, region, v3d); + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create(scene, 0); float obmat[4][4]; Object *ob_dummy = NULL; float dist_px = 0; if (ED_transform_snap_object_project_view3d_ex(snap_context, CTX_data_ensure_evaluated_depsgraph(C), + region, + v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index f8278edbcae..573f5348b5e 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -356,8 +356,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, if (do_thickness && inter->co_index != 1) { Scene *scene = DEG_get_input_scene(depsgraph); View3D *v3d = ruler_info->area->spacedata.first; - SnapObjectContext *snap_context = ED_gizmotypes_snap_3d_context_ensure( - scene, ruler_info->region, v3d, snap_gizmo); + SnapObjectContext *snap_context = ED_gizmotypes_snap_3d_context_ensure(scene, snap_gizmo); const float mval_fl[2] = {UNPACK2(mval)}; float ray_normal[3]; float ray_start[3]; @@ -367,6 +366,8 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, if (ED_transform_snap_object_project_view3d(snap_context, depsgraph, + ruler_info->region, + v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, @@ -382,6 +383,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias); ED_transform_snap_object_project_ray(snap_context, depsgraph, + v3d, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, .edit_mode_type = SNAP_GEOM_CAGE, diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c index 1ac241013ed..83b8c04acb6 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_walk.c +++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c @@ -423,6 +423,7 @@ static bool walk_floor_distance_get(RegionView3D *rv3d, ret = ED_transform_snap_object_project_ray( walk->snap_context, walk->depsgraph, + walk->v3d, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, /* Avoid having to convert the edit-mesh to a regular mesh. */ @@ -464,6 +465,7 @@ static bool walk_ray_cast(RegionView3D *rv3d, ret = ED_transform_snap_object_project_ray(walk->snap_context, walk->depsgraph, + walk->v3d, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, }, @@ -602,8 +604,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->rv3d->rflag |= RV3D_NAVIGATING; - walk->snap_context = ED_transform_snap_object_context_create_view3d( - walk->scene, 0, walk->region, walk->v3d); + walk->snap_context = ED_transform_snap_object_context_create(walk->scene, 0); walk->v3d_camera_control = ED_view3d_cameracontrol_acquire( walk->depsgraph, walk->scene, walk->v3d, walk->rv3d); diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index aa3bf46d2e5..6cd6d85d204 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -306,6 +306,8 @@ static bool idp_snap_normal_from_gizmo(wmGizmo *gz, float r_normal[3]) */ static bool idp_poject_surface_normal(SnapObjectContext *snap_context, struct Depsgraph *depsgraph, + ARegion *region, + View3D *v3d, const float mval_fl[2], const float mat_fallback[3][3], const float normal_fallback[3], @@ -320,6 +322,8 @@ static bool idp_poject_surface_normal(SnapObjectContext *snap_context, if (ED_transform_snap_object_project_view3d_ex(snap_context, depsgraph, + region, + v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, @@ -885,11 +889,9 @@ static void view3d_interactive_add_calc_plane(bContext *C, /* Set the orientation. */ if ((plane_orient == PLACE_ORIENT_SURFACE) || (plane_depth == PLACE_DEPTH_SURFACE)) { - snap_context = (snap_gizmo ? - ED_gizmotypes_snap_3d_context_ensure(scene, region, v3d, snap_gizmo) : - NULL); + snap_context = (snap_gizmo ? ED_gizmotypes_snap_3d_context_ensure(scene, snap_gizmo) : NULL); if (snap_context == NULL) { - snap_context = ED_transform_snap_object_context_create_view3d(scene, 0, region, v3d); + snap_context = ED_transform_snap_object_context_create(scene, 0); snap_context_free = true; } } @@ -908,6 +910,8 @@ static void view3d_interactive_add_calc_plane(bContext *C, if ((snap_context != NULL) && idp_poject_surface_normal(snap_context, CTX_data_ensure_evaluated_depsgraph(C), + region, + v3d, mval_fl, use_normal_fallback ? r_matrix_orient : NULL, use_normal_fallback ? normal_fallback : NULL, @@ -938,6 +942,8 @@ static void view3d_interactive_add_calc_plane(bContext *C, if ((snap_context != NULL) && ED_transform_snap_object_project_view3d(snap_context, CTX_data_ensure_evaluated_depsgraph(C), + region, + v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 39a70f5477e..b8a35cb51e3 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -374,6 +374,8 @@ void applyProject(TransInfo *t) if (ED_transform_snap_object_project_view3d( t->tsnap.object_context, t->depsgraph, + t->region, + t->view, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, @@ -671,8 +673,7 @@ static void initSnappingMode(TransInfo *t) if (t->spacetype == SPACE_VIEW3D) { if (t->tsnap.object_context == NULL) { t->tsnap.use_backface_culling = snap_use_backface_culling(t); - t->tsnap.object_context = ED_transform_snap_object_context_create_view3d( - t->scene, 0, t->region, t->view); + t->tsnap.object_context = ED_transform_snap_object_context_create(t->scene, 0); if (t->data_type == TC_MESH_VERTS) { /* Ignore elements being transformed. */ @@ -1245,6 +1246,8 @@ short snapObjectsTransform( return ED_transform_snap_object_project_view3d_ex( t->tsnap.object_context, t->depsgraph, + t->region, + t->view, t->settings->snap_mode, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, @@ -1280,6 +1283,8 @@ bool peelObjectsTransform(TransInfo *t, ED_transform_snap_object_project_all_view3d_ex( t->tsnap.object_context, t->depsgraph, + t->region, + t->view, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 883a89b8467..5f9250e87f7 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -69,17 +69,6 @@ enum eViewProj { VIEW_PROJ_PERSP = -1, }; -typedef struct SnapData { - float mval[2]; - float pmat[4][4]; /* perspective matrix */ - float win_size[2]; /* win x and y */ - enum eViewProj view_proj; - float clip_plane[MAX_CLIPPLANE_LEN][4]; - short clip_plane_len; - short snap_to_flag; - bool has_occlusion_plane; /* Ignore plane of occlusion in curves. */ -} SnapData; - typedef struct SnapObjectData { enum { SNAP_MESH = 1, @@ -113,14 +102,6 @@ struct SnapObjectContext { int flag; - /* Optional: when performing screen-space projection. - * otherwise this doesn't take viewport into account. */ - bool use_v3d; - struct { - const struct View3D *v3d; - const struct ARegion *region; - } v3d_data; - /* Object -> SnapObjectData map */ struct { GHash *object_map; @@ -138,6 +119,21 @@ struct SnapObjectContext { void *user_data; } edit_mesh; } callbacks; + + struct { + Depsgraph *depsgraph; + const ARegion *region; + const View3D *v3d; + + float mval[2]; + float pmat[4][4]; /* perspective matrix */ + float win_size[2]; /* win x and y */ + enum eViewProj view_proj; + float clip_plane[MAX_CLIPPLANE_LEN][4]; + short clip_plane_len; + short snap_to_flag; + bool has_occlusion_plane; /* Ignore plane of occlusion in curves. */ + } runtime; }; /** \} */ @@ -457,27 +453,25 @@ typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx, * Walks through all objects in the scene to create the list of objects to snap. */ static void iter_snap_objects(SnapObjectContext *sctx, - Depsgraph *depsgraph, const struct SnapObjectParams *params, IterSnapObjsCallback sob_callback, void *data) { - ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); - const View3D *v3d = sctx->v3d_data.v3d; + ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph); const eSnapSelect snap_select = params->snap_select; const eSnapEditType edit_mode_type = params->edit_mode_type; const bool use_backface_culling = params->use_backface_culling; Base *base_act = view_layer->basact; if (snap_select == SNAP_ONLY_ACTIVE) { - Object *obj_eval = DEG_get_evaluated_object(depsgraph, base_act->object); + Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base_act->object); sob_callback( sctx, obj_eval, obj_eval->obmat, edit_mode_type, use_backface_culling, true, data); return; } for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) { - if (!BASE_VISIBLE(v3d, base)) { + if (!BASE_VISIBLE(sctx->runtime.v3d, base)) { continue; } @@ -500,9 +494,9 @@ static void iter_snap_objects(SnapObjectContext *sctx, } } - Object *obj_eval = DEG_get_evaluated_object(depsgraph, base->object); + Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base->object); if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) { - ListBase *lb = object_duplilist(depsgraph, sctx->scene, obj_eval); + ListBase *lb = object_duplilist(sctx->runtime.depsgraph, sctx->scene, obj_eval); for (DupliObject *dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { BLI_assert(DEG_is_evaluated_object(dupli_ob->ob)); sob_callback(sctx, @@ -1031,12 +1025,6 @@ static void raycast_obj_fn(SnapObjectContext *sctx, bool retval = false; if (use_occlusion_test) { - if ((edit_mode_type == SNAP_GEOM_EDIT) && sctx->use_v3d && - XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) { - /* Use of occlude geometry in editing mode disabled. */ - return; - } - if (ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) { /* Do not hit objects that are in wire or bounding box * display mode. */ @@ -1147,6 +1135,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx, */ static bool raycastObjects(SnapObjectContext *sctx, Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_dir[3], @@ -1162,6 +1151,14 @@ static bool raycastObjects(SnapObjectContext *sctx, float r_obmat[4][4], ListBase *r_hit_list) { + if (v3d && (params->edit_mode_type == SNAP_GEOM_EDIT) && XRAY_FLAG_ENABLED(v3d)) { + /* Use of occlude geometry in editing mode disabled. */ + return false; + } + + sctx->runtime.depsgraph = depsgraph; + sctx->runtime.v3d = v3d; + struct RaycastObjUserData data = { .ray_start = ray_start, .ray_dir = ray_dir, @@ -1177,7 +1174,7 @@ static bool raycastObjects(SnapObjectContext *sctx, .ret = false, }; - iter_snap_objects(sctx, depsgraph, params, raycast_obj_fn, &data); + iter_snap_objects(sctx, params, raycast_obj_fn, &data); return data.ret; } @@ -1554,7 +1551,6 @@ static void cb_snap_tri_verts(void *userdata, * \{ */ static short snap_mesh_polygon(SnapObjectContext *sctx, - SnapData *snapdata, Object *ob_eval, const float obmat[4][4], bool use_backface_culling, @@ -1568,16 +1564,16 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, short elem = 0; float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); struct DistProjectedAABBPrecalc neasrest_precalc; dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval); float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); - for (int i = snapdata->clip_plane_len; i--;) { - mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + for (int i = sctx->runtime.clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]); } BVHTreeNearest nearest = { @@ -1590,14 +1586,14 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, Nearest2dUserData nearest2d; nearest2d_data_init( - sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); + sod, sctx->runtime.view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); if (sod->type == SNAP_MESH) { BVHTreeFromMesh *treedata = &sod->treedata_mesh; const MPoly *mp = &sod->poly[*r_index]; const MLoop *ml = &treedata->loop[mp->loopstart]; - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { elem = SCE_SNAP_MODE_EDGE; BLI_assert(treedata->edge != NULL); for (int i = mp->totloop; i--; ml++) { @@ -1605,7 +1601,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, ml->e, &neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest); } } @@ -1616,7 +1612,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, ml->v, &neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest); } } @@ -1629,7 +1625,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, BMFace *f = BM_face_at_index(em->bm, *r_index); BMLoop *l_iter, *l_first; l_iter = l_first = BM_FACE_FIRST_LOOP(f); - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { elem = SCE_SNAP_MODE_EDGE; BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_EDGE); BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE); @@ -1638,7 +1634,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, BM_elem_index_get(l_iter->e), &neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest); } while ((l_iter = l_iter->next) != l_first); } @@ -1651,7 +1647,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, BM_elem_index_get(l_iter->v), &neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest); } while ((l_iter = l_iter->next) != l_first); } @@ -1680,7 +1676,6 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, } static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, - SnapData *snapdata, Object *ob_eval, const float obmat[4][4], float original_dist_px, @@ -1704,7 +1699,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, Nearest2dUserData nearest2d; nearest2d_data_init( - sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); + sod, sctx->runtime.view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); int vindex[2]; nearest2d.get_edge_verts_index(*r_index, nearest2d.userdata, vindex); @@ -1716,10 +1711,10 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, struct DistProjectedAABBPrecalc neasrest_precalc; { float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval); } BVHTreeNearest nearest = { @@ -1736,7 +1731,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, /* do nothing */ } else { - short snap_to_flag = snapdata->snap_to_flag; + short snap_to_flag = sctx->runtime.snap_to_flag; int e_mode_len = ((snap_to_flag & SCE_SNAP_MODE_EDGE) != 0) + ((snap_to_flag & SCE_SNAP_MODE_VERTEX) != 0) + ((snap_to_flag & SCE_SNAP_MODE_EDGE_MIDPOINT) != 0); @@ -1785,7 +1780,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, } } - if (prev_co && (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + if (prev_co && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { float v_near[3], va_g[3], vb_g[3]; mul_v3_m4v3(va_g, obmat, v_pair[0]); @@ -1797,7 +1792,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, if (len_squared_v3v3(prev_co, v_near) > FLT_EPSILON) { dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, sctx->runtime.pmat, sctx->runtime.win_size, sctx->runtime.mval); if (test_projected_vert_dist(&neasrest_precalc, NULL, @@ -1828,7 +1823,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, return elem; } -static short snapArmature(SnapData *snapdata, +static short snapArmature(SnapObjectContext *sctx, Object *ob_eval, const float obmat[4][4], /* read/write args */ @@ -1840,35 +1835,39 @@ static short snapArmature(SnapData *snapdata, { short retval = 0; - if (snapdata->snap_to_flag == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */ + if (sctx->runtime.snap_to_flag == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */ return retval; } float lpmat[4][4], dist_px_sq = square_f(*dist_px); - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); struct DistProjectedAABBPrecalc neasrest_precalc; dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval); bool use_obedit = ((bArmature *)ob_eval->data)->edbo != NULL; if (use_obedit == false) { /* Test BoundBox */ BoundBox *bb = BKE_armature_boundbox_get(ob_eval); - if (bb && !snap_bound_box_check_dist( - bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { + if (bb && !snap_bound_box_check_dist(bb->vec[0], + bb->vec[6], + lpmat, + sctx->runtime.win_size, + sctx->runtime.mval, + dist_px_sq)) { return retval; } } float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); - for (int i = snapdata->clip_plane_len; i--;) { - mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + for (int i = sctx->runtime.clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]); } - bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP; bArmature *arm = ob_eval->data; if (arm->edbo) { @@ -1878,17 +1877,17 @@ static short snapArmature(SnapData *snapdata, if ((eBone->flag & (BONE_HIDDEN_A | BONE_ROOTSEL | BONE_TIPSEL)) == 0) { bool has_vert_snap = false; - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { has_vert_snap = test_projected_vert_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, eBone->head, &dist_px_sq, r_loc); has_vert_snap |= test_projected_vert_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, eBone->tail, &dist_px_sq, @@ -1898,10 +1897,10 @@ static short snapArmature(SnapData *snapdata, retval = SCE_SNAP_MODE_VERTEX; } } - if (!has_vert_snap && snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (!has_vert_snap && sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { if (test_projected_edge_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, eBone->head, eBone->tail, @@ -1923,17 +1922,17 @@ static short snapArmature(SnapData *snapdata, const float *head_vec = pchan->pose_head; const float *tail_vec = pchan->pose_tail; - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { has_vert_snap = test_projected_vert_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, head_vec, &dist_px_sq, r_loc); has_vert_snap |= test_projected_vert_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, tail_vec, &dist_px_sq, @@ -1943,10 +1942,10 @@ static short snapArmature(SnapData *snapdata, retval = SCE_SNAP_MODE_VERTEX; } } - if (!has_vert_snap && snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (!has_vert_snap && sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { if (test_projected_edge_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, head_vec, tail_vec, @@ -1972,7 +1971,7 @@ static short snapArmature(SnapData *snapdata, return 0; } -static short snapCurve(SnapData *snapdata, +static short snapCurve(SnapObjectContext *sctx, Object *ob_eval, const float obmat[4][4], bool use_obedit, @@ -1986,7 +1985,7 @@ static short snapCurve(SnapData *snapdata, bool has_snap = false; /* only vertex snapping mode (eg control points and handles) supported for now) */ - if ((snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) == 0) { + if ((sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) == 0) { return 0; } @@ -1994,19 +1993,23 @@ static short snapCurve(SnapData *snapdata, float dist_px_sq = square_f(*dist_px); float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); struct DistProjectedAABBPrecalc neasrest_precalc; dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval); use_obedit = use_obedit && BKE_object_is_in_editmode(ob_eval); if (use_obedit == false) { /* Test BoundBox */ BoundBox *bb = BKE_curve_boundbox_get(ob_eval); - if (bb && !snap_bound_box_check_dist( - bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { + if (bb && !snap_bound_box_check_dist(bb->vec[0], + bb->vec[6], + lpmat, + sctx->runtime.win_size, + sctx->runtime.mval, + dist_px_sq)) { return 0; } } @@ -2014,10 +2017,10 @@ static short snapCurve(SnapData *snapdata, float tobmat[4][4]; transpose_m4_m4(tobmat, obmat); - float(*clip_planes)[4] = snapdata->clip_plane; - int clip_plane_len = snapdata->clip_plane_len; + float(*clip_planes)[4] = sctx->runtime.clip_plane; + int clip_plane_len = sctx->runtime.clip_plane_len; - if (snapdata->has_occlusion_plane) { + if (sctx->runtime.has_occlusion_plane) { /* We snap to vertices even if occluded. */ clip_planes++; clip_plane_len--; @@ -2028,11 +2031,11 @@ static short snapCurve(SnapData *snapdata, mul_v4_m4v4(clip_planes_local[i], tobmat, clip_planes[i]); } - bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP; for (Nurb *nu = (use_obedit ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { for (int u = 0; u < nu->pntsu; u++) { - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { if (use_obedit) { if (nu->bezt) { /* don't snap to selected (moving) or hidden */ @@ -2123,7 +2126,7 @@ static short snapCurve(SnapData *snapdata, } /* may extend later (for now just snaps to empty center) */ -static short snap_object_center(SnapData *snapdata, +static short snap_object_center(const SnapObjectContext *sctx, Object *ob_eval, const float obmat[4][4], /* read/write args */ @@ -2140,24 +2143,24 @@ static short snap_object_center(SnapData *snapdata, } /* for now only vertex supported */ - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { struct DistProjectedAABBPrecalc neasrest_precalc; dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, sctx->runtime.pmat, sctx->runtime.win_size, sctx->runtime.mval); float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); - for (int i = snapdata->clip_plane_len; i--;) { - mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + for (int i = sctx->runtime.clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]); } - bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP; float dist_px_sq = square_f(*dist_px); float co[3]; copy_v3_v3(co, obmat[3]); if (test_projected_vert_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, co, &dist_px_sq, @@ -2179,7 +2182,6 @@ static short snap_object_center(SnapData *snapdata, } static short snapCamera(const SnapObjectContext *sctx, - SnapData *snapdata, Object *object, float obmat[4][4], /* read/write args */ @@ -2193,7 +2195,7 @@ static short snapCamera(const SnapObjectContext *sctx, Scene *scene = sctx->scene; - bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP; float dist_px_sq = square_f(*dist_px); float orig_camera_mat[4][4], orig_camera_imat[4][4], imat[4][4]; @@ -2201,7 +2203,7 @@ static short snapCamera(const SnapObjectContext *sctx, MovieTracking *tracking; if (clip == NULL) { - return snap_object_center(snapdata, object, obmat, dist_px, r_loc, r_no, r_index); + return snap_object_center(sctx, object, obmat, dist_px, r_loc, r_no, r_index); } if (object->transflag & OB_DUPLI) { return retval; @@ -2214,10 +2216,10 @@ static short snapCamera(const SnapObjectContext *sctx, invert_m4_m4(orig_camera_imat, orig_camera_mat); invert_m4_m4(imat, obmat); - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { struct DistProjectedAABBPrecalc neasrest_precalc; dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, sctx->runtime.pmat, sctx->runtime.win_size, sctx->runtime.mval); MovieTrackingObject *tracking_object; for (tracking_object = tracking->objects.first; tracking_object; @@ -2252,8 +2254,8 @@ static short snapCamera(const SnapObjectContext *sctx, mul_m4_v3(vertex_obmat, bundle_pos); if (test_projected_vert_dist(&neasrest_precalc, - snapdata->clip_plane, - snapdata->clip_plane_len, + sctx->runtime.clip_plane, + sctx->runtime.clip_plane_len, is_persp, bundle_pos, &dist_px_sq, @@ -2277,7 +2279,6 @@ static short snapCamera(const SnapObjectContext *sctx, } static short snapMesh(SnapObjectContext *sctx, - SnapData *snapdata, Object *ob_eval, Mesh *me_eval, const float obmat[4][4], @@ -2290,23 +2291,24 @@ static short snapMesh(SnapObjectContext *sctx, float r_no[3], int *r_index) { - BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE); + BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE); if (me_eval->totvert == 0) { return 0; } - if (me_eval->totedge == 0 && !(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { + if (me_eval->totedge == 0 && !(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) { return 0; } float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); float dist_px_sq = square_f(*dist_px); /* Test BoundBox */ BoundBox *bb = BKE_mesh_boundbox_get(ob_eval); - if (bb && !snap_bound_box_check_dist( - bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { + if (bb && + !snap_bound_box_check_dist( + bb->vec[0], bb->vec[6], lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) { return 0; } @@ -2330,7 +2332,7 @@ static short snapMesh(SnapObjectContext *sctx, treedata_tmp.looptri_allocated)); } - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { if (sod->has_loose_vert && sod->bvhtree[1] == NULL) { sod->bvhtree[1] = BKE_bvhtree_from_mesh_get( &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEVERTS, 2); @@ -2353,7 +2355,7 @@ static short snapMesh(SnapObjectContext *sctx, Nearest2dUserData nearest2d; nearest2d_data_init( - sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); + sod, sctx->runtime.view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); BVHTreeNearest nearest = { .index = -1, @@ -2364,18 +2366,18 @@ static short snapMesh(SnapObjectContext *sctx, float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); - for (int i = snapdata->clip_plane_len; i--;) { - mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + for (int i = sctx->runtime.clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]); } - if (sod->bvhtree[1] && (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { + if (sod->bvhtree[1] && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) { /* snap to loose verts */ BLI_bvhtree_find_nearest_projected(sod->bvhtree[1], lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_vert, &nearest2d); @@ -2383,15 +2385,15 @@ static short snapMesh(SnapObjectContext *sctx, last_index = nearest.index; } - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { if (sod->bvhtree[0]) { /* snap to loose edges */ BLI_bvhtree_find_nearest_projected(sod->bvhtree[0], lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_edge, &nearest2d); @@ -2401,10 +2403,10 @@ static short snapMesh(SnapObjectContext *sctx, /* snap to looptris */ BLI_bvhtree_find_nearest_projected(treedata->tree, lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_tri_edges, &nearest2d); @@ -2415,15 +2417,15 @@ static short snapMesh(SnapObjectContext *sctx, } } else { - BLI_assert(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX); + BLI_assert(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX); if (sod->bvhtree[0]) { /* snap to loose edge verts */ BLI_bvhtree_find_nearest_projected(sod->bvhtree[0], lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_edge_verts, &nearest2d); @@ -2433,10 +2435,10 @@ static short snapMesh(SnapObjectContext *sctx, /* snap to looptri verts */ BLI_bvhtree_find_nearest_projected(treedata->tree, lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_tri_verts, &nearest2d); @@ -2468,7 +2470,6 @@ static short snapMesh(SnapObjectContext *sctx, } static short snapEditMesh(SnapObjectContext *sctx, - SnapData *snapdata, Object *ob_eval, BMEditMesh *em, const float obmat[4][4], @@ -2480,9 +2481,9 @@ static short snapEditMesh(SnapObjectContext *sctx, float r_no[3], int *r_index) { - BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE); + BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE); - if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) { + if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) { if (em->bm->totvert == 0) { return 0; } @@ -2494,7 +2495,7 @@ static short snapEditMesh(SnapObjectContext *sctx, } float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); float dist_px_sq = square_f(*dist_px); @@ -2504,11 +2505,11 @@ static short snapEditMesh(SnapObjectContext *sctx, /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ if (!snap_bound_box_check_dist( - sod->min, sod->max, lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { + sod->min, sod->max, lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) { return 0; } - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { BVHTreeFromEditMesh treedata = {.tree = sod->bvhtree[0]}; if (treedata.tree == NULL) { @@ -2540,7 +2541,7 @@ static short snapEditMesh(SnapObjectContext *sctx, } } - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { BVHTreeFromEditMesh treedata = {.tree = sod->bvhtree[1]}; if (treedata.tree == NULL) { @@ -2574,7 +2575,7 @@ static short snapEditMesh(SnapObjectContext *sctx, Nearest2dUserData nearest2d; nearest2d_data_init( - sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); + sod, sctx->runtime.view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); BVHTreeNearest nearest = { .index = -1, @@ -2585,35 +2586,35 @@ static short snapEditMesh(SnapObjectContext *sctx, float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); - for (int i = snapdata->clip_plane_len; i--;) { - mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + for (int i = sctx->runtime.clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]); } - if (sod->bvhtree[0] && (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { + if (sod->bvhtree[0] && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) { BM_mesh_elem_table_ensure(em->bm, BM_VERT); BM_mesh_elem_index_ensure(em->bm, BM_VERT); BLI_bvhtree_find_nearest_projected(sod->bvhtree[0], lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_vert, &nearest2d); } - if (sod->bvhtree[1] && (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE)) { + if (sod->bvhtree[1] && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE)) { int last_index = nearest.index; nearest.index = -1; BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_VERT); BM_mesh_elem_index_ensure(em->bm, BM_EDGE | BM_VERT); BLI_bvhtree_find_nearest_projected(sod->bvhtree[1], lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_edge, &nearest2d); @@ -2650,7 +2651,6 @@ static short snapEditMesh(SnapObjectContext *sctx, } struct SnapObjUserData { - SnapData *snapdata; /* read/write args */ float *dist_px; /* return args */ @@ -2686,7 +2686,6 @@ static void snap_obj_fn(SnapObjectContext *sctx, /* Operators only update the editmesh looptris of the original mesh. */ BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval)); retval = snapEditMesh(sctx, - dt->snapdata, ob_eval, em_orig, obmat, @@ -2703,7 +2702,6 @@ static void snap_obj_fn(SnapObjectContext *sctx, } retval = snapMesh(sctx, - dt->snapdata, ob_eval, me_eval, obmat, @@ -2716,11 +2714,10 @@ static void snap_obj_fn(SnapObjectContext *sctx, break; } case OB_ARMATURE: - retval = snapArmature( - dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + retval = snapArmature(sctx, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; case OB_CURVE: - retval = snapCurve(dt->snapdata, + retval = snapCurve(sctx, ob_eval, obmat, edit_mode_type == SNAP_GEOM_EDIT, @@ -2734,7 +2731,6 @@ static void snap_obj_fn(SnapObjectContext *sctx, Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); if (mesh_eval) { retval |= snapMesh(sctx, - dt->snapdata, ob_eval, mesh_eval, obmat, @@ -2751,11 +2747,10 @@ static void snap_obj_fn(SnapObjectContext *sctx, case OB_GPENCIL: case OB_LAMP: retval = snap_object_center( - dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + sctx, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; case OB_CAMERA: - retval = snapCamera( - sctx, dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + retval = snapCamera(sctx, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; } @@ -2796,8 +2791,6 @@ static void snap_obj_fn(SnapObjectContext *sctx, * \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances). */ static short snapObjectsRay(SnapObjectContext *sctx, - Depsgraph *depsgraph, - SnapData *snapdata, const struct SnapObjectParams *params, /* read/write args */ /* Parameters below cannot be const, because they are assigned to a @@ -2811,7 +2804,6 @@ static short snapObjectsRay(SnapObjectContext *sctx, float r_obmat[4][4]) { struct SnapObjUserData data = { - .snapdata = snapdata, .dist_px = dist_px, .r_loc = r_loc, .r_no = r_no, @@ -2821,7 +2813,7 @@ static short snapObjectsRay(SnapObjectContext *sctx, .ret = 0, }; - iter_snap_objects(sctx, depsgraph, params, snap_obj_fn, &data); + iter_snap_objects(sctx, params, snap_obj_fn, &data); return data.ret; } @@ -2848,21 +2840,6 @@ SnapObjectContext *ED_transform_snap_object_context_create(Scene *scene, int fla return sctx; } -SnapObjectContext *ED_transform_snap_object_context_create_view3d(Scene *scene, - int flag, - /* extra args for view3d */ - const ARegion *region, - const View3D *v3d) -{ - SnapObjectContext *sctx = ED_transform_snap_object_context_create(scene, flag); - - sctx->use_v3d = true; - sctx->v3d_data.region = region; - sctx->v3d_data.v3d = v3d; - - return sctx; -} - static void snap_object_data_free(void *sod_v) { SnapObjectData *sod = sod_v; @@ -2896,6 +2873,7 @@ void ED_transform_snap_object_context_set_editmesh_callbacks( bool ED_transform_snap_object_project_ray_ex(SnapObjectContext *sctx, Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], @@ -2908,6 +2886,7 @@ bool ED_transform_snap_object_project_ray_ex(SnapObjectContext *sctx, { return raycastObjects(sctx, depsgraph, + v3d, params, ray_start, ray_normal, @@ -2929,6 +2908,7 @@ bool ED_transform_snap_object_project_ray_ex(SnapObjectContext *sctx, */ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], @@ -2946,6 +2926,7 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, bool retval = raycastObjects(sctx, depsgraph, + v3d, params, ray_start, ray_normal, @@ -2978,6 +2959,7 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, */ static bool transform_snap_context_project_ray_impl(SnapObjectContext *sctx, Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], @@ -2988,14 +2970,25 @@ static bool transform_snap_context_project_ray_impl(SnapObjectContext *sctx, bool ret; /* try snap edge, then face if it fails */ - ret = ED_transform_snap_object_project_ray_ex( - sctx, depsgraph, params, ray_start, ray_normal, ray_depth, r_co, r_no, NULL, NULL, NULL); + ret = ED_transform_snap_object_project_ray_ex(sctx, + depsgraph, + v3d, + params, + ray_start, + ray_normal, + ray_depth, + r_co, + r_no, + NULL, + NULL, + NULL); return ret; } bool ED_transform_snap_object_project_ray(SnapObjectContext *sctx, Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_origin[3], const float ray_direction[3], @@ -3010,12 +3003,14 @@ bool ED_transform_snap_object_project_ray(SnapObjectContext *sctx, } return transform_snap_context_project_ray_impl( - sctx, depsgraph, params, ray_origin, ray_direction, ray_depth, r_co, r_no); + sctx, depsgraph, v3d, params, ray_origin, ray_direction, ray_depth, r_co, r_no); } static short transform_snap_context_project_view3d_mixed_impl( SnapObjectContext *sctx, Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const ushort snap_to_flag, const struct SnapObjectParams *params, const float mval[2], @@ -3042,21 +3037,18 @@ static short transform_snap_context_project_view3d_mixed_impl( float obmat[4][4]; int index = -1; - const ARegion *region = sctx->v3d_data.region; const RegionView3D *rv3d = region->regiondata; - bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(sctx->v3d_data.v3d); + bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(v3d); + + sctx->runtime.depsgraph = depsgraph; + sctx->runtime.region = region; + sctx->runtime.v3d = v3d; if (snap_to_flag & SCE_SNAP_MODE_FACE || use_occlusion_test) { float ray_start[3], ray_normal[3]; - if (!ED_view3d_win_to_ray_clipped_ex(depsgraph, - sctx->v3d_data.region, - sctx->v3d_data.v3d, - mval, - NULL, - ray_normal, - ray_start, - true)) { + if (!ED_view3d_win_to_ray_clipped_ex( + depsgraph, region, v3d, mval, NULL, ray_normal, ray_start, true)) { return 0; } @@ -3064,6 +3056,7 @@ static short transform_snap_context_project_view3d_mixed_impl( has_hit = raycastObjects(sctx, depsgraph, + v3d, params, ray_start, ray_normal, @@ -3099,24 +3092,28 @@ static short transform_snap_context_project_view3d_mixed_impl( short elem_test, elem = 0; float dist_px_tmp = *dist_px; - SnapData snapdata; - copy_m4_m4(snapdata.pmat, rv3d->persmat); - snapdata.win_size[0] = region->winx; - snapdata.win_size[1] = region->winy; - copy_v2_v2(snapdata.mval, mval); - snapdata.view_proj = rv3d->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO; + copy_m4_m4(sctx->runtime.pmat, rv3d->persmat); + sctx->runtime.win_size[0] = region->winx; + sctx->runtime.win_size[1] = region->winy; + copy_v2_v2(sctx->runtime.mval, mval); + sctx->runtime.view_proj = rv3d->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO; /* First snap to edge instead of middle or perpendicular. */ - snapdata.snap_to_flag = snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE); + sctx->runtime.snap_to_flag = snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE); if (snap_to_flag & (SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { - snapdata.snap_to_flag |= SCE_SNAP_MODE_EDGE; + sctx->runtime.snap_to_flag |= SCE_SNAP_MODE_EDGE; } - planes_from_projmat( - snapdata.pmat, NULL, NULL, NULL, NULL, snapdata.clip_plane[0], snapdata.clip_plane[1]); + planes_from_projmat(sctx->runtime.pmat, + NULL, + NULL, + NULL, + NULL, + sctx->runtime.clip_plane[0], + sctx->runtime.clip_plane[1]); - snapdata.clip_plane_len = 2; - snapdata.has_occlusion_plane = false; + sctx->runtime.clip_plane_len = 2; + sctx->runtime.has_occlusion_plane = false; /* By convention we only snap to the original elements of a curve. */ if (has_hit && ob_eval->type != OB_CURVE) { @@ -3124,7 +3121,7 @@ static short transform_snap_context_project_view3d_mixed_impl( float new_clipplane[4]; BLI_ASSERT_UNIT_V3(no); plane_from_point_normal_v3(new_clipplane, loc, no); - if (dot_v3v3(snapdata.clip_plane[0], new_clipplane) > 0.0f) { + if (dot_v3v3(sctx->runtime.clip_plane[0], new_clipplane) > 0.0f) { /* The plane is facing the wrong direction. */ negate_v4(new_clipplane); } @@ -3133,30 +3130,22 @@ static short transform_snap_context_project_view3d_mixed_impl( new_clipplane[3] += 0.01f; /* Try to snap only to the polygon. */ - elem_test = snap_mesh_polygon(sctx, - &snapdata, - ob_eval, - obmat, - params->use_backface_culling, - &dist_px_tmp, - loc, - no, - &index); + elem_test = snap_mesh_polygon( + sctx, ob_eval, obmat, params->use_backface_culling, &dist_px_tmp, loc, no, &index); if (elem_test) { elem = elem_test; } /* Add the new clip plane to the beginning of the list. */ - for (int i = snapdata.clip_plane_len; i != 0; i--) { - copy_v4_v4(snapdata.clip_plane[i], snapdata.clip_plane[i - 1]); + for (int i = sctx->runtime.clip_plane_len; i != 0; i--) { + copy_v4_v4(sctx->runtime.clip_plane[i], sctx->runtime.clip_plane[i - 1]); } - copy_v4_v4(snapdata.clip_plane[0], new_clipplane); - snapdata.clip_plane_len++; - snapdata.has_occlusion_plane = true; + copy_v4_v4(sctx->runtime.clip_plane[0], new_clipplane); + sctx->runtime.clip_plane_len++; + sctx->runtime.has_occlusion_plane = true; } - elem_test = snapObjectsRay( - sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob_eval, obmat); + elem_test = snapObjectsRay(sctx, params, &dist_px_tmp, loc, no, &index, &ob_eval, obmat); if (elem_test) { elem = elem_test; } @@ -3164,9 +3153,8 @@ static short transform_snap_context_project_view3d_mixed_impl( if ((elem == SCE_SNAP_MODE_EDGE) && (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR))) { - snapdata.snap_to_flag = snap_to_flag; + sctx->runtime.snap_to_flag = snap_to_flag; elem = snap_mesh_edge_verts_mixed(sctx, - &snapdata, ob_eval, obmat, *dist_px, @@ -3204,6 +3192,8 @@ static short transform_snap_context_project_view3d_mixed_impl( short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const ushort snap_to, const struct SnapObjectParams *params, const float mval[2], @@ -3217,6 +3207,8 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, { return transform_snap_context_project_view3d_mixed_impl(sctx, depsgraph, + region, + v3d, snap_to, params, mval, @@ -3244,6 +3236,8 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, */ bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const ushort snap_to, const struct SnapObjectParams *params, const float mval[2], @@ -3254,6 +3248,8 @@ bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, { return ED_transform_snap_object_project_view3d_ex(sctx, depsgraph, + region, + v3d, snap_to, params, mval, @@ -3271,6 +3267,8 @@ bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, */ bool ED_transform_snap_object_project_all_view3d_ex(SnapObjectContext *sctx, Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const struct SnapObjectParams *params, const float mval[2], float ray_depth, @@ -3279,19 +3277,13 @@ bool ED_transform_snap_object_project_all_view3d_ex(SnapObjectContext *sctx, { float ray_start[3], ray_normal[3]; - if (!ED_view3d_win_to_ray_clipped_ex(depsgraph, - sctx->v3d_data.region, - sctx->v3d_data.v3d, - mval, - NULL, - ray_normal, - ray_start, - true)) { + if (!ED_view3d_win_to_ray_clipped_ex( + depsgraph, region, v3d, mval, NULL, ray_normal, ray_start, true)) { return false; } return ED_transform_snap_object_project_ray_all( - sctx, depsgraph, params, ray_start, ray_normal, ray_depth, sort, r_hit_list); + sctx, depsgraph, v3d, params, ray_start, ray_normal, ray_depth, sort, r_hit_list); } /** \} */ diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 3e292ea89e2..543d69842be 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -154,6 +154,7 @@ static void rna_Scene_ray_cast(Scene *scene, bool ret = ED_transform_snap_object_project_ray_ex(sctx, depsgraph, + NULL, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, }, From ecb8a574c752068de9f8d9eb98f54db1569df2f7 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Wed, 13 Oct 2021 23:00:24 +0200 Subject: [PATCH 0765/1500] Cleanup: remove unused includes in Compositor And move unneeded includes in frequently used headers to source files. Slightly reduces compile time. --- source/blender/compositor/COM_defines.h | 4 ++-- .../compositor/intern/COM_BufferRange.h | 1 - .../compositor/intern/COM_CPUDevice.cc | 3 +-- .../compositor/intern/COM_ChunkOrder.cc | 4 ++-- .../intern/COM_CompositorContext.cc | 5 ----- .../compositor/intern/COM_CompositorContext.h | 5 +---- .../compositor/intern/COM_ConstantFolder.cc | 5 +++-- .../compositor/intern/COM_ConstantFolder.h | 6 +----- .../compositor/intern/COM_Converter.cc | 2 -- .../blender/compositor/intern/COM_Converter.h | 3 +-- source/blender/compositor/intern/COM_Debug.cc | 12 +---------- source/blender/compositor/intern/COM_Debug.h | 7 +++++-- source/blender/compositor/intern/COM_Device.h | 4 ++-- source/blender/compositor/intern/COM_Enums.cc | 1 - .../compositor/intern/COM_ExecutionGroup.cc | 17 +-------------- .../compositor/intern/COM_ExecutionGroup.h | 15 +++++++------ .../compositor/intern/COM_ExecutionModel.cc | 1 + .../compositor/intern/COM_ExecutionModel.h | 9 ++++---- .../compositor/intern/COM_ExecutionSystem.cc | 5 ++--- .../compositor/intern/COM_ExecutionSystem.h | 21 +++++++++++-------- .../intern/COM_FullFrameExecutionModel.cc | 9 ++++---- .../intern/COM_FullFrameExecutionModel.h | 9 +++++++- .../compositor/intern/COM_MemoryBuffer.cc | 3 ++- .../compositor/intern/COM_MemoryBuffer.h | 7 ++++--- .../compositor/intern/COM_MemoryProxy.cc | 3 --- .../blender/compositor/intern/COM_MetaData.cc | 2 -- .../intern/COM_MultiThreadedRowOperation.h | 2 ++ source/blender/compositor/intern/COM_Node.cc | 10 --------- source/blender/compositor/intern/COM_Node.h | 3 --- .../compositor/intern/COM_NodeConverter.cc | 4 +--- .../compositor/intern/COM_NodeGraph.cc | 5 ----- .../blender/compositor/intern/COM_NodeGraph.h | 5 ----- .../compositor/intern/COM_NodeOperation.cc | 3 --- .../compositor/intern/COM_NodeOperation.h | 16 +++++++------- .../intern/COM_NodeOperationBuilder.cc | 11 +++------- .../intern/COM_NodeOperationBuilder.h | 5 ++--- .../compositor/intern/COM_OpenCLDevice.cc | 4 +++- .../compositor/intern/COM_OpenCLDevice.h | 11 ++++++++-- .../intern/COM_SharedOperationBuffers.cc | 1 - .../intern/COM_SharedOperationBuffers.h | 9 +++++--- .../intern/COM_TiledExecutionModel.h | 1 + .../compositor/intern/COM_WorkPackage.cc | 1 - .../compositor/intern/COM_WorkPackage.h | 2 +- .../compositor/intern/COM_WorkScheduler.cc | 8 +++---- .../compositor/intern/COM_WorkScheduler.h | 10 ++++----- .../compositor/intern/COM_compositor.cc | 2 -- .../compositor/nodes/COM_AlphaOverNode.cc | 4 ---- .../compositor/nodes/COM_AntiAliasingNode.cc | 1 - .../compositor/nodes/COM_BilateralBlurNode.cc | 2 -- .../blender/compositor/nodes/COM_BlurNode.cc | 2 -- .../compositor/nodes/COM_BokehBlurNode.cc | 5 ----- .../compositor/nodes/COM_BokehImageNode.cc | 1 - .../compositor/nodes/COM_BoxMaskNode.cc | 1 - .../compositor/nodes/COM_BrightnessNode.cc | 1 - .../compositor/nodes/COM_ChannelMatteNode.cc | 1 - .../compositor/nodes/COM_ChromaMatteNode.cc | 1 - .../compositor/nodes/COM_ColorBalanceNode.cc | 3 --- .../nodes/COM_ColorCorrectionNode.cc | 1 - .../compositor/nodes/COM_ColorCurveNode.cc | 1 - .../compositor/nodes/COM_ColorExposureNode.cc | 1 - .../compositor/nodes/COM_ColorMatteNode.cc | 1 - .../blender/compositor/nodes/COM_ColorNode.cc | 1 - .../compositor/nodes/COM_ColorRampNode.cc | 3 --- .../compositor/nodes/COM_ColorSpillNode.cc | 1 - .../compositor/nodes/COM_ColorToBWNode.cc | 1 - .../compositor/nodes/COM_CompositorNode.cc | 1 - .../compositor/nodes/COM_ConvertAlphaNode.cc | 1 - .../compositor/nodes/COM_CornerPinNode.cc | 1 - .../compositor/nodes/COM_CryptomatteNode.cc | 7 ------- .../compositor/nodes/COM_DefocusNode.cc | 6 ------ .../compositor/nodes/COM_DenoiseNode.cc | 3 --- .../compositor/nodes/COM_DespeckleNode.cc | 3 --- .../nodes/COM_DifferenceMatteNode.cc | 1 - .../compositor/nodes/COM_DilateErodeNode.cc | 2 -- .../nodes/COM_DirectionalBlurNode.cc | 2 -- .../compositor/nodes/COM_DisplaceNode.cc | 1 - .../compositor/nodes/COM_DistanceMatteNode.cc | 2 -- .../nodes/COM_DoubleEdgeMaskNode.cc | 1 - .../compositor/nodes/COM_EllipseMaskNode.cc | 1 - .../compositor/nodes/COM_FilterNode.cc | 3 --- .../blender/compositor/nodes/COM_FlipNode.cc | 1 - .../blender/compositor/nodes/COM_GammaNode.cc | 1 - .../blender/compositor/nodes/COM_GlareNode.cc | 2 -- .../COM_HueSaturationValueCorrectNode.cc | 4 ---- .../nodes/COM_HueSaturationValueNode.cc | 4 ---- .../compositor/nodes/COM_IDMaskNode.cc | 1 - .../blender/compositor/nodes/COM_ImageNode.cc | 5 ----- .../compositor/nodes/COM_InpaintNode.cc | 3 --- .../compositor/nodes/COM_InvertNode.cc | 1 - .../compositor/nodes/COM_KeyingNode.cc | 2 -- .../compositor/nodes/COM_KeyingScreenNode.cc | 3 --- .../nodes/COM_LensDistortionNode.cc | 1 - .../nodes/COM_LuminanceMatteNode.cc | 2 -- .../compositor/nodes/COM_MapRangeNode.cc | 1 - .../blender/compositor/nodes/COM_MapUVNode.cc | 1 - .../compositor/nodes/COM_MapValueNode.cc | 1 - .../blender/compositor/nodes/COM_MaskNode.cc | 3 --- .../blender/compositor/nodes/COM_MathNode.cc | 1 - .../blender/compositor/nodes/COM_MixNode.cc | 2 -- .../compositor/nodes/COM_MovieClipNode.cc | 4 +--- .../nodes/COM_MovieDistortionNode.cc | 2 -- .../compositor/nodes/COM_NormalNode.cc | 2 -- .../compositor/nodes/COM_NormalizeNode.cc | 1 - .../compositor/nodes/COM_OutputFileNode.cc | 6 ------ .../compositor/nodes/COM_PixelateNode.cc | 1 - .../nodes/COM_PlaneTrackDeformNode.cc | 5 ----- .../compositor/nodes/COM_PosterizeNode.cc | 1 - .../compositor/nodes/COM_RenderLayersNode.cc | 4 ---- .../compositor/nodes/COM_RotateNode.cc | 1 - .../blender/compositor/nodes/COM_ScaleNode.cc | 2 -- .../compositor/nodes/COM_SetAlphaNode.cc | 1 - .../compositor/nodes/COM_SocketProxyNode.cc | 5 ----- .../compositor/nodes/COM_SplitViewerNode.cc | 4 ---- .../compositor/nodes/COM_Stabilize2dNode.cc | 6 ------ .../compositor/nodes/COM_SwitchViewNode.cc | 1 - .../compositor/nodes/COM_TextureNode.cc | 1 - .../blender/compositor/nodes/COM_TimeNode.cc | 4 +--- .../compositor/nodes/COM_TonemapNode.cc | 1 - .../compositor/nodes/COM_TrackPositionNode.cc | 1 - .../compositor/nodes/COM_TransformNode.cc | 3 --- .../compositor/nodes/COM_TranslateNode.cc | 1 - .../blender/compositor/nodes/COM_ValueNode.cc | 1 - .../compositor/nodes/COM_VectorBlurNode.cc | 1 - .../compositor/nodes/COM_VectorCurveNode.cc | 1 - .../compositor/nodes/COM_ViewLevelsNode.cc | 3 --- .../compositor/nodes/COM_ViewerNode.cc | 5 ----- .../compositor/nodes/COM_ZCombineNode.cc | 8 +------ .../operations/COM_AntiAliasOperation.cc | 6 ------ .../operations/COM_BilateralBlurOperation.cc | 3 --- .../operations/COM_BlurBaseOperation.cc | 3 --- .../operations/COM_BokehBlurOperation.cc | 3 --- .../operations/COM_BokehImageOperation.cc | 1 - .../operations/COM_BoxMaskOperation.cc | 2 -- .../operations/COM_CalculateMeanOperation.cc | 3 +-- ...COM_CalculateStandardDeviationOperation.cc | 3 +-- .../operations/COM_ChannelMatteOperation.cc | 1 - .../operations/COM_ChromaMatteOperation.cc | 1 - .../COM_ColorBalanceASCCDLOperation.cc | 1 - .../COM_ColorBalanceLGGOperation.cc | 1 - .../COM_ColorCorrectionOperation.cc | 1 - .../operations/COM_ColorCurveOperation.cc | 2 -- .../operations/COM_ColorCurveOperation.h | 2 -- .../operations/COM_ColorMatteOperation.cc | 1 - .../operations/COM_ColorSpillOperation.cc | 1 - .../operations/COM_CompositorOperation.cc | 10 +-------- .../operations/COM_CompositorOperation.h | 2 -- .../COM_ConvertDepthToRadiusOperation.cc | 1 - .../COM_ConvolutionEdgeFilterOperation.cc | 1 - .../COM_ConvolutionFilterOperation.cc | 4 ---- .../operations/COM_CropOperation.cc | 1 - .../operations/COM_CurveBaseOperation.h | 3 ++- .../operations/COM_DenoiseOperation.cc | 2 -- .../operations/COM_DespeckleOperation.cc | 2 -- .../COM_DifferenceMatteOperation.cc | 1 - .../operations/COM_DilateErodeOperation.cc | 3 --- .../COM_DirectionalBlurOperation.cc | 4 ---- .../operations/COM_DisplaceOperation.cc | 2 -- .../operations/COM_DisplaceSimpleOperation.cc | 2 -- .../COM_DistanceRGBMatteOperation.cc | 1 - .../COM_DistanceYCCMatteOperation.cc | 1 - .../operations/COM_DoubleEdgeMaskOperation.cc | 3 --- .../operations/COM_EllipseMaskOperation.cc | 4 ---- .../COM_FastGaussianBlurOperation.cc | 2 -- .../operations/COM_GammaCorrectOperation.cc | 1 - .../operations/COM_GammaOperation.cc | 1 - .../COM_GaussianAlphaXBlurOperation.cc | 4 ---- .../COM_GaussianAlphaYBlurOperation.cc | 4 ---- .../COM_GaussianBokehBlurOperation.cc | 2 -- .../operations/COM_GaussianXBlurOperation.cc | 4 ---- .../operations/COM_GaussianYBlurOperation.cc | 4 ---- .../operations/COM_GlareBaseOperation.cc | 1 - .../operations/COM_GlareFogGlowOperation.cc | 1 - .../operations/COM_GlareGhostOperation.cc | 1 - .../operations/COM_GlareStreaksOperation.cc | 1 - .../operations/COM_GlareThresholdOperation.cc | 1 - .../COM_HueSaturationValueCorrectOperation.cc | 2 +- .../operations/COM_ImageOperation.cc | 7 ------- .../operations/COM_InpaintOperation.cc | 3 --- .../operations/COM_KeyingBlurOperation.cc | 5 ----- .../operations/COM_KeyingClipOperation.cc | 5 ----- .../operations/COM_KeyingDespillOperation.cc | 5 ----- .../operations/COM_KeyingOperation.cc | 5 ----- .../operations/COM_KeyingScreenOperation.cc | 6 ------ .../operations/COM_LuminanceMatteOperation.cc | 1 - .../operations/COM_MapUVOperation.cc | 1 - .../operations/COM_MaskOperation.cc | 5 ----- .../operations/COM_MathBaseOperation.cc | 2 -- .../compositor/operations/COM_MixOperation.cc | 2 -- .../operations/COM_MovieClipOperation.cc | 3 --- .../COM_MovieDistortionOperation.cc | 3 --- .../COM_MultilayerImageOperation.cc | 1 - .../COM_OutputFileMultiViewOperation.cc | 11 ---------- .../operations/COM_OutputFileOperation.cc | 6 ------ .../operations/COM_PlaneCornerPinOperation.cc | 9 -------- .../COM_PlaneDistortCommonOperation.cc | 7 ------- .../operations/COM_PlaneTrackOperation.cc | 8 ------- .../operations/COM_PreviewOperation.cc | 12 ----------- .../COM_ProjectorLensDistortionOperation.cc | 3 --- .../operations/COM_ReadBufferOperation.cc | 3 ++- .../operations/COM_RenderLayersProg.cc | 12 ----------- .../operations/COM_RotateOperation.cc | 3 --- .../operations/COM_SMAAOperation.cc | 1 - .../COM_ScreenLensDistortionOperation.cc | 3 +-- .../operations/COM_SetVectorOperation.cc | 1 - .../operations/COM_SplitOperation.cc | 9 -------- .../operations/COM_TextureOperation.cc | 3 --- .../operations/COM_TonemapOperation.cc | 4 +--- .../operations/COM_TrackPositionOperation.cc | 6 ------ .../operations/COM_TransformOperation.cc | 3 --- .../COM_VariableSizeBokehBlurOperation.cc | 4 ---- .../operations/COM_VectorBlurOperation.cc | 5 ----- .../operations/COM_ViewerOperation.cc | 8 ------- .../operations/COM_WriteBufferOperation.cc | 2 -- .../operations/COM_ZCombineOperation.cc | 1 - 214 files changed, 120 insertions(+), 627 deletions(-) diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 9991414aba4..1c3a28670df 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -19,8 +19,8 @@ #pragma once #include "BLI_float2.hh" -#include "BLI_index_range.hh" -#include "BLI_rect.h" + +#include "DNA_vec_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_BufferRange.h b/source/blender/compositor/intern/COM_BufferRange.h index ffdf1f2f1e5..7684ae52fab 100644 --- a/source/blender/compositor/intern/COM_BufferRange.h +++ b/source/blender/compositor/intern/COM_BufferRange.h @@ -19,7 +19,6 @@ #pragma once #include "BLI_assert.h" -#include "BLI_rect.h" #include diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc index 2ca5557e278..dbf813a61b4 100644 --- a/source/blender/compositor/intern/COM_CPUDevice.cc +++ b/source/blender/compositor/intern/COM_CPUDevice.cc @@ -19,8 +19,7 @@ #include "COM_CPUDevice.h" #include "COM_ExecutionGroup.h" - -#include "BLI_rect.h" +#include "COM_NodeOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_ChunkOrder.cc b/source/blender/compositor/intern/COM_ChunkOrder.cc index a03718d720d..3e0246ce893 100644 --- a/source/blender/compositor/intern/COM_ChunkOrder.cc +++ b/source/blender/compositor/intern/COM_ChunkOrder.cc @@ -16,9 +16,9 @@ * Copyright 2011, Blender Foundation. */ -#include "COM_ChunkOrder.h" +#include -#include "BLI_math.h" +#include "COM_ChunkOrder.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index f5f490b0bf6..5d8355c181a 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -17,11 +17,6 @@ */ #include "COM_CompositorContext.h" -#include "COM_defines.h" -#include - -#include "BLI_assert.h" -#include "DNA_userdef_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index ae298c5a65a..1ae596736ae 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -18,16 +18,13 @@ #pragma once -#include "BLI_rect.h" - #include "COM_Enums.h" #include "DNA_color_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" -#include -#include +struct bNodeInstanceHash; namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc index 445a9ce7433..d3031765eae 100644 --- a/source/blender/compositor/intern/COM_ConstantFolder.cc +++ b/source/blender/compositor/intern/COM_ConstantFolder.cc @@ -16,10 +16,11 @@ * Copyright 2021, Blender Foundation. */ -#include "BLI_rect.h" +#include "BLI_map.hh" +#include "BLI_set.hh" #include "COM_ConstantFolder.h" -#include "COM_ConstantOperation.h" +#include "COM_NodeOperationBuilder.h" #include "COM_SetColorOperation.h" #include "COM_SetValueOperation.h" #include "COM_SetVectorOperation.h" diff --git a/source/blender/compositor/intern/COM_ConstantFolder.h b/source/blender/compositor/intern/COM_ConstantFolder.h index 2432e859a5a..f31cb4f4e78 100644 --- a/source/blender/compositor/intern/COM_ConstantFolder.h +++ b/source/blender/compositor/intern/COM_ConstantFolder.h @@ -18,16 +18,12 @@ #pragma once -#include "BLI_map.hh" -#include "BLI_set.hh" -#include "BLI_vector.hh" - -#include "COM_NodeOperationBuilder.h" #include "COM_defines.h" namespace blender::compositor { class NodeOperation; +class NodeOperationBuilder; class ConstantOperation; class MemoryBuffer; diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index ee77beb8a82..346c3c4b600 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -22,7 +22,6 @@ #include "BKE_node.h" -#include "COM_NodeOperation.h" #include "COM_NodeOperationBuilder.h" #include "COM_AlphaOverNode.h" @@ -62,7 +61,6 @@ #include "COM_DistanceMatteNode.h" #include "COM_DoubleEdgeMaskNode.h" #include "COM_EllipseMaskNode.h" -#include "COM_ExecutionSystem.h" #include "COM_FilterNode.h" #include "COM_FlipNode.h" #include "COM_GammaNode.h" diff --git a/source/blender/compositor/intern/COM_Converter.h b/source/blender/compositor/intern/COM_Converter.h index 7f0402d4e70..9cb6b1862b2 100644 --- a/source/blender/compositor/intern/COM_Converter.h +++ b/source/blender/compositor/intern/COM_Converter.h @@ -18,8 +18,6 @@ #pragma once -#include "COM_NodeOperation.h" - #ifdef WITH_CXX_GUARDEDALLOC # include "MEM_guardedalloc.h" #endif @@ -29,6 +27,7 @@ struct bNode; namespace blender::compositor { class Node; +class NodeOperation; class NodeOperationInput; class NodeOperationOutput; class NodeOperationBuilder; diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 9e47aa9fcb5..d0c8311ef6e 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -18,26 +18,16 @@ #include "COM_Debug.h" -#include -#include -#include - extern "C" { #include "BLI_fileops.h" #include "BLI_path_util.h" -#include "BLI_string.h" -#include "BLI_sys_types.h" #include "BKE_appdir.h" -#include "BKE_node.h" -#include "DNA_node_types.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" } -#include "COM_ExecutionSystem.h" -#include "COM_Node.h" - +#include "COM_ExecutionGroup.h" #include "COM_ReadBufferOperation.h" #include "COM_SetValueOperation.h" #include "COM_ViewerOperation.h" diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h index 23d99c7e529..689cd4029f1 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -21,9 +21,11 @@ #include #include +#include "BLI_vector.hh" + #include "COM_ExecutionSystem.h" -#include "COM_NodeOperation.h" -#include "COM_defines.h" +#include "COM_MemoryBuffer.h" +#include "COM_Node.h" namespace blender::compositor { @@ -34,6 +36,7 @@ static constexpr bool COM_GRAPHVIZ_SHOW_NODE_NAME = false; static constexpr bool COM_EXPORT_OPERATION_BUFFERS = false; class Node; +class NodeOperation; class ExecutionSystem; class ExecutionGroup; diff --git a/source/blender/compositor/intern/COM_Device.h b/source/blender/compositor/intern/COM_Device.h index c848672a405..1e3618e6c5e 100644 --- a/source/blender/compositor/intern/COM_Device.h +++ b/source/blender/compositor/intern/COM_Device.h @@ -18,10 +18,10 @@ #pragma once -#include "COM_WorkPackage.h" - namespace blender::compositor { +struct WorkPackage; + /** * \brief Abstract class for device implementations to be used by the Compositor. * devices are queried, initialized and used by the WorkScheduler. diff --git a/source/blender/compositor/intern/COM_Enums.cc b/source/blender/compositor/intern/COM_Enums.cc index 2f20d2652ba..63a5a78436b 100644 --- a/source/blender/compositor/intern/COM_Enums.cc +++ b/source/blender/compositor/intern/COM_Enums.cc @@ -17,7 +17,6 @@ */ #include "COM_Enums.h" -#include "BLI_rect.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc index 505a4066a25..655bc030dec 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.cc +++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc @@ -16,36 +16,21 @@ * Copyright 2011, Blender Foundation. */ -#include -#include -#include -#include - -#include "atomic_ops.h" - +#include "COM_ExecutionGroup.h" #include "COM_ChunkOrder.h" #include "COM_Debug.h" -#include "COM_ExecutionGroup.h" -#include "COM_ExecutionSystem.h" #include "COM_ReadBufferOperation.h" #include "COM_ViewerOperation.h" #include "COM_WorkScheduler.h" #include "COM_WriteBufferOperation.h" #include "COM_defines.h" -#include "BLI_math.h" #include "BLI_rand.hh" -#include "BLI_string.h" #include "BLT_translation.h" -#include "MEM_guardedalloc.h" - #include "PIL_time.h" -#include "WM_api.h" -#include "WM_types.h" - namespace blender::compositor { std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags) diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.h b/source/blender/compositor/intern/COM_ExecutionGroup.h index cb593feabb0..58386c959a4 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.h +++ b/source/blender/compositor/intern/COM_ExecutionGroup.h @@ -22,25 +22,24 @@ # include "MEM_guardedalloc.h" #endif +#include + #include "BLI_array.hh" -#include "BLI_rect.h" #include "BLI_vector.hh" -#include "COM_CompositorContext.h" -#include "COM_Device.h" -#include "COM_MemoryProxy.h" -#include "COM_Node.h" -#include "COM_NodeOperation.h" +#include "COM_Enums.h" #include "COM_WorkPackage.h" -#include + +#include "DNA_node_types.h" +#include "DNA_vec_types.h" namespace blender::compositor { class ExecutionSystem; +class NodeOperation; class MemoryProxy; class MemoryBuffer; class ReadBufferOperation; -class Device; struct ExecutionGroupFlags { bool initialized : 1; diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc index b75b277e92c..0ac99152c9f 100644 --- a/source/blender/compositor/intern/COM_ExecutionModel.cc +++ b/source/blender/compositor/intern/COM_ExecutionModel.cc @@ -17,6 +17,7 @@ */ #include "COM_ExecutionModel.h" +#include "COM_CompositorContext.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_ExecutionModel.h b/source/blender/compositor/intern/COM_ExecutionModel.h index 452861ae4be..f0adc7d3e54 100644 --- a/source/blender/compositor/intern/COM_ExecutionModel.h +++ b/source/blender/compositor/intern/COM_ExecutionModel.h @@ -18,12 +18,9 @@ #pragma once -#include "BLI_rect.h" -#include "BLI_vector.hh" +#include "BLI_span.hh" -#include "COM_ExecutionSystem.h" - -#include +#include "DNA_vec_types.h" #ifdef WITH_CXX_GUARDEDALLOC # include "MEM_guardedalloc.h" @@ -31,6 +28,8 @@ namespace blender::compositor { +class CompositorContext; +class ExecutionSystem; class NodeOperation; /** diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index c92e292c74f..9b1ec823f75 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -18,14 +18,13 @@ #include "COM_ExecutionSystem.h" -#include "BLI_utildefines.h" -#include "PIL_time.h" - #include "COM_Debug.h" +#include "COM_ExecutionGroup.h" #include "COM_FullFrameExecutionModel.h" #include "COM_NodeOperation.h" #include "COM_NodeOperationBuilder.h" #include "COM_TiledExecutionModel.h" +#include "COM_WorkPackage.h" #include "COM_WorkScheduler.h" #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index bce96db52c7..eb0ad805217 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -16,22 +16,23 @@ * Copyright 2011, Blender Foundation. */ -class ExecutionGroup; - #pragma once -#include "BKE_text.h" +#include -#include "COM_ExecutionGroup.h" -#include "COM_Node.h" -#include "COM_NodeOperation.h" +#include "atomic_ops.h" + +#include "BLI_index_range.hh" +#include "BLI_threads.h" +#include "BLI_vector.hh" + +#include "COM_CompositorContext.h" #include "COM_SharedOperationBuffers.h" #include "DNA_color_types.h" #include "DNA_node_types.h" - -#include "BLI_vector.hh" -#include "atomic_ops.h" +#include "DNA_scene_types.h" +#include "DNA_vec_types.h" namespace blender::compositor { @@ -118,7 +119,9 @@ namespace blender::compositor { */ /* Forward declarations. */ +class ExecutionGroup; class ExecutionModel; +class NodeOperation; /** * \brief the ExecutionSystem contains the whole compositor tree. diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index c44a168390b..ce00fff5858 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -17,14 +17,13 @@ */ #include "COM_FullFrameExecutionModel.h" -#include "COM_Debug.h" -#include "COM_ExecutionGroup.h" -#include "COM_ReadBufferOperation.h" -#include "COM_ViewerOperation.h" -#include "COM_WorkScheduler.h" #include "BLT_translation.h" +#include "COM_Debug.h" +#include "COM_ViewerOperation.h" +#include "COM_WorkScheduler.h" + #ifdef WITH_CXX_GUARDEDALLOC # include "MEM_guardedalloc.h" #endif diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h index 66dfb8f052c..36d42886c27 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h @@ -18,6 +18,9 @@ #pragma once +#include "BLI_vector.hh" + +#include "COM_Enums.h" #include "COM_ExecutionModel.h" #ifdef WITH_CXX_GUARDEDALLOC @@ -27,7 +30,11 @@ namespace blender::compositor { /* Forward declarations. */ -class ExecutionGroup; +class CompositorContext; +class ExecutionSystem; +class MemoryBuffer; +class NodeOperation; +class SharedOperationBuffers; /** * Fully renders operations in order from inputs to outputs. diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index f57f0f055bf..b80be46d46a 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -18,9 +18,10 @@ #include "COM_MemoryBuffer.h" +#include "COM_MemoryProxy.h" + #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" -#include "MEM_guardedalloc.h" #define ASSERT_BUFFER_CONTAINS_AREA(buf, area) \ BLI_assert(BLI_rcti_inside_rcti(&(buf)->get_rect(), &(area))) diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 9e173f73f63..1eafc42ea69 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -21,12 +21,13 @@ #include "COM_BufferArea.h" #include "COM_BufferRange.h" #include "COM_BuffersIterator.h" -#include "COM_ExecutionGroup.h" -#include "COM_MemoryProxy.h" +#include "COM_Enums.h" -#include "BLI_math.h" +#include "BLI_math_interp.h" #include "BLI_rect.h" +struct ImBuf; + namespace blender::compositor { /** diff --git a/source/blender/compositor/intern/COM_MemoryProxy.cc b/source/blender/compositor/intern/COM_MemoryProxy.cc index 6023850c944..58dea234ced 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.cc +++ b/source/blender/compositor/intern/COM_MemoryProxy.cc @@ -17,11 +17,8 @@ */ #include "COM_MemoryProxy.h" - #include "COM_MemoryBuffer.h" -#include "BLI_rect.h" - namespace blender::compositor { MemoryProxy::MemoryProxy(DataType datatype) diff --git a/source/blender/compositor/intern/COM_MetaData.cc b/source/blender/compositor/intern/COM_MetaData.cc index a6fb84dfb87..5775a7eab65 100644 --- a/source/blender/compositor/intern/COM_MetaData.cc +++ b/source/blender/compositor/intern/COM_MetaData.cc @@ -22,8 +22,6 @@ #include "RE_pipeline.h" -#include - namespace blender::compositor { void MetaData::add(const blender::StringRef key, const blender::StringRef value) diff --git a/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h b/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h index 3daa9eec474..c1835e85949 100644 --- a/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h +++ b/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h @@ -18,6 +18,8 @@ #pragma once +#include "BLI_array.h" + #include "COM_MultiThreadedOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_Node.cc b/source/blender/compositor/intern/COM_Node.cc index 6ac48e3646c..2cd56c1d7c1 100644 --- a/source/blender/compositor/intern/COM_Node.cc +++ b/source/blender/compositor/intern/COM_Node.cc @@ -16,20 +16,10 @@ * Copyright 2011, Blender Foundation. */ -#include - #include "BKE_node.h" #include "RNA_access.h" -#include "COM_ExecutionSystem.h" -#include "COM_NodeOperation.h" -#include "COM_TranslateOperation.h" - -#include "COM_SocketProxyNode.h" - -#include "COM_defines.h" - #include "COM_Node.h" /* own include */ namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h index 28b59397af9..08d0b9a4fd5 100644 --- a/source/blender/compositor/intern/COM_Node.h +++ b/source/blender/compositor/intern/COM_Node.h @@ -22,9 +22,6 @@ #include "DNA_node_types.h" -#include -#include - /* common node includes * added here so node files don't have to include themselves */ diff --git a/source/blender/compositor/intern/COM_NodeConverter.cc b/source/blender/compositor/intern/COM_NodeConverter.cc index 49a2e7988c4..73f64c7cfd4 100644 --- a/source/blender/compositor/intern/COM_NodeConverter.cc +++ b/source/blender/compositor/intern/COM_NodeConverter.cc @@ -18,9 +18,7 @@ #include "BLI_utildefines.h" -#include "COM_Debug.h" - -#include "COM_NodeOperation.h" +#include "COM_Node.h" #include "COM_NodeOperationBuilder.h" #include "COM_SetColorOperation.h" #include "COM_SetValueOperation.h" diff --git a/source/blender/compositor/intern/COM_NodeGraph.cc b/source/blender/compositor/intern/COM_NodeGraph.cc index 1872fbcf656..0c94dfce409 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.cc +++ b/source/blender/compositor/intern/COM_NodeGraph.cc @@ -18,17 +18,12 @@ #include -#include "BLI_listbase.h" -#include "BLI_utildefines.h" - #include "DNA_node_types.h" #include "BKE_node.h" -#include "COM_CompositorContext.h" #include "COM_Converter.h" #include "COM_Debug.h" -#include "COM_Node.h" #include "COM_SocketProxyNode.h" #include "COM_NodeGraph.h" /* own include */ diff --git a/source/blender/compositor/intern/COM_NodeGraph.h b/source/blender/compositor/intern/COM_NodeGraph.h index dfcc6c2fcf9..12aca8f6069 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.h +++ b/source/blender/compositor/intern/COM_NodeGraph.h @@ -18,11 +18,6 @@ #pragma once -#include "BLI_vector.hh" - -#include -#include - #include "DNA_node_types.h" #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index a6a395261f2..a34fdd64da9 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -17,13 +17,10 @@ */ #include -#include -#include #include "COM_BufferOperation.h" #include "COM_ExecutionSystem.h" #include "COM_ReadBufferOperation.h" -#include "COM_defines.h" #include "COM_NodeOperation.h" /* own include */ diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index f507665bee3..0483090d8ca 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -18,32 +18,32 @@ #pragma once +#include #include -#include -#include #include "BLI_ghash.h" #include "BLI_hash.hh" -#include "BLI_math_color.h" -#include "BLI_math_vector.h" +#include "BLI_rect.h" +#include "BLI_span.hh" #include "BLI_threads.h" +#include "BLI_utildefines.h" #include "COM_Enums.h" #include "COM_MemoryBuffer.h" -#include "COM_MemoryProxy.h" #include "COM_MetaData.h" -#include "COM_Node.h" #include "clew.h" +#include "DNA_node_types.h" + namespace blender::compositor { class OpenCLDevice; class ReadBufferOperation; -class WriteBufferOperation; class ExecutionSystem; - class NodeOperation; +class NodeOperationOutput; + typedef NodeOperation SocketReader; /** diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index acb7f61f6dd..2801cb401be 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -16,24 +16,19 @@ * Copyright 2013, Blender Foundation. */ +#include + #include "BLI_multi_value_map.hh" -#include "BLI_utildefines.h" #include "COM_Converter.h" #include "COM_Debug.h" -#include "COM_ExecutionSystem.h" -#include "COM_Node.h" -#include "COM_NodeConverter.h" -#include "COM_SocketProxyNode.h" -#include "COM_NodeOperation.h" +#include "COM_ExecutionGroup.h" #include "COM_PreviewOperation.h" #include "COM_ReadBufferOperation.h" #include "COM_SetColorOperation.h" #include "COM_SetValueOperation.h" #include "COM_SetVectorOperation.h" -#include "COM_SocketProxyOperation.h" -#include "COM_TranslateOperation.h" #include "COM_ViewerOperation.h" #include "COM_WriteBufferOperation.h" diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h index 1f9c86b51cd..a6a6201bbb5 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h @@ -18,9 +18,8 @@ #pragma once -#include -#include -#include +#include "BLI_map.hh" +#include "BLI_vector.hh" #include "COM_NodeGraph.h" diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cc b/source/blender/compositor/intern/COM_OpenCLDevice.cc index 3409c8fa3bc..815bac4009e 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.cc +++ b/source/blender/compositor/intern/COM_OpenCLDevice.cc @@ -17,7 +17,9 @@ */ #include "COM_OpenCLDevice.h" -#include "COM_WorkScheduler.h" + +#include "COM_ExecutionGroup.h" +#include "COM_ReadBufferOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.h b/source/blender/compositor/intern/COM_OpenCLDevice.h index 826b0457a49..3de9d01c28c 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.h +++ b/source/blender/compositor/intern/COM_OpenCLDevice.h @@ -20,13 +20,20 @@ class OpenCLDevice; #pragma once +#include + #include "COM_Device.h" -#include "COM_ReadBufferOperation.h" -#include "COM_WorkScheduler.h" + #include "clew.h" namespace blender::compositor { +class NodeOperation; +class MemoryBuffer; +class ReadBufferOperation; + +typedef NodeOperation SocketReader; + /** * \brief device representing an GPU OpenCL device. * an instance of this class represents a single cl_device diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc index 55153bd4f0a..c67251b2d20 100644 --- a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc @@ -17,7 +17,6 @@ */ #include "COM_SharedOperationBuffers.h" -#include "BLI_rect.h" #include "COM_NodeOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h index 4461ba75cbe..d7d0dffabb5 100644 --- a/source/blender/compositor/intern/COM_SharedOperationBuffers.h +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h @@ -19,16 +19,19 @@ #pragma once #include "BLI_map.hh" -#include "BLI_span.hh" #include "BLI_vector.hh" -#include "COM_MemoryBuffer.h" + +#include "DNA_vec_types.h" + #ifdef WITH_CXX_GUARDEDALLOC # include "MEM_guardedalloc.h" #endif -#include namespace blender::compositor { +class MemoryBuffer; +class NodeOperation; + /** * Stores and shares operations rendered buffers including render data. Buffers are * disposed once all dependent operations have finished reading them. diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.h b/source/blender/compositor/intern/COM_TiledExecutionModel.h index 05a795b9f07..1f5345241e2 100644 --- a/source/blender/compositor/intern/COM_TiledExecutionModel.h +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.h @@ -18,6 +18,7 @@ #pragma once +#include "COM_Enums.h" #include "COM_ExecutionModel.h" #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/intern/COM_WorkPackage.cc b/source/blender/compositor/intern/COM_WorkPackage.cc index ea78c0d6333..7db44226574 100644 --- a/source/blender/compositor/intern/COM_WorkPackage.cc +++ b/source/blender/compositor/intern/COM_WorkPackage.cc @@ -18,7 +18,6 @@ #include "COM_WorkPackage.h" -#include "COM_Enums.h" #include "COM_ExecutionGroup.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h index 20fca89aa4c..5ea3237d412 100644 --- a/source/blender/compositor/intern/COM_WorkPackage.h +++ b/source/blender/compositor/intern/COM_WorkPackage.h @@ -24,7 +24,7 @@ #include "COM_Enums.h" -#include "BLI_rect.h" +#include "DNA_vec_types.h" #include #include diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index a08f9dd284c..22f60ec17fe 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -16,15 +16,14 @@ * Copyright 2011, Blender Foundation. */ -#include -#include +#include "COM_WorkScheduler.h" #include "COM_CPUDevice.h" +#include "COM_CompositorContext.h" +#include "COM_ExecutionGroup.h" #include "COM_OpenCLDevice.h" #include "COM_OpenCLKernels.cl.h" -#include "COM_WorkScheduler.h" #include "COM_WriteBufferOperation.h" -#include "COM_compositor.h" #include "clew.h" @@ -33,7 +32,6 @@ #include "BLI_task.h" #include "BLI_threads.h" #include "BLI_vector.hh" -#include "PIL_time.h" #include "BKE_global.h" diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h index 297943aa63b..d0fa3286a3b 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.h +++ b/source/blender/compositor/intern/COM_WorkScheduler.h @@ -18,14 +18,12 @@ #pragma once -#include "COM_ExecutionGroup.h" - -#include "COM_Device.h" -#include "COM_WorkPackage.h" -#include "COM_defines.h" - namespace blender::compositor { +struct WorkPackage; + +class CompositorContext; + /** \brief the workscheduler * \ingroup execution */ diff --git a/source/blender/compositor/intern/COM_compositor.cc b/source/blender/compositor/intern/COM_compositor.cc index c05234f3bd0..ed32a477384 100644 --- a/source/blender/compositor/intern/COM_compositor.cc +++ b/source/blender/compositor/intern/COM_compositor.cc @@ -24,10 +24,8 @@ #include "BKE_scene.h" #include "COM_ExecutionSystem.h" -#include "COM_MovieDistortionOperation.h" #include "COM_WorkScheduler.h" #include "COM_compositor.h" -#include "clew.h" static struct { bool is_initialized = false; diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.cc b/source/blender/compositor/nodes/COM_AlphaOverNode.cc index c9038886b0d..603f713a43f 100644 --- a/source/blender/compositor/nodes/COM_AlphaOverNode.cc +++ b/source/blender/compositor/nodes/COM_AlphaOverNode.cc @@ -21,10 +21,6 @@ #include "COM_AlphaOverKeyOperation.h" #include "COM_AlphaOverMixedOperation.h" #include "COM_AlphaOverPremultiplyOperation.h" -#include "COM_MixOperation.h" - -#include "COM_SetValueOperation.h" -#include "DNA_material_types.h" /* the ramp types */ namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc index af4832665df..8cbb3994939 100644 --- a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc +++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc @@ -20,7 +20,6 @@ #include "COM_AntiAliasingNode.h" #include "COM_SMAAOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_BilateralBlurNode.cc b/source/blender/compositor/nodes/COM_BilateralBlurNode.cc index 1b9da789d3c..40a70e37e4b 100644 --- a/source/blender/compositor/nodes/COM_BilateralBlurNode.cc +++ b/source/blender/compositor/nodes/COM_BilateralBlurNode.cc @@ -18,8 +18,6 @@ #include "COM_BilateralBlurNode.h" #include "COM_BilateralBlurOperation.h" -#include "COM_ExecutionSystem.h" -#include "DNA_node_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_BlurNode.cc b/source/blender/compositor/nodes/COM_BlurNode.cc index c10bc2a05f0..5dfd5c8d0bc 100644 --- a/source/blender/compositor/nodes/COM_BlurNode.cc +++ b/source/blender/compositor/nodes/COM_BlurNode.cc @@ -17,7 +17,6 @@ */ #include "COM_BlurNode.h" -#include "COM_ExecutionSystem.h" #include "COM_FastGaussianBlurOperation.h" #include "COM_GammaCorrectOperation.h" #include "COM_GaussianAlphaXBlurOperation.h" @@ -27,7 +26,6 @@ #include "COM_GaussianYBlurOperation.h" #include "COM_MathBaseOperation.h" #include "COM_SetValueOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.cc b/source/blender/compositor/nodes/COM_BokehBlurNode.cc index c51a98c0f82..c18f6351cbd 100644 --- a/source/blender/compositor/nodes/COM_BokehBlurNode.cc +++ b/source/blender/compositor/nodes/COM_BokehBlurNode.cc @@ -18,12 +18,7 @@ #include "COM_BokehBlurNode.h" #include "COM_BokehBlurOperation.h" -#include "COM_ConvertDepthToRadiusOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_VariableSizeBokehBlurOperation.h" -#include "DNA_camera_types.h" -#include "DNA_node_types.h" -#include "DNA_object_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.cc b/source/blender/compositor/nodes/COM_BokehImageNode.cc index 2b0a47c76bc..64237571e90 100644 --- a/source/blender/compositor/nodes/COM_BokehImageNode.cc +++ b/source/blender/compositor/nodes/COM_BokehImageNode.cc @@ -18,7 +18,6 @@ #include "COM_BokehImageNode.h" #include "COM_BokehImageOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.cc b/source/blender/compositor/nodes/COM_BoxMaskNode.cc index 8017e063a69..6fcaeada406 100644 --- a/source/blender/compositor/nodes/COM_BoxMaskNode.cc +++ b/source/blender/compositor/nodes/COM_BoxMaskNode.cc @@ -18,7 +18,6 @@ #include "COM_BoxMaskNode.h" #include "COM_BoxMaskOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_ScaleOperation.h" #include "COM_SetValueOperation.h" diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.cc b/source/blender/compositor/nodes/COM_BrightnessNode.cc index b64f1fea99f..e01ec3d8729 100644 --- a/source/blender/compositor/nodes/COM_BrightnessNode.cc +++ b/source/blender/compositor/nodes/COM_BrightnessNode.cc @@ -18,7 +18,6 @@ #include "COM_BrightnessNode.h" #include "COM_BrightnessOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc index d0f72274aea..ed49d9c36a9 100644 --- a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc @@ -17,7 +17,6 @@ */ #include "COM_ChannelMatteNode.h" -#include "BKE_node.h" #include "COM_ChannelMatteOperation.h" #include "COM_ConvertOperation.h" #include "COM_SetAlphaMultiplyOperation.h" diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc index 9abf183a843..33516181152 100644 --- a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc @@ -17,7 +17,6 @@ */ #include "COM_ChromaMatteNode.h" -#include "BKE_node.h" #include "COM_ChromaMatteOperation.h" #include "COM_ConvertOperation.h" #include "COM_SetAlphaMultiplyOperation.h" diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc index 03e4e143061..8f9d05ad1e1 100644 --- a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc +++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc @@ -17,11 +17,8 @@ */ #include "COM_ColorBalanceNode.h" -#include "BKE_node.h" #include "COM_ColorBalanceASCCDLOperation.h" #include "COM_ColorBalanceLGGOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_MixOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc index 6397b1d8e4b..b5bf118d7de 100644 --- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc +++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc @@ -18,7 +18,6 @@ #include "COM_ColorCorrectionNode.h" #include "COM_ColorCorrectionOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.cc b/source/blender/compositor/nodes/COM_ColorCurveNode.cc index 774dd689a46..4a82a42b036 100644 --- a/source/blender/compositor/nodes/COM_ColorCurveNode.cc +++ b/source/blender/compositor/nodes/COM_ColorCurveNode.cc @@ -18,7 +18,6 @@ #include "COM_ColorCurveNode.h" #include "COM_ColorCurveOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ColorExposureNode.cc b/source/blender/compositor/nodes/COM_ColorExposureNode.cc index a8f164e6b66..83507395108 100644 --- a/source/blender/compositor/nodes/COM_ColorExposureNode.cc +++ b/source/blender/compositor/nodes/COM_ColorExposureNode.cc @@ -18,7 +18,6 @@ #include "COM_ColorExposureNode.h" #include "COM_ColorExposureOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.cc b/source/blender/compositor/nodes/COM_ColorMatteNode.cc index eadb8ce4f96..345cdb3ecf7 100644 --- a/source/blender/compositor/nodes/COM_ColorMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ColorMatteNode.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorMatteNode.h" -#include "BKE_node.h" #include "COM_ColorMatteOperation.h" #include "COM_ConvertOperation.h" #include "COM_SetAlphaMultiplyOperation.h" diff --git a/source/blender/compositor/nodes/COM_ColorNode.cc b/source/blender/compositor/nodes/COM_ColorNode.cc index f8277645a4b..e79f2eb97af 100644 --- a/source/blender/compositor/nodes/COM_ColorNode.cc +++ b/source/blender/compositor/nodes/COM_ColorNode.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorNode.h" -#include "COM_ExecutionSystem.h" #include "COM_SetColorOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.cc b/source/blender/compositor/nodes/COM_ColorRampNode.cc index 6b44ef3057e..e4346459dd4 100644 --- a/source/blender/compositor/nodes/COM_ColorRampNode.cc +++ b/source/blender/compositor/nodes/COM_ColorRampNode.cc @@ -17,11 +17,8 @@ */ #include "COM_ColorRampNode.h" -#include "BKE_node.h" #include "COM_ColorRampOperation.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" -#include "DNA_texture_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.cc b/source/blender/compositor/nodes/COM_ColorSpillNode.cc index 6119e635e59..d0bd4eb12ea 100644 --- a/source/blender/compositor/nodes/COM_ColorSpillNode.cc +++ b/source/blender/compositor/nodes/COM_ColorSpillNode.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorSpillNode.h" -#include "BKE_node.h" #include "COM_ColorSpillOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ColorToBWNode.cc b/source/blender/compositor/nodes/COM_ColorToBWNode.cc index dcedfc19e4d..ac051ab0d55 100644 --- a/source/blender/compositor/nodes/COM_ColorToBWNode.cc +++ b/source/blender/compositor/nodes/COM_ColorToBWNode.cc @@ -19,7 +19,6 @@ #include "COM_ColorToBWNode.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cc b/source/blender/compositor/nodes/COM_CompositorNode.cc index 262fa550915..10c87b8c886 100644 --- a/source/blender/compositor/nodes/COM_CompositorNode.cc +++ b/source/blender/compositor/nodes/COM_CompositorNode.cc @@ -18,7 +18,6 @@ #include "COM_CompositorNode.h" #include "COM_CompositorOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc index ac4e45357dc..649d851fe84 100644 --- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc +++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc @@ -18,7 +18,6 @@ #include "COM_ConvertAlphaNode.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_CornerPinNode.cc b/source/blender/compositor/nodes/COM_CornerPinNode.cc index 3cfa20f4e05..fb188dc45f8 100644 --- a/source/blender/compositor/nodes/COM_CornerPinNode.cc +++ b/source/blender/compositor/nodes/COM_CornerPinNode.cc @@ -16,7 +16,6 @@ */ #include "COM_CornerPinNode.h" -#include "COM_ExecutionSystem.h" #include "COM_PlaneCornerPinOperation.h" diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index c04d98d6a2b..21596d5aebb 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -18,18 +18,11 @@ #include "COM_CryptomatteNode.h" #include "BKE_node.h" -#include "BLI_assert.h" -#include "BLI_hash_mm3.h" -#include "BLI_listbase.h" -#include "BLI_string.h" #include "COM_ConvertOperation.h" -#include "COM_CryptomatteOperation.h" #include "COM_MultilayerImageOperation.h" #include "COM_RenderLayersProg.h" #include "COM_SetAlphaMultiplyOperation.h" #include "COM_SetColorOperation.h" -#include -#include namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_DefocusNode.cc b/source/blender/compositor/nodes/COM_DefocusNode.cc index 2023e4f7118..6f1361df284 100644 --- a/source/blender/compositor/nodes/COM_DefocusNode.cc +++ b/source/blender/compositor/nodes/COM_DefocusNode.cc @@ -19,16 +19,10 @@ #include "COM_DefocusNode.h" #include "COM_BokehImageOperation.h" #include "COM_ConvertDepthToRadiusOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_FastGaussianBlurOperation.h" #include "COM_GammaCorrectOperation.h" #include "COM_MathBaseOperation.h" #include "COM_SetValueOperation.h" #include "COM_VariableSizeBokehBlurOperation.h" -#include "DNA_camera_types.h" -#include "DNA_node_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.cc b/source/blender/compositor/nodes/COM_DenoiseNode.cc index cc9328414ef..e576966626c 100644 --- a/source/blender/compositor/nodes/COM_DenoiseNode.cc +++ b/source/blender/compositor/nodes/COM_DenoiseNode.cc @@ -17,9 +17,6 @@ */ #include "COM_DenoiseNode.h" #include "COM_DenoiseOperation.h" -#include "COM_MixOperation.h" -#include "COM_SetValueOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.cc b/source/blender/compositor/nodes/COM_DespeckleNode.cc index beda479025d..1a068112210 100644 --- a/source/blender/compositor/nodes/COM_DespeckleNode.cc +++ b/source/blender/compositor/nodes/COM_DespeckleNode.cc @@ -17,10 +17,7 @@ */ #include "COM_DespeckleNode.h" -#include "BLI_math.h" #include "COM_DespeckleOperation.h" -#include "COM_ExecutionSystem.h" -#include "DNA_scene_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc index 8c989bfc04e..9700b761eca 100644 --- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc @@ -17,7 +17,6 @@ */ #include "COM_DifferenceMatteNode.h" -#include "BKE_node.h" #include "COM_DifferenceMatteOperation.h" #include "COM_SetAlphaMultiplyOperation.h" diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.cc b/source/blender/compositor/nodes/COM_DilateErodeNode.cc index 1867014f64b..dbd03b02b8e 100644 --- a/source/blender/compositor/nodes/COM_DilateErodeNode.cc +++ b/source/blender/compositor/nodes/COM_DilateErodeNode.cc @@ -17,10 +17,8 @@ */ #include "COM_DilateErodeNode.h" -#include "BLI_math.h" #include "COM_AntiAliasOperation.h" #include "COM_DilateErodeOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_GaussianAlphaXBlurOperation.h" #include "COM_GaussianAlphaYBlurOperation.h" diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc index 90c4236bce8..dce2a1a7911 100644 --- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc +++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc @@ -18,8 +18,6 @@ #include "COM_DirectionalBlurNode.h" #include "COM_DirectionalBlurOperation.h" -#include "COM_ExecutionSystem.h" -#include "DNA_node_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_DisplaceNode.cc b/source/blender/compositor/nodes/COM_DisplaceNode.cc index f2ec750c595..d13c0c347fb 100644 --- a/source/blender/compositor/nodes/COM_DisplaceNode.cc +++ b/source/blender/compositor/nodes/COM_DisplaceNode.cc @@ -19,7 +19,6 @@ #include "COM_DisplaceNode.h" #include "COM_DisplaceOperation.h" #include "COM_DisplaceSimpleOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc index 4450c4a2f4a..26886a6580f 100644 --- a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc @@ -17,9 +17,7 @@ */ #include "COM_DistanceMatteNode.h" -#include "BKE_node.h" #include "COM_ConvertOperation.h" -#include "COM_DistanceRGBMatteOperation.h" #include "COM_DistanceYCCMatteOperation.h" #include "COM_SetAlphaMultiplyOperation.h" diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc index 847dcc2f8f1..273f0ee7026 100644 --- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc +++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc @@ -18,7 +18,6 @@ #include "COM_DoubleEdgeMaskNode.h" #include "COM_DoubleEdgeMaskOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc index 752597ef937..4f3703dceb1 100644 --- a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc +++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc @@ -18,7 +18,6 @@ #include "COM_EllipseMaskNode.h" #include "COM_EllipseMaskOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_ScaleOperation.h" #include "COM_SetValueOperation.h" diff --git a/source/blender/compositor/nodes/COM_FilterNode.cc b/source/blender/compositor/nodes/COM_FilterNode.cc index 351219155c2..4cdb00a87f4 100644 --- a/source/blender/compositor/nodes/COM_FilterNode.cc +++ b/source/blender/compositor/nodes/COM_FilterNode.cc @@ -19,9 +19,6 @@ #include "COM_FilterNode.h" #include "BKE_node.h" #include "COM_ConvolutionEdgeFilterOperation.h" -#include "COM_ConvolutionFilterOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_MixOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_FlipNode.cc b/source/blender/compositor/nodes/COM_FlipNode.cc index bca6cd3c4f7..1c613bde674 100644 --- a/source/blender/compositor/nodes/COM_FlipNode.cc +++ b/source/blender/compositor/nodes/COM_FlipNode.cc @@ -18,7 +18,6 @@ #include "COM_FlipNode.h" -#include "COM_ExecutionSystem.h" #include "COM_FlipOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_GammaNode.cc b/source/blender/compositor/nodes/COM_GammaNode.cc index 52148a80a8f..31253a90462 100644 --- a/source/blender/compositor/nodes/COM_GammaNode.cc +++ b/source/blender/compositor/nodes/COM_GammaNode.cc @@ -17,7 +17,6 @@ */ #include "COM_GammaNode.h" -#include "COM_ExecutionSystem.h" #include "COM_GammaOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_GlareNode.cc b/source/blender/compositor/nodes/COM_GlareNode.cc index 9c26d7c86a9..bbbbb7fa7ee 100644 --- a/source/blender/compositor/nodes/COM_GlareNode.cc +++ b/source/blender/compositor/nodes/COM_GlareNode.cc @@ -17,7 +17,6 @@ */ #include "COM_GlareNode.h" -#include "COM_FastGaussianBlurOperation.h" #include "COM_GlareFogGlowOperation.h" #include "COM_GlareGhostOperation.h" #include "COM_GlareSimpleStarOperation.h" @@ -25,7 +24,6 @@ #include "COM_GlareThresholdOperation.h" #include "COM_MixOperation.h" #include "COM_SetValueOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc index e7b1664c354..3cf4b218bb2 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc +++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc @@ -19,12 +19,8 @@ #include "COM_HueSaturationValueCorrectNode.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_HueSaturationValueCorrectOperation.h" #include "COM_MixOperation.h" -#include "COM_SetColorOperation.h" -#include "COM_SetValueOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc index 29e5f39a144..7711c306f24 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc +++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc @@ -20,11 +20,7 @@ #include "COM_ChangeHSVOperation.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_MixOperation.h" -#include "COM_SetColorOperation.h" -#include "COM_SetValueOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.cc b/source/blender/compositor/nodes/COM_IDMaskNode.cc index 761cb8b98cf..0a7ed521afb 100644 --- a/source/blender/compositor/nodes/COM_IDMaskNode.cc +++ b/source/blender/compositor/nodes/COM_IDMaskNode.cc @@ -17,7 +17,6 @@ */ #include "COM_IDMaskNode.h" -#include "COM_ExecutionSystem.h" #include "COM_IDMaskOperation.h" #include "COM_SMAAOperation.h" diff --git a/source/blender/compositor/nodes/COM_ImageNode.cc b/source/blender/compositor/nodes/COM_ImageNode.cc index 20476144efa..992d5697109 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cc +++ b/source/blender/compositor/nodes/COM_ImageNode.cc @@ -17,14 +17,9 @@ */ #include "COM_ImageNode.h" -#include "BKE_node.h" -#include "BLI_utildefines.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_ImageOperation.h" #include "COM_MultilayerImageOperation.h" -#include "COM_SeparateColorNode.h" #include "COM_SetColorOperation.h" #include "COM_SetValueOperation.h" #include "COM_SetVectorOperation.h" diff --git a/source/blender/compositor/nodes/COM_InpaintNode.cc b/source/blender/compositor/nodes/COM_InpaintNode.cc index 01ec5523939..93fb051090c 100644 --- a/source/blender/compositor/nodes/COM_InpaintNode.cc +++ b/source/blender/compositor/nodes/COM_InpaintNode.cc @@ -17,10 +17,7 @@ */ #include "COM_InpaintNode.h" -#include "BLI_math.h" -#include "COM_ExecutionSystem.h" #include "COM_InpaintOperation.h" -#include "DNA_scene_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_InvertNode.cc b/source/blender/compositor/nodes/COM_InvertNode.cc index 5fe2033227f..cb15f3b28b0 100644 --- a/source/blender/compositor/nodes/COM_InvertNode.cc +++ b/source/blender/compositor/nodes/COM_InvertNode.cc @@ -18,7 +18,6 @@ #include "COM_InvertNode.h" #include "BKE_node.h" -#include "COM_ExecutionSystem.h" #include "COM_InvertOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_KeyingNode.cc b/source/blender/compositor/nodes/COM_KeyingNode.cc index 0af328a3601..c928b55fbb0 100644 --- a/source/blender/compositor/nodes/COM_KeyingNode.cc +++ b/source/blender/compositor/nodes/COM_KeyingNode.cc @@ -18,8 +18,6 @@ #include "COM_KeyingNode.h" -#include "COM_ExecutionSystem.h" - #include "COM_KeyingBlurOperation.h" #include "COM_KeyingClipOperation.h" #include "COM_KeyingDespillOperation.h" diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc index 43574d02d80..e405c01ddf2 100644 --- a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc +++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc @@ -17,11 +17,8 @@ */ #include "COM_KeyingScreenNode.h" -#include "COM_ExecutionSystem.h" #include "COM_KeyingScreenOperation.h" -#include "DNA_movieclip_types.h" - namespace blender::compositor { KeyingScreenNode::KeyingScreenNode(bNode *editorNode) : Node(editorNode) diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.cc b/source/blender/compositor/nodes/COM_LensDistortionNode.cc index f5226d31989..46223c1f294 100644 --- a/source/blender/compositor/nodes/COM_LensDistortionNode.cc +++ b/source/blender/compositor/nodes/COM_LensDistortionNode.cc @@ -17,7 +17,6 @@ */ #include "COM_LensDistortionNode.h" -#include "COM_ExecutionSystem.h" #include "COM_ProjectorLensDistortionOperation.h" #include "COM_ScreenLensDistortionOperation.h" diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc index 920da53231f..e38aff91431 100644 --- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc @@ -17,8 +17,6 @@ */ #include "COM_LuminanceMatteNode.h" -#include "BKE_node.h" -#include "COM_ConvertOperation.h" #include "COM_LuminanceMatteOperation.h" #include "COM_SetAlphaMultiplyOperation.h" diff --git a/source/blender/compositor/nodes/COM_MapRangeNode.cc b/source/blender/compositor/nodes/COM_MapRangeNode.cc index 718a6d9e47b..01288b779ec 100644 --- a/source/blender/compositor/nodes/COM_MapRangeNode.cc +++ b/source/blender/compositor/nodes/COM_MapRangeNode.cc @@ -18,7 +18,6 @@ #include "COM_MapRangeNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MapRangeOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_MapUVNode.cc b/source/blender/compositor/nodes/COM_MapUVNode.cc index bbf9e8f3aeb..ddda1e2db1d 100644 --- a/source/blender/compositor/nodes/COM_MapUVNode.cc +++ b/source/blender/compositor/nodes/COM_MapUVNode.cc @@ -17,7 +17,6 @@ */ #include "COM_MapUVNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MapUVOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_MapValueNode.cc b/source/blender/compositor/nodes/COM_MapValueNode.cc index ae48bda6cb8..9c6be55e93b 100644 --- a/source/blender/compositor/nodes/COM_MapValueNode.cc +++ b/source/blender/compositor/nodes/COM_MapValueNode.cc @@ -18,7 +18,6 @@ #include "COM_MapValueNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MapValueOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc index b5b23798160..27488c8a6c8 100644 --- a/source/blender/compositor/nodes/COM_MaskNode.cc +++ b/source/blender/compositor/nodes/COM_MaskNode.cc @@ -17,11 +17,8 @@ */ #include "COM_MaskNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MaskOperation.h" -#include "DNA_mask_types.h" - namespace blender::compositor { MaskNode::MaskNode(bNode *editorNode) : Node(editorNode) diff --git a/source/blender/compositor/nodes/COM_MathNode.cc b/source/blender/compositor/nodes/COM_MathNode.cc index dd0d8931d58..f8a1530fe29 100644 --- a/source/blender/compositor/nodes/COM_MathNode.cc +++ b/source/blender/compositor/nodes/COM_MathNode.cc @@ -17,7 +17,6 @@ */ #include "COM_MathNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MathBaseOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_MixNode.cc b/source/blender/compositor/nodes/COM_MixNode.cc index cfa8d0ee6a6..9eb38783d6a 100644 --- a/source/blender/compositor/nodes/COM_MixNode.cc +++ b/source/blender/compositor/nodes/COM_MixNode.cc @@ -20,8 +20,6 @@ #include "COM_MixOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_SetValueOperation.h" #include "DNA_material_types.h" /* the ramp types */ namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc index b80071d27c7..9c2e3c95a03 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.cc +++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc @@ -17,10 +17,8 @@ */ #include "COM_MovieClipNode.h" -#include "COM_ConvertColorProfileOperation.h" -#include "COM_ExecutionSystem.h" + #include "COM_MovieClipOperation.h" -#include "COM_SetValueOperation.h" #include "BKE_movieclip.h" #include "BKE_tracking.h" diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc index 8f17ef8bb98..c9155ee8ca3 100644 --- a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc +++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc @@ -18,9 +18,7 @@ #include "COM_MovieDistortionNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MovieDistortionOperation.h" -#include "DNA_movieclip_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_NormalNode.cc b/source/blender/compositor/nodes/COM_NormalNode.cc index 5a97b0932ef..5a05f4c89b5 100644 --- a/source/blender/compositor/nodes/COM_NormalNode.cc +++ b/source/blender/compositor/nodes/COM_NormalNode.cc @@ -17,9 +17,7 @@ */ #include "COM_NormalNode.h" -#include "BKE_node.h" #include "COM_DotproductOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_SetVectorOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_NormalizeNode.cc b/source/blender/compositor/nodes/COM_NormalizeNode.cc index 639dd8e5a51..b42f8154ce0 100644 --- a/source/blender/compositor/nodes/COM_NormalizeNode.cc +++ b/source/blender/compositor/nodes/COM_NormalizeNode.cc @@ -17,7 +17,6 @@ */ #include "COM_NormalizeNode.h" -#include "COM_ExecutionSystem.h" #include "COM_NormalizeOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc index 25ec7331849..772456da963 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cc +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc @@ -17,12 +17,6 @@ */ #include "COM_OutputFileNode.h" -#include "COM_ExecutionSystem.h" -#include "COM_OutputFileOperation.h" - -#include "BKE_scene.h" - -#include "BLI_path_util.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_PixelateNode.cc b/source/blender/compositor/nodes/COM_PixelateNode.cc index 396f339e5a2..ae7169b2d98 100644 --- a/source/blender/compositor/nodes/COM_PixelateNode.cc +++ b/source/blender/compositor/nodes/COM_PixelateNode.cc @@ -18,7 +18,6 @@ #include "COM_PixelateNode.h" -#include "COM_ExecutionSystem.h" #include "COM_PixelateOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc index 54a0f4d0452..716392f8bcf 100644 --- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc +++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc @@ -17,14 +17,9 @@ */ #include "COM_PlaneTrackDeformNode.h" -#include "COM_ExecutionSystem.h" #include "COM_PlaneTrackOperation.h" -#include "BKE_movieclip.h" -#include "BKE_node.h" -#include "BKE_tracking.h" - namespace blender::compositor { PlaneTrackDeformNode::PlaneTrackDeformNode(bNode *editorNode) : Node(editorNode) diff --git a/source/blender/compositor/nodes/COM_PosterizeNode.cc b/source/blender/compositor/nodes/COM_PosterizeNode.cc index 9f5a69961a4..a69e4f17250 100644 --- a/source/blender/compositor/nodes/COM_PosterizeNode.cc +++ b/source/blender/compositor/nodes/COM_PosterizeNode.cc @@ -17,7 +17,6 @@ */ #include "COM_PosterizeNode.h" -#include "COM_ExecutionSystem.h" #include "COM_PosterizeOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cc b/source/blender/compositor/nodes/COM_RenderLayersNode.cc index 6744e98ecdb..87dccec7bfc 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.cc +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cc @@ -17,13 +17,9 @@ */ #include "COM_RenderLayersNode.h" -#include "COM_RenderLayersProg.h" -#include "COM_RotateOperation.h" -#include "COM_ScaleOperation.h" #include "COM_SetColorOperation.h" #include "COM_SetValueOperation.h" #include "COM_SetVectorOperation.h" -#include "COM_TranslateOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_RotateNode.cc b/source/blender/compositor/nodes/COM_RotateNode.cc index c2fd8ed5594..76c11a79d2f 100644 --- a/source/blender/compositor/nodes/COM_RotateNode.cc +++ b/source/blender/compositor/nodes/COM_RotateNode.cc @@ -18,7 +18,6 @@ #include "COM_RotateNode.h" -#include "COM_ExecutionSystem.h" #include "COM_RotateOperation.h" #include "COM_SetSamplerOperation.h" diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc index f1f41375eba..1e33a8688a9 100644 --- a/source/blender/compositor/nodes/COM_ScaleNode.cc +++ b/source/blender/compositor/nodes/COM_ScaleNode.cc @@ -19,9 +19,7 @@ #include "COM_ScaleNode.h" #include "BKE_node.h" -#include "COM_ExecutionSystem.h" #include "COM_ScaleOperation.h" -#include "COM_SetSamplerOperation.h" #include "COM_SetValueOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.cc b/source/blender/compositor/nodes/COM_SetAlphaNode.cc index c7365b09f71..a3a8472ccc9 100644 --- a/source/blender/compositor/nodes/COM_SetAlphaNode.cc +++ b/source/blender/compositor/nodes/COM_SetAlphaNode.cc @@ -17,7 +17,6 @@ */ #include "COM_SetAlphaNode.h" -#include "COM_ExecutionSystem.h" #include "COM_SetAlphaMultiplyOperation.h" #include "COM_SetAlphaReplaceOperation.h" diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.cc b/source/blender/compositor/nodes/COM_SocketProxyNode.cc index b3aa1770551..4bd3eb0b058 100644 --- a/source/blender/compositor/nodes/COM_SocketProxyNode.cc +++ b/source/blender/compositor/nodes/COM_SocketProxyNode.cc @@ -17,12 +17,7 @@ */ #include "COM_SocketProxyNode.h" -#include "COM_ExecutionSystem.h" #include "COM_ReadBufferOperation.h" -#include "COM_SetColorOperation.h" -#include "COM_SetValueOperation.h" -#include "COM_SetVectorOperation.h" -#include "COM_SocketProxyOperation.h" #include "COM_WriteBufferOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cc b/source/blender/compositor/nodes/COM_SplitViewerNode.cc index 582c650f205..bdc927c59a5 100644 --- a/source/blender/compositor/nodes/COM_SplitViewerNode.cc +++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cc @@ -17,11 +17,7 @@ */ #include "COM_SplitViewerNode.h" -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_scene.h" -#include "COM_ExecutionSystem.h" #include "COM_SplitOperation.h" #include "COM_ViewerOperation.h" diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc index 3d8f0bbda7e..48eeb337842 100644 --- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc +++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc @@ -17,18 +17,12 @@ */ #include "COM_Stabilize2dNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MovieClipAttributeOperation.h" #include "COM_RotateOperation.h" #include "COM_ScaleOperation.h" #include "COM_SetSamplerOperation.h" -#include "COM_TransformOperation.h" #include "COM_TranslateOperation.h" -#include "BKE_tracking.h" - -#include "DNA_movieclip_types.h" - namespace blender::compositor { Stabilize2dNode::Stabilize2dNode(bNode *editorNode) : Node(editorNode) diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.cc b/source/blender/compositor/nodes/COM_SwitchViewNode.cc index 395122dd11b..5bf854bb0a0 100644 --- a/source/blender/compositor/nodes/COM_SwitchViewNode.cc +++ b/source/blender/compositor/nodes/COM_SwitchViewNode.cc @@ -17,7 +17,6 @@ */ #include "COM_SwitchViewNode.h" -#include "BLI_listbase.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_TextureNode.cc b/source/blender/compositor/nodes/COM_TextureNode.cc index 317355b8c9a..4063e4b4bce 100644 --- a/source/blender/compositor/nodes/COM_TextureNode.cc +++ b/source/blender/compositor/nodes/COM_TextureNode.cc @@ -17,7 +17,6 @@ */ #include "COM_TextureNode.h" -#include "COM_ExecutionSystem.h" #include "COM_TextureOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_TimeNode.cc b/source/blender/compositor/nodes/COM_TimeNode.cc index c14c5344eee..37236959366 100644 --- a/source/blender/compositor/nodes/COM_TimeNode.cc +++ b/source/blender/compositor/nodes/COM_TimeNode.cc @@ -17,13 +17,11 @@ */ #include "COM_TimeNode.h" -#include "COM_ExecutionSystem.h" + #include "COM_SetValueOperation.h" #include "BKE_colortools.h" -#include "BLI_utildefines.h" - namespace blender::compositor { TimeNode::TimeNode(bNode *editorNode) : Node(editorNode) diff --git a/source/blender/compositor/nodes/COM_TonemapNode.cc b/source/blender/compositor/nodes/COM_TonemapNode.cc index 844fe3e8cb6..a2b3e84b604 100644 --- a/source/blender/compositor/nodes/COM_TonemapNode.cc +++ b/source/blender/compositor/nodes/COM_TonemapNode.cc @@ -17,7 +17,6 @@ */ #include "COM_TonemapNode.h" -#include "COM_ExecutionSystem.h" #include "COM_TonemapOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.cc b/source/blender/compositor/nodes/COM_TrackPositionNode.cc index 3fb5fc02f20..f683eeb61c2 100644 --- a/source/blender/compositor/nodes/COM_TrackPositionNode.cc +++ b/source/blender/compositor/nodes/COM_TrackPositionNode.cc @@ -19,7 +19,6 @@ #include "COM_TrackPositionNode.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_TrackPositionOperation.h" #include "DNA_movieclip_types.h" diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc index b38aad78d90..a5b3c41de80 100644 --- a/source/blender/compositor/nodes/COM_TransformNode.cc +++ b/source/blender/compositor/nodes/COM_TransformNode.cc @@ -17,12 +17,9 @@ */ #include "COM_TransformNode.h" -#include "COM_ExecutionSystem.h" #include "COM_RotateOperation.h" #include "COM_ScaleOperation.h" #include "COM_SetSamplerOperation.h" -#include "COM_SetValueOperation.h" -#include "COM_TransformOperation.h" #include "COM_TranslateOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc index 165a03baf41..6dbd350fabf 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cc +++ b/source/blender/compositor/nodes/COM_TranslateNode.cc @@ -18,7 +18,6 @@ #include "COM_TranslateNode.h" -#include "COM_ExecutionSystem.h" #include "COM_TranslateOperation.h" #include "COM_WrapOperation.h" #include "COM_WriteBufferOperation.h" diff --git a/source/blender/compositor/nodes/COM_ValueNode.cc b/source/blender/compositor/nodes/COM_ValueNode.cc index 6b640fa2a3a..611353fceba 100644 --- a/source/blender/compositor/nodes/COM_ValueNode.cc +++ b/source/blender/compositor/nodes/COM_ValueNode.cc @@ -17,7 +17,6 @@ */ #include "COM_ValueNode.h" -#include "COM_ExecutionSystem.h" #include "COM_SetValueOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.cc b/source/blender/compositor/nodes/COM_VectorBlurNode.cc index 7aa5f5668c9..2bbfd3591c3 100644 --- a/source/blender/compositor/nodes/COM_VectorBlurNode.cc +++ b/source/blender/compositor/nodes/COM_VectorBlurNode.cc @@ -18,7 +18,6 @@ #include "COM_VectorBlurNode.h" #include "COM_VectorBlurOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.cc b/source/blender/compositor/nodes/COM_VectorCurveNode.cc index f2fd80cd93e..2b26056296d 100644 --- a/source/blender/compositor/nodes/COM_VectorCurveNode.cc +++ b/source/blender/compositor/nodes/COM_VectorCurveNode.cc @@ -17,7 +17,6 @@ */ #include "COM_VectorCurveNode.h" -#include "COM_ExecutionSystem.h" #include "COM_VectorCurveOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc index 5a03972c89d..605ff9357ab 100644 --- a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc +++ b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc @@ -17,10 +17,7 @@ */ #include "COM_ViewLevelsNode.h" -#include "COM_CalculateMeanOperation.h" #include "COM_CalculateStandardDeviationOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_SetValueOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cc b/source/blender/compositor/nodes/COM_ViewerNode.cc index 4dbcdbe9e40..38ea37a28d6 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.cc +++ b/source/blender/compositor/nodes/COM_ViewerNode.cc @@ -17,12 +17,7 @@ */ #include "COM_ViewerNode.h" -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_scene.h" -#include "BLI_listbase.h" -#include "COM_ExecutionSystem.h" #include "COM_ViewerOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.cc b/source/blender/compositor/nodes/COM_ZCombineNode.cc index 9753e812a8b..5f7fec53808 100644 --- a/source/blender/compositor/nodes/COM_ZCombineNode.cc +++ b/source/blender/compositor/nodes/COM_ZCombineNode.cc @@ -18,15 +18,9 @@ #include "COM_ZCombineNode.h" -#include "COM_ZCombineOperation.h" - #include "COM_AntiAliasOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_MathBaseOperation.h" -#include "COM_MixOperation.h" -#include "COM_SetValueOperation.h" - -#include "DNA_material_types.h" /* the ramp types */ +#include "COM_ZCombineOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.cc b/source/blender/compositor/operations/COM_AntiAliasOperation.cc index deccbb28f49..dd73f9a7a77 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.cc +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.cc @@ -17,12 +17,6 @@ */ #include "COM_AntiAliasOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" - -#include "MEM_guardedalloc.h" - -#include "RE_texture.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc index 44680c3acd1..de70feed907 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc @@ -17,9 +17,6 @@ */ #include "COM_BilateralBlurOperation.h" -#include "BLI_math.h" - -#include "RE_pipeline.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc index 412632e2e22..f21cb9f4285 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc @@ -19,9 +19,6 @@ #include "COM_BlurBaseOperation.h" #include "COM_ConstantOperation.h" -#include "BLI_math.h" -#include "MEM_guardedalloc.h" - #include "RE_pipeline.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc index 93482dd2a54..c175eff093b 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc @@ -19,11 +19,8 @@ #include "COM_BokehBlurOperation.h" #include "COM_ConstantOperation.h" -#include "BLI_math.h" #include "COM_OpenCLDevice.h" -#include "RE_pipeline.h" - namespace blender::compositor { constexpr int IMAGE_INPUT_INDEX = 0; diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc index 5c9c8b36ee0..c6e0683b4de 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.cc +++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_BokehImageOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.cc b/source/blender/compositor/operations/COM_BoxMaskOperation.cc index 15bb19660dc..f536d9eb32d 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.cc +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.cc @@ -17,8 +17,6 @@ */ #include "COM_BoxMaskOperation.h" -#include "BLI_math.h" -#include "DNA_node_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc index a573a9d7eed..850b762ede4 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc @@ -17,8 +17,7 @@ */ #include "COM_CalculateMeanOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" + #include "COM_ExecutionSystem.h" #include "IMB_colormanagement.h" diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc index 494b66cb888..6e5ef690b5d 100644 --- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc @@ -17,8 +17,7 @@ */ #include "COM_CalculateStandardDeviationOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" + #include "COM_ExecutionSystem.h" #include "IMB_colormanagement.h" diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc index 65742d0cfcc..3d79d3d03a0 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ChannelMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc index 0784f266b19..207cc641ebd 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ChromaMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc index 0b6590ae4c7..08df31b81d8 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorBalanceASCCDLOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc index c658ecd6394..9a482875cdd 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorBalanceLGGOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc index d3557e541c0..2b2baa15037 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorCorrectionOperation.h" -#include "BLI_math.h" #include "IMB_colormanagement.h" diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc index 364b310945e..31541786b85 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc @@ -20,8 +20,6 @@ #include "BKE_colortools.h" -#include "MEM_guardedalloc.h" - namespace blender::compositor { ColorCurveOperation::ColorCurveOperation() diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h index d8271e56d1d..8f6b34c5c6f 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.h +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h @@ -19,8 +19,6 @@ #pragma once #include "COM_CurveBaseOperation.h" -#include "COM_NodeOperation.h" -#include "DNA_color_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc index dec6571f217..7dd6a4ab73f 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc index 5bf7a9ee9cd..deed8636360 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorSpillOperation.h" -#include "BLI_math.h" #define AVG(a, b) ((a + b) / 2) namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index f7466b5db34..0713ecd9316 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -17,19 +17,11 @@ */ #include "COM_CompositorOperation.h" + #include "BKE_global.h" #include "BKE_image.h" -#include "BLI_listbase.h" -#include "MEM_guardedalloc.h" - -#include "BLI_threads.h" #include "RE_pipeline.h" -#include "RE_texture.h" - -#include "render_types.h" - -#include "PIL_time.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index 6eb96e01b47..7b823824cff 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -18,8 +18,6 @@ #pragma once -#include "BLI_rect.h" -#include "BLI_string.h" #include "COM_MultiThreadedOperation.h" struct Scene; diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc index 405ba03abf3..18a28f854be 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc @@ -18,7 +18,6 @@ #include "COM_ConvertDepthToRadiusOperation.h" #include "BKE_camera.h" -#include "BLI_math.h" #include "DNA_camera_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc index 9127a871b04..0f44ccd6fcb 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ConvolutionEdgeFilterOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc index 807223fd45f..9d4e8397f54 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc @@ -18,10 +18,6 @@ #include "COM_ConvolutionFilterOperation.h" -#include "BLI_utildefines.h" - -#include "MEM_guardedalloc.h" - namespace blender::compositor { ConvolutionFilterOperation::ConvolutionFilterOperation() diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc index 6ac30c22ad1..c5a5d1409f4 100644 --- a/source/blender/compositor/operations/COM_CropOperation.cc +++ b/source/blender/compositor/operations/COM_CropOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_CropOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h index da665e7ea60..27d3a68608c 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.h +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h @@ -19,7 +19,8 @@ #pragma once #include "COM_MultiThreadedOperation.h" -#include "DNA_color_types.h" + +struct CurveMapping; namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index f8a575acc3a..d8362f4da8d 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -17,14 +17,12 @@ */ #include "COM_DenoiseOperation.h" -#include "BLI_math.h" #include "BLI_system.h" #ifdef WITH_OPENIMAGEDENOISE # include "BLI_threads.h" # include static pthread_mutex_t oidn_lock = BLI_MUTEX_INITIALIZER; #endif -#include namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc index df637ee6709..7c5f33d0f59 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.cc +++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc @@ -20,8 +20,6 @@ #include "COM_DespeckleOperation.h" -#include "BLI_utildefines.h" - namespace blender::compositor { DespeckleOperation::DespeckleOperation() diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc index 31714b03b06..eb92f0e9905 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_DifferenceMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index b7fd714ba5b..6e4a4d4780e 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -17,11 +17,8 @@ */ #include "COM_DilateErodeOperation.h" -#include "BLI_math.h" #include "COM_OpenCLDevice.h" -#include "MEM_guardedalloc.h" - namespace blender::compositor { /* DilateErode Distance Threshold */ diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc index e69124205d0..d1698e989d0 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc @@ -19,10 +19,6 @@ #include "COM_DirectionalBlurOperation.h" #include "COM_OpenCLDevice.h" -#include "BLI_math.h" - -#include "RE_pipeline.h" - namespace blender::compositor { DirectionalBlurOperation::DirectionalBlurOperation() diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc index d08ff60d5d0..e6149add5e9 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc @@ -17,8 +17,6 @@ */ #include "COM_DisplaceOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc index 712b61be805..81c047ec9f9 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc @@ -17,8 +17,6 @@ */ #include "COM_DisplaceSimpleOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc index 8155ff769a0..ab53bf32bea 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_DistanceRGBMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc index 50e473ea5b3..9903b80ce02 100644 --- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_DistanceYCCMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc index d112334b749..fa0201519f8 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc @@ -18,10 +18,7 @@ #include -#include "BLI_math.h" #include "COM_DoubleEdgeMaskOperation.h" -#include "DNA_node_types.h" -#include "MEM_guardedalloc.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc index bf6eee6d3f9..a32f15a1b8f 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc @@ -17,10 +17,6 @@ */ #include "COM_EllipseMaskOperation.h" -#include "BLI_math.h" -#include "DNA_node_types.h" - -#include namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index f45b77c6ebc..629a8f1a5cb 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -18,9 +18,7 @@ #include -#include "BLI_utildefines.h" #include "COM_FastGaussianBlurOperation.h" -#include "MEM_guardedalloc.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc index 1bff3b965c6..2d97acf4122 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_GammaCorrectOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GammaOperation.cc b/source/blender/compositor/operations/COM_GammaOperation.cc index 396d3942b06..fab7a8dfb78 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.cc +++ b/source/blender/compositor/operations/COM_GammaOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_GammaOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc index 6710ed3cf5b..f53a1da598c 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc @@ -17,10 +17,6 @@ */ #include "COM_GaussianAlphaXBlurOperation.h" -#include "BLI_math.h" -#include "MEM_guardedalloc.h" - -#include "RE_pipeline.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc index 09aeddb6573..b171ad428b1 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc @@ -17,10 +17,6 @@ */ #include "COM_GaussianAlphaYBlurOperation.h" -#include "BLI_math.h" -#include "MEM_guardedalloc.h" - -#include "RE_pipeline.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc index aafc269abac..3034d6c590f 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc @@ -17,8 +17,6 @@ */ #include "COM_GaussianBokehBlurOperation.h" -#include "BLI_math.h" -#include "MEM_guardedalloc.h" #include "RE_pipeline.h" diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc index 8d686265231..6f1839eee30 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc @@ -17,11 +17,7 @@ */ #include "COM_GaussianXBlurOperation.h" -#include "BLI_math.h" #include "COM_OpenCLDevice.h" -#include "MEM_guardedalloc.h" - -#include "RE_pipeline.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc index 32d469a0ae4..cc8c1fce617 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc @@ -17,11 +17,7 @@ */ #include "COM_GaussianYBlurOperation.h" -#include "BLI_math.h" #include "COM_OpenCLDevice.h" -#include "MEM_guardedalloc.h" - -#include "RE_pipeline.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cc b/source/blender/compositor/operations/COM_GlareBaseOperation.cc index cd4607b1dde..32283a12b27 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_GlareBaseOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc index 0026615f08b..2142a1b822e 100644 --- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc +++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_GlareFogGlowOperation.h" -#include "MEM_guardedalloc.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.cc b/source/blender/compositor/operations/COM_GlareGhostOperation.cc index 22c8767632e..c5ecfed024c 100644 --- a/source/blender/compositor/operations/COM_GlareGhostOperation.cc +++ b/source/blender/compositor/operations/COM_GlareGhostOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_GlareGhostOperation.h" -#include "BLI_math.h" #include "COM_FastGaussianBlurOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc index 5ca64b02586..8ffe44d9a36 100644 --- a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc +++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_GlareStreaksOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc index 1bf7cf5ae07..5b84b4087f4 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_GlareThresholdOperation.h" -#include "BLI_math.h" #include "IMB_colormanagement.h" diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc index 5ae868c5964..d083133cb3e 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc @@ -18,7 +18,7 @@ #include "COM_HueSaturationValueCorrectOperation.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BKE_colortools.h" diff --git a/source/blender/compositor/operations/COM_ImageOperation.cc b/source/blender/compositor/operations/COM_ImageOperation.cc index ff389093f5a..773b61bc225 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cc +++ b/source/blender/compositor/operations/COM_ImageOperation.cc @@ -18,19 +18,12 @@ #include "COM_ImageOperation.h" -#include "BKE_image.h" #include "BKE_scene.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "DNA_image_types.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" -#include "RE_pipeline.h" -#include "RE_texture.h" - namespace blender::compositor { BaseImageOperation::BaseImageOperation() diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc index 5d440dd7d76..163699caa14 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cc +++ b/source/blender/compositor/operations/COM_InpaintOperation.cc @@ -19,9 +19,6 @@ #include "MEM_guardedalloc.h" #include "COM_InpaintOperation.h" -#include "COM_OpenCLDevice.h" - -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc index d5ebd5e9df7..d61c63cfc04 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc @@ -18,11 +18,6 @@ #include "COM_KeyingBlurOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - namespace blender::compositor { KeyingBlurOperation::KeyingBlurOperation() diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc index 817c920ed91..d4ef43b6521 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc @@ -18,11 +18,6 @@ #include "COM_KeyingClipOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - namespace blender::compositor { KeyingClipOperation::KeyingClipOperation() diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc index 620b767e584..2c91d7771bf 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc @@ -18,11 +18,6 @@ #include "COM_KeyingDespillOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - namespace blender::compositor { KeyingDespillOperation::KeyingDespillOperation() diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cc b/source/blender/compositor/operations/COM_KeyingOperation.cc index 3edb5a5d34e..76f30726522 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingOperation.cc @@ -18,11 +18,6 @@ #include "COM_KeyingOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - namespace blender::compositor { static float get_pixel_saturation(const float pixelColor[4], diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc index b4840926d55..32a29ed62bb 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc @@ -18,12 +18,6 @@ #include "COM_KeyingScreenOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" - #include "BKE_movieclip.h" #include "BKE_tracking.h" diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc index c642c60b912..e6cb1d8f718 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_LuminanceMatteOperation.h" -#include "BLI_math.h" #include "IMB_colormanagement.h" diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index ba38e583b30..194fa504978 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_MapUVOperation.h" -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc index 65b89a8c79a..5044df0b128 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cc +++ b/source/blender/compositor/operations/COM_MaskOperation.cc @@ -18,11 +18,6 @@ #include "COM_MaskOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - #include "BKE_lib_id.h" #include "BKE_mask.h" diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc index d3fb83caf7c..3a447ad53e0 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.cc +++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc @@ -18,8 +18,6 @@ #include "COM_MathBaseOperation.h" -#include "BLI_math.h" - namespace blender::compositor { MathBaseOperation::MathBaseOperation() diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc index 895d32e6fee..d16fbb1324e 100644 --- a/source/blender/compositor/operations/COM_MixOperation.cc +++ b/source/blender/compositor/operations/COM_MixOperation.cc @@ -18,8 +18,6 @@ #include "COM_MixOperation.h" -#include "BLI_math.h" - namespace blender::compositor { /* ******** Mix Base Operation ******** */ diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc index 1ca33b12432..6430e2af0db 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc @@ -18,9 +18,6 @@ #include "COM_MovieClipOperation.h" -#include "BLI_listbase.h" -#include "BLI_math.h" - #include "BKE_image.h" #include "BKE_movieclip.h" diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc index 49f43d2c1a7..ff942fda54f 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc @@ -19,9 +19,6 @@ #include "COM_MovieDistortionOperation.h" #include "BKE_movieclip.h" -#include "BKE_tracking.h" - -#include "BLI_linklist.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc index 3a5de944a00..2974cd8dbd1 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc @@ -19,7 +19,6 @@ #include "COM_MultilayerImageOperation.h" #include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index d436b00a6e3..cc177a82056 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -17,22 +17,11 @@ */ #include "COM_OutputFileMultiViewOperation.h" -#include "COM_OutputFileOperation.h" -#include - -#include "BLI_listbase.h" -#include "BLI_path_util.h" -#include "BLI_string.h" - -#include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_scene.h" -#include "DNA_color_types.h" -#include "MEM_guardedalloc.h" - #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 79be95bb686..6ccff9bd0ef 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -18,21 +18,15 @@ #include "COM_OutputFileOperation.h" -#include "COM_MetaData.h" - -#include - #include "BLI_listbase.h" #include "BLI_path_util.h" #include "BLI_string.h" -#include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_scene.h" #include "DNA_color_types.h" -#include "MEM_guardedalloc.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc index 65cd08456ef..92733186979 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc @@ -17,15 +17,6 @@ #include "COM_PlaneCornerPinOperation.h" #include "COM_ConstantOperation.h" -#include "COM_ReadBufferOperation.h" - -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" - -#include "BKE_node.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 31ef41789fd..259c772e296 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -18,15 +18,8 @@ #include "COM_PlaneDistortCommonOperation.h" -#include "MEM_guardedalloc.h" - #include "BLI_jitter_2d.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" -#include "BKE_movieclip.h" -#include "BKE_node.h" #include "BKE_tracking.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc index 7226a133a52..593c3604568 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc @@ -17,16 +17,8 @@ */ #include "COM_PlaneTrackOperation.h" -#include "COM_ReadBufferOperation.h" - -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" #include "BKE_movieclip.h" -#include "BKE_node.h" #include "BKE_tracking.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc index 34520264d54..c08ce82690d 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cc +++ b/source/blender/compositor/operations/COM_PreviewOperation.cc @@ -17,21 +17,9 @@ */ #include "COM_PreviewOperation.h" -#include "BKE_image.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" -#include "BLI_utildefines.h" -#include "COM_defines.h" -#include "MEM_guardedalloc.h" -#include "PIL_time.h" -#include "WM_api.h" -#include "WM_types.h" #include "BKE_node.h" #include "IMB_colormanagement.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc index 2982f59a019..fb8bd2f145f 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc @@ -19,9 +19,6 @@ #include "COM_ProjectorLensDistortionOperation.h" #include "COM_ConstantOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" - namespace blender::compositor { ProjectorLensDistortionOperation::ProjectorLensDistortionOperation() diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cc b/source/blender/compositor/operations/COM_ReadBufferOperation.cc index 599370751bb..0a41ba2d0be 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.cc +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cc @@ -17,8 +17,9 @@ */ #include "COM_ReadBufferOperation.h" + +#include "COM_ExecutionGroup.h" #include "COM_WriteBufferOperation.h" -#include "COM_defines.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc index 2ac551ffe6f..6045a416d74 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cc +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc @@ -18,19 +18,7 @@ #include "COM_RenderLayersProg.h" -#include "COM_MetaData.h" - #include "BKE_image.h" -#include "BKE_scene.h" - -#include "BLI_listbase.h" -#include "BLI_string.h" -#include "BLI_string_ref.hh" - -#include "DNA_scene_types.h" - -#include "RE_pipeline.h" -#include "RE_texture.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc index 9e26c93feac..8aca25bb2a0 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.cc +++ b/source/blender/compositor/operations/COM_RotateOperation.cc @@ -17,9 +17,6 @@ */ #include "COM_RotateOperation.h" -#include "COM_ConstantOperation.h" - -#include "BLI_math.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index 4153b9c8523..8c30976d200 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -20,7 +20,6 @@ #include "COM_SMAAOperation.h" #include "BKE_node.h" -#include "BLI_math.h" #include "COM_SMAAAreaTexture.h" extern "C" { diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index 21d9210bdac..bef5beb2bce 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -17,11 +17,10 @@ */ #include "COM_ScreenLensDistortionOperation.h" + #include "COM_ConstantOperation.h" -#include "BLI_math.h" #include "BLI_rand.h" -#include "BLI_utildefines.h" #include "PIL_time.h" diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.cc b/source/blender/compositor/operations/COM_SetVectorOperation.cc index 3e8514f1f59..6f0a947c52d 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.cc +++ b/source/blender/compositor/operations/COM_SetVectorOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_SetVectorOperation.h" -#include "COM_defines.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc index 47b2bbb7e94..ddc5b6dce13 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.cc +++ b/source/blender/compositor/operations/COM_SplitOperation.cc @@ -17,15 +17,6 @@ */ #include "COM_SplitOperation.h" -#include "BKE_image.h" -#include "BLI_listbase.h" -#include "BLI_math_color.h" -#include "BLI_math_vector.h" -#include "BLI_utildefines.h" -#include "MEM_guardedalloc.h" - -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index c06e3ac7cb0..cfd22b16c4e 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -22,9 +22,6 @@ #include "BKE_image.h" #include "BKE_node.h" -#include "BLI_listbase.h" -#include "BLI_threads.h" - namespace blender::compositor { TextureBaseOperation::TextureBaseOperation() diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index cb671c54abe..2ff54571c24 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -17,10 +17,8 @@ */ #include "COM_TonemapOperation.h" -#include "COM_ExecutionSystem.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" +#include "COM_ExecutionSystem.h" #include "IMB_colormanagement.h" diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index 1929c578177..d6f0ce9d24c 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -18,12 +18,6 @@ #include "COM_TrackPositionOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" - #include "BKE_movieclip.h" #include "BKE_node.h" #include "BKE_tracking.h" diff --git a/source/blender/compositor/operations/COM_TransformOperation.cc b/source/blender/compositor/operations/COM_TransformOperation.cc index 5f6e9ed4d21..38bb443bdea 100644 --- a/source/blender/compositor/operations/COM_TransformOperation.cc +++ b/source/blender/compositor/operations/COM_TransformOperation.cc @@ -17,12 +17,9 @@ */ #include "COM_TransformOperation.h" -#include "COM_ConstantOperation.h" #include "COM_RotateOperation.h" #include "COM_ScaleOperation.h" -#include "BLI_math.h" - namespace blender::compositor { TransformOperation::TransformOperation() diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc index c524447a4fa..d31290acac7 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc @@ -17,12 +17,8 @@ */ #include "COM_VariableSizeBokehBlurOperation.h" -#include "BLI_math.h" -#include "COM_ExecutionSystem.h" #include "COM_OpenCLDevice.h" -#include "RE_pipeline.h" - namespace blender::compositor { VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation() diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc index 63956410b60..57053470d7f 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc @@ -16,12 +16,7 @@ * Copyright 2011, Blender Foundation. */ -#include - -#include "MEM_guardedalloc.h" - #include "BLI_jitter_2d.h" -#include "BLI_math.h" #include "COM_VectorBlurOperation.h" diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index 1faff0fd07f..b0cfe90088a 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -19,15 +19,7 @@ #include "COM_ViewerOperation.h" #include "BKE_image.h" #include "BKE_scene.h" -#include "BLI_listbase.h" -#include "BLI_math_color.h" -#include "BLI_math_vector.h" -#include "BLI_utildefines.h" #include "COM_ExecutionSystem.h" -#include "MEM_guardedalloc.h" -#include "PIL_time.h" -#include "WM_api.h" -#include "WM_types.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cc b/source/blender/compositor/operations/COM_WriteBufferOperation.cc index a1c1e514eb7..5d3f1ea3e1e 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.cc +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cc @@ -18,8 +18,6 @@ #include "COM_WriteBufferOperation.h" #include "COM_OpenCLDevice.h" -#include "COM_defines.h" -#include namespace blender::compositor { diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc index 7050c3b2d83..1b04c749c8b 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.cc +++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ZCombineOperation.h" -#include "BLI_utildefines.h" namespace blender::compositor { From ea79efef70da14100b591b50dcada819808f20b6 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Wed, 13 Oct 2021 23:00:50 +0200 Subject: [PATCH 0766/1500] Cleanup: remove `this->` for `m_` prefixed members in Compositor For cleaning old code style as new code usually omit it. --- .../intern/COM_CompositorContext.cc | 16 +- .../compositor/intern/COM_CompositorContext.h | 40 ++-- .../compositor/intern/COM_ExecutionGroup.cc | 179 ++++++++---------- .../compositor/intern/COM_ExecutionGroup.h | 8 +- .../compositor/intern/COM_ExecutionSystem.cc | 30 +-- .../compositor/intern/COM_ExecutionSystem.h | 2 +- .../compositor/intern/COM_MemoryBuffer.cc | 74 ++++---- .../compositor/intern/COM_MemoryBuffer.h | 34 ++-- .../compositor/intern/COM_MemoryProxy.cc | 14 +- .../compositor/intern/COM_MemoryProxy.h | 12 +- source/blender/compositor/intern/COM_Node.h | 16 +- .../compositor/intern/COM_NodeOperation.cc | 10 +- .../compositor/intern/COM_NodeOperation.h | 12 +- .../intern/COM_NodeOperationBuilder.cc | 2 +- .../compositor/intern/COM_OpenCLDevice.cc | 29 ++- .../compositor/intern/COM_OpenCLDevice.h | 4 +- .../intern/COM_SingleThreadedOperation.cc | 20 +- .../intern/COM_SingleThreadedOperation.h | 2 +- .../operations/COM_AlphaOverKeyOperation.cc | 6 +- .../operations/COM_AlphaOverMixedOperation.cc | 12 +- .../operations/COM_AlphaOverMixedOperation.h | 2 +- .../COM_AlphaOverPremultiplyOperation.cc | 6 +- .../operations/COM_AntiAliasOperation.cc | 6 +- .../operations/COM_BilateralBlurOperation.cc | 30 +-- .../operations/COM_BilateralBlurOperation.h | 4 +- .../operations/COM_BlurBaseOperation.cc | 46 ++--- .../operations/COM_BlurBaseOperation.h | 6 +- .../operations/COM_BokehBlurOperation.cc | 78 ++++---- .../operations/COM_BokehBlurOperation.h | 6 +- .../operations/COM_BokehImageOperation.cc | 60 +++--- .../operations/COM_BokehImageOperation.h | 4 +- .../operations/COM_BoxMaskOperation.cc | 62 +++--- .../operations/COM_BoxMaskOperation.h | 4 +- .../operations/COM_BrightnessOperation.cc | 32 ++-- .../operations/COM_CalculateMeanOperation.cc | 34 ++-- ...COM_CalculateStandardDeviationOperation.cc | 25 ++- .../operations/COM_ChangeHSVOperation.cc | 26 +-- .../operations/COM_ChannelMatteOperation.cc | 52 ++--- .../operations/COM_ChannelMatteOperation.h | 10 +- .../operations/COM_ChromaMatteOperation.cc | 28 +-- .../operations/COM_ChromaMatteOperation.h | 2 +- .../COM_ColorBalanceASCCDLOperation.cc | 25 ++- .../COM_ColorBalanceASCCDLOperation.h | 6 +- .../COM_ColorBalanceLGGOperation.cc | 25 ++- .../operations/COM_ColorBalanceLGGOperation.h | 6 +- .../COM_ColorCorrectionOperation.cc | 126 ++++++------ .../operations/COM_ColorCorrectionOperation.h | 8 +- .../operations/COM_ColorCurveOperation.cc | 64 +++---- .../operations/COM_ColorCurveOperation.h | 4 +- .../operations/COM_ColorExposureOperation.cc | 14 +- .../operations/COM_ColorMatteOperation.cc | 22 +-- .../operations/COM_ColorMatteOperation.h | 2 +- .../operations/COM_ColorRampOperation.cc | 12 +- .../operations/COM_ColorRampOperation.h | 2 +- .../operations/COM_ColorSpillOperation.cc | 94 +++++---- .../operations/COM_ColorSpillOperation.h | 6 +- .../operations/COM_CompositorOperation.cc | 96 +++++----- .../operations/COM_CompositorOperation.h | 12 +- .../COM_ConvertColorProfileOperation.cc | 12 +- .../COM_ConvertColorProfileOperation.h | 6 +- .../COM_ConvertDepthToRadiusOperation.cc | 54 +++--- .../COM_ConvertDepthToRadiusOperation.h | 8 +- .../operations/COM_ConvertOperation.cc | 108 +++++------ .../operations/COM_ConvertOperation.h | 2 +- .../COM_ConvolutionEdgeFilterOperation.cc | 56 +++--- .../COM_ConvolutionFilterOperation.cc | 74 ++++---- .../operations/COM_CropOperation.cc | 52 ++--- .../compositor/operations/COM_CropOperation.h | 4 +- .../operations/COM_CurveBaseOperation.cc | 22 +-- .../operations/COM_DenoiseOperation.cc | 22 +-- .../operations/COM_DenoiseOperation.h | 2 +- .../operations/COM_DespeckleOperation.cc | 50 ++--- .../operations/COM_DespeckleOperation.h | 4 +- .../COM_DifferenceMatteOperation.cc | 20 +- .../operations/COM_DifferenceMatteOperation.h | 2 +- .../operations/COM_DilateErodeOperation.cc | 124 ++++++------ .../operations/COM_DilateErodeOperation.h | 10 +- .../COM_DirectionalBlurOperation.cc | 100 +++++----- .../operations/COM_DirectionalBlurOperation.h | 2 +- .../operations/COM_DisplaceOperation.cc | 14 +- .../operations/COM_DisplaceSimpleOperation.cc | 40 ++-- .../COM_DistanceRGBMatteOperation.cc | 24 +-- .../COM_DistanceRGBMatteOperation.h | 2 +- .../operations/COM_DotproductOperation.cc | 16 +- .../operations/COM_DoubleEdgeMaskOperation.cc | 48 ++--- .../operations/COM_DoubleEdgeMaskOperation.h | 4 +- .../operations/COM_EllipseMaskOperation.cc | 62 +++--- .../operations/COM_EllipseMaskOperation.h | 4 +- .../COM_FastGaussianBlurOperation.cc | 86 ++++----- .../COM_FastGaussianBlurOperation.h | 4 +- .../operations/COM_FlipOperation.cc | 28 +-- .../compositor/operations/COM_FlipOperation.h | 4 +- .../operations/COM_GammaCorrectOperation.cc | 16 +- .../operations/COM_GammaOperation.cc | 16 +- .../COM_GaussianAlphaBlurBaseOperation.cc | 18 +- .../COM_GaussianAlphaBlurBaseOperation.h | 4 +- .../COM_GaussianAlphaXBlurOperation.cc | 34 ++-- .../COM_GaussianAlphaYBlurOperation.cc | 34 ++-- .../COM_GaussianBokehBlurOperation.cc | 116 ++++++------ .../operations/COM_GaussianXBlurOperation.cc | 60 +++--- .../operations/COM_GaussianYBlurOperation.cc | 56 +++--- .../operations/COM_GlareBaseOperation.cc | 10 +- .../operations/COM_GlareBaseOperation.h | 2 +- .../operations/COM_GlareThresholdOperation.cc | 16 +- .../operations/COM_GlareThresholdOperation.h | 2 +- .../COM_HueSaturationValueCorrectOperation.cc | 20 +- .../operations/COM_IDMaskOperation.cc | 2 +- .../operations/COM_IDMaskOperation.h | 2 +- .../operations/COM_ImageOperation.cc | 72 +++---- .../operations/COM_ImageOperation.h | 10 +- .../operations/COM_InpaintOperation.cc | 81 ++++---- .../operations/COM_InpaintOperation.h | 2 +- .../operations/COM_InvertOperation.cc | 28 +-- .../operations/COM_InvertOperation.h | 4 +- .../operations/COM_KeyingBlurOperation.cc | 21 +- .../operations/COM_KeyingBlurOperation.h | 4 +- .../operations/COM_KeyingClipOperation.cc | 30 +-- .../operations/COM_KeyingClipOperation.h | 10 +- .../operations/COM_KeyingDespillOperation.cc | 26 +-- .../operations/COM_KeyingDespillOperation.h | 4 +- .../operations/COM_KeyingOperation.cc | 23 ++- .../operations/COM_KeyingOperation.h | 2 +- .../operations/COM_KeyingScreenOperation.cc | 47 +++-- .../operations/COM_KeyingScreenOperation.h | 6 +- .../operations/COM_LuminanceMatteOperation.cc | 12 +- .../operations/COM_LuminanceMatteOperation.h | 2 +- .../operations/COM_MapRangeOperation.cc | 36 ++-- .../operations/COM_MapRangeOperation.h | 2 +- .../operations/COM_MapUVOperation.cc | 22 +-- .../operations/COM_MapUVOperation.h | 2 +- .../operations/COM_MapValueOperation.cc | 12 +- .../operations/COM_MapValueOperation.h | 2 +- .../operations/COM_MaskOperation.cc | 81 ++++---- .../compositor/operations/COM_MaskOperation.h | 22 +-- .../operations/COM_MathBaseOperation.cc | 170 ++++++++--------- .../operations/COM_MathBaseOperation.h | 6 +- .../compositor/operations/COM_MixOperation.cc | 138 +++++++------- .../compositor/operations/COM_MixOperation.h | 6 +- .../COM_MovieClipAttributeOperation.cc | 32 ++-- .../COM_MovieClipAttributeOperation.h | 8 +- .../operations/COM_MovieClipOperation.cc | 36 ++-- .../operations/COM_MovieClipOperation.h | 8 +- .../COM_MovieDistortionOperation.cc | 62 +++--- .../operations/COM_MovieDistortionOperation.h | 4 +- .../COM_MultilayerImageOperation.cc | 48 ++--- .../operations/COM_NormalizeOperation.cc | 22 +-- .../COM_OutputFileMultiViewOperation.cc | 126 ++++++------ .../operations/COM_OutputFileOperation.cc | 139 +++++++------- .../operations/COM_PixelateOperation.cc | 8 +- .../COM_PlaneDistortCommonOperation.cc | 76 ++++---- .../COM_PlaneDistortCommonOperation.h | 4 +- .../operations/COM_PlaneTrackOperation.cc | 28 +-- .../operations/COM_PlaneTrackOperation.h | 8 +- .../operations/COM_PosterizeOperation.cc | 16 +- .../operations/COM_PreviewOperation.cc | 75 ++++---- .../COM_ProjectorLensDistortionOperation.cc | 49 +++-- .../operations/COM_QualityStepHelper.cc | 34 ++-- .../operations/COM_QualityStepHelper.h | 6 +- .../operations/COM_ReadBufferOperation.cc | 20 +- .../operations/COM_ReadBufferOperation.h | 10 +- .../operations/COM_RenderLayersProg.cc | 41 ++-- .../operations/COM_RenderLayersProg.h | 16 +- .../operations/COM_RotateOperation.cc | 62 +++--- .../operations/COM_RotateOperation.h | 2 +- .../operations/COM_SMAAOperation.cc | 30 +-- .../operations/COM_ScaleOperation.cc | 119 ++++++------ .../operations/COM_ScaleOperation.h | 14 +- .../COM_ScreenLensDistortionOperation.cc | 26 +-- .../COM_SetAlphaMultiplyOperation.cc | 16 +- .../COM_SetAlphaReplaceOperation.cc | 16 +- .../operations/COM_SetColorOperation.cc | 2 +- .../operations/COM_SetColorOperation.h | 18 +- .../operations/COM_SetSamplerOperation.cc | 6 +- .../operations/COM_SetSamplerOperation.h | 2 +- .../operations/COM_SetValueOperation.cc | 2 +- .../operations/COM_SetValueOperation.h | 4 +- .../operations/COM_SplitOperation.cc | 28 +-- .../operations/COM_SplitOperation.h | 4 +- .../operations/COM_SunBeamsOperation.cc | 17 +- .../operations/COM_TextureOperation.cc | 59 +++--- .../operations/COM_TextureOperation.h | 6 +- .../operations/COM_TonemapOperation.cc | 52 ++--- .../operations/COM_TonemapOperation.h | 2 +- .../operations/COM_TrackPositionOperation.cc | 79 ++++---- .../operations/COM_TrackPositionOperation.h | 16 +- .../operations/COM_TranslateOperation.cc | 26 +-- .../operations/COM_TranslateOperation.h | 16 +- .../COM_VariableSizeBokehBlurOperation.cc | 92 +++++---- .../COM_VariableSizeBokehBlurOperation.h | 8 +- .../operations/COM_VectorBlurOperation.cc | 58 +++--- .../operations/COM_VectorBlurOperation.h | 2 +- .../operations/COM_VectorCurveOperation.cc | 12 +- .../operations/COM_ViewerOperation.cc | 84 ++++---- .../operations/COM_ViewerOperation.h | 30 +-- .../operations/COM_WrapOperation.cc | 2 +- .../operations/COM_WriteBufferOperation.cc | 46 ++--- .../operations/COM_WriteBufferOperation.h | 2 +- .../operations/COM_ZCombineOperation.cc | 74 ++++---- 198 files changed, 2879 insertions(+), 2961 deletions(-) diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index 5d8355c181a..81043f1f163 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -22,14 +22,14 @@ namespace blender::compositor { CompositorContext::CompositorContext() { - this->m_scene = nullptr; - this->m_rd = nullptr; - this->m_quality = eCompositorQuality::High; - this->m_hasActiveOpenCLDevices = false; - this->m_fastCalculation = false; - this->m_viewSettings = nullptr; - this->m_displaySettings = nullptr; - this->m_bnodetree = nullptr; + m_scene = nullptr; + m_rd = nullptr; + m_quality = eCompositorQuality::High; + m_hasActiveOpenCLDevices = false; + m_fastCalculation = false; + m_viewSettings = nullptr; + m_displaySettings = nullptr; + m_bnodetree = nullptr; } int CompositorContext::getFramenumber() const diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index 1ae596736ae..835d565fc91 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -99,7 +99,7 @@ class CompositorContext { */ void setRendering(bool rendering) { - this->m_rendering = rendering; + m_rendering = rendering; } /** @@ -107,7 +107,7 @@ class CompositorContext { */ bool isRendering() const { - return this->m_rendering; + return m_rendering; } /** @@ -115,7 +115,7 @@ class CompositorContext { */ void setRenderData(RenderData *rd) { - this->m_rd = rd; + m_rd = rd; } /** @@ -123,7 +123,7 @@ class CompositorContext { */ void setbNodeTree(bNodeTree *bnodetree) { - this->m_bnodetree = bnodetree; + m_bnodetree = bnodetree; } /** @@ -131,7 +131,7 @@ class CompositorContext { */ const bNodeTree *getbNodeTree() const { - return this->m_bnodetree; + return m_bnodetree; } /** @@ -139,7 +139,7 @@ class CompositorContext { */ const RenderData *getRenderData() const { - return this->m_rd; + return m_rd; } void setScene(Scene *scene) @@ -156,7 +156,7 @@ class CompositorContext { */ void setPreviewHash(bNodeInstanceHash *previews) { - this->m_previews = previews; + m_previews = previews; } /** @@ -164,7 +164,7 @@ class CompositorContext { */ bNodeInstanceHash *getPreviewHash() const { - return this->m_previews; + return m_previews; } /** @@ -172,7 +172,7 @@ class CompositorContext { */ void setViewSettings(const ColorManagedViewSettings *viewSettings) { - this->m_viewSettings = viewSettings; + m_viewSettings = viewSettings; } /** @@ -180,7 +180,7 @@ class CompositorContext { */ const ColorManagedViewSettings *getViewSettings() const { - return this->m_viewSettings; + return m_viewSettings; } /** @@ -188,7 +188,7 @@ class CompositorContext { */ void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) { - this->m_displaySettings = displaySettings; + m_displaySettings = displaySettings; } /** @@ -196,7 +196,7 @@ class CompositorContext { */ const ColorManagedDisplaySettings *getDisplaySettings() const { - return this->m_displaySettings; + return m_displaySettings; } /** @@ -204,7 +204,7 @@ class CompositorContext { */ void setQuality(eCompositorQuality quality) { - this->m_quality = quality; + m_quality = quality; } /** @@ -212,7 +212,7 @@ class CompositorContext { */ eCompositorQuality getQuality() const { - return this->m_quality; + return m_quality; } /** @@ -225,7 +225,7 @@ class CompositorContext { */ bool getHasActiveOpenCLDevices() const { - return this->m_hasActiveOpenCLDevices; + return m_hasActiveOpenCLDevices; } /** @@ -233,7 +233,7 @@ class CompositorContext { */ void setHasActiveOpenCLDevices(bool hasAvtiveOpenCLDevices) { - this->m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices; + m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices; } /** Whether it has a view with a specific name and not the default one. */ @@ -247,7 +247,7 @@ class CompositorContext { */ const char *getViewName() const { - return this->m_viewName; + return m_viewName; } /** @@ -255,7 +255,7 @@ class CompositorContext { */ void setViewName(const char *viewName) { - this->m_viewName = viewName; + m_viewName = viewName; } int getChunksize() const @@ -265,11 +265,11 @@ class CompositorContext { void setFastCalculation(bool fastCalculation) { - this->m_fastCalculation = fastCalculation; + m_fastCalculation = fastCalculation; } bool isFastCalculation() const { - return this->m_fastCalculation; + return m_fastCalculation; } bool isGroupnodeBufferEnabled() const { diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc index 655bc030dec..8841f44ea48 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.cc +++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc @@ -56,16 +56,16 @@ std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags) ExecutionGroup::ExecutionGroup(int id) { m_id = id; - this->m_bTree = nullptr; - this->m_height = 0; - this->m_width = 0; - this->m_max_read_buffer_offset = 0; - this->m_x_chunks_len = 0; - this->m_y_chunks_len = 0; - this->m_chunks_len = 0; - this->m_chunks_finished = 0; - BLI_rcti_init(&this->m_viewerBorder, 0, 0, 0, 0); - this->m_executionStartTime = 0; + m_bTree = nullptr; + m_height = 0; + m_width = 0; + m_max_read_buffer_offset = 0; + m_x_chunks_len = 0; + m_y_chunks_len = 0; + m_chunks_len = 0; + m_chunks_finished = 0; + BLI_rcti_init(&m_viewerBorder, 0, 0, 0, 0); + m_executionStartTime = 0; } std::ostream &operator<<(std::ostream &os, const ExecutionGroup &execution_group) @@ -139,8 +139,8 @@ NodeOperation *ExecutionGroup::getOutputOperation() const void ExecutionGroup::init_work_packages() { m_work_packages.clear(); - if (this->m_chunks_len != 0) { - m_work_packages.resize(this->m_chunks_len); + if (m_chunks_len != 0) { + m_work_packages.resize(m_chunks_len); for (unsigned int index = 0; index < m_chunks_len; index++) { m_work_packages[index].type = eWorkPackageType::Tile; m_work_packages[index].state = eWorkPackageState::NotScheduled; @@ -157,12 +157,12 @@ void ExecutionGroup::init_read_buffer_operations() for (NodeOperation *operation : m_operations) { if (operation->get_flags().is_read_buffer_operation) { ReadBufferOperation *readOperation = static_cast(operation); - this->m_read_operations.append(readOperation); + m_read_operations.append(readOperation); max_offset = MAX2(max_offset, readOperation->getOffset()); } } max_offset++; - this->m_max_read_buffer_offset = max_offset; + m_max_read_buffer_offset = max_offset; } void ExecutionGroup::initExecution() @@ -175,11 +175,11 @@ void ExecutionGroup::initExecution() void ExecutionGroup::deinitExecution() { m_work_packages.clear(); - this->m_chunks_len = 0; - this->m_x_chunks_len = 0; - this->m_y_chunks_len = 0; - this->m_read_operations.clear(); - this->m_bTree = nullptr; + m_chunks_len = 0; + m_x_chunks_len = 0; + m_y_chunks_len = 0; + m_read_operations.clear(); + m_bTree = nullptr; } void ExecutionGroup::determineResolution(unsigned int resolution[2]) @@ -188,30 +188,30 @@ void ExecutionGroup::determineResolution(unsigned int resolution[2]) resolution[0] = operation->getWidth(); resolution[1] = operation->getHeight(); this->setResolution(resolution); - BLI_rcti_init(&this->m_viewerBorder, 0, this->m_width, 0, this->m_height); + BLI_rcti_init(&m_viewerBorder, 0, m_width, 0, m_height); } void ExecutionGroup::init_number_of_chunks() { - if (this->m_flags.single_threaded) { - this->m_x_chunks_len = 1; - this->m_y_chunks_len = 1; - this->m_chunks_len = 1; + if (m_flags.single_threaded) { + m_x_chunks_len = 1; + m_y_chunks_len = 1; + m_chunks_len = 1; } else { - const float chunkSizef = this->m_chunkSize; - const int border_width = BLI_rcti_size_x(&this->m_viewerBorder); - const int border_height = BLI_rcti_size_y(&this->m_viewerBorder); - this->m_x_chunks_len = ceil(border_width / chunkSizef); - this->m_y_chunks_len = ceil(border_height / chunkSizef); - this->m_chunks_len = this->m_x_chunks_len * this->m_y_chunks_len; + const float chunkSizef = m_chunkSize; + const int border_width = BLI_rcti_size_x(&m_viewerBorder); + const int border_height = BLI_rcti_size_y(&m_viewerBorder); + m_x_chunks_len = ceil(border_width / chunkSizef); + m_y_chunks_len = ceil(border_height / chunkSizef); + m_chunks_len = m_x_chunks_len * m_y_chunks_len; } } blender::Array ExecutionGroup::get_execution_order() const { blender::Array chunk_order(m_chunks_len); - for (int chunk_index = 0; chunk_index < this->m_chunks_len; chunk_index++) { + for (int chunk_index = 0; chunk_index < m_chunks_len; chunk_index++) { chunk_order[chunk_index] = chunk_index; } @@ -227,8 +227,8 @@ blender::Array ExecutionGroup::get_execution_order() const order_type = viewer->getChunkOrder(); } - const int border_width = BLI_rcti_size_x(&this->m_viewerBorder); - const int border_height = BLI_rcti_size_y(&this->m_viewerBorder); + const int border_width = BLI_rcti_size_x(&m_viewerBorder); + const int border_height = BLI_rcti_size_y(&m_viewerBorder); int index; switch (order_type) { case ChunkOrdering::Random: { @@ -242,16 +242,16 @@ blender::Array ExecutionGroup::get_execution_order() const case ChunkOrdering::CenterOut: { ChunkOrderHotspot hotspot(border_width * centerX, border_height * centerY, 0.0f); blender::Array chunk_orders(m_chunks_len); - for (index = 0; index < this->m_chunks_len; index++) { + for (index = 0; index < m_chunks_len; index++) { const WorkPackage &work_package = m_work_packages[index]; chunk_orders[index].index = index; - chunk_orders[index].x = work_package.rect.xmin - this->m_viewerBorder.xmin; - chunk_orders[index].y = work_package.rect.ymin - this->m_viewerBorder.ymin; + chunk_orders[index].x = work_package.rect.xmin - m_viewerBorder.xmin; + chunk_orders[index].y = work_package.rect.ymin - m_viewerBorder.ymin; chunk_orders[index].update_distance(&hotspot, 1); } - std::sort(&chunk_orders[0], &chunk_orders[this->m_chunks_len - 1]); - for (index = 0; index < this->m_chunks_len; index++) { + std::sort(&chunk_orders[0], &chunk_orders[m_chunks_len - 1]); + for (index = 0; index < m_chunks_len; index++) { chunk_order[index] = chunk_orders[index].index; } @@ -264,7 +264,7 @@ blender::Array ExecutionGroup::get_execution_order() const unsigned int my = border_height / 2; unsigned int bx = mx + 2 * tx; unsigned int by = my + 2 * ty; - float addition = this->m_chunks_len / COM_RULE_OF_THIRDS_DIVIDER; + float addition = m_chunks_len / COM_RULE_OF_THIRDS_DIVIDER; ChunkOrderHotspot hotspots[9]{ ChunkOrderHotspot(mx, my, addition * 0), @@ -279,17 +279,17 @@ blender::Array ExecutionGroup::get_execution_order() const }; blender::Array chunk_orders(m_chunks_len); - for (index = 0; index < this->m_chunks_len; index++) { + for (index = 0; index < m_chunks_len; index++) { const WorkPackage &work_package = m_work_packages[index]; chunk_orders[index].index = index; - chunk_orders[index].x = work_package.rect.xmin - this->m_viewerBorder.xmin; - chunk_orders[index].y = work_package.rect.ymin - this->m_viewerBorder.ymin; + chunk_orders[index].x = work_package.rect.xmin - m_viewerBorder.xmin; + chunk_orders[index].y = work_package.rect.ymin - m_viewerBorder.ymin; chunk_orders[index].update_distance(hotspots, 9); } - std::sort(&chunk_orders[0], &chunk_orders[this->m_chunks_len]); + std::sort(&chunk_orders[0], &chunk_orders[m_chunks_len]); - for (index = 0; index < this->m_chunks_len; index++) { + for (index = 0; index < m_chunks_len; index++) { chunk_order[index] = chunk_orders[index].index; } @@ -310,21 +310,21 @@ void ExecutionGroup::execute(ExecutionSystem *graph) { const CompositorContext &context = graph->getContext(); const bNodeTree *bTree = context.getbNodeTree(); - if (this->m_width == 0 || this->m_height == 0) { + if (m_width == 0 || m_height == 0) { return; } /** \note Break out... no pixels to calculate. */ if (bTree->test_break && bTree->test_break(bTree->tbh)) { return; } /** \note Early break out for blur and preview nodes. */ - if (this->m_chunks_len == 0) { + if (m_chunks_len == 0) { return; } /** \note Early break out. */ unsigned int chunk_index; - this->m_executionStartTime = PIL_check_seconds_timer(); + m_executionStartTime = PIL_check_seconds_timer(); - this->m_chunks_finished = 0; - this->m_bTree = bTree; + m_chunks_finished = 0; + m_bTree = bTree; blender::Array chunk_order = get_execution_order(); @@ -341,12 +341,11 @@ void ExecutionGroup::execute(ExecutionSystem *graph) finished = true; int numberEvaluated = 0; - for (int index = startIndex; - index < this->m_chunks_len && numberEvaluated < maxNumberEvaluated; + for (int index = startIndex; index < m_chunks_len && numberEvaluated < maxNumberEvaluated; index++) { chunk_index = chunk_order[index]; - int yChunk = chunk_index / this->m_x_chunks_len; - int xChunk = chunk_index - (yChunk * this->m_x_chunks_len); + int yChunk = chunk_index / m_x_chunks_len; + int xChunk = chunk_index - (yChunk * m_x_chunks_len); const WorkPackage &work_package = m_work_packages[chunk_index]; switch (work_package.state) { case eWorkPackageState::NotScheduled: { @@ -389,7 +388,7 @@ MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber) WorkPackage &work_package = m_work_packages[chunkNumber]; MemoryBuffer **memoryBuffers = (MemoryBuffer **)MEM_callocN( - sizeof(MemoryBuffer *) * this->m_max_read_buffer_offset, __func__); + sizeof(MemoryBuffer *) * m_max_read_buffer_offset, __func__); rcti output; for (ReadBufferOperation *readOperation : m_read_operations) { MemoryProxy *memoryProxy = readOperation->getMemoryProxy(); @@ -417,9 +416,9 @@ void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memo work_package.state = eWorkPackageState::Executed; } - atomic_add_and_fetch_u(&this->m_chunks_finished, 1); + atomic_add_and_fetch_u(&m_chunks_finished, 1); if (memoryBuffers) { - for (unsigned int index = 0; index < this->m_max_read_buffer_offset; index++) { + for (unsigned int index = 0; index < m_max_read_buffer_offset; index++) { MemoryBuffer *buffer = memoryBuffers[index]; if (buffer) { if (buffer->isTemporarily()) { @@ -430,19 +429,16 @@ void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memo } MEM_freeN(memoryBuffers); } - if (this->m_bTree) { + if (m_bTree) { /* Status report is only performed for top level Execution Groups. */ - float progress = this->m_chunks_finished; - progress /= this->m_chunks_len; - this->m_bTree->progress(this->m_bTree->prh, progress); + float progress = m_chunks_finished; + progress /= m_chunks_len; + m_bTree->progress(m_bTree->prh, progress); char buf[128]; - BLI_snprintf(buf, - sizeof(buf), - TIP_("Compositing | Tile %u-%u"), - this->m_chunks_finished, - this->m_chunks_len); - this->m_bTree->stats_draw(this->m_bTree->sdh, buf); + BLI_snprintf( + buf, sizeof(buf), TIP_("Compositing | Tile %u-%u"), m_chunks_finished, m_chunks_len); + m_bTree->stats_draw(m_bTree->sdh, buf); } } @@ -450,30 +446,29 @@ inline void ExecutionGroup::determineChunkRect(rcti *r_rect, const unsigned int xChunk, const unsigned int yChunk) const { - const int border_width = BLI_rcti_size_x(&this->m_viewerBorder); - const int border_height = BLI_rcti_size_y(&this->m_viewerBorder); + const int border_width = BLI_rcti_size_x(&m_viewerBorder); + const int border_height = BLI_rcti_size_y(&m_viewerBorder); - if (this->m_flags.single_threaded) { - BLI_rcti_init( - r_rect, this->m_viewerBorder.xmin, border_width, this->m_viewerBorder.ymin, border_height); + if (m_flags.single_threaded) { + BLI_rcti_init(r_rect, m_viewerBorder.xmin, border_width, m_viewerBorder.ymin, border_height); } else { - const unsigned int minx = xChunk * this->m_chunkSize + this->m_viewerBorder.xmin; - const unsigned int miny = yChunk * this->m_chunkSize + this->m_viewerBorder.ymin; - const unsigned int width = MIN2((unsigned int)this->m_viewerBorder.xmax, this->m_width); - const unsigned int height = MIN2((unsigned int)this->m_viewerBorder.ymax, this->m_height); + const unsigned int minx = xChunk * m_chunkSize + m_viewerBorder.xmin; + const unsigned int miny = yChunk * m_chunkSize + m_viewerBorder.ymin; + const unsigned int width = MIN2((unsigned int)m_viewerBorder.xmax, m_width); + const unsigned int height = MIN2((unsigned int)m_viewerBorder.ymax, m_height); BLI_rcti_init(r_rect, - MIN2(minx, this->m_width), - MIN2(minx + this->m_chunkSize, width), - MIN2(miny, this->m_height), - MIN2(miny + this->m_chunkSize, height)); + MIN2(minx, m_width), + MIN2(minx + m_chunkSize, width), + MIN2(miny, m_height), + MIN2(miny + m_chunkSize, height)); } } void ExecutionGroup::determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const { - const unsigned int yChunk = chunkNumber / this->m_x_chunks_len; - const unsigned int xChunk = chunkNumber - (yChunk * this->m_x_chunks_len); + const unsigned int yChunk = chunkNumber / m_x_chunks_len; + const unsigned int xChunk = chunkNumber - (yChunk * m_x_chunks_len); determineChunkRect(r_rect, xChunk, yChunk); } @@ -492,7 +487,7 @@ MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect) bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area) { - if (this->m_flags.single_threaded) { + if (m_flags.single_threaded) { return scheduleChunkWhenPossible(graph, 0, 0); } /* Find all chunks inside the rect @@ -540,15 +535,15 @@ bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph, const int chunk_x, const int chunk_y) { - if (chunk_x < 0 || chunk_x >= (int)this->m_x_chunks_len) { + if (chunk_x < 0 || chunk_x >= (int)m_x_chunks_len) { return true; } - if (chunk_y < 0 || chunk_y >= (int)this->m_y_chunks_len) { + if (chunk_y < 0 || chunk_y >= (int)m_y_chunks_len) { return true; } /* Check if chunk is already executed or scheduled and not yet executed. */ - const int chunk_index = chunk_y * this->m_x_chunks_len + chunk_x; + const int chunk_index = chunk_y * m_x_chunks_len + chunk_x; WorkPackage &work_package = m_work_packages[chunk_index]; if (work_package.state == eWorkPackageState::Executed) { return true; @@ -589,11 +584,8 @@ void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float y { const NodeOperation &operation = *this->getOutputOperation(); if (operation.get_flags().use_viewer_border) { - BLI_rcti_init(&this->m_viewerBorder, - xmin * this->m_width, - xmax * this->m_width, - ymin * this->m_height, - ymax * this->m_height); + BLI_rcti_init( + &m_viewerBorder, xmin * m_width, xmax * m_width, ymin * m_height, ymax * m_height); } } @@ -601,11 +593,8 @@ void ExecutionGroup::setRenderBorder(float xmin, float xmax, float ymin, float y { const NodeOperation &operation = *this->getOutputOperation(); if (operation.isOutputOperation(true) && operation.get_flags().use_render_border) { - BLI_rcti_init(&this->m_viewerBorder, - xmin * this->m_width, - xmax * this->m_width, - ymin * this->m_height, - ymax * this->m_height); + BLI_rcti_init( + &m_viewerBorder, xmin * m_width, xmax * m_width, ymin * m_height, ymax * m_height); } } diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.h b/source/blender/compositor/intern/COM_ExecutionGroup.h index 58386c959a4..2799bef80d4 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.h +++ b/source/blender/compositor/intern/COM_ExecutionGroup.h @@ -266,7 +266,7 @@ class ExecutionGroup { */ void setOutputExecutionGroup(bool is_output) { - this->m_flags.is_output = is_output; + m_flags.is_output = is_output; } /** @@ -281,8 +281,8 @@ class ExecutionGroup { */ void setResolution(unsigned int resolution[2]) { - this->m_width = resolution[0]; - this->m_height = resolution[1]; + m_width = resolution[0]; + m_height = resolution[1]; } /** @@ -381,7 +381,7 @@ class ExecutionGroup { void setChunksize(int chunksize) { - this->m_chunkSize = chunksize; + m_chunkSize = chunksize; } /** diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index 9b1ec823f75..510331f3294 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -43,25 +43,25 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, const char *viewName) { num_work_threads_ = WorkScheduler::get_num_cpu_threads(); - this->m_context.setViewName(viewName); - this->m_context.setScene(scene); - this->m_context.setbNodeTree(editingtree); - this->m_context.setPreviewHash(editingtree->previews); - this->m_context.setFastCalculation(fastcalculation); + m_context.setViewName(viewName); + m_context.setScene(scene); + m_context.setbNodeTree(editingtree); + m_context.setPreviewHash(editingtree->previews); + m_context.setFastCalculation(fastcalculation); /* initialize the CompositorContext */ if (rendering) { - this->m_context.setQuality((eCompositorQuality)editingtree->render_quality); + m_context.setQuality((eCompositorQuality)editingtree->render_quality); } else { - this->m_context.setQuality((eCompositorQuality)editingtree->edit_quality); + m_context.setQuality((eCompositorQuality)editingtree->edit_quality); } - this->m_context.setRendering(rendering); - this->m_context.setHasActiveOpenCLDevices(WorkScheduler::has_gpu_devices() && - (editingtree->flag & NTREE_COM_OPENCL)); + m_context.setRendering(rendering); + m_context.setHasActiveOpenCLDevices(WorkScheduler::has_gpu_devices() && + (editingtree->flag & NTREE_COM_OPENCL)); - this->m_context.setRenderData(rd); - this->m_context.setViewSettings(viewSettings); - this->m_context.setDisplaySettings(displaySettings); + m_context.setRenderData(rd); + m_context.setViewSettings(viewSettings); + m_context.setDisplaySettings(displaySettings); BLI_mutex_init(&work_mutex_); BLI_condition_init(&work_finished_cond_); @@ -94,12 +94,12 @@ ExecutionSystem::~ExecutionSystem() for (NodeOperation *operation : m_operations) { delete operation; } - this->m_operations.clear(); + m_operations.clear(); for (ExecutionGroup *group : m_groups) { delete group; } - this->m_groups.clear(); + m_groups.clear(); } void ExecutionSystem::set_operations(const Vector &operations, diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index eb0ad805217..303111b8b42 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -200,7 +200,7 @@ class ExecutionSystem { */ const CompositorContext &getContext() const { - return this->m_context; + return m_context; } SharedOperationBuffers &get_active_buffers() diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index b80be46d46a..6851c3b5c5c 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -47,14 +47,14 @@ static rcti create_rect(const int width, const int height) MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state) { m_rect = rect; - this->m_is_a_single_elem = false; - this->m_memoryProxy = memoryProxy; - this->m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType()); - this->m_buffer = (float *)MEM_mallocN_aligned( - sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer"); + m_is_a_single_elem = false; + m_memoryProxy = memoryProxy; + m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType()); + m_buffer = (float *)MEM_mallocN_aligned( + sizeof(float) * buffer_len() * m_num_channels, 16, "COM_MemoryBuffer"); owns_data_ = true; - this->m_state = state; - this->m_datatype = memoryProxy->getDataType(); + m_state = state; + m_datatype = memoryProxy->getDataType(); set_strides(); } @@ -62,14 +62,14 @@ MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBuf MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single_elem) { m_rect = rect; - this->m_is_a_single_elem = is_a_single_elem; - this->m_memoryProxy = nullptr; - this->m_num_channels = COM_data_type_num_channels(dataType); - this->m_buffer = (float *)MEM_mallocN_aligned( - sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer"); + m_is_a_single_elem = is_a_single_elem; + m_memoryProxy = nullptr; + m_num_channels = COM_data_type_num_channels(dataType); + m_buffer = (float *)MEM_mallocN_aligned( + sizeof(float) * buffer_len() * m_num_channels, 16, "COM_MemoryBuffer"); owns_data_ = true; - this->m_state = MemoryBufferState::Temporary; - this->m_datatype = dataType; + m_state = MemoryBufferState::Temporary; + m_datatype = dataType; set_strides(); } @@ -153,20 +153,20 @@ BuffersIterator MemoryBuffer::iterate_with(Span inputs, c MemoryBuffer *MemoryBuffer::inflate() const { BLI_assert(is_a_single_elem()); - MemoryBuffer *inflated = new MemoryBuffer(this->m_datatype, this->m_rect, false); - inflated->copy_from(this, this->m_rect); + MemoryBuffer *inflated = new MemoryBuffer(m_datatype, m_rect, false); + inflated->copy_from(this, m_rect); return inflated; } float MemoryBuffer::get_max_value() const { - float result = this->m_buffer[0]; + float result = m_buffer[0]; const unsigned int size = this->buffer_len(); unsigned int i; - const float *fp_src = this->m_buffer; + const float *fp_src = m_buffer; - for (i = 0; i < size; i++, fp_src += this->m_num_channels) { + for (i = 0; i < size; i++, fp_src += m_num_channels) { float value = *fp_src; if (value > result) { result = value; @@ -181,10 +181,10 @@ float MemoryBuffer::get_max_value(const rcti &rect) const rcti rect_clamp; /* first clamp the rect by the bounds or we get un-initialized values */ - BLI_rcti_isect(&rect, &this->m_rect, &rect_clamp); + BLI_rcti_isect(&rect, &m_rect, &rect_clamp); if (!BLI_rcti_is_empty(&rect_clamp)) { - MemoryBuffer temp_buffer(this->m_datatype, rect_clamp); + MemoryBuffer temp_buffer(m_datatype, rect_clamp); temp_buffer.fill_from(*this); return temp_buffer.get_max_value(); } @@ -195,9 +195,9 @@ float MemoryBuffer::get_max_value(const rcti &rect) const MemoryBuffer::~MemoryBuffer() { - if (this->m_buffer && owns_data_) { - MEM_freeN(this->m_buffer); - this->m_buffer = nullptr; + if (m_buffer && owns_data_) { + MEM_freeN(m_buffer); + m_buffer = nullptr; } } @@ -398,30 +398,28 @@ void MemoryBuffer::fill(const rcti &area, void MemoryBuffer::fill_from(const MemoryBuffer &src) { rcti overlap; - overlap.xmin = MAX2(this->m_rect.xmin, src.m_rect.xmin); - overlap.xmax = MIN2(this->m_rect.xmax, src.m_rect.xmax); - overlap.ymin = MAX2(this->m_rect.ymin, src.m_rect.ymin); - overlap.ymax = MIN2(this->m_rect.ymax, src.m_rect.ymax); + overlap.xmin = MAX2(m_rect.xmin, src.m_rect.xmin); + overlap.xmax = MIN2(m_rect.xmax, src.m_rect.xmax); + overlap.ymin = MAX2(m_rect.ymin, src.m_rect.ymin); + overlap.ymax = MIN2(m_rect.ymax, src.m_rect.ymax); copy_from(&src, overlap); } void MemoryBuffer::writePixel(int x, int y, const float color[4]) { - if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin && - y < this->m_rect.ymax) { + if (x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax) { const int offset = get_coords_offset(x, y); - memcpy(&this->m_buffer[offset], color, sizeof(float) * this->m_num_channels); + memcpy(&m_buffer[offset], color, sizeof(float) * m_num_channels); } } void MemoryBuffer::addPixel(int x, int y, const float color[4]) { - if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin && - y < this->m_rect.ymax) { + if (x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax) { const int offset = get_coords_offset(x, y); - float *dst = &this->m_buffer[offset]; + float *dst = &m_buffer[offset]; const float *src = color; - for (int i = 0; i < this->m_num_channels; i++, dst++, src++) { + for (int i = 0; i < m_num_channels; i++, dst++, src++) { *dst += *src; } } @@ -436,7 +434,7 @@ static void read_ewa_elem(void *userdata, int x, int y, float result[4]) void MemoryBuffer::read_elem_filtered( const float x, const float y, float dx[2], float dy[2], float *out) const { - BLI_assert(this->m_datatype == DataType::Color); + BLI_assert(m_datatype == DataType::Color); const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; @@ -472,10 +470,10 @@ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4] void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2]) { if (m_is_a_single_elem) { - memcpy(result, m_buffer, sizeof(float) * this->m_num_channels); + memcpy(result, m_buffer, sizeof(float) * m_num_channels); } else { - BLI_assert(this->m_datatype == DataType::Color); + BLI_assert(m_datatype == DataType::Color); float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight(); /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives, * but compositor uses pixel space. For now let's just divide the values and diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 1eafc42ea69..c0d086e5727 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -330,12 +330,12 @@ class MemoryBuffer { uint8_t get_num_channels() const { - return this->m_num_channels; + return m_num_channels; } uint8_t get_elem_bytes_len() const { - return this->m_num_channels * sizeof(float); + return m_num_channels * sizeof(float); } /** @@ -370,13 +370,13 @@ class MemoryBuffer { */ float *getBuffer() { - return this->m_buffer; + return m_buffer; } float *release_ownership_buffer() { owns_data_ = false; - return this->m_buffer; + return m_buffer; } MemoryBuffer *inflate() const; @@ -494,15 +494,15 @@ class MemoryBuffer { bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < m_rect.ymin || y >= m_rect.ymax)); if (clip_x || clip_y) { /* clip result outside rect is zero */ - memset(result, 0, this->m_num_channels * sizeof(float)); + memset(result, 0, m_num_channels * sizeof(float)); } else { int u = x; int v = y; this->wrap_pixel(u, v, extend_x, extend_y); const int offset = get_coords_offset(u, v); - float *buffer = &this->m_buffer[offset]; - memcpy(result, buffer, sizeof(float) * this->m_num_channels); + float *buffer = &m_buffer[offset]; + memcpy(result, buffer, sizeof(float) * m_num_channels); } } @@ -520,11 +520,11 @@ class MemoryBuffer { const int offset = get_coords_offset(u, v); BLI_assert(offset >= 0); - BLI_assert(offset < this->buffer_len() * this->m_num_channels); + BLI_assert(offset < this->buffer_len() * m_num_channels); BLI_assert(!(extend_x == MemoryBufferExtend::Clip && (u < m_rect.xmin || u >= m_rect.xmax)) && !(extend_y == MemoryBufferExtend::Clip && (v < m_rect.ymin || v >= m_rect.ymax))); - float *buffer = &this->m_buffer[offset]; - memcpy(result, buffer, sizeof(float) * this->m_num_channels); + float *buffer = &m_buffer[offset]; + memcpy(result, buffer, sizeof(float) * m_num_channels); } void writePixel(int x, int y, const float color[4]); @@ -540,18 +540,18 @@ class MemoryBuffer { this->wrap_pixel(u, v, extend_x, extend_y); if ((extend_x != MemoryBufferExtend::Repeat && (u < 0.0f || u >= getWidth())) || (extend_y != MemoryBufferExtend::Repeat && (v < 0.0f || v >= getHeight()))) { - copy_vn_fl(result, this->m_num_channels, 0.0f); + copy_vn_fl(result, m_num_channels, 0.0f); return; } if (m_is_a_single_elem) { - memcpy(result, m_buffer, sizeof(float) * this->m_num_channels); + memcpy(result, m_buffer, sizeof(float) * m_num_channels); } else { - BLI_bilinear_interpolation_wrap_fl(this->m_buffer, + BLI_bilinear_interpolation_wrap_fl(m_buffer, result, getWidth(), getHeight(), - this->m_num_channels, + m_num_channels, u, v, extend_x == MemoryBufferExtend::Repeat, @@ -566,7 +566,7 @@ class MemoryBuffer { */ inline bool isTemporarily() const { - return this->m_state == MemoryBufferState::Temporary; + return m_state == MemoryBufferState::Temporary; } void copy_from(const MemoryBuffer *src, const rcti &area); @@ -632,7 +632,7 @@ class MemoryBuffer { */ const rcti &get_rect() const { - return this->m_rect; + return m_rect; } /** @@ -668,7 +668,7 @@ class MemoryBuffer { void clear_elem(float *out) const { - memset(out, 0, this->m_num_channels * sizeof(float)); + memset(out, 0, m_num_channels * sizeof(float)); } template T get_relative_x(T x) const diff --git a/source/blender/compositor/intern/COM_MemoryProxy.cc b/source/blender/compositor/intern/COM_MemoryProxy.cc index 58dea234ced..6502a8f4b9a 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.cc +++ b/source/blender/compositor/intern/COM_MemoryProxy.cc @@ -23,9 +23,9 @@ namespace blender::compositor { MemoryProxy::MemoryProxy(DataType datatype) { - this->m_writeBufferOperation = nullptr; - this->m_executor = nullptr; - this->m_datatype = datatype; + m_writeBufferOperation = nullptr; + m_executor = nullptr; + m_datatype = datatype; } void MemoryProxy::allocate(unsigned int width, unsigned int height) @@ -36,14 +36,14 @@ void MemoryProxy::allocate(unsigned int width, unsigned int height) result.ymin = 0; result.ymax = height; - this->m_buffer = new MemoryBuffer(this, result, MemoryBufferState::Default); + m_buffer = new MemoryBuffer(this, result, MemoryBufferState::Default); } void MemoryProxy::free() { - if (this->m_buffer) { - delete this->m_buffer; - this->m_buffer = nullptr; + if (m_buffer) { + delete m_buffer; + m_buffer = nullptr; } } diff --git a/source/blender/compositor/intern/COM_MemoryProxy.h b/source/blender/compositor/intern/COM_MemoryProxy.h index 6814afada74..cae52182f26 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.h +++ b/source/blender/compositor/intern/COM_MemoryProxy.h @@ -68,7 +68,7 @@ class MemoryProxy { */ void setExecutor(ExecutionGroup *executor) { - this->m_executor = executor; + m_executor = executor; } /** @@ -76,7 +76,7 @@ class MemoryProxy { */ ExecutionGroup *getExecutor() const { - return this->m_executor; + return m_executor; } /** @@ -85,7 +85,7 @@ class MemoryProxy { */ void setWriteBufferOperation(WriteBufferOperation *operation) { - this->m_writeBufferOperation = operation; + m_writeBufferOperation = operation; } /** @@ -94,7 +94,7 @@ class MemoryProxy { */ WriteBufferOperation *getWriteBufferOperation() const { - return this->m_writeBufferOperation; + return m_writeBufferOperation; } /** @@ -112,12 +112,12 @@ class MemoryProxy { */ inline MemoryBuffer *getBuffer() { - return this->m_buffer; + return m_buffer; } inline DataType getDataType() { - return this->m_datatype; + return m_datatype; } #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h index 08d0b9a4fd5..76eb03693ae 100644 --- a/source/blender/compositor/intern/COM_Node.h +++ b/source/blender/compositor/intern/COM_Node.h @@ -97,7 +97,7 @@ class Node { */ void setbNode(bNode *node) { - this->m_editorNode = node; + m_editorNode = node; } /** @@ -106,7 +106,7 @@ class Node { */ void setbNodeTree(bNodeTree *nodetree) { - this->m_editorNodeTree = nodetree; + m_editorNodeTree = nodetree; } /** @@ -145,7 +145,7 @@ class Node { */ void setIsInActiveGroup(bool value) { - this->m_inActiveGroup = value; + m_inActiveGroup = value; } /** @@ -156,7 +156,7 @@ class Node { */ inline bool isInActiveGroup() const { - return this->m_inActiveGroup; + return m_inActiveGroup; } /** @@ -222,7 +222,7 @@ class NodeInput { Node *getNode() const { - return this->m_node; + return m_node; } DataType getDataType() const { @@ -230,7 +230,7 @@ class NodeInput { } bNodeSocket *getbNodeSocket() const { - return this->m_editorSocket; + return m_editorSocket; } void setLink(NodeOutput *link); @@ -264,7 +264,7 @@ class NodeOutput { Node *getNode() const { - return this->m_node; + return m_node; } DataType getDataType() const { @@ -272,7 +272,7 @@ class NodeOutput { } bNodeSocket *getbNodeSocket() const { - return this->m_editorSocket; + return m_editorSocket; } float getEditorValueFloat(); diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index a34fdd64da9..914842c816c 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -34,7 +34,7 @@ NodeOperation::NodeOperation() { canvas_input_index_ = 0; canvas_ = COM_AREA_NONE; - this->m_btree = nullptr; + m_btree = nullptr; } /** Get constant value when operation is constant, otherwise return default_value. */ @@ -179,22 +179,22 @@ void NodeOperation::initExecution() void NodeOperation::initMutex() { - BLI_mutex_init(&this->m_mutex); + BLI_mutex_init(&m_mutex); } void NodeOperation::lockMutex() { - BLI_mutex_lock(&this->m_mutex); + BLI_mutex_lock(&m_mutex); } void NodeOperation::unlockMutex() { - BLI_mutex_unlock(&this->m_mutex); + BLI_mutex_unlock(&m_mutex); } void NodeOperation::deinitMutex() { - BLI_mutex_end(&this->m_mutex); + BLI_mutex_end(&m_mutex); } void NodeOperation::deinitExecution() diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index 0483090d8ca..2bdc2bfeb0b 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -125,11 +125,11 @@ class NodeOperationInput { void setResizeMode(ResizeMode resizeMode) { - this->m_resizeMode = resizeMode; + m_resizeMode = resizeMode; } ResizeMode getResizeMode() const { - return this->m_resizeMode; + return m_resizeMode; } SocketReader *getReader(); @@ -442,7 +442,7 @@ class NodeOperation { void setbNodeTree(const bNodeTree *tree) { - this->m_btree = tree; + m_btree = tree; } void set_execution_system(ExecutionSystem *system) @@ -561,13 +561,13 @@ class NodeOperation { inline bool isBraked() const { - return this->m_btree->test_break(this->m_btree->tbh); + return m_btree->test_break(m_btree->tbh); } inline void updateDraw() { - if (this->m_btree->update_draw) { - this->m_btree->update_draw(this->m_btree->udh); + if (m_btree->update_draw) { + m_btree->update_draw(m_btree->udh); } } diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 2801cb401be..151356efb92 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -753,7 +753,7 @@ static void add_group_operations_recursive(Tags &visited, NodeOperation *op, Exe ExecutionGroup *NodeOperationBuilder::make_group(NodeOperation *op) { - ExecutionGroup *group = new ExecutionGroup(this->m_groups.size()); + ExecutionGroup *group = new ExecutionGroup(m_groups.size()); m_groups.append(group); Tags visited; diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cc b/source/blender/compositor/intern/COM_OpenCLDevice.cc index 815bac4009e..0b52d92e92d 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.cc +++ b/source/blender/compositor/intern/COM_OpenCLDevice.cc @@ -42,14 +42,14 @@ OpenCLDevice::OpenCLDevice(cl_context context, cl_program program, cl_int vendorId) { - this->m_device = device; - this->m_context = context; - this->m_program = program; - this->m_queue = nullptr; - this->m_vendorID = vendorId; + m_device = device; + m_context = context; + m_program = program; + m_queue = nullptr; + m_vendorID = vendorId; cl_int error; - this->m_queue = clCreateCommandQueue(this->m_context, this->m_device, 0, &error); + m_queue = clCreateCommandQueue(m_context, m_device, 0, &error); } OpenCLDevice::OpenCLDevice(OpenCLDevice &&other) noexcept @@ -64,8 +64,8 @@ OpenCLDevice::OpenCLDevice(OpenCLDevice &&other) noexcept OpenCLDevice::~OpenCLDevice() { - if (this->m_queue) { - clReleaseCommandQueue(this->m_queue); + if (m_queue) { + clReleaseCommandQueue(m_queue); } } @@ -131,7 +131,7 @@ cl_mem OpenCLDevice::COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, const cl_image_format *imageFormat = determineImageFormat(result); - cl_mem clBuffer = clCreateImage2D(this->m_context, + cl_mem clBuffer = clCreateImage2D(m_context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, imageFormat, result->getWidth(), @@ -206,8 +206,7 @@ void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, MemoryBuffer *outputMemo (size_t)outputMemoryBuffer->getHeight(), }; - error = clEnqueueNDRangeKernel( - this->m_queue, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); + error = clEnqueueNDRangeKernel(m_queue, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } @@ -227,7 +226,7 @@ void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, size_t size[2]; cl_int2 offset; - if (this->m_vendorID == NVIDIA) { + if (m_vendorID == NVIDIA) { localSize = 32; } @@ -255,11 +254,11 @@ void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } error = clEnqueueNDRangeKernel( - this->m_queue, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); + m_queue, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - clFlush(this->m_queue); + clFlush(m_queue); if (operation->isBraked()) { breaked = false; } @@ -271,7 +270,7 @@ cl_kernel OpenCLDevice::COM_clCreateKernel(const char *kernelname, std::list *clKernelsToCleanUp) { cl_int error; - cl_kernel kernel = clCreateKernel(this->m_program, kernelname, &error); + cl_kernel kernel = clCreateKernel(m_program, kernelname, &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.h b/source/blender/compositor/intern/COM_OpenCLDevice.h index 3de9d01c28c..9c72fa31d95 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.h +++ b/source/blender/compositor/intern/COM_OpenCLDevice.h @@ -93,12 +93,12 @@ class OpenCLDevice : public Device { cl_context getContext() { - return this->m_context; + return m_context; } cl_command_queue getQueue() { - return this->m_queue; + return m_queue; } cl_mem COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc index 01be6e1afed..7d7ae0ab155 100644 --- a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc +++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc @@ -22,7 +22,7 @@ namespace blender::compositor { SingleThreadedOperation::SingleThreadedOperation() { - this->m_cachedInstance = nullptr; + m_cachedInstance = nullptr; flags.complex = true; flags.single_threaded = true; } @@ -34,30 +34,30 @@ void SingleThreadedOperation::initExecution() void SingleThreadedOperation::executePixel(float output[4], int x, int y, void * /*data*/) { - this->m_cachedInstance->readNoCheck(output, x, y); + m_cachedInstance->readNoCheck(output, x, y); } void SingleThreadedOperation::deinitExecution() { deinitMutex(); - if (this->m_cachedInstance) { - delete this->m_cachedInstance; - this->m_cachedInstance = nullptr; + if (m_cachedInstance) { + delete m_cachedInstance; + m_cachedInstance = nullptr; } } void *SingleThreadedOperation::initializeTileData(rcti *rect) { - if (this->m_cachedInstance) { - return this->m_cachedInstance; + if (m_cachedInstance) { + return m_cachedInstance; } lockMutex(); - if (this->m_cachedInstance == nullptr) { + if (m_cachedInstance == nullptr) { // - this->m_cachedInstance = createMemoryBuffer(rect); + m_cachedInstance = createMemoryBuffer(rect); } unlockMutex(); - return this->m_cachedInstance; + return m_cachedInstance; } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.h b/source/blender/compositor/intern/COM_SingleThreadedOperation.h index 9945f938ff9..ac81b495d6f 100644 --- a/source/blender/compositor/intern/COM_SingleThreadedOperation.h +++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.h @@ -29,7 +29,7 @@ class SingleThreadedOperation : public NodeOperation { protected: inline bool isCached() { - return this->m_cachedInstance != nullptr; + return m_cachedInstance != nullptr; } public: diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc index 30e7fab4027..5e056b3f52f 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc @@ -34,9 +34,9 @@ void AlphaOverKeyOperation::executePixelSampled(float output[4], float inputOverColor[4]; float value[4]; - this->m_inputValueOperation->readSampled(value, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); + m_inputValueOperation->readSampled(value, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); if (inputOverColor[3] <= 0.0f) { copy_v4_v4(output, inputColor1); diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc index 0cc179ea209..61fab7b3bc8 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc @@ -22,7 +22,7 @@ namespace blender::compositor { AlphaOverMixedOperation::AlphaOverMixedOperation() { - this->m_x = 0.0f; + m_x = 0.0f; this->flags.can_be_constant = true; } @@ -35,9 +35,9 @@ void AlphaOverMixedOperation::executePixelSampled(float output[4], float inputOverColor[4]; float value[4]; - this->m_inputValueOperation->readSampled(value, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); + m_inputValueOperation->readSampled(value, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); if (inputOverColor[3] <= 0.0f) { copy_v4_v4(output, inputColor1); @@ -46,7 +46,7 @@ void AlphaOverMixedOperation::executePixelSampled(float output[4], copy_v4_v4(output, inputOverColor); } else { - float addfac = 1.0f - this->m_x + inputOverColor[3] * this->m_x; + float addfac = 1.0f - m_x + inputOverColor[3] * m_x; float premul = value[0] * addfac; float mul = 1.0f - value[0] * inputOverColor[3]; @@ -71,7 +71,7 @@ void AlphaOverMixedOperation::update_memory_buffer_row(PixelCursor &p) copy_v4_v4(p.out, over_color); } else { - const float addfac = 1.0f - this->m_x + over_color[3] * this->m_x; + const float addfac = 1.0f - m_x + over_color[3] * m_x; const float premul = value * addfac; const float mul = 1.0f - value * over_color[3]; diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h index 2b88cd5f421..319176c8c9f 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h @@ -43,7 +43,7 @@ class AlphaOverMixedOperation : public MixBaseOperation { void setX(float x) { - this->m_x = x; + m_x = x; } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc index 911e8d2df92..47383706ba2 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc @@ -34,9 +34,9 @@ void AlphaOverPremultiplyOperation::executePixelSampled(float output[4], float inputOverColor[4]; float value[4]; - this->m_inputValueOperation->readSampled(value, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); + m_inputValueOperation->readSampled(value, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); /* Zero alpha values should still permit an add of RGB data */ if (inputOverColor[3] < 0.0f) { diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.cc b/source/blender/compositor/operations/COM_AntiAliasOperation.cc index dd73f9a7a77..50e90111604 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.cc +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.cc @@ -112,13 +112,13 @@ AntiAliasOperation::AntiAliasOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_valueReader = nullptr; + m_valueReader = nullptr; this->flags.complex = true; } void AntiAliasOperation::initExecution() { - this->m_valueReader = this->getInputSocketReader(0); + m_valueReader = this->getInputSocketReader(0); } void AntiAliasOperation::executePixel(float output[4], int x, int y, void *data) @@ -175,7 +175,7 @@ void AntiAliasOperation::executePixel(float output[4], int x, int y, void *data) void AntiAliasOperation::deinitExecution() { - this->m_valueReader = nullptr; + m_valueReader = nullptr; } bool AntiAliasOperation::determineDependingAreaOfInterest(rcti *input, diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc index de70feed907..4baaa416054 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc @@ -27,14 +27,14 @@ BilateralBlurOperation::BilateralBlurOperation() this->addOutputSocket(DataType::Color); this->flags.complex = true; - this->m_inputColorProgram = nullptr; - this->m_inputDeterminatorProgram = nullptr; + m_inputColorProgram = nullptr; + m_inputDeterminatorProgram = nullptr; } void BilateralBlurOperation::initExecution() { - this->m_inputColorProgram = getInputSocketReader(0); - this->m_inputDeterminatorProgram = getInputSocketReader(1); + m_inputColorProgram = getInputSocketReader(0); + m_inputDeterminatorProgram = getInputSocketReader(1); QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -47,14 +47,14 @@ void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *d float tempColor[4]; float blurColor[4]; float blurDivider; - float space = this->m_space; - float sigmacolor = this->m_data->sigma_color; + float space = m_space; + float sigmacolor = m_data->sigma_color; int minx = floor(x - space); int maxx = ceil(x + space); int miny = floor(y - space); int maxy = ceil(y + space); float deltaColor; - this->m_inputDeterminatorProgram->read(determinatorReferenceColor, x, y, data); + m_inputDeterminatorProgram->read(determinatorReferenceColor, x, y, data); zero_v4(blurColor); blurDivider = 0.0f; @@ -65,14 +65,14 @@ void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *d for (int yi = miny; yi < maxy; yi += QualityStepHelper::getStep()) { for (int xi = minx; xi < maxx; xi += QualityStepHelper::getStep()) { /* Read determinator. */ - this->m_inputDeterminatorProgram->read(determinator, xi, yi, data); + m_inputDeterminatorProgram->read(determinator, xi, yi, data); deltaColor = (fabsf(determinatorReferenceColor[0] - determinator[0]) + fabsf(determinatorReferenceColor[1] - determinator[1]) + /* Do not take the alpha channel into account. */ fabsf(determinatorReferenceColor[2] - determinator[2])); if (deltaColor < sigmacolor) { /* Add this to the blur. */ - this->m_inputColorProgram->read(tempColor, xi, yi, data); + m_inputColorProgram->read(tempColor, xi, yi, data); add_v4_v4(blurColor, tempColor); blurDivider += 1.0f; } @@ -92,8 +92,8 @@ void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *d void BilateralBlurOperation::deinitExecution() { - this->m_inputColorProgram = nullptr; - this->m_inputDeterminatorProgram = nullptr; + m_inputColorProgram = nullptr; + m_inputDeterminatorProgram = nullptr; } bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input, @@ -101,7 +101,7 @@ bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input, rcti *output) { rcti newInput; - int add = ceil(this->m_space) + 1; + int add = ceil(m_space) + 1; newInput.xmax = input->xmax + (add); newInput.xmin = input->xmin - (add); @@ -115,7 +115,7 @@ void BilateralBlurOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &output_area, rcti &r_input_area) { - const int add = ceil(this->m_space) + 1; + const int add = ceil(m_space) + 1; r_input_area.xmax = output_area.xmax + (add); r_input_area.xmin = output_area.xmin - (add); @@ -174,10 +174,10 @@ void BilateralBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, { PixelCursor p = {}; p.step = QualityStepHelper::getStep(); - p.sigma_color = this->m_data->sigma_color; + p.sigma_color = m_data->sigma_color; p.input_color = inputs[0]; p.input_determinator = inputs[1]; - const float space = this->m_space; + const float space = m_space; for (int y = area.ymin; y < area.ymax; y++) { p.out = output->get_elem(area.xmin, y); /* This will be used as the reference color for the determinator. */ diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h index 517c5292827..a1884ddf1ab 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h @@ -54,8 +54,8 @@ class BilateralBlurOperation : public MultiThreadedOperation, public QualityStep void setData(NodeBilateralBlurData *data) { - this->m_data = data; - this->m_space = data->sigma_space + data->iter; + m_data = data; + m_space = data->sigma_space + data->iter; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc index f21cb9f4285..f264603c8ba 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc @@ -30,11 +30,11 @@ BlurBaseOperation::BlurBaseOperation(DataType data_type) this->addInputSocket(DataType::Value); this->addOutputSocket(data_type); this->flags.complex = true; - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; memset(&m_data, 0, sizeof(NodeBlurData)); - this->m_size = 1.0f; - this->m_sizeavailable = false; - this->m_extend_bounds = false; + m_size = 1.0f; + m_sizeavailable = false; + m_extend_bounds = false; use_variable_size_ = false; } @@ -44,32 +44,32 @@ void BlurBaseOperation::init_data() updateSize(); } - this->m_data.image_in_width = this->getWidth(); - this->m_data.image_in_height = this->getHeight(); - if (this->m_data.relative) { + m_data.image_in_width = this->getWidth(); + m_data.image_in_height = this->getHeight(); + if (m_data.relative) { int sizex, sizey; - switch (this->m_data.aspect) { + switch (m_data.aspect) { case CMP_NODE_BLUR_ASPECT_Y: - sizex = sizey = this->m_data.image_in_width; + sizex = sizey = m_data.image_in_width; break; case CMP_NODE_BLUR_ASPECT_X: - sizex = sizey = this->m_data.image_in_height; + sizex = sizey = m_data.image_in_height; break; default: - BLI_assert(this->m_data.aspect == CMP_NODE_BLUR_ASPECT_NONE); - sizex = this->m_data.image_in_width; - sizey = this->m_data.image_in_height; + BLI_assert(m_data.aspect == CMP_NODE_BLUR_ASPECT_NONE); + sizex = m_data.image_in_width; + sizey = m_data.image_in_height; break; } - this->m_data.sizex = round_fl_to_int(this->m_data.percentx * 0.01f * sizex); - this->m_data.sizey = round_fl_to_int(this->m_data.percenty * 0.01f * sizey); + m_data.sizex = round_fl_to_int(m_data.percentx * 0.01f * sizex); + m_data.sizey = round_fl_to_int(m_data.percenty * 0.01f * sizey); } } void BlurBaseOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputSize = this->getInputSocketReader(1); + m_inputProgram = this->getInputSocketReader(0); + m_inputSize = this->getInputSocketReader(1); QualityStepHelper::initExecution(COM_QH_MULTIPLY); } @@ -86,7 +86,7 @@ float *BlurBaseOperation::make_gausstab(float rad, int size) sum = 0.0f; float fac = (rad > 0.0f ? 1.0f / rad : 0.0f); for (i = -size; i <= size; i++) { - val = RE_filter_value(this->m_data.filtertype, (float)i * fac); + val = RE_filter_value(m_data.filtertype, (float)i * fac); sum += val; gausstab[i + size] = val; } @@ -165,8 +165,8 @@ float *BlurBaseOperation::make_dist_fac_inverse(float rad, int size, int falloff void BlurBaseOperation::deinitExecution() { - this->m_inputProgram = nullptr; - this->m_inputSize = nullptr; + m_inputProgram = nullptr; + m_inputSize = nullptr; } void BlurBaseOperation::setData(const NodeBlurData *data) @@ -187,7 +187,7 @@ int BlurBaseOperation::get_blur_size(eDimension dim) const void BlurBaseOperation::updateSize() { - if (this->m_sizeavailable || use_variable_size_) { + if (m_sizeavailable || use_variable_size_) { return; } @@ -195,7 +195,7 @@ void BlurBaseOperation::updateSize() case eExecutionModel::Tiled: { float result[4]; this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest); - this->m_size = result[0]; + m_size = result[0]; break; } case eExecutionModel::FullFrame: { @@ -206,7 +206,7 @@ void BlurBaseOperation::updateSize() break; } } - this->m_sizeavailable = true; + m_sizeavailable = true; } void BlurBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h index 9ab9bf5a173..d8fcd0f8364 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h @@ -74,13 +74,13 @@ class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelpe void setSize(float size) { - this->m_size = size; - this->m_sizeavailable = true; + m_size = size; + m_sizeavailable = true; } void setExtendBounds(bool extend_bounds) { - this->m_extend_bounds = extend_bounds; + m_extend_bounds = extend_bounds; } int get_blur_size(eDimension dim) const; diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc index c175eff093b..a9d1275805a 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc @@ -39,13 +39,13 @@ BokehBlurOperation::BokehBlurOperation() flags.complex = true; flags.open_cl = true; - this->m_size = 1.0f; - this->m_sizeavailable = false; - this->m_inputProgram = nullptr; - this->m_inputBokehProgram = nullptr; - this->m_inputBoundingBoxReader = nullptr; + m_size = 1.0f; + m_sizeavailable = false; + m_inputProgram = nullptr; + m_inputBokehProgram = nullptr; + m_inputBoundingBoxReader = nullptr; - this->m_extend_bounds = false; + m_extend_bounds = false; } void BokehBlurOperation::init_data() @@ -68,7 +68,7 @@ void BokehBlurOperation::init_data() void *BokehBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!this->m_sizeavailable) { + if (!m_sizeavailable) { updateSize(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -80,9 +80,9 @@ void BokehBlurOperation::initExecution() { initMutex(); - this->m_inputProgram = getInputSocketReader(0); - this->m_inputBokehProgram = getInputSocketReader(1); - this->m_inputBoundingBoxReader = getInputSocketReader(2); + m_inputProgram = getInputSocketReader(0); + m_inputBokehProgram = getInputSocketReader(1); + m_inputBoundingBoxReader = getInputSocketReader(2); QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -93,7 +93,7 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) float tempBoundingBox[4]; float bokeh[4]; - this->m_inputBoundingBoxReader->readSampled(tempBoundingBox, x, y, PixelSampler::Nearest); + m_inputBoundingBoxReader->readSampled(tempBoundingBox, x, y, PixelSampler::Nearest); if (tempBoundingBox[0] > 0.0f) { float multiplier_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; @@ -103,11 +103,11 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; const float max_dim = MAX2(this->getWidth(), this->getHeight()); - int pixelSize = this->m_size * max_dim / 100.0f; + int pixelSize = m_size * max_dim / 100.0f; zero_v4(color_accum); if (pixelSize < 2) { - this->m_inputProgram->readSampled(color_accum, x, y, PixelSampler::Nearest); + m_inputProgram->readSampled(color_accum, x, y, PixelSampler::Nearest); multiplier_accum[0] = 1.0f; multiplier_accum[1] = 1.0f; multiplier_accum[2] = 1.0f; @@ -125,14 +125,14 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) int step = getStep(); int offsetadd = getOffsetAdd() * COM_DATA_TYPE_COLOR_CHANNELS; - float m = this->m_bokehDimension / pixelSize; + float m = m_bokehDimension / pixelSize; for (int ny = miny; ny < maxy; ny += step) { int bufferindex = ((minx - bufferstartx) * COM_DATA_TYPE_COLOR_CHANNELS) + ((ny - bufferstarty) * COM_DATA_TYPE_COLOR_CHANNELS * bufferwidth); for (int nx = minx; nx < maxx; nx += step) { - float u = this->m_bokehMidX - (nx - x) * m; - float v = this->m_bokehMidY - (ny - y) * m; - this->m_inputBokehProgram->readSampled(bokeh, u, v, PixelSampler::Nearest); + float u = m_bokehMidX - (nx - x) * m; + float v = m_bokehMidY - (ny - y) * m; + m_inputBokehProgram->readSampled(bokeh, u, v, PixelSampler::Nearest); madd_v4_v4v4(color_accum, bokeh, &buffer[bufferindex]); add_v4_v4(multiplier_accum, bokeh); bufferindex += offsetadd; @@ -144,16 +144,16 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) output[3] = color_accum[3] * (1.0f / multiplier_accum[3]); } else { - this->m_inputProgram->readSampled(output, x, y, PixelSampler::Nearest); + m_inputProgram->readSampled(output, x, y, PixelSampler::Nearest); } } void BokehBlurOperation::deinitExecution() { deinitMutex(); - this->m_inputProgram = nullptr; - this->m_inputBokehProgram = nullptr; - this->m_inputBoundingBoxReader = nullptr; + m_inputProgram = nullptr; + m_inputBokehProgram = nullptr; + m_inputBoundingBoxReader = nullptr; } bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, @@ -164,11 +164,11 @@ bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, rcti bokehInput; const float max_dim = MAX2(this->getWidth(), this->getHeight()); - if (this->m_sizeavailable) { - newInput.xmax = input->xmax + (this->m_size * max_dim / 100.0f); - newInput.xmin = input->xmin - (this->m_size * max_dim / 100.0f); - newInput.ymax = input->ymax + (this->m_size * max_dim / 100.0f); - newInput.ymin = input->ymin - (this->m_size * max_dim / 100.0f); + if (m_sizeavailable) { + newInput.xmax = input->xmax + (m_size * max_dim / 100.0f); + newInput.xmin = input->xmin - (m_size * max_dim / 100.0f); + newInput.ymax = input->ymax + (m_size * max_dim / 100.0f); + newInput.ymin = input->ymin - (m_size * max_dim / 100.0f); } else { newInput.xmax = input->xmax + (10.0f * max_dim / 100.0f); @@ -193,7 +193,7 @@ bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { return true; } - if (!this->m_sizeavailable) { + if (!m_sizeavailable) { rcti sizeInput; sizeInput.xmin = 0; sizeInput.ymin = 0; @@ -215,19 +215,19 @@ void BokehBlurOperation::executeOpenCL(OpenCLDevice *device, std::list * /*clKernelsToCleanUp*/) { cl_kernel kernel = device->COM_clCreateKernel("bokehBlurKernel", nullptr); - if (!this->m_sizeavailable) { + if (!m_sizeavailable) { updateSize(); } const float max_dim = MAX2(this->getWidth(), this->getHeight()); - cl_int radius = this->m_size * max_dim / 100.0f; + cl_int radius = m_size * max_dim / 100.0f; cl_int step = this->getStep(); device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputBoundingBoxReader); + kernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, m_inputBoundingBoxReader); device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 1, 4, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram); + kernel, 1, 4, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 2, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputBokehProgram); + kernel, 2, -1, clMemToCleanUp, inputMemoryBuffers, m_inputBokehProgram); device->COM_clAttachOutputMemoryBufferToKernelParameter(kernel, 3, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter(kernel, 5, outputMemoryBuffer); clSetKernelArg(kernel, 6, sizeof(cl_int), &radius); @@ -239,7 +239,7 @@ void BokehBlurOperation::executeOpenCL(OpenCLDevice *device, void BokehBlurOperation::updateSize() { - if (this->m_sizeavailable) { + if (m_sizeavailable) { return; } @@ -247,8 +247,8 @@ void BokehBlurOperation::updateSize() case eExecutionModel::Tiled: { float result[4]; this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest); - this->m_size = result[0]; - CLAMP(this->m_size, 0.0f, 10.0f); + m_size = result[0]; + CLAMP(m_size, 0.0f, 10.0f); break; } case eExecutionModel::FullFrame: { @@ -260,7 +260,7 @@ void BokehBlurOperation::updateSize() break; } } - this->m_sizeavailable = true; + m_sizeavailable = true; } void BokehBlurOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -274,15 +274,15 @@ void BokehBlurOperation::determine_canvas(const rcti &preferred_area, rcti &r_ar case eExecutionModel::Tiled: { NodeOperation::determine_canvas(preferred_area, r_area); const float max_dim = MAX2(BLI_rcti_size_x(&r_area), BLI_rcti_size_y(&r_area)); - r_area.xmax += 2 * this->m_size * max_dim / 100.0f; - r_area.ymax += 2 * this->m_size * max_dim / 100.0f; + r_area.xmax += 2 * m_size * max_dim / 100.0f; + r_area.ymax += 2 * m_size * max_dim / 100.0f; break; } case eExecutionModel::FullFrame: { set_determined_canvas_modifier([=](rcti &canvas) { const float max_dim = MAX2(BLI_rcti_size_x(&canvas), BLI_rcti_size_y(&canvas)); /* Rounding to even prevents image jiggling in backdrop while switching size values. */ - float add_size = round_to_even(2 * this->m_size * max_dim / 100.0f); + float add_size = round_to_even(2 * m_size * max_dim / 100.0f); canvas.xmax += add_size; canvas.ymax += add_size; }); diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.h b/source/blender/compositor/operations/COM_BokehBlurOperation.h index 84f5a8293ba..7b0f0710a00 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.h @@ -64,8 +64,8 @@ class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelp void setSize(float size) { - this->m_size = size; - this->m_sizeavailable = true; + m_size = size; + m_sizeavailable = true; } void executeOpenCL(OpenCLDevice *device, @@ -77,7 +77,7 @@ class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelp void setExtendBounds(bool extend_bounds) { - this->m_extend_bounds = extend_bounds; + m_extend_bounds = extend_bounds; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc index c6e0683b4de..fb2799e378b 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.cc +++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc @@ -23,33 +23,33 @@ namespace blender::compositor { BokehImageOperation::BokehImageOperation() { this->addOutputSocket(DataType::Color); - this->m_deleteData = false; + m_deleteData = false; } void BokehImageOperation::initExecution() { - this->m_center[0] = getWidth() / 2; - this->m_center[1] = getHeight() / 2; - this->m_inverseRounding = 1.0f - this->m_data->rounding; - this->m_circularDistance = getWidth() / 2; - this->m_flapRad = (float)(M_PI * 2) / this->m_data->flaps; - this->m_flapRadAdd = this->m_data->angle; - while (this->m_flapRadAdd < 0.0f) { - this->m_flapRadAdd += (float)(M_PI * 2.0); + m_center[0] = getWidth() / 2; + m_center[1] = getHeight() / 2; + m_inverseRounding = 1.0f - m_data->rounding; + m_circularDistance = getWidth() / 2; + m_flapRad = (float)(M_PI * 2) / m_data->flaps; + m_flapRadAdd = m_data->angle; + while (m_flapRadAdd < 0.0f) { + m_flapRadAdd += (float)(M_PI * 2.0); } - while (this->m_flapRadAdd > (float)M_PI) { - this->m_flapRadAdd -= (float)(M_PI * 2.0); + while (m_flapRadAdd > (float)M_PI) { + m_flapRadAdd -= (float)(M_PI * 2.0); } } void BokehImageOperation::detemineStartPointOfFlap(float r[2], int flapNumber, float distance) { - r[0] = sinf(this->m_flapRad * flapNumber + this->m_flapRadAdd) * distance + this->m_center[0]; - r[1] = cosf(this->m_flapRad * flapNumber + this->m_flapRadAdd) * distance + this->m_center[1]; + r[0] = sinf(m_flapRad * flapNumber + m_flapRadAdd) * distance + m_center[0]; + r[1] = cosf(m_flapRad * flapNumber + m_flapRadAdd) * distance + m_center[1]; } float BokehImageOperation::isInsideBokeh(float distance, float x, float y) { float insideBokeh = 0.0f; - const float deltaX = x - this->m_center[0]; - const float deltaY = y - this->m_center[1]; + const float deltaX = x - m_center[0]; + const float deltaY = y - m_center[1]; float closestPoint[2]; float lineP1[2]; float lineP2[2]; @@ -57,25 +57,25 @@ float BokehImageOperation::isInsideBokeh(float distance, float x, float y) point[0] = x; point[1] = y; - const float distanceToCenter = len_v2v2(point, this->m_center); + const float distanceToCenter = len_v2v2(point, m_center); const float bearing = (atan2f(deltaX, deltaY) + (float)(M_PI * 2.0)); - int flapNumber = (int)((bearing - this->m_flapRadAdd) / this->m_flapRad); + int flapNumber = (int)((bearing - m_flapRadAdd) / m_flapRad); detemineStartPointOfFlap(lineP1, flapNumber, distance); detemineStartPointOfFlap(lineP2, flapNumber + 1, distance); closest_to_line_v2(closestPoint, point, lineP1, lineP2); - const float distanceLineToCenter = len_v2v2(this->m_center, closestPoint); - const float distanceRoundingToCenter = this->m_inverseRounding * distanceLineToCenter + - this->m_data->rounding * distance; + const float distanceLineToCenter = len_v2v2(m_center, closestPoint); + const float distanceRoundingToCenter = m_inverseRounding * distanceLineToCenter + + m_data->rounding * distance; - const float catadioptricDistanceToCenter = distanceRoundingToCenter * this->m_data->catadioptric; + const float catadioptricDistanceToCenter = distanceRoundingToCenter * m_data->catadioptric; if (distanceRoundingToCenter >= distanceToCenter && catadioptricDistanceToCenter <= distanceToCenter) { if (distanceRoundingToCenter - distanceToCenter < 1.0f) { insideBokeh = (distanceRoundingToCenter - distanceToCenter); } - else if (this->m_data->catadioptric != 0.0f && + else if (m_data->catadioptric != 0.0f && distanceToCenter - catadioptricDistanceToCenter < 1.0f) { insideBokeh = (distanceToCenter - catadioptricDistanceToCenter); } @@ -90,9 +90,9 @@ void BokehImageOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - float shift = this->m_data->lensshift; + float shift = m_data->lensshift; float shift2 = shift / 2.0f; - float distance = this->m_circularDistance; + float distance = m_circularDistance; float insideBokehMax = isInsideBokeh(distance, x, y); float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), x, y); float insideBokehMin = isInsideBokeh(distance - fabsf(shift * distance), x, y); @@ -113,9 +113,9 @@ void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - const float shift = this->m_data->lensshift; + const float shift = m_data->lensshift; const float shift2 = shift / 2.0f; - const float distance = this->m_circularDistance; + const float distance = m_circularDistance; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { const float insideBokehMax = isInsideBokeh(distance, it.x, it.y); const float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), it.x, it.y); @@ -136,10 +136,10 @@ void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output, void BokehImageOperation::deinitExecution() { - if (this->m_deleteData) { - if (this->m_data) { - delete this->m_data; - this->m_data = nullptr; + if (m_deleteData) { + if (m_data) { + delete m_data; + m_data = nullptr; } } } diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h index f7fec5d71a5..888446b4791 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.h +++ b/source/blender/compositor/operations/COM_BokehImageOperation.h @@ -136,7 +136,7 @@ class BokehImageOperation : public MultiThreadedOperation { */ void setData(NodeBokehImage *data) { - this->m_data = data; + m_data = data; } /** @@ -148,7 +148,7 @@ class BokehImageOperation : public MultiThreadedOperation { */ void deleteDataOnFinish() { - this->m_deleteData = true; + m_deleteData = true; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.cc b/source/blender/compositor/operations/COM_BoxMaskOperation.cc index f536d9eb32d..e79e86e5ed2 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.cc +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.cc @@ -25,19 +25,19 @@ BoxMaskOperation::BoxMaskOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_inputMask = nullptr; - this->m_inputValue = nullptr; - this->m_cosine = 0.0f; - this->m_sine = 0.0f; + m_inputMask = nullptr; + m_inputValue = nullptr; + m_cosine = 0.0f; + m_sine = 0.0f; } void BoxMaskOperation::initExecution() { - this->m_inputMask = this->getInputSocketReader(0); - this->m_inputValue = this->getInputSocketReader(1); - const double rad = (double)this->m_data->rotation; - this->m_cosine = cos(rad); - this->m_sine = sin(rad); - this->m_aspectRatio = ((float)this->getWidth()) / this->getHeight(); + m_inputMask = this->getInputSocketReader(0); + m_inputValue = this->getInputSocketReader(1); + const double rad = (double)m_data->rotation; + m_cosine = cos(rad); + m_sine = sin(rad); + m_aspectRatio = ((float)this->getWidth()) / this->getHeight(); } void BoxMaskOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -48,20 +48,20 @@ void BoxMaskOperation::executePixelSampled(float output[4], float x, float y, Pi float rx = x / this->getWidth(); float ry = y / this->getHeight(); - const float dy = (ry - this->m_data->y) / this->m_aspectRatio; - const float dx = rx - this->m_data->x; - rx = this->m_data->x + (this->m_cosine * dx + this->m_sine * dy); - ry = this->m_data->y + (-this->m_sine * dx + this->m_cosine * dy); + const float dy = (ry - m_data->y) / m_aspectRatio; + const float dx = rx - m_data->x; + rx = m_data->x + (m_cosine * dx + m_sine * dy); + ry = m_data->y + (-m_sine * dx + m_cosine * dy); - this->m_inputMask->readSampled(inputMask, x, y, sampler); - this->m_inputValue->readSampled(inputValue, x, y, sampler); + m_inputMask->readSampled(inputMask, x, y, sampler); + m_inputValue->readSampled(inputValue, x, y, sampler); - float halfHeight = this->m_data->height / 2.0f; - float halfWidth = this->m_data->width / 2.0f; - bool inside = (rx > this->m_data->x - halfWidth && rx < this->m_data->x + halfWidth && - ry > this->m_data->y - halfHeight && ry < this->m_data->y + halfHeight); + float halfHeight = m_data->height / 2.0f; + float halfWidth = m_data->width / 2.0f; + bool inside = (rx > m_data->x - halfWidth && rx < m_data->x + halfWidth && + ry > m_data->y - halfHeight && ry < m_data->y + halfHeight); - switch (this->m_maskType) { + switch (m_maskType) { case CMP_NODE_MASKTYPE_ADD: if (inside) { output[0] = MAX2(inputMask[0], inputValue[0]); @@ -143,18 +143,18 @@ void BoxMaskOperation::apply_mask(MemoryBuffer *output, { const float op_w = this->getWidth(); const float op_h = this->getHeight(); - const float half_w = this->m_data->width / 2.0f; - const float half_h = this->m_data->height / 2.0f; + const float half_w = m_data->width / 2.0f; + const float half_h = m_data->height / 2.0f; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float op_ry = it.y / op_h; - const float dy = (op_ry - this->m_data->y) / m_aspectRatio; + const float dy = (op_ry - m_data->y) / m_aspectRatio; const float op_rx = it.x / op_w; - const float dx = op_rx - this->m_data->x; - const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy); - const float ry = this->m_data->y + (-m_sine * dx + m_cosine * dy); + const float dx = op_rx - m_data->x; + const float rx = m_data->x + (m_cosine * dx + m_sine * dy); + const float ry = m_data->y + (-m_sine * dx + m_cosine * dy); - const bool inside = (rx > this->m_data->x - half_w && rx < this->m_data->x + half_w && - ry > this->m_data->y - half_h && ry < this->m_data->y + half_h); + const bool inside = (rx > m_data->x - half_w && rx < m_data->x + half_w && + ry > m_data->y - half_h && ry < m_data->y + half_h); const float *mask = it.in(0); const float *value = it.in(1); *it.out = mask_func(inside, mask, value); @@ -163,8 +163,8 @@ void BoxMaskOperation::apply_mask(MemoryBuffer *output, void BoxMaskOperation::deinitExecution() { - this->m_inputMask = nullptr; - this->m_inputValue = nullptr; + m_inputMask = nullptr; + m_inputValue = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.h b/source/blender/compositor/operations/COM_BoxMaskOperation.h index 4c48dde844a..1f1365897cf 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.h +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.h @@ -59,12 +59,12 @@ class BoxMaskOperation : public MultiThreadedOperation { void setData(NodeBoxMask *data) { - this->m_data = data; + m_data = data; } void setMaskType(int maskType) { - this->m_maskType = maskType; + m_maskType = maskType; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc index 7878eca2bbd..2f2313d87d8 100644 --- a/source/blender/compositor/operations/COM_BrightnessOperation.cc +++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc @@ -26,21 +26,21 @@ BrightnessOperation::BrightnessOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; - this->m_use_premultiply = false; + m_inputProgram = nullptr; + m_use_premultiply = false; flags.can_be_constant = true; } void BrightnessOperation::setUsePremultiply(bool use_premultiply) { - this->m_use_premultiply = use_premultiply; + m_use_premultiply = use_premultiply; } void BrightnessOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputBrightnessProgram = this->getInputSocketReader(1); - this->m_inputContrastProgram = this->getInputSocketReader(2); + m_inputProgram = this->getInputSocketReader(0); + m_inputBrightnessProgram = this->getInputSocketReader(1); + m_inputContrastProgram = this->getInputSocketReader(2); } void BrightnessOperation::executePixelSampled(float output[4], @@ -52,9 +52,9 @@ void BrightnessOperation::executePixelSampled(float output[4], float a, b; float inputBrightness[4]; float inputContrast[4]; - this->m_inputProgram->readSampled(inputValue, x, y, sampler); - this->m_inputBrightnessProgram->readSampled(inputBrightness, x, y, sampler); - this->m_inputContrastProgram->readSampled(inputContrast, x, y, sampler); + m_inputProgram->readSampled(inputValue, x, y, sampler); + m_inputBrightnessProgram->readSampled(inputBrightness, x, y, sampler); + m_inputContrastProgram->readSampled(inputContrast, x, y, sampler); float brightness = inputBrightness[0]; float contrast = inputContrast[0]; brightness /= 100.0f; @@ -74,14 +74,14 @@ void BrightnessOperation::executePixelSampled(float output[4], a = max_ff(1.0f - delta * 2.0f, 0.0f); b = a * brightness + delta; } - if (this->m_use_premultiply) { + if (m_use_premultiply) { premul_to_straight_v4(inputValue); } output[0] = a * inputValue[0] + b; output[1] = a * inputValue[1] + b; output[2] = a * inputValue[2] + b; output[3] = inputValue[3]; - if (this->m_use_premultiply) { + if (m_use_premultiply) { straight_to_premul_v4(output); } } @@ -113,7 +113,7 @@ void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output, b = a * brightness + delta; } const float *color; - if (this->m_use_premultiply) { + if (m_use_premultiply) { premul_to_straight_v4_v4(tmp_color, in_color); color = tmp_color; } @@ -124,7 +124,7 @@ void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output, it.out[1] = a * color[1] + b; it.out[2] = a * color[2] + b; it.out[3] = color[3]; - if (this->m_use_premultiply) { + if (m_use_premultiply) { straight_to_premul_v4(it.out); } } @@ -132,9 +132,9 @@ void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output, void BrightnessOperation::deinitExecution() { - this->m_inputProgram = nullptr; - this->m_inputBrightnessProgram = nullptr; - this->m_inputContrastProgram = nullptr; + m_inputProgram = nullptr; + m_inputBrightnessProgram = nullptr; + m_inputContrastProgram = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc index 850b762ede4..d8a34332242 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc @@ -28,26 +28,26 @@ CalculateMeanOperation::CalculateMeanOperation() { this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Value); - this->m_imageReader = nullptr; - this->m_iscalculated = false; - this->m_setting = 1; + m_imageReader = nullptr; + m_iscalculated = false; + m_setting = 1; this->flags.complex = true; } void CalculateMeanOperation::initExecution() { - this->m_imageReader = this->getInputSocketReader(0); - this->m_iscalculated = false; + m_imageReader = this->getInputSocketReader(0); + m_iscalculated = false; NodeOperation::initMutex(); } void CalculateMeanOperation::executePixel(float output[4], int /*x*/, int /*y*/, void * /*data*/) { - output[0] = this->m_result; + output[0] = m_result; } void CalculateMeanOperation::deinitExecution() { - this->m_imageReader = nullptr; + m_imageReader = nullptr; NodeOperation::deinitMutex(); } @@ -56,7 +56,7 @@ bool CalculateMeanOperation::determineDependingAreaOfInterest(rcti * /*input*/, rcti *output) { rcti imageInput; - if (this->m_iscalculated) { + if (m_iscalculated) { return false; } NodeOperation *operation = getInputOperation(0); @@ -73,10 +73,10 @@ bool CalculateMeanOperation::determineDependingAreaOfInterest(rcti * /*input*/, void *CalculateMeanOperation::initializeTileData(rcti *rect) { lockMutex(); - if (!this->m_iscalculated) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_imageReader->initializeTileData(rect); + if (!m_iscalculated) { + MemoryBuffer *tile = (MemoryBuffer *)m_imageReader->initializeTileData(rect); calculateMean(tile); - this->m_iscalculated = true; + m_iscalculated = true; } unlockMutex(); return nullptr; @@ -84,7 +84,7 @@ void *CalculateMeanOperation::initializeTileData(rcti *rect) void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) { - this->m_result = 0.0f; + m_result = 0.0f; float *buffer = tile->getBuffer(); int size = tile->getWidth() * tile->getHeight(); int pixels = 0; @@ -93,7 +93,7 @@ void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) if (buffer[offset + 3] > 0) { pixels++; - switch (this->m_setting) { + switch (m_setting) { case 1: { sum += IMB_colormanagement_get_luminance(&buffer[offset]); break; @@ -125,12 +125,12 @@ void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) } } } - this->m_result = sum / pixels; + m_result = sum / pixels; } void CalculateMeanOperation::setSetting(int setting) { - this->m_setting = setting; + m_setting = setting; switch (setting) { case 1: { setting_func_ = IMB_colormanagement_get_luminance; @@ -171,10 +171,10 @@ void CalculateMeanOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(o const rcti &UNUSED(area), Span inputs) { - if (!this->m_iscalculated) { + if (!m_iscalculated) { MemoryBuffer *input = inputs[0]; m_result = calc_mean(input); - this->m_iscalculated = true; + m_iscalculated = true; } } diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc index 6e5ef690b5d..cda77f0a83b 100644 --- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc @@ -29,26 +29,26 @@ void CalculateStandardDeviationOperation::executePixel(float output[4], int /*y*/, void * /*data*/) { - output[0] = this->m_standardDeviation; + output[0] = m_standardDeviation; } void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) { lockMutex(); - if (!this->m_iscalculated) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_imageReader->initializeTileData(rect); + if (!m_iscalculated) { + MemoryBuffer *tile = (MemoryBuffer *)m_imageReader->initializeTileData(rect); CalculateMeanOperation::calculateMean(tile); - this->m_standardDeviation = 0.0f; + m_standardDeviation = 0.0f; float *buffer = tile->getBuffer(); int size = tile->getWidth() * tile->getHeight(); int pixels = 0; float sum = 0.0f; - float mean = this->m_result; + float mean = m_result; for (int i = 0, offset = 0; i < size; i++, offset += 4) { if (buffer[offset + 3] > 0) { pixels++; - switch (this->m_setting) { + switch (m_setting) { case 1: /* rgb combined */ { float value = IMB_colormanagement_get_luminance(&buffer[offset]); @@ -89,8 +89,8 @@ void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) } } } - this->m_standardDeviation = sqrt(sum / (float)(pixels - 1)); - this->m_iscalculated = true; + m_standardDeviation = sqrt(sum / (float)(pixels - 1)); + m_iscalculated = true; } unlockMutex(); return nullptr; @@ -99,7 +99,7 @@ void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) void CalculateStandardDeviationOperation::update_memory_buffer_started( MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span inputs) { - if (!this->m_iscalculated) { + if (!m_iscalculated) { const MemoryBuffer *input = inputs[0]; const float mean = CalculateMeanOperation::calc_mean(input); @@ -112,10 +112,9 @@ void CalculateStandardDeviationOperation::update_memory_buffer_started( join.sum += chunk.sum; join.num_pixels += chunk.num_pixels; }); - this->m_standardDeviation = total.num_pixels <= 1 ? - 0.0f : - sqrt(total.sum / (float)(total.num_pixels - 1)); - this->m_iscalculated = true; + m_standardDeviation = total.num_pixels <= 1 ? 0.0f : + sqrt(total.sum / (float)(total.num_pixels - 1)); + m_iscalculated = true; } } diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc index 1e3e7806968..794574e95ca 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc @@ -27,24 +27,24 @@ ChangeHSVOperation::ChangeHSVOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; this->flags.can_be_constant = true; } void ChangeHSVOperation::initExecution() { - this->m_inputOperation = getInputSocketReader(0); - this->m_hueOperation = getInputSocketReader(1); - this->m_saturationOperation = getInputSocketReader(2); - this->m_valueOperation = getInputSocketReader(3); + m_inputOperation = getInputSocketReader(0); + m_hueOperation = getInputSocketReader(1); + m_saturationOperation = getInputSocketReader(2); + m_valueOperation = getInputSocketReader(3); } void ChangeHSVOperation::deinitExecution() { - this->m_inputOperation = nullptr; - this->m_hueOperation = nullptr; - this->m_saturationOperation = nullptr; - this->m_valueOperation = nullptr; + m_inputOperation = nullptr; + m_hueOperation = nullptr; + m_saturationOperation = nullptr; + m_valueOperation = nullptr; } void ChangeHSVOperation::executePixelSampled(float output[4], @@ -55,10 +55,10 @@ void ChangeHSVOperation::executePixelSampled(float output[4], float inputColor1[4]; float hue[4], saturation[4], value[4]; - this->m_inputOperation->readSampled(inputColor1, x, y, sampler); - this->m_hueOperation->readSampled(hue, x, y, sampler); - this->m_saturationOperation->readSampled(saturation, x, y, sampler); - this->m_valueOperation->readSampled(value, x, y, sampler); + m_inputOperation->readSampled(inputColor1, x, y, sampler); + m_hueOperation->readSampled(hue, x, y, sampler); + m_saturationOperation->readSampled(saturation, x, y, sampler); + m_valueOperation->readSampled(value, x, y, sampler); output[0] = inputColor1[0] + (hue[0] - 0.5f); if (output[0] > 1.0f) { diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc index 3d79d3d03a0..f3664c7e2de 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc @@ -25,46 +25,46 @@ ChannelMatteOperation::ChannelMatteOperation() addInputSocket(DataType::Color); addOutputSocket(DataType::Value); - this->m_inputImageProgram = nullptr; + m_inputImageProgram = nullptr; flags.can_be_constant = true; } void ChannelMatteOperation::initExecution() { - this->m_inputImageProgram = this->getInputSocketReader(0); + m_inputImageProgram = this->getInputSocketReader(0); - this->m_limit_range = this->m_limit_max - this->m_limit_min; + m_limit_range = m_limit_max - m_limit_min; - switch (this->m_limit_method) { + switch (m_limit_method) { /* SINGLE */ case 0: { /* 123 / RGB / HSV / YUV / YCC */ - const int matte_channel = this->m_matte_channel - 1; - const int limit_channel = this->m_limit_channel - 1; - this->m_ids[0] = matte_channel; - this->m_ids[1] = limit_channel; - this->m_ids[2] = limit_channel; + const int matte_channel = m_matte_channel - 1; + const int limit_channel = m_limit_channel - 1; + m_ids[0] = matte_channel; + m_ids[1] = limit_channel; + m_ids[2] = limit_channel; break; } /* MAX */ case 1: { - switch (this->m_matte_channel) { + switch (m_matte_channel) { case 1: { - this->m_ids[0] = 0; - this->m_ids[1] = 1; - this->m_ids[2] = 2; + m_ids[0] = 0; + m_ids[1] = 1; + m_ids[2] = 2; break; } case 2: { - this->m_ids[0] = 1; - this->m_ids[1] = 0; - this->m_ids[2] = 2; + m_ids[0] = 1; + m_ids[1] = 0; + m_ids[2] = 2; break; } case 3: { - this->m_ids[0] = 2; - this->m_ids[1] = 0; - this->m_ids[2] = 1; + m_ids[0] = 2; + m_ids[1] = 0; + m_ids[2] = 1; break; } default: @@ -79,7 +79,7 @@ void ChannelMatteOperation::initExecution() void ChannelMatteOperation::deinitExecution() { - this->m_inputImageProgram = nullptr; + m_inputImageProgram = nullptr; } void ChannelMatteOperation::executePixelSampled(float output[4], @@ -90,14 +90,14 @@ void ChannelMatteOperation::executePixelSampled(float output[4], float inColor[4]; float alpha; - const float limit_max = this->m_limit_max; - const float limit_min = this->m_limit_min; - const float limit_range = this->m_limit_range; + const float limit_max = m_limit_max; + const float limit_min = m_limit_min; + const float limit_range = m_limit_range; - this->m_inputImageProgram->readSampled(inColor, x, y, sampler); + m_inputImageProgram->readSampled(inColor, x, y, sampler); /* matte operation */ - alpha = inColor[this->m_ids[0]] - MAX2(inColor[this->m_ids[1]], inColor[this->m_ids[2]]); + alpha = inColor[m_ids[0]] - MAX2(inColor[m_ids[1]], inColor[m_ids[2]]); /* flip because 0.0 is transparent, not 1.0 */ alpha = 1.0f - alpha; @@ -129,7 +129,7 @@ void ChannelMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, const float *color = it.in(0); /* Matte operation. */ - float alpha = color[this->m_ids[0]] - MAX2(color[this->m_ids[1]], color[this->m_ids[2]]); + float alpha = color[m_ids[0]] - MAX2(color[m_ids[1]], color[m_ids[2]]); /* Flip because 0.0 is transparent, not 1.0. */ alpha = 1.0f - alpha; diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.h b/source/blender/compositor/operations/COM_ChannelMatteOperation.h index ba50105dd3b..83f187ae443 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.h +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.h @@ -65,11 +65,11 @@ class ChannelMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma, const int custom2) { - this->m_limit_max = nodeChroma->t1; - this->m_limit_min = nodeChroma->t2; - this->m_limit_method = nodeChroma->algorithm; - this->m_limit_channel = nodeChroma->channel; - this->m_matte_channel = custom2; + m_limit_max = nodeChroma->t1; + m_limit_min = nodeChroma->t2; + m_limit_method = nodeChroma->algorithm; + m_limit_channel = nodeChroma->channel; + m_matte_channel = custom2; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc index 207cc641ebd..7b2f3470efe 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc @@ -26,21 +26,21 @@ ChromaMatteOperation::ChromaMatteOperation() addInputSocket(DataType::Color); addOutputSocket(DataType::Value); - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; + m_inputImageProgram = nullptr; + m_inputKeyProgram = nullptr; flags.can_be_constant = true; } void ChromaMatteOperation::initExecution() { - this->m_inputImageProgram = this->getInputSocketReader(0); - this->m_inputKeyProgram = this->getInputSocketReader(1); + m_inputImageProgram = this->getInputSocketReader(0); + m_inputKeyProgram = this->getInputSocketReader(1); } void ChromaMatteOperation::deinitExecution() { - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; + m_inputImageProgram = nullptr; + m_inputKeyProgram = nullptr; } void ChromaMatteOperation::executePixelSampled(float output[4], @@ -51,16 +51,16 @@ void ChromaMatteOperation::executePixelSampled(float output[4], float inKey[4]; float inImage[4]; - const float acceptance = this->m_settings->t1; /* in radians */ - const float cutoff = this->m_settings->t2; /* in radians */ - const float gain = this->m_settings->fstrength; + const float acceptance = m_settings->t1; /* in radians */ + const float cutoff = m_settings->t2; /* in radians */ + const float gain = m_settings->fstrength; float x_angle, z_angle, alpha; float theta, beta; float kfg; - this->m_inputKeyProgram->readSampled(inKey, x, y, sampler); - this->m_inputImageProgram->readSampled(inImage, x, y, sampler); + m_inputKeyProgram->readSampled(inKey, x, y, sampler); + m_inputImageProgram->readSampled(inImage, x, y, sampler); /* Store matte(alpha) value in [0] to go with * #COM_SetAlphaMultiplyOperation and the Value output. */ @@ -114,9 +114,9 @@ void ChromaMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float acceptance = this->m_settings->t1; /* In radians. */ - const float cutoff = this->m_settings->t2; /* In radians. */ - const float gain = this->m_settings->fstrength; + const float acceptance = m_settings->t1; /* In radians. */ + const float cutoff = m_settings->t2; /* In radians. */ + const float gain = m_settings->fstrength; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float *in_image = it.in(0); const float *in_key = it.in(1); diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.h b/source/blender/compositor/operations/COM_ChromaMatteOperation.h index 065349910a7..182d0baba76 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.h +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.h @@ -48,7 +48,7 @@ class ChromaMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma) { - this->m_settings = nodeChroma; + m_settings = nodeChroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc index 08df31b81d8..d4118503341 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc @@ -37,16 +37,16 @@ ColorBalanceASCCDLOperation::ColorBalanceASCCDLOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_inputValueOperation = nullptr; - this->m_inputColorOperation = nullptr; + m_inputValueOperation = nullptr; + m_inputColorOperation = nullptr; this->set_canvas_input_index(1); flags.can_be_constant = true; } void ColorBalanceASCCDLOperation::initExecution() { - this->m_inputValueOperation = this->getInputSocketReader(0); - this->m_inputColorOperation = this->getInputSocketReader(1); + m_inputValueOperation = this->getInputSocketReader(0); + m_inputColorOperation = this->getInputSocketReader(1); } void ColorBalanceASCCDLOperation::executePixelSampled(float output[4], @@ -57,22 +57,19 @@ void ColorBalanceASCCDLOperation::executePixelSampled(float output[4], float inputColor[4]; float value[4]; - this->m_inputValueOperation->readSampled(value, x, y, sampler); - this->m_inputColorOperation->readSampled(inputColor, x, y, sampler); + m_inputValueOperation->readSampled(value, x, y, sampler); + m_inputColorOperation->readSampled(inputColor, x, y, sampler); float fac = value[0]; fac = MIN2(1.0f, fac); const float mfac = 1.0f - fac; output[0] = mfac * inputColor[0] + - fac * colorbalance_cdl( - inputColor[0], this->m_offset[0], this->m_power[0], this->m_slope[0]); + fac * colorbalance_cdl(inputColor[0], m_offset[0], m_power[0], m_slope[0]); output[1] = mfac * inputColor[1] + - fac * colorbalance_cdl( - inputColor[1], this->m_offset[1], this->m_power[1], this->m_slope[1]); + fac * colorbalance_cdl(inputColor[1], m_offset[1], m_power[1], m_slope[1]); output[2] = mfac * inputColor[2] + - fac * colorbalance_cdl( - inputColor[2], this->m_offset[2], this->m_power[2], this->m_slope[2]); + fac * colorbalance_cdl(inputColor[2], m_offset[2], m_power[2], m_slope[2]); output[3] = inputColor[3]; } @@ -95,8 +92,8 @@ void ColorBalanceASCCDLOperation::update_memory_buffer_row(PixelCursor &p) void ColorBalanceASCCDLOperation::deinitExecution() { - this->m_inputValueOperation = nullptr; - this->m_inputColorOperation = nullptr; + m_inputValueOperation = nullptr; + m_inputColorOperation = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h index d161ea66af2..b9b3d45a68b 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h @@ -61,15 +61,15 @@ class ColorBalanceASCCDLOperation : public MultiThreadedRowOperation { void setOffset(float offset[3]) { - copy_v3_v3(this->m_offset, offset); + copy_v3_v3(m_offset, offset); } void setPower(float power[3]) { - copy_v3_v3(this->m_power, power); + copy_v3_v3(m_power, power); } void setSlope(float slope[3]) { - copy_v3_v3(this->m_slope, slope); + copy_v3_v3(m_slope, slope); } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc index 9a482875cdd..b6059d70b70 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc @@ -42,16 +42,16 @@ ColorBalanceLGGOperation::ColorBalanceLGGOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_inputValueOperation = nullptr; - this->m_inputColorOperation = nullptr; + m_inputValueOperation = nullptr; + m_inputColorOperation = nullptr; this->set_canvas_input_index(1); flags.can_be_constant = true; } void ColorBalanceLGGOperation::initExecution() { - this->m_inputValueOperation = this->getInputSocketReader(0); - this->m_inputColorOperation = this->getInputSocketReader(1); + m_inputValueOperation = this->getInputSocketReader(0); + m_inputColorOperation = this->getInputSocketReader(1); } void ColorBalanceLGGOperation::executePixelSampled(float output[4], @@ -62,22 +62,19 @@ void ColorBalanceLGGOperation::executePixelSampled(float output[4], float inputColor[4]; float value[4]; - this->m_inputValueOperation->readSampled(value, x, y, sampler); - this->m_inputColorOperation->readSampled(inputColor, x, y, sampler); + m_inputValueOperation->readSampled(value, x, y, sampler); + m_inputColorOperation->readSampled(inputColor, x, y, sampler); float fac = value[0]; fac = MIN2(1.0f, fac); const float mfac = 1.0f - fac; output[0] = mfac * inputColor[0] + - fac * colorbalance_lgg( - inputColor[0], this->m_lift[0], this->m_gamma_inv[0], this->m_gain[0]); + fac * colorbalance_lgg(inputColor[0], m_lift[0], m_gamma_inv[0], m_gain[0]); output[1] = mfac * inputColor[1] + - fac * colorbalance_lgg( - inputColor[1], this->m_lift[1], this->m_gamma_inv[1], this->m_gain[1]); + fac * colorbalance_lgg(inputColor[1], m_lift[1], m_gamma_inv[1], m_gain[1]); output[2] = mfac * inputColor[2] + - fac * colorbalance_lgg( - inputColor[2], this->m_lift[2], this->m_gamma_inv[2], this->m_gain[2]); + fac * colorbalance_lgg(inputColor[2], m_lift[2], m_gamma_inv[2], m_gain[2]); output[3] = inputColor[3]; } @@ -100,8 +97,8 @@ void ColorBalanceLGGOperation::update_memory_buffer_row(PixelCursor &p) void ColorBalanceLGGOperation::deinitExecution() { - this->m_inputValueOperation = nullptr; - this->m_inputColorOperation = nullptr; + m_inputValueOperation = nullptr; + m_inputColorOperation = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h index 4bc929ed76c..0a447f50514 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h @@ -61,15 +61,15 @@ class ColorBalanceLGGOperation : public MultiThreadedRowOperation { void setGain(const float gain[3]) { - copy_v3_v3(this->m_gain, gain); + copy_v3_v3(m_gain, gain); } void setLift(const float lift[3]) { - copy_v3_v3(this->m_lift, lift); + copy_v3_v3(m_lift, lift); } void setGammaInv(const float gamma_inv[3]) { - copy_v3_v3(this->m_gamma_inv, gamma_inv); + copy_v3_v3(m_gamma_inv, gamma_inv); } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc index 2b2baa15037..9cbe9a16ade 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc @@ -27,17 +27,17 @@ ColorCorrectionOperation::ColorCorrectionOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_inputImage = nullptr; - this->m_inputMask = nullptr; - this->m_redChannelEnabled = true; - this->m_greenChannelEnabled = true; - this->m_blueChannelEnabled = true; + m_inputImage = nullptr; + m_inputMask = nullptr; + m_redChannelEnabled = true; + m_greenChannelEnabled = true; + m_blueChannelEnabled = true; flags.can_be_constant = true; } void ColorCorrectionOperation::initExecution() { - this->m_inputImage = this->getInputSocketReader(0); - this->m_inputMask = this->getInputSocketReader(1); + m_inputImage = this->getInputSocketReader(0); + m_inputMask = this->getInputSocketReader(1); } /* Calculate x^y if the function is defined. Otherwise return the given fallback value. */ @@ -56,15 +56,15 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], { float inputImageColor[4]; float inputMask[4]; - this->m_inputImage->readSampled(inputImageColor, x, y, sampler); - this->m_inputMask->readSampled(inputMask, x, y, sampler); + m_inputImage->readSampled(inputImageColor, x, y, sampler); + m_inputMask->readSampled(inputMask, x, y, sampler); float level = (inputImageColor[0] + inputImageColor[1] + inputImageColor[2]) / 3.0f; - float contrast = this->m_data->master.contrast; - float saturation = this->m_data->master.saturation; - float gamma = this->m_data->master.gamma; - float gain = this->m_data->master.gain; - float lift = this->m_data->master.lift; + float contrast = m_data->master.contrast; + float saturation = m_data->master.saturation; + float gamma = m_data->master.gamma; + float gain = m_data->master.gain; + float lift = m_data->master.lift; float r, g, b; float value = inputMask[0]; @@ -76,18 +76,18 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], float levelHighlights = 0.0; #define MARGIN 0.10f #define MARGIN_DIV (0.5f / MARGIN) - if (level < this->m_data->startmidtones - MARGIN) { + if (level < m_data->startmidtones - MARGIN) { levelShadows = 1.0f; } - else if (level < this->m_data->startmidtones + MARGIN) { - levelMidtones = ((level - this->m_data->startmidtones) * MARGIN_DIV) + 0.5f; + else if (level < m_data->startmidtones + MARGIN) { + levelMidtones = ((level - m_data->startmidtones) * MARGIN_DIV) + 0.5f; levelShadows = 1.0f - levelMidtones; } - else if (level < this->m_data->endmidtones - MARGIN) { + else if (level < m_data->endmidtones - MARGIN) { levelMidtones = 1.0f; } - else if (level < this->m_data->endmidtones + MARGIN) { - levelHighlights = ((level - this->m_data->endmidtones) * MARGIN_DIV) + 0.5f; + else if (level < m_data->endmidtones + MARGIN) { + levelHighlights = ((level - m_data->endmidtones) * MARGIN_DIV) + 0.5f; levelMidtones = 1.0f - levelHighlights; } else { @@ -95,21 +95,18 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], } #undef MARGIN #undef MARGIN_DIV - contrast *= (levelShadows * this->m_data->shadows.contrast) + - (levelMidtones * this->m_data->midtones.contrast) + - (levelHighlights * this->m_data->highlights.contrast); - saturation *= (levelShadows * this->m_data->shadows.saturation) + - (levelMidtones * this->m_data->midtones.saturation) + - (levelHighlights * this->m_data->highlights.saturation); - gamma *= (levelShadows * this->m_data->shadows.gamma) + - (levelMidtones * this->m_data->midtones.gamma) + - (levelHighlights * this->m_data->highlights.gamma); - gain *= (levelShadows * this->m_data->shadows.gain) + - (levelMidtones * this->m_data->midtones.gain) + - (levelHighlights * this->m_data->highlights.gain); - lift += (levelShadows * this->m_data->shadows.lift) + - (levelMidtones * this->m_data->midtones.lift) + - (levelHighlights * this->m_data->highlights.lift); + contrast *= (levelShadows * m_data->shadows.contrast) + + (levelMidtones * m_data->midtones.contrast) + + (levelHighlights * m_data->highlights.contrast); + saturation *= (levelShadows * m_data->shadows.saturation) + + (levelMidtones * m_data->midtones.saturation) + + (levelHighlights * m_data->highlights.saturation); + gamma *= (levelShadows * m_data->shadows.gamma) + (levelMidtones * m_data->midtones.gamma) + + (levelHighlights * m_data->highlights.gamma); + gain *= (levelShadows * m_data->shadows.gain) + (levelMidtones * m_data->midtones.gain) + + (levelHighlights * m_data->highlights.gain); + lift += (levelShadows * m_data->shadows.lift) + (levelMidtones * m_data->midtones.lift) + + (levelHighlights * m_data->highlights.lift); float invgamma = 1.0f / gamma; float luma = IMB_colormanagement_get_luminance(inputImageColor); @@ -136,19 +133,19 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], g = mvalue * inputImageColor[1] + value * g; b = mvalue * inputImageColor[2] + value * b; - if (this->m_redChannelEnabled) { + if (m_redChannelEnabled) { output[0] = r; } else { output[0] = inputImageColor[0]; } - if (this->m_greenChannelEnabled) { + if (m_greenChannelEnabled) { output[1] = g; } else { output[1] = inputImageColor[1]; } - if (this->m_blueChannelEnabled) { + if (m_blueChannelEnabled) { output[2] = b; } else { @@ -169,43 +166,40 @@ void ColorCorrectionOperation::update_memory_buffer_row(PixelCursor &p) float level_highlights = 0.0f; constexpr float MARGIN = 0.10f; constexpr float MARGIN_DIV = 0.5f / MARGIN; - if (level < this->m_data->startmidtones - MARGIN) { + if (level < m_data->startmidtones - MARGIN) { level_shadows = 1.0f; } - else if (level < this->m_data->startmidtones + MARGIN) { - level_midtones = ((level - this->m_data->startmidtones) * MARGIN_DIV) + 0.5f; + else if (level < m_data->startmidtones + MARGIN) { + level_midtones = ((level - m_data->startmidtones) * MARGIN_DIV) + 0.5f; level_shadows = 1.0f - level_midtones; } - else if (level < this->m_data->endmidtones - MARGIN) { + else if (level < m_data->endmidtones - MARGIN) { level_midtones = 1.0f; } - else if (level < this->m_data->endmidtones + MARGIN) { - level_highlights = ((level - this->m_data->endmidtones) * MARGIN_DIV) + 0.5f; + else if (level < m_data->endmidtones + MARGIN) { + level_highlights = ((level - m_data->endmidtones) * MARGIN_DIV) + 0.5f; level_midtones = 1.0f - level_highlights; } else { level_highlights = 1.0f; } - float contrast = this->m_data->master.contrast; - float saturation = this->m_data->master.saturation; - float gamma = this->m_data->master.gamma; - float gain = this->m_data->master.gain; - float lift = this->m_data->master.lift; - contrast *= level_shadows * this->m_data->shadows.contrast + - level_midtones * this->m_data->midtones.contrast + - level_highlights * this->m_data->highlights.contrast; - saturation *= level_shadows * this->m_data->shadows.saturation + - level_midtones * this->m_data->midtones.saturation + - level_highlights * this->m_data->highlights.saturation; - gamma *= level_shadows * this->m_data->shadows.gamma + - level_midtones * this->m_data->midtones.gamma + - level_highlights * this->m_data->highlights.gamma; - gain *= level_shadows * this->m_data->shadows.gain + - level_midtones * this->m_data->midtones.gain + - level_highlights * this->m_data->highlights.gain; - lift += level_shadows * this->m_data->shadows.lift + - level_midtones * this->m_data->midtones.lift + - level_highlights * this->m_data->highlights.lift; + float contrast = m_data->master.contrast; + float saturation = m_data->master.saturation; + float gamma = m_data->master.gamma; + float gain = m_data->master.gain; + float lift = m_data->master.lift; + contrast *= level_shadows * m_data->shadows.contrast + + level_midtones * m_data->midtones.contrast + + level_highlights * m_data->highlights.contrast; + saturation *= level_shadows * m_data->shadows.saturation + + level_midtones * m_data->midtones.saturation + + level_highlights * m_data->highlights.saturation; + gamma *= level_shadows * m_data->shadows.gamma + level_midtones * m_data->midtones.gamma + + level_highlights * m_data->highlights.gamma; + gain *= level_shadows * m_data->shadows.gain + level_midtones * m_data->midtones.gain + + level_highlights * m_data->highlights.gain; + lift += level_shadows * m_data->shadows.lift + level_midtones * m_data->midtones.lift + + level_highlights * m_data->highlights.lift; const float inv_gamma = 1.0f / gamma; const float luma = IMB_colormanagement_get_luminance(in_color); @@ -239,8 +233,8 @@ void ColorCorrectionOperation::update_memory_buffer_row(PixelCursor &p) void ColorCorrectionOperation::deinitExecution() { - this->m_inputImage = nullptr; - this->m_inputMask = nullptr; + m_inputImage = nullptr; + m_inputMask = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h index 32b5e02e77a..64af049e48c 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h @@ -55,19 +55,19 @@ class ColorCorrectionOperation : public MultiThreadedRowOperation { void setData(NodeColorCorrection *data) { - this->m_data = data; + m_data = data; } void setRedChannelEnabled(bool enabled) { - this->m_redChannelEnabled = enabled; + m_redChannelEnabled = enabled; } void setGreenChannelEnabled(bool enabled) { - this->m_greenChannelEnabled = enabled; + m_greenChannelEnabled = enabled; } void setBlueChannelEnabled(bool enabled) { - this->m_blueChannelEnabled = enabled; + m_blueChannelEnabled = enabled; } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc index 31541786b85..c103a3fc651 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc @@ -30,22 +30,22 @@ ColorCurveOperation::ColorCurveOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_inputFacProgram = nullptr; - this->m_inputImageProgram = nullptr; - this->m_inputBlackProgram = nullptr; - this->m_inputWhiteProgram = nullptr; + m_inputFacProgram = nullptr; + m_inputImageProgram = nullptr; + m_inputBlackProgram = nullptr; + m_inputWhiteProgram = nullptr; this->set_canvas_input_index(1); } void ColorCurveOperation::initExecution() { CurveBaseOperation::initExecution(); - this->m_inputFacProgram = this->getInputSocketReader(0); - this->m_inputImageProgram = this->getInputSocketReader(1); - this->m_inputBlackProgram = this->getInputSocketReader(2); - this->m_inputWhiteProgram = this->getInputSocketReader(3); + m_inputFacProgram = this->getInputSocketReader(0); + m_inputImageProgram = this->getInputSocketReader(1); + m_inputBlackProgram = this->getInputSocketReader(2); + m_inputWhiteProgram = this->getInputSocketReader(3); - BKE_curvemapping_premultiply(this->m_curveMapping, 0); + BKE_curvemapping_premultiply(m_curveMapping, 0); } void ColorCurveOperation::executePixelSampled(float output[4], @@ -53,7 +53,7 @@ void ColorCurveOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - CurveMapping *cumap = this->m_curveMapping; + CurveMapping *cumap = m_curveMapping; float fac[4]; float image[4]; @@ -63,15 +63,15 @@ void ColorCurveOperation::executePixelSampled(float output[4], float white[4]; float bwmul[3]; - this->m_inputBlackProgram->readSampled(black, x, y, sampler); - this->m_inputWhiteProgram->readSampled(white, x, y, sampler); + m_inputBlackProgram->readSampled(black, x, y, sampler); + m_inputWhiteProgram->readSampled(white, x, y, sampler); /* get our own local bwmul value, * since we can't be threadsafe and use cumap->bwmul & friends */ BKE_curvemapping_set_black_white_ex(black, white, bwmul); - this->m_inputFacProgram->readSampled(fac, x, y, sampler); - this->m_inputImageProgram->readSampled(image, x, y, sampler); + m_inputFacProgram->readSampled(fac, x, y, sampler); + m_inputImageProgram->readSampled(image, x, y, sampler); if (*fac >= 1.0f) { BKE_curvemapping_evaluate_premulRGBF_ex(cumap, output, image, black, bwmul); @@ -90,17 +90,17 @@ void ColorCurveOperation::executePixelSampled(float output[4], void ColorCurveOperation::deinitExecution() { CurveBaseOperation::deinitExecution(); - this->m_inputFacProgram = nullptr; - this->m_inputImageProgram = nullptr; - this->m_inputBlackProgram = nullptr; - this->m_inputWhiteProgram = nullptr; + m_inputFacProgram = nullptr; + m_inputImageProgram = nullptr; + m_inputBlackProgram = nullptr; + m_inputWhiteProgram = nullptr; } void ColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *cumap = this->m_curveMapping; + CurveMapping *cumap = m_curveMapping; float bwmul[3]; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { /* Local versions of `cumap->black` and `cumap->white`. */ @@ -134,20 +134,20 @@ ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_inputFacProgram = nullptr; - this->m_inputImageProgram = nullptr; + m_inputFacProgram = nullptr; + m_inputImageProgram = nullptr; this->set_canvas_input_index(1); } void ConstantLevelColorCurveOperation::initExecution() { CurveBaseOperation::initExecution(); - this->m_inputFacProgram = this->getInputSocketReader(0); - this->m_inputImageProgram = this->getInputSocketReader(1); + m_inputFacProgram = this->getInputSocketReader(0); + m_inputImageProgram = this->getInputSocketReader(1); - BKE_curvemapping_premultiply(this->m_curveMapping, 0); + BKE_curvemapping_premultiply(m_curveMapping, 0); - BKE_curvemapping_set_black_white(this->m_curveMapping, this->m_black, this->m_white); + BKE_curvemapping_set_black_white(m_curveMapping, m_black, m_white); } void ConstantLevelColorCurveOperation::executePixelSampled(float output[4], @@ -158,18 +158,18 @@ void ConstantLevelColorCurveOperation::executePixelSampled(float output[4], float fac[4]; float image[4]; - this->m_inputFacProgram->readSampled(fac, x, y, sampler); - this->m_inputImageProgram->readSampled(image, x, y, sampler); + m_inputFacProgram->readSampled(fac, x, y, sampler); + m_inputImageProgram->readSampled(image, x, y, sampler); if (*fac >= 1.0f) { - BKE_curvemapping_evaluate_premulRGBF(this->m_curveMapping, output, image); + BKE_curvemapping_evaluate_premulRGBF(m_curveMapping, output, image); } else if (*fac <= 0.0f) { copy_v3_v3(output, image); } else { float col[4]; - BKE_curvemapping_evaluate_premulRGBF(this->m_curveMapping, col, image); + BKE_curvemapping_evaluate_premulRGBF(m_curveMapping, col, image); interp_v3_v3v3(output, image, col, *fac); } output[3] = image[3]; @@ -178,15 +178,15 @@ void ConstantLevelColorCurveOperation::executePixelSampled(float output[4], void ConstantLevelColorCurveOperation::deinitExecution() { CurveBaseOperation::deinitExecution(); - this->m_inputFacProgram = nullptr; - this->m_inputImageProgram = nullptr; + m_inputFacProgram = nullptr; + m_inputImageProgram = nullptr; } void ConstantLevelColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *cumap = this->m_curveMapping; + CurveMapping *cumap = m_curveMapping; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float fac = *it.in(0); const float *image = it.in(1); diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h index 8f6b34c5c6f..b92640839ba 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.h +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h @@ -85,11 +85,11 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation { void setBlackLevel(float black[3]) { - copy_v3_v3(this->m_black, black); + copy_v3_v3(m_black, black); } void setWhiteLevel(float white[3]) { - copy_v3_v3(this->m_white, white); + copy_v3_v3(m_white, white); } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.cc b/source/blender/compositor/operations/COM_ColorExposureOperation.cc index 228550a31c5..7886f2a619f 100644 --- a/source/blender/compositor/operations/COM_ColorExposureOperation.cc +++ b/source/blender/compositor/operations/COM_ColorExposureOperation.cc @@ -25,14 +25,14 @@ ExposureOperation::ExposureOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; flags.can_be_constant = true; } void ExposureOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputExposureProgram = this->getInputSocketReader(1); + m_inputProgram = this->getInputSocketReader(0); + m_inputExposureProgram = this->getInputSocketReader(1); } void ExposureOperation::executePixelSampled(float output[4], @@ -42,8 +42,8 @@ void ExposureOperation::executePixelSampled(float output[4], { float inputValue[4]; float inputExposure[4]; - this->m_inputProgram->readSampled(inputValue, x, y, sampler); - this->m_inputExposureProgram->readSampled(inputExposure, x, y, sampler); + m_inputProgram->readSampled(inputValue, x, y, sampler); + m_inputExposureProgram->readSampled(inputExposure, x, y, sampler); const float exposure = pow(2, inputExposure[0]); output[0] = inputValue[0] * exposure; @@ -68,8 +68,8 @@ void ExposureOperation::update_memory_buffer_row(PixelCursor &p) void ExposureOperation::deinitExecution() { - this->m_inputProgram = nullptr; - this->m_inputExposureProgram = nullptr; + m_inputProgram = nullptr; + m_inputExposureProgram = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc index 7dd6a4ab73f..be211f6c966 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc @@ -26,21 +26,21 @@ ColorMatteOperation::ColorMatteOperation() addInputSocket(DataType::Color); addOutputSocket(DataType::Value); - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; + m_inputImageProgram = nullptr; + m_inputKeyProgram = nullptr; flags.can_be_constant = true; } void ColorMatteOperation::initExecution() { - this->m_inputImageProgram = this->getInputSocketReader(0); - this->m_inputKeyProgram = this->getInputSocketReader(1); + m_inputImageProgram = this->getInputSocketReader(0); + m_inputKeyProgram = this->getInputSocketReader(1); } void ColorMatteOperation::deinitExecution() { - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; + m_inputImageProgram = nullptr; + m_inputKeyProgram = nullptr; } void ColorMatteOperation::executePixelSampled(float output[4], @@ -51,14 +51,14 @@ void ColorMatteOperation::executePixelSampled(float output[4], float inColor[4]; float inKey[4]; - const float hue = this->m_settings->t1; - const float sat = this->m_settings->t2; - const float val = this->m_settings->t3; + const float hue = m_settings->t1; + const float sat = m_settings->t2; + const float val = m_settings->t3; float h_wrap; - this->m_inputImageProgram->readSampled(inColor, x, y, sampler); - this->m_inputKeyProgram->readSampled(inKey, x, y, sampler); + m_inputImageProgram->readSampled(inColor, x, y, sampler); + m_inputKeyProgram->readSampled(inKey, x, y, sampler); /* Store matte(alpha) value in [0] to go with * COM_SetAlphaMultiplyOperation and the Value output. diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.h b/source/blender/compositor/operations/COM_ColorMatteOperation.h index 49d06e62e65..f1fc2cf7e95 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.h +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.h @@ -48,7 +48,7 @@ class ColorMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma) { - this->m_settings = nodeChroma; + m_settings = nodeChroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.cc b/source/blender/compositor/operations/COM_ColorRampOperation.cc index 6c1b23ea731..274e1cc3f0f 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.cc +++ b/source/blender/compositor/operations/COM_ColorRampOperation.cc @@ -27,13 +27,13 @@ ColorRampOperation::ColorRampOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; - this->m_colorBand = nullptr; + m_inputProgram = nullptr; + m_colorBand = nullptr; this->flags.can_be_constant = true; } void ColorRampOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); } void ColorRampOperation::executePixelSampled(float output[4], @@ -43,13 +43,13 @@ void ColorRampOperation::executePixelSampled(float output[4], { float values[4]; - this->m_inputProgram->readSampled(values, x, y, sampler); - BKE_colorband_evaluate(this->m_colorBand, values[0], output); + m_inputProgram->readSampled(values, x, y, sampler); + BKE_colorband_evaluate(m_colorBand, values[0], output); } void ColorRampOperation::deinitExecution() { - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.h b/source/blender/compositor/operations/COM_ColorRampOperation.h index ab64b9928fd..21442e52800 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.h +++ b/source/blender/compositor/operations/COM_ColorRampOperation.h @@ -51,7 +51,7 @@ class ColorRampOperation : public MultiThreadedOperation { void setColorBand(ColorBand *colorBand) { - this->m_colorBand = colorBand; + m_colorBand = colorBand; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc index deed8636360..fde769fadc0 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc @@ -27,60 +27,60 @@ ColorSpillOperation::ColorSpillOperation() addInputSocket(DataType::Value); addOutputSocket(DataType::Color); - this->m_inputImageReader = nullptr; - this->m_inputFacReader = nullptr; - this->m_spillChannel = 1; /* GREEN */ - this->m_spillMethod = 0; + m_inputImageReader = nullptr; + m_inputFacReader = nullptr; + m_spillChannel = 1; /* GREEN */ + m_spillMethod = 0; flags.can_be_constant = true; } void ColorSpillOperation::initExecution() { - this->m_inputImageReader = this->getInputSocketReader(0); - this->m_inputFacReader = this->getInputSocketReader(1); - if (this->m_spillChannel == 0) { - this->m_rmut = -1.0f; - this->m_gmut = 1.0f; - this->m_bmut = 1.0f; - this->m_channel2 = 1; - this->m_channel3 = 2; - if (this->m_settings->unspill == 0) { - this->m_settings->uspillr = 1.0f; - this->m_settings->uspillg = 0.0f; - this->m_settings->uspillb = 0.0f; + m_inputImageReader = this->getInputSocketReader(0); + m_inputFacReader = this->getInputSocketReader(1); + if (m_spillChannel == 0) { + m_rmut = -1.0f; + m_gmut = 1.0f; + m_bmut = 1.0f; + m_channel2 = 1; + m_channel3 = 2; + if (m_settings->unspill == 0) { + m_settings->uspillr = 1.0f; + m_settings->uspillg = 0.0f; + m_settings->uspillb = 0.0f; } } - else if (this->m_spillChannel == 1) { - this->m_rmut = 1.0f; - this->m_gmut = -1.0f; - this->m_bmut = 1.0f; - this->m_channel2 = 0; - this->m_channel3 = 2; - if (this->m_settings->unspill == 0) { - this->m_settings->uspillr = 0.0f; - this->m_settings->uspillg = 1.0f; - this->m_settings->uspillb = 0.0f; + else if (m_spillChannel == 1) { + m_rmut = 1.0f; + m_gmut = -1.0f; + m_bmut = 1.0f; + m_channel2 = 0; + m_channel3 = 2; + if (m_settings->unspill == 0) { + m_settings->uspillr = 0.0f; + m_settings->uspillg = 1.0f; + m_settings->uspillb = 0.0f; } } else { - this->m_rmut = 1.0f; - this->m_gmut = 1.0f; - this->m_bmut = -1.0f; + m_rmut = 1.0f; + m_gmut = 1.0f; + m_bmut = -1.0f; - this->m_channel2 = 0; - this->m_channel3 = 1; - if (this->m_settings->unspill == 0) { - this->m_settings->uspillr = 0.0f; - this->m_settings->uspillg = 0.0f; - this->m_settings->uspillb = 1.0f; + m_channel2 = 0; + m_channel3 = 1; + if (m_settings->unspill == 0) { + m_settings->uspillr = 0.0f; + m_settings->uspillg = 0.0f; + m_settings->uspillb = 1.0f; } } } void ColorSpillOperation::deinitExecution() { - this->m_inputImageReader = nullptr; - this->m_inputFacReader = nullptr; + m_inputImageReader = nullptr; + m_inputFacReader = nullptr; } void ColorSpillOperation::executePixelSampled(float output[4], @@ -90,27 +90,25 @@ void ColorSpillOperation::executePixelSampled(float output[4], { float fac[4]; float input[4]; - this->m_inputFacReader->readSampled(fac, x, y, sampler); - this->m_inputImageReader->readSampled(input, x, y, sampler); + m_inputFacReader->readSampled(fac, x, y, sampler); + m_inputImageReader->readSampled(input, x, y, sampler); float rfac = MIN2(1.0f, fac[0]); float map; - switch (this->m_spillMethod) { + switch (m_spillMethod) { case 0: /* simple */ - map = rfac * (input[this->m_spillChannel] - - (this->m_settings->limscale * input[this->m_settings->limchan])); + map = rfac * (input[m_spillChannel] - (m_settings->limscale * input[m_settings->limchan])); break; default: /* average */ - map = rfac * - (input[this->m_spillChannel] - - (this->m_settings->limscale * AVG(input[this->m_channel2], input[this->m_channel3]))); + map = rfac * (input[m_spillChannel] - + (m_settings->limscale * AVG(input[m_channel2], input[m_channel3]))); break; } if (map > 0.0f) { - output[0] = input[0] + this->m_rmut * (this->m_settings->uspillr * map); - output[1] = input[1] + this->m_gmut * (this->m_settings->uspillg * map); - output[2] = input[2] + this->m_bmut * (this->m_settings->uspillb * map); + output[0] = input[0] + m_rmut * (m_settings->uspillr * map); + output[1] = input[1] + m_gmut * (m_settings->uspillg * map); + output[2] = input[2] + m_bmut * (m_settings->uspillb * map); output[3] = input[3]; } else { diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.h b/source/blender/compositor/operations/COM_ColorSpillOperation.h index 6a5e688c160..453eb8e88d9 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.h +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.h @@ -53,15 +53,15 @@ class ColorSpillOperation : public MultiThreadedOperation { void setSettings(NodeColorspill *nodeColorSpill) { - this->m_settings = nodeColorSpill; + m_settings = nodeColorSpill; } void setSpillChannel(int channel) { - this->m_spillChannel = channel; + m_spillChannel = channel; } void setSpillMethod(int method) { - this->m_spillMethod = method; + m_spillMethod = method; } float calculateMapValue(float fac, float *input); diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index 0713ecd9316..481aca1a68b 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -32,71 +32,71 @@ CompositorOperation::CompositorOperation() this->addInputSocket(DataType::Value); this->setRenderData(nullptr); - this->m_outputBuffer = nullptr; - this->m_depthBuffer = nullptr; - this->m_imageInput = nullptr; - this->m_alphaInput = nullptr; - this->m_depthInput = nullptr; + m_outputBuffer = nullptr; + m_depthBuffer = nullptr; + m_imageInput = nullptr; + m_alphaInput = nullptr; + m_depthInput = nullptr; - this->m_useAlphaInput = false; - this->m_active = false; + m_useAlphaInput = false; + m_active = false; - this->m_scene = nullptr; - this->m_sceneName[0] = '\0'; - this->m_viewName = nullptr; + m_scene = nullptr; + m_sceneName[0] = '\0'; + m_viewName = nullptr; flags.use_render_border = true; } void CompositorOperation::initExecution() { - if (!this->m_active) { + if (!m_active) { return; } /* When initializing the tree during initial load the width and height can be zero. */ - this->m_imageInput = getInputSocketReader(0); - this->m_alphaInput = getInputSocketReader(1); - this->m_depthInput = getInputSocketReader(2); + m_imageInput = getInputSocketReader(0); + m_alphaInput = getInputSocketReader(1); + m_depthInput = getInputSocketReader(2); if (this->getWidth() * this->getHeight() != 0) { - this->m_outputBuffer = (float *)MEM_callocN( - sizeof(float[4]) * this->getWidth() * this->getHeight(), "CompositorOperation"); + m_outputBuffer = (float *)MEM_callocN(sizeof(float[4]) * this->getWidth() * this->getHeight(), + "CompositorOperation"); } - if (this->m_depthInput != nullptr) { - this->m_depthBuffer = (float *)MEM_callocN( - sizeof(float) * this->getWidth() * this->getHeight(), "CompositorOperation"); + if (m_depthInput != nullptr) { + m_depthBuffer = (float *)MEM_callocN(sizeof(float) * this->getWidth() * this->getHeight(), + "CompositorOperation"); } } void CompositorOperation::deinitExecution() { - if (!this->m_active) { + if (!m_active) { return; } if (!isBraked()) { - Render *re = RE_GetSceneRender(this->m_scene); + Render *re = RE_GetSceneRender(m_scene); RenderResult *rr = RE_AcquireResultWrite(re); if (rr) { - RenderView *rv = RE_RenderViewGetByName(rr, this->m_viewName); + RenderView *rv = RE_RenderViewGetByName(rr, m_viewName); if (rv->rectf != nullptr) { MEM_freeN(rv->rectf); } - rv->rectf = this->m_outputBuffer; + rv->rectf = m_outputBuffer; if (rv->rectz != nullptr) { MEM_freeN(rv->rectz); } - rv->rectz = this->m_depthBuffer; + rv->rectz = m_depthBuffer; rr->have_combined = true; } else { - if (this->m_outputBuffer) { - MEM_freeN(this->m_outputBuffer); + if (m_outputBuffer) { + MEM_freeN(m_outputBuffer); } - if (this->m_depthBuffer) { - MEM_freeN(this->m_depthBuffer); + if (m_depthBuffer) { + MEM_freeN(m_depthBuffer); } } @@ -113,26 +113,26 @@ void CompositorOperation::deinitExecution() BLI_thread_unlock(LOCK_DRAW_IMAGE); } else { - if (this->m_outputBuffer) { - MEM_freeN(this->m_outputBuffer); + if (m_outputBuffer) { + MEM_freeN(m_outputBuffer); } - if (this->m_depthBuffer) { - MEM_freeN(this->m_depthBuffer); + if (m_depthBuffer) { + MEM_freeN(m_depthBuffer); } } - this->m_outputBuffer = nullptr; - this->m_depthBuffer = nullptr; - this->m_imageInput = nullptr; - this->m_alphaInput = nullptr; - this->m_depthInput = nullptr; + m_outputBuffer = nullptr; + m_depthBuffer = nullptr; + m_imageInput = nullptr; + m_alphaInput = nullptr; + m_depthInput = nullptr; } void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) { float color[8]; // 7 is enough - float *buffer = this->m_outputBuffer; - float *zbuffer = this->m_depthBuffer; + float *buffer = m_outputBuffer; + float *zbuffer = m_depthBuffer; if (!buffer) { return; @@ -150,7 +150,7 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) int dx = 0, dy = 0; #if 0 - const RenderData *rd = this->m_rd; + const RenderData *rd = m_rd; if (rd->mode & R_BORDER && rd->mode & R_CROP) { /** @@ -192,14 +192,14 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) for (x = x1; x < x2 && (!breaked); x++) { int input_x = x + dx, input_y = y + dy; - this->m_imageInput->readSampled(color, input_x, input_y, PixelSampler::Nearest); - if (this->m_useAlphaInput) { - this->m_alphaInput->readSampled(&(color[3]), input_x, input_y, PixelSampler::Nearest); + m_imageInput->readSampled(color, input_x, input_y, PixelSampler::Nearest); + if (m_useAlphaInput) { + m_alphaInput->readSampled(&(color[3]), input_x, input_y, PixelSampler::Nearest); } copy_v4_v4(buffer + offset4, color); - this->m_depthInput->readSampled(color, input_x, input_y, PixelSampler::Nearest); + m_depthInput->readSampled(color, input_x, input_y, PixelSampler::Nearest); zbuffer[offset] = color[0]; offset4 += COM_DATA_TYPE_COLOR_CHANNELS; offset++; @@ -221,7 +221,7 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp } MemoryBuffer output_buf(m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); output_buf.copy_from(inputs[0], area); - if (this->m_useAlphaInput) { + if (m_useAlphaInput) { output_buf.copy_from(inputs[1], area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3); } MemoryBuffer depth_buf(m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); @@ -230,12 +230,12 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp void CompositorOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - int width = this->m_rd->xsch * this->m_rd->size / 100; - int height = this->m_rd->ysch * this->m_rd->size / 100; + int width = m_rd->xsch * m_rd->size / 100; + int height = m_rd->ysch * m_rd->size / 100; /* Check actual render resolution with cropping it may differ with cropped border.rendering * Fix for T31777 Border Crop gives black (easy). */ - Render *re = RE_GetSceneRender(this->m_scene); + Render *re = RE_GetSceneRender(m_scene); if (re) { RenderResult *rr = RE_AcquireResultRead(re); if (rr) { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index 7b823824cff..c63613b7b35 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -84,7 +84,7 @@ class CompositorOperation : public MultiThreadedOperation { CompositorOperation(); bool isActiveCompositorOutput() const { - return this->m_active; + return m_active; } void executeRegion(rcti *rect, unsigned int tileNumber) override; void setScene(const struct Scene *scene) @@ -93,15 +93,15 @@ class CompositorOperation : public MultiThreadedOperation { } void setSceneName(const char *sceneName) { - BLI_strncpy(this->m_sceneName, sceneName, sizeof(this->m_sceneName)); + BLI_strncpy(m_sceneName, sceneName, sizeof(m_sceneName)); } void setViewName(const char *viewName) { - this->m_viewName = viewName; + m_viewName = viewName; } void setRenderData(const RenderData *rd) { - this->m_rd = rd; + m_rd = rd; } bool isOutputOperation(bool /*rendering*/) const override { @@ -116,11 +116,11 @@ class CompositorOperation : public MultiThreadedOperation { void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void setUseAlphaInput(bool value) { - this->m_useAlphaInput = value; + m_useAlphaInput = value; } void setActive(bool active) { - this->m_active = active; + m_active = active; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc index c00fe5d5f61..09a1541985b 100644 --- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc @@ -26,13 +26,13 @@ ConvertColorProfileOperation::ConvertColorProfileOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_inputOperation = nullptr; - this->m_predivided = false; + m_inputOperation = nullptr; + m_predivided = false; } void ConvertColorProfileOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); + m_inputOperation = this->getInputSocketReader(0); } void ConvertColorProfileOperation::executePixelSampled(float output[4], @@ -41,14 +41,14 @@ void ConvertColorProfileOperation::executePixelSampled(float output[4], PixelSampler sampler) { float color[4]; - this->m_inputOperation->readSampled(color, x, y, sampler); + m_inputOperation->readSampled(color, x, y, sampler); IMB_buffer_float_from_float( - output, color, 4, this->m_toProfile, this->m_fromProfile, this->m_predivided, 1, 1, 0, 0); + output, color, 4, m_toProfile, m_fromProfile, m_predivided, 1, 1, 0, 0); } void ConvertColorProfileOperation::deinitExecution() { - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h index 6162408501b..6d7b19aa859 100644 --- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h +++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h @@ -71,15 +71,15 @@ class ConvertColorProfileOperation : public NodeOperation { void setFromColorProfile(int colorProfile) { - this->m_fromProfile = colorProfile; + m_fromProfile = colorProfile; } void setToColorProfile(int colorProfile) { - this->m_toProfile = colorProfile; + m_toProfile = colorProfile; } void setPredivided(bool predivided) { - this->m_predivided = predivided; + m_predivided = predivided; } }; diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc index 18a28f854be..ee2a62f6147 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc @@ -26,19 +26,19 @@ ConvertDepthToRadiusOperation::ConvertDepthToRadiusOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_inputOperation = nullptr; - this->m_fStop = 128.0f; - this->m_cameraObject = nullptr; - this->m_maxRadius = 32.0f; - this->m_blurPostOperation = nullptr; + m_inputOperation = nullptr; + m_fStop = 128.0f; + m_cameraObject = nullptr; + m_maxRadius = 32.0f; + m_blurPostOperation = nullptr; } float ConvertDepthToRadiusOperation::determineFocalDistance() { - if (this->m_cameraObject && this->m_cameraObject->type == OB_CAMERA) { - Camera *camera = (Camera *)this->m_cameraObject->data; - this->m_cam_lens = camera->lens; - return BKE_camera_object_dof_distance(this->m_cameraObject); + if (m_cameraObject && m_cameraObject->type == OB_CAMERA) { + Camera *camera = (Camera *)m_cameraObject->data; + m_cam_lens = camera->lens; + return BKE_camera_object_dof_distance(m_cameraObject); } return 10.0f; @@ -49,28 +49,27 @@ void ConvertDepthToRadiusOperation::initExecution() float cam_sensor = DEFAULT_SENSOR_WIDTH; Camera *camera = nullptr; - if (this->m_cameraObject && this->m_cameraObject->type == OB_CAMERA) { - camera = (Camera *)this->m_cameraObject->data; + if (m_cameraObject && m_cameraObject->type == OB_CAMERA) { + camera = (Camera *)m_cameraObject->data; cam_sensor = BKE_camera_sensor_size(camera->sensor_fit, camera->sensor_x, camera->sensor_y); } - this->m_inputOperation = this->getInputSocketReader(0); + m_inputOperation = this->getInputSocketReader(0); float focalDistance = determineFocalDistance(); if (focalDistance == 0.0f) { focalDistance = 1e10f; /* If the DOF is 0.0 then set it to be far away. */ } - this->m_inverseFocalDistance = 1.0f / focalDistance; - this->m_aspect = (this->getWidth() > this->getHeight()) ? - (this->getHeight() / (float)this->getWidth()) : - (this->getWidth() / (float)this->getHeight()); - this->m_aperture = 0.5f * (this->m_cam_lens / (this->m_aspect * cam_sensor)) / this->m_fStop; + m_inverseFocalDistance = 1.0f / focalDistance; + m_aspect = (this->getWidth() > this->getHeight()) ? + (this->getHeight() / (float)this->getWidth()) : + (this->getWidth() / (float)this->getHeight()); + m_aperture = 0.5f * (m_cam_lens / (m_aspect * cam_sensor)) / m_fStop; const float minsz = MIN2(getWidth(), getHeight()); - this->m_dof_sp = - minsz / ((cam_sensor / 2.0f) / - this->m_cam_lens); /* <- == `aspect * MIN2(img->x, img->y) / tan(0.5f * fov)` */ + m_dof_sp = minsz / ((cam_sensor / 2.0f) / + m_cam_lens); /* <- == `aspect * MIN2(img->x, img->y) / tan(0.5f * fov)` */ - if (this->m_blurPostOperation) { - m_blurPostOperation->setSigma(MIN2(m_aperture * 128.0f, this->m_maxRadius)); + if (m_blurPostOperation) { + m_blurPostOperation->setSigma(MIN2(m_aperture * 128.0f, m_maxRadius)); } } @@ -82,7 +81,7 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], float inputValue[4]; float z; float radius; - this->m_inputOperation->readSampled(inputValue, x, y, sampler); + m_inputOperation->readSampled(inputValue, x, y, sampler); z = inputValue[0]; if (z != 0.0f) { float iZ = (1.0f / z); @@ -93,15 +92,14 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], /* Scale crad back to original maximum and blend. */ crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad); #endif - radius = 0.5f * fabsf(this->m_aperture * - (this->m_dof_sp * (this->m_inverseFocalDistance - iZ) - 1.0f)); + radius = 0.5f * fabsf(m_aperture * (m_dof_sp * (m_inverseFocalDistance - iZ) - 1.0f)); /* 'bug' T6615, limit minimum radius to 1 pixel, * not really a solution, but somewhat mitigates the problem. */ if (radius < 0.0f) { radius = 0.0f; } - if (radius > this->m_maxRadius) { - radius = this->m_maxRadius; + if (radius > m_maxRadius) { + radius = m_maxRadius; } output[0] = radius; } @@ -112,7 +110,7 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], void ConvertDepthToRadiusOperation::deinitExecution() { - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h index 3d163843d06..746cb89b6c0 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h @@ -68,20 +68,20 @@ class ConvertDepthToRadiusOperation : public MultiThreadedOperation { void setfStop(float fStop) { - this->m_fStop = fStop; + m_fStop = fStop; } void setMaxRadius(float maxRadius) { - this->m_maxRadius = maxRadius; + m_maxRadius = maxRadius; } void setCameraObject(Object *camera) { - this->m_cameraObject = camera; + m_cameraObject = camera; } float determineFocalDistance(); void setPostBlur(FastGaussianBlurValueOperation *operation) { - this->m_blurPostOperation = operation; + m_blurPostOperation = operation; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 7b2721ebbb2..1cef0490ff5 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -26,18 +26,18 @@ namespace blender::compositor { ConvertBaseOperation::ConvertBaseOperation() { - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; this->flags.can_be_constant = true; } void ConvertBaseOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); + m_inputOperation = this->getInputSocketReader(0); } void ConvertBaseOperation::deinitExecution() { - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } void ConvertBaseOperation::hash_output_params() @@ -66,7 +66,7 @@ void ConvertValueToColorOperation::executePixelSampled(float output[4], PixelSampler sampler) { float value; - this->m_inputOperation->readSampled(&value, x, y, sampler); + m_inputOperation->readSampled(&value, x, y, sampler); output[0] = output[1] = output[2] = value; output[3] = 1.0f; } @@ -93,7 +93,7 @@ void ConvertColorToValueOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); + m_inputOperation->readSampled(inputColor, x, y, sampler); output[0] = (inputColor[0] + inputColor[1] + inputColor[2]) / 3.0f; } @@ -119,7 +119,7 @@ void ConvertColorToBWOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); + m_inputOperation->readSampled(inputColor, x, y, sampler); output[0] = IMB_colormanagement_get_luminance(inputColor); } @@ -144,7 +144,7 @@ void ConvertColorToVectorOperation::executePixelSampled(float output[4], PixelSampler sampler) { float color[4]; - this->m_inputOperation->readSampled(color, x, y, sampler); + m_inputOperation->readSampled(color, x, y, sampler); copy_v3_v3(output, color); } @@ -169,7 +169,7 @@ void ConvertValueToVectorOperation::executePixelSampled(float output[4], PixelSampler sampler) { float value; - this->m_inputOperation->readSampled(&value, x, y, sampler); + m_inputOperation->readSampled(&value, x, y, sampler); output[0] = output[1] = output[2] = value; } @@ -193,7 +193,7 @@ void ConvertVectorToColorOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - this->m_inputOperation->readSampled(output, x, y, sampler); + m_inputOperation->readSampled(output, x, y, sampler); output[3] = 1.0f; } @@ -219,7 +219,7 @@ void ConvertVectorToValueOperation::executePixelSampled(float output[4], PixelSampler sampler) { float input[4]; - this->m_inputOperation->readSampled(input, x, y, sampler); + m_inputOperation->readSampled(input, x, y, sampler); output[0] = (input[0] + input[1] + input[2]) / 3.0f; } @@ -243,14 +243,14 @@ void ConvertRGBToYCCOperation::setMode(int mode) { switch (mode) { case 0: - this->m_mode = BLI_YCC_ITU_BT601; + m_mode = BLI_YCC_ITU_BT601; break; case 2: - this->m_mode = BLI_YCC_JFIF_0_255; + m_mode = BLI_YCC_JFIF_0_255; break; case 1: default: - this->m_mode = BLI_YCC_ITU_BT709; + m_mode = BLI_YCC_ITU_BT709; break; } } @@ -263,9 +263,9 @@ void ConvertRGBToYCCOperation::executePixelSampled(float output[4], float inputColor[4]; float color[3]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); + m_inputOperation->readSampled(inputColor, x, y, sampler); rgb_to_ycc( - inputColor[0], inputColor[1], inputColor[2], &color[0], &color[1], &color[2], this->m_mode); + inputColor[0], inputColor[1], inputColor[2], &color[0], &color[1], &color[2], m_mode); /* divided by 255 to normalize for viewing in */ /* R,G,B --> Y,Cb,Cr */ @@ -283,7 +283,7 @@ void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIteratorm_mode); + rgb_to_ycc(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], m_mode); /* Normalize for viewing (#rgb_to_ycc returns 0-255 values). */ mul_v3_fl(it.out, 1.0f / 255.0f); @@ -303,14 +303,14 @@ void ConvertYCCToRGBOperation::setMode(int mode) { switch (mode) { case 0: - this->m_mode = BLI_YCC_ITU_BT601; + m_mode = BLI_YCC_ITU_BT601; break; case 2: - this->m_mode = BLI_YCC_JFIF_0_255; + m_mode = BLI_YCC_JFIF_0_255; break; case 1: default: - this->m_mode = BLI_YCC_ITU_BT709; + m_mode = BLI_YCC_ITU_BT709; break; } } @@ -321,7 +321,7 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); + m_inputOperation->readSampled(inputColor, x, y, sampler); /* need to un-normalize the data */ /* R,G,B --> Y,Cb,Cr */ @@ -333,7 +333,7 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4], &output[0], &output[1], &output[2], - this->m_mode); + m_mode); output[3] = inputColor[3]; } @@ -354,7 +354,7 @@ void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIteratorm_mode); + m_mode); it.out[3] = in[3]; } } @@ -373,7 +373,7 @@ void ConvertRGBToYUVOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); + m_inputOperation->readSampled(inputColor, x, y, sampler); rgb_to_yuv(inputColor[0], inputColor[1], inputColor[2], @@ -407,7 +407,7 @@ void ConvertYUVToRGBOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); + m_inputOperation->readSampled(inputColor, x, y, sampler); yuv_to_rgb(inputColor[0], inputColor[1], inputColor[2], @@ -441,7 +441,7 @@ void ConvertRGBToHSVOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); + m_inputOperation->readSampled(inputColor, x, y, sampler); rgb_to_hsv_v(inputColor, output); output[3] = inputColor[3]; } @@ -469,7 +469,7 @@ void ConvertHSVToRGBOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); + m_inputOperation->readSampled(inputColor, x, y, sampler); hsv_to_rgb_v(inputColor, output); output[0] = max_ff(output[0], 0.0f); output[1] = max_ff(output[1], 0.0f); @@ -503,7 +503,7 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4], PixelSampler sampler) { ColorSceneLinear4f input; - this->m_inputOperation->readSampled(input, x, y, sampler); + m_inputOperation->readSampled(input, x, y, sampler); ColorSceneLinear4f converted = input.unpremultiply_alpha(); copy_v4_v4(output, converted); } @@ -529,7 +529,7 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4], PixelSampler sampler) { ColorSceneLinear4f input; - this->m_inputOperation->readSampled(input, x, y, sampler); + m_inputOperation->readSampled(input, x, y, sampler); ColorSceneLinear4f converted = input.premultiply_alpha(); copy_v4_v4(output, converted); } @@ -547,16 +547,16 @@ SeparateChannelOperation::SeparateChannelOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Value); - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } void SeparateChannelOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); + m_inputOperation = this->getInputSocketReader(0); } void SeparateChannelOperation::deinitExecution() { - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } void SeparateChannelOperation::executePixelSampled(float output[4], @@ -565,8 +565,8 @@ void SeparateChannelOperation::executePixelSampled(float output[4], PixelSampler sampler) { float input[4]; - this->m_inputOperation->readSampled(input, x, y, sampler); - output[0] = input[this->m_channel]; + m_inputOperation->readSampled(input, x, y, sampler); + output[0] = input[m_channel]; } void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -574,7 +574,7 @@ void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output Span inputs) { for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - it.out[0] = it.in(0)[this->m_channel]; + it.out[0] = it.in(0)[m_channel]; } } @@ -588,26 +588,26 @@ CombineChannelsOperation::CombineChannelsOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - this->m_inputChannel1Operation = nullptr; - this->m_inputChannel2Operation = nullptr; - this->m_inputChannel3Operation = nullptr; - this->m_inputChannel4Operation = nullptr; + m_inputChannel1Operation = nullptr; + m_inputChannel2Operation = nullptr; + m_inputChannel3Operation = nullptr; + m_inputChannel4Operation = nullptr; } void CombineChannelsOperation::initExecution() { - this->m_inputChannel1Operation = this->getInputSocketReader(0); - this->m_inputChannel2Operation = this->getInputSocketReader(1); - this->m_inputChannel3Operation = this->getInputSocketReader(2); - this->m_inputChannel4Operation = this->getInputSocketReader(3); + m_inputChannel1Operation = this->getInputSocketReader(0); + m_inputChannel2Operation = this->getInputSocketReader(1); + m_inputChannel3Operation = this->getInputSocketReader(2); + m_inputChannel4Operation = this->getInputSocketReader(3); } void CombineChannelsOperation::deinitExecution() { - this->m_inputChannel1Operation = nullptr; - this->m_inputChannel2Operation = nullptr; - this->m_inputChannel3Operation = nullptr; - this->m_inputChannel4Operation = nullptr; + m_inputChannel1Operation = nullptr; + m_inputChannel2Operation = nullptr; + m_inputChannel3Operation = nullptr; + m_inputChannel4Operation = nullptr; } void CombineChannelsOperation::executePixelSampled(float output[4], @@ -616,20 +616,20 @@ void CombineChannelsOperation::executePixelSampled(float output[4], PixelSampler sampler) { float input[4]; - if (this->m_inputChannel1Operation) { - this->m_inputChannel1Operation->readSampled(input, x, y, sampler); + if (m_inputChannel1Operation) { + m_inputChannel1Operation->readSampled(input, x, y, sampler); output[0] = input[0]; } - if (this->m_inputChannel2Operation) { - this->m_inputChannel2Operation->readSampled(input, x, y, sampler); + if (m_inputChannel2Operation) { + m_inputChannel2Operation->readSampled(input, x, y, sampler); output[1] = input[0]; } - if (this->m_inputChannel3Operation) { - this->m_inputChannel3Operation->readSampled(input, x, y, sampler); + if (m_inputChannel3Operation) { + m_inputChannel3Operation->readSampled(input, x, y, sampler); output[2] = input[0]; } - if (this->m_inputChannel4Operation) { - this->m_inputChannel4Operation->readSampled(input, x, y, sampler); + if (m_inputChannel4Operation) { + m_inputChannel4Operation->readSampled(input, x, y, sampler); output[3] = input[0]; } } diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h index 72864b3c5e2..cd8e3016e97 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.h +++ b/source/blender/compositor/operations/COM_ConvertOperation.h @@ -221,7 +221,7 @@ class SeparateChannelOperation : public MultiThreadedOperation { void setChannel(int channel) { - this->m_channel = channel; + m_channel = channel; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc index 0f44ccd6fcb..b768a5e80ba 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc @@ -38,44 +38,44 @@ void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, CLAMP(y3, 0, getHeight() - 1); float value[4]; - this->m_inputValueOperation->read(value, x2, y2, nullptr); + m_inputValueOperation->read(value, x2, y2, nullptr); float mval = 1.0f - value[0]; - this->m_inputOperation->read(in1, x1, y1, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[0]); - madd_v3_v3fl(res2, in1, this->m_filter[0]); + m_inputOperation->read(in1, x1, y1, nullptr); + madd_v3_v3fl(res1, in1, m_filter[0]); + madd_v3_v3fl(res2, in1, m_filter[0]); - this->m_inputOperation->read(in1, x2, y1, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[1]); - madd_v3_v3fl(res2, in1, this->m_filter[3]); + m_inputOperation->read(in1, x2, y1, nullptr); + madd_v3_v3fl(res1, in1, m_filter[1]); + madd_v3_v3fl(res2, in1, m_filter[3]); - this->m_inputOperation->read(in1, x3, y1, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[2]); - madd_v3_v3fl(res2, in1, this->m_filter[6]); + m_inputOperation->read(in1, x3, y1, nullptr); + madd_v3_v3fl(res1, in1, m_filter[2]); + madd_v3_v3fl(res2, in1, m_filter[6]); - this->m_inputOperation->read(in1, x1, y2, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[3]); - madd_v3_v3fl(res2, in1, this->m_filter[1]); + m_inputOperation->read(in1, x1, y2, nullptr); + madd_v3_v3fl(res1, in1, m_filter[3]); + madd_v3_v3fl(res2, in1, m_filter[1]); - this->m_inputOperation->read(in2, x2, y2, nullptr); - madd_v3_v3fl(res1, in2, this->m_filter[4]); - madd_v3_v3fl(res2, in2, this->m_filter[4]); + m_inputOperation->read(in2, x2, y2, nullptr); + madd_v3_v3fl(res1, in2, m_filter[4]); + madd_v3_v3fl(res2, in2, m_filter[4]); - this->m_inputOperation->read(in1, x3, y2, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[5]); - madd_v3_v3fl(res2, in1, this->m_filter[7]); + m_inputOperation->read(in1, x3, y2, nullptr); + madd_v3_v3fl(res1, in1, m_filter[5]); + madd_v3_v3fl(res2, in1, m_filter[7]); - this->m_inputOperation->read(in1, x1, y3, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[6]); - madd_v3_v3fl(res2, in1, this->m_filter[2]); + m_inputOperation->read(in1, x1, y3, nullptr); + madd_v3_v3fl(res1, in1, m_filter[6]); + madd_v3_v3fl(res2, in1, m_filter[2]); - this->m_inputOperation->read(in1, x2, y3, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[7]); - madd_v3_v3fl(res2, in1, this->m_filter[5]); + m_inputOperation->read(in1, x2, y3, nullptr); + madd_v3_v3fl(res1, in1, m_filter[7]); + madd_v3_v3fl(res2, in1, m_filter[5]); - this->m_inputOperation->read(in1, x3, y3, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[8]); - madd_v3_v3fl(res2, in1, this->m_filter[8]); + m_inputOperation->read(in1, x3, y3, nullptr); + madd_v3_v3fl(res1, in1, m_filter[8]); + madd_v3_v3fl(res2, in1, m_filter[8]); output[0] = sqrt(res1[0] * res1[0] + res2[0] * res2[0]); output[1] = sqrt(res1[1] * res1[1] + res2[1] * res2[1]); diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc index 9d4e8397f54..f438f01e11a 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc @@ -26,35 +26,35 @@ ConvolutionFilterOperation::ConvolutionFilterOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; this->flags.complex = true; } void ConvolutionFilterOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_inputValueOperation = this->getInputSocketReader(1); + m_inputOperation = this->getInputSocketReader(0); + m_inputValueOperation = this->getInputSocketReader(1); } void ConvolutionFilterOperation::set3x3Filter( float f1, float f2, float f3, float f4, float f5, float f6, float f7, float f8, float f9) { - this->m_filter[0] = f1; - this->m_filter[1] = f2; - this->m_filter[2] = f3; - this->m_filter[3] = f4; - this->m_filter[4] = f5; - this->m_filter[5] = f6; - this->m_filter[6] = f7; - this->m_filter[7] = f8; - this->m_filter[8] = f9; - this->m_filterHeight = 3; - this->m_filterWidth = 3; + m_filter[0] = f1; + m_filter[1] = f2; + m_filter[2] = f3; + m_filter[3] = f4; + m_filter[4] = f5; + m_filter[5] = f6; + m_filter[6] = f7; + m_filter[7] = f8; + m_filter[8] = f9; + m_filterHeight = 3; + m_filterWidth = 3; } void ConvolutionFilterOperation::deinitExecution() { - this->m_inputOperation = nullptr; - this->m_inputValueOperation = nullptr; + m_inputOperation = nullptr; + m_inputValueOperation = nullptr; } void ConvolutionFilterOperation::executePixel(float output[4], int x, int y, void * /*data*/) @@ -74,28 +74,28 @@ void ConvolutionFilterOperation::executePixel(float output[4], int x, int y, voi CLAMP(y2, 0, getHeight() - 1); CLAMP(y3, 0, getHeight() - 1); float value[4]; - this->m_inputValueOperation->read(value, x2, y2, nullptr); + m_inputValueOperation->read(value, x2, y2, nullptr); const float mval = 1.0f - value[0]; zero_v4(output); - this->m_inputOperation->read(in1, x1, y1, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[0]); - this->m_inputOperation->read(in1, x2, y1, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[1]); - this->m_inputOperation->read(in1, x3, y1, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[2]); - this->m_inputOperation->read(in1, x1, y2, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[3]); - this->m_inputOperation->read(in2, x2, y2, nullptr); - madd_v4_v4fl(output, in2, this->m_filter[4]); - this->m_inputOperation->read(in1, x3, y2, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[5]); - this->m_inputOperation->read(in1, x1, y3, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[6]); - this->m_inputOperation->read(in1, x2, y3, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[7]); - this->m_inputOperation->read(in1, x3, y3, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[8]); + m_inputOperation->read(in1, x1, y1, nullptr); + madd_v4_v4fl(output, in1, m_filter[0]); + m_inputOperation->read(in1, x2, y1, nullptr); + madd_v4_v4fl(output, in1, m_filter[1]); + m_inputOperation->read(in1, x3, y1, nullptr); + madd_v4_v4fl(output, in1, m_filter[2]); + m_inputOperation->read(in1, x1, y2, nullptr); + madd_v4_v4fl(output, in1, m_filter[3]); + m_inputOperation->read(in2, x2, y2, nullptr); + madd_v4_v4fl(output, in2, m_filter[4]); + m_inputOperation->read(in1, x3, y2, nullptr); + madd_v4_v4fl(output, in1, m_filter[5]); + m_inputOperation->read(in1, x1, y3, nullptr); + madd_v4_v4fl(output, in1, m_filter[6]); + m_inputOperation->read(in1, x2, y3, nullptr); + madd_v4_v4fl(output, in1, m_filter[7]); + m_inputOperation->read(in1, x3, y3, nullptr); + madd_v4_v4fl(output, in1, m_filter[8]); output[0] = output[0] * value[0] + in2[0] * mval; output[1] = output[1] * value[0] + in2[1] * mval; @@ -113,8 +113,8 @@ bool ConvolutionFilterOperation::determineDependingAreaOfInterest( rcti *input, ReadBufferOperation *readOperation, rcti *output) { rcti newInput; - int addx = (this->m_filterWidth - 1) / 2 + 1; - int addy = (this->m_filterHeight - 1) / 2 + 1; + int addx = (m_filterWidth - 1) / 2 + 1; + int addy = (m_filterHeight - 1) / 2 + 1; newInput.xmax = input->xmax + addx; newInput.xmin = input->xmin - addx; newInput.ymax = input->ymax + addy; diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc index c5a5d1409f4..d1443e3e571 100644 --- a/source/blender/compositor/operations/COM_CropOperation.cc +++ b/source/blender/compositor/operations/COM_CropOperation.cc @@ -24,8 +24,8 @@ CropBaseOperation::CropBaseOperation() { this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Color); - this->m_inputOperation = nullptr; - this->m_settings = nullptr; + m_inputOperation = nullptr; + m_settings = nullptr; } void CropBaseOperation::updateArea() @@ -33,10 +33,10 @@ void CropBaseOperation::updateArea() SocketReader *inputReference = this->getInputSocketReader(0); float width = inputReference->getWidth(); float height = inputReference->getHeight(); - NodeTwoXYs local_settings = *this->m_settings; + NodeTwoXYs local_settings = *m_settings; if (width > 0.0f && height > 0.0f) { - if (this->m_relative) { + if (m_relative) { local_settings.x1 = width * local_settings.fac_x1; local_settings.x2 = width * local_settings.fac_x2; local_settings.y1 = height * local_settings.fac_y1; @@ -55,28 +55,28 @@ void CropBaseOperation::updateArea() local_settings.y2 = height - 1; } - this->m_xmax = MAX2(local_settings.x1, local_settings.x2) + 1; - this->m_xmin = MIN2(local_settings.x1, local_settings.x2); - this->m_ymax = MAX2(local_settings.y1, local_settings.y2) + 1; - this->m_ymin = MIN2(local_settings.y1, local_settings.y2); + m_xmax = MAX2(local_settings.x1, local_settings.x2) + 1; + m_xmin = MIN2(local_settings.x1, local_settings.x2); + m_ymax = MAX2(local_settings.y1, local_settings.y2) + 1; + m_ymin = MIN2(local_settings.y1, local_settings.y2); } else { - this->m_xmax = 0; - this->m_xmin = 0; - this->m_ymax = 0; - this->m_ymin = 0; + m_xmax = 0; + m_xmin = 0; + m_ymax = 0; + m_ymin = 0; } } void CropBaseOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); + m_inputOperation = this->getInputSocketReader(0); updateArea(); } void CropBaseOperation::deinitExecution() { - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } CropOperation::CropOperation() : CropBaseOperation() @@ -86,8 +86,8 @@ CropOperation::CropOperation() : CropBaseOperation() void CropOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { - if ((x < this->m_xmax && x >= this->m_xmin) && (y < this->m_ymax && y >= this->m_ymin)) { - this->m_inputOperation->readSampled(output, x, y, sampler); + if ((x < m_xmax && x >= m_xmin) && (y < m_ymax && y >= m_ymin)) { + m_inputOperation->readSampled(output, x, y, sampler); } else { zero_v4(output); @@ -121,10 +121,10 @@ bool CropImageOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - newInput.xmax = input->xmax + this->m_xmin; - newInput.xmin = input->xmin + this->m_xmin; - newInput.ymax = input->ymax + this->m_ymin; - newInput.ymin = input->ymin + this->m_ymin; + newInput.xmax = input->xmax + m_xmin; + newInput.xmin = input->xmin + m_xmin; + newInput.ymax = input->ymax + m_ymin; + newInput.ymin = input->ymin + m_ymin; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -135,10 +135,10 @@ void CropImageOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmax = output_area.xmax + this->m_xmin; - r_input_area.xmin = output_area.xmin + this->m_xmin; - r_input_area.ymax = output_area.ymax + this->m_ymin; - r_input_area.ymin = output_area.ymin + this->m_ymin; + r_input_area.xmax = output_area.xmax + m_xmin; + r_input_area.xmin = output_area.xmin + m_xmin; + r_input_area.ymax = output_area.ymax + m_ymin; + r_input_area.ymin = output_area.ymin + m_ymin; } void CropImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -155,7 +155,7 @@ void CropImageOperation::executePixelSampled(float output[4], PixelSampler sampler) { if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { - this->m_inputOperation->readSampled(output, (x + this->m_xmin), (y + this->m_ymin), sampler); + m_inputOperation->readSampled(output, (x + m_xmin), (y + m_ymin), sampler); } else { zero_v4(output); @@ -171,7 +171,7 @@ void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output, const MemoryBuffer *input = inputs[0]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) { - input->read_elem_checked(it.x + this->m_xmin, it.y + this->m_ymin, it.out); + input->read_elem_checked(it.x + m_xmin, it.y + m_ymin, it.out); } else { zero_v4(it.out); diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h index a156727402b..49d0a70fb4e 100644 --- a/source/blender/compositor/operations/COM_CropOperation.h +++ b/source/blender/compositor/operations/COM_CropOperation.h @@ -40,11 +40,11 @@ class CropBaseOperation : public MultiThreadedOperation { void deinitExecution() override; void setCropSettings(NodeTwoXYs *settings) { - this->m_settings = settings; + m_settings = settings; } void setRelative(bool rel) { - this->m_relative = rel; + m_relative = rel; } }; diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc index 3c4b27aa4cf..5a0d6581f2c 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc @@ -24,37 +24,37 @@ namespace blender::compositor { CurveBaseOperation::CurveBaseOperation() { - this->m_curveMapping = nullptr; + m_curveMapping = nullptr; this->flags.can_be_constant = true; } CurveBaseOperation::~CurveBaseOperation() { - if (this->m_curveMapping) { - BKE_curvemapping_free(this->m_curveMapping); - this->m_curveMapping = nullptr; + if (m_curveMapping) { + BKE_curvemapping_free(m_curveMapping); + m_curveMapping = nullptr; } } void CurveBaseOperation::initExecution() { - BKE_curvemapping_init(this->m_curveMapping); + BKE_curvemapping_init(m_curveMapping); } void CurveBaseOperation::deinitExecution() { - if (this->m_curveMapping) { - BKE_curvemapping_free(this->m_curveMapping); - this->m_curveMapping = nullptr; + if (m_curveMapping) { + BKE_curvemapping_free(m_curveMapping); + m_curveMapping = nullptr; } } void CurveBaseOperation::setCurveMapping(CurveMapping *mapping) { /* duplicate the curve to avoid glitches while drawing, see bug T32374. */ - if (this->m_curveMapping) { - BKE_curvemapping_free(this->m_curveMapping); + if (m_curveMapping) { + BKE_curvemapping_free(m_curveMapping); } - this->m_curveMapping = BKE_curvemapping_copy(mapping); + m_curveMapping = BKE_curvemapping_copy(mapping); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index d8362f4da8d..fa702558a20 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -160,21 +160,21 @@ DenoiseOperation::DenoiseOperation() this->addInputSocket(DataType::Vector); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_settings = nullptr; + m_settings = nullptr; } void DenoiseOperation::initExecution() { SingleThreadedOperation::initExecution(); - this->m_inputProgramColor = getInputSocketReader(0); - this->m_inputProgramNormal = getInputSocketReader(1); - this->m_inputProgramAlbedo = getInputSocketReader(2); + m_inputProgramColor = getInputSocketReader(0); + m_inputProgramNormal = getInputSocketReader(1); + m_inputProgramAlbedo = getInputSocketReader(2); } void DenoiseOperation::deinitExecution() { - this->m_inputProgramColor = nullptr; - this->m_inputProgramNormal = nullptr; - this->m_inputProgramAlbedo = nullptr; + m_inputProgramColor = nullptr; + m_inputProgramNormal = nullptr; + m_inputProgramAlbedo = nullptr; SingleThreadedOperation::deinitExecution(); } @@ -199,16 +199,16 @@ void DenoiseOperation::hash_output_params() MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2) { - MemoryBuffer *tileColor = (MemoryBuffer *)this->m_inputProgramColor->initializeTileData(rect2); - MemoryBuffer *tileNormal = (MemoryBuffer *)this->m_inputProgramNormal->initializeTileData(rect2); - MemoryBuffer *tileAlbedo = (MemoryBuffer *)this->m_inputProgramAlbedo->initializeTileData(rect2); + MemoryBuffer *tileColor = (MemoryBuffer *)m_inputProgramColor->initializeTileData(rect2); + MemoryBuffer *tileNormal = (MemoryBuffer *)m_inputProgramNormal->initializeTileData(rect2); + MemoryBuffer *tileAlbedo = (MemoryBuffer *)m_inputProgramAlbedo->initializeTileData(rect2); rcti rect; rect.xmin = 0; rect.ymin = 0; rect.xmax = getWidth(); rect.ymax = getHeight(); MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); - this->generateDenoise(result, tileColor, tileNormal, tileAlbedo, this->m_settings); + this->generateDenoise(result, tileColor, tileNormal, tileAlbedo, m_settings); return result; } diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h index 1b053b79c2d..e5b7ebbfeca 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.h +++ b/source/blender/compositor/operations/COM_DenoiseOperation.h @@ -68,7 +68,7 @@ class DenoiseOperation : public DenoiseBaseOperation { void setDenoiseSettings(NodeDenoise *settings) { - this->m_settings = settings; + m_settings = settings; } void update_memory_buffer(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc index 7c5f33d0f59..a317cdcc7f0 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.cc +++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc @@ -28,19 +28,19 @@ DespeckleOperation::DespeckleOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; this->flags.complex = true; } void DespeckleOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_inputValueOperation = this->getInputSocketReader(1); + m_inputOperation = this->getInputSocketReader(0); + m_inputValueOperation = this->getInputSocketReader(1); } void DespeckleOperation::deinitExecution() { - this->m_inputOperation = nullptr; - this->m_inputValueOperation = nullptr; + m_inputOperation = nullptr; + m_inputValueOperation = nullptr; } BLI_INLINE int color_diff(const float a[3], const float b[3], const float threshold) @@ -69,10 +69,10 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da CLAMP(y2, 0, getHeight() - 1); CLAMP(y3, 0, getHeight() - 1); float value[4]; - this->m_inputValueOperation->read(value, x2, y2, nullptr); + m_inputValueOperation->read(value, x2, y2, nullptr); // const float mval = 1.0f - value[0]; - this->m_inputOperation->read(color_org, x2, y2, nullptr); + m_inputOperation->read(color_org, x2, y2, nullptr); #define TOT_DIV_ONE 1.0f #define TOT_DIV_CNR (float)M_SQRT1_2 @@ -82,7 +82,7 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da #define COLOR_ADD(fac) \ { \ madd_v4_v4fl(color_mid, in1, fac); \ - if (color_diff(in1, color_org, this->m_threshold)) { \ + if (color_diff(in1, color_org, m_threshold)) { \ w += fac; \ madd_v4_v4fl(color_mid_ok, in1, fac); \ } \ @@ -91,34 +91,34 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da zero_v4(color_mid); zero_v4(color_mid_ok); - this->m_inputOperation->read(in1, x1, y1, nullptr); + m_inputOperation->read(in1, x1, y1, nullptr); COLOR_ADD(TOT_DIV_CNR) - this->m_inputOperation->read(in1, x2, y1, nullptr); + m_inputOperation->read(in1, x2, y1, nullptr); COLOR_ADD(TOT_DIV_ONE) - this->m_inputOperation->read(in1, x3, y1, nullptr); + m_inputOperation->read(in1, x3, y1, nullptr); COLOR_ADD(TOT_DIV_CNR) - this->m_inputOperation->read(in1, x1, y2, nullptr); + m_inputOperation->read(in1, x1, y2, nullptr); COLOR_ADD(TOT_DIV_ONE) #if 0 - this->m_inputOperation->read(in2, x2, y2, nullptr); - madd_v4_v4fl(color_mid, in2, this->m_filter[4]); + m_inputOperation->read(in2, x2, y2, nullptr); + madd_v4_v4fl(color_mid, in2, m_filter[4]); #endif - this->m_inputOperation->read(in1, x3, y2, nullptr); + m_inputOperation->read(in1, x3, y2, nullptr); COLOR_ADD(TOT_DIV_ONE) - this->m_inputOperation->read(in1, x1, y3, nullptr); + m_inputOperation->read(in1, x1, y3, nullptr); COLOR_ADD(TOT_DIV_CNR) - this->m_inputOperation->read(in1, x2, y3, nullptr); + m_inputOperation->read(in1, x2, y3, nullptr); COLOR_ADD(TOT_DIV_ONE) - this->m_inputOperation->read(in1, x3, y3, nullptr); + m_inputOperation->read(in1, x3, y3, nullptr); COLOR_ADD(TOT_DIV_CNR) mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2))); // mul_v4_fl(color_mid, 1.0f / w); - if ((w != 0.0f) && ((w / WTOT) > (this->m_threshold_neighbor)) && - color_diff(color_mid, color_org, this->m_threshold)) { + if ((w != 0.0f) && ((w / WTOT) > (m_threshold_neighbor)) && + color_diff(color_mid, color_org, m_threshold)) { mul_v4_fl(color_mid_ok, 1.0f / w); interp_v4_v4v4(output, color_org, color_mid_ok, value[0]); } @@ -137,8 +137,8 @@ bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input, rcti *output) { rcti newInput; - int addx = 2; //(this->m_filterWidth - 1) / 2 + 1; - int addy = 2; //(this->m_filterHeight - 1) / 2 + 1; + int addx = 2; //(m_filterWidth - 1) / 2 + 1; + int addy = 2; //(m_filterHeight - 1) / 2 + 1; newInput.xmax = input->xmax + addx; newInput.xmin = input->xmin - addx; newInput.ymax = input->ymax + addy; @@ -153,8 +153,8 @@ void DespeckleOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - const int add_x = 2; //(this->m_filterWidth - 1) / 2 + 1; - const int add_y = 2; //(this->m_filterHeight - 1) / 2 + 1; + const int add_x = 2; //(m_filterWidth - 1) / 2 + 1; + const int add_y = 2; //(m_filterHeight - 1) / 2 + 1; r_input_area.xmin = output_area.xmin - add_x; r_input_area.xmax = output_area.xmax + add_x; r_input_area.ymin = output_area.ymin - add_y; @@ -217,7 +217,7 @@ void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, #if 0 const float* in2 = image->get_elem(x2, y2); - madd_v4_v4fl(color_mid, in2, this->m_filter[4]); + madd_v4_v4fl(color_mid, in2, m_filter[4]); #endif in1 = image->get_elem(x3, y2); diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.h b/source/blender/compositor/operations/COM_DespeckleOperation.h index 70d6c2227f4..a77010f1853 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.h +++ b/source/blender/compositor/operations/COM_DespeckleOperation.h @@ -46,11 +46,11 @@ class DespeckleOperation : public MultiThreadedOperation { void setThreshold(float threshold) { - this->m_threshold = threshold; + m_threshold = threshold; } void setThresholdNeighbor(float threshold) { - this->m_threshold_neighbor = threshold; + m_threshold_neighbor = threshold; } void initExecution() override; diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc index eb92f0e9905..25004d947aa 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc @@ -26,20 +26,20 @@ DifferenceMatteOperation::DifferenceMatteOperation() addInputSocket(DataType::Color); addOutputSocket(DataType::Value); - this->m_inputImage1Program = nullptr; - this->m_inputImage2Program = nullptr; + m_inputImage1Program = nullptr; + m_inputImage2Program = nullptr; flags.can_be_constant = true; } void DifferenceMatteOperation::initExecution() { - this->m_inputImage1Program = this->getInputSocketReader(0); - this->m_inputImage2Program = this->getInputSocketReader(1); + m_inputImage1Program = this->getInputSocketReader(0); + m_inputImage2Program = this->getInputSocketReader(1); } void DifferenceMatteOperation::deinitExecution() { - this->m_inputImage1Program = nullptr; - this->m_inputImage2Program = nullptr; + m_inputImage1Program = nullptr; + m_inputImage2Program = nullptr; } void DifferenceMatteOperation::executePixelSampled(float output[4], @@ -50,13 +50,13 @@ void DifferenceMatteOperation::executePixelSampled(float output[4], float inColor1[4]; float inColor2[4]; - const float tolerance = this->m_settings->t1; - const float falloff = this->m_settings->t2; + const float tolerance = m_settings->t1; + const float falloff = m_settings->t2; float difference; float alpha; - this->m_inputImage1Program->readSampled(inColor1, x, y, sampler); - this->m_inputImage2Program->readSampled(inColor2, x, y, sampler); + m_inputImage1Program->readSampled(inColor1, x, y, sampler); + m_inputImage2Program->readSampled(inColor2, x, y, sampler); difference = (fabsf(inColor2[0] - inColor1[0]) + fabsf(inColor2[1] - inColor1[1]) + fabsf(inColor2[2] - inColor1[2])); diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h index 0a86535d946..f95c78a38de 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h @@ -48,7 +48,7 @@ class DifferenceMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma) { - this->m_settings = nodeChroma; + m_settings = nodeChroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index 6e4a4d4780e..60f6bd42144 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -27,58 +27,58 @@ DilateErodeThresholdOperation::DilateErodeThresholdOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); this->flags.complex = true; - this->m_inputProgram = nullptr; - this->m_inset = 0.0f; - this->m__switch = 0.5f; - this->m_distance = 0.0f; + m_inputProgram = nullptr; + m_inset = 0.0f; + m__switch = 0.5f; + m_distance = 0.0f; } void DilateErodeThresholdOperation::init_data() { - if (this->m_distance < 0.0f) { - this->m_scope = -this->m_distance + this->m_inset; + if (m_distance < 0.0f) { + m_scope = -m_distance + m_inset; } else { - if (this->m_inset * 2 > this->m_distance) { - this->m_scope = MAX2(this->m_inset * 2 - this->m_distance, this->m_distance); + if (m_inset * 2 > m_distance) { + m_scope = MAX2(m_inset * 2 - m_distance, m_distance); } else { - this->m_scope = this->m_distance; + m_scope = m_distance; } } - if (this->m_scope < 3) { - this->m_scope = 3; + if (m_scope < 3) { + m_scope = 3; } } void DilateErodeThresholdOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); } void *DilateErodeThresholdOperation::initializeTileData(rcti * /*rect*/) { - void *buffer = this->m_inputProgram->initializeTileData(nullptr); + void *buffer = m_inputProgram->initializeTileData(nullptr); return buffer; } void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, void *data) { float inputValue[4]; - const float sw = this->m__switch; - const float distance = this->m_distance; + const float sw = m__switch; + const float distance = m_distance; float pixelvalue; - const float rd = this->m_scope * this->m_scope; - const float inset = this->m_inset; + const float rd = m_scope * m_scope; + const float inset = m_inset; float mindist = rd * 2; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; float *buffer = inputBuffer->getBuffer(); const rcti &input_rect = inputBuffer->get_rect(); - const int minx = MAX2(x - this->m_scope, input_rect.xmin); - const int miny = MAX2(y - this->m_scope, input_rect.ymin); - const int maxx = MIN2(x + this->m_scope, input_rect.xmax); - const int maxy = MIN2(y + this->m_scope, input_rect.ymax); + const int minx = MAX2(x - m_scope, input_rect.xmin); + const int miny = MAX2(y - m_scope, input_rect.ymin); + const int maxx = MIN2(x + m_scope, input_rect.xmax); + const int maxy = MIN2(y + m_scope, input_rect.ymax); const int bufferWidth = inputBuffer->getWidth(); int offset; @@ -146,7 +146,7 @@ void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, void DilateErodeThresholdOperation::deinitExecution() { - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } bool DilateErodeThresholdOperation::determineDependingAreaOfInterest( @@ -154,10 +154,10 @@ bool DilateErodeThresholdOperation::determineDependingAreaOfInterest( { rcti newInput; - newInput.xmax = input->xmax + this->m_scope; - newInput.xmin = input->xmin - this->m_scope; - newInput.ymax = input->ymax + this->m_scope; - newInput.ymin = input->ymin - this->m_scope; + newInput.xmax = input->xmax + m_scope; + newInput.xmin = input->xmin - m_scope; + newInput.ymax = input->ymax + m_scope; + newInput.ymin = input->ymin - m_scope; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -273,43 +273,43 @@ DilateDistanceOperation::DilateDistanceOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_inputProgram = nullptr; - this->m_distance = 0.0f; + m_inputProgram = nullptr; + m_distance = 0.0f; flags.complex = true; flags.open_cl = true; } void DilateDistanceOperation::init_data() { - this->m_scope = this->m_distance; - if (this->m_scope < 3) { - this->m_scope = 3; + m_scope = m_distance; + if (m_scope < 3) { + m_scope = 3; } } void DilateDistanceOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); } void *DilateDistanceOperation::initializeTileData(rcti * /*rect*/) { - void *buffer = this->m_inputProgram->initializeTileData(nullptr); + void *buffer = m_inputProgram->initializeTileData(nullptr); return buffer; } void DilateDistanceOperation::executePixel(float output[4], int x, int y, void *data) { - const float distance = this->m_distance; + const float distance = m_distance; const float mindist = distance * distance; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; float *buffer = inputBuffer->getBuffer(); const rcti &input_rect = inputBuffer->get_rect(); - const int minx = MAX2(x - this->m_scope, input_rect.xmin); - const int miny = MAX2(y - this->m_scope, input_rect.ymin); - const int maxx = MIN2(x + this->m_scope, input_rect.xmax); - const int maxy = MIN2(y + this->m_scope, input_rect.ymax); + const int minx = MAX2(x - m_scope, input_rect.xmin); + const int miny = MAX2(y - m_scope, input_rect.ymin); + const int maxx = MIN2(x + m_scope, input_rect.xmax); + const int maxy = MIN2(y + m_scope, input_rect.ymax); const int bufferWidth = inputBuffer->getWidth(); int offset; @@ -332,7 +332,7 @@ void DilateDistanceOperation::executePixel(float output[4], int x, int y, void * void DilateDistanceOperation::deinitExecution() { - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } bool DilateDistanceOperation::determineDependingAreaOfInterest(rcti *input, @@ -341,10 +341,10 @@ bool DilateDistanceOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - newInput.xmax = input->xmax + this->m_scope; - newInput.xmin = input->xmin - this->m_scope; - newInput.ymax = input->ymax + this->m_scope; - newInput.ymin = input->ymin - this->m_scope; + newInput.xmax = input->xmax + m_scope; + newInput.xmin = input->xmin - m_scope; + newInput.ymax = input->ymax + m_scope; + newInput.ymin = input->ymin - m_scope; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -358,11 +358,11 @@ void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device, { cl_kernel dilateKernel = device->COM_clCreateKernel("dilateKernel", nullptr); - cl_int distanceSquared = this->m_distance * this->m_distance; - cl_int scope = this->m_scope; + cl_int distanceSquared = m_distance * m_distance; + cl_int scope = m_scope; device->COM_clAttachMemoryBufferToKernelParameter( - dilateKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram); + dilateKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); device->COM_clAttachOutputMemoryBufferToKernelParameter(dilateKernel, 1, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter(dilateKernel, 3, outputMemoryBuffer); clSetKernelArg(dilateKernel, 4, sizeof(cl_int), &scope); @@ -465,16 +465,16 @@ ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() void ErodeDistanceOperation::executePixel(float output[4], int x, int y, void *data) { - const float distance = this->m_distance; + const float distance = m_distance; const float mindist = distance * distance; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; float *buffer = inputBuffer->getBuffer(); const rcti &input_rect = inputBuffer->get_rect(); - const int minx = MAX2(x - this->m_scope, input_rect.xmin); - const int miny = MAX2(y - this->m_scope, input_rect.ymin); - const int maxx = MIN2(x + this->m_scope, input_rect.xmax); - const int maxy = MIN2(y + this->m_scope, input_rect.ymax); + const int minx = MAX2(x - m_scope, input_rect.xmin); + const int miny = MAX2(y - m_scope, input_rect.ymin); + const int maxx = MIN2(x + m_scope, input_rect.xmax); + const int maxy = MIN2(y + m_scope, input_rect.ymax); const int bufferWidth = inputBuffer->getWidth(); int offset; @@ -504,11 +504,11 @@ void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device, { cl_kernel erodeKernel = device->COM_clCreateKernel("erodeKernel", nullptr); - cl_int distanceSquared = this->m_distance * this->m_distance; - cl_int scope = this->m_scope; + cl_int distanceSquared = m_distance * m_distance; + cl_int scope = m_scope; device->COM_clAttachMemoryBufferToKernelParameter( - erodeKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram); + erodeKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); device->COM_clAttachOutputMemoryBufferToKernelParameter(erodeKernel, 1, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter(erodeKernel, 3, outputMemoryBuffer); clSetKernelArg(erodeKernel, 4, sizeof(cl_int), &scope); @@ -534,11 +534,11 @@ DilateStepOperation::DilateStepOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); this->flags.complex = true; - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } void DilateStepOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); } /* Small helper to pass data from initializeTileData to executePixel. */ @@ -563,13 +563,13 @@ static tile_info *create_cache(int xmin, int xmax, int ymin, int ymax) void *DilateStepOperation::initializeTileData(rcti *rect) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_inputProgram->initializeTileData(nullptr); + MemoryBuffer *tile = (MemoryBuffer *)m_inputProgram->initializeTileData(nullptr); int x, y, i; int width = tile->getWidth(); int height = tile->getHeight(); float *buffer = tile->getBuffer(); - int half_window = this->m_iterations; + int half_window = m_iterations; int window = half_window * 2 + 1; int xmin = MAX2(0, rect->xmin - half_window); @@ -661,7 +661,7 @@ void DilateStepOperation::executePixel(float output[4], int x, int y, void *data void DilateStepOperation::deinitExecution() { - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } void DilateStepOperation::deinitializeTileData(rcti * /*rect*/, void *data) @@ -676,7 +676,7 @@ bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, rcti *output) { rcti newInput; - int it = this->m_iterations; + int it = m_iterations; newInput.xmax = input->xmax + it; newInput.xmin = input->xmin - it; newInput.ymax = input->ymax + it; @@ -814,13 +814,13 @@ ErodeStepOperation::ErodeStepOperation() : DilateStepOperation() void *ErodeStepOperation::initializeTileData(rcti *rect) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_inputProgram->initializeTileData(nullptr); + MemoryBuffer *tile = (MemoryBuffer *)m_inputProgram->initializeTileData(nullptr); int x, y, i; int width = tile->getWidth(); int height = tile->getHeight(); float *buffer = tile->getBuffer(); - int half_window = this->m_iterations; + int half_window = m_iterations; int window = half_window * 2 + 1; int xmin = MAX2(0, rect->xmin - half_window); diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.h b/source/blender/compositor/operations/COM_DilateErodeOperation.h index 9c32a5ac1fd..fb22a72fb70 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.h +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.h @@ -64,15 +64,15 @@ class DilateErodeThresholdOperation : public MultiThreadedOperation { void setDistance(float distance) { - this->m_distance = distance; + m_distance = distance; } void setSwitch(float sw) { - this->m__switch = sw; + m__switch = sw; } void setInset(float inset) { - this->m_inset = inset; + m_inset = inset; } bool determineDependingAreaOfInterest(rcti *input, @@ -119,7 +119,7 @@ class DilateDistanceOperation : public MultiThreadedOperation { void setDistance(float distance) { - this->m_distance = distance; + m_distance = distance; } bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, @@ -190,7 +190,7 @@ class DilateStepOperation : public MultiThreadedOperation { void setIterations(int iterations) { - this->m_iterations = iterations; + m_iterations = iterations; } bool determineDependingAreaOfInterest(rcti *input, diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc index d1698e989d0..7a6f3d972a4 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc @@ -27,20 +27,20 @@ DirectionalBlurOperation::DirectionalBlurOperation() this->addOutputSocket(DataType::Color); flags.complex = true; flags.open_cl = true; - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } void DirectionalBlurOperation::initExecution() { - this->m_inputProgram = getInputSocketReader(0); + m_inputProgram = getInputSocketReader(0); QualityStepHelper::initExecution(COM_QH_INCREASE); - const float angle = this->m_data->angle; - const float zoom = this->m_data->zoom; - const float spin = this->m_data->spin; - const float iterations = this->m_data->iter; - const float distance = this->m_data->distance; - const float center_x = this->m_data->center_x; - const float center_y = this->m_data->center_y; + const float angle = m_data->angle; + const float zoom = m_data->zoom; + const float spin = m_data->spin; + const float iterations = m_data->iter; + const float distance = m_data->distance; + const float center_x = m_data->center_x; + const float center_y = m_data->center_y; const float width = getWidth(); const float height = getHeight(); @@ -49,45 +49,45 @@ void DirectionalBlurOperation::initExecution() float D; D = distance * sqrtf(width * width + height * height); - this->m_center_x_pix = center_x * width; - this->m_center_y_pix = center_y * height; + m_center_x_pix = center_x * width; + m_center_y_pix = center_y * height; - this->m_tx = itsc * D * cosf(a); - this->m_ty = -itsc * D * sinf(a); - this->m_sc = itsc * zoom; - this->m_rot = itsc * spin; + m_tx = itsc * D * cosf(a); + m_ty = -itsc * D * sinf(a); + m_sc = itsc * zoom; + m_rot = itsc * spin; } void DirectionalBlurOperation::executePixel(float output[4], int x, int y, void * /*data*/) { - const int iterations = pow(2.0f, this->m_data->iter); + const int iterations = pow(2.0f, m_data->iter); float col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float col2[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - this->m_inputProgram->readSampled(col2, x, y, PixelSampler::Bilinear); - float ltx = this->m_tx; - float lty = this->m_ty; - float lsc = this->m_sc; - float lrot = this->m_rot; + m_inputProgram->readSampled(col2, x, y, PixelSampler::Bilinear); + float ltx = m_tx; + float lty = m_ty; + float lsc = m_sc; + float lrot = m_rot; /* blur the image */ for (int i = 0; i < iterations; i++) { const float cs = cosf(lrot), ss = sinf(lrot); const float isc = 1.0f / (1.0f + lsc); - const float v = isc * (y - this->m_center_y_pix) + lty; - const float u = isc * (x - this->m_center_x_pix) + ltx; + const float v = isc * (y - m_center_y_pix) + lty; + const float u = isc * (x - m_center_x_pix) + ltx; - this->m_inputProgram->readSampled(col, - cs * u + ss * v + this->m_center_x_pix, - cs * v - ss * u + this->m_center_y_pix, - PixelSampler::Bilinear); + m_inputProgram->readSampled(col, + cs * u + ss * v + m_center_x_pix, + cs * v - ss * u + m_center_y_pix, + PixelSampler::Bilinear); add_v4_v4(col2, col); /* double transformations */ - ltx += this->m_tx; - lty += this->m_ty; - lrot += this->m_rot; - lsc += this->m_sc; + ltx += m_tx; + lty += m_ty; + lrot += m_rot; + lsc += m_sc; } mul_v4_v4fl(output, col2, 1.0f / (iterations + 1)); @@ -102,14 +102,14 @@ void DirectionalBlurOperation::executeOpenCL(OpenCLDevice *device, { cl_kernel directionalBlurKernel = device->COM_clCreateKernel("directionalBlurKernel", nullptr); - cl_int iterations = pow(2.0f, this->m_data->iter); - cl_float2 ltxy = {{this->m_tx, this->m_ty}}; - cl_float2 centerpix = {{this->m_center_x_pix, this->m_center_y_pix}}; - cl_float lsc = this->m_sc; - cl_float lrot = this->m_rot; + cl_int iterations = pow(2.0f, m_data->iter); + cl_float2 ltxy = {{m_tx, m_ty}}; + cl_float2 centerpix = {{m_center_x_pix, m_center_y_pix}}; + cl_float lsc = m_sc; + cl_float lrot = m_rot; device->COM_clAttachMemoryBufferToKernelParameter( - directionalBlurKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram); + directionalBlurKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); device->COM_clAttachOutputMemoryBufferToKernelParameter( directionalBlurKernel, 1, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter( @@ -125,7 +125,7 @@ void DirectionalBlurOperation::executeOpenCL(OpenCLDevice *device, void DirectionalBlurOperation::deinitExecution() { - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } bool DirectionalBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/, @@ -156,7 +156,7 @@ void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output Span inputs) { const MemoryBuffer *input = inputs[0]; - const int iterations = pow(2.0f, this->m_data->iter); + const int iterations = pow(2.0f, m_data->iter); for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { const int x = it.x; const int y = it.y; @@ -166,27 +166,27 @@ void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output /* Blur pixel. */ /* TODO(manzanilla): Many values used on iterations can be calculated beforehand. Create a * table on operation initialization. */ - float ltx = this->m_tx; - float lty = this->m_ty; - float lsc = this->m_sc; - float lrot = this->m_rot; + float ltx = m_tx; + float lty = m_ty; + float lsc = m_sc; + float lrot = m_rot; for (int i = 0; i < iterations; i++) { const float cs = cosf(lrot), ss = sinf(lrot); const float isc = 1.0f / (1.0f + lsc); - const float v = isc * (y - this->m_center_y_pix) + lty; - const float u = isc * (x - this->m_center_x_pix) + ltx; + const float v = isc * (y - m_center_y_pix) + lty; + const float u = isc * (x - m_center_x_pix) + ltx; float color[4]; input->read_elem_bilinear( - cs * u + ss * v + this->m_center_x_pix, cs * v - ss * u + this->m_center_y_pix, color); + cs * u + ss * v + m_center_x_pix, cs * v - ss * u + m_center_y_pix, color); add_v4_v4(color_accum, color); /* Double transformations. */ - ltx += this->m_tx; - lty += this->m_ty; - lrot += this->m_rot; - lsc += this->m_sc; + ltx += m_tx; + lty += m_ty; + lrot += m_rot; + lsc += m_sc; } mul_v4_v4fl(it.out, color_accum, 1.0f / (iterations + 1)); diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h index 9a982bf6481..20d4b41b326 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h @@ -56,7 +56,7 @@ class DirectionalBlurOperation : public MultiThreadedOperation, public QualitySt void setData(NodeDBlurData *data) { - this->m_data = data; + m_data = data; } void executeOpenCL(OpenCLDevice *device, diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc index e6149add5e9..dd4618ff827 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc @@ -29,12 +29,12 @@ DisplaceOperation::DisplaceOperation() this->addOutputSocket(DataType::Color); this->flags.complex = true; - this->m_inputColorProgram = nullptr; + m_inputColorProgram = nullptr; } void DisplaceOperation::initExecution() { - this->m_inputColorProgram = this->getInputSocketReader(0); + m_inputColorProgram = this->getInputSocketReader(0); NodeOperation *vector = this->getInputSocketReader(1); NodeOperation *scale_x = this->getInputSocketReader(2); NodeOperation *scale_y = this->getInputSocketReader(3); @@ -50,8 +50,8 @@ void DisplaceOperation::initExecution() }; } - this->m_width_x4 = this->getWidth() * 4; - this->m_height_x4 = this->getHeight() * 4; + m_width_x4 = this->getWidth() * 4; + m_height_x4 = this->getHeight() * 4; input_vector_width_ = vector->getWidth(); input_vector_height_ = vector->getHeight(); } @@ -66,11 +66,11 @@ void DisplaceOperation::executePixelSampled(float output[4], pixelTransform(xy, uv, deriv); if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) { - this->m_inputColorProgram->readSampled(output, uv[0], uv[1], PixelSampler::Bilinear); + m_inputColorProgram->readSampled(output, uv[0], uv[1], PixelSampler::Bilinear); } else { /* EWA filtering (without nearest it gets blurry with NO distortion) */ - this->m_inputColorProgram->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + m_inputColorProgram->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); } } @@ -153,7 +153,7 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r void DisplaceOperation::deinitExecution() { - this->m_inputColorProgram = nullptr; + m_inputColorProgram = nullptr; vector_read_fn_ = nullptr; scale_x_read_fn_ = nullptr; scale_y_read_fn_ = nullptr; diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc index 81c047ec9f9..fa7c4199fca 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc @@ -28,21 +28,21 @@ DisplaceSimpleOperation::DisplaceSimpleOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_inputColorProgram = nullptr; - this->m_inputVectorProgram = nullptr; - this->m_inputScaleXProgram = nullptr; - this->m_inputScaleYProgram = nullptr; + m_inputColorProgram = nullptr; + m_inputVectorProgram = nullptr; + m_inputScaleXProgram = nullptr; + m_inputScaleYProgram = nullptr; } void DisplaceSimpleOperation::initExecution() { - this->m_inputColorProgram = this->getInputSocketReader(0); - this->m_inputVectorProgram = this->getInputSocketReader(1); - this->m_inputScaleXProgram = this->getInputSocketReader(2); - this->m_inputScaleYProgram = this->getInputSocketReader(3); + m_inputColorProgram = this->getInputSocketReader(0); + m_inputVectorProgram = this->getInputSocketReader(1); + m_inputScaleXProgram = this->getInputSocketReader(2); + m_inputScaleYProgram = this->getInputSocketReader(3); - this->m_width_x4 = this->getWidth() * 4; - this->m_height_x4 = this->getHeight() * 4; + m_width_x4 = this->getWidth() * 4; + m_height_x4 = this->getHeight() * 4; } /* minimum distance (in pixels) a pixel has to be displaced @@ -60,17 +60,17 @@ void DisplaceSimpleOperation::executePixelSampled(float output[4], float p_dx, p_dy; /* main displacement in pixel space */ float u, v; - this->m_inputScaleXProgram->readSampled(inScale, x, y, sampler); + m_inputScaleXProgram->readSampled(inScale, x, y, sampler); float xs = inScale[0]; - this->m_inputScaleYProgram->readSampled(inScale, x, y, sampler); + m_inputScaleYProgram->readSampled(inScale, x, y, sampler); float ys = inScale[0]; /* clamp x and y displacement to triple image resolution - * to prevent hangs from huge values mistakenly plugged in eg. z buffers */ - CLAMP(xs, -this->m_width_x4, this->m_width_x4); - CLAMP(ys, -this->m_height_x4, this->m_height_x4); + CLAMP(xs, -m_width_x4, m_width_x4); + CLAMP(ys, -m_height_x4, m_height_x4); - this->m_inputVectorProgram->readSampled(inVector, x, y, sampler); + m_inputVectorProgram->readSampled(inVector, x, y, sampler); p_dx = inVector[0] * xs; p_dy = inVector[1] * ys; @@ -81,15 +81,15 @@ void DisplaceSimpleOperation::executePixelSampled(float output[4], CLAMP(u, 0.0f, this->getWidth() - 1.0f); CLAMP(v, 0.0f, this->getHeight() - 1.0f); - this->m_inputColorProgram->readSampled(output, u, v, sampler); + m_inputColorProgram->readSampled(output, u, v, sampler); } void DisplaceSimpleOperation::deinitExecution() { - this->m_inputColorProgram = nullptr; - this->m_inputVectorProgram = nullptr; - this->m_inputScaleXProgram = nullptr; - this->m_inputScaleYProgram = nullptr; + m_inputColorProgram = nullptr; + m_inputVectorProgram = nullptr; + m_inputScaleXProgram = nullptr; + m_inputScaleYProgram = nullptr; } bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input, diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc index ab53bf32bea..0439cb35ba8 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc @@ -26,21 +26,21 @@ DistanceRGBMatteOperation::DistanceRGBMatteOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Value); - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; + m_inputImageProgram = nullptr; + m_inputKeyProgram = nullptr; flags.can_be_constant = true; } void DistanceRGBMatteOperation::initExecution() { - this->m_inputImageProgram = this->getInputSocketReader(0); - this->m_inputKeyProgram = this->getInputSocketReader(1); + m_inputImageProgram = this->getInputSocketReader(0); + m_inputKeyProgram = this->getInputSocketReader(1); } void DistanceRGBMatteOperation::deinitExecution() { - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; + m_inputImageProgram = nullptr; + m_inputKeyProgram = nullptr; } float DistanceRGBMatteOperation::calculateDistance(const float key[4], const float image[4]) @@ -56,14 +56,14 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4], float inKey[4]; float inImage[4]; - const float tolerance = this->m_settings->t1; - const float falloff = this->m_settings->t2; + const float tolerance = m_settings->t1; + const float falloff = m_settings->t2; float distance; float alpha; - this->m_inputKeyProgram->readSampled(inKey, x, y, sampler); - this->m_inputImageProgram->readSampled(inImage, x, y, sampler); + m_inputKeyProgram->readSampled(inKey, x, y, sampler); + m_inputImageProgram->readSampled(inImage, x, y, sampler); distance = this->calculateDistance(inKey, inImage); @@ -102,8 +102,8 @@ void DistanceRGBMatteOperation::update_memory_buffer_partial(MemoryBuffer *outpu const float *in_key = it.in(1); float distance = this->calculateDistance(in_key, in_image); - const float tolerance = this->m_settings->t1; - const float falloff = this->m_settings->t2; + const float tolerance = m_settings->t1; + const float falloff = m_settings->t2; /* Store matte(alpha) value in [0] to go with * COM_SetAlphaMultiplyOperation and the Value output. diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h index ba6682214ae..0effad2accd 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h @@ -50,7 +50,7 @@ class DistanceRGBMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma) { - this->m_settings = nodeChroma; + m_settings = nodeChroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc index aa18ff1e827..86df465ca31 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.cc +++ b/source/blender/compositor/operations/COM_DotproductOperation.cc @@ -26,20 +26,20 @@ DotproductOperation::DotproductOperation() this->addInputSocket(DataType::Vector); this->addOutputSocket(DataType::Value); this->set_canvas_input_index(0); - this->m_input1Operation = nullptr; - this->m_input2Operation = nullptr; + m_input1Operation = nullptr; + m_input2Operation = nullptr; flags.can_be_constant = true; } void DotproductOperation::initExecution() { - this->m_input1Operation = this->getInputSocketReader(0); - this->m_input2Operation = this->getInputSocketReader(1); + m_input1Operation = this->getInputSocketReader(0); + m_input2Operation = this->getInputSocketReader(1); } void DotproductOperation::deinitExecution() { - this->m_input1Operation = nullptr; - this->m_input2Operation = nullptr; + m_input1Operation = nullptr; + m_input2Operation = nullptr; } /** \todo current implementation is the inverse of a dot-product. not 'logically' correct @@ -51,8 +51,8 @@ void DotproductOperation::executePixelSampled(float output[4], { float input1[4]; float input2[4]; - this->m_input1Operation->readSampled(input1, x, y, sampler); - this->m_input2Operation->readSampled(input2, x, y, sampler); + m_input1Operation->readSampled(input1, x, y, sampler); + m_input2Operation->readSampled(input2, x, y, sampler); output[0] = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]); } diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc index fa0201519f8..b83d7f343bd 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc @@ -1267,8 +1267,8 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float * * Each version has slightly different criteria for detecting an edge pixel. */ - if (this->m_adjacentOnly) { /* If "adjacent only" inner edge mode is turned on. */ - if (this->m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */ + if (m_adjacentOnly) { /* If "adjacent only" inner edge mode is turned on. */ + if (m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */ do_adjacentKeepBorders(t, rw, limask, lomask, lres, res, rsize); } else { /* "bleed out" buffer edge mode is turned on. */ @@ -1281,8 +1281,8 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float /* Detect edges in all non-border pixels in the buffer. */ do_adjacentEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz); } - else { /* "all" inner edge mode is turned on. */ - if (this->m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */ + else { /* "all" inner edge mode is turned on. */ + if (m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */ do_allKeepBorders(t, rw, limask, lomask, lres, res, rsize); } else { /* "bleed out" buffer edge mode is turned on. */ @@ -1322,10 +1322,10 @@ DoubleEdgeMaskOperation::DoubleEdgeMaskOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_inputInnerMask = nullptr; - this->m_inputOuterMask = nullptr; - this->m_adjacentOnly = false; - this->m_keepInside = false; + m_inputInnerMask = nullptr; + m_inputOuterMask = nullptr; + m_adjacentOnly = false; + m_keepInside = false; this->flags.complex = true; is_output_rendered_ = false; } @@ -1334,7 +1334,7 @@ bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) { - if (this->m_cachedInstance == nullptr) { + if (m_cachedInstance == nullptr) { rcti newInput; newInput.xmax = this->getWidth(); newInput.xmin = 0; @@ -1348,31 +1348,31 @@ bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/, void DoubleEdgeMaskOperation::initExecution() { - this->m_inputInnerMask = this->getInputSocketReader(0); - this->m_inputOuterMask = this->getInputSocketReader(1); + m_inputInnerMask = this->getInputSocketReader(0); + m_inputOuterMask = this->getInputSocketReader(1); initMutex(); - this->m_cachedInstance = nullptr; + m_cachedInstance = nullptr; } void *DoubleEdgeMaskOperation::initializeTileData(rcti *rect) { - if (this->m_cachedInstance) { - return this->m_cachedInstance; + if (m_cachedInstance) { + return m_cachedInstance; } lockMutex(); - if (this->m_cachedInstance == nullptr) { - MemoryBuffer *innerMask = (MemoryBuffer *)this->m_inputInnerMask->initializeTileData(rect); - MemoryBuffer *outerMask = (MemoryBuffer *)this->m_inputOuterMask->initializeTileData(rect); + if (m_cachedInstance == nullptr) { + MemoryBuffer *innerMask = (MemoryBuffer *)m_inputInnerMask->initializeTileData(rect); + MemoryBuffer *outerMask = (MemoryBuffer *)m_inputOuterMask->initializeTileData(rect); float *data = (float *)MEM_mallocN(sizeof(float) * this->getWidth() * this->getHeight(), __func__); float *imask = innerMask->getBuffer(); float *omask = outerMask->getBuffer(); doDoubleEdgeMask(imask, omask, data); - this->m_cachedInstance = data; + m_cachedInstance = data; } unlockMutex(); - return this->m_cachedInstance; + return m_cachedInstance; } void DoubleEdgeMaskOperation::executePixel(float output[4], int x, int y, void *data) { @@ -1383,12 +1383,12 @@ void DoubleEdgeMaskOperation::executePixel(float output[4], int x, int y, void * void DoubleEdgeMaskOperation::deinitExecution() { - this->m_inputInnerMask = nullptr; - this->m_inputOuterMask = nullptr; + m_inputInnerMask = nullptr; + m_inputOuterMask = nullptr; deinitMutex(); - if (this->m_cachedInstance) { - MEM_freeN(this->m_cachedInstance); - this->m_cachedInstance = nullptr; + if (m_cachedInstance) { + MEM_freeN(m_cachedInstance); + m_cachedInstance = nullptr; } } diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h index 45a80bbbbf0..091310dc9e3 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h @@ -64,11 +64,11 @@ class DoubleEdgeMaskOperation : public NodeOperation { void setAdjecentOnly(bool adjacentOnly) { - this->m_adjacentOnly = adjacentOnly; + m_adjacentOnly = adjacentOnly; } void setKeepInside(bool keepInside) { - this->m_keepInside = keepInside; + m_keepInside = keepInside; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc index a32f15a1b8f..e3649fc9013 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc @@ -25,19 +25,19 @@ EllipseMaskOperation::EllipseMaskOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_inputMask = nullptr; - this->m_inputValue = nullptr; - this->m_cosine = 0.0f; - this->m_sine = 0.0f; + m_inputMask = nullptr; + m_inputValue = nullptr; + m_cosine = 0.0f; + m_sine = 0.0f; } void EllipseMaskOperation::initExecution() { - this->m_inputMask = this->getInputSocketReader(0); - this->m_inputValue = this->getInputSocketReader(1); - const double rad = (double)this->m_data->rotation; - this->m_cosine = cos(rad); - this->m_sine = sin(rad); - this->m_aspectRatio = ((float)this->getWidth()) / this->getHeight(); + m_inputMask = this->getInputSocketReader(0); + m_inputValue = this->getInputSocketReader(1); + const double rad = (double)m_data->rotation; + m_cosine = cos(rad); + m_sine = sin(rad); + m_aspectRatio = ((float)this->getWidth()) / this->getHeight(); } void EllipseMaskOperation::executePixelSampled(float output[4], @@ -51,26 +51,26 @@ void EllipseMaskOperation::executePixelSampled(float output[4], float rx = x / this->getWidth(); float ry = y / this->getHeight(); - const float dy = (ry - this->m_data->y) / this->m_aspectRatio; - const float dx = rx - this->m_data->x; - rx = this->m_data->x + (this->m_cosine * dx + this->m_sine * dy); - ry = this->m_data->y + (-this->m_sine * dx + this->m_cosine * dy); + const float dy = (ry - m_data->y) / m_aspectRatio; + const float dx = rx - m_data->x; + rx = m_data->x + (m_cosine * dx + m_sine * dy); + ry = m_data->y + (-m_sine * dx + m_cosine * dy); - this->m_inputMask->readSampled(inputMask, x, y, sampler); - this->m_inputValue->readSampled(inputValue, x, y, sampler); + m_inputMask->readSampled(inputMask, x, y, sampler); + m_inputValue->readSampled(inputValue, x, y, sampler); - const float halfHeight = (this->m_data->height) / 2.0f; - const float halfWidth = this->m_data->width / 2.0f; - float sx = rx - this->m_data->x; + const float halfHeight = (m_data->height) / 2.0f; + const float halfWidth = m_data->width / 2.0f; + float sx = rx - m_data->x; sx *= sx; const float tx = halfWidth * halfWidth; - float sy = ry - this->m_data->y; + float sy = ry - m_data->y; sy *= sy; const float ty = halfHeight * halfHeight; bool inside = ((sx / tx) + (sy / ty)) < 1.0f; - switch (this->m_maskType) { + switch (m_maskType) { case CMP_NODE_MASKTYPE_ADD: if (inside) { output[0] = MAX2(inputMask[0], inputValue[0]); @@ -154,24 +154,24 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, const MemoryBuffer *input_value = inputs[1]; const float op_w = this->getWidth(); const float op_h = this->getHeight(); - const float half_w = this->m_data->width / 2.0f; - const float half_h = this->m_data->height / 2.0f; + const float half_w = m_data->width / 2.0f; + const float half_h = m_data->height / 2.0f; const float tx = half_w * half_w; const float ty = half_h * half_h; for (int y = area.ymin; y < area.ymax; y++) { const float op_ry = y / op_h; - const float dy = (op_ry - this->m_data->y) / m_aspectRatio; + const float dy = (op_ry - m_data->y) / m_aspectRatio; float *out = output->get_elem(area.xmin, y); const float *mask = input_mask->get_elem(area.xmin, y); const float *value = input_value->get_elem(area.xmin, y); for (int x = area.xmin; x < area.xmax; x++) { const float op_rx = x / op_w; - const float dx = op_rx - this->m_data->x; - const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy); - const float ry = this->m_data->y + (-m_sine * dx + m_cosine * dy); - float sx = rx - this->m_data->x; + const float dx = op_rx - m_data->x; + const float rx = m_data->x + (m_cosine * dx + m_sine * dy); + const float ry = m_data->y + (-m_sine * dx + m_cosine * dy); + float sx = rx - m_data->x; sx *= sx; - float sy = ry - this->m_data->y; + float sy = ry - m_data->y; sy *= sy; const bool inside = ((sx / tx) + (sy / ty)) < 1.0f; out[0] = mask_func(inside, mask, value); @@ -185,8 +185,8 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, void EllipseMaskOperation::deinitExecution() { - this->m_inputMask = nullptr; - this->m_inputValue = nullptr; + m_inputMask = nullptr; + m_inputValue = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.h b/source/blender/compositor/operations/COM_EllipseMaskOperation.h index fba3f979d26..b0bebb5b80d 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.h +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.h @@ -59,12 +59,12 @@ class EllipseMaskOperation : public MultiThreadedOperation { void setData(NodeEllipseMask *data) { - this->m_data = data; + m_data = data; } void setMaskType(int maskType) { - this->m_maskType = maskType; + m_maskType = maskType; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index 629a8f1a5cb..ea24a23522c 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -24,7 +24,7 @@ namespace blender::compositor { FastGaussianBlurOperation::FastGaussianBlurOperation() : BlurBaseOperation(DataType::Color) { - this->m_iirgaus = nullptr; + m_iirgaus = nullptr; } void FastGaussianBlurOperation::executePixel(float output[4], int x, int y, void *data) @@ -48,7 +48,7 @@ bool FastGaussianBlurOperation::determineDependingAreaOfInterest( return true; } - if (this->m_iirgaus) { + if (m_iirgaus) { return false; } @@ -63,8 +63,8 @@ bool FastGaussianBlurOperation::determineDependingAreaOfInterest( void FastGaussianBlurOperation::init_data() { BlurBaseOperation::init_data(); - this->m_sx = this->m_data.sizex * this->m_size / 2.0f; - this->m_sy = this->m_data.sizey * this->m_size / 2.0f; + m_sx = m_data.sizex * m_size / 2.0f; + m_sy = m_data.sizey * m_size / 2.0f; } void FastGaussianBlurOperation::initExecution() @@ -75,9 +75,9 @@ void FastGaussianBlurOperation::initExecution() void FastGaussianBlurOperation::deinitExecution() { - if (this->m_iirgaus) { - delete this->m_iirgaus; - this->m_iirgaus = nullptr; + if (m_iirgaus) { + delete m_iirgaus; + m_iirgaus = nullptr; } BlurBaseOperation::deinitMutex(); } @@ -85,36 +85,36 @@ void FastGaussianBlurOperation::deinitExecution() void *FastGaussianBlurOperation::initializeTileData(rcti *rect) { lockMutex(); - if (!this->m_iirgaus) { - MemoryBuffer *newBuf = (MemoryBuffer *)this->m_inputProgram->initializeTileData(rect); + if (!m_iirgaus) { + MemoryBuffer *newBuf = (MemoryBuffer *)m_inputProgram->initializeTileData(rect); MemoryBuffer *copy = new MemoryBuffer(*newBuf); updateSize(); int c; - this->m_sx = this->m_data.sizex * this->m_size / 2.0f; - this->m_sy = this->m_data.sizey * this->m_size / 2.0f; + m_sx = m_data.sizex * m_size / 2.0f; + m_sy = m_data.sizey * m_size / 2.0f; - if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) { + if ((m_sx == m_sy) && (m_sx > 0.0f)) { for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) { - IIR_gauss(copy, this->m_sx, c, 3); + IIR_gauss(copy, m_sx, c, 3); } } else { - if (this->m_sx > 0.0f) { + if (m_sx > 0.0f) { for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) { - IIR_gauss(copy, this->m_sx, c, 1); + IIR_gauss(copy, m_sx, c, 1); } } - if (this->m_sy > 0.0f) { + if (m_sy > 0.0f) { for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) { - IIR_gauss(copy, this->m_sy, c, 2); + IIR_gauss(copy, m_sy, c, 2); } } } - this->m_iirgaus = copy; + m_iirgaus = copy; } unlockMutex(); - return this->m_iirgaus; + return m_iirgaus; } void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, @@ -294,20 +294,20 @@ void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *outpu } image->copy_from(input, area); - if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) { + if ((m_sx == m_sy) && (m_sx > 0.0f)) { for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { - IIR_gauss(image, this->m_sx, c, 3); + IIR_gauss(image, m_sx, c, 3); } } else { - if (this->m_sx > 0.0f) { + if (m_sx > 0.0f) { for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { - IIR_gauss(image, this->m_sx, c, 1); + IIR_gauss(image, m_sx, c, 1); } } - if (this->m_sy > 0.0f) { + if (m_sy > 0.0f) { for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { - IIR_gauss(image, this->m_sy, c, 2); + IIR_gauss(image, m_sy, c, 2); } } } @@ -322,10 +322,10 @@ FastGaussianBlurValueOperation::FastGaussianBlurValueOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_iirgaus = nullptr; - this->m_inputprogram = nullptr; - this->m_sigma = 1.0f; - this->m_overlay = 0; + m_iirgaus = nullptr; + m_inputprogram = nullptr; + m_sigma = 1.0f; + m_overlay = 0; flags.complex = true; } @@ -340,7 +340,7 @@ bool FastGaussianBlurValueOperation::determineDependingAreaOfInterest( { rcti newInput; - if (this->m_iirgaus) { + if (m_iirgaus) { return false; } @@ -354,15 +354,15 @@ bool FastGaussianBlurValueOperation::determineDependingAreaOfInterest( void FastGaussianBlurValueOperation::initExecution() { - this->m_inputprogram = getInputSocketReader(0); + m_inputprogram = getInputSocketReader(0); initMutex(); } void FastGaussianBlurValueOperation::deinitExecution() { - if (this->m_iirgaus) { - delete this->m_iirgaus; - this->m_iirgaus = nullptr; + if (m_iirgaus) { + delete m_iirgaus; + m_iirgaus = nullptr; } deinitMutex(); } @@ -370,12 +370,12 @@ void FastGaussianBlurValueOperation::deinitExecution() void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) { lockMutex(); - if (!this->m_iirgaus) { - MemoryBuffer *newBuf = (MemoryBuffer *)this->m_inputprogram->initializeTileData(rect); + if (!m_iirgaus) { + MemoryBuffer *newBuf = (MemoryBuffer *)m_inputprogram->initializeTileData(rect); MemoryBuffer *copy = new MemoryBuffer(*newBuf); - FastGaussianBlurOperation::IIR_gauss(copy, this->m_sigma, 0, 3); + FastGaussianBlurOperation::IIR_gauss(copy, m_sigma, 0, 3); - if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) { + if (m_overlay == FAST_GAUSS_OVERLAY_MIN) { float *src = newBuf->getBuffer(); float *dst = copy->getBuffer(); for (int i = copy->getWidth() * copy->getHeight(); i != 0; @@ -385,7 +385,7 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) } } } - else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) { + else if (m_overlay == FAST_GAUSS_OVERLAY_MAX) { float *src = newBuf->getBuffer(); float *dst = copy->getBuffer(); for (int i = copy->getWidth() * copy->getHeight(); i != 0; @@ -396,10 +396,10 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) } } - this->m_iirgaus = copy; + m_iirgaus = copy; } unlockMutex(); - return this->m_iirgaus; + return m_iirgaus; } void FastGaussianBlurValueOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -427,12 +427,12 @@ void FastGaussianBlurValueOperation::update_memory_buffer_partial(MemoryBuffer * { MemoryBuffer *image = inputs[0]; BuffersIterator it = output->iterate_with({image, m_iirgaus}, area); - if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) { + if (m_overlay == FAST_GAUSS_OVERLAY_MIN) { for (; !it.is_end(); ++it) { *it.out = MIN2(*it.in(0), *it.in(1)); } } - else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) { + else if (m_overlay == FAST_GAUSS_OVERLAY_MAX) { for (; !it.is_end(); ++it) { *it.out = MAX2(*it.in(0), *it.in(1)); } diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h index f42fc76a119..b2ad199f17d 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h @@ -83,13 +83,13 @@ class FastGaussianBlurValueOperation : public MultiThreadedOperation { void initExecution() override; void setSigma(float sigma) { - this->m_sigma = sigma; + m_sigma = sigma; } /* used for DOF blurring ZBuffer */ void setOverlay(int overlay) { - this->m_overlay = overlay; + m_overlay = overlay; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc index 2d8865e41e0..c08dce2fc01 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.cc +++ b/source/blender/compositor/operations/COM_FlipOperation.cc @@ -25,26 +25,26 @@ FlipOperation::FlipOperation() this->addInputSocket(DataType::Color, ResizeMode::None); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - this->m_inputOperation = nullptr; - this->m_flipX = true; - this->m_flipY = false; + m_inputOperation = nullptr; + m_flipX = true; + m_flipY = false; } void FlipOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); + m_inputOperation = this->getInputSocketReader(0); } void FlipOperation::deinitExecution() { - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } void FlipOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { - float nx = this->m_flipX ? ((int)this->getWidth() - 1) - x : x; - float ny = this->m_flipY ? ((int)this->getHeight() - 1) - y : y; + float nx = m_flipX ? ((int)this->getWidth() - 1) - x : x; + float ny = m_flipY ? ((int)this->getHeight() - 1) - y : y; - this->m_inputOperation->readSampled(output, nx, ny, sampler); + m_inputOperation->readSampled(output, nx, ny, sampler); } bool FlipOperation::determineDependingAreaOfInterest(rcti *input, @@ -53,7 +53,7 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - if (this->m_flipX) { + if (m_flipX) { const int w = (int)this->getWidth() - 1; newInput.xmax = (w - input->xmin) + 1; newInput.xmin = (w - input->xmax) - 1; @@ -62,7 +62,7 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input, newInput.xmin = input->xmin; newInput.xmax = input->xmax; } - if (this->m_flipY) { + if (m_flipY) { const int h = (int)this->getHeight() - 1; newInput.ymax = (h - input->ymin) + 1; newInput.ymin = (h - input->ymax) - 1; @@ -99,7 +99,7 @@ void FlipOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - if (this->m_flipX) { + if (m_flipX) { const int w = (int)this->getWidth() - 1; r_input_area.xmax = (w - output_area.xmin) + 1; r_input_area.xmin = (w - output_area.xmax) + 1; @@ -108,7 +108,7 @@ void FlipOperation::get_area_of_interest(const int input_idx, r_input_area.xmin = output_area.xmin; r_input_area.xmax = output_area.xmax; } - if (this->m_flipY) { + if (m_flipY) { const int h = (int)this->getHeight() - 1; r_input_area.ymax = (h - output_area.ymin) + 1; r_input_area.ymin = (h - output_area.ymax) + 1; @@ -127,8 +127,8 @@ void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output, const int input_offset_x = input_img->get_rect().xmin; const int input_offset_y = input_img->get_rect().ymin; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x; - const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y; + const int nx = m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x; + const int ny = m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y; input_img->read_elem(input_offset_x + nx, input_offset_y + ny, it.out); } } diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h index 963996a5b0d..b43c3a33495 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.h +++ b/source/blender/compositor/operations/COM_FlipOperation.h @@ -39,11 +39,11 @@ class FlipOperation : public MultiThreadedOperation { void deinitExecution() override; void setFlipX(bool flipX) { - this->m_flipX = flipX; + m_flipX = flipX; } void setFlipY(bool flipY) { - this->m_flipY = flipY; + m_flipY = flipY; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc index 2d97acf4122..886ff9d09c2 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc @@ -24,12 +24,12 @@ GammaCorrectOperation::GammaCorrectOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; flags.can_be_constant = true; } void GammaCorrectOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); } void GammaCorrectOperation::executePixelSampled(float output[4], @@ -38,7 +38,7 @@ void GammaCorrectOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - this->m_inputProgram->readSampled(inputColor, x, y, sampler); + m_inputProgram->readSampled(inputColor, x, y, sampler); if (inputColor[3] > 0.0f) { inputColor[0] /= inputColor[3]; inputColor[1] /= inputColor[3]; @@ -88,19 +88,19 @@ void GammaCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, void GammaCorrectOperation::deinitExecution() { - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } GammaUncorrectOperation::GammaUncorrectOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; flags.can_be_constant = true; } void GammaUncorrectOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); } void GammaUncorrectOperation::executePixelSampled(float output[4], @@ -109,7 +109,7 @@ void GammaUncorrectOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - this->m_inputProgram->readSampled(inputColor, x, y, sampler); + m_inputProgram->readSampled(inputColor, x, y, sampler); if (inputColor[3] > 0.0f) { inputColor[0] /= inputColor[3]; @@ -158,7 +158,7 @@ void GammaUncorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, void GammaUncorrectOperation::deinitExecution() { - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GammaOperation.cc b/source/blender/compositor/operations/COM_GammaOperation.cc index fab7a8dfb78..21430a68848 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.cc +++ b/source/blender/compositor/operations/COM_GammaOperation.cc @@ -25,14 +25,14 @@ GammaOperation::GammaOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; - this->m_inputGammaProgram = nullptr; + m_inputProgram = nullptr; + m_inputGammaProgram = nullptr; flags.can_be_constant = true; } void GammaOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputGammaProgram = this->getInputSocketReader(1); + m_inputProgram = this->getInputSocketReader(0); + m_inputGammaProgram = this->getInputSocketReader(1); } void GammaOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -40,8 +40,8 @@ void GammaOperation::executePixelSampled(float output[4], float x, float y, Pixe float inputValue[4]; float inputGamma[4]; - this->m_inputProgram->readSampled(inputValue, x, y, sampler); - this->m_inputGammaProgram->readSampled(inputGamma, x, y, sampler); + m_inputProgram->readSampled(inputValue, x, y, sampler); + m_inputGammaProgram->readSampled(inputGamma, x, y, sampler); const float gamma = inputGamma[0]; /* check for negative to avoid nan's */ output[0] = inputValue[0] > 0.0f ? powf(inputValue[0], gamma) : inputValue[0]; @@ -67,8 +67,8 @@ void GammaOperation::update_memory_buffer_row(PixelCursor &p) void GammaOperation::deinitExecution() { - this->m_inputProgram = nullptr; - this->m_inputGammaProgram = nullptr; + m_inputProgram = nullptr; + m_inputGammaProgram = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc index 9bdc652b466..20459a8b038 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc @@ -23,9 +23,9 @@ namespace blender::compositor { GaussianAlphaBlurBaseOperation::GaussianAlphaBlurBaseOperation(eDimension dim) : BlurBaseOperation(DataType::Value) { - this->m_gausstab = nullptr; - this->m_filtersize = 0; - this->m_falloff = -1; /* Intentionally invalid, so we can detect uninitialized values. */ + m_gausstab = nullptr; + m_filtersize = 0; + m_falloff = -1; /* Intentionally invalid, so we can detect uninitialized values. */ dimension_ = dim; } @@ -52,14 +52,14 @@ void GaussianAlphaBlurBaseOperation::deinitExecution() { BlurBaseOperation::deinitExecution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (m_gausstab) { + MEM_freeN(m_gausstab); + m_gausstab = nullptr; } - if (this->m_distbuf_inv) { - MEM_freeN(this->m_distbuf_inv); - this->m_distbuf_inv = nullptr; + if (m_distbuf_inv) { + MEM_freeN(m_distbuf_inv); + m_distbuf_inv = nullptr; } } diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h index d7ca975ca0a..bb03b672a34 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h @@ -51,11 +51,11 @@ class GaussianAlphaBlurBaseOperation : public BlurBaseOperation { */ void setSubtract(bool subtract) { - this->m_do_subtract = subtract; + m_do_subtract = subtract; } void setFalloff(int falloff) { - this->m_falloff = falloff; + m_falloff = falloff; } }; diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc index f53a1da598c..4900c12f13a 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc @@ -28,7 +28,7 @@ GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!this->m_sizeavailable) { + if (!m_sizeavailable) { updateGauss(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -42,7 +42,7 @@ void GaussianAlphaXBlurOperation::initExecution() initMutex(); - if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { + if (m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(m_size * m_data.sizex, 0.0f); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -53,7 +53,7 @@ void GaussianAlphaXBlurOperation::initExecution() void GaussianAlphaXBlurOperation::updateGauss() { - if (this->m_gausstab == nullptr) { + if (m_gausstab == nullptr) { updateSize(); float rad = max_ff(m_size * m_data.sizex, 0.0f); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -61,7 +61,7 @@ void GaussianAlphaXBlurOperation::updateGauss() m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); } - if (this->m_distbuf_inv == nullptr) { + if (m_distbuf_inv == nullptr) { updateSize(); float rad = max_ff(m_size * m_data.sizex, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); @@ -78,7 +78,7 @@ BLI_INLINE float finv_test(const float f, const bool test) void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, void *data) { - const bool do_invert = this->m_do_subtract; + const bool do_invert = m_do_subtract; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; float *buffer = inputBuffer->getBuffer(); int bufferwidth = inputBuffer->getWidth(); @@ -106,20 +106,20 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo float distfacinv_max = 1.0f; /* 0 to 1 */ for (int nx = xmin; nx < xmax; nx += step) { - const int index = (nx - x) + this->m_filtersize; + const int index = (nx - x) + m_filtersize; float value = finv_test(buffer[bufferindex], do_invert); float multiplier; /* gauss */ { - multiplier = this->m_gausstab[index]; + multiplier = m_gausstab[index]; alpha_accum += value * multiplier; multiplier_accum += multiplier; } /* dilate - find most extreme color */ if (value > value_max) { - multiplier = this->m_distbuf_inv[index]; + multiplier = m_distbuf_inv[index]; value *= multiplier; if (value > value_max) { value_max = value; @@ -139,14 +139,14 @@ void GaussianAlphaXBlurOperation::deinitExecution() { GaussianAlphaBlurBaseOperation::deinitExecution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (m_gausstab) { + MEM_freeN(m_gausstab); + m_gausstab = nullptr; } - if (this->m_distbuf_inv) { - MEM_freeN(this->m_distbuf_inv); - this->m_distbuf_inv = nullptr; + if (m_distbuf_inv) { + MEM_freeN(m_distbuf_inv); + m_distbuf_inv = nullptr; } deinitMutex(); @@ -170,9 +170,9 @@ bool GaussianAlphaXBlurOperation::determineDependingAreaOfInterest( else #endif { - if (this->m_sizeavailable && this->m_gausstab != nullptr) { - newInput.xmax = input->xmax + this->m_filtersize + 1; - newInput.xmin = input->xmin - this->m_filtersize - 1; + if (m_sizeavailable && m_gausstab != nullptr) { + newInput.xmax = input->xmax + m_filtersize + 1; + newInput.xmin = input->xmin - m_filtersize - 1; newInput.ymax = input->ymax; newInput.ymin = input->ymin; } diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc index b171ad428b1..49f5902ba9c 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc @@ -28,7 +28,7 @@ GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!this->m_sizeavailable) { + if (!m_sizeavailable) { updateGauss(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -43,7 +43,7 @@ void GaussianAlphaYBlurOperation::initExecution() initMutex(); - if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { + if (m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(m_size * m_data.sizey, 0.0f); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -55,7 +55,7 @@ void GaussianAlphaYBlurOperation::initExecution() /* TODO(manzanilla): to be removed with tiled implementation. */ void GaussianAlphaYBlurOperation::updateGauss() { - if (this->m_gausstab == nullptr) { + if (m_gausstab == nullptr) { updateSize(); float rad = max_ff(m_size * m_data.sizey, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); @@ -64,7 +64,7 @@ void GaussianAlphaYBlurOperation::updateGauss() m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); } - if (this->m_distbuf_inv == nullptr) { + if (m_distbuf_inv == nullptr) { updateSize(); float rad = max_ff(m_size * m_data.sizey, 0.0f); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -80,7 +80,7 @@ BLI_INLINE float finv_test(const float f, const bool test) void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, void *data) { - const bool do_invert = this->m_do_subtract; + const bool do_invert = m_do_subtract; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; const rcti &input_rect = inputBuffer->get_rect(); float *buffer = inputBuffer->getBuffer(); @@ -108,20 +108,20 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo for (int ny = ymin; ny < ymax; ny += step) { int bufferindex = ((xmin - bufferstartx)) + ((ny - bufferstarty) * bufferwidth); - const int index = (ny - y) + this->m_filtersize; + const int index = (ny - y) + m_filtersize; float value = finv_test(buffer[bufferindex], do_invert); float multiplier; /* gauss */ { - multiplier = this->m_gausstab[index]; + multiplier = m_gausstab[index]; alpha_accum += value * multiplier; multiplier_accum += multiplier; } /* dilate - find most extreme color */ if (value > value_max) { - multiplier = this->m_distbuf_inv[index]; + multiplier = m_distbuf_inv[index]; value *= multiplier; if (value > value_max) { value_max = value; @@ -140,14 +140,14 @@ void GaussianAlphaYBlurOperation::deinitExecution() { GaussianAlphaBlurBaseOperation::deinitExecution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (m_gausstab) { + MEM_freeN(m_gausstab); + m_gausstab = nullptr; } - if (this->m_distbuf_inv) { - MEM_freeN(this->m_distbuf_inv); - this->m_distbuf_inv = nullptr; + if (m_distbuf_inv) { + MEM_freeN(m_distbuf_inv); + m_distbuf_inv = nullptr; } deinitMutex(); @@ -171,11 +171,11 @@ bool GaussianAlphaYBlurOperation::determineDependingAreaOfInterest( else #endif { - if (this->m_sizeavailable && this->m_gausstab != nullptr) { + if (m_sizeavailable && m_gausstab != nullptr) { newInput.xmax = input->xmax; newInput.xmin = input->xmin; - newInput.ymax = input->ymax + this->m_filtersize + 1; - newInput.ymin = input->ymin - this->m_filtersize - 1; + newInput.ymax = input->ymax + m_filtersize + 1; + newInput.ymin = input->ymin - m_filtersize - 1; } else { newInput.xmax = this->getWidth(); diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc index 3034d6c590f..670eb3196c6 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc @@ -24,13 +24,13 @@ namespace blender::compositor { GaussianBokehBlurOperation::GaussianBokehBlurOperation() : BlurBaseOperation(DataType::Color) { - this->m_gausstab = nullptr; + m_gausstab = nullptr; } void *GaussianBokehBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!this->m_sizeavailable) { + if (!m_sizeavailable) { updateGauss(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -44,19 +44,19 @@ void GaussianBokehBlurOperation::init_data() const float width = this->getWidth(); const float height = this->getHeight(); - if (!this->m_sizeavailable) { + if (!m_sizeavailable) { updateSize(); } - radxf_ = this->m_size * (float)this->m_data.sizex; + radxf_ = m_size * (float)m_data.sizex; CLAMP(radxf_, 0.0f, width / 2.0f); /* Vertical. */ - radyf_ = this->m_size * (float)this->m_data.sizey; + radyf_ = m_size * (float)m_data.sizey; CLAMP(radyf_, 0.0f, height / 2.0f); - this->m_radx = ceil(radxf_); - this->m_rady = ceil(radyf_); + m_radx = ceil(radxf_); + m_rady = ceil(radyf_); } void GaussianBokehBlurOperation::initExecution() @@ -65,16 +65,16 @@ void GaussianBokehBlurOperation::initExecution() initMutex(); - if (this->m_sizeavailable) { + if (m_sizeavailable) { updateGauss(); } } void GaussianBokehBlurOperation::updateGauss() { - if (this->m_gausstab == nullptr) { - int ddwidth = 2 * this->m_radx + 1; - int ddheight = 2 * this->m_rady + 1; + if (m_gausstab == nullptr) { + int ddwidth = 2 * m_radx + 1; + int ddheight = 2 * m_rady + 1; int n = ddwidth * ddheight; /* create a full filter image */ float *ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__); @@ -82,12 +82,12 @@ void GaussianBokehBlurOperation::updateGauss() float sum = 0.0f; float facx = (radxf_ > 0.0f ? 1.0f / radxf_ : 0.0f); float facy = (radyf_ > 0.0f ? 1.0f / radyf_ : 0.0f); - for (int j = -this->m_rady; j <= this->m_rady; j++) { - for (int i = -this->m_radx; i <= this->m_radx; i++, dgauss++) { + for (int j = -m_rady; j <= m_rady; j++) { + for (int i = -m_radx; i <= m_radx; i++, dgauss++) { float fj = (float)j * facy; float fi = (float)i * facx; float dist = sqrt(fj * fj + fi * fi); - *dgauss = RE_filter_value(this->m_data.filtertype, dist); + *dgauss = RE_filter_value(m_data.filtertype, dist); sum += *dgauss; } @@ -105,7 +105,7 @@ void GaussianBokehBlurOperation::updateGauss() ddgauss[center] = 1.0f; } - this->m_gausstab = ddgauss; + m_gausstab = ddgauss; } } @@ -124,21 +124,21 @@ void GaussianBokehBlurOperation::executePixel(float output[4], int x, int y, voi int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; - int ymin = max_ii(y - this->m_rady, input_rect.ymin); - int ymax = min_ii(y + this->m_rady + 1, input_rect.ymax); - int xmin = max_ii(x - this->m_radx, input_rect.xmin); - int xmax = min_ii(x + this->m_radx + 1, input_rect.xmax); + int ymin = max_ii(y - m_rady, input_rect.ymin); + int ymax = min_ii(y + m_rady + 1, input_rect.ymax); + int xmin = max_ii(x - m_radx, input_rect.xmin); + int xmax = min_ii(x + m_radx + 1, input_rect.xmax); int index; int step = QualityStepHelper::getStep(); int offsetadd = QualityStepHelper::getOffsetAdd(); - const int addConst = (xmin - x + this->m_radx); - const int mulConst = (this->m_radx * 2 + 1); + const int addConst = (xmin - x + m_radx); + const int mulConst = (m_radx * 2 + 1); for (int ny = ymin; ny < ymax; ny += step) { - index = ((ny - y) + this->m_rady) * mulConst + addConst; + index = ((ny - y) + m_rady) * mulConst + addConst; int bufferindex = ((xmin - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth); for (int nx = xmin; nx < xmax; nx += step) { - const float multiplier = this->m_gausstab[index]; + const float multiplier = m_gausstab[index]; madd_v4_v4fl(tempColor, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; index += step; @@ -153,9 +153,9 @@ void GaussianBokehBlurOperation::deinitExecution() { BlurBaseOperation::deinitExecution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (m_gausstab) { + MEM_freeN(m_gausstab); + m_gausstab = nullptr; } deinitMutex(); @@ -176,15 +176,15 @@ bool GaussianBokehBlurOperation::determineDependingAreaOfInterest( return true; } - if (this->m_sizeavailable && this->m_gausstab != nullptr) { + if (m_sizeavailable && m_gausstab != nullptr) { newInput.xmin = 0; newInput.ymin = 0; newInput.xmax = this->getWidth(); newInput.ymax = this->getHeight(); } else { - int addx = this->m_radx; - int addy = this->m_rady; + int addx = m_radx; + int addy = m_rady; newInput.xmax = input->xmax + addx; newInput.xmin = input->xmin - addx; newInput.ymax = input->ymax + addy; @@ -219,23 +219,23 @@ void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *outp const int x = it.x; const int y = it.y; - const int ymin = max_ii(y - this->m_rady, input_rect.ymin); - const int ymax = min_ii(y + this->m_rady + 1, input_rect.ymax); - const int xmin = max_ii(x - this->m_radx, input_rect.xmin); - const int xmax = min_ii(x + this->m_radx + 1, input_rect.xmax); + const int ymin = max_ii(y - m_rady, input_rect.ymin); + const int ymax = min_ii(y + m_rady + 1, input_rect.ymax); + const int xmin = max_ii(x - m_radx, input_rect.xmin); + const int xmax = min_ii(x + m_radx + 1, input_rect.xmax); float tempColor[4] = {0}; float multiplier_accum = 0; const int step = QualityStepHelper::getStep(); const int elem_step = step * input->elem_stride; - const int add_const = (xmin - x + this->m_radx); - const int mul_const = (this->m_radx * 2 + 1); + const int add_const = (xmin - x + m_radx); + const int mul_const = (m_radx * 2 + 1); for (int ny = ymin; ny < ymax; ny += step) { const float *color = input->get_elem(xmin, ny); - int gauss_index = ((ny - y) + this->m_rady) * mul_const + add_const; + int gauss_index = ((ny - y) + m_rady) * mul_const + add_const; const int gauss_end = gauss_index + (xmax - xmin); for (; gauss_index < gauss_end; gauss_index += step, color += elem_step) { - const float multiplier = this->m_gausstab[gauss_index]; + const float multiplier = m_gausstab[gauss_index]; madd_v4_v4fl(tempColor, color, multiplier); multiplier_accum += multiplier; } @@ -249,34 +249,34 @@ void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *outp GaussianBlurReferenceOperation::GaussianBlurReferenceOperation() : BlurBaseOperation(DataType::Color) { - this->m_maintabs = nullptr; + m_maintabs = nullptr; use_variable_size_ = true; } void GaussianBlurReferenceOperation::init_data() { /* Setup variables for gausstab and area of interest. */ - this->m_data.image_in_width = this->getWidth(); - this->m_data.image_in_height = this->getHeight(); - if (this->m_data.relative) { - switch (this->m_data.aspect) { + m_data.image_in_width = this->getWidth(); + m_data.image_in_height = this->getHeight(); + if (m_data.relative) { + switch (m_data.aspect) { case CMP_NODE_BLUR_ASPECT_NONE: - this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_width); - this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_height); + m_data.sizex = (int)(m_data.percentx * 0.01f * m_data.image_in_width); + m_data.sizey = (int)(m_data.percenty * 0.01f * m_data.image_in_height); break; case CMP_NODE_BLUR_ASPECT_Y: - this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_width); - this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_width); + m_data.sizex = (int)(m_data.percentx * 0.01f * m_data.image_in_width); + m_data.sizey = (int)(m_data.percenty * 0.01f * m_data.image_in_width); break; case CMP_NODE_BLUR_ASPECT_X: - this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_height); - this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_height); + m_data.sizex = (int)(m_data.percentx * 0.01f * m_data.image_in_height); + m_data.sizey = (int)(m_data.percenty * 0.01f * m_data.image_in_height); break; } } /* Horizontal. */ - m_filtersizex = (float)this->m_data.sizex; + m_filtersizex = (float)m_data.sizex; int imgx = getWidth() / 2; if (m_filtersizex > imgx) { m_filtersizex = imgx; @@ -287,7 +287,7 @@ void GaussianBlurReferenceOperation::init_data() m_radx = (float)m_filtersizex; /* Vertical. */ - m_filtersizey = (float)this->m_data.sizey; + m_filtersizey = (float)m_data.sizey; int imgy = getHeight() / 2; if (m_filtersizey > imgy) { m_filtersizey = imgy; @@ -334,7 +334,7 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, int imgx = getWidth(); int imgy = getHeight(); float tempSize[4]; - this->m_inputSize->read(tempSize, x, y, data); + m_inputSize->read(tempSize, x, y, data); float refSize = tempSize[0]; int refradx = (int)(refSize * m_radx); int refrady = (int)(refSize * m_rady); @@ -391,11 +391,11 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, void GaussianBlurReferenceOperation::deinitExecution() { int x, i; - x = MAX2(this->m_filtersizex, this->m_filtersizey); + x = MAX2(m_filtersizex, m_filtersizey); for (i = 0; i < x; i++) { - MEM_freeN(this->m_maintabs[i]); + MEM_freeN(m_maintabs[i]); } - MEM_freeN(this->m_maintabs); + MEM_freeN(m_maintabs); BlurBaseOperation::deinitExecution(); } @@ -409,8 +409,8 @@ bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest( return true; } - int addx = this->m_data.sizex + 2; - int addy = this->m_data.sizey + 2; + int addx = m_data.sizex + 2; + int addy = m_data.sizey + 2; newInput.xmax = input->xmax + addx; newInput.xmin = input->xmin - addx; newInput.ymax = input->ymax + addy; @@ -427,8 +427,8 @@ void GaussianBlurReferenceOperation::get_area_of_interest(const int input_idx, return; } - const int add_x = this->m_data.sizex + 2; - const int add_y = this->m_data.sizey + 2; + const int add_x = m_data.sizex + 2; + const int add_y = m_data.sizey + 2; r_input_area.xmax = output_area.xmax + add_x; r_input_area.xmin = output_area.xmin - add_x; r_input_area.ymax = output_area.ymax + add_y; diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc index 6f1839eee30..6208820e280 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc @@ -28,7 +28,7 @@ GaussianXBlurOperation::GaussianXBlurOperation() : GaussianBlurBaseOperation(eDi void *GaussianXBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!this->m_sizeavailable) { + if (!m_sizeavailable) { updateGauss(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -43,14 +43,14 @@ void GaussianXBlurOperation::initExecution() initMutex(); - if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { + if (m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(m_size * m_data.sizex, 0.0f); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); /* TODO(sergey): De-duplicate with the case below and Y blur. */ - this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); #ifdef BLI_HAVE_SSE2 - this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab, m_filtersize); + m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); #endif } } @@ -58,15 +58,15 @@ void GaussianXBlurOperation::initExecution() /* TODO(manzanilla): to be removed with tiled implementation. */ void GaussianXBlurOperation::updateGauss() { - if (this->m_gausstab == nullptr) { + if (m_gausstab == nullptr) { updateSize(); float rad = max_ff(m_size * m_data.sizex, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); #ifdef BLI_HAVE_SSE2 - this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab, m_filtersize); + m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); #endif } } @@ -92,19 +92,17 @@ void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *d #ifdef BLI_HAVE_SSE2 __m128 accum_r = _mm_load_ps(color_accum); - for (int nx = xmin, index = (xmin - x) + this->m_filtersize; nx < xmax; - nx += step, index += step) { + for (int nx = xmin, index = (xmin - x) + m_filtersize; nx < xmax; nx += step, index += step) { __m128 reg_a = _mm_load_ps(&buffer[bufferindex]); - reg_a = _mm_mul_ps(reg_a, this->m_gausstab_sse[index]); + reg_a = _mm_mul_ps(reg_a, m_gausstab_sse[index]); accum_r = _mm_add_ps(accum_r, reg_a); - multiplier_accum += this->m_gausstab[index]; + multiplier_accum += m_gausstab[index]; bufferindex += offsetadd; } _mm_store_ps(color_accum, accum_r); #else - for (int nx = xmin, index = (xmin - x) + this->m_filtersize; nx < xmax; - nx += step, index += step) { - const float multiplier = this->m_gausstab[index]; + for (int nx = xmin, index = (xmin - x) + m_filtersize; nx < xmax; nx += step, index += step) { + const float multiplier = m_gausstab[index]; madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; bufferindex += offsetadd; @@ -122,20 +120,16 @@ void GaussianXBlurOperation::executeOpenCL(OpenCLDevice *device, { cl_kernel gaussianXBlurOperationKernel = device->COM_clCreateKernel( "gaussianXBlurOperationKernel", nullptr); - cl_int filter_size = this->m_filtersize; + cl_int filter_size = m_filtersize; cl_mem gausstab = clCreateBuffer(device->getContext(), CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, - sizeof(float) * (this->m_filtersize * 2 + 1), - this->m_gausstab, + sizeof(float) * (m_filtersize * 2 + 1), + m_gausstab, nullptr); - device->COM_clAttachMemoryBufferToKernelParameter(gaussianXBlurOperationKernel, - 0, - 1, - clMemToCleanUp, - inputMemoryBuffers, - this->m_inputProgram); + device->COM_clAttachMemoryBufferToKernelParameter( + gaussianXBlurOperationKernel, 0, 1, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); device->COM_clAttachOutputMemoryBufferToKernelParameter( gaussianXBlurOperationKernel, 2, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter( @@ -153,14 +147,14 @@ void GaussianXBlurOperation::deinitExecution() { GaussianBlurBaseOperation::deinitExecution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (m_gausstab) { + MEM_freeN(m_gausstab); + m_gausstab = nullptr; } #ifdef BLI_HAVE_SSE2 - if (this->m_gausstab_sse) { - MEM_freeN(this->m_gausstab_sse); - this->m_gausstab_sse = nullptr; + if (m_gausstab_sse) { + MEM_freeN(m_gausstab_sse); + m_gausstab_sse = nullptr; } #endif @@ -173,7 +167,7 @@ bool GaussianXBlurOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - if (!this->m_sizeavailable) { + if (!m_sizeavailable) { rcti sizeInput; sizeInput.xmin = 0; sizeInput.ymin = 0; @@ -185,9 +179,9 @@ bool GaussianXBlurOperation::determineDependingAreaOfInterest(rcti *input, } } { - if (this->m_sizeavailable && this->m_gausstab != nullptr) { - newInput.xmax = input->xmax + this->m_filtersize + 1; - newInput.xmin = input->xmin - this->m_filtersize - 1; + if (m_sizeavailable && m_gausstab != nullptr) { + newInput.xmax = input->xmax + m_filtersize + 1; + newInput.xmin = input->xmin - m_filtersize - 1; newInput.ymax = input->ymax; newInput.ymin = input->ymin; } diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc index cc8c1fce617..8a95721992c 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc @@ -28,7 +28,7 @@ GaussianYBlurOperation::GaussianYBlurOperation() : GaussianBlurBaseOperation(eDi void *GaussianYBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!this->m_sizeavailable) { + if (!m_sizeavailable) { updateGauss(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -42,28 +42,28 @@ void GaussianYBlurOperation::initExecution() initMutex(); - if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { + if (m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(m_size * m_data.sizey, 0.0f); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); #ifdef BLI_HAVE_SSE2 - this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab, m_filtersize); + m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); #endif } } void GaussianYBlurOperation::updateGauss() { - if (this->m_gausstab == nullptr) { + if (m_gausstab == nullptr) { updateSize(); float rad = max_ff(m_size * m_data.sizey, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); #ifdef BLI_HAVE_SSE2 - this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab, m_filtersize); + m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); #endif } } @@ -90,20 +90,20 @@ void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *d #ifdef BLI_HAVE_SSE2 __m128 accum_r = _mm_load_ps(color_accum); for (int ny = ymin; ny < ymax; ny += step) { - index = (ny - y) + this->m_filtersize; + index = (ny - y) + m_filtersize; int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth); - const float multiplier = this->m_gausstab[index]; + const float multiplier = m_gausstab[index]; __m128 reg_a = _mm_load_ps(&buffer[bufferindex]); - reg_a = _mm_mul_ps(reg_a, this->m_gausstab_sse[index]); + reg_a = _mm_mul_ps(reg_a, m_gausstab_sse[index]); accum_r = _mm_add_ps(accum_r, reg_a); multiplier_accum += multiplier; } _mm_store_ps(color_accum, accum_r); #else for (int ny = ymin; ny < ymax; ny += step) { - index = (ny - y) + this->m_filtersize; + index = (ny - y) + m_filtersize; int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth); - const float multiplier = this->m_gausstab[index]; + const float multiplier = m_gausstab[index]; madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; } @@ -120,20 +120,16 @@ void GaussianYBlurOperation::executeOpenCL(OpenCLDevice *device, { cl_kernel gaussianYBlurOperationKernel = device->COM_clCreateKernel( "gaussianYBlurOperationKernel", nullptr); - cl_int filter_size = this->m_filtersize; + cl_int filter_size = m_filtersize; cl_mem gausstab = clCreateBuffer(device->getContext(), CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, - sizeof(float) * (this->m_filtersize * 2 + 1), - this->m_gausstab, + sizeof(float) * (m_filtersize * 2 + 1), + m_gausstab, nullptr); - device->COM_clAttachMemoryBufferToKernelParameter(gaussianYBlurOperationKernel, - 0, - 1, - clMemToCleanUp, - inputMemoryBuffers, - this->m_inputProgram); + device->COM_clAttachMemoryBufferToKernelParameter( + gaussianYBlurOperationKernel, 0, 1, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); device->COM_clAttachOutputMemoryBufferToKernelParameter( gaussianYBlurOperationKernel, 2, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter( @@ -151,14 +147,14 @@ void GaussianYBlurOperation::deinitExecution() { GaussianBlurBaseOperation::deinitExecution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (m_gausstab) { + MEM_freeN(m_gausstab); + m_gausstab = nullptr; } #ifdef BLI_HAVE_SSE2 - if (this->m_gausstab_sse) { - MEM_freeN(this->m_gausstab_sse); - this->m_gausstab_sse = nullptr; + if (m_gausstab_sse) { + MEM_freeN(m_gausstab_sse); + m_gausstab_sse = nullptr; } #endif @@ -183,11 +179,11 @@ bool GaussianYBlurOperation::determineDependingAreaOfInterest(rcti *input, } } { - if (this->m_sizeavailable && this->m_gausstab != nullptr) { + if (m_sizeavailable && m_gausstab != nullptr) { newInput.xmax = input->xmax; newInput.xmin = input->xmin; - newInput.ymax = input->ymax + this->m_filtersize + 1; - newInput.ymin = input->ymin - this->m_filtersize - 1; + newInput.ymax = input->ymax + m_filtersize + 1; + newInput.ymin = input->ymin - m_filtersize - 1; } else { newInput.xmax = this->getWidth(); diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cc b/source/blender/compositor/operations/COM_GlareBaseOperation.cc index 32283a12b27..6e05705163f 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cc @@ -24,25 +24,25 @@ GlareBaseOperation::GlareBaseOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_settings = nullptr; + m_settings = nullptr; flags.is_fullframe_operation = true; is_output_rendered_ = false; } void GlareBaseOperation::initExecution() { SingleThreadedOperation::initExecution(); - this->m_inputProgram = getInputSocketReader(0); + m_inputProgram = getInputSocketReader(0); } void GlareBaseOperation::deinitExecution() { - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; SingleThreadedOperation::deinitExecution(); } MemoryBuffer *GlareBaseOperation::createMemoryBuffer(rcti *rect2) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_inputProgram->initializeTileData(rect2); + MemoryBuffer *tile = (MemoryBuffer *)m_inputProgram->initializeTileData(rect2); rcti rect; rect.xmin = 0; rect.ymin = 0; @@ -50,7 +50,7 @@ MemoryBuffer *GlareBaseOperation::createMemoryBuffer(rcti *rect2) rect.ymax = getHeight(); MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); float *data = result->getBuffer(); - this->generateGlare(data, tile, this->m_settings); + this->generateGlare(data, tile, m_settings); return result; } diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h index 5ca240a9e66..1d0c080ab8d 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.h +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h @@ -64,7 +64,7 @@ class GlareBaseOperation : public SingleThreadedOperation { void setGlareSettings(NodeGlare *settings) { - this->m_settings = settings; + m_settings = settings; } bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc index 5b84b4087f4..22e5e987aad 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc @@ -26,21 +26,21 @@ GlareThresholdOperation::GlareThresholdOperation() { this->addInputSocket(DataType::Color, ResizeMode::FitAny); this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } void GlareThresholdOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { NodeOperation::determine_canvas(preferred_area, r_area); - const int width = BLI_rcti_size_x(&r_area) / (1 << this->m_settings->quality); - const int height = BLI_rcti_size_y(&r_area) / (1 << this->m_settings->quality); + const int width = BLI_rcti_size_x(&r_area) / (1 << m_settings->quality); + const int height = BLI_rcti_size_y(&r_area) / (1 << m_settings->quality); r_area.xmax = r_area.xmin + width; r_area.ymax = r_area.ymin + height; } void GlareThresholdOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); } void GlareThresholdOperation::executePixelSampled(float output[4], @@ -48,9 +48,9 @@ void GlareThresholdOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - const float threshold = this->m_settings->threshold; + const float threshold = m_settings->threshold; - this->m_inputProgram->readSampled(output, x, y, sampler); + m_inputProgram->readSampled(output, x, y, sampler); if (IMB_colormanagement_get_luminance(output) >= threshold) { output[0] -= threshold; output[1] -= threshold; @@ -67,14 +67,14 @@ void GlareThresholdOperation::executePixelSampled(float output[4], void GlareThresholdOperation::deinitExecution() { - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } void GlareThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float threshold = this->m_settings->threshold; + const float threshold = m_settings->threshold; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float *color = it.in(0); if (IMB_colormanagement_get_luminance(color) >= threshold) { diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h index 44f2d717c0e..1917f016f72 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h @@ -55,7 +55,7 @@ class GlareThresholdOperation : public MultiThreadedOperation { void setGlareSettings(NodeGlare *settings) { - this->m_settings = settings; + m_settings = settings; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc index d083133cb3e..54f07f6f4a6 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc @@ -29,12 +29,12 @@ HueSaturationValueCorrectOperation::HueSaturationValueCorrectOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } void HueSaturationValueCorrectOperation::initExecution() { CurveBaseOperation::initExecution(); - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); } void HueSaturationValueCorrectOperation::executePixelSampled(float output[4], @@ -44,18 +44,18 @@ void HueSaturationValueCorrectOperation::executePixelSampled(float output[4], { float hsv[4], f; - this->m_inputProgram->readSampled(hsv, x, y, sampler); + m_inputProgram->readSampled(hsv, x, y, sampler); /* adjust hue, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(this->m_curveMapping, 0, hsv[0]); + f = BKE_curvemapping_evaluateF(m_curveMapping, 0, hsv[0]); hsv[0] += f - 0.5f; /* adjust saturation, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(this->m_curveMapping, 1, hsv[0]); + f = BKE_curvemapping_evaluateF(m_curveMapping, 1, hsv[0]); hsv[1] *= (f * 2.0f); /* adjust value, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(this->m_curveMapping, 2, hsv[0]); + f = BKE_curvemapping_evaluateF(m_curveMapping, 2, hsv[0]); hsv[2] *= (f * 2.0f); hsv[0] = hsv[0] - floorf(hsv[0]); /* mod 1.0 */ @@ -70,7 +70,7 @@ void HueSaturationValueCorrectOperation::executePixelSampled(float output[4], void HueSaturationValueCorrectOperation::deinitExecution() { CurveBaseOperation::deinitExecution(); - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -82,15 +82,15 @@ void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuff copy_v4_v4(hsv, it.in(0)); /* Adjust hue, scaling returned default 0.5 up to 1. */ - float f = BKE_curvemapping_evaluateF(this->m_curveMapping, 0, hsv[0]); + float f = BKE_curvemapping_evaluateF(m_curveMapping, 0, hsv[0]); hsv[0] += f - 0.5f; /* Adjust saturation, scaling returned default 0.5 up to 1. */ - f = BKE_curvemapping_evaluateF(this->m_curveMapping, 1, hsv[0]); + f = BKE_curvemapping_evaluateF(m_curveMapping, 1, hsv[0]); hsv[1] *= (f * 2.0f); /* Adjust value, scaling returned default 0.5 up to 1. */ - f = BKE_curvemapping_evaluateF(this->m_curveMapping, 2, hsv[0]); + f = BKE_curvemapping_evaluateF(m_curveMapping, 2, hsv[0]); hsv[2] *= (f * 2.0f); hsv[0] = hsv[0] - floorf(hsv[0]); /* Mod 1.0. */ diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.cc b/source/blender/compositor/operations/COM_IDMaskOperation.cc index bb11ac8b1be..56bd88b0ddd 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.cc +++ b/source/blender/compositor/operations/COM_IDMaskOperation.cc @@ -40,7 +40,7 @@ void IDMaskOperation::executePixel(float output[4], int x, int y, void *data) const int buffer_width = input_buffer->getWidth(); float *buffer = input_buffer->getBuffer(); int buffer_index = (y * buffer_width + x); - output[0] = (roundf(buffer[buffer_index]) == this->m_objectIndex) ? 1.0f : 0.0f; + output[0] = (roundf(buffer[buffer_index]) == m_objectIndex) ? 1.0f : 0.0f; } void IDMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.h b/source/blender/compositor/operations/COM_IDMaskOperation.h index c2e13641b46..ed6d065216f 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.h +++ b/source/blender/compositor/operations/COM_IDMaskOperation.h @@ -34,7 +34,7 @@ class IDMaskOperation : public MultiThreadedOperation { void setObjectIndex(float objectIndex) { - this->m_objectIndex = objectIndex; + m_objectIndex = objectIndex; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ImageOperation.cc b/source/blender/compositor/operations/COM_ImageOperation.cc index 773b61bc225..6c4f860df1a 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cc +++ b/source/blender/compositor/operations/COM_ImageOperation.cc @@ -28,19 +28,19 @@ namespace blender::compositor { BaseImageOperation::BaseImageOperation() { - this->m_image = nullptr; - this->m_buffer = nullptr; - this->m_imageFloatBuffer = nullptr; - this->m_imageByteBuffer = nullptr; - this->m_imageUser = nullptr; - this->m_imagewidth = 0; - this->m_imageheight = 0; - this->m_framenumber = 0; - this->m_depthBuffer = nullptr; + m_image = nullptr; + m_buffer = nullptr; + m_imageFloatBuffer = nullptr; + m_imageByteBuffer = nullptr; + m_imageUser = nullptr; + m_imagewidth = 0; + m_imageheight = 0; + m_framenumber = 0; + m_depthBuffer = nullptr; depth_buffer_ = nullptr; - this->m_numberOfChannels = 0; - this->m_rd = nullptr; - this->m_viewName = nullptr; + m_numberOfChannels = 0; + m_rd = nullptr; + m_viewName = nullptr; } ImageOperation::ImageOperation() : BaseImageOperation() { @@ -58,20 +58,20 @@ ImageDepthOperation::ImageDepthOperation() : BaseImageOperation() ImBuf *BaseImageOperation::getImBuf() { ImBuf *ibuf; - ImageUser iuser = *this->m_imageUser; + ImageUser iuser = *m_imageUser; - if (this->m_image == nullptr) { + if (m_image == nullptr) { return nullptr; } /* local changes to the original ImageUser */ - if (BKE_image_is_multilayer(this->m_image) == false) { - iuser.multi_index = BKE_scene_multiview_view_id_get(this->m_rd, this->m_viewName); + if (BKE_image_is_multilayer(m_image) == false) { + iuser.multi_index = BKE_scene_multiview_view_id_get(m_rd, m_viewName); } - ibuf = BKE_image_acquire_ibuf(this->m_image, &iuser, nullptr); + ibuf = BKE_image_acquire_ibuf(m_image, &iuser, nullptr); if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) { - BKE_image_release_ibuf(this->m_image, ibuf, nullptr); + BKE_image_release_ibuf(m_image, ibuf, nullptr); return nullptr; } return ibuf; @@ -80,25 +80,25 @@ ImBuf *BaseImageOperation::getImBuf() void BaseImageOperation::initExecution() { ImBuf *stackbuf = getImBuf(); - this->m_buffer = stackbuf; + m_buffer = stackbuf; if (stackbuf) { - this->m_imageFloatBuffer = stackbuf->rect_float; - this->m_imageByteBuffer = stackbuf->rect; - this->m_depthBuffer = stackbuf->zbuf_float; + m_imageFloatBuffer = stackbuf->rect_float; + m_imageByteBuffer = stackbuf->rect; + m_depthBuffer = stackbuf->zbuf_float; if (stackbuf->zbuf_float) { depth_buffer_ = new MemoryBuffer(stackbuf->zbuf_float, 1, stackbuf->x, stackbuf->y); } - this->m_imagewidth = stackbuf->x; - this->m_imageheight = stackbuf->y; - this->m_numberOfChannels = stackbuf->channels; + m_imagewidth = stackbuf->x; + m_imageheight = stackbuf->y; + m_numberOfChannels = stackbuf->channels; } } void BaseImageOperation::deinitExecution() { - this->m_imageFloatBuffer = nullptr; - this->m_imageByteBuffer = nullptr; - BKE_image_release_ibuf(this->m_image, this->m_buffer, nullptr); + m_imageFloatBuffer = nullptr; + m_imageByteBuffer = nullptr; + BKE_image_release_ibuf(m_image, m_buffer, nullptr); if (depth_buffer_) { delete depth_buffer_; depth_buffer_ = nullptr; @@ -115,7 +115,7 @@ void BaseImageOperation::determine_canvas(const rcti &UNUSED(preferred_area), rc BLI_rcti_init(&r_area, 0, stackbuf->x, 0, stackbuf->y); } - BKE_image_release_ibuf(this->m_image, stackbuf, nullptr); + BKE_image_release_ibuf(m_image, stackbuf, nullptr); } static void sampleImageAtLocation( @@ -157,14 +157,14 @@ static void sampleImageAtLocation( void ImageOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { int ix = x, iy = y; - if (this->m_imageFloatBuffer == nullptr && this->m_imageByteBuffer == nullptr) { + if (m_imageFloatBuffer == nullptr && m_imageByteBuffer == nullptr) { zero_v4(output); } - else if (ix < 0 || iy < 0 || ix >= this->m_buffer->x || iy >= this->m_buffer->y) { + else if (ix < 0 || iy < 0 || ix >= m_buffer->x || iy >= m_buffer->y) { zero_v4(output); } else { - sampleImageAtLocation(this->m_buffer, x, y, sampler, true, output); + sampleImageAtLocation(m_buffer, x, y, sampler, true, output); } } @@ -182,12 +182,12 @@ void ImageAlphaOperation::executePixelSampled(float output[4], { float tempcolor[4]; - if (this->m_imageFloatBuffer == nullptr && this->m_imageByteBuffer == nullptr) { + if (m_imageFloatBuffer == nullptr && m_imageByteBuffer == nullptr) { output[0] = 0.0f; } else { tempcolor[3] = 1.0f; - sampleImageAtLocation(this->m_buffer, x, y, sampler, false, tempcolor); + sampleImageAtLocation(m_buffer, x, y, sampler, false, tempcolor); output[0] = tempcolor[3]; } } @@ -204,7 +204,7 @@ void ImageDepthOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - if (this->m_depthBuffer == nullptr) { + if (m_depthBuffer == nullptr) { output[0] = 0.0f; } else { @@ -213,7 +213,7 @@ void ImageDepthOperation::executePixelSampled(float output[4], } else { int offset = y * getWidth() + x; - output[0] = this->m_depthBuffer[offset]; + output[0] = m_depthBuffer[offset]; } } } diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h index e2fdfbf6f81..b82b89290ad 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.h +++ b/source/blender/compositor/operations/COM_ImageOperation.h @@ -63,23 +63,23 @@ class BaseImageOperation : public MultiThreadedOperation { void deinitExecution() override; void setImage(Image *image) { - this->m_image = image; + m_image = image; } void setImageUser(ImageUser *imageuser) { - this->m_imageUser = imageuser; + m_imageUser = imageuser; } void setRenderData(const RenderData *rd) { - this->m_rd = rd; + m_rd = rd; } void setViewName(const char *viewName) { - this->m_viewName = viewName; + m_viewName = viewName; } void setFramenumber(int framenumber) { - this->m_framenumber = framenumber; + m_framenumber = framenumber; } }; class ImageOperation : public BaseImageOperation { diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc index 163699caa14..1b7b42706bc 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cc +++ b/source/blender/compositor/operations/COM_InpaintOperation.cc @@ -31,21 +31,21 @@ InpaintSimpleOperation::InpaintSimpleOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); this->flags.complex = true; - this->m_inputImageProgram = nullptr; - this->m_pixelorder = nullptr; - this->m_manhattan_distance = nullptr; - this->m_cached_buffer = nullptr; - this->m_cached_buffer_ready = false; + m_inputImageProgram = nullptr; + m_pixelorder = nullptr; + m_manhattan_distance = nullptr; + m_cached_buffer = nullptr; + m_cached_buffer_ready = false; flags.is_fullframe_operation = true; } void InpaintSimpleOperation::initExecution() { - this->m_inputImageProgram = this->getInputSocketReader(0); + m_inputImageProgram = this->getInputSocketReader(0); - this->m_pixelorder = nullptr; - this->m_manhattan_distance = nullptr; - this->m_cached_buffer = nullptr; - this->m_cached_buffer_ready = false; + m_pixelorder = nullptr; + m_manhattan_distance = nullptr; + m_cached_buffer = nullptr; + m_cached_buffer_ready = false; this->initMutex(); } @@ -76,8 +76,8 @@ float *InpaintSimpleOperation::get_pixel(int x, int y) ASSERT_XY_RANGE(x, y); - return &this->m_cached_buffer[y * width * COM_DATA_TYPE_COLOR_CHANNELS + - x * COM_DATA_TYPE_COLOR_CHANNELS]; + return &m_cached_buffer[y * width * COM_DATA_TYPE_COLOR_CHANNELS + + x * COM_DATA_TYPE_COLOR_CHANNELS]; } int InpaintSimpleOperation::mdist(int x, int y) @@ -86,18 +86,18 @@ int InpaintSimpleOperation::mdist(int x, int y) ASSERT_XY_RANGE(x, y); - return this->m_manhattan_distance[y * width + x]; + return m_manhattan_distance[y * width + x]; } bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) { int width = this->getWidth(); - if (curr >= this->m_area_size) { + if (curr >= m_area_size) { return false; } - int r = this->m_pixelorder[curr++]; + int r = m_pixelorder[curr++]; x = r % width; y = r / width; @@ -113,8 +113,7 @@ void InpaintSimpleOperation::calc_manhattan_distance() { int width = this->getWidth(); int height = this->getHeight(); - short *m = this->m_manhattan_distance = (short *)MEM_mallocN(sizeof(short) * width * height, - __func__); + short *m = m_manhattan_distance = (short *)MEM_mallocN(sizeof(short) * width * height, __func__); int *offsets; offsets = (int *)MEM_callocN(sizeof(int) * (width + height + 1), @@ -160,12 +159,12 @@ void InpaintSimpleOperation::calc_manhattan_distance() offsets[i] += offsets[i - 1]; } - this->m_area_size = offsets[width + height]; - this->m_pixelorder = (int *)MEM_mallocN(sizeof(int) * this->m_area_size, __func__); + m_area_size = offsets[width + height]; + m_pixelorder = (int *)MEM_mallocN(sizeof(int) * m_area_size, __func__); for (int i = 0; i < width * height; i++) { if (m[i] > 0) { - this->m_pixelorder[offsets[m[i] - 1]++] = i; + m_pixelorder[offsets[m[i] - 1]++] = i; } } @@ -216,27 +215,27 @@ void InpaintSimpleOperation::pix_step(int x, int y) void *InpaintSimpleOperation::initializeTileData(rcti *rect) { - if (this->m_cached_buffer_ready) { - return this->m_cached_buffer; + if (m_cached_buffer_ready) { + return m_cached_buffer; } lockMutex(); - if (!this->m_cached_buffer_ready) { - MemoryBuffer *buf = (MemoryBuffer *)this->m_inputImageProgram->initializeTileData(rect); - this->m_cached_buffer = (float *)MEM_dupallocN(buf->getBuffer()); + if (!m_cached_buffer_ready) { + MemoryBuffer *buf = (MemoryBuffer *)m_inputImageProgram->initializeTileData(rect); + m_cached_buffer = (float *)MEM_dupallocN(buf->getBuffer()); this->calc_manhattan_distance(); int curr = 0; int x, y; - while (this->next_pixel(x, y, curr, this->m_iterations)) { + while (this->next_pixel(x, y, curr, m_iterations)) { this->pix_step(x, y); } - this->m_cached_buffer_ready = true; + m_cached_buffer_ready = true; } unlockMutex(); - return this->m_cached_buffer; + return m_cached_buffer; } void InpaintSimpleOperation::executePixel(float output[4], int x, int y, void * /*data*/) @@ -247,30 +246,30 @@ void InpaintSimpleOperation::executePixel(float output[4], int x, int y, void * void InpaintSimpleOperation::deinitExecution() { - this->m_inputImageProgram = nullptr; + m_inputImageProgram = nullptr; this->deinitMutex(); - if (this->m_cached_buffer) { - MEM_freeN(this->m_cached_buffer); - this->m_cached_buffer = nullptr; + if (m_cached_buffer) { + MEM_freeN(m_cached_buffer); + m_cached_buffer = nullptr; } - if (this->m_pixelorder) { - MEM_freeN(this->m_pixelorder); - this->m_pixelorder = nullptr; + if (m_pixelorder) { + MEM_freeN(m_pixelorder); + m_pixelorder = nullptr; } - if (this->m_manhattan_distance) { - MEM_freeN(this->m_manhattan_distance); - this->m_manhattan_distance = nullptr; + if (m_manhattan_distance) { + MEM_freeN(m_manhattan_distance); + m_manhattan_distance = nullptr; } - this->m_cached_buffer_ready = false; + m_cached_buffer_ready = false; } bool InpaintSimpleOperation::determineDependingAreaOfInterest(rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) { - if (this->m_cached_buffer_ready) { + if (m_cached_buffer_ready) { return false; } @@ -313,7 +312,7 @@ void InpaintSimpleOperation::update_memory_buffer(MemoryBuffer *output, int curr = 0; int x, y; - while (this->next_pixel(x, y, curr, this->m_iterations)) { + while (this->next_pixel(x, y, curr, m_iterations)) { this->pix_step(x, y); } m_cached_buffer_ready = true; diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h index e11610bd263..87839dcb802 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.h +++ b/source/blender/compositor/operations/COM_InpaintOperation.h @@ -59,7 +59,7 @@ class InpaintSimpleOperation : public NodeOperation { void setIterations(int iterations) { - this->m_iterations = iterations; + m_iterations = iterations; } bool determineDependingAreaOfInterest(rcti *input, diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc index d406b809c7a..f9f08a1d377 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.cc +++ b/source/blender/compositor/operations/COM_InvertOperation.cc @@ -25,30 +25,30 @@ InvertOperation::InvertOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_inputValueProgram = nullptr; - this->m_inputColorProgram = nullptr; - this->m_color = true; - this->m_alpha = false; + m_inputValueProgram = nullptr; + m_inputColorProgram = nullptr; + m_color = true; + m_alpha = false; set_canvas_input_index(1); this->flags.can_be_constant = true; } void InvertOperation::initExecution() { - this->m_inputValueProgram = this->getInputSocketReader(0); - this->m_inputColorProgram = this->getInputSocketReader(1); + m_inputValueProgram = this->getInputSocketReader(0); + m_inputColorProgram = this->getInputSocketReader(1); } void InvertOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { float inputValue[4]; float inputColor[4]; - this->m_inputValueProgram->readSampled(inputValue, x, y, sampler); - this->m_inputColorProgram->readSampled(inputColor, x, y, sampler); + m_inputValueProgram->readSampled(inputValue, x, y, sampler); + m_inputColorProgram->readSampled(inputColor, x, y, sampler); const float value = inputValue[0]; const float invertedValue = 1.0f - value; - if (this->m_color) { + if (m_color) { output[0] = (1.0f - inputColor[0]) * value + inputColor[0] * invertedValue; output[1] = (1.0f - inputColor[1]) * value + inputColor[1] * invertedValue; output[2] = (1.0f - inputColor[2]) * value + inputColor[2] * invertedValue; @@ -57,7 +57,7 @@ void InvertOperation::executePixelSampled(float output[4], float x, float y, Pix copy_v3_v3(output, inputColor); } - if (this->m_alpha) { + if (m_alpha) { output[3] = (1.0f - inputColor[3]) * value + inputColor[3] * invertedValue; } else { @@ -67,8 +67,8 @@ void InvertOperation::executePixelSampled(float output[4], float x, float y, Pix void InvertOperation::deinitExecution() { - this->m_inputValueProgram = nullptr; - this->m_inputColorProgram = nullptr; + m_inputValueProgram = nullptr; + m_inputColorProgram = nullptr; } void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -80,7 +80,7 @@ void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, const float inverted_value = 1.0f - value; const float *color = it.in(1); - if (this->m_color) { + if (m_color) { it.out[0] = (1.0f - color[0]) * value + color[0] * inverted_value; it.out[1] = (1.0f - color[1]) * value + color[1] * inverted_value; it.out[2] = (1.0f - color[2]) * value + color[2] * inverted_value; @@ -89,7 +89,7 @@ void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, copy_v3_v3(it.out, color); } - if (this->m_alpha) { + if (m_alpha) { it.out[3] = (1.0f - color[3]) * value + color[3] * inverted_value; } else { diff --git a/source/blender/compositor/operations/COM_InvertOperation.h b/source/blender/compositor/operations/COM_InvertOperation.h index a084bf5d559..cb77c51f2b7 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.h +++ b/source/blender/compositor/operations/COM_InvertOperation.h @@ -53,11 +53,11 @@ class InvertOperation : public MultiThreadedOperation { void setColor(bool color) { - this->m_color = color; + m_color = color; } void setAlpha(bool alpha) { - this->m_alpha = alpha; + m_alpha = alpha; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc index d61c63cfc04..b5c93ad2888 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc @@ -25,8 +25,8 @@ KeyingBlurOperation::KeyingBlurOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_size = 0; - this->m_axis = BLUR_AXIS_X; + m_size = 0; + m_axis = BLUR_AXIS_X; this->flags.complex = true; } @@ -46,8 +46,8 @@ void KeyingBlurOperation::executePixel(float output[4], int x, int y, void *data int count = 0; float average = 0.0f; - if (this->m_axis == 0) { - const int start = MAX2(0, x - this->m_size + 1), end = MIN2(bufferWidth, x + this->m_size); + if (m_axis == 0) { + const int start = MAX2(0, x - m_size + 1), end = MIN2(bufferWidth, x + m_size); for (int cx = start; cx < end; cx++) { int bufferIndex = (y * bufferWidth + cx); average += buffer[bufferIndex]; @@ -55,8 +55,7 @@ void KeyingBlurOperation::executePixel(float output[4], int x, int y, void *data } } else { - const int start = MAX2(0, y - this->m_size + 1), - end = MIN2(inputBuffer->getHeight(), y + this->m_size); + const int start = MAX2(0, y - m_size + 1), end = MIN2(inputBuffer->getHeight(), y + m_size); for (int cy = start; cy < end; cy++) { int bufferIndex = (cy * bufferWidth + x); average += buffer[bufferIndex]; @@ -75,17 +74,17 @@ bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - if (this->m_axis == BLUR_AXIS_X) { - newInput.xmin = input->xmin - this->m_size; + if (m_axis == BLUR_AXIS_X) { + newInput.xmin = input->xmin - m_size; newInput.ymin = input->ymin; - newInput.xmax = input->xmax + this->m_size; + newInput.xmax = input->xmax + m_size; newInput.ymax = input->ymax; } else { newInput.xmin = input->xmin; - newInput.ymin = input->ymin - this->m_size; + newInput.ymin = input->ymin - m_size; newInput.xmax = input->xmax; - newInput.ymax = input->ymax + this->m_size; + newInput.ymax = input->ymax + m_size; } return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.h b/source/blender/compositor/operations/COM_KeyingBlurOperation.h index b290b905e63..9f312c86747 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.h +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.h @@ -40,11 +40,11 @@ class KeyingBlurOperation : public MultiThreadedOperation { void setSize(int value) { - this->m_size = value; + m_size = value; } void setAxis(int value) { - this->m_axis = value; + m_axis = value; } void *initializeTileData(rcti *rect) override; diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc index d4ef43b6521..b5a3b28e54e 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc @@ -25,13 +25,13 @@ KeyingClipOperation::KeyingClipOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_kernelRadius = 3; - this->m_kernelTolerance = 0.1f; + m_kernelRadius = 3; + m_kernelTolerance = 0.1f; - this->m_clipBlack = 0.0f; - this->m_clipWhite = 1.0f; + m_clipBlack = 0.0f; + m_clipWhite = 1.0f; - this->m_isEdgeMatte = false; + m_isEdgeMatte = false; this->flags.complex = true; } @@ -45,8 +45,8 @@ void *KeyingClipOperation::initializeTileData(rcti *rect) void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data) { - const int delta = this->m_kernelRadius; - const float tolerance = this->m_kernelTolerance; + const int delta = m_kernelRadius; + const float tolerance = m_kernelTolerance; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; float *buffer = inputBuffer->getBuffer(); @@ -86,7 +86,7 @@ void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data } } - if (this->m_isEdgeMatte) { + if (m_isEdgeMatte) { if (ok) { output[0] = 0.0f; } @@ -98,14 +98,14 @@ void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data output[0] = value; if (ok) { - if (output[0] < this->m_clipBlack) { + if (output[0] < m_clipBlack) { output[0] = 0.0f; } - else if (output[0] >= this->m_clipWhite) { + else if (output[0] >= m_clipWhite) { output[0] = 1.0f; } else { - output[0] = (output[0] - this->m_clipBlack) / (this->m_clipWhite - this->m_clipBlack); + output[0] = (output[0] - m_clipBlack) / (m_clipWhite - m_clipBlack); } } } @@ -117,10 +117,10 @@ bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - newInput.xmin = input->xmin - this->m_kernelRadius; - newInput.ymin = input->ymin - this->m_kernelRadius; - newInput.xmax = input->xmax + this->m_kernelRadius; - newInput.ymax = input->ymax + this->m_kernelRadius; + newInput.xmin = input->xmin - m_kernelRadius; + newInput.ymin = input->ymin - m_kernelRadius; + newInput.xmax = input->xmax + m_kernelRadius; + newInput.ymax = input->ymax + m_kernelRadius; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.h b/source/blender/compositor/operations/COM_KeyingClipOperation.h index 1a17d591781..adbb792d7b8 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.h +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.h @@ -40,25 +40,25 @@ class KeyingClipOperation : public MultiThreadedOperation { void setClipBlack(float value) { - this->m_clipBlack = value; + m_clipBlack = value; } void setClipWhite(float value) { - this->m_clipWhite = value; + m_clipWhite = value; } void setKernelRadius(int value) { - this->m_kernelRadius = value; + m_kernelRadius = value; } void setKernelTolerance(float value) { - this->m_kernelTolerance = value; + m_kernelTolerance = value; } void setIsEdgeMatte(bool value) { - this->m_isEdgeMatte = value; + m_isEdgeMatte = value; } void *initializeTileData(rcti *rect) override; diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc index 2c91d7771bf..e107fee6bb4 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc @@ -26,24 +26,24 @@ KeyingDespillOperation::KeyingDespillOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_despillFactor = 0.5f; - this->m_colorBalance = 0.5f; + m_despillFactor = 0.5f; + m_colorBalance = 0.5f; - this->m_pixelReader = nullptr; - this->m_screenReader = nullptr; + m_pixelReader = nullptr; + m_screenReader = nullptr; flags.can_be_constant = true; } void KeyingDespillOperation::initExecution() { - this->m_pixelReader = this->getInputSocketReader(0); - this->m_screenReader = this->getInputSocketReader(1); + m_pixelReader = this->getInputSocketReader(0); + m_screenReader = this->getInputSocketReader(1); } void KeyingDespillOperation::deinitExecution() { - this->m_pixelReader = nullptr; - this->m_screenReader = nullptr; + m_pixelReader = nullptr; + m_screenReader = nullptr; } void KeyingDespillOperation::executePixelSampled(float output[4], @@ -54,8 +54,8 @@ void KeyingDespillOperation::executePixelSampled(float output[4], float pixelColor[4]; float screenColor[4]; - this->m_pixelReader->readSampled(pixelColor, x, y, sampler); - this->m_screenReader->readSampled(screenColor, x, y, sampler); + m_pixelReader->readSampled(pixelColor, x, y, sampler); + m_screenReader->readSampled(screenColor, x, y, sampler); const int screen_primary_channel = max_axis_v3(screenColor); const int other_1 = (screen_primary_channel + 1) % 3; @@ -66,13 +66,13 @@ void KeyingDespillOperation::executePixelSampled(float output[4], float average_value, amount; - average_value = this->m_colorBalance * pixelColor[min_channel] + - (1.0f - this->m_colorBalance) * pixelColor[max_channel]; + average_value = m_colorBalance * pixelColor[min_channel] + + (1.0f - m_colorBalance) * pixelColor[max_channel]; amount = (pixelColor[screen_primary_channel] - average_value); copy_v4_v4(output, pixelColor); - const float amount_despill = this->m_despillFactor * amount; + const float amount_despill = m_despillFactor * amount; if (amount_despill > 0.0f) { output[screen_primary_channel] = pixelColor[screen_primary_channel] - amount_despill; } diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.h b/source/blender/compositor/operations/COM_KeyingDespillOperation.h index 16bed651d3a..257c24f3b58 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.h +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.h @@ -40,11 +40,11 @@ class KeyingDespillOperation : public MultiThreadedOperation { void setDespillFactor(float value) { - this->m_despillFactor = value; + m_despillFactor = value; } void setColorBalance(float value) { - this->m_colorBalance = value; + m_colorBalance = value; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cc b/source/blender/compositor/operations/COM_KeyingOperation.cc index 76f30726522..7b0b80b5796 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingOperation.cc @@ -42,22 +42,22 @@ KeyingOperation::KeyingOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Value); - this->m_screenBalance = 0.5f; + m_screenBalance = 0.5f; - this->m_pixelReader = nullptr; - this->m_screenReader = nullptr; + m_pixelReader = nullptr; + m_screenReader = nullptr; } void KeyingOperation::initExecution() { - this->m_pixelReader = this->getInputSocketReader(0); - this->m_screenReader = this->getInputSocketReader(1); + m_pixelReader = this->getInputSocketReader(0); + m_screenReader = this->getInputSocketReader(1); } void KeyingOperation::deinitExecution() { - this->m_pixelReader = nullptr; - this->m_screenReader = nullptr; + m_pixelReader = nullptr; + m_screenReader = nullptr; } void KeyingOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -65,8 +65,8 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix float pixel_color[4]; float screen_color[4]; - this->m_pixelReader->readSampled(pixel_color, x, y, sampler); - this->m_screenReader->readSampled(screen_color, x, y, sampler); + m_pixelReader->readSampled(pixel_color, x, y, sampler); + m_screenReader->readSampled(screen_color, x, y, sampler); const int primary_channel = max_axis_v3(screen_color); const float min_pixel_color = min_fff(pixel_color[0], pixel_color[1], pixel_color[2]); @@ -80,9 +80,8 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix output[0] = 1.0f; } else { - float saturation = get_pixel_saturation(pixel_color, this->m_screenBalance, primary_channel); - float screen_saturation = get_pixel_saturation( - screen_color, this->m_screenBalance, primary_channel); + float saturation = get_pixel_saturation(pixel_color, m_screenBalance, primary_channel); + float screen_saturation = get_pixel_saturation(screen_color, m_screenBalance, primary_channel); if (saturation < 0) { /* means main channel of pixel is different from screen, diff --git a/source/blender/compositor/operations/COM_KeyingOperation.h b/source/blender/compositor/operations/COM_KeyingOperation.h index e134ad54896..89674e64701 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.h +++ b/source/blender/compositor/operations/COM_KeyingOperation.h @@ -44,7 +44,7 @@ class KeyingOperation : public MultiThreadedOperation { void setScreenBalance(float value) { - this->m_screenBalance = value; + m_screenBalance = value; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc index 32a29ed62bb..646450eb970 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc @@ -29,9 +29,9 @@ namespace blender::compositor { KeyingScreenOperation::KeyingScreenOperation() { this->addOutputSocket(DataType::Color); - this->m_movieClip = nullptr; - this->m_framenumber = 0; - this->m_trackingObject[0] = 0; + m_movieClip = nullptr; + m_framenumber = 0; + m_trackingObject[0] = 0; flags.complex = true; m_cachedTriangulation = nullptr; } @@ -46,14 +46,14 @@ void KeyingScreenOperation::initExecution() } } else { - this->m_cachedTriangulation = nullptr; + m_cachedTriangulation = nullptr; } } void KeyingScreenOperation::deinitExecution() { - if (this->m_cachedTriangulation) { - TriangulationData *triangulation = this->m_cachedTriangulation; + if (m_cachedTriangulation) { + TriangulationData *triangulation = m_cachedTriangulation; if (triangulation->triangulated_points) { MEM_freeN(triangulation->triangulated_points); @@ -67,9 +67,9 @@ void KeyingScreenOperation::deinitExecution() MEM_freeN(triangulation->triangles_AABB); } - MEM_freeN(this->m_cachedTriangulation); + MEM_freeN(m_cachedTriangulation); - this->m_cachedTriangulation = nullptr; + m_cachedTriangulation = nullptr; } } @@ -77,7 +77,7 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri { MovieClipUser user = {0}; TriangulationData *triangulation; - MovieTracking *tracking = &this->m_movieClip->tracking; + MovieTracking *tracking = &m_movieClip->tracking; MovieTrackingTrack *track; VoronoiSite *sites, *site; ImBuf *ibuf; @@ -87,10 +87,10 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri int i; int width = this->getWidth(); int height = this->getHeight(); - int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, this->m_framenumber); + int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, m_framenumber); - if (this->m_trackingObject[0]) { - MovieTrackingObject *object = BKE_tracking_object_get_named(tracking, this->m_trackingObject); + if (m_trackingObject[0]) { + MovieTrackingObject *object = BKE_tracking_object_get_named(tracking, m_trackingObject); if (!object) { return nullptr; @@ -126,7 +126,7 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri } BKE_movieclip_user_set_frame(&user, clip_frame); - ibuf = BKE_movieclip_get_ibuf(this->m_movieClip, &user); + ibuf = BKE_movieclip_get_ibuf(m_movieClip, &user); if (!ibuf) { return nullptr; @@ -237,7 +237,7 @@ KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti * int chunk_size = 20; int i; - triangulation = this->m_cachedTriangulation; + triangulation = m_cachedTriangulation; if (!triangulation) { return nullptr; @@ -271,14 +271,14 @@ KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti * void *KeyingScreenOperation::initializeTileData(rcti *rect) { - if (this->m_movieClip == nullptr) { + if (m_movieClip == nullptr) { return nullptr; } - if (!this->m_cachedTriangulation) { + if (!m_cachedTriangulation) { lockMutex(); - if (this->m_cachedTriangulation == nullptr) { - this->m_cachedTriangulation = buildVoronoiTriangulation(); + if (m_cachedTriangulation == nullptr) { + m_cachedTriangulation = buildVoronoiTriangulation(); } unlockMutex(); } @@ -301,14 +301,13 @@ void KeyingScreenOperation::determine_canvas(const rcti &preferred_area, rcti &r { r_area = COM_AREA_NONE; - if (this->m_movieClip) { + if (m_movieClip) { MovieClipUser user = {0}; int width, height; - int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, - this->m_framenumber); + int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, m_framenumber); BKE_movieclip_user_set_frame(&user, clip_frame); - BKE_movieclip_get_size(this->m_movieClip, &user, &width, &height); + BKE_movieclip_get_size(m_movieClip, &user, &width, &height); r_area = preferred_area; r_area.xmax = r_area.xmin + width; r_area.ymax = r_area.ymin + height; @@ -322,8 +321,8 @@ void KeyingScreenOperation::executePixel(float output[4], int x, int y, void *da output[2] = 0.0f; output[3] = 1.0f; - if (this->m_movieClip && data) { - TriangulationData *triangulation = this->m_cachedTriangulation; + if (m_movieClip && data) { + TriangulationData *triangulation = m_cachedTriangulation; TileData *tile_data = (TileData *)data; int i; float co[2] = {(float)x, (float)y}; diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h index 7a7dda27710..32299c8f241 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h @@ -72,15 +72,15 @@ class KeyingScreenOperation : public MultiThreadedOperation { void setMovieClip(MovieClip *clip) { - this->m_movieClip = clip; + m_movieClip = clip; } void setTrackingObject(const char *object) { - BLI_strncpy(this->m_trackingObject, object, sizeof(this->m_trackingObject)); + BLI_strncpy(m_trackingObject, object, sizeof(m_trackingObject)); } void setFramenumber(int framenumber) { - this->m_framenumber = framenumber; + m_framenumber = framenumber; } void executePixel(float output[4], int x, int y, void *data) override; diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc index e6cb1d8f718..97a615bdd1e 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc @@ -27,18 +27,18 @@ LuminanceMatteOperation::LuminanceMatteOperation() addInputSocket(DataType::Color); addOutputSocket(DataType::Value); - this->m_inputImageProgram = nullptr; + m_inputImageProgram = nullptr; flags.can_be_constant = true; } void LuminanceMatteOperation::initExecution() { - this->m_inputImageProgram = this->getInputSocketReader(0); + m_inputImageProgram = this->getInputSocketReader(0); } void LuminanceMatteOperation::deinitExecution() { - this->m_inputImageProgram = nullptr; + m_inputImageProgram = nullptr; } void LuminanceMatteOperation::executePixelSampled(float output[4], @@ -47,10 +47,10 @@ void LuminanceMatteOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inColor[4]; - this->m_inputImageProgram->readSampled(inColor, x, y, sampler); + m_inputImageProgram->readSampled(inColor, x, y, sampler); - const float high = this->m_settings->t1; - const float low = this->m_settings->t2; + const float high = m_settings->t1; + const float low = m_settings->t2; const float luminance = IMB_colormanagement_get_luminance(inColor); float alpha; diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h index aedfc715382..53618a4a107 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h @@ -47,7 +47,7 @@ class LuminanceMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma) { - this->m_settings = nodeChroma; + m_settings = nodeChroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.cc b/source/blender/compositor/operations/COM_MapRangeOperation.cc index 82fb033bf24..cdf41290a0a 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.cc +++ b/source/blender/compositor/operations/COM_MapRangeOperation.cc @@ -28,18 +28,18 @@ MapRangeOperation::MapRangeOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_inputOperation = nullptr; - this->m_useClamp = false; + m_inputOperation = nullptr; + m_useClamp = false; flags.can_be_constant = true; } void MapRangeOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_sourceMinOperation = this->getInputSocketReader(1); - this->m_sourceMaxOperation = this->getInputSocketReader(2); - this->m_destMinOperation = this->getInputSocketReader(3); - this->m_destMaxOperation = this->getInputSocketReader(4); + m_inputOperation = this->getInputSocketReader(0); + m_sourceMinOperation = this->getInputSocketReader(1); + m_sourceMaxOperation = this->getInputSocketReader(2); + m_destMinOperation = this->getInputSocketReader(3); + m_destMaxOperation = this->getInputSocketReader(4); } /* The code below assumes all data is inside range +- this, and that input buffer is single channel @@ -56,11 +56,11 @@ void MapRangeOperation::executePixelSampled(float output[4], float source_min, source_max; float dest_min, dest_max; - this->m_inputOperation->readSampled(inputs, x, y, sampler); - this->m_sourceMinOperation->readSampled(inputs + 1, x, y, sampler); - this->m_sourceMaxOperation->readSampled(inputs + 2, x, y, sampler); - this->m_destMinOperation->readSampled(inputs + 3, x, y, sampler); - this->m_destMaxOperation->readSampled(inputs + 4, x, y, sampler); + m_inputOperation->readSampled(inputs, x, y, sampler); + m_sourceMinOperation->readSampled(inputs + 1, x, y, sampler); + m_sourceMaxOperation->readSampled(inputs + 2, x, y, sampler); + m_destMinOperation->readSampled(inputs + 3, x, y, sampler); + m_destMaxOperation->readSampled(inputs + 4, x, y, sampler); value = inputs[0]; source_min = inputs[1]; @@ -84,7 +84,7 @@ void MapRangeOperation::executePixelSampled(float output[4], value = dest_min; } - if (this->m_useClamp) { + if (m_useClamp) { if (dest_max > dest_min) { CLAMP(value, dest_min, dest_max); } @@ -98,11 +98,11 @@ void MapRangeOperation::executePixelSampled(float output[4], void MapRangeOperation::deinitExecution() { - this->m_inputOperation = nullptr; - this->m_sourceMinOperation = nullptr; - this->m_sourceMaxOperation = nullptr; - this->m_destMinOperation = nullptr; - this->m_destMaxOperation = nullptr; + m_inputOperation = nullptr; + m_sourceMinOperation = nullptr; + m_sourceMaxOperation = nullptr; + m_destMinOperation = nullptr; + m_destMaxOperation = nullptr; } void MapRangeOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.h b/source/blender/compositor/operations/COM_MapRangeOperation.h index a01be14d528..b061eb687c1 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.h +++ b/source/blender/compositor/operations/COM_MapRangeOperation.h @@ -66,7 +66,7 @@ class MapRangeOperation : public MultiThreadedOperation { */ void setUseClamp(bool value) { - this->m_useClamp = value; + m_useClamp = value; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index 194fa504978..83f4be133ed 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -25,12 +25,12 @@ MapUVOperation::MapUVOperation() this->addInputSocket(DataType::Color, ResizeMode::Align); this->addInputSocket(DataType::Vector); this->addOutputSocket(DataType::Color); - this->m_alpha = 0.0f; + m_alpha = 0.0f; this->flags.complex = true; set_canvas_input_index(UV_INPUT_INDEX); - this->m_inputUVProgram = nullptr; - this->m_inputColorProgram = nullptr; + m_inputUVProgram = nullptr; + m_inputColorProgram = nullptr; } void MapUVOperation::init_data() @@ -46,11 +46,11 @@ void MapUVOperation::init_data() void MapUVOperation::initExecution() { - this->m_inputColorProgram = this->getInputSocketReader(0); - this->m_inputUVProgram = this->getInputSocketReader(1); + m_inputColorProgram = this->getInputSocketReader(0); + m_inputUVProgram = this->getInputSocketReader(1); if (execution_model_ == eExecutionModel::Tiled) { uv_input_read_fn_ = [=](float x, float y, float *out) { - this->m_inputUVProgram->readSampled(out, x, y, PixelSampler::Bilinear); + m_inputUVProgram->readSampled(out, x, y, PixelSampler::Bilinear); }; } } @@ -70,10 +70,10 @@ void MapUVOperation::executePixelSampled(float output[4], } /* EWA filtering */ - this->m_inputColorProgram->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + m_inputColorProgram->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); /* UV to alpha threshold */ - const float threshold = this->m_alpha * 0.05f; + const float threshold = m_alpha * 0.05f; /* XXX alpha threshold is used to fade out pixels on boundaries with invalid derivatives. * this calculation is not very well defined, should be looked into if it becomes a problem ... */ @@ -164,8 +164,8 @@ void MapUVOperation::pixelTransform(const float xy[2], void MapUVOperation::deinitExecution() { - this->m_inputUVProgram = nullptr; - this->m_inputColorProgram = nullptr; + m_inputUVProgram = nullptr; + m_inputColorProgram = nullptr; } bool MapUVOperation::determineDependingAreaOfInterest(rcti *input, @@ -246,7 +246,7 @@ void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output, input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); /* UV to alpha threshold. */ - const float threshold = this->m_alpha * 0.05f; + const float threshold = m_alpha * 0.05f; /* XXX alpha threshold is used to fade out pixels on boundaries with invalid derivatives. * this calculation is not very well defined, should be looked into if it becomes a problem ... */ diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h index 49c6689f700..612caab21ad 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.h +++ b/source/blender/compositor/operations/COM_MapUVOperation.h @@ -72,7 +72,7 @@ class MapUVOperation : public MultiThreadedOperation { void setAlpha(float alpha) { - this->m_alpha = alpha; + m_alpha = alpha; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc index 94fecc3f49e..5351f7ac7e4 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.cc +++ b/source/blender/compositor/operations/COM_MapValueOperation.cc @@ -24,13 +24,13 @@ MapValueOperation::MapValueOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; flags.can_be_constant = true; } void MapValueOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); + m_inputOperation = this->getInputSocketReader(0); } void MapValueOperation::executePixelSampled(float output[4], @@ -39,8 +39,8 @@ void MapValueOperation::executePixelSampled(float output[4], PixelSampler sampler) { float src[4]; - this->m_inputOperation->readSampled(src, x, y, sampler); - TexMapping *texmap = this->m_settings; + m_inputOperation->readSampled(src, x, y, sampler); + TexMapping *texmap = m_settings; float value = (src[0] + texmap->loc[0]) * texmap->size[0]; if (texmap->flag & TEXMAP_CLIP_MIN) { if (value < texmap->min[0]) { @@ -58,7 +58,7 @@ void MapValueOperation::executePixelSampled(float output[4], void MapValueOperation::deinitExecution() { - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -67,7 +67,7 @@ void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output, { for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float input = *it.in(0); - TexMapping *texmap = this->m_settings; + TexMapping *texmap = m_settings; float value = (input + texmap->loc[0]) * texmap->size[0]; if (texmap->flag & TEXMAP_CLIP_MIN) { if (value < texmap->min[0]) { diff --git a/source/blender/compositor/operations/COM_MapValueOperation.h b/source/blender/compositor/operations/COM_MapValueOperation.h index a595eac3155..75761681f12 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.h +++ b/source/blender/compositor/operations/COM_MapValueOperation.h @@ -61,7 +61,7 @@ class MapValueOperation : public MultiThreadedOperation { */ void setSettings(TexMapping *settings) { - this->m_settings = settings; + m_settings = settings; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc index 5044df0b128..aa7b693554f 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cc +++ b/source/blender/compositor/operations/COM_MaskOperation.cc @@ -26,39 +26,34 @@ namespace blender::compositor { MaskOperation::MaskOperation() { this->addOutputSocket(DataType::Value); - this->m_mask = nullptr; - this->m_maskWidth = 0; - this->m_maskHeight = 0; - this->m_maskWidthInv = 0.0f; - this->m_maskHeightInv = 0.0f; - this->m_frame_shutter = 0.0f; - this->m_frame_number = 0; - this->m_rasterMaskHandleTot = 1; - memset(this->m_rasterMaskHandles, 0, sizeof(this->m_rasterMaskHandles)); + m_mask = nullptr; + m_maskWidth = 0; + m_maskHeight = 0; + m_maskWidthInv = 0.0f; + m_maskHeightInv = 0.0f; + m_frame_shutter = 0.0f; + m_frame_number = 0; + m_rasterMaskHandleTot = 1; + memset(m_rasterMaskHandles, 0, sizeof(m_rasterMaskHandles)); } void MaskOperation::initExecution() { - if (this->m_mask && this->m_rasterMaskHandles[0] == nullptr) { - if (this->m_rasterMaskHandleTot == 1) { - this->m_rasterMaskHandles[0] = BKE_maskrasterize_handle_new(); + if (m_mask && m_rasterMaskHandles[0] == nullptr) { + if (m_rasterMaskHandleTot == 1) { + m_rasterMaskHandles[0] = BKE_maskrasterize_handle_new(); - BKE_maskrasterize_handle_init(this->m_rasterMaskHandles[0], - this->m_mask, - this->m_maskWidth, - this->m_maskHeight, - true, - true, - this->m_do_feather); + BKE_maskrasterize_handle_init( + m_rasterMaskHandles[0], m_mask, m_maskWidth, m_maskHeight, true, true, m_do_feather); } else { /* make a throw away copy of the mask */ - const float frame = (float)this->m_frame_number - this->m_frame_shutter; - const float frame_step = (this->m_frame_shutter * 2.0f) / this->m_rasterMaskHandleTot; + const float frame = (float)m_frame_number - m_frame_shutter; + const float frame_step = (m_frame_shutter * 2.0f) / m_rasterMaskHandleTot; float frame_iter = frame; Mask *mask_temp = (Mask *)BKE_id_copy_ex( - nullptr, &this->m_mask->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); + nullptr, &m_mask->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); /* trick so we can get unkeyed edits to display */ { @@ -67,24 +62,24 @@ void MaskOperation::initExecution() for (masklay = (MaskLayer *)mask_temp->masklayers.first; masklay; masklay = masklay->next) { - masklay_shape = BKE_mask_layer_shape_verify_frame(masklay, this->m_frame_number); + masklay_shape = BKE_mask_layer_shape_verify_frame(masklay, m_frame_number); BKE_mask_layer_shape_from_mask(masklay, masklay_shape); } } - for (unsigned int i = 0; i < this->m_rasterMaskHandleTot; i++) { - this->m_rasterMaskHandles[i] = BKE_maskrasterize_handle_new(); + for (unsigned int i = 0; i < m_rasterMaskHandleTot; i++) { + m_rasterMaskHandles[i] = BKE_maskrasterize_handle_new(); /* re-eval frame info */ BKE_mask_evaluate(mask_temp, frame_iter, true); - BKE_maskrasterize_handle_init(this->m_rasterMaskHandles[i], + BKE_maskrasterize_handle_init(m_rasterMaskHandles[i], mask_temp, - this->m_maskWidth, - this->m_maskHeight, + m_maskWidth, + m_maskHeight, true, true, - this->m_do_feather); + m_do_feather); frame_iter += frame_step; } @@ -96,17 +91,17 @@ void MaskOperation::initExecution() void MaskOperation::deinitExecution() { - for (unsigned int i = 0; i < this->m_rasterMaskHandleTot; i++) { - if (this->m_rasterMaskHandles[i]) { - BKE_maskrasterize_handle_free(this->m_rasterMaskHandles[i]); - this->m_rasterMaskHandles[i] = nullptr; + for (unsigned int i = 0; i < m_rasterMaskHandleTot; i++) { + if (m_rasterMaskHandles[i]) { + BKE_maskrasterize_handle_free(m_rasterMaskHandles[i]); + m_rasterMaskHandles[i] = nullptr; } } } void MaskOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (this->m_maskWidth == 0 || this->m_maskHeight == 0) { + if (m_maskWidth == 0 || m_maskHeight == 0) { r_area = COM_AREA_NONE; } else { @@ -122,13 +117,13 @@ void MaskOperation::executePixelSampled(float output[4], PixelSampler /*sampler*/) { const float xy[2] = { - (x * this->m_maskWidthInv) + this->m_mask_px_ofs[0], - (y * this->m_maskHeightInv) + this->m_mask_px_ofs[1], + (x * m_maskWidthInv) + m_mask_px_ofs[0], + (y * m_maskHeightInv) + m_mask_px_ofs[1], }; - if (this->m_rasterMaskHandleTot == 1) { - if (this->m_rasterMaskHandles[0]) { - output[0] = BKE_maskrasterize_handle_sample(this->m_rasterMaskHandles[0], xy); + if (m_rasterMaskHandleTot == 1) { + if (m_rasterMaskHandles[0]) { + output[0] = BKE_maskrasterize_handle_sample(m_rasterMaskHandles[0], xy); } else { output[0] = 0.0f; @@ -138,14 +133,14 @@ void MaskOperation::executePixelSampled(float output[4], /* In case loop below fails. */ output[0] = 0.0f; - for (unsigned int i = 0; i < this->m_rasterMaskHandleTot; i++) { - if (this->m_rasterMaskHandles[i]) { - output[0] += BKE_maskrasterize_handle_sample(this->m_rasterMaskHandles[i], xy); + for (unsigned int i = 0; i < m_rasterMaskHandleTot; i++) { + if (m_rasterMaskHandles[i]) { + output[0] += BKE_maskrasterize_handle_sample(m_rasterMaskHandles[i], xy); } } /* until we get better falloff */ - output[0] /= this->m_rasterMaskHandleTot; + output[0] /= m_rasterMaskHandleTot; } } diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h index cc7eb0c022c..8ad3044b6cd 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.h +++ b/source/blender/compositor/operations/COM_MaskOperation.h @@ -64,36 +64,36 @@ class MaskOperation : public MultiThreadedOperation { void setMask(Mask *mask) { - this->m_mask = mask; + m_mask = mask; } void setMaskWidth(int width) { - this->m_maskWidth = width; - this->m_maskWidthInv = 1.0f / (float)width; - this->m_mask_px_ofs[0] = this->m_maskWidthInv * 0.5f; + m_maskWidth = width; + m_maskWidthInv = 1.0f / (float)width; + m_mask_px_ofs[0] = m_maskWidthInv * 0.5f; } void setMaskHeight(int height) { - this->m_maskHeight = height; - this->m_maskHeightInv = 1.0f / (float)height; - this->m_mask_px_ofs[1] = this->m_maskHeightInv * 0.5f; + m_maskHeight = height; + m_maskHeightInv = 1.0f / (float)height; + m_mask_px_ofs[1] = m_maskHeightInv * 0.5f; } void setFramenumber(int frame_number) { - this->m_frame_number = frame_number; + m_frame_number = frame_number; } void setFeather(bool feather) { - this->m_do_feather = feather; + m_do_feather = feather; } void setMotionBlurSamples(int samples) { - this->m_rasterMaskHandleTot = MIN2(MAX2(1, samples), CMP_NODE_MASK_MBLUR_SAMPLES_MAX); + m_rasterMaskHandleTot = MIN2(MAX2(1, samples), CMP_NODE_MASK_MBLUR_SAMPLES_MAX); } void setMotionBlurShutter(float shutter) { - this->m_frame_shutter = shutter; + m_frame_shutter = shutter; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc index 3a447ad53e0..a16ecfc7327 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.cc +++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc @@ -28,25 +28,25 @@ MathBaseOperation::MathBaseOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_inputValue1Operation = nullptr; - this->m_inputValue2Operation = nullptr; - this->m_inputValue3Operation = nullptr; - this->m_useClamp = false; + m_inputValue1Operation = nullptr; + m_inputValue2Operation = nullptr; + m_inputValue3Operation = nullptr; + m_useClamp = false; this->flags.can_be_constant = true; } void MathBaseOperation::initExecution() { - this->m_inputValue1Operation = this->getInputSocketReader(0); - this->m_inputValue2Operation = this->getInputSocketReader(1); - this->m_inputValue3Operation = this->getInputSocketReader(2); + m_inputValue1Operation = this->getInputSocketReader(0); + m_inputValue2Operation = this->getInputSocketReader(1); + m_inputValue3Operation = this->getInputSocketReader(2); } void MathBaseOperation::deinitExecution() { - this->m_inputValue1Operation = nullptr; - this->m_inputValue2Operation = nullptr; - this->m_inputValue3Operation = nullptr; + m_inputValue1Operation = nullptr; + m_inputValue2Operation = nullptr; + m_inputValue3Operation = nullptr; } void MathBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -66,7 +66,7 @@ void MathBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_are void MathBaseOperation::clampIfNeeded(float *color) { - if (this->m_useClamp) { + if (m_useClamp) { CLAMP(color[0], 0.0f, 1.0f); } } @@ -84,8 +84,8 @@ void MathAddOperation::executePixelSampled(float output[4], float x, float y, Pi float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = inputValue1[0] + inputValue2[0]; @@ -100,8 +100,8 @@ void MathSubtractOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = inputValue1[0] - inputValue2[0]; @@ -116,8 +116,8 @@ void MathMultiplyOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = inputValue1[0] * inputValue2[0]; @@ -132,8 +132,8 @@ void MathDivideOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); if (inputValue2[0] == 0) { /* We don't want to divide by zero. */ output[0] = 0.0; @@ -161,8 +161,8 @@ void MathSineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = sin(inputValue1[0]); @@ -185,8 +185,8 @@ void MathCosineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = cos(inputValue1[0]); @@ -209,8 +209,8 @@ void MathTangentOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = tan(inputValue1[0]); @@ -233,8 +233,8 @@ void MathHyperbolicSineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = sinh(inputValue1[0]); @@ -257,8 +257,8 @@ void MathHyperbolicCosineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = cosh(inputValue1[0]); @@ -281,8 +281,8 @@ void MathHyperbolicTangentOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = tanh(inputValue1[0]); @@ -305,8 +305,8 @@ void MathArcSineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); if (inputValue1[0] <= 1 && inputValue1[0] >= -1) { output[0] = asin(inputValue1[0]); @@ -334,8 +334,8 @@ void MathArcCosineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); if (inputValue1[0] <= 1 && inputValue1[0] >= -1) { output[0] = acos(inputValue1[0]); @@ -363,8 +363,8 @@ void MathArcTangentOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = atan(inputValue1[0]); @@ -387,8 +387,8 @@ void MathPowerOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); if (inputValue1[0] >= 0) { output[0] = pow(inputValue1[0], inputValue2[0]); @@ -438,8 +438,8 @@ void MathLogarithmOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); if (inputValue1[0] > 0 && inputValue2[0] > 0) { output[0] = log(inputValue1[0]) / log(inputValue2[0]); @@ -474,8 +474,8 @@ void MathMinimumOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = MIN2(inputValue1[0], inputValue2[0]); @@ -498,8 +498,8 @@ void MathMaximumOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = MAX2(inputValue1[0], inputValue2[0]); @@ -522,8 +522,8 @@ void MathRoundOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = round(inputValue1[0]); @@ -546,8 +546,8 @@ void MathLessThanOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = inputValue1[0] < inputValue2[0] ? 1.0f : 0.0f; @@ -562,8 +562,8 @@ void MathGreaterThanOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = inputValue1[0] > inputValue2[0] ? 1.0f : 0.0f; @@ -578,8 +578,8 @@ void MathModuloOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); if (inputValue2[0] == 0) { output[0] = 0.0; @@ -607,7 +607,7 @@ void MathAbsoluteOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); output[0] = fabs(inputValue1[0]); @@ -629,7 +629,7 @@ void MathRadiansOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); output[0] = DEG2RADF(inputValue1[0]); @@ -651,7 +651,7 @@ void MathDegreesOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); output[0] = RAD2DEGF(inputValue1[0]); @@ -674,8 +674,8 @@ void MathArcTan2Operation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = atan2(inputValue1[0], inputValue2[0]); @@ -697,7 +697,7 @@ void MathFloorOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); output[0] = floor(inputValue1[0]); @@ -719,7 +719,7 @@ void MathCeilOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); output[0] = ceil(inputValue1[0]); @@ -741,7 +741,7 @@ void MathFractOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); output[0] = inputValue1[0] - floor(inputValue1[0]); @@ -763,7 +763,7 @@ void MathSqrtOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); if (inputValue1[0] > 0) { output[0] = sqrt(inputValue1[0]); @@ -790,7 +790,7 @@ void MathInverseSqrtOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); if (inputValue1[0] > 0) { output[0] = 1.0f / sqrt(inputValue1[0]); @@ -817,7 +817,7 @@ void MathSignOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); output[0] = compatible_signf(inputValue1[0]); @@ -839,7 +839,7 @@ void MathExponentOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); output[0] = expf(inputValue1[0]); @@ -861,7 +861,7 @@ void MathTruncOperation::executePixelSampled(float output[4], { float inputValue1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); output[0] = (inputValue1[0] >= 0.0f) ? floor(inputValue1[0]) : ceil(inputValue1[0]); @@ -885,8 +885,8 @@ void MathSnapOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); if (inputValue1[0] == 0 || inputValue2[0] == 0) { /* We don't want to divide by zero. */ output[0] = 0.0f; @@ -922,9 +922,9 @@ void MathWrapOperation::executePixelSampled(float output[4], float inputValue2[4]; float inputValue3[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); output[0] = wrapf(inputValue1[0], inputValue2[0], inputValue3[0]); @@ -947,8 +947,8 @@ void MathPingpongOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); output[0] = pingpongf(inputValue1[0], inputValue2[0]); @@ -972,9 +972,9 @@ void MathCompareOperation::executePixelSampled(float output[4], float inputValue2[4]; float inputValue3[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); output[0] = (fabsf(inputValue1[0] - inputValue2[0]) <= MAX2(inputValue3[0], 1e-5f)) ? 1.0f : 0.0f; @@ -999,9 +999,9 @@ void MathMultiplyAddOperation::executePixelSampled(float output[4], float inputValue2[4]; float inputValue3[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); output[0] = inputValue1[0] * inputValue2[0] + inputValue3[0]; @@ -1025,9 +1025,9 @@ void MathSmoothMinOperation::executePixelSampled(float output[4], float inputValue2[4]; float inputValue3[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); output[0] = smoothminf(inputValue1[0], inputValue2[0], inputValue3[0]); @@ -1051,9 +1051,9 @@ void MathSmoothMaxOperation::executePixelSampled(float output[4], float inputValue2[4]; float inputValue3[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); output[0] = -smoothminf(-inputValue1[0], -inputValue2[0], inputValue3[0]); diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h index 0188eb50fa8..1424a7a9769 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.h +++ b/source/blender/compositor/operations/COM_MathBaseOperation.h @@ -48,7 +48,7 @@ class MathBaseOperation : public MultiThreadedOperation { float clamp_when_enabled(float value) { - if (this->m_useClamp) { + if (m_useClamp) { return CLAMPIS(value, 0.0f, 1.0f); } return value; @@ -56,7 +56,7 @@ class MathBaseOperation : public MultiThreadedOperation { void clamp_when_enabled(float *out) { - if (this->m_useClamp) { + if (m_useClamp) { CLAMP(*out, 0.0f, 1.0f); } } @@ -79,7 +79,7 @@ class MathBaseOperation : public MultiThreadedOperation { void setUseClamp(bool value) { - this->m_useClamp = value; + m_useClamp = value; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc index d16fbb1324e..d7a8d0c0e8e 100644 --- a/source/blender/compositor/operations/COM_MixOperation.cc +++ b/source/blender/compositor/operations/COM_MixOperation.cc @@ -28,9 +28,9 @@ MixBaseOperation::MixBaseOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_inputValueOperation = nullptr; - this->m_inputColor1Operation = nullptr; - this->m_inputColor2Operation = nullptr; + m_inputValueOperation = nullptr; + m_inputColor1Operation = nullptr; + m_inputColor2Operation = nullptr; this->setUseValueAlphaMultiply(false); this->setUseClamp(false); flags.can_be_constant = true; @@ -38,9 +38,9 @@ MixBaseOperation::MixBaseOperation() void MixBaseOperation::initExecution() { - this->m_inputValueOperation = this->getInputSocketReader(0); - this->m_inputColor1Operation = this->getInputSocketReader(1); - this->m_inputColor2Operation = this->getInputSocketReader(2); + m_inputValueOperation = this->getInputSocketReader(0); + m_inputColor1Operation = this->getInputSocketReader(1); + m_inputColor2Operation = this->getInputSocketReader(2); } void MixBaseOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -49,9 +49,9 @@ void MixBaseOperation::executePixelSampled(float output[4], float x, float y, Pi float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -89,9 +89,9 @@ void MixBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area void MixBaseOperation::deinitExecution() { - this->m_inputValueOperation = nullptr; - this->m_inputColor1Operation = nullptr; - this->m_inputColor2Operation = nullptr; + m_inputValueOperation = nullptr; + m_inputColor1Operation = nullptr; + m_inputColor2Operation = nullptr; } void MixBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -141,9 +141,9 @@ void MixAddOperation::executePixelSampled(float output[4], float x, float y, Pix float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -186,9 +186,9 @@ void MixBlendOperation::executePixelSampled(float output[4], float inputValue[4]; float value; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -233,9 +233,9 @@ void MixColorBurnOperation::executePixelSampled(float output[4], float inputValue[4]; float tmp; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -352,9 +352,9 @@ void MixColorOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -422,9 +422,9 @@ void MixDarkenOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -468,9 +468,9 @@ void MixDifferenceOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -514,9 +514,9 @@ void MixDivideOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -595,9 +595,9 @@ void MixDodgeOperation::executePixelSampled(float output[4], float inputValue[4]; float tmp; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -736,9 +736,9 @@ void MixGlareOperation::executePixelSampled(float output[4], float inputValue[4]; float value, input_weight, glare_weight; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); value = inputValue[0]; /* Linear interpolation between 3 cases: * value=-1:output=input value=0:output=input+glare value=1:output=glare @@ -794,9 +794,9 @@ void MixHueOperation::executePixelSampled(float output[4], float x, float y, Pix float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -864,9 +864,9 @@ void MixLightenOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -934,9 +934,9 @@ void MixLinearLightOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1010,9 +1010,9 @@ void MixMultiplyOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1057,9 +1057,9 @@ void MixOverlayOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1136,9 +1136,9 @@ void MixSaturationOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1200,9 +1200,9 @@ void MixScreenOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1248,9 +1248,9 @@ void MixSoftLightOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1317,9 +1317,9 @@ void MixSubtractOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1361,9 +1361,9 @@ void MixValueOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + m_inputValueOperation->readSampled(inputValue, x, y, sampler); + m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); + m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h index fabbea422eb..5d96aa4f5d7 100644 --- a/source/blender/compositor/operations/COM_MixOperation.h +++ b/source/blender/compositor/operations/COM_MixOperation.h @@ -91,15 +91,15 @@ class MixBaseOperation : public MultiThreadedOperation { void setUseValueAlphaMultiply(const bool value) { - this->m_valueAlphaMultiply = value; + m_valueAlphaMultiply = value; } inline bool useValueAlphaMultiply() { - return this->m_valueAlphaMultiply; + return m_valueAlphaMultiply; } void setUseClamp(bool value) { - this->m_useClamp = value; + m_useClamp = value; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc index aed91d4d46e..e62c126812f 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc @@ -26,9 +26,9 @@ namespace blender::compositor { MovieClipAttributeOperation::MovieClipAttributeOperation() { this->addOutputSocket(DataType::Value); - this->m_framenumber = 0; - this->m_attribute = MCA_X; - this->m_invert = false; + m_framenumber = 0; + m_attribute = MCA_X; + m_invert = false; needs_canvas_to_get_constant_ = true; is_value_calculated_ = false; stabilization_resolution_socket_ = nullptr; @@ -45,7 +45,7 @@ void MovieClipAttributeOperation::calc_value() { BLI_assert(this->get_flags().is_canvas_set); is_value_calculated_ = true; - if (this->m_clip == nullptr) { + if (m_clip == nullptr) { return; } float loc[2], scale, angle; @@ -53,38 +53,38 @@ void MovieClipAttributeOperation::calc_value() loc[1] = 0.0f; scale = 1.0f; angle = 0.0f; - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_clip, this->m_framenumber); + int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(m_clip, m_framenumber); NodeOperation &stabilization_operation = stabilization_resolution_socket_ ? stabilization_resolution_socket_->getLink()->getOperation() : *this; - BKE_tracking_stabilization_data_get(this->m_clip, + BKE_tracking_stabilization_data_get(m_clip, clip_framenr, stabilization_operation.getWidth(), stabilization_operation.getHeight(), loc, &scale, &angle); - switch (this->m_attribute) { + switch (m_attribute) { case MCA_SCALE: - this->m_value = scale; + m_value = scale; break; case MCA_ANGLE: - this->m_value = angle; + m_value = angle; break; case MCA_X: - this->m_value = loc[0]; + m_value = loc[0]; break; case MCA_Y: - this->m_value = loc[1]; + m_value = loc[1]; break; } - if (this->m_invert) { - if (this->m_attribute != MCA_SCALE) { - this->m_value = -this->m_value; + if (m_invert) { + if (m_attribute != MCA_SCALE) { + m_value = -m_value; } else { - this->m_value = 1.0f / this->m_value; + m_value = 1.0f / m_value; } } } @@ -94,7 +94,7 @@ void MovieClipAttributeOperation::executePixelSampled(float output[4], float /*y*/, PixelSampler /*sampler*/) { - output[0] = this->m_value; + output[0] = m_value; } void MovieClipAttributeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h index 50680653aea..640d578abb0 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h @@ -62,19 +62,19 @@ class MovieClipAttributeOperation : public ConstantOperation { void setMovieClip(MovieClip *clip) { - this->m_clip = clip; + m_clip = clip; } void setFramenumber(int framenumber) { - this->m_framenumber = framenumber; + m_framenumber = framenumber; } void setAttribute(MovieClipAttribute attribute) { - this->m_attribute = attribute; + m_attribute = attribute; } void setInvert(bool invert) { - this->m_invert = invert; + m_invert = invert; } /** diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc index 6430e2af0db..aac263142d7 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc @@ -27,30 +27,30 @@ namespace blender::compositor { MovieClipBaseOperation::MovieClipBaseOperation() { - this->m_movieClip = nullptr; - this->m_movieClipBuffer = nullptr; - this->m_movieClipUser = nullptr; - this->m_movieClipwidth = 0; - this->m_movieClipheight = 0; - this->m_framenumber = 0; + m_movieClip = nullptr; + m_movieClipBuffer = nullptr; + m_movieClipUser = nullptr; + m_movieClipwidth = 0; + m_movieClipheight = 0; + m_framenumber = 0; } void MovieClipBaseOperation::initExecution() { - if (this->m_movieClip) { - BKE_movieclip_user_set_frame(this->m_movieClipUser, this->m_framenumber); + if (m_movieClip) { + BKE_movieclip_user_set_frame(m_movieClipUser, m_framenumber); ImBuf *ibuf; - if (this->m_cacheFrame) { - ibuf = BKE_movieclip_get_ibuf(this->m_movieClip, this->m_movieClipUser); + if (m_cacheFrame) { + ibuf = BKE_movieclip_get_ibuf(m_movieClip, m_movieClipUser); } else { ibuf = BKE_movieclip_get_ibuf_flag( - this->m_movieClip, this->m_movieClipUser, this->m_movieClip->flag, MOVIECLIP_CACHE_SKIP); + m_movieClip, m_movieClipUser, m_movieClip->flag, MOVIECLIP_CACHE_SKIP); } if (ibuf) { - this->m_movieClipBuffer = ibuf; + m_movieClipBuffer = ibuf; if (ibuf->rect_float == nullptr || ibuf->userflags & IB_RECT_INVALID) { IMB_float_from_rect(ibuf); ibuf->userflags &= ~IB_RECT_INVALID; @@ -61,19 +61,19 @@ void MovieClipBaseOperation::initExecution() void MovieClipBaseOperation::deinitExecution() { - if (this->m_movieClipBuffer) { - IMB_freeImBuf(this->m_movieClipBuffer); + if (m_movieClipBuffer) { + IMB_freeImBuf(m_movieClipBuffer); - this->m_movieClipBuffer = nullptr; + m_movieClipBuffer = nullptr; } } void MovieClipBaseOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { r_area = COM_AREA_NONE; - if (this->m_movieClip) { + if (m_movieClip) { int width, height; - BKE_movieclip_get_size(this->m_movieClip, this->m_movieClipUser, &width, &height); + BKE_movieclip_get_size(m_movieClip, m_movieClipUser, &width, &height); BLI_rcti_init(&r_area, 0, width, 0, height); } } @@ -83,7 +83,7 @@ void MovieClipBaseOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - ImBuf *ibuf = this->m_movieClipBuffer; + ImBuf *ibuf = m_movieClipBuffer; if (ibuf == nullptr) { zero_v4(output); diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.h b/source/blender/compositor/operations/COM_MovieClipOperation.h index 465ccd4fc00..a319c2116b7 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipOperation.h @@ -50,20 +50,20 @@ class MovieClipBaseOperation : public MultiThreadedOperation { void deinitExecution() override; void setMovieClip(MovieClip *image) { - this->m_movieClip = image; + m_movieClip = image; } void setMovieClipUser(MovieClipUser *imageuser) { - this->m_movieClipUser = imageuser; + m_movieClipUser = imageuser; } void setCacheFrame(bool value) { - this->m_cacheFrame = value; + m_cacheFrame = value; } void setFramenumber(int framenumber) { - this->m_framenumber = framenumber; + m_framenumber = framenumber; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc index ff942fda54f..ec17c2d030f 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc @@ -27,20 +27,20 @@ MovieDistortionOperation::MovieDistortionOperation(bool distortion) this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - this->m_inputOperation = nullptr; - this->m_movieClip = nullptr; - this->m_apply = distortion; + m_inputOperation = nullptr; + m_movieClip = nullptr; + m_apply = distortion; } void MovieDistortionOperation::init_data() { - if (this->m_movieClip) { - MovieTracking *tracking = &this->m_movieClip->tracking; + if (m_movieClip) { + MovieTracking *tracking = &m_movieClip->tracking; MovieClipUser clipUser = {0}; int calibration_width, calibration_height; - BKE_movieclip_user_set_frame(&clipUser, this->m_framenumber); - BKE_movieclip_get_size(this->m_movieClip, &clipUser, &calibration_width, &calibration_height); + BKE_movieclip_user_set_frame(&clipUser, m_framenumber); + BKE_movieclip_get_size(m_movieClip, &clipUser, &calibration_width, &calibration_height); float delta[2]; rcti full_frame; @@ -48,7 +48,7 @@ void MovieDistortionOperation::init_data() full_frame.xmax = this->getWidth(); full_frame.ymax = this->getHeight(); BKE_tracking_max_distortion_delta_across_bound( - tracking, this->getWidth(), this->getHeight(), &full_frame, !this->m_apply, delta); + tracking, this->getWidth(), this->getHeight(), &full_frame, !m_apply, delta); /* 5 is just in case we didn't hit real max of distortion in * BKE_tracking_max_undistortion_delta_across_bound @@ -56,9 +56,9 @@ void MovieDistortionOperation::init_data() m_margin[0] = delta[0] + 5; m_margin[1] = delta[1] + 5; - this->m_calibration_width = calibration_width; - this->m_calibration_height = calibration_height; - this->m_pixel_aspect = tracking->camera.pixel_aspect; + m_calibration_width = calibration_width; + m_calibration_height = calibration_height; + m_pixel_aspect = tracking->camera.pixel_aspect; } else { m_margin[0] = m_margin[1] = 0; @@ -80,10 +80,10 @@ void MovieDistortionOperation::initExecution() void MovieDistortionOperation::deinitExecution() { - this->m_inputOperation = nullptr; - this->m_movieClip = nullptr; - if (this->m_distortion != nullptr) { - BKE_tracking_distortion_free(this->m_distortion); + m_inputOperation = nullptr; + m_movieClip = nullptr; + if (m_distortion != nullptr) { + BKE_tracking_distortion_free(m_distortion); } } @@ -92,33 +92,33 @@ void MovieDistortionOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - if (this->m_distortion != nullptr) { + if (m_distortion != nullptr) { /* float overscan = 0.0f; */ - const float pixel_aspect = this->m_pixel_aspect; + const float pixel_aspect = m_pixel_aspect; const float w = (float)this->getWidth() /* / (1 + overscan) */; const float h = (float)this->getHeight() /* / (1 + overscan) */; - const float aspx = w / (float)this->m_calibration_width; - const float aspy = h / (float)this->m_calibration_height; + const float aspx = w / (float)m_calibration_width; + const float aspy = h / (float)m_calibration_height; float in[2]; float out[2]; in[0] = (x /* - 0.5 * overscan * w */) / aspx; in[1] = (y /* - 0.5 * overscan * h */) / aspy / pixel_aspect; - if (this->m_apply) { - BKE_tracking_distortion_undistort_v2(this->m_distortion, in, out); + if (m_apply) { + BKE_tracking_distortion_undistort_v2(m_distortion, in, out); } else { - BKE_tracking_distortion_distort_v2(this->m_distortion, in, out); + BKE_tracking_distortion_distort_v2(m_distortion, in, out); } float u = out[0] * aspx /* + 0.5 * overscan * w */, v = (out[1] * aspy /* + 0.5 * overscan * h */) * pixel_aspect; - this->m_inputOperation->readSampled(output, u, v, PixelSampler::Bilinear); + m_inputOperation->readSampled(output, u, v, PixelSampler::Bilinear); } else { - this->m_inputOperation->readSampled(output, x, y, PixelSampler::Bilinear); + m_inputOperation->readSampled(output, x, y, PixelSampler::Bilinear); } } @@ -151,28 +151,28 @@ void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output Span inputs) { const MemoryBuffer *input_img = inputs[0]; - if (this->m_distortion == nullptr) { + if (m_distortion == nullptr) { output->copy_from(input_img, area); return; } /* `float overscan = 0.0f;` */ - const float pixel_aspect = this->m_pixel_aspect; + const float pixel_aspect = m_pixel_aspect; const float w = (float)this->getWidth() /* `/ (1 + overscan)` */; const float h = (float)this->getHeight() /* `/ (1 + overscan)` */; - const float aspx = w / (float)this->m_calibration_width; - const float aspy = h / (float)this->m_calibration_height; + const float aspx = w / (float)m_calibration_width; + const float aspy = h / (float)m_calibration_height; float xy[2]; float distorted_xy[2]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { xy[0] = (it.x /* `- 0.5 * overscan * w` */) / aspx; xy[1] = (it.y /* `- 0.5 * overscan * h` */) / aspy / pixel_aspect; - if (this->m_apply) { - BKE_tracking_distortion_undistort_v2(this->m_distortion, xy, distorted_xy); + if (m_apply) { + BKE_tracking_distortion_undistort_v2(m_distortion, xy, distorted_xy); } else { - BKE_tracking_distortion_distort_v2(this->m_distortion, xy, distorted_xy); + BKE_tracking_distortion_distort_v2(m_distortion, xy, distorted_xy); } const float u = distorted_xy[0] * aspx /* `+ 0.5 * overscan * w` */; diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h index abf0553b75b..a3630504442 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h @@ -50,11 +50,11 @@ class MovieDistortionOperation : public MultiThreadedOperation { void setMovieClip(MovieClip *clip) { - this->m_movieClip = clip; + m_movieClip = clip; } void setFramenumber(int framenumber) { - this->m_framenumber = framenumber; + m_framenumber = framenumber; } bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc index 2974cd8dbd1..20cea6ad659 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc @@ -26,27 +26,27 @@ MultilayerBaseOperation::MultilayerBaseOperation(RenderLayer *render_layer, RenderPass *render_pass, int view) { - this->m_passId = BLI_findindex(&render_layer->passes, render_pass); - this->m_view = view; - this->m_renderLayer = render_layer; - this->m_renderPass = render_pass; + m_passId = BLI_findindex(&render_layer->passes, render_pass); + m_view = view; + m_renderLayer = render_layer; + m_renderPass = render_pass; } ImBuf *MultilayerBaseOperation::getImBuf() { /* temporarily changes the view to get the right ImBuf */ - int view = this->m_imageUser->view; + int view = m_imageUser->view; - this->m_imageUser->view = this->m_view; - this->m_imageUser->pass = this->m_passId; + m_imageUser->view = m_view; + m_imageUser->pass = m_passId; - if (BKE_image_multilayer_index(this->m_image->rr, this->m_imageUser)) { + if (BKE_image_multilayer_index(m_image->rr, m_imageUser)) { ImBuf *ibuf = BaseImageOperation::getImBuf(); - this->m_imageUser->view = view; + m_imageUser->view = view; return ibuf; } - this->m_imageUser->view = view; + m_imageUser->view = view; return nullptr; } @@ -59,12 +59,12 @@ void MultilayerBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, std::unique_ptr MultilayerColorOperation::getMetaData() { - BLI_assert(this->m_buffer); + BLI_assert(m_buffer); MetaDataExtractCallbackData callback_data = {nullptr}; - RenderResult *render_result = this->m_image->rr; + RenderResult *render_result = m_image->rr; if (render_result && render_result->stamp_data) { - RenderLayer *render_layer = this->m_renderLayer; - RenderPass *render_pass = this->m_renderPass; + RenderLayer *render_layer = m_renderLayer; + RenderPass *render_pass = m_renderPass; std::string full_layer_name = std::string(render_layer->name, BLI_strnlen(render_layer->name, sizeof(render_layer->name))) + @@ -88,20 +88,20 @@ void MultilayerColorOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - if (this->m_imageFloatBuffer == nullptr) { + if (m_imageFloatBuffer == nullptr) { zero_v4(output); } else { - if (this->m_numberOfChannels == 4) { + if (m_numberOfChannels == 4) { switch (sampler) { case PixelSampler::Nearest: - nearest_interpolation_color(this->m_buffer, nullptr, output, x, y); + nearest_interpolation_color(m_buffer, nullptr, output, x, y); break; case PixelSampler::Bilinear: - bilinear_interpolation_color(this->m_buffer, nullptr, output, x, y); + bilinear_interpolation_color(m_buffer, nullptr, output, x, y); break; case PixelSampler::Bicubic: - bicubic_interpolation_color(this->m_buffer, nullptr, output, x, y); + bicubic_interpolation_color(m_buffer, nullptr, output, x, y); break; } } @@ -114,7 +114,7 @@ void MultilayerColorOperation::executePixelSampled(float output[4], } else { int offset = (yi * this->getWidth() + xi) * 3; - copy_v3_v3(output, &this->m_imageFloatBuffer[offset]); + copy_v3_v3(output, &m_imageFloatBuffer[offset]); } } } @@ -125,7 +125,7 @@ void MultilayerValueOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - if (this->m_imageFloatBuffer == nullptr) { + if (m_imageFloatBuffer == nullptr) { output[0] = 0.0f; } else { @@ -136,7 +136,7 @@ void MultilayerValueOperation::executePixelSampled(float output[4], output[0] = 0.0f; } else { - float result = this->m_imageFloatBuffer[yi * this->getWidth() + xi]; + float result = m_imageFloatBuffer[yi * this->getWidth() + xi]; output[0] = result; } } @@ -147,7 +147,7 @@ void MultilayerVectorOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - if (this->m_imageFloatBuffer == nullptr) { + if (m_imageFloatBuffer == nullptr) { output[0] = 0.0f; } else { @@ -159,7 +159,7 @@ void MultilayerVectorOperation::executePixelSampled(float output[4], } else { int offset = (yi * this->getWidth() + xi) * 3; - copy_v3_v3(output, &this->m_imageFloatBuffer[offset]); + copy_v3_v3(output, &m_imageFloatBuffer[offset]); } } } diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc index 7d79b375b45..f3388397a79 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.cc +++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc @@ -24,14 +24,14 @@ NormalizeOperation::NormalizeOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - this->m_imageReader = nullptr; - this->m_cachedInstance = nullptr; + m_imageReader = nullptr; + m_cachedInstance = nullptr; this->flags.complex = true; flags.can_be_constant = true; } void NormalizeOperation::initExecution() { - this->m_imageReader = this->getInputSocketReader(0); + m_imageReader = this->getInputSocketReader(0); NodeOperation::initMutex(); } @@ -40,7 +40,7 @@ void NormalizeOperation::executePixel(float output[4], int x, int y, void *data) /* using generic two floats struct to store `x: min`, `y: multiply` */ NodeTwoFloats *minmult = (NodeTwoFloats *)data; - this->m_imageReader->read(output, x, y, nullptr); + m_imageReader->read(output, x, y, nullptr); output[0] = (output[0] - minmult->x) * minmult->y; @@ -55,8 +55,8 @@ void NormalizeOperation::executePixel(float output[4], int x, int y, void *data) void NormalizeOperation::deinitExecution() { - this->m_imageReader = nullptr; - delete this->m_cachedInstance; + m_imageReader = nullptr; + delete m_cachedInstance; m_cachedInstance = nullptr; NodeOperation::deinitMutex(); } @@ -66,7 +66,7 @@ bool NormalizeOperation::determineDependingAreaOfInterest(rcti * /*input*/, rcti *output) { rcti imageInput; - if (this->m_cachedInstance) { + if (m_cachedInstance) { return false; } @@ -89,8 +89,8 @@ bool NormalizeOperation::determineDependingAreaOfInterest(rcti * /*input*/, void *NormalizeOperation::initializeTileData(rcti *rect) { lockMutex(); - if (this->m_cachedInstance == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_imageReader->initializeTileData(rect); + if (m_cachedInstance == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)m_imageReader->initializeTileData(rect); /* using generic two floats struct to store `x: min`, `y: multiply`. */ NodeTwoFloats *minmult = new NodeTwoFloats(); @@ -117,11 +117,11 @@ void *NormalizeOperation::initializeTileData(rcti *rect) /* The rare case of flat buffer would cause a divide by 0 */ minmult->y = ((maxv != minv) ? 1.0f / (maxv - minv) : 0.0f); - this->m_cachedInstance = minmult; + m_cachedInstance = minmult; } unlockMutex(); - return this->m_cachedInstance; + return m_cachedInstance; } void NormalizeOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index cc177a82056..1a49d83af54 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -56,27 +56,26 @@ void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filenam exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) { + if (!BKE_scene_multiview_is_render_view_first(m_rd, m_viewName)) { return exrhandle; } IMB_exr_clear_channels(exrhandle); - for (srv = (SceneRenderView *)this->m_rd->views.first; srv; srv = srv->next) { - if (BKE_scene_multiview_is_render_view_active(this->m_rd, srv) == false) { + for (srv = (SceneRenderView *)m_rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(m_rd, srv) == false) { continue; } IMB_exr_add_view(exrhandle, srv->name); - add_exr_channels(exrhandle, nullptr, this->m_datatype, srv->name, width, false, nullptr); + add_exr_channels(exrhandle, nullptr, m_datatype, srv->name, width, false, nullptr); } BLI_make_existing_file(filename); /* prepare the file with all the channels */ - if (!IMB_exr_begin_write( - exrhandle, filename, width, height, this->m_format->exr_codec, nullptr)) { + if (!IMB_exr_begin_write(exrhandle, filename, width, height, m_format->exr_codec, nullptr)) { printf("Error Writing Singlelayer Multiview Openexr\n"); IMB_exr_close(exrhandle); } @@ -98,33 +97,33 @@ void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution() char filename[FILE_MAX]; BKE_image_path_from_imtype(filename, - this->m_path, + m_path, BKE_main_blendfile_path_from_global(), - this->m_rd->cfra, + m_rd->cfra, R_IMF_IMTYPE_OPENEXR, - (this->m_rd->scemode & R_EXTENSION) != 0, + (m_rd->scemode & R_EXTENSION) != 0, true, nullptr); exrhandle = this->get_handle(filename); add_exr_channels(exrhandle, nullptr, - this->m_datatype, - this->m_viewName, + m_datatype, + m_viewName, width, - this->m_format->depth == R_IMF_CHAN_DEPTH_16, - this->m_outputBuffer); + m_format->depth == R_IMF_CHAN_DEPTH_16, + m_outputBuffer); /* memory can only be freed after we write all views to the file */ - this->m_outputBuffer = nullptr; - this->m_imageInput = nullptr; + m_outputBuffer = nullptr; + m_imageInput = nullptr; /* ready to close the file */ - if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + if (BKE_scene_multiview_is_render_view_last(m_rd, m_viewName)) { IMB_exr_write_channels(exrhandle); /* free buffer memory for all the views */ - free_exr_channels(exrhandle, this->m_rd, nullptr, this->m_datatype); + free_exr_channels(exrhandle, m_rd, nullptr, m_datatype); /* remove exr handle and data */ IMB_exr_close(exrhandle); @@ -159,28 +158,28 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename /* get a new global handle */ exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) { + if (!BKE_scene_multiview_is_render_view_first(m_rd, m_viewName)) { return exrhandle; } IMB_exr_clear_channels(exrhandle); /* check renderdata for amount of views */ - for (srv = (SceneRenderView *)this->m_rd->views.first; srv; srv = srv->next) { + for (srv = (SceneRenderView *)m_rd->views.first; srv; srv = srv->next) { - if (BKE_scene_multiview_is_render_view_active(this->m_rd, srv) == false) { + if (BKE_scene_multiview_is_render_view_active(m_rd, srv) == false) { continue; } IMB_exr_add_view(exrhandle, srv->name); - for (unsigned int i = 0; i < this->m_layers.size(); i++) { + for (unsigned int i = 0; i < m_layers.size(); i++) { add_exr_channels(exrhandle, - this->m_layers[i].name, - this->m_layers[i].datatype, + m_layers[i].name, + m_layers[i].datatype, srv->name, width, - this->m_exr_half_float, + m_exr_half_float, nullptr); } } @@ -189,7 +188,7 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename /* prepare the file with all the channels for the header */ StampData *stamp_data = createStampData(); - if (!IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data)) { + if (!IMB_exr_begin_write(exrhandle, filename, width, height, m_exr_codec, stamp_data)) { printf("Error Writing Multilayer Multiview Openexr\n"); IMB_exr_close(exrhandle); BKE_stamp_data_free(stamp_data); @@ -213,40 +212,39 @@ void OutputOpenExrMultiLayerMultiViewOperation::deinitExecution() char filename[FILE_MAX]; BKE_image_path_from_imtype(filename, - this->m_path, + m_path, BKE_main_blendfile_path_from_global(), - this->m_rd->cfra, + m_rd->cfra, R_IMF_IMTYPE_MULTILAYER, - (this->m_rd->scemode & R_EXTENSION) != 0, + (m_rd->scemode & R_EXTENSION) != 0, true, nullptr); exrhandle = this->get_handle(filename); - for (unsigned int i = 0; i < this->m_layers.size(); i++) { + for (unsigned int i = 0; i < m_layers.size(); i++) { add_exr_channels(exrhandle, - this->m_layers[i].name, - this->m_layers[i].datatype, - this->m_viewName, + m_layers[i].name, + m_layers[i].datatype, + m_viewName, width, - this->m_exr_half_float, - this->m_layers[i].outputBuffer); + m_exr_half_float, + m_layers[i].outputBuffer); } - for (unsigned int i = 0; i < this->m_layers.size(); i++) { + for (unsigned int i = 0; i < m_layers.size(); i++) { /* memory can only be freed after we write all views to the file */ - this->m_layers[i].outputBuffer = nullptr; - this->m_layers[i].imageInput = nullptr; + m_layers[i].outputBuffer = nullptr; + m_layers[i].imageInput = nullptr; } /* ready to close the file */ - if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + if (BKE_scene_multiview_is_render_view_last(m_rd, m_viewName)) { IMB_exr_write_channels(exrhandle); /* free buffer memory for all the views */ - for (unsigned int i = 0; i < this->m_layers.size(); i++) { - free_exr_channels( - exrhandle, this->m_rd, this->m_layers[i].name, this->m_layers[i].datatype); + for (unsigned int i = 0; i < m_layers.size(); i++) { + free_exr_channels(exrhandle, m_rd, m_layers[i].name, m_layers[i].datatype); } IMB_exr_close(exrhandle); @@ -269,8 +267,8 @@ OutputStereoOperation::OutputStereoOperation(const RenderData *rd, : OutputSingleLayerOperation( rd, tree, datatype, format, path, viewSettings, displaySettings, viewName, saveAsRender) { - BLI_strncpy(this->m_name, name, sizeof(this->m_name)); - this->m_channels = get_datatype_size(datatype); + BLI_strncpy(m_name, name, sizeof(m_name)); + m_channels = get_datatype_size(datatype); } void *OutputStereoOperation::get_handle(const char *filename) @@ -285,7 +283,7 @@ void *OutputStereoOperation::get_handle(const char *filename) exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) { + if (!BKE_scene_multiview_is_render_view_first(m_rd, m_viewName)) { return exrhandle; } @@ -308,24 +306,24 @@ void OutputStereoOperation::deinitExecution() if (width != 0 && height != 0) { void *exrhandle; - exrhandle = this->get_handle(this->m_path); - float *buf = this->m_outputBuffer; + exrhandle = this->get_handle(m_path); + float *buf = m_outputBuffer; /* populate single EXR channel with view data */ IMB_exr_add_channel(exrhandle, nullptr, - this->m_name, - this->m_viewName, + m_name, + m_viewName, 1, - this->m_channels * width * height, + m_channels * width * height, buf, - this->m_format->depth == R_IMF_CHAN_DEPTH_16); + m_format->depth == R_IMF_CHAN_DEPTH_16); - this->m_imageInput = nullptr; - this->m_outputBuffer = nullptr; + m_imageInput = nullptr; + m_outputBuffer = nullptr; /* create stereo ibuf */ - if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + if (BKE_scene_multiview_is_render_view_last(m_rd, m_viewName)) { ImBuf *ibuf[3] = {nullptr}; const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; char filename[FILE_MAX]; @@ -333,33 +331,33 @@ void OutputStereoOperation::deinitExecution() /* get rectf from EXR */ for (i = 0; i < 2; i++) { - float *rectf = IMB_exr_channel_rect(exrhandle, nullptr, this->m_name, names[i]); - ibuf[i] = IMB_allocImBuf(width, height, this->m_format->planes, 0); + float *rectf = IMB_exr_channel_rect(exrhandle, nullptr, m_name, names[i]); + ibuf[i] = IMB_allocImBuf(width, height, m_format->planes, 0); - ibuf[i]->channels = this->m_channels; + ibuf[i]->channels = m_channels; ibuf[i]->rect_float = rectf; ibuf[i]->mall |= IB_rectfloat; - ibuf[i]->dither = this->m_rd->dither_intensity; + ibuf[i]->dither = m_rd->dither_intensity; /* do colormanagement in the individual views, so it doesn't need to do in the stereo */ IMB_colormanagement_imbuf_for_write( - ibuf[i], true, false, this->m_viewSettings, this->m_displaySettings, this->m_format); + ibuf[i], true, false, m_viewSettings, m_displaySettings, m_format); IMB_prepare_write_ImBuf(IMB_isfloat(ibuf[i]), ibuf[i]); } /* create stereo buffer */ - ibuf[2] = IMB_stereo3d_ImBuf(this->m_format, ibuf[0], ibuf[1]); + ibuf[2] = IMB_stereo3d_ImBuf(m_format, ibuf[0], ibuf[1]); BKE_image_path_from_imformat(filename, - this->m_path, + m_path, BKE_main_blendfile_path_from_global(), - this->m_rd->cfra, - this->m_format, - (this->m_rd->scemode & R_EXTENSION) != 0, + m_rd->cfra, + m_format, + (m_rd->scemode & R_EXTENSION) != 0, true, nullptr); - BKE_imbuf_write(ibuf[2], filename, this->m_format); + BKE_imbuf_write(ibuf[2], filename, m_format); /* imbuf knows which rects are not part of ibuf */ for (i = 0; i < 3; i++) { diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 6ccff9bd0ef..92eeb0b600b 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -213,69 +213,64 @@ OutputSingleLayerOperation::OutputSingleLayerOperation( const char *viewName, const bool saveAsRender) { - this->m_rd = rd; - this->m_tree = tree; + m_rd = rd; + m_tree = tree; this->addInputSocket(datatype); - this->m_outputBuffer = nullptr; - this->m_datatype = datatype; - this->m_imageInput = nullptr; + m_outputBuffer = nullptr; + m_datatype = datatype; + m_imageInput = nullptr; - this->m_format = format; - BLI_strncpy(this->m_path, path, sizeof(this->m_path)); + m_format = format; + BLI_strncpy(m_path, path, sizeof(m_path)); - this->m_viewSettings = viewSettings; - this->m_displaySettings = displaySettings; - this->m_viewName = viewName; - this->m_saveAsRender = saveAsRender; + m_viewSettings = viewSettings; + m_displaySettings = displaySettings; + m_viewName = viewName; + m_saveAsRender = saveAsRender; } void OutputSingleLayerOperation::initExecution() { - this->m_imageInput = getInputSocketReader(0); - this->m_outputBuffer = init_buffer(this->getWidth(), this->getHeight(), this->m_datatype); + m_imageInput = getInputSocketReader(0); + m_outputBuffer = init_buffer(this->getWidth(), this->getHeight(), m_datatype); } void OutputSingleLayerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) { - write_buffer_rect(rect, - this->m_tree, - this->m_imageInput, - this->m_outputBuffer, - this->getWidth(), - this->m_datatype); + write_buffer_rect(rect, m_tree, m_imageInput, m_outputBuffer, this->getWidth(), m_datatype); } void OutputSingleLayerOperation::deinitExecution() { if (this->getWidth() * this->getHeight() != 0) { - int size = get_datatype_size(this->m_datatype); - ImBuf *ibuf = IMB_allocImBuf(this->getWidth(), this->getHeight(), this->m_format->planes, 0); + int size = get_datatype_size(m_datatype); + ImBuf *ibuf = IMB_allocImBuf(this->getWidth(), this->getHeight(), m_format->planes, 0); char filename[FILE_MAX]; const char *suffix; ibuf->channels = size; - ibuf->rect_float = this->m_outputBuffer; + ibuf->rect_float = m_outputBuffer; ibuf->mall |= IB_rectfloat; - ibuf->dither = this->m_rd->dither_intensity; + ibuf->dither = m_rd->dither_intensity; IMB_colormanagement_imbuf_for_write( - ibuf, m_saveAsRender, false, m_viewSettings, m_displaySettings, this->m_format); + ibuf, m_saveAsRender, false, m_viewSettings, m_displaySettings, m_format); - suffix = BKE_scene_multiview_view_suffix_get(this->m_rd, this->m_viewName); + suffix = BKE_scene_multiview_view_suffix_get(m_rd, m_viewName); BKE_image_path_from_imformat(filename, - this->m_path, + m_path, BKE_main_blendfile_path_from_global(), - this->m_rd->cfra, - this->m_format, - (this->m_rd->scemode & R_EXTENSION) != 0, + m_rd->cfra, + m_format, + (m_rd->scemode & R_EXTENSION) != 0, true, suffix); - if (0 == BKE_imbuf_write(ibuf, filename, this->m_format)) { + if (0 == BKE_imbuf_write(ibuf, filename, m_format)) { printf("Cannot save Node File Output to %s\n", filename); } else { @@ -284,8 +279,8 @@ void OutputSingleLayerOperation::deinitExecution() IMB_freeImBuf(ibuf); } - this->m_outputBuffer = nullptr; - this->m_imageInput = nullptr; + m_outputBuffer = nullptr; + m_imageInput = nullptr; } void OutputSingleLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), @@ -296,10 +291,8 @@ void OutputSingleLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUS return; } - MemoryBuffer output_buf(m_outputBuffer, - COM_data_type_num_channels(this->m_datatype), - this->getWidth(), - this->getHeight()); + MemoryBuffer output_buf( + m_outputBuffer, COM_data_type_num_channels(m_datatype), this->getWidth(), this->getHeight()); const MemoryBuffer *input_image = inputs[0]; output_buf.copy_from(input_image, area); } @@ -325,14 +318,14 @@ OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene * bool exr_half_float, const char *viewName) { - this->m_scene = scene; - this->m_rd = rd; - this->m_tree = tree; + m_scene = scene; + m_rd = rd; + m_tree = tree; - BLI_strncpy(this->m_path, path, sizeof(this->m_path)); - this->m_exr_codec = exr_codec; - this->m_exr_half_float = exr_half_float; - this->m_viewName = viewName; + BLI_strncpy(m_path, path, sizeof(m_path)); + m_exr_codec = exr_codec; + m_exr_half_float = exr_half_float; + m_viewName = viewName; this->set_canvas_input_index(RESOLUTION_INPUT_ANY); } @@ -341,7 +334,7 @@ void OutputOpenExrMultiLayerOperation::add_layer(const char *name, bool use_layer) { this->addInputSocket(datatype); - this->m_layers.append(OutputOpenExrLayer(name, datatype, use_layer)); + m_layers.append(OutputOpenExrLayer(name, datatype, use_layer)); } StampData *OutputOpenExrMultiLayerOperation::createStampData() const @@ -370,27 +363,23 @@ StampData *OutputOpenExrMultiLayerOperation::createStampData() const void OutputOpenExrMultiLayerOperation::initExecution() { - for (unsigned int i = 0; i < this->m_layers.size(); i++) { - if (this->m_layers[i].use_layer) { + for (unsigned int i = 0; i < m_layers.size(); i++) { + if (m_layers[i].use_layer) { SocketReader *reader = getInputSocketReader(i); - this->m_layers[i].imageInput = reader; - this->m_layers[i].outputBuffer = init_buffer( - this->getWidth(), this->getHeight(), this->m_layers[i].datatype); + m_layers[i].imageInput = reader; + m_layers[i].outputBuffer = init_buffer( + this->getWidth(), this->getHeight(), m_layers[i].datatype); } } } void OutputOpenExrMultiLayerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) { - for (unsigned int i = 0; i < this->m_layers.size(); i++) { - OutputOpenExrLayer &layer = this->m_layers[i]; + for (unsigned int i = 0; i < m_layers.size(); i++) { + OutputOpenExrLayer &layer = m_layers[i]; if (layer.imageInput) { - write_buffer_rect(rect, - this->m_tree, - layer.imageInput, - layer.outputBuffer, - this->getWidth(), - layer.datatype); + write_buffer_rect( + rect, m_tree, layer.imageInput, layer.outputBuffer, this->getWidth(), layer.datatype); } } } @@ -404,35 +393,35 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() const char *suffix; void *exrhandle = IMB_exr_get_handle(); - suffix = BKE_scene_multiview_view_suffix_get(this->m_rd, this->m_viewName); + suffix = BKE_scene_multiview_view_suffix_get(m_rd, m_viewName); BKE_image_path_from_imtype(filename, - this->m_path, + m_path, BKE_main_blendfile_path_from_global(), - this->m_rd->cfra, + m_rd->cfra, R_IMF_IMTYPE_MULTILAYER, - (this->m_rd->scemode & R_EXTENSION) != 0, + (m_rd->scemode & R_EXTENSION) != 0, true, suffix); BLI_make_existing_file(filename); - for (unsigned int i = 0; i < this->m_layers.size(); i++) { - OutputOpenExrLayer &layer = this->m_layers[i]; + for (unsigned int i = 0; i < m_layers.size(); i++) { + OutputOpenExrLayer &layer = m_layers[i]; if (!layer.imageInput) { continue; /* skip unconnected sockets */ } add_exr_channels(exrhandle, - this->m_layers[i].name, - this->m_layers[i].datatype, + m_layers[i].name, + m_layers[i].datatype, "", width, - this->m_exr_half_float, - this->m_layers[i].outputBuffer); + m_exr_half_float, + m_layers[i].outputBuffer); } /* when the filename has no permissions, this can fail */ StampData *stamp_data = createStampData(); - if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data)) { + if (IMB_exr_begin_write(exrhandle, filename, width, height, m_exr_codec, stamp_data)) { IMB_exr_write_channels(exrhandle); } else { @@ -442,13 +431,13 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() } IMB_exr_close(exrhandle); - for (unsigned int i = 0; i < this->m_layers.size(); i++) { - if (this->m_layers[i].outputBuffer) { - MEM_freeN(this->m_layers[i].outputBuffer); - this->m_layers[i].outputBuffer = nullptr; + for (unsigned int i = 0; i < m_layers.size(); i++) { + if (m_layers[i].outputBuffer) { + MEM_freeN(m_layers[i].outputBuffer); + m_layers[i].outputBuffer = nullptr; } - this->m_layers[i].imageInput = nullptr; + m_layers[i].imageInput = nullptr; } BKE_stamp_data_free(stamp_data); } @@ -459,8 +448,8 @@ void OutputOpenExrMultiLayerOperation::update_memory_buffer_partial(MemoryBuffer Span inputs) { const MemoryBuffer *input_image = inputs[0]; - for (int i = 0; i < this->m_layers.size(); i++) { - OutputOpenExrLayer &layer = this->m_layers[i]; + for (int i = 0; i < m_layers.size(); i++) { + OutputOpenExrLayer &layer = m_layers[i]; if (layer.outputBuffer) { MemoryBuffer output_buf(layer.outputBuffer, COM_data_type_num_channels(layer.datatype), diff --git a/source/blender/compositor/operations/COM_PixelateOperation.cc b/source/blender/compositor/operations/COM_PixelateOperation.cc index 4fc238bc094..b9a18756550 100644 --- a/source/blender/compositor/operations/COM_PixelateOperation.cc +++ b/source/blender/compositor/operations/COM_PixelateOperation.cc @@ -25,17 +25,17 @@ PixelateOperation::PixelateOperation(DataType datatype) this->addInputSocket(datatype); this->addOutputSocket(datatype); this->set_canvas_input_index(0); - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } void PixelateOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); + m_inputOperation = this->getInputSocketReader(0); } void PixelateOperation::deinitExecution() { - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } void PixelateOperation::executePixelSampled(float output[4], @@ -45,7 +45,7 @@ void PixelateOperation::executePixelSampled(float output[4], { float nx = round(x); float ny = round(y); - this->m_inputOperation->readSampled(output, nx, ny, sampler); + m_inputOperation->readSampled(output, nx, ny, sampler); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 259c772e296..9bed0ca14cb 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -33,8 +33,8 @@ void PlaneDistortBaseOperation::calculateCorners(const float corners[4][2], bool normalized, int sample) { - BLI_assert(sample < this->m_motion_blur_samples); - MotionSample *sample_data = &this->m_samples[sample]; + BLI_assert(sample < m_motion_blur_samples); + MotionSample *sample_data = &m_samples[sample]; if (normalized) { for (int i = 0; i < 4; i++) { sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); @@ -68,7 +68,7 @@ PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortB { this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Color); - this->m_pixelReader = nullptr; + m_pixelReader = nullptr; this->flags.complex = true; } @@ -83,19 +83,19 @@ void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], const int height = image->getHeight(); float frame_corners[4][2] = { {0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}}; - MotionSample *sample_data = &this->m_samples[sample]; + MotionSample *sample_data = &m_samples[sample]; BKE_tracking_homography_between_two_quads( sample_data->frameSpaceCorners, frame_corners, sample_data->perspectiveMatrix); } void PlaneDistortWarpImageOperation::initExecution() { - this->m_pixelReader = this->getInputSocketReader(0); + m_pixelReader = this->getInputSocketReader(0); } void PlaneDistortWarpImageOperation::deinitExecution() { - this->m_pixelReader = nullptr; + m_pixelReader = nullptr; } void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], @@ -105,19 +105,19 @@ void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], { float uv[2]; float deriv[2][2]; - if (this->m_motion_blur_samples == 1) { - warpCoord(x, y, this->m_samples[0].perspectiveMatrix, uv, deriv); + if (m_motion_blur_samples == 1) { + warpCoord(x, y, m_samples[0].perspectiveMatrix, uv, deriv); m_pixelReader->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); } else { zero_v4(output); - for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { + for (int sample = 0; sample < m_motion_blur_samples; sample++) { float color[4]; - warpCoord(x, y, this->m_samples[sample].perspectiveMatrix, uv, deriv); + warpCoord(x, y, m_samples[sample].perspectiveMatrix, uv, deriv); m_pixelReader->readFiltered(color, uv[0], uv[1], deriv[0], deriv[1]); add_v4_v4(output, color); } - mul_v4_fl(output, 1.0f / (float)this->m_motion_blur_samples); + mul_v4_fl(output, 1.0f / (float)m_motion_blur_samples); } } @@ -129,22 +129,22 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer * float uv[2]; float deriv[2][2]; BuffersIterator it = output->iterate_with({}, area); - if (this->m_motion_blur_samples == 1) { + if (m_motion_blur_samples == 1) { for (; !it.is_end(); ++it) { - warpCoord(it.x, it.y, this->m_samples[0].perspectiveMatrix, uv, deriv); + warpCoord(it.x, it.y, m_samples[0].perspectiveMatrix, uv, deriv); input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); } } else { for (; !it.is_end(); ++it) { zero_v4(it.out); - for (const int sample : IndexRange(this->m_motion_blur_samples)) { + for (const int sample : IndexRange(m_motion_blur_samples)) { float color[4]; - warpCoord(it.x, it.y, this->m_samples[sample].perspectiveMatrix, uv, deriv); + warpCoord(it.x, it.y, m_samples[sample].perspectiveMatrix, uv, deriv); input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], color); add_v4_v4(it.out, color); } - mul_v4_fl(it.out, 1.0f / (float)this->m_motion_blur_samples); + mul_v4_fl(it.out, 1.0f / (float)m_motion_blur_samples); } } } @@ -155,10 +155,10 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( float min[2], max[2]; INIT_MINMAX2(min, max); - for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { + for (int sample = 0; sample < m_motion_blur_samples; sample++) { float UVs[4][2]; float deriv[2][2]; - MotionSample *sample_data = &this->m_samples[sample]; + MotionSample *sample_data = &m_samples[sample]; /* TODO(sergey): figure out proper way to do this. */ warpCoord(input->xmin - 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); warpCoord(input->xmax + 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv); @@ -196,10 +196,10 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, #if 0 float min[2], max[2]; INIT_MINMAX2(min, max); - for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { + for (int sample = 0; sample < m_motion_blur_samples; sample++) { float UVs[4][2]; float deriv[2][2]; - MotionSample *sample_data = &this->m_samples[sample]; + MotionSample *sample_data = &m_samples[sample]; /* TODO(sergey): figure out proper way to do this. */ warpCoord( output_area.xmin - 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); @@ -243,11 +243,11 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4], { float point[2]; int inside_counter = 0; - if (this->m_motion_blur_samples == 1) { - MotionSample *sample_data = &this->m_samples[0]; - for (int sample = 0; sample < this->m_osa; sample++) { - point[0] = x + this->m_jitter[sample][0]; - point[1] = y + this->m_jitter[sample][1]; + if (m_motion_blur_samples == 1) { + MotionSample *sample_data = &m_samples[0]; + for (int sample = 0; sample < m_osa; sample++) { + point[0] = x + m_jitter[sample][0]; + point[1] = y + m_jitter[sample][1]; if (isect_point_tri_v2(point, sample_data->frameSpaceCorners[0], sample_data->frameSpaceCorners[1], @@ -259,14 +259,14 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4], inside_counter++; } } - output[0] = (float)inside_counter / this->m_osa; + output[0] = (float)inside_counter / m_osa; } else { - for (int motion_sample = 0; motion_sample < this->m_motion_blur_samples; motion_sample++) { - MotionSample *sample_data = &this->m_samples[motion_sample]; - for (int osa_sample = 0; osa_sample < this->m_osa; osa_sample++) { - point[0] = x + this->m_jitter[osa_sample][0]; - point[1] = y + this->m_jitter[osa_sample][1]; + for (int motion_sample = 0; motion_sample < m_motion_blur_samples; motion_sample++) { + MotionSample *sample_data = &m_samples[motion_sample]; + for (int osa_sample = 0; osa_sample < m_osa; osa_sample++) { + point[0] = x + m_jitter[osa_sample][0]; + point[1] = y + m_jitter[osa_sample][1]; if (isect_point_tri_v2(point, sample_data->frameSpaceCorners[0], sample_data->frameSpaceCorners[1], @@ -279,7 +279,7 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4], } } } - output[0] = (float)inside_counter / (this->m_osa * this->m_motion_blur_samples); + output[0] = (float)inside_counter / (m_osa * m_motion_blur_samples); } } @@ -289,11 +289,11 @@ void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *outpu { for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { int inside_count = 0; - for (const int motion_sample : IndexRange(this->m_motion_blur_samples)) { - MotionSample &sample = this->m_samples[motion_sample]; + for (const int motion_sample : IndexRange(m_motion_blur_samples)) { + MotionSample &sample = m_samples[motion_sample]; inside_count += get_jitter_samples_inside_count(it.x, it.y, sample); } - *it.out = (float)inside_count / (this->m_osa * this->m_motion_blur_samples); + *it.out = (float)inside_count / (m_osa * m_motion_blur_samples); } } @@ -303,9 +303,9 @@ int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x, { float point[2]; int inside_count = 0; - for (int sample = 0; sample < this->m_osa; sample++) { - point[0] = x + this->m_jitter[sample][0]; - point[1] = y + this->m_jitter[sample][1]; + for (int sample = 0; sample < m_osa; sample++) { + point[0] = x + m_jitter[sample][0]; + point[1] = y + m_jitter[sample][1]; if (isect_point_tri_v2(point, sample_data.frameSpaceCorners[0], sample_data.frameSpaceCorners[1], diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h index 3ef9c1dfab8..7b863d562ab 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h @@ -48,11 +48,11 @@ class PlaneDistortBaseOperation : public MultiThreadedOperation { void setMotionBlurSamples(int samples) { BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); - this->m_motion_blur_samples = samples; + m_motion_blur_samples = samples; } void setMotionBlurShutter(float shutter) { - this->m_motion_blur_shutter = shutter; + m_motion_blur_shutter = shutter; } virtual void calculateCorners(const float corners[4][2], bool normalized, int sample); diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc index 593c3604568..64e31cc01e2 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc @@ -27,21 +27,21 @@ namespace blender::compositor { PlaneTrackCommon::PlaneTrackCommon() { - this->m_movieClip = nullptr; - this->m_framenumber = 0; - this->m_trackingObjectName[0] = '\0'; - this->m_planeTrackName[0] = '\0'; + m_movieClip = nullptr; + m_framenumber = 0; + m_trackingObjectName[0] = '\0'; + m_planeTrackName[0] = '\0'; } void PlaneTrackCommon::read_and_calculate_corners(PlaneDistortBaseOperation *distort_op) { float corners[4][2]; if (distort_op->m_motion_blur_samples == 1) { - readCornersFromTrack(corners, this->m_framenumber); + readCornersFromTrack(corners, m_framenumber); distort_op->calculateCorners(corners, true, 0); } else { - const float frame = (float)this->m_framenumber - distort_op->m_motion_blur_shutter; + const float frame = (float)m_framenumber - distort_op->m_motion_blur_shutter; const float frame_step = (distort_op->m_motion_blur_shutter * 2.0f) / distort_op->m_motion_blur_samples; float frame_iter = frame; @@ -58,18 +58,18 @@ void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) MovieTracking *tracking; MovieTrackingObject *object; - if (!this->m_movieClip) { + if (!m_movieClip) { return; } - tracking = &this->m_movieClip->tracking; + tracking = &m_movieClip->tracking; - object = BKE_tracking_object_get_named(tracking, this->m_trackingObjectName); + object = BKE_tracking_object_get_named(tracking, m_trackingObjectName); if (object) { MovieTrackingPlaneTrack *plane_track; - plane_track = BKE_tracking_plane_track_get_named(tracking, object, this->m_planeTrackName); + plane_track = BKE_tracking_plane_track_get_named(tracking, object, m_planeTrackName); if (plane_track) { - float clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, frame); + float clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, frame); BKE_tracking_plane_marker_get_subframe_corners(plane_track, clip_framenr, corners); } } @@ -78,11 +78,11 @@ void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) void PlaneTrackCommon::determine_canvas(const rcti &preferred_area, rcti &r_area) { r_area = COM_AREA_NONE; - if (this->m_movieClip) { + if (m_movieClip) { int width, height; MovieClipUser user = {0}; - BKE_movieclip_user_set_frame(&user, this->m_framenumber); - BKE_movieclip_get_size(this->m_movieClip, &user, &width, &height); + BKE_movieclip_user_set_frame(&user, m_framenumber); + BKE_movieclip_get_size(m_movieClip, &user, &width, &height); r_area = preferred_area; r_area.xmax = r_area.xmin + width; r_area.ymax = r_area.ymin + height; diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h index 60845aac514..1890195d14a 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h @@ -48,19 +48,19 @@ class PlaneTrackCommon { void setMovieClip(MovieClip *clip) { - this->m_movieClip = clip; + m_movieClip = clip; } void setTrackingObject(char *object) { - BLI_strncpy(this->m_trackingObjectName, object, sizeof(this->m_trackingObjectName)); + BLI_strncpy(m_trackingObjectName, object, sizeof(m_trackingObjectName)); } void setPlaneTrackName(char *plane_track) { - BLI_strncpy(this->m_planeTrackName, plane_track, sizeof(this->m_planeTrackName)); + BLI_strncpy(m_planeTrackName, plane_track, sizeof(m_planeTrackName)); } void setFramenumber(int framenumber) { - this->m_framenumber = framenumber; + m_framenumber = framenumber; } private: diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.cc b/source/blender/compositor/operations/COM_PosterizeOperation.cc index db5860f48f8..dd1c5cae1bb 100644 --- a/source/blender/compositor/operations/COM_PosterizeOperation.cc +++ b/source/blender/compositor/operations/COM_PosterizeOperation.cc @@ -25,15 +25,15 @@ PosterizeOperation::PosterizeOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; - this->m_inputStepsProgram = nullptr; + m_inputProgram = nullptr; + m_inputStepsProgram = nullptr; flags.can_be_constant = true; } void PosterizeOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputStepsProgram = this->getInputSocketReader(1); + m_inputProgram = this->getInputSocketReader(0); + m_inputStepsProgram = this->getInputSocketReader(1); } void PosterizeOperation::executePixelSampled(float output[4], @@ -44,8 +44,8 @@ void PosterizeOperation::executePixelSampled(float output[4], float inputValue[4]; float inputSteps[4]; - this->m_inputProgram->readSampled(inputValue, x, y, sampler); - this->m_inputStepsProgram->readSampled(inputSteps, x, y, sampler); + m_inputProgram->readSampled(inputValue, x, y, sampler); + m_inputStepsProgram->readSampled(inputSteps, x, y, sampler); CLAMP(inputSteps[0], 2.0f, 1024.0f); const float steps_inv = 1.0f / inputSteps[0]; @@ -75,8 +75,8 @@ void PosterizeOperation::update_memory_buffer_partial(MemoryBuffer *output, void PosterizeOperation::deinitExecution() { - this->m_inputProgram = nullptr; - this->m_inputStepsProgram = nullptr; + m_inputProgram = nullptr; + m_inputStepsProgram = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc index c08ce82690d..707706a9d07 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cc +++ b/source/blender/compositor/operations/COM_PreviewOperation.cc @@ -30,14 +30,14 @@ PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings, { this->addInputSocket(DataType::Color, ResizeMode::Align); - this->m_preview = nullptr; - this->m_outputBuffer = nullptr; - this->m_input = nullptr; - this->m_divider = 1.0f; - this->m_viewSettings = viewSettings; - this->m_displaySettings = displaySettings; - this->m_defaultWidth = defaultWidth; - this->m_defaultHeight = defaultHeight; + m_preview = nullptr; + m_outputBuffer = nullptr; + m_input = nullptr; + m_divider = 1.0f; + m_viewSettings = viewSettings; + m_displaySettings = displaySettings; + m_defaultWidth = defaultWidth; + m_defaultHeight = defaultHeight; flags.use_viewer_border = true; flags.is_preview_operation = true; } @@ -47,34 +47,34 @@ void PreviewOperation::verifyPreview(bNodeInstanceHash *previews, bNodeInstanceK /* Size (0, 0) ensures the preview rect is not allocated in advance, * this is set later in initExecution once the resolution is determined. */ - this->m_preview = BKE_node_preview_verify(previews, key, 0, 0, true); + m_preview = BKE_node_preview_verify(previews, key, 0, 0, true); } void PreviewOperation::initExecution() { - this->m_input = getInputSocketReader(0); + m_input = getInputSocketReader(0); - if (this->getWidth() == (unsigned int)this->m_preview->xsize && - this->getHeight() == (unsigned int)this->m_preview->ysize) { - this->m_outputBuffer = this->m_preview->rect; + if (this->getWidth() == (unsigned int)m_preview->xsize && + this->getHeight() == (unsigned int)m_preview->ysize) { + m_outputBuffer = m_preview->rect; } - if (this->m_outputBuffer == nullptr) { - this->m_outputBuffer = (unsigned char *)MEM_callocN( + if (m_outputBuffer == nullptr) { + m_outputBuffer = (unsigned char *)MEM_callocN( sizeof(unsigned char) * 4 * getWidth() * getHeight(), "PreviewOperation"); - if (this->m_preview->rect) { - MEM_freeN(this->m_preview->rect); + if (m_preview->rect) { + MEM_freeN(m_preview->rect); } - this->m_preview->xsize = getWidth(); - this->m_preview->ysize = getHeight(); - this->m_preview->rect = this->m_outputBuffer; + m_preview->xsize = getWidth(); + m_preview->ysize = getHeight(); + m_preview->rect = m_outputBuffer; } } void PreviewOperation::deinitExecution() { - this->m_outputBuffer = nullptr; - this->m_input = nullptr; + m_outputBuffer = nullptr; + m_input = nullptr; } void PreviewOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) @@ -83,22 +83,21 @@ void PreviewOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) float color[4]; struct ColormanageProcessor *cm_processor; - cm_processor = IMB_colormanagement_display_processor_new(this->m_viewSettings, - this->m_displaySettings); + cm_processor = IMB_colormanagement_display_processor_new(m_viewSettings, m_displaySettings); for (int y = rect->ymin; y < rect->ymax; y++) { offset = (y * getWidth() + rect->xmin) * 4; for (int x = rect->xmin; x < rect->xmax; x++) { - float rx = floor(x / this->m_divider); - float ry = floor(y / this->m_divider); + float rx = floor(x / m_divider); + float ry = floor(y / m_divider); color[0] = 0.0f; color[1] = 0.0f; color[2] = 0.0f; color[3] = 1.0f; - this->m_input->readSampled(color, rx, ry, PixelSampler::Nearest); + m_input->readSampled(color, rx, ry, PixelSampler::Nearest); IMB_colormanagement_processor_apply_v4(cm_processor, color); - rgba_float_to_uchar(this->m_outputBuffer + offset, color); + rgba_float_to_uchar(m_outputBuffer + offset, color); offset += 4; } } @@ -111,10 +110,10 @@ bool PreviewOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - newInput.xmin = input->xmin / this->m_divider; - newInput.xmax = input->xmax / this->m_divider; - newInput.ymin = input->ymin / this->m_divider; - newInput.ymax = input->ymax / this->m_divider; + newInput.xmin = input->xmin / m_divider; + newInput.xmax = input->xmax / m_divider; + newInput.ymin = input->ymin / m_divider; + newInput.ymax = input->ymax / m_divider; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -122,7 +121,7 @@ void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti { /* Use default preview resolution as preferred ensuring it has size so that * generated inputs (which don't have resolution on their own) are displayed */ - BLI_assert(this->m_defaultWidth > 0 && this->m_defaultHeight > 0); + BLI_assert(m_defaultWidth > 0 && m_defaultHeight > 0); rcti local_preferred; BLI_rcti_init(&local_preferred, 0, m_defaultWidth, 0, m_defaultHeight); NodeOperation::determine_canvas(local_preferred, r_area); @@ -138,17 +137,17 @@ void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti */ int width = BLI_rcti_size_x(&r_area); int height = BLI_rcti_size_y(&r_area); - this->m_divider = 0.0f; + m_divider = 0.0f; if (width > 0 && height > 0) { if (width > height) { - this->m_divider = (float)COM_PREVIEW_SIZE / (width); + m_divider = (float)COM_PREVIEW_SIZE / (width); } else { - this->m_divider = (float)COM_PREVIEW_SIZE / (height); + m_divider = (float)COM_PREVIEW_SIZE / (height); } } - width = width * this->m_divider; - height = height * this->m_divider; + width = width * m_divider; + height = height * m_divider; BLI_rcti_init(&r_area, r_area.xmin, r_area.xmin + width, r_area.ymin, r_area.ymin + height); } diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc index fb8bd2f145f..d2d5581deb2 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc @@ -27,9 +27,9 @@ ProjectorLensDistortionOperation::ProjectorLensDistortionOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->flags.complex = true; - this->m_inputProgram = nullptr; - this->m_dispersionAvailable = false; - this->m_dispersion = 0.0f; + m_inputProgram = nullptr; + m_dispersionAvailable = false; + m_dispersion = 0.0f; } void ProjectorLensDistortionOperation::init_data() @@ -37,24 +37,23 @@ void ProjectorLensDistortionOperation::init_data() if (execution_model_ == eExecutionModel::FullFrame) { NodeOperation *dispersion_input = get_input_operation(1); if (dispersion_input->get_flags().is_constant_operation) { - this->m_dispersion = - static_cast(dispersion_input)->get_constant_elem()[0]; + m_dispersion = static_cast(dispersion_input)->get_constant_elem()[0]; } - this->m_kr = 0.25f * max_ff(min_ff(this->m_dispersion, 1.0f), 0.0f); - this->m_kr2 = this->m_kr * 20; + m_kr = 0.25f * max_ff(min_ff(m_dispersion, 1.0f), 0.0f); + m_kr2 = m_kr * 20; } } void ProjectorLensDistortionOperation::initExecution() { this->initMutex(); - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); } void *ProjectorLensDistortionOperation::initializeTileData(rcti * /*rect*/) { updateDispersion(); - void *buffer = this->m_inputProgram->initializeTileData(nullptr); + void *buffer = m_inputProgram->initializeTileData(nullptr); return buffer; } @@ -66,11 +65,11 @@ void ProjectorLensDistortionOperation::executePixel(float output[4], int x, int const float v = (y + 0.5f) / height; const float u = (x + 0.5f) / width; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - inputBuffer->readBilinear(inputValue, (u * width + this->m_kr2) - 0.5f, v * height - 0.5f); + inputBuffer->readBilinear(inputValue, (u * width + m_kr2) - 0.5f, v * height - 0.5f); output[0] = inputValue[0]; inputBuffer->read(inputValue, x, y); output[1] = inputValue[1]; - inputBuffer->readBilinear(inputValue, (u * width - this->m_kr2) - 0.5f, v * height - 0.5f); + inputBuffer->readBilinear(inputValue, (u * width - m_kr2) - 0.5f, v * height - 0.5f); output[2] = inputValue[2]; output[3] = 1.0f; } @@ -78,18 +77,18 @@ void ProjectorLensDistortionOperation::executePixel(float output[4], int x, int void ProjectorLensDistortionOperation::deinitExecution() { this->deinitMutex(); - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } bool ProjectorLensDistortionOperation::determineDependingAreaOfInterest( rcti *input, ReadBufferOperation *readOperation, rcti *output) { rcti newInput; - if (this->m_dispersionAvailable) { + if (m_dispersionAvailable) { newInput.ymax = input->ymax; newInput.ymin = input->ymin; - newInput.xmin = input->xmin - this->m_kr2 - 2; - newInput.xmax = input->xmax + this->m_kr2 + 2; + newInput.xmin = input->xmin - m_kr2 - 2; + newInput.xmax = input->xmax + m_kr2 + 2; } else { rcti dispInput; @@ -113,17 +112,17 @@ bool ProjectorLensDistortionOperation::determineDependingAreaOfInterest( /* TODO(manzanilla): to be removed with tiled implementation. */ void ProjectorLensDistortionOperation::updateDispersion() { - if (this->m_dispersionAvailable) { + if (m_dispersionAvailable) { return; } this->lockMutex(); - if (!this->m_dispersionAvailable) { + if (!m_dispersionAvailable) { float result[4]; this->getInputSocketReader(1)->readSampled(result, 1, 1, PixelSampler::Nearest); - this->m_dispersion = result[0]; - this->m_kr = 0.25f * max_ff(min_ff(this->m_dispersion, 1.0f), 0.0f); - this->m_kr2 = this->m_kr * 20; - this->m_dispersionAvailable = true; + m_dispersion = result[0]; + m_kr = 0.25f * max_ff(min_ff(m_dispersion, 1.0f), 0.0f); + m_kr2 = m_kr * 20; + m_dispersionAvailable = true; } this->unlockMutex(); } @@ -157,8 +156,8 @@ void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx, r_input_area.ymax = output_area.ymax; r_input_area.ymin = output_area.ymin; - r_input_area.xmin = output_area.xmin - this->m_kr2 - 2; - r_input_area.xmax = output_area.xmax + this->m_kr2 + 2; + r_input_area.xmin = output_area.xmin - m_kr2 - 2; + r_input_area.xmax = output_area.xmax + m_kr2 + 2; } void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -172,11 +171,11 @@ void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { const float v = (it.y + 0.5f) / height; const float u = (it.x + 0.5f) / width; - input_image->read_elem_bilinear((u * width + this->m_kr2) - 0.5f, v * height - 0.5f, color); + input_image->read_elem_bilinear((u * width + m_kr2) - 0.5f, v * height - 0.5f, color); it.out[0] = color[0]; input_image->read_elem(it.x, it.y, color); it.out[1] = color[1]; - input_image->read_elem_bilinear((u * width - this->m_kr2) - 0.5f, v * height - 0.5f, color); + input_image->read_elem_bilinear((u * width - m_kr2) - 0.5f, v * height - 0.5f, color); it.out[2] = color[2]; it.out[3] = 1.0f; } diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.cc b/source/blender/compositor/operations/COM_QualityStepHelper.cc index e347d278ce4..77d5afa93e0 100644 --- a/source/blender/compositor/operations/COM_QualityStepHelper.cc +++ b/source/blender/compositor/operations/COM_QualityStepHelper.cc @@ -22,45 +22,45 @@ namespace blender::compositor { QualityStepHelper::QualityStepHelper() { - this->m_quality = eCompositorQuality::High; - this->m_step = 1; - this->m_offsetadd = 4; + m_quality = eCompositorQuality::High; + m_step = 1; + m_offsetadd = 4; } void QualityStepHelper::initExecution(QualityHelper helper) { switch (helper) { case COM_QH_INCREASE: - switch (this->m_quality) { + switch (m_quality) { case eCompositorQuality::High: default: - this->m_step = 1; - this->m_offsetadd = 1; + m_step = 1; + m_offsetadd = 1; break; case eCompositorQuality::Medium: - this->m_step = 2; - this->m_offsetadd = 2; + m_step = 2; + m_offsetadd = 2; break; case eCompositorQuality::Low: - this->m_step = 3; - this->m_offsetadd = 3; + m_step = 3; + m_offsetadd = 3; break; } break; case COM_QH_MULTIPLY: - switch (this->m_quality) { + switch (m_quality) { case eCompositorQuality::High: default: - this->m_step = 1; - this->m_offsetadd = 4; + m_step = 1; + m_offsetadd = 4; break; case eCompositorQuality::Medium: - this->m_step = 2; - this->m_offsetadd = 8; + m_step = 2; + m_offsetadd = 8; break; case eCompositorQuality::Low: - this->m_step = 4; - this->m_offsetadd = 16; + m_step = 4; + m_offsetadd = 16; break; } break; diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.h b/source/blender/compositor/operations/COM_QualityStepHelper.h index c5f16a58686..08ebf33cdeb 100644 --- a/source/blender/compositor/operations/COM_QualityStepHelper.h +++ b/source/blender/compositor/operations/COM_QualityStepHelper.h @@ -41,11 +41,11 @@ class QualityStepHelper { inline int getStep() const { - return this->m_step; + return m_step; } inline int getOffsetAdd() const { - return this->m_offsetadd; + return m_offsetadd; } public: @@ -53,7 +53,7 @@ class QualityStepHelper { void setQuality(eCompositorQuality quality) { - this->m_quality = quality; + m_quality = quality; } }; diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cc b/source/blender/compositor/operations/COM_ReadBufferOperation.cc index 0a41ba2d0be..4e542488eb0 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.cc +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cc @@ -26,9 +26,9 @@ namespace blender::compositor { ReadBufferOperation::ReadBufferOperation(DataType datatype) { this->addOutputSocket(datatype); - this->m_single_value = false; - this->m_offset = 0; - this->m_buffer = nullptr; + m_single_value = false; + m_offset = 0; + m_buffer = nullptr; flags.is_read_buffer_operation = true; } @@ -39,16 +39,16 @@ void *ReadBufferOperation::initializeTileData(rcti * /*rect*/) void ReadBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (this->m_memoryProxy != nullptr) { - WriteBufferOperation *operation = this->m_memoryProxy->getWriteBufferOperation(); + if (m_memoryProxy != nullptr) { + WriteBufferOperation *operation = m_memoryProxy->getWriteBufferOperation(); operation->determine_canvas(preferred_area, r_area); operation->set_canvas(r_area); /** \todo may not occur! But does with blur node. */ - if (this->m_memoryProxy->getExecutor()) { + if (m_memoryProxy->getExecutor()) { uint resolution[2] = {static_cast(BLI_rcti_size_x(&r_area)), static_cast(BLI_rcti_size_y(&r_area))}; - this->m_memoryProxy->getExecutor()->setResolution(resolution); + m_memoryProxy->getExecutor()->setResolution(resolution); } m_single_value = operation->isSingleValue(); @@ -125,8 +125,8 @@ bool ReadBufferOperation::determineDependingAreaOfInterest(rcti *input, void ReadBufferOperation::readResolutionFromWriteBuffer() { - if (this->m_memoryProxy != nullptr) { - WriteBufferOperation *operation = this->m_memoryProxy->getWriteBufferOperation(); + if (m_memoryProxy != nullptr) { + WriteBufferOperation *operation = m_memoryProxy->getWriteBufferOperation(); this->setWidth(operation->getWidth()); this->setHeight(operation->getHeight()); } @@ -134,7 +134,7 @@ void ReadBufferOperation::readResolutionFromWriteBuffer() void ReadBufferOperation::updateMemoryBuffer() { - this->m_buffer = this->getMemoryProxy()->getBuffer(); + m_buffer = this->getMemoryProxy()->getBuffer(); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.h b/source/blender/compositor/operations/COM_ReadBufferOperation.h index 02f6eccd246..d4a8fe19761 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.h +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.h @@ -35,12 +35,12 @@ class ReadBufferOperation : public NodeOperation { ReadBufferOperation(DataType datatype); void setMemoryProxy(MemoryProxy *memoryProxy) { - this->m_memoryProxy = memoryProxy; + m_memoryProxy = memoryProxy; } MemoryProxy *getMemoryProxy() const { - return this->m_memoryProxy; + return m_memoryProxy; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; @@ -56,18 +56,18 @@ class ReadBufferOperation : public NodeOperation { void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; void setOffset(unsigned int offset) { - this->m_offset = offset; + m_offset = offset; } unsigned int getOffset() const { - return this->m_offset; + return m_offset; } bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; MemoryBuffer *getInputMemoryBuffer(MemoryBuffer **memoryBuffers) override { - return memoryBuffers[this->m_offset]; + return memoryBuffers[m_offset]; } void readResolutionFromWriteBuffer(); void updateMemoryBuffer(); diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc index 6045a416d74..1291c85b4ad 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cc +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc @@ -28,9 +28,9 @@ RenderLayersProg::RenderLayersProg(const char *passName, DataType type, int elem : m_passName(passName) { this->setScene(nullptr); - this->m_inputBuffer = nullptr; - this->m_elementsize = elementsize; - this->m_rd = nullptr; + m_inputBuffer = nullptr; + m_elementsize = elementsize; + m_rd = nullptr; layer_buffer_ = nullptr; this->addOutputSocket(type); @@ -52,8 +52,7 @@ void RenderLayersProg::initExecution() RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name); if (rl) { - this->m_inputBuffer = RE_RenderLayerGetPass( - rl, this->m_passName.c_str(), this->m_viewName); + m_inputBuffer = RE_RenderLayerGetPass(rl, m_passName.c_str(), m_viewName); if (m_inputBuffer) { layer_buffer_ = new MemoryBuffer(m_inputBuffer, m_elementsize, getWidth(), getHeight()); } @@ -73,10 +72,10 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS int ix = x, iy = y; if (ix < 0 || iy < 0 || ix >= width || iy >= height) { - if (this->m_elementsize == 1) { + if (m_elementsize == 1) { output[0] = 0.0f; } - else if (this->m_elementsize == 3) { + else if (m_elementsize == 3) { zero_v3(output); } else { @@ -87,28 +86,26 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS switch (sampler) { case PixelSampler::Nearest: { - offset = (iy * width + ix) * this->m_elementsize; + offset = (iy * width + ix) * m_elementsize; - if (this->m_elementsize == 1) { - output[0] = this->m_inputBuffer[offset]; + if (m_elementsize == 1) { + output[0] = m_inputBuffer[offset]; } - else if (this->m_elementsize == 3) { - copy_v3_v3(output, &this->m_inputBuffer[offset]); + else if (m_elementsize == 3) { + copy_v3_v3(output, &m_inputBuffer[offset]); } else { - copy_v4_v4(output, &this->m_inputBuffer[offset]); + copy_v4_v4(output, &m_inputBuffer[offset]); } break; } case PixelSampler::Bilinear: - BLI_bilinear_interpolation_fl( - this->m_inputBuffer, output, width, height, this->m_elementsize, x, y); + BLI_bilinear_interpolation_fl(m_inputBuffer, output, width, height, m_elementsize, x, y); break; case PixelSampler::Bicubic: - BLI_bicubic_interpolation_fl( - this->m_inputBuffer, output, width, height, this->m_elementsize, x, y); + BLI_bicubic_interpolation_fl(m_inputBuffer, output, width, height, m_elementsize, x, y); break; } } @@ -116,7 +113,7 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS void RenderLayersProg::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { #if 0 - const RenderData *rd = this->m_rd; + const RenderData *rd = m_rd; int dx = 0, dy = 0; @@ -138,7 +135,7 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi #ifndef NDEBUG { const DataType data_type = this->getOutputSocket()->getDataType(); - int actual_element_size = this->m_elementsize; + int actual_element_size = m_elementsize; int expected_element_size; if (data_type == DataType::Value) { expected_element_size = 1; @@ -157,8 +154,8 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi } #endif - if (this->m_inputBuffer == nullptr) { - int elemsize = this->m_elementsize; + if (m_inputBuffer == nullptr) { + int elemsize = m_elementsize; if (elemsize == 1) { output[0] = 0.0f; } @@ -177,7 +174,7 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi void RenderLayersProg::deinitExecution() { - this->m_inputBuffer = nullptr; + m_inputBuffer = nullptr; if (layer_buffer_) { delete layer_buffer_; layer_buffer_ = nullptr; diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h index b499afd45df..0be21d8aad7 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.h +++ b/source/blender/compositor/operations/COM_RenderLayersProg.h @@ -80,7 +80,7 @@ class RenderLayersProg : public MultiThreadedOperation { */ inline float *getInputBuffer() { - return this->m_inputBuffer; + return m_inputBuffer; } void doInterpolation(float output[4], float x, float y, PixelSampler sampler); @@ -97,31 +97,31 @@ class RenderLayersProg : public MultiThreadedOperation { */ void setScene(Scene *scene) { - this->m_scene = scene; + m_scene = scene; } Scene *getScene() const { - return this->m_scene; + return m_scene; } void setRenderData(const RenderData *rd) { - this->m_rd = rd; + m_rd = rd; } void setLayerId(short layerId) { - this->m_layerId = layerId; + m_layerId = layerId; } short getLayerId() const { - return this->m_layerId; + return m_layerId; } void setViewName(const char *viewName) { - this->m_viewName = viewName; + m_viewName = viewName; } const char *getViewName() { - return this->m_viewName; + return m_viewName; } void initExecution() override; void deinitExecution() override; diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc index 8aca25bb2a0..e32005d3c8d 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.cc +++ b/source/blender/compositor/operations/COM_RotateOperation.cc @@ -26,10 +26,10 @@ RotateOperation::RotateOperation() this->addInputSocket(DataType::Value, ResizeMode::None); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - this->m_imageSocket = nullptr; - this->m_degreeSocket = nullptr; - this->m_doDegree2RadConversion = false; - this->m_isDegreeSet = false; + m_imageSocket = nullptr; + m_degreeSocket = nullptr; + m_doDegree2RadConversion = false; + m_isDegreeSet = false; sampler_ = PixelSampler::Bilinear; } @@ -133,23 +133,23 @@ void RotateOperation::init_data() void RotateOperation::initExecution() { - this->m_imageSocket = this->getInputSocketReader(0); - this->m_degreeSocket = this->getInputSocketReader(1); + m_imageSocket = this->getInputSocketReader(0); + m_degreeSocket = this->getInputSocketReader(1); } void RotateOperation::deinitExecution() { - this->m_imageSocket = nullptr; - this->m_degreeSocket = nullptr; + m_imageSocket = nullptr; + m_degreeSocket = nullptr; } inline void RotateOperation::ensureDegree() { - if (!this->m_isDegreeSet) { + if (!m_isDegreeSet) { float degree[4]; switch (execution_model_) { case eExecutionModel::Tiled: - this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest); + m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest); break; case eExecutionModel::FullFrame: degree[0] = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f); @@ -157,27 +157,27 @@ inline void RotateOperation::ensureDegree() } double rad; - if (this->m_doDegree2RadConversion) { + if (m_doDegree2RadConversion) { rad = DEG2RAD((double)degree[0]); } else { rad = degree[0]; } - this->m_cosine = cos(rad); - this->m_sine = sin(rad); + m_cosine = cos(rad); + m_sine = sin(rad); - this->m_isDegreeSet = true; + m_isDegreeSet = true; } } void RotateOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { ensureDegree(); - const float dy = y - this->m_centerY; - const float dx = x - this->m_centerX; - const float nx = this->m_centerX + (this->m_cosine * dx + this->m_sine * dy); - const float ny = this->m_centerY + (-this->m_sine * dx + this->m_cosine * dy); - this->m_imageSocket->readSampled(output, nx, ny, sampler); + const float dy = y - m_centerY; + const float dx = x - m_centerX; + const float nx = m_centerX + (m_cosine * dx + m_sine * dy); + const float ny = m_centerY + (-m_sine * dx + m_cosine * dy); + m_imageSocket->readSampled(output, nx, ny, sampler); } bool RotateOperation::determineDependingAreaOfInterest(rcti *input, @@ -187,19 +187,19 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input, ensureDegree(); rcti newInput; - const float dxmin = input->xmin - this->m_centerX; - const float dymin = input->ymin - this->m_centerY; - const float dxmax = input->xmax - this->m_centerX; - const float dymax = input->ymax - this->m_centerY; + const float dxmin = input->xmin - m_centerX; + const float dymin = input->ymin - m_centerY; + const float dxmax = input->xmax - m_centerX; + const float dymax = input->ymax - m_centerY; - const float x1 = this->m_centerX + (this->m_cosine * dxmin + this->m_sine * dymin); - const float x2 = this->m_centerX + (this->m_cosine * dxmax + this->m_sine * dymin); - const float x3 = this->m_centerX + (this->m_cosine * dxmin + this->m_sine * dymax); - const float x4 = this->m_centerX + (this->m_cosine * dxmax + this->m_sine * dymax); - const float y1 = this->m_centerY + (-this->m_sine * dxmin + this->m_cosine * dymin); - const float y2 = this->m_centerY + (-this->m_sine * dxmax + this->m_cosine * dymin); - const float y3 = this->m_centerY + (-this->m_sine * dxmin + this->m_cosine * dymax); - const float y4 = this->m_centerY + (-this->m_sine * dxmax + this->m_cosine * dymax); + const float x1 = m_centerX + (m_cosine * dxmin + m_sine * dymin); + const float x2 = m_centerX + (m_cosine * dxmax + m_sine * dymin); + const float x3 = m_centerX + (m_cosine * dxmin + m_sine * dymax); + const float x4 = m_centerX + (m_cosine * dxmax + m_sine * dymax); + const float y1 = m_centerY + (-m_sine * dxmin + m_cosine * dymin); + const float y2 = m_centerY + (-m_sine * dxmax + m_cosine * dymin); + const float y3 = m_centerY + (-m_sine * dxmin + m_cosine * dymax); + const float y4 = m_centerY + (-m_sine * dxmax + m_cosine * dymax); const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4))); const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4))); const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4))); diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h index 76f203cfbc0..314e0f6076f 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.h +++ b/source/blender/compositor/operations/COM_RotateOperation.h @@ -89,7 +89,7 @@ class RotateOperation : public MultiThreadedOperation { void setDoDegree2RadConversion(bool abool) { - this->m_doDegree2RadConversion = abool; + m_doDegree2RadConversion = abool; } void set_sampler(PixelSampler sampler) diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index 8c30976d200..eb5db8d1698 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -171,22 +171,22 @@ SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation() this->addInputSocket(DataType::Value); /* Depth, material ID, etc. TODO: currently unused. */ this->addOutputSocket(DataType::Color); this->flags.complex = true; - this->m_imageReader = nullptr; - this->m_valueReader = nullptr; + m_imageReader = nullptr; + m_valueReader = nullptr; this->setThreshold(CMP_DEFAULT_SMAA_THRESHOLD); this->setLocalContrastAdaptationFactor(CMP_DEFAULT_SMAA_CONTRAST_LIMIT); } void SMAAEdgeDetectionOperation::initExecution() { - this->m_imageReader = this->getInputSocketReader(0); - this->m_valueReader = this->getInputSocketReader(1); + m_imageReader = this->getInputSocketReader(0); + m_valueReader = this->getInputSocketReader(1); } void SMAAEdgeDetectionOperation::deinitExecution() { - this->m_imageReader = nullptr; - this->m_valueReader = nullptr; + m_imageReader = nullptr; + m_valueReader = nullptr; } void SMAAEdgeDetectionOperation::setThreshold(float threshold) @@ -401,7 +401,7 @@ SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation() this->addInputSocket(DataType::Color); /* edges */ this->addOutputSocket(DataType::Color); this->flags.complex = true; - this->m_imageReader = nullptr; + m_imageReader = nullptr; this->setCornerRounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING); } @@ -412,7 +412,7 @@ void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect) void SMAABlendingWeightCalculationOperation::initExecution() { - this->m_imageReader = this->getInputSocketReader(0); + m_imageReader = this->getInputSocketReader(0); if (execution_model_ == eExecutionModel::Tiled) { sample_image_fn_ = [=](int x, int y, float *out) { sample(m_imageReader, x, y, out); }; } @@ -630,7 +630,7 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( void SMAABlendingWeightCalculationOperation::deinitExecution() { - this->m_imageReader = nullptr; + m_imageReader = nullptr; } bool SMAABlendingWeightCalculationOperation::determineDependingAreaOfInterest( @@ -1011,8 +1011,8 @@ SMAANeighborhoodBlendingOperation::SMAANeighborhoodBlendingOperation() this->addInputSocket(DataType::Color); /* blend */ this->addOutputSocket(DataType::Color); this->flags.complex = true; - this->m_image1Reader = nullptr; - this->m_image2Reader = nullptr; + m_image1Reader = nullptr; + m_image2Reader = nullptr; } void *SMAANeighborhoodBlendingOperation::initializeTileData(rcti *rect) @@ -1022,8 +1022,8 @@ void *SMAANeighborhoodBlendingOperation::initializeTileData(rcti *rect) void SMAANeighborhoodBlendingOperation::initExecution() { - this->m_image1Reader = this->getInputSocketReader(0); - this->m_image2Reader = this->getInputSocketReader(1); + m_image1Reader = this->getInputSocketReader(0); + m_image2Reader = this->getInputSocketReader(1); } void SMAANeighborhoodBlendingOperation::executePixel(float output[4], @@ -1129,8 +1129,8 @@ void SMAANeighborhoodBlendingOperation::update_memory_buffer_partial(MemoryBuffe void SMAANeighborhoodBlendingOperation::deinitExecution() { - this->m_image1Reader = nullptr; - this->m_image2Reader = nullptr; + m_image1Reader = nullptr; + m_image2Reader = nullptr; } bool SMAANeighborhoodBlendingOperation::determineDependingAreaOfInterest( diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index dbd8faf0f1d..5d0f9a2ad5e 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -53,9 +53,9 @@ ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(data_type); - this->m_inputOperation = nullptr; - this->m_inputXOperation = nullptr; - this->m_inputYOperation = nullptr; + m_inputOperation = nullptr; + m_inputXOperation = nullptr; + m_inputYOperation = nullptr; } float ScaleOperation::get_constant_scale(const int input_op_idx, const float factor) @@ -118,16 +118,16 @@ void ScaleOperation::init_data() void ScaleOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_inputXOperation = this->getInputSocketReader(1); - this->m_inputYOperation = this->getInputSocketReader(2); + m_inputOperation = this->getInputSocketReader(0); + m_inputXOperation = this->getInputSocketReader(1); + m_inputYOperation = this->getInputSocketReader(2); } void ScaleOperation::deinitExecution() { - this->m_inputOperation = nullptr; - this->m_inputXOperation = nullptr; - this->m_inputYOperation = nullptr; + m_inputOperation = nullptr; + m_inputXOperation = nullptr; + m_inputYOperation = nullptr; } void ScaleOperation::get_scale_offset(const rcti &input_canvas, @@ -268,15 +268,15 @@ void ScaleRelativeOperation::executePixelSampled(float output[4], float scaleX[4]; float scaleY[4]; - this->m_inputXOperation->readSampled(scaleX, x, y, effective_sampler); - this->m_inputYOperation->readSampled(scaleY, x, y, effective_sampler); + m_inputXOperation->readSampled(scaleX, x, y, effective_sampler); + m_inputYOperation->readSampled(scaleY, x, y, effective_sampler); const float scx = scaleX[0]; const float scy = scaleY[0]; float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / scx; float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / scy; - this->m_inputOperation->readSampled(output, nx, ny, effective_sampler); + m_inputOperation->readSampled(output, nx, ny, effective_sampler); } bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input, @@ -288,8 +288,8 @@ bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input, float scaleX[4]; float scaleY[4]; - this->m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest); - this->m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest); + m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest); + m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest); const float scx = scaleX[0]; const float scy = scaleY[0]; @@ -318,8 +318,8 @@ void ScaleAbsoluteOperation::executePixelSampled(float output[4], float scaleX[4]; float scaleY[4]; - this->m_inputXOperation->readSampled(scaleX, x, y, effective_sampler); - this->m_inputYOperation->readSampled(scaleY, x, y, effective_sampler); + m_inputXOperation->readSampled(scaleX, x, y, effective_sampler); + m_inputYOperation->readSampled(scaleY, x, y, effective_sampler); const float scx = scaleX[0]; /* Target absolute scale. */ const float scy = scaleY[0]; /* Target absolute scale. */ @@ -333,7 +333,7 @@ void ScaleAbsoluteOperation::executePixelSampled(float output[4], float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / relativeXScale; float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / relativeYScale; - this->m_inputOperation->readSampled(output, nx, ny, effective_sampler); + m_inputOperation->readSampled(output, nx, ny, effective_sampler); } bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input, @@ -345,8 +345,8 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input, float scaleX[4]; float scaleY[4]; - this->m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest); - this->m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest); + m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest); + m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest); const float scx = scaleX[0]; const float scy = scaleY[0]; @@ -380,49 +380,49 @@ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation() this->addInputSocket(DataType::Color, ResizeMode::None); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - this->m_inputOperation = nullptr; - this->m_is_offset = false; + m_inputOperation = nullptr; + m_is_offset = false; } void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) { const int input_width = BLI_rcti_size_x(&input_canvas); const int input_height = BLI_rcti_size_y(&input_canvas); - this->m_relX = input_width / (float)this->m_newWidth; - this->m_relY = input_height / (float)this->m_newHeight; + m_relX = input_width / (float)m_newWidth; + m_relY = input_height / (float)m_newHeight; /* *** all the options below are for a fairly special case - camera framing *** */ - if (this->m_offsetX != 0.0f || this->m_offsetY != 0.0f) { - this->m_is_offset = true; + if (m_offsetX != 0.0f || m_offsetY != 0.0f) { + m_is_offset = true; - if (this->m_newWidth > this->m_newHeight) { - this->m_offsetX *= this->m_newWidth; - this->m_offsetY *= this->m_newWidth; + if (m_newWidth > m_newHeight) { + m_offsetX *= m_newWidth; + m_offsetY *= m_newWidth; } else { - this->m_offsetX *= this->m_newHeight; - this->m_offsetY *= this->m_newHeight; + m_offsetX *= m_newHeight; + m_offsetY *= m_newHeight; } } - if (this->m_is_aspect) { + if (m_is_aspect) { /* apply aspect from clip */ const float w_src = input_width; const float h_src = input_height; /* destination aspect is already applied from the camera frame */ - const float w_dst = this->m_newWidth; - const float h_dst = this->m_newHeight; + const float w_dst = m_newWidth; + const float h_dst = m_newHeight; const float asp_src = w_src / h_src; const float asp_dst = w_dst / h_dst; if (fabsf(asp_src - asp_dst) >= FLT_EPSILON) { - if ((asp_src > asp_dst) == (this->m_is_crop == true)) { + if ((asp_src > asp_dst) == (m_is_crop == true)) { /* fit X */ const float div = asp_src / asp_dst; - this->m_relX /= div; - this->m_offsetX += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f; + m_relX /= div; + m_offsetX += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f; if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) { int fit_width = m_newWidth * div; if (fit_width > max_scale_canvas_size_.x) { @@ -437,8 +437,8 @@ void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) else { /* fit Y */ const float div = asp_dst / asp_src; - this->m_relY /= div; - this->m_offsetY += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f; + m_relY /= div; + m_offsetY += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f; if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) { int fit_height = m_newHeight * div; if (fit_height > max_scale_canvas_size_.y) { @@ -451,7 +451,7 @@ void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) } } - this->m_is_offset = true; + m_is_offset = true; } } /* *** end framing options *** */ @@ -459,12 +459,12 @@ void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) void ScaleFixedSizeOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); + m_inputOperation = this->getInputSocketReader(0); } void ScaleFixedSizeOperation::deinitExecution() { - this->m_inputOperation = nullptr; + m_inputOperation = nullptr; } void ScaleFixedSizeOperation::executePixelSampled(float output[4], @@ -474,14 +474,13 @@ void ScaleFixedSizeOperation::executePixelSampled(float output[4], { PixelSampler effective_sampler = getEffectiveSampler(sampler); - if (this->m_is_offset) { - float nx = ((x - this->m_offsetX) * this->m_relX); - float ny = ((y - this->m_offsetY) * this->m_relY); - this->m_inputOperation->readSampled(output, nx, ny, effective_sampler); + if (m_is_offset) { + float nx = ((x - m_offsetX) * m_relX); + float ny = ((y - m_offsetY) * m_relY); + m_inputOperation->readSampled(output, nx, ny, effective_sampler); } else { - this->m_inputOperation->readSampled( - output, x * this->m_relX, y * this->m_relY, effective_sampler); + m_inputOperation->readSampled(output, x * m_relX, y * m_relY, effective_sampler); } } @@ -491,10 +490,10 @@ bool ScaleFixedSizeOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - newInput.xmax = (input->xmax - m_offsetX) * this->m_relX + 1; - newInput.xmin = (input->xmin - m_offsetX) * this->m_relX; - newInput.ymax = (input->ymax - m_offsetY) * this->m_relY + 1; - newInput.ymin = (input->ymin - m_offsetY) * this->m_relY; + newInput.xmax = (input->xmax - m_offsetX) * m_relX + 1; + newInput.xmin = (input->xmin - m_offsetX) * m_relX; + newInput.ymax = (input->ymax - m_offsetY) * m_relY + 1; + newInput.ymin = (input->ymin - m_offsetY) * m_relY; return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -528,10 +527,10 @@ void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx, BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmax = ceilf((output_area.xmax - m_offsetX) * this->m_relX); - r_input_area.xmin = floorf((output_area.xmin - m_offsetX) * this->m_relX); - r_input_area.ymax = ceilf((output_area.ymax - m_offsetY) * this->m_relY); - r_input_area.ymin = floorf((output_area.ymin - m_offsetY) * this->m_relY); + r_input_area.xmax = ceilf((output_area.xmax - m_offsetX) * m_relX); + r_input_area.xmin = floorf((output_area.xmin - m_offsetX) * m_relX); + r_input_area.ymax = ceilf((output_area.ymax - m_offsetY) * m_relY); + r_input_area.ymin = floorf((output_area.ymin - m_offsetY) * m_relY); expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler); } @@ -542,17 +541,17 @@ void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output, const MemoryBuffer *input_img = inputs[0]; PixelSampler sampler = (PixelSampler)m_sampler; BuffersIterator it = output->iterate_with({}, area); - if (this->m_is_offset) { + if (m_is_offset) { for (; !it.is_end(); ++it) { - const float nx = (canvas_.xmin + it.x - this->m_offsetX) * this->m_relX; - const float ny = (canvas_.ymin + it.y - this->m_offsetY) * this->m_relY; + const float nx = (canvas_.xmin + it.x - m_offsetX) * m_relX; + const float ny = (canvas_.ymin + it.y - m_offsetY) * m_relY; input_img->read_elem_sampled(nx - canvas_.xmin, ny - canvas_.ymin, sampler, it.out); } } else { for (; !it.is_end(); ++it) { - input_img->read_elem_sampled((canvas_.xmin + it.x) * this->m_relX - canvas_.xmin, - (canvas_.ymin + it.y) * this->m_relY - canvas_.ymin, + input_img->read_elem_sampled((canvas_.xmin + it.x) * m_relX - canvas_.xmin, + (canvas_.ymin + it.y) * m_relY - canvas_.ymin, sampler, it.out); } diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h index 746e490d900..4ba17e2ba20 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.h +++ b/source/blender/compositor/operations/COM_ScaleOperation.h @@ -29,7 +29,7 @@ class BaseScaleOperation : public MultiThreadedOperation { public: void setSampler(PixelSampler sampler) { - this->m_sampler = (int)sampler; + m_sampler = (int)sampler; } void setVariableSize(bool variable_size) { @@ -184,24 +184,24 @@ class ScaleFixedSizeOperation : public BaseScaleOperation { void deinitExecution() override; void setNewWidth(int width) { - this->m_newWidth = width; + m_newWidth = width; } void setNewHeight(int height) { - this->m_newHeight = height; + m_newHeight = height; } void setIsAspect(bool is_aspect) { - this->m_is_aspect = is_aspect; + m_is_aspect = is_aspect; } void setIsCrop(bool is_crop) { - this->m_is_crop = is_crop; + m_is_crop = is_crop; } void setOffset(float x, float y) { - this->m_offsetX = x; - this->m_offsetY = y; + m_offsetX = x; + m_offsetY = y; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index bef5beb2bce..ec2fc1aab65 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -33,12 +33,12 @@ ScreenLensDistortionOperation::ScreenLensDistortionOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->flags.complex = true; - this->m_inputProgram = nullptr; - this->m_distortion = 0.0f; - this->m_dispersion = 0.0f; - this->m_distortion_const = false; - this->m_dispersion_const = false; - this->m_variables_ready = false; + m_inputProgram = nullptr; + m_distortion = 0.0f; + m_dispersion = 0.0f; + m_distortion_const = false; + m_dispersion_const = false; + m_variables_ready = false; } void ScreenLensDistortionOperation::setDistortion(float distortion) @@ -55,8 +55,8 @@ void ScreenLensDistortionOperation::setDispersion(float dispersion) void ScreenLensDistortionOperation::init_data() { - this->m_cx = 0.5f * (float)getWidth(); - this->m_cy = 0.5f * (float)getHeight(); + m_cx = 0.5f * (float)getWidth(); + m_cy = 0.5f * (float)getHeight(); switch (execution_model_) { case eExecutionModel::FullFrame: { @@ -84,17 +84,17 @@ void ScreenLensDistortionOperation::init_data() void ScreenLensDistortionOperation::initExecution() { - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); this->initMutex(); uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); rng_seed ^= (uint)POINTER_AS_INT(m_inputProgram); - this->m_rng = BLI_rng_new(rng_seed); + m_rng = BLI_rng_new(rng_seed); } void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/) { - void *buffer = this->m_inputProgram->initializeTileData(nullptr); + void *buffer = m_inputProgram->initializeTileData(nullptr); /* get distortion/dispersion values once, by reading inputs at (0,0) * XXX this assumes invariable values (no image inputs), @@ -231,8 +231,8 @@ void ScreenLensDistortionOperation::executePixel(float output[4], int x, int y, void ScreenLensDistortionOperation::deinitExecution() { this->deinitMutex(); - this->m_inputProgram = nullptr; - BLI_rng_free(this->m_rng); + m_inputProgram = nullptr; + BLI_rng_free(m_rng); } void ScreenLensDistortionOperation::determineUV(float result[6], float x, float y) const diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc index e4686ffa76d..da930186901 100644 --- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc @@ -26,15 +26,15 @@ SetAlphaMultiplyOperation::SetAlphaMultiplyOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_inputColor = nullptr; - this->m_inputAlpha = nullptr; + m_inputColor = nullptr; + m_inputAlpha = nullptr; this->flags.can_be_constant = true; } void SetAlphaMultiplyOperation::initExecution() { - this->m_inputColor = getInputSocketReader(0); - this->m_inputAlpha = getInputSocketReader(1); + m_inputColor = getInputSocketReader(0); + m_inputAlpha = getInputSocketReader(1); } void SetAlphaMultiplyOperation::executePixelSampled(float output[4], @@ -45,16 +45,16 @@ void SetAlphaMultiplyOperation::executePixelSampled(float output[4], float color_input[4]; float alpha_input[4]; - this->m_inputColor->readSampled(color_input, x, y, sampler); - this->m_inputAlpha->readSampled(alpha_input, x, y, sampler); + m_inputColor->readSampled(color_input, x, y, sampler); + m_inputAlpha->readSampled(alpha_input, x, y, sampler); mul_v4_v4fl(output, color_input, alpha_input[0]); } void SetAlphaMultiplyOperation::deinitExecution() { - this->m_inputColor = nullptr; - this->m_inputAlpha = nullptr; + m_inputColor = nullptr; + m_inputAlpha = nullptr; } void SetAlphaMultiplyOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc index 434f5d9b63c..5abb490d698 100644 --- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc @@ -26,15 +26,15 @@ SetAlphaReplaceOperation::SetAlphaReplaceOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_inputColor = nullptr; - this->m_inputAlpha = nullptr; + m_inputColor = nullptr; + m_inputAlpha = nullptr; this->flags.can_be_constant = true; } void SetAlphaReplaceOperation::initExecution() { - this->m_inputColor = getInputSocketReader(0); - this->m_inputAlpha = getInputSocketReader(1); + m_inputColor = getInputSocketReader(0); + m_inputAlpha = getInputSocketReader(1); } void SetAlphaReplaceOperation::executePixelSampled(float output[4], @@ -44,15 +44,15 @@ void SetAlphaReplaceOperation::executePixelSampled(float output[4], { float alpha_input[4]; - this->m_inputColor->readSampled(output, x, y, sampler); - this->m_inputAlpha->readSampled(alpha_input, x, y, sampler); + m_inputColor->readSampled(output, x, y, sampler); + m_inputAlpha->readSampled(alpha_input, x, y, sampler); output[3] = alpha_input[0]; } void SetAlphaReplaceOperation::deinitExecution() { - this->m_inputColor = nullptr; - this->m_inputAlpha = nullptr; + m_inputColor = nullptr; + m_inputAlpha = nullptr; } void SetAlphaReplaceOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index 1e7a950e727..25969821931 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -31,7 +31,7 @@ void SetColorOperation::executePixelSampled(float output[4], float /*y*/, PixelSampler /*sampler*/) { - copy_v4_v4(output, this->m_color); + copy_v4_v4(output, m_color); } void SetColorOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h index 34ea35bdcbc..b529d245091 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.h +++ b/source/blender/compositor/operations/COM_SetColorOperation.h @@ -43,39 +43,39 @@ class SetColorOperation : public ConstantOperation { float getChannel1() { - return this->m_color[0]; + return m_color[0]; } void setChannel1(float value) { - this->m_color[0] = value; + m_color[0] = value; } float getChannel2() { - return this->m_color[1]; + return m_color[1]; } void setChannel2(float value) { - this->m_color[1] = value; + m_color[1] = value; } float getChannel3() { - return this->m_color[2]; + return m_color[2]; } void setChannel3(float value) { - this->m_color[2] = value; + m_color[2] = value; } float getChannel4() { - return this->m_color[3]; + return m_color[3]; } void setChannel4(const float value) { - this->m_color[3] = value; + m_color[3] = value; } void setChannels(const float value[4]) { - copy_v4_v4(this->m_color, value); + copy_v4_v4(m_color, value); } /** diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.cc b/source/blender/compositor/operations/COM_SetSamplerOperation.cc index e68774736f3..eb7eedb2bd5 100644 --- a/source/blender/compositor/operations/COM_SetSamplerOperation.cc +++ b/source/blender/compositor/operations/COM_SetSamplerOperation.cc @@ -28,11 +28,11 @@ SetSamplerOperation::SetSamplerOperation() void SetSamplerOperation::initExecution() { - this->m_reader = this->getInputSocketReader(0); + m_reader = this->getInputSocketReader(0); } void SetSamplerOperation::deinitExecution() { - this->m_reader = nullptr; + m_reader = nullptr; } void SetSamplerOperation::executePixelSampled(float output[4], @@ -40,7 +40,7 @@ void SetSamplerOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - this->m_reader->readSampled(output, x, y, this->m_sampler); + m_reader->readSampled(output, x, y, m_sampler); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.h b/source/blender/compositor/operations/COM_SetSamplerOperation.h index d355d937806..0b6c1f2ccff 100644 --- a/source/blender/compositor/operations/COM_SetSamplerOperation.h +++ b/source/blender/compositor/operations/COM_SetSamplerOperation.h @@ -39,7 +39,7 @@ class SetSamplerOperation : public NodeOperation { void setSampler(PixelSampler sampler) { - this->m_sampler = sampler; + m_sampler = sampler; } /** diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index b7c50f94887..4db0963f45b 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -31,7 +31,7 @@ void SetValueOperation::executePixelSampled(float output[4], float /*y*/, PixelSampler /*sampler*/) { - output[0] = this->m_value; + output[0] = m_value; } void SetValueOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h index e72652450a9..25f5e806eaf 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.h +++ b/source/blender/compositor/operations/COM_SetValueOperation.h @@ -43,11 +43,11 @@ class SetValueOperation : public ConstantOperation { float getValue() { - return this->m_value; + return m_value; } void setValue(float value) { - this->m_value = value; + m_value = value; } /** diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc index ddc5b6dce13..794934a741a 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.cc +++ b/source/blender/compositor/operations/COM_SplitOperation.cc @@ -25,21 +25,21 @@ SplitOperation::SplitOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_image1Input = nullptr; - this->m_image2Input = nullptr; + m_image1Input = nullptr; + m_image2Input = nullptr; } void SplitOperation::initExecution() { /* When initializing the tree during initial load the width and height can be zero. */ - this->m_image1Input = getInputSocketReader(0); - this->m_image2Input = getInputSocketReader(1); + m_image1Input = getInputSocketReader(0); + m_image2Input = getInputSocketReader(1); } void SplitOperation::deinitExecution() { - this->m_image1Input = nullptr; - this->m_image2Input = nullptr; + m_image1Input = nullptr; + m_image2Input = nullptr; } void SplitOperation::executePixelSampled(float output[4], @@ -47,14 +47,14 @@ void SplitOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - int perc = this->m_xSplit ? this->m_splitPercentage * this->getWidth() / 100.0f : - this->m_splitPercentage * this->getHeight() / 100.0f; - bool image1 = this->m_xSplit ? x > perc : y > perc; + int perc = m_xSplit ? m_splitPercentage * this->getWidth() / 100.0f : + m_splitPercentage * this->getHeight() / 100.0f; + bool image1 = m_xSplit ? x > perc : y > perc; if (image1) { - this->m_image1Input->readSampled(output, x, y, PixelSampler::Nearest); + m_image1Input->readSampled(output, x, y, PixelSampler::Nearest); } else { - this->m_image2Input->readSampled(output, x, y, PixelSampler::Nearest); + m_image2Input->readSampled(output, x, y, PixelSampler::Nearest); } } @@ -72,11 +72,11 @@ void SplitOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const int percent = this->m_xSplit ? this->m_splitPercentage * this->getWidth() / 100.0f : - this->m_splitPercentage * this->getHeight() / 100.0f; + const int percent = m_xSplit ? m_splitPercentage * this->getWidth() / 100.0f : + m_splitPercentage * this->getHeight() / 100.0f; const size_t elem_bytes = COM_data_type_bytes_len(getOutputSocket()->getDataType()); for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - const bool is_image1 = this->m_xSplit ? it.x > percent : it.y > percent; + const bool is_image1 = m_xSplit ? it.x > percent : it.y > percent; memcpy(it.out, it.in(is_image1 ? 0 : 1), elem_bytes); } } diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h index f923c9f8b7a..ae4d83fd059 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.h +++ b/source/blender/compositor/operations/COM_SplitOperation.h @@ -38,11 +38,11 @@ class SplitOperation : public MultiThreadedOperation { void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void setSplitPercentage(float splitPercentage) { - this->m_splitPercentage = splitPercentage; + m_splitPercentage = splitPercentage; } void setXSplit(bool xsplit) { - this->m_xSplit = xsplit; + m_xSplit = xsplit; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index 494506389c5..b71350fd472 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -33,9 +33,9 @@ SunBeamsOperation::SunBeamsOperation() void SunBeamsOperation::calc_rays_common_data() { /* convert to pixels */ - this->m_source_px[0] = this->m_data.source[0] * this->getWidth(); - this->m_source_px[1] = this->m_data.source[1] * this->getHeight(); - this->m_ray_length_px = this->m_data.ray_length * MAX2(this->getWidth(), this->getHeight()); + m_source_px[0] = m_data.source[0] * this->getWidth(); + m_source_px[1] = m_data.source[1] * this->getHeight(); + m_ray_length_px = m_data.ray_length * MAX2(this->getWidth(), this->getHeight()); } void SunBeamsOperation::initExecution() @@ -322,8 +322,7 @@ void SunBeamsOperation::executePixel(float output[4], int x, int y, void *data) { const float co[2] = {(float)x, (float)y}; - accumulate_line( - (MemoryBuffer *)data, output, co, this->m_source_px, 0.0f, this->m_ray_length_px); + accumulate_line((MemoryBuffer *)data, output, co, m_source_px, 0.0f, m_ray_length_px); } static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], float ray_length) @@ -350,10 +349,10 @@ bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input, * and gives a rect that contains all possible accumulated pixels. */ rcti rect = *input; - calc_ray_shift(&rect, input->xmin, input->ymin, this->m_source_px, this->m_ray_length_px); - calc_ray_shift(&rect, input->xmin, input->ymax, this->m_source_px, this->m_ray_length_px); - calc_ray_shift(&rect, input->xmax, input->ymin, this->m_source_px, this->m_ray_length_px); - calc_ray_shift(&rect, input->xmax, input->ymax, this->m_source_px, this->m_ray_length_px); + calc_ray_shift(&rect, input->xmin, input->ymin, m_source_px, m_ray_length_px); + calc_ray_shift(&rect, input->xmin, input->ymax, m_source_px, m_ray_length_px); + calc_ray_shift(&rect, input->xmax, input->ymin, m_source_px, m_ray_length_px); + calc_ray_shift(&rect, input->xmax, input->ymax, m_source_px, m_ray_length_px); return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output); } diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index cfd22b16c4e..994379c35c1 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -28,12 +28,12 @@ TextureBaseOperation::TextureBaseOperation() { this->addInputSocket(DataType::Vector); // offset this->addInputSocket(DataType::Vector); // size - this->m_texture = nullptr; - this->m_inputSize = nullptr; - this->m_inputOffset = nullptr; - this->m_rd = nullptr; - this->m_pool = nullptr; - this->m_sceneColorManage = false; + m_texture = nullptr; + m_inputSize = nullptr; + m_inputOffset = nullptr; + m_rd = nullptr; + m_pool = nullptr; + m_sceneColorManage = false; flags.complex = true; } TextureOperation::TextureOperation() : TextureBaseOperation() @@ -47,24 +47,23 @@ TextureAlphaOperation::TextureAlphaOperation() : TextureBaseOperation() void TextureBaseOperation::initExecution() { - this->m_inputOffset = getInputSocketReader(0); - this->m_inputSize = getInputSocketReader(1); - this->m_pool = BKE_image_pool_new(); - if (this->m_texture != nullptr && this->m_texture->nodetree != nullptr && - this->m_texture->use_nodes) { - ntreeTexBeginExecTree(this->m_texture->nodetree); + m_inputOffset = getInputSocketReader(0); + m_inputSize = getInputSocketReader(1); + m_pool = BKE_image_pool_new(); + if (m_texture != nullptr && m_texture->nodetree != nullptr && m_texture->use_nodes) { + ntreeTexBeginExecTree(m_texture->nodetree); } NodeOperation::initExecution(); } void TextureBaseOperation::deinitExecution() { - this->m_inputSize = nullptr; - this->m_inputOffset = nullptr; - BKE_image_pool_free(this->m_pool); - this->m_pool = nullptr; - if (this->m_texture != nullptr && this->m_texture->use_nodes && - this->m_texture->nodetree != nullptr && this->m_texture->nodetree->execdata != nullptr) { - ntreeTexEndExecTree(this->m_texture->nodetree->execdata); + m_inputSize = nullptr; + m_inputOffset = nullptr; + BKE_image_pool_free(m_pool); + m_pool = nullptr; + if (m_texture != nullptr && m_texture->use_nodes && m_texture->nodetree != nullptr && + m_texture->nodetree->execdata != nullptr) { + ntreeTexEndExecTree(m_texture->nodetree->execdata); } NodeOperation::deinitExecution(); } @@ -73,8 +72,8 @@ void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_ { r_area = preferred_area; if (BLI_rcti_is_empty(&preferred_area)) { - int width = this->m_rd->xsch * this->m_rd->size / 100; - int height = this->m_rd->ysch * this->m_rd->size / 100; + int width = m_rd->xsch * m_rd->size / 100; + int height = m_rd->ysch * m_rd->size / 100; r_area.xmax = preferred_area.xmin + width; r_area.ymax = preferred_area.ymin + height; } @@ -121,24 +120,16 @@ void TextureBaseOperation::executePixelSampled(float output[4], v += 0.5f / cy; } - this->m_inputSize->readSampled(textureSize, x, y, sampler); - this->m_inputOffset->readSampled(textureOffset, x, y, sampler); + m_inputSize->readSampled(textureSize, x, y, sampler); + m_inputOffset->readSampled(textureOffset, x, y, sampler); vec[0] = textureSize[0] * (u + textureOffset[0]); vec[1] = textureSize[1] * (v + textureOffset[1]); vec[2] = textureSize[2] * textureOffset[2]; const int thread_id = WorkScheduler::current_thread_id(); - retval = multitex_ext(this->m_texture, - vec, - nullptr, - nullptr, - 0, - &texres, - thread_id, - m_pool, - m_sceneColorManage, - false); + retval = multitex_ext( + m_texture, vec, nullptr, nullptr, 0, &texres, thread_id, m_pool, m_sceneColorManage, false); output[3] = texres.talpha ? texres.ta : texres.tin; if (retval & TEX_RGB) { @@ -182,7 +173,7 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, vec[1] = tex_size[1] * (v + tex_offset[1]); vec[2] = tex_size[2] * tex_offset[2]; - const int retval = multitex_ext(this->m_texture, + const int retval = multitex_ext(m_texture, vec, nullptr, nullptr, diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index 5bc21da1f96..2efb76e8c37 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -58,17 +58,17 @@ class TextureBaseOperation : public MultiThreadedOperation { void setTexture(Tex *texture) { - this->m_texture = texture; + m_texture = texture; } void initExecution() override; void deinitExecution() override; void setRenderData(const RenderData *rd) { - this->m_rd = rd; + m_rd = rd; } void setSceneColorManage(bool sceneColorManage) { - this->m_sceneColorManage = sceneColorManage; + m_sceneColorManage = sceneColorManage; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index 2ff54571c24..4d2a9a7f965 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -28,14 +28,14 @@ TonemapOperation::TonemapOperation() { this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Color); - this->m_imageReader = nullptr; - this->m_data = nullptr; - this->m_cachedInstance = nullptr; + m_imageReader = nullptr; + m_data = nullptr; + m_cachedInstance = nullptr; this->flags.complex = true; } void TonemapOperation::initExecution() { - this->m_imageReader = this->getInputSocketReader(0); + m_imageReader = this->getInputSocketReader(0); NodeOperation::initMutex(); } @@ -43,11 +43,11 @@ void TonemapOperation::executePixel(float output[4], int x, int y, void *data) { AvgLogLum *avg = (AvgLogLum *)data; - this->m_imageReader->read(output, x, y, nullptr); + m_imageReader->read(output, x, y, nullptr); mul_v3_fl(output, avg->al); - float dr = output[0] + this->m_data->offset; - float dg = output[1] + this->m_data->offset; - float db = output[2] + this->m_data->offset; + float dr = output[0] + m_data->offset; + float dg = output[1] + m_data->offset; + float db = output[2] + m_data->offset; output[0] /= ((dr == 0.0f) ? 1.0f : dr); output[1] /= ((dg == 0.0f) ? 1.0f : dg); output[2] /= ((db == 0.0f) ? 1.0f : db); @@ -61,13 +61,13 @@ void TonemapOperation::executePixel(float output[4], int x, int y, void *data) void PhotoreceptorTonemapOperation::executePixel(float output[4], int x, int y, void *data) { AvgLogLum *avg = (AvgLogLum *)data; - NodeTonemap *ntm = this->m_data; + NodeTonemap *ntm = m_data; - const float f = expf(-this->m_data->f); + const float f = expf(-m_data->f); const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); const float ic = 1.0f - ntm->c, ia = 1.0f - ntm->a; - this->m_imageReader->read(output, x, y, nullptr); + m_imageReader->read(output, x, y, nullptr); const float L = IMB_colormanagement_get_luminance(output); float I_l = output[0] + ic * (L - output[0]); @@ -86,8 +86,8 @@ void PhotoreceptorTonemapOperation::executePixel(float output[4], int x, int y, void TonemapOperation::deinitExecution() { - this->m_imageReader = nullptr; - delete this->m_cachedInstance; + m_imageReader = nullptr; + delete m_cachedInstance; NodeOperation::deinitMutex(); } @@ -111,8 +111,8 @@ bool TonemapOperation::determineDependingAreaOfInterest(rcti * /*input*/, void *TonemapOperation::initializeTileData(rcti *rect) { lockMutex(); - if (this->m_cachedInstance == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_imageReader->initializeTileData(rect); + if (m_cachedInstance == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)m_imageReader->initializeTileData(rect); AvgLogLum *data = new AvgLogLum(); float *buffer = tile->getBuffer(); @@ -140,12 +140,12 @@ void *TonemapOperation::initializeTileData(rcti *rect) avl = lsum * sc; data->auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.0f; float al = exp((double)avl); - data->al = (al == 0.0f) ? 0.0f : (this->m_data->key / al); - data->igm = (this->m_data->gamma == 0.0f) ? 1 : (1.0f / this->m_data->gamma); - this->m_cachedInstance = data; + data->al = (al == 0.0f) ? 0.0f : (m_data->key / al); + data->igm = (m_data->gamma == 0.0f) ? 1 : (1.0f / m_data->gamma); + m_cachedInstance = data; } unlockMutex(); - return this->m_cachedInstance; + return m_cachedInstance; } void TonemapOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) @@ -189,7 +189,7 @@ void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output) const rcti &UNUSED(area), Span inputs) { - if (this->m_cachedInstance == nullptr) { + if (m_cachedInstance == nullptr) { Luminance lum = {0}; const MemoryBuffer *input = inputs[0]; exec_system_->execute_work( @@ -213,9 +213,9 @@ void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output) const float avg_log = lum.log_sum / lum.num_pixels; avg->auto_key = (max_log > min_log) ? ((max_log - avg_log) / (max_log - min_log)) : 1.0f; const float al = exp((double)avg_log); - avg->al = (al == 0.0f) ? 0.0f : (this->m_data->key / al); - avg->igm = (this->m_data->gamma == 0.0f) ? 1 : (1.0f / this->m_data->gamma); - this->m_cachedInstance = avg; + avg->al = (al == 0.0f) ? 0.0f : (m_data->key / al); + avg->igm = (m_data->gamma == 0.0f) ? 1 : (1.0f / m_data->gamma); + m_cachedInstance = avg; } } @@ -225,7 +225,7 @@ void TonemapOperation::update_memory_buffer_partial(MemoryBuffer *output, { AvgLogLum *avg = m_cachedInstance; const float igm = avg->igm; - const float offset = this->m_data->offset; + const float offset = m_data->offset; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { copy_v4_v4(it.out, it.in(0)); mul_v3_fl(it.out, avg->al); @@ -248,8 +248,8 @@ void PhotoreceptorTonemapOperation::update_memory_buffer_partial(MemoryBuffer *o Span inputs) { AvgLogLum *avg = m_cachedInstance; - NodeTonemap *ntm = this->m_data; - const float f = expf(-this->m_data->f); + NodeTonemap *ntm = m_data; + const float f = expf(-m_data->f); const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); const float ic = 1.0f - ntm->c; const float ia = 1.0f - ntm->a; diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h index 56b57730ec1..7fe496ba107 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.h +++ b/source/blender/compositor/operations/COM_TonemapOperation.h @@ -79,7 +79,7 @@ class TonemapOperation : public MultiThreadedOperation { void setData(NodeTonemap *data) { - this->m_data = data; + m_data = data; } bool determineDependingAreaOfInterest(rcti *input, diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index d6f0ce9d24c..c424ece572d 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -27,14 +27,14 @@ namespace blender::compositor { TrackPositionOperation::TrackPositionOperation() { this->addOutputSocket(DataType::Value); - this->m_movieClip = nullptr; - this->m_framenumber = 0; - this->m_trackingObjectName[0] = 0; - this->m_trackName[0] = 0; - this->m_axis = 0; - this->m_position = CMP_TRACKPOS_ABSOLUTE; - this->m_relativeFrame = 0; - this->m_speed_output = false; + m_movieClip = nullptr; + m_framenumber = 0; + m_trackingObjectName[0] = 0; + m_trackName[0] = 0; + m_axis = 0; + m_position = CMP_TRACKPOS_ABSOLUTE; + m_relativeFrame = 0; + m_speed_output = false; flags.is_set_operation = true; is_track_position_calculated_ = false; } @@ -54,77 +54,76 @@ void TrackPositionOperation::calc_track_position() MovieTrackingObject *object; track_position_ = 0; - zero_v2(this->m_markerPos); - zero_v2(this->m_relativePos); + zero_v2(m_markerPos); + zero_v2(m_relativePos); - if (!this->m_movieClip) { + if (!m_movieClip) { return; } - tracking = &this->m_movieClip->tracking; + tracking = &m_movieClip->tracking; - BKE_movieclip_user_set_frame(&user, this->m_framenumber); - BKE_movieclip_get_size(this->m_movieClip, &user, &this->m_width, &this->m_height); + BKE_movieclip_user_set_frame(&user, m_framenumber); + BKE_movieclip_get_size(m_movieClip, &user, &m_width, &m_height); - object = BKE_tracking_object_get_named(tracking, this->m_trackingObjectName); + object = BKE_tracking_object_get_named(tracking, m_trackingObjectName); if (object) { MovieTrackingTrack *track; - track = BKE_tracking_track_get_named(tracking, object, this->m_trackName); + track = BKE_tracking_track_get_named(tracking, object, m_trackName); if (track) { MovieTrackingMarker *marker; - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, - this->m_framenumber); + int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, m_framenumber); marker = BKE_tracking_marker_get(track, clip_framenr); - copy_v2_v2(this->m_markerPos, marker->pos); + copy_v2_v2(m_markerPos, marker->pos); - if (this->m_speed_output) { - int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, - this->m_relativeFrame); + if (m_speed_output) { + int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, + m_relativeFrame); marker = BKE_tracking_marker_get_exact(track, relative_clip_framenr); if (marker != nullptr && (marker->flag & MARKER_DISABLED) == 0) { - copy_v2_v2(this->m_relativePos, marker->pos); + copy_v2_v2(m_relativePos, marker->pos); } else { - copy_v2_v2(this->m_relativePos, this->m_markerPos); + copy_v2_v2(m_relativePos, m_markerPos); } - if (this->m_relativeFrame < this->m_framenumber) { - swap_v2_v2(this->m_relativePos, this->m_markerPos); + if (m_relativeFrame < m_framenumber) { + swap_v2_v2(m_relativePos, m_markerPos); } } - else if (this->m_position == CMP_TRACKPOS_RELATIVE_START) { + else if (m_position == CMP_TRACKPOS_RELATIVE_START) { int i; for (i = 0; i < track->markersnr; i++) { marker = &track->markers[i]; if ((marker->flag & MARKER_DISABLED) == 0) { - copy_v2_v2(this->m_relativePos, marker->pos); + copy_v2_v2(m_relativePos, marker->pos); break; } } } - else if (this->m_position == CMP_TRACKPOS_RELATIVE_FRAME) { - int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, - this->m_relativeFrame); + else if (m_position == CMP_TRACKPOS_RELATIVE_FRAME) { + int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, + m_relativeFrame); marker = BKE_tracking_marker_get(track, relative_clip_framenr); - copy_v2_v2(this->m_relativePos, marker->pos); + copy_v2_v2(m_relativePos, marker->pos); } } } - track_position_ = this->m_markerPos[this->m_axis] - this->m_relativePos[this->m_axis]; - if (this->m_axis == 0) { - track_position_ *= this->m_width; + track_position_ = m_markerPos[m_axis] - m_relativePos[m_axis]; + if (m_axis == 0) { + track_position_ *= m_width; } else { - track_position_ *= this->m_height; + track_position_ *= m_height; } } @@ -133,13 +132,13 @@ void TrackPositionOperation::executePixelSampled(float output[4], float /*y*/, PixelSampler /*sampler*/) { - output[0] = this->m_markerPos[this->m_axis] - this->m_relativePos[this->m_axis]; + output[0] = m_markerPos[m_axis] - m_relativePos[m_axis]; - if (this->m_axis == 0) { - output[0] *= this->m_width; + if (m_axis == 0) { + output[0] *= m_width; } else { - output[0] *= this->m_height; + output[0] *= m_height; } } diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h index a47d3e03ed4..8fcbf6d31af 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.h +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h @@ -60,35 +60,35 @@ class TrackPositionOperation : public ConstantOperation { void setMovieClip(MovieClip *clip) { - this->m_movieClip = clip; + m_movieClip = clip; } void setTrackingObject(char *object) { - BLI_strncpy(this->m_trackingObjectName, object, sizeof(this->m_trackingObjectName)); + BLI_strncpy(m_trackingObjectName, object, sizeof(m_trackingObjectName)); } void setTrackName(char *track) { - BLI_strncpy(this->m_trackName, track, sizeof(this->m_trackName)); + BLI_strncpy(m_trackName, track, sizeof(m_trackName)); } void setFramenumber(int framenumber) { - this->m_framenumber = framenumber; + m_framenumber = framenumber; } void setAxis(int value) { - this->m_axis = value; + m_axis = value; } void setPosition(int value) { - this->m_position = value; + m_position = value; } void setRelativeFrame(int value) { - this->m_relativeFrame = value; + m_relativeFrame = value; } void setSpeedOutput(bool speed_output) { - this->m_speed_output = speed_output; + m_speed_output = speed_output; } void initExecution() override; diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc index 9868f21654e..05fe267b174 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.cc +++ b/source/blender/compositor/operations/COM_TranslateOperation.cc @@ -30,28 +30,28 @@ TranslateOperation::TranslateOperation(DataType data_type, ResizeMode resize_mod this->addInputSocket(DataType::Value, ResizeMode::None); this->addOutputSocket(data_type); this->set_canvas_input_index(0); - this->m_inputOperation = nullptr; - this->m_inputXOperation = nullptr; - this->m_inputYOperation = nullptr; - this->m_isDeltaSet = false; - this->m_factorX = 1.0f; - this->m_factorY = 1.0f; + m_inputOperation = nullptr; + m_inputXOperation = nullptr; + m_inputYOperation = nullptr; + m_isDeltaSet = false; + m_factorX = 1.0f; + m_factorY = 1.0f; this->x_extend_mode_ = MemoryBufferExtend::Clip; this->y_extend_mode_ = MemoryBufferExtend::Clip; } void TranslateOperation::initExecution() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_inputXOperation = this->getInputSocketReader(1); - this->m_inputYOperation = this->getInputSocketReader(2); + m_inputOperation = this->getInputSocketReader(0); + m_inputXOperation = this->getInputSocketReader(1); + m_inputYOperation = this->getInputSocketReader(2); } void TranslateOperation::deinitExecution() { - this->m_inputOperation = nullptr; - this->m_inputXOperation = nullptr; - this->m_inputYOperation = nullptr; + m_inputOperation = nullptr; + m_inputXOperation = nullptr; + m_inputYOperation = nullptr; } void TranslateOperation::executePixelSampled(float output[4], @@ -64,7 +64,7 @@ void TranslateOperation::executePixelSampled(float output[4], float originalXPos = x - this->getDeltaX(); float originalYPos = y - this->getDeltaY(); - this->m_inputOperation->readSampled(output, originalXPos, originalYPos, PixelSampler::Bilinear); + m_inputOperation->readSampled(output, originalXPos, originalYPos, PixelSampler::Bilinear); } bool TranslateOperation::determineDependingAreaOfInterest(rcti *input, diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h index c5a3d1ffd99..734a19008d6 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.h +++ b/source/blender/compositor/operations/COM_TranslateOperation.h @@ -56,29 +56,29 @@ class TranslateOperation : public MultiThreadedOperation { float getDeltaX() { - return this->m_deltaX * this->m_factorX; + return m_deltaX * m_factorX; } float getDeltaY() { - return this->m_deltaY * this->m_factorY; + return m_deltaY * m_factorY; } inline void ensureDelta() { - if (!this->m_isDeltaSet) { + if (!m_isDeltaSet) { if (execution_model_ == eExecutionModel::Tiled) { float tempDelta[4]; - this->m_inputXOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); - this->m_deltaX = tempDelta[0]; - this->m_inputYOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); - this->m_deltaY = tempDelta[0]; + m_inputXOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); + m_deltaX = tempDelta[0]; + m_inputYOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); + m_deltaY = tempDelta[0]; } else { m_deltaX = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f); m_deltaY = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f); } - this->m_isDeltaSet = true; + m_isDeltaSet = true; } } diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc index d31290acac7..0afc315ae7e 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc @@ -34,24 +34,24 @@ VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation() flags.complex = true; flags.open_cl = true; - this->m_inputProgram = nullptr; - this->m_inputBokehProgram = nullptr; - this->m_inputSizeProgram = nullptr; - this->m_maxBlur = 32.0f; - this->m_threshold = 1.0f; - this->m_do_size_scale = false; + m_inputProgram = nullptr; + m_inputBokehProgram = nullptr; + m_inputSizeProgram = nullptr; + m_maxBlur = 32.0f; + m_threshold = 1.0f; + m_do_size_scale = false; #ifdef COM_DEFOCUS_SEARCH - this->m_inputSearchProgram = nullptr; + m_inputSearchProgram = nullptr; #endif } void VariableSizeBokehBlurOperation::initExecution() { - this->m_inputProgram = getInputSocketReader(0); - this->m_inputBokehProgram = getInputSocketReader(1); - this->m_inputSizeProgram = getInputSocketReader(2); + m_inputProgram = getInputSocketReader(0); + m_inputBokehProgram = getInputSocketReader(1); + m_inputSizeProgram = getInputSocketReader(2); #ifdef COM_DEFOCUS_SEARCH - this->m_inputSearchProgram = getInputSocketReader(3); + m_inputSearchProgram = getInputSocketReader(3); #endif QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -65,19 +65,18 @@ struct VariableSizeBokehBlurTileData { void *VariableSizeBokehBlurOperation::initializeTileData(rcti *rect) { VariableSizeBokehBlurTileData *data = new VariableSizeBokehBlurTileData(); - data->color = (MemoryBuffer *)this->m_inputProgram->initializeTileData(rect); - data->bokeh = (MemoryBuffer *)this->m_inputBokehProgram->initializeTileData(rect); - data->size = (MemoryBuffer *)this->m_inputSizeProgram->initializeTileData(rect); + data->color = (MemoryBuffer *)m_inputProgram->initializeTileData(rect); + data->bokeh = (MemoryBuffer *)m_inputBokehProgram->initializeTileData(rect); + data->size = (MemoryBuffer *)m_inputSizeProgram->initializeTileData(rect); rcti rect2; - this->determineDependingAreaOfInterest( - rect, (ReadBufferOperation *)this->m_inputSizeProgram, &rect2); + this->determineDependingAreaOfInterest(rect, (ReadBufferOperation *)m_inputSizeProgram, &rect2); const float max_dim = MAX2(this->getWidth(), this->getHeight()); - const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + const float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; data->maxBlurScalar = (int)(data->size->get_max_value(rect2) * scalar); - CLAMP(data->maxBlurScalar, 1.0f, this->m_maxBlur); + CLAMP(data->maxBlurScalar, 1.0f, m_maxBlur); return data; } @@ -102,7 +101,7 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, float color_accum[4]; const float max_dim = MAX2(getWidth(), getHeight()); - const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + const float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; int maxBlurScalar = tileData->maxBlurScalar; BLI_assert(inputBokehBuffer->getWidth() == COM_BLUR_BOKEH_PIXELS); @@ -110,10 +109,10 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, #ifdef COM_DEFOCUS_SEARCH float search[4]; - this->m_inputSearchProgram->read(search, - x / InverseSearchRadiusOperation::DIVIDER, - y / InverseSearchRadiusOperation::DIVIDER, - nullptr); + m_inputSearchProgram->read(search, + x / InverseSearchRadiusOperation::DIVIDER, + y / InverseSearchRadiusOperation::DIVIDER, + nullptr); int minx = search[0]; int miny = search[1]; int maxx = search[2]; @@ -136,7 +135,7 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, const int addYStepValue = addXStepValue; const int addXStepColor = addXStepValue * COM_DATA_TYPE_COLOR_CHANNELS; - if (size_center > this->m_threshold) { + if (size_center > m_threshold) { for (int ny = miny; ny < maxy; ny += addYStepValue) { float dy = ny - y; int offsetValueNy = ny * inputSizeBuffer->getWidth(); @@ -145,7 +144,7 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, for (int nx = minx; nx < maxx; nx += addXStepValue) { if (nx != x || ny != y) { float size = MIN2(inputSizeFloatBuffer[offsetValueNxNy] * scalar, size_center); - if (size > this->m_threshold) { + if (size > m_threshold) { float dx = nx - x; if (size > fabsf(dx) && size > fabsf(dy)) { float uv[2] = { @@ -172,9 +171,9 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, output[3] = color_accum[3] / multiplier_accum[3]; /* blend in out values over the threshold, otherwise we get sharp, ugly transitions */ - if ((size_center > this->m_threshold) && (size_center < this->m_threshold * 2.0f)) { + if ((size_center > m_threshold) && (size_center < m_threshold * 2.0f)) { /* factor from 0-1 */ - float fac = (size_center - this->m_threshold) / this->m_threshold; + float fac = (size_center - m_threshold) / m_threshold; interp_v4_v4v4(output, readColor, output, fac); } } @@ -191,22 +190,21 @@ void VariableSizeBokehBlurOperation::executeOpenCL(OpenCLDevice *device, cl_int step = this->getStep(); cl_int maxBlur; - cl_float threshold = this->m_threshold; + cl_float threshold = m_threshold; - MemoryBuffer *sizeMemoryBuffer = this->m_inputSizeProgram->getInputMemoryBuffer( - inputMemoryBuffers); + MemoryBuffer *sizeMemoryBuffer = m_inputSizeProgram->getInputMemoryBuffer(inputMemoryBuffers); const float max_dim = MAX2(getWidth(), getHeight()); - cl_float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + cl_float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; - maxBlur = (cl_int)min_ff(sizeMemoryBuffer->get_max_value() * scalar, (float)this->m_maxBlur); + maxBlur = (cl_int)min_ff(sizeMemoryBuffer->get_max_value() * scalar, (float)m_maxBlur); device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram); + defocusKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 1, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputBokehProgram); + defocusKernel, 1, -1, clMemToCleanUp, inputMemoryBuffers, m_inputBokehProgram); device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 2, 4, clMemToCleanUp, inputMemoryBuffers, this->m_inputSizeProgram); + defocusKernel, 2, 4, clMemToCleanUp, inputMemoryBuffers, m_inputSizeProgram); device->COM_clAttachOutputMemoryBufferToKernelParameter(defocusKernel, 3, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter(defocusKernel, 5, outputMemoryBuffer); clSetKernelArg(defocusKernel, 6, sizeof(cl_int), &step); @@ -220,11 +218,11 @@ void VariableSizeBokehBlurOperation::executeOpenCL(OpenCLDevice *device, void VariableSizeBokehBlurOperation::deinitExecution() { - this->m_inputProgram = nullptr; - this->m_inputBokehProgram = nullptr; - this->m_inputSizeProgram = nullptr; + m_inputProgram = nullptr; + m_inputBokehProgram = nullptr; + m_inputSizeProgram = nullptr; #ifdef COM_DEFOCUS_SEARCH - this->m_inputSearchProgram = nullptr; + m_inputSearchProgram = nullptr; #endif } @@ -235,8 +233,8 @@ bool VariableSizeBokehBlurOperation::determineDependingAreaOfInterest( rcti bokehInput; const float max_dim = MAX2(getWidth(), getHeight()); - const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; - int maxBlurScalar = this->m_maxBlur * scalar; + const float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + int maxBlurScalar = m_maxBlur * scalar; newInput.xmax = input->xmax + maxBlurScalar + 2; newInput.xmin = input->xmin - maxBlurScalar + 2; @@ -439,12 +437,12 @@ InverseSearchRadiusOperation::InverseSearchRadiusOperation() this->addInputSocket(DataType::Value, ResizeMode::Align); /* Radius. */ this->addOutputSocket(DataType::Color); this->flags.complex = true; - this->m_inputRadius = nullptr; + m_inputRadius = nullptr; } void InverseSearchRadiusOperation::initExecution() { - this->m_inputRadius = this->getInputSocketReader(0); + m_inputRadius = this->getInputSocketReader(0); } void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) @@ -452,8 +450,8 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) MemoryBuffer *data = new MemoryBuffer(DataType::Color, rect); float *buffer = data->getBuffer(); int x, y; - int width = this->m_inputRadius->getWidth(); - int height = this->m_inputRadius->getHeight(); + int width = m_inputRadius->getWidth(); + int height = m_inputRadius->getHeight(); float temp[4]; int offset = 0; for (y = rect->ymin; y < rect->ymax; y++) { @@ -478,7 +476,7 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) for (int x2 = 0; x2 < DIVIDER; x2++) { for (int y2 = 0; y2 < DIVIDER; y2++) { - this->m_inputRadius->read(temp, rx + x2, ry + y2, PixelSampler::Nearest); + m_inputRadius->read(temp, rx + x2, ry + y2, PixelSampler::Nearest); if (radius < temp[0]) { radius = temp[0]; maxx = x2; @@ -519,7 +517,7 @@ void InverseSearchRadiusOperation::deinitializeTileData(rcti *rect, void *data) void InverseSearchRadiusOperation::deinitExecution() { - this->m_inputRadius = nullptr; + m_inputRadius = nullptr; } void InverseSearchRadiusOperation::determineResolution(unsigned int resolution[2], diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h index 3634bc2f1d7..6819e64ce80 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h @@ -72,17 +72,17 @@ class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public Qua void setMaxBlur(int maxRadius) { - this->m_maxBlur = maxRadius; + m_maxBlur = maxRadius; } void setThreshold(float threshold) { - this->m_threshold = threshold; + m_threshold = threshold; } void setDoScaleSize(bool scale_size) { - this->m_do_size_scale = scale_size; + m_do_size_scale = scale_size; } void executeOpenCL(OpenCLDevice *device, @@ -134,7 +134,7 @@ class InverseSearchRadiusOperation : public NodeOperation { void setMaxBlur(int maxRadius) { - this->m_maxBlur = maxRadius; + m_maxBlur = maxRadius; } }; #endif diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc index 57053470d7f..acde0447596 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc @@ -46,21 +46,21 @@ VectorBlurOperation::VectorBlurOperation() this->addInputSocket(DataType::Value); /* ZBUF */ this->addInputSocket(DataType::Color); /* SPEED */ this->addOutputSocket(DataType::Color); - this->m_settings = nullptr; - this->m_cachedInstance = nullptr; - this->m_inputImageProgram = nullptr; - this->m_inputSpeedProgram = nullptr; - this->m_inputZProgram = nullptr; + m_settings = nullptr; + m_cachedInstance = nullptr; + m_inputImageProgram = nullptr; + m_inputSpeedProgram = nullptr; + m_inputZProgram = nullptr; flags.complex = true; flags.is_fullframe_operation = true; } void VectorBlurOperation::initExecution() { initMutex(); - this->m_inputImageProgram = getInputSocketReader(0); - this->m_inputZProgram = getInputSocketReader(1); - this->m_inputSpeedProgram = getInputSocketReader(2); - this->m_cachedInstance = nullptr; + m_inputImageProgram = getInputSocketReader(0); + m_inputZProgram = getInputSocketReader(1); + m_inputSpeedProgram = getInputSocketReader(2); + m_cachedInstance = nullptr; QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -74,38 +74,38 @@ void VectorBlurOperation::executePixel(float output[4], int x, int y, void *data void VectorBlurOperation::deinitExecution() { deinitMutex(); - this->m_inputImageProgram = nullptr; - this->m_inputSpeedProgram = nullptr; - this->m_inputZProgram = nullptr; - if (this->m_cachedInstance) { - MEM_freeN(this->m_cachedInstance); - this->m_cachedInstance = nullptr; + m_inputImageProgram = nullptr; + m_inputSpeedProgram = nullptr; + m_inputZProgram = nullptr; + if (m_cachedInstance) { + MEM_freeN(m_cachedInstance); + m_cachedInstance = nullptr; } } void *VectorBlurOperation::initializeTileData(rcti *rect) { - if (this->m_cachedInstance) { - return this->m_cachedInstance; + if (m_cachedInstance) { + return m_cachedInstance; } lockMutex(); - if (this->m_cachedInstance == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_inputImageProgram->initializeTileData(rect); - MemoryBuffer *speed = (MemoryBuffer *)this->m_inputSpeedProgram->initializeTileData(rect); - MemoryBuffer *z = (MemoryBuffer *)this->m_inputZProgram->initializeTileData(rect); + if (m_cachedInstance == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)m_inputImageProgram->initializeTileData(rect); + MemoryBuffer *speed = (MemoryBuffer *)m_inputSpeedProgram->initializeTileData(rect); + MemoryBuffer *z = (MemoryBuffer *)m_inputZProgram->initializeTileData(rect); float *data = (float *)MEM_dupallocN(tile->getBuffer()); this->generateVectorBlur(data, tile, speed, z); - this->m_cachedInstance = data; + m_cachedInstance = data; } unlockMutex(); - return this->m_cachedInstance; + return m_cachedInstance; } bool VectorBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) { - if (this->m_cachedInstance == nullptr) { + if (m_cachedInstance == nullptr) { rcti newInput; newInput.xmax = this->getWidth(); newInput.xmin = 0; @@ -165,11 +165,11 @@ void VectorBlurOperation::generateVectorBlur(float *data, MemoryBuffer *inputZ) { NodeBlurData blurdata; - blurdata.samples = this->m_settings->samples / QualityStepHelper::getStep(); - blurdata.maxspeed = this->m_settings->maxspeed; - blurdata.minspeed = this->m_settings->minspeed; - blurdata.curved = this->m_settings->curved; - blurdata.fac = this->m_settings->fac; + blurdata.samples = m_settings->samples / QualityStepHelper::getStep(); + blurdata.maxspeed = m_settings->maxspeed; + blurdata.minspeed = m_settings->minspeed; + blurdata.curved = m_settings->curved; + blurdata.fac = m_settings->fac; zbuf_accumulate_vecblur(&blurdata, this->getWidth(), this->getHeight(), diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.h b/source/blender/compositor/operations/COM_VectorBlurOperation.h index c30c150db3c..a6c619590e8 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.h +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.h @@ -66,7 +66,7 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { void setVectorBlurSettings(NodeBlurData *settings) { - this->m_settings = settings; + m_settings = settings; } bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cc b/source/blender/compositor/operations/COM_VectorCurveOperation.cc index c2087fd071e..2dd8f114038 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cc @@ -27,12 +27,12 @@ VectorCurveOperation::VectorCurveOperation() this->addInputSocket(DataType::Vector); this->addOutputSocket(DataType::Vector); - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } void VectorCurveOperation::initExecution() { CurveBaseOperation::initExecution(); - this->m_inputProgram = this->getInputSocketReader(0); + m_inputProgram = this->getInputSocketReader(0); } void VectorCurveOperation::executePixelSampled(float output[4], @@ -42,22 +42,22 @@ void VectorCurveOperation::executePixelSampled(float output[4], { float input[4]; - this->m_inputProgram->readSampled(input, x, y, sampler); + m_inputProgram->readSampled(input, x, y, sampler); - BKE_curvemapping_evaluate_premulRGBF(this->m_curveMapping, output, input); + BKE_curvemapping_evaluate_premulRGBF(m_curveMapping, output, input); } void VectorCurveOperation::deinitExecution() { CurveBaseOperation::deinitExecution(); - this->m_inputProgram = nullptr; + m_inputProgram = nullptr; } void VectorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *curve_map = this->m_curveMapping; + CurveMapping *curve_map = m_curveMapping; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { BKE_curvemapping_evaluate_premulRGBF(curve_map, it.out, it.in(0)); } diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index b0cfe90088a..effda365d56 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -33,23 +33,23 @@ ViewerOperation::ViewerOperation() { this->setImage(nullptr); this->setImageUser(nullptr); - this->m_outputBuffer = nullptr; - this->m_depthBuffer = nullptr; - this->m_active = false; - this->m_doDepthBuffer = false; - this->m_viewSettings = nullptr; - this->m_displaySettings = nullptr; - this->m_useAlphaInput = false; + m_outputBuffer = nullptr; + m_depthBuffer = nullptr; + m_active = false; + m_doDepthBuffer = false; + m_viewSettings = nullptr; + m_displaySettings = nullptr; + m_useAlphaInput = false; this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); - this->m_imageInput = nullptr; - this->m_alphaInput = nullptr; - this->m_depthInput = nullptr; - this->m_rd = nullptr; - this->m_viewName = nullptr; + m_imageInput = nullptr; + m_alphaInput = nullptr; + m_depthInput = nullptr; + m_rd = nullptr; + m_viewName = nullptr; flags.use_viewer_border = true; flags.is_viewer_operation = true; } @@ -57,10 +57,10 @@ ViewerOperation::ViewerOperation() void ViewerOperation::initExecution() { /* When initializing the tree during initial load the width and height can be zero. */ - this->m_imageInput = getInputSocketReader(0); - this->m_alphaInput = getInputSocketReader(1); - this->m_depthInput = getInputSocketReader(2); - this->m_doDepthBuffer = (this->m_depthInput != nullptr); + m_imageInput = getInputSocketReader(0); + m_alphaInput = getInputSocketReader(1); + m_depthInput = getInputSocketReader(2); + m_doDepthBuffer = (m_depthInput != nullptr); if (isActiveViewerOutput() && !exec_system_->is_breaked()) { initImage(); @@ -69,16 +69,16 @@ void ViewerOperation::initExecution() void ViewerOperation::deinitExecution() { - this->m_imageInput = nullptr; - this->m_alphaInput = nullptr; - this->m_depthInput = nullptr; - this->m_outputBuffer = nullptr; + m_imageInput = nullptr; + m_alphaInput = nullptr; + m_depthInput = nullptr; + m_outputBuffer = nullptr; } void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) { - float *buffer = this->m_outputBuffer; - float *depthbuffer = this->m_depthBuffer; + float *buffer = m_outputBuffer; + float *depthbuffer = m_depthBuffer; if (!buffer) { return; } @@ -97,12 +97,12 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) for (y = y1; y < y2 && (!breaked); y++) { for (x = x1; x < x2; x++) { - this->m_imageInput->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); - if (this->m_useAlphaInput) { - this->m_alphaInput->readSampled(alpha, x, y, PixelSampler::Nearest); + m_imageInput->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); + if (m_useAlphaInput) { + m_alphaInput->readSampled(alpha, x, y, PixelSampler::Nearest); buffer[offset4 + 3] = alpha[0]; } - this->m_depthInput->readSampled(depth, x, y, PixelSampler::Nearest); + m_depthInput->readSampled(depth, x, y, PixelSampler::Nearest); depthbuffer[offset] = depth[0]; offset++; @@ -119,8 +119,8 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - const int sceneRenderWidth = this->m_rd->xsch * this->m_rd->size / 100; - const int sceneRenderHeight = this->m_rd->ysch * this->m_rd->size / 100; + const int sceneRenderWidth = m_rd->xsch * m_rd->size / 100; + const int sceneRenderHeight = m_rd->ysch * m_rd->size / 100; rcti local_preferred = preferred_area; local_preferred.xmax = local_preferred.xmin + sceneRenderWidth; @@ -131,20 +131,20 @@ void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) void ViewerOperation::initImage() { - Image *ima = this->m_image; - ImageUser iuser = *this->m_imageUser; + Image *ima = m_image; + ImageUser iuser = *m_imageUser; void *lock; ImBuf *ibuf; /* make sure the image has the correct number of views */ - if (ima && BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) { - BKE_image_ensure_viewer_views(this->m_rd, ima, this->m_imageUser); + if (ima && BKE_scene_multiview_is_render_view_first(m_rd, m_viewName)) { + BKE_image_ensure_viewer_views(m_rd, ima, m_imageUser); } BLI_thread_lock(LOCK_DRAW_IMAGE); /* local changes to the original ImageUser */ - iuser.multi_index = BKE_scene_multiview_view_id_get(this->m_rd, this->m_viewName); + iuser.multi_index = BKE_scene_multiview_view_id_get(m_rd, m_viewName); ibuf = BKE_image_acquire_ibuf(ima, &iuser, &lock); if (!ibuf) { @@ -184,16 +184,16 @@ void ViewerOperation::initImage() } /* now we combine the input with ibuf */ - this->m_outputBuffer = ibuf->rect_float; + m_outputBuffer = ibuf->rect_float; /* needed for display buffer update */ - this->m_ibuf = ibuf; + m_ibuf = ibuf; if (m_doDepthBuffer) { - this->m_depthBuffer = ibuf->zbuf_float; + m_depthBuffer = ibuf->zbuf_float; } - BKE_image_release_ibuf(this->m_image, this->m_ibuf, lock); + BKE_image_release_ibuf(m_image, m_ibuf, lock); BLI_thread_unlock(LOCK_DRAW_IMAGE); } @@ -205,19 +205,19 @@ void ViewerOperation::updateImage(const rcti *rect) } float *buffer = m_outputBuffer; - IMB_partial_display_buffer_update(this->m_ibuf, + IMB_partial_display_buffer_update(m_ibuf, buffer, nullptr, display_width_, 0, 0, - this->m_viewSettings, - this->m_displaySettings, + m_viewSettings, + m_displaySettings, rect->xmin, rect->ymin, rect->xmax, rect->ymax); - this->m_image->gpuflag |= IMA_GPU_REFRESH; + m_image->gpuflag |= IMA_GPU_REFRESH; this->updateDraw(); } @@ -244,7 +244,7 @@ void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, display_width_, display_height_); const MemoryBuffer *input_image = inputs[0]; output_buffer.copy_from(input_image, area, offset_x, offset_y); - if (this->m_useAlphaInput) { + if (m_useAlphaInput) { const MemoryBuffer *input_alpha = inputs[1]; output_buffer.copy_from( input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, offset_x, offset_y, 3); diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h index 95ee982f692..137b7be53a9 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.h +++ b/source/blender/compositor/operations/COM_ViewerOperation.h @@ -68,65 +68,65 @@ class ViewerOperation : public MultiThreadedOperation { } void setImage(Image *image) { - this->m_image = image; + m_image = image; } void setImageUser(ImageUser *imageUser) { - this->m_imageUser = imageUser; + m_imageUser = imageUser; } bool isActiveViewerOutput() const override { - return this->m_active; + return m_active; } void setActive(bool active) { - this->m_active = active; + m_active = active; } void setCenterX(float centerX) { - this->m_centerX = centerX; + m_centerX = centerX; } void setCenterY(float centerY) { - this->m_centerY = centerY; + m_centerY = centerY; } void setChunkOrder(ChunkOrdering tileOrder) { - this->m_chunkOrder = tileOrder; + m_chunkOrder = tileOrder; } float getCenterX() const { - return this->m_centerX; + return m_centerX; } float getCenterY() const { - return this->m_centerY; + return m_centerY; } ChunkOrdering getChunkOrder() const { - return this->m_chunkOrder; + return m_chunkOrder; } eCompositorPriority getRenderPriority() const override; void setUseAlphaInput(bool value) { - this->m_useAlphaInput = value; + m_useAlphaInput = value; } void setRenderData(const RenderData *rd) { - this->m_rd = rd; + m_rd = rd; } void setViewName(const char *viewName) { - this->m_viewName = viewName; + m_viewName = viewName; } void setViewSettings(const ColorManagedViewSettings *viewSettings) { - this->m_viewSettings = viewSettings; + m_viewSettings = viewSettings; } void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) { - this->m_displaySettings = displaySettings; + m_displaySettings = displaySettings; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc index 393128ded41..7e49d7a7a79 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.cc +++ b/source/blender/compositor/operations/COM_WrapOperation.cc @@ -24,7 +24,7 @@ namespace blender::compositor { WrapOperation::WrapOperation(DataType datatype) : ReadBufferOperation(datatype) { - this->m_wrappingType = CMP_NODE_WRAP_NONE; + m_wrappingType = CMP_NODE_WRAP_NONE; } inline float WrapOperation::getWrappedOriginalXPos(float x) diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cc b/source/blender/compositor/operations/COM_WriteBufferOperation.cc index 5d3f1ea3e1e..ffc3788de12 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.cc +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cc @@ -24,16 +24,16 @@ namespace blender::compositor { WriteBufferOperation::WriteBufferOperation(DataType datatype) { this->addInputSocket(datatype); - this->m_memoryProxy = new MemoryProxy(datatype); - this->m_memoryProxy->setWriteBufferOperation(this); - this->m_memoryProxy->setExecutor(nullptr); + m_memoryProxy = new MemoryProxy(datatype); + m_memoryProxy->setWriteBufferOperation(this); + m_memoryProxy->setExecutor(nullptr); flags.is_write_buffer_operation = true; } WriteBufferOperation::~WriteBufferOperation() { - if (this->m_memoryProxy) { - delete this->m_memoryProxy; - this->m_memoryProxy = nullptr; + if (m_memoryProxy) { + delete m_memoryProxy; + m_memoryProxy = nullptr; } } @@ -42,28 +42,28 @@ void WriteBufferOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - this->m_input->readSampled(output, x, y, sampler); + m_input->readSampled(output, x, y, sampler); } void WriteBufferOperation::initExecution() { - this->m_input = this->getInputOperation(0); - this->m_memoryProxy->allocate(this->getWidth(), this->getHeight()); + m_input = this->getInputOperation(0); + m_memoryProxy->allocate(this->getWidth(), this->getHeight()); } void WriteBufferOperation::deinitExecution() { - this->m_input = nullptr; - this->m_memoryProxy->free(); + m_input = nullptr; + m_memoryProxy->free(); } void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) { - MemoryBuffer *memoryBuffer = this->m_memoryProxy->getBuffer(); + MemoryBuffer *memoryBuffer = m_memoryProxy->getBuffer(); float *buffer = memoryBuffer->getBuffer(); const uint8_t num_channels = memoryBuffer->get_num_channels(); - if (this->m_input->get_flags().complex) { - void *data = this->m_input->initializeTileData(rect); + if (m_input->get_flags().complex) { + void *data = m_input->initializeTileData(rect); int x1 = rect->xmin; int y1 = rect->ymin; int x2 = rect->xmax; @@ -74,7 +74,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ for (y = y1; y < y2 && (!breaked); y++) { int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels; for (x = x1; x < x2; x++) { - this->m_input->read(&(buffer[offset4]), x, y, data); + m_input->read(&(buffer[offset4]), x, y, data); offset4 += num_channels; } if (isBraked()) { @@ -82,7 +82,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ } } if (data) { - this->m_input->deinitializeTileData(rect, data); + m_input->deinitializeTileData(rect, data); data = nullptr; } } @@ -98,7 +98,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ for (y = y1; y < y2 && (!breaked); y++) { int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels; for (x = x1; x < x2; x++) { - this->m_input->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); + m_input->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); offset4 += num_channels; } if (isBraked()) { @@ -147,12 +147,12 @@ void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, clMemToCleanUp->push_back(clOutputBuffer); std::list *clKernelsToCleanUp = new std::list(); - this->m_input->executeOpenCL(device, - outputBuffer, - clOutputBuffer, - inputMemoryBuffers, - clMemToCleanUp, - clKernelsToCleanUp); + m_input->executeOpenCL(device, + outputBuffer, + clOutputBuffer, + inputMemoryBuffers, + clMemToCleanUp, + clKernelsToCleanUp); /* STEP 3 */ diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.h b/source/blender/compositor/operations/COM_WriteBufferOperation.h index b7dededc69f..4e6e81b7a7f 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.h +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.h @@ -40,7 +40,7 @@ class WriteBufferOperation : public NodeOperation { ~WriteBufferOperation(); MemoryProxy *getMemoryProxy() { - return this->m_memoryProxy; + return m_memoryProxy; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; bool isSingleValue() const diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc index 1b04c749c8b..ed77d05738d 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.cc +++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc @@ -28,19 +28,19 @@ ZCombineOperation::ZCombineOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - this->m_image1Reader = nullptr; - this->m_depth1Reader = nullptr; - this->m_image2Reader = nullptr; - this->m_depth2Reader = nullptr; + m_image1Reader = nullptr; + m_depth1Reader = nullptr; + m_image2Reader = nullptr; + m_depth2Reader = nullptr; this->flags.can_be_constant = true; } void ZCombineOperation::initExecution() { - this->m_image1Reader = this->getInputSocketReader(0); - this->m_depth1Reader = this->getInputSocketReader(1); - this->m_image2Reader = this->getInputSocketReader(2); - this->m_depth2Reader = this->getInputSocketReader(3); + m_image1Reader = this->getInputSocketReader(0); + m_depth1Reader = this->getInputSocketReader(1); + m_image2Reader = this->getInputSocketReader(2); + m_depth2Reader = this->getInputSocketReader(3); } void ZCombineOperation::executePixelSampled(float output[4], @@ -51,13 +51,13 @@ void ZCombineOperation::executePixelSampled(float output[4], float depth1[4]; float depth2[4]; - this->m_depth1Reader->readSampled(depth1, x, y, sampler); - this->m_depth2Reader->readSampled(depth2, x, y, sampler); + m_depth1Reader->readSampled(depth1, x, y, sampler); + m_depth2Reader->readSampled(depth2, x, y, sampler); if (depth1[0] < depth2[0]) { - this->m_image1Reader->readSampled(output, x, y, sampler); + m_image1Reader->readSampled(output, x, y, sampler); } else { - this->m_image2Reader->readSampled(output, x, y, sampler); + m_image2Reader->readSampled(output, x, y, sampler); } } @@ -83,15 +83,15 @@ void ZCombineAlphaOperation::executePixelSampled(float output[4], float color1[4]; float color2[4]; - this->m_depth1Reader->readSampled(depth1, x, y, sampler); - this->m_depth2Reader->readSampled(depth2, x, y, sampler); + m_depth1Reader->readSampled(depth1, x, y, sampler); + m_depth2Reader->readSampled(depth2, x, y, sampler); if (depth1[0] <= depth2[0]) { - this->m_image1Reader->readSampled(color1, x, y, sampler); - this->m_image2Reader->readSampled(color2, x, y, sampler); + m_image1Reader->readSampled(color1, x, y, sampler); + m_image2Reader->readSampled(color2, x, y, sampler); } else { - this->m_image1Reader->readSampled(color2, x, y, sampler); - this->m_image2Reader->readSampled(color1, x, y, sampler); + m_image1Reader->readSampled(color2, x, y, sampler); + m_image2Reader->readSampled(color1, x, y, sampler); } float fac = color1[3]; float ifac = 1.0f - fac; @@ -129,10 +129,10 @@ void ZCombineAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, void ZCombineOperation::deinitExecution() { - this->m_image1Reader = nullptr; - this->m_depth1Reader = nullptr; - this->m_image2Reader = nullptr; - this->m_depth2Reader = nullptr; + m_image1Reader = nullptr; + m_depth1Reader = nullptr; + m_image2Reader = nullptr; + m_depth2Reader = nullptr; } // MASK combine @@ -143,16 +143,16 @@ ZCombineMaskOperation::ZCombineMaskOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - this->m_maskReader = nullptr; - this->m_image1Reader = nullptr; - this->m_image2Reader = nullptr; + m_maskReader = nullptr; + m_image1Reader = nullptr; + m_image2Reader = nullptr; } void ZCombineMaskOperation::initExecution() { - this->m_maskReader = this->getInputSocketReader(0); - this->m_image1Reader = this->getInputSocketReader(1); - this->m_image2Reader = this->getInputSocketReader(2); + m_maskReader = this->getInputSocketReader(0); + m_image1Reader = this->getInputSocketReader(1); + m_image2Reader = this->getInputSocketReader(2); } void ZCombineMaskOperation::executePixelSampled(float output[4], @@ -164,9 +164,9 @@ void ZCombineMaskOperation::executePixelSampled(float output[4], float color1[4]; float color2[4]; - this->m_maskReader->readSampled(mask, x, y, sampler); - this->m_image1Reader->readSampled(color1, x, y, sampler); - this->m_image2Reader->readSampled(color2, x, y, sampler); + m_maskReader->readSampled(mask, x, y, sampler); + m_image1Reader->readSampled(color1, x, y, sampler); + m_image2Reader->readSampled(color2, x, y, sampler); interp_v4_v4v4(output, color1, color2, 1.0f - mask[0]); } @@ -192,9 +192,9 @@ void ZCombineMaskAlphaOperation::executePixelSampled(float output[4], float color1[4]; float color2[4]; - this->m_maskReader->readSampled(mask, x, y, sampler); - this->m_image1Reader->readSampled(color1, x, y, sampler); - this->m_image2Reader->readSampled(color2, x, y, sampler); + m_maskReader->readSampled(mask, x, y, sampler); + m_image1Reader->readSampled(color1, x, y, sampler); + m_image2Reader->readSampled(color2, x, y, sampler); float fac = (1.0f - mask[0]) * (1.0f - color1[3]) + mask[0] * color2[3]; float mfac = 1.0f - fac; @@ -225,9 +225,9 @@ void ZCombineMaskAlphaOperation::update_memory_buffer_partial(MemoryBuffer *outp void ZCombineMaskOperation::deinitExecution() { - this->m_image1Reader = nullptr; - this->m_maskReader = nullptr; - this->m_image2Reader = nullptr; + m_image1Reader = nullptr; + m_maskReader = nullptr; + m_image2Reader = nullptr; } } // namespace blender::compositor From a2ee3c3a9f01f5cb2f05f1e84a1b6c1931d9d4a4 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Wed, 13 Oct 2021 23:01:04 +0200 Subject: [PATCH 0767/1500] Cleanup: replace members `m_` prefix by `_` suffix in Compositor To convert old code to the current convention and use a single code style. --- .../compositor/intern/COM_CPUDevice.cc | 2 +- .../blender/compositor/intern/COM_CPUDevice.h | 4 +- .../intern/COM_CompositorContext.cc | 24 +- .../compositor/intern/COM_CompositorContext.h | 70 ++--- source/blender/compositor/intern/COM_Debug.cc | 44 ++-- source/blender/compositor/intern/COM_Debug.h | 34 +-- .../compositor/intern/COM_ExecutionGroup.cc | 244 +++++++++--------- .../compositor/intern/COM_ExecutionGroup.h | 50 ++-- .../compositor/intern/COM_ExecutionSystem.cc | 50 ++-- .../compositor/intern/COM_ExecutionSystem.h | 8 +- .../compositor/intern/COM_MemoryBuffer.cc | 113 ++++---- .../compositor/intern/COM_MemoryBuffer.h | 140 +++++----- .../compositor/intern/COM_MemoryProxy.cc | 14 +- .../compositor/intern/COM_MemoryProxy.h | 20 +- source/blender/compositor/intern/COM_Node.cc | 14 +- source/blender/compositor/intern/COM_Node.h | 54 ++-- .../compositor/intern/COM_NodeConverter.cc | 54 ++-- .../compositor/intern/COM_NodeConverter.h | 2 +- .../compositor/intern/COM_NodeGraph.cc | 12 +- .../blender/compositor/intern/COM_NodeGraph.h | 8 +- .../compositor/intern/COM_NodeOperation.cc | 52 ++-- .../compositor/intern/COM_NodeOperation.h | 62 ++--- .../intern/COM_NodeOperationBuilder.cc | 160 ++++++------ .../intern/COM_NodeOperationBuilder.h | 36 +-- .../compositor/intern/COM_OpenCLDevice.cc | 40 +-- .../compositor/intern/COM_OpenCLDevice.h | 14 +- .../intern/COM_SingleThreadedOperation.cc | 20 +- .../intern/COM_SingleThreadedOperation.h | 4 +- .../compositor/nodes/COM_DilateErodeNode.cc | 6 +- .../compositor/nodes/COM_DilateErodeNode.h | 2 +- .../compositor/nodes/COM_SocketProxyNode.cc | 4 +- .../compositor/nodes/COM_SocketProxyNode.h | 6 +- .../operations/COM_AlphaOverKeyOperation.cc | 6 +- .../operations/COM_AlphaOverMixedOperation.cc | 12 +- .../operations/COM_AlphaOverMixedOperation.h | 4 +- .../COM_AlphaOverPremultiplyOperation.cc | 6 +- .../operations/COM_AntiAliasOperation.cc | 6 +- .../operations/COM_AntiAliasOperation.h | 2 +- .../operations/COM_BilateralBlurOperation.cc | 30 +-- .../operations/COM_BilateralBlurOperation.h | 12 +- .../operations/COM_BlurBaseOperation.cc | 66 ++--- .../operations/COM_BlurBaseOperation.h | 18 +- .../operations/COM_BokehBlurOperation.cc | 100 +++---- .../operations/COM_BokehBlurOperation.h | 24 +- .../operations/COM_BokehImageOperation.cc | 60 ++--- .../operations/COM_BokehImageOperation.h | 20 +- .../operations/COM_BoxMaskOperation.cc | 64 ++--- .../operations/COM_BoxMaskOperation.h | 18 +- .../operations/COM_BrightnessOperation.cc | 32 +-- .../operations/COM_BrightnessOperation.h | 8 +- .../operations/COM_CalculateMeanOperation.cc | 38 +-- .../operations/COM_CalculateMeanOperation.h | 8 +- ...COM_CalculateStandardDeviationOperation.cc | 26 +- .../COM_CalculateStandardDeviationOperation.h | 2 +- .../operations/COM_ChangeHSVOperation.cc | 26 +- .../operations/COM_ChangeHSVOperation.h | 8 +- .../operations/COM_ChannelMatteOperation.cc | 58 ++--- .../operations/COM_ChannelMatteOperation.h | 28 +- .../operations/COM_ChromaMatteOperation.cc | 28 +- .../operations/COM_ChromaMatteOperation.h | 8 +- .../COM_ColorBalanceASCCDLOperation.cc | 28 +- .../COM_ColorBalanceASCCDLOperation.h | 16 +- .../COM_ColorBalanceLGGOperation.cc | 28 +- .../operations/COM_ColorBalanceLGGOperation.h | 16 +- .../COM_ColorCorrectionOperation.cc | 134 +++++----- .../operations/COM_ColorCorrectionOperation.h | 20 +- .../operations/COM_ColorCurveOperation.cc | 64 ++--- .../operations/COM_ColorCurveOperation.h | 20 +- .../operations/COM_ColorExposureOperation.cc | 14 +- .../operations/COM_ColorExposureOperation.h | 4 +- .../operations/COM_ColorMatteOperation.cc | 28 +- .../operations/COM_ColorMatteOperation.h | 8 +- .../operations/COM_ColorRampOperation.cc | 14 +- .../operations/COM_ColorRampOperation.h | 6 +- .../operations/COM_ColorSpillOperation.cc | 107 ++++---- .../operations/COM_ColorSpillOperation.h | 22 +- .../operations/COM_CompositorOperation.cc | 102 ++++---- .../operations/COM_CompositorOperation.h | 36 +-- .../COM_ConvertColorProfileOperation.cc | 13 +- .../COM_ConvertColorProfileOperation.h | 14 +- .../COM_ConvertDepthToRadiusOperation.cc | 56 ++-- .../COM_ConvertDepthToRadiusOperation.h | 28 +- .../operations/COM_ConvertOperation.cc | 127 +++++---- .../operations/COM_ConvertOperation.h | 20 +- .../COM_ConvolutionEdgeFilterOperation.cc | 94 +++---- .../COM_ConvolutionFilterOperation.cc | 106 ++++---- .../COM_ConvolutionFilterOperation.h | 10 +- .../operations/COM_CropOperation.cc | 58 ++--- .../compositor/operations/COM_CropOperation.h | 18 +- .../operations/COM_CryptomatteOperation.cc | 6 +- .../operations/COM_CryptomatteOperation.h | 2 +- .../operations/COM_CurveBaseOperation.cc | 22 +- .../operations/COM_CurveBaseOperation.h | 2 +- .../operations/COM_DenoiseOperation.cc | 28 +- .../operations/COM_DenoiseOperation.h | 10 +- .../operations/COM_DespeckleOperation.cc | 56 ++-- .../operations/COM_DespeckleOperation.h | 16 +- .../COM_DifferenceMatteOperation.cc | 24 +- .../operations/COM_DifferenceMatteOperation.h | 8 +- .../operations/COM_DilateErodeOperation.cc | 176 ++++++------- .../operations/COM_DilateErodeOperation.h | 30 +-- .../COM_DirectionalBlurOperation.cc | 100 +++---- .../operations/COM_DirectionalBlurOperation.h | 12 +- .../operations/COM_DisplaceOperation.cc | 18 +- .../operations/COM_DisplaceOperation.h | 6 +- .../operations/COM_DisplaceSimpleOperation.cc | 44 ++-- .../operations/COM_DisplaceSimpleOperation.h | 12 +- .../COM_DistanceRGBMatteOperation.cc | 24 +- .../COM_DistanceRGBMatteOperation.h | 8 +- .../operations/COM_DotproductOperation.cc | 16 +- .../operations/COM_DotproductOperation.h | 4 +- .../operations/COM_DoubleEdgeMaskOperation.cc | 48 ++-- .../operations/COM_DoubleEdgeMaskOperation.h | 14 +- .../operations/COM_EllipseMaskOperation.cc | 64 ++--- .../operations/COM_EllipseMaskOperation.h | 18 +- .../COM_FastGaussianBlurOperation.cc | 94 +++---- .../COM_FastGaussianBlurOperation.h | 18 +- .../operations/COM_FlipOperation.cc | 32 +-- .../compositor/operations/COM_FlipOperation.h | 10 +- .../operations/COM_GammaCorrectOperation.cc | 16 +- .../operations/COM_GammaCorrectOperation.h | 4 +- .../operations/COM_GammaOperation.cc | 16 +- .../operations/COM_GammaOperation.h | 4 +- .../COM_GaussianAlphaBlurBaseOperation.cc | 46 ++-- .../COM_GaussianAlphaBlurBaseOperation.h | 14 +- .../COM_GaussianAlphaXBlurOperation.cc | 58 ++--- .../COM_GaussianAlphaYBlurOperation.cc | 58 ++--- .../COM_GaussianBlurBaseOperation.cc | 46 ++-- .../COM_GaussianBlurBaseOperation.h | 6 +- .../COM_GaussianBokehBlurOperation.cc | 184 ++++++------- .../COM_GaussianBokehBlurOperation.h | 14 +- .../operations/COM_GaussianXBlurOperation.cc | 64 ++--- .../operations/COM_GaussianXBlurOperation.h | 2 +- .../operations/COM_GaussianYBlurOperation.cc | 64 ++--- .../operations/COM_GaussianYBlurOperation.h | 2 +- .../operations/COM_GlareBaseOperation.cc | 12 +- .../operations/COM_GlareBaseOperation.h | 6 +- .../operations/COM_GlareThresholdOperation.cc | 16 +- .../operations/COM_GlareThresholdOperation.h | 6 +- .../COM_HueSaturationValueCorrectOperation.cc | 20 +- .../COM_HueSaturationValueCorrectOperation.h | 2 +- .../operations/COM_IDMaskOperation.cc | 4 +- .../operations/COM_IDMaskOperation.h | 4 +- .../operations/COM_ImageOperation.cc | 76 +++--- .../operations/COM_ImageOperation.h | 34 +-- .../operations/COM_InpaintOperation.cc | 90 +++---- .../operations/COM_InpaintOperation.h | 16 +- .../operations/COM_InvertOperation.cc | 28 +- .../operations/COM_InvertOperation.h | 12 +- .../operations/COM_KeyingBlurOperation.cc | 36 +-- .../operations/COM_KeyingBlurOperation.h | 8 +- .../operations/COM_KeyingClipOperation.cc | 50 ++-- .../operations/COM_KeyingClipOperation.h | 20 +- .../operations/COM_KeyingDespillOperation.cc | 32 +-- .../operations/COM_KeyingDespillOperation.h | 12 +- .../operations/COM_KeyingOperation.cc | 26 +- .../operations/COM_KeyingOperation.h | 8 +- .../operations/COM_KeyingScreenOperation.cc | 58 ++--- .../operations/COM_KeyingScreenOperation.h | 14 +- .../operations/COM_LuminanceMatteOperation.cc | 16 +- .../operations/COM_LuminanceMatteOperation.h | 6 +- .../operations/COM_MapRangeOperation.cc | 38 +-- .../operations/COM_MapRangeOperation.h | 14 +- .../operations/COM_MapUVOperation.cc | 26 +- .../operations/COM_MapUVOperation.h | 8 +- .../operations/COM_MapValueOperation.cc | 12 +- .../operations/COM_MapValueOperation.h | 6 +- .../operations/COM_MaskOperation.cc | 89 +++---- .../compositor/operations/COM_MaskOperation.h | 44 ++-- .../operations/COM_MathBaseOperation.cc | 170 ++++++------ .../operations/COM_MathBaseOperation.h | 14 +- .../compositor/operations/COM_MixOperation.cc | 138 +++++----- .../compositor/operations/COM_MixOperation.h | 18 +- .../COM_MovieClipAttributeOperation.cc | 34 +-- .../COM_MovieClipAttributeOperation.h | 18 +- .../operations/COM_MovieClipOperation.cc | 44 ++-- .../operations/COM_MovieClipOperation.h | 22 +- .../COM_MovieDistortionOperation.cc | 95 ++++--- .../operations/COM_MovieDistortionOperation.h | 20 +- .../COM_MultilayerImageOperation.cc | 50 ++-- .../operations/COM_MultilayerImageOperation.h | 8 +- .../operations/COM_NormalizeOperation.cc | 30 +-- .../operations/COM_NormalizeOperation.h | 4 +- .../COM_OutputFileMultiViewOperation.cc | 124 ++++----- .../COM_OutputFileMultiViewOperation.h | 4 +- .../operations/COM_OutputFileOperation.cc | 130 +++++----- .../operations/COM_OutputFileOperation.h | 38 +-- .../operations/COM_PixelateOperation.cc | 8 +- .../operations/COM_PixelateOperation.h | 2 +- .../operations/COM_PlaneCornerPinOperation.cc | 12 +- .../operations/COM_PlaneCornerPinOperation.h | 4 +- .../COM_PlaneDistortCommonOperation.cc | 86 +++--- .../COM_PlaneDistortCommonOperation.h | 16 +- .../operations/COM_PlaneTrackOperation.cc | 36 +-- .../operations/COM_PlaneTrackOperation.h | 16 +- .../operations/COM_PosterizeOperation.cc | 16 +- .../operations/COM_PosterizeOperation.h | 4 +- .../operations/COM_PreviewOperation.cc | 92 +++---- .../operations/COM_PreviewOperation.h | 16 +- .../COM_ProjectorLensDistortionOperation.cc | 48 ++-- .../COM_ProjectorLensDistortionOperation.h | 8 +- .../operations/COM_QualityStepHelper.cc | 34 +-- .../operations/COM_QualityStepHelper.h | 12 +- .../operations/COM_ReadBufferOperation.cc | 48 ++-- .../operations/COM_ReadBufferOperation.h | 18 +- .../operations/COM_RenderLayersProg.cc | 60 ++--- .../operations/COM_RenderLayersProg.h | 30 +-- .../operations/COM_RotateOperation.cc | 70 ++--- .../operations/COM_RotateOperation.h | 18 +- .../operations/COM_SMAAOperation.cc | 116 ++++----- .../compositor/operations/COM_SMAAOperation.h | 16 +- .../operations/COM_ScaleOperation.cc | 172 ++++++------ .../operations/COM_ScaleOperation.h | 48 ++-- .../COM_ScreenLensDistortionOperation.cc | 142 +++++----- .../COM_ScreenLensDistortionOperation.h | 32 +-- .../COM_SetAlphaMultiplyOperation.cc | 16 +- .../COM_SetAlphaMultiplyOperation.h | 4 +- .../COM_SetAlphaReplaceOperation.cc | 16 +- .../operations/COM_SetAlphaReplaceOperation.h | 4 +- .../operations/COM_SetColorOperation.cc | 2 +- .../operations/COM_SetColorOperation.h | 22 +- .../operations/COM_SetSamplerOperation.cc | 6 +- .../operations/COM_SetSamplerOperation.h | 6 +- .../operations/COM_SetValueOperation.cc | 2 +- .../operations/COM_SetValueOperation.h | 8 +- .../operations/COM_SplitOperation.cc | 28 +- .../operations/COM_SplitOperation.h | 12 +- .../operations/COM_SunBeamsOperation.cc | 26 +- .../operations/COM_SunBeamsOperation.h | 8 +- .../operations/COM_TextureOperation.cc | 56 ++-- .../operations/COM_TextureOperation.h | 18 +- .../operations/COM_TonemapOperation.cc | 56 ++-- .../operations/COM_TonemapOperation.h | 8 +- .../operations/COM_TrackPositionOperation.cc | 78 +++--- .../operations/COM_TrackPositionOperation.h | 38 +-- .../operations/COM_TranslateOperation.cc | 30 +-- .../operations/COM_TranslateOperation.h | 36 +-- .../COM_VariableSizeBokehBlurOperation.cc | 116 ++++----- .../COM_VariableSizeBokehBlurOperation.h | 26 +- .../operations/COM_VectorBlurOperation.cc | 66 ++--- .../operations/COM_VectorBlurOperation.h | 12 +- .../operations/COM_VectorCurveOperation.cc | 12 +- .../operations/COM_VectorCurveOperation.h | 2 +- .../operations/COM_ViewerOperation.cc | 106 ++++---- .../operations/COM_ViewerOperation.h | 66 ++--- .../operations/COM_WrapOperation.cc | 10 +- .../compositor/operations/COM_WrapOperation.h | 2 +- .../operations/COM_WriteBufferOperation.cc | 52 ++-- .../operations/COM_WriteBufferOperation.h | 12 +- .../operations/COM_ZCombineOperation.cc | 74 +++--- .../operations/COM_ZCombineOperation.h | 14 +- 251 files changed, 4416 insertions(+), 4440 deletions(-) diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc index dbf813a61b4..89aea47f4a6 100644 --- a/source/blender/compositor/intern/COM_CPUDevice.cc +++ b/source/blender/compositor/intern/COM_CPUDevice.cc @@ -23,7 +23,7 @@ namespace blender::compositor { -CPUDevice::CPUDevice(int thread_id) : m_thread_id(thread_id) +CPUDevice::CPUDevice(int thread_id) : thread_id_(thread_id) { } diff --git a/source/blender/compositor/intern/COM_CPUDevice.h b/source/blender/compositor/intern/COM_CPUDevice.h index 99629890b30..b5d1fd1fff1 100644 --- a/source/blender/compositor/intern/COM_CPUDevice.h +++ b/source/blender/compositor/intern/COM_CPUDevice.h @@ -39,11 +39,11 @@ class CPUDevice : public Device { int thread_id() { - return m_thread_id; + return thread_id_; } protected: - int m_thread_id; + int thread_id_; }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index 81043f1f163..5e2e5ea295b 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -22,20 +22,20 @@ namespace blender::compositor { CompositorContext::CompositorContext() { - m_scene = nullptr; - m_rd = nullptr; - m_quality = eCompositorQuality::High; - m_hasActiveOpenCLDevices = false; - m_fastCalculation = false; - m_viewSettings = nullptr; - m_displaySettings = nullptr; - m_bnodetree = nullptr; + scene_ = nullptr; + rd_ = nullptr; + quality_ = eCompositorQuality::High; + hasActiveOpenCLDevices_ = false; + fastCalculation_ = false; + viewSettings_ = nullptr; + displaySettings_ = nullptr; + bnodetree_ = nullptr; } int CompositorContext::getFramenumber() const { - BLI_assert(m_rd); - return m_rd->cfra; + BLI_assert(rd_); + return rd_->cfra; } Size2f CompositorContext::get_render_size() const @@ -47,8 +47,8 @@ Size2f CompositorContext::get_render_size() const eExecutionModel CompositorContext::get_execution_model() const { if (U.experimental.use_full_frame_compositor) { - BLI_assert(m_bnodetree != nullptr); - switch (m_bnodetree->execution_mode) { + BLI_assert(bnodetree_ != nullptr); + switch (bnodetree_->execution_mode) { case 1: return eExecutionModel::FullFrame; case 0: diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index 835d565fc91..9354551d66a 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -38,55 +38,55 @@ class CompositorContext { * editor) This field is initialized in ExecutionSystem and must only be read from that point * on. \see ExecutionSystem */ - bool m_rendering; + bool rendering_; /** * \brief The quality of the composite. * This field is initialized in ExecutionSystem and must only be read from that point on. * \see ExecutionSystem */ - eCompositorQuality m_quality; + eCompositorQuality quality_; - Scene *m_scene; + Scene *scene_; /** * \brief Reference to the render data that is being composited. * This field is initialized in ExecutionSystem and must only be read from that point on. * \see ExecutionSystem */ - RenderData *m_rd; + RenderData *rd_; /** * \brief reference to the bNodeTree * This field is initialized in ExecutionSystem and must only be read from that point on. * \see ExecutionSystem */ - bNodeTree *m_bnodetree; + bNodeTree *bnodetree_; /** * \brief Preview image hash table * This field is initialized in ExecutionSystem and must only be read from that point on. */ - bNodeInstanceHash *m_previews; + bNodeInstanceHash *previews_; /** * \brief does this system have active opencl devices? */ - bool m_hasActiveOpenCLDevices; + bool hasActiveOpenCLDevices_; /** * \brief Skip slow nodes */ - bool m_fastCalculation; + bool fastCalculation_; /* \brief color management settings */ - const ColorManagedViewSettings *m_viewSettings; - const ColorManagedDisplaySettings *m_displaySettings; + const ColorManagedViewSettings *viewSettings_; + const ColorManagedDisplaySettings *displaySettings_; /** * \brief active rendering view name */ - const char *m_viewName; + const char *viewName_; public: /** @@ -99,7 +99,7 @@ class CompositorContext { */ void setRendering(bool rendering) { - m_rendering = rendering; + rendering_ = rendering; } /** @@ -107,7 +107,7 @@ class CompositorContext { */ bool isRendering() const { - return m_rendering; + return rendering_; } /** @@ -115,7 +115,7 @@ class CompositorContext { */ void setRenderData(RenderData *rd) { - m_rd = rd; + rd_ = rd; } /** @@ -123,7 +123,7 @@ class CompositorContext { */ void setbNodeTree(bNodeTree *bnodetree) { - m_bnodetree = bnodetree; + bnodetree_ = bnodetree; } /** @@ -131,7 +131,7 @@ class CompositorContext { */ const bNodeTree *getbNodeTree() const { - return m_bnodetree; + return bnodetree_; } /** @@ -139,16 +139,16 @@ class CompositorContext { */ const RenderData *getRenderData() const { - return m_rd; + return rd_; } void setScene(Scene *scene) { - m_scene = scene; + scene_ = scene; } Scene *getScene() const { - return m_scene; + return scene_; } /** @@ -156,7 +156,7 @@ class CompositorContext { */ void setPreviewHash(bNodeInstanceHash *previews) { - m_previews = previews; + previews_ = previews; } /** @@ -164,7 +164,7 @@ class CompositorContext { */ bNodeInstanceHash *getPreviewHash() const { - return m_previews; + return previews_; } /** @@ -172,7 +172,7 @@ class CompositorContext { */ void setViewSettings(const ColorManagedViewSettings *viewSettings) { - m_viewSettings = viewSettings; + viewSettings_ = viewSettings; } /** @@ -180,7 +180,7 @@ class CompositorContext { */ const ColorManagedViewSettings *getViewSettings() const { - return m_viewSettings; + return viewSettings_; } /** @@ -188,7 +188,7 @@ class CompositorContext { */ void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) { - m_displaySettings = displaySettings; + displaySettings_ = displaySettings; } /** @@ -196,7 +196,7 @@ class CompositorContext { */ const ColorManagedDisplaySettings *getDisplaySettings() const { - return m_displaySettings; + return displaySettings_; } /** @@ -204,7 +204,7 @@ class CompositorContext { */ void setQuality(eCompositorQuality quality) { - m_quality = quality; + quality_ = quality; } /** @@ -212,7 +212,7 @@ class CompositorContext { */ eCompositorQuality getQuality() const { - return m_quality; + return quality_; } /** @@ -225,7 +225,7 @@ class CompositorContext { */ bool getHasActiveOpenCLDevices() const { - return m_hasActiveOpenCLDevices; + return hasActiveOpenCLDevices_; } /** @@ -233,13 +233,13 @@ class CompositorContext { */ void setHasActiveOpenCLDevices(bool hasAvtiveOpenCLDevices) { - m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices; + hasActiveOpenCLDevices_ = hasAvtiveOpenCLDevices; } /** Whether it has a view with a specific name and not the default one. */ bool has_explicit_view() const { - return m_viewName && m_viewName[0] != '\0'; + return viewName_ && viewName_[0] != '\0'; } /** @@ -247,7 +247,7 @@ class CompositorContext { */ const char *getViewName() const { - return m_viewName; + return viewName_; } /** @@ -255,7 +255,7 @@ class CompositorContext { */ void setViewName(const char *viewName) { - m_viewName = viewName; + viewName_ = viewName; } int getChunksize() const @@ -265,11 +265,11 @@ class CompositorContext { void setFastCalculation(bool fastCalculation) { - m_fastCalculation = fastCalculation; + fastCalculation_ = fastCalculation; } bool isFastCalculation() const { - return m_fastCalculation; + return fastCalculation_; } bool isGroupnodeBufferEnabled() const { @@ -282,7 +282,7 @@ class CompositorContext { */ float getRenderPercentageAsFactor() const { - return m_rd->size * 0.01f; + return rd_->size * 0.01f; } Size2f get_render_size() const; diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index d0c8311ef6e..023db368ac9 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -35,12 +35,12 @@ extern "C" { namespace blender::compositor { -int DebugInfo::m_file_index = 0; -DebugInfo::NodeNameMap DebugInfo::m_node_names; -DebugInfo::OpNameMap DebugInfo::m_op_names; -std::string DebugInfo::m_current_node_name; -std::string DebugInfo::m_current_op_name; -DebugInfo::GroupStateMap DebugInfo::m_group_states; +int DebugInfo::file_index_ = 0; +DebugInfo::NodeNameMap DebugInfo::node_names_; +DebugInfo::OpNameMap DebugInfo::op_names_; +std::string DebugInfo::current_node_name_; +std::string DebugInfo::current_op_name_; +DebugInfo::GroupStateMap DebugInfo::group_states_; static std::string operation_class_name(const NodeOperation *op) { @@ -53,8 +53,8 @@ static std::string operation_class_name(const NodeOperation *op) std::string DebugInfo::node_name(const Node *node) { - NodeNameMap::const_iterator it = m_node_names.find(node); - if (it != m_node_names.end()) { + NodeNameMap::const_iterator it = node_names_.find(node); + if (it != node_names_.end()) { return it->second; } return ""; @@ -62,8 +62,8 @@ std::string DebugInfo::node_name(const Node *node) std::string DebugInfo::operation_name(const NodeOperation *op) { - OpNameMap::const_iterator it = m_op_names.find(op); - if (it != m_op_names.end()) { + OpNameMap::const_iterator it = op_names_.find(op); + if (it != op_names_.end()) { return it->second; } return ""; @@ -300,25 +300,25 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma std::map> op_groups; int index = 0; - for (const ExecutionGroup *group : system->m_groups) { + for (const ExecutionGroup *group : system->groups_) { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "// GROUP: %d\r\n", index); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "subgraph cluster_%d{\r\n", index); /* used as a check for executing group */ - if (m_group_states[group] == EG_WAIT) { + if (group_states_[group] == EG_WAIT) { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "style=dashed\r\n"); } - else if (m_group_states[group] == EG_RUNNING) { + else if (group_states_[group] == EG_RUNNING) { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "style=filled\r\n"); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "color=black\r\n"); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "fillcolor=firebrick1\r\n"); } - else if (m_group_states[group] == EG_FINISHED) { + else if (group_states_[group] == EG_FINISHED) { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "style=filled\r\n"); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "color=black\r\n"); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "fillcolor=chartreuse4\r\n"); } - for (NodeOperation *operation : group->m_operations) { + for (NodeOperation *operation : group->operations_) { sprintf(strbuf, "_%p", group); op_groups[operation].push_back(std::string(strbuf)); @@ -332,7 +332,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma } /* operations not included in any group */ - for (NodeOperation *operation : system->m_operations) { + for (NodeOperation *operation : system->operations_) { if (op_groups.find(operation) != op_groups.end()) { continue; } @@ -343,7 +343,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma system, operation, nullptr, str + len, maxlen > len ? maxlen - len : 0); } - for (NodeOperation *operation : system->m_operations) { + for (NodeOperation *operation : system->operations_) { if (operation->get_flags().is_read_buffer_operation) { ReadBufferOperation *read = (ReadBufferOperation *)operation; WriteBufferOperation *write = read->getMemoryProxy()->getWriteBufferOperation(); @@ -364,8 +364,8 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma } } - for (NodeOperation *op : system->m_operations) { - for (NodeOperationInput &to : op->m_inputs) { + for (NodeOperation *op : system->operations_) { + for (NodeOperationInput &to : op->inputs_) { NodeOperationOutput *from = to.getLink(); if (!from) { @@ -418,7 +418,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma const bool has_execution_groups = system->getContext().get_execution_model() == eExecutionModel::Tiled && - system->m_groups.size() > 0; + system->groups_.size() > 0; len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n"); @@ -437,13 +437,13 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name) char filename[FILE_MAX]; if (name.is_empty()) { - BLI_snprintf(basename, sizeof(basename), "compositor_%d.dot", m_file_index); + BLI_snprintf(basename, sizeof(basename), "compositor_%d.dot", file_index_); } else { BLI_strncpy(basename, (name + ".dot").c_str(), sizeof(basename)); } BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), basename); - m_file_index++; + file_index_++; std::cout << "Writing compositor debug to: " << filename << "\n"; diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h index 689cd4029f1..f1edd3ea15f 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -52,33 +52,33 @@ class DebugInfo { static std::string operation_name(const NodeOperation *op); private: - static int m_file_index; + static int file_index_; /** Map nodes to usable names for debug output. */ - static NodeNameMap m_node_names; + static NodeNameMap node_names_; /** Map operations to usable names for debug output. */ - static OpNameMap m_op_names; + static OpNameMap op_names_; /** Base name for all operations added by a node. */ - static std::string m_current_node_name; + static std::string current_node_name_; /** Base name for automatic sub-operations. */ - static std::string m_current_op_name; + static std::string current_op_name_; /** For visualizing group states. */ - static GroupStateMap m_group_states; + static GroupStateMap group_states_; public: static void convert_started() { if (COM_EXPORT_GRAPHVIZ) { - m_op_names.clear(); + op_names_.clear(); } } static void execute_started(const ExecutionSystem *system) { if (COM_EXPORT_GRAPHVIZ) { - m_file_index = 1; - m_group_states.clear(); - for (ExecutionGroup *execution_group : system->m_groups) { - m_group_states[execution_group] = EG_WAIT; + file_index_ = 1; + group_states_.clear(); + for (ExecutionGroup *execution_group : system->groups_) { + group_states_[execution_group] = EG_WAIT; } } if (COM_EXPORT_OPERATION_BUFFERS) { @@ -89,41 +89,41 @@ class DebugInfo { static void node_added(const Node *node) { if (COM_EXPORT_GRAPHVIZ) { - m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : ""); + node_names_[node] = std::string(node->getbNode() ? node->getbNode()->name : ""); } } static void node_to_operations(const Node *node) { if (COM_EXPORT_GRAPHVIZ) { - m_current_node_name = m_node_names[node]; + current_node_name_ = node_names_[node]; } } static void operation_added(const NodeOperation *operation) { if (COM_EXPORT_GRAPHVIZ) { - m_op_names[operation] = m_current_node_name; + op_names_[operation] = current_node_name_; } }; static void operation_read_write_buffer(const NodeOperation *operation) { if (COM_EXPORT_GRAPHVIZ) { - m_current_op_name = m_op_names[operation]; + current_op_name_ = op_names_[operation]; } }; static void execution_group_started(const ExecutionGroup *group) { if (COM_EXPORT_GRAPHVIZ) { - m_group_states[group] = EG_RUNNING; + group_states_[group] = EG_RUNNING; } }; static void execution_group_finished(const ExecutionGroup *group) { if (COM_EXPORT_GRAPHVIZ) { - m_group_states[group] = EG_FINISHED; + group_states_[group] = EG_FINISHED; } }; diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc index 8841f44ea48..a38a1cd0a6c 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.cc +++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc @@ -55,17 +55,17 @@ std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags) ExecutionGroup::ExecutionGroup(int id) { - m_id = id; - m_bTree = nullptr; - m_height = 0; - m_width = 0; - m_max_read_buffer_offset = 0; - m_x_chunks_len = 0; - m_y_chunks_len = 0; - m_chunks_len = 0; - m_chunks_finished = 0; - BLI_rcti_init(&m_viewerBorder, 0, 0, 0, 0); - m_executionStartTime = 0; + id_ = id; + bTree_ = nullptr; + height_ = 0; + width_ = 0; + max_read_buffer_offset_ = 0; + x_chunks_len_ = 0; + y_chunks_len_ = 0; + chunks_len_ = 0; + chunks_finished_ = 0; + BLI_rcti_init(&viewerBorder_, 0, 0, 0, 0); + executionStartTime_ = 0; } std::ostream &operator<<(std::ostream &os, const ExecutionGroup &execution_group) @@ -84,7 +84,7 @@ eCompositorPriority ExecutionGroup::getRenderPriority() bool ExecutionGroup::can_contain(NodeOperation &operation) { - if (!m_flags.initialized) { + if (!flags_.initialized) { return true; } @@ -99,7 +99,7 @@ bool ExecutionGroup::can_contain(NodeOperation &operation) } /* complex groups don't allow further ops (except read buffer and values, see above) */ - if (m_flags.complex) { + if (flags_.complex) { return false; } /* complex ops can't be added to other groups (except their own, which they initialize, see @@ -119,13 +119,13 @@ bool ExecutionGroup::addOperation(NodeOperation *operation) if (!operation->get_flags().is_read_buffer_operation && !operation->get_flags().is_write_buffer_operation) { - m_flags.complex = operation->get_flags().complex; - m_flags.open_cl = operation->get_flags().open_cl; - m_flags.single_threaded = operation->get_flags().single_threaded; - m_flags.initialized = true; + flags_.complex = operation->get_flags().complex; + flags_.open_cl = operation->get_flags().open_cl; + flags_.single_threaded = operation->get_flags().single_threaded; + flags_.initialized = true; } - m_operations.append(operation); + operations_.append(operation); return true; } @@ -133,20 +133,20 @@ bool ExecutionGroup::addOperation(NodeOperation *operation) NodeOperation *ExecutionGroup::getOutputOperation() const { return this - ->m_operations[0]; /* the first operation of the group is always the output operation. */ + ->operations_[0]; /* the first operation of the group is always the output operation. */ } void ExecutionGroup::init_work_packages() { - m_work_packages.clear(); - if (m_chunks_len != 0) { - m_work_packages.resize(m_chunks_len); - for (unsigned int index = 0; index < m_chunks_len; index++) { - m_work_packages[index].type = eWorkPackageType::Tile; - m_work_packages[index].state = eWorkPackageState::NotScheduled; - m_work_packages[index].execution_group = this; - m_work_packages[index].chunk_number = index; - determineChunkRect(&m_work_packages[index].rect, index); + work_packages_.clear(); + if (chunks_len_ != 0) { + work_packages_.resize(chunks_len_); + for (unsigned int index = 0; index < chunks_len_; index++) { + work_packages_[index].type = eWorkPackageType::Tile; + work_packages_[index].state = eWorkPackageState::NotScheduled; + work_packages_[index].execution_group = this; + work_packages_[index].chunk_number = index; + determineChunkRect(&work_packages_[index].rect, index); } } } @@ -154,15 +154,15 @@ void ExecutionGroup::init_work_packages() void ExecutionGroup::init_read_buffer_operations() { unsigned int max_offset = 0; - for (NodeOperation *operation : m_operations) { + for (NodeOperation *operation : operations_) { if (operation->get_flags().is_read_buffer_operation) { ReadBufferOperation *readOperation = static_cast(operation); - m_read_operations.append(readOperation); + read_operations_.append(readOperation); max_offset = MAX2(max_offset, readOperation->getOffset()); } } max_offset++; - m_max_read_buffer_offset = max_offset; + max_read_buffer_offset_ = max_offset; } void ExecutionGroup::initExecution() @@ -174,12 +174,12 @@ void ExecutionGroup::initExecution() void ExecutionGroup::deinitExecution() { - m_work_packages.clear(); - m_chunks_len = 0; - m_x_chunks_len = 0; - m_y_chunks_len = 0; - m_read_operations.clear(); - m_bTree = nullptr; + work_packages_.clear(); + chunks_len_ = 0; + x_chunks_len_ = 0; + y_chunks_len_ = 0; + read_operations_.clear(); + bTree_ = nullptr; } void ExecutionGroup::determineResolution(unsigned int resolution[2]) @@ -188,30 +188,30 @@ void ExecutionGroup::determineResolution(unsigned int resolution[2]) resolution[0] = operation->getWidth(); resolution[1] = operation->getHeight(); this->setResolution(resolution); - BLI_rcti_init(&m_viewerBorder, 0, m_width, 0, m_height); + BLI_rcti_init(&viewerBorder_, 0, width_, 0, height_); } void ExecutionGroup::init_number_of_chunks() { - if (m_flags.single_threaded) { - m_x_chunks_len = 1; - m_y_chunks_len = 1; - m_chunks_len = 1; + if (flags_.single_threaded) { + x_chunks_len_ = 1; + y_chunks_len_ = 1; + chunks_len_ = 1; } else { - const float chunkSizef = m_chunkSize; - const int border_width = BLI_rcti_size_x(&m_viewerBorder); - const int border_height = BLI_rcti_size_y(&m_viewerBorder); - m_x_chunks_len = ceil(border_width / chunkSizef); - m_y_chunks_len = ceil(border_height / chunkSizef); - m_chunks_len = m_x_chunks_len * m_y_chunks_len; + const float chunkSizef = chunkSize_; + const int border_width = BLI_rcti_size_x(&viewerBorder_); + const int border_height = BLI_rcti_size_y(&viewerBorder_); + x_chunks_len_ = ceil(border_width / chunkSizef); + y_chunks_len_ = ceil(border_height / chunkSizef); + chunks_len_ = x_chunks_len_ * y_chunks_len_; } } blender::Array ExecutionGroup::get_execution_order() const { - blender::Array chunk_order(m_chunks_len); - for (int chunk_index = 0; chunk_index < m_chunks_len; chunk_index++) { + blender::Array chunk_order(chunks_len_); + for (int chunk_index = 0; chunk_index < chunks_len_; chunk_index++) { chunk_order[chunk_index] = chunk_index; } @@ -227,8 +227,8 @@ blender::Array ExecutionGroup::get_execution_order() const order_type = viewer->getChunkOrder(); } - const int border_width = BLI_rcti_size_x(&m_viewerBorder); - const int border_height = BLI_rcti_size_y(&m_viewerBorder); + const int border_width = BLI_rcti_size_x(&viewerBorder_); + const int border_height = BLI_rcti_size_y(&viewerBorder_); int index; switch (order_type) { case ChunkOrdering::Random: { @@ -241,17 +241,17 @@ blender::Array ExecutionGroup::get_execution_order() const } case ChunkOrdering::CenterOut: { ChunkOrderHotspot hotspot(border_width * centerX, border_height * centerY, 0.0f); - blender::Array chunk_orders(m_chunks_len); - for (index = 0; index < m_chunks_len; index++) { - const WorkPackage &work_package = m_work_packages[index]; + blender::Array chunk_orders(chunks_len_); + for (index = 0; index < chunks_len_; index++) { + const WorkPackage &work_package = work_packages_[index]; chunk_orders[index].index = index; - chunk_orders[index].x = work_package.rect.xmin - m_viewerBorder.xmin; - chunk_orders[index].y = work_package.rect.ymin - m_viewerBorder.ymin; + chunk_orders[index].x = work_package.rect.xmin - viewerBorder_.xmin; + chunk_orders[index].y = work_package.rect.ymin - viewerBorder_.ymin; chunk_orders[index].update_distance(&hotspot, 1); } - std::sort(&chunk_orders[0], &chunk_orders[m_chunks_len - 1]); - for (index = 0; index < m_chunks_len; index++) { + std::sort(&chunk_orders[0], &chunk_orders[chunks_len_ - 1]); + for (index = 0; index < chunks_len_; index++) { chunk_order[index] = chunk_orders[index].index; } @@ -264,7 +264,7 @@ blender::Array ExecutionGroup::get_execution_order() const unsigned int my = border_height / 2; unsigned int bx = mx + 2 * tx; unsigned int by = my + 2 * ty; - float addition = m_chunks_len / COM_RULE_OF_THIRDS_DIVIDER; + float addition = chunks_len_ / COM_RULE_OF_THIRDS_DIVIDER; ChunkOrderHotspot hotspots[9]{ ChunkOrderHotspot(mx, my, addition * 0), @@ -278,18 +278,18 @@ blender::Array ExecutionGroup::get_execution_order() const ChunkOrderHotspot(mx, by, addition * 8), }; - blender::Array chunk_orders(m_chunks_len); - for (index = 0; index < m_chunks_len; index++) { - const WorkPackage &work_package = m_work_packages[index]; + blender::Array chunk_orders(chunks_len_); + for (index = 0; index < chunks_len_; index++) { + const WorkPackage &work_package = work_packages_[index]; chunk_orders[index].index = index; - chunk_orders[index].x = work_package.rect.xmin - m_viewerBorder.xmin; - chunk_orders[index].y = work_package.rect.ymin - m_viewerBorder.ymin; + chunk_orders[index].x = work_package.rect.xmin - viewerBorder_.xmin; + chunk_orders[index].y = work_package.rect.ymin - viewerBorder_.ymin; chunk_orders[index].update_distance(hotspots, 9); } - std::sort(&chunk_orders[0], &chunk_orders[m_chunks_len]); + std::sort(&chunk_orders[0], &chunk_orders[chunks_len_]); - for (index = 0; index < m_chunks_len; index++) { + for (index = 0; index < chunks_len_; index++) { chunk_order[index] = chunk_orders[index].index; } @@ -310,21 +310,21 @@ void ExecutionGroup::execute(ExecutionSystem *graph) { const CompositorContext &context = graph->getContext(); const bNodeTree *bTree = context.getbNodeTree(); - if (m_width == 0 || m_height == 0) { + if (width_ == 0 || height_ == 0) { return; } /** \note Break out... no pixels to calculate. */ if (bTree->test_break && bTree->test_break(bTree->tbh)) { return; } /** \note Early break out for blur and preview nodes. */ - if (m_chunks_len == 0) { + if (chunks_len_ == 0) { return; } /** \note Early break out. */ unsigned int chunk_index; - m_executionStartTime = PIL_check_seconds_timer(); + executionStartTime_ = PIL_check_seconds_timer(); - m_chunks_finished = 0; - m_bTree = bTree; + chunks_finished_ = 0; + bTree_ = bTree; blender::Array chunk_order = get_execution_order(); @@ -341,12 +341,12 @@ void ExecutionGroup::execute(ExecutionSystem *graph) finished = true; int numberEvaluated = 0; - for (int index = startIndex; index < m_chunks_len && numberEvaluated < maxNumberEvaluated; + for (int index = startIndex; index < chunks_len_ && numberEvaluated < maxNumberEvaluated; index++) { chunk_index = chunk_order[index]; - int yChunk = chunk_index / m_x_chunks_len; - int xChunk = chunk_index - (yChunk * m_x_chunks_len); - const WorkPackage &work_package = m_work_packages[chunk_index]; + int yChunk = chunk_index / x_chunks_len_; + int xChunk = chunk_index - (yChunk * x_chunks_len_); + const WorkPackage &work_package = work_packages_[chunk_index]; switch (work_package.state) { case eWorkPackageState::NotScheduled: { scheduleChunkWhenPossible(graph, xChunk, yChunk); @@ -385,12 +385,12 @@ void ExecutionGroup::execute(ExecutionSystem *graph) MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber) { - WorkPackage &work_package = m_work_packages[chunkNumber]; + WorkPackage &work_package = work_packages_[chunkNumber]; MemoryBuffer **memoryBuffers = (MemoryBuffer **)MEM_callocN( - sizeof(MemoryBuffer *) * m_max_read_buffer_offset, __func__); + sizeof(MemoryBuffer *) * max_read_buffer_offset_, __func__); rcti output; - for (ReadBufferOperation *readOperation : m_read_operations) { + for (ReadBufferOperation *readOperation : read_operations_) { MemoryProxy *memoryProxy = readOperation->getMemoryProxy(); this->determineDependingAreaOfInterest(&work_package.rect, readOperation, &output); MemoryBuffer *memoryBuffer = memoryProxy->getExecutor()->constructConsolidatedMemoryBuffer( @@ -411,14 +411,14 @@ MemoryBuffer *ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy &mem void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers) { - WorkPackage &work_package = m_work_packages[chunkNumber]; + WorkPackage &work_package = work_packages_[chunkNumber]; if (work_package.state == eWorkPackageState::Scheduled) { work_package.state = eWorkPackageState::Executed; } - atomic_add_and_fetch_u(&m_chunks_finished, 1); + atomic_add_and_fetch_u(&chunks_finished_, 1); if (memoryBuffers) { - for (unsigned int index = 0; index < m_max_read_buffer_offset; index++) { + for (unsigned int index = 0; index < max_read_buffer_offset_; index++) { MemoryBuffer *buffer = memoryBuffers[index]; if (buffer) { if (buffer->isTemporarily()) { @@ -429,16 +429,16 @@ void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memo } MEM_freeN(memoryBuffers); } - if (m_bTree) { + if (bTree_) { /* Status report is only performed for top level Execution Groups. */ - float progress = m_chunks_finished; - progress /= m_chunks_len; - m_bTree->progress(m_bTree->prh, progress); + float progress = chunks_finished_; + progress /= chunks_len_; + bTree_->progress(bTree_->prh, progress); char buf[128]; BLI_snprintf( - buf, sizeof(buf), TIP_("Compositing | Tile %u-%u"), m_chunks_finished, m_chunks_len); - m_bTree->stats_draw(m_bTree->sdh, buf); + buf, sizeof(buf), TIP_("Compositing | Tile %u-%u"), chunks_finished_, chunks_len_); + bTree_->stats_draw(bTree_->sdh, buf); } } @@ -446,29 +446,29 @@ inline void ExecutionGroup::determineChunkRect(rcti *r_rect, const unsigned int xChunk, const unsigned int yChunk) const { - const int border_width = BLI_rcti_size_x(&m_viewerBorder); - const int border_height = BLI_rcti_size_y(&m_viewerBorder); + const int border_width = BLI_rcti_size_x(&viewerBorder_); + const int border_height = BLI_rcti_size_y(&viewerBorder_); - if (m_flags.single_threaded) { - BLI_rcti_init(r_rect, m_viewerBorder.xmin, border_width, m_viewerBorder.ymin, border_height); + if (flags_.single_threaded) { + BLI_rcti_init(r_rect, viewerBorder_.xmin, border_width, viewerBorder_.ymin, border_height); } else { - const unsigned int minx = xChunk * m_chunkSize + m_viewerBorder.xmin; - const unsigned int miny = yChunk * m_chunkSize + m_viewerBorder.ymin; - const unsigned int width = MIN2((unsigned int)m_viewerBorder.xmax, m_width); - const unsigned int height = MIN2((unsigned int)m_viewerBorder.ymax, m_height); + const unsigned int minx = xChunk * chunkSize_ + viewerBorder_.xmin; + const unsigned int miny = yChunk * chunkSize_ + viewerBorder_.ymin; + const unsigned int width = MIN2((unsigned int)viewerBorder_.xmax, width_); + const unsigned int height = MIN2((unsigned int)viewerBorder_.ymax, height_); BLI_rcti_init(r_rect, - MIN2(minx, m_width), - MIN2(minx + m_chunkSize, width), - MIN2(miny, m_height), - MIN2(miny + m_chunkSize, height)); + MIN2(minx, width_), + MIN2(minx + chunkSize_, width), + MIN2(miny, height_), + MIN2(miny + chunkSize_, height)); } } void ExecutionGroup::determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const { - const unsigned int yChunk = chunkNumber / m_x_chunks_len; - const unsigned int xChunk = chunkNumber - (yChunk * m_x_chunks_len); + const unsigned int yChunk = chunkNumber / x_chunks_len_; + const unsigned int xChunk = chunkNumber - (yChunk * x_chunks_len_); determineChunkRect(r_rect, xChunk, yChunk); } @@ -487,7 +487,7 @@ MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect) bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area) { - if (m_flags.single_threaded) { + if (flags_.single_threaded) { return scheduleChunkWhenPossible(graph, 0, 0); } /* Find all chunks inside the rect @@ -495,18 +495,18 @@ bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area * where x and y are chunk-numbers. */ int indexx, indexy; - int minx = max_ii(area->xmin - m_viewerBorder.xmin, 0); - int maxx = min_ii(area->xmax - m_viewerBorder.xmin, m_viewerBorder.xmax - m_viewerBorder.xmin); - int miny = max_ii(area->ymin - m_viewerBorder.ymin, 0); - int maxy = min_ii(area->ymax - m_viewerBorder.ymin, m_viewerBorder.ymax - m_viewerBorder.ymin); - int minxchunk = minx / (int)m_chunkSize; - int maxxchunk = (maxx + (int)m_chunkSize - 1) / (int)m_chunkSize; - int minychunk = miny / (int)m_chunkSize; - int maxychunk = (maxy + (int)m_chunkSize - 1) / (int)m_chunkSize; + int minx = max_ii(area->xmin - viewerBorder_.xmin, 0); + int maxx = min_ii(area->xmax - viewerBorder_.xmin, viewerBorder_.xmax - viewerBorder_.xmin); + int miny = max_ii(area->ymin - viewerBorder_.ymin, 0); + int maxy = min_ii(area->ymax - viewerBorder_.ymin, viewerBorder_.ymax - viewerBorder_.ymin); + int minxchunk = minx / (int)chunkSize_; + int maxxchunk = (maxx + (int)chunkSize_ - 1) / (int)chunkSize_; + int minychunk = miny / (int)chunkSize_; + int maxychunk = (maxy + (int)chunkSize_ - 1) / (int)chunkSize_; minxchunk = max_ii(minxchunk, 0); minychunk = max_ii(minychunk, 0); - maxxchunk = min_ii(maxxchunk, (int)m_x_chunks_len); - maxychunk = min_ii(maxychunk, (int)m_y_chunks_len); + maxxchunk = min_ii(maxxchunk, (int)x_chunks_len_); + maxychunk = min_ii(maxychunk, (int)y_chunks_len_); bool result = true; for (indexx = minxchunk; indexx < maxxchunk; indexx++) { @@ -522,7 +522,7 @@ bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber) { - WorkPackage &work_package = m_work_packages[chunkNumber]; + WorkPackage &work_package = work_packages_[chunkNumber]; if (work_package.state == eWorkPackageState::NotScheduled) { work_package.state = eWorkPackageState::Scheduled; WorkScheduler::schedule(&work_package); @@ -535,16 +535,16 @@ bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph, const int chunk_x, const int chunk_y) { - if (chunk_x < 0 || chunk_x >= (int)m_x_chunks_len) { + if (chunk_x < 0 || chunk_x >= (int)x_chunks_len_) { return true; } - if (chunk_y < 0 || chunk_y >= (int)m_y_chunks_len) { + if (chunk_y < 0 || chunk_y >= (int)y_chunks_len_) { return true; } /* Check if chunk is already executed or scheduled and not yet executed. */ - const int chunk_index = chunk_y * m_x_chunks_len + chunk_x; - WorkPackage &work_package = m_work_packages[chunk_index]; + const int chunk_index = chunk_y * x_chunks_len_ + chunk_x; + WorkPackage &work_package = work_packages_[chunk_index]; if (work_package.state == eWorkPackageState::Executed) { return true; } @@ -555,7 +555,7 @@ bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph, bool can_be_executed = true; rcti area; - for (ReadBufferOperation *read_operation : m_read_operations) { + for (ReadBufferOperation *read_operation : read_operations_) { BLI_rcti_init(&area, 0, 0, 0, 0); MemoryProxy *memory_proxy = read_operation->getMemoryProxy(); determineDependingAreaOfInterest(&work_package.rect, read_operation, &area); @@ -584,8 +584,7 @@ void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float y { const NodeOperation &operation = *this->getOutputOperation(); if (operation.get_flags().use_viewer_border) { - BLI_rcti_init( - &m_viewerBorder, xmin * m_width, xmax * m_width, ymin * m_height, ymax * m_height); + BLI_rcti_init(&viewerBorder_, xmin * width_, xmax * width_, ymin * height_, ymax * height_); } } @@ -593,8 +592,7 @@ void ExecutionGroup::setRenderBorder(float xmin, float xmax, float ymin, float y { const NodeOperation &operation = *this->getOutputOperation(); if (operation.isOutputOperation(true) && operation.get_flags().use_render_border) { - BLI_rcti_init( - &m_viewerBorder, xmin * m_width, xmax * m_width, ymin * m_height, ymax * m_height); + BLI_rcti_init(&viewerBorder_, xmin * width_, xmax * width_, ymin * height_, ymax * height_); } } diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.h b/source/blender/compositor/intern/COM_ExecutionGroup.h index 2799bef80d4..c0737b2bc02 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.h +++ b/source/blender/compositor/intern/COM_ExecutionGroup.h @@ -86,84 +86,84 @@ class ExecutionGroup { /** * Id of the execution group. For debugging purposes. */ - int m_id; + int id_; /** * \brief list of operations in this ExecutionGroup */ - Vector m_operations; + Vector operations_; - ExecutionGroupFlags m_flags; + ExecutionGroupFlags flags_; /** * \brief Width of the output */ - unsigned int m_width; + unsigned int width_; /** * \brief Height of the output */ - unsigned int m_height; + unsigned int height_; /** * \brief size of a single chunk, being Width or of height * a chunk is always a square, except at the edges of the MemoryBuffer */ - unsigned int m_chunkSize; + unsigned int chunkSize_; /** * \brief number of chunks in the x-axis */ - unsigned int m_x_chunks_len; + unsigned int x_chunks_len_; /** * \brief number of chunks in the y-axis */ - unsigned int m_y_chunks_len; + unsigned int y_chunks_len_; /** * \brief total number of chunks */ - unsigned int m_chunks_len; + unsigned int chunks_len_; /** * \brief what is the maximum number field of all ReadBufferOperation in this ExecutionGroup. * \note this is used to construct the MemoryBuffers that will be passed during execution. */ - unsigned int m_max_read_buffer_offset; + unsigned int max_read_buffer_offset_; /** * \brief All read operations of this execution group. */ - Vector m_read_operations; + Vector read_operations_; /** * \brief reference to the original bNodeTree, * this field is only set for the 'top' execution group. * \note can only be used to call the callbacks for progress, status and break. */ - const bNodeTree *m_bTree; + const bNodeTree *bTree_; /** * \brief total number of chunks that have been calculated for this ExecutionGroup */ - unsigned int m_chunks_finished; + unsigned int chunks_finished_; /** - * \brief m_work_packages holds all unit of work. + * \brief work_packages_ holds all unit of work. */ - Vector m_work_packages; + Vector work_packages_; /** * \brief denotes boundary for border compositing * \note measured in pixel space */ - rcti m_viewerBorder; + rcti viewerBorder_; /** * \brief start time of execution */ - double m_executionStartTime; + double executionStartTime_; // methods /** @@ -241,12 +241,12 @@ class ExecutionGroup { int get_id() const { - return m_id; + return id_; } const ExecutionGroupFlags get_flags() const { - return m_flags; + return flags_; } // methods @@ -266,7 +266,7 @@ class ExecutionGroup { */ void setOutputExecutionGroup(bool is_output) { - m_flags.is_output = is_output; + flags_.is_output = is_output; } /** @@ -281,8 +281,8 @@ class ExecutionGroup { */ void setResolution(unsigned int resolution[2]) { - m_width = resolution[0]; - m_height = resolution[1]; + width_ = resolution[0]; + height_ = resolution[1]; } /** @@ -290,7 +290,7 @@ class ExecutionGroup { */ unsigned int getWidth() const { - return m_width; + return width_; } /** @@ -298,7 +298,7 @@ class ExecutionGroup { */ unsigned int getHeight() const { - return m_height; + return height_; } /** @@ -381,7 +381,7 @@ class ExecutionGroup { void setChunksize(int chunksize) { - m_chunkSize = chunksize; + chunkSize_ = chunksize; } /** diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index 510331f3294..880383853d6 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -43,40 +43,40 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, const char *viewName) { num_work_threads_ = WorkScheduler::get_num_cpu_threads(); - m_context.setViewName(viewName); - m_context.setScene(scene); - m_context.setbNodeTree(editingtree); - m_context.setPreviewHash(editingtree->previews); - m_context.setFastCalculation(fastcalculation); + context_.setViewName(viewName); + context_.setScene(scene); + context_.setbNodeTree(editingtree); + context_.setPreviewHash(editingtree->previews); + context_.setFastCalculation(fastcalculation); /* initialize the CompositorContext */ if (rendering) { - m_context.setQuality((eCompositorQuality)editingtree->render_quality); + context_.setQuality((eCompositorQuality)editingtree->render_quality); } else { - m_context.setQuality((eCompositorQuality)editingtree->edit_quality); + context_.setQuality((eCompositorQuality)editingtree->edit_quality); } - m_context.setRendering(rendering); - m_context.setHasActiveOpenCLDevices(WorkScheduler::has_gpu_devices() && - (editingtree->flag & NTREE_COM_OPENCL)); + context_.setRendering(rendering); + context_.setHasActiveOpenCLDevices(WorkScheduler::has_gpu_devices() && + (editingtree->flag & NTREE_COM_OPENCL)); - m_context.setRenderData(rd); - m_context.setViewSettings(viewSettings); - m_context.setDisplaySettings(displaySettings); + context_.setRenderData(rd); + context_.setViewSettings(viewSettings); + context_.setDisplaySettings(displaySettings); BLI_mutex_init(&work_mutex_); BLI_condition_init(&work_finished_cond_); { - NodeOperationBuilder builder(&m_context, editingtree, this); + NodeOperationBuilder builder(&context_, editingtree, this); builder.convertToOperations(this); } - switch (m_context.get_execution_model()) { + switch (context_.get_execution_model()) { case eExecutionModel::Tiled: - execution_model_ = new TiledExecutionModel(m_context, m_operations, m_groups); + execution_model_ = new TiledExecutionModel(context_, operations_, groups_); break; case eExecutionModel::FullFrame: - execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations); + execution_model_ = new FullFrameExecutionModel(context_, active_buffers_, operations_); break; default: BLI_assert_msg(0, "Non implemented execution model"); @@ -91,28 +91,28 @@ ExecutionSystem::~ExecutionSystem() delete execution_model_; - for (NodeOperation *operation : m_operations) { + for (NodeOperation *operation : operations_) { delete operation; } - m_operations.clear(); + operations_.clear(); - for (ExecutionGroup *group : m_groups) { + for (ExecutionGroup *group : groups_) { delete group; } - m_groups.clear(); + groups_.clear(); } void ExecutionSystem::set_operations(const Vector &operations, const Vector &groups) { - m_operations = operations; - m_groups = groups; + operations_ = operations; + groups_ = groups; } void ExecutionSystem::execute() { DebugInfo::execute_started(this); - for (NodeOperation *op : m_operations) { + for (NodeOperation *op : operations_) { op->init_data(); } execution_model_->execute(*this); @@ -184,7 +184,7 @@ void ExecutionSystem::execute_work(const rcti &work_rect, bool ExecutionSystem::is_breaked() const { - const bNodeTree *btree = m_context.getbNodeTree(); + const bNodeTree *btree = context_.getbNodeTree(); return btree->test_break(btree->tbh); } diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index 303111b8b42..4a6a0f1bad8 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -137,17 +137,17 @@ class ExecutionSystem { /** * \brief the context used during execution */ - CompositorContext m_context; + CompositorContext context_; /** * \brief vector of operations */ - Vector m_operations; + Vector operations_; /** * \brief vector of groups */ - Vector m_groups; + Vector groups_; /** * Active execution model implementation. @@ -200,7 +200,7 @@ class ExecutionSystem { */ const CompositorContext &getContext() const { - return m_context; + return context_; } SharedOperationBuffers &get_active_buffers() diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 6851c3b5c5c..3aefe8a3e2f 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -46,30 +46,30 @@ static rcti create_rect(const int width, const int height) MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state) { - m_rect = rect; - m_is_a_single_elem = false; - m_memoryProxy = memoryProxy; - m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType()); - m_buffer = (float *)MEM_mallocN_aligned( - sizeof(float) * buffer_len() * m_num_channels, 16, "COM_MemoryBuffer"); + rect_ = rect; + is_a_single_elem_ = false; + memoryProxy_ = memoryProxy; + num_channels_ = COM_data_type_num_channels(memoryProxy->getDataType()); + buffer_ = (float *)MEM_mallocN_aligned( + sizeof(float) * buffer_len() * num_channels_, 16, "COM_MemoryBuffer"); owns_data_ = true; - m_state = state; - m_datatype = memoryProxy->getDataType(); + state_ = state; + datatype_ = memoryProxy->getDataType(); set_strides(); } MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single_elem) { - m_rect = rect; - m_is_a_single_elem = is_a_single_elem; - m_memoryProxy = nullptr; - m_num_channels = COM_data_type_num_channels(dataType); - m_buffer = (float *)MEM_mallocN_aligned( - sizeof(float) * buffer_len() * m_num_channels, 16, "COM_MemoryBuffer"); + rect_ = rect; + is_a_single_elem_ = is_a_single_elem; + memoryProxy_ = nullptr; + num_channels_ = COM_data_type_num_channels(dataType); + buffer_ = (float *)MEM_mallocN_aligned( + sizeof(float) * buffer_len() * num_channels_, 16, "COM_MemoryBuffer"); owns_data_ = true; - m_state = MemoryBufferState::Temporary; - m_datatype = dataType; + state_ = MemoryBufferState::Temporary; + datatype_ = dataType; set_strides(); } @@ -93,53 +93,52 @@ MemoryBuffer::MemoryBuffer(float *buffer, const rcti &rect, const bool is_a_single_elem) { - m_rect = rect; - m_is_a_single_elem = is_a_single_elem; - m_memoryProxy = nullptr; - m_num_channels = num_channels; - m_datatype = COM_num_channels_data_type(num_channels); - m_buffer = buffer; + rect_ = rect; + is_a_single_elem_ = is_a_single_elem; + memoryProxy_ = nullptr; + num_channels_ = num_channels; + datatype_ = COM_num_channels_data_type(num_channels); + buffer_ = buffer; owns_data_ = false; - m_state = MemoryBufferState::Temporary; + state_ = MemoryBufferState::Temporary; set_strides(); } -MemoryBuffer::MemoryBuffer(const MemoryBuffer &src) - : MemoryBuffer(src.m_datatype, src.m_rect, false) +MemoryBuffer::MemoryBuffer(const MemoryBuffer &src) : MemoryBuffer(src.datatype_, src.rect_, false) { - m_memoryProxy = src.m_memoryProxy; + memoryProxy_ = src.memoryProxy_; /* src may be single elem buffer */ fill_from(src); } void MemoryBuffer::set_strides() { - if (m_is_a_single_elem) { + if (is_a_single_elem_) { this->elem_stride = 0; this->row_stride = 0; } else { - this->elem_stride = m_num_channels; - this->row_stride = getWidth() * m_num_channels; + this->elem_stride = num_channels_; + this->row_stride = getWidth() * num_channels_; } - to_positive_x_stride_ = m_rect.xmin < 0 ? -m_rect.xmin + 1 : (m_rect.xmin == 0 ? 1 : 0); - to_positive_y_stride_ = m_rect.ymin < 0 ? -m_rect.ymin + 1 : (m_rect.ymin == 0 ? 1 : 0); + to_positive_x_stride_ = rect_.xmin < 0 ? -rect_.xmin + 1 : (rect_.xmin == 0 ? 1 : 0); + to_positive_y_stride_ = rect_.ymin < 0 ? -rect_.ymin + 1 : (rect_.ymin == 0 ? 1 : 0); } void MemoryBuffer::clear() { - memset(m_buffer, 0, buffer_len() * m_num_channels * sizeof(float)); + memset(buffer_, 0, buffer_len() * num_channels_ * sizeof(float)); } BuffersIterator MemoryBuffer::iterate_with(Span inputs) { - return iterate_with(inputs, m_rect); + return iterate_with(inputs, rect_); } BuffersIterator MemoryBuffer::iterate_with(Span inputs, const rcti &area) { - BuffersIteratorBuilder builder(m_buffer, m_rect, area, elem_stride); + BuffersIteratorBuilder builder(buffer_, rect_, area, elem_stride); for (MemoryBuffer *input : inputs) { builder.add_input(input->getBuffer(), input->get_rect(), input->elem_stride); } @@ -153,20 +152,20 @@ BuffersIterator MemoryBuffer::iterate_with(Span inputs, c MemoryBuffer *MemoryBuffer::inflate() const { BLI_assert(is_a_single_elem()); - MemoryBuffer *inflated = new MemoryBuffer(m_datatype, m_rect, false); - inflated->copy_from(this, m_rect); + MemoryBuffer *inflated = new MemoryBuffer(datatype_, rect_, false); + inflated->copy_from(this, rect_); return inflated; } float MemoryBuffer::get_max_value() const { - float result = m_buffer[0]; + float result = buffer_[0]; const unsigned int size = this->buffer_len(); unsigned int i; - const float *fp_src = m_buffer; + const float *fp_src = buffer_; - for (i = 0; i < size; i++, fp_src += m_num_channels) { + for (i = 0; i < size; i++, fp_src += num_channels_) { float value = *fp_src; if (value > result) { result = value; @@ -181,10 +180,10 @@ float MemoryBuffer::get_max_value(const rcti &rect) const rcti rect_clamp; /* first clamp the rect by the bounds or we get un-initialized values */ - BLI_rcti_isect(&rect, &m_rect, &rect_clamp); + BLI_rcti_isect(&rect, &rect_, &rect_clamp); if (!BLI_rcti_is_empty(&rect_clamp)) { - MemoryBuffer temp_buffer(m_datatype, rect_clamp); + MemoryBuffer temp_buffer(datatype_, rect_clamp); temp_buffer.fill_from(*this); return temp_buffer.get_max_value(); } @@ -195,9 +194,9 @@ float MemoryBuffer::get_max_value(const rcti &rect) const MemoryBuffer::~MemoryBuffer() { - if (m_buffer && owns_data_) { - MEM_freeN(m_buffer); - m_buffer = nullptr; + if (buffer_ && owns_data_) { + MEM_freeN(buffer_); + buffer_ = nullptr; } } @@ -398,28 +397,28 @@ void MemoryBuffer::fill(const rcti &area, void MemoryBuffer::fill_from(const MemoryBuffer &src) { rcti overlap; - overlap.xmin = MAX2(m_rect.xmin, src.m_rect.xmin); - overlap.xmax = MIN2(m_rect.xmax, src.m_rect.xmax); - overlap.ymin = MAX2(m_rect.ymin, src.m_rect.ymin); - overlap.ymax = MIN2(m_rect.ymax, src.m_rect.ymax); + overlap.xmin = MAX2(rect_.xmin, src.rect_.xmin); + overlap.xmax = MIN2(rect_.xmax, src.rect_.xmax); + overlap.ymin = MAX2(rect_.ymin, src.rect_.ymin); + overlap.ymax = MIN2(rect_.ymax, src.rect_.ymax); copy_from(&src, overlap); } void MemoryBuffer::writePixel(int x, int y, const float color[4]) { - if (x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax) { + if (x >= rect_.xmin && x < rect_.xmax && y >= rect_.ymin && y < rect_.ymax) { const int offset = get_coords_offset(x, y); - memcpy(&m_buffer[offset], color, sizeof(float) * m_num_channels); + memcpy(&buffer_[offset], color, sizeof(float) * num_channels_); } } void MemoryBuffer::addPixel(int x, int y, const float color[4]) { - if (x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax) { + if (x >= rect_.xmin && x < rect_.xmax && y >= rect_.ymin && y < rect_.ymax) { const int offset = get_coords_offset(x, y); - float *dst = &m_buffer[offset]; + float *dst = &buffer_[offset]; const float *src = color; - for (int i = 0; i < m_num_channels; i++, dst++, src++) { + for (int i = 0; i < num_channels_; i++, dst++, src++) { *dst += *src; } } @@ -434,7 +433,7 @@ static void read_ewa_elem(void *userdata, int x, int y, float result[4]) void MemoryBuffer::read_elem_filtered( const float x, const float y, float dx[2], float dy[2], float *out) const { - BLI_assert(m_datatype == DataType::Color); + BLI_assert(datatype_ == DataType::Color); const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; @@ -469,11 +468,11 @@ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4] /* TODO(manzanilla): to be removed with tiled implementation. */ void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2]) { - if (m_is_a_single_elem) { - memcpy(result, m_buffer, sizeof(float) * m_num_channels); + if (is_a_single_elem_) { + memcpy(result, buffer_, sizeof(float) * num_channels_); } else { - BLI_assert(m_datatype == DataType::Color); + BLI_assert(datatype_ == DataType::Color); float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight(); /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives, * but compositor uses pixel space. For now let's just divide the values and diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index c0d086e5727..984db4acc2a 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -77,38 +77,38 @@ class MemoryBuffer { /** * \brief proxy of the memory (same for all chunks in the same buffer) */ - MemoryProxy *m_memoryProxy; + MemoryProxy *memoryProxy_; /** * \brief the type of buffer DataType::Value, DataType::Vector, DataType::Color */ - DataType m_datatype; + DataType datatype_; /** * \brief region of this buffer inside relative to the MemoryProxy */ - rcti m_rect; + rcti rect_; /** * \brief state of the buffer */ - MemoryBufferState m_state; + MemoryBufferState state_; /** * \brief the actual float buffer/data */ - float *m_buffer; + float *buffer_; /** * \brief the number of channels of a single value in the buffer. * For value buffers this is 1, vector 3 and color 4 */ - uint8_t m_num_channels; + uint8_t num_channels_; /** * Whether buffer is a single element in memory. */ - bool m_is_a_single_elem; + bool is_a_single_elem_; /** * Whether MemoryBuffer owns buffer data. @@ -153,21 +153,21 @@ class MemoryBuffer { */ bool is_a_single_elem() const { - return m_is_a_single_elem; + return is_a_single_elem_; } float &operator[](int index) { - BLI_assert(m_is_a_single_elem ? index < m_num_channels : - index < get_coords_offset(getWidth(), getHeight())); - return m_buffer[index]; + BLI_assert(is_a_single_elem_ ? index < num_channels_ : + index < get_coords_offset(getWidth(), getHeight())); + return buffer_[index]; } const float &operator[](int index) const { - BLI_assert(m_is_a_single_elem ? index < m_num_channels : - index < get_coords_offset(getWidth(), getHeight())); - return m_buffer[index]; + BLI_assert(is_a_single_elem_ ? index < num_channels_ : + index < get_coords_offset(getWidth(), getHeight())); + return buffer_[index]; } /** @@ -175,7 +175,7 @@ class MemoryBuffer { */ intptr_t get_coords_offset(int x, int y) const { - return ((intptr_t)y - m_rect.ymin) * row_stride + ((intptr_t)x - m_rect.xmin) * elem_stride; + return ((intptr_t)y - rect_.ymin) * row_stride + ((intptr_t)x - rect_.xmin) * elem_stride; } /** @@ -184,7 +184,7 @@ class MemoryBuffer { float *get_elem(int x, int y) { BLI_assert(has_coords(x, y)); - return m_buffer + get_coords_offset(x, y); + return buffer_ + get_coords_offset(x, y); } /** @@ -193,7 +193,7 @@ class MemoryBuffer { const float *get_elem(int x, int y) const { BLI_assert(has_coords(x, y)); - return m_buffer + get_coords_offset(x, y); + return buffer_ + get_coords_offset(x, y); } void read_elem(int x, int y, float *out) const @@ -219,16 +219,14 @@ class MemoryBuffer { void read_elem_bilinear(float x, float y, float *out) const { /* Only clear past +/-1 borders to be able to smooth edges. */ - if (x <= m_rect.xmin - 1.0f || x >= m_rect.xmax || y <= m_rect.ymin - 1.0f || - y >= m_rect.ymax) { + if (x <= rect_.xmin - 1.0f || x >= rect_.xmax || y <= rect_.ymin - 1.0f || y >= rect_.ymax) { clear_elem(out); return; } - if (m_is_a_single_elem) { - if (x >= m_rect.xmin && x < m_rect.xmax - 1.0f && y >= m_rect.ymin && - y < m_rect.ymax - 1.0f) { - memcpy(out, m_buffer, get_elem_bytes_len()); + if (is_a_single_elem_) { + if (x >= rect_.xmin && x < rect_.xmax - 1.0f && y >= rect_.ymin && y < rect_.ymax - 1.0f) { + memcpy(out, buffer_, get_elem_bytes_len()); return; } @@ -253,15 +251,15 @@ class MemoryBuffer { single_y = rel_y - last_y; } - BLI_bilinear_interpolation_fl(m_buffer, out, 1, 1, m_num_channels, single_x, single_y); + BLI_bilinear_interpolation_fl(buffer_, out, 1, 1, num_channels_, single_x, single_y); return; } - BLI_bilinear_interpolation_fl(m_buffer, + BLI_bilinear_interpolation_fl(buffer_, out, getWidth(), getHeight(), - m_num_channels, + num_channels_, get_relative_x(x), get_relative_y(y)); } @@ -288,8 +286,8 @@ class MemoryBuffer { */ float &get_value(int x, int y, int channel) { - BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels); - return m_buffer[get_coords_offset(x, y) + channel]; + BLI_assert(has_coords(x, y) && channel >= 0 && channel < num_channels_); + return buffer_[get_coords_offset(x, y) + channel]; } /** @@ -297,8 +295,8 @@ class MemoryBuffer { */ const float &get_value(int x, int y, int channel) const { - BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels); - return m_buffer[get_coords_offset(x, y) + channel]; + BLI_assert(has_coords(x, y) && channel >= 0 && channel < num_channels_); + return buffer_[get_coords_offset(x, y) + channel]; } /** @@ -307,7 +305,7 @@ class MemoryBuffer { const float *get_row_end(int y) const { BLI_assert(has_y(y)); - return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y)); + return buffer_ + (is_a_single_elem() ? num_channels_ : get_coords_offset(getWidth(), y)); } /** @@ -330,12 +328,12 @@ class MemoryBuffer { uint8_t get_num_channels() const { - return m_num_channels; + return num_channels_; } uint8_t get_elem_bytes_len() const { - return m_num_channels * sizeof(float); + return num_channels_ * sizeof(float); } /** @@ -343,22 +341,22 @@ class MemoryBuffer { */ BufferRange as_range() { - return BufferRange(m_buffer, 0, buffer_len(), elem_stride); + return BufferRange(buffer_, 0, buffer_len(), elem_stride); } BufferRange as_range() const { - return BufferRange(m_buffer, 0, buffer_len(), elem_stride); + return BufferRange(buffer_, 0, buffer_len(), elem_stride); } BufferArea get_buffer_area(const rcti &area) { - return BufferArea(m_buffer, getWidth(), area, elem_stride); + return BufferArea(buffer_, getWidth(), area, elem_stride); } BufferArea get_buffer_area(const rcti &area) const { - return BufferArea(m_buffer, getWidth(), area, elem_stride); + return BufferArea(buffer_, getWidth(), area, elem_stride); } BuffersIterator iterate_with(Span inputs); @@ -370,13 +368,13 @@ class MemoryBuffer { */ float *getBuffer() { - return m_buffer; + return buffer_; } float *release_ownership_buffer() { owns_data_ = false; - return m_buffer; + return buffer_; } MemoryBuffer *inflate() const; @@ -385,8 +383,8 @@ class MemoryBuffer { { const int w = getWidth(); const int h = getHeight(); - x = x - m_rect.xmin; - y = y - m_rect.ymin; + x = x - rect_.xmin; + y = y - rect_.ymin; switch (extend_x) { case MemoryBufferExtend::Clip: @@ -426,8 +424,8 @@ class MemoryBuffer { break; } - x = x + m_rect.xmin; - y = y + m_rect.ymin; + x = x + rect_.xmin; + y = y + rect_.ymin; } inline void wrap_pixel(float &x, @@ -437,8 +435,8 @@ class MemoryBuffer { { const float w = (float)getWidth(); const float h = (float)getHeight(); - x = x - m_rect.xmin; - y = y - m_rect.ymin; + x = x - rect_.xmin; + y = y - rect_.ymin; switch (extend_x) { case MemoryBufferExtend::Clip: @@ -478,8 +476,8 @@ class MemoryBuffer { break; } - x = x + m_rect.xmin; - y = y + m_rect.ymin; + x = x + rect_.xmin; + y = y + rect_.ymin; } /* TODO(manzanilla): to be removed with tiled implementation. For applying #MemoryBufferExtend @@ -490,19 +488,19 @@ class MemoryBuffer { MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) { - bool clip_x = (extend_x == MemoryBufferExtend::Clip && (x < m_rect.xmin || x >= m_rect.xmax)); - bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < m_rect.ymin || y >= m_rect.ymax)); + bool clip_x = (extend_x == MemoryBufferExtend::Clip && (x < rect_.xmin || x >= rect_.xmax)); + bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < rect_.ymin || y >= rect_.ymax)); if (clip_x || clip_y) { /* clip result outside rect is zero */ - memset(result, 0, m_num_channels * sizeof(float)); + memset(result, 0, num_channels_ * sizeof(float)); } else { int u = x; int v = y; this->wrap_pixel(u, v, extend_x, extend_y); const int offset = get_coords_offset(u, v); - float *buffer = &m_buffer[offset]; - memcpy(result, buffer, sizeof(float) * m_num_channels); + float *buffer = &buffer_[offset]; + memcpy(result, buffer, sizeof(float) * num_channels_); } } @@ -520,11 +518,11 @@ class MemoryBuffer { const int offset = get_coords_offset(u, v); BLI_assert(offset >= 0); - BLI_assert(offset < this->buffer_len() * m_num_channels); - BLI_assert(!(extend_x == MemoryBufferExtend::Clip && (u < m_rect.xmin || u >= m_rect.xmax)) && - !(extend_y == MemoryBufferExtend::Clip && (v < m_rect.ymin || v >= m_rect.ymax))); - float *buffer = &m_buffer[offset]; - memcpy(result, buffer, sizeof(float) * m_num_channels); + BLI_assert(offset < this->buffer_len() * num_channels_); + BLI_assert(!(extend_x == MemoryBufferExtend::Clip && (u < rect_.xmin || u >= rect_.xmax)) && + !(extend_y == MemoryBufferExtend::Clip && (v < rect_.ymin || v >= rect_.ymax))); + float *buffer = &buffer_[offset]; + memcpy(result, buffer, sizeof(float) * num_channels_); } void writePixel(int x, int y, const float color[4]); @@ -540,18 +538,18 @@ class MemoryBuffer { this->wrap_pixel(u, v, extend_x, extend_y); if ((extend_x != MemoryBufferExtend::Repeat && (u < 0.0f || u >= getWidth())) || (extend_y != MemoryBufferExtend::Repeat && (v < 0.0f || v >= getHeight()))) { - copy_vn_fl(result, m_num_channels, 0.0f); + copy_vn_fl(result, num_channels_, 0.0f); return; } - if (m_is_a_single_elem) { - memcpy(result, m_buffer, sizeof(float) * m_num_channels); + if (is_a_single_elem_) { + memcpy(result, buffer_, sizeof(float) * num_channels_); } else { - BLI_bilinear_interpolation_wrap_fl(m_buffer, + BLI_bilinear_interpolation_wrap_fl(buffer_, result, getWidth(), getHeight(), - m_num_channels, + num_channels_, u, v, extend_x == MemoryBufferExtend::Repeat, @@ -566,7 +564,7 @@ class MemoryBuffer { */ inline bool isTemporarily() const { - return m_state == MemoryBufferState::Temporary; + return state_ == MemoryBufferState::Temporary; } void copy_from(const MemoryBuffer *src, const rcti &area); @@ -632,7 +630,7 @@ class MemoryBuffer { */ const rcti &get_rect() const { - return m_rect; + return rect_; } /** @@ -640,7 +638,7 @@ class MemoryBuffer { */ const int getWidth() const { - return BLI_rcti_size_x(&m_rect); + return BLI_rcti_size_x(&rect_); } /** @@ -648,7 +646,7 @@ class MemoryBuffer { */ const int getHeight() const { - return BLI_rcti_size_y(&m_rect); + return BLI_rcti_size_y(&rect_); } /** @@ -668,17 +666,17 @@ class MemoryBuffer { void clear_elem(float *out) const { - memset(out, 0, m_num_channels * sizeof(float)); + memset(out, 0, num_channels_ * sizeof(float)); } template T get_relative_x(T x) const { - return x - m_rect.xmin; + return x - rect_.xmin; } template T get_relative_y(T y) const { - return y - m_rect.ymin; + return y - rect_.ymin; } template bool has_coords(T x, T y) const @@ -688,12 +686,12 @@ class MemoryBuffer { template bool has_x(T x) const { - return x >= m_rect.xmin && x < m_rect.xmax; + return x >= rect_.xmin && x < rect_.xmax; } template bool has_y(T y) const { - return y >= m_rect.ymin && y < m_rect.ymax; + return y >= rect_.ymin && y < rect_.ymax; } /* Fast `floor(..)` functions. The caller should check result is within buffer bounds. diff --git a/source/blender/compositor/intern/COM_MemoryProxy.cc b/source/blender/compositor/intern/COM_MemoryProxy.cc index 6502a8f4b9a..507caa25655 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.cc +++ b/source/blender/compositor/intern/COM_MemoryProxy.cc @@ -23,9 +23,9 @@ namespace blender::compositor { MemoryProxy::MemoryProxy(DataType datatype) { - m_writeBufferOperation = nullptr; - m_executor = nullptr; - m_datatype = datatype; + writeBufferOperation_ = nullptr; + executor_ = nullptr; + datatype_ = datatype; } void MemoryProxy::allocate(unsigned int width, unsigned int height) @@ -36,14 +36,14 @@ void MemoryProxy::allocate(unsigned int width, unsigned int height) result.ymin = 0; result.ymax = height; - m_buffer = new MemoryBuffer(this, result, MemoryBufferState::Default); + buffer_ = new MemoryBuffer(this, result, MemoryBufferState::Default); } void MemoryProxy::free() { - if (m_buffer) { - delete m_buffer; - m_buffer = nullptr; + if (buffer_) { + delete buffer_; + buffer_ = nullptr; } } diff --git a/source/blender/compositor/intern/COM_MemoryProxy.h b/source/blender/compositor/intern/COM_MemoryProxy.h index cae52182f26..cf262d72649 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.h +++ b/source/blender/compositor/intern/COM_MemoryProxy.h @@ -42,22 +42,22 @@ class MemoryProxy { /** * \brief reference to the output operation of the executiongroup */ - WriteBufferOperation *m_writeBufferOperation; + WriteBufferOperation *writeBufferOperation_; /** * \brief reference to the executor. the Execution group that can fill a chunk */ - ExecutionGroup *m_executor; + ExecutionGroup *executor_; /** * \brief the allocated memory */ - MemoryBuffer *m_buffer; + MemoryBuffer *buffer_; /** * \brief datatype of this MemoryProxy */ - DataType m_datatype; + DataType datatype_; public: MemoryProxy(DataType type); @@ -68,7 +68,7 @@ class MemoryProxy { */ void setExecutor(ExecutionGroup *executor) { - m_executor = executor; + executor_ = executor; } /** @@ -76,7 +76,7 @@ class MemoryProxy { */ ExecutionGroup *getExecutor() const { - return m_executor; + return executor_; } /** @@ -85,7 +85,7 @@ class MemoryProxy { */ void setWriteBufferOperation(WriteBufferOperation *operation) { - m_writeBufferOperation = operation; + writeBufferOperation_ = operation; } /** @@ -94,7 +94,7 @@ class MemoryProxy { */ WriteBufferOperation *getWriteBufferOperation() const { - return m_writeBufferOperation; + return writeBufferOperation_; } /** @@ -112,12 +112,12 @@ class MemoryProxy { */ inline MemoryBuffer *getBuffer() { - return m_buffer; + return buffer_; } inline DataType getDataType() { - return m_datatype; + return datatype_; } #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/intern/COM_Node.cc b/source/blender/compositor/intern/COM_Node.cc index 2cd56c1d7c1..7acf91df7d4 100644 --- a/source/blender/compositor/intern/COM_Node.cc +++ b/source/blender/compositor/intern/COM_Node.cc @@ -29,10 +29,10 @@ namespace blender::compositor { **************/ Node::Node(bNode *editorNode, bool create_sockets) - : m_editorNodeTree(nullptr), - m_editorNode(editorNode), - m_inActiveGroup(false), - m_instanceKey(NODE_INSTANCE_KEY_NONE) + : editorNodeTree_(nullptr), + editorNode_(editorNode), + inActiveGroup_(false), + instanceKey_(NODE_INSTANCE_KEY_NONE) { if (create_sockets) { bNodeSocket *input = (bNodeSocket *)editorNode->inputs.first; @@ -137,13 +137,13 @@ bNodeSocket *Node::getEditorOutputSocket(int editorNodeOutputSocketIndex) *******************/ NodeInput::NodeInput(Node *node, bNodeSocket *b_socket, DataType datatype) - : m_node(node), m_editorSocket(b_socket), m_datatype(datatype), m_link(nullptr) + : node_(node), editorSocket_(b_socket), datatype_(datatype), link_(nullptr) { } void NodeInput::setLink(NodeOutput *link) { - m_link = link; + link_ = link; } float NodeInput::getEditorValueFloat() const @@ -172,7 +172,7 @@ void NodeInput::getEditorValueVector(float *value) const ********************/ NodeOutput::NodeOutput(Node *node, bNodeSocket *b_socket, DataType datatype) - : m_node(node), m_editorSocket(b_socket), m_datatype(datatype) + : node_(node), editorSocket_(b_socket), datatype_(datatype) { } diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h index 76eb03693ae..3d9c62aca67 100644 --- a/source/blender/compositor/intern/COM_Node.h +++ b/source/blender/compositor/intern/COM_Node.h @@ -41,22 +41,22 @@ class Node { /** * \brief stores the reference to the SDNA bNode struct */ - bNodeTree *m_editorNodeTree; + bNodeTree *editorNodeTree_; /** * \brief stores the reference to the SDNA bNode struct */ - bNode *m_editorNode; + bNode *editorNode_; /** * \brief Is this node part of the active group */ - bool m_inActiveGroup; + bool inActiveGroup_; /** * \brief Instance key to identify the node in an instance hash table */ - bNodeInstanceKey m_instanceKey; + bNodeInstanceKey instanceKey_; protected: /** @@ -78,7 +78,7 @@ class Node { */ bNode *getbNode() const { - return m_editorNode; + return editorNode_; } /** @@ -86,7 +86,7 @@ class Node { */ bNodeTree *getbNodeTree() const { - return m_editorNodeTree; + return editorNodeTree_; } /** @@ -97,7 +97,7 @@ class Node { */ void setbNode(bNode *node) { - m_editorNode = node; + editorNode_ = node; } /** @@ -106,7 +106,7 @@ class Node { */ void setbNodeTree(bNodeTree *nodetree) { - m_editorNodeTree = nodetree; + editorNodeTree_ = nodetree; } /** @@ -145,7 +145,7 @@ class Node { */ void setIsInActiveGroup(bool value) { - m_inActiveGroup = value; + inActiveGroup_ = value; } /** @@ -156,7 +156,7 @@ class Node { */ inline bool isInActiveGroup() const { - return m_inActiveGroup; + return inActiveGroup_; } /** @@ -172,11 +172,11 @@ class Node { void setInstanceKey(bNodeInstanceKey instance_key) { - m_instanceKey = instance_key; + instanceKey_ = instance_key; } bNodeInstanceKey getInstanceKey() const { - return m_instanceKey; + return instanceKey_; } protected: @@ -206,41 +206,41 @@ class Node { */ class NodeInput { private: - Node *m_node; - bNodeSocket *m_editorSocket; + Node *node_; + bNodeSocket *editorSocket_; - DataType m_datatype; + DataType datatype_; /** * \brief link connected to this NodeInput. * An input socket can only have a single link */ - NodeOutput *m_link; + NodeOutput *link_; public: NodeInput(Node *node, bNodeSocket *b_socket, DataType datatype); Node *getNode() const { - return m_node; + return node_; } DataType getDataType() const { - return m_datatype; + return datatype_; } bNodeSocket *getbNodeSocket() const { - return m_editorSocket; + return editorSocket_; } void setLink(NodeOutput *link); bool isLinked() const { - return m_link; + return link_; } NodeOutput *getLink() { - return m_link; + return link_; } float getEditorValueFloat() const; @@ -254,25 +254,25 @@ class NodeInput { */ class NodeOutput { private: - Node *m_node; - bNodeSocket *m_editorSocket; + Node *node_; + bNodeSocket *editorSocket_; - DataType m_datatype; + DataType datatype_; public: NodeOutput(Node *node, bNodeSocket *b_socket, DataType datatype); Node *getNode() const { - return m_node; + return node_; } DataType getDataType() const { - return m_datatype; + return datatype_; } bNodeSocket *getbNodeSocket() const { - return m_editorSocket; + return editorSocket_; } float getEditorValueFloat(); diff --git a/source/blender/compositor/intern/COM_NodeConverter.cc b/source/blender/compositor/intern/COM_NodeConverter.cc index 73f64c7cfd4..314b5e9572a 100644 --- a/source/blender/compositor/intern/COM_NodeConverter.cc +++ b/source/blender/compositor/intern/COM_NodeConverter.cc @@ -29,38 +29,38 @@ namespace blender::compositor { -NodeConverter::NodeConverter(NodeOperationBuilder *builder) : m_builder(builder) +NodeConverter::NodeConverter(NodeOperationBuilder *builder) : builder_(builder) { } void NodeConverter::addOperation(NodeOperation *operation) { - m_builder->addOperation(operation); + builder_->addOperation(operation); } void NodeConverter::mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket) { - m_builder->mapInputSocket(node_socket, operation_socket); + builder_->mapInputSocket(node_socket, operation_socket); } void NodeConverter::mapOutputSocket(NodeOutput *node_socket, NodeOperationOutput *operation_socket) { - m_builder->mapOutputSocket(node_socket, operation_socket); + builder_->mapOutputSocket(node_socket, operation_socket); } void NodeConverter::addLink(NodeOperationOutput *from, NodeOperationInput *to) { - m_builder->addLink(from, to); + builder_->addLink(from, to); } void NodeConverter::addPreview(NodeOperationOutput *output) { - m_builder->addPreview(output); + builder_->addPreview(output); } void NodeConverter::addNodeInputPreview(NodeInput *input) { - m_builder->addNodeInputPreview(input); + builder_->addNodeInputPreview(input); } NodeOperation *NodeConverter::setInvalidOutput(NodeOutput *output) @@ -71,8 +71,8 @@ NodeOperation *NodeConverter::setInvalidOutput(NodeOutput *output) SetColorOperation *operation = new SetColorOperation(); operation->setChannels(warning_color); - m_builder->addOperation(operation); - m_builder->mapOutputSocket(output, operation->getOutputSocket()); + builder_->addOperation(operation); + builder_->mapOutputSocket(output, operation->getOutputSocket()); return operation; } @@ -80,9 +80,9 @@ NodeOperation *NodeConverter::setInvalidOutput(NodeOutput *output) NodeOperationOutput *NodeConverter::addInputProxy(NodeInput *input, bool use_conversion) { SocketProxyOperation *proxy = new SocketProxyOperation(input->getDataType(), use_conversion); - m_builder->addOperation(proxy); + builder_->addOperation(proxy); - m_builder->mapInputSocket(input, proxy->getInputSocket(0)); + builder_->mapInputSocket(input, proxy->getInputSocket(0)); return proxy->getOutputSocket(); } @@ -90,9 +90,9 @@ NodeOperationOutput *NodeConverter::addInputProxy(NodeInput *input, bool use_con NodeOperationInput *NodeConverter::addOutputProxy(NodeOutput *output, bool use_conversion) { SocketProxyOperation *proxy = new SocketProxyOperation(output->getDataType(), use_conversion); - m_builder->addOperation(proxy); + builder_->addOperation(proxy); - m_builder->mapOutputSocket(output, proxy->getOutputSocket()); + builder_->mapOutputSocket(output, proxy->getOutputSocket()); return proxy->getInputSocket(0); } @@ -102,8 +102,8 @@ void NodeConverter::addInputValue(NodeOperationInput *input, float value) SetValueOperation *operation = new SetValueOperation(); operation->setValue(value); - m_builder->addOperation(operation); - m_builder->addLink(operation->getOutputSocket(), input); + builder_->addOperation(operation); + builder_->addLink(operation->getOutputSocket(), input); } void NodeConverter::addInputColor(NodeOperationInput *input, const float value[4]) @@ -111,8 +111,8 @@ void NodeConverter::addInputColor(NodeOperationInput *input, const float value[4 SetColorOperation *operation = new SetColorOperation(); operation->setChannels(value); - m_builder->addOperation(operation); - m_builder->addLink(operation->getOutputSocket(), input); + builder_->addOperation(operation); + builder_->addLink(operation->getOutputSocket(), input); } void NodeConverter::addInputVector(NodeOperationInput *input, const float value[3]) @@ -120,8 +120,8 @@ void NodeConverter::addInputVector(NodeOperationInput *input, const float value[ SetVectorOperation *operation = new SetVectorOperation(); operation->setVector(value); - m_builder->addOperation(operation); - m_builder->addLink(operation->getOutputSocket(), input); + builder_->addOperation(operation); + builder_->addLink(operation->getOutputSocket(), input); } void NodeConverter::addOutputValue(NodeOutput *output, float value) @@ -129,8 +129,8 @@ void NodeConverter::addOutputValue(NodeOutput *output, float value) SetValueOperation *operation = new SetValueOperation(); operation->setValue(value); - m_builder->addOperation(operation); - m_builder->mapOutputSocket(output, operation->getOutputSocket()); + builder_->addOperation(operation); + builder_->mapOutputSocket(output, operation->getOutputSocket()); } void NodeConverter::addOutputColor(NodeOutput *output, const float value[4]) @@ -138,8 +138,8 @@ void NodeConverter::addOutputColor(NodeOutput *output, const float value[4]) SetColorOperation *operation = new SetColorOperation(); operation->setChannels(value); - m_builder->addOperation(operation); - m_builder->mapOutputSocket(output, operation->getOutputSocket()); + builder_->addOperation(operation); + builder_->mapOutputSocket(output, operation->getOutputSocket()); } void NodeConverter::addOutputVector(NodeOutput *output, const float value[3]) @@ -147,18 +147,18 @@ void NodeConverter::addOutputVector(NodeOutput *output, const float value[3]) SetVectorOperation *operation = new SetVectorOperation(); operation->setVector(value); - m_builder->addOperation(operation); - m_builder->mapOutputSocket(output, operation->getOutputSocket()); + builder_->addOperation(operation); + builder_->mapOutputSocket(output, operation->getOutputSocket()); } void NodeConverter::registerViewer(ViewerOperation *viewer) { - m_builder->registerViewer(viewer); + builder_->registerViewer(viewer); } ViewerOperation *NodeConverter::active_viewer() const { - return m_builder->active_viewer(); + return builder_->active_viewer(); } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_NodeConverter.h b/source/blender/compositor/intern/COM_NodeConverter.h index b3f03485249..afbd53fa67d 100644 --- a/source/blender/compositor/intern/COM_NodeConverter.h +++ b/source/blender/compositor/intern/COM_NodeConverter.h @@ -116,7 +116,7 @@ class NodeConverter { private: /** The internal builder for storing the results of the graph construction. */ - NodeOperationBuilder *m_builder; + NodeOperationBuilder *builder_; #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeCompiler") diff --git a/source/blender/compositor/intern/COM_NodeGraph.cc b/source/blender/compositor/intern/COM_NodeGraph.cc index 0c94dfce409..a06c9349bb7 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.cc +++ b/source/blender/compositor/intern/COM_NodeGraph.cc @@ -36,8 +36,8 @@ namespace blender::compositor { NodeGraph::~NodeGraph() { - while (m_nodes.size()) { - delete m_nodes.pop_last(); + while (nodes_.size()) { + delete nodes_.pop_last(); } } @@ -75,14 +75,14 @@ void NodeGraph::add_node(Node *node, node->setInstanceKey(key); node->setIsInActiveGroup(is_active_group); - m_nodes.append(node); + nodes_.append(node); DebugInfo::node_added(node); } void NodeGraph::add_link(NodeOutput *fromSocket, NodeInput *toSocket) { - m_links.append(Link(fromSocket, toSocket)); + links_.append(Link(fromSocket, toSocket)); /* register with the input */ toSocket->setLink(fromSocket); @@ -104,7 +104,7 @@ void NodeGraph::add_bNodeTree(const CompositorContext &context, add_bNode(context, tree, node, key, is_active_group); } - NodeRange node_range(m_nodes.begin() + nodes_start, m_nodes.end()); + NodeRange node_range(nodes_.begin() + nodes_start, nodes_.end()); /* Add all node-links of the tree to the link list. */ for (bNodeLink *nodelink = (bNodeLink *)tree->links.first; nodelink; nodelink = nodelink->next) { add_bNodeLink(node_range, nodelink); @@ -285,7 +285,7 @@ void NodeGraph::add_proxies_group(const CompositorContext &context, } /* use node list size before adding proxies, so they can be connected in add_bNodeTree */ - int nodes_start = m_nodes.size(); + int nodes_start = nodes_.size(); /* create proxy nodes for group input/output nodes */ for (bNode *b_node_io = (bNode *)b_group_tree->nodes.first; b_node_io; diff --git a/source/blender/compositor/intern/COM_NodeGraph.h b/source/blender/compositor/intern/COM_NodeGraph.h index 12aca8f6069..cc628ebb724 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.h +++ b/source/blender/compositor/intern/COM_NodeGraph.h @@ -47,19 +47,19 @@ class NodeGraph { }; private: - Vector m_nodes; - Vector m_links; + Vector nodes_; + Vector links_; public: ~NodeGraph(); const Vector &nodes() const { - return m_nodes; + return nodes_; } const Vector &links() const { - return m_links; + return links_; } void from_bNodeTree(const CompositorContext &context, bNodeTree *tree); diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 914842c816c..0bfc088e4bf 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -34,20 +34,20 @@ NodeOperation::NodeOperation() { canvas_input_index_ = 0; canvas_ = COM_AREA_NONE; - m_btree = nullptr; + btree_ = nullptr; } /** Get constant value when operation is constant, otherwise return default_value. */ float NodeOperation::get_constant_value_default(float default_value) { - BLI_assert(m_outputs.size() > 0 && getOutputSocket()->getDataType() == DataType::Value); + BLI_assert(outputs_.size() > 0 && getOutputSocket()->getDataType() == DataType::Value); return *get_constant_elem_default(&default_value); } /** Get constant elem when operation is constant, otherwise return default_elem. */ const float *NodeOperation::get_constant_elem_default(const float *default_elem) { - BLI_assert(m_outputs.size() > 0); + BLI_assert(outputs_.size() > 0); if (get_flags().is_constant_operation) { return static_cast(this)->get_constant_elem(); } @@ -72,15 +72,15 @@ std::optional NodeOperation::generate_hash() } hash_params(canvas_.ymin, canvas_.ymax); - if (m_outputs.size() > 0) { - BLI_assert(m_outputs.size() == 1); + if (outputs_.size() > 0) { + BLI_assert(outputs_.size() == 1); hash_param(this->getOutputSocket()->getDataType()); } NodeOperationHash hash; hash.params_hash_ = params_hash_; hash.parents_hash_ = 0; - for (NodeOperationInput &socket : m_inputs) { + for (NodeOperationInput &socket : inputs_) { if (!socket.isConnected()) { continue; } @@ -108,29 +108,29 @@ std::optional NodeOperation::generate_hash() NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index) { - return &m_outputs[index]; + return &outputs_[index]; } NodeOperationInput *NodeOperation::getInputSocket(unsigned int index) { - return &m_inputs[index]; + return &inputs_[index]; } void NodeOperation::addInputSocket(DataType datatype, ResizeMode resize_mode) { - m_inputs.append(NodeOperationInput(this, datatype, resize_mode)); + inputs_.append(NodeOperationInput(this, datatype, resize_mode)); } void NodeOperation::addOutputSocket(DataType datatype) { - m_outputs.append(NodeOperationOutput(this, datatype)); + outputs_.append(NodeOperationOutput(this, datatype)); } void NodeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { unsigned int used_canvas_index = 0; if (canvas_input_index_ == RESOLUTION_INPUT_ANY) { - for (NodeOperationInput &input : m_inputs) { + for (NodeOperationInput &input : inputs_) { rcti any_area = COM_AREA_NONE; const bool determined = input.determine_canvas(preferred_area, any_area); if (determined) { @@ -140,8 +140,8 @@ void NodeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) used_canvas_index += 1; } } - else if (canvas_input_index_ < m_inputs.size()) { - NodeOperationInput &input = m_inputs[canvas_input_index_]; + else if (canvas_input_index_ < inputs_.size()) { + NodeOperationInput &input = inputs_[canvas_input_index_]; input.determine_canvas(preferred_area, r_area); used_canvas_index = canvas_input_index_; } @@ -152,11 +152,11 @@ void NodeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) rcti unused_area; const rcti &local_preferred_area = r_area; - for (unsigned int index = 0; index < m_inputs.size(); index++) { + for (unsigned int index = 0; index < inputs_.size(); index++) { if (index == used_canvas_index) { continue; } - NodeOperationInput &input = m_inputs[index]; + NodeOperationInput &input = inputs_[index]; if (input.isConnected()) { input.determine_canvas(local_preferred_area, unused_area); } @@ -179,22 +179,22 @@ void NodeOperation::initExecution() void NodeOperation::initMutex() { - BLI_mutex_init(&m_mutex); + BLI_mutex_init(&mutex_); } void NodeOperation::lockMutex() { - BLI_mutex_lock(&m_mutex); + BLI_mutex_lock(&mutex_); } void NodeOperation::unlockMutex() { - BLI_mutex_unlock(&m_mutex); + BLI_mutex_unlock(&mutex_); } void NodeOperation::deinitMutex() { - BLI_mutex_end(&m_mutex); + BLI_mutex_end(&mutex_); } void NodeOperation::deinitExecution() @@ -219,7 +219,7 @@ const rcti &NodeOperation::get_canvas() const */ void NodeOperation::unset_canvas() { - BLI_assert(m_inputs.size() == 0); + BLI_assert(inputs_.size() == 0); flags.is_canvas_set = false; } @@ -242,7 +242,7 @@ bool NodeOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) { - if (m_inputs.size() == 0) { + if (inputs_.size() == 0) { BLI_rcti_init(output, input->xmin, input->xmax, input->ymin, input->ymax); return false; } @@ -445,14 +445,14 @@ void NodeOperation::remove_buffers_and_restore_original_inputs( *****************/ NodeOperationInput::NodeOperationInput(NodeOperation *op, DataType datatype, ResizeMode resizeMode) - : m_operation(op), m_datatype(datatype), m_resizeMode(resizeMode), m_link(nullptr) + : operation_(op), datatype_(datatype), resizeMode_(resizeMode), link_(nullptr) { } SocketReader *NodeOperationInput::getReader() { if (isConnected()) { - return &m_link->getOperation(); + return &link_->getOperation(); } return nullptr; @@ -463,8 +463,8 @@ SocketReader *NodeOperationInput::getReader() */ bool NodeOperationInput::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (m_link) { - m_link->determine_canvas(preferred_area, r_area); + if (link_) { + link_->determine_canvas(preferred_area, r_area); return !BLI_rcti_is_empty(&r_area); } return false; @@ -475,7 +475,7 @@ bool NodeOperationInput::determine_canvas(const rcti &preferred_area, rcti &r_ar ******************/ NodeOperationOutput::NodeOperationOutput(NodeOperation *op, DataType datatype) - : m_operation(op), m_datatype(datatype) + : operation_(op), datatype_(datatype) { } diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index 2bdc2bfeb0b..af9d62b4276 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -83,18 +83,18 @@ enum class ResizeMode { class NodeOperationInput { private: - NodeOperation *m_operation; + NodeOperation *operation_; /** Datatype of this socket. Is used for automatically data transformation. * \section data-conversion */ - DataType m_datatype; + DataType datatype_; /** Resize mode of this socket */ - ResizeMode m_resizeMode; + ResizeMode resizeMode_; /** Connected output */ - NodeOperationOutput *m_link; + NodeOperationOutput *link_; public: NodeOperationInput(NodeOperation *op, @@ -103,33 +103,33 @@ class NodeOperationInput { NodeOperation &getOperation() const { - return *m_operation; + return *operation_; } DataType getDataType() const { - return m_datatype; + return datatype_; } void setLink(NodeOperationOutput *link) { - m_link = link; + link_ = link; } NodeOperationOutput *getLink() const { - return m_link; + return link_; } bool isConnected() const { - return m_link; + return link_; } void setResizeMode(ResizeMode resizeMode) { - m_resizeMode = resizeMode; + resizeMode_ = resizeMode; } ResizeMode getResizeMode() const { - return m_resizeMode; + return resizeMode_; } SocketReader *getReader(); @@ -143,23 +143,23 @@ class NodeOperationInput { class NodeOperationOutput { private: - NodeOperation *m_operation; + NodeOperation *operation_; /** Datatype of this socket. Is used for automatically data transformation. * \section data-conversion */ - DataType m_datatype; + DataType datatype_; public: NodeOperationOutput(NodeOperation *op, DataType datatype); NodeOperation &getOperation() const { - return *m_operation; + return *operation_; } DataType getDataType() const { - return m_datatype; + return datatype_; } void determine_canvas(const rcti &preferred_area, rcti &r_area); @@ -314,10 +314,10 @@ struct NodeOperationHash { */ class NodeOperation { private: - int m_id; - std::string m_name; - Vector m_inputs; - Vector m_outputs; + int id_; + std::string name_; + Vector inputs_; + Vector outputs_; size_t params_hash_; bool is_hash_output_params_implemented_; @@ -338,12 +338,12 @@ class NodeOperation { * \see NodeOperation.deinitMutex deinitializes this mutex * \see NodeOperation.getMutex retrieve a pointer to this mutex. */ - ThreadMutex m_mutex; + ThreadMutex mutex_; /** * \brief reference to the editing bNodeTree, used for break and update callback */ - const bNodeTree *m_btree; + const bNodeTree *btree_; protected: /** @@ -367,22 +367,22 @@ class NodeOperation { void set_name(const std::string name) { - m_name = name; + name_ = name; } const std::string get_name() const { - return m_name; + return name_; } void set_id(const int id) { - m_id = id; + id_ = id; } const int get_id() const { - return m_id; + return id_; } float get_constant_value_default(float default_value); @@ -397,11 +397,11 @@ class NodeOperation { unsigned int getNumberOfInputSockets() const { - return m_inputs.size(); + return inputs_.size(); } unsigned int getNumberOfOutputSockets() const { - return m_outputs.size(); + return outputs_.size(); } NodeOperationOutput *getOutputSocket(unsigned int index = 0); NodeOperationInput *getInputSocket(unsigned int index); @@ -442,7 +442,7 @@ class NodeOperation { void setbNodeTree(const bNodeTree *tree) { - m_btree = tree; + btree_ = tree; } void set_execution_system(ExecutionSystem *system) @@ -561,13 +561,13 @@ class NodeOperation { inline bool isBraked() const { - return m_btree->test_break(m_btree->tbh); + return btree_->test_break(btree_->tbh); } inline void updateDraw() { - if (m_btree->update_draw) { - m_btree->update_draw(m_btree->udh); + if (btree_->update_draw) { + btree_->update_draw(btree_->udh); } } diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 151356efb92..708bda30636 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -40,9 +40,9 @@ namespace blender::compositor { NodeOperationBuilder::NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree, ExecutionSystem *system) - : m_context(context), exec_system_(system), m_current_node(nullptr), m_active_viewer(nullptr) + : context_(context), exec_system_(system), current_node_(nullptr), active_viewer_(nullptr) { - m_graph.from_bNodeTree(*context, b_nodetree); + graph_.from_bNodeTree(*context, b_nodetree); } void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) @@ -50,29 +50,29 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) /* interface handle for nodes */ NodeConverter converter(this); - for (Node *node : m_graph.nodes()) { - m_current_node = node; + for (Node *node : graph_.nodes()) { + current_node_ = node; DebugInfo::node_to_operations(node); - node->convertToOperations(converter, *m_context); + node->convertToOperations(converter, *context_); } - m_current_node = nullptr; + current_node_ = nullptr; /* The input map constructed by nodes maps operation inputs to node inputs. * Inverting yields a map of node inputs to all connected operation inputs, * so multiple operations can use the same node input. */ blender::MultiValueMap inverse_input_map; - for (Map::MutableItem item : m_input_map.items()) { + for (Map::MutableItem item : input_map_.items()) { inverse_input_map.add(item.value, item.key); } - for (const NodeGraph::Link &link : m_graph.links()) { + for (const NodeGraph::Link &link : graph_.links()) { NodeOutput *from = link.from; NodeInput *to = link.to; - NodeOperationOutput *op_from = m_output_map.lookup_default(from, nullptr); + NodeOperationOutput *op_from = output_map_.lookup_default(from, nullptr); const blender::Span op_to_list = inverse_input_map.lookup(to); if (!op_from || op_to_list.is_empty()) { @@ -96,7 +96,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) add_datatype_conversions(); - if (m_context->get_execution_model() == eExecutionModel::FullFrame) { + if (context_->get_execution_model() == eExecutionModel::FullFrame) { save_graphviz("compositor_prior_folding"); ConstantFolder folder(*this); folder.fold_operations(); @@ -107,37 +107,37 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) save_graphviz("compositor_prior_merging"); merge_equal_operations(); - if (m_context->get_execution_model() == eExecutionModel::Tiled) { + if (context_->get_execution_model() == eExecutionModel::Tiled) { /* surround complex ops with read/write buffer */ add_complex_operation_buffers(); } /* links not available from here on */ - /* XXX make m_links a local variable to avoid confusion! */ - m_links.clear(); + /* XXX make links_ a local variable to avoid confusion! */ + links_.clear(); prune_operations(); /* ensure topological (link-based) order of nodes */ /*sort_operations();*/ /* not needed yet */ - if (m_context->get_execution_model() == eExecutionModel::Tiled) { + if (context_->get_execution_model() == eExecutionModel::Tiled) { /* create execution groups */ group_operations(); } /* transfer resulting operations to the system */ - system->set_operations(m_operations, m_groups); + system->set_operations(operations_, groups_); } void NodeOperationBuilder::addOperation(NodeOperation *operation) { - operation->set_id(m_operations.size()); - m_operations.append(operation); - if (m_current_node) { - operation->set_name(m_current_node->getbNode()->name); + operation->set_id(operations_.size()); + operations_.append(operation); + if (current_node_) { + operation->set_name(current_node_->getbNode()->name); } - operation->set_execution_model(m_context->get_execution_model()); + operation->set_execution_model(context_->get_execution_model()); operation->set_execution_system(exec_system_); } @@ -153,17 +153,17 @@ void NodeOperationBuilder::unlink_inputs_and_relink_outputs(NodeOperation *unlin NodeOperation *linked_op) { int i = 0; - while (i < m_links.size()) { - Link &link = m_links[i]; + while (i < links_.size()) { + Link &link = links_[i]; if (&link.to()->getOperation() == unlinked_op) { link.to()->setLink(nullptr); - m_links.remove(i); + links_.remove(i); continue; } if (&link.from()->getOperation() == unlinked_op) { link.to()->setLink(linked_op->getOutputSocket()); - m_links[i] = Link(linked_op->getOutputSocket(), link.to()); + links_[i] = Link(linked_op->getOutputSocket(), link.to()); } i++; } @@ -172,23 +172,23 @@ void NodeOperationBuilder::unlink_inputs_and_relink_outputs(NodeOperation *unlin void NodeOperationBuilder::mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket) { - BLI_assert(m_current_node); - BLI_assert(node_socket->getNode() == m_current_node); + BLI_assert(current_node_); + BLI_assert(node_socket->getNode() == current_node_); /* NOTE: this maps operation sockets to node sockets. * for resolving links the map will be inverted first in convertToOperations, * to get a list of links for each node input socket. */ - m_input_map.add_new(operation_socket, node_socket); + input_map_.add_new(operation_socket, node_socket); } void NodeOperationBuilder::mapOutputSocket(NodeOutput *node_socket, NodeOperationOutput *operation_socket) { - BLI_assert(m_current_node); - BLI_assert(node_socket->getNode() == m_current_node); + BLI_assert(current_node_); + BLI_assert(node_socket->getNode() == current_node_); - m_output_map.add_new(node_socket, operation_socket); + output_map_.add_new(node_socket, operation_socket); } void NodeOperationBuilder::addLink(NodeOperationOutput *from, NodeOperationInput *to) @@ -197,7 +197,7 @@ void NodeOperationBuilder::addLink(NodeOperationOutput *from, NodeOperationInput return; } - m_links.append(Link(from, to)); + links_.append(Link(from, to)); /* register with the input */ to->setLink(from); @@ -206,12 +206,12 @@ void NodeOperationBuilder::addLink(NodeOperationOutput *from, NodeOperationInput void NodeOperationBuilder::removeInputLink(NodeOperationInput *to) { int index = 0; - for (Link &link : m_links) { + for (Link &link : links_) { if (link.to() == to) { /* unregister with the input */ to->setLink(nullptr); - m_links.remove(index); + links_.remove(index); return; } index++; @@ -220,28 +220,28 @@ void NodeOperationBuilder::removeInputLink(NodeOperationInput *to) PreviewOperation *NodeOperationBuilder::make_preview_operation() const { - BLI_assert(m_current_node); + BLI_assert(current_node_); - if (!(m_current_node->getbNode()->flag & NODE_PREVIEW)) { + if (!(current_node_->getbNode()->flag & NODE_PREVIEW)) { return nullptr; } /* previews only in the active group */ - if (!m_current_node->isInActiveGroup()) { + if (!current_node_->isInActiveGroup()) { return nullptr; } /* do not calculate previews of hidden nodes */ - if (m_current_node->getbNode()->flag & NODE_HIDDEN) { + if (current_node_->getbNode()->flag & NODE_HIDDEN) { return nullptr; } - bNodeInstanceHash *previews = m_context->getPreviewHash(); + bNodeInstanceHash *previews = context_->getPreviewHash(); if (previews) { - PreviewOperation *operation = new PreviewOperation(m_context->getViewSettings(), - m_context->getDisplaySettings(), - m_current_node->getbNode()->preview_xsize, - m_current_node->getbNode()->preview_ysize); - operation->setbNodeTree(m_context->getbNodeTree()); - operation->verifyPreview(previews, m_current_node->getInstanceKey()); + PreviewOperation *operation = new PreviewOperation(context_->getViewSettings(), + context_->getDisplaySettings(), + current_node_->getbNode()->preview_xsize, + current_node_->getbNode()->preview_ysize); + operation->setbNodeTree(context_->getbNodeTree()); + operation->verifyPreview(previews, current_node_->getInstanceKey()); return operation; } @@ -270,18 +270,18 @@ void NodeOperationBuilder::addNodeInputPreview(NodeInput *input) void NodeOperationBuilder::registerViewer(ViewerOperation *viewer) { - if (m_active_viewer) { - if (m_current_node->isInActiveGroup()) { + if (active_viewer_) { + if (current_node_->isInActiveGroup()) { /* deactivate previous viewer */ - m_active_viewer->setActive(false); + active_viewer_->setActive(false); - m_active_viewer = viewer; + active_viewer_ = viewer; viewer->setActive(true); } } else { - if (m_current_node->getbNodeTree() == m_context->getbNodeTree()) { - m_active_viewer = viewer; + if (current_node_->getbNodeTree() == context_->getbNodeTree()) { + active_viewer_ = viewer; viewer->setActive(true); } } @@ -294,7 +294,7 @@ void NodeOperationBuilder::registerViewer(ViewerOperation *viewer) void NodeOperationBuilder::add_datatype_conversions() { Vector convert_links; - for (const Link &link : m_links) { + for (const Link &link : links_) { /* proxy operations can skip data type conversion */ NodeOperation *from_op = &link.from()->getOperation(); NodeOperation *to_op = &link.to()->getOperation(); @@ -322,10 +322,10 @@ void NodeOperationBuilder::add_datatype_conversions() void NodeOperationBuilder::add_operation_input_constants() { /* NOTE: unconnected inputs cached first to avoid modifying - * m_operations while iterating over it + * operations_ while iterating over it */ Vector pending_inputs; - for (NodeOperation *op : m_operations) { + for (NodeOperation *op : operations_) { for (int k = 0; k < op->getNumberOfInputSockets(); ++k) { NodeOperationInput *input = op->getInputSocket(k); if (!input->isConnected()) { @@ -334,7 +334,7 @@ void NodeOperationBuilder::add_operation_input_constants() } } for (NodeOperationInput *input : pending_inputs) { - add_input_constant_value(input, m_input_map.lookup_default(input, nullptr)); + add_input_constant_value(input, input_map_.lookup_default(input, nullptr)); } } @@ -393,7 +393,7 @@ void NodeOperationBuilder::add_input_constant_value(NodeOperationInput *input, void NodeOperationBuilder::resolve_proxies() { Vector proxy_links; - for (const Link &link : m_links) { + for (const Link &link : links_) { /* don't replace links from proxy to proxy, since we may need them for replacing others! */ if (link.from()->getOperation().get_flags().is_proxy_operation && !link.to()->getOperation().get_flags().is_proxy_operation) { @@ -423,16 +423,16 @@ void NodeOperationBuilder::determine_canvases() { /* Determine all canvas areas of the operations. */ const rcti &preferred_area = COM_AREA_NONE; - for (NodeOperation *op : m_operations) { - if (op->isOutputOperation(m_context->isRendering()) && !op->get_flags().is_preview_operation) { + for (NodeOperation *op : operations_) { + if (op->isOutputOperation(context_->isRendering()) && !op->get_flags().is_preview_operation) { rcti canvas = COM_AREA_NONE; op->determine_canvas(preferred_area, canvas); op->set_canvas(canvas); } } - for (NodeOperation *op : m_operations) { - if (op->isOutputOperation(m_context->isRendering()) && op->get_flags().is_preview_operation) { + for (NodeOperation *op : operations_) { + if (op->isOutputOperation(context_->isRendering()) && op->get_flags().is_preview_operation) { rcti canvas = COM_AREA_NONE; op->determine_canvas(preferred_area, canvas); op->set_canvas(canvas); @@ -442,7 +442,7 @@ void NodeOperationBuilder::determine_canvases() /* Convert operation canvases when needed. */ { Vector convert_links; - for (const Link &link : m_links) { + for (const Link &link : links_) { if (link.to()->getResizeMode() != ResizeMode::None) { const rcti &from_canvas = link.from()->getOperation().get_canvas(); const rcti &to_canvas = link.to()->getOperation().get_canvas(); @@ -485,7 +485,7 @@ void NodeOperationBuilder::merge_equal_operations() bool check_for_next_merge = true; while (check_for_next_merge) { /* Re-generate hashes with any change. */ - Vector hashes = generate_hashes(m_operations); + Vector hashes = generate_hashes(operations_); /* Make hashes be consecutive when they are equal. */ std::sort(hashes.begin(), hashes.end()); @@ -507,7 +507,7 @@ void NodeOperationBuilder::merge_equal_operations() void NodeOperationBuilder::merge_equal_operations(NodeOperation *from, NodeOperation *into) { unlink_inputs_and_relink_outputs(from, into); - m_operations.remove_first_occurrence_and_reorder(from); + operations_.remove_first_occurrence_and_reorder(from); delete from; } @@ -515,7 +515,7 @@ Vector NodeOperationBuilder::cache_output_links( NodeOperationOutput *output) const { Vector inputs; - for (const Link &link : m_links) { + for (const Link &link : links_) { if (link.from() == output) { inputs.append(link.to()); } @@ -526,7 +526,7 @@ Vector NodeOperationBuilder::cache_output_links( WriteBufferOperation *NodeOperationBuilder::find_attached_write_buffer_operation( NodeOperationOutput *output) const { - for (const Link &link : m_links) { + for (const Link &link : links_) { if (link.from() == output) { NodeOperation &op = link.to()->getOperation(); if (op.get_flags().is_write_buffer_operation) { @@ -557,7 +557,7 @@ void NodeOperationBuilder::add_input_buffers(NodeOperation * /*operation*/, WriteBufferOperation *writeoperation = find_attached_write_buffer_operation(output); if (!writeoperation) { writeoperation = new WriteBufferOperation(output->getDataType()); - writeoperation->setbNodeTree(m_context->getbNodeTree()); + writeoperation->setbNodeTree(context_->getbNodeTree()); addOperation(writeoperation); addLink(output, writeoperation->getInputSocket(0)); @@ -600,7 +600,7 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation, /* if no write buffer operation exists yet, create a new one */ if (!writeOperation) { writeOperation = new WriteBufferOperation(operation->getOutputSocket()->getDataType()); - writeOperation->setbNodeTree(m_context->getbNodeTree()); + writeOperation->setbNodeTree(context_->getbNodeTree()); addOperation(writeOperation); addLink(output, writeOperation->getInputSocket(0)); @@ -628,10 +628,10 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation, void NodeOperationBuilder::add_complex_operation_buffers() { /* NOTE: complex ops and get cached here first, since adding operations - * will invalidate iterators over the main m_operations + * will invalidate iterators over the main operations_ */ Vector complex_ops; - for (NodeOperation *operation : m_operations) { + for (NodeOperation *operation : operations_) { if (operation->get_flags().complex) { complex_ops.append(operation); } @@ -677,16 +677,16 @@ static void find_reachable_operations_recursive(Tags &reachable, NodeOperation * void NodeOperationBuilder::prune_operations() { Tags reachable; - for (NodeOperation *op : m_operations) { + for (NodeOperation *op : operations_) { /* output operations are primary executed operations */ - if (op->isOutputOperation(m_context->isRendering())) { + if (op->isOutputOperation(context_->isRendering())) { find_reachable_operations_recursive(reachable, op); } } /* delete unreachable operations */ Vector reachable_ops; - for (NodeOperation *op : m_operations) { + for (NodeOperation *op : operations_) { if (reachable.find(op) != reachable.end()) { reachable_ops.append(op); } @@ -695,7 +695,7 @@ void NodeOperationBuilder::prune_operations() } } /* finally replace the operations list with the pruned list */ - m_operations = reachable_ops; + operations_ = reachable_ops; } /* topological (depth-first) sorting of operations */ @@ -721,14 +721,14 @@ static void sort_operations_recursive(Vector &sorted, void NodeOperationBuilder::sort_operations() { Vector sorted; - sorted.reserve(m_operations.size()); + sorted.reserve(operations_.size()); Tags visited; - for (NodeOperation *operation : m_operations) { + for (NodeOperation *operation : operations_) { sort_operations_recursive(sorted, visited, operation); } - m_operations = sorted; + operations_ = sorted; } static void add_group_operations_recursive(Tags &visited, NodeOperation *op, ExecutionGroup *group) @@ -753,8 +753,8 @@ static void add_group_operations_recursive(Tags &visited, NodeOperation *op, Exe ExecutionGroup *NodeOperationBuilder::make_group(NodeOperation *op) { - ExecutionGroup *group = new ExecutionGroup(m_groups.size()); - m_groups.append(group); + ExecutionGroup *group = new ExecutionGroup(groups_.size()); + groups_.append(group); Tags visited; add_group_operations_recursive(visited, op, group); @@ -764,8 +764,8 @@ ExecutionGroup *NodeOperationBuilder::make_group(NodeOperation *op) void NodeOperationBuilder::group_operations() { - for (NodeOperation *op : m_operations) { - if (op->isOutputOperation(m_context->isRendering())) { + for (NodeOperation *op : operations_) { + if (op->isOutputOperation(context_->isRendering())) { ExecutionGroup *group = make_group(op); group->setOutputExecutionGroup(true); } @@ -786,7 +786,7 @@ void NodeOperationBuilder::group_operations() void NodeOperationBuilder::save_graphviz(StringRefNull name) { if (COM_EXPORT_GRAPHVIZ) { - exec_system_->set_operations(m_operations, m_groups); + exec_system_->set_operations(operations_, groups_); DebugInfo::graphviz(exec_system_, name); } } diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h index a6a6201bbb5..21e2f4ce95d 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h @@ -46,45 +46,45 @@ class NodeOperationBuilder { public: class Link { private: - NodeOperationOutput *m_from; - NodeOperationInput *m_to; + NodeOperationOutput *from_; + NodeOperationInput *to_; public: - Link(NodeOperationOutput *from, NodeOperationInput *to) : m_from(from), m_to(to) + Link(NodeOperationOutput *from, NodeOperationInput *to) : from_(from), to_(to) { } NodeOperationOutput *from() const { - return m_from; + return from_; } NodeOperationInput *to() const { - return m_to; + return to_; } }; private: - const CompositorContext *m_context; - NodeGraph m_graph; + const CompositorContext *context_; + NodeGraph graph_; ExecutionSystem *exec_system_; - Vector m_operations; - Vector m_links; - Vector m_groups; + Vector operations_; + Vector links_; + Vector groups_; /** Maps operation inputs to node inputs */ - Map m_input_map; + Map input_map_; /** Maps node outputs to operation outputs */ - Map m_output_map; + Map output_map_; - Node *m_current_node; + Node *current_node_; /** Operation that will be writing to the viewer image * Only one operation can occupy this place at a time, * to avoid race conditions */ - ViewerOperation *m_active_viewer; + ViewerOperation *active_viewer_; public: NodeOperationBuilder(const CompositorContext *context, @@ -93,7 +93,7 @@ class NodeOperationBuilder { const CompositorContext &context() const { - return *m_context; + return *context_; } void convertToOperations(ExecutionSystem *system); @@ -120,17 +120,17 @@ class NodeOperationBuilder { /** The currently active viewer output operation */ ViewerOperation *active_viewer() const { - return m_active_viewer; + return active_viewer_; } const Vector &get_operations() const { - return m_operations; + return operations_; } const Vector &get_links() const { - return m_links; + return links_; } protected: diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cc b/source/blender/compositor/intern/COM_OpenCLDevice.cc index 0b52d92e92d..af55890cba7 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.cc +++ b/source/blender/compositor/intern/COM_OpenCLDevice.cc @@ -42,30 +42,30 @@ OpenCLDevice::OpenCLDevice(cl_context context, cl_program program, cl_int vendorId) { - m_device = device; - m_context = context; - m_program = program; - m_queue = nullptr; - m_vendorID = vendorId; + device_ = device; + context_ = context; + program_ = program; + queue_ = nullptr; + vendorID_ = vendorId; cl_int error; - m_queue = clCreateCommandQueue(m_context, m_device, 0, &error); + queue_ = clCreateCommandQueue(context_, device_, 0, &error); } OpenCLDevice::OpenCLDevice(OpenCLDevice &&other) noexcept - : m_context(other.m_context), - m_device(other.m_device), - m_program(other.m_program), - m_queue(other.m_queue), - m_vendorID(other.m_vendorID) + : context_(other.context_), + device_(other.device_), + program_(other.program_), + queue_(other.queue_), + vendorID_(other.vendorID_) { - other.m_queue = nullptr; + other.queue_ = nullptr; } OpenCLDevice::~OpenCLDevice() { - if (m_queue) { - clReleaseCommandQueue(m_queue); + if (queue_) { + clReleaseCommandQueue(queue_); } } @@ -131,7 +131,7 @@ cl_mem OpenCLDevice::COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, const cl_image_format *imageFormat = determineImageFormat(result); - cl_mem clBuffer = clCreateImage2D(m_context, + cl_mem clBuffer = clCreateImage2D(context_, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, imageFormat, result->getWidth(), @@ -206,7 +206,7 @@ void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, MemoryBuffer *outputMemo (size_t)outputMemoryBuffer->getHeight(), }; - error = clEnqueueNDRangeKernel(m_queue, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); + error = clEnqueueNDRangeKernel(queue_, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } @@ -226,7 +226,7 @@ void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, size_t size[2]; cl_int2 offset; - if (m_vendorID == NVIDIA) { + if (vendorID_ == NVIDIA) { localSize = 32; } @@ -254,11 +254,11 @@ void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } error = clEnqueueNDRangeKernel( - m_queue, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); + queue_, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - clFlush(m_queue); + clFlush(queue_); if (operation->isBraked()) { breaked = false; } @@ -270,7 +270,7 @@ cl_kernel OpenCLDevice::COM_clCreateKernel(const char *kernelname, std::list *clKernelsToCleanUp) { cl_int error; - cl_kernel kernel = clCreateKernel(m_program, kernelname, &error); + cl_kernel kernel = clCreateKernel(program_, kernelname, &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.h b/source/blender/compositor/intern/COM_OpenCLDevice.h index 9c72fa31d95..ca5a95f4a48 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.h +++ b/source/blender/compositor/intern/COM_OpenCLDevice.h @@ -43,27 +43,27 @@ class OpenCLDevice : public Device { /** * \brief opencl context */ - cl_context m_context; + cl_context context_; /** * \brief opencl device */ - cl_device_id m_device; + cl_device_id device_; /** * \brief opencl program */ - cl_program m_program; + cl_program program_; /** * \brief opencl command queue */ - cl_command_queue m_queue; + cl_command_queue queue_; /** * \brief opencl vendor ID */ - cl_int m_vendorID; + cl_int vendorID_; public: /** @@ -93,12 +93,12 @@ class OpenCLDevice : public Device { cl_context getContext() { - return m_context; + return context_; } cl_command_queue getQueue() { - return m_queue; + return queue_; } cl_mem COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc index 7d7ae0ab155..83e3f033b32 100644 --- a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc +++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc @@ -22,7 +22,7 @@ namespace blender::compositor { SingleThreadedOperation::SingleThreadedOperation() { - m_cachedInstance = nullptr; + cachedInstance_ = nullptr; flags.complex = true; flags.single_threaded = true; } @@ -34,30 +34,30 @@ void SingleThreadedOperation::initExecution() void SingleThreadedOperation::executePixel(float output[4], int x, int y, void * /*data*/) { - m_cachedInstance->readNoCheck(output, x, y); + cachedInstance_->readNoCheck(output, x, y); } void SingleThreadedOperation::deinitExecution() { deinitMutex(); - if (m_cachedInstance) { - delete m_cachedInstance; - m_cachedInstance = nullptr; + if (cachedInstance_) { + delete cachedInstance_; + cachedInstance_ = nullptr; } } void *SingleThreadedOperation::initializeTileData(rcti *rect) { - if (m_cachedInstance) { - return m_cachedInstance; + if (cachedInstance_) { + return cachedInstance_; } lockMutex(); - if (m_cachedInstance == nullptr) { + if (cachedInstance_ == nullptr) { // - m_cachedInstance = createMemoryBuffer(rect); + cachedInstance_ = createMemoryBuffer(rect); } unlockMutex(); - return m_cachedInstance; + return cachedInstance_; } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.h b/source/blender/compositor/intern/COM_SingleThreadedOperation.h index ac81b495d6f..3f90ce96e00 100644 --- a/source/blender/compositor/intern/COM_SingleThreadedOperation.h +++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.h @@ -24,12 +24,12 @@ namespace blender::compositor { class SingleThreadedOperation : public NodeOperation { private: - MemoryBuffer *m_cachedInstance; + MemoryBuffer *cachedInstance_; protected: inline bool isCached() { - return m_cachedInstance != nullptr; + return cachedInstance_ != nullptr; } public: diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.cc b/source/blender/compositor/nodes/COM_DilateErodeNode.cc index dbd03b02b8e..ea7ce396189 100644 --- a/source/blender/compositor/nodes/COM_DilateErodeNode.cc +++ b/source/blender/compositor/nodes/COM_DilateErodeNode.cc @@ -27,7 +27,7 @@ namespace blender::compositor { DilateErodeNode::DilateErodeNode(bNode *editorNode) : Node(editorNode) { /* initialize node data */ - NodeBlurData *data = &m_alpha_blur; + NodeBlurData *data = &alpha_blur_; memset(data, 0, sizeof(NodeBlurData)); data->filtertype = R_FILTER_GAUSS; @@ -86,7 +86,7 @@ void DilateErodeNode::convertToOperations(NodeConverter &converter, eCompositorQuality quality = context.getQuality(); GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation(); - operationx->setData(&m_alpha_blur); + operationx->setData(&alpha_blur_); operationx->setQuality(quality); operationx->setFalloff(PROP_SMOOTH); converter.addOperation(operationx); @@ -96,7 +96,7 @@ void DilateErodeNode::convertToOperations(NodeConverter &converter, // yet GaussianAlphaYBlurOperation *operationy = new GaussianAlphaYBlurOperation(); - operationy->setData(&m_alpha_blur); + operationy->setData(&alpha_blur_); operationy->setQuality(quality); operationy->setFalloff(PROP_SMOOTH); converter.addOperation(operationy); diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.h b/source/blender/compositor/nodes/COM_DilateErodeNode.h index 7684d7e3834..2a635a8b0a6 100644 --- a/source/blender/compositor/nodes/COM_DilateErodeNode.h +++ b/source/blender/compositor/nodes/COM_DilateErodeNode.h @@ -28,7 +28,7 @@ namespace blender::compositor { */ class DilateErodeNode : public Node { /** only used for blurring alpha, since the dilate/erode node doesn't have this. */ - NodeBlurData m_alpha_blur; + NodeBlurData alpha_blur_; public: DilateErodeNode(bNode *editorNode); diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.cc b/source/blender/compositor/nodes/COM_SocketProxyNode.cc index 4bd3eb0b058..1109dbaf735 100644 --- a/source/blender/compositor/nodes/COM_SocketProxyNode.cc +++ b/source/blender/compositor/nodes/COM_SocketProxyNode.cc @@ -26,7 +26,7 @@ SocketProxyNode::SocketProxyNode(bNode *editorNode, bNodeSocket *editorInput, bNodeSocket *editorOutput, bool use_conversion) - : Node(editorNode, false), m_use_conversion(use_conversion) + : Node(editorNode, false), use_conversion_(use_conversion) { DataType dt; @@ -52,7 +52,7 @@ SocketProxyNode::SocketProxyNode(bNode *editorNode, void SocketProxyNode::convertToOperations(NodeConverter &converter, const CompositorContext & /*context*/) const { - NodeOperationOutput *proxy_output = converter.addInputProxy(getInputSocket(0), m_use_conversion); + NodeOperationOutput *proxy_output = converter.addInputProxy(getInputSocket(0), use_conversion_); converter.mapOutputSocket(getOutputSocket(), proxy_output); } diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.h b/source/blender/compositor/nodes/COM_SocketProxyNode.h index d19fb802767..2b584b344f0 100644 --- a/source/blender/compositor/nodes/COM_SocketProxyNode.h +++ b/source/blender/compositor/nodes/COM_SocketProxyNode.h @@ -37,16 +37,16 @@ class SocketProxyNode : public Node { bool getUseConversion() const { - return m_use_conversion; + return use_conversion_; } void setUseConversion(bool use_conversion) { - m_use_conversion = use_conversion; + use_conversion_ = use_conversion; } private: /** If true, the proxy will convert input and output data to/from the proxy socket types. */ - bool m_use_conversion; + bool use_conversion_; }; class SocketBufferNode : public Node { diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc index 5e056b3f52f..2e494d1adf3 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc @@ -34,9 +34,9 @@ void AlphaOverKeyOperation::executePixelSampled(float output[4], float inputOverColor[4]; float value[4]; - m_inputValueOperation->readSampled(value, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); + inputValueOperation_->readSampled(value, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputOverColor, x, y, sampler); if (inputOverColor[3] <= 0.0f) { copy_v4_v4(output, inputColor1); diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc index 61fab7b3bc8..1df74ffac8e 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc @@ -22,7 +22,7 @@ namespace blender::compositor { AlphaOverMixedOperation::AlphaOverMixedOperation() { - m_x = 0.0f; + x_ = 0.0f; this->flags.can_be_constant = true; } @@ -35,9 +35,9 @@ void AlphaOverMixedOperation::executePixelSampled(float output[4], float inputOverColor[4]; float value[4]; - m_inputValueOperation->readSampled(value, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); + inputValueOperation_->readSampled(value, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputOverColor, x, y, sampler); if (inputOverColor[3] <= 0.0f) { copy_v4_v4(output, inputColor1); @@ -46,7 +46,7 @@ void AlphaOverMixedOperation::executePixelSampled(float output[4], copy_v4_v4(output, inputOverColor); } else { - float addfac = 1.0f - m_x + inputOverColor[3] * m_x; + float addfac = 1.0f - x_ + inputOverColor[3] * x_; float premul = value[0] * addfac; float mul = 1.0f - value[0] * inputOverColor[3]; @@ -71,7 +71,7 @@ void AlphaOverMixedOperation::update_memory_buffer_row(PixelCursor &p) copy_v4_v4(p.out, over_color); } else { - const float addfac = 1.0f - m_x + over_color[3] * m_x; + const float addfac = 1.0f - x_ + over_color[3] * x_; const float premul = value * addfac; const float mul = 1.0f - value * over_color[3]; diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h index 319176c8c9f..65967c03eb9 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h @@ -28,7 +28,7 @@ namespace blender::compositor { */ class AlphaOverMixedOperation : public MixBaseOperation { private: - float m_x; + float x_; public: /** @@ -43,7 +43,7 @@ class AlphaOverMixedOperation : public MixBaseOperation { void setX(float x) { - m_x = x; + x_ = x; } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc index 47383706ba2..5af992c8809 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc @@ -34,9 +34,9 @@ void AlphaOverPremultiplyOperation::executePixelSampled(float output[4], float inputOverColor[4]; float value[4]; - m_inputValueOperation->readSampled(value, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); + inputValueOperation_->readSampled(value, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputOverColor, x, y, sampler); /* Zero alpha values should still permit an add of RGB data */ if (inputOverColor[3] < 0.0f) { diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.cc b/source/blender/compositor/operations/COM_AntiAliasOperation.cc index 50e90111604..8a3b6f4df11 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.cc +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.cc @@ -112,13 +112,13 @@ AntiAliasOperation::AntiAliasOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_valueReader = nullptr; + valueReader_ = nullptr; this->flags.complex = true; } void AntiAliasOperation::initExecution() { - m_valueReader = this->getInputSocketReader(0); + valueReader_ = this->getInputSocketReader(0); } void AntiAliasOperation::executePixel(float output[4], int x, int y, void *data) @@ -175,7 +175,7 @@ void AntiAliasOperation::executePixel(float output[4], int x, int y, void *data) void AntiAliasOperation::deinitExecution() { - m_valueReader = nullptr; + valueReader_ = nullptr; } bool AntiAliasOperation::determineDependingAreaOfInterest(rcti *input, diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.h b/source/blender/compositor/operations/COM_AntiAliasOperation.h index b5048248425..48ae03a1aaa 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.h +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.h @@ -33,7 +33,7 @@ class AntiAliasOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *m_valueReader; + SocketReader *valueReader_; public: AntiAliasOperation(); diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc index 4baaa416054..7368dcf0cad 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc @@ -27,14 +27,14 @@ BilateralBlurOperation::BilateralBlurOperation() this->addOutputSocket(DataType::Color); this->flags.complex = true; - m_inputColorProgram = nullptr; - m_inputDeterminatorProgram = nullptr; + inputColorProgram_ = nullptr; + inputDeterminatorProgram_ = nullptr; } void BilateralBlurOperation::initExecution() { - m_inputColorProgram = getInputSocketReader(0); - m_inputDeterminatorProgram = getInputSocketReader(1); + inputColorProgram_ = getInputSocketReader(0); + inputDeterminatorProgram_ = getInputSocketReader(1); QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -47,14 +47,14 @@ void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *d float tempColor[4]; float blurColor[4]; float blurDivider; - float space = m_space; - float sigmacolor = m_data->sigma_color; + float space = space_; + float sigmacolor = data_->sigma_color; int minx = floor(x - space); int maxx = ceil(x + space); int miny = floor(y - space); int maxy = ceil(y + space); float deltaColor; - m_inputDeterminatorProgram->read(determinatorReferenceColor, x, y, data); + inputDeterminatorProgram_->read(determinatorReferenceColor, x, y, data); zero_v4(blurColor); blurDivider = 0.0f; @@ -65,14 +65,14 @@ void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *d for (int yi = miny; yi < maxy; yi += QualityStepHelper::getStep()) { for (int xi = minx; xi < maxx; xi += QualityStepHelper::getStep()) { /* Read determinator. */ - m_inputDeterminatorProgram->read(determinator, xi, yi, data); + inputDeterminatorProgram_->read(determinator, xi, yi, data); deltaColor = (fabsf(determinatorReferenceColor[0] - determinator[0]) + fabsf(determinatorReferenceColor[1] - determinator[1]) + /* Do not take the alpha channel into account. */ fabsf(determinatorReferenceColor[2] - determinator[2])); if (deltaColor < sigmacolor) { /* Add this to the blur. */ - m_inputColorProgram->read(tempColor, xi, yi, data); + inputColorProgram_->read(tempColor, xi, yi, data); add_v4_v4(blurColor, tempColor); blurDivider += 1.0f; } @@ -92,8 +92,8 @@ void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *d void BilateralBlurOperation::deinitExecution() { - m_inputColorProgram = nullptr; - m_inputDeterminatorProgram = nullptr; + inputColorProgram_ = nullptr; + inputDeterminatorProgram_ = nullptr; } bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input, @@ -101,7 +101,7 @@ bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input, rcti *output) { rcti newInput; - int add = ceil(m_space) + 1; + int add = ceil(space_) + 1; newInput.xmax = input->xmax + (add); newInput.xmin = input->xmin - (add); @@ -115,7 +115,7 @@ void BilateralBlurOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &output_area, rcti &r_input_area) { - const int add = ceil(m_space) + 1; + const int add = ceil(space_) + 1; r_input_area.xmax = output_area.xmax + (add); r_input_area.xmin = output_area.xmin - (add); @@ -174,10 +174,10 @@ void BilateralBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, { PixelCursor p = {}; p.step = QualityStepHelper::getStep(); - p.sigma_color = m_data->sigma_color; + p.sigma_color = data_->sigma_color; p.input_color = inputs[0]; p.input_determinator = inputs[1]; - const float space = m_space; + const float space = space_; for (int y = area.ymin; y < area.ymax; y++) { p.out = output->get_elem(area.xmin, y); /* This will be used as the reference color for the determinator. */ diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h index a1884ddf1ab..aa94df1e254 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h @@ -25,10 +25,10 @@ namespace blender::compositor { class BilateralBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: - SocketReader *m_inputColorProgram; - SocketReader *m_inputDeterminatorProgram; - NodeBilateralBlurData *m_data; - float m_space; + SocketReader *inputColorProgram_; + SocketReader *inputDeterminatorProgram_; + NodeBilateralBlurData *data_; + float space_; public: BilateralBlurOperation(); @@ -54,8 +54,8 @@ class BilateralBlurOperation : public MultiThreadedOperation, public QualityStep void setData(NodeBilateralBlurData *data) { - m_data = data; - m_space = data->sigma_space + data->iter; + data_ = data; + space_ = data->sigma_space + data->iter; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc index f264603c8ba..3c43a16599b 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc @@ -30,11 +30,11 @@ BlurBaseOperation::BlurBaseOperation(DataType data_type) this->addInputSocket(DataType::Value); this->addOutputSocket(data_type); this->flags.complex = true; - m_inputProgram = nullptr; - memset(&m_data, 0, sizeof(NodeBlurData)); - m_size = 1.0f; - m_sizeavailable = false; - m_extend_bounds = false; + inputProgram_ = nullptr; + memset(&data_, 0, sizeof(NodeBlurData)); + size_ = 1.0f; + sizeavailable_ = false; + extend_bounds_ = false; use_variable_size_ = false; } @@ -44,32 +44,32 @@ void BlurBaseOperation::init_data() updateSize(); } - m_data.image_in_width = this->getWidth(); - m_data.image_in_height = this->getHeight(); - if (m_data.relative) { + data_.image_in_width = this->getWidth(); + data_.image_in_height = this->getHeight(); + if (data_.relative) { int sizex, sizey; - switch (m_data.aspect) { + switch (data_.aspect) { case CMP_NODE_BLUR_ASPECT_Y: - sizex = sizey = m_data.image_in_width; + sizex = sizey = data_.image_in_width; break; case CMP_NODE_BLUR_ASPECT_X: - sizex = sizey = m_data.image_in_height; + sizex = sizey = data_.image_in_height; break; default: - BLI_assert(m_data.aspect == CMP_NODE_BLUR_ASPECT_NONE); - sizex = m_data.image_in_width; - sizey = m_data.image_in_height; + BLI_assert(data_.aspect == CMP_NODE_BLUR_ASPECT_NONE); + sizex = data_.image_in_width; + sizey = data_.image_in_height; break; } - m_data.sizex = round_fl_to_int(m_data.percentx * 0.01f * sizex); - m_data.sizey = round_fl_to_int(m_data.percenty * 0.01f * sizey); + data_.sizex = round_fl_to_int(data_.percentx * 0.01f * sizex); + data_.sizey = round_fl_to_int(data_.percenty * 0.01f * sizey); } } void BlurBaseOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); - m_inputSize = this->getInputSocketReader(1); + inputProgram_ = this->getInputSocketReader(0); + inputSize_ = this->getInputSocketReader(1); QualityStepHelper::initExecution(COM_QH_MULTIPLY); } @@ -86,7 +86,7 @@ float *BlurBaseOperation::make_gausstab(float rad, int size) sum = 0.0f; float fac = (rad > 0.0f ? 1.0f / rad : 0.0f); for (i = -size; i <= size; i++) { - val = RE_filter_value(m_data.filtertype, (float)i * fac); + val = RE_filter_value(data_.filtertype, (float)i * fac); sum += val; gausstab[i + size] = val; } @@ -165,29 +165,29 @@ float *BlurBaseOperation::make_dist_fac_inverse(float rad, int size, int falloff void BlurBaseOperation::deinitExecution() { - m_inputProgram = nullptr; - m_inputSize = nullptr; + inputProgram_ = nullptr; + inputSize_ = nullptr; } void BlurBaseOperation::setData(const NodeBlurData *data) { - memcpy(&m_data, data, sizeof(NodeBlurData)); + memcpy(&data_, data, sizeof(NodeBlurData)); } int BlurBaseOperation::get_blur_size(eDimension dim) const { switch (dim) { case eDimension::X: - return m_data.sizex; + return data_.sizex; case eDimension::Y: - return m_data.sizey; + return data_.sizey; } return -1; } void BlurBaseOperation::updateSize() { - if (m_sizeavailable || use_variable_size_) { + if (sizeavailable_ || use_variable_size_) { return; } @@ -195,23 +195,23 @@ void BlurBaseOperation::updateSize() case eExecutionModel::Tiled: { float result[4]; this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest); - m_size = result[0]; + size_ = result[0]; break; } case eExecutionModel::FullFrame: { NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX); if (size_input->get_flags().is_constant_operation) { - m_size = *static_cast(size_input)->get_constant_elem(); + size_ = *static_cast(size_input)->get_constant_elem(); } /* Else use default. */ break; } } - m_sizeavailable = true; + sizeavailable_ = true; } void BlurBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (!m_extend_bounds) { + if (!extend_bounds_) { NodeOperation::determine_canvas(preferred_area, r_area); return; } @@ -219,8 +219,8 @@ void BlurBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_are switch (execution_model_) { case eExecutionModel::Tiled: { NodeOperation::determine_canvas(preferred_area, r_area); - r_area.xmax += 2 * m_size * m_data.sizex; - r_area.ymax += 2 * m_size * m_data.sizey; + r_area.xmax += 2 * size_ * data_.sizex; + r_area.ymax += 2 * size_ * data_.sizey; break; } case eExecutionModel::FullFrame: { @@ -229,8 +229,8 @@ void BlurBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_are * operations. */ set_determined_canvas_modifier([=](rcti &canvas) { /* Rounding to even prevents jiggling in backdrop while switching size values. */ - canvas.xmax += round_to_even(2 * m_size * m_data.sizex); - canvas.ymax += round_to_even(2 * m_size * m_data.sizey); + canvas.xmax += round_to_even(2 * size_ * data_.sizex); + canvas.ymax += round_to_even(2 * size_ * data_.sizey); }); NodeOperation::determine_canvas(preferred_area, r_area); break; diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h index d8fcd0f8364..b1dad2e868f 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h @@ -29,7 +29,7 @@ namespace blender::compositor { class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelper { private: - bool m_extend_bounds; + bool extend_bounds_; protected: static constexpr int IMAGE_INPUT_INDEX = 0; @@ -48,12 +48,12 @@ class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelpe /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; - SocketReader *m_inputSize; - NodeBlurData m_data; + SocketReader *inputProgram_; + SocketReader *inputSize_; + NodeBlurData data_; - float m_size; - bool m_sizeavailable; + float size_; + bool sizeavailable_; /* Flags for inheriting classes. */ bool use_variable_size_; @@ -74,13 +74,13 @@ class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelpe void setSize(float size) { - m_size = size; - m_sizeavailable = true; + size_ = size; + sizeavailable_ = true; } void setExtendBounds(bool extend_bounds) { - m_extend_bounds = extend_bounds; + extend_bounds_ = extend_bounds; } int get_blur_size(eDimension dim) const; diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc index a9d1275805a..d00a18baf7b 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc @@ -39,13 +39,13 @@ BokehBlurOperation::BokehBlurOperation() flags.complex = true; flags.open_cl = true; - m_size = 1.0f; - m_sizeavailable = false; - m_inputProgram = nullptr; - m_inputBokehProgram = nullptr; - m_inputBoundingBoxReader = nullptr; + size_ = 1.0f; + sizeavailable_ = false; + inputProgram_ = nullptr; + inputBokehProgram_ = nullptr; + inputBoundingBoxReader_ = nullptr; - m_extend_bounds = false; + extend_bounds_ = false; } void BokehBlurOperation::init_data() @@ -60,15 +60,15 @@ void BokehBlurOperation::init_data() const float dimension = MIN2(width, height); - m_bokehMidX = width / 2.0f; - m_bokehMidY = height / 2.0f; - m_bokehDimension = dimension / 2.0f; + bokehMidX_ = width / 2.0f; + bokehMidY_ = height / 2.0f; + bokehDimension_ = dimension / 2.0f; } void *BokehBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!m_sizeavailable) { + if (!sizeavailable_) { updateSize(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -80,9 +80,9 @@ void BokehBlurOperation::initExecution() { initMutex(); - m_inputProgram = getInputSocketReader(0); - m_inputBokehProgram = getInputSocketReader(1); - m_inputBoundingBoxReader = getInputSocketReader(2); + inputProgram_ = getInputSocketReader(0); + inputBokehProgram_ = getInputSocketReader(1); + inputBoundingBoxReader_ = getInputSocketReader(2); QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -93,7 +93,7 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) float tempBoundingBox[4]; float bokeh[4]; - m_inputBoundingBoxReader->readSampled(tempBoundingBox, x, y, PixelSampler::Nearest); + inputBoundingBoxReader_->readSampled(tempBoundingBox, x, y, PixelSampler::Nearest); if (tempBoundingBox[0] > 0.0f) { float multiplier_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; @@ -103,11 +103,11 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; const float max_dim = MAX2(this->getWidth(), this->getHeight()); - int pixelSize = m_size * max_dim / 100.0f; + int pixelSize = size_ * max_dim / 100.0f; zero_v4(color_accum); if (pixelSize < 2) { - m_inputProgram->readSampled(color_accum, x, y, PixelSampler::Nearest); + inputProgram_->readSampled(color_accum, x, y, PixelSampler::Nearest); multiplier_accum[0] = 1.0f; multiplier_accum[1] = 1.0f; multiplier_accum[2] = 1.0f; @@ -125,14 +125,14 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) int step = getStep(); int offsetadd = getOffsetAdd() * COM_DATA_TYPE_COLOR_CHANNELS; - float m = m_bokehDimension / pixelSize; + float m = bokehDimension_ / pixelSize; for (int ny = miny; ny < maxy; ny += step) { int bufferindex = ((minx - bufferstartx) * COM_DATA_TYPE_COLOR_CHANNELS) + ((ny - bufferstarty) * COM_DATA_TYPE_COLOR_CHANNELS * bufferwidth); for (int nx = minx; nx < maxx; nx += step) { - float u = m_bokehMidX - (nx - x) * m; - float v = m_bokehMidY - (ny - y) * m; - m_inputBokehProgram->readSampled(bokeh, u, v, PixelSampler::Nearest); + float u = bokehMidX_ - (nx - x) * m; + float v = bokehMidY_ - (ny - y) * m; + inputBokehProgram_->readSampled(bokeh, u, v, PixelSampler::Nearest); madd_v4_v4v4(color_accum, bokeh, &buffer[bufferindex]); add_v4_v4(multiplier_accum, bokeh); bufferindex += offsetadd; @@ -144,16 +144,16 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) output[3] = color_accum[3] * (1.0f / multiplier_accum[3]); } else { - m_inputProgram->readSampled(output, x, y, PixelSampler::Nearest); + inputProgram_->readSampled(output, x, y, PixelSampler::Nearest); } } void BokehBlurOperation::deinitExecution() { deinitMutex(); - m_inputProgram = nullptr; - m_inputBokehProgram = nullptr; - m_inputBoundingBoxReader = nullptr; + inputProgram_ = nullptr; + inputBokehProgram_ = nullptr; + inputBoundingBoxReader_ = nullptr; } bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, @@ -164,11 +164,11 @@ bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, rcti bokehInput; const float max_dim = MAX2(this->getWidth(), this->getHeight()); - if (m_sizeavailable) { - newInput.xmax = input->xmax + (m_size * max_dim / 100.0f); - newInput.xmin = input->xmin - (m_size * max_dim / 100.0f); - newInput.ymax = input->ymax + (m_size * max_dim / 100.0f); - newInput.ymin = input->ymin - (m_size * max_dim / 100.0f); + if (sizeavailable_) { + newInput.xmax = input->xmax + (size_ * max_dim / 100.0f); + newInput.xmin = input->xmin - (size_ * max_dim / 100.0f); + newInput.ymax = input->ymax + (size_ * max_dim / 100.0f); + newInput.ymin = input->ymin - (size_ * max_dim / 100.0f); } else { newInput.xmax = input->xmax + (10.0f * max_dim / 100.0f); @@ -193,7 +193,7 @@ bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { return true; } - if (!m_sizeavailable) { + if (!sizeavailable_) { rcti sizeInput; sizeInput.xmin = 0; sizeInput.ymin = 0; @@ -215,19 +215,19 @@ void BokehBlurOperation::executeOpenCL(OpenCLDevice *device, std::list * /*clKernelsToCleanUp*/) { cl_kernel kernel = device->COM_clCreateKernel("bokehBlurKernel", nullptr); - if (!m_sizeavailable) { + if (!sizeavailable_) { updateSize(); } const float max_dim = MAX2(this->getWidth(), this->getHeight()); - cl_int radius = m_size * max_dim / 100.0f; + cl_int radius = size_ * max_dim / 100.0f; cl_int step = this->getStep(); device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, m_inputBoundingBoxReader); + kernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, inputBoundingBoxReader_); device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 1, 4, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); + kernel, 1, 4, clMemToCleanUp, inputMemoryBuffers, inputProgram_); device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 2, -1, clMemToCleanUp, inputMemoryBuffers, m_inputBokehProgram); + kernel, 2, -1, clMemToCleanUp, inputMemoryBuffers, inputBokehProgram_); device->COM_clAttachOutputMemoryBufferToKernelParameter(kernel, 3, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter(kernel, 5, outputMemoryBuffer); clSetKernelArg(kernel, 6, sizeof(cl_int), &radius); @@ -239,7 +239,7 @@ void BokehBlurOperation::executeOpenCL(OpenCLDevice *device, void BokehBlurOperation::updateSize() { - if (m_sizeavailable) { + if (sizeavailable_) { return; } @@ -247,25 +247,25 @@ void BokehBlurOperation::updateSize() case eExecutionModel::Tiled: { float result[4]; this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest); - m_size = result[0]; - CLAMP(m_size, 0.0f, 10.0f); + size_ = result[0]; + CLAMP(size_, 0.0f, 10.0f); break; } case eExecutionModel::FullFrame: { NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX); if (size_input->get_flags().is_constant_operation) { - m_size = *static_cast(size_input)->get_constant_elem(); - CLAMP(m_size, 0.0f, 10.0f); + size_ = *static_cast(size_input)->get_constant_elem(); + CLAMP(size_, 0.0f, 10.0f); } /* Else use default. */ break; } } - m_sizeavailable = true; + sizeavailable_ = true; } void BokehBlurOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (!m_extend_bounds) { + if (!extend_bounds_) { NodeOperation::determine_canvas(preferred_area, r_area); return; } @@ -274,15 +274,15 @@ void BokehBlurOperation::determine_canvas(const rcti &preferred_area, rcti &r_ar case eExecutionModel::Tiled: { NodeOperation::determine_canvas(preferred_area, r_area); const float max_dim = MAX2(BLI_rcti_size_x(&r_area), BLI_rcti_size_y(&r_area)); - r_area.xmax += 2 * m_size * max_dim / 100.0f; - r_area.ymax += 2 * m_size * max_dim / 100.0f; + r_area.xmax += 2 * size_ * max_dim / 100.0f; + r_area.ymax += 2 * size_ * max_dim / 100.0f; break; } case eExecutionModel::FullFrame: { set_determined_canvas_modifier([=](rcti &canvas) { const float max_dim = MAX2(BLI_rcti_size_x(&canvas), BLI_rcti_size_y(&canvas)); /* Rounding to even prevents image jiggling in backdrop while switching size values. */ - float add_size = round_to_even(2 * m_size * max_dim / 100.0f); + float add_size = round_to_even(2 * size_ * max_dim / 100.0f); canvas.xmax += add_size; canvas.ymax += add_size; }); @@ -299,7 +299,7 @@ void BokehBlurOperation::get_area_of_interest(const int input_idx, switch (input_idx) { case IMAGE_INPUT_INDEX: { const float max_dim = MAX2(this->getWidth(), this->getHeight()); - const float add_size = m_size * max_dim / 100.0f; + const float add_size = size_ * max_dim / 100.0f; r_input_area.xmin = output_area.xmin - add_size; r_input_area.xmax = output_area.xmax + add_size; r_input_area.ymin = output_area.ymin - add_size; @@ -326,8 +326,8 @@ void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { const float max_dim = MAX2(this->getWidth(), this->getHeight()); - const int pixel_size = m_size * max_dim / 100.0f; - const float m = m_bokehDimension / pixel_size; + const int pixel_size = size_ * max_dim / 100.0f; + const float m = bokehDimension_ / pixel_size; const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX]; const MemoryBuffer *bokeh_input = inputs[BOKEH_INPUT_INDEX]; @@ -362,9 +362,9 @@ void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, const float *row_color = image_input->get_elem(minx, miny); for (int ny = miny; ny < maxy; ny += step, row_color += row_stride) { const float *color = row_color; - const float v = m_bokehMidY - (ny - y) * m; + const float v = bokehMidY_ - (ny - y) * m; for (int nx = minx; nx < maxx; nx += step, color += elem_stride) { - const float u = m_bokehMidX - (nx - x) * m; + const float u = bokehMidX_ - (nx - x) * m; float bokeh[4]; bokeh_input->read_elem_checked(u, v, bokeh); madd_v4_v4v4(color_accum, bokeh, color); diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.h b/source/blender/compositor/operations/COM_BokehBlurOperation.h index 7b0f0710a00..0b70b7a714a 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.h @@ -25,17 +25,17 @@ namespace blender::compositor { class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: - SocketReader *m_inputProgram; - SocketReader *m_inputBokehProgram; - SocketReader *m_inputBoundingBoxReader; + SocketReader *inputProgram_; + SocketReader *inputBokehProgram_; + SocketReader *inputBoundingBoxReader_; void updateSize(); - float m_size; - bool m_sizeavailable; + float size_; + bool sizeavailable_; - float m_bokehMidX; - float m_bokehMidY; - float m_bokehDimension; - bool m_extend_bounds; + float bokehMidX_; + float bokehMidY_; + float bokehDimension_; + bool extend_bounds_; public: BokehBlurOperation(); @@ -64,8 +64,8 @@ class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelp void setSize(float size) { - m_size = size; - m_sizeavailable = true; + size_ = size; + sizeavailable_ = true; } void executeOpenCL(OpenCLDevice *device, @@ -77,7 +77,7 @@ class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelp void setExtendBounds(bool extend_bounds) { - m_extend_bounds = extend_bounds; + extend_bounds_ = extend_bounds; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc index fb2799e378b..5389fa633b0 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.cc +++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc @@ -23,33 +23,33 @@ namespace blender::compositor { BokehImageOperation::BokehImageOperation() { this->addOutputSocket(DataType::Color); - m_deleteData = false; + deleteData_ = false; } void BokehImageOperation::initExecution() { - m_center[0] = getWidth() / 2; - m_center[1] = getHeight() / 2; - m_inverseRounding = 1.0f - m_data->rounding; - m_circularDistance = getWidth() / 2; - m_flapRad = (float)(M_PI * 2) / m_data->flaps; - m_flapRadAdd = m_data->angle; - while (m_flapRadAdd < 0.0f) { - m_flapRadAdd += (float)(M_PI * 2.0); + center_[0] = getWidth() / 2; + center_[1] = getHeight() / 2; + inverseRounding_ = 1.0f - data_->rounding; + circularDistance_ = getWidth() / 2; + flapRad_ = (float)(M_PI * 2) / data_->flaps; + flapRadAdd_ = data_->angle; + while (flapRadAdd_ < 0.0f) { + flapRadAdd_ += (float)(M_PI * 2.0); } - while (m_flapRadAdd > (float)M_PI) { - m_flapRadAdd -= (float)(M_PI * 2.0); + while (flapRadAdd_ > (float)M_PI) { + flapRadAdd_ -= (float)(M_PI * 2.0); } } void BokehImageOperation::detemineStartPointOfFlap(float r[2], int flapNumber, float distance) { - r[0] = sinf(m_flapRad * flapNumber + m_flapRadAdd) * distance + m_center[0]; - r[1] = cosf(m_flapRad * flapNumber + m_flapRadAdd) * distance + m_center[1]; + r[0] = sinf(flapRad_ * flapNumber + flapRadAdd_) * distance + center_[0]; + r[1] = cosf(flapRad_ * flapNumber + flapRadAdd_) * distance + center_[1]; } float BokehImageOperation::isInsideBokeh(float distance, float x, float y) { float insideBokeh = 0.0f; - const float deltaX = x - m_center[0]; - const float deltaY = y - m_center[1]; + const float deltaX = x - center_[0]; + const float deltaY = y - center_[1]; float closestPoint[2]; float lineP1[2]; float lineP2[2]; @@ -57,25 +57,25 @@ float BokehImageOperation::isInsideBokeh(float distance, float x, float y) point[0] = x; point[1] = y; - const float distanceToCenter = len_v2v2(point, m_center); + const float distanceToCenter = len_v2v2(point, center_); const float bearing = (atan2f(deltaX, deltaY) + (float)(M_PI * 2.0)); - int flapNumber = (int)((bearing - m_flapRadAdd) / m_flapRad); + int flapNumber = (int)((bearing - flapRadAdd_) / flapRad_); detemineStartPointOfFlap(lineP1, flapNumber, distance); detemineStartPointOfFlap(lineP2, flapNumber + 1, distance); closest_to_line_v2(closestPoint, point, lineP1, lineP2); - const float distanceLineToCenter = len_v2v2(m_center, closestPoint); - const float distanceRoundingToCenter = m_inverseRounding * distanceLineToCenter + - m_data->rounding * distance; + const float distanceLineToCenter = len_v2v2(center_, closestPoint); + const float distanceRoundingToCenter = inverseRounding_ * distanceLineToCenter + + data_->rounding * distance; - const float catadioptricDistanceToCenter = distanceRoundingToCenter * m_data->catadioptric; + const float catadioptricDistanceToCenter = distanceRoundingToCenter * data_->catadioptric; if (distanceRoundingToCenter >= distanceToCenter && catadioptricDistanceToCenter <= distanceToCenter) { if (distanceRoundingToCenter - distanceToCenter < 1.0f) { insideBokeh = (distanceRoundingToCenter - distanceToCenter); } - else if (m_data->catadioptric != 0.0f && + else if (data_->catadioptric != 0.0f && distanceToCenter - catadioptricDistanceToCenter < 1.0f) { insideBokeh = (distanceToCenter - catadioptricDistanceToCenter); } @@ -90,9 +90,9 @@ void BokehImageOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - float shift = m_data->lensshift; + float shift = data_->lensshift; float shift2 = shift / 2.0f; - float distance = m_circularDistance; + float distance = circularDistance_; float insideBokehMax = isInsideBokeh(distance, x, y); float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), x, y); float insideBokehMin = isInsideBokeh(distance - fabsf(shift * distance), x, y); @@ -113,9 +113,9 @@ void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - const float shift = m_data->lensshift; + const float shift = data_->lensshift; const float shift2 = shift / 2.0f; - const float distance = m_circularDistance; + const float distance = circularDistance_; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { const float insideBokehMax = isInsideBokeh(distance, it.x, it.y); const float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), it.x, it.y); @@ -136,10 +136,10 @@ void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output, void BokehImageOperation::deinitExecution() { - if (m_deleteData) { - if (m_data) { - delete m_data; - m_data = nullptr; + if (deleteData_) { + if (data_) { + delete data_; + data_ = nullptr; } } } diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h index 888446b4791..de1c05fe360 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.h +++ b/source/blender/compositor/operations/COM_BokehImageOperation.h @@ -54,37 +54,37 @@ class BokehImageOperation : public MultiThreadedOperation { /** * \brief Settings of the bokeh image */ - NodeBokehImage *m_data; + NodeBokehImage *data_; /** * \brief precalculate center of the image */ - float m_center[2]; + float center_[2]; /** * \brief 1.0-rounding */ - float m_inverseRounding; + float inverseRounding_; /** * \brief distance of a full circle lens */ - float m_circularDistance; + float circularDistance_; /** * \brief radius when the first flap starts */ - float m_flapRad; + float flapRad_; /** * \brief radians of a single flap */ - float m_flapRadAdd; + float flapRadAdd_; /** - * \brief should the m_data field by deleted when this operation is finished + * \brief should the data_ field by deleted when this operation is finished */ - bool m_deleteData; + bool deleteData_; /** * \brief determine the coordinate of a flap corner. @@ -136,7 +136,7 @@ class BokehImageOperation : public MultiThreadedOperation { */ void setData(NodeBokehImage *data) { - m_data = data; + data_ = data; } /** @@ -148,7 +148,7 @@ class BokehImageOperation : public MultiThreadedOperation { */ void deleteDataOnFinish() { - m_deleteData = true; + deleteData_ = true; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.cc b/source/blender/compositor/operations/COM_BoxMaskOperation.cc index e79e86e5ed2..7263e6a1a59 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.cc +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.cc @@ -25,19 +25,19 @@ BoxMaskOperation::BoxMaskOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_inputMask = nullptr; - m_inputValue = nullptr; - m_cosine = 0.0f; - m_sine = 0.0f; + inputMask_ = nullptr; + inputValue_ = nullptr; + cosine_ = 0.0f; + sine_ = 0.0f; } void BoxMaskOperation::initExecution() { - m_inputMask = this->getInputSocketReader(0); - m_inputValue = this->getInputSocketReader(1); - const double rad = (double)m_data->rotation; - m_cosine = cos(rad); - m_sine = sin(rad); - m_aspectRatio = ((float)this->getWidth()) / this->getHeight(); + inputMask_ = this->getInputSocketReader(0); + inputValue_ = this->getInputSocketReader(1); + const double rad = (double)data_->rotation; + cosine_ = cos(rad); + sine_ = sin(rad); + aspectRatio_ = ((float)this->getWidth()) / this->getHeight(); } void BoxMaskOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -48,20 +48,20 @@ void BoxMaskOperation::executePixelSampled(float output[4], float x, float y, Pi float rx = x / this->getWidth(); float ry = y / this->getHeight(); - const float dy = (ry - m_data->y) / m_aspectRatio; - const float dx = rx - m_data->x; - rx = m_data->x + (m_cosine * dx + m_sine * dy); - ry = m_data->y + (-m_sine * dx + m_cosine * dy); + const float dy = (ry - data_->y) / aspectRatio_; + const float dx = rx - data_->x; + rx = data_->x + (cosine_ * dx + sine_ * dy); + ry = data_->y + (-sine_ * dx + cosine_ * dy); - m_inputMask->readSampled(inputMask, x, y, sampler); - m_inputValue->readSampled(inputValue, x, y, sampler); + inputMask_->readSampled(inputMask, x, y, sampler); + inputValue_->readSampled(inputValue, x, y, sampler); - float halfHeight = m_data->height / 2.0f; - float halfWidth = m_data->width / 2.0f; - bool inside = (rx > m_data->x - halfWidth && rx < m_data->x + halfWidth && - ry > m_data->y - halfHeight && ry < m_data->y + halfHeight); + float halfHeight = data_->height / 2.0f; + float halfWidth = data_->width / 2.0f; + bool inside = (rx > data_->x - halfWidth && rx < data_->x + halfWidth && + ry > data_->y - halfHeight && ry < data_->y + halfHeight); - switch (m_maskType) { + switch (maskType_) { case CMP_NODE_MASKTYPE_ADD: if (inside) { output[0] = MAX2(inputMask[0], inputValue[0]); @@ -108,7 +108,7 @@ void BoxMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { MaskFunc mask_func; - switch (m_maskType) { + switch (maskType_) { case CMP_NODE_MASKTYPE_ADD: mask_func = [](const bool is_inside, const float *mask, const float *value) { return is_inside ? MAX2(mask[0], value[0]) : mask[0]; @@ -143,18 +143,18 @@ void BoxMaskOperation::apply_mask(MemoryBuffer *output, { const float op_w = this->getWidth(); const float op_h = this->getHeight(); - const float half_w = m_data->width / 2.0f; - const float half_h = m_data->height / 2.0f; + const float half_w = data_->width / 2.0f; + const float half_h = data_->height / 2.0f; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float op_ry = it.y / op_h; - const float dy = (op_ry - m_data->y) / m_aspectRatio; + const float dy = (op_ry - data_->y) / aspectRatio_; const float op_rx = it.x / op_w; - const float dx = op_rx - m_data->x; - const float rx = m_data->x + (m_cosine * dx + m_sine * dy); - const float ry = m_data->y + (-m_sine * dx + m_cosine * dy); + const float dx = op_rx - data_->x; + const float rx = data_->x + (cosine_ * dx + sine_ * dy); + const float ry = data_->y + (-sine_ * dx + cosine_ * dy); - const bool inside = (rx > m_data->x - half_w && rx < m_data->x + half_w && - ry > m_data->y - half_h && ry < m_data->y + half_h); + const bool inside = (rx > data_->x - half_w && rx < data_->x + half_w && + ry > data_->y - half_h && ry < data_->y + half_h); const float *mask = it.in(0); const float *value = it.in(1); *it.out = mask_func(inside, mask, value); @@ -163,8 +163,8 @@ void BoxMaskOperation::apply_mask(MemoryBuffer *output, void BoxMaskOperation::deinitExecution() { - m_inputMask = nullptr; - m_inputValue = nullptr; + inputMask_ = nullptr; + inputValue_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.h b/source/blender/compositor/operations/COM_BoxMaskOperation.h index 1f1365897cf..458cdf7ee16 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.h +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.h @@ -29,15 +29,15 @@ class BoxMaskOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputMask; - SocketReader *m_inputValue; + SocketReader *inputMask_; + SocketReader *inputValue_; - float m_sine; - float m_cosine; - float m_aspectRatio; - int m_maskType; + float sine_; + float cosine_; + float aspectRatio_; + int maskType_; - NodeBoxMask *m_data; + NodeBoxMask *data_; public: BoxMaskOperation(); @@ -59,12 +59,12 @@ class BoxMaskOperation : public MultiThreadedOperation { void setData(NodeBoxMask *data) { - m_data = data; + data_ = data; } void setMaskType(int maskType) { - m_maskType = maskType; + maskType_ = maskType; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc index 2f2313d87d8..77f30331767 100644 --- a/source/blender/compositor/operations/COM_BrightnessOperation.cc +++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc @@ -26,21 +26,21 @@ BrightnessOperation::BrightnessOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_inputProgram = nullptr; - m_use_premultiply = false; + inputProgram_ = nullptr; + use_premultiply_ = false; flags.can_be_constant = true; } void BrightnessOperation::setUsePremultiply(bool use_premultiply) { - m_use_premultiply = use_premultiply; + use_premultiply_ = use_premultiply; } void BrightnessOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); - m_inputBrightnessProgram = this->getInputSocketReader(1); - m_inputContrastProgram = this->getInputSocketReader(2); + inputProgram_ = this->getInputSocketReader(0); + inputBrightnessProgram_ = this->getInputSocketReader(1); + inputContrastProgram_ = this->getInputSocketReader(2); } void BrightnessOperation::executePixelSampled(float output[4], @@ -52,9 +52,9 @@ void BrightnessOperation::executePixelSampled(float output[4], float a, b; float inputBrightness[4]; float inputContrast[4]; - m_inputProgram->readSampled(inputValue, x, y, sampler); - m_inputBrightnessProgram->readSampled(inputBrightness, x, y, sampler); - m_inputContrastProgram->readSampled(inputContrast, x, y, sampler); + inputProgram_->readSampled(inputValue, x, y, sampler); + inputBrightnessProgram_->readSampled(inputBrightness, x, y, sampler); + inputContrastProgram_->readSampled(inputContrast, x, y, sampler); float brightness = inputBrightness[0]; float contrast = inputContrast[0]; brightness /= 100.0f; @@ -74,14 +74,14 @@ void BrightnessOperation::executePixelSampled(float output[4], a = max_ff(1.0f - delta * 2.0f, 0.0f); b = a * brightness + delta; } - if (m_use_premultiply) { + if (use_premultiply_) { premul_to_straight_v4(inputValue); } output[0] = a * inputValue[0] + b; output[1] = a * inputValue[1] + b; output[2] = a * inputValue[2] + b; output[3] = inputValue[3]; - if (m_use_premultiply) { + if (use_premultiply_) { straight_to_premul_v4(output); } } @@ -113,7 +113,7 @@ void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output, b = a * brightness + delta; } const float *color; - if (m_use_premultiply) { + if (use_premultiply_) { premul_to_straight_v4_v4(tmp_color, in_color); color = tmp_color; } @@ -124,7 +124,7 @@ void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output, it.out[1] = a * color[1] + b; it.out[2] = a * color[2] + b; it.out[3] = color[3]; - if (m_use_premultiply) { + if (use_premultiply_) { straight_to_premul_v4(it.out); } } @@ -132,9 +132,9 @@ void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output, void BrightnessOperation::deinitExecution() { - m_inputProgram = nullptr; - m_inputBrightnessProgram = nullptr; - m_inputContrastProgram = nullptr; + inputProgram_ = nullptr; + inputBrightnessProgram_ = nullptr; + inputContrastProgram_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.h b/source/blender/compositor/operations/COM_BrightnessOperation.h index 64b4fa0dbe2..0ecb8473319 100644 --- a/source/blender/compositor/operations/COM_BrightnessOperation.h +++ b/source/blender/compositor/operations/COM_BrightnessOperation.h @@ -27,11 +27,11 @@ class BrightnessOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; - SocketReader *m_inputBrightnessProgram; - SocketReader *m_inputContrastProgram; + SocketReader *inputProgram_; + SocketReader *inputBrightnessProgram_; + SocketReader *inputContrastProgram_; - bool m_use_premultiply; + bool use_premultiply_; public: BrightnessOperation(); diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc index d8a34332242..ce2351cf759 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc @@ -28,26 +28,26 @@ CalculateMeanOperation::CalculateMeanOperation() { this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Value); - m_imageReader = nullptr; - m_iscalculated = false; - m_setting = 1; + imageReader_ = nullptr; + iscalculated_ = false; + setting_ = 1; this->flags.complex = true; } void CalculateMeanOperation::initExecution() { - m_imageReader = this->getInputSocketReader(0); - m_iscalculated = false; + imageReader_ = this->getInputSocketReader(0); + iscalculated_ = false; NodeOperation::initMutex(); } void CalculateMeanOperation::executePixel(float output[4], int /*x*/, int /*y*/, void * /*data*/) { - output[0] = m_result; + output[0] = result_; } void CalculateMeanOperation::deinitExecution() { - m_imageReader = nullptr; + imageReader_ = nullptr; NodeOperation::deinitMutex(); } @@ -56,7 +56,7 @@ bool CalculateMeanOperation::determineDependingAreaOfInterest(rcti * /*input*/, rcti *output) { rcti imageInput; - if (m_iscalculated) { + if (iscalculated_) { return false; } NodeOperation *operation = getInputOperation(0); @@ -73,10 +73,10 @@ bool CalculateMeanOperation::determineDependingAreaOfInterest(rcti * /*input*/, void *CalculateMeanOperation::initializeTileData(rcti *rect) { lockMutex(); - if (!m_iscalculated) { - MemoryBuffer *tile = (MemoryBuffer *)m_imageReader->initializeTileData(rect); + if (!iscalculated_) { + MemoryBuffer *tile = (MemoryBuffer *)imageReader_->initializeTileData(rect); calculateMean(tile); - m_iscalculated = true; + iscalculated_ = true; } unlockMutex(); return nullptr; @@ -84,7 +84,7 @@ void *CalculateMeanOperation::initializeTileData(rcti *rect) void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) { - m_result = 0.0f; + result_ = 0.0f; float *buffer = tile->getBuffer(); int size = tile->getWidth() * tile->getHeight(); int pixels = 0; @@ -93,7 +93,7 @@ void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) if (buffer[offset + 3] > 0) { pixels++; - switch (m_setting) { + switch (setting_) { case 1: { sum += IMB_colormanagement_get_luminance(&buffer[offset]); break; @@ -125,12 +125,12 @@ void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) } } } - m_result = sum / pixels; + result_ = sum / pixels; } void CalculateMeanOperation::setSetting(int setting) { - m_setting = setting; + setting_ = setting; switch (setting) { case 1: { setting_func_ = IMB_colormanagement_get_luminance; @@ -171,10 +171,10 @@ void CalculateMeanOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(o const rcti &UNUSED(area), Span inputs) { - if (!m_iscalculated) { + if (!iscalculated_) { MemoryBuffer *input = inputs[0]; - m_result = calc_mean(input); - m_iscalculated = true; + result_ = calc_mean(input); + iscalculated_ = true; } } @@ -182,7 +182,7 @@ void CalculateMeanOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->fill(area, &m_result); + output->fill(area, &result_); } float CalculateMeanOperation::calc_mean(const MemoryBuffer *input) diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.h b/source/blender/compositor/operations/COM_CalculateMeanOperation.h index 779ca79b38a..a34bab45211 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.h +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.h @@ -39,11 +39,11 @@ class CalculateMeanOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *m_imageReader; + SocketReader *imageReader_; - bool m_iscalculated; - float m_result; - int m_setting; + bool iscalculated_; + float result_; + int setting_; std::function setting_func_; public: diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc index cda77f0a83b..3c8d7f21801 100644 --- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc @@ -29,26 +29,26 @@ void CalculateStandardDeviationOperation::executePixel(float output[4], int /*y*/, void * /*data*/) { - output[0] = m_standardDeviation; + output[0] = standardDeviation_; } void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) { lockMutex(); - if (!m_iscalculated) { - MemoryBuffer *tile = (MemoryBuffer *)m_imageReader->initializeTileData(rect); + if (!iscalculated_) { + MemoryBuffer *tile = (MemoryBuffer *)imageReader_->initializeTileData(rect); CalculateMeanOperation::calculateMean(tile); - m_standardDeviation = 0.0f; + standardDeviation_ = 0.0f; float *buffer = tile->getBuffer(); int size = tile->getWidth() * tile->getHeight(); int pixels = 0; float sum = 0.0f; - float mean = m_result; + float mean = result_; for (int i = 0, offset = 0; i < size; i++, offset += 4) { if (buffer[offset + 3] > 0) { pixels++; - switch (m_setting) { + switch (setting_) { case 1: /* rgb combined */ { float value = IMB_colormanagement_get_luminance(&buffer[offset]); @@ -89,8 +89,8 @@ void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) } } } - m_standardDeviation = sqrt(sum / (float)(pixels - 1)); - m_iscalculated = true; + standardDeviation_ = sqrt(sum / (float)(pixels - 1)); + iscalculated_ = true; } unlockMutex(); return nullptr; @@ -99,7 +99,7 @@ void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) void CalculateStandardDeviationOperation::update_memory_buffer_started( MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span inputs) { - if (!m_iscalculated) { + if (!iscalculated_) { const MemoryBuffer *input = inputs[0]; const float mean = CalculateMeanOperation::calc_mean(input); @@ -112,16 +112,16 @@ void CalculateStandardDeviationOperation::update_memory_buffer_started( join.sum += chunk.sum; join.num_pixels += chunk.num_pixels; }); - m_standardDeviation = total.num_pixels <= 1 ? 0.0f : - sqrt(total.sum / (float)(total.num_pixels - 1)); - m_iscalculated = true; + standardDeviation_ = total.num_pixels <= 1 ? 0.0f : + sqrt(total.sum / (float)(total.num_pixels - 1)); + iscalculated_ = true; } } void CalculateStandardDeviationOperation::update_memory_buffer_partial( MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->fill(area, &m_standardDeviation); + output->fill(area, &standardDeviation_); } using PixelsSum = CalculateMeanOperation::PixelsSum; diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h index 20de4cf4701..29d9c45c986 100644 --- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h +++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h @@ -31,7 +31,7 @@ namespace blender::compositor { */ class CalculateStandardDeviationOperation : public CalculateMeanOperation { protected: - float m_standardDeviation; + float standardDeviation_; public: /** diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc index 794574e95ca..7e240a0b95a 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc @@ -27,24 +27,24 @@ ChangeHSVOperation::ChangeHSVOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_inputOperation = nullptr; + inputOperation_ = nullptr; this->flags.can_be_constant = true; } void ChangeHSVOperation::initExecution() { - m_inputOperation = getInputSocketReader(0); - m_hueOperation = getInputSocketReader(1); - m_saturationOperation = getInputSocketReader(2); - m_valueOperation = getInputSocketReader(3); + inputOperation_ = getInputSocketReader(0); + hueOperation_ = getInputSocketReader(1); + saturationOperation_ = getInputSocketReader(2); + valueOperation_ = getInputSocketReader(3); } void ChangeHSVOperation::deinitExecution() { - m_inputOperation = nullptr; - m_hueOperation = nullptr; - m_saturationOperation = nullptr; - m_valueOperation = nullptr; + inputOperation_ = nullptr; + hueOperation_ = nullptr; + saturationOperation_ = nullptr; + valueOperation_ = nullptr; } void ChangeHSVOperation::executePixelSampled(float output[4], @@ -55,10 +55,10 @@ void ChangeHSVOperation::executePixelSampled(float output[4], float inputColor1[4]; float hue[4], saturation[4], value[4]; - m_inputOperation->readSampled(inputColor1, x, y, sampler); - m_hueOperation->readSampled(hue, x, y, sampler); - m_saturationOperation->readSampled(saturation, x, y, sampler); - m_valueOperation->readSampled(value, x, y, sampler); + inputOperation_->readSampled(inputColor1, x, y, sampler); + hueOperation_->readSampled(hue, x, y, sampler); + saturationOperation_->readSampled(saturation, x, y, sampler); + valueOperation_->readSampled(value, x, y, sampler); output[0] = inputColor1[0] + (hue[0] - 0.5f); if (output[0] > 1.0f) { diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.h b/source/blender/compositor/operations/COM_ChangeHSVOperation.h index e7bc3274f25..4b810a93816 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.h +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.h @@ -28,10 +28,10 @@ namespace blender::compositor { */ class ChangeHSVOperation : public MultiThreadedOperation { private: - SocketReader *m_inputOperation; - SocketReader *m_hueOperation; - SocketReader *m_saturationOperation; - SocketReader *m_valueOperation; + SocketReader *inputOperation_; + SocketReader *hueOperation_; + SocketReader *saturationOperation_; + SocketReader *valueOperation_; public: /** diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc index f3664c7e2de..cbc6d078ac1 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc @@ -25,46 +25,46 @@ ChannelMatteOperation::ChannelMatteOperation() addInputSocket(DataType::Color); addOutputSocket(DataType::Value); - m_inputImageProgram = nullptr; + inputImageProgram_ = nullptr; flags.can_be_constant = true; } void ChannelMatteOperation::initExecution() { - m_inputImageProgram = this->getInputSocketReader(0); + inputImageProgram_ = this->getInputSocketReader(0); - m_limit_range = m_limit_max - m_limit_min; + limit_range_ = limit_max_ - limit_min_; - switch (m_limit_method) { + switch (limit_method_) { /* SINGLE */ case 0: { /* 123 / RGB / HSV / YUV / YCC */ - const int matte_channel = m_matte_channel - 1; - const int limit_channel = m_limit_channel - 1; - m_ids[0] = matte_channel; - m_ids[1] = limit_channel; - m_ids[2] = limit_channel; + const int matte_channel = matte_channel_ - 1; + const int limit_channel = limit_channel_ - 1; + ids_[0] = matte_channel; + ids_[1] = limit_channel; + ids_[2] = limit_channel; break; } /* MAX */ case 1: { - switch (m_matte_channel) { + switch (matte_channel_) { case 1: { - m_ids[0] = 0; - m_ids[1] = 1; - m_ids[2] = 2; + ids_[0] = 0; + ids_[1] = 1; + ids_[2] = 2; break; } case 2: { - m_ids[0] = 1; - m_ids[1] = 0; - m_ids[2] = 2; + ids_[0] = 1; + ids_[1] = 0; + ids_[2] = 2; break; } case 3: { - m_ids[0] = 2; - m_ids[1] = 0; - m_ids[2] = 1; + ids_[0] = 2; + ids_[1] = 0; + ids_[2] = 1; break; } default: @@ -79,7 +79,7 @@ void ChannelMatteOperation::initExecution() void ChannelMatteOperation::deinitExecution() { - m_inputImageProgram = nullptr; + inputImageProgram_ = nullptr; } void ChannelMatteOperation::executePixelSampled(float output[4], @@ -90,14 +90,14 @@ void ChannelMatteOperation::executePixelSampled(float output[4], float inColor[4]; float alpha; - const float limit_max = m_limit_max; - const float limit_min = m_limit_min; - const float limit_range = m_limit_range; + const float limit_max = limit_max_; + const float limit_min = limit_min_; + const float limit_range = limit_range_; - m_inputImageProgram->readSampled(inColor, x, y, sampler); + inputImageProgram_->readSampled(inColor, x, y, sampler); /* matte operation */ - alpha = inColor[m_ids[0]] - MAX2(inColor[m_ids[1]], inColor[m_ids[2]]); + alpha = inColor[ids_[0]] - MAX2(inColor[ids_[1]], inColor[ids_[2]]); /* flip because 0.0 is transparent, not 1.0 */ alpha = 1.0f - alpha; @@ -129,20 +129,20 @@ void ChannelMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, const float *color = it.in(0); /* Matte operation. */ - float alpha = color[m_ids[0]] - MAX2(color[m_ids[1]], color[m_ids[2]]); + float alpha = color[ids_[0]] - MAX2(color[ids_[1]], color[ids_[2]]); /* Flip because 0.0 is transparent, not 1.0. */ alpha = 1.0f - alpha; /* Test range. */ - if (alpha > m_limit_max) { + if (alpha > limit_max_) { alpha = color[3]; /* Whatever it was prior. */ } - else if (alpha < m_limit_min) { + else if (alpha < limit_min_) { alpha = 0.0f; } else { /* Blend. */ - alpha = (alpha - m_limit_min) / m_limit_range; + alpha = (alpha - limit_min_) / limit_range_; } /* Store matte(alpha) value in [0] to go with diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.h b/source/blender/compositor/operations/COM_ChannelMatteOperation.h index 83f187ae443..f7bf58b99c5 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.h +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.h @@ -28,16 +28,16 @@ namespace blender::compositor { */ class ChannelMatteOperation : public MultiThreadedOperation { private: - SocketReader *m_inputImageProgram; + SocketReader *inputImageProgram_; - /* int m_color_space; */ /* node->custom1 */ /* UNUSED */ /* TODO ? */ - int m_matte_channel; /* node->custom2 */ - int m_limit_method; /* node->algorithm */ - int m_limit_channel; /* node->channel */ - float m_limit_max; /* node->storage->t1 */ - float m_limit_min; /* node->storage->t2 */ + /* int color_space_; */ /* node->custom1 */ /* UNUSED */ /* TODO ? */ + int matte_channel_; /* node->custom2 */ + int limit_method_; /* node->algorithm */ + int limit_channel_; /* node->channel */ + float limit_max_; /* node->storage->t1 */ + float limit_min_; /* node->storage->t2 */ - float m_limit_range; + float limit_range_; /** ids to use for the operations (max and simple) * alpha = in[ids[0]] - MAX2(in[ids[1]], in[ids[2]]) @@ -47,7 +47,7 @@ class ChannelMatteOperation : public MultiThreadedOperation { * ids[2] = ids[1] * alpha = in[ids[0]] - MAX2(in[ids[1]], in[ids[2]]) */ - int m_ids[3]; + int ids_[3]; public: /** @@ -65,11 +65,11 @@ class ChannelMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma, const int custom2) { - m_limit_max = nodeChroma->t1; - m_limit_min = nodeChroma->t2; - m_limit_method = nodeChroma->algorithm; - m_limit_channel = nodeChroma->channel; - m_matte_channel = custom2; + limit_max_ = nodeChroma->t1; + limit_min_ = nodeChroma->t2; + limit_method_ = nodeChroma->algorithm; + limit_channel_ = nodeChroma->channel; + matte_channel_ = custom2; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc index 7b2f3470efe..172460e9203 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc @@ -26,21 +26,21 @@ ChromaMatteOperation::ChromaMatteOperation() addInputSocket(DataType::Color); addOutputSocket(DataType::Value); - m_inputImageProgram = nullptr; - m_inputKeyProgram = nullptr; + inputImageProgram_ = nullptr; + inputKeyProgram_ = nullptr; flags.can_be_constant = true; } void ChromaMatteOperation::initExecution() { - m_inputImageProgram = this->getInputSocketReader(0); - m_inputKeyProgram = this->getInputSocketReader(1); + inputImageProgram_ = this->getInputSocketReader(0); + inputKeyProgram_ = this->getInputSocketReader(1); } void ChromaMatteOperation::deinitExecution() { - m_inputImageProgram = nullptr; - m_inputKeyProgram = nullptr; + inputImageProgram_ = nullptr; + inputKeyProgram_ = nullptr; } void ChromaMatteOperation::executePixelSampled(float output[4], @@ -51,16 +51,16 @@ void ChromaMatteOperation::executePixelSampled(float output[4], float inKey[4]; float inImage[4]; - const float acceptance = m_settings->t1; /* in radians */ - const float cutoff = m_settings->t2; /* in radians */ - const float gain = m_settings->fstrength; + const float acceptance = settings_->t1; /* in radians */ + const float cutoff = settings_->t2; /* in radians */ + const float gain = settings_->fstrength; float x_angle, z_angle, alpha; float theta, beta; float kfg; - m_inputKeyProgram->readSampled(inKey, x, y, sampler); - m_inputImageProgram->readSampled(inImage, x, y, sampler); + inputKeyProgram_->readSampled(inKey, x, y, sampler); + inputImageProgram_->readSampled(inImage, x, y, sampler); /* Store matte(alpha) value in [0] to go with * #COM_SetAlphaMultiplyOperation and the Value output. */ @@ -114,9 +114,9 @@ void ChromaMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float acceptance = m_settings->t1; /* In radians. */ - const float cutoff = m_settings->t2; /* In radians. */ - const float gain = m_settings->fstrength; + const float acceptance = settings_->t1; /* In radians. */ + const float cutoff = settings_->t2; /* In radians. */ + const float gain = settings_->fstrength; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float *in_image = it.in(0); const float *in_key = it.in(1); diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.h b/source/blender/compositor/operations/COM_ChromaMatteOperation.h index 182d0baba76..913d0f27107 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.h +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ChromaMatteOperation : public MultiThreadedOperation { private: - NodeChroma *m_settings; - SocketReader *m_inputImageProgram; - SocketReader *m_inputKeyProgram; + NodeChroma *settings_; + SocketReader *inputImageProgram_; + SocketReader *inputKeyProgram_; public: /** @@ -48,7 +48,7 @@ class ChromaMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma) { - m_settings = nodeChroma; + settings_ = nodeChroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc index d4118503341..b6253c07f12 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc @@ -37,16 +37,16 @@ ColorBalanceASCCDLOperation::ColorBalanceASCCDLOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_inputValueOperation = nullptr; - m_inputColorOperation = nullptr; + inputValueOperation_ = nullptr; + inputColorOperation_ = nullptr; this->set_canvas_input_index(1); flags.can_be_constant = true; } void ColorBalanceASCCDLOperation::initExecution() { - m_inputValueOperation = this->getInputSocketReader(0); - m_inputColorOperation = this->getInputSocketReader(1); + inputValueOperation_ = this->getInputSocketReader(0); + inputColorOperation_ = this->getInputSocketReader(1); } void ColorBalanceASCCDLOperation::executePixelSampled(float output[4], @@ -57,19 +57,19 @@ void ColorBalanceASCCDLOperation::executePixelSampled(float output[4], float inputColor[4]; float value[4]; - m_inputValueOperation->readSampled(value, x, y, sampler); - m_inputColorOperation->readSampled(inputColor, x, y, sampler); + inputValueOperation_->readSampled(value, x, y, sampler); + inputColorOperation_->readSampled(inputColor, x, y, sampler); float fac = value[0]; fac = MIN2(1.0f, fac); const float mfac = 1.0f - fac; output[0] = mfac * inputColor[0] + - fac * colorbalance_cdl(inputColor[0], m_offset[0], m_power[0], m_slope[0]); + fac * colorbalance_cdl(inputColor[0], offset_[0], power_[0], slope_[0]); output[1] = mfac * inputColor[1] + - fac * colorbalance_cdl(inputColor[1], m_offset[1], m_power[1], m_slope[1]); + fac * colorbalance_cdl(inputColor[1], offset_[1], power_[1], slope_[1]); output[2] = mfac * inputColor[2] + - fac * colorbalance_cdl(inputColor[2], m_offset[2], m_power[2], m_slope[2]); + fac * colorbalance_cdl(inputColor[2], offset_[2], power_[2], slope_[2]); output[3] = inputColor[3]; } @@ -81,19 +81,19 @@ void ColorBalanceASCCDLOperation::update_memory_buffer_row(PixelCursor &p) const float fac = MIN2(1.0f, in_factor[0]); const float fac_m = 1.0f - fac; p.out[0] = fac_m * in_color[0] + - fac * colorbalance_cdl(in_color[0], m_offset[0], m_power[0], m_slope[0]); + fac * colorbalance_cdl(in_color[0], offset_[0], power_[0], slope_[0]); p.out[1] = fac_m * in_color[1] + - fac * colorbalance_cdl(in_color[1], m_offset[1], m_power[1], m_slope[1]); + fac * colorbalance_cdl(in_color[1], offset_[1], power_[1], slope_[1]); p.out[2] = fac_m * in_color[2] + - fac * colorbalance_cdl(in_color[2], m_offset[2], m_power[2], m_slope[2]); + fac * colorbalance_cdl(in_color[2], offset_[2], power_[2], slope_[2]); p.out[3] = in_color[3]; } } void ColorBalanceASCCDLOperation::deinitExecution() { - m_inputValueOperation = nullptr; - m_inputColorOperation = nullptr; + inputValueOperation_ = nullptr; + inputColorOperation_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h index b9b3d45a68b..79c5cc1a047 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h @@ -31,12 +31,12 @@ class ColorBalanceASCCDLOperation : public MultiThreadedRowOperation { /** * Prefetched reference to the inputProgram */ - SocketReader *m_inputValueOperation; - SocketReader *m_inputColorOperation; + SocketReader *inputValueOperation_; + SocketReader *inputColorOperation_; - float m_offset[3]; - float m_power[3]; - float m_slope[3]; + float offset_[3]; + float power_[3]; + float slope_[3]; public: /** @@ -61,15 +61,15 @@ class ColorBalanceASCCDLOperation : public MultiThreadedRowOperation { void setOffset(float offset[3]) { - copy_v3_v3(m_offset, offset); + copy_v3_v3(offset_, offset); } void setPower(float power[3]) { - copy_v3_v3(m_power, power); + copy_v3_v3(power_, power); } void setSlope(float slope[3]) { - copy_v3_v3(m_slope, slope); + copy_v3_v3(slope_, slope); } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc index b6059d70b70..d7c7368a214 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc @@ -42,16 +42,16 @@ ColorBalanceLGGOperation::ColorBalanceLGGOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_inputValueOperation = nullptr; - m_inputColorOperation = nullptr; + inputValueOperation_ = nullptr; + inputColorOperation_ = nullptr; this->set_canvas_input_index(1); flags.can_be_constant = true; } void ColorBalanceLGGOperation::initExecution() { - m_inputValueOperation = this->getInputSocketReader(0); - m_inputColorOperation = this->getInputSocketReader(1); + inputValueOperation_ = this->getInputSocketReader(0); + inputColorOperation_ = this->getInputSocketReader(1); } void ColorBalanceLGGOperation::executePixelSampled(float output[4], @@ -62,19 +62,19 @@ void ColorBalanceLGGOperation::executePixelSampled(float output[4], float inputColor[4]; float value[4]; - m_inputValueOperation->readSampled(value, x, y, sampler); - m_inputColorOperation->readSampled(inputColor, x, y, sampler); + inputValueOperation_->readSampled(value, x, y, sampler); + inputColorOperation_->readSampled(inputColor, x, y, sampler); float fac = value[0]; fac = MIN2(1.0f, fac); const float mfac = 1.0f - fac; output[0] = mfac * inputColor[0] + - fac * colorbalance_lgg(inputColor[0], m_lift[0], m_gamma_inv[0], m_gain[0]); + fac * colorbalance_lgg(inputColor[0], lift_[0], gamma_inv_[0], gain_[0]); output[1] = mfac * inputColor[1] + - fac * colorbalance_lgg(inputColor[1], m_lift[1], m_gamma_inv[1], m_gain[1]); + fac * colorbalance_lgg(inputColor[1], lift_[1], gamma_inv_[1], gain_[1]); output[2] = mfac * inputColor[2] + - fac * colorbalance_lgg(inputColor[2], m_lift[2], m_gamma_inv[2], m_gain[2]); + fac * colorbalance_lgg(inputColor[2], lift_[2], gamma_inv_[2], gain_[2]); output[3] = inputColor[3]; } @@ -86,19 +86,19 @@ void ColorBalanceLGGOperation::update_memory_buffer_row(PixelCursor &p) const float fac = MIN2(1.0f, in_factor[0]); const float fac_m = 1.0f - fac; p.out[0] = fac_m * in_color[0] + - fac * colorbalance_lgg(in_color[0], m_lift[0], m_gamma_inv[0], m_gain[0]); + fac * colorbalance_lgg(in_color[0], lift_[0], gamma_inv_[0], gain_[0]); p.out[1] = fac_m * in_color[1] + - fac * colorbalance_lgg(in_color[1], m_lift[1], m_gamma_inv[1], m_gain[1]); + fac * colorbalance_lgg(in_color[1], lift_[1], gamma_inv_[1], gain_[1]); p.out[2] = fac_m * in_color[2] + - fac * colorbalance_lgg(in_color[2], m_lift[2], m_gamma_inv[2], m_gain[2]); + fac * colorbalance_lgg(in_color[2], lift_[2], gamma_inv_[2], gain_[2]); p.out[3] = in_color[3]; } } void ColorBalanceLGGOperation::deinitExecution() { - m_inputValueOperation = nullptr; - m_inputColorOperation = nullptr; + inputValueOperation_ = nullptr; + inputColorOperation_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h index 0a447f50514..9393f635f9e 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h @@ -31,12 +31,12 @@ class ColorBalanceLGGOperation : public MultiThreadedRowOperation { /** * Prefetched reference to the inputProgram */ - SocketReader *m_inputValueOperation; - SocketReader *m_inputColorOperation; + SocketReader *inputValueOperation_; + SocketReader *inputColorOperation_; - float m_gain[3]; - float m_lift[3]; - float m_gamma_inv[3]; + float gain_[3]; + float lift_[3]; + float gamma_inv_[3]; public: /** @@ -61,15 +61,15 @@ class ColorBalanceLGGOperation : public MultiThreadedRowOperation { void setGain(const float gain[3]) { - copy_v3_v3(m_gain, gain); + copy_v3_v3(gain_, gain); } void setLift(const float lift[3]) { - copy_v3_v3(m_lift, lift); + copy_v3_v3(lift_, lift); } void setGammaInv(const float gamma_inv[3]) { - copy_v3_v3(m_gamma_inv, gamma_inv); + copy_v3_v3(gamma_inv_, gamma_inv); } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc index 9cbe9a16ade..128c7c80ea9 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc @@ -27,17 +27,17 @@ ColorCorrectionOperation::ColorCorrectionOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_inputImage = nullptr; - m_inputMask = nullptr; - m_redChannelEnabled = true; - m_greenChannelEnabled = true; - m_blueChannelEnabled = true; + inputImage_ = nullptr; + inputMask_ = nullptr; + redChannelEnabled_ = true; + greenChannelEnabled_ = true; + blueChannelEnabled_ = true; flags.can_be_constant = true; } void ColorCorrectionOperation::initExecution() { - m_inputImage = this->getInputSocketReader(0); - m_inputMask = this->getInputSocketReader(1); + inputImage_ = this->getInputSocketReader(0); + inputMask_ = this->getInputSocketReader(1); } /* Calculate x^y if the function is defined. Otherwise return the given fallback value. */ @@ -56,15 +56,15 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], { float inputImageColor[4]; float inputMask[4]; - m_inputImage->readSampled(inputImageColor, x, y, sampler); - m_inputMask->readSampled(inputMask, x, y, sampler); + inputImage_->readSampled(inputImageColor, x, y, sampler); + inputMask_->readSampled(inputMask, x, y, sampler); float level = (inputImageColor[0] + inputImageColor[1] + inputImageColor[2]) / 3.0f; - float contrast = m_data->master.contrast; - float saturation = m_data->master.saturation; - float gamma = m_data->master.gamma; - float gain = m_data->master.gain; - float lift = m_data->master.lift; + float contrast = data_->master.contrast; + float saturation = data_->master.saturation; + float gamma = data_->master.gamma; + float gain = data_->master.gain; + float lift = data_->master.lift; float r, g, b; float value = inputMask[0]; @@ -76,18 +76,18 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], float levelHighlights = 0.0; #define MARGIN 0.10f #define MARGIN_DIV (0.5f / MARGIN) - if (level < m_data->startmidtones - MARGIN) { + if (level < data_->startmidtones - MARGIN) { levelShadows = 1.0f; } - else if (level < m_data->startmidtones + MARGIN) { - levelMidtones = ((level - m_data->startmidtones) * MARGIN_DIV) + 0.5f; + else if (level < data_->startmidtones + MARGIN) { + levelMidtones = ((level - data_->startmidtones) * MARGIN_DIV) + 0.5f; levelShadows = 1.0f - levelMidtones; } - else if (level < m_data->endmidtones - MARGIN) { + else if (level < data_->endmidtones - MARGIN) { levelMidtones = 1.0f; } - else if (level < m_data->endmidtones + MARGIN) { - levelHighlights = ((level - m_data->endmidtones) * MARGIN_DIV) + 0.5f; + else if (level < data_->endmidtones + MARGIN) { + levelHighlights = ((level - data_->endmidtones) * MARGIN_DIV) + 0.5f; levelMidtones = 1.0f - levelHighlights; } else { @@ -95,18 +95,18 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], } #undef MARGIN #undef MARGIN_DIV - contrast *= (levelShadows * m_data->shadows.contrast) + - (levelMidtones * m_data->midtones.contrast) + - (levelHighlights * m_data->highlights.contrast); - saturation *= (levelShadows * m_data->shadows.saturation) + - (levelMidtones * m_data->midtones.saturation) + - (levelHighlights * m_data->highlights.saturation); - gamma *= (levelShadows * m_data->shadows.gamma) + (levelMidtones * m_data->midtones.gamma) + - (levelHighlights * m_data->highlights.gamma); - gain *= (levelShadows * m_data->shadows.gain) + (levelMidtones * m_data->midtones.gain) + - (levelHighlights * m_data->highlights.gain); - lift += (levelShadows * m_data->shadows.lift) + (levelMidtones * m_data->midtones.lift) + - (levelHighlights * m_data->highlights.lift); + contrast *= (levelShadows * data_->shadows.contrast) + + (levelMidtones * data_->midtones.contrast) + + (levelHighlights * data_->highlights.contrast); + saturation *= (levelShadows * data_->shadows.saturation) + + (levelMidtones * data_->midtones.saturation) + + (levelHighlights * data_->highlights.saturation); + gamma *= (levelShadows * data_->shadows.gamma) + (levelMidtones * data_->midtones.gamma) + + (levelHighlights * data_->highlights.gamma); + gain *= (levelShadows * data_->shadows.gain) + (levelMidtones * data_->midtones.gain) + + (levelHighlights * data_->highlights.gain); + lift += (levelShadows * data_->shadows.lift) + (levelMidtones * data_->midtones.lift) + + (levelHighlights * data_->highlights.lift); float invgamma = 1.0f / gamma; float luma = IMB_colormanagement_get_luminance(inputImageColor); @@ -133,19 +133,19 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], g = mvalue * inputImageColor[1] + value * g; b = mvalue * inputImageColor[2] + value * b; - if (m_redChannelEnabled) { + if (redChannelEnabled_) { output[0] = r; } else { output[0] = inputImageColor[0]; } - if (m_greenChannelEnabled) { + if (greenChannelEnabled_) { output[1] = g; } else { output[1] = inputImageColor[1]; } - if (m_blueChannelEnabled) { + if (blueChannelEnabled_) { output[2] = b; } else { @@ -166,40 +166,40 @@ void ColorCorrectionOperation::update_memory_buffer_row(PixelCursor &p) float level_highlights = 0.0f; constexpr float MARGIN = 0.10f; constexpr float MARGIN_DIV = 0.5f / MARGIN; - if (level < m_data->startmidtones - MARGIN) { + if (level < data_->startmidtones - MARGIN) { level_shadows = 1.0f; } - else if (level < m_data->startmidtones + MARGIN) { - level_midtones = ((level - m_data->startmidtones) * MARGIN_DIV) + 0.5f; + else if (level < data_->startmidtones + MARGIN) { + level_midtones = ((level - data_->startmidtones) * MARGIN_DIV) + 0.5f; level_shadows = 1.0f - level_midtones; } - else if (level < m_data->endmidtones - MARGIN) { + else if (level < data_->endmidtones - MARGIN) { level_midtones = 1.0f; } - else if (level < m_data->endmidtones + MARGIN) { - level_highlights = ((level - m_data->endmidtones) * MARGIN_DIV) + 0.5f; + else if (level < data_->endmidtones + MARGIN) { + level_highlights = ((level - data_->endmidtones) * MARGIN_DIV) + 0.5f; level_midtones = 1.0f - level_highlights; } else { level_highlights = 1.0f; } - float contrast = m_data->master.contrast; - float saturation = m_data->master.saturation; - float gamma = m_data->master.gamma; - float gain = m_data->master.gain; - float lift = m_data->master.lift; - contrast *= level_shadows * m_data->shadows.contrast + - level_midtones * m_data->midtones.contrast + - level_highlights * m_data->highlights.contrast; - saturation *= level_shadows * m_data->shadows.saturation + - level_midtones * m_data->midtones.saturation + - level_highlights * m_data->highlights.saturation; - gamma *= level_shadows * m_data->shadows.gamma + level_midtones * m_data->midtones.gamma + - level_highlights * m_data->highlights.gamma; - gain *= level_shadows * m_data->shadows.gain + level_midtones * m_data->midtones.gain + - level_highlights * m_data->highlights.gain; - lift += level_shadows * m_data->shadows.lift + level_midtones * m_data->midtones.lift + - level_highlights * m_data->highlights.lift; + float contrast = data_->master.contrast; + float saturation = data_->master.saturation; + float gamma = data_->master.gamma; + float gain = data_->master.gain; + float lift = data_->master.lift; + contrast *= level_shadows * data_->shadows.contrast + + level_midtones * data_->midtones.contrast + + level_highlights * data_->highlights.contrast; + saturation *= level_shadows * data_->shadows.saturation + + level_midtones * data_->midtones.saturation + + level_highlights * data_->highlights.saturation; + gamma *= level_shadows * data_->shadows.gamma + level_midtones * data_->midtones.gamma + + level_highlights * data_->highlights.gamma; + gain *= level_shadows * data_->shadows.gain + level_midtones * data_->midtones.gain + + level_highlights * data_->highlights.gain; + lift += level_shadows * data_->shadows.lift + level_midtones * data_->midtones.lift + + level_highlights * data_->highlights.lift; const float inv_gamma = 1.0f / gamma; const float luma = IMB_colormanagement_get_luminance(in_color); @@ -219,22 +219,22 @@ void ColorCorrectionOperation::update_memory_buffer_row(PixelCursor &p) /* Mix with mask. */ const float value = MIN2(1.0f, in_mask[0]); - const float m_value = 1.0f - value; - r = m_value * in_color[0] + value * r; - g = m_value * in_color[1] + value * g; - b = m_value * in_color[2] + value * b; + const float value_ = 1.0f - value; + r = value_ * in_color[0] + value * r; + g = value_ * in_color[1] + value * g; + b = value_ * in_color[2] + value * b; - p.out[0] = m_redChannelEnabled ? r : in_color[0]; - p.out[1] = m_greenChannelEnabled ? g : in_color[1]; - p.out[2] = m_blueChannelEnabled ? b : in_color[2]; + p.out[0] = redChannelEnabled_ ? r : in_color[0]; + p.out[1] = greenChannelEnabled_ ? g : in_color[1]; + p.out[2] = blueChannelEnabled_ ? b : in_color[2]; p.out[3] = in_color[3]; } } void ColorCorrectionOperation::deinitExecution() { - m_inputImage = nullptr; - m_inputMask = nullptr; + inputImage_ = nullptr; + inputMask_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h index 64af049e48c..157babd299b 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h @@ -27,13 +27,13 @@ class ColorCorrectionOperation : public MultiThreadedRowOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputImage; - SocketReader *m_inputMask; - NodeColorCorrection *m_data; + SocketReader *inputImage_; + SocketReader *inputMask_; + NodeColorCorrection *data_; - bool m_redChannelEnabled; - bool m_greenChannelEnabled; - bool m_blueChannelEnabled; + bool redChannelEnabled_; + bool greenChannelEnabled_; + bool blueChannelEnabled_; public: ColorCorrectionOperation(); @@ -55,19 +55,19 @@ class ColorCorrectionOperation : public MultiThreadedRowOperation { void setData(NodeColorCorrection *data) { - m_data = data; + data_ = data; } void setRedChannelEnabled(bool enabled) { - m_redChannelEnabled = enabled; + redChannelEnabled_ = enabled; } void setGreenChannelEnabled(bool enabled) { - m_greenChannelEnabled = enabled; + greenChannelEnabled_ = enabled; } void setBlueChannelEnabled(bool enabled) { - m_blueChannelEnabled = enabled; + blueChannelEnabled_ = enabled; } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc index c103a3fc651..b88989ad224 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc @@ -30,22 +30,22 @@ ColorCurveOperation::ColorCurveOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_inputFacProgram = nullptr; - m_inputImageProgram = nullptr; - m_inputBlackProgram = nullptr; - m_inputWhiteProgram = nullptr; + inputFacProgram_ = nullptr; + inputImageProgram_ = nullptr; + inputBlackProgram_ = nullptr; + inputWhiteProgram_ = nullptr; this->set_canvas_input_index(1); } void ColorCurveOperation::initExecution() { CurveBaseOperation::initExecution(); - m_inputFacProgram = this->getInputSocketReader(0); - m_inputImageProgram = this->getInputSocketReader(1); - m_inputBlackProgram = this->getInputSocketReader(2); - m_inputWhiteProgram = this->getInputSocketReader(3); + inputFacProgram_ = this->getInputSocketReader(0); + inputImageProgram_ = this->getInputSocketReader(1); + inputBlackProgram_ = this->getInputSocketReader(2); + inputWhiteProgram_ = this->getInputSocketReader(3); - BKE_curvemapping_premultiply(m_curveMapping, 0); + BKE_curvemapping_premultiply(curveMapping_, 0); } void ColorCurveOperation::executePixelSampled(float output[4], @@ -53,7 +53,7 @@ void ColorCurveOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - CurveMapping *cumap = m_curveMapping; + CurveMapping *cumap = curveMapping_; float fac[4]; float image[4]; @@ -63,15 +63,15 @@ void ColorCurveOperation::executePixelSampled(float output[4], float white[4]; float bwmul[3]; - m_inputBlackProgram->readSampled(black, x, y, sampler); - m_inputWhiteProgram->readSampled(white, x, y, sampler); + inputBlackProgram_->readSampled(black, x, y, sampler); + inputWhiteProgram_->readSampled(white, x, y, sampler); /* get our own local bwmul value, * since we can't be threadsafe and use cumap->bwmul & friends */ BKE_curvemapping_set_black_white_ex(black, white, bwmul); - m_inputFacProgram->readSampled(fac, x, y, sampler); - m_inputImageProgram->readSampled(image, x, y, sampler); + inputFacProgram_->readSampled(fac, x, y, sampler); + inputImageProgram_->readSampled(image, x, y, sampler); if (*fac >= 1.0f) { BKE_curvemapping_evaluate_premulRGBF_ex(cumap, output, image, black, bwmul); @@ -90,17 +90,17 @@ void ColorCurveOperation::executePixelSampled(float output[4], void ColorCurveOperation::deinitExecution() { CurveBaseOperation::deinitExecution(); - m_inputFacProgram = nullptr; - m_inputImageProgram = nullptr; - m_inputBlackProgram = nullptr; - m_inputWhiteProgram = nullptr; + inputFacProgram_ = nullptr; + inputImageProgram_ = nullptr; + inputBlackProgram_ = nullptr; + inputWhiteProgram_ = nullptr; } void ColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *cumap = m_curveMapping; + CurveMapping *cumap = curveMapping_; float bwmul[3]; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { /* Local versions of `cumap->black` and `cumap->white`. */ @@ -134,20 +134,20 @@ ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_inputFacProgram = nullptr; - m_inputImageProgram = nullptr; + inputFacProgram_ = nullptr; + inputImageProgram_ = nullptr; this->set_canvas_input_index(1); } void ConstantLevelColorCurveOperation::initExecution() { CurveBaseOperation::initExecution(); - m_inputFacProgram = this->getInputSocketReader(0); - m_inputImageProgram = this->getInputSocketReader(1); + inputFacProgram_ = this->getInputSocketReader(0); + inputImageProgram_ = this->getInputSocketReader(1); - BKE_curvemapping_premultiply(m_curveMapping, 0); + BKE_curvemapping_premultiply(curveMapping_, 0); - BKE_curvemapping_set_black_white(m_curveMapping, m_black, m_white); + BKE_curvemapping_set_black_white(curveMapping_, black_, white_); } void ConstantLevelColorCurveOperation::executePixelSampled(float output[4], @@ -158,18 +158,18 @@ void ConstantLevelColorCurveOperation::executePixelSampled(float output[4], float fac[4]; float image[4]; - m_inputFacProgram->readSampled(fac, x, y, sampler); - m_inputImageProgram->readSampled(image, x, y, sampler); + inputFacProgram_->readSampled(fac, x, y, sampler); + inputImageProgram_->readSampled(image, x, y, sampler); if (*fac >= 1.0f) { - BKE_curvemapping_evaluate_premulRGBF(m_curveMapping, output, image); + BKE_curvemapping_evaluate_premulRGBF(curveMapping_, output, image); } else if (*fac <= 0.0f) { copy_v3_v3(output, image); } else { float col[4]; - BKE_curvemapping_evaluate_premulRGBF(m_curveMapping, col, image); + BKE_curvemapping_evaluate_premulRGBF(curveMapping_, col, image); interp_v3_v3v3(output, image, col, *fac); } output[3] = image[3]; @@ -178,15 +178,15 @@ void ConstantLevelColorCurveOperation::executePixelSampled(float output[4], void ConstantLevelColorCurveOperation::deinitExecution() { CurveBaseOperation::deinitExecution(); - m_inputFacProgram = nullptr; - m_inputImageProgram = nullptr; + inputFacProgram_ = nullptr; + inputImageProgram_ = nullptr; } void ConstantLevelColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *cumap = m_curveMapping; + CurveMapping *cumap = curveMapping_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float fac = *it.in(0); const float *image = it.in(1); diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h index b92640839ba..6f16c45d11e 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.h +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h @@ -27,10 +27,10 @@ class ColorCurveOperation : public CurveBaseOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputFacProgram; - SocketReader *m_inputImageProgram; - SocketReader *m_inputBlackProgram; - SocketReader *m_inputWhiteProgram; + SocketReader *inputFacProgram_; + SocketReader *inputImageProgram_; + SocketReader *inputBlackProgram_; + SocketReader *inputWhiteProgram_; public: ColorCurveOperation(); @@ -60,10 +60,10 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputFacProgram; - SocketReader *m_inputImageProgram; - float m_black[3]; - float m_white[3]; + SocketReader *inputFacProgram_; + SocketReader *inputImageProgram_; + float black_[3]; + float white_[3]; public: ConstantLevelColorCurveOperation(); @@ -85,11 +85,11 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation { void setBlackLevel(float black[3]) { - copy_v3_v3(m_black, black); + copy_v3_v3(black_, black); } void setWhiteLevel(float white[3]) { - copy_v3_v3(m_white, white); + copy_v3_v3(white_, white); } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.cc b/source/blender/compositor/operations/COM_ColorExposureOperation.cc index 7886f2a619f..f0e6abe67f9 100644 --- a/source/blender/compositor/operations/COM_ColorExposureOperation.cc +++ b/source/blender/compositor/operations/COM_ColorExposureOperation.cc @@ -25,14 +25,14 @@ ExposureOperation::ExposureOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_inputProgram = nullptr; + inputProgram_ = nullptr; flags.can_be_constant = true; } void ExposureOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); - m_inputExposureProgram = this->getInputSocketReader(1); + inputProgram_ = this->getInputSocketReader(0); + inputExposureProgram_ = this->getInputSocketReader(1); } void ExposureOperation::executePixelSampled(float output[4], @@ -42,8 +42,8 @@ void ExposureOperation::executePixelSampled(float output[4], { float inputValue[4]; float inputExposure[4]; - m_inputProgram->readSampled(inputValue, x, y, sampler); - m_inputExposureProgram->readSampled(inputExposure, x, y, sampler); + inputProgram_->readSampled(inputValue, x, y, sampler); + inputExposureProgram_->readSampled(inputExposure, x, y, sampler); const float exposure = pow(2, inputExposure[0]); output[0] = inputValue[0] * exposure; @@ -68,8 +68,8 @@ void ExposureOperation::update_memory_buffer_row(PixelCursor &p) void ExposureOperation::deinitExecution() { - m_inputProgram = nullptr; - m_inputExposureProgram = nullptr; + inputProgram_ = nullptr; + inputExposureProgram_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.h b/source/blender/compositor/operations/COM_ColorExposureOperation.h index 1eb790e8d52..1a0f912d6bd 100644 --- a/source/blender/compositor/operations/COM_ColorExposureOperation.h +++ b/source/blender/compositor/operations/COM_ColorExposureOperation.h @@ -27,8 +27,8 @@ class ExposureOperation : public MultiThreadedRowOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; - SocketReader *m_inputExposureProgram; + SocketReader *inputProgram_; + SocketReader *inputExposureProgram_; public: ExposureOperation(); diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc index be211f6c966..c693e7d06e1 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc @@ -26,21 +26,21 @@ ColorMatteOperation::ColorMatteOperation() addInputSocket(DataType::Color); addOutputSocket(DataType::Value); - m_inputImageProgram = nullptr; - m_inputKeyProgram = nullptr; + inputImageProgram_ = nullptr; + inputKeyProgram_ = nullptr; flags.can_be_constant = true; } void ColorMatteOperation::initExecution() { - m_inputImageProgram = this->getInputSocketReader(0); - m_inputKeyProgram = this->getInputSocketReader(1); + inputImageProgram_ = this->getInputSocketReader(0); + inputKeyProgram_ = this->getInputSocketReader(1); } void ColorMatteOperation::deinitExecution() { - m_inputImageProgram = nullptr; - m_inputKeyProgram = nullptr; + inputImageProgram_ = nullptr; + inputKeyProgram_ = nullptr; } void ColorMatteOperation::executePixelSampled(float output[4], @@ -51,14 +51,14 @@ void ColorMatteOperation::executePixelSampled(float output[4], float inColor[4]; float inKey[4]; - const float hue = m_settings->t1; - const float sat = m_settings->t2; - const float val = m_settings->t3; + const float hue = settings_->t1; + const float sat = settings_->t2; + const float val = settings_->t3; float h_wrap; - m_inputImageProgram->readSampled(inColor, x, y, sampler); - m_inputKeyProgram->readSampled(inKey, x, y, sampler); + inputImageProgram_->readSampled(inColor, x, y, sampler); + inputKeyProgram_->readSampled(inKey, x, y, sampler); /* Store matte(alpha) value in [0] to go with * COM_SetAlphaMultiplyOperation and the Value output. @@ -86,9 +86,9 @@ void ColorMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float hue = m_settings->t1; - const float sat = m_settings->t2; - const float val = m_settings->t3; + const float hue = settings_->t1; + const float sat = settings_->t2; + const float val = settings_->t3; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float *in_color = it.in(0); const float *in_key = it.in(1); diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.h b/source/blender/compositor/operations/COM_ColorMatteOperation.h index f1fc2cf7e95..2037fa0cdc3 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.h +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorMatteOperation : public MultiThreadedOperation { private: - NodeChroma *m_settings; - SocketReader *m_inputImageProgram; - SocketReader *m_inputKeyProgram; + NodeChroma *settings_; + SocketReader *inputImageProgram_; + SocketReader *inputKeyProgram_; public: /** @@ -48,7 +48,7 @@ class ColorMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma) { - m_settings = nodeChroma; + settings_ = nodeChroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.cc b/source/blender/compositor/operations/COM_ColorRampOperation.cc index 274e1cc3f0f..054bdcf5b04 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.cc +++ b/source/blender/compositor/operations/COM_ColorRampOperation.cc @@ -27,13 +27,13 @@ ColorRampOperation::ColorRampOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_inputProgram = nullptr; - m_colorBand = nullptr; + inputProgram_ = nullptr; + colorBand_ = nullptr; this->flags.can_be_constant = true; } void ColorRampOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); } void ColorRampOperation::executePixelSampled(float output[4], @@ -43,13 +43,13 @@ void ColorRampOperation::executePixelSampled(float output[4], { float values[4]; - m_inputProgram->readSampled(values, x, y, sampler); - BKE_colorband_evaluate(m_colorBand, values[0], output); + inputProgram_->readSampled(values, x, y, sampler); + BKE_colorband_evaluate(colorBand_, values[0], output); } void ColorRampOperation::deinitExecution() { - m_inputProgram = nullptr; + inputProgram_ = nullptr; } void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -57,7 +57,7 @@ void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - BKE_colorband_evaluate(m_colorBand, *it.in(0), it.out); + BKE_colorband_evaluate(colorBand_, *it.in(0), it.out); } } diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.h b/source/blender/compositor/operations/COM_ColorRampOperation.h index 21442e52800..3cb5a7d83e1 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.h +++ b/source/blender/compositor/operations/COM_ColorRampOperation.h @@ -28,8 +28,8 @@ class ColorRampOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; - ColorBand *m_colorBand; + SocketReader *inputProgram_; + ColorBand *colorBand_; public: ColorRampOperation(); @@ -51,7 +51,7 @@ class ColorRampOperation : public MultiThreadedOperation { void setColorBand(ColorBand *colorBand) { - m_colorBand = colorBand; + colorBand_ = colorBand; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc index fde769fadc0..45197bf2b1d 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc @@ -27,60 +27,60 @@ ColorSpillOperation::ColorSpillOperation() addInputSocket(DataType::Value); addOutputSocket(DataType::Color); - m_inputImageReader = nullptr; - m_inputFacReader = nullptr; - m_spillChannel = 1; /* GREEN */ - m_spillMethod = 0; + inputImageReader_ = nullptr; + inputFacReader_ = nullptr; + spillChannel_ = 1; /* GREEN */ + spillMethod_ = 0; flags.can_be_constant = true; } void ColorSpillOperation::initExecution() { - m_inputImageReader = this->getInputSocketReader(0); - m_inputFacReader = this->getInputSocketReader(1); - if (m_spillChannel == 0) { - m_rmut = -1.0f; - m_gmut = 1.0f; - m_bmut = 1.0f; - m_channel2 = 1; - m_channel3 = 2; - if (m_settings->unspill == 0) { - m_settings->uspillr = 1.0f; - m_settings->uspillg = 0.0f; - m_settings->uspillb = 0.0f; + inputImageReader_ = this->getInputSocketReader(0); + inputFacReader_ = this->getInputSocketReader(1); + if (spillChannel_ == 0) { + rmut_ = -1.0f; + gmut_ = 1.0f; + bmut_ = 1.0f; + channel2_ = 1; + channel3_ = 2; + if (settings_->unspill == 0) { + settings_->uspillr = 1.0f; + settings_->uspillg = 0.0f; + settings_->uspillb = 0.0f; } } - else if (m_spillChannel == 1) { - m_rmut = 1.0f; - m_gmut = -1.0f; - m_bmut = 1.0f; - m_channel2 = 0; - m_channel3 = 2; - if (m_settings->unspill == 0) { - m_settings->uspillr = 0.0f; - m_settings->uspillg = 1.0f; - m_settings->uspillb = 0.0f; + else if (spillChannel_ == 1) { + rmut_ = 1.0f; + gmut_ = -1.0f; + bmut_ = 1.0f; + channel2_ = 0; + channel3_ = 2; + if (settings_->unspill == 0) { + settings_->uspillr = 0.0f; + settings_->uspillg = 1.0f; + settings_->uspillb = 0.0f; } } else { - m_rmut = 1.0f; - m_gmut = 1.0f; - m_bmut = -1.0f; + rmut_ = 1.0f; + gmut_ = 1.0f; + bmut_ = -1.0f; - m_channel2 = 0; - m_channel3 = 1; - if (m_settings->unspill == 0) { - m_settings->uspillr = 0.0f; - m_settings->uspillg = 0.0f; - m_settings->uspillb = 1.0f; + channel2_ = 0; + channel3_ = 1; + if (settings_->unspill == 0) { + settings_->uspillr = 0.0f; + settings_->uspillg = 0.0f; + settings_->uspillb = 1.0f; } } } void ColorSpillOperation::deinitExecution() { - m_inputImageReader = nullptr; - m_inputFacReader = nullptr; + inputImageReader_ = nullptr; + inputFacReader_ = nullptr; } void ColorSpillOperation::executePixelSampled(float output[4], @@ -90,25 +90,25 @@ void ColorSpillOperation::executePixelSampled(float output[4], { float fac[4]; float input[4]; - m_inputFacReader->readSampled(fac, x, y, sampler); - m_inputImageReader->readSampled(input, x, y, sampler); + inputFacReader_->readSampled(fac, x, y, sampler); + inputImageReader_->readSampled(input, x, y, sampler); float rfac = MIN2(1.0f, fac[0]); float map; - switch (m_spillMethod) { + switch (spillMethod_) { case 0: /* simple */ - map = rfac * (input[m_spillChannel] - (m_settings->limscale * input[m_settings->limchan])); + map = rfac * (input[spillChannel_] - (settings_->limscale * input[settings_->limchan])); break; default: /* average */ - map = rfac * (input[m_spillChannel] - - (m_settings->limscale * AVG(input[m_channel2], input[m_channel3]))); + map = rfac * (input[spillChannel_] - + (settings_->limscale * AVG(input[channel2_], input[channel3_]))); break; } if (map > 0.0f) { - output[0] = input[0] + m_rmut * (m_settings->uspillr * map); - output[1] = input[1] + m_gmut * (m_settings->uspillg * map); - output[2] = input[2] + m_bmut * (m_settings->uspillb * map); + output[0] = input[0] + rmut_ * (settings_->uspillr * map); + output[1] = input[1] + gmut_ * (settings_->uspillg * map); + output[2] = input[2] + bmut_ * (settings_->uspillb * map); output[3] = input[3]; } else { @@ -125,21 +125,20 @@ void ColorSpillOperation::update_memory_buffer_partial(MemoryBuffer *output, const float factor = MIN2(1.0f, *it.in(1)); float map; - switch (m_spillMethod) { + switch (spillMethod_) { case 0: /* simple */ - map = factor * - (color[m_spillChannel] - (m_settings->limscale * color[m_settings->limchan])); + map = factor * (color[spillChannel_] - (settings_->limscale * color[settings_->limchan])); break; default: /* average */ - map = factor * (color[m_spillChannel] - - (m_settings->limscale * AVG(color[m_channel2], color[m_channel3]))); + map = factor * (color[spillChannel_] - + (settings_->limscale * AVG(color[channel2_], color[channel3_]))); break; } if (map > 0.0f) { - it.out[0] = color[0] + m_rmut * (m_settings->uspillr * map); - it.out[1] = color[1] + m_gmut * (m_settings->uspillg * map); - it.out[2] = color[2] + m_bmut * (m_settings->uspillb * map); + it.out[0] = color[0] + rmut_ * (settings_->uspillr * map); + it.out[1] = color[1] + gmut_ * (settings_->uspillg * map); + it.out[2] = color[2] + bmut_ * (settings_->uspillb * map); it.out[3] = color[3]; } else { diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.h b/source/blender/compositor/operations/COM_ColorSpillOperation.h index 453eb8e88d9..aab74e68f41 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.h +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.h @@ -28,14 +28,14 @@ namespace blender::compositor { */ class ColorSpillOperation : public MultiThreadedOperation { protected: - NodeColorspill *m_settings; - SocketReader *m_inputImageReader; - SocketReader *m_inputFacReader; - int m_spillChannel; - int m_spillMethod; - int m_channel2; - int m_channel3; - float m_rmut, m_gmut, m_bmut; + NodeColorspill *settings_; + SocketReader *inputImageReader_; + SocketReader *inputFacReader_; + int spillChannel_; + int spillMethod_; + int channel2_; + int channel3_; + float rmut_, gmut_, bmut_; public: /** @@ -53,15 +53,15 @@ class ColorSpillOperation : public MultiThreadedOperation { void setSettings(NodeColorspill *nodeColorSpill) { - m_settings = nodeColorSpill; + settings_ = nodeColorSpill; } void setSpillChannel(int channel) { - m_spillChannel = channel; + spillChannel_ = channel; } void setSpillMethod(int method) { - m_spillMethod = method; + spillMethod_ = method; } float calculateMapValue(float fac, float *input); diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index 481aca1a68b..029dec8c371 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -32,71 +32,71 @@ CompositorOperation::CompositorOperation() this->addInputSocket(DataType::Value); this->setRenderData(nullptr); - m_outputBuffer = nullptr; - m_depthBuffer = nullptr; - m_imageInput = nullptr; - m_alphaInput = nullptr; - m_depthInput = nullptr; + outputBuffer_ = nullptr; + depthBuffer_ = nullptr; + imageInput_ = nullptr; + alphaInput_ = nullptr; + depthInput_ = nullptr; - m_useAlphaInput = false; - m_active = false; + useAlphaInput_ = false; + active_ = false; - m_scene = nullptr; - m_sceneName[0] = '\0'; - m_viewName = nullptr; + scene_ = nullptr; + sceneName_[0] = '\0'; + viewName_ = nullptr; flags.use_render_border = true; } void CompositorOperation::initExecution() { - if (!m_active) { + if (!active_) { return; } /* When initializing the tree during initial load the width and height can be zero. */ - m_imageInput = getInputSocketReader(0); - m_alphaInput = getInputSocketReader(1); - m_depthInput = getInputSocketReader(2); + imageInput_ = getInputSocketReader(0); + alphaInput_ = getInputSocketReader(1); + depthInput_ = getInputSocketReader(2); if (this->getWidth() * this->getHeight() != 0) { - m_outputBuffer = (float *)MEM_callocN(sizeof(float[4]) * this->getWidth() * this->getHeight(), - "CompositorOperation"); - } - if (m_depthInput != nullptr) { - m_depthBuffer = (float *)MEM_callocN(sizeof(float) * this->getWidth() * this->getHeight(), + outputBuffer_ = (float *)MEM_callocN(sizeof(float[4]) * this->getWidth() * this->getHeight(), "CompositorOperation"); } + if (depthInput_ != nullptr) { + depthBuffer_ = (float *)MEM_callocN(sizeof(float) * this->getWidth() * this->getHeight(), + "CompositorOperation"); + } } void CompositorOperation::deinitExecution() { - if (!m_active) { + if (!active_) { return; } if (!isBraked()) { - Render *re = RE_GetSceneRender(m_scene); + Render *re = RE_GetSceneRender(scene_); RenderResult *rr = RE_AcquireResultWrite(re); if (rr) { - RenderView *rv = RE_RenderViewGetByName(rr, m_viewName); + RenderView *rv = RE_RenderViewGetByName(rr, viewName_); if (rv->rectf != nullptr) { MEM_freeN(rv->rectf); } - rv->rectf = m_outputBuffer; + rv->rectf = outputBuffer_; if (rv->rectz != nullptr) { MEM_freeN(rv->rectz); } - rv->rectz = m_depthBuffer; + rv->rectz = depthBuffer_; rr->have_combined = true; } else { - if (m_outputBuffer) { - MEM_freeN(m_outputBuffer); + if (outputBuffer_) { + MEM_freeN(outputBuffer_); } - if (m_depthBuffer) { - MEM_freeN(m_depthBuffer); + if (depthBuffer_) { + MEM_freeN(depthBuffer_); } } @@ -113,26 +113,26 @@ void CompositorOperation::deinitExecution() BLI_thread_unlock(LOCK_DRAW_IMAGE); } else { - if (m_outputBuffer) { - MEM_freeN(m_outputBuffer); + if (outputBuffer_) { + MEM_freeN(outputBuffer_); } - if (m_depthBuffer) { - MEM_freeN(m_depthBuffer); + if (depthBuffer_) { + MEM_freeN(depthBuffer_); } } - m_outputBuffer = nullptr; - m_depthBuffer = nullptr; - m_imageInput = nullptr; - m_alphaInput = nullptr; - m_depthInput = nullptr; + outputBuffer_ = nullptr; + depthBuffer_ = nullptr; + imageInput_ = nullptr; + alphaInput_ = nullptr; + depthInput_ = nullptr; } void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) { float color[8]; // 7 is enough - float *buffer = m_outputBuffer; - float *zbuffer = m_depthBuffer; + float *buffer = outputBuffer_; + float *zbuffer = depthBuffer_; if (!buffer) { return; @@ -150,7 +150,7 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) int dx = 0, dy = 0; #if 0 - const RenderData *rd = m_rd; + const RenderData *rd = rd_; if (rd->mode & R_BORDER && rd->mode & R_CROP) { /** @@ -192,14 +192,14 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) for (x = x1; x < x2 && (!breaked); x++) { int input_x = x + dx, input_y = y + dy; - m_imageInput->readSampled(color, input_x, input_y, PixelSampler::Nearest); - if (m_useAlphaInput) { - m_alphaInput->readSampled(&(color[3]), input_x, input_y, PixelSampler::Nearest); + imageInput_->readSampled(color, input_x, input_y, PixelSampler::Nearest); + if (useAlphaInput_) { + alphaInput_->readSampled(&(color[3]), input_x, input_y, PixelSampler::Nearest); } copy_v4_v4(buffer + offset4, color); - m_depthInput->readSampled(color, input_x, input_y, PixelSampler::Nearest); + depthInput_->readSampled(color, input_x, input_y, PixelSampler::Nearest); zbuffer[offset] = color[0]; offset4 += COM_DATA_TYPE_COLOR_CHANNELS; offset++; @@ -216,26 +216,26 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp const rcti &area, Span inputs) { - if (!m_outputBuffer) { + if (!outputBuffer_) { return; } - MemoryBuffer output_buf(m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); + MemoryBuffer output_buf(outputBuffer_, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); output_buf.copy_from(inputs[0], area); - if (m_useAlphaInput) { + if (useAlphaInput_) { output_buf.copy_from(inputs[1], area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3); } - MemoryBuffer depth_buf(m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); + MemoryBuffer depth_buf(depthBuffer_, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); depth_buf.copy_from(inputs[2], area); } void CompositorOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - int width = m_rd->xsch * m_rd->size / 100; - int height = m_rd->ysch * m_rd->size / 100; + int width = rd_->xsch * rd_->size / 100; + int height = rd_->ysch * rd_->size / 100; /* Check actual render resolution with cropping it may differ with cropped border.rendering * Fix for T31777 Border Crop gives black (easy). */ - Render *re = RE_GetSceneRender(m_scene); + Render *re = RE_GetSceneRender(scene_); if (re) { RenderResult *rr = RE_AcquireResultRead(re); if (rr) { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index c63613b7b35..e9539ae66fd 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -29,79 +29,79 @@ namespace blender::compositor { */ class CompositorOperation : public MultiThreadedOperation { private: - const struct Scene *m_scene; + const struct Scene *scene_; /** * \brief Scene name, used for getting the render output, includes 'SC' prefix. */ - char m_sceneName[MAX_ID_NAME]; + char sceneName_[MAX_ID_NAME]; /** * \brief local reference to the scene */ - const RenderData *m_rd; + const RenderData *rd_; /** * \brief reference to the output float buffer */ - float *m_outputBuffer; + float *outputBuffer_; /** * \brief reference to the output depth float buffer */ - float *m_depthBuffer; + float *depthBuffer_; /** * \brief local reference to the input image operation */ - SocketReader *m_imageInput; + SocketReader *imageInput_; /** * \brief local reference to the input alpha operation */ - SocketReader *m_alphaInput; + SocketReader *alphaInput_; /** * \brief local reference to the depth operation */ - SocketReader *m_depthInput; + SocketReader *depthInput_; /** * \brief Ignore any alpha input */ - bool m_useAlphaInput; + bool useAlphaInput_; /** * \brief operation is active for calculating final compo result */ - bool m_active; + bool active_; /** * \brief View name, used for multiview */ - const char *m_viewName; + const char *viewName_; public: CompositorOperation(); bool isActiveCompositorOutput() const { - return m_active; + return active_; } void executeRegion(rcti *rect, unsigned int tileNumber) override; void setScene(const struct Scene *scene) { - m_scene = scene; + scene_ = scene; } void setSceneName(const char *sceneName) { - BLI_strncpy(m_sceneName, sceneName, sizeof(m_sceneName)); + BLI_strncpy(sceneName_, sceneName, sizeof(sceneName_)); } void setViewName(const char *viewName) { - m_viewName = viewName; + viewName_ = viewName; } void setRenderData(const RenderData *rd) { - m_rd = rd; + rd_ = rd; } bool isOutputOperation(bool /*rendering*/) const override { @@ -116,11 +116,11 @@ class CompositorOperation : public MultiThreadedOperation { void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void setUseAlphaInput(bool value) { - m_useAlphaInput = value; + useAlphaInput_ = value; } void setActive(bool active) { - m_active = active; + active_ = active; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc index 09a1541985b..4f78c919695 100644 --- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc @@ -26,13 +26,13 @@ ConvertColorProfileOperation::ConvertColorProfileOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_inputOperation = nullptr; - m_predivided = false; + inputOperation_ = nullptr; + predivided_ = false; } void ConvertColorProfileOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); + inputOperation_ = this->getInputSocketReader(0); } void ConvertColorProfileOperation::executePixelSampled(float output[4], @@ -41,14 +41,13 @@ void ConvertColorProfileOperation::executePixelSampled(float output[4], PixelSampler sampler) { float color[4]; - m_inputOperation->readSampled(color, x, y, sampler); - IMB_buffer_float_from_float( - output, color, 4, m_toProfile, m_fromProfile, m_predivided, 1, 1, 0, 0); + inputOperation_->readSampled(color, x, y, sampler); + IMB_buffer_float_from_float(output, color, 4, toProfile_, fromProfile_, predivided_, 1, 1, 0, 0); } void ConvertColorProfileOperation::deinitExecution() { - m_inputOperation = nullptr; + inputOperation_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h index 6d7b19aa859..532fad86091 100644 --- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h +++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h @@ -31,22 +31,22 @@ class ConvertColorProfileOperation : public NodeOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputOperation; + SocketReader *inputOperation_; /** * \brief color profile where to convert from */ - int m_fromProfile; + int fromProfile_; /** * \brief color profile where to convert to */ - int m_toProfile; + int toProfile_; /** * \brief is color predivided */ - bool m_predivided; + bool predivided_; public: /** @@ -71,15 +71,15 @@ class ConvertColorProfileOperation : public NodeOperation { void setFromColorProfile(int colorProfile) { - m_fromProfile = colorProfile; + fromProfile_ = colorProfile; } void setToColorProfile(int colorProfile) { - m_toProfile = colorProfile; + toProfile_ = colorProfile; } void setPredivided(bool predivided) { - m_predivided = predivided; + predivided_ = predivided; } }; diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc index ee2a62f6147..fcd9d26c3f0 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc @@ -26,19 +26,19 @@ ConvertDepthToRadiusOperation::ConvertDepthToRadiusOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_inputOperation = nullptr; - m_fStop = 128.0f; - m_cameraObject = nullptr; - m_maxRadius = 32.0f; - m_blurPostOperation = nullptr; + inputOperation_ = nullptr; + fStop_ = 128.0f; + cameraObject_ = nullptr; + maxRadius_ = 32.0f; + blurPostOperation_ = nullptr; } float ConvertDepthToRadiusOperation::determineFocalDistance() { - if (m_cameraObject && m_cameraObject->type == OB_CAMERA) { - Camera *camera = (Camera *)m_cameraObject->data; - m_cam_lens = camera->lens; - return BKE_camera_object_dof_distance(m_cameraObject); + if (cameraObject_ && cameraObject_->type == OB_CAMERA) { + Camera *camera = (Camera *)cameraObject_->data; + cam_lens_ = camera->lens; + return BKE_camera_object_dof_distance(cameraObject_); } return 10.0f; @@ -49,27 +49,27 @@ void ConvertDepthToRadiusOperation::initExecution() float cam_sensor = DEFAULT_SENSOR_WIDTH; Camera *camera = nullptr; - if (m_cameraObject && m_cameraObject->type == OB_CAMERA) { - camera = (Camera *)m_cameraObject->data; + if (cameraObject_ && cameraObject_->type == OB_CAMERA) { + camera = (Camera *)cameraObject_->data; cam_sensor = BKE_camera_sensor_size(camera->sensor_fit, camera->sensor_x, camera->sensor_y); } - m_inputOperation = this->getInputSocketReader(0); + inputOperation_ = this->getInputSocketReader(0); float focalDistance = determineFocalDistance(); if (focalDistance == 0.0f) { focalDistance = 1e10f; /* If the DOF is 0.0 then set it to be far away. */ } - m_inverseFocalDistance = 1.0f / focalDistance; - m_aspect = (this->getWidth() > this->getHeight()) ? - (this->getHeight() / (float)this->getWidth()) : - (this->getWidth() / (float)this->getHeight()); - m_aperture = 0.5f * (m_cam_lens / (m_aspect * cam_sensor)) / m_fStop; + inverseFocalDistance_ = 1.0f / focalDistance; + aspect_ = (this->getWidth() > this->getHeight()) ? + (this->getHeight() / (float)this->getWidth()) : + (this->getWidth() / (float)this->getHeight()); + aperture_ = 0.5f * (cam_lens_ / (aspect_ * cam_sensor)) / fStop_; const float minsz = MIN2(getWidth(), getHeight()); - m_dof_sp = minsz / ((cam_sensor / 2.0f) / - m_cam_lens); /* <- == `aspect * MIN2(img->x, img->y) / tan(0.5f * fov)` */ + dof_sp_ = minsz / ((cam_sensor / 2.0f) / + cam_lens_); /* <- == `aspect * MIN2(img->x, img->y) / tan(0.5f * fov)` */ - if (m_blurPostOperation) { - m_blurPostOperation->setSigma(MIN2(m_aperture * 128.0f, m_maxRadius)); + if (blurPostOperation_) { + blurPostOperation_->setSigma(MIN2(aperture_ * 128.0f, maxRadius_)); } } @@ -81,7 +81,7 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], float inputValue[4]; float z; float radius; - m_inputOperation->readSampled(inputValue, x, y, sampler); + inputOperation_->readSampled(inputValue, x, y, sampler); z = inputValue[0]; if (z != 0.0f) { float iZ = (1.0f / z); @@ -92,14 +92,14 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], /* Scale crad back to original maximum and blend. */ crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad); #endif - radius = 0.5f * fabsf(m_aperture * (m_dof_sp * (m_inverseFocalDistance - iZ) - 1.0f)); + radius = 0.5f * fabsf(aperture_ * (dof_sp_ * (inverseFocalDistance_ - iZ) - 1.0f)); /* 'bug' T6615, limit minimum radius to 1 pixel, * not really a solution, but somewhat mitigates the problem. */ if (radius < 0.0f) { radius = 0.0f; } - if (radius > m_maxRadius) { - radius = m_maxRadius; + if (radius > maxRadius_) { + radius = maxRadius_; } output[0] = radius; } @@ -110,7 +110,7 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], void ConvertDepthToRadiusOperation::deinitExecution() { - m_inputOperation = nullptr; + inputOperation_ = nullptr; } void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -133,10 +133,10 @@ void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *o * `crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad);` */ #endif const float radius = 0.5f * - fabsf(m_aperture * (m_dof_sp * (m_inverseFocalDistance - inv_z) - 1.0f)); + fabsf(aperture_ * (dof_sp_ * (inverseFocalDistance_ - inv_z) - 1.0f)); /* Bug T6615, limit minimum radius to 1 pixel, * not really a solution, but somewhat mitigates the problem. */ - *it.out = CLAMPIS(radius, 0.0f, m_maxRadius); + *it.out = CLAMPIS(radius, 0.0f, maxRadius_); } } diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h index 746cb89b6c0..d3beff60207 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h @@ -33,17 +33,17 @@ class ConvertDepthToRadiusOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputOperation; - float m_fStop; - float m_aspect; - float m_maxRadius; - float m_inverseFocalDistance; - float m_aperture; - float m_cam_lens; - float m_dof_sp; - Object *m_cameraObject; + SocketReader *inputOperation_; + float fStop_; + float aspect_; + float maxRadius_; + float inverseFocalDistance_; + float aperture_; + float cam_lens_; + float dof_sp_; + Object *cameraObject_; - FastGaussianBlurValueOperation *m_blurPostOperation; + FastGaussianBlurValueOperation *blurPostOperation_; public: /** @@ -68,20 +68,20 @@ class ConvertDepthToRadiusOperation : public MultiThreadedOperation { void setfStop(float fStop) { - m_fStop = fStop; + fStop_ = fStop; } void setMaxRadius(float maxRadius) { - m_maxRadius = maxRadius; + maxRadius_ = maxRadius; } void setCameraObject(Object *camera) { - m_cameraObject = camera; + cameraObject_ = camera; } float determineFocalDistance(); void setPostBlur(FastGaussianBlurValueOperation *operation) { - m_blurPostOperation = operation; + blurPostOperation_ = operation; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 1cef0490ff5..88de54e8c41 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -26,18 +26,18 @@ namespace blender::compositor { ConvertBaseOperation::ConvertBaseOperation() { - m_inputOperation = nullptr; + inputOperation_ = nullptr; this->flags.can_be_constant = true; } void ConvertBaseOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); + inputOperation_ = this->getInputSocketReader(0); } void ConvertBaseOperation::deinitExecution() { - m_inputOperation = nullptr; + inputOperation_ = nullptr; } void ConvertBaseOperation::hash_output_params() @@ -66,7 +66,7 @@ void ConvertValueToColorOperation::executePixelSampled(float output[4], PixelSampler sampler) { float value; - m_inputOperation->readSampled(&value, x, y, sampler); + inputOperation_->readSampled(&value, x, y, sampler); output[0] = output[1] = output[2] = value; output[3] = 1.0f; } @@ -93,7 +93,7 @@ void ConvertColorToValueOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - m_inputOperation->readSampled(inputColor, x, y, sampler); + inputOperation_->readSampled(inputColor, x, y, sampler); output[0] = (inputColor[0] + inputColor[1] + inputColor[2]) / 3.0f; } @@ -119,7 +119,7 @@ void ConvertColorToBWOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - m_inputOperation->readSampled(inputColor, x, y, sampler); + inputOperation_->readSampled(inputColor, x, y, sampler); output[0] = IMB_colormanagement_get_luminance(inputColor); } @@ -144,7 +144,7 @@ void ConvertColorToVectorOperation::executePixelSampled(float output[4], PixelSampler sampler) { float color[4]; - m_inputOperation->readSampled(color, x, y, sampler); + inputOperation_->readSampled(color, x, y, sampler); copy_v3_v3(output, color); } @@ -169,7 +169,7 @@ void ConvertValueToVectorOperation::executePixelSampled(float output[4], PixelSampler sampler) { float value; - m_inputOperation->readSampled(&value, x, y, sampler); + inputOperation_->readSampled(&value, x, y, sampler); output[0] = output[1] = output[2] = value; } @@ -193,7 +193,7 @@ void ConvertVectorToColorOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - m_inputOperation->readSampled(output, x, y, sampler); + inputOperation_->readSampled(output, x, y, sampler); output[3] = 1.0f; } @@ -219,7 +219,7 @@ void ConvertVectorToValueOperation::executePixelSampled(float output[4], PixelSampler sampler) { float input[4]; - m_inputOperation->readSampled(input, x, y, sampler); + inputOperation_->readSampled(input, x, y, sampler); output[0] = (input[0] + input[1] + input[2]) / 3.0f; } @@ -243,14 +243,14 @@ void ConvertRGBToYCCOperation::setMode(int mode) { switch (mode) { case 0: - m_mode = BLI_YCC_ITU_BT601; + mode_ = BLI_YCC_ITU_BT601; break; case 2: - m_mode = BLI_YCC_JFIF_0_255; + mode_ = BLI_YCC_JFIF_0_255; break; case 1: default: - m_mode = BLI_YCC_ITU_BT709; + mode_ = BLI_YCC_ITU_BT709; break; } } @@ -263,9 +263,8 @@ void ConvertRGBToYCCOperation::executePixelSampled(float output[4], float inputColor[4]; float color[3]; - m_inputOperation->readSampled(inputColor, x, y, sampler); - rgb_to_ycc( - inputColor[0], inputColor[1], inputColor[2], &color[0], &color[1], &color[2], m_mode); + inputOperation_->readSampled(inputColor, x, y, sampler); + rgb_to_ycc(inputColor[0], inputColor[1], inputColor[2], &color[0], &color[1], &color[2], mode_); /* divided by 255 to normalize for viewing in */ /* R,G,B --> Y,Cb,Cr */ @@ -276,14 +275,14 @@ void ConvertRGBToYCCOperation::executePixelSampled(float output[4], void ConvertRGBToYCCOperation::hash_output_params() { ConvertBaseOperation::hash_output_params(); - hash_param(m_mode); + hash_param(mode_); } void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIterator &it) { for (; !it.is_end(); ++it) { const float *in = it.in(0); - rgb_to_ycc(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], m_mode); + rgb_to_ycc(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], mode_); /* Normalize for viewing (#rgb_to_ycc returns 0-255 values). */ mul_v3_fl(it.out, 1.0f / 255.0f); @@ -303,14 +302,14 @@ void ConvertYCCToRGBOperation::setMode(int mode) { switch (mode) { case 0: - m_mode = BLI_YCC_ITU_BT601; + mode_ = BLI_YCC_ITU_BT601; break; case 2: - m_mode = BLI_YCC_JFIF_0_255; + mode_ = BLI_YCC_JFIF_0_255; break; case 1: default: - m_mode = BLI_YCC_ITU_BT709; + mode_ = BLI_YCC_ITU_BT709; break; } } @@ -321,26 +320,21 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - m_inputOperation->readSampled(inputColor, x, y, sampler); + inputOperation_->readSampled(inputColor, x, y, sampler); /* need to un-normalize the data */ /* R,G,B --> Y,Cb,Cr */ mul_v3_fl(inputColor, 255.0f); - ycc_to_rgb(inputColor[0], - inputColor[1], - inputColor[2], - &output[0], - &output[1], - &output[2], - m_mode); + ycc_to_rgb( + inputColor[0], inputColor[1], inputColor[2], &output[0], &output[1], &output[2], mode_); output[3] = inputColor[3]; } void ConvertYCCToRGBOperation::hash_output_params() { ConvertBaseOperation::hash_output_params(); - hash_param(m_mode); + hash_param(mode_); } void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -348,13 +342,8 @@ void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIteratorreadSampled(inputColor, x, y, sampler); + inputOperation_->readSampled(inputColor, x, y, sampler); rgb_to_yuv(inputColor[0], inputColor[1], inputColor[2], @@ -407,7 +396,7 @@ void ConvertYUVToRGBOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - m_inputOperation->readSampled(inputColor, x, y, sampler); + inputOperation_->readSampled(inputColor, x, y, sampler); yuv_to_rgb(inputColor[0], inputColor[1], inputColor[2], @@ -441,7 +430,7 @@ void ConvertRGBToHSVOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - m_inputOperation->readSampled(inputColor, x, y, sampler); + inputOperation_->readSampled(inputColor, x, y, sampler); rgb_to_hsv_v(inputColor, output); output[3] = inputColor[3]; } @@ -469,7 +458,7 @@ void ConvertHSVToRGBOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - m_inputOperation->readSampled(inputColor, x, y, sampler); + inputOperation_->readSampled(inputColor, x, y, sampler); hsv_to_rgb_v(inputColor, output); output[0] = max_ff(output[0], 0.0f); output[1] = max_ff(output[1], 0.0f); @@ -503,7 +492,7 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4], PixelSampler sampler) { ColorSceneLinear4f input; - m_inputOperation->readSampled(input, x, y, sampler); + inputOperation_->readSampled(input, x, y, sampler); ColorSceneLinear4f converted = input.unpremultiply_alpha(); copy_v4_v4(output, converted); } @@ -529,7 +518,7 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4], PixelSampler sampler) { ColorSceneLinear4f input; - m_inputOperation->readSampled(input, x, y, sampler); + inputOperation_->readSampled(input, x, y, sampler); ColorSceneLinear4f converted = input.premultiply_alpha(); copy_v4_v4(output, converted); } @@ -547,16 +536,16 @@ SeparateChannelOperation::SeparateChannelOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Value); - m_inputOperation = nullptr; + inputOperation_ = nullptr; } void SeparateChannelOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); + inputOperation_ = this->getInputSocketReader(0); } void SeparateChannelOperation::deinitExecution() { - m_inputOperation = nullptr; + inputOperation_ = nullptr; } void SeparateChannelOperation::executePixelSampled(float output[4], @@ -565,8 +554,8 @@ void SeparateChannelOperation::executePixelSampled(float output[4], PixelSampler sampler) { float input[4]; - m_inputOperation->readSampled(input, x, y, sampler); - output[0] = input[m_channel]; + inputOperation_->readSampled(input, x, y, sampler); + output[0] = input[channel_]; } void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -574,7 +563,7 @@ void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output Span inputs) { for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - it.out[0] = it.in(0)[m_channel]; + it.out[0] = it.in(0)[channel_]; } } @@ -588,26 +577,26 @@ CombineChannelsOperation::CombineChannelsOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - m_inputChannel1Operation = nullptr; - m_inputChannel2Operation = nullptr; - m_inputChannel3Operation = nullptr; - m_inputChannel4Operation = nullptr; + inputChannel1Operation_ = nullptr; + inputChannel2Operation_ = nullptr; + inputChannel3Operation_ = nullptr; + inputChannel4Operation_ = nullptr; } void CombineChannelsOperation::initExecution() { - m_inputChannel1Operation = this->getInputSocketReader(0); - m_inputChannel2Operation = this->getInputSocketReader(1); - m_inputChannel3Operation = this->getInputSocketReader(2); - m_inputChannel4Operation = this->getInputSocketReader(3); + inputChannel1Operation_ = this->getInputSocketReader(0); + inputChannel2Operation_ = this->getInputSocketReader(1); + inputChannel3Operation_ = this->getInputSocketReader(2); + inputChannel4Operation_ = this->getInputSocketReader(3); } void CombineChannelsOperation::deinitExecution() { - m_inputChannel1Operation = nullptr; - m_inputChannel2Operation = nullptr; - m_inputChannel3Operation = nullptr; - m_inputChannel4Operation = nullptr; + inputChannel1Operation_ = nullptr; + inputChannel2Operation_ = nullptr; + inputChannel3Operation_ = nullptr; + inputChannel4Operation_ = nullptr; } void CombineChannelsOperation::executePixelSampled(float output[4], @@ -616,20 +605,20 @@ void CombineChannelsOperation::executePixelSampled(float output[4], PixelSampler sampler) { float input[4]; - if (m_inputChannel1Operation) { - m_inputChannel1Operation->readSampled(input, x, y, sampler); + if (inputChannel1Operation_) { + inputChannel1Operation_->readSampled(input, x, y, sampler); output[0] = input[0]; } - if (m_inputChannel2Operation) { - m_inputChannel2Operation->readSampled(input, x, y, sampler); + if (inputChannel2Operation_) { + inputChannel2Operation_->readSampled(input, x, y, sampler); output[1] = input[0]; } - if (m_inputChannel3Operation) { - m_inputChannel3Operation->readSampled(input, x, y, sampler); + if (inputChannel3Operation_) { + inputChannel3Operation_->readSampled(input, x, y, sampler); output[2] = input[0]; } - if (m_inputChannel4Operation) { - m_inputChannel4Operation->readSampled(input, x, y, sampler); + if (inputChannel4Operation_) { + inputChannel4Operation_->readSampled(input, x, y, sampler); output[3] = input[0]; } } diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h index cd8e3016e97..9d4539c8faa 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.h +++ b/source/blender/compositor/operations/COM_ConvertOperation.h @@ -24,7 +24,7 @@ namespace blender::compositor { class ConvertBaseOperation : public MultiThreadedOperation { protected: - SocketReader *m_inputOperation; + SocketReader *inputOperation_; public: ConvertBaseOperation(); @@ -114,7 +114,7 @@ class ConvertVectorToValueOperation : public ConvertBaseOperation { class ConvertRGBToYCCOperation : public ConvertBaseOperation { private: /** YCbCr mode (Jpeg, ITU601, ITU709) */ - int m_mode; + int mode_; public: ConvertRGBToYCCOperation(); @@ -132,7 +132,7 @@ class ConvertRGBToYCCOperation : public ConvertBaseOperation { class ConvertYCCToRGBOperation : public ConvertBaseOperation { private: /** YCbCr mode (Jpeg, ITU601, ITU709) */ - int m_mode; + int mode_; public: ConvertYCCToRGBOperation(); @@ -209,8 +209,8 @@ class ConvertStraightToPremulOperation : public ConvertBaseOperation { class SeparateChannelOperation : public MultiThreadedOperation { private: - SocketReader *m_inputOperation; - int m_channel; + SocketReader *inputOperation_; + int channel_; public: SeparateChannelOperation(); @@ -221,7 +221,7 @@ class SeparateChannelOperation : public MultiThreadedOperation { void setChannel(int channel) { - m_channel = channel; + channel_ = channel; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -231,10 +231,10 @@ class SeparateChannelOperation : public MultiThreadedOperation { class CombineChannelsOperation : public MultiThreadedOperation { private: - SocketReader *m_inputChannel1Operation; - SocketReader *m_inputChannel2Operation; - SocketReader *m_inputChannel3Operation; - SocketReader *m_inputChannel4Operation; + SocketReader *inputChannel1Operation_; + SocketReader *inputChannel2Operation_; + SocketReader *inputChannel3Operation_; + SocketReader *inputChannel4Operation_; public: CombineChannelsOperation(); diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc index b768a5e80ba..343cc32b625 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc @@ -38,44 +38,44 @@ void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, CLAMP(y3, 0, getHeight() - 1); float value[4]; - m_inputValueOperation->read(value, x2, y2, nullptr); + inputValueOperation_->read(value, x2, y2, nullptr); float mval = 1.0f - value[0]; - m_inputOperation->read(in1, x1, y1, nullptr); - madd_v3_v3fl(res1, in1, m_filter[0]); - madd_v3_v3fl(res2, in1, m_filter[0]); + inputOperation_->read(in1, x1, y1, nullptr); + madd_v3_v3fl(res1, in1, filter_[0]); + madd_v3_v3fl(res2, in1, filter_[0]); - m_inputOperation->read(in1, x2, y1, nullptr); - madd_v3_v3fl(res1, in1, m_filter[1]); - madd_v3_v3fl(res2, in1, m_filter[3]); + inputOperation_->read(in1, x2, y1, nullptr); + madd_v3_v3fl(res1, in1, filter_[1]); + madd_v3_v3fl(res2, in1, filter_[3]); - m_inputOperation->read(in1, x3, y1, nullptr); - madd_v3_v3fl(res1, in1, m_filter[2]); - madd_v3_v3fl(res2, in1, m_filter[6]); + inputOperation_->read(in1, x3, y1, nullptr); + madd_v3_v3fl(res1, in1, filter_[2]); + madd_v3_v3fl(res2, in1, filter_[6]); - m_inputOperation->read(in1, x1, y2, nullptr); - madd_v3_v3fl(res1, in1, m_filter[3]); - madd_v3_v3fl(res2, in1, m_filter[1]); + inputOperation_->read(in1, x1, y2, nullptr); + madd_v3_v3fl(res1, in1, filter_[3]); + madd_v3_v3fl(res2, in1, filter_[1]); - m_inputOperation->read(in2, x2, y2, nullptr); - madd_v3_v3fl(res1, in2, m_filter[4]); - madd_v3_v3fl(res2, in2, m_filter[4]); + inputOperation_->read(in2, x2, y2, nullptr); + madd_v3_v3fl(res1, in2, filter_[4]); + madd_v3_v3fl(res2, in2, filter_[4]); - m_inputOperation->read(in1, x3, y2, nullptr); - madd_v3_v3fl(res1, in1, m_filter[5]); - madd_v3_v3fl(res2, in1, m_filter[7]); + inputOperation_->read(in1, x3, y2, nullptr); + madd_v3_v3fl(res1, in1, filter_[5]); + madd_v3_v3fl(res2, in1, filter_[7]); - m_inputOperation->read(in1, x1, y3, nullptr); - madd_v3_v3fl(res1, in1, m_filter[6]); - madd_v3_v3fl(res2, in1, m_filter[2]); + inputOperation_->read(in1, x1, y3, nullptr); + madd_v3_v3fl(res1, in1, filter_[6]); + madd_v3_v3fl(res2, in1, filter_[2]); - m_inputOperation->read(in1, x2, y3, nullptr); - madd_v3_v3fl(res1, in1, m_filter[7]); - madd_v3_v3fl(res2, in1, m_filter[5]); + inputOperation_->read(in1, x2, y3, nullptr); + madd_v3_v3fl(res1, in1, filter_[7]); + madd_v3_v3fl(res2, in1, filter_[5]); - m_inputOperation->read(in1, x3, y3, nullptr); - madd_v3_v3fl(res1, in1, m_filter[8]); - madd_v3_v3fl(res2, in1, m_filter[8]); + inputOperation_->read(in1, x3, y3, nullptr); + madd_v3_v3fl(res1, in1, filter_[8]); + madd_v3_v3fl(res2, in1, filter_[8]); output[0] = sqrt(res1[0] * res1[0] + res2[0] * res2[0]); output[1] = sqrt(res1[1] * res1[1] + res2[1] * res2[1]); @@ -112,44 +112,44 @@ void ConvolutionEdgeFilterOperation::update_memory_buffer_partial(MemoryBuffer * float res2[4] = {0}; const float *color = center_color + down_offset + left_offset; - madd_v3_v3fl(res1, color, m_filter[0]); + madd_v3_v3fl(res1, color, filter_[0]); copy_v3_v3(res2, res1); color = center_color + down_offset; - madd_v3_v3fl(res1, color, m_filter[1]); - madd_v3_v3fl(res2, color, m_filter[3]); + madd_v3_v3fl(res1, color, filter_[1]); + madd_v3_v3fl(res2, color, filter_[3]); color = center_color + down_offset + right_offset; - madd_v3_v3fl(res1, color, m_filter[2]); - madd_v3_v3fl(res2, color, m_filter[6]); + madd_v3_v3fl(res1, color, filter_[2]); + madd_v3_v3fl(res2, color, filter_[6]); color = center_color + left_offset; - madd_v3_v3fl(res1, color, m_filter[3]); - madd_v3_v3fl(res2, color, m_filter[1]); + madd_v3_v3fl(res1, color, filter_[3]); + madd_v3_v3fl(res2, color, filter_[1]); { float rgb_filtered[3]; - mul_v3_v3fl(rgb_filtered, center_color, m_filter[4]); + mul_v3_v3fl(rgb_filtered, center_color, filter_[4]); add_v3_v3(res1, rgb_filtered); add_v3_v3(res2, rgb_filtered); } color = center_color + right_offset; - madd_v3_v3fl(res1, color, m_filter[5]); - madd_v3_v3fl(res2, color, m_filter[7]); + madd_v3_v3fl(res1, color, filter_[5]); + madd_v3_v3fl(res2, color, filter_[7]); color = center_color + up_offset + left_offset; - madd_v3_v3fl(res1, color, m_filter[6]); - madd_v3_v3fl(res2, color, m_filter[2]); + madd_v3_v3fl(res1, color, filter_[6]); + madd_v3_v3fl(res2, color, filter_[2]); color = center_color + up_offset; - madd_v3_v3fl(res1, color, m_filter[7]); - madd_v3_v3fl(res2, color, m_filter[5]); + madd_v3_v3fl(res1, color, filter_[7]); + madd_v3_v3fl(res2, color, filter_[5]); { color = center_color + up_offset + right_offset; float rgb_filtered[3]; - mul_v3_v3fl(rgb_filtered, color, m_filter[8]); + mul_v3_v3fl(rgb_filtered, color, filter_[8]); add_v3_v3(res1, rgb_filtered); add_v3_v3(res2, rgb_filtered); } @@ -159,10 +159,10 @@ void ConvolutionEdgeFilterOperation::update_memory_buffer_partial(MemoryBuffer * it.out[2] = sqrt(res1[2] * res1[2] + res2[2] * res2[2]); const float factor = *it.in(FACTOR_INPUT_INDEX); - const float m_factor = 1.0f - factor; - it.out[0] = it.out[0] * factor + center_color[0] * m_factor; - it.out[1] = it.out[1] * factor + center_color[1] * m_factor; - it.out[2] = it.out[2] * factor + center_color[2] * m_factor; + const float factor_ = 1.0f - factor; + it.out[0] = it.out[0] * factor + center_color[0] * factor_; + it.out[1] = it.out[1] * factor + center_color[1] * factor_; + it.out[2] = it.out[2] * factor + center_color[2] * factor_; it.out[3] = center_color[3]; diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc index f438f01e11a..a4ef88e7a33 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc @@ -26,35 +26,35 @@ ConvolutionFilterOperation::ConvolutionFilterOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - m_inputOperation = nullptr; + inputOperation_ = nullptr; this->flags.complex = true; } void ConvolutionFilterOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); - m_inputValueOperation = this->getInputSocketReader(1); + inputOperation_ = this->getInputSocketReader(0); + inputValueOperation_ = this->getInputSocketReader(1); } void ConvolutionFilterOperation::set3x3Filter( float f1, float f2, float f3, float f4, float f5, float f6, float f7, float f8, float f9) { - m_filter[0] = f1; - m_filter[1] = f2; - m_filter[2] = f3; - m_filter[3] = f4; - m_filter[4] = f5; - m_filter[5] = f6; - m_filter[6] = f7; - m_filter[7] = f8; - m_filter[8] = f9; - m_filterHeight = 3; - m_filterWidth = 3; + filter_[0] = f1; + filter_[1] = f2; + filter_[2] = f3; + filter_[3] = f4; + filter_[4] = f5; + filter_[5] = f6; + filter_[6] = f7; + filter_[7] = f8; + filter_[8] = f9; + filterHeight_ = 3; + filterWidth_ = 3; } void ConvolutionFilterOperation::deinitExecution() { - m_inputOperation = nullptr; - m_inputValueOperation = nullptr; + inputOperation_ = nullptr; + inputValueOperation_ = nullptr; } void ConvolutionFilterOperation::executePixel(float output[4], int x, int y, void * /*data*/) @@ -74,28 +74,28 @@ void ConvolutionFilterOperation::executePixel(float output[4], int x, int y, voi CLAMP(y2, 0, getHeight() - 1); CLAMP(y3, 0, getHeight() - 1); float value[4]; - m_inputValueOperation->read(value, x2, y2, nullptr); + inputValueOperation_->read(value, x2, y2, nullptr); const float mval = 1.0f - value[0]; zero_v4(output); - m_inputOperation->read(in1, x1, y1, nullptr); - madd_v4_v4fl(output, in1, m_filter[0]); - m_inputOperation->read(in1, x2, y1, nullptr); - madd_v4_v4fl(output, in1, m_filter[1]); - m_inputOperation->read(in1, x3, y1, nullptr); - madd_v4_v4fl(output, in1, m_filter[2]); - m_inputOperation->read(in1, x1, y2, nullptr); - madd_v4_v4fl(output, in1, m_filter[3]); - m_inputOperation->read(in2, x2, y2, nullptr); - madd_v4_v4fl(output, in2, m_filter[4]); - m_inputOperation->read(in1, x3, y2, nullptr); - madd_v4_v4fl(output, in1, m_filter[5]); - m_inputOperation->read(in1, x1, y3, nullptr); - madd_v4_v4fl(output, in1, m_filter[6]); - m_inputOperation->read(in1, x2, y3, nullptr); - madd_v4_v4fl(output, in1, m_filter[7]); - m_inputOperation->read(in1, x3, y3, nullptr); - madd_v4_v4fl(output, in1, m_filter[8]); + inputOperation_->read(in1, x1, y1, nullptr); + madd_v4_v4fl(output, in1, filter_[0]); + inputOperation_->read(in1, x2, y1, nullptr); + madd_v4_v4fl(output, in1, filter_[1]); + inputOperation_->read(in1, x3, y1, nullptr); + madd_v4_v4fl(output, in1, filter_[2]); + inputOperation_->read(in1, x1, y2, nullptr); + madd_v4_v4fl(output, in1, filter_[3]); + inputOperation_->read(in2, x2, y2, nullptr); + madd_v4_v4fl(output, in2, filter_[4]); + inputOperation_->read(in1, x3, y2, nullptr); + madd_v4_v4fl(output, in1, filter_[5]); + inputOperation_->read(in1, x1, y3, nullptr); + madd_v4_v4fl(output, in1, filter_[6]); + inputOperation_->read(in1, x2, y3, nullptr); + madd_v4_v4fl(output, in1, filter_[7]); + inputOperation_->read(in1, x3, y3, nullptr); + madd_v4_v4fl(output, in1, filter_[8]); output[0] = output[0] * value[0] + in2[0] * mval; output[1] = output[1] * value[0] + in2[1] * mval; @@ -113,8 +113,8 @@ bool ConvolutionFilterOperation::determineDependingAreaOfInterest( rcti *input, ReadBufferOperation *readOperation, rcti *output) { rcti newInput; - int addx = (m_filterWidth - 1) / 2 + 1; - int addy = (m_filterHeight - 1) / 2 + 1; + int addx = (filterWidth_ - 1) / 2 + 1; + int addy = (filterHeight_ - 1) / 2 + 1; newInput.xmax = input->xmax + addx; newInput.xmin = input->xmin - addx; newInput.ymax = input->ymax + addy; @@ -129,8 +129,8 @@ void ConvolutionFilterOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - const int add_x = (m_filterWidth - 1) / 2 + 1; - const int add_y = (m_filterHeight - 1) / 2 + 1; + const int add_x = (filterWidth_ - 1) / 2 + 1; + const int add_y = (filterHeight_ - 1) / 2 + 1; r_input_area.xmin = output_area.xmin - add_x; r_input_area.xmax = output_area.xmax + add_x; r_input_area.ymin = output_area.ymin - add_y; @@ -159,22 +159,22 @@ void ConvolutionFilterOperation::update_memory_buffer_partial(MemoryBuffer *outp const float *center_color = it.in(IMAGE_INPUT_INDEX); zero_v4(it.out); - madd_v4_v4fl(it.out, center_color + down_offset + left_offset, m_filter[0]); - madd_v4_v4fl(it.out, center_color + down_offset, m_filter[1]); - madd_v4_v4fl(it.out, center_color + down_offset + right_offset, m_filter[2]); - madd_v4_v4fl(it.out, center_color + left_offset, m_filter[3]); - madd_v4_v4fl(it.out, center_color, m_filter[4]); - madd_v4_v4fl(it.out, center_color + right_offset, m_filter[5]); - madd_v4_v4fl(it.out, center_color + up_offset + left_offset, m_filter[6]); - madd_v4_v4fl(it.out, center_color + up_offset, m_filter[7]); - madd_v4_v4fl(it.out, center_color + up_offset + right_offset, m_filter[8]); + madd_v4_v4fl(it.out, center_color + down_offset + left_offset, filter_[0]); + madd_v4_v4fl(it.out, center_color + down_offset, filter_[1]); + madd_v4_v4fl(it.out, center_color + down_offset + right_offset, filter_[2]); + madd_v4_v4fl(it.out, center_color + left_offset, filter_[3]); + madd_v4_v4fl(it.out, center_color, filter_[4]); + madd_v4_v4fl(it.out, center_color + right_offset, filter_[5]); + madd_v4_v4fl(it.out, center_color + up_offset + left_offset, filter_[6]); + madd_v4_v4fl(it.out, center_color + up_offset, filter_[7]); + madd_v4_v4fl(it.out, center_color + up_offset + right_offset, filter_[8]); const float factor = *it.in(FACTOR_INPUT_INDEX); - const float m_factor = 1.0f - factor; - it.out[0] = it.out[0] * factor + center_color[0] * m_factor; - it.out[1] = it.out[1] * factor + center_color[1] * m_factor; - it.out[2] = it.out[2] * factor + center_color[2] * m_factor; - it.out[3] = it.out[3] * factor + center_color[3] * m_factor; + const float factor_ = 1.0f - factor; + it.out[0] = it.out[0] * factor + center_color[0] * factor_; + it.out[1] = it.out[1] * factor + center_color[1] * factor_; + it.out[2] = it.out[2] * factor + center_color[2] * factor_; + it.out[3] = it.out[3] * factor + center_color[3] * factor_; /* Make sure we don't return negative color. */ CLAMP4_MIN(it.out, 0.0f); diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h index 7e12c7faa5c..7000a0f69d6 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h @@ -28,13 +28,13 @@ class ConvolutionFilterOperation : public MultiThreadedOperation { static constexpr int FACTOR_INPUT_INDEX = 1; private: - int m_filterWidth; - int m_filterHeight; + int filterWidth_; + int filterHeight_; protected: - SocketReader *m_inputOperation; - SocketReader *m_inputValueOperation; - float m_filter[9]; + SocketReader *inputOperation_; + SocketReader *inputValueOperation_; + float filter_[9]; public: ConvolutionFilterOperation(); diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc index d1443e3e571..d8a402cc582 100644 --- a/source/blender/compositor/operations/COM_CropOperation.cc +++ b/source/blender/compositor/operations/COM_CropOperation.cc @@ -24,8 +24,8 @@ CropBaseOperation::CropBaseOperation() { this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Color); - m_inputOperation = nullptr; - m_settings = nullptr; + inputOperation_ = nullptr; + settings_ = nullptr; } void CropBaseOperation::updateArea() @@ -33,10 +33,10 @@ void CropBaseOperation::updateArea() SocketReader *inputReference = this->getInputSocketReader(0); float width = inputReference->getWidth(); float height = inputReference->getHeight(); - NodeTwoXYs local_settings = *m_settings; + NodeTwoXYs local_settings = *settings_; if (width > 0.0f && height > 0.0f) { - if (m_relative) { + if (relative_) { local_settings.x1 = width * local_settings.fac_x1; local_settings.x2 = width * local_settings.fac_x2; local_settings.y1 = height * local_settings.fac_y1; @@ -55,28 +55,28 @@ void CropBaseOperation::updateArea() local_settings.y2 = height - 1; } - m_xmax = MAX2(local_settings.x1, local_settings.x2) + 1; - m_xmin = MIN2(local_settings.x1, local_settings.x2); - m_ymax = MAX2(local_settings.y1, local_settings.y2) + 1; - m_ymin = MIN2(local_settings.y1, local_settings.y2); + xmax_ = MAX2(local_settings.x1, local_settings.x2) + 1; + xmin_ = MIN2(local_settings.x1, local_settings.x2); + ymax_ = MAX2(local_settings.y1, local_settings.y2) + 1; + ymin_ = MIN2(local_settings.y1, local_settings.y2); } else { - m_xmax = 0; - m_xmin = 0; - m_ymax = 0; - m_ymin = 0; + xmax_ = 0; + xmin_ = 0; + ymax_ = 0; + ymin_ = 0; } } void CropBaseOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); + inputOperation_ = this->getInputSocketReader(0); updateArea(); } void CropBaseOperation::deinitExecution() { - m_inputOperation = nullptr; + inputOperation_ = nullptr; } CropOperation::CropOperation() : CropBaseOperation() @@ -86,8 +86,8 @@ CropOperation::CropOperation() : CropBaseOperation() void CropOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { - if ((x < m_xmax && x >= m_xmin) && (y < m_ymax && y >= m_ymin)) { - m_inputOperation->readSampled(output, x, y, sampler); + if ((x < xmax_ && x >= xmin_) && (y < ymax_ && y >= ymin_)) { + inputOperation_->readSampled(output, x, y, sampler); } else { zero_v4(output); @@ -99,7 +99,7 @@ void CropOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { rcti crop_area; - BLI_rcti_init(&crop_area, m_xmin, m_xmax, m_ymin, m_ymax); + BLI_rcti_init(&crop_area, xmin_, xmax_, ymin_, ymax_); for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { if (BLI_rcti_isect_pt(&crop_area, it.x, it.y)) { copy_v4_v4(it.out, it.in(0)); @@ -121,10 +121,10 @@ bool CropImageOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - newInput.xmax = input->xmax + m_xmin; - newInput.xmin = input->xmin + m_xmin; - newInput.ymax = input->ymax + m_ymin; - newInput.ymin = input->ymin + m_ymin; + newInput.xmax = input->xmax + xmin_; + newInput.xmin = input->xmin + xmin_; + newInput.ymax = input->ymax + ymin_; + newInput.ymin = input->ymin + ymin_; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -135,18 +135,18 @@ void CropImageOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmax = output_area.xmax + m_xmin; - r_input_area.xmin = output_area.xmin + m_xmin; - r_input_area.ymax = output_area.ymax + m_ymin; - r_input_area.ymin = output_area.ymin + m_ymin; + r_input_area.xmax = output_area.xmax + xmin_; + r_input_area.xmin = output_area.xmin + xmin_; + r_input_area.ymax = output_area.ymax + ymin_; + r_input_area.ymin = output_area.ymin + ymin_; } void CropImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { NodeOperation::determine_canvas(preferred_area, r_area); updateArea(); - r_area.xmax = r_area.xmin + (m_xmax - m_xmin); - r_area.ymax = r_area.ymin + (m_ymax - m_ymin); + r_area.xmax = r_area.xmin + (xmax_ - xmin_); + r_area.ymax = r_area.ymin + (ymax_ - ymin_); } void CropImageOperation::executePixelSampled(float output[4], @@ -155,7 +155,7 @@ void CropImageOperation::executePixelSampled(float output[4], PixelSampler sampler) { if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { - m_inputOperation->readSampled(output, (x + m_xmin), (y + m_ymin), sampler); + inputOperation_->readSampled(output, (x + xmin_), (y + ymin_), sampler); } else { zero_v4(output); @@ -171,7 +171,7 @@ void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output, const MemoryBuffer *input = inputs[0]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) { - input->read_elem_checked(it.x + m_xmin, it.y + m_ymin, it.out); + input->read_elem_checked(it.x + xmin_, it.y + ymin_, it.out); } else { zero_v4(it.out); diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h index 49d0a70fb4e..50d4d3c5126 100644 --- a/source/blender/compositor/operations/COM_CropOperation.h +++ b/source/blender/compositor/operations/COM_CropOperation.h @@ -24,13 +24,13 @@ namespace blender::compositor { class CropBaseOperation : public MultiThreadedOperation { protected: - SocketReader *m_inputOperation; - NodeTwoXYs *m_settings; - bool m_relative; - int m_xmax; - int m_xmin; - int m_ymax; - int m_ymin; + SocketReader *inputOperation_; + NodeTwoXYs *settings_; + bool relative_; + int xmax_; + int xmin_; + int ymax_; + int ymin_; void updateArea(); @@ -40,11 +40,11 @@ class CropBaseOperation : public MultiThreadedOperation { void deinitExecution() override; void setCropSettings(NodeTwoXYs *settings) { - m_settings = settings; + settings_ = settings; } void setRelative(bool rel) { - m_relative = rel; + relative_ = rel; } }; diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc index 02e7c5607d8..a6b9e2c2ee4 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc @@ -40,7 +40,7 @@ void CryptomatteOperation::initExecution() void CryptomatteOperation::addObjectIndex(float objectIndex) { if (objectIndex != 0.0f) { - m_objectIndex.append(objectIndex); + objectIndex_.append(objectIndex); } } @@ -60,7 +60,7 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat output[1] = ((float)(m3hash << 8) / (float)UINT32_MAX); output[2] = ((float)(m3hash << 16) / (float)UINT32_MAX); } - for (float hash : m_objectIndex) { + for (float hash : objectIndex_) { if (input[0] == hash) { output[3] += input[1]; } @@ -89,7 +89,7 @@ void CryptomatteOperation::update_memory_buffer_partial(MemoryBuffer *output, it.out[1] = ((float)(m3hash << 8) / (float)UINT32_MAX); it.out[2] = ((float)(m3hash << 16) / (float)UINT32_MAX); } - for (const float hash : m_objectIndex) { + for (const float hash : objectIndex_) { if (input[0] == hash) { it.out[3] += input[1]; } diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.h b/source/blender/compositor/operations/COM_CryptomatteOperation.h index f1bf4cdf624..039b4e3b07a 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.h +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.h @@ -24,7 +24,7 @@ namespace blender::compositor { class CryptomatteOperation : public MultiThreadedOperation { private: - Vector m_objectIndex; + Vector objectIndex_; public: Vector inputs; diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc index 5a0d6581f2c..9ae4cad73d3 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc @@ -24,37 +24,37 @@ namespace blender::compositor { CurveBaseOperation::CurveBaseOperation() { - m_curveMapping = nullptr; + curveMapping_ = nullptr; this->flags.can_be_constant = true; } CurveBaseOperation::~CurveBaseOperation() { - if (m_curveMapping) { - BKE_curvemapping_free(m_curveMapping); - m_curveMapping = nullptr; + if (curveMapping_) { + BKE_curvemapping_free(curveMapping_); + curveMapping_ = nullptr; } } void CurveBaseOperation::initExecution() { - BKE_curvemapping_init(m_curveMapping); + BKE_curvemapping_init(curveMapping_); } void CurveBaseOperation::deinitExecution() { - if (m_curveMapping) { - BKE_curvemapping_free(m_curveMapping); - m_curveMapping = nullptr; + if (curveMapping_) { + BKE_curvemapping_free(curveMapping_); + curveMapping_ = nullptr; } } void CurveBaseOperation::setCurveMapping(CurveMapping *mapping) { /* duplicate the curve to avoid glitches while drawing, see bug T32374. */ - if (m_curveMapping) { - BKE_curvemapping_free(m_curveMapping); + if (curveMapping_) { + BKE_curvemapping_free(curveMapping_); } - m_curveMapping = BKE_curvemapping_copy(mapping); + curveMapping_ = BKE_curvemapping_copy(mapping); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h index 27d3a68608c..70e9fa9da76 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.h +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h @@ -29,7 +29,7 @@ class CurveBaseOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - CurveMapping *m_curveMapping; + CurveMapping *curveMapping_; public: CurveBaseOperation(); diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index fa702558a20..2e41181559e 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -160,21 +160,21 @@ DenoiseOperation::DenoiseOperation() this->addInputSocket(DataType::Vector); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_settings = nullptr; + settings_ = nullptr; } void DenoiseOperation::initExecution() { SingleThreadedOperation::initExecution(); - m_inputProgramColor = getInputSocketReader(0); - m_inputProgramNormal = getInputSocketReader(1); - m_inputProgramAlbedo = getInputSocketReader(2); + inputProgramColor_ = getInputSocketReader(0); + inputProgramNormal_ = getInputSocketReader(1); + inputProgramAlbedo_ = getInputSocketReader(2); } void DenoiseOperation::deinitExecution() { - m_inputProgramColor = nullptr; - m_inputProgramNormal = nullptr; - m_inputProgramAlbedo = nullptr; + inputProgramColor_ = nullptr; + inputProgramNormal_ = nullptr; + inputProgramAlbedo_ = nullptr; SingleThreadedOperation::deinitExecution(); } @@ -192,23 +192,23 @@ static bool are_guiding_passes_noise_free(NodeDenoise *settings) void DenoiseOperation::hash_output_params() { - if (m_settings) { - hash_params((int)m_settings->hdr, are_guiding_passes_noise_free(m_settings)); + if (settings_) { + hash_params((int)settings_->hdr, are_guiding_passes_noise_free(settings_)); } } MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2) { - MemoryBuffer *tileColor = (MemoryBuffer *)m_inputProgramColor->initializeTileData(rect2); - MemoryBuffer *tileNormal = (MemoryBuffer *)m_inputProgramNormal->initializeTileData(rect2); - MemoryBuffer *tileAlbedo = (MemoryBuffer *)m_inputProgramAlbedo->initializeTileData(rect2); + MemoryBuffer *tileColor = (MemoryBuffer *)inputProgramColor_->initializeTileData(rect2); + MemoryBuffer *tileNormal = (MemoryBuffer *)inputProgramNormal_->initializeTileData(rect2); + MemoryBuffer *tileAlbedo = (MemoryBuffer *)inputProgramAlbedo_->initializeTileData(rect2); rcti rect; rect.xmin = 0; rect.ymin = 0; rect.xmax = getWidth(); rect.ymax = getHeight(); MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); - this->generateDenoise(result, tileColor, tileNormal, tileAlbedo, m_settings); + this->generateDenoise(result, tileColor, tileNormal, tileAlbedo, settings_); return result; } @@ -270,7 +270,7 @@ void DenoiseOperation::update_memory_buffer(MemoryBuffer *output, Span inputs) { if (!output_rendered_) { - this->generateDenoise(output, inputs[0], inputs[1], inputs[2], m_settings); + this->generateDenoise(output, inputs[0], inputs[1], inputs[2], settings_); output_rendered_ = true; } } diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h index e5b7ebbfeca..24852009e6d 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.h +++ b/source/blender/compositor/operations/COM_DenoiseOperation.h @@ -45,14 +45,14 @@ class DenoiseOperation : public DenoiseBaseOperation { /** * \brief Cached reference to the input programs */ - SocketReader *m_inputProgramColor; - SocketReader *m_inputProgramAlbedo; - SocketReader *m_inputProgramNormal; + SocketReader *inputProgramColor_; + SocketReader *inputProgramAlbedo_; + SocketReader *inputProgramNormal_; /** * \brief settings of the denoise node. */ - NodeDenoise *m_settings; + NodeDenoise *settings_; public: DenoiseOperation(); @@ -68,7 +68,7 @@ class DenoiseOperation : public DenoiseBaseOperation { void setDenoiseSettings(NodeDenoise *settings) { - m_settings = settings; + settings_ = settings; } void update_memory_buffer(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc index a317cdcc7f0..f655c17fdf2 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.cc +++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc @@ -28,19 +28,19 @@ DespeckleOperation::DespeckleOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - m_inputOperation = nullptr; + inputOperation_ = nullptr; this->flags.complex = true; } void DespeckleOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); - m_inputValueOperation = this->getInputSocketReader(1); + inputOperation_ = this->getInputSocketReader(0); + inputValueOperation_ = this->getInputSocketReader(1); } void DespeckleOperation::deinitExecution() { - m_inputOperation = nullptr; - m_inputValueOperation = nullptr; + inputOperation_ = nullptr; + inputValueOperation_ = nullptr; } BLI_INLINE int color_diff(const float a[3], const float b[3], const float threshold) @@ -69,10 +69,10 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da CLAMP(y2, 0, getHeight() - 1); CLAMP(y3, 0, getHeight() - 1); float value[4]; - m_inputValueOperation->read(value, x2, y2, nullptr); + inputValueOperation_->read(value, x2, y2, nullptr); // const float mval = 1.0f - value[0]; - m_inputOperation->read(color_org, x2, y2, nullptr); + inputOperation_->read(color_org, x2, y2, nullptr); #define TOT_DIV_ONE 1.0f #define TOT_DIV_CNR (float)M_SQRT1_2 @@ -82,7 +82,7 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da #define COLOR_ADD(fac) \ { \ madd_v4_v4fl(color_mid, in1, fac); \ - if (color_diff(in1, color_org, m_threshold)) { \ + if (color_diff(in1, color_org, threshold_)) { \ w += fac; \ madd_v4_v4fl(color_mid_ok, in1, fac); \ } \ @@ -91,34 +91,34 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da zero_v4(color_mid); zero_v4(color_mid_ok); - m_inputOperation->read(in1, x1, y1, nullptr); + inputOperation_->read(in1, x1, y1, nullptr); COLOR_ADD(TOT_DIV_CNR) - m_inputOperation->read(in1, x2, y1, nullptr); + inputOperation_->read(in1, x2, y1, nullptr); COLOR_ADD(TOT_DIV_ONE) - m_inputOperation->read(in1, x3, y1, nullptr); + inputOperation_->read(in1, x3, y1, nullptr); COLOR_ADD(TOT_DIV_CNR) - m_inputOperation->read(in1, x1, y2, nullptr); + inputOperation_->read(in1, x1, y2, nullptr); COLOR_ADD(TOT_DIV_ONE) #if 0 - m_inputOperation->read(in2, x2, y2, nullptr); - madd_v4_v4fl(color_mid, in2, m_filter[4]); + inputOperation_->read(in2, x2, y2, nullptr); + madd_v4_v4fl(color_mid, in2, filter_[4]); #endif - m_inputOperation->read(in1, x3, y2, nullptr); + inputOperation_->read(in1, x3, y2, nullptr); COLOR_ADD(TOT_DIV_ONE) - m_inputOperation->read(in1, x1, y3, nullptr); + inputOperation_->read(in1, x1, y3, nullptr); COLOR_ADD(TOT_DIV_CNR) - m_inputOperation->read(in1, x2, y3, nullptr); + inputOperation_->read(in1, x2, y3, nullptr); COLOR_ADD(TOT_DIV_ONE) - m_inputOperation->read(in1, x3, y3, nullptr); + inputOperation_->read(in1, x3, y3, nullptr); COLOR_ADD(TOT_DIV_CNR) mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2))); // mul_v4_fl(color_mid, 1.0f / w); - if ((w != 0.0f) && ((w / WTOT) > (m_threshold_neighbor)) && - color_diff(color_mid, color_org, m_threshold)) { + if ((w != 0.0f) && ((w / WTOT) > (threshold_neighbor_)) && + color_diff(color_mid, color_org, threshold_)) { mul_v4_fl(color_mid_ok, 1.0f / w); interp_v4_v4v4(output, color_org, color_mid_ok, value[0]); } @@ -137,8 +137,8 @@ bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input, rcti *output) { rcti newInput; - int addx = 2; //(m_filterWidth - 1) / 2 + 1; - int addy = 2; //(m_filterHeight - 1) / 2 + 1; + int addx = 2; //(filterWidth_ - 1) / 2 + 1; + int addy = 2; //(filterHeight_ - 1) / 2 + 1; newInput.xmax = input->xmax + addx; newInput.xmin = input->xmin - addx; newInput.ymax = input->ymax + addy; @@ -153,8 +153,8 @@ void DespeckleOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - const int add_x = 2; //(m_filterWidth - 1) / 2 + 1; - const int add_y = 2; //(m_filterHeight - 1) / 2 + 1; + const int add_x = 2; //(filterWidth_ - 1) / 2 + 1; + const int add_y = 2; //(filterHeight_ - 1) / 2 + 1; r_input_area.xmin = output_area.xmin - add_x; r_input_area.xmax = output_area.xmax + add_x; r_input_area.ymin = output_area.ymin - add_y; @@ -197,7 +197,7 @@ void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, #define COLOR_ADD(fac) \ { \ madd_v4_v4fl(color_mid, in1, fac); \ - if (color_diff(in1, color_org, m_threshold)) { \ + if (color_diff(in1, color_org, threshold_)) { \ w += fac; \ madd_v4_v4fl(color_mid_ok, in1, fac); \ } \ @@ -217,7 +217,7 @@ void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, #if 0 const float* in2 = image->get_elem(x2, y2); - madd_v4_v4fl(color_mid, in2, m_filter[4]); + madd_v4_v4fl(color_mid, in2, filter_[4]); #endif in1 = image->get_elem(x3, y2); @@ -232,8 +232,8 @@ void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2))); // mul_v4_fl(color_mid, 1.0f / w); - if ((w != 0.0f) && ((w / WTOT) > (m_threshold_neighbor)) && - color_diff(color_mid, color_org, m_threshold)) { + if ((w != 0.0f) && ((w / WTOT) > (threshold_neighbor_)) && + color_diff(color_mid, color_org, threshold_)) { const float factor = *it.in(FACTOR_INPUT_INDEX); mul_v4_fl(color_mid_ok, 1.0f / w); interp_v4_v4v4(it.out, color_org, color_mid_ok, factor); diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.h b/source/blender/compositor/operations/COM_DespeckleOperation.h index a77010f1853..9aca01000a7 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.h +++ b/source/blender/compositor/operations/COM_DespeckleOperation.h @@ -27,15 +27,15 @@ class DespeckleOperation : public MultiThreadedOperation { constexpr static int IMAGE_INPUT_INDEX = 0; constexpr static int FACTOR_INPUT_INDEX = 1; - float m_threshold; - float m_threshold_neighbor; + float threshold_; + float threshold_neighbor_; - // int m_filterWidth; - // int m_filterHeight; + // int filterWidth_; + // int filterHeight_; protected: - SocketReader *m_inputOperation; - SocketReader *m_inputValueOperation; + SocketReader *inputOperation_; + SocketReader *inputValueOperation_; public: DespeckleOperation(); @@ -46,11 +46,11 @@ class DespeckleOperation : public MultiThreadedOperation { void setThreshold(float threshold) { - m_threshold = threshold; + threshold_ = threshold; } void setThresholdNeighbor(float threshold) { - m_threshold_neighbor = threshold; + threshold_neighbor_ = threshold; } void initExecution() override; diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc index 25004d947aa..d5f11ff8e35 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc @@ -26,20 +26,20 @@ DifferenceMatteOperation::DifferenceMatteOperation() addInputSocket(DataType::Color); addOutputSocket(DataType::Value); - m_inputImage1Program = nullptr; - m_inputImage2Program = nullptr; + inputImage1Program_ = nullptr; + inputImage2Program_ = nullptr; flags.can_be_constant = true; } void DifferenceMatteOperation::initExecution() { - m_inputImage1Program = this->getInputSocketReader(0); - m_inputImage2Program = this->getInputSocketReader(1); + inputImage1Program_ = this->getInputSocketReader(0); + inputImage2Program_ = this->getInputSocketReader(1); } void DifferenceMatteOperation::deinitExecution() { - m_inputImage1Program = nullptr; - m_inputImage2Program = nullptr; + inputImage1Program_ = nullptr; + inputImage2Program_ = nullptr; } void DifferenceMatteOperation::executePixelSampled(float output[4], @@ -50,13 +50,13 @@ void DifferenceMatteOperation::executePixelSampled(float output[4], float inColor1[4]; float inColor2[4]; - const float tolerance = m_settings->t1; - const float falloff = m_settings->t2; + const float tolerance = settings_->t1; + const float falloff = settings_->t2; float difference; float alpha; - m_inputImage1Program->readSampled(inColor1, x, y, sampler); - m_inputImage2Program->readSampled(inColor2, x, y, sampler); + inputImage1Program_->readSampled(inColor1, x, y, sampler); + inputImage2Program_->readSampled(inColor2, x, y, sampler); difference = (fabsf(inColor2[0] - inColor1[0]) + fabsf(inColor2[1] - inColor1[1]) + fabsf(inColor2[2] - inColor1[2])); @@ -100,8 +100,8 @@ void DifferenceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output /* Average together the distances. */ difference = difference / 3.0f; - const float tolerance = m_settings->t1; - const float falloff = m_settings->t2; + const float tolerance = settings_->t1; + const float falloff = settings_->t2; /* Make 100% transparent. */ if (difference <= tolerance) { diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h index f95c78a38de..31ce0d3dbe5 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DifferenceMatteOperation : public MultiThreadedOperation { private: - NodeChroma *m_settings; - SocketReader *m_inputImage1Program; - SocketReader *m_inputImage2Program; + NodeChroma *settings_; + SocketReader *inputImage1Program_; + SocketReader *inputImage2Program_; public: /** @@ -48,7 +48,7 @@ class DifferenceMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma) { - m_settings = nodeChroma; + settings_ = nodeChroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index 60f6bd42144..1366eb43776 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -27,58 +27,58 @@ DilateErodeThresholdOperation::DilateErodeThresholdOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); this->flags.complex = true; - m_inputProgram = nullptr; - m_inset = 0.0f; - m__switch = 0.5f; - m_distance = 0.0f; + inputProgram_ = nullptr; + inset_ = 0.0f; + switch_ = 0.5f; + distance_ = 0.0f; } void DilateErodeThresholdOperation::init_data() { - if (m_distance < 0.0f) { - m_scope = -m_distance + m_inset; + if (distance_ < 0.0f) { + scope_ = -distance_ + inset_; } else { - if (m_inset * 2 > m_distance) { - m_scope = MAX2(m_inset * 2 - m_distance, m_distance); + if (inset_ * 2 > distance_) { + scope_ = MAX2(inset_ * 2 - distance_, distance_); } else { - m_scope = m_distance; + scope_ = distance_; } } - if (m_scope < 3) { - m_scope = 3; + if (scope_ < 3) { + scope_ = 3; } } void DilateErodeThresholdOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); } void *DilateErodeThresholdOperation::initializeTileData(rcti * /*rect*/) { - void *buffer = m_inputProgram->initializeTileData(nullptr); + void *buffer = inputProgram_->initializeTileData(nullptr); return buffer; } void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, void *data) { float inputValue[4]; - const float sw = m__switch; - const float distance = m_distance; + const float sw = switch_; + const float distance = distance_; float pixelvalue; - const float rd = m_scope * m_scope; - const float inset = m_inset; + const float rd = scope_ * scope_; + const float inset = inset_; float mindist = rd * 2; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; float *buffer = inputBuffer->getBuffer(); const rcti &input_rect = inputBuffer->get_rect(); - const int minx = MAX2(x - m_scope, input_rect.xmin); - const int miny = MAX2(y - m_scope, input_rect.ymin); - const int maxx = MIN2(x + m_scope, input_rect.xmax); - const int maxy = MIN2(y + m_scope, input_rect.ymax); + const int minx = MAX2(x - scope_, input_rect.xmin); + const int miny = MAX2(y - scope_, input_rect.ymin); + const int maxx = MIN2(x + scope_, input_rect.xmax); + const int maxy = MIN2(y + scope_, input_rect.ymax); const int bufferWidth = inputBuffer->getWidth(); int offset; @@ -146,7 +146,7 @@ void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, void DilateErodeThresholdOperation::deinitExecution() { - m_inputProgram = nullptr; + inputProgram_ = nullptr; } bool DilateErodeThresholdOperation::determineDependingAreaOfInterest( @@ -154,10 +154,10 @@ bool DilateErodeThresholdOperation::determineDependingAreaOfInterest( { rcti newInput; - newInput.xmax = input->xmax + m_scope; - newInput.xmin = input->xmin - m_scope; - newInput.ymax = input->ymax + m_scope; - newInput.ymin = input->ymin - m_scope; + newInput.xmax = input->xmax + scope_; + newInput.xmin = input->xmin - scope_; + newInput.ymax = input->ymax + scope_; + newInput.ymin = input->ymin - scope_; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -168,10 +168,10 @@ void DilateErodeThresholdOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - m_scope; - r_input_area.xmax = output_area.xmax + m_scope; - r_input_area.ymin = output_area.ymin - m_scope; - r_input_area.ymax = output_area.ymax + m_scope; + r_input_area.xmin = output_area.xmin - scope_; + r_input_area.xmax = output_area.xmax + scope_; + r_input_area.ymin = output_area.ymin - scope_; + r_input_area.ymax = output_area.ymax + scope_; } struct DilateErodeThresholdOperation::PixelData { @@ -222,21 +222,21 @@ void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *o { const MemoryBuffer *input = inputs[0]; const rcti &input_rect = input->get_rect(); - const float rd = m_scope * m_scope; - const float inset = m_inset; + const float rd = scope_ * scope_; + const float inset = inset_; PixelData p; - p.sw = m__switch; + p.sw = switch_; p.distance = rd * 2; p.elem_stride = input->elem_stride; p.row_stride = input->row_stride; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { p.x = it.x; p.y = it.y; - p.xmin = MAX2(p.x - m_scope, input_rect.xmin); - p.ymin = MAX2(p.y - m_scope, input_rect.ymin); - p.xmax = MIN2(p.x + m_scope, input_rect.xmax); - p.ymax = MIN2(p.y + m_scope, input_rect.ymax); + p.xmin = MAX2(p.x - scope_, input_rect.xmin); + p.ymin = MAX2(p.y - scope_, input_rect.ymin); + p.xmax = MIN2(p.x + scope_, input_rect.xmax); + p.ymax = MIN2(p.y + scope_, input_rect.ymax); p.elem = it.in(0); float pixel_value; @@ -247,8 +247,8 @@ void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *o pixel_value = sqrtf(get_min_distance(p)); } - if (m_distance > 0.0f) { - const float delta = m_distance - pixel_value; + if (distance_ > 0.0f) { + const float delta = distance_ - pixel_value; if (delta >= 0.0f) { *it.out = delta >= inset ? 1.0f : delta / inset; } @@ -257,7 +257,7 @@ void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *o } } else { - const float delta = -m_distance + pixel_value; + const float delta = -distance_ + pixel_value; if (delta < 0.0f) { *it.out = delta < -inset ? 1.0f : (-delta) / inset; } @@ -273,43 +273,43 @@ DilateDistanceOperation::DilateDistanceOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_inputProgram = nullptr; - m_distance = 0.0f; + inputProgram_ = nullptr; + distance_ = 0.0f; flags.complex = true; flags.open_cl = true; } void DilateDistanceOperation::init_data() { - m_scope = m_distance; - if (m_scope < 3) { - m_scope = 3; + scope_ = distance_; + if (scope_ < 3) { + scope_ = 3; } } void DilateDistanceOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); } void *DilateDistanceOperation::initializeTileData(rcti * /*rect*/) { - void *buffer = m_inputProgram->initializeTileData(nullptr); + void *buffer = inputProgram_->initializeTileData(nullptr); return buffer; } void DilateDistanceOperation::executePixel(float output[4], int x, int y, void *data) { - const float distance = m_distance; + const float distance = distance_; const float mindist = distance * distance; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; float *buffer = inputBuffer->getBuffer(); const rcti &input_rect = inputBuffer->get_rect(); - const int minx = MAX2(x - m_scope, input_rect.xmin); - const int miny = MAX2(y - m_scope, input_rect.ymin); - const int maxx = MIN2(x + m_scope, input_rect.xmax); - const int maxy = MIN2(y + m_scope, input_rect.ymax); + const int minx = MAX2(x - scope_, input_rect.xmin); + const int miny = MAX2(y - scope_, input_rect.ymin); + const int maxx = MIN2(x + scope_, input_rect.xmax); + const int maxy = MIN2(y + scope_, input_rect.ymax); const int bufferWidth = inputBuffer->getWidth(); int offset; @@ -332,7 +332,7 @@ void DilateDistanceOperation::executePixel(float output[4], int x, int y, void * void DilateDistanceOperation::deinitExecution() { - m_inputProgram = nullptr; + inputProgram_ = nullptr; } bool DilateDistanceOperation::determineDependingAreaOfInterest(rcti *input, @@ -341,10 +341,10 @@ bool DilateDistanceOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - newInput.xmax = input->xmax + m_scope; - newInput.xmin = input->xmin - m_scope; - newInput.ymax = input->ymax + m_scope; - newInput.ymin = input->ymin - m_scope; + newInput.xmax = input->xmax + scope_; + newInput.xmin = input->xmin - scope_; + newInput.ymax = input->ymax + scope_; + newInput.ymin = input->ymin - scope_; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -358,11 +358,11 @@ void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device, { cl_kernel dilateKernel = device->COM_clCreateKernel("dilateKernel", nullptr); - cl_int distanceSquared = m_distance * m_distance; - cl_int scope = m_scope; + cl_int distanceSquared = distance_ * distance_; + cl_int scope = scope_; device->COM_clAttachMemoryBufferToKernelParameter( - dilateKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); + dilateKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, inputProgram_); device->COM_clAttachOutputMemoryBufferToKernelParameter(dilateKernel, 1, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter(dilateKernel, 3, outputMemoryBuffer); clSetKernelArg(dilateKernel, 4, sizeof(cl_int), &scope); @@ -377,10 +377,10 @@ void DilateDistanceOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - m_scope; - r_input_area.xmax = output_area.xmax + m_scope; - r_input_area.ymin = output_area.ymin - m_scope; - r_input_area.ymax = output_area.ymax + m_scope; + r_input_area.xmin = output_area.xmin - scope_; + r_input_area.xmax = output_area.xmax + scope_; + r_input_area.ymin = output_area.ymin - scope_; + r_input_area.ymax = output_area.ymax + scope_; } struct DilateDistanceOperation::PixelData { @@ -450,7 +450,7 @@ void DilateDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - PixelData p(inputs[0], m_distance, m_scope); + PixelData p(inputs[0], distance_, scope_); for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { p.update(it); *it.out = get_distance_value(p, 0.0f); @@ -465,16 +465,16 @@ ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() void ErodeDistanceOperation::executePixel(float output[4], int x, int y, void *data) { - const float distance = m_distance; + const float distance = distance_; const float mindist = distance * distance; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; float *buffer = inputBuffer->getBuffer(); const rcti &input_rect = inputBuffer->get_rect(); - const int minx = MAX2(x - m_scope, input_rect.xmin); - const int miny = MAX2(y - m_scope, input_rect.ymin); - const int maxx = MIN2(x + m_scope, input_rect.xmax); - const int maxy = MIN2(y + m_scope, input_rect.ymax); + const int minx = MAX2(x - scope_, input_rect.xmin); + const int miny = MAX2(y - scope_, input_rect.ymin); + const int maxx = MIN2(x + scope_, input_rect.xmax); + const int maxy = MIN2(y + scope_, input_rect.ymax); const int bufferWidth = inputBuffer->getWidth(); int offset; @@ -504,11 +504,11 @@ void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device, { cl_kernel erodeKernel = device->COM_clCreateKernel("erodeKernel", nullptr); - cl_int distanceSquared = m_distance * m_distance; - cl_int scope = m_scope; + cl_int distanceSquared = distance_ * distance_; + cl_int scope = scope_; device->COM_clAttachMemoryBufferToKernelParameter( - erodeKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); + erodeKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, inputProgram_); device->COM_clAttachOutputMemoryBufferToKernelParameter(erodeKernel, 1, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter(erodeKernel, 3, outputMemoryBuffer); clSetKernelArg(erodeKernel, 4, sizeof(cl_int), &scope); @@ -521,7 +521,7 @@ void ErodeDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - PixelData p(inputs[0], m_distance, m_scope); + PixelData p(inputs[0], distance_, scope_); for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { p.update(it); *it.out = get_distance_value(p, 1.0f); @@ -534,11 +534,11 @@ DilateStepOperation::DilateStepOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); this->flags.complex = true; - m_inputProgram = nullptr; + inputProgram_ = nullptr; } void DilateStepOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); } /* Small helper to pass data from initializeTileData to executePixel. */ @@ -563,13 +563,13 @@ static tile_info *create_cache(int xmin, int xmax, int ymin, int ymax) void *DilateStepOperation::initializeTileData(rcti *rect) { - MemoryBuffer *tile = (MemoryBuffer *)m_inputProgram->initializeTileData(nullptr); + MemoryBuffer *tile = (MemoryBuffer *)inputProgram_->initializeTileData(nullptr); int x, y, i; int width = tile->getWidth(); int height = tile->getHeight(); float *buffer = tile->getBuffer(); - int half_window = m_iterations; + int half_window = iterations_; int window = half_window * 2 + 1; int xmin = MAX2(0, rect->xmin - half_window); @@ -661,7 +661,7 @@ void DilateStepOperation::executePixel(float output[4], int x, int y, void *data void DilateStepOperation::deinitExecution() { - m_inputProgram = nullptr; + inputProgram_ = nullptr; } void DilateStepOperation::deinitializeTileData(rcti * /*rect*/, void *data) @@ -676,7 +676,7 @@ bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, rcti *output) { rcti newInput; - int it = m_iterations; + int it = iterations_; newInput.xmax = input->xmax + it; newInput.xmin = input->xmin - it; newInput.ymax = input->ymax + it; @@ -691,10 +691,10 @@ void DilateStepOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - m_iterations; - r_input_area.xmax = output_area.xmax + m_iterations; - r_input_area.ymin = output_area.ymin - m_iterations; - r_input_area.ymax = output_area.ymax + m_iterations; + r_input_area.xmin = output_area.xmin - iterations_; + r_input_area.xmax = output_area.xmax + iterations_; + r_input_area.ymin = output_area.ymin - iterations_; + r_input_area.ymax = output_area.ymax + iterations_; } template @@ -803,7 +803,7 @@ void DilateStepOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - step_update_memory_buffer(output, inputs[0], area, m_iterations, -FLT_MAX); + step_update_memory_buffer(output, inputs[0], area, iterations_, -FLT_MAX); } /* Erode step */ @@ -814,13 +814,13 @@ ErodeStepOperation::ErodeStepOperation() : DilateStepOperation() void *ErodeStepOperation::initializeTileData(rcti *rect) { - MemoryBuffer *tile = (MemoryBuffer *)m_inputProgram->initializeTileData(nullptr); + MemoryBuffer *tile = (MemoryBuffer *)inputProgram_->initializeTileData(nullptr); int x, y, i; int width = tile->getWidth(); int height = tile->getHeight(); float *buffer = tile->getBuffer(); - int half_window = m_iterations; + int half_window = iterations_; int window = half_window * 2 + 1; int xmin = MAX2(0, rect->xmin - half_window); @@ -913,7 +913,7 @@ void ErodeStepOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - step_update_memory_buffer(output, inputs[0], area, m_iterations, FLT_MAX); + step_update_memory_buffer(output, inputs[0], area, iterations_, FLT_MAX); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.h b/source/blender/compositor/operations/COM_DilateErodeOperation.h index fb22a72fb70..1919c926710 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.h +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.h @@ -30,17 +30,17 @@ class DilateErodeThresholdOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; + SocketReader *inputProgram_; - float m_distance; - float m__switch; - float m_inset; + float distance_; + float switch_; + float inset_; /** * determines the area of interest to track pixels * keep this one as small as possible for speed gain. */ - int m_scope; + int scope_; public: DilateErodeThresholdOperation(); @@ -64,15 +64,15 @@ class DilateErodeThresholdOperation : public MultiThreadedOperation { void setDistance(float distance) { - m_distance = distance; + distance_ = distance; } void setSwitch(float sw) { - m__switch = sw; + switch_ = sw; } void setInset(float inset) { - m_inset = inset; + inset_ = inset; } bool determineDependingAreaOfInterest(rcti *input, @@ -93,9 +93,9 @@ class DilateDistanceOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; - float m_distance; - int m_scope; + SocketReader *inputProgram_; + float distance_; + int scope_; public: DilateDistanceOperation(); @@ -119,7 +119,7 @@ class DilateDistanceOperation : public MultiThreadedOperation { void setDistance(float distance) { - m_distance = distance; + distance_ = distance; } bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, @@ -164,9 +164,9 @@ class DilateStepOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; + SocketReader *inputProgram_; - int m_iterations; + int iterations_; public: DilateStepOperation(); @@ -190,7 +190,7 @@ class DilateStepOperation : public MultiThreadedOperation { void setIterations(int iterations) { - m_iterations = iterations; + iterations_ = iterations; } bool determineDependingAreaOfInterest(rcti *input, diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc index 7a6f3d972a4..3cf3ee3b0c8 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc @@ -27,20 +27,20 @@ DirectionalBlurOperation::DirectionalBlurOperation() this->addOutputSocket(DataType::Color); flags.complex = true; flags.open_cl = true; - m_inputProgram = nullptr; + inputProgram_ = nullptr; } void DirectionalBlurOperation::initExecution() { - m_inputProgram = getInputSocketReader(0); + inputProgram_ = getInputSocketReader(0); QualityStepHelper::initExecution(COM_QH_INCREASE); - const float angle = m_data->angle; - const float zoom = m_data->zoom; - const float spin = m_data->spin; - const float iterations = m_data->iter; - const float distance = m_data->distance; - const float center_x = m_data->center_x; - const float center_y = m_data->center_y; + const float angle = data_->angle; + const float zoom = data_->zoom; + const float spin = data_->spin; + const float iterations = data_->iter; + const float distance = data_->distance; + const float center_x = data_->center_x; + const float center_y = data_->center_y; const float width = getWidth(); const float height = getHeight(); @@ -49,45 +49,45 @@ void DirectionalBlurOperation::initExecution() float D; D = distance * sqrtf(width * width + height * height); - m_center_x_pix = center_x * width; - m_center_y_pix = center_y * height; + center_x_pix_ = center_x * width; + center_y_pix_ = center_y * height; - m_tx = itsc * D * cosf(a); - m_ty = -itsc * D * sinf(a); - m_sc = itsc * zoom; - m_rot = itsc * spin; + tx_ = itsc * D * cosf(a); + ty_ = -itsc * D * sinf(a); + sc_ = itsc * zoom; + rot_ = itsc * spin; } void DirectionalBlurOperation::executePixel(float output[4], int x, int y, void * /*data*/) { - const int iterations = pow(2.0f, m_data->iter); + const int iterations = pow(2.0f, data_->iter); float col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float col2[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - m_inputProgram->readSampled(col2, x, y, PixelSampler::Bilinear); - float ltx = m_tx; - float lty = m_ty; - float lsc = m_sc; - float lrot = m_rot; + inputProgram_->readSampled(col2, x, y, PixelSampler::Bilinear); + float ltx = tx_; + float lty = ty_; + float lsc = sc_; + float lrot = rot_; /* blur the image */ for (int i = 0; i < iterations; i++) { const float cs = cosf(lrot), ss = sinf(lrot); const float isc = 1.0f / (1.0f + lsc); - const float v = isc * (y - m_center_y_pix) + lty; - const float u = isc * (x - m_center_x_pix) + ltx; + const float v = isc * (y - center_y_pix_) + lty; + const float u = isc * (x - center_x_pix_) + ltx; - m_inputProgram->readSampled(col, - cs * u + ss * v + m_center_x_pix, - cs * v - ss * u + m_center_y_pix, - PixelSampler::Bilinear); + inputProgram_->readSampled(col, + cs * u + ss * v + center_x_pix_, + cs * v - ss * u + center_y_pix_, + PixelSampler::Bilinear); add_v4_v4(col2, col); /* double transformations */ - ltx += m_tx; - lty += m_ty; - lrot += m_rot; - lsc += m_sc; + ltx += tx_; + lty += ty_; + lrot += rot_; + lsc += sc_; } mul_v4_v4fl(output, col2, 1.0f / (iterations + 1)); @@ -102,14 +102,14 @@ void DirectionalBlurOperation::executeOpenCL(OpenCLDevice *device, { cl_kernel directionalBlurKernel = device->COM_clCreateKernel("directionalBlurKernel", nullptr); - cl_int iterations = pow(2.0f, m_data->iter); - cl_float2 ltxy = {{m_tx, m_ty}}; - cl_float2 centerpix = {{m_center_x_pix, m_center_y_pix}}; - cl_float lsc = m_sc; - cl_float lrot = m_rot; + cl_int iterations = pow(2.0f, data_->iter); + cl_float2 ltxy = {{tx_, ty_}}; + cl_float2 centerpix = {{center_x_pix_, center_y_pix_}}; + cl_float lsc = sc_; + cl_float lrot = rot_; device->COM_clAttachMemoryBufferToKernelParameter( - directionalBlurKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); + directionalBlurKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, inputProgram_); device->COM_clAttachOutputMemoryBufferToKernelParameter( directionalBlurKernel, 1, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter( @@ -125,7 +125,7 @@ void DirectionalBlurOperation::executeOpenCL(OpenCLDevice *device, void DirectionalBlurOperation::deinitExecution() { - m_inputProgram = nullptr; + inputProgram_ = nullptr; } bool DirectionalBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/, @@ -156,7 +156,7 @@ void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output Span inputs) { const MemoryBuffer *input = inputs[0]; - const int iterations = pow(2.0f, m_data->iter); + const int iterations = pow(2.0f, data_->iter); for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { const int x = it.x; const int y = it.y; @@ -166,27 +166,27 @@ void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output /* Blur pixel. */ /* TODO(manzanilla): Many values used on iterations can be calculated beforehand. Create a * table on operation initialization. */ - float ltx = m_tx; - float lty = m_ty; - float lsc = m_sc; - float lrot = m_rot; + float ltx = tx_; + float lty = ty_; + float lsc = sc_; + float lrot = rot_; for (int i = 0; i < iterations; i++) { const float cs = cosf(lrot), ss = sinf(lrot); const float isc = 1.0f / (1.0f + lsc); - const float v = isc * (y - m_center_y_pix) + lty; - const float u = isc * (x - m_center_x_pix) + ltx; + const float v = isc * (y - center_y_pix_) + lty; + const float u = isc * (x - center_x_pix_) + ltx; float color[4]; input->read_elem_bilinear( - cs * u + ss * v + m_center_x_pix, cs * v - ss * u + m_center_y_pix, color); + cs * u + ss * v + center_x_pix_, cs * v - ss * u + center_y_pix_, color); add_v4_v4(color_accum, color); /* Double transformations. */ - ltx += m_tx; - lty += m_ty; - lrot += m_rot; - lsc += m_sc; + ltx += tx_; + lty += ty_; + lrot += rot_; + lsc += sc_; } mul_v4_v4fl(it.out, color_accum, 1.0f / (iterations + 1)); diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h index 20d4b41b326..0687ff4ae5d 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h @@ -25,12 +25,12 @@ namespace blender::compositor { class DirectionalBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: - SocketReader *m_inputProgram; - NodeDBlurData *m_data; + SocketReader *inputProgram_; + NodeDBlurData *data_; - float m_center_x_pix, m_center_y_pix; - float m_tx, m_ty; - float m_sc, m_rot; + float center_x_pix_, center_y_pix_; + float tx_, ty_; + float sc_, rot_; public: DirectionalBlurOperation(); @@ -56,7 +56,7 @@ class DirectionalBlurOperation : public MultiThreadedOperation, public QualitySt void setData(NodeDBlurData *data) { - m_data = data; + data_ = data; } void executeOpenCL(OpenCLDevice *device, diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc index dd4618ff827..8476db73b32 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc @@ -29,12 +29,12 @@ DisplaceOperation::DisplaceOperation() this->addOutputSocket(DataType::Color); this->flags.complex = true; - m_inputColorProgram = nullptr; + inputColorProgram_ = nullptr; } void DisplaceOperation::initExecution() { - m_inputColorProgram = this->getInputSocketReader(0); + inputColorProgram_ = this->getInputSocketReader(0); NodeOperation *vector = this->getInputSocketReader(1); NodeOperation *scale_x = this->getInputSocketReader(2); NodeOperation *scale_y = this->getInputSocketReader(3); @@ -50,8 +50,8 @@ void DisplaceOperation::initExecution() }; } - m_width_x4 = this->getWidth() * 4; - m_height_x4 = this->getHeight() * 4; + width_x4_ = this->getWidth() * 4; + height_x4_ = this->getHeight() * 4; input_vector_width_ = vector->getWidth(); input_vector_height_ = vector->getHeight(); } @@ -66,11 +66,11 @@ void DisplaceOperation::executePixelSampled(float output[4], pixelTransform(xy, uv, deriv); if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) { - m_inputColorProgram->readSampled(output, uv[0], uv[1], PixelSampler::Bilinear); + inputColorProgram_->readSampled(output, uv[0], uv[1], PixelSampler::Bilinear); } else { /* EWA filtering (without nearest it gets blurry with NO distortion) */ - m_inputColorProgram->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + inputColorProgram_->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); } } @@ -104,8 +104,8 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r float ys = col[0]; /* clamp x and y displacement to triple image resolution - * to prevent hangs from huge values mistakenly plugged in eg. z buffers */ - CLAMP(xs, -m_width_x4, m_width_x4); - CLAMP(ys, -m_height_x4, m_height_x4); + CLAMP(xs, -width_x4_, width_x4_); + CLAMP(ys, -height_x4_, height_x4_); /* displaced pixel in uv coords, for image sampling */ read_displacement(xy[0], xy[1], xs, ys, xy, r_uv[0], r_uv[1]); @@ -153,7 +153,7 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r void DisplaceOperation::deinitExecution() { - m_inputColorProgram = nullptr; + inputColorProgram_ = nullptr; vector_read_fn_ = nullptr; scale_x_read_fn_ = nullptr; scale_y_read_fn_ = nullptr; diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.h b/source/blender/compositor/operations/COM_DisplaceOperation.h index 5be914ab672..56cd1c1f2a6 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.h +++ b/source/blender/compositor/operations/COM_DisplaceOperation.h @@ -27,10 +27,10 @@ class DisplaceOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputColorProgram; + SocketReader *inputColorProgram_; - float m_width_x4; - float m_height_x4; + float width_x4_; + float height_x4_; int input_vector_width_; int input_vector_height_; diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc index fa7c4199fca..77d7831c680 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc @@ -28,21 +28,21 @@ DisplaceSimpleOperation::DisplaceSimpleOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_inputColorProgram = nullptr; - m_inputVectorProgram = nullptr; - m_inputScaleXProgram = nullptr; - m_inputScaleYProgram = nullptr; + inputColorProgram_ = nullptr; + inputVectorProgram_ = nullptr; + inputScaleXProgram_ = nullptr; + inputScaleYProgram_ = nullptr; } void DisplaceSimpleOperation::initExecution() { - m_inputColorProgram = this->getInputSocketReader(0); - m_inputVectorProgram = this->getInputSocketReader(1); - m_inputScaleXProgram = this->getInputSocketReader(2); - m_inputScaleYProgram = this->getInputSocketReader(3); + inputColorProgram_ = this->getInputSocketReader(0); + inputVectorProgram_ = this->getInputSocketReader(1); + inputScaleXProgram_ = this->getInputSocketReader(2); + inputScaleYProgram_ = this->getInputSocketReader(3); - m_width_x4 = this->getWidth() * 4; - m_height_x4 = this->getHeight() * 4; + width_x4_ = this->getWidth() * 4; + height_x4_ = this->getHeight() * 4; } /* minimum distance (in pixels) a pixel has to be displaced @@ -60,17 +60,17 @@ void DisplaceSimpleOperation::executePixelSampled(float output[4], float p_dx, p_dy; /* main displacement in pixel space */ float u, v; - m_inputScaleXProgram->readSampled(inScale, x, y, sampler); + inputScaleXProgram_->readSampled(inScale, x, y, sampler); float xs = inScale[0]; - m_inputScaleYProgram->readSampled(inScale, x, y, sampler); + inputScaleYProgram_->readSampled(inScale, x, y, sampler); float ys = inScale[0]; /* clamp x and y displacement to triple image resolution - * to prevent hangs from huge values mistakenly plugged in eg. z buffers */ - CLAMP(xs, -m_width_x4, m_width_x4); - CLAMP(ys, -m_height_x4, m_height_x4); + CLAMP(xs, -width_x4_, width_x4_); + CLAMP(ys, -height_x4_, height_x4_); - m_inputVectorProgram->readSampled(inVector, x, y, sampler); + inputVectorProgram_->readSampled(inVector, x, y, sampler); p_dx = inVector[0] * xs; p_dy = inVector[1] * ys; @@ -81,15 +81,15 @@ void DisplaceSimpleOperation::executePixelSampled(float output[4], CLAMP(u, 0.0f, this->getWidth() - 1.0f); CLAMP(v, 0.0f, this->getHeight() - 1.0f); - m_inputColorProgram->readSampled(output, u, v, sampler); + inputColorProgram_->readSampled(output, u, v, sampler); } void DisplaceSimpleOperation::deinitExecution() { - m_inputColorProgram = nullptr; - m_inputVectorProgram = nullptr; - m_inputScaleXProgram = nullptr; - m_inputScaleYProgram = nullptr; + inputColorProgram_ = nullptr; + inputVectorProgram_ = nullptr; + inputScaleXProgram_ = nullptr; + inputScaleYProgram_ = nullptr; } bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input, @@ -160,8 +160,8 @@ void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output, /* Clamp x and y displacement to triple image resolution - * to prevent hangs from huge values mistakenly plugged in eg. z buffers. */ - CLAMP(scale_x, -m_width_x4, m_width_x4); - CLAMP(scale_y, -m_height_x4, m_height_x4); + CLAMP(scale_x, -width_x4_, width_x4_); + CLAMP(scale_y, -height_x4_, height_x4_); /* Main displacement in pixel space. */ const float *vector = it.in(0); diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h index 99f52155466..1baf8c8275c 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h @@ -27,13 +27,13 @@ class DisplaceSimpleOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputColorProgram; - SocketReader *m_inputVectorProgram; - SocketReader *m_inputScaleXProgram; - SocketReader *m_inputScaleYProgram; + SocketReader *inputColorProgram_; + SocketReader *inputVectorProgram_; + SocketReader *inputScaleXProgram_; + SocketReader *inputScaleYProgram_; - float m_width_x4; - float m_height_x4; + float width_x4_; + float height_x4_; public: DisplaceSimpleOperation(); diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc index 0439cb35ba8..e076bf4ae7d 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc @@ -26,21 +26,21 @@ DistanceRGBMatteOperation::DistanceRGBMatteOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Value); - m_inputImageProgram = nullptr; - m_inputKeyProgram = nullptr; + inputImageProgram_ = nullptr; + inputKeyProgram_ = nullptr; flags.can_be_constant = true; } void DistanceRGBMatteOperation::initExecution() { - m_inputImageProgram = this->getInputSocketReader(0); - m_inputKeyProgram = this->getInputSocketReader(1); + inputImageProgram_ = this->getInputSocketReader(0); + inputKeyProgram_ = this->getInputSocketReader(1); } void DistanceRGBMatteOperation::deinitExecution() { - m_inputImageProgram = nullptr; - m_inputKeyProgram = nullptr; + inputImageProgram_ = nullptr; + inputKeyProgram_ = nullptr; } float DistanceRGBMatteOperation::calculateDistance(const float key[4], const float image[4]) @@ -56,14 +56,14 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4], float inKey[4]; float inImage[4]; - const float tolerance = m_settings->t1; - const float falloff = m_settings->t2; + const float tolerance = settings_->t1; + const float falloff = settings_->t2; float distance; float alpha; - m_inputKeyProgram->readSampled(inKey, x, y, sampler); - m_inputImageProgram->readSampled(inImage, x, y, sampler); + inputKeyProgram_->readSampled(inKey, x, y, sampler); + inputImageProgram_->readSampled(inImage, x, y, sampler); distance = this->calculateDistance(inKey, inImage); @@ -102,8 +102,8 @@ void DistanceRGBMatteOperation::update_memory_buffer_partial(MemoryBuffer *outpu const float *in_key = it.in(1); float distance = this->calculateDistance(in_key, in_image); - const float tolerance = m_settings->t1; - const float falloff = m_settings->t2; + const float tolerance = settings_->t1; + const float falloff = settings_->t2; /* Store matte(alpha) value in [0] to go with * COM_SetAlphaMultiplyOperation and the Value output. diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h index 0effad2accd..e741a9b80d5 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DistanceRGBMatteOperation : public MultiThreadedOperation { protected: - NodeChroma *m_settings; - SocketReader *m_inputImageProgram; - SocketReader *m_inputKeyProgram; + NodeChroma *settings_; + SocketReader *inputImageProgram_; + SocketReader *inputKeyProgram_; virtual float calculateDistance(const float key[4], const float image[4]); @@ -50,7 +50,7 @@ class DistanceRGBMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma) { - m_settings = nodeChroma; + settings_ = nodeChroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc index 86df465ca31..ef309392d4d 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.cc +++ b/source/blender/compositor/operations/COM_DotproductOperation.cc @@ -26,20 +26,20 @@ DotproductOperation::DotproductOperation() this->addInputSocket(DataType::Vector); this->addOutputSocket(DataType::Value); this->set_canvas_input_index(0); - m_input1Operation = nullptr; - m_input2Operation = nullptr; + input1Operation_ = nullptr; + input2Operation_ = nullptr; flags.can_be_constant = true; } void DotproductOperation::initExecution() { - m_input1Operation = this->getInputSocketReader(0); - m_input2Operation = this->getInputSocketReader(1); + input1Operation_ = this->getInputSocketReader(0); + input2Operation_ = this->getInputSocketReader(1); } void DotproductOperation::deinitExecution() { - m_input1Operation = nullptr; - m_input2Operation = nullptr; + input1Operation_ = nullptr; + input2Operation_ = nullptr; } /** \todo current implementation is the inverse of a dot-product. not 'logically' correct @@ -51,8 +51,8 @@ void DotproductOperation::executePixelSampled(float output[4], { float input1[4]; float input2[4]; - m_input1Operation->readSampled(input1, x, y, sampler); - m_input2Operation->readSampled(input2, x, y, sampler); + input1Operation_->readSampled(input1, x, y, sampler); + input2Operation_->readSampled(input2, x, y, sampler); output[0] = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]); } diff --git a/source/blender/compositor/operations/COM_DotproductOperation.h b/source/blender/compositor/operations/COM_DotproductOperation.h index c3f39d43fff..5190a161533 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.h +++ b/source/blender/compositor/operations/COM_DotproductOperation.h @@ -24,8 +24,8 @@ namespace blender::compositor { class DotproductOperation : public MultiThreadedOperation { private: - SocketReader *m_input1Operation; - SocketReader *m_input2Operation; + SocketReader *input1Operation_; + SocketReader *input2Operation_; public: DotproductOperation(); diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc index b83d7f343bd..2ae33d5451f 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc @@ -1267,8 +1267,8 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float * * Each version has slightly different criteria for detecting an edge pixel. */ - if (m_adjacentOnly) { /* If "adjacent only" inner edge mode is turned on. */ - if (m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */ + if (adjacentOnly_) { /* If "adjacent only" inner edge mode is turned on. */ + if (keepInside_) { /* If "keep inside" buffer edge mode is turned on. */ do_adjacentKeepBorders(t, rw, limask, lomask, lres, res, rsize); } else { /* "bleed out" buffer edge mode is turned on. */ @@ -1281,8 +1281,8 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float /* Detect edges in all non-border pixels in the buffer. */ do_adjacentEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz); } - else { /* "all" inner edge mode is turned on. */ - if (m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */ + else { /* "all" inner edge mode is turned on. */ + if (keepInside_) { /* If "keep inside" buffer edge mode is turned on. */ do_allKeepBorders(t, rw, limask, lomask, lres, res, rsize); } else { /* "bleed out" buffer edge mode is turned on. */ @@ -1322,10 +1322,10 @@ DoubleEdgeMaskOperation::DoubleEdgeMaskOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_inputInnerMask = nullptr; - m_inputOuterMask = nullptr; - m_adjacentOnly = false; - m_keepInside = false; + inputInnerMask_ = nullptr; + inputOuterMask_ = nullptr; + adjacentOnly_ = false; + keepInside_ = false; this->flags.complex = true; is_output_rendered_ = false; } @@ -1334,7 +1334,7 @@ bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) { - if (m_cachedInstance == nullptr) { + if (cachedInstance_ == nullptr) { rcti newInput; newInput.xmax = this->getWidth(); newInput.xmin = 0; @@ -1348,31 +1348,31 @@ bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/, void DoubleEdgeMaskOperation::initExecution() { - m_inputInnerMask = this->getInputSocketReader(0); - m_inputOuterMask = this->getInputSocketReader(1); + inputInnerMask_ = this->getInputSocketReader(0); + inputOuterMask_ = this->getInputSocketReader(1); initMutex(); - m_cachedInstance = nullptr; + cachedInstance_ = nullptr; } void *DoubleEdgeMaskOperation::initializeTileData(rcti *rect) { - if (m_cachedInstance) { - return m_cachedInstance; + if (cachedInstance_) { + return cachedInstance_; } lockMutex(); - if (m_cachedInstance == nullptr) { - MemoryBuffer *innerMask = (MemoryBuffer *)m_inputInnerMask->initializeTileData(rect); - MemoryBuffer *outerMask = (MemoryBuffer *)m_inputOuterMask->initializeTileData(rect); + if (cachedInstance_ == nullptr) { + MemoryBuffer *innerMask = (MemoryBuffer *)inputInnerMask_->initializeTileData(rect); + MemoryBuffer *outerMask = (MemoryBuffer *)inputOuterMask_->initializeTileData(rect); float *data = (float *)MEM_mallocN(sizeof(float) * this->getWidth() * this->getHeight(), __func__); float *imask = innerMask->getBuffer(); float *omask = outerMask->getBuffer(); doDoubleEdgeMask(imask, omask, data); - m_cachedInstance = data; + cachedInstance_ = data; } unlockMutex(); - return m_cachedInstance; + return cachedInstance_; } void DoubleEdgeMaskOperation::executePixel(float output[4], int x, int y, void *data) { @@ -1383,12 +1383,12 @@ void DoubleEdgeMaskOperation::executePixel(float output[4], int x, int y, void * void DoubleEdgeMaskOperation::deinitExecution() { - m_inputInnerMask = nullptr; - m_inputOuterMask = nullptr; + inputInnerMask_ = nullptr; + inputOuterMask_ = nullptr; deinitMutex(); - if (m_cachedInstance) { - MEM_freeN(m_cachedInstance); - m_cachedInstance = nullptr; + if (cachedInstance_) { + MEM_freeN(cachedInstance_); + cachedInstance_ = nullptr; } } diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h index 091310dc9e3..29cb40c29af 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h @@ -27,13 +27,13 @@ class DoubleEdgeMaskOperation : public NodeOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputOuterMask; - SocketReader *m_inputInnerMask; - bool m_adjacentOnly; - bool m_keepInside; + SocketReader *inputOuterMask_; + SocketReader *inputInnerMask_; + bool adjacentOnly_; + bool keepInside_; /* TODO(manzanilla): To be removed with tiled implementation. */ - float *m_cachedInstance; + float *cachedInstance_; bool is_output_rendered_; @@ -64,11 +64,11 @@ class DoubleEdgeMaskOperation : public NodeOperation { void setAdjecentOnly(bool adjacentOnly) { - m_adjacentOnly = adjacentOnly; + adjacentOnly_ = adjacentOnly; } void setKeepInside(bool keepInside) { - m_keepInside = keepInside; + keepInside_ = keepInside; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc index e3649fc9013..57cd3cb1423 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc @@ -25,19 +25,19 @@ EllipseMaskOperation::EllipseMaskOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_inputMask = nullptr; - m_inputValue = nullptr; - m_cosine = 0.0f; - m_sine = 0.0f; + inputMask_ = nullptr; + inputValue_ = nullptr; + cosine_ = 0.0f; + sine_ = 0.0f; } void EllipseMaskOperation::initExecution() { - m_inputMask = this->getInputSocketReader(0); - m_inputValue = this->getInputSocketReader(1); - const double rad = (double)m_data->rotation; - m_cosine = cos(rad); - m_sine = sin(rad); - m_aspectRatio = ((float)this->getWidth()) / this->getHeight(); + inputMask_ = this->getInputSocketReader(0); + inputValue_ = this->getInputSocketReader(1); + const double rad = (double)data_->rotation; + cosine_ = cos(rad); + sine_ = sin(rad); + aspectRatio_ = ((float)this->getWidth()) / this->getHeight(); } void EllipseMaskOperation::executePixelSampled(float output[4], @@ -51,26 +51,26 @@ void EllipseMaskOperation::executePixelSampled(float output[4], float rx = x / this->getWidth(); float ry = y / this->getHeight(); - const float dy = (ry - m_data->y) / m_aspectRatio; - const float dx = rx - m_data->x; - rx = m_data->x + (m_cosine * dx + m_sine * dy); - ry = m_data->y + (-m_sine * dx + m_cosine * dy); + const float dy = (ry - data_->y) / aspectRatio_; + const float dx = rx - data_->x; + rx = data_->x + (cosine_ * dx + sine_ * dy); + ry = data_->y + (-sine_ * dx + cosine_ * dy); - m_inputMask->readSampled(inputMask, x, y, sampler); - m_inputValue->readSampled(inputValue, x, y, sampler); + inputMask_->readSampled(inputMask, x, y, sampler); + inputValue_->readSampled(inputValue, x, y, sampler); - const float halfHeight = (m_data->height) / 2.0f; - const float halfWidth = m_data->width / 2.0f; - float sx = rx - m_data->x; + const float halfHeight = (data_->height) / 2.0f; + const float halfWidth = data_->width / 2.0f; + float sx = rx - data_->x; sx *= sx; const float tx = halfWidth * halfWidth; - float sy = ry - m_data->y; + float sy = ry - data_->y; sy *= sy; const float ty = halfHeight * halfHeight; bool inside = ((sx / tx) + (sy / ty)) < 1.0f; - switch (m_maskType) { + switch (maskType_) { case CMP_NODE_MASKTYPE_ADD: if (inside) { output[0] = MAX2(inputMask[0], inputValue[0]); @@ -117,7 +117,7 @@ void EllipseMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { MaskFunc mask_func; - switch (m_maskType) { + switch (maskType_) { case CMP_NODE_MASKTYPE_ADD: mask_func = [](const bool is_inside, const float *mask, const float *value) { return is_inside ? MAX2(mask[0], value[0]) : mask[0]; @@ -154,24 +154,24 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, const MemoryBuffer *input_value = inputs[1]; const float op_w = this->getWidth(); const float op_h = this->getHeight(); - const float half_w = m_data->width / 2.0f; - const float half_h = m_data->height / 2.0f; + const float half_w = data_->width / 2.0f; + const float half_h = data_->height / 2.0f; const float tx = half_w * half_w; const float ty = half_h * half_h; for (int y = area.ymin; y < area.ymax; y++) { const float op_ry = y / op_h; - const float dy = (op_ry - m_data->y) / m_aspectRatio; + const float dy = (op_ry - data_->y) / aspectRatio_; float *out = output->get_elem(area.xmin, y); const float *mask = input_mask->get_elem(area.xmin, y); const float *value = input_value->get_elem(area.xmin, y); for (int x = area.xmin; x < area.xmax; x++) { const float op_rx = x / op_w; - const float dx = op_rx - m_data->x; - const float rx = m_data->x + (m_cosine * dx + m_sine * dy); - const float ry = m_data->y + (-m_sine * dx + m_cosine * dy); - float sx = rx - m_data->x; + const float dx = op_rx - data_->x; + const float rx = data_->x + (cosine_ * dx + sine_ * dy); + const float ry = data_->y + (-sine_ * dx + cosine_ * dy); + float sx = rx - data_->x; sx *= sx; - float sy = ry - m_data->y; + float sy = ry - data_->y; sy *= sy; const bool inside = ((sx / tx) + (sy / ty)) < 1.0f; out[0] = mask_func(inside, mask, value); @@ -185,8 +185,8 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, void EllipseMaskOperation::deinitExecution() { - m_inputMask = nullptr; - m_inputValue = nullptr; + inputMask_ = nullptr; + inputValue_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.h b/source/blender/compositor/operations/COM_EllipseMaskOperation.h index b0bebb5b80d..ec6fb4624f7 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.h +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.h @@ -29,15 +29,15 @@ class EllipseMaskOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputMask; - SocketReader *m_inputValue; + SocketReader *inputMask_; + SocketReader *inputValue_; - float m_sine; - float m_cosine; - float m_aspectRatio; - int m_maskType; + float sine_; + float cosine_; + float aspectRatio_; + int maskType_; - NodeEllipseMask *m_data; + NodeEllipseMask *data_; public: EllipseMaskOperation(); @@ -59,12 +59,12 @@ class EllipseMaskOperation : public MultiThreadedOperation { void setData(NodeEllipseMask *data) { - m_data = data; + data_ = data; } void setMaskType(int maskType) { - m_maskType = maskType; + maskType_ = maskType; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index ea24a23522c..b1843a225e0 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -24,7 +24,7 @@ namespace blender::compositor { FastGaussianBlurOperation::FastGaussianBlurOperation() : BlurBaseOperation(DataType::Color) { - m_iirgaus = nullptr; + iirgaus_ = nullptr; } void FastGaussianBlurOperation::executePixel(float output[4], int x, int y, void *data) @@ -48,7 +48,7 @@ bool FastGaussianBlurOperation::determineDependingAreaOfInterest( return true; } - if (m_iirgaus) { + if (iirgaus_) { return false; } @@ -63,8 +63,8 @@ bool FastGaussianBlurOperation::determineDependingAreaOfInterest( void FastGaussianBlurOperation::init_data() { BlurBaseOperation::init_data(); - m_sx = m_data.sizex * m_size / 2.0f; - m_sy = m_data.sizey * m_size / 2.0f; + sx_ = data_.sizex * size_ / 2.0f; + sy_ = data_.sizey * size_ / 2.0f; } void FastGaussianBlurOperation::initExecution() @@ -75,9 +75,9 @@ void FastGaussianBlurOperation::initExecution() void FastGaussianBlurOperation::deinitExecution() { - if (m_iirgaus) { - delete m_iirgaus; - m_iirgaus = nullptr; + if (iirgaus_) { + delete iirgaus_; + iirgaus_ = nullptr; } BlurBaseOperation::deinitMutex(); } @@ -85,36 +85,36 @@ void FastGaussianBlurOperation::deinitExecution() void *FastGaussianBlurOperation::initializeTileData(rcti *rect) { lockMutex(); - if (!m_iirgaus) { - MemoryBuffer *newBuf = (MemoryBuffer *)m_inputProgram->initializeTileData(rect); + if (!iirgaus_) { + MemoryBuffer *newBuf = (MemoryBuffer *)inputProgram_->initializeTileData(rect); MemoryBuffer *copy = new MemoryBuffer(*newBuf); updateSize(); int c; - m_sx = m_data.sizex * m_size / 2.0f; - m_sy = m_data.sizey * m_size / 2.0f; + sx_ = data_.sizex * size_ / 2.0f; + sy_ = data_.sizey * size_ / 2.0f; - if ((m_sx == m_sy) && (m_sx > 0.0f)) { + if ((sx_ == sy_) && (sx_ > 0.0f)) { for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) { - IIR_gauss(copy, m_sx, c, 3); + IIR_gauss(copy, sx_, c, 3); } } else { - if (m_sx > 0.0f) { + if (sx_ > 0.0f) { for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) { - IIR_gauss(copy, m_sx, c, 1); + IIR_gauss(copy, sx_, c, 1); } } - if (m_sy > 0.0f) { + if (sy_ > 0.0f) { for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) { - IIR_gauss(copy, m_sy, c, 2); + IIR_gauss(copy, sy_, c, 2); } } } - m_iirgaus = copy; + iirgaus_ = copy; } unlockMutex(); - return m_iirgaus; + return iirgaus_; } void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, @@ -294,20 +294,20 @@ void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *outpu } image->copy_from(input, area); - if ((m_sx == m_sy) && (m_sx > 0.0f)) { + if ((sx_ == sy_) && (sx_ > 0.0f)) { for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { - IIR_gauss(image, m_sx, c, 3); + IIR_gauss(image, sx_, c, 3); } } else { - if (m_sx > 0.0f) { + if (sx_ > 0.0f) { for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { - IIR_gauss(image, m_sx, c, 1); + IIR_gauss(image, sx_, c, 1); } } - if (m_sy > 0.0f) { + if (sy_ > 0.0f) { for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { - IIR_gauss(image, m_sy, c, 2); + IIR_gauss(image, sy_, c, 2); } } } @@ -322,10 +322,10 @@ FastGaussianBlurValueOperation::FastGaussianBlurValueOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_iirgaus = nullptr; - m_inputprogram = nullptr; - m_sigma = 1.0f; - m_overlay = 0; + iirgaus_ = nullptr; + inputprogram_ = nullptr; + sigma_ = 1.0f; + overlay_ = 0; flags.complex = true; } @@ -340,7 +340,7 @@ bool FastGaussianBlurValueOperation::determineDependingAreaOfInterest( { rcti newInput; - if (m_iirgaus) { + if (iirgaus_) { return false; } @@ -354,15 +354,15 @@ bool FastGaussianBlurValueOperation::determineDependingAreaOfInterest( void FastGaussianBlurValueOperation::initExecution() { - m_inputprogram = getInputSocketReader(0); + inputprogram_ = getInputSocketReader(0); initMutex(); } void FastGaussianBlurValueOperation::deinitExecution() { - if (m_iirgaus) { - delete m_iirgaus; - m_iirgaus = nullptr; + if (iirgaus_) { + delete iirgaus_; + iirgaus_ = nullptr; } deinitMutex(); } @@ -370,12 +370,12 @@ void FastGaussianBlurValueOperation::deinitExecution() void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) { lockMutex(); - if (!m_iirgaus) { - MemoryBuffer *newBuf = (MemoryBuffer *)m_inputprogram->initializeTileData(rect); + if (!iirgaus_) { + MemoryBuffer *newBuf = (MemoryBuffer *)inputprogram_->initializeTileData(rect); MemoryBuffer *copy = new MemoryBuffer(*newBuf); - FastGaussianBlurOperation::IIR_gauss(copy, m_sigma, 0, 3); + FastGaussianBlurOperation::IIR_gauss(copy, sigma_, 0, 3); - if (m_overlay == FAST_GAUSS_OVERLAY_MIN) { + if (overlay_ == FAST_GAUSS_OVERLAY_MIN) { float *src = newBuf->getBuffer(); float *dst = copy->getBuffer(); for (int i = copy->getWidth() * copy->getHeight(); i != 0; @@ -385,7 +385,7 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) } } } - else if (m_overlay == FAST_GAUSS_OVERLAY_MAX) { + else if (overlay_ == FAST_GAUSS_OVERLAY_MAX) { float *src = newBuf->getBuffer(); float *dst = copy->getBuffer(); for (int i = copy->getWidth() * copy->getHeight(); i != 0; @@ -396,10 +396,10 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) } } - m_iirgaus = copy; + iirgaus_ = copy; } unlockMutex(); - return m_iirgaus; + return iirgaus_; } void FastGaussianBlurValueOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -413,11 +413,11 @@ void FastGaussianBlurValueOperation::update_memory_buffer_started(MemoryBuffer * const rcti &UNUSED(area), Span inputs) { - if (m_iirgaus == nullptr) { + if (iirgaus_ == nullptr) { const MemoryBuffer *image = inputs[0]; MemoryBuffer *gauss = new MemoryBuffer(*image); - FastGaussianBlurOperation::IIR_gauss(gauss, m_sigma, 0, 3); - m_iirgaus = gauss; + FastGaussianBlurOperation::IIR_gauss(gauss, sigma_, 0, 3); + iirgaus_ = gauss; } } @@ -426,13 +426,13 @@ void FastGaussianBlurValueOperation::update_memory_buffer_partial(MemoryBuffer * Span inputs) { MemoryBuffer *image = inputs[0]; - BuffersIterator it = output->iterate_with({image, m_iirgaus}, area); - if (m_overlay == FAST_GAUSS_OVERLAY_MIN) { + BuffersIterator it = output->iterate_with({image, iirgaus_}, area); + if (overlay_ == FAST_GAUSS_OVERLAY_MIN) { for (; !it.is_end(); ++it) { *it.out = MIN2(*it.in(0), *it.in(1)); } } - else if (m_overlay == FAST_GAUSS_OVERLAY_MAX) { + else if (overlay_ == FAST_GAUSS_OVERLAY_MAX) { for (; !it.is_end(); ++it) { *it.out = MAX2(*it.in(0), *it.in(1)); } diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h index b2ad199f17d..2a0ed078612 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h @@ -25,9 +25,9 @@ namespace blender::compositor { class FastGaussianBlurOperation : public BlurBaseOperation { private: - float m_sx; - float m_sy; - MemoryBuffer *m_iirgaus; + float sx_; + float sy_; + MemoryBuffer *iirgaus_; public: FastGaussianBlurOperation(); @@ -61,15 +61,15 @@ enum { class FastGaussianBlurValueOperation : public MultiThreadedOperation { private: - float m_sigma; - MemoryBuffer *m_iirgaus; - SocketReader *m_inputprogram; + float sigma_; + MemoryBuffer *iirgaus_; + SocketReader *inputprogram_; /** * -1: re-mix with darker * 0: do nothing * 1 re-mix with lighter */ - int m_overlay; + int overlay_; public: FastGaussianBlurValueOperation(); @@ -83,13 +83,13 @@ class FastGaussianBlurValueOperation : public MultiThreadedOperation { void initExecution() override; void setSigma(float sigma) { - m_sigma = sigma; + sigma_ = sigma; } /* used for DOF blurring ZBuffer */ void setOverlay(int overlay) { - m_overlay = overlay; + overlay_ = overlay; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc index c08dce2fc01..ae2aa21131a 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.cc +++ b/source/blender/compositor/operations/COM_FlipOperation.cc @@ -25,26 +25,26 @@ FlipOperation::FlipOperation() this->addInputSocket(DataType::Color, ResizeMode::None); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - m_inputOperation = nullptr; - m_flipX = true; - m_flipY = false; + inputOperation_ = nullptr; + flipX_ = true; + flipY_ = false; } void FlipOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); + inputOperation_ = this->getInputSocketReader(0); } void FlipOperation::deinitExecution() { - m_inputOperation = nullptr; + inputOperation_ = nullptr; } void FlipOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { - float nx = m_flipX ? ((int)this->getWidth() - 1) - x : x; - float ny = m_flipY ? ((int)this->getHeight() - 1) - y : y; + float nx = flipX_ ? ((int)this->getWidth() - 1) - x : x; + float ny = flipY_ ? ((int)this->getHeight() - 1) - y : y; - m_inputOperation->readSampled(output, nx, ny, sampler); + inputOperation_->readSampled(output, nx, ny, sampler); } bool FlipOperation::determineDependingAreaOfInterest(rcti *input, @@ -53,7 +53,7 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - if (m_flipX) { + if (flipX_) { const int w = (int)this->getWidth() - 1; newInput.xmax = (w - input->xmin) + 1; newInput.xmin = (w - input->xmax) - 1; @@ -62,7 +62,7 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input, newInput.xmin = input->xmin; newInput.xmax = input->xmax; } - if (m_flipY) { + if (flipY_) { const int h = (int)this->getHeight() - 1; newInput.ymax = (h - input->ymin) + 1; newInput.ymin = (h - input->ymax) - 1; @@ -80,12 +80,12 @@ void FlipOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) NodeOperation::determine_canvas(preferred_area, r_area); if (execution_model_ == eExecutionModel::FullFrame) { rcti input_area = r_area; - if (m_flipX) { + if (flipX_) { const int width = BLI_rcti_size_x(&input_area) - 1; r_area.xmax = (width - input_area.xmin) + 1; r_area.xmin = (width - input_area.xmax) + 1; } - if (m_flipY) { + if (flipY_) { const int height = BLI_rcti_size_y(&input_area) - 1; r_area.ymax = (height - input_area.ymin) + 1; r_area.ymin = (height - input_area.ymax) + 1; @@ -99,7 +99,7 @@ void FlipOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - if (m_flipX) { + if (flipX_) { const int w = (int)this->getWidth() - 1; r_input_area.xmax = (w - output_area.xmin) + 1; r_input_area.xmin = (w - output_area.xmax) + 1; @@ -108,7 +108,7 @@ void FlipOperation::get_area_of_interest(const int input_idx, r_input_area.xmin = output_area.xmin; r_input_area.xmax = output_area.xmax; } - if (m_flipY) { + if (flipY_) { const int h = (int)this->getHeight() - 1; r_input_area.ymax = (h - output_area.ymin) + 1; r_input_area.ymin = (h - output_area.ymax) + 1; @@ -127,8 +127,8 @@ void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output, const int input_offset_x = input_img->get_rect().xmin; const int input_offset_y = input_img->get_rect().ymin; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - const int nx = m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x; - const int ny = m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y; + const int nx = flipX_ ? ((int)this->getWidth() - 1) - it.x : it.x; + const int ny = flipY_ ? ((int)this->getHeight() - 1) - it.y : it.y; input_img->read_elem(input_offset_x + nx, input_offset_y + ny, it.out); } } diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h index b43c3a33495..3310819051a 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.h +++ b/source/blender/compositor/operations/COM_FlipOperation.h @@ -24,9 +24,9 @@ namespace blender::compositor { class FlipOperation : public MultiThreadedOperation { private: - SocketReader *m_inputOperation; - bool m_flipX; - bool m_flipY; + SocketReader *inputOperation_; + bool flipX_; + bool flipY_; public: FlipOperation(); @@ -39,11 +39,11 @@ class FlipOperation : public MultiThreadedOperation { void deinitExecution() override; void setFlipX(bool flipX) { - m_flipX = flipX; + flipX_ = flipX; } void setFlipY(bool flipY) { - m_flipY = flipY; + flipY_ = flipY; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc index 886ff9d09c2..9a51c789943 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc @@ -24,12 +24,12 @@ GammaCorrectOperation::GammaCorrectOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_inputProgram = nullptr; + inputProgram_ = nullptr; flags.can_be_constant = true; } void GammaCorrectOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); } void GammaCorrectOperation::executePixelSampled(float output[4], @@ -38,7 +38,7 @@ void GammaCorrectOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - m_inputProgram->readSampled(inputColor, x, y, sampler); + inputProgram_->readSampled(inputColor, x, y, sampler); if (inputColor[3] > 0.0f) { inputColor[0] /= inputColor[3]; inputColor[1] /= inputColor[3]; @@ -88,19 +88,19 @@ void GammaCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, void GammaCorrectOperation::deinitExecution() { - m_inputProgram = nullptr; + inputProgram_ = nullptr; } GammaUncorrectOperation::GammaUncorrectOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_inputProgram = nullptr; + inputProgram_ = nullptr; flags.can_be_constant = true; } void GammaUncorrectOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); } void GammaUncorrectOperation::executePixelSampled(float output[4], @@ -109,7 +109,7 @@ void GammaUncorrectOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inputColor[4]; - m_inputProgram->readSampled(inputColor, x, y, sampler); + inputProgram_->readSampled(inputColor, x, y, sampler); if (inputColor[3] > 0.0f) { inputColor[0] /= inputColor[3]; @@ -158,7 +158,7 @@ void GammaUncorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, void GammaUncorrectOperation::deinitExecution() { - m_inputProgram = nullptr; + inputProgram_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.h b/source/blender/compositor/operations/COM_GammaCorrectOperation.h index 2a9fde70e87..5c6307fe6d0 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.h +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.h @@ -27,7 +27,7 @@ class GammaCorrectOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; + SocketReader *inputProgram_; public: GammaCorrectOperation(); @@ -57,7 +57,7 @@ class GammaUncorrectOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; + SocketReader *inputProgram_; public: GammaUncorrectOperation(); diff --git a/source/blender/compositor/operations/COM_GammaOperation.cc b/source/blender/compositor/operations/COM_GammaOperation.cc index 21430a68848..a809b16f523 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.cc +++ b/source/blender/compositor/operations/COM_GammaOperation.cc @@ -25,14 +25,14 @@ GammaOperation::GammaOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_inputProgram = nullptr; - m_inputGammaProgram = nullptr; + inputProgram_ = nullptr; + inputGammaProgram_ = nullptr; flags.can_be_constant = true; } void GammaOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); - m_inputGammaProgram = this->getInputSocketReader(1); + inputProgram_ = this->getInputSocketReader(0); + inputGammaProgram_ = this->getInputSocketReader(1); } void GammaOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -40,8 +40,8 @@ void GammaOperation::executePixelSampled(float output[4], float x, float y, Pixe float inputValue[4]; float inputGamma[4]; - m_inputProgram->readSampled(inputValue, x, y, sampler); - m_inputGammaProgram->readSampled(inputGamma, x, y, sampler); + inputProgram_->readSampled(inputValue, x, y, sampler); + inputGammaProgram_->readSampled(inputGamma, x, y, sampler); const float gamma = inputGamma[0]; /* check for negative to avoid nan's */ output[0] = inputValue[0] > 0.0f ? powf(inputValue[0], gamma) : inputValue[0]; @@ -67,8 +67,8 @@ void GammaOperation::update_memory_buffer_row(PixelCursor &p) void GammaOperation::deinitExecution() { - m_inputProgram = nullptr; - m_inputGammaProgram = nullptr; + inputProgram_ = nullptr; + inputGammaProgram_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GammaOperation.h b/source/blender/compositor/operations/COM_GammaOperation.h index 713d3d8484f..9b68a2c8a1b 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.h +++ b/source/blender/compositor/operations/COM_GammaOperation.h @@ -27,8 +27,8 @@ class GammaOperation : public MultiThreadedRowOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; - SocketReader *m_inputGammaProgram; + SocketReader *inputProgram_; + SocketReader *inputGammaProgram_; public: GammaOperation(); diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc index 20459a8b038..fa742775f24 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc @@ -23,9 +23,9 @@ namespace blender::compositor { GaussianAlphaBlurBaseOperation::GaussianAlphaBlurBaseOperation(eDimension dim) : BlurBaseOperation(DataType::Value) { - m_gausstab = nullptr; - m_filtersize = 0; - m_falloff = -1; /* Intentionally invalid, so we can detect uninitialized values. */ + gausstab_ = nullptr; + filtersize_ = 0; + falloff_ = -1; /* Intentionally invalid, so we can detect uninitialized values. */ dimension_ = dim; } @@ -33,9 +33,9 @@ void GaussianAlphaBlurBaseOperation::init_data() { BlurBaseOperation::init_data(); if (execution_model_ == eExecutionModel::FullFrame) { - rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f); + rad_ = max_ff(size_ * this->get_blur_size(dimension_), 0.0f); rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); } } @@ -43,8 +43,8 @@ void GaussianAlphaBlurBaseOperation::initExecution() { BlurBaseOperation::initExecution(); if (execution_model_ == eExecutionModel::FullFrame) { - m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize); - m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad_, m_filtersize, m_falloff); + gausstab_ = BlurBaseOperation::make_gausstab(rad_, filtersize_); + distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad_, filtersize_, falloff_); } } @@ -52,14 +52,14 @@ void GaussianAlphaBlurBaseOperation::deinitExecution() { BlurBaseOperation::deinitExecution(); - if (m_gausstab) { - MEM_freeN(m_gausstab); - m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } - if (m_distbuf_inv) { - MEM_freeN(m_distbuf_inv); - m_distbuf_inv = nullptr; + if (distbuf_inv_) { + MEM_freeN(distbuf_inv_); + distbuf_inv_ = nullptr; } } @@ -75,12 +75,12 @@ void GaussianAlphaBlurBaseOperation::get_area_of_interest(const int input_idx, r_input_area = output_area; switch (dimension_) { case eDimension::X: - r_input_area.xmin = output_area.xmin - m_filtersize - 1; - r_input_area.xmax = output_area.xmax + m_filtersize + 1; + r_input_area.xmin = output_area.xmin - filtersize_ - 1; + r_input_area.xmax = output_area.xmax + filtersize_ + 1; break; case eDimension::Y: - r_input_area.ymin = output_area.ymin - m_filtersize - 1; - r_input_area.ymax = output_area.ymax + m_filtersize + 1; + r_input_area.ymin = output_area.ymin - filtersize_ - 1; + r_input_area.ymax = output_area.ymax + filtersize_ + 1; break; } } @@ -119,8 +119,8 @@ void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer * for (; !it.is_end(); ++it) { const int coord = get_current_coord(); - const int coord_min = max_ii(coord - m_filtersize, min_input_coord); - const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord); + const int coord_min = max_ii(coord - filtersize_, min_input_coord); + const int coord_max = min_ii(coord + filtersize_ + 1, max_input_coord); /* *** This is the main part which is different to #GaussianBlurBaseOperation. *** */ /* Gauss. */ @@ -128,7 +128,7 @@ void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer * float multiplier_accum = 0.0f; /* Dilate. */ - const bool do_invert = m_do_subtract; + const bool do_invert = do_subtract_; /* Init with the current color to avoid unneeded lookups. */ float value_max = finv_test(*it.in(0), do_invert); float distfacinv_max = 1.0f; /* 0 to 1 */ @@ -136,19 +136,19 @@ void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer * const int step = QualityStepHelper::getStep(); const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride; const int in_stride = elem_stride * step; - int index = (coord_min - coord) + m_filtersize; + int index = (coord_min - coord) + filtersize_; const int index_end = index + (coord_max - coord_min); for (; index < index_end; in += in_stride, index += step) { float value = finv_test(*in, do_invert); /* Gauss. */ - float multiplier = m_gausstab[index]; + float multiplier = gausstab_[index]; alpha_accum += value * multiplier; multiplier_accum += multiplier; /* Dilate - find most extreme color. */ if (value > value_max) { - multiplier = m_distbuf_inv[index]; + multiplier = distbuf_inv_[index]; value *= multiplier; if (value > value_max) { value_max = value; diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h index bb03b672a34..307d8b0dff5 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h @@ -24,11 +24,11 @@ namespace blender::compositor { class GaussianAlphaBlurBaseOperation : public BlurBaseOperation { protected: - float *m_gausstab; - float *m_distbuf_inv; - int m_falloff; /* Falloff for #distbuf_inv. */ - bool m_do_subtract; - int m_filtersize; + float *gausstab_; + float *distbuf_inv_; + int falloff_; /* Falloff for #distbuf_inv. */ + bool do_subtract_; + int filtersize_; float rad_; eDimension dimension_; @@ -51,11 +51,11 @@ class GaussianAlphaBlurBaseOperation : public BlurBaseOperation { */ void setSubtract(bool subtract) { - m_do_subtract = subtract; + do_subtract_ = subtract; } void setFalloff(int falloff) { - m_falloff = falloff; + falloff_ = falloff; } }; diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc index 4900c12f13a..b854ea736b3 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc @@ -28,7 +28,7 @@ GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!m_sizeavailable) { + if (!sizeavailable_) { updateGauss(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -42,32 +42,32 @@ void GaussianAlphaXBlurOperation::initExecution() initMutex(); - if (m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { - float rad = max_ff(m_size * m_data.sizex, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { + float rad = max_ff(size_ * data_.sizex, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); - m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, m_filtersize, m_falloff); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); + distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad, filtersize_, falloff_); } } void GaussianAlphaXBlurOperation::updateGauss() { - if (m_gausstab == nullptr) { + if (gausstab_ == nullptr) { updateSize(); - float rad = max_ff(m_size * m_data.sizex, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + float rad = max_ff(size_ * data_.sizex, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); } - if (m_distbuf_inv == nullptr) { + if (distbuf_inv_ == nullptr) { updateSize(); - float rad = max_ff(m_size * m_data.sizex, 0.0f); + float rad = max_ff(size_ * data_.sizex, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, m_filtersize, m_falloff); + distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad, filtersize_, falloff_); } } @@ -78,7 +78,7 @@ BLI_INLINE float finv_test(const float f, const bool test) void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, void *data) { - const bool do_invert = m_do_subtract; + const bool do_invert = do_subtract_; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; float *buffer = inputBuffer->getBuffer(); int bufferwidth = inputBuffer->getWidth(); @@ -87,8 +87,8 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo int bufferstarty = input_rect.ymin; const rcti &rect = inputBuffer->get_rect(); - int xmin = max_ii(x - m_filtersize, rect.xmin); - int xmax = min_ii(x + m_filtersize + 1, rect.xmax); + int xmin = max_ii(x - filtersize_, rect.xmin); + int xmax = min_ii(x + filtersize_ + 1, rect.xmax); int ymin = max_ii(y, rect.ymin); /* *** this is the main part which is different to 'GaussianXBlurOperation' *** */ @@ -106,20 +106,20 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo float distfacinv_max = 1.0f; /* 0 to 1 */ for (int nx = xmin; nx < xmax; nx += step) { - const int index = (nx - x) + m_filtersize; + const int index = (nx - x) + filtersize_; float value = finv_test(buffer[bufferindex], do_invert); float multiplier; /* gauss */ { - multiplier = m_gausstab[index]; + multiplier = gausstab_[index]; alpha_accum += value * multiplier; multiplier_accum += multiplier; } /* dilate - find most extreme color */ if (value > value_max) { - multiplier = m_distbuf_inv[index]; + multiplier = distbuf_inv_[index]; value *= multiplier; if (value > value_max) { value_max = value; @@ -139,14 +139,14 @@ void GaussianAlphaXBlurOperation::deinitExecution() { GaussianAlphaBlurBaseOperation::deinitExecution(); - if (m_gausstab) { - MEM_freeN(m_gausstab); - m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } - if (m_distbuf_inv) { - MEM_freeN(m_distbuf_inv); - m_distbuf_inv = nullptr; + if (distbuf_inv_) { + MEM_freeN(distbuf_inv_); + distbuf_inv_ = nullptr; } deinitMutex(); @@ -170,9 +170,9 @@ bool GaussianAlphaXBlurOperation::determineDependingAreaOfInterest( else #endif { - if (m_sizeavailable && m_gausstab != nullptr) { - newInput.xmax = input->xmax + m_filtersize + 1; - newInput.xmin = input->xmin - m_filtersize - 1; + if (sizeavailable_ && gausstab_ != nullptr) { + newInput.xmax = input->xmax + filtersize_ + 1; + newInput.xmin = input->xmin - filtersize_ - 1; newInput.ymax = input->ymax; newInput.ymin = input->ymin; } diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc index 49f5902ba9c..eaf3abc18cd 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc @@ -28,7 +28,7 @@ GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!m_sizeavailable) { + if (!sizeavailable_) { updateGauss(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -43,33 +43,33 @@ void GaussianAlphaYBlurOperation::initExecution() initMutex(); - if (m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { - float rad = max_ff(m_size * m_data.sizey, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { + float rad = max_ff(size_ * data_.sizey, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); - m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, m_filtersize, m_falloff); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); + distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad, filtersize_, falloff_); } } /* TODO(manzanilla): to be removed with tiled implementation. */ void GaussianAlphaYBlurOperation::updateGauss() { - if (m_gausstab == nullptr) { + if (gausstab_ == nullptr) { updateSize(); - float rad = max_ff(m_size * m_data.sizey, 0.0f); + float rad = max_ff(size_ * data_.sizey, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); } - if (m_distbuf_inv == nullptr) { + if (distbuf_inv_ == nullptr) { updateSize(); - float rad = max_ff(m_size * m_data.sizey, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + float rad = max_ff(size_ * data_.sizey, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, m_filtersize, m_falloff); + distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad, filtersize_, falloff_); } } @@ -80,7 +80,7 @@ BLI_INLINE float finv_test(const float f, const bool test) void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, void *data) { - const bool do_invert = m_do_subtract; + const bool do_invert = do_subtract_; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; const rcti &input_rect = inputBuffer->get_rect(); float *buffer = inputBuffer->getBuffer(); @@ -89,8 +89,8 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo int bufferstarty = input_rect.ymin; int xmin = max_ii(x, input_rect.xmin); - int ymin = max_ii(y - m_filtersize, input_rect.ymin); - int ymax = min_ii(y + m_filtersize + 1, input_rect.ymax); + int ymin = max_ii(y - filtersize_, input_rect.ymin); + int ymax = min_ii(y + filtersize_ + 1, input_rect.ymax); /* *** this is the main part which is different to 'GaussianYBlurOperation' *** */ int step = getStep(); @@ -108,20 +108,20 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo for (int ny = ymin; ny < ymax; ny += step) { int bufferindex = ((xmin - bufferstartx)) + ((ny - bufferstarty) * bufferwidth); - const int index = (ny - y) + m_filtersize; + const int index = (ny - y) + filtersize_; float value = finv_test(buffer[bufferindex], do_invert); float multiplier; /* gauss */ { - multiplier = m_gausstab[index]; + multiplier = gausstab_[index]; alpha_accum += value * multiplier; multiplier_accum += multiplier; } /* dilate - find most extreme color */ if (value > value_max) { - multiplier = m_distbuf_inv[index]; + multiplier = distbuf_inv_[index]; value *= multiplier; if (value > value_max) { value_max = value; @@ -140,14 +140,14 @@ void GaussianAlphaYBlurOperation::deinitExecution() { GaussianAlphaBlurBaseOperation::deinitExecution(); - if (m_gausstab) { - MEM_freeN(m_gausstab); - m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } - if (m_distbuf_inv) { - MEM_freeN(m_distbuf_inv); - m_distbuf_inv = nullptr; + if (distbuf_inv_) { + MEM_freeN(distbuf_inv_); + distbuf_inv_ = nullptr; } deinitMutex(); @@ -171,11 +171,11 @@ bool GaussianAlphaYBlurOperation::determineDependingAreaOfInterest( else #endif { - if (m_sizeavailable && m_gausstab != nullptr) { + if (sizeavailable_ && gausstab_ != nullptr) { newInput.xmax = input->xmax; newInput.xmin = input->xmin; - newInput.ymax = input->ymax + m_filtersize + 1; - newInput.ymin = input->ymin - m_filtersize - 1; + newInput.ymax = input->ymax + filtersize_ + 1; + newInput.ymin = input->ymin - filtersize_ - 1; } else { newInput.xmax = this->getWidth(); diff --git a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc index 959f599fab4..e6d3b3d4424 100644 --- a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc @@ -23,11 +23,11 @@ namespace blender::compositor { GaussianBlurBaseOperation::GaussianBlurBaseOperation(eDimension dim) : BlurBaseOperation(DataType::Color) { - m_gausstab = nullptr; + gausstab_ = nullptr; #ifdef BLI_HAVE_SSE2 - m_gausstab_sse = nullptr; + gausstab_sse_ = nullptr; #endif - m_filtersize = 0; + filtersize_ = 0; rad_ = 0.0f; dimension_ = dim; } @@ -36,9 +36,9 @@ void GaussianBlurBaseOperation::init_data() { BlurBaseOperation::init_data(); if (execution_model_ == eExecutionModel::FullFrame) { - rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f); + rad_ = max_ff(size_ * this->get_blur_size(dimension_), 0.0f); rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); } } @@ -46,9 +46,9 @@ void GaussianBlurBaseOperation::initExecution() { BlurBaseOperation::initExecution(); if (execution_model_ == eExecutionModel::FullFrame) { - m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad_, filtersize_); #ifdef BLI_HAVE_SSE2 - m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); + gausstab_sse_ = BlurBaseOperation::convert_gausstab_sse(gausstab_, filtersize_); #endif } } @@ -57,14 +57,14 @@ void GaussianBlurBaseOperation::deinitExecution() { BlurBaseOperation::deinitExecution(); - if (m_gausstab) { - MEM_freeN(m_gausstab); - m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } #ifdef BLI_HAVE_SSE2 - if (m_gausstab_sse) { - MEM_freeN(m_gausstab_sse); - m_gausstab_sse = nullptr; + if (gausstab_sse_) { + MEM_freeN(gausstab_sse_); + gausstab_sse_ = nullptr; } #endif } @@ -81,12 +81,12 @@ void GaussianBlurBaseOperation::get_area_of_interest(const int input_idx, r_input_area = output_area; switch (dimension_) { case eDimension::X: - r_input_area.xmin = output_area.xmin - m_filtersize - 1; - r_input_area.xmax = output_area.xmax + m_filtersize + 1; + r_input_area.xmin = output_area.xmin - filtersize_ - 1; + r_input_area.xmax = output_area.xmax + filtersize_ + 1; break; case eDimension::Y: - r_input_area.ymin = output_area.ymin - m_filtersize - 1; - r_input_area.ymax = output_area.ymax + m_filtersize + 1; + r_input_area.ymin = output_area.ymin - filtersize_ - 1; + r_input_area.ymax = output_area.ymax + filtersize_ + 1; break; } } @@ -120,8 +120,8 @@ void GaussianBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *outpu for (; !it.is_end(); ++it) { const int coord = get_current_coord(); - const int coord_min = max_ii(coord - m_filtersize, min_input_coord); - const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord); + const int coord_min = max_ii(coord - filtersize_, min_input_coord); + const int coord_max = min_ii(coord + filtersize_ + 1, max_input_coord); float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float multiplier_accum = 0.0f; @@ -129,20 +129,20 @@ void GaussianBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *outpu const int step = QualityStepHelper::getStep(); const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride; const int in_stride = elem_stride * step; - int gauss_idx = (coord_min - coord) + m_filtersize; + int gauss_idx = (coord_min - coord) + filtersize_; const int gauss_end = gauss_idx + (coord_max - coord_min); #ifdef BLI_HAVE_SSE2 __m128 accum_r = _mm_load_ps(color_accum); for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) { __m128 reg_a = _mm_load_ps(in); - reg_a = _mm_mul_ps(reg_a, m_gausstab_sse[gauss_idx]); + reg_a = _mm_mul_ps(reg_a, gausstab_sse_[gauss_idx]); accum_r = _mm_add_ps(accum_r, reg_a); - multiplier_accum += m_gausstab[gauss_idx]; + multiplier_accum += gausstab_[gauss_idx]; } _mm_store_ps(color_accum, accum_r); #else for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) { - const float multiplier = m_gausstab[gauss_idx]; + const float multiplier = gausstab_[gauss_idx]; madd_v4_v4fl(color_accum, in, multiplier); multiplier_accum += multiplier; } diff --git a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h index c0b27078a24..0e3a86e63ec 100644 --- a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h @@ -24,11 +24,11 @@ namespace blender::compositor { class GaussianBlurBaseOperation : public BlurBaseOperation { protected: - float *m_gausstab; + float *gausstab_; #ifdef BLI_HAVE_SSE2 - __m128 *m_gausstab_sse; + __m128 *gausstab_sse_; #endif - int m_filtersize; + int filtersize_; float rad_; eDimension dimension_; diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc index 670eb3196c6..b5d6aa69423 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc @@ -24,13 +24,13 @@ namespace blender::compositor { GaussianBokehBlurOperation::GaussianBokehBlurOperation() : BlurBaseOperation(DataType::Color) { - m_gausstab = nullptr; + gausstab_ = nullptr; } void *GaussianBokehBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!m_sizeavailable) { + if (!sizeavailable_) { updateGauss(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -44,19 +44,19 @@ void GaussianBokehBlurOperation::init_data() const float width = this->getWidth(); const float height = this->getHeight(); - if (!m_sizeavailable) { + if (!sizeavailable_) { updateSize(); } - radxf_ = m_size * (float)m_data.sizex; + radxf_ = size_ * (float)data_.sizex; CLAMP(radxf_, 0.0f, width / 2.0f); /* Vertical. */ - radyf_ = m_size * (float)m_data.sizey; + radyf_ = size_ * (float)data_.sizey; CLAMP(radyf_, 0.0f, height / 2.0f); - m_radx = ceil(radxf_); - m_rady = ceil(radyf_); + radx_ = ceil(radxf_); + rady_ = ceil(radyf_); } void GaussianBokehBlurOperation::initExecution() @@ -65,16 +65,16 @@ void GaussianBokehBlurOperation::initExecution() initMutex(); - if (m_sizeavailable) { + if (sizeavailable_) { updateGauss(); } } void GaussianBokehBlurOperation::updateGauss() { - if (m_gausstab == nullptr) { - int ddwidth = 2 * m_radx + 1; - int ddheight = 2 * m_rady + 1; + if (gausstab_ == nullptr) { + int ddwidth = 2 * radx_ + 1; + int ddheight = 2 * rady_ + 1; int n = ddwidth * ddheight; /* create a full filter image */ float *ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__); @@ -82,12 +82,12 @@ void GaussianBokehBlurOperation::updateGauss() float sum = 0.0f; float facx = (radxf_ > 0.0f ? 1.0f / radxf_ : 0.0f); float facy = (radyf_ > 0.0f ? 1.0f / radyf_ : 0.0f); - for (int j = -m_rady; j <= m_rady; j++) { - for (int i = -m_radx; i <= m_radx; i++, dgauss++) { + for (int j = -rady_; j <= rady_; j++) { + for (int i = -radx_; i <= radx_; i++, dgauss++) { float fj = (float)j * facy; float fi = (float)i * facx; float dist = sqrt(fj * fj + fi * fi); - *dgauss = RE_filter_value(m_data.filtertype, dist); + *dgauss = RE_filter_value(data_.filtertype, dist); sum += *dgauss; } @@ -101,11 +101,11 @@ void GaussianBokehBlurOperation::updateGauss() } } else { - int center = m_rady * ddwidth + m_radx; + int center = rady_ * ddwidth + radx_; ddgauss[center] = 1.0f; } - m_gausstab = ddgauss; + gausstab_ = ddgauss; } } @@ -124,21 +124,21 @@ void GaussianBokehBlurOperation::executePixel(float output[4], int x, int y, voi int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; - int ymin = max_ii(y - m_rady, input_rect.ymin); - int ymax = min_ii(y + m_rady + 1, input_rect.ymax); - int xmin = max_ii(x - m_radx, input_rect.xmin); - int xmax = min_ii(x + m_radx + 1, input_rect.xmax); + int ymin = max_ii(y - rady_, input_rect.ymin); + int ymax = min_ii(y + rady_ + 1, input_rect.ymax); + int xmin = max_ii(x - radx_, input_rect.xmin); + int xmax = min_ii(x + radx_ + 1, input_rect.xmax); int index; int step = QualityStepHelper::getStep(); int offsetadd = QualityStepHelper::getOffsetAdd(); - const int addConst = (xmin - x + m_radx); - const int mulConst = (m_radx * 2 + 1); + const int addConst = (xmin - x + radx_); + const int mulConst = (radx_ * 2 + 1); for (int ny = ymin; ny < ymax; ny += step) { - index = ((ny - y) + m_rady) * mulConst + addConst; + index = ((ny - y) + rady_) * mulConst + addConst; int bufferindex = ((xmin - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth); for (int nx = xmin; nx < xmax; nx += step) { - const float multiplier = m_gausstab[index]; + const float multiplier = gausstab_[index]; madd_v4_v4fl(tempColor, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; index += step; @@ -153,9 +153,9 @@ void GaussianBokehBlurOperation::deinitExecution() { BlurBaseOperation::deinitExecution(); - if (m_gausstab) { - MEM_freeN(m_gausstab); - m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } deinitMutex(); @@ -176,15 +176,15 @@ bool GaussianBokehBlurOperation::determineDependingAreaOfInterest( return true; } - if (m_sizeavailable && m_gausstab != nullptr) { + if (sizeavailable_ && gausstab_ != nullptr) { newInput.xmin = 0; newInput.ymin = 0; newInput.xmax = this->getWidth(); newInput.ymax = this->getHeight(); } else { - int addx = m_radx; - int addy = m_rady; + int addx = radx_; + int addy = rady_; newInput.xmax = input->xmax + addx; newInput.xmin = input->xmin - addx; newInput.ymax = input->ymax + addy; @@ -202,10 +202,10 @@ void GaussianBokehBlurOperation::get_area_of_interest(const int input_idx, return; } - r_input_area.xmax = output_area.xmax + m_radx; - r_input_area.xmin = output_area.xmin - m_radx; - r_input_area.ymax = output_area.ymax + m_rady; - r_input_area.ymin = output_area.ymin - m_rady; + r_input_area.xmax = output_area.xmax + radx_; + r_input_area.xmin = output_area.xmin - radx_; + r_input_area.ymax = output_area.ymax + rady_; + r_input_area.ymin = output_area.ymin - rady_; } void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -219,23 +219,23 @@ void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *outp const int x = it.x; const int y = it.y; - const int ymin = max_ii(y - m_rady, input_rect.ymin); - const int ymax = min_ii(y + m_rady + 1, input_rect.ymax); - const int xmin = max_ii(x - m_radx, input_rect.xmin); - const int xmax = min_ii(x + m_radx + 1, input_rect.xmax); + const int ymin = max_ii(y - rady_, input_rect.ymin); + const int ymax = min_ii(y + rady_ + 1, input_rect.ymax); + const int xmin = max_ii(x - radx_, input_rect.xmin); + const int xmax = min_ii(x + radx_ + 1, input_rect.xmax); float tempColor[4] = {0}; float multiplier_accum = 0; const int step = QualityStepHelper::getStep(); const int elem_step = step * input->elem_stride; - const int add_const = (xmin - x + m_radx); - const int mul_const = (m_radx * 2 + 1); + const int add_const = (xmin - x + radx_); + const int mul_const = (radx_ * 2 + 1); for (int ny = ymin; ny < ymax; ny += step) { const float *color = input->get_elem(xmin, ny); - int gauss_index = ((ny - y) + m_rady) * mul_const + add_const; + int gauss_index = ((ny - y) + rady_) * mul_const + add_const; const int gauss_end = gauss_index + (xmax - xmin); for (; gauss_index < gauss_end; gauss_index += step, color += elem_step) { - const float multiplier = m_gausstab[gauss_index]; + const float multiplier = gausstab_[gauss_index]; madd_v4_v4fl(tempColor, color, multiplier); multiplier_accum += multiplier; } @@ -249,53 +249,53 @@ void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *outp GaussianBlurReferenceOperation::GaussianBlurReferenceOperation() : BlurBaseOperation(DataType::Color) { - m_maintabs = nullptr; + maintabs_ = nullptr; use_variable_size_ = true; } void GaussianBlurReferenceOperation::init_data() { /* Setup variables for gausstab and area of interest. */ - m_data.image_in_width = this->getWidth(); - m_data.image_in_height = this->getHeight(); - if (m_data.relative) { - switch (m_data.aspect) { + data_.image_in_width = this->getWidth(); + data_.image_in_height = this->getHeight(); + if (data_.relative) { + switch (data_.aspect) { case CMP_NODE_BLUR_ASPECT_NONE: - m_data.sizex = (int)(m_data.percentx * 0.01f * m_data.image_in_width); - m_data.sizey = (int)(m_data.percenty * 0.01f * m_data.image_in_height); + data_.sizex = (int)(data_.percentx * 0.01f * data_.image_in_width); + data_.sizey = (int)(data_.percenty * 0.01f * data_.image_in_height); break; case CMP_NODE_BLUR_ASPECT_Y: - m_data.sizex = (int)(m_data.percentx * 0.01f * m_data.image_in_width); - m_data.sizey = (int)(m_data.percenty * 0.01f * m_data.image_in_width); + data_.sizex = (int)(data_.percentx * 0.01f * data_.image_in_width); + data_.sizey = (int)(data_.percenty * 0.01f * data_.image_in_width); break; case CMP_NODE_BLUR_ASPECT_X: - m_data.sizex = (int)(m_data.percentx * 0.01f * m_data.image_in_height); - m_data.sizey = (int)(m_data.percenty * 0.01f * m_data.image_in_height); + data_.sizex = (int)(data_.percentx * 0.01f * data_.image_in_height); + data_.sizey = (int)(data_.percenty * 0.01f * data_.image_in_height); break; } } /* Horizontal. */ - m_filtersizex = (float)m_data.sizex; + filtersizex_ = (float)data_.sizex; int imgx = getWidth() / 2; - if (m_filtersizex > imgx) { - m_filtersizex = imgx; + if (filtersizex_ > imgx) { + filtersizex_ = imgx; } - else if (m_filtersizex < 1) { - m_filtersizex = 1; + else if (filtersizex_ < 1) { + filtersizex_ = 1; } - m_radx = (float)m_filtersizex; + radx_ = (float)filtersizex_; /* Vertical. */ - m_filtersizey = (float)m_data.sizey; + filtersizey_ = (float)data_.sizey; int imgy = getHeight() / 2; - if (m_filtersizey > imgy) { - m_filtersizey = imgy; + if (filtersizey_ > imgy) { + filtersizey_ = imgy; } - else if (m_filtersizey < 1) { - m_filtersizey = 1; + else if (filtersizey_ < 1) { + filtersizey_ = 1; } - m_rady = (float)m_filtersizey; + rady_ = (float)filtersizey_; } void *GaussianBlurReferenceOperation::initializeTileData(rcti * /*rect*/) @@ -314,10 +314,10 @@ void GaussianBlurReferenceOperation::initExecution() void GaussianBlurReferenceOperation::updateGauss() { int i; - int x = MAX2(m_filtersizex, m_filtersizey); - m_maintabs = (float **)MEM_mallocN(x * sizeof(float *), "gauss array"); + int x = MAX2(filtersizex_, filtersizey_); + maintabs_ = (float **)MEM_mallocN(x * sizeof(float *), "gauss array"); for (i = 0; i < x; i++) { - m_maintabs[i] = make_gausstab(i + 1, i + 1); + maintabs_[i] = make_gausstab(i + 1, i + 1); } } @@ -334,18 +334,18 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, int imgx = getWidth(); int imgy = getHeight(); float tempSize[4]; - m_inputSize->read(tempSize, x, y, data); + inputSize_->read(tempSize, x, y, data); float refSize = tempSize[0]; - int refradx = (int)(refSize * m_radx); - int refrady = (int)(refSize * m_rady); - if (refradx > m_filtersizex) { - refradx = m_filtersizex; + int refradx = (int)(refSize * radx_); + int refrady = (int)(refSize * rady_); + if (refradx > filtersizex_) { + refradx = filtersizex_; } else if (refradx < 1) { refradx = 1; } - if (refrady > m_filtersizey) { - refrady = m_filtersizey; + if (refrady > filtersizey_) { + refrady = filtersizey_; } else if (refrady < 1) { refrady = 1; @@ -362,9 +362,9 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, float *srcd = buffer + COM_DATA_TYPE_COLOR_CHANNELS * ((y + minyr) * imgx + x + minxr); - gausstabx = m_maintabs[refradx - 1]; + gausstabx = maintabs_[refradx - 1]; gausstabcentx = gausstabx + refradx; - gausstaby = m_maintabs[refrady - 1]; + gausstaby = maintabs_[refrady - 1]; gausstabcenty = gausstaby + refrady; sum = gval = rval = bval = aval = 0.0f; @@ -391,11 +391,11 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, void GaussianBlurReferenceOperation::deinitExecution() { int x, i; - x = MAX2(m_filtersizex, m_filtersizey); + x = MAX2(filtersizex_, filtersizey_); for (i = 0; i < x; i++) { - MEM_freeN(m_maintabs[i]); + MEM_freeN(maintabs_[i]); } - MEM_freeN(m_maintabs); + MEM_freeN(maintabs_); BlurBaseOperation::deinitExecution(); } @@ -409,8 +409,8 @@ bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest( return true; } - int addx = m_data.sizex + 2; - int addy = m_data.sizey + 2; + int addx = data_.sizex + 2; + int addy = data_.sizey + 2; newInput.xmax = input->xmax + addx; newInput.xmin = input->xmin - addx; newInput.ymax = input->ymax + addy; @@ -427,8 +427,8 @@ void GaussianBlurReferenceOperation::get_area_of_interest(const int input_idx, return; } - const int add_x = m_data.sizex + 2; - const int add_y = m_data.sizey + 2; + const int add_x = data_.sizex + 2; + const int add_y = data_.sizey + 2; r_input_area.xmax = output_area.xmax + add_x; r_input_area.xmin = output_area.xmin - add_x; r_input_area.ymax = output_area.ymax + add_y; @@ -443,16 +443,16 @@ void GaussianBlurReferenceOperation::update_memory_buffer_partial(MemoryBuffer * MemoryBuffer *size_input = inputs[SIZE_INPUT_INDEX]; for (BuffersIterator it = output->iterate_with({size_input}, area); !it.is_end(); ++it) { const float ref_size = *it.in(0); - int ref_radx = (int)(ref_size * m_radx); - int ref_rady = (int)(ref_size * m_rady); - if (ref_radx > m_filtersizex) { - ref_radx = m_filtersizex; + 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 > m_filtersizey) { - ref_rady = m_filtersizey; + if (ref_rady > filtersizey_) { + ref_rady = filtersizey_; } else if (ref_rady < 1) { ref_rady = 1; @@ -472,9 +472,9 @@ void GaussianBlurReferenceOperation::update_memory_buffer_partial(MemoryBuffer * const int minyr = y - ref_rady < 0 ? -y : -ref_rady; const int maxyr = y + ref_rady > height ? height - y : ref_rady; - const float *gausstabx = m_maintabs[ref_radx - 1]; + const float *gausstabx = maintabs_[ref_radx - 1]; const float *gausstabcentx = gausstabx + ref_radx; - const float *gausstaby = m_maintabs[ref_rady - 1]; + const float *gausstaby = maintabs_[ref_rady - 1]; const float *gausstabcenty = gausstaby + ref_rady; float gauss_sum = 0.0f; diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h index a64b5b327b0..5e6eeefa512 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h @@ -26,8 +26,8 @@ namespace blender::compositor { class GaussianBokehBlurOperation : public BlurBaseOperation { private: - float *m_gausstab; - int m_radx, m_rady; + float *gausstab_; + int radx_, rady_; float radxf_; float radyf_; void updateGauss(); @@ -61,13 +61,13 @@ class GaussianBokehBlurOperation : public BlurBaseOperation { class GaussianBlurReferenceOperation : public BlurBaseOperation { private: - float **m_maintabs; + float **maintabs_; void updateGauss(); - int m_filtersizex; - int m_filtersizey; - float m_radx; - float m_rady; + int filtersizex_; + int filtersizey_; + float radx_; + float rady_; public: GaussianBlurReferenceOperation(); diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc index 6208820e280..4720b3f69d0 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc @@ -28,7 +28,7 @@ GaussianXBlurOperation::GaussianXBlurOperation() : GaussianBlurBaseOperation(eDi void *GaussianXBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!m_sizeavailable) { + if (!sizeavailable_) { updateGauss(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -43,14 +43,14 @@ void GaussianXBlurOperation::initExecution() initMutex(); - if (m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { - float rad = max_ff(m_size * m_data.sizex, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { + float rad = max_ff(size_ * data_.sizex, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); /* TODO(sergey): De-duplicate with the case below and Y blur. */ - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); #ifdef BLI_HAVE_SSE2 - m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); + gausstab_sse_ = BlurBaseOperation::convert_gausstab_sse(gausstab_, filtersize_); #endif } } @@ -58,15 +58,15 @@ void GaussianXBlurOperation::initExecution() /* TODO(manzanilla): to be removed with tiled implementation. */ void GaussianXBlurOperation::updateGauss() { - if (m_gausstab == nullptr) { + if (gausstab_ == nullptr) { updateSize(); - float rad = max_ff(m_size * m_data.sizex, 0.0f); + float rad = max_ff(size_ * data_.sizex, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); #ifdef BLI_HAVE_SSE2 - m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); + gausstab_sse_ = BlurBaseOperation::convert_gausstab_sse(gausstab_, filtersize_); #endif } } @@ -82,8 +82,8 @@ void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *d int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; - int xmin = max_ii(x - m_filtersize, input_rect.xmin); - int xmax = min_ii(x + m_filtersize + 1, input_rect.xmax); + int xmin = max_ii(x - filtersize_, input_rect.xmin); + int xmax = min_ii(x + filtersize_ + 1, input_rect.xmax); int ymin = max_ii(y, input_rect.ymin); int step = getStep(); @@ -92,17 +92,17 @@ void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *d #ifdef BLI_HAVE_SSE2 __m128 accum_r = _mm_load_ps(color_accum); - for (int nx = xmin, index = (xmin - x) + m_filtersize; nx < xmax; nx += step, index += step) { + for (int nx = xmin, index = (xmin - x) + filtersize_; nx < xmax; nx += step, index += step) { __m128 reg_a = _mm_load_ps(&buffer[bufferindex]); - reg_a = _mm_mul_ps(reg_a, m_gausstab_sse[index]); + reg_a = _mm_mul_ps(reg_a, gausstab_sse_[index]); accum_r = _mm_add_ps(accum_r, reg_a); - multiplier_accum += m_gausstab[index]; + multiplier_accum += gausstab_[index]; bufferindex += offsetadd; } _mm_store_ps(color_accum, accum_r); #else - for (int nx = xmin, index = (xmin - x) + m_filtersize; nx < xmax; nx += step, index += step) { - const float multiplier = m_gausstab[index]; + for (int nx = xmin, index = (xmin - x) + filtersize_; nx < xmax; nx += step, index += step) { + const float multiplier = gausstab_[index]; madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; bufferindex += offsetadd; @@ -120,16 +120,16 @@ void GaussianXBlurOperation::executeOpenCL(OpenCLDevice *device, { cl_kernel gaussianXBlurOperationKernel = device->COM_clCreateKernel( "gaussianXBlurOperationKernel", nullptr); - cl_int filter_size = m_filtersize; + cl_int filter_size = filtersize_; cl_mem gausstab = clCreateBuffer(device->getContext(), CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, - sizeof(float) * (m_filtersize * 2 + 1), - m_gausstab, + sizeof(float) * (filtersize_ * 2 + 1), + gausstab_, nullptr); device->COM_clAttachMemoryBufferToKernelParameter( - gaussianXBlurOperationKernel, 0, 1, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); + gaussianXBlurOperationKernel, 0, 1, clMemToCleanUp, inputMemoryBuffers, inputProgram_); device->COM_clAttachOutputMemoryBufferToKernelParameter( gaussianXBlurOperationKernel, 2, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter( @@ -147,14 +147,14 @@ void GaussianXBlurOperation::deinitExecution() { GaussianBlurBaseOperation::deinitExecution(); - if (m_gausstab) { - MEM_freeN(m_gausstab); - m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } #ifdef BLI_HAVE_SSE2 - if (m_gausstab_sse) { - MEM_freeN(m_gausstab_sse); - m_gausstab_sse = nullptr; + if (gausstab_sse_) { + MEM_freeN(gausstab_sse_); + gausstab_sse_ = nullptr; } #endif @@ -167,7 +167,7 @@ bool GaussianXBlurOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - if (!m_sizeavailable) { + if (!sizeavailable_) { rcti sizeInput; sizeInput.xmin = 0; sizeInput.ymin = 0; @@ -179,9 +179,9 @@ bool GaussianXBlurOperation::determineDependingAreaOfInterest(rcti *input, } } { - if (m_sizeavailable && m_gausstab != nullptr) { - newInput.xmax = input->xmax + m_filtersize + 1; - newInput.xmin = input->xmin - m_filtersize - 1; + if (sizeavailable_ && gausstab_ != nullptr) { + newInput.xmax = input->xmax + filtersize_ + 1; + newInput.xmin = input->xmin - filtersize_ - 1; newInput.ymax = input->ymax; newInput.ymin = input->ymin; } diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h index e09e57bad67..1bb77031056 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h @@ -59,7 +59,7 @@ class GaussianXBlurOperation : public GaussianBlurBaseOperation { void checkOpenCL() { - flags.open_cl = (m_data.sizex >= 128); + flags.open_cl = (data_.sizex >= 128); } }; diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc index 8a95721992c..50edc0bea29 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc @@ -28,7 +28,7 @@ GaussianYBlurOperation::GaussianYBlurOperation() : GaussianBlurBaseOperation(eDi void *GaussianYBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); - if (!m_sizeavailable) { + if (!sizeavailable_) { updateGauss(); } void *buffer = getInputOperation(0)->initializeTileData(nullptr); @@ -42,28 +42,28 @@ void GaussianYBlurOperation::initExecution() initMutex(); - if (m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { - float rad = max_ff(m_size * m_data.sizey, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { + float rad = max_ff(size_ * data_.sizey, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); #ifdef BLI_HAVE_SSE2 - m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); + gausstab_sse_ = BlurBaseOperation::convert_gausstab_sse(gausstab_, filtersize_); #endif } } void GaussianYBlurOperation::updateGauss() { - if (m_gausstab == nullptr) { + if (gausstab_ == nullptr) { updateSize(); - float rad = max_ff(m_size * m_data.sizey, 0.0f); + float rad = max_ff(size_ * data_.sizey, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); #ifdef BLI_HAVE_SSE2 - m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); + gausstab_sse_ = BlurBaseOperation::convert_gausstab_sse(gausstab_, filtersize_); #endif } } @@ -80,8 +80,8 @@ void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *d int bufferstarty = input_rect.ymin; int xmin = max_ii(x, input_rect.xmin); - int ymin = max_ii(y - m_filtersize, input_rect.ymin); - int ymax = min_ii(y + m_filtersize + 1, input_rect.ymax); + int ymin = max_ii(y - filtersize_, input_rect.ymin); + int ymax = min_ii(y + filtersize_ + 1, input_rect.ymax); int index; int step = getStep(); @@ -90,20 +90,20 @@ void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *d #ifdef BLI_HAVE_SSE2 __m128 accum_r = _mm_load_ps(color_accum); for (int ny = ymin; ny < ymax; ny += step) { - index = (ny - y) + m_filtersize; + index = (ny - y) + filtersize_; int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth); - const float multiplier = m_gausstab[index]; + const float multiplier = gausstab_[index]; __m128 reg_a = _mm_load_ps(&buffer[bufferindex]); - reg_a = _mm_mul_ps(reg_a, m_gausstab_sse[index]); + reg_a = _mm_mul_ps(reg_a, gausstab_sse_[index]); accum_r = _mm_add_ps(accum_r, reg_a); multiplier_accum += multiplier; } _mm_store_ps(color_accum, accum_r); #else for (int ny = ymin; ny < ymax; ny += step) { - index = (ny - y) + m_filtersize; + index = (ny - y) + filtersize_; int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth); - const float multiplier = m_gausstab[index]; + const float multiplier = gausstab_[index]; madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; } @@ -120,16 +120,16 @@ void GaussianYBlurOperation::executeOpenCL(OpenCLDevice *device, { cl_kernel gaussianYBlurOperationKernel = device->COM_clCreateKernel( "gaussianYBlurOperationKernel", nullptr); - cl_int filter_size = m_filtersize; + cl_int filter_size = filtersize_; cl_mem gausstab = clCreateBuffer(device->getContext(), CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, - sizeof(float) * (m_filtersize * 2 + 1), - m_gausstab, + sizeof(float) * (filtersize_ * 2 + 1), + gausstab_, nullptr); device->COM_clAttachMemoryBufferToKernelParameter( - gaussianYBlurOperationKernel, 0, 1, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); + gaussianYBlurOperationKernel, 0, 1, clMemToCleanUp, inputMemoryBuffers, inputProgram_); device->COM_clAttachOutputMemoryBufferToKernelParameter( gaussianYBlurOperationKernel, 2, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter( @@ -147,14 +147,14 @@ void GaussianYBlurOperation::deinitExecution() { GaussianBlurBaseOperation::deinitExecution(); - if (m_gausstab) { - MEM_freeN(m_gausstab); - m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } #ifdef BLI_HAVE_SSE2 - if (m_gausstab_sse) { - MEM_freeN(m_gausstab_sse); - m_gausstab_sse = nullptr; + if (gausstab_sse_) { + MEM_freeN(gausstab_sse_); + gausstab_sse_ = nullptr; } #endif @@ -167,7 +167,7 @@ bool GaussianYBlurOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - if (!m_sizeavailable) { + if (!sizeavailable_) { rcti sizeInput; sizeInput.xmin = 0; sizeInput.ymin = 0; @@ -179,11 +179,11 @@ bool GaussianYBlurOperation::determineDependingAreaOfInterest(rcti *input, } } { - if (m_sizeavailable && m_gausstab != nullptr) { + if (sizeavailable_ && gausstab_ != nullptr) { newInput.xmax = input->xmax; newInput.xmin = input->xmin; - newInput.ymax = input->ymax + m_filtersize + 1; - newInput.ymin = input->ymin - m_filtersize - 1; + newInput.ymax = input->ymax + filtersize_ + 1; + newInput.ymin = input->ymin - filtersize_ - 1; } else { newInput.xmax = this->getWidth(); diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h index bb33f8b74cb..92c2d71f487 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h @@ -59,7 +59,7 @@ class GaussianYBlurOperation : public GaussianBlurBaseOperation { void checkOpenCL() { - flags.open_cl = (m_data.sizex >= 128); + flags.open_cl = (data_.sizex >= 128); } }; diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cc b/source/blender/compositor/operations/COM_GlareBaseOperation.cc index 6e05705163f..0987f0efb31 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cc @@ -24,25 +24,25 @@ GlareBaseOperation::GlareBaseOperation() { this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_settings = nullptr; + settings_ = nullptr; flags.is_fullframe_operation = true; is_output_rendered_ = false; } void GlareBaseOperation::initExecution() { SingleThreadedOperation::initExecution(); - m_inputProgram = getInputSocketReader(0); + inputProgram_ = getInputSocketReader(0); } void GlareBaseOperation::deinitExecution() { - m_inputProgram = nullptr; + inputProgram_ = nullptr; SingleThreadedOperation::deinitExecution(); } MemoryBuffer *GlareBaseOperation::createMemoryBuffer(rcti *rect2) { - MemoryBuffer *tile = (MemoryBuffer *)m_inputProgram->initializeTileData(rect2); + MemoryBuffer *tile = (MemoryBuffer *)inputProgram_->initializeTileData(rect2); rcti rect; rect.xmin = 0; rect.ymin = 0; @@ -50,7 +50,7 @@ MemoryBuffer *GlareBaseOperation::createMemoryBuffer(rcti *rect2) rect.ymax = getHeight(); MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); float *data = result->getBuffer(); - this->generateGlare(data, tile, m_settings); + this->generateGlare(data, tile, settings_); return result; } @@ -93,7 +93,7 @@ void GlareBaseOperation::update_memory_buffer(MemoryBuffer *output, input = input->inflate(); } - this->generateGlare(output->getBuffer(), input, m_settings); + this->generateGlare(output->getBuffer(), input, settings_); is_output_rendered_ = true; if (is_input_inflated) { diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h index 1d0c080ab8d..a50042f1ff4 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.h +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h @@ -42,12 +42,12 @@ class GlareBaseOperation : public SingleThreadedOperation { /** * \brief Cached reference to the inputProgram */ - SocketReader *m_inputProgram; + SocketReader *inputProgram_; /** * \brief settings of the glare node. */ - NodeGlare *m_settings; + NodeGlare *settings_; bool is_output_rendered_; @@ -64,7 +64,7 @@ class GlareBaseOperation : public SingleThreadedOperation { void setGlareSettings(NodeGlare *settings) { - m_settings = settings; + settings_ = settings; } bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc index 22e5e987aad..7135155cb68 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc @@ -26,21 +26,21 @@ GlareThresholdOperation::GlareThresholdOperation() { this->addInputSocket(DataType::Color, ResizeMode::FitAny); this->addOutputSocket(DataType::Color); - m_inputProgram = nullptr; + inputProgram_ = nullptr; } void GlareThresholdOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { NodeOperation::determine_canvas(preferred_area, r_area); - const int width = BLI_rcti_size_x(&r_area) / (1 << m_settings->quality); - const int height = BLI_rcti_size_y(&r_area) / (1 << m_settings->quality); + const int width = BLI_rcti_size_x(&r_area) / (1 << settings_->quality); + const int height = BLI_rcti_size_y(&r_area) / (1 << settings_->quality); r_area.xmax = r_area.xmin + width; r_area.ymax = r_area.ymin + height; } void GlareThresholdOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); } void GlareThresholdOperation::executePixelSampled(float output[4], @@ -48,9 +48,9 @@ void GlareThresholdOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - const float threshold = m_settings->threshold; + const float threshold = settings_->threshold; - m_inputProgram->readSampled(output, x, y, sampler); + inputProgram_->readSampled(output, x, y, sampler); if (IMB_colormanagement_get_luminance(output) >= threshold) { output[0] -= threshold; output[1] -= threshold; @@ -67,14 +67,14 @@ void GlareThresholdOperation::executePixelSampled(float output[4], void GlareThresholdOperation::deinitExecution() { - m_inputProgram = nullptr; + inputProgram_ = nullptr; } void GlareThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float threshold = m_settings->threshold; + const float threshold = settings_->threshold; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float *color = it.in(0); if (IMB_colormanagement_get_luminance(color) >= threshold) { diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h index 1917f016f72..f37020b61a8 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h @@ -28,12 +28,12 @@ class GlareThresholdOperation : public MultiThreadedOperation { /** * \brief Cached reference to the inputProgram */ - SocketReader *m_inputProgram; + SocketReader *inputProgram_; /** * \brief settings of the glare node. */ - NodeGlare *m_settings; + NodeGlare *settings_; public: GlareThresholdOperation(); @@ -55,7 +55,7 @@ class GlareThresholdOperation : public MultiThreadedOperation { void setGlareSettings(NodeGlare *settings) { - m_settings = settings; + settings_ = settings; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc index 54f07f6f4a6..e2f4552c69c 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc @@ -29,12 +29,12 @@ HueSaturationValueCorrectOperation::HueSaturationValueCorrectOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_inputProgram = nullptr; + inputProgram_ = nullptr; } void HueSaturationValueCorrectOperation::initExecution() { CurveBaseOperation::initExecution(); - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); } void HueSaturationValueCorrectOperation::executePixelSampled(float output[4], @@ -44,18 +44,18 @@ void HueSaturationValueCorrectOperation::executePixelSampled(float output[4], { float hsv[4], f; - m_inputProgram->readSampled(hsv, x, y, sampler); + inputProgram_->readSampled(hsv, x, y, sampler); /* adjust hue, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(m_curveMapping, 0, hsv[0]); + f = BKE_curvemapping_evaluateF(curveMapping_, 0, hsv[0]); hsv[0] += f - 0.5f; /* adjust saturation, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(m_curveMapping, 1, hsv[0]); + f = BKE_curvemapping_evaluateF(curveMapping_, 1, hsv[0]); hsv[1] *= (f * 2.0f); /* adjust value, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(m_curveMapping, 2, hsv[0]); + f = BKE_curvemapping_evaluateF(curveMapping_, 2, hsv[0]); hsv[2] *= (f * 2.0f); hsv[0] = hsv[0] - floorf(hsv[0]); /* mod 1.0 */ @@ -70,7 +70,7 @@ void HueSaturationValueCorrectOperation::executePixelSampled(float output[4], void HueSaturationValueCorrectOperation::deinitExecution() { CurveBaseOperation::deinitExecution(); - m_inputProgram = nullptr; + inputProgram_ = nullptr; } void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -82,15 +82,15 @@ void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuff copy_v4_v4(hsv, it.in(0)); /* Adjust hue, scaling returned default 0.5 up to 1. */ - float f = BKE_curvemapping_evaluateF(m_curveMapping, 0, hsv[0]); + float f = BKE_curvemapping_evaluateF(curveMapping_, 0, hsv[0]); hsv[0] += f - 0.5f; /* Adjust saturation, scaling returned default 0.5 up to 1. */ - f = BKE_curvemapping_evaluateF(m_curveMapping, 1, hsv[0]); + f = BKE_curvemapping_evaluateF(curveMapping_, 1, hsv[0]); hsv[1] *= (f * 2.0f); /* Adjust value, scaling returned default 0.5 up to 1. */ - f = BKE_curvemapping_evaluateF(m_curveMapping, 2, hsv[0]); + f = BKE_curvemapping_evaluateF(curveMapping_, 2, hsv[0]); hsv[2] *= (f * 2.0f); hsv[0] = hsv[0] - floorf(hsv[0]); /* Mod 1.0. */ diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h index 6c1b66aba1f..26792a54d26 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h @@ -28,7 +28,7 @@ class HueSaturationValueCorrectOperation : public CurveBaseOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; + SocketReader *inputProgram_; public: HueSaturationValueCorrectOperation(); diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.cc b/source/blender/compositor/operations/COM_IDMaskOperation.cc index 56bd88b0ddd..677d0a8ccff 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.cc +++ b/source/blender/compositor/operations/COM_IDMaskOperation.cc @@ -40,7 +40,7 @@ void IDMaskOperation::executePixel(float output[4], int x, int y, void *data) const int buffer_width = input_buffer->getWidth(); float *buffer = input_buffer->getBuffer(); int buffer_index = (y * buffer_width + x); - output[0] = (roundf(buffer[buffer_index]) == m_objectIndex) ? 1.0f : 0.0f; + output[0] = (roundf(buffer[buffer_index]) == objectIndex_) ? 1.0f : 0.0f; } void IDMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -54,7 +54,7 @@ void IDMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, const float *in = input->get_elem(area.xmin, y); const float *row_end = out + width * output->elem_stride; while (out < row_end) { - out[0] = (roundf(in[0]) == m_objectIndex) ? 1.0f : 0.0f; + out[0] = (roundf(in[0]) == objectIndex_) ? 1.0f : 0.0f; in += input->elem_stride; out += output->elem_stride; } diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.h b/source/blender/compositor/operations/COM_IDMaskOperation.h index ed6d065216f..333f9eeee98 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.h +++ b/source/blender/compositor/operations/COM_IDMaskOperation.h @@ -24,7 +24,7 @@ namespace blender::compositor { class IDMaskOperation : public MultiThreadedOperation { private: - float m_objectIndex; + float objectIndex_; public: IDMaskOperation(); @@ -34,7 +34,7 @@ class IDMaskOperation : public MultiThreadedOperation { void setObjectIndex(float objectIndex) { - m_objectIndex = objectIndex; + objectIndex_ = objectIndex; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ImageOperation.cc b/source/blender/compositor/operations/COM_ImageOperation.cc index 6c4f860df1a..84c5b9654ce 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cc +++ b/source/blender/compositor/operations/COM_ImageOperation.cc @@ -28,19 +28,19 @@ namespace blender::compositor { BaseImageOperation::BaseImageOperation() { - m_image = nullptr; - m_buffer = nullptr; - m_imageFloatBuffer = nullptr; - m_imageByteBuffer = nullptr; - m_imageUser = nullptr; - m_imagewidth = 0; - m_imageheight = 0; - m_framenumber = 0; - m_depthBuffer = nullptr; + image_ = nullptr; + buffer_ = nullptr; + imageFloatBuffer_ = nullptr; + imageByteBuffer_ = nullptr; + imageUser_ = nullptr; + imagewidth_ = 0; + imageheight_ = 0; + framenumber_ = 0; + depthBuffer_ = nullptr; depth_buffer_ = nullptr; - m_numberOfChannels = 0; - m_rd = nullptr; - m_viewName = nullptr; + numberOfChannels_ = 0; + rd_ = nullptr; + viewName_ = nullptr; } ImageOperation::ImageOperation() : BaseImageOperation() { @@ -58,20 +58,20 @@ ImageDepthOperation::ImageDepthOperation() : BaseImageOperation() ImBuf *BaseImageOperation::getImBuf() { ImBuf *ibuf; - ImageUser iuser = *m_imageUser; + ImageUser iuser = *imageUser_; - if (m_image == nullptr) { + if (image_ == nullptr) { return nullptr; } /* local changes to the original ImageUser */ - if (BKE_image_is_multilayer(m_image) == false) { - iuser.multi_index = BKE_scene_multiview_view_id_get(m_rd, m_viewName); + if (BKE_image_is_multilayer(image_) == false) { + iuser.multi_index = BKE_scene_multiview_view_id_get(rd_, viewName_); } - ibuf = BKE_image_acquire_ibuf(m_image, &iuser, nullptr); + ibuf = BKE_image_acquire_ibuf(image_, &iuser, nullptr); if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) { - BKE_image_release_ibuf(m_image, ibuf, nullptr); + BKE_image_release_ibuf(image_, ibuf, nullptr); return nullptr; } return ibuf; @@ -80,25 +80,25 @@ ImBuf *BaseImageOperation::getImBuf() void BaseImageOperation::initExecution() { ImBuf *stackbuf = getImBuf(); - m_buffer = stackbuf; + buffer_ = stackbuf; if (stackbuf) { - m_imageFloatBuffer = stackbuf->rect_float; - m_imageByteBuffer = stackbuf->rect; - m_depthBuffer = stackbuf->zbuf_float; + imageFloatBuffer_ = stackbuf->rect_float; + imageByteBuffer_ = stackbuf->rect; + depthBuffer_ = stackbuf->zbuf_float; if (stackbuf->zbuf_float) { depth_buffer_ = new MemoryBuffer(stackbuf->zbuf_float, 1, stackbuf->x, stackbuf->y); } - m_imagewidth = stackbuf->x; - m_imageheight = stackbuf->y; - m_numberOfChannels = stackbuf->channels; + imagewidth_ = stackbuf->x; + imageheight_ = stackbuf->y; + numberOfChannels_ = stackbuf->channels; } } void BaseImageOperation::deinitExecution() { - m_imageFloatBuffer = nullptr; - m_imageByteBuffer = nullptr; - BKE_image_release_ibuf(m_image, m_buffer, nullptr); + imageFloatBuffer_ = nullptr; + imageByteBuffer_ = nullptr; + BKE_image_release_ibuf(image_, buffer_, nullptr); if (depth_buffer_) { delete depth_buffer_; depth_buffer_ = nullptr; @@ -115,7 +115,7 @@ void BaseImageOperation::determine_canvas(const rcti &UNUSED(preferred_area), rc BLI_rcti_init(&r_area, 0, stackbuf->x, 0, stackbuf->y); } - BKE_image_release_ibuf(m_image, stackbuf, nullptr); + BKE_image_release_ibuf(image_, stackbuf, nullptr); } static void sampleImageAtLocation( @@ -157,14 +157,14 @@ static void sampleImageAtLocation( void ImageOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { int ix = x, iy = y; - if (m_imageFloatBuffer == nullptr && m_imageByteBuffer == nullptr) { + if (imageFloatBuffer_ == nullptr && imageByteBuffer_ == nullptr) { zero_v4(output); } - else if (ix < 0 || iy < 0 || ix >= m_buffer->x || iy >= m_buffer->y) { + else if (ix < 0 || iy < 0 || ix >= buffer_->x || iy >= buffer_->y) { zero_v4(output); } else { - sampleImageAtLocation(m_buffer, x, y, sampler, true, output); + sampleImageAtLocation(buffer_, x, y, sampler, true, output); } } @@ -172,7 +172,7 @@ void ImageOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->copy_from(m_buffer, area, true); + output->copy_from(buffer_, area, true); } void ImageAlphaOperation::executePixelSampled(float output[4], @@ -182,12 +182,12 @@ void ImageAlphaOperation::executePixelSampled(float output[4], { float tempcolor[4]; - if (m_imageFloatBuffer == nullptr && m_imageByteBuffer == nullptr) { + if (imageFloatBuffer_ == nullptr && imageByteBuffer_ == nullptr) { output[0] = 0.0f; } else { tempcolor[3] = 1.0f; - sampleImageAtLocation(m_buffer, x, y, sampler, false, tempcolor); + sampleImageAtLocation(buffer_, x, y, sampler, false, tempcolor); output[0] = tempcolor[3]; } } @@ -196,7 +196,7 @@ void ImageAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->copy_from(m_buffer, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); + output->copy_from(buffer_, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); } void ImageDepthOperation::executePixelSampled(float output[4], @@ -204,7 +204,7 @@ void ImageDepthOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - if (m_depthBuffer == nullptr) { + if (depthBuffer_ == nullptr) { output[0] = 0.0f; } else { @@ -213,7 +213,7 @@ void ImageDepthOperation::executePixelSampled(float output[4], } else { int offset = y * getWidth() + x; - output[0] = m_depthBuffer[offset]; + output[0] = depthBuffer_[offset]; } } } diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h index b82b89290ad..40ef9f38f79 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.h +++ b/source/blender/compositor/operations/COM_ImageOperation.h @@ -34,21 +34,21 @@ namespace blender::compositor { */ class BaseImageOperation : public MultiThreadedOperation { protected: - ImBuf *m_buffer; - Image *m_image; - ImageUser *m_imageUser; + ImBuf *buffer_; + Image *image_; + ImageUser *imageUser_; /* TODO: Remove raw buffers when removing Tiled implementation. */ - float *m_imageFloatBuffer; - unsigned int *m_imageByteBuffer; - float *m_depthBuffer; + float *imageFloatBuffer_; + unsigned int *imageByteBuffer_; + float *depthBuffer_; MemoryBuffer *depth_buffer_; - int m_imageheight; - int m_imagewidth; - int m_framenumber; - int m_numberOfChannels; - const RenderData *m_rd; - const char *m_viewName; + int imageheight_; + int imagewidth_; + int framenumber_; + int numberOfChannels_; + const RenderData *rd_; + const char *viewName_; BaseImageOperation(); /** @@ -63,23 +63,23 @@ class BaseImageOperation : public MultiThreadedOperation { void deinitExecution() override; void setImage(Image *image) { - m_image = image; + image_ = image; } void setImageUser(ImageUser *imageuser) { - m_imageUser = imageuser; + imageUser_ = imageuser; } void setRenderData(const RenderData *rd) { - m_rd = rd; + rd_ = rd; } void setViewName(const char *viewName) { - m_viewName = viewName; + viewName_ = viewName; } void setFramenumber(int framenumber) { - m_framenumber = framenumber; + framenumber_ = framenumber; } }; class ImageOperation : public BaseImageOperation { diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc index 1b7b42706bc..45b163c7e8b 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cc +++ b/source/blender/compositor/operations/COM_InpaintOperation.cc @@ -31,21 +31,21 @@ InpaintSimpleOperation::InpaintSimpleOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); this->flags.complex = true; - m_inputImageProgram = nullptr; - m_pixelorder = nullptr; - m_manhattan_distance = nullptr; - m_cached_buffer = nullptr; - m_cached_buffer_ready = false; + inputImageProgram_ = nullptr; + pixelorder_ = nullptr; + manhattan_distance_ = nullptr; + cached_buffer_ = nullptr; + cached_buffer_ready_ = false; flags.is_fullframe_operation = true; } void InpaintSimpleOperation::initExecution() { - m_inputImageProgram = this->getInputSocketReader(0); + inputImageProgram_ = this->getInputSocketReader(0); - m_pixelorder = nullptr; - m_manhattan_distance = nullptr; - m_cached_buffer = nullptr; - m_cached_buffer_ready = false; + pixelorder_ = nullptr; + manhattan_distance_ = nullptr; + cached_buffer_ = nullptr; + cached_buffer_ready_ = false; this->initMutex(); } @@ -76,8 +76,8 @@ float *InpaintSimpleOperation::get_pixel(int x, int y) ASSERT_XY_RANGE(x, y); - return &m_cached_buffer[y * width * COM_DATA_TYPE_COLOR_CHANNELS + - x * COM_DATA_TYPE_COLOR_CHANNELS]; + return &cached_buffer_[y * width * COM_DATA_TYPE_COLOR_CHANNELS + + x * COM_DATA_TYPE_COLOR_CHANNELS]; } int InpaintSimpleOperation::mdist(int x, int y) @@ -86,18 +86,18 @@ int InpaintSimpleOperation::mdist(int x, int y) ASSERT_XY_RANGE(x, y); - return m_manhattan_distance[y * width + x]; + return manhattan_distance_[y * width + x]; } bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) { int width = this->getWidth(); - if (curr >= m_area_size) { + if (curr >= area_size_) { return false; } - int r = m_pixelorder[curr++]; + int r = pixelorder_[curr++]; x = r % width; y = r / width; @@ -113,7 +113,7 @@ void InpaintSimpleOperation::calc_manhattan_distance() { int width = this->getWidth(); int height = this->getHeight(); - short *m = m_manhattan_distance = (short *)MEM_mallocN(sizeof(short) * width * height, __func__); + short *m = manhattan_distance_ = (short *)MEM_mallocN(sizeof(short) * width * height, __func__); int *offsets; offsets = (int *)MEM_callocN(sizeof(int) * (width + height + 1), @@ -159,12 +159,12 @@ void InpaintSimpleOperation::calc_manhattan_distance() offsets[i] += offsets[i - 1]; } - m_area_size = offsets[width + height]; - m_pixelorder = (int *)MEM_mallocN(sizeof(int) * m_area_size, __func__); + area_size_ = offsets[width + height]; + pixelorder_ = (int *)MEM_mallocN(sizeof(int) * area_size_, __func__); for (int i = 0; i < width * height; i++) { if (m[i] > 0) { - m_pixelorder[offsets[m[i] - 1]++] = i; + pixelorder_[offsets[m[i] - 1]++] = i; } } @@ -215,27 +215,27 @@ void InpaintSimpleOperation::pix_step(int x, int y) void *InpaintSimpleOperation::initializeTileData(rcti *rect) { - if (m_cached_buffer_ready) { - return m_cached_buffer; + if (cached_buffer_ready_) { + return cached_buffer_; } lockMutex(); - if (!m_cached_buffer_ready) { - MemoryBuffer *buf = (MemoryBuffer *)m_inputImageProgram->initializeTileData(rect); - m_cached_buffer = (float *)MEM_dupallocN(buf->getBuffer()); + if (!cached_buffer_ready_) { + MemoryBuffer *buf = (MemoryBuffer *)inputImageProgram_->initializeTileData(rect); + cached_buffer_ = (float *)MEM_dupallocN(buf->getBuffer()); this->calc_manhattan_distance(); int curr = 0; int x, y; - while (this->next_pixel(x, y, curr, m_iterations)) { + while (this->next_pixel(x, y, curr, iterations_)) { this->pix_step(x, y); } - m_cached_buffer_ready = true; + cached_buffer_ready_ = true; } unlockMutex(); - return m_cached_buffer; + return cached_buffer_; } void InpaintSimpleOperation::executePixel(float output[4], int x, int y, void * /*data*/) @@ -246,30 +246,30 @@ void InpaintSimpleOperation::executePixel(float output[4], int x, int y, void * void InpaintSimpleOperation::deinitExecution() { - m_inputImageProgram = nullptr; + inputImageProgram_ = nullptr; this->deinitMutex(); - if (m_cached_buffer) { - MEM_freeN(m_cached_buffer); - m_cached_buffer = nullptr; + if (cached_buffer_) { + MEM_freeN(cached_buffer_); + cached_buffer_ = nullptr; } - if (m_pixelorder) { - MEM_freeN(m_pixelorder); - m_pixelorder = nullptr; + if (pixelorder_) { + MEM_freeN(pixelorder_); + pixelorder_ = nullptr; } - if (m_manhattan_distance) { - MEM_freeN(m_manhattan_distance); - m_manhattan_distance = nullptr; + if (manhattan_distance_) { + MEM_freeN(manhattan_distance_); + manhattan_distance_ = nullptr; } - m_cached_buffer_ready = false; + cached_buffer_ready_ = false; } bool InpaintSimpleOperation::determineDependingAreaOfInterest(rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) { - if (m_cached_buffer_ready) { + if (cached_buffer_ready_) { return false; } @@ -298,28 +298,28 @@ void InpaintSimpleOperation::update_memory_buffer(MemoryBuffer *output, { /* TODO(manzanilla): once tiled implementation is removed, run multi-threaded where possible. */ MemoryBuffer *input = inputs[0]; - if (!m_cached_buffer_ready) { + if (!cached_buffer_ready_) { if (input->is_a_single_elem()) { MemoryBuffer *tmp = input->inflate(); - m_cached_buffer = tmp->release_ownership_buffer(); + cached_buffer_ = tmp->release_ownership_buffer(); delete tmp; } else { - m_cached_buffer = (float *)MEM_dupallocN(input->getBuffer()); + cached_buffer_ = (float *)MEM_dupallocN(input->getBuffer()); } this->calc_manhattan_distance(); int curr = 0; int x, y; - while (this->next_pixel(x, y, curr, m_iterations)) { + while (this->next_pixel(x, y, curr, iterations_)) { this->pix_step(x, y); } - m_cached_buffer_ready = true; + cached_buffer_ready_ = true; } const int num_channels = COM_data_type_num_channels(getOutputSocket()->getDataType()); - MemoryBuffer buf(m_cached_buffer, num_channels, input->getWidth(), input->getHeight()); + MemoryBuffer buf(cached_buffer_, num_channels, input->getWidth(), input->getHeight()); output->copy_from(&buf, area); } diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h index 87839dcb802..4187099f346 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.h +++ b/source/blender/compositor/operations/COM_InpaintOperation.h @@ -27,16 +27,16 @@ class InpaintSimpleOperation : public NodeOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputImageProgram; + SocketReader *inputImageProgram_; - int m_iterations; + int iterations_; - float *m_cached_buffer; - bool m_cached_buffer_ready; + float *cached_buffer_; + bool cached_buffer_ready_; - int *m_pixelorder; - int m_area_size; - short *m_manhattan_distance; + int *pixelorder_; + int area_size_; + short *manhattan_distance_; public: InpaintSimpleOperation(); @@ -59,7 +59,7 @@ class InpaintSimpleOperation : public NodeOperation { void setIterations(int iterations) { - m_iterations = iterations; + iterations_ = iterations; } bool determineDependingAreaOfInterest(rcti *input, diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc index f9f08a1d377..ba36dd35e47 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.cc +++ b/source/blender/compositor/operations/COM_InvertOperation.cc @@ -25,30 +25,30 @@ InvertOperation::InvertOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_inputValueProgram = nullptr; - m_inputColorProgram = nullptr; - m_color = true; - m_alpha = false; + inputValueProgram_ = nullptr; + inputColorProgram_ = nullptr; + color_ = true; + alpha_ = false; set_canvas_input_index(1); this->flags.can_be_constant = true; } void InvertOperation::initExecution() { - m_inputValueProgram = this->getInputSocketReader(0); - m_inputColorProgram = this->getInputSocketReader(1); + inputValueProgram_ = this->getInputSocketReader(0); + inputColorProgram_ = this->getInputSocketReader(1); } void InvertOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { float inputValue[4]; float inputColor[4]; - m_inputValueProgram->readSampled(inputValue, x, y, sampler); - m_inputColorProgram->readSampled(inputColor, x, y, sampler); + inputValueProgram_->readSampled(inputValue, x, y, sampler); + inputColorProgram_->readSampled(inputColor, x, y, sampler); const float value = inputValue[0]; const float invertedValue = 1.0f - value; - if (m_color) { + if (color_) { output[0] = (1.0f - inputColor[0]) * value + inputColor[0] * invertedValue; output[1] = (1.0f - inputColor[1]) * value + inputColor[1] * invertedValue; output[2] = (1.0f - inputColor[2]) * value + inputColor[2] * invertedValue; @@ -57,7 +57,7 @@ void InvertOperation::executePixelSampled(float output[4], float x, float y, Pix copy_v3_v3(output, inputColor); } - if (m_alpha) { + if (alpha_) { output[3] = (1.0f - inputColor[3]) * value + inputColor[3] * invertedValue; } else { @@ -67,8 +67,8 @@ void InvertOperation::executePixelSampled(float output[4], float x, float y, Pix void InvertOperation::deinitExecution() { - m_inputValueProgram = nullptr; - m_inputColorProgram = nullptr; + inputValueProgram_ = nullptr; + inputColorProgram_ = nullptr; } void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -80,7 +80,7 @@ void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, const float inverted_value = 1.0f - value; const float *color = it.in(1); - if (m_color) { + if (color_) { it.out[0] = (1.0f - color[0]) * value + color[0] * inverted_value; it.out[1] = (1.0f - color[1]) * value + color[1] * inverted_value; it.out[2] = (1.0f - color[2]) * value + color[2] * inverted_value; @@ -89,7 +89,7 @@ void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, copy_v3_v3(it.out, color); } - if (m_alpha) { + if (alpha_) { it.out[3] = (1.0f - color[3]) * value + color[3] * inverted_value; } else { diff --git a/source/blender/compositor/operations/COM_InvertOperation.h b/source/blender/compositor/operations/COM_InvertOperation.h index cb77c51f2b7..7db0394ad7a 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.h +++ b/source/blender/compositor/operations/COM_InvertOperation.h @@ -27,11 +27,11 @@ class InvertOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputValueProgram; - SocketReader *m_inputColorProgram; + SocketReader *inputValueProgram_; + SocketReader *inputColorProgram_; - bool m_alpha; - bool m_color; + bool alpha_; + bool color_; public: InvertOperation(); @@ -53,11 +53,11 @@ class InvertOperation : public MultiThreadedOperation { void setColor(bool color) { - m_color = color; + color_ = color; } void setAlpha(bool alpha) { - m_alpha = alpha; + alpha_ = alpha; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc index b5c93ad2888..fcf304de174 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc @@ -25,8 +25,8 @@ KeyingBlurOperation::KeyingBlurOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_size = 0; - m_axis = BLUR_AXIS_X; + size_ = 0; + axis_ = BLUR_AXIS_X; this->flags.complex = true; } @@ -46,8 +46,8 @@ void KeyingBlurOperation::executePixel(float output[4], int x, int y, void *data int count = 0; float average = 0.0f; - if (m_axis == 0) { - const int start = MAX2(0, x - m_size + 1), end = MIN2(bufferWidth, x + m_size); + if (axis_ == 0) { + const int start = MAX2(0, x - size_ + 1), end = MIN2(bufferWidth, x + size_); for (int cx = start; cx < end; cx++) { int bufferIndex = (y * bufferWidth + cx); average += buffer[bufferIndex]; @@ -55,7 +55,7 @@ void KeyingBlurOperation::executePixel(float output[4], int x, int y, void *data } } else { - const int start = MAX2(0, y - m_size + 1), end = MIN2(inputBuffer->getHeight(), y + m_size); + const int start = MAX2(0, y - size_ + 1), end = MIN2(inputBuffer->getHeight(), y + size_); for (int cy = start; cy < end; cy++) { int bufferIndex = (cy * bufferWidth + x); average += buffer[bufferIndex]; @@ -74,17 +74,17 @@ bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - if (m_axis == BLUR_AXIS_X) { - newInput.xmin = input->xmin - m_size; + if (axis_ == BLUR_AXIS_X) { + newInput.xmin = input->xmin - size_; newInput.ymin = input->ymin; - newInput.xmax = input->xmax + m_size; + newInput.xmax = input->xmax + size_; newInput.ymax = input->ymax; } else { newInput.xmin = input->xmin; - newInput.ymin = input->ymin - m_size; + newInput.ymin = input->ymin - size_; newInput.xmax = input->xmax; - newInput.ymax = input->ymax + m_size; + newInput.ymax = input->ymax + size_; } return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); @@ -94,18 +94,18 @@ void KeyingBlurOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &output_area, rcti &r_input_area) { - switch (m_axis) { + switch (axis_) { case BLUR_AXIS_X: - r_input_area.xmin = output_area.xmin - m_size; + r_input_area.xmin = output_area.xmin - size_; r_input_area.ymin = output_area.ymin; - r_input_area.xmax = output_area.xmax + m_size; + r_input_area.xmax = output_area.xmax + size_; r_input_area.ymax = output_area.ymax; break; case BLUR_AXIS_Y: r_input_area.xmin = output_area.xmin; - r_input_area.ymin = output_area.ymin - m_size; + r_input_area.ymin = output_area.ymin - size_; r_input_area.xmax = output_area.xmax; - r_input_area.ymax = output_area.ymax + m_size; + r_input_area.ymax = output_area.ymax + size_; break; default: BLI_assert_msg(0, "Unknown axis"); @@ -123,7 +123,7 @@ void KeyingBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, int coord_max; int elem_stride; std::function get_current_coord; - switch (m_axis) { + switch (axis_) { case BLUR_AXIS_X: get_current_coord = [&] { return it.x; }; coord_max = this->getWidth(); @@ -138,8 +138,8 @@ void KeyingBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, for (; !it.is_end(); ++it) { const int coord = get_current_coord(); - const int start_coord = MAX2(0, coord - m_size + 1); - const int end_coord = MIN2(coord_max, coord + m_size); + const int start_coord = MAX2(0, coord - size_ + 1); + const int end_coord = MIN2(coord_max, coord + size_); const int count = end_coord - start_coord; float sum = 0.0f; diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.h b/source/blender/compositor/operations/COM_KeyingBlurOperation.h index 9f312c86747..56fa0f84880 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.h +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.h @@ -27,8 +27,8 @@ namespace blender::compositor { */ class KeyingBlurOperation : public MultiThreadedOperation { protected: - int m_size; - int m_axis; + int size_; + int axis_; public: enum BlurAxis { @@ -40,11 +40,11 @@ class KeyingBlurOperation : public MultiThreadedOperation { void setSize(int value) { - m_size = value; + size_ = value; } void setAxis(int value) { - m_axis = value; + axis_ = value; } void *initializeTileData(rcti *rect) override; diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc index b5a3b28e54e..e6097dd71a6 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc @@ -25,13 +25,13 @@ KeyingClipOperation::KeyingClipOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_kernelRadius = 3; - m_kernelTolerance = 0.1f; + kernelRadius_ = 3; + kernelTolerance_ = 0.1f; - m_clipBlack = 0.0f; - m_clipWhite = 1.0f; + clipBlack_ = 0.0f; + clipWhite_ = 1.0f; - m_isEdgeMatte = false; + isEdgeMatte_ = false; this->flags.complex = true; } @@ -45,8 +45,8 @@ void *KeyingClipOperation::initializeTileData(rcti *rect) void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data) { - const int delta = m_kernelRadius; - const float tolerance = m_kernelTolerance; + const int delta = kernelRadius_; + const float tolerance = kernelTolerance_; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; float *buffer = inputBuffer->getBuffer(); @@ -86,7 +86,7 @@ void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data } } - if (m_isEdgeMatte) { + if (isEdgeMatte_) { if (ok) { output[0] = 0.0f; } @@ -98,14 +98,14 @@ void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data output[0] = value; if (ok) { - if (output[0] < m_clipBlack) { + if (output[0] < clipBlack_) { output[0] = 0.0f; } - else if (output[0] >= m_clipWhite) { + else if (output[0] >= clipWhite_) { output[0] = 1.0f; } else { - output[0] = (output[0] - m_clipBlack) / (m_clipWhite - m_clipBlack); + output[0] = (output[0] - clipBlack_) / (clipWhite_ - clipBlack_); } } } @@ -117,10 +117,10 @@ bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - newInput.xmin = input->xmin - m_kernelRadius; - newInput.ymin = input->ymin - m_kernelRadius; - newInput.xmax = input->xmax + m_kernelRadius; - newInput.ymax = input->ymax + m_kernelRadius; + newInput.xmin = input->xmin - kernelRadius_; + newInput.ymin = input->ymin - kernelRadius_; + newInput.xmax = input->xmax + kernelRadius_; + newInput.ymax = input->ymax + kernelRadius_; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -131,10 +131,10 @@ void KeyingClipOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - m_kernelRadius; - r_input_area.xmax = output_area.xmax + m_kernelRadius; - r_input_area.ymin = output_area.ymin - m_kernelRadius; - r_input_area.ymax = output_area.ymax + m_kernelRadius; + r_input_area.xmin = output_area.xmin - kernelRadius_; + r_input_area.xmax = output_area.xmax + kernelRadius_; + r_input_area.ymin = output_area.ymin - kernelRadius_; + r_input_area.ymax = output_area.ymax + kernelRadius_; } void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -144,8 +144,8 @@ void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output, const MemoryBuffer *input = inputs[0]; BuffersIterator it = output->iterate_with(inputs, area); - const int delta = m_kernelRadius; - const float tolerance = m_kernelTolerance; + const int delta = kernelRadius_; + const float tolerance = kernelTolerance_; const int width = this->getWidth(); const int height = this->getHeight(); const int row_stride = input->row_stride; @@ -190,21 +190,21 @@ void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output, } } - if (m_isEdgeMatte) { + if (isEdgeMatte_) { *it.out = ok ? 0.0f : 1.0f; } else { if (!ok) { *it.out = value; } - else if (value < m_clipBlack) { + else if (value < clipBlack_) { *it.out = 0.0f; } - else if (value >= m_clipWhite) { + else if (value >= clipWhite_) { *it.out = 1.0f; } else { - *it.out = (value - m_clipBlack) / (m_clipWhite - m_clipBlack); + *it.out = (value - clipBlack_) / (clipWhite_ - clipBlack_); } } } diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.h b/source/blender/compositor/operations/COM_KeyingClipOperation.h index adbb792d7b8..422d79597ce 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.h +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.h @@ -27,38 +27,38 @@ namespace blender::compositor { */ class KeyingClipOperation : public MultiThreadedOperation { protected: - float m_clipBlack; - float m_clipWhite; + float clipBlack_; + float clipWhite_; - int m_kernelRadius; - float m_kernelTolerance; + int kernelRadius_; + float kernelTolerance_; - bool m_isEdgeMatte; + bool isEdgeMatte_; public: KeyingClipOperation(); void setClipBlack(float value) { - m_clipBlack = value; + clipBlack_ = value; } void setClipWhite(float value) { - m_clipWhite = value; + clipWhite_ = value; } void setKernelRadius(int value) { - m_kernelRadius = value; + kernelRadius_ = value; } void setKernelTolerance(float value) { - m_kernelTolerance = value; + kernelTolerance_ = value; } void setIsEdgeMatte(bool value) { - m_isEdgeMatte = value; + isEdgeMatte_ = value; } void *initializeTileData(rcti *rect) override; diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc index e107fee6bb4..8b090d5dc38 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc @@ -26,24 +26,24 @@ KeyingDespillOperation::KeyingDespillOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_despillFactor = 0.5f; - m_colorBalance = 0.5f; + despillFactor_ = 0.5f; + colorBalance_ = 0.5f; - m_pixelReader = nullptr; - m_screenReader = nullptr; + pixelReader_ = nullptr; + screenReader_ = nullptr; flags.can_be_constant = true; } void KeyingDespillOperation::initExecution() { - m_pixelReader = this->getInputSocketReader(0); - m_screenReader = this->getInputSocketReader(1); + pixelReader_ = this->getInputSocketReader(0); + screenReader_ = this->getInputSocketReader(1); } void KeyingDespillOperation::deinitExecution() { - m_pixelReader = nullptr; - m_screenReader = nullptr; + pixelReader_ = nullptr; + screenReader_ = nullptr; } void KeyingDespillOperation::executePixelSampled(float output[4], @@ -54,8 +54,8 @@ void KeyingDespillOperation::executePixelSampled(float output[4], float pixelColor[4]; float screenColor[4]; - m_pixelReader->readSampled(pixelColor, x, y, sampler); - m_screenReader->readSampled(screenColor, x, y, sampler); + pixelReader_->readSampled(pixelColor, x, y, sampler); + screenReader_->readSampled(screenColor, x, y, sampler); const int screen_primary_channel = max_axis_v3(screenColor); const int other_1 = (screen_primary_channel + 1) % 3; @@ -66,13 +66,13 @@ void KeyingDespillOperation::executePixelSampled(float output[4], float average_value, amount; - average_value = m_colorBalance * pixelColor[min_channel] + - (1.0f - m_colorBalance) * pixelColor[max_channel]; + average_value = colorBalance_ * pixelColor[min_channel] + + (1.0f - colorBalance_) * pixelColor[max_channel]; amount = (pixelColor[screen_primary_channel] - average_value); copy_v4_v4(output, pixelColor); - const float amount_despill = m_despillFactor * amount; + const float amount_despill = despillFactor_ * amount; if (amount_despill > 0.0f) { output[screen_primary_channel] = pixelColor[screen_primary_channel] - amount_despill; } @@ -93,13 +93,13 @@ void KeyingDespillOperation::update_memory_buffer_partial(MemoryBuffer *output, const int min_channel = MIN2(other_1, other_2); const int max_channel = MAX2(other_1, other_2); - const float average_value = m_colorBalance * pixel_color[min_channel] + - (1.0f - m_colorBalance) * pixel_color[max_channel]; + const float average_value = colorBalance_ * pixel_color[min_channel] + + (1.0f - colorBalance_) * pixel_color[max_channel]; const float amount = (pixel_color[screen_primary_channel] - average_value); copy_v4_v4(it.out, pixel_color); - const float amount_despill = m_despillFactor * amount; + const float amount_despill = despillFactor_ * amount; if (amount_despill > 0.0f) { it.out[screen_primary_channel] = pixel_color[screen_primary_channel] - amount_despill; } diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.h b/source/blender/compositor/operations/COM_KeyingDespillOperation.h index 257c24f3b58..51efa44ce1c 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.h +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.h @@ -27,10 +27,10 @@ namespace blender::compositor { */ class KeyingDespillOperation : public MultiThreadedOperation { protected: - SocketReader *m_pixelReader; - SocketReader *m_screenReader; - float m_despillFactor; - float m_colorBalance; + SocketReader *pixelReader_; + SocketReader *screenReader_; + float despillFactor_; + float colorBalance_; public: KeyingDespillOperation(); @@ -40,11 +40,11 @@ class KeyingDespillOperation : public MultiThreadedOperation { void setDespillFactor(float value) { - m_despillFactor = value; + despillFactor_ = value; } void setColorBalance(float value) { - m_colorBalance = value; + colorBalance_ = value; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cc b/source/blender/compositor/operations/COM_KeyingOperation.cc index 7b0b80b5796..7a7fe716fb5 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingOperation.cc @@ -42,22 +42,22 @@ KeyingOperation::KeyingOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Value); - m_screenBalance = 0.5f; + screenBalance_ = 0.5f; - m_pixelReader = nullptr; - m_screenReader = nullptr; + pixelReader_ = nullptr; + screenReader_ = nullptr; } void KeyingOperation::initExecution() { - m_pixelReader = this->getInputSocketReader(0); - m_screenReader = this->getInputSocketReader(1); + pixelReader_ = this->getInputSocketReader(0); + screenReader_ = this->getInputSocketReader(1); } void KeyingOperation::deinitExecution() { - m_pixelReader = nullptr; - m_screenReader = nullptr; + pixelReader_ = nullptr; + screenReader_ = nullptr; } void KeyingOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -65,8 +65,8 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix float pixel_color[4]; float screen_color[4]; - m_pixelReader->readSampled(pixel_color, x, y, sampler); - m_screenReader->readSampled(screen_color, x, y, sampler); + pixelReader_->readSampled(pixel_color, x, y, sampler); + screenReader_->readSampled(screen_color, x, y, sampler); const int primary_channel = max_axis_v3(screen_color); const float min_pixel_color = min_fff(pixel_color[0], pixel_color[1], pixel_color[2]); @@ -80,8 +80,8 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix output[0] = 1.0f; } else { - float saturation = get_pixel_saturation(pixel_color, m_screenBalance, primary_channel); - float screen_saturation = get_pixel_saturation(screen_color, m_screenBalance, primary_channel); + float saturation = get_pixel_saturation(pixel_color, screenBalance_, primary_channel); + float screen_saturation = get_pixel_saturation(screen_color, screenBalance_, primary_channel); if (saturation < 0) { /* means main channel of pixel is different from screen, @@ -124,9 +124,9 @@ void KeyingOperation::update_memory_buffer_partial(MemoryBuffer *output, it.out[0] = 1.0f; } else { - const float saturation = get_pixel_saturation(pixel_color, m_screenBalance, primary_channel); + const float saturation = get_pixel_saturation(pixel_color, screenBalance_, primary_channel); const float screen_saturation = get_pixel_saturation( - screen_color, m_screenBalance, primary_channel); + screen_color, screenBalance_, primary_channel); if (saturation < 0) { /* Means main channel of pixel is different from screen, diff --git a/source/blender/compositor/operations/COM_KeyingOperation.h b/source/blender/compositor/operations/COM_KeyingOperation.h index 89674e64701..3ad18085f6b 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.h +++ b/source/blender/compositor/operations/COM_KeyingOperation.h @@ -31,10 +31,10 @@ namespace blender::compositor { */ class KeyingOperation : public MultiThreadedOperation { protected: - SocketReader *m_pixelReader; - SocketReader *m_screenReader; + SocketReader *pixelReader_; + SocketReader *screenReader_; - float m_screenBalance; + float screenBalance_; public: KeyingOperation(); @@ -44,7 +44,7 @@ class KeyingOperation : public MultiThreadedOperation { void setScreenBalance(float value) { - m_screenBalance = value; + screenBalance_ = value; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc index 646450eb970..8f14b5415b5 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc @@ -29,31 +29,31 @@ namespace blender::compositor { KeyingScreenOperation::KeyingScreenOperation() { this->addOutputSocket(DataType::Color); - m_movieClip = nullptr; - m_framenumber = 0; - m_trackingObject[0] = 0; + movieClip_ = nullptr; + framenumber_ = 0; + trackingObject_[0] = 0; flags.complex = true; - m_cachedTriangulation = nullptr; + cachedTriangulation_ = nullptr; } void KeyingScreenOperation::initExecution() { initMutex(); if (execution_model_ == eExecutionModel::FullFrame) { - BLI_assert(m_cachedTriangulation == nullptr); - if (m_movieClip) { - m_cachedTriangulation = buildVoronoiTriangulation(); + BLI_assert(cachedTriangulation_ == nullptr); + if (movieClip_) { + cachedTriangulation_ = buildVoronoiTriangulation(); } } else { - m_cachedTriangulation = nullptr; + cachedTriangulation_ = nullptr; } } void KeyingScreenOperation::deinitExecution() { - if (m_cachedTriangulation) { - TriangulationData *triangulation = m_cachedTriangulation; + if (cachedTriangulation_) { + TriangulationData *triangulation = cachedTriangulation_; if (triangulation->triangulated_points) { MEM_freeN(triangulation->triangulated_points); @@ -67,9 +67,9 @@ void KeyingScreenOperation::deinitExecution() MEM_freeN(triangulation->triangles_AABB); } - MEM_freeN(m_cachedTriangulation); + MEM_freeN(cachedTriangulation_); - m_cachedTriangulation = nullptr; + cachedTriangulation_ = nullptr; } } @@ -77,7 +77,7 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri { MovieClipUser user = {0}; TriangulationData *triangulation; - MovieTracking *tracking = &m_movieClip->tracking; + MovieTracking *tracking = &movieClip_->tracking; MovieTrackingTrack *track; VoronoiSite *sites, *site; ImBuf *ibuf; @@ -87,10 +87,10 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri int i; int width = this->getWidth(); int height = this->getHeight(); - int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, m_framenumber); + int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, framenumber_); - if (m_trackingObject[0]) { - MovieTrackingObject *object = BKE_tracking_object_get_named(tracking, m_trackingObject); + if (trackingObject_[0]) { + MovieTrackingObject *object = BKE_tracking_object_get_named(tracking, trackingObject_); if (!object) { return nullptr; @@ -126,7 +126,7 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri } BKE_movieclip_user_set_frame(&user, clip_frame); - ibuf = BKE_movieclip_get_ibuf(m_movieClip, &user); + ibuf = BKE_movieclip_get_ibuf(movieClip_, &user); if (!ibuf) { return nullptr; @@ -237,7 +237,7 @@ KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti * int chunk_size = 20; int i; - triangulation = m_cachedTriangulation; + triangulation = cachedTriangulation_; if (!triangulation) { return nullptr; @@ -271,14 +271,14 @@ KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti * void *KeyingScreenOperation::initializeTileData(rcti *rect) { - if (m_movieClip == nullptr) { + if (movieClip_ == nullptr) { return nullptr; } - if (!m_cachedTriangulation) { + if (!cachedTriangulation_) { lockMutex(); - if (m_cachedTriangulation == nullptr) { - m_cachedTriangulation = buildVoronoiTriangulation(); + if (cachedTriangulation_ == nullptr) { + cachedTriangulation_ = buildVoronoiTriangulation(); } unlockMutex(); } @@ -301,13 +301,13 @@ void KeyingScreenOperation::determine_canvas(const rcti &preferred_area, rcti &r { r_area = COM_AREA_NONE; - if (m_movieClip) { + if (movieClip_) { MovieClipUser user = {0}; int width, height; - int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, m_framenumber); + int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, framenumber_); BKE_movieclip_user_set_frame(&user, clip_frame); - BKE_movieclip_get_size(m_movieClip, &user, &width, &height); + BKE_movieclip_get_size(movieClip_, &user, &width, &height); r_area = preferred_area; r_area.xmax = r_area.xmin + width; r_area.ymax = r_area.ymin + height; @@ -321,8 +321,8 @@ void KeyingScreenOperation::executePixel(float output[4], int x, int y, void *da output[2] = 0.0f; output[3] = 1.0f; - if (m_movieClip && data) { - TriangulationData *triangulation = m_cachedTriangulation; + if (movieClip_ && data) { + TriangulationData *triangulation = cachedTriangulation_; TileData *tile_data = (TileData *)data; int i; float co[2] = {(float)x, (float)y}; @@ -356,7 +356,7 @@ void KeyingScreenOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - if (m_movieClip == nullptr) { + if (movieClip_ == nullptr) { output->fill(area, COM_COLOR_BLACK); return; } @@ -366,7 +366,7 @@ void KeyingScreenOperation::update_memory_buffer_partial(MemoryBuffer *output, const int *triangles = tri_area->triangles; const int num_triangles = tri_area->triangles_total; - const TriangulationData *triangulation = m_cachedTriangulation; + const TriangulationData *triangulation = cachedTriangulation_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { copy_v4_v4(it.out, COM_COLOR_BLACK); diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h index 32299c8f241..a828513d9fa 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h @@ -49,10 +49,10 @@ class KeyingScreenOperation : public MultiThreadedOperation { int triangles_total; } TileData; - MovieClip *m_movieClip; - int m_framenumber; - TriangulationData *m_cachedTriangulation; - char m_trackingObject[64]; + MovieClip *movieClip_; + int framenumber_; + TriangulationData *cachedTriangulation_; + char trackingObject_[64]; /** * Determine the output resolution. The resolution is retrieved from the Renderer @@ -72,15 +72,15 @@ class KeyingScreenOperation : public MultiThreadedOperation { void setMovieClip(MovieClip *clip) { - m_movieClip = clip; + movieClip_ = clip; } void setTrackingObject(const char *object) { - BLI_strncpy(m_trackingObject, object, sizeof(m_trackingObject)); + BLI_strncpy(trackingObject_, object, sizeof(trackingObject_)); } void setFramenumber(int framenumber) { - m_framenumber = framenumber; + framenumber_ = framenumber; } void executePixel(float output[4], int x, int y, void *data) override; diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc index 97a615bdd1e..d266cbd00f5 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc @@ -27,18 +27,18 @@ LuminanceMatteOperation::LuminanceMatteOperation() addInputSocket(DataType::Color); addOutputSocket(DataType::Value); - m_inputImageProgram = nullptr; + inputImageProgram_ = nullptr; flags.can_be_constant = true; } void LuminanceMatteOperation::initExecution() { - m_inputImageProgram = this->getInputSocketReader(0); + inputImageProgram_ = this->getInputSocketReader(0); } void LuminanceMatteOperation::deinitExecution() { - m_inputImageProgram = nullptr; + inputImageProgram_ = nullptr; } void LuminanceMatteOperation::executePixelSampled(float output[4], @@ -47,10 +47,10 @@ void LuminanceMatteOperation::executePixelSampled(float output[4], PixelSampler sampler) { float inColor[4]; - m_inputImageProgram->readSampled(inColor, x, y, sampler); + inputImageProgram_->readSampled(inColor, x, y, sampler); - const float high = m_settings->t1; - const float low = m_settings->t2; + const float high = settings_->t1; + const float low = settings_->t2; const float luminance = IMB_colormanagement_get_luminance(inColor); float alpha; @@ -91,8 +91,8 @@ void LuminanceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, */ /* Test range. */ - const float high = m_settings->t1; - const float low = m_settings->t2; + const float high = settings_->t1; + const float low = settings_->t2; float alpha; if (luminance > high) { alpha = 1.0f; diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h index 53618a4a107..6262d1999bc 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h @@ -28,8 +28,8 @@ namespace blender::compositor { */ class LuminanceMatteOperation : public MultiThreadedOperation { private: - NodeChroma *m_settings; - SocketReader *m_inputImageProgram; + NodeChroma *settings_; + SocketReader *inputImageProgram_; public: /** @@ -47,7 +47,7 @@ class LuminanceMatteOperation : public MultiThreadedOperation { void setSettings(NodeChroma *nodeChroma) { - m_settings = nodeChroma; + settings_ = nodeChroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.cc b/source/blender/compositor/operations/COM_MapRangeOperation.cc index cdf41290a0a..0fcdbd1fd99 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.cc +++ b/source/blender/compositor/operations/COM_MapRangeOperation.cc @@ -28,18 +28,18 @@ MapRangeOperation::MapRangeOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_inputOperation = nullptr; - m_useClamp = false; + inputOperation_ = nullptr; + useClamp_ = false; flags.can_be_constant = true; } void MapRangeOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); - m_sourceMinOperation = this->getInputSocketReader(1); - m_sourceMaxOperation = this->getInputSocketReader(2); - m_destMinOperation = this->getInputSocketReader(3); - m_destMaxOperation = this->getInputSocketReader(4); + inputOperation_ = this->getInputSocketReader(0); + sourceMinOperation_ = this->getInputSocketReader(1); + sourceMaxOperation_ = this->getInputSocketReader(2); + destMinOperation_ = this->getInputSocketReader(3); + destMaxOperation_ = this->getInputSocketReader(4); } /* The code below assumes all data is inside range +- this, and that input buffer is single channel @@ -56,11 +56,11 @@ void MapRangeOperation::executePixelSampled(float output[4], float source_min, source_max; float dest_min, dest_max; - m_inputOperation->readSampled(inputs, x, y, sampler); - m_sourceMinOperation->readSampled(inputs + 1, x, y, sampler); - m_sourceMaxOperation->readSampled(inputs + 2, x, y, sampler); - m_destMinOperation->readSampled(inputs + 3, x, y, sampler); - m_destMaxOperation->readSampled(inputs + 4, x, y, sampler); + inputOperation_->readSampled(inputs, x, y, sampler); + sourceMinOperation_->readSampled(inputs + 1, x, y, sampler); + sourceMaxOperation_->readSampled(inputs + 2, x, y, sampler); + destMinOperation_->readSampled(inputs + 3, x, y, sampler); + destMaxOperation_->readSampled(inputs + 4, x, y, sampler); value = inputs[0]; source_min = inputs[1]; @@ -84,7 +84,7 @@ void MapRangeOperation::executePixelSampled(float output[4], value = dest_min; } - if (m_useClamp) { + if (useClamp_) { if (dest_max > dest_min) { CLAMP(value, dest_min, dest_max); } @@ -98,11 +98,11 @@ void MapRangeOperation::executePixelSampled(float output[4], void MapRangeOperation::deinitExecution() { - m_inputOperation = nullptr; - m_sourceMinOperation = nullptr; - m_sourceMaxOperation = nullptr; - m_destMinOperation = nullptr; - m_destMaxOperation = nullptr; + inputOperation_ = nullptr; + sourceMinOperation_ = nullptr; + sourceMaxOperation_ = nullptr; + destMinOperation_ = nullptr; + destMaxOperation_ = nullptr; } void MapRangeOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -131,7 +131,7 @@ void MapRangeOperation::update_memory_buffer_partial(MemoryBuffer *output, value = dest_min; } - if (m_useClamp) { + if (useClamp_) { if (dest_max > dest_min) { CLAMP(value, dest_min, dest_max); } diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.h b/source/blender/compositor/operations/COM_MapRangeOperation.h index b061eb687c1..f5c42d19079 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.h +++ b/source/blender/compositor/operations/COM_MapRangeOperation.h @@ -32,13 +32,13 @@ class MapRangeOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputOperation; - SocketReader *m_sourceMinOperation; - SocketReader *m_sourceMaxOperation; - SocketReader *m_destMinOperation; - SocketReader *m_destMaxOperation; + SocketReader *inputOperation_; + SocketReader *sourceMinOperation_; + SocketReader *sourceMaxOperation_; + SocketReader *destMinOperation_; + SocketReader *destMaxOperation_; - bool m_useClamp; + bool useClamp_; public: /** @@ -66,7 +66,7 @@ class MapRangeOperation : public MultiThreadedOperation { */ void setUseClamp(bool value) { - m_useClamp = value; + useClamp_ = value; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index 83f4be133ed..a5140c30fed 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -25,12 +25,12 @@ MapUVOperation::MapUVOperation() this->addInputSocket(DataType::Color, ResizeMode::Align); this->addInputSocket(DataType::Vector); this->addOutputSocket(DataType::Color); - m_alpha = 0.0f; + alpha_ = 0.0f; this->flags.complex = true; set_canvas_input_index(UV_INPUT_INDEX); - m_inputUVProgram = nullptr; - m_inputColorProgram = nullptr; + inputUVProgram_ = nullptr; + inputColorProgram_ = nullptr; } void MapUVOperation::init_data() @@ -46,11 +46,11 @@ void MapUVOperation::init_data() void MapUVOperation::initExecution() { - m_inputColorProgram = this->getInputSocketReader(0); - m_inputUVProgram = this->getInputSocketReader(1); + inputColorProgram_ = this->getInputSocketReader(0); + inputUVProgram_ = this->getInputSocketReader(1); if (execution_model_ == eExecutionModel::Tiled) { uv_input_read_fn_ = [=](float x, float y, float *out) { - m_inputUVProgram->readSampled(out, x, y, PixelSampler::Bilinear); + inputUVProgram_->readSampled(out, x, y, PixelSampler::Bilinear); }; } } @@ -70,17 +70,17 @@ void MapUVOperation::executePixelSampled(float output[4], } /* EWA filtering */ - m_inputColorProgram->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + inputColorProgram_->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); /* UV to alpha threshold */ - const float threshold = m_alpha * 0.05f; + const float threshold = alpha_ * 0.05f; /* XXX alpha threshold is used to fade out pixels on boundaries with invalid derivatives. * this calculation is not very well defined, should be looked into if it becomes a problem ... */ float du = len_v2(deriv[0]); float dv = len_v2(deriv[1]); - float factor = 1.0f - threshold * (du / m_inputColorProgram->getWidth() + - dv / m_inputColorProgram->getHeight()); + float factor = 1.0f - threshold * (du / inputColorProgram_->getWidth() + + dv / inputColorProgram_->getHeight()); if (factor < 0.0f) { alpha = 0.0f; } @@ -164,8 +164,8 @@ void MapUVOperation::pixelTransform(const float xy[2], void MapUVOperation::deinitExecution() { - m_inputUVProgram = nullptr; - m_inputColorProgram = nullptr; + inputUVProgram_ = nullptr; + inputColorProgram_ = nullptr; } bool MapUVOperation::determineDependingAreaOfInterest(rcti *input, @@ -246,7 +246,7 @@ void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output, input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); /* UV to alpha threshold. */ - const float threshold = m_alpha * 0.05f; + const float threshold = alpha_ * 0.05f; /* XXX alpha threshold is used to fade out pixels on boundaries with invalid derivatives. * this calculation is not very well defined, should be looked into if it becomes a problem ... */ diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h index 612caab21ad..7025e02e069 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.h +++ b/source/blender/compositor/operations/COM_MapUVOperation.h @@ -29,15 +29,15 @@ class MapUVOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputUVProgram; - SocketReader *m_inputColorProgram; + SocketReader *inputUVProgram_; + SocketReader *inputColorProgram_; int uv_width_; int uv_height_; int image_width_; int image_height_; - float m_alpha; + float alpha_; std::function uv_input_read_fn_; @@ -72,7 +72,7 @@ class MapUVOperation : public MultiThreadedOperation { void setAlpha(float alpha) { - m_alpha = alpha; + alpha_ = alpha; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc index 5351f7ac7e4..0f4175c3d3d 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.cc +++ b/source/blender/compositor/operations/COM_MapValueOperation.cc @@ -24,13 +24,13 @@ MapValueOperation::MapValueOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_inputOperation = nullptr; + inputOperation_ = nullptr; flags.can_be_constant = true; } void MapValueOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); + inputOperation_ = this->getInputSocketReader(0); } void MapValueOperation::executePixelSampled(float output[4], @@ -39,8 +39,8 @@ void MapValueOperation::executePixelSampled(float output[4], PixelSampler sampler) { float src[4]; - m_inputOperation->readSampled(src, x, y, sampler); - TexMapping *texmap = m_settings; + inputOperation_->readSampled(src, x, y, sampler); + TexMapping *texmap = settings_; float value = (src[0] + texmap->loc[0]) * texmap->size[0]; if (texmap->flag & TEXMAP_CLIP_MIN) { if (value < texmap->min[0]) { @@ -58,7 +58,7 @@ void MapValueOperation::executePixelSampled(float output[4], void MapValueOperation::deinitExecution() { - m_inputOperation = nullptr; + inputOperation_ = nullptr; } void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -67,7 +67,7 @@ void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output, { for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float input = *it.in(0); - TexMapping *texmap = m_settings; + TexMapping *texmap = settings_; float value = (input + texmap->loc[0]) * texmap->size[0]; if (texmap->flag & TEXMAP_CLIP_MIN) { if (value < texmap->min[0]) { diff --git a/source/blender/compositor/operations/COM_MapValueOperation.h b/source/blender/compositor/operations/COM_MapValueOperation.h index 75761681f12..38cb75869d5 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.h +++ b/source/blender/compositor/operations/COM_MapValueOperation.h @@ -32,8 +32,8 @@ class MapValueOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputOperation; - TexMapping *m_settings; + SocketReader *inputOperation_; + TexMapping *settings_; public: /** @@ -61,7 +61,7 @@ class MapValueOperation : public MultiThreadedOperation { */ void setSettings(TexMapping *settings) { - m_settings = settings; + settings_ = settings; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc index aa7b693554f..d02d23445b6 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cc +++ b/source/blender/compositor/operations/COM_MaskOperation.cc @@ -26,34 +26,34 @@ namespace blender::compositor { MaskOperation::MaskOperation() { this->addOutputSocket(DataType::Value); - m_mask = nullptr; - m_maskWidth = 0; - m_maskHeight = 0; - m_maskWidthInv = 0.0f; - m_maskHeightInv = 0.0f; - m_frame_shutter = 0.0f; - m_frame_number = 0; - m_rasterMaskHandleTot = 1; - memset(m_rasterMaskHandles, 0, sizeof(m_rasterMaskHandles)); + mask_ = nullptr; + maskWidth_ = 0; + maskHeight_ = 0; + maskWidthInv_ = 0.0f; + maskHeightInv_ = 0.0f; + frame_shutter_ = 0.0f; + frame_number_ = 0; + rasterMaskHandleTot_ = 1; + memset(rasterMaskHandles_, 0, sizeof(rasterMaskHandles_)); } void MaskOperation::initExecution() { - if (m_mask && m_rasterMaskHandles[0] == nullptr) { - if (m_rasterMaskHandleTot == 1) { - m_rasterMaskHandles[0] = BKE_maskrasterize_handle_new(); + if (mask_ && rasterMaskHandles_[0] == nullptr) { + if (rasterMaskHandleTot_ == 1) { + rasterMaskHandles_[0] = BKE_maskrasterize_handle_new(); BKE_maskrasterize_handle_init( - m_rasterMaskHandles[0], m_mask, m_maskWidth, m_maskHeight, true, true, m_do_feather); + rasterMaskHandles_[0], mask_, maskWidth_, maskHeight_, true, true, do_feather_); } else { /* make a throw away copy of the mask */ - const float frame = (float)m_frame_number - m_frame_shutter; - const float frame_step = (m_frame_shutter * 2.0f) / m_rasterMaskHandleTot; + const float frame = (float)frame_number_ - frame_shutter_; + const float frame_step = (frame_shutter_ * 2.0f) / rasterMaskHandleTot_; float frame_iter = frame; Mask *mask_temp = (Mask *)BKE_id_copy_ex( - nullptr, &m_mask->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); + nullptr, &mask_->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); /* trick so we can get unkeyed edits to display */ { @@ -62,24 +62,19 @@ void MaskOperation::initExecution() for (masklay = (MaskLayer *)mask_temp->masklayers.first; masklay; masklay = masklay->next) { - masklay_shape = BKE_mask_layer_shape_verify_frame(masklay, m_frame_number); + masklay_shape = BKE_mask_layer_shape_verify_frame(masklay, frame_number_); BKE_mask_layer_shape_from_mask(masklay, masklay_shape); } } - for (unsigned int i = 0; i < m_rasterMaskHandleTot; i++) { - m_rasterMaskHandles[i] = BKE_maskrasterize_handle_new(); + for (unsigned int i = 0; i < rasterMaskHandleTot_; i++) { + rasterMaskHandles_[i] = BKE_maskrasterize_handle_new(); /* re-eval frame info */ BKE_mask_evaluate(mask_temp, frame_iter, true); - BKE_maskrasterize_handle_init(m_rasterMaskHandles[i], - mask_temp, - m_maskWidth, - m_maskHeight, - true, - true, - m_do_feather); + BKE_maskrasterize_handle_init( + rasterMaskHandles_[i], mask_temp, maskWidth_, maskHeight_, true, true, do_feather_); frame_iter += frame_step; } @@ -91,23 +86,23 @@ void MaskOperation::initExecution() void MaskOperation::deinitExecution() { - for (unsigned int i = 0; i < m_rasterMaskHandleTot; i++) { - if (m_rasterMaskHandles[i]) { - BKE_maskrasterize_handle_free(m_rasterMaskHandles[i]); - m_rasterMaskHandles[i] = nullptr; + for (unsigned int i = 0; i < rasterMaskHandleTot_; i++) { + if (rasterMaskHandles_[i]) { + BKE_maskrasterize_handle_free(rasterMaskHandles_[i]); + rasterMaskHandles_[i] = nullptr; } } } void MaskOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (m_maskWidth == 0 || m_maskHeight == 0) { + if (maskWidth_ == 0 || maskHeight_ == 0) { r_area = COM_AREA_NONE; } else { r_area = preferred_area; - r_area.xmax = r_area.xmin + m_maskWidth; - r_area.ymax = r_area.ymin + m_maskHeight; + r_area.xmax = r_area.xmin + maskWidth_; + r_area.ymax = r_area.ymin + maskHeight_; } } @@ -117,13 +112,13 @@ void MaskOperation::executePixelSampled(float output[4], PixelSampler /*sampler*/) { const float xy[2] = { - (x * m_maskWidthInv) + m_mask_px_ofs[0], - (y * m_maskHeightInv) + m_mask_px_ofs[1], + (x * maskWidthInv_) + mask_px_ofs_[0], + (y * maskHeightInv_) + mask_px_ofs_[1], }; - if (m_rasterMaskHandleTot == 1) { - if (m_rasterMaskHandles[0]) { - output[0] = BKE_maskrasterize_handle_sample(m_rasterMaskHandles[0], xy); + if (rasterMaskHandleTot_ == 1) { + if (rasterMaskHandles_[0]) { + output[0] = BKE_maskrasterize_handle_sample(rasterMaskHandles_[0], xy); } else { output[0] = 0.0f; @@ -133,14 +128,14 @@ void MaskOperation::executePixelSampled(float output[4], /* In case loop below fails. */ output[0] = 0.0f; - for (unsigned int i = 0; i < m_rasterMaskHandleTot; i++) { - if (m_rasterMaskHandles[i]) { - output[0] += BKE_maskrasterize_handle_sample(m_rasterMaskHandles[i], xy); + for (unsigned int i = 0; i < rasterMaskHandleTot_; i++) { + if (rasterMaskHandles_[i]) { + output[0] += BKE_maskrasterize_handle_sample(rasterMaskHandles_[i], xy); } } /* until we get better falloff */ - output[0] /= m_rasterMaskHandleTot; + output[0] /= rasterMaskHandleTot_; } } @@ -156,23 +151,23 @@ void MaskOperation::update_memory_buffer_partial(MemoryBuffer *output, float xy[2]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - xy[0] = it.x * m_maskWidthInv + m_mask_px_ofs[0]; - xy[1] = it.y * m_maskHeightInv + m_mask_px_ofs[1]; + xy[0] = it.x * maskWidthInv_ + mask_px_ofs_[0]; + xy[1] = it.y * maskHeightInv_ + mask_px_ofs_[1]; *it.out = 0.0f; for (MaskRasterHandle *handle : handles) { *it.out += BKE_maskrasterize_handle_sample(handle, xy); } /* Until we get better falloff. */ - *it.out /= m_rasterMaskHandleTot; + *it.out /= rasterMaskHandleTot_; } } Vector MaskOperation::get_non_null_handles() const { Vector handles; - for (int i = 0; i < m_rasterMaskHandleTot; i++) { - MaskRasterHandle *handle = m_rasterMaskHandles[i]; + for (int i = 0; i < rasterMaskHandleTot_; i++) { + MaskRasterHandle *handle = rasterMaskHandles_[i]; if (handle == nullptr) { continue; } diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h index 8ad3044b6cd..e8f386242b4 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.h +++ b/source/blender/compositor/operations/COM_MaskOperation.h @@ -33,23 +33,23 @@ namespace blender::compositor { */ class MaskOperation : public MultiThreadedOperation { protected: - Mask *m_mask; + Mask *mask_; /* NOTE: these are used more like aspect, * but they _do_ impact on mask detail */ - int m_maskWidth; - int m_maskHeight; - float m_maskWidthInv; /* `1 / m_maskWidth` */ - float m_maskHeightInv; /* `1 / m_maskHeight` */ - float m_mask_px_ofs[2]; + int maskWidth_; + int maskHeight_; + float maskWidthInv_; /* `1 / maskWidth_` */ + float maskHeightInv_; /* `1 / maskHeight_` */ + float mask_px_ofs_[2]; - float m_frame_shutter; - int m_frame_number; + float frame_shutter_; + int frame_number_; - bool m_do_feather; + bool do_feather_; - struct MaskRasterHandle *m_rasterMaskHandles[CMP_NODE_MASK_MBLUR_SAMPLES_MAX]; - unsigned int m_rasterMaskHandleTot; + struct MaskRasterHandle *rasterMaskHandles_[CMP_NODE_MASK_MBLUR_SAMPLES_MAX]; + unsigned int rasterMaskHandleTot_; /** * Determine the output resolution. The resolution is retrieved from the Renderer @@ -64,36 +64,36 @@ class MaskOperation : public MultiThreadedOperation { void setMask(Mask *mask) { - m_mask = mask; + mask_ = mask; } void setMaskWidth(int width) { - m_maskWidth = width; - m_maskWidthInv = 1.0f / (float)width; - m_mask_px_ofs[0] = m_maskWidthInv * 0.5f; + maskWidth_ = width; + maskWidthInv_ = 1.0f / (float)width; + mask_px_ofs_[0] = maskWidthInv_ * 0.5f; } void setMaskHeight(int height) { - m_maskHeight = height; - m_maskHeightInv = 1.0f / (float)height; - m_mask_px_ofs[1] = m_maskHeightInv * 0.5f; + maskHeight_ = height; + maskHeightInv_ = 1.0f / (float)height; + mask_px_ofs_[1] = maskHeightInv_ * 0.5f; } void setFramenumber(int frame_number) { - m_frame_number = frame_number; + frame_number_ = frame_number; } void setFeather(bool feather) { - m_do_feather = feather; + do_feather_ = feather; } void setMotionBlurSamples(int samples) { - m_rasterMaskHandleTot = MIN2(MAX2(1, samples), CMP_NODE_MASK_MBLUR_SAMPLES_MAX); + rasterMaskHandleTot_ = MIN2(MAX2(1, samples), CMP_NODE_MASK_MBLUR_SAMPLES_MAX); } void setMotionBlurShutter(float shutter) { - m_frame_shutter = shutter; + frame_shutter_ = shutter; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc index a16ecfc7327..30b6e14a324 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.cc +++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc @@ -28,25 +28,25 @@ MathBaseOperation::MathBaseOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_inputValue1Operation = nullptr; - m_inputValue2Operation = nullptr; - m_inputValue3Operation = nullptr; - m_useClamp = false; + inputValue1Operation_ = nullptr; + inputValue2Operation_ = nullptr; + inputValue3Operation_ = nullptr; + useClamp_ = false; this->flags.can_be_constant = true; } void MathBaseOperation::initExecution() { - m_inputValue1Operation = this->getInputSocketReader(0); - m_inputValue2Operation = this->getInputSocketReader(1); - m_inputValue3Operation = this->getInputSocketReader(2); + inputValue1Operation_ = this->getInputSocketReader(0); + inputValue2Operation_ = this->getInputSocketReader(1); + inputValue3Operation_ = this->getInputSocketReader(2); } void MathBaseOperation::deinitExecution() { - m_inputValue1Operation = nullptr; - m_inputValue2Operation = nullptr; - m_inputValue3Operation = nullptr; + inputValue1Operation_ = nullptr; + inputValue2Operation_ = nullptr; + inputValue3Operation_ = nullptr; } void MathBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -66,7 +66,7 @@ void MathBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_are void MathBaseOperation::clampIfNeeded(float *color) { - if (m_useClamp) { + if (useClamp_) { CLAMP(color[0], 0.0f, 1.0f); } } @@ -84,8 +84,8 @@ void MathAddOperation::executePixelSampled(float output[4], float x, float y, Pi float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = inputValue1[0] + inputValue2[0]; @@ -100,8 +100,8 @@ void MathSubtractOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = inputValue1[0] - inputValue2[0]; @@ -116,8 +116,8 @@ void MathMultiplyOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = inputValue1[0] * inputValue2[0]; @@ -132,8 +132,8 @@ void MathDivideOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); if (inputValue2[0] == 0) { /* We don't want to divide by zero. */ output[0] = 0.0; @@ -161,8 +161,8 @@ void MathSineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = sin(inputValue1[0]); @@ -185,8 +185,8 @@ void MathCosineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = cos(inputValue1[0]); @@ -209,8 +209,8 @@ void MathTangentOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = tan(inputValue1[0]); @@ -233,8 +233,8 @@ void MathHyperbolicSineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = sinh(inputValue1[0]); @@ -257,8 +257,8 @@ void MathHyperbolicCosineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = cosh(inputValue1[0]); @@ -281,8 +281,8 @@ void MathHyperbolicTangentOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = tanh(inputValue1[0]); @@ -305,8 +305,8 @@ void MathArcSineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); if (inputValue1[0] <= 1 && inputValue1[0] >= -1) { output[0] = asin(inputValue1[0]); @@ -334,8 +334,8 @@ void MathArcCosineOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); if (inputValue1[0] <= 1 && inputValue1[0] >= -1) { output[0] = acos(inputValue1[0]); @@ -363,8 +363,8 @@ void MathArcTangentOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = atan(inputValue1[0]); @@ -387,8 +387,8 @@ void MathPowerOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); if (inputValue1[0] >= 0) { output[0] = pow(inputValue1[0], inputValue2[0]); @@ -438,8 +438,8 @@ void MathLogarithmOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); if (inputValue1[0] > 0 && inputValue2[0] > 0) { output[0] = log(inputValue1[0]) / log(inputValue2[0]); @@ -474,8 +474,8 @@ void MathMinimumOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = MIN2(inputValue1[0], inputValue2[0]); @@ -498,8 +498,8 @@ void MathMaximumOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = MAX2(inputValue1[0], inputValue2[0]); @@ -522,8 +522,8 @@ void MathRoundOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = round(inputValue1[0]); @@ -546,8 +546,8 @@ void MathLessThanOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = inputValue1[0] < inputValue2[0] ? 1.0f : 0.0f; @@ -562,8 +562,8 @@ void MathGreaterThanOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = inputValue1[0] > inputValue2[0] ? 1.0f : 0.0f; @@ -578,8 +578,8 @@ void MathModuloOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); if (inputValue2[0] == 0) { output[0] = 0.0; @@ -607,7 +607,7 @@ void MathAbsoluteOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); output[0] = fabs(inputValue1[0]); @@ -629,7 +629,7 @@ void MathRadiansOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); output[0] = DEG2RADF(inputValue1[0]); @@ -651,7 +651,7 @@ void MathDegreesOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); output[0] = RAD2DEGF(inputValue1[0]); @@ -674,8 +674,8 @@ void MathArcTan2Operation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = atan2(inputValue1[0], inputValue2[0]); @@ -697,7 +697,7 @@ void MathFloorOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); output[0] = floor(inputValue1[0]); @@ -719,7 +719,7 @@ void MathCeilOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); output[0] = ceil(inputValue1[0]); @@ -741,7 +741,7 @@ void MathFractOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); output[0] = inputValue1[0] - floor(inputValue1[0]); @@ -763,7 +763,7 @@ void MathSqrtOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); if (inputValue1[0] > 0) { output[0] = sqrt(inputValue1[0]); @@ -790,7 +790,7 @@ void MathInverseSqrtOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); if (inputValue1[0] > 0) { output[0] = 1.0f / sqrt(inputValue1[0]); @@ -817,7 +817,7 @@ void MathSignOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); output[0] = compatible_signf(inputValue1[0]); @@ -839,7 +839,7 @@ void MathExponentOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); output[0] = expf(inputValue1[0]); @@ -861,7 +861,7 @@ void MathTruncOperation::executePixelSampled(float output[4], { float inputValue1[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); output[0] = (inputValue1[0] >= 0.0f) ? floor(inputValue1[0]) : ceil(inputValue1[0]); @@ -885,8 +885,8 @@ void MathSnapOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); if (inputValue1[0] == 0 || inputValue2[0] == 0) { /* We don't want to divide by zero. */ output[0] = 0.0f; @@ -922,9 +922,9 @@ void MathWrapOperation::executePixelSampled(float output[4], float inputValue2[4]; float inputValue3[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + inputValue3Operation_->readSampled(inputValue3, x, y, sampler); output[0] = wrapf(inputValue1[0], inputValue2[0], inputValue3[0]); @@ -947,8 +947,8 @@ void MathPingpongOperation::executePixelSampled(float output[4], float inputValue1[4]; float inputValue2[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); output[0] = pingpongf(inputValue1[0], inputValue2[0]); @@ -972,9 +972,9 @@ void MathCompareOperation::executePixelSampled(float output[4], float inputValue2[4]; float inputValue3[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + inputValue3Operation_->readSampled(inputValue3, x, y, sampler); output[0] = (fabsf(inputValue1[0] - inputValue2[0]) <= MAX2(inputValue3[0], 1e-5f)) ? 1.0f : 0.0f; @@ -999,9 +999,9 @@ void MathMultiplyAddOperation::executePixelSampled(float output[4], float inputValue2[4]; float inputValue3[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + inputValue3Operation_->readSampled(inputValue3, x, y, sampler); output[0] = inputValue1[0] * inputValue2[0] + inputValue3[0]; @@ -1025,9 +1025,9 @@ void MathSmoothMinOperation::executePixelSampled(float output[4], float inputValue2[4]; float inputValue3[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + inputValue3Operation_->readSampled(inputValue3, x, y, sampler); output[0] = smoothminf(inputValue1[0], inputValue2[0], inputValue3[0]); @@ -1051,9 +1051,9 @@ void MathSmoothMaxOperation::executePixelSampled(float output[4], float inputValue2[4]; float inputValue3[4]; - m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + inputValue3Operation_->readSampled(inputValue3, x, y, sampler); output[0] = -smoothminf(-inputValue1[0], -inputValue2[0], inputValue3[0]); diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h index 1424a7a9769..fcb735720b2 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.h +++ b/source/blender/compositor/operations/COM_MathBaseOperation.h @@ -31,11 +31,11 @@ class MathBaseOperation : public MultiThreadedOperation { /** * Prefetched reference to the inputProgram */ - SocketReader *m_inputValue1Operation; - SocketReader *m_inputValue2Operation; - SocketReader *m_inputValue3Operation; + SocketReader *inputValue1Operation_; + SocketReader *inputValue2Operation_; + SocketReader *inputValue3Operation_; - bool m_useClamp; + bool useClamp_; protected: /** @@ -48,7 +48,7 @@ class MathBaseOperation : public MultiThreadedOperation { float clamp_when_enabled(float value) { - if (m_useClamp) { + if (useClamp_) { return CLAMPIS(value, 0.0f, 1.0f); } return value; @@ -56,7 +56,7 @@ class MathBaseOperation : public MultiThreadedOperation { void clamp_when_enabled(float *out) { - if (m_useClamp) { + if (useClamp_) { CLAMP(*out, 0.0f, 1.0f); } } @@ -79,7 +79,7 @@ class MathBaseOperation : public MultiThreadedOperation { void setUseClamp(bool value) { - m_useClamp = value; + useClamp_ = value; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc index d7a8d0c0e8e..216dbc3a701 100644 --- a/source/blender/compositor/operations/COM_MixOperation.cc +++ b/source/blender/compositor/operations/COM_MixOperation.cc @@ -28,9 +28,9 @@ MixBaseOperation::MixBaseOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_inputValueOperation = nullptr; - m_inputColor1Operation = nullptr; - m_inputColor2Operation = nullptr; + inputValueOperation_ = nullptr; + inputColor1Operation_ = nullptr; + inputColor2Operation_ = nullptr; this->setUseValueAlphaMultiply(false); this->setUseClamp(false); flags.can_be_constant = true; @@ -38,9 +38,9 @@ MixBaseOperation::MixBaseOperation() void MixBaseOperation::initExecution() { - m_inputValueOperation = this->getInputSocketReader(0); - m_inputColor1Operation = this->getInputSocketReader(1); - m_inputColor2Operation = this->getInputSocketReader(2); + inputValueOperation_ = this->getInputSocketReader(0); + inputColor1Operation_ = this->getInputSocketReader(1); + inputColor2Operation_ = this->getInputSocketReader(2); } void MixBaseOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -49,9 +49,9 @@ void MixBaseOperation::executePixelSampled(float output[4], float x, float y, Pi float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -89,9 +89,9 @@ void MixBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area void MixBaseOperation::deinitExecution() { - m_inputValueOperation = nullptr; - m_inputColor1Operation = nullptr; - m_inputColor2Operation = nullptr; + inputValueOperation_ = nullptr; + inputColor1Operation_ = nullptr; + inputColor2Operation_ = nullptr; } void MixBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -141,9 +141,9 @@ void MixAddOperation::executePixelSampled(float output[4], float x, float y, Pix float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -186,9 +186,9 @@ void MixBlendOperation::executePixelSampled(float output[4], float inputValue[4]; float value; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -233,9 +233,9 @@ void MixColorBurnOperation::executePixelSampled(float output[4], float inputValue[4]; float tmp; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -352,9 +352,9 @@ void MixColorOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -422,9 +422,9 @@ void MixDarkenOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -468,9 +468,9 @@ void MixDifferenceOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -514,9 +514,9 @@ void MixDivideOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -595,9 +595,9 @@ void MixDodgeOperation::executePixelSampled(float output[4], float inputValue[4]; float tmp; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -736,9 +736,9 @@ void MixGlareOperation::executePixelSampled(float output[4], float inputValue[4]; float value, input_weight, glare_weight; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); value = inputValue[0]; /* Linear interpolation between 3 cases: * value=-1:output=input value=0:output=input+glare value=1:output=glare @@ -794,9 +794,9 @@ void MixHueOperation::executePixelSampled(float output[4], float x, float y, Pix float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -864,9 +864,9 @@ void MixLightenOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -934,9 +934,9 @@ void MixLinearLightOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1010,9 +1010,9 @@ void MixMultiplyOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1057,9 +1057,9 @@ void MixOverlayOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1136,9 +1136,9 @@ void MixSaturationOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1200,9 +1200,9 @@ void MixScreenOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1248,9 +1248,9 @@ void MixSoftLightOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1317,9 +1317,9 @@ void MixSubtractOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { @@ -1361,9 +1361,9 @@ void MixValueOperation::executePixelSampled(float output[4], float inputColor2[4]; float inputValue[4]; - m_inputValueOperation->readSampled(inputValue, x, y, sampler); - m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + inputValueOperation_->readSampled(inputValue, x, y, sampler); + inputColor1Operation_->readSampled(inputColor1, x, y, sampler); + inputColor2Operation_->readSampled(inputColor2, x, y, sampler); float value = inputValue[0]; if (this->useValueAlphaMultiply()) { diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h index 5d96aa4f5d7..0efc98617a6 100644 --- a/source/blender/compositor/operations/COM_MixOperation.h +++ b/source/blender/compositor/operations/COM_MixOperation.h @@ -53,15 +53,15 @@ class MixBaseOperation : public MultiThreadedOperation { /** * Prefetched reference to the inputProgram */ - SocketReader *m_inputValueOperation; - SocketReader *m_inputColor1Operation; - SocketReader *m_inputColor2Operation; - bool m_valueAlphaMultiply; - bool m_useClamp; + SocketReader *inputValueOperation_; + SocketReader *inputColor1Operation_; + SocketReader *inputColor2Operation_; + bool valueAlphaMultiply_; + bool useClamp_; inline void clampIfNeeded(float color[4]) { - if (m_useClamp) { + if (useClamp_) { clamp_v4(color, 0.0f, 1.0f); } } @@ -91,15 +91,15 @@ class MixBaseOperation : public MultiThreadedOperation { void setUseValueAlphaMultiply(const bool value) { - m_valueAlphaMultiply = value; + valueAlphaMultiply_ = value; } inline bool useValueAlphaMultiply() { - return m_valueAlphaMultiply; + return valueAlphaMultiply_; } void setUseClamp(bool value) { - m_useClamp = value; + useClamp_ = value; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc index e62c126812f..c43a552f1ce 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc @@ -26,9 +26,9 @@ namespace blender::compositor { MovieClipAttributeOperation::MovieClipAttributeOperation() { this->addOutputSocket(DataType::Value); - m_framenumber = 0; - m_attribute = MCA_X; - m_invert = false; + framenumber_ = 0; + attribute_ = MCA_X; + invert_ = false; needs_canvas_to_get_constant_ = true; is_value_calculated_ = false; stabilization_resolution_socket_ = nullptr; @@ -45,7 +45,7 @@ void MovieClipAttributeOperation::calc_value() { BLI_assert(this->get_flags().is_canvas_set); is_value_calculated_ = true; - if (m_clip == nullptr) { + if (clip_ == nullptr) { return; } float loc[2], scale, angle; @@ -53,38 +53,38 @@ void MovieClipAttributeOperation::calc_value() loc[1] = 0.0f; scale = 1.0f; angle = 0.0f; - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(m_clip, m_framenumber); + int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip_, framenumber_); NodeOperation &stabilization_operation = stabilization_resolution_socket_ ? stabilization_resolution_socket_->getLink()->getOperation() : *this; - BKE_tracking_stabilization_data_get(m_clip, + BKE_tracking_stabilization_data_get(clip_, clip_framenr, stabilization_operation.getWidth(), stabilization_operation.getHeight(), loc, &scale, &angle); - switch (m_attribute) { + switch (attribute_) { case MCA_SCALE: - m_value = scale; + value_ = scale; break; case MCA_ANGLE: - m_value = angle; + value_ = angle; break; case MCA_X: - m_value = loc[0]; + value_ = loc[0]; break; case MCA_Y: - m_value = loc[1]; + value_ = loc[1]; break; } - if (m_invert) { - if (m_attribute != MCA_SCALE) { - m_value = -m_value; + if (invert_) { + if (attribute_ != MCA_SCALE) { + value_ = -value_; } else { - m_value = 1.0f / m_value; + value_ = 1.0f / value_; } } } @@ -94,7 +94,7 @@ void MovieClipAttributeOperation::executePixelSampled(float output[4], float /*y*/, PixelSampler /*sampler*/) { - output[0] = m_value; + output[0] = value_; } void MovieClipAttributeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -107,7 +107,7 @@ const float *MovieClipAttributeOperation::get_constant_elem() if (!is_value_calculated_) { calc_value(); } - return &m_value; + return &value_; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h index 640d578abb0..2df2b199107 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h @@ -36,11 +36,11 @@ typedef enum MovieClipAttribute { */ class MovieClipAttributeOperation : public ConstantOperation { private: - MovieClip *m_clip; - float m_value; - int m_framenumber; - bool m_invert; - MovieClipAttribute m_attribute; + MovieClip *clip_; + float value_; + int framenumber_; + bool invert_; + MovieClipAttribute attribute_; bool is_value_calculated_; NodeOperationInput *stabilization_resolution_socket_; @@ -62,19 +62,19 @@ class MovieClipAttributeOperation : public ConstantOperation { void setMovieClip(MovieClip *clip) { - m_clip = clip; + clip_ = clip; } void setFramenumber(int framenumber) { - m_framenumber = framenumber; + framenumber_ = framenumber; } void setAttribute(MovieClipAttribute attribute) { - m_attribute = attribute; + attribute_ = attribute; } void setInvert(bool invert) { - m_invert = invert; + invert_ = invert; } /** diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc index aac263142d7..2803c707371 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc @@ -27,30 +27,30 @@ namespace blender::compositor { MovieClipBaseOperation::MovieClipBaseOperation() { - m_movieClip = nullptr; - m_movieClipBuffer = nullptr; - m_movieClipUser = nullptr; - m_movieClipwidth = 0; - m_movieClipheight = 0; - m_framenumber = 0; + movieClip_ = nullptr; + movieClipBuffer_ = nullptr; + movieClipUser_ = nullptr; + movieClipwidth_ = 0; + movieClipheight_ = 0; + framenumber_ = 0; } void MovieClipBaseOperation::initExecution() { - if (m_movieClip) { - BKE_movieclip_user_set_frame(m_movieClipUser, m_framenumber); + if (movieClip_) { + BKE_movieclip_user_set_frame(movieClipUser_, framenumber_); ImBuf *ibuf; - if (m_cacheFrame) { - ibuf = BKE_movieclip_get_ibuf(m_movieClip, m_movieClipUser); + if (cacheFrame_) { + ibuf = BKE_movieclip_get_ibuf(movieClip_, movieClipUser_); } else { ibuf = BKE_movieclip_get_ibuf_flag( - m_movieClip, m_movieClipUser, m_movieClip->flag, MOVIECLIP_CACHE_SKIP); + movieClip_, movieClipUser_, movieClip_->flag, MOVIECLIP_CACHE_SKIP); } if (ibuf) { - m_movieClipBuffer = ibuf; + movieClipBuffer_ = ibuf; if (ibuf->rect_float == nullptr || ibuf->userflags & IB_RECT_INVALID) { IMB_float_from_rect(ibuf); ibuf->userflags &= ~IB_RECT_INVALID; @@ -61,19 +61,19 @@ void MovieClipBaseOperation::initExecution() void MovieClipBaseOperation::deinitExecution() { - if (m_movieClipBuffer) { - IMB_freeImBuf(m_movieClipBuffer); + if (movieClipBuffer_) { + IMB_freeImBuf(movieClipBuffer_); - m_movieClipBuffer = nullptr; + movieClipBuffer_ = nullptr; } } void MovieClipBaseOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { r_area = COM_AREA_NONE; - if (m_movieClip) { + if (movieClip_) { int width, height; - BKE_movieclip_get_size(m_movieClip, m_movieClipUser, &width, &height); + BKE_movieclip_get_size(movieClip_, movieClipUser_, &width, &height); BLI_rcti_init(&r_area, 0, width, 0, height); } } @@ -83,7 +83,7 @@ void MovieClipBaseOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - ImBuf *ibuf = m_movieClipBuffer; + ImBuf *ibuf = movieClipBuffer_; if (ibuf == nullptr) { zero_v4(output); @@ -111,8 +111,8 @@ void MovieClipBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - if (m_movieClipBuffer) { - output->copy_from(m_movieClipBuffer, area); + if (movieClipBuffer_) { + output->copy_from(movieClipBuffer_, area); } else { output->fill(area, COM_COLOR_TRANSPARENT); @@ -143,8 +143,8 @@ void MovieClipAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - if (m_movieClipBuffer) { - output->copy_from(m_movieClipBuffer, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); + if (movieClipBuffer_) { + output->copy_from(movieClipBuffer_, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); } else { output->fill(area, COM_VALUE_ZERO); diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.h b/source/blender/compositor/operations/COM_MovieClipOperation.h index a319c2116b7..58cebba45d7 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipOperation.h @@ -30,13 +30,13 @@ namespace blender::compositor { */ class MovieClipBaseOperation : public MultiThreadedOperation { protected: - MovieClip *m_movieClip; - MovieClipUser *m_movieClipUser; - ImBuf *m_movieClipBuffer; - int m_movieClipheight; - int m_movieClipwidth; - int m_framenumber; - bool m_cacheFrame; + MovieClip *movieClip_; + MovieClipUser *movieClipUser_; + ImBuf *movieClipBuffer_; + int movieClipheight_; + int movieClipwidth_; + int framenumber_; + bool cacheFrame_; /** * Determine the output resolution. The resolution is retrieved from the Renderer @@ -50,20 +50,20 @@ class MovieClipBaseOperation : public MultiThreadedOperation { void deinitExecution() override; void setMovieClip(MovieClip *image) { - m_movieClip = image; + movieClip_ = image; } void setMovieClipUser(MovieClipUser *imageuser) { - m_movieClipUser = imageuser; + movieClipUser_ = imageuser; } void setCacheFrame(bool value) { - m_cacheFrame = value; + cacheFrame_ = value; } void setFramenumber(int framenumber) { - m_framenumber = framenumber; + framenumber_ = framenumber; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc index ec17c2d030f..0cb0742dbee 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc @@ -27,20 +27,20 @@ MovieDistortionOperation::MovieDistortionOperation(bool distortion) this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - m_inputOperation = nullptr; - m_movieClip = nullptr; - m_apply = distortion; + inputOperation_ = nullptr; + movieClip_ = nullptr; + apply_ = distortion; } void MovieDistortionOperation::init_data() { - if (m_movieClip) { - MovieTracking *tracking = &m_movieClip->tracking; + if (movieClip_) { + MovieTracking *tracking = &movieClip_->tracking; MovieClipUser clipUser = {0}; int calibration_width, calibration_height; - BKE_movieclip_user_set_frame(&clipUser, m_framenumber); - BKE_movieclip_get_size(m_movieClip, &clipUser, &calibration_width, &calibration_height); + BKE_movieclip_user_set_frame(&clipUser, framenumber_); + BKE_movieclip_get_size(movieClip_, &clipUser, &calibration_width, &calibration_height); float delta[2]; rcti full_frame; @@ -48,42 +48,41 @@ void MovieDistortionOperation::init_data() full_frame.xmax = this->getWidth(); full_frame.ymax = this->getHeight(); BKE_tracking_max_distortion_delta_across_bound( - tracking, this->getWidth(), this->getHeight(), &full_frame, !m_apply, delta); + tracking, this->getWidth(), this->getHeight(), &full_frame, !apply_, delta); /* 5 is just in case we didn't hit real max of distortion in * BKE_tracking_max_undistortion_delta_across_bound */ - m_margin[0] = delta[0] + 5; - m_margin[1] = delta[1] + 5; + margin_[0] = delta[0] + 5; + margin_[1] = delta[1] + 5; - m_calibration_width = calibration_width; - m_calibration_height = calibration_height; - m_pixel_aspect = tracking->camera.pixel_aspect; + calibration_width_ = calibration_width; + calibration_height_ = calibration_height; + pixel_aspect_ = tracking->camera.pixel_aspect; } else { - m_margin[0] = m_margin[1] = 0; + margin_[0] = margin_[1] = 0; } } void MovieDistortionOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); - if (m_movieClip) { - MovieTracking *tracking = &m_movieClip->tracking; - m_distortion = BKE_tracking_distortion_new( - tracking, m_calibration_width, m_calibration_height); + inputOperation_ = this->getInputSocketReader(0); + if (movieClip_) { + MovieTracking *tracking = &movieClip_->tracking; + distortion_ = BKE_tracking_distortion_new(tracking, calibration_width_, calibration_height_); } else { - m_distortion = nullptr; + distortion_ = nullptr; } } void MovieDistortionOperation::deinitExecution() { - m_inputOperation = nullptr; - m_movieClip = nullptr; - if (m_distortion != nullptr) { - BKE_tracking_distortion_free(m_distortion); + inputOperation_ = nullptr; + movieClip_ = nullptr; + if (distortion_ != nullptr) { + BKE_tracking_distortion_free(distortion_); } } @@ -92,33 +91,33 @@ void MovieDistortionOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - if (m_distortion != nullptr) { + if (distortion_ != nullptr) { /* float overscan = 0.0f; */ - const float pixel_aspect = m_pixel_aspect; + const float pixel_aspect = pixel_aspect_; const float w = (float)this->getWidth() /* / (1 + overscan) */; const float h = (float)this->getHeight() /* / (1 + overscan) */; - const float aspx = w / (float)m_calibration_width; - const float aspy = h / (float)m_calibration_height; + const float aspx = w / (float)calibration_width_; + const float aspy = h / (float)calibration_height_; float in[2]; float out[2]; in[0] = (x /* - 0.5 * overscan * w */) / aspx; in[1] = (y /* - 0.5 * overscan * h */) / aspy / pixel_aspect; - if (m_apply) { - BKE_tracking_distortion_undistort_v2(m_distortion, in, out); + if (apply_) { + BKE_tracking_distortion_undistort_v2(distortion_, in, out); } else { - BKE_tracking_distortion_distort_v2(m_distortion, in, out); + BKE_tracking_distortion_distort_v2(distortion_, in, out); } float u = out[0] * aspx /* + 0.5 * overscan * w */, v = (out[1] * aspy /* + 0.5 * overscan * h */) * pixel_aspect; - m_inputOperation->readSampled(output, u, v, PixelSampler::Bilinear); + inputOperation_->readSampled(output, u, v, PixelSampler::Bilinear); } else { - m_inputOperation->readSampled(output, x, y, PixelSampler::Bilinear); + inputOperation_->readSampled(output, x, y, PixelSampler::Bilinear); } } @@ -127,10 +126,10 @@ bool MovieDistortionOperation::determineDependingAreaOfInterest(rcti *input, rcti *output) { rcti newInput; - newInput.xmin = input->xmin - m_margin[0]; - newInput.ymin = input->ymin - m_margin[1]; - newInput.xmax = input->xmax + m_margin[0]; - newInput.ymax = input->ymax + m_margin[1]; + newInput.xmin = input->xmin - margin_[0]; + newInput.ymin = input->ymin - margin_[1]; + newInput.xmax = input->xmax + margin_[0]; + newInput.ymax = input->ymax + margin_[1]; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -140,10 +139,10 @@ void MovieDistortionOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - m_margin[0]; - r_input_area.ymin = output_area.ymin - m_margin[1]; - r_input_area.xmax = output_area.xmax + m_margin[0]; - r_input_area.ymax = output_area.ymax + m_margin[1]; + r_input_area.xmin = output_area.xmin - margin_[0]; + r_input_area.ymin = output_area.ymin - margin_[1]; + r_input_area.xmax = output_area.xmax + margin_[0]; + r_input_area.ymax = output_area.ymax + margin_[1]; } void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -151,28 +150,28 @@ void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output Span inputs) { const MemoryBuffer *input_img = inputs[0]; - if (m_distortion == nullptr) { + if (distortion_ == nullptr) { output->copy_from(input_img, area); return; } /* `float overscan = 0.0f;` */ - const float pixel_aspect = m_pixel_aspect; + const float pixel_aspect = pixel_aspect_; const float w = (float)this->getWidth() /* `/ (1 + overscan)` */; const float h = (float)this->getHeight() /* `/ (1 + overscan)` */; - const float aspx = w / (float)m_calibration_width; - const float aspy = h / (float)m_calibration_height; + const float aspx = w / (float)calibration_width_; + const float aspy = h / (float)calibration_height_; float xy[2]; float distorted_xy[2]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { xy[0] = (it.x /* `- 0.5 * overscan * w` */) / aspx; xy[1] = (it.y /* `- 0.5 * overscan * h` */) / aspy / pixel_aspect; - if (m_apply) { - BKE_tracking_distortion_undistort_v2(m_distortion, xy, distorted_xy); + if (apply_) { + BKE_tracking_distortion_undistort_v2(distortion_, xy, distorted_xy); } else { - BKE_tracking_distortion_distort_v2(m_distortion, xy, distorted_xy); + BKE_tracking_distortion_distort_v2(distortion_, xy, distorted_xy); } const float u = distorted_xy[0] * aspx /* `+ 0.5 * overscan * w` */; diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h index a3630504442..3e728962bc3 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h @@ -28,17 +28,17 @@ namespace blender::compositor { class MovieDistortionOperation : public MultiThreadedOperation { private: - SocketReader *m_inputOperation; - MovieClip *m_movieClip; - int m_margin[2]; + SocketReader *inputOperation_; + MovieClip *movieClip_; + int margin_[2]; protected: - bool m_apply; - int m_framenumber; + bool apply_; + int framenumber_; - struct MovieDistortion *m_distortion; - int m_calibration_width, m_calibration_height; - float m_pixel_aspect; + struct MovieDistortion *distortion_; + int calibration_width_, calibration_height_; + float pixel_aspect_; public: MovieDistortionOperation(bool distortion); @@ -50,11 +50,11 @@ class MovieDistortionOperation : public MultiThreadedOperation { void setMovieClip(MovieClip *clip) { - m_movieClip = clip; + movieClip_ = clip; } void setFramenumber(int framenumber) { - m_framenumber = framenumber; + framenumber_ = framenumber; } bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc index 20cea6ad659..0209ea0e18c 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc @@ -26,27 +26,27 @@ MultilayerBaseOperation::MultilayerBaseOperation(RenderLayer *render_layer, RenderPass *render_pass, int view) { - m_passId = BLI_findindex(&render_layer->passes, render_pass); - m_view = view; - m_renderLayer = render_layer; - m_renderPass = render_pass; + passId_ = BLI_findindex(&render_layer->passes, render_pass); + view_ = view; + renderLayer_ = render_layer; + renderPass_ = render_pass; } ImBuf *MultilayerBaseOperation::getImBuf() { /* temporarily changes the view to get the right ImBuf */ - int view = m_imageUser->view; + int view = imageUser_->view; - m_imageUser->view = m_view; - m_imageUser->pass = m_passId; + imageUser_->view = view_; + imageUser_->pass = passId_; - if (BKE_image_multilayer_index(m_image->rr, m_imageUser)) { + if (BKE_image_multilayer_index(image_->rr, imageUser_)) { ImBuf *ibuf = BaseImageOperation::getImBuf(); - m_imageUser->view = view; + imageUser_->view = view; return ibuf; } - m_imageUser->view = view; + imageUser_->view = view; return nullptr; } @@ -54,17 +54,17 @@ void MultilayerBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->copy_from(m_buffer, area); + output->copy_from(buffer_, area); } std::unique_ptr MultilayerColorOperation::getMetaData() { - BLI_assert(m_buffer); + BLI_assert(buffer_); MetaDataExtractCallbackData callback_data = {nullptr}; - RenderResult *render_result = m_image->rr; + RenderResult *render_result = image_->rr; if (render_result && render_result->stamp_data) { - RenderLayer *render_layer = m_renderLayer; - RenderPass *render_pass = m_renderPass; + RenderLayer *render_layer = renderLayer_; + RenderPass *render_pass = renderPass_; std::string full_layer_name = std::string(render_layer->name, BLI_strnlen(render_layer->name, sizeof(render_layer->name))) + @@ -88,20 +88,20 @@ void MultilayerColorOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - if (m_imageFloatBuffer == nullptr) { + if (imageFloatBuffer_ == nullptr) { zero_v4(output); } else { - if (m_numberOfChannels == 4) { + if (numberOfChannels_ == 4) { switch (sampler) { case PixelSampler::Nearest: - nearest_interpolation_color(m_buffer, nullptr, output, x, y); + nearest_interpolation_color(buffer_, nullptr, output, x, y); break; case PixelSampler::Bilinear: - bilinear_interpolation_color(m_buffer, nullptr, output, x, y); + bilinear_interpolation_color(buffer_, nullptr, output, x, y); break; case PixelSampler::Bicubic: - bicubic_interpolation_color(m_buffer, nullptr, output, x, y); + bicubic_interpolation_color(buffer_, nullptr, output, x, y); break; } } @@ -114,7 +114,7 @@ void MultilayerColorOperation::executePixelSampled(float output[4], } else { int offset = (yi * this->getWidth() + xi) * 3; - copy_v3_v3(output, &m_imageFloatBuffer[offset]); + copy_v3_v3(output, &imageFloatBuffer_[offset]); } } } @@ -125,7 +125,7 @@ void MultilayerValueOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - if (m_imageFloatBuffer == nullptr) { + if (imageFloatBuffer_ == nullptr) { output[0] = 0.0f; } else { @@ -136,7 +136,7 @@ void MultilayerValueOperation::executePixelSampled(float output[4], output[0] = 0.0f; } else { - float result = m_imageFloatBuffer[yi * this->getWidth() + xi]; + float result = imageFloatBuffer_[yi * this->getWidth() + xi]; output[0] = result; } } @@ -147,7 +147,7 @@ void MultilayerVectorOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - if (m_imageFloatBuffer == nullptr) { + if (imageFloatBuffer_ == nullptr) { output[0] = 0.0f; } else { @@ -159,7 +159,7 @@ void MultilayerVectorOperation::executePixelSampled(float output[4], } else { int offset = (yi * this->getWidth() + xi) * 3; - copy_v3_v3(output, &m_imageFloatBuffer[offset]); + copy_v3_v3(output, &imageFloatBuffer_[offset]); } } } diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.h b/source/blender/compositor/operations/COM_MultilayerImageOperation.h index a682ca1941c..77805decc1f 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.h +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.h @@ -24,12 +24,12 @@ namespace blender::compositor { class MultilayerBaseOperation : public BaseImageOperation { private: - int m_passId; - int m_view; + int passId_; + int view_; protected: - RenderLayer *m_renderLayer; - RenderPass *m_renderPass; + RenderLayer *renderLayer_; + RenderPass *renderPass_; ImBuf *getImBuf() override; public: diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc index f3388397a79..0c2dc309ede 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.cc +++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc @@ -24,14 +24,14 @@ NormalizeOperation::NormalizeOperation() { this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); - m_imageReader = nullptr; - m_cachedInstance = nullptr; + imageReader_ = nullptr; + cachedInstance_ = nullptr; this->flags.complex = true; flags.can_be_constant = true; } void NormalizeOperation::initExecution() { - m_imageReader = this->getInputSocketReader(0); + imageReader_ = this->getInputSocketReader(0); NodeOperation::initMutex(); } @@ -40,7 +40,7 @@ void NormalizeOperation::executePixel(float output[4], int x, int y, void *data) /* using generic two floats struct to store `x: min`, `y: multiply` */ NodeTwoFloats *minmult = (NodeTwoFloats *)data; - m_imageReader->read(output, x, y, nullptr); + imageReader_->read(output, x, y, nullptr); output[0] = (output[0] - minmult->x) * minmult->y; @@ -55,9 +55,9 @@ void NormalizeOperation::executePixel(float output[4], int x, int y, void *data) void NormalizeOperation::deinitExecution() { - m_imageReader = nullptr; - delete m_cachedInstance; - m_cachedInstance = nullptr; + imageReader_ = nullptr; + delete cachedInstance_; + cachedInstance_ = nullptr; NodeOperation::deinitMutex(); } @@ -66,7 +66,7 @@ bool NormalizeOperation::determineDependingAreaOfInterest(rcti * /*input*/, rcti *output) { rcti imageInput; - if (m_cachedInstance) { + if (cachedInstance_) { return false; } @@ -89,8 +89,8 @@ bool NormalizeOperation::determineDependingAreaOfInterest(rcti * /*input*/, void *NormalizeOperation::initializeTileData(rcti *rect) { lockMutex(); - if (m_cachedInstance == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)m_imageReader->initializeTileData(rect); + if (cachedInstance_ == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)imageReader_->initializeTileData(rect); /* using generic two floats struct to store `x: min`, `y: multiply`. */ NodeTwoFloats *minmult = new NodeTwoFloats(); @@ -117,11 +117,11 @@ void *NormalizeOperation::initializeTileData(rcti *rect) /* The rare case of flat buffer would cause a divide by 0 */ minmult->y = ((maxv != minv) ? 1.0f / (maxv - minv) : 0.0f); - m_cachedInstance = minmult; + cachedInstance_ = minmult; } unlockMutex(); - return m_cachedInstance; + return cachedInstance_; } void NormalizeOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) @@ -140,7 +140,7 @@ void NormalizeOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(outpu const rcti &UNUSED(area), Span inputs) { - if (m_cachedInstance == nullptr) { + if (cachedInstance_ == nullptr) { MemoryBuffer *input = inputs[0]; /* Using generic two floats struct to store `x: min`, `y: multiply`. */ @@ -162,7 +162,7 @@ void NormalizeOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(outpu /* The case of a flat buffer would cause a divide by 0. */ minmult->y = ((maxv != minv) ? 1.0f / (maxv - minv) : 0.0f); - m_cachedInstance = minmult; + cachedInstance_ = minmult; } } @@ -170,7 +170,7 @@ void NormalizeOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - NodeTwoFloats *minmult = m_cachedInstance; + NodeTwoFloats *minmult = cachedInstance_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float input_value = *it.in(0); diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.h b/source/blender/compositor/operations/COM_NormalizeOperation.h index 7af2aad8a88..450155c319a 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.h +++ b/source/blender/compositor/operations/COM_NormalizeOperation.h @@ -32,13 +32,13 @@ class NormalizeOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *m_imageReader; + SocketReader *imageReader_; /** * \brief temporarily cache of the execution storage * it stores `x->min` and `y->multiply`. */ - NodeTwoFloats *m_cachedInstance; + NodeTwoFloats *cachedInstance_; public: NormalizeOperation(); diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index 1a49d83af54..e652a0be3e6 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -56,26 +56,26 @@ void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filenam exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(m_rd, m_viewName)) { + if (!BKE_scene_multiview_is_render_view_first(rd_, viewName_)) { return exrhandle; } IMB_exr_clear_channels(exrhandle); - for (srv = (SceneRenderView *)m_rd->views.first; srv; srv = srv->next) { - if (BKE_scene_multiview_is_render_view_active(m_rd, srv) == false) { + for (srv = (SceneRenderView *)rd_->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd_, srv) == false) { continue; } IMB_exr_add_view(exrhandle, srv->name); - add_exr_channels(exrhandle, nullptr, m_datatype, srv->name, width, false, nullptr); + add_exr_channels(exrhandle, nullptr, datatype_, srv->name, width, false, nullptr); } BLI_make_existing_file(filename); /* prepare the file with all the channels */ - if (!IMB_exr_begin_write(exrhandle, filename, width, height, m_format->exr_codec, nullptr)) { + if (!IMB_exr_begin_write(exrhandle, filename, width, height, format_->exr_codec, nullptr)) { printf("Error Writing Singlelayer Multiview Openexr\n"); IMB_exr_close(exrhandle); } @@ -97,33 +97,33 @@ void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution() char filename[FILE_MAX]; BKE_image_path_from_imtype(filename, - m_path, + path_, BKE_main_blendfile_path_from_global(), - m_rd->cfra, + rd_->cfra, R_IMF_IMTYPE_OPENEXR, - (m_rd->scemode & R_EXTENSION) != 0, + (rd_->scemode & R_EXTENSION) != 0, true, nullptr); exrhandle = this->get_handle(filename); add_exr_channels(exrhandle, nullptr, - m_datatype, - m_viewName, + datatype_, + viewName_, width, - m_format->depth == R_IMF_CHAN_DEPTH_16, - m_outputBuffer); + format_->depth == R_IMF_CHAN_DEPTH_16, + outputBuffer_); /* memory can only be freed after we write all views to the file */ - m_outputBuffer = nullptr; - m_imageInput = nullptr; + outputBuffer_ = nullptr; + imageInput_ = nullptr; /* ready to close the file */ - if (BKE_scene_multiview_is_render_view_last(m_rd, m_viewName)) { + if (BKE_scene_multiview_is_render_view_last(rd_, viewName_)) { IMB_exr_write_channels(exrhandle); /* free buffer memory for all the views */ - free_exr_channels(exrhandle, m_rd, nullptr, m_datatype); + free_exr_channels(exrhandle, rd_, nullptr, datatype_); /* remove exr handle and data */ IMB_exr_close(exrhandle); @@ -158,28 +158,28 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename /* get a new global handle */ exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(m_rd, m_viewName)) { + if (!BKE_scene_multiview_is_render_view_first(rd_, viewName_)) { return exrhandle; } IMB_exr_clear_channels(exrhandle); /* check renderdata for amount of views */ - for (srv = (SceneRenderView *)m_rd->views.first; srv; srv = srv->next) { + for (srv = (SceneRenderView *)rd_->views.first; srv; srv = srv->next) { - if (BKE_scene_multiview_is_render_view_active(m_rd, srv) == false) { + if (BKE_scene_multiview_is_render_view_active(rd_, srv) == false) { continue; } IMB_exr_add_view(exrhandle, srv->name); - for (unsigned int i = 0; i < m_layers.size(); i++) { + for (unsigned int i = 0; i < layers_.size(); i++) { add_exr_channels(exrhandle, - m_layers[i].name, - m_layers[i].datatype, + layers_[i].name, + layers_[i].datatype, srv->name, width, - m_exr_half_float, + exr_half_float_, nullptr); } } @@ -188,7 +188,7 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename /* prepare the file with all the channels for the header */ StampData *stamp_data = createStampData(); - if (!IMB_exr_begin_write(exrhandle, filename, width, height, m_exr_codec, stamp_data)) { + if (!IMB_exr_begin_write(exrhandle, filename, width, height, exr_codec_, stamp_data)) { printf("Error Writing Multilayer Multiview Openexr\n"); IMB_exr_close(exrhandle); BKE_stamp_data_free(stamp_data); @@ -212,39 +212,39 @@ void OutputOpenExrMultiLayerMultiViewOperation::deinitExecution() char filename[FILE_MAX]; BKE_image_path_from_imtype(filename, - m_path, + path_, BKE_main_blendfile_path_from_global(), - m_rd->cfra, + rd_->cfra, R_IMF_IMTYPE_MULTILAYER, - (m_rd->scemode & R_EXTENSION) != 0, + (rd_->scemode & R_EXTENSION) != 0, true, nullptr); exrhandle = this->get_handle(filename); - for (unsigned int i = 0; i < m_layers.size(); i++) { + for (unsigned int i = 0; i < layers_.size(); i++) { add_exr_channels(exrhandle, - m_layers[i].name, - m_layers[i].datatype, - m_viewName, + layers_[i].name, + layers_[i].datatype, + viewName_, width, - m_exr_half_float, - m_layers[i].outputBuffer); + exr_half_float_, + layers_[i].outputBuffer); } - for (unsigned int i = 0; i < m_layers.size(); i++) { + for (unsigned int i = 0; i < layers_.size(); i++) { /* memory can only be freed after we write all views to the file */ - m_layers[i].outputBuffer = nullptr; - m_layers[i].imageInput = nullptr; + layers_[i].outputBuffer = nullptr; + layers_[i].imageInput = nullptr; } /* ready to close the file */ - if (BKE_scene_multiview_is_render_view_last(m_rd, m_viewName)) { + if (BKE_scene_multiview_is_render_view_last(rd_, viewName_)) { IMB_exr_write_channels(exrhandle); /* free buffer memory for all the views */ - for (unsigned int i = 0; i < m_layers.size(); i++) { - free_exr_channels(exrhandle, m_rd, m_layers[i].name, m_layers[i].datatype); + for (unsigned int i = 0; i < layers_.size(); i++) { + free_exr_channels(exrhandle, rd_, layers_[i].name, layers_[i].datatype); } IMB_exr_close(exrhandle); @@ -267,8 +267,8 @@ OutputStereoOperation::OutputStereoOperation(const RenderData *rd, : OutputSingleLayerOperation( rd, tree, datatype, format, path, viewSettings, displaySettings, viewName, saveAsRender) { - BLI_strncpy(m_name, name, sizeof(m_name)); - m_channels = get_datatype_size(datatype); + BLI_strncpy(name_, name, sizeof(name_)); + channels_ = get_datatype_size(datatype); } void *OutputStereoOperation::get_handle(const char *filename) @@ -283,7 +283,7 @@ void *OutputStereoOperation::get_handle(const char *filename) exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(m_rd, m_viewName)) { + if (!BKE_scene_multiview_is_render_view_first(rd_, viewName_)) { return exrhandle; } @@ -306,24 +306,24 @@ void OutputStereoOperation::deinitExecution() if (width != 0 && height != 0) { void *exrhandle; - exrhandle = this->get_handle(m_path); - float *buf = m_outputBuffer; + exrhandle = this->get_handle(path_); + float *buf = outputBuffer_; /* populate single EXR channel with view data */ IMB_exr_add_channel(exrhandle, nullptr, - m_name, - m_viewName, + name_, + viewName_, 1, - m_channels * width * height, + channels_ * width * height, buf, - m_format->depth == R_IMF_CHAN_DEPTH_16); + format_->depth == R_IMF_CHAN_DEPTH_16); - m_imageInput = nullptr; - m_outputBuffer = nullptr; + imageInput_ = nullptr; + outputBuffer_ = nullptr; /* create stereo ibuf */ - if (BKE_scene_multiview_is_render_view_last(m_rd, m_viewName)) { + if (BKE_scene_multiview_is_render_view_last(rd_, viewName_)) { ImBuf *ibuf[3] = {nullptr}; const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; char filename[FILE_MAX]; @@ -331,33 +331,33 @@ void OutputStereoOperation::deinitExecution() /* get rectf from EXR */ for (i = 0; i < 2; i++) { - float *rectf = IMB_exr_channel_rect(exrhandle, nullptr, m_name, names[i]); - ibuf[i] = IMB_allocImBuf(width, height, m_format->planes, 0); + float *rectf = IMB_exr_channel_rect(exrhandle, nullptr, name_, names[i]); + ibuf[i] = IMB_allocImBuf(width, height, format_->planes, 0); - ibuf[i]->channels = m_channels; + ibuf[i]->channels = channels_; ibuf[i]->rect_float = rectf; ibuf[i]->mall |= IB_rectfloat; - ibuf[i]->dither = m_rd->dither_intensity; + ibuf[i]->dither = rd_->dither_intensity; /* do colormanagement in the individual views, so it doesn't need to do in the stereo */ IMB_colormanagement_imbuf_for_write( - ibuf[i], true, false, m_viewSettings, m_displaySettings, m_format); + ibuf[i], true, false, viewSettings_, displaySettings_, format_); IMB_prepare_write_ImBuf(IMB_isfloat(ibuf[i]), ibuf[i]); } /* create stereo buffer */ - ibuf[2] = IMB_stereo3d_ImBuf(m_format, ibuf[0], ibuf[1]); + ibuf[2] = IMB_stereo3d_ImBuf(format_, ibuf[0], ibuf[1]); BKE_image_path_from_imformat(filename, - m_path, + path_, BKE_main_blendfile_path_from_global(), - m_rd->cfra, - m_format, - (m_rd->scemode & R_EXTENSION) != 0, + rd_->cfra, + format_, + (rd_->scemode & R_EXTENSION) != 0, true, nullptr); - BKE_imbuf_write(ibuf[2], filename, m_format); + BKE_imbuf_write(ibuf[2], filename, format_); /* imbuf knows which rects are not part of ibuf */ for (i = 0; i < 3; i++) { diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h index 6230a6f306b..a6e76e6190e 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -65,8 +65,8 @@ class OutputOpenExrMultiLayerMultiViewOperation : public OutputOpenExrMultiLayer class OutputStereoOperation : public OutputSingleLayerOperation { private: - char m_name[FILE_MAX]; - size_t m_channels; + char name_[FILE_MAX]; + size_t channels_; public: OutputStereoOperation(const RenderData *rd, diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 92eeb0b600b..6a5ddd969b4 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -213,64 +213,64 @@ OutputSingleLayerOperation::OutputSingleLayerOperation( const char *viewName, const bool saveAsRender) { - m_rd = rd; - m_tree = tree; + rd_ = rd; + tree_ = tree; this->addInputSocket(datatype); - m_outputBuffer = nullptr; - m_datatype = datatype; - m_imageInput = nullptr; + outputBuffer_ = nullptr; + datatype_ = datatype; + imageInput_ = nullptr; - m_format = format; - BLI_strncpy(m_path, path, sizeof(m_path)); + format_ = format; + BLI_strncpy(path_, path, sizeof(path_)); - m_viewSettings = viewSettings; - m_displaySettings = displaySettings; - m_viewName = viewName; - m_saveAsRender = saveAsRender; + viewSettings_ = viewSettings; + displaySettings_ = displaySettings; + viewName_ = viewName; + saveAsRender_ = saveAsRender; } void OutputSingleLayerOperation::initExecution() { - m_imageInput = getInputSocketReader(0); - m_outputBuffer = init_buffer(this->getWidth(), this->getHeight(), m_datatype); + imageInput_ = getInputSocketReader(0); + outputBuffer_ = init_buffer(this->getWidth(), this->getHeight(), datatype_); } void OutputSingleLayerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) { - write_buffer_rect(rect, m_tree, m_imageInput, m_outputBuffer, this->getWidth(), m_datatype); + write_buffer_rect(rect, tree_, imageInput_, outputBuffer_, this->getWidth(), datatype_); } void OutputSingleLayerOperation::deinitExecution() { if (this->getWidth() * this->getHeight() != 0) { - int size = get_datatype_size(m_datatype); - ImBuf *ibuf = IMB_allocImBuf(this->getWidth(), this->getHeight(), m_format->planes, 0); + int size = get_datatype_size(datatype_); + ImBuf *ibuf = IMB_allocImBuf(this->getWidth(), this->getHeight(), format_->planes, 0); char filename[FILE_MAX]; const char *suffix; ibuf->channels = size; - ibuf->rect_float = m_outputBuffer; + ibuf->rect_float = outputBuffer_; ibuf->mall |= IB_rectfloat; - ibuf->dither = m_rd->dither_intensity; + ibuf->dither = rd_->dither_intensity; IMB_colormanagement_imbuf_for_write( - ibuf, m_saveAsRender, false, m_viewSettings, m_displaySettings, m_format); + ibuf, saveAsRender_, false, viewSettings_, displaySettings_, format_); - suffix = BKE_scene_multiview_view_suffix_get(m_rd, m_viewName); + suffix = BKE_scene_multiview_view_suffix_get(rd_, viewName_); BKE_image_path_from_imformat(filename, - m_path, + path_, BKE_main_blendfile_path_from_global(), - m_rd->cfra, - m_format, - (m_rd->scemode & R_EXTENSION) != 0, + rd_->cfra, + format_, + (rd_->scemode & R_EXTENSION) != 0, true, suffix); - if (0 == BKE_imbuf_write(ibuf, filename, m_format)) { + if (0 == BKE_imbuf_write(ibuf, filename, format_)) { printf("Cannot save Node File Output to %s\n", filename); } else { @@ -279,20 +279,20 @@ void OutputSingleLayerOperation::deinitExecution() IMB_freeImBuf(ibuf); } - m_outputBuffer = nullptr; - m_imageInput = nullptr; + outputBuffer_ = nullptr; + imageInput_ = nullptr; } void OutputSingleLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), const rcti &area, Span inputs) { - if (!m_outputBuffer) { + if (!outputBuffer_) { return; } MemoryBuffer output_buf( - m_outputBuffer, COM_data_type_num_channels(m_datatype), this->getWidth(), this->getHeight()); + outputBuffer_, COM_data_type_num_channels(datatype_), this->getWidth(), this->getHeight()); const MemoryBuffer *input_image = inputs[0]; output_buf.copy_from(input_image, area); } @@ -318,14 +318,14 @@ OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene * bool exr_half_float, const char *viewName) { - m_scene = scene; - m_rd = rd; - m_tree = tree; + scene_ = scene; + rd_ = rd; + tree_ = tree; - BLI_strncpy(m_path, path, sizeof(m_path)); - m_exr_codec = exr_codec; - m_exr_half_float = exr_half_float; - m_viewName = viewName; + BLI_strncpy(path_, path, sizeof(path_)); + exr_codec_ = exr_codec; + exr_half_float_ = exr_half_float; + viewName_ = viewName; this->set_canvas_input_index(RESOLUTION_INPUT_ANY); } @@ -334,7 +334,7 @@ void OutputOpenExrMultiLayerOperation::add_layer(const char *name, bool use_layer) { this->addInputSocket(datatype); - m_layers.append(OutputOpenExrLayer(name, datatype, use_layer)); + layers_.append(OutputOpenExrLayer(name, datatype, use_layer)); } StampData *OutputOpenExrMultiLayerOperation::createStampData() const @@ -342,9 +342,9 @@ StampData *OutputOpenExrMultiLayerOperation::createStampData() const /* StampData API doesn't provide functions to modify an instance without having a RenderResult. */ RenderResult render_result; - StampData *stamp_data = BKE_stamp_info_from_scene_static(m_scene); + StampData *stamp_data = BKE_stamp_info_from_scene_static(scene_); render_result.stamp_data = stamp_data; - for (const OutputOpenExrLayer &layer : m_layers) { + for (const OutputOpenExrLayer &layer : layers_) { /* Skip unconnected sockets. */ if (layer.imageInput == nullptr) { continue; @@ -363,23 +363,23 @@ StampData *OutputOpenExrMultiLayerOperation::createStampData() const void OutputOpenExrMultiLayerOperation::initExecution() { - for (unsigned int i = 0; i < m_layers.size(); i++) { - if (m_layers[i].use_layer) { + for (unsigned int i = 0; i < layers_.size(); i++) { + if (layers_[i].use_layer) { SocketReader *reader = getInputSocketReader(i); - m_layers[i].imageInput = reader; - m_layers[i].outputBuffer = init_buffer( - this->getWidth(), this->getHeight(), m_layers[i].datatype); + layers_[i].imageInput = reader; + layers_[i].outputBuffer = init_buffer( + this->getWidth(), this->getHeight(), layers_[i].datatype); } } } void OutputOpenExrMultiLayerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) { - for (unsigned int i = 0; i < m_layers.size(); i++) { - OutputOpenExrLayer &layer = m_layers[i]; + for (unsigned int i = 0; i < layers_.size(); i++) { + OutputOpenExrLayer &layer = layers_[i]; if (layer.imageInput) { write_buffer_rect( - rect, m_tree, layer.imageInput, layer.outputBuffer, this->getWidth(), layer.datatype); + rect, tree_, layer.imageInput, layer.outputBuffer, this->getWidth(), layer.datatype); } } } @@ -393,35 +393,35 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() const char *suffix; void *exrhandle = IMB_exr_get_handle(); - suffix = BKE_scene_multiview_view_suffix_get(m_rd, m_viewName); + suffix = BKE_scene_multiview_view_suffix_get(rd_, viewName_); BKE_image_path_from_imtype(filename, - m_path, + path_, BKE_main_blendfile_path_from_global(), - m_rd->cfra, + rd_->cfra, R_IMF_IMTYPE_MULTILAYER, - (m_rd->scemode & R_EXTENSION) != 0, + (rd_->scemode & R_EXTENSION) != 0, true, suffix); BLI_make_existing_file(filename); - for (unsigned int i = 0; i < m_layers.size(); i++) { - OutputOpenExrLayer &layer = m_layers[i]; + for (unsigned int i = 0; i < layers_.size(); i++) { + OutputOpenExrLayer &layer = layers_[i]; if (!layer.imageInput) { continue; /* skip unconnected sockets */ } add_exr_channels(exrhandle, - m_layers[i].name, - m_layers[i].datatype, + layers_[i].name, + layers_[i].datatype, "", width, - m_exr_half_float, - m_layers[i].outputBuffer); + exr_half_float_, + layers_[i].outputBuffer); } /* when the filename has no permissions, this can fail */ StampData *stamp_data = createStampData(); - if (IMB_exr_begin_write(exrhandle, filename, width, height, m_exr_codec, stamp_data)) { + if (IMB_exr_begin_write(exrhandle, filename, width, height, exr_codec_, stamp_data)) { IMB_exr_write_channels(exrhandle); } else { @@ -431,13 +431,13 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() } IMB_exr_close(exrhandle); - for (unsigned int i = 0; i < m_layers.size(); i++) { - if (m_layers[i].outputBuffer) { - MEM_freeN(m_layers[i].outputBuffer); - m_layers[i].outputBuffer = nullptr; + for (unsigned int i = 0; i < layers_.size(); i++) { + if (layers_[i].outputBuffer) { + MEM_freeN(layers_[i].outputBuffer); + layers_[i].outputBuffer = nullptr; } - m_layers[i].imageInput = nullptr; + layers_[i].imageInput = nullptr; } BKE_stamp_data_free(stamp_data); } @@ -448,8 +448,8 @@ void OutputOpenExrMultiLayerOperation::update_memory_buffer_partial(MemoryBuffer Span inputs) { const MemoryBuffer *input_image = inputs[0]; - for (int i = 0; i < m_layers.size(); i++) { - OutputOpenExrLayer &layer = m_layers[i]; + for (int i = 0; i < layers_.size(); i++) { + OutputOpenExrLayer &layer = layers_[i]; if (layer.outputBuffer) { MemoryBuffer output_buf(layer.outputBuffer, COM_data_type_num_channels(layer.datatype), diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 057cee0c43e..e862b964431 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -32,21 +32,21 @@ namespace blender::compositor { /* Writes the image to a single-layer file. */ class OutputSingleLayerOperation : public MultiThreadedOperation { protected: - const RenderData *m_rd; - const bNodeTree *m_tree; + const RenderData *rd_; + const bNodeTree *tree_; - ImageFormatData *m_format; - char m_path[FILE_MAX]; + ImageFormatData *format_; + char path_[FILE_MAX]; - float *m_outputBuffer; - DataType m_datatype; - SocketReader *m_imageInput; + float *outputBuffer_; + DataType datatype_; + SocketReader *imageInput_; - const ColorManagedViewSettings *m_viewSettings; - const ColorManagedDisplaySettings *m_displaySettings; + const ColorManagedViewSettings *viewSettings_; + const ColorManagedDisplaySettings *displaySettings_; - const char *m_viewName; - bool m_saveAsRender; + const char *viewName_; + bool saveAsRender_; public: OutputSingleLayerOperation(const RenderData *rd, @@ -92,15 +92,15 @@ struct OutputOpenExrLayer { /* Writes inputs into OpenEXR multilayer channels. */ class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation { protected: - const Scene *m_scene; - const RenderData *m_rd; - const bNodeTree *m_tree; + const Scene *scene_; + const RenderData *rd_; + const bNodeTree *tree_; - char m_path[FILE_MAX]; - char m_exr_codec; - bool m_exr_half_float; - Vector m_layers; - const char *m_viewName; + char path_[FILE_MAX]; + char exr_codec_; + bool exr_half_float_; + Vector layers_; + const char *viewName_; StampData *createStampData() const; diff --git a/source/blender/compositor/operations/COM_PixelateOperation.cc b/source/blender/compositor/operations/COM_PixelateOperation.cc index b9a18756550..0d6e906a4c3 100644 --- a/source/blender/compositor/operations/COM_PixelateOperation.cc +++ b/source/blender/compositor/operations/COM_PixelateOperation.cc @@ -25,17 +25,17 @@ PixelateOperation::PixelateOperation(DataType datatype) this->addInputSocket(datatype); this->addOutputSocket(datatype); this->set_canvas_input_index(0); - m_inputOperation = nullptr; + inputOperation_ = nullptr; } void PixelateOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); + inputOperation_ = this->getInputSocketReader(0); } void PixelateOperation::deinitExecution() { - m_inputOperation = nullptr; + inputOperation_ = nullptr; } void PixelateOperation::executePixelSampled(float output[4], @@ -45,7 +45,7 @@ void PixelateOperation::executePixelSampled(float output[4], { float nx = round(x); float ny = round(y); - m_inputOperation->readSampled(output, nx, ny, sampler); + inputOperation_->readSampled(output, nx, ny, sampler); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PixelateOperation.h b/source/blender/compositor/operations/COM_PixelateOperation.h index e8b272853da..f8155e54f75 100644 --- a/source/blender/compositor/operations/COM_PixelateOperation.h +++ b/source/blender/compositor/operations/COM_PixelateOperation.h @@ -34,7 +34,7 @@ class PixelateOperation : public NodeOperation { /** * \brief cached reference to the input operation */ - SocketReader *m_inputOperation; + SocketReader *inputOperation_; public: /** diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc index 92733186979..fbaab16efc2 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc @@ -134,7 +134,7 @@ static void read_input_corners(NodeOperation *op, const int first_input_idx, flo /* ******** PlaneCornerPinMaskOperation ******** */ -PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(false) +PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : corners_ready_(false) { addInputSocket(DataType::Vector); addInputSocket(DataType::Vector); @@ -182,7 +182,7 @@ void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect) * we don't have a nice generic system for that yet */ lockMutex(); - if (!m_corners_ready) { + if (!corners_ready_) { SocketReader *readers[4] = { getInputSocketReader(0), getInputSocketReader(1), @@ -193,7 +193,7 @@ void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect) readCornersFromSockets(rect, readers, corners); calculateCorners(corners, true, 0); - m_corners_ready = true; + corners_ready_ = true; } unlockMutex(); @@ -219,7 +219,7 @@ void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_id /* ******** PlaneCornerPinWarpImageOperation ******** */ -PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : m_corners_ready(false) +PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : corners_ready_(false) { addInputSocket(DataType::Vector); addInputSocket(DataType::Vector); @@ -259,7 +259,7 @@ void *PlaneCornerPinWarpImageOperation::initializeTileData(rcti *rect) * we don't have a nice generic system for that yet */ lockMutex(); - if (!m_corners_ready) { + if (!corners_ready_) { /* corner sockets start at index 1 */ SocketReader *readers[4] = { getInputSocketReader(1), @@ -271,7 +271,7 @@ void *PlaneCornerPinWarpImageOperation::initializeTileData(rcti *rect) readCornersFromSockets(rect, readers, corners); calculateCorners(corners, true, 0); - m_corners_ready = true; + corners_ready_ = true; } unlockMutex(); diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h index 7b3917923d2..211dcee5132 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h @@ -32,7 +32,7 @@ namespace blender::compositor { class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation { private: /* TODO(manzanilla): to be removed with tiled implementation. */ - bool m_corners_ready; + bool corners_ready_; public: PlaneCornerPinMaskOperation(); @@ -50,7 +50,7 @@ class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation { class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation { private: - bool m_corners_ready; + bool corners_ready_; public: PlaneCornerPinWarpImageOperation(); diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 9bed0ca14cb..8cfa8715cc2 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -25,7 +25,7 @@ namespace blender::compositor { PlaneDistortBaseOperation::PlaneDistortBaseOperation() - : m_motion_blur_samples(1), m_motion_blur_shutter(0.5f) + : motion_blur_samples_(1), motion_blur_shutter_(0.5f) { } @@ -33,8 +33,8 @@ void PlaneDistortBaseOperation::calculateCorners(const float corners[4][2], bool normalized, int sample) { - BLI_assert(sample < m_motion_blur_samples); - MotionSample *sample_data = &m_samples[sample]; + BLI_assert(sample < motion_blur_samples_); + MotionSample *sample_data = &samples_[sample]; if (normalized) { for (int i = 0; i < 4; i++) { sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); @@ -68,7 +68,7 @@ PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortB { this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Color); - m_pixelReader = nullptr; + pixelReader_ = nullptr; this->flags.complex = true; } @@ -83,19 +83,19 @@ void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], const int height = image->getHeight(); float frame_corners[4][2] = { {0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}}; - MotionSample *sample_data = &m_samples[sample]; + MotionSample *sample_data = &samples_[sample]; BKE_tracking_homography_between_two_quads( sample_data->frameSpaceCorners, frame_corners, sample_data->perspectiveMatrix); } void PlaneDistortWarpImageOperation::initExecution() { - m_pixelReader = this->getInputSocketReader(0); + pixelReader_ = this->getInputSocketReader(0); } void PlaneDistortWarpImageOperation::deinitExecution() { - m_pixelReader = nullptr; + pixelReader_ = nullptr; } void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], @@ -105,19 +105,19 @@ void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], { float uv[2]; float deriv[2][2]; - if (m_motion_blur_samples == 1) { - warpCoord(x, y, m_samples[0].perspectiveMatrix, uv, deriv); - m_pixelReader->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + if (motion_blur_samples_ == 1) { + warpCoord(x, y, samples_[0].perspectiveMatrix, uv, deriv); + pixelReader_->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); } else { zero_v4(output); - for (int sample = 0; sample < m_motion_blur_samples; sample++) { + for (int sample = 0; sample < motion_blur_samples_; sample++) { float color[4]; - warpCoord(x, y, m_samples[sample].perspectiveMatrix, uv, deriv); - m_pixelReader->readFiltered(color, uv[0], uv[1], deriv[0], deriv[1]); + warpCoord(x, y, samples_[sample].perspectiveMatrix, uv, deriv); + pixelReader_->readFiltered(color, uv[0], uv[1], deriv[0], deriv[1]); add_v4_v4(output, color); } - mul_v4_fl(output, 1.0f / (float)m_motion_blur_samples); + mul_v4_fl(output, 1.0f / (float)motion_blur_samples_); } } @@ -129,22 +129,22 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer * float uv[2]; float deriv[2][2]; BuffersIterator it = output->iterate_with({}, area); - if (m_motion_blur_samples == 1) { + if (motion_blur_samples_ == 1) { for (; !it.is_end(); ++it) { - warpCoord(it.x, it.y, m_samples[0].perspectiveMatrix, uv, deriv); + warpCoord(it.x, it.y, samples_[0].perspectiveMatrix, uv, deriv); input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); } } else { for (; !it.is_end(); ++it) { zero_v4(it.out); - for (const int sample : IndexRange(m_motion_blur_samples)) { + for (const int sample : IndexRange(motion_blur_samples_)) { float color[4]; - warpCoord(it.x, it.y, m_samples[sample].perspectiveMatrix, uv, deriv); + warpCoord(it.x, it.y, samples_[sample].perspectiveMatrix, uv, deriv); input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], color); add_v4_v4(it.out, color); } - mul_v4_fl(it.out, 1.0f / (float)m_motion_blur_samples); + mul_v4_fl(it.out, 1.0f / (float)motion_blur_samples_); } } } @@ -155,10 +155,10 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( float min[2], max[2]; INIT_MINMAX2(min, max); - for (int sample = 0; sample < m_motion_blur_samples; sample++) { + for (int sample = 0; sample < motion_blur_samples_; sample++) { float UVs[4][2]; float deriv[2][2]; - MotionSample *sample_data = &m_samples[sample]; + MotionSample *sample_data = &samples_[sample]; /* TODO(sergey): figure out proper way to do this. */ warpCoord(input->xmin - 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); warpCoord(input->xmax + 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv); @@ -196,10 +196,10 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, #if 0 float min[2], max[2]; INIT_MINMAX2(min, max); - for (int sample = 0; sample < m_motion_blur_samples; sample++) { + for (int sample = 0; sample < motion_blur_samples_; sample++) { float UVs[4][2]; float deriv[2][2]; - MotionSample *sample_data = &m_samples[sample]; + MotionSample *sample_data = &samples_[sample]; /* TODO(sergey): figure out proper way to do this. */ warpCoord( output_area.xmin - 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); @@ -228,12 +228,12 @@ PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperati addOutputSocket(DataType::Value); /* Currently hardcoded to 8 samples. */ - m_osa = 8; + osa_ = 8; } void PlaneDistortMaskOperation::initExecution() { - BLI_jitter_init(m_jitter, m_osa); + BLI_jitter_init(jitter_, osa_); } void PlaneDistortMaskOperation::executePixelSampled(float output[4], @@ -243,11 +243,11 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4], { float point[2]; int inside_counter = 0; - if (m_motion_blur_samples == 1) { - MotionSample *sample_data = &m_samples[0]; - for (int sample = 0; sample < m_osa; sample++) { - point[0] = x + m_jitter[sample][0]; - point[1] = y + m_jitter[sample][1]; + if (motion_blur_samples_ == 1) { + MotionSample *sample_data = &samples_[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->frameSpaceCorners[0], sample_data->frameSpaceCorners[1], @@ -259,14 +259,14 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4], inside_counter++; } } - output[0] = (float)inside_counter / m_osa; + output[0] = (float)inside_counter / osa_; } else { - for (int motion_sample = 0; motion_sample < m_motion_blur_samples; motion_sample++) { - MotionSample *sample_data = &m_samples[motion_sample]; - for (int osa_sample = 0; osa_sample < m_osa; osa_sample++) { - point[0] = x + m_jitter[osa_sample][0]; - point[1] = y + m_jitter[osa_sample][1]; + for (int motion_sample = 0; motion_sample < motion_blur_samples_; motion_sample++) { + MotionSample *sample_data = &samples_[motion_sample]; + for (int osa_sample = 0; osa_sample < osa_; osa_sample++) { + point[0] = x + jitter_[osa_sample][0]; + point[1] = y + jitter_[osa_sample][1]; if (isect_point_tri_v2(point, sample_data->frameSpaceCorners[0], sample_data->frameSpaceCorners[1], @@ -279,7 +279,7 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4], } } } - output[0] = (float)inside_counter / (m_osa * m_motion_blur_samples); + output[0] = (float)inside_counter / (osa_ * motion_blur_samples_); } } @@ -289,11 +289,11 @@ void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *outpu { for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { int inside_count = 0; - for (const int motion_sample : IndexRange(m_motion_blur_samples)) { - MotionSample &sample = m_samples[motion_sample]; + 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); } - *it.out = (float)inside_count / (m_osa * m_motion_blur_samples); + *it.out = (float)inside_count / (osa_ * motion_blur_samples_); } } @@ -303,9 +303,9 @@ int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x, { float point[2]; int inside_count = 0; - for (int sample = 0; sample < m_osa; sample++) { - point[0] = x + m_jitter[sample][0]; - point[1] = y + m_jitter[sample][1]; + 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.frameSpaceCorners[0], sample_data.frameSpaceCorners[1], diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h index 7b863d562ab..e9880624ebf 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h @@ -38,9 +38,9 @@ class PlaneDistortBaseOperation : public MultiThreadedOperation { float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ float perspectiveMatrix[3][3]; }; - MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES]; - int m_motion_blur_samples; - float m_motion_blur_shutter; + MotionSample samples_[PLANE_DISTORT_MAX_SAMPLES]; + int motion_blur_samples_; + float motion_blur_shutter_; public: PlaneDistortBaseOperation(); @@ -48,11 +48,11 @@ class PlaneDistortBaseOperation : public MultiThreadedOperation { void setMotionBlurSamples(int samples) { BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); - m_motion_blur_samples = samples; + motion_blur_samples_ = samples; } void setMotionBlurShutter(float shutter) { - m_motion_blur_shutter = shutter; + motion_blur_shutter_ = shutter; } virtual void calculateCorners(const float corners[4][2], bool normalized, int sample); @@ -63,7 +63,7 @@ class PlaneDistortBaseOperation : public MultiThreadedOperation { class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation { protected: - SocketReader *m_pixelReader; + SocketReader *pixelReader_; public: PlaneDistortWarpImageOperation(); @@ -87,8 +87,8 @@ class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation { class PlaneDistortMaskOperation : public PlaneDistortBaseOperation { protected: - int m_osa; - float m_jitter[32][2]; + int osa_; + float jitter_[32][2]; public: PlaneDistortMaskOperation(); diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc index 64e31cc01e2..1f40556a935 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc @@ -27,25 +27,25 @@ namespace blender::compositor { PlaneTrackCommon::PlaneTrackCommon() { - m_movieClip = nullptr; - m_framenumber = 0; - m_trackingObjectName[0] = '\0'; - m_planeTrackName[0] = '\0'; + movieClip_ = nullptr; + framenumber_ = 0; + trackingObjectName_[0] = '\0'; + planeTrackName_[0] = '\0'; } void PlaneTrackCommon::read_and_calculate_corners(PlaneDistortBaseOperation *distort_op) { float corners[4][2]; - if (distort_op->m_motion_blur_samples == 1) { - readCornersFromTrack(corners, m_framenumber); + if (distort_op->motion_blur_samples_ == 1) { + readCornersFromTrack(corners, framenumber_); distort_op->calculateCorners(corners, true, 0); } else { - const float frame = (float)m_framenumber - distort_op->m_motion_blur_shutter; - const float frame_step = (distort_op->m_motion_blur_shutter * 2.0f) / - distort_op->m_motion_blur_samples; + const float frame = (float)framenumber_ - distort_op->motion_blur_shutter_; + const float frame_step = (distort_op->motion_blur_shutter_ * 2.0f) / + distort_op->motion_blur_samples_; float frame_iter = frame; - for (int sample = 0; sample < distort_op->m_motion_blur_samples; sample++) { + for (int sample = 0; sample < distort_op->motion_blur_samples_; sample++) { readCornersFromTrack(corners, frame_iter); distort_op->calculateCorners(corners, true, sample); frame_iter += frame_step; @@ -58,18 +58,18 @@ void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) MovieTracking *tracking; MovieTrackingObject *object; - if (!m_movieClip) { + if (!movieClip_) { return; } - tracking = &m_movieClip->tracking; + tracking = &movieClip_->tracking; - object = BKE_tracking_object_get_named(tracking, m_trackingObjectName); + object = BKE_tracking_object_get_named(tracking, trackingObjectName_); if (object) { MovieTrackingPlaneTrack *plane_track; - plane_track = BKE_tracking_plane_track_get_named(tracking, object, m_planeTrackName); + plane_track = BKE_tracking_plane_track_get_named(tracking, object, planeTrackName_); if (plane_track) { - float clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, frame); + float clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, frame); BKE_tracking_plane_marker_get_subframe_corners(plane_track, clip_framenr, corners); } } @@ -78,11 +78,11 @@ void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) void PlaneTrackCommon::determine_canvas(const rcti &preferred_area, rcti &r_area) { r_area = COM_AREA_NONE; - if (m_movieClip) { + if (movieClip_) { int width, height; MovieClipUser user = {0}; - BKE_movieclip_user_set_frame(&user, m_framenumber); - BKE_movieclip_get_size(m_movieClip, &user, &width, &height); + BKE_movieclip_user_set_frame(&user, framenumber_); + BKE_movieclip_get_size(movieClip_, &user, &width, &height); r_area = preferred_area; r_area.xmax = r_area.xmin + width; r_area.ymax = r_area.ymin + height; diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h index 1890195d14a..d3dbd092024 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h @@ -32,10 +32,10 @@ namespace blender::compositor { class PlaneTrackCommon { protected: - MovieClip *m_movieClip; - int m_framenumber; - char m_trackingObjectName[64]; - char m_planeTrackName[64]; + MovieClip *movieClip_; + int framenumber_; + char trackingObjectName_[64]; + char planeTrackName_[64]; /* NOTE: this class is not an operation itself (to prevent virtual inheritance issues) * implementation classes must make wrappers to use these methods, see below. @@ -48,19 +48,19 @@ class PlaneTrackCommon { void setMovieClip(MovieClip *clip) { - m_movieClip = clip; + movieClip_ = clip; } void setTrackingObject(char *object) { - BLI_strncpy(m_trackingObjectName, object, sizeof(m_trackingObjectName)); + BLI_strncpy(trackingObjectName_, object, sizeof(trackingObjectName_)); } void setPlaneTrackName(char *plane_track) { - BLI_strncpy(m_planeTrackName, plane_track, sizeof(m_planeTrackName)); + BLI_strncpy(planeTrackName_, plane_track, sizeof(planeTrackName_)); } void setFramenumber(int framenumber) { - m_framenumber = framenumber; + framenumber_ = framenumber; } private: diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.cc b/source/blender/compositor/operations/COM_PosterizeOperation.cc index dd1c5cae1bb..44a86f1d9de 100644 --- a/source/blender/compositor/operations/COM_PosterizeOperation.cc +++ b/source/blender/compositor/operations/COM_PosterizeOperation.cc @@ -25,15 +25,15 @@ PosterizeOperation::PosterizeOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_inputProgram = nullptr; - m_inputStepsProgram = nullptr; + inputProgram_ = nullptr; + inputStepsProgram_ = nullptr; flags.can_be_constant = true; } void PosterizeOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); - m_inputStepsProgram = this->getInputSocketReader(1); + inputProgram_ = this->getInputSocketReader(0); + inputStepsProgram_ = this->getInputSocketReader(1); } void PosterizeOperation::executePixelSampled(float output[4], @@ -44,8 +44,8 @@ void PosterizeOperation::executePixelSampled(float output[4], float inputValue[4]; float inputSteps[4]; - m_inputProgram->readSampled(inputValue, x, y, sampler); - m_inputStepsProgram->readSampled(inputSteps, x, y, sampler); + inputProgram_->readSampled(inputValue, x, y, sampler); + inputStepsProgram_->readSampled(inputSteps, x, y, sampler); CLAMP(inputSteps[0], 2.0f, 1024.0f); const float steps_inv = 1.0f / inputSteps[0]; @@ -75,8 +75,8 @@ void PosterizeOperation::update_memory_buffer_partial(MemoryBuffer *output, void PosterizeOperation::deinitExecution() { - m_inputProgram = nullptr; - m_inputStepsProgram = nullptr; + inputProgram_ = nullptr; + inputStepsProgram_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.h b/source/blender/compositor/operations/COM_PosterizeOperation.h index c625cbb83c6..58ce599c6fe 100644 --- a/source/blender/compositor/operations/COM_PosterizeOperation.h +++ b/source/blender/compositor/operations/COM_PosterizeOperation.h @@ -27,8 +27,8 @@ class PosterizeOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; - SocketReader *m_inputStepsProgram; + SocketReader *inputProgram_; + SocketReader *inputStepsProgram_; public: PosterizeOperation(); diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc index 707706a9d07..960cfda9ec0 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cc +++ b/source/blender/compositor/operations/COM_PreviewOperation.cc @@ -30,14 +30,14 @@ PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings, { this->addInputSocket(DataType::Color, ResizeMode::Align); - m_preview = nullptr; - m_outputBuffer = nullptr; - m_input = nullptr; - m_divider = 1.0f; - m_viewSettings = viewSettings; - m_displaySettings = displaySettings; - m_defaultWidth = defaultWidth; - m_defaultHeight = defaultHeight; + preview_ = nullptr; + outputBuffer_ = nullptr; + input_ = nullptr; + divider_ = 1.0f; + viewSettings_ = viewSettings; + displaySettings_ = displaySettings; + defaultWidth_ = defaultWidth; + defaultHeight_ = defaultHeight; flags.use_viewer_border = true; flags.is_preview_operation = true; } @@ -47,34 +47,34 @@ void PreviewOperation::verifyPreview(bNodeInstanceHash *previews, bNodeInstanceK /* Size (0, 0) ensures the preview rect is not allocated in advance, * this is set later in initExecution once the resolution is determined. */ - m_preview = BKE_node_preview_verify(previews, key, 0, 0, true); + preview_ = BKE_node_preview_verify(previews, key, 0, 0, true); } void PreviewOperation::initExecution() { - m_input = getInputSocketReader(0); + input_ = getInputSocketReader(0); - if (this->getWidth() == (unsigned int)m_preview->xsize && - this->getHeight() == (unsigned int)m_preview->ysize) { - m_outputBuffer = m_preview->rect; + if (this->getWidth() == (unsigned int)preview_->xsize && + this->getHeight() == (unsigned int)preview_->ysize) { + outputBuffer_ = preview_->rect; } - if (m_outputBuffer == nullptr) { - m_outputBuffer = (unsigned char *)MEM_callocN( + if (outputBuffer_ == nullptr) { + outputBuffer_ = (unsigned char *)MEM_callocN( sizeof(unsigned char) * 4 * getWidth() * getHeight(), "PreviewOperation"); - if (m_preview->rect) { - MEM_freeN(m_preview->rect); + if (preview_->rect) { + MEM_freeN(preview_->rect); } - m_preview->xsize = getWidth(); - m_preview->ysize = getHeight(); - m_preview->rect = m_outputBuffer; + preview_->xsize = getWidth(); + preview_->ysize = getHeight(); + preview_->rect = outputBuffer_; } } void PreviewOperation::deinitExecution() { - m_outputBuffer = nullptr; - m_input = nullptr; + outputBuffer_ = nullptr; + input_ = nullptr; } void PreviewOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) @@ -83,21 +83,21 @@ void PreviewOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) float color[4]; struct ColormanageProcessor *cm_processor; - cm_processor = IMB_colormanagement_display_processor_new(m_viewSettings, m_displaySettings); + cm_processor = IMB_colormanagement_display_processor_new(viewSettings_, displaySettings_); for (int y = rect->ymin; y < rect->ymax; y++) { offset = (y * getWidth() + rect->xmin) * 4; for (int x = rect->xmin; x < rect->xmax; x++) { - float rx = floor(x / m_divider); - float ry = floor(y / m_divider); + float rx = floor(x / divider_); + float ry = floor(y / divider_); color[0] = 0.0f; color[1] = 0.0f; color[2] = 0.0f; color[3] = 1.0f; - m_input->readSampled(color, rx, ry, PixelSampler::Nearest); + input_->readSampled(color, rx, ry, PixelSampler::Nearest); IMB_colormanagement_processor_apply_v4(cm_processor, color); - rgba_float_to_uchar(m_outputBuffer + offset, color); + rgba_float_to_uchar(outputBuffer_ + offset, color); offset += 4; } } @@ -110,10 +110,10 @@ bool PreviewOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - newInput.xmin = input->xmin / m_divider; - newInput.xmax = input->xmax / m_divider; - newInput.ymin = input->ymin / m_divider; - newInput.ymax = input->ymax / m_divider; + newInput.xmin = input->xmin / divider_; + newInput.xmax = input->xmax / divider_; + newInput.ymin = input->ymin / divider_; + newInput.ymax = input->ymax / divider_; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -121,9 +121,9 @@ void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti { /* Use default preview resolution as preferred ensuring it has size so that * generated inputs (which don't have resolution on their own) are displayed */ - BLI_assert(m_defaultWidth > 0 && m_defaultHeight > 0); + BLI_assert(defaultWidth_ > 0 && defaultHeight_ > 0); rcti local_preferred; - BLI_rcti_init(&local_preferred, 0, m_defaultWidth, 0, m_defaultHeight); + BLI_rcti_init(&local_preferred, 0, defaultWidth_, 0, defaultHeight_); NodeOperation::determine_canvas(local_preferred, r_area); /* If resolution is 0 there are two possible scenarios: @@ -137,17 +137,17 @@ void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti */ int width = BLI_rcti_size_x(&r_area); int height = BLI_rcti_size_y(&r_area); - m_divider = 0.0f; + divider_ = 0.0f; if (width > 0 && height > 0) { if (width > height) { - m_divider = (float)COM_PREVIEW_SIZE / (width); + divider_ = (float)COM_PREVIEW_SIZE / (width); } else { - m_divider = (float)COM_PREVIEW_SIZE / (height); + divider_ = (float)COM_PREVIEW_SIZE / (height); } } - width = width * m_divider; - height = height * m_divider; + width = width * divider_; + height = height * divider_; BLI_rcti_init(&r_area, r_area.xmin, r_area.xmin + width, r_area.ymin, r_area.ymin + height); } @@ -164,10 +164,10 @@ void PreviewOperation::get_area_of_interest(const int input_idx, BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin / m_divider; - r_input_area.xmax = output_area.xmax / m_divider; - r_input_area.ymin = output_area.ymin / m_divider; - r_input_area.ymax = output_area.ymax / m_divider; + r_input_area.xmin = output_area.xmin / divider_; + r_input_area.xmax = output_area.xmax / divider_; + r_input_area.ymin = output_area.ymin / divider_; + r_input_area.ymax = output_area.ymax / divider_; } void PreviewOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), @@ -176,16 +176,16 @@ void PreviewOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output) { MemoryBuffer *input = inputs[0]; struct ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new( - m_viewSettings, m_displaySettings); + viewSettings_, displaySettings_); rcti buffer_area; BLI_rcti_init(&buffer_area, 0, this->getWidth(), 0, this->getHeight()); BuffersIteratorBuilder it_builder( - m_outputBuffer, buffer_area, area, COM_data_type_num_channels(DataType::Color)); + outputBuffer_, buffer_area, area, COM_data_type_num_channels(DataType::Color)); for (BuffersIterator it = it_builder.build(); !it.is_end(); ++it) { - const float rx = it.x / m_divider; - const float ry = it.y / m_divider; + const float rx = it.x / divider_; + const float ry = it.y / divider_; float color[4]; input->read_elem_checked(rx, ry, color); diff --git a/source/blender/compositor/operations/COM_PreviewOperation.h b/source/blender/compositor/operations/COM_PreviewOperation.h index 4bd0f07d882..1df8815d62b 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.h +++ b/source/blender/compositor/operations/COM_PreviewOperation.h @@ -28,19 +28,19 @@ namespace blender::compositor { class PreviewOperation : public MultiThreadedOperation { protected: - unsigned char *m_outputBuffer; + unsigned char *outputBuffer_; /** * \brief holds reference to the SDNA bNode, where this nodes will render the preview image for */ - bNodePreview *m_preview; - SocketReader *m_input; - float m_divider; - unsigned int m_defaultWidth; - unsigned int m_defaultHeight; + bNodePreview *preview_; + SocketReader *input_; + float divider_; + unsigned int defaultWidth_; + unsigned int defaultHeight_; - const ColorManagedViewSettings *m_viewSettings; - const ColorManagedDisplaySettings *m_displaySettings; + const ColorManagedViewSettings *viewSettings_; + const ColorManagedDisplaySettings *displaySettings_; public: PreviewOperation(const ColorManagedViewSettings *viewSettings, diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc index d2d5581deb2..8e6aa7e8db8 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc @@ -27,9 +27,9 @@ ProjectorLensDistortionOperation::ProjectorLensDistortionOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->flags.complex = true; - m_inputProgram = nullptr; - m_dispersionAvailable = false; - m_dispersion = 0.0f; + inputProgram_ = nullptr; + dispersionAvailable_ = false; + dispersion_ = 0.0f; } void ProjectorLensDistortionOperation::init_data() @@ -37,23 +37,23 @@ void ProjectorLensDistortionOperation::init_data() if (execution_model_ == eExecutionModel::FullFrame) { NodeOperation *dispersion_input = get_input_operation(1); if (dispersion_input->get_flags().is_constant_operation) { - m_dispersion = static_cast(dispersion_input)->get_constant_elem()[0]; + dispersion_ = static_cast(dispersion_input)->get_constant_elem()[0]; } - m_kr = 0.25f * max_ff(min_ff(m_dispersion, 1.0f), 0.0f); - m_kr2 = m_kr * 20; + kr_ = 0.25f * max_ff(min_ff(dispersion_, 1.0f), 0.0f); + kr2_ = kr_ * 20; } } void ProjectorLensDistortionOperation::initExecution() { this->initMutex(); - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); } void *ProjectorLensDistortionOperation::initializeTileData(rcti * /*rect*/) { updateDispersion(); - void *buffer = m_inputProgram->initializeTileData(nullptr); + void *buffer = inputProgram_->initializeTileData(nullptr); return buffer; } @@ -65,11 +65,11 @@ void ProjectorLensDistortionOperation::executePixel(float output[4], int x, int const float v = (y + 0.5f) / height; const float u = (x + 0.5f) / width; MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - inputBuffer->readBilinear(inputValue, (u * width + m_kr2) - 0.5f, v * height - 0.5f); + inputBuffer->readBilinear(inputValue, (u * width + kr2_) - 0.5f, v * height - 0.5f); output[0] = inputValue[0]; inputBuffer->read(inputValue, x, y); output[1] = inputValue[1]; - inputBuffer->readBilinear(inputValue, (u * width - m_kr2) - 0.5f, v * height - 0.5f); + inputBuffer->readBilinear(inputValue, (u * width - kr2_) - 0.5f, v * height - 0.5f); output[2] = inputValue[2]; output[3] = 1.0f; } @@ -77,18 +77,18 @@ void ProjectorLensDistortionOperation::executePixel(float output[4], int x, int void ProjectorLensDistortionOperation::deinitExecution() { this->deinitMutex(); - m_inputProgram = nullptr; + inputProgram_ = nullptr; } bool ProjectorLensDistortionOperation::determineDependingAreaOfInterest( rcti *input, ReadBufferOperation *readOperation, rcti *output) { rcti newInput; - if (m_dispersionAvailable) { + if (dispersionAvailable_) { newInput.ymax = input->ymax; newInput.ymin = input->ymin; - newInput.xmin = input->xmin - m_kr2 - 2; - newInput.xmax = input->xmax + m_kr2 + 2; + newInput.xmin = input->xmin - kr2_ - 2; + newInput.xmax = input->xmax + kr2_ + 2; } else { rcti dispInput; @@ -112,17 +112,17 @@ bool ProjectorLensDistortionOperation::determineDependingAreaOfInterest( /* TODO(manzanilla): to be removed with tiled implementation. */ void ProjectorLensDistortionOperation::updateDispersion() { - if (m_dispersionAvailable) { + if (dispersionAvailable_) { return; } this->lockMutex(); - if (!m_dispersionAvailable) { + if (!dispersionAvailable_) { float result[4]; this->getInputSocketReader(1)->readSampled(result, 1, 1, PixelSampler::Nearest); - m_dispersion = result[0]; - m_kr = 0.25f * max_ff(min_ff(m_dispersion, 1.0f), 0.0f); - m_kr2 = m_kr * 20; - m_dispersionAvailable = true; + dispersion_ = result[0]; + kr_ = 0.25f * max_ff(min_ff(dispersion_, 1.0f), 0.0f); + kr2_ = kr_ * 20; + dispersionAvailable_ = true; } this->unlockMutex(); } @@ -156,8 +156,8 @@ void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx, r_input_area.ymax = output_area.ymax; r_input_area.ymin = output_area.ymin; - r_input_area.xmin = output_area.xmin - m_kr2 - 2; - r_input_area.xmax = output_area.xmax + m_kr2 + 2; + r_input_area.xmin = output_area.xmin - kr2_ - 2; + r_input_area.xmax = output_area.xmax + kr2_ + 2; } void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -171,11 +171,11 @@ void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { const float v = (it.y + 0.5f) / height; const float u = (it.x + 0.5f) / width; - input_image->read_elem_bilinear((u * width + m_kr2) - 0.5f, v * height - 0.5f, color); + input_image->read_elem_bilinear((u * width + kr2_) - 0.5f, v * height - 0.5f, color); it.out[0] = color[0]; input_image->read_elem(it.x, it.y, color); it.out[1] = color[1]; - input_image->read_elem_bilinear((u * width - m_kr2) - 0.5f, v * height - 0.5f, color); + input_image->read_elem_bilinear((u * width - kr2_) - 0.5f, v * height - 0.5f, color); it.out[2] = color[2]; it.out[3] = 1.0f; } diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h index b42fa3a361c..af9b216db07 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h @@ -28,13 +28,13 @@ class ProjectorLensDistortionOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; + SocketReader *inputProgram_; - float m_dispersion; + float dispersion_; /* TODO(manzanilla): to be removed with tiled implementation. */ - bool m_dispersionAvailable; + bool dispersionAvailable_; - float m_kr, m_kr2; + float kr_, kr2_; public: ProjectorLensDistortionOperation(); diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.cc b/source/blender/compositor/operations/COM_QualityStepHelper.cc index 77d5afa93e0..a45c7ab552c 100644 --- a/source/blender/compositor/operations/COM_QualityStepHelper.cc +++ b/source/blender/compositor/operations/COM_QualityStepHelper.cc @@ -22,45 +22,45 @@ namespace blender::compositor { QualityStepHelper::QualityStepHelper() { - m_quality = eCompositorQuality::High; - m_step = 1; - m_offsetadd = 4; + quality_ = eCompositorQuality::High; + step_ = 1; + offsetadd_ = 4; } void QualityStepHelper::initExecution(QualityHelper helper) { switch (helper) { case COM_QH_INCREASE: - switch (m_quality) { + switch (quality_) { case eCompositorQuality::High: default: - m_step = 1; - m_offsetadd = 1; + step_ = 1; + offsetadd_ = 1; break; case eCompositorQuality::Medium: - m_step = 2; - m_offsetadd = 2; + step_ = 2; + offsetadd_ = 2; break; case eCompositorQuality::Low: - m_step = 3; - m_offsetadd = 3; + step_ = 3; + offsetadd_ = 3; break; } break; case COM_QH_MULTIPLY: - switch (m_quality) { + switch (quality_) { case eCompositorQuality::High: default: - m_step = 1; - m_offsetadd = 4; + step_ = 1; + offsetadd_ = 4; break; case eCompositorQuality::Medium: - m_step = 2; - m_offsetadd = 8; + step_ = 2; + offsetadd_ = 8; break; case eCompositorQuality::Low: - m_step = 4; - m_offsetadd = 16; + step_ = 4; + offsetadd_ = 16; break; } break; diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.h b/source/blender/compositor/operations/COM_QualityStepHelper.h index 08ebf33cdeb..5c59f4d2a37 100644 --- a/source/blender/compositor/operations/COM_QualityStepHelper.h +++ b/source/blender/compositor/operations/COM_QualityStepHelper.h @@ -29,9 +29,9 @@ typedef enum QualityHelper { class QualityStepHelper { private: - eCompositorQuality m_quality; - int m_step; - int m_offsetadd; + eCompositorQuality quality_; + int step_; + int offsetadd_; protected: /** @@ -41,11 +41,11 @@ class QualityStepHelper { inline int getStep() const { - return m_step; + return step_; } inline int getOffsetAdd() const { - return m_offsetadd; + return offsetadd_; } public: @@ -53,7 +53,7 @@ class QualityStepHelper { void setQuality(eCompositorQuality quality) { - m_quality = quality; + quality_ = quality; } }; diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cc b/source/blender/compositor/operations/COM_ReadBufferOperation.cc index 4e542488eb0..9fe800021e7 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.cc +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cc @@ -26,32 +26,32 @@ namespace blender::compositor { ReadBufferOperation::ReadBufferOperation(DataType datatype) { this->addOutputSocket(datatype); - m_single_value = false; - m_offset = 0; - m_buffer = nullptr; + single_value_ = false; + offset_ = 0; + buffer_ = nullptr; flags.is_read_buffer_operation = true; } void *ReadBufferOperation::initializeTileData(rcti * /*rect*/) { - return m_buffer; + return buffer_; } void ReadBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (m_memoryProxy != nullptr) { - WriteBufferOperation *operation = m_memoryProxy->getWriteBufferOperation(); + if (memoryProxy_ != nullptr) { + WriteBufferOperation *operation = memoryProxy_->getWriteBufferOperation(); operation->determine_canvas(preferred_area, r_area); operation->set_canvas(r_area); /** \todo may not occur! But does with blur node. */ - if (m_memoryProxy->getExecutor()) { + if (memoryProxy_->getExecutor()) { uint resolution[2] = {static_cast(BLI_rcti_size_x(&r_area)), static_cast(BLI_rcti_size_y(&r_area))}; - m_memoryProxy->getExecutor()->setResolution(resolution); + memoryProxy_->getExecutor()->setResolution(resolution); } - m_single_value = operation->isSingleValue(); + single_value_ = operation->isSingleValue(); } } void ReadBufferOperation::executePixelSampled(float output[4], @@ -59,21 +59,21 @@ void ReadBufferOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - if (m_single_value) { + if (single_value_) { /* write buffer has a single value stored at (0,0) */ - m_buffer->read(output, 0, 0); + buffer_->read(output, 0, 0); } else { switch (sampler) { case PixelSampler::Nearest: - m_buffer->read(output, x, y); + buffer_->read(output, x, y); break; case PixelSampler::Bilinear: default: - m_buffer->readBilinear(output, x, y); + buffer_->readBilinear(output, x, y); break; case PixelSampler::Bicubic: - m_buffer->readBilinear(output, x, y); + buffer_->readBilinear(output, x, y); break; } } @@ -86,29 +86,29 @@ void ReadBufferOperation::executePixelExtend(float output[4], MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) { - if (m_single_value) { + if (single_value_) { /* write buffer has a single value stored at (0,0) */ - m_buffer->read(output, 0, 0); + buffer_->read(output, 0, 0); } else if (sampler == PixelSampler::Nearest) { - m_buffer->read(output, x, y, extend_x, extend_y); + buffer_->read(output, x, y, extend_x, extend_y); } else { - m_buffer->readBilinear(output, x, y, extend_x, extend_y); + buffer_->readBilinear(output, x, y, extend_x, extend_y); } } void ReadBufferOperation::executePixelFiltered( float output[4], float x, float y, float dx[2], float dy[2]) { - if (m_single_value) { + if (single_value_) { /* write buffer has a single value stored at (0,0) */ - m_buffer->read(output, 0, 0); + buffer_->read(output, 0, 0); } else { const float uv[2] = {x, y}; const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; - m_buffer->readEWA(output, uv, deriv); + buffer_->readEWA(output, uv, deriv); } } @@ -125,8 +125,8 @@ bool ReadBufferOperation::determineDependingAreaOfInterest(rcti *input, void ReadBufferOperation::readResolutionFromWriteBuffer() { - if (m_memoryProxy != nullptr) { - WriteBufferOperation *operation = m_memoryProxy->getWriteBufferOperation(); + if (memoryProxy_ != nullptr) { + WriteBufferOperation *operation = memoryProxy_->getWriteBufferOperation(); this->setWidth(operation->getWidth()); this->setHeight(operation->getHeight()); } @@ -134,7 +134,7 @@ void ReadBufferOperation::readResolutionFromWriteBuffer() void ReadBufferOperation::updateMemoryBuffer() { - m_buffer = this->getMemoryProxy()->getBuffer(); + buffer_ = this->getMemoryProxy()->getBuffer(); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.h b/source/blender/compositor/operations/COM_ReadBufferOperation.h index d4a8fe19761..f4504e11f48 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.h +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.h @@ -26,21 +26,21 @@ namespace blender::compositor { class ReadBufferOperation : public NodeOperation { private: - MemoryProxy *m_memoryProxy; - bool m_single_value; /* single value stored in buffer, copied from associated write operation */ - unsigned int m_offset; - MemoryBuffer *m_buffer; + MemoryProxy *memoryProxy_; + bool single_value_; /* single value stored in buffer, copied from associated write operation */ + unsigned int offset_; + MemoryBuffer *buffer_; public: ReadBufferOperation(DataType datatype); void setMemoryProxy(MemoryProxy *memoryProxy) { - m_memoryProxy = memoryProxy; + memoryProxy_ = memoryProxy; } MemoryProxy *getMemoryProxy() const { - return m_memoryProxy; + return memoryProxy_; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; @@ -56,18 +56,18 @@ class ReadBufferOperation : public NodeOperation { void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; void setOffset(unsigned int offset) { - m_offset = offset; + offset_ = offset; } unsigned int getOffset() const { - return m_offset; + return offset_; } bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; MemoryBuffer *getInputMemoryBuffer(MemoryBuffer **memoryBuffers) override { - return memoryBuffers[m_offset]; + return memoryBuffers[offset_]; } void readResolutionFromWriteBuffer(); void updateMemoryBuffer(); diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc index 1291c85b4ad..02a278899bb 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cc +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc @@ -25,12 +25,12 @@ namespace blender::compositor { /* ******** Render Layers Base Prog ******** */ RenderLayersProg::RenderLayersProg(const char *passName, DataType type, int elementsize) - : m_passName(passName) + : passName_(passName) { this->setScene(nullptr); - m_inputBuffer = nullptr; - m_elementsize = elementsize; - m_rd = nullptr; + inputBuffer_ = nullptr; + elementsize_ = elementsize; + rd_ = nullptr; layer_buffer_ = nullptr; this->addOutputSocket(type); @@ -52,9 +52,9 @@ void RenderLayersProg::initExecution() RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name); if (rl) { - m_inputBuffer = RE_RenderLayerGetPass(rl, m_passName.c_str(), m_viewName); - if (m_inputBuffer) { - layer_buffer_ = new MemoryBuffer(m_inputBuffer, m_elementsize, getWidth(), getHeight()); + inputBuffer_ = RE_RenderLayerGetPass(rl, passName_.c_str(), viewName_); + if (inputBuffer_) { + layer_buffer_ = new MemoryBuffer(inputBuffer_, elementsize_, getWidth(), getHeight()); } } } @@ -72,10 +72,10 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS int ix = x, iy = y; if (ix < 0 || iy < 0 || ix >= width || iy >= height) { - if (m_elementsize == 1) { + if (elementsize_ == 1) { output[0] = 0.0f; } - else if (m_elementsize == 3) { + else if (elementsize_ == 3) { zero_v3(output); } else { @@ -86,26 +86,26 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS switch (sampler) { case PixelSampler::Nearest: { - offset = (iy * width + ix) * m_elementsize; + offset = (iy * width + ix) * elementsize_; - if (m_elementsize == 1) { - output[0] = m_inputBuffer[offset]; + if (elementsize_ == 1) { + output[0] = inputBuffer_[offset]; } - else if (m_elementsize == 3) { - copy_v3_v3(output, &m_inputBuffer[offset]); + else if (elementsize_ == 3) { + copy_v3_v3(output, &inputBuffer_[offset]); } else { - copy_v4_v4(output, &m_inputBuffer[offset]); + copy_v4_v4(output, &inputBuffer_[offset]); } break; } case PixelSampler::Bilinear: - BLI_bilinear_interpolation_fl(m_inputBuffer, output, width, height, m_elementsize, x, y); + BLI_bilinear_interpolation_fl(inputBuffer_, output, width, height, elementsize_, x, y); break; case PixelSampler::Bicubic: - BLI_bicubic_interpolation_fl(m_inputBuffer, output, width, height, m_elementsize, x, y); + BLI_bicubic_interpolation_fl(inputBuffer_, output, width, height, elementsize_, x, y); break; } } @@ -113,7 +113,7 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS void RenderLayersProg::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { #if 0 - const RenderData *rd = m_rd; + const RenderData *rd = rd_; int dx = 0, dy = 0; @@ -135,7 +135,7 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi #ifndef NDEBUG { const DataType data_type = this->getOutputSocket()->getDataType(); - int actual_element_size = m_elementsize; + int actual_element_size = elementsize_; int expected_element_size; if (data_type == DataType::Value) { expected_element_size = 1; @@ -154,8 +154,8 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi } #endif - if (m_inputBuffer == nullptr) { - int elemsize = m_elementsize; + if (inputBuffer_ == nullptr) { + int elemsize = elementsize_; if (elemsize == 1) { output[0] = 0.0f; } @@ -174,7 +174,7 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi void RenderLayersProg::deinitExecution() { - m_inputBuffer = nullptr; + inputBuffer_ = nullptr; if (layer_buffer_) { delete layer_buffer_; layer_buffer_ = nullptr; @@ -225,7 +225,7 @@ std::unique_ptr RenderLayersProg::getMetaData() std::string full_layer_name = std::string( view_layer->name, BLI_strnlen(view_layer->name, sizeof(view_layer->name))) + - "." + m_passName; + "." + passName_; blender::StringRef cryptomatte_layer_name = blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name(full_layer_name); callback_data.setCryptomatteKeys(cryptomatte_layer_name); @@ -249,13 +249,13 @@ void RenderLayersProg::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - BLI_assert(output->get_num_channels() >= m_elementsize); + BLI_assert(output->get_num_channels() >= elementsize_); if (layer_buffer_) { - output->copy_from(layer_buffer_, area, 0, m_elementsize, 0); + output->copy_from(layer_buffer_, area, 0, elementsize_, 0); } else { - std::unique_ptr zero_elem = std::make_unique(m_elementsize); - output->fill(area, 0, zero_elem.get(), m_elementsize); + std::unique_ptr zero_elem = std::make_unique(elementsize_); + output->fill(area, 0, zero_elem.get(), elementsize_); } } @@ -280,7 +280,7 @@ void RenderLayersAOOperation::update_memory_buffer_partial(MemoryBuffer *output, Span UNUSED(inputs)) { BLI_assert(output->get_num_channels() == COM_DATA_TYPE_COLOR_CHANNELS); - BLI_assert(m_elementsize == COM_DATA_TYPE_COLOR_CHANNELS); + BLI_assert(elementsize_ == COM_DATA_TYPE_COLOR_CHANNELS); if (layer_buffer_) { output->copy_from(layer_buffer_, area, 0, COM_DATA_TYPE_VECTOR_CHANNELS, 0); } @@ -313,7 +313,7 @@ void RenderLayersAlphaProg::update_memory_buffer_partial(MemoryBuffer *output, Span UNUSED(inputs)) { BLI_assert(output->get_num_channels() == COM_DATA_TYPE_VALUE_CHANNELS); - BLI_assert(m_elementsize == COM_DATA_TYPE_COLOR_CHANNELS); + BLI_assert(elementsize_ == COM_DATA_TYPE_COLOR_CHANNELS); if (layer_buffer_) { output->copy_from(layer_buffer_, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); } @@ -347,7 +347,7 @@ void RenderLayersDepthProg::update_memory_buffer_partial(MemoryBuffer *output, Span UNUSED(inputs)) { BLI_assert(output->get_num_channels() == COM_DATA_TYPE_VALUE_CHANNELS); - BLI_assert(m_elementsize == COM_DATA_TYPE_VALUE_CHANNELS); + BLI_assert(elementsize_ == COM_DATA_TYPE_VALUE_CHANNELS); if (layer_buffer_) { output->copy_from(layer_buffer_, area); } diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h index 0be21d8aad7..14fc3babe32 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.h +++ b/source/blender/compositor/operations/COM_RenderLayersProg.h @@ -38,17 +38,17 @@ class RenderLayersProg : public MultiThreadedOperation { /** * Reference to the scene object. */ - Scene *m_scene; + Scene *scene_; /** * layerId of the layer where this operation needs to get its data from */ - short m_layerId; + short layerId_; /** * viewName of the view to use (unless another view is specified by the node */ - const char *m_viewName; + const char *viewName_; const MemoryBuffer *layer_buffer_; @@ -56,19 +56,19 @@ class RenderLayersProg : public MultiThreadedOperation { * Cached instance to the float buffer inside the layer. * TODO: To be removed with tiled implementation. */ - float *m_inputBuffer; + float *inputBuffer_; /** * Render-pass where this operation needs to get its data from. */ - std::string m_passName; + std::string passName_; - int m_elementsize; + int elementsize_; /** * \brief render data used for active rendering */ - const RenderData *m_rd; + const RenderData *rd_; /** * Determine the output resolution. The resolution is retrieved from the Renderer @@ -80,7 +80,7 @@ class RenderLayersProg : public MultiThreadedOperation { */ inline float *getInputBuffer() { - return m_inputBuffer; + return inputBuffer_; } void doInterpolation(float output[4], float x, float y, PixelSampler sampler); @@ -97,31 +97,31 @@ class RenderLayersProg : public MultiThreadedOperation { */ void setScene(Scene *scene) { - m_scene = scene; + scene_ = scene; } Scene *getScene() const { - return m_scene; + return scene_; } void setRenderData(const RenderData *rd) { - m_rd = rd; + rd_ = rd; } void setLayerId(short layerId) { - m_layerId = layerId; + layerId_ = layerId; } short getLayerId() const { - return m_layerId; + return layerId_; } void setViewName(const char *viewName) { - m_viewName = viewName; + viewName_ = viewName; } const char *getViewName() { - return m_viewName; + return viewName_; } void initExecution() override; void deinitExecution() override; diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc index e32005d3c8d..9cef80c14b9 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.cc +++ b/source/blender/compositor/operations/COM_RotateOperation.cc @@ -26,10 +26,10 @@ RotateOperation::RotateOperation() this->addInputSocket(DataType::Value, ResizeMode::None); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - m_imageSocket = nullptr; - m_degreeSocket = nullptr; - m_doDegree2RadConversion = false; - m_isDegreeSet = false; + imageSocket_ = nullptr; + degreeSocket_ = nullptr; + doDegree2RadConversion_ = false; + isDegreeSet_ = false; sampler_ = PixelSampler::Bilinear; } @@ -127,29 +127,29 @@ void RotateOperation::get_rotation_canvas(const rcti &input_canvas, void RotateOperation::init_data() { if (execution_model_ == eExecutionModel::Tiled) { - get_rotation_center(get_canvas(), m_centerX, m_centerY); + get_rotation_center(get_canvas(), centerX_, centerY_); } } void RotateOperation::initExecution() { - m_imageSocket = this->getInputSocketReader(0); - m_degreeSocket = this->getInputSocketReader(1); + imageSocket_ = this->getInputSocketReader(0); + degreeSocket_ = this->getInputSocketReader(1); } void RotateOperation::deinitExecution() { - m_imageSocket = nullptr; - m_degreeSocket = nullptr; + imageSocket_ = nullptr; + degreeSocket_ = nullptr; } inline void RotateOperation::ensureDegree() { - if (!m_isDegreeSet) { + if (!isDegreeSet_) { float degree[4]; switch (execution_model_) { case eExecutionModel::Tiled: - m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest); + degreeSocket_->readSampled(degree, 0, 0, PixelSampler::Nearest); break; case eExecutionModel::FullFrame: degree[0] = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f); @@ -157,27 +157,27 @@ inline void RotateOperation::ensureDegree() } double rad; - if (m_doDegree2RadConversion) { + if (doDegree2RadConversion_) { rad = DEG2RAD((double)degree[0]); } else { rad = degree[0]; } - m_cosine = cos(rad); - m_sine = sin(rad); + cosine_ = cos(rad); + sine_ = sin(rad); - m_isDegreeSet = true; + isDegreeSet_ = true; } } void RotateOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { ensureDegree(); - const float dy = y - m_centerY; - const float dx = x - m_centerX; - const float nx = m_centerX + (m_cosine * dx + m_sine * dy); - const float ny = m_centerY + (-m_sine * dx + m_cosine * dy); - m_imageSocket->readSampled(output, nx, ny, sampler); + const float dy = y - centerY_; + const float dx = x - centerX_; + const float nx = centerX_ + (cosine_ * dx + sine_ * dy); + const float ny = centerY_ + (-sine_ * dx + cosine_ * dy); + imageSocket_->readSampled(output, nx, ny, sampler); } bool RotateOperation::determineDependingAreaOfInterest(rcti *input, @@ -187,19 +187,19 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input, ensureDegree(); rcti newInput; - const float dxmin = input->xmin - m_centerX; - const float dymin = input->ymin - m_centerY; - const float dxmax = input->xmax - m_centerX; - const float dymax = input->ymax - m_centerY; + const float dxmin = input->xmin - centerX_; + const float dymin = input->ymin - centerY_; + const float dxmax = input->xmax - centerX_; + const float dymax = input->ymax - centerY_; - const float x1 = m_centerX + (m_cosine * dxmin + m_sine * dymin); - const float x2 = m_centerX + (m_cosine * dxmax + m_sine * dymin); - const float x3 = m_centerX + (m_cosine * dxmin + m_sine * dymax); - const float x4 = m_centerX + (m_cosine * dxmax + m_sine * dymax); - const float y1 = m_centerY + (-m_sine * dxmin + m_cosine * dymin); - const float y2 = m_centerY + (-m_sine * dxmax + m_cosine * dymin); - const float y3 = m_centerY + (-m_sine * dxmin + m_cosine * dymax); - const float y4 = m_centerY + (-m_sine * dxmax + m_cosine * dymax); + const float x1 = centerX_ + (cosine_ * dxmin + sine_ * dymin); + const float x2 = centerX_ + (cosine_ * dxmax + sine_ * dymin); + const float x3 = centerX_ + (cosine_ * dxmin + sine_ * dymax); + const float x4 = centerX_ + (cosine_ * dxmax + sine_ * dymax); + const float y1 = centerY_ + (-sine_ * dxmin + cosine_ * dymin); + const float y2 = centerY_ + (-sine_ * dxmax + cosine_ * dymin); + const float y3 = centerY_ + (-sine_ * dxmin + cosine_ * dymax); + const float y4 = centerY_ + (-sine_ * dxmax + cosine_ * dymax); const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4))); const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4))); const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4))); @@ -229,7 +229,7 @@ void RotateOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) ensureDegree(); - get_rotation_canvas(input_canvas, m_sine, m_cosine, r_area); + get_rotation_canvas(input_canvas, sine_, cosine_, r_area); } } @@ -246,7 +246,7 @@ void RotateOperation::get_area_of_interest(const int input_idx, const rcti &input_image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas(); get_rotation_area_of_interest( - input_image_canvas, this->get_canvas(), m_sine, m_cosine, output_area, r_input_area); + input_image_canvas, this->get_canvas(), sine_, cosine_, output_area, r_input_area); expand_area_for_sampler(r_input_area, sampler_); } @@ -266,7 +266,7 @@ void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output, for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { float x = rotate_offset_x + it.x + canvas_.xmin; float y = rotate_offset_y + it.y + canvas_.ymin; - rotate_coords(x, y, center_x, center_y, m_sine, m_cosine); + rotate_coords(x, y, center_x, center_y, sine_, cosine_); input_img->read_elem_sampled(x - canvas_.xmin, y - canvas_.ymin, sampler_, it.out); } } diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h index 314e0f6076f..d99c914bed7 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.h +++ b/source/blender/compositor/operations/COM_RotateOperation.h @@ -27,16 +27,16 @@ class RotateOperation : public MultiThreadedOperation { constexpr static int IMAGE_INPUT_INDEX = 0; constexpr static int DEGREE_INPUT_INDEX = 1; - SocketReader *m_imageSocket; - SocketReader *m_degreeSocket; + SocketReader *imageSocket_; + SocketReader *degreeSocket_; /* TODO(manzanilla): to be removed with tiled implementation. */ - float m_centerX; - float m_centerY; + float centerX_; + float centerY_; - float m_cosine; - float m_sine; - bool m_doDegree2RadConversion; - bool m_isDegreeSet; + float cosine_; + float sine_; + bool doDegree2RadConversion_; + bool isDegreeSet_; PixelSampler sampler_; public: @@ -89,7 +89,7 @@ class RotateOperation : public MultiThreadedOperation { void setDoDegree2RadConversion(bool abool) { - m_doDegree2RadConversion = abool; + doDegree2RadConversion_ = abool; } void set_sampler(PixelSampler sampler) diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index eb5db8d1698..40af7389d7f 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -171,34 +171,34 @@ SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation() this->addInputSocket(DataType::Value); /* Depth, material ID, etc. TODO: currently unused. */ this->addOutputSocket(DataType::Color); this->flags.complex = true; - m_imageReader = nullptr; - m_valueReader = nullptr; + imageReader_ = nullptr; + valueReader_ = nullptr; this->setThreshold(CMP_DEFAULT_SMAA_THRESHOLD); this->setLocalContrastAdaptationFactor(CMP_DEFAULT_SMAA_CONTRAST_LIMIT); } void SMAAEdgeDetectionOperation::initExecution() { - m_imageReader = this->getInputSocketReader(0); - m_valueReader = this->getInputSocketReader(1); + imageReader_ = this->getInputSocketReader(0); + valueReader_ = this->getInputSocketReader(1); } void SMAAEdgeDetectionOperation::deinitExecution() { - m_imageReader = nullptr; - m_valueReader = nullptr; + imageReader_ = nullptr; + valueReader_ = nullptr; } void SMAAEdgeDetectionOperation::setThreshold(float threshold) { /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 0.5 */ - m_threshold = scalenorm(0, 0.5, threshold); + threshold_ = scalenorm(0, 0.5, threshold); } void SMAAEdgeDetectionOperation::setLocalContrastAdaptationFactor(float factor) { /* UI values are between 0 and 1 for simplicity but algorithm expects values between 1 and 10 */ - m_contrast_limit = scalenorm(1, 10, factor); + contrast_limit_ = scalenorm(1, 10, factor); } bool SMAAEdgeDetectionOperation::determineDependingAreaOfInterest( @@ -228,18 +228,18 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi float color[4]; /* Calculate luma deltas: */ - sample(m_imageReader, x, y, color); + sample(imageReader_, x, y, color); float L = IMB_colormanagement_get_luminance(color); - sample(m_imageReader, x - 1, y, color); + sample(imageReader_, x - 1, y, color); float Lleft = IMB_colormanagement_get_luminance(color); - sample(m_imageReader, x, y - 1, color); + sample(imageReader_, x, y - 1, color); float Ltop = IMB_colormanagement_get_luminance(color); float Dleft = fabsf(L - Lleft); float Dtop = fabsf(L - Ltop); /* We do the usual threshold: */ - output[0] = (x > 0 && Dleft >= m_threshold) ? 1.0f : 0.0f; - output[1] = (y > 0 && Dtop >= m_threshold) ? 1.0f : 0.0f; + output[0] = (x > 0 && Dleft >= threshold_) ? 1.0f : 0.0f; + output[1] = (y > 0 && Dtop >= threshold_) ? 1.0f : 0.0f; output[2] = 0.0f; output[3] = 1.0f; @@ -249,9 +249,9 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi } /* Calculate right and bottom deltas: */ - sample(m_imageReader, x + 1, y, color); + sample(imageReader_, x + 1, y, color); float Lright = IMB_colormanagement_get_luminance(color); - sample(m_imageReader, x, y + 1, color); + sample(imageReader_, x, y + 1, color); float Lbottom = IMB_colormanagement_get_luminance(color); float Dright = fabsf(L - Lright); float Dbottom = fabsf(L - Lbottom); @@ -260,15 +260,15 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); /* Calculate luma used for both left and top edges: */ - sample(m_imageReader, x - 1, y - 1, color); + sample(imageReader_, x - 1, y - 1, color); float Llefttop = IMB_colormanagement_get_luminance(color); /* Left edge */ if (output[0] != 0.0f) { /* Calculate deltas around the left pixel: */ - sample(m_imageReader, x - 2, y, color); + sample(imageReader_, x - 2, y, color); float Lleftleft = IMB_colormanagement_get_luminance(color); - sample(m_imageReader, x - 1, y + 1, color); + sample(imageReader_, x - 1, y + 1, color); float Lleftbottom = IMB_colormanagement_get_luminance(color); float Dleftleft = fabsf(Lleft - Lleftleft); float Dlefttop = fabsf(Lleft - Llefttop); @@ -278,7 +278,7 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); /* Local contrast adaptation: */ - if (maxDelta > m_contrast_limit * Dleft) { + if (maxDelta > contrast_limit_ * Dleft) { output[0] = 0.0f; } } @@ -286,9 +286,9 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi /* Top edge */ if (output[1] != 0.0f) { /* Calculate top-top delta: */ - sample(m_imageReader, x, y - 2, color); + sample(imageReader_, x, y - 2, color); float Ltoptop = IMB_colormanagement_get_luminance(color); - sample(m_imageReader, x + 1, y - 1, color); + sample(imageReader_, x + 1, y - 1, color); float Ltopright = IMB_colormanagement_get_luminance(color); float Dtoptop = fabsf(Ltop - Ltoptop); float Dtopleft = fabsf(Ltop - Llefttop); @@ -298,7 +298,7 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); /* Local contrast adaptation: */ - if (maxDelta > m_contrast_limit * Dtop) { + if (maxDelta > contrast_limit_ * Dtop) { output[1] = 0.0f; } } @@ -325,8 +325,8 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp const float Dtop = fabsf(L - Ltop); /* We do the usual threshold: */ - it.out[0] = (x > 0 && Dleft >= m_threshold) ? 1.0f : 0.0f; - it.out[1] = (y > 0 && Dtop >= m_threshold) ? 1.0f : 0.0f; + it.out[0] = (x > 0 && Dleft >= threshold_) ? 1.0f : 0.0f; + it.out[1] = (y > 0 && Dtop >= threshold_) ? 1.0f : 0.0f; it.out[2] = 0.0f; it.out[3] = 1.0f; @@ -365,7 +365,7 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); /* Local contrast adaptation: */ - if (maxDelta > m_contrast_limit * Dleft) { + if (maxDelta > contrast_limit_ * Dleft) { it.out[0] = 0.0f; } } @@ -385,7 +385,7 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); /* Local contrast adaptation: */ - if (maxDelta > m_contrast_limit * Dtop) { + if (maxDelta > contrast_limit_ * Dtop) { it.out[1] = 0.0f; } } @@ -401,7 +401,7 @@ SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation() this->addInputSocket(DataType::Color); /* edges */ this->addOutputSocket(DataType::Color); this->flags.complex = true; - m_imageReader = nullptr; + imageReader_ = nullptr; this->setCornerRounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING); } @@ -412,16 +412,16 @@ void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect) void SMAABlendingWeightCalculationOperation::initExecution() { - m_imageReader = this->getInputSocketReader(0); + imageReader_ = this->getInputSocketReader(0); if (execution_model_ == eExecutionModel::Tiled) { - sample_image_fn_ = [=](int x, int y, float *out) { sample(m_imageReader, x, y, out); }; + sample_image_fn_ = [=](int x, int y, float *out) { sample(imageReader_, x, y, out); }; } } void SMAABlendingWeightCalculationOperation::setCornerRounding(float rounding) { /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 100 */ - m_corner_rounding = static_cast(scalenorm(0, 100, rounding)); + corner_rounding_ = static_cast(scalenorm(0, 100, rounding)); } void SMAABlendingWeightCalculationOperation::executePixel(float output[4], @@ -432,7 +432,7 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], float edges[4], c[4]; zero_v4(output); - sample(m_imageReader, x, y, edges); + sample(imageReader_, x, y, edges); /* Edge at north */ if (edges[1] > 0.0f) { @@ -453,19 +453,19 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], /* Fetch the left and right crossing edges: */ int e1 = 0, e2 = 0; - sample(m_imageReader, left, y - 1, c); + sample(imageReader_, left, y - 1, c); if (c[0] > 0.0) { e1 += 1; } - sample(m_imageReader, left, y, c); + sample(imageReader_, left, y, c); if (c[0] > 0.0) { e1 += 2; } - sample(m_imageReader, right + 1, y - 1, c); + sample(imageReader_, right + 1, y - 1, c); if (c[0] > 0.0) { e2 += 1; } - sample(m_imageReader, right + 1, y, c); + sample(imageReader_, right + 1, y, c); if (c[0] > 0.0) { e2 += 2; } @@ -475,7 +475,7 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], area(d1, d2, e1, e2, output); /* R, G */ /* Fix corners: */ - if (m_corner_rounding) { + if (corner_rounding_) { detectHorizontalCornerPattern(output, left, right, y, d1, d2); } } @@ -494,19 +494,19 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], /* Fetch the top and bottom crossing edges: */ int e1 = 0, e2 = 0; - sample(m_imageReader, x - 1, top, c); + sample(imageReader_, x - 1, top, c); if (c[1] > 0.0) { e1 += 1; } - sample(m_imageReader, x, top, c); + sample(imageReader_, x, top, c); if (c[1] > 0.0) { e1 += 2; } - sample(m_imageReader, x - 1, bottom + 1, c); + sample(imageReader_, x - 1, bottom + 1, c); if (c[1] > 0.0) { e2 += 1; } - sample(m_imageReader, x, bottom + 1, c); + sample(imageReader_, x, bottom + 1, c); if (c[1] > 0.0) { e2 += 2; } @@ -515,7 +515,7 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], area(d1, d2, e1, e2, output + 2); /* B, A */ /* Fix corners: */ - if (m_corner_rounding) { + if (corner_rounding_) { detectVerticalCornerPattern(output + 2, x, top, bottom, d1, d2); } } @@ -581,7 +581,7 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( area(d1, d2, e1, e2, it.out); /* R, G */ /* Fix corners: */ - if (m_corner_rounding) { + if (corner_rounding_) { detectHorizontalCornerPattern(it.out, left, right, y, d1, d2); } } @@ -621,7 +621,7 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( area(d1, d2, e1, e2, it.out + 2); /* B, A */ /* Fix corners: */ - if (m_corner_rounding) { + if (corner_rounding_) { detectVerticalCornerPattern(it.out + 2, x, top, bottom, d1, d2); } } @@ -630,7 +630,7 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( void SMAABlendingWeightCalculationOperation::deinitExecution() { - m_imageReader = nullptr; + imageReader_ = nullptr; } bool SMAABlendingWeightCalculationOperation::determineDependingAreaOfInterest( @@ -947,7 +947,7 @@ void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern( float weights[2], int left, int right, int y, int d1, int d2) { float factor[2] = {1.0f, 1.0f}; - float rounding = m_corner_rounding / 100.0f; + float rounding = corner_rounding_ / 100.0f; float e[4]; /* Reduce blending for pixels in the center of a line. */ @@ -976,7 +976,7 @@ void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern( float weights[2], int x, int top, int bottom, int d1, int d2) { float factor[2] = {1.0f, 1.0f}; - float rounding = m_corner_rounding / 100.0f; + float rounding = corner_rounding_ / 100.0f; float e[4]; /* Reduce blending for pixels in the center of a line. */ @@ -1011,8 +1011,8 @@ SMAANeighborhoodBlendingOperation::SMAANeighborhoodBlendingOperation() this->addInputSocket(DataType::Color); /* blend */ this->addOutputSocket(DataType::Color); this->flags.complex = true; - m_image1Reader = nullptr; - m_image2Reader = nullptr; + image1Reader_ = nullptr; + image2Reader_ = nullptr; } void *SMAANeighborhoodBlendingOperation::initializeTileData(rcti *rect) @@ -1022,8 +1022,8 @@ void *SMAANeighborhoodBlendingOperation::initializeTileData(rcti *rect) void SMAANeighborhoodBlendingOperation::initExecution() { - m_image1Reader = this->getInputSocketReader(0); - m_image2Reader = this->getInputSocketReader(1); + image1Reader_ = this->getInputSocketReader(0); + image2Reader_ = this->getInputSocketReader(1); } void SMAANeighborhoodBlendingOperation::executePixel(float output[4], @@ -1034,16 +1034,16 @@ void SMAANeighborhoodBlendingOperation::executePixel(float output[4], float w[4]; /* Fetch the blending weights for current pixel: */ - sample(m_image2Reader, x, y, w); + sample(image2Reader_, x, y, w); float left = w[2], top = w[0]; - sample(m_image2Reader, x + 1, y, w); + sample(image2Reader_, x + 1, y, w); float right = w[3]; - sample(m_image2Reader, x, y + 1, w); + sample(image2Reader_, x, y + 1, w); float bottom = w[1]; /* Is there any blending weight with a value greater than 0.0? */ if (right + bottom + left + top < 1e-5f) { - sample(m_image1Reader, x, y, output); + sample(image1Reader_, x, y, output); return; } @@ -1067,8 +1067,8 @@ void SMAANeighborhoodBlendingOperation::executePixel(float output[4], } /* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */ - samplefunc(m_image1Reader, x, y, offset1, color1); - samplefunc(m_image1Reader, x, y, offset2, color2); + samplefunc(image1Reader_, x, y, offset1, color1); + samplefunc(image1Reader_, x, y, offset2, color2); mul_v4_v4fl(output, color1, weight1); madd_v4_v4fl(output, color2, weight2); @@ -1129,8 +1129,8 @@ void SMAANeighborhoodBlendingOperation::update_memory_buffer_partial(MemoryBuffe void SMAANeighborhoodBlendingOperation::deinitExecution() { - m_image1Reader = nullptr; - m_image2Reader = nullptr; + image1Reader_ = nullptr; + image2Reader_ = nullptr; } bool SMAANeighborhoodBlendingOperation::determineDependingAreaOfInterest( diff --git a/source/blender/compositor/operations/COM_SMAAOperation.h b/source/blender/compositor/operations/COM_SMAAOperation.h index 91b9299ee43..d4c9e128dca 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.h +++ b/source/blender/compositor/operations/COM_SMAAOperation.h @@ -29,11 +29,11 @@ namespace blender::compositor { class SMAAEdgeDetectionOperation : public MultiThreadedOperation { protected: - SocketReader *m_imageReader; - SocketReader *m_valueReader; + SocketReader *imageReader_; + SocketReader *valueReader_; - float m_threshold; - float m_contrast_limit; + float threshold_; + float contrast_limit_; public: SMAAEdgeDetectionOperation(); @@ -72,9 +72,9 @@ class SMAAEdgeDetectionOperation : public MultiThreadedOperation { class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { private: - SocketReader *m_imageReader; + SocketReader *imageReader_; std::function sample_image_fn_; - int m_corner_rounding; + int corner_rounding_; public: SMAABlendingWeightCalculationOperation(); @@ -132,8 +132,8 @@ class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { class SMAANeighborhoodBlendingOperation : public MultiThreadedOperation { private: - SocketReader *m_image1Reader; - SocketReader *m_image2Reader; + SocketReader *image1Reader_; + SocketReader *image2Reader_; public: SMAANeighborhoodBlendingOperation(); diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index 5d0f9a2ad5e..82878c1276f 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -31,11 +31,11 @@ namespace blender::compositor { BaseScaleOperation::BaseScaleOperation() { #ifdef USE_FORCE_BILINEAR - m_sampler = (int)PixelSampler::Bilinear; + sampler_ = (int)PixelSampler::Bilinear; #else - m_sampler = -1; + sampler_ = -1; #endif - m_variable_size = false; + variable_size_ = false; } void BaseScaleOperation::set_scale_canvas_max_size(Size2f size) @@ -53,9 +53,9 @@ ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation() this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addOutputSocket(data_type); - m_inputOperation = nullptr; - m_inputXOperation = nullptr; - m_inputYOperation = nullptr; + inputOperation_ = nullptr; + inputXOperation_ = nullptr; + inputYOperation_ = nullptr; } float ScaleOperation::get_constant_scale(const int input_op_idx, const float factor) @@ -118,16 +118,16 @@ void ScaleOperation::init_data() void ScaleOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); - m_inputXOperation = this->getInputSocketReader(1); - m_inputYOperation = this->getInputSocketReader(2); + inputOperation_ = this->getInputSocketReader(0); + inputXOperation_ = this->getInputSocketReader(1); + inputYOperation_ = this->getInputSocketReader(2); } void ScaleOperation::deinitExecution() { - m_inputOperation = nullptr; - m_inputXOperation = nullptr; - m_inputYOperation = nullptr; + inputOperation_ = nullptr; + inputXOperation_ = nullptr; + inputYOperation_ = nullptr; } void ScaleOperation::get_scale_offset(const rcti &input_canvas, @@ -176,7 +176,7 @@ void ScaleOperation::get_area_of_interest(const int input_idx, get_scale_area_of_interest( image_op->get_canvas(), this->get_canvas(), scale_x, scale_y, output_area, r_input_area); - expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler); + expand_area_for_sampler(r_input_area, (PixelSampler)sampler_); } void ScaleOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -207,7 +207,7 @@ void ScaleOperation::update_memory_buffer_partial(MemoryBuffer *output, from_scale_offset_y + canvas_.ymin + it.y, scale_center_y, rel_scale_y); input_image->read_elem_sampled( - scaled_x - canvas_.xmin, scaled_y - canvas_.ymin, (PixelSampler)m_sampler, it.out); + scaled_x - canvas_.xmin, scaled_y - canvas_.ymin, (PixelSampler)sampler_, it.out); } } @@ -268,15 +268,15 @@ void ScaleRelativeOperation::executePixelSampled(float output[4], float scaleX[4]; float scaleY[4]; - m_inputXOperation->readSampled(scaleX, x, y, effective_sampler); - m_inputYOperation->readSampled(scaleY, x, y, effective_sampler); + inputXOperation_->readSampled(scaleX, x, y, effective_sampler); + inputYOperation_->readSampled(scaleY, x, y, effective_sampler); const float scx = scaleX[0]; const float scy = scaleY[0]; float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / scx; float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / scy; - m_inputOperation->readSampled(output, nx, ny, effective_sampler); + inputOperation_->readSampled(output, nx, ny, effective_sampler); } bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input, @@ -284,12 +284,12 @@ bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input, rcti *output) { rcti newInput; - if (!m_variable_size) { + if (!variable_size_) { float scaleX[4]; float scaleY[4]; - m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest); - m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest); + inputXOperation_->readSampled(scaleX, 0, 0, PixelSampler::Nearest); + inputYOperation_->readSampled(scaleY, 0, 0, PixelSampler::Nearest); const float scx = scaleX[0]; const float scy = scaleY[0]; @@ -318,8 +318,8 @@ void ScaleAbsoluteOperation::executePixelSampled(float output[4], float scaleX[4]; float scaleY[4]; - m_inputXOperation->readSampled(scaleX, x, y, effective_sampler); - m_inputYOperation->readSampled(scaleY, x, y, effective_sampler); + inputXOperation_->readSampled(scaleX, x, y, effective_sampler); + inputYOperation_->readSampled(scaleY, x, y, effective_sampler); const float scx = scaleX[0]; /* Target absolute scale. */ const float scy = scaleY[0]; /* Target absolute scale. */ @@ -333,7 +333,7 @@ void ScaleAbsoluteOperation::executePixelSampled(float output[4], float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / relativeXScale; float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / relativeYScale; - m_inputOperation->readSampled(output, nx, ny, effective_sampler); + inputOperation_->readSampled(output, nx, ny, effective_sampler); } bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input, @@ -341,12 +341,12 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input, rcti *output) { rcti newInput; - if (!m_variable_size) { + if (!variable_size_) { float scaleX[4]; float scaleY[4]; - m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest); - m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest); + inputXOperation_->readSampled(scaleX, 0, 0, PixelSampler::Nearest); + inputYOperation_->readSampled(scaleY, 0, 0, PixelSampler::Nearest); const float scx = scaleX[0]; const float scy = scaleY[0]; @@ -380,78 +380,78 @@ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation() this->addInputSocket(DataType::Color, ResizeMode::None); this->addOutputSocket(DataType::Color); this->set_canvas_input_index(0); - m_inputOperation = nullptr; - m_is_offset = false; + inputOperation_ = nullptr; + is_offset_ = false; } void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) { const int input_width = BLI_rcti_size_x(&input_canvas); const int input_height = BLI_rcti_size_y(&input_canvas); - m_relX = input_width / (float)m_newWidth; - m_relY = input_height / (float)m_newHeight; + relX_ = input_width / (float)newWidth_; + relY_ = input_height / (float)newHeight_; /* *** all the options below are for a fairly special case - camera framing *** */ - if (m_offsetX != 0.0f || m_offsetY != 0.0f) { - m_is_offset = true; + if (offsetX_ != 0.0f || offsetY_ != 0.0f) { + is_offset_ = true; - if (m_newWidth > m_newHeight) { - m_offsetX *= m_newWidth; - m_offsetY *= m_newWidth; + if (newWidth_ > newHeight_) { + offsetX_ *= newWidth_; + offsetY_ *= newWidth_; } else { - m_offsetX *= m_newHeight; - m_offsetY *= m_newHeight; + offsetX_ *= newHeight_; + offsetY_ *= newHeight_; } } - if (m_is_aspect) { + if (is_aspect_) { /* apply aspect from clip */ const float w_src = input_width; const float h_src = input_height; /* destination aspect is already applied from the camera frame */ - const float w_dst = m_newWidth; - const float h_dst = m_newHeight; + const float w_dst = newWidth_; + const float h_dst = newHeight_; const float asp_src = w_src / h_src; const float asp_dst = w_dst / h_dst; if (fabsf(asp_src - asp_dst) >= FLT_EPSILON) { - if ((asp_src > asp_dst) == (m_is_crop == true)) { + if ((asp_src > asp_dst) == (is_crop_ == true)) { /* fit X */ const float div = asp_src / asp_dst; - m_relX /= div; - m_offsetX += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f; - if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) { - int fit_width = m_newWidth * div; + relX_ /= div; + offsetX_ += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f; + if (is_crop_ && execution_model_ == eExecutionModel::FullFrame) { + int fit_width = newWidth_ * div; if (fit_width > max_scale_canvas_size_.x) { fit_width = max_scale_canvas_size_.x; } - const int added_width = fit_width - m_newWidth; - m_newWidth += added_width; - m_offsetX += added_width / 2.0f; + const int added_width = fit_width - newWidth_; + newWidth_ += added_width; + offsetX_ += added_width / 2.0f; } } else { /* fit Y */ const float div = asp_dst / asp_src; - m_relY /= div; - m_offsetY += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f; - if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) { - int fit_height = m_newHeight * div; + relY_ /= div; + offsetY_ += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f; + if (is_crop_ && execution_model_ == eExecutionModel::FullFrame) { + int fit_height = newHeight_ * div; if (fit_height > max_scale_canvas_size_.y) { fit_height = max_scale_canvas_size_.y; } - const int added_height = fit_height - m_newHeight; - m_newHeight += added_height; - m_offsetY += added_height / 2.0f; + const int added_height = fit_height - newHeight_; + newHeight_ += added_height; + offsetY_ += added_height / 2.0f; } } - m_is_offset = true; + is_offset_ = true; } } /* *** end framing options *** */ @@ -459,12 +459,12 @@ void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) void ScaleFixedSizeOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); + inputOperation_ = this->getInputSocketReader(0); } void ScaleFixedSizeOperation::deinitExecution() { - m_inputOperation = nullptr; + inputOperation_ = nullptr; } void ScaleFixedSizeOperation::executePixelSampled(float output[4], @@ -474,13 +474,13 @@ void ScaleFixedSizeOperation::executePixelSampled(float output[4], { PixelSampler effective_sampler = getEffectiveSampler(sampler); - if (m_is_offset) { - float nx = ((x - m_offsetX) * m_relX); - float ny = ((y - m_offsetY) * m_relY); - m_inputOperation->readSampled(output, nx, ny, effective_sampler); + if (is_offset_) { + float nx = ((x - offsetX_) * relX_); + float ny = ((y - offsetY_) * relY_); + inputOperation_->readSampled(output, nx, ny, effective_sampler); } else { - m_inputOperation->readSampled(output, x * m_relX, y * m_relY, effective_sampler); + inputOperation_->readSampled(output, x * relX_, y * relY_, effective_sampler); } } @@ -490,10 +490,10 @@ bool ScaleFixedSizeOperation::determineDependingAreaOfInterest(rcti *input, { rcti newInput; - newInput.xmax = (input->xmax - m_offsetX) * m_relX + 1; - newInput.xmin = (input->xmin - m_offsetX) * m_relX; - newInput.ymax = (input->ymax - m_offsetY) * m_relY + 1; - newInput.ymin = (input->ymin - m_offsetY) * m_relY; + newInput.xmax = (input->xmax - offsetX_) * relX_ + 1; + newInput.xmin = (input->xmin - offsetX_) * relX_; + newInput.ymax = (input->ymax - offsetY_) * relY_ + 1; + newInput.ymin = (input->ymin - offsetY_) * relY_; return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } @@ -501,22 +501,22 @@ bool ScaleFixedSizeOperation::determineDependingAreaOfInterest(rcti *input, void ScaleFixedSizeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { rcti local_preferred = preferred_area; - local_preferred.xmax = local_preferred.xmin + m_newWidth; - local_preferred.ymax = local_preferred.ymin + m_newHeight; + local_preferred.xmax = local_preferred.xmin + newWidth_; + local_preferred.ymax = local_preferred.ymin + newHeight_; rcti input_canvas; const bool input_determined = getInputSocket(0)->determine_canvas(local_preferred, input_canvas); if (input_determined) { init_data(input_canvas); r_area = input_canvas; if (execution_model_ == eExecutionModel::FullFrame) { - r_area.xmin /= m_relX; - r_area.ymin /= m_relY; - r_area.xmin += m_offsetX; - r_area.ymin += m_offsetY; + r_area.xmin /= relX_; + r_area.ymin /= relY_; + r_area.xmin += offsetX_; + r_area.ymin += offsetY_; } - r_area.xmax = r_area.xmin + m_newWidth; - r_area.ymax = r_area.ymin + m_newHeight; + r_area.xmax = r_area.xmin + newWidth_; + r_area.ymax = r_area.ymin + newHeight_; } } @@ -527,11 +527,11 @@ void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx, BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmax = ceilf((output_area.xmax - m_offsetX) * m_relX); - r_input_area.xmin = floorf((output_area.xmin - m_offsetX) * m_relX); - r_input_area.ymax = ceilf((output_area.ymax - m_offsetY) * m_relY); - r_input_area.ymin = floorf((output_area.ymin - m_offsetY) * m_relY); - expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler); + r_input_area.xmax = ceilf((output_area.xmax - offsetX_) * relX_); + r_input_area.xmin = floorf((output_area.xmin - offsetX_) * relX_); + r_input_area.ymax = ceilf((output_area.ymax - offsetY_) * relY_); + r_input_area.ymin = floorf((output_area.ymin - offsetY_) * relY_); + expand_area_for_sampler(r_input_area, (PixelSampler)sampler_); } void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -539,19 +539,19 @@ void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { const MemoryBuffer *input_img = inputs[0]; - PixelSampler sampler = (PixelSampler)m_sampler; + PixelSampler sampler = (PixelSampler)sampler_; BuffersIterator it = output->iterate_with({}, area); - if (m_is_offset) { + if (is_offset_) { for (; !it.is_end(); ++it) { - const float nx = (canvas_.xmin + it.x - m_offsetX) * m_relX; - const float ny = (canvas_.ymin + it.y - m_offsetY) * m_relY; + const float nx = (canvas_.xmin + it.x - offsetX_) * relX_; + const float ny = (canvas_.ymin + it.y - offsetY_) * relY_; input_img->read_elem_sampled(nx - canvas_.xmin, ny - canvas_.ymin, sampler, it.out); } } else { for (; !it.is_end(); ++it) { - input_img->read_elem_sampled((canvas_.xmin + it.x) * m_relX - canvas_.xmin, - (canvas_.ymin + it.y) * m_relY - canvas_.ymin, + input_img->read_elem_sampled((canvas_.xmin + it.x) * relX_ - canvas_.xmin, + (canvas_.ymin + it.y) * relY_ - canvas_.ymin, sampler, it.out); } diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h index 4ba17e2ba20..868a17bc394 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.h +++ b/source/blender/compositor/operations/COM_ScaleOperation.h @@ -29,11 +29,11 @@ class BaseScaleOperation : public MultiThreadedOperation { public: void setSampler(PixelSampler sampler) { - m_sampler = (int)sampler; + sampler_ = (int)sampler; } void setVariableSize(bool variable_size) { - m_variable_size = variable_size; + variable_size_ = variable_size; }; void set_scale_canvas_max_size(Size2f size); @@ -43,13 +43,13 @@ class BaseScaleOperation : public MultiThreadedOperation { PixelSampler getEffectiveSampler(PixelSampler sampler) { - return (m_sampler == -1) ? sampler : (PixelSampler)m_sampler; + return (sampler_ == -1) ? sampler : (PixelSampler)sampler_; } Size2f max_scale_canvas_size_ = {DEFAULT_MAX_SCALE_CANVAS_SIZE, DEFAULT_MAX_SCALE_CANVAS_SIZE}; - int m_sampler; + int sampler_; /* TODO(manzanilla): to be removed with tiled implementation. */ - bool m_variable_size; + bool variable_size_; }; class ScaleOperation : public BaseScaleOperation { @@ -61,9 +61,9 @@ class ScaleOperation : public BaseScaleOperation { static constexpr int X_INPUT_INDEX = 1; static constexpr int Y_INPUT_INDEX = 2; - SocketReader *m_inputOperation; - SocketReader *m_inputXOperation; - SocketReader *m_inputYOperation; + SocketReader *inputOperation_; + SocketReader *inputXOperation_; + SocketReader *inputYOperation_; float canvas_center_x_; float canvas_center_y_; @@ -157,20 +157,20 @@ class ScaleAbsoluteOperation : public ScaleOperation { }; class ScaleFixedSizeOperation : public BaseScaleOperation { - SocketReader *m_inputOperation; - int m_newWidth; - int m_newHeight; - float m_relX; - float m_relY; + SocketReader *inputOperation_; + int newWidth_; + int newHeight_; + float relX_; + float relY_; /* center is only used for aspect correction */ - float m_offsetX; - float m_offsetY; - bool m_is_aspect; - bool m_is_crop; + float offsetX_; + float offsetY_; + bool is_aspect_; + bool is_crop_; /* set from other properties on initialization, * check if we need to apply offset */ - bool m_is_offset; + bool is_offset_; public: ScaleFixedSizeOperation(); @@ -184,24 +184,24 @@ class ScaleFixedSizeOperation : public BaseScaleOperation { void deinitExecution() override; void setNewWidth(int width) { - m_newWidth = width; + newWidth_ = width; } void setNewHeight(int height) { - m_newHeight = height; + newHeight_ = height; } void setIsAspect(bool is_aspect) { - m_is_aspect = is_aspect; + is_aspect_ = is_aspect; } void setIsCrop(bool is_crop) { - m_is_crop = is_crop; + is_crop_ = is_crop; } void setOffset(float x, float y) { - m_offsetX = x; - m_offsetY = y; + offsetX_ = x; + offsetY_ = y; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index ec2fc1aab65..3892d76874c 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -33,49 +33,49 @@ ScreenLensDistortionOperation::ScreenLensDistortionOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->flags.complex = true; - m_inputProgram = nullptr; - m_distortion = 0.0f; - m_dispersion = 0.0f; - m_distortion_const = false; - m_dispersion_const = false; - m_variables_ready = false; + inputProgram_ = nullptr; + distortion_ = 0.0f; + dispersion_ = 0.0f; + distortion_const_ = false; + dispersion_const_ = false; + variables_ready_ = false; } void ScreenLensDistortionOperation::setDistortion(float distortion) { - m_distortion = distortion; - m_distortion_const = true; + distortion_ = distortion; + distortion_const_ = true; } void ScreenLensDistortionOperation::setDispersion(float dispersion) { - m_dispersion = dispersion; - m_dispersion_const = true; + dispersion_ = dispersion; + dispersion_const_ = true; } void ScreenLensDistortionOperation::init_data() { - m_cx = 0.5f * (float)getWidth(); - m_cy = 0.5f * (float)getHeight(); + cx_ = 0.5f * (float)getWidth(); + cy_ = 0.5f * (float)getHeight(); switch (execution_model_) { case eExecutionModel::FullFrame: { NodeOperation *distortion_op = get_input_operation(1); NodeOperation *dispersion_op = get_input_operation(2); - if (!m_distortion_const && distortion_op->get_flags().is_constant_operation) { - m_distortion = static_cast(distortion_op)->get_constant_elem()[0]; + if (!distortion_const_ && distortion_op->get_flags().is_constant_operation) { + distortion_ = static_cast(distortion_op)->get_constant_elem()[0]; } - if (!m_dispersion_const && distortion_op->get_flags().is_constant_operation) { - m_dispersion = static_cast(dispersion_op)->get_constant_elem()[0]; + if (!dispersion_const_ && distortion_op->get_flags().is_constant_operation) { + dispersion_ = static_cast(dispersion_op)->get_constant_elem()[0]; } - updateVariables(m_distortion, m_dispersion); + updateVariables(distortion_, dispersion_); break; } case eExecutionModel::Tiled: { /* If both are constant, init variables once. */ - if (m_distortion_const && m_dispersion_const) { - updateVariables(m_distortion, m_dispersion); - m_variables_ready = true; + if (distortion_const_ && dispersion_const_) { + updateVariables(distortion_, dispersion_); + variables_ready_ = true; } break; } @@ -84,38 +84,38 @@ void ScreenLensDistortionOperation::init_data() void ScreenLensDistortionOperation::initExecution() { - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); this->initMutex(); uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); - rng_seed ^= (uint)POINTER_AS_INT(m_inputProgram); - m_rng = BLI_rng_new(rng_seed); + rng_seed ^= (uint)POINTER_AS_INT(inputProgram_); + rng_ = BLI_rng_new(rng_seed); } void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/) { - void *buffer = m_inputProgram->initializeTileData(nullptr); + void *buffer = inputProgram_->initializeTileData(nullptr); /* get distortion/dispersion values once, by reading inputs at (0,0) * XXX this assumes invariable values (no image inputs), * we don't have a nice generic system for that yet */ - if (!m_variables_ready) { + if (!variables_ready_) { this->lockMutex(); - if (!m_distortion_const) { + if (!distortion_const_) { float result[4]; getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest); - m_distortion = result[0]; + distortion_ = result[0]; } - if (!m_dispersion_const) { + if (!dispersion_const_) { float result[4]; getInputSocketReader(2)->readSampled(result, 0, 0, PixelSampler::Nearest); - m_dispersion = result[0]; + dispersion_ = result[0]; } - updateVariables(m_distortion, m_dispersion); - m_variables_ready = true; + updateVariables(distortion_, dispersion_); + variables_ready_ = true; this->unlockMutex(); } @@ -125,8 +125,8 @@ void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/) void ScreenLensDistortionOperation::get_uv(const float xy[2], float uv[2]) const { - uv[0] = m_sc * ((xy[0] + 0.5f) - m_cx) / m_cx; - uv[1] = m_sc * ((xy[1] + 0.5f) - m_cy) / m_cy; + uv[0] = sc_ * ((xy[0] + 0.5f) - cx_) / cx_; + uv[1] = sc_ * ((xy[1] + 0.5f) - cy_) / cy_; } void ScreenLensDistortionOperation::distort_uv(const float uv[2], float t, float xy[2]) const @@ -162,14 +162,14 @@ void ScreenLensDistortionOperation::accumulate(const MemoryBuffer *buffer, float color[4]; float dsf = len_v2v2(delta[a], delta[b]) + 1.0f; - int ds = m_jitter ? (dsf < 4.0f ? 2 : (int)sqrtf(dsf)) : (int)dsf; + int ds = jitter_ ? (dsf < 4.0f ? 2 : (int)sqrtf(dsf)) : (int)dsf; float sd = 1.0f / (float)ds; - float k4 = m_k4[a]; - float dk4 = m_dk4[a]; + float k4 = k4_[a]; + float dk4 = dk4_[a]; for (float z = 0; z < ds; z++) { - float tz = (z + (m_jitter ? BLI_rng_get_float(m_rng) : 0.5f)) * sd; + float tz = (z + (jitter_ ? BLI_rng_get_float(rng_) : 0.5f)) * sd; float t = 1.0f - (k4 + tz * dk4) * r_sq; float xy[2]; @@ -202,9 +202,9 @@ void ScreenLensDistortionOperation::executePixel(float output[4], int x, int y, float delta[3][2]; float sum[4] = {0, 0, 0, 0}; - bool valid_r = get_delta(uv_dot, m_k4[0], uv, delta[0]); - bool valid_g = get_delta(uv_dot, m_k4[1], uv, delta[1]); - bool valid_b = get_delta(uv_dot, m_k4[2], uv, delta[2]); + bool valid_r = get_delta(uv_dot, k4_[0], uv, delta[0]); + bool valid_g = get_delta(uv_dot, k4_[1], uv, delta[1]); + bool valid_b = get_delta(uv_dot, k4_[2], uv, delta[2]); if (valid_r && valid_g && valid_b) { accumulate(buffer, 0, 1, uv_dot, uv, delta, sum, count); @@ -231,8 +231,8 @@ void ScreenLensDistortionOperation::executePixel(float output[4], int x, int y, void ScreenLensDistortionOperation::deinitExecution() { this->deinitMutex(); - m_inputProgram = nullptr; - BLI_rng_free(m_rng); + inputProgram_ = nullptr; + BLI_rng_free(rng_); } void ScreenLensDistortionOperation::determineUV(float result[6], float x, float y) const @@ -245,9 +245,9 @@ void ScreenLensDistortionOperation::determineUV(float result[6], float x, float copy_v2_v2(result + 0, xy); copy_v2_v2(result + 2, xy); copy_v2_v2(result + 4, xy); - get_delta(uv_dot, m_k4[0], uv, result + 0); - get_delta(uv_dot, m_k4[1], uv, result + 2); - get_delta(uv_dot, m_k4[2], uv, result + 4); + get_delta(uv_dot, k4_[0], uv, result + 0); + get_delta(uv_dot, k4_[1], uv, result + 2); + get_delta(uv_dot, k4_[2], uv, result + 4); } bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( @@ -293,7 +293,7 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( BLI_rcti_init_minmax(&newInput); - if (m_dispersion_const && m_distortion_const) { + if (dispersion_const_ && distortion_const_) { /* update from fixed distortion/dispersion */ # define UPDATE_INPUT(x, y) \ { \ @@ -315,7 +315,7 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( } else { /* use maximum dispersion 1.0 if not const */ - float dispersion = m_dispersion_const ? m_dispersion : 1.0f; + float dispersion = dispersion_const_ ? dispersion_ : 1.0f; # define UPDATE_INPUT(x, y, distortion) \ { \ @@ -329,12 +329,12 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( } \ (void)0 - if (m_distortion_const) { + if (distortion_const_) { /* update from fixed distortion */ - UPDATE_INPUT(input->xmin, input->xmax, m_distortion); - UPDATE_INPUT(input->xmin, input->ymax, m_distortion); - UPDATE_INPUT(input->xmax, input->ymax, m_distortion); - UPDATE_INPUT(input->xmax, input->ymin, m_distortion); + UPDATE_INPUT(input->xmin, input->xmax, distortion_); + UPDATE_INPUT(input->xmin, input->ymax, distortion_); + UPDATE_INPUT(input->xmax, input->ymax, distortion_); + UPDATE_INPUT(input->xmax, input->ymin, distortion_); } else { /* update from min/max distortion (-1..1) */ @@ -367,18 +367,18 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( void ScreenLensDistortionOperation::updateVariables(float distortion, float dispersion) { - m_k[1] = max_ff(min_ff(distortion, 1.0f), -0.999f); + k_[1] = max_ff(min_ff(distortion, 1.0f), -0.999f); /* Smaller dispersion range for somewhat more control. */ float d = 0.25f * max_ff(min_ff(dispersion, 1.0f), 0.0f); - m_k[0] = max_ff(min_ff((m_k[1] + d), 1.0f), -0.999f); - m_k[2] = max_ff(min_ff((m_k[1] - d), 1.0f), -0.999f); - m_maxk = max_fff(m_k[0], m_k[1], m_k[2]); - m_sc = (m_fit && (m_maxk > 0.0f)) ? (1.0f / (1.0f + 2.0f * m_maxk)) : (1.0f / (1.0f + m_maxk)); - m_dk4[0] = 4.0f * (m_k[1] - m_k[0]); - m_dk4[1] = 4.0f * (m_k[2] - m_k[1]); - m_dk4[2] = 0.0f; /* unused */ + k_[0] = max_ff(min_ff((k_[1] + d), 1.0f), -0.999f); + k_[2] = max_ff(min_ff((k_[1] - d), 1.0f), -0.999f); + maxk_ = max_fff(k_[0], k_[1], k_[2]); + sc_ = (fit_ && (maxk_ > 0.0f)) ? (1.0f / (1.0f + 2.0f * maxk_)) : (1.0f / (1.0f + maxk_)); + dk4_[0] = 4.0f * (k_[1] - k_[0]); + dk4_[1] = 4.0f * (k_[2] - k_[1]); + dk4_[2] = 0.0f; /* unused */ - mul_v3_v3fl(m_k4, m_k, 4.0f); + mul_v3_v3fl(k4_, k_, 4.0f); } void ScreenLensDistortionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -422,7 +422,7 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, BLI_rcti_init_minmax(&newInput); - if (m_dispersion_const && m_distortion_const) { + if (dispersion_const_ && distortion_const_) { /* update from fixed distortion/dispersion */ # define UPDATE_INPUT(x, y) \ { \ @@ -444,7 +444,7 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, } else { /* use maximum dispersion 1.0 if not const */ - float dispersion = m_dispersion_const ? m_dispersion : 1.0f; + float dispersion = dispersion_const_ ? dispersion_ : 1.0f; # define UPDATE_INPUT(x, y, distortion) \ { \ @@ -458,12 +458,12 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, } \ (void)0 - if (m_distortion_const) { + if (distortion_const_) { /* update from fixed distortion */ - UPDATE_INPUT(input->xmin, input->xmax, m_distortion); - UPDATE_INPUT(input->xmin, input->ymax, m_distortion); - UPDATE_INPUT(input->xmax, input->ymax, m_distortion); - UPDATE_INPUT(input->xmax, input->ymin, m_distortion); + UPDATE_INPUT(input->xmin, input->xmax, distortion_); + UPDATE_INPUT(input->xmin, input->ymax, distortion_); + UPDATE_INPUT(input->xmax, input->ymax, distortion_); + UPDATE_INPUT(input->xmax, input->ymin, distortion_); } else { /* update from min/max distortion (-1..1) */ @@ -506,9 +506,9 @@ void ScreenLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *o const float uv_dot = len_squared_v2(uv); float delta[3][2]; - const bool valid_r = get_delta(uv_dot, m_k4[0], uv, delta[0]); - const bool valid_g = get_delta(uv_dot, m_k4[1], uv, delta[1]); - const bool valid_b = get_delta(uv_dot, m_k4[2], uv, delta[2]); + const bool valid_r = get_delta(uv_dot, k4_[0], uv, delta[0]); + const bool valid_g = get_delta(uv_dot, k4_[1], uv, delta[1]); + const bool valid_b = get_delta(uv_dot, k4_[2], uv, delta[2]); if (!(valid_r && valid_g && valid_b)) { zero_v4(it.out); continue; diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h index 93681b2f934..8962ee09290 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h @@ -30,22 +30,22 @@ class ScreenLensDistortionOperation : public MultiThreadedOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; - struct RNG *m_rng; + SocketReader *inputProgram_; + struct RNG *rng_; - bool m_fit; - bool m_jitter; + bool fit_; + bool jitter_; - float m_dispersion; - float m_distortion; - bool m_dispersion_const; - bool m_distortion_const; - bool m_variables_ready; - float m_k[3]; - float m_k4[3]; - float m_dk4[3]; - float m_maxk; - float m_sc, m_cx, m_cy; + float dispersion_; + float distortion_; + bool dispersion_const_; + bool distortion_const_; + bool variables_ready_; + float k_[3]; + float k4_[3]; + float dk4_[3]; + float maxk_; + float sc_, cx_, cy_; public: ScreenLensDistortionOperation(); @@ -70,11 +70,11 @@ class ScreenLensDistortionOperation : public MultiThreadedOperation { void setFit(bool fit) { - m_fit = fit; + fit_ = fit; } void setJitter(bool jitter) { - m_jitter = jitter; + jitter_ = jitter; } /** Set constant distortion value */ diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc index da930186901..2f11e1dbb18 100644 --- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc @@ -26,15 +26,15 @@ SetAlphaMultiplyOperation::SetAlphaMultiplyOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_inputColor = nullptr; - m_inputAlpha = nullptr; + inputColor_ = nullptr; + inputAlpha_ = nullptr; this->flags.can_be_constant = true; } void SetAlphaMultiplyOperation::initExecution() { - m_inputColor = getInputSocketReader(0); - m_inputAlpha = getInputSocketReader(1); + inputColor_ = getInputSocketReader(0); + inputAlpha_ = getInputSocketReader(1); } void SetAlphaMultiplyOperation::executePixelSampled(float output[4], @@ -45,16 +45,16 @@ void SetAlphaMultiplyOperation::executePixelSampled(float output[4], float color_input[4]; float alpha_input[4]; - m_inputColor->readSampled(color_input, x, y, sampler); - m_inputAlpha->readSampled(alpha_input, x, y, sampler); + inputColor_->readSampled(color_input, x, y, sampler); + inputAlpha_->readSampled(alpha_input, x, y, sampler); mul_v4_v4fl(output, color_input, alpha_input[0]); } void SetAlphaMultiplyOperation::deinitExecution() { - m_inputColor = nullptr; - m_inputAlpha = nullptr; + inputColor_ = nullptr; + inputAlpha_ = nullptr; } void SetAlphaMultiplyOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h index 44885318901..77f61e976e1 100644 --- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h +++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h @@ -29,8 +29,8 @@ namespace blender::compositor { */ class SetAlphaMultiplyOperation : public MultiThreadedOperation { private: - SocketReader *m_inputColor; - SocketReader *m_inputAlpha; + SocketReader *inputColor_; + SocketReader *inputAlpha_; public: SetAlphaMultiplyOperation(); diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc index 5abb490d698..09539d2fe57 100644 --- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc @@ -26,15 +26,15 @@ SetAlphaReplaceOperation::SetAlphaReplaceOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_inputColor = nullptr; - m_inputAlpha = nullptr; + inputColor_ = nullptr; + inputAlpha_ = nullptr; this->flags.can_be_constant = true; } void SetAlphaReplaceOperation::initExecution() { - m_inputColor = getInputSocketReader(0); - m_inputAlpha = getInputSocketReader(1); + inputColor_ = getInputSocketReader(0); + inputAlpha_ = getInputSocketReader(1); } void SetAlphaReplaceOperation::executePixelSampled(float output[4], @@ -44,15 +44,15 @@ void SetAlphaReplaceOperation::executePixelSampled(float output[4], { float alpha_input[4]; - m_inputColor->readSampled(output, x, y, sampler); - m_inputAlpha->readSampled(alpha_input, x, y, sampler); + inputColor_->readSampled(output, x, y, sampler); + inputAlpha_->readSampled(alpha_input, x, y, sampler); output[3] = alpha_input[0]; } void SetAlphaReplaceOperation::deinitExecution() { - m_inputColor = nullptr; - m_inputAlpha = nullptr; + inputColor_ = nullptr; + inputAlpha_ = nullptr; } void SetAlphaReplaceOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h index 2c2d4cddf5b..9c295eec3bd 100644 --- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h +++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h @@ -28,8 +28,8 @@ namespace blender::compositor { */ class SetAlphaReplaceOperation : public MultiThreadedOperation { private: - SocketReader *m_inputColor; - SocketReader *m_inputAlpha; + SocketReader *inputColor_; + SocketReader *inputAlpha_; public: /** diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index 25969821931..8700ba7496b 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -31,7 +31,7 @@ void SetColorOperation::executePixelSampled(float output[4], float /*y*/, PixelSampler /*sampler*/) { - copy_v4_v4(output, m_color); + copy_v4_v4(output, color_); } void SetColorOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h index b529d245091..ad30c1d820d 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.h +++ b/source/blender/compositor/operations/COM_SetColorOperation.h @@ -28,7 +28,7 @@ namespace blender::compositor { */ class SetColorOperation : public ConstantOperation { private: - float m_color[4]; + float color_[4]; public: /** @@ -38,44 +38,44 @@ class SetColorOperation : public ConstantOperation { const float *get_constant_elem() override { - return m_color; + return color_; } float getChannel1() { - return m_color[0]; + return color_[0]; } void setChannel1(float value) { - m_color[0] = value; + color_[0] = value; } float getChannel2() { - return m_color[1]; + return color_[1]; } void setChannel2(float value) { - m_color[1] = value; + color_[1] = value; } float getChannel3() { - return m_color[2]; + return color_[2]; } void setChannel3(float value) { - m_color[2] = value; + color_[2] = value; } float getChannel4() { - return m_color[3]; + return color_[3]; } void setChannel4(const float value) { - m_color[3] = value; + color_[3] = value; } void setChannels(const float value[4]) { - copy_v4_v4(m_color, value); + copy_v4_v4(color_, value); } /** diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.cc b/source/blender/compositor/operations/COM_SetSamplerOperation.cc index eb7eedb2bd5..7919b885556 100644 --- a/source/blender/compositor/operations/COM_SetSamplerOperation.cc +++ b/source/blender/compositor/operations/COM_SetSamplerOperation.cc @@ -28,11 +28,11 @@ SetSamplerOperation::SetSamplerOperation() void SetSamplerOperation::initExecution() { - m_reader = this->getInputSocketReader(0); + reader_ = this->getInputSocketReader(0); } void SetSamplerOperation::deinitExecution() { - m_reader = nullptr; + reader_ = nullptr; } void SetSamplerOperation::executePixelSampled(float output[4], @@ -40,7 +40,7 @@ void SetSamplerOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - m_reader->readSampled(output, x, y, m_sampler); + reader_->readSampled(output, x, y, sampler_); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.h b/source/blender/compositor/operations/COM_SetSamplerOperation.h index 0b6c1f2ccff..87b232cbfa7 100644 --- a/source/blender/compositor/operations/COM_SetSamplerOperation.h +++ b/source/blender/compositor/operations/COM_SetSamplerOperation.h @@ -28,8 +28,8 @@ namespace blender::compositor { */ class SetSamplerOperation : public NodeOperation { private: - PixelSampler m_sampler; - SocketReader *m_reader; + PixelSampler sampler_; + SocketReader *reader_; public: /** @@ -39,7 +39,7 @@ class SetSamplerOperation : public NodeOperation { void setSampler(PixelSampler sampler) { - m_sampler = sampler; + sampler_ = sampler; } /** diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index 4db0963f45b..b5e2f50338f 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -31,7 +31,7 @@ void SetValueOperation::executePixelSampled(float output[4], float /*y*/, PixelSampler /*sampler*/) { - output[0] = m_value; + output[0] = value_; } void SetValueOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h index 25f5e806eaf..bf914d2f918 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.h +++ b/source/blender/compositor/operations/COM_SetValueOperation.h @@ -28,7 +28,7 @@ namespace blender::compositor { */ class SetValueOperation : public ConstantOperation { private: - float m_value; + float value_; public: /** @@ -38,16 +38,16 @@ class SetValueOperation : public ConstantOperation { const float *get_constant_elem() override { - return &m_value; + return &value_; } float getValue() { - return m_value; + return value_; } void setValue(float value) { - m_value = value; + value_ = value; } /** diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc index 794934a741a..99db558343d 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.cc +++ b/source/blender/compositor/operations/COM_SplitOperation.cc @@ -25,21 +25,21 @@ SplitOperation::SplitOperation() this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_image1Input = nullptr; - m_image2Input = nullptr; + image1Input_ = nullptr; + image2Input_ = nullptr; } void SplitOperation::initExecution() { /* When initializing the tree during initial load the width and height can be zero. */ - m_image1Input = getInputSocketReader(0); - m_image2Input = getInputSocketReader(1); + image1Input_ = getInputSocketReader(0); + image2Input_ = getInputSocketReader(1); } void SplitOperation::deinitExecution() { - m_image1Input = nullptr; - m_image2Input = nullptr; + image1Input_ = nullptr; + image2Input_ = nullptr; } void SplitOperation::executePixelSampled(float output[4], @@ -47,14 +47,14 @@ void SplitOperation::executePixelSampled(float output[4], float y, PixelSampler /*sampler*/) { - int perc = m_xSplit ? m_splitPercentage * this->getWidth() / 100.0f : - m_splitPercentage * this->getHeight() / 100.0f; - bool image1 = m_xSplit ? x > perc : y > perc; + int perc = xSplit_ ? splitPercentage_ * this->getWidth() / 100.0f : + splitPercentage_ * this->getHeight() / 100.0f; + bool image1 = xSplit_ ? x > perc : y > perc; if (image1) { - m_image1Input->readSampled(output, x, y, PixelSampler::Nearest); + image1Input_->readSampled(output, x, y, PixelSampler::Nearest); } else { - m_image2Input->readSampled(output, x, y, PixelSampler::Nearest); + image2Input_->readSampled(output, x, y, PixelSampler::Nearest); } } @@ -72,11 +72,11 @@ void SplitOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const int percent = m_xSplit ? m_splitPercentage * this->getWidth() / 100.0f : - m_splitPercentage * this->getHeight() / 100.0f; + const int percent = xSplit_ ? splitPercentage_ * this->getWidth() / 100.0f : + splitPercentage_ * this->getHeight() / 100.0f; const size_t elem_bytes = COM_data_type_bytes_len(getOutputSocket()->getDataType()); for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - const bool is_image1 = m_xSplit ? it.x > percent : it.y > percent; + const bool is_image1 = xSplit_ ? it.x > percent : it.y > percent; memcpy(it.out, it.in(is_image1 ? 0 : 1), elem_bytes); } } diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h index ae4d83fd059..9f594793be5 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.h +++ b/source/blender/compositor/operations/COM_SplitOperation.h @@ -24,11 +24,11 @@ namespace blender::compositor { class SplitOperation : public MultiThreadedOperation { private: - SocketReader *m_image1Input; - SocketReader *m_image2Input; + SocketReader *image1Input_; + SocketReader *image2Input_; - float m_splitPercentage; - bool m_xSplit; + float splitPercentage_; + bool xSplit_; public: SplitOperation(); @@ -38,11 +38,11 @@ class SplitOperation : public MultiThreadedOperation { void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void setSplitPercentage(float splitPercentage) { - m_splitPercentage = splitPercentage; + splitPercentage_ = splitPercentage; } void setXSplit(bool xsplit) { - m_xSplit = xsplit; + xSplit_ = xsplit; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index b71350fd472..b055d0a7644 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -33,9 +33,9 @@ SunBeamsOperation::SunBeamsOperation() void SunBeamsOperation::calc_rays_common_data() { /* convert to pixels */ - m_source_px[0] = m_data.source[0] * this->getWidth(); - m_source_px[1] = m_data.source[1] * this->getHeight(); - m_ray_length_px = m_data.ray_length * MAX2(this->getWidth(), this->getHeight()); + source_px_[0] = data_.source[0] * this->getWidth(); + source_px_[1] = data_.source[1] * this->getHeight(); + ray_length_px_ = data_.ray_length * MAX2(this->getWidth(), this->getHeight()); } void SunBeamsOperation::initExecution() @@ -322,7 +322,7 @@ void SunBeamsOperation::executePixel(float output[4], int x, int y, void *data) { const float co[2] = {(float)x, (float)y}; - accumulate_line((MemoryBuffer *)data, output, co, m_source_px, 0.0f, m_ray_length_px); + accumulate_line((MemoryBuffer *)data, output, co, source_px_, 0.0f, ray_length_px_); } static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], float ray_length) @@ -349,10 +349,10 @@ bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input, * and gives a rect that contains all possible accumulated pixels. */ rcti rect = *input; - calc_ray_shift(&rect, input->xmin, input->ymin, m_source_px, m_ray_length_px); - calc_ray_shift(&rect, input->xmin, input->ymax, m_source_px, m_ray_length_px); - calc_ray_shift(&rect, input->xmax, input->ymin, m_source_px, m_ray_length_px); - calc_ray_shift(&rect, input->xmax, input->ymax, m_source_px, m_ray_length_px); + calc_ray_shift(&rect, input->xmin, input->ymin, source_px_, ray_length_px_); + calc_ray_shift(&rect, input->xmin, input->ymax, source_px_, ray_length_px_); + calc_ray_shift(&rect, input->xmax, input->ymin, source_px_, ray_length_px_); + calc_ray_shift(&rect, input->xmax, input->ymax, source_px_, ray_length_px_); return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output); } @@ -369,10 +369,10 @@ void SunBeamsOperation::get_area_of_interest(const int input_idx, /* Enlarges the rect by moving each corner toward the source. * This is the maximum distance that pixels can influence each other * and gives a rect that contains all possible accumulated pixels. */ - calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymin, m_source_px, m_ray_length_px); - calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymax, m_source_px, m_ray_length_px); - calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymin, m_source_px, m_ray_length_px); - calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymax, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymin, source_px_, ray_length_px_); + calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymax, source_px_, ray_length_px_); + calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymin, source_px_, ray_length_px_); + calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymax, source_px_, ray_length_px_); } void SunBeamsOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -386,7 +386,7 @@ void SunBeamsOperation::update_memory_buffer_partial(MemoryBuffer *output, float *out_elem = output->get_elem(area.xmin, y); for (int x = area.xmin; x < area.xmax; x++) { coords[0] = x; - accumulate_line(input, out_elem, coords, m_source_px, 0.0f, m_ray_length_px); + accumulate_line(input, out_elem, coords, source_px_, 0.0f, ray_length_px_); out_elem += output->elem_stride; } } diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.h b/source/blender/compositor/operations/COM_SunBeamsOperation.h index 71fc04453fe..2a62be065a4 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.h +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.h @@ -37,7 +37,7 @@ class SunBeamsOperation : public MultiThreadedOperation { void setData(const NodeSunBeams &data) { - m_data = data; + data_ = data; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -49,10 +49,10 @@ class SunBeamsOperation : public MultiThreadedOperation { void calc_rays_common_data(); private: - NodeSunBeams m_data; + NodeSunBeams data_; - float m_source_px[2]; - float m_ray_length_px; + float source_px_[2]; + float ray_length_px_; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index 994379c35c1..7c18b8214de 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -28,12 +28,12 @@ TextureBaseOperation::TextureBaseOperation() { this->addInputSocket(DataType::Vector); // offset this->addInputSocket(DataType::Vector); // size - m_texture = nullptr; - m_inputSize = nullptr; - m_inputOffset = nullptr; - m_rd = nullptr; - m_pool = nullptr; - m_sceneColorManage = false; + texture_ = nullptr; + inputSize_ = nullptr; + inputOffset_ = nullptr; + rd_ = nullptr; + pool_ = nullptr; + sceneColorManage_ = false; flags.complex = true; } TextureOperation::TextureOperation() : TextureBaseOperation() @@ -47,23 +47,23 @@ TextureAlphaOperation::TextureAlphaOperation() : TextureBaseOperation() void TextureBaseOperation::initExecution() { - m_inputOffset = getInputSocketReader(0); - m_inputSize = getInputSocketReader(1); - m_pool = BKE_image_pool_new(); - if (m_texture != nullptr && m_texture->nodetree != nullptr && m_texture->use_nodes) { - ntreeTexBeginExecTree(m_texture->nodetree); + inputOffset_ = getInputSocketReader(0); + inputSize_ = getInputSocketReader(1); + pool_ = BKE_image_pool_new(); + if (texture_ != nullptr && texture_->nodetree != nullptr && texture_->use_nodes) { + ntreeTexBeginExecTree(texture_->nodetree); } NodeOperation::initExecution(); } void TextureBaseOperation::deinitExecution() { - m_inputSize = nullptr; - m_inputOffset = nullptr; - BKE_image_pool_free(m_pool); - m_pool = nullptr; - if (m_texture != nullptr && m_texture->use_nodes && m_texture->nodetree != nullptr && - m_texture->nodetree->execdata != nullptr) { - ntreeTexEndExecTree(m_texture->nodetree->execdata); + inputSize_ = nullptr; + inputOffset_ = nullptr; + BKE_image_pool_free(pool_); + pool_ = nullptr; + if (texture_ != nullptr && texture_->use_nodes && texture_->nodetree != nullptr && + texture_->nodetree->execdata != nullptr) { + ntreeTexEndExecTree(texture_->nodetree->execdata); } NodeOperation::deinitExecution(); } @@ -72,8 +72,8 @@ void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_ { r_area = preferred_area; if (BLI_rcti_is_empty(&preferred_area)) { - int width = m_rd->xsch * m_rd->size / 100; - int height = m_rd->ysch * m_rd->size / 100; + int width = rd_->xsch * rd_->size / 100; + int height = rd_->ysch * rd_->size / 100; r_area.xmax = preferred_area.xmin + width; r_area.ymax = preferred_area.ymin + height; } @@ -115,13 +115,13 @@ void TextureBaseOperation::executePixelSampled(float output[4], * interpolation and (b) in such configuration multitex() simply floor's the value * which often produces artifacts. */ - if (m_texture != nullptr && (m_texture->imaflag & TEX_INTERPOL) == 0) { + if (texture_ != nullptr && (texture_->imaflag & TEX_INTERPOL) == 0) { u += 0.5f / cx; v += 0.5f / cy; } - m_inputSize->readSampled(textureSize, x, y, sampler); - m_inputOffset->readSampled(textureOffset, x, y, sampler); + inputSize_->readSampled(textureSize, x, y, sampler); + inputOffset_->readSampled(textureOffset, x, y, sampler); vec[0] = textureSize[0] * (u + textureOffset[0]); vec[1] = textureSize[1] * (v + textureOffset[1]); @@ -129,7 +129,7 @@ void TextureBaseOperation::executePixelSampled(float output[4], const int thread_id = WorkScheduler::current_thread_id(); retval = multitex_ext( - m_texture, vec, nullptr, nullptr, 0, &texres, thread_id, m_pool, m_sceneColorManage, false); + texture_, vec, nullptr, nullptr, 0, &texres, thread_id, pool_, sceneColorManage_, false); output[3] = texres.talpha ? texres.ta : texres.tin; if (retval & TEX_RGB) { @@ -164,7 +164,7 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, * interpolation and (b) in such configuration multitex() simply floor's the value * which often produces artifacts. */ - if (m_texture != nullptr && (m_texture->imaflag & TEX_INTERPOL) == 0) { + if (texture_ != nullptr && (texture_->imaflag & TEX_INTERPOL) == 0) { u += 0.5f / center_x; v += 0.5f / center_y; } @@ -173,15 +173,15 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, vec[1] = tex_size[1] * (v + tex_offset[1]); vec[2] = tex_size[2] * tex_offset[2]; - const int retval = multitex_ext(m_texture, + const int retval = multitex_ext(texture_, vec, nullptr, nullptr, 0, &tex_result, thread_id, - m_pool, - m_sceneColorManage, + pool_, + sceneColorManage_, false); it.out[3] = tex_result.talpha ? tex_result.ta : tex_result.tin; diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index 2efb76e8c37..3916f82c77b 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -35,12 +35,12 @@ namespace blender::compositor { */ class TextureBaseOperation : public MultiThreadedOperation { private: - Tex *m_texture; - const RenderData *m_rd; - SocketReader *m_inputSize; - SocketReader *m_inputOffset; - struct ImagePool *m_pool; - bool m_sceneColorManage; + Tex *texture_; + const RenderData *rd_; + SocketReader *inputSize_; + SocketReader *inputOffset_; + struct ImagePool *pool_; + bool sceneColorManage_; protected: /** @@ -58,17 +58,17 @@ class TextureBaseOperation : public MultiThreadedOperation { void setTexture(Tex *texture) { - m_texture = texture; + texture_ = texture; } void initExecution() override; void deinitExecution() override; void setRenderData(const RenderData *rd) { - m_rd = rd; + rd_ = rd; } void setSceneColorManage(bool sceneColorManage) { - m_sceneColorManage = sceneColorManage; + sceneColorManage_ = sceneColorManage; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index 4d2a9a7f965..b9d67ec8be3 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -28,14 +28,14 @@ TonemapOperation::TonemapOperation() { this->addInputSocket(DataType::Color, ResizeMode::Align); this->addOutputSocket(DataType::Color); - m_imageReader = nullptr; - m_data = nullptr; - m_cachedInstance = nullptr; + imageReader_ = nullptr; + data_ = nullptr; + cachedInstance_ = nullptr; this->flags.complex = true; } void TonemapOperation::initExecution() { - m_imageReader = this->getInputSocketReader(0); + imageReader_ = this->getInputSocketReader(0); NodeOperation::initMutex(); } @@ -43,11 +43,11 @@ void TonemapOperation::executePixel(float output[4], int x, int y, void *data) { AvgLogLum *avg = (AvgLogLum *)data; - m_imageReader->read(output, x, y, nullptr); + imageReader_->read(output, x, y, nullptr); mul_v3_fl(output, avg->al); - float dr = output[0] + m_data->offset; - float dg = output[1] + m_data->offset; - float db = output[2] + m_data->offset; + float dr = output[0] + data_->offset; + float dg = output[1] + data_->offset; + float db = output[2] + data_->offset; output[0] /= ((dr == 0.0f) ? 1.0f : dr); output[1] /= ((dg == 0.0f) ? 1.0f : dg); output[2] /= ((db == 0.0f) ? 1.0f : db); @@ -61,13 +61,13 @@ void TonemapOperation::executePixel(float output[4], int x, int y, void *data) void PhotoreceptorTonemapOperation::executePixel(float output[4], int x, int y, void *data) { AvgLogLum *avg = (AvgLogLum *)data; - NodeTonemap *ntm = m_data; + NodeTonemap *ntm = data_; - const float f = expf(-m_data->f); + const float f = expf(-data_->f); const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); const float ic = 1.0f - ntm->c, ia = 1.0f - ntm->a; - m_imageReader->read(output, x, y, nullptr); + imageReader_->read(output, x, y, nullptr); const float L = IMB_colormanagement_get_luminance(output); float I_l = output[0] + ic * (L - output[0]); @@ -86,8 +86,8 @@ void PhotoreceptorTonemapOperation::executePixel(float output[4], int x, int y, void TonemapOperation::deinitExecution() { - m_imageReader = nullptr; - delete m_cachedInstance; + imageReader_ = nullptr; + delete cachedInstance_; NodeOperation::deinitMutex(); } @@ -111,8 +111,8 @@ bool TonemapOperation::determineDependingAreaOfInterest(rcti * /*input*/, void *TonemapOperation::initializeTileData(rcti *rect) { lockMutex(); - if (m_cachedInstance == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)m_imageReader->initializeTileData(rect); + if (cachedInstance_ == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)imageReader_->initializeTileData(rect); AvgLogLum *data = new AvgLogLum(); float *buffer = tile->getBuffer(); @@ -140,12 +140,12 @@ void *TonemapOperation::initializeTileData(rcti *rect) avl = lsum * sc; data->auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.0f; float al = exp((double)avl); - data->al = (al == 0.0f) ? 0.0f : (m_data->key / al); - data->igm = (m_data->gamma == 0.0f) ? 1 : (1.0f / m_data->gamma); - m_cachedInstance = data; + data->al = (al == 0.0f) ? 0.0f : (data_->key / al); + data->igm = (data_->gamma == 0.0f) ? 1 : (1.0f / data_->gamma); + cachedInstance_ = data; } unlockMutex(); - return m_cachedInstance; + return cachedInstance_; } void TonemapOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) @@ -189,7 +189,7 @@ void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output) const rcti &UNUSED(area), Span inputs) { - if (m_cachedInstance == nullptr) { + if (cachedInstance_ == nullptr) { Luminance lum = {0}; const MemoryBuffer *input = inputs[0]; exec_system_->execute_work( @@ -213,9 +213,9 @@ void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output) const float avg_log = lum.log_sum / lum.num_pixels; avg->auto_key = (max_log > min_log) ? ((max_log - avg_log) / (max_log - min_log)) : 1.0f; const float al = exp((double)avg_log); - avg->al = (al == 0.0f) ? 0.0f : (m_data->key / al); - avg->igm = (m_data->gamma == 0.0f) ? 1 : (1.0f / m_data->gamma); - m_cachedInstance = avg; + avg->al = (al == 0.0f) ? 0.0f : (data_->key / al); + avg->igm = (data_->gamma == 0.0f) ? 1 : (1.0f / data_->gamma); + cachedInstance_ = avg; } } @@ -223,9 +223,9 @@ void TonemapOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - AvgLogLum *avg = m_cachedInstance; + AvgLogLum *avg = cachedInstance_; const float igm = avg->igm; - const float offset = m_data->offset; + const float offset = data_->offset; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { copy_v4_v4(it.out, it.in(0)); mul_v3_fl(it.out, avg->al); @@ -247,9 +247,9 @@ void PhotoreceptorTonemapOperation::update_memory_buffer_partial(MemoryBuffer *o const rcti &area, Span inputs) { - AvgLogLum *avg = m_cachedInstance; - NodeTonemap *ntm = m_data; - const float f = expf(-m_data->f); + AvgLogLum *avg = cachedInstance_; + NodeTonemap *ntm = data_; + const float f = expf(-data_->f); const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); const float ic = 1.0f - ntm->c; const float ia = 1.0f - ntm->a; diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h index 7fe496ba107..c04ee4fcbe9 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.h +++ b/source/blender/compositor/operations/COM_TonemapOperation.h @@ -44,17 +44,17 @@ class TonemapOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *m_imageReader; + SocketReader *imageReader_; /** * \brief settings of the Tonemap */ - NodeTonemap *m_data; + NodeTonemap *data_; /** * \brief temporarily cache of the execution storage */ - AvgLogLum *m_cachedInstance; + AvgLogLum *cachedInstance_; public: TonemapOperation(); @@ -79,7 +79,7 @@ class TonemapOperation : public MultiThreadedOperation { void setData(NodeTonemap *data) { - m_data = data; + data_ = data; } bool determineDependingAreaOfInterest(rcti *input, diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index c424ece572d..d9e55011d16 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -27,14 +27,14 @@ namespace blender::compositor { TrackPositionOperation::TrackPositionOperation() { this->addOutputSocket(DataType::Value); - m_movieClip = nullptr; - m_framenumber = 0; - m_trackingObjectName[0] = 0; - m_trackName[0] = 0; - m_axis = 0; - m_position = CMP_TRACKPOS_ABSOLUTE; - m_relativeFrame = 0; - m_speed_output = false; + movieClip_ = nullptr; + framenumber_ = 0; + trackingObjectName_[0] = 0; + trackName_[0] = 0; + axis_ = 0; + position_ = CMP_TRACKPOS_ABSOLUTE; + relativeFrame_ = 0; + speed_output_ = false; flags.is_set_operation = true; is_track_position_calculated_ = false; } @@ -54,76 +54,76 @@ void TrackPositionOperation::calc_track_position() MovieTrackingObject *object; track_position_ = 0; - zero_v2(m_markerPos); - zero_v2(m_relativePos); + zero_v2(markerPos_); + zero_v2(relativePos_); - if (!m_movieClip) { + if (!movieClip_) { return; } - tracking = &m_movieClip->tracking; + tracking = &movieClip_->tracking; - BKE_movieclip_user_set_frame(&user, m_framenumber); - BKE_movieclip_get_size(m_movieClip, &user, &m_width, &m_height); + BKE_movieclip_user_set_frame(&user, framenumber_); + BKE_movieclip_get_size(movieClip_, &user, &width_, &height_); - object = BKE_tracking_object_get_named(tracking, m_trackingObjectName); + object = BKE_tracking_object_get_named(tracking, trackingObjectName_); if (object) { MovieTrackingTrack *track; - track = BKE_tracking_track_get_named(tracking, object, m_trackName); + track = BKE_tracking_track_get_named(tracking, object, trackName_); if (track) { MovieTrackingMarker *marker; - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, m_framenumber); + int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, framenumber_); marker = BKE_tracking_marker_get(track, clip_framenr); - copy_v2_v2(m_markerPos, marker->pos); + copy_v2_v2(markerPos_, marker->pos); - if (m_speed_output) { - int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, - m_relativeFrame); + if (speed_output_) { + int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, + relativeFrame_); marker = BKE_tracking_marker_get_exact(track, relative_clip_framenr); if (marker != nullptr && (marker->flag & MARKER_DISABLED) == 0) { - copy_v2_v2(m_relativePos, marker->pos); + copy_v2_v2(relativePos_, marker->pos); } else { - copy_v2_v2(m_relativePos, m_markerPos); + copy_v2_v2(relativePos_, markerPos_); } - if (m_relativeFrame < m_framenumber) { - swap_v2_v2(m_relativePos, m_markerPos); + if (relativeFrame_ < framenumber_) { + swap_v2_v2(relativePos_, markerPos_); } } - else if (m_position == CMP_TRACKPOS_RELATIVE_START) { + else if (position_ == CMP_TRACKPOS_RELATIVE_START) { int i; for (i = 0; i < track->markersnr; i++) { marker = &track->markers[i]; if ((marker->flag & MARKER_DISABLED) == 0) { - copy_v2_v2(m_relativePos, marker->pos); + copy_v2_v2(relativePos_, marker->pos); break; } } } - else if (m_position == CMP_TRACKPOS_RELATIVE_FRAME) { - int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(m_movieClip, - m_relativeFrame); + else if (position_ == CMP_TRACKPOS_RELATIVE_FRAME) { + int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, + relativeFrame_); marker = BKE_tracking_marker_get(track, relative_clip_framenr); - copy_v2_v2(m_relativePos, marker->pos); + copy_v2_v2(relativePos_, marker->pos); } } } - track_position_ = m_markerPos[m_axis] - m_relativePos[m_axis]; - if (m_axis == 0) { - track_position_ *= m_width; + track_position_ = markerPos_[axis_] - relativePos_[axis_]; + if (axis_ == 0) { + track_position_ *= width_; } else { - track_position_ *= m_height; + track_position_ *= height_; } } @@ -132,13 +132,13 @@ void TrackPositionOperation::executePixelSampled(float output[4], float /*y*/, PixelSampler /*sampler*/) { - output[0] = m_markerPos[m_axis] - m_relativePos[m_axis]; + output[0] = markerPos_[axis_] - relativePos_[axis_]; - if (m_axis == 0) { - output[0] *= m_width; + if (axis_ == 0) { + output[0] *= width_; } else { - output[0] *= m_height; + output[0] *= height_; } } diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h index 8fcbf6d31af..25e5a144228 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.h +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h @@ -35,18 +35,18 @@ namespace blender::compositor { */ class TrackPositionOperation : public ConstantOperation { protected: - MovieClip *m_movieClip; - int m_framenumber; - char m_trackingObjectName[64]; - char m_trackName[64]; - int m_axis; - int m_position; - int m_relativeFrame; - bool m_speed_output; + MovieClip *movieClip_; + int framenumber_; + char trackingObjectName_[64]; + char trackName_[64]; + int axis_; + int position_; + int relativeFrame_; + bool speed_output_; - int m_width, m_height; - float m_markerPos[2]; - float m_relativePos[2]; + int width_, height_; + float markerPos_[2]; + float relativePos_[2]; float track_position_; bool is_track_position_calculated_; @@ -60,35 +60,35 @@ class TrackPositionOperation : public ConstantOperation { void setMovieClip(MovieClip *clip) { - m_movieClip = clip; + movieClip_ = clip; } void setTrackingObject(char *object) { - BLI_strncpy(m_trackingObjectName, object, sizeof(m_trackingObjectName)); + BLI_strncpy(trackingObjectName_, object, sizeof(trackingObjectName_)); } void setTrackName(char *track) { - BLI_strncpy(m_trackName, track, sizeof(m_trackName)); + BLI_strncpy(trackName_, track, sizeof(trackName_)); } void setFramenumber(int framenumber) { - m_framenumber = framenumber; + framenumber_ = framenumber; } void setAxis(int value) { - m_axis = value; + axis_ = value; } void setPosition(int value) { - m_position = value; + position_ = value; } void setRelativeFrame(int value) { - m_relativeFrame = value; + relativeFrame_ = value; } void setSpeedOutput(bool speed_output) { - m_speed_output = speed_output; + speed_output_ = speed_output; } void initExecution() override; diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc index 05fe267b174..1ec5029385e 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.cc +++ b/source/blender/compositor/operations/COM_TranslateOperation.cc @@ -30,28 +30,28 @@ TranslateOperation::TranslateOperation(DataType data_type, ResizeMode resize_mod this->addInputSocket(DataType::Value, ResizeMode::None); this->addOutputSocket(data_type); this->set_canvas_input_index(0); - m_inputOperation = nullptr; - m_inputXOperation = nullptr; - m_inputYOperation = nullptr; - m_isDeltaSet = false; - m_factorX = 1.0f; - m_factorY = 1.0f; + inputOperation_ = nullptr; + inputXOperation_ = nullptr; + inputYOperation_ = nullptr; + isDeltaSet_ = false; + factorX_ = 1.0f; + factorY_ = 1.0f; this->x_extend_mode_ = MemoryBufferExtend::Clip; this->y_extend_mode_ = MemoryBufferExtend::Clip; } void TranslateOperation::initExecution() { - m_inputOperation = this->getInputSocketReader(0); - m_inputXOperation = this->getInputSocketReader(1); - m_inputYOperation = this->getInputSocketReader(2); + inputOperation_ = this->getInputSocketReader(0); + inputXOperation_ = this->getInputSocketReader(1); + inputYOperation_ = this->getInputSocketReader(2); } void TranslateOperation::deinitExecution() { - m_inputOperation = nullptr; - m_inputXOperation = nullptr; - m_inputYOperation = nullptr; + inputOperation_ = nullptr; + inputXOperation_ = nullptr; + inputYOperation_ = nullptr; } void TranslateOperation::executePixelSampled(float output[4], @@ -64,7 +64,7 @@ void TranslateOperation::executePixelSampled(float output[4], float originalXPos = x - this->getDeltaX(); float originalYPos = y - this->getDeltaY(); - m_inputOperation->readSampled(output, originalXPos, originalYPos, PixelSampler::Bilinear); + inputOperation_->readSampled(output, originalXPos, originalYPos, PixelSampler::Bilinear); } bool TranslateOperation::determineDependingAreaOfInterest(rcti *input, @@ -85,8 +85,8 @@ bool TranslateOperation::determineDependingAreaOfInterest(rcti *input, void TranslateOperation::setFactorXY(float factorX, float factorY) { - m_factorX = factorX; - m_factorY = factorY; + factorX_ = factorX; + factorY_ = factorY; } void TranslateOperation::set_wrapping(int wrapping_type) diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h index 734a19008d6..25251ff1d9e 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.h +++ b/source/blender/compositor/operations/COM_TranslateOperation.h @@ -30,14 +30,14 @@ class TranslateOperation : public MultiThreadedOperation { static constexpr int Y_INPUT_INDEX = 2; private: - SocketReader *m_inputOperation; - SocketReader *m_inputXOperation; - SocketReader *m_inputYOperation; - float m_deltaX; - float m_deltaY; - bool m_isDeltaSet; - float m_factorX; - float m_factorY; + SocketReader *inputOperation_; + SocketReader *inputXOperation_; + SocketReader *inputYOperation_; + float deltaX_; + float deltaY_; + bool isDeltaSet_; + float factorX_; + float factorY_; protected: MemoryBufferExtend x_extend_mode_; @@ -56,29 +56,29 @@ class TranslateOperation : public MultiThreadedOperation { float getDeltaX() { - return m_deltaX * m_factorX; + return deltaX_ * factorX_; } float getDeltaY() { - return m_deltaY * m_factorY; + return deltaY_ * factorY_; } inline void ensureDelta() { - if (!m_isDeltaSet) { + if (!isDeltaSet_) { if (execution_model_ == eExecutionModel::Tiled) { float tempDelta[4]; - m_inputXOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); - m_deltaX = tempDelta[0]; - m_inputYOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); - m_deltaY = tempDelta[0]; + inputXOperation_->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); + deltaX_ = tempDelta[0]; + inputYOperation_->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); + deltaY_ = tempDelta[0]; } else { - m_deltaX = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f); - m_deltaY = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f); + deltaX_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f); + deltaY_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f); } - m_isDeltaSet = true; + isDeltaSet_ = true; } } diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc index 0afc315ae7e..a0013ae1061 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc @@ -34,24 +34,24 @@ VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation() flags.complex = true; flags.open_cl = true; - m_inputProgram = nullptr; - m_inputBokehProgram = nullptr; - m_inputSizeProgram = nullptr; - m_maxBlur = 32.0f; - m_threshold = 1.0f; - m_do_size_scale = false; + inputProgram_ = nullptr; + inputBokehProgram_ = nullptr; + inputSizeProgram_ = nullptr; + maxBlur_ = 32.0f; + threshold_ = 1.0f; + do_size_scale_ = false; #ifdef COM_DEFOCUS_SEARCH - m_inputSearchProgram = nullptr; + inputSearchProgram_ = nullptr; #endif } void VariableSizeBokehBlurOperation::initExecution() { - m_inputProgram = getInputSocketReader(0); - m_inputBokehProgram = getInputSocketReader(1); - m_inputSizeProgram = getInputSocketReader(2); + inputProgram_ = getInputSocketReader(0); + inputBokehProgram_ = getInputSocketReader(1); + inputSizeProgram_ = getInputSocketReader(2); #ifdef COM_DEFOCUS_SEARCH - m_inputSearchProgram = getInputSocketReader(3); + inputSearchProgram_ = getInputSocketReader(3); #endif QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -65,18 +65,18 @@ struct VariableSizeBokehBlurTileData { void *VariableSizeBokehBlurOperation::initializeTileData(rcti *rect) { VariableSizeBokehBlurTileData *data = new VariableSizeBokehBlurTileData(); - data->color = (MemoryBuffer *)m_inputProgram->initializeTileData(rect); - data->bokeh = (MemoryBuffer *)m_inputBokehProgram->initializeTileData(rect); - data->size = (MemoryBuffer *)m_inputSizeProgram->initializeTileData(rect); + data->color = (MemoryBuffer *)inputProgram_->initializeTileData(rect); + data->bokeh = (MemoryBuffer *)inputBokehProgram_->initializeTileData(rect); + data->size = (MemoryBuffer *)inputSizeProgram_->initializeTileData(rect); rcti rect2; - this->determineDependingAreaOfInterest(rect, (ReadBufferOperation *)m_inputSizeProgram, &rect2); + this->determineDependingAreaOfInterest(rect, (ReadBufferOperation *)inputSizeProgram_, &rect2); const float max_dim = MAX2(this->getWidth(), this->getHeight()); - const float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; data->maxBlurScalar = (int)(data->size->get_max_value(rect2) * scalar); - CLAMP(data->maxBlurScalar, 1.0f, m_maxBlur); + CLAMP(data->maxBlurScalar, 1.0f, maxBlur_); return data; } @@ -101,7 +101,7 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, float color_accum[4]; const float max_dim = MAX2(getWidth(), getHeight()); - const float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; int maxBlurScalar = tileData->maxBlurScalar; BLI_assert(inputBokehBuffer->getWidth() == COM_BLUR_BOKEH_PIXELS); @@ -109,10 +109,10 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, #ifdef COM_DEFOCUS_SEARCH float search[4]; - m_inputSearchProgram->read(search, - x / InverseSearchRadiusOperation::DIVIDER, - y / InverseSearchRadiusOperation::DIVIDER, - nullptr); + inputSearchProgram_->read(search, + x / InverseSearchRadiusOperation::DIVIDER, + y / InverseSearchRadiusOperation::DIVIDER, + nullptr); int minx = search[0]; int miny = search[1]; int maxx = search[2]; @@ -135,7 +135,7 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, const int addYStepValue = addXStepValue; const int addXStepColor = addXStepValue * COM_DATA_TYPE_COLOR_CHANNELS; - if (size_center > m_threshold) { + if (size_center > threshold_) { for (int ny = miny; ny < maxy; ny += addYStepValue) { float dy = ny - y; int offsetValueNy = ny * inputSizeBuffer->getWidth(); @@ -144,7 +144,7 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, for (int nx = minx; nx < maxx; nx += addXStepValue) { if (nx != x || ny != y) { float size = MIN2(inputSizeFloatBuffer[offsetValueNxNy] * scalar, size_center); - if (size > m_threshold) { + if (size > threshold_) { float dx = nx - x; if (size > fabsf(dx) && size > fabsf(dy)) { float uv[2] = { @@ -171,9 +171,9 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, output[3] = color_accum[3] / multiplier_accum[3]; /* blend in out values over the threshold, otherwise we get sharp, ugly transitions */ - if ((size_center > m_threshold) && (size_center < m_threshold * 2.0f)) { + if ((size_center > threshold_) && (size_center < threshold_ * 2.0f)) { /* factor from 0-1 */ - float fac = (size_center - m_threshold) / m_threshold; + float fac = (size_center - threshold_) / threshold_; interp_v4_v4v4(output, readColor, output, fac); } } @@ -190,21 +190,21 @@ void VariableSizeBokehBlurOperation::executeOpenCL(OpenCLDevice *device, cl_int step = this->getStep(); cl_int maxBlur; - cl_float threshold = m_threshold; + cl_float threshold = threshold_; - MemoryBuffer *sizeMemoryBuffer = m_inputSizeProgram->getInputMemoryBuffer(inputMemoryBuffers); + MemoryBuffer *sizeMemoryBuffer = inputSizeProgram_->getInputMemoryBuffer(inputMemoryBuffers); const float max_dim = MAX2(getWidth(), getHeight()); - cl_float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + cl_float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; - maxBlur = (cl_int)min_ff(sizeMemoryBuffer->get_max_value() * scalar, (float)m_maxBlur); + maxBlur = (cl_int)min_ff(sizeMemoryBuffer->get_max_value() * scalar, (float)maxBlur_); device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, m_inputProgram); + defocusKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, inputProgram_); device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 1, -1, clMemToCleanUp, inputMemoryBuffers, m_inputBokehProgram); + defocusKernel, 1, -1, clMemToCleanUp, inputMemoryBuffers, inputBokehProgram_); device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 2, 4, clMemToCleanUp, inputMemoryBuffers, m_inputSizeProgram); + defocusKernel, 2, 4, clMemToCleanUp, inputMemoryBuffers, inputSizeProgram_); device->COM_clAttachOutputMemoryBufferToKernelParameter(defocusKernel, 3, clOutputBuffer); device->COM_clAttachMemoryBufferOffsetToKernelParameter(defocusKernel, 5, outputMemoryBuffer); clSetKernelArg(defocusKernel, 6, sizeof(cl_int), &step); @@ -218,11 +218,11 @@ void VariableSizeBokehBlurOperation::executeOpenCL(OpenCLDevice *device, void VariableSizeBokehBlurOperation::deinitExecution() { - m_inputProgram = nullptr; - m_inputBokehProgram = nullptr; - m_inputSizeProgram = nullptr; + inputProgram_ = nullptr; + inputBokehProgram_ = nullptr; + inputSizeProgram_ = nullptr; #ifdef COM_DEFOCUS_SEARCH - m_inputSearchProgram = nullptr; + inputSearchProgram_ = nullptr; #endif } @@ -233,8 +233,8 @@ bool VariableSizeBokehBlurOperation::determineDependingAreaOfInterest( rcti bokehInput; const float max_dim = MAX2(getWidth(), getHeight()); - const float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; - int maxBlurScalar = m_maxBlur * scalar; + const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; + int maxBlurScalar = maxBlur_ * scalar; newInput.xmax = input->xmax + maxBlurScalar + 2; newInput.xmin = input->xmin - maxBlurScalar + 2; @@ -279,8 +279,8 @@ void VariableSizeBokehBlurOperation::get_area_of_interest(const int input_idx, case IMAGE_INPUT_INDEX: case SIZE_INPUT_INDEX: { const float max_dim = MAX2(getWidth(), getHeight()); - const float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; - const int max_blur_scalar = m_maxBlur * scalar; + const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; + const int max_blur_scalar = maxBlur_ * scalar; r_input_area.xmax = output_area.xmax + max_blur_scalar + 2; r_input_area.xmin = output_area.xmin - max_blur_scalar - 2; r_input_area.ymax = output_area.ymax + max_blur_scalar + 2; @@ -389,7 +389,7 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer * p.size_input = inputs[SIZE_INPUT_INDEX]; p.image_input = inputs[IMAGE_INPUT_INDEX]; p.step = QualityStepHelper::getStep(); - p.threshold = m_threshold; + p.threshold = threshold_; p.image_width = this->getWidth(); p.image_height = this->getHeight(); @@ -399,9 +399,9 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer * const float max_size = p.size_input->get_max_value(scalar_area); const float max_dim = MAX2(this->getWidth(), this->getHeight()); - p.scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + p.scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; p.max_blur_scalar = static_cast(max_size * p.scalar); - CLAMP(p.max_blur_scalar, 1, m_maxBlur); + CLAMP(p.max_blur_scalar, 1, maxBlur_); for (BuffersIterator it = output->iterate_with({p.image_input, p.size_input}, area); !it.is_end(); @@ -437,12 +437,12 @@ InverseSearchRadiusOperation::InverseSearchRadiusOperation() this->addInputSocket(DataType::Value, ResizeMode::Align); /* Radius. */ this->addOutputSocket(DataType::Color); this->flags.complex = true; - m_inputRadius = nullptr; + inputRadius_ = nullptr; } void InverseSearchRadiusOperation::initExecution() { - m_inputRadius = this->getInputSocketReader(0); + inputRadius_ = this->getInputSocketReader(0); } void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) @@ -450,18 +450,18 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) MemoryBuffer *data = new MemoryBuffer(DataType::Color, rect); float *buffer = data->getBuffer(); int x, y; - int width = m_inputRadius->getWidth(); - int height = m_inputRadius->getHeight(); + int width = inputRadius_->getWidth(); + int height = inputRadius_->getHeight(); float temp[4]; int offset = 0; for (y = rect->ymin; y < rect->ymax; y++) { for (x = rect->xmin; x < rect->xmax; x++) { int rx = x * DIVIDER; int ry = y * DIVIDER; - buffer[offset] = MAX2(rx - m_maxBlur, 0); - buffer[offset + 1] = MAX2(ry - m_maxBlur, 0); - buffer[offset + 2] = MIN2(rx + DIVIDER + m_maxBlur, width); - buffer[offset + 3] = MIN2(ry + DIVIDER + m_maxBlur, height); + buffer[offset] = MAX2(rx - maxBlur_, 0); + buffer[offset + 1] = MAX2(ry - maxBlur_, 0); + buffer[offset + 2] = MIN2(rx + DIVIDER + maxBlur_, width); + buffer[offset + 3] = MIN2(ry + DIVIDER + maxBlur_, height); offset += 4; } } @@ -476,7 +476,7 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) for (int x2 = 0; x2 < DIVIDER; x2++) { for (int y2 = 0; y2 < DIVIDER; y2++) { - m_inputRadius->read(temp, rx + x2, ry + y2, PixelSampler::Nearest); + inputRadius_->read(temp, rx + x2, ry + y2, PixelSampler::Nearest); if (radius < temp[0]) { radius = temp[0]; maxx = x2; @@ -517,7 +517,7 @@ void InverseSearchRadiusOperation::deinitializeTileData(rcti *rect, void *data) void InverseSearchRadiusOperation::deinitExecution() { - m_inputRadius = nullptr; + inputRadius_ = nullptr; } void InverseSearchRadiusOperation::determineResolution(unsigned int resolution[2], @@ -532,10 +532,10 @@ bool InverseSearchRadiusOperation::determineDependingAreaOfInterest( rcti *input, ReadBufferOperation *readOperation, rcti *output) { rcti newRect; - newRect.ymin = input->ymin * DIVIDER - m_maxBlur; - newRect.ymax = input->ymax * DIVIDER + m_maxBlur; - newRect.xmin = input->xmin * DIVIDER - m_maxBlur; - newRect.xmax = input->xmax * DIVIDER + m_maxBlur; + newRect.ymin = input->ymin * DIVIDER - maxBlur_; + newRect.ymax = input->ymax * DIVIDER + maxBlur_; + newRect.xmin = input->xmin * DIVIDER - maxBlur_; + newRect.xmax = input->xmax * DIVIDER + maxBlur_; return NodeOperation::determineDependingAreaOfInterest(&newRect, readOperation, output); } #endif diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h index 6819e64ce80..c32d10ae3b9 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h @@ -34,14 +34,14 @@ class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public Qua static constexpr int DEFOCUS_INPUT_INDEX = 3; #endif - int m_maxBlur; - float m_threshold; - bool m_do_size_scale; /* scale size, matching 'BokehBlurNode' */ - SocketReader *m_inputProgram; - SocketReader *m_inputBokehProgram; - SocketReader *m_inputSizeProgram; + int maxBlur_; + float threshold_; + bool do_size_scale_; /* scale size, matching 'BokehBlurNode' */ + SocketReader *inputProgram_; + SocketReader *inputBokehProgram_; + SocketReader *inputSizeProgram_; #ifdef COM_DEFOCUS_SEARCH - SocketReader *m_inputSearchProgram; + SocketReader *inputSearchProgram_; #endif public: @@ -72,17 +72,17 @@ class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public Qua void setMaxBlur(int maxRadius) { - m_maxBlur = maxRadius; + maxBlur_ = maxRadius; } void setThreshold(float threshold) { - m_threshold = threshold; + threshold_ = threshold; } void setDoScaleSize(bool scale_size) { - m_do_size_scale = scale_size; + do_size_scale_ = scale_size; } void executeOpenCL(OpenCLDevice *device, @@ -102,8 +102,8 @@ class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public Qua #ifdef COM_DEFOCUS_SEARCH class InverseSearchRadiusOperation : public NodeOperation { private: - int m_maxBlur; - SocketReader *m_inputRadius; + int maxBlur_; + SocketReader *inputRadius_; public: static const int DIVIDER = 4; @@ -134,7 +134,7 @@ class InverseSearchRadiusOperation : public NodeOperation { void setMaxBlur(int maxRadius) { - m_maxBlur = maxRadius; + maxBlur_ = maxRadius; } }; #endif diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc index acde0447596..6ebd8e27d76 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc @@ -46,21 +46,21 @@ VectorBlurOperation::VectorBlurOperation() this->addInputSocket(DataType::Value); /* ZBUF */ this->addInputSocket(DataType::Color); /* SPEED */ this->addOutputSocket(DataType::Color); - m_settings = nullptr; - m_cachedInstance = nullptr; - m_inputImageProgram = nullptr; - m_inputSpeedProgram = nullptr; - m_inputZProgram = nullptr; + settings_ = nullptr; + cachedInstance_ = nullptr; + inputImageProgram_ = nullptr; + inputSpeedProgram_ = nullptr; + inputZProgram_ = nullptr; flags.complex = true; flags.is_fullframe_operation = true; } void VectorBlurOperation::initExecution() { initMutex(); - m_inputImageProgram = getInputSocketReader(0); - m_inputZProgram = getInputSocketReader(1); - m_inputSpeedProgram = getInputSocketReader(2); - m_cachedInstance = nullptr; + inputImageProgram_ = getInputSocketReader(0); + inputZProgram_ = getInputSocketReader(1); + inputSpeedProgram_ = getInputSocketReader(2); + cachedInstance_ = nullptr; QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -74,38 +74,38 @@ void VectorBlurOperation::executePixel(float output[4], int x, int y, void *data void VectorBlurOperation::deinitExecution() { deinitMutex(); - m_inputImageProgram = nullptr; - m_inputSpeedProgram = nullptr; - m_inputZProgram = nullptr; - if (m_cachedInstance) { - MEM_freeN(m_cachedInstance); - m_cachedInstance = nullptr; + inputImageProgram_ = nullptr; + inputSpeedProgram_ = nullptr; + inputZProgram_ = nullptr; + if (cachedInstance_) { + MEM_freeN(cachedInstance_); + cachedInstance_ = nullptr; } } void *VectorBlurOperation::initializeTileData(rcti *rect) { - if (m_cachedInstance) { - return m_cachedInstance; + if (cachedInstance_) { + return cachedInstance_; } lockMutex(); - if (m_cachedInstance == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)m_inputImageProgram->initializeTileData(rect); - MemoryBuffer *speed = (MemoryBuffer *)m_inputSpeedProgram->initializeTileData(rect); - MemoryBuffer *z = (MemoryBuffer *)m_inputZProgram->initializeTileData(rect); + if (cachedInstance_ == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)inputImageProgram_->initializeTileData(rect); + MemoryBuffer *speed = (MemoryBuffer *)inputSpeedProgram_->initializeTileData(rect); + MemoryBuffer *z = (MemoryBuffer *)inputZProgram_->initializeTileData(rect); float *data = (float *)MEM_dupallocN(tile->getBuffer()); this->generateVectorBlur(data, tile, speed, z); - m_cachedInstance = data; + cachedInstance_ = data; } unlockMutex(); - return m_cachedInstance; + return cachedInstance_; } bool VectorBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) { - if (m_cachedInstance == nullptr) { + if (cachedInstance_ == nullptr) { rcti newInput; newInput.xmax = this->getWidth(); newInput.xmin = 0; @@ -129,7 +129,7 @@ void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, Span inputs) { /* TODO(manzanilla): once tiled implementation is removed, run multi-threaded where possible. */ - if (!m_cachedInstance) { + if (!cachedInstance_) { MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; const bool is_image_inflated = image->is_a_single_elem(); image = is_image_inflated ? image->inflate() : image; @@ -142,8 +142,8 @@ void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, const bool is_z_inflated = z->is_a_single_elem(); z = is_z_inflated ? z->inflate() : z; - m_cachedInstance = (float *)MEM_dupallocN(image->getBuffer()); - this->generateVectorBlur(m_cachedInstance, image, speed, z); + cachedInstance_ = (float *)MEM_dupallocN(image->getBuffer()); + this->generateVectorBlur(cachedInstance_, image, speed, z); if (is_image_inflated) { delete image; @@ -155,7 +155,7 @@ void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, } const int num_channels = COM_data_type_num_channels(getOutputSocket()->getDataType()); - MemoryBuffer buf(m_cachedInstance, num_channels, this->getWidth(), this->getHeight()); + MemoryBuffer buf(cachedInstance_, num_channels, this->getWidth(), this->getHeight()); output->copy_from(&buf, area); } @@ -165,11 +165,11 @@ void VectorBlurOperation::generateVectorBlur(float *data, MemoryBuffer *inputZ) { NodeBlurData blurdata; - blurdata.samples = m_settings->samples / QualityStepHelper::getStep(); - blurdata.maxspeed = m_settings->maxspeed; - blurdata.minspeed = m_settings->minspeed; - blurdata.curved = m_settings->curved; - blurdata.fac = m_settings->fac; + blurdata.samples = settings_->samples / QualityStepHelper::getStep(); + blurdata.maxspeed = settings_->maxspeed; + blurdata.minspeed = settings_->minspeed; + blurdata.curved = settings_->curved; + blurdata.fac = settings_->fac; zbuf_accumulate_vecblur(&blurdata, this->getWidth(), this->getHeight(), diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.h b/source/blender/compositor/operations/COM_VectorBlurOperation.h index a6c619590e8..cb384283c79 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.h +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.h @@ -33,16 +33,16 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { /** * \brief Cached reference to the inputProgram */ - SocketReader *m_inputImageProgram; - SocketReader *m_inputSpeedProgram; - SocketReader *m_inputZProgram; + SocketReader *inputImageProgram_; + SocketReader *inputSpeedProgram_; + SocketReader *inputZProgram_; /** * \brief settings of the glare node. */ - NodeBlurData *m_settings; + NodeBlurData *settings_; - float *m_cachedInstance; + float *cachedInstance_; public: VectorBlurOperation(); @@ -66,7 +66,7 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { void setVectorBlurSettings(NodeBlurData *settings) { - m_settings = settings; + settings_ = settings; } bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cc b/source/blender/compositor/operations/COM_VectorCurveOperation.cc index 2dd8f114038..ade0c5ee788 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cc @@ -27,12 +27,12 @@ VectorCurveOperation::VectorCurveOperation() this->addInputSocket(DataType::Vector); this->addOutputSocket(DataType::Vector); - m_inputProgram = nullptr; + inputProgram_ = nullptr; } void VectorCurveOperation::initExecution() { CurveBaseOperation::initExecution(); - m_inputProgram = this->getInputSocketReader(0); + inputProgram_ = this->getInputSocketReader(0); } void VectorCurveOperation::executePixelSampled(float output[4], @@ -42,22 +42,22 @@ void VectorCurveOperation::executePixelSampled(float output[4], { float input[4]; - m_inputProgram->readSampled(input, x, y, sampler); + inputProgram_->readSampled(input, x, y, sampler); - BKE_curvemapping_evaluate_premulRGBF(m_curveMapping, output, input); + BKE_curvemapping_evaluate_premulRGBF(curveMapping_, output, input); } void VectorCurveOperation::deinitExecution() { CurveBaseOperation::deinitExecution(); - m_inputProgram = nullptr; + inputProgram_ = nullptr; } void VectorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *curve_map = m_curveMapping; + CurveMapping *curve_map = curveMapping_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { BKE_curvemapping_evaluate_premulRGBF(curve_map, it.out, it.in(0)); } diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.h b/source/blender/compositor/operations/COM_VectorCurveOperation.h index 27b3ad69e17..0f819628935 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.h +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.h @@ -28,7 +28,7 @@ class VectorCurveOperation : public CurveBaseOperation { /** * Cached reference to the inputProgram */ - SocketReader *m_inputProgram; + SocketReader *inputProgram_; public: VectorCurveOperation(); diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index effda365d56..5360b118dee 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -33,23 +33,23 @@ ViewerOperation::ViewerOperation() { this->setImage(nullptr); this->setImageUser(nullptr); - m_outputBuffer = nullptr; - m_depthBuffer = nullptr; - m_active = false; - m_doDepthBuffer = false; - m_viewSettings = nullptr; - m_displaySettings = nullptr; - m_useAlphaInput = false; + outputBuffer_ = nullptr; + depthBuffer_ = nullptr; + active_ = false; + doDepthBuffer_ = false; + viewSettings_ = nullptr; + displaySettings_ = nullptr; + useAlphaInput_ = false; this->addInputSocket(DataType::Color); this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); - m_imageInput = nullptr; - m_alphaInput = nullptr; - m_depthInput = nullptr; - m_rd = nullptr; - m_viewName = nullptr; + imageInput_ = nullptr; + alphaInput_ = nullptr; + depthInput_ = nullptr; + rd_ = nullptr; + viewName_ = nullptr; flags.use_viewer_border = true; flags.is_viewer_operation = true; } @@ -57,10 +57,10 @@ ViewerOperation::ViewerOperation() void ViewerOperation::initExecution() { /* When initializing the tree during initial load the width and height can be zero. */ - m_imageInput = getInputSocketReader(0); - m_alphaInput = getInputSocketReader(1); - m_depthInput = getInputSocketReader(2); - m_doDepthBuffer = (m_depthInput != nullptr); + imageInput_ = getInputSocketReader(0); + alphaInput_ = getInputSocketReader(1); + depthInput_ = getInputSocketReader(2); + doDepthBuffer_ = (depthInput_ != nullptr); if (isActiveViewerOutput() && !exec_system_->is_breaked()) { initImage(); @@ -69,16 +69,16 @@ void ViewerOperation::initExecution() void ViewerOperation::deinitExecution() { - m_imageInput = nullptr; - m_alphaInput = nullptr; - m_depthInput = nullptr; - m_outputBuffer = nullptr; + imageInput_ = nullptr; + alphaInput_ = nullptr; + depthInput_ = nullptr; + outputBuffer_ = nullptr; } void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) { - float *buffer = m_outputBuffer; - float *depthbuffer = m_depthBuffer; + float *buffer = outputBuffer_; + float *depthbuffer = depthBuffer_; if (!buffer) { return; } @@ -97,12 +97,12 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) for (y = y1; y < y2 && (!breaked); y++) { for (x = x1; x < x2; x++) { - m_imageInput->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); - if (m_useAlphaInput) { - m_alphaInput->readSampled(alpha, x, y, PixelSampler::Nearest); + imageInput_->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); + if (useAlphaInput_) { + alphaInput_->readSampled(alpha, x, y, PixelSampler::Nearest); buffer[offset4 + 3] = alpha[0]; } - m_depthInput->readSampled(depth, x, y, PixelSampler::Nearest); + depthInput_->readSampled(depth, x, y, PixelSampler::Nearest); depthbuffer[offset] = depth[0]; offset++; @@ -119,8 +119,8 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - const int sceneRenderWidth = m_rd->xsch * m_rd->size / 100; - const int sceneRenderHeight = m_rd->ysch * m_rd->size / 100; + const int sceneRenderWidth = rd_->xsch * rd_->size / 100; + const int sceneRenderHeight = rd_->ysch * rd_->size / 100; rcti local_preferred = preferred_area; local_preferred.xmax = local_preferred.xmin + sceneRenderWidth; @@ -131,20 +131,20 @@ void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) void ViewerOperation::initImage() { - Image *ima = m_image; - ImageUser iuser = *m_imageUser; + Image *ima = image_; + ImageUser iuser = *imageUser_; void *lock; ImBuf *ibuf; /* make sure the image has the correct number of views */ - if (ima && BKE_scene_multiview_is_render_view_first(m_rd, m_viewName)) { - BKE_image_ensure_viewer_views(m_rd, ima, m_imageUser); + if (ima && BKE_scene_multiview_is_render_view_first(rd_, viewName_)) { + BKE_image_ensure_viewer_views(rd_, ima, imageUser_); } BLI_thread_lock(LOCK_DRAW_IMAGE); /* local changes to the original ImageUser */ - iuser.multi_index = BKE_scene_multiview_view_id_get(m_rd, m_viewName); + iuser.multi_index = BKE_scene_multiview_view_id_get(rd_, viewName_); ibuf = BKE_image_acquire_ibuf(ima, &iuser, &lock); if (!ibuf) { @@ -179,21 +179,21 @@ void ViewerOperation::initImage() ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; } - if (m_doDepthBuffer) { + if (doDepthBuffer_) { addzbuffloatImBuf(ibuf); } /* now we combine the input with ibuf */ - m_outputBuffer = ibuf->rect_float; + outputBuffer_ = ibuf->rect_float; /* needed for display buffer update */ - m_ibuf = ibuf; + ibuf_ = ibuf; - if (m_doDepthBuffer) { - m_depthBuffer = ibuf->zbuf_float; + if (doDepthBuffer_) { + depthBuffer_ = ibuf->zbuf_float; } - BKE_image_release_ibuf(m_image, m_ibuf, lock); + BKE_image_release_ibuf(image_, ibuf_, lock); BLI_thread_unlock(LOCK_DRAW_IMAGE); } @@ -204,20 +204,20 @@ void ViewerOperation::updateImage(const rcti *rect) return; } - float *buffer = m_outputBuffer; - IMB_partial_display_buffer_update(m_ibuf, + float *buffer = outputBuffer_; + IMB_partial_display_buffer_update(ibuf_, buffer, nullptr, display_width_, 0, 0, - m_viewSettings, - m_displaySettings, + viewSettings_, + displaySettings_, rect->xmin, rect->ymin, rect->xmax, rect->ymax); - m_image->gpuflag |= IMA_GPU_REFRESH; + image_->gpuflag |= IMA_GPU_REFRESH; this->updateDraw(); } @@ -234,25 +234,25 @@ void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), const rcti &area, Span inputs) { - if (!m_outputBuffer) { + if (!outputBuffer_) { return; } const int offset_x = area.xmin + (canvas_.xmin > 0 ? canvas_.xmin * 2 : 0); const int offset_y = area.ymin + (canvas_.ymin > 0 ? canvas_.ymin * 2 : 0); MemoryBuffer output_buffer( - m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, display_width_, display_height_); + outputBuffer_, COM_DATA_TYPE_COLOR_CHANNELS, display_width_, display_height_); const MemoryBuffer *input_image = inputs[0]; output_buffer.copy_from(input_image, area, offset_x, offset_y); - if (m_useAlphaInput) { + if (useAlphaInput_) { const MemoryBuffer *input_alpha = inputs[1]; output_buffer.copy_from( input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, offset_x, offset_y, 3); } - if (m_depthBuffer) { + if (depthBuffer_) { MemoryBuffer depth_buffer( - m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, display_width_, display_height_); + depthBuffer_, COM_DATA_TYPE_VALUE_CHANNELS, display_width_, display_height_); const MemoryBuffer *input_depth = inputs[2]; depth_buffer.copy_from(input_depth, area, offset_x, offset_y); } @@ -274,15 +274,15 @@ void ViewerOperation::clear_display_buffer() } initImage(); - if (m_outputBuffer == nullptr) { + if (outputBuffer_ == nullptr) { return; } - size_t buf_bytes = (size_t)m_ibuf->y * m_ibuf->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float); + size_t buf_bytes = (size_t)ibuf_->y * ibuf_->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float); if (buf_bytes > 0) { - memset(m_outputBuffer, 0, buf_bytes); + memset(outputBuffer_, 0, buf_bytes); rcti display_area; - BLI_rcti_init(&display_area, 0, m_ibuf->x, 0, m_ibuf->y); + BLI_rcti_init(&display_area, 0, ibuf_->x, 0, ibuf_->y); updateImage(&display_area); } } diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h index 137b7be53a9..9a812c8d87d 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.h +++ b/source/blender/compositor/operations/COM_ViewerOperation.h @@ -28,27 +28,27 @@ namespace blender::compositor { class ViewerOperation : public MultiThreadedOperation { private: /* TODO(manzanilla): To be removed together with tiled implementation. */ - float *m_outputBuffer; - float *m_depthBuffer; + float *outputBuffer_; + float *depthBuffer_; - Image *m_image; - ImageUser *m_imageUser; - bool m_active; - float m_centerX; - float m_centerY; - ChunkOrdering m_chunkOrder; - bool m_doDepthBuffer; - ImBuf *m_ibuf; - bool m_useAlphaInput; - const RenderData *m_rd; - const char *m_viewName; + Image *image_; + ImageUser *imageUser_; + bool active_; + float centerX_; + float centerY_; + ChunkOrdering chunkOrder_; + bool doDepthBuffer_; + ImBuf *ibuf_; + bool useAlphaInput_; + const RenderData *rd_; + const char *viewName_; - const ColorManagedViewSettings *m_viewSettings; - const ColorManagedDisplaySettings *m_displaySettings; + const ColorManagedViewSettings *viewSettings_; + const ColorManagedDisplaySettings *displaySettings_; - SocketReader *m_imageInput; - SocketReader *m_alphaInput; - SocketReader *m_depthInput; + SocketReader *imageInput_; + SocketReader *alphaInput_; + SocketReader *depthInput_; int display_width_; int display_height_; @@ -68,65 +68,65 @@ class ViewerOperation : public MultiThreadedOperation { } void setImage(Image *image) { - m_image = image; + image_ = image; } void setImageUser(ImageUser *imageUser) { - m_imageUser = imageUser; + imageUser_ = imageUser; } bool isActiveViewerOutput() const override { - return m_active; + return active_; } void setActive(bool active) { - m_active = active; + active_ = active; } void setCenterX(float centerX) { - m_centerX = centerX; + centerX_ = centerX; } void setCenterY(float centerY) { - m_centerY = centerY; + centerY_ = centerY; } void setChunkOrder(ChunkOrdering tileOrder) { - m_chunkOrder = tileOrder; + chunkOrder_ = tileOrder; } float getCenterX() const { - return m_centerX; + return centerX_; } float getCenterY() const { - return m_centerY; + return centerY_; } ChunkOrdering getChunkOrder() const { - return m_chunkOrder; + return chunkOrder_; } eCompositorPriority getRenderPriority() const override; void setUseAlphaInput(bool value) { - m_useAlphaInput = value; + useAlphaInput_ = value; } void setRenderData(const RenderData *rd) { - m_rd = rd; + rd_ = rd; } void setViewName(const char *viewName) { - m_viewName = viewName; + viewName_ = viewName; } void setViewSettings(const ColorManagedViewSettings *viewSettings) { - m_viewSettings = viewSettings; + viewSettings_ = viewSettings; } void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) { - m_displaySettings = displaySettings; + displaySettings_ = displaySettings; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc index 7e49d7a7a79..be4798c605f 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.cc +++ b/source/blender/compositor/operations/COM_WrapOperation.cc @@ -24,7 +24,7 @@ namespace blender::compositor { WrapOperation::WrapOperation(DataType datatype) : ReadBufferOperation(datatype) { - m_wrappingType = CMP_NODE_WRAP_NONE; + wrappingType_ = CMP_NODE_WRAP_NONE; } inline float WrapOperation::getWrappedOriginalXPos(float x) @@ -55,7 +55,7 @@ void WrapOperation::executePixelSampled(float output[4], float x, float y, Pixel nx = x; ny = y; MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, extend_y = MemoryBufferExtend::Clip; - switch (m_wrappingType) { + switch (wrappingType_) { case CMP_NODE_WRAP_NONE: /* Intentionally empty, originalXPos and originalYPos have been set before. */ break; @@ -91,7 +91,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input, newInput.ymin = input->ymin; newInput.ymax = input->ymax; - if (ELEM(m_wrappingType, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY)) { + if (ELEM(wrappingType_, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY)) { /* Wrap only on the x-axis if tile is wrapping. */ newInput.xmin = getWrappedOriginalXPos(input->xmin); newInput.xmax = roundf(getWrappedOriginalXPos(input->xmax)); @@ -100,7 +100,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input, newInput.xmax = this->getWidth(); } } - if (ELEM(m_wrappingType, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY)) { + if (ELEM(wrappingType_, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY)) { /* Wrap only on the y-axis if tile is wrapping. */ newInput.ymin = getWrappedOriginalYPos(input->ymin); newInput.ymax = roundf(getWrappedOriginalYPos(input->ymax)); @@ -115,7 +115,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input, void WrapOperation::setWrapping(int wrapping_type) { - m_wrappingType = wrapping_type; + wrappingType_ = wrapping_type; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_WrapOperation.h b/source/blender/compositor/operations/COM_WrapOperation.h index 6279129a550..15fc43cc65a 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.h +++ b/source/blender/compositor/operations/COM_WrapOperation.h @@ -24,7 +24,7 @@ namespace blender::compositor { class WrapOperation : public ReadBufferOperation { private: - int m_wrappingType; + int wrappingType_; public: WrapOperation(DataType datatype); diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cc b/source/blender/compositor/operations/COM_WriteBufferOperation.cc index ffc3788de12..26c1d71c9da 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.cc +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cc @@ -24,16 +24,16 @@ namespace blender::compositor { WriteBufferOperation::WriteBufferOperation(DataType datatype) { this->addInputSocket(datatype); - m_memoryProxy = new MemoryProxy(datatype); - m_memoryProxy->setWriteBufferOperation(this); - m_memoryProxy->setExecutor(nullptr); + memoryProxy_ = new MemoryProxy(datatype); + memoryProxy_->setWriteBufferOperation(this); + memoryProxy_->setExecutor(nullptr); flags.is_write_buffer_operation = true; } WriteBufferOperation::~WriteBufferOperation() { - if (m_memoryProxy) { - delete m_memoryProxy; - m_memoryProxy = nullptr; + if (memoryProxy_) { + delete memoryProxy_; + memoryProxy_ = nullptr; } } @@ -42,28 +42,28 @@ void WriteBufferOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - m_input->readSampled(output, x, y, sampler); + input_->readSampled(output, x, y, sampler); } void WriteBufferOperation::initExecution() { - m_input = this->getInputOperation(0); - m_memoryProxy->allocate(this->getWidth(), this->getHeight()); + input_ = this->getInputOperation(0); + memoryProxy_->allocate(this->getWidth(), this->getHeight()); } void WriteBufferOperation::deinitExecution() { - m_input = nullptr; - m_memoryProxy->free(); + input_ = nullptr; + memoryProxy_->free(); } void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) { - MemoryBuffer *memoryBuffer = m_memoryProxy->getBuffer(); + MemoryBuffer *memoryBuffer = memoryProxy_->getBuffer(); float *buffer = memoryBuffer->getBuffer(); const uint8_t num_channels = memoryBuffer->get_num_channels(); - if (m_input->get_flags().complex) { - void *data = m_input->initializeTileData(rect); + if (input_->get_flags().complex) { + void *data = input_->initializeTileData(rect); int x1 = rect->xmin; int y1 = rect->ymin; int x2 = rect->xmax; @@ -74,7 +74,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ for (y = y1; y < y2 && (!breaked); y++) { int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels; for (x = x1; x < x2; x++) { - m_input->read(&(buffer[offset4]), x, y, data); + input_->read(&(buffer[offset4]), x, y, data); offset4 += num_channels; } if (isBraked()) { @@ -82,7 +82,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ } } if (data) { - m_input->deinitializeTileData(rect, data); + input_->deinitializeTileData(rect, data); data = nullptr; } } @@ -98,7 +98,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ for (y = y1; y < y2 && (!breaked); y++) { int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels; for (x = x1; x < x2; x++) { - m_input->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); + input_->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); offset4 += num_channels; } if (isBraked()) { @@ -147,12 +147,12 @@ void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, clMemToCleanUp->push_back(clOutputBuffer); std::list *clKernelsToCleanUp = new std::list(); - m_input->executeOpenCL(device, - outputBuffer, - clOutputBuffer, - inputMemoryBuffers, - clMemToCleanUp, - clKernelsToCleanUp); + input_->executeOpenCL(device, + outputBuffer, + clOutputBuffer, + inputMemoryBuffers, + clMemToCleanUp, + clKernelsToCleanUp); /* STEP 3 */ @@ -208,14 +208,14 @@ void WriteBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_ { NodeOperation::determine_canvas(preferred_area, r_area); /* make sure there is at least one pixel stored in case the input is a single value */ - m_single_value = false; + single_value_ = false; if (BLI_rcti_size_x(&r_area) == 0) { r_area.xmax += 1; - m_single_value = true; + single_value_ = true; } if (BLI_rcti_size_y(&r_area) == 0) { r_area.ymax += 1; - m_single_value = true; + single_value_ = true; } } diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.h b/source/blender/compositor/operations/COM_WriteBufferOperation.h index 4e6e81b7a7f..bd8a2876a42 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.h +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.h @@ -31,21 +31,21 @@ class MemoryProxy; * \ingroup Operation */ class WriteBufferOperation : public NodeOperation { - MemoryProxy *m_memoryProxy; - bool m_single_value; /* single value stored in buffer */ - NodeOperation *m_input; + MemoryProxy *memoryProxy_; + bool single_value_; /* single value stored in buffer */ + NodeOperation *input_; public: WriteBufferOperation(DataType datatype); ~WriteBufferOperation(); MemoryProxy *getMemoryProxy() { - return m_memoryProxy; + return memoryProxy_; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; bool isSingleValue() const { - return m_single_value; + return single_value_; } void executeRegion(rcti *rect, unsigned int tileNumber) override; @@ -60,7 +60,7 @@ class WriteBufferOperation : public NodeOperation { void readResolutionFromInputSocket(); inline NodeOperation *getInput() { - return m_input; + return input_; } }; diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc index ed77d05738d..3a0126a12d4 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.cc +++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc @@ -28,19 +28,19 @@ ZCombineOperation::ZCombineOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); - m_image1Reader = nullptr; - m_depth1Reader = nullptr; - m_image2Reader = nullptr; - m_depth2Reader = nullptr; + image1Reader_ = nullptr; + depth1Reader_ = nullptr; + image2Reader_ = nullptr; + depth2Reader_ = nullptr; this->flags.can_be_constant = true; } void ZCombineOperation::initExecution() { - m_image1Reader = this->getInputSocketReader(0); - m_depth1Reader = this->getInputSocketReader(1); - m_image2Reader = this->getInputSocketReader(2); - m_depth2Reader = this->getInputSocketReader(3); + image1Reader_ = this->getInputSocketReader(0); + depth1Reader_ = this->getInputSocketReader(1); + image2Reader_ = this->getInputSocketReader(2); + depth2Reader_ = this->getInputSocketReader(3); } void ZCombineOperation::executePixelSampled(float output[4], @@ -51,13 +51,13 @@ void ZCombineOperation::executePixelSampled(float output[4], float depth1[4]; float depth2[4]; - m_depth1Reader->readSampled(depth1, x, y, sampler); - m_depth2Reader->readSampled(depth2, x, y, sampler); + depth1Reader_->readSampled(depth1, x, y, sampler); + depth2Reader_->readSampled(depth2, x, y, sampler); if (depth1[0] < depth2[0]) { - m_image1Reader->readSampled(output, x, y, sampler); + image1Reader_->readSampled(output, x, y, sampler); } else { - m_image2Reader->readSampled(output, x, y, sampler); + image2Reader_->readSampled(output, x, y, sampler); } } @@ -83,15 +83,15 @@ void ZCombineAlphaOperation::executePixelSampled(float output[4], float color1[4]; float color2[4]; - m_depth1Reader->readSampled(depth1, x, y, sampler); - m_depth2Reader->readSampled(depth2, x, y, sampler); + depth1Reader_->readSampled(depth1, x, y, sampler); + depth2Reader_->readSampled(depth2, x, y, sampler); if (depth1[0] <= depth2[0]) { - m_image1Reader->readSampled(color1, x, y, sampler); - m_image2Reader->readSampled(color2, x, y, sampler); + image1Reader_->readSampled(color1, x, y, sampler); + image2Reader_->readSampled(color2, x, y, sampler); } else { - m_image1Reader->readSampled(color2, x, y, sampler); - m_image2Reader->readSampled(color1, x, y, sampler); + image1Reader_->readSampled(color2, x, y, sampler); + image2Reader_->readSampled(color1, x, y, sampler); } float fac = color1[3]; float ifac = 1.0f - fac; @@ -129,10 +129,10 @@ void ZCombineAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, void ZCombineOperation::deinitExecution() { - m_image1Reader = nullptr; - m_depth1Reader = nullptr; - m_image2Reader = nullptr; - m_depth2Reader = nullptr; + image1Reader_ = nullptr; + depth1Reader_ = nullptr; + image2Reader_ = nullptr; + depth2Reader_ = nullptr; } // MASK combine @@ -143,16 +143,16 @@ ZCombineMaskOperation::ZCombineMaskOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); - m_maskReader = nullptr; - m_image1Reader = nullptr; - m_image2Reader = nullptr; + maskReader_ = nullptr; + image1Reader_ = nullptr; + image2Reader_ = nullptr; } void ZCombineMaskOperation::initExecution() { - m_maskReader = this->getInputSocketReader(0); - m_image1Reader = this->getInputSocketReader(1); - m_image2Reader = this->getInputSocketReader(2); + maskReader_ = this->getInputSocketReader(0); + image1Reader_ = this->getInputSocketReader(1); + image2Reader_ = this->getInputSocketReader(2); } void ZCombineMaskOperation::executePixelSampled(float output[4], @@ -164,9 +164,9 @@ void ZCombineMaskOperation::executePixelSampled(float output[4], float color1[4]; float color2[4]; - m_maskReader->readSampled(mask, x, y, sampler); - m_image1Reader->readSampled(color1, x, y, sampler); - m_image2Reader->readSampled(color2, x, y, sampler); + maskReader_->readSampled(mask, x, y, sampler); + image1Reader_->readSampled(color1, x, y, sampler); + image2Reader_->readSampled(color2, x, y, sampler); interp_v4_v4v4(output, color1, color2, 1.0f - mask[0]); } @@ -192,9 +192,9 @@ void ZCombineMaskAlphaOperation::executePixelSampled(float output[4], float color1[4]; float color2[4]; - m_maskReader->readSampled(mask, x, y, sampler); - m_image1Reader->readSampled(color1, x, y, sampler); - m_image2Reader->readSampled(color2, x, y, sampler); + maskReader_->readSampled(mask, x, y, sampler); + image1Reader_->readSampled(color1, x, y, sampler); + image2Reader_->readSampled(color2, x, y, sampler); float fac = (1.0f - mask[0]) * (1.0f - color1[3]) + mask[0] * color2[3]; float mfac = 1.0f - fac; @@ -225,9 +225,9 @@ void ZCombineMaskAlphaOperation::update_memory_buffer_partial(MemoryBuffer *outp void ZCombineMaskOperation::deinitExecution() { - m_image1Reader = nullptr; - m_maskReader = nullptr; - m_image2Reader = nullptr; + image1Reader_ = nullptr; + maskReader_ = nullptr; + image2Reader_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.h b/source/blender/compositor/operations/COM_ZCombineOperation.h index acd60b6c866..612107e3c77 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.h +++ b/source/blender/compositor/operations/COM_ZCombineOperation.h @@ -28,10 +28,10 @@ namespace blender::compositor { */ class ZCombineOperation : public MultiThreadedOperation { protected: - SocketReader *m_image1Reader; - SocketReader *m_depth1Reader; - SocketReader *m_image2Reader; - SocketReader *m_depth2Reader; + SocketReader *image1Reader_; + SocketReader *depth1Reader_; + SocketReader *image2Reader_; + SocketReader *depth2Reader_; public: /** @@ -62,9 +62,9 @@ class ZCombineAlphaOperation : public ZCombineOperation { class ZCombineMaskOperation : public MultiThreadedOperation { protected: - SocketReader *m_maskReader; - SocketReader *m_image1Reader; - SocketReader *m_image2Reader; + SocketReader *maskReader_; + SocketReader *image1Reader_; + SocketReader *image2Reader_; public: ZCombineMaskOperation(); From 1c42d4930a24d639b3aa561b9a8b4bbce05977e0 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Wed, 13 Oct 2021 23:01:15 +0200 Subject: [PATCH 0768/1500] Cleanup: convert camelCase naming to snake_case in Compositor To convert old code to the current convention and use a single code style. --- source/blender/compositor/COM_compositor.h | 58 +- .../compositor/intern/COM_BufferOperation.cc | 29 +- .../compositor/intern/COM_BufferOperation.h | 11 +- .../compositor/intern/COM_CPUDevice.cc | 8 +- .../intern/COM_CompositorContext.cc | 12 +- .../compositor/intern/COM_CompositorContext.h | 84 +- .../compositor/intern/COM_ConstantFolder.cc | 22 +- .../compositor/intern/COM_Converter.cc | 143 +-- .../blender/compositor/intern/COM_Converter.h | 4 +- source/blender/compositor/intern/COM_Debug.cc | 36 +- source/blender/compositor/intern/COM_Debug.h | 2 +- .../compositor/intern/COM_ExecutionGroup.cc | 247 ++--- .../compositor/intern/COM_ExecutionGroup.h | 89 +- .../compositor/intern/COM_ExecutionModel.cc | 6 +- .../compositor/intern/COM_ExecutionSystem.cc | 32 +- .../compositor/intern/COM_ExecutionSystem.h | 20 +- .../intern/COM_FullFrameExecutionModel.cc | 57 +- .../compositor/intern/COM_MemoryBuffer.cc | 45 +- .../compositor/intern/COM_MemoryBuffer.h | 80 +- .../compositor/intern/COM_MemoryProxy.cc | 2 +- .../compositor/intern/COM_MemoryProxy.h | 18 +- .../blender/compositor/intern/COM_MetaData.cc | 29 +- .../blender/compositor/intern/COM_MetaData.h | 14 +- source/blender/compositor/intern/COM_Node.cc | 76 +- source/blender/compositor/intern/COM_Node.h | 106 +- .../compositor/intern/COM_NodeConverter.cc | 105 +- .../compositor/intern/COM_NodeConverter.h | 34 +- .../compositor/intern/COM_NodeGraph.cc | 28 +- .../blender/compositor/intern/COM_NodeGraph.h | 2 +- .../compositor/intern/COM_NodeOperation.cc | 141 +-- .../compositor/intern/COM_NodeOperation.h | 186 ++-- .../intern/COM_NodeOperationBuilder.cc | 294 +++--- .../intern/COM_NodeOperationBuilder.h | 18 +- .../compositor/intern/COM_OpenCLDevice.cc | 170 ++-- .../compositor/intern/COM_OpenCLDevice.h | 65 +- .../intern/COM_SingleThreadedOperation.cc | 36 +- .../intern/COM_SingleThreadedOperation.h | 16 +- .../intern/COM_TiledExecutionModel.cc | 44 +- .../compositor/intern/COM_WorkPackage.h | 2 +- .../compositor/intern/COM_WorkScheduler.cc | 46 +- .../compositor/intern/COM_WorkScheduler.h | 2 +- .../compositor/intern/COM_compositor.cc | 18 +- .../compositor/nodes/COM_AlphaOverNode.cc | 48 +- .../compositor/nodes/COM_AlphaOverNode.h | 6 +- .../compositor/nodes/COM_AntiAliasingNode.cc | 28 +- .../compositor/nodes/COM_AntiAliasingNode.h | 6 +- .../compositor/nodes/COM_BilateralBlurNode.cc | 20 +- .../compositor/nodes/COM_BilateralBlurNode.h | 6 +- .../blender/compositor/nodes/COM_BlurNode.cc | 150 +-- .../blender/compositor/nodes/COM_BlurNode.h | 6 +- .../compositor/nodes/COM_BokehBlurNode.cc | 52 +- .../compositor/nodes/COM_BokehBlurNode.h | 6 +- .../compositor/nodes/COM_BokehImageNode.cc | 14 +- .../compositor/nodes/COM_BokehImageNode.h | 6 +- .../compositor/nodes/COM_BoxMaskNode.cc | 57 +- .../compositor/nodes/COM_BoxMaskNode.h | 6 +- .../compositor/nodes/COM_BrightnessNode.cc | 20 +- .../compositor/nodes/COM_BrightnessNode.h | 6 +- .../compositor/nodes/COM_ChannelMatteNode.cc | 54 +- .../compositor/nodes/COM_ChannelMatteNode.h | 6 +- .../compositor/nodes/COM_ChromaMatteNode.cc | 50 +- .../compositor/nodes/COM_ChromaMatteNode.h | 6 +- .../compositor/nodes/COM_ColorBalanceNode.cc | 34 +- .../compositor/nodes/COM_ColorBalanceNode.h | 6 +- .../nodes/COM_ColorCorrectionNode.cc | 24 +- .../nodes/COM_ColorCorrectionNode.h | 6 +- .../compositor/nodes/COM_ColorCurveNode.cc | 40 +- .../compositor/nodes/COM_ColorCurveNode.h | 6 +- .../compositor/nodes/COM_ColorExposureNode.cc | 14 +- .../compositor/nodes/COM_ColorExposureNode.h | 6 +- .../compositor/nodes/COM_ColorMatteNode.cc | 46 +- .../compositor/nodes/COM_ColorMatteNode.h | 6 +- .../blender/compositor/nodes/COM_ColorNode.cc | 16 +- .../blender/compositor/nodes/COM_ColorNode.h | 6 +- .../compositor/nodes/COM_ColorRampNode.cc | 30 +- .../compositor/nodes/COM_ColorRampNode.h | 6 +- .../compositor/nodes/COM_ColorSpillNode.cc | 28 +- .../compositor/nodes/COM_ColorSpillNode.h | 6 +- .../compositor/nodes/COM_ColorToBWNode.cc | 18 +- .../compositor/nodes/COM_ColorToBWNode.h | 6 +- .../compositor/nodes/COM_CombineColorNode.cc | 54 +- .../compositor/nodes/COM_CombineColorNode.h | 24 +- .../compositor/nodes/COM_CompositorNode.cc | 46 +- .../compositor/nodes/COM_CompositorNode.h | 6 +- .../compositor/nodes/COM_ConvertAlphaNode.cc | 12 +- .../compositor/nodes/COM_ConvertAlphaNode.h | 6 +- .../compositor/nodes/COM_CornerPinNode.cc | 28 +- .../compositor/nodes/COM_CornerPinNode.h | 6 +- .../blender/compositor/nodes/COM_CropNode.cc | 24 +- .../blender/compositor/nodes/COM_CropNode.h | 6 +- .../compositor/nodes/COM_CryptomatteNode.cc | 86 +- .../compositor/nodes/COM_CryptomatteNode.h | 4 +- .../compositor/nodes/COM_DefocusNode.cc | 100 +- .../compositor/nodes/COM_DefocusNode.h | 6 +- .../compositor/nodes/COM_DenoiseNode.cc | 36 +- .../compositor/nodes/COM_DenoiseNode.h | 6 +- .../compositor/nodes/COM_DespeckleNode.cc | 28 +- .../compositor/nodes/COM_DespeckleNode.h | 6 +- .../nodes/COM_DifferenceMatteNode.cc | 38 +- .../nodes/COM_DifferenceMatteNode.h | 6 +- .../compositor/nodes/COM_DilateErodeNode.cc | 132 +-- .../compositor/nodes/COM_DilateErodeNode.h | 6 +- .../nodes/COM_DirectionalBlurNode.cc | 18 +- .../nodes/COM_DirectionalBlurNode.h | 6 +- .../compositor/nodes/COM_DisplaceNode.cc | 20 +- .../compositor/nodes/COM_DisplaceNode.h | 6 +- .../compositor/nodes/COM_DistanceMatteNode.cc | 75 +- .../compositor/nodes/COM_DistanceMatteNode.h | 6 +- .../nodes/COM_DoubleEdgeMaskNode.cc | 20 +- .../compositor/nodes/COM_DoubleEdgeMaskNode.h | 6 +- .../compositor/nodes/COM_EllipseMaskNode.cc | 57 +- .../compositor/nodes/COM_EllipseMaskNode.h | 6 +- .../compositor/nodes/COM_FilterNode.cc | 24 +- .../blender/compositor/nodes/COM_FilterNode.h | 6 +- .../blender/compositor/nodes/COM_FlipNode.cc | 18 +- .../blender/compositor/nodes/COM_FlipNode.h | 6 +- .../blender/compositor/nodes/COM_GammaNode.cc | 14 +- .../blender/compositor/nodes/COM_GammaNode.h | 6 +- .../blender/compositor/nodes/COM_GlareNode.cc | 39 +- .../blender/compositor/nodes/COM_GlareNode.h | 6 +- .../COM_HueSaturationValueCorrectNode.cc | 37 +- .../nodes/COM_HueSaturationValueCorrectNode.h | 6 +- .../nodes/COM_HueSaturationValueNode.cc | 46 +- .../nodes/COM_HueSaturationValueNode.h | 6 +- .../compositor/nodes/COM_IDMaskNode.cc | 32 +- .../blender/compositor/nodes/COM_IDMaskNode.h | 6 +- .../blender/compositor/nodes/COM_ImageNode.cc | 235 ++--- .../blender/compositor/nodes/COM_ImageNode.h | 24 +- .../compositor/nodes/COM_InpaintNode.cc | 18 +- .../compositor/nodes/COM_InpaintNode.h | 6 +- .../compositor/nodes/COM_InvertNode.cc | 20 +- .../blender/compositor/nodes/COM_InvertNode.h | 6 +- .../compositor/nodes/COM_KeyingNode.cc | 372 +++---- .../blender/compositor/nodes/COM_KeyingNode.h | 58 +- .../compositor/nodes/COM_KeyingScreenNode.cc | 24 +- .../compositor/nodes/COM_KeyingScreenNode.h | 6 +- .../nodes/COM_LensDistortionNode.cc | 40 +- .../compositor/nodes/COM_LensDistortionNode.h | 6 +- .../nodes/COM_LuminanceMatteNode.cc | 34 +- .../compositor/nodes/COM_LuminanceMatteNode.h | 6 +- .../compositor/nodes/COM_MapRangeNode.cc | 34 +- .../compositor/nodes/COM_MapRangeNode.h | 6 +- .../blender/compositor/nodes/COM_MapUVNode.cc | 18 +- .../blender/compositor/nodes/COM_MapUVNode.h | 6 +- .../compositor/nodes/COM_MapValueNode.cc | 22 +- .../compositor/nodes/COM_MapValueNode.h | 6 +- .../blender/compositor/nodes/COM_MaskNode.cc | 52 +- .../blender/compositor/nodes/COM_MaskNode.h | 6 +- .../blender/compositor/nodes/COM_MathNode.cc | 20 +- .../blender/compositor/nodes/COM_MathNode.h | 6 +- .../blender/compositor/nodes/COM_MixNode.cc | 76 +- source/blender/compositor/nodes/COM_MixNode.h | 6 +- .../compositor/nodes/COM_MovieClipNode.cc | 78 +- .../compositor/nodes/COM_MovieClipNode.h | 6 +- .../nodes/COM_MovieDistortionNode.cc | 22 +- .../nodes/COM_MovieDistortionNode.h | 6 +- .../compositor/nodes/COM_NormalNode.cc | 36 +- .../blender/compositor/nodes/COM_NormalNode.h | 6 +- .../compositor/nodes/COM_NormalizeNode.cc | 12 +- .../compositor/nodes/COM_NormalizeNode.h | 6 +- .../compositor/nodes/COM_OutputFileNode.cc | 132 +-- .../compositor/nodes/COM_OutputFileNode.h | 6 +- .../compositor/nodes/COM_PixelateNode.cc | 24 +- .../compositor/nodes/COM_PixelateNode.h | 6 +- .../nodes/COM_PlaneTrackDeformNode.cc | 54 +- .../nodes/COM_PlaneTrackDeformNode.h | 6 +- .../compositor/nodes/COM_PosterizeNode.cc | 14 +- .../compositor/nodes/COM_PosterizeNode.h | 6 +- .../compositor/nodes/COM_RenderLayersNode.cc | 88 +- .../compositor/nodes/COM_RenderLayersNode.h | 30 +- .../compositor/nodes/COM_RotateNode.cc | 30 +- .../blender/compositor/nodes/COM_RotateNode.h | 6 +- .../blender/compositor/nodes/COM_ScaleNode.cc | 82 +- .../blender/compositor/nodes/COM_ScaleNode.h | 6 +- .../compositor/nodes/COM_SeparateColorNode.cc | 74 +- .../compositor/nodes/COM_SeparateColorNode.h | 24 +- .../compositor/nodes/COM_SetAlphaNode.cc | 18 +- .../compositor/nodes/COM_SetAlphaNode.h | 6 +- .../compositor/nodes/COM_SocketProxyNode.cc | 73 +- .../compositor/nodes/COM_SocketProxyNode.h | 20 +- .../compositor/nodes/COM_SplitViewerNode.cc | 61 +- .../compositor/nodes/COM_SplitViewerNode.h | 6 +- .../compositor/nodes/COM_Stabilize2dNode.cc | 191 ++-- .../compositor/nodes/COM_Stabilize2dNode.h | 6 +- .../compositor/nodes/COM_SunBeamsNode.cc | 20 +- .../compositor/nodes/COM_SunBeamsNode.h | 6 +- .../compositor/nodes/COM_SwitchNode.cc | 14 +- .../blender/compositor/nodes/COM_SwitchNode.h | 6 +- .../compositor/nodes/COM_SwitchViewNode.cc | 16 +- .../compositor/nodes/COM_SwitchViewNode.h | 6 +- .../compositor/nodes/COM_TextureNode.cc | 46 +- .../compositor/nodes/COM_TextureNode.h | 6 +- .../blender/compositor/nodes/COM_TimeNode.cc | 18 +- .../blender/compositor/nodes/COM_TimeNode.h | 6 +- .../compositor/nodes/COM_TonemapNode.cc | 16 +- .../compositor/nodes/COM_TonemapNode.h | 6 +- .../compositor/nodes/COM_TrackPositionNode.cc | 94 +- .../compositor/nodes/COM_TrackPositionNode.h | 6 +- .../compositor/nodes/COM_TransformNode.cc | 100 +- .../compositor/nodes/COM_TransformNode.h | 6 +- .../compositor/nodes/COM_TranslateNode.cc | 46 +- .../compositor/nodes/COM_TranslateNode.h | 6 +- .../blender/compositor/nodes/COM_ValueNode.cc | 14 +- .../blender/compositor/nodes/COM_ValueNode.h | 6 +- .../compositor/nodes/COM_VectorBlurNode.cc | 24 +- .../compositor/nodes/COM_VectorBlurNode.h | 6 +- .../compositor/nodes/COM_VectorCurveNode.cc | 14 +- .../compositor/nodes/COM_VectorCurveNode.h | 6 +- .../compositor/nodes/COM_ViewLevelsNode.cc | 30 +- .../compositor/nodes/COM_ViewLevelsNode.h | 6 +- .../compositor/nodes/COM_ViewerNode.cc | 70 +- .../blender/compositor/nodes/COM_ViewerNode.h | 6 +- .../compositor/nodes/COM_ZCombineNode.cc | 62 +- .../compositor/nodes/COM_ZCombineNode.h | 6 +- .../operations/COM_AlphaOverKeyOperation.cc | 36 +- .../operations/COM_AlphaOverKeyOperation.h | 2 +- .../operations/COM_AlphaOverMixedOperation.cc | 38 +- .../operations/COM_AlphaOverMixedOperation.h | 2 +- .../COM_AlphaOverPremultiplyOperation.cc | 36 +- .../COM_AlphaOverPremultiplyOperation.h | 2 +- .../operations/COM_AntiAliasOperation.cc | 44 +- .../operations/COM_AntiAliasOperation.h | 16 +- .../operations/COM_BilateralBlurOperation.cc | 87 +- .../operations/COM_BilateralBlurOperation.h | 18 +- .../operations/COM_BlurBaseOperation.cc | 34 +- .../operations/COM_BlurBaseOperation.h | 18 +- .../operations/COM_BokehBlurOperation.cc | 222 ++--- .../operations/COM_BokehBlurOperation.h | 42 +- .../operations/COM_BokehImageOperation.cc | 133 +-- .../operations/COM_BokehImageOperation.h | 36 +- .../operations/COM_BoxMaskOperation.cc | 77 +- .../operations/COM_BoxMaskOperation.h | 22 +- .../operations/COM_BrightnessOperation.cc | 62 +- .../operations/COM_BrightnessOperation.h | 16 +- .../operations/COM_CalculateMeanOperation.cc | 57 +- .../operations/COM_CalculateMeanOperation.h | 20 +- ...COM_CalculateStandardDeviationOperation.cc | 34 +- .../COM_CalculateStandardDeviationOperation.h | 6 +- .../operations/COM_ChangeHSVOperation.cc | 58 +- .../operations/COM_ChangeHSVOperation.h | 14 +- .../operations/COM_ChannelMatteOperation.cc | 32 +- .../operations/COM_ChannelMatteOperation.h | 18 +- .../operations/COM_ChromaMatteOperation.cc | 64 +- .../operations/COM_ChromaMatteOperation.h | 14 +- .../COM_ColorBalanceASCCDLOperation.cc | 50 +- .../COM_ColorBalanceASCCDLOperation.h | 18 +- .../COM_ColorBalanceLGGOperation.cc | 50 +- .../operations/COM_ColorBalanceLGGOperation.h | 18 +- .../COM_ColorCorrectionOperation.cc | 126 +-- .../operations/COM_ColorCorrectionOperation.h | 32 +- .../operations/COM_ColorCurveOperation.cc | 112 +-- .../operations/COM_ColorCurveOperation.h | 32 +- .../operations/COM_ColorExposureOperation.cc | 46 +- .../operations/COM_ColorExposureOperation.h | 12 +- .../operations/COM_ColorMatteOperation.cc | 49 +- .../operations/COM_ColorMatteOperation.h | 14 +- .../operations/COM_ColorRampOperation.cc | 30 +- .../operations/COM_ColorRampOperation.h | 16 +- .../operations/COM_ColorSpillOperation.cc | 54 +- .../operations/COM_ColorSpillOperation.h | 28 +- .../operations/COM_CompositorOperation.cc | 112 +-- .../operations/COM_CompositorOperation.h | 48 +- .../operations/COM_ConstantOperation.h | 2 +- .../COM_ConvertColorProfileOperation.cc | 27 +- .../COM_ConvertColorProfileOperation.h | 24 +- .../COM_ConvertDepthToRadiusOperation.cc | 80 +- .../COM_ConvertDepthToRadiusOperation.h | 38 +- .../operations/COM_ConvertOperation.cc | 365 +++---- .../operations/COM_ConvertOperation.h | 64 +- .../COM_ConvolutionEdgeFilterOperation.cc | 38 +- .../COM_ConvolutionEdgeFilterOperation.h | 2 +- .../COM_ConvolutionFilterOperation.cc | 86 +- .../COM_ConvolutionFilterOperation.h | 20 +- .../operations/COM_CropOperation.cc | 62 +- .../compositor/operations/COM_CropOperation.h | 22 +- .../operations/COM_CryptomatteOperation.cc | 20 +- .../operations/COM_CryptomatteOperation.h | 8 +- .../operations/COM_CurveBaseOperation.cc | 28 +- .../operations/COM_CurveBaseOperation.h | 10 +- .../operations/COM_DenoiseOperation.cc | 97 +- .../operations/COM_DenoiseOperation.h | 32 +- .../operations/COM_DespeckleOperation.cc | 86 +- .../operations/COM_DespeckleOperation.h | 24 +- .../COM_DifferenceMatteOperation.cc | 48 +- .../operations/COM_DifferenceMatteOperation.h | 14 +- .../operations/COM_DilateErodeOperation.cc | 243 ++--- .../operations/COM_DilateErodeOperation.h | 94 +- .../COM_DirectionalBlurOperation.cc | 88 +- .../operations/COM_DirectionalBlurOperation.h | 28 +- .../operations/COM_DisplaceOperation.cc | 102 +- .../operations/COM_DisplaceOperation.h | 18 +- .../operations/COM_DisplaceSimpleOperation.cc | 107 +- .../operations/COM_DisplaceSimpleOperation.h | 22 +- .../COM_DistanceRGBMatteOperation.cc | 50 +- .../COM_DistanceRGBMatteOperation.h | 16 +- .../COM_DistanceYCCMatteOperation.cc | 2 +- .../COM_DistanceYCCMatteOperation.h | 2 +- .../operations/COM_DotproductOperation.cc | 26 +- .../operations/COM_DotproductOperation.h | 6 +- .../operations/COM_DoubleEdgeMaskOperation.cc | 189 ++-- .../operations/COM_DoubleEdgeMaskOperation.h | 36 +- .../operations/COM_EllipseMaskOperation.cc | 80 +- .../operations/COM_EllipseMaskOperation.h | 22 +- .../COM_FastGaussianBlurOperation.cc | 124 +-- .../COM_FastGaussianBlurOperation.h | 32 +- .../operations/COM_FlipOperation.cc | 76 +- .../compositor/operations/COM_FlipOperation.h | 22 +- .../operations/COM_GammaCorrectOperation.cc | 100 +- .../operations/COM_GammaCorrectOperation.h | 20 +- .../operations/COM_GammaOperation.cc | 42 +- .../operations/COM_GammaOperation.h | 12 +- .../COM_GaussianAlphaBlurBaseOperation.cc | 10 +- .../COM_GaussianAlphaBlurBaseOperation.h | 8 +- .../COM_GaussianAlphaXBlurOperation.cc | 80 +- .../COM_GaussianAlphaXBlurOperation.h | 16 +- .../COM_GaussianAlphaYBlurOperation.cc | 78 +- .../COM_GaussianAlphaYBlurOperation.h | 16 +- .../COM_GaussianBlurBaseOperation.cc | 10 +- .../COM_GaussianBlurBaseOperation.h | 4 +- .../COM_GaussianBokehBlurOperation.cc | 181 ++-- .../COM_GaussianBokehBlurOperation.h | 32 +- .../operations/COM_GaussianXBlurOperation.cc | 121 +-- .../operations/COM_GaussianXBlurOperation.h | 30 +- .../operations/COM_GaussianYBlurOperation.cc | 125 +-- .../operations/COM_GaussianYBlurOperation.h | 30 +- .../operations/COM_GlareBaseOperation.cc | 54 +- .../operations/COM_GlareBaseOperation.h | 20 +- .../operations/COM_GlareFogGlowOperation.cc | 96 +- .../operations/COM_GlareFogGlowOperation.h | 2 +- .../operations/COM_GlareGhostOperation.cc | 68 +- .../operations/COM_GlareGhostOperation.h | 2 +- .../COM_GlareSimpleStarOperation.cc | 34 +- .../operations/COM_GlareSimpleStarOperation.h | 2 +- .../operations/COM_GlareStreaksOperation.cc | 32 +- .../operations/COM_GlareStreaksOperation.h | 2 +- .../operations/COM_GlareThresholdOperation.cc | 24 +- .../operations/COM_GlareThresholdOperation.h | 12 +- .../COM_HueSaturationValueCorrectOperation.cc | 40 +- .../COM_HueSaturationValueCorrectOperation.h | 10 +- .../operations/COM_IDMaskOperation.cc | 18 +- .../operations/COM_IDMaskOperation.h | 10 +- .../operations/COM_ImageOperation.cc | 80 +- .../operations/COM_ImageOperation.h | 38 +- .../operations/COM_InpaintOperation.cc | 69 +- .../operations/COM_InpaintOperation.h | 20 +- .../operations/COM_InvertOperation.cc | 51 +- .../operations/COM_InvertOperation.h | 16 +- .../operations/COM_KeyingBlurOperation.cc | 58 +- .../operations/COM_KeyingBlurOperation.h | 14 +- .../operations/COM_KeyingClipOperation.cc | 100 +- .../operations/COM_KeyingClipOperation.h | 40 +- .../operations/COM_KeyingDespillOperation.cc | 62 +- .../operations/COM_KeyingDespillOperation.h | 22 +- .../operations/COM_KeyingOperation.cc | 49 +- .../operations/COM_KeyingOperation.h | 16 +- .../operations/COM_KeyingScreenOperation.cc | 80 +- .../operations/COM_KeyingScreenOperation.h | 28 +- .../operations/COM_LuminanceMatteOperation.cc | 32 +- .../operations/COM_LuminanceMatteOperation.h | 12 +- .../operations/COM_MapRangeOperation.cc | 62 +- .../operations/COM_MapRangeOperation.h | 24 +- .../operations/COM_MapUVOperation.cc | 88 +- .../operations/COM_MapUVOperation.h | 20 +- .../operations/COM_MapValueOperation.cc | 24 +- .../operations/COM_MapValueOperation.h | 12 +- .../operations/COM_MaskOperation.cc | 91 +- .../compositor/operations/COM_MaskOperation.h | 46 +- .../operations/COM_MathBaseOperation.cc | 871 ++++++++--------- .../operations/COM_MathBaseOperation.h | 104 +- .../compositor/operations/COM_MixOperation.cc | 910 +++++++++--------- .../compositor/operations/COM_MixOperation.h | 72 +- .../COM_MovieClipAttributeOperation.cc | 18 +- .../COM_MovieClipAttributeOperation.h | 12 +- .../operations/COM_MovieClipOperation.cc | 68 +- .../operations/COM_MovieClipOperation.h | 34 +- .../COM_MovieDistortionOperation.cc | 75 +- .../operations/COM_MovieDistortionOperation.h | 22 +- .../COM_MultilayerImageOperation.cc | 84 +- .../operations/COM_MultilayerImageOperation.h | 22 +- .../operations/COM_NormalizeOperation.cc | 76 +- .../operations/COM_NormalizeOperation.h | 20 +- .../operations/COM_OpenCLKernels.cl | 196 ++-- .../COM_OutputFileMultiViewOperation.cc | 112 ++- .../COM_OutputFileMultiViewOperation.h | 24 +- .../operations/COM_OutputFileOperation.cc | 180 ++-- .../operations/COM_OutputFileOperation.h | 56 +- .../operations/COM_PixelateOperation.cc | 26 +- .../operations/COM_PixelateOperation.h | 14 +- .../operations/COM_PlaneCornerPinOperation.cc | 107 +- .../operations/COM_PlaneCornerPinOperation.h | 18 +- .../COM_PlaneDistortCommonOperation.cc | 152 +-- .../COM_PlaneDistortCommonOperation.h | 30 +- .../operations/COM_PlaneTrackOperation.cc | 38 +- .../operations/COM_PlaneTrackOperation.h | 26 +- .../operations/COM_PosterizeOperation.cc | 50 +- .../operations/COM_PosterizeOperation.h | 12 +- .../operations/COM_PreviewOperation.cc | 90 +- .../operations/COM_PreviewOperation.h | 36 +- .../COM_ProjectorLensDistortionOperation.cc | 104 +- .../COM_ProjectorLensDistortionOperation.h | 22 +- .../operations/COM_QualityStepHelper.cc | 2 +- .../operations/COM_QualityStepHelper.h | 8 +- .../operations/COM_ReadBufferOperation.cc | 64 +- .../operations/COM_ReadBufferOperation.h | 47 +- .../operations/COM_RenderLayersProg.cc | 117 +-- .../operations/COM_RenderLayersProg.h | 68 +- .../operations/COM_RotateOperation.cc | 107 +- .../operations/COM_RotateOperation.h | 30 +- .../operations/COM_SMAAOperation.cc | 274 +++--- .../compositor/operations/COM_SMAAOperation.h | 74 +- .../operations/COM_ScaleOperation.cc | 301 +++--- .../operations/COM_ScaleOperation.h | 76 +- .../COM_ScreenLensDistortionOperation.cc | 166 ++-- .../COM_ScreenLensDistortionOperation.h | 28 +- .../COM_SetAlphaMultiplyOperation.cc | 34 +- .../COM_SetAlphaMultiplyOperation.h | 10 +- .../COM_SetAlphaReplaceOperation.cc | 34 +- .../operations/COM_SetAlphaReplaceOperation.h | 10 +- .../operations/COM_SetColorOperation.cc | 10 +- .../operations/COM_SetColorOperation.h | 20 +- .../operations/COM_SetSamplerOperation.cc | 20 +- .../operations/COM_SetSamplerOperation.h | 8 +- .../operations/COM_SetValueOperation.cc | 10 +- .../operations/COM_SetValueOperation.h | 6 +- .../operations/COM_SetVectorOperation.cc | 10 +- .../operations/COM_SetVectorOperation.h | 4 +- .../operations/COM_SocketProxyOperation.cc | 8 +- .../operations/COM_SocketProxyOperation.h | 2 +- .../operations/COM_SplitOperation.cc | 42 +- .../operations/COM_SplitOperation.h | 18 +- .../operations/COM_SunBeamsOperation.cc | 28 +- .../operations/COM_SunBeamsOperation.h | 14 +- .../operations/COM_TextureOperation.cc | 78 +- .../operations/COM_TextureOperation.h | 22 +- .../operations/COM_TonemapOperation.cc | 78 +- .../operations/COM_TonemapOperation.h | 24 +- .../operations/COM_TrackPositionOperation.cc | 62 +- .../operations/COM_TrackPositionOperation.h | 40 +- .../operations/COM_TransformOperation.cc | 22 +- .../operations/COM_TranslateOperation.cc | 86 +- .../operations/COM_TranslateOperation.h | 52 +- .../COM_VariableSizeBokehBlurOperation.cc | 337 +++---- .../COM_VariableSizeBokehBlurOperation.h | 70 +- .../operations/COM_VectorBlurOperation.cc | 128 +-- .../operations/COM_VectorBlurOperation.h | 34 +- .../operations/COM_VectorCurveOperation.cc | 32 +- .../operations/COM_VectorCurveOperation.h | 10 +- .../operations/COM_ViewerOperation.cc | 142 +-- .../operations/COM_ViewerOperation.h | 88 +- .../operations/COM_WrapOperation.cc | 81 +- .../compositor/operations/COM_WrapOperation.h | 16 +- .../operations/COM_WriteBufferOperation.cc | 142 +-- .../operations/COM_WriteBufferOperation.h | 30 +- .../operations/COM_ZCombineOperation.cc | 108 +-- .../operations/COM_ZCombineOperation.h | 18 +- .../tests/COM_NodeOperation_test.cc | 22 +- 456 files changed, 10870 insertions(+), 10727 deletions(-) diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h index a7f9081f3fc..73e66ffeb03 100644 --- a/source/blender/compositor/COM_compositor.h +++ b/source/blender/compositor/COM_compositor.h @@ -74,8 +74,8 @@ extern "C" { * * during the preparation of the execution All ReadBufferOperation will receive an offset. * This offset is used during execution as an optimization trick - * Next all operations will be initialized for execution \see NodeOperation.initExecution - * Next all ExecutionGroup's will be initialized for execution \see ExecutionGroup.initExecution + * Next all operations will be initialized for execution \see NodeOperation.init_execution + * Next all ExecutionGroup's will be initialized for execution \see ExecutionGroup.init_execution * this all is controlled from \see ExecutionSystem.execute * * \section priority Render priority @@ -92,7 +92,7 @@ extern "C" { * When match the ExecutionGroup will be executed (this happens in serial) * * \see ExecutionSystem.execute control of the Render priority - * \see NodeOperation.getRenderPriority receive the render priority + * \see NodeOperation.get_render_priority receive the render priority * \see ExecutionGroup.execute the main loop to execute a whole ExecutionGroup * * \section order Chunk order @@ -121,7 +121,7 @@ extern "C" { * Chunk is finished. * * \see ExecutionGroup.execute - * \see ViewerOperation.getChunkOrder + * \see ViewerOperation.get_chunk_order * \see ChunkOrdering * * \section interest Area of interest @@ -152,13 +152,13 @@ extern "C" { * * In the above example ExecutionGroup B has an outputoperation (ViewerOperation) * and is being executed. - * The first chunk is evaluated [@ref ExecutionGroup.scheduleChunkWhenPossible], + * The first chunk is evaluated [@ref ExecutionGroup.schedule_chunk_when_possible], * but not all input chunks are available. * The relevant ExecutionGroup (that can calculate the missing chunks; ExecutionGroup A) * is asked to calculate the area ExecutionGroup B is missing. - * [@ref ExecutionGroup.scheduleAreaWhenPossible] + * [@ref ExecutionGroup.schedule_area_when_possible] * ExecutionGroup B checks what chunks the area spans, and tries to schedule these chunks. - * If all input data is available these chunks are scheduled [@ref ExecutionGroup.scheduleChunk] + * If all input data is available these chunks are scheduled [@ref ExecutionGroup.schedule_chunk] * *
  *
@@ -171,18 +171,18 @@ extern "C" {
  *            O------------------------------->O                                            |
  *            .                                O                                            |
  *            .                                O-------\                                    |
- *            .                                .       | ExecutionGroup.scheduleChunkWhenPossible
+ *            .                                .       | ExecutionGroup.schedule_chunk_when_possible
  *            .                                .  O----/ (*)                                |
  *            .                                .  O                                         |
  *            .                                .  O                                         |
- *            .                                .  O  ExecutionGroup.scheduleAreaWhenPossible|
+ *            .                                .  O  ExecutionGroup.schedule_area_when_possible|
  *            .                                .  O---------------------------------------->O
- *            .                                .  .                                         O----------\ ExecutionGroup.scheduleChunkWhenPossible
+ *            .                                .  .                                         O----------\ ExecutionGroup.schedule_chunk_when_possible
  *            .                                .  .                                         .          | (*)
  *            .                                .  .                                         .  O-------/
  *            .                                .  .                                         .  O
  *            .                                .  .                                         .  O
- *            .                                .  .                                         .  O-------\ ExecutionGroup.scheduleChunk
+ *            .                                .  .                                         .  O-------\ ExecutionGroup.schedule_chunk
  *            .                                .  .                                         .  .       |
  *            .                                .  .                                         .  .  O----/
  *            .                                .  .                                         .  O<=O
@@ -198,7 +198,7 @@ extern "C" {
  * This happens until all chunks of (ExecutionGroup B) are finished executing or the user break's the process.
  *
  * NodeOperation like the ScaleOperation can influence the area of interest by reimplementing the
- * [@ref NodeOperation.determineAreaOfInterest] method
+ * [@ref NodeOperation.determine_area_of_interest] method
  *
  * 
  *
@@ -221,13 +221,13 @@ extern "C" {
  *
  * \see ExecutionGroup.execute Execute a complete ExecutionGroup.
  * Halts until finished or breaked by user
- * \see ExecutionGroup.scheduleChunkWhenPossible Tries to schedule a single chunk,
+ * \see ExecutionGroup.schedule_chunk_when_possible Tries to schedule a single chunk,
  * checks if all input data is available. Can trigger dependent chunks to be calculated
- * \see ExecutionGroup.scheduleAreaWhenPossible
+ * \see ExecutionGroup.schedule_area_when_possible
  * Tries to schedule an area. This can be multiple chunks
- * (is called from [@ref ExecutionGroup.scheduleChunkWhenPossible])
- * \see ExecutionGroup.scheduleChunk Schedule a chunk on the WorkScheduler
- * \see NodeOperation.determineDependingAreaOfInterest Influence the area of interest of a chunk.
+ * (is called from [@ref ExecutionGroup.schedule_chunk_when_possible])
+ * \see ExecutionGroup.schedule_chunk Schedule a chunk on the WorkScheduler
+ * \see NodeOperation.determine_depending_area_of_interest Influence the area of interest of a chunk.
  * \see WriteBufferOperation Operation to write to a MemoryProxy/MemoryBuffer
  * \see ReadBufferOperation Operation to read from a MemoryProxy/MemoryBuffer
  * \see MemoryProxy proxy for information about memory image
@@ -283,16 +283,16 @@ extern "C" {
  * The OutputOperation of the ExecutionGroup is called to execute the area of the outputbuffer.
  *
  * \see ExecutionGroup
- * \see NodeOperation.executeRegion executes a single chunk of a NodeOperation
+ * \see NodeOperation.execute_region executes a single chunk of a NodeOperation
  * \see CPUDevice.execute
  *
  * \subsection GPUDevice OpenCLDevice
  *
  * To be completed!
- * \see NodeOperation.executeOpenCLRegion
+ * \see NodeOperation.execute_opencl_region
  * \see OpenCLDevice.execute
  *
- * \section executePixel executing a pixel
+ * \section execute_pixel executing a pixel
  * Finally the last step, the node functionality :)
  */
 
@@ -312,10 +312,10 @@ extern "C" {
  *    (true) or editing (false).
  *    based on this setting the system will work differently:
  *     - during rendering only Composite & the File output node will be calculated
- * \see NodeOperation.isOutputProgram(int rendering) of the specific operations
+ * \see NodeOperation.is_output_program(int rendering) of the specific operations
  *
  *     - during editing all output nodes will be calculated
- * \see NodeOperation.isOutputProgram(int rendering) of the specific operations
+ * \see NodeOperation.is_output_program(int rendering) of the specific operations
  *
  *     - another quality setting can be used bNodeTree.
  *       The quality is determined by the bNodeTree fields.
@@ -326,10 +326,10 @@ extern "C" {
  *     - output nodes can have different priorities in the WorkScheduler.
  * This is implemented in the COM_execute function.
  *
- * \param viewSettings:
+ * \param view_settings:
  *   reference to view settings used for color management
  *
- * \param displaySettings:
+ * \param display_settings:
  *   reference to display settings used for color management
  *
  * OCIO_TODO: this options only used in rare cases, namely in output file node,
@@ -343,13 +343,13 @@ void COM_execute(RenderData *render_data,
                  Scene *scene,
                  bNodeTree *node_tree,
                  int rendering,
-                 const ColorManagedViewSettings *viewSettings,
-                 const ColorManagedDisplaySettings *displaySettings,
-                 const char *viewName);
+                 const ColorManagedViewSettings *view_settings,
+                 const ColorManagedDisplaySettings *display_settings,
+                 const char *view_name);
 
 /**
  * \brief Deinitialize the compositor caches and allocated memory.
- * Use COM_clearCaches to only free the caches.
+ * Use COM_clear_caches to only free the caches.
  */
 void COM_deinitialize(void);
 
@@ -357,7 +357,7 @@ void COM_deinitialize(void);
  * \brief Clear all compositor caches. (Compositor system will still remain available).
  * To deinitialize the compositor use the COM_deinitialize method.
  */
-// void COM_clearCaches(void); // NOT YET WRITTEN
+// void COM_clear_caches(void); // NOT YET WRITTEN
 
 #ifdef __cplusplus
 }
diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc
index c6530cf6bd1..21238733925 100644
--- a/source/blender/compositor/intern/COM_BufferOperation.cc
+++ b/source/blender/compositor/intern/COM_BufferOperation.cc
@@ -25,7 +25,7 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
   buffer_ = buffer;
   inflated_buffer_ = nullptr;
   set_canvas(buffer->get_rect());
-  addOutputSocket(data_type);
+  add_output_socket(data_type);
   flags.is_constant_operation = buffer_->is_a_single_elem();
   flags.is_fullframe_operation = false;
 }
@@ -33,39 +33,42 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
 const float *BufferOperation::get_constant_elem()
 {
   BLI_assert(buffer_->is_a_single_elem());
-  return buffer_->getBuffer();
+  return buffer_->get_buffer();
 }
 
-void BufferOperation::initExecution()
+void BufferOperation::init_execution()
 {
   if (buffer_->is_a_single_elem()) {
-    initMutex();
+    init_mutex();
   }
 }
 
-void *BufferOperation::initializeTileData(rcti * /*rect*/)
+void *BufferOperation::initialize_tile_data(rcti * /*rect*/)
 {
   if (buffer_->is_a_single_elem() == false) {
     return buffer_;
   }
 
-  lockMutex();
+  lock_mutex();
   if (!inflated_buffer_) {
     inflated_buffer_ = buffer_->inflate();
   }
-  unlockMutex();
+  unlock_mutex();
   return inflated_buffer_;
 }
 
-void BufferOperation::deinitExecution()
+void BufferOperation::deinit_execution()
 {
   if (buffer_->is_a_single_elem()) {
-    deinitMutex();
+    deinit_mutex();
   }
   delete inflated_buffer_;
 }
 
-void BufferOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
+void BufferOperation::execute_pixel_sampled(float output[4],
+                                            float x,
+                                            float y,
+                                            PixelSampler sampler)
 {
   switch (sampler) {
     case PixelSampler::Nearest:
@@ -73,16 +76,16 @@ void BufferOperation::executePixelSampled(float output[4], float x, float y, Pix
       break;
     case PixelSampler::Bilinear:
     default:
-      buffer_->readBilinear(output, x, y);
+      buffer_->read_bilinear(output, x, y);
       break;
     case PixelSampler::Bicubic:
       /* No bicubic. Same implementation as ReadBufferOperation. */
-      buffer_->readBilinear(output, x, y);
+      buffer_->read_bilinear(output, x, y);
       break;
   }
 }
 
-void BufferOperation::executePixelFiltered(
+void BufferOperation::execute_pixel_filtered(
     float output[4], float x, float y, float dx[2], float dy[2])
 {
   const float uv[2] = {x, y};
diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h
index b4cbc0a56b6..4aba3a705dd 100644
--- a/source/blender/compositor/intern/COM_BufferOperation.h
+++ b/source/blender/compositor/intern/COM_BufferOperation.h
@@ -31,11 +31,12 @@ class BufferOperation : public ConstantOperation {
   BufferOperation(MemoryBuffer *buffer, DataType data_type);
 
   const float *get_constant_elem() override;
-  void *initializeTileData(rcti *rect) override;
-  void initExecution() override;
-  void deinitExecution() override;
-  void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
-  void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override;
+  void *initialize_tile_data(rcti *rect) override;
+  void init_execution() override;
+  void deinit_execution() override;
+  void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override;
+  void execute_pixel_filtered(
+      float output[4], float x, float y, float dx[2], float dy[2]) override;
 };
 
 }  // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc
index 89aea47f4a6..ad7a807b8fd 100644
--- a/source/blender/compositor/intern/COM_CPUDevice.cc
+++ b/source/blender/compositor/intern/COM_CPUDevice.cc
@@ -31,11 +31,11 @@ void CPUDevice::execute(WorkPackage *work_package)
 {
   switch (work_package->type) {
     case eWorkPackageType::Tile: {
-      const unsigned int chunkNumber = work_package->chunk_number;
-      ExecutionGroup *executionGroup = work_package->execution_group;
+      const unsigned int chunk_number = work_package->chunk_number;
+      ExecutionGroup *execution_group = work_package->execution_group;
 
-      executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber);
-      executionGroup->finalizeChunkExecution(chunkNumber, nullptr);
+      execution_group->get_output_operation()->execute_region(&work_package->rect, chunk_number);
+      execution_group->finalize_chunk_execution(chunk_number, nullptr);
       break;
     }
     case eWorkPackageType::CustomFunction: {
diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc
index 5e2e5ea295b..7b0f7b0f8fb 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.cc
+++ b/source/blender/compositor/intern/COM_CompositorContext.cc
@@ -26,13 +26,13 @@ CompositorContext::CompositorContext()
   rd_ = nullptr;
   quality_ = eCompositorQuality::High;
   hasActiveOpenCLDevices_ = false;
-  fastCalculation_ = false;
-  viewSettings_ = nullptr;
-  displaySettings_ = nullptr;
+  fast_calculation_ = false;
+  view_settings_ = nullptr;
+  display_settings_ = nullptr;
   bnodetree_ = nullptr;
 }
 
-int CompositorContext::getFramenumber() const
+int CompositorContext::get_framenumber() const
 {
   BLI_assert(rd_);
   return rd_->cfra;
@@ -40,8 +40,8 @@ int CompositorContext::getFramenumber() const
 
 Size2f CompositorContext::get_render_size() const
 {
-  return {getRenderData()->xsch * getRenderPercentageAsFactor(),
-          getRenderData()->ysch * getRenderPercentageAsFactor()};
+  return {get_render_data()->xsch * get_render_percentage_as_factor(),
+          get_render_data()->ysch * get_render_percentage_as_factor()};
 }
 
 eExecutionModel CompositorContext::get_execution_model() const
diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h
index 9354551d66a..e41576b6f69 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -77,16 +77,16 @@ class CompositorContext {
   /**
    * \brief Skip slow nodes
    */
-  bool fastCalculation_;
+  bool fast_calculation_;
 
   /* \brief color management settings */
-  const ColorManagedViewSettings *viewSettings_;
-  const ColorManagedDisplaySettings *displaySettings_;
+  const ColorManagedViewSettings *view_settings_;
+  const ColorManagedDisplaySettings *display_settings_;
 
   /**
    * \brief active rendering view name
    */
-  const char *viewName_;
+  const char *view_name_;
 
  public:
   /**
@@ -97,7 +97,7 @@ class CompositorContext {
   /**
    * \brief set the rendering field of the context
    */
-  void setRendering(bool rendering)
+  void set_rendering(bool rendering)
   {
     rendering_ = rendering;
   }
@@ -105,7 +105,7 @@ class CompositorContext {
   /**
    * \brief get the rendering field of the context
    */
-  bool isRendering() const
+  bool is_rendering() const
   {
     return rendering_;
   }
@@ -113,7 +113,7 @@ class CompositorContext {
   /**
    * \brief set the scene of the context
    */
-  void setRenderData(RenderData *rd)
+  void set_render_data(RenderData *rd)
   {
     rd_ = rd;
   }
@@ -121,7 +121,7 @@ class CompositorContext {
   /**
    * \brief set the bnodetree of the context
    */
-  void setbNodeTree(bNodeTree *bnodetree)
+  void set_bnodetree(bNodeTree *bnodetree)
   {
     bnodetree_ = bnodetree;
   }
@@ -129,7 +129,7 @@ class CompositorContext {
   /**
    * \brief get the bnodetree of the context
    */
-  const bNodeTree *getbNodeTree() const
+  const bNodeTree *get_bnodetree() const
   {
     return bnodetree_;
   }
@@ -137,16 +137,16 @@ class CompositorContext {
   /**
    * \brief get the scene of the context
    */
-  const RenderData *getRenderData() const
+  const RenderData *get_render_data() const
   {
     return rd_;
   }
 
-  void setScene(Scene *scene)
+  void set_scene(Scene *scene)
   {
     scene_ = scene;
   }
-  Scene *getScene() const
+  Scene *get_scene() const
   {
     return scene_;
   }
@@ -154,7 +154,7 @@ class CompositorContext {
   /**
    * \brief set the preview image hash table
    */
-  void setPreviewHash(bNodeInstanceHash *previews)
+  void set_preview_hash(bNodeInstanceHash *previews)
   {
     previews_ = previews;
   }
@@ -162,7 +162,7 @@ class CompositorContext {
   /**
    * \brief get the preview image hash table
    */
-  bNodeInstanceHash *getPreviewHash() const
+  bNodeInstanceHash *get_preview_hash() const
   {
     return previews_;
   }
@@ -170,39 +170,39 @@ class CompositorContext {
   /**
    * \brief set view settings of color management
    */
-  void setViewSettings(const ColorManagedViewSettings *viewSettings)
+  void set_view_settings(const ColorManagedViewSettings *view_settings)
   {
-    viewSettings_ = viewSettings;
+    view_settings_ = view_settings;
   }
 
   /**
    * \brief get view settings of color management
    */
-  const ColorManagedViewSettings *getViewSettings() const
+  const ColorManagedViewSettings *get_view_settings() const
   {
-    return viewSettings_;
+    return view_settings_;
   }
 
   /**
    * \brief set display settings of color management
    */
-  void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings)
+  void set_display_settings(const ColorManagedDisplaySettings *display_settings)
   {
-    displaySettings_ = displaySettings;
+    display_settings_ = display_settings;
   }
 
   /**
    * \brief get display settings of color management
    */
-  const ColorManagedDisplaySettings *getDisplaySettings() const
+  const ColorManagedDisplaySettings *get_display_settings() const
   {
-    return displaySettings_;
+    return display_settings_;
   }
 
   /**
    * \brief set the quality
    */
-  void setQuality(eCompositorQuality quality)
+  void set_quality(eCompositorQuality quality)
   {
     quality_ = quality;
   }
@@ -210,7 +210,7 @@ class CompositorContext {
   /**
    * \brief get the quality
    */
-  eCompositorQuality getQuality() const
+  eCompositorQuality get_quality() const
   {
     return quality_;
   }
@@ -218,18 +218,18 @@ class CompositorContext {
   /**
    * \brief get the current frame-number of the scene in this context
    */
-  int getFramenumber() const;
+  int get_framenumber() const;
 
   /**
-   * \brief has this system active openclDevices?
+   * \brief has this system active opencl_devices?
    */
-  bool getHasActiveOpenCLDevices() const
+  bool get_has_active_opencl_devices() const
   {
     return hasActiveOpenCLDevices_;
   }
 
   /**
-   * \brief set has this system active openclDevices?
+   * \brief set has this system active opencl_devices?
    */
   void setHasActiveOpenCLDevices(bool hasAvtiveOpenCLDevices)
   {
@@ -239,48 +239,48 @@ class CompositorContext {
   /** Whether it has a view with a specific name and not the default one. */
   bool has_explicit_view() const
   {
-    return viewName_ && viewName_[0] != '\0';
+    return view_name_ && view_name_[0] != '\0';
   }
 
   /**
    * \brief get the active rendering view
    */
-  const char *getViewName() const
+  const char *get_view_name() const
   {
-    return viewName_;
+    return view_name_;
   }
 
   /**
    * \brief set the active rendering view
    */
-  void setViewName(const char *viewName)
+  void set_view_name(const char *view_name)
   {
-    viewName_ = viewName;
+    view_name_ = view_name;
   }
 
-  int getChunksize() const
+  int get_chunksize() const
   {
-    return this->getbNodeTree()->chunksize;
+    return this->get_bnodetree()->chunksize;
   }
 
-  void setFastCalculation(bool fastCalculation)
+  void set_fast_calculation(bool fast_calculation)
   {
-    fastCalculation_ = fastCalculation;
+    fast_calculation_ = fast_calculation;
   }
-  bool isFastCalculation() const
+  bool is_fast_calculation() const
   {
-    return fastCalculation_;
+    return fast_calculation_;
   }
-  bool isGroupnodeBufferEnabled() const
+  bool is_groupnode_buffer_enabled() const
   {
-    return (this->getbNodeTree()->flag & NTREE_COM_GROUPNODE_BUFFER) != 0;
+    return (this->get_bnodetree()->flag & NTREE_COM_GROUPNODE_BUFFER) != 0;
   }
 
   /**
    * \brief Get the render percentage as a factor.
    * The compositor uses a factor i.o. a percentage.
    */
-  float getRenderPercentageAsFactor() const
+  float get_render_percentage_as_factor() const
   {
     return rd_->size * 0.01f;
   }
diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc
index d3031765eae..2cf3ac001d1 100644
--- a/source/blender/compositor/intern/COM_ConstantFolder.cc
+++ b/source/blender/compositor/intern/COM_ConstantFolder.cc
@@ -44,7 +44,7 @@ ConstantFolder::ConstantFolder(NodeOperationBuilder &operations_builder)
 static bool is_constant_foldable(NodeOperation *operation)
 {
   if (operation->get_flags().can_be_constant && !operation->get_flags().is_constant_operation) {
-    for (int i = 0; i < operation->getNumberOfInputSockets(); i++) {
+    for (int i = 0; i < operation->get_number_of_input_sockets(); i++) {
       NodeOperation *input = operation->get_input_operation(i);
       if (!input->get_flags().is_constant_operation ||
           !static_cast(input)->can_get_constant_elem()) {
@@ -72,17 +72,17 @@ static ConstantOperation *create_constant_operation(DataType data_type, const fl
   switch (data_type) {
     case DataType::Color: {
       SetColorOperation *color_op = new SetColorOperation();
-      color_op->setChannels(constant_elem);
+      color_op->set_channels(constant_elem);
       return color_op;
     }
     case DataType::Vector: {
       SetVectorOperation *vector_op = new SetVectorOperation();
-      vector_op->setVector(constant_elem);
+      vector_op->set_vector(constant_elem);
       return vector_op;
     }
     case DataType::Value: {
       SetValueOperation *value_op = new SetValueOperation();
-      value_op->setValue(*constant_elem);
+      value_op->set_value(*constant_elem);
       return value_op;
     }
     default: {
@@ -94,7 +94,7 @@ static ConstantOperation *create_constant_operation(DataType data_type, const fl
 
 ConstantOperation *ConstantFolder::fold_operation(NodeOperation *operation)
 {
-  const DataType data_type = operation->getOutputSocket()->getDataType();
+  const DataType data_type = operation->get_output_socket()->get_data_type();
   MemoryBuffer fold_buf(data_type, first_elem_area_);
   Vector input_bufs = get_constant_input_buffers(operation);
   operation->init_data();
@@ -102,7 +102,8 @@ ConstantOperation *ConstantFolder::fold_operation(NodeOperation *operation)
 
   MemoryBuffer *constant_buf = create_constant_buffer(data_type);
   constant_buf->copy_from(&fold_buf, first_elem_area_);
-  ConstantOperation *constant_op = create_constant_operation(data_type, constant_buf->getBuffer());
+  ConstantOperation *constant_op = create_constant_operation(data_type,
+                                                             constant_buf->get_buffer());
   operations_builder_.replace_operation_with_constant(operation, constant_op);
   constant_buffers_.add_new(constant_op, constant_buf);
   return constant_op;
@@ -117,14 +118,15 @@ MemoryBuffer *ConstantFolder::create_constant_buffer(const DataType data_type)
 
 Vector ConstantFolder::get_constant_input_buffers(NodeOperation *operation)
 {
-  const int num_inputs = operation->getNumberOfInputSockets();
+  const int num_inputs = operation->get_number_of_input_sockets();
   Vector inputs_bufs(num_inputs);
   for (int i = 0; i < num_inputs; i++) {
     BLI_assert(operation->get_input_operation(i)->get_flags().is_constant_operation);
     ConstantOperation *constant_op = static_cast(
         operation->get_input_operation(i));
     MemoryBuffer *constant_buf = constant_buffers_.lookup_or_add_cb(constant_op, [=] {
-      MemoryBuffer *buf = create_constant_buffer(constant_op->getOutputSocket()->getDataType());
+      MemoryBuffer *buf = create_constant_buffer(
+          constant_op->get_output_socket()->get_data_type());
       constant_op->render(buf, {first_elem_area_}, {});
       return buf;
     });
@@ -186,8 +188,8 @@ void ConstantFolder::get_operation_output_operations(NodeOperation *operation,
 {
   const Vector &links = operations_builder_.get_links();
   for (const Link &link : links) {
-    if (&link.from()->getOperation() == operation) {
-      r_outputs.append(&link.to()->getOperation());
+    if (&link.from()->get_operation() == operation) {
+      r_outputs.append(&link.to()->get_operation());
     }
   }
 }
diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc
index 346c3c4b600..0af4ff7d98d 100644
--- a/source/blender/compositor/intern/COM_Converter.cc
+++ b/source/blender/compositor/intern/COM_Converter.cc
@@ -433,8 +433,8 @@ Node *COM_convert_bnode(bNode *b_node)
 /* TODO(jbakker): make this an std::optional. */
 NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, const NodeOperationInput &to)
 {
-  const DataType src_data_type = from.getDataType();
-  const DataType dst_data_type = to.getDataType();
+  const DataType src_data_type = from.get_data_type();
+  const DataType dst_data_type = to.get_data_type();
 
   if (src_data_type == DataType::Value && dst_data_type == DataType::Color) {
     return new ConvertValueToColorOperation();
@@ -459,24 +459,24 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, const Node
 }
 
 void COM_convert_canvas(NodeOperationBuilder &builder,
-                        NodeOperationOutput *fromSocket,
-                        NodeOperationInput *toSocket)
+                        NodeOperationOutput *from_socket,
+                        NodeOperationInput *to_socket)
 {
   /* Data type conversions are executed before resolutions to ensure convert operations have
    * resolution. This method have to ensure same datatypes are linked for new operations. */
-  BLI_assert(fromSocket->getDataType() == toSocket->getDataType());
+  BLI_assert(from_socket->get_data_type() == to_socket->get_data_type());
 
-  ResizeMode mode = toSocket->getResizeMode();
+  ResizeMode mode = to_socket->get_resize_mode();
   BLI_assert(mode != ResizeMode::None);
 
-  NodeOperation *toOperation = &toSocket->getOperation();
-  const float toWidth = toOperation->getWidth();
-  const float toHeight = toOperation->getHeight();
-  NodeOperation *fromOperation = &fromSocket->getOperation();
-  const float fromWidth = fromOperation->getWidth();
-  const float fromHeight = fromOperation->getHeight();
-  bool doCenter = false;
-  bool doScale = false;
+  NodeOperation *to_operation = &to_socket->get_operation();
+  const float to_width = to_operation->get_width();
+  const float to_height = to_operation->get_height();
+  NodeOperation *from_operation = &from_socket->get_operation();
+  const float from_width = from_operation->get_width();
+  const float from_height = from_operation->get_height();
+  bool do_center = false;
+  bool do_scale = false;
   float scaleX = 0;
   float scaleY = 0;
 
@@ -485,23 +485,23 @@ void COM_convert_canvas(NodeOperationBuilder &builder,
     case ResizeMode::Align:
       break;
     case ResizeMode::Center:
-      doCenter = true;
+      do_center = true;
       break;
     case ResizeMode::FitWidth:
-      doCenter = true;
-      doScale = true;
-      scaleX = scaleY = toWidth / fromWidth;
+      do_center = true;
+      do_scale = true;
+      scaleX = scaleY = to_width / from_width;
       break;
     case ResizeMode::FitHeight:
-      doCenter = true;
-      doScale = true;
-      scaleX = scaleY = toHeight / fromHeight;
+      do_center = true;
+      do_scale = true;
+      scaleX = scaleY = to_height / from_height;
       break;
     case ResizeMode::FitAny:
-      doCenter = true;
-      doScale = true;
-      scaleX = toWidth / fromWidth;
-      scaleY = toHeight / fromHeight;
+      do_center = true;
+      do_scale = true;
+      scaleX = to_width / from_width;
+      scaleY = to_height / from_height;
       if (scaleX < scaleY) {
         scaleX = scaleY;
       }
@@ -510,81 +510,82 @@ void COM_convert_canvas(NodeOperationBuilder &builder,
       }
       break;
     case ResizeMode::Stretch:
-      doCenter = true;
-      doScale = true;
-      scaleX = toWidth / fromWidth;
-      scaleY = toHeight / fromHeight;
+      do_center = true;
+      do_scale = true;
+      scaleX = to_width / from_width;
+      scaleY = to_height / from_height;
       break;
   }
 
-  float addX = doCenter ? (toWidth - fromWidth) / 2.0f : 0.0f;
-  float addY = doCenter ? (toHeight - fromHeight) / 2.0f : 0.0f;
+  float addX = do_center ? (to_width - from_width) / 2.0f : 0.0f;
+  float addY = do_center ? (to_height - from_height) / 2.0f : 0.0f;
   NodeOperation *first = nullptr;
-  ScaleOperation *scaleOperation = nullptr;
-  if (doScale) {
-    scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
-    scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
-    scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
-    first = scaleOperation;
+  ScaleOperation *scale_operation = nullptr;
+  if (do_scale) {
+    scale_operation = new ScaleRelativeOperation(from_socket->get_data_type());
+    scale_operation->get_input_socket(1)->set_resize_mode(ResizeMode::None);
+    scale_operation->get_input_socket(2)->set_resize_mode(ResizeMode::None);
+    first = scale_operation;
     SetValueOperation *sxop = new SetValueOperation();
-    sxop->setValue(scaleX);
-    builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1));
+    sxop->set_value(scaleX);
+    builder.add_link(sxop->get_output_socket(), scale_operation->get_input_socket(1));
     SetValueOperation *syop = new SetValueOperation();
-    syop->setValue(scaleY);
-    builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2));
-    builder.addOperation(sxop);
-    builder.addOperation(syop);
+    syop->set_value(scaleY);
+    builder.add_link(syop->get_output_socket(), scale_operation->get_input_socket(2));
+    builder.add_operation(sxop);
+    builder.add_operation(syop);
 
-    rcti scale_canvas = fromOperation->get_canvas();
+    rcti scale_canvas = from_operation->get_canvas();
     if (builder.context().get_execution_model() == eExecutionModel::FullFrame) {
       ScaleOperation::scale_area(scale_canvas, scaleX, scaleY);
-      scale_canvas.xmax = scale_canvas.xmin + toOperation->getWidth();
-      scale_canvas.ymax = scale_canvas.ymin + toOperation->getHeight();
+      scale_canvas.xmax = scale_canvas.xmin + to_operation->get_width();
+      scale_canvas.ymax = scale_canvas.ymin + to_operation->get_height();
       addX = 0;
       addY = 0;
     }
-    scaleOperation->set_canvas(scale_canvas);
+    scale_operation->set_canvas(scale_canvas);
     sxop->set_canvas(scale_canvas);
     syop->set_canvas(scale_canvas);
-    builder.addOperation(scaleOperation);
+    builder.add_operation(scale_operation);
   }
 
-  TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType());
-  translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
-  translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
+  TranslateOperation *translate_operation = new TranslateOperation(to_socket->get_data_type());
+  translate_operation->get_input_socket(1)->set_resize_mode(ResizeMode::None);
+  translate_operation->get_input_socket(2)->set_resize_mode(ResizeMode::None);
   if (!first) {
-    first = translateOperation;
+    first = translate_operation;
   }
   SetValueOperation *xop = new SetValueOperation();
-  xop->setValue(addX);
-  builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1));
+  xop->set_value(addX);
+  builder.add_link(xop->get_output_socket(), translate_operation->get_input_socket(1));
   SetValueOperation *yop = new SetValueOperation();
-  yop->setValue(addY);
-  builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2));
-  builder.addOperation(xop);
-  builder.addOperation(yop);
+  yop->set_value(addY);
+  builder.add_link(yop->get_output_socket(), translate_operation->get_input_socket(2));
+  builder.add_operation(xop);
+  builder.add_operation(yop);
 
-  rcti translate_canvas = toOperation->get_canvas();
+  rcti translate_canvas = to_operation->get_canvas();
   if (mode == ResizeMode::Align) {
-    translate_canvas.xmax = translate_canvas.xmin + fromWidth;
-    translate_canvas.ymax = translate_canvas.ymin + fromHeight;
+    translate_canvas.xmax = translate_canvas.xmin + from_width;
+    translate_canvas.ymax = translate_canvas.ymin + from_height;
   }
-  translateOperation->set_canvas(translate_canvas);
+  translate_operation->set_canvas(translate_canvas);
   xop->set_canvas(translate_canvas);
   yop->set_canvas(translate_canvas);
-  builder.addOperation(translateOperation);
+  builder.add_operation(translate_operation);
 
-  if (doScale) {
-    translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
-    builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
+  if (do_scale) {
+    translate_operation->get_input_socket(0)->set_resize_mode(ResizeMode::None);
+    builder.add_link(scale_operation->get_output_socket(),
+                     translate_operation->get_input_socket(0));
   }
 
   /* remove previous link and replace */
-  builder.removeInputLink(toSocket);
-  first->getInputSocket(0)->setResizeMode(ResizeMode::None);
-  toSocket->setResizeMode(ResizeMode::None);
-  builder.addLink(fromSocket, first->getInputSocket(0));
-  builder.addLink(translateOperation->getOutputSocket(), toSocket);
+  builder.remove_input_link(to_socket);
+  first->get_input_socket(0)->set_resize_mode(ResizeMode::None);
+  to_socket->set_resize_mode(ResizeMode::None);
+  builder.add_link(from_socket, first->get_input_socket(0));
+  builder.add_link(translate_operation->get_output_socket(), to_socket);
 }
 
 }  // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Converter.h b/source/blender/compositor/intern/COM_Converter.h
index 9cb6b1862b2..39bd44e8a9f 100644
--- a/source/blender/compositor/intern/COM_Converter.h
+++ b/source/blender/compositor/intern/COM_Converter.h
@@ -65,7 +65,7 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from,
  * \see InputSocketResizeMode for the possible conversions.
  */
 void COM_convert_canvas(NodeOperationBuilder &builder,
-                        NodeOperationOutput *fromSocket,
-                        NodeOperationInput *toSocket);
+                        NodeOperationOutput *from_socket,
+                        NodeOperationInput *to_socket);
 
 }  // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index 023db368ac9..50a69e55b2b 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -80,14 +80,14 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
   std::string fillcolor = "gainsboro";
   if (operation->get_flags().is_viewer_operation) {
     const ViewerOperation *viewer = (const ViewerOperation *)operation;
-    if (viewer->isActiveViewerOutput()) {
+    if (viewer->is_active_viewer_output()) {
       fillcolor = "lightskyblue1";
     }
     else {
       fillcolor = "lightskyblue3";
     }
   }
-  else if (operation->isOutputOperation(system->getContext().isRendering())) {
+  else if (operation->is_output_operation(system->get_context().is_rendering())) {
     fillcolor = "dodgerblue1";
   }
   else if (operation->get_flags().is_set_operation) {
@@ -112,16 +112,16 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
                   " [fillcolor=%s,style=filled,shape=record,label=\"{",
                   fillcolor.c_str());
 
-  int totinputs = operation->getNumberOfInputSockets();
+  int totinputs = operation->get_number_of_input_sockets();
   if (totinputs != 0) {
     len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{");
     for (int k = 0; k < totinputs; k++) {
-      NodeOperationInput *socket = operation->getInputSocket(k);
+      NodeOperationInput *socket = operation->get_input_socket(k);
       if (k != 0) {
         len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
       }
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "", socket);
-      switch (socket->getDataType()) {
+      switch (socket->get_data_type()) {
         case DataType::Value:
           len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value");
           break;
@@ -156,20 +156,20 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
                   operation->get_id(),
                   operation->get_canvas().xmin,
                   operation->get_canvas().ymin,
-                  operation->getWidth(),
-                  operation->getHeight());
+                  operation->get_width(),
+                  operation->get_height());
 
-  int totoutputs = operation->getNumberOfOutputSockets();
+  int totoutputs = operation->get_number_of_output_sockets();
   if (totoutputs != 0) {
     len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
     len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{");
     for (int k = 0; k < totoutputs; k++) {
-      NodeOperationOutput *socket = operation->getOutputSocket(k);
+      NodeOperationOutput *socket = operation->get_output_socket(k);
       if (k != 0) {
         len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
       }
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "", socket);
-      switch (socket->getDataType()) {
+      switch (socket->get_data_type()) {
         case DataType::Value: {
           ConstantOperation *constant = operation->get_flags().is_constant_operation ?
                                             static_cast(operation) :
@@ -346,7 +346,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
   for (NodeOperation *operation : system->operations_) {
     if (operation->get_flags().is_read_buffer_operation) {
       ReadBufferOperation *read = (ReadBufferOperation *)operation;
-      WriteBufferOperation *write = read->getMemoryProxy()->getWriteBufferOperation();
+      WriteBufferOperation *write = read->get_memory_proxy()->get_write_buffer_operation();
       std::vector &read_groups = op_groups[read];
       std::vector &write_groups = op_groups[write];
 
@@ -366,14 +366,14 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
 
   for (NodeOperation *op : system->operations_) {
     for (NodeOperationInput &to : op->inputs_) {
-      NodeOperationOutput *from = to.getLink();
+      NodeOperationOutput *from = to.get_link();
 
       if (!from) {
         continue;
       }
 
       std::string color;
-      switch (from->getDataType()) {
+      switch (from->get_data_type()) {
         case DataType::Value:
           color = "gray";
           break;
@@ -385,8 +385,8 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
           break;
       }
 
-      NodeOperation *to_op = &to.getOperation();
-      NodeOperation *from_op = &from->getOperation();
+      NodeOperation *to_op = &to.get_operation();
+      NodeOperation *from_op = &from->get_operation();
       std::vector &from_groups = op_groups[from_op];
       std::vector &to_groups = op_groups[to_op];
 
@@ -416,7 +416,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
     }
   }
 
-  const bool has_execution_groups = system->getContext().get_execution_model() ==
+  const bool has_execution_groups = system->get_context().get_execution_model() ==
                                         eExecutionModel::Tiled &&
                                     system->groups_.size() > 0;
   len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups);
@@ -460,8 +460,8 @@ static std::string get_operations_export_dir()
 
 void DebugInfo::export_operation(const NodeOperation *op, MemoryBuffer *render)
 {
-  const int width = render->getWidth();
-  const int height = render->getHeight();
+  const int width = render->get_width();
+  const int height = render->get_height();
   const int num_channels = render->get_num_channels();
 
   ImBuf *ibuf = IMB_allocImBuf(width, height, 8 * num_channels, IB_rectfloat);
diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h
index f1edd3ea15f..021d6e744fb 100644
--- a/source/blender/compositor/intern/COM_Debug.h
+++ b/source/blender/compositor/intern/COM_Debug.h
@@ -89,7 +89,7 @@ class DebugInfo {
   static void node_added(const Node *node)
   {
     if (COM_EXPORT_GRAPHVIZ) {
-      node_names_[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
+      node_names_[node] = std::string(node->get_bnode() ? node->get_bnode()->name : "");
     }
   }
 
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc
index a38a1cd0a6c..d2198fc14e8 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.cc
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc
@@ -64,22 +64,22 @@ ExecutionGroup::ExecutionGroup(int id)
   y_chunks_len_ = 0;
   chunks_len_ = 0;
   chunks_finished_ = 0;
-  BLI_rcti_init(&viewerBorder_, 0, 0, 0, 0);
-  executionStartTime_ = 0;
+  BLI_rcti_init(&viewer_border_, 0, 0, 0, 0);
+  execution_start_time_ = 0;
 }
 
 std::ostream &operator<<(std::ostream &os, const ExecutionGroup &execution_group)
 {
   os << "ExecutionGroup(id=" << execution_group.get_id();
   os << ",flags={" << execution_group.get_flags() << "}";
-  os << ",operation=" << *execution_group.getOutputOperation() << "";
+  os << ",operation=" << *execution_group.get_output_operation() << "";
   os << ")";
   return os;
 }
 
-eCompositorPriority ExecutionGroup::getRenderPriority()
+eCompositorPriority ExecutionGroup::get_render_priority()
 {
-  return this->getOutputOperation()->getRenderPriority();
+  return this->get_output_operation()->get_render_priority();
 }
 
 bool ExecutionGroup::can_contain(NodeOperation &operation)
@@ -111,7 +111,7 @@ bool ExecutionGroup::can_contain(NodeOperation &operation)
   return true;
 }
 
-bool ExecutionGroup::addOperation(NodeOperation *operation)
+bool ExecutionGroup::add_operation(NodeOperation *operation)
 {
   if (!can_contain(*operation)) {
     return false;
@@ -130,7 +130,7 @@ bool ExecutionGroup::addOperation(NodeOperation *operation)
   return true;
 }
 
-NodeOperation *ExecutionGroup::getOutputOperation() const
+NodeOperation *ExecutionGroup::get_output_operation() const
 {
   return this
       ->operations_[0]; /* the first operation of the group is always the output operation. */
@@ -146,7 +146,7 @@ void ExecutionGroup::init_work_packages()
       work_packages_[index].state = eWorkPackageState::NotScheduled;
       work_packages_[index].execution_group = this;
       work_packages_[index].chunk_number = index;
-      determineChunkRect(&work_packages_[index].rect, index);
+      determine_chunk_rect(&work_packages_[index].rect, index);
     }
   }
 }
@@ -156,23 +156,23 @@ void ExecutionGroup::init_read_buffer_operations()
   unsigned int max_offset = 0;
   for (NodeOperation *operation : operations_) {
     if (operation->get_flags().is_read_buffer_operation) {
-      ReadBufferOperation *readOperation = static_cast(operation);
-      read_operations_.append(readOperation);
-      max_offset = MAX2(max_offset, readOperation->getOffset());
+      ReadBufferOperation *read_operation = static_cast(operation);
+      read_operations_.append(read_operation);
+      max_offset = MAX2(max_offset, read_operation->get_offset());
     }
   }
   max_offset++;
   max_read_buffer_offset_ = max_offset;
 }
 
-void ExecutionGroup::initExecution()
+void ExecutionGroup::init_execution()
 {
   init_number_of_chunks();
   init_work_packages();
   init_read_buffer_operations();
 }
 
-void ExecutionGroup::deinitExecution()
+void ExecutionGroup::deinit_execution()
 {
   work_packages_.clear();
   chunks_len_ = 0;
@@ -182,13 +182,13 @@ void ExecutionGroup::deinitExecution()
   bTree_ = nullptr;
 }
 
-void ExecutionGroup::determineResolution(unsigned int resolution[2])
+void ExecutionGroup::determine_resolution(unsigned int resolution[2])
 {
-  NodeOperation *operation = this->getOutputOperation();
-  resolution[0] = operation->getWidth();
-  resolution[1] = operation->getHeight();
-  this->setResolution(resolution);
-  BLI_rcti_init(&viewerBorder_, 0, width_, 0, height_);
+  NodeOperation *operation = this->get_output_operation();
+  resolution[0] = operation->get_width();
+  resolution[1] = operation->get_height();
+  this->set_resolution(resolution);
+  BLI_rcti_init(&viewer_border_, 0, width_, 0, height_);
 }
 
 void ExecutionGroup::init_number_of_chunks()
@@ -199,11 +199,11 @@ void ExecutionGroup::init_number_of_chunks()
     chunks_len_ = 1;
   }
   else {
-    const float chunkSizef = chunkSize_;
-    const int border_width = BLI_rcti_size_x(&viewerBorder_);
-    const int border_height = BLI_rcti_size_y(&viewerBorder_);
-    x_chunks_len_ = ceil(border_width / chunkSizef);
-    y_chunks_len_ = ceil(border_height / chunkSizef);
+    const float chunk_sizef = chunk_size_;
+    const int border_width = BLI_rcti_size_x(&viewer_border_);
+    const int border_height = BLI_rcti_size_y(&viewer_border_);
+    x_chunks_len_ = ceil(border_width / chunk_sizef);
+    y_chunks_len_ = ceil(border_height / chunk_sizef);
     chunks_len_ = x_chunks_len_ * y_chunks_len_;
   }
 }
@@ -215,7 +215,7 @@ blender::Array ExecutionGroup::get_execution_order() const
     chunk_order[chunk_index] = chunk_index;
   }
 
-  NodeOperation *operation = this->getOutputOperation();
+  NodeOperation *operation = this->get_output_operation();
   float centerX = 0.5f;
   float centerY = 0.5f;
   ChunkOrdering order_type = ChunkOrdering::Default;
@@ -224,11 +224,11 @@ blender::Array ExecutionGroup::get_execution_order() const
     ViewerOperation *viewer = (ViewerOperation *)operation;
     centerX = viewer->getCenterX();
     centerY = viewer->getCenterY();
-    order_type = viewer->getChunkOrder();
+    order_type = viewer->get_chunk_order();
   }
 
-  const int border_width = BLI_rcti_size_x(&viewerBorder_);
-  const int border_height = BLI_rcti_size_y(&viewerBorder_);
+  const int border_width = BLI_rcti_size_x(&viewer_border_);
+  const int border_height = BLI_rcti_size_y(&viewer_border_);
   int index;
   switch (order_type) {
     case ChunkOrdering::Random: {
@@ -245,8 +245,8 @@ blender::Array ExecutionGroup::get_execution_order() const
       for (index = 0; index < chunks_len_; index++) {
         const WorkPackage &work_package = work_packages_[index];
         chunk_orders[index].index = index;
-        chunk_orders[index].x = work_package.rect.xmin - viewerBorder_.xmin;
-        chunk_orders[index].y = work_package.rect.ymin - viewerBorder_.ymin;
+        chunk_orders[index].x = work_package.rect.xmin - viewer_border_.xmin;
+        chunk_orders[index].y = work_package.rect.ymin - viewer_border_.ymin;
         chunk_orders[index].update_distance(&hotspot, 1);
       }
 
@@ -282,8 +282,8 @@ blender::Array ExecutionGroup::get_execution_order() const
       for (index = 0; index < chunks_len_; index++) {
         const WorkPackage &work_package = work_packages_[index];
         chunk_orders[index].index = index;
-        chunk_orders[index].x = work_package.rect.xmin - viewerBorder_.xmin;
-        chunk_orders[index].y = work_package.rect.ymin - viewerBorder_.ymin;
+        chunk_orders[index].x = work_package.rect.xmin - viewer_border_.xmin;
+        chunk_orders[index].y = work_package.rect.ymin - viewer_border_.ymin;
         chunk_orders[index].update_distance(hotspots, 9);
       }
 
@@ -308,8 +308,8 @@ blender::Array ExecutionGroup::get_execution_order() const
  */
 void ExecutionGroup::execute(ExecutionSystem *graph)
 {
-  const CompositorContext &context = graph->getContext();
-  const bNodeTree *bTree = context.getbNodeTree();
+  const CompositorContext &context = graph->get_context();
+  const bNodeTree *bTree = context.get_bnodetree();
   if (width_ == 0 || height_ == 0) {
     return;
   } /** \note Break out... no pixels to calculate. */
@@ -321,7 +321,7 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
   } /** \note Early break out. */
   unsigned int chunk_index;
 
-  executionStartTime_ = PIL_check_seconds_timer();
+  execution_start_time_ = PIL_check_seconds_timer();
 
   chunks_finished_ = 0;
   bTree_ = bTree;
@@ -333,26 +333,26 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
 
   bool breaked = false;
   bool finished = false;
-  unsigned int startIndex = 0;
-  const int maxNumberEvaluated = BLI_system_thread_count() * 2;
+  unsigned int start_index = 0;
+  const int max_number_evaluated = BLI_system_thread_count() * 2;
 
   while (!finished && !breaked) {
-    bool startEvaluated = false;
+    bool start_evaluated = false;
     finished = true;
-    int numberEvaluated = 0;
+    int number_evaluated = 0;
 
-    for (int index = startIndex; index < chunks_len_ && numberEvaluated < maxNumberEvaluated;
+    for (int index = start_index; index < chunks_len_ && number_evaluated < max_number_evaluated;
          index++) {
       chunk_index = chunk_order[index];
-      int yChunk = chunk_index / x_chunks_len_;
-      int xChunk = chunk_index - (yChunk * x_chunks_len_);
+      int y_chunk = chunk_index / x_chunks_len_;
+      int x_chunk = chunk_index - (y_chunk * x_chunks_len_);
       const WorkPackage &work_package = work_packages_[chunk_index];
       switch (work_package.state) {
         case eWorkPackageState::NotScheduled: {
-          scheduleChunkWhenPossible(graph, xChunk, yChunk);
+          schedule_chunk_when_possible(graph, x_chunk, y_chunk);
           finished = false;
-          startEvaluated = true;
-          numberEvaluated++;
+          start_evaluated = true;
+          number_evaluated++;
 
           if (bTree->update_draw) {
             bTree->update_draw(bTree->udh);
@@ -361,13 +361,13 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
         }
         case eWorkPackageState::Scheduled: {
           finished = false;
-          startEvaluated = true;
-          numberEvaluated++;
+          start_evaluated = true;
+          number_evaluated++;
           break;
         }
         case eWorkPackageState::Executed: {
-          if (!startEvaluated) {
-            startIndex = index + 1;
+          if (!start_evaluated) {
+            start_index = index + 1;
           }
         }
       };
@@ -383,51 +383,51 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
   DebugInfo::graphviz(graph);
 }
 
-MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber)
+MemoryBuffer **ExecutionGroup::get_input_buffers_opencl(int chunk_number)
 {
-  WorkPackage &work_package = work_packages_[chunkNumber];
+  WorkPackage &work_package = work_packages_[chunk_number];
 
-  MemoryBuffer **memoryBuffers = (MemoryBuffer **)MEM_callocN(
+  MemoryBuffer **memory_buffers = (MemoryBuffer **)MEM_callocN(
       sizeof(MemoryBuffer *) * max_read_buffer_offset_, __func__);
   rcti output;
-  for (ReadBufferOperation *readOperation : read_operations_) {
-    MemoryProxy *memoryProxy = readOperation->getMemoryProxy();
-    this->determineDependingAreaOfInterest(&work_package.rect, readOperation, &output);
-    MemoryBuffer *memoryBuffer = memoryProxy->getExecutor()->constructConsolidatedMemoryBuffer(
-        *memoryProxy, output);
-    memoryBuffers[readOperation->getOffset()] = memoryBuffer;
+  for (ReadBufferOperation *read_operation : read_operations_) {
+    MemoryProxy *memory_proxy = read_operation->get_memory_proxy();
+    this->determine_depending_area_of_interest(&work_package.rect, read_operation, &output);
+    MemoryBuffer *memory_buffer =
+        memory_proxy->get_executor()->construct_consolidated_memory_buffer(*memory_proxy, output);
+    memory_buffers[read_operation->get_offset()] = memory_buffer;
   }
-  return memoryBuffers;
+  return memory_buffers;
 }
 
-MemoryBuffer *ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy &memoryProxy,
-                                                                rcti &rect)
+MemoryBuffer *ExecutionGroup::construct_consolidated_memory_buffer(MemoryProxy &memory_proxy,
+                                                                   rcti &rect)
 {
-  MemoryBuffer *imageBuffer = memoryProxy.getBuffer();
-  MemoryBuffer *result = new MemoryBuffer(&memoryProxy, rect, MemoryBufferState::Temporary);
-  result->fill_from(*imageBuffer);
+  MemoryBuffer *image_buffer = memory_proxy.get_buffer();
+  MemoryBuffer *result = new MemoryBuffer(&memory_proxy, rect, MemoryBufferState::Temporary);
+  result->fill_from(*image_buffer);
   return result;
 }
 
-void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers)
+void ExecutionGroup::finalize_chunk_execution(int chunk_number, MemoryBuffer **memory_buffers)
 {
-  WorkPackage &work_package = work_packages_[chunkNumber];
+  WorkPackage &work_package = work_packages_[chunk_number];
   if (work_package.state == eWorkPackageState::Scheduled) {
     work_package.state = eWorkPackageState::Executed;
   }
 
   atomic_add_and_fetch_u(&chunks_finished_, 1);
-  if (memoryBuffers) {
+  if (memory_buffers) {
     for (unsigned int index = 0; index < max_read_buffer_offset_; index++) {
-      MemoryBuffer *buffer = memoryBuffers[index];
+      MemoryBuffer *buffer = memory_buffers[index];
       if (buffer) {
-        if (buffer->isTemporarily()) {
-          memoryBuffers[index] = nullptr;
+        if (buffer->is_temporarily()) {
+          memory_buffers[index] = nullptr;
           delete buffer;
         }
       }
     }
-    MEM_freeN(memoryBuffers);
+    MEM_freeN(memory_buffers);
   }
   if (bTree_) {
     /* Status report is only performed for top level Execution Groups. */
@@ -442,67 +442,67 @@ void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memo
   }
 }
 
-inline void ExecutionGroup::determineChunkRect(rcti *r_rect,
-                                               const unsigned int xChunk,
-                                               const unsigned int yChunk) const
+inline void ExecutionGroup::determine_chunk_rect(rcti *r_rect,
+                                                 const unsigned int x_chunk,
+                                                 const unsigned int y_chunk) const
 {
-  const int border_width = BLI_rcti_size_x(&viewerBorder_);
-  const int border_height = BLI_rcti_size_y(&viewerBorder_);
+  const int border_width = BLI_rcti_size_x(&viewer_border_);
+  const int border_height = BLI_rcti_size_y(&viewer_border_);
 
   if (flags_.single_threaded) {
-    BLI_rcti_init(r_rect, viewerBorder_.xmin, border_width, viewerBorder_.ymin, border_height);
+    BLI_rcti_init(r_rect, viewer_border_.xmin, border_width, viewer_border_.ymin, border_height);
   }
   else {
-    const unsigned int minx = xChunk * chunkSize_ + viewerBorder_.xmin;
-    const unsigned int miny = yChunk * chunkSize_ + viewerBorder_.ymin;
-    const unsigned int width = MIN2((unsigned int)viewerBorder_.xmax, width_);
-    const unsigned int height = MIN2((unsigned int)viewerBorder_.ymax, height_);
+    const unsigned int minx = x_chunk * chunk_size_ + viewer_border_.xmin;
+    const unsigned int miny = y_chunk * chunk_size_ + viewer_border_.ymin;
+    const unsigned int width = MIN2((unsigned int)viewer_border_.xmax, width_);
+    const unsigned int height = MIN2((unsigned int)viewer_border_.ymax, height_);
     BLI_rcti_init(r_rect,
                   MIN2(minx, width_),
-                  MIN2(minx + chunkSize_, width),
+                  MIN2(minx + chunk_size_, width),
                   MIN2(miny, height_),
-                  MIN2(miny + chunkSize_, height));
+                  MIN2(miny + chunk_size_, height));
   }
 }
 
-void ExecutionGroup::determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const
+void ExecutionGroup::determine_chunk_rect(rcti *r_rect, const unsigned int chunk_number) const
 {
-  const unsigned int yChunk = chunkNumber / x_chunks_len_;
-  const unsigned int xChunk = chunkNumber - (yChunk * x_chunks_len_);
-  determineChunkRect(r_rect, xChunk, yChunk);
+  const unsigned int y_chunk = chunk_number / x_chunks_len_;
+  const unsigned int x_chunk = chunk_number - (y_chunk * x_chunks_len_);
+  determine_chunk_rect(r_rect, x_chunk, y_chunk);
 }
 
-MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect)
+MemoryBuffer *ExecutionGroup::allocate_output_buffer(rcti &rect)
 {
   /* We assume that this method is only called from complex execution groups. */
-  NodeOperation *operation = this->getOutputOperation();
+  NodeOperation *operation = this->get_output_operation();
   if (operation->get_flags().is_write_buffer_operation) {
-    WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation;
+    WriteBufferOperation *write_operation = (WriteBufferOperation *)operation;
     MemoryBuffer *buffer = new MemoryBuffer(
-        writeOperation->getMemoryProxy(), rect, MemoryBufferState::Temporary);
+        write_operation->get_memory_proxy(), rect, MemoryBufferState::Temporary);
     return buffer;
   }
   return nullptr;
 }
 
-bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area)
+bool ExecutionGroup::schedule_area_when_possible(ExecutionSystem *graph, rcti *area)
 {
   if (flags_.single_threaded) {
-    return scheduleChunkWhenPossible(graph, 0, 0);
+    return schedule_chunk_when_possible(graph, 0, 0);
   }
   /* Find all chunks inside the rect
    * determine `minxchunk`, `minychunk`, `maxxchunk`, `maxychunk`
    * where x and y are chunk-numbers. */
 
   int indexx, indexy;
-  int minx = max_ii(area->xmin - viewerBorder_.xmin, 0);
-  int maxx = min_ii(area->xmax - viewerBorder_.xmin, viewerBorder_.xmax - viewerBorder_.xmin);
-  int miny = max_ii(area->ymin - viewerBorder_.ymin, 0);
-  int maxy = min_ii(area->ymax - viewerBorder_.ymin, viewerBorder_.ymax - viewerBorder_.ymin);
-  int minxchunk = minx / (int)chunkSize_;
-  int maxxchunk = (maxx + (int)chunkSize_ - 1) / (int)chunkSize_;
-  int minychunk = miny / (int)chunkSize_;
-  int maxychunk = (maxy + (int)chunkSize_ - 1) / (int)chunkSize_;
+  int minx = max_ii(area->xmin - viewer_border_.xmin, 0);
+  int maxx = min_ii(area->xmax - viewer_border_.xmin, viewer_border_.xmax - viewer_border_.xmin);
+  int miny = max_ii(area->ymin - viewer_border_.ymin, 0);
+  int maxy = min_ii(area->ymax - viewer_border_.ymin, viewer_border_.ymax - viewer_border_.ymin);
+  int minxchunk = minx / (int)chunk_size_;
+  int maxxchunk = (maxx + (int)chunk_size_ - 1) / (int)chunk_size_;
+  int minychunk = miny / (int)chunk_size_;
+  int maxychunk = (maxy + (int)chunk_size_ - 1) / (int)chunk_size_;
   minxchunk = max_ii(minxchunk, 0);
   minychunk = max_ii(minychunk, 0);
   maxxchunk = min_ii(maxxchunk, (int)x_chunks_len_);
@@ -511,7 +511,7 @@ bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area
   bool result = true;
   for (indexx = minxchunk; indexx < maxxchunk; indexx++) {
     for (indexy = minychunk; indexy < maxychunk; indexy++) {
-      if (!scheduleChunkWhenPossible(graph, indexx, indexy)) {
+      if (!schedule_chunk_when_possible(graph, indexx, indexy)) {
         result = false;
       }
     }
@@ -520,9 +520,9 @@ bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area
   return result;
 }
 
-bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber)
+bool ExecutionGroup::schedule_chunk(unsigned int chunk_number)
 {
-  WorkPackage &work_package = work_packages_[chunkNumber];
+  WorkPackage &work_package = work_packages_[chunk_number];
   if (work_package.state == eWorkPackageState::NotScheduled) {
     work_package.state = eWorkPackageState::Scheduled;
     WorkScheduler::schedule(&work_package);
@@ -531,9 +531,9 @@ bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber)
   return false;
 }
 
-bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph,
-                                               const int chunk_x,
-                                               const int chunk_y)
+bool ExecutionGroup::schedule_chunk_when_possible(ExecutionSystem *graph,
+                                                  const int chunk_x,
+                                                  const int chunk_y)
 {
   if (chunk_x < 0 || chunk_x >= (int)x_chunks_len_) {
     return true;
@@ -557,42 +557,43 @@ bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph,
 
   for (ReadBufferOperation *read_operation : read_operations_) {
     BLI_rcti_init(&area, 0, 0, 0, 0);
-    MemoryProxy *memory_proxy = read_operation->getMemoryProxy();
-    determineDependingAreaOfInterest(&work_package.rect, read_operation, &area);
-    ExecutionGroup *group = memory_proxy->getExecutor();
+    MemoryProxy *memory_proxy = read_operation->get_memory_proxy();
+    determine_depending_area_of_interest(&work_package.rect, read_operation, &area);
+    ExecutionGroup *group = memory_proxy->get_executor();
 
-    if (!group->scheduleAreaWhenPossible(graph, &area)) {
+    if (!group->schedule_area_when_possible(graph, &area)) {
       can_be_executed = false;
     }
   }
 
   if (can_be_executed) {
-    scheduleChunk(chunk_index);
+    schedule_chunk(chunk_index);
   }
 
   return false;
 }
 
-void ExecutionGroup::determineDependingAreaOfInterest(rcti *input,
-                                                      ReadBufferOperation *readOperation,
-                                                      rcti *output)
+void ExecutionGroup::determine_depending_area_of_interest(rcti *input,
+                                                          ReadBufferOperation *read_operation,
+                                                          rcti *output)
 {
-  this->getOutputOperation()->determineDependingAreaOfInterest(input, readOperation, output);
+  this->get_output_operation()->determine_depending_area_of_interest(
+      input, read_operation, output);
 }
 
-void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float ymax)
+void ExecutionGroup::set_viewer_border(float xmin, float xmax, float ymin, float ymax)
 {
-  const NodeOperation &operation = *this->getOutputOperation();
+  const NodeOperation &operation = *this->get_output_operation();
   if (operation.get_flags().use_viewer_border) {
-    BLI_rcti_init(&viewerBorder_, xmin * width_, xmax * width_, ymin * height_, ymax * height_);
+    BLI_rcti_init(&viewer_border_, xmin * width_, xmax * width_, ymin * height_, ymax * height_);
   }
 }
 
-void ExecutionGroup::setRenderBorder(float xmin, float xmax, float ymin, float ymax)
+void ExecutionGroup::set_render_border(float xmin, float xmax, float ymin, float ymax)
 {
-  const NodeOperation &operation = *this->getOutputOperation();
-  if (operation.isOutputOperation(true) && operation.get_flags().use_render_border) {
-    BLI_rcti_init(&viewerBorder_, xmin * width_, xmax * width_, ymin * height_, ymax * height_);
+  const NodeOperation &operation = *this->get_output_operation();
+  if (operation.is_output_operation(true) && operation.get_flags().use_render_border) {
+    BLI_rcti_init(&viewer_border_, xmin * width_, xmax * width_, ymin * height_, ymax * height_);
   }
 }
 
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.h b/source/blender/compositor/intern/COM_ExecutionGroup.h
index c0737b2bc02..6d1520f9c24 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.h
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.h
@@ -109,7 +109,7 @@ class ExecutionGroup {
    * \brief size of a single chunk, being Width or of height
    * a chunk is always a square, except at the edges of the MemoryBuffer
    */
-  unsigned int chunkSize_;
+  unsigned int chunk_size_;
 
   /**
    * \brief number of chunks in the x-axis
@@ -158,12 +158,12 @@ class ExecutionGroup {
    * \brief denotes boundary for border compositing
    * \note measured in pixel space
    */
-  rcti viewerBorder_;
+  rcti viewer_border_;
 
   /**
    * \brief start time of execution
    */
-  double executionStartTime_;
+  double execution_start_time_;
 
   // methods
   /**
@@ -175,13 +175,14 @@ class ExecutionGroup {
   /**
    * \brief Determine the rect (minx, maxx, miny, maxy) of a chunk at a position.
    */
-  void determineChunkRect(rcti *r_rect,
-                          const unsigned int xChunk,
-                          const unsigned int yChunk) const;
+  void determine_chunk_rect(rcti *r_rect,
+                            const unsigned int x_chunk,
+                            const unsigned int y_chunk) const;
 
   /**
-   * \brief determine the number of chunks, based on the chunkSize, width and height.
-   * \note The result are stored in the fields numberOfChunks, numberOfXChunks, numberOfYChunks
+   * \brief determine the number of chunks, based on the chunk_size, width and height.
+   * \note The result are stored in the fields number_of_chunks, number_of_xchunks,
+   * number_of_ychunks
    */
   void init_number_of_chunks();
 
@@ -190,13 +191,13 @@ class ExecutionGroup {
    * \note scheduling succeeds when all input requirements are met and the chunks hasn't been
    * scheduled yet.
    * \param graph:
-   * \param xChunk:
-   * \param yChunk:
+   * \param x_chunk:
+   * \param y_chunk:
    * \return [true:false]
    * true: package(s) are scheduled
    * false: scheduling is deferred (depending workpackages are scheduled)
    */
-  bool scheduleChunkWhenPossible(ExecutionSystem *graph, const int chunk_x, const int chunk_y);
+  bool schedule_chunk_when_possible(ExecutionSystem *graph, const int chunk_x, const int chunk_y);
 
   /**
    * \brief try to schedule a specific area.
@@ -208,24 +209,24 @@ class ExecutionGroup {
    * true: package(s) are scheduled
    * false: scheduling is deferred (depending workpackages are scheduled)
    */
-  bool scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area);
+  bool schedule_area_when_possible(ExecutionSystem *graph, rcti *area);
 
   /**
    * \brief add a chunk to the WorkScheduler.
    * \param chunknumber:
    */
-  bool scheduleChunk(unsigned int chunkNumber);
+  bool schedule_chunk(unsigned int chunk_number);
 
   /**
    * \brief determine the area of interest of a certain input area
    * \note This method only evaluates a single ReadBufferOperation
    * \param input: the input area
-   * \param readOperation: The ReadBufferOperation where the area needs to be evaluated
+   * \param read_operation: The ReadBufferOperation where the area needs to be evaluated
    * \param output: the area needed of the ReadBufferOperation. Result
    */
-  void determineDependingAreaOfInterest(rcti *input,
-                                        ReadBufferOperation *readOperation,
-                                        rcti *output);
+  void determine_depending_area_of_interest(rcti *input,
+                                            ReadBufferOperation *read_operation,
+                                            rcti *output);
 
   /**
    * Return the execution order of the user visible chunks.
@@ -258,13 +259,13 @@ class ExecutionGroup {
    * \param operation:
    * \return True if the operation was successfully added
    */
-  bool addOperation(NodeOperation *operation);
+  bool add_operation(NodeOperation *operation);
 
   /**
    * \brief set whether this ExecutionGroup is an output
-   * \param isOutput:
+   * \param is_output:
    */
-  void setOutputExecutionGroup(bool is_output)
+  void set_output_execution_group(bool is_output)
   {
     flags_.is_output = is_output;
   }
@@ -273,13 +274,13 @@ class ExecutionGroup {
    * \brief determine the resolution of this ExecutionGroup
    * \param resolution:
    */
-  void determineResolution(unsigned int resolution[2]);
+  void determine_resolution(unsigned int resolution[2]);
 
   /**
    * \brief set the resolution of this executiongroup
    * \param resolution:
    */
-  void setResolution(unsigned int resolution[2])
+  void set_resolution(unsigned int resolution[2])
   {
     width_ = resolution[0];
     height_ = resolution[1];
@@ -288,7 +289,7 @@ class ExecutionGroup {
   /**
    * \brief get the width of this execution group
    */
-  unsigned int getWidth() const
+  unsigned int get_width() const
   {
     return width_;
   }
@@ -296,7 +297,7 @@ class ExecutionGroup {
   /**
    * \brief get the height of this execution group
    */
-  unsigned int getHeight() const
+  unsigned int get_height() const
   {
     return height_;
   }
@@ -305,24 +306,24 @@ class ExecutionGroup {
    * \brief get the output operation of this ExecutionGroup
    * \return NodeOperation *output operation
    */
-  NodeOperation *getOutputOperation() const;
+  NodeOperation *get_output_operation() const;
 
   /**
    * \brief compose multiple chunks into a single chunk
    * \return Memorybuffer *consolidated chunk
    */
-  MemoryBuffer *constructConsolidatedMemoryBuffer(MemoryProxy &memoryProxy, rcti &rect);
+  MemoryBuffer *construct_consolidated_memory_buffer(MemoryProxy &memory_proxy, rcti &rect);
 
   /**
-   * \brief initExecution is called just before the execution of the whole graph will be done.
-   * \note The implementation will calculate the chunkSize of this execution group.
+   * \brief init_execution is called just before the execution of the whole graph will be done.
+   * \note The implementation will calculate the chunk_size of this execution group.
    */
-  void initExecution();
+  void init_execution();
 
   /**
    * \brief get all inputbuffers needed to calculate an chunk
    * \note all inputbuffers must be executed
-   * \param chunkNumber: the chunk to be calculated
+   * \param chunk_number: the chunk to be calculated
    * \return (MemoryBuffer **) the inputbuffers
    */
   MemoryBuffer **getInputBuffersCPU();
@@ -330,31 +331,31 @@ class ExecutionGroup {
   /**
    * \brief get all inputbuffers needed to calculate an chunk
    * \note all inputbuffers must be executed
-   * \param chunkNumber: the chunk to be calculated
+   * \param chunk_number: the chunk to be calculated
    * \return (MemoryBuffer **) the inputbuffers
    */
-  MemoryBuffer **getInputBuffersOpenCL(int chunkNumber);
+  MemoryBuffer **get_input_buffers_opencl(int chunk_number);
 
   /**
    * \brief allocate the outputbuffer of a chunk
-   * \param chunkNumber: the number of the chunk in the ExecutionGroup
+   * \param chunk_number: the number of the chunk in the ExecutionGroup
    * \param rect: the rect of that chunk
-   * \see determineChunkRect
+   * \see determine_chunk_rect
    */
-  MemoryBuffer *allocateOutputBuffer(rcti &rect);
+  MemoryBuffer *allocate_output_buffer(rcti &rect);
 
   /**
    * \brief after a chunk is executed the needed resources can be freed or unlocked.
    * \param chunknumber:
    * \param memorybuffers:
    */
-  void finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers);
+  void finalize_chunk_execution(int chunk_number, MemoryBuffer **memory_buffers);
 
   /**
-   * \brief deinitExecution is called just after execution the whole graph.
+   * \brief deinit_execution is called just after execution the whole graph.
    * \note It will release all needed resources
    */
-  void deinitExecution();
+  void deinit_execution();
 
   /**
    * \brief schedule an ExecutionGroup
@@ -377,26 +378,26 @@ class ExecutionGroup {
   /**
    * \brief Determine the rect (minx, maxx, miny, maxy) of a chunk.
    */
-  void determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const;
+  void determine_chunk_rect(rcti *r_rect, const unsigned int chunk_number) const;
 
-  void setChunksize(int chunksize)
+  void set_chunksize(int chunksize)
   {
-    chunkSize_ = chunksize;
+    chunk_size_ = chunksize;
   }
 
   /**
    * \brief get the Render priority of this ExecutionGroup
    * \see ExecutionSystem.execute
    */
-  eCompositorPriority getRenderPriority();
+  eCompositorPriority get_render_priority();
 
   /**
    * \brief set border for viewer operation
    * \note all the coordinates are assumed to be in normalized space
    */
-  void setViewerBorder(float xmin, float xmax, float ymin, float ymax);
+  void set_viewer_border(float xmin, float xmax, float ymin, float ymax);
 
-  void setRenderBorder(float xmin, float xmax, float ymin, float ymax);
+  void set_render_border(float xmin, float xmax, float ymin, float ymax);
 
   /* allow the DebugInfo class to look at internals */
   friend class DebugInfo;
diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc
index 0ac99152c9f..b319aaa4b21 100644
--- a/source/blender/compositor/intern/COM_ExecutionModel.cc
+++ b/source/blender/compositor/intern/COM_ExecutionModel.cc
@@ -24,7 +24,7 @@ namespace blender::compositor {
 ExecutionModel::ExecutionModel(CompositorContext &context, Span operations)
     : context_(context), operations_(operations)
 {
-  const bNodeTree *node_tree = context_.getbNodeTree();
+  const bNodeTree *node_tree = context_.get_bnodetree();
 
   const rctf *viewer_border = &node_tree->viewer_border;
   border_.use_viewer_border = (node_tree->flag & NTREE_VIEWER_BORDER) &&
@@ -32,10 +32,10 @@ ExecutionModel::ExecutionModel(CompositorContext &context, Span
                               viewer_border->ymin < viewer_border->ymax;
   border_.viewer_border = viewer_border;
 
-  const RenderData *rd = context_.getRenderData();
+  const RenderData *rd = context_.get_render_data();
   /* Case when cropping to render border happens is handled in
    * compositor output and render layer nodes. */
-  border_.use_render_border = context.isRendering() && (rd->mode & R_BORDER) &&
+  border_.use_render_border = context.is_rendering() && (rd->mode & R_BORDER) &&
                               !(rd->mode & R_CROP);
   border_.render_border = &rd->border;
 }
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc
index 880383853d6..82821a1ddb1 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.cc
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc
@@ -38,37 +38,37 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
                                  bNodeTree *editingtree,
                                  bool rendering,
                                  bool fastcalculation,
-                                 const ColorManagedViewSettings *viewSettings,
-                                 const ColorManagedDisplaySettings *displaySettings,
-                                 const char *viewName)
+                                 const ColorManagedViewSettings *view_settings,
+                                 const ColorManagedDisplaySettings *display_settings,
+                                 const char *view_name)
 {
   num_work_threads_ = WorkScheduler::get_num_cpu_threads();
-  context_.setViewName(viewName);
-  context_.setScene(scene);
-  context_.setbNodeTree(editingtree);
-  context_.setPreviewHash(editingtree->previews);
-  context_.setFastCalculation(fastcalculation);
+  context_.set_view_name(view_name);
+  context_.set_scene(scene);
+  context_.set_bnodetree(editingtree);
+  context_.set_preview_hash(editingtree->previews);
+  context_.set_fast_calculation(fastcalculation);
   /* initialize the CompositorContext */
   if (rendering) {
-    context_.setQuality((eCompositorQuality)editingtree->render_quality);
+    context_.set_quality((eCompositorQuality)editingtree->render_quality);
   }
   else {
-    context_.setQuality((eCompositorQuality)editingtree->edit_quality);
+    context_.set_quality((eCompositorQuality)editingtree->edit_quality);
   }
-  context_.setRendering(rendering);
+  context_.set_rendering(rendering);
   context_.setHasActiveOpenCLDevices(WorkScheduler::has_gpu_devices() &&
                                      (editingtree->flag & NTREE_COM_OPENCL));
 
-  context_.setRenderData(rd);
-  context_.setViewSettings(viewSettings);
-  context_.setDisplaySettings(displaySettings);
+  context_.set_render_data(rd);
+  context_.set_view_settings(view_settings);
+  context_.set_display_settings(display_settings);
 
   BLI_mutex_init(&work_mutex_);
   BLI_condition_init(&work_finished_cond_);
 
   {
     NodeOperationBuilder builder(&context_, editingtree, this);
-    builder.convertToOperations(this);
+    builder.convert_to_operations(this);
   }
 
   switch (context_.get_execution_model()) {
@@ -184,7 +184,7 @@ void ExecutionSystem::execute_work(const rcti &work_rect,
 
 bool ExecutionSystem::is_breaked() const
 {
-  const bNodeTree *btree = context_.getbNodeTree();
+  const bNodeTree *btree = context_.get_bnodetree();
   return btree->test_break(btree->tbh);
 }
 
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h
index 4a6a0f1bad8..4a3fecaca47 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.h
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.h
@@ -64,8 +64,8 @@ namespace blender::compositor {
  * based on settings; like MixNode. based on the selected Mixtype a different operation will be
  * used. for more information see the page about creating new Nodes. [@subpage newnode]
  *
- * \see ExecutionSystem.convertToOperations
- * \see Node.convertToOperations
+ * \see ExecutionSystem.convert_to_operations
+ * \see Node.convert_to_operations
  * \see NodeOperation base class for all operations in the system
  *
  * \section EM_Step3 Step3: add additional conversions to the operation system
@@ -89,7 +89,7 @@ namespace blender::compositor {
  *       Bottom left of the images are aligned.
  *
  * \see COM_convert_data_type Datatype conversions
- * \see Converter.convertResolution Image size conversions
+ * \see Converter.convert_resolution Image size conversions
  *
  * \section EM_Step4 Step4: group operations in executions groups
  * ExecutionGroup are groups of operations that are calculated as being one bigger operation.
@@ -112,9 +112,9 @@ namespace blender::compositor {
  * |cFAA  |           |cFAA  |         |cFAA   |           |cFAA   |
  * +------+           +------+         +-------+           +-------+
  * 
- * \see ExecutionSystem.groupOperations method doing this step - * \see ExecutionSystem.addReadWriteBufferOperations - * \see NodeOperation.isComplex + * \see ExecutionSystem.group_operations method doing this step + * \see ExecutionSystem.add_read_write_buffer_operations + * \see NodeOperation.is_complex * \see ExecutionGroup class representing the ExecutionGroup */ @@ -175,9 +175,9 @@ class ExecutionSystem { bNodeTree *editingtree, bool rendering, bool fastcalculation, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName); + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name); /** * Destructor @@ -198,7 +198,7 @@ class ExecutionSystem { /** * \brief get the reference to the compositor context */ - const CompositorContext &getContext() const + const CompositorContext &get_context() const { return context_; } diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index ce00fff5858..80c10aec00a 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -38,7 +38,7 @@ FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context, num_operations_finished_(0) { priorities_.append(eCompositorPriority::High); - if (!context.isFastCalculation()) { + if (!context.is_fast_calculation()) { priorities_.append(eCompositorPriority::Medium); priorities_.append(eCompositorPriority::Low); } @@ -46,7 +46,7 @@ FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context, void FullFrameExecutionModel::execute(ExecutionSystem &exec_system) { - const bNodeTree *node_tree = this->context_.getbNodeTree(); + const bNodeTree *node_tree = this->context_.get_bnodetree(); node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Initializing execution")); DebugInfo::graphviz(&exec_system, "compositor_prior_rendering"); @@ -57,14 +57,14 @@ void FullFrameExecutionModel::execute(ExecutionSystem &exec_system) void FullFrameExecutionModel::determine_areas_to_render_and_reads() { - const bool is_rendering = context_.isRendering(); - const bNodeTree *node_tree = context_.getbNodeTree(); + const bool is_rendering = context_.is_rendering(); + const bNodeTree *node_tree = context_.get_bnodetree(); rcti area; for (eCompositorPriority priority : priorities_) { for (NodeOperation *op : operations_) { - op->setbNodeTree(node_tree); - if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + op->set_bnodetree(node_tree); + if (op->is_output_operation(is_rendering) && op->get_render_priority() == priority) { get_output_render_area(op, area); determine_areas_to_render(op, area); determine_reads(op); @@ -81,7 +81,7 @@ Vector FullFrameExecutionModel::get_input_buffers(NodeOperation const int output_x, const int output_y) { - const int num_inputs = op->getNumberOfInputSockets(); + const int num_inputs = op->get_number_of_input_sockets(); Vector inputs_buffers(num_inputs); for (int i = 0; i < num_inputs; i++) { NodeOperation *input = op->get_input_operation(i); @@ -92,7 +92,7 @@ Vector FullFrameExecutionModel::get_input_buffers(NodeOperation rcti rect = buf->get_rect(); BLI_rcti_translate(&rect, offset_x, offset_y); inputs_buffers[i] = new MemoryBuffer( - buf->getBuffer(), buf->get_num_channels(), rect, buf->is_a_single_elem()); + buf->get_buffer(), buf->get_num_channels(), rect, buf->is_a_single_elem()); } return inputs_buffers; } @@ -102,9 +102,10 @@ MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op const int output_y) { rcti rect; - BLI_rcti_init(&rect, output_x, output_x + op->getWidth(), output_y, output_y + op->getHeight()); + BLI_rcti_init( + &rect, output_x, output_x + op->get_width(), output_y, output_y + op->get_height()); - const DataType data_type = op->getOutputSocket(0)->getDataType(); + const DataType data_type = op->get_output_socket(0)->get_data_type(); const bool is_a_single_elem = op->get_flags().is_constant_operation; return new MemoryBuffer(data_type, rect, is_a_single_elem); } @@ -115,9 +116,9 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op) constexpr int output_x = 0; constexpr int output_y = 0; - const bool has_outputs = op->getNumberOfOutputSockets() > 0; + const bool has_outputs = op->get_number_of_output_sockets() > 0; MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op, output_x, output_y) : nullptr; - if (op->getWidth() > 0 && op->getHeight() > 0) { + if (op->get_width() > 0 && op->get_height() > 0) { Vector input_bufs = get_input_buffers(op, output_x, output_y); const int op_offset_x = output_x - op->get_canvas().xmin; const int op_offset_y = output_y - op->get_canvas().ymin; @@ -141,19 +142,19 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op) */ void FullFrameExecutionModel::render_operations() { - const bool is_rendering = context_.isRendering(); + const bool is_rendering = context_.is_rendering(); WorkScheduler::start(this->context_); for (eCompositorPriority priority : priorities_) { for (NodeOperation *op : operations_) { - const bool has_size = op->getWidth() > 0 && op->getHeight() > 0; - const bool is_priority_output = op->isOutputOperation(is_rendering) && - op->getRenderPriority() == priority; + const bool has_size = op->get_width() > 0 && op->get_height() > 0; + const bool is_priority_output = op->is_output_operation(is_rendering) && + op->get_render_priority() == priority; if (is_priority_output && has_size) { render_output_dependencies(op); render_operation(op); } - else if (is_priority_output && !has_size && op->isActiveViewerOutput()) { + else if (is_priority_output && !has_size && op->is_active_viewer_output()) { static_cast(op)->clear_display_buffer(); } } @@ -175,7 +176,7 @@ static Vector get_operation_dependencies(NodeOperation *operati Vector outputs(next_outputs); next_outputs.clear(); for (NodeOperation *output : outputs) { - for (int i = 0; i < output->getNumberOfInputSockets(); i++) { + for (int i = 0; i < output->get_number_of_input_sockets(); i++) { next_outputs.append(output->get_input_operation(i)); } } @@ -190,7 +191,7 @@ static Vector get_operation_dependencies(NodeOperation *operati void FullFrameExecutionModel::render_output_dependencies(NodeOperation *output_op) { - BLI_assert(output_op->isOutputOperation(context_.isRendering())); + BLI_assert(output_op->is_output_operation(context_.is_rendering())); Vector dependencies = get_operation_dependencies(output_op); for (NodeOperation *op : dependencies) { if (!active_buffers_.is_operation_rendered(op)) { @@ -205,7 +206,7 @@ void FullFrameExecutionModel::render_output_dependencies(NodeOperation *output_o void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op, const rcti &output_area) { - BLI_assert(output_op->isOutputOperation(context_.isRendering())); + BLI_assert(output_op->is_output_operation(context_.is_rendering())); Vector> stack; stack.append({output_op, output_area}); @@ -220,7 +221,7 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op active_buffers_.register_area(operation, render_area); - const int num_inputs = operation->getNumberOfInputSockets(); + const int num_inputs = operation->get_number_of_input_sockets(); for (int i = 0; i < num_inputs; i++) { NodeOperation *input_op = operation->get_input_operation(i); rcti input_area; @@ -240,13 +241,13 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op */ void FullFrameExecutionModel::determine_reads(NodeOperation *output_op) { - BLI_assert(output_op->isOutputOperation(context_.isRendering())); + BLI_assert(output_op->is_output_operation(context_.is_rendering())); Vector stack; stack.append(output_op); while (stack.size() > 0) { NodeOperation *operation = stack.pop_last(); - const int num_inputs = operation->getNumberOfInputSockets(); + const int num_inputs = operation->get_number_of_input_sockets(); for (int i = 0; i < num_inputs; i++) { NodeOperation *input_op = operation->get_input_operation(i); if (!active_buffers_.has_registered_reads(input_op)) { @@ -263,7 +264,7 @@ void FullFrameExecutionModel::determine_reads(NodeOperation *output_op) */ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, rcti &r_area) { - BLI_assert(output_op->isOutputOperation(context_.isRendering())); + BLI_assert(output_op->is_output_operation(context_.is_rendering())); /* By default return operation bounds (no border). */ rcti canvas = output_op->get_canvas(); @@ -278,8 +279,8 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border; /* Return de-normalized border within canvas. */ - const int w = output_op->getWidth(); - const int h = output_op->getHeight(); + const int w = output_op->get_width(); + const int h = output_op->get_height(); r_area.xmin = canvas.xmin + norm_border->xmin * w; r_area.xmax = canvas.xmin + norm_border->xmax * w; r_area.ymin = canvas.ymin + norm_border->ymin * h; @@ -290,7 +291,7 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r void FullFrameExecutionModel::operation_finished(NodeOperation *operation) { /* Report inputs reads so that buffers may be freed/reused. */ - const int num_inputs = operation->getNumberOfInputSockets(); + const int num_inputs = operation->get_number_of_input_sockets(); for (int i = 0; i < num_inputs; i++) { active_buffers_.read_finished(operation->get_input_operation(i)); } @@ -301,7 +302,7 @@ void FullFrameExecutionModel::operation_finished(NodeOperation *operation) void FullFrameExecutionModel::update_progress_bar() { - const bNodeTree *tree = context_.getbNodeTree(); + const bNodeTree *tree = context_.get_bnodetree(); if (tree) { const float progress = num_operations_finished_ / static_cast(operations_.size()); tree->progress(tree->prh, progress); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 3aefe8a3e2f..96888ea1c96 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -44,32 +44,32 @@ static rcti create_rect(const int width, const int height) return rect; } -MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state) +MemoryBuffer::MemoryBuffer(MemoryProxy *memory_proxy, const rcti &rect, MemoryBufferState state) { rect_ = rect; is_a_single_elem_ = false; - memoryProxy_ = memoryProxy; - num_channels_ = COM_data_type_num_channels(memoryProxy->getDataType()); + memory_proxy_ = memory_proxy; + num_channels_ = COM_data_type_num_channels(memory_proxy->get_data_type()); buffer_ = (float *)MEM_mallocN_aligned( sizeof(float) * buffer_len() * num_channels_, 16, "COM_MemoryBuffer"); owns_data_ = true; state_ = state; - datatype_ = memoryProxy->getDataType(); + datatype_ = memory_proxy->get_data_type(); set_strides(); } -MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single_elem) +MemoryBuffer::MemoryBuffer(DataType data_type, const rcti &rect, bool is_a_single_elem) { rect_ = rect; is_a_single_elem_ = is_a_single_elem; - memoryProxy_ = nullptr; - num_channels_ = COM_data_type_num_channels(dataType); + memory_proxy_ = nullptr; + num_channels_ = COM_data_type_num_channels(data_type); buffer_ = (float *)MEM_mallocN_aligned( sizeof(float) * buffer_len() * num_channels_, 16, "COM_MemoryBuffer"); owns_data_ = true; state_ = MemoryBufferState::Temporary; - datatype_ = dataType; + datatype_ = data_type; set_strides(); } @@ -95,7 +95,7 @@ MemoryBuffer::MemoryBuffer(float *buffer, { rect_ = rect; is_a_single_elem_ = is_a_single_elem; - memoryProxy_ = nullptr; + memory_proxy_ = nullptr; num_channels_ = num_channels; datatype_ = COM_num_channels_data_type(num_channels); buffer_ = buffer; @@ -107,7 +107,7 @@ MemoryBuffer::MemoryBuffer(float *buffer, MemoryBuffer::MemoryBuffer(const MemoryBuffer &src) : MemoryBuffer(src.datatype_, src.rect_, false) { - memoryProxy_ = src.memoryProxy_; + memory_proxy_ = src.memory_proxy_; /* src may be single elem buffer */ fill_from(src); } @@ -120,7 +120,7 @@ void MemoryBuffer::set_strides() } else { this->elem_stride = num_channels_; - this->row_stride = getWidth() * num_channels_; + this->row_stride = get_width() * num_channels_; } to_positive_x_stride_ = rect_.xmin < 0 ? -rect_.xmin + 1 : (rect_.xmin == 0 ? 1 : 0); to_positive_y_stride_ = rect_.ymin < 0 ? -rect_.ymin + 1 : (rect_.ymin == 0 ? 1 : 0); @@ -140,7 +140,7 @@ BuffersIterator MemoryBuffer::iterate_with(Span inputs, c { BuffersIteratorBuilder builder(buffer_, rect_, area, elem_stride); for (MemoryBuffer *input : inputs) { - builder.add_input(input->getBuffer(), input->get_rect(), input->elem_stride); + builder.add_input(input->get_buffer(), input->get_rect(), input->elem_stride); } return builder.build(); } @@ -248,7 +248,7 @@ void MemoryBuffer::copy_from(const MemoryBuffer *src, void MemoryBuffer::copy_from(const uchar *src, const rcti &area) { const int elem_stride = this->get_num_channels(); - const int row_stride = elem_stride * getWidth(); + const int row_stride = elem_stride * get_width(); copy_from(src, area, 0, this->get_num_channels(), elem_stride, row_stride, 0); } @@ -307,7 +307,7 @@ static void colorspace_to_scene_linear(MemoryBuffer *buf, const rcti &area, Colo const int height = BLI_rcti_size_y(&area); float *out = buf->get_elem(area.xmin, area.ymin); /* If area allows continuous memory do conversion in one step. Otherwise per row. */ - if (buf->getWidth() == width) { + if (buf->get_width() == width) { IMB_colormanagement_colorspace_to_scene_linear( out, width, height, buf->get_num_channels(), colorspace, false); } @@ -404,7 +404,7 @@ void MemoryBuffer::fill_from(const MemoryBuffer &src) copy_from(&src, overlap); } -void MemoryBuffer::writePixel(int x, int y, const float color[4]) +void MemoryBuffer::write_pixel(int x, int y, const float color[4]) { if (x >= rect_.xmin && x < rect_.xmax && y >= rect_.ymin && y < rect_.ymax) { const int offset = get_coords_offset(x, y); @@ -412,7 +412,7 @@ void MemoryBuffer::writePixel(int x, int y, const float color[4]) } } -void MemoryBuffer::addPixel(int x, int y, const float color[4]) +void MemoryBuffer::add_pixel(int x, int y, const float color[4]) { if (x >= rect_.xmin && x < rect_.xmax && y >= rect_.ymin && y < rect_.ymax) { const int offset = get_coords_offset(x, y); @@ -437,7 +437,7 @@ void MemoryBuffer::read_elem_filtered( const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; - float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight(); + float inv_width = 1.0f / (float)this->get_width(), inv_height = 1.0f / (float)this->get_height(); /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives, * but compositor uses pixel space. For now let's just divide the values and * switch compositor to normalized space for EWA later. @@ -446,8 +446,8 @@ void MemoryBuffer::read_elem_filtered( float du_normal[2] = {deriv[0][0] * inv_width, deriv[0][1] * inv_height}; float dv_normal[2] = {deriv[1][0] * inv_width, deriv[1][1] * inv_height}; - BLI_ewa_filter(this->getWidth(), - this->getHeight(), + BLI_ewa_filter(this->get_width(), + this->get_height(), false, true, uv_normal, @@ -473,7 +473,8 @@ void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivat } else { BLI_assert(datatype_ == DataType::Color); - float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight(); + float inv_width = 1.0f / (float)this->get_width(), + inv_height = 1.0f / (float)this->get_height(); /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives, * but compositor uses pixel space. For now let's just divide the values and * switch compositor to normalized space for EWA later. @@ -482,8 +483,8 @@ void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivat float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height}; float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height}; - BLI_ewa_filter(this->getWidth(), - this->getHeight(), + BLI_ewa_filter(this->get_width(), + this->get_height(), false, true, uv_normal, diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 984db4acc2a..33d78c99ca6 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -77,7 +77,7 @@ class MemoryBuffer { /** * \brief proxy of the memory (same for all chunks in the same buffer) */ - MemoryProxy *memoryProxy_; + MemoryProxy *memory_proxy_; /** * \brief the type of buffer DataType::Value, DataType::Vector, DataType::Color @@ -125,12 +125,12 @@ class MemoryBuffer { /** * \brief construct new temporarily MemoryBuffer for an area */ - MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state); + MemoryBuffer(MemoryProxy *memory_proxy, const rcti &rect, MemoryBufferState state); /** * \brief construct new temporarily MemoryBuffer for an area */ - MemoryBuffer(DataType datatype, const rcti &rect, bool is_a_single_elem = false); + MemoryBuffer(DataType data_type, const rcti &rect, bool is_a_single_elem = false); MemoryBuffer( float *buffer, int num_channels, int width, int height, bool is_a_single_elem = false); @@ -159,14 +159,14 @@ class MemoryBuffer { float &operator[](int index) { BLI_assert(is_a_single_elem_ ? index < num_channels_ : - index < get_coords_offset(getWidth(), getHeight())); + index < get_coords_offset(get_width(), get_height())); return buffer_[index]; } const float &operator[](int index) const { BLI_assert(is_a_single_elem_ ? index < num_channels_ : - index < get_coords_offset(getWidth(), getHeight())); + index < get_coords_offset(get_width(), get_height())); return buffer_[index]; } @@ -231,7 +231,7 @@ class MemoryBuffer { } /* Do sampling at borders to smooth edges. */ - const float last_x = getWidth() - 1.0f; + const float last_x = get_width() - 1.0f; const float rel_x = get_relative_x(x); float single_x = 0.0f; if (rel_x < 0.0f) { @@ -241,7 +241,7 @@ class MemoryBuffer { single_x = rel_x - last_x; } - const float last_y = getHeight() - 1.0f; + const float last_y = get_height() - 1.0f; const float rel_y = get_relative_y(y); float single_y = 0.0f; if (rel_y < 0.0f) { @@ -257,8 +257,8 @@ class MemoryBuffer { BLI_bilinear_interpolation_fl(buffer_, out, - getWidth(), - getHeight(), + get_width(), + get_height(), num_channels_, get_relative_x(x), get_relative_y(y)); @@ -305,7 +305,7 @@ class MemoryBuffer { const float *get_row_end(int y) const { BLI_assert(has_y(y)); - return buffer_ + (is_a_single_elem() ? num_channels_ : get_coords_offset(getWidth(), y)); + return buffer_ + (is_a_single_elem() ? num_channels_ : get_coords_offset(get_width(), y)); } /** @@ -314,7 +314,7 @@ class MemoryBuffer { */ int get_memory_width() const { - return is_a_single_elem() ? 1 : getWidth(); + return is_a_single_elem() ? 1 : get_width(); } /** @@ -323,7 +323,7 @@ class MemoryBuffer { */ int get_memory_height() const { - return is_a_single_elem() ? 1 : getHeight(); + return is_a_single_elem() ? 1 : get_height(); } uint8_t get_num_channels() const @@ -351,12 +351,12 @@ class MemoryBuffer { BufferArea get_buffer_area(const rcti &area) { - return BufferArea(buffer_, getWidth(), area, elem_stride); + return BufferArea(buffer_, get_width(), area, elem_stride); } BufferArea get_buffer_area(const rcti &area) const { - return BufferArea(buffer_, getWidth(), area, elem_stride); + return BufferArea(buffer_, get_width(), area, elem_stride); } BuffersIterator iterate_with(Span inputs); @@ -366,7 +366,7 @@ class MemoryBuffer { * \brief get the data of this MemoryBuffer * \note buffer should already be available in memory */ - float *getBuffer() + float *get_buffer() { return buffer_; } @@ -381,8 +381,8 @@ class MemoryBuffer { inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) { - const int w = getWidth(); - const int h = getHeight(); + const int w = get_width(); + const int h = get_height(); x = x - rect_.xmin; y = y - rect_.ymin; @@ -433,8 +433,8 @@ class MemoryBuffer { MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) const { - const float w = (float)getWidth(); - const float h = (float)getHeight(); + const float w = (float)get_width(); + const float h = (float)get_height(); x = x - rect_.xmin; y = y - rect_.ymin; @@ -505,11 +505,11 @@ class MemoryBuffer { } /* TODO(manzanilla): to be removed with tiled implementation. */ - inline void readNoCheck(float *result, - int x, - int y, - MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, - MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) + inline void read_no_check(float *result, + int x, + int y, + MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, + MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) { int u = x; int v = y; @@ -525,19 +525,19 @@ class MemoryBuffer { memcpy(result, buffer, sizeof(float) * num_channels_); } - void writePixel(int x, int y, const float color[4]); - void addPixel(int x, int y, const float color[4]); - inline void readBilinear(float *result, - float x, - float y, - MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, - MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) const + void write_pixel(int x, int y, const float color[4]); + void add_pixel(int x, int y, const float color[4]); + inline void read_bilinear(float *result, + float x, + float y, + MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, + MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) const { float u = x; float v = y; this->wrap_pixel(u, v, extend_x, extend_y); - if ((extend_x != MemoryBufferExtend::Repeat && (u < 0.0f || u >= getWidth())) || - (extend_y != MemoryBufferExtend::Repeat && (v < 0.0f || v >= getHeight()))) { + if ((extend_x != MemoryBufferExtend::Repeat && (u < 0.0f || u >= get_width())) || + (extend_y != MemoryBufferExtend::Repeat && (v < 0.0f || v >= get_height()))) { copy_vn_fl(result, num_channels_, 0.0f); return; } @@ -547,8 +547,8 @@ class MemoryBuffer { else { BLI_bilinear_interpolation_wrap_fl(buffer_, result, - getWidth(), - getHeight(), + get_width(), + get_height(), num_channels_, u, v, @@ -562,7 +562,7 @@ class MemoryBuffer { /** * \brief is this MemoryBuffer a temporarily buffer (based on an area, not on a chunk) */ - inline bool isTemporarily() const + inline bool is_temporarily() const { return state_ == MemoryBufferState::Temporary; } @@ -617,8 +617,8 @@ class MemoryBuffer { void fill(const rcti &area, const float *value); void fill(const rcti &area, int channel_offset, const float *value, int value_size); /** - * \brief add the content from otherBuffer to this MemoryBuffer - * \param otherBuffer: source buffer + * \brief add the content from other_buffer to this MemoryBuffer + * \param other_buffer: source buffer * * \note take care when running this on a new buffer since it won't fill in * uninitialized values in areas where the buffers don't overlap. @@ -636,7 +636,7 @@ class MemoryBuffer { /** * \brief get the width of this MemoryBuffer */ - const int getWidth() const + const int get_width() const { return BLI_rcti_size_x(&rect_); } @@ -644,7 +644,7 @@ class MemoryBuffer { /** * \brief get the height of this MemoryBuffer */ - const int getHeight() const + const int get_height() const { return BLI_rcti_size_y(&rect_); } diff --git a/source/blender/compositor/intern/COM_MemoryProxy.cc b/source/blender/compositor/intern/COM_MemoryProxy.cc index 507caa25655..895990bc87f 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.cc +++ b/source/blender/compositor/intern/COM_MemoryProxy.cc @@ -23,7 +23,7 @@ namespace blender::compositor { MemoryProxy::MemoryProxy(DataType datatype) { - writeBufferOperation_ = nullptr; + write_buffer_operation_ = nullptr; executor_ = nullptr; datatype_ = datatype; } diff --git a/source/blender/compositor/intern/COM_MemoryProxy.h b/source/blender/compositor/intern/COM_MemoryProxy.h index cf262d72649..89149a47976 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.h +++ b/source/blender/compositor/intern/COM_MemoryProxy.h @@ -42,7 +42,7 @@ class MemoryProxy { /** * \brief reference to the output operation of the executiongroup */ - WriteBufferOperation *writeBufferOperation_; + WriteBufferOperation *write_buffer_operation_; /** * \brief reference to the executor. the Execution group that can fill a chunk @@ -66,7 +66,7 @@ class MemoryProxy { * \brief set the ExecutionGroup that can be scheduled to calculate a certain chunk. * \param group: the ExecutionGroup to set */ - void setExecutor(ExecutionGroup *executor) + void set_executor(ExecutionGroup *executor) { executor_ = executor; } @@ -74,7 +74,7 @@ class MemoryProxy { /** * \brief get the ExecutionGroup that can be scheduled to calculate a certain chunk. */ - ExecutionGroup *getExecutor() const + ExecutionGroup *get_executor() const { return executor_; } @@ -83,18 +83,18 @@ class MemoryProxy { * \brief set the WriteBufferOperation that is responsible for writing to this MemoryProxy * \param operation: */ - void setWriteBufferOperation(WriteBufferOperation *operation) + void set_write_buffer_operation(WriteBufferOperation *operation) { - writeBufferOperation_ = operation; + write_buffer_operation_ = operation; } /** * \brief get the WriteBufferOperation that is responsible for writing to this MemoryProxy * \return WriteBufferOperation */ - WriteBufferOperation *getWriteBufferOperation() const + WriteBufferOperation *get_write_buffer_operation() const { - return writeBufferOperation_; + return write_buffer_operation_; } /** @@ -110,12 +110,12 @@ class MemoryProxy { /** * \brief get the allocated memory */ - inline MemoryBuffer *getBuffer() + inline MemoryBuffer *get_buffer() { return buffer_; } - inline DataType getDataType() + inline DataType get_data_type() { return datatype_; } diff --git a/source/blender/compositor/intern/COM_MetaData.cc b/source/blender/compositor/intern/COM_MetaData.cc index 5775a7eab65..530634a6e41 100644 --- a/source/blender/compositor/intern/COM_MetaData.cc +++ b/source/blender/compositor/intern/COM_MetaData.cc @@ -29,9 +29,9 @@ void MetaData::add(const blender::StringRef key, const blender::StringRef value) entries_.add(key, value); } -void MetaData::addCryptomatteEntry(const blender::StringRef layer_name, - const blender::StringRefNull key, - const blender::StringRef value) +void MetaData::add_cryptomatte_entry(const blender::StringRef layer_name, + const blender::StringRefNull key, + const blender::StringRef value) { add(blender::bke::cryptomatte::BKE_cryptomatte_meta_data_key(layer_name, key), value); } @@ -40,7 +40,7 @@ void MetaData::addCryptomatteEntry(const blender::StringRef layer_name, * * When a conversion happens it will also add the cryptomatte name key with the given * `layer_name`. */ -void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name) +void MetaData::replace_hash_neutral_cryptomatte_keys(const blender::StringRef layer_name) { std::string cryptomatte_hash = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_HASH, ""); std::string cryptomatte_conversion = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_CONVERSION, @@ -49,27 +49,28 @@ void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_ if (cryptomatte_hash.length() || cryptomatte_conversion.length() || cryptomatte_manifest.length()) { - addCryptomatteEntry(layer_name, "name", layer_name); + add_cryptomatte_entry(layer_name, "name", layer_name); } if (cryptomatte_hash.length()) { - addCryptomatteEntry(layer_name, "hash", cryptomatte_hash); + add_cryptomatte_entry(layer_name, "hash", cryptomatte_hash); } if (cryptomatte_conversion.length()) { - addCryptomatteEntry(layer_name, "conversion", cryptomatte_conversion); + add_cryptomatte_entry(layer_name, "conversion", cryptomatte_conversion); } if (cryptomatte_manifest.length()) { - addCryptomatteEntry(layer_name, "manifest", cryptomatte_manifest); + add_cryptomatte_entry(layer_name, "manifest", cryptomatte_manifest); } } -void MetaData::addToRenderResult(RenderResult *render_result) const +void MetaData::add_to_render_result(RenderResult *render_result) const { for (Map::Item entry : entries_.items()) { BKE_render_result_stamp_data(render_result, entry.key.c_str(), entry.value.c_str()); } } -void MetaDataExtractCallbackData::addMetaData(blender::StringRef key, blender::StringRefNull value) +void MetaDataExtractCallbackData::add_meta_data(blender::StringRef key, + blender::StringRefNull value) { if (!meta_data) { meta_data = std::make_unique(); @@ -77,7 +78,7 @@ void MetaDataExtractCallbackData::addMetaData(blender::StringRef key, blender::S meta_data->add(key, value); } -void MetaDataExtractCallbackData::setCryptomatteKeys(blender::StringRef cryptomatte_layer_name) +void MetaDataExtractCallbackData::set_cryptomatte_keys(blender::StringRef cryptomatte_layer_name) { manifest_key = blender::bke::cryptomatte::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "manifest"); @@ -95,13 +96,13 @@ void MetaDataExtractCallbackData::extract_cryptomatte_meta_data(void *_data, MetaDataExtractCallbackData *data = static_cast(_data); blender::StringRefNull key(propname); if (key == data->hash_key) { - data->addMetaData(META_DATA_KEY_CRYPTOMATTE_HASH, propvalue); + data->add_meta_data(META_DATA_KEY_CRYPTOMATTE_HASH, propvalue); } else if (key == data->conversion_key) { - data->addMetaData(META_DATA_KEY_CRYPTOMATTE_CONVERSION, propvalue); + data->add_meta_data(META_DATA_KEY_CRYPTOMATTE_CONVERSION, propvalue); } else if (key == data->manifest_key) { - data->addMetaData(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue); + data->add_meta_data(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue); } } diff --git a/source/blender/compositor/intern/COM_MetaData.h b/source/blender/compositor/intern/COM_MetaData.h index a76540dc3af..0bb014525c3 100644 --- a/source/blender/compositor/intern/COM_MetaData.h +++ b/source/blender/compositor/intern/COM_MetaData.h @@ -44,14 +44,14 @@ constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_NAME("cryptomatte/{hash}/ class MetaData { private: Map entries_; - void addCryptomatteEntry(const blender::StringRef layer_name, - const blender::StringRefNull key, - const blender::StringRef value); + void add_cryptomatte_entry(const blender::StringRef layer_name, + const blender::StringRefNull key, + const blender::StringRef value); public: void add(const blender::StringRef key, const blender::StringRef value); - void replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name); - void addToRenderResult(RenderResult *render_result) const; + void replace_hash_neutral_cryptomatte_keys(const blender::StringRef layer_name); + void add_to_render_result(RenderResult *render_result) const; #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:MetaData") #endif @@ -63,8 +63,8 @@ struct MetaDataExtractCallbackData { std::string conversion_key; std::string manifest_key; - void addMetaData(blender::StringRef key, blender::StringRefNull value); - void setCryptomatteKeys(blender::StringRef cryptomatte_layer_name); + void add_meta_data(blender::StringRef key, blender::StringRefNull value); + void set_cryptomatte_keys(blender::StringRef cryptomatte_layer_name); /* C type callback function (StampCallback). */ static void extract_cryptomatte_meta_data(void *_data, const char *propname, diff --git a/source/blender/compositor/intern/COM_Node.cc b/source/blender/compositor/intern/COM_Node.cc index 7acf91df7d4..58b2b195579 100644 --- a/source/blender/compositor/intern/COM_Node.cc +++ b/source/blender/compositor/intern/COM_Node.cc @@ -28,14 +28,14 @@ namespace blender::compositor { **** Node **** **************/ -Node::Node(bNode *editorNode, bool create_sockets) - : editorNodeTree_(nullptr), - editorNode_(editorNode), - inActiveGroup_(false), - instanceKey_(NODE_INSTANCE_KEY_NONE) +Node::Node(bNode *editor_node, bool create_sockets) + : editor_node_tree_(nullptr), + editor_node_(editor_node), + in_active_group_(false), + instance_key_(NODE_INSTANCE_KEY_NONE) { if (create_sockets) { - bNodeSocket *input = (bNodeSocket *)editorNode->inputs.first; + bNodeSocket *input = (bNodeSocket *)editor_node->inputs.first; while (input != nullptr) { DataType dt = DataType::Value; if (input->type == SOCK_RGBA) { @@ -45,10 +45,10 @@ Node::Node(bNode *editorNode, bool create_sockets) dt = DataType::Vector; } - this->addInputSocket(dt, input); + this->add_input_socket(dt, input); input = input->next; } - bNodeSocket *output = (bNodeSocket *)editorNode->outputs.first; + bNodeSocket *output = (bNodeSocket *)editor_node->outputs.first; while (output != nullptr) { DataType dt = DataType::Value; if (output->type == SOCK_RGBA) { @@ -58,7 +58,7 @@ Node::Node(bNode *editorNode, bool create_sockets) dt = DataType::Vector; } - this->addOutputSocket(dt, output); + this->add_output_socket(dt, output); output = output->next; } } @@ -74,43 +74,43 @@ Node::~Node() } } -void Node::addInputSocket(DataType datatype) +void Node::add_input_socket(DataType datatype) { - this->addInputSocket(datatype, nullptr); + this->add_input_socket(datatype, nullptr); } -void Node::addInputSocket(DataType datatype, bNodeSocket *bSocket) +void Node::add_input_socket(DataType datatype, bNodeSocket *bSocket) { NodeInput *socket = new NodeInput(this, bSocket, datatype); this->inputs.append(socket); } -void Node::addOutputSocket(DataType datatype) +void Node::add_output_socket(DataType datatype) { - this->addOutputSocket(datatype, nullptr); + this->add_output_socket(datatype, nullptr); } -void Node::addOutputSocket(DataType datatype, bNodeSocket *bSocket) +void Node::add_output_socket(DataType datatype, bNodeSocket *bSocket) { NodeOutput *socket = new NodeOutput(this, bSocket, datatype); outputs.append(socket); } -NodeOutput *Node::getOutputSocket(unsigned int index) const +NodeOutput *Node::get_output_socket(unsigned int index) const { return outputs[index]; } -NodeInput *Node::getInputSocket(unsigned int index) const +NodeInput *Node::get_input_socket(unsigned int index) const { return inputs[index]; } -bNodeSocket *Node::getEditorInputSocket(int editorNodeInputSocketIndex) +bNodeSocket *Node::get_editor_input_socket(int editor_node_input_socket_index) { - bNodeSocket *bSock = (bNodeSocket *)this->getbNode()->inputs.first; + bNodeSocket *bSock = (bNodeSocket *)this->get_bnode()->inputs.first; int index = 0; while (bSock != nullptr) { - if (index == editorNodeInputSocketIndex) { + if (index == editor_node_input_socket_index) { return bSock; } index++; @@ -118,12 +118,12 @@ bNodeSocket *Node::getEditorInputSocket(int editorNodeInputSocketIndex) } return nullptr; } -bNodeSocket *Node::getEditorOutputSocket(int editorNodeOutputSocketIndex) +bNodeSocket *Node::get_editor_output_socket(int editor_node_output_socket_index) { - bNodeSocket *bSock = (bNodeSocket *)this->getbNode()->outputs.first; + bNodeSocket *bSock = (bNodeSocket *)this->get_bnode()->outputs.first; int index = 0; while (bSock != nullptr) { - if (index == editorNodeOutputSocketIndex) { + if (index == editor_node_output_socket_index) { return bSock; } index++; @@ -137,33 +137,33 @@ bNodeSocket *Node::getEditorOutputSocket(int editorNodeOutputSocketIndex) *******************/ NodeInput::NodeInput(Node *node, bNodeSocket *b_socket, DataType datatype) - : node_(node), editorSocket_(b_socket), datatype_(datatype), link_(nullptr) + : node_(node), editor_socket_(b_socket), datatype_(datatype), link_(nullptr) { } -void NodeInput::setLink(NodeOutput *link) +void NodeInput::set_link(NodeOutput *link) { link_ = link; } -float NodeInput::getEditorValueFloat() const +float NodeInput::get_editor_value_float() const { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get(&ptr, "default_value"); } -void NodeInput::getEditorValueColor(float *value) const +void NodeInput::get_editor_value_color(float *value) const { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get_array(&ptr, "default_value", value); } -void NodeInput::getEditorValueVector(float *value) const +void NodeInput::get_editor_value_vector(float *value) const { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get_array(&ptr, "default_value", value); } @@ -172,28 +172,28 @@ void NodeInput::getEditorValueVector(float *value) const ********************/ NodeOutput::NodeOutput(Node *node, bNodeSocket *b_socket, DataType datatype) - : node_(node), editorSocket_(b_socket), datatype_(datatype) + : node_(node), editor_socket_(b_socket), datatype_(datatype) { } -float NodeOutput::getEditorValueFloat() +float NodeOutput::get_editor_value_float() { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get(&ptr, "default_value"); } -void NodeOutput::getEditorValueColor(float *value) +void NodeOutput::get_editor_value_color(float *value) { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get_array(&ptr, "default_value", value); } -void NodeOutput::getEditorValueVector(float *value) +void NodeOutput::get_editor_value_vector(float *value) { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get_array(&ptr, "default_value", value); } diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h index 3d9c62aca67..41283b821bb 100644 --- a/source/blender/compositor/intern/COM_Node.h +++ b/source/blender/compositor/intern/COM_Node.h @@ -41,22 +41,22 @@ class Node { /** * \brief stores the reference to the SDNA bNode struct */ - bNodeTree *editorNodeTree_; + bNodeTree *editor_node_tree_; /** * \brief stores the reference to the SDNA bNode struct */ - bNode *editorNode_; + bNode *editor_node_; /** * \brief Is this node part of the active group */ - bool inActiveGroup_; + bool in_active_group_; /** * \brief Instance key to identify the node in an instance hash table */ - bNodeInstanceKey instanceKey_; + bNodeInstanceKey instance_key_; protected: /** @@ -70,23 +70,23 @@ class Node { Vector outputs; public: - Node(bNode *editorNode, bool create_sockets = true); + Node(bNode *editor_node, bool create_sockets = true); virtual ~Node(); /** * \brief get the reference to the SDNA bNode struct */ - bNode *getbNode() const + bNode *get_bnode() const { - return editorNode_; + return editor_node_; } /** * \brief get the reference to the SDNA bNodeTree struct */ - bNodeTree *getbNodeTree() const + bNodeTree *get_bnodetree() const { - return editorNodeTree_; + return editor_node_tree_; } /** @@ -95,24 +95,24 @@ class Node { * node for highlight during execution. * \param bNode: */ - void setbNode(bNode *node) + void set_bnode(bNode *node) { - editorNode_ = node; + editor_node_ = node; } /** * \brief set the reference to the bNodeTree * \param bNodeTree: */ - void setbNodeTree(bNodeTree *nodetree) + void set_bnodetree(bNodeTree *nodetree) { - editorNodeTree_ = nodetree; + editor_node_tree_ = nodetree; } /** * \brief get access to the vector of input sockets */ - const Vector &getInputSockets() const + const Vector &get_input_sockets() const { return this->inputs; } @@ -120,7 +120,7 @@ class Node { /** * \brief get access to the vector of input sockets */ - const Vector &getOutputSockets() const + const Vector &get_output_sockets() const { return this->outputs; } @@ -130,22 +130,22 @@ class Node { * \param index: * the index of the needed outputsocket */ - NodeOutput *getOutputSocket(const unsigned int index = 0) const; + NodeOutput *get_output_socket(const unsigned int index = 0) const; /** * get the reference to a certain inputsocket * \param index: * the index of the needed inputsocket */ - NodeInput *getInputSocket(const unsigned int index) const; + NodeInput *get_input_socket(const unsigned int index) const; /** * \brief Is this node in the active group (the group that is being edited) - * \param isInActiveGroup: + * \param is_in_active_group: */ - void setIsInActiveGroup(bool value) + void set_is_in_active_group(bool value) { - inActiveGroup_ = value; + in_active_group_ = value; } /** @@ -154,9 +154,9 @@ class Node { * the active group will be the main tree (all nodes that are not part of a group will be active) * \return bool [false:true] */ - inline bool isInActiveGroup() const + inline bool is_in_active_group() const { - return inActiveGroup_; + return in_active_group_; } /** @@ -167,16 +167,16 @@ class Node { * \param system: the ExecutionSystem where the operations need to be added * \param context: reference to the CompositorContext */ - virtual void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const = 0; + virtual void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const = 0; - void setInstanceKey(bNodeInstanceKey instance_key) + void set_instance_key(bNodeInstanceKey instance_key) { - instanceKey_ = instance_key; + instance_key_ = instance_key; } - bNodeInstanceKey getInstanceKey() const + bNodeInstanceKey get_instance_key() const { - return instanceKey_; + return instance_key_; } protected: @@ -185,19 +185,19 @@ class Node { * \note may only be called in an constructor * \param socket: the NodeInput to add */ - void addInputSocket(DataType datatype); - void addInputSocket(DataType datatype, bNodeSocket *socket); + void add_input_socket(DataType datatype); + void add_input_socket(DataType datatype, bNodeSocket *socket); /** * \brief add an NodeOutput to the collection of output-sockets * \note may only be called in an constructor * \param socket: the NodeOutput to add */ - void addOutputSocket(DataType datatype); - void addOutputSocket(DataType datatype, bNodeSocket *socket); + void add_output_socket(DataType datatype); + void add_output_socket(DataType datatype, bNodeSocket *socket); - bNodeSocket *getEditorInputSocket(int editorNodeInputSocketIndex); - bNodeSocket *getEditorOutputSocket(int editorNodeOutputSocketIndex); + bNodeSocket *get_editor_input_socket(int editor_node_input_socket_index); + bNodeSocket *get_editor_output_socket(int editor_node_output_socket_index); }; /** @@ -207,7 +207,7 @@ class Node { class NodeInput { private: Node *node_; - bNodeSocket *editorSocket_; + bNodeSocket *editor_socket_; DataType datatype_; @@ -220,32 +220,32 @@ class NodeInput { public: NodeInput(Node *node, bNodeSocket *b_socket, DataType datatype); - Node *getNode() const + Node *get_node() const { return node_; } - DataType getDataType() const + DataType get_data_type() const { return datatype_; } - bNodeSocket *getbNodeSocket() const + bNodeSocket *get_bnode_socket() const { - return editorSocket_; + return editor_socket_; } - void setLink(NodeOutput *link); - bool isLinked() const + void set_link(NodeOutput *link); + bool is_linked() const { return link_; } - NodeOutput *getLink() + NodeOutput *get_link() { return link_; } - float getEditorValueFloat() const; - void getEditorValueColor(float *value) const; - void getEditorValueVector(float *value) const; + float get_editor_value_float() const; + void get_editor_value_color(float *value) const; + void get_editor_value_vector(float *value) const; }; /** @@ -255,29 +255,29 @@ class NodeInput { class NodeOutput { private: Node *node_; - bNodeSocket *editorSocket_; + bNodeSocket *editor_socket_; DataType datatype_; public: NodeOutput(Node *node, bNodeSocket *b_socket, DataType datatype); - Node *getNode() const + Node *get_node() const { return node_; } - DataType getDataType() const + DataType get_data_type() const { return datatype_; } - bNodeSocket *getbNodeSocket() const + bNodeSocket *get_bnode_socket() const { - return editorSocket_; + return editor_socket_; } - float getEditorValueFloat(); - void getEditorValueColor(float *value); - void getEditorValueVector(float *value); + float get_editor_value_float(); + void get_editor_value_color(float *value); + void get_editor_value_vector(float *value); }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_NodeConverter.cc b/source/blender/compositor/intern/COM_NodeConverter.cc index 314b5e9572a..93ec559533b 100644 --- a/source/blender/compositor/intern/COM_NodeConverter.cc +++ b/source/blender/compositor/intern/COM_NodeConverter.cc @@ -33,127 +33,128 @@ NodeConverter::NodeConverter(NodeOperationBuilder *builder) : builder_(builder) { } -void NodeConverter::addOperation(NodeOperation *operation) +void NodeConverter::add_operation(NodeOperation *operation) { - builder_->addOperation(operation); + builder_->add_operation(operation); } -void NodeConverter::mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket) +void NodeConverter::map_input_socket(NodeInput *node_socket, NodeOperationInput *operation_socket) { - builder_->mapInputSocket(node_socket, operation_socket); + builder_->map_input_socket(node_socket, operation_socket); } -void NodeConverter::mapOutputSocket(NodeOutput *node_socket, NodeOperationOutput *operation_socket) +void NodeConverter::map_output_socket(NodeOutput *node_socket, + NodeOperationOutput *operation_socket) { - builder_->mapOutputSocket(node_socket, operation_socket); + builder_->map_output_socket(node_socket, operation_socket); } -void NodeConverter::addLink(NodeOperationOutput *from, NodeOperationInput *to) +void NodeConverter::add_link(NodeOperationOutput *from, NodeOperationInput *to) { - builder_->addLink(from, to); + builder_->add_link(from, to); } -void NodeConverter::addPreview(NodeOperationOutput *output) +void NodeConverter::add_preview(NodeOperationOutput *output) { - builder_->addPreview(output); + builder_->add_preview(output); } -void NodeConverter::addNodeInputPreview(NodeInput *input) +void NodeConverter::add_node_input_preview(NodeInput *input) { - builder_->addNodeInputPreview(input); + builder_->add_node_input_preview(input); } -NodeOperation *NodeConverter::setInvalidOutput(NodeOutput *output) +NodeOperation *NodeConverter::set_invalid_output(NodeOutput *output) { /* this is a really bad situation - bring on the pink! - so artists know this is bad */ const float warning_color[4] = {1.0f, 0.0f, 1.0f, 1.0f}; SetColorOperation *operation = new SetColorOperation(); - operation->setChannels(warning_color); + operation->set_channels(warning_color); - builder_->addOperation(operation); - builder_->mapOutputSocket(output, operation->getOutputSocket()); + builder_->add_operation(operation); + builder_->map_output_socket(output, operation->get_output_socket()); return operation; } -NodeOperationOutput *NodeConverter::addInputProxy(NodeInput *input, bool use_conversion) +NodeOperationOutput *NodeConverter::add_input_proxy(NodeInput *input, bool use_conversion) { - SocketProxyOperation *proxy = new SocketProxyOperation(input->getDataType(), use_conversion); - builder_->addOperation(proxy); + SocketProxyOperation *proxy = new SocketProxyOperation(input->get_data_type(), use_conversion); + builder_->add_operation(proxy); - builder_->mapInputSocket(input, proxy->getInputSocket(0)); + builder_->map_input_socket(input, proxy->get_input_socket(0)); - return proxy->getOutputSocket(); + return proxy->get_output_socket(); } -NodeOperationInput *NodeConverter::addOutputProxy(NodeOutput *output, bool use_conversion) +NodeOperationInput *NodeConverter::add_output_proxy(NodeOutput *output, bool use_conversion) { - SocketProxyOperation *proxy = new SocketProxyOperation(output->getDataType(), use_conversion); - builder_->addOperation(proxy); + SocketProxyOperation *proxy = new SocketProxyOperation(output->get_data_type(), use_conversion); + builder_->add_operation(proxy); - builder_->mapOutputSocket(output, proxy->getOutputSocket()); + builder_->map_output_socket(output, proxy->get_output_socket()); - return proxy->getInputSocket(0); + return proxy->get_input_socket(0); } -void NodeConverter::addInputValue(NodeOperationInput *input, float value) +void NodeConverter::add_input_value(NodeOperationInput *input, float value) { SetValueOperation *operation = new SetValueOperation(); - operation->setValue(value); + operation->set_value(value); - builder_->addOperation(operation); - builder_->addLink(operation->getOutputSocket(), input); + builder_->add_operation(operation); + builder_->add_link(operation->get_output_socket(), input); } -void NodeConverter::addInputColor(NodeOperationInput *input, const float value[4]) +void NodeConverter::add_input_color(NodeOperationInput *input, const float value[4]) { SetColorOperation *operation = new SetColorOperation(); - operation->setChannels(value); + operation->set_channels(value); - builder_->addOperation(operation); - builder_->addLink(operation->getOutputSocket(), input); + builder_->add_operation(operation); + builder_->add_link(operation->get_output_socket(), input); } -void NodeConverter::addInputVector(NodeOperationInput *input, const float value[3]) +void NodeConverter::add_input_vector(NodeOperationInput *input, const float value[3]) { SetVectorOperation *operation = new SetVectorOperation(); - operation->setVector(value); + operation->set_vector(value); - builder_->addOperation(operation); - builder_->addLink(operation->getOutputSocket(), input); + builder_->add_operation(operation); + builder_->add_link(operation->get_output_socket(), input); } -void NodeConverter::addOutputValue(NodeOutput *output, float value) +void NodeConverter::add_output_value(NodeOutput *output, float value) { SetValueOperation *operation = new SetValueOperation(); - operation->setValue(value); + operation->set_value(value); - builder_->addOperation(operation); - builder_->mapOutputSocket(output, operation->getOutputSocket()); + builder_->add_operation(operation); + builder_->map_output_socket(output, operation->get_output_socket()); } -void NodeConverter::addOutputColor(NodeOutput *output, const float value[4]) +void NodeConverter::add_output_color(NodeOutput *output, const float value[4]) { SetColorOperation *operation = new SetColorOperation(); - operation->setChannels(value); + operation->set_channels(value); - builder_->addOperation(operation); - builder_->mapOutputSocket(output, operation->getOutputSocket()); + builder_->add_operation(operation); + builder_->map_output_socket(output, operation->get_output_socket()); } -void NodeConverter::addOutputVector(NodeOutput *output, const float value[3]) +void NodeConverter::add_output_vector(NodeOutput *output, const float value[3]) { SetVectorOperation *operation = new SetVectorOperation(); - operation->setVector(value); + operation->set_vector(value); - builder_->addOperation(operation); - builder_->mapOutputSocket(output, operation->getOutputSocket()); + builder_->add_operation(operation); + builder_->map_output_socket(output, operation->get_output_socket()); } -void NodeConverter::registerViewer(ViewerOperation *viewer) +void NodeConverter::register_viewer(ViewerOperation *viewer) { - builder_->registerViewer(viewer); + builder_->register_viewer(viewer); } ViewerOperation *NodeConverter::active_viewer() const diff --git a/source/blender/compositor/intern/COM_NodeConverter.h b/source/blender/compositor/intern/COM_NodeConverter.h index afbd53fa67d..9193a28a77f 100644 --- a/source/blender/compositor/intern/COM_NodeConverter.h +++ b/source/blender/compositor/intern/COM_NodeConverter.h @@ -36,7 +36,7 @@ class ViewerOperation; /** * Interface type for converting a \a Node into \a NodeOperation. - * This is passed to \a Node::convertToOperation methods and allows them + * This is passed to \a Node::convert_to_operation methods and allows them * to register any number of operations, create links between them, * and map original node sockets to their inputs or outputs. */ @@ -48,7 +48,7 @@ class NodeConverter { * Insert a new operation into the operations graph. * The operation must be created by the node. */ - void addOperation(NodeOperation *operation); + void add_operation(NodeOperation *operation); /** * Map input socket of the node to an operation socket. @@ -57,7 +57,7 @@ class NodeConverter { * * \note A \a Node input can be mapped to multiple \a NodeOperation inputs. */ - void mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket); + void map_input_socket(NodeInput *node_socket, NodeOperationInput *operation_socket); /** * Map output socket of the node to an operation socket. * Links between nodes will then generate equivalent links between @@ -66,51 +66,51 @@ class NodeConverter { * \note A \a Node output can only be mapped to one \a NodeOperation output. * Any existing operation output mapping will be replaced. */ - void mapOutputSocket(NodeOutput *node_socket, NodeOperationOutput *operation_socket); + void map_output_socket(NodeOutput *node_socket, NodeOperationOutput *operation_socket); /** * Create a proxy operation for a node input. * This operation will be removed later and replaced * by direct links between the connected operations. */ - NodeOperationOutput *addInputProxy(NodeInput *input, bool use_conversion); + NodeOperationOutput *add_input_proxy(NodeInput *input, bool use_conversion); /** * Create a proxy operation for a node output. * This operation will be removed later and replaced * by direct links between the connected operations. */ - NodeOperationInput *addOutputProxy(NodeOutput *output, bool use_conversion); + NodeOperationInput *add_output_proxy(NodeOutput *output, bool use_conversion); /** Define a constant input value. */ - void addInputValue(NodeOperationInput *input, float value); + void add_input_value(NodeOperationInput *input, float value); /** Define a constant input color. */ - void addInputColor(NodeOperationInput *input, const float value[4]); + void add_input_color(NodeOperationInput *input, const float value[4]); /** Define a constant input vector. */ - void addInputVector(NodeOperationInput *input, const float value[3]); + void add_input_vector(NodeOperationInput *input, const float value[3]); /** Define a constant output value. */ - void addOutputValue(NodeOutput *output, float value); + void add_output_value(NodeOutput *output, float value); /** Define a constant output color. */ - void addOutputColor(NodeOutput *output, const float value[4]); + void add_output_color(NodeOutput *output, const float value[4]); /** Define a constant output vector. */ - void addOutputVector(NodeOutput *output, const float value[3]); + void add_output_vector(NodeOutput *output, const float value[3]); /** Add an explicit link between two operations. */ - void addLink(NodeOperationOutput *from, NodeOperationInput *to); + void add_link(NodeOperationOutput *from, NodeOperationInput *to); /** Add a preview operation for a operation output. */ - void addPreview(NodeOperationOutput *output); + void add_preview(NodeOperationOutput *output); /** Add a preview operation for a node input. */ - void addNodeInputPreview(NodeInput *input); + void add_node_input_preview(NodeInput *input); /** * When a node has no valid data * \note missing image / group pointer, or missing renderlayer from EXR */ - NodeOperation *setInvalidOutput(NodeOutput *output); + NodeOperation *set_invalid_output(NodeOutput *output); /** Define a viewer operation as the active output, if possible */ - void registerViewer(ViewerOperation *viewer); + void register_viewer(ViewerOperation *viewer); /** The currently active viewer output operation */ ViewerOperation *active_viewer() const; diff --git a/source/blender/compositor/intern/COM_NodeGraph.cc b/source/blender/compositor/intern/COM_NodeGraph.cc index a06c9349bb7..64684b772d9 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.cc +++ b/source/blender/compositor/intern/COM_NodeGraph.cc @@ -71,21 +71,21 @@ void NodeGraph::add_node(Node *node, bNodeInstanceKey key, bool is_active_group) { - node->setbNodeTree(b_ntree); - node->setInstanceKey(key); - node->setIsInActiveGroup(is_active_group); + node->set_bnodetree(b_ntree); + node->set_instance_key(key); + node->set_is_in_active_group(is_active_group); nodes_.append(node); DebugInfo::node_added(node); } -void NodeGraph::add_link(NodeOutput *fromSocket, NodeInput *toSocket) +void NodeGraph::add_link(NodeOutput *from_socket, NodeInput *to_socket) { - links_.append(Link(fromSocket, toSocket)); + links_.append(Link(from_socket, to_socket)); /* register with the input */ - toSocket->setLink(fromSocket); + to_socket->set_link(from_socket); } void NodeGraph::add_bNodeTree(const CompositorContext &context, @@ -93,7 +93,7 @@ void NodeGraph::add_bNodeTree(const CompositorContext &context, bNodeTree *tree, bNodeInstanceKey parent_key) { - const bNodeTree *basetree = context.getbNodeTree(); + const bNodeTree *basetree = context.get_bnodetree(); /* Update viewers in the active edit-tree as well the base tree (for backdrop). */ bool is_active_group = (parent_key.value == basetree->active_viewer_key.value); @@ -124,7 +124,7 @@ void NodeGraph::add_bNode(const CompositorContext &context, } /* replace slow nodes with proxies for fast execution */ - if (context.isFastCalculation() && !COM_bnode_is_fast_node(*b_node)) { + if (context.is_fast_calculation() && !COM_bnode_is_fast_node(*b_node)) { add_proxies_skip(b_ntree, b_node, key, is_active_group); return; } @@ -149,8 +149,8 @@ NodeOutput *NodeGraph::find_output(const NodeRange &node_range, bNodeSocket *b_s { for (Vector::iterator it = node_range.first; it != node_range.second; ++it) { Node *node = *it; - for (NodeOutput *output : node->getOutputSockets()) { - if (output->getbNodeSocket() == b_socket) { + for (NodeOutput *output : node->get_output_sockets()) { + if (output->get_bnode_socket() == b_socket) { return output; } } @@ -180,8 +180,8 @@ void NodeGraph::add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink for (Vector::iterator it = node_range.first; it != node_range.second; ++it) { Node *node = *it; - for (NodeInput *input : node->getInputSockets()) { - if (input->getbNodeSocket() == b_nodelink->tosock && !input->isLinked()) { + for (NodeInput *input : node->get_input_sockets()) { + if (input->get_bnode_socket() == b_nodelink->tosock && !input->is_linked()) { add_link(output, input); } } @@ -258,7 +258,7 @@ void NodeGraph::add_proxies_group_outputs(const CompositorContext &context, b_sock_io = b_sock_io->next) { bNodeSocket *b_sock_group = find_b_node_output(b_node, b_sock_io->identifier); if (b_sock_group) { - if (context.isGroupnodeBufferEnabled() && + if (context.is_groupnode_buffer_enabled() && context.get_execution_model() == eExecutionModel::Tiled) { SocketBufferNode *buffer = new SocketBufferNode(b_node_io, b_sock_io, b_sock_group); add_node(buffer, b_group_tree, key, is_active_group); @@ -279,7 +279,7 @@ void NodeGraph::add_proxies_group(const CompositorContext &context, /* missing node group datablock can happen with library linking */ if (!b_group_tree) { - /* This error case its handled in convertToOperations() + /* This error case its handled in convert_to_operations() * so we don't get un-converted sockets. */ return; } diff --git a/source/blender/compositor/intern/COM_NodeGraph.h b/source/blender/compositor/intern/COM_NodeGraph.h index cc628ebb724..476b99033c9 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.h +++ b/source/blender/compositor/intern/COM_NodeGraph.h @@ -71,7 +71,7 @@ class NodeGraph { static bNodeSocket *find_b_node_output(bNode *b_node, const char *identifier); void add_node(Node *node, bNodeTree *b_ntree, bNodeInstanceKey key, bool is_active_group); - void add_link(NodeOutput *fromSocket, NodeInput *toSocket); + void add_link(NodeOutput *from_socket, NodeInput *to_socket); void add_bNodeTree(const CompositorContext &context, int nodes_start, diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 0bfc088e4bf..41c645b9eea 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -40,7 +40,7 @@ NodeOperation::NodeOperation() /** Get constant value when operation is constant, otherwise return default_value. */ float NodeOperation::get_constant_value_default(float default_value) { - BLI_assert(outputs_.size() > 0 && getOutputSocket()->getDataType() == DataType::Value); + BLI_assert(outputs_.size() > 0 && get_output_socket()->get_data_type() == DataType::Value); return *get_constant_elem_default(&default_value); } @@ -74,23 +74,23 @@ std::optional NodeOperation::generate_hash() hash_params(canvas_.ymin, canvas_.ymax); if (outputs_.size() > 0) { BLI_assert(outputs_.size() == 1); - hash_param(this->getOutputSocket()->getDataType()); + hash_param(this->get_output_socket()->get_data_type()); } NodeOperationHash hash; hash.params_hash_ = params_hash_; hash.parents_hash_ = 0; for (NodeOperationInput &socket : inputs_) { - if (!socket.isConnected()) { + if (!socket.is_connected()) { continue; } - NodeOperation &input = socket.getLink()->getOperation(); + NodeOperation &input = socket.get_link()->get_operation(); const bool is_constant = input.get_flags().is_constant_operation; combine_hashes(hash.parents_hash_, get_default_hash(is_constant)); if (is_constant) { const float *elem = ((ConstantOperation *)&input)->get_constant_elem(); - const int num_channels = COM_data_type_num_channels(socket.getDataType()); + const int num_channels = COM_data_type_num_channels(socket.get_data_type()); for (const int i : IndexRange(num_channels)) { combine_hashes(hash.parents_hash_, get_default_hash(elem[i])); } @@ -106,22 +106,22 @@ std::optional NodeOperation::generate_hash() return hash; } -NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index) +NodeOperationOutput *NodeOperation::get_output_socket(unsigned int index) { return &outputs_[index]; } -NodeOperationInput *NodeOperation::getInputSocket(unsigned int index) +NodeOperationInput *NodeOperation::get_input_socket(unsigned int index) { return &inputs_[index]; } -void NodeOperation::addInputSocket(DataType datatype, ResizeMode resize_mode) +void NodeOperation::add_input_socket(DataType datatype, ResizeMode resize_mode) { inputs_.append(NodeOperationInput(this, datatype, resize_mode)); } -void NodeOperation::addOutputSocket(DataType datatype) +void NodeOperation::add_output_socket(DataType datatype) { outputs_.append(NodeOperationOutput(this, datatype)); } @@ -157,7 +157,7 @@ void NodeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) continue; } NodeOperationInput &input = inputs_[index]; - if (input.isConnected()) { + if (input.is_connected()) { input.determine_canvas(local_preferred_area, unused_area); } } @@ -172,32 +172,32 @@ void NodeOperation::init_data() { /* Pass. */ } -void NodeOperation::initExecution() +void NodeOperation::init_execution() { /* pass */ } -void NodeOperation::initMutex() +void NodeOperation::init_mutex() { BLI_mutex_init(&mutex_); } -void NodeOperation::lockMutex() +void NodeOperation::lock_mutex() { BLI_mutex_lock(&mutex_); } -void NodeOperation::unlockMutex() +void NodeOperation::unlock_mutex() { BLI_mutex_unlock(&mutex_); } -void NodeOperation::deinitMutex() +void NodeOperation::deinit_mutex() { BLI_mutex_end(&mutex_); } -void NodeOperation::deinitExecution() +void NodeOperation::deinit_execution() { /* pass */ } @@ -223,48 +223,48 @@ void NodeOperation::unset_canvas() flags.is_canvas_set = false; } -SocketReader *NodeOperation::getInputSocketReader(unsigned int inputSocketIndex) +SocketReader *NodeOperation::get_input_socket_reader(unsigned int index) { - return this->getInputSocket(inputSocketIndex)->getReader(); + return this->get_input_socket(index)->get_reader(); } -NodeOperation *NodeOperation::getInputOperation(unsigned int inputSocketIndex) +NodeOperation *NodeOperation::get_input_operation(int index) { - NodeOperationInput *input = getInputSocket(inputSocketIndex); - if (input && input->isConnected()) { - return &input->getLink()->getOperation(); + NodeOperationInput *input = get_input_socket(index); + if (input && input->is_connected()) { + return &input->get_link()->get_operation(); } return nullptr; } -bool NodeOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool NodeOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { if (inputs_.size() == 0) { BLI_rcti_init(output, input->xmin, input->xmax, input->ymin, input->ymax); return false; } - rcti tempOutput; + rcti temp_output; bool first = true; - for (int i = 0; i < getNumberOfInputSockets(); i++) { - NodeOperation *inputOperation = this->getInputOperation(i); - if (inputOperation && - inputOperation->determineDependingAreaOfInterest(input, readOperation, &tempOutput)) { + for (int i = 0; i < get_number_of_input_sockets(); i++) { + NodeOperation *input_operation = this->get_input_operation(i); + if (input_operation && input_operation->determine_depending_area_of_interest( + input, read_operation, &temp_output)) { if (first) { - output->xmin = tempOutput.xmin; - output->ymin = tempOutput.ymin; - output->xmax = tempOutput.xmax; - output->ymax = tempOutput.ymax; + output->xmin = temp_output.xmin; + output->ymin = temp_output.ymin; + output->xmax = temp_output.xmax; + output->ymax = temp_output.ymax; first = false; } else { - output->xmin = MIN2(output->xmin, tempOutput.xmin); - output->ymin = MIN2(output->ymin, tempOutput.ymin); - output->xmax = MAX2(output->xmax, tempOutput.xmax); - output->ymax = MAX2(output->ymax, tempOutput.ymax); + output->xmin = MIN2(output->xmin, temp_output.xmin); + output->ymin = MIN2(output->ymin, temp_output.ymin); + output->xmax = MAX2(output->xmax, temp_output.xmax); + output->ymax = MAX2(output->ymax, temp_output.ymax); } } } @@ -297,7 +297,7 @@ void NodeOperation::get_area_of_interest(const int input_idx, else { /* Non full-frame operations never implement this method. To ensure correctness assume * whole area is used. */ - NodeOperation *input_op = getInputOperation(input_idx); + NodeOperation *input_op = get_input_operation(input_idx); r_input_area = input_op->get_canvas(); } } @@ -306,8 +306,8 @@ void NodeOperation::get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area) { - for (int i = 0; i < getNumberOfInputSockets(); i++) { - if (input_op == getInputOperation(i)) { + for (int i = 0; i < get_number_of_input_sockets(); i++) { + if (input_op == get_input_operation(i)) { get_area_of_interest(i, output_area, r_input_area); return; } @@ -340,11 +340,11 @@ void NodeOperation::render_full_frame(MemoryBuffer *output_buf, Span areas, Span inputs_bufs) { - initExecution(); + init_execution(); for (const rcti &area : areas) { update_memory_buffer(output_buf, area, inputs_bufs); } - deinitExecution(); + deinit_execution(); } /** @@ -356,18 +356,18 @@ void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, { Vector orig_input_links = replace_inputs_with_buffers(inputs_bufs); - initExecution(); - const bool is_output_operation = getNumberOfOutputSockets() == 0; + init_execution(); + const bool is_output_operation = get_number_of_output_sockets() == 0; if (!is_output_operation && output_buf->is_a_single_elem()) { float *output_elem = output_buf->get_elem(0, 0); - readSampled(output_elem, 0, 0, PixelSampler::Nearest); + read_sampled(output_elem, 0, 0, PixelSampler::Nearest); } else { for (const rcti &rect : areas) { exec_system_->execute_work(rect, [=](const rcti &split_rect) { rcti tile_rect = split_rect; if (is_output_operation) { - executeRegion(&tile_rect, 0); + execute_region(&tile_rect, 0); } else { render_tile(output_buf, &tile_rect); @@ -375,7 +375,7 @@ void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, }); } } - deinitExecution(); + deinit_execution(); remove_buffers_and_restore_original_inputs(orig_input_links); } @@ -383,7 +383,7 @@ void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect) { const bool is_complex = get_flags().complex; - void *tile_data = is_complex ? initializeTileData(tile_rect) : nullptr; + void *tile_data = is_complex ? initialize_tile_data(tile_rect) : nullptr; const int elem_stride = output_buf->elem_stride; for (int y = tile_rect->ymin; y < tile_rect->ymax; y++) { float *output_elem = output_buf->get_elem(tile_rect->xmin, y); @@ -395,13 +395,13 @@ void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect) } else { for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) { - readSampled(output_elem, x, y, PixelSampler::Nearest); + read_sampled(output_elem, x, y, PixelSampler::Nearest); output_elem += elem_stride; } } } if (tile_data) { - deinitializeTileData(tile_rect, tile_data); + deinitialize_tile_data(tile_rect, tile_data); } } @@ -411,14 +411,15 @@ void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect) Vector NodeOperation::replace_inputs_with_buffers( Span inputs_bufs) { - BLI_assert(inputs_bufs.size() == getNumberOfInputSockets()); + BLI_assert(inputs_bufs.size() == get_number_of_input_sockets()); Vector orig_links(inputs_bufs.size()); for (int i = 0; i < inputs_bufs.size(); i++) { - NodeOperationInput *input_socket = getInputSocket(i); - BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], input_socket->getDataType()); - orig_links[i] = input_socket->getLink(); - input_socket->setLink(buffer_op->getOutputSocket()); - buffer_op->initExecution(); + NodeOperationInput *input_socket = get_input_socket(i); + BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], + input_socket->get_data_type()); + orig_links[i] = input_socket->get_link(); + input_socket->set_link(buffer_op->get_output_socket()); + buffer_op->init_execution(); } return orig_links; } @@ -426,14 +427,14 @@ Vector NodeOperation::replace_inputs_with_buffers( void NodeOperation::remove_buffers_and_restore_original_inputs( Span original_inputs_links) { - BLI_assert(original_inputs_links.size() == getNumberOfInputSockets()); + BLI_assert(original_inputs_links.size() == get_number_of_input_sockets()); for (int i = 0; i < original_inputs_links.size(); i++) { NodeOperation *buffer_op = get_input_operation(i); BLI_assert(buffer_op != nullptr); BLI_assert(typeid(*buffer_op) == typeid(BufferOperation)); - buffer_op->deinitExecution(); - NodeOperationInput *input_socket = getInputSocket(i); - input_socket->setLink(original_inputs_links[i]); + buffer_op->deinit_execution(); + NodeOperationInput *input_socket = get_input_socket(i); + input_socket->set_link(original_inputs_links[i]); delete buffer_op; } } @@ -444,15 +445,17 @@ void NodeOperation::remove_buffers_and_restore_original_inputs( **** OpInput **** *****************/ -NodeOperationInput::NodeOperationInput(NodeOperation *op, DataType datatype, ResizeMode resizeMode) - : operation_(op), datatype_(datatype), resizeMode_(resizeMode), link_(nullptr) +NodeOperationInput::NodeOperationInput(NodeOperation *op, + DataType datatype, + ResizeMode resize_mode) + : operation_(op), datatype_(datatype), resize_mode_(resize_mode), link_(nullptr) { } -SocketReader *NodeOperationInput::getReader() +SocketReader *NodeOperationInput::get_reader() { - if (isConnected()) { - return &link_->getOperation(); + if (is_connected()) { + return &link_->get_operation(); } return nullptr; @@ -481,7 +484,7 @@ NodeOperationOutput::NodeOperationOutput(NodeOperation *op, DataType datatype) void NodeOperationOutput::determine_canvas(const rcti &preferred_area, rcti &r_area) { - NodeOperation &operation = getOperation(); + NodeOperation &operation = get_operation(); if (operation.get_flags().is_canvas_set) { r_area = operation.get_canvas(); } @@ -558,9 +561,9 @@ std::ostream &operator<<(std::ostream &os, const NodeOperation &node_operation) os << ",flags={" << flags << "}"; if (flags.is_read_buffer_operation) { const ReadBufferOperation *read_operation = (const ReadBufferOperation *)&node_operation; - const MemoryProxy *proxy = read_operation->getMemoryProxy(); + const MemoryProxy *proxy = read_operation->get_memory_proxy(); if (proxy) { - const WriteBufferOperation *write_operation = proxy->getWriteBufferOperation(); + const WriteBufferOperation *write_operation = proxy->get_write_buffer_operation(); if (write_operation) { os << ",write=" << (NodeOperation &)*write_operation; } diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index af9d62b4276..3c172faca50 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -91,7 +91,7 @@ class NodeOperationInput { DataType datatype_; /** Resize mode of this socket */ - ResizeMode resizeMode_; + ResizeMode resize_mode_; /** Connected output */ NodeOperationOutput *link_; @@ -99,40 +99,40 @@ class NodeOperationInput { public: NodeOperationInput(NodeOperation *op, DataType datatype, - ResizeMode resizeMode = ResizeMode::Center); + ResizeMode resize_mode = ResizeMode::Center); - NodeOperation &getOperation() const + NodeOperation &get_operation() const { return *operation_; } - DataType getDataType() const + DataType get_data_type() const { return datatype_; } - void setLink(NodeOperationOutput *link) + void set_link(NodeOperationOutput *link) { link_ = link; } - NodeOperationOutput *getLink() const + NodeOperationOutput *get_link() const { return link_; } - bool isConnected() const + bool is_connected() const { return link_; } - void setResizeMode(ResizeMode resizeMode) + void set_resize_mode(ResizeMode resize_mode) { - resizeMode_ = resizeMode; + resize_mode_ = resize_mode; } - ResizeMode getResizeMode() const + ResizeMode get_resize_mode() const { - return resizeMode_; + return resize_mode_; } - SocketReader *getReader(); + SocketReader *get_reader(); bool determine_canvas(const rcti &preferred_area, rcti &r_area); @@ -153,11 +153,11 @@ class NodeOperationOutput { public: NodeOperationOutput(NodeOperation *op, DataType datatype); - NodeOperation &getOperation() const + NodeOperation &get_operation() const { return *operation_; } - DataType getDataType() const + DataType get_data_type() const { return datatype_; } @@ -334,9 +334,9 @@ class NodeOperation { * \note only use when you really know what you are doing. * this mutex is used to share data among chunks in the same operation * \see TonemapOperation for an example of usage - * \see NodeOperation.initMutex initializes this mutex - * \see NodeOperation.deinitMutex deinitializes this mutex - * \see NodeOperation.getMutex retrieve a pointer to this mutex. + * \see NodeOperation.init_mutex initializes this mutex + * \see NodeOperation.deinit_mutex deinitializes this mutex + * \see NodeOperation.get_mutex retrieve a pointer to this mutex. */ ThreadMutex mutex_; @@ -395,28 +395,23 @@ class NodeOperation { std::optional generate_hash(); - unsigned int getNumberOfInputSockets() const + unsigned int get_number_of_input_sockets() const { return inputs_.size(); } - unsigned int getNumberOfOutputSockets() const + unsigned int get_number_of_output_sockets() const { return outputs_.size(); } - NodeOperationOutput *getOutputSocket(unsigned int index = 0); - NodeOperationInput *getInputSocket(unsigned int index); + NodeOperationOutput *get_output_socket(unsigned int index = 0); + NodeOperationInput *get_input_socket(unsigned int index); - NodeOperation *get_input_operation(int index) - { - /* TODO: Rename protected getInputOperation to get_input_operation and make it public replacing - * this method. */ - return getInputOperation(index); - } + NodeOperation *get_input_operation(int index); virtual void determine_canvas(const rcti &preferred_area, rcti &r_area); /** - * \brief isOutputOperation determines whether this operation is an output of the + * \brief is_output_operation determines whether this operation is an output of the * ExecutionSystem during rendering or editing. * * Default behavior if not overridden, this operation will not be evaluated as being an output @@ -430,7 +425,7 @@ class NodeOperation { * * \return bool the result of this method */ - virtual bool isOutputOperation(bool /*rendering*/) const + virtual bool is_output_operation(bool /*rendering*/) const { return false; } @@ -440,7 +435,7 @@ class NodeOperation { execution_model_ = model; } - void setbNodeTree(const bNodeTree *tree) + void set_bnodetree(const bNodeTree *tree) { btree_ = tree; } @@ -452,20 +447,20 @@ class NodeOperation { /** * Initializes operation data needed after operations are linked and resolutions determined. For - * rendering heap memory data use initExecution(). + * rendering heap memory data use init_execution(). */ virtual void init_data(); - virtual void initExecution(); + virtual void init_execution(); /** * \brief when a chunk is executed by a CPUDevice, this method is called * \ingroup execution * \param rect: the rectangle of the chunk (location and size) - * \param chunkNumber: the chunkNumber to be calculated - * \param memoryBuffers: all input MemoryBuffer's needed + * \param chunk_number: the chunk_number to be calculated + * \param memory_buffers: all input MemoryBuffer's needed */ - virtual void executeRegion(rcti * /*rect*/, unsigned int /*chunkNumber*/) + virtual void execute_region(rcti * /*rect*/, unsigned int /*chunk_number*/) { } @@ -477,15 +472,15 @@ class NodeOperation { * \param program: the OpenCL program containing all compositor kernels * \param queue: the OpenCL command queue of the device the chunk is executed on * \param rect: the rectangle of the chunk (location and size) - * \param chunkNumber: the chunkNumber to be calculated - * \param memoryBuffers: all input MemoryBuffer's needed - * \param outputBuffer: the outputbuffer to write to + * \param chunk_number: the chunk_number to be calculated + * \param memory_buffers: all input MemoryBuffer's needed + * \param output_buffer: the outputbuffer to write to */ - virtual void executeOpenCLRegion(OpenCLDevice * /*device*/, - rcti * /*rect*/, - unsigned int /*chunkNumber*/, - MemoryBuffer ** /*memoryBuffers*/, - MemoryBuffer * /*outputBuffer*/) + virtual void execute_opencl_region(OpenCLDevice * /*device*/, + rcti * /*rect*/, + unsigned int /*chunk_number*/, + MemoryBuffer ** /*memory_buffers*/, + MemoryBuffer * /*output_buffer*/) { } @@ -496,23 +491,23 @@ class NodeOperation { * \param context: the OpenCL context * \param program: the OpenCL program containing all compositor kernels * \param queue: the OpenCL command queue of the device the chunk is executed on - * \param outputMemoryBuffer: the allocated memory buffer in main CPU memory - * \param clOutputBuffer: the allocated memory buffer in OpenCLDevice memory - * \param inputMemoryBuffers: all input MemoryBuffer's needed - * \param clMemToCleanUp: all created cl_mem references must be added to this list. + * \param output_memory_buffer: the allocated memory buffer in main CPU memory + * \param cl_output_buffer: the allocated memory buffer in OpenCLDevice memory + * \param input_memory_buffers: all input MemoryBuffer's needed + * \param cl_mem_to_clean_up: all created cl_mem references must be added to this list. * Framework will clean this after execution - * \param clKernelsToCleanUp: all created cl_kernel references must be added to this list. + * \param cl_kernels_to_clean_up: all created cl_kernel references must be added to this list. * Framework will clean this after execution */ - virtual void executeOpenCL(OpenCLDevice * /*device*/, - MemoryBuffer * /*outputMemoryBuffer*/, - cl_mem /*clOutputBuffer*/, - MemoryBuffer ** /*inputMemoryBuffers*/, - std::list * /*clMemToCleanUp*/, - std::list * /*clKernelsToCleanUp*/) + virtual void execute_opencl(OpenCLDevice * /*device*/, + MemoryBuffer * /*output_memory_buffer*/, + cl_mem /*cl_output_buffer*/, + MemoryBuffer ** /*input_memory_buffers*/, + std::list * /*cl_mem_to_clean_up*/, + std::list * /*cl_kernels_to_clean_up*/) { } - virtual void deinitExecution(); + virtual void deinit_execution(); void set_canvas(const rcti &canvas_area); const rcti &get_canvas() const; @@ -525,14 +520,14 @@ class NodeOperation { * \return [true:false] * \see BaseViewerOperation */ - virtual bool isActiveViewerOutput() const + virtual bool is_active_viewer_output() const { return false; } - virtual bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output); + virtual bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output); /** * \brief set the index of the input socket that will determine the canvas of this @@ -554,58 +549,58 @@ class NodeOperation { * \note only applicable for output operations like ViewerOperation * \return eCompositorPriority */ - virtual eCompositorPriority getRenderPriority() const + virtual eCompositorPriority get_render_priority() const { return eCompositorPriority::Low; } - inline bool isBraked() const + inline bool is_braked() const { return btree_->test_break(btree_->tbh); } - inline void updateDraw() + inline void update_draw() { if (btree_->update_draw) { btree_->update_draw(btree_->udh); } } - unsigned int getWidth() const + unsigned int get_width() const { return BLI_rcti_size_x(&get_canvas()); } - unsigned int getHeight() const + unsigned int get_height() const { return BLI_rcti_size_y(&get_canvas()); } - inline void readSampled(float result[4], float x, float y, PixelSampler sampler) + inline void read_sampled(float result[4], float x, float y, PixelSampler sampler) { - executePixelSampled(result, x, y, sampler); + execute_pixel_sampled(result, x, y, sampler); } - inline void readFiltered(float result[4], float x, float y, float dx[2], float dy[2]) + inline void read_filtered(float result[4], float x, float y, float dx[2], float dy[2]) { - executePixelFiltered(result, x, y, dx, dy); + execute_pixel_filtered(result, x, y, dx, dy); } - inline void read(float result[4], int x, int y, void *chunkData) + inline void read(float result[4], int x, int y, void *chunk_data) { - executePixel(result, x, y, chunkData); + execute_pixel(result, x, y, chunk_data); } - virtual void *initializeTileData(rcti * /*rect*/) + virtual void *initialize_tile_data(rcti * /*rect*/) { return 0; } - virtual void deinitializeTileData(rcti * /*rect*/, void * /*data*/) + virtual void deinitialize_tile_data(rcti * /*rect*/, void * /*data*/) { } - virtual MemoryBuffer *getInputMemoryBuffer(MemoryBuffer ** /*memoryBuffers*/) + virtual MemoryBuffer *get_input_memory_buffer(MemoryBuffer ** /*memory_buffers*/) { return 0; } @@ -614,7 +609,7 @@ class NodeOperation { * Return the meta data associated with this branch. * * The return parameter holds an instance or is an nullptr. */ - virtual std::unique_ptr getMetaData() + virtual std::unique_ptr get_meta_data() { return std::unique_ptr(); } @@ -672,28 +667,27 @@ class NodeOperation { combine_hashes(params_hash_, get_default_hash_3(param1, param2, param3)); } - void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center); - void addOutputSocket(DataType datatype); + void add_input_socket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center); + void add_output_socket(DataType datatype); /* TODO(manzanilla): to be removed with tiled implementation. */ - void setWidth(unsigned int width) + void set_width(unsigned int width) { canvas_.xmax = canvas_.xmin + width; this->flags.is_canvas_set = true; } - void setHeight(unsigned int height) + void set_height(unsigned int height) { canvas_.ymax = canvas_.ymin + height; this->flags.is_canvas_set = true; } - SocketReader *getInputSocketReader(unsigned int inputSocketindex); - NodeOperation *getInputOperation(unsigned int inputSocketindex); + SocketReader *get_input_socket_reader(unsigned int index); - void deinitMutex(); - void initMutex(); - void lockMutex(); - void unlockMutex(); + void deinit_mutex(); + void init_mutex(); + void lock_mutex(); + void unlock_mutex(); /** * \brief set whether this operation is complex @@ -701,7 +695,7 @@ class NodeOperation { * Complex operations are typically doing many reads to calculate the output of a single pixel. * Mostly Filter types (Blurs, Convolution, Defocus etc) need this to be set to true. */ - void setComplex(bool complex) + void set_complex(bool complex) { this->flags.complex = complex; } @@ -712,12 +706,12 @@ class NodeOperation { * \param result: is a float[4] array to store the result * \param x: the x-coordinate of the pixel to calculate in image space * \param y: the y-coordinate of the pixel to calculate in image space - * \param inputBuffers: chunks that can be read by their ReadBufferOperation. + * \param input_buffers: chunks that can be read by their ReadBufferOperation. */ - virtual void executePixelSampled(float /*output*/[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) + virtual void execute_pixel_sampled(float /*output*/[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { } @@ -727,12 +721,12 @@ class NodeOperation { * \param result: is a float[4] array to store the result * \param x: the x-coordinate of the pixel to calculate in image space * \param y: the y-coordinate of the pixel to calculate in image space - * \param inputBuffers: chunks that can be read by their ReadBufferOperation. - * \param chunkData: chunk specific data a during execution time. + * \param input_buffers: chunks that can be read by their ReadBufferOperation. + * \param chunk_data: chunk specific data a during execution time. */ - virtual void executePixel(float output[4], int x, int y, void * /*chunkData*/) + virtual void execute_pixel(float output[4], int x, int y, void * /*chunk_data*/) { - executePixelSampled(output, x, y, PixelSampler::Nearest); + execute_pixel_sampled(output, x, y, PixelSampler::Nearest); } /** @@ -743,9 +737,9 @@ class NodeOperation { * \param y: the y-coordinate of the pixel to calculate in image space * \param dx: * \param dy: - * \param inputBuffers: chunks that can be read by their ReadBufferOperation. + * \param input_buffers: chunks that can be read by their ReadBufferOperation. */ - virtual void executePixelFiltered( + virtual void execute_pixel_filtered( float /*output*/[4], float /*x*/, float /*y*/, float /*dx*/[2], float /*dy*/[2]) { } diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 708bda30636..5dc66fea670 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -45,7 +45,7 @@ NodeOperationBuilder::NodeOperationBuilder(const CompositorContext *context, graph_.from_bNodeTree(*context, b_nodetree); } -void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) +void NodeOperationBuilder::convert_to_operations(ExecutionSystem *system) { /* interface handle for nodes */ NodeConverter converter(this); @@ -54,7 +54,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) current_node_ = node; DebugInfo::node_to_operations(node); - node->convertToOperations(converter, *context_); + node->convert_to_operations(converter, *context_); } current_node_ = nullptr; @@ -86,7 +86,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) } for (NodeOperationInput *op_to : op_to_list) { - addLink(op_from, op_to); + add_link(op_from, op_to); } } @@ -130,12 +130,12 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) system->set_operations(operations_, groups_); } -void NodeOperationBuilder::addOperation(NodeOperation *operation) +void NodeOperationBuilder::add_operation(NodeOperation *operation) { operation->set_id(operations_.size()); operations_.append(operation); if (current_node_) { - operation->set_name(current_node_->getbNode()->name); + operation->set_name(current_node_->get_bnode()->name); } operation->set_execution_model(context_->get_execution_model()); operation->set_execution_system(exec_system_); @@ -144,9 +144,9 @@ void NodeOperationBuilder::addOperation(NodeOperation *operation) void NodeOperationBuilder::replace_operation_with_constant(NodeOperation *operation, ConstantOperation *constant_operation) { - BLI_assert(constant_operation->getNumberOfInputSockets() == 0); + BLI_assert(constant_operation->get_number_of_input_sockets() == 0); unlink_inputs_and_relink_outputs(operation, constant_operation); - addOperation(constant_operation); + add_operation(constant_operation); } void NodeOperationBuilder::unlink_inputs_and_relink_outputs(NodeOperation *unlinked_op, @@ -155,61 +155,61 @@ void NodeOperationBuilder::unlink_inputs_and_relink_outputs(NodeOperation *unlin int i = 0; while (i < links_.size()) { Link &link = links_[i]; - if (&link.to()->getOperation() == unlinked_op) { - link.to()->setLink(nullptr); + if (&link.to()->get_operation() == unlinked_op) { + link.to()->set_link(nullptr); links_.remove(i); continue; } - if (&link.from()->getOperation() == unlinked_op) { - link.to()->setLink(linked_op->getOutputSocket()); - links_[i] = Link(linked_op->getOutputSocket(), link.to()); + if (&link.from()->get_operation() == unlinked_op) { + link.to()->set_link(linked_op->get_output_socket()); + links_[i] = Link(linked_op->get_output_socket(), link.to()); } i++; } } -void NodeOperationBuilder::mapInputSocket(NodeInput *node_socket, - NodeOperationInput *operation_socket) +void NodeOperationBuilder::map_input_socket(NodeInput *node_socket, + NodeOperationInput *operation_socket) { BLI_assert(current_node_); - BLI_assert(node_socket->getNode() == current_node_); + BLI_assert(node_socket->get_node() == current_node_); /* NOTE: this maps operation sockets to node sockets. - * for resolving links the map will be inverted first in convertToOperations, + * for resolving links the map will be inverted first in convert_to_operations, * to get a list of links for each node input socket. */ input_map_.add_new(operation_socket, node_socket); } -void NodeOperationBuilder::mapOutputSocket(NodeOutput *node_socket, - NodeOperationOutput *operation_socket) +void NodeOperationBuilder::map_output_socket(NodeOutput *node_socket, + NodeOperationOutput *operation_socket) { BLI_assert(current_node_); - BLI_assert(node_socket->getNode() == current_node_); + BLI_assert(node_socket->get_node() == current_node_); output_map_.add_new(node_socket, operation_socket); } -void NodeOperationBuilder::addLink(NodeOperationOutput *from, NodeOperationInput *to) +void NodeOperationBuilder::add_link(NodeOperationOutput *from, NodeOperationInput *to) { - if (to->isConnected()) { + if (to->is_connected()) { return; } links_.append(Link(from, to)); /* register with the input */ - to->setLink(from); + to->set_link(from); } -void NodeOperationBuilder::removeInputLink(NodeOperationInput *to) +void NodeOperationBuilder::remove_input_link(NodeOperationInput *to) { int index = 0; for (Link &link : links_) { if (link.to() == to) { /* unregister with the input */ - to->setLink(nullptr); + to->set_link(nullptr); links_.remove(index); return; @@ -222,67 +222,67 @@ PreviewOperation *NodeOperationBuilder::make_preview_operation() const { BLI_assert(current_node_); - if (!(current_node_->getbNode()->flag & NODE_PREVIEW)) { + if (!(current_node_->get_bnode()->flag & NODE_PREVIEW)) { return nullptr; } /* previews only in the active group */ - if (!current_node_->isInActiveGroup()) { + if (!current_node_->is_in_active_group()) { return nullptr; } /* do not calculate previews of hidden nodes */ - if (current_node_->getbNode()->flag & NODE_HIDDEN) { + if (current_node_->get_bnode()->flag & NODE_HIDDEN) { return nullptr; } - bNodeInstanceHash *previews = context_->getPreviewHash(); + bNodeInstanceHash *previews = context_->get_preview_hash(); if (previews) { - PreviewOperation *operation = new PreviewOperation(context_->getViewSettings(), - context_->getDisplaySettings(), - current_node_->getbNode()->preview_xsize, - current_node_->getbNode()->preview_ysize); - operation->setbNodeTree(context_->getbNodeTree()); - operation->verifyPreview(previews, current_node_->getInstanceKey()); + PreviewOperation *operation = new PreviewOperation(context_->get_view_settings(), + context_->get_display_settings(), + current_node_->get_bnode()->preview_xsize, + current_node_->get_bnode()->preview_ysize); + operation->set_bnodetree(context_->get_bnodetree()); + operation->verify_preview(previews, current_node_->get_instance_key()); return operation; } return nullptr; } -void NodeOperationBuilder::addPreview(NodeOperationOutput *output) +void NodeOperationBuilder::add_preview(NodeOperationOutput *output) { PreviewOperation *operation = make_preview_operation(); if (operation) { - addOperation(operation); + add_operation(operation); - addLink(output, operation->getInputSocket(0)); + add_link(output, operation->get_input_socket(0)); } } -void NodeOperationBuilder::addNodeInputPreview(NodeInput *input) +void NodeOperationBuilder::add_node_input_preview(NodeInput *input) { PreviewOperation *operation = make_preview_operation(); if (operation) { - addOperation(operation); + add_operation(operation); - mapInputSocket(input, operation->getInputSocket(0)); + map_input_socket(input, operation->get_input_socket(0)); } } -void NodeOperationBuilder::registerViewer(ViewerOperation *viewer) +void NodeOperationBuilder::register_viewer(ViewerOperation *viewer) { if (active_viewer_) { - if (current_node_->isInActiveGroup()) { + if (current_node_->is_in_active_group()) { /* deactivate previous viewer */ - active_viewer_->setActive(false); + active_viewer_->set_active(false); active_viewer_ = viewer; - viewer->setActive(true); + viewer->set_active(true); } } else { - if (current_node_->getbNodeTree() == context_->getbNodeTree()) { + if (current_node_->get_bnodetree() == context_->get_bnodetree()) { active_viewer_ = viewer; - viewer->setActive(true); + viewer->set_active(true); } } } @@ -296,25 +296,25 @@ void NodeOperationBuilder::add_datatype_conversions() Vector convert_links; for (const Link &link : links_) { /* proxy operations can skip data type conversion */ - NodeOperation *from_op = &link.from()->getOperation(); - NodeOperation *to_op = &link.to()->getOperation(); + NodeOperation *from_op = &link.from()->get_operation(); + NodeOperation *to_op = &link.to()->get_operation(); if (!(from_op->get_flags().use_datatype_conversion || to_op->get_flags().use_datatype_conversion)) { continue; } - if (link.from()->getDataType() != link.to()->getDataType()) { + if (link.from()->get_data_type() != link.to()->get_data_type()) { convert_links.append(link); } } for (const Link &link : convert_links) { NodeOperation *converter = COM_convert_data_type(*link.from(), *link.to()); if (converter) { - addOperation(converter); + add_operation(converter); - removeInputLink(link.to()); - addLink(link.from(), converter->getInputSocket(0)); - addLink(converter->getOutputSocket(0), link.to()); + remove_input_link(link.to()); + add_link(link.from(), converter->get_input_socket(0)); + add_link(converter->get_output_socket(0), link.to()); } } } @@ -326,9 +326,9 @@ void NodeOperationBuilder::add_operation_input_constants() */ Vector pending_inputs; for (NodeOperation *op : operations_) { - for (int k = 0; k < op->getNumberOfInputSockets(); ++k) { - NodeOperationInput *input = op->getInputSocket(k); - if (!input->isConnected()) { + for (int k = 0; k < op->get_number_of_input_sockets(); ++k) { + NodeOperationInput *input = op->get_input_socket(k); + if (!input->is_connected()) { pending_inputs.append(input); } } @@ -341,50 +341,50 @@ void NodeOperationBuilder::add_operation_input_constants() void NodeOperationBuilder::add_input_constant_value(NodeOperationInput *input, const NodeInput *node_input) { - switch (input->getDataType()) { + switch (input->get_data_type()) { case DataType::Value: { float value; - if (node_input && node_input->getbNodeSocket()) { - value = node_input->getEditorValueFloat(); + if (node_input && node_input->get_bnode_socket()) { + value = node_input->get_editor_value_float(); } else { value = 0.0f; } SetValueOperation *op = new SetValueOperation(); - op->setValue(value); - addOperation(op); - addLink(op->getOutputSocket(), input); + op->set_value(value); + add_operation(op); + add_link(op->get_output_socket(), input); break; } case DataType::Color: { float value[4]; - if (node_input && node_input->getbNodeSocket()) { - node_input->getEditorValueColor(value); + if (node_input && node_input->get_bnode_socket()) { + node_input->get_editor_value_color(value); } else { zero_v4(value); } SetColorOperation *op = new SetColorOperation(); - op->setChannels(value); - addOperation(op); - addLink(op->getOutputSocket(), input); + op->set_channels(value); + add_operation(op); + add_link(op->get_output_socket(), input); break; } case DataType::Vector: { float value[3]; - if (node_input && node_input->getbNodeSocket()) { - node_input->getEditorValueVector(value); + if (node_input && node_input->get_bnode_socket()) { + node_input->get_editor_value_vector(value); } else { zero_v3(value); } SetVectorOperation *op = new SetVectorOperation(); - op->setVector(value); - addOperation(op); - addLink(op->getOutputSocket(), input); + op->set_vector(value); + add_operation(op); + add_link(op->get_output_socket(), input); break; } } @@ -395,8 +395,8 @@ void NodeOperationBuilder::resolve_proxies() Vector proxy_links; for (const Link &link : links_) { /* don't replace links from proxy to proxy, since we may need them for replacing others! */ - if (link.from()->getOperation().get_flags().is_proxy_operation && - !link.to()->getOperation().get_flags().is_proxy_operation) { + if (link.from()->get_operation().get_flags().is_proxy_operation && + !link.to()->get_operation().get_flags().is_proxy_operation) { proxy_links.append(link); } } @@ -406,15 +406,15 @@ void NodeOperationBuilder::resolve_proxies() NodeOperationOutput *from = link.from(); do { /* walk upstream bypassing the proxy operation */ - from = from->getOperation().getInputSocket(0)->getLink(); - } while (from && from->getOperation().get_flags().is_proxy_operation); + from = from->get_operation().get_input_socket(0)->get_link(); + } while (from && from->get_operation().get_flags().is_proxy_operation); - removeInputLink(to); + remove_input_link(to); /* we may not have a final proxy input link, * in that case it just gets dropped */ if (from) { - addLink(from, to); + add_link(from, to); } } } @@ -424,7 +424,8 @@ void NodeOperationBuilder::determine_canvases() /* Determine all canvas areas of the operations. */ const rcti &preferred_area = COM_AREA_NONE; for (NodeOperation *op : operations_) { - if (op->isOutputOperation(context_->isRendering()) && !op->get_flags().is_preview_operation) { + if (op->is_output_operation(context_->is_rendering()) && + !op->get_flags().is_preview_operation) { rcti canvas = COM_AREA_NONE; op->determine_canvas(preferred_area, canvas); op->set_canvas(canvas); @@ -432,7 +433,8 @@ void NodeOperationBuilder::determine_canvases() } for (NodeOperation *op : operations_) { - if (op->isOutputOperation(context_->isRendering()) && op->get_flags().is_preview_operation) { + if (op->is_output_operation(context_->is_rendering()) && + op->get_flags().is_preview_operation) { rcti canvas = COM_AREA_NONE; op->determine_canvas(preferred_area, canvas); op->set_canvas(canvas); @@ -443,12 +445,12 @@ void NodeOperationBuilder::determine_canvases() { Vector convert_links; for (const Link &link : links_) { - if (link.to()->getResizeMode() != ResizeMode::None) { - const rcti &from_canvas = link.from()->getOperation().get_canvas(); - const rcti &to_canvas = link.to()->getOperation().get_canvas(); + if (link.to()->get_resize_mode() != ResizeMode::None) { + const rcti &from_canvas = link.from()->get_operation().get_canvas(); + const rcti &to_canvas = link.to()->get_operation().get_canvas(); bool needs_conversion; - if (link.to()->getResizeMode() == ResizeMode::Align) { + if (link.to()->get_resize_mode() == ResizeMode::Align) { needs_conversion = from_canvas.xmin != to_canvas.xmin || from_canvas.ymin != to_canvas.ymin; } @@ -528,7 +530,7 @@ WriteBufferOperation *NodeOperationBuilder::find_attached_write_buffer_operation { for (const Link &link : links_) { if (link.from() == output) { - NodeOperation &op = link.to()->getOperation(); + NodeOperation &op = link.to()->get_operation(); if (op.get_flags().is_write_buffer_operation) { return (WriteBufferOperation *)(&op); } @@ -540,39 +542,39 @@ WriteBufferOperation *NodeOperationBuilder::find_attached_write_buffer_operation void NodeOperationBuilder::add_input_buffers(NodeOperation * /*operation*/, NodeOperationInput *input) { - if (!input->isConnected()) { + if (!input->is_connected()) { return; } - NodeOperationOutput *output = input->getLink(); - if (output->getOperation().get_flags().is_read_buffer_operation) { + NodeOperationOutput *output = input->get_link(); + if (output->get_operation().get_flags().is_read_buffer_operation) { /* input is already buffered, no need to add another */ return; } /* this link will be replaced below */ - removeInputLink(input); + remove_input_link(input); /* check of other end already has write operation, otherwise add a new one */ WriteBufferOperation *writeoperation = find_attached_write_buffer_operation(output); if (!writeoperation) { - writeoperation = new WriteBufferOperation(output->getDataType()); - writeoperation->setbNodeTree(context_->getbNodeTree()); - addOperation(writeoperation); + writeoperation = new WriteBufferOperation(output->get_data_type()); + writeoperation->set_bnodetree(context_->get_bnodetree()); + add_operation(writeoperation); - addLink(output, writeoperation->getInputSocket(0)); + add_link(output, writeoperation->get_input_socket(0)); - writeoperation->readResolutionFromInputSocket(); + writeoperation->read_resolution_from_input_socket(); } /* add readbuffer op for the input */ - ReadBufferOperation *readoperation = new ReadBufferOperation(output->getDataType()); - readoperation->setMemoryProxy(writeoperation->getMemoryProxy()); - this->addOperation(readoperation); + ReadBufferOperation *readoperation = new ReadBufferOperation(output->get_data_type()); + readoperation->set_memory_proxy(writeoperation->get_memory_proxy()); + this->add_operation(readoperation); - addLink(readoperation->getOutputSocket(), input); + add_link(readoperation->get_output_socket(), input); - readoperation->readResolutionFromWriteBuffer(); + readoperation->read_resolution_from_write_buffer(); } void NodeOperationBuilder::add_output_buffers(NodeOperation *operation, @@ -584,44 +586,44 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation, return; } - WriteBufferOperation *writeOperation = nullptr; + WriteBufferOperation *write_operation = nullptr; for (NodeOperationInput *target : targets) { /* try to find existing write buffer operation */ - if (target->getOperation().get_flags().is_write_buffer_operation) { - BLI_assert(writeOperation == nullptr); /* there should only be one write op connected */ - writeOperation = (WriteBufferOperation *)(&target->getOperation()); + if (target->get_operation().get_flags().is_write_buffer_operation) { + BLI_assert(write_operation == nullptr); /* there should only be one write op connected */ + write_operation = (WriteBufferOperation *)(&target->get_operation()); } else { /* remove all links to other nodes */ - removeInputLink(target); + remove_input_link(target); } } /* if no write buffer operation exists yet, create a new one */ - if (!writeOperation) { - writeOperation = new WriteBufferOperation(operation->getOutputSocket()->getDataType()); - writeOperation->setbNodeTree(context_->getbNodeTree()); - addOperation(writeOperation); + if (!write_operation) { + write_operation = new WriteBufferOperation(operation->get_output_socket()->get_data_type()); + write_operation->set_bnodetree(context_->get_bnodetree()); + add_operation(write_operation); - addLink(output, writeOperation->getInputSocket(0)); + add_link(output, write_operation->get_input_socket(0)); } - writeOperation->readResolutionFromInputSocket(); + write_operation->read_resolution_from_input_socket(); /* add readbuffer op for every former connected input */ for (NodeOperationInput *target : targets) { - if (&target->getOperation() == writeOperation) { + if (&target->get_operation() == write_operation) { continue; /* skip existing write op links */ } ReadBufferOperation *readoperation = new ReadBufferOperation( - operation->getOutputSocket()->getDataType()); - readoperation->setMemoryProxy(writeOperation->getMemoryProxy()); - addOperation(readoperation); + operation->get_output_socket()->get_data_type()); + readoperation->set_memory_proxy(write_operation->get_memory_proxy()); + add_operation(readoperation); - addLink(readoperation->getOutputSocket(), target); + add_link(readoperation->get_output_socket(), target); - readoperation->readResolutionFromWriteBuffer(); + readoperation->read_resolution_from_write_buffer(); } } @@ -640,12 +642,12 @@ void NodeOperationBuilder::add_complex_operation_buffers() for (NodeOperation *op : complex_ops) { DebugInfo::operation_read_write_buffer(op); - for (int index = 0; index < op->getNumberOfInputSockets(); index++) { - add_input_buffers(op, op->getInputSocket(index)); + for (int index = 0; index < op->get_number_of_input_sockets(); index++) { + add_input_buffers(op, op->get_input_socket(index)); } - for (int index = 0; index < op->getNumberOfOutputSockets(); index++) { - add_output_buffers(op, op->getOutputSocket(index)); + for (int index = 0; index < op->get_number_of_output_sockets(); index++) { + add_output_buffers(op, op->get_output_socket(index)); } } } @@ -659,18 +661,18 @@ static void find_reachable_operations_recursive(Tags &reachable, NodeOperation * } reachable.insert(op); - for (int i = 0; i < op->getNumberOfInputSockets(); i++) { - NodeOperationInput *input = op->getInputSocket(i); - if (input->isConnected()) { - find_reachable_operations_recursive(reachable, &input->getLink()->getOperation()); + for (int i = 0; i < op->get_number_of_input_sockets(); i++) { + NodeOperationInput *input = op->get_input_socket(i); + if (input->is_connected()) { + find_reachable_operations_recursive(reachable, &input->get_link()->get_operation()); } } /* associated write-buffer operations are executed as well */ if (op->get_flags().is_read_buffer_operation) { ReadBufferOperation *read_op = (ReadBufferOperation *)op; - MemoryProxy *memproxy = read_op->getMemoryProxy(); - find_reachable_operations_recursive(reachable, memproxy->getWriteBufferOperation()); + MemoryProxy *memproxy = read_op->get_memory_proxy(); + find_reachable_operations_recursive(reachable, memproxy->get_write_buffer_operation()); } } @@ -679,7 +681,7 @@ void NodeOperationBuilder::prune_operations() Tags reachable; for (NodeOperation *op : operations_) { /* output operations are primary executed operations */ - if (op->isOutputOperation(context_->isRendering())) { + if (op->is_output_operation(context_->is_rendering())) { find_reachable_operations_recursive(reachable, op); } } @@ -708,10 +710,10 @@ static void sort_operations_recursive(Vector &sorted, } visited.insert(op); - for (int i = 0; i < op->getNumberOfInputSockets(); i++) { - NodeOperationInput *input = op->getInputSocket(i); - if (input->isConnected()) { - sort_operations_recursive(sorted, visited, &input->getLink()->getOperation()); + for (int i = 0; i < op->get_number_of_input_sockets(); i++) { + NodeOperationInput *input = op->get_input_socket(i); + if (input->is_connected()) { + sort_operations_recursive(sorted, visited, &input->get_link()->get_operation()); } } @@ -738,15 +740,15 @@ static void add_group_operations_recursive(Tags &visited, NodeOperation *op, Exe } visited.insert(op); - if (!group->addOperation(op)) { + if (!group->add_operation(op)) { return; } /* add all eligible input ops to the group */ - for (int i = 0; i < op->getNumberOfInputSockets(); i++) { - NodeOperationInput *input = op->getInputSocket(i); - if (input->isConnected()) { - add_group_operations_recursive(visited, &input->getLink()->getOperation(), group); + for (int i = 0; i < op->get_number_of_input_sockets(); i++) { + NodeOperationInput *input = op->get_input_socket(i); + if (input->is_connected()) { + add_group_operations_recursive(visited, &input->get_link()->get_operation(), group); } } } @@ -765,19 +767,19 @@ ExecutionGroup *NodeOperationBuilder::make_group(NodeOperation *op) void NodeOperationBuilder::group_operations() { for (NodeOperation *op : operations_) { - if (op->isOutputOperation(context_->isRendering())) { + if (op->is_output_operation(context_->is_rendering())) { ExecutionGroup *group = make_group(op); - group->setOutputExecutionGroup(true); + group->set_output_execution_group(true); } /* add new groups for associated memory proxies where needed */ if (op->get_flags().is_read_buffer_operation) { ReadBufferOperation *read_op = (ReadBufferOperation *)op; - MemoryProxy *memproxy = read_op->getMemoryProxy(); + MemoryProxy *memproxy = read_op->get_memory_proxy(); - if (memproxy->getExecutor() == nullptr) { - ExecutionGroup *group = make_group(memproxy->getWriteBufferOperation()); - memproxy->setExecutor(group); + if (memproxy->get_executor() == nullptr) { + ExecutionGroup *group = make_group(memproxy->get_write_buffer_operation()); + memproxy->set_executor(group); } } } @@ -804,15 +806,15 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder) os << "\n"; for (const NodeOperationBuilder::Link &link : builder.get_links()) { - os << " op" << link.from()->getOperation().get_id() << " -> op" - << link.to()->getOperation().get_id() << ";\n"; + os << " op" << link.from()->get_operation().get_id() << " -> op" + << link.to()->get_operation().get_id() << ";\n"; } for (const NodeOperation *operation : builder.get_operations()) { if (operation->get_flags().is_read_buffer_operation) { const ReadBufferOperation &read_operation = static_cast( *operation); const WriteBufferOperation &write_operation = - *read_operation.getMemoryProxy()->getWriteBufferOperation(); + *read_operation.get_memory_proxy()->get_write_buffer_operation(); os << " op" << write_operation.get_id() << " -> op" << read_operation.get_id() << ";\n"; } } @@ -824,7 +826,7 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder) std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link) { - os << link.from()->getOperation().get_id() << " -> " << link.to()->getOperation().get_id(); + os << link.from()->get_operation().get_id() << " -> " << link.to()->get_operation().get_id(); return os; } diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h index 21e2f4ce95d..fcb2dd3800e 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h @@ -96,27 +96,27 @@ class NodeOperationBuilder { return *context_; } - void convertToOperations(ExecutionSystem *system); + void convert_to_operations(ExecutionSystem *system); - void addOperation(NodeOperation *operation); + void add_operation(NodeOperation *operation); void replace_operation_with_constant(NodeOperation *operation, ConstantOperation *constant_operation); /** Map input socket of the current node to an operation socket */ - void mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket); + void map_input_socket(NodeInput *node_socket, NodeOperationInput *operation_socket); /** Map output socket of the current node to an operation socket */ - void mapOutputSocket(NodeOutput *node_socket, NodeOperationOutput *operation_socket); + void map_output_socket(NodeOutput *node_socket, NodeOperationOutput *operation_socket); - void addLink(NodeOperationOutput *from, NodeOperationInput *to); - void removeInputLink(NodeOperationInput *to); + void add_link(NodeOperationOutput *from, NodeOperationInput *to); + void remove_input_link(NodeOperationInput *to); /** Add a preview operation for a operation output */ - void addPreview(NodeOperationOutput *output); + void add_preview(NodeOperationOutput *output); /** Add a preview operation for a node input */ - void addNodeInputPreview(NodeInput *input); + void add_node_input_preview(NodeInput *input); /** Define a viewer operation as the active output, if possible */ - void registerViewer(ViewerOperation *viewer); + void register_viewer(ViewerOperation *viewer); /** The currently active viewer output operation */ ViewerOperation *active_viewer() const { diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cc b/source/blender/compositor/intern/COM_OpenCLDevice.cc index af55890cba7..9a274abf806 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.cc +++ b/source/blender/compositor/intern/COM_OpenCLDevice.cc @@ -40,13 +40,13 @@ const cl_image_format IMAGE_FORMAT_VALUE = { OpenCLDevice::OpenCLDevice(cl_context context, cl_device_id device, cl_program program, - cl_int vendorId) + cl_int vendor_id) { device_ = device; context_ = context; program_ = program; queue_ = nullptr; - vendorID_ = vendorId; + vendor_id_ = vendor_id; cl_int error; queue_ = clCreateCommandQueue(context_, device_, 0, &error); @@ -57,7 +57,7 @@ OpenCLDevice::OpenCLDevice(OpenCLDevice &&other) noexcept device_(other.device_), program_(other.program_), queue_(other.queue_), - vendorID_(other.vendorID_) + vendor_id_(other.vendor_id_) { other.queue_ = nullptr; } @@ -71,37 +71,38 @@ OpenCLDevice::~OpenCLDevice() void OpenCLDevice::execute(WorkPackage *work_package) { - const unsigned int chunkNumber = work_package->chunk_number; - ExecutionGroup *executionGroup = work_package->execution_group; + const unsigned int chunk_number = work_package->chunk_number; + ExecutionGroup *execution_group = work_package->execution_group; - MemoryBuffer **inputBuffers = executionGroup->getInputBuffersOpenCL(chunkNumber); - MemoryBuffer *outputBuffer = executionGroup->allocateOutputBuffer(work_package->rect); + MemoryBuffer **input_buffers = execution_group->get_input_buffers_opencl(chunk_number); + MemoryBuffer *output_buffer = execution_group->allocate_output_buffer(work_package->rect); - executionGroup->getOutputOperation()->executeOpenCLRegion( - this, &work_package->rect, chunkNumber, inputBuffers, outputBuffer); + execution_group->get_output_operation()->execute_opencl_region( + this, &work_package->rect, chunk_number, input_buffers, output_buffer); - delete outputBuffer; + delete output_buffer; - executionGroup->finalizeChunkExecution(chunkNumber, inputBuffers); + execution_group->finalize_chunk_execution(chunk_number, input_buffers); } -cl_mem OpenCLDevice::COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - int offsetIndex, - std::list *cleanup, - MemoryBuffer **inputMemoryBuffers, - SocketReader *reader) +cl_mem OpenCLDevice::COM_cl_attach_memory_buffer_to_kernel_parameter( + cl_kernel kernel, + int parameter_index, + int offset_index, + std::list *cleanup, + MemoryBuffer **input_memory_buffers, + SocketReader *reader) { - return COM_clAttachMemoryBufferToKernelParameter(kernel, - parameterIndex, - offsetIndex, - cleanup, - inputMemoryBuffers, - (ReadBufferOperation *)reader); + return COM_cl_attach_memory_buffer_to_kernel_parameter(kernel, + parameter_index, + offset_index, + cleanup, + input_memory_buffers, + (ReadBufferOperation *)reader); } -const cl_image_format *OpenCLDevice::determineImageFormat(MemoryBuffer *memoryBuffer) +const cl_image_format *OpenCLDevice::determine_image_format(MemoryBuffer *memory_buffer) { - switch (memoryBuffer->get_num_channels()) { + switch (memory_buffer->get_num_channels()) { case 1: return &IMAGE_FORMAT_VALUE; break; @@ -118,92 +119,91 @@ const cl_image_format *OpenCLDevice::determineImageFormat(MemoryBuffer *memoryBu return &IMAGE_FORMAT_COLOR; } -cl_mem OpenCLDevice::COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - int offsetIndex, - std::list *cleanup, - MemoryBuffer **inputMemoryBuffers, - ReadBufferOperation *reader) +cl_mem OpenCLDevice::COM_cl_attach_memory_buffer_to_kernel_parameter( + cl_kernel kernel, + int parameter_index, + int offset_index, + std::list *cleanup, + MemoryBuffer **input_memory_buffers, + ReadBufferOperation *reader) { cl_int error; - MemoryBuffer *result = reader->getInputMemoryBuffer(inputMemoryBuffers); + MemoryBuffer *result = reader->get_input_memory_buffer(input_memory_buffers); - const cl_image_format *imageFormat = determineImageFormat(result); + const cl_image_format *image_format = determine_image_format(result); - cl_mem clBuffer = clCreateImage2D(context_, - CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, - imageFormat, - result->getWidth(), - result->getHeight(), - 0, - result->getBuffer(), - &error); + cl_mem cl_buffer = clCreateImage2D(context_, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + image_format, + result->get_width(), + result->get_height(), + 0, + result->get_buffer(), + &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } if (error == CL_SUCCESS) { - cleanup->push_back(clBuffer); + cleanup->push_back(cl_buffer); } - error = clSetKernelArg(kernel, parameterIndex, sizeof(cl_mem), &clBuffer); + error = clSetKernelArg(kernel, parameter_index, sizeof(cl_mem), &cl_buffer); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - COM_clAttachMemoryBufferOffsetToKernelParameter(kernel, offsetIndex, result); - return clBuffer; + COM_cl_attach_memory_buffer_offset_to_kernel_parameter(kernel, offset_index, result); + return cl_buffer; } -void OpenCLDevice::COM_clAttachMemoryBufferOffsetToKernelParameter(cl_kernel kernel, - int offsetIndex, - MemoryBuffer *memoryBuffer) +void OpenCLDevice::COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + cl_kernel kernel, int offset_index, MemoryBuffer *memory_buffer) { - if (offsetIndex != -1) { + if (offset_index != -1) { cl_int error; - const rcti &rect = memoryBuffer->get_rect(); + const rcti &rect = memory_buffer->get_rect(); cl_int2 offset = {{rect.xmin, rect.ymin}}; - error = clSetKernelArg(kernel, offsetIndex, sizeof(cl_int2), &offset); + error = clSetKernelArg(kernel, offset_index, sizeof(cl_int2), &offset); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } } } -void OpenCLDevice::COM_clAttachSizeToKernelParameter(cl_kernel kernel, - int offsetIndex, - NodeOperation *operation) +void OpenCLDevice::COM_cl_attach_size_to_kernel_parameter(cl_kernel kernel, + int offset_index, + NodeOperation *operation) { - if (offsetIndex != -1) { + if (offset_index != -1) { cl_int error; - cl_int2 offset = {{(cl_int)operation->getWidth(), (cl_int)operation->getHeight()}}; + cl_int2 offset = {{(cl_int)operation->get_width(), (cl_int)operation->get_height()}}; - error = clSetKernelArg(kernel, offsetIndex, sizeof(cl_int2), &offset); + error = clSetKernelArg(kernel, offset_index, sizeof(cl_int2), &offset); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } } } -void OpenCLDevice::COM_clAttachOutputMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - cl_mem clOutputMemoryBuffer) +void OpenCLDevice::COM_cl_attach_output_memory_buffer_to_kernel_parameter( + cl_kernel kernel, int parameter_index, cl_mem cl_output_memory_buffer) { cl_int error; - error = clSetKernelArg(kernel, parameterIndex, sizeof(cl_mem), &clOutputMemoryBuffer); + error = clSetKernelArg(kernel, parameter_index, sizeof(cl_mem), &cl_output_memory_buffer); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } } -void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, MemoryBuffer *outputMemoryBuffer) +void OpenCLDevice::COM_cl_enqueue_range(cl_kernel kernel, MemoryBuffer *output_memory_buffer) { cl_int error; const size_t size[] = { - (size_t)outputMemoryBuffer->getWidth(), - (size_t)outputMemoryBuffer->getHeight(), + (size_t)output_memory_buffer->get_width(), + (size_t)output_memory_buffer->get_height(), }; error = clEnqueueNDRangeKernel(queue_, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); @@ -212,44 +212,44 @@ void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, MemoryBuffer *outputMemo } } -void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, - MemoryBuffer *outputMemoryBuffer, - int offsetIndex, - NodeOperation *operation) +void OpenCLDevice::COM_cl_enqueue_range(cl_kernel kernel, + MemoryBuffer *output_memory_buffer, + int offset_index, + NodeOperation *operation) { cl_int error; - const int width = outputMemoryBuffer->getWidth(); - const int height = outputMemoryBuffer->getHeight(); + const int width = output_memory_buffer->get_width(); + const int height = output_memory_buffer->get_height(); int offsetx; int offsety; - int localSize = 1024; + int local_size = 1024; size_t size[2]; cl_int2 offset; - if (vendorID_ == NVIDIA) { - localSize = 32; + if (vendor_id_ == NVIDIA) { + local_size = 32; } bool breaked = false; - for (offsety = 0; offsety < height && (!breaked); offsety += localSize) { + for (offsety = 0; offsety < height && (!breaked); offsety += local_size) { offset.s[1] = offsety; - if (offsety + localSize < height) { - size[1] = localSize; + if (offsety + local_size < height) { + size[1] = local_size; } else { size[1] = height - offsety; } - for (offsetx = 0; offsetx < width && (!breaked); offsetx += localSize) { - if (offsetx + localSize < width) { - size[0] = localSize; + for (offsetx = 0; offsetx < width && (!breaked); offsetx += local_size) { + if (offsetx + local_size < width) { + size[0] = local_size; } else { size[0] = width - offsetx; } offset.s[0] = offsetx; - error = clSetKernelArg(kernel, offsetIndex, sizeof(cl_int2), &offset); + error = clSetKernelArg(kernel, offset_index, sizeof(cl_int2), &offset); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } @@ -259,15 +259,15 @@ void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } clFlush(queue_); - if (operation->isBraked()) { + if (operation->is_braked()) { breaked = false; } } } } -cl_kernel OpenCLDevice::COM_clCreateKernel(const char *kernelname, - std::list *clKernelsToCleanUp) +cl_kernel OpenCLDevice::COM_cl_create_kernel(const char *kernelname, + std::list *cl_kernels_to_clean_up) { cl_int error; cl_kernel kernel = clCreateKernel(program_, kernelname, &error); @@ -275,8 +275,8 @@ cl_kernel OpenCLDevice::COM_clCreateKernel(const char *kernelname, printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } else { - if (clKernelsToCleanUp) { - clKernelsToCleanUp->push_back(kernel); + if (cl_kernels_to_clean_up) { + cl_kernels_to_clean_up->push_back(kernel); } } return kernel; diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.h b/source/blender/compositor/intern/COM_OpenCLDevice.h index ca5a95f4a48..44a25747407 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.h +++ b/source/blender/compositor/intern/COM_OpenCLDevice.h @@ -63,7 +63,7 @@ class OpenCLDevice : public Device { /** * \brief opencl vendor ID */ - cl_int vendorID_; + cl_int vendor_id_; public: /** @@ -73,7 +73,7 @@ class OpenCLDevice : public Device { * \param program: * \param vendorID: */ - OpenCLDevice(cl_context context, cl_device_id device, cl_program program, cl_int vendorId); + OpenCLDevice(cl_context context, cl_device_id device, cl_program program, cl_int vendor_id); OpenCLDevice(OpenCLDevice &&other) noexcept; @@ -89,45 +89,46 @@ class OpenCLDevice : public Device { * \brief determine an image format * \param memorybuffer: */ - static const cl_image_format *determineImageFormat(MemoryBuffer *memoryBuffer); + static const cl_image_format *determine_image_format(MemoryBuffer *memory_buffer); - cl_context getContext() + cl_context get_context() { return context_; } - cl_command_queue getQueue() + cl_command_queue get_queue() { return queue_; } - cl_mem COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - int offsetIndex, - std::list *cleanup, - MemoryBuffer **inputMemoryBuffers, - SocketReader *reader); - cl_mem COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - int offsetIndex, - std::list *cleanup, - MemoryBuffer **inputMemoryBuffers, - ReadBufferOperation *reader); - void COM_clAttachMemoryBufferOffsetToKernelParameter(cl_kernel kernel, - int offsetIndex, - MemoryBuffer *memoryBuffers); - void COM_clAttachOutputMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - cl_mem clOutputMemoryBuffer); - void COM_clAttachSizeToKernelParameter(cl_kernel kernel, - int offsetIndex, - NodeOperation *operation); - void COM_clEnqueueRange(cl_kernel kernel, MemoryBuffer *outputMemoryBuffer); - void COM_clEnqueueRange(cl_kernel kernel, - MemoryBuffer *outputMemoryBuffer, - int offsetIndex, - NodeOperation *operation); - cl_kernel COM_clCreateKernel(const char *kernelname, std::list *clKernelsToCleanUp); + cl_mem COM_cl_attach_memory_buffer_to_kernel_parameter(cl_kernel kernel, + int parameter_index, + int offset_index, + std::list *cleanup, + MemoryBuffer **input_memory_buffers, + SocketReader *reader); + cl_mem COM_cl_attach_memory_buffer_to_kernel_parameter(cl_kernel kernel, + int parameter_index, + int offset_index, + std::list *cleanup, + MemoryBuffer **input_memory_buffers, + ReadBufferOperation *reader); + void COM_cl_attach_memory_buffer_offset_to_kernel_parameter(cl_kernel kernel, + int offset_index, + MemoryBuffer *memory_buffers); + void COM_cl_attach_output_memory_buffer_to_kernel_parameter(cl_kernel kernel, + int parameter_index, + cl_mem cl_output_memory_buffer); + void COM_cl_attach_size_to_kernel_parameter(cl_kernel kernel, + int offset_index, + NodeOperation *operation); + void COM_cl_enqueue_range(cl_kernel kernel, MemoryBuffer *output_memory_buffer); + void COM_cl_enqueue_range(cl_kernel kernel, + MemoryBuffer *output_memory_buffer, + int offset_index, + NodeOperation *operation); + cl_kernel COM_cl_create_kernel(const char *kernelname, + std::list *cl_kernels_to_clean_up); }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc index 83e3f033b32..fab6c352a02 100644 --- a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc +++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc @@ -22,42 +22,42 @@ namespace blender::compositor { SingleThreadedOperation::SingleThreadedOperation() { - cachedInstance_ = nullptr; + cached_instance_ = nullptr; flags.complex = true; flags.single_threaded = true; } -void SingleThreadedOperation::initExecution() +void SingleThreadedOperation::init_execution() { - initMutex(); + init_mutex(); } -void SingleThreadedOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void SingleThreadedOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { - cachedInstance_->readNoCheck(output, x, y); + cached_instance_->read_no_check(output, x, y); } -void SingleThreadedOperation::deinitExecution() +void SingleThreadedOperation::deinit_execution() { - deinitMutex(); - if (cachedInstance_) { - delete cachedInstance_; - cachedInstance_ = nullptr; + deinit_mutex(); + if (cached_instance_) { + delete cached_instance_; + cached_instance_ = nullptr; } } -void *SingleThreadedOperation::initializeTileData(rcti *rect) +void *SingleThreadedOperation::initialize_tile_data(rcti *rect) { - if (cachedInstance_) { - return cachedInstance_; + if (cached_instance_) { + return cached_instance_; } - lockMutex(); - if (cachedInstance_ == nullptr) { + lock_mutex(); + if (cached_instance_ == nullptr) { // - cachedInstance_ = createMemoryBuffer(rect); + cached_instance_ = create_memory_buffer(rect); } - unlockMutex(); - return cachedInstance_; + unlock_mutex(); + return cached_instance_; } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.h b/source/blender/compositor/intern/COM_SingleThreadedOperation.h index 3f90ce96e00..7588e654f75 100644 --- a/source/blender/compositor/intern/COM_SingleThreadedOperation.h +++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.h @@ -24,12 +24,12 @@ namespace blender::compositor { class SingleThreadedOperation : public NodeOperation { private: - MemoryBuffer *cachedInstance_; + MemoryBuffer *cached_instance_; protected: - inline bool isCached() + inline bool is_cached() { - return cachedInstance_ != nullptr; + return cached_instance_ != nullptr; } public: @@ -38,21 +38,21 @@ class SingleThreadedOperation : public NodeOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - virtual MemoryBuffer *createMemoryBuffer(rcti *rect) = 0; + virtual MemoryBuffer *create_memory_buffer(rcti *rect) = 0; }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc index a081b80349d..e9f7310cead 100644 --- a/source/blender/compositor/intern/COM_TiledExecutionModel.cc +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc @@ -35,24 +35,24 @@ TiledExecutionModel::TiledExecutionModel(CompositorContext &context, Span groups) : ExecutionModel(context, operations), groups_(groups) { - const bNodeTree *node_tree = context.getbNodeTree(); + const bNodeTree *node_tree = context.get_bnodetree(); node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Determining resolution")); unsigned int resolution[2]; for (ExecutionGroup *group : groups_) { resolution[0] = 0; resolution[1] = 0; - group->determineResolution(resolution); + group->determine_resolution(resolution); if (border_.use_render_border) { const rctf *render_border = border_.render_border; - group->setRenderBorder( + group->set_render_border( render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax); } if (border_.use_viewer_border) { const rctf *viewer_border = border_.viewer_border; - group->setViewerBorder( + group->set_viewer_border( viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax); } } @@ -63,8 +63,8 @@ static void update_read_buffer_offset(Span operations) unsigned int order = 0; for (NodeOperation *operation : operations) { if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = (ReadBufferOperation *)operation; - readOperation->setOffset(order); + ReadBufferOperation *read_operation = (ReadBufferOperation *)operation; + read_operation->set_offset(order); order++; } } @@ -75,8 +75,8 @@ static void init_write_operations_for_execution(Span operations { for (NodeOperation *operation : operations) { if (operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); + operation->set_bnodetree(bTree); + operation->init_execution(); } } } @@ -85,8 +85,8 @@ static void link_write_buffers(Span operations) { for (NodeOperation *operation : operations) { if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = static_cast(operation); - readOperation->updateMemoryBuffer(); + ReadBufferOperation *read_operation = static_cast(operation); + read_operation->update_memory_buffer(); } } } @@ -96,8 +96,8 @@ static void init_non_write_operations_for_execution(Span operat { for (NodeOperation *operation : operations) { if (!operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); + operation->set_bnodetree(bTree); + operation->init_execution(); } } } @@ -106,27 +106,27 @@ static void init_execution_groups_for_execution(Span groups, const int chunk_size) { for (ExecutionGroup *execution_group : groups) { - execution_group->setChunksize(chunk_size); - execution_group->initExecution(); + execution_group->set_chunksize(chunk_size); + execution_group->init_execution(); } } void TiledExecutionModel::execute(ExecutionSystem &exec_system) { - const bNodeTree *editingtree = this->context_.getbNodeTree(); + const bNodeTree *editingtree = this->context_.get_bnodetree(); editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution")); update_read_buffer_offset(operations_); - init_write_operations_for_execution(operations_, context_.getbNodeTree()); + init_write_operations_for_execution(operations_, context_.get_bnodetree()); link_write_buffers(operations_); - init_non_write_operations_for_execution(operations_, context_.getbNodeTree()); - init_execution_groups_for_execution(groups_, context_.getChunksize()); + init_non_write_operations_for_execution(operations_, context_.get_bnodetree()); + init_execution_groups_for_execution(groups_, context_.get_chunksize()); WorkScheduler::start(context_); execute_groups(eCompositorPriority::High, exec_system); - if (!context_.isFastCalculation()) { + if (!context_.is_fast_calculation()) { execute_groups(eCompositorPriority::Medium, exec_system); execute_groups(eCompositorPriority::Low, exec_system); } @@ -136,11 +136,11 @@ void TiledExecutionModel::execute(ExecutionSystem &exec_system) editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution")); for (NodeOperation *operation : operations_) { - operation->deinitExecution(); + operation->deinit_execution(); } for (ExecutionGroup *execution_group : groups_) { - execution_group->deinitExecution(); + execution_group->deinit_execution(); } } @@ -149,7 +149,7 @@ void TiledExecutionModel::execute_groups(eCompositorPriority priority, { for (ExecutionGroup *execution_group : groups_) { if (execution_group->get_flags().is_output && - execution_group->getRenderPriority() == priority) { + execution_group->get_render_priority() == priority) { execution_group->execute(&exec_system); } } diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h index 5ea3237d412..1fe50b9ecf3 100644 --- a/source/blender/compositor/intern/COM_WorkPackage.h +++ b/source/blender/compositor/intern/COM_WorkPackage.h @@ -43,7 +43,7 @@ struct WorkPackage { eWorkPackageState state = eWorkPackageState::NotScheduled; /** - * \brief executionGroup with the operations-setup to be evaluated + * \brief execution_group with the operations-setup to be evaluated */ ExecutionGroup *execution_group; diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index 22f60ec17fe..c88cc556e72 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -104,10 +104,10 @@ static struct { /** \name OpenCL Scheduling * \{ */ -static void CL_CALLBACK clContextError(const char *errinfo, - const void * /*private_info*/, - size_t /*cb*/, - void * /*user_data*/) +static void CL_CALLBACK cl_context_error(const char *errinfo, + const void * /*private_info*/, + size_t /*cb*/, + void * /*user_data*/) { printf("OPENCL error: %s\n", errinfo); } @@ -126,7 +126,7 @@ static void *thread_execute_gpu(void *data) static void opencl_start(const CompositorContext &context) { - if (context.getHasActiveOpenCLDevices()) { + if (context.get_has_active_opencl_devices()) { g_work_scheduler.opencl.queue = BLI_thread_queue_init(); BLI_threadpool_init(&g_work_scheduler.opencl.threads, thread_execute_gpu, @@ -186,35 +186,35 @@ static void opencl_initialize(const bool use_opencl) } if (clCreateContextFromType) { - cl_uint numberOfPlatforms = 0; + cl_uint number_of_platforms = 0; cl_int error; - error = clGetPlatformIDs(0, nullptr, &numberOfPlatforms); + error = clGetPlatformIDs(0, nullptr, &number_of_platforms); if (error == -1001) { } /* GPU not supported */ else if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } if (G.f & G_DEBUG) { - printf("%u number of platforms\n", numberOfPlatforms); + printf("%u number of platforms\n", number_of_platforms); } cl_platform_id *platforms = (cl_platform_id *)MEM_mallocN( - sizeof(cl_platform_id) * numberOfPlatforms, __func__); - error = clGetPlatformIDs(numberOfPlatforms, platforms, nullptr); - unsigned int indexPlatform; - for (indexPlatform = 0; indexPlatform < numberOfPlatforms; indexPlatform++) { - cl_platform_id platform = platforms[indexPlatform]; - cl_uint numberOfDevices = 0; - clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, nullptr, &numberOfDevices); - if (numberOfDevices <= 0) { + sizeof(cl_platform_id) * number_of_platforms, __func__); + error = clGetPlatformIDs(number_of_platforms, platforms, nullptr); + unsigned int index_platform; + for (index_platform = 0; index_platform < number_of_platforms; index_platform++) { + cl_platform_id platform = platforms[index_platform]; + cl_uint number_of_devices = 0; + clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, nullptr, &number_of_devices); + if (number_of_devices <= 0) { continue; } cl_device_id *cldevices = (cl_device_id *)MEM_mallocN( - sizeof(cl_device_id) * numberOfDevices, __func__); - clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, numberOfDevices, cldevices, nullptr); + sizeof(cl_device_id) * number_of_devices, __func__); + clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, number_of_devices, cldevices, nullptr); g_work_scheduler.opencl.context = clCreateContext( - nullptr, numberOfDevices, cldevices, clContextError, nullptr, &error); + nullptr, number_of_devices, cldevices, cl_context_error, nullptr, &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } @@ -222,7 +222,7 @@ static void opencl_initialize(const bool use_opencl) g_work_scheduler.opencl.program = clCreateProgramWithSource( g_work_scheduler.opencl.context, 1, cl_str, nullptr, &error); error = clBuildProgram(g_work_scheduler.opencl.program, - numberOfDevices, + number_of_devices, cldevices, nullptr, nullptr, @@ -255,9 +255,9 @@ static void opencl_initialize(const bool use_opencl) MEM_freeN(build_log); } else { - unsigned int indexDevices; - for (indexDevices = 0; indexDevices < numberOfDevices; indexDevices++) { - cl_device_id device = cldevices[indexDevices]; + unsigned int index_devices; + for (index_devices = 0; index_devices < number_of_devices; index_devices++) { + cl_device_id device = cldevices[index_devices]; cl_int vendorID = 0; cl_int error2 = clGetDeviceInfo( device, CL_DEVICE_VENDOR_ID, sizeof(cl_int), &vendorID, nullptr); diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h index d0fa3286a3b..2c60a6f2a8a 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.h +++ b/source/blender/compositor/intern/COM_WorkScheduler.h @@ -81,7 +81,7 @@ struct WorkScheduler { * \brief Are there OpenCL capable GPU devices initialized? * the result of this method is stored in the CompositorContext * A node can generate a different operation tree when OpenCLDevices exists. - * \see CompositorContext.getHasActiveOpenCLDevices + * \see CompositorContext.get_has_active_opencl_devices */ static bool has_gpu_devices(); diff --git a/source/blender/compositor/intern/COM_compositor.cc b/source/blender/compositor/intern/COM_compositor.cc index ed32a477384..be70ae792cb 100644 --- a/source/blender/compositor/intern/COM_compositor.cc +++ b/source/blender/compositor/intern/COM_compositor.cc @@ -64,9 +64,9 @@ void COM_execute(RenderData *render_data, Scene *scene, bNodeTree *node_tree, int rendering, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName) + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name) { /* Initialize mutex, TODO: this mutex init is actually not thread safe and * should be done somewhere as part of blender startup, all the other @@ -95,8 +95,14 @@ void COM_execute(RenderData *render_data, /* Execute. */ const bool twopass = (node_tree->flag & NTREE_TWO_PASS) && !rendering; if (twopass) { - blender::compositor::ExecutionSystem fast_pass( - render_data, scene, node_tree, rendering, true, viewSettings, displaySettings, viewName); + blender::compositor::ExecutionSystem fast_pass(render_data, + scene, + node_tree, + rendering, + true, + view_settings, + display_settings, + view_name); fast_pass.execute(); if (node_tree->test_break(node_tree->tbh)) { @@ -106,7 +112,7 @@ void COM_execute(RenderData *render_data, } blender::compositor::ExecutionSystem system( - render_data, scene, node_tree, rendering, false, viewSettings, displaySettings, viewName); + render_data, scene, node_tree, rendering, false, view_settings, display_settings, view_name); system.execute(); BLI_mutex_unlock(&g_compositor.mutex); diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.cc b/source/blender/compositor/nodes/COM_AlphaOverNode.cc index 603f713a43f..42836d0a575 100644 --- a/source/blender/compositor/nodes/COM_AlphaOverNode.cc +++ b/source/blender/compositor/nodes/COM_AlphaOverNode.cc @@ -24,43 +24,43 @@ namespace blender::compositor { -void AlphaOverNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void AlphaOverNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *color1Socket = this->getInputSocket(1); - NodeInput *color2Socket = this->getInputSocket(2); - bNode *editorNode = this->getbNode(); + NodeInput *color1Socket = this->get_input_socket(1); + NodeInput *color2Socket = this->get_input_socket(2); + bNode *editor_node = this->get_bnode(); - MixBaseOperation *convertProg; - NodeTwoFloats *ntf = (NodeTwoFloats *)editorNode->storage; + MixBaseOperation *convert_prog; + NodeTwoFloats *ntf = (NodeTwoFloats *)editor_node->storage; if (ntf->x != 0.0f) { - AlphaOverMixedOperation *mixOperation = new AlphaOverMixedOperation(); - mixOperation->setX(ntf->x); - convertProg = mixOperation; + AlphaOverMixedOperation *mix_operation = new AlphaOverMixedOperation(); + mix_operation->setX(ntf->x); + convert_prog = mix_operation; } - else if (editorNode->custom1) { - convertProg = new AlphaOverKeyOperation(); + else if (editor_node->custom1) { + convert_prog = new AlphaOverKeyOperation(); } else { - convertProg = new AlphaOverPremultiplyOperation(); + convert_prog = new AlphaOverPremultiplyOperation(); } - convertProg->setUseValueAlphaMultiply(false); - if (color1Socket->isLinked()) { - convertProg->set_canvas_input_index(1); + convert_prog->set_use_value_alpha_multiply(false); + if (color1Socket->is_linked()) { + convert_prog->set_canvas_input_index(1); } - else if (color2Socket->isLinked()) { - convertProg->set_canvas_input_index(2); + else if (color2Socket->is_linked()) { + convert_prog->set_canvas_input_index(2); } else { - convertProg->set_canvas_input_index(0); + convert_prog->set_canvas_input_index(0); } - converter.addOperation(convertProg); - converter.mapInputSocket(getInputSocket(0), convertProg->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), convertProg->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), convertProg->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), convertProg->getOutputSocket(0)); + converter.add_operation(convert_prog); + converter.map_input_socket(get_input_socket(0), convert_prog->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), convert_prog->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), convert_prog->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), convert_prog->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.h b/source/blender/compositor/nodes/COM_AlphaOverNode.h index 201c8ed5b6e..447570fe604 100644 --- a/source/blender/compositor/nodes/COM_AlphaOverNode.h +++ b/source/blender/compositor/nodes/COM_AlphaOverNode.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class AlphaOverNode : public Node { public: - AlphaOverNode(bNode *editorNode) : Node(editorNode) + AlphaOverNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc index 8cbb3994939..b11c57041d9 100644 --- a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc +++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc @@ -23,37 +23,37 @@ namespace blender::compositor { -void AntiAliasingNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void AntiAliasingNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeAntiAliasingData *data = (NodeAntiAliasingData *)node->storage; /* Edge Detection (First Pass) */ SMAAEdgeDetectionOperation *operation1 = nullptr; operation1 = new SMAAEdgeDetectionOperation(); - operation1->setThreshold(data->threshold); - operation1->setLocalContrastAdaptationFactor(data->contrast_limit); - converter.addOperation(operation1); + operation1->set_threshold(data->threshold); + operation1->set_local_contrast_adaptation_factor(data->contrast_limit); + converter.add_operation(operation1); - converter.mapInputSocket(getInputSocket(0), operation1->getInputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation1->get_input_socket(0)); /* Blending Weight Calculation Pixel Shader (Second Pass) */ SMAABlendingWeightCalculationOperation *operation2 = new SMAABlendingWeightCalculationOperation(); - operation2->setCornerRounding(data->corner_rounding); - converter.addOperation(operation2); + operation2->set_corner_rounding(data->corner_rounding); + converter.add_operation(operation2); - converter.addLink(operation1->getOutputSocket(), operation2->getInputSocket(0)); + converter.add_link(operation1->get_output_socket(), operation2->get_input_socket(0)); /* Neighborhood Blending Pixel Shader (Third Pass) */ SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation(); - converter.addOperation(operation3); + converter.add_operation(operation3); - converter.mapInputSocket(getInputSocket(0), operation3->getInputSocket(0)); - converter.addLink(operation2->getOutputSocket(), operation3->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation3->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation3->get_input_socket(0)); + converter.add_link(operation2->get_output_socket(), operation3->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation3->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.h b/source/blender/compositor/nodes/COM_AntiAliasingNode.h index 7d3dd750864..05c51d5856a 100644 --- a/source/blender/compositor/nodes/COM_AntiAliasingNode.h +++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.h @@ -30,11 +30,11 @@ namespace blender::compositor { */ class AntiAliasingNode : public Node { public: - AntiAliasingNode(bNode *editorNode) : Node(editorNode) + AntiAliasingNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BilateralBlurNode.cc b/source/blender/compositor/nodes/COM_BilateralBlurNode.cc index 40a70e37e4b..2390f6305b2 100644 --- a/source/blender/compositor/nodes/COM_BilateralBlurNode.cc +++ b/source/blender/compositor/nodes/COM_BilateralBlurNode.cc @@ -21,23 +21,23 @@ namespace blender::compositor { -BilateralBlurNode::BilateralBlurNode(bNode *editorNode) : Node(editorNode) +BilateralBlurNode::BilateralBlurNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BilateralBlurNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void BilateralBlurNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeBilateralBlurData *data = (NodeBilateralBlurData *)this->getbNode()->storage; + NodeBilateralBlurData *data = (NodeBilateralBlurData *)this->get_bnode()->storage; BilateralBlurOperation *operation = new BilateralBlurOperation(); - operation->setQuality(context.getQuality()); - operation->setData(data); + operation->set_quality(context.get_quality()); + operation->set_data(data); - converter.addOperation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.add_operation(operation); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BilateralBlurNode.h b/source/blender/compositor/nodes/COM_BilateralBlurNode.h index fed2612ac02..6bef897f2ef 100644 --- a/source/blender/compositor/nodes/COM_BilateralBlurNode.h +++ b/source/blender/compositor/nodes/COM_BilateralBlurNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BilateralBlurNode : public Node { public: - BilateralBlurNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BilateralBlurNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BlurNode.cc b/source/blender/compositor/nodes/COM_BlurNode.cc index 5dfd5c8d0bc..97a7159de67 100644 --- a/source/blender/compositor/nodes/COM_BlurNode.cc +++ b/source/blender/compositor/nodes/COM_BlurNode.cc @@ -29,103 +29,103 @@ namespace blender::compositor { -BlurNode::BlurNode(bNode *editorNode) : Node(editorNode) +BlurNode::BlurNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BlurNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void BlurNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - NodeBlurData *data = (NodeBlurData *)editorNode->storage; - NodeInput *inputSizeSocket = this->getInputSocket(1); - bool connectedSizeSocket = inputSizeSocket->isLinked(); + bNode *editor_node = this->get_bnode(); + NodeBlurData *data = (NodeBlurData *)editor_node->storage; + NodeInput *input_size_socket = this->get_input_socket(1); + bool connected_size_socket = input_size_socket->is_linked(); - const float size = this->getInputSocket(1)->getEditorValueFloat(); - const bool extend_bounds = (editorNode->custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS) != 0; + const float size = this->get_input_socket(1)->get_editor_value_float(); + const bool extend_bounds = (editor_node->custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS) != 0; - eCompositorQuality quality = context.getQuality(); + eCompositorQuality quality = context.get_quality(); NodeOperation *input_operation = nullptr, *output_operation = nullptr; if (data->filtertype == R_FILTER_FAST_GAUSS) { FastGaussianBlurOperation *operationfgb = new FastGaussianBlurOperation(); - operationfgb->setData(data); - operationfgb->setExtendBounds(extend_bounds); - converter.addOperation(operationfgb); + operationfgb->set_data(data); + operationfgb->set_extend_bounds(extend_bounds); + converter.add_operation(operationfgb); - converter.mapInputSocket(getInputSocket(1), operationfgb->getInputSocket(1)); + converter.map_input_socket(get_input_socket(1), operationfgb->get_input_socket(1)); input_operation = operationfgb; output_operation = operationfgb; } - else if (editorNode->custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE) { + else if (editor_node->custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE) { MathAddOperation *clamp = new MathAddOperation(); SetValueOperation *zero = new SetValueOperation(); - zero->setValue(0.0f); - clamp->setUseClamp(true); + zero->set_value(0.0f); + clamp->set_use_clamp(true); - converter.addOperation(clamp); - converter.addOperation(zero); - converter.mapInputSocket(getInputSocket(1), clamp->getInputSocket(0)); - converter.addLink(zero->getOutputSocket(), clamp->getInputSocket(1)); + converter.add_operation(clamp); + converter.add_operation(zero); + 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->setData(data); - operationx->setQuality(quality); - operationx->setSize(1.0f); - operationx->setFalloff(PROP_SMOOTH); - operationx->setSubtract(false); - operationx->setExtendBounds(extend_bounds); + 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.addOperation(operationx); - converter.addLink(clamp->getOutputSocket(), operationx->getInputSocket(0)); + converter.add_operation(operationx); + converter.add_link(clamp->get_output_socket(), operationx->get_input_socket(0)); GaussianAlphaYBlurOperation *operationy = new GaussianAlphaYBlurOperation(); - operationy->setData(data); - operationy->setQuality(quality); - operationy->setSize(1.0f); - operationy->setFalloff(PROP_SMOOTH); - operationy->setSubtract(false); - operationy->setExtendBounds(extend_bounds); + 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.addOperation(operationy); - converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0)); + converter.add_operation(operationy); + converter.add_link(operationx->get_output_socket(), operationy->get_input_socket(0)); GaussianBlurReferenceOperation *operation = new GaussianBlurReferenceOperation(); - operation->setData(data); - operation->setQuality(quality); - operation->setExtendBounds(extend_bounds); + operation->set_data(data); + operation->set_quality(quality); + operation->set_extend_bounds(extend_bounds); - converter.addOperation(operation); - converter.addLink(operationy->getOutputSocket(), operation->getInputSocket(1)); + converter.add_operation(operation); + converter.add_link(operationy->get_output_socket(), operation->get_input_socket(1)); output_operation = operation; input_operation = operation; } else if (!data->bokeh) { GaussianXBlurOperation *operationx = new GaussianXBlurOperation(); - operationx->setData(data); - operationx->setQuality(quality); - operationx->checkOpenCL(); - operationx->setExtendBounds(extend_bounds); + operationx->set_data(data); + operationx->set_quality(quality); + operationx->check_opencl(); + operationx->set_extend_bounds(extend_bounds); - converter.addOperation(operationx); - converter.mapInputSocket(getInputSocket(1), operationx->getInputSocket(1)); + converter.add_operation(operationx); + converter.map_input_socket(get_input_socket(1), operationx->get_input_socket(1)); GaussianYBlurOperation *operationy = new GaussianYBlurOperation(); - operationy->setData(data); - operationy->setQuality(quality); - operationy->checkOpenCL(); - operationy->setExtendBounds(extend_bounds); + operationy->set_data(data); + operationy->set_quality(quality); + operationy->check_opencl(); + operationy->set_extend_bounds(extend_bounds); - converter.addOperation(operationy); - converter.mapInputSocket(getInputSocket(1), operationy->getInputSocket(1)); - converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0)); + converter.add_operation(operationy); + converter.map_input_socket(get_input_socket(1), operationy->get_input_socket(1)); + converter.add_link(operationx->get_output_socket(), operationy->get_input_socket(0)); - if (!connectedSizeSocket) { - operationx->setSize(size); - operationy->setSize(size); + if (!connected_size_socket) { + operationx->set_size(size); + operationy->set_size(size); } input_operation = operationx; @@ -133,15 +133,15 @@ void BlurNode::convertToOperations(NodeConverter &converter, } else { GaussianBokehBlurOperation *operation = new GaussianBokehBlurOperation(); - operation->setData(data); - operation->setQuality(quality); - operation->setExtendBounds(extend_bounds); + operation->set_data(data); + operation->set_quality(quality); + operation->set_extend_bounds(extend_bounds); - converter.addOperation(operation); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.add_operation(operation); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); - if (!connectedSizeSocket) { - operation->setSize(size); + if (!connected_size_socket) { + operation->set_size(size); } input_operation = operation; @@ -151,21 +151,21 @@ void BlurNode::convertToOperations(NodeConverter &converter, if (data->gamma) { GammaCorrectOperation *correct = new GammaCorrectOperation(); GammaUncorrectOperation *inverse = new GammaUncorrectOperation(); - converter.addOperation(correct); - converter.addOperation(inverse); + converter.add_operation(correct); + converter.add_operation(inverse); - converter.mapInputSocket(getInputSocket(0), correct->getInputSocket(0)); - converter.addLink(correct->getOutputSocket(), input_operation->getInputSocket(0)); - converter.addLink(output_operation->getOutputSocket(), inverse->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(), inverse->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), correct->get_input_socket(0)); + converter.add_link(correct->get_output_socket(), input_operation->get_input_socket(0)); + converter.add_link(output_operation->get_output_socket(), inverse->get_input_socket(0)); + converter.map_output_socket(get_output_socket(), inverse->get_output_socket()); - converter.addPreview(inverse->getOutputSocket()); + converter.add_preview(inverse->get_output_socket()); } else { - converter.mapInputSocket(getInputSocket(0), input_operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(), output_operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), input_operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(), output_operation->get_output_socket()); - converter.addPreview(output_operation->getOutputSocket()); + converter.add_preview(output_operation->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_BlurNode.h b/source/blender/compositor/nodes/COM_BlurNode.h index 61cdc17f3a9..816f359cab1 100644 --- a/source/blender/compositor/nodes/COM_BlurNode.h +++ b/source/blender/compositor/nodes/COM_BlurNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BlurNode : public Node { public: - BlurNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BlurNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.cc b/source/blender/compositor/nodes/COM_BokehBlurNode.cc index c18f6351cbd..8f71621c435 100644 --- a/source/blender/compositor/nodes/COM_BokehBlurNode.cc +++ b/source/blender/compositor/nodes/COM_BokehBlurNode.cc @@ -22,52 +22,52 @@ namespace blender::compositor { -BokehBlurNode::BokehBlurNode(bNode *editorNode) : Node(editorNode) +BokehBlurNode::BokehBlurNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BokehBlurNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void BokehBlurNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *b_node = this->getbNode(); + bNode *b_node = this->get_bnode(); - NodeInput *inputSizeSocket = this->getInputSocket(2); + NodeInput *input_size_socket = this->get_input_socket(2); - bool connectedSizeSocket = inputSizeSocket->isLinked(); + bool connected_size_socket = input_size_socket->is_linked(); const bool extend_bounds = (b_node->custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS) != 0; - if ((b_node->custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE) && connectedSizeSocket) { + if ((b_node->custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE) && connected_size_socket) { VariableSizeBokehBlurOperation *operation = new VariableSizeBokehBlurOperation(); - operation->setQuality(context.getQuality()); - operation->setThreshold(0.0f); - operation->setMaxBlur(b_node->custom4); - operation->setDoScaleSize(true); + operation->set_quality(context.get_quality()); + operation->set_threshold(0.0f); + operation->set_max_blur(b_node->custom4); + operation->set_do_scale_size(true); - converter.addOperation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } else { BokehBlurOperation *operation = new BokehBlurOperation(); - operation->setQuality(context.getQuality()); - operation->setExtendBounds(extend_bounds); + operation->set_quality(context.get_quality()); + operation->set_extend_bounds(extend_bounds); - converter.addOperation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.add_operation(operation); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); /* NOTE: on the bokeh blur operation the sockets are switched. * for this reason the next two lines are correct. Fix for T43771. */ - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(3)); - converter.mapInputSocket(getInputSocket(3), operation->getInputSocket(2)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(3)); + converter.map_input_socket(get_input_socket(3), operation->get_input_socket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); - if (!connectedSizeSocket) { - operation->setSize(this->getInputSocket(2)->getEditorValueFloat()); + if (!connected_size_socket) { + operation->set_size(this->get_input_socket(2)->get_editor_value_float()); } } } diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.h b/source/blender/compositor/nodes/COM_BokehBlurNode.h index 2c060936025..869eee44a6f 100644 --- a/source/blender/compositor/nodes/COM_BokehBlurNode.h +++ b/source/blender/compositor/nodes/COM_BokehBlurNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BokehBlurNode : public Node { public: - BokehBlurNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BokehBlurNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.cc b/source/blender/compositor/nodes/COM_BokehImageNode.cc index 64237571e90..c860ef7a1c9 100644 --- a/source/blender/compositor/nodes/COM_BokehImageNode.cc +++ b/source/blender/compositor/nodes/COM_BokehImageNode.cc @@ -21,21 +21,21 @@ namespace blender::compositor { -BokehImageNode::BokehImageNode(bNode *editorNode) : Node(editorNode) +BokehImageNode::BokehImageNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BokehImageNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void BokehImageNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { BokehImageOperation *operation = new BokehImageOperation(); - operation->setData((NodeBokehImage *)this->getbNode()->storage); + operation->set_data((NodeBokehImage *)this->get_bnode()->storage); - converter.addOperation(operation); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.add_operation(operation); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); - converter.addPreview(operation->getOutputSocket(0)); + converter.add_preview(operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.h b/source/blender/compositor/nodes/COM_BokehImageNode.h index 323561a7e4f..cb685c93237 100644 --- a/source/blender/compositor/nodes/COM_BokehImageNode.h +++ b/source/blender/compositor/nodes/COM_BokehImageNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BokehImageNode : public Node { public: - BokehImageNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BokehImageNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.cc b/source/blender/compositor/nodes/COM_BoxMaskNode.cc index 6fcaeada406..1cf05727a0a 100644 --- a/source/blender/compositor/nodes/COM_BoxMaskNode.cc +++ b/source/blender/compositor/nodes/COM_BoxMaskNode.cc @@ -24,52 +24,53 @@ namespace blender::compositor { -BoxMaskNode::BoxMaskNode(bNode *editorNode) : Node(editorNode) +BoxMaskNode::BoxMaskNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BoxMaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void BoxMaskNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); BoxMaskOperation *operation; operation = new BoxMaskOperation(); - operation->setData((NodeBoxMask *)this->getbNode()->storage); - operation->setMaskType(this->getbNode()->custom1); - converter.addOperation(operation); + operation->set_data((NodeBoxMask *)this->get_bnode()->storage); + operation->set_mask_type(this->get_bnode()->custom1); + converter.add_operation(operation); - if (inputSocket->isLinked()) { - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + if (input_socket->is_linked()) { + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket()); } else { /* Value operation to produce original transparent image */ - SetValueOperation *valueOperation = new SetValueOperation(); - valueOperation->setValue(0.0f); - converter.addOperation(valueOperation); + SetValueOperation *value_operation = new SetValueOperation(); + value_operation->set_value(0.0f); + converter.add_operation(value_operation); /* Scale that image up to render resolution */ - const RenderData *rd = context.getRenderData(); - const float render_size_factor = context.getRenderPercentageAsFactor(); - ScaleFixedSizeOperation *scaleOperation = new ScaleFixedSizeOperation(); + const RenderData *rd = context.get_render_data(); + const float render_size_factor = context.get_render_percentage_as_factor(); + ScaleFixedSizeOperation *scale_operation = new ScaleFixedSizeOperation(); - scaleOperation->setIsAspect(false); - scaleOperation->setIsCrop(false); - scaleOperation->setOffset(0.0f, 0.0f); - scaleOperation->setNewWidth(rd->xsch * render_size_factor); - scaleOperation->setNewHeight(rd->ysch * render_size_factor); - scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align); - converter.addOperation(scaleOperation); + scale_operation->set_is_aspect(false); + scale_operation->set_is_crop(false); + scale_operation->set_offset(0.0f, 0.0f); + scale_operation->set_new_width(rd->xsch * render_size_factor); + scale_operation->set_new_height(rd->ysch * render_size_factor); + scale_operation->get_input_socket(0)->set_resize_mode(ResizeMode::Align); + converter.add_operation(scale_operation); - converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0)); - converter.addLink(scaleOperation->getOutputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.add_link(value_operation->get_output_socket(0), + scale_operation->get_input_socket(0)); + converter.add_link(scale_operation->get_output_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.h b/source/blender/compositor/nodes/COM_BoxMaskNode.h index 46cedf7af75..68d98f6c0ea 100644 --- a/source/blender/compositor/nodes/COM_BoxMaskNode.h +++ b/source/blender/compositor/nodes/COM_BoxMaskNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BoxMaskNode : public Node { public: - BoxMaskNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BoxMaskNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.cc b/source/blender/compositor/nodes/COM_BrightnessNode.cc index e01ec3d8729..0b87d5d3d49 100644 --- a/source/blender/compositor/nodes/COM_BrightnessNode.cc +++ b/source/blender/compositor/nodes/COM_BrightnessNode.cc @@ -21,23 +21,23 @@ namespace blender::compositor { -BrightnessNode::BrightnessNode(bNode *editorNode) : Node(editorNode) +BrightnessNode::BrightnessNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BrightnessNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void BrightnessNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); BrightnessOperation *operation = new BrightnessOperation(); - operation->setUsePremultiply((bnode->custom1 & 1) != 0); - converter.addOperation(operation); + operation->set_use_premultiply((bnode->custom1 & 1) != 0); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.h b/source/blender/compositor/nodes/COM_BrightnessNode.h index 1084108b1c3..82a383557ba 100644 --- a/source/blender/compositor/nodes/COM_BrightnessNode.h +++ b/source/blender/compositor/nodes/COM_BrightnessNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BrightnessNode : public Node { public: - BrightnessNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BrightnessNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc index ed49d9c36a9..cf8e8a7392d 100644 --- a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc @@ -23,19 +23,19 @@ namespace blender::compositor { -ChannelMatteNode::ChannelMatteNode(bNode *editorNode) : Node(editorNode) +ChannelMatteNode::ChannelMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ChannelMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ChannelMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); - NodeInput *inputSocketImage = this->getInputSocket(0); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); + NodeInput *input_socket_image = this->get_input_socket(0); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); NodeOperation *convert = nullptr, *inv_convert = nullptr; /* colorspace */ @@ -52,9 +52,9 @@ void ChannelMatteNode::convertToOperations(NodeConverter &converter, break; case CMP_NODE_CHANNEL_MATTE_CS_YCC: /* YCC */ convert = new ConvertRGBToYCCOperation(); - ((ConvertRGBToYCCOperation *)convert)->setMode(BLI_YCC_ITU_BT709); + ((ConvertRGBToYCCOperation *)convert)->set_mode(BLI_YCC_ITU_BT709); inv_convert = new ConvertYCCToRGBOperation(); - ((ConvertYCCToRGBOperation *)inv_convert)->setMode(BLI_YCC_ITU_BT709); + ((ConvertYCCToRGBOperation *)inv_convert)->set_mode(BLI_YCC_ITU_BT709); break; default: break; @@ -62,36 +62,36 @@ void ChannelMatteNode::convertToOperations(NodeConverter &converter, ChannelMatteOperation *operation = new ChannelMatteOperation(); /* pass the ui properties to the operation */ - operation->setSettings((NodeChroma *)node->storage, node->custom2); - converter.addOperation(operation); + operation->set_settings((NodeChroma *)node->storage, node->custom2); + converter.add_operation(operation); - SetAlphaMultiplyOperation *operationAlpha = new SetAlphaMultiplyOperation(); - converter.addOperation(operationAlpha); + SetAlphaMultiplyOperation *operation_alpha = new SetAlphaMultiplyOperation(); + converter.add_operation(operation_alpha); if (convert != nullptr) { - converter.addOperation(convert); + converter.add_operation(convert); - converter.mapInputSocket(inputSocketImage, convert->getInputSocket(0)); - converter.addLink(convert->getOutputSocket(), operation->getInputSocket(0)); - converter.addLink(convert->getOutputSocket(), operationAlpha->getInputSocket(0)); + converter.map_input_socket(input_socket_image, convert->get_input_socket(0)); + converter.add_link(convert->get_output_socket(), operation->get_input_socket(0)); + converter.add_link(convert->get_output_socket(), operation_alpha->get_input_socket(0)); } else { - converter.mapInputSocket(inputSocketImage, operation->getInputSocket(0)); - converter.mapInputSocket(inputSocketImage, operationAlpha->getInputSocket(0)); + converter.map_input_socket(input_socket_image, operation->get_input_socket(0)); + converter.map_input_socket(input_socket_image, operation_alpha->get_input_socket(0)); } - converter.mapOutputSocket(outputSocketMatte, operation->getOutputSocket(0)); - converter.addLink(operation->getOutputSocket(), operationAlpha->getInputSocket(1)); + converter.map_output_socket(output_socket_matte, operation->get_output_socket(0)); + converter.add_link(operation->get_output_socket(), operation_alpha->get_input_socket(1)); if (inv_convert != nullptr) { - converter.addOperation(inv_convert); - converter.addLink(operationAlpha->getOutputSocket(0), inv_convert->getInputSocket(0)); - converter.mapOutputSocket(outputSocketImage, inv_convert->getOutputSocket()); - converter.addPreview(inv_convert->getOutputSocket()); + converter.add_operation(inv_convert); + converter.add_link(operation_alpha->get_output_socket(0), inv_convert->get_input_socket(0)); + converter.map_output_socket(output_socket_image, inv_convert->get_output_socket()); + converter.add_preview(inv_convert->get_output_socket()); } else { - converter.mapOutputSocket(outputSocketImage, operationAlpha->getOutputSocket()); - converter.addPreview(operationAlpha->getOutputSocket()); + converter.map_output_socket(output_socket_image, operation_alpha->get_output_socket()); + converter.add_preview(operation_alpha->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.h b/source/blender/compositor/nodes/COM_ChannelMatteNode.h index 46100b3f7ea..c071c516e6d 100644 --- a/source/blender/compositor/nodes/COM_ChannelMatteNode.h +++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ChannelMatteNode : public Node { public: - ChannelMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ChannelMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc index 33516181152..8db96e0308d 100644 --- a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc @@ -23,46 +23,46 @@ namespace blender::compositor { -ChromaMatteNode::ChromaMatteNode(bNode *editorNode) : Node(editorNode) +ChromaMatteNode::ChromaMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ChromaMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ChromaMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorsnode = getbNode(); + bNode *editorsnode = get_bnode(); - NodeInput *inputSocketImage = this->getInputSocket(0); - NodeInput *inputSocketKey = this->getInputSocket(1); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); + NodeInput *input_socket_image = this->get_input_socket(0); + NodeInput *input_socket_key = this->get_input_socket(1); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); ConvertRGBToYCCOperation *operationRGBToYCC_Image = new ConvertRGBToYCCOperation(); ConvertRGBToYCCOperation *operationRGBToYCC_Key = new ConvertRGBToYCCOperation(); - operationRGBToYCC_Image->setMode(BLI_YCC_ITU_BT709); - operationRGBToYCC_Key->setMode(BLI_YCC_ITU_BT709); - converter.addOperation(operationRGBToYCC_Image); - converter.addOperation(operationRGBToYCC_Key); + operationRGBToYCC_Image->set_mode(BLI_YCC_ITU_BT709); + operationRGBToYCC_Key->set_mode(BLI_YCC_ITU_BT709); + converter.add_operation(operationRGBToYCC_Image); + converter.add_operation(operationRGBToYCC_Key); ChromaMatteOperation *operation = new ChromaMatteOperation(); - operation->setSettings((NodeChroma *)editorsnode->storage); - converter.addOperation(operation); + operation->set_settings((NodeChroma *)editorsnode->storage); + converter.add_operation(operation); - SetAlphaMultiplyOperation *operationAlpha = new SetAlphaMultiplyOperation(); - converter.addOperation(operationAlpha); + SetAlphaMultiplyOperation *operation_alpha = new SetAlphaMultiplyOperation(); + converter.add_operation(operation_alpha); - converter.mapInputSocket(inputSocketImage, operationRGBToYCC_Image->getInputSocket(0)); - converter.mapInputSocket(inputSocketKey, operationRGBToYCC_Key->getInputSocket(0)); - converter.addLink(operationRGBToYCC_Image->getOutputSocket(), operation->getInputSocket(0)); - converter.addLink(operationRGBToYCC_Key->getOutputSocket(), operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketMatte, operation->getOutputSocket()); + converter.map_input_socket(input_socket_image, operationRGBToYCC_Image->get_input_socket(0)); + converter.map_input_socket(input_socket_key, operationRGBToYCC_Key->get_input_socket(0)); + converter.add_link(operationRGBToYCC_Image->get_output_socket(), operation->get_input_socket(0)); + converter.add_link(operationRGBToYCC_Key->get_output_socket(), operation->get_input_socket(1)); + converter.map_output_socket(output_socket_matte, operation->get_output_socket()); - converter.mapInputSocket(inputSocketImage, operationAlpha->getInputSocket(0)); - converter.addLink(operation->getOutputSocket(), operationAlpha->getInputSocket(1)); - converter.mapOutputSocket(outputSocketImage, operationAlpha->getOutputSocket()); + converter.map_input_socket(input_socket_image, operation_alpha->get_input_socket(0)); + converter.add_link(operation->get_output_socket(), operation_alpha->get_input_socket(1)); + converter.map_output_socket(output_socket_image, operation_alpha->get_output_socket()); - converter.addPreview(operationAlpha->getOutputSocket()); + converter.add_preview(operation_alpha->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.h b/source/blender/compositor/nodes/COM_ChromaMatteNode.h index f3ddd013fa4..854c07c104b 100644 --- a/source/blender/compositor/nodes/COM_ChromaMatteNode.h +++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ChromaMatteNode : public Node { public: - ChromaMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ChromaMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc index 8f9d05ad1e1..e7c43161a00 100644 --- a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc +++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc @@ -22,20 +22,20 @@ namespace blender::compositor { -ColorBalanceNode::ColorBalanceNode(bNode *editorNode) : Node(editorNode) +ColorBalanceNode::ColorBalanceNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorBalanceNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorBalanceNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeColorBalance *n = (NodeColorBalance *)node->storage; - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputImageSocket = this->getInputSocket(1); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_image_socket = this->get_input_socket(1); + NodeOutput *output_socket = this->get_output_socket(0); NodeOperation *operation; if (node->custom1 == 0) { @@ -47,9 +47,9 @@ void ColorBalanceNode::convertToOperations(NodeConverter &converter, gamma_inv[c] = (n->gamma[c] != 0.0f) ? 1.0f / n->gamma[c] : 1000000.0f; } - operationLGG->setGain(n->gain); - operationLGG->setLift(lift_lgg); - operationLGG->setGammaInv(gamma_inv); + operationLGG->set_gain(n->gain); + operationLGG->set_lift(lift_lgg); + operationLGG->set_gamma_inv(gamma_inv); operation = operationLGG; } else { @@ -59,16 +59,16 @@ void ColorBalanceNode::convertToOperations(NodeConverter &converter, copy_v3_fl(offset, n->offset_basis); add_v3_v3(offset, n->offset); - operationCDL->setOffset(offset); - operationCDL->setPower(n->power); - operationCDL->setSlope(n->slope); + operationCDL->set_offset(offset); + operationCDL->set_power(n->power); + operationCDL->set_slope(n->slope); operation = operationCDL; } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputImageSocket, operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_image_socket, operation->get_input_socket(1)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.h b/source/blender/compositor/nodes/COM_ColorBalanceNode.h index 243713b4912..3ac0b7cdee1 100644 --- a/source/blender/compositor/nodes/COM_ColorBalanceNode.h +++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorBalanceNode : public Node { public: - ColorBalanceNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorBalanceNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc index b5bf118d7de..49d8a7fec98 100644 --- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc +++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc @@ -21,26 +21,26 @@ namespace blender::compositor { -ColorCorrectionNode::ColorCorrectionNode(bNode *editorNode) : Node(editorNode) +ColorCorrectionNode::ColorCorrectionNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorCorrectionNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorCorrectionNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorNode = getbNode(); + bNode *editor_node = get_bnode(); ColorCorrectionOperation *operation = new ColorCorrectionOperation(); - operation->setData((NodeColorCorrection *)editorNode->storage); - operation->setRedChannelEnabled((editorNode->custom1 & 1) != 0); - operation->setGreenChannelEnabled((editorNode->custom1 & 2) != 0); - operation->setBlueChannelEnabled((editorNode->custom1 & 4) != 0); - converter.addOperation(operation); + operation->set_data((NodeColorCorrection *)editor_node->storage); + operation->set_red_channel_enabled((editor_node->custom1 & 1) != 0); + operation->set_green_channel_enabled((editor_node->custom1 & 2) != 0); + operation->set_blue_channel_enabled((editor_node->custom1 & 4) != 0); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.h b/source/blender/compositor/nodes/COM_ColorCorrectionNode.h index aee07ee07a3..6fe4212e428 100644 --- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.h +++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorCorrectionNode : public Node { public: - ColorCorrectionNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorCorrectionNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.cc b/source/blender/compositor/nodes/COM_ColorCurveNode.cc index 4a82a42b036..5b59b2bee14 100644 --- a/source/blender/compositor/nodes/COM_ColorCurveNode.cc +++ b/source/blender/compositor/nodes/COM_ColorCurveNode.cc @@ -21,39 +21,39 @@ namespace blender::compositor { -ColorCurveNode::ColorCurveNode(bNode *editorNode) : Node(editorNode) +ColorCurveNode::ColorCurveNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorCurveNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorCurveNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - if (this->getInputSocket(2)->isLinked() || this->getInputSocket(3)->isLinked()) { + if (this->get_input_socket(2)->is_linked() || this->get_input_socket(3)->is_linked()) { ColorCurveOperation *operation = new ColorCurveOperation(); - operation->setCurveMapping((CurveMapping *)this->getbNode()->storage); - converter.addOperation(operation); + operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapInputSocket(getInputSocket(3), operation->getInputSocket(3)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_input_socket(get_input_socket(3), operation->get_input_socket(3)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } else { ConstantLevelColorCurveOperation *operation = new ConstantLevelColorCurveOperation(); float col[4]; - this->getInputSocket(2)->getEditorValueColor(col); - operation->setBlackLevel(col); - this->getInputSocket(3)->getEditorValueColor(col); - operation->setWhiteLevel(col); - operation->setCurveMapping((CurveMapping *)this->getbNode()->storage); - converter.addOperation(operation); + this->get_input_socket(2)->get_editor_value_color(col); + operation->set_black_level(col); + this->get_input_socket(3)->get_editor_value_color(col); + operation->set_white_level(col); + operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.h b/source/blender/compositor/nodes/COM_ColorCurveNode.h index 89786b47cf5..852f755bf95 100644 --- a/source/blender/compositor/nodes/COM_ColorCurveNode.h +++ b/source/blender/compositor/nodes/COM_ColorCurveNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorCurveNode : public Node { public: - ColorCurveNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorCurveNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorExposureNode.cc b/source/blender/compositor/nodes/COM_ColorExposureNode.cc index 83507395108..2f5e6b0a7bf 100644 --- a/source/blender/compositor/nodes/COM_ColorExposureNode.cc +++ b/source/blender/compositor/nodes/COM_ColorExposureNode.cc @@ -21,20 +21,20 @@ namespace blender::compositor { -ExposureNode::ExposureNode(bNode *editorNode) : Node(editorNode) +ExposureNode::ExposureNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ExposureNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ExposureNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { ExposureOperation *operation = new ExposureOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorExposureNode.h b/source/blender/compositor/nodes/COM_ColorExposureNode.h index df9bfc65f81..33b143e0676 100644 --- a/source/blender/compositor/nodes/COM_ColorExposureNode.h +++ b/source/blender/compositor/nodes/COM_ColorExposureNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ExposureNode : public Node { public: - ExposureNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ExposureNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.cc b/source/blender/compositor/nodes/COM_ColorMatteNode.cc index 345cdb3ecf7..629ae932eb7 100644 --- a/source/blender/compositor/nodes/COM_ColorMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ColorMatteNode.cc @@ -23,44 +23,44 @@ namespace blender::compositor { -ColorMatteNode::ColorMatteNode(bNode *editorNode) : Node(editorNode) +ColorMatteNode::ColorMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorsnode = getbNode(); + bNode *editorsnode = get_bnode(); - NodeInput *inputSocketImage = this->getInputSocket(0); - NodeInput *inputSocketKey = this->getInputSocket(1); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); + NodeInput *input_socket_image = this->get_input_socket(0); + NodeInput *input_socket_key = this->get_input_socket(1); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); ConvertRGBToHSVOperation *operationRGBToHSV_Image = new ConvertRGBToHSVOperation(); ConvertRGBToHSVOperation *operationRGBToHSV_Key = new ConvertRGBToHSVOperation(); - converter.addOperation(operationRGBToHSV_Image); - converter.addOperation(operationRGBToHSV_Key); + converter.add_operation(operationRGBToHSV_Image); + converter.add_operation(operationRGBToHSV_Key); ColorMatteOperation *operation = new ColorMatteOperation(); - operation->setSettings((NodeChroma *)editorsnode->storage); - converter.addOperation(operation); + operation->set_settings((NodeChroma *)editorsnode->storage); + converter.add_operation(operation); - SetAlphaMultiplyOperation *operationAlpha = new SetAlphaMultiplyOperation(); - converter.addOperation(operationAlpha); + SetAlphaMultiplyOperation *operation_alpha = new SetAlphaMultiplyOperation(); + converter.add_operation(operation_alpha); - converter.mapInputSocket(inputSocketImage, operationRGBToHSV_Image->getInputSocket(0)); - converter.mapInputSocket(inputSocketKey, operationRGBToHSV_Key->getInputSocket(0)); - converter.addLink(operationRGBToHSV_Image->getOutputSocket(), operation->getInputSocket(0)); - converter.addLink(operationRGBToHSV_Key->getOutputSocket(), operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketMatte, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket_image, operationRGBToHSV_Image->get_input_socket(0)); + converter.map_input_socket(input_socket_key, operationRGBToHSV_Key->get_input_socket(0)); + converter.add_link(operationRGBToHSV_Image->get_output_socket(), operation->get_input_socket(0)); + converter.add_link(operationRGBToHSV_Key->get_output_socket(), operation->get_input_socket(1)); + converter.map_output_socket(output_socket_matte, operation->get_output_socket(0)); - converter.mapInputSocket(inputSocketImage, operationAlpha->getInputSocket(0)); - converter.addLink(operation->getOutputSocket(), operationAlpha->getInputSocket(1)); - converter.mapOutputSocket(outputSocketImage, operationAlpha->getOutputSocket()); + converter.map_input_socket(input_socket_image, operation_alpha->get_input_socket(0)); + converter.add_link(operation->get_output_socket(), operation_alpha->get_input_socket(1)); + converter.map_output_socket(output_socket_image, operation_alpha->get_output_socket()); - converter.addPreview(operationAlpha->getOutputSocket()); + converter.add_preview(operation_alpha->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.h b/source/blender/compositor/nodes/COM_ColorMatteNode.h index 9d70b6d8416..3e1bfd607e8 100644 --- a/source/blender/compositor/nodes/COM_ColorMatteNode.h +++ b/source/blender/compositor/nodes/COM_ColorMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorMatteNode : public Node { public: - ColorMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorNode.cc b/source/blender/compositor/nodes/COM_ColorNode.cc index e79f2eb97af..2d875d703c7 100644 --- a/source/blender/compositor/nodes/COM_ColorNode.cc +++ b/source/blender/compositor/nodes/COM_ColorNode.cc @@ -21,22 +21,22 @@ namespace blender::compositor { -ColorNode::ColorNode(bNode *editorNode) : Node(editorNode) +ColorNode::ColorNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { SetColorOperation *operation = new SetColorOperation(); - NodeOutput *output = this->getOutputSocket(0); + NodeOutput *output = this->get_output_socket(0); float col[4]; - output->getEditorValueColor(col); - operation->setChannels(col); - converter.addOperation(operation); + output->get_editor_value_color(col); + operation->set_channels(col); + converter.add_operation(operation); - converter.mapOutputSocket(output, operation->getOutputSocket()); + converter.map_output_socket(output, operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorNode.h b/source/blender/compositor/nodes/COM_ColorNode.h index ae3bf575bb4..f5b932f6321 100644 --- a/source/blender/compositor/nodes/COM_ColorNode.h +++ b/source/blender/compositor/nodes/COM_ColorNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorNode : public Node { public: - ColorNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.cc b/source/blender/compositor/nodes/COM_ColorRampNode.cc index e4346459dd4..7ae83be65c2 100644 --- a/source/blender/compositor/nodes/COM_ColorRampNode.cc +++ b/source/blender/compositor/nodes/COM_ColorRampNode.cc @@ -22,32 +22,32 @@ namespace blender::compositor { -ColorRampNode::ColorRampNode(bNode *editorNode) : Node(editorNode) +ColorRampNode::ColorRampNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorRampNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorRampNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); - NodeOutput *outputSocketAlpha = this->getOutputSocket(1); - bNode *editorNode = this->getbNode(); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); + NodeOutput *output_socket_alpha = this->get_output_socket(1); + bNode *editor_node = this->get_bnode(); ColorRampOperation *operation = new ColorRampOperation(); - operation->setColorBand((ColorBand *)editorNode->storage); - converter.addOperation(operation); + operation->set_color_band((ColorBand *)editor_node->storage); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); SeparateChannelOperation *operation2 = new SeparateChannelOperation(); - operation2->setChannel(3); - converter.addOperation(operation2); + operation2->set_channel(3); + converter.add_operation(operation2); - converter.addLink(operation->getOutputSocket(), operation2->getInputSocket(0)); - converter.mapOutputSocket(outputSocketAlpha, operation2->getOutputSocket()); + converter.add_link(operation->get_output_socket(), operation2->get_input_socket(0)); + converter.map_output_socket(output_socket_alpha, operation2->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.h b/source/blender/compositor/nodes/COM_ColorRampNode.h index d0c0e43d56c..70e10311b2a 100644 --- a/source/blender/compositor/nodes/COM_ColorRampNode.h +++ b/source/blender/compositor/nodes/COM_ColorRampNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorRampNode : public Node { public: - ColorRampNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorRampNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.cc b/source/blender/compositor/nodes/COM_ColorSpillNode.cc index d0bd4eb12ea..063c515b460 100644 --- a/source/blender/compositor/nodes/COM_ColorSpillNode.cc +++ b/source/blender/compositor/nodes/COM_ColorSpillNode.cc @@ -21,30 +21,30 @@ namespace blender::compositor { -ColorSpillNode::ColorSpillNode(bNode *editorNode) : Node(editorNode) +ColorSpillNode::ColorSpillNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorSpillNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorSpillNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorsnode = getbNode(); + bNode *editorsnode = get_bnode(); - NodeInput *inputSocketImage = this->getInputSocket(0); - NodeInput *inputSocketFac = this->getInputSocket(1); - NodeOutput *outputSocketImage = this->getOutputSocket(0); + NodeInput *input_socket_image = this->get_input_socket(0); + NodeInput *input_socket_fac = this->get_input_socket(1); + NodeOutput *output_socket_image = this->get_output_socket(0); ColorSpillOperation *operation; operation = new ColorSpillOperation(); - operation->setSettings((NodeColorspill *)editorsnode->storage); - operation->setSpillChannel(editorsnode->custom1 - 1); /* Channel for spilling */ - operation->setSpillMethod(editorsnode->custom2); /* Channel method */ - converter.addOperation(operation); + operation->set_settings((NodeColorspill *)editorsnode->storage); + operation->set_spill_channel(editorsnode->custom1 - 1); /* Channel for spilling */ + operation->set_spill_method(editorsnode->custom2); /* Channel method */ + converter.add_operation(operation); - converter.mapInputSocket(inputSocketImage, operation->getInputSocket(0)); - converter.mapInputSocket(inputSocketFac, operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketImage, operation->getOutputSocket()); + converter.map_input_socket(input_socket_image, operation->get_input_socket(0)); + converter.map_input_socket(input_socket_fac, operation->get_input_socket(1)); + converter.map_output_socket(output_socket_image, operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.h b/source/blender/compositor/nodes/COM_ColorSpillNode.h index 731a76e8811..3a9b0e665e8 100644 --- a/source/blender/compositor/nodes/COM_ColorSpillNode.h +++ b/source/blender/compositor/nodes/COM_ColorSpillNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorSpillNode : public Node { public: - ColorSpillNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorSpillNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorToBWNode.cc b/source/blender/compositor/nodes/COM_ColorToBWNode.cc index ac051ab0d55..c021ee59b9e 100644 --- a/source/blender/compositor/nodes/COM_ColorToBWNode.cc +++ b/source/blender/compositor/nodes/COM_ColorToBWNode.cc @@ -22,22 +22,22 @@ namespace blender::compositor { -ColorToBWNode::ColorToBWNode(bNode *editorNode) : Node(editorNode) +ColorToBWNode::ColorToBWNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorToBWNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorToBWNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *colorSocket = this->getInputSocket(0); - NodeOutput *valueSocket = this->getOutputSocket(0); + NodeInput *color_socket = this->get_input_socket(0); + NodeOutput *value_socket = this->get_output_socket(0); - ConvertColorToBWOperation *convertProg = new ConvertColorToBWOperation(); - converter.addOperation(convertProg); + ConvertColorToBWOperation *convert_prog = new ConvertColorToBWOperation(); + converter.add_operation(convert_prog); - converter.mapInputSocket(colorSocket, convertProg->getInputSocket(0)); - converter.mapOutputSocket(valueSocket, convertProg->getOutputSocket(0)); + converter.map_input_socket(color_socket, convert_prog->get_input_socket(0)); + converter.map_output_socket(value_socket, convert_prog->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorToBWNode.h b/source/blender/compositor/nodes/COM_ColorToBWNode.h index 60c08a3c886..7018f30f559 100644 --- a/source/blender/compositor/nodes/COM_ColorToBWNode.h +++ b/source/blender/compositor/nodes/COM_ColorToBWNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class ColorToBWNode : public Node { public: - ColorToBWNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorToBWNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc index dd68780dc19..a37fc6ad5fa 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.cc +++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc @@ -22,70 +22,70 @@ namespace blender::compositor { -CombineColorNode::CombineColorNode(bNode *editorNode) : Node(editorNode) +CombineColorNode::CombineColorNode(bNode *editor_node) : Node(editor_node) { } -void CombineColorNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void CombineColorNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *inputRSocket = this->getInputSocket(0); - NodeInput *inputGSocket = this->getInputSocket(1); - NodeInput *inputBSocket = this->getInputSocket(2); - NodeInput *inputASocket = this->getInputSocket(3); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_rsocket = this->get_input_socket(0); + NodeInput *input_gsocket = this->get_input_socket(1); + NodeInput *input_bsocket = this->get_input_socket(2); + NodeInput *input_asocket = this->get_input_socket(3); + NodeOutput *output_socket = this->get_output_socket(0); CombineChannelsOperation *operation = new CombineChannelsOperation(); - if (inputRSocket->isLinked()) { + if (input_rsocket->is_linked()) { operation->set_canvas_input_index(0); } - else if (inputGSocket->isLinked()) { + else if (input_gsocket->is_linked()) { operation->set_canvas_input_index(1); } - else if (inputBSocket->isLinked()) { + else if (input_bsocket->is_linked()) { operation->set_canvas_input_index(2); } else { operation->set_canvas_input_index(3); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputRSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputGSocket, operation->getInputSocket(1)); - converter.mapInputSocket(inputBSocket, operation->getInputSocket(2)); - converter.mapInputSocket(inputASocket, operation->getInputSocket(3)); + converter.map_input_socket(input_rsocket, operation->get_input_socket(0)); + converter.map_input_socket(input_gsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_bsocket, operation->get_input_socket(2)); + converter.map_input_socket(input_asocket, operation->get_input_socket(3)); - NodeOperation *color_conv = getColorConverter(context); + NodeOperation *color_conv = get_color_converter(context); if (color_conv) { - converter.addOperation(color_conv); + converter.add_operation(color_conv); - converter.addLink(operation->getOutputSocket(), color_conv->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, color_conv->getOutputSocket()); + converter.add_link(operation->get_output_socket(), color_conv->get_input_socket(0)); + converter.map_output_socket(output_socket, color_conv->get_output_socket()); } else { - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + converter.map_output_socket(output_socket, operation->get_output_socket()); } } -NodeOperation *CombineRGBANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const { return nullptr; /* no conversion needed */ } -NodeOperation *CombineHSVANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const { return new ConvertHSVToRGBOperation(); } -NodeOperation *CombineYCCANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const { ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); - bNode *editorNode = this->getbNode(); - operation->setMode(editorNode->custom1); + bNode *editor_node = this->get_bnode(); + operation->set_mode(editor_node->custom1); return operation; } -NodeOperation *CombineYUVANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const { return new ConvertYUVToRGBOperation(); } diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.h b/source/blender/compositor/nodes/COM_CombineColorNode.h index 29d3fa37817..3252fc779f4 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.h +++ b/source/blender/compositor/nodes/COM_CombineColorNode.h @@ -24,48 +24,48 @@ namespace blender::compositor { class CombineColorNode : public Node { public: - CombineColorNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + CombineColorNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; protected: - virtual NodeOperation *getColorConverter(const CompositorContext &context) const = 0; + virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; }; class CombineRGBANode : public CombineColorNode { public: - CombineRGBANode(bNode *editorNode) : CombineColorNode(editorNode) + CombineRGBANode(bNode *editor_node) : CombineColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class CombineHSVANode : public CombineColorNode { public: - CombineHSVANode(bNode *editorNode) : CombineColorNode(editorNode) + CombineHSVANode(bNode *editor_node) : CombineColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class CombineYCCANode : public CombineColorNode { public: - CombineYCCANode(bNode *editorNode) : CombineColorNode(editorNode) + CombineYCCANode(bNode *editor_node) : CombineColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class CombineYUVANode : public CombineColorNode { public: - CombineYUVANode(bNode *editorNode) : CombineColorNode(editorNode) + CombineYUVANode(bNode *editor_node) : CombineColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cc b/source/blender/compositor/nodes/COM_CompositorNode.cc index 10c87b8c886..5121a7cc123 100644 --- a/source/blender/compositor/nodes/COM_CompositorNode.cc +++ b/source/blender/compositor/nodes/COM_CompositorNode.cc @@ -21,44 +21,44 @@ namespace blender::compositor { -CompositorNode::CompositorNode(bNode *editorNode) : Node(editorNode) +CompositorNode::CompositorNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void CompositorNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void CompositorNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - bool is_active = (editorNode->flag & NODE_DO_OUTPUT_RECALC) || context.isRendering(); - bool ignore_alpha = (editorNode->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0; + bNode *editor_node = this->get_bnode(); + bool is_active = (editor_node->flag & NODE_DO_OUTPUT_RECALC) || context.is_rendering(); + bool ignore_alpha = (editor_node->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0; - NodeInput *imageSocket = this->getInputSocket(0); - NodeInput *alphaSocket = this->getInputSocket(1); - NodeInput *depthSocket = this->getInputSocket(2); + NodeInput *image_socket = this->get_input_socket(0); + NodeInput *alpha_socket = this->get_input_socket(1); + NodeInput *depth_socket = this->get_input_socket(2); - CompositorOperation *compositorOperation = new CompositorOperation(); - compositorOperation->setScene(context.getScene()); - compositorOperation->setSceneName(context.getScene()->id.name); - compositorOperation->setRenderData(context.getRenderData()); - compositorOperation->setViewName(context.getViewName()); - compositorOperation->setbNodeTree(context.getbNodeTree()); + CompositorOperation *compositor_operation = new CompositorOperation(); + compositor_operation->set_scene(context.get_scene()); + compositor_operation->set_scene_name(context.get_scene()->id.name); + compositor_operation->set_render_data(context.get_render_data()); + compositor_operation->set_view_name(context.get_view_name()); + compositor_operation->set_bnodetree(context.get_bnodetree()); /* alpha socket gives either 1 or a custom alpha value if "use alpha" is enabled */ - compositorOperation->setUseAlphaInput(ignore_alpha || alphaSocket->isLinked()); - compositorOperation->setActive(is_active); + compositor_operation->set_use_alpha_input(ignore_alpha || alpha_socket->is_linked()); + compositor_operation->set_active(is_active); - converter.addOperation(compositorOperation); - converter.mapInputSocket(imageSocket, compositorOperation->getInputSocket(0)); + converter.add_operation(compositor_operation); + converter.map_input_socket(image_socket, compositor_operation->get_input_socket(0)); /* only use alpha link if "use alpha" is enabled */ if (ignore_alpha) { - converter.addInputValue(compositorOperation->getInputSocket(1), 1.0f); + converter.add_input_value(compositor_operation->get_input_socket(1), 1.0f); } else { - converter.mapInputSocket(alphaSocket, compositorOperation->getInputSocket(1)); + converter.map_input_socket(alpha_socket, compositor_operation->get_input_socket(1)); } - converter.mapInputSocket(depthSocket, compositorOperation->getInputSocket(2)); + converter.map_input_socket(depth_socket, compositor_operation->get_input_socket(2)); - converter.addNodeInputPreview(imageSocket); + converter.add_node_input_preview(image_socket); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CompositorNode.h b/source/blender/compositor/nodes/COM_CompositorNode.h index 4da9f9a766f..68005230ba2 100644 --- a/source/blender/compositor/nodes/COM_CompositorNode.h +++ b/source/blender/compositor/nodes/COM_CompositorNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class CompositorNode : public Node { public: - CompositorNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + CompositorNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc index 649d851fe84..c2c4b989007 100644 --- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc +++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc @@ -21,11 +21,11 @@ namespace blender::compositor { -void ConvertAlphaNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ConvertAlphaNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { NodeOperation *operation = nullptr; - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); /* value hardcoded in rna_nodetree.c */ if (node->custom1 == 1) { @@ -35,10 +35,10 @@ void ConvertAlphaNode::convertToOperations(NodeConverter &converter, operation = new ConvertStraightToPremulOperation(); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.h b/source/blender/compositor/nodes/COM_ConvertAlphaNode.h index f3d0ef2cd5b..8f26c82c110 100644 --- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.h +++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class ConvertAlphaNode : public Node { public: - ConvertAlphaNode(bNode *editorNode) : Node(editorNode) + ConvertAlphaNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CornerPinNode.cc b/source/blender/compositor/nodes/COM_CornerPinNode.cc index fb188dc45f8..0f7b58db89d 100644 --- a/source/blender/compositor/nodes/COM_CornerPinNode.cc +++ b/source/blender/compositor/nodes/COM_CornerPinNode.cc @@ -21,14 +21,14 @@ namespace blender::compositor { -CornerPinNode::CornerPinNode(bNode *editorNode) : Node(editorNode) +CornerPinNode::CornerPinNode(bNode *editor_node) : Node(editor_node) { } -void CornerPinNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void CornerPinNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *input_image = this->getInputSocket(0); + NodeInput *input_image = this->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 @@ -37,22 +37,22 @@ void CornerPinNode::convertToOperations(NodeConverter &converter, */ const int node_corner_index[4] = {3, 4, 2, 1}; - NodeOutput *output_warped_image = this->getOutputSocket(0); - NodeOutput *output_plane = this->getOutputSocket(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.addOperation(warp_image_operation); + converter.add_operation(warp_image_operation); PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation(); - converter.addOperation(plane_mask_operation); + converter.add_operation(plane_mask_operation); - converter.mapInputSocket(input_image, warp_image_operation->getInputSocket(0)); + converter.map_input_socket(input_image, warp_image_operation->get_input_socket(0)); for (int i = 0; i < 4; i++) { - NodeInput *corner_input = getInputSocket(node_corner_index[i]); - converter.mapInputSocket(corner_input, warp_image_operation->getInputSocket(i + 1)); - converter.mapInputSocket(corner_input, plane_mask_operation->getInputSocket(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.mapOutputSocket(output_warped_image, warp_image_operation->getOutputSocket()); - converter.mapOutputSocket(output_plane, plane_mask_operation->getOutputSocket()); + 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()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CornerPinNode.h b/source/blender/compositor/nodes/COM_CornerPinNode.h index 779e057ebb5..a1b4d107126 100644 --- a/source/blender/compositor/nodes/COM_CornerPinNode.h +++ b/source/blender/compositor/nodes/COM_CornerPinNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class CornerPinNode : public Node { public: - CornerPinNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + CornerPinNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CropNode.cc b/source/blender/compositor/nodes/COM_CropNode.cc index 3f01062c789..e33af232f17 100644 --- a/source/blender/compositor/nodes/COM_CropNode.cc +++ b/source/blender/compositor/nodes/COM_CropNode.cc @@ -21,31 +21,31 @@ namespace blender::compositor { -CropNode::CropNode(bNode *editorNode) : Node(editorNode) +CropNode::CropNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void CropNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void CropNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = getbNode(); - NodeTwoXYs *cropSettings = (NodeTwoXYs *)node->storage; + bNode *node = get_bnode(); + NodeTwoXYs *crop_settings = (NodeTwoXYs *)node->storage; bool relative = (bool)node->custom2; - bool cropImage = (bool)node->custom1; + bool crop_image = (bool)node->custom1; CropBaseOperation *operation; - if (cropImage) { + if (crop_image) { operation = new CropImageOperation(); } else { operation = new CropOperation(); } - operation->setCropSettings(cropSettings); - operation->setRelative(relative); - converter.addOperation(operation); + operation->set_crop_settings(crop_settings); + operation->set_relative(relative); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CropNode.h b/source/blender/compositor/nodes/COM_CropNode.h index be3c9a268f9..55324bb37df 100644 --- a/source/blender/compositor/nodes/COM_CropNode.h +++ b/source/blender/compositor/nodes/COM_CropNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class CropNode : public Node { public: - CropNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + CropNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index 21596d5aebb..f54088627e1 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -30,41 +30,41 @@ namespace blender::compositor { /** \name Cryptomatte Base * \{ */ -void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void CryptomatteBaseNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeOutput *output_image_socket = this->getOutputSocket(0); + NodeOutput *output_image_socket = this->get_output_socket(0); - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeCryptomatte *cryptomatte_settings = static_cast(node->storage); CryptomatteOperation *cryptomatte_operation = create_cryptomatte_operation( converter, context, *node, cryptomatte_settings); - converter.addOperation(cryptomatte_operation); + converter.add_operation(cryptomatte_operation); - NodeOutput *output_matte_socket = this->getOutputSocket(1); + NodeOutput *output_matte_socket = this->get_output_socket(1); SeparateChannelOperation *extract_mask_operation = new SeparateChannelOperation; - extract_mask_operation->setChannel(3); - converter.addOperation(extract_mask_operation); - converter.addLink(cryptomatte_operation->getOutputSocket(0), - extract_mask_operation->getInputSocket(0)); - converter.mapOutputSocket(output_matte_socket, extract_mask_operation->getOutputSocket(0)); + extract_mask_operation->set_channel(3); + converter.add_operation(extract_mask_operation); + converter.add_link(cryptomatte_operation->get_output_socket(0), + extract_mask_operation->get_input_socket(0)); + converter.map_output_socket(output_matte_socket, extract_mask_operation->get_output_socket(0)); - NodeInput *input_image_socket = this->getInputSocket(0); + NodeInput *input_image_socket = this->get_input_socket(0); SetAlphaMultiplyOperation *apply_mask_operation = new SetAlphaMultiplyOperation(); - converter.mapInputSocket(input_image_socket, apply_mask_operation->getInputSocket(0)); - converter.addOperation(apply_mask_operation); - converter.addLink(extract_mask_operation->getOutputSocket(0), - apply_mask_operation->getInputSocket(1)); - converter.mapOutputSocket(output_image_socket, apply_mask_operation->getOutputSocket(0)); + converter.map_input_socket(input_image_socket, apply_mask_operation->get_input_socket(0)); + converter.add_operation(apply_mask_operation); + converter.add_link(extract_mask_operation->get_output_socket(0), + apply_mask_operation->get_input_socket(1)); + converter.map_output_socket(output_image_socket, apply_mask_operation->get_output_socket(0)); - NodeOutput *output_pick_socket = this->getOutputSocket(2); + NodeOutput *output_pick_socket = this->get_output_socket(2); SetAlphaMultiplyOperation *extract_pick_operation = new SetAlphaMultiplyOperation(); - converter.addOperation(extract_pick_operation); - converter.addInputValue(extract_pick_operation->getInputSocket(1), 1.0f); - converter.addLink(cryptomatte_operation->getOutputSocket(0), - extract_pick_operation->getInputSocket(0)); - converter.mapOutputSocket(output_pick_socket, extract_pick_operation->getOutputSocket(0)); + converter.add_operation(extract_pick_operation); + converter.add_input_value(extract_pick_operation->get_input_socket(1), 1.0f); + converter.add_link(cryptomatte_operation->get_output_socket(0), + extract_pick_operation->get_input_socket(0)); + converter.map_output_socket(output_pick_socket, extract_pick_operation->get_output_socket(0)); } /** \} */ @@ -76,7 +76,7 @@ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, static std::string prefix_from_node(const CompositorContext &context, const bNode &node) { char prefix[MAX_NAME]; - ntreeCompositCryptomatteLayerPrefix(context.getScene(), &node, prefix, sizeof(prefix)); + ntreeCompositCryptomatteLayerPrefix(context.get_scene(), &node, prefix, sizeof(prefix)); return std::string(prefix, BLI_strnlen(prefix, sizeof(prefix))); } @@ -120,7 +120,7 @@ void CryptomatteNode::input_operations_from_render_source( RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name); if (render_layer) { LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) { - if (context.has_explicit_view() && !STREQ(render_pass->view, context.getViewName())) { + if (context.has_explicit_view() && !STREQ(render_pass->view, context.get_view_name())) { continue; } @@ -128,10 +128,10 @@ void CryptomatteNode::input_operations_from_render_source( if (blender::StringRef(combined_name).startswith(prefix)) { RenderLayersProg *op = new RenderLayersProg( render_pass->name, DataType::Color, render_pass->channels); - op->setScene(scene); - op->setLayerId(view_layer_id); - op->setRenderData(context.getRenderData()); - op->setViewName(context.getViewName()); + op->set_scene(scene); + op->set_layer_id(view_layer_id); + op->set_render_data(context.get_render_data()); + op->set_view_name(context.get_view_name()); r_input_operations.append(op); } } @@ -157,7 +157,7 @@ void CryptomatteNode::input_operations_from_image_source( } ImageUser *iuser = &cryptomatte_settings->iuser; - BKE_image_user_frame_calc(image, iuser, context.getFramenumber()); + BKE_image_user_frame_calc(image, iuser, context.get_framenumber()); ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr); if (image->rr) { @@ -167,7 +167,7 @@ void CryptomatteNode::input_operations_from_image_source( /* Heuristic to match image name with scene names, check if the view name exists in the * image. */ view = BLI_findstringindex( - &image->rr->views, context.getViewName(), offsetof(RenderView, name)); + &image->rr->views, context.get_view_name(), offsetof(RenderView, name)); if (view == -1) { view = 0; } @@ -189,10 +189,10 @@ void CryptomatteNode::input_operations_from_image_source( if (blender::StringRef(combined_name).startswith(prefix)) { MultilayerColorOperation *op = new MultilayerColorOperation( render_layer, render_pass, view); - op->setImage(image); - op->setImageUser(iuser); + op->set_image(image); + op->set_image_user(iuser); iuser->layer = layer_index; - op->setFramenumber(context.getFramenumber()); + op->set_framenumber(context.get_framenumber()); r_input_operations.append(op); } } @@ -217,10 +217,10 @@ Vector CryptomatteNode::create_input_operations(const Composito if (input_operations.is_empty()) { SetColorOperation *op = new SetColorOperation(); - op->setChannel1(0.0f); - op->setChannel2(1.0f); - op->setChannel3(0.0f); - op->setChannel4(0.0f); + op->set_channel1(0.0f); + op->set_channel2(1.0f); + op->set_channel3(0.0f); + op->set_channel4(0.0f); input_operations.append(op); } return input_operations; @@ -234,11 +234,11 @@ CryptomatteOperation *CryptomatteNode::create_cryptomatte_operation( Vector input_operations = create_input_operations(context, node); CryptomatteOperation *operation = new CryptomatteOperation(input_operations.size()); LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) { - operation->addObjectIndex(cryptomatte_entry->encoded_hash); + operation->add_object_index(cryptomatte_entry->encoded_hash); } for (int i = 0; i < input_operations.size(); ++i) { - converter.addOperation(input_operations[i]); - converter.addLink(input_operations[i]->getOutputSocket(), operation->getInputSocket(i)); + converter.add_operation(input_operations[i]); + converter.add_link(input_operations[i]->get_output_socket(), operation->get_input_socket(i)); } return operation; } @@ -259,12 +259,12 @@ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation( CryptomatteOperation *operation = new CryptomatteOperation(num_inputs); if (cryptomatte_settings) { LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) { - operation->addObjectIndex(cryptomatte_entry->encoded_hash); + operation->add_object_index(cryptomatte_entry->encoded_hash); } } for (int i = 0; i < num_inputs; i++) { - converter.mapInputSocket(this->getInputSocket(i + 1), operation->getInputSocket(i)); + converter.map_input_socket(this->get_input_socket(i + 1), operation->get_input_socket(i)); } return operation; diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.h b/source/blender/compositor/nodes/COM_CryptomatteNode.h index eacb49e2033..7ca1084886b 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.h +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.h @@ -38,8 +38,8 @@ class CryptomatteBaseNode : public Node { } public: - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; protected: virtual CryptomatteOperation *create_cryptomatte_operation( diff --git a/source/blender/compositor/nodes/COM_DefocusNode.cc b/source/blender/compositor/nodes/COM_DefocusNode.cc index 6f1361df284..684f9014c3c 100644 --- a/source/blender/compositor/nodes/COM_DefocusNode.cc +++ b/source/blender/compositor/nodes/COM_DefocusNode.cc @@ -26,58 +26,58 @@ namespace blender::compositor { -DefocusNode::DefocusNode(bNode *editorNode) : Node(editorNode) +DefocusNode::DefocusNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DefocusNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void DefocusNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeDefocus *data = (NodeDefocus *)node->storage; - Scene *scene = node->id ? (Scene *)node->id : context.getScene(); + Scene *scene = node->id ? (Scene *)node->id : context.get_scene(); Object *camob = scene ? scene->camera : nullptr; - NodeOperation *radiusOperation; + NodeOperation *radius_operation; if (data->no_zbuf) { MathMultiplyOperation *multiply = new MathMultiplyOperation(); SetValueOperation *multiplier = new SetValueOperation(); - multiplier->setValue(data->scale); - SetValueOperation *maxRadius = new SetValueOperation(); - maxRadius->setValue(data->maxblur); + multiplier->set_value(data->scale); + SetValueOperation *max_radius = new SetValueOperation(); + max_radius->set_value(data->maxblur); MathMinimumOperation *minimize = new MathMinimumOperation(); - converter.addOperation(multiply); - converter.addOperation(multiplier); - converter.addOperation(maxRadius); - converter.addOperation(minimize); + converter.add_operation(multiply); + converter.add_operation(multiplier); + converter.add_operation(max_radius); + converter.add_operation(minimize); - converter.mapInputSocket(getInputSocket(1), multiply->getInputSocket(0)); - converter.addLink(multiplier->getOutputSocket(), multiply->getInputSocket(1)); - converter.addLink(multiply->getOutputSocket(), minimize->getInputSocket(0)); - converter.addLink(maxRadius->getOutputSocket(), minimize->getInputSocket(1)); + converter.map_input_socket(get_input_socket(1), multiply->get_input_socket(0)); + converter.add_link(multiplier->get_output_socket(), multiply->get_input_socket(1)); + converter.add_link(multiply->get_output_socket(), minimize->get_input_socket(0)); + converter.add_link(max_radius->get_output_socket(), minimize->get_input_socket(1)); - radiusOperation = minimize; + radius_operation = minimize; } else { ConvertDepthToRadiusOperation *radius_op = new ConvertDepthToRadiusOperation(); - radius_op->setCameraObject(camob); - radius_op->setfStop(data->fstop); - radius_op->setMaxRadius(data->maxblur); - converter.addOperation(radius_op); + radius_op->set_camera_object(camob); + radius_op->setf_stop(data->fstop); + radius_op->set_max_radius(data->maxblur); + converter.add_operation(radius_op); - converter.mapInputSocket(getInputSocket(1), radius_op->getInputSocket(0)); + converter.map_input_socket(get_input_socket(1), radius_op->get_input_socket(0)); FastGaussianBlurValueOperation *blur = new FastGaussianBlurValueOperation(); /* maintain close pixels so far Z values don't bleed into the foreground */ - blur->setOverlay(FAST_GAUSS_OVERLAY_MIN); - converter.addOperation(blur); + blur->set_overlay(FAST_GAUSS_OVERLAY_MIN); + converter.add_operation(blur); - converter.addLink(radius_op->getOutputSocket(0), blur->getInputSocket(0)); - radius_op->setPostBlur(blur); + converter.add_link(radius_op->get_output_socket(0), blur->get_input_socket(0)); + radius_op->set_post_blur(blur); - radiusOperation = blur; + radius_operation = blur; } NodeBokehImage *bokehdata = new NodeBokehImage(); @@ -92,49 +92,49 @@ void DefocusNode::convertToOperations(NodeConverter &converter, bokehdata->lensshift = 0.0f; BokehImageOperation *bokeh = new BokehImageOperation(); - bokeh->setData(bokehdata); - bokeh->deleteDataOnFinish(); - converter.addOperation(bokeh); + bokeh->set_data(bokehdata); + bokeh->delete_data_on_finish(); + converter.add_operation(bokeh); #ifdef COM_DEFOCUS_SEARCH InverseSearchRadiusOperation *search = new InverseSearchRadiusOperation(); - search->setMaxBlur(data->maxblur); - converter.addOperation(search); + search->set_max_blur(data->maxblur); + converter.add_operation(search); - converter.addLink(radiusOperation->getOutputSocket(0), search->getInputSocket(0)); + converter.add_link(radius_operation->get_output_socket(0), search->get_input_socket(0)); #endif VariableSizeBokehBlurOperation *operation = new VariableSizeBokehBlurOperation(); if (data->preview) { - operation->setQuality(eCompositorQuality::Low); + operation->set_quality(eCompositorQuality::Low); } else { - operation->setQuality(context.getQuality()); + operation->set_quality(context.get_quality()); } - operation->setMaxBlur(data->maxblur); - operation->setThreshold(data->bthresh); - converter.addOperation(operation); + operation->set_max_blur(data->maxblur); + operation->set_threshold(data->bthresh); + converter.add_operation(operation); - converter.addLink(bokeh->getOutputSocket(), operation->getInputSocket(1)); - converter.addLink(radiusOperation->getOutputSocket(), operation->getInputSocket(2)); + converter.add_link(bokeh->get_output_socket(), operation->get_input_socket(1)); + converter.add_link(radius_operation->get_output_socket(), operation->get_input_socket(2)); #ifdef COM_DEFOCUS_SEARCH - converter.addLink(search->getOutputSocket(), operation->getInputSocket(3)); + converter.add_link(search->get_output_socket(), operation->get_input_socket(3)); #endif if (data->gamco) { GammaCorrectOperation *correct = new GammaCorrectOperation(); - converter.addOperation(correct); + converter.add_operation(correct); GammaUncorrectOperation *inverse = new GammaUncorrectOperation(); - converter.addOperation(inverse); + converter.add_operation(inverse); - converter.mapInputSocket(getInputSocket(0), correct->getInputSocket(0)); - converter.addLink(correct->getOutputSocket(), operation->getInputSocket(0)); - converter.addLink(operation->getOutputSocket(), inverse->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(), inverse->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), correct->get_input_socket(0)); + converter.add_link(correct->get_output_socket(), operation->get_input_socket(0)); + converter.add_link(operation->get_output_socket(), inverse->get_input_socket(0)); + converter.map_output_socket(get_output_socket(), inverse->get_output_socket()); } else { - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(), operation->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_DefocusNode.h b/source/blender/compositor/nodes/COM_DefocusNode.h index 5e51a0ccd52..58993495bee 100644 --- a/source/blender/compositor/nodes/COM_DefocusNode.h +++ b/source/blender/compositor/nodes/COM_DefocusNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DefocusNode : public Node { public: - DefocusNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DefocusNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.cc b/source/blender/compositor/nodes/COM_DenoiseNode.cc index e576966626c..2ff8870f716 100644 --- a/source/blender/compositor/nodes/COM_DenoiseNode.cc +++ b/source/blender/compositor/nodes/COM_DenoiseNode.cc @@ -20,51 +20,51 @@ namespace blender::compositor { -DenoiseNode::DenoiseNode(bNode *editorNode) : Node(editorNode) +DenoiseNode::DenoiseNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DenoiseNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void DenoiseNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { if (!COM_is_denoise_supported()) { - converter.mapOutputSocket(getOutputSocket(0), - converter.addInputProxy(getInputSocket(0), false)); + converter.map_output_socket(get_output_socket(0), + converter.add_input_proxy(get_input_socket(0), false)); return; } - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeDenoise *denoise = (NodeDenoise *)node->storage; DenoiseOperation *operation = new DenoiseOperation(); - converter.addOperation(operation); - operation->setDenoiseSettings(denoise); + converter.add_operation(operation); + operation->set_denoise_settings(denoise); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); if (denoise && denoise->prefilter == CMP_NODE_DENOISE_PREFILTER_ACCURATE) { { DenoisePrefilterOperation *normal_prefilter = new DenoisePrefilterOperation( DataType::Vector); normal_prefilter->set_image_name("normal"); - converter.addOperation(normal_prefilter); - converter.mapInputSocket(getInputSocket(1), normal_prefilter->getInputSocket(0)); - converter.addLink(normal_prefilter->getOutputSocket(), operation->getInputSocket(1)); + converter.add_operation(normal_prefilter); + converter.map_input_socket(get_input_socket(1), normal_prefilter->get_input_socket(0)); + converter.add_link(normal_prefilter->get_output_socket(), operation->get_input_socket(1)); } { DenoisePrefilterOperation *albedo_prefilter = new DenoisePrefilterOperation(DataType::Color); albedo_prefilter->set_image_name("albedo"); - converter.addOperation(albedo_prefilter); - converter.mapInputSocket(getInputSocket(2), albedo_prefilter->getInputSocket(0)); - converter.addLink(albedo_prefilter->getOutputSocket(), operation->getInputSocket(2)); + converter.add_operation(albedo_prefilter); + converter.map_input_socket(get_input_socket(2), albedo_prefilter->get_input_socket(0)); + converter.add_link(albedo_prefilter->get_output_socket(), operation->get_input_socket(2)); } } else { - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); } - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.h b/source/blender/compositor/nodes/COM_DenoiseNode.h index 91be8e3e3ad..7e18c22c1e9 100644 --- a/source/blender/compositor/nodes/COM_DenoiseNode.h +++ b/source/blender/compositor/nodes/COM_DenoiseNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DenoiseNode : public Node { public: - DenoiseNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DenoiseNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.cc b/source/blender/compositor/nodes/COM_DespeckleNode.cc index 1a068112210..12c9d50f214 100644 --- a/source/blender/compositor/nodes/COM_DespeckleNode.cc +++ b/source/blender/compositor/nodes/COM_DespeckleNode.cc @@ -21,29 +21,29 @@ namespace blender::compositor { -DespeckleNode::DespeckleNode(bNode *editorNode) : Node(editorNode) +DespeckleNode::DespeckleNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DespeckleNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void DespeckleNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorNode = this->getbNode(); - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputImageSocket = this->getInputSocket(1); - NodeOutput *outputSocket = this->getOutputSocket(0); + bNode *editor_node = this->get_bnode(); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_image_socket = this->get_input_socket(1); + NodeOutput *output_socket = this->get_output_socket(0); DespeckleOperation *operation = new DespeckleOperation(); - operation->setThreshold(editorNode->custom3); - operation->setThresholdNeighbor(editorNode->custom4); - converter.addOperation(operation); + operation->set_threshold(editor_node->custom3); + operation->set_threshold_neighbor(editor_node->custom4); + converter.add_operation(operation); - converter.mapInputSocket(inputImageSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputSocket, operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + converter.map_input_socket(input_image_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(1)); + converter.map_output_socket(output_socket, operation->get_output_socket()); - converter.addPreview(operation->getOutputSocket(0)); + converter.add_preview(operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.h b/source/blender/compositor/nodes/COM_DespeckleNode.h index 2f268e99e1b..2c3af600aed 100644 --- a/source/blender/compositor/nodes/COM_DespeckleNode.h +++ b/source/blender/compositor/nodes/COM_DespeckleNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DespeckleNode : public Node { public: - DespeckleNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DespeckleNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc index 9700b761eca..ceb5be7bd08 100644 --- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc @@ -22,36 +22,36 @@ namespace blender::compositor { -DifferenceMatteNode::DifferenceMatteNode(bNode *editorNode) : Node(editorNode) +DifferenceMatteNode::DifferenceMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DifferenceMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void DifferenceMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputSocket2 = this->getInputSocket(1); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); - bNode *editorNode = this->getbNode(); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_socket2 = this->get_input_socket(1); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); + bNode *editor_node = this->get_bnode(); - DifferenceMatteOperation *operationSet = new DifferenceMatteOperation(); - operationSet->setSettings((NodeChroma *)editorNode->storage); - converter.addOperation(operationSet); + DifferenceMatteOperation *operation_set = new DifferenceMatteOperation(); + operation_set->set_settings((NodeChroma *)editor_node->storage); + converter.add_operation(operation_set); - converter.mapInputSocket(inputSocket, operationSet->getInputSocket(0)); - converter.mapInputSocket(inputSocket2, operationSet->getInputSocket(1)); - converter.mapOutputSocket(outputSocketMatte, operationSet->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation_set->get_input_socket(0)); + converter.map_input_socket(input_socket2, operation_set->get_input_socket(1)); + converter.map_output_socket(output_socket_matte, operation_set->get_output_socket(0)); SetAlphaMultiplyOperation *operation = new SetAlphaMultiplyOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.addLink(operationSet->getOutputSocket(), operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketImage, operation->getOutputSocket()); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.add_link(operation_set->get_output_socket(), operation->get_input_socket(1)); + converter.map_output_socket(output_socket_image, operation->get_output_socket()); - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.h b/source/blender/compositor/nodes/COM_DifferenceMatteNode.h index a173c723192..f99d4022507 100644 --- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.h +++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DifferenceMatteNode : public Node { public: - DifferenceMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DifferenceMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.cc b/source/blender/compositor/nodes/COM_DilateErodeNode.cc index ea7ce396189..b121fcb3724 100644 --- a/source/blender/compositor/nodes/COM_DilateErodeNode.cc +++ b/source/blender/compositor/nodes/COM_DilateErodeNode.cc @@ -24,126 +24,126 @@ namespace blender::compositor { -DilateErodeNode::DilateErodeNode(bNode *editorNode) : Node(editorNode) +DilateErodeNode::DilateErodeNode(bNode *editor_node) : Node(editor_node) { /* initialize node data */ NodeBlurData *data = &alpha_blur_; memset(data, 0, sizeof(NodeBlurData)); data->filtertype = R_FILTER_GAUSS; - if (editorNode->custom2 > 0) { - data->sizex = data->sizey = editorNode->custom2; + if (editor_node->custom2 > 0) { + data->sizex = data->sizey = editor_node->custom2; } else { - data->sizex = data->sizey = -editorNode->custom2; + data->sizex = data->sizey = -editor_node->custom2; } } -void DilateErodeNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void DilateErodeNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - if (editorNode->custom1 == CMP_NODE_DILATEERODE_DISTANCE_THRESH) { + bNode *editor_node = this->get_bnode(); + if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE_THRESH) { DilateErodeThresholdOperation *operation = new DilateErodeThresholdOperation(); - operation->setDistance(editorNode->custom2); - operation->setInset(editorNode->custom3); - converter.addOperation(operation); + operation->set_distance(editor_node->custom2); + operation->set_inset(editor_node->custom3); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); - if (editorNode->custom3 < 2.0f) { - AntiAliasOperation *antiAlias = new AntiAliasOperation(); - converter.addOperation(antiAlias); + if (editor_node->custom3 < 2.0f) { + AntiAliasOperation *anti_alias = new AntiAliasOperation(); + converter.add_operation(anti_alias); - converter.addLink(operation->getOutputSocket(), antiAlias->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), antiAlias->getOutputSocket(0)); + converter.add_link(operation->get_output_socket(), anti_alias->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), anti_alias->get_output_socket(0)); } else { - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } - else if (editorNode->custom1 == CMP_NODE_DILATEERODE_DISTANCE) { - if (editorNode->custom2 > 0) { + else if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE) { + if (editor_node->custom2 > 0) { DilateDistanceOperation *operation = new DilateDistanceOperation(); - operation->setDistance(editorNode->custom2); - converter.addOperation(operation); + operation->set_distance(editor_node->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } else { ErodeDistanceOperation *operation = new ErodeDistanceOperation(); - operation->setDistance(-editorNode->custom2); - converter.addOperation(operation); + operation->set_distance(-editor_node->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } - else if (editorNode->custom1 == CMP_NODE_DILATEERODE_DISTANCE_FEATHER) { + else if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE_FEATHER) { /* this uses a modified gaussian blur function otherwise its far too slow */ - eCompositorQuality quality = context.getQuality(); + eCompositorQuality quality = context.get_quality(); GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation(); - operationx->setData(&alpha_blur_); - operationx->setQuality(quality); - operationx->setFalloff(PROP_SMOOTH); - converter.addOperation(operationx); + operationx->set_data(&alpha_blur_); + operationx->set_quality(quality); + operationx->set_falloff(PROP_SMOOTH); + converter.add_operation(operationx); - converter.mapInputSocket(getInputSocket(0), operationx->getInputSocket(0)); - // converter.mapInputSocket(getInputSocket(1), operationx->getInputSocket(1)); // no size input - // yet + converter.map_input_socket(get_input_socket(0), operationx->get_input_socket(0)); + // converter.map_input_socket(get_input_socket(1), operationx->get_input_socket(1)); // no size + // input yet GaussianAlphaYBlurOperation *operationy = new GaussianAlphaYBlurOperation(); - operationy->setData(&alpha_blur_); - operationy->setQuality(quality); - operationy->setFalloff(PROP_SMOOTH); - converter.addOperation(operationy); + operationy->set_data(&alpha_blur_); + operationy->set_quality(quality); + operationy->set_falloff(PROP_SMOOTH); + converter.add_operation(operationy); - converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0)); - // converter.mapInputSocket(getInputSocket(1), operationy->getInputSocket(1)); // no size input - // yet - converter.mapOutputSocket(getOutputSocket(0), operationy->getOutputSocket()); + converter.add_link(operationx->get_output_socket(), operationy->get_input_socket(0)); + // converter.map_input_socket(get_input_socket(1), operationy->get_input_socket(1)); // no size + // input yet + converter.map_output_socket(get_output_socket(0), operationy->get_output_socket()); - converter.addPreview(operationy->getOutputSocket()); + converter.add_preview(operationy->get_output_socket()); /* TODO? */ /* see gaussian blue node for original usage */ #if 0 - if (!connectedSizeSocket) { - operationx->setSize(size); - operationy->setSize(size); + if (!connected_size_socket) { + operationx->set_size(size); + operationy->set_size(size); } #else - operationx->setSize(1.0f); - operationy->setSize(1.0f); + operationx->set_size(1.0f); + operationy->set_size(1.0f); #endif - operationx->setSubtract(editorNode->custom2 < 0); - operationy->setSubtract(editorNode->custom2 < 0); + operationx->set_subtract(editor_node->custom2 < 0); + operationy->set_subtract(editor_node->custom2 < 0); - if (editorNode->storage) { - NodeDilateErode *data_storage = (NodeDilateErode *)editorNode->storage; - operationx->setFalloff(data_storage->falloff); - operationy->setFalloff(data_storage->falloff); + if (editor_node->storage) { + NodeDilateErode *data_storage = (NodeDilateErode *)editor_node->storage; + operationx->set_falloff(data_storage->falloff); + operationy->set_falloff(data_storage->falloff); } } else { - if (editorNode->custom2 > 0) { + if (editor_node->custom2 > 0) { DilateStepOperation *operation = new DilateStepOperation(); - operation->setIterations(editorNode->custom2); - converter.addOperation(operation); + operation->set_iterations(editor_node->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } else { ErodeStepOperation *operation = new ErodeStepOperation(); - operation->setIterations(-editorNode->custom2); - converter.addOperation(operation); + operation->set_iterations(-editor_node->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } } diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.h b/source/blender/compositor/nodes/COM_DilateErodeNode.h index 2a635a8b0a6..df6abe7a6b6 100644 --- a/source/blender/compositor/nodes/COM_DilateErodeNode.h +++ b/source/blender/compositor/nodes/COM_DilateErodeNode.h @@ -31,9 +31,9 @@ class DilateErodeNode : public Node { NodeBlurData alpha_blur_; public: - DilateErodeNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DilateErodeNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc index dce2a1a7911..b8e3e44f41f 100644 --- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc +++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc @@ -21,22 +21,22 @@ namespace blender::compositor { -DirectionalBlurNode::DirectionalBlurNode(bNode *editorNode) : Node(editorNode) +DirectionalBlurNode::DirectionalBlurNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DirectionalBlurNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void DirectionalBlurNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeDBlurData *data = (NodeDBlurData *)this->getbNode()->storage; + NodeDBlurData *data = (NodeDBlurData *)this->get_bnode()->storage; DirectionalBlurOperation *operation = new DirectionalBlurOperation(); - operation->setQuality(context.getQuality()); - operation->setData(data); - converter.addOperation(operation); + operation->set_quality(context.get_quality()); + operation->set_data(data); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.h b/source/blender/compositor/nodes/COM_DirectionalBlurNode.h index ce3ef378aaf..96a5122d134 100644 --- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.h +++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DirectionalBlurNode : public Node { public: - DirectionalBlurNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DirectionalBlurNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DisplaceNode.cc b/source/blender/compositor/nodes/COM_DisplaceNode.cc index d13c0c347fb..06208fb7d89 100644 --- a/source/blender/compositor/nodes/COM_DisplaceNode.cc +++ b/source/blender/compositor/nodes/COM_DisplaceNode.cc @@ -22,28 +22,28 @@ namespace blender::compositor { -DisplaceNode::DisplaceNode(bNode *editorNode) : Node(editorNode) +DisplaceNode::DisplaceNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DisplaceNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void DisplaceNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { NodeOperation *operation; - if (context.getQuality() == eCompositorQuality::Low) { + if (context.get_quality() == eCompositorQuality::Low) { operation = new DisplaceSimpleOperation(); } else { operation = new DisplaceOperation(); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapInputSocket(getInputSocket(3), operation->getInputSocket(3)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_input_socket(get_input_socket(3), operation->get_input_socket(3)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DisplaceNode.h b/source/blender/compositor/nodes/COM_DisplaceNode.h index b2495839da3..394624e8f60 100644 --- a/source/blender/compositor/nodes/COM_DisplaceNode.h +++ b/source/blender/compositor/nodes/COM_DisplaceNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DisplaceNode : public Node { public: - DisplaceNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DisplaceNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc index 26886a6580f..db78e60fbbe 100644 --- a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc @@ -23,77 +23,78 @@ namespace blender::compositor { -DistanceMatteNode::DistanceMatteNode(bNode *editorNode) : Node(editorNode) +DistanceMatteNode::DistanceMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DistanceMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void DistanceMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorsnode = getbNode(); + bNode *editorsnode = get_bnode(); NodeChroma *storage = (NodeChroma *)editorsnode->storage; - NodeInput *inputSocketImage = this->getInputSocket(0); - NodeInput *inputSocketKey = this->getInputSocket(1); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); + NodeInput *input_socket_image = this->get_input_socket(0); + NodeInput *input_socket_key = this->get_input_socket(1); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); - SetAlphaMultiplyOperation *operationAlpha = new SetAlphaMultiplyOperation(); - converter.addOperation(operationAlpha); + SetAlphaMultiplyOperation *operation_alpha = new SetAlphaMultiplyOperation(); + converter.add_operation(operation_alpha); /* work in RGB color space */ NodeOperation *operation; if (storage->channel == 1) { DistanceRGBMatteOperation *matte = new DistanceRGBMatteOperation(); - matte->setSettings(storage); - converter.addOperation(matte); + matte->set_settings(storage); + converter.add_operation(matte); - converter.mapInputSocket(inputSocketImage, matte->getInputSocket(0)); - converter.mapInputSocket(inputSocketImage, operationAlpha->getInputSocket(0)); + converter.map_input_socket(input_socket_image, matte->get_input_socket(0)); + converter.map_input_socket(input_socket_image, operation_alpha->get_input_socket(0)); - converter.mapInputSocket(inputSocketKey, matte->getInputSocket(1)); + converter.map_input_socket(input_socket_key, matte->get_input_socket(1)); operation = matte; } /* work in YCbCr color space */ else { DistanceYCCMatteOperation *matte = new DistanceYCCMatteOperation(); - matte->setSettings(storage); - converter.addOperation(matte); + matte->set_settings(storage); + converter.add_operation(matte); - ConvertRGBToYCCOperation *operationYCCImage = new ConvertRGBToYCCOperation(); - ConvertRGBToYCCOperation *operationYCCMatte = new ConvertRGBToYCCOperation(); - operationYCCImage->setMode(BLI_YCC_ITU_BT709); - operationYCCMatte->setMode(BLI_YCC_ITU_BT709); - converter.addOperation(operationYCCImage); - converter.addOperation(operationYCCMatte); + ConvertRGBToYCCOperation *operation_yccimage = new ConvertRGBToYCCOperation(); + ConvertRGBToYCCOperation *operation_yccmatte = new ConvertRGBToYCCOperation(); + operation_yccimage->set_mode(BLI_YCC_ITU_BT709); + operation_yccmatte->set_mode(BLI_YCC_ITU_BT709); + converter.add_operation(operation_yccimage); + converter.add_operation(operation_yccmatte); - converter.mapInputSocket(inputSocketImage, operationYCCImage->getInputSocket(0)); - converter.addLink(operationYCCImage->getOutputSocket(), matte->getInputSocket(0)); - converter.addLink(operationYCCImage->getOutputSocket(), operationAlpha->getInputSocket(0)); + converter.map_input_socket(input_socket_image, operation_yccimage->get_input_socket(0)); + converter.add_link(operation_yccimage->get_output_socket(), matte->get_input_socket(0)); + converter.add_link(operation_yccimage->get_output_socket(), + operation_alpha->get_input_socket(0)); - converter.mapInputSocket(inputSocketKey, operationYCCMatte->getInputSocket(0)); - converter.addLink(operationYCCMatte->getOutputSocket(), matte->getInputSocket(1)); + converter.map_input_socket(input_socket_key, operation_yccmatte->get_input_socket(0)); + converter.add_link(operation_yccmatte->get_output_socket(), matte->get_input_socket(1)); operation = matte; } - converter.mapOutputSocket(outputSocketMatte, operation->getOutputSocket(0)); - converter.addLink(operation->getOutputSocket(), operationAlpha->getInputSocket(1)); + converter.map_output_socket(output_socket_matte, operation->get_output_socket(0)); + converter.add_link(operation->get_output_socket(), operation_alpha->get_input_socket(1)); if (storage->channel != 1) { ConvertYCCToRGBOperation *inv_convert = new ConvertYCCToRGBOperation(); - inv_convert->setMode(BLI_YCC_ITU_BT709); + inv_convert->set_mode(BLI_YCC_ITU_BT709); - converter.addOperation(inv_convert); - converter.addLink(operationAlpha->getOutputSocket(0), inv_convert->getInputSocket(0)); - converter.mapOutputSocket(outputSocketImage, inv_convert->getOutputSocket()); - converter.addPreview(inv_convert->getOutputSocket()); + converter.add_operation(inv_convert); + converter.add_link(operation_alpha->get_output_socket(0), inv_convert->get_input_socket(0)); + converter.map_output_socket(output_socket_image, inv_convert->get_output_socket()); + converter.add_preview(inv_convert->get_output_socket()); } else { - converter.mapOutputSocket(outputSocketImage, operationAlpha->getOutputSocket()); - converter.addPreview(operationAlpha->getOutputSocket()); + converter.map_output_socket(output_socket_image, operation_alpha->get_output_socket()); + converter.add_preview(operation_alpha->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.h b/source/blender/compositor/nodes/COM_DistanceMatteNode.h index 0baa531b4d2..d12e69d8065 100644 --- a/source/blender/compositor/nodes/COM_DistanceMatteNode.h +++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DistanceMatteNode : public Node { public: - DistanceMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DistanceMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc index 273f0ee7026..b88348ccc60 100644 --- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc +++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc @@ -21,25 +21,25 @@ namespace blender::compositor { -DoubleEdgeMaskNode::DoubleEdgeMaskNode(bNode *editorNode) : Node(editorNode) +DoubleEdgeMaskNode::DoubleEdgeMaskNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DoubleEdgeMaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void DoubleEdgeMaskNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { DoubleEdgeMaskOperation *operation; - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); operation = new DoubleEdgeMaskOperation(); - operation->setAdjecentOnly(bnode->custom1); - operation->setKeepInside(bnode->custom2); - converter.addOperation(operation); + operation->set_adjecent_only(bnode->custom1); + operation->set_keep_inside(bnode->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h index 90e009747c1..99b3b1d0546 100644 --- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h +++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DoubleEdgeMaskNode : public Node { public: - DoubleEdgeMaskNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DoubleEdgeMaskNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc index 4f3703dceb1..1f6e2ab2a6c 100644 --- a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc +++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc @@ -24,52 +24,53 @@ namespace blender::compositor { -EllipseMaskNode::EllipseMaskNode(bNode *editorNode) : Node(editorNode) +EllipseMaskNode::EllipseMaskNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void EllipseMaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void EllipseMaskNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); EllipseMaskOperation *operation; operation = new EllipseMaskOperation(); - operation->setData((NodeEllipseMask *)this->getbNode()->storage); - operation->setMaskType(this->getbNode()->custom1); - converter.addOperation(operation); + operation->set_data((NodeEllipseMask *)this->get_bnode()->storage); + operation->set_mask_type(this->get_bnode()->custom1); + converter.add_operation(operation); - if (inputSocket->isLinked()) { - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + if (input_socket->is_linked()) { + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket()); } else { /* Value operation to produce original transparent image */ - SetValueOperation *valueOperation = new SetValueOperation(); - valueOperation->setValue(0.0f); - converter.addOperation(valueOperation); + SetValueOperation *value_operation = new SetValueOperation(); + value_operation->set_value(0.0f); + converter.add_operation(value_operation); /* Scale that image up to render resolution */ - const RenderData *rd = context.getRenderData(); - const float render_size_factor = context.getRenderPercentageAsFactor(); - ScaleFixedSizeOperation *scaleOperation = new ScaleFixedSizeOperation(); + const RenderData *rd = context.get_render_data(); + const float render_size_factor = context.get_render_percentage_as_factor(); + ScaleFixedSizeOperation *scale_operation = new ScaleFixedSizeOperation(); - scaleOperation->setIsAspect(false); - scaleOperation->setIsCrop(false); - scaleOperation->setOffset(0.0f, 0.0f); - scaleOperation->setNewWidth(rd->xsch * render_size_factor); - scaleOperation->setNewHeight(rd->ysch * render_size_factor); - scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align); - converter.addOperation(scaleOperation); + scale_operation->set_is_aspect(false); + scale_operation->set_is_crop(false); + scale_operation->set_offset(0.0f, 0.0f); + scale_operation->set_new_width(rd->xsch * render_size_factor); + scale_operation->set_new_height(rd->ysch * render_size_factor); + scale_operation->get_input_socket(0)->set_resize_mode(ResizeMode::Align); + converter.add_operation(scale_operation); - converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0)); - converter.addLink(scaleOperation->getOutputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.add_link(value_operation->get_output_socket(0), + scale_operation->get_input_socket(0)); + converter.add_link(scale_operation->get_output_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.h b/source/blender/compositor/nodes/COM_EllipseMaskNode.h index cbe189be9f6..4a582b63d91 100644 --- a/source/blender/compositor/nodes/COM_EllipseMaskNode.h +++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class EllipseMaskNode : public Node { public: - EllipseMaskNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + EllipseMaskNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_FilterNode.cc b/source/blender/compositor/nodes/COM_FilterNode.cc index 4cdb00a87f4..2108e68cbec 100644 --- a/source/blender/compositor/nodes/COM_FilterNode.cc +++ b/source/blender/compositor/nodes/COM_FilterNode.cc @@ -22,20 +22,20 @@ namespace blender::compositor { -FilterNode::FilterNode(bNode *editorNode) : Node(editorNode) +FilterNode::FilterNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void FilterNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void FilterNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputImageSocket = this->getInputSocket(1); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_image_socket = this->get_input_socket(1); + NodeOutput *output_socket = this->get_output_socket(0); ConvolutionFilterOperation *operation = nullptr; - switch (this->getbNode()->custom1) { + switch (this->get_bnode()->custom1) { case CMP_FILT_SOFT: operation = new ConvolutionFilterOperation(); operation->set3x3Filter(1 / 16.0f, @@ -85,13 +85,13 @@ void FilterNode::convertToOperations(NodeConverter &converter, operation->set3x3Filter(0, 0, 0, 0, 1, 0, 0, 0, 0); break; } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputImageSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputSocket, operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + converter.map_input_socket(input_image_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(1)); + converter.map_output_socket(output_socket, operation->get_output_socket()); - converter.addPreview(operation->getOutputSocket(0)); + converter.add_preview(operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_FilterNode.h b/source/blender/compositor/nodes/COM_FilterNode.h index f7f4176cea5..c160e11fd9f 100644 --- a/source/blender/compositor/nodes/COM_FilterNode.h +++ b/source/blender/compositor/nodes/COM_FilterNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class FilterNode : public Node { public: - FilterNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + FilterNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_FlipNode.cc b/source/blender/compositor/nodes/COM_FlipNode.cc index 1c613bde674..ec9a4f9419b 100644 --- a/source/blender/compositor/nodes/COM_FlipNode.cc +++ b/source/blender/compositor/nodes/COM_FlipNode.cc @@ -22,18 +22,18 @@ namespace blender::compositor { -FlipNode::FlipNode(bNode *editorNode) : Node(editorNode) +FlipNode::FlipNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void FlipNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void FlipNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); FlipOperation *operation = new FlipOperation(); - switch (this->getbNode()->custom1) { + switch (this->get_bnode()->custom1) { case 0: /* TODO: I didn't find any constants in the old implementation, * should I introduce them. */ operation->setFlipX(true); @@ -49,9 +49,9 @@ void FlipNode::convertToOperations(NodeConverter &converter, break; } - converter.addOperation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.add_operation(operation); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_FlipNode.h b/source/blender/compositor/nodes/COM_FlipNode.h index ee61d09fbba..40dc44edbbe 100644 --- a/source/blender/compositor/nodes/COM_FlipNode.h +++ b/source/blender/compositor/nodes/COM_FlipNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class FlipNode : public Node { public: - FlipNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + FlipNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_GammaNode.cc b/source/blender/compositor/nodes/COM_GammaNode.cc index 31253a90462..3c4e885ff76 100644 --- a/source/blender/compositor/nodes/COM_GammaNode.cc +++ b/source/blender/compositor/nodes/COM_GammaNode.cc @@ -21,20 +21,20 @@ namespace blender::compositor { -GammaNode::GammaNode(bNode *editorNode) : Node(editorNode) +GammaNode::GammaNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void GammaNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void GammaNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { GammaOperation *operation = new GammaOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_GammaNode.h b/source/blender/compositor/nodes/COM_GammaNode.h index 29c9ed170fa..3c7a8ee4983 100644 --- a/source/blender/compositor/nodes/COM_GammaNode.h +++ b/source/blender/compositor/nodes/COM_GammaNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class GammaNode : public Node { public: - GammaNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + GammaNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_GlareNode.cc b/source/blender/compositor/nodes/COM_GlareNode.cc index bbbbb7fa7ee..77b1162f7cc 100644 --- a/source/blender/compositor/nodes/COM_GlareNode.cc +++ b/source/blender/compositor/nodes/COM_GlareNode.cc @@ -27,15 +27,15 @@ namespace blender::compositor { -GlareNode::GlareNode(bNode *editorNode) : Node(editorNode) +GlareNode::GlareNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void GlareNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void GlareNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeGlare *glare = (NodeGlare *)node->storage; GlareBaseOperation *glareoperation = nullptr; @@ -55,30 +55,31 @@ void GlareNode::convertToOperations(NodeConverter &converter, break; } BLI_assert(glareoperation); - glareoperation->setGlareSettings(glare); + glareoperation->set_glare_settings(glare); - GlareThresholdOperation *thresholdOperation = new GlareThresholdOperation(); - thresholdOperation->setGlareSettings(glare); + GlareThresholdOperation *threshold_operation = new GlareThresholdOperation(); + threshold_operation->set_glare_settings(glare); SetValueOperation *mixvalueoperation = new SetValueOperation(); - mixvalueoperation->setValue(glare->mix); + mixvalueoperation->set_value(glare->mix); MixGlareOperation *mixoperation = new MixGlareOperation(); mixoperation->set_canvas_input_index(1); - mixoperation->getInputSocket(2)->setResizeMode(ResizeMode::FitAny); + mixoperation->get_input_socket(2)->set_resize_mode(ResizeMode::FitAny); - converter.addOperation(glareoperation); - converter.addOperation(thresholdOperation); - converter.addOperation(mixvalueoperation); - converter.addOperation(mixoperation); + converter.add_operation(glareoperation); + converter.add_operation(threshold_operation); + converter.add_operation(mixvalueoperation); + converter.add_operation(mixoperation); - converter.mapInputSocket(getInputSocket(0), thresholdOperation->getInputSocket(0)); - converter.addLink(thresholdOperation->getOutputSocket(), glareoperation->getInputSocket(0)); + converter.map_input_socket(get_input_socket(0), threshold_operation->get_input_socket(0)); + converter.add_link(threshold_operation->get_output_socket(), + glareoperation->get_input_socket(0)); - converter.addLink(mixvalueoperation->getOutputSocket(), mixoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(0), mixoperation->getInputSocket(1)); - converter.addLink(glareoperation->getOutputSocket(), mixoperation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(), mixoperation->getOutputSocket()); + converter.add_link(mixvalueoperation->get_output_socket(), mixoperation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(0), mixoperation->get_input_socket(1)); + converter.add_link(glareoperation->get_output_socket(), mixoperation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(), mixoperation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_GlareNode.h b/source/blender/compositor/nodes/COM_GlareNode.h index 7db5fa85e04..85e657984cd 100644 --- a/source/blender/compositor/nodes/COM_GlareNode.h +++ b/source/blender/compositor/nodes/COM_GlareNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class GlareNode : public Node { public: - GlareNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + GlareNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc index 3cf4b218bb2..6cf9a5356c4 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc +++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc @@ -24,41 +24,42 @@ namespace blender::compositor { -HueSaturationValueCorrectNode::HueSaturationValueCorrectNode(bNode *editorNode) : Node(editorNode) +HueSaturationValueCorrectNode::HueSaturationValueCorrectNode(bNode *editor_node) + : Node(editor_node) { /* pass */ } -void HueSaturationValueCorrectNode::convertToOperations( +void HueSaturationValueCorrectNode::convert_to_operations( NodeConverter &converter, const CompositorContext & /*context*/) const { - NodeInput *valueSocket = this->getInputSocket(0); - NodeInput *colorSocket = this->getInputSocket(1); - NodeOutput *outputSocket = this->getOutputSocket(0); - bNode *editorsnode = getbNode(); + NodeInput *value_socket = this->get_input_socket(0); + NodeInput *color_socket = this->get_input_socket(1); + NodeOutput *output_socket = this->get_output_socket(0); + bNode *editorsnode = get_bnode(); CurveMapping *storage = (CurveMapping *)editorsnode->storage; ConvertRGBToHSVOperation *rgbToHSV = new ConvertRGBToHSVOperation(); - converter.addOperation(rgbToHSV); + converter.add_operation(rgbToHSV); ConvertHSVToRGBOperation *hsvToRGB = new ConvertHSVToRGBOperation(); - converter.addOperation(hsvToRGB); + converter.add_operation(hsvToRGB); HueSaturationValueCorrectOperation *changeHSV = new HueSaturationValueCorrectOperation(); - changeHSV->setCurveMapping(storage); - converter.addOperation(changeHSV); + changeHSV->set_curve_mapping(storage); + converter.add_operation(changeHSV); MixBlendOperation *blend = new MixBlendOperation(); blend->set_canvas_input_index(1); - converter.addOperation(blend); + converter.add_operation(blend); - converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0)); - converter.addLink(rgbToHSV->getOutputSocket(), changeHSV->getInputSocket(0)); - converter.addLink(changeHSV->getOutputSocket(), hsvToRGB->getInputSocket(0)); - converter.addLink(hsvToRGB->getOutputSocket(), blend->getInputSocket(2)); - converter.mapInputSocket(colorSocket, blend->getInputSocket(1)); - converter.mapInputSocket(valueSocket, blend->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, blend->getOutputSocket()); + converter.map_input_socket(color_socket, rgbToHSV->get_input_socket(0)); + converter.add_link(rgbToHSV->get_output_socket(), changeHSV->get_input_socket(0)); + converter.add_link(changeHSV->get_output_socket(), hsvToRGB->get_input_socket(0)); + converter.add_link(hsvToRGB->get_output_socket(), blend->get_input_socket(2)); + converter.map_input_socket(color_socket, blend->get_input_socket(1)); + converter.map_input_socket(value_socket, blend->get_input_socket(0)); + converter.map_output_socket(output_socket, blend->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h index d75b2ba51ca..1f59c06cb9f 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h +++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class HueSaturationValueCorrectNode : public Node { public: - HueSaturationValueCorrectNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + HueSaturationValueCorrectNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc index 7711c306f24..fa296f54d8f 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc +++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc @@ -24,44 +24,44 @@ namespace blender::compositor { -HueSaturationValueNode::HueSaturationValueNode(bNode *editorNode) : Node(editorNode) +HueSaturationValueNode::HueSaturationValueNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void HueSaturationValueNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void HueSaturationValueNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *colorSocket = this->getInputSocket(0); - NodeInput *hueSocket = this->getInputSocket(1); - NodeInput *saturationSocket = this->getInputSocket(2); - NodeInput *valueSocket = this->getInputSocket(3); - NodeInput *facSocket = this->getInputSocket(4); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *color_socket = this->get_input_socket(0); + NodeInput *hue_socket = this->get_input_socket(1); + NodeInput *saturation_socket = this->get_input_socket(2); + NodeInput *value_socket = this->get_input_socket(3); + NodeInput *fac_socket = this->get_input_socket(4); + NodeOutput *output_socket = this->get_output_socket(0); ConvertRGBToHSVOperation *rgbToHSV = new ConvertRGBToHSVOperation(); - converter.addOperation(rgbToHSV); + converter.add_operation(rgbToHSV); ConvertHSVToRGBOperation *hsvToRGB = new ConvertHSVToRGBOperation(); - converter.addOperation(hsvToRGB); + converter.add_operation(hsvToRGB); ChangeHSVOperation *changeHSV = new ChangeHSVOperation(); - converter.mapInputSocket(hueSocket, changeHSV->getInputSocket(1)); - converter.mapInputSocket(saturationSocket, changeHSV->getInputSocket(2)); - converter.mapInputSocket(valueSocket, changeHSV->getInputSocket(3)); - converter.addOperation(changeHSV); + converter.map_input_socket(hue_socket, changeHSV->get_input_socket(1)); + converter.map_input_socket(saturation_socket, changeHSV->get_input_socket(2)); + converter.map_input_socket(value_socket, changeHSV->get_input_socket(3)); + converter.add_operation(changeHSV); MixBlendOperation *blend = new MixBlendOperation(); blend->set_canvas_input_index(1); - converter.addOperation(blend); + converter.add_operation(blend); - converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0)); - converter.addLink(rgbToHSV->getOutputSocket(), changeHSV->getInputSocket(0)); - converter.addLink(changeHSV->getOutputSocket(), hsvToRGB->getInputSocket(0)); - converter.addLink(hsvToRGB->getOutputSocket(), blend->getInputSocket(2)); - converter.mapInputSocket(colorSocket, blend->getInputSocket(1)); - converter.mapInputSocket(facSocket, blend->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, blend->getOutputSocket()); + converter.map_input_socket(color_socket, rgbToHSV->get_input_socket(0)); + converter.add_link(rgbToHSV->get_output_socket(), changeHSV->get_input_socket(0)); + converter.add_link(changeHSV->get_output_socket(), hsvToRGB->get_input_socket(0)); + converter.add_link(hsvToRGB->get_output_socket(), blend->get_input_socket(2)); + converter.map_input_socket(color_socket, blend->get_input_socket(1)); + converter.map_input_socket(fac_socket, blend->get_input_socket(0)); + converter.map_output_socket(output_socket, blend->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.h b/source/blender/compositor/nodes/COM_HueSaturationValueNode.h index 0b295158cc7..abb7d0afebe 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.h +++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class HueSaturationValueNode : public Node { public: - HueSaturationValueNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + HueSaturationValueNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.cc b/source/blender/compositor/nodes/COM_IDMaskNode.cc index 0a7ed521afb..4d761f25fd3 100644 --- a/source/blender/compositor/nodes/COM_IDMaskNode.cc +++ b/source/blender/compositor/nodes/COM_IDMaskNode.cc @@ -22,46 +22,46 @@ namespace blender::compositor { -IDMaskNode::IDMaskNode(bNode *editorNode) : Node(editorNode) +IDMaskNode::IDMaskNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void IDMaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void IDMaskNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); IDMaskOperation *operation; operation = new IDMaskOperation(); - operation->setObjectIndex(bnode->custom1); - converter.addOperation(operation); + operation->set_object_index(bnode->custom1); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); if (bnode->custom2 == 0) { - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } else { SMAAEdgeDetectionOperation *operation1 = nullptr; operation1 = new SMAAEdgeDetectionOperation(); - converter.addOperation(operation1); + converter.add_operation(operation1); - converter.addLink(operation->getOutputSocket(0), operation1->getInputSocket(0)); + converter.add_link(operation->get_output_socket(0), operation1->get_input_socket(0)); /* Blending Weight Calculation Pixel Shader (Second Pass). */ SMAABlendingWeightCalculationOperation *operation2 = new SMAABlendingWeightCalculationOperation(); - converter.addOperation(operation2); + converter.add_operation(operation2); - converter.addLink(operation1->getOutputSocket(), operation2->getInputSocket(0)); + converter.add_link(operation1->get_output_socket(), operation2->get_input_socket(0)); /* Neighborhood Blending Pixel Shader (Third Pass). */ SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation(); - converter.addOperation(operation3); + converter.add_operation(operation3); - converter.addLink(operation->getOutputSocket(0), operation3->getInputSocket(0)); - converter.addLink(operation2->getOutputSocket(), operation3->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation3->getOutputSocket()); + converter.add_link(operation->get_output_socket(0), operation3->get_input_socket(0)); + converter.add_link(operation2->get_output_socket(), operation3->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation3->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.h b/source/blender/compositor/nodes/COM_IDMaskNode.h index f702732a8ed..8eba2af715f 100644 --- a/source/blender/compositor/nodes/COM_IDMaskNode.h +++ b/source/blender/compositor/nodes/COM_IDMaskNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class IDMaskNode : public Node { public: - IDMaskNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + IDMaskNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ImageNode.cc b/source/blender/compositor/nodes/COM_ImageNode.cc index 992d5697109..928a7c735c7 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cc +++ b/source/blender/compositor/nodes/COM_ImageNode.cc @@ -26,21 +26,21 @@ namespace blender::compositor { -ImageNode::ImageNode(bNode *editorNode) : Node(editorNode) +ImageNode::ImageNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -NodeOperation *ImageNode::doMultilayerCheck(NodeConverter &converter, - RenderLayer *render_layer, - RenderPass *render_pass, - Image *image, - ImageUser *user, - int framenumber, - int outputsocketIndex, - int view, - DataType datatype) const +NodeOperation *ImageNode::do_multilayer_check(NodeConverter &converter, + RenderLayer *render_layer, + RenderPass *render_pass, + Image *image, + ImageUser *user, + int framenumber, + int outputsocket_index, + int view, + DataType datatype) const { - NodeOutput *outputSocket = this->getOutputSocket(outputsocketIndex); + NodeOutput *output_socket = this->get_output_socket(outputsocket_index); MultilayerBaseOperation *operation = nullptr; switch (datatype) { case DataType::Value: @@ -55,27 +55,27 @@ NodeOperation *ImageNode::doMultilayerCheck(NodeConverter &converter, default: break; } - operation->setImage(image); - operation->setImageUser(user); - operation->setFramenumber(framenumber); + operation->set_image(image); + operation->set_image_user(user); + operation->set_framenumber(framenumber); - converter.addOperation(operation); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_output_socket(output_socket, operation->get_output_socket()); return operation; } -void ImageNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void ImageNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { /** Image output */ - NodeOutput *outputImage = this->getOutputSocket(0); - bNode *editorNode = this->getbNode(); - Image *image = (Image *)editorNode->id; - ImageUser *imageuser = (ImageUser *)editorNode->storage; - int framenumber = context.getFramenumber(); - bool outputStraightAlpha = (editorNode->custom1 & CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT) != 0; - BKE_image_user_frame_calc(image, imageuser, context.getFramenumber()); + NodeOutput *output_image = this->get_output_socket(0); + bNode *editor_node = this->get_bnode(); + Image *image = (Image *)editor_node->id; + ImageUser *imageuser = (ImageUser *)editor_node->storage; + int framenumber = context.get_framenumber(); + bool output_straight_alpha = (editor_node->custom1 & CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT) != 0; + BKE_image_user_frame_calc(image, imageuser, context.get_framenumber()); /* force a load, we assume iuser index will be set OK anyway */ if (image && image->type == IMA_TYPE_MULTILAYER) { bool is_multilayer_ok = false; @@ -88,14 +88,14 @@ void ImageNode::convertToOperations(NodeConverter &converter, for (int64_t index = 0; index < outputs.size(); index++) { NodeOutput *socket = outputs[index]; NodeOperation *operation = nullptr; - bNodeSocket *bnodeSocket = socket->getbNodeSocket(); - NodeImageLayer *storage = (NodeImageLayer *)bnodeSocket->storage; + bNodeSocket *bnode_socket = socket->get_bnode_socket(); + NodeImageLayer *storage = (NodeImageLayer *)bnode_socket->storage; RenderPass *rpass = (RenderPass *)BLI_findstring( &rl->passes, storage->pass_name, offsetof(RenderPass, name)); int view = 0; if (STREQ(storage->pass_name, RE_PASSNAME_COMBINED) && - STREQ(bnodeSocket->name, "Alpha")) { + STREQ(bnode_socket->name, "Alpha")) { /* Alpha output is already handled with the associated combined output. */ continue; } @@ -109,7 +109,7 @@ void ImageNode::convertToOperations(NodeConverter &converter, /* heuristic to match image name with scene names * check if the view name exists in the image */ view = BLI_findstringindex( - &image->rr->views, context.getViewName(), offsetof(RenderView, name)); + &image->rr->views, context.get_view_name(), offsetof(RenderView, name)); if (view == -1) { view = 0; } @@ -122,64 +122,64 @@ void ImageNode::convertToOperations(NodeConverter &converter, if (rpass) { switch (rpass->channels) { case 1: - operation = doMultilayerCheck(converter, - rl, - rpass, - image, - imageuser, - framenumber, - index, - view, - DataType::Value); + operation = do_multilayer_check(converter, + rl, + rpass, + image, + imageuser, + framenumber, + index, + view, + DataType::Value); break; /* using image operations for both 3 and 4 channels (RGB and RGBA respectively) */ /* XXX any way to detect actual vector images? */ case 3: - operation = doMultilayerCheck(converter, - rl, - rpass, - image, - imageuser, - framenumber, - index, - view, - DataType::Vector); + operation = do_multilayer_check(converter, + rl, + rpass, + image, + imageuser, + framenumber, + index, + view, + DataType::Vector); break; case 4: - operation = doMultilayerCheck(converter, - rl, - rpass, - image, - imageuser, - framenumber, - index, - view, - DataType::Color); + operation = do_multilayer_check(converter, + rl, + rpass, + image, + imageuser, + framenumber, + index, + view, + DataType::Color); break; default: /* dummy operation is added below */ break; } if (index == 0 && operation) { - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); } - if (STREQ(rpass->name, RE_PASSNAME_COMBINED) && !(bnodeSocket->flag & SOCK_UNAVAIL)) { - for (NodeOutput *alphaSocket : getOutputSockets()) { - bNodeSocket *bnodeAlphaSocket = alphaSocket->getbNodeSocket(); - if (!STREQ(bnodeAlphaSocket->name, "Alpha")) { + if (STREQ(rpass->name, RE_PASSNAME_COMBINED) && !(bnode_socket->flag & SOCK_UNAVAIL)) { + for (NodeOutput *alpha_socket : get_output_sockets()) { + bNodeSocket *bnode_alpha_socket = alpha_socket->get_bnode_socket(); + if (!STREQ(bnode_alpha_socket->name, "Alpha")) { continue; } - NodeImageLayer *alphaStorage = (NodeImageLayer *)bnodeSocket->storage; - if (!STREQ(alphaStorage->pass_name, RE_PASSNAME_COMBINED)) { + NodeImageLayer *alpha_storage = (NodeImageLayer *)bnode_socket->storage; + if (!STREQ(alpha_storage->pass_name, RE_PASSNAME_COMBINED)) { continue; } SeparateChannelOperation *separate_operation; separate_operation = new SeparateChannelOperation(); - separate_operation->setChannel(3); - converter.addOperation(separate_operation); - converter.addLink(operation->getOutputSocket(), - separate_operation->getInputSocket(0)); - converter.mapOutputSocket(alphaSocket, separate_operation->getOutputSocket()); + separate_operation->set_channel(3); + converter.add_operation(separate_operation); + converter.add_link(operation->get_output_socket(), + separate_operation->get_input_socket(0)); + converter.map_output_socket(alpha_socket, separate_operation->get_output_socket()); break; } } @@ -187,7 +187,7 @@ void ImageNode::convertToOperations(NodeConverter &converter, /* In case we can't load the layer. */ if (operation == nullptr) { - converter.setInvalidOutput(getOutputSocket(index)); + converter.set_invalid_output(get_output_socket(index)); } } } @@ -196,69 +196,70 @@ void ImageNode::convertToOperations(NodeConverter &converter, /* without this, multilayer that fail to load will crash blender T32490. */ if (is_multilayer_ok == false) { - for (NodeOutput *output : getOutputSockets()) { - converter.setInvalidOutput(output); + for (NodeOutput *output : get_output_sockets()) { + converter.set_invalid_output(output); } } } else { - const int64_t numberOfOutputs = getOutputSockets().size(); - if (numberOfOutputs > 0) { + const int64_t number_of_outputs = get_output_sockets().size(); + if (number_of_outputs > 0) { ImageOperation *operation = new ImageOperation(); - operation->setImage(image); - operation->setImageUser(imageuser); - operation->setFramenumber(framenumber); - operation->setRenderData(context.getRenderData()); - operation->setViewName(context.getViewName()); - converter.addOperation(operation); + operation->set_image(image); + operation->set_image_user(imageuser); + operation->set_framenumber(framenumber); + operation->set_render_data(context.get_render_data()); + operation->set_view_name(context.get_view_name()); + converter.add_operation(operation); - if (outputStraightAlpha) { - NodeOperation *alphaConvertOperation = new ConvertPremulToStraightOperation(); + if (output_straight_alpha) { + NodeOperation *alpha_convert_operation = new ConvertPremulToStraightOperation(); - converter.addOperation(alphaConvertOperation); - converter.mapOutputSocket(outputImage, alphaConvertOperation->getOutputSocket()); - converter.addLink(operation->getOutputSocket(0), alphaConvertOperation->getInputSocket(0)); + converter.add_operation(alpha_convert_operation); + converter.map_output_socket(output_image, alpha_convert_operation->get_output_socket()); + converter.add_link(operation->get_output_socket(0), + alpha_convert_operation->get_input_socket(0)); } else { - converter.mapOutputSocket(outputImage, operation->getOutputSocket()); + converter.map_output_socket(output_image, operation->get_output_socket()); } - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); } - if (numberOfOutputs > 1) { - NodeOutput *alphaImage = this->getOutputSocket(1); - ImageAlphaOperation *alphaOperation = new ImageAlphaOperation(); - alphaOperation->setImage(image); - alphaOperation->setImageUser(imageuser); - alphaOperation->setFramenumber(framenumber); - alphaOperation->setRenderData(context.getRenderData()); - alphaOperation->setViewName(context.getViewName()); - converter.addOperation(alphaOperation); + if (number_of_outputs > 1) { + NodeOutput *alpha_image = this->get_output_socket(1); + ImageAlphaOperation *alpha_operation = new ImageAlphaOperation(); + alpha_operation->set_image(image); + alpha_operation->set_image_user(imageuser); + alpha_operation->set_framenumber(framenumber); + alpha_operation->set_render_data(context.get_render_data()); + alpha_operation->set_view_name(context.get_view_name()); + converter.add_operation(alpha_operation); - converter.mapOutputSocket(alphaImage, alphaOperation->getOutputSocket()); + converter.map_output_socket(alpha_image, alpha_operation->get_output_socket()); } - if (numberOfOutputs > 2) { - NodeOutput *depthImage = this->getOutputSocket(2); - ImageDepthOperation *depthOperation = new ImageDepthOperation(); - depthOperation->setImage(image); - depthOperation->setImageUser(imageuser); - depthOperation->setFramenumber(framenumber); - depthOperation->setRenderData(context.getRenderData()); - depthOperation->setViewName(context.getViewName()); - converter.addOperation(depthOperation); + if (number_of_outputs > 2) { + NodeOutput *depth_image = this->get_output_socket(2); + ImageDepthOperation *depth_operation = new ImageDepthOperation(); + depth_operation->set_image(image); + depth_operation->set_image_user(imageuser); + depth_operation->set_framenumber(framenumber); + depth_operation->set_render_data(context.get_render_data()); + depth_operation->set_view_name(context.get_view_name()); + converter.add_operation(depth_operation); - converter.mapOutputSocket(depthImage, depthOperation->getOutputSocket()); + converter.map_output_socket(depth_image, depth_operation->get_output_socket()); } - if (numberOfOutputs > 3) { + if (number_of_outputs > 3) { /* happens when unlinking image datablock from multilayer node */ - for (int i = 3; i < numberOfOutputs; i++) { - NodeOutput *output = this->getOutputSocket(i); + for (int i = 3; i < number_of_outputs; i++) { + NodeOutput *output = this->get_output_socket(i); NodeOperation *operation = nullptr; - switch (output->getDataType()) { + switch (output->get_data_type()) { case DataType::Value: { SetValueOperation *valueoperation = new SetValueOperation(); - valueoperation->setValue(0.0f); + valueoperation->set_value(0.0f); operation = valueoperation; break; } @@ -272,10 +273,10 @@ void ImageNode::convertToOperations(NodeConverter &converter, } case DataType::Color: { SetColorOperation *coloroperation = new SetColorOperation(); - coloroperation->setChannel1(0.0f); - coloroperation->setChannel2(0.0f); - coloroperation->setChannel3(0.0f); - coloroperation->setChannel4(0.0f); + coloroperation->set_channel1(0.0f); + coloroperation->set_channel2(0.0f); + coloroperation->set_channel3(0.0f); + coloroperation->set_channel4(0.0f); operation = coloroperation; break; } @@ -283,8 +284,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, if (operation) { /* not supporting multiview for this generic case */ - converter.addOperation(operation); - converter.mapOutputSocket(output, operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_output_socket(output, operation->get_output_socket()); } } } diff --git a/source/blender/compositor/nodes/COM_ImageNode.h b/source/blender/compositor/nodes/COM_ImageNode.h index 047cc496f83..7dd948be946 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.h +++ b/source/blender/compositor/nodes/COM_ImageNode.h @@ -34,20 +34,20 @@ namespace blender::compositor { */ class ImageNode : public Node { private: - NodeOperation *doMultilayerCheck(NodeConverter &converter, - RenderLayer *render_layer, - RenderPass *render_pass, - Image *image, - ImageUser *user, - int framenumber, - int outputsocketIndex, - int view, - DataType datatype) const; + NodeOperation *do_multilayer_check(NodeConverter &converter, + RenderLayer *render_layer, + RenderPass *render_pass, + Image *image, + ImageUser *user, + int framenumber, + int outputsocket_index, + int view, + DataType datatype) const; public: - ImageNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ImageNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_InpaintNode.cc b/source/blender/compositor/nodes/COM_InpaintNode.cc index 93fb051090c..1feb618d47a 100644 --- a/source/blender/compositor/nodes/COM_InpaintNode.cc +++ b/source/blender/compositor/nodes/COM_InpaintNode.cc @@ -21,25 +21,25 @@ namespace blender::compositor { -InpaintNode::InpaintNode(bNode *editorNode) : Node(editorNode) +InpaintNode::InpaintNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void InpaintNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void InpaintNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorNode = this->getbNode(); + bNode *editor_node = this->get_bnode(); - /* if (editorNode->custom1 == CMP_NODE_INPAINT_SIMPLE) { */ + /* if (editor_node->custom1 == CMP_NODE_INPAINT_SIMPLE) { */ if (true) { InpaintSimpleOperation *operation = new InpaintSimpleOperation(); - operation->setIterations(editorNode->custom2); - converter.addOperation(operation); + operation->set_iterations(editor_node->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } diff --git a/source/blender/compositor/nodes/COM_InpaintNode.h b/source/blender/compositor/nodes/COM_InpaintNode.h index 3a10c11bf61..389ca1d56f4 100644 --- a/source/blender/compositor/nodes/COM_InpaintNode.h +++ b/source/blender/compositor/nodes/COM_InpaintNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class InpaintNode : public Node { public: - InpaintNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + InpaintNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_InvertNode.cc b/source/blender/compositor/nodes/COM_InvertNode.cc index cb15f3b28b0..ffde5b1e12e 100644 --- a/source/blender/compositor/nodes/COM_InvertNode.cc +++ b/source/blender/compositor/nodes/COM_InvertNode.cc @@ -22,23 +22,23 @@ namespace blender::compositor { -InvertNode::InvertNode(bNode *editorNode) : Node(editorNode) +InvertNode::InvertNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void InvertNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void InvertNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { InvertOperation *operation = new InvertOperation(); - bNode *node = this->getbNode(); - operation->setColor(node->custom1 & CMP_CHAN_RGB); - operation->setAlpha(node->custom1 & CMP_CHAN_A); - converter.addOperation(operation); + bNode *node = this->get_bnode(); + operation->set_color(node->custom1 & CMP_CHAN_RGB); + operation->set_alpha(node->custom1 & CMP_CHAN_A); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_InvertNode.h b/source/blender/compositor/nodes/COM_InvertNode.h index 1cc975b8236..8f2eb258ab5 100644 --- a/source/blender/compositor/nodes/COM_InvertNode.h +++ b/source/blender/compositor/nodes/COM_InvertNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class InvertNode : public Node { public: - InvertNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + InvertNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_KeyingNode.cc b/source/blender/compositor/nodes/COM_KeyingNode.cc index c928b55fbb0..d3130fdd2eb 100644 --- a/source/blender/compositor/nodes/COM_KeyingNode.cc +++ b/source/blender/compositor/nodes/COM_KeyingNode.cc @@ -37,112 +37,114 @@ namespace blender::compositor { -KeyingNode::KeyingNode(bNode *editorNode) : Node(editorNode) +KeyingNode::KeyingNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -NodeOperationOutput *KeyingNode::setupPreBlur(NodeConverter &converter, - NodeInput *inputImage, - int size) const +NodeOperationOutput *KeyingNode::setup_pre_blur(NodeConverter &converter, + NodeInput *input_image, + int size) const { ConvertRGBToYCCOperation *convertRGBToYCCOperation = new ConvertRGBToYCCOperation(); - convertRGBToYCCOperation->setMode(BLI_YCC_ITU_BT709); - converter.addOperation(convertRGBToYCCOperation); + convertRGBToYCCOperation->set_mode(BLI_YCC_ITU_BT709); + converter.add_operation(convertRGBToYCCOperation); - converter.mapInputSocket(inputImage, convertRGBToYCCOperation->getInputSocket(0)); + converter.map_input_socket(input_image, convertRGBToYCCOperation->get_input_socket(0)); - CombineChannelsOperation *combineOperation = new CombineChannelsOperation(); - converter.addOperation(combineOperation); + CombineChannelsOperation *combine_operation = new CombineChannelsOperation(); + converter.add_operation(combine_operation); for (int channel = 0; channel < 4; channel++) { - SeparateChannelOperation *separateOperation = new SeparateChannelOperation(); - separateOperation->setChannel(channel); - converter.addOperation(separateOperation); + SeparateChannelOperation *separate_operation = new SeparateChannelOperation(); + separate_operation->set_channel(channel); + converter.add_operation(separate_operation); - converter.addLink(convertRGBToYCCOperation->getOutputSocket(0), - separateOperation->getInputSocket(0)); + converter.add_link(convertRGBToYCCOperation->get_output_socket(0), + separate_operation->get_input_socket(0)); if (ELEM(channel, 0, 3)) { - converter.addLink(separateOperation->getOutputSocket(0), - combineOperation->getInputSocket(channel)); + converter.add_link(separate_operation->get_output_socket(0), + combine_operation->get_input_socket(channel)); } else { - KeyingBlurOperation *blurXOperation = new KeyingBlurOperation(); - blurXOperation->setSize(size); - blurXOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_X); - converter.addOperation(blurXOperation); + KeyingBlurOperation *blur_xoperation = new KeyingBlurOperation(); + blur_xoperation->set_size(size); + blur_xoperation->set_axis(KeyingBlurOperation::BLUR_AXIS_X); + converter.add_operation(blur_xoperation); - KeyingBlurOperation *blurYOperation = new KeyingBlurOperation(); - blurYOperation->setSize(size); - blurYOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_Y); - converter.addOperation(blurYOperation); + KeyingBlurOperation *blur_yoperation = new KeyingBlurOperation(); + blur_yoperation->set_size(size); + blur_yoperation->set_axis(KeyingBlurOperation::BLUR_AXIS_Y); + converter.add_operation(blur_yoperation); - converter.addLink(separateOperation->getOutputSocket(), blurXOperation->getInputSocket(0)); - converter.addLink(blurXOperation->getOutputSocket(), blurYOperation->getInputSocket(0)); - converter.addLink(blurYOperation->getOutputSocket(0), - combineOperation->getInputSocket(channel)); + converter.add_link(separate_operation->get_output_socket(), + blur_xoperation->get_input_socket(0)); + converter.add_link(blur_xoperation->get_output_socket(), + blur_yoperation->get_input_socket(0)); + converter.add_link(blur_yoperation->get_output_socket(0), + combine_operation->get_input_socket(channel)); } } ConvertYCCToRGBOperation *convertYCCToRGBOperation = new ConvertYCCToRGBOperation(); - convertYCCToRGBOperation->setMode(BLI_YCC_ITU_BT709); - converter.addOperation(convertYCCToRGBOperation); + convertYCCToRGBOperation->set_mode(BLI_YCC_ITU_BT709); + converter.add_operation(convertYCCToRGBOperation); - converter.addLink(combineOperation->getOutputSocket(0), - convertYCCToRGBOperation->getInputSocket(0)); + converter.add_link(combine_operation->get_output_socket(0), + convertYCCToRGBOperation->get_input_socket(0)); - return convertYCCToRGBOperation->getOutputSocket(0); + return convertYCCToRGBOperation->get_output_socket(0); } -NodeOperationOutput *KeyingNode::setupPostBlur(NodeConverter &converter, - NodeOperationOutput *postBlurInput, - int size) const +NodeOperationOutput *KeyingNode::setup_post_blur(NodeConverter &converter, + NodeOperationOutput *post_blur_input, + int size) const { - KeyingBlurOperation *blurXOperation = new KeyingBlurOperation(); - blurXOperation->setSize(size); - blurXOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_X); - converter.addOperation(blurXOperation); + KeyingBlurOperation *blur_xoperation = new KeyingBlurOperation(); + blur_xoperation->set_size(size); + blur_xoperation->set_axis(KeyingBlurOperation::BLUR_AXIS_X); + converter.add_operation(blur_xoperation); - KeyingBlurOperation *blurYOperation = new KeyingBlurOperation(); - blurYOperation->setSize(size); - blurYOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_Y); - converter.addOperation(blurYOperation); + KeyingBlurOperation *blur_yoperation = new KeyingBlurOperation(); + blur_yoperation->set_size(size); + blur_yoperation->set_axis(KeyingBlurOperation::BLUR_AXIS_Y); + converter.add_operation(blur_yoperation); - converter.addLink(postBlurInput, blurXOperation->getInputSocket(0)); - converter.addLink(blurXOperation->getOutputSocket(), blurYOperation->getInputSocket(0)); + converter.add_link(post_blur_input, blur_xoperation->get_input_socket(0)); + converter.add_link(blur_xoperation->get_output_socket(), blur_yoperation->get_input_socket(0)); - return blurYOperation->getOutputSocket(); + return blur_yoperation->get_output_socket(); } -NodeOperationOutput *KeyingNode::setupDilateErode(NodeConverter &converter, - NodeOperationOutput *dilateErodeInput, - int distance) const +NodeOperationOutput *KeyingNode::setup_dilate_erode(NodeConverter &converter, + NodeOperationOutput *dilate_erode_input, + int distance) const { - DilateDistanceOperation *dilateErodeOperation; + DilateDistanceOperation *dilate_erode_operation; if (distance > 0) { - dilateErodeOperation = new DilateDistanceOperation(); - dilateErodeOperation->setDistance(distance); + dilate_erode_operation = new DilateDistanceOperation(); + dilate_erode_operation->set_distance(distance); } else { - dilateErodeOperation = new ErodeDistanceOperation(); - dilateErodeOperation->setDistance(-distance); + dilate_erode_operation = new ErodeDistanceOperation(); + dilate_erode_operation->set_distance(-distance); } - converter.addOperation(dilateErodeOperation); + converter.add_operation(dilate_erode_operation); - converter.addLink(dilateErodeInput, dilateErodeOperation->getInputSocket(0)); + converter.add_link(dilate_erode_input, dilate_erode_operation->get_input_socket(0)); - return dilateErodeOperation->getOutputSocket(0); + return dilate_erode_operation->get_output_socket(0); } -NodeOperationOutput *KeyingNode::setupFeather(NodeConverter &converter, - const CompositorContext &context, - NodeOperationOutput *featherInput, - int falloff, - int distance) const +NodeOperationOutput *KeyingNode::setup_feather(NodeConverter &converter, + const CompositorContext &context, + NodeOperationOutput *feather_input, + int falloff, + int distance) const { /* this uses a modified gaussian blur function otherwise its far too slow */ - eCompositorQuality quality = context.getQuality(); + eCompositorQuality quality = context.get_quality(); /* initialize node data */ NodeBlurData data; @@ -156,196 +158,198 @@ NodeOperationOutput *KeyingNode::setupFeather(NodeConverter &converter, } GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation(); - operationx->setData(&data); - operationx->setQuality(quality); - operationx->setSize(1.0f); - operationx->setSubtract(distance < 0); - operationx->setFalloff(falloff); - converter.addOperation(operationx); + operationx->set_data(&data); + operationx->set_quality(quality); + operationx->set_size(1.0f); + operationx->set_subtract(distance < 0); + operationx->set_falloff(falloff); + converter.add_operation(operationx); GaussianAlphaYBlurOperation *operationy = new GaussianAlphaYBlurOperation(); - operationy->setData(&data); - operationy->setQuality(quality); - operationy->setSize(1.0f); - operationy->setSubtract(distance < 0); - operationy->setFalloff(falloff); - converter.addOperation(operationy); + operationy->set_data(&data); + operationy->set_quality(quality); + operationy->set_size(1.0f); + operationy->set_subtract(distance < 0); + operationy->set_falloff(falloff); + converter.add_operation(operationy); - converter.addLink(featherInput, operationx->getInputSocket(0)); - converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0)); + converter.add_link(feather_input, operationx->get_input_socket(0)); + converter.add_link(operationx->get_output_socket(), operationy->get_input_socket(0)); - return operationy->getOutputSocket(); + return operationy->get_output_socket(); } -NodeOperationOutput *KeyingNode::setupDespill(NodeConverter &converter, - NodeOperationOutput *despillInput, - NodeInput *inputScreen, - float factor, - float colorBalance) const +NodeOperationOutput *KeyingNode::setup_despill(NodeConverter &converter, + NodeOperationOutput *despill_input, + NodeInput *input_screen, + float factor, + float color_balance) const { - KeyingDespillOperation *despillOperation = new KeyingDespillOperation(); - despillOperation->setDespillFactor(factor); - despillOperation->setColorBalance(colorBalance); - converter.addOperation(despillOperation); + KeyingDespillOperation *despill_operation = new KeyingDespillOperation(); + despill_operation->set_despill_factor(factor); + despill_operation->set_color_balance(color_balance); + converter.add_operation(despill_operation); - converter.addLink(despillInput, despillOperation->getInputSocket(0)); - converter.mapInputSocket(inputScreen, despillOperation->getInputSocket(1)); + converter.add_link(despill_input, despill_operation->get_input_socket(0)); + converter.map_input_socket(input_screen, despill_operation->get_input_socket(1)); - return despillOperation->getOutputSocket(0); + return despill_operation->get_output_socket(0); } -NodeOperationOutput *KeyingNode::setupClip(NodeConverter &converter, - NodeOperationOutput *clipInput, - int kernelRadius, - float kernelTolerance, - float clipBlack, - float clipWhite, - bool edgeMatte) const +NodeOperationOutput *KeyingNode::setup_clip(NodeConverter &converter, + NodeOperationOutput *clip_input, + int kernel_radius, + float kernel_tolerance, + float clip_black, + float clip_white, + bool edge_matte) const { - KeyingClipOperation *clipOperation = new KeyingClipOperation(); - clipOperation->setKernelRadius(kernelRadius); - clipOperation->setKernelTolerance(kernelTolerance); - clipOperation->setClipBlack(clipBlack); - clipOperation->setClipWhite(clipWhite); - clipOperation->setIsEdgeMatte(edgeMatte); - converter.addOperation(clipOperation); + KeyingClipOperation *clip_operation = new KeyingClipOperation(); + clip_operation->set_kernel_radius(kernel_radius); + clip_operation->set_kernel_tolerance(kernel_tolerance); + clip_operation->set_clip_black(clip_black); + clip_operation->set_clip_white(clip_white); + clip_operation->set_is_edge_matte(edge_matte); + converter.add_operation(clip_operation); - converter.addLink(clipInput, clipOperation->getInputSocket(0)); + converter.add_link(clip_input, clip_operation->get_input_socket(0)); - return clipOperation->getOutputSocket(0); + return clip_operation->get_output_socket(0); } -void KeyingNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void KeyingNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - NodeKeyingData *keying_data = (NodeKeyingData *)editorNode->storage; + bNode *editor_node = this->get_bnode(); + NodeKeyingData *keying_data = (NodeKeyingData *)editor_node->storage; - NodeInput *inputImage = this->getInputSocket(0); - NodeInput *inputScreen = this->getInputSocket(1); - NodeInput *inputGarbageMatte = this->getInputSocket(2); - NodeInput *inputCoreMatte = this->getInputSocket(3); - NodeOutput *outputImage = this->getOutputSocket(0); - NodeOutput *outputMatte = this->getOutputSocket(1); - NodeOutput *outputEdges = this->getOutputSocket(2); - NodeOperationOutput *postprocessedMatte = nullptr, *postprocessedImage = nullptr, - *edgesMatte = nullptr; + NodeInput *input_image = this->get_input_socket(0); + NodeInput *input_screen = this->get_input_socket(1); + NodeInput *input_garbage_matte = this->get_input_socket(2); + NodeInput *input_core_matte = this->get_input_socket(3); + NodeOutput *output_image = this->get_output_socket(0); + NodeOutput *output_matte = this->get_output_socket(1); + NodeOutput *output_edges = this->get_output_socket(2); + NodeOperationOutput *postprocessed_matte = nullptr, *postprocessed_image = nullptr, + *edges_matte = nullptr; /* keying operation */ - KeyingOperation *keyingOperation = new KeyingOperation(); - keyingOperation->setScreenBalance(keying_data->screen_balance); - converter.addOperation(keyingOperation); + KeyingOperation *keying_operation = new KeyingOperation(); + keying_operation->set_screen_balance(keying_data->screen_balance); + converter.add_operation(keying_operation); - converter.mapInputSocket(inputScreen, keyingOperation->getInputSocket(1)); + converter.map_input_socket(input_screen, keying_operation->get_input_socket(1)); if (keying_data->blur_pre) { /* Chroma pre-blur operation for input of keying operation. */ - NodeOperationOutput *preBlurredImage = setupPreBlur( - converter, inputImage, keying_data->blur_pre); - converter.addLink(preBlurredImage, keyingOperation->getInputSocket(0)); + NodeOperationOutput *pre_blurred_image = setup_pre_blur( + converter, input_image, keying_data->blur_pre); + converter.add_link(pre_blurred_image, keying_operation->get_input_socket(0)); } else { - converter.mapInputSocket(inputImage, keyingOperation->getInputSocket(0)); + converter.map_input_socket(input_image, keying_operation->get_input_socket(0)); } - postprocessedMatte = keyingOperation->getOutputSocket(); + postprocessed_matte = keying_operation->get_output_socket(); /* black / white clipping */ if (keying_data->clip_black > 0.0f || keying_data->clip_white < 1.0f) { - postprocessedMatte = setupClip(converter, - postprocessedMatte, - keying_data->edge_kernel_radius, - keying_data->edge_kernel_tolerance, - keying_data->clip_black, - keying_data->clip_white, - false); + postprocessed_matte = setup_clip(converter, + postprocessed_matte, + keying_data->edge_kernel_radius, + keying_data->edge_kernel_tolerance, + keying_data->clip_black, + keying_data->clip_white, + false); } /* output edge matte */ - edgesMatte = setupClip(converter, - postprocessedMatte, - keying_data->edge_kernel_radius, - keying_data->edge_kernel_tolerance, - keying_data->clip_black, - keying_data->clip_white, - true); + edges_matte = setup_clip(converter, + postprocessed_matte, + keying_data->edge_kernel_radius, + keying_data->edge_kernel_tolerance, + keying_data->clip_black, + keying_data->clip_white, + true); /* apply garbage matte */ - if (inputGarbageMatte->isLinked()) { - SetValueOperation *valueOperation = new SetValueOperation(); - valueOperation->setValue(1.0f); - converter.addOperation(valueOperation); + if (input_garbage_matte->is_linked()) { + SetValueOperation *value_operation = new SetValueOperation(); + value_operation->set_value(1.0f); + converter.add_operation(value_operation); - MathSubtractOperation *subtractOperation = new MathSubtractOperation(); - converter.addOperation(subtractOperation); + MathSubtractOperation *subtract_operation = new MathSubtractOperation(); + converter.add_operation(subtract_operation); - MathMinimumOperation *minOperation = new MathMinimumOperation(); - converter.addOperation(minOperation); + MathMinimumOperation *min_operation = new MathMinimumOperation(); + converter.add_operation(min_operation); - converter.addLink(valueOperation->getOutputSocket(), subtractOperation->getInputSocket(0)); - converter.mapInputSocket(inputGarbageMatte, subtractOperation->getInputSocket(1)); + converter.add_link(value_operation->get_output_socket(), + subtract_operation->get_input_socket(0)); + converter.map_input_socket(input_garbage_matte, subtract_operation->get_input_socket(1)); - converter.addLink(subtractOperation->getOutputSocket(), minOperation->getInputSocket(0)); - converter.addLink(postprocessedMatte, minOperation->getInputSocket(1)); + converter.add_link(subtract_operation->get_output_socket(), + min_operation->get_input_socket(0)); + converter.add_link(postprocessed_matte, min_operation->get_input_socket(1)); - postprocessedMatte = minOperation->getOutputSocket(); + postprocessed_matte = min_operation->get_output_socket(); } /* apply core matte */ - if (inputCoreMatte->isLinked()) { - MathMaximumOperation *maxOperation = new MathMaximumOperation(); - converter.addOperation(maxOperation); + if (input_core_matte->is_linked()) { + MathMaximumOperation *max_operation = new MathMaximumOperation(); + converter.add_operation(max_operation); - converter.mapInputSocket(inputCoreMatte, maxOperation->getInputSocket(0)); - converter.addLink(postprocessedMatte, maxOperation->getInputSocket(1)); + converter.map_input_socket(input_core_matte, max_operation->get_input_socket(0)); + converter.add_link(postprocessed_matte, max_operation->get_input_socket(1)); - postprocessedMatte = maxOperation->getOutputSocket(); + postprocessed_matte = max_operation->get_output_socket(); } /* apply blur on matte if needed */ if (keying_data->blur_post) { - postprocessedMatte = setupPostBlur(converter, postprocessedMatte, keying_data->blur_post); + postprocessed_matte = setup_post_blur(converter, postprocessed_matte, keying_data->blur_post); } /* matte dilate/erode */ if (keying_data->dilate_distance != 0) { - postprocessedMatte = setupDilateErode( - converter, postprocessedMatte, keying_data->dilate_distance); + postprocessed_matte = setup_dilate_erode( + converter, postprocessed_matte, keying_data->dilate_distance); } /* matte feather */ if (keying_data->feather_distance != 0) { - postprocessedMatte = setupFeather(converter, - context, - postprocessedMatte, - keying_data->feather_falloff, - keying_data->feather_distance); + postprocessed_matte = setup_feather(converter, + context, + postprocessed_matte, + keying_data->feather_falloff, + keying_data->feather_distance); } /* set alpha channel to output image */ - SetAlphaMultiplyOperation *alphaOperation = new SetAlphaMultiplyOperation(); - converter.addOperation(alphaOperation); + SetAlphaMultiplyOperation *alpha_operation = new SetAlphaMultiplyOperation(); + converter.add_operation(alpha_operation); - converter.mapInputSocket(inputImage, alphaOperation->getInputSocket(0)); - converter.addLink(postprocessedMatte, alphaOperation->getInputSocket(1)); + converter.map_input_socket(input_image, alpha_operation->get_input_socket(0)); + converter.add_link(postprocessed_matte, alpha_operation->get_input_socket(1)); - postprocessedImage = alphaOperation->getOutputSocket(); + postprocessed_image = alpha_operation->get_output_socket(); /* despill output image */ if (keying_data->despill_factor > 0.0f) { - postprocessedImage = setupDespill(converter, - postprocessedImage, - inputScreen, - keying_data->despill_factor, - keying_data->despill_balance); + postprocessed_image = setup_despill(converter, + postprocessed_image, + input_screen, + keying_data->despill_factor, + keying_data->despill_balance); } /* connect result to output sockets */ - converter.mapOutputSocket(outputImage, postprocessedImage); - converter.mapOutputSocket(outputMatte, postprocessedMatte); + converter.map_output_socket(output_image, postprocessed_image); + converter.map_output_socket(output_matte, postprocessed_matte); - if (edgesMatte) { - converter.mapOutputSocket(outputEdges, edgesMatte); + if (edges_matte) { + converter.map_output_socket(output_edges, edges_matte); } } diff --git a/source/blender/compositor/nodes/COM_KeyingNode.h b/source/blender/compositor/nodes/COM_KeyingNode.h index 6d5e3ca1883..ed70433fea3 100644 --- a/source/blender/compositor/nodes/COM_KeyingNode.h +++ b/source/blender/compositor/nodes/COM_KeyingNode.h @@ -28,37 +28,37 @@ namespace blender::compositor { */ class KeyingNode : public Node { protected: - NodeOperationOutput *setupPreBlur(NodeConverter &converter, - NodeInput *inputImage, - int size) const; - NodeOperationOutput *setupPostBlur(NodeConverter &converter, - NodeOperationOutput *postBlurInput, - int size) const; - NodeOperationOutput *setupDilateErode(NodeConverter &converter, - NodeOperationOutput *dilateErodeInput, - int distance) const; - NodeOperationOutput *setupFeather(NodeConverter &converter, - const CompositorContext &context, - NodeOperationOutput *featherInput, - int falloff, - int distance) const; - NodeOperationOutput *setupDespill(NodeConverter &converter, - NodeOperationOutput *despillInput, - NodeInput *inputScreen, - float factor, - float colorBalance) const; - NodeOperationOutput *setupClip(NodeConverter &converter, - NodeOperationOutput *clipInput, - int kernelRadius, - float kernelTolerance, - float clipBlack, - float clipWhite, - bool edgeMatte) const; + NodeOperationOutput *setup_pre_blur(NodeConverter &converter, + NodeInput *input_image, + int size) const; + NodeOperationOutput *setup_post_blur(NodeConverter &converter, + NodeOperationOutput *post_blur_input, + int size) const; + NodeOperationOutput *setup_dilate_erode(NodeConverter &converter, + NodeOperationOutput *dilate_erode_input, + int distance) const; + NodeOperationOutput *setup_feather(NodeConverter &converter, + const CompositorContext &context, + NodeOperationOutput *feather_input, + int falloff, + int distance) const; + NodeOperationOutput *setup_despill(NodeConverter &converter, + NodeOperationOutput *despill_input, + NodeInput *input_screen, + float factor, + float color_balance) const; + NodeOperationOutput *setup_clip(NodeConverter &converter, + NodeOperationOutput *clip_input, + int kernel_radius, + float kernel_tolerance, + float clip_black, + float clip_white, + bool edge_matte) const; public: - KeyingNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + KeyingNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc index e405c01ddf2..bf5d7bb237a 100644 --- a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc +++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc @@ -21,28 +21,28 @@ namespace blender::compositor { -KeyingScreenNode::KeyingScreenNode(bNode *editorNode) : Node(editorNode) +KeyingScreenNode::KeyingScreenNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void KeyingScreenNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void KeyingScreenNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - MovieClip *clip = (MovieClip *)editorNode->id; - NodeKeyingScreenData *keyingscreen_data = (NodeKeyingScreenData *)editorNode->storage; + bNode *editor_node = this->get_bnode(); + MovieClip *clip = (MovieClip *)editor_node->id; + NodeKeyingScreenData *keyingscreen_data = (NodeKeyingScreenData *)editor_node->storage; - NodeOutput *outputScreen = this->getOutputSocket(0); + NodeOutput *output_screen = this->get_output_socket(0); /* Always connect the output image. */ KeyingScreenOperation *operation = new KeyingScreenOperation(); - operation->setMovieClip(clip); - operation->setTrackingObject(keyingscreen_data->tracking_object); - operation->setFramenumber(context.getFramenumber()); - converter.addOperation(operation); + operation->set_movie_clip(clip); + operation->set_tracking_object(keyingscreen_data->tracking_object); + operation->set_framenumber(context.get_framenumber()); + converter.add_operation(operation); - converter.mapOutputSocket(outputScreen, operation->getOutputSocket()); + converter.map_output_socket(output_screen, operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.h b/source/blender/compositor/nodes/COM_KeyingScreenNode.h index f2ad3b344f1..3704081041f 100644 --- a/source/blender/compositor/nodes/COM_KeyingScreenNode.h +++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class KeyingScreenNode : public Node { public: - KeyingScreenNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + KeyingScreenNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.cc b/source/blender/compositor/nodes/COM_LensDistortionNode.cc index 46223c1f294..7cef21487df 100644 --- a/source/blender/compositor/nodes/COM_LensDistortionNode.cc +++ b/source/blender/compositor/nodes/COM_LensDistortionNode.cc @@ -22,42 +22,42 @@ namespace blender::compositor { -LensDistortionNode::LensDistortionNode(bNode *editorNode) : Node(editorNode) +LensDistortionNode::LensDistortionNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void LensDistortionNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void LensDistortionNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorNode = this->getbNode(); - NodeLensDist *data = (NodeLensDist *)editorNode->storage; + bNode *editor_node = this->get_bnode(); + NodeLensDist *data = (NodeLensDist *)editor_node->storage; if (data->proj) { ProjectorLensDistortionOperation *operation = new ProjectorLensDistortionOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } else { ScreenLensDistortionOperation *operation = new ScreenLensDistortionOperation(); - operation->setFit(data->fit); - operation->setJitter(data->jit); + operation->set_fit(data->fit); + operation->set_jitter(data->jit); - if (!getInputSocket(1)->isLinked()) { - operation->setDistortion(getInputSocket(1)->getEditorValueFloat()); + if (!get_input_socket(1)->is_linked()) { + operation->set_distortion(get_input_socket(1)->get_editor_value_float()); } - if (!getInputSocket(2)->isLinked()) { - operation->setDispersion(getInputSocket(2)->getEditorValueFloat()); + if (!get_input_socket(2)->is_linked()) { + operation->set_dispersion(get_input_socket(2)->get_editor_value_float()); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.h b/source/blender/compositor/nodes/COM_LensDistortionNode.h index 4de1b0fe4da..154b8c20b86 100644 --- a/source/blender/compositor/nodes/COM_LensDistortionNode.h +++ b/source/blender/compositor/nodes/COM_LensDistortionNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class LensDistortionNode : public Node { public: - LensDistortionNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + LensDistortionNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc index e38aff91431..645cbe91fd2 100644 --- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc @@ -22,34 +22,34 @@ namespace blender::compositor { -LuminanceMatteNode::LuminanceMatteNode(bNode *editorNode) : Node(editorNode) +LuminanceMatteNode::LuminanceMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void LuminanceMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void LuminanceMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorsnode = getbNode(); - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); + bNode *editorsnode = get_bnode(); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); - LuminanceMatteOperation *operationSet = new LuminanceMatteOperation(); - operationSet->setSettings((NodeChroma *)editorsnode->storage); - converter.addOperation(operationSet); + LuminanceMatteOperation *operation_set = new LuminanceMatteOperation(); + operation_set->set_settings((NodeChroma *)editorsnode->storage); + converter.add_operation(operation_set); - converter.mapInputSocket(inputSocket, operationSet->getInputSocket(0)); - converter.mapOutputSocket(outputSocketMatte, operationSet->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation_set->get_input_socket(0)); + converter.map_output_socket(output_socket_matte, operation_set->get_output_socket(0)); SetAlphaMultiplyOperation *operation = new SetAlphaMultiplyOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.addLink(operationSet->getOutputSocket(), operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketImage, operation->getOutputSocket()); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.add_link(operation_set->get_output_socket(), operation->get_input_socket(1)); + converter.map_output_socket(output_socket_image, operation->get_output_socket()); - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.h b/source/blender/compositor/nodes/COM_LuminanceMatteNode.h index ef4ebc8ad92..1f934c73d1e 100644 --- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.h +++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class LuminanceMatteNode : public Node { public: - LuminanceMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + LuminanceMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapRangeNode.cc b/source/blender/compositor/nodes/COM_MapRangeNode.cc index 01288b779ec..45f3113ca9e 100644 --- a/source/blender/compositor/nodes/COM_MapRangeNode.cc +++ b/source/blender/compositor/nodes/COM_MapRangeNode.cc @@ -22,31 +22,31 @@ namespace blender::compositor { -MapRangeNode::MapRangeNode(bNode *editorNode) : Node(editorNode) +MapRangeNode::MapRangeNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MapRangeNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void MapRangeNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *valueSocket = this->getInputSocket(0); - NodeInput *sourceMinSocket = this->getInputSocket(1); - NodeInput *sourceMaxSocket = this->getInputSocket(2); - NodeInput *destMinSocket = this->getInputSocket(3); - NodeInput *destMaxSocket = this->getInputSocket(4); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *value_socket = this->get_input_socket(0); + NodeInput *source_min_socket = this->get_input_socket(1); + NodeInput *source_max_socket = this->get_input_socket(2); + NodeInput *dest_min_socket = this->get_input_socket(3); + NodeInput *dest_max_socket = this->get_input_socket(4); + NodeOutput *output_socket = this->get_output_socket(0); MapRangeOperation *operation = new MapRangeOperation(); - operation->setUseClamp(this->getbNode()->custom1); - converter.addOperation(operation); + operation->set_use_clamp(this->get_bnode()->custom1); + converter.add_operation(operation); - converter.mapInputSocket(valueSocket, operation->getInputSocket(0)); - converter.mapInputSocket(sourceMinSocket, operation->getInputSocket(1)); - converter.mapInputSocket(sourceMaxSocket, operation->getInputSocket(2)); - converter.mapInputSocket(destMinSocket, operation->getInputSocket(3)); - converter.mapInputSocket(destMaxSocket, operation->getInputSocket(4)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(value_socket, operation->get_input_socket(0)); + converter.map_input_socket(source_min_socket, operation->get_input_socket(1)); + converter.map_input_socket(source_max_socket, operation->get_input_socket(2)); + converter.map_input_socket(dest_min_socket, operation->get_input_socket(3)); + converter.map_input_socket(dest_max_socket, operation->get_input_socket(4)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapRangeNode.h b/source/blender/compositor/nodes/COM_MapRangeNode.h index ad6fd78a7d5..c9323ac00c2 100644 --- a/source/blender/compositor/nodes/COM_MapRangeNode.h +++ b/source/blender/compositor/nodes/COM_MapRangeNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class MapRangeNode : public Node { public: - MapRangeNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MapRangeNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapUVNode.cc b/source/blender/compositor/nodes/COM_MapUVNode.cc index ddda1e2db1d..9c794165e0b 100644 --- a/source/blender/compositor/nodes/COM_MapUVNode.cc +++ b/source/blender/compositor/nodes/COM_MapUVNode.cc @@ -21,24 +21,24 @@ namespace blender::compositor { -MapUVNode::MapUVNode(bNode *editorNode) : Node(editorNode) +MapUVNode::MapUVNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MapUVNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void MapUVNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); MapUVOperation *operation = new MapUVOperation(); - operation->setAlpha((float)node->custom1); + operation->set_alpha((float)node->custom1); operation->set_canvas_input_index(1); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapUVNode.h b/source/blender/compositor/nodes/COM_MapUVNode.h index f7f4db167ea..eebfd0e66fa 100644 --- a/source/blender/compositor/nodes/COM_MapUVNode.h +++ b/source/blender/compositor/nodes/COM_MapUVNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class MapUVNode : public Node { public: - MapUVNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MapUVNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapValueNode.cc b/source/blender/compositor/nodes/COM_MapValueNode.cc index 9c6be55e93b..8c81a902863 100644 --- a/source/blender/compositor/nodes/COM_MapValueNode.cc +++ b/source/blender/compositor/nodes/COM_MapValueNode.cc @@ -22,25 +22,25 @@ namespace blender::compositor { -MapValueNode::MapValueNode(bNode *editorNode) : Node(editorNode) +MapValueNode::MapValueNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MapValueNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void MapValueNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - TexMapping *storage = (TexMapping *)this->getbNode()->storage; + TexMapping *storage = (TexMapping *)this->get_bnode()->storage; - NodeInput *colorSocket = this->getInputSocket(0); - NodeOutput *valueSocket = this->getOutputSocket(0); + NodeInput *color_socket = this->get_input_socket(0); + NodeOutput *value_socket = this->get_output_socket(0); - MapValueOperation *convertProg = new MapValueOperation(); - convertProg->setSettings(storage); - converter.addOperation(convertProg); + MapValueOperation *convert_prog = new MapValueOperation(); + convert_prog->set_settings(storage); + converter.add_operation(convert_prog); - converter.mapInputSocket(colorSocket, convertProg->getInputSocket(0)); - converter.mapOutputSocket(valueSocket, convertProg->getOutputSocket(0)); + converter.map_input_socket(color_socket, convert_prog->get_input_socket(0)); + converter.map_output_socket(value_socket, convert_prog->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapValueNode.h b/source/blender/compositor/nodes/COM_MapValueNode.h index dcac1d6e3c5..4516d7eb016 100644 --- a/source/blender/compositor/nodes/COM_MapValueNode.h +++ b/source/blender/compositor/nodes/COM_MapValueNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class MapValueNode : public Node { public: - MapValueNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MapValueNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc index 27488c8a6c8..b1e0506089e 100644 --- a/source/blender/compositor/nodes/COM_MaskNode.cc +++ b/source/blender/compositor/nodes/COM_MaskNode.cc @@ -21,51 +21,51 @@ namespace blender::compositor { -MaskNode::MaskNode(bNode *editorNode) : Node(editorNode) +MaskNode::MaskNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void MaskNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - const RenderData *rd = context.getRenderData(); - const float render_size_factor = context.getRenderPercentageAsFactor(); + const RenderData *rd = context.get_render_data(); + const float render_size_factor = context.get_render_percentage_as_factor(); - NodeOutput *outputMask = this->getOutputSocket(0); + NodeOutput *output_mask = this->get_output_socket(0); - bNode *editorNode = this->getbNode(); - NodeMask *data = (NodeMask *)editorNode->storage; - Mask *mask = (Mask *)editorNode->id; + bNode *editor_node = this->get_bnode(); + NodeMask *data = (NodeMask *)editor_node->storage; + Mask *mask = (Mask *)editor_node->id; /* Always connect the output image. */ MaskOperation *operation = new MaskOperation(); - if (editorNode->custom1 & CMP_NODEFLAG_MASK_FIXED) { - operation->setMaskWidth(data->size_x); - operation->setMaskHeight(data->size_y); + if (editor_node->custom1 & CMP_NODEFLAG_MASK_FIXED) { + operation->set_mask_width(data->size_x); + operation->set_mask_height(data->size_y); } - else if (editorNode->custom1 & CMP_NODEFLAG_MASK_FIXED_SCENE) { - operation->setMaskWidth(data->size_x * render_size_factor); - operation->setMaskHeight(data->size_y * render_size_factor); + else if (editor_node->custom1 & CMP_NODEFLAG_MASK_FIXED_SCENE) { + operation->set_mask_width(data->size_x * render_size_factor); + operation->set_mask_height(data->size_y * render_size_factor); } else { - operation->setMaskWidth(rd->xsch * render_size_factor); - operation->setMaskHeight(rd->ysch * render_size_factor); + operation->set_mask_width(rd->xsch * render_size_factor); + operation->set_mask_height(rd->ysch * render_size_factor); } - operation->setMask(mask); - operation->setFramenumber(context.getFramenumber()); - operation->setFeather((bool)(editorNode->custom1 & CMP_NODEFLAG_MASK_NO_FEATHER) == 0); + operation->set_mask(mask); + operation->set_framenumber(context.get_framenumber()); + operation->set_feather((bool)(editor_node->custom1 & CMP_NODEFLAG_MASK_NO_FEATHER) == 0); - if ((editorNode->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) && (editorNode->custom2 > 1) && - (editorNode->custom3 > FLT_EPSILON)) { - operation->setMotionBlurSamples(editorNode->custom2); - operation->setMotionBlurShutter(editorNode->custom3); + if ((editor_node->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) && (editor_node->custom2 > 1) && + (editor_node->custom3 > FLT_EPSILON)) { + operation->set_motion_blur_samples(editor_node->custom2); + operation->set_motion_blur_shutter(editor_node->custom3); } - converter.addOperation(operation); - converter.mapOutputSocket(outputMask, operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_output_socket(output_mask, operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MaskNode.h b/source/blender/compositor/nodes/COM_MaskNode.h index 5890cf5957a..0d9d4284b27 100644 --- a/source/blender/compositor/nodes/COM_MaskNode.h +++ b/source/blender/compositor/nodes/COM_MaskNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class MaskNode : public Node { public: - MaskNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MaskNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MathNode.cc b/source/blender/compositor/nodes/COM_MathNode.cc index f8a1530fe29..6102aed0f88 100644 --- a/source/blender/compositor/nodes/COM_MathNode.cc +++ b/source/blender/compositor/nodes/COM_MathNode.cc @@ -21,12 +21,12 @@ namespace blender::compositor { -void MathNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void MathNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { MathBaseOperation *operation = nullptr; - switch (this->getbNode()->custom1) { + switch (this->get_bnode()->custom1) { case NODE_MATH_ADD: operation = new MathAddOperation(); break; @@ -150,14 +150,14 @@ void MathNode::convertToOperations(NodeConverter &converter, } if (operation) { - bool useClamp = getbNode()->custom2; - operation->setUseClamp(useClamp); - converter.addOperation(operation); + bool use_clamp = get_bnode()->custom2; + operation->set_use_clamp(use_clamp); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_MathNode.h b/source/blender/compositor/nodes/COM_MathNode.h index 5db59e62bab..7f60fc03fcd 100644 --- a/source/blender/compositor/nodes/COM_MathNode.h +++ b/source/blender/compositor/nodes/COM_MathNode.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class MathNode : public Node { public: - MathNode(bNode *editorNode) : Node(editorNode) + MathNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MixNode.cc b/source/blender/compositor/nodes/COM_MixNode.cc index 9eb38783d6a..ae7736b4b67 100644 --- a/source/blender/compositor/nodes/COM_MixNode.cc +++ b/source/blender/compositor/nodes/COM_MixNode.cc @@ -24,91 +24,91 @@ namespace blender::compositor { -MixNode::MixNode(bNode *editorNode) : Node(editorNode) +MixNode::MixNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MixNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void MixNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *valueSocket = this->getInputSocket(0); - NodeInput *color1Socket = this->getInputSocket(1); - NodeInput *color2Socket = this->getInputSocket(2); - NodeOutput *outputSocket = this->getOutputSocket(0); - bNode *editorNode = this->getbNode(); - bool useAlphaPremultiply = (this->getbNode()->custom2 & 1) != 0; - bool useClamp = (this->getbNode()->custom2 & 2) != 0; + NodeInput *value_socket = this->get_input_socket(0); + NodeInput *color1Socket = this->get_input_socket(1); + NodeInput *color2Socket = this->get_input_socket(2); + NodeOutput *output_socket = this->get_output_socket(0); + bNode *editor_node = this->get_bnode(); + bool use_alpha_premultiply = (this->get_bnode()->custom2 & 1) != 0; + bool use_clamp = (this->get_bnode()->custom2 & 2) != 0; - MixBaseOperation *convertProg; - switch (editorNode->custom1) { + MixBaseOperation *convert_prog; + switch (editor_node->custom1) { case MA_RAMP_ADD: - convertProg = new MixAddOperation(); + convert_prog = new MixAddOperation(); break; case MA_RAMP_MULT: - convertProg = new MixMultiplyOperation(); + convert_prog = new MixMultiplyOperation(); break; case MA_RAMP_LIGHT: - convertProg = new MixLightenOperation(); + convert_prog = new MixLightenOperation(); break; case MA_RAMP_BURN: - convertProg = new MixColorBurnOperation(); + convert_prog = new MixColorBurnOperation(); break; case MA_RAMP_HUE: - convertProg = new MixHueOperation(); + convert_prog = new MixHueOperation(); break; case MA_RAMP_COLOR: - convertProg = new MixColorOperation(); + convert_prog = new MixColorOperation(); break; case MA_RAMP_SOFT: - convertProg = new MixSoftLightOperation(); + convert_prog = new MixSoftLightOperation(); break; case MA_RAMP_SCREEN: - convertProg = new MixScreenOperation(); + convert_prog = new MixScreenOperation(); break; case MA_RAMP_LINEAR: - convertProg = new MixLinearLightOperation(); + convert_prog = new MixLinearLightOperation(); break; case MA_RAMP_DIFF: - convertProg = new MixDifferenceOperation(); + convert_prog = new MixDifferenceOperation(); break; case MA_RAMP_SAT: - convertProg = new MixSaturationOperation(); + convert_prog = new MixSaturationOperation(); break; case MA_RAMP_DIV: - convertProg = new MixDivideOperation(); + convert_prog = new MixDivideOperation(); break; case MA_RAMP_SUB: - convertProg = new MixSubtractOperation(); + convert_prog = new MixSubtractOperation(); break; case MA_RAMP_DARK: - convertProg = new MixDarkenOperation(); + convert_prog = new MixDarkenOperation(); break; case MA_RAMP_OVERLAY: - convertProg = new MixOverlayOperation(); + convert_prog = new MixOverlayOperation(); break; case MA_RAMP_VAL: - convertProg = new MixValueOperation(); + convert_prog = new MixValueOperation(); break; case MA_RAMP_DODGE: - convertProg = new MixDodgeOperation(); + convert_prog = new MixDodgeOperation(); break; case MA_RAMP_BLEND: default: - convertProg = new MixBlendOperation(); + convert_prog = new MixBlendOperation(); break; } - convertProg->setUseValueAlphaMultiply(useAlphaPremultiply); - convertProg->setUseClamp(useClamp); - converter.addOperation(convertProg); + convert_prog->set_use_value_alpha_multiply(use_alpha_premultiply); + convert_prog->set_use_clamp(use_clamp); + converter.add_operation(convert_prog); - converter.mapInputSocket(valueSocket, convertProg->getInputSocket(0)); - converter.mapInputSocket(color1Socket, convertProg->getInputSocket(1)); - converter.mapInputSocket(color2Socket, convertProg->getInputSocket(2)); - converter.mapOutputSocket(outputSocket, convertProg->getOutputSocket(0)); + converter.map_input_socket(value_socket, convert_prog->get_input_socket(0)); + converter.map_input_socket(color1Socket, convert_prog->get_input_socket(1)); + converter.map_input_socket(color2Socket, convert_prog->get_input_socket(2)); + converter.map_output_socket(output_socket, convert_prog->get_output_socket(0)); - converter.addPreview(convertProg->getOutputSocket(0)); + converter.add_preview(convert_prog->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MixNode.h b/source/blender/compositor/nodes/COM_MixNode.h index 81f9c41871e..1a6bdf1cf57 100644 --- a/source/blender/compositor/nodes/COM_MixNode.h +++ b/source/blender/compositor/nodes/COM_MixNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class MixNode : public Node { public: - MixNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MixNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc index 9c2e3c95a03..b71e379304e 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.cc +++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc @@ -29,58 +29,58 @@ namespace blender::compositor { -MovieClipNode::MovieClipNode(bNode *editorNode) : Node(editorNode) +MovieClipNode::MovieClipNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MovieClipNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void MovieClipNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeOutput *outputMovieClip = this->getOutputSocket(0); - NodeOutput *alphaMovieClip = this->getOutputSocket(1); - NodeOutput *offsetXMovieClip = this->getOutputSocket(2); - NodeOutput *offsetYMovieClip = this->getOutputSocket(3); - NodeOutput *scaleMovieClip = this->getOutputSocket(4); - NodeOutput *angleMovieClip = this->getOutputSocket(5); + NodeOutput *output_movie_clip = this->get_output_socket(0); + NodeOutput *alpha_movie_clip = this->get_output_socket(1); + NodeOutput *offset_xmovie_clip = this->get_output_socket(2); + NodeOutput *offset_ymovie_clip = this->get_output_socket(3); + NodeOutput *scale_movie_clip = this->get_output_socket(4); + NodeOutput *angle_movie_clip = this->get_output_socket(5); - bNode *editorNode = this->getbNode(); - MovieClip *movieClip = (MovieClip *)editorNode->id; - MovieClipUser *movieClipUser = (MovieClipUser *)editorNode->storage; - bool cacheFrame = !context.isRendering(); + bNode *editor_node = this->get_bnode(); + MovieClip *movie_clip = (MovieClip *)editor_node->id; + MovieClipUser *movie_clip_user = (MovieClipUser *)editor_node->storage; + bool cache_frame = !context.is_rendering(); ImBuf *ibuf = nullptr; - if (movieClip) { - if (cacheFrame) { - ibuf = BKE_movieclip_get_ibuf(movieClip, movieClipUser); + if (movie_clip) { + if (cache_frame) { + ibuf = BKE_movieclip_get_ibuf(movie_clip, movie_clip_user); } else { ibuf = BKE_movieclip_get_ibuf_flag( - movieClip, movieClipUser, movieClip->flag, MOVIECLIP_CACHE_SKIP); + movie_clip, movie_clip_user, movie_clip->flag, MOVIECLIP_CACHE_SKIP); } } /* Always connect the output image. */ MovieClipOperation *operation = new MovieClipOperation(); - operation->setMovieClip(movieClip); - operation->setMovieClipUser(movieClipUser); - operation->setFramenumber(context.getFramenumber()); - operation->setCacheFrame(cacheFrame); + operation->set_movie_clip(movie_clip); + operation->set_movie_clip_user(movie_clip_user); + operation->set_framenumber(context.get_framenumber()); + operation->set_cache_frame(cache_frame); - converter.addOperation(operation); - converter.mapOutputSocket(outputMovieClip, operation->getOutputSocket()); - converter.addPreview(operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_output_socket(output_movie_clip, operation->get_output_socket()); + converter.add_preview(operation->get_output_socket()); - MovieClipAlphaOperation *alphaOperation = new MovieClipAlphaOperation(); - alphaOperation->setMovieClip(movieClip); - alphaOperation->setMovieClipUser(movieClipUser); - alphaOperation->setFramenumber(context.getFramenumber()); - alphaOperation->setCacheFrame(cacheFrame); + MovieClipAlphaOperation *alpha_operation = new MovieClipAlphaOperation(); + alpha_operation->set_movie_clip(movie_clip); + alpha_operation->set_movie_clip_user(movie_clip_user); + alpha_operation->set_framenumber(context.get_framenumber()); + alpha_operation->set_cache_frame(cache_frame); - converter.addOperation(alphaOperation); - converter.mapOutputSocket(alphaMovieClip, alphaOperation->getOutputSocket()); + converter.add_operation(alpha_operation); + converter.map_output_socket(alpha_movie_clip, alpha_operation->get_output_socket()); - MovieTrackingStabilization *stab = &movieClip->tracking.stabilization; + MovieTrackingStabilization *stab = &movie_clip->tracking.stabilization; float loc[2], scale, angle; loc[0] = 0.0f; loc[1] = 0.0f; @@ -89,18 +89,18 @@ void MovieClipNode::convertToOperations(NodeConverter &converter, if (ibuf) { if (stab->flag & TRACKING_2D_STABILIZATION) { - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip, - context.getFramenumber()); + int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip, + context.get_framenumber()); BKE_tracking_stabilization_data_get( - movieClip, clip_framenr, ibuf->x, ibuf->y, loc, &scale, &angle); + movie_clip, clip_framenr, ibuf->x, ibuf->y, loc, &scale, &angle); } } - converter.addOutputValue(offsetXMovieClip, loc[0]); - converter.addOutputValue(offsetYMovieClip, loc[1]); - converter.addOutputValue(scaleMovieClip, scale); - converter.addOutputValue(angleMovieClip, angle); + converter.add_output_value(offset_xmovie_clip, loc[0]); + converter.add_output_value(offset_ymovie_clip, loc[1]); + converter.add_output_value(scale_movie_clip, scale); + converter.add_output_value(angle_movie_clip, angle); if (ibuf) { IMB_freeImBuf(ibuf); diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.h b/source/blender/compositor/nodes/COM_MovieClipNode.h index a469ce9e2a4..cf9e7a3e01e 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.h +++ b/source/blender/compositor/nodes/COM_MovieClipNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class MovieClipNode : public Node { public: - MovieClipNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MovieClipNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc index c9155ee8ca3..03b780198cd 100644 --- a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc +++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc @@ -22,27 +22,27 @@ namespace blender::compositor { -MovieDistortionNode::MovieDistortionNode(bNode *editorNode) : Node(editorNode) +MovieDistortionNode::MovieDistortionNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MovieDistortionNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void MovieDistortionNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); MovieClip *clip = (MovieClip *)bnode->id; - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); MovieDistortionOperation *operation = new MovieDistortionOperation(bnode->custom1 == 1); - operation->setMovieClip(clip); - operation->setFramenumber(context.getFramenumber()); - converter.addOperation(operation); + operation->set_movie_clip(clip); + operation->set_framenumber(context.get_framenumber()); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.h b/source/blender/compositor/nodes/COM_MovieDistortionNode.h index 0c1610aa3d3..530b0dd6f97 100644 --- a/source/blender/compositor/nodes/COM_MovieDistortionNode.h +++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class MovieDistortionNode : public Node { public: - MovieDistortionNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MovieDistortionNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_NormalNode.cc b/source/blender/compositor/nodes/COM_NormalNode.cc index 5a05f4c89b5..f9875c6015c 100644 --- a/source/blender/compositor/nodes/COM_NormalNode.cc +++ b/source/blender/compositor/nodes/COM_NormalNode.cc @@ -22,37 +22,37 @@ namespace blender::compositor { -NormalNode::NormalNode(bNode *editorNode) : Node(editorNode) +NormalNode::NormalNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void NormalNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void NormalNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); - NodeOutput *outputSocketDotproduct = this->getOutputSocket(1); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); + NodeOutput *output_socket_dotproduct = this->get_output_socket(1); - SetVectorOperation *operationSet = new SetVectorOperation(); + SetVectorOperation *operation_set = new SetVectorOperation(); float normal[3]; - outputSocket->getEditorValueVector(normal); + output_socket->get_editor_value_vector(normal); /* animation can break normalization, this restores it */ normalize_v3(normal); - operationSet->setX(normal[0]); - operationSet->setY(normal[1]); - operationSet->setZ(normal[2]); - operationSet->setW(0.0f); - converter.addOperation(operationSet); + operation_set->setX(normal[0]); + operation_set->setY(normal[1]); + operation_set->setZ(normal[2]); + operation_set->setW(0.0f); + converter.add_operation(operation_set); - converter.mapOutputSocket(outputSocket, operationSet->getOutputSocket(0)); + converter.map_output_socket(output_socket, operation_set->get_output_socket(0)); DotproductOperation *operation = new DotproductOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.addLink(operationSet->getOutputSocket(0), operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketDotproduct, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.add_link(operation_set->get_output_socket(0), operation->get_input_socket(1)); + converter.map_output_socket(output_socket_dotproduct, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_NormalNode.h b/source/blender/compositor/nodes/COM_NormalNode.h index 6d5cbb394a0..2fcbb280c20 100644 --- a/source/blender/compositor/nodes/COM_NormalNode.h +++ b/source/blender/compositor/nodes/COM_NormalNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class NormalNode : public Node { public: - NormalNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + NormalNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_NormalizeNode.cc b/source/blender/compositor/nodes/COM_NormalizeNode.cc index b42f8154ce0..0dbd9e1837c 100644 --- a/source/blender/compositor/nodes/COM_NormalizeNode.cc +++ b/source/blender/compositor/nodes/COM_NormalizeNode.cc @@ -21,19 +21,19 @@ namespace blender::compositor { -NormalizeNode::NormalizeNode(bNode *editorNode) : Node(editorNode) +NormalizeNode::NormalizeNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void NormalizeNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void NormalizeNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { NormalizeOperation *operation = new NormalizeOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_NormalizeNode.h b/source/blender/compositor/nodes/COM_NormalizeNode.h index 7770fc49b61..50d3b939b30 100644 --- a/source/blender/compositor/nodes/COM_NormalizeNode.h +++ b/source/blender/compositor/nodes/COM_NormalizeNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class NormalizeNode : public Node { public: - NormalizeNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + NormalizeNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc index 772456da963..25d201fc9c4 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cc +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc @@ -20,7 +20,7 @@ namespace blender::compositor { -OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode) +OutputFileNode::OutputFileNode(bNode *editor_node) : Node(editor_node) { /* pass */ } @@ -29,34 +29,34 @@ void OutputFileNode::add_input_sockets(OutputOpenExrMultiLayerOperation &operati { for (NodeInput *input : inputs) { NodeImageMultiFileSocket *sockdata = - (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage; + (NodeImageMultiFileSocket *)input->get_bnode_socket()->storage; /* NOTE: layer becomes an empty placeholder if the input is not linked. */ - operation.add_layer(sockdata->layer, input->getDataType(), input->isLinked()); + operation.add_layer(sockdata->layer, input->get_data_type(), input->is_linked()); } } void OutputFileNode::map_input_sockets(NodeConverter &converter, OutputOpenExrMultiLayerOperation &operation) const { - bool previewAdded = false; + bool preview_added = false; int index = 0; for (NodeInput *input : inputs) { - converter.mapInputSocket(input, operation.getInputSocket(index++)); + converter.map_input_socket(input, operation.get_input_socket(index++)); - if (!previewAdded) { - converter.addNodeInputPreview(input); - previewAdded = true; + if (!preview_added) { + converter.add_node_input_preview(input); + preview_added = true; } } } -void OutputFileNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void OutputFileNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeImageMultiFile *storage = (NodeImageMultiFile *)this->getbNode()->storage; - const bool is_multiview = (context.getRenderData()->scemode & R_MULTIVIEW) != 0; + NodeImageMultiFile *storage = (NodeImageMultiFile *)this->get_bnode()->storage; + const bool is_multiview = (context.get_render_data()->scemode & R_MULTIVIEW) != 0; - if (!context.isRendering()) { + if (!context.is_rendering()) { /* only output files when rendering a sequence - * otherwise, it overwrites the output files just * scrubbing through the timeline when the compositor updates. @@ -67,40 +67,40 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, if (storage->format.imtype == R_IMF_IMTYPE_MULTILAYER) { const bool use_half_float = (storage->format.depth == R_IMF_CHAN_DEPTH_16); /* single output operation for the multilayer file */ - OutputOpenExrMultiLayerOperation *outputOperation; + OutputOpenExrMultiLayerOperation *output_operation; if (is_multiview && storage->format.views_format == R_IMF_VIEWS_MULTIVIEW) { - outputOperation = new OutputOpenExrMultiLayerMultiViewOperation(context.getScene(), - context.getRenderData(), - context.getbNodeTree(), - storage->base_path, - storage->format.exr_codec, - use_half_float, - context.getViewName()); + output_operation = new OutputOpenExrMultiLayerMultiViewOperation(context.get_scene(), + context.get_render_data(), + context.get_bnodetree(), + storage->base_path, + storage->format.exr_codec, + use_half_float, + context.get_view_name()); } else { - outputOperation = new OutputOpenExrMultiLayerOperation(context.getScene(), - context.getRenderData(), - context.getbNodeTree(), - storage->base_path, - storage->format.exr_codec, - use_half_float, - context.getViewName()); + output_operation = new OutputOpenExrMultiLayerOperation(context.get_scene(), + context.get_render_data(), + context.get_bnodetree(), + storage->base_path, + storage->format.exr_codec, + use_half_float, + context.get_view_name()); } - converter.addOperation(outputOperation); + converter.add_operation(output_operation); /* First add all inputs. Inputs are stored in a Vector and can be moved to a different * memory address during this time. */ - add_input_sockets(*outputOperation); + add_input_sockets(*output_operation); /* After adding the sockets the memory addresses will stick. */ - map_input_sockets(converter, *outputOperation); + map_input_sockets(converter, *output_operation); } else { /* single layer format */ - bool previewAdded = false; + bool preview_added = false; for (NodeInput *input : inputs) { - if (input->isLinked()) { + if (input->is_linked()) { NodeImageMultiFileSocket *sockdata = - (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage; + (NodeImageMultiFileSocket *)input->get_bnode_socket()->storage; ImageFormatData *format = (sockdata->use_node_format ? &storage->format : &sockdata->format); char path[FILE_MAX]; @@ -108,50 +108,50 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, /* combine file path for the input */ BLI_join_dirfile(path, FILE_MAX, storage->base_path, sockdata->path); - NodeOperation *outputOperation = nullptr; + NodeOperation *output_operation = nullptr; if (is_multiview && format->views_format == R_IMF_VIEWS_MULTIVIEW) { - outputOperation = new OutputOpenExrSingleLayerMultiViewOperation( - context.getRenderData(), - context.getbNodeTree(), - input->getDataType(), + output_operation = new OutputOpenExrSingleLayerMultiViewOperation( + context.get_render_data(), + context.get_bnodetree(), + input->get_data_type(), format, path, - context.getViewSettings(), - context.getDisplaySettings(), - context.getViewName(), + context.get_view_settings(), + context.get_display_settings(), + context.get_view_name(), sockdata->save_as_render); } else if ((!is_multiview) || (format->views_format == R_IMF_VIEWS_INDIVIDUAL)) { - outputOperation = new OutputSingleLayerOperation(context.getRenderData(), - context.getbNodeTree(), - input->getDataType(), - format, - path, - context.getViewSettings(), - context.getDisplaySettings(), - context.getViewName(), - sockdata->save_as_render); + output_operation = new OutputSingleLayerOperation(context.get_render_data(), + context.get_bnodetree(), + input->get_data_type(), + format, + path, + context.get_view_settings(), + context.get_display_settings(), + context.get_view_name(), + sockdata->save_as_render); } else { /* R_IMF_VIEWS_STEREO_3D */ - outputOperation = new OutputStereoOperation(context.getRenderData(), - context.getbNodeTree(), - input->getDataType(), - format, - path, - sockdata->layer, - context.getViewSettings(), - context.getDisplaySettings(), - context.getViewName(), - sockdata->save_as_render); + output_operation = new OutputStereoOperation(context.get_render_data(), + context.get_bnodetree(), + input->get_data_type(), + format, + path, + sockdata->layer, + context.get_view_settings(), + context.get_display_settings(), + context.get_view_name(), + sockdata->save_as_render); } - converter.addOperation(outputOperation); - converter.mapInputSocket(input, outputOperation->getInputSocket(0)); + converter.add_operation(output_operation); + converter.map_input_socket(input, output_operation->get_input_socket(0)); - if (!previewAdded) { - converter.addNodeInputPreview(input); - previewAdded = true; + if (!preview_added) { + converter.add_node_input_preview(input); + preview_added = true; } } } diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.h b/source/blender/compositor/nodes/COM_OutputFileNode.h index c64128a708f..4c5575cc6b9 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.h +++ b/source/blender/compositor/nodes/COM_OutputFileNode.h @@ -32,9 +32,9 @@ namespace blender::compositor { */ class OutputFileNode : public Node { public: - OutputFileNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + OutputFileNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; private: void add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const; diff --git a/source/blender/compositor/nodes/COM_PixelateNode.cc b/source/blender/compositor/nodes/COM_PixelateNode.cc index ae7169b2d98..251ef7b192a 100644 --- a/source/blender/compositor/nodes/COM_PixelateNode.cc +++ b/source/blender/compositor/nodes/COM_PixelateNode.cc @@ -22,28 +22,28 @@ namespace blender::compositor { -PixelateNode::PixelateNode(bNode *editorNode) : Node(editorNode) +PixelateNode::PixelateNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void PixelateNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void PixelateNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); - DataType datatype = inputSocket->getDataType(); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); + DataType datatype = input_socket->get_data_type(); - if (inputSocket->isLinked()) { - NodeOutput *link = inputSocket->getLink(); - datatype = link->getDataType(); + if (input_socket->is_linked()) { + NodeOutput *link = input_socket->get_link(); + datatype = link->get_data_type(); } PixelateOperation *operation = new PixelateOperation(datatype); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PixelateNode.h b/source/blender/compositor/nodes/COM_PixelateNode.h index 1a6555550cf..c52a73cafa6 100644 --- a/source/blender/compositor/nodes/COM_PixelateNode.h +++ b/source/blender/compositor/nodes/COM_PixelateNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class PixelateNode : public Node { public: - PixelateNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + PixelateNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc index 716392f8bcf..0d2623f6e8b 100644 --- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc +++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc @@ -22,50 +22,50 @@ namespace blender::compositor { -PlaneTrackDeformNode::PlaneTrackDeformNode(bNode *editorNode) : Node(editorNode) +PlaneTrackDeformNode::PlaneTrackDeformNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void PlaneTrackDeformNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - MovieClip *clip = (MovieClip *)editorNode->id; - NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)editorNode->storage; + bNode *editor_node = this->get_bnode(); + MovieClip *clip = (MovieClip *)editor_node->id; + NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)editor_node->storage; - int frame_number = context.getFramenumber(); + int frame_number = context.get_framenumber(); - NodeInput *input_image = this->getInputSocket(0); - NodeOutput *output_warped_image = this->getOutputSocket(0); - NodeOutput *output_plane = this->getOutputSocket(1); + 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); PlaneTrackWarpImageOperation *warp_image_operation = new PlaneTrackWarpImageOperation(); - warp_image_operation->setMovieClip(clip); - warp_image_operation->setTrackingObject(data->tracking_object); - warp_image_operation->setPlaneTrackName(data->plane_track_name); - warp_image_operation->setFramenumber(frame_number); + warp_image_operation->set_movie_clip(clip); + warp_image_operation->set_tracking_object(data->tracking_object); + warp_image_operation->set_plane_track_name(data->plane_track_name); + warp_image_operation->set_framenumber(frame_number); if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { - warp_image_operation->setMotionBlurSamples(data->motion_blur_samples); - warp_image_operation->setMotionBlurShutter(data->motion_blur_shutter); + warp_image_operation->set_motion_blur_samples(data->motion_blur_samples); + warp_image_operation->set_motion_blur_shutter(data->motion_blur_shutter); } - converter.addOperation(warp_image_operation); + converter.add_operation(warp_image_operation); - converter.mapInputSocket(input_image, warp_image_operation->getInputSocket(0)); - converter.mapOutputSocket(output_warped_image, warp_image_operation->getOutputSocket()); + 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()); PlaneTrackMaskOperation *plane_mask_operation = new PlaneTrackMaskOperation(); - plane_mask_operation->setMovieClip(clip); - plane_mask_operation->setTrackingObject(data->tracking_object); - plane_mask_operation->setPlaneTrackName(data->plane_track_name); - plane_mask_operation->setFramenumber(frame_number); + 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_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { - plane_mask_operation->setMotionBlurSamples(data->motion_blur_samples); - plane_mask_operation->setMotionBlurShutter(data->motion_blur_shutter); + plane_mask_operation->set_motion_blur_samples(data->motion_blur_samples); + plane_mask_operation->set_motion_blur_shutter(data->motion_blur_shutter); } - converter.addOperation(plane_mask_operation); + converter.add_operation(plane_mask_operation); - converter.mapOutputSocket(output_plane, plane_mask_operation->getOutputSocket()); + converter.map_output_socket(output_plane, plane_mask_operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h index 307738fcaf0..16367423454 100644 --- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h +++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h @@ -31,9 +31,9 @@ namespace blender::compositor { */ class PlaneTrackDeformNode : public Node { public: - PlaneTrackDeformNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + PlaneTrackDeformNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PosterizeNode.cc b/source/blender/compositor/nodes/COM_PosterizeNode.cc index a69e4f17250..c055a954c01 100644 --- a/source/blender/compositor/nodes/COM_PosterizeNode.cc +++ b/source/blender/compositor/nodes/COM_PosterizeNode.cc @@ -21,20 +21,20 @@ namespace blender::compositor { -PosterizeNode::PosterizeNode(bNode *editorNode) : Node(editorNode) +PosterizeNode::PosterizeNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void PosterizeNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void PosterizeNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { PosterizeOperation *operation = new PosterizeOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PosterizeNode.h b/source/blender/compositor/nodes/COM_PosterizeNode.h index bb9bef2bdd0..d19e598632a 100644 --- a/source/blender/compositor/nodes/COM_PosterizeNode.h +++ b/source/blender/compositor/nodes/COM_PosterizeNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class PosterizeNode : public Node { public: - PosterizeNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + PosterizeNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cc b/source/blender/compositor/nodes/COM_RenderLayersNode.cc index 87dccec7bfc..92033c3fbe8 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.cc +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cc @@ -23,66 +23,66 @@ namespace blender::compositor { -RenderLayersNode::RenderLayersNode(bNode *editorNode) : Node(editorNode) +RenderLayersNode::RenderLayersNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void RenderLayersNode::testSocketLink(NodeConverter &converter, - const CompositorContext &context, - NodeOutput *output, - RenderLayersProg *operation, - Scene *scene, - int layerId, - bool is_preview) const +void RenderLayersNode::test_socket_link(NodeConverter &converter, + const CompositorContext &context, + NodeOutput *output, + RenderLayersProg *operation, + Scene *scene, + int layer_id, + bool is_preview) const { - operation->setScene(scene); - operation->setLayerId(layerId); - operation->setRenderData(context.getRenderData()); - operation->setViewName(context.getViewName()); + operation->set_scene(scene); + operation->set_layer_id(layer_id); + operation->set_render_data(context.get_render_data()); + operation->set_view_name(context.get_view_name()); - converter.mapOutputSocket(output, operation->getOutputSocket()); - converter.addOperation(operation); + converter.map_output_socket(output, operation->get_output_socket()); + converter.add_operation(operation); if (is_preview) { /* only for image socket */ - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); } } -void RenderLayersNode::testRenderLink(NodeConverter &converter, - const CompositorContext &context, - Render *re) const +void RenderLayersNode::test_render_link(NodeConverter &converter, + const CompositorContext &context, + Render *re) const { - Scene *scene = (Scene *)this->getbNode()->id; - const short layerId = this->getbNode()->custom1; + Scene *scene = (Scene *)this->get_bnode()->id; + const short layer_id = this->get_bnode()->custom1; RenderResult *rr = RE_AcquireResultRead(re); if (rr == nullptr) { - missingRenderLink(converter); + missing_render_link(converter); return; } - ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, layerId); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, layer_id); if (view_layer == nullptr) { - missingRenderLink(converter); + missing_render_link(converter); return; } RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name); if (rl == nullptr) { - missingRenderLink(converter); + missing_render_link(converter); return; } - for (NodeOutput *output : getOutputSockets()) { - NodeImageLayer *storage = (NodeImageLayer *)output->getbNodeSocket()->storage; + for (NodeOutput *output : get_output_sockets()) { + NodeImageLayer *storage = (NodeImageLayer *)output->get_bnode_socket()->storage; RenderPass *rpass = (RenderPass *)BLI_findstring( &rl->passes, storage->pass_name, offsetof(RenderPass, name)); if (rpass == nullptr) { - missingSocketLink(converter, output); + missing_socket_link(converter, output); continue; } RenderLayersProg *operation; bool is_preview; if (STREQ(rpass->name, RE_PASSNAME_COMBINED) && - STREQ(output->getbNodeSocket()->name, "Alpha")) { + STREQ(output->get_bnode_socket()->name, "Alpha")) { operation = new RenderLayersAlphaProg(rpass->name, DataType::Value, rpass->channels); is_preview = false; } @@ -108,33 +108,33 @@ void RenderLayersNode::testRenderLink(NodeConverter &converter, break; } operation = new RenderLayersProg(rpass->name, type, rpass->channels); - is_preview = STREQ(output->getbNodeSocket()->name, "Image"); + is_preview = STREQ(output->get_bnode_socket()->name, "Image"); } - testSocketLink(converter, context, output, operation, scene, layerId, is_preview); + test_socket_link(converter, context, output, operation, scene, layer_id, is_preview); } } -void RenderLayersNode::missingSocketLink(NodeConverter &converter, NodeOutput *output) const +void RenderLayersNode::missing_socket_link(NodeConverter &converter, NodeOutput *output) const { NodeOperation *operation; - switch (output->getDataType()) { + switch (output->get_data_type()) { case DataType::Color: { const float color[4] = {0.0f, 0.0f, 0.0f, 0.0f}; SetColorOperation *color_operation = new SetColorOperation(); - color_operation->setChannels(color); + color_operation->set_channels(color); operation = color_operation; break; } case DataType::Vector: { const float vector[3] = {0.0f, 0.0f, 0.0f}; SetVectorOperation *vector_operation = new SetVectorOperation(); - vector_operation->setVector(vector); + vector_operation->set_vector(vector); operation = vector_operation; break; } case DataType::Value: { SetValueOperation *value_operation = new SetValueOperation(); - value_operation->setValue(0.0f); + value_operation->set_value(0.0f); operation = value_operation; break; } @@ -144,29 +144,29 @@ void RenderLayersNode::missingSocketLink(NodeConverter &converter, NodeOutput *o } } - converter.mapOutputSocket(output, operation->getOutputSocket()); - converter.addOperation(operation); + converter.map_output_socket(output, operation->get_output_socket()); + converter.add_operation(operation); } -void RenderLayersNode::missingRenderLink(NodeConverter &converter) const +void RenderLayersNode::missing_render_link(NodeConverter &converter) const { for (NodeOutput *output : outputs) { - missingSocketLink(converter, output); + missing_socket_link(converter, output); } } -void RenderLayersNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void RenderLayersNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - Scene *scene = (Scene *)this->getbNode()->id; + Scene *scene = (Scene *)this->get_bnode()->id; Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr; if (re != nullptr) { - testRenderLink(converter, context, re); + test_render_link(converter, context, re); RE_ReleaseResult(re); } else { - missingRenderLink(converter); + missing_render_link(converter); } } diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.h b/source/blender/compositor/nodes/COM_RenderLayersNode.h index 4eb2427c8e0..33e61c58336 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.h +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.h @@ -31,24 +31,24 @@ namespace blender::compositor { */ class RenderLayersNode : public Node { public: - RenderLayersNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + RenderLayersNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; private: - void testSocketLink(NodeConverter &converter, - const CompositorContext &context, - NodeOutput *output, - RenderLayersProg *operation, - Scene *scene, - int layerId, - bool is_preview) const; - void testRenderLink(NodeConverter &converter, - const CompositorContext &context, - Render *re) const; + void test_socket_link(NodeConverter &converter, + const CompositorContext &context, + NodeOutput *output, + RenderLayersProg *operation, + Scene *scene, + int layer_id, + bool is_preview) const; + void test_render_link(NodeConverter &converter, + const CompositorContext &context, + Render *re) const; - void missingSocketLink(NodeConverter &converter, NodeOutput *output) const; - void missingRenderLink(NodeConverter &converter) const; + void missing_socket_link(NodeConverter &converter, NodeOutput *output) const; + void missing_render_link(NodeConverter &converter) const; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_RotateNode.cc b/source/blender/compositor/nodes/COM_RotateNode.cc index 76c11a79d2f..64f49893335 100644 --- a/source/blender/compositor/nodes/COM_RotateNode.cc +++ b/source/blender/compositor/nodes/COM_RotateNode.cc @@ -23,39 +23,39 @@ namespace blender::compositor { -RotateNode::RotateNode(bNode *editorNode) : Node(editorNode) +RotateNode::RotateNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void RotateNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void RotateNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputDegreeSocket = this->getInputSocket(1); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_degree_socket = this->get_input_socket(1); + NodeOutput *output_socket = this->get_output_socket(0); RotateOperation *operation = new RotateOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - PixelSampler sampler = (PixelSampler)this->getbNode()->custom1; + PixelSampler sampler = (PixelSampler)this->get_bnode()->custom1; switch (context.get_execution_model()) { case eExecutionModel::Tiled: { SetSamplerOperation *sampler_op = new SetSamplerOperation(); - sampler_op->setSampler(sampler); - converter.addOperation(sampler_op); - converter.addLink(sampler_op->getOutputSocket(), operation->getInputSocket(0)); - converter.mapInputSocket(inputSocket, sampler_op->getInputSocket(0)); + sampler_op->set_sampler(sampler); + converter.add_operation(sampler_op); + converter.add_link(sampler_op->get_output_socket(), operation->get_input_socket(0)); + converter.map_input_socket(input_socket, sampler_op->get_input_socket(0)); break; } case eExecutionModel::FullFrame: { operation->set_sampler(sampler); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); break; } } - converter.mapInputSocket(inputDegreeSocket, operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_degree_socket, operation->get_input_socket(1)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_RotateNode.h b/source/blender/compositor/nodes/COM_RotateNode.h index 5d8bcb2e3e4..fa50cd1f689 100644 --- a/source/blender/compositor/nodes/COM_RotateNode.h +++ b/source/blender/compositor/nodes/COM_RotateNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class RotateNode : public Node { public: - RotateNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + RotateNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc index 1e33a8688a9..56f1d998db8 100644 --- a/source/blender/compositor/nodes/COM_ScaleNode.cc +++ b/source/blender/compositor/nodes/COM_ScaleNode.cc @@ -24,70 +24,72 @@ namespace blender::compositor { -ScaleNode::ScaleNode(bNode *editorNode) : Node(editorNode) +ScaleNode::ScaleNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ScaleNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void ScaleNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputXSocket = this->getInputSocket(1); - NodeInput *inputYSocket = this->getInputSocket(2); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_xsocket = this->get_input_socket(1); + NodeInput *input_ysocket = this->get_input_socket(2); + NodeOutput *output_socket = this->get_output_socket(0); switch (bnode->custom1) { case CMP_SCALE_RELATIVE: { ScaleRelativeOperation *operation = new ScaleRelativeOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputXSocket, operation->getInputSocket(1)); - converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_xsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_ysocket, operation->get_input_socket(2)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); - operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_variable_size(input_xsocket->is_linked() || input_ysocket->is_linked()); operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f); break; } case CMP_SCALE_SCENEPERCENT: { - SetValueOperation *scaleFactorOperation = new SetValueOperation(); - scaleFactorOperation->setValue(context.getRenderPercentageAsFactor()); - converter.addOperation(scaleFactorOperation); + SetValueOperation *scale_factor_operation = new SetValueOperation(); + scale_factor_operation->set_value(context.get_render_percentage_as_factor()); + converter.add_operation(scale_factor_operation); ScaleRelativeOperation *operation = new ScaleRelativeOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.addLink(scaleFactorOperation->getOutputSocket(), operation->getInputSocket(1)); - converter.addLink(scaleFactorOperation->getOutputSocket(), operation->getInputSocket(2)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.add_link(scale_factor_operation->get_output_socket(), + operation->get_input_socket(1)); + converter.add_link(scale_factor_operation->get_output_socket(), + operation->get_input_socket(2)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); - operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_variable_size(input_xsocket->is_linked() || input_ysocket->is_linked()); operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f); break; } case CMP_SCALE_RENDERPERCENT: { - const RenderData *rd = context.getRenderData(); - const float render_size_factor = context.getRenderPercentageAsFactor(); + const RenderData *rd = context.get_render_data(); + const float render_size_factor = context.get_render_percentage_as_factor(); ScaleFixedSizeOperation *operation = new ScaleFixedSizeOperation(); /* framing options */ - operation->setIsAspect((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_ASPECT) != 0); - operation->setIsCrop((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_CROP) != 0); - operation->setOffset(bnode->custom3, bnode->custom4); - operation->setNewWidth(rd->xsch * render_size_factor); - operation->setNewHeight(rd->ysch * render_size_factor); - converter.addOperation(operation); + operation->set_is_aspect((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_ASPECT) != 0); + operation->set_is_crop((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_CROP) != 0); + operation->set_offset(bnode->custom3, bnode->custom4); + operation->set_new_width(rd->xsch * render_size_factor); + operation->set_new_height(rd->ysch * render_size_factor); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); - operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_variable_size(input_xsocket->is_linked() || input_ysocket->is_linked()); operation->set_scale_canvas_max_size(context.get_render_size() * 3.0f); break; @@ -95,14 +97,14 @@ void ScaleNode::convertToOperations(NodeConverter &converter, case CMP_SCALE_ABSOLUTE: { /* TODO: what is the use of this one.... perhaps some issues when the ui was updated... */ ScaleAbsoluteOperation *operation = new ScaleAbsoluteOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputXSocket, operation->getInputSocket(1)); - converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_xsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_ysocket, operation->get_input_socket(2)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); - operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_variable_size(input_xsocket->is_linked() || input_ysocket->is_linked()); operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f); break; diff --git a/source/blender/compositor/nodes/COM_ScaleNode.h b/source/blender/compositor/nodes/COM_ScaleNode.h index 186ffa8bdce..7800a7acb8b 100644 --- a/source/blender/compositor/nodes/COM_ScaleNode.h +++ b/source/blender/compositor/nodes/COM_ScaleNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ScaleNode : public Node { public: - ScaleNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ScaleNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.cc b/source/blender/compositor/nodes/COM_SeparateColorNode.cc index fcaf52c701d..046a813895e 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.cc +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.cc @@ -22,102 +22,102 @@ namespace blender::compositor { -SeparateColorNode::SeparateColorNode(bNode *editorNode) : Node(editorNode) +SeparateColorNode::SeparateColorNode(bNode *editor_node) : Node(editor_node) { } -void SeparateColorNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void SeparateColorNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *imageSocket = this->getInputSocket(0); - NodeOutput *outputRSocket = this->getOutputSocket(0); - NodeOutput *outputGSocket = this->getOutputSocket(1); - NodeOutput *outputBSocket = this->getOutputSocket(2); - NodeOutput *outputASocket = this->getOutputSocket(3); + NodeInput *image_socket = this->get_input_socket(0); + NodeOutput *output_rsocket = this->get_output_socket(0); + NodeOutput *output_gsocket = this->get_output_socket(1); + NodeOutput *output_bsocket = this->get_output_socket(2); + NodeOutput *output_asocket = this->get_output_socket(3); - NodeOperation *color_conv = getColorConverter(context); + NodeOperation *color_conv = get_color_converter(context); if (color_conv) { - converter.addOperation(color_conv); + converter.add_operation(color_conv); - converter.mapInputSocket(imageSocket, color_conv->getInputSocket(0)); + converter.map_input_socket(image_socket, color_conv->get_input_socket(0)); } { SeparateChannelOperation *operation = new SeparateChannelOperation(); - operation->setChannel(0); - converter.addOperation(operation); + operation->set_channel(0); + converter.add_operation(operation); if (color_conv) { - converter.addLink(color_conv->getOutputSocket(), operation->getInputSocket(0)); + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); } else { - converter.mapInputSocket(imageSocket, operation->getInputSocket(0)); + converter.map_input_socket(image_socket, operation->get_input_socket(0)); } - converter.mapOutputSocket(outputRSocket, operation->getOutputSocket(0)); + converter.map_output_socket(output_rsocket, operation->get_output_socket(0)); } { SeparateChannelOperation *operation = new SeparateChannelOperation(); - operation->setChannel(1); - converter.addOperation(operation); + operation->set_channel(1); + converter.add_operation(operation); if (color_conv) { - converter.addLink(color_conv->getOutputSocket(), operation->getInputSocket(0)); + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); } else { - converter.mapInputSocket(imageSocket, operation->getInputSocket(0)); + converter.map_input_socket(image_socket, operation->get_input_socket(0)); } - converter.mapOutputSocket(outputGSocket, operation->getOutputSocket(0)); + converter.map_output_socket(output_gsocket, operation->get_output_socket(0)); } { SeparateChannelOperation *operation = new SeparateChannelOperation(); - operation->setChannel(2); - converter.addOperation(operation); + operation->set_channel(2); + converter.add_operation(operation); if (color_conv) { - converter.addLink(color_conv->getOutputSocket(), operation->getInputSocket(0)); + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); } else { - converter.mapInputSocket(imageSocket, operation->getInputSocket(0)); + converter.map_input_socket(image_socket, operation->get_input_socket(0)); } - converter.mapOutputSocket(outputBSocket, operation->getOutputSocket(0)); + converter.map_output_socket(output_bsocket, operation->get_output_socket(0)); } { SeparateChannelOperation *operation = new SeparateChannelOperation(); - operation->setChannel(3); - converter.addOperation(operation); + operation->set_channel(3); + converter.add_operation(operation); if (color_conv) { - converter.addLink(color_conv->getOutputSocket(), operation->getInputSocket(0)); + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); } else { - converter.mapInputSocket(imageSocket, operation->getInputSocket(0)); + converter.map_input_socket(image_socket, operation->get_input_socket(0)); } - converter.mapOutputSocket(outputASocket, operation->getOutputSocket(0)); + converter.map_output_socket(output_asocket, operation->get_output_socket(0)); } } -NodeOperation *SeparateRGBANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const { return nullptr; /* no conversion needed */ } -NodeOperation *SeparateHSVANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const { return new ConvertRGBToHSVOperation(); } -NodeOperation *SeparateYCCANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const { ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); - bNode *editorNode = this->getbNode(); - operation->setMode(editorNode->custom1); + bNode *editor_node = this->get_bnode(); + operation->set_mode(editor_node->custom1); return operation; } -NodeOperation *SeparateYUVANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const { return new ConvertRGBToYUVOperation(); } diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.h b/source/blender/compositor/nodes/COM_SeparateColorNode.h index eaf543df51f..0dfcd676772 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.h +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.h @@ -24,48 +24,48 @@ namespace blender::compositor { class SeparateColorNode : public Node { public: - SeparateColorNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SeparateColorNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; protected: - virtual NodeOperation *getColorConverter(const CompositorContext &context) const = 0; + virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; }; class SeparateRGBANode : public SeparateColorNode { public: - SeparateRGBANode(bNode *editorNode) : SeparateColorNode(editorNode) + SeparateRGBANode(bNode *editor_node) : SeparateColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class SeparateHSVANode : public SeparateColorNode { public: - SeparateHSVANode(bNode *editorNode) : SeparateColorNode(editorNode) + SeparateHSVANode(bNode *editor_node) : SeparateColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class SeparateYCCANode : public SeparateColorNode { public: - SeparateYCCANode(bNode *editorNode) : SeparateColorNode(editorNode) + SeparateYCCANode(bNode *editor_node) : SeparateColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class SeparateYUVANode : public SeparateColorNode { public: - SeparateYUVANode(bNode *editorNode) : SeparateColorNode(editorNode) + SeparateYUVANode(bNode *editor_node) : SeparateColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.cc b/source/blender/compositor/nodes/COM_SetAlphaNode.cc index a3a8472ccc9..58b8df98bc8 100644 --- a/source/blender/compositor/nodes/COM_SetAlphaNode.cc +++ b/source/blender/compositor/nodes/COM_SetAlphaNode.cc @@ -22,11 +22,11 @@ namespace blender::compositor { -void SetAlphaNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void SetAlphaNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - const bNode *editorNode = this->getbNode(); - const NodeSetAlpha *storage = static_cast(editorNode->storage); + const bNode *editor_node = this->get_bnode(); + const NodeSetAlpha *storage = static_cast(editor_node->storage); NodeOperation *operation = nullptr; switch (storage->mode) { case CMP_NODE_SETALPHA_MODE_APPLY: @@ -37,15 +37,15 @@ void SetAlphaNode::convertToOperations(NodeConverter &converter, break; } - if (!this->getInputSocket(0)->isLinked() && this->getInputSocket(1)->isLinked()) { + if (!this->get_input_socket(0)->is_linked() && this->get_input_socket(1)->is_linked()) { operation->set_canvas_input_index(1); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.h b/source/blender/compositor/nodes/COM_SetAlphaNode.h index c8d340eb64b..9e369838bbe 100644 --- a/source/blender/compositor/nodes/COM_SetAlphaNode.h +++ b/source/blender/compositor/nodes/COM_SetAlphaNode.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class SetAlphaNode : public Node { public: - SetAlphaNode(bNode *editorNode) : Node(editorNode) + SetAlphaNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.cc b/source/blender/compositor/nodes/COM_SocketProxyNode.cc index 1109dbaf735..e7c8a90a48b 100644 --- a/source/blender/compositor/nodes/COM_SocketProxyNode.cc +++ b/source/blender/compositor/nodes/COM_SocketProxyNode.cc @@ -22,81 +22,82 @@ namespace blender::compositor { -SocketProxyNode::SocketProxyNode(bNode *editorNode, - bNodeSocket *editorInput, - bNodeSocket *editorOutput, +SocketProxyNode::SocketProxyNode(bNode *editor_node, + bNodeSocket *editor_input, + bNodeSocket *editor_output, bool use_conversion) - : Node(editorNode, false), use_conversion_(use_conversion) + : Node(editor_node, false), use_conversion_(use_conversion) { DataType dt; dt = DataType::Value; - if (editorInput->type == SOCK_RGBA) { + if (editor_input->type == SOCK_RGBA) { dt = DataType::Color; } - if (editorInput->type == SOCK_VECTOR) { + if (editor_input->type == SOCK_VECTOR) { dt = DataType::Vector; } - this->addInputSocket(dt, editorInput); + this->add_input_socket(dt, editor_input); dt = DataType::Value; - if (editorOutput->type == SOCK_RGBA) { + if (editor_output->type == SOCK_RGBA) { dt = DataType::Color; } - if (editorOutput->type == SOCK_VECTOR) { + if (editor_output->type == SOCK_VECTOR) { dt = DataType::Vector; } - this->addOutputSocket(dt, editorOutput); + this->add_output_socket(dt, editor_output); } -void SocketProxyNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void SocketProxyNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeOperationOutput *proxy_output = converter.addInputProxy(getInputSocket(0), use_conversion_); - converter.mapOutputSocket(getOutputSocket(), proxy_output); + NodeOperationOutput *proxy_output = converter.add_input_proxy(get_input_socket(0), + use_conversion_); + converter.map_output_socket(get_output_socket(), proxy_output); } -SocketBufferNode::SocketBufferNode(bNode *editorNode, - bNodeSocket *editorInput, - bNodeSocket *editorOutput) - : Node(editorNode, false) +SocketBufferNode::SocketBufferNode(bNode *editor_node, + bNodeSocket *editor_input, + bNodeSocket *editor_output) + : Node(editor_node, false) { DataType dt; dt = DataType::Value; - if (editorInput->type == SOCK_RGBA) { + if (editor_input->type == SOCK_RGBA) { dt = DataType::Color; } - if (editorInput->type == SOCK_VECTOR) { + if (editor_input->type == SOCK_VECTOR) { dt = DataType::Vector; } - this->addInputSocket(dt, editorInput); + this->add_input_socket(dt, editor_input); dt = DataType::Value; - if (editorOutput->type == SOCK_RGBA) { + if (editor_output->type == SOCK_RGBA) { dt = DataType::Color; } - if (editorOutput->type == SOCK_VECTOR) { + if (editor_output->type == SOCK_VECTOR) { dt = DataType::Vector; } - this->addOutputSocket(dt, editorOutput); + this->add_output_socket(dt, editor_output); } -void SocketBufferNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void SocketBufferNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeOutput *output = this->getOutputSocket(0); - NodeInput *input = this->getInputSocket(0); + NodeOutput *output = this->get_output_socket(0); + NodeInput *input = this->get_input_socket(0); - DataType datatype = output->getDataType(); - WriteBufferOperation *writeOperation = new WriteBufferOperation(datatype); - ReadBufferOperation *readOperation = new ReadBufferOperation(datatype); - readOperation->setMemoryProxy(writeOperation->getMemoryProxy()); - converter.addOperation(writeOperation); - converter.addOperation(readOperation); + DataType datatype = output->get_data_type(); + WriteBufferOperation *write_operation = new WriteBufferOperation(datatype); + ReadBufferOperation *read_operation = new ReadBufferOperation(datatype); + read_operation->set_memory_proxy(write_operation->get_memory_proxy()); + converter.add_operation(write_operation); + converter.add_operation(read_operation); - converter.mapInputSocket(input, writeOperation->getInputSocket(0)); - converter.mapOutputSocket(output, readOperation->getOutputSocket()); + converter.map_input_socket(input, write_operation->get_input_socket(0)); + converter.map_output_socket(output, read_operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.h b/source/blender/compositor/nodes/COM_SocketProxyNode.h index 2b584b344f0..50079ed4718 100644 --- a/source/blender/compositor/nodes/COM_SocketProxyNode.h +++ b/source/blender/compositor/nodes/COM_SocketProxyNode.h @@ -28,18 +28,18 @@ namespace blender::compositor { */ class SocketProxyNode : public Node { public: - SocketProxyNode(bNode *editorNode, - bNodeSocket *editorInput, - bNodeSocket *editorOutput, + SocketProxyNode(bNode *editor_node, + bNodeSocket *editor_input, + bNodeSocket *editor_output, bool use_conversion); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; - bool getUseConversion() const + bool get_use_conversion() const { return use_conversion_; } - void setUseConversion(bool use_conversion) + void set_use_conversion(bool use_conversion) { use_conversion_ = use_conversion; } @@ -51,9 +51,9 @@ class SocketProxyNode : public Node { class SocketBufferNode : public Node { public: - SocketBufferNode(bNode *editorNode, bNodeSocket *editorInput, bNodeSocket *editorOutput); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SocketBufferNode(bNode *editor_node, bNodeSocket *editor_input, bNodeSocket *editor_output); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cc b/source/blender/compositor/nodes/COM_SplitViewerNode.cc index bdc927c59a5..c3234fad0fb 100644 --- a/source/blender/compositor/nodes/COM_SplitViewerNode.cc +++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cc @@ -23,52 +23,53 @@ namespace blender::compositor { -SplitViewerNode::SplitViewerNode(bNode *editorNode) : Node(editorNode) +SplitViewerNode::SplitViewerNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void SplitViewerNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void SplitViewerNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - bool do_output = (editorNode->flag & NODE_DO_OUTPUT_RECALC || context.isRendering()) && - (editorNode->flag & NODE_DO_OUTPUT); + bNode *editor_node = this->get_bnode(); + bool do_output = (editor_node->flag & NODE_DO_OUTPUT_RECALC || context.is_rendering()) && + (editor_node->flag & NODE_DO_OUTPUT); - NodeInput *image1Socket = this->getInputSocket(0); - NodeInput *image2Socket = this->getInputSocket(1); - Image *image = (Image *)this->getbNode()->id; - ImageUser *imageUser = (ImageUser *)this->getbNode()->storage; + NodeInput *image1Socket = this->get_input_socket(0); + NodeInput *image2Socket = this->get_input_socket(1); + Image *image = (Image *)this->get_bnode()->id; + ImageUser *image_user = (ImageUser *)this->get_bnode()->storage; - SplitOperation *splitViewerOperation = new SplitOperation(); - splitViewerOperation->setSplitPercentage(this->getbNode()->custom1); - splitViewerOperation->setXSplit(!this->getbNode()->custom2); + SplitOperation *split_viewer_operation = new SplitOperation(); + split_viewer_operation->set_split_percentage(this->get_bnode()->custom1); + split_viewer_operation->set_xsplit(!this->get_bnode()->custom2); - converter.addOperation(splitViewerOperation); - converter.mapInputSocket(image1Socket, splitViewerOperation->getInputSocket(0)); - converter.mapInputSocket(image2Socket, splitViewerOperation->getInputSocket(1)); + converter.add_operation(split_viewer_operation); + converter.map_input_socket(image1Socket, split_viewer_operation->get_input_socket(0)); + converter.map_input_socket(image2Socket, split_viewer_operation->get_input_socket(1)); - ViewerOperation *viewerOperation = new ViewerOperation(); - viewerOperation->setImage(image); - viewerOperation->setImageUser(imageUser); - viewerOperation->setViewSettings(context.getViewSettings()); - viewerOperation->setDisplaySettings(context.getDisplaySettings()); - viewerOperation->setRenderData(context.getRenderData()); - viewerOperation->setViewName(context.getViewName()); + ViewerOperation *viewer_operation = new ViewerOperation(); + viewer_operation->set_image(image); + viewer_operation->set_image_user(image_user); + viewer_operation->set_view_settings(context.get_view_settings()); + viewer_operation->set_display_settings(context.get_display_settings()); + viewer_operation->set_render_data(context.get_render_data()); + viewer_operation->set_view_name(context.get_view_name()); /* defaults - the viewer node has these options but not exposed for split view * we could use the split to define an area of interest on one axis at least */ - viewerOperation->setChunkOrder(ChunkOrdering::Default); - viewerOperation->setCenterX(0.5f); - viewerOperation->setCenterY(0.5f); + viewer_operation->set_chunk_order(ChunkOrdering::Default); + viewer_operation->setCenterX(0.5f); + viewer_operation->setCenterY(0.5f); - converter.addOperation(viewerOperation); - converter.addLink(splitViewerOperation->getOutputSocket(), viewerOperation->getInputSocket(0)); + converter.add_operation(viewer_operation); + converter.add_link(split_viewer_operation->get_output_socket(), + viewer_operation->get_input_socket(0)); - converter.addPreview(splitViewerOperation->getOutputSocket()); + converter.add_preview(split_viewer_operation->get_output_socket()); if (do_output) { - converter.registerViewer(viewerOperation); + converter.register_viewer(viewer_operation); } } diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.h b/source/blender/compositor/nodes/COM_SplitViewerNode.h index 8a42775eb0d..579eca9d8e1 100644 --- a/source/blender/compositor/nodes/COM_SplitViewerNode.h +++ b/source/blender/compositor/nodes/COM_SplitViewerNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class SplitViewerNode : public Node { public: - SplitViewerNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SplitViewerNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc index 48eeb337842..9892c4f5c05 100644 --- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc +++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc @@ -25,145 +25,160 @@ namespace blender::compositor { -Stabilize2dNode::Stabilize2dNode(bNode *editorNode) : Node(editorNode) +Stabilize2dNode::Stabilize2dNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void Stabilize2dNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void Stabilize2dNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - NodeInput *imageInput = this->getInputSocket(0); - MovieClip *clip = (MovieClip *)editorNode->id; - bool invert = (editorNode->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0; - const PixelSampler sampler = (PixelSampler)editorNode->custom1; + bNode *editor_node = this->get_bnode(); + NodeInput *image_input = this->get_input_socket(0); + MovieClip *clip = (MovieClip *)editor_node->id; + bool invert = (editor_node->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0; + const PixelSampler sampler = (PixelSampler)editor_node->custom1; - MovieClipAttributeOperation *scaleAttribute = new MovieClipAttributeOperation(); - MovieClipAttributeOperation *angleAttribute = new MovieClipAttributeOperation(); - MovieClipAttributeOperation *xAttribute = new MovieClipAttributeOperation(); - MovieClipAttributeOperation *yAttribute = new MovieClipAttributeOperation(); + MovieClipAttributeOperation *scale_attribute = new MovieClipAttributeOperation(); + MovieClipAttributeOperation *angle_attribute = new MovieClipAttributeOperation(); + MovieClipAttributeOperation *x_attribute = new MovieClipAttributeOperation(); + MovieClipAttributeOperation *y_attribute = new MovieClipAttributeOperation(); - scaleAttribute->setAttribute(MCA_SCALE); - scaleAttribute->setFramenumber(context.getFramenumber()); - scaleAttribute->setMovieClip(clip); - scaleAttribute->setInvert(invert); + scale_attribute->set_attribute(MCA_SCALE); + scale_attribute->set_framenumber(context.get_framenumber()); + scale_attribute->set_movie_clip(clip); + scale_attribute->set_invert(invert); - angleAttribute->setAttribute(MCA_ANGLE); - angleAttribute->setFramenumber(context.getFramenumber()); - angleAttribute->setMovieClip(clip); - angleAttribute->setInvert(invert); + angle_attribute->set_attribute(MCA_ANGLE); + angle_attribute->set_framenumber(context.get_framenumber()); + angle_attribute->set_movie_clip(clip); + angle_attribute->set_invert(invert); - xAttribute->setAttribute(MCA_X); - xAttribute->setFramenumber(context.getFramenumber()); - xAttribute->setMovieClip(clip); - xAttribute->setInvert(invert); + x_attribute->set_attribute(MCA_X); + x_attribute->set_framenumber(context.get_framenumber()); + x_attribute->set_movie_clip(clip); + x_attribute->set_invert(invert); - yAttribute->setAttribute(MCA_Y); - yAttribute->setFramenumber(context.getFramenumber()); - yAttribute->setMovieClip(clip); - yAttribute->setInvert(invert); + y_attribute->set_attribute(MCA_Y); + y_attribute->set_framenumber(context.get_framenumber()); + y_attribute->set_movie_clip(clip); + y_attribute->set_invert(invert); - converter.addOperation(scaleAttribute); - converter.addOperation(angleAttribute); - converter.addOperation(xAttribute); - converter.addOperation(yAttribute); + converter.add_operation(scale_attribute); + converter.add_operation(angle_attribute); + converter.add_operation(x_attribute); + converter.add_operation(y_attribute); switch (context.get_execution_model()) { case eExecutionModel::Tiled: { - ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); - scaleOperation->setSampler(sampler); - RotateOperation *rotateOperation = new RotateOperation(); - rotateOperation->setDoDegree2RadConversion(false); - TranslateOperation *translateOperation = new TranslateOperation(); + ScaleRelativeOperation *scale_operation = new ScaleRelativeOperation(); + scale_operation->set_sampler(sampler); + RotateOperation *rotate_operation = new RotateOperation(); + rotate_operation->set_do_degree2_rad_conversion(false); + TranslateOperation *translate_operation = new TranslateOperation(); SetSamplerOperation *psoperation = new SetSamplerOperation(); - psoperation->setSampler(sampler); + psoperation->set_sampler(sampler); - converter.addOperation(scaleOperation); - converter.addOperation(translateOperation); - converter.addOperation(rotateOperation); - converter.addOperation(psoperation); + converter.add_operation(scale_operation); + converter.add_operation(translate_operation); + converter.add_operation(rotate_operation); + converter.add_operation(psoperation); - converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1)); - converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2)); + converter.add_link(scale_attribute->get_output_socket(), + scale_operation->get_input_socket(1)); + converter.add_link(scale_attribute->get_output_socket(), + scale_operation->get_input_socket(2)); - converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1)); + converter.add_link(angle_attribute->get_output_socket(), + rotate_operation->get_input_socket(1)); - converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1)); - converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2)); + converter.add_link(x_attribute->get_output_socket(), + translate_operation->get_input_socket(1)); + converter.add_link(y_attribute->get_output_socket(), + translate_operation->get_input_socket(2)); - converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket()); + converter.map_output_socket(get_output_socket(), psoperation->get_output_socket()); if (invert) { /* Translate -> Rotate -> Scale. */ - converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0)); + converter.map_input_socket(image_input, translate_operation->get_input_socket(0)); - converter.addLink(translateOperation->getOutputSocket(), - rotateOperation->getInputSocket(0)); - converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0)); + converter.add_link(translate_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.add_link(rotate_operation->get_output_socket(), + scale_operation->get_input_socket(0)); - converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0)); + converter.add_link(scale_operation->get_output_socket(), psoperation->get_input_socket(0)); } else { /* Scale -> Rotate -> Translate. */ - converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0)); + converter.map_input_socket(image_input, scale_operation->get_input_socket(0)); - converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); - converter.addLink(rotateOperation->getOutputSocket(), - translateOperation->getInputSocket(0)); + converter.add_link(scale_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.add_link(rotate_operation->get_output_socket(), + translate_operation->get_input_socket(0)); - converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0)); + converter.add_link(translate_operation->get_output_socket(), + psoperation->get_input_socket(0)); } break; } case eExecutionModel::FullFrame: { - ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); - scaleOperation->setSampler(sampler); - RotateOperation *rotateOperation = new RotateOperation(); - rotateOperation->setDoDegree2RadConversion(false); - rotateOperation->set_sampler(sampler); - TranslateOperation *translateOperation = new TranslateCanvasOperation(); + ScaleRelativeOperation *scale_operation = new ScaleRelativeOperation(); + scale_operation->set_sampler(sampler); + RotateOperation *rotate_operation = new RotateOperation(); + rotate_operation->set_do_degree2_rad_conversion(false); + rotate_operation->set_sampler(sampler); + TranslateOperation *translate_operation = new TranslateCanvasOperation(); - converter.addOperation(scaleOperation); - converter.addOperation(translateOperation); - converter.addOperation(rotateOperation); + converter.add_operation(scale_operation); + converter.add_operation(translate_operation); + converter.add_operation(rotate_operation); - converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1)); - converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2)); + converter.add_link(scale_attribute->get_output_socket(), + scale_operation->get_input_socket(1)); + converter.add_link(scale_attribute->get_output_socket(), + scale_operation->get_input_socket(2)); - converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1)); + converter.add_link(angle_attribute->get_output_socket(), + rotate_operation->get_input_socket(1)); - converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1)); - converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2)); + converter.add_link(x_attribute->get_output_socket(), + translate_operation->get_input_socket(1)); + converter.add_link(y_attribute->get_output_socket(), + translate_operation->get_input_socket(2)); NodeOperationInput *stabilization_socket = nullptr; if (invert) { /* Translate -> Rotate -> Scale. */ - stabilization_socket = translateOperation->getInputSocket(0); - converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0)); + stabilization_socket = translate_operation->get_input_socket(0); + converter.map_input_socket(image_input, translate_operation->get_input_socket(0)); - converter.addLink(translateOperation->getOutputSocket(), - rotateOperation->getInputSocket(0)); - converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0)); + converter.add_link(translate_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.add_link(rotate_operation->get_output_socket(), + scale_operation->get_input_socket(0)); - converter.mapOutputSocket(getOutputSocket(), scaleOperation->getOutputSocket()); + converter.map_output_socket(get_output_socket(), scale_operation->get_output_socket()); } else { /* Scale -> Rotate -> Translate. */ - stabilization_socket = scaleOperation->getInputSocket(0); - converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0)); + stabilization_socket = scale_operation->get_input_socket(0); + converter.map_input_socket(image_input, scale_operation->get_input_socket(0)); - converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); - converter.addLink(rotateOperation->getOutputSocket(), - translateOperation->getInputSocket(0)); + converter.add_link(scale_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.add_link(rotate_operation->get_output_socket(), + translate_operation->get_input_socket(0)); - converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket()); + converter.map_output_socket(get_output_socket(), translate_operation->get_output_socket()); } - xAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket); - yAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket); - scaleAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket); - angleAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + x_attribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + y_attribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + scale_attribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + angle_attribute->set_socket_input_resolution_for_stabilization(stabilization_socket); break; } } diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.h b/source/blender/compositor/nodes/COM_Stabilize2dNode.h index 34ed8871e33..0c6afb272e9 100644 --- a/source/blender/compositor/nodes/COM_Stabilize2dNode.h +++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class Stabilize2dNode : public Node { public: - Stabilize2dNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + Stabilize2dNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SunBeamsNode.cc b/source/blender/compositor/nodes/COM_SunBeamsNode.cc index 1e5aa0b8020..ba42b79f307 100644 --- a/source/blender/compositor/nodes/COM_SunBeamsNode.cc +++ b/source/blender/compositor/nodes/COM_SunBeamsNode.cc @@ -20,24 +20,24 @@ namespace blender::compositor { -SunBeamsNode::SunBeamsNode(bNode *editorNode) : Node(editorNode) +SunBeamsNode::SunBeamsNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void SunBeamsNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void SunBeamsNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); - NodeSunBeams *data = (NodeSunBeams *)getbNode()->storage; + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); + NodeSunBeams *data = (NodeSunBeams *)get_bnode()->storage; SunBeamsOperation *operation = new SunBeamsOperation(); - operation->setData(*data); - converter.addOperation(operation); + operation->set_data(*data); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SunBeamsNode.h b/source/blender/compositor/nodes/COM_SunBeamsNode.h index 8b68d3f4cb5..478b5ce39f8 100644 --- a/source/blender/compositor/nodes/COM_SunBeamsNode.h +++ b/source/blender/compositor/nodes/COM_SunBeamsNode.h @@ -27,9 +27,9 @@ namespace blender::compositor { */ class SunBeamsNode : public Node { public: - SunBeamsNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SunBeamsNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SwitchNode.cc b/source/blender/compositor/nodes/COM_SwitchNode.cc index 4006d10dafb..d0bc0f6dea2 100644 --- a/source/blender/compositor/nodes/COM_SwitchNode.cc +++ b/source/blender/compositor/nodes/COM_SwitchNode.cc @@ -20,25 +20,25 @@ namespace blender::compositor { -SwitchNode::SwitchNode(bNode *editorNode) : Node(editorNode) +SwitchNode::SwitchNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void SwitchNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void SwitchNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bool condition = this->getbNode()->custom1; + bool condition = this->get_bnode()->custom1; NodeOperationOutput *result; if (!condition) { - result = converter.addInputProxy(getInputSocket(0), false); + result = converter.add_input_proxy(get_input_socket(0), false); } else { - result = converter.addInputProxy(getInputSocket(1), false); + result = converter.add_input_proxy(get_input_socket(1), false); } - converter.mapOutputSocket(getOutputSocket(0), result); + converter.map_output_socket(get_output_socket(0), result); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SwitchNode.h b/source/blender/compositor/nodes/COM_SwitchNode.h index aa6caa2e59f..eafdeb4ac74 100644 --- a/source/blender/compositor/nodes/COM_SwitchNode.h +++ b/source/blender/compositor/nodes/COM_SwitchNode.h @@ -30,9 +30,9 @@ namespace blender::compositor { */ class SwitchNode : public Node { public: - SwitchNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SwitchNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.cc b/source/blender/compositor/nodes/COM_SwitchViewNode.cc index 5bf854bb0a0..735ccaf3513 100644 --- a/source/blender/compositor/nodes/COM_SwitchViewNode.cc +++ b/source/blender/compositor/nodes/COM_SwitchViewNode.cc @@ -20,24 +20,24 @@ namespace blender::compositor { -SwitchViewNode::SwitchViewNode(bNode *editorNode) : Node(editorNode) +SwitchViewNode::SwitchViewNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void SwitchViewNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void SwitchViewNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { NodeOperationOutput *result; - const char *viewName = context.getViewName(); - bNode *bnode = this->getbNode(); + const char *view_name = context.get_view_name(); + bNode *bnode = this->get_bnode(); /* get the internal index of the socket with a matching name */ - int nr = BLI_findstringindex(&bnode->inputs, viewName, offsetof(bNodeSocket, name)); + int nr = BLI_findstringindex(&bnode->inputs, view_name, offsetof(bNodeSocket, name)); nr = MAX2(nr, 0); - result = converter.addInputProxy(getInputSocket(nr), false); - converter.mapOutputSocket(getOutputSocket(0), result); + result = converter.add_input_proxy(get_input_socket(nr), false); + converter.map_output_socket(get_output_socket(0), result); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.h b/source/blender/compositor/nodes/COM_SwitchViewNode.h index ce6de52182c..227c076f181 100644 --- a/source/blender/compositor/nodes/COM_SwitchViewNode.h +++ b/source/blender/compositor/nodes/COM_SwitchViewNode.h @@ -30,9 +30,9 @@ namespace blender::compositor { */ class SwitchViewNode : public Node { public: - SwitchViewNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SwitchViewNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TextureNode.cc b/source/blender/compositor/nodes/COM_TextureNode.cc index 4063e4b4bce..ca57680781f 100644 --- a/source/blender/compositor/nodes/COM_TextureNode.cc +++ b/source/blender/compositor/nodes/COM_TextureNode.cc @@ -21,39 +21,39 @@ namespace blender::compositor { -TextureNode::TextureNode(bNode *editorNode) : Node(editorNode) +TextureNode::TextureNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void TextureNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void TextureNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - Tex *texture = (Tex *)editorNode->id; + bNode *editor_node = this->get_bnode(); + Tex *texture = (Tex *)editor_node->id; TextureOperation *operation = new TextureOperation(); - const ColorManagedDisplaySettings *displaySettings = context.getDisplaySettings(); - bool sceneColorManage = !STREQ(displaySettings->display_device, "None"); - operation->setTexture(texture); - operation->setRenderData(context.getRenderData()); - operation->setSceneColorManage(sceneColorManage); - converter.addOperation(operation); + const ColorManagedDisplaySettings *display_settings = context.get_display_settings(); + bool scene_color_manage = !STREQ(display_settings->display_device, "None"); + operation->set_texture(texture); + operation->set_render_data(context.get_render_data()); + operation->set_scene_color_manage(scene_color_manage); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(1), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(1), operation->get_output_socket()); - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); - TextureAlphaOperation *alphaOperation = new TextureAlphaOperation(); - alphaOperation->setTexture(texture); - alphaOperation->setRenderData(context.getRenderData()); - alphaOperation->setSceneColorManage(sceneColorManage); - converter.addOperation(alphaOperation); + TextureAlphaOperation *alpha_operation = new TextureAlphaOperation(); + alpha_operation->set_texture(texture); + alpha_operation->set_render_data(context.get_render_data()); + alpha_operation->set_scene_color_manage(scene_color_manage); + converter.add_operation(alpha_operation); - converter.mapInputSocket(getInputSocket(0), alphaOperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), alphaOperation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), alphaOperation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), alpha_operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), alpha_operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), alpha_operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TextureNode.h b/source/blender/compositor/nodes/COM_TextureNode.h index b886e3b74e1..bd477e6c6e6 100644 --- a/source/blender/compositor/nodes/COM_TextureNode.h +++ b/source/blender/compositor/nodes/COM_TextureNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class TextureNode : public Node { public: - TextureNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TextureNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TimeNode.cc b/source/blender/compositor/nodes/COM_TimeNode.cc index 37236959366..0d1532f62f9 100644 --- a/source/blender/compositor/nodes/COM_TimeNode.cc +++ b/source/blender/compositor/nodes/COM_TimeNode.cc @@ -24,20 +24,20 @@ namespace blender::compositor { -TimeNode::TimeNode(bNode *editorNode) : Node(editorNode) +TimeNode::TimeNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void TimeNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void TimeNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { SetValueOperation *operation = new SetValueOperation(); - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); /* stack order output: fac */ float fac = 0.0f; - const int framenumber = context.getFramenumber(); + const int framenumber = context.get_framenumber(); if (framenumber < node->custom1) { fac = 0.0f; @@ -46,15 +46,15 @@ void TimeNode::convertToOperations(NodeConverter &converter, fac = 1.0f; } else if (node->custom1 < node->custom2) { - fac = (context.getFramenumber() - node->custom1) / (float)(node->custom2 - node->custom1); + fac = (context.get_framenumber() - node->custom1) / (float)(node->custom2 - node->custom1); } BKE_curvemapping_init((CurveMapping *)node->storage); fac = BKE_curvemapping_evaluateF((CurveMapping *)node->storage, 0, fac); - operation->setValue(clamp_f(fac, 0.0f, 1.0f)); - converter.addOperation(operation); + operation->set_value(clamp_f(fac, 0.0f, 1.0f)); + converter.add_operation(operation); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TimeNode.h b/source/blender/compositor/nodes/COM_TimeNode.h index 5688e2cff03..5c5a0c98058 100644 --- a/source/blender/compositor/nodes/COM_TimeNode.h +++ b/source/blender/compositor/nodes/COM_TimeNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class TimeNode : public Node { public: - TimeNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TimeNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TonemapNode.cc b/source/blender/compositor/nodes/COM_TonemapNode.cc index a2b3e84b604..54ba84d46bd 100644 --- a/source/blender/compositor/nodes/COM_TonemapNode.cc +++ b/source/blender/compositor/nodes/COM_TonemapNode.cc @@ -21,23 +21,23 @@ namespace blender::compositor { -TonemapNode::TonemapNode(bNode *editorNode) : Node(editorNode) +TonemapNode::TonemapNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void TonemapNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void TonemapNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeTonemap *data = (NodeTonemap *)this->getbNode()->storage; + NodeTonemap *data = (NodeTonemap *)this->get_bnode()->storage; TonemapOperation *operation = data->type == 1 ? new PhotoreceptorTonemapOperation() : new TonemapOperation(); - operation->setData(data); - converter.addOperation(operation); + operation->set_data(data); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TonemapNode.h b/source/blender/compositor/nodes/COM_TonemapNode.h index cac9004c32a..3c67472bf98 100644 --- a/source/blender/compositor/nodes/COM_TonemapNode.h +++ b/source/blender/compositor/nodes/COM_TonemapNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class TonemapNode : public Node { public: - TonemapNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TonemapNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.cc b/source/blender/compositor/nodes/COM_TrackPositionNode.cc index f683eeb61c2..ced09d8535f 100644 --- a/source/blender/compositor/nodes/COM_TrackPositionNode.cc +++ b/source/blender/compositor/nodes/COM_TrackPositionNode.cc @@ -27,7 +27,7 @@ namespace blender::compositor { -TrackPositionNode::TrackPositionNode(bNode *editorNode) : Node(editorNode) +TrackPositionNode::TrackPositionNode(bNode *editor_node) : Node(editor_node) { /* pass */ } @@ -40,58 +40,58 @@ static TrackPositionOperation *create_motion_operation(NodeConverter &converter, int delta) { TrackPositionOperation *operation = new TrackPositionOperation(); - operation->setMovieClip(clip); - operation->setTrackingObject(trackpos_data->tracking_object); - operation->setTrackName(trackpos_data->track_name); - operation->setFramenumber(frame_number); - operation->setAxis(axis); - operation->setPosition(CMP_TRACKPOS_ABSOLUTE); - operation->setRelativeFrame(frame_number + delta); - operation->setSpeedOutput(true); - converter.addOperation(operation); + operation->set_movie_clip(clip); + operation->set_tracking_object(trackpos_data->tracking_object); + operation->set_track_name(trackpos_data->track_name); + operation->set_framenumber(frame_number); + operation->set_axis(axis); + operation->set_position(CMP_TRACKPOS_ABSOLUTE); + operation->set_relative_frame(frame_number + delta); + operation->set_speed_output(true); + converter.add_operation(operation); return operation; } -void TrackPositionNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void TrackPositionNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - MovieClip *clip = (MovieClip *)editorNode->id; - NodeTrackPosData *trackpos_data = (NodeTrackPosData *)editorNode->storage; + bNode *editor_node = this->get_bnode(); + MovieClip *clip = (MovieClip *)editor_node->id; + NodeTrackPosData *trackpos_data = (NodeTrackPosData *)editor_node->storage; - NodeOutput *outputX = this->getOutputSocket(0); - NodeOutput *outputY = this->getOutputSocket(1); - NodeOutput *outputSpeed = this->getOutputSocket(2); + NodeOutput *outputX = this->get_output_socket(0); + NodeOutput *outputY = this->get_output_socket(1); + NodeOutput *output_speed = this->get_output_socket(2); int frame_number; - if (editorNode->custom1 == CMP_TRACKPOS_ABSOLUTE_FRAME) { - frame_number = editorNode->custom2; + if (editor_node->custom1 == CMP_TRACKPOS_ABSOLUTE_FRAME) { + frame_number = editor_node->custom2; } else { - frame_number = context.getFramenumber(); + frame_number = context.get_framenumber(); } TrackPositionOperation *operationX = new TrackPositionOperation(); - operationX->setMovieClip(clip); - operationX->setTrackingObject(trackpos_data->tracking_object); - operationX->setTrackName(trackpos_data->track_name); - operationX->setFramenumber(frame_number); - operationX->setAxis(0); - operationX->setPosition(editorNode->custom1); - operationX->setRelativeFrame(editorNode->custom2); - converter.addOperation(operationX); - converter.mapOutputSocket(outputX, operationX->getOutputSocket()); + operationX->set_movie_clip(clip); + operationX->set_tracking_object(trackpos_data->tracking_object); + operationX->set_track_name(trackpos_data->track_name); + operationX->set_framenumber(frame_number); + operationX->set_axis(0); + operationX->set_position(editor_node->custom1); + operationX->set_relative_frame(editor_node->custom2); + converter.add_operation(operationX); + converter.map_output_socket(outputX, operationX->get_output_socket()); TrackPositionOperation *operationY = new TrackPositionOperation(); - operationY->setMovieClip(clip); - operationY->setTrackingObject(trackpos_data->tracking_object); - operationY->setTrackName(trackpos_data->track_name); - operationY->setFramenumber(frame_number); - operationY->setAxis(1); - operationY->setPosition(editorNode->custom1); - operationY->setRelativeFrame(editorNode->custom2); - converter.addOperation(operationY); - converter.mapOutputSocket(outputY, operationY->getOutputSocket()); + operationY->set_movie_clip(clip); + operationY->set_tracking_object(trackpos_data->tracking_object); + operationY->set_track_name(trackpos_data->track_name); + operationY->set_framenumber(frame_number); + operationY->set_axis(1); + operationY->set_position(editor_node->custom1); + operationY->set_relative_frame(editor_node->custom2); + converter.add_operation(operationY); + converter.map_output_socket(outputY, operationY->get_output_socket()); TrackPositionOperation *operationMotionPreX = create_motion_operation( converter, clip, trackpos_data, 0, frame_number, -1); @@ -103,12 +103,16 @@ void TrackPositionNode::convertToOperations(NodeConverter &converter, converter, clip, trackpos_data, 1, frame_number, 1); CombineChannelsOperation *combine_operation = new CombineChannelsOperation(); - converter.addOperation(combine_operation); - converter.addLink(operationMotionPreX->getOutputSocket(), combine_operation->getInputSocket(0)); - converter.addLink(operationMotionPreY->getOutputSocket(), combine_operation->getInputSocket(1)); - converter.addLink(operationMotionPostX->getOutputSocket(), combine_operation->getInputSocket(2)); - converter.addLink(operationMotionPostY->getOutputSocket(), combine_operation->getInputSocket(3)); - converter.mapOutputSocket(outputSpeed, combine_operation->getOutputSocket()); + converter.add_operation(combine_operation); + converter.add_link(operationMotionPreX->get_output_socket(), + combine_operation->get_input_socket(0)); + converter.add_link(operationMotionPreY->get_output_socket(), + combine_operation->get_input_socket(1)); + converter.add_link(operationMotionPostX->get_output_socket(), + combine_operation->get_input_socket(2)); + converter.add_link(operationMotionPostY->get_output_socket(), + combine_operation->get_input_socket(3)); + converter.map_output_socket(output_speed, combine_operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.h b/source/blender/compositor/nodes/COM_TrackPositionNode.h index 665ba36fe09..d0b1648b0c9 100644 --- a/source/blender/compositor/nodes/COM_TrackPositionNode.h +++ b/source/blender/compositor/nodes/COM_TrackPositionNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class TrackPositionNode : public Node { public: - TrackPositionNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TrackPositionNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc index a5b3c41de80..cd60e8c17db 100644 --- a/source/blender/compositor/nodes/COM_TransformNode.cc +++ b/source/blender/compositor/nodes/COM_TransformNode.cc @@ -24,79 +24,85 @@ namespace blender::compositor { -TransformNode::TransformNode(bNode *editorNode) : Node(editorNode) +TransformNode::TransformNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void TransformNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void TransformNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *imageInput = this->getInputSocket(0); - NodeInput *xInput = this->getInputSocket(1); - NodeInput *yInput = this->getInputSocket(2); - NodeInput *angleInput = this->getInputSocket(3); - NodeInput *scaleInput = this->getInputSocket(4); + NodeInput *image_input = this->get_input_socket(0); + NodeInput *x_input = this->get_input_socket(1); + NodeInput *y_input = this->get_input_socket(2); + NodeInput *angle_input = this->get_input_socket(3); + NodeInput *scale_input = this->get_input_socket(4); switch (context.get_execution_model()) { case eExecutionModel::Tiled: { - ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); - converter.addOperation(scaleOperation); + ScaleRelativeOperation *scale_operation = new ScaleRelativeOperation(); + converter.add_operation(scale_operation); - RotateOperation *rotateOperation = new RotateOperation(); - rotateOperation->setDoDegree2RadConversion(false); - converter.addOperation(rotateOperation); + RotateOperation *rotate_operation = new RotateOperation(); + rotate_operation->set_do_degree2_rad_conversion(false); + converter.add_operation(rotate_operation); - TranslateOperation *translateOperation = new TranslateOperation(); - converter.addOperation(translateOperation); + TranslateOperation *translate_operation = new TranslateOperation(); + converter.add_operation(translate_operation); SetSamplerOperation *sampler = new SetSamplerOperation(); - sampler->setSampler((PixelSampler)this->getbNode()->custom1); - converter.addOperation(sampler); + sampler->set_sampler((PixelSampler)this->get_bnode()->custom1); + converter.add_operation(sampler); - converter.mapInputSocket(imageInput, sampler->getInputSocket(0)); - converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0)); - converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1)); - converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale + converter.map_input_socket(image_input, sampler->get_input_socket(0)); + converter.add_link(sampler->get_output_socket(), scale_operation->get_input_socket(0)); + converter.map_input_socket(scale_input, scale_operation->get_input_socket(1)); + converter.map_input_socket(scale_input, + scale_operation->get_input_socket(2)); // xscale = yscale - converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); - converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1)); + converter.add_link(scale_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.map_input_socket(angle_input, rotate_operation->get_input_socket(1)); - converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0)); - converter.mapInputSocket(xInput, translateOperation->getInputSocket(1)); - converter.mapInputSocket(yInput, translateOperation->getInputSocket(2)); + converter.add_link(rotate_operation->get_output_socket(), + translate_operation->get_input_socket(0)); + converter.map_input_socket(x_input, translate_operation->get_input_socket(1)); + converter.map_input_socket(y_input, translate_operation->get_input_socket(2)); - converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket()); + converter.map_output_socket(get_output_socket(), translate_operation->get_output_socket()); break; } case eExecutionModel::FullFrame: { - ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); - converter.addOperation(scaleOperation); + ScaleRelativeOperation *scale_operation = new ScaleRelativeOperation(); + converter.add_operation(scale_operation); - RotateOperation *rotateOperation = new RotateOperation(); - rotateOperation->setDoDegree2RadConversion(false); - converter.addOperation(rotateOperation); + RotateOperation *rotate_operation = new RotateOperation(); + rotate_operation->set_do_degree2_rad_conversion(false); + converter.add_operation(rotate_operation); - TranslateOperation *translateOperation = new TranslateCanvasOperation(); - converter.addOperation(translateOperation); + TranslateOperation *translate_operation = new TranslateCanvasOperation(); + converter.add_operation(translate_operation); - PixelSampler sampler = (PixelSampler)this->getbNode()->custom1; - scaleOperation->setSampler(sampler); - rotateOperation->set_sampler(sampler); - scaleOperation->set_scale_canvas_max_size(context.get_render_size()); + PixelSampler sampler = (PixelSampler)this->get_bnode()->custom1; + scale_operation->set_sampler(sampler); + rotate_operation->set_sampler(sampler); + scale_operation->set_scale_canvas_max_size(context.get_render_size()); - converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0)); - converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1)); - converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale + converter.map_input_socket(image_input, scale_operation->get_input_socket(0)); + converter.map_input_socket(scale_input, scale_operation->get_input_socket(1)); + converter.map_input_socket(scale_input, + scale_operation->get_input_socket(2)); // xscale = yscale - converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); - converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1)); + converter.add_link(scale_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.map_input_socket(angle_input, rotate_operation->get_input_socket(1)); - converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0)); - converter.mapInputSocket(xInput, translateOperation->getInputSocket(1)); - converter.mapInputSocket(yInput, translateOperation->getInputSocket(2)); + converter.add_link(rotate_operation->get_output_socket(), + translate_operation->get_input_socket(0)); + converter.map_input_socket(x_input, translate_operation->get_input_socket(1)); + converter.map_input_socket(y_input, translate_operation->get_input_socket(2)); - converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket()); + converter.map_output_socket(get_output_socket(), translate_operation->get_output_socket()); break; } } diff --git a/source/blender/compositor/nodes/COM_TransformNode.h b/source/blender/compositor/nodes/COM_TransformNode.h index 137e162256d..66255d63813 100644 --- a/source/blender/compositor/nodes/COM_TransformNode.h +++ b/source/blender/compositor/nodes/COM_TransformNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class TransformNode : public Node { public: - TransformNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TransformNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc index 6dbd350fabf..b9f96d92942 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cc +++ b/source/blender/compositor/nodes/COM_TranslateNode.cc @@ -24,53 +24,53 @@ namespace blender::compositor { -TranslateNode::TranslateNode(bNode *editorNode) : Node(editorNode) +TranslateNode::TranslateNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void TranslateNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void TranslateNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); NodeTranslateData *data = (NodeTranslateData *)bnode->storage; - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputXSocket = this->getInputSocket(1); - NodeInput *inputYSocket = this->getInputSocket(2); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_xsocket = this->get_input_socket(1); + NodeInput *input_ysocket = this->get_input_socket(2); + NodeOutput *output_socket = this->get_output_socket(0); TranslateOperation *operation = context.get_execution_model() == eExecutionModel::Tiled ? new TranslateOperation() : new TranslateCanvasOperation(); operation->set_wrapping(data->wrap_axis); if (data->relative) { - const RenderData *rd = context.getRenderData(); - const float render_size_factor = context.getRenderPercentageAsFactor(); + const RenderData *rd = context.get_render_data(); + const float render_size_factor = context.get_render_percentage_as_factor(); float fx = rd->xsch * render_size_factor; float fy = rd->ysch * render_size_factor; operation->setFactorXY(fx, fy); } - converter.addOperation(operation); - converter.mapInputSocket(inputXSocket, operation->getInputSocket(1)); - converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.add_operation(operation); + converter.map_input_socket(input_xsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_ysocket, operation->get_input_socket(2)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); if (data->wrap_axis && context.get_execution_model() != eExecutionModel::FullFrame) { /* TODO: To be removed with tiled implementation. */ - WriteBufferOperation *writeOperation = new WriteBufferOperation(DataType::Color); - WrapOperation *wrapOperation = new WrapOperation(DataType::Color); - wrapOperation->setMemoryProxy(writeOperation->getMemoryProxy()); - wrapOperation->setWrapping(data->wrap_axis); + WriteBufferOperation *write_operation = new WriteBufferOperation(DataType::Color); + WrapOperation *wrap_operation = new WrapOperation(DataType::Color); + wrap_operation->set_memory_proxy(write_operation->get_memory_proxy()); + wrap_operation->set_wrapping(data->wrap_axis); - converter.addOperation(writeOperation); - converter.addOperation(wrapOperation); - converter.mapInputSocket(inputSocket, writeOperation->getInputSocket(0)); - converter.addLink(wrapOperation->getOutputSocket(), operation->getInputSocket(0)); + converter.add_operation(write_operation); + converter.add_operation(wrap_operation); + converter.map_input_socket(input_socket, write_operation->get_input_socket(0)); + converter.add_link(wrap_operation->get_output_socket(), operation->get_input_socket(0)); } else { - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); } } diff --git a/source/blender/compositor/nodes/COM_TranslateNode.h b/source/blender/compositor/nodes/COM_TranslateNode.h index 0cea234bff8..5ccbb01f3f0 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.h +++ b/source/blender/compositor/nodes/COM_TranslateNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class TranslateNode : public Node { public: - TranslateNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TranslateNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ValueNode.cc b/source/blender/compositor/nodes/COM_ValueNode.cc index 611353fceba..892a68dabcc 100644 --- a/source/blender/compositor/nodes/COM_ValueNode.cc +++ b/source/blender/compositor/nodes/COM_ValueNode.cc @@ -21,20 +21,20 @@ namespace blender::compositor { -ValueNode::ValueNode(bNode *editorNode) : Node(editorNode) +ValueNode::ValueNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ValueNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ValueNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { SetValueOperation *operation = new SetValueOperation(); - NodeOutput *output = this->getOutputSocket(0); - operation->setValue(output->getEditorValueFloat()); - converter.addOperation(operation); + NodeOutput *output = this->get_output_socket(0); + operation->set_value(output->get_editor_value_float()); + converter.add_operation(operation); - converter.mapOutputSocket(output, operation->getOutputSocket()); + converter.map_output_socket(output, operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ValueNode.h b/source/blender/compositor/nodes/COM_ValueNode.h index 1401b2c7e0a..d70013c48af 100644 --- a/source/blender/compositor/nodes/COM_ValueNode.h +++ b/source/blender/compositor/nodes/COM_ValueNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ValueNode : public Node { public: - ValueNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ValueNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.cc b/source/blender/compositor/nodes/COM_VectorBlurNode.cc index 2bbfd3591c3..b3f32465a34 100644 --- a/source/blender/compositor/nodes/COM_VectorBlurNode.cc +++ b/source/blender/compositor/nodes/COM_VectorBlurNode.cc @@ -21,26 +21,26 @@ namespace blender::compositor { -VectorBlurNode::VectorBlurNode(bNode *editorNode) : Node(editorNode) +VectorBlurNode::VectorBlurNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void VectorBlurNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void VectorBlurNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *node = this->getbNode(); - NodeBlurData *vectorBlurSettings = (NodeBlurData *)node->storage; + bNode *node = this->get_bnode(); + NodeBlurData *vector_blur_settings = (NodeBlurData *)node->storage; VectorBlurOperation *operation = new VectorBlurOperation(); - operation->setVectorBlurSettings(vectorBlurSettings); - operation->setQuality(context.getQuality()); - converter.addOperation(operation); + operation->set_vector_blur_settings(vector_blur_settings); + operation->set_quality(context.get_quality()); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.h b/source/blender/compositor/nodes/COM_VectorBlurNode.h index 8c98a0b81a1..fb8a70ab2ec 100644 --- a/source/blender/compositor/nodes/COM_VectorBlurNode.h +++ b/source/blender/compositor/nodes/COM_VectorBlurNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class VectorBlurNode : public Node { public: - VectorBlurNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + VectorBlurNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.cc b/source/blender/compositor/nodes/COM_VectorCurveNode.cc index 2b26056296d..d064c5787fd 100644 --- a/source/blender/compositor/nodes/COM_VectorCurveNode.cc +++ b/source/blender/compositor/nodes/COM_VectorCurveNode.cc @@ -21,20 +21,20 @@ namespace blender::compositor { -VectorCurveNode::VectorCurveNode(bNode *editorNode) : Node(editorNode) +VectorCurveNode::VectorCurveNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void VectorCurveNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void VectorCurveNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { VectorCurveOperation *operation = new VectorCurveOperation(); - operation->setCurveMapping((CurveMapping *)this->getbNode()->storage); - converter.addOperation(operation); + operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.h b/source/blender/compositor/nodes/COM_VectorCurveNode.h index ee4df5d3a42..901bd6f64b9 100644 --- a/source/blender/compositor/nodes/COM_VectorCurveNode.h +++ b/source/blender/compositor/nodes/COM_VectorCurveNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class VectorCurveNode : public Node { public: - VectorCurveNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + VectorCurveNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc index 605ff9357ab..79d535fc58a 100644 --- a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc +++ b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc @@ -21,41 +21,41 @@ namespace blender::compositor { -ViewLevelsNode::ViewLevelsNode(bNode *editorNode) : Node(editorNode) +ViewLevelsNode::ViewLevelsNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ViewLevelsNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ViewLevelsNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *input = this->getInputSocket(0); - if (input->isLinked()) { + NodeInput *input = this->get_input_socket(0); + if (input->is_linked()) { /* Add preview to input-socket. */ /* calculate mean operation */ { CalculateMeanOperation *operation = new CalculateMeanOperation(); - operation->setSetting(this->getbNode()->custom1); + operation->set_setting(this->get_bnode()->custom1); - converter.addOperation(operation); - converter.mapInputSocket(input, operation->getInputSocket(0)); - converter.mapOutputSocket(this->getOutputSocket(0), operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_input_socket(input, operation->get_input_socket(0)); + converter.map_output_socket(this->get_output_socket(0), operation->get_output_socket()); } /* calculate standard deviation operation */ { CalculateStandardDeviationOperation *operation = new CalculateStandardDeviationOperation(); - operation->setSetting(this->getbNode()->custom1); + operation->set_setting(this->get_bnode()->custom1); - converter.addOperation(operation); - converter.mapInputSocket(input, operation->getInputSocket(0)); - converter.mapOutputSocket(this->getOutputSocket(1), operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_input_socket(input, operation->get_input_socket(0)); + converter.map_output_socket(this->get_output_socket(1), operation->get_output_socket()); } } else { - converter.addOutputValue(getOutputSocket(0), 0.0f); - converter.addOutputValue(getOutputSocket(1), 0.0f); + converter.add_output_value(get_output_socket(0), 0.0f); + converter.add_output_value(get_output_socket(1), 0.0f); } } diff --git a/source/blender/compositor/nodes/COM_ViewLevelsNode.h b/source/blender/compositor/nodes/COM_ViewLevelsNode.h index 055d871498e..5415bbb2ee9 100644 --- a/source/blender/compositor/nodes/COM_ViewLevelsNode.h +++ b/source/blender/compositor/nodes/COM_ViewLevelsNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ViewLevelsNode : public Node { public: - ViewLevelsNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ViewLevelsNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cc b/source/blender/compositor/nodes/COM_ViewerNode.cc index 38ea37a28d6..7efb23b849d 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.cc +++ b/source/blender/compositor/nodes/COM_ViewerNode.cc @@ -22,61 +22,61 @@ namespace blender::compositor { -ViewerNode::ViewerNode(bNode *editorNode) : Node(editorNode) +ViewerNode::ViewerNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ViewerNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void ViewerNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - bool do_output = (editorNode->flag & NODE_DO_OUTPUT_RECALC || context.isRendering()) && - (editorNode->flag & NODE_DO_OUTPUT); - bool ignore_alpha = (editorNode->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0; + bNode *editor_node = this->get_bnode(); + bool do_output = (editor_node->flag & NODE_DO_OUTPUT_RECALC || context.is_rendering()) && + (editor_node->flag & NODE_DO_OUTPUT); + bool ignore_alpha = (editor_node->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0; - NodeInput *imageSocket = this->getInputSocket(0); - NodeInput *alphaSocket = this->getInputSocket(1); - NodeInput *depthSocket = this->getInputSocket(2); - Image *image = (Image *)this->getbNode()->id; - ImageUser *imageUser = (ImageUser *)this->getbNode()->storage; - ViewerOperation *viewerOperation = new ViewerOperation(); - viewerOperation->setbNodeTree(context.getbNodeTree()); - viewerOperation->setImage(image); - viewerOperation->setImageUser(imageUser); - viewerOperation->setChunkOrder((ChunkOrdering)editorNode->custom1); - viewerOperation->setCenterX(editorNode->custom3); - viewerOperation->setCenterY(editorNode->custom4); + NodeInput *image_socket = this->get_input_socket(0); + NodeInput *alpha_socket = this->get_input_socket(1); + NodeInput *depth_socket = this->get_input_socket(2); + Image *image = (Image *)this->get_bnode()->id; + ImageUser *image_user = (ImageUser *)this->get_bnode()->storage; + ViewerOperation *viewer_operation = new ViewerOperation(); + viewer_operation->set_bnodetree(context.get_bnodetree()); + viewer_operation->set_image(image); + viewer_operation->set_image_user(image_user); + viewer_operation->set_chunk_order((ChunkOrdering)editor_node->custom1); + viewer_operation->setCenterX(editor_node->custom3); + viewer_operation->setCenterY(editor_node->custom4); /* alpha socket gives either 1 or a custom alpha value if "use alpha" is enabled */ - viewerOperation->setUseAlphaInput(ignore_alpha || alphaSocket->isLinked()); - viewerOperation->setRenderData(context.getRenderData()); - viewerOperation->setViewName(context.getViewName()); + viewer_operation->set_use_alpha_input(ignore_alpha || alpha_socket->is_linked()); + viewer_operation->set_render_data(context.get_render_data()); + viewer_operation->set_view_name(context.get_view_name()); - viewerOperation->setViewSettings(context.getViewSettings()); - viewerOperation->setDisplaySettings(context.getDisplaySettings()); + viewer_operation->set_view_settings(context.get_view_settings()); + viewer_operation->set_display_settings(context.get_display_settings()); - viewerOperation->set_canvas_input_index(0); - if (!imageSocket->isLinked()) { - if (alphaSocket->isLinked()) { - viewerOperation->set_canvas_input_index(1); + viewer_operation->set_canvas_input_index(0); + if (!image_socket->is_linked()) { + if (alpha_socket->is_linked()) { + viewer_operation->set_canvas_input_index(1); } } - converter.addOperation(viewerOperation); - converter.mapInputSocket(imageSocket, viewerOperation->getInputSocket(0)); + converter.add_operation(viewer_operation); + converter.map_input_socket(image_socket, viewer_operation->get_input_socket(0)); /* only use alpha link if "use alpha" is enabled */ if (ignore_alpha) { - converter.addInputValue(viewerOperation->getInputSocket(1), 1.0f); + converter.add_input_value(viewer_operation->get_input_socket(1), 1.0f); } else { - converter.mapInputSocket(alphaSocket, viewerOperation->getInputSocket(1)); + converter.map_input_socket(alpha_socket, viewer_operation->get_input_socket(1)); } - converter.mapInputSocket(depthSocket, viewerOperation->getInputSocket(2)); + converter.map_input_socket(depth_socket, viewer_operation->get_input_socket(2)); - converter.addNodeInputPreview(imageSocket); + converter.add_node_input_preview(image_socket); if (do_output) { - converter.registerViewer(viewerOperation); + converter.register_viewer(viewer_operation); } } diff --git a/source/blender/compositor/nodes/COM_ViewerNode.h b/source/blender/compositor/nodes/COM_ViewerNode.h index 544a5e6a504..ce9076666bc 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.h +++ b/source/blender/compositor/nodes/COM_ViewerNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class ViewerNode : public Node { public: - ViewerNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ViewerNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.cc b/source/blender/compositor/nodes/COM_ZCombineNode.cc index 5f7fec53808..52cb1451ba1 100644 --- a/source/blender/compositor/nodes/COM_ZCombineNode.cc +++ b/source/blender/compositor/nodes/COM_ZCombineNode.cc @@ -24,31 +24,31 @@ namespace blender::compositor { -void ZCombineNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ZCombineNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - if (this->getbNode()->custom2) { + if (this->get_bnode()->custom2) { ZCombineOperation *operation = nullptr; - if (this->getbNode()->custom1) { + if (this->get_bnode()->custom1) { operation = new ZCombineAlphaOperation(); } else { operation = new ZCombineOperation(); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapInputSocket(getInputSocket(3), operation->getInputSocket(3)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_input_socket(get_input_socket(3), operation->get_input_socket(3)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); MathMinimumOperation *zoperation = new MathMinimumOperation(); - converter.addOperation(zoperation); + converter.add_operation(zoperation); - converter.mapInputSocket(getInputSocket(1), zoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(3), zoperation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(1), zoperation->getOutputSocket()); + converter.map_input_socket(get_input_socket(1), zoperation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(3), zoperation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(1), zoperation->get_output_socket()); } else { /* XXX custom1 is "use_alpha", what on earth is this supposed to do here?!? */ @@ -56,40 +56,42 @@ void ZCombineNode::convertToOperations(NodeConverter &converter, /* Step 1 create mask. */ NodeOperation *maskoperation; - if (this->getbNode()->custom1) { + if (this->get_bnode()->custom1) { maskoperation = new MathGreaterThanOperation(); } else { maskoperation = new MathLessThanOperation(); } - converter.addOperation(maskoperation); + converter.add_operation(maskoperation); - converter.mapInputSocket(getInputSocket(1), maskoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(3), maskoperation->getInputSocket(1)); + converter.map_input_socket(get_input_socket(1), maskoperation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(3), maskoperation->get_input_socket(1)); /* Step 2 anti alias mask bit of an expensive operation, but does the trick. */ AntiAliasOperation *antialiasoperation = new AntiAliasOperation(); - converter.addOperation(antialiasoperation); + converter.add_operation(antialiasoperation); - converter.addLink(maskoperation->getOutputSocket(), antialiasoperation->getInputSocket(0)); + converter.add_link(maskoperation->get_output_socket(), + antialiasoperation->get_input_socket(0)); /* use mask to blend between the input colors. */ - ZCombineMaskOperation *zcombineoperation = this->getbNode()->custom1 ? + ZCombineMaskOperation *zcombineoperation = this->get_bnode()->custom1 ? new ZCombineMaskAlphaOperation() : new ZCombineMaskOperation(); - converter.addOperation(zcombineoperation); + converter.add_operation(zcombineoperation); - converter.addLink(antialiasoperation->getOutputSocket(), zcombineoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(0), zcombineoperation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), zcombineoperation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), zcombineoperation->getOutputSocket()); + converter.add_link(antialiasoperation->get_output_socket(), + zcombineoperation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(0), zcombineoperation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), zcombineoperation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), zcombineoperation->get_output_socket()); MathMinimumOperation *zoperation = new MathMinimumOperation(); - converter.addOperation(zoperation); + converter.add_operation(zoperation); - converter.mapInputSocket(getInputSocket(1), zoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(3), zoperation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(1), zoperation->getOutputSocket()); + converter.map_input_socket(get_input_socket(1), zoperation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(3), zoperation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(1), zoperation->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.h b/source/blender/compositor/nodes/COM_ZCombineNode.h index 82f2f30fb3c..4761b336fc1 100644 --- a/source/blender/compositor/nodes/COM_ZCombineNode.h +++ b/source/blender/compositor/nodes/COM_ZCombineNode.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class ZCombineNode : public Node { public: - ZCombineNode(bNode *editorNode) : Node(editorNode) + ZCombineNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc index 2e494d1adf3..3c5053d8357 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc @@ -25,33 +25,33 @@ AlphaOverKeyOperation::AlphaOverKeyOperation() this->flags.can_be_constant = true; } -void AlphaOverKeyOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void AlphaOverKeyOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputOverColor[4]; + float input_color1[4]; + float input_over_color[4]; float value[4]; - inputValueOperation_->readSampled(value, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputOverColor, x, y, sampler); + input_value_operation_->read_sampled(value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_over_color, x, y, sampler); - if (inputOverColor[3] <= 0.0f) { - copy_v4_v4(output, inputColor1); + if (input_over_color[3] <= 0.0f) { + copy_v4_v4(output, input_color1); } - else if (value[0] == 1.0f && inputOverColor[3] >= 1.0f) { - copy_v4_v4(output, inputOverColor); + else if (value[0] == 1.0f && input_over_color[3] >= 1.0f) { + copy_v4_v4(output, input_over_color); } else { - float premul = value[0] * inputOverColor[3]; + float premul = value[0] * input_over_color[3]; float mul = 1.0f - premul; - output[0] = (mul * inputColor1[0]) + premul * inputOverColor[0]; - output[1] = (mul * inputColor1[1]) + premul * inputOverColor[1]; - output[2] = (mul * inputColor1[2]) + premul * inputOverColor[2]; - output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3]; + output[0] = (mul * input_color1[0]) + premul * input_over_color[0]; + output[1] = (mul * input_color1[1]) + premul * input_over_color[1]; + output[2] = (mul * input_color1[2]) + premul * input_over_color[2]; + output[3] = (mul * input_color1[3]) + value[0] * input_over_color[3]; } } diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h index 960fbc98fe9..cfcb655ceca 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h @@ -33,7 +33,7 @@ class AlphaOverKeyOperation : public MixBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_row(PixelCursor &p) override; }; diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc index 1df74ffac8e..102d031d9be 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc @@ -26,34 +26,34 @@ AlphaOverMixedOperation::AlphaOverMixedOperation() this->flags.can_be_constant = true; } -void AlphaOverMixedOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void AlphaOverMixedOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputOverColor[4]; + float input_color1[4]; + float input_over_color[4]; float value[4]; - inputValueOperation_->readSampled(value, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputOverColor, x, y, sampler); + input_value_operation_->read_sampled(value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_over_color, x, y, sampler); - if (inputOverColor[3] <= 0.0f) { - copy_v4_v4(output, inputColor1); + if (input_over_color[3] <= 0.0f) { + copy_v4_v4(output, input_color1); } - else if (value[0] == 1.0f && inputOverColor[3] >= 1.0f) { - copy_v4_v4(output, inputOverColor); + else if (value[0] == 1.0f && input_over_color[3] >= 1.0f) { + copy_v4_v4(output, input_over_color); } else { - float addfac = 1.0f - x_ + inputOverColor[3] * x_; + float addfac = 1.0f - x_ + input_over_color[3] * x_; float premul = value[0] * addfac; - float mul = 1.0f - value[0] * inputOverColor[3]; + float mul = 1.0f - value[0] * input_over_color[3]; - output[0] = (mul * inputColor1[0]) + premul * inputOverColor[0]; - output[1] = (mul * inputColor1[1]) + premul * inputOverColor[1]; - output[2] = (mul * inputColor1[2]) + premul * inputOverColor[2]; - output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3]; + output[0] = (mul * input_color1[0]) + premul * input_over_color[0]; + output[1] = (mul * input_color1[1]) + premul * input_over_color[1]; + output[2] = (mul * input_color1[2]) + premul * input_over_color[2]; + output[3] = (mul * input_color1[3]) + value[0] * input_over_color[3]; } } diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h index 65967c03eb9..32ff77fdecd 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h @@ -39,7 +39,7 @@ class AlphaOverMixedOperation : public MixBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void setX(float x) { diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc index 5af992c8809..2d04c22533b 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc @@ -25,33 +25,33 @@ AlphaOverPremultiplyOperation::AlphaOverPremultiplyOperation() this->flags.can_be_constant = true; } -void AlphaOverPremultiplyOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void AlphaOverPremultiplyOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputOverColor[4]; + float input_color1[4]; + float input_over_color[4]; float value[4]; - inputValueOperation_->readSampled(value, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputOverColor, x, y, sampler); + input_value_operation_->read_sampled(value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_over_color, x, y, sampler); /* Zero alpha values should still permit an add of RGB data */ - if (inputOverColor[3] < 0.0f) { - copy_v4_v4(output, inputColor1); + if (input_over_color[3] < 0.0f) { + copy_v4_v4(output, input_color1); } - else if (value[0] == 1.0f && inputOverColor[3] >= 1.0f) { - copy_v4_v4(output, inputOverColor); + else if (value[0] == 1.0f && input_over_color[3] >= 1.0f) { + copy_v4_v4(output, input_over_color); } else { - float mul = 1.0f - value[0] * inputOverColor[3]; + float mul = 1.0f - value[0] * input_over_color[3]; - output[0] = (mul * inputColor1[0]) + value[0] * inputOverColor[0]; - output[1] = (mul * inputColor1[1]) + value[0] * inputOverColor[1]; - output[2] = (mul * inputColor1[2]) + value[0] * inputOverColor[2]; - output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3]; + output[0] = (mul * input_color1[0]) + value[0] * input_over_color[0]; + output[1] = (mul * input_color1[1]) + value[0] * input_over_color[1]; + output[2] = (mul * input_color1[2]) + value[0] * input_over_color[2]; + output[3] = (mul * input_color1[3]) + value[0] * input_over_color[3]; } } diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h index 701bc07cc27..cc4cb615de8 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h @@ -33,7 +33,7 @@ class AlphaOverPremultiplyOperation : public MixBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_row(PixelCursor &p) override; }; diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.cc b/source/blender/compositor/operations/COM_AntiAliasOperation.cc index 8a3b6f4df11..28fea7b406c 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.cc +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.cc @@ -110,26 +110,26 @@ static int extrapolate9(float *E0, AntiAliasOperation::AntiAliasOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - valueReader_ = nullptr; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + value_reader_ = nullptr; this->flags.complex = true; } -void AntiAliasOperation::initExecution() +void AntiAliasOperation::init_execution() { - valueReader_ = this->getInputSocketReader(0); + value_reader_ = this->get_input_socket_reader(0); } -void AntiAliasOperation::executePixel(float output[4], int x, int y, void *data) +void AntiAliasOperation::execute_pixel(float output[4], int x, int y, void *data) { MemoryBuffer *input_buffer = (MemoryBuffer *)data; - const int buffer_width = input_buffer->getWidth(), buffer_height = input_buffer->getHeight(); + const int buffer_width = input_buffer->get_width(), buffer_height = input_buffer->get_height(); if (y < 0 || y >= buffer_height || x < 0 || x >= buffer_width) { output[0] = 0.0f; } else { - const float *buffer = input_buffer->getBuffer(); + const float *buffer = input_buffer->get_buffer(); const float *row_curr = &buffer[y * buffer_width]; if (x == 0 || x == buffer_width - 1 || y == 0 || y == buffer_height - 1) { output[0] = row_curr[x]; @@ -173,27 +173,27 @@ void AntiAliasOperation::executePixel(float output[4], int x, int y, void *data) } } -void AntiAliasOperation::deinitExecution() +void AntiAliasOperation::deinit_execution() { - valueReader_ = nullptr; + value_reader_ = nullptr; } -bool AntiAliasOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool AntiAliasOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti imageInput; - NodeOperation *operation = getInputOperation(0); - imageInput.xmax = input->xmax + 1; - imageInput.xmin = input->xmin - 1; - imageInput.ymax = input->ymax + 1; - imageInput.ymin = input->ymin - 1; - return operation->determineDependingAreaOfInterest(&imageInput, readOperation, output); + rcti image_input; + NodeOperation *operation = get_input_operation(0); + image_input.xmax = input->xmax + 1; + image_input.xmin = input->xmin - 1; + image_input.ymax = input->ymax + 1; + image_input.ymin = input->ymin - 1; + return operation->determine_depending_area_of_interest(&image_input, read_operation, output); } -void *AntiAliasOperation::initializeTileData(rcti *rect) +void *AntiAliasOperation::initialize_tile_data(rcti *rect) { - return getInputOperation(0)->initializeTileData(rect); + return get_input_operation(0)->initialize_tile_data(rect); } void AntiAliasOperation::get_area_of_interest(const int input_idx, diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.h b/source/blender/compositor/operations/COM_AntiAliasOperation.h index 48ae03a1aaa..0a6a63bf57e 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.h +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.h @@ -33,7 +33,7 @@ class AntiAliasOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *valueReader_; + SocketReader *value_reader_; public: AntiAliasOperation(); @@ -41,22 +41,22 @@ class AntiAliasOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void deinit_execution() override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc index 7368dcf0cad..5f7d2432f05 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc @@ -22,65 +22,65 @@ namespace blender::compositor { BilateralBlurOperation::BilateralBlurOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); this->flags.complex = true; - inputColorProgram_ = nullptr; - inputDeterminatorProgram_ = nullptr; + input_color_program_ = nullptr; + input_determinator_program_ = nullptr; } -void BilateralBlurOperation::initExecution() +void BilateralBlurOperation::init_execution() { - inputColorProgram_ = getInputSocketReader(0); - inputDeterminatorProgram_ = getInputSocketReader(1); - QualityStepHelper::initExecution(COM_QH_INCREASE); + input_color_program_ = get_input_socket_reader(0); + input_determinator_program_ = get_input_socket_reader(1); + QualityStepHelper::init_execution(COM_QH_INCREASE); } -void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *data) +void BilateralBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { /* Read the determinator color at x, y, * this will be used as the reference color for the determinator. */ - float determinatorReferenceColor[4]; + float determinator_reference_color[4]; float determinator[4]; - float tempColor[4]; - float blurColor[4]; - float blurDivider; + float temp_color[4]; + float blur_color[4]; + float blur_divider; float space = space_; float sigmacolor = data_->sigma_color; int minx = floor(x - space); int maxx = ceil(x + space); int miny = floor(y - space); int maxy = ceil(y + space); - float deltaColor; - inputDeterminatorProgram_->read(determinatorReferenceColor, x, y, data); + float delta_color; + input_determinator_program_->read(determinator_reference_color, x, y, data); - zero_v4(blurColor); - blurDivider = 0.0f; + zero_v4(blur_color); + blur_divider = 0.0f; /* TODO(sergey): This isn't really good bilateral filter, it should be * using gaussian bell for weights. Also sigma_color doesn't seem to be * used correct at all. */ - for (int yi = miny; yi < maxy; yi += QualityStepHelper::getStep()) { - for (int xi = minx; xi < maxx; xi += QualityStepHelper::getStep()) { + for (int yi = miny; yi < maxy; yi += QualityStepHelper::get_step()) { + for (int xi = minx; xi < maxx; xi += QualityStepHelper::get_step()) { /* Read determinator. */ - inputDeterminatorProgram_->read(determinator, xi, yi, data); - deltaColor = (fabsf(determinatorReferenceColor[0] - determinator[0]) + - fabsf(determinatorReferenceColor[1] - determinator[1]) + - /* Do not take the alpha channel into account. */ - fabsf(determinatorReferenceColor[2] - determinator[2])); - if (deltaColor < sigmacolor) { + input_determinator_program_->read(determinator, xi, yi, data); + delta_color = (fabsf(determinator_reference_color[0] - determinator[0]) + + fabsf(determinator_reference_color[1] - determinator[1]) + + /* Do not take the alpha channel into account. */ + fabsf(determinator_reference_color[2] - determinator[2])); + if (delta_color < sigmacolor) { /* Add this to the blur. */ - inputColorProgram_->read(tempColor, xi, yi, data); - add_v4_v4(blurColor, tempColor); - blurDivider += 1.0f; + input_color_program_->read(temp_color, xi, yi, data); + add_v4_v4(blur_color, temp_color); + blur_divider += 1.0f; } } } - if (blurDivider > 0.0f) { - mul_v4_v4fl(output, blurColor, 1.0f / blurDivider); + if (blur_divider > 0.0f) { + mul_v4_v4fl(output, blur_color, 1.0f / blur_divider); } else { output[0] = 0.0f; @@ -90,25 +90,24 @@ void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *d } } -void BilateralBlurOperation::deinitExecution() +void BilateralBlurOperation::deinit_execution() { - inputColorProgram_ = nullptr; - inputDeterminatorProgram_ = nullptr; + input_color_program_ = nullptr; + input_determinator_program_ = nullptr; } -bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool BilateralBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; int add = ceil(space_) + 1; - newInput.xmax = input->xmax + (add); - newInput.xmin = input->xmin - (add); - newInput.ymax = input->ymax + (add); - newInput.ymin = input->ymin - (add); + new_input.xmax = input->xmax + (add); + new_input.xmin = input->xmin - (add); + new_input.ymax = input->ymax + (add); + new_input.ymin = input->ymin - (add); - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void BilateralBlurOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -173,7 +172,7 @@ void BilateralBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { PixelCursor p = {}; - p.step = QualityStepHelper::getStep(); + p.step = QualityStepHelper::get_step(); p.sigma_color = data_->sigma_color; p.input_color = inputs[0]; p.input_determinator = inputs[1]; diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h index aa94df1e254..42df82a5296 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h @@ -25,8 +25,8 @@ namespace blender::compositor { class BilateralBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: - SocketReader *inputColorProgram_; - SocketReader *inputDeterminatorProgram_; + SocketReader *input_color_program_; + SocketReader *input_determinator_program_; NodeBilateralBlurData *data_; float space_; @@ -36,23 +36,23 @@ class BilateralBlurOperation : public MultiThreadedOperation, public QualityStep /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setData(NodeBilateralBlurData *data) + void set_data(NodeBilateralBlurData *data) { data_ = data; space_ = data->sigma_space + data->iter; diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc index 3c43a16599b..15e2ce2d1e0 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc @@ -26,11 +26,11 @@ namespace blender::compositor { BlurBaseOperation::BlurBaseOperation(DataType data_type) { /* data_type is almost always DataType::Color except for alpha-blur */ - this->addInputSocket(data_type); - this->addInputSocket(DataType::Value); - this->addOutputSocket(data_type); + this->add_input_socket(data_type); + this->add_input_socket(DataType::Value); + this->add_output_socket(data_type); this->flags.complex = true; - inputProgram_ = nullptr; + input_program_ = nullptr; memset(&data_, 0, sizeof(NodeBlurData)); size_ = 1.0f; sizeavailable_ = false; @@ -41,11 +41,11 @@ BlurBaseOperation::BlurBaseOperation(DataType data_type) void BlurBaseOperation::init_data() { if (execution_model_ == eExecutionModel::FullFrame) { - updateSize(); + update_size(); } - data_.image_in_width = this->getWidth(); - data_.image_in_height = this->getHeight(); + data_.image_in_width = this->get_width(); + data_.image_in_height = this->get_height(); if (data_.relative) { int sizex, sizey; switch (data_.aspect) { @@ -66,12 +66,12 @@ void BlurBaseOperation::init_data() } } -void BlurBaseOperation::initExecution() +void BlurBaseOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); - inputSize_ = this->getInputSocketReader(1); + input_program_ = this->get_input_socket_reader(0); + input_size_ = this->get_input_socket_reader(1); - QualityStepHelper::initExecution(COM_QH_MULTIPLY); + QualityStepHelper::init_execution(COM_QH_MULTIPLY); } float *BlurBaseOperation::make_gausstab(float rad, int size) @@ -163,13 +163,13 @@ float *BlurBaseOperation::make_dist_fac_inverse(float rad, int size, int falloff return dist_fac_invert; } -void BlurBaseOperation::deinitExecution() +void BlurBaseOperation::deinit_execution() { - inputProgram_ = nullptr; - inputSize_ = nullptr; + input_program_ = nullptr; + input_size_ = nullptr; } -void BlurBaseOperation::setData(const NodeBlurData *data) +void BlurBaseOperation::set_data(const NodeBlurData *data) { memcpy(&data_, data, sizeof(NodeBlurData)); } @@ -185,7 +185,7 @@ int BlurBaseOperation::get_blur_size(eDimension dim) const return -1; } -void BlurBaseOperation::updateSize() +void BlurBaseOperation::update_size() { if (sizeavailable_ || use_variable_size_) { return; @@ -194,7 +194,7 @@ void BlurBaseOperation::updateSize() switch (execution_model_) { case eExecutionModel::Tiled: { float result[4]; - this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest); + this->get_input_socket_reader(1)->read_sampled(result, 0, 0, PixelSampler::Nearest); size_ = result[0]; break; } diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h index b1dad2e868f..ab378983100 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h @@ -43,13 +43,13 @@ class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelpe #endif float *make_dist_fac_inverse(float rad, int size, int falloff); - void updateSize(); + void update_size(); /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; - SocketReader *inputSize_; + SocketReader *input_program_; + SocketReader *input_size_; NodeBlurData data_; float size_; @@ -63,22 +63,22 @@ class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelpe /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setData(const NodeBlurData *data); + void set_data(const NodeBlurData *data); - void setSize(float size) + void set_size(float size) { size_ = size; sizeavailable_ = true; } - void setExtendBounds(bool extend_bounds) + void set_extend_bounds(bool extend_bounds) { extend_bounds_ = extend_bounds; } diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc index d00a18baf7b..80e7390e02f 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc @@ -30,20 +30,20 @@ constexpr int SIZE_INPUT_INDEX = 3; BokehBlurOperation::BokehBlurOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color, ResizeMode::Align); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); flags.complex = true; flags.open_cl = true; size_ = 1.0f; sizeavailable_ = false; - inputProgram_ = nullptr; - inputBokehProgram_ = nullptr; - inputBoundingBoxReader_ = nullptr; + input_program_ = nullptr; + input_bokeh_program_ = nullptr; + input_bounding_box_reader_ = nullptr; extend_bounds_ = false; } @@ -51,88 +51,88 @@ BokehBlurOperation::BokehBlurOperation() void BokehBlurOperation::init_data() { if (execution_model_ == eExecutionModel::FullFrame) { - updateSize(); + update_size(); } NodeOperation *bokeh = get_input_operation(BOKEH_INPUT_INDEX); - const int width = bokeh->getWidth(); - const int height = bokeh->getHeight(); + const int width = bokeh->get_width(); + const int height = bokeh->get_height(); const float dimension = MIN2(width, height); - bokehMidX_ = width / 2.0f; - bokehMidY_ = height / 2.0f; + bokeh_mid_x_ = width / 2.0f; + bokeh_mid_y_ = height / 2.0f; bokehDimension_ = dimension / 2.0f; } -void *BokehBlurOperation::initializeTileData(rcti * /*rect*/) +void *BokehBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); + lock_mutex(); if (!sizeavailable_) { - updateSize(); + update_size(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } -void BokehBlurOperation::initExecution() +void BokehBlurOperation::init_execution() { - initMutex(); + init_mutex(); - inputProgram_ = getInputSocketReader(0); - inputBokehProgram_ = getInputSocketReader(1); - inputBoundingBoxReader_ = getInputSocketReader(2); + input_program_ = get_input_socket_reader(0); + input_bokeh_program_ = get_input_socket_reader(1); + input_bounding_box_reader_ = get_input_socket_reader(2); - QualityStepHelper::initExecution(COM_QH_INCREASE); + QualityStepHelper::init_execution(COM_QH_INCREASE); } -void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) +void BokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { float color_accum[4]; - float tempBoundingBox[4]; + float temp_bounding_box[4]; float bokeh[4]; - inputBoundingBoxReader_->readSampled(tempBoundingBox, x, y, PixelSampler::Nearest); - if (tempBoundingBox[0] > 0.0f) { + input_bounding_box_reader_->read_sampled(temp_bounding_box, x, y, PixelSampler::Nearest); + if (temp_bounding_box[0] > 0.0f) { float multiplier_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - const rcti &input_rect = inputBuffer->get_rect(); - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + const rcti &input_rect = input_buffer->get_rect(); + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; - const float max_dim = MAX2(this->getWidth(), this->getHeight()); - int pixelSize = size_ * max_dim / 100.0f; + const float max_dim = MAX2(this->get_width(), this->get_height()); + int pixel_size = size_ * max_dim / 100.0f; zero_v4(color_accum); - if (pixelSize < 2) { - inputProgram_->readSampled(color_accum, x, y, PixelSampler::Nearest); + if (pixel_size < 2) { + input_program_->read_sampled(color_accum, x, y, PixelSampler::Nearest); multiplier_accum[0] = 1.0f; multiplier_accum[1] = 1.0f; multiplier_accum[2] = 1.0f; multiplier_accum[3] = 1.0f; } - int miny = y - pixelSize; - int maxy = y + pixelSize; - int minx = x - pixelSize; - int maxx = x + pixelSize; + int miny = y - pixel_size; + int maxy = y + pixel_size; + int minx = x - pixel_size; + int maxx = x + pixel_size; miny = MAX2(miny, input_rect.ymin); minx = MAX2(minx, input_rect.xmin); maxy = MIN2(maxy, input_rect.ymax); maxx = MIN2(maxx, input_rect.xmax); - int step = getStep(); - int offsetadd = getOffsetAdd() * COM_DATA_TYPE_COLOR_CHANNELS; + int step = get_step(); + int offsetadd = get_offset_add() * COM_DATA_TYPE_COLOR_CHANNELS; - float m = bokehDimension_ / pixelSize; + float m = bokehDimension_ / pixel_size; for (int ny = miny; ny < maxy; ny += step) { int bufferindex = ((minx - bufferstartx) * COM_DATA_TYPE_COLOR_CHANNELS) + ((ny - bufferstarty) * COM_DATA_TYPE_COLOR_CHANNELS * bufferwidth); for (int nx = minx; nx < maxx; nx += step) { - float u = bokehMidX_ - (nx - x) * m; - float v = bokehMidY_ - (ny - y) * m; - inputBokehProgram_->readSampled(bokeh, u, v, PixelSampler::Nearest); + float u = bokeh_mid_x_ - (nx - x) * m; + float v = bokeh_mid_y_ - (ny - y) * m; + input_bokeh_program_->read_sampled(bokeh, u, v, PixelSampler::Nearest); madd_v4_v4v4(color_accum, bokeh, &buffer[bufferindex]); add_v4_v4(multiplier_accum, bokeh); bufferindex += offsetadd; @@ -144,100 +144,100 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) output[3] = color_accum[3] * (1.0f / multiplier_accum[3]); } else { - inputProgram_->readSampled(output, x, y, PixelSampler::Nearest); + input_program_->read_sampled(output, x, y, PixelSampler::Nearest); } } -void BokehBlurOperation::deinitExecution() +void BokehBlurOperation::deinit_execution() { - deinitMutex(); - inputProgram_ = nullptr; - inputBokehProgram_ = nullptr; - inputBoundingBoxReader_ = nullptr; + deinit_mutex(); + input_program_ = nullptr; + input_bokeh_program_ = nullptr; + input_bounding_box_reader_ = nullptr; } -bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool BokehBlurOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; - rcti bokehInput; - const float max_dim = MAX2(this->getWidth(), this->getHeight()); + rcti new_input; + rcti bokeh_input; + const float max_dim = MAX2(this->get_width(), this->get_height()); if (sizeavailable_) { - newInput.xmax = input->xmax + (size_ * max_dim / 100.0f); - newInput.xmin = input->xmin - (size_ * max_dim / 100.0f); - newInput.ymax = input->ymax + (size_ * max_dim / 100.0f); - newInput.ymin = input->ymin - (size_ * max_dim / 100.0f); + new_input.xmax = input->xmax + (size_ * max_dim / 100.0f); + new_input.xmin = input->xmin - (size_ * max_dim / 100.0f); + new_input.ymax = input->ymax + (size_ * max_dim / 100.0f); + new_input.ymin = input->ymin - (size_ * max_dim / 100.0f); } else { - newInput.xmax = input->xmax + (10.0f * max_dim / 100.0f); - newInput.xmin = input->xmin - (10.0f * max_dim / 100.0f); - newInput.ymax = input->ymax + (10.0f * max_dim / 100.0f); - newInput.ymin = input->ymin - (10.0f * max_dim / 100.0f); + new_input.xmax = input->xmax + (10.0f * max_dim / 100.0f); + new_input.xmin = input->xmin - (10.0f * max_dim / 100.0f); + new_input.ymax = input->ymax + (10.0f * max_dim / 100.0f); + new_input.ymin = input->ymin - (10.0f * max_dim / 100.0f); } - NodeOperation *operation = getInputOperation(1); - bokehInput.xmax = operation->getWidth(); - bokehInput.xmin = 0; - bokehInput.ymax = operation->getHeight(); - bokehInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&bokehInput, readOperation, output)) { + NodeOperation *operation = get_input_operation(1); + bokeh_input.xmax = operation->get_width(); + bokeh_input.xmin = 0; + bokeh_input.ymax = operation->get_height(); + bokeh_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&bokeh_input, read_operation, output)) { return true; } - operation = getInputOperation(0); - if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + operation = get_input_operation(0); + if (operation->determine_depending_area_of_interest(&new_input, read_operation, output)) { return true; } - operation = getInputOperation(2); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + operation = get_input_operation(2); + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } if (!sizeavailable_) { - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; - operation = getInputOperation(3); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; + operation = get_input_operation(3); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } } return false; } -void BokehBlurOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void BokehBlurOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel kernel = device->COM_clCreateKernel("bokehBlurKernel", nullptr); + cl_kernel kernel = device->COM_cl_create_kernel("bokeh_blur_kernel", nullptr); if (!sizeavailable_) { - updateSize(); + update_size(); } - const float max_dim = MAX2(this->getWidth(), this->getHeight()); + const float max_dim = MAX2(this->get_width(), this->get_height()); cl_int radius = size_ * max_dim / 100.0f; - cl_int step = this->getStep(); + cl_int step = this->get_step(); - device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, inputBoundingBoxReader_); - device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 1, 4, clMemToCleanUp, inputMemoryBuffers, inputProgram_); - device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 2, -1, clMemToCleanUp, inputMemoryBuffers, inputBokehProgram_); - device->COM_clAttachOutputMemoryBufferToKernelParameter(kernel, 3, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter(kernel, 5, outputMemoryBuffer); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + kernel, 0, -1, cl_mem_to_clean_up, input_memory_buffers, input_bounding_box_reader_); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + kernel, 1, 4, cl_mem_to_clean_up, input_memory_buffers, input_program_); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + kernel, 2, -1, cl_mem_to_clean_up, input_memory_buffers, input_bokeh_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter(kernel, 3, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter(kernel, 5, output_memory_buffer); clSetKernelArg(kernel, 6, sizeof(cl_int), &radius); clSetKernelArg(kernel, 7, sizeof(cl_int), &step); - device->COM_clAttachSizeToKernelParameter(kernel, 8, this); + device->COM_cl_attach_size_to_kernel_parameter(kernel, 8, this); - device->COM_clEnqueueRange(kernel, outputMemoryBuffer, 9, this); + device->COM_cl_enqueue_range(kernel, output_memory_buffer, 9, this); } -void BokehBlurOperation::updateSize() +void BokehBlurOperation::update_size() { if (sizeavailable_) { return; @@ -246,7 +246,7 @@ void BokehBlurOperation::updateSize() switch (execution_model_) { case eExecutionModel::Tiled: { float result[4]; - this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest); + this->get_input_socket_reader(3)->read_sampled(result, 0, 0, PixelSampler::Nearest); size_ = result[0]; CLAMP(size_, 0.0f, 10.0f); break; @@ -298,7 +298,7 @@ void BokehBlurOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - const float max_dim = MAX2(this->getWidth(), this->getHeight()); + const float max_dim = MAX2(this->get_width(), this->get_height()); const float add_size = size_ * max_dim / 100.0f; r_input_area.xmin = output_area.xmin - add_size; r_input_area.xmax = output_area.xmax + add_size; @@ -307,7 +307,7 @@ void BokehBlurOperation::get_area_of_interest(const int input_idx, break; } case BOKEH_INPUT_INDEX: { - NodeOperation *bokeh_input = getInputOperation(BOKEH_INPUT_INDEX); + NodeOperation *bokeh_input = get_input_operation(BOKEH_INPUT_INDEX); r_input_area = bokeh_input->get_canvas(); break; } @@ -325,7 +325,7 @@ void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float max_dim = MAX2(this->getWidth(), this->getHeight()); + const float max_dim = MAX2(this->get_width(), this->get_height()); const int pixel_size = size_ * max_dim / 100.0f; const float m = bokehDimension_ / pixel_size; @@ -356,15 +356,15 @@ void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, const int maxy = MIN2(y + pixel_size, image_rect.ymax); const int minx = MAX2(x - pixel_size, image_rect.xmin); const int maxx = MIN2(x + pixel_size, image_rect.xmax); - const int step = getStep(); + const int step = get_step(); const int elem_stride = image_input->elem_stride * step; const int row_stride = image_input->row_stride * step; const float *row_color = image_input->get_elem(minx, miny); for (int ny = miny; ny < maxy; ny += step, row_color += row_stride) { const float *color = row_color; - const float v = bokehMidY_ - (ny - y) * m; + const float v = bokeh_mid_y_ - (ny - y) * m; for (int nx = minx; nx < maxx; nx += step, color += elem_stride) { - const float u = bokehMidX_ - (nx - x) * m; + const float u = bokeh_mid_x_ - (nx - x) * m; float bokeh[4]; bokeh_input->read_elem_checked(u, v, bokeh); madd_v4_v4v4(color_accum, bokeh, color); diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.h b/source/blender/compositor/operations/COM_BokehBlurOperation.h index 0b70b7a714a..5f6a880f890 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.h @@ -25,15 +25,15 @@ namespace blender::compositor { class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: - SocketReader *inputProgram_; - SocketReader *inputBokehProgram_; - SocketReader *inputBoundingBoxReader_; - void updateSize(); + SocketReader *input_program_; + SocketReader *input_bokeh_program_; + SocketReader *input_bounding_box_reader_; + void update_size(); float size_; bool sizeavailable_; - float bokehMidX_; - float bokehMidY_; + float bokeh_mid_x_; + float bokeh_mid_y_; float bokehDimension_; bool extend_bounds_; @@ -42,40 +42,40 @@ class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelp void init_data() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setSize(float size) + void set_size(float size) { size_ = size; sizeavailable_ = true; } - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; - void setExtendBounds(bool extend_bounds) + void set_extend_bounds(bool extend_bounds) { extend_bounds_ = extend_bounds; } diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc index 5389fa633b0..9e7c1162052 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.cc +++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc @@ -22,91 +22,91 @@ namespace blender::compositor { BokehImageOperation::BokehImageOperation() { - this->addOutputSocket(DataType::Color); - deleteData_ = false; + this->add_output_socket(DataType::Color); + delete_data_ = false; } -void BokehImageOperation::initExecution() +void BokehImageOperation::init_execution() { - center_[0] = getWidth() / 2; - center_[1] = getHeight() / 2; - inverseRounding_ = 1.0f - data_->rounding; - circularDistance_ = getWidth() / 2; - flapRad_ = (float)(M_PI * 2) / data_->flaps; - flapRadAdd_ = data_->angle; - while (flapRadAdd_ < 0.0f) { - flapRadAdd_ += (float)(M_PI * 2.0); + center_[0] = get_width() / 2; + center_[1] = get_height() / 2; + inverse_rounding_ = 1.0f - data_->rounding; + circular_distance_ = get_width() / 2; + flap_rad_ = (float)(M_PI * 2) / data_->flaps; + flap_rad_add_ = data_->angle; + while (flap_rad_add_ < 0.0f) { + flap_rad_add_ += (float)(M_PI * 2.0); } - while (flapRadAdd_ > (float)M_PI) { - flapRadAdd_ -= (float)(M_PI * 2.0); + while (flap_rad_add_ > (float)M_PI) { + flap_rad_add_ -= (float)(M_PI * 2.0); } } -void BokehImageOperation::detemineStartPointOfFlap(float r[2], int flapNumber, float distance) +void BokehImageOperation::detemine_start_point_of_flap(float r[2], int flap_number, float distance) { - r[0] = sinf(flapRad_ * flapNumber + flapRadAdd_) * distance + center_[0]; - r[1] = cosf(flapRad_ * flapNumber + flapRadAdd_) * distance + center_[1]; + r[0] = sinf(flap_rad_ * flap_number + flap_rad_add_) * distance + center_[0]; + r[1] = cosf(flap_rad_ * flap_number + flap_rad_add_) * distance + center_[1]; } -float BokehImageOperation::isInsideBokeh(float distance, float x, float y) +float BokehImageOperation::is_inside_bokeh(float distance, float x, float y) { - float insideBokeh = 0.0f; + float inside_bokeh = 0.0f; const float deltaX = x - center_[0]; const float deltaY = y - center_[1]; - float closestPoint[2]; - float lineP1[2]; - float lineP2[2]; + float closest_point[2]; + float line_p1[2]; + float line_p2[2]; float point[2]; point[0] = x; point[1] = y; - const float distanceToCenter = len_v2v2(point, center_); + const float distance_to_center = len_v2v2(point, center_); const float bearing = (atan2f(deltaX, deltaY) + (float)(M_PI * 2.0)); - int flapNumber = (int)((bearing - flapRadAdd_) / flapRad_); + int flap_number = (int)((bearing - flap_rad_add_) / flap_rad_); - detemineStartPointOfFlap(lineP1, flapNumber, distance); - detemineStartPointOfFlap(lineP2, flapNumber + 1, distance); - closest_to_line_v2(closestPoint, point, lineP1, lineP2); + detemine_start_point_of_flap(line_p1, flap_number, distance); + detemine_start_point_of_flap(line_p2, flap_number + 1, distance); + closest_to_line_v2(closest_point, point, line_p1, line_p2); - const float distanceLineToCenter = len_v2v2(center_, closestPoint); - const float distanceRoundingToCenter = inverseRounding_ * distanceLineToCenter + - data_->rounding * distance; + const float distance_line_to_center = len_v2v2(center_, closest_point); + const float distance_rounding_to_center = inverse_rounding_ * distance_line_to_center + + data_->rounding * distance; - const float catadioptricDistanceToCenter = distanceRoundingToCenter * data_->catadioptric; - if (distanceRoundingToCenter >= distanceToCenter && - catadioptricDistanceToCenter <= distanceToCenter) { - if (distanceRoundingToCenter - distanceToCenter < 1.0f) { - insideBokeh = (distanceRoundingToCenter - distanceToCenter); + const float catadioptric_distance_to_center = distance_rounding_to_center * data_->catadioptric; + if (distance_rounding_to_center >= distance_to_center && + catadioptric_distance_to_center <= distance_to_center) { + if (distance_rounding_to_center - distance_to_center < 1.0f) { + inside_bokeh = (distance_rounding_to_center - distance_to_center); } else if (data_->catadioptric != 0.0f && - distanceToCenter - catadioptricDistanceToCenter < 1.0f) { - insideBokeh = (distanceToCenter - catadioptricDistanceToCenter); + distance_to_center - catadioptric_distance_to_center < 1.0f) { + inside_bokeh = (distance_to_center - catadioptric_distance_to_center); } else { - insideBokeh = 1.0f; + inside_bokeh = 1.0f; } } - return insideBokeh; + return inside_bokeh; } -void BokehImageOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void BokehImageOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { float shift = data_->lensshift; float shift2 = shift / 2.0f; - float distance = circularDistance_; - float insideBokehMax = isInsideBokeh(distance, x, y); - float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), x, y); - float insideBokehMin = isInsideBokeh(distance - fabsf(shift * distance), x, y); + float distance = circular_distance_; + float inside_bokeh_max = is_inside_bokeh(distance, x, y); + float inside_bokeh_med = is_inside_bokeh(distance - fabsf(shift2 * distance), x, y); + float inside_bokeh_min = is_inside_bokeh(distance - fabsf(shift * distance), x, y); if (shift < 0) { - output[0] = insideBokehMax; - output[1] = insideBokehMed; - output[2] = insideBokehMin; + output[0] = inside_bokeh_max; + output[1] = inside_bokeh_med; + output[2] = inside_bokeh_min; } else { - output[0] = insideBokehMin; - output[1] = insideBokehMed; - output[2] = insideBokehMax; + output[0] = inside_bokeh_min; + output[1] = inside_bokeh_med; + output[2] = inside_bokeh_max; } - output[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f; + output[3] = (inside_bokeh_max + inside_bokeh_med + inside_bokeh_min) / 3.0f; } void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -115,28 +115,29 @@ void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output, { const float shift = data_->lensshift; const float shift2 = shift / 2.0f; - const float distance = circularDistance_; + const float distance = circular_distance_; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - const float insideBokehMax = isInsideBokeh(distance, it.x, it.y); - const float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), it.x, it.y); - const float insideBokehMin = isInsideBokeh(distance - fabsf(shift * distance), it.x, it.y); + const float inside_bokeh_max = is_inside_bokeh(distance, it.x, it.y); + const float inside_bokeh_med = is_inside_bokeh( + distance - fabsf(shift2 * distance), it.x, it.y); + const float inside_bokeh_min = is_inside_bokeh(distance - fabsf(shift * distance), it.x, it.y); if (shift < 0) { - it.out[0] = insideBokehMax; - it.out[1] = insideBokehMed; - it.out[2] = insideBokehMin; + it.out[0] = inside_bokeh_max; + it.out[1] = inside_bokeh_med; + it.out[2] = inside_bokeh_min; } else { - it.out[0] = insideBokehMin; - it.out[1] = insideBokehMed; - it.out[2] = insideBokehMax; + it.out[0] = inside_bokeh_min; + it.out[1] = inside_bokeh_med; + it.out[2] = inside_bokeh_max; } - it.out[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f; + it.out[3] = (inside_bokeh_max + inside_bokeh_med + inside_bokeh_min) / 3.0f; } } -void BokehImageOperation::deinitExecution() +void BokehImageOperation::deinit_execution() { - if (deleteData_) { + if (delete_data_) { if (data_) { delete data_; data_ = nullptr; diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h index de1c05fe360..6c7818724b3 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.h +++ b/source/blender/compositor/operations/COM_BokehImageOperation.h @@ -64,36 +64,36 @@ class BokehImageOperation : public MultiThreadedOperation { /** * \brief 1.0-rounding */ - float inverseRounding_; + float inverse_rounding_; /** * \brief distance of a full circle lens */ - float circularDistance_; + float circular_distance_; /** * \brief radius when the first flap starts */ - float flapRad_; + float flap_rad_; /** * \brief radians of a single flap */ - float flapRadAdd_; + float flap_rad_add_; /** * \brief should the data_ field by deleted when this operation is finished */ - bool deleteData_; + bool delete_data_; /** * \brief determine the coordinate of a flap corner. * * \param r: result in bokeh-image space are stored [x,y] - * \param flapNumber: the flap number to calculate + * \param flap_number: the flap number to calculate * \param distance: the lens distance is used to simulate lens shifts */ - void detemineStartPointOfFlap(float r[2], int flapNumber, float distance); + void detemine_start_point_of_flap(float r[2], int flap_number, float distance); /** * \brief Determine if a coordinate is inside the bokeh image @@ -104,7 +104,7 @@ class BokehImageOperation : public MultiThreadedOperation { * \param y: the y coordinate of the pixel to evaluate * \return float range 0..1 0 is completely outside */ - float isInsideBokeh(float distance, float x, float y); + float is_inside_bokeh(float distance, float x, float y); public: BokehImageOperation(); @@ -112,21 +112,21 @@ class BokehImageOperation : public MultiThreadedOperation { /** * \brief The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * \brief Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * \brief Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; /** * \brief determine the resolution of this operation. currently fixed at [COM_BLUR_BOKEH_PIXELS, - * COM_BLUR_BOKEH_PIXELS] \param resolution: \param preferredResolution: + * COM_BLUR_BOKEH_PIXELS] \param resolution: \param preferred_resolution: */ void determine_canvas(const rcti &preferred_area, rcti &r_area) override; @@ -134,21 +134,21 @@ class BokehImageOperation : public MultiThreadedOperation { * \brief set the node data * \param data: */ - void setData(NodeBokehImage *data) + void set_data(NodeBokehImage *data) { data_ = data; } /** - * \brief deleteDataOnFinish + * \brief delete_data_on_finish * * There are cases that the compositor uses this operation on its own (see defocus node) - * the deleteDataOnFinish must only be called when the data has been created by the compositor. - *It should not be called when the data has been created by the node-editor/user. + * the delete_data_on_finish must only be called when the data has been created by the + *compositor. It should not be called when the data has been created by the node-editor/user. */ - void deleteDataOnFinish() + void delete_data_on_finish() { - deleteData_ = true; + delete_data_ = true; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.cc b/source/blender/compositor/operations/COM_BoxMaskOperation.cc index 7263e6a1a59..adfe7fcd87a 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.cc +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.cc @@ -22,66 +22,69 @@ namespace blender::compositor { BoxMaskOperation::BoxMaskOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - inputMask_ = nullptr; - inputValue_ = nullptr; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_mask_ = nullptr; + input_value_ = nullptr; cosine_ = 0.0f; sine_ = 0.0f; } -void BoxMaskOperation::initExecution() +void BoxMaskOperation::init_execution() { - inputMask_ = this->getInputSocketReader(0); - inputValue_ = this->getInputSocketReader(1); + input_mask_ = this->get_input_socket_reader(0); + input_value_ = this->get_input_socket_reader(1); const double rad = (double)data_->rotation; cosine_ = cos(rad); sine_ = sin(rad); - aspectRatio_ = ((float)this->getWidth()) / this->getHeight(); + aspect_ratio_ = ((float)this->get_width()) / this->get_height(); } -void BoxMaskOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void BoxMaskOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputMask[4]; - float inputValue[4]; + float input_mask[4]; + float input_value[4]; - float rx = x / this->getWidth(); - float ry = y / this->getHeight(); + float rx = x / this->get_width(); + float ry = y / this->get_height(); - const float dy = (ry - data_->y) / aspectRatio_; + const float dy = (ry - data_->y) / aspect_ratio_; const float dx = rx - data_->x; rx = data_->x + (cosine_ * dx + sine_ * dy); ry = data_->y + (-sine_ * dx + cosine_ * dy); - inputMask_->readSampled(inputMask, x, y, sampler); - inputValue_->readSampled(inputValue, x, y, sampler); + input_mask_->read_sampled(input_mask, x, y, sampler); + input_value_->read_sampled(input_value, x, y, sampler); - float halfHeight = data_->height / 2.0f; - float halfWidth = data_->width / 2.0f; - bool inside = (rx > data_->x - halfWidth && rx < data_->x + halfWidth && - ry > data_->y - halfHeight && ry < data_->y + halfHeight); + float half_height = data_->height / 2.0f; + float half_width = data_->width / 2.0f; + bool inside = (rx > data_->x - half_width && rx < data_->x + half_width && + ry > data_->y - half_height && ry < data_->y + half_height); - switch (maskType_) { + switch (mask_type_) { case CMP_NODE_MASKTYPE_ADD: if (inside) { - output[0] = MAX2(inputMask[0], inputValue[0]); + output[0] = MAX2(input_mask[0], input_value[0]); } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; case CMP_NODE_MASKTYPE_SUBTRACT: if (inside) { - output[0] = inputMask[0] - inputValue[0]; + output[0] = input_mask[0] - input_value[0]; CLAMP(output[0], 0, 1); } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; case CMP_NODE_MASKTYPE_MULTIPLY: if (inside) { - output[0] = inputMask[0] * inputValue[0]; + output[0] = input_mask[0] * input_value[0]; } else { output[0] = 0; @@ -89,15 +92,15 @@ void BoxMaskOperation::executePixelSampled(float output[4], float x, float y, Pi break; case CMP_NODE_MASKTYPE_NOT: if (inside) { - if (inputMask[0] > 0.0f) { + if (input_mask[0] > 0.0f) { output[0] = 0; } else { - output[0] = inputValue[0]; + output[0] = input_value[0]; } } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; } @@ -108,7 +111,7 @@ void BoxMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { MaskFunc mask_func; - switch (maskType_) { + switch (mask_type_) { case CMP_NODE_MASKTYPE_ADD: mask_func = [](const bool is_inside, const float *mask, const float *value) { return is_inside ? MAX2(mask[0], value[0]) : mask[0]; @@ -141,13 +144,13 @@ void BoxMaskOperation::apply_mask(MemoryBuffer *output, Span inputs, MaskFunc mask_func) { - const float op_w = this->getWidth(); - const float op_h = this->getHeight(); + const float op_w = this->get_width(); + const float op_h = this->get_height(); const float half_w = data_->width / 2.0f; const float half_h = data_->height / 2.0f; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float op_ry = it.y / op_h; - const float dy = (op_ry - data_->y) / aspectRatio_; + const float dy = (op_ry - data_->y) / aspect_ratio_; const float op_rx = it.x / op_w; const float dx = op_rx - data_->x; const float rx = data_->x + (cosine_ * dx + sine_ * dy); @@ -161,10 +164,10 @@ void BoxMaskOperation::apply_mask(MemoryBuffer *output, } } -void BoxMaskOperation::deinitExecution() +void BoxMaskOperation::deinit_execution() { - inputMask_ = nullptr; - inputValue_ = nullptr; + input_mask_ = nullptr; + input_value_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.h b/source/blender/compositor/operations/COM_BoxMaskOperation.h index 458cdf7ee16..4aee88556ee 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.h +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.h @@ -27,15 +27,15 @@ class BoxMaskOperation : public MultiThreadedOperation { using MaskFunc = std::function; /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputMask_; - SocketReader *inputValue_; + SocketReader *input_mask_; + SocketReader *input_value_; float sine_; float cosine_; - float aspectRatio_; - int maskType_; + float aspect_ratio_; + int mask_type_; NodeBoxMask *data_; @@ -45,26 +45,26 @@ class BoxMaskOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setData(NodeBoxMask *data) + void set_data(NodeBoxMask *data) { data_ = data; } - void setMaskType(int maskType) + void set_mask_type(int mask_type) { - maskType_ = maskType; + mask_type_ = mask_type; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc index 77f30331767..a445bb122c2 100644 --- a/source/blender/compositor/operations/COM_BrightnessOperation.cc +++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc @@ -22,41 +22,41 @@ namespace blender::compositor { BrightnessOperation::BrightnessOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - inputProgram_ = nullptr; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; use_premultiply_ = false; flags.can_be_constant = true; } -void BrightnessOperation::setUsePremultiply(bool use_premultiply) +void BrightnessOperation::set_use_premultiply(bool use_premultiply) { use_premultiply_ = use_premultiply; } -void BrightnessOperation::initExecution() +void BrightnessOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); - inputBrightnessProgram_ = this->getInputSocketReader(1); - inputContrastProgram_ = this->getInputSocketReader(2); + input_program_ = this->get_input_socket_reader(0); + input_brightness_program_ = this->get_input_socket_reader(1); + input_contrast_program_ = this->get_input_socket_reader(2); } -void BrightnessOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void BrightnessOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue[4]; + float input_value[4]; float a, b; - float inputBrightness[4]; - float inputContrast[4]; - inputProgram_->readSampled(inputValue, x, y, sampler); - inputBrightnessProgram_->readSampled(inputBrightness, x, y, sampler); - inputContrastProgram_->readSampled(inputContrast, x, y, sampler); - float brightness = inputBrightness[0]; - float contrast = inputContrast[0]; + float input_brightness[4]; + float input_contrast[4]; + input_program_->read_sampled(input_value, x, y, sampler); + input_brightness_program_->read_sampled(input_brightness, x, y, sampler); + input_contrast_program_->read_sampled(input_contrast, x, y, sampler); + float brightness = input_brightness[0]; + float contrast = input_contrast[0]; brightness /= 100.0f; float delta = contrast / 200.0f; /* @@ -75,12 +75,12 @@ void BrightnessOperation::executePixelSampled(float output[4], b = a * brightness + delta; } if (use_premultiply_) { - premul_to_straight_v4(inputValue); + premul_to_straight_v4(input_value); } - output[0] = a * inputValue[0] + b; - output[1] = a * inputValue[1] + b; - output[2] = a * inputValue[2] + b; - output[3] = inputValue[3]; + output[0] = a * input_value[0] + b; + output[1] = a * input_value[1] + b; + output[2] = a * input_value[2] + b; + output[3] = input_value[3]; if (use_premultiply_) { straight_to_premul_v4(output); } @@ -130,11 +130,11 @@ void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void BrightnessOperation::deinitExecution() +void BrightnessOperation::deinit_execution() { - inputProgram_ = nullptr; - inputBrightnessProgram_ = nullptr; - inputContrastProgram_ = nullptr; + input_program_ = nullptr; + input_brightness_program_ = nullptr; + input_contrast_program_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.h b/source/blender/compositor/operations/COM_BrightnessOperation.h index 0ecb8473319..003baa96613 100644 --- a/source/blender/compositor/operations/COM_BrightnessOperation.h +++ b/source/blender/compositor/operations/COM_BrightnessOperation.h @@ -25,11 +25,11 @@ namespace blender::compositor { class BrightnessOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; - SocketReader *inputBrightnessProgram_; - SocketReader *inputContrastProgram_; + SocketReader *input_program_; + SocketReader *input_brightness_program_; + SocketReader *input_contrast_program_; bool use_premultiply_; @@ -39,19 +39,19 @@ class BrightnessOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setUsePremultiply(bool use_premultiply); + void set_use_premultiply(bool use_premultiply); void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc index ce2351cf759..f759ba5ccef 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc @@ -26,67 +26,66 @@ namespace blender::compositor { CalculateMeanOperation::CalculateMeanOperation() { - this->addInputSocket(DataType::Color, ResizeMode::Align); - this->addOutputSocket(DataType::Value); - imageReader_ = nullptr; + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_output_socket(DataType::Value); + image_reader_ = nullptr; iscalculated_ = false; setting_ = 1; this->flags.complex = true; } -void CalculateMeanOperation::initExecution() +void CalculateMeanOperation::init_execution() { - imageReader_ = this->getInputSocketReader(0); + image_reader_ = this->get_input_socket_reader(0); iscalculated_ = false; - NodeOperation::initMutex(); + NodeOperation::init_mutex(); } -void CalculateMeanOperation::executePixel(float output[4], int /*x*/, int /*y*/, void * /*data*/) +void CalculateMeanOperation::execute_pixel(float output[4], int /*x*/, int /*y*/, void * /*data*/) { output[0] = result_; } -void CalculateMeanOperation::deinitExecution() +void CalculateMeanOperation::deinit_execution() { - imageReader_ = nullptr; - NodeOperation::deinitMutex(); + image_reader_ = nullptr; + NodeOperation::deinit_mutex(); } -bool CalculateMeanOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool CalculateMeanOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - rcti imageInput; + rcti image_input; if (iscalculated_) { return false; } - NodeOperation *operation = getInputOperation(0); - imageInput.xmax = operation->getWidth(); - imageInput.xmin = 0; - imageInput.ymax = operation->getHeight(); - imageInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&imageInput, readOperation, output)) { + NodeOperation *operation = get_input_operation(0); + image_input.xmax = operation->get_width(); + image_input.xmin = 0; + image_input.ymax = operation->get_height(); + image_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&image_input, read_operation, output)) { return true; } return false; } -void *CalculateMeanOperation::initializeTileData(rcti *rect) +void *CalculateMeanOperation::initialize_tile_data(rcti *rect) { - lockMutex(); + lock_mutex(); if (!iscalculated_) { - MemoryBuffer *tile = (MemoryBuffer *)imageReader_->initializeTileData(rect); - calculateMean(tile); + MemoryBuffer *tile = (MemoryBuffer *)image_reader_->initialize_tile_data(rect); + calculate_mean(tile); iscalculated_ = true; } - unlockMutex(); + unlock_mutex(); return nullptr; } -void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) +void CalculateMeanOperation::calculate_mean(MemoryBuffer *tile) { result_ = 0.0f; - float *buffer = tile->getBuffer(); - int size = tile->getWidth() * tile->getHeight(); + float *buffer = tile->get_buffer(); + int size = tile->get_width() * tile->get_height(); int pixels = 0; float sum = 0.0f; for (int i = 0, offset = 0; i < size; i++, offset += 4) { @@ -128,7 +127,7 @@ void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) result_ = sum / pixels; } -void CalculateMeanOperation::setSetting(int setting) +void CalculateMeanOperation::set_setting(int setting) { setting_ = setting; switch (setting) { diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.h b/source/blender/compositor/operations/COM_CalculateMeanOperation.h index a34bab45211..7c4fafb2f27 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.h +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.h @@ -39,7 +39,7 @@ class CalculateMeanOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *imageReader_; + SocketReader *image_reader_; bool iscalculated_; float result_; @@ -52,24 +52,24 @@ class CalculateMeanOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void setSetting(int setting); + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void set_setting(int setting); void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; @@ -82,7 +82,7 @@ class CalculateMeanOperation : public MultiThreadedOperation { Span inputs) override; protected: - void calculateMean(MemoryBuffer *tile); + void calculate_mean(MemoryBuffer *tile); float calc_mean(const MemoryBuffer *input); private: diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc index 3c8d7f21801..06b3114845e 100644 --- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc @@ -24,23 +24,23 @@ namespace blender::compositor { -void CalculateStandardDeviationOperation::executePixel(float output[4], - int /*x*/, - int /*y*/, - void * /*data*/) +void CalculateStandardDeviationOperation::execute_pixel(float output[4], + int /*x*/, + int /*y*/, + void * /*data*/) { - output[0] = standardDeviation_; + output[0] = standard_deviation_; } -void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) +void *CalculateStandardDeviationOperation::initialize_tile_data(rcti *rect) { - lockMutex(); + lock_mutex(); if (!iscalculated_) { - MemoryBuffer *tile = (MemoryBuffer *)imageReader_->initializeTileData(rect); - CalculateMeanOperation::calculateMean(tile); - standardDeviation_ = 0.0f; - float *buffer = tile->getBuffer(); - int size = tile->getWidth() * tile->getHeight(); + MemoryBuffer *tile = (MemoryBuffer *)image_reader_->initialize_tile_data(rect); + CalculateMeanOperation::calculate_mean(tile); + standard_deviation_ = 0.0f; + float *buffer = tile->get_buffer(); + int size = tile->get_width() * tile->get_height(); int pixels = 0; float sum = 0.0f; float mean = result_; @@ -89,10 +89,10 @@ void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) } } } - standardDeviation_ = sqrt(sum / (float)(pixels - 1)); + standard_deviation_ = sqrt(sum / (float)(pixels - 1)); iscalculated_ = true; } - unlockMutex(); + unlock_mutex(); return nullptr; } @@ -112,8 +112,8 @@ void CalculateStandardDeviationOperation::update_memory_buffer_started( join.sum += chunk.sum; join.num_pixels += chunk.num_pixels; }); - standardDeviation_ = total.num_pixels <= 1 ? 0.0f : - sqrt(total.sum / (float)(total.num_pixels - 1)); + standard_deviation_ = total.num_pixels <= 1 ? 0.0f : + sqrt(total.sum / (float)(total.num_pixels - 1)); iscalculated_ = true; } } @@ -121,7 +121,7 @@ void CalculateStandardDeviationOperation::update_memory_buffer_started( void CalculateStandardDeviationOperation::update_memory_buffer_partial( MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->fill(area, &standardDeviation_); + output->fill(area, &standard_deviation_); } using PixelsSum = CalculateMeanOperation::PixelsSum; diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h index 29d9c45c986..95b9eb21982 100644 --- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h +++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h @@ -31,15 +31,15 @@ namespace blender::compositor { */ class CalculateStandardDeviationOperation : public CalculateMeanOperation { protected: - float standardDeviation_; + float standard_deviation_; public: /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; void update_memory_buffer_started(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc index 7e240a0b95a..b4effc087b1 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc @@ -22,54 +22,54 @@ namespace blender::compositor { ChangeHSVOperation::ChangeHSVOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - inputOperation_ = nullptr; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_operation_ = nullptr; this->flags.can_be_constant = true; } -void ChangeHSVOperation::initExecution() +void ChangeHSVOperation::init_execution() { - inputOperation_ = getInputSocketReader(0); - hueOperation_ = getInputSocketReader(1); - saturationOperation_ = getInputSocketReader(2); - valueOperation_ = getInputSocketReader(3); + input_operation_ = get_input_socket_reader(0); + hue_operation_ = get_input_socket_reader(1); + saturation_operation_ = get_input_socket_reader(2); + value_operation_ = get_input_socket_reader(3); } -void ChangeHSVOperation::deinitExecution() +void ChangeHSVOperation::deinit_execution() { - inputOperation_ = nullptr; - hueOperation_ = nullptr; - saturationOperation_ = nullptr; - valueOperation_ = nullptr; + input_operation_ = nullptr; + hue_operation_ = nullptr; + saturation_operation_ = nullptr; + value_operation_ = nullptr; } -void ChangeHSVOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ChangeHSVOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; + float input_color1[4]; float hue[4], saturation[4], value[4]; - inputOperation_->readSampled(inputColor1, x, y, sampler); - hueOperation_->readSampled(hue, x, y, sampler); - saturationOperation_->readSampled(saturation, x, y, sampler); - valueOperation_->readSampled(value, x, y, sampler); + input_operation_->read_sampled(input_color1, x, y, sampler); + hue_operation_->read_sampled(hue, x, y, sampler); + saturation_operation_->read_sampled(saturation, x, y, sampler); + value_operation_->read_sampled(value, x, y, sampler); - output[0] = inputColor1[0] + (hue[0] - 0.5f); + output[0] = input_color1[0] + (hue[0] - 0.5f); if (output[0] > 1.0f) { output[0] -= 1.0f; } else if (output[0] < 0.0f) { output[0] += 1.0f; } - output[1] = inputColor1[1] * saturation[0]; - output[2] = inputColor1[2] * value[0]; - output[3] = inputColor1[3]; + output[1] = input_color1[1] * saturation[0]; + output[2] = input_color1[2] * value[0]; + output[3] = input_color1[3]; } void ChangeHSVOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.h b/source/blender/compositor/operations/COM_ChangeHSVOperation.h index 4b810a93816..35e606f63df 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.h +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.h @@ -28,10 +28,10 @@ namespace blender::compositor { */ class ChangeHSVOperation : public MultiThreadedOperation { private: - SocketReader *inputOperation_; - SocketReader *hueOperation_; - SocketReader *saturationOperation_; - SocketReader *valueOperation_; + SocketReader *input_operation_; + SocketReader *hue_operation_; + SocketReader *saturation_operation_; + SocketReader *value_operation_; public: /** @@ -39,13 +39,13 @@ class ChangeHSVOperation : public MultiThreadedOperation { */ ChangeHSVOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc index cbc6d078ac1..0a6b8255c42 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc @@ -22,16 +22,16 @@ namespace blender::compositor { ChannelMatteOperation::ChannelMatteOperation() { - addInputSocket(DataType::Color); - addOutputSocket(DataType::Value); + add_input_socket(DataType::Color); + add_output_socket(DataType::Value); - inputImageProgram_ = nullptr; + input_image_program_ = nullptr; flags.can_be_constant = true; } -void ChannelMatteOperation::initExecution() +void ChannelMatteOperation::init_execution() { - inputImageProgram_ = this->getInputSocketReader(0); + input_image_program_ = this->get_input_socket_reader(0); limit_range_ = limit_max_ - limit_min_; @@ -77,34 +77,34 @@ void ChannelMatteOperation::initExecution() } } -void ChannelMatteOperation::deinitExecution() +void ChannelMatteOperation::deinit_execution() { - inputImageProgram_ = nullptr; + input_image_program_ = nullptr; } -void ChannelMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ChannelMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inColor[4]; + float in_color[4]; float alpha; const float limit_max = limit_max_; const float limit_min = limit_min_; const float limit_range = limit_range_; - inputImageProgram_->readSampled(inColor, x, y, sampler); + input_image_program_->read_sampled(in_color, x, y, sampler); /* matte operation */ - alpha = inColor[ids_[0]] - MAX2(inColor[ids_[1]], inColor[ids_[2]]); + alpha = in_color[ids_[0]] - MAX2(in_color[ids_[1]], in_color[ids_[2]]); /* flip because 0.0 is transparent, not 1.0 */ alpha = 1.0f - alpha; /* test range */ if (alpha > limit_max) { - alpha = inColor[3]; /* Whatever it was prior. */ + alpha = in_color[3]; /* Whatever it was prior. */ } else if (alpha < limit_min) { alpha = 0.0f; @@ -118,7 +118,7 @@ void ChannelMatteOperation::executePixelSampled(float output[4], */ /* Don't make something that was more transparent less transparent. */ - output[0] = MIN2(alpha, inColor[3]); + output[0] = MIN2(alpha, in_color[3]); } void ChannelMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.h b/source/blender/compositor/operations/COM_ChannelMatteOperation.h index f7bf58b99c5..64b173885d5 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.h +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.h @@ -28,7 +28,7 @@ namespace blender::compositor { */ class ChannelMatteOperation : public MultiThreadedOperation { private: - SocketReader *inputImageProgram_; + SocketReader *input_image_program_; /* int color_space_; */ /* node->custom1 */ /* UNUSED */ /* TODO ? */ int matte_channel_; /* node->custom2 */ @@ -58,17 +58,17 @@ class ChannelMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma, const int custom2) + void set_settings(NodeChroma *node_chroma, const int custom2) { - limit_max_ = nodeChroma->t1; - limit_min_ = nodeChroma->t2; - limit_method_ = nodeChroma->algorithm; - limit_channel_ = nodeChroma->channel; + limit_max_ = node_chroma->t1; + limit_min_ = node_chroma->t2; + limit_method_ = node_chroma->algorithm; + limit_channel_ = node_chroma->channel; matte_channel_ = custom2; } diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc index 172460e9203..fceb0ea1c23 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc @@ -22,34 +22,34 @@ namespace blender::compositor { ChromaMatteOperation::ChromaMatteOperation() { - addInputSocket(DataType::Color); - addInputSocket(DataType::Color); - addOutputSocket(DataType::Value); + add_input_socket(DataType::Color); + add_input_socket(DataType::Color); + add_output_socket(DataType::Value); - inputImageProgram_ = nullptr; - inputKeyProgram_ = nullptr; + input_image_program_ = nullptr; + input_key_program_ = nullptr; flags.can_be_constant = true; } -void ChromaMatteOperation::initExecution() +void ChromaMatteOperation::init_execution() { - inputImageProgram_ = this->getInputSocketReader(0); - inputKeyProgram_ = this->getInputSocketReader(1); + input_image_program_ = this->get_input_socket_reader(0); + input_key_program_ = this->get_input_socket_reader(1); } -void ChromaMatteOperation::deinitExecution() +void ChromaMatteOperation::deinit_execution() { - inputImageProgram_ = nullptr; - inputKeyProgram_ = nullptr; + input_image_program_ = nullptr; + input_key_program_ = nullptr; } -void ChromaMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ChromaMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inKey[4]; - float inImage[4]; + float in_key[4]; + float in_image[4]; const float acceptance = settings_->t1; /* in radians */ const float cutoff = settings_->t2; /* in radians */ @@ -59,8 +59,8 @@ void ChromaMatteOperation::executePixelSampled(float output[4], float theta, beta; float kfg; - inputKeyProgram_->readSampled(inKey, x, y, sampler); - inputImageProgram_->readSampled(inImage, x, y, sampler); + input_key_program_->read_sampled(in_key, x, y, sampler); + input_image_program_->read_sampled(in_image, x, y, sampler); /* Store matte(alpha) value in [0] to go with * #COM_SetAlphaMultiplyOperation and the Value output. */ @@ -69,19 +69,19 @@ void ChromaMatteOperation::executePixelSampled(float output[4], /* Find theta, the angle that the color space should be rotated based on key. */ /* rescale to -1.0..1.0 */ - // inImage[0] = (inImage[0] * 2.0f) - 1.0f; // UNUSED - inImage[1] = (inImage[1] * 2.0f) - 1.0f; - inImage[2] = (inImage[2] * 2.0f) - 1.0f; + // in_image[0] = (in_image[0] * 2.0f) - 1.0f; // UNUSED + in_image[1] = (in_image[1] * 2.0f) - 1.0f; + in_image[2] = (in_image[2] * 2.0f) - 1.0f; - // inKey[0] = (inKey[0] * 2.0f) - 1.0f; // UNUSED - inKey[1] = (inKey[1] * 2.0f) - 1.0f; - inKey[2] = (inKey[2] * 2.0f) - 1.0f; + // in_key[0] = (in_key[0] * 2.0f) - 1.0f; // UNUSED + in_key[1] = (in_key[1] * 2.0f) - 1.0f; + in_key[2] = (in_key[2] * 2.0f) - 1.0f; - theta = atan2(inKey[2], inKey[1]); + theta = atan2(in_key[2], in_key[1]); /* Rotate the cb and cr into x/z space. */ - x_angle = inImage[1] * cosf(theta) + inImage[2] * sinf(theta); - z_angle = inImage[2] * cosf(theta) - inImage[1] * sinf(theta); + x_angle = in_image[1] * cosf(theta) + in_image[2] * sinf(theta); + z_angle = in_image[2] * cosf(theta) - in_image[1] * sinf(theta); /* If within the acceptance angle. */ /* If kfg is <0 then the pixel is outside of the key color. */ @@ -98,15 +98,15 @@ void ChromaMatteOperation::executePixelSampled(float output[4], } /* don't make something that was more transparent less transparent */ - if (alpha < inImage[3]) { + if (alpha < in_image[3]) { output[0] = alpha; } else { - output[0] = inImage[3]; + output[0] = in_image[3]; } } - else { /* Pixel is outside key color. */ - output[0] = inImage[3]; /* Make pixel just as transparent as it was before. */ + else { /* Pixel is outside key color. */ + output[0] = in_image[3]; /* Make pixel just as transparent as it was before. */ } } diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.h b/source/blender/compositor/operations/COM_ChromaMatteOperation.h index 913d0f27107..954daef1be7 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.h +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.h @@ -29,8 +29,8 @@ namespace blender::compositor { class ChromaMatteOperation : public MultiThreadedOperation { private: NodeChroma *settings_; - SocketReader *inputImageProgram_; - SocketReader *inputKeyProgram_; + SocketReader *input_image_program_; + SocketReader *input_key_program_; public: /** @@ -41,14 +41,14 @@ class ChromaMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma) + void set_settings(NodeChroma *node_chroma) { - settings_ = nodeChroma; + settings_ = node_chroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc index b6253c07f12..e12138c1f00 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc @@ -34,43 +34,43 @@ inline float colorbalance_cdl(float in, float offset, float power, float slope) ColorBalanceASCCDLOperation::ColorBalanceASCCDLOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - inputValueOperation_ = nullptr; - inputColorOperation_ = nullptr; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_value_operation_ = nullptr; + input_color_operation_ = nullptr; this->set_canvas_input_index(1); flags.can_be_constant = true; } -void ColorBalanceASCCDLOperation::initExecution() +void ColorBalanceASCCDLOperation::init_execution() { - inputValueOperation_ = this->getInputSocketReader(0); - inputColorOperation_ = this->getInputSocketReader(1); + input_value_operation_ = this->get_input_socket_reader(0); + input_color_operation_ = this->get_input_socket_reader(1); } -void ColorBalanceASCCDLOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorBalanceASCCDLOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; + float input_color[4]; float value[4]; - inputValueOperation_->readSampled(value, x, y, sampler); - inputColorOperation_->readSampled(inputColor, x, y, sampler); + input_value_operation_->read_sampled(value, x, y, sampler); + input_color_operation_->read_sampled(input_color, x, y, sampler); float fac = value[0]; fac = MIN2(1.0f, fac); const float mfac = 1.0f - fac; - output[0] = mfac * inputColor[0] + - fac * colorbalance_cdl(inputColor[0], offset_[0], power_[0], slope_[0]); - output[1] = mfac * inputColor[1] + - fac * colorbalance_cdl(inputColor[1], offset_[1], power_[1], slope_[1]); - output[2] = mfac * inputColor[2] + - fac * colorbalance_cdl(inputColor[2], offset_[2], power_[2], slope_[2]); - output[3] = inputColor[3]; + output[0] = mfac * input_color[0] + + fac * colorbalance_cdl(input_color[0], offset_[0], power_[0], slope_[0]); + output[1] = mfac * input_color[1] + + fac * colorbalance_cdl(input_color[1], offset_[1], power_[1], slope_[1]); + output[2] = mfac * input_color[2] + + fac * colorbalance_cdl(input_color[2], offset_[2], power_[2], slope_[2]); + output[3] = input_color[3]; } void ColorBalanceASCCDLOperation::update_memory_buffer_row(PixelCursor &p) @@ -90,10 +90,10 @@ void ColorBalanceASCCDLOperation::update_memory_buffer_row(PixelCursor &p) } } -void ColorBalanceASCCDLOperation::deinitExecution() +void ColorBalanceASCCDLOperation::deinit_execution() { - inputValueOperation_ = nullptr; - inputColorOperation_ = nullptr; + input_value_operation_ = nullptr; + input_color_operation_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h index 79c5cc1a047..64622e399d3 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h @@ -29,10 +29,10 @@ namespace blender::compositor { class ColorBalanceASCCDLOperation : public MultiThreadedRowOperation { protected: /** - * Prefetched reference to the inputProgram + * Prefetched reference to the input_program */ - SocketReader *inputValueOperation_; - SocketReader *inputColorOperation_; + SocketReader *input_value_operation_; + SocketReader *input_color_operation_; float offset_[3]; float power_[3]; @@ -47,27 +47,27 @@ class ColorBalanceASCCDLOperation : public MultiThreadedRowOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setOffset(float offset[3]) + void set_offset(float offset[3]) { copy_v3_v3(offset_, offset); } - void setPower(float power[3]) + void set_power(float power[3]) { copy_v3_v3(power_, power); } - void setSlope(float slope[3]) + void set_slope(float slope[3]) { copy_v3_v3(slope_, slope); } diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc index d7c7368a214..d9af1b0a074 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc @@ -39,43 +39,43 @@ inline float colorbalance_lgg(float in, float lift_lgg, float gamma_inv, float g ColorBalanceLGGOperation::ColorBalanceLGGOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - inputValueOperation_ = nullptr; - inputColorOperation_ = nullptr; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_value_operation_ = nullptr; + input_color_operation_ = nullptr; this->set_canvas_input_index(1); flags.can_be_constant = true; } -void ColorBalanceLGGOperation::initExecution() +void ColorBalanceLGGOperation::init_execution() { - inputValueOperation_ = this->getInputSocketReader(0); - inputColorOperation_ = this->getInputSocketReader(1); + input_value_operation_ = this->get_input_socket_reader(0); + input_color_operation_ = this->get_input_socket_reader(1); } -void ColorBalanceLGGOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorBalanceLGGOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; + float input_color[4]; float value[4]; - inputValueOperation_->readSampled(value, x, y, sampler); - inputColorOperation_->readSampled(inputColor, x, y, sampler); + input_value_operation_->read_sampled(value, x, y, sampler); + input_color_operation_->read_sampled(input_color, x, y, sampler); float fac = value[0]; fac = MIN2(1.0f, fac); const float mfac = 1.0f - fac; - output[0] = mfac * inputColor[0] + - fac * colorbalance_lgg(inputColor[0], lift_[0], gamma_inv_[0], gain_[0]); - output[1] = mfac * inputColor[1] + - fac * colorbalance_lgg(inputColor[1], lift_[1], gamma_inv_[1], gain_[1]); - output[2] = mfac * inputColor[2] + - fac * colorbalance_lgg(inputColor[2], lift_[2], gamma_inv_[2], gain_[2]); - output[3] = inputColor[3]; + output[0] = mfac * input_color[0] + + fac * colorbalance_lgg(input_color[0], lift_[0], gamma_inv_[0], gain_[0]); + output[1] = mfac * input_color[1] + + fac * colorbalance_lgg(input_color[1], lift_[1], gamma_inv_[1], gain_[1]); + output[2] = mfac * input_color[2] + + fac * colorbalance_lgg(input_color[2], lift_[2], gamma_inv_[2], gain_[2]); + output[3] = input_color[3]; } void ColorBalanceLGGOperation::update_memory_buffer_row(PixelCursor &p) @@ -95,10 +95,10 @@ void ColorBalanceLGGOperation::update_memory_buffer_row(PixelCursor &p) } } -void ColorBalanceLGGOperation::deinitExecution() +void ColorBalanceLGGOperation::deinit_execution() { - inputValueOperation_ = nullptr; - inputColorOperation_ = nullptr; + input_value_operation_ = nullptr; + input_color_operation_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h index 9393f635f9e..738669e71f9 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h @@ -29,10 +29,10 @@ namespace blender::compositor { class ColorBalanceLGGOperation : public MultiThreadedRowOperation { protected: /** - * Prefetched reference to the inputProgram + * Prefetched reference to the input_program */ - SocketReader *inputValueOperation_; - SocketReader *inputColorOperation_; + SocketReader *input_value_operation_; + SocketReader *input_color_operation_; float gain_[3]; float lift_[3]; @@ -47,27 +47,27 @@ class ColorBalanceLGGOperation : public MultiThreadedRowOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setGain(const float gain[3]) + void set_gain(const float gain[3]) { copy_v3_v3(gain_, gain); } - void setLift(const float lift[3]) + void set_lift(const float lift[3]) { copy_v3_v3(lift_, lift); } - void setGammaInv(const float gamma_inv[3]) + void set_gamma_inv(const float gamma_inv[3]) { copy_v3_v3(gamma_inv_, gamma_inv); } diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc index 128c7c80ea9..538227d899b 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc @@ -24,20 +24,20 @@ namespace blender::compositor { ColorCorrectionOperation::ColorCorrectionOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - inputImage_ = nullptr; - inputMask_ = nullptr; - redChannelEnabled_ = true; - greenChannelEnabled_ = true; - blueChannelEnabled_ = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_image_ = nullptr; + input_mask_ = nullptr; + red_channel_enabled_ = true; + green_channel_enabled_ = true; + blue_channel_enabled_ = true; flags.can_be_constant = true; } -void ColorCorrectionOperation::initExecution() +void ColorCorrectionOperation::init_execution() { - inputImage_ = this->getInputSocketReader(0); - inputMask_ = this->getInputSocketReader(1); + input_image_ = this->get_input_socket_reader(0); + input_mask_ = this->get_input_socket_reader(1); } /* Calculate x^y if the function is defined. Otherwise return the given fallback value. */ @@ -49,17 +49,17 @@ BLI_INLINE float color_correct_powf_safe(const float x, const float y, const flo return powf(x, y); } -void ColorCorrectionOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorCorrectionOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputImageColor[4]; - float inputMask[4]; - inputImage_->readSampled(inputImageColor, x, y, sampler); - inputMask_->readSampled(inputMask, x, y, sampler); + float input_image_color[4]; + float input_mask[4]; + input_image_->read_sampled(input_image_color, x, y, sampler); + input_mask_->read_sampled(input_mask, x, y, sampler); - float level = (inputImageColor[0] + inputImageColor[1] + inputImageColor[2]) / 3.0f; + float level = (input_image_color[0] + input_image_color[1] + input_image_color[2]) / 3.0f; float contrast = data_->master.contrast; float saturation = data_->master.saturation; float gamma = data_->master.gamma; @@ -67,53 +67,53 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], float lift = data_->master.lift; float r, g, b; - float value = inputMask[0]; + float value = input_mask[0]; value = MIN2(1.0f, value); const float mvalue = 1.0f - value; - float levelShadows = 0.0; - float levelMidtones = 0.0; - float levelHighlights = 0.0; + float level_shadows = 0.0; + float level_midtones = 0.0; + float level_highlights = 0.0; #define MARGIN 0.10f #define MARGIN_DIV (0.5f / MARGIN) if (level < data_->startmidtones - MARGIN) { - levelShadows = 1.0f; + level_shadows = 1.0f; } else if (level < data_->startmidtones + MARGIN) { - levelMidtones = ((level - data_->startmidtones) * MARGIN_DIV) + 0.5f; - levelShadows = 1.0f - levelMidtones; + level_midtones = ((level - data_->startmidtones) * MARGIN_DIV) + 0.5f; + level_shadows = 1.0f - level_midtones; } else if (level < data_->endmidtones - MARGIN) { - levelMidtones = 1.0f; + level_midtones = 1.0f; } else if (level < data_->endmidtones + MARGIN) { - levelHighlights = ((level - data_->endmidtones) * MARGIN_DIV) + 0.5f; - levelMidtones = 1.0f - levelHighlights; + level_highlights = ((level - data_->endmidtones) * MARGIN_DIV) + 0.5f; + level_midtones = 1.0f - level_highlights; } else { - levelHighlights = 1.0f; + level_highlights = 1.0f; } #undef MARGIN #undef MARGIN_DIV - contrast *= (levelShadows * data_->shadows.contrast) + - (levelMidtones * data_->midtones.contrast) + - (levelHighlights * data_->highlights.contrast); - saturation *= (levelShadows * data_->shadows.saturation) + - (levelMidtones * data_->midtones.saturation) + - (levelHighlights * data_->highlights.saturation); - gamma *= (levelShadows * data_->shadows.gamma) + (levelMidtones * data_->midtones.gamma) + - (levelHighlights * data_->highlights.gamma); - gain *= (levelShadows * data_->shadows.gain) + (levelMidtones * data_->midtones.gain) + - (levelHighlights * data_->highlights.gain); - lift += (levelShadows * data_->shadows.lift) + (levelMidtones * data_->midtones.lift) + - (levelHighlights * data_->highlights.lift); + contrast *= (level_shadows * data_->shadows.contrast) + + (level_midtones * data_->midtones.contrast) + + (level_highlights * data_->highlights.contrast); + saturation *= (level_shadows * data_->shadows.saturation) + + (level_midtones * data_->midtones.saturation) + + (level_highlights * data_->highlights.saturation); + gamma *= (level_shadows * data_->shadows.gamma) + (level_midtones * data_->midtones.gamma) + + (level_highlights * data_->highlights.gamma); + gain *= (level_shadows * data_->shadows.gain) + (level_midtones * data_->midtones.gain) + + (level_highlights * data_->highlights.gain); + lift += (level_shadows * data_->shadows.lift) + (level_midtones * data_->midtones.lift) + + (level_highlights * data_->highlights.lift); float invgamma = 1.0f / gamma; - float luma = IMB_colormanagement_get_luminance(inputImageColor); + float luma = IMB_colormanagement_get_luminance(input_image_color); - r = inputImageColor[0]; - g = inputImageColor[1]; - b = inputImageColor[2]; + r = input_image_color[0]; + g = input_image_color[1]; + b = input_image_color[2]; r = (luma + saturation * (r - luma)); g = (luma + saturation * (g - luma)); @@ -129,29 +129,29 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], b = color_correct_powf_safe(b * gain + lift, invgamma, b); /* Mix with mask. */ - r = mvalue * inputImageColor[0] + value * r; - g = mvalue * inputImageColor[1] + value * g; - b = mvalue * inputImageColor[2] + value * b; + r = mvalue * input_image_color[0] + value * r; + g = mvalue * input_image_color[1] + value * g; + b = mvalue * input_image_color[2] + value * b; - if (redChannelEnabled_) { + if (red_channel_enabled_) { output[0] = r; } else { - output[0] = inputImageColor[0]; + output[0] = input_image_color[0]; } - if (greenChannelEnabled_) { + if (green_channel_enabled_) { output[1] = g; } else { - output[1] = inputImageColor[1]; + output[1] = input_image_color[1]; } - if (blueChannelEnabled_) { + if (blue_channel_enabled_) { output[2] = b; } else { - output[2] = inputImageColor[2]; + output[2] = input_image_color[2]; } - output[3] = inputImageColor[3]; + output[3] = input_image_color[3]; } void ColorCorrectionOperation::update_memory_buffer_row(PixelCursor &p) @@ -224,17 +224,17 @@ void ColorCorrectionOperation::update_memory_buffer_row(PixelCursor &p) g = value_ * in_color[1] + value * g; b = value_ * in_color[2] + value * b; - p.out[0] = redChannelEnabled_ ? r : in_color[0]; - p.out[1] = greenChannelEnabled_ ? g : in_color[1]; - p.out[2] = blueChannelEnabled_ ? b : in_color[2]; + p.out[0] = red_channel_enabled_ ? r : in_color[0]; + p.out[1] = green_channel_enabled_ ? g : in_color[1]; + p.out[2] = blue_channel_enabled_ ? b : in_color[2]; p.out[3] = in_color[3]; } } -void ColorCorrectionOperation::deinitExecution() +void ColorCorrectionOperation::deinit_execution() { - inputImage_ = nullptr; - inputMask_ = nullptr; + input_image_ = nullptr; + input_mask_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h index 157babd299b..b0d52507204 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h @@ -25,15 +25,15 @@ namespace blender::compositor { class ColorCorrectionOperation : public MultiThreadedRowOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputImage_; - SocketReader *inputMask_; + SocketReader *input_image_; + SocketReader *input_mask_; NodeColorCorrection *data_; - bool redChannelEnabled_; - bool greenChannelEnabled_; - bool blueChannelEnabled_; + bool red_channel_enabled_; + bool green_channel_enabled_; + bool blue_channel_enabled_; public: ColorCorrectionOperation(); @@ -41,33 +41,33 @@ class ColorCorrectionOperation : public MultiThreadedRowOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setData(NodeColorCorrection *data) + void set_data(NodeColorCorrection *data) { data_ = data; } - void setRedChannelEnabled(bool enabled) + void set_red_channel_enabled(bool enabled) { - redChannelEnabled_ = enabled; + red_channel_enabled_ = enabled; } - void setGreenChannelEnabled(bool enabled) + void set_green_channel_enabled(bool enabled) { - greenChannelEnabled_ = enabled; + green_channel_enabled_ = enabled; } - void setBlueChannelEnabled(bool enabled) + void set_blue_channel_enabled(bool enabled) { - blueChannelEnabled_ = enabled; + blue_channel_enabled_ = enabled; } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc index b88989ad224..7cfa1c09298 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc @@ -24,36 +24,36 @@ namespace blender::compositor { ColorCurveOperation::ColorCurveOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); - inputFacProgram_ = nullptr; - inputImageProgram_ = nullptr; - inputBlackProgram_ = nullptr; - inputWhiteProgram_ = nullptr; + input_fac_program_ = nullptr; + input_image_program_ = nullptr; + input_black_program_ = nullptr; + input_white_program_ = nullptr; this->set_canvas_input_index(1); } -void ColorCurveOperation::initExecution() +void ColorCurveOperation::init_execution() { - CurveBaseOperation::initExecution(); - inputFacProgram_ = this->getInputSocketReader(0); - inputImageProgram_ = this->getInputSocketReader(1); - inputBlackProgram_ = this->getInputSocketReader(2); - inputWhiteProgram_ = this->getInputSocketReader(3); + CurveBaseOperation::init_execution(); + input_fac_program_ = this->get_input_socket_reader(0); + input_image_program_ = this->get_input_socket_reader(1); + input_black_program_ = this->get_input_socket_reader(2); + input_white_program_ = this->get_input_socket_reader(3); - BKE_curvemapping_premultiply(curveMapping_, 0); + BKE_curvemapping_premultiply(curve_mapping_, 0); } -void ColorCurveOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorCurveOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - CurveMapping *cumap = curveMapping_; + CurveMapping *cumap = curve_mapping_; float fac[4]; float image[4]; @@ -63,15 +63,15 @@ void ColorCurveOperation::executePixelSampled(float output[4], float white[4]; float bwmul[3]; - inputBlackProgram_->readSampled(black, x, y, sampler); - inputWhiteProgram_->readSampled(white, x, y, sampler); + input_black_program_->read_sampled(black, x, y, sampler); + input_white_program_->read_sampled(white, x, y, sampler); /* get our own local bwmul value, * since we can't be threadsafe and use cumap->bwmul & friends */ BKE_curvemapping_set_black_white_ex(black, white, bwmul); - inputFacProgram_->readSampled(fac, x, y, sampler); - inputImageProgram_->readSampled(image, x, y, sampler); + input_fac_program_->read_sampled(fac, x, y, sampler); + input_image_program_->read_sampled(image, x, y, sampler); if (*fac >= 1.0f) { BKE_curvemapping_evaluate_premulRGBF_ex(cumap, output, image, black, bwmul); @@ -87,20 +87,20 @@ void ColorCurveOperation::executePixelSampled(float output[4], output[3] = image[3]; } -void ColorCurveOperation::deinitExecution() +void ColorCurveOperation::deinit_execution() { - CurveBaseOperation::deinitExecution(); - inputFacProgram_ = nullptr; - inputImageProgram_ = nullptr; - inputBlackProgram_ = nullptr; - inputWhiteProgram_ = nullptr; + CurveBaseOperation::deinit_execution(); + input_fac_program_ = nullptr; + input_image_program_ = nullptr; + input_black_program_ = nullptr; + input_white_program_ = nullptr; } void ColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *cumap = curveMapping_; + CurveMapping *cumap = curve_mapping_; float bwmul[3]; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { /* Local versions of `cumap->black` and `cumap->white`. */ @@ -130,63 +130,63 @@ void ColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); - inputFacProgram_ = nullptr; - inputImageProgram_ = nullptr; + input_fac_program_ = nullptr; + input_image_program_ = nullptr; this->set_canvas_input_index(1); } -void ConstantLevelColorCurveOperation::initExecution() +void ConstantLevelColorCurveOperation::init_execution() { - CurveBaseOperation::initExecution(); - inputFacProgram_ = this->getInputSocketReader(0); - inputImageProgram_ = this->getInputSocketReader(1); + CurveBaseOperation::init_execution(); + input_fac_program_ = this->get_input_socket_reader(0); + input_image_program_ = this->get_input_socket_reader(1); - BKE_curvemapping_premultiply(curveMapping_, 0); + BKE_curvemapping_premultiply(curve_mapping_, 0); - BKE_curvemapping_set_black_white(curveMapping_, black_, white_); + BKE_curvemapping_set_black_white(curve_mapping_, black_, white_); } -void ConstantLevelColorCurveOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConstantLevelColorCurveOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float fac[4]; float image[4]; - inputFacProgram_->readSampled(fac, x, y, sampler); - inputImageProgram_->readSampled(image, x, y, sampler); + input_fac_program_->read_sampled(fac, x, y, sampler); + input_image_program_->read_sampled(image, x, y, sampler); if (*fac >= 1.0f) { - BKE_curvemapping_evaluate_premulRGBF(curveMapping_, output, image); + BKE_curvemapping_evaluate_premulRGBF(curve_mapping_, output, image); } else if (*fac <= 0.0f) { copy_v3_v3(output, image); } else { float col[4]; - BKE_curvemapping_evaluate_premulRGBF(curveMapping_, col, image); + BKE_curvemapping_evaluate_premulRGBF(curve_mapping_, col, image); interp_v3_v3v3(output, image, col, *fac); } output[3] = image[3]; } -void ConstantLevelColorCurveOperation::deinitExecution() +void ConstantLevelColorCurveOperation::deinit_execution() { - CurveBaseOperation::deinitExecution(); - inputFacProgram_ = nullptr; - inputImageProgram_ = nullptr; + CurveBaseOperation::deinit_execution(); + input_fac_program_ = nullptr; + input_image_program_ = nullptr; } void ConstantLevelColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *cumap = curveMapping_; + CurveMapping *cumap = curve_mapping_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float fac = *it.in(0); const float *image = it.in(1); diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h index 6f16c45d11e..eaebf1f3ff6 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.h +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h @@ -25,12 +25,12 @@ namespace blender::compositor { class ColorCurveOperation : public CurveBaseOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputFacProgram_; - SocketReader *inputImageProgram_; - SocketReader *inputBlackProgram_; - SocketReader *inputWhiteProgram_; + SocketReader *input_fac_program_; + SocketReader *input_image_program_; + SocketReader *input_black_program_; + SocketReader *input_white_program_; public: ColorCurveOperation(); @@ -38,17 +38,17 @@ class ColorCurveOperation : public CurveBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -58,10 +58,10 @@ class ColorCurveOperation : public CurveBaseOperation { class ConstantLevelColorCurveOperation : public CurveBaseOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputFacProgram_; - SocketReader *inputImageProgram_; + SocketReader *input_fac_program_; + SocketReader *input_image_program_; float black_[3]; float white_[3]; @@ -71,23 +71,23 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setBlackLevel(float black[3]) + void set_black_level(float black[3]) { copy_v3_v3(black_, black); } - void setWhiteLevel(float white[3]) + void set_white_level(float white[3]) { copy_v3_v3(white_, white); } diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.cc b/source/blender/compositor/operations/COM_ColorExposureOperation.cc index f0e6abe67f9..9a785962bd5 100644 --- a/source/blender/compositor/operations/COM_ColorExposureOperation.cc +++ b/source/blender/compositor/operations/COM_ColorExposureOperation.cc @@ -22,35 +22,35 @@ namespace blender::compositor { ExposureOperation::ExposureOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - inputProgram_ = nullptr; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; flags.can_be_constant = true; } -void ExposureOperation::initExecution() +void ExposureOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); - inputExposureProgram_ = this->getInputSocketReader(1); + input_program_ = this->get_input_socket_reader(0); + input_exposure_program_ = this->get_input_socket_reader(1); } -void ExposureOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ExposureOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue[4]; - float inputExposure[4]; - inputProgram_->readSampled(inputValue, x, y, sampler); - inputExposureProgram_->readSampled(inputExposure, x, y, sampler); - const float exposure = pow(2, inputExposure[0]); + float input_value[4]; + float input_exposure[4]; + input_program_->read_sampled(input_value, x, y, sampler); + input_exposure_program_->read_sampled(input_exposure, x, y, sampler); + const float exposure = pow(2, input_exposure[0]); - output[0] = inputValue[0] * exposure; - output[1] = inputValue[1] * exposure; - output[2] = inputValue[2] * exposure; + output[0] = input_value[0] * exposure; + output[1] = input_value[1] * exposure; + output[2] = input_value[2] * exposure; - output[3] = inputValue[3]; + output[3] = input_value[3]; } void ExposureOperation::update_memory_buffer_row(PixelCursor &p) @@ -66,10 +66,10 @@ void ExposureOperation::update_memory_buffer_row(PixelCursor &p) } } -void ExposureOperation::deinitExecution() +void ExposureOperation::deinit_execution() { - inputProgram_ = nullptr; - inputExposureProgram_ = nullptr; + input_program_ = nullptr; + input_exposure_program_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.h b/source/blender/compositor/operations/COM_ColorExposureOperation.h index 1a0f912d6bd..6de93a24ddd 100644 --- a/source/blender/compositor/operations/COM_ColorExposureOperation.h +++ b/source/blender/compositor/operations/COM_ColorExposureOperation.h @@ -25,10 +25,10 @@ namespace blender::compositor { class ExposureOperation : public MultiThreadedRowOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; - SocketReader *inputExposureProgram_; + SocketReader *input_program_; + SocketReader *input_exposure_program_; public: ExposureOperation(); @@ -36,17 +36,17 @@ class ExposureOperation : public MultiThreadedRowOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_row(PixelCursor &p) override; }; diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc index c693e7d06e1..c4bf1bdf1cf 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc @@ -22,34 +22,34 @@ namespace blender::compositor { ColorMatteOperation::ColorMatteOperation() { - addInputSocket(DataType::Color); - addInputSocket(DataType::Color); - addOutputSocket(DataType::Value); + add_input_socket(DataType::Color); + add_input_socket(DataType::Color); + add_output_socket(DataType::Value); - inputImageProgram_ = nullptr; - inputKeyProgram_ = nullptr; + input_image_program_ = nullptr; + input_key_program_ = nullptr; flags.can_be_constant = true; } -void ColorMatteOperation::initExecution() +void ColorMatteOperation::init_execution() { - inputImageProgram_ = this->getInputSocketReader(0); - inputKeyProgram_ = this->getInputSocketReader(1); + input_image_program_ = this->get_input_socket_reader(0); + input_key_program_ = this->get_input_socket_reader(1); } -void ColorMatteOperation::deinitExecution() +void ColorMatteOperation::deinit_execution() { - inputImageProgram_ = nullptr; - inputKeyProgram_ = nullptr; + input_image_program_ = nullptr; + input_key_program_ = nullptr; } -void ColorMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inColor[4]; - float inKey[4]; + float in_color[4]; + float in_key[4]; const float hue = settings_->t1; const float sat = settings_->t2; @@ -57,8 +57,8 @@ void ColorMatteOperation::executePixelSampled(float output[4], float h_wrap; - inputImageProgram_->readSampled(inColor, x, y, sampler); - inputKeyProgram_->readSampled(inKey, x, y, sampler); + input_image_program_->read_sampled(in_color, x, y, sampler); + input_key_program_->read_sampled(in_key, x, y, sampler); /* Store matte(alpha) value in [0] to go with * COM_SetAlphaMultiplyOperation and the Value output. @@ -67,18 +67,19 @@ void ColorMatteOperation::executePixelSampled(float output[4], if ( /* Do hue last because it needs to wrap, and does some more checks. */ - /* sat */ (fabsf(inColor[1] - inKey[1]) < sat) && - /* val */ (fabsf(inColor[2] - inKey[2]) < val) && + /* sat */ (fabsf(in_color[1] - in_key[1]) < sat) && + /* val */ (fabsf(in_color[2] - in_key[2]) < val) && /* multiply by 2 because it wraps on both sides of the hue, * otherwise 0.5 would key all hue's */ - /* hue */ ((h_wrap = 2.0f * fabsf(inColor[0] - inKey[0])) < hue || (2.0f - h_wrap) < hue)) { + /* hue */ + ((h_wrap = 2.0f * fabsf(in_color[0] - in_key[0])) < hue || (2.0f - h_wrap) < hue)) { output[0] = 0.0f; /* make transparent */ } - else { /* Pixel is outside key color. */ - output[0] = inColor[3]; /* Make pixel just as transparent as it was before. */ + else { /* Pixel is outside key color. */ + output[0] = in_color[3]; /* Make pixel just as transparent as it was before. */ } } diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.h b/source/blender/compositor/operations/COM_ColorMatteOperation.h index 2037fa0cdc3..97cf568621a 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.h +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.h @@ -29,8 +29,8 @@ namespace blender::compositor { class ColorMatteOperation : public MultiThreadedOperation { private: NodeChroma *settings_; - SocketReader *inputImageProgram_; - SocketReader *inputKeyProgram_; + SocketReader *input_image_program_; + SocketReader *input_key_program_; public: /** @@ -41,14 +41,14 @@ class ColorMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma) + void set_settings(NodeChroma *node_chroma) { - settings_ = nodeChroma; + settings_ = node_chroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.cc b/source/blender/compositor/operations/COM_ColorRampOperation.cc index 054bdcf5b04..bd86528d143 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.cc +++ b/source/blender/compositor/operations/COM_ColorRampOperation.cc @@ -24,32 +24,32 @@ namespace blender::compositor { ColorRampOperation::ColorRampOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); - inputProgram_ = nullptr; - colorBand_ = nullptr; + input_program_ = nullptr; + color_band_ = nullptr; this->flags.can_be_constant = true; } -void ColorRampOperation::initExecution() +void ColorRampOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void ColorRampOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorRampOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float values[4]; - inputProgram_->readSampled(values, x, y, sampler); - BKE_colorband_evaluate(colorBand_, values[0], output); + input_program_->read_sampled(values, x, y, sampler); + BKE_colorband_evaluate(color_band_, values[0], output); } -void ColorRampOperation::deinitExecution() +void ColorRampOperation::deinit_execution() { - inputProgram_ = nullptr; + input_program_ = nullptr; } void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -57,7 +57,7 @@ void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - BKE_colorband_evaluate(colorBand_, *it.in(0), it.out); + BKE_colorband_evaluate(color_band_, *it.in(0), it.out); } } diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.h b/source/blender/compositor/operations/COM_ColorRampOperation.h index 3cb5a7d83e1..2faaab4ba54 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.h +++ b/source/blender/compositor/operations/COM_ColorRampOperation.h @@ -26,10 +26,10 @@ namespace blender::compositor { class ColorRampOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; - ColorBand *colorBand_; + SocketReader *input_program_; + ColorBand *color_band_; public: ColorRampOperation(); @@ -37,21 +37,21 @@ class ColorRampOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setColorBand(ColorBand *colorBand) + void set_color_band(ColorBand *color_band) { - colorBand_ = colorBand; + color_band_ = color_band; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc index 45197bf2b1d..39be05ece38 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc @@ -23,22 +23,22 @@ namespace blender::compositor { ColorSpillOperation::ColorSpillOperation() { - addInputSocket(DataType::Color); - addInputSocket(DataType::Value); - addOutputSocket(DataType::Color); + add_input_socket(DataType::Color); + add_input_socket(DataType::Value); + add_output_socket(DataType::Color); - inputImageReader_ = nullptr; - inputFacReader_ = nullptr; - spillChannel_ = 1; /* GREEN */ - spillMethod_ = 0; + input_image_reader_ = nullptr; + input_fac_reader_ = nullptr; + spill_channel_ = 1; /* GREEN */ + spill_method_ = 0; flags.can_be_constant = true; } -void ColorSpillOperation::initExecution() +void ColorSpillOperation::init_execution() { - inputImageReader_ = this->getInputSocketReader(0); - inputFacReader_ = this->getInputSocketReader(1); - if (spillChannel_ == 0) { + input_image_reader_ = this->get_input_socket_reader(0); + input_fac_reader_ = this->get_input_socket_reader(1); + if (spill_channel_ == 0) { rmut_ = -1.0f; gmut_ = 1.0f; bmut_ = 1.0f; @@ -50,7 +50,7 @@ void ColorSpillOperation::initExecution() settings_->uspillb = 0.0f; } } - else if (spillChannel_ == 1) { + else if (spill_channel_ == 1) { rmut_ = 1.0f; gmut_ = -1.0f; bmut_ = 1.0f; @@ -77,30 +77,30 @@ void ColorSpillOperation::initExecution() } } -void ColorSpillOperation::deinitExecution() +void ColorSpillOperation::deinit_execution() { - inputImageReader_ = nullptr; - inputFacReader_ = nullptr; + input_image_reader_ = nullptr; + input_fac_reader_ = nullptr; } -void ColorSpillOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorSpillOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float fac[4]; float input[4]; - inputFacReader_->readSampled(fac, x, y, sampler); - inputImageReader_->readSampled(input, x, y, sampler); + input_fac_reader_->read_sampled(fac, x, y, sampler); + input_image_reader_->read_sampled(input, x, y, sampler); float rfac = MIN2(1.0f, fac[0]); float map; - switch (spillMethod_) { + switch (spill_method_) { case 0: /* simple */ - map = rfac * (input[spillChannel_] - (settings_->limscale * input[settings_->limchan])); + map = rfac * (input[spill_channel_] - (settings_->limscale * input[settings_->limchan])); break; default: /* average */ - map = rfac * (input[spillChannel_] - + map = rfac * (input[spill_channel_] - (settings_->limscale * AVG(input[channel2_], input[channel3_]))); break; } @@ -125,12 +125,12 @@ void ColorSpillOperation::update_memory_buffer_partial(MemoryBuffer *output, const float factor = MIN2(1.0f, *it.in(1)); float map; - switch (spillMethod_) { + switch (spill_method_) { case 0: /* simple */ - map = factor * (color[spillChannel_] - (settings_->limscale * color[settings_->limchan])); + map = factor * (color[spill_channel_] - (settings_->limscale * color[settings_->limchan])); break; default: /* average */ - map = factor * (color[spillChannel_] - + map = factor * (color[spill_channel_] - (settings_->limscale * AVG(color[channel2_], color[channel3_]))); break; } diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.h b/source/blender/compositor/operations/COM_ColorSpillOperation.h index aab74e68f41..bfb0c6c963f 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.h +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.h @@ -29,10 +29,10 @@ namespace blender::compositor { class ColorSpillOperation : public MultiThreadedOperation { protected: NodeColorspill *settings_; - SocketReader *inputImageReader_; - SocketReader *inputFacReader_; - int spillChannel_; - int spillMethod_; + SocketReader *input_image_reader_; + SocketReader *input_fac_reader_; + int spill_channel_; + int spill_method_; int channel2_; int channel3_; float rmut_, gmut_, bmut_; @@ -46,25 +46,25 @@ class ColorSpillOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeColorspill *nodeColorSpill) + void set_settings(NodeColorspill *node_color_spill) { - settings_ = nodeColorSpill; + settings_ = node_color_spill; } - void setSpillChannel(int channel) + void set_spill_channel(int channel) { - spillChannel_ = channel; + spill_channel_ = channel; } - void setSpillMethod(int method) + void set_spill_method(int method) { - spillMethod_ = method; + spill_method_ = method; } - float calculateMapValue(float fac, float *input); + float calculate_map_value(float fac, float *input); void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index 029dec8c371..e27fc971a87 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -27,76 +27,76 @@ namespace blender::compositor { CompositorOperation::CompositorOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); - this->setRenderData(nullptr); - outputBuffer_ = nullptr; - depthBuffer_ = nullptr; - imageInput_ = nullptr; - alphaInput_ = nullptr; - depthInput_ = nullptr; + this->set_render_data(nullptr); + output_buffer_ = nullptr; + depth_buffer_ = nullptr; + image_input_ = nullptr; + alpha_input_ = nullptr; + depth_input_ = nullptr; - useAlphaInput_ = false; + use_alpha_input_ = false; active_ = false; scene_ = nullptr; - sceneName_[0] = '\0'; - viewName_ = nullptr; + scene_name_[0] = '\0'; + view_name_ = nullptr; flags.use_render_border = true; } -void CompositorOperation::initExecution() +void CompositorOperation::init_execution() { if (!active_) { return; } /* When initializing the tree during initial load the width and height can be zero. */ - imageInput_ = getInputSocketReader(0); - alphaInput_ = getInputSocketReader(1); - depthInput_ = getInputSocketReader(2); - if (this->getWidth() * this->getHeight() != 0) { - outputBuffer_ = (float *)MEM_callocN(sizeof(float[4]) * this->getWidth() * this->getHeight(), - "CompositorOperation"); + image_input_ = get_input_socket_reader(0); + alpha_input_ = get_input_socket_reader(1); + depth_input_ = get_input_socket_reader(2); + if (this->get_width() * this->get_height() != 0) { + output_buffer_ = (float *)MEM_callocN( + sizeof(float[4]) * this->get_width() * this->get_height(), "CompositorOperation"); } - if (depthInput_ != nullptr) { - depthBuffer_ = (float *)MEM_callocN(sizeof(float) * this->getWidth() * this->getHeight(), - "CompositorOperation"); + if (depth_input_ != nullptr) { + depth_buffer_ = (float *)MEM_callocN(sizeof(float) * this->get_width() * this->get_height(), + "CompositorOperation"); } } -void CompositorOperation::deinitExecution() +void CompositorOperation::deinit_execution() { if (!active_) { return; } - if (!isBraked()) { + if (!is_braked()) { Render *re = RE_GetSceneRender(scene_); RenderResult *rr = RE_AcquireResultWrite(re); if (rr) { - RenderView *rv = RE_RenderViewGetByName(rr, viewName_); + RenderView *rv = RE_RenderViewGetByName(rr, view_name_); if (rv->rectf != nullptr) { MEM_freeN(rv->rectf); } - rv->rectf = outputBuffer_; + rv->rectf = output_buffer_; if (rv->rectz != nullptr) { MEM_freeN(rv->rectz); } - rv->rectz = depthBuffer_; + rv->rectz = depth_buffer_; rr->have_combined = true; } else { - if (outputBuffer_) { - MEM_freeN(outputBuffer_); + if (output_buffer_) { + MEM_freeN(output_buffer_); } - if (depthBuffer_) { - MEM_freeN(depthBuffer_); + if (depth_buffer_) { + MEM_freeN(depth_buffer_); } } @@ -113,26 +113,26 @@ void CompositorOperation::deinitExecution() BLI_thread_unlock(LOCK_DRAW_IMAGE); } else { - if (outputBuffer_) { - MEM_freeN(outputBuffer_); + if (output_buffer_) { + MEM_freeN(output_buffer_); } - if (depthBuffer_) { - MEM_freeN(depthBuffer_); + if (depth_buffer_) { + MEM_freeN(depth_buffer_); } } - outputBuffer_ = nullptr; - depthBuffer_ = nullptr; - imageInput_ = nullptr; - alphaInput_ = nullptr; - depthInput_ = nullptr; + output_buffer_ = nullptr; + depth_buffer_ = nullptr; + image_input_ = nullptr; + alpha_input_ = nullptr; + depth_input_ = nullptr; } -void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void CompositorOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { float color[8]; // 7 is enough - float *buffer = outputBuffer_; - float *zbuffer = depthBuffer_; + float *buffer = output_buffer_; + float *zbuffer = depth_buffer_; if (!buffer) { return; @@ -141,8 +141,8 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) int y1 = rect->ymin; int x2 = rect->xmax; int y2 = rect->ymax; - int offset = (y1 * this->getWidth() + x1); - int add = (this->getWidth() - (x2 - x1)); + int offset = (y1 * this->get_width() + x1); + int add = (this->get_width() - (x2 - x1)); int offset4 = offset * COM_DATA_TYPE_COLOR_CHANNELS; int x; int y; @@ -183,8 +183,8 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) int full_width = rd->xsch * rd->size / 100; int full_height = rd->ysch * rd->size / 100; - dx = rd->border.xmin * full_width - (full_width - this->getWidth()) / 2.0f; - dy = rd->border.ymin * full_height - (full_height - this->getHeight()) / 2.0f; + dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f; + dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f; } #endif @@ -192,18 +192,18 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) for (x = x1; x < x2 && (!breaked); x++) { int input_x = x + dx, input_y = y + dy; - imageInput_->readSampled(color, input_x, input_y, PixelSampler::Nearest); - if (useAlphaInput_) { - alphaInput_->readSampled(&(color[3]), input_x, input_y, PixelSampler::Nearest); + image_input_->read_sampled(color, input_x, input_y, PixelSampler::Nearest); + if (use_alpha_input_) { + alpha_input_->read_sampled(&(color[3]), input_x, input_y, PixelSampler::Nearest); } copy_v4_v4(buffer + offset4, color); - depthInput_->readSampled(color, input_x, input_y, PixelSampler::Nearest); + depth_input_->read_sampled(color, input_x, input_y, PixelSampler::Nearest); zbuffer[offset] = color[0]; offset4 += COM_DATA_TYPE_COLOR_CHANNELS; offset++; - if (isBraked()) { + if (is_braked()) { breaked = true; } } @@ -216,15 +216,15 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp const rcti &area, Span inputs) { - if (!outputBuffer_) { + if (!output_buffer_) { return; } - MemoryBuffer output_buf(outputBuffer_, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); + MemoryBuffer output_buf(output_buffer_, COM_DATA_TYPE_COLOR_CHANNELS, get_width(), get_height()); output_buf.copy_from(inputs[0], area); - if (useAlphaInput_) { + if (use_alpha_input_) { output_buf.copy_from(inputs[1], area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3); } - MemoryBuffer depth_buf(depthBuffer_, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); + MemoryBuffer depth_buf(depth_buffer_, COM_DATA_TYPE_VALUE_CHANNELS, get_width(), get_height()); depth_buf.copy_from(inputs[2], area); } diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index e9539ae66fd..a0960074708 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -33,7 +33,7 @@ class CompositorOperation : public MultiThreadedOperation { /** * \brief Scene name, used for getting the render output, includes 'SC' prefix. */ - char sceneName_[MAX_ID_NAME]; + char scene_name_[MAX_ID_NAME]; /** * \brief local reference to the scene @@ -43,32 +43,32 @@ class CompositorOperation : public MultiThreadedOperation { /** * \brief reference to the output float buffer */ - float *outputBuffer_; + float *output_buffer_; /** * \brief reference to the output depth float buffer */ - float *depthBuffer_; + float *depth_buffer_; /** * \brief local reference to the input image operation */ - SocketReader *imageInput_; + SocketReader *image_input_; /** * \brief local reference to the input alpha operation */ - SocketReader *alphaInput_; + SocketReader *alpha_input_; /** * \brief local reference to the depth operation */ - SocketReader *depthInput_; + SocketReader *depth_input_; /** * \brief Ignore any alpha input */ - bool useAlphaInput_; + bool use_alpha_input_; /** * \brief operation is active for calculating final compo result @@ -78,47 +78,47 @@ class CompositorOperation : public MultiThreadedOperation { /** * \brief View name, used for multiview */ - const char *viewName_; + const char *view_name_; public: CompositorOperation(); - bool isActiveCompositorOutput() const + bool is_active_compositor_output() const { return active_; } - void executeRegion(rcti *rect, unsigned int tileNumber) override; - void setScene(const struct Scene *scene) + void execute_region(rcti *rect, unsigned int tile_number) override; + void set_scene(const struct Scene *scene) { scene_ = scene; } - void setSceneName(const char *sceneName) + void set_scene_name(const char *scene_name) { - BLI_strncpy(sceneName_, sceneName, sizeof(sceneName_)); + BLI_strncpy(scene_name_, scene_name, sizeof(scene_name_)); } - void setViewName(const char *viewName) + void set_view_name(const char *view_name) { - viewName_ = viewName; + view_name_ = view_name; } - void setRenderData(const RenderData *rd) + void set_render_data(const RenderData *rd) { rd_ = rd; } - bool isOutputOperation(bool /*rendering*/) const override + bool is_output_operation(bool /*rendering*/) const override { - return this->isActiveCompositorOutput(); + return this->is_active_compositor_output(); } - void initExecution() override; - void deinitExecution() override; - eCompositorPriority getRenderPriority() const override + void init_execution() override; + void deinit_execution() override; + eCompositorPriority get_render_priority() const override { return eCompositorPriority::Medium; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void setUseAlphaInput(bool value) + void set_use_alpha_input(bool value) { - useAlphaInput_ = value; + use_alpha_input_ = value; } - void setActive(bool active) + void set_active(bool active) { active_ = active; } diff --git a/source/blender/compositor/operations/COM_ConstantOperation.h b/source/blender/compositor/operations/COM_ConstantOperation.h index d44d1939424..d0db1ef1366 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.h +++ b/source/blender/compositor/operations/COM_ConstantOperation.h @@ -22,7 +22,7 @@ namespace blender::compositor { -/* TODO(manzanilla): After removing tiled implementation, implement a default #determineResolution +/* TODO(manzanilla): After removing tiled implementation, implement a default #determine_resolution * for all constant operations and make all initialization and deinitilization methods final. */ /** * Base class for operations that are always constant. Operations that can be constant only when diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc index 4f78c919695..db75b2724c4 100644 --- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc @@ -24,30 +24,31 @@ namespace blender::compositor { ConvertColorProfileOperation::ConvertColorProfileOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - inputOperation_ = nullptr; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_operation_ = nullptr; predivided_ = false; } -void ConvertColorProfileOperation::initExecution() +void ConvertColorProfileOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void ConvertColorProfileOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertColorProfileOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float color[4]; - inputOperation_->readSampled(color, x, y, sampler); - IMB_buffer_float_from_float(output, color, 4, toProfile_, fromProfile_, predivided_, 1, 1, 0, 0); + input_operation_->read_sampled(color, x, y, sampler); + IMB_buffer_float_from_float( + output, color, 4, to_profile_, from_profile_, predivided_, 1, 1, 0, 0); } -void ConvertColorProfileOperation::deinitExecution() +void ConvertColorProfileOperation::deinit_execution() { - inputOperation_ = nullptr; + input_operation_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h index 532fad86091..960d267b906 100644 --- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h +++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h @@ -29,19 +29,19 @@ namespace blender::compositor { class ConvertColorProfileOperation : public NodeOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputOperation_; + SocketReader *input_operation_; /** * \brief color profile where to convert from */ - int fromProfile_; + int from_profile_; /** * \brief color profile where to convert to */ - int toProfile_; + int to_profile_; /** * \brief is color predivided @@ -57,27 +57,27 @@ class ConvertColorProfileOperation : public NodeOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setFromColorProfile(int colorProfile) + void set_from_color_profile(int color_profile) { - fromProfile_ = colorProfile; + from_profile_ = color_profile; } - void setToColorProfile(int colorProfile) + void set_to_color_profile(int color_profile) { - toProfile_ = colorProfile; + to_profile_ = color_profile; } - void setPredivided(bool predivided) + void set_predivided(bool predivided) { predivided_ = predivided; } diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc index fcd9d26c3f0..a781b3ea1ed 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc @@ -24,65 +24,65 @@ namespace blender::compositor { ConvertDepthToRadiusOperation::ConvertDepthToRadiusOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - inputOperation_ = nullptr; - fStop_ = 128.0f; - cameraObject_ = nullptr; - maxRadius_ = 32.0f; - blurPostOperation_ = nullptr; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_operation_ = nullptr; + f_stop_ = 128.0f; + camera_object_ = nullptr; + max_radius_ = 32.0f; + blur_post_operation_ = nullptr; } -float ConvertDepthToRadiusOperation::determineFocalDistance() +float ConvertDepthToRadiusOperation::determine_focal_distance() { - if (cameraObject_ && cameraObject_->type == OB_CAMERA) { - Camera *camera = (Camera *)cameraObject_->data; + if (camera_object_ && camera_object_->type == OB_CAMERA) { + Camera *camera = (Camera *)camera_object_->data; cam_lens_ = camera->lens; - return BKE_camera_object_dof_distance(cameraObject_); + return BKE_camera_object_dof_distance(camera_object_); } return 10.0f; } -void ConvertDepthToRadiusOperation::initExecution() +void ConvertDepthToRadiusOperation::init_execution() { float cam_sensor = DEFAULT_SENSOR_WIDTH; Camera *camera = nullptr; - if (cameraObject_ && cameraObject_->type == OB_CAMERA) { - camera = (Camera *)cameraObject_->data; + if (camera_object_ && camera_object_->type == OB_CAMERA) { + camera = (Camera *)camera_object_->data; cam_sensor = BKE_camera_sensor_size(camera->sensor_fit, camera->sensor_x, camera->sensor_y); } - inputOperation_ = this->getInputSocketReader(0); - float focalDistance = determineFocalDistance(); - if (focalDistance == 0.0f) { - focalDistance = 1e10f; /* If the DOF is 0.0 then set it to be far away. */ + input_operation_ = this->get_input_socket_reader(0); + float focal_distance = determine_focal_distance(); + if (focal_distance == 0.0f) { + focal_distance = 1e10f; /* If the DOF is 0.0 then set it to be far away. */ } - inverseFocalDistance_ = 1.0f / focalDistance; - aspect_ = (this->getWidth() > this->getHeight()) ? - (this->getHeight() / (float)this->getWidth()) : - (this->getWidth() / (float)this->getHeight()); - aperture_ = 0.5f * (cam_lens_ / (aspect_ * cam_sensor)) / fStop_; - const float minsz = MIN2(getWidth(), getHeight()); + inverse_focal_distance_ = 1.0f / focal_distance; + aspect_ = (this->get_width() > this->get_height()) ? + (this->get_height() / (float)this->get_width()) : + (this->get_width() / (float)this->get_height()); + aperture_ = 0.5f * (cam_lens_ / (aspect_ * cam_sensor)) / f_stop_; + const float minsz = MIN2(get_width(), get_height()); dof_sp_ = minsz / ((cam_sensor / 2.0f) / cam_lens_); /* <- == `aspect * MIN2(img->x, img->y) / tan(0.5f * fov)` */ - if (blurPostOperation_) { - blurPostOperation_->setSigma(MIN2(aperture_ * 128.0f, maxRadius_)); + if (blur_post_operation_) { + blur_post_operation_->set_sigma(MIN2(aperture_ * 128.0f, max_radius_)); } } -void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertDepthToRadiusOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue[4]; + float input_value[4]; float z; float radius; - inputOperation_->readSampled(inputValue, x, y, sampler); - z = inputValue[0]; + input_operation_->read_sampled(input_value, x, y, sampler); + z = input_value[0]; if (z != 0.0f) { float iZ = (1.0f / z); @@ -92,14 +92,14 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], /* Scale crad back to original maximum and blend. */ crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad); #endif - radius = 0.5f * fabsf(aperture_ * (dof_sp_ * (inverseFocalDistance_ - iZ) - 1.0f)); + radius = 0.5f * fabsf(aperture_ * (dof_sp_ * (inverse_focal_distance_ - iZ) - 1.0f)); /* 'bug' T6615, limit minimum radius to 1 pixel, * not really a solution, but somewhat mitigates the problem. */ if (radius < 0.0f) { radius = 0.0f; } - if (radius > maxRadius_) { - radius = maxRadius_; + if (radius > max_radius_) { + radius = max_radius_; } output[0] = radius; } @@ -108,9 +108,9 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], } } -void ConvertDepthToRadiusOperation::deinitExecution() +void ConvertDepthToRadiusOperation::deinit_execution() { - inputOperation_ = nullptr; + input_operation_ = nullptr; } void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -133,10 +133,10 @@ void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *o * `crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad);` */ #endif const float radius = 0.5f * - fabsf(aperture_ * (dof_sp_ * (inverseFocalDistance_ - inv_z) - 1.0f)); + fabsf(aperture_ * (dof_sp_ * (inverse_focal_distance_ - inv_z) - 1.0f)); /* Bug T6615, limit minimum radius to 1 pixel, * not really a solution, but somewhat mitigates the problem. */ - *it.out = CLAMPIS(radius, 0.0f, maxRadius_); + *it.out = CLAMPIS(radius, 0.0f, max_radius_); } } diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h index d3beff60207..72d19eb3dd8 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h @@ -31,19 +31,19 @@ namespace blender::compositor { class ConvertDepthToRadiusOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputOperation_; - float fStop_; + SocketReader *input_operation_; + float f_stop_; float aspect_; - float maxRadius_; - float inverseFocalDistance_; + float max_radius_; + float inverse_focal_distance_; float aperture_; float cam_lens_; float dof_sp_; - Object *cameraObject_; + Object *camera_object_; - FastGaussianBlurValueOperation *blurPostOperation_; + FastGaussianBlurValueOperation *blur_post_operation_; public: /** @@ -54,34 +54,34 @@ class ConvertDepthToRadiusOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setfStop(float fStop) + void setf_stop(float f_stop) { - fStop_ = fStop; + f_stop_ = f_stop; } - void setMaxRadius(float maxRadius) + void set_max_radius(float max_radius) { - maxRadius_ = maxRadius; + max_radius_ = max_radius; } - void setCameraObject(Object *camera) + void set_camera_object(Object *camera) { - cameraObject_ = camera; + camera_object_ = camera; } - float determineFocalDistance(); - void setPostBlur(FastGaussianBlurValueOperation *operation) + float determine_focal_distance(); + void set_post_blur(FastGaussianBlurValueOperation *operation) { - blurPostOperation_ = operation; + blur_post_operation_ = operation; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 88de54e8c41..7f1018ca71e 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -26,18 +26,18 @@ namespace blender::compositor { ConvertBaseOperation::ConvertBaseOperation() { - inputOperation_ = nullptr; + input_operation_ = nullptr; this->flags.can_be_constant = true; } -void ConvertBaseOperation::initExecution() +void ConvertBaseOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void ConvertBaseOperation::deinitExecution() +void ConvertBaseOperation::deinit_execution() { - inputOperation_ = nullptr; + input_operation_ = nullptr; } void ConvertBaseOperation::hash_output_params() @@ -56,17 +56,17 @@ void ConvertBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, ConvertValueToColorOperation::ConvertValueToColorOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); } -void ConvertValueToColorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertValueToColorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float value; - inputOperation_->readSampled(&value, x, y, sampler); + input_operation_->read_sampled(&value, x, y, sampler); output[0] = output[1] = output[2] = value; output[3] = 1.0f; } @@ -83,18 +83,18 @@ void ConvertValueToColorOperation::update_memory_buffer_partial(BuffersIterator< ConvertColorToValueOperation::ConvertColorToValueOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Value); } -void ConvertColorToValueOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertColorToValueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - inputOperation_->readSampled(inputColor, x, y, sampler); - output[0] = (inputColor[0] + inputColor[1] + inputColor[2]) / 3.0f; + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + output[0] = (input_color[0] + input_color[1] + input_color[2]) / 3.0f; } void ConvertColorToValueOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -109,18 +109,18 @@ void ConvertColorToValueOperation::update_memory_buffer_partial(BuffersIterator< ConvertColorToBWOperation::ConvertColorToBWOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Value); } -void ConvertColorToBWOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertColorToBWOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - inputOperation_->readSampled(inputColor, x, y, sampler); - output[0] = IMB_colormanagement_get_luminance(inputColor); + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + output[0] = IMB_colormanagement_get_luminance(input_color); } void ConvertColorToBWOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -134,17 +134,17 @@ void ConvertColorToBWOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Vector); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Vector); } -void ConvertColorToVectorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertColorToVectorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float color[4]; - inputOperation_->readSampled(color, x, y, sampler); + input_operation_->read_sampled(color, x, y, sampler); copy_v3_v3(output, color); } @@ -159,17 +159,17 @@ void ConvertColorToVectorOperation::update_memory_buffer_partial(BuffersIterator ConvertValueToVectorOperation::ConvertValueToVectorOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Vector); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Vector); } -void ConvertValueToVectorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertValueToVectorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float value; - inputOperation_->readSampled(&value, x, y, sampler); + input_operation_->read_sampled(&value, x, y, sampler); output[0] = output[1] = output[2] = value; } @@ -184,16 +184,16 @@ void ConvertValueToVectorOperation::update_memory_buffer_partial(BuffersIterator ConvertVectorToColorOperation::ConvertVectorToColorOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Vector); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Vector); + this->add_output_socket(DataType::Color); } -void ConvertVectorToColorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertVectorToColorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - inputOperation_->readSampled(output, x, y, sampler); + input_operation_->read_sampled(output, x, y, sampler); output[3] = 1.0f; } @@ -209,17 +209,17 @@ void ConvertVectorToColorOperation::update_memory_buffer_partial(BuffersIterator ConvertVectorToValueOperation::ConvertVectorToValueOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Vector); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Vector); + this->add_output_socket(DataType::Value); } -void ConvertVectorToValueOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertVectorToValueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float input[4]; - inputOperation_->readSampled(input, x, y, sampler); + input_operation_->read_sampled(input, x, y, sampler); output[0] = (input[0] + input[1] + input[2]) / 3.0f; } @@ -235,11 +235,11 @@ void ConvertVectorToValueOperation::update_memory_buffer_partial(BuffersIterator ConvertRGBToYCCOperation::ConvertRGBToYCCOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertRGBToYCCOperation::setMode(int mode) +void ConvertRGBToYCCOperation::set_mode(int mode) { switch (mode) { case 0: @@ -255,21 +255,22 @@ void ConvertRGBToYCCOperation::setMode(int mode) } } -void ConvertRGBToYCCOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertRGBToYCCOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; + float input_color[4]; float color[3]; - inputOperation_->readSampled(inputColor, x, y, sampler); - rgb_to_ycc(inputColor[0], inputColor[1], inputColor[2], &color[0], &color[1], &color[2], mode_); + input_operation_->read_sampled(input_color, x, y, sampler); + rgb_to_ycc( + input_color[0], input_color[1], input_color[2], &color[0], &color[1], &color[2], mode_); /* divided by 255 to normalize for viewing in */ /* R,G,B --> Y,Cb,Cr */ mul_v3_v3fl(output, color, 1.0f / 255.0f); - output[3] = inputColor[3]; + output[3] = input_color[3]; } void ConvertRGBToYCCOperation::hash_output_params() @@ -294,11 +295,11 @@ void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertYCCToRGBOperation::setMode(int mode) +void ConvertYCCToRGBOperation::set_mode(int mode) { switch (mode) { case 0: @@ -314,21 +315,21 @@ void ConvertYCCToRGBOperation::setMode(int mode) } } -void ConvertYCCToRGBOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertYCCToRGBOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - inputOperation_->readSampled(inputColor, x, y, sampler); + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); /* need to un-normalize the data */ /* R,G,B --> Y,Cb,Cr */ - mul_v3_fl(inputColor, 255.0f); + mul_v3_fl(input_color, 255.0f); ycc_to_rgb( - inputColor[0], inputColor[1], inputColor[2], &output[0], &output[1], &output[2], mode_); - output[3] = inputColor[3]; + input_color[0], input_color[1], input_color[2], &output[0], &output[1], &output[2], mode_); + output[3] = input_color[3]; } void ConvertYCCToRGBOperation::hash_output_params() @@ -352,25 +353,25 @@ void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertRGBToYUVOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertRGBToYUVOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - inputOperation_->readSampled(inputColor, x, y, sampler); - rgb_to_yuv(inputColor[0], - inputColor[1], - inputColor[2], + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + rgb_to_yuv(input_color[0], + input_color[1], + input_color[2], &output[0], &output[1], &output[2], BLI_YUV_ITU_BT709); - output[3] = inputColor[3]; + output[3] = input_color[3]; } void ConvertRGBToYUVOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -386,25 +387,25 @@ void ConvertRGBToYUVOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertYUVToRGBOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertYUVToRGBOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - inputOperation_->readSampled(inputColor, x, y, sampler); - yuv_to_rgb(inputColor[0], - inputColor[1], - inputColor[2], + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + yuv_to_rgb(input_color[0], + input_color[1], + input_color[2], &output[0], &output[1], &output[2], BLI_YUV_ITU_BT709); - output[3] = inputColor[3]; + output[3] = input_color[3]; } void ConvertYUVToRGBOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -420,19 +421,19 @@ void ConvertYUVToRGBOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertRGBToHSVOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertRGBToHSVOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - inputOperation_->readSampled(inputColor, x, y, sampler); - rgb_to_hsv_v(inputColor, output); - output[3] = inputColor[3]; + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + rgb_to_hsv_v(input_color, output); + output[3] = input_color[3]; } void ConvertRGBToHSVOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -448,22 +449,22 @@ void ConvertRGBToHSVOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertHSVToRGBOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertHSVToRGBOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - inputOperation_->readSampled(inputColor, x, y, sampler); - hsv_to_rgb_v(inputColor, output); + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + hsv_to_rgb_v(input_color, output); output[0] = max_ff(output[0], 0.0f); output[1] = max_ff(output[1], 0.0f); output[2] = max_ff(output[2], 0.0f); - output[3] = inputColor[3]; + output[3] = input_color[3]; } void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -482,17 +483,17 @@ void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertPremulToStraightOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertPremulToStraightOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { ColorSceneLinear4f input; - inputOperation_->readSampled(input, x, y, sampler); + input_operation_->read_sampled(input, x, y, sampler); ColorSceneLinear4f converted = input.unpremultiply_alpha(); copy_v4_v4(output, converted); } @@ -508,17 +509,17 @@ void ConvertPremulToStraightOperation::update_memory_buffer_partial(BuffersItera ConvertStraightToPremulOperation::ConvertStraightToPremulOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertStraightToPremulOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertStraightToPremulOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { ColorSceneLinear4f input; - inputOperation_->readSampled(input, x, y, sampler); + input_operation_->read_sampled(input, x, y, sampler); ColorSceneLinear4f converted = input.premultiply_alpha(); copy_v4_v4(output, converted); } @@ -534,27 +535,27 @@ void ConvertStraightToPremulOperation::update_memory_buffer_partial(BuffersItera SeparateChannelOperation::SeparateChannelOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Value); - inputOperation_ = nullptr; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Value); + input_operation_ = nullptr; } -void SeparateChannelOperation::initExecution() +void SeparateChannelOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void SeparateChannelOperation::deinitExecution() +void SeparateChannelOperation::deinit_execution() { - inputOperation_ = nullptr; + input_operation_ = nullptr; } -void SeparateChannelOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void SeparateChannelOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float input[4]; - inputOperation_->readSampled(input, x, y, sampler); + input_operation_->read_sampled(input, x, y, sampler); output[0] = input[channel_]; } @@ -571,54 +572,54 @@ void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output CombineChannelsOperation::CombineChannelsOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); - inputChannel1Operation_ = nullptr; - inputChannel2Operation_ = nullptr; - inputChannel3Operation_ = nullptr; - inputChannel4Operation_ = nullptr; + input_channel1_operation_ = nullptr; + input_channel2_operation_ = nullptr; + input_channel3_operation_ = nullptr; + input_channel4_operation_ = nullptr; } -void CombineChannelsOperation::initExecution() +void CombineChannelsOperation::init_execution() { - inputChannel1Operation_ = this->getInputSocketReader(0); - inputChannel2Operation_ = this->getInputSocketReader(1); - inputChannel3Operation_ = this->getInputSocketReader(2); - inputChannel4Operation_ = this->getInputSocketReader(3); + input_channel1_operation_ = this->get_input_socket_reader(0); + input_channel2_operation_ = this->get_input_socket_reader(1); + input_channel3_operation_ = this->get_input_socket_reader(2); + input_channel4_operation_ = this->get_input_socket_reader(3); } -void CombineChannelsOperation::deinitExecution() +void CombineChannelsOperation::deinit_execution() { - inputChannel1Operation_ = nullptr; - inputChannel2Operation_ = nullptr; - inputChannel3Operation_ = nullptr; - inputChannel4Operation_ = nullptr; + input_channel1_operation_ = nullptr; + input_channel2_operation_ = nullptr; + input_channel3_operation_ = nullptr; + input_channel4_operation_ = nullptr; } -void CombineChannelsOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void CombineChannelsOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float input[4]; - if (inputChannel1Operation_) { - inputChannel1Operation_->readSampled(input, x, y, sampler); + if (input_channel1_operation_) { + input_channel1_operation_->read_sampled(input, x, y, sampler); output[0] = input[0]; } - if (inputChannel2Operation_) { - inputChannel2Operation_->readSampled(input, x, y, sampler); + if (input_channel2_operation_) { + input_channel2_operation_->read_sampled(input, x, y, sampler); output[1] = input[0]; } - if (inputChannel3Operation_) { - inputChannel3Operation_->readSampled(input, x, y, sampler); + if (input_channel3_operation_) { + input_channel3_operation_->read_sampled(input, x, y, sampler); output[2] = input[0]; } - if (inputChannel4Operation_) { - inputChannel4Operation_->readSampled(input, x, y, sampler); + if (input_channel4_operation_) { + input_channel4_operation_->read_sampled(input, x, y, sampler); output[3] = input[0]; } } diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h index 9d4539c8faa..3c4ce358eec 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.h +++ b/source/blender/compositor/operations/COM_ConvertOperation.h @@ -24,13 +24,13 @@ namespace blender::compositor { class ConvertBaseOperation : public MultiThreadedOperation { protected: - SocketReader *inputOperation_; + SocketReader *input_operation_; public: ConvertBaseOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -45,7 +45,7 @@ class ConvertValueToColorOperation : public ConvertBaseOperation { public: ConvertValueToColorOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -55,7 +55,7 @@ class ConvertColorToValueOperation : public ConvertBaseOperation { public: ConvertColorToValueOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -65,7 +65,7 @@ class ConvertColorToBWOperation : public ConvertBaseOperation { public: ConvertColorToBWOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -75,7 +75,7 @@ class ConvertColorToVectorOperation : public ConvertBaseOperation { public: ConvertColorToVectorOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -85,7 +85,7 @@ class ConvertValueToVectorOperation : public ConvertBaseOperation { public: ConvertValueToVectorOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -95,7 +95,7 @@ class ConvertVectorToColorOperation : public ConvertBaseOperation { public: ConvertVectorToColorOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -105,7 +105,7 @@ class ConvertVectorToValueOperation : public ConvertBaseOperation { public: ConvertVectorToValueOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -119,10 +119,10 @@ class ConvertRGBToYCCOperation : public ConvertBaseOperation { public: ConvertRGBToYCCOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** Set the YCC mode */ - void setMode(int mode); + void set_mode(int mode); protected: void hash_output_params() override; @@ -137,10 +137,10 @@ class ConvertYCCToRGBOperation : public ConvertBaseOperation { public: ConvertYCCToRGBOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** Set the YCC mode */ - void setMode(int mode); + void set_mode(int mode); protected: void hash_output_params() override; @@ -151,7 +151,7 @@ class ConvertRGBToYUVOperation : public ConvertBaseOperation { public: ConvertRGBToYUVOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -161,7 +161,7 @@ class ConvertYUVToRGBOperation : public ConvertBaseOperation { public: ConvertYUVToRGBOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -171,7 +171,7 @@ class ConvertRGBToHSVOperation : public ConvertBaseOperation { public: ConvertRGBToHSVOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -181,7 +181,7 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation { public: ConvertHSVToRGBOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -191,7 +191,7 @@ class ConvertPremulToStraightOperation : public ConvertBaseOperation { public: ConvertPremulToStraightOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -201,7 +201,7 @@ class ConvertStraightToPremulOperation : public ConvertBaseOperation { public: ConvertStraightToPremulOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -209,17 +209,17 @@ class ConvertStraightToPremulOperation : public ConvertBaseOperation { class SeparateChannelOperation : public MultiThreadedOperation { private: - SocketReader *inputOperation_; + SocketReader *input_operation_; int channel_; public: SeparateChannelOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setChannel(int channel) + void set_channel(int channel) { channel_ = channel; } @@ -231,17 +231,17 @@ class SeparateChannelOperation : public MultiThreadedOperation { class CombineChannelsOperation : public MultiThreadedOperation { private: - SocketReader *inputChannel1Operation_; - SocketReader *inputChannel2Operation_; - SocketReader *inputChannel3Operation_; - SocketReader *inputChannel4Operation_; + SocketReader *input_channel1_operation_; + SocketReader *input_channel2_operation_; + SocketReader *input_channel3_operation_; + SocketReader *input_channel4_operation_; public: CombineChannelsOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc index 343cc32b625..7ba1f0bbe14 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc @@ -20,7 +20,7 @@ namespace blender::compositor { -void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void ConvolutionEdgeFilterOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { float in1[4], in2[4], res1[4] = {0.0}, res2[4] = {0.0}; @@ -30,50 +30,50 @@ void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, int y1 = y - 1; int y2 = y; int y3 = y + 1; - CLAMP(x1, 0, getWidth() - 1); - CLAMP(x2, 0, getWidth() - 1); - CLAMP(x3, 0, getWidth() - 1); - CLAMP(y1, 0, getHeight() - 1); - CLAMP(y2, 0, getHeight() - 1); - CLAMP(y3, 0, getHeight() - 1); + CLAMP(x1, 0, get_width() - 1); + CLAMP(x2, 0, get_width() - 1); + CLAMP(x3, 0, get_width() - 1); + CLAMP(y1, 0, get_height() - 1); + CLAMP(y2, 0, get_height() - 1); + CLAMP(y3, 0, get_height() - 1); float value[4]; - inputValueOperation_->read(value, x2, y2, nullptr); + input_value_operation_->read(value, x2, y2, nullptr); float mval = 1.0f - value[0]; - inputOperation_->read(in1, x1, y1, nullptr); + input_operation_->read(in1, x1, y1, nullptr); madd_v3_v3fl(res1, in1, filter_[0]); madd_v3_v3fl(res2, in1, filter_[0]); - inputOperation_->read(in1, x2, y1, nullptr); + input_operation_->read(in1, x2, y1, nullptr); madd_v3_v3fl(res1, in1, filter_[1]); madd_v3_v3fl(res2, in1, filter_[3]); - inputOperation_->read(in1, x3, y1, nullptr); + input_operation_->read(in1, x3, y1, nullptr); madd_v3_v3fl(res1, in1, filter_[2]); madd_v3_v3fl(res2, in1, filter_[6]); - inputOperation_->read(in1, x1, y2, nullptr); + input_operation_->read(in1, x1, y2, nullptr); madd_v3_v3fl(res1, in1, filter_[3]); madd_v3_v3fl(res2, in1, filter_[1]); - inputOperation_->read(in2, x2, y2, nullptr); + input_operation_->read(in2, x2, y2, nullptr); madd_v3_v3fl(res1, in2, filter_[4]); madd_v3_v3fl(res2, in2, filter_[4]); - inputOperation_->read(in1, x3, y2, nullptr); + input_operation_->read(in1, x3, y2, nullptr); madd_v3_v3fl(res1, in1, filter_[5]); madd_v3_v3fl(res2, in1, filter_[7]); - inputOperation_->read(in1, x1, y3, nullptr); + input_operation_->read(in1, x1, y3, nullptr); madd_v3_v3fl(res1, in1, filter_[6]); madd_v3_v3fl(res2, in1, filter_[2]); - inputOperation_->read(in1, x2, y3, nullptr); + input_operation_->read(in1, x2, y3, nullptr); madd_v3_v3fl(res1, in1, filter_[7]); madd_v3_v3fl(res2, in1, filter_[5]); - inputOperation_->read(in1, x3, y3, nullptr); + input_operation_->read(in1, x3, y3, nullptr); madd_v3_v3fl(res1, in1, filter_[8]); madd_v3_v3fl(res2, in1, filter_[8]); @@ -99,8 +99,8 @@ void ConvolutionEdgeFilterOperation::update_memory_buffer_partial(MemoryBuffer * Span inputs) { const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; - const int last_x = getWidth() - 1; - const int last_y = getHeight() - 1; + const int last_x = get_width() - 1; + const int last_y = get_height() - 1; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const int left_offset = (it.x == 0) ? 0 : -image->elem_stride; const int right_offset = (it.x == last_x) ? 0 : image->elem_stride; diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h index bd38e27165a..a89a9412cf2 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h @@ -24,7 +24,7 @@ namespace blender::compositor { class ConvolutionEdgeFilterOperation : public ConvolutionFilterOperation { public: - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc index a4ef88e7a33..6ee5a1c86f9 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc @@ -22,17 +22,17 @@ namespace blender::compositor { ConvolutionFilterOperation::ConvolutionFilterOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); - inputOperation_ = nullptr; + input_operation_ = nullptr; this->flags.complex = true; } -void ConvolutionFilterOperation::initExecution() +void ConvolutionFilterOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); - inputValueOperation_ = this->getInputSocketReader(1); + input_operation_ = this->get_input_socket_reader(0); + input_value_operation_ = this->get_input_socket_reader(1); } void ConvolutionFilterOperation::set3x3Filter( @@ -47,17 +47,17 @@ void ConvolutionFilterOperation::set3x3Filter( filter_[6] = f7; filter_[7] = f8; filter_[8] = f9; - filterHeight_ = 3; - filterWidth_ = 3; + filter_height_ = 3; + filter_width_ = 3; } -void ConvolutionFilterOperation::deinitExecution() +void ConvolutionFilterOperation::deinit_execution() { - inputOperation_ = nullptr; - inputValueOperation_ = nullptr; + input_operation_ = nullptr; + input_value_operation_ = nullptr; } -void ConvolutionFilterOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void ConvolutionFilterOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { float in1[4]; float in2[4]; @@ -67,34 +67,34 @@ void ConvolutionFilterOperation::executePixel(float output[4], int x, int y, voi int y1 = y - 1; int y2 = y; int y3 = y + 1; - CLAMP(x1, 0, getWidth() - 1); - CLAMP(x2, 0, getWidth() - 1); - CLAMP(x3, 0, getWidth() - 1); - CLAMP(y1, 0, getHeight() - 1); - CLAMP(y2, 0, getHeight() - 1); - CLAMP(y3, 0, getHeight() - 1); + CLAMP(x1, 0, get_width() - 1); + CLAMP(x2, 0, get_width() - 1); + CLAMP(x3, 0, get_width() - 1); + CLAMP(y1, 0, get_height() - 1); + CLAMP(y2, 0, get_height() - 1); + CLAMP(y3, 0, get_height() - 1); float value[4]; - inputValueOperation_->read(value, x2, y2, nullptr); + input_value_operation_->read(value, x2, y2, nullptr); const float mval = 1.0f - value[0]; zero_v4(output); - inputOperation_->read(in1, x1, y1, nullptr); + input_operation_->read(in1, x1, y1, nullptr); madd_v4_v4fl(output, in1, filter_[0]); - inputOperation_->read(in1, x2, y1, nullptr); + input_operation_->read(in1, x2, y1, nullptr); madd_v4_v4fl(output, in1, filter_[1]); - inputOperation_->read(in1, x3, y1, nullptr); + input_operation_->read(in1, x3, y1, nullptr); madd_v4_v4fl(output, in1, filter_[2]); - inputOperation_->read(in1, x1, y2, nullptr); + input_operation_->read(in1, x1, y2, nullptr); madd_v4_v4fl(output, in1, filter_[3]); - inputOperation_->read(in2, x2, y2, nullptr); + input_operation_->read(in2, x2, y2, nullptr); madd_v4_v4fl(output, in2, filter_[4]); - inputOperation_->read(in1, x3, y2, nullptr); + input_operation_->read(in1, x3, y2, nullptr); madd_v4_v4fl(output, in1, filter_[5]); - inputOperation_->read(in1, x1, y3, nullptr); + input_operation_->read(in1, x1, y3, nullptr); madd_v4_v4fl(output, in1, filter_[6]); - inputOperation_->read(in1, x2, y3, nullptr); + input_operation_->read(in1, x2, y3, nullptr); madd_v4_v4fl(output, in1, filter_[7]); - inputOperation_->read(in1, x3, y3, nullptr); + input_operation_->read(in1, x3, y3, nullptr); madd_v4_v4fl(output, in1, filter_[8]); output[0] = output[0] * value[0] + in2[0] * mval; @@ -109,18 +109,18 @@ void ConvolutionFilterOperation::executePixel(float output[4], int x, int y, voi output[3] = MAX2(output[3], 0.0f); } -bool ConvolutionFilterOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool ConvolutionFilterOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - int addx = (filterWidth_ - 1) / 2 + 1; - int addy = (filterHeight_ - 1) / 2 + 1; - newInput.xmax = input->xmax + addx; - newInput.xmin = input->xmin - addx; - newInput.ymax = input->ymax + addy; - newInput.ymin = input->ymin - addy; + rcti new_input; + int addx = (filter_width_ - 1) / 2 + 1; + int addy = (filter_height_ - 1) / 2 + 1; + new_input.xmax = input->xmax + addx; + new_input.xmin = input->xmin - addx; + new_input.ymax = input->ymax + addy; + new_input.ymin = input->ymin - addy; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void ConvolutionFilterOperation::get_area_of_interest(const int input_idx, @@ -129,8 +129,8 @@ void ConvolutionFilterOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - const int add_x = (filterWidth_ - 1) / 2 + 1; - const int add_y = (filterHeight_ - 1) / 2 + 1; + const int add_x = (filter_width_ - 1) / 2 + 1; + const int add_y = (filter_height_ - 1) / 2 + 1; r_input_area.xmin = output_area.xmin - add_x; r_input_area.xmax = output_area.xmax + add_x; r_input_area.ymin = output_area.ymin - add_y; @@ -149,8 +149,8 @@ void ConvolutionFilterOperation::update_memory_buffer_partial(MemoryBuffer *outp Span inputs) { const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; - const int last_x = getWidth() - 1; - const int last_y = getHeight() - 1; + const int last_x = get_width() - 1; + const int last_y = get_height() - 1; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const int left_offset = (it.x == 0) ? 0 : -image->elem_stride; const int right_offset = (it.x == last_x) ? 0 : image->elem_stride; diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h index 7000a0f69d6..d764c7c9081 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h @@ -28,25 +28,25 @@ class ConvolutionFilterOperation : public MultiThreadedOperation { static constexpr int FACTOR_INPUT_INDEX = 1; private: - int filterWidth_; - int filterHeight_; + int filter_width_; + int filter_height_; protected: - SocketReader *inputOperation_; - SocketReader *inputValueOperation_; + SocketReader *input_operation_; + SocketReader *input_value_operation_; float filter_[9]; public: ConvolutionFilterOperation(); void set3x3Filter( float f1, float f2, float f3, float f4, float f5, float f6, float f7, float f8, float f9); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixel(float output[4], int x, int y, void *data) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void initExecution() override; - void deinitExecution() 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) final; virtual void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc index d8a402cc582..5d78ed9d41a 100644 --- a/source/blender/compositor/operations/COM_CropOperation.cc +++ b/source/blender/compositor/operations/COM_CropOperation.cc @@ -22,17 +22,17 @@ namespace blender::compositor { CropBaseOperation::CropBaseOperation() { - this->addInputSocket(DataType::Color, ResizeMode::Align); - this->addOutputSocket(DataType::Color); - inputOperation_ = nullptr; + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_output_socket(DataType::Color); + input_operation_ = nullptr; settings_ = nullptr; } -void CropBaseOperation::updateArea() +void CropBaseOperation::update_area() { - SocketReader *inputReference = this->getInputSocketReader(0); - float width = inputReference->getWidth(); - float height = inputReference->getHeight(); + SocketReader *input_reference = this->get_input_socket_reader(0); + float width = input_reference->get_width(); + float height = input_reference->get_height(); NodeTwoXYs local_settings = *settings_; if (width > 0.0f && height > 0.0f) { @@ -68,15 +68,15 @@ void CropBaseOperation::updateArea() } } -void CropBaseOperation::initExecution() +void CropBaseOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); - updateArea(); + input_operation_ = this->get_input_socket_reader(0); + update_area(); } -void CropBaseOperation::deinitExecution() +void CropBaseOperation::deinit_execution() { - inputOperation_ = nullptr; + input_operation_ = nullptr; } CropOperation::CropOperation() : CropBaseOperation() @@ -84,10 +84,10 @@ CropOperation::CropOperation() : CropBaseOperation() /* pass */ } -void CropOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void CropOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { if ((x < xmax_ && x >= xmin_) && (y < ymax_ && y >= ymin_)) { - inputOperation_->readSampled(output, x, y, sampler); + input_operation_->read_sampled(output, x, y, sampler); } else { zero_v4(output); @@ -115,18 +115,18 @@ CropImageOperation::CropImageOperation() : CropBaseOperation() /* pass */ } -bool CropImageOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool CropImageOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = input->xmax + xmin_; - newInput.xmin = input->xmin + xmin_; - newInput.ymax = input->ymax + ymin_; - newInput.ymin = input->ymin + ymin_; + new_input.xmax = input->xmax + xmin_; + new_input.xmin = input->xmin + xmin_; + new_input.ymax = input->ymax + ymin_; + new_input.ymin = input->ymin + ymin_; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void CropImageOperation::get_area_of_interest(const int input_idx, @@ -144,18 +144,18 @@ void CropImageOperation::get_area_of_interest(const int input_idx, void CropImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { NodeOperation::determine_canvas(preferred_area, r_area); - updateArea(); + update_area(); r_area.xmax = r_area.xmin + (xmax_ - xmin_); r_area.ymax = r_area.ymin + (ymax_ - ymin_); } -void CropImageOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void CropImageOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { - inputOperation_->readSampled(output, (x + xmin_), (y + ymin_), sampler); + if (x >= 0 && x < get_width() && y >= 0 && y < get_height()) { + input_operation_->read_sampled(output, (x + xmin_), (y + ymin_), sampler); } else { zero_v4(output); @@ -167,7 +167,7 @@ void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { rcti op_area; - BLI_rcti_init(&op_area, 0, getWidth(), 0, getHeight()); + BLI_rcti_init(&op_area, 0, get_width(), 0, get_height()); const MemoryBuffer *input = inputs[0]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) { diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h index 50d4d3c5126..69bfd72b052 100644 --- a/source/blender/compositor/operations/COM_CropOperation.h +++ b/source/blender/compositor/operations/COM_CropOperation.h @@ -24,7 +24,7 @@ namespace blender::compositor { class CropBaseOperation : public MultiThreadedOperation { protected: - SocketReader *inputOperation_; + SocketReader *input_operation_; NodeTwoXYs *settings_; bool relative_; int xmax_; @@ -32,17 +32,17 @@ class CropBaseOperation : public MultiThreadedOperation { int ymax_; int ymin_; - void updateArea(); + void update_area(); public: CropBaseOperation(); - void initExecution() override; - void deinitExecution() override; - void setCropSettings(NodeTwoXYs *settings) + void init_execution() override; + void deinit_execution() override; + void set_crop_settings(NodeTwoXYs *settings) { settings_ = settings; } - void setRelative(bool rel) + void set_relative(bool rel) { relative_ = rel; } @@ -52,7 +52,7 @@ class CropOperation : public CropBaseOperation { private: public: CropOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -63,11 +63,11 @@ class CropImageOperation : public CropBaseOperation { private: public: CropImageOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) 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, diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc index a6b9e2c2ee4..078ec9b6dfe 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc @@ -24,27 +24,27 @@ CryptomatteOperation::CryptomatteOperation(size_t num_inputs) { inputs.resize(num_inputs); for (size_t i = 0; i < num_inputs; i++) { - this->addInputSocket(DataType::Color); + this->add_input_socket(DataType::Color); } - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); this->flags.complex = true; } -void CryptomatteOperation::initExecution() +void CryptomatteOperation::init_execution() { for (size_t i = 0; i < inputs.size(); i++) { - inputs[i] = this->getInputSocketReader(i); + inputs[i] = this->get_input_socket_reader(i); } } -void CryptomatteOperation::addObjectIndex(float objectIndex) +void CryptomatteOperation::add_object_index(float object_index) { - if (objectIndex != 0.0f) { - objectIndex_.append(objectIndex); + if (object_index != 0.0f) { + object_index_.append(object_index); } } -void CryptomatteOperation::executePixel(float output[4], int x, int y, void *data) +void CryptomatteOperation::execute_pixel(float output[4], int x, int y, void *data) { float input[4]; output[0] = output[1] = output[2] = output[3] = 0.0f; @@ -60,7 +60,7 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat output[1] = ((float)(m3hash << 8) / (float)UINT32_MAX); output[2] = ((float)(m3hash << 16) / (float)UINT32_MAX); } - for (float hash : objectIndex_) { + for (float hash : object_index_) { if (input[0] == hash) { output[3] += input[1]; } @@ -89,7 +89,7 @@ void CryptomatteOperation::update_memory_buffer_partial(MemoryBuffer *output, it.out[1] = ((float)(m3hash << 8) / (float)UINT32_MAX); it.out[2] = ((float)(m3hash << 16) / (float)UINT32_MAX); } - for (const float hash : objectIndex_) { + for (const float hash : object_index_) { if (input[0] == hash) { it.out[3] += input[1]; } diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.h b/source/blender/compositor/operations/COM_CryptomatteOperation.h index 039b4e3b07a..2fa6fbc8390 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.h +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.h @@ -24,17 +24,17 @@ namespace blender::compositor { class CryptomatteOperation : public MultiThreadedOperation { private: - Vector objectIndex_; + Vector object_index_; public: Vector inputs; CryptomatteOperation(size_t num_inputs = 6); - void initExecution() override; - void executePixel(float output[4], int x, int y, void *data) override; + void init_execution() override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void addObjectIndex(float objectIndex); + void add_object_index(float object_index); void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc index 9ae4cad73d3..c65a1a45750 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc @@ -24,37 +24,37 @@ namespace blender::compositor { CurveBaseOperation::CurveBaseOperation() { - curveMapping_ = nullptr; + curve_mapping_ = nullptr; this->flags.can_be_constant = true; } CurveBaseOperation::~CurveBaseOperation() { - if (curveMapping_) { - BKE_curvemapping_free(curveMapping_); - curveMapping_ = nullptr; + if (curve_mapping_) { + BKE_curvemapping_free(curve_mapping_); + curve_mapping_ = nullptr; } } -void CurveBaseOperation::initExecution() +void CurveBaseOperation::init_execution() { - BKE_curvemapping_init(curveMapping_); + BKE_curvemapping_init(curve_mapping_); } -void CurveBaseOperation::deinitExecution() +void CurveBaseOperation::deinit_execution() { - if (curveMapping_) { - BKE_curvemapping_free(curveMapping_); - curveMapping_ = nullptr; + if (curve_mapping_) { + BKE_curvemapping_free(curve_mapping_); + curve_mapping_ = nullptr; } } -void CurveBaseOperation::setCurveMapping(CurveMapping *mapping) +void CurveBaseOperation::set_curve_mapping(CurveMapping *mapping) { /* duplicate the curve to avoid glitches while drawing, see bug T32374. */ - if (curveMapping_) { - BKE_curvemapping_free(curveMapping_); + if (curve_mapping_) { + BKE_curvemapping_free(curve_mapping_); } - curveMapping_ = BKE_curvemapping_copy(mapping); + curve_mapping_ = BKE_curvemapping_copy(mapping); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h index 70e9fa9da76..d3548c36870 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.h +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h @@ -27,9 +27,9 @@ namespace blender::compositor { class CurveBaseOperation : public MultiThreadedOperation { protected: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - CurveMapping *curveMapping_; + CurveMapping *curve_mapping_; public: CurveBaseOperation(); @@ -38,10 +38,10 @@ class CurveBaseOperation : public MultiThreadedOperation { /** * Initialize the execution */ - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setCurveMapping(CurveMapping *mapping); + void set_curve_mapping(CurveMapping *mapping); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index 2e41181559e..dd8e6df69fc 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -81,10 +81,10 @@ class DenoiseFilter { BLI_assert(initialized_); BLI_assert(!buffer->is_a_single_elem()); filter.setImage(name.data(), - buffer->getBuffer(), + buffer->get_buffer(), oidn::Format::Float3, - buffer->getWidth(), - buffer->getHeight(), + buffer->get_width(), + buffer->get_height(), 0, buffer->get_elem_bytes_len()); } @@ -131,20 +131,19 @@ DenoiseBaseOperation::DenoiseBaseOperation() output_rendered_ = false; } -bool DenoiseBaseOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool DenoiseBaseOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - if (isCached()) { + if (is_cached()) { return false; } - rcti newInput; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + rcti new_input; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void DenoiseBaseOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -156,26 +155,26 @@ void DenoiseBaseOperation::get_area_of_interest(const int UNUSED(input_idx), DenoiseOperation::DenoiseOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Vector); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Vector); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); settings_ = nullptr; } -void DenoiseOperation::initExecution() +void DenoiseOperation::init_execution() { - SingleThreadedOperation::initExecution(); - inputProgramColor_ = getInputSocketReader(0); - inputProgramNormal_ = getInputSocketReader(1); - inputProgramAlbedo_ = getInputSocketReader(2); + SingleThreadedOperation::init_execution(); + input_program_color_ = get_input_socket_reader(0); + input_program_normal_ = get_input_socket_reader(1); + input_program_albedo_ = get_input_socket_reader(2); } -void DenoiseOperation::deinitExecution() +void DenoiseOperation::deinit_execution() { - inputProgramColor_ = nullptr; - inputProgramNormal_ = nullptr; - inputProgramAlbedo_ = nullptr; - SingleThreadedOperation::deinitExecution(); + input_program_color_ = nullptr; + input_program_normal_ = nullptr; + input_program_albedo_ = nullptr; + SingleThreadedOperation::deinit_execution(); } static bool are_guiding_passes_noise_free(NodeDenoise *settings) @@ -197,29 +196,29 @@ void DenoiseOperation::hash_output_params() } } -MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2) +MemoryBuffer *DenoiseOperation::create_memory_buffer(rcti *rect2) { - MemoryBuffer *tileColor = (MemoryBuffer *)inputProgramColor_->initializeTileData(rect2); - MemoryBuffer *tileNormal = (MemoryBuffer *)inputProgramNormal_->initializeTileData(rect2); - MemoryBuffer *tileAlbedo = (MemoryBuffer *)inputProgramAlbedo_->initializeTileData(rect2); + MemoryBuffer *tile_color = (MemoryBuffer *)input_program_color_->initialize_tile_data(rect2); + MemoryBuffer *tile_normal = (MemoryBuffer *)input_program_normal_->initialize_tile_data(rect2); + MemoryBuffer *tile_albedo = (MemoryBuffer *)input_program_albedo_->initialize_tile_data(rect2); rcti rect; rect.xmin = 0; rect.ymin = 0; - rect.xmax = getWidth(); - rect.ymax = getHeight(); + rect.xmax = get_width(); + rect.ymax = get_height(); MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); - this->generateDenoise(result, tileColor, tileNormal, tileAlbedo, settings_); + this->generate_denoise(result, tile_color, tile_normal, tile_albedo, settings_); return result; } -void DenoiseOperation::generateDenoise(MemoryBuffer *output, - MemoryBuffer *input_color, - MemoryBuffer *input_normal, - MemoryBuffer *input_albedo, - NodeDenoise *settings) +void DenoiseOperation::generate_denoise(MemoryBuffer *output, + MemoryBuffer *input_color, + MemoryBuffer *input_normal, + MemoryBuffer *input_albedo, + NodeDenoise *settings) { - BLI_assert(input_color->getBuffer()); - if (!input_color->getBuffer()) { + BLI_assert(input_color->get_buffer()); + if (!input_color->get_buffer()) { return; } @@ -244,7 +243,7 @@ void DenoiseOperation::generateDenoise(MemoryBuffer *output, if (settings) { filter.set("hdr", settings->hdr); filter.set("srgb", false); - filter.set("cleanAux", are_guiding_passes_noise_free(settings)); + filter.set("clean_aux", are_guiding_passes_noise_free(settings)); } filter.execute(); @@ -270,15 +269,15 @@ void DenoiseOperation::update_memory_buffer(MemoryBuffer *output, Span inputs) { if (!output_rendered_) { - this->generateDenoise(output, inputs[0], inputs[1], inputs[2], settings_); + this->generate_denoise(output, inputs[0], inputs[1], inputs[2], settings_); output_rendered_ = true; } } DenoisePrefilterOperation::DenoisePrefilterOperation(DataType data_type) { - this->addInputSocket(data_type); - this->addOutputSocket(data_type); + this->add_input_socket(data_type); + this->add_output_socket(data_type); image_name_ = ""; } @@ -287,13 +286,13 @@ void DenoisePrefilterOperation::hash_output_params() hash_param(image_name_); } -MemoryBuffer *DenoisePrefilterOperation::createMemoryBuffer(rcti *rect2) +MemoryBuffer *DenoisePrefilterOperation::create_memory_buffer(rcti *rect2) { - MemoryBuffer *input = (MemoryBuffer *)this->get_input_operation(0)->initializeTileData(rect2); + MemoryBuffer *input = (MemoryBuffer *)this->get_input_operation(0)->initialize_tile_data(rect2); rcti rect; - BLI_rcti_init(&rect, 0, getWidth(), 0, getHeight()); + BLI_rcti_init(&rect, 0, get_width(), 0, get_height()); - MemoryBuffer *result = new MemoryBuffer(getOutputSocket()->getDataType(), rect); + MemoryBuffer *result = new MemoryBuffer(get_output_socket()->get_data_type(), rect); generate_denoise(result, input); return result; diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h index 24852009e6d..a3eb1f57f2f 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.h +++ b/source/blender/compositor/operations/COM_DenoiseOperation.h @@ -33,9 +33,9 @@ class DenoiseBaseOperation : public SingleThreadedOperation { DenoiseBaseOperation(); public: - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; }; @@ -45,9 +45,9 @@ class DenoiseOperation : public DenoiseBaseOperation { /** * \brief Cached reference to the input programs */ - SocketReader *inputProgramColor_; - SocketReader *inputProgramAlbedo_; - SocketReader *inputProgramNormal_; + SocketReader *input_program_color_; + SocketReader *input_program_albedo_; + SocketReader *input_program_normal_; /** * \brief settings of the denoise node. @@ -59,14 +59,14 @@ class DenoiseOperation : public DenoiseBaseOperation { /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setDenoiseSettings(NodeDenoise *settings) + void set_denoise_settings(NodeDenoise *settings) { settings_ = settings; } @@ -77,13 +77,13 @@ class DenoiseOperation : public DenoiseBaseOperation { protected: void hash_output_params() override; - void generateDenoise(MemoryBuffer *output, - MemoryBuffer *input_color, - MemoryBuffer *input_normal, - MemoryBuffer *input_albedo, - NodeDenoise *settings); + void generate_denoise(MemoryBuffer *output, + MemoryBuffer *input_color, + MemoryBuffer *input_normal, + MemoryBuffer *input_albedo, + NodeDenoise *settings); - MemoryBuffer *createMemoryBuffer(rcti *rect) override; + MemoryBuffer *create_memory_buffer(rcti *rect) override; }; class DenoisePrefilterOperation : public DenoiseBaseOperation { @@ -104,7 +104,7 @@ class DenoisePrefilterOperation : public DenoiseBaseOperation { protected: void hash_output_params() override; - MemoryBuffer *createMemoryBuffer(rcti *rect) override; + MemoryBuffer *create_memory_buffer(rcti *rect) override; private: void generate_denoise(MemoryBuffer *output, MemoryBuffer *input); diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc index f655c17fdf2..be0dc06b8ec 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.cc +++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc @@ -24,23 +24,23 @@ namespace blender::compositor { DespeckleOperation::DespeckleOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); - inputOperation_ = nullptr; + input_operation_ = nullptr; this->flags.complex = true; } -void DespeckleOperation::initExecution() +void DespeckleOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); - inputValueOperation_ = this->getInputSocketReader(1); + input_operation_ = this->get_input_socket_reader(0); + input_value_operation_ = this->get_input_socket_reader(1); } -void DespeckleOperation::deinitExecution() +void DespeckleOperation::deinit_execution() { - inputOperation_ = nullptr; - inputValueOperation_ = nullptr; + input_operation_ = nullptr; + input_value_operation_ = nullptr; } BLI_INLINE int color_diff(const float a[3], const float b[3], const float threshold) @@ -49,7 +49,7 @@ BLI_INLINE int color_diff(const float a[3], const float b[3], const float thresh (fabsf(a[2] - b[2]) > threshold)); } -void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void DespeckleOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { float w = 0.0f; float color_org[4]; @@ -62,17 +62,17 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da int y1 = y - 1; int y2 = y; int y3 = y + 1; - CLAMP(x1, 0, getWidth() - 1); - CLAMP(x2, 0, getWidth() - 1); - CLAMP(x3, 0, getWidth() - 1); - CLAMP(y1, 0, getHeight() - 1); - CLAMP(y2, 0, getHeight() - 1); - CLAMP(y3, 0, getHeight() - 1); + CLAMP(x1, 0, get_width() - 1); + CLAMP(x2, 0, get_width() - 1); + CLAMP(x3, 0, get_width() - 1); + CLAMP(y1, 0, get_height() - 1); + CLAMP(y2, 0, get_height() - 1); + CLAMP(y3, 0, get_height() - 1); float value[4]; - inputValueOperation_->read(value, x2, y2, nullptr); + input_value_operation_->read(value, x2, y2, nullptr); // const float mval = 1.0f - value[0]; - inputOperation_->read(color_org, x2, y2, nullptr); + input_operation_->read(color_org, x2, y2, nullptr); #define TOT_DIV_ONE 1.0f #define TOT_DIV_CNR (float)M_SQRT1_2 @@ -91,27 +91,27 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da zero_v4(color_mid); zero_v4(color_mid_ok); - inputOperation_->read(in1, x1, y1, nullptr); + input_operation_->read(in1, x1, y1, nullptr); COLOR_ADD(TOT_DIV_CNR) - inputOperation_->read(in1, x2, y1, nullptr); + input_operation_->read(in1, x2, y1, nullptr); COLOR_ADD(TOT_DIV_ONE) - inputOperation_->read(in1, x3, y1, nullptr); + input_operation_->read(in1, x3, y1, nullptr); COLOR_ADD(TOT_DIV_CNR) - inputOperation_->read(in1, x1, y2, nullptr); + input_operation_->read(in1, x1, y2, nullptr); COLOR_ADD(TOT_DIV_ONE) #if 0 - inputOperation_->read(in2, x2, y2, nullptr); + input_operation_->read(in2, x2, y2, nullptr); madd_v4_v4fl(color_mid, in2, filter_[4]); #endif - inputOperation_->read(in1, x3, y2, nullptr); + input_operation_->read(in1, x3, y2, nullptr); COLOR_ADD(TOT_DIV_ONE) - inputOperation_->read(in1, x1, y3, nullptr); + input_operation_->read(in1, x1, y3, nullptr); COLOR_ADD(TOT_DIV_CNR) - inputOperation_->read(in1, x2, y3, nullptr); + input_operation_->read(in1, x2, y3, nullptr); COLOR_ADD(TOT_DIV_ONE) - inputOperation_->read(in1, x3, y3, nullptr); + input_operation_->read(in1, x3, y3, nullptr); COLOR_ADD(TOT_DIV_CNR) mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2))); @@ -132,19 +132,19 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da #undef COLOR_ADD } -bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool DespeckleOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; - int addx = 2; //(filterWidth_ - 1) / 2 + 1; - int addy = 2; //(filterHeight_ - 1) / 2 + 1; - newInput.xmax = input->xmax + addx; - newInput.xmin = input->xmin - addx; - newInput.ymax = input->ymax + addy; - newInput.ymin = input->ymin - addy; + rcti new_input; + int addx = 2; //(filter_width_ - 1) / 2 + 1; + int addy = 2; //(filter_height_ - 1) / 2 + 1; + new_input.xmax = input->xmax + addx; + new_input.xmin = input->xmin - addx; + new_input.ymax = input->ymax + addy; + new_input.ymin = input->ymin - addy; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void DespeckleOperation::get_area_of_interest(const int input_idx, @@ -153,8 +153,8 @@ void DespeckleOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - const int add_x = 2; //(filterWidth_ - 1) / 2 + 1; - const int add_y = 2; //(filterHeight_ - 1) / 2 + 1; + const int add_x = 2; //(filter_width_ - 1) / 2 + 1; + const int add_y = 2; //(filter_height_ - 1) / 2 + 1; r_input_area.xmin = output_area.xmin - add_x; r_input_area.xmax = output_area.xmax + add_x; r_input_area.ymin = output_area.ymin - add_y; @@ -173,8 +173,8 @@ void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; - const int last_x = getWidth() - 1; - const int last_y = getHeight() - 1; + const int last_x = get_width() - 1; + const int last_y = get_height() - 1; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const int x1 = MAX2(it.x - 1, 0); const int x2 = it.x; diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.h b/source/blender/compositor/operations/COM_DespeckleOperation.h index 9aca01000a7..bb5b012a330 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.h +++ b/source/blender/compositor/operations/COM_DespeckleOperation.h @@ -30,31 +30,31 @@ class DespeckleOperation : public MultiThreadedOperation { float threshold_; float threshold_neighbor_; - // int filterWidth_; - // int filterHeight_; + // int filter_width_; + // int filter_height_; protected: - SocketReader *inputOperation_; - SocketReader *inputValueOperation_; + SocketReader *input_operation_; + SocketReader *input_value_operation_; public: DespeckleOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixel(float output[4], int x, int y, void *data) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void setThreshold(float threshold) + void set_threshold(float threshold) { threshold_ = threshold; } - void setThresholdNeighbor(float threshold) + void set_threshold_neighbor(float threshold) { threshold_neighbor_ = threshold; } - void initExecution() override; - void deinitExecution() 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, diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc index d5f11ff8e35..8391bbb799a 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc @@ -22,44 +22,44 @@ namespace blender::compositor { DifferenceMatteOperation::DifferenceMatteOperation() { - addInputSocket(DataType::Color); - addInputSocket(DataType::Color); - addOutputSocket(DataType::Value); + add_input_socket(DataType::Color); + add_input_socket(DataType::Color); + add_output_socket(DataType::Value); - inputImage1Program_ = nullptr; - inputImage2Program_ = nullptr; + input_image1_program_ = nullptr; + input_image2_program_ = nullptr; flags.can_be_constant = true; } -void DifferenceMatteOperation::initExecution() +void DifferenceMatteOperation::init_execution() { - inputImage1Program_ = this->getInputSocketReader(0); - inputImage2Program_ = this->getInputSocketReader(1); + input_image1_program_ = this->get_input_socket_reader(0); + input_image2_program_ = this->get_input_socket_reader(1); } -void DifferenceMatteOperation::deinitExecution() +void DifferenceMatteOperation::deinit_execution() { - inputImage1Program_ = nullptr; - inputImage2Program_ = nullptr; + input_image1_program_ = nullptr; + input_image2_program_ = nullptr; } -void DifferenceMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void DifferenceMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inColor1[4]; - float inColor2[4]; + float in_color1[4]; + float in_color2[4]; const float tolerance = settings_->t1; const float falloff = settings_->t2; float difference; float alpha; - inputImage1Program_->readSampled(inColor1, x, y, sampler); - inputImage2Program_->readSampled(inColor2, x, y, sampler); + input_image1_program_->read_sampled(in_color1, x, y, sampler); + input_image2_program_->read_sampled(in_color2, x, y, sampler); - difference = (fabsf(inColor2[0] - inColor1[0]) + fabsf(inColor2[1] - inColor1[1]) + - fabsf(inColor2[2] - inColor1[2])); + difference = (fabsf(in_color2[0] - in_color1[0]) + fabsf(in_color2[1] - in_color1[1]) + + fabsf(in_color2[2] - in_color1[2])); /* average together the distances */ difference = difference / 3.0f; @@ -73,16 +73,16 @@ void DifferenceMatteOperation::executePixelSampled(float output[4], difference = difference - tolerance; alpha = difference / falloff; /* Only change if more transparent than before. */ - if (alpha < inColor1[3]) { + if (alpha < in_color1[3]) { output[0] = alpha; } else { /* leave as before */ - output[0] = inColor1[3]; + output[0] = in_color1[3]; } } else { /* foreground object */ - output[0] = inColor1[3]; + output[0] = in_color1[3]; } } diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h index 31ce0d3dbe5..700dabc9b8d 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h @@ -29,8 +29,8 @@ namespace blender::compositor { class DifferenceMatteOperation : public MultiThreadedOperation { private: NodeChroma *settings_; - SocketReader *inputImage1Program_; - SocketReader *inputImage2Program_; + SocketReader *input_image1_program_; + SocketReader *input_image2_program_; public: /** @@ -41,14 +41,14 @@ class DifferenceMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma) + void set_settings(NodeChroma *node_chroma) { - settings_ = nodeChroma; + settings_ = node_chroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index 1366eb43776..7fc186df35f 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -24,10 +24,10 @@ namespace blender::compositor { /* DilateErode Distance Threshold */ DilateErodeThresholdOperation::DilateErodeThresholdOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); this->flags.complex = true; - inputProgram_ = nullptr; + input_program_ = nullptr; inset_ = 0.0f; switch_ = 0.5f; distance_ = 0.0f; @@ -51,20 +51,20 @@ void DilateErodeThresholdOperation::init_data() } } -void DilateErodeThresholdOperation::initExecution() +void DilateErodeThresholdOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void *DilateErodeThresholdOperation::initializeTileData(rcti * /*rect*/) +void *DilateErodeThresholdOperation::initialize_tile_data(rcti * /*rect*/) { - void *buffer = inputProgram_->initializeTileData(nullptr); + void *buffer = input_program_->initialize_tile_data(nullptr); return buffer; } -void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, void *data) +void DilateErodeThresholdOperation::execute_pixel(float output[4], int x, int y, void *data) { - float inputValue[4]; + float input_value[4]; const float sw = switch_; const float distance = distance_; float pixelvalue; @@ -72,21 +72,21 @@ void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, const float inset = inset_; float mindist = rd * 2; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); - const rcti &input_rect = inputBuffer->get_rect(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); + const rcti &input_rect = input_buffer->get_rect(); const int minx = MAX2(x - scope_, input_rect.xmin); const int miny = MAX2(y - scope_, input_rect.ymin); const int maxx = MIN2(x + scope_, input_rect.xmax); const int maxy = MIN2(y + scope_, input_rect.ymax); - const int bufferWidth = inputBuffer->getWidth(); + const int buffer_width = input_buffer->get_width(); int offset; - inputBuffer->read(inputValue, x, y); - if (inputValue[0] > sw) { + input_buffer->read(input_value, x, y); + if (input_value[0] > sw) { for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - input_rect.ymin) * bufferWidth + (minx - input_rect.xmin)); + offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin)); for (int xi = minx; xi < maxx; xi++) { if (buffer[offset] < sw) { const float dx = xi - x; @@ -101,7 +101,7 @@ void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, else { for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - input_rect.ymin) * bufferWidth + (minx - input_rect.xmin)); + offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin)); for (int xi = minx; xi < maxx; xi++) { if (buffer[offset] > sw) { const float dx = xi - x; @@ -144,22 +144,22 @@ void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, } } -void DilateErodeThresholdOperation::deinitExecution() +void DilateErodeThresholdOperation::deinit_execution() { - inputProgram_ = nullptr; + input_program_ = nullptr; } -bool DilateErodeThresholdOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool DilateErodeThresholdOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = input->xmax + scope_; - newInput.xmin = input->xmin - scope_; - newInput.ymax = input->ymax + scope_; - newInput.ymin = input->ymin - scope_; + new_input.xmax = input->xmax + scope_; + new_input.xmin = input->xmin - scope_; + new_input.ymax = input->ymax + scope_; + new_input.ymin = input->ymin - scope_; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void DilateErodeThresholdOperation::get_area_of_interest(const int input_idx, @@ -271,9 +271,9 @@ void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *o /* Dilate Distance. */ DilateDistanceOperation::DilateDistanceOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - inputProgram_ = nullptr; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_program_ = nullptr; distance_ = 0.0f; flags.complex = true; flags.open_cl = true; @@ -287,37 +287,37 @@ void DilateDistanceOperation::init_data() } } -void DilateDistanceOperation::initExecution() +void DilateDistanceOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void *DilateDistanceOperation::initializeTileData(rcti * /*rect*/) +void *DilateDistanceOperation::initialize_tile_data(rcti * /*rect*/) { - void *buffer = inputProgram_->initializeTileData(nullptr); + void *buffer = input_program_->initialize_tile_data(nullptr); return buffer; } -void DilateDistanceOperation::executePixel(float output[4], int x, int y, void *data) +void DilateDistanceOperation::execute_pixel(float output[4], int x, int y, void *data) { const float distance = distance_; const float mindist = distance * distance; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); - const rcti &input_rect = inputBuffer->get_rect(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); + const rcti &input_rect = input_buffer->get_rect(); const int minx = MAX2(x - scope_, input_rect.xmin); const int miny = MAX2(y - scope_, input_rect.ymin); const int maxx = MIN2(x + scope_, input_rect.xmax); const int maxy = MIN2(y + scope_, input_rect.ymax); - const int bufferWidth = inputBuffer->getWidth(); + const int buffer_width = input_buffer->get_width(); int offset; float value = 0.0f; for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - input_rect.ymin) * bufferWidth + (minx - input_rect.xmin)); + offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin)); for (int xi = minx; xi < maxx; xi++) { const float dx = xi - x; const float dis = dx * dx + dy * dy; @@ -330,45 +330,46 @@ void DilateDistanceOperation::executePixel(float output[4], int x, int y, void * output[0] = value; } -void DilateDistanceOperation::deinitExecution() +void DilateDistanceOperation::deinit_execution() { - inputProgram_ = nullptr; + input_program_ = nullptr; } -bool DilateDistanceOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool DilateDistanceOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = input->xmax + scope_; - newInput.xmin = input->xmin - scope_; - newInput.ymax = input->ymax + scope_; - newInput.ymin = input->ymin - scope_; + new_input.xmax = input->xmax + scope_; + new_input.xmin = input->xmin - scope_; + new_input.ymax = input->ymax + scope_; + new_input.ymin = input->ymin - scope_; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } -void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void DilateDistanceOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel dilateKernel = device->COM_clCreateKernel("dilateKernel", nullptr); + cl_kernel dilate_kernel = device->COM_cl_create_kernel("dilate_kernel", nullptr); - cl_int distanceSquared = distance_ * distance_; + cl_int distance_squared = distance_ * distance_; cl_int scope = scope_; - device->COM_clAttachMemoryBufferToKernelParameter( - dilateKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, inputProgram_); - device->COM_clAttachOutputMemoryBufferToKernelParameter(dilateKernel, 1, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter(dilateKernel, 3, outputMemoryBuffer); - clSetKernelArg(dilateKernel, 4, sizeof(cl_int), &scope); - clSetKernelArg(dilateKernel, 5, sizeof(cl_int), &distanceSquared); - device->COM_clAttachSizeToKernelParameter(dilateKernel, 6, this); - device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + dilate_kernel, 0, 2, cl_mem_to_clean_up, input_memory_buffers, input_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + dilate_kernel, 1, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + dilate_kernel, 3, output_memory_buffer); + clSetKernelArg(dilate_kernel, 4, sizeof(cl_int), &scope); + clSetKernelArg(dilate_kernel, 5, sizeof(cl_int), &distance_squared); + device->COM_cl_attach_size_to_kernel_parameter(dilate_kernel, 6, this); + device->COM_cl_enqueue_range(dilate_kernel, output_memory_buffer, 7, this); } void DilateDistanceOperation::get_area_of_interest(const int input_idx, @@ -463,26 +464,26 @@ ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() /* pass */ } -void ErodeDistanceOperation::executePixel(float output[4], int x, int y, void *data) +void ErodeDistanceOperation::execute_pixel(float output[4], int x, int y, void *data) { const float distance = distance_; const float mindist = distance * distance; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); - const rcti &input_rect = inputBuffer->get_rect(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); + const rcti &input_rect = input_buffer->get_rect(); const int minx = MAX2(x - scope_, input_rect.xmin); const int miny = MAX2(y - scope_, input_rect.ymin); const int maxx = MIN2(x + scope_, input_rect.xmax); const int maxy = MIN2(y + scope_, input_rect.ymax); - const int bufferWidth = inputBuffer->getWidth(); + const int buffer_width = input_buffer->get_width(); int offset; float value = 1.0f; for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - input_rect.ymin) * bufferWidth + (minx - input_rect.xmin)); + offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin)); for (int xi = minx; xi < maxx; xi++) { const float dx = xi - x; const float dis = dx * dx + dy * dy; @@ -495,26 +496,28 @@ void ErodeDistanceOperation::executePixel(float output[4], int x, int y, void *d output[0] = value; } -void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void ErodeDistanceOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel erodeKernel = device->COM_clCreateKernel("erodeKernel", nullptr); + cl_kernel erode_kernel = device->COM_cl_create_kernel("erode_kernel", nullptr); - cl_int distanceSquared = distance_ * distance_; + cl_int distance_squared = distance_ * distance_; cl_int scope = scope_; - device->COM_clAttachMemoryBufferToKernelParameter( - erodeKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, inputProgram_); - device->COM_clAttachOutputMemoryBufferToKernelParameter(erodeKernel, 1, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter(erodeKernel, 3, outputMemoryBuffer); - clSetKernelArg(erodeKernel, 4, sizeof(cl_int), &scope); - clSetKernelArg(erodeKernel, 5, sizeof(cl_int), &distanceSquared); - device->COM_clAttachSizeToKernelParameter(erodeKernel, 6, this); - device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + erode_kernel, 0, 2, cl_mem_to_clean_up, input_memory_buffers, input_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + erode_kernel, 1, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + erode_kernel, 3, output_memory_buffer); + clSetKernelArg(erode_kernel, 4, sizeof(cl_int), &scope); + clSetKernelArg(erode_kernel, 5, sizeof(cl_int), &distance_squared); + device->COM_cl_attach_size_to_kernel_parameter(erode_kernel, 6, this); + device->COM_cl_enqueue_range(erode_kernel, output_memory_buffer, 7, this); } void ErodeDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -531,17 +534,17 @@ void ErodeDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, /* Dilate step */ DilateStepOperation::DilateStepOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); this->flags.complex = true; - inputProgram_ = nullptr; + input_program_ = nullptr; } -void DilateStepOperation::initExecution() +void DilateStepOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -/* Small helper to pass data from initializeTileData to executePixel. */ +/* Small helper to pass data from initialize_tile_data to execute_pixel. */ struct tile_info { rcti rect; int width; @@ -561,13 +564,13 @@ static tile_info *create_cache(int xmin, int xmax, int ymin, int ymax) return result; } -void *DilateStepOperation::initializeTileData(rcti *rect) +void *DilateStepOperation::initialize_tile_data(rcti *rect) { - MemoryBuffer *tile = (MemoryBuffer *)inputProgram_->initializeTileData(nullptr); + MemoryBuffer *tile = (MemoryBuffer *)input_program_->initialize_tile_data(nullptr); int x, y, i; - int width = tile->getWidth(); - int height = tile->getHeight(); - float *buffer = tile->getBuffer(); + int width = tile->get_width(); + int height = tile->get_height(); + float *buffer = tile->get_buffer(); int half_window = iterations_; int window = half_window * 2 + 1; @@ -651,7 +654,7 @@ void *DilateStepOperation::initializeTileData(rcti *rect) return result; } -void DilateStepOperation::executePixel(float output[4], int x, int y, void *data) +void DilateStepOperation::execute_pixel(float output[4], int x, int y, void *data) { tile_info *tile = (tile_info *)data; int nx = x - tile->rect.xmin; @@ -659,30 +662,30 @@ void DilateStepOperation::executePixel(float output[4], int x, int y, void *data output[0] = tile->buffer[tile->width * ny + nx]; } -void DilateStepOperation::deinitExecution() +void DilateStepOperation::deinit_execution() { - inputProgram_ = nullptr; + input_program_ = nullptr; } -void DilateStepOperation::deinitializeTileData(rcti * /*rect*/, void *data) +void DilateStepOperation::deinitialize_tile_data(rcti * /*rect*/, void *data) { tile_info *tile = (tile_info *)data; MEM_freeN(tile->buffer); MEM_freeN(tile); } -bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool DilateStepOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; int it = iterations_; - newInput.xmax = input->xmax + it; - newInput.xmin = input->xmin - it; - newInput.ymax = input->ymax + it; - newInput.ymin = input->ymin - it; + new_input.xmax = input->xmax + it; + new_input.xmin = input->xmin - it; + new_input.ymax = input->ymax + it; + new_input.ymin = input->ymin - it; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void DilateStepOperation::get_area_of_interest(const int input_idx, @@ -706,8 +709,8 @@ static void step_update_memory_buffer(MemoryBuffer *output, { TCompareSelector selector; - const int width = output->getWidth(); - const int height = output->getHeight(); + const int width = output->get_width(); + const int height = output->get_height(); const int half_window = num_iterations; const int window = half_window * 2 + 1; @@ -812,13 +815,13 @@ ErodeStepOperation::ErodeStepOperation() : DilateStepOperation() /* pass */ } -void *ErodeStepOperation::initializeTileData(rcti *rect) +void *ErodeStepOperation::initialize_tile_data(rcti *rect) { - MemoryBuffer *tile = (MemoryBuffer *)inputProgram_->initializeTileData(nullptr); + MemoryBuffer *tile = (MemoryBuffer *)input_program_->initialize_tile_data(nullptr); int x, y, i; - int width = tile->getWidth(); - int height = tile->getHeight(); - float *buffer = tile->getBuffer(); + int width = tile->get_width(); + int height = tile->get_height(); + float *buffer = tile->get_buffer(); int half_window = iterations_; int window = half_window * 2 + 1; diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.h b/source/blender/compositor/operations/COM_DilateErodeOperation.h index 1919c926710..04d25a1fca6 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.h +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.h @@ -28,9 +28,9 @@ class DilateErodeThresholdOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; float distance_; float switch_; @@ -48,36 +48,36 @@ class DilateErodeThresholdOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void init_data() override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setDistance(float distance) + void set_distance(float distance) { distance_ = distance; } - void setSwitch(float sw) + void set_switch(float sw) { switch_ = sw; } - void setInset(float inset) + void set_inset(float inset) { inset_ = inset; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) 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, @@ -91,9 +91,9 @@ class DilateDistanceOperation : public MultiThreadedOperation { protected: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; float distance_; int scope_; @@ -103,34 +103,34 @@ class DilateDistanceOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void init_data() override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setDistance(float distance) + void set_distance(float distance) { distance_ = distance; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; virtual void update_memory_buffer_partial(MemoryBuffer *output, @@ -145,14 +145,14 @@ class ErodeDistanceOperation : public DilateDistanceOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -162,9 +162,9 @@ class ErodeDistanceOperation : public DilateDistanceOperation { class DilateStepOperation : public MultiThreadedOperation { protected: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; int iterations_; @@ -174,28 +174,28 @@ class DilateStepOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; - void deinitializeTileData(rcti *rect, void *data) override; + void deinit_execution() override; + void deinitialize_tile_data(rcti *rect, void *data) override; - void setIterations(int iterations) + void set_iterations(int iterations) { iterations_ = iterations; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; virtual void update_memory_buffer_partial(MemoryBuffer *output, @@ -207,7 +207,7 @@ class ErodeStepOperation : public DilateStepOperation { public: ErodeStepOperation(); - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc index 3cf3ee3b0c8..eff7fa84475 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc @@ -23,17 +23,17 @@ namespace blender::compositor { DirectionalBlurOperation::DirectionalBlurOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); flags.complex = true; flags.open_cl = true; - inputProgram_ = nullptr; + input_program_ = nullptr; } -void DirectionalBlurOperation::initExecution() +void DirectionalBlurOperation::init_execution() { - inputProgram_ = getInputSocketReader(0); - QualityStepHelper::initExecution(COM_QH_INCREASE); + input_program_ = get_input_socket_reader(0); + QualityStepHelper::init_execution(COM_QH_INCREASE); const float angle = data_->angle; const float zoom = data_->zoom; const float spin = data_->spin; @@ -41,8 +41,8 @@ void DirectionalBlurOperation::initExecution() const float distance = data_->distance; const float center_x = data_->center_x; const float center_y = data_->center_y; - const float width = getWidth(); - const float height = getHeight(); + const float width = get_width(); + const float height = get_height(); const float a = angle; const float itsc = 1.0f / powf(2.0f, (float)iterations); @@ -58,12 +58,12 @@ void DirectionalBlurOperation::initExecution() rot_ = itsc * spin; } -void DirectionalBlurOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void DirectionalBlurOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { const int iterations = pow(2.0f, data_->iter); float col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float col2[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - inputProgram_->readSampled(col2, x, y, PixelSampler::Bilinear); + input_program_->read_sampled(col2, x, y, PixelSampler::Bilinear); float ltx = tx_; float lty = ty_; float lsc = sc_; @@ -76,10 +76,10 @@ void DirectionalBlurOperation::executePixel(float output[4], int x, int y, void const float v = isc * (y - center_y_pix_) + lty; const float u = isc * (x - center_x_pix_) + ltx; - inputProgram_->readSampled(col, - cs * u + ss * v + center_x_pix_, - cs * v - ss * u + center_y_pix_, - PixelSampler::Bilinear); + input_program_->read_sampled(col, + cs * u + ss * v + center_x_pix_, + cs * v - ss * u + center_y_pix_, + PixelSampler::Bilinear); add_v4_v4(col2, col); @@ -93,14 +93,15 @@ void DirectionalBlurOperation::executePixel(float output[4], int x, int y, void mul_v4_v4fl(output, col2, 1.0f / (iterations + 1)); } -void DirectionalBlurOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void DirectionalBlurOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel directionalBlurKernel = device->COM_clCreateKernel("directionalBlurKernel", nullptr); + cl_kernel directional_blur_kernel = device->COM_cl_create_kernel("directional_blur_kernel", + nullptr); cl_int iterations = pow(2.0f, data_->iter); cl_float2 ltxy = {{tx_, ty_}}; @@ -108,38 +109,37 @@ void DirectionalBlurOperation::executeOpenCL(OpenCLDevice *device, cl_float lsc = sc_; cl_float lrot = rot_; - device->COM_clAttachMemoryBufferToKernelParameter( - directionalBlurKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, inputProgram_); - device->COM_clAttachOutputMemoryBufferToKernelParameter( - directionalBlurKernel, 1, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter( - directionalBlurKernel, 2, outputMemoryBuffer); - clSetKernelArg(directionalBlurKernel, 3, sizeof(cl_int), &iterations); - clSetKernelArg(directionalBlurKernel, 4, sizeof(cl_float), &lsc); - clSetKernelArg(directionalBlurKernel, 5, sizeof(cl_float), &lrot); - clSetKernelArg(directionalBlurKernel, 6, sizeof(cl_float2), <xy); - clSetKernelArg(directionalBlurKernel, 7, sizeof(cl_float2), ¢erpix); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + directional_blur_kernel, 0, -1, cl_mem_to_clean_up, input_memory_buffers, input_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + directional_blur_kernel, 1, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + directional_blur_kernel, 2, output_memory_buffer); + clSetKernelArg(directional_blur_kernel, 3, sizeof(cl_int), &iterations); + clSetKernelArg(directional_blur_kernel, 4, sizeof(cl_float), &lsc); + clSetKernelArg(directional_blur_kernel, 5, sizeof(cl_float), &lrot); + clSetKernelArg(directional_blur_kernel, 6, sizeof(cl_float2), <xy); + clSetKernelArg(directional_blur_kernel, 7, sizeof(cl_float2), ¢erpix); - device->COM_clEnqueueRange(directionalBlurKernel, outputMemoryBuffer, 8, this); + device->COM_cl_enqueue_range(directional_blur_kernel, output_memory_buffer, 8, this); } -void DirectionalBlurOperation::deinitExecution() +void DirectionalBlurOperation::deinit_execution() { - inputProgram_ = nullptr; + input_program_ = nullptr; } -bool DirectionalBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool DirectionalBlurOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void DirectionalBlurOperation::get_area_of_interest(const int input_idx, diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h index 0687ff4ae5d..2dd2fc98ccd 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h @@ -25,7 +25,7 @@ namespace blender::compositor { class DirectionalBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: - SocketReader *inputProgram_; + SocketReader *input_program_; NodeDBlurData *data_; float center_x_pix_, center_y_pix_; @@ -38,33 +38,33 @@ class DirectionalBlurOperation : public MultiThreadedOperation, public QualitySt /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setData(NodeDBlurData *data) + void set_data(NodeDBlurData *data) { data_ = data; } - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) 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, diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc index 8476db73b32..0c5b8dea2e6 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc @@ -22,55 +22,55 @@ namespace blender::compositor { DisplaceOperation::DisplaceOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Vector); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Vector); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); this->flags.complex = true; - inputColorProgram_ = nullptr; + input_color_program_ = nullptr; } -void DisplaceOperation::initExecution() +void DisplaceOperation::init_execution() { - inputColorProgram_ = this->getInputSocketReader(0); - NodeOperation *vector = this->getInputSocketReader(1); - NodeOperation *scale_x = this->getInputSocketReader(2); - NodeOperation *scale_y = this->getInputSocketReader(3); + input_color_program_ = this->get_input_socket_reader(0); + NodeOperation *vector = this->get_input_socket_reader(1); + NodeOperation *scale_x = this->get_input_socket_reader(2); + NodeOperation *scale_y = this->get_input_socket_reader(3); if (execution_model_ == eExecutionModel::Tiled) { vector_read_fn_ = [=](float x, float y, float *out) { - vector->readSampled(out, x, y, PixelSampler::Bilinear); + vector->read_sampled(out, x, y, PixelSampler::Bilinear); }; scale_x_read_fn_ = [=](float x, float y, float *out) { - scale_x->readSampled(out, x, y, PixelSampler::Nearest); + scale_x->read_sampled(out, x, y, PixelSampler::Nearest); }; scale_y_read_fn_ = [=](float x, float y, float *out) { - scale_y->readSampled(out, x, y, PixelSampler::Nearest); + scale_y->read_sampled(out, x, y, PixelSampler::Nearest); }; } - width_x4_ = this->getWidth() * 4; - height_x4_ = this->getHeight() * 4; - input_vector_width_ = vector->getWidth(); - input_vector_height_ = vector->getHeight(); + width_x4_ = this->get_width() * 4; + height_x4_ = this->get_height() * 4; + input_vector_width_ = vector->get_width(); + input_vector_height_ = vector->get_height(); } -void DisplaceOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void DisplaceOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { float xy[2] = {x, y}; float uv[2], deriv[2][2]; - pixelTransform(xy, uv, deriv); + pixel_transform(xy, uv, deriv); if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) { - inputColorProgram_->readSampled(output, uv[0], uv[1], PixelSampler::Bilinear); + input_color_program_->read_sampled(output, uv[0], uv[1], PixelSampler::Bilinear); } else { /* EWA filtering (without nearest it gets blurry with NO distortion) */ - inputColorProgram_->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + input_color_program_->read_filtered(output, uv[0], uv[1], deriv[0], deriv[1]); } } @@ -92,7 +92,7 @@ bool DisplaceOperation::read_displacement( return true; } -void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2]) +void DisplaceOperation::pixel_transform(const float xy[2], float r_uv[2], float r_deriv[2][2]) { float col[4]; float uv[2]; /* temporary variables for derivative estimation */ @@ -151,52 +151,52 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r } } -void DisplaceOperation::deinitExecution() +void DisplaceOperation::deinit_execution() { - inputColorProgram_ = nullptr; + input_color_program_ = nullptr; vector_read_fn_ = nullptr; scale_x_read_fn_ = nullptr; scale_y_read_fn_ = nullptr; } -bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool DisplaceOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti colorInput; - rcti vectorInput; + rcti color_input; + rcti vector_input; NodeOperation *operation = nullptr; /* the vector buffer only needs a 2x2 buffer. The image needs whole buffer */ /* image */ - operation = getInputOperation(0); - colorInput.xmax = operation->getWidth(); - colorInput.xmin = 0; - colorInput.ymax = operation->getHeight(); - colorInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&colorInput, readOperation, output)) { + operation = get_input_operation(0); + color_input.xmax = operation->get_width(); + color_input.xmin = 0; + color_input.ymax = operation->get_height(); + color_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&color_input, read_operation, output)) { return true; } /* vector */ - operation = getInputOperation(1); - vectorInput.xmax = input->xmax + 1; - vectorInput.xmin = input->xmin - 1; - vectorInput.ymax = input->ymax + 1; - vectorInput.ymin = input->ymin - 1; - if (operation->determineDependingAreaOfInterest(&vectorInput, readOperation, output)) { + operation = get_input_operation(1); + vector_input.xmax = input->xmax + 1; + vector_input.xmin = input->xmin - 1; + vector_input.ymax = input->ymax + 1; + vector_input.ymin = input->ymin - 1; + if (operation->determine_depending_area_of_interest(&vector_input, read_operation, output)) { return true; } /* scale x */ - operation = getInputOperation(2); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + operation = get_input_operation(2); + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } /* scale y */ - operation = getInputOperation(3); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + operation = get_input_operation(3); + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } @@ -209,7 +209,7 @@ void DisplaceOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case 0: { - r_input_area = getInputOperation(input_idx)->get_canvas(); + r_input_area = get_input_operation(input_idx)->get_canvas(); break; } case 1: { @@ -246,7 +246,7 @@ void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output, float uv[2]; float deriv[2][2]; - pixelTransform(xy, uv, deriv); + pixel_transform(xy, uv, deriv); if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) { input_color->read_elem_bilinear(uv[0], uv[1], it.out); } diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.h b/source/blender/compositor/operations/COM_DisplaceOperation.h index 56cd1c1f2a6..a6f6927407f 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.h +++ b/source/blender/compositor/operations/COM_DisplaceOperation.h @@ -25,9 +25,9 @@ namespace blender::compositor { class DisplaceOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputColorProgram_; + SocketReader *input_color_program_; float width_x4_; float height_x4_; @@ -45,26 +45,26 @@ class DisplaceOperation : public MultiThreadedOperation { /** * we need a 2x2 differential filter for Vector Input and full buffer for the image */ - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2]); + void pixel_transform(const float xy[2], float r_uv[2], float r_deriv[2][2]); /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() 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_started(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc index 77d7831c680..729b01d924d 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc @@ -22,108 +22,107 @@ namespace blender::compositor { DisplaceSimpleOperation::DisplaceSimpleOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Vector); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Vector); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); - inputColorProgram_ = nullptr; - inputVectorProgram_ = nullptr; - inputScaleXProgram_ = nullptr; - inputScaleYProgram_ = nullptr; + input_color_program_ = nullptr; + input_vector_program_ = nullptr; + input_scale_xprogram_ = nullptr; + input_scale_yprogram_ = nullptr; } -void DisplaceSimpleOperation::initExecution() +void DisplaceSimpleOperation::init_execution() { - inputColorProgram_ = this->getInputSocketReader(0); - inputVectorProgram_ = this->getInputSocketReader(1); - inputScaleXProgram_ = this->getInputSocketReader(2); - inputScaleYProgram_ = this->getInputSocketReader(3); + input_color_program_ = this->get_input_socket_reader(0); + input_vector_program_ = this->get_input_socket_reader(1); + input_scale_xprogram_ = this->get_input_socket_reader(2); + input_scale_yprogram_ = this->get_input_socket_reader(3); - width_x4_ = this->getWidth() * 4; - height_x4_ = this->getHeight() * 4; + width_x4_ = this->get_width() * 4; + height_x4_ = this->get_height() * 4; } /* minimum distance (in pixels) a pixel has to be displaced * in order to take effect */ // #define DISPLACE_EPSILON 0.01f -void DisplaceSimpleOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void DisplaceSimpleOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inVector[4]; - float inScale[4]; + float in_vector[4]; + float in_scale[4]; float p_dx, p_dy; /* main displacement in pixel space */ float u, v; - inputScaleXProgram_->readSampled(inScale, x, y, sampler); - float xs = inScale[0]; - inputScaleYProgram_->readSampled(inScale, x, y, sampler); - float ys = inScale[0]; + input_scale_xprogram_->read_sampled(in_scale, x, y, sampler); + float xs = in_scale[0]; + input_scale_yprogram_->read_sampled(in_scale, x, y, sampler); + float ys = in_scale[0]; /* clamp x and y displacement to triple image resolution - * to prevent hangs from huge values mistakenly plugged in eg. z buffers */ CLAMP(xs, -width_x4_, width_x4_); CLAMP(ys, -height_x4_, height_x4_); - inputVectorProgram_->readSampled(inVector, x, y, sampler); - p_dx = inVector[0] * xs; - p_dy = inVector[1] * ys; + input_vector_program_->read_sampled(in_vector, x, y, sampler); + p_dx = in_vector[0] * xs; + p_dy = in_vector[1] * ys; /* displaced pixel in uv coords, for image sampling */ /* clamp nodes to avoid glitches */ u = x - p_dx + 0.5f; v = y - p_dy + 0.5f; - CLAMP(u, 0.0f, this->getWidth() - 1.0f); - CLAMP(v, 0.0f, this->getHeight() - 1.0f); + CLAMP(u, 0.0f, this->get_width() - 1.0f); + CLAMP(v, 0.0f, this->get_height() - 1.0f); - inputColorProgram_->readSampled(output, u, v, sampler); + input_color_program_->read_sampled(output, u, v, sampler); } -void DisplaceSimpleOperation::deinitExecution() +void DisplaceSimpleOperation::deinit_execution() { - inputColorProgram_ = nullptr; - inputVectorProgram_ = nullptr; - inputScaleXProgram_ = nullptr; - inputScaleYProgram_ = nullptr; + input_color_program_ = nullptr; + input_vector_program_ = nullptr; + input_scale_xprogram_ = nullptr; + input_scale_yprogram_ = nullptr; } -bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool DisplaceSimpleOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti colorInput; + rcti color_input; NodeOperation *operation = nullptr; /* the vector buffer only needs a 2x2 buffer. The image needs whole buffer */ /* image */ - operation = getInputOperation(0); - colorInput.xmax = operation->getWidth(); - colorInput.xmin = 0; - colorInput.ymax = operation->getHeight(); - colorInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&colorInput, readOperation, output)) { + operation = get_input_operation(0); + color_input.xmax = operation->get_width(); + color_input.xmin = 0; + color_input.ymax = operation->get_height(); + color_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&color_input, read_operation, output)) { return true; } /* vector */ - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } /* scale x */ - operation = getInputOperation(2); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + operation = get_input_operation(2); + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } /* scale y */ - operation = getInputOperation(3); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + operation = get_input_operation(3); + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } @@ -150,8 +149,8 @@ void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float width = this->getWidth(); - const float height = this->getHeight(); + const float width = this->get_width(); + const float height = this->get_height(); const MemoryBuffer *input_color = inputs[0]; for (BuffersIterator it = output->iterate_with(inputs.drop_front(1), area); !it.is_end(); ++it) { diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h index 1baf8c8275c..c16146dc464 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h @@ -25,12 +25,12 @@ namespace blender::compositor { class DisplaceSimpleOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputColorProgram_; - SocketReader *inputVectorProgram_; - SocketReader *inputScaleXProgram_; - SocketReader *inputScaleYProgram_; + SocketReader *input_color_program_; + SocketReader *input_vector_program_; + SocketReader *input_scale_xprogram_; + SocketReader *input_scale_yprogram_; float width_x4_; float height_x4_; @@ -41,24 +41,24 @@ class DisplaceSimpleOperation : public MultiThreadedOperation { /** * we need a full buffer for the image */ - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() 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, diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc index e076bf4ae7d..95593d7d49f 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc @@ -22,39 +22,39 @@ namespace blender::compositor { DistanceRGBMatteOperation::DistanceRGBMatteOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Value); - inputImageProgram_ = nullptr; - inputKeyProgram_ = nullptr; + input_image_program_ = nullptr; + input_key_program_ = nullptr; flags.can_be_constant = true; } -void DistanceRGBMatteOperation::initExecution() +void DistanceRGBMatteOperation::init_execution() { - inputImageProgram_ = this->getInputSocketReader(0); - inputKeyProgram_ = this->getInputSocketReader(1); + input_image_program_ = this->get_input_socket_reader(0); + input_key_program_ = this->get_input_socket_reader(1); } -void DistanceRGBMatteOperation::deinitExecution() +void DistanceRGBMatteOperation::deinit_execution() { - inputImageProgram_ = nullptr; - inputKeyProgram_ = nullptr; + input_image_program_ = nullptr; + input_key_program_ = nullptr; } -float DistanceRGBMatteOperation::calculateDistance(const float key[4], const float image[4]) +float DistanceRGBMatteOperation::calculate_distance(const float key[4], const float image[4]) { return len_v3v3(key, image); } -void DistanceRGBMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void DistanceRGBMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inKey[4]; - float inImage[4]; + float in_key[4]; + float in_image[4]; const float tolerance = settings_->t1; const float falloff = settings_->t2; @@ -62,10 +62,10 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4], float distance; float alpha; - inputKeyProgram_->readSampled(inKey, x, y, sampler); - inputImageProgram_->readSampled(inImage, x, y, sampler); + input_key_program_->read_sampled(in_key, x, y, sampler); + input_image_program_->read_sampled(in_image, x, y, sampler); - distance = this->calculateDistance(inKey, inImage); + distance = this->calculate_distance(in_key, in_image); /* Store matte(alpha) value in [0] to go with * COM_SetAlphaMultiplyOperation and the Value output. @@ -80,16 +80,16 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4], distance = distance - tolerance; alpha = distance / falloff; /* Only change if more transparent than before. */ - if (alpha < inImage[3]) { + if (alpha < in_image[3]) { output[0] = alpha; } else { /* leave as before */ - output[0] = inImage[3]; + output[0] = in_image[3]; } } else { /* leave as before */ - output[0] = inImage[3]; + output[0] = in_image[3]; } } @@ -101,7 +101,7 @@ void DistanceRGBMatteOperation::update_memory_buffer_partial(MemoryBuffer *outpu const float *in_image = it.in(0); const float *in_key = it.in(1); - float distance = this->calculateDistance(in_key, in_image); + float distance = this->calculate_distance(in_key, in_image); const float tolerance = settings_->t1; const float falloff = settings_->t2; diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h index e741a9b80d5..306a4e316f5 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h @@ -29,10 +29,10 @@ namespace blender::compositor { class DistanceRGBMatteOperation : public MultiThreadedOperation { protected: NodeChroma *settings_; - SocketReader *inputImageProgram_; - SocketReader *inputKeyProgram_; + SocketReader *input_image_program_; + SocketReader *input_key_program_; - virtual float calculateDistance(const float key[4], const float image[4]); + virtual float calculate_distance(const float key[4], const float image[4]); public: /** @@ -43,14 +43,14 @@ class DistanceRGBMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma) + void set_settings(NodeChroma *node_chroma) { - settings_ = nodeChroma; + settings_ = node_chroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc index 9903b80ce02..682c01a63d5 100644 --- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc @@ -20,7 +20,7 @@ namespace blender::compositor { -float DistanceYCCMatteOperation::calculateDistance(const float key[4], const float image[4]) +float DistanceYCCMatteOperation::calculate_distance(const float key[4], const float image[4]) { /* only measure the second 2 values */ return len_v2v2(key + 1, image + 1); diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h index 0e178fddc39..1d5ed9f0de1 100644 --- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h +++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h @@ -29,7 +29,7 @@ namespace blender::compositor { */ class DistanceYCCMatteOperation : public DistanceRGBMatteOperation { protected: - float calculateDistance(const float key[4], const float image[4]) override; + float calculate_distance(const float key[4], const float image[4]) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc index ef309392d4d..9507f991b52 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.cc +++ b/source/blender/compositor/operations/COM_DotproductOperation.cc @@ -22,21 +22,21 @@ namespace blender::compositor { DotproductOperation::DotproductOperation() { - this->addInputSocket(DataType::Vector); - this->addInputSocket(DataType::Vector); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Vector); + this->add_input_socket(DataType::Vector); + this->add_output_socket(DataType::Value); this->set_canvas_input_index(0); input1Operation_ = nullptr; input2Operation_ = nullptr; flags.can_be_constant = true; } -void DotproductOperation::initExecution() +void DotproductOperation::init_execution() { - input1Operation_ = this->getInputSocketReader(0); - input2Operation_ = this->getInputSocketReader(1); + input1Operation_ = this->get_input_socket_reader(0); + input2Operation_ = this->get_input_socket_reader(1); } -void DotproductOperation::deinitExecution() +void DotproductOperation::deinit_execution() { input1Operation_ = nullptr; input2Operation_ = nullptr; @@ -44,15 +44,15 @@ void DotproductOperation::deinitExecution() /** \todo current implementation is the inverse of a dot-product. not 'logically' correct */ -void DotproductOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void DotproductOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float input1[4]; float input2[4]; - input1Operation_->readSampled(input1, x, y, sampler); - input2Operation_->readSampled(input2, x, y, sampler); + input1Operation_->read_sampled(input1, x, y, sampler); + input2Operation_->read_sampled(input2, x, y, sampler); output[0] = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]); } diff --git a/source/blender/compositor/operations/COM_DotproductOperation.h b/source/blender/compositor/operations/COM_DotproductOperation.h index 5190a161533..f9e5d4d39c4 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.h +++ b/source/blender/compositor/operations/COM_DotproductOperation.h @@ -29,10 +29,10 @@ class DotproductOperation : public MultiThreadedOperation { public: DotproductOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc index 2ae33d5451f..559df4abb0c 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc @@ -945,8 +945,8 @@ static void do_createEdgeLocationBuffer(unsigned int t, const unsigned int *lres, float *res, unsigned short *gbuf, - unsigned int *innerEdgeOffset, - unsigned int *outerEdgeOffset, + unsigned int *inner_edge_offset, + unsigned int *outer_edge_offset, unsigned int isz, unsigned int gsz) { @@ -956,14 +956,14 @@ static void do_createEdgeLocationBuffer(unsigned int t, unsigned int dmin; /* Minimum edge distance. */ unsigned int rsl; /* Long used for finding fast `1.0/sqrt`. */ - unsigned int gradientFillOffset; + unsigned int gradient_fill_offset; /* For looping inner edge pixel indexes, represents current position from offset. */ - unsigned int innerAccum = 0; + unsigned int inner_accum = 0; /* For looping outer edge pixel indexes, represents current position from offset. */ - unsigned int outerAccum = 0; + unsigned int outer_accum = 0; /* For looping gradient pixel indexes, represents current position from offset. */ - unsigned int gradientAccum = 0; + unsigned int gradient_accum = 0; /* Disable clang-format to prevent line-wrapping. */ /* clang-format off */ @@ -996,7 +996,7 @@ static void do_createEdgeLocationBuffer(unsigned int t, * ..oooo... * .oggggo.. * .oggiggo. - * .ogiFigo. + * .ogi_figo. * .oggiggo. * .oggggo.. * ..oooo... @@ -1009,7 +1009,7 @@ static void do_createEdgeLocationBuffer(unsigned int t, * * The memory in gbuf[] after filling will look like this: * - * gradientFillOffset (0 pixels) innerEdgeOffset (18 pixels) outerEdgeOffset (22 pixels) + * gradient_fill_offset (0 pixels) inner_edge_offset (18 pixels) outer_edge_offset (22 pixels) * / / / * / / / * |X Y X Y X Y X Y > = 0; x--) { - gradientFillOffset = x << 1; - t = gbuf[gradientFillOffset]; /* Calculate column of pixel indexed by `gbuf[x]`. */ - fsz = gbuf[gradientFillOffset + 1]; /* Calculate row of pixel indexed by `gbuf[x]`. */ - dmin = 0xffffffff; /* Reset min distance to edge pixel. */ - for (a = outerEdgeOffset + osz - 1; a >= outerEdgeOffset; + gradient_fill_offset = x << 1; + t = gbuf[gradient_fill_offset]; /* Calculate column of pixel indexed by `gbuf[x]`. */ + fsz = gbuf[gradient_fill_offset + 1]; /* Calculate row of pixel indexed by `gbuf[x]`. */ + dmin = 0xffffffff; /* Reset min distance to edge pixel. */ + for (a = outer_edge_offset + osz - 1; a >= outer_edge_offset; a--) { /* Loop through all outer edge buffer pixels. */ ud = a << 1; dy = t - gbuf[ud]; /* Set dx to gradient pixel column - outer edge pixel row. */ @@ -1173,7 +1173,7 @@ static void do_fillGradientBuffer(unsigned int rw, odist = odist * (rsopf - (rsf * odist * odist)); /* -- This line can be iterated for more accuracy. -- */ dmin = 0xffffffff; /* Reset min distance to edge pixel. */ - for (a = innerEdgeOffset + isz - 1; a >= innerEdgeOffset; + for (a = inner_edge_offset + isz - 1; a >= inner_edge_offset; a--) { /* Loop through all inside edge pixels. */ ud = a << 1; dy = t - gbuf[ud]; /* Compute delta in Y from gradient pixel to inside edge pixel. */ @@ -1201,14 +1201,14 @@ static void do_fillGradientBuffer(unsigned int rw, /* Here we reconstruct the pixel's memory location in the CompBuf by * `Pixel Index = Pixel Column + ( Pixel Row * Row Width )`. */ - res[gbuf[gradientFillOffset + 1] + (gbuf[gradientFillOffset] * rw)] = + res[gbuf[gradient_fill_offset + 1] + (gbuf[gradient_fill_offset] * rw)] = (idist / (idist + odist)); /* Set intensity. */ } } /* End of copy. */ -void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float *res) +void DoubleEdgeMaskOperation::do_double_edge_mask(float *imask, float *omask, float *res) { unsigned int *lres; /* Pointer to output pixel buffer (for bit operations). */ unsigned int *limask; /* Pointer to inner mask (for bit operations). */ @@ -1222,17 +1222,17 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float unsigned int osz = 0; /* Size (in pixels) of outside edge pixel index buffer. */ unsigned int gsz = 0; /* Size (in pixels) of gradient pixel index buffer. */ unsigned int rsize[3]; /* Size storage to pass to helper functions. */ - unsigned int innerEdgeOffset = + unsigned int inner_edge_offset = 0; /* Offset into final buffer where inner edge pixel indexes start. */ - unsigned int outerEdgeOffset = + unsigned int outer_edge_offset = 0; /* Offset into final buffer where outer edge pixel indexes start. */ unsigned short *gbuf; /* Gradient/inner/outer pixel location index buffer. */ if (true) { /* If both input sockets have some data coming in... */ - rw = this->getWidth(); /* Width of a row of pixels. */ - t = (rw * this->getHeight()) - 1; /* Determine size of the frame. */ + rw = this->get_width(); /* Width of a row of pixels. */ + t = (rw * this->get_height()) - 1; /* Determine size of the frame. */ memset(res, 0, sizeof(float) * @@ -1267,8 +1267,8 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float * * Each version has slightly different criteria for detecting an edge pixel. */ - if (adjacentOnly_) { /* If "adjacent only" inner edge mode is turned on. */ - if (keepInside_) { /* If "keep inside" buffer edge mode is turned on. */ + if (adjacent_only_) { /* If "adjacent only" inner edge mode is turned on. */ + if (keep_inside_) { /* If "keep inside" buffer edge mode is turned on. */ do_adjacentKeepBorders(t, rw, limask, lomask, lres, res, rsize); } else { /* "bleed out" buffer edge mode is turned on. */ @@ -1281,8 +1281,8 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float /* Detect edges in all non-border pixels in the buffer. */ do_adjacentEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz); } - else { /* "all" inner edge mode is turned on. */ - if (keepInside_) { /* If "keep inside" buffer edge mode is turned on. */ + else { /* "all" inner edge mode is turned on. */ + if (keep_inside_) { /* If "keep inside" buffer edge mode is turned on. */ do_allKeepBorders(t, rw, limask, lomask, lres, res, rsize); } else { /* "bleed out" buffer edge mode is turned on. */ @@ -1309,8 +1309,8 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float gbuf = (unsigned short *)MEM_callocN(sizeof(unsigned short) * fsz * 2, "DEM"); do_createEdgeLocationBuffer( - t, rw, lres, res, gbuf, &innerEdgeOffset, &outerEdgeOffset, isz, gsz); - do_fillGradientBuffer(rw, res, gbuf, isz, osz, gsz, innerEdgeOffset, outerEdgeOffset); + t, rw, lres, res, gbuf, &inner_edge_offset, &outer_edge_offset, isz, gsz); + do_fillGradientBuffer(rw, res, gbuf, isz, osz, gsz, inner_edge_offset, outer_edge_offset); /* Free the gradient index buffer. */ MEM_freeN(gbuf); @@ -1319,76 +1319,75 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float DoubleEdgeMaskOperation::DoubleEdgeMaskOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - inputInnerMask_ = nullptr; - inputOuterMask_ = nullptr; - adjacentOnly_ = false; - keepInside_ = false; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_inner_mask_ = nullptr; + input_outer_mask_ = nullptr; + adjacent_only_ = false; + keep_inside_ = false; this->flags.complex = true; is_output_rendered_ = false; } -bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool DoubleEdgeMaskOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - if (cachedInstance_ == nullptr) { - rcti newInput; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + if (cached_instance_ == nullptr) { + rcti new_input; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } return false; } -void DoubleEdgeMaskOperation::initExecution() +void DoubleEdgeMaskOperation::init_execution() { - inputInnerMask_ = this->getInputSocketReader(0); - inputOuterMask_ = this->getInputSocketReader(1); - initMutex(); - cachedInstance_ = nullptr; + input_inner_mask_ = this->get_input_socket_reader(0); + input_outer_mask_ = this->get_input_socket_reader(1); + init_mutex(); + cached_instance_ = nullptr; } -void *DoubleEdgeMaskOperation::initializeTileData(rcti *rect) +void *DoubleEdgeMaskOperation::initialize_tile_data(rcti *rect) { - if (cachedInstance_) { - return cachedInstance_; + if (cached_instance_) { + return cached_instance_; } - lockMutex(); - if (cachedInstance_ == nullptr) { - MemoryBuffer *innerMask = (MemoryBuffer *)inputInnerMask_->initializeTileData(rect); - MemoryBuffer *outerMask = (MemoryBuffer *)inputOuterMask_->initializeTileData(rect); - float *data = (float *)MEM_mallocN(sizeof(float) * this->getWidth() * this->getHeight(), + lock_mutex(); + if (cached_instance_ == nullptr) { + MemoryBuffer *inner_mask = (MemoryBuffer *)input_inner_mask_->initialize_tile_data(rect); + MemoryBuffer *outer_mask = (MemoryBuffer *)input_outer_mask_->initialize_tile_data(rect); + float *data = (float *)MEM_mallocN(sizeof(float) * this->get_width() * this->get_height(), __func__); - float *imask = innerMask->getBuffer(); - float *omask = outerMask->getBuffer(); - doDoubleEdgeMask(imask, omask, data); - cachedInstance_ = data; + float *imask = inner_mask->get_buffer(); + float *omask = outer_mask->get_buffer(); + do_double_edge_mask(imask, omask, data); + cached_instance_ = data; } - unlockMutex(); - return cachedInstance_; + unlock_mutex(); + return cached_instance_; } -void DoubleEdgeMaskOperation::executePixel(float output[4], int x, int y, void *data) +void DoubleEdgeMaskOperation::execute_pixel(float output[4], int x, int y, void *data) { float *buffer = (float *)data; - int index = (y * this->getWidth() + x); + int index = (y * this->get_width() + x); output[0] = buffer[index]; } -void DoubleEdgeMaskOperation::deinitExecution() +void DoubleEdgeMaskOperation::deinit_execution() { - inputInnerMask_ = nullptr; - inputOuterMask_ = nullptr; - deinitMutex(); - if (cachedInstance_) { - MEM_freeN(cachedInstance_); - cachedInstance_ = nullptr; + input_inner_mask_ = nullptr; + input_outer_mask_ = nullptr; + deinit_mutex(); + if (cached_instance_) { + MEM_freeN(cached_instance_); + cached_instance_ = nullptr; } } @@ -1412,11 +1411,11 @@ void DoubleEdgeMaskOperation::update_memory_buffer(MemoryBuffer *output, MemoryBuffer *outer_mask = input_outer_mask->is_a_single_elem() ? input_outer_mask->inflate() : input_outer_mask; - BLI_assert(output->getWidth() == this->getWidth()); - BLI_assert(output->getHeight() == this->getHeight()); + BLI_assert(output->get_width() == this->get_width()); + BLI_assert(output->get_height() == this->get_height()); /* TODO(manzanilla): Once tiled implementation is removed, use execution system to run * multi-threaded where possible. */ - doDoubleEdgeMask(inner_mask->getBuffer(), outer_mask->getBuffer(), output->getBuffer()); + do_double_edge_mask(inner_mask->get_buffer(), outer_mask->get_buffer(), output->get_buffer()); is_output_rendered_ = true; if (inner_mask != input_inner_mask) { diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h index 29cb40c29af..f2086d9ebd4 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h @@ -25,50 +25,50 @@ namespace blender::compositor { class DoubleEdgeMaskOperation : public NodeOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputOuterMask_; - SocketReader *inputInnerMask_; - bool adjacentOnly_; - bool keepInside_; + SocketReader *input_outer_mask_; + SocketReader *input_inner_mask_; + bool adjacent_only_; + bool keep_inside_; /* TODO(manzanilla): To be removed with tiled implementation. */ - float *cachedInstance_; + float *cached_instance_; bool is_output_rendered_; public: DoubleEdgeMaskOperation(); - void doDoubleEdgeMask(float *imask, float *omask, float *res); + void do_double_edge_mask(float *imask, float *omask, float *res); /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setAdjecentOnly(bool adjacentOnly) + void set_adjecent_only(bool adjacent_only) { - adjacentOnly_ = adjacentOnly; + adjacent_only_ = adjacent_only; } - void setKeepInside(bool keepInside) + void set_keep_inside(bool keep_inside) { - keepInside_ = keepInside; + keep_inside_ = keep_inside; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc index 57cd3cb1423..19497c38131 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc @@ -22,75 +22,75 @@ namespace blender::compositor { EllipseMaskOperation::EllipseMaskOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - inputMask_ = nullptr; - inputValue_ = nullptr; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_mask_ = nullptr; + input_value_ = nullptr; cosine_ = 0.0f; sine_ = 0.0f; } -void EllipseMaskOperation::initExecution() +void EllipseMaskOperation::init_execution() { - inputMask_ = this->getInputSocketReader(0); - inputValue_ = this->getInputSocketReader(1); + input_mask_ = this->get_input_socket_reader(0); + input_value_ = this->get_input_socket_reader(1); const double rad = (double)data_->rotation; cosine_ = cos(rad); sine_ = sin(rad); - aspectRatio_ = ((float)this->getWidth()) / this->getHeight(); + aspect_ratio_ = ((float)this->get_width()) / this->get_height(); } -void EllipseMaskOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void EllipseMaskOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputMask[4]; - float inputValue[4]; + float input_mask[4]; + float input_value[4]; - float rx = x / this->getWidth(); - float ry = y / this->getHeight(); + float rx = x / this->get_width(); + float ry = y / this->get_height(); - const float dy = (ry - data_->y) / aspectRatio_; + const float dy = (ry - data_->y) / aspect_ratio_; const float dx = rx - data_->x; rx = data_->x + (cosine_ * dx + sine_ * dy); ry = data_->y + (-sine_ * dx + cosine_ * dy); - inputMask_->readSampled(inputMask, x, y, sampler); - inputValue_->readSampled(inputValue, x, y, sampler); + input_mask_->read_sampled(input_mask, x, y, sampler); + input_value_->read_sampled(input_value, x, y, sampler); - const float halfHeight = (data_->height) / 2.0f; - const float halfWidth = data_->width / 2.0f; + const float half_height = (data_->height) / 2.0f; + const float half_width = data_->width / 2.0f; float sx = rx - data_->x; sx *= sx; - const float tx = halfWidth * halfWidth; + const float tx = half_width * half_width; float sy = ry - data_->y; sy *= sy; - const float ty = halfHeight * halfHeight; + const float ty = half_height * half_height; bool inside = ((sx / tx) + (sy / ty)) < 1.0f; - switch (maskType_) { + switch (mask_type_) { case CMP_NODE_MASKTYPE_ADD: if (inside) { - output[0] = MAX2(inputMask[0], inputValue[0]); + output[0] = MAX2(input_mask[0], input_value[0]); } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; case CMP_NODE_MASKTYPE_SUBTRACT: if (inside) { - output[0] = inputMask[0] - inputValue[0]; + output[0] = input_mask[0] - input_value[0]; CLAMP(output[0], 0, 1); } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; case CMP_NODE_MASKTYPE_MULTIPLY: if (inside) { - output[0] = inputMask[0] * inputValue[0]; + output[0] = input_mask[0] * input_value[0]; } else { output[0] = 0; @@ -98,15 +98,15 @@ void EllipseMaskOperation::executePixelSampled(float output[4], break; case CMP_NODE_MASKTYPE_NOT: if (inside) { - if (inputMask[0] > 0.0f) { + if (input_mask[0] > 0.0f) { output[0] = 0; } else { - output[0] = inputValue[0]; + output[0] = input_value[0]; } } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; } @@ -117,7 +117,7 @@ void EllipseMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { MaskFunc mask_func; - switch (maskType_) { + switch (mask_type_) { case CMP_NODE_MASKTYPE_ADD: mask_func = [](const bool is_inside, const float *mask, const float *value) { return is_inside ? MAX2(mask[0], value[0]) : mask[0]; @@ -152,15 +152,15 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, { const MemoryBuffer *input_mask = inputs[0]; const MemoryBuffer *input_value = inputs[1]; - const float op_w = this->getWidth(); - const float op_h = this->getHeight(); + const float op_w = this->get_width(); + const float op_h = this->get_height(); const float half_w = data_->width / 2.0f; const float half_h = data_->height / 2.0f; const float tx = half_w * half_w; const float ty = half_h * half_h; for (int y = area.ymin; y < area.ymax; y++) { const float op_ry = y / op_h; - const float dy = (op_ry - data_->y) / aspectRatio_; + const float dy = (op_ry - data_->y) / aspect_ratio_; float *out = output->get_elem(area.xmin, y); const float *mask = input_mask->get_elem(area.xmin, y); const float *value = input_value->get_elem(area.xmin, y); @@ -183,10 +183,10 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, } } -void EllipseMaskOperation::deinitExecution() +void EllipseMaskOperation::deinit_execution() { - inputMask_ = nullptr; - inputValue_ = nullptr; + input_mask_ = nullptr; + input_value_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.h b/source/blender/compositor/operations/COM_EllipseMaskOperation.h index ec6fb4624f7..7a6f96ab1b1 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.h +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.h @@ -27,15 +27,15 @@ class EllipseMaskOperation : public MultiThreadedOperation { using MaskFunc = std::function; /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputMask_; - SocketReader *inputValue_; + SocketReader *input_mask_; + SocketReader *input_value_; float sine_; float cosine_; - float aspectRatio_; - int maskType_; + float aspect_ratio_; + int mask_type_; NodeEllipseMask *data_; @@ -45,26 +45,26 @@ class EllipseMaskOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setData(NodeEllipseMask *data) + void set_data(NodeEllipseMask *data) { data_ = data; } - void setMaskType(int maskType) + void set_mask_type(int mask_type) { - maskType_ = maskType; + mask_type_ = mask_type; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index b1843a225e0..e39db9e06eb 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -27,24 +27,24 @@ FastGaussianBlurOperation::FastGaussianBlurOperation() : BlurBaseOperation(DataT iirgaus_ = nullptr; } -void FastGaussianBlurOperation::executePixel(float output[4], int x, int y, void *data) +void FastGaussianBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { - MemoryBuffer *newData = (MemoryBuffer *)data; - newData->read(output, x, y); + MemoryBuffer *new_data = (MemoryBuffer *)data; + new_data->read(output, x, y); } -bool FastGaussianBlurOperation::determineDependingAreaOfInterest( - rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) +bool FastGaussianBlurOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; + rcti new_input; + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + NodeOperation *operation = this->get_input_operation(1); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } @@ -52,12 +52,12 @@ bool FastGaussianBlurOperation::determineDependingAreaOfInterest( return false; } - newInput.xmin = 0; - newInput.ymin = 0; - newInput.xmax = this->getWidth(); - newInput.ymax = this->getHeight(); + new_input.xmin = 0; + new_input.ymin = 0; + new_input.xmax = this->get_width(); + new_input.ymax = this->get_height(); - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void FastGaussianBlurOperation::init_data() @@ -67,28 +67,28 @@ void FastGaussianBlurOperation::init_data() sy_ = data_.sizey * size_ / 2.0f; } -void FastGaussianBlurOperation::initExecution() +void FastGaussianBlurOperation::init_execution() { - BlurBaseOperation::initExecution(); - BlurBaseOperation::initMutex(); + BlurBaseOperation::init_execution(); + BlurBaseOperation::init_mutex(); } -void FastGaussianBlurOperation::deinitExecution() +void FastGaussianBlurOperation::deinit_execution() { if (iirgaus_) { delete iirgaus_; iirgaus_ = nullptr; } - BlurBaseOperation::deinitMutex(); + BlurBaseOperation::deinit_mutex(); } -void *FastGaussianBlurOperation::initializeTileData(rcti *rect) +void *FastGaussianBlurOperation::initialize_tile_data(rcti *rect) { - lockMutex(); + lock_mutex(); if (!iirgaus_) { - MemoryBuffer *newBuf = (MemoryBuffer *)inputProgram_->initializeTileData(rect); - MemoryBuffer *copy = new MemoryBuffer(*newBuf); - updateSize(); + MemoryBuffer *new_buf = (MemoryBuffer *)input_program_->initialize_tile_data(rect); + MemoryBuffer *copy = new MemoryBuffer(*new_buf); + update_size(); int c; sx_ = data_.sizex * size_ / 2.0f; @@ -113,7 +113,7 @@ void *FastGaussianBlurOperation::initializeTileData(rcti *rect) } iirgaus_ = copy; } - unlockMutex(); + unlock_mutex(); return iirgaus_; } @@ -125,11 +125,11 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, BLI_assert(!src->is_a_single_elem()); double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3]; double *X, *Y, *W; - const unsigned int src_width = src->getWidth(); - const unsigned int src_height = src->getHeight(); + const unsigned int src_width = src->get_width(); + const unsigned int src_height = src->get_height(); unsigned int x, y, sz; unsigned int i; - float *buffer = src->getBuffer(); + float *buffer = src->get_buffer(); const uint8_t num_channels = src->get_num_channels(); /* <0.5 not valid, though can have a possibly useful sort of sharpening effect. */ @@ -290,7 +290,7 @@ void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *outpu image = output; } else { - image = new MemoryBuffer(getOutputSocket()->getDataType(), area); + image = new MemoryBuffer(get_output_socket()->get_data_type(), area); } image->copy_from(input, area); @@ -320,8 +320,8 @@ void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *outpu FastGaussianBlurValueOperation::FastGaussianBlurValueOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); iirgaus_ = nullptr; inputprogram_ = nullptr; sigma_ = 1.0f; @@ -329,56 +329,56 @@ FastGaussianBlurValueOperation::FastGaussianBlurValueOperation() flags.complex = true; } -void FastGaussianBlurValueOperation::executePixel(float output[4], int x, int y, void *data) +void FastGaussianBlurValueOperation::execute_pixel(float output[4], int x, int y, void *data) { - MemoryBuffer *newData = (MemoryBuffer *)data; - newData->read(output, x, y); + MemoryBuffer *new_data = (MemoryBuffer *)data; + new_data->read(output, x, y); } -bool FastGaussianBlurValueOperation::determineDependingAreaOfInterest( - rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) +bool FastGaussianBlurValueOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; if (iirgaus_) { return false; } - newInput.xmin = 0; - newInput.ymin = 0; - newInput.xmax = this->getWidth(); - newInput.ymax = this->getHeight(); + new_input.xmin = 0; + new_input.ymin = 0; + new_input.xmax = this->get_width(); + new_input.ymax = this->get_height(); - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } -void FastGaussianBlurValueOperation::initExecution() +void FastGaussianBlurValueOperation::init_execution() { - inputprogram_ = getInputSocketReader(0); - initMutex(); + inputprogram_ = get_input_socket_reader(0); + init_mutex(); } -void FastGaussianBlurValueOperation::deinitExecution() +void FastGaussianBlurValueOperation::deinit_execution() { if (iirgaus_) { delete iirgaus_; iirgaus_ = nullptr; } - deinitMutex(); + deinit_mutex(); } -void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) +void *FastGaussianBlurValueOperation::initialize_tile_data(rcti *rect) { - lockMutex(); + lock_mutex(); if (!iirgaus_) { - MemoryBuffer *newBuf = (MemoryBuffer *)inputprogram_->initializeTileData(rect); - MemoryBuffer *copy = new MemoryBuffer(*newBuf); + MemoryBuffer *new_buf = (MemoryBuffer *)inputprogram_->initialize_tile_data(rect); + MemoryBuffer *copy = new MemoryBuffer(*new_buf); FastGaussianBlurOperation::IIR_gauss(copy, sigma_, 0, 3); if (overlay_ == FAST_GAUSS_OVERLAY_MIN) { - float *src = newBuf->getBuffer(); - float *dst = copy->getBuffer(); - for (int i = copy->getWidth() * copy->getHeight(); i != 0; + float *src = new_buf->get_buffer(); + float *dst = copy->get_buffer(); + for (int i = copy->get_width() * copy->get_height(); i != 0; i--, src += COM_DATA_TYPE_VALUE_CHANNELS, dst += COM_DATA_TYPE_VALUE_CHANNELS) { if (*src < *dst) { *dst = *src; @@ -386,9 +386,9 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) } } else if (overlay_ == FAST_GAUSS_OVERLAY_MAX) { - float *src = newBuf->getBuffer(); - float *dst = copy->getBuffer(); - for (int i = copy->getWidth() * copy->getHeight(); i != 0; + float *src = new_buf->get_buffer(); + float *dst = copy->get_buffer(); + for (int i = copy->get_width() * copy->get_height(); i != 0; i--, src += COM_DATA_TYPE_VALUE_CHANNELS, dst += COM_DATA_TYPE_VALUE_CHANNELS) { if (*src > *dst) { *dst = *src; @@ -398,7 +398,7 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) iirgaus_ = copy; } - unlockMutex(); + unlock_mutex(); return iirgaus_; } diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h index 2a0ed078612..8d33e5ef244 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h @@ -31,16 +31,16 @@ class FastGaussianBlurOperation : public BlurBaseOperation { public: FastGaussianBlurOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixel(float output[4], int x, int y, void *data) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel(float output[4], int x, int y, void *data) override; static void IIR_gauss(MemoryBuffer *src, float sigma, unsigned int channel, unsigned int xy); - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; void init_data() override; - void deinitExecution() override; - void initExecution() override; + void deinit_execution() override; + void init_execution() override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_started(MemoryBuffer *output, @@ -73,21 +73,21 @@ class FastGaussianBlurValueOperation : public MultiThreadedOperation { public: FastGaussianBlurValueOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixel(float output[4], int x, int y, void *data) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void *initializeTileData(rcti *rect) override; - void deinitExecution() override; - void initExecution() override; - void setSigma(float sigma) + void *initialize_tile_data(rcti *rect) override; + void deinit_execution() override; + void init_execution() override; + void set_sigma(float sigma) { sigma_ = sigma; } /* used for DOF blurring ZBuffer */ - void setOverlay(int overlay) + void set_overlay(int overlay) { overlay_ = overlay; } diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc index ae2aa21131a..7ca12a504b7 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.cc +++ b/source/blender/compositor/operations/COM_FlipOperation.cc @@ -22,57 +22,57 @@ namespace blender::compositor { FlipOperation::FlipOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color, ResizeMode::None); + this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); - inputOperation_ = nullptr; - flipX_ = true; - flipY_ = false; + input_operation_ = nullptr; + flip_x_ = true; + flip_y_ = false; } -void FlipOperation::initExecution() +void FlipOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void FlipOperation::deinitExecution() +void FlipOperation::deinit_execution() { - inputOperation_ = nullptr; + input_operation_ = nullptr; } -void FlipOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void FlipOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { - float nx = flipX_ ? ((int)this->getWidth() - 1) - x : x; - float ny = flipY_ ? ((int)this->getHeight() - 1) - y : y; + float nx = flip_x_ ? ((int)this->get_width() - 1) - x : x; + float ny = flip_y_ ? ((int)this->get_height() - 1) - y : y; - inputOperation_->readSampled(output, nx, ny, sampler); + input_operation_->read_sampled(output, nx, ny, sampler); } -bool FlipOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool FlipOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - if (flipX_) { - const int w = (int)this->getWidth() - 1; - newInput.xmax = (w - input->xmin) + 1; - newInput.xmin = (w - input->xmax) - 1; + if (flip_x_) { + const int w = (int)this->get_width() - 1; + new_input.xmax = (w - input->xmin) + 1; + new_input.xmin = (w - input->xmax) - 1; } else { - newInput.xmin = input->xmin; - newInput.xmax = input->xmax; + new_input.xmin = input->xmin; + new_input.xmax = input->xmax; } - if (flipY_) { - const int h = (int)this->getHeight() - 1; - newInput.ymax = (h - input->ymin) + 1; - newInput.ymin = (h - input->ymax) - 1; + if (flip_y_) { + const int h = (int)this->get_height() - 1; + new_input.ymax = (h - input->ymin) + 1; + new_input.ymin = (h - input->ymax) - 1; } else { - newInput.ymin = input->ymin; - newInput.ymax = input->ymax; + new_input.ymin = input->ymin; + new_input.ymax = input->ymax; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void FlipOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -80,12 +80,12 @@ void FlipOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) NodeOperation::determine_canvas(preferred_area, r_area); if (execution_model_ == eExecutionModel::FullFrame) { rcti input_area = r_area; - if (flipX_) { + if (flip_x_) { const int width = BLI_rcti_size_x(&input_area) - 1; r_area.xmax = (width - input_area.xmin) + 1; r_area.xmin = (width - input_area.xmax) + 1; } - if (flipY_) { + if (flip_y_) { const int height = BLI_rcti_size_y(&input_area) - 1; r_area.ymax = (height - input_area.ymin) + 1; r_area.ymin = (height - input_area.ymax) + 1; @@ -99,8 +99,8 @@ void FlipOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - if (flipX_) { - const int w = (int)this->getWidth() - 1; + if (flip_x_) { + const int w = (int)this->get_width() - 1; r_input_area.xmax = (w - output_area.xmin) + 1; r_input_area.xmin = (w - output_area.xmax) + 1; } @@ -108,8 +108,8 @@ void FlipOperation::get_area_of_interest(const int input_idx, r_input_area.xmin = output_area.xmin; r_input_area.xmax = output_area.xmax; } - if (flipY_) { - const int h = (int)this->getHeight() - 1; + if (flip_y_) { + const int h = (int)this->get_height() - 1; r_input_area.ymax = (h - output_area.ymin) + 1; r_input_area.ymin = (h - output_area.ymax) + 1; } @@ -127,8 +127,8 @@ void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output, const int input_offset_x = input_img->get_rect().xmin; const int input_offset_y = input_img->get_rect().ymin; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - const int nx = flipX_ ? ((int)this->getWidth() - 1) - it.x : it.x; - const int ny = flipY_ ? ((int)this->getHeight() - 1) - it.y : it.y; + const int nx = flip_x_ ? ((int)this->get_width() - 1) - it.x : it.x; + const int ny = flip_y_ ? ((int)this->get_height() - 1) - it.y : it.y; input_img->read_elem(input_offset_x + nx, input_offset_y + ny, it.out); } } diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h index 3310819051a..001fd652740 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.h +++ b/source/blender/compositor/operations/COM_FlipOperation.h @@ -24,26 +24,26 @@ namespace blender::compositor { class FlipOperation : public MultiThreadedOperation { private: - SocketReader *inputOperation_; - bool flipX_; - bool flipY_; + SocketReader *input_operation_; + bool flip_x_; + bool flip_y_; public: FlipOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void setFlipX(bool flipX) { - flipX_ = flipX; + flip_x_ = flipX; } void setFlipY(bool flipY) { - flipY_ = flipY; + flip_y_ = flipY; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc index 9a51c789943..0271e57b2c4 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc @@ -22,39 +22,39 @@ namespace blender::compositor { GammaCorrectOperation::GammaCorrectOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - inputProgram_ = nullptr; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; flags.can_be_constant = true; } -void GammaCorrectOperation::initExecution() +void GammaCorrectOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void GammaCorrectOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void GammaCorrectOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - inputProgram_->readSampled(inputColor, x, y, sampler); - if (inputColor[3] > 0.0f) { - inputColor[0] /= inputColor[3]; - inputColor[1] /= inputColor[3]; - inputColor[2] /= inputColor[3]; + float input_color[4]; + input_program_->read_sampled(input_color, x, y, sampler); + if (input_color[3] > 0.0f) { + input_color[0] /= input_color[3]; + input_color[1] /= input_color[3]; + input_color[2] /= input_color[3]; } /* check for negative to avoid nan's */ - output[0] = inputColor[0] > 0.0f ? inputColor[0] * inputColor[0] : 0.0f; - output[1] = inputColor[1] > 0.0f ? inputColor[1] * inputColor[1] : 0.0f; - output[2] = inputColor[2] > 0.0f ? inputColor[2] * inputColor[2] : 0.0f; - output[3] = inputColor[3]; + output[0] = input_color[0] > 0.0f ? input_color[0] * input_color[0] : 0.0f; + output[1] = input_color[1] > 0.0f ? input_color[1] * input_color[1] : 0.0f; + output[2] = input_color[2] > 0.0f ? input_color[2] * input_color[2] : 0.0f; + output[3] = input_color[3]; - if (inputColor[3] > 0.0f) { - output[0] *= inputColor[3]; - output[1] *= inputColor[3]; - output[2] *= inputColor[3]; + if (input_color[3] > 0.0f) { + output[0] *= input_color[3]; + output[1] *= input_color[3]; + output[2] *= input_color[3]; } } @@ -86,46 +86,46 @@ void GammaCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void GammaCorrectOperation::deinitExecution() +void GammaCorrectOperation::deinit_execution() { - inputProgram_ = nullptr; + input_program_ = nullptr; } GammaUncorrectOperation::GammaUncorrectOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - inputProgram_ = nullptr; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; flags.can_be_constant = true; } -void GammaUncorrectOperation::initExecution() +void GammaUncorrectOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void GammaUncorrectOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void GammaUncorrectOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - inputProgram_->readSampled(inputColor, x, y, sampler); + float input_color[4]; + input_program_->read_sampled(input_color, x, y, sampler); - if (inputColor[3] > 0.0f) { - inputColor[0] /= inputColor[3]; - inputColor[1] /= inputColor[3]; - inputColor[2] /= inputColor[3]; + if (input_color[3] > 0.0f) { + input_color[0] /= input_color[3]; + input_color[1] /= input_color[3]; + input_color[2] /= input_color[3]; } - output[0] = inputColor[0] > 0.0f ? sqrtf(inputColor[0]) : 0.0f; - output[1] = inputColor[1] > 0.0f ? sqrtf(inputColor[1]) : 0.0f; - output[2] = inputColor[2] > 0.0f ? sqrtf(inputColor[2]) : 0.0f; - output[3] = inputColor[3]; + output[0] = input_color[0] > 0.0f ? sqrtf(input_color[0]) : 0.0f; + output[1] = input_color[1] > 0.0f ? sqrtf(input_color[1]) : 0.0f; + output[2] = input_color[2] > 0.0f ? sqrtf(input_color[2]) : 0.0f; + output[3] = input_color[3]; - if (inputColor[3] > 0.0f) { - output[0] *= inputColor[3]; - output[1] *= inputColor[3]; - output[2] *= inputColor[3]; + if (input_color[3] > 0.0f) { + output[0] *= input_color[3]; + output[1] *= input_color[3]; + output[2] *= input_color[3]; } } @@ -156,9 +156,9 @@ void GammaUncorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void GammaUncorrectOperation::deinitExecution() +void GammaUncorrectOperation::deinit_execution() { - inputProgram_ = nullptr; + input_program_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.h b/source/blender/compositor/operations/COM_GammaCorrectOperation.h index 5c6307fe6d0..e13d96184ad 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.h +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.h @@ -25,9 +25,9 @@ namespace blender::compositor { class GammaCorrectOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; public: GammaCorrectOperation(); @@ -35,17 +35,17 @@ class GammaCorrectOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -55,9 +55,9 @@ class GammaCorrectOperation : public MultiThreadedOperation { class GammaUncorrectOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; public: GammaUncorrectOperation(); @@ -65,17 +65,17 @@ class GammaUncorrectOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_GammaOperation.cc b/source/blender/compositor/operations/COM_GammaOperation.cc index a809b16f523..fb8f18bcc28 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.cc +++ b/source/blender/compositor/operations/COM_GammaOperation.cc @@ -22,33 +22,33 @@ namespace blender::compositor { GammaOperation::GammaOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - inputProgram_ = nullptr; - inputGammaProgram_ = nullptr; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; + input_gamma_program_ = nullptr; flags.can_be_constant = true; } -void GammaOperation::initExecution() +void GammaOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); - inputGammaProgram_ = this->getInputSocketReader(1); + input_program_ = this->get_input_socket_reader(0); + input_gamma_program_ = this->get_input_socket_reader(1); } -void GammaOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void GammaOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { - float inputValue[4]; - float inputGamma[4]; + float input_value[4]; + float input_gamma[4]; - inputProgram_->readSampled(inputValue, x, y, sampler); - inputGammaProgram_->readSampled(inputGamma, x, y, sampler); - const float gamma = inputGamma[0]; + input_program_->read_sampled(input_value, x, y, sampler); + input_gamma_program_->read_sampled(input_gamma, x, y, sampler); + const float gamma = input_gamma[0]; /* check for negative to avoid nan's */ - output[0] = inputValue[0] > 0.0f ? powf(inputValue[0], gamma) : inputValue[0]; - output[1] = inputValue[1] > 0.0f ? powf(inputValue[1], gamma) : inputValue[1]; - output[2] = inputValue[2] > 0.0f ? powf(inputValue[2], gamma) : inputValue[2]; + output[0] = input_value[0] > 0.0f ? powf(input_value[0], gamma) : input_value[0]; + output[1] = input_value[1] > 0.0f ? powf(input_value[1], gamma) : input_value[1]; + output[2] = input_value[2] > 0.0f ? powf(input_value[2], gamma) : input_value[2]; - output[3] = inputValue[3]; + output[3] = input_value[3]; } void GammaOperation::update_memory_buffer_row(PixelCursor &p) @@ -65,10 +65,10 @@ void GammaOperation::update_memory_buffer_row(PixelCursor &p) } } -void GammaOperation::deinitExecution() +void GammaOperation::deinit_execution() { - inputProgram_ = nullptr; - inputGammaProgram_ = nullptr; + input_program_ = nullptr; + input_gamma_program_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GammaOperation.h b/source/blender/compositor/operations/COM_GammaOperation.h index 9b68a2c8a1b..d8703a0a19a 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.h +++ b/source/blender/compositor/operations/COM_GammaOperation.h @@ -25,10 +25,10 @@ namespace blender::compositor { class GammaOperation : public MultiThreadedRowOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; - SocketReader *inputGammaProgram_; + SocketReader *input_program_; + SocketReader *input_gamma_program_; public: GammaOperation(); @@ -36,17 +36,17 @@ class GammaOperation : public MultiThreadedRowOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_row(PixelCursor &p) override; }; diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc index fa742775f24..1a8c000cdd8 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc @@ -39,18 +39,18 @@ void GaussianAlphaBlurBaseOperation::init_data() } } -void GaussianAlphaBlurBaseOperation::initExecution() +void GaussianAlphaBlurBaseOperation::init_execution() { - BlurBaseOperation::initExecution(); + BlurBaseOperation::init_execution(); if (execution_model_ == eExecutionModel::FullFrame) { gausstab_ = BlurBaseOperation::make_gausstab(rad_, filtersize_); distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad_, filtersize_, falloff_); } } -void GaussianAlphaBlurBaseOperation::deinitExecution() +void GaussianAlphaBlurBaseOperation::deinit_execution() { - BlurBaseOperation::deinitExecution(); + BlurBaseOperation::deinit_execution(); if (gausstab_) { MEM_freeN(gausstab_); @@ -133,7 +133,7 @@ void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer * float value_max = finv_test(*it.in(0), do_invert); float distfacinv_max = 1.0f; /* 0 to 1 */ - const int step = QualityStepHelper::getStep(); + const int step = QualityStepHelper::get_step(); const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride; const int in_stride = elem_stride * step; int index = (coord_min - coord) + filtersize_; diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h index 307d8b0dff5..0f83a67cef7 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h @@ -36,8 +36,8 @@ class GaussianAlphaBlurBaseOperation : public BlurBaseOperation { GaussianAlphaBlurBaseOperation(eDimension dim); virtual void init_data() override; - virtual void initExecution() override; - virtual void deinitExecution() override; + virtual void init_execution() override; + virtual void deinit_execution() override; void get_area_of_interest(const int input_idx, const rcti &output_area, @@ -49,11 +49,11 @@ class GaussianAlphaBlurBaseOperation : public BlurBaseOperation { /** * Set subtract for Dilate/Erode functionality */ - void setSubtract(bool subtract) + void set_subtract(bool subtract) { do_subtract_ = subtract; } - void setFalloff(int falloff) + void set_falloff(int falloff) { falloff_ = falloff; } diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc index b854ea736b3..de0010fcb58 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc @@ -25,22 +25,22 @@ GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() { } -void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/) +void *GaussianAlphaXBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); + lock_mutex(); if (!sizeavailable_) { - updateGauss(); + update_gauss(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } -void GaussianAlphaXBlurOperation::initExecution() +void GaussianAlphaXBlurOperation::init_execution() { - GaussianAlphaBlurBaseOperation::initExecution(); + GaussianAlphaBlurBaseOperation::init_execution(); - initMutex(); + init_mutex(); if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(size_ * data_.sizex, 0.0f); @@ -51,10 +51,10 @@ void GaussianAlphaXBlurOperation::initExecution() } } -void GaussianAlphaXBlurOperation::updateGauss() +void GaussianAlphaXBlurOperation::update_gauss() { if (gausstab_ == nullptr) { - updateSize(); + update_size(); float rad = max_ff(size_ * data_.sizex, 0.0f); filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -62,7 +62,7 @@ void GaussianAlphaXBlurOperation::updateGauss() } if (distbuf_inv_ == nullptr) { - updateSize(); + update_size(); float rad = max_ff(size_ * data_.sizex, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -76,23 +76,23 @@ BLI_INLINE float finv_test(const float f, const bool test) return (LIKELY(test == false)) ? f : 1.0f - f; } -void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianAlphaXBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { const bool do_invert = do_subtract_; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); - const rcti &input_rect = inputBuffer->get_rect(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); + const rcti &input_rect = input_buffer->get_rect(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; - const rcti &rect = inputBuffer->get_rect(); + const rcti &rect = input_buffer->get_rect(); int xmin = max_ii(x - filtersize_, rect.xmin); int xmax = min_ii(x + filtersize_ + 1, rect.xmax); int ymin = max_ii(y, rect.ymin); /* *** this is the main part which is different to 'GaussianXBlurOperation' *** */ - int step = getStep(); + int step = get_step(); int bufferindex = ((xmin - bufferstartx)) + ((ymin - bufferstarty) * bufferwidth); /* gauss */ @@ -135,9 +135,9 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo output[0] = finv_test(value_final, do_invert); } -void GaussianAlphaXBlurOperation::deinitExecution() +void GaussianAlphaXBlurOperation::deinit_execution() { - GaussianAlphaBlurBaseOperation::deinitExecution(); + GaussianAlphaBlurBaseOperation::deinit_execution(); if (gausstab_) { MEM_freeN(gausstab_); @@ -149,40 +149,40 @@ void GaussianAlphaXBlurOperation::deinitExecution() distbuf_inv_ = nullptr; } - deinitMutex(); + deinit_mutex(); } -bool GaussianAlphaXBlurOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool GaussianAlphaXBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; #if 0 /* until we add size input */ - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + NodeOperation *operation = this->get_input_operation(1); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } else #endif { if (sizeavailable_ && gausstab_ != nullptr) { - newInput.xmax = input->xmax + filtersize_ + 1; - newInput.xmin = input->xmin - filtersize_ - 1; - newInput.ymax = input->ymax; - newInput.ymin = input->ymin; + new_input.xmax = input->xmax + filtersize_ + 1; + new_input.xmin = input->xmin - filtersize_ - 1; + new_input.ymax = input->ymax; + new_input.ymin = input->ymin; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } } diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h index 2a44c639665..31b4a582c4b 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h @@ -25,7 +25,7 @@ namespace blender::compositor { /* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ class GaussianAlphaXBlurOperation : public GaussianAlphaBlurBaseOperation { private: - void updateGauss(); + void update_gauss(); public: GaussianAlphaXBlurOperation(); @@ -33,22 +33,22 @@ class GaussianAlphaXBlurOperation : public GaussianAlphaBlurBaseOperation { /** * \brief The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * \brief initialize the execution */ - void initExecution() override; + void init_execution() override; /** * \brief Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void *initialize_tile_data(rcti *rect) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc index eaf3abc18cd..90a80e6779a 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc @@ -25,23 +25,23 @@ GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() { } -void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/) +void *GaussianAlphaYBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); + lock_mutex(); if (!sizeavailable_) { - updateGauss(); + update_gauss(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } /* TODO(manzanilla): to be removed with tiled implementation. */ -void GaussianAlphaYBlurOperation::initExecution() +void GaussianAlphaYBlurOperation::init_execution() { - GaussianAlphaBlurBaseOperation::initExecution(); + GaussianAlphaBlurBaseOperation::init_execution(); - initMutex(); + init_mutex(); if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(size_ * data_.sizey, 0.0f); @@ -53,10 +53,10 @@ void GaussianAlphaYBlurOperation::initExecution() } /* TODO(manzanilla): to be removed with tiled implementation. */ -void GaussianAlphaYBlurOperation::updateGauss() +void GaussianAlphaYBlurOperation::update_gauss() { if (gausstab_ == nullptr) { - updateSize(); + update_size(); float rad = max_ff(size_ * data_.sizey, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -65,7 +65,7 @@ void GaussianAlphaYBlurOperation::updateGauss() } if (distbuf_inv_ == nullptr) { - updateSize(); + update_size(); float rad = max_ff(size_ * data_.sizey, 0.0f); filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -78,13 +78,13 @@ BLI_INLINE float finv_test(const float f, const bool test) return (LIKELY(test == false)) ? f : 1.0f - f; } -void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianAlphaYBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { const bool do_invert = do_subtract_; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - const rcti &input_rect = inputBuffer->get_rect(); - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + const rcti &input_rect = input_buffer->get_rect(); + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; @@ -93,7 +93,7 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo int ymax = min_ii(y + filtersize_ + 1, input_rect.ymax); /* *** this is the main part which is different to 'GaussianYBlurOperation' *** */ - int step = getStep(); + int step = get_step(); /* gauss */ float alpha_accum = 0.0f; @@ -136,9 +136,9 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo output[0] = finv_test(value_final, do_invert); } -void GaussianAlphaYBlurOperation::deinitExecution() +void GaussianAlphaYBlurOperation::deinit_execution() { - GaussianAlphaBlurBaseOperation::deinitExecution(); + GaussianAlphaBlurBaseOperation::deinit_execution(); if (gausstab_) { MEM_freeN(gausstab_); @@ -150,40 +150,40 @@ void GaussianAlphaYBlurOperation::deinitExecution() distbuf_inv_ = nullptr; } - deinitMutex(); + deinit_mutex(); } -bool GaussianAlphaYBlurOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool GaussianAlphaYBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; #if 0 /* until we add size input */ - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + NodeOperation *operation = this->get_input_operation(1); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } else #endif { if (sizeavailable_ && gausstab_ != nullptr) { - newInput.xmax = input->xmax; - newInput.xmin = input->xmin; - newInput.ymax = input->ymax + filtersize_ + 1; - newInput.ymin = input->ymin - filtersize_ - 1; + new_input.xmax = input->xmax; + new_input.xmin = input->xmin; + new_input.ymax = input->ymax + filtersize_ + 1; + new_input.ymin = input->ymin - filtersize_ - 1; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } } diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h index ef01f7e0f92..28dc289c445 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h @@ -25,7 +25,7 @@ namespace blender::compositor { /* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ class GaussianAlphaYBlurOperation : public GaussianAlphaBlurBaseOperation { private: - void updateGauss(); + void update_gauss(); public: GaussianAlphaYBlurOperation(); @@ -33,22 +33,22 @@ class GaussianAlphaYBlurOperation : public GaussianAlphaBlurBaseOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * \brief initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void *initialize_tile_data(rcti *rect) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc index e6d3b3d4424..9f4ee376fbf 100644 --- a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc @@ -42,9 +42,9 @@ void GaussianBlurBaseOperation::init_data() } } -void GaussianBlurBaseOperation::initExecution() +void GaussianBlurBaseOperation::init_execution() { - BlurBaseOperation::initExecution(); + BlurBaseOperation::init_execution(); if (execution_model_ == eExecutionModel::FullFrame) { gausstab_ = BlurBaseOperation::make_gausstab(rad_, filtersize_); #ifdef BLI_HAVE_SSE2 @@ -53,9 +53,9 @@ void GaussianBlurBaseOperation::initExecution() } } -void GaussianBlurBaseOperation::deinitExecution() +void GaussianBlurBaseOperation::deinit_execution() { - BlurBaseOperation::deinitExecution(); + BlurBaseOperation::deinit_execution(); if (gausstab_) { MEM_freeN(gausstab_); @@ -126,7 +126,7 @@ void GaussianBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *outpu float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float multiplier_accum = 0.0f; - const int step = QualityStepHelper::getStep(); + const int step = QualityStepHelper::get_step(); const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride; const int in_stride = elem_stride * step; int gauss_idx = (coord_min - coord) + filtersize_; diff --git a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h index 0e3a86e63ec..31b045241f8 100644 --- a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h @@ -36,8 +36,8 @@ class GaussianBlurBaseOperation : public BlurBaseOperation { GaussianBlurBaseOperation(eDimension dim); virtual void init_data() override; - virtual void initExecution() override; - virtual void deinitExecution() override; + virtual void init_execution() override; + virtual void deinit_execution() override; void get_area_of_interest(const int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc index b5d6aa69423..db5f9c7c35d 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc @@ -27,25 +27,25 @@ GaussianBokehBlurOperation::GaussianBokehBlurOperation() : BlurBaseOperation(Dat gausstab_ = nullptr; } -void *GaussianBokehBlurOperation::initializeTileData(rcti * /*rect*/) +void *GaussianBokehBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); + lock_mutex(); if (!sizeavailable_) { - updateGauss(); + update_gauss(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } void GaussianBokehBlurOperation::init_data() { BlurBaseOperation::init_data(); - const float width = this->getWidth(); - const float height = this->getHeight(); + const float width = this->get_width(); + const float height = this->get_height(); if (!sizeavailable_) { - updateSize(); + update_size(); } radxf_ = size_ * (float)data_.sizex; @@ -59,18 +59,18 @@ void GaussianBokehBlurOperation::init_data() rady_ = ceil(radyf_); } -void GaussianBokehBlurOperation::initExecution() +void GaussianBokehBlurOperation::init_execution() { - BlurBaseOperation::initExecution(); + BlurBaseOperation::init_execution(); - initMutex(); + init_mutex(); if (sizeavailable_) { - updateGauss(); + update_gauss(); } } -void GaussianBokehBlurOperation::updateGauss() +void GaussianBokehBlurOperation::update_gauss() { if (gausstab_ == nullptr) { int ddwidth = 2 * radx_ + 1; @@ -109,18 +109,18 @@ void GaussianBokehBlurOperation::updateGauss() } } -void GaussianBokehBlurOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianBokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { - float tempColor[4]; - tempColor[0] = 0; - tempColor[1] = 0; - tempColor[2] = 0; - tempColor[3] = 0; + float temp_color[4]; + temp_color[0] = 0; + temp_color[1] = 0; + temp_color[2] = 0; + temp_color[3] = 0; float multiplier_accum = 0; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); - const rcti &input_rect = inputBuffer->get_rect(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); + const rcti &input_rect = input_buffer->get_rect(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; @@ -130,67 +130,68 @@ void GaussianBokehBlurOperation::executePixel(float output[4], int x, int y, voi int xmax = min_ii(x + radx_ + 1, input_rect.xmax); int index; - int step = QualityStepHelper::getStep(); - int offsetadd = QualityStepHelper::getOffsetAdd(); - const int addConst = (xmin - x + radx_); - const int mulConst = (radx_ * 2 + 1); + int step = QualityStepHelper::get_step(); + int offsetadd = QualityStepHelper::get_offset_add(); + const int add_const = (xmin - x + radx_); + const int mul_const = (radx_ * 2 + 1); for (int ny = ymin; ny < ymax; ny += step) { - index = ((ny - y) + rady_) * mulConst + addConst; + index = ((ny - y) + rady_) * mul_const + add_const; int bufferindex = ((xmin - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth); for (int nx = xmin; nx < xmax; nx += step) { const float multiplier = gausstab_[index]; - madd_v4_v4fl(tempColor, &buffer[bufferindex], multiplier); + madd_v4_v4fl(temp_color, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; index += step; bufferindex += offsetadd; } } - mul_v4_v4fl(output, tempColor, 1.0f / multiplier_accum); + mul_v4_v4fl(output, temp_color, 1.0f / multiplier_accum); } -void GaussianBokehBlurOperation::deinitExecution() +void GaussianBokehBlurOperation::deinit_execution() { - BlurBaseOperation::deinitExecution(); + BlurBaseOperation::deinit_execution(); if (gausstab_) { MEM_freeN(gausstab_); gausstab_ = nullptr; } - deinitMutex(); + deinit_mutex(); } -bool GaussianBokehBlurOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool GaussianBokehBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); + rcti new_input; + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; + NodeOperation *operation = this->get_input_operation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } if (sizeavailable_ && gausstab_ != nullptr) { - newInput.xmin = 0; - newInput.ymin = 0; - newInput.xmax = this->getWidth(); - newInput.ymax = this->getHeight(); + new_input.xmin = 0; + new_input.ymin = 0; + new_input.xmax = this->get_width(); + new_input.ymax = this->get_height(); } else { int addx = radx_; int addy = rady_; - newInput.xmax = input->xmax + addx; - newInput.xmin = input->xmin - addx; - newInput.ymax = input->ymax + addy; - newInput.ymin = input->ymin - addy; + new_input.xmax = input->xmax + addx; + new_input.xmin = input->xmin - addx; + new_input.ymax = input->ymax + addy; + new_input.ymin = input->ymin - addy; } - return BlurBaseOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return BlurBaseOperation::determine_depending_area_of_interest( + &new_input, read_operation, output); } void GaussianBokehBlurOperation::get_area_of_interest(const int input_idx, @@ -224,9 +225,9 @@ void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *outp const int xmin = max_ii(x - radx_, input_rect.xmin); const int xmax = min_ii(x + radx_ + 1, input_rect.xmax); - float tempColor[4] = {0}; + float temp_color[4] = {0}; float multiplier_accum = 0; - const int step = QualityStepHelper::getStep(); + const int step = QualityStepHelper::get_step(); const int elem_step = step * input->elem_stride; const int add_const = (xmin - x + radx_); const int mul_const = (radx_ * 2 + 1); @@ -236,12 +237,12 @@ void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *outp const int gauss_end = gauss_index + (xmax - xmin); for (; gauss_index < gauss_end; gauss_index += step, color += elem_step) { const float multiplier = gausstab_[gauss_index]; - madd_v4_v4fl(tempColor, color, multiplier); + madd_v4_v4fl(temp_color, color, multiplier); multiplier_accum += multiplier; } } - mul_v4_v4fl(it.out, tempColor, 1.0f / multiplier_accum); + mul_v4_v4fl(it.out, temp_color, 1.0f / multiplier_accum); } } @@ -256,8 +257,8 @@ GaussianBlurReferenceOperation::GaussianBlurReferenceOperation() void GaussianBlurReferenceOperation::init_data() { /* Setup variables for gausstab and area of interest. */ - data_.image_in_width = this->getWidth(); - data_.image_in_height = this->getHeight(); + data_.image_in_width = this->get_width(); + data_.image_in_height = this->get_height(); if (data_.relative) { switch (data_.aspect) { case CMP_NODE_BLUR_ASPECT_NONE: @@ -277,7 +278,7 @@ void GaussianBlurReferenceOperation::init_data() /* Horizontal. */ filtersizex_ = (float)data_.sizex; - int imgx = getWidth() / 2; + int imgx = get_width() / 2; if (filtersizex_ > imgx) { filtersizex_ = imgx; } @@ -288,7 +289,7 @@ void GaussianBlurReferenceOperation::init_data() /* Vertical. */ filtersizey_ = (float)data_.sizey; - int imgy = getHeight() / 2; + int imgy = get_height() / 2; if (filtersizey_ > imgy) { filtersizey_ = imgy; } @@ -298,20 +299,20 @@ void GaussianBlurReferenceOperation::init_data() rady_ = (float)filtersizey_; } -void *GaussianBlurReferenceOperation::initializeTileData(rcti * /*rect*/) +void *GaussianBlurReferenceOperation::initialize_tile_data(rcti * /*rect*/) { - void *buffer = getInputOperation(0)->initializeTileData(nullptr); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); return buffer; } -void GaussianBlurReferenceOperation::initExecution() +void GaussianBlurReferenceOperation::init_execution() { - BlurBaseOperation::initExecution(); + BlurBaseOperation::init_execution(); - updateGauss(); + update_gauss(); } -void GaussianBlurReferenceOperation::updateGauss() +void GaussianBlurReferenceOperation::update_gauss() { int i; int x = MAX2(filtersizex_, filtersizey_); @@ -321,23 +322,23 @@ void GaussianBlurReferenceOperation::updateGauss() } } -void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianBlurReferenceOperation::execute_pixel(float output[4], int x, int y, void *data) { MemoryBuffer *memorybuffer = (MemoryBuffer *)data; - float *buffer = memorybuffer->getBuffer(); + float *buffer = memorybuffer->get_buffer(); float *gausstabx, *gausstabcenty; float *gausstaby, *gausstabcentx; int i, j; float *src; float sum, val; float rval, gval, bval, aval; - int imgx = getWidth(); - int imgy = getHeight(); - float tempSize[4]; - inputSize_->read(tempSize, x, y, data); - float refSize = tempSize[0]; - int refradx = (int)(refSize * radx_); - int refrady = (int)(refSize * rady_); + int imgx = get_width(); + int imgy = get_height(); + float temp_size[4]; + input_size_->read(temp_size, x, y, data); + float ref_size = temp_size[0]; + int refradx = (int)(ref_size * radx_); + int refrady = (int)(ref_size * rady_); if (refradx > filtersizex_) { refradx = filtersizex_; } @@ -352,7 +353,7 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, } if (refradx == 1 && refrady == 1) { - memorybuffer->readNoCheck(output, x, y); + memorybuffer->read_no_check(output, x, y); } else { int minxr = x - refradx < 0 ? -x : -refradx; @@ -388,7 +389,7 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, } } -void GaussianBlurReferenceOperation::deinitExecution() +void GaussianBlurReferenceOperation::deinit_execution() { int x, i; x = MAX2(filtersizex_, filtersizey_); @@ -396,26 +397,26 @@ void GaussianBlurReferenceOperation::deinitExecution() MEM_freeN(maintabs_[i]); } MEM_freeN(maintabs_); - BlurBaseOperation::deinitExecution(); + BlurBaseOperation::deinit_execution(); } -bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool GaussianBlurReferenceOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - NodeOperation *operation = this->getInputOperation(1); + rcti new_input; + NodeOperation *operation = this->get_input_operation(1); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } int addx = data_.sizex + 2; int addy = data_.sizey + 2; - newInput.xmax = input->xmax + addx; - newInput.xmin = input->xmin - addx; - newInput.ymax = input->ymax + addy; - newInput.ymin = input->ymin - addy; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + new_input.xmax = input->xmax + addx; + new_input.xmin = input->xmin - addx; + new_input.ymax = input->ymax + addy; + new_input.ymin = input->ymin - addy; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void GaussianBlurReferenceOperation::get_area_of_interest(const int input_idx, @@ -465,8 +466,8 @@ void GaussianBlurReferenceOperation::update_memory_buffer_partial(MemoryBuffer * continue; } - const int w = getWidth(); - const int height = getHeight(); + 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; diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h index 5e6eeefa512..923daf7a447 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h @@ -30,26 +30,26 @@ class GaussianBokehBlurOperation : public BlurBaseOperation { int radx_, rady_; float radxf_; float radyf_; - void updateGauss(); + void update_gauss(); public: GaussianBokehBlurOperation(); void init_data() override; - void initExecution() override; - void *initializeTileData(rcti *rect) override; + void init_execution() override; + void *initialize_tile_data(rcti *rect) override; /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, @@ -63,7 +63,7 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation { private: float **maintabs_; - void updateGauss(); + void update_gauss(); int filtersizex_; int filtersizey_; float radx_; @@ -72,21 +72,21 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation { public: GaussianBlurReferenceOperation(); void init_data() override; - void initExecution() override; - void *initializeTileData(rcti *rect) override; + void init_execution() override; + void *initialize_tile_data(rcti *rect) override; /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc index 4720b3f69d0..b43db5ba664 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc @@ -25,23 +25,23 @@ GaussianXBlurOperation::GaussianXBlurOperation() : GaussianBlurBaseOperation(eDi { } -void *GaussianXBlurOperation::initializeTileData(rcti * /*rect*/) +void *GaussianXBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); + lock_mutex(); if (!sizeavailable_) { - updateGauss(); + update_gauss(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } /* TODO(manzanilla): to be removed with tiled implementation. */ -void GaussianXBlurOperation::initExecution() +void GaussianXBlurOperation::init_execution() { - GaussianBlurBaseOperation::initExecution(); + GaussianBlurBaseOperation::init_execution(); - initMutex(); + init_mutex(); if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(size_ * data_.sizex, 0.0f); @@ -56,10 +56,10 @@ void GaussianXBlurOperation::initExecution() } /* TODO(manzanilla): to be removed with tiled implementation. */ -void GaussianXBlurOperation::updateGauss() +void GaussianXBlurOperation::update_gauss() { if (gausstab_ == nullptr) { - updateSize(); + update_size(); float rad = max_ff(size_ * data_.sizex, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -71,14 +71,14 @@ void GaussianXBlurOperation::updateGauss() } } -void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianXBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float multiplier_accum = 0.0f; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - const rcti &input_rect = inputBuffer->get_rect(); - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + const rcti &input_rect = input_buffer->get_rect(); + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; @@ -86,8 +86,8 @@ void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *d int xmax = min_ii(x + filtersize_ + 1, input_rect.xmax); int ymin = max_ii(y, input_rect.ymin); - int step = getStep(); - int offsetadd = getOffsetAdd(); + int step = get_step(); + int offsetadd = get_offset_add(); int bufferindex = ((xmin - bufferstartx) * 4) + ((ymin - bufferstarty) * 4 * bufferwidth); #ifdef BLI_HAVE_SSE2 @@ -111,41 +111,45 @@ void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *d mul_v4_v4fl(output, color_accum, 1.0f / multiplier_accum); } -void GaussianXBlurOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void GaussianXBlurOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel gaussianXBlurOperationKernel = device->COM_clCreateKernel( - "gaussianXBlurOperationKernel", nullptr); + cl_kernel gaussian_xblur_operation_kernel = device->COM_cl_create_kernel( + "gaussian_xblur_operation_kernel", nullptr); cl_int filter_size = filtersize_; - cl_mem gausstab = clCreateBuffer(device->getContext(), + cl_mem gausstab = clCreateBuffer(device->get_context(), CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, sizeof(float) * (filtersize_ * 2 + 1), gausstab_, nullptr); - device->COM_clAttachMemoryBufferToKernelParameter( - gaussianXBlurOperationKernel, 0, 1, clMemToCleanUp, inputMemoryBuffers, inputProgram_); - device->COM_clAttachOutputMemoryBufferToKernelParameter( - gaussianXBlurOperationKernel, 2, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter( - gaussianXBlurOperationKernel, 3, outputMemoryBuffer); - clSetKernelArg(gaussianXBlurOperationKernel, 4, sizeof(cl_int), &filter_size); - device->COM_clAttachSizeToKernelParameter(gaussianXBlurOperationKernel, 5, this); - clSetKernelArg(gaussianXBlurOperationKernel, 6, sizeof(cl_mem), &gausstab); + device->COM_cl_attach_memory_buffer_to_kernel_parameter(gaussian_xblur_operation_kernel, + 0, + 1, + cl_mem_to_clean_up, + input_memory_buffers, + input_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + gaussian_xblur_operation_kernel, 2, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + gaussian_xblur_operation_kernel, 3, output_memory_buffer); + clSetKernelArg(gaussian_xblur_operation_kernel, 4, sizeof(cl_int), &filter_size); + device->COM_cl_attach_size_to_kernel_parameter(gaussian_xblur_operation_kernel, 5, this); + clSetKernelArg(gaussian_xblur_operation_kernel, 6, sizeof(cl_mem), &gausstab); - device->COM_clEnqueueRange(gaussianXBlurOperationKernel, outputMemoryBuffer, 7, this); + device->COM_cl_enqueue_range(gaussian_xblur_operation_kernel, output_memory_buffer, 7, this); clReleaseMemObject(gausstab); } -void GaussianXBlurOperation::deinitExecution() +void GaussianXBlurOperation::deinit_execution() { - GaussianBlurBaseOperation::deinitExecution(); + GaussianBlurBaseOperation::deinit_execution(); if (gausstab_) { MEM_freeN(gausstab_); @@ -158,40 +162,39 @@ void GaussianXBlurOperation::deinitExecution() } #endif - deinitMutex(); + deinit_mutex(); } -bool GaussianXBlurOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool GaussianXBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; if (!sizeavailable_) { - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; + NodeOperation *operation = this->get_input_operation(1); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } } { if (sizeavailable_ && gausstab_ != nullptr) { - newInput.xmax = input->xmax + filtersize_ + 1; - newInput.xmin = input->xmin - filtersize_ - 1; - newInput.ymax = input->ymax; - newInput.ymin = input->ymin; + new_input.xmax = input->xmax + filtersize_ + 1; + new_input.xmin = input->xmin - filtersize_ - 1; + new_input.ymax = input->ymax; + new_input.ymin = input->ymin; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } } diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h index 1bb77031056..b9bbe1df900 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h @@ -25,7 +25,7 @@ namespace blender::compositor { /* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ class GaussianXBlurOperation : public GaussianBlurBaseOperation { private: - void updateGauss(); + void update_gauss(); public: GaussianXBlurOperation(); @@ -33,31 +33,31 @@ class GaussianXBlurOperation : public GaussianBlurBaseOperation { /** * \brief The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; /** * \brief initialize the execution */ - void initExecution() override; + void init_execution() override; /** * \brief Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void *initialize_tile_data(rcti *rect) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void checkOpenCL() + void check_opencl() { flags.open_cl = (data_.sizex >= 128); } diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc index 50edc0bea29..639536ebcc1 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc @@ -25,22 +25,22 @@ GaussianYBlurOperation::GaussianYBlurOperation() : GaussianBlurBaseOperation(eDi { } -void *GaussianYBlurOperation::initializeTileData(rcti * /*rect*/) +void *GaussianYBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); + lock_mutex(); if (!sizeavailable_) { - updateGauss(); + update_gauss(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } -void GaussianYBlurOperation::initExecution() +void GaussianYBlurOperation::init_execution() { - GaussianBlurBaseOperation::initExecution(); + GaussianBlurBaseOperation::init_execution(); - initMutex(); + init_mutex(); if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(size_ * data_.sizey, 0.0f); @@ -53,10 +53,10 @@ void GaussianYBlurOperation::initExecution() } } -void GaussianYBlurOperation::updateGauss() +void GaussianYBlurOperation::update_gauss() { if (gausstab_ == nullptr) { - updateSize(); + update_size(); float rad = max_ff(size_ * data_.sizey, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -68,14 +68,14 @@ void GaussianYBlurOperation::updateGauss() } } -void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianYBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float multiplier_accum = 0.0f; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - const rcti &input_rect = inputBuffer->get_rect(); - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + const rcti &input_rect = input_buffer->get_rect(); + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; @@ -84,14 +84,14 @@ void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *d int ymax = min_ii(y + filtersize_ + 1, input_rect.ymax); int index; - int step = getStep(); - const int bufferIndexx = ((xmin - bufferstartx) * 4); + int step = get_step(); + const int buffer_indexx = ((xmin - bufferstartx) * 4); #ifdef BLI_HAVE_SSE2 __m128 accum_r = _mm_load_ps(color_accum); for (int ny = ymin; ny < ymax; ny += step) { index = (ny - y) + filtersize_; - int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth); + int bufferindex = buffer_indexx + ((ny - bufferstarty) * 4 * bufferwidth); const float multiplier = gausstab_[index]; __m128 reg_a = _mm_load_ps(&buffer[bufferindex]); reg_a = _mm_mul_ps(reg_a, gausstab_sse_[index]); @@ -102,7 +102,7 @@ void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *d #else for (int ny = ymin; ny < ymax; ny += step) { index = (ny - y) + filtersize_; - int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth); + int bufferindex = buffer_indexx + ((ny - bufferstarty) * 4 * bufferwidth); const float multiplier = gausstab_[index]; madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; @@ -111,41 +111,45 @@ void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *d mul_v4_v4fl(output, color_accum, 1.0f / multiplier_accum); } -void GaussianYBlurOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void GaussianYBlurOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel gaussianYBlurOperationKernel = device->COM_clCreateKernel( - "gaussianYBlurOperationKernel", nullptr); + cl_kernel gaussian_yblur_operation_kernel = device->COM_cl_create_kernel( + "gaussian_yblur_operation_kernel", nullptr); cl_int filter_size = filtersize_; - cl_mem gausstab = clCreateBuffer(device->getContext(), + cl_mem gausstab = clCreateBuffer(device->get_context(), CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, sizeof(float) * (filtersize_ * 2 + 1), gausstab_, nullptr); - device->COM_clAttachMemoryBufferToKernelParameter( - gaussianYBlurOperationKernel, 0, 1, clMemToCleanUp, inputMemoryBuffers, inputProgram_); - device->COM_clAttachOutputMemoryBufferToKernelParameter( - gaussianYBlurOperationKernel, 2, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter( - gaussianYBlurOperationKernel, 3, outputMemoryBuffer); - clSetKernelArg(gaussianYBlurOperationKernel, 4, sizeof(cl_int), &filter_size); - device->COM_clAttachSizeToKernelParameter(gaussianYBlurOperationKernel, 5, this); - clSetKernelArg(gaussianYBlurOperationKernel, 6, sizeof(cl_mem), &gausstab); + device->COM_cl_attach_memory_buffer_to_kernel_parameter(gaussian_yblur_operation_kernel, + 0, + 1, + cl_mem_to_clean_up, + input_memory_buffers, + input_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + gaussian_yblur_operation_kernel, 2, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + gaussian_yblur_operation_kernel, 3, output_memory_buffer); + clSetKernelArg(gaussian_yblur_operation_kernel, 4, sizeof(cl_int), &filter_size); + device->COM_cl_attach_size_to_kernel_parameter(gaussian_yblur_operation_kernel, 5, this); + clSetKernelArg(gaussian_yblur_operation_kernel, 6, sizeof(cl_mem), &gausstab); - device->COM_clEnqueueRange(gaussianYBlurOperationKernel, outputMemoryBuffer, 7, this); + device->COM_cl_enqueue_range(gaussian_yblur_operation_kernel, output_memory_buffer, 7, this); clReleaseMemObject(gausstab); } -void GaussianYBlurOperation::deinitExecution() +void GaussianYBlurOperation::deinit_execution() { - GaussianBlurBaseOperation::deinitExecution(); + GaussianBlurBaseOperation::deinit_execution(); if (gausstab_) { MEM_freeN(gausstab_); @@ -158,40 +162,39 @@ void GaussianYBlurOperation::deinitExecution() } #endif - deinitMutex(); + deinit_mutex(); } -bool GaussianYBlurOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool GaussianYBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; if (!sizeavailable_) { - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; + NodeOperation *operation = this->get_input_operation(1); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } } { if (sizeavailable_ && gausstab_ != nullptr) { - newInput.xmax = input->xmax; - newInput.xmin = input->xmin; - newInput.ymax = input->ymax + filtersize_ + 1; - newInput.ymin = input->ymin - filtersize_ - 1; + new_input.xmax = input->xmax; + new_input.xmin = input->xmin; + new_input.ymax = input->ymax + filtersize_ + 1; + new_input.ymin = input->ymin - filtersize_ - 1; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } } diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h index 92c2d71f487..4390c9544e4 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h @@ -25,7 +25,7 @@ namespace blender::compositor { /* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ class GaussianYBlurOperation : public GaussianBlurBaseOperation { private: - void updateGauss(); + void update_gauss(); public: GaussianYBlurOperation(); @@ -33,31 +33,31 @@ class GaussianYBlurOperation : public GaussianBlurBaseOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; /** * \brief initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void *initialize_tile_data(rcti *rect) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void checkOpenCL() + void check_opencl() { flags.open_cl = (data_.sizex >= 128); } diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cc b/source/blender/compositor/operations/COM_GlareBaseOperation.cc index 0987f0efb31..37082f29579 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cc @@ -22,52 +22,52 @@ namespace blender::compositor { GlareBaseOperation::GlareBaseOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); settings_ = nullptr; flags.is_fullframe_operation = true; is_output_rendered_ = false; } -void GlareBaseOperation::initExecution() +void GlareBaseOperation::init_execution() { - SingleThreadedOperation::initExecution(); - inputProgram_ = getInputSocketReader(0); + SingleThreadedOperation::init_execution(); + input_program_ = get_input_socket_reader(0); } -void GlareBaseOperation::deinitExecution() +void GlareBaseOperation::deinit_execution() { - inputProgram_ = nullptr; - SingleThreadedOperation::deinitExecution(); + input_program_ = nullptr; + SingleThreadedOperation::deinit_execution(); } -MemoryBuffer *GlareBaseOperation::createMemoryBuffer(rcti *rect2) +MemoryBuffer *GlareBaseOperation::create_memory_buffer(rcti *rect2) { - MemoryBuffer *tile = (MemoryBuffer *)inputProgram_->initializeTileData(rect2); + MemoryBuffer *tile = (MemoryBuffer *)input_program_->initialize_tile_data(rect2); rcti rect; rect.xmin = 0; rect.ymin = 0; - rect.xmax = getWidth(); - rect.ymax = getHeight(); + rect.xmax = get_width(); + rect.ymax = get_height(); MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); - float *data = result->getBuffer(); - this->generateGlare(data, tile, settings_); + float *data = result->get_buffer(); + this->generate_glare(data, tile, settings_); return result; } -bool GlareBaseOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool GlareBaseOperation::determine_depending_area_of_interest(rcti * /*input*/, + ReadBufferOperation *read_operation, + rcti *output) { - if (isCached()) { + if (is_cached()) { return false; } - rcti newInput; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + rcti new_input; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void GlareBaseOperation::get_area_of_interest(const int input_idx, @@ -77,9 +77,9 @@ void GlareBaseOperation::get_area_of_interest(const int input_idx, BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); r_input_area.xmin = 0; - r_input_area.xmax = this->getWidth(); + r_input_area.xmax = this->get_width(); r_input_area.ymin = 0; - r_input_area.ymax = this->getHeight(); + r_input_area.ymax = this->get_height(); } void GlareBaseOperation::update_memory_buffer(MemoryBuffer *output, @@ -93,7 +93,7 @@ void GlareBaseOperation::update_memory_buffer(MemoryBuffer *output, input = input->inflate(); } - this->generateGlare(output->getBuffer(), input, settings_); + this->generate_glare(output->get_buffer(), input, settings_); is_output_rendered_ = true; if (is_input_inflated) { diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h index a50042f1ff4..09db5efcec9 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.h +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h @@ -40,9 +40,9 @@ typedef float fRGB[4]; class GlareBaseOperation : public SingleThreadedOperation { private: /** - * \brief Cached reference to the inputProgram + * \brief Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; /** * \brief settings of the glare node. @@ -55,20 +55,20 @@ class GlareBaseOperation : public SingleThreadedOperation { /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setGlareSettings(NodeGlare *settings) + void set_glare_settings(NodeGlare *settings) { settings_ = settings; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, @@ -81,9 +81,9 @@ class GlareBaseOperation : public SingleThreadedOperation { protected: GlareBaseOperation(); - virtual void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) = 0; + virtual void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) = 0; - MemoryBuffer *createMemoryBuffer(rcti *rect) override; + MemoryBuffer *create_memory_buffer(rcti *rect) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc index 2142a1b822e..fa2e2597452 100644 --- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc +++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc @@ -27,7 +27,7 @@ namespace blender::compositor { using fREAL = float; /* Returns next highest power of 2 of x, as well its log2 in L2. */ -static unsigned int nextPow2(unsigned int x, unsigned int *L2) +static unsigned int next_pow2(unsigned int x, unsigned int *L2) { unsigned int pw, x_notpow2 = x & (x - 1); *L2 = 0; @@ -262,24 +262,24 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) int x, y, ch; int xbl, ybl, nxb, nyb, xbsz, ybsz; bool in2done = false; - const unsigned int kernelWidth = in2->getWidth(); - const unsigned int kernelHeight = in2->getHeight(); - const unsigned int imageWidth = in1->getWidth(); - const unsigned int imageHeight = in1->getHeight(); - float *kernelBuffer = in2->getBuffer(); - float *imageBuffer = in1->getBuffer(); + const unsigned int kernel_width = in2->get_width(); + const unsigned int kernel_height = in2->get_height(); + const unsigned int image_width = in1->get_width(); + const unsigned int image_height = in1->get_height(); + float *kernel_buffer = in2->get_buffer(); + float *image_buffer = in1->get_buffer(); MemoryBuffer *rdst = new MemoryBuffer(DataType::Color, in1->get_rect()); - memset(rdst->getBuffer(), + memset(rdst->get_buffer(), 0, - rdst->getWidth() * rdst->getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); + rdst->get_width() * rdst->get_height() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); /* Convolution result width & height. */ - w2 = 2 * kernelWidth - 1; - h2 = 2 * kernelHeight - 1; + w2 = 2 * kernel_width - 1; + h2 = 2 * kernel_height - 1; /* FFT pow2 required size & log2. */ - w2 = nextPow2(w2, &log2_w); - h2 = nextPow2(h2, &log2_h); + w2 = next_pow2(w2, &log2_w); + h2 = next_pow2(h2, &log2_h); /* Allocate space. */ data1 = (fREAL *)MEM_callocN(3 * w2 * h2 * sizeof(fREAL), "convolve_fast FHT data1"); @@ -287,9 +287,9 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) /* Normalize convolutor. */ wt[0] = wt[1] = wt[2] = 0.0f; - for (y = 0; y < kernelHeight; y++) { - colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS]; - for (x = 0; x < kernelWidth; x++) { + for (y = 0; y < kernel_height; y++) { + colp = (fRGB *)&kernel_buffer[y * kernel_width * COM_DATA_TYPE_COLOR_CHANNELS]; + for (x = 0; x < kernel_width; x++) { add_v3_v3(wt, colp[x]); } } @@ -302,9 +302,9 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) if (wt[2] != 0.0f) { wt[2] = 1.0f / wt[2]; } - for (y = 0; y < kernelHeight; y++) { - colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS]; - for (x = 0; x < kernelWidth; x++) { + for (y = 0; y < kernel_height; y++) { + colp = (fRGB *)&kernel_buffer[y * kernel_width * COM_DATA_TYPE_COLOR_CHANNELS]; + for (x = 0; x < kernel_width; x++) { mul_v3_v3(colp[x], wt); } } @@ -313,16 +313,16 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) * only need to calc data1 once. */ /* Block add-overlap. */ - hw = kernelWidth >> 1; - hh = kernelHeight >> 1; - xbsz = (w2 + 1) - kernelWidth; - ybsz = (h2 + 1) - kernelHeight; - nxb = imageWidth / xbsz; - if (imageWidth % xbsz) { + hw = kernel_width >> 1; + hh = kernel_height >> 1; + xbsz = (w2 + 1) - kernel_width; + ybsz = (h2 + 1) - kernel_height; + nxb = image_width / xbsz; + if (image_width % xbsz) { nxb++; } - nyb = imageHeight / ybsz; - if (imageHeight % ybsz) { + nyb = image_height / ybsz; + if (image_height % ybsz) { nyb++; } for (ybl = 0; ybl < nyb; ybl++) { @@ -335,10 +335,10 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) /* Only need to calc fht data from in2 once, can re-use for every block. */ if (!in2done) { /* in2, channel ch -> data1 */ - for (y = 0; y < kernelHeight; y++) { + for (y = 0; y < kernel_height; y++) { fp = &data1ch[y * w2]; - colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS]; - for (x = 0; x < kernelWidth; x++) { + colp = (fRGB *)&kernel_buffer[y * kernel_width * COM_DATA_TYPE_COLOR_CHANNELS]; + for (x = 0; x < kernel_width; x++) { fp[x] = colp[x][ch]; } } @@ -348,14 +348,14 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) memset(data2, 0, w2 * h2 * sizeof(fREAL)); for (y = 0; y < ybsz; y++) { int yy = ybl * ybsz + y; - if (yy >= imageHeight) { + if (yy >= image_height) { continue; } fp = &data2[y * w2]; - colp = (fRGB *)&imageBuffer[yy * imageWidth * COM_DATA_TYPE_COLOR_CHANNELS]; + colp = (fRGB *)&image_buffer[yy * image_width * COM_DATA_TYPE_COLOR_CHANNELS]; for (x = 0; x < xbsz; x++) { int xx = xbl * xbsz + x; - if (xx >= imageWidth) { + if (xx >= image_width) { continue; } fp[x] = colp[xx][ch]; @@ -365,9 +365,9 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) /* Forward FHT * zero pad data start is different for each == height+1. */ if (!in2done) { - FHT2D(data1ch, log2_w, log2_h, kernelHeight + 1, 0); + FHT2D(data1ch, log2_w, log2_h, kernel_height + 1, 0); } - FHT2D(data2, log2_w, log2_h, kernelHeight + 1, 0); + FHT2D(data2, log2_w, log2_h, kernel_height + 1, 0); /* FHT2D transposed data, row/col now swapped * convolve & inverse FHT. */ @@ -378,14 +378,14 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) /* Overlap-add result. */ for (y = 0; y < (int)h2; y++) { const int yy = ybl * ybsz + y - hh; - if ((yy < 0) || (yy >= imageHeight)) { + if ((yy < 0) || (yy >= image_height)) { continue; } fp = &data2[y * w2]; - colp = (fRGB *)&rdst->getBuffer()[yy * imageWidth * COM_DATA_TYPE_COLOR_CHANNELS]; + colp = (fRGB *)&rdst->get_buffer()[yy * image_width * COM_DATA_TYPE_COLOR_CHANNELS]; for (x = 0; x < (int)w2; x++) { const int xx = xbl * xbsz + x - hw; - if ((xx < 0) || (xx >= imageWidth)) { + if ((xx < 0) || (xx >= image_width)) { continue; } colp[xx][ch] += fp[x]; @@ -399,14 +399,14 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) MEM_freeN(data2); MEM_freeN(data1); memcpy(dst, - rdst->getBuffer(), - sizeof(float) * imageWidth * imageHeight * COM_DATA_TYPE_COLOR_CHANNELS); + rdst->get_buffer(), + sizeof(float) * image_width * image_height * COM_DATA_TYPE_COLOR_CHANNELS); delete (rdst); } -void GlareFogGlowOperation::generateGlare(float *data, - MemoryBuffer *inputTile, - NodeGlare *settings) +void GlareFogGlowOperation::generate_glare(float *data, + MemoryBuffer *input_tile, + NodeGlare *settings) { int x, y; float scale, u, v, r, w, d; @@ -417,9 +417,9 @@ void GlareFogGlowOperation::generateGlare(float *data, /* Temp. src image * make the convolution kernel. */ - rcti kernelRect; - BLI_rcti_init(&kernelRect, 0, sz, 0, sz); - ckrn = new MemoryBuffer(DataType::Color, kernelRect); + rcti kernel_rect; + BLI_rcti_init(&kernel_rect, 0, sz, 0, sz); + ckrn = new MemoryBuffer(DataType::Color, kernel_rect); scale = 0.25f * sqrtf((float)(sz * sz)); @@ -437,11 +437,11 @@ void GlareFogGlowOperation::generateGlare(float *data, * actually, Hanning window is ok, `cos^2` for some reason is slower. */ w = (0.5f + 0.5f * cosf(u * (float)M_PI)) * (0.5f + 0.5f * cosf(v * (float)M_PI)); mul_v3_fl(fcol, w); - ckrn->writePixel(x, y, fcol); + ckrn->write_pixel(x, y, fcol); } } - convolve(data, inputTile, ckrn); + convolve(data, input_tile, ckrn); delete ckrn; } diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h index 5701f76ab13..648351fa692 100644 --- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h +++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h @@ -31,7 +31,7 @@ class GlareFogGlowOperation : public GlareBaseOperation { } protected: - void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.cc b/source/blender/compositor/operations/COM_GlareGhostOperation.cc index c5ecfed024c..930c723e94e 100644 --- a/source/blender/compositor/operations/COM_GlareGhostOperation.cc +++ b/source/blender/compositor/operations/COM_GlareGhostOperation.cc @@ -21,7 +21,7 @@ namespace blender::compositor { -static float smoothMask(float x, float y) +static float smooth_mask(float x, float y) { float t; x = 2.0f * x - 1.0f; @@ -33,7 +33,9 @@ static float smoothMask(float x, float y) return 0.0f; } -void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) +void GlareGhostOperation::generate_glare(float *data, + MemoryBuffer *input_tile, + NodeGlare *settings) { const int qt = 1 << settings->quality; const float s1 = 4.0f / (float)qt, s2 = 2.0f * s1; @@ -42,8 +44,8 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No float sc, isc, u, v, sm, s, t, ofs, scalef[64]; const float cmo = 1.0f - settings->colmod; - MemoryBuffer gbuf(*inputTile); - MemoryBuffer tbuf1(*inputTile); + MemoryBuffer gbuf(*input_tile); + MemoryBuffer tbuf1(*input_tile); bool breaked = false; @@ -51,7 +53,7 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No if (!breaked) { FastGaussianBlurOperation::IIR_gauss(&tbuf1, s1, 1, 3); } - if (isBraked()) { + if (is_braked()) { breaked = true; } if (!breaked) { @@ -60,19 +62,19 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No MemoryBuffer tbuf2(tbuf1); - if (isBraked()) { + if (is_braked()) { breaked = true; } if (!breaked) { FastGaussianBlurOperation::IIR_gauss(&tbuf2, s2, 0, 3); } - if (isBraked()) { + if (is_braked()) { breaked = true; } if (!breaked) { FastGaussianBlurOperation::IIR_gauss(&tbuf2, s2, 1, 3); } - if (isBraked()) { + if (is_braked()) { breaked = true; } if (!breaked) { @@ -100,59 +102,59 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No sc = 2.13; isc = -0.97; - for (y = 0; y < gbuf.getHeight() && (!breaked); y++) { - v = ((float)y + 0.5f) / (float)gbuf.getHeight(); - for (x = 0; x < gbuf.getWidth(); x++) { - u = ((float)x + 0.5f) / (float)gbuf.getWidth(); + for (y = 0; y < gbuf.get_height() && (!breaked); y++) { + v = ((float)y + 0.5f) / (float)gbuf.get_height(); + for (x = 0; x < gbuf.get_width(); x++) { + u = ((float)x + 0.5f) / (float)gbuf.get_width(); s = (u - 0.5f) * sc + 0.5f; t = (v - 0.5f) * sc + 0.5f; - tbuf1.readBilinear(c, s * gbuf.getWidth(), t * gbuf.getHeight()); - sm = smoothMask(s, t); + tbuf1.read_bilinear(c, s * gbuf.get_width(), t * gbuf.get_height()); + sm = smooth_mask(s, t); mul_v3_fl(c, sm); s = (u - 0.5f) * isc + 0.5f; t = (v - 0.5f) * isc + 0.5f; - tbuf2.readBilinear(tc, s * gbuf.getWidth() - 0.5f, t * gbuf.getHeight() - 0.5f); - sm = smoothMask(s, t); + tbuf2.read_bilinear(tc, s * gbuf.get_width() - 0.5f, t * gbuf.get_height() - 0.5f); + sm = smooth_mask(s, t); madd_v3_v3fl(c, tc, sm); - gbuf.writePixel(x, y, c); + gbuf.write_pixel(x, y, c); } - if (isBraked()) { + if (is_braked()) { breaked = true; } } - memset(tbuf1.getBuffer(), + memset(tbuf1.get_buffer(), 0, - tbuf1.getWidth() * tbuf1.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); + tbuf1.get_width() * tbuf1.get_height() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); for (n = 1; n < settings->iter && (!breaked); n++) { - for (y = 0; y < gbuf.getHeight() && (!breaked); y++) { - v = ((float)y + 0.5f) / (float)gbuf.getHeight(); - for (x = 0; x < gbuf.getWidth(); x++) { - u = ((float)x + 0.5f) / (float)gbuf.getWidth(); + for (y = 0; y < gbuf.get_height() && (!breaked); y++) { + v = ((float)y + 0.5f) / (float)gbuf.get_height(); + for (x = 0; x < gbuf.get_width(); x++) { + u = ((float)x + 0.5f) / (float)gbuf.get_width(); tc[0] = tc[1] = tc[2] = 0.0f; for (p = 0; p < 4; p++) { np = (n << 2) + p; s = (u - 0.5f) * scalef[np] + 0.5f; t = (v - 0.5f) * scalef[np] + 0.5f; - gbuf.readBilinear(c, s * gbuf.getWidth() - 0.5f, t * gbuf.getHeight() - 0.5f); + gbuf.read_bilinear(c, s * gbuf.get_width() - 0.5f, t * gbuf.get_height() - 0.5f); mul_v3_v3(c, cm[np]); - sm = smoothMask(s, t) * 0.25f; + sm = smooth_mask(s, t) * 0.25f; madd_v3_v3fl(tc, c, sm); } - tbuf1.addPixel(x, y, tc); + tbuf1.add_pixel(x, y, tc); } - if (isBraked()) { + if (is_braked()) { breaked = true; } } - memcpy(gbuf.getBuffer(), - tbuf1.getBuffer(), - tbuf1.getWidth() * tbuf1.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); + memcpy(gbuf.get_buffer(), + tbuf1.get_buffer(), + tbuf1.get_width() * tbuf1.get_height() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); } memcpy(data, - gbuf.getBuffer(), - gbuf.getWidth() * gbuf.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); + gbuf.get_buffer(), + gbuf.get_width() * gbuf.get_height() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.h b/source/blender/compositor/operations/COM_GlareGhostOperation.h index 60256d8e0ef..e84084c3f6e 100644 --- a/source/blender/compositor/operations/COM_GlareGhostOperation.h +++ b/source/blender/compositor/operations/COM_GlareGhostOperation.h @@ -31,7 +31,7 @@ class GlareGhostOperation : public GlareBaseOperation { } protected: - void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc index cc24a50a307..5d8dca055aa 100644 --- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc +++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc @@ -20,26 +20,26 @@ namespace blender::compositor { -void GlareSimpleStarOperation::generateGlare(float *data, - MemoryBuffer *inputTile, - NodeGlare *settings) +void GlareSimpleStarOperation::generate_glare(float *data, + MemoryBuffer *input_tile, + NodeGlare *settings) { int i, x, y, ym, yp, xm, xp; float c[4] = {0, 0, 0, 0}, tc[4] = {0, 0, 0, 0}; const float f1 = 1.0f - settings->fade; const float f2 = (1.0f - f1) * 0.5f; - MemoryBuffer tbuf1(*inputTile); - MemoryBuffer tbuf2(*inputTile); + MemoryBuffer tbuf1(*input_tile); + MemoryBuffer tbuf2(*input_tile); bool breaked = false; for (i = 0; i < settings->iter && (!breaked); i++) { // // (x || x-1, y-1) to (x || x+1, y+1) // // F - for (y = 0; y < this->getHeight() && (!breaked); y++) { + for (y = 0; y < this->get_height() && (!breaked); y++) { ym = y - i; yp = y + i; - for (x = 0; x < this->getWidth(); x++) { + for (x = 0; x < this->get_width(); x++) { xm = x - i; xp = x + i; tbuf1.read(c, x, y); @@ -49,7 +49,7 @@ void GlareSimpleStarOperation::generateGlare(float *data, tbuf1.read(tc, (settings->star_45 ? xp : x), yp); madd_v3_v3fl(c, tc, f2); c[3] = 1.0f; - tbuf1.writePixel(x, y, c); + tbuf1.write_pixel(x, y, c); tbuf2.read(c, x, y); mul_v3_fl(c, f1); @@ -58,17 +58,17 @@ void GlareSimpleStarOperation::generateGlare(float *data, tbuf2.read(tc, xp, (settings->star_45 ? ym : y)); madd_v3_v3fl(c, tc, f2); c[3] = 1.0f; - tbuf2.writePixel(x, y, c); + tbuf2.write_pixel(x, y, c); } - if (isBraked()) { + if (is_braked()) { breaked = true; } } // // B - for (y = this->getHeight() - 1; y >= 0 && (!breaked); y--) { + for (y = this->get_height() - 1; y >= 0 && (!breaked); y--) { ym = y - i; yp = y + i; - for (x = this->getWidth() - 1; x >= 0; x--) { + for (x = this->get_width() - 1; x >= 0; x--) { xm = x - i; xp = x + i; tbuf1.read(c, x, y); @@ -78,7 +78,7 @@ void GlareSimpleStarOperation::generateGlare(float *data, tbuf1.read(tc, (settings->star_45 ? xp : x), yp); madd_v3_v3fl(c, tc, f2); c[3] = 1.0f; - tbuf1.writePixel(x, y, c); + tbuf1.write_pixel(x, y, c); tbuf2.read(c, x, y); mul_v3_fl(c, f1); @@ -87,16 +87,16 @@ void GlareSimpleStarOperation::generateGlare(float *data, tbuf2.read(tc, xp, (settings->star_45 ? ym : y)); madd_v3_v3fl(c, tc, f2); c[3] = 1.0f; - tbuf2.writePixel(x, y, c); + tbuf2.write_pixel(x, y, c); } - if (isBraked()) { + if (is_braked()) { breaked = true; } } } - for (i = 0; i < this->getWidth() * this->getHeight() * 4; i++) { - data[i] = tbuf1.getBuffer()[i] + tbuf2.getBuffer()[i]; + for (i = 0; i < this->get_width() * this->get_height() * 4; i++) { + data[i] = tbuf1.get_buffer()[i] + tbuf2.get_buffer()[i]; } } diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h index 4a074f53e7b..be162bbda44 100644 --- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h +++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h @@ -31,7 +31,7 @@ class GlareSimpleStarOperation : public GlareBaseOperation { } protected: - void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc index 8ffe44d9a36..f4d9b588917 100644 --- a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc +++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc @@ -20,22 +20,22 @@ namespace blender::compositor { -void GlareStreaksOperation::generateGlare(float *data, - MemoryBuffer *inputTile, - NodeGlare *settings) +void GlareStreaksOperation::generate_glare(float *data, + MemoryBuffer *input_tile, + NodeGlare *settings) { int x, y, n; unsigned int nump = 0; float c1[4], c2[4], c3[4], c4[4]; float a, ang = DEG2RADF(360.0f) / (float)settings->streaks; - int size = inputTile->getWidth() * inputTile->getHeight(); + int size = input_tile->get_width() * input_tile->get_height(); int size4 = size * 4; bool breaked = false; - MemoryBuffer tsrc(*inputTile); - MemoryBuffer tdst(DataType::Color, inputTile->get_rect()); + MemoryBuffer tsrc(*input_tile); + MemoryBuffer tdst(DataType::Color, input_tile->get_rect()); tdst.clear(); memset(data, 0, size4 * sizeof(float)); @@ -50,9 +50,9 @@ void GlareStreaksOperation::generateGlare(float *data, /* Color-modulation amount relative to current pass. */ const float cmo = 1.0f - (float)pow((double)settings->colmod, (double)n + 1); - float *tdstcol = tdst.getBuffer(); - for (y = 0; y < tsrc.getHeight() && (!breaked); y++) { - for (x = 0; x < tsrc.getWidth(); x++, tdstcol += 4) { + float *tdstcol = tdst.get_buffer(); + for (y = 0; y < tsrc.get_height() && (!breaked); y++) { + for (x = 0; x < tsrc.get_width(); x++, tdstcol += 4) { /* First pass no offset, always same for every pass, exact copy, * otherwise results in uneven brightness, only need once. */ if (n == 0) { @@ -61,9 +61,9 @@ void GlareStreaksOperation::generateGlare(float *data, else { c1[0] = c1[1] = c1[2] = 0; } - tsrc.readBilinear(c2, x + vxp, y + vyp); - tsrc.readBilinear(c3, x + vxp * 2.0f, y + vyp * 2.0f); - tsrc.readBilinear(c4, x + vxp * 3.0f, y + vyp * 3.0f); + tsrc.read_bilinear(c2, x + vxp, y + vyp); + tsrc.read_bilinear(c3, x + vxp * 2.0f, y + vyp * 2.0f); + tsrc.read_bilinear(c4, x + vxp * 3.0f, y + vyp * 3.0f); /* Modulate color to look vaguely similar to a color spectrum. */ c2[1] *= cmo; c2[2] *= cmo; @@ -79,14 +79,14 @@ void GlareStreaksOperation::generateGlare(float *data, tdstcol[2] = 0.5f * (tdstcol[2] + c1[2] + wt * (c2[2] + wt * (c3[2] + wt * c4[2]))); tdstcol[3] = 1.0f; } - if (isBraked()) { + if (is_braked()) { breaked = true; } } - memcpy(tsrc.getBuffer(), tdst.getBuffer(), sizeof(float) * size4); + memcpy(tsrc.get_buffer(), tdst.get_buffer(), sizeof(float) * size4); } - float *sourcebuffer = tsrc.getBuffer(); + float *sourcebuffer = tsrc.get_buffer(); float factor = 1.0f / (float)(6 - settings->iter); for (int i = 0; i < size4; i += 4) { madd_v3_v3fl(&data[i], &sourcebuffer[i], factor); @@ -94,7 +94,7 @@ void GlareStreaksOperation::generateGlare(float *data, } tdst.clear(); - memcpy(tsrc.getBuffer(), inputTile->getBuffer(), sizeof(float) * size4); + memcpy(tsrc.get_buffer(), input_tile->get_buffer(), sizeof(float) * size4); nump++; } } diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.h b/source/blender/compositor/operations/COM_GlareStreaksOperation.h index 487c910960a..bad73687afd 100644 --- a/source/blender/compositor/operations/COM_GlareStreaksOperation.h +++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.h @@ -31,7 +31,7 @@ class GlareStreaksOperation : public GlareBaseOperation { } protected: - void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc index 7135155cb68..0538b4e48a0 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc @@ -24,9 +24,9 @@ namespace blender::compositor { GlareThresholdOperation::GlareThresholdOperation() { - this->addInputSocket(DataType::Color, ResizeMode::FitAny); - this->addOutputSocket(DataType::Color); - inputProgram_ = nullptr; + this->add_input_socket(DataType::Color, ResizeMode::FitAny); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; } void GlareThresholdOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -38,19 +38,19 @@ void GlareThresholdOperation::determine_canvas(const rcti &preferred_area, rcti r_area.ymax = r_area.ymin + height; } -void GlareThresholdOperation::initExecution() +void GlareThresholdOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void GlareThresholdOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void GlareThresholdOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { const float threshold = settings_->threshold; - inputProgram_->readSampled(output, x, y, sampler); + input_program_->read_sampled(output, x, y, sampler); if (IMB_colormanagement_get_luminance(output) >= threshold) { output[0] -= threshold; output[1] -= threshold; @@ -65,9 +65,9 @@ void GlareThresholdOperation::executePixelSampled(float output[4], } } -void GlareThresholdOperation::deinitExecution() +void GlareThresholdOperation::deinit_execution() { - inputProgram_ = nullptr; + input_program_ = nullptr; } void GlareThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h index f37020b61a8..41eac34375d 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h @@ -26,9 +26,9 @@ namespace blender::compositor { class GlareThresholdOperation : public MultiThreadedOperation { private: /** - * \brief Cached reference to the inputProgram + * \brief Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; /** * \brief settings of the glare node. @@ -41,19 +41,19 @@ class GlareThresholdOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setGlareSettings(NodeGlare *settings) + void set_glare_settings(NodeGlare *settings) { settings_ = settings; } diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc index e2f4552c69c..d1fad29498a 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc @@ -26,36 +26,36 @@ namespace blender::compositor { HueSaturationValueCorrectOperation::HueSaturationValueCorrectOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); - inputProgram_ = nullptr; + input_program_ = nullptr; } -void HueSaturationValueCorrectOperation::initExecution() +void HueSaturationValueCorrectOperation::init_execution() { - CurveBaseOperation::initExecution(); - inputProgram_ = this->getInputSocketReader(0); + CurveBaseOperation::init_execution(); + input_program_ = this->get_input_socket_reader(0); } -void HueSaturationValueCorrectOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void HueSaturationValueCorrectOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float hsv[4], f; - inputProgram_->readSampled(hsv, x, y, sampler); + input_program_->read_sampled(hsv, x, y, sampler); /* adjust hue, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(curveMapping_, 0, hsv[0]); + f = BKE_curvemapping_evaluateF(curve_mapping_, 0, hsv[0]); hsv[0] += f - 0.5f; /* adjust saturation, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(curveMapping_, 1, hsv[0]); + f = BKE_curvemapping_evaluateF(curve_mapping_, 1, hsv[0]); hsv[1] *= (f * 2.0f); /* adjust value, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(curveMapping_, 2, hsv[0]); + f = BKE_curvemapping_evaluateF(curve_mapping_, 2, hsv[0]); hsv[2] *= (f * 2.0f); hsv[0] = hsv[0] - floorf(hsv[0]); /* mod 1.0 */ @@ -67,10 +67,10 @@ void HueSaturationValueCorrectOperation::executePixelSampled(float output[4], output[3] = hsv[3]; } -void HueSaturationValueCorrectOperation::deinitExecution() +void HueSaturationValueCorrectOperation::deinit_execution() { - CurveBaseOperation::deinitExecution(); - inputProgram_ = nullptr; + CurveBaseOperation::deinit_execution(); + input_program_ = nullptr; } void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -82,15 +82,15 @@ void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuff copy_v4_v4(hsv, it.in(0)); /* Adjust hue, scaling returned default 0.5 up to 1. */ - float f = BKE_curvemapping_evaluateF(curveMapping_, 0, hsv[0]); + float f = BKE_curvemapping_evaluateF(curve_mapping_, 0, hsv[0]); hsv[0] += f - 0.5f; /* Adjust saturation, scaling returned default 0.5 up to 1. */ - f = BKE_curvemapping_evaluateF(curveMapping_, 1, hsv[0]); + f = BKE_curvemapping_evaluateF(curve_mapping_, 1, hsv[0]); hsv[1] *= (f * 2.0f); /* Adjust value, scaling returned default 0.5 up to 1. */ - f = BKE_curvemapping_evaluateF(curveMapping_, 2, hsv[0]); + f = BKE_curvemapping_evaluateF(curve_mapping_, 2, hsv[0]); hsv[2] *= (f * 2.0f); hsv[0] = hsv[0] - floorf(hsv[0]); /* Mod 1.0. */ diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h index 26792a54d26..ac64d31e494 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h @@ -26,9 +26,9 @@ namespace blender::compositor { class HueSaturationValueCorrectOperation : public CurveBaseOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; public: HueSaturationValueCorrectOperation(); @@ -36,17 +36,17 @@ class HueSaturationValueCorrectOperation : public CurveBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.cc b/source/blender/compositor/operations/COM_IDMaskOperation.cc index 677d0a8ccff..5837cb1fda9 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.cc +++ b/source/blender/compositor/operations/COM_IDMaskOperation.cc @@ -22,25 +22,25 @@ namespace blender::compositor { IDMaskOperation::IDMaskOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); this->flags.complex = true; flags.can_be_constant = true; } -void *IDMaskOperation::initializeTileData(rcti *rect) +void *IDMaskOperation::initialize_tile_data(rcti *rect) { - void *buffer = getInputOperation(0)->initializeTileData(rect); + void *buffer = get_input_operation(0)->initialize_tile_data(rect); return buffer; } -void IDMaskOperation::executePixel(float output[4], int x, int y, void *data) +void IDMaskOperation::execute_pixel(float output[4], int x, int y, void *data) { MemoryBuffer *input_buffer = (MemoryBuffer *)data; - const int buffer_width = input_buffer->getWidth(); - float *buffer = input_buffer->getBuffer(); + const int buffer_width = input_buffer->get_width(); + float *buffer = input_buffer->get_buffer(); int buffer_index = (y * buffer_width + x); - output[0] = (roundf(buffer[buffer_index]) == objectIndex_) ? 1.0f : 0.0f; + output[0] = (roundf(buffer[buffer_index]) == object_index_) ? 1.0f : 0.0f; } void IDMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -54,7 +54,7 @@ void IDMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, const float *in = input->get_elem(area.xmin, y); const float *row_end = out + width * output->elem_stride; while (out < row_end) { - out[0] = (roundf(in[0]) == objectIndex_) ? 1.0f : 0.0f; + out[0] = (roundf(in[0]) == object_index_) ? 1.0f : 0.0f; in += input->elem_stride; out += output->elem_stride; } diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.h b/source/blender/compositor/operations/COM_IDMaskOperation.h index 333f9eeee98..3a7ceec8c49 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.h +++ b/source/blender/compositor/operations/COM_IDMaskOperation.h @@ -24,17 +24,17 @@ namespace blender::compositor { class IDMaskOperation : public MultiThreadedOperation { private: - float objectIndex_; + float object_index_; public: IDMaskOperation(); - void *initializeTileData(rcti *rect) override; - void executePixel(float output[4], int x, int y, void *data) override; + void *initialize_tile_data(rcti *rect) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void setObjectIndex(float objectIndex) + void set_object_index(float object_index) { - objectIndex_ = objectIndex; + object_index_ = object_index; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ImageOperation.cc b/source/blender/compositor/operations/COM_ImageOperation.cc index 84c5b9654ce..e32343cfbf2 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cc +++ b/source/blender/compositor/operations/COM_ImageOperation.cc @@ -30,35 +30,35 @@ BaseImageOperation::BaseImageOperation() { image_ = nullptr; buffer_ = nullptr; - imageFloatBuffer_ = nullptr; - imageByteBuffer_ = nullptr; - imageUser_ = nullptr; + image_float_buffer_ = nullptr; + image_byte_buffer_ = nullptr; + image_user_ = nullptr; imagewidth_ = 0; imageheight_ = 0; framenumber_ = 0; - depthBuffer_ = nullptr; + image_depth_buffer_ = nullptr; depth_buffer_ = nullptr; - numberOfChannels_ = 0; + number_of_channels_ = 0; rd_ = nullptr; - viewName_ = nullptr; + view_name_ = nullptr; } ImageOperation::ImageOperation() : BaseImageOperation() { - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); } ImageAlphaOperation::ImageAlphaOperation() : BaseImageOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); } ImageDepthOperation::ImageDepthOperation() : BaseImageOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); } -ImBuf *BaseImageOperation::getImBuf() +ImBuf *BaseImageOperation::get_im_buf() { ImBuf *ibuf; - ImageUser iuser = *imageUser_; + ImageUser iuser = *image_user_; if (image_ == nullptr) { return nullptr; @@ -66,7 +66,7 @@ ImBuf *BaseImageOperation::getImBuf() /* local changes to the original ImageUser */ if (BKE_image_is_multilayer(image_) == false) { - iuser.multi_index = BKE_scene_multiview_view_id_get(rd_, viewName_); + iuser.multi_index = BKE_scene_multiview_view_id_get(rd_, view_name_); } ibuf = BKE_image_acquire_ibuf(image_, &iuser, nullptr); @@ -77,27 +77,27 @@ ImBuf *BaseImageOperation::getImBuf() return ibuf; } -void BaseImageOperation::initExecution() +void BaseImageOperation::init_execution() { - ImBuf *stackbuf = getImBuf(); + ImBuf *stackbuf = get_im_buf(); buffer_ = stackbuf; if (stackbuf) { - imageFloatBuffer_ = stackbuf->rect_float; - imageByteBuffer_ = stackbuf->rect; - depthBuffer_ = stackbuf->zbuf_float; + image_float_buffer_ = stackbuf->rect_float; + image_byte_buffer_ = stackbuf->rect; + image_depth_buffer_ = stackbuf->zbuf_float; if (stackbuf->zbuf_float) { depth_buffer_ = new MemoryBuffer(stackbuf->zbuf_float, 1, stackbuf->x, stackbuf->y); } imagewidth_ = stackbuf->x; imageheight_ = stackbuf->y; - numberOfChannels_ = stackbuf->channels; + number_of_channels_ = stackbuf->channels; } } -void BaseImageOperation::deinitExecution() +void BaseImageOperation::deinit_execution() { - imageFloatBuffer_ = nullptr; - imageByteBuffer_ = nullptr; + image_float_buffer_ = nullptr; + image_byte_buffer_ = nullptr; BKE_image_release_ibuf(image_, buffer_, nullptr); if (depth_buffer_) { delete depth_buffer_; @@ -107,7 +107,7 @@ void BaseImageOperation::deinitExecution() void BaseImageOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - ImBuf *stackbuf = getImBuf(); + ImBuf *stackbuf = get_im_buf(); r_area = COM_AREA_NONE; @@ -118,7 +118,7 @@ void BaseImageOperation::determine_canvas(const rcti &UNUSED(preferred_area), rc BKE_image_release_ibuf(image_, stackbuf, nullptr); } -static void sampleImageAtLocation( +static void sample_image_at_location( ImBuf *ibuf, float x, float y, PixelSampler sampler, bool make_linear_rgb, float color[4]) { if (ibuf->rect_float) { @@ -154,17 +154,17 @@ static void sampleImageAtLocation( } } -void ImageOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void ImageOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { int ix = x, iy = y; - if (imageFloatBuffer_ == nullptr && imageByteBuffer_ == nullptr) { + if (image_float_buffer_ == nullptr && image_byte_buffer_ == nullptr) { zero_v4(output); } else if (ix < 0 || iy < 0 || ix >= buffer_->x || iy >= buffer_->y) { zero_v4(output); } else { - sampleImageAtLocation(buffer_, x, y, sampler, true, output); + sample_image_at_location(buffer_, x, y, sampler, true, output); } } @@ -175,19 +175,19 @@ void ImageOperation::update_memory_buffer_partial(MemoryBuffer *output, output->copy_from(buffer_, area, true); } -void ImageAlphaOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ImageAlphaOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float tempcolor[4]; - if (imageFloatBuffer_ == nullptr && imageByteBuffer_ == nullptr) { + if (image_float_buffer_ == nullptr && image_byte_buffer_ == nullptr) { output[0] = 0.0f; } else { tempcolor[3] = 1.0f; - sampleImageAtLocation(buffer_, x, y, sampler, false, tempcolor); + sample_image_at_location(buffer_, x, y, sampler, false, tempcolor); output[0] = tempcolor[3]; } } @@ -199,21 +199,21 @@ void ImageAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, output->copy_from(buffer_, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); } -void ImageDepthOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void ImageDepthOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - if (depthBuffer_ == nullptr) { + if (image_depth_buffer_ == nullptr) { output[0] = 0.0f; } else { - if (x < 0 || y < 0 || x >= this->getWidth() || y >= this->getHeight()) { + if (x < 0 || y < 0 || x >= this->get_width() || y >= this->get_height()) { output[0] = 0.0f; } else { - int offset = y * getWidth() + x; - output[0] = depthBuffer_[offset]; + int offset = y * get_width() + x; + output[0] = image_depth_buffer_[offset]; } } } diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h index 40ef9f38f79..054398604b6 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.h +++ b/source/blender/compositor/operations/COM_ImageOperation.h @@ -36,19 +36,19 @@ class BaseImageOperation : public MultiThreadedOperation { protected: ImBuf *buffer_; Image *image_; - ImageUser *imageUser_; + ImageUser *image_user_; /* TODO: Remove raw buffers when removing Tiled implementation. */ - float *imageFloatBuffer_; - unsigned int *imageByteBuffer_; - float *depthBuffer_; + float *image_float_buffer_; + unsigned int *image_byte_buffer_; + float *image_depth_buffer_; MemoryBuffer *depth_buffer_; int imageheight_; int imagewidth_; int framenumber_; - int numberOfChannels_; + int number_of_channels_; const RenderData *rd_; - const char *viewName_; + const char *view_name_; BaseImageOperation(); /** @@ -56,28 +56,28 @@ class BaseImageOperation : public MultiThreadedOperation { */ void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - virtual ImBuf *getImBuf(); + virtual ImBuf *get_im_buf(); public: - void initExecution() override; - void deinitExecution() override; - void setImage(Image *image) + void init_execution() override; + void deinit_execution() override; + void set_image(Image *image) { image_ = image; } - void setImageUser(ImageUser *imageuser) + void set_image_user(ImageUser *imageuser) { - imageUser_ = imageuser; + image_user_ = imageuser; } - void setRenderData(const RenderData *rd) + void set_render_data(const RenderData *rd) { rd_ = rd; } - void setViewName(const char *viewName) + void set_view_name(const char *view_name) { - viewName_ = viewName; + view_name_ = view_name; } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { framenumber_ = framenumber; } @@ -88,7 +88,7 @@ class ImageOperation : public BaseImageOperation { * Constructor */ ImageOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -100,7 +100,7 @@ class ImageAlphaOperation : public BaseImageOperation { * Constructor */ ImageAlphaOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -112,7 +112,7 @@ class ImageDepthOperation : public BaseImageOperation { * Constructor */ ImageDepthOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc index 45b163c7e8b..96cd0043470 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cc +++ b/source/blender/compositor/operations/COM_InpaintOperation.cc @@ -23,37 +23,37 @@ namespace blender::compositor { #define ASSERT_XY_RANGE(x, y) \ - BLI_assert(x >= 0 && x < this->getWidth() && y >= 0 && y < this->getHeight()) + BLI_assert(x >= 0 && x < this->get_width() && y >= 0 && y < this->get_height()) /* In-paint (simple convolve using average of known pixels). */ InpaintSimpleOperation::InpaintSimpleOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); this->flags.complex = true; - inputImageProgram_ = nullptr; + input_image_program_ = nullptr; pixelorder_ = nullptr; manhattan_distance_ = nullptr; cached_buffer_ = nullptr; cached_buffer_ready_ = false; flags.is_fullframe_operation = true; } -void InpaintSimpleOperation::initExecution() +void InpaintSimpleOperation::init_execution() { - inputImageProgram_ = this->getInputSocketReader(0); + input_image_program_ = this->get_input_socket_reader(0); pixelorder_ = nullptr; manhattan_distance_ = nullptr; cached_buffer_ = nullptr; cached_buffer_ready_ = false; - this->initMutex(); + this->init_mutex(); } void InpaintSimpleOperation::clamp_xy(int &x, int &y) { - int width = this->getWidth(); - int height = this->getHeight(); + int width = this->get_width(); + int height = this->get_height(); if (x < 0) { x = 0; @@ -72,7 +72,7 @@ void InpaintSimpleOperation::clamp_xy(int &x, int &y) float *InpaintSimpleOperation::get_pixel(int x, int y) { - int width = this->getWidth(); + int width = this->get_width(); ASSERT_XY_RANGE(x, y); @@ -82,7 +82,7 @@ float *InpaintSimpleOperation::get_pixel(int x, int y) int InpaintSimpleOperation::mdist(int x, int y) { - int width = this->getWidth(); + int width = this->get_width(); ASSERT_XY_RANGE(x, y); @@ -91,7 +91,7 @@ int InpaintSimpleOperation::mdist(int x, int y) bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) { - int width = this->getWidth(); + int width = this->get_width(); if (curr >= area_size_) { return false; @@ -111,8 +111,8 @@ bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) void InpaintSimpleOperation::calc_manhattan_distance() { - int width = this->getWidth(); - int height = this->getHeight(); + int width = this->get_width(); + int height = this->get_height(); short *m = manhattan_distance_ = (short *)MEM_mallocN(sizeof(short) * width * height, __func__); int *offsets; @@ -213,15 +213,15 @@ void InpaintSimpleOperation::pix_step(int x, int y) } } -void *InpaintSimpleOperation::initializeTileData(rcti *rect) +void *InpaintSimpleOperation::initialize_tile_data(rcti *rect) { if (cached_buffer_ready_) { return cached_buffer_; } - lockMutex(); + lock_mutex(); if (!cached_buffer_ready_) { - MemoryBuffer *buf = (MemoryBuffer *)inputImageProgram_->initializeTileData(rect); - cached_buffer_ = (float *)MEM_dupallocN(buf->getBuffer()); + MemoryBuffer *buf = (MemoryBuffer *)input_image_program_->initialize_tile_data(rect); + cached_buffer_ = (float *)MEM_dupallocN(buf->get_buffer()); this->calc_manhattan_distance(); @@ -234,20 +234,20 @@ void *InpaintSimpleOperation::initializeTileData(rcti *rect) cached_buffer_ready_ = true; } - unlockMutex(); + unlock_mutex(); return cached_buffer_; } -void InpaintSimpleOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void InpaintSimpleOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { this->clamp_xy(x, y); copy_v4_v4(output, this->get_pixel(x, y)); } -void InpaintSimpleOperation::deinitExecution() +void InpaintSimpleOperation::deinit_execution() { - inputImageProgram_ = nullptr; - this->deinitMutex(); + input_image_program_ = nullptr; + this->deinit_mutex(); if (cached_buffer_) { MEM_freeN(cached_buffer_); cached_buffer_ = nullptr; @@ -265,22 +265,21 @@ void InpaintSimpleOperation::deinitExecution() cached_buffer_ready_ = false; } -bool InpaintSimpleOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool InpaintSimpleOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { if (cached_buffer_ready_) { return false; } - rcti newInput; + rcti new_input; - newInput.xmax = getWidth(); - newInput.xmin = 0; - newInput.ymax = getHeight(); - newInput.ymin = 0; + new_input.xmax = get_width(); + new_input.xmin = 0; + new_input.ymax = get_height(); + new_input.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void InpaintSimpleOperation::get_area_of_interest(const int input_idx, @@ -305,7 +304,7 @@ void InpaintSimpleOperation::update_memory_buffer(MemoryBuffer *output, delete tmp; } else { - cached_buffer_ = (float *)MEM_dupallocN(input->getBuffer()); + cached_buffer_ = (float *)MEM_dupallocN(input->get_buffer()); } this->calc_manhattan_distance(); @@ -318,8 +317,8 @@ void InpaintSimpleOperation::update_memory_buffer(MemoryBuffer *output, cached_buffer_ready_ = true; } - const int num_channels = COM_data_type_num_channels(getOutputSocket()->getDataType()); - MemoryBuffer buf(cached_buffer_, num_channels, input->getWidth(), input->getHeight()); + const int num_channels = COM_data_type_num_channels(get_output_socket()->get_data_type()); + MemoryBuffer buf(cached_buffer_, num_channels, input->get_width(), input->get_height()); output->copy_from(&buf, area); } diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h index 4187099f346..b0d44e22b85 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.h +++ b/source/blender/compositor/operations/COM_InpaintOperation.h @@ -25,9 +25,9 @@ namespace blender::compositor { class InpaintSimpleOperation : public NodeOperation { protected: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputImageProgram_; + SocketReader *input_image_program_; int iterations_; @@ -44,27 +44,27 @@ class InpaintSimpleOperation : public NodeOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setIterations(int iterations) + void set_iterations(int iterations) { iterations_ = iterations; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc index ba36dd35e47..cbc15c95f46 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.cc +++ b/source/blender/compositor/operations/COM_InvertOperation.cc @@ -22,53 +22,56 @@ namespace blender::compositor { InvertOperation::InvertOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - inputValueProgram_ = nullptr; - inputColorProgram_ = nullptr; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_value_program_ = nullptr; + input_color_program_ = nullptr; color_ = true; alpha_ = false; set_canvas_input_index(1); this->flags.can_be_constant = true; } -void InvertOperation::initExecution() +void InvertOperation::init_execution() { - inputValueProgram_ = this->getInputSocketReader(0); - inputColorProgram_ = this->getInputSocketReader(1); + input_value_program_ = this->get_input_socket_reader(0); + input_color_program_ = this->get_input_socket_reader(1); } -void InvertOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void InvertOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue[4]; - float inputColor[4]; - inputValueProgram_->readSampled(inputValue, x, y, sampler); - inputColorProgram_->readSampled(inputColor, x, y, sampler); + float input_value[4]; + float input_color[4]; + input_value_program_->read_sampled(input_value, x, y, sampler); + input_color_program_->read_sampled(input_color, x, y, sampler); - const float value = inputValue[0]; - const float invertedValue = 1.0f - value; + const float value = input_value[0]; + const float inverted_value = 1.0f - value; if (color_) { - output[0] = (1.0f - inputColor[0]) * value + inputColor[0] * invertedValue; - output[1] = (1.0f - inputColor[1]) * value + inputColor[1] * invertedValue; - output[2] = (1.0f - inputColor[2]) * value + inputColor[2] * invertedValue; + output[0] = (1.0f - input_color[0]) * value + input_color[0] * inverted_value; + output[1] = (1.0f - input_color[1]) * value + input_color[1] * inverted_value; + output[2] = (1.0f - input_color[2]) * value + input_color[2] * inverted_value; } else { - copy_v3_v3(output, inputColor); + copy_v3_v3(output, input_color); } if (alpha_) { - output[3] = (1.0f - inputColor[3]) * value + inputColor[3] * invertedValue; + output[3] = (1.0f - input_color[3]) * value + input_color[3] * inverted_value; } else { - output[3] = inputColor[3]; + output[3] = input_color[3]; } } -void InvertOperation::deinitExecution() +void InvertOperation::deinit_execution() { - inputValueProgram_ = nullptr; - inputColorProgram_ = nullptr; + input_value_program_ = nullptr; + input_color_program_ = nullptr; } void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_InvertOperation.h b/source/blender/compositor/operations/COM_InvertOperation.h index 7db0394ad7a..19abca968c0 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.h +++ b/source/blender/compositor/operations/COM_InvertOperation.h @@ -25,10 +25,10 @@ namespace blender::compositor { class InvertOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputValueProgram_; - SocketReader *inputColorProgram_; + SocketReader *input_value_program_; + SocketReader *input_color_program_; bool alpha_; bool color_; @@ -39,23 +39,23 @@ class InvertOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setColor(bool color) + void set_color(bool color) { color_ = color; } - void setAlpha(bool alpha) + void set_alpha(bool alpha) { alpha_ = alpha; } diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc index fcf304de174..bb82e74f92a 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc @@ -22,8 +22,8 @@ namespace blender::compositor { KeyingBlurOperation::KeyingBlurOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); size_ = 0; axis_ = BLUR_AXIS_X; @@ -31,34 +31,34 @@ KeyingBlurOperation::KeyingBlurOperation() this->flags.complex = true; } -void *KeyingBlurOperation::initializeTileData(rcti *rect) +void *KeyingBlurOperation::initialize_tile_data(rcti *rect) { - void *buffer = getInputOperation(0)->initializeTileData(rect); + void *buffer = get_input_operation(0)->initialize_tile_data(rect); return buffer; } -void KeyingBlurOperation::executePixel(float output[4], int x, int y, void *data) +void KeyingBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - const int bufferWidth = inputBuffer->getWidth(); - float *buffer = inputBuffer->getBuffer(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + const int buffer_width = input_buffer->get_width(); + float *buffer = input_buffer->get_buffer(); int count = 0; float average = 0.0f; if (axis_ == 0) { - const int start = MAX2(0, x - size_ + 1), end = MIN2(bufferWidth, x + size_); + const int start = MAX2(0, x - size_ + 1), end = MIN2(buffer_width, x + size_); for (int cx = start; cx < end; cx++) { - int bufferIndex = (y * bufferWidth + cx); - average += buffer[bufferIndex]; + int buffer_index = (y * buffer_width + cx); + average += buffer[buffer_index]; count++; } } else { - const int start = MAX2(0, y - size_ + 1), end = MIN2(inputBuffer->getHeight(), y + size_); + const int start = MAX2(0, y - size_ + 1), end = MIN2(input_buffer->get_height(), y + size_); for (int cy = start; cy < end; cy++) { - int bufferIndex = (cy * bufferWidth + x); - average += buffer[bufferIndex]; + int buffer_index = (cy * buffer_width + x); + average += buffer[buffer_index]; count++; } } @@ -68,26 +68,26 @@ void KeyingBlurOperation::executePixel(float output[4], int x, int y, void *data output[0] = average; } -bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool KeyingBlurOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; if (axis_ == BLUR_AXIS_X) { - newInput.xmin = input->xmin - size_; - newInput.ymin = input->ymin; - newInput.xmax = input->xmax + size_; - newInput.ymax = input->ymax; + new_input.xmin = input->xmin - size_; + new_input.ymin = input->ymin; + new_input.xmax = input->xmax + size_; + new_input.ymax = input->ymax; } else { - newInput.xmin = input->xmin; - newInput.ymin = input->ymin - size_; - newInput.xmax = input->xmax; - newInput.ymax = input->ymax + size_; + new_input.xmin = input->xmin; + new_input.ymin = input->ymin - size_; + new_input.xmax = input->xmax; + new_input.ymax = input->ymax + size_; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void KeyingBlurOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -126,12 +126,12 @@ void KeyingBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, switch (axis_) { case BLUR_AXIS_X: get_current_coord = [&] { return it.x; }; - coord_max = this->getWidth(); + coord_max = this->get_width(); elem_stride = input->elem_stride; break; case BLUR_AXIS_Y: get_current_coord = [&] { return it.y; }; - coord_max = this->getHeight(); + coord_max = this->get_height(); elem_stride = input->row_stride; break; } diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.h b/source/blender/compositor/operations/COM_KeyingBlurOperation.h index 56fa0f84880..11fde5f5436 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.h +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.h @@ -38,22 +38,22 @@ class KeyingBlurOperation : public MultiThreadedOperation { KeyingBlurOperation(); - void setSize(int value) + void set_size(int value) { size_ = value; } - void setAxis(int value) + void set_axis(int value) { axis_ = value; } - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc index e6097dd71a6..03e3f0611c0 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc @@ -22,47 +22,47 @@ namespace blender::compositor { KeyingClipOperation::KeyingClipOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); - kernelRadius_ = 3; - kernelTolerance_ = 0.1f; + kernel_radius_ = 3; + kernel_tolerance_ = 0.1f; - clipBlack_ = 0.0f; - clipWhite_ = 1.0f; + clip_black_ = 0.0f; + clip_white_ = 1.0f; - isEdgeMatte_ = false; + is_edge_matte_ = false; this->flags.complex = true; } -void *KeyingClipOperation::initializeTileData(rcti *rect) +void *KeyingClipOperation::initialize_tile_data(rcti *rect) { - void *buffer = getInputOperation(0)->initializeTileData(rect); + void *buffer = get_input_operation(0)->initialize_tile_data(rect); return buffer; } -void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data) +void KeyingClipOperation::execute_pixel(float output[4], int x, int y, void *data) { - const int delta = kernelRadius_; - const float tolerance = kernelTolerance_; + const int delta = kernel_radius_; + const float tolerance = kernel_tolerance_; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); - int bufferWidth = inputBuffer->getWidth(); - int bufferHeight = inputBuffer->getHeight(); + int buffer_width = input_buffer->get_width(); + int buffer_height = input_buffer->get_height(); - float value = buffer[(y * bufferWidth + x)]; + float value = buffer[(y * buffer_width + x)]; bool ok = false; int start_x = max_ff(0, x - delta + 1), start_y = max_ff(0, y - delta + 1), - end_x = min_ff(x + delta - 1, bufferWidth - 1), - end_y = min_ff(y + delta - 1, bufferHeight - 1); + end_x = min_ff(x + delta - 1, buffer_width - 1), + end_y = min_ff(y + delta - 1, buffer_height - 1); - int count = 0, totalCount = (end_x - start_x + 1) * (end_y - start_y + 1) - 1; - int thresholdCount = ceil((float)totalCount * 0.9f); + int count = 0, total_count = (end_x - start_x + 1) * (end_y - start_y + 1) - 1; + int threshold_count = ceil((float)total_count * 0.9f); if (delta == 0) { ok = true; @@ -74,19 +74,19 @@ void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data continue; } - int bufferIndex = (cy * bufferWidth + cx); - float currentValue = buffer[bufferIndex]; + int buffer_index = (cy * buffer_width + cx); + float current_value = buffer[buffer_index]; - if (fabsf(currentValue - value) < tolerance) { + if (fabsf(current_value - value) < tolerance) { count++; - if (count >= thresholdCount) { + if (count >= threshold_count) { ok = true; } } } } - if (isEdgeMatte_) { + if (is_edge_matte_) { if (ok) { output[0] = 0.0f; } @@ -98,31 +98,31 @@ void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data output[0] = value; if (ok) { - if (output[0] < clipBlack_) { + if (output[0] < clip_black_) { output[0] = 0.0f; } - else if (output[0] >= clipWhite_) { + else if (output[0] >= clip_white_) { output[0] = 1.0f; } else { - output[0] = (output[0] - clipBlack_) / (clipWhite_ - clipBlack_); + output[0] = (output[0] - clip_black_) / (clip_white_ - clip_black_); } } } } -bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool KeyingClipOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmin = input->xmin - kernelRadius_; - newInput.ymin = input->ymin - kernelRadius_; - newInput.xmax = input->xmax + kernelRadius_; - newInput.ymax = input->ymax + kernelRadius_; + new_input.xmin = input->xmin - kernel_radius_; + new_input.ymin = input->ymin - kernel_radius_; + new_input.xmax = input->xmax + kernel_radius_; + new_input.ymax = input->ymax + kernel_radius_; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void KeyingClipOperation::get_area_of_interest(const int input_idx, @@ -131,10 +131,10 @@ void KeyingClipOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - kernelRadius_; - r_input_area.xmax = output_area.xmax + kernelRadius_; - r_input_area.ymin = output_area.ymin - kernelRadius_; - r_input_area.ymax = output_area.ymax + kernelRadius_; + r_input_area.xmin = output_area.xmin - kernel_radius_; + r_input_area.xmax = output_area.xmax + kernel_radius_; + r_input_area.ymin = output_area.ymin - kernel_radius_; + r_input_area.ymax = output_area.ymax + kernel_radius_; } void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -144,10 +144,10 @@ void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output, const MemoryBuffer *input = inputs[0]; BuffersIterator it = output->iterate_with(inputs, area); - const int delta = kernelRadius_; - const float tolerance = kernelTolerance_; - const int width = this->getWidth(); - const int height = this->getHeight(); + const int delta = kernel_radius_; + const float tolerance = kernel_tolerance_; + const int width = this->get_width(); + const int height = this->get_height(); const int row_stride = input->row_stride; const int elem_stride = input->elem_stride; for (; !it.is_end(); ++it) { @@ -190,21 +190,21 @@ void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output, } } - if (isEdgeMatte_) { + if (is_edge_matte_) { *it.out = ok ? 0.0f : 1.0f; } else { if (!ok) { *it.out = value; } - else if (value < clipBlack_) { + else if (value < clip_black_) { *it.out = 0.0f; } - else if (value >= clipWhite_) { + else if (value >= clip_white_) { *it.out = 1.0f; } else { - *it.out = (value - clipBlack_) / (clipWhite_ - clipBlack_); + *it.out = (value - clip_black_) / (clip_white_ - clip_black_); } } } diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.h b/source/blender/compositor/operations/COM_KeyingClipOperation.h index 422d79597ce..3bdc7281683 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.h +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.h @@ -27,47 +27,47 @@ namespace blender::compositor { */ class KeyingClipOperation : public MultiThreadedOperation { protected: - float clipBlack_; - float clipWhite_; + float clip_black_; + float clip_white_; - int kernelRadius_; - float kernelTolerance_; + int kernel_radius_; + float kernel_tolerance_; - bool isEdgeMatte_; + bool is_edge_matte_; public: KeyingClipOperation(); - void setClipBlack(float value) + void set_clip_black(float value) { - clipBlack_ = value; + clip_black_ = value; } - void setClipWhite(float value) + void set_clip_white(float value) { - clipWhite_ = value; + clip_white_ = value; } - void setKernelRadius(int value) + void set_kernel_radius(int value) { - kernelRadius_ = value; + kernel_radius_ = value; } - void setKernelTolerance(float value) + void set_kernel_tolerance(float value) { - kernelTolerance_ = value; + kernel_tolerance_ = value; } - void setIsEdgeMatte(bool value) + void set_is_edge_matte(bool value) { - isEdgeMatte_ = value; + is_edge_matte_ = value; } - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc index 8b090d5dc38..f819a39e9e0 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc @@ -22,42 +22,42 @@ namespace blender::compositor { KeyingDespillOperation::KeyingDespillOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); - despillFactor_ = 0.5f; - colorBalance_ = 0.5f; + despill_factor_ = 0.5f; + color_balance_ = 0.5f; - pixelReader_ = nullptr; - screenReader_ = nullptr; + pixel_reader_ = nullptr; + screen_reader_ = nullptr; flags.can_be_constant = true; } -void KeyingDespillOperation::initExecution() +void KeyingDespillOperation::init_execution() { - pixelReader_ = this->getInputSocketReader(0); - screenReader_ = this->getInputSocketReader(1); + pixel_reader_ = this->get_input_socket_reader(0); + screen_reader_ = this->get_input_socket_reader(1); } -void KeyingDespillOperation::deinitExecution() +void KeyingDespillOperation::deinit_execution() { - pixelReader_ = nullptr; - screenReader_ = nullptr; + pixel_reader_ = nullptr; + screen_reader_ = nullptr; } -void KeyingDespillOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void KeyingDespillOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float pixelColor[4]; - float screenColor[4]; + float pixel_color[4]; + float screen_color[4]; - pixelReader_->readSampled(pixelColor, x, y, sampler); - screenReader_->readSampled(screenColor, x, y, sampler); + pixel_reader_->read_sampled(pixel_color, x, y, sampler); + screen_reader_->read_sampled(screen_color, x, y, sampler); - const int screen_primary_channel = max_axis_v3(screenColor); + const int screen_primary_channel = max_axis_v3(screen_color); const int other_1 = (screen_primary_channel + 1) % 3; const int other_2 = (screen_primary_channel + 2) % 3; @@ -66,15 +66,15 @@ void KeyingDespillOperation::executePixelSampled(float output[4], float average_value, amount; - average_value = colorBalance_ * pixelColor[min_channel] + - (1.0f - colorBalance_) * pixelColor[max_channel]; - amount = (pixelColor[screen_primary_channel] - average_value); + average_value = color_balance_ * pixel_color[min_channel] + + (1.0f - color_balance_) * pixel_color[max_channel]; + amount = (pixel_color[screen_primary_channel] - average_value); - copy_v4_v4(output, pixelColor); + copy_v4_v4(output, pixel_color); - const float amount_despill = despillFactor_ * amount; + const float amount_despill = despill_factor_ * amount; if (amount_despill > 0.0f) { - output[screen_primary_channel] = pixelColor[screen_primary_channel] - amount_despill; + output[screen_primary_channel] = pixel_color[screen_primary_channel] - amount_despill; } } @@ -93,13 +93,13 @@ void KeyingDespillOperation::update_memory_buffer_partial(MemoryBuffer *output, const int min_channel = MIN2(other_1, other_2); const int max_channel = MAX2(other_1, other_2); - const float average_value = colorBalance_ * pixel_color[min_channel] + - (1.0f - colorBalance_) * pixel_color[max_channel]; + const float average_value = color_balance_ * pixel_color[min_channel] + + (1.0f - color_balance_) * pixel_color[max_channel]; const float amount = (pixel_color[screen_primary_channel] - average_value); copy_v4_v4(it.out, pixel_color); - const float amount_despill = despillFactor_ * amount; + const float amount_despill = despill_factor_ * amount; if (amount_despill > 0.0f) { it.out[screen_primary_channel] = pixel_color[screen_primary_channel] - amount_despill; } diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.h b/source/blender/compositor/operations/COM_KeyingDespillOperation.h index 51efa44ce1c..b57fbd09e74 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.h +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.h @@ -27,27 +27,27 @@ namespace blender::compositor { */ class KeyingDespillOperation : public MultiThreadedOperation { protected: - SocketReader *pixelReader_; - SocketReader *screenReader_; - float despillFactor_; - float colorBalance_; + SocketReader *pixel_reader_; + SocketReader *screen_reader_; + float despill_factor_; + float color_balance_; public: KeyingDespillOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setDespillFactor(float value) + void set_despill_factor(float value) { - despillFactor_ = value; + despill_factor_ = value; } - void setColorBalance(float value) + void set_color_balance(float value) { - colorBalance_ = value; + color_balance_ = value; } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cc b/source/blender/compositor/operations/COM_KeyingOperation.cc index 7a7fe716fb5..c7ab3aca2e2 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingOperation.cc @@ -20,7 +20,7 @@ namespace blender::compositor { -static float get_pixel_saturation(const float pixelColor[4], +static float get_pixel_saturation(const float pixel_color[4], float screen_balance, int primary_channel) { @@ -30,43 +30,46 @@ static float get_pixel_saturation(const float pixelColor[4], const int min_channel = MIN2(other_1, other_2); const int max_channel = MAX2(other_1, other_2); - const float val = screen_balance * pixelColor[min_channel] + - (1.0f - screen_balance) * pixelColor[max_channel]; + const float val = screen_balance * pixel_color[min_channel] + + (1.0f - screen_balance) * pixel_color[max_channel]; - return (pixelColor[primary_channel] - val) * fabsf(1.0f - val); + return (pixel_color[primary_channel] - val) * fabsf(1.0f - val); } KeyingOperation::KeyingOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Value); - screenBalance_ = 0.5f; + screen_balance_ = 0.5f; - pixelReader_ = nullptr; - screenReader_ = nullptr; + pixel_reader_ = nullptr; + screen_reader_ = nullptr; } -void KeyingOperation::initExecution() +void KeyingOperation::init_execution() { - pixelReader_ = this->getInputSocketReader(0); - screenReader_ = this->getInputSocketReader(1); + pixel_reader_ = this->get_input_socket_reader(0); + screen_reader_ = this->get_input_socket_reader(1); } -void KeyingOperation::deinitExecution() +void KeyingOperation::deinit_execution() { - pixelReader_ = nullptr; - screenReader_ = nullptr; + pixel_reader_ = nullptr; + screen_reader_ = nullptr; } -void KeyingOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void KeyingOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float pixel_color[4]; float screen_color[4]; - pixelReader_->readSampled(pixel_color, x, y, sampler); - screenReader_->readSampled(screen_color, x, y, sampler); + pixel_reader_->read_sampled(pixel_color, x, y, sampler); + screen_reader_->read_sampled(screen_color, x, y, sampler); const int primary_channel = max_axis_v3(screen_color); const float min_pixel_color = min_fff(pixel_color[0], pixel_color[1], pixel_color[2]); @@ -80,8 +83,8 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix output[0] = 1.0f; } else { - float saturation = get_pixel_saturation(pixel_color, screenBalance_, primary_channel); - float screen_saturation = get_pixel_saturation(screen_color, screenBalance_, primary_channel); + float saturation = get_pixel_saturation(pixel_color, screen_balance_, primary_channel); + float screen_saturation = get_pixel_saturation(screen_color, screen_balance_, primary_channel); if (saturation < 0) { /* means main channel of pixel is different from screen, @@ -124,9 +127,9 @@ void KeyingOperation::update_memory_buffer_partial(MemoryBuffer *output, it.out[0] = 1.0f; } else { - const float saturation = get_pixel_saturation(pixel_color, screenBalance_, primary_channel); + const float saturation = get_pixel_saturation(pixel_color, screen_balance_, primary_channel); const float screen_saturation = get_pixel_saturation( - screen_color, screenBalance_, primary_channel); + screen_color, screen_balance_, primary_channel); if (saturation < 0) { /* Means main channel of pixel is different from screen, diff --git a/source/blender/compositor/operations/COM_KeyingOperation.h b/source/blender/compositor/operations/COM_KeyingOperation.h index 3ad18085f6b..d7be4872065 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.h +++ b/source/blender/compositor/operations/COM_KeyingOperation.h @@ -31,23 +31,23 @@ namespace blender::compositor { */ class KeyingOperation : public MultiThreadedOperation { protected: - SocketReader *pixelReader_; - SocketReader *screenReader_; + SocketReader *pixel_reader_; + SocketReader *screen_reader_; - float screenBalance_; + float screen_balance_; public: KeyingOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setScreenBalance(float value) + void set_screen_balance(float value) { - screenBalance_ = value; + screen_balance_ = value; } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc index 8f14b5415b5..d7e56aaa7c6 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc @@ -28,32 +28,32 @@ namespace blender::compositor { KeyingScreenOperation::KeyingScreenOperation() { - this->addOutputSocket(DataType::Color); - movieClip_ = nullptr; + this->add_output_socket(DataType::Color); + movie_clip_ = nullptr; framenumber_ = 0; - trackingObject_[0] = 0; + tracking_object_[0] = 0; flags.complex = true; - cachedTriangulation_ = nullptr; + cached_triangulation_ = nullptr; } -void KeyingScreenOperation::initExecution() +void KeyingScreenOperation::init_execution() { - initMutex(); + init_mutex(); if (execution_model_ == eExecutionModel::FullFrame) { - BLI_assert(cachedTriangulation_ == nullptr); - if (movieClip_) { - cachedTriangulation_ = buildVoronoiTriangulation(); + BLI_assert(cached_triangulation_ == nullptr); + if (movie_clip_) { + cached_triangulation_ = build_voronoi_triangulation(); } } else { - cachedTriangulation_ = nullptr; + cached_triangulation_ = nullptr; } } -void KeyingScreenOperation::deinitExecution() +void KeyingScreenOperation::deinit_execution() { - if (cachedTriangulation_) { - TriangulationData *triangulation = cachedTriangulation_; + if (cached_triangulation_) { + TriangulationData *triangulation = cached_triangulation_; if (triangulation->triangulated_points) { MEM_freeN(triangulation->triangulated_points); @@ -67,17 +67,17 @@ void KeyingScreenOperation::deinitExecution() MEM_freeN(triangulation->triangles_AABB); } - MEM_freeN(cachedTriangulation_); + MEM_freeN(cached_triangulation_); - cachedTriangulation_ = nullptr; + cached_triangulation_ = nullptr; } } -KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTriangulation() +KeyingScreenOperation::TriangulationData *KeyingScreenOperation::build_voronoi_triangulation() { MovieClipUser user = {0}; TriangulationData *triangulation; - MovieTracking *tracking = &movieClip_->tracking; + MovieTracking *tracking = &movie_clip_->tracking; MovieTrackingTrack *track; VoronoiSite *sites, *site; ImBuf *ibuf; @@ -85,12 +85,12 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri ListBase edges = {nullptr, nullptr}; int sites_total; int i; - int width = this->getWidth(); - int height = this->getHeight(); - int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, framenumber_); + int width = this->get_width(); + int height = this->get_height(); + int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, framenumber_); - if (trackingObject_[0]) { - MovieTrackingObject *object = BKE_tracking_object_get_named(tracking, trackingObject_); + if (tracking_object_[0]) { + MovieTrackingObject *object = BKE_tracking_object_get_named(tracking, tracking_object_); if (!object) { return nullptr; @@ -126,7 +126,7 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri } BKE_movieclip_user_set_frame(&user, clip_frame); - ibuf = BKE_movieclip_get_ibuf(movieClip_, &user); + ibuf = BKE_movieclip_get_ibuf(movie_clip_, &user); if (!ibuf) { return nullptr; @@ -237,7 +237,7 @@ KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti * int chunk_size = 20; int i; - triangulation = cachedTriangulation_; + triangulation = cached_triangulation_; if (!triangulation) { return nullptr; @@ -269,24 +269,24 @@ KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti * return tile_data; } -void *KeyingScreenOperation::initializeTileData(rcti *rect) +void *KeyingScreenOperation::initialize_tile_data(rcti *rect) { - if (movieClip_ == nullptr) { + if (movie_clip_ == nullptr) { return nullptr; } - if (!cachedTriangulation_) { - lockMutex(); - if (cachedTriangulation_ == nullptr) { - cachedTriangulation_ = buildVoronoiTriangulation(); + if (!cached_triangulation_) { + lock_mutex(); + if (cached_triangulation_ == nullptr) { + cached_triangulation_ = build_voronoi_triangulation(); } - unlockMutex(); + unlock_mutex(); } return triangulate(rect); } -void KeyingScreenOperation::deinitializeTileData(rcti * /*rect*/, void *data) +void KeyingScreenOperation::deinitialize_tile_data(rcti * /*rect*/, void *data) { TileData *tile_data = (TileData *)data; @@ -301,28 +301,28 @@ void KeyingScreenOperation::determine_canvas(const rcti &preferred_area, rcti &r { r_area = COM_AREA_NONE; - if (movieClip_) { + if (movie_clip_) { MovieClipUser user = {0}; int width, height; - int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, framenumber_); + int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, framenumber_); BKE_movieclip_user_set_frame(&user, clip_frame); - BKE_movieclip_get_size(movieClip_, &user, &width, &height); + BKE_movieclip_get_size(movie_clip_, &user, &width, &height); r_area = preferred_area; r_area.xmax = r_area.xmin + width; r_area.ymax = r_area.ymin + height; } } -void KeyingScreenOperation::executePixel(float output[4], int x, int y, void *data) +void KeyingScreenOperation::execute_pixel(float output[4], int x, int y, void *data) { output[0] = 0.0f; output[1] = 0.0f; output[2] = 0.0f; output[3] = 1.0f; - if (movieClip_ && data) { - TriangulationData *triangulation = cachedTriangulation_; + if (movie_clip_ && data) { + TriangulationData *triangulation = cached_triangulation_; TileData *tile_data = (TileData *)data; int i; float co[2] = {(float)x, (float)y}; @@ -356,7 +356,7 @@ void KeyingScreenOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - if (movieClip_ == nullptr) { + if (movie_clip_ == nullptr) { output->fill(area, COM_COLOR_BLACK); return; } @@ -366,7 +366,7 @@ void KeyingScreenOperation::update_memory_buffer_partial(MemoryBuffer *output, const int *triangles = tri_area->triangles; const int num_triangles = tri_area->triangles_total; - const TriangulationData *triangulation = cachedTriangulation_; + const TriangulationData *triangulation = cached_triangulation_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { copy_v4_v4(it.out, COM_COLOR_BLACK); diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h index a828513d9fa..3450a82a485 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h @@ -49,41 +49,41 @@ class KeyingScreenOperation : public MultiThreadedOperation { int triangles_total; } TileData; - MovieClip *movieClip_; + MovieClip *movie_clip_; int framenumber_; - TriangulationData *cachedTriangulation_; - char trackingObject_[64]; + TriangulationData *cached_triangulation_; + char tracking_object_[64]; /** * Determine the output resolution. The resolution is retrieved from the Renderer */ void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - TriangulationData *buildVoronoiTriangulation(); + TriangulationData *build_voronoi_triangulation(); public: KeyingScreenOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; - void deinitializeTileData(rcti *rect, void *data) override; + void *initialize_tile_data(rcti *rect) override; + void deinitialize_tile_data(rcti *rect, void *data) override; - void setMovieClip(MovieClip *clip) + void set_movie_clip(MovieClip *clip) { - movieClip_ = clip; + movie_clip_ = clip; } - void setTrackingObject(const char *object) + void set_tracking_object(const char *object) { - BLI_strncpy(trackingObject_, object, sizeof(trackingObject_)); + BLI_strncpy(tracking_object_, object, sizeof(tracking_object_)); } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { framenumber_ = framenumber; } - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc index d266cbd00f5..36dc9c42b8b 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc @@ -24,39 +24,39 @@ namespace blender::compositor { LuminanceMatteOperation::LuminanceMatteOperation() { - addInputSocket(DataType::Color); - addOutputSocket(DataType::Value); + add_input_socket(DataType::Color); + add_output_socket(DataType::Value); - inputImageProgram_ = nullptr; + input_image_program_ = nullptr; flags.can_be_constant = true; } -void LuminanceMatteOperation::initExecution() +void LuminanceMatteOperation::init_execution() { - inputImageProgram_ = this->getInputSocketReader(0); + input_image_program_ = this->get_input_socket_reader(0); } -void LuminanceMatteOperation::deinitExecution() +void LuminanceMatteOperation::deinit_execution() { - inputImageProgram_ = nullptr; + input_image_program_ = nullptr; } -void LuminanceMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void LuminanceMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inColor[4]; - inputImageProgram_->readSampled(inColor, x, y, sampler); + float in_color[4]; + input_image_program_->read_sampled(in_color, x, y, sampler); const float high = settings_->t1; const float low = settings_->t2; - const float luminance = IMB_colormanagement_get_luminance(inColor); + const float luminance = IMB_colormanagement_get_luminance(in_color); float alpha; /* one line thread-friend algorithm: - * output[0] = MIN2(inputValue[3], MIN2(1.0f, MAX2(0.0f, ((luminance - low) / (high - low)))); + * output[0] = MIN2(input_value[3], MIN2(1.0f, MAX2(0.0f, ((luminance - low) / (high - low)))); */ /* test range */ @@ -75,7 +75,7 @@ void LuminanceMatteOperation::executePixelSampled(float output[4], */ /* don't make something that was more transparent less transparent */ - output[0] = min_ff(alpha, inColor[3]); + output[0] = min_ff(alpha, in_color[3]); } void LuminanceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h index 6262d1999bc..7f9ccfc83ac 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h @@ -29,7 +29,7 @@ namespace blender::compositor { class LuminanceMatteOperation : public MultiThreadedOperation { private: NodeChroma *settings_; - SocketReader *inputImageProgram_; + SocketReader *input_image_program_; public: /** @@ -40,14 +40,14 @@ class LuminanceMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma) + void set_settings(NodeChroma *node_chroma) { - settings_ = nodeChroma; + settings_ = node_chroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.cc b/source/blender/compositor/operations/COM_MapRangeOperation.cc index 0fcdbd1fd99..e8ce1f100c3 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.cc +++ b/source/blender/compositor/operations/COM_MapRangeOperation.cc @@ -22,45 +22,45 @@ namespace blender::compositor { MapRangeOperation::MapRangeOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - inputOperation_ = nullptr; - useClamp_ = false; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_operation_ = nullptr; + use_clamp_ = false; flags.can_be_constant = true; } -void MapRangeOperation::initExecution() +void MapRangeOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); - sourceMinOperation_ = this->getInputSocketReader(1); - sourceMaxOperation_ = this->getInputSocketReader(2); - destMinOperation_ = this->getInputSocketReader(3); - destMaxOperation_ = this->getInputSocketReader(4); + input_operation_ = this->get_input_socket_reader(0); + source_min_operation_ = this->get_input_socket_reader(1); + source_max_operation_ = this->get_input_socket_reader(2); + dest_min_operation_ = this->get_input_socket_reader(3); + dest_max_operation_ = this->get_input_socket_reader(4); } /* The code below assumes all data is inside range +- this, and that input buffer is single channel */ #define BLENDER_ZMAX 10000.0f -void MapRangeOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MapRangeOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float inputs[8]; /* includes the 5 inputs + 3 pads */ float value; float source_min, source_max; float dest_min, dest_max; - inputOperation_->readSampled(inputs, x, y, sampler); - sourceMinOperation_->readSampled(inputs + 1, x, y, sampler); - sourceMaxOperation_->readSampled(inputs + 2, x, y, sampler); - destMinOperation_->readSampled(inputs + 3, x, y, sampler); - destMaxOperation_->readSampled(inputs + 4, x, y, sampler); + input_operation_->read_sampled(inputs, x, y, sampler); + source_min_operation_->read_sampled(inputs + 1, x, y, sampler); + source_max_operation_->read_sampled(inputs + 2, x, y, sampler); + dest_min_operation_->read_sampled(inputs + 3, x, y, sampler); + dest_max_operation_->read_sampled(inputs + 4, x, y, sampler); value = inputs[0]; source_min = inputs[1]; @@ -84,7 +84,7 @@ void MapRangeOperation::executePixelSampled(float output[4], value = dest_min; } - if (useClamp_) { + if (use_clamp_) { if (dest_max > dest_min) { CLAMP(value, dest_min, dest_max); } @@ -96,13 +96,13 @@ void MapRangeOperation::executePixelSampled(float output[4], output[0] = value; } -void MapRangeOperation::deinitExecution() +void MapRangeOperation::deinit_execution() { - inputOperation_ = nullptr; - sourceMinOperation_ = nullptr; - sourceMaxOperation_ = nullptr; - destMinOperation_ = nullptr; - destMaxOperation_ = nullptr; + input_operation_ = nullptr; + source_min_operation_ = nullptr; + source_max_operation_ = nullptr; + dest_min_operation_ = nullptr; + dest_max_operation_ = nullptr; } void MapRangeOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -131,7 +131,7 @@ void MapRangeOperation::update_memory_buffer_partial(MemoryBuffer *output, value = dest_min; } - if (useClamp_) { + if (use_clamp_) { if (dest_max > dest_min) { CLAMP(value, dest_min, dest_max); } diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.h b/source/blender/compositor/operations/COM_MapRangeOperation.h index f5c42d19079..0d0f3c90e35 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.h +++ b/source/blender/compositor/operations/COM_MapRangeOperation.h @@ -30,15 +30,15 @@ namespace blender::compositor { class MapRangeOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputOperation_; - SocketReader *sourceMinOperation_; - SocketReader *sourceMaxOperation_; - SocketReader *destMinOperation_; - SocketReader *destMaxOperation_; + SocketReader *input_operation_; + SocketReader *source_min_operation_; + SocketReader *source_max_operation_; + SocketReader *dest_min_operation_; + SocketReader *dest_max_operation_; - bool useClamp_; + bool use_clamp_; public: /** @@ -49,24 +49,24 @@ class MapRangeOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; /** * Clamp the output */ - void setUseClamp(bool value) + void set_use_clamp(bool value) { - useClamp_ = value; + use_clamp_ = value; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index a5140c30fed..fb4fcff381d 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -22,55 +22,55 @@ namespace blender::compositor { MapUVOperation::MapUVOperation() { - this->addInputSocket(DataType::Color, ResizeMode::Align); - this->addInputSocket(DataType::Vector); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_input_socket(DataType::Vector); + this->add_output_socket(DataType::Color); alpha_ = 0.0f; this->flags.complex = true; set_canvas_input_index(UV_INPUT_INDEX); inputUVProgram_ = nullptr; - inputColorProgram_ = nullptr; + input_color_program_ = nullptr; } void MapUVOperation::init_data() { NodeOperation *image_input = get_input_operation(IMAGE_INPUT_INDEX); - image_width_ = image_input->getWidth(); - image_height_ = image_input->getHeight(); + image_width_ = image_input->get_width(); + image_height_ = image_input->get_height(); NodeOperation *uv_input = get_input_operation(UV_INPUT_INDEX); - uv_width_ = uv_input->getWidth(); - uv_height_ = uv_input->getHeight(); + uv_width_ = uv_input->get_width(); + uv_height_ = uv_input->get_height(); } -void MapUVOperation::initExecution() +void MapUVOperation::init_execution() { - inputColorProgram_ = this->getInputSocketReader(0); - inputUVProgram_ = this->getInputSocketReader(1); + input_color_program_ = this->get_input_socket_reader(0); + inputUVProgram_ = this->get_input_socket_reader(1); if (execution_model_ == eExecutionModel::Tiled) { uv_input_read_fn_ = [=](float x, float y, float *out) { - inputUVProgram_->readSampled(out, x, y, PixelSampler::Bilinear); + inputUVProgram_->read_sampled(out, x, y, PixelSampler::Bilinear); }; } } -void MapUVOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void MapUVOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { float xy[2] = {x, y}; float uv[2], deriv[2][2], alpha; - pixelTransform(xy, uv, deriv, alpha); + pixel_transform(xy, uv, deriv, alpha); if (alpha == 0.0f) { zero_v4(output); return; } /* EWA filtering */ - inputColorProgram_->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + input_color_program_->read_filtered(output, uv[0], uv[1], deriv[0], deriv[1]); /* UV to alpha threshold */ const float threshold = alpha_ * 0.05f; @@ -79,8 +79,8 @@ void MapUVOperation::executePixelSampled(float output[4], */ float du = len_v2(deriv[0]); float dv = len_v2(deriv[1]); - float factor = 1.0f - threshold * (du / inputColorProgram_->getWidth() + - dv / inputColorProgram_->getHeight()); + float factor = 1.0f - threshold * (du / input_color_program_->get_width() + + dv / input_color_program_->get_height()); if (factor < 0.0f) { alpha = 0.0f; } @@ -111,10 +111,10 @@ bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_ return true; } -void MapUVOperation::pixelTransform(const float xy[2], - float r_uv[2], - float r_deriv[2][2], - float &r_alpha) +void MapUVOperation::pixel_transform(const float xy[2], + float r_uv[2], + float r_deriv[2][2], + float &r_alpha) { float uv[2], alpha; /* temporary variables for derivative estimation */ int num; @@ -162,37 +162,37 @@ void MapUVOperation::pixelTransform(const float xy[2], } } -void MapUVOperation::deinitExecution() +void MapUVOperation::deinit_execution() { inputUVProgram_ = nullptr; - inputColorProgram_ = nullptr; + input_color_program_ = nullptr; } -bool MapUVOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool MapUVOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti colorInput; - rcti uvInput; + rcti color_input; + rcti uv_input; NodeOperation *operation = nullptr; /* the uv buffer only needs a 3x3 buffer. The image needs whole buffer */ - operation = getInputOperation(0); - colorInput.xmax = operation->getWidth(); - colorInput.xmin = 0; - colorInput.ymax = operation->getHeight(); - colorInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&colorInput, readOperation, output)) { + operation = get_input_operation(0); + color_input.xmax = operation->get_width(); + color_input.xmin = 0; + color_input.ymax = operation->get_height(); + color_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&color_input, read_operation, output)) { return true; } - operation = getInputOperation(1); - uvInput.xmax = input->xmax + 1; - uvInput.xmin = input->xmin - 1; - uvInput.ymax = input->ymax + 1; - uvInput.ymin = input->ymin - 1; - if (operation->determineDependingAreaOfInterest(&uvInput, readOperation, output)) { + operation = get_input_operation(1); + uv_input.xmax = input->xmax + 1; + uv_input.xmin = input->xmin - 1; + uv_input.ymax = input->ymax + 1; + uv_input.ymin = input->ymin - 1; + if (operation->determine_depending_area_of_interest(&uv_input, read_operation, output)) { return true; } @@ -236,7 +236,7 @@ void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output, float uv[2]; float deriv[2][2]; float alpha; - pixelTransform(xy, uv, deriv, alpha); + pixel_transform(xy, uv, deriv, alpha); if (alpha == 0.0f) { zero_v4(it.out); continue; diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h index 7025e02e069..38d848f61a0 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.h +++ b/source/blender/compositor/operations/COM_MapUVOperation.h @@ -27,10 +27,10 @@ class MapUVOperation : public MultiThreadedOperation { static constexpr int IMAGE_INPUT_INDEX = 0; static constexpr int UV_INPUT_INDEX = 1; /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ SocketReader *inputUVProgram_; - SocketReader *inputColorProgram_; + SocketReader *input_color_program_; int uv_width_; int uv_height_; @@ -47,30 +47,30 @@ class MapUVOperation : public MultiThreadedOperation { /** * we need a 3x3 differential filter for UV Input and full buffer for the image */ - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2], float &r_alpha); + void pixel_transform(const float xy[2], float r_uv[2], float r_deriv[2][2], float &r_alpha); void init_data() override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setAlpha(float alpha) + void set_alpha(float alpha) { alpha_ = alpha; } diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc index 0f4175c3d3d..da5ab04cc98 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.cc +++ b/source/blender/compositor/operations/COM_MapValueOperation.cc @@ -22,24 +22,24 @@ namespace blender::compositor { MapValueOperation::MapValueOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - inputOperation_ = nullptr; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_operation_ = nullptr; flags.can_be_constant = true; } -void MapValueOperation::initExecution() +void MapValueOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void MapValueOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MapValueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float src[4]; - inputOperation_->readSampled(src, x, y, sampler); + input_operation_->read_sampled(src, x, y, sampler); TexMapping *texmap = settings_; float value = (src[0] + texmap->loc[0]) * texmap->size[0]; if (texmap->flag & TEXMAP_CLIP_MIN) { @@ -56,9 +56,9 @@ void MapValueOperation::executePixelSampled(float output[4], output[0] = value; } -void MapValueOperation::deinitExecution() +void MapValueOperation::deinit_execution() { - inputOperation_ = nullptr; + input_operation_ = nullptr; } void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MapValueOperation.h b/source/blender/compositor/operations/COM_MapValueOperation.h index 38cb75869d5..77d3a4a2932 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.h +++ b/source/blender/compositor/operations/COM_MapValueOperation.h @@ -30,9 +30,9 @@ namespace blender::compositor { class MapValueOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputOperation_; + SocketReader *input_operation_; TexMapping *settings_; public: @@ -44,22 +44,22 @@ class MapValueOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; /** * \brief set the TexMapping settings */ - void setSettings(TexMapping *settings) + void set_settings(TexMapping *settings) { settings_ = settings; } diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc index d02d23445b6..4e4cc820edb 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cc +++ b/source/blender/compositor/operations/COM_MaskOperation.cc @@ -25,31 +25,31 @@ namespace blender::compositor { MaskOperation::MaskOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); mask_ = nullptr; - maskWidth_ = 0; - maskHeight_ = 0; - maskWidthInv_ = 0.0f; - maskHeightInv_ = 0.0f; + mask_width_ = 0; + mask_height_ = 0; + mask_width_inv_ = 0.0f; + mask_height_inv_ = 0.0f; frame_shutter_ = 0.0f; frame_number_ = 0; - rasterMaskHandleTot_ = 1; - memset(rasterMaskHandles_, 0, sizeof(rasterMaskHandles_)); + raster_mask_handle_tot_ = 1; + memset(raster_mask_handles_, 0, sizeof(raster_mask_handles_)); } -void MaskOperation::initExecution() +void MaskOperation::init_execution() { - if (mask_ && rasterMaskHandles_[0] == nullptr) { - if (rasterMaskHandleTot_ == 1) { - rasterMaskHandles_[0] = BKE_maskrasterize_handle_new(); + if (mask_ && raster_mask_handles_[0] == nullptr) { + if (raster_mask_handle_tot_ == 1) { + raster_mask_handles_[0] = BKE_maskrasterize_handle_new(); BKE_maskrasterize_handle_init( - rasterMaskHandles_[0], mask_, maskWidth_, maskHeight_, true, true, do_feather_); + raster_mask_handles_[0], mask_, mask_width_, mask_height_, true, true, do_feather_); } else { /* make a throw away copy of the mask */ const float frame = (float)frame_number_ - frame_shutter_; - const float frame_step = (frame_shutter_ * 2.0f) / rasterMaskHandleTot_; + const float frame_step = (frame_shutter_ * 2.0f) / raster_mask_handle_tot_; float frame_iter = frame; Mask *mask_temp = (Mask *)BKE_id_copy_ex( @@ -67,14 +67,19 @@ void MaskOperation::initExecution() } } - for (unsigned int i = 0; i < rasterMaskHandleTot_; i++) { - rasterMaskHandles_[i] = BKE_maskrasterize_handle_new(); + for (unsigned int i = 0; i < raster_mask_handle_tot_; i++) { + raster_mask_handles_[i] = BKE_maskrasterize_handle_new(); /* re-eval frame info */ BKE_mask_evaluate(mask_temp, frame_iter, true); - BKE_maskrasterize_handle_init( - rasterMaskHandles_[i], mask_temp, maskWidth_, maskHeight_, true, true, do_feather_); + BKE_maskrasterize_handle_init(raster_mask_handles_[i], + mask_temp, + mask_width_, + mask_height_, + true, + true, + do_feather_); frame_iter += frame_step; } @@ -84,41 +89,41 @@ void MaskOperation::initExecution() } } -void MaskOperation::deinitExecution() +void MaskOperation::deinit_execution() { - for (unsigned int i = 0; i < rasterMaskHandleTot_; i++) { - if (rasterMaskHandles_[i]) { - BKE_maskrasterize_handle_free(rasterMaskHandles_[i]); - rasterMaskHandles_[i] = nullptr; + for (unsigned int i = 0; i < raster_mask_handle_tot_; i++) { + if (raster_mask_handles_[i]) { + BKE_maskrasterize_handle_free(raster_mask_handles_[i]); + raster_mask_handles_[i] = nullptr; } } } void MaskOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (maskWidth_ == 0 || maskHeight_ == 0) { + if (mask_width_ == 0 || mask_height_ == 0) { r_area = COM_AREA_NONE; } else { r_area = preferred_area; - r_area.xmax = r_area.xmin + maskWidth_; - r_area.ymax = r_area.ymin + maskHeight_; + r_area.xmax = r_area.xmin + mask_width_; + r_area.ymax = r_area.ymin + mask_height_; } } -void MaskOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void MaskOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { const float xy[2] = { - (x * maskWidthInv_) + mask_px_ofs_[0], - (y * maskHeightInv_) + mask_px_ofs_[1], + (x * mask_width_inv_) + mask_px_ofs_[0], + (y * mask_height_inv_) + mask_px_ofs_[1], }; - if (rasterMaskHandleTot_ == 1) { - if (rasterMaskHandles_[0]) { - output[0] = BKE_maskrasterize_handle_sample(rasterMaskHandles_[0], xy); + if (raster_mask_handle_tot_ == 1) { + if (raster_mask_handles_[0]) { + output[0] = BKE_maskrasterize_handle_sample(raster_mask_handles_[0], xy); } else { output[0] = 0.0f; @@ -128,14 +133,14 @@ void MaskOperation::executePixelSampled(float output[4], /* In case loop below fails. */ output[0] = 0.0f; - for (unsigned int i = 0; i < rasterMaskHandleTot_; i++) { - if (rasterMaskHandles_[i]) { - output[0] += BKE_maskrasterize_handle_sample(rasterMaskHandles_[i], xy); + for (unsigned int i = 0; i < raster_mask_handle_tot_; i++) { + if (raster_mask_handles_[i]) { + output[0] += BKE_maskrasterize_handle_sample(raster_mask_handles_[i], xy); } } /* until we get better falloff */ - output[0] /= rasterMaskHandleTot_; + output[0] /= raster_mask_handle_tot_; } } @@ -151,23 +156,23 @@ void MaskOperation::update_memory_buffer_partial(MemoryBuffer *output, float xy[2]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - xy[0] = it.x * maskWidthInv_ + mask_px_ofs_[0]; - xy[1] = it.y * maskHeightInv_ + mask_px_ofs_[1]; + xy[0] = it.x * mask_width_inv_ + mask_px_ofs_[0]; + xy[1] = it.y * mask_height_inv_ + mask_px_ofs_[1]; *it.out = 0.0f; for (MaskRasterHandle *handle : handles) { *it.out += BKE_maskrasterize_handle_sample(handle, xy); } /* Until we get better falloff. */ - *it.out /= rasterMaskHandleTot_; + *it.out /= raster_mask_handle_tot_; } } Vector MaskOperation::get_non_null_handles() const { Vector handles; - for (int i = 0; i < rasterMaskHandleTot_; i++) { - MaskRasterHandle *handle = rasterMaskHandles_[i]; + for (int i = 0; i < raster_mask_handle_tot_; i++) { + MaskRasterHandle *handle = raster_mask_handles_[i]; if (handle == nullptr) { continue; } diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h index e8f386242b4..5fc0ed96c22 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.h +++ b/source/blender/compositor/operations/COM_MaskOperation.h @@ -37,10 +37,10 @@ class MaskOperation : public MultiThreadedOperation { /* NOTE: these are used more like aspect, * but they _do_ impact on mask detail */ - int maskWidth_; - int maskHeight_; - float maskWidthInv_; /* `1 / maskWidth_` */ - float maskHeightInv_; /* `1 / maskHeight_` */ + int mask_width_; + int mask_height_; + float mask_width_inv_; /* `1 / mask_width_` */ + float mask_height_inv_; /* `1 / mask_height_` */ float mask_px_ofs_[2]; float frame_shutter_; @@ -48,8 +48,8 @@ class MaskOperation : public MultiThreadedOperation { bool do_feather_; - struct MaskRasterHandle *rasterMaskHandles_[CMP_NODE_MASK_MBLUR_SAMPLES_MAX]; - unsigned int rasterMaskHandleTot_; + struct MaskRasterHandle *raster_mask_handles_[CMP_NODE_MASK_MBLUR_SAMPLES_MAX]; + unsigned int raster_mask_handle_tot_; /** * Determine the output resolution. The resolution is retrieved from the Renderer @@ -59,44 +59,44 @@ class MaskOperation : public MultiThreadedOperation { public: MaskOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setMask(Mask *mask) + void set_mask(Mask *mask) { mask_ = mask; } - void setMaskWidth(int width) + void set_mask_width(int width) { - maskWidth_ = width; - maskWidthInv_ = 1.0f / (float)width; - mask_px_ofs_[0] = maskWidthInv_ * 0.5f; + mask_width_ = width; + mask_width_inv_ = 1.0f / (float)width; + mask_px_ofs_[0] = mask_width_inv_ * 0.5f; } - void setMaskHeight(int height) + void set_mask_height(int height) { - maskHeight_ = height; - maskHeightInv_ = 1.0f / (float)height; - mask_px_ofs_[1] = maskHeightInv_ * 0.5f; + mask_height_ = height; + mask_height_inv_ = 1.0f / (float)height; + mask_px_ofs_[1] = mask_height_inv_ * 0.5f; } - void setFramenumber(int frame_number) + void set_framenumber(int frame_number) { frame_number_ = frame_number; } - void setFeather(bool feather) + void set_feather(bool feather) { do_feather_ = feather; } - void setMotionBlurSamples(int samples) + void set_motion_blur_samples(int samples) { - rasterMaskHandleTot_ = MIN2(MAX2(1, samples), CMP_NODE_MASK_MBLUR_SAMPLES_MAX); + raster_mask_handle_tot_ = MIN2(MAX2(1, samples), CMP_NODE_MASK_MBLUR_SAMPLES_MAX); } - void setMotionBlurShutter(float shutter) + void set_motion_blur_shutter(float shutter) { frame_shutter_ = shutter; } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc index 30b6e14a324..55151b70050 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.cc +++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc @@ -24,36 +24,36 @@ MathBaseOperation::MathBaseOperation() { /* TODO(manzanilla): after removing tiled implementation, template this class to only add needed * number of inputs. */ - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - inputValue1Operation_ = nullptr; - inputValue2Operation_ = nullptr; - inputValue3Operation_ = nullptr; - useClamp_ = false; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_value1_operation_ = nullptr; + input_value2_operation_ = nullptr; + input_value3_operation_ = nullptr; + use_clamp_ = false; this->flags.can_be_constant = true; } -void MathBaseOperation::initExecution() +void MathBaseOperation::init_execution() { - inputValue1Operation_ = this->getInputSocketReader(0); - inputValue2Operation_ = this->getInputSocketReader(1); - inputValue3Operation_ = this->getInputSocketReader(2); + input_value1_operation_ = this->get_input_socket_reader(0); + input_value2_operation_ = this->get_input_socket_reader(1); + input_value3_operation_ = this->get_input_socket_reader(2); } -void MathBaseOperation::deinitExecution() +void MathBaseOperation::deinit_execution() { - inputValue1Operation_ = nullptr; - inputValue2Operation_ = nullptr; - inputValue3Operation_ = nullptr; + input_value1_operation_ = nullptr; + input_value2_operation_ = nullptr; + input_value3_operation_ = nullptr; } void MathBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { NodeOperationInput *socket; rcti temp_area; - socket = this->getInputSocket(0); + socket = this->get_input_socket(0); const bool determined = socket->determine_canvas(COM_AREA_NONE, temp_area); if (determined) { this->set_canvas_input_index(0); @@ -64,9 +64,9 @@ void MathBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_are NodeOperation::determine_canvas(preferred_area, r_area); } -void MathBaseOperation::clampIfNeeded(float *color) +void MathBaseOperation::clamp_if_needed(float *color) { - if (useClamp_) { + if (use_clamp_) { CLAMP(color[0], 0.0f, 1.0f); } } @@ -79,70 +79,73 @@ void MathBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, update_memory_buffer_partial(it); } -void MathAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void MathAddOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = inputValue1[0] + inputValue2[0]; + output[0] = input_value1[0] + input_value2[0]; - clampIfNeeded(output); + clamp_if_needed(output); } -void MathSubtractOperation::executePixelSampled(float output[4], +void MathSubtractOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_value1[4]; + float input_value2[4]; + + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + + output[0] = input_value1[0] - input_value2[0]; + + clamp_if_needed(output); +} + +void MathMultiplyOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_value1[4]; + float input_value2[4]; + + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + + output[0] = input_value1[0] * input_value2[0]; + + clamp_if_needed(output); +} + +void MathDivideOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = inputValue1[0] - inputValue2[0]; - - clampIfNeeded(output); -} - -void MathMultiplyOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) -{ - float inputValue1[4]; - float inputValue2[4]; - - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); - - output[0] = inputValue1[0] * inputValue2[0]; - - clampIfNeeded(output); -} - -void MathDivideOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) -{ - float inputValue1[4]; - float inputValue2[4]; - - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); - - if (inputValue2[0] == 0) { /* We don't want to divide by zero. */ + if (input_value2[0] == 0) { /* We don't want to divide by zero. */ output[0] = 0.0; } else { - output[0] = inputValue1[0] / inputValue2[0]; + output[0] = input_value1[0] / input_value2[0]; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathDivideOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -153,20 +156,20 @@ void MathDivideOperation::update_memory_buffer_partial(BuffersIterator &i } } -void MathSineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathSineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = sin(inputValue1[0]); + output[0] = sin(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathSineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -177,20 +180,20 @@ void MathSineOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathCosineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathCosineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = cos(inputValue1[0]); + output[0] = cos(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathCosineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -201,20 +204,20 @@ void MathCosineOperation::update_memory_buffer_partial(BuffersIterator &i } } -void MathTangentOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathTangentOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = tan(inputValue1[0]); + output[0] = tan(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathTangentOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -225,20 +228,20 @@ void MathTangentOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathHyperbolicSineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathHyperbolicSineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = sinh(inputValue1[0]); + output[0] = sinh(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathHyperbolicSineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -249,20 +252,20 @@ void MathHyperbolicSineOperation::update_memory_buffer_partial(BuffersIteratorreadSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = cosh(inputValue1[0]); + output[0] = cosh(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathHyperbolicCosineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -273,20 +276,20 @@ void MathHyperbolicCosineOperation::update_memory_buffer_partial(BuffersIterator } } -void MathHyperbolicTangentOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathHyperbolicTangentOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = tanh(inputValue1[0]); + output[0] = tanh(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathHyperbolicTangentOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -297,25 +300,25 @@ void MathHyperbolicTangentOperation::update_memory_buffer_partial(BuffersIterato } } -void MathArcSineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathArcSineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - if (inputValue1[0] <= 1 && inputValue1[0] >= -1) { - output[0] = asin(inputValue1[0]); + if (input_value1[0] <= 1 && input_value1[0] >= -1) { + output[0] = asin(input_value1[0]); } else { output[0] = 0.0; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathArcSineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -326,25 +329,25 @@ void MathArcSineOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathArcCosineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathArcCosineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - if (inputValue1[0] <= 1 && inputValue1[0] >= -1) { - output[0] = acos(inputValue1[0]); + if (input_value1[0] <= 1 && input_value1[0] >= -1) { + output[0] = acos(input_value1[0]); } else { output[0] = 0.0; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathArcCosineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -355,20 +358,20 @@ void MathArcCosineOperation::update_memory_buffer_partial(BuffersIterator } } -void MathArcTangentOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathArcTangentOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = atan(inputValue1[0]); + output[0] = atan(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathArcTangentOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -379,32 +382,32 @@ void MathArcTangentOperation::update_memory_buffer_partial(BuffersIteratorreadSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - if (inputValue1[0] >= 0) { - output[0] = pow(inputValue1[0], inputValue2[0]); + if (input_value1[0] >= 0) { + output[0] = pow(input_value1[0], input_value2[0]); } else { - float y_mod_1 = fmod(inputValue2[0], 1); + float y_mod_1 = fmod(input_value2[0], 1); /* if input value is not nearly an integer, fall back to zero, nicer than straight rounding */ if (y_mod_1 > 0.999f || y_mod_1 < 0.001f) { - output[0] = pow(inputValue1[0], floorf(inputValue2[0] + 0.5f)); + output[0] = pow(input_value1[0], floorf(input_value2[0] + 0.5f)); } else { output[0] = 0.0; } } - clampIfNeeded(output); + clamp_if_needed(output); } void MathPowerOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -430,25 +433,25 @@ void MathPowerOperation::update_memory_buffer_partial(BuffersIterator &it } } -void MathLogarithmOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathLogarithmOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - if (inputValue1[0] > 0 && inputValue2[0] > 0) { - output[0] = log(inputValue1[0]) / log(inputValue2[0]); + if (input_value1[0] > 0 && input_value2[0] > 0) { + output[0] = log(input_value1[0]) / log(input_value2[0]); } else { output[0] = 0.0; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathLogarithmOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -466,20 +469,20 @@ void MathLogarithmOperation::update_memory_buffer_partial(BuffersIterator } } -void MathMinimumOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathMinimumOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = MIN2(inputValue1[0], inputValue2[0]); + output[0] = MIN2(input_value1[0], input_value2[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathMinimumOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -490,20 +493,20 @@ void MathMinimumOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathMaximumOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathMaximumOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = MAX2(inputValue1[0], inputValue2[0]); + output[0] = MAX2(input_value1[0], input_value2[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathMaximumOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -514,20 +517,20 @@ void MathMaximumOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathRoundOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathRoundOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = round(inputValue1[0]); + output[0] = round(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathRoundOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -538,57 +541,57 @@ void MathRoundOperation::update_memory_buffer_partial(BuffersIterator &it } } -void MathLessThanOperation::executePixelSampled(float output[4], +void MathLessThanOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_value1[4]; + float input_value2[4]; + + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + + output[0] = input_value1[0] < input_value2[0] ? 1.0f : 0.0f; + + clamp_if_needed(output); +} + +void MathGreaterThanOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_value1[4]; + float input_value2[4]; + + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + + output[0] = input_value1[0] > input_value2[0] ? 1.0f : 0.0f; + + clamp_if_needed(output); +} + +void MathModuloOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = inputValue1[0] < inputValue2[0] ? 1.0f : 0.0f; - - clampIfNeeded(output); -} - -void MathGreaterThanOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) -{ - float inputValue1[4]; - float inputValue2[4]; - - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); - - output[0] = inputValue1[0] > inputValue2[0] ? 1.0f : 0.0f; - - clampIfNeeded(output); -} - -void MathModuloOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) -{ - float inputValue1[4]; - float inputValue2[4]; - - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); - - if (inputValue2[0] == 0) { + if (input_value2[0] == 0) { output[0] = 0.0; } else { - output[0] = fmod(inputValue1[0], inputValue2[0]); + output[0] = fmod(input_value1[0], input_value2[0]); } - clampIfNeeded(output); + clamp_if_needed(output); } void MathModuloOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -600,18 +603,18 @@ void MathModuloOperation::update_memory_buffer_partial(BuffersIterator &i } } -void MathAbsoluteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathAbsoluteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = fabs(inputValue1[0]); + output[0] = fabs(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathAbsoluteOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -622,18 +625,18 @@ void MathAbsoluteOperation::update_memory_buffer_partial(BuffersIterator } } -void MathRadiansOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathRadiansOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = DEG2RADF(inputValue1[0]); + output[0] = DEG2RADF(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathRadiansOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -644,18 +647,18 @@ void MathRadiansOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathDegreesOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathDegreesOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = RAD2DEGF(inputValue1[0]); + output[0] = RAD2DEGF(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathDegreesOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -666,20 +669,20 @@ void MathDegreesOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathArcTan2Operation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathArcTan2Operation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = atan2(inputValue1[0], inputValue2[0]); + output[0] = atan2(input_value1[0], input_value2[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathArcTan2Operation::update_memory_buffer_partial(BuffersIterator &it) @@ -690,18 +693,18 @@ void MathArcTan2Operation::update_memory_buffer_partial(BuffersIterator & } } -void MathFloorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathFloorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = floor(inputValue1[0]); + output[0] = floor(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathFloorOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -712,18 +715,18 @@ void MathFloorOperation::update_memory_buffer_partial(BuffersIterator &it } } -void MathCeilOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathCeilOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = ceil(inputValue1[0]); + output[0] = ceil(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathCeilOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -734,18 +737,18 @@ void MathCeilOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathFractOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathFractOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = inputValue1[0] - floor(inputValue1[0]); + output[0] = input_value1[0] - floor(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathFractOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -756,23 +759,23 @@ void MathFractOperation::update_memory_buffer_partial(BuffersIterator &it } } -void MathSqrtOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathSqrtOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - if (inputValue1[0] > 0) { - output[0] = sqrt(inputValue1[0]); + if (input_value1[0] > 0) { + output[0] = sqrt(input_value1[0]); } else { output[0] = 0.0f; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathSqrtOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -783,23 +786,23 @@ void MathSqrtOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathInverseSqrtOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathInverseSqrtOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - if (inputValue1[0] > 0) { - output[0] = 1.0f / sqrt(inputValue1[0]); + if (input_value1[0] > 0) { + output[0] = 1.0f / sqrt(input_value1[0]); } else { output[0] = 0.0f; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathInverseSqrtOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -810,18 +813,18 @@ void MathInverseSqrtOperation::update_memory_buffer_partial(BuffersIteratorreadSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = compatible_signf(inputValue1[0]); + output[0] = compatible_signf(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathSignOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -832,18 +835,18 @@ void MathSignOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathExponentOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathExponentOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = expf(inputValue1[0]); + output[0] = expf(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathExponentOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -854,18 +857,18 @@ void MathExponentOperation::update_memory_buffer_partial(BuffersIterator } } -void MathTruncOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathTruncOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = (inputValue1[0] >= 0.0f) ? floor(inputValue1[0]) : ceil(inputValue1[0]); + output[0] = (input_value1[0] >= 0.0f) ? floor(input_value1[0]) : ceil(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathTruncOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -877,25 +880,25 @@ void MathTruncOperation::update_memory_buffer_partial(BuffersIterator &it } } -void MathSnapOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathSnapOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - if (inputValue1[0] == 0 || inputValue2[0] == 0) { /* We don't want to divide by zero. */ + if (input_value1[0] == 0 || input_value2[0] == 0) { /* We don't want to divide by zero. */ output[0] = 0.0f; } else { - output[0] = floorf(inputValue1[0] / inputValue2[0]) * inputValue2[0]; + output[0] = floorf(input_value1[0] / input_value2[0]) * input_value2[0]; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathSnapOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -913,22 +916,22 @@ void MathSnapOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathWrapOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathWrapOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; - float inputValue3[4]; + float input_value1[4]; + float input_value2[4]; + float input_value3[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); - inputValue3Operation_->readSampled(inputValue3, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + input_value3_operation_->read_sampled(input_value3, x, y, sampler); - output[0] = wrapf(inputValue1[0], inputValue2[0], inputValue3[0]); + output[0] = wrapf(input_value1[0], input_value2[0], input_value3[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathWrapOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -939,20 +942,20 @@ void MathWrapOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathPingpongOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathPingpongOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = pingpongf(inputValue1[0], inputValue2[0]); + output[0] = pingpongf(input_value1[0], input_value2[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathPingpongOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -963,23 +966,23 @@ void MathPingpongOperation::update_memory_buffer_partial(BuffersIterator } } -void MathCompareOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathCompareOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; - float inputValue3[4]; + float input_value1[4]; + float input_value2[4]; + float input_value3[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); - inputValue3Operation_->readSampled(inputValue3, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + input_value3_operation_->read_sampled(input_value3, x, y, sampler); - output[0] = (fabsf(inputValue1[0] - inputValue2[0]) <= MAX2(inputValue3[0], 1e-5f)) ? 1.0f : - 0.0f; + output[0] = (fabsf(input_value1[0] - input_value2[0]) <= MAX2(input_value3[0], 1e-5f)) ? 1.0f : + 0.0f; - clampIfNeeded(output); + clamp_if_needed(output); } void MathCompareOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -990,22 +993,22 @@ void MathCompareOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathMultiplyAddOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathMultiplyAddOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; - float inputValue3[4]; + float input_value1[4]; + float input_value2[4]; + float input_value3[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); - inputValue3Operation_->readSampled(inputValue3, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + input_value3_operation_->read_sampled(input_value3, x, y, sampler); - output[0] = inputValue1[0] * inputValue2[0] + inputValue3[0]; + output[0] = input_value1[0] * input_value2[0] + input_value3[0]; - clampIfNeeded(output); + clamp_if_needed(output); } void MathMultiplyAddOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -1016,22 +1019,22 @@ void MathMultiplyAddOperation::update_memory_buffer_partial(BuffersIteratorreadSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); - inputValue3Operation_->readSampled(inputValue3, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + input_value3_operation_->read_sampled(input_value3, x, y, sampler); - output[0] = smoothminf(inputValue1[0], inputValue2[0], inputValue3[0]); + output[0] = smoothminf(input_value1[0], input_value2[0], input_value3[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathSmoothMinOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -1042,22 +1045,22 @@ void MathSmoothMinOperation::update_memory_buffer_partial(BuffersIterator } } -void MathSmoothMaxOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathSmoothMaxOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; - float inputValue3[4]; + float input_value1[4]; + float input_value2[4]; + float input_value3[4]; - inputValue1Operation_->readSampled(inputValue1, x, y, sampler); - inputValue2Operation_->readSampled(inputValue2, x, y, sampler); - inputValue3Operation_->readSampled(inputValue3, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + input_value3_operation_->read_sampled(input_value3, x, y, sampler); - output[0] = -smoothminf(-inputValue1[0], -inputValue2[0], inputValue3[0]); + output[0] = -smoothminf(-input_value1[0], -input_value2[0], input_value3[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathSmoothMaxOperation::update_memory_buffer_partial(BuffersIterator &it) diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h index fcb735720b2..dc5a16ba4a1 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.h +++ b/source/blender/compositor/operations/COM_MathBaseOperation.h @@ -29,13 +29,13 @@ namespace blender::compositor { class MathBaseOperation : public MultiThreadedOperation { protected: /** - * Prefetched reference to the inputProgram + * Prefetched reference to the input_program */ - SocketReader *inputValue1Operation_; - SocketReader *inputValue2Operation_; - SocketReader *inputValue3Operation_; + SocketReader *input_value1_operation_; + SocketReader *input_value2_operation_; + SocketReader *input_value3_operation_; - bool useClamp_; + bool use_clamp_; protected: /** @@ -44,11 +44,11 @@ class MathBaseOperation : public MultiThreadedOperation { MathBaseOperation(); /* TODO(manzanilla): to be removed with tiled implementation. */ - void clampIfNeeded(float color[4]); + void clamp_if_needed(float color[4]); float clamp_when_enabled(float value) { - if (useClamp_) { + if (use_clamp_) { return CLAMPIS(value, 0.0f, 1.0f); } return value; @@ -56,7 +56,7 @@ class MathBaseOperation : public MultiThreadedOperation { void clamp_when_enabled(float *out) { - if (useClamp_) { + if (use_clamp_) { CLAMP(*out, 0.0f, 1.0f); } } @@ -65,21 +65,21 @@ class MathBaseOperation : public MultiThreadedOperation { /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; /** * Determine resolution */ void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void setUseClamp(bool value) + void set_use_clamp(bool value) { - useClamp_ = value; + use_clamp_ = value; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -104,40 +104,40 @@ class MathFunctor2Operation : public MathBaseOperation { class MathAddOperation : public MathFunctor2Operation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathSubtractOperation : public MathFunctor2Operation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathMultiplyOperation : public MathFunctor2Operation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathDivideOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathSineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathCosineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathTangentOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -145,21 +145,21 @@ class MathTangentOperation : public MathBaseOperation { class MathHyperbolicSineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathHyperbolicCosineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathHyperbolicTangentOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -167,72 +167,72 @@ class MathHyperbolicTangentOperation : public MathBaseOperation { class MathArcSineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathArcCosineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathArcTangentOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathPowerOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathLogarithmOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathMinimumOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathMaximumOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathRoundOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathLessThanOperation : public MathFunctor2Operation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathGreaterThanOperation : public MathFunctor2Operation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathModuloOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -240,7 +240,7 @@ class MathModuloOperation : public MathBaseOperation { class MathAbsoluteOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -248,7 +248,7 @@ class MathAbsoluteOperation : public MathBaseOperation { class MathRadiansOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -256,7 +256,7 @@ class MathRadiansOperation : public MathBaseOperation { class MathDegreesOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -264,7 +264,7 @@ class MathDegreesOperation : public MathBaseOperation { class MathArcTan2Operation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -272,7 +272,7 @@ class MathArcTan2Operation : public MathBaseOperation { class MathFloorOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -280,7 +280,7 @@ class MathFloorOperation : public MathBaseOperation { class MathCeilOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -288,7 +288,7 @@ class MathCeilOperation : public MathBaseOperation { class MathFractOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -296,7 +296,7 @@ class MathFractOperation : public MathBaseOperation { class MathSqrtOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -304,7 +304,7 @@ class MathSqrtOperation : public MathBaseOperation { class MathInverseSqrtOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -312,7 +312,7 @@ class MathInverseSqrtOperation : public MathBaseOperation { class MathSignOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -320,7 +320,7 @@ class MathSignOperation : public MathBaseOperation { class MathExponentOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -328,7 +328,7 @@ class MathExponentOperation : public MathBaseOperation { class MathTruncOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -336,7 +336,7 @@ class MathTruncOperation : public MathBaseOperation { class MathSnapOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -344,7 +344,7 @@ class MathSnapOperation : public MathBaseOperation { class MathWrapOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -352,7 +352,7 @@ class MathWrapOperation : public MathBaseOperation { class MathPingpongOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -360,7 +360,7 @@ class MathPingpongOperation : public MathBaseOperation { class MathCompareOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -368,7 +368,7 @@ class MathCompareOperation : public MathBaseOperation { class MathMultiplyAddOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -376,7 +376,7 @@ class MathMultiplyAddOperation : public MathBaseOperation { class MathSmoothMinOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -384,7 +384,7 @@ class MathSmoothMinOperation : public MathBaseOperation { class MathSmoothMaxOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc index 216dbc3a701..a40979fc1e6 100644 --- a/source/blender/compositor/operations/COM_MixOperation.cc +++ b/source/blender/compositor/operations/COM_MixOperation.cc @@ -24,44 +24,47 @@ namespace blender::compositor { MixBaseOperation::MixBaseOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - inputValueOperation_ = nullptr; - inputColor1Operation_ = nullptr; - inputColor2Operation_ = nullptr; - this->setUseValueAlphaMultiply(false); - this->setUseClamp(false); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_value_operation_ = nullptr; + input_color1_operation_ = nullptr; + input_color2_operation_ = nullptr; + this->set_use_value_alpha_multiply(false); + this->set_use_clamp(false); flags.can_be_constant = true; } -void MixBaseOperation::initExecution() +void MixBaseOperation::init_execution() { - inputValueOperation_ = this->getInputSocketReader(0); - inputColor1Operation_ = this->getInputSocketReader(1); - inputColor2Operation_ = this->getInputSocketReader(2); + input_value_operation_ = this->get_input_socket_reader(0); + input_color1_operation_ = this->get_input_socket_reader(1); + input_color2_operation_ = this->get_input_socket_reader(2); } -void MixBaseOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void MixBaseOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = valuem * (inputColor1[0]) + value * (inputColor2[0]); - output[1] = valuem * (inputColor1[1]) + value * (inputColor2[1]); - output[2] = valuem * (inputColor1[2]) + value * (inputColor2[2]); - output[3] = inputColor1[3]; + output[0] = valuem * (input_color1[0]) + value * (input_color2[0]); + output[1] = valuem * (input_color1[1]) + value * (input_color2[1]); + output[2] = valuem * (input_color1[2]) + value * (input_color2[2]); + output[3] = input_color1[3]; } void MixBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -69,13 +72,13 @@ void MixBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area NodeOperationInput *socket; rcti temp_area; - socket = this->getInputSocket(1); + socket = this->get_input_socket(1); bool determined = socket->determine_canvas(COM_AREA_NONE, temp_area); if (determined) { this->set_canvas_input_index(1); } else { - socket = this->getInputSocket(2); + socket = this->get_input_socket(2); determined = socket->determine_canvas(COM_AREA_NONE, temp_area); if (determined) { this->set_canvas_input_index(2); @@ -87,11 +90,11 @@ void MixBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area NodeOperation::determine_canvas(preferred_area, r_area); } -void MixBaseOperation::deinitExecution() +void MixBaseOperation::deinit_execution() { - inputValueOperation_ = nullptr; - inputColor1Operation_ = nullptr; - inputColor2Operation_ = nullptr; + input_value_operation_ = nullptr; + input_color1_operation_ = nullptr; + input_color2_operation_ = nullptr; } void MixBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -121,7 +124,7 @@ void MixBaseOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -135,33 +138,36 @@ void MixBaseOperation::update_memory_buffer_row(PixelCursor &p) /* ******** Mix Add Operation ******** */ -void MixAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void MixAddOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } - output[0] = inputColor1[0] + value * inputColor2[0]; - output[1] = inputColor1[1] + value * inputColor2[1]; - output[2] = inputColor1[2] + value * inputColor2[2]; - output[3] = inputColor1[3]; + output[0] = input_color1[0] + value * input_color2[0]; + output[1] = input_color1[1] + value * input_color2[1]; + output[2] = input_color1[2] + value * input_color2[2]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixAddOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } p.out[0] = p.color1[0] + value * p.color2[0]; @@ -169,45 +175,45 @@ void MixAddOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = p.color1[2] + value * p.color2[2]; p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Blend Operation ******** */ -void MixBlendOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixBlendOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; float value; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); - value = inputValue[0]; + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); + value = input_value[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = valuem * (inputColor1[0]) + value * (inputColor2[0]); - output[1] = valuem * (inputColor1[1]) + value * (inputColor2[1]); - output[2] = valuem * (inputColor1[2]) + value * (inputColor2[2]); - output[3] = inputColor1[3]; + output[0] = valuem * (input_color1[0]) + value * (input_color2[0]); + output[1] = valuem * (input_color1[1]) + value * (input_color2[1]); + output[2] = valuem * (input_color1[2]) + value * (input_color2[2]); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixBlendOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } float value_m = 1.0f - value; @@ -216,39 +222,39 @@ void MixBlendOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = value_m * p.color1[2] + value * p.color2[2]; p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Burn Operation ******** */ -void MixColorBurnOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixColorBurnOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; float tmp; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - tmp = valuem + value * inputColor2[0]; + tmp = valuem + value * input_color2[0]; if (tmp <= 0.0f) { output[0] = 0.0f; } else { - tmp = 1.0f - (1.0f - inputColor1[0]) / tmp; + tmp = 1.0f - (1.0f - input_color1[0]) / tmp; if (tmp < 0.0f) { output[0] = 0.0f; } @@ -260,12 +266,12 @@ void MixColorBurnOperation::executePixelSampled(float output[4], } } - tmp = valuem + value * inputColor2[1]; + tmp = valuem + value * input_color2[1]; if (tmp <= 0.0f) { output[1] = 0.0f; } else { - tmp = 1.0f - (1.0f - inputColor1[1]) / tmp; + tmp = 1.0f - (1.0f - input_color1[1]) / tmp; if (tmp < 0.0f) { output[1] = 0.0f; } @@ -277,12 +283,12 @@ void MixColorBurnOperation::executePixelSampled(float output[4], } } - tmp = valuem + value * inputColor2[2]; + tmp = valuem + value * input_color2[2]; if (tmp <= 0.0f) { output[2] = 0.0f; } else { - tmp = 1.0f - (1.0f - inputColor1[2]) / tmp; + tmp = 1.0f - (1.0f - input_color1[2]) / tmp; if (tmp < 0.0f) { output[2] = 0.0f; } @@ -294,16 +300,16 @@ void MixColorBurnOperation::executePixelSampled(float output[4], } } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixColorBurnOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -336,56 +342,56 @@ void MixColorBurnOperation::update_memory_buffer_row(PixelCursor &p) } p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Color Operation ******** */ -void MixColorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixColorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; float colH, colS, colV; - rgb_to_hsv(inputColor2[0], inputColor2[1], inputColor2[2], &colH, &colS, &colV); + rgb_to_hsv(input_color2[0], input_color2[1], input_color2[2], &colH, &colS, &colV); if (colS != 0.0f) { float rH, rS, rV; float tmpr, tmpg, tmpb; - rgb_to_hsv(inputColor1[0], inputColor1[1], inputColor1[2], &rH, &rS, &rV); + rgb_to_hsv(input_color1[0], input_color1[1], input_color1[2], &rH, &rS, &rV); hsv_to_rgb(colH, colS, rV, &tmpr, &tmpg, &tmpb); - output[0] = (valuem * inputColor1[0]) + (value * tmpr); - output[1] = (valuem * inputColor1[1]) + (value * tmpg); - output[2] = (valuem * inputColor1[2]) + (value * tmpb); + output[0] = (valuem * input_color1[0]) + (value * tmpr); + output[1] = (valuem * input_color1[1]) + (value * tmpg); + output[2] = (valuem * input_color1[2]) + (value * tmpb); } else { - copy_v3_v3(output, inputColor1); + copy_v3_v3(output, input_color1); } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixColorOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -406,44 +412,44 @@ void MixColorOperation::update_memory_buffer_row(PixelCursor &p) } p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Darken Operation ******** */ -void MixDarkenOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixDarkenOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = min_ff(inputColor1[0], inputColor2[0]) * value + inputColor1[0] * valuem; - output[1] = min_ff(inputColor1[1], inputColor2[1]) * value + inputColor1[1] * valuem; - output[2] = min_ff(inputColor1[2], inputColor2[2]) * value + inputColor1[2] * valuem; - output[3] = inputColor1[3]; + output[0] = min_ff(input_color1[0], input_color2[0]) * value + input_color1[0] * valuem; + output[1] = min_ff(input_color1[1], input_color2[1]) * value + input_color1[1] * valuem; + output[2] = min_ff(input_color1[2], input_color2[2]) * value + input_color1[2] * valuem; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixDarkenOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } float value_m = 1.0f - value; @@ -452,44 +458,44 @@ void MixDarkenOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = min_ff(p.color1[2], p.color2[2]) * value + p.color1[2] * value_m; p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Difference Operation ******** */ -void MixDifferenceOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixDifferenceOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = valuem * inputColor1[0] + value * fabsf(inputColor1[0] - inputColor2[0]); - output[1] = valuem * inputColor1[1] + value * fabsf(inputColor1[1] - inputColor2[1]); - output[2] = valuem * inputColor1[2] + value * fabsf(inputColor1[2] - inputColor2[2]); - output[3] = inputColor1[3]; + output[0] = valuem * input_color1[0] + value * fabsf(input_color1[0] - input_color2[0]); + output[1] = valuem * input_color1[1] + value * fabsf(input_color1[1] - input_color2[1]); + output[2] = valuem * input_color1[2] + value * fabsf(input_color1[2] - input_color2[2]); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixDifferenceOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -498,61 +504,61 @@ void MixDifferenceOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = value_m * p.color1[2] + value * fabsf(p.color1[2] - p.color2[2]); p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Difference Operation ******** */ -void MixDivideOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixDivideOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - if (inputColor2[0] != 0.0f) { - output[0] = valuem * (inputColor1[0]) + value * (inputColor1[0]) / inputColor2[0]; + if (input_color2[0] != 0.0f) { + output[0] = valuem * (input_color1[0]) + value * (input_color1[0]) / input_color2[0]; } else { output[0] = 0.0f; } - if (inputColor2[1] != 0.0f) { - output[1] = valuem * (inputColor1[1]) + value * (inputColor1[1]) / inputColor2[1]; + if (input_color2[1] != 0.0f) { + output[1] = valuem * (input_color1[1]) + value * (input_color1[1]) / input_color2[1]; } else { output[1] = 0.0f; } - if (inputColor2[2] != 0.0f) { - output[2] = valuem * (inputColor1[2]) + value * (inputColor1[2]) / inputColor2[2]; + if (input_color2[2] != 0.0f) { + output[2] = valuem * (input_color1[2]) + value * (input_color1[2]) / input_color2[2]; } else { output[2] = 0.0f; } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixDivideOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -578,39 +584,39 @@ void MixDivideOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Dodge Operation ******** */ -void MixDodgeOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixDodgeOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; float tmp; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } - if (inputColor1[0] != 0.0f) { - tmp = 1.0f - value * inputColor2[0]; + if (input_color1[0] != 0.0f) { + tmp = 1.0f - value * input_color2[0]; if (tmp <= 0.0f) { output[0] = 1.0f; } else { - tmp = inputColor1[0] / tmp; + tmp = input_color1[0] / tmp; if (tmp > 1.0f) { output[0] = 1.0f; } @@ -623,13 +629,13 @@ void MixDodgeOperation::executePixelSampled(float output[4], output[0] = 0.0f; } - if (inputColor1[1] != 0.0f) { - tmp = 1.0f - value * inputColor2[1]; + if (input_color1[1] != 0.0f) { + tmp = 1.0f - value * input_color2[1]; if (tmp <= 0.0f) { output[1] = 1.0f; } else { - tmp = inputColor1[1] / tmp; + tmp = input_color1[1] / tmp; if (tmp > 1.0f) { output[1] = 1.0f; } @@ -642,13 +648,13 @@ void MixDodgeOperation::executePixelSampled(float output[4], output[1] = 0.0f; } - if (inputColor1[2] != 0.0f) { - tmp = 1.0f - value * inputColor2[2]; + if (input_color1[2] != 0.0f) { + tmp = 1.0f - value * input_color2[2]; if (tmp <= 0.0f) { output[2] = 1.0f; } else { - tmp = inputColor1[2] / tmp; + tmp = input_color1[2] / tmp; if (tmp > 1.0f) { output[2] = 1.0f; } @@ -661,16 +667,16 @@ void MixDodgeOperation::executePixelSampled(float output[4], output[2] = 0.0f; } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixDodgeOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } @@ -719,27 +725,27 @@ void MixDodgeOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Glare Operation ******** */ -void MixGlareOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixGlareOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; float value, input_weight, glare_weight; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); - value = inputValue[0]; + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); + value = input_value[0]; /* Linear interpolation between 3 cases: * value=-1:output=input value=0:output=input+glare value=1:output=glare */ @@ -751,12 +757,12 @@ void MixGlareOperation::executePixelSampled(float output[4], input_weight = 1.0f - value; glare_weight = 1.0f; } - output[0] = input_weight * MAX2(inputColor1[0], 0.0f) + glare_weight * inputColor2[0]; - output[1] = input_weight * MAX2(inputColor1[1], 0.0f) + glare_weight * inputColor2[1]; - output[2] = input_weight * MAX2(inputColor1[2], 0.0f) + glare_weight * inputColor2[2]; - output[3] = inputColor1[3]; + output[0] = input_weight * MAX2(input_color1[0], 0.0f) + glare_weight * input_color2[0]; + output[1] = input_weight * MAX2(input_color1[1], 0.0f) + glare_weight * input_color2[1]; + output[2] = input_weight * MAX2(input_color1[2], 0.0f) + glare_weight * input_color2[2]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixGlareOperation::update_memory_buffer_row(PixelCursor &p) @@ -781,53 +787,56 @@ void MixGlareOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = input_weight * MAX2(p.color1[2], 0.0f) + glare_weight * p.color2[2]; p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Hue Operation ******** */ -void MixHueOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void MixHueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; float colH, colS, colV; - rgb_to_hsv(inputColor2[0], inputColor2[1], inputColor2[2], &colH, &colS, &colV); + rgb_to_hsv(input_color2[0], input_color2[1], input_color2[2], &colH, &colS, &colV); if (colS != 0.0f) { float rH, rS, rV; float tmpr, tmpg, tmpb; - rgb_to_hsv(inputColor1[0], inputColor1[1], inputColor1[2], &rH, &rS, &rV); + rgb_to_hsv(input_color1[0], input_color1[1], input_color1[2], &rH, &rS, &rV); hsv_to_rgb(colH, rS, rV, &tmpr, &tmpg, &tmpb); - output[0] = valuem * (inputColor1[0]) + value * tmpr; - output[1] = valuem * (inputColor1[1]) + value * tmpg; - output[2] = valuem * (inputColor1[2]) + value * tmpb; + output[0] = valuem * (input_color1[0]) + value * tmpr; + output[1] = valuem * (input_color1[1]) + value * tmpg; + output[2] = valuem * (input_color1[2]) + value * tmpb; } else { - copy_v3_v3(output, inputColor1); + copy_v3_v3(output, input_color1); } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixHueOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -848,62 +857,62 @@ void MixHueOperation::update_memory_buffer_row(PixelCursor &p) } p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Lighten Operation ******** */ -void MixLightenOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixLightenOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float tmp; - tmp = value * inputColor2[0]; - if (tmp > inputColor1[0]) { + tmp = value * input_color2[0]; + if (tmp > input_color1[0]) { output[0] = tmp; } else { - output[0] = inputColor1[0]; + output[0] = input_color1[0]; } - tmp = value * inputColor2[1]; - if (tmp > inputColor1[1]) { + tmp = value * input_color2[1]; + if (tmp > input_color1[1]) { output[1] = tmp; } else { - output[1] = inputColor1[1]; + output[1] = input_color1[1]; } - tmp = value * inputColor2[2]; - if (tmp > inputColor1[2]) { + tmp = value * input_color2[2]; + if (tmp > input_color1[2]) { output[2] = tmp; } else { - output[2] = inputColor1[2]; + output[2] = input_color1[2]; } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixLightenOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } @@ -918,59 +927,59 @@ void MixLightenOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Linear Light Operation ******** */ -void MixLinearLightOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixLinearLightOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } - if (inputColor2[0] > 0.5f) { - output[0] = inputColor1[0] + value * (2.0f * (inputColor2[0] - 0.5f)); + if (input_color2[0] > 0.5f) { + output[0] = input_color1[0] + value * (2.0f * (input_color2[0] - 0.5f)); } else { - output[0] = inputColor1[0] + value * (2.0f * (inputColor2[0]) - 1.0f); + output[0] = input_color1[0] + value * (2.0f * (input_color2[0]) - 1.0f); } - if (inputColor2[1] > 0.5f) { - output[1] = inputColor1[1] + value * (2.0f * (inputColor2[1] - 0.5f)); + if (input_color2[1] > 0.5f) { + output[1] = input_color1[1] + value * (2.0f * (input_color2[1] - 0.5f)); } else { - output[1] = inputColor1[1] + value * (2.0f * (inputColor2[1]) - 1.0f); + output[1] = input_color1[1] + value * (2.0f * (input_color2[1]) - 1.0f); } - if (inputColor2[2] > 0.5f) { - output[2] = inputColor1[2] + value * (2.0f * (inputColor2[2] - 0.5f)); + if (input_color2[2] > 0.5f) { + output[2] = input_color1[2] + value * (2.0f * (input_color2[2] - 0.5f)); } else { - output[2] = inputColor1[2] + value * (2.0f * (inputColor2[2]) - 1.0f); + output[2] = input_color1[2] + value * (2.0f * (input_color2[2]) - 1.0f); } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixLinearLightOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } if (p.color2[0] > 0.5f) { @@ -994,44 +1003,44 @@ void MixLinearLightOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Multiply Operation ******** */ -void MixMultiplyOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixMultiplyOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = inputColor1[0] * (valuem + value * inputColor2[0]); - output[1] = inputColor1[1] * (valuem + value * inputColor2[1]); - output[2] = inputColor1[2] * (valuem + value * inputColor2[2]); - output[3] = inputColor1[3]; + output[0] = input_color1[0] * (valuem + value * input_color2[0]); + output[1] = input_color1[1] * (valuem + value * input_color2[1]); + output[2] = input_color1[2] * (valuem + value * input_color2[2]); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixMultiplyOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -1041,61 +1050,64 @@ void MixMultiplyOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Overlay Operation ******** */ -void MixOverlayOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixOverlayOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - if (inputColor1[0] < 0.5f) { - output[0] = inputColor1[0] * (valuem + 2.0f * value * inputColor2[0]); + if (input_color1[0] < 0.5f) { + output[0] = input_color1[0] * (valuem + 2.0f * value * input_color2[0]); } else { - output[0] = 1.0f - (valuem + 2.0f * value * (1.0f - inputColor2[0])) * (1.0f - inputColor1[0]); + output[0] = 1.0f - + (valuem + 2.0f * value * (1.0f - input_color2[0])) * (1.0f - input_color1[0]); } - if (inputColor1[1] < 0.5f) { - output[1] = inputColor1[1] * (valuem + 2.0f * value * inputColor2[1]); + if (input_color1[1] < 0.5f) { + output[1] = input_color1[1] * (valuem + 2.0f * value * input_color2[1]); } else { - output[1] = 1.0f - (valuem + 2.0f * value * (1.0f - inputColor2[1])) * (1.0f - inputColor1[1]); + output[1] = 1.0f - + (valuem + 2.0f * value * (1.0f - input_color2[1])) * (1.0f - input_color1[1]); } - if (inputColor1[2] < 0.5f) { - output[2] = inputColor1[2] * (valuem + 2.0f * value * inputColor2[2]); + if (input_color1[2] < 0.5f) { + output[2] = input_color1[2] * (valuem + 2.0f * value * input_color2[2]); } else { - output[2] = 1.0f - (valuem + 2.0f * value * (1.0f - inputColor2[2])) * (1.0f - inputColor1[2]); + output[2] = 1.0f - + (valuem + 2.0f * value * (1.0f - input_color2[2])) * (1.0f - input_color1[2]); } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixOverlayOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -1120,53 +1132,53 @@ void MixOverlayOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Saturation Operation ******** */ -void MixSaturationOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixSaturationOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; float rH, rS, rV; - rgb_to_hsv(inputColor1[0], inputColor1[1], inputColor1[2], &rH, &rS, &rV); + rgb_to_hsv(input_color1[0], input_color1[1], input_color1[2], &rH, &rS, &rV); if (rS != 0.0f) { float colH, colS, colV; - rgb_to_hsv(inputColor2[0], inputColor2[1], inputColor2[2], &colH, &colS, &colV); + rgb_to_hsv(input_color2[0], input_color2[1], input_color2[2], &colH, &colS, &colV); hsv_to_rgb(rH, (valuem * rS + value * colS), rV, &output[0], &output[1], &output[2]); } else { - copy_v3_v3(output, inputColor1); + copy_v3_v3(output, input_color1); } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixSaturationOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -1184,45 +1196,45 @@ void MixSaturationOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Screen Operation ******** */ -void MixScreenOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixScreenOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = 1.0f - (valuem + value * (1.0f - inputColor2[0])) * (1.0f - inputColor1[0]); - output[1] = 1.0f - (valuem + value * (1.0f - inputColor2[1])) * (1.0f - inputColor1[1]); - output[2] = 1.0f - (valuem + value * (1.0f - inputColor2[2])) * (1.0f - inputColor1[2]); - output[3] = inputColor1[3]; + output[0] = 1.0f - (valuem + value * (1.0f - input_color2[0])) * (1.0f - input_color1[0]); + output[1] = 1.0f - (valuem + value * (1.0f - input_color2[1])) * (1.0f - input_color1[1]); + output[2] = 1.0f - (valuem + value * (1.0f - input_color2[2])) * (1.0f - input_color1[2]); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixScreenOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -1232,57 +1244,57 @@ void MixScreenOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = 1.0f - (value_m + value * (1.0f - p.color2[2])) * (1.0f - p.color1[2]); p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Soft Light Operation ******** */ -void MixSoftLightOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixSoftLightOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; float scr, scg, scb; /* first calculate non-fac based Screen mix */ - scr = 1.0f - (1.0f - inputColor2[0]) * (1.0f - inputColor1[0]); - scg = 1.0f - (1.0f - inputColor2[1]) * (1.0f - inputColor1[1]); - scb = 1.0f - (1.0f - inputColor2[2]) * (1.0f - inputColor1[2]); + scr = 1.0f - (1.0f - input_color2[0]) * (1.0f - input_color1[0]); + scg = 1.0f - (1.0f - input_color2[1]) * (1.0f - input_color1[1]); + scb = 1.0f - (1.0f - input_color2[2]) * (1.0f - input_color1[2]); - output[0] = valuem * (inputColor1[0]) + - value * (((1.0f - inputColor1[0]) * inputColor2[0] * (inputColor1[0])) + - (inputColor1[0] * scr)); - output[1] = valuem * (inputColor1[1]) + - value * (((1.0f - inputColor1[1]) * inputColor2[1] * (inputColor1[1])) + - (inputColor1[1] * scg)); - output[2] = valuem * (inputColor1[2]) + - value * (((1.0f - inputColor1[2]) * inputColor2[2] * (inputColor1[2])) + - (inputColor1[2] * scb)); - output[3] = inputColor1[3]; + output[0] = valuem * (input_color1[0]) + + value * (((1.0f - input_color1[0]) * input_color2[0] * (input_color1[0])) + + (input_color1[0] * scr)); + output[1] = valuem * (input_color1[1]) + + value * (((1.0f - input_color1[1]) * input_color2[1] * (input_color1[1])) + + (input_color1[1] * scg)); + output[2] = valuem * (input_color1[2]) + + value * (((1.0f - input_color1[2]) * input_color2[2] * (input_color1[2])) + + (input_color1[2] * scb)); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixSoftLightOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -1301,43 +1313,43 @@ void MixSoftLightOperation::update_memory_buffer_row(PixelCursor &p) value * ((1.0f - p.color1[2]) * p.color2[2] * p.color1[2] + p.color1[2] * scb); p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Subtract Operation ******** */ -void MixSubtractOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixSubtractOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } - output[0] = inputColor1[0] - value * (inputColor2[0]); - output[1] = inputColor1[1] - value * (inputColor2[1]); - output[2] = inputColor1[2] - value * (inputColor2[2]); - output[3] = inputColor1[3]; + output[0] = input_color1[0] - value * (input_color2[0]); + output[1] = input_color1[1] - value * (input_color2[1]); + output[2] = input_color1[2] - value * (input_color2[2]); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixSubtractOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } p.out[0] = p.color1[0] - value * p.color2[0]; @@ -1345,47 +1357,47 @@ void MixSubtractOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = p.color1[2] - value * p.color2[2]; p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Value Operation ******** */ -void MixValueOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixValueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - inputValueOperation_->readSampled(inputValue, x, y, sampler); - inputColor1Operation_->readSampled(inputColor1, x, y, sampler); - inputColor2Operation_->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; float rH, rS, rV; float colH, colS, colV; - rgb_to_hsv(inputColor1[0], inputColor1[1], inputColor1[2], &rH, &rS, &rV); - rgb_to_hsv(inputColor2[0], inputColor2[1], inputColor2[2], &colH, &colS, &colV); + rgb_to_hsv(input_color1[0], input_color1[1], input_color1[2], &rH, &rS, &rV); + rgb_to_hsv(input_color2[0], input_color2[1], input_color2[2], &colH, &colS, &colV); hsv_to_rgb(rH, rS, (valuem * rV + value * colV), &output[0], &output[1], &output[2]); - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixValueOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } float value_m = 1.0f - value; @@ -1397,7 +1409,7 @@ void MixValueOperation::update_memory_buffer_row(PixelCursor &p) hsv_to_rgb(rH, rS, (value_m * rV + value * colV), &p.out[0], &p.out[1], &p.out[2]); p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h index 0efc98617a6..bf5639f43a3 100644 --- a/source/blender/compositor/operations/COM_MixOperation.h +++ b/source/blender/compositor/operations/COM_MixOperation.h @@ -51,17 +51,17 @@ class MixBaseOperation : public MultiThreadedOperation { }; /** - * Prefetched reference to the inputProgram + * Prefetched reference to the input_program */ - SocketReader *inputValueOperation_; - SocketReader *inputColor1Operation_; - SocketReader *inputColor2Operation_; - bool valueAlphaMultiply_; - bool useClamp_; + SocketReader *input_value_operation_; + SocketReader *input_color1_operation_; + SocketReader *input_color2_operation_; + bool value_alpha_multiply_; + bool use_clamp_; - inline void clampIfNeeded(float color[4]) + inline void clamp_if_needed(float color[4]) { - if (useClamp_) { + if (use_clamp_) { clamp_v4(color, 0.0f, 1.0f); } } @@ -75,31 +75,31 @@ class MixBaseOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void setUseValueAlphaMultiply(const bool value) + void set_use_value_alpha_multiply(const bool value) { - valueAlphaMultiply_ = value; + value_alpha_multiply_ = value; } - inline bool useValueAlphaMultiply() + inline bool use_value_alpha_multiply() { - return valueAlphaMultiply_; + return value_alpha_multiply_; } - void setUseClamp(bool value) + void set_use_clamp(bool value) { - useClamp_ = value; + use_clamp_ = value; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -112,7 +112,7 @@ class MixBaseOperation : public MultiThreadedOperation { class MixAddOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -120,7 +120,7 @@ class MixAddOperation : public MixBaseOperation { class MixBlendOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -128,7 +128,7 @@ class MixBlendOperation : public MixBaseOperation { class MixColorBurnOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -136,7 +136,7 @@ class MixColorBurnOperation : public MixBaseOperation { class MixColorOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -144,7 +144,7 @@ class MixColorOperation : public MixBaseOperation { class MixDarkenOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -152,7 +152,7 @@ class MixDarkenOperation : public MixBaseOperation { class MixDifferenceOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -160,7 +160,7 @@ class MixDifferenceOperation : public MixBaseOperation { class MixDivideOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -168,7 +168,7 @@ class MixDivideOperation : public MixBaseOperation { class MixDodgeOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -176,7 +176,7 @@ class MixDodgeOperation : public MixBaseOperation { class MixGlareOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -184,7 +184,7 @@ class MixGlareOperation : public MixBaseOperation { class MixHueOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -192,7 +192,7 @@ class MixHueOperation : public MixBaseOperation { class MixLightenOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -200,7 +200,7 @@ class MixLightenOperation : public MixBaseOperation { class MixLinearLightOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -208,7 +208,7 @@ class MixLinearLightOperation : public MixBaseOperation { class MixMultiplyOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -216,7 +216,7 @@ class MixMultiplyOperation : public MixBaseOperation { class MixOverlayOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -224,7 +224,7 @@ class MixOverlayOperation : public MixBaseOperation { class MixSaturationOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -232,7 +232,7 @@ class MixSaturationOperation : public MixBaseOperation { class MixScreenOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -240,7 +240,7 @@ class MixScreenOperation : public MixBaseOperation { class MixSoftLightOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -248,7 +248,7 @@ class MixSoftLightOperation : public MixBaseOperation { class MixSubtractOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -256,7 +256,7 @@ class MixSubtractOperation : public MixBaseOperation { class MixValueOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc index c43a552f1ce..1765012d873 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc @@ -25,7 +25,7 @@ namespace blender::compositor { MovieClipAttributeOperation::MovieClipAttributeOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); framenumber_ = 0; attribute_ = MCA_X; invert_ = false; @@ -34,7 +34,7 @@ MovieClipAttributeOperation::MovieClipAttributeOperation() stabilization_resolution_socket_ = nullptr; } -void MovieClipAttributeOperation::initExecution() +void MovieClipAttributeOperation::init_execution() { if (!is_value_calculated_) { calc_value(); @@ -56,12 +56,12 @@ void MovieClipAttributeOperation::calc_value() int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip_, framenumber_); NodeOperation &stabilization_operation = stabilization_resolution_socket_ ? - stabilization_resolution_socket_->getLink()->getOperation() : + stabilization_resolution_socket_->get_link()->get_operation() : *this; BKE_tracking_stabilization_data_get(clip_, clip_framenr, - stabilization_operation.getWidth(), - stabilization_operation.getHeight(), + stabilization_operation.get_width(), + stabilization_operation.get_height(), loc, &scale, &angle); @@ -89,10 +89,10 @@ void MovieClipAttributeOperation::calc_value() } } -void MovieClipAttributeOperation::executePixelSampled(float output[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) +void MovieClipAttributeOperation::execute_pixel_sampled(float output[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { output[0] = value_; } diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h index 2df2b199107..9f333189811 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h @@ -50,29 +50,29 @@ class MovieClipAttributeOperation : public ConstantOperation { */ MovieClipAttributeOperation(); - void initExecution() override; + void init_execution() override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; const float *get_constant_elem() override; - void setMovieClip(MovieClip *clip) + void set_movie_clip(MovieClip *clip) { clip_ = clip; } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { framenumber_ = framenumber; } - void setAttribute(MovieClipAttribute attribute) + void set_attribute(MovieClipAttribute attribute) { attribute_ = attribute; } - void setInvert(bool invert) + void set_invert(bool invert) { invert_ = invert; } diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc index 2803c707371..a73b408e3ff 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc @@ -27,30 +27,30 @@ namespace blender::compositor { MovieClipBaseOperation::MovieClipBaseOperation() { - movieClip_ = nullptr; - movieClipBuffer_ = nullptr; - movieClipUser_ = nullptr; - movieClipwidth_ = 0; - movieClipheight_ = 0; + movie_clip_ = nullptr; + movie_clip_buffer_ = nullptr; + movie_clip_user_ = nullptr; + movie_clipwidth_ = 0; + movie_clipheight_ = 0; framenumber_ = 0; } -void MovieClipBaseOperation::initExecution() +void MovieClipBaseOperation::init_execution() { - if (movieClip_) { - BKE_movieclip_user_set_frame(movieClipUser_, framenumber_); + if (movie_clip_) { + BKE_movieclip_user_set_frame(movie_clip_user_, framenumber_); ImBuf *ibuf; - if (cacheFrame_) { - ibuf = BKE_movieclip_get_ibuf(movieClip_, movieClipUser_); + if (cache_frame_) { + ibuf = BKE_movieclip_get_ibuf(movie_clip_, movie_clip_user_); } else { ibuf = BKE_movieclip_get_ibuf_flag( - movieClip_, movieClipUser_, movieClip_->flag, MOVIECLIP_CACHE_SKIP); + movie_clip_, movie_clip_user_, movie_clip_->flag, MOVIECLIP_CACHE_SKIP); } if (ibuf) { - movieClipBuffer_ = ibuf; + movie_clip_buffer_ = ibuf; if (ibuf->rect_float == nullptr || ibuf->userflags & IB_RECT_INVALID) { IMB_float_from_rect(ibuf); ibuf->userflags &= ~IB_RECT_INVALID; @@ -59,31 +59,31 @@ void MovieClipBaseOperation::initExecution() } } -void MovieClipBaseOperation::deinitExecution() +void MovieClipBaseOperation::deinit_execution() { - if (movieClipBuffer_) { - IMB_freeImBuf(movieClipBuffer_); + if (movie_clip_buffer_) { + IMB_freeImBuf(movie_clip_buffer_); - movieClipBuffer_ = nullptr; + movie_clip_buffer_ = nullptr; } } void MovieClipBaseOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { r_area = COM_AREA_NONE; - if (movieClip_) { + if (movie_clip_) { int width, height; - BKE_movieclip_get_size(movieClip_, movieClipUser_, &width, &height); + BKE_movieclip_get_size(movie_clip_, movie_clip_user_, &width, &height); BLI_rcti_init(&r_area, 0, width, 0, height); } } -void MovieClipBaseOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MovieClipBaseOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - ImBuf *ibuf = movieClipBuffer_; + ImBuf *ibuf = movie_clip_buffer_; if (ibuf == nullptr) { zero_v4(output); @@ -111,8 +111,8 @@ void MovieClipBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - if (movieClipBuffer_) { - output->copy_from(movieClipBuffer_, area); + if (movie_clip_buffer_) { + output->copy_from(movie_clip_buffer_, area); } else { output->fill(area, COM_COLOR_TRANSPARENT); @@ -121,21 +121,21 @@ void MovieClipBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, MovieClipOperation::MovieClipOperation() : MovieClipBaseOperation() { - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); } MovieClipAlphaOperation::MovieClipAlphaOperation() : MovieClipBaseOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); } -void MovieClipAlphaOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MovieClipAlphaOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float result[4]; - MovieClipBaseOperation::executePixelSampled(result, x, y, sampler); + MovieClipBaseOperation::execute_pixel_sampled(result, x, y, sampler); output[0] = result[3]; } @@ -143,8 +143,8 @@ void MovieClipAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - if (movieClipBuffer_) { - output->copy_from(movieClipBuffer_, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); + if (movie_clip_buffer_) { + output->copy_from(movie_clip_buffer_, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); } else { output->fill(area, COM_VALUE_ZERO); diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.h b/source/blender/compositor/operations/COM_MovieClipOperation.h index 58cebba45d7..e576917df1b 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipOperation.h @@ -30,13 +30,13 @@ namespace blender::compositor { */ class MovieClipBaseOperation : public MultiThreadedOperation { protected: - MovieClip *movieClip_; - MovieClipUser *movieClipUser_; - ImBuf *movieClipBuffer_; - int movieClipheight_; - int movieClipwidth_; + MovieClip *movie_clip_; + MovieClipUser *movie_clip_user_; + ImBuf *movie_clip_buffer_; + int movie_clipheight_; + int movie_clipwidth_; int framenumber_; - bool cacheFrame_; + bool cache_frame_; /** * Determine the output resolution. The resolution is retrieved from the Renderer @@ -46,26 +46,26 @@ class MovieClipBaseOperation : public MultiThreadedOperation { public: MovieClipBaseOperation(); - void initExecution() override; - void deinitExecution() override; - void setMovieClip(MovieClip *image) + void init_execution() override; + void deinit_execution() override; + void set_movie_clip(MovieClip *image) { - movieClip_ = image; + movie_clip_ = image; } - void setMovieClipUser(MovieClipUser *imageuser) + void set_movie_clip_user(MovieClipUser *imageuser) { - movieClipUser_ = imageuser; + movie_clip_user_ = imageuser; } - void setCacheFrame(bool value) + void set_cache_frame(bool value) { - cacheFrame_ = value; + cache_frame_ = value; } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { framenumber_ = framenumber; } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -80,7 +80,7 @@ class MovieClipOperation : public MovieClipBaseOperation { class MovieClipAlphaOperation : public MovieClipBaseOperation { public: MovieClipAlphaOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc index 0cb0742dbee..d04a970bc03 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc @@ -24,31 +24,31 @@ namespace blender::compositor { MovieDistortionOperation::MovieDistortionOperation(bool distortion) { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); - inputOperation_ = nullptr; - movieClip_ = nullptr; + input_operation_ = nullptr; + movie_clip_ = nullptr; apply_ = distortion; } void MovieDistortionOperation::init_data() { - if (movieClip_) { - MovieTracking *tracking = &movieClip_->tracking; - MovieClipUser clipUser = {0}; + if (movie_clip_) { + MovieTracking *tracking = &movie_clip_->tracking; + MovieClipUser clip_user = {0}; int calibration_width, calibration_height; - BKE_movieclip_user_set_frame(&clipUser, framenumber_); - BKE_movieclip_get_size(movieClip_, &clipUser, &calibration_width, &calibration_height); + BKE_movieclip_user_set_frame(&clip_user, framenumber_); + BKE_movieclip_get_size(movie_clip_, &clip_user, &calibration_width, &calibration_height); float delta[2]; rcti full_frame; full_frame.xmin = full_frame.ymin = 0; - full_frame.xmax = this->getWidth(); - full_frame.ymax = this->getHeight(); + full_frame.xmax = this->get_width(); + full_frame.ymax = this->get_height(); BKE_tracking_max_distortion_delta_across_bound( - tracking, this->getWidth(), this->getHeight(), &full_frame, !apply_, delta); + tracking, this->get_width(), this->get_height(), &full_frame, !apply_, delta); /* 5 is just in case we didn't hit real max of distortion in * BKE_tracking_max_undistortion_delta_across_bound @@ -65,11 +65,11 @@ void MovieDistortionOperation::init_data() } } -void MovieDistortionOperation::initExecution() +void MovieDistortionOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); - if (movieClip_) { - MovieTracking *tracking = &movieClip_->tracking; + input_operation_ = this->get_input_socket_reader(0); + if (movie_clip_) { + MovieTracking *tracking = &movie_clip_->tracking; distortion_ = BKE_tracking_distortion_new(tracking, calibration_width_, calibration_height_); } else { @@ -77,25 +77,25 @@ void MovieDistortionOperation::initExecution() } } -void MovieDistortionOperation::deinitExecution() +void MovieDistortionOperation::deinit_execution() { - inputOperation_ = nullptr; - movieClip_ = nullptr; + input_operation_ = nullptr; + movie_clip_ = nullptr; if (distortion_ != nullptr) { BKE_tracking_distortion_free(distortion_); } } -void MovieDistortionOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void MovieDistortionOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { if (distortion_ != nullptr) { /* float overscan = 0.0f; */ const float pixel_aspect = pixel_aspect_; - const float w = (float)this->getWidth() /* / (1 + overscan) */; - const float h = (float)this->getHeight() /* / (1 + overscan) */; + const float w = (float)this->get_width() /* / (1 + overscan) */; + const float h = (float)this->get_height() /* / (1 + overscan) */; const float aspx = w / (float)calibration_width_; const float aspy = h / (float)calibration_height_; float in[2]; @@ -114,23 +114,22 @@ void MovieDistortionOperation::executePixelSampled(float output[4], float u = out[0] * aspx /* + 0.5 * overscan * w */, v = (out[1] * aspy /* + 0.5 * overscan * h */) * pixel_aspect; - inputOperation_->readSampled(output, u, v, PixelSampler::Bilinear); + input_operation_->read_sampled(output, u, v, PixelSampler::Bilinear); } else { - inputOperation_->readSampled(output, x, y, PixelSampler::Bilinear); + input_operation_->read_sampled(output, x, y, PixelSampler::Bilinear); } } -bool MovieDistortionOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool MovieDistortionOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - newInput.xmin = input->xmin - margin_[0]; - newInput.ymin = input->ymin - margin_[1]; - newInput.xmax = input->xmax + margin_[0]; - newInput.ymax = input->ymax + margin_[1]; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + rcti new_input; + new_input.xmin = input->xmin - margin_[0]; + new_input.ymin = input->ymin - margin_[1]; + new_input.xmax = input->xmax + margin_[0]; + new_input.ymax = input->ymax + margin_[1]; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void MovieDistortionOperation::get_area_of_interest(const int input_idx, @@ -157,8 +156,8 @@ void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output /* `float overscan = 0.0f;` */ const float pixel_aspect = pixel_aspect_; - const float w = (float)this->getWidth() /* `/ (1 + overscan)` */; - const float h = (float)this->getHeight() /* `/ (1 + overscan)` */; + const float w = (float)this->get_width() /* `/ (1 + overscan)` */; + const float h = (float)this->get_height() /* `/ (1 + overscan)` */; const float aspx = w / (float)calibration_width_; const float aspy = h / (float)calibration_height_; float xy[2]; diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h index 3e728962bc3..cf430c74f30 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h @@ -28,8 +28,8 @@ namespace blender::compositor { class MovieDistortionOperation : public MultiThreadedOperation { private: - SocketReader *inputOperation_; - MovieClip *movieClip_; + SocketReader *input_operation_; + MovieClip *movie_clip_; int margin_[2]; protected: @@ -42,23 +42,23 @@ class MovieDistortionOperation : public MultiThreadedOperation { public: MovieDistortionOperation(bool distortion); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void init_data() override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setMovieClip(MovieClip *clip) + void set_movie_clip(MovieClip *clip) { - movieClip_ = clip; + movie_clip_ = clip; } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { framenumber_ = framenumber; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) 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, diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc index 0209ea0e18c..5549cd1e937 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc @@ -26,27 +26,27 @@ MultilayerBaseOperation::MultilayerBaseOperation(RenderLayer *render_layer, RenderPass *render_pass, int view) { - passId_ = BLI_findindex(&render_layer->passes, render_pass); + pass_id_ = BLI_findindex(&render_layer->passes, render_pass); view_ = view; - renderLayer_ = render_layer; - renderPass_ = render_pass; + render_layer_ = render_layer; + render_pass_ = render_pass; } -ImBuf *MultilayerBaseOperation::getImBuf() +ImBuf *MultilayerBaseOperation::get_im_buf() { /* temporarily changes the view to get the right ImBuf */ - int view = imageUser_->view; + int view = image_user_->view; - imageUser_->view = view_; - imageUser_->pass = passId_; + image_user_->view = view_; + image_user_->pass = pass_id_; - if (BKE_image_multilayer_index(image_->rr, imageUser_)) { - ImBuf *ibuf = BaseImageOperation::getImBuf(); - imageUser_->view = view; + if (BKE_image_multilayer_index(image_->rr, image_user_)) { + ImBuf *ibuf = BaseImageOperation::get_im_buf(); + image_user_->view = view; return ibuf; } - imageUser_->view = view; + image_user_->view = view; return nullptr; } @@ -57,14 +57,14 @@ void MultilayerBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, output->copy_from(buffer_, area); } -std::unique_ptr MultilayerColorOperation::getMetaData() +std::unique_ptr MultilayerColorOperation::get_meta_data() { BLI_assert(buffer_); MetaDataExtractCallbackData callback_data = {nullptr}; RenderResult *render_result = image_->rr; if (render_result && render_result->stamp_data) { - RenderLayer *render_layer = renderLayer_; - RenderPass *render_pass = renderPass_; + RenderLayer *render_layer = render_layer_; + RenderPass *render_pass = render_pass_; std::string full_layer_name = std::string(render_layer->name, BLI_strnlen(render_layer->name, sizeof(render_layer->name))) + @@ -72,7 +72,7 @@ std::unique_ptr MultilayerColorOperation::getMetaData() std::string(render_pass->name, BLI_strnlen(render_pass->name, sizeof(render_pass->name))); blender::StringRef cryptomatte_layer_name = blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name(full_layer_name); - callback_data.setCryptomatteKeys(cryptomatte_layer_name); + callback_data.set_cryptomatte_keys(cryptomatte_layer_name); BKE_stamp_info_callback(&callback_data, render_result->stamp_data, @@ -83,16 +83,16 @@ std::unique_ptr MultilayerColorOperation::getMetaData() return std::move(callback_data.meta_data); } -void MultilayerColorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MultilayerColorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - if (imageFloatBuffer_ == nullptr) { + if (image_float_buffer_ == nullptr) { zero_v4(output); } else { - if (numberOfChannels_ == 4) { + if (number_of_channels_ == 4) { switch (sampler) { case PixelSampler::Nearest: nearest_interpolation_color(buffer_, nullptr, output, x, y); @@ -108,58 +108,58 @@ void MultilayerColorOperation::executePixelSampled(float output[4], else { int yi = y; int xi = x; - if (xi < 0 || yi < 0 || (unsigned int)xi >= this->getWidth() || - (unsigned int)yi >= this->getHeight()) { + if (xi < 0 || yi < 0 || (unsigned int)xi >= this->get_width() || + (unsigned int)yi >= this->get_height()) { zero_v4(output); } else { - int offset = (yi * this->getWidth() + xi) * 3; - copy_v3_v3(output, &imageFloatBuffer_[offset]); + int offset = (yi * this->get_width() + xi) * 3; + copy_v3_v3(output, &image_float_buffer_[offset]); } } } } -void MultilayerValueOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void MultilayerValueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - if (imageFloatBuffer_ == nullptr) { + if (image_float_buffer_ == nullptr) { output[0] = 0.0f; } else { int yi = y; int xi = x; - if (xi < 0 || yi < 0 || (unsigned int)xi >= this->getWidth() || - (unsigned int)yi >= this->getHeight()) { + if (xi < 0 || yi < 0 || (unsigned int)xi >= this->get_width() || + (unsigned int)yi >= this->get_height()) { output[0] = 0.0f; } else { - float result = imageFloatBuffer_[yi * this->getWidth() + xi]; + float result = image_float_buffer_[yi * this->get_width() + xi]; output[0] = result; } } } -void MultilayerVectorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void MultilayerVectorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - if (imageFloatBuffer_ == nullptr) { + if (image_float_buffer_ == nullptr) { output[0] = 0.0f; } else { int yi = y; int xi = x; - if (xi < 0 || yi < 0 || (unsigned int)xi >= this->getWidth() || - (unsigned int)yi >= this->getHeight()) { + if (xi < 0 || yi < 0 || (unsigned int)xi >= this->get_width() || + (unsigned int)yi >= this->get_height()) { output[0] = 0.0f; } else { - int offset = (yi * this->getWidth() + xi) * 3; - copy_v3_v3(output, &imageFloatBuffer_[offset]); + int offset = (yi * this->get_width() + xi) * 3; + copy_v3_v3(output, &image_float_buffer_[offset]); } } } diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.h b/source/blender/compositor/operations/COM_MultilayerImageOperation.h index 77805decc1f..ea5b0fb46ea 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.h +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.h @@ -24,13 +24,13 @@ namespace blender::compositor { class MultilayerBaseOperation : public BaseImageOperation { private: - int passId_; + int pass_id_; int view_; protected: - RenderLayer *renderLayer_; - RenderPass *renderPass_; - ImBuf *getImBuf() override; + RenderLayer *render_layer_; + RenderPass *render_pass_; + ImBuf *get_im_buf() override; public: /** @@ -48,10 +48,10 @@ class MultilayerColorOperation : public MultilayerBaseOperation { MultilayerColorOperation(RenderLayer *render_layer, RenderPass *render_pass, int view) : MultilayerBaseOperation(render_layer, render_pass, view) { - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - std::unique_ptr getMetaData() override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + std::unique_ptr get_meta_data() override; }; class MultilayerValueOperation : public MultilayerBaseOperation { @@ -59,9 +59,9 @@ class MultilayerValueOperation : public MultilayerBaseOperation { MultilayerValueOperation(RenderLayer *render_layer, RenderPass *render_pass, int view) : MultilayerBaseOperation(render_layer, render_pass, view) { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MultilayerVectorOperation : public MultilayerBaseOperation { @@ -69,9 +69,9 @@ class MultilayerVectorOperation : public MultilayerBaseOperation { MultilayerVectorOperation(RenderLayer *render_layer, RenderPass *render_pass, int view) : MultilayerBaseOperation(render_layer, render_pass, view) { - this->addOutputSocket(DataType::Vector); + this->add_output_socket(DataType::Vector); } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc index 0c2dc309ede..89793897a58 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.cc +++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc @@ -22,25 +22,25 @@ namespace blender::compositor { NormalizeOperation::NormalizeOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - imageReader_ = nullptr; - cachedInstance_ = nullptr; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + image_reader_ = nullptr; + cached_instance_ = nullptr; this->flags.complex = true; flags.can_be_constant = true; } -void NormalizeOperation::initExecution() +void NormalizeOperation::init_execution() { - imageReader_ = this->getInputSocketReader(0); - NodeOperation::initMutex(); + image_reader_ = this->get_input_socket_reader(0); + NodeOperation::init_mutex(); } -void NormalizeOperation::executePixel(float output[4], int x, int y, void *data) +void NormalizeOperation::execute_pixel(float output[4], int x, int y, void *data) { /* using generic two floats struct to store `x: min`, `y: multiply` */ NodeTwoFloats *minmult = (NodeTwoFloats *)data; - imageReader_->read(output, x, y, nullptr); + image_reader_->read(output, x, y, nullptr); output[0] = (output[0] - minmult->x) * minmult->y; @@ -53,30 +53,30 @@ void NormalizeOperation::executePixel(float output[4], int x, int y, void *data) } } -void NormalizeOperation::deinitExecution() +void NormalizeOperation::deinit_execution() { - imageReader_ = nullptr; - delete cachedInstance_; - cachedInstance_ = nullptr; - NodeOperation::deinitMutex(); + image_reader_ = nullptr; + delete cached_instance_; + cached_instance_ = nullptr; + NodeOperation::deinit_mutex(); } -bool NormalizeOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool NormalizeOperation::determine_depending_area_of_interest(rcti * /*input*/, + ReadBufferOperation *read_operation, + rcti *output) { - rcti imageInput; - if (cachedInstance_) { + rcti image_input; + if (cached_instance_) { return false; } - NodeOperation *operation = getInputOperation(0); - imageInput.xmax = operation->getWidth(); - imageInput.xmin = 0; - imageInput.ymax = operation->getHeight(); - imageInput.ymin = 0; + NodeOperation *operation = get_input_operation(0); + image_input.xmax = operation->get_width(); + image_input.xmin = 0; + image_input.ymax = operation->get_height(); + image_input.ymin = 0; - if (operation->determineDependingAreaOfInterest(&imageInput, readOperation, output)) { + if (operation->determine_depending_area_of_interest(&image_input, read_operation, output)) { return true; } return false; @@ -86,16 +86,16 @@ bool NormalizeOperation::determineDependingAreaOfInterest(rcti * /*input*/, */ #define BLENDER_ZMAX 10000.0f -void *NormalizeOperation::initializeTileData(rcti *rect) +void *NormalizeOperation::initialize_tile_data(rcti *rect) { - lockMutex(); - if (cachedInstance_ == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)imageReader_->initializeTileData(rect); + lock_mutex(); + if (cached_instance_ == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)image_reader_->initialize_tile_data(rect); /* using generic two floats struct to store `x: min`, `y: multiply`. */ NodeTwoFloats *minmult = new NodeTwoFloats(); - float *buffer = tile->getBuffer(); - int p = tile->getWidth() * tile->getHeight(); + float *buffer = tile->get_buffer(); + int p = tile->get_width() * tile->get_height(); float *bc = buffer; float minv = 1.0f + BLENDER_ZMAX; @@ -117,14 +117,14 @@ void *NormalizeOperation::initializeTileData(rcti *rect) /* The rare case of flat buffer would cause a divide by 0 */ minmult->y = ((maxv != minv) ? 1.0f / (maxv - minv) : 0.0f); - cachedInstance_ = minmult; + cached_instance_ = minmult; } - unlockMutex(); - return cachedInstance_; + unlock_mutex(); + return cached_instance_; } -void NormalizeOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) +void NormalizeOperation::deinitialize_tile_data(rcti * /*rect*/, void * /*data*/) { /* pass */ } @@ -140,7 +140,7 @@ void NormalizeOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(outpu const rcti &UNUSED(area), Span inputs) { - if (cachedInstance_ == nullptr) { + if (cached_instance_ == nullptr) { MemoryBuffer *input = inputs[0]; /* Using generic two floats struct to store `x: min`, `y: multiply`. */ @@ -162,7 +162,7 @@ void NormalizeOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(outpu /* The case of a flat buffer would cause a divide by 0. */ minmult->y = ((maxv != minv) ? 1.0f / (maxv - minv) : 0.0f); - cachedInstance_ = minmult; + cached_instance_ = minmult; } } @@ -170,7 +170,7 @@ void NormalizeOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - NodeTwoFloats *minmult = cachedInstance_; + NodeTwoFloats *minmult = cached_instance_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float input_value = *it.in(0); diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.h b/source/blender/compositor/operations/COM_NormalizeOperation.h index 450155c319a..dd9abcfad26 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.h +++ b/source/blender/compositor/operations/COM_NormalizeOperation.h @@ -32,13 +32,13 @@ class NormalizeOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *imageReader_; + SocketReader *image_reader_; /** * \brief temporarily cache of the execution storage * it stores `x->min` and `y->multiply`. */ - NodeTwoFloats *cachedInstance_; + NodeTwoFloats *cached_instance_; public: NormalizeOperation(); @@ -46,24 +46,24 @@ class NormalizeOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; - void deinitializeTileData(rcti *rect, void *data) override; + void *initialize_tile_data(rcti *rect) override; + void deinitialize_tile_data(rcti *rect, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_started(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_OpenCLKernels.cl b/source/blender/compositor/operations/COM_OpenCLKernels.cl index ebe8a6d08ec..d01e209d3e4 100644 --- a/source/blender/compositor/operations/COM_OpenCLKernels.cl +++ b/source/blender/compositor/operations/COM_OpenCLKernels.cl @@ -29,101 +29,101 @@ const sampler_t SAMPLER_NEAREST_CLAMP = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRES __constant const int2 zero = {0,0}; // KERNEL --- BOKEH BLUR --- -__kernel void bokehBlurKernel(__read_only image2d_t boundingBox, __read_only image2d_t inputImage, - __read_only image2d_t bokehImage, __write_only image2d_t output, - int2 offsetInput, int2 offsetOutput, int radius, int step, int2 dimension, int2 offset) +__kernel void bokeh_blur_kernel(__read_only image2d_t bounding_box, __read_only image2d_t input_image, + __read_only image2d_t bokeh_image, __write_only image2d_t output, + int2 offset_input, int2 offset_output, int radius, int step, int2 dimension, int2 offset) { int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - float tempBoundingBox; + float temp_bounding_box; float4 color = {0.0f,0.0f,0.0f,0.0f}; float4 multiplyer = {0.0f,0.0f,0.0f,0.0f}; float4 bokeh; const float radius2 = radius*2.0f; - const int2 realCoordinate = coords + offsetOutput; - int2 imageCoordinates = realCoordinate - offsetInput; + const int2 real_coordinate = coords + offset_output; + int2 image_coordinates = real_coordinate - offset_input; - tempBoundingBox = read_imagef(boundingBox, SAMPLER_NEAREST, coords).s0; + temp_bounding_box = read_imagef(bounding_box, SAMPLER_NEAREST, coords).s0; - if (tempBoundingBox > 0.0f && radius > 0 ) { - const int2 bokehImageDim = get_image_dim(bokehImage); - const int2 bokehImageCenter = bokehImageDim/2; - const int2 minXY = max(realCoordinate - radius, zero); - const int2 maxXY = min(realCoordinate + radius, dimension); + if (temp_bounding_box > 0.0f && radius > 0 ) { + const int2 bokeh_image_dim = get_image_dim(bokeh_image); + const int2 bokeh_image_center = bokeh_image_dim/2; + const int2 minXY = max(real_coordinate - radius, zero); + const int2 maxXY = min(real_coordinate + radius, dimension); int nx, ny; float2 uv; - int2 inputXy; + int2 input_xy; if (radius < 2) { - color = read_imagef(inputImage, SAMPLER_NEAREST, imageCoordinates); + color = read_imagef(input_image, SAMPLER_NEAREST, image_coordinates); multiplyer = (float4)(1.0f, 1.0f, 1.0f, 1.0f); } - for (ny = minXY.y, inputXy.y = ny - offsetInput.y ; ny < maxXY.y ; ny += step, inputXy.y += step) { - uv.y = ((realCoordinate.y-ny)/radius2)*bokehImageDim.y+bokehImageCenter.y; + for (ny = minXY.y, input_xy.y = ny - offset_input.y ; ny < maxXY.y ; ny += step, input_xy.y += step) { + uv.y = ((real_coordinate.y-ny)/radius2)*bokeh_image_dim.y+bokeh_image_center.y; - for (nx = minXY.x, inputXy.x = nx - offsetInput.x; nx < maxXY.x ; nx += step, inputXy.x += step) { - uv.x = ((realCoordinate.x-nx)/radius2)*bokehImageDim.x+bokehImageCenter.x; - bokeh = read_imagef(bokehImage, SAMPLER_NEAREST, uv); - color += bokeh * read_imagef(inputImage, SAMPLER_NEAREST, inputXy); + for (nx = minXY.x, input_xy.x = nx - offset_input.x; nx < maxXY.x ; nx += step, input_xy.x += step) { + uv.x = ((real_coordinate.x-nx)/radius2)*bokeh_image_dim.x+bokeh_image_center.x; + bokeh = read_imagef(bokeh_image, SAMPLER_NEAREST, uv); + color += bokeh * read_imagef(input_image, SAMPLER_NEAREST, input_xy); multiplyer += bokeh; } } color /= multiplyer; } else { - color = read_imagef(inputImage, SAMPLER_NEAREST, imageCoordinates); + color = read_imagef(input_image, SAMPLER_NEAREST, image_coordinates); } write_imagef(output, coords, color); } //KERNEL --- DEFOCUS /VARIABLESIZEBOKEHBLUR --- -__kernel void defocusKernel(__read_only image2d_t inputImage, __read_only image2d_t bokehImage, - __read_only image2d_t inputSize, - __write_only image2d_t output, int2 offsetInput, int2 offsetOutput, - int step, int maxBlurScalar, float threshold, float scalar, int2 dimension, int2 offset) +__kernel void defocus_kernel(__read_only image2d_t input_image, __read_only image2d_t bokeh_image, + __read_only image2d_t input_size, + __write_only image2d_t output, int2 offset_input, int2 offset_output, + int step, int max_blur_scalar, float threshold, float scalar, int2 dimension, int2 offset) { float4 color = {1.0f, 0.0f, 0.0f, 1.0f}; int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; + const int2 real_coordinate = coords + offset_output; - float4 readColor; - float4 tempColor; + float4 read_color; + float4 temp_color; float4 bokeh; float size; float4 multiplier_accum = {1.0f, 1.0f, 1.0f, 1.0f}; float4 color_accum; - int minx = max(realCoordinate.s0 - maxBlurScalar, 0); - int miny = max(realCoordinate.s1 - maxBlurScalar, 0); - int maxx = min(realCoordinate.s0 + maxBlurScalar, dimension.s0); - int maxy = min(realCoordinate.s1 + maxBlurScalar, dimension.s1); + int minx = max(real_coordinate.s0 - max_blur_scalar, 0); + int miny = max(real_coordinate.s1 - max_blur_scalar, 0); + int maxx = min(real_coordinate.s0 + max_blur_scalar, dimension.s0); + int maxy = min(real_coordinate.s1 + max_blur_scalar, dimension.s1); { - int2 inputCoordinate = realCoordinate - offsetInput; - float size_center = read_imagef(inputSize, SAMPLER_NEAREST, inputCoordinate).s0 * scalar; - color_accum = read_imagef(inputImage, SAMPLER_NEAREST, inputCoordinate); - readColor = color_accum; + int2 input_coordinate = real_coordinate - offset_input; + float size_center = read_imagef(input_size, SAMPLER_NEAREST, input_coordinate).s0 * scalar; + color_accum = read_imagef(input_image, SAMPLER_NEAREST, input_coordinate); + read_color = color_accum; if (size_center > threshold) { for (int ny = miny; ny < maxy; ny += step) { - inputCoordinate.s1 = ny - offsetInput.s1; - float dy = ny - realCoordinate.s1; + input_coordinate.s1 = ny - offset_input.s1; + float dy = ny - real_coordinate.s1; for (int nx = minx; nx < maxx; nx += step) { - float dx = nx - realCoordinate.s0; + float dx = nx - real_coordinate.s0; if (dx != 0 || dy != 0) { - inputCoordinate.s0 = nx - offsetInput.s0; - size = min(read_imagef(inputSize, SAMPLER_NEAREST, inputCoordinate).s0 * scalar, size_center); + input_coordinate.s0 = nx - offset_input.s0; + size = min(read_imagef(input_size, SAMPLER_NEAREST, input_coordinate).s0 * scalar, size_center); if (size > threshold) { if (size >= fabs(dx) && size >= fabs(dy)) { float2 uv = {256.0f + dx * 255.0f / size, 256.0f + dy * 255.0f / size}; - bokeh = read_imagef(bokehImage, SAMPLER_NEAREST, uv); - tempColor = read_imagef(inputImage, SAMPLER_NEAREST, inputCoordinate); - color_accum += bokeh * tempColor; + bokeh = read_imagef(bokeh_image, SAMPLER_NEAREST, uv); + temp_color = read_imagef(input_image, SAMPLER_NEAREST, input_coordinate); + color_accum += bokeh * temp_color; multiplier_accum += bokeh; } } @@ -140,7 +140,7 @@ __kernel void defocusKernel(__read_only image2d_t inputImage, __read_only image2 { /* factor from 0-1 */ float fac = (size_center - threshold) / threshold; - color = (readColor * (1.0f - fac)) + (color * fac); + color = (read_color * (1.0f - fac)) + (color * fac); } write_imagef(output, coords, color); @@ -149,28 +149,28 @@ __kernel void defocusKernel(__read_only image2d_t inputImage, __read_only image2 // KERNEL --- DILATE --- -__kernel void dilateKernel(__read_only image2d_t inputImage, __write_only image2d_t output, - int2 offsetInput, int2 offsetOutput, int scope, int distanceSquared, int2 dimension, +__kernel void dilate_kernel(__read_only image2d_t input_image, __write_only image2d_t output, + int2 offset_input, int2 offset_output, int scope, int distance_squared, int2 dimension, int2 offset) { int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; + const int2 real_coordinate = coords + offset_output; - const int2 minXY = max(realCoordinate - scope, zero); - const int2 maxXY = min(realCoordinate + scope, dimension); + const int2 minXY = max(real_coordinate - scope, zero); + const int2 maxXY = min(real_coordinate + scope, dimension); float value = 0.0f; int nx, ny; - int2 inputXy; + int2 input_xy; - for (ny = minXY.y, inputXy.y = ny - offsetInput.y ; ny < maxXY.y ; ny ++, inputXy.y++) { - const float deltaY = (realCoordinate.y - ny); - for (nx = minXY.x, inputXy.x = nx - offsetInput.x; nx < maxXY.x ; nx ++, inputXy.x++) { - const float deltaX = (realCoordinate.x - nx); - const float measuredDistance = deltaX * deltaX + deltaY * deltaY; - if (measuredDistance <= distanceSquared) { - value = max(value, read_imagef(inputImage, SAMPLER_NEAREST, inputXy).s0); + for (ny = minXY.y, input_xy.y = ny - offset_input.y ; ny < maxXY.y ; ny ++, input_xy.y++) { + const float deltaY = (real_coordinate.y - ny); + for (nx = minXY.x, input_xy.x = nx - offset_input.x; nx < maxXY.x ; nx ++, input_xy.x++) { + const float deltaX = (real_coordinate.x - nx); + const float measured_distance = deltaX * deltaX + deltaY * deltaY; + if (measured_distance <= distance_squared) { + value = max(value, read_imagef(input_image, SAMPLER_NEAREST, input_xy).s0); } } } @@ -180,28 +180,28 @@ __kernel void dilateKernel(__read_only image2d_t inputImage, __write_only image } // KERNEL --- DILATE --- -__kernel void erodeKernel(__read_only image2d_t inputImage, __write_only image2d_t output, - int2 offsetInput, int2 offsetOutput, int scope, int distanceSquared, int2 dimension, +__kernel void erode_kernel(__read_only image2d_t input_image, __write_only image2d_t output, + int2 offset_input, int2 offset_output, int scope, int distance_squared, int2 dimension, int2 offset) { int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; + const int2 real_coordinate = coords + offset_output; - const int2 minXY = max(realCoordinate - scope, zero); - const int2 maxXY = min(realCoordinate + scope, dimension); + const int2 minXY = max(real_coordinate - scope, zero); + const int2 maxXY = min(real_coordinate + scope, dimension); float value = 1.0f; int nx, ny; - int2 inputXy; + int2 input_xy; - for (ny = minXY.y, inputXy.y = ny - offsetInput.y ; ny < maxXY.y ; ny ++, inputXy.y++) { - for (nx = minXY.x, inputXy.x = nx - offsetInput.x; nx < maxXY.x ; nx ++, inputXy.x++) { - const float deltaX = (realCoordinate.x - nx); - const float deltaY = (realCoordinate.y - ny); - const float measuredDistance = deltaX * deltaX+deltaY * deltaY; - if (measuredDistance <= distanceSquared) { - value = min(value, read_imagef(inputImage, SAMPLER_NEAREST, inputXy).s0); + for (ny = minXY.y, input_xy.y = ny - offset_input.y ; ny < maxXY.y ; ny ++, input_xy.y++) { + for (nx = minXY.x, input_xy.x = nx - offset_input.x; nx < maxXY.x ; nx ++, input_xy.x++) { + const float deltaX = (real_coordinate.x - nx); + const float deltaY = (real_coordinate.y - ny); + const float measured_distance = deltaX * deltaX+deltaY * deltaY; + if (measured_distance <= distance_squared) { + value = min(value, read_imagef(input_image, SAMPLER_NEAREST, input_xy).s0); } } } @@ -211,34 +211,34 @@ __kernel void erodeKernel(__read_only image2d_t inputImage, __write_only image2 } // KERNEL --- DIRECTIONAL BLUR --- -__kernel void directionalBlurKernel(__read_only image2d_t inputImage, __write_only image2d_t output, - int2 offsetOutput, int iterations, float scale, float rotation, float2 translate, +__kernel void directional_blur_kernel(__read_only image2d_t input_image, __write_only image2d_t output, + int2 offset_output, int iterations, float scale, float rotation, float2 translate, float2 center, int2 offset) { int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; + const int2 real_coordinate = coords + offset_output; float4 col; float2 ltxy = translate; float lsc = scale; float lrot = rotation; - col = read_imagef(inputImage, SAMPLER_NEAREST, realCoordinate); + col = read_imagef(input_image, SAMPLER_NEAREST, real_coordinate); /* blur the image */ for (int i = 0; i < iterations; ++i) { const float cs = cos(lrot), ss = sin(lrot); const float isc = 1.0f / (1.0f + lsc); - const float v = isc * (realCoordinate.s1 - center.s1) + ltxy.s1; - const float u = isc * (realCoordinate.s0 - center.s0) + ltxy.s0; + const float v = isc * (real_coordinate.s1 - center.s1) + ltxy.s1; + const float u = isc * (real_coordinate.s0 - center.s0) + ltxy.s0; float2 uv = { cs * u + ss * v + center.s0, cs * v - ss * u + center.s1 }; - col += read_imagef(inputImage, SAMPLER_NEAREST_CLAMP, uv); + col += read_imagef(input_image, SAMPLER_NEAREST_CLAMP, uv); /* double transformations */ ltxy += translate; @@ -252,10 +252,10 @@ __kernel void directionalBlurKernel(__read_only image2d_t inputImage, __write_o } // KERNEL --- GAUSSIAN BLUR --- -__kernel void gaussianXBlurOperationKernel(__read_only image2d_t inputImage, - int2 offsetInput, +__kernel void gaussian_xblur_operation_kernel(__read_only image2d_t input_image, + int2 offset_input, __write_only image2d_t output, - int2 offsetOutput, + int2 offset_output, int filter_size, int2 dimension, __global float *gausstab, @@ -264,17 +264,17 @@ __kernel void gaussianXBlurOperationKernel(__read_only image2d_t inputImage, float4 color = {0.0f, 0.0f, 0.0f, 0.0f}; int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; - int2 inputCoordinate = realCoordinate - offsetInput; + const int2 real_coordinate = coords + offset_output; + int2 input_coordinate = real_coordinate - offset_input; float weight = 0.0f; - int xmin = max(realCoordinate.x - filter_size, 0) - offsetInput.x; - int xmax = min(realCoordinate.x + filter_size + 1, dimension.x) - offsetInput.x; + int xmin = max(real_coordinate.x - filter_size, 0) - offset_input.x; + int xmax = min(real_coordinate.x + filter_size + 1, dimension.x) - offset_input.x; - for (int nx = xmin, i = max(filter_size - realCoordinate.x, 0); nx < xmax; ++nx, ++i) { + for (int nx = xmin, i = max(filter_size - real_coordinate.x, 0); nx < xmax; ++nx, ++i) { float w = gausstab[i]; - inputCoordinate.x = nx; - color += read_imagef(inputImage, SAMPLER_NEAREST, inputCoordinate) * w; + input_coordinate.x = nx; + color += read_imagef(input_image, SAMPLER_NEAREST, input_coordinate) * w; weight += w; } @@ -283,10 +283,10 @@ __kernel void gaussianXBlurOperationKernel(__read_only image2d_t inputImage, write_imagef(output, coords, color); } -__kernel void gaussianYBlurOperationKernel(__read_only image2d_t inputImage, - int2 offsetInput, +__kernel void gaussian_yblur_operation_kernel(__read_only image2d_t input_image, + int2 offset_input, __write_only image2d_t output, - int2 offsetOutput, + int2 offset_output, int filter_size, int2 dimension, __global float *gausstab, @@ -295,17 +295,17 @@ __kernel void gaussianYBlurOperationKernel(__read_only image2d_t inputImage, float4 color = {0.0f, 0.0f, 0.0f, 0.0f}; int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; - int2 inputCoordinate = realCoordinate - offsetInput; + const int2 real_coordinate = coords + offset_output; + int2 input_coordinate = real_coordinate - offset_input; float weight = 0.0f; - int ymin = max(realCoordinate.y - filter_size, 0) - offsetInput.y; - int ymax = min(realCoordinate.y + filter_size + 1, dimension.y) - offsetInput.y; + int ymin = max(real_coordinate.y - filter_size, 0) - offset_input.y; + int ymax = min(real_coordinate.y + filter_size + 1, dimension.y) - offset_input.y; - for (int ny = ymin, i = max(filter_size - realCoordinate.y, 0); ny < ymax; ++ny, ++i) { + for (int ny = ymin, i = max(filter_size - real_coordinate.y, 0); ny < ymax; ++ny, ++i) { float w = gausstab[i]; - inputCoordinate.y = ny; - color += read_imagef(inputImage, SAMPLER_NEAREST, inputCoordinate) * w; + input_coordinate.y = ny; + color += read_imagef(input_image, SAMPLER_NEAREST, input_coordinate) * w; weight += w; } diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index e652a0be3e6..7f2968a4719 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -36,19 +36,26 @@ OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOpe DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender) - : OutputSingleLayerOperation( - rd, tree, datatype, format, path, viewSettings, displaySettings, viewName, saveAsRender) + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render) + : OutputSingleLayerOperation(rd, + tree, + datatype, + format, + path, + view_settings, + display_settings, + view_name, + save_as_render) { } void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filename) { - size_t width = this->getWidth(); - size_t height = this->getHeight(); + size_t width = this->get_width(); + size_t height = this->get_height(); SceneRenderView *srv; if (width != 0 && height != 0) { @@ -56,7 +63,7 @@ void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filenam exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(rd_, viewName_)) { + if (!BKE_scene_multiview_is_render_view_first(rd_, view_name_)) { return exrhandle; } @@ -87,10 +94,10 @@ void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filenam return nullptr; } -void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution() +void OutputOpenExrSingleLayerMultiViewOperation::deinit_execution() { - unsigned int width = this->getWidth(); - unsigned int height = this->getHeight(); + unsigned int width = this->get_width(); + unsigned int height = this->get_height(); if (width != 0 && height != 0) { void *exrhandle; @@ -109,17 +116,17 @@ void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution() add_exr_channels(exrhandle, nullptr, datatype_, - viewName_, + view_name_, width, format_->depth == R_IMF_CHAN_DEPTH_16, - outputBuffer_); + output_buffer_); /* memory can only be freed after we write all views to the file */ - outputBuffer_ = nullptr; - imageInput_ = nullptr; + output_buffer_ = nullptr; + image_input_ = nullptr; /* ready to close the file */ - if (BKE_scene_multiview_is_render_view_last(rd_, viewName_)) { + if (BKE_scene_multiview_is_render_view_last(rd_, view_name_)) { IMB_exr_write_channels(exrhandle); /* free buffer memory for all the views */ @@ -140,15 +147,15 @@ OutputOpenExrMultiLayerMultiViewOperation::OutputOpenExrMultiLayerMultiViewOpera const char *path, char exr_codec, bool exr_half_float, - const char *viewName) - : OutputOpenExrMultiLayerOperation(scene, rd, tree, path, exr_codec, exr_half_float, viewName) + const char *view_name) + : OutputOpenExrMultiLayerOperation(scene, rd, tree, path, exr_codec, exr_half_float, view_name) { } void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename) { - unsigned int width = this->getWidth(); - unsigned int height = this->getHeight(); + unsigned int width = this->get_width(); + unsigned int height = this->get_height(); if (width != 0 && height != 0) { @@ -158,7 +165,7 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename /* get a new global handle */ exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(rd_, viewName_)) { + if (!BKE_scene_multiview_is_render_view_first(rd_, view_name_)) { return exrhandle; } @@ -187,7 +194,7 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename BLI_make_existing_file(filename); /* prepare the file with all the channels for the header */ - StampData *stamp_data = createStampData(); + StampData *stamp_data = create_stamp_data(); if (!IMB_exr_begin_write(exrhandle, filename, width, height, exr_codec_, stamp_data)) { printf("Error Writing Multilayer Multiview Openexr\n"); IMB_exr_close(exrhandle); @@ -202,10 +209,10 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename return nullptr; } -void OutputOpenExrMultiLayerMultiViewOperation::deinitExecution() +void OutputOpenExrMultiLayerMultiViewOperation::deinit_execution() { - unsigned int width = this->getWidth(); - unsigned int height = this->getHeight(); + unsigned int width = this->get_width(); + unsigned int height = this->get_height(); if (width != 0 && height != 0) { void *exrhandle; @@ -226,20 +233,20 @@ void OutputOpenExrMultiLayerMultiViewOperation::deinitExecution() add_exr_channels(exrhandle, layers_[i].name, layers_[i].datatype, - viewName_, + view_name_, width, exr_half_float_, - layers_[i].outputBuffer); + layers_[i].output_buffer); } for (unsigned int i = 0; i < layers_.size(); i++) { /* memory can only be freed after we write all views to the file */ - layers_[i].outputBuffer = nullptr; - layers_[i].imageInput = nullptr; + layers_[i].output_buffer = nullptr; + layers_[i].image_input = nullptr; } /* ready to close the file */ - if (BKE_scene_multiview_is_render_view_last(rd_, viewName_)) { + if (BKE_scene_multiview_is_render_view_last(rd_, view_name_)) { IMB_exr_write_channels(exrhandle); /* free buffer memory for all the views */ @@ -260,12 +267,19 @@ OutputStereoOperation::OutputStereoOperation(const RenderData *rd, ImageFormatData *format, const char *path, const char *name, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender) - : OutputSingleLayerOperation( - rd, tree, datatype, format, path, viewSettings, displaySettings, viewName, saveAsRender) + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render) + : OutputSingleLayerOperation(rd, + tree, + datatype, + format, + path, + view_settings, + display_settings, + view_name, + save_as_render) { BLI_strncpy(name_, name, sizeof(name_)); channels_ = get_datatype_size(datatype); @@ -273,8 +287,8 @@ OutputStereoOperation::OutputStereoOperation(const RenderData *rd, void *OutputStereoOperation::get_handle(const char *filename) { - size_t width = this->getWidth(); - size_t height = this->getHeight(); + size_t width = this->get_width(); + size_t height = this->get_height(); const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; size_t i; @@ -283,7 +297,7 @@ void *OutputStereoOperation::get_handle(const char *filename) exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(rd_, viewName_)) { + if (!BKE_scene_multiview_is_render_view_first(rd_, view_name_)) { return exrhandle; } @@ -298,32 +312,32 @@ void *OutputStereoOperation::get_handle(const char *filename) return nullptr; } -void OutputStereoOperation::deinitExecution() +void OutputStereoOperation::deinit_execution() { - unsigned int width = this->getWidth(); - unsigned int height = this->getHeight(); + unsigned int width = this->get_width(); + unsigned int height = this->get_height(); if (width != 0 && height != 0) { void *exrhandle; exrhandle = this->get_handle(path_); - float *buf = outputBuffer_; + float *buf = output_buffer_; /* populate single EXR channel with view data */ IMB_exr_add_channel(exrhandle, nullptr, name_, - viewName_, + view_name_, 1, channels_ * width * height, buf, format_->depth == R_IMF_CHAN_DEPTH_16); - imageInput_ = nullptr; - outputBuffer_ = nullptr; + image_input_ = nullptr; + output_buffer_ = nullptr; /* create stereo ibuf */ - if (BKE_scene_multiview_is_render_view_last(rd_, viewName_)) { + if (BKE_scene_multiview_is_render_view_last(rd_, view_name_)) { ImBuf *ibuf[3] = {nullptr}; const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; char filename[FILE_MAX]; @@ -341,7 +355,7 @@ void OutputStereoOperation::deinitExecution() /* do colormanagement in the individual views, so it doesn't need to do in the stereo */ IMB_colormanagement_imbuf_for_write( - ibuf[i], true, false, viewSettings_, displaySettings_, format_); + ibuf[i], true, false, view_settings_, display_settings_, format_); IMB_prepare_write_ImBuf(IMB_isfloat(ibuf[i]), ibuf[i]); } diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h index a6e76e6190e..9f8bf2ed127 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -38,13 +38,13 @@ class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOpera DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender); + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render); void *get_handle(const char *filename); - void deinitExecution() override; + void deinit_execution() override; }; /* Writes inputs into OpenEXR multilayer channels. */ @@ -57,10 +57,10 @@ class OutputOpenExrMultiLayerMultiViewOperation : public OutputOpenExrMultiLayer const char *path, char exr_codec, bool exr_half_float, - const char *viewName); + const char *view_name); void *get_handle(const char *filename); - void deinitExecution() override; + void deinit_execution() override; }; class OutputStereoOperation : public OutputSingleLayerOperation { @@ -75,12 +75,12 @@ class OutputStereoOperation : public OutputSingleLayerOperation { struct ImageFormatData *format, const char *path, const char *name, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender); + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render); void *get_handle(const char *filename); - void deinitExecution() override; + void deinit_execution() override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 6a5ddd969b4..77e291e9dc4 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -37,9 +37,9 @@ namespace blender::compositor { void add_exr_channels(void *exrhandle, - const char *layerName, + const char *layer_name, const DataType datatype, - const char *viewName, + const char *view_name, const size_t width, bool use_half_float, float *buf) @@ -48,51 +48,63 @@ void add_exr_channels(void *exrhandle, switch (datatype) { case DataType::Value: IMB_exr_add_channel( - exrhandle, layerName, "V", viewName, 1, width, buf ? buf : nullptr, use_half_float); + exrhandle, layer_name, "V", view_name, 1, width, buf ? buf : nullptr, use_half_float); break; case DataType::Vector: - IMB_exr_add_channel( - exrhandle, layerName, "X", viewName, 3, 3 * width, buf ? buf : nullptr, use_half_float); IMB_exr_add_channel(exrhandle, - layerName, + layer_name, + "X", + view_name, + 3, + 3 * width, + buf ? buf : nullptr, + use_half_float); + IMB_exr_add_channel(exrhandle, + layer_name, "Y", - viewName, + view_name, 3, 3 * width, buf ? buf + 1 : nullptr, use_half_float); IMB_exr_add_channel(exrhandle, - layerName, + layer_name, "Z", - viewName, + view_name, 3, 3 * width, buf ? buf + 2 : nullptr, use_half_float); break; case DataType::Color: - IMB_exr_add_channel( - exrhandle, layerName, "R", viewName, 4, 4 * width, buf ? buf : nullptr, use_half_float); IMB_exr_add_channel(exrhandle, - layerName, + layer_name, + "R", + view_name, + 4, + 4 * width, + buf ? buf : nullptr, + use_half_float); + IMB_exr_add_channel(exrhandle, + layer_name, "G", - viewName, + view_name, 4, 4 * width, buf ? buf + 1 : nullptr, use_half_float); IMB_exr_add_channel(exrhandle, - layerName, + layer_name, "B", - viewName, + view_name, 4, 4 * width, buf ? buf + 2 : nullptr, use_half_float); IMB_exr_add_channel(exrhandle, - layerName, + layer_name, "A", - viewName, + view_name, 4, 4 * width, buf ? buf + 3 : nullptr, @@ -105,7 +117,7 @@ void add_exr_channels(void *exrhandle, void free_exr_channels(void *exrhandle, const RenderData *rd, - const char *layerName, + const char *layer_name, const DataType datatype) { SceneRenderView *srv; @@ -121,13 +133,13 @@ void free_exr_channels(void *exrhandle, /* the pointer is stored in the first channel of each datatype */ switch (datatype) { case DataType::Value: - rect = IMB_exr_channel_rect(exrhandle, layerName, "V", srv->name); + rect = IMB_exr_channel_rect(exrhandle, layer_name, "V", srv->name); break; case DataType::Vector: - rect = IMB_exr_channel_rect(exrhandle, layerName, "X", srv->name); + rect = IMB_exr_channel_rect(exrhandle, layer_name, "X", srv->name); break; case DataType::Color: - rect = IMB_exr_channel_rect(exrhandle, layerName, "R", srv->name); + rect = IMB_exr_channel_rect(exrhandle, layer_name, "R", srv->name); break; default: break; @@ -187,7 +199,7 @@ static void write_buffer_rect(rcti *rect, for (y = y1; y < y2 && (!breaked); y++) { for (x = x1; x < x2 && (!breaked); x++) { - reader->readSampled(color, x, y, PixelSampler::Nearest); + reader->read_sampled(color, x, y, PixelSampler::Nearest); for (i = 0; i < size; i++) { buffer[offset + i] = color[i]; @@ -208,58 +220,58 @@ OutputSingleLayerOperation::OutputSingleLayerOperation( DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender) + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render) { rd_ = rd; tree_ = tree; - this->addInputSocket(datatype); + this->add_input_socket(datatype); - outputBuffer_ = nullptr; + output_buffer_ = nullptr; datatype_ = datatype; - imageInput_ = nullptr; + image_input_ = nullptr; format_ = format; BLI_strncpy(path_, path, sizeof(path_)); - viewSettings_ = viewSettings; - displaySettings_ = displaySettings; - viewName_ = viewName; - saveAsRender_ = saveAsRender; + view_settings_ = view_settings; + display_settings_ = display_settings; + view_name_ = view_name; + save_as_render_ = save_as_render; } -void OutputSingleLayerOperation::initExecution() +void OutputSingleLayerOperation::init_execution() { - imageInput_ = getInputSocketReader(0); - outputBuffer_ = init_buffer(this->getWidth(), this->getHeight(), datatype_); + image_input_ = get_input_socket_reader(0); + output_buffer_ = init_buffer(this->get_width(), this->get_height(), datatype_); } -void OutputSingleLayerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void OutputSingleLayerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { - write_buffer_rect(rect, tree_, imageInput_, outputBuffer_, this->getWidth(), datatype_); + write_buffer_rect(rect, tree_, image_input_, output_buffer_, this->get_width(), datatype_); } -void OutputSingleLayerOperation::deinitExecution() +void OutputSingleLayerOperation::deinit_execution() { - if (this->getWidth() * this->getHeight() != 0) { + if (this->get_width() * this->get_height() != 0) { int size = get_datatype_size(datatype_); - ImBuf *ibuf = IMB_allocImBuf(this->getWidth(), this->getHeight(), format_->planes, 0); + ImBuf *ibuf = IMB_allocImBuf(this->get_width(), this->get_height(), format_->planes, 0); char filename[FILE_MAX]; const char *suffix; ibuf->channels = size; - ibuf->rect_float = outputBuffer_; + ibuf->rect_float = output_buffer_; ibuf->mall |= IB_rectfloat; ibuf->dither = rd_->dither_intensity; IMB_colormanagement_imbuf_for_write( - ibuf, saveAsRender_, false, viewSettings_, displaySettings_, format_); + ibuf, save_as_render_, false, view_settings_, display_settings_, format_); - suffix = BKE_scene_multiview_view_suffix_get(rd_, viewName_); + suffix = BKE_scene_multiview_view_suffix_get(rd_, view_name_); BKE_image_path_from_imformat(filename, path_, @@ -279,20 +291,22 @@ void OutputSingleLayerOperation::deinitExecution() IMB_freeImBuf(ibuf); } - outputBuffer_ = nullptr; - imageInput_ = nullptr; + output_buffer_ = nullptr; + image_input_ = nullptr; } void OutputSingleLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), const rcti &area, Span inputs) { - if (!outputBuffer_) { + if (!output_buffer_) { return; } - MemoryBuffer output_buf( - outputBuffer_, COM_data_type_num_channels(datatype_), this->getWidth(), this->getHeight()); + MemoryBuffer output_buf(output_buffer_, + COM_data_type_num_channels(datatype_), + this->get_width(), + this->get_height()); const MemoryBuffer *input_image = inputs[0]; output_buf.copy_from(input_image, area); } @@ -305,9 +319,9 @@ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bo this->datatype = datatype_; this->use_layer = use_layer_; - /* these are created in initExecution */ - this->outputBuffer = nullptr; - this->imageInput = nullptr; + /* these are created in init_execution */ + this->output_buffer = nullptr; + this->image_input = nullptr; } OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene *scene, @@ -316,7 +330,7 @@ OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene * const char *path, char exr_codec, bool exr_half_float, - const char *viewName) + const char *view_name) { scene_ = scene; rd_ = rd; @@ -325,7 +339,7 @@ OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene * BLI_strncpy(path_, path, sizeof(path_)); exr_codec_ = exr_codec; exr_half_float_ = exr_half_float; - viewName_ = viewName; + view_name_ = view_name; this->set_canvas_input_index(RESOLUTION_INPUT_ANY); } @@ -333,11 +347,11 @@ void OutputOpenExrMultiLayerOperation::add_layer(const char *name, DataType datatype, bool use_layer) { - this->addInputSocket(datatype); + this->add_input_socket(datatype); layers_.append(OutputOpenExrLayer(name, datatype, use_layer)); } -StampData *OutputOpenExrMultiLayerOperation::createStampData() const +StampData *OutputOpenExrMultiLayerOperation::create_stamp_data() const { /* StampData API doesn't provide functions to modify an instance without having a RenderResult. */ @@ -346,54 +360,54 @@ StampData *OutputOpenExrMultiLayerOperation::createStampData() const render_result.stamp_data = stamp_data; for (const OutputOpenExrLayer &layer : layers_) { /* Skip unconnected sockets. */ - if (layer.imageInput == nullptr) { + if (layer.image_input == nullptr) { continue; } - std::unique_ptr meta_data = layer.imageInput->getMetaData(); + std::unique_ptr meta_data = layer.image_input->get_meta_data(); if (meta_data) { blender::StringRef layer_name = blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name( blender::StringRef(layer.name, BLI_strnlen(layer.name, sizeof(layer.name)))); - meta_data->replaceHashNeutralCryptomatteKeys(layer_name); - meta_data->addToRenderResult(&render_result); + meta_data->replace_hash_neutral_cryptomatte_keys(layer_name); + meta_data->add_to_render_result(&render_result); } } return stamp_data; } -void OutputOpenExrMultiLayerOperation::initExecution() +void OutputOpenExrMultiLayerOperation::init_execution() { for (unsigned int i = 0; i < layers_.size(); i++) { if (layers_[i].use_layer) { - SocketReader *reader = getInputSocketReader(i); - layers_[i].imageInput = reader; - layers_[i].outputBuffer = init_buffer( - this->getWidth(), this->getHeight(), layers_[i].datatype); + SocketReader *reader = get_input_socket_reader(i); + layers_[i].image_input = reader; + layers_[i].output_buffer = init_buffer( + this->get_width(), this->get_height(), layers_[i].datatype); } } } -void OutputOpenExrMultiLayerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void OutputOpenExrMultiLayerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { for (unsigned int i = 0; i < layers_.size(); i++) { OutputOpenExrLayer &layer = layers_[i]; - if (layer.imageInput) { + if (layer.image_input) { write_buffer_rect( - rect, tree_, layer.imageInput, layer.outputBuffer, this->getWidth(), layer.datatype); + rect, tree_, layer.image_input, layer.output_buffer, this->get_width(), layer.datatype); } } } -void OutputOpenExrMultiLayerOperation::deinitExecution() +void OutputOpenExrMultiLayerOperation::deinit_execution() { - unsigned int width = this->getWidth(); - unsigned int height = this->getHeight(); + unsigned int width = this->get_width(); + unsigned int height = this->get_height(); if (width != 0 && height != 0) { char filename[FILE_MAX]; const char *suffix; void *exrhandle = IMB_exr_get_handle(); - suffix = BKE_scene_multiview_view_suffix_get(rd_, viewName_); + suffix = BKE_scene_multiview_view_suffix_get(rd_, view_name_); BKE_image_path_from_imtype(filename, path_, BKE_main_blendfile_path_from_global(), @@ -406,7 +420,7 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() for (unsigned int i = 0; i < layers_.size(); i++) { OutputOpenExrLayer &layer = layers_[i]; - if (!layer.imageInput) { + if (!layer.image_input) { continue; /* skip unconnected sockets */ } @@ -416,11 +430,11 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() "", width, exr_half_float_, - layers_[i].outputBuffer); + layers_[i].output_buffer); } /* when the filename has no permissions, this can fail */ - StampData *stamp_data = createStampData(); + StampData *stamp_data = create_stamp_data(); if (IMB_exr_begin_write(exrhandle, filename, width, height, exr_codec_, stamp_data)) { IMB_exr_write_channels(exrhandle); } @@ -432,12 +446,12 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() IMB_exr_close(exrhandle); for (unsigned int i = 0; i < layers_.size(); i++) { - if (layers_[i].outputBuffer) { - MEM_freeN(layers_[i].outputBuffer); - layers_[i].outputBuffer = nullptr; + if (layers_[i].output_buffer) { + MEM_freeN(layers_[i].output_buffer); + layers_[i].output_buffer = nullptr; } - layers_[i].imageInput = nullptr; + layers_[i].image_input = nullptr; } BKE_stamp_data_free(stamp_data); } @@ -450,11 +464,11 @@ void OutputOpenExrMultiLayerOperation::update_memory_buffer_partial(MemoryBuffer const MemoryBuffer *input_image = inputs[0]; for (int i = 0; i < layers_.size(); i++) { OutputOpenExrLayer &layer = layers_[i]; - if (layer.outputBuffer) { - MemoryBuffer output_buf(layer.outputBuffer, + if (layer.output_buffer) { + MemoryBuffer output_buf(layer.output_buffer, COM_data_type_num_channels(layer.datatype), - this->getWidth(), - this->getHeight()); + this->get_width(), + this->get_height()); output_buf.copy_from(input_image, area); } } diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index e862b964431..2a18b056c12 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -38,15 +38,15 @@ class OutputSingleLayerOperation : public MultiThreadedOperation { ImageFormatData *format_; char path_[FILE_MAX]; - float *outputBuffer_; + float *output_buffer_; DataType datatype_; - SocketReader *imageInput_; + SocketReader *image_input_; - const ColorManagedViewSettings *viewSettings_; - const ColorManagedDisplaySettings *displaySettings_; + const ColorManagedViewSettings *view_settings_; + const ColorManagedDisplaySettings *display_settings_; - const char *viewName_; - bool saveAsRender_; + const char *view_name_; + bool save_as_render_; public: OutputSingleLayerOperation(const RenderData *rd, @@ -54,19 +54,19 @@ class OutputSingleLayerOperation : public MultiThreadedOperation { DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender); + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render); - void executeRegion(rcti *rect, unsigned int tileNumber) override; - bool isOutputOperation(bool /*rendering*/) const override + void execute_region(rcti *rect, unsigned int tile_number) override; + bool is_output_operation(bool /*rendering*/) const override { return true; } - void initExecution() override; - void deinitExecution() override; - eCompositorPriority getRenderPriority() const override + void init_execution() override; + void deinit_execution() override; + eCompositorPriority get_render_priority() const override { return eCompositorPriority::Low; } @@ -85,8 +85,8 @@ struct OutputOpenExrLayer { bool use_layer; /* internals */ - float *outputBuffer; - SocketReader *imageInput; + float *output_buffer; + SocketReader *image_input; }; /* Writes inputs into OpenEXR multilayer channels. */ @@ -100,9 +100,9 @@ class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation { char exr_codec_; bool exr_half_float_; Vector layers_; - const char *viewName_; + const char *view_name_; - StampData *createStampData() const; + StampData *create_stamp_data() const; public: OutputOpenExrMultiLayerOperation(const Scene *scene, @@ -111,18 +111,18 @@ class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation { const char *path, char exr_codec, bool exr_half_float, - const char *viewName); + const char *view_name); void add_layer(const char *name, DataType datatype, bool use_layer); - void executeRegion(rcti *rect, unsigned int tileNumber) override; - bool isOutputOperation(bool /*rendering*/) const override + void execute_region(rcti *rect, unsigned int tile_number) override; + bool is_output_operation(bool /*rendering*/) const override { return true; } - void initExecution() override; - void deinitExecution() override; - eCompositorPriority getRenderPriority() const override + void init_execution() override; + void deinit_execution() override; + eCompositorPriority get_render_priority() const override { return eCompositorPriority::Low; } @@ -133,15 +133,15 @@ class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation { }; void add_exr_channels(void *exrhandle, - const char *layerName, + const char *layer_name, const DataType datatype, - const char *viewName, + const char *view_name, const size_t width, bool use_half_float, float *buf); void free_exr_channels(void *exrhandle, const RenderData *rd, - const char *layerName, + const char *layer_name, const DataType datatype); int get_datatype_size(DataType datatype); diff --git a/source/blender/compositor/operations/COM_PixelateOperation.cc b/source/blender/compositor/operations/COM_PixelateOperation.cc index 0d6e906a4c3..869e2357126 100644 --- a/source/blender/compositor/operations/COM_PixelateOperation.cc +++ b/source/blender/compositor/operations/COM_PixelateOperation.cc @@ -20,32 +20,32 @@ namespace blender::compositor { -PixelateOperation::PixelateOperation(DataType datatype) +PixelateOperation::PixelateOperation(DataType data_type) { - this->addInputSocket(datatype); - this->addOutputSocket(datatype); + this->add_input_socket(data_type); + this->add_output_socket(data_type); this->set_canvas_input_index(0); - inputOperation_ = nullptr; + input_operation_ = nullptr; } -void PixelateOperation::initExecution() +void PixelateOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void PixelateOperation::deinitExecution() +void PixelateOperation::deinit_execution() { - inputOperation_ = nullptr; + input_operation_ = nullptr; } -void PixelateOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void PixelateOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float nx = round(x); float ny = round(y); - inputOperation_->readSampled(output, nx, ny, sampler); + input_operation_->read_sampled(output, nx, ny, sampler); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PixelateOperation.h b/source/blender/compositor/operations/COM_PixelateOperation.h index f8155e54f75..51a480b98e9 100644 --- a/source/blender/compositor/operations/COM_PixelateOperation.h +++ b/source/blender/compositor/operations/COM_PixelateOperation.h @@ -34,33 +34,33 @@ class PixelateOperation : public NodeOperation { /** * \brief cached reference to the input operation */ - SocketReader *inputOperation_; + SocketReader *input_operation_; public: /** * \brief PixelateOperation - * \param dataType: the datatype to create this operator for (saves datatype conversions) + * \param data_type: the datatype to create this operator for (saves datatype conversions) */ - PixelateOperation(DataType dataType); + PixelateOperation(DataType data_type); /** * \brief initialization of the execution */ - void initExecution() override; + void init_execution() override; /** * \brief de-initialization of the execution */ - void deinitExecution() override; + void deinit_execution() override; /** - * \brief executePixel + * \brief execute_pixel * \param output: result * \param x: x-coordinate * \param y: y-coordinate * \param sampler: sampler */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc index fbaab16efc2..6a6692c977d 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc @@ -56,11 +56,11 @@ static bool check_corners(float corners[4][2]) } /* TODO(manzanilla): to be removed with tiled implementation. */ -static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float corners[4][2]) +static void read_corners_from_sockets(rcti *rect, SocketReader *readers[4], float corners[4][2]) { for (int i = 0; i < 4; i++) { float result[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - readers[i]->readSampled(result, rect->xmin, rect->ymin, PixelSampler::Nearest); + readers[i]->read_sampled(result, rect->xmin, rect->ymin, PixelSampler::Nearest); corners[i][0] = result[0]; corners[i][1] = result[1]; } @@ -136,13 +136,13 @@ static void read_input_corners(NodeOperation *op, const int first_input_idx, flo PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : corners_ready_(false) { - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); /* XXX this is stupid: we need to make this "complex", - * so we can use the initializeTileData function + * so we can use the initialize_tile_data function * to read corners from input sockets ... */ flags.complex = true; @@ -153,49 +153,49 @@ void PlaneCornerPinMaskOperation::init_data() if (execution_model_ == eExecutionModel::FullFrame) { float corners[4][2]; read_input_corners(this, 0, corners); - calculateCorners(corners, true, 0); + calculate_corners(corners, true, 0); } } -/* TODO(manzanilla): to be removed with tiled implementation. Same for #deinitExecution and do the +/* TODO(manzanilla): to be removed with tiled implementation. Same for #deinit_execution and do the * same on #PlaneCornerPinWarpImageOperation. */ -void PlaneCornerPinMaskOperation::initExecution() +void PlaneCornerPinMaskOperation::init_execution() { - PlaneDistortMaskOperation::initExecution(); + PlaneDistortMaskOperation::init_execution(); - initMutex(); + init_mutex(); } -void PlaneCornerPinMaskOperation::deinitExecution() +void PlaneCornerPinMaskOperation::deinit_execution() { - PlaneDistortMaskOperation::deinitExecution(); + PlaneDistortMaskOperation::deinit_execution(); - deinitMutex(); + deinit_mutex(); } -void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect) +void *PlaneCornerPinMaskOperation::initialize_tile_data(rcti *rect) { - void *data = PlaneDistortMaskOperation::initializeTileData(rect); + void *data = PlaneDistortMaskOperation::initialize_tile_data(rect); /* get corner values once, by reading inputs at (0,0) * XXX this assumes invariable values (no image inputs), * we don't have a nice generic system for that yet */ - lockMutex(); + lock_mutex(); if (!corners_ready_) { SocketReader *readers[4] = { - getInputSocketReader(0), - getInputSocketReader(1), - getInputSocketReader(2), - getInputSocketReader(3), + get_input_socket_reader(0), + get_input_socket_reader(1), + get_input_socket_reader(2), + get_input_socket_reader(3), }; float corners[4][2]; - readCornersFromSockets(rect, readers, corners); - calculateCorners(corners, true, 0); + read_corners_from_sockets(rect, readers, corners); + calculate_corners(corners, true, 0); corners_ready_ = true; } - unlockMutex(); + unlock_mutex(); return data; } @@ -221,10 +221,10 @@ void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_id PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : corners_ready_(false) { - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); } void PlaneCornerPinWarpImageOperation::init_data() @@ -232,57 +232,58 @@ void PlaneCornerPinWarpImageOperation::init_data() if (execution_model_ == eExecutionModel::FullFrame) { float corners[4][2]; read_input_corners(this, 1, corners); - calculateCorners(corners, true, 0); + calculate_corners(corners, true, 0); } } -void PlaneCornerPinWarpImageOperation::initExecution() +void PlaneCornerPinWarpImageOperation::init_execution() { - PlaneDistortWarpImageOperation::initExecution(); + PlaneDistortWarpImageOperation::init_execution(); - initMutex(); + init_mutex(); } -void PlaneCornerPinWarpImageOperation::deinitExecution() +void PlaneCornerPinWarpImageOperation::deinit_execution() { - PlaneDistortWarpImageOperation::deinitExecution(); + PlaneDistortWarpImageOperation::deinit_execution(); - deinitMutex(); + deinit_mutex(); } -void *PlaneCornerPinWarpImageOperation::initializeTileData(rcti *rect) +void *PlaneCornerPinWarpImageOperation::initialize_tile_data(rcti *rect) { - void *data = PlaneDistortWarpImageOperation::initializeTileData(rect); + void *data = PlaneDistortWarpImageOperation::initialize_tile_data(rect); /* get corner values once, by reading inputs at (0,0) * XXX this assumes invariable values (no image inputs), * we don't have a nice generic system for that yet */ - lockMutex(); + lock_mutex(); if (!corners_ready_) { /* corner sockets start at index 1 */ SocketReader *readers[4] = { - getInputSocketReader(1), - getInputSocketReader(2), - getInputSocketReader(3), - getInputSocketReader(4), + get_input_socket_reader(1), + get_input_socket_reader(2), + get_input_socket_reader(3), + get_input_socket_reader(4), }; float corners[4][2]; - readCornersFromSockets(rect, readers, corners); - calculateCorners(corners, true, 0); + read_corners_from_sockets(rect, readers, corners); + calculate_corners(corners, true, 0); corners_ready_ = true; } - unlockMutex(); + unlock_mutex(); return data; } -bool PlaneCornerPinWarpImageOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool PlaneCornerPinWarpImageOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { for (int i = 0; i < 4; i++) { - if (getInputOperation(i + 1)->determineDependingAreaOfInterest(input, readOperation, output)) { + if (get_input_operation(i + 1)->determine_depending_area_of_interest( + input, read_operation, output)) { return true; } } @@ -293,12 +294,12 @@ bool PlaneCornerPinWarpImageOperation::determineDependingAreaOfInterest( */ output->xmin = 0; output->ymin = 0; - output->xmax = getInputOperation(0)->getWidth(); - output->ymax = getInputOperation(0)->getHeight(); + output->xmax = get_input_operation(0)->get_width(); + output->ymax = get_input_operation(0)->get_height(); return true; #if 0 - return PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( - input, readOperation, output); + return PlaneDistortWarpImageOperation::determine_depending_area_of_interest( + input, read_operation, output); #endif } diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h index 211dcee5132..bb2166bf8b8 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h @@ -38,10 +38,10 @@ class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation { PlaneCornerPinMaskOperation(); void init_data() override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; @@ -56,14 +56,14 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation { PlaneCornerPinWarpImageOperation(); void init_data() override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; }; diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 8cfa8715cc2..21757e4c97d 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -29,29 +29,29 @@ PlaneDistortBaseOperation::PlaneDistortBaseOperation() { } -void PlaneDistortBaseOperation::calculateCorners(const float corners[4][2], - bool normalized, - int sample) +void PlaneDistortBaseOperation::calculate_corners(const float corners[4][2], + bool normalized, + int sample) { BLI_assert(sample < motion_blur_samples_); MotionSample *sample_data = &samples_[sample]; if (normalized) { for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); - sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); + sample_data->frame_space_corners[i][0] = corners[i][0] * this->get_width(); + sample_data->frame_space_corners[i][1] = corners[i][1] * this->get_height(); } } else { for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0]; - sample_data->frameSpaceCorners[i][1] = corners[i][1]; + sample_data->frame_space_corners[i][0] = corners[i][0]; + sample_data->frame_space_corners[i][1] = corners[i][1]; } } } /* ******** PlaneDistort WarpImage ******** */ -BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], float deriv[2][2]) +BLI_INLINE void warp_coord(float x, float y, float matrix[3][3], float uv[2], float deriv[2][2]) { float vec[3] = {x, y, 1.0f}; mul_m3_v3(matrix, vec); @@ -66,55 +66,55 @@ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], flo PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortBaseOperation() { - this->addInputSocket(DataType::Color, ResizeMode::Align); - this->addOutputSocket(DataType::Color); - pixelReader_ = nullptr; + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_output_socket(DataType::Color); + pixel_reader_ = nullptr; this->flags.complex = true; } -void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], - bool normalized, - int sample) +void PlaneDistortWarpImageOperation::calculate_corners(const float corners[4][2], + bool normalized, + int sample) { - PlaneDistortBaseOperation::calculateCorners(corners, normalized, sample); + PlaneDistortBaseOperation::calculate_corners(corners, normalized, sample); const NodeOperation *image = get_input_operation(0); - const int width = image->getWidth(); - const int height = image->getHeight(); + const int width = image->get_width(); + const int height = image->get_height(); float frame_corners[4][2] = { {0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}}; MotionSample *sample_data = &samples_[sample]; BKE_tracking_homography_between_two_quads( - sample_data->frameSpaceCorners, frame_corners, sample_data->perspectiveMatrix); + sample_data->frame_space_corners, frame_corners, sample_data->perspective_matrix); } -void PlaneDistortWarpImageOperation::initExecution() +void PlaneDistortWarpImageOperation::init_execution() { - pixelReader_ = this->getInputSocketReader(0); + pixel_reader_ = this->get_input_socket_reader(0); } -void PlaneDistortWarpImageOperation::deinitExecution() +void PlaneDistortWarpImageOperation::deinit_execution() { - pixelReader_ = nullptr; + pixel_reader_ = nullptr; } -void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void PlaneDistortWarpImageOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { float uv[2]; float deriv[2][2]; if (motion_blur_samples_ == 1) { - warpCoord(x, y, samples_[0].perspectiveMatrix, uv, deriv); - pixelReader_->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + warp_coord(x, y, samples_[0].perspective_matrix, uv, deriv); + pixel_reader_->read_filtered(output, uv[0], uv[1], deriv[0], deriv[1]); } else { zero_v4(output); for (int sample = 0; sample < motion_blur_samples_; sample++) { float color[4]; - warpCoord(x, y, samples_[sample].perspectiveMatrix, uv, deriv); - pixelReader_->readFiltered(color, uv[0], uv[1], deriv[0], deriv[1]); + warp_coord(x, y, samples_[sample].perspective_matrix, uv, deriv); + pixel_reader_->read_filtered(color, uv[0], uv[1], deriv[0], deriv[1]); add_v4_v4(output, color); } mul_v4_fl(output, 1.0f / (float)motion_blur_samples_); @@ -131,7 +131,7 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer * BuffersIterator it = output->iterate_with({}, area); if (motion_blur_samples_ == 1) { for (; !it.is_end(); ++it) { - warpCoord(it.x, it.y, samples_[0].perspectiveMatrix, uv, deriv); + 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); } } @@ -140,7 +140,7 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer * zero_v4(it.out); for (const int sample : IndexRange(motion_blur_samples_)) { float color[4]; - warpCoord(it.x, it.y, samples_[sample].perspectiveMatrix, uv, deriv); + 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); add_v4_v4(it.out, color); } @@ -149,8 +149,8 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer * } } -bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool PlaneDistortWarpImageOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { float min[2], max[2]; INIT_MINMAX2(min, max); @@ -160,23 +160,23 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( float deriv[2][2]; MotionSample *sample_data = &samples_[sample]; /* TODO(sergey): figure out proper way to do this. */ - warpCoord(input->xmin - 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); - warpCoord(input->xmax + 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv); - warpCoord(input->xmax + 2, input->ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv); - warpCoord(input->xmin - 2, input->ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv); + warp_coord(input->xmin - 2, input->ymin - 2, sample_data->perspective_matrix, UVs[0], deriv); + warp_coord(input->xmax + 2, input->ymin - 2, sample_data->perspective_matrix, UVs[1], deriv); + warp_coord(input->xmax + 2, input->ymax + 2, sample_data->perspective_matrix, UVs[2], deriv); + warp_coord(input->xmin - 2, input->ymax + 2, sample_data->perspective_matrix, UVs[3], deriv); for (int i = 0; i < 4; i++) { minmax_v2v2_v2(min, max, UVs[i]); } } - rcti newInput; + rcti new_input; - newInput.xmin = min[0] - 1; - newInput.ymin = min[1] - 1; - newInput.xmax = max[0] + 1; - newInput.ymax = max[1] + 1; + new_input.xmin = min[0] - 1; + new_input.ymin = min[1] - 1; + new_input.xmax = max[0] + 1; + new_input.ymax = max[1] + 1; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, @@ -201,14 +201,14 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, float deriv[2][2]; MotionSample *sample_data = &samples_[sample]; /* TODO(sergey): figure out proper way to do this. */ - warpCoord( - output_area.xmin - 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); - warpCoord( - output_area.xmax + 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv); - warpCoord( - output_area.xmax + 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv); - warpCoord( - output_area.xmin - 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv); + warp_coord( + output_area.xmin - 2, output_area.ymin - 2, sample_data->perspective_matrix, UVs[0], deriv); + warp_coord( + output_area.xmax + 2, output_area.ymin - 2, sample_data->perspective_matrix, UVs[1], deriv); + warp_coord( + output_area.xmax + 2, output_area.ymax + 2, sample_data->perspective_matrix, UVs[2], deriv); + warp_coord( + output_area.xmin - 2, output_area.ymax + 2, sample_data->perspective_matrix, UVs[3], deriv); for (int i = 0; i < 4; i++) { minmax_v2v2_v2(min, max, UVs[i]); } @@ -225,21 +225,21 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation() { - addOutputSocket(DataType::Value); + add_output_socket(DataType::Value); /* Currently hardcoded to 8 samples. */ osa_ = 8; } -void PlaneDistortMaskOperation::initExecution() +void PlaneDistortMaskOperation::init_execution() { BLI_jitter_init(jitter_, osa_); } -void PlaneDistortMaskOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void PlaneDistortMaskOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { float point[2]; int inside_counter = 0; @@ -249,13 +249,13 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4], point[0] = x + jitter_[sample][0]; point[1] = y + jitter_[sample][1]; if (isect_point_tri_v2(point, - sample_data->frameSpaceCorners[0], - sample_data->frameSpaceCorners[1], - sample_data->frameSpaceCorners[2]) || + 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->frameSpaceCorners[0], - sample_data->frameSpaceCorners[2], - sample_data->frameSpaceCorners[3])) { + sample_data->frame_space_corners[0], + sample_data->frame_space_corners[2], + sample_data->frame_space_corners[3])) { inside_counter++; } } @@ -268,13 +268,13 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4], point[0] = x + jitter_[osa_sample][0]; point[1] = y + jitter_[osa_sample][1]; if (isect_point_tri_v2(point, - sample_data->frameSpaceCorners[0], - sample_data->frameSpaceCorners[1], - sample_data->frameSpaceCorners[2]) || + 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->frameSpaceCorners[0], - sample_data->frameSpaceCorners[2], - sample_data->frameSpaceCorners[3])) { + sample_data->frame_space_corners[0], + sample_data->frame_space_corners[2], + sample_data->frame_space_corners[3])) { inside_counter++; } } @@ -307,13 +307,13 @@ int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x, point[0] = x + jitter_[sample][0]; point[1] = y + jitter_[sample][1]; if (isect_point_tri_v2(point, - sample_data.frameSpaceCorners[0], - sample_data.frameSpaceCorners[1], - sample_data.frameSpaceCorners[2]) || + 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.frameSpaceCorners[0], - sample_data.frameSpaceCorners[2], - sample_data.frameSpaceCorners[3])) { + sample_data.frame_space_corners[0], + sample_data.frame_space_corners[2], + sample_data.frame_space_corners[3])) { inside_count++; } } diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h index e9880624ebf..bf330ed7230 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h @@ -35,8 +35,8 @@ namespace blender::compositor { class PlaneDistortBaseOperation : public MultiThreadedOperation { protected: struct MotionSample { - float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ - float perspectiveMatrix[3][3]; + float frame_space_corners[4][2]; /* Corners coordinates in pixel space. */ + float perspective_matrix[3][3]; }; MotionSample samples_[PLANE_DISTORT_MAX_SAMPLES]; int motion_blur_samples_; @@ -45,17 +45,17 @@ class PlaneDistortBaseOperation : public MultiThreadedOperation { public: PlaneDistortBaseOperation(); - void setMotionBlurSamples(int samples) + void set_motion_blur_samples(int samples) { BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); motion_blur_samples_ = samples; } - void setMotionBlurShutter(float shutter) + void set_motion_blur_shutter(float shutter) { motion_blur_shutter_ = shutter; } - virtual void calculateCorners(const float corners[4][2], bool normalized, int sample); + virtual void calculate_corners(const float corners[4][2], bool normalized, int sample); private: friend class PlaneTrackCommon; @@ -63,21 +63,21 @@ class PlaneDistortBaseOperation : public MultiThreadedOperation { class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation { protected: - SocketReader *pixelReader_; + SocketReader *pixel_reader_; public: PlaneDistortWarpImageOperation(); - void calculateCorners(const float corners[4][2], bool normalized, int sample) override; + void calculate_corners(const float corners[4][2], bool normalized, int sample) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) 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, @@ -93,9 +93,9 @@ class PlaneDistortMaskOperation : public PlaneDistortBaseOperation { public: PlaneDistortMaskOperation(); - void initExecution() override; + void init_execution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc index 1f40556a935..28da200c615 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc @@ -27,18 +27,18 @@ namespace blender::compositor { PlaneTrackCommon::PlaneTrackCommon() { - movieClip_ = nullptr; + movie_clip_ = nullptr; framenumber_ = 0; - trackingObjectName_[0] = '\0'; - planeTrackName_[0] = '\0'; + tracking_object_name_[0] = '\0'; + plane_track_name_[0] = '\0'; } void PlaneTrackCommon::read_and_calculate_corners(PlaneDistortBaseOperation *distort_op) { float corners[4][2]; if (distort_op->motion_blur_samples_ == 1) { - readCornersFromTrack(corners, framenumber_); - distort_op->calculateCorners(corners, true, 0); + read_corners_from_track(corners, framenumber_); + distort_op->calculate_corners(corners, true, 0); } else { const float frame = (float)framenumber_ - distort_op->motion_blur_shutter_; @@ -46,30 +46,30 @@ void PlaneTrackCommon::read_and_calculate_corners(PlaneDistortBaseOperation *dis distort_op->motion_blur_samples_; float frame_iter = frame; for (int sample = 0; sample < distort_op->motion_blur_samples_; sample++) { - readCornersFromTrack(corners, frame_iter); - distort_op->calculateCorners(corners, true, sample); + read_corners_from_track(corners, frame_iter); + distort_op->calculate_corners(corners, true, sample); frame_iter += frame_step; } } } -void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) +void PlaneTrackCommon::read_corners_from_track(float corners[4][2], float frame) { MovieTracking *tracking; MovieTrackingObject *object; - if (!movieClip_) { + if (!movie_clip_) { return; } - tracking = &movieClip_->tracking; + tracking = &movie_clip_->tracking; - object = BKE_tracking_object_get_named(tracking, trackingObjectName_); + object = BKE_tracking_object_get_named(tracking, tracking_object_name_); if (object) { MovieTrackingPlaneTrack *plane_track; - plane_track = BKE_tracking_plane_track_get_named(tracking, object, planeTrackName_); + plane_track = BKE_tracking_plane_track_get_named(tracking, object, plane_track_name_); if (plane_track) { - float clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, frame); + float clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, frame); BKE_tracking_plane_marker_get_subframe_corners(plane_track, clip_framenr, corners); } } @@ -78,11 +78,11 @@ void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) void PlaneTrackCommon::determine_canvas(const rcti &preferred_area, rcti &r_area) { r_area = COM_AREA_NONE; - if (movieClip_) { + if (movie_clip_) { int width, height; MovieClipUser user = {0}; BKE_movieclip_user_set_frame(&user, framenumber_); - BKE_movieclip_get_size(movieClip_, &user, &width, &height); + BKE_movieclip_get_size(movie_clip_, &user, &width, &height); r_area = preferred_area; r_area.xmax = r_area.xmin + width; r_area.ymax = r_area.ymin + height; @@ -100,9 +100,9 @@ void PlaneTrackMaskOperation::init_data() } /* TODO(manzanilla): to be removed with tiled implementation. */ -void PlaneTrackMaskOperation::initExecution() +void PlaneTrackMaskOperation::init_execution() { - PlaneDistortMaskOperation::initExecution(); + PlaneDistortMaskOperation::init_execution(); if (execution_model_ == eExecutionModel::Tiled) { PlaneTrackCommon::read_and_calculate_corners(this); } @@ -119,9 +119,9 @@ void PlaneTrackWarpImageOperation::init_data() } /* TODO(manzanilla): to be removed with tiled implementation. */ -void PlaneTrackWarpImageOperation::initExecution() +void PlaneTrackWarpImageOperation::init_execution() { - PlaneDistortWarpImageOperation::initExecution(); + PlaneDistortWarpImageOperation::init_execution(); if (execution_model_ == eExecutionModel::Tiled) { PlaneTrackCommon::read_and_calculate_corners(this); } diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h index d3dbd092024..4c584ca43f4 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h @@ -32,10 +32,10 @@ namespace blender::compositor { class PlaneTrackCommon { protected: - MovieClip *movieClip_; + MovieClip *movie_clip_; int framenumber_; - char trackingObjectName_[64]; - char planeTrackName_[64]; + char tracking_object_name_[64]; + char plane_track_name_[64]; /* NOTE: this class is not an operation itself (to prevent virtual inheritance issues) * implementation classes must make wrappers to use these methods, see below. @@ -46,25 +46,25 @@ class PlaneTrackCommon { public: PlaneTrackCommon(); - void setMovieClip(MovieClip *clip) + void set_movie_clip(MovieClip *clip) { - movieClip_ = clip; + movie_clip_ = clip; } - void setTrackingObject(char *object) + void set_tracking_object(char *object) { - BLI_strncpy(trackingObjectName_, object, sizeof(trackingObjectName_)); + BLI_strncpy(tracking_object_name_, object, sizeof(tracking_object_name_)); } - void setPlaneTrackName(char *plane_track) + void set_plane_track_name(char *plane_track) { - BLI_strncpy(planeTrackName_, plane_track, sizeof(planeTrackName_)); + BLI_strncpy(plane_track_name_, plane_track, sizeof(plane_track_name_)); } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { framenumber_ = framenumber; } private: - void readCornersFromTrack(float corners[4][2], float frame); + void read_corners_from_track(float corners[4][2], float frame); }; class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTrackCommon { @@ -75,7 +75,7 @@ class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTr void init_data() override; - void initExecution() override; + void init_execution() override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override { @@ -96,7 +96,7 @@ class PlaneTrackWarpImageOperation : public PlaneDistortWarpImageOperation, void init_data() override; - void initExecution() override; + void init_execution() override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override { diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.cc b/source/blender/compositor/operations/COM_PosterizeOperation.cc index 44a86f1d9de..c43520d103c 100644 --- a/source/blender/compositor/operations/COM_PosterizeOperation.cc +++ b/source/blender/compositor/operations/COM_PosterizeOperation.cc @@ -22,37 +22,37 @@ namespace blender::compositor { PosterizeOperation::PosterizeOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - inputProgram_ = nullptr; - inputStepsProgram_ = nullptr; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; + input_steps_program_ = nullptr; flags.can_be_constant = true; } -void PosterizeOperation::initExecution() +void PosterizeOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); - inputStepsProgram_ = this->getInputSocketReader(1); + input_program_ = this->get_input_socket_reader(0); + input_steps_program_ = this->get_input_socket_reader(1); } -void PosterizeOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void PosterizeOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue[4]; - float inputSteps[4]; + float input_value[4]; + float input_steps[4]; - inputProgram_->readSampled(inputValue, x, y, sampler); - inputStepsProgram_->readSampled(inputSteps, x, y, sampler); - CLAMP(inputSteps[0], 2.0f, 1024.0f); - const float steps_inv = 1.0f / inputSteps[0]; + input_program_->read_sampled(input_value, x, y, sampler); + input_steps_program_->read_sampled(input_steps, x, y, sampler); + CLAMP(input_steps[0], 2.0f, 1024.0f); + const float steps_inv = 1.0f / input_steps[0]; - output[0] = floor(inputValue[0] / steps_inv) * steps_inv; - output[1] = floor(inputValue[1] / steps_inv) * steps_inv; - output[2] = floor(inputValue[2] / steps_inv) * steps_inv; - output[3] = inputValue[3]; + output[0] = floor(input_value[0] / steps_inv) * steps_inv; + output[1] = floor(input_value[1] / steps_inv) * steps_inv; + output[2] = floor(input_value[2] / steps_inv) * steps_inv; + output[3] = input_value[3]; } void PosterizeOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -73,10 +73,10 @@ void PosterizeOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void PosterizeOperation::deinitExecution() +void PosterizeOperation::deinit_execution() { - inputProgram_ = nullptr; - inputStepsProgram_ = nullptr; + input_program_ = nullptr; + input_steps_program_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.h b/source/blender/compositor/operations/COM_PosterizeOperation.h index 58ce599c6fe..70e516bccf8 100644 --- a/source/blender/compositor/operations/COM_PosterizeOperation.h +++ b/source/blender/compositor/operations/COM_PosterizeOperation.h @@ -25,10 +25,10 @@ namespace blender::compositor { class PosterizeOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; - SocketReader *inputStepsProgram_; + SocketReader *input_program_; + SocketReader *input_steps_program_; public: PosterizeOperation(); @@ -36,17 +36,17 @@ class PosterizeOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc index 960cfda9ec0..f46f0f5b13f 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cc +++ b/source/blender/compositor/operations/COM_PreviewOperation.cc @@ -23,70 +23,70 @@ namespace blender::compositor { -PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const unsigned int defaultWidth, - const unsigned int defaultHeight) +PreviewOperation::PreviewOperation(const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const unsigned int default_width, + const unsigned int default_height) { - this->addInputSocket(DataType::Color, ResizeMode::Align); + this->add_input_socket(DataType::Color, ResizeMode::Align); preview_ = nullptr; - outputBuffer_ = nullptr; + output_buffer_ = nullptr; input_ = nullptr; divider_ = 1.0f; - viewSettings_ = viewSettings; - displaySettings_ = displaySettings; - defaultWidth_ = defaultWidth; - defaultHeight_ = defaultHeight; + view_settings_ = view_settings; + display_settings_ = display_settings; + default_width_ = default_width; + default_height_ = default_height; flags.use_viewer_border = true; flags.is_preview_operation = true; } -void PreviewOperation::verifyPreview(bNodeInstanceHash *previews, bNodeInstanceKey key) +void PreviewOperation::verify_preview(bNodeInstanceHash *previews, bNodeInstanceKey key) { /* Size (0, 0) ensures the preview rect is not allocated in advance, - * this is set later in initExecution once the resolution is determined. + * this is set later in init_execution once the resolution is determined. */ preview_ = BKE_node_preview_verify(previews, key, 0, 0, true); } -void PreviewOperation::initExecution() +void PreviewOperation::init_execution() { - input_ = getInputSocketReader(0); + input_ = get_input_socket_reader(0); - if (this->getWidth() == (unsigned int)preview_->xsize && - this->getHeight() == (unsigned int)preview_->ysize) { - outputBuffer_ = preview_->rect; + if (this->get_width() == (unsigned int)preview_->xsize && + this->get_height() == (unsigned int)preview_->ysize) { + output_buffer_ = preview_->rect; } - if (outputBuffer_ == nullptr) { - outputBuffer_ = (unsigned char *)MEM_callocN( - sizeof(unsigned char) * 4 * getWidth() * getHeight(), "PreviewOperation"); + if (output_buffer_ == nullptr) { + output_buffer_ = (unsigned char *)MEM_callocN( + sizeof(unsigned char) * 4 * get_width() * get_height(), "PreviewOperation"); if (preview_->rect) { MEM_freeN(preview_->rect); } - preview_->xsize = getWidth(); - preview_->ysize = getHeight(); - preview_->rect = outputBuffer_; + preview_->xsize = get_width(); + preview_->ysize = get_height(); + preview_->rect = output_buffer_; } } -void PreviewOperation::deinitExecution() +void PreviewOperation::deinit_execution() { - outputBuffer_ = nullptr; + output_buffer_ = nullptr; input_ = nullptr; } -void PreviewOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void PreviewOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { int offset; float color[4]; struct ColormanageProcessor *cm_processor; - cm_processor = IMB_colormanagement_display_processor_new(viewSettings_, displaySettings_); + cm_processor = IMB_colormanagement_display_processor_new(view_settings_, display_settings_); for (int y = rect->ymin; y < rect->ymax; y++) { - offset = (y * getWidth() + rect->xmin) * 4; + offset = (y * get_width() + rect->xmin) * 4; for (int x = rect->xmin; x < rect->xmax; x++) { float rx = floor(x / divider_); float ry = floor(y / divider_); @@ -95,35 +95,35 @@ void PreviewOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) color[1] = 0.0f; color[2] = 0.0f; color[3] = 1.0f; - input_->readSampled(color, rx, ry, PixelSampler::Nearest); + input_->read_sampled(color, rx, ry, PixelSampler::Nearest); IMB_colormanagement_processor_apply_v4(cm_processor, color); - rgba_float_to_uchar(outputBuffer_ + offset, color); + rgba_float_to_uchar(output_buffer_ + offset, color); offset += 4; } } IMB_colormanagement_processor_free(cm_processor); } -bool PreviewOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool PreviewOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmin = input->xmin / divider_; - newInput.xmax = input->xmax / divider_; - newInput.ymin = input->ymin / divider_; - newInput.ymax = input->ymax / divider_; + new_input.xmin = input->xmin / divider_; + new_input.xmax = input->xmax / divider_; + new_input.ymin = input->ymin / divider_; + new_input.ymax = input->ymax / divider_; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { /* Use default preview resolution as preferred ensuring it has size so that * generated inputs (which don't have resolution on their own) are displayed */ - BLI_assert(defaultWidth_ > 0 && defaultHeight_ > 0); + BLI_assert(default_width_ > 0 && default_height_ > 0); rcti local_preferred; - BLI_rcti_init(&local_preferred, 0, defaultWidth_, 0, defaultHeight_); + BLI_rcti_init(&local_preferred, 0, default_width_, 0, default_height_); NodeOperation::determine_canvas(local_preferred, r_area); /* If resolution is 0 there are two possible scenarios: @@ -152,7 +152,7 @@ void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti BLI_rcti_init(&r_area, r_area.xmin, r_area.xmin + width, r_area.ymin, r_area.ymin + height); } -eCompositorPriority PreviewOperation::getRenderPriority() const +eCompositorPriority PreviewOperation::get_render_priority() const { return eCompositorPriority::Low; } @@ -176,12 +176,12 @@ void PreviewOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output) { MemoryBuffer *input = inputs[0]; struct ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new( - viewSettings_, displaySettings_); + view_settings_, display_settings_); rcti buffer_area; - BLI_rcti_init(&buffer_area, 0, this->getWidth(), 0, this->getHeight()); + BLI_rcti_init(&buffer_area, 0, this->get_width(), 0, this->get_height()); BuffersIteratorBuilder it_builder( - outputBuffer_, buffer_area, area, COM_data_type_num_channels(DataType::Color)); + output_buffer_, buffer_area, area, COM_data_type_num_channels(DataType::Color)); for (BuffersIterator it = it_builder.build(); !it.is_end(); ++it) { const float rx = it.x / divider_; diff --git a/source/blender/compositor/operations/COM_PreviewOperation.h b/source/blender/compositor/operations/COM_PreviewOperation.h index 1df8815d62b..97d1884c9a7 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.h +++ b/source/blender/compositor/operations/COM_PreviewOperation.h @@ -28,7 +28,7 @@ namespace blender::compositor { class PreviewOperation : public MultiThreadedOperation { protected: - unsigned char *outputBuffer_; + unsigned char *output_buffer_; /** * \brief holds reference to the SDNA bNode, where this nodes will render the preview image for @@ -36,32 +36,32 @@ class PreviewOperation : public MultiThreadedOperation { bNodePreview *preview_; SocketReader *input_; float divider_; - unsigned int defaultWidth_; - unsigned int defaultHeight_; + unsigned int default_width_; + unsigned int default_height_; - const ColorManagedViewSettings *viewSettings_; - const ColorManagedDisplaySettings *displaySettings_; + const ColorManagedViewSettings *view_settings_; + const ColorManagedDisplaySettings *display_settings_; public: - PreviewOperation(const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - unsigned int defaultWidth, - unsigned int defaultHeight); - void verifyPreview(bNodeInstanceHash *previews, bNodeInstanceKey key); + PreviewOperation(const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + unsigned int default_width, + unsigned int default_height); + void verify_preview(bNodeInstanceHash *previews, bNodeInstanceKey key); - bool isOutputOperation(bool /*rendering*/) const override + bool is_output_operation(bool /*rendering*/) const override { return !G.background; } - void initExecution() override; - void deinitExecution() override; - eCompositorPriority getRenderPriority() const override; + void init_execution() override; + void deinit_execution() override; + eCompositorPriority get_render_priority() const override; - void executeRegion(rcti *rect, unsigned int tileNumber) override; + void execute_region(rcti *rect, unsigned int tile_number) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) 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, diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc index 8e6aa7e8db8..436fdb16e8d 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc @@ -23,12 +23,12 @@ namespace blender::compositor { ProjectorLensDistortionOperation::ProjectorLensDistortionOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); this->flags.complex = true; - inputProgram_ = nullptr; - dispersionAvailable_ = false; + input_program_ = nullptr; + dispersion_available_ = false; dispersion_ = 0.0f; } @@ -44,87 +44,87 @@ void ProjectorLensDistortionOperation::init_data() } } -void ProjectorLensDistortionOperation::initExecution() +void ProjectorLensDistortionOperation::init_execution() { - this->initMutex(); - inputProgram_ = this->getInputSocketReader(0); + this->init_mutex(); + input_program_ = this->get_input_socket_reader(0); } -void *ProjectorLensDistortionOperation::initializeTileData(rcti * /*rect*/) +void *ProjectorLensDistortionOperation::initialize_tile_data(rcti * /*rect*/) { - updateDispersion(); - void *buffer = inputProgram_->initializeTileData(nullptr); + update_dispersion(); + void *buffer = input_program_->initialize_tile_data(nullptr); return buffer; } -void ProjectorLensDistortionOperation::executePixel(float output[4], int x, int y, void *data) +void ProjectorLensDistortionOperation::execute_pixel(float output[4], int x, int y, void *data) { - float inputValue[4]; - const float height = this->getHeight(); - const float width = this->getWidth(); + float input_value[4]; + const float height = this->get_height(); + const float width = this->get_width(); const float v = (y + 0.5f) / height; const float u = (x + 0.5f) / width; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - inputBuffer->readBilinear(inputValue, (u * width + kr2_) - 0.5f, v * height - 0.5f); - output[0] = inputValue[0]; - inputBuffer->read(inputValue, x, y); - output[1] = inputValue[1]; - inputBuffer->readBilinear(inputValue, (u * width - kr2_) - 0.5f, v * height - 0.5f); - output[2] = inputValue[2]; + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + input_buffer->read_bilinear(input_value, (u * width + kr2_) - 0.5f, v * height - 0.5f); + output[0] = input_value[0]; + input_buffer->read(input_value, x, y); + output[1] = input_value[1]; + input_buffer->read_bilinear(input_value, (u * width - kr2_) - 0.5f, v * height - 0.5f); + output[2] = input_value[2]; output[3] = 1.0f; } -void ProjectorLensDistortionOperation::deinitExecution() +void ProjectorLensDistortionOperation::deinit_execution() { - this->deinitMutex(); - inputProgram_ = nullptr; + this->deinit_mutex(); + input_program_ = nullptr; } -bool ProjectorLensDistortionOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool ProjectorLensDistortionOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - if (dispersionAvailable_) { - newInput.ymax = input->ymax; - newInput.ymin = input->ymin; - newInput.xmin = input->xmin - kr2_ - 2; - newInput.xmax = input->xmax + kr2_ + 2; + rcti new_input; + if (dispersion_available_) { + new_input.ymax = input->ymax; + new_input.ymin = input->ymin; + new_input.xmin = input->xmin - kr2_ - 2; + new_input.xmax = input->xmax + kr2_ + 2; } else { - rcti dispInput; - BLI_rcti_init(&dispInput, 0, 5, 0, 5); - if (this->getInputOperation(1)->determineDependingAreaOfInterest( - &dispInput, readOperation, output)) { + rcti disp_input; + BLI_rcti_init(&disp_input, 0, 5, 0, 5); + if (this->get_input_operation(1)->determine_depending_area_of_interest( + &disp_input, read_operation, output)) { return true; } - newInput.xmin = input->xmin - 7; /* (0.25f * 20 * 1) + 2 == worse case dispersion */ - newInput.ymin = input->ymin; - newInput.ymax = input->ymax; - newInput.xmax = input->xmax + 7; /* (0.25f * 20 * 1) + 2 == worse case dispersion */ + new_input.xmin = input->xmin - 7; /* (0.25f * 20 * 1) + 2 == worse case dispersion */ + new_input.ymin = input->ymin; + new_input.ymax = input->ymax; + new_input.xmax = input->xmax + 7; /* (0.25f * 20 * 1) + 2 == worse case dispersion */ } - if (this->getInputOperation(0)->determineDependingAreaOfInterest( - &newInput, readOperation, output)) { + if (this->get_input_operation(0)->determine_depending_area_of_interest( + &new_input, read_operation, output)) { return true; } return false; } /* TODO(manzanilla): to be removed with tiled implementation. */ -void ProjectorLensDistortionOperation::updateDispersion() +void ProjectorLensDistortionOperation::update_dispersion() { - if (dispersionAvailable_) { + if (dispersion_available_) { return; } - this->lockMutex(); - if (!dispersionAvailable_) { + this->lock_mutex(); + if (!dispersion_available_) { float result[4]; - this->getInputSocketReader(1)->readSampled(result, 1, 1, PixelSampler::Nearest); + this->get_input_socket_reader(1)->read_sampled(result, 1, 1, PixelSampler::Nearest); dispersion_ = result[0]; kr_ = 0.25f * max_ff(min_ff(dispersion_, 1.0f), 0.0f); kr2_ = kr_ * 20; - dispersionAvailable_ = true; + dispersion_available_ = true; } - this->unlockMutex(); + this->unlock_mutex(); } void ProjectorLensDistortionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -165,8 +165,8 @@ void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer Span inputs) { const MemoryBuffer *input_image = inputs[0]; - const float height = this->getHeight(); - const float width = this->getWidth(); + const float height = this->get_height(); + const float width = this->get_width(); float color[4]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { const float v = (it.y + 0.5f) / height; diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h index af9b216db07..ea90840e61c 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h @@ -26,13 +26,13 @@ namespace blender::compositor { class ProjectorLensDistortionOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; float dispersion_; /* TODO(manzanilla): to be removed with tiled implementation. */ - bool dispersionAvailable_; + bool dispersion_available_; float kr_, kr2_; @@ -42,25 +42,25 @@ class ProjectorLensDistortionOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void init_data() override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void updateDispersion(); + void update_dispersion(); void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.cc b/source/blender/compositor/operations/COM_QualityStepHelper.cc index a45c7ab552c..895e341d550 100644 --- a/source/blender/compositor/operations/COM_QualityStepHelper.cc +++ b/source/blender/compositor/operations/COM_QualityStepHelper.cc @@ -27,7 +27,7 @@ QualityStepHelper::QualityStepHelper() offsetadd_ = 4; } -void QualityStepHelper::initExecution(QualityHelper helper) +void QualityStepHelper::init_execution(QualityHelper helper) { switch (helper) { case COM_QH_INCREASE: diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.h b/source/blender/compositor/operations/COM_QualityStepHelper.h index 5c59f4d2a37..6f168babeae 100644 --- a/source/blender/compositor/operations/COM_QualityStepHelper.h +++ b/source/blender/compositor/operations/COM_QualityStepHelper.h @@ -37,13 +37,13 @@ class QualityStepHelper { /** * Initialize the execution */ - void initExecution(QualityHelper helper); + void init_execution(QualityHelper helper); - inline int getStep() const + inline int get_step() const { return step_; } - inline int getOffsetAdd() const + inline int get_offset_add() const { return offsetadd_; } @@ -51,7 +51,7 @@ class QualityStepHelper { public: QualityStepHelper(); - void setQuality(eCompositorQuality quality) + void set_quality(eCompositorQuality quality) { quality_ = quality; } diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cc b/source/blender/compositor/operations/COM_ReadBufferOperation.cc index 9fe800021e7..75b4c61a376 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.cc +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cc @@ -25,39 +25,39 @@ namespace blender::compositor { ReadBufferOperation::ReadBufferOperation(DataType datatype) { - this->addOutputSocket(datatype); + this->add_output_socket(datatype); single_value_ = false; offset_ = 0; buffer_ = nullptr; flags.is_read_buffer_operation = true; } -void *ReadBufferOperation::initializeTileData(rcti * /*rect*/) +void *ReadBufferOperation::initialize_tile_data(rcti * /*rect*/) { return buffer_; } void ReadBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (memoryProxy_ != nullptr) { - WriteBufferOperation *operation = memoryProxy_->getWriteBufferOperation(); + if (memory_proxy_ != nullptr) { + WriteBufferOperation *operation = memory_proxy_->get_write_buffer_operation(); operation->determine_canvas(preferred_area, r_area); operation->set_canvas(r_area); /** \todo may not occur! But does with blur node. */ - if (memoryProxy_->getExecutor()) { + if (memory_proxy_->get_executor()) { uint resolution[2] = {static_cast(BLI_rcti_size_x(&r_area)), static_cast(BLI_rcti_size_y(&r_area))}; - memoryProxy_->getExecutor()->setResolution(resolution); + memory_proxy_->get_executor()->set_resolution(resolution); } - single_value_ = operation->isSingleValue(); + single_value_ = operation->is_single_value(); } } -void ReadBufferOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ReadBufferOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { if (single_value_) { /* write buffer has a single value stored at (0,0) */ @@ -70,21 +70,21 @@ void ReadBufferOperation::executePixelSampled(float output[4], break; case PixelSampler::Bilinear: default: - buffer_->readBilinear(output, x, y); + buffer_->read_bilinear(output, x, y); break; case PixelSampler::Bicubic: - buffer_->readBilinear(output, x, y); + buffer_->read_bilinear(output, x, y); break; } } } -void ReadBufferOperation::executePixelExtend(float output[4], - float x, - float y, - PixelSampler sampler, - MemoryBufferExtend extend_x, - MemoryBufferExtend extend_y) +void ReadBufferOperation::execute_pixel_extend(float output[4], + float x, + float y, + PixelSampler sampler, + MemoryBufferExtend extend_x, + MemoryBufferExtend extend_y) { if (single_value_) { /* write buffer has a single value stored at (0,0) */ @@ -94,11 +94,11 @@ void ReadBufferOperation::executePixelExtend(float output[4], buffer_->read(output, x, y, extend_x, extend_y); } else { - buffer_->readBilinear(output, x, y, extend_x, extend_y); + buffer_->read_bilinear(output, x, y, extend_x, extend_y); } } -void ReadBufferOperation::executePixelFiltered( +void ReadBufferOperation::execute_pixel_filtered( float output[4], float x, float y, float dx[2], float dy[2]) { if (single_value_) { @@ -112,29 +112,29 @@ void ReadBufferOperation::executePixelFiltered( } } -bool ReadBufferOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool ReadBufferOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - if (this == readOperation) { + if (this == read_operation) { BLI_rcti_init(output, input->xmin, input->xmax, input->ymin, input->ymax); return true; } return false; } -void ReadBufferOperation::readResolutionFromWriteBuffer() +void ReadBufferOperation::read_resolution_from_write_buffer() { - if (memoryProxy_ != nullptr) { - WriteBufferOperation *operation = memoryProxy_->getWriteBufferOperation(); - this->setWidth(operation->getWidth()); - this->setHeight(operation->getHeight()); + if (memory_proxy_ != nullptr) { + WriteBufferOperation *operation = memory_proxy_->get_write_buffer_operation(); + this->set_width(operation->get_width()); + this->set_height(operation->get_height()); } } -void ReadBufferOperation::updateMemoryBuffer() +void ReadBufferOperation::update_memory_buffer() { - buffer_ = this->getMemoryProxy()->getBuffer(); + buffer_ = this->get_memory_proxy()->get_buffer(); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.h b/source/blender/compositor/operations/COM_ReadBufferOperation.h index f4504e11f48..7f461d1c925 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.h +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.h @@ -26,51 +26,52 @@ namespace blender::compositor { class ReadBufferOperation : public NodeOperation { private: - MemoryProxy *memoryProxy_; + MemoryProxy *memory_proxy_; bool single_value_; /* single value stored in buffer, copied from associated write operation */ unsigned int offset_; MemoryBuffer *buffer_; public: ReadBufferOperation(DataType datatype); - void setMemoryProxy(MemoryProxy *memoryProxy) + void set_memory_proxy(MemoryProxy *memory_proxy) { - memoryProxy_ = memoryProxy; + memory_proxy_ = memory_proxy; } - MemoryProxy *getMemoryProxy() const + MemoryProxy *get_memory_proxy() const { - return memoryProxy_; + return memory_proxy_; } void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void *initializeTileData(rcti *rect) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void executePixelExtend(float output[4], - float x, - float y, - PixelSampler sampler, - MemoryBufferExtend extend_x, - MemoryBufferExtend extend_y); - void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; - void setOffset(unsigned int offset) + void *initialize_tile_data(rcti *rect) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_extend(float output[4], + float x, + float y, + PixelSampler sampler, + MemoryBufferExtend extend_x, + MemoryBufferExtend extend_y); + void execute_pixel_filtered( + float output[4], float x, float y, float dx[2], float dy[2]) override; + void set_offset(unsigned int offset) { offset_ = offset; } - unsigned int getOffset() const + unsigned int get_offset() const { return offset_; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - MemoryBuffer *getInputMemoryBuffer(MemoryBuffer **memoryBuffers) override + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + MemoryBuffer *get_input_memory_buffer(MemoryBuffer **memory_buffers) override { - return memoryBuffers[offset_]; + return memory_buffers[offset_]; } - void readResolutionFromWriteBuffer(); - void updateMemoryBuffer(); + void read_resolution_from_write_buffer(); + void update_memory_buffer(); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc index 02a278899bb..7650def2c87 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cc +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc @@ -24,21 +24,21 @@ namespace blender::compositor { /* ******** Render Layers Base Prog ******** */ -RenderLayersProg::RenderLayersProg(const char *passName, DataType type, int elementsize) - : passName_(passName) +RenderLayersProg::RenderLayersProg(const char *pass_name, DataType type, int elementsize) + : pass_name_(pass_name) { - this->setScene(nullptr); - inputBuffer_ = nullptr; + this->set_scene(nullptr); + input_buffer_ = nullptr; elementsize_ = elementsize; rd_ = nullptr; layer_buffer_ = nullptr; - this->addOutputSocket(type); + this->add_output_socket(type); } -void RenderLayersProg::initExecution() +void RenderLayersProg::init_execution() { - Scene *scene = this->getScene(); + Scene *scene = this->get_scene(); Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr; RenderResult *rr = nullptr; @@ -47,14 +47,14 @@ void RenderLayersProg::initExecution() } if (rr) { - ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, getLayerId()); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, get_layer_id()); if (view_layer) { RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name); if (rl) { - inputBuffer_ = RE_RenderLayerGetPass(rl, passName_.c_str(), viewName_); - if (inputBuffer_) { - layer_buffer_ = new MemoryBuffer(inputBuffer_, elementsize_, getWidth(), getHeight()); + input_buffer_ = RE_RenderLayerGetPass(rl, pass_name_.c_str(), view_name_); + if (input_buffer_) { + layer_buffer_ = new MemoryBuffer(input_buffer_, elementsize_, get_width(), get_height()); } } } @@ -65,10 +65,10 @@ void RenderLayersProg::initExecution() } } -void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelSampler sampler) +void RenderLayersProg::do_interpolation(float output[4], float x, float y, PixelSampler sampler) { unsigned int offset; - int width = this->getWidth(), height = this->getHeight(); + int width = this->get_width(), height = this->get_height(); int ix = x, iy = y; if (ix < 0 || iy < 0 || ix >= width || iy >= height) { @@ -89,28 +89,31 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS offset = (iy * width + ix) * elementsize_; if (elementsize_ == 1) { - output[0] = inputBuffer_[offset]; + output[0] = input_buffer_[offset]; } else if (elementsize_ == 3) { - copy_v3_v3(output, &inputBuffer_[offset]); + copy_v3_v3(output, &input_buffer_[offset]); } else { - copy_v4_v4(output, &inputBuffer_[offset]); + copy_v4_v4(output, &input_buffer_[offset]); } break; } case PixelSampler::Bilinear: - BLI_bilinear_interpolation_fl(inputBuffer_, output, width, height, elementsize_, x, y); + BLI_bilinear_interpolation_fl(input_buffer_, output, width, height, elementsize_, x, y); break; case PixelSampler::Bicubic: - BLI_bicubic_interpolation_fl(inputBuffer_, output, width, height, elementsize_, x, y); + BLI_bicubic_interpolation_fl(input_buffer_, output, width, height, elementsize_, x, y); break; } } -void RenderLayersProg::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void RenderLayersProg::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { #if 0 const RenderData *rd = rd_; @@ -118,14 +121,14 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi int dx = 0, dy = 0; if (rd->mode & R_BORDER && rd->mode & R_CROP) { - /* see comment in executeRegion describing coordinate mapping, + /* see comment in execute_region describing coordinate mapping, * here it simply goes other way around */ int full_width = rd->xsch * rd->size / 100; int full_height = rd->ysch * rd->size / 100; - dx = rd->border.xmin * full_width - (full_width - this->getWidth()) / 2.0f; - dy = rd->border.ymin * full_height - (full_height - this->getHeight()) / 2.0f; + dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f; + dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f; } int ix = x - dx; @@ -134,7 +137,7 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi #ifndef NDEBUG { - const DataType data_type = this->getOutputSocket()->getDataType(); + const DataType data_type = this->get_output_socket()->get_data_type(); int actual_element_size = elementsize_; int expected_element_size; if (data_type == DataType::Value) { @@ -154,7 +157,7 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi } #endif - if (inputBuffer_ == nullptr) { + if (input_buffer_ == nullptr) { int elemsize = elementsize_; if (elemsize == 1) { output[0] = 0.0f; @@ -168,13 +171,13 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi } } else { - doInterpolation(output, x, y, sampler); + do_interpolation(output, x, y, sampler); } } -void RenderLayersProg::deinitExecution() +void RenderLayersProg::deinit_execution() { - inputBuffer_ = nullptr; + input_buffer_ = nullptr; if (layer_buffer_) { delete layer_buffer_; layer_buffer_ = nullptr; @@ -183,7 +186,7 @@ void RenderLayersProg::deinitExecution() void RenderLayersProg::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - Scene *sce = this->getScene(); + Scene *sce = this->get_scene(); Render *re = (sce) ? RE_GetSceneRender(sce) : nullptr; RenderResult *rr = nullptr; @@ -194,7 +197,7 @@ void RenderLayersProg::determine_canvas(const rcti &UNUSED(preferred_area), rcti } if (rr) { - ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&sce->view_layers, getLayerId()); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&sce->view_layers, get_layer_id()); if (view_layer) { RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name); if (rl) { @@ -208,9 +211,9 @@ void RenderLayersProg::determine_canvas(const rcti &UNUSED(preferred_area), rcti } } -std::unique_ptr RenderLayersProg::getMetaData() +std::unique_ptr RenderLayersProg::get_meta_data() { - Scene *scene = this->getScene(); + Scene *scene = this->get_scene(); Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr; RenderResult *render_result = nullptr; MetaDataExtractCallbackData callback_data = {nullptr}; @@ -220,15 +223,15 @@ std::unique_ptr RenderLayersProg::getMetaData() } if (render_result && render_result->stamp_data) { - ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, getLayerId()); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, get_layer_id()); if (view_layer) { std::string full_layer_name = std::string( view_layer->name, BLI_strnlen(view_layer->name, sizeof(view_layer->name))) + - "." + passName_; + "." + pass_name_; blender::StringRef cryptomatte_layer_name = blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name(full_layer_name); - callback_data.setCryptomatteKeys(cryptomatte_layer_name); + callback_data.set_cryptomatte_keys(cryptomatte_layer_name); BKE_stamp_info_callback(&callback_data, render_result->stamp_data, @@ -260,17 +263,17 @@ void RenderLayersProg::update_memory_buffer_partial(MemoryBuffer *output, } /* ******** Render Layers AO Operation ******** */ -void RenderLayersAOOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void RenderLayersAOOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float *inputBuffer = this->getInputBuffer(); - if (inputBuffer == nullptr) { + float *input_buffer = this->get_input_buffer(); + if (input_buffer == nullptr) { zero_v3(output); } else { - doInterpolation(output, x, y, sampler); + do_interpolation(output, x, y, sampler); } output[3] = 1.0f; } @@ -291,19 +294,19 @@ void RenderLayersAOOperation::update_memory_buffer_partial(MemoryBuffer *output, } /* ******** Render Layers Alpha Operation ******** */ -void RenderLayersAlphaProg::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void RenderLayersAlphaProg::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float *inputBuffer = this->getInputBuffer(); + float *input_buffer = this->get_input_buffer(); - if (inputBuffer == nullptr) { + if (input_buffer == nullptr) { output[0] = 0.0f; } else { float temp[4]; - doInterpolation(temp, x, y, sampler); + do_interpolation(temp, x, y, sampler); output[0] = temp[3]; } } @@ -323,22 +326,22 @@ void RenderLayersAlphaProg::update_memory_buffer_partial(MemoryBuffer *output, } /* ******** Render Layers Depth Operation ******** */ -void RenderLayersDepthProg::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void RenderLayersDepthProg::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { int ix = x; int iy = y; - float *inputBuffer = this->getInputBuffer(); + float *input_buffer = this->get_input_buffer(); - if (inputBuffer == nullptr || ix < 0 || iy < 0 || ix >= (int)this->getWidth() || - iy >= (int)this->getHeight()) { + if (input_buffer == nullptr || ix < 0 || iy < 0 || ix >= (int)this->get_width() || + iy >= (int)this->get_height()) { output[0] = 10e10f; } else { - unsigned int offset = (iy * this->getWidth() + ix); - output[0] = inputBuffer[offset]; + unsigned int offset = (iy * this->get_width() + ix); + output[0] = input_buffer[offset]; } } diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h index 14fc3babe32..79afe3c2a8c 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.h +++ b/source/blender/compositor/operations/COM_RenderLayersProg.h @@ -41,14 +41,14 @@ class RenderLayersProg : public MultiThreadedOperation { Scene *scene_; /** - * layerId of the layer where this operation needs to get its data from + * layer_id of the layer where this operation needs to get its data from */ - short layerId_; + short layer_id_; /** - * viewName of the view to use (unless another view is specified by the node + * view_name of the view to use (unless another view is specified by the node */ - const char *viewName_; + const char *view_name_; const MemoryBuffer *layer_buffer_; @@ -56,12 +56,12 @@ class RenderLayersProg : public MultiThreadedOperation { * Cached instance to the float buffer inside the layer. * TODO: To be removed with tiled implementation. */ - float *inputBuffer_; + float *input_buffer_; /** * Render-pass where this operation needs to get its data from. */ - std::string passName_; + std::string pass_name_; int elementsize_; @@ -78,56 +78,56 @@ class RenderLayersProg : public MultiThreadedOperation { /** * retrieve the reference to the float buffer of the renderer. */ - inline float *getInputBuffer() + inline float *get_input_buffer() { - return inputBuffer_; + return input_buffer_; } - void doInterpolation(float output[4], float x, float y, PixelSampler sampler); + void do_interpolation(float output[4], float x, float y, PixelSampler sampler); public: /** * Constructor */ - RenderLayersProg(const char *passName, DataType type, int elementsize); + RenderLayersProg(const char *pass_name, DataType type, int elementsize); /** * setter for the scene field. Will be called from * \see RenderLayerNode to set the actual scene where * the data will be retrieved from. */ - void setScene(Scene *scene) + void set_scene(Scene *scene) { scene_ = scene; } - Scene *getScene() const + Scene *get_scene() const { return scene_; } - void setRenderData(const RenderData *rd) + void set_render_data(const RenderData *rd) { rd_ = rd; } - void setLayerId(short layerId) + void set_layer_id(short layer_id) { - layerId_ = layerId; + layer_id_ = layer_id; } - short getLayerId() const + short get_layer_id() const { - return layerId_; + return layer_id_; } - void setViewName(const char *viewName) + void set_view_name(const char *view_name) { - viewName_ = viewName; + view_name_ = view_name; } - const char *getViewName() + const char *get_view_name() { - return viewName_; + return view_name_; } - void initExecution() override; - void deinitExecution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_execution() override; + void deinit_execution() override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - std::unique_ptr getMetaData() override; + std::unique_ptr get_meta_data() override; virtual void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -136,11 +136,11 @@ class RenderLayersProg : public MultiThreadedOperation { class RenderLayersAOOperation : public RenderLayersProg { public: - RenderLayersAOOperation(const char *passName, DataType type, int elementsize) - : RenderLayersProg(passName, type, elementsize) + RenderLayersAOOperation(const char *pass_name, DataType type, int elementsize) + : RenderLayersProg(pass_name, type, elementsize) { } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -149,11 +149,11 @@ class RenderLayersAOOperation : public RenderLayersProg { class RenderLayersAlphaProg : public RenderLayersProg { public: - RenderLayersAlphaProg(const char *passName, DataType type, int elementsize) - : RenderLayersProg(passName, type, elementsize) + RenderLayersAlphaProg(const char *pass_name, DataType type, int elementsize) + : RenderLayersProg(pass_name, type, elementsize) { } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -162,11 +162,11 @@ class RenderLayersAlphaProg : public RenderLayersProg { class RenderLayersDepthProg : public RenderLayersProg { public: - RenderLayersDepthProg(const char *passName, DataType type, int elementsize) - : RenderLayersProg(passName, type, elementsize) + RenderLayersDepthProg(const char *pass_name, DataType type, int elementsize) + : RenderLayersProg(pass_name, type, elementsize) { } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc index 9cef80c14b9..8129516f81c 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.cc +++ b/source/blender/compositor/operations/COM_RotateOperation.cc @@ -22,14 +22,14 @@ namespace blender::compositor { RotateOperation::RotateOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); - this->addInputSocket(DataType::Value, ResizeMode::None); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color, ResizeMode::None); + this->add_input_socket(DataType::Value, ResizeMode::None); + this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); - imageSocket_ = nullptr; - degreeSocket_ = nullptr; - doDegree2RadConversion_ = false; - isDegreeSet_ = false; + image_socket_ = nullptr; + degree_socket_ = nullptr; + do_degree2_rad_conversion_ = false; + is_degree_set_ = false; sampler_ = PixelSampler::Bilinear; } @@ -127,29 +127,29 @@ void RotateOperation::get_rotation_canvas(const rcti &input_canvas, void RotateOperation::init_data() { if (execution_model_ == eExecutionModel::Tiled) { - get_rotation_center(get_canvas(), centerX_, centerY_); + get_rotation_center(get_canvas(), center_x_, center_y_); } } -void RotateOperation::initExecution() +void RotateOperation::init_execution() { - imageSocket_ = this->getInputSocketReader(0); - degreeSocket_ = this->getInputSocketReader(1); + image_socket_ = this->get_input_socket_reader(0); + degree_socket_ = this->get_input_socket_reader(1); } -void RotateOperation::deinitExecution() +void RotateOperation::deinit_execution() { - imageSocket_ = nullptr; - degreeSocket_ = nullptr; + image_socket_ = nullptr; + degree_socket_ = nullptr; } -inline void RotateOperation::ensureDegree() +inline void RotateOperation::ensure_degree() { - if (!isDegreeSet_) { + if (!is_degree_set_) { float degree[4]; switch (execution_model_) { case eExecutionModel::Tiled: - degreeSocket_->readSampled(degree, 0, 0, PixelSampler::Nearest); + degree_socket_->read_sampled(degree, 0, 0, PixelSampler::Nearest); break; case eExecutionModel::FullFrame: degree[0] = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f); @@ -157,7 +157,7 @@ inline void RotateOperation::ensureDegree() } double rad; - if (doDegree2RadConversion_) { + if (do_degree2_rad_conversion_) { rad = DEG2RAD((double)degree[0]); } else { @@ -166,51 +166,54 @@ inline void RotateOperation::ensureDegree() cosine_ = cos(rad); sine_ = sin(rad); - isDegreeSet_ = true; + is_degree_set_ = true; } } -void RotateOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void RotateOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - ensureDegree(); - const float dy = y - centerY_; - const float dx = x - centerX_; - const float nx = centerX_ + (cosine_ * dx + sine_ * dy); - const float ny = centerY_ + (-sine_ * dx + cosine_ * dy); - imageSocket_->readSampled(output, nx, ny, sampler); + ensure_degree(); + const float dy = y - center_y_; + const float dx = x - center_x_; + const float nx = center_x_ + (cosine_ * dx + sine_ * dy); + const float ny = center_y_ + (-sine_ * dx + cosine_ * dy); + image_socket_->read_sampled(output, nx, ny, sampler); } -bool RotateOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool RotateOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - ensureDegree(); - rcti newInput; + ensure_degree(); + rcti new_input; - const float dxmin = input->xmin - centerX_; - const float dymin = input->ymin - centerY_; - const float dxmax = input->xmax - centerX_; - const float dymax = input->ymax - centerY_; + const float dxmin = input->xmin - center_x_; + const float dymin = input->ymin - center_y_; + const float dxmax = input->xmax - center_x_; + const float dymax = input->ymax - center_y_; - const float x1 = centerX_ + (cosine_ * dxmin + sine_ * dymin); - const float x2 = centerX_ + (cosine_ * dxmax + sine_ * dymin); - const float x3 = centerX_ + (cosine_ * dxmin + sine_ * dymax); - const float x4 = centerX_ + (cosine_ * dxmax + sine_ * dymax); - const float y1 = centerY_ + (-sine_ * dxmin + cosine_ * dymin); - const float y2 = centerY_ + (-sine_ * dxmax + cosine_ * dymin); - const float y3 = centerY_ + (-sine_ * dxmin + cosine_ * dymax); - const float y4 = centerY_ + (-sine_ * dxmax + cosine_ * dymax); + const float x1 = center_x_ + (cosine_ * dxmin + sine_ * dymin); + const float x2 = center_x_ + (cosine_ * dxmax + sine_ * dymin); + const float x3 = center_x_ + (cosine_ * dxmin + sine_ * dymax); + const float x4 = center_x_ + (cosine_ * dxmax + sine_ * dymax); + const float y1 = center_y_ + (-sine_ * dxmin + cosine_ * dymin); + const float y2 = center_y_ + (-sine_ * dxmax + cosine_ * dymin); + const float y3 = center_y_ + (-sine_ * dxmin + cosine_ * dymax); + const float y4 = center_y_ + (-sine_ * dxmax + cosine_ * dymax); const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4))); const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4))); const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4))); const float maxy = MAX2(y1, MAX2(y2, MAX2(y3, y4))); - newInput.xmax = ceil(maxx) + 1; - newInput.xmin = floor(minx) - 1; - newInput.ymax = ceil(maxy) + 1; - newInput.ymin = floor(miny) - 1; + new_input.xmax = ceil(maxx) + 1; + new_input.xmin = floor(minx) - 1; + new_input.ymax = ceil(maxy) + 1; + new_input.ymin = floor(miny) - 1; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void RotateOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -221,13 +224,13 @@ void RotateOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) } const bool image_determined = - getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + get_input_socket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); if (image_determined) { rcti input_canvas = r_area; rcti unused; - getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(input_canvas, unused); + get_input_socket(DEGREE_INPUT_INDEX)->determine_canvas(input_canvas, unused); - ensureDegree(); + ensure_degree(); get_rotation_canvas(input_canvas, sine_, cosine_, r_area); } @@ -242,7 +245,7 @@ void RotateOperation::get_area_of_interest(const int input_idx, return; } - ensureDegree(); + ensure_degree(); const rcti &input_image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas(); get_rotation_area_of_interest( diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h index d99c914bed7..3970d3a2776 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.h +++ b/source/blender/compositor/operations/COM_RotateOperation.h @@ -27,16 +27,16 @@ class RotateOperation : public MultiThreadedOperation { constexpr static int IMAGE_INPUT_INDEX = 0; constexpr static int DEGREE_INPUT_INDEX = 1; - SocketReader *imageSocket_; - SocketReader *degreeSocket_; + SocketReader *image_socket_; + SocketReader *degree_socket_; /* TODO(manzanilla): to be removed with tiled implementation. */ - float centerX_; - float centerY_; + float center_x_; + float center_y_; float cosine_; float sine_; - bool doDegree2RadConversion_; - bool isDegreeSet_; + bool do_degree2_rad_conversion_; + bool is_degree_set_; PixelSampler sampler_; public: @@ -79,17 +79,17 @@ class RotateOperation : public MultiThreadedOperation { const float cosine, rcti &r_canvas); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void init_data() override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setDoDegree2RadConversion(bool abool) + void set_do_degree2_rad_conversion(bool abool) { - doDegree2RadConversion_ = abool; + do_degree2_rad_conversion_ = abool; } void set_sampler(PixelSampler sampler) @@ -97,7 +97,7 @@ class RotateOperation : public MultiThreadedOperation { sampler_ = sampler; } - void ensureDegree(); + void ensure_degree(); void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index 40af7389d7f..c73462d2e6d 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -38,7 +38,7 @@ namespace blender::compositor { * * This file is based on SMAA-CPP: * - * https://github.com/iRi-E/smaa-cpp + * https://github.com/i_ri-E/smaa-cpp * * Currently only SMAA 1x mode is provided, so the operation will be done * with no spatial multi-sampling nor temporal super-sampling. @@ -64,7 +64,7 @@ namespace blender::compositor { * #buffer->read_elem_checked. */ static inline void sample(SocketReader *reader, int x, int y, float color[4]) { - if (x < 0 || x >= reader->getWidth() || y < 0 || y >= reader->getHeight()) { + if (x < 0 || x >= reader->get_width() || y < 0 || y >= reader->get_height()) { color[0] = color[1] = color[2] = color[3] = 0.0; return; } @@ -167,50 +167,50 @@ static void area_diag(int d1, int d2, int e1, int e2, float weights[2]) SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation() { - this->addInputSocket(DataType::Color); /* image */ - this->addInputSocket(DataType::Value); /* Depth, material ID, etc. TODO: currently unused. */ - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); /* image */ + this->add_input_socket(DataType::Value); /* Depth, material ID, etc. TODO: currently unused. */ + this->add_output_socket(DataType::Color); this->flags.complex = true; - imageReader_ = nullptr; - valueReader_ = nullptr; - this->setThreshold(CMP_DEFAULT_SMAA_THRESHOLD); - this->setLocalContrastAdaptationFactor(CMP_DEFAULT_SMAA_CONTRAST_LIMIT); + image_reader_ = nullptr; + value_reader_ = nullptr; + this->set_threshold(CMP_DEFAULT_SMAA_THRESHOLD); + this->set_local_contrast_adaptation_factor(CMP_DEFAULT_SMAA_CONTRAST_LIMIT); } -void SMAAEdgeDetectionOperation::initExecution() +void SMAAEdgeDetectionOperation::init_execution() { - imageReader_ = this->getInputSocketReader(0); - valueReader_ = this->getInputSocketReader(1); + image_reader_ = this->get_input_socket_reader(0); + value_reader_ = this->get_input_socket_reader(1); } -void SMAAEdgeDetectionOperation::deinitExecution() +void SMAAEdgeDetectionOperation::deinit_execution() { - imageReader_ = nullptr; - valueReader_ = nullptr; + image_reader_ = nullptr; + value_reader_ = nullptr; } -void SMAAEdgeDetectionOperation::setThreshold(float threshold) +void SMAAEdgeDetectionOperation::set_threshold(float threshold) { /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 0.5 */ threshold_ = scalenorm(0, 0.5, threshold); } -void SMAAEdgeDetectionOperation::setLocalContrastAdaptationFactor(float factor) +void SMAAEdgeDetectionOperation::set_local_contrast_adaptation_factor(float factor) { /* UI values are between 0 and 1 for simplicity but algorithm expects values between 1 and 10 */ contrast_limit_ = scalenorm(1, 10, factor); } -bool SMAAEdgeDetectionOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool SMAAEdgeDetectionOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - newInput.xmax = input->xmax + 1; - newInput.xmin = input->xmin - 2; - newInput.ymax = input->ymax + 1; - newInput.ymin = input->ymin - 2; + rcti new_input; + new_input.xmax = input->xmax + 1; + new_input.xmin = input->xmin - 2; + new_input.ymax = input->ymax + 1; + new_input.ymin = input->ymin - 2; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void SMAAEdgeDetectionOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -223,16 +223,16 @@ void SMAAEdgeDetectionOperation::get_area_of_interest(const int UNUSED(input_idx r_input_area.ymin = output_area.ymin - 2; } -void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void SMAAEdgeDetectionOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { float color[4]; /* Calculate luma deltas: */ - sample(imageReader_, x, y, color); + sample(image_reader_, x, y, color); float L = IMB_colormanagement_get_luminance(color); - sample(imageReader_, x - 1, y, color); + sample(image_reader_, x - 1, y, color); float Lleft = IMB_colormanagement_get_luminance(color); - sample(imageReader_, x, y - 1, color); + sample(image_reader_, x, y - 1, color); float Ltop = IMB_colormanagement_get_luminance(color); float Dleft = fabsf(L - Lleft); float Dtop = fabsf(L - Ltop); @@ -249,36 +249,36 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi } /* Calculate right and bottom deltas: */ - sample(imageReader_, x + 1, y, color); + sample(image_reader_, x + 1, y, color); float Lright = IMB_colormanagement_get_luminance(color); - sample(imageReader_, x, y + 1, color); + sample(image_reader_, x, y + 1, color); float Lbottom = IMB_colormanagement_get_luminance(color); float Dright = fabsf(L - Lright); float Dbottom = fabsf(L - Lbottom); /* Calculate the maximum delta in the direct neighborhood: */ - float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); + float max_delta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); /* Calculate luma used for both left and top edges: */ - sample(imageReader_, x - 1, y - 1, color); + sample(image_reader_, x - 1, y - 1, color); float Llefttop = IMB_colormanagement_get_luminance(color); /* Left edge */ if (output[0] != 0.0f) { /* Calculate deltas around the left pixel: */ - sample(imageReader_, x - 2, y, color); + sample(image_reader_, x - 2, y, color); float Lleftleft = IMB_colormanagement_get_luminance(color); - sample(imageReader_, x - 1, y + 1, color); + sample(image_reader_, x - 1, y + 1, color); float Lleftbottom = IMB_colormanagement_get_luminance(color); float Dleftleft = fabsf(Lleft - Lleftleft); float Dlefttop = fabsf(Lleft - Llefttop); float Dleftbottom = fabsf(Lleft - Lleftbottom); /* Calculate the final maximum delta: */ - maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); + max_delta = fmaxf(max_delta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); /* Local contrast adaptation: */ - if (maxDelta > contrast_limit_ * Dleft) { + if (max_delta > contrast_limit_ * Dleft) { output[0] = 0.0f; } } @@ -286,19 +286,19 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi /* Top edge */ if (output[1] != 0.0f) { /* Calculate top-top delta: */ - sample(imageReader_, x, y - 2, color); + sample(image_reader_, x, y - 2, color); float Ltoptop = IMB_colormanagement_get_luminance(color); - sample(imageReader_, x + 1, y - 1, color); + sample(image_reader_, x + 1, y - 1, color); float Ltopright = IMB_colormanagement_get_luminance(color); float Dtoptop = fabsf(Ltop - Ltoptop); float Dtopleft = fabsf(Ltop - Llefttop); float Dtopright = fabsf(Ltop - Ltopright); /* Calculate the final maximum delta: */ - maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); + max_delta = fmaxf(max_delta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); /* Local contrast adaptation: */ - if (maxDelta > contrast_limit_ * Dtop) { + if (max_delta > contrast_limit_ * Dtop) { output[1] = 0.0f; } } @@ -344,7 +344,7 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp const float Dbottom = fabsf(L - Lbottom); /* Calculate the maximum delta in the direct neighborhood: */ - float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); + float max_delta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); /* Calculate luma used for both left and top edges: */ image->read_elem_checked(x - 1, y - 1, color); @@ -362,10 +362,10 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp const float Dleftbottom = fabsf(Lleft - Lleftbottom); /* Calculate the final maximum delta: */ - maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); + max_delta = fmaxf(max_delta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); /* Local contrast adaptation: */ - if (maxDelta > contrast_limit_ * Dleft) { + if (max_delta > contrast_limit_ * Dleft) { it.out[0] = 0.0f; } } @@ -382,10 +382,10 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp const float Dtopright = fabsf(Ltop - Ltopright); /* Calculate the final maximum delta: */ - maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); + max_delta = fmaxf(max_delta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); /* Local contrast adaptation: */ - if (maxDelta > contrast_limit_ * Dtop) { + if (max_delta > contrast_limit_ * Dtop) { it.out[1] = 0.0f; } } @@ -398,47 +398,47 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation() { - this->addInputSocket(DataType::Color); /* edges */ - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); /* edges */ + this->add_output_socket(DataType::Color); this->flags.complex = true; - imageReader_ = nullptr; - this->setCornerRounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING); + image_reader_ = nullptr; + this->set_corner_rounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING); } -void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect) +void *SMAABlendingWeightCalculationOperation::initialize_tile_data(rcti *rect) { - return getInputOperation(0)->initializeTileData(rect); + return get_input_operation(0)->initialize_tile_data(rect); } -void SMAABlendingWeightCalculationOperation::initExecution() +void SMAABlendingWeightCalculationOperation::init_execution() { - imageReader_ = this->getInputSocketReader(0); + image_reader_ = this->get_input_socket_reader(0); if (execution_model_ == eExecutionModel::Tiled) { - sample_image_fn_ = [=](int x, int y, float *out) { sample(imageReader_, x, y, out); }; + sample_image_fn_ = [=](int x, int y, float *out) { sample(image_reader_, x, y, out); }; } } -void SMAABlendingWeightCalculationOperation::setCornerRounding(float rounding) +void SMAABlendingWeightCalculationOperation::set_corner_rounding(float rounding) { /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 100 */ corner_rounding_ = static_cast(scalenorm(0, 100, rounding)); } -void SMAABlendingWeightCalculationOperation::executePixel(float output[4], - int x, - int y, - void * /*data*/) +void SMAABlendingWeightCalculationOperation::execute_pixel(float output[4], + int x, + int y, + void * /*data*/) { float edges[4], c[4]; zero_v4(output); - sample(imageReader_, x, y, edges); + sample(image_reader_, x, y, edges); /* Edge at north */ if (edges[1] > 0.0f) { /* Diagonals have both north and west edges, so calculating weights for them */ /* in one of the boundaries is enough. */ - calculateDiagWeights(x, y, edges, output); + calculate_diag_weights(x, y, edges, output); /* We give priority to diagonals, so if we find a diagonal we skip. */ /* horizontal/vertical processing. */ @@ -447,25 +447,25 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], } /* Find the distance to the left and the right: */ - int left = searchXLeft(x, y); - int right = searchXRight(x, y); + int left = search_xleft(x, y); + int right = search_xright(x, y); int d1 = x - left, d2 = right - x; /* Fetch the left and right crossing edges: */ int e1 = 0, e2 = 0; - sample(imageReader_, left, y - 1, c); + sample(image_reader_, left, y - 1, c); if (c[0] > 0.0) { e1 += 1; } - sample(imageReader_, left, y, c); + sample(image_reader_, left, y, c); if (c[0] > 0.0) { e1 += 2; } - sample(imageReader_, right + 1, y - 1, c); + sample(image_reader_, right + 1, y - 1, c); if (c[0] > 0.0) { e2 += 1; } - sample(imageReader_, right + 1, y, c); + sample(image_reader_, right + 1, y, c); if (c[0] > 0.0) { e2 += 2; } @@ -476,37 +476,37 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], /* Fix corners: */ if (corner_rounding_) { - detectHorizontalCornerPattern(output, left, right, y, d1, d2); + detect_horizontal_corner_pattern(output, left, right, y, d1, d2); } } /* Edge at west */ if (edges[0] > 0.0f) { /* Did we already do diagonal search for this west edge from the left neighboring pixel? */ - if (isVerticalSearchUnneeded(x, y)) { + if (is_vertical_search_unneeded(x, y)) { return; } /* Find the distance to the top and the bottom: */ - int top = searchYUp(x, y); - int bottom = searchYDown(x, y); + int top = search_yup(x, y); + int bottom = search_ydown(x, y); int d1 = y - top, d2 = bottom - y; /* Fetch the top and bottom crossing edges: */ int e1 = 0, e2 = 0; - sample(imageReader_, x - 1, top, c); + sample(image_reader_, x - 1, top, c); if (c[1] > 0.0) { e1 += 1; } - sample(imageReader_, x, top, c); + sample(image_reader_, x, top, c); if (c[1] > 0.0) { e1 += 2; } - sample(imageReader_, x - 1, bottom + 1, c); + sample(image_reader_, x - 1, bottom + 1, c); if (c[1] > 0.0) { e2 += 1; } - sample(imageReader_, x, bottom + 1, c); + sample(image_reader_, x, bottom + 1, c); if (c[1] > 0.0) { e2 += 2; } @@ -516,7 +516,7 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], /* Fix corners: */ if (corner_rounding_) { - detectVerticalCornerPattern(output + 2, x, top, bottom, d1, d2); + detect_vertical_corner_pattern(output + 2, x, top, bottom, d1, d2); } } } @@ -544,7 +544,7 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( if (edges[1] > 0.0f) { /* Diagonals have both north and west edges, so calculating weights for them */ /* in one of the boundaries is enough. */ - calculateDiagWeights(x, y, edges, it.out); + calculate_diag_weights(x, y, edges, it.out); /* We give priority to diagonals, so if we find a diagonal we skip. */ /* horizontal/vertical processing. */ @@ -553,8 +553,8 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( } /* Find the distance to the left and the right: */ - int left = searchXLeft(x, y); - int right = searchXRight(x, y); + int left = search_xleft(x, y); + int right = search_xright(x, y); int d1 = x - left, d2 = right - x; /* Fetch the left and right crossing edges: */ @@ -582,20 +582,20 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( /* Fix corners: */ if (corner_rounding_) { - detectHorizontalCornerPattern(it.out, left, right, y, d1, d2); + detect_horizontal_corner_pattern(it.out, left, right, y, d1, d2); } } /* Edge at west */ if (edges[0] > 0.0f) { /* Did we already do diagonal search for this west edge from the left neighboring pixel? */ - if (isVerticalSearchUnneeded(x, y)) { + if (is_vertical_search_unneeded(x, y)) { continue; } /* Find the distance to the top and the bottom: */ - int top = searchYUp(x, y); - int bottom = searchYDown(x, y); + int top = search_yup(x, y); + int bottom = search_ydown(x, y); int d1 = y - top, d2 = bottom - y; /* Fetch the top and bottom crossing edges: */ @@ -622,30 +622,30 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( /* Fix corners: */ if (corner_rounding_) { - detectVerticalCornerPattern(it.out + 2, x, top, bottom, d1, d2); + detect_vertical_corner_pattern(it.out + 2, x, top, bottom, d1, d2); } } } } -void SMAABlendingWeightCalculationOperation::deinitExecution() +void SMAABlendingWeightCalculationOperation::deinit_execution() { - imageReader_ = nullptr; + image_reader_ = nullptr; } -bool SMAABlendingWeightCalculationOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool SMAABlendingWeightCalculationOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = input->xmax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1); - newInput.xmin = input->xmin - - fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1); - newInput.ymax = input->ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG); - newInput.ymin = input->ymin - - fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG); + new_input.xmax = input->xmax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1); + new_input.xmin = input->xmin - + fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1); + new_input.ymax = input->ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG); + new_input.ymin = input->ymin - + fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG); - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void SMAABlendingWeightCalculationOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -667,7 +667,7 @@ void SMAABlendingWeightCalculationOperation::get_area_of_interest(const int UNUS /** * These functions allows to perform diagonal pattern searches. */ -int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, bool *found) +int SMAABlendingWeightCalculationOperation::search_diag1(int x, int y, int dir, bool *found) { float e[4]; int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir; @@ -690,7 +690,7 @@ int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, b return x - dir; } -int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, bool *found) +int SMAABlendingWeightCalculationOperation::search_diag2(int x, int y, int dir, bool *found) { float e[4]; int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir; @@ -717,10 +717,10 @@ int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, b /** * This searches for diagonal patterns and returns the corresponding weights. */ -void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, - int y, - const float edges[2], - float weights[2]) +void SMAABlendingWeightCalculationOperation::calculate_diag_weights(int x, + int y, + const float edges[2], + float weights[2]) { int d1, d2; bool d1_found, d2_found; @@ -734,13 +734,13 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Search for the line ends: */ if (edges[0] > 0.0f) { - d1 = x - searchDiag1(x, y, -1, &d1_found); + d1 = x - search_diag1(x, y, -1, &d1_found); } else { d1 = 0; d1_found = true; } - d2 = searchDiag1(x, y, 1, &d2_found) - x; + d2 = search_diag1(x, y, 1, &d2_found) - x; if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */ int e1 = 0, e2 = 0; @@ -778,10 +778,10 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, } /* Search for the line ends: */ - d1 = x - searchDiag2(x, y, -1, &d1_found); + d1 = x - search_diag2(x, y, -1, &d1_found); sample_image_fn_(x + 1, y, e); if (e[0] > 0.0f) { - d2 = searchDiag2(x, y, 1, &d2_found) - x; + d2 = search_diag2(x, y, 1, &d2_found) - x; } else { d2 = 0; @@ -826,7 +826,7 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, } } -bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int y) +bool SMAABlendingWeightCalculationOperation::is_vertical_search_unneeded(int x, int y) { int d1, d2; bool found; @@ -839,12 +839,12 @@ bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int /* Search for the line ends: */ sample_image_fn_(x - 1, y, e); if (e[1] > 0.0f) { - d1 = x - searchDiag2(x - 1, y, -1, &found); + d1 = x - search_diag2(x - 1, y, -1, &found); } else { d1 = 0; } - d2 = searchDiag2(x - 1, y, 1, &found) - x; + d2 = search_diag2(x - 1, y, 1, &found) - x; return (d1 + d2 > 2); /* d1 + d2 + 1 > 3 */ } @@ -852,7 +852,7 @@ bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int /*-----------------------------------------------------------------------------*/ /* Horizontal/Vertical Search Functions */ -int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y) +int SMAABlendingWeightCalculationOperation::search_xleft(int x, int y) { int end = x - SMAA_MAX_SEARCH_STEPS; float e[4]; @@ -875,7 +875,7 @@ int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y) return x + 1; } -int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y) +int SMAABlendingWeightCalculationOperation::search_xright(int x, int y) { int end = x + SMAA_MAX_SEARCH_STEPS; float e[4]; @@ -896,7 +896,7 @@ int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y) return x - 1; } -int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y) +int SMAABlendingWeightCalculationOperation::search_yup(int x, int y) { int end = y - SMAA_MAX_SEARCH_STEPS; float e[4]; @@ -919,7 +919,7 @@ int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y) return y + 1; } -int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y) +int SMAABlendingWeightCalculationOperation::search_ydown(int x, int y) { int end = y + SMAA_MAX_SEARCH_STEPS; float e[4]; @@ -943,7 +943,7 @@ int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y) /*-----------------------------------------------------------------------------*/ /* Corner Detection Functions */ -void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern( +void SMAABlendingWeightCalculationOperation::detect_horizontal_corner_pattern( float weights[2], int left, int right, int y, int d1, int d2) { float factor[2] = {1.0f, 1.0f}; @@ -972,7 +972,7 @@ void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern( weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f); } -void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern( +void SMAABlendingWeightCalculationOperation::detect_vertical_corner_pattern( float weights[2], int x, int top, int bottom, int d1, int d2) { float factor[2] = {1.0f, 1.0f}; @@ -1007,29 +1007,29 @@ void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern( SMAANeighborhoodBlendingOperation::SMAANeighborhoodBlendingOperation() { - this->addInputSocket(DataType::Color); /* image */ - this->addInputSocket(DataType::Color); /* blend */ - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); /* image */ + this->add_input_socket(DataType::Color); /* blend */ + this->add_output_socket(DataType::Color); this->flags.complex = true; image1Reader_ = nullptr; image2Reader_ = nullptr; } -void *SMAANeighborhoodBlendingOperation::initializeTileData(rcti *rect) +void *SMAANeighborhoodBlendingOperation::initialize_tile_data(rcti *rect) { - return getInputOperation(0)->initializeTileData(rect); + return get_input_operation(0)->initialize_tile_data(rect); } -void SMAANeighborhoodBlendingOperation::initExecution() +void SMAANeighborhoodBlendingOperation::init_execution() { - image1Reader_ = this->getInputSocketReader(0); - image2Reader_ = this->getInputSocketReader(1); + image1Reader_ = this->get_input_socket_reader(0); + image2Reader_ = this->get_input_socket_reader(1); } -void SMAANeighborhoodBlendingOperation::executePixel(float output[4], - int x, - int y, - void * /*data*/) +void SMAANeighborhoodBlendingOperation::execute_pixel(float output[4], + int x, + int y, + void * /*data*/) { float w[4]; @@ -1127,23 +1127,23 @@ void SMAANeighborhoodBlendingOperation::update_memory_buffer_partial(MemoryBuffe } } -void SMAANeighborhoodBlendingOperation::deinitExecution() +void SMAANeighborhoodBlendingOperation::deinit_execution() { image1Reader_ = nullptr; image2Reader_ = nullptr; } -bool SMAANeighborhoodBlendingOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool SMAANeighborhoodBlendingOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = input->xmax + 1; - newInput.xmin = input->xmin - 1; - newInput.ymax = input->ymax + 1; - newInput.ymin = input->ymin - 1; + new_input.xmax = input->xmax + 1; + new_input.xmin = input->xmin - 1; + new_input.ymax = input->ymax + 1; + new_input.ymin = input->ymin - 1; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void SMAANeighborhoodBlendingOperation::get_area_of_interest(const int UNUSED(input_idx), diff --git a/source/blender/compositor/operations/COM_SMAAOperation.h b/source/blender/compositor/operations/COM_SMAAOperation.h index d4c9e128dca..65a88d43fdf 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.h +++ b/source/blender/compositor/operations/COM_SMAAOperation.h @@ -29,8 +29,8 @@ namespace blender::compositor { class SMAAEdgeDetectionOperation : public MultiThreadedOperation { protected: - SocketReader *imageReader_; - SocketReader *valueReader_; + SocketReader *image_reader_; + SocketReader *value_reader_; float threshold_; float contrast_limit_; @@ -41,25 +41,25 @@ class SMAAEdgeDetectionOperation : public MultiThreadedOperation { /** * the inner loop of this program */ - virtual void executePixel(float output[4], int x, int y, void *data) override; + virtual void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setThreshold(float threshold); + void set_threshold(float threshold); - void setLocalContrastAdaptationFactor(float factor); + void set_local_contrast_adaptation_factor(float factor); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) 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, @@ -72,7 +72,7 @@ class SMAAEdgeDetectionOperation : public MultiThreadedOperation { class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { private: - SocketReader *imageReader_; + SocketReader *image_reader_; std::function sample_image_fn_; int corner_rounding_; @@ -82,24 +82,24 @@ class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { /** * the inner loop of this program */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; - void *initializeTileData(rcti *rect) override; + void init_execution() override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setCornerRounding(float rounding); + void set_corner_rounding(float rounding); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_started(MemoryBuffer *output, @@ -111,20 +111,22 @@ class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { private: /* Diagonal Search Functions */ - int searchDiag1(int x, int y, int dir, bool *found); - int searchDiag2(int x, int y, int dir, bool *found); - void calculateDiagWeights(int x, int y, const float edges[2], float weights[2]); - bool isVerticalSearchUnneeded(int x, int y); + int search_diag1(int x, int y, int dir, bool *found); + int search_diag2(int x, int y, int dir, bool *found); + void calculate_diag_weights(int x, int y, const float edges[2], float weights[2]); + bool is_vertical_search_unneeded(int x, int y); /* Horizontal/Vertical Search Functions */ - int searchXLeft(int x, int y); - int searchXRight(int x, int y); - int searchYUp(int x, int y); - int searchYDown(int x, int y); + int search_xleft(int x, int y); + int search_xright(int x, int y); + int search_yup(int x, int y); + int search_ydown(int x, int y); /* Corner Detection Functions */ - void detectHorizontalCornerPattern(float weights[2], int left, int right, int y, int d1, int d2); - void detectVerticalCornerPattern(float weights[2], int x, int top, int bottom, int d1, int d2); + void detect_horizontal_corner_pattern( + float weights[2], int left, int right, int y, int d1, int d2); + void detect_vertical_corner_pattern( + float weights[2], int x, int top, int bottom, int d1, int d2); }; /*-----------------------------------------------------------------------------*/ @@ -141,22 +143,22 @@ class SMAANeighborhoodBlendingOperation : public MultiThreadedOperation { /** * the inner loop of this program */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; - void *initializeTileData(rcti *rect) override; + void init_execution() override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) 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, diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index 82878c1276f..350934b0d3b 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -49,20 +49,21 @@ ScaleOperation::ScaleOperation() : ScaleOperation(DataType::Color) ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation() { - this->addInputSocket(data_type, ResizeMode::None); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(data_type); - inputOperation_ = nullptr; - inputXOperation_ = nullptr; - inputYOperation_ = nullptr; + this->add_input_socket(data_type, ResizeMode::None); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(data_type); + input_operation_ = nullptr; + input_xoperation_ = nullptr; + input_yoperation_ = nullptr; } float ScaleOperation::get_constant_scale(const int input_op_idx, const float factor) { - const bool is_constant = getInputOperation(input_op_idx)->get_flags().is_constant_operation; + const bool is_constant = get_input_operation(input_op_idx)->get_flags().is_constant_operation; if (is_constant) { - return ((ConstantOperation *)getInputOperation(input_op_idx))->get_constant_elem()[0] * factor; + return ((ConstantOperation *)get_input_operation(input_op_idx))->get_constant_elem()[0] * + factor; } return 1.0f; @@ -112,22 +113,22 @@ void ScaleOperation::clamp_area_size_max(rcti &area, Size2f max_size) void ScaleOperation::init_data() { - canvas_center_x_ = canvas_.xmin + getWidth() / 2.0f; - canvas_center_y_ = canvas_.ymin + getHeight() / 2.0f; + canvas_center_x_ = canvas_.xmin + get_width() / 2.0f; + canvas_center_y_ = canvas_.ymin + get_height() / 2.0f; } -void ScaleOperation::initExecution() +void ScaleOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); - inputXOperation_ = this->getInputSocketReader(1); - inputYOperation_ = this->getInputSocketReader(2); + input_operation_ = this->get_input_socket_reader(0); + input_xoperation_ = this->get_input_socket_reader(1); + input_yoperation_ = this->get_input_socket_reader(2); } -void ScaleOperation::deinitExecution() +void ScaleOperation::deinit_execution() { - inputOperation_ = nullptr; - inputXOperation_ = nullptr; - inputYOperation_ = nullptr; + input_operation_ = nullptr; + input_xoperation_ = nullptr; + input_yoperation_ = nullptr; } void ScaleOperation::get_scale_offset(const rcti &input_canvas, @@ -171,8 +172,8 @@ void ScaleOperation::get_area_of_interest(const int input_idx, } NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX); - const float scale_x = get_constant_scale_x(image_op->getWidth()); - const float scale_y = get_constant_scale_y(image_op->getHeight()); + const float scale_x = get_constant_scale_x(image_op->get_width()); + const float scale_y = get_constant_scale_y(image_op->get_height()); get_scale_area_of_interest( image_op->get_canvas(), this->get_canvas(), scale_x, scale_y, output_area, r_input_area); @@ -184,8 +185,8 @@ void ScaleOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { NodeOperation *input_image_op = get_input_operation(IMAGE_INPUT_INDEX); - const int input_image_width = input_image_op->getWidth(); - const int input_image_height = input_image_op->getHeight(); + const int input_image_width = input_image_op->get_width(); + const int input_image_height = input_image_op->get_height(); const float scale_x_factor = get_relative_scale_x_factor(input_image_width); const float scale_y_factor = get_relative_scale_y_factor(input_image_height); const float scale_center_x = input_image_width / 2.0f; @@ -219,12 +220,12 @@ void ScaleOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) } const bool image_determined = - getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + get_input_socket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); if (image_determined) { rcti image_canvas = r_area; rcti unused; - NodeOperationInput *x_socket = getInputSocket(X_INPUT_INDEX); - NodeOperationInput *y_socket = getInputSocket(Y_INPUT_INDEX); + NodeOperationInput *x_socket = get_input_socket(X_INPUT_INDEX); + NodeOperationInput *y_socket = get_input_socket(Y_INPUT_INDEX); x_socket->determine_canvas(image_canvas, unused); y_socket->determine_canvas(image_canvas, unused); if (is_scaling_variable()) { @@ -258,129 +259,128 @@ ScaleRelativeOperation::ScaleRelativeOperation(DataType data_type) : ScaleOperat { } -void ScaleRelativeOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ScaleRelativeOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - PixelSampler effective_sampler = getEffectiveSampler(sampler); + PixelSampler effective_sampler = get_effective_sampler(sampler); float scaleX[4]; float scaleY[4]; - inputXOperation_->readSampled(scaleX, x, y, effective_sampler); - inputYOperation_->readSampled(scaleY, x, y, effective_sampler); + input_xoperation_->read_sampled(scaleX, x, y, effective_sampler); + input_yoperation_->read_sampled(scaleY, x, y, effective_sampler); const float scx = scaleX[0]; const float scy = scaleY[0]; float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / scx; float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / scy; - inputOperation_->readSampled(output, nx, ny, effective_sampler); + input_operation_->read_sampled(output, nx, ny, effective_sampler); } -bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool ScaleRelativeOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; if (!variable_size_) { float scaleX[4]; float scaleY[4]; - inputXOperation_->readSampled(scaleX, 0, 0, PixelSampler::Nearest); - inputYOperation_->readSampled(scaleY, 0, 0, PixelSampler::Nearest); + input_xoperation_->read_sampled(scaleX, 0, 0, PixelSampler::Nearest); + input_yoperation_->read_sampled(scaleY, 0, 0, PixelSampler::Nearest); const float scx = scaleX[0]; const float scy = scaleY[0]; - newInput.xmax = this->canvas_center_x_ + (input->xmax - this->canvas_center_x_) / scx + 1; - newInput.xmin = this->canvas_center_x_ + (input->xmin - this->canvas_center_x_) / scx - 1; - newInput.ymax = this->canvas_center_y_ + (input->ymax - this->canvas_center_y_) / scy + 1; - newInput.ymin = this->canvas_center_y_ + (input->ymin - this->canvas_center_y_) / scy - 1; + new_input.xmax = this->canvas_center_x_ + (input->xmax - this->canvas_center_x_) / scx + 1; + new_input.xmin = this->canvas_center_x_ + (input->xmin - this->canvas_center_x_) / scx - 1; + new_input.ymax = this->canvas_center_y_ + (input->ymax - this->canvas_center_y_) / scy + 1; + new_input.ymin = this->canvas_center_y_ + (input->ymin - this->canvas_center_y_) / scy - 1; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return BaseScaleOperation::determine_depending_area_of_interest( + &new_input, read_operation, output); } -void ScaleAbsoluteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ScaleAbsoluteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - PixelSampler effective_sampler = getEffectiveSampler(sampler); + PixelSampler effective_sampler = get_effective_sampler(sampler); float scaleX[4]; float scaleY[4]; - inputXOperation_->readSampled(scaleX, x, y, effective_sampler); - inputYOperation_->readSampled(scaleY, x, y, effective_sampler); + input_xoperation_->read_sampled(scaleX, x, y, effective_sampler); + input_yoperation_->read_sampled(scaleY, x, y, effective_sampler); const float scx = scaleX[0]; /* Target absolute scale. */ const float scy = scaleY[0]; /* Target absolute scale. */ - const float width = this->getWidth(); - const float height = this->getHeight(); + const float width = this->get_width(); + const float height = this->get_height(); /* Divide. */ - float relativeXScale = scx / width; - float relativeYScale = scy / height; + float relative_xscale = scx / width; + float relative_yscale = scy / height; - float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / relativeXScale; - float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / relativeYScale; + float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / relative_xscale; + float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / relative_yscale; - inputOperation_->readSampled(output, nx, ny, effective_sampler); + input_operation_->read_sampled(output, nx, ny, effective_sampler); } -bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool ScaleAbsoluteOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; if (!variable_size_) { float scaleX[4]; float scaleY[4]; - inputXOperation_->readSampled(scaleX, 0, 0, PixelSampler::Nearest); - inputYOperation_->readSampled(scaleY, 0, 0, PixelSampler::Nearest); + input_xoperation_->read_sampled(scaleX, 0, 0, PixelSampler::Nearest); + input_yoperation_->read_sampled(scaleY, 0, 0, PixelSampler::Nearest); const float scx = scaleX[0]; const float scy = scaleY[0]; - const float width = this->getWidth(); - const float height = this->getHeight(); + const float width = this->get_width(); + const float height = this->get_height(); /* Divide. */ - float relateveXScale = scx / width; - float relateveYScale = scy / height; + float relateve_xscale = scx / width; + float relateve_yscale = scy / height; - newInput.xmax = this->canvas_center_x_ + - (input->xmax - this->canvas_center_x_) / relateveXScale; - newInput.xmin = this->canvas_center_x_ + - (input->xmin - this->canvas_center_x_) / relateveXScale; - newInput.ymax = this->canvas_center_y_ + - (input->ymax - this->canvas_center_y_) / relateveYScale; - newInput.ymin = this->canvas_center_y_ + - (input->ymin - this->canvas_center_y_) / relateveYScale; + new_input.xmax = this->canvas_center_x_ + + (input->xmax - this->canvas_center_x_) / relateve_xscale; + new_input.xmin = this->canvas_center_x_ + + (input->xmin - this->canvas_center_x_) / relateve_xscale; + new_input.ymax = this->canvas_center_y_ + + (input->ymax - this->canvas_center_y_) / relateve_yscale; + new_input.ymin = this->canvas_center_y_ + + (input->ymin - this->canvas_center_y_) / relateve_yscale; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return ScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return ScaleOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } /* Absolute fixed size. */ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color, ResizeMode::None); + this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); - inputOperation_ = nullptr; + input_operation_ = nullptr; is_offset_ = false; } @@ -388,20 +388,20 @@ void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) { const int input_width = BLI_rcti_size_x(&input_canvas); const int input_height = BLI_rcti_size_y(&input_canvas); - relX_ = input_width / (float)newWidth_; - relY_ = input_height / (float)newHeight_; + rel_x_ = input_width / (float)new_width_; + rel_y_ = input_height / (float)new_height_; /* *** all the options below are for a fairly special case - camera framing *** */ - if (offsetX_ != 0.0f || offsetY_ != 0.0f) { + if (offset_x_ != 0.0f || offset_y_ != 0.0f) { is_offset_ = true; - if (newWidth_ > newHeight_) { - offsetX_ *= newWidth_; - offsetY_ *= newWidth_; + if (new_width_ > new_height_) { + offset_x_ *= new_width_; + offset_y_ *= new_width_; } else { - offsetX_ *= newHeight_; - offsetY_ *= newHeight_; + offset_x_ *= new_height_; + offset_y_ *= new_height_; } } @@ -411,8 +411,8 @@ void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) const float h_src = input_height; /* destination aspect is already applied from the camera frame */ - const float w_dst = newWidth_; - const float h_dst = newHeight_; + const float w_dst = new_width_; + const float h_dst = new_height_; const float asp_src = w_src / h_src; const float asp_dst = w_dst / h_dst; @@ -421,33 +421,33 @@ void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) if ((asp_src > asp_dst) == (is_crop_ == true)) { /* fit X */ const float div = asp_src / asp_dst; - relX_ /= div; - offsetX_ += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f; + rel_x_ /= div; + offset_x_ += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f; if (is_crop_ && execution_model_ == eExecutionModel::FullFrame) { - int fit_width = newWidth_ * div; + int fit_width = new_width_ * div; if (fit_width > max_scale_canvas_size_.x) { fit_width = max_scale_canvas_size_.x; } - const int added_width = fit_width - newWidth_; - newWidth_ += added_width; - offsetX_ += added_width / 2.0f; + const int added_width = fit_width - new_width_; + new_width_ += added_width; + offset_x_ += added_width / 2.0f; } } else { /* fit Y */ const float div = asp_dst / asp_src; - relY_ /= div; - offsetY_ += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f; + rel_y_ /= div; + offset_y_ += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f; if (is_crop_ && execution_model_ == eExecutionModel::FullFrame) { - int fit_height = newHeight_ * div; + int fit_height = new_height_ * div; if (fit_height > max_scale_canvas_size_.y) { fit_height = max_scale_canvas_size_.y; } - const int added_height = fit_height - newHeight_; - newHeight_ += added_height; - offsetY_ += added_height / 2.0f; + const int added_height = fit_height - new_height_; + new_height_ += added_height; + offset_y_ += added_height / 2.0f; } } @@ -457,66 +457,67 @@ void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) /* *** end framing options *** */ } -void ScaleFixedSizeOperation::initExecution() +void ScaleFixedSizeOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void ScaleFixedSizeOperation::deinitExecution() +void ScaleFixedSizeOperation::deinit_execution() { - inputOperation_ = nullptr; + input_operation_ = nullptr; } -void ScaleFixedSizeOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ScaleFixedSizeOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - PixelSampler effective_sampler = getEffectiveSampler(sampler); + PixelSampler effective_sampler = get_effective_sampler(sampler); if (is_offset_) { - float nx = ((x - offsetX_) * relX_); - float ny = ((y - offsetY_) * relY_); - inputOperation_->readSampled(output, nx, ny, effective_sampler); + float nx = ((x - offset_x_) * rel_x_); + float ny = ((y - offset_y_) * rel_y_); + input_operation_->read_sampled(output, nx, ny, effective_sampler); } else { - inputOperation_->readSampled(output, x * relX_, y * relY_, effective_sampler); + input_operation_->read_sampled(output, x * rel_x_, y * rel_y_, effective_sampler); } } -bool ScaleFixedSizeOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool ScaleFixedSizeOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = (input->xmax - offsetX_) * relX_ + 1; - newInput.xmin = (input->xmin - offsetX_) * relX_; - newInput.ymax = (input->ymax - offsetY_) * relY_ + 1; - newInput.ymin = (input->ymin - offsetY_) * relY_; + new_input.xmax = (input->xmax - offset_x_) * rel_x_ + 1; + new_input.xmin = (input->xmin - offset_x_) * rel_x_; + new_input.ymax = (input->ymax - offset_y_) * rel_y_ + 1; + new_input.ymin = (input->ymin - offset_y_) * rel_y_; - return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return BaseScaleOperation::determine_depending_area_of_interest( + &new_input, read_operation, output); } void ScaleFixedSizeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { rcti local_preferred = preferred_area; - local_preferred.xmax = local_preferred.xmin + newWidth_; - local_preferred.ymax = local_preferred.ymin + newHeight_; + local_preferred.xmax = local_preferred.xmin + new_width_; + local_preferred.ymax = local_preferred.ymin + new_height_; rcti input_canvas; - const bool input_determined = getInputSocket(0)->determine_canvas(local_preferred, input_canvas); + const bool input_determined = get_input_socket(0)->determine_canvas(local_preferred, + input_canvas); if (input_determined) { init_data(input_canvas); r_area = input_canvas; if (execution_model_ == eExecutionModel::FullFrame) { - r_area.xmin /= relX_; - r_area.ymin /= relY_; - r_area.xmin += offsetX_; - r_area.ymin += offsetY_; + r_area.xmin /= rel_x_; + r_area.ymin /= rel_y_; + r_area.xmin += offset_x_; + r_area.ymin += offset_y_; } - r_area.xmax = r_area.xmin + newWidth_; - r_area.ymax = r_area.ymin + newHeight_; + r_area.xmax = r_area.xmin + new_width_; + r_area.ymax = r_area.ymin + new_height_; } } @@ -527,10 +528,10 @@ void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx, BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmax = ceilf((output_area.xmax - offsetX_) * relX_); - r_input_area.xmin = floorf((output_area.xmin - offsetX_) * relX_); - r_input_area.ymax = ceilf((output_area.ymax - offsetY_) * relY_); - r_input_area.ymin = floorf((output_area.ymin - offsetY_) * relY_); + r_input_area.xmax = ceilf((output_area.xmax - offset_x_) * rel_x_); + r_input_area.xmin = floorf((output_area.xmin - offset_x_) * rel_x_); + r_input_area.ymax = ceilf((output_area.ymax - offset_y_) * rel_y_); + r_input_area.ymin = floorf((output_area.ymin - offset_y_) * rel_y_); expand_area_for_sampler(r_input_area, (PixelSampler)sampler_); } @@ -543,15 +544,15 @@ void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output, BuffersIterator it = output->iterate_with({}, area); if (is_offset_) { for (; !it.is_end(); ++it) { - const float nx = (canvas_.xmin + it.x - offsetX_) * relX_; - const float ny = (canvas_.ymin + it.y - offsetY_) * relY_; + const float nx = (canvas_.xmin + it.x - offset_x_) * rel_x_; + const float ny = (canvas_.ymin + it.y - offset_y_) * rel_y_; input_img->read_elem_sampled(nx - canvas_.xmin, ny - canvas_.ymin, sampler, it.out); } } else { for (; !it.is_end(); ++it) { - input_img->read_elem_sampled((canvas_.xmin + it.x) * relX_ - canvas_.xmin, - (canvas_.ymin + it.y) * relY_ - canvas_.ymin, + input_img->read_elem_sampled((canvas_.xmin + it.x) * rel_x_ - canvas_.xmin, + (canvas_.ymin + it.y) * rel_y_ - canvas_.ymin, sampler, it.out); } diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h index 868a17bc394..7710aa34c54 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.h +++ b/source/blender/compositor/operations/COM_ScaleOperation.h @@ -27,11 +27,11 @@ class BaseScaleOperation : public MultiThreadedOperation { static constexpr float DEFAULT_MAX_SCALE_CANVAS_SIZE = 12000; public: - void setSampler(PixelSampler sampler) + void set_sampler(PixelSampler sampler) { sampler_ = (int)sampler; } - void setVariableSize(bool variable_size) + void set_variable_size(bool variable_size) { variable_size_ = variable_size; }; @@ -41,7 +41,7 @@ class BaseScaleOperation : public MultiThreadedOperation { protected: BaseScaleOperation(); - PixelSampler getEffectiveSampler(PixelSampler sampler) + PixelSampler get_effective_sampler(PixelSampler sampler) { return (sampler_ == -1) ? sampler : (PixelSampler)sampler_; } @@ -61,9 +61,9 @@ class ScaleOperation : public BaseScaleOperation { static constexpr int X_INPUT_INDEX = 1; static constexpr int Y_INPUT_INDEX = 2; - SocketReader *inputOperation_; - SocketReader *inputXOperation_; - SocketReader *inputYOperation_; + SocketReader *input_operation_; + SocketReader *input_xoperation_; + SocketReader *input_yoperation_; float canvas_center_x_; float canvas_center_y_; @@ -97,8 +97,8 @@ class ScaleOperation : public BaseScaleOperation { static void clamp_area_size_max(rcti &area, Size2f max_size); void init_data() override; - void initExecution() override; - void deinitExecution() 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, @@ -122,10 +122,10 @@ class ScaleRelativeOperation : public ScaleOperation { public: ScaleRelativeOperation(); ScaleRelativeOperation(DataType data_type); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; float get_relative_scale_x_factor(float UNUSED(width)) override { @@ -140,10 +140,10 @@ class ScaleRelativeOperation : public ScaleOperation { class ScaleAbsoluteOperation : public ScaleOperation { public: - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; float get_relative_scale_x_factor(float width) override { @@ -157,15 +157,15 @@ class ScaleAbsoluteOperation : public ScaleOperation { }; class ScaleFixedSizeOperation : public BaseScaleOperation { - SocketReader *inputOperation_; - int newWidth_; - int newHeight_; - float relX_; - float relY_; + SocketReader *input_operation_; + int new_width_; + int new_height_; + float rel_x_; + float rel_y_; /* center is only used for aspect correction */ - float offsetX_; - float offsetY_; + float offset_x_; + float offset_y_; bool is_aspect_; bool is_crop_; /* set from other properties on initialization, @@ -174,34 +174,34 @@ class ScaleFixedSizeOperation : public BaseScaleOperation { public: ScaleFixedSizeOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; - void setNewWidth(int width) + void init_execution() override; + void deinit_execution() override; + void set_new_width(int width) { - newWidth_ = width; + new_width_ = width; } - void setNewHeight(int height) + void set_new_height(int height) { - newHeight_ = height; + new_height_ = height; } - void setIsAspect(bool is_aspect) + void set_is_aspect(bool is_aspect) { is_aspect_ = is_aspect; } - void setIsCrop(bool is_crop) + void set_is_crop(bool is_crop) { is_crop_ = is_crop; } - void setOffset(float x, float y) + void set_offset(float x, float y) { - offsetX_ = x; - offsetY_ = y; + offset_x_ = x; + offset_y_ = y; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index 3892d76874c..1ce0378c335 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -28,12 +28,12 @@ namespace blender::compositor { ScreenLensDistortionOperation::ScreenLensDistortionOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); this->flags.complex = true; - inputProgram_ = nullptr; + input_program_ = nullptr; distortion_ = 0.0f; dispersion_ = 0.0f; distortion_const_ = false; @@ -41,13 +41,13 @@ ScreenLensDistortionOperation::ScreenLensDistortionOperation() variables_ready_ = false; } -void ScreenLensDistortionOperation::setDistortion(float distortion) +void ScreenLensDistortionOperation::set_distortion(float distortion) { distortion_ = distortion; distortion_const_ = true; } -void ScreenLensDistortionOperation::setDispersion(float dispersion) +void ScreenLensDistortionOperation::set_dispersion(float dispersion) { dispersion_ = dispersion; dispersion_const_ = true; @@ -55,8 +55,8 @@ void ScreenLensDistortionOperation::setDispersion(float dispersion) void ScreenLensDistortionOperation::init_data() { - cx_ = 0.5f * (float)getWidth(); - cy_ = 0.5f * (float)getHeight(); + cx_ = 0.5f * (float)get_width(); + cy_ = 0.5f * (float)get_height(); switch (execution_model_) { case eExecutionModel::FullFrame: { @@ -68,13 +68,13 @@ void ScreenLensDistortionOperation::init_data() if (!dispersion_const_ && distortion_op->get_flags().is_constant_operation) { dispersion_ = static_cast(dispersion_op)->get_constant_elem()[0]; } - updateVariables(distortion_, dispersion_); + update_variables(distortion_, dispersion_); break; } case eExecutionModel::Tiled: { /* If both are constant, init variables once. */ if (distortion_const_ && dispersion_const_) { - updateVariables(distortion_, dispersion_); + update_variables(distortion_, dispersion_); variables_ready_ = true; } break; @@ -82,42 +82,42 @@ void ScreenLensDistortionOperation::init_data() } } -void ScreenLensDistortionOperation::initExecution() +void ScreenLensDistortionOperation::init_execution() { - inputProgram_ = this->getInputSocketReader(0); - this->initMutex(); + input_program_ = this->get_input_socket_reader(0); + this->init_mutex(); uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); - rng_seed ^= (uint)POINTER_AS_INT(inputProgram_); + rng_seed ^= (uint)POINTER_AS_INT(input_program_); rng_ = BLI_rng_new(rng_seed); } -void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/) +void *ScreenLensDistortionOperation::initialize_tile_data(rcti * /*rect*/) { - void *buffer = inputProgram_->initializeTileData(nullptr); + void *buffer = input_program_->initialize_tile_data(nullptr); /* get distortion/dispersion values once, by reading inputs at (0,0) * XXX this assumes invariable values (no image inputs), * we don't have a nice generic system for that yet */ if (!variables_ready_) { - this->lockMutex(); + this->lock_mutex(); if (!distortion_const_) { float result[4]; - getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest); + get_input_socket_reader(1)->read_sampled(result, 0, 0, PixelSampler::Nearest); distortion_ = result[0]; } if (!dispersion_const_) { float result[4]; - getInputSocketReader(2)->readSampled(result, 0, 0, PixelSampler::Nearest); + get_input_socket_reader(2)->read_sampled(result, 0, 0, PixelSampler::Nearest); dispersion_ = result[0]; } - updateVariables(distortion_, dispersion_); + update_variables(distortion_, dispersion_); variables_ready_ = true; - this->unlockMutex(); + this->unlock_mutex(); } return buffer; @@ -132,8 +132,8 @@ void ScreenLensDistortionOperation::get_uv(const float xy[2], float uv[2]) const void ScreenLensDistortionOperation::distort_uv(const float uv[2], float t, float xy[2]) const { float d = 1.0f / (1.0f + sqrtf(t)); - xy[0] = (uv[0] * d + 0.5f) * getWidth() - 0.5f; - xy[1] = (uv[1] * d + 0.5f) * getHeight() - 0.5f; + xy[0] = (uv[0] * d + 0.5f) * get_width() - 0.5f; + xy[1] = (uv[1] * d + 0.5f) * get_height() - 0.5f; } bool ScreenLensDistortionOperation::get_delta(float r_sq, @@ -176,7 +176,7 @@ void ScreenLensDistortionOperation::accumulate(const MemoryBuffer *buffer, distort_uv(uv, t, xy); switch (execution_model_) { case eExecutionModel::Tiled: - buffer->readBilinear(color, xy[0], xy[1]); + buffer->read_bilinear(color, xy[0], xy[1]); break; case eExecutionModel::FullFrame: buffer->read_elem_bilinear(xy[0], xy[1], color); @@ -190,7 +190,7 @@ void ScreenLensDistortionOperation::accumulate(const MemoryBuffer *buffer, } } -void ScreenLensDistortionOperation::executePixel(float output[4], int x, int y, void *data) +void ScreenLensDistortionOperation::execute_pixel(float output[4], int x, int y, void *data) { MemoryBuffer *buffer = (MemoryBuffer *)data; float xy[2] = {(float)x, (float)y}; @@ -228,10 +228,10 @@ void ScreenLensDistortionOperation::executePixel(float output[4], int x, int y, } } -void ScreenLensDistortionOperation::deinitExecution() +void ScreenLensDistortionOperation::deinit_execution() { - this->deinitMutex(); - inputProgram_ = nullptr; + this->deinit_mutex(); + input_program_ = nullptr; BLI_rng_free(rng_); } @@ -250,22 +250,22 @@ void ScreenLensDistortionOperation::determineUV(float result[6], float x, float get_delta(uv_dot, k4_[2], uv, result + 4); } -bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( - rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) +bool ScreenLensDistortionOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - rcti newInputValue; - newInputValue.xmin = 0; - newInputValue.ymin = 0; - newInputValue.xmax = 2; - newInputValue.ymax = 2; + rcti new_input_value; + new_input_value.xmin = 0; + new_input_value.ymin = 0; + new_input_value.xmax = 2; + new_input_value.ymax = 2; - NodeOperation *operation = getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&newInputValue, readOperation, output)) { + NodeOperation *operation = get_input_operation(1); + if (operation->determine_depending_area_of_interest(&new_input_value, read_operation, output)) { return true; } - operation = getInputOperation(2); - if (operation->determineDependingAreaOfInterest(&newInputValue, readOperation, output)) { + operation = get_input_operation(2); + if (operation->determine_depending_area_of_interest(&new_input_value, read_operation, output)) { return true; } @@ -275,23 +275,23 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( * So now just use the full image area, which may not be as efficient but works at least ... */ #if 1 - rcti imageInput; + rcti image_input; - operation = getInputOperation(0); - imageInput.xmax = operation->getWidth(); - imageInput.xmin = 0; - imageInput.ymax = operation->getHeight(); - imageInput.ymin = 0; + operation = get_input_operation(0); + image_input.xmax = operation->get_width(); + image_input.xmin = 0; + image_input.ymax = operation->get_height(); + image_input.ymin = 0; - if (operation->determineDependingAreaOfInterest(&imageInput, readOperation, output)) { + if (operation->determine_depending_area_of_interest(&image_input, read_operation, output)) { return true; } return false; #else - rcti newInput; + rcti new_input; const float margin = 2; - BLI_rcti_init_minmax(&newInput); + BLI_rcti_init_minmax(&new_input); if (dispersion_const_ && distortion_const_) { /* update from fixed distortion/dispersion */ @@ -299,10 +299,10 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( { \ float coords[6]; \ determineUV(coords, x, y); \ - newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ - newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ - newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ - newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + new_input.xmin = min_ffff(new_input.xmin, coords[0], coords[2], coords[4]); \ + new_input.ymin = min_ffff(new_input.ymin, coords[1], coords[3], coords[5]); \ + new_input.xmax = max_ffff(new_input.xmax, coords[0], coords[2], coords[4]); \ + new_input.ymax = max_ffff(new_input.ymax, coords[1], coords[3], coords[5]); \ } \ (void)0 @@ -320,12 +320,12 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( # define UPDATE_INPUT(x, y, distortion) \ { \ float coords[6]; \ - updateVariables(distortion, dispersion); \ + update_variables(distortion, dispersion); \ determineUV(coords, x, y); \ - newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ - newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ - newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ - newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + new_input.xmin = min_ffff(new_input.xmin, coords[0], coords[2], coords[4]); \ + new_input.ymin = min_ffff(new_input.ymin, coords[1], coords[3], coords[5]); \ + new_input.xmax = max_ffff(new_input.xmax, coords[0], coords[2], coords[4]); \ + new_input.ymax = max_ffff(new_input.ymax, coords[1], coords[3], coords[5]); \ } \ (void)0 @@ -352,20 +352,20 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( } } - newInput.xmin -= margin; - newInput.ymin -= margin; - newInput.xmax += margin; - newInput.ymax += margin; + new_input.xmin -= margin; + new_input.ymin -= margin; + new_input.xmax += margin; + new_input.ymax += margin; - operation = getInputOperation(0); - if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + operation = get_input_operation(0); + if (operation->determine_depending_area_of_interest(&new_input, read_operation, output)) { return true; } return false; #endif } -void ScreenLensDistortionOperation::updateVariables(float distortion, float dispersion) +void ScreenLensDistortionOperation::update_variables(float distortion, float dispersion) { k_[1] = max_ff(min_ff(distortion, 1.0f), -0.999f); /* Smaller dispersion range for somewhat more control. */ @@ -413,14 +413,14 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, * So now just use the full image area, which may not be as efficient but works at least ... */ #if 1 - NodeOperation *image = getInputOperation(0); + NodeOperation *image = get_input_operation(0); r_input_area = image->get_canvas(); #else /* Original method in tiled implementation. */ - rcti newInput; + rcti new_input; const float margin = 2; - BLI_rcti_init_minmax(&newInput); + BLI_rcti_init_minmax(&new_input); if (dispersion_const_ && distortion_const_) { /* update from fixed distortion/dispersion */ @@ -428,10 +428,10 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, { \ float coords[6]; \ determineUV(coords, x, y); \ - newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ - newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ - newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ - newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + new_input.xmin = min_ffff(new_input.xmin, coords[0], coords[2], coords[4]); \ + new_input.ymin = min_ffff(new_input.ymin, coords[1], coords[3], coords[5]); \ + new_input.xmax = max_ffff(new_input.xmax, coords[0], coords[2], coords[4]); \ + new_input.ymax = max_ffff(new_input.ymax, coords[1], coords[3], coords[5]); \ } \ (void)0 @@ -449,12 +449,12 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, # define UPDATE_INPUT(x, y, distortion) \ { \ float coords[6]; \ - updateVariables(distortion, dispersion); \ + update_variables(distortion, dispersion); \ determineUV(coords, x, y); \ - newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ - newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ - newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ - newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + new_input.xmin = min_ffff(new_input.xmin, coords[0], coords[2], coords[4]); \ + new_input.ymin = min_ffff(new_input.ymin, coords[1], coords[3], coords[5]); \ + new_input.xmax = max_ffff(new_input.xmax, coords[0], coords[2], coords[4]); \ + new_input.ymax = max_ffff(new_input.ymax, coords[1], coords[3], coords[5]); \ } \ (void)0 @@ -481,13 +481,13 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, } } - newInput.xmin -= margin; - newInput.ymin -= margin; - newInput.xmax += margin; - newInput.ymax += margin; + new_input.xmin -= margin; + new_input.ymin -= margin; + new_input.xmax += margin; + new_input.ymax += margin; - operation = getInputOperation(0); - if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + operation = get_input_operation(0); + if (operation->determine_depending_area_of_interest(&new_input, read_operation, output)) { return true; } return false; diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h index 8962ee09290..fb610b90466 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h @@ -28,9 +28,9 @@ namespace blender::compositor { class ScreenLensDistortionOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; struct RNG *rng_; bool fit_; @@ -55,36 +55,36 @@ class ScreenLensDistortionOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setFit(bool fit) + void set_fit(bool fit) { fit_ = fit; } - void setJitter(bool jitter) + void set_jitter(bool jitter) { jitter_ = jitter; } /** Set constant distortion value */ - void setDistortion(float distortion); + void set_distortion(float distortion); /** Set constant dispersion value */ - void setDispersion(float dispersion); + void set_dispersion(float dispersion); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; @@ -94,7 +94,7 @@ class ScreenLensDistortionOperation : public MultiThreadedOperation { private: void determineUV(float result[6], float x, float y) const; - void updateVariables(float distortion, float dispersion); + void update_variables(float distortion, float dispersion); void get_uv(const float xy[2], float uv[2]) const; void distort_uv(const float uv[2], float t, float xy[2]) const; diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc index 2f11e1dbb18..a1ea5758a83 100644 --- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc @@ -22,39 +22,39 @@ namespace blender::compositor { SetAlphaMultiplyOperation::SetAlphaMultiplyOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); - inputColor_ = nullptr; - inputAlpha_ = nullptr; + input_color_ = nullptr; + input_alpha_ = nullptr; this->flags.can_be_constant = true; } -void SetAlphaMultiplyOperation::initExecution() +void SetAlphaMultiplyOperation::init_execution() { - inputColor_ = getInputSocketReader(0); - inputAlpha_ = getInputSocketReader(1); + input_color_ = get_input_socket_reader(0); + input_alpha_ = get_input_socket_reader(1); } -void SetAlphaMultiplyOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void SetAlphaMultiplyOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float color_input[4]; float alpha_input[4]; - inputColor_->readSampled(color_input, x, y, sampler); - inputAlpha_->readSampled(alpha_input, x, y, sampler); + input_color_->read_sampled(color_input, x, y, sampler); + input_alpha_->read_sampled(alpha_input, x, y, sampler); mul_v4_v4fl(output, color_input, alpha_input[0]); } -void SetAlphaMultiplyOperation::deinitExecution() +void SetAlphaMultiplyOperation::deinit_execution() { - inputColor_ = nullptr; - inputAlpha_ = nullptr; + input_color_ = nullptr; + input_alpha_ = nullptr; } void SetAlphaMultiplyOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h index 77f61e976e1..2aa56800ff7 100644 --- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h +++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h @@ -29,16 +29,16 @@ namespace blender::compositor { */ class SetAlphaMultiplyOperation : public MultiThreadedOperation { private: - SocketReader *inputColor_; - SocketReader *inputAlpha_; + SocketReader *input_color_; + SocketReader *input_alpha_; public: SetAlphaMultiplyOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc index 09539d2fe57..649e6ce0a4e 100644 --- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc @@ -22,37 +22,37 @@ namespace blender::compositor { SetAlphaReplaceOperation::SetAlphaReplaceOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); - inputColor_ = nullptr; - inputAlpha_ = nullptr; + input_color_ = nullptr; + input_alpha_ = nullptr; this->flags.can_be_constant = true; } -void SetAlphaReplaceOperation::initExecution() +void SetAlphaReplaceOperation::init_execution() { - inputColor_ = getInputSocketReader(0); - inputAlpha_ = getInputSocketReader(1); + input_color_ = get_input_socket_reader(0); + input_alpha_ = get_input_socket_reader(1); } -void SetAlphaReplaceOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void SetAlphaReplaceOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float alpha_input[4]; - inputColor_->readSampled(output, x, y, sampler); - inputAlpha_->readSampled(alpha_input, x, y, sampler); + input_color_->read_sampled(output, x, y, sampler); + input_alpha_->read_sampled(alpha_input, x, y, sampler); output[3] = alpha_input[0]; } -void SetAlphaReplaceOperation::deinitExecution() +void SetAlphaReplaceOperation::deinit_execution() { - inputColor_ = nullptr; - inputAlpha_ = nullptr; + input_color_ = nullptr; + input_alpha_ = nullptr; } void SetAlphaReplaceOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h index 9c295eec3bd..ea27e6af672 100644 --- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h +++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h @@ -28,8 +28,8 @@ namespace blender::compositor { */ class SetAlphaReplaceOperation : public MultiThreadedOperation { private: - SocketReader *inputColor_; - SocketReader *inputAlpha_; + SocketReader *input_color_; + SocketReader *input_alpha_; public: /** @@ -40,10 +40,10 @@ class SetAlphaReplaceOperation : public MultiThreadedOperation { /** * the inner loop of this program */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index 8700ba7496b..2f89e6722fa 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -22,14 +22,14 @@ namespace blender::compositor { SetColorOperation::SetColorOperation() { - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); flags.is_set_operation = true; } -void SetColorOperation::executePixelSampled(float output[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) +void SetColorOperation::execute_pixel_sampled(float output[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { copy_v4_v4(output, color_); } diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h index ad30c1d820d..0e04c0324b8 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.h +++ b/source/blender/compositor/operations/COM_SetColorOperation.h @@ -41,39 +41,39 @@ class SetColorOperation : public ConstantOperation { return color_; } - float getChannel1() + float get_channel1() { return color_[0]; } - void setChannel1(float value) + void set_channel1(float value) { color_[0] = value; } - float getChannel2() + float get_channel2() { return color_[1]; } - void setChannel2(float value) + void set_channel2(float value) { color_[1] = value; } - float getChannel3() + float get_channel3() { return color_[2]; } - void setChannel3(float value) + void set_channel3(float value) { color_[2] = value; } - float getChannel4() + float get_channel4() { return color_[3]; } - void setChannel4(const float value) + void set_channel4(const float value) { color_[3] = value; } - void setChannels(const float value[4]) + void set_channels(const float value[4]) { copy_v4_v4(color_, value); } @@ -81,7 +81,7 @@ class SetColorOperation : public ConstantOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; }; diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.cc b/source/blender/compositor/operations/COM_SetSamplerOperation.cc index 7919b885556..6f5ac63317b 100644 --- a/source/blender/compositor/operations/COM_SetSamplerOperation.cc +++ b/source/blender/compositor/operations/COM_SetSamplerOperation.cc @@ -22,25 +22,25 @@ namespace blender::compositor { SetSamplerOperation::SetSamplerOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void SetSamplerOperation::initExecution() +void SetSamplerOperation::init_execution() { - reader_ = this->getInputSocketReader(0); + reader_ = this->get_input_socket_reader(0); } -void SetSamplerOperation::deinitExecution() +void SetSamplerOperation::deinit_execution() { reader_ = nullptr; } -void SetSamplerOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void SetSamplerOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - reader_->readSampled(output, x, y, sampler_); + reader_->read_sampled(output, x, y, sampler_); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.h b/source/blender/compositor/operations/COM_SetSamplerOperation.h index 87b232cbfa7..27ef6603ae4 100644 --- a/source/blender/compositor/operations/COM_SetSamplerOperation.h +++ b/source/blender/compositor/operations/COM_SetSamplerOperation.h @@ -37,7 +37,7 @@ class SetSamplerOperation : public NodeOperation { */ SetSamplerOperation(); - void setSampler(PixelSampler sampler) + void set_sampler(PixelSampler sampler) { sampler_ = sampler; } @@ -45,9 +45,9 @@ class SetSamplerOperation : public NodeOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_execution() override; + void deinit_execution() override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index b5e2f50338f..9cd4bed0750 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -22,14 +22,14 @@ namespace blender::compositor { SetValueOperation::SetValueOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); flags.is_set_operation = true; } -void SetValueOperation::executePixelSampled(float output[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) +void SetValueOperation::execute_pixel_sampled(float output[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { output[0] = value_; } diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h index bf914d2f918..4c824d00294 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.h +++ b/source/blender/compositor/operations/COM_SetValueOperation.h @@ -41,11 +41,11 @@ class SetValueOperation : public ConstantOperation { return &value_; } - float getValue() + float get_value() { return value_; } - void setValue(float value) + void set_value(float value) { value_ = value; } @@ -53,7 +53,7 @@ class SetValueOperation : public ConstantOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; }; diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.cc b/source/blender/compositor/operations/COM_SetVectorOperation.cc index 6f0a947c52d..47f13f10fee 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.cc +++ b/source/blender/compositor/operations/COM_SetVectorOperation.cc @@ -22,14 +22,14 @@ namespace blender::compositor { SetVectorOperation::SetVectorOperation() { - this->addOutputSocket(DataType::Vector); + this->add_output_socket(DataType::Vector); flags.is_set_operation = true; } -void SetVectorOperation::executePixelSampled(float output[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) +void SetVectorOperation::execute_pixel_sampled(float output[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { output[0] = vector_.x; output[1] = vector_.y; diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.h b/source/blender/compositor/operations/COM_SetVectorOperation.h index 920ea8051e4..291fef37817 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.h +++ b/source/blender/compositor/operations/COM_SetVectorOperation.h @@ -82,11 +82,11 @@ class SetVectorOperation : public ConstantOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void setVector(const float vector[3]) + void set_vector(const float vector[3]) { setX(vector[0]); setY(vector[1]); diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.cc b/source/blender/compositor/operations/COM_SocketProxyOperation.cc index 39876439b7b..18a09362591 100644 --- a/source/blender/compositor/operations/COM_SocketProxyOperation.cc +++ b/source/blender/compositor/operations/COM_SocketProxyOperation.cc @@ -22,15 +22,15 @@ namespace blender::compositor { SocketProxyOperation::SocketProxyOperation(DataType type, bool use_conversion) { - this->addInputSocket(type); - this->addOutputSocket(type); + this->add_input_socket(type); + this->add_output_socket(type); flags.is_proxy_operation = true; flags.use_datatype_conversion = use_conversion; } -std::unique_ptr SocketProxyOperation::getMetaData() +std::unique_ptr SocketProxyOperation::get_meta_data() { - return this->getInputSocket(0)->getReader()->getMetaData(); + return this->get_input_socket(0)->get_reader()->get_meta_data(); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.h b/source/blender/compositor/operations/COM_SocketProxyOperation.h index 1d3b76055bd..e9740cf43bc 100644 --- a/source/blender/compositor/operations/COM_SocketProxyOperation.h +++ b/source/blender/compositor/operations/COM_SocketProxyOperation.h @@ -26,7 +26,7 @@ class SocketProxyOperation : public NodeOperation { public: SocketProxyOperation(DataType type, bool use_conversion); - std::unique_ptr getMetaData() override; + std::unique_ptr get_meta_data() override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc index 99db558343d..e98c5c986f5 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.cc +++ b/source/blender/compositor/operations/COM_SplitOperation.cc @@ -22,39 +22,39 @@ namespace blender::compositor { SplitOperation::SplitOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); image1Input_ = nullptr; image2Input_ = nullptr; } -void SplitOperation::initExecution() +void SplitOperation::init_execution() { /* When initializing the tree during initial load the width and height can be zero. */ - image1Input_ = getInputSocketReader(0); - image2Input_ = getInputSocketReader(1); + image1Input_ = get_input_socket_reader(0); + image2Input_ = get_input_socket_reader(1); } -void SplitOperation::deinitExecution() +void SplitOperation::deinit_execution() { image1Input_ = nullptr; image2Input_ = nullptr; } -void SplitOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void SplitOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - int perc = xSplit_ ? splitPercentage_ * this->getWidth() / 100.0f : - splitPercentage_ * this->getHeight() / 100.0f; - bool image1 = xSplit_ ? x > perc : y > perc; + int perc = x_split_ ? split_percentage_ * this->get_width() / 100.0f : + split_percentage_ * this->get_height() / 100.0f; + bool image1 = x_split_ ? x > perc : y > perc; if (image1) { - image1Input_->readSampled(output, x, y, PixelSampler::Nearest); + image1Input_->read_sampled(output, x, y, PixelSampler::Nearest); } else { - image2Input_->readSampled(output, x, y, PixelSampler::Nearest); + image2Input_->read_sampled(output, x, y, PixelSampler::Nearest); } } @@ -62,7 +62,7 @@ void SplitOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { rcti unused_area; - const bool determined = this->getInputSocket(0)->determine_canvas(COM_AREA_NONE, unused_area); + const bool determined = this->get_input_socket(0)->determine_canvas(COM_AREA_NONE, unused_area); this->set_canvas_input_index(determined ? 0 : 1); NodeOperation::determine_canvas(preferred_area, r_area); @@ -72,11 +72,11 @@ void SplitOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const int percent = xSplit_ ? splitPercentage_ * this->getWidth() / 100.0f : - splitPercentage_ * this->getHeight() / 100.0f; - const size_t elem_bytes = COM_data_type_bytes_len(getOutputSocket()->getDataType()); + const int percent = x_split_ ? split_percentage_ * this->get_width() / 100.0f : + split_percentage_ * this->get_height() / 100.0f; + const size_t elem_bytes = COM_data_type_bytes_len(get_output_socket()->get_data_type()); for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - const bool is_image1 = xSplit_ ? it.x > percent : it.y > percent; + const bool is_image1 = x_split_ ? it.x > percent : it.y > percent; memcpy(it.out, it.in(is_image1 ? 0 : 1), elem_bytes); } } diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h index 9f594793be5..2d2b2e89cca 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.h +++ b/source/blender/compositor/operations/COM_SplitOperation.h @@ -27,22 +27,22 @@ class SplitOperation : public MultiThreadedOperation { SocketReader *image1Input_; SocketReader *image2Input_; - float splitPercentage_; - bool xSplit_; + float split_percentage_; + bool x_split_; public: SplitOperation(); - void initExecution() override; - void deinitExecution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_execution() override; + void deinit_execution() override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void setSplitPercentage(float splitPercentage) + void set_split_percentage(float split_percentage) { - splitPercentage_ = splitPercentage; + split_percentage_ = split_percentage; } - void setXSplit(bool xsplit) + void set_xsplit(bool xsplit) { - xSplit_ = xsplit; + x_split_ = xsplit; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index b055d0a7644..5842fcc2278 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -23,8 +23,8 @@ namespace blender::compositor { SunBeamsOperation::SunBeamsOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); this->flags.complex = true; @@ -33,12 +33,12 @@ SunBeamsOperation::SunBeamsOperation() void SunBeamsOperation::calc_rays_common_data() { /* convert to pixels */ - source_px_[0] = data_.source[0] * this->getWidth(); - source_px_[1] = data_.source[1] * this->getHeight(); - ray_length_px_ = data_.ray_length * MAX2(this->getWidth(), this->getHeight()); + source_px_[0] = data_.source[0] * this->get_width(); + source_px_[1] = data_.source[1] * this->get_height(); + ray_length_px_ = data_.ray_length * MAX2(this->get_width(), this->get_height()); } -void SunBeamsOperation::initExecution() +void SunBeamsOperation::init_execution() { calc_rays_common_data(); } @@ -145,7 +145,7 @@ template struct BufferLineAccumulator { falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f; - float *iter = input->getBuffer() + input->get_coords_offset(x, y); + float *iter = input->get_buffer() + input->get_coords_offset(x, y); return iter; } @@ -312,13 +312,13 @@ static void accumulate_line(MemoryBuffer *input, } } -void *SunBeamsOperation::initializeTileData(rcti * /*rect*/) +void *SunBeamsOperation::initialize_tile_data(rcti * /*rect*/) { - void *buffer = getInputOperation(0)->initializeTileData(nullptr); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); return buffer; } -void SunBeamsOperation::executePixel(float output[4], int x, int y, void *data) +void SunBeamsOperation::execute_pixel(float output[4], int x, int y, void *data) { const float co[2] = {(float)x, (float)y}; @@ -340,9 +340,9 @@ static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], BLI_rcti_do_minmax_v(rect, ico); } -bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool SunBeamsOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { /* Enlarges the rect by moving each corner toward the source. * This is the maximum distance that pixels can influence each other @@ -354,7 +354,7 @@ bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input, calc_ray_shift(&rect, input->xmax, input->ymin, source_px_, ray_length_px_); calc_ray_shift(&rect, input->xmax, input->ymax, source_px_, ray_length_px_); - return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&rect, read_operation, output); } void SunBeamsOperation::get_area_of_interest(const int input_idx, diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.h b/source/blender/compositor/operations/COM_SunBeamsOperation.h index 2a62be065a4..c40d540996e 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.h +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.h @@ -25,17 +25,17 @@ class SunBeamsOperation : public MultiThreadedOperation { public: SunBeamsOperation(); - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setData(const NodeSunBeams &data) + void set_data(const NodeSunBeams &data) { data_ = data; } diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index 7c18b8214de..da1c90cd1c5 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -26,46 +26,46 @@ namespace blender::compositor { TextureBaseOperation::TextureBaseOperation() { - this->addInputSocket(DataType::Vector); // offset - this->addInputSocket(DataType::Vector); // size + this->add_input_socket(DataType::Vector); // offset + this->add_input_socket(DataType::Vector); // size texture_ = nullptr; - inputSize_ = nullptr; - inputOffset_ = nullptr; + input_size_ = nullptr; + input_offset_ = nullptr; rd_ = nullptr; pool_ = nullptr; - sceneColorManage_ = false; + scene_color_manage_ = false; flags.complex = true; } TextureOperation::TextureOperation() : TextureBaseOperation() { - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); } TextureAlphaOperation::TextureAlphaOperation() : TextureBaseOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); } -void TextureBaseOperation::initExecution() +void TextureBaseOperation::init_execution() { - inputOffset_ = getInputSocketReader(0); - inputSize_ = getInputSocketReader(1); + input_offset_ = get_input_socket_reader(0); + input_size_ = get_input_socket_reader(1); pool_ = BKE_image_pool_new(); if (texture_ != nullptr && texture_->nodetree != nullptr && texture_->use_nodes) { ntreeTexBeginExecTree(texture_->nodetree); } - NodeOperation::initExecution(); + NodeOperation::init_execution(); } -void TextureBaseOperation::deinitExecution() +void TextureBaseOperation::deinit_execution() { - inputSize_ = nullptr; - inputOffset_ = nullptr; + input_size_ = nullptr; + input_offset_ = nullptr; BKE_image_pool_free(pool_); pool_ = nullptr; if (texture_ != nullptr && texture_->use_nodes && texture_->nodetree != nullptr && texture_->nodetree->execdata != nullptr) { ntreeTexEndExecTree(texture_->nodetree->execdata); } - NodeOperation::deinitExecution(); + NodeOperation::deinit_execution(); } void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -85,30 +85,30 @@ void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_ } } -void TextureAlphaOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void TextureAlphaOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float color[4]; - TextureBaseOperation::executePixelSampled(color, x, y, sampler); + TextureBaseOperation::execute_pixel_sampled(color, x, y, sampler); output[0] = color[3]; } -void TextureBaseOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void TextureBaseOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { TexResult texres = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, nullptr}; - float textureSize[4]; - float textureOffset[4]; + float texture_size[4]; + float texture_offset[4]; float vec[3]; int retval; - const float cx = this->getWidth() / 2; - const float cy = this->getHeight() / 2; - float u = (x - cx) / this->getWidth() * 2; - float v = (y - cy) / this->getHeight() * 2; + const float cx = this->get_width() / 2; + const float cy = this->get_height() / 2; + float u = (x - cx) / this->get_width() * 2; + float v = (y - cy) / this->get_height() * 2; /* When no interpolation/filtering happens in multitex() force nearest interpolation. * We do it here because (a) we can't easily say multitex() that we want nearest @@ -120,16 +120,16 @@ void TextureBaseOperation::executePixelSampled(float output[4], v += 0.5f / cy; } - inputSize_->readSampled(textureSize, x, y, sampler); - inputOffset_->readSampled(textureOffset, x, y, sampler); + input_size_->read_sampled(texture_size, x, y, sampler); + input_offset_->read_sampled(texture_offset, x, y, sampler); - vec[0] = textureSize[0] * (u + textureOffset[0]); - vec[1] = textureSize[1] * (v + textureOffset[1]); - vec[2] = textureSize[2] * textureOffset[2]; + vec[0] = texture_size[0] * (u + texture_offset[0]); + vec[1] = texture_size[1] * (v + texture_offset[1]); + vec[2] = texture_size[2] * texture_offset[2]; const int thread_id = WorkScheduler::current_thread_id(); retval = multitex_ext( - texture_, vec, nullptr, nullptr, 0, &texres, thread_id, pool_, sceneColorManage_, false); + texture_, vec, nullptr, nullptr, 0, &texres, thread_id, pool_, scene_color_manage_, false); output[3] = texres.talpha ? texres.ta : texres.tin; if (retval & TEX_RGB) { @@ -146,8 +146,8 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const int op_width = this->getWidth(); - const int op_height = this->getHeight(); + const int op_width = this->get_width(); + const int op_height = this->get_height(); const float center_x = op_width / 2; const float center_y = op_height / 2; TexResult tex_result = {0}; @@ -181,7 +181,7 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, &tex_result, thread_id, pool_, - sceneColorManage_, + scene_color_manage_, false); it.out[3] = tex_result.talpha ? tex_result.ta : tex_result.tin; diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index 3916f82c77b..bf1bb1affb8 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -37,10 +37,10 @@ class TextureBaseOperation : public MultiThreadedOperation { private: Tex *texture_; const RenderData *rd_; - SocketReader *inputSize_; - SocketReader *inputOffset_; + SocketReader *input_size_; + SocketReader *input_offset_; struct ImagePool *pool_; - bool sceneColorManage_; + bool scene_color_manage_; protected: /** @@ -54,21 +54,21 @@ class TextureBaseOperation : public MultiThreadedOperation { TextureBaseOperation(); public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void setTexture(Tex *texture) + void set_texture(Tex *texture) { texture_ = texture; } - void initExecution() override; - void deinitExecution() override; - void setRenderData(const RenderData *rd) + void init_execution() override; + void deinit_execution() override; + void set_render_data(const RenderData *rd) { rd_ = rd; } - void setSceneColorManage(bool sceneColorManage) + void set_scene_color_manage(bool scene_color_manage) { - sceneColorManage_ = sceneColorManage; + scene_color_manage_ = scene_color_manage; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -83,7 +83,7 @@ class TextureOperation : public TextureBaseOperation { class TextureAlphaOperation : public TextureBaseOperation { public: TextureAlphaOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index b9d67ec8be3..6d3fc9380c1 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -26,24 +26,24 @@ namespace blender::compositor { TonemapOperation::TonemapOperation() { - this->addInputSocket(DataType::Color, ResizeMode::Align); - this->addOutputSocket(DataType::Color); - imageReader_ = nullptr; + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_output_socket(DataType::Color); + image_reader_ = nullptr; data_ = nullptr; - cachedInstance_ = nullptr; + cached_instance_ = nullptr; this->flags.complex = true; } -void TonemapOperation::initExecution() +void TonemapOperation::init_execution() { - imageReader_ = this->getInputSocketReader(0); - NodeOperation::initMutex(); + image_reader_ = this->get_input_socket_reader(0); + NodeOperation::init_mutex(); } -void TonemapOperation::executePixel(float output[4], int x, int y, void *data) +void TonemapOperation::execute_pixel(float output[4], int x, int y, void *data) { AvgLogLum *avg = (AvgLogLum *)data; - imageReader_->read(output, x, y, nullptr); + image_reader_->read(output, x, y, nullptr); mul_v3_fl(output, avg->al); float dr = output[0] + data_->offset; float dg = output[1] + data_->offset; @@ -58,7 +58,7 @@ void TonemapOperation::executePixel(float output[4], int x, int y, void *data) output[2] = powf(MAX2(output[2], 0.0f), igm); } } -void PhotoreceptorTonemapOperation::executePixel(float output[4], int x, int y, void *data) +void PhotoreceptorTonemapOperation::execute_pixel(float output[4], int x, int y, void *data) { AvgLogLum *avg = (AvgLogLum *)data; NodeTonemap *ntm = data_; @@ -67,7 +67,7 @@ void PhotoreceptorTonemapOperation::executePixel(float output[4], int x, int y, const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); const float ic = 1.0f - ntm->c, ia = 1.0f - ntm->a; - imageReader_->read(output, x, y, nullptr); + image_reader_->read(output, x, y, nullptr); const float L = IMB_colormanagement_get_luminance(output); float I_l = output[0] + ic * (L - output[0]); @@ -84,41 +84,41 @@ void PhotoreceptorTonemapOperation::executePixel(float output[4], int x, int y, output[2] /= (output[2] + powf(f * I_a, m)); } -void TonemapOperation::deinitExecution() +void TonemapOperation::deinit_execution() { - imageReader_ = nullptr; - delete cachedInstance_; - NodeOperation::deinitMutex(); + image_reader_ = nullptr; + delete cached_instance_; + NodeOperation::deinit_mutex(); } -bool TonemapOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool TonemapOperation::determine_depending_area_of_interest(rcti * /*input*/, + ReadBufferOperation *read_operation, + rcti *output) { - rcti imageInput; + rcti image_input; - NodeOperation *operation = getInputOperation(0); - imageInput.xmax = operation->getWidth(); - imageInput.xmin = 0; - imageInput.ymax = operation->getHeight(); - imageInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&imageInput, readOperation, output)) { + NodeOperation *operation = get_input_operation(0); + image_input.xmax = operation->get_width(); + image_input.xmin = 0; + image_input.ymax = operation->get_height(); + image_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&image_input, read_operation, output)) { return true; } return false; } -void *TonemapOperation::initializeTileData(rcti *rect) +void *TonemapOperation::initialize_tile_data(rcti *rect) { - lockMutex(); - if (cachedInstance_ == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)imageReader_->initializeTileData(rect); + lock_mutex(); + if (cached_instance_ == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)image_reader_->initialize_tile_data(rect); AvgLogLum *data = new AvgLogLum(); - float *buffer = tile->getBuffer(); + float *buffer = tile->get_buffer(); float lsum = 0.0f; - int p = tile->getWidth() * tile->getHeight(); + int p = tile->get_width() * tile->get_height(); float *bc = buffer; float avl, maxl = -1e10f, minl = 1e10f; const float sc = 1.0f / p; @@ -142,13 +142,13 @@ void *TonemapOperation::initializeTileData(rcti *rect) float al = exp((double)avl); data->al = (al == 0.0f) ? 0.0f : (data_->key / al); data->igm = (data_->gamma == 0.0f) ? 1 : (1.0f / data_->gamma); - cachedInstance_ = data; + cached_instance_ = data; } - unlockMutex(); - return cachedInstance_; + unlock_mutex(); + return cached_instance_; } -void TonemapOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) +void TonemapOperation::deinitialize_tile_data(rcti * /*rect*/, void * /*data*/) { /* pass */ } @@ -189,7 +189,7 @@ void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output) const rcti &UNUSED(area), Span inputs) { - if (cachedInstance_ == nullptr) { + if (cached_instance_ == nullptr) { Luminance lum = {0}; const MemoryBuffer *input = inputs[0]; exec_system_->execute_work( @@ -215,7 +215,7 @@ void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output) const float al = exp((double)avg_log); avg->al = (al == 0.0f) ? 0.0f : (data_->key / al); avg->igm = (data_->gamma == 0.0f) ? 1 : (1.0f / data_->gamma); - cachedInstance_ = avg; + cached_instance_ = avg; } } @@ -223,7 +223,7 @@ void TonemapOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - AvgLogLum *avg = cachedInstance_; + AvgLogLum *avg = cached_instance_; const float igm = avg->igm; const float offset = data_->offset; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { @@ -247,7 +247,7 @@ void PhotoreceptorTonemapOperation::update_memory_buffer_partial(MemoryBuffer *o const rcti &area, Span inputs) { - AvgLogLum *avg = cachedInstance_; + AvgLogLum *avg = cached_instance_; NodeTonemap *ntm = data_; const float f = expf(-data_->f); const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h index c04ee4fcbe9..a20c061965e 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.h +++ b/source/blender/compositor/operations/COM_TonemapOperation.h @@ -44,7 +44,7 @@ class TonemapOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *imageReader_; + SocketReader *image_reader_; /** * \brief settings of the Tonemap @@ -54,7 +54,7 @@ class TonemapOperation : public MultiThreadedOperation { /** * \brief temporarily cache of the execution storage */ - AvgLogLum *cachedInstance_; + AvgLogLum *cached_instance_; public: TonemapOperation(); @@ -62,29 +62,29 @@ class TonemapOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; - void deinitializeTileData(rcti *rect, void *data) override; + void *initialize_tile_data(rcti *rect) override; + void deinitialize_tile_data(rcti *rect, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setData(NodeTonemap *data) + void set_data(NodeTonemap *data) { data_ = data; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_started(MemoryBuffer *output, @@ -106,7 +106,7 @@ class PhotoreceptorTonemapOperation : public TonemapOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index d9e55011d16..f329bfb0656 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -26,20 +26,20 @@ namespace blender::compositor { TrackPositionOperation::TrackPositionOperation() { - this->addOutputSocket(DataType::Value); - movieClip_ = nullptr; + this->add_output_socket(DataType::Value); + movie_clip_ = nullptr; framenumber_ = 0; - trackingObjectName_[0] = 0; - trackName_[0] = 0; + tracking_object_name_[0] = 0; + track_name_[0] = 0; axis_ = 0; position_ = CMP_TRACKPOS_ABSOLUTE; - relativeFrame_ = 0; + relative_frame_ = 0; speed_output_ = false; flags.is_set_operation = true; is_track_position_calculated_ = false; } -void TrackPositionOperation::initExecution() +void TrackPositionOperation::init_execution() { if (!is_track_position_calculated_) { calc_track_position(); @@ -54,45 +54,45 @@ void TrackPositionOperation::calc_track_position() MovieTrackingObject *object; track_position_ = 0; - zero_v2(markerPos_); - zero_v2(relativePos_); + zero_v2(marker_pos_); + zero_v2(relative_pos_); - if (!movieClip_) { + if (!movie_clip_) { return; } - tracking = &movieClip_->tracking; + tracking = &movie_clip_->tracking; BKE_movieclip_user_set_frame(&user, framenumber_); - BKE_movieclip_get_size(movieClip_, &user, &width_, &height_); + BKE_movieclip_get_size(movie_clip_, &user, &width_, &height_); - object = BKE_tracking_object_get_named(tracking, trackingObjectName_); + object = BKE_tracking_object_get_named(tracking, tracking_object_name_); if (object) { MovieTrackingTrack *track; - track = BKE_tracking_track_get_named(tracking, object, trackName_); + track = BKE_tracking_track_get_named(tracking, object, track_name_); if (track) { MovieTrackingMarker *marker; - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, framenumber_); + int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, framenumber_); marker = BKE_tracking_marker_get(track, clip_framenr); - copy_v2_v2(markerPos_, marker->pos); + copy_v2_v2(marker_pos_, marker->pos); if (speed_output_) { - int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, - relativeFrame_); + int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, + relative_frame_); marker = BKE_tracking_marker_get_exact(track, relative_clip_framenr); if (marker != nullptr && (marker->flag & MARKER_DISABLED) == 0) { - copy_v2_v2(relativePos_, marker->pos); + copy_v2_v2(relative_pos_, marker->pos); } else { - copy_v2_v2(relativePos_, markerPos_); + copy_v2_v2(relative_pos_, marker_pos_); } - if (relativeFrame_ < framenumber_) { - swap_v2_v2(relativePos_, markerPos_); + if (relative_frame_ < framenumber_) { + swap_v2_v2(relative_pos_, marker_pos_); } } else if (position_ == CMP_TRACKPOS_RELATIVE_START) { @@ -102,23 +102,23 @@ void TrackPositionOperation::calc_track_position() marker = &track->markers[i]; if ((marker->flag & MARKER_DISABLED) == 0) { - copy_v2_v2(relativePos_, marker->pos); + copy_v2_v2(relative_pos_, marker->pos); break; } } } else if (position_ == CMP_TRACKPOS_RELATIVE_FRAME) { - int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip_, - relativeFrame_); + int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, + relative_frame_); marker = BKE_tracking_marker_get(track, relative_clip_framenr); - copy_v2_v2(relativePos_, marker->pos); + copy_v2_v2(relative_pos_, marker->pos); } } } - track_position_ = markerPos_[axis_] - relativePos_[axis_]; + track_position_ = marker_pos_[axis_] - relative_pos_[axis_]; if (axis_ == 0) { track_position_ *= width_; } @@ -127,12 +127,12 @@ void TrackPositionOperation::calc_track_position() } } -void TrackPositionOperation::executePixelSampled(float output[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) +void TrackPositionOperation::execute_pixel_sampled(float output[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { - output[0] = markerPos_[axis_] - relativePos_[axis_]; + output[0] = marker_pos_[axis_] - relative_pos_[axis_]; if (axis_ == 0) { output[0] *= width_; diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h index 25e5a144228..1de8b72f783 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.h +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h @@ -35,18 +35,18 @@ namespace blender::compositor { */ class TrackPositionOperation : public ConstantOperation { protected: - MovieClip *movieClip_; + MovieClip *movie_clip_; int framenumber_; - char trackingObjectName_[64]; - char trackName_[64]; + char tracking_object_name_[64]; + char track_name_[64]; int axis_; int position_; - int relativeFrame_; + int relative_frame_; bool speed_output_; int width_, height_; - float markerPos_[2]; - float relativePos_[2]; + float marker_pos_[2]; + float relative_pos_[2]; float track_position_; bool is_track_position_calculated_; @@ -58,42 +58,42 @@ class TrackPositionOperation : public ConstantOperation { public: TrackPositionOperation(); - void setMovieClip(MovieClip *clip) + void set_movie_clip(MovieClip *clip) { - movieClip_ = clip; + movie_clip_ = clip; } - void setTrackingObject(char *object) + void set_tracking_object(char *object) { - BLI_strncpy(trackingObjectName_, object, sizeof(trackingObjectName_)); + BLI_strncpy(tracking_object_name_, object, sizeof(tracking_object_name_)); } - void setTrackName(char *track) + void set_track_name(char *track) { - BLI_strncpy(trackName_, track, sizeof(trackName_)); + BLI_strncpy(track_name_, track, sizeof(track_name_)); } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { framenumber_ = framenumber; } - void setAxis(int value) + void set_axis(int value) { axis_ = value; } - void setPosition(int value) + void set_position(int value) { position_ = value; } - void setRelativeFrame(int value) + void set_relative_frame(int value) { - relativeFrame_ = value; + relative_frame_ = value; } - void setSpeedOutput(bool speed_output) + void set_speed_output(bool speed_output) { speed_output_ = speed_output; } - void initExecution() override; + void init_execution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; const float *get_constant_elem() override; diff --git a/source/blender/compositor/operations/COM_TransformOperation.cc b/source/blender/compositor/operations/COM_TransformOperation.cc index 38bb443bdea..be9bb32e7f0 100644 --- a/source/blender/compositor/operations/COM_TransformOperation.cc +++ b/source/blender/compositor/operations/COM_TransformOperation.cc @@ -24,12 +24,12 @@ namespace blender::compositor { TransformOperation::TransformOperation() { - addInputSocket(DataType::Color, ResizeMode::None); - addInputSocket(DataType::Value, ResizeMode::None); - addInputSocket(DataType::Value, ResizeMode::None); - addInputSocket(DataType::Value, ResizeMode::None); - addInputSocket(DataType::Value, ResizeMode::None); - addOutputSocket(DataType::Color); + add_input_socket(DataType::Color, ResizeMode::None); + add_input_socket(DataType::Value, ResizeMode::None); + add_input_socket(DataType::Value, ResizeMode::None); + add_input_socket(DataType::Value, ResizeMode::None); + add_input_socket(DataType::Value, ResizeMode::None); + add_output_socket(DataType::Color); translate_factor_x_ = 1.0f; translate_factor_y_ = 1.0f; convert_degree_to_rad_ = false; @@ -123,14 +123,14 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output, void TransformOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { const bool image_determined = - getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + get_input_socket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); if (image_determined) { rcti image_canvas = r_area; rcti unused; - getInputSocket(X_INPUT_INDEX)->determine_canvas(image_canvas, unused); - getInputSocket(Y_INPUT_INDEX)->determine_canvas(image_canvas, unused); - getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(image_canvas, unused); - getInputSocket(SCALE_INPUT_INDEX)->determine_canvas(image_canvas, unused); + get_input_socket(X_INPUT_INDEX)->determine_canvas(image_canvas, unused); + get_input_socket(Y_INPUT_INDEX)->determine_canvas(image_canvas, unused); + get_input_socket(DEGREE_INPUT_INDEX)->determine_canvas(image_canvas, unused); + get_input_socket(SCALE_INPUT_INDEX)->determine_canvas(image_canvas, unused); init_data(); if (invert_) { diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc index 1ec5029385e..89c7468a67c 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.cc +++ b/source/blender/compositor/operations/COM_TranslateOperation.cc @@ -25,68 +25,68 @@ TranslateOperation::TranslateOperation() : TranslateOperation(DataType::Color) } TranslateOperation::TranslateOperation(DataType data_type, ResizeMode resize_mode) { - this->addInputSocket(data_type, resize_mode); - this->addInputSocket(DataType::Value, ResizeMode::None); - this->addInputSocket(DataType::Value, ResizeMode::None); - this->addOutputSocket(data_type); + this->add_input_socket(data_type, resize_mode); + this->add_input_socket(DataType::Value, ResizeMode::None); + this->add_input_socket(DataType::Value, ResizeMode::None); + this->add_output_socket(data_type); this->set_canvas_input_index(0); - inputOperation_ = nullptr; - inputXOperation_ = nullptr; - inputYOperation_ = nullptr; - isDeltaSet_ = false; - factorX_ = 1.0f; - factorY_ = 1.0f; + input_operation_ = nullptr; + input_xoperation_ = nullptr; + input_yoperation_ = nullptr; + is_delta_set_ = false; + factor_x_ = 1.0f; + factor_y_ = 1.0f; this->x_extend_mode_ = MemoryBufferExtend::Clip; this->y_extend_mode_ = MemoryBufferExtend::Clip; } -void TranslateOperation::initExecution() +void TranslateOperation::init_execution() { - inputOperation_ = this->getInputSocketReader(0); - inputXOperation_ = this->getInputSocketReader(1); - inputYOperation_ = this->getInputSocketReader(2); + input_operation_ = this->get_input_socket_reader(0); + input_xoperation_ = this->get_input_socket_reader(1); + input_yoperation_ = this->get_input_socket_reader(2); } -void TranslateOperation::deinitExecution() +void TranslateOperation::deinit_execution() { - inputOperation_ = nullptr; - inputXOperation_ = nullptr; - inputYOperation_ = nullptr; + input_operation_ = nullptr; + input_xoperation_ = nullptr; + input_yoperation_ = nullptr; } -void TranslateOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void TranslateOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - ensureDelta(); + ensure_delta(); - float originalXPos = x - this->getDeltaX(); - float originalYPos = y - this->getDeltaY(); + float original_xpos = x - this->getDeltaX(); + float original_ypos = y - this->getDeltaY(); - inputOperation_->readSampled(output, originalXPos, originalYPos, PixelSampler::Bilinear); + input_operation_->read_sampled(output, original_xpos, original_ypos, PixelSampler::Bilinear); } -bool TranslateOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool TranslateOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - ensureDelta(); + ensure_delta(); - newInput.xmin = input->xmin - this->getDeltaX(); - newInput.xmax = input->xmax - this->getDeltaX(); - newInput.ymin = input->ymin - this->getDeltaY(); - newInput.ymax = input->ymax - this->getDeltaY(); + new_input.xmin = input->xmin - this->getDeltaX(); + new_input.xmax = input->xmax - this->getDeltaX(); + new_input.ymin = input->ymin - this->getDeltaY(); + new_input.ymax = input->ymax - this->getDeltaY(); - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void TranslateOperation::setFactorXY(float factorX, float factorY) { - factorX_ = factorX; - factorY_ = factorY; + factor_x_ = factorX; + factor_y_ = factorY; } void TranslateOperation::set_wrapping(int wrapping_type) @@ -112,7 +112,7 @@ void TranslateOperation::get_area_of_interest(const int input_idx, rcti &r_input_area) { if (input_idx == 0) { - ensureDelta(); + ensure_delta(); r_input_area = output_area; if (x_extend_mode_ == MemoryBufferExtend::Clip) { const int delta_x = this->getDeltaX(); @@ -154,15 +154,15 @@ TranslateCanvasOperation::TranslateCanvasOperation() void TranslateCanvasOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { const bool determined = - getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + get_input_socket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); if (determined) { - NodeOperationInput *x_socket = getInputSocket(X_INPUT_INDEX); - NodeOperationInput *y_socket = getInputSocket(Y_INPUT_INDEX); + NodeOperationInput *x_socket = get_input_socket(X_INPUT_INDEX); + NodeOperationInput *y_socket = get_input_socket(Y_INPUT_INDEX); rcti unused; x_socket->determine_canvas(r_area, unused); y_socket->determine_canvas(r_area, unused); - ensureDelta(); + ensure_delta(); const float delta_x = x_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaX() : 0.0f; const float delta_y = y_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaY() : 0.0f; BLI_rcti_translate(&r_area, delta_x, delta_y); diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h index 25251ff1d9e..30e489bf395 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.h +++ b/source/blender/compositor/operations/COM_TranslateOperation.h @@ -30,14 +30,14 @@ class TranslateOperation : public MultiThreadedOperation { static constexpr int Y_INPUT_INDEX = 2; private: - SocketReader *inputOperation_; - SocketReader *inputXOperation_; - SocketReader *inputYOperation_; - float deltaX_; - float deltaY_; - bool isDeltaSet_; - float factorX_; - float factorY_; + SocketReader *input_operation_; + SocketReader *input_xoperation_; + SocketReader *input_yoperation_; + float delta_x_; + float delta_y_; + bool is_delta_set_; + float factor_x_; + float factor_y_; protected: MemoryBufferExtend x_extend_mode_; @@ -46,39 +46,39 @@ class TranslateOperation : public MultiThreadedOperation { public: TranslateOperation(); TranslateOperation(DataType data_type, ResizeMode mode = ResizeMode::Center); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; float getDeltaX() { - return deltaX_ * factorX_; + return delta_x_ * factor_x_; } float getDeltaY() { - return deltaY_ * factorY_; + return delta_y_ * factor_y_; } - inline void ensureDelta() + inline void ensure_delta() { - if (!isDeltaSet_) { + if (!is_delta_set_) { if (execution_model_ == eExecutionModel::Tiled) { - float tempDelta[4]; - inputXOperation_->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); - deltaX_ = tempDelta[0]; - inputYOperation_->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); - deltaY_ = tempDelta[0]; + float temp_delta[4]; + input_xoperation_->read_sampled(temp_delta, 0, 0, PixelSampler::Nearest); + delta_x_ = temp_delta[0]; + input_yoperation_->read_sampled(temp_delta, 0, 0, PixelSampler::Nearest); + delta_y_ = temp_delta[0]; } else { - deltaX_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f); - deltaY_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f); + delta_x_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f); + delta_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f); } - isDeltaSet_ = true; + is_delta_set_ = true; } } diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc index a0013ae1061..07baf370d1d 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc @@ -23,127 +23,128 @@ namespace blender::compositor { VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color, ResizeMode::Align); /* Do not resize the bokeh image. */ - this->addInputSocket(DataType::Value); /* Radius. */ + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color, ResizeMode::Align); /* Do not resize the bokeh image. */ + this->add_input_socket(DataType::Value); /* Radius. */ #ifdef COM_DEFOCUS_SEARCH /* Inverse search radius optimization structure. */ - this->addInputSocket(DataType::Color, ResizeMode::None); + this->add_input_socket(DataType::Color, ResizeMode::None); #endif - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); flags.complex = true; flags.open_cl = true; - inputProgram_ = nullptr; - inputBokehProgram_ = nullptr; - inputSizeProgram_ = nullptr; - maxBlur_ = 32.0f; + input_program_ = nullptr; + input_bokeh_program_ = nullptr; + input_size_program_ = nullptr; + max_blur_ = 32.0f; threshold_ = 1.0f; do_size_scale_ = false; #ifdef COM_DEFOCUS_SEARCH - inputSearchProgram_ = nullptr; + input_search_program_ = nullptr; #endif } -void VariableSizeBokehBlurOperation::initExecution() +void VariableSizeBokehBlurOperation::init_execution() { - inputProgram_ = getInputSocketReader(0); - inputBokehProgram_ = getInputSocketReader(1); - inputSizeProgram_ = getInputSocketReader(2); + input_program_ = get_input_socket_reader(0); + input_bokeh_program_ = get_input_socket_reader(1); + input_size_program_ = get_input_socket_reader(2); #ifdef COM_DEFOCUS_SEARCH - inputSearchProgram_ = getInputSocketReader(3); + input_search_program_ = get_input_socket_reader(3); #endif - QualityStepHelper::initExecution(COM_QH_INCREASE); + QualityStepHelper::init_execution(COM_QH_INCREASE); } struct VariableSizeBokehBlurTileData { MemoryBuffer *color; MemoryBuffer *bokeh; MemoryBuffer *size; - int maxBlurScalar; + int max_blur_scalar; }; -void *VariableSizeBokehBlurOperation::initializeTileData(rcti *rect) +void *VariableSizeBokehBlurOperation::initialize_tile_data(rcti *rect) { VariableSizeBokehBlurTileData *data = new VariableSizeBokehBlurTileData(); - data->color = (MemoryBuffer *)inputProgram_->initializeTileData(rect); - data->bokeh = (MemoryBuffer *)inputBokehProgram_->initializeTileData(rect); - data->size = (MemoryBuffer *)inputSizeProgram_->initializeTileData(rect); + data->color = (MemoryBuffer *)input_program_->initialize_tile_data(rect); + data->bokeh = (MemoryBuffer *)input_bokeh_program_->initialize_tile_data(rect); + data->size = (MemoryBuffer *)input_size_program_->initialize_tile_data(rect); rcti rect2; - this->determineDependingAreaOfInterest(rect, (ReadBufferOperation *)inputSizeProgram_, &rect2); + this->determine_depending_area_of_interest( + rect, (ReadBufferOperation *)input_size_program_, &rect2); - const float max_dim = MAX2(this->getWidth(), this->getHeight()); + const float max_dim = MAX2(this->get_width(), this->get_height()); const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; - data->maxBlurScalar = (int)(data->size->get_max_value(rect2) * scalar); - CLAMP(data->maxBlurScalar, 1.0f, maxBlur_); + data->max_blur_scalar = (int)(data->size->get_max_value(rect2) * scalar); + CLAMP(data->max_blur_scalar, 1.0f, max_blur_); return data; } -void VariableSizeBokehBlurOperation::deinitializeTileData(rcti * /*rect*/, void *data) +void VariableSizeBokehBlurOperation::deinitialize_tile_data(rcti * /*rect*/, void *data) { VariableSizeBokehBlurTileData *result = (VariableSizeBokehBlurTileData *)data; delete result; } -void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, void *data) +void VariableSizeBokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { - VariableSizeBokehBlurTileData *tileData = (VariableSizeBokehBlurTileData *)data; - MemoryBuffer *inputProgramBuffer = tileData->color; - MemoryBuffer *inputBokehBuffer = tileData->bokeh; - MemoryBuffer *inputSizeBuffer = tileData->size; - float *inputSizeFloatBuffer = inputSizeBuffer->getBuffer(); - float *inputProgramFloatBuffer = inputProgramBuffer->getBuffer(); - float readColor[4]; + VariableSizeBokehBlurTileData *tile_data = (VariableSizeBokehBlurTileData *)data; + MemoryBuffer *input_program_buffer = tile_data->color; + MemoryBuffer *input_bokeh_buffer = tile_data->bokeh; + MemoryBuffer *input_size_buffer = tile_data->size; + float *input_size_float_buffer = input_size_buffer->get_buffer(); + float *input_program_float_buffer = input_program_buffer->get_buffer(); + float read_color[4]; float bokeh[4]; - float tempSize[4]; + float temp_size[4]; float multiplier_accum[4]; float color_accum[4]; - const float max_dim = MAX2(getWidth(), getHeight()); + const float max_dim = MAX2(get_width(), get_height()); const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; - int maxBlurScalar = tileData->maxBlurScalar; + int max_blur_scalar = tile_data->max_blur_scalar; - BLI_assert(inputBokehBuffer->getWidth() == COM_BLUR_BOKEH_PIXELS); - BLI_assert(inputBokehBuffer->getHeight() == COM_BLUR_BOKEH_PIXELS); + BLI_assert(input_bokeh_buffer->get_width() == COM_BLUR_BOKEH_PIXELS); + BLI_assert(input_bokeh_buffer->get_height() == COM_BLUR_BOKEH_PIXELS); #ifdef COM_DEFOCUS_SEARCH float search[4]; - inputSearchProgram_->read(search, - x / InverseSearchRadiusOperation::DIVIDER, - y / InverseSearchRadiusOperation::DIVIDER, - nullptr); + input_search_program_->read(search, + x / InverseSearchRadiusOperation::DIVIDER, + y / InverseSearchRadiusOperation::DIVIDER, + nullptr); int minx = search[0]; int miny = search[1]; int maxx = search[2]; int maxy = search[3]; #else - int minx = MAX2(x - maxBlurScalar, 0); - int miny = MAX2(y - maxBlurScalar, 0); - int maxx = MIN2(x + maxBlurScalar, (int)getWidth()); - int maxy = MIN2(y + maxBlurScalar, (int)getHeight()); + int minx = MAX2(x - max_blur_scalar, 0); + int miny = MAX2(y - max_blur_scalar, 0); + int maxx = MIN2(x + max_blur_scalar, (int)get_width()); + int maxy = MIN2(y + max_blur_scalar, (int)get_height()); #endif { - inputSizeBuffer->readNoCheck(tempSize, x, y); - inputProgramBuffer->readNoCheck(readColor, x, y); + input_size_buffer->read_no_check(temp_size, x, y); + input_program_buffer->read_no_check(read_color, x, y); - copy_v4_v4(color_accum, readColor); + copy_v4_v4(color_accum, read_color); copy_v4_fl(multiplier_accum, 1.0f); - float size_center = tempSize[0] * scalar; + float size_center = temp_size[0] * scalar; - const int addXStepValue = QualityStepHelper::getStep(); - const int addYStepValue = addXStepValue; - const int addXStepColor = addXStepValue * COM_DATA_TYPE_COLOR_CHANNELS; + const int add_xstep_value = QualityStepHelper::get_step(); + const int add_ystep_value = add_xstep_value; + const int add_xstep_color = add_xstep_value * COM_DATA_TYPE_COLOR_CHANNELS; if (size_center > threshold_) { - for (int ny = miny; ny < maxy; ny += addYStepValue) { + for (int ny = miny; ny < maxy; ny += add_ystep_value) { float dy = ny - y; - int offsetValueNy = ny * inputSizeBuffer->getWidth(); - int offsetValueNxNy = offsetValueNy + (minx); - int offsetColorNxNy = offsetValueNxNy * COM_DATA_TYPE_COLOR_CHANNELS; - for (int nx = minx; nx < maxx; nx += addXStepValue) { + int offset_value_ny = ny * input_size_buffer->get_width(); + int offset_value_nx_ny = offset_value_ny + (minx); + int offset_color_nx_ny = offset_value_nx_ny * COM_DATA_TYPE_COLOR_CHANNELS; + for (int nx = minx; nx < maxx; nx += add_xstep_value) { if (nx != x || ny != y) { - float size = MIN2(inputSizeFloatBuffer[offsetValueNxNy] * scalar, size_center); + float size = MIN2(input_size_float_buffer[offset_value_nx_ny] * scalar, size_center); if (size > threshold_) { float dx = nx - x; if (size > fabsf(dx) && size > fabsf(dy)) { @@ -153,14 +154,14 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, (float)(COM_BLUR_BOKEH_PIXELS / 2) + (dy / size) * (float)((COM_BLUR_BOKEH_PIXELS / 2) - 1), }; - inputBokehBuffer->read(bokeh, uv[0], uv[1]); - madd_v4_v4v4(color_accum, bokeh, &inputProgramFloatBuffer[offsetColorNxNy]); + input_bokeh_buffer->read(bokeh, uv[0], uv[1]); + madd_v4_v4v4(color_accum, bokeh, &input_program_float_buffer[offset_color_nx_ny]); add_v4_v4(multiplier_accum, bokeh); } } } - offsetColorNxNy += addXStepColor; - offsetValueNxNy += addXStepValue; + offset_color_nx_ny += add_xstep_color; + offset_value_nx_ny += add_xstep_value; } } } @@ -174,98 +175,102 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, if ((size_center > threshold_) && (size_center < threshold_ * 2.0f)) { /* factor from 0-1 */ float fac = (size_center - threshold_) / threshold_; - interp_v4_v4v4(output, readColor, output, fac); + interp_v4_v4v4(output, read_color, output, fac); } } } -void VariableSizeBokehBlurOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void VariableSizeBokehBlurOperation::execute_opencl( + OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel defocusKernel = device->COM_clCreateKernel("defocusKernel", nullptr); + cl_kernel defocus_kernel = device->COM_cl_create_kernel("defocus_kernel", nullptr); - cl_int step = this->getStep(); - cl_int maxBlur; + cl_int step = this->get_step(); + cl_int max_blur; cl_float threshold = threshold_; - MemoryBuffer *sizeMemoryBuffer = inputSizeProgram_->getInputMemoryBuffer(inputMemoryBuffers); + MemoryBuffer *size_memory_buffer = input_size_program_->get_input_memory_buffer( + input_memory_buffers); - const float max_dim = MAX2(getWidth(), getHeight()); + const float max_dim = MAX2(get_width(), get_height()); cl_float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; - maxBlur = (cl_int)min_ff(sizeMemoryBuffer->get_max_value() * scalar, (float)maxBlur_); + max_blur = (cl_int)min_ff(size_memory_buffer->get_max_value() * scalar, (float)max_blur_); - device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, inputProgram_); - device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 1, -1, clMemToCleanUp, inputMemoryBuffers, inputBokehProgram_); - device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 2, 4, clMemToCleanUp, inputMemoryBuffers, inputSizeProgram_); - device->COM_clAttachOutputMemoryBufferToKernelParameter(defocusKernel, 3, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter(defocusKernel, 5, outputMemoryBuffer); - clSetKernelArg(defocusKernel, 6, sizeof(cl_int), &step); - clSetKernelArg(defocusKernel, 7, sizeof(cl_int), &maxBlur); - clSetKernelArg(defocusKernel, 8, sizeof(cl_float), &threshold); - clSetKernelArg(defocusKernel, 9, sizeof(cl_float), &scalar); - device->COM_clAttachSizeToKernelParameter(defocusKernel, 10, this); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + defocus_kernel, 0, -1, cl_mem_to_clean_up, input_memory_buffers, input_program_); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + defocus_kernel, 1, -1, cl_mem_to_clean_up, input_memory_buffers, input_bokeh_program_); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + defocus_kernel, 2, 4, cl_mem_to_clean_up, input_memory_buffers, input_size_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + defocus_kernel, 3, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + defocus_kernel, 5, output_memory_buffer); + clSetKernelArg(defocus_kernel, 6, sizeof(cl_int), &step); + clSetKernelArg(defocus_kernel, 7, sizeof(cl_int), &max_blur); + clSetKernelArg(defocus_kernel, 8, sizeof(cl_float), &threshold); + clSetKernelArg(defocus_kernel, 9, sizeof(cl_float), &scalar); + device->COM_cl_attach_size_to_kernel_parameter(defocus_kernel, 10, this); - device->COM_clEnqueueRange(defocusKernel, outputMemoryBuffer, 11, this); + device->COM_cl_enqueue_range(defocus_kernel, output_memory_buffer, 11, this); } -void VariableSizeBokehBlurOperation::deinitExecution() +void VariableSizeBokehBlurOperation::deinit_execution() { - inputProgram_ = nullptr; - inputBokehProgram_ = nullptr; - inputSizeProgram_ = nullptr; + input_program_ = nullptr; + input_bokeh_program_ = nullptr; + input_size_program_ = nullptr; #ifdef COM_DEFOCUS_SEARCH - inputSearchProgram_ = nullptr; + input_search_program_ = nullptr; #endif } -bool VariableSizeBokehBlurOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool VariableSizeBokehBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - rcti bokehInput; + rcti new_input; + rcti bokeh_input; - const float max_dim = MAX2(getWidth(), getHeight()); + const float max_dim = MAX2(get_width(), get_height()); const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; - int maxBlurScalar = maxBlur_ * scalar; + int max_blur_scalar = max_blur_ * scalar; - newInput.xmax = input->xmax + maxBlurScalar + 2; - newInput.xmin = input->xmin - maxBlurScalar + 2; - newInput.ymax = input->ymax + maxBlurScalar - 2; - newInput.ymin = input->ymin - maxBlurScalar - 2; - bokehInput.xmax = COM_BLUR_BOKEH_PIXELS; - bokehInput.xmin = 0; - bokehInput.ymax = COM_BLUR_BOKEH_PIXELS; - bokehInput.ymin = 0; + new_input.xmax = input->xmax + max_blur_scalar + 2; + new_input.xmin = input->xmin - max_blur_scalar + 2; + new_input.ymax = input->ymax + max_blur_scalar - 2; + new_input.ymin = input->ymin - max_blur_scalar - 2; + bokeh_input.xmax = COM_BLUR_BOKEH_PIXELS; + bokeh_input.xmin = 0; + bokeh_input.ymax = COM_BLUR_BOKEH_PIXELS; + bokeh_input.ymin = 0; - NodeOperation *operation = getInputOperation(2); - if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + NodeOperation *operation = get_input_operation(2); + if (operation->determine_depending_area_of_interest(&new_input, read_operation, output)) { return true; } - operation = getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&bokehInput, readOperation, output)) { + operation = get_input_operation(1); + if (operation->determine_depending_area_of_interest(&bokeh_input, read_operation, output)) { return true; } #ifdef COM_DEFOCUS_SEARCH - rcti searchInput; - searchInput.xmax = (input->xmax / InverseSearchRadiusOperation::DIVIDER) + 1; - searchInput.xmin = (input->xmin / InverseSearchRadiusOperation::DIVIDER) - 1; - searchInput.ymax = (input->ymax / InverseSearchRadiusOperation::DIVIDER) + 1; - searchInput.ymin = (input->ymin / InverseSearchRadiusOperation::DIVIDER) - 1; - operation = getInputOperation(3); - if (operation->determineDependingAreaOfInterest(&searchInput, readOperation, output)) { + rcti search_input; + search_input.xmax = (input->xmax / InverseSearchRadiusOperation::DIVIDER) + 1; + search_input.xmin = (input->xmin / InverseSearchRadiusOperation::DIVIDER) - 1; + search_input.ymax = (input->ymax / InverseSearchRadiusOperation::DIVIDER) + 1; + search_input.ymin = (input->ymin / InverseSearchRadiusOperation::DIVIDER) - 1; + operation = get_input_operation(3); + if (operation->determine_depending_area_of_interest(&search_input, read_operation, output)) { return true; } #endif - operation = getInputOperation(0); - if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + operation = get_input_operation(0); + if (operation->determine_depending_area_of_interest(&new_input, read_operation, output)) { return true; } return false; @@ -278,9 +283,9 @@ void VariableSizeBokehBlurOperation::get_area_of_interest(const int input_idx, switch (input_idx) { case IMAGE_INPUT_INDEX: case SIZE_INPUT_INDEX: { - const float max_dim = MAX2(getWidth(), getHeight()); + const float max_dim = MAX2(get_width(), get_height()); const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; - const int max_blur_scalar = maxBlur_ * scalar; + const int max_blur_scalar = max_blur_ * scalar; r_input_area.xmax = output_area.xmax + max_blur_scalar + 2; r_input_area.xmin = output_area.xmin - max_blur_scalar - 2; r_input_area.ymax = output_area.ymax + max_blur_scalar + 2; @@ -322,8 +327,8 @@ struct PixelData { static void blur_pixel(int x, int y, PixelData &p) { - BLI_assert(p.bokeh_input->getWidth() == COM_BLUR_BOKEH_PIXELS); - BLI_assert(p.bokeh_input->getHeight() == COM_BLUR_BOKEH_PIXELS); + BLI_assert(p.bokeh_input->get_width() == COM_BLUR_BOKEH_PIXELS); + BLI_assert(p.bokeh_input->get_height() == COM_BLUR_BOKEH_PIXELS); #ifdef COM_DEFOCUS_SEARCH float search[4]; @@ -388,20 +393,20 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer * p.bokeh_input = inputs[BOKEH_INPUT_INDEX]; p.size_input = inputs[SIZE_INPUT_INDEX]; p.image_input = inputs[IMAGE_INPUT_INDEX]; - p.step = QualityStepHelper::getStep(); + p.step = QualityStepHelper::get_step(); p.threshold = threshold_; - p.image_width = this->getWidth(); - p.image_height = this->getHeight(); + p.image_width = this->get_width(); + p.image_height = this->get_height(); rcti scalar_area; this->get_area_of_interest(SIZE_INPUT_INDEX, area, scalar_area); BLI_rcti_isect(&scalar_area, &p.size_input->get_rect(), &scalar_area); const float max_size = p.size_input->get_max_value(scalar_area); - const float max_dim = MAX2(this->getWidth(), this->getHeight()); + const float max_dim = MAX2(this->get_width(), this->get_height()); p.scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; p.max_blur_scalar = static_cast(max_size * p.scalar); - CLAMP(p.max_blur_scalar, 1, maxBlur_); + CLAMP(p.max_blur_scalar, 1, max_blur_); for (BuffersIterator it = output->iterate_with({p.image_input, p.size_input}, area); !it.is_end(); @@ -434,34 +439,34 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer * /* #InverseSearchRadiusOperation. */ InverseSearchRadiusOperation::InverseSearchRadiusOperation() { - this->addInputSocket(DataType::Value, ResizeMode::Align); /* Radius. */ - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value, ResizeMode::Align); /* Radius. */ + this->add_output_socket(DataType::Color); this->flags.complex = true; - inputRadius_ = nullptr; + input_radius_ = nullptr; } -void InverseSearchRadiusOperation::initExecution() +void InverseSearchRadiusOperation::init_execution() { - inputRadius_ = this->getInputSocketReader(0); + input_radius_ = this->get_input_socket_reader(0); } -void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) +void *InverseSearchRadiusOperation::initialize_tile_data(rcti *rect) { MemoryBuffer *data = new MemoryBuffer(DataType::Color, rect); - float *buffer = data->getBuffer(); + float *buffer = data->get_buffer(); int x, y; - int width = inputRadius_->getWidth(); - int height = inputRadius_->getHeight(); + int width = input_radius_->get_width(); + int height = input_radius_->get_height(); float temp[4]; int offset = 0; for (y = rect->ymin; y < rect->ymax; y++) { for (x = rect->xmin; x < rect->xmax; x++) { int rx = x * DIVIDER; int ry = y * DIVIDER; - buffer[offset] = MAX2(rx - maxBlur_, 0); - buffer[offset + 1] = MAX2(ry - maxBlur_, 0); - buffer[offset + 2] = MIN2(rx + DIVIDER + maxBlur_, width); - buffer[offset + 3] = MIN2(ry + DIVIDER + maxBlur_, height); + buffer[offset] = MAX2(rx - max_blur_, 0); + buffer[offset + 1] = MAX2(ry - max_blur_, 0); + buffer[offset + 2] = MIN2(rx + DIVIDER + max_blur_, width); + buffer[offset + 3] = MIN2(ry + DIVIDER + max_blur_, height); offset += 4; } } @@ -476,7 +481,7 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) for (int x2 = 0; x2 < DIVIDER; x2++) { for (int y2 = 0; y2 < DIVIDER; y2++) { - inputRadius_->read(temp, rx + x2, ry + y2, PixelSampler::Nearest); + input_radius_->read(temp, rx + x2, ry + y2, PixelSampler::Nearest); if (radius < temp[0]) { radius = temp[0]; maxx = x2; @@ -484,15 +489,15 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) } } } - int impactRadius = ceil(radius / DIVIDER); - for (int x2 = x - impactRadius; x2 < x + impactRadius; x2++) { - for (int y2 = y - impactRadius; y2 < y + impactRadius; y2++) { + int impact_radius = ceil(radius / DIVIDER); + for (int x2 = x - impact_radius; x2 < x + impact_radius; x2++) { + for (int y2 = y - impact_radius; y2 < y + impact_radius; y2++) { data->read(temp, x2, y2); temp[0] = MIN2(temp[0], maxx); temp[1] = MIN2(temp[1], maxy); temp[2] = MAX2(temp[2], maxx); temp[3] = MAX2(temp[3], maxy); - data->writePixel(x2, y2, temp); + data->write_pixel(x2, y2, temp); } } } @@ -501,13 +506,13 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) return data; } -void InverseSearchRadiusOperation::executePixelChunk(float output[4], int x, int y, void *data) +void InverseSearchRadiusOperation::execute_pixel_chunk(float output[4], int x, int y, void *data) { MemoryBuffer *buffer = (MemoryBuffer *)data; - buffer->readNoCheck(output, x, y); + buffer->read_no_check(output, x, y); } -void InverseSearchRadiusOperation::deinitializeTileData(rcti *rect, void *data) +void InverseSearchRadiusOperation::deinitialize_tile_data(rcti *rect, void *data) { if (data) { MemoryBuffer *mb = (MemoryBuffer *)data; @@ -515,28 +520,28 @@ void InverseSearchRadiusOperation::deinitializeTileData(rcti *rect, void *data) } } -void InverseSearchRadiusOperation::deinitExecution() +void InverseSearchRadiusOperation::deinit_execution() { - inputRadius_ = nullptr; + input_radius_ = nullptr; } -void InverseSearchRadiusOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void InverseSearchRadiusOperation::determine_resolution(unsigned int resolution[2], + unsigned int preferred_resolution[2]) { - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_resolution(resolution, preferred_resolution); resolution[0] = resolution[0] / DIVIDER; resolution[1] = resolution[1] / DIVIDER; } -bool InverseSearchRadiusOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool InverseSearchRadiusOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newRect; - newRect.ymin = input->ymin * DIVIDER - maxBlur_; - newRect.ymax = input->ymax * DIVIDER + maxBlur_; - newRect.xmin = input->xmin * DIVIDER - maxBlur_; - newRect.xmax = input->xmax * DIVIDER + maxBlur_; - return NodeOperation::determineDependingAreaOfInterest(&newRect, readOperation, output); + rcti new_rect; + new_rect.ymin = input->ymin * DIVIDER - max_blur_; + new_rect.ymax = input->ymax * DIVIDER + max_blur_; + new_rect.xmin = input->xmin * DIVIDER - max_blur_; + new_rect.xmax = input->xmax * DIVIDER + max_blur_; + return NodeOperation::determine_depending_area_of_interest(&new_rect, read_operation, output); } #endif diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h index c32d10ae3b9..7e53ed8a979 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h @@ -34,14 +34,14 @@ class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public Qua static constexpr int DEFOCUS_INPUT_INDEX = 3; #endif - int maxBlur_; + int max_blur_; float threshold_; bool do_size_scale_; /* scale size, matching 'BokehBlurNode' */ - SocketReader *inputProgram_; - SocketReader *inputBokehProgram_; - SocketReader *inputSizeProgram_; + SocketReader *input_program_; + SocketReader *input_bokeh_program_; + SocketReader *input_size_program_; #ifdef COM_DEFOCUS_SEARCH - SocketReader *inputSearchProgram_; + SocketReader *input_search_program_; #endif public: @@ -50,47 +50,47 @@ class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public Qua /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - void deinitializeTileData(rcti *rect, void *data) override; + void deinitialize_tile_data(rcti *rect, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setMaxBlur(int maxRadius) + void set_max_blur(int max_radius) { - maxBlur_ = maxRadius; + max_blur_ = max_radius; } - void setThreshold(float threshold) + void set_threshold(float threshold) { threshold_ = threshold; } - void setDoScaleSize(bool scale_size) + void set_do_scale_size(bool scale_size) { do_size_scale_ = scale_size; } - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) 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, @@ -102,8 +102,8 @@ class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public Qua #ifdef COM_DEFOCUS_SEARCH class InverseSearchRadiusOperation : public NodeOperation { private: - int maxBlur_; - SocketReader *inputRadius_; + int max_blur_; + SocketReader *input_radius_; public: static const int DIVIDER = 4; @@ -113,28 +113,28 @@ class InverseSearchRadiusOperation : public NodeOperation { /** * The inner loop of this operation. */ - void executePixelChunk(float output[4], int x, int y, void *data); + void execute_pixel_chunk(float output[4], int x, int y, void *data); /** * Initialize the execution */ - void initExecution() override; - void *initializeTileData(rcti *rect) override; - void deinitializeTileData(rcti *rect, void *data) override; + void init_execution() override; + void *initialize_tile_data(rcti *rect) override; + void deinitialize_tile_data(rcti *rect, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void setMaxBlur(int maxRadius) + void set_max_blur(int max_radius) { - maxBlur_ = maxRadius; + max_blur_ = max_radius; } }; #endif diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc index 6ebd8e27d76..8fd074749cd 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc @@ -42,76 +42,76 @@ void antialias_tagbuf(int xsize, int ysize, char *rectmove); /* VectorBlurOperation */ VectorBlurOperation::VectorBlurOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); /* ZBUF */ - this->addInputSocket(DataType::Color); /* SPEED */ - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); /* ZBUF */ + this->add_input_socket(DataType::Color); /* SPEED */ + this->add_output_socket(DataType::Color); settings_ = nullptr; - cachedInstance_ = nullptr; - inputImageProgram_ = nullptr; - inputSpeedProgram_ = nullptr; - inputZProgram_ = nullptr; + cached_instance_ = nullptr; + input_image_program_ = nullptr; + input_speed_program_ = nullptr; + input_zprogram_ = nullptr; flags.complex = true; flags.is_fullframe_operation = true; } -void VectorBlurOperation::initExecution() +void VectorBlurOperation::init_execution() { - initMutex(); - inputImageProgram_ = getInputSocketReader(0); - inputZProgram_ = getInputSocketReader(1); - inputSpeedProgram_ = getInputSocketReader(2); - cachedInstance_ = nullptr; - QualityStepHelper::initExecution(COM_QH_INCREASE); + init_mutex(); + input_image_program_ = get_input_socket_reader(0); + input_zprogram_ = get_input_socket_reader(1); + input_speed_program_ = get_input_socket_reader(2); + cached_instance_ = nullptr; + QualityStepHelper::init_execution(COM_QH_INCREASE); } -void VectorBlurOperation::executePixel(float output[4], int x, int y, void *data) +void VectorBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { float *buffer = (float *)data; - int index = (y * this->getWidth() + x) * COM_DATA_TYPE_COLOR_CHANNELS; + int index = (y * this->get_width() + x) * COM_DATA_TYPE_COLOR_CHANNELS; copy_v4_v4(output, &buffer[index]); } -void VectorBlurOperation::deinitExecution() +void VectorBlurOperation::deinit_execution() { - deinitMutex(); - inputImageProgram_ = nullptr; - inputSpeedProgram_ = nullptr; - inputZProgram_ = nullptr; - if (cachedInstance_) { - MEM_freeN(cachedInstance_); - cachedInstance_ = nullptr; + deinit_mutex(); + input_image_program_ = nullptr; + input_speed_program_ = nullptr; + input_zprogram_ = nullptr; + if (cached_instance_) { + MEM_freeN(cached_instance_); + cached_instance_ = nullptr; } } -void *VectorBlurOperation::initializeTileData(rcti *rect) +void *VectorBlurOperation::initialize_tile_data(rcti *rect) { - if (cachedInstance_) { - return cachedInstance_; + if (cached_instance_) { + return cached_instance_; } - lockMutex(); - if (cachedInstance_ == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)inputImageProgram_->initializeTileData(rect); - MemoryBuffer *speed = (MemoryBuffer *)inputSpeedProgram_->initializeTileData(rect); - MemoryBuffer *z = (MemoryBuffer *)inputZProgram_->initializeTileData(rect); - float *data = (float *)MEM_dupallocN(tile->getBuffer()); - this->generateVectorBlur(data, tile, speed, z); - cachedInstance_ = data; + lock_mutex(); + if (cached_instance_ == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)input_image_program_->initialize_tile_data(rect); + MemoryBuffer *speed = (MemoryBuffer *)input_speed_program_->initialize_tile_data(rect); + MemoryBuffer *z = (MemoryBuffer *)input_zprogram_->initialize_tile_data(rect); + float *data = (float *)MEM_dupallocN(tile->get_buffer()); + this->generate_vector_blur(data, tile, speed, z); + cached_instance_ = data; } - unlockMutex(); - return cachedInstance_; + unlock_mutex(); + return cached_instance_; } -bool VectorBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool VectorBlurOperation::determine_depending_area_of_interest(rcti * /*input*/, + ReadBufferOperation *read_operation, + rcti *output) { - if (cachedInstance_ == nullptr) { - rcti newInput; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + if (cached_instance_ == nullptr) { + rcti new_input; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } return false; @@ -129,12 +129,12 @@ void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, Span inputs) { /* TODO(manzanilla): once tiled implementation is removed, run multi-threaded where possible. */ - if (!cachedInstance_) { + if (!cached_instance_) { MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; const bool is_image_inflated = image->is_a_single_elem(); image = is_image_inflated ? image->inflate() : image; - /* Must be a copy because it's modified in #generateVectorBlur. */ + /* Must be a copy because it's modified in #generate_vector_blur. */ MemoryBuffer *speed = inputs[SPEED_INPUT_INDEX]; speed = speed->is_a_single_elem() ? speed->inflate() : new MemoryBuffer(*speed); @@ -142,8 +142,8 @@ void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, const bool is_z_inflated = z->is_a_single_elem(); z = is_z_inflated ? z->inflate() : z; - cachedInstance_ = (float *)MEM_dupallocN(image->getBuffer()); - this->generateVectorBlur(cachedInstance_, image, speed, z); + cached_instance_ = (float *)MEM_dupallocN(image->get_buffer()); + this->generate_vector_blur(cached_instance_, image, speed, z); if (is_image_inflated) { delete image; @@ -154,29 +154,29 @@ void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, } } - const int num_channels = COM_data_type_num_channels(getOutputSocket()->getDataType()); - MemoryBuffer buf(cachedInstance_, num_channels, this->getWidth(), this->getHeight()); + const int num_channels = COM_data_type_num_channels(get_output_socket()->get_data_type()); + MemoryBuffer buf(cached_instance_, num_channels, this->get_width(), this->get_height()); output->copy_from(&buf, area); } -void VectorBlurOperation::generateVectorBlur(float *data, - MemoryBuffer *inputImage, - MemoryBuffer *inputSpeed, - MemoryBuffer *inputZ) +void VectorBlurOperation::generate_vector_blur(float *data, + MemoryBuffer *input_image, + MemoryBuffer *input_speed, + MemoryBuffer *inputZ) { NodeBlurData blurdata; - blurdata.samples = settings_->samples / QualityStepHelper::getStep(); + blurdata.samples = settings_->samples / QualityStepHelper::get_step(); blurdata.maxspeed = settings_->maxspeed; blurdata.minspeed = settings_->minspeed; blurdata.curved = settings_->curved; blurdata.fac = settings_->fac; zbuf_accumulate_vecblur(&blurdata, - this->getWidth(), - this->getHeight(), + this->get_width(), + this->get_height(), data, - inputImage->getBuffer(), - inputSpeed->getBuffer(), - inputZ->getBuffer()); + input_image->get_buffer(), + input_speed->get_buffer(), + inputZ->get_buffer()); } /* ****************** Spans ******************************* */ diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.h b/source/blender/compositor/operations/COM_VectorBlurOperation.h index cb384283c79..efcb5001fd4 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.h +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.h @@ -31,18 +31,18 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { static constexpr int SPEED_INPUT_INDEX = 2; /** - * \brief Cached reference to the inputProgram + * \brief Cached reference to the input_program */ - SocketReader *inputImageProgram_; - SocketReader *inputSpeedProgram_; - SocketReader *inputZProgram_; + SocketReader *input_image_program_; + SocketReader *input_speed_program_; + SocketReader *input_zprogram_; /** * \brief settings of the glare node. */ NodeBlurData *settings_; - float *cachedInstance_; + float *cached_instance_; public: VectorBlurOperation(); @@ -50,27 +50,27 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - void setVectorBlurSettings(NodeBlurData *settings) + void set_vector_blur_settings(NodeBlurData *settings) { settings_ = settings; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, @@ -80,10 +80,10 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { Span inputs) override; protected: - void generateVectorBlur(float *data, - MemoryBuffer *inputImage, - MemoryBuffer *inputSpeed, - MemoryBuffer *inputZ); + void generate_vector_blur(float *data, + MemoryBuffer *input_image, + MemoryBuffer *input_speed, + MemoryBuffer *inputZ); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cc b/source/blender/compositor/operations/COM_VectorCurveOperation.cc index ade0c5ee788..c7cd8dc5684 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cc @@ -24,40 +24,40 @@ namespace blender::compositor { VectorCurveOperation::VectorCurveOperation() { - this->addInputSocket(DataType::Vector); - this->addOutputSocket(DataType::Vector); + this->add_input_socket(DataType::Vector); + this->add_output_socket(DataType::Vector); - inputProgram_ = nullptr; + input_program_ = nullptr; } -void VectorCurveOperation::initExecution() +void VectorCurveOperation::init_execution() { - CurveBaseOperation::initExecution(); - inputProgram_ = this->getInputSocketReader(0); + CurveBaseOperation::init_execution(); + input_program_ = this->get_input_socket_reader(0); } -void VectorCurveOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void VectorCurveOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float input[4]; - inputProgram_->readSampled(input, x, y, sampler); + input_program_->read_sampled(input, x, y, sampler); - BKE_curvemapping_evaluate_premulRGBF(curveMapping_, output, input); + BKE_curvemapping_evaluate_premulRGBF(curve_mapping_, output, input); } -void VectorCurveOperation::deinitExecution() +void VectorCurveOperation::deinit_execution() { - CurveBaseOperation::deinitExecution(); - inputProgram_ = nullptr; + CurveBaseOperation::deinit_execution(); + input_program_ = nullptr; } void VectorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *curve_map = curveMapping_; + CurveMapping *curve_map = curve_mapping_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { BKE_curvemapping_evaluate_premulRGBF(curve_map, it.out, it.in(0)); } diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.h b/source/blender/compositor/operations/COM_VectorCurveOperation.h index 0f819628935..87864e08e25 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.h +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.h @@ -26,9 +26,9 @@ namespace blender::compositor { class VectorCurveOperation : public CurveBaseOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *inputProgram_; + SocketReader *input_program_; public: VectorCurveOperation(); @@ -36,17 +36,17 @@ class VectorCurveOperation : public CurveBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index 5360b118dee..440deea7796 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -31,54 +31,54 @@ static int MAX_VIEWER_TRANSLATION_PADDING = 12000; ViewerOperation::ViewerOperation() { - this->setImage(nullptr); - this->setImageUser(nullptr); - outputBuffer_ = nullptr; - depthBuffer_ = nullptr; + this->set_image(nullptr); + this->set_image_user(nullptr); + output_buffer_ = nullptr; + depth_buffer_ = nullptr; active_ = false; - doDepthBuffer_ = false; - viewSettings_ = nullptr; - displaySettings_ = nullptr; - useAlphaInput_ = false; + do_depth_buffer_ = false; + view_settings_ = nullptr; + display_settings_ = nullptr; + use_alpha_input_ = false; - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); - imageInput_ = nullptr; - alphaInput_ = nullptr; - depthInput_ = nullptr; + image_input_ = nullptr; + alpha_input_ = nullptr; + depth_input_ = nullptr; rd_ = nullptr; - viewName_ = nullptr; + view_name_ = nullptr; flags.use_viewer_border = true; flags.is_viewer_operation = true; } -void ViewerOperation::initExecution() +void ViewerOperation::init_execution() { /* When initializing the tree during initial load the width and height can be zero. */ - imageInput_ = getInputSocketReader(0); - alphaInput_ = getInputSocketReader(1); - depthInput_ = getInputSocketReader(2); - doDepthBuffer_ = (depthInput_ != nullptr); + image_input_ = get_input_socket_reader(0); + alpha_input_ = get_input_socket_reader(1); + depth_input_ = get_input_socket_reader(2); + do_depth_buffer_ = (depth_input_ != nullptr); - if (isActiveViewerOutput() && !exec_system_->is_breaked()) { - initImage(); + if (is_active_viewer_output() && !exec_system_->is_breaked()) { + init_image(); } } -void ViewerOperation::deinitExecution() +void ViewerOperation::deinit_execution() { - imageInput_ = nullptr; - alphaInput_ = nullptr; - depthInput_ = nullptr; - outputBuffer_ = nullptr; + image_input_ = nullptr; + alpha_input_ = nullptr; + depth_input_ = nullptr; + output_buffer_ = nullptr; } -void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void ViewerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { - float *buffer = outputBuffer_; - float *depthbuffer = depthBuffer_; + float *buffer = output_buffer_; + float *depthbuffer = depth_buffer_; if (!buffer) { return; } @@ -86,9 +86,9 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) const int y1 = rect->ymin; const int x2 = rect->xmax; const int y2 = rect->ymax; - const int offsetadd = (this->getWidth() - (x2 - x1)); + const int offsetadd = (this->get_width() - (x2 - x1)); const int offsetadd4 = offsetadd * 4; - int offset = (y1 * this->getWidth() + x1); + int offset = (y1 * this->get_width() + x1); int offset4 = offset * 4; float alpha[4], depth[4]; int x; @@ -97,54 +97,54 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) for (y = y1; y < y2 && (!breaked); y++) { for (x = x1; x < x2; x++) { - imageInput_->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); - if (useAlphaInput_) { - alphaInput_->readSampled(alpha, x, y, PixelSampler::Nearest); + image_input_->read_sampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); + if (use_alpha_input_) { + alpha_input_->read_sampled(alpha, x, y, PixelSampler::Nearest); buffer[offset4 + 3] = alpha[0]; } - depthInput_->readSampled(depth, x, y, PixelSampler::Nearest); + depth_input_->read_sampled(depth, x, y, PixelSampler::Nearest); depthbuffer[offset] = depth[0]; offset++; offset4 += 4; } - if (isBraked()) { + if (is_braked()) { breaked = true; } offset += offsetadd; offset4 += offsetadd4; } - updateImage(rect); + update_image(rect); } void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - const int sceneRenderWidth = rd_->xsch * rd_->size / 100; - const int sceneRenderHeight = rd_->ysch * rd_->size / 100; + const int scene_render_width = rd_->xsch * rd_->size / 100; + const int scene_render_height = rd_->ysch * rd_->size / 100; rcti local_preferred = preferred_area; - local_preferred.xmax = local_preferred.xmin + sceneRenderWidth; - local_preferred.ymax = local_preferred.ymin + sceneRenderHeight; + local_preferred.xmax = local_preferred.xmin + scene_render_width; + local_preferred.ymax = local_preferred.ymin + scene_render_height; NodeOperation::determine_canvas(local_preferred, r_area); } -void ViewerOperation::initImage() +void ViewerOperation::init_image() { Image *ima = image_; - ImageUser iuser = *imageUser_; + ImageUser iuser = *image_user_; void *lock; ImBuf *ibuf; /* make sure the image has the correct number of views */ - if (ima && BKE_scene_multiview_is_render_view_first(rd_, viewName_)) { - BKE_image_ensure_viewer_views(rd_, ima, imageUser_); + if (ima && BKE_scene_multiview_is_render_view_first(rd_, view_name_)) { + BKE_image_ensure_viewer_views(rd_, ima, image_user_); } BLI_thread_lock(LOCK_DRAW_IMAGE); /* local changes to the original ImageUser */ - iuser.multi_index = BKE_scene_multiview_view_id_get(rd_, viewName_); + iuser.multi_index = BKE_scene_multiview_view_id_get(rd_, view_name_); ibuf = BKE_image_acquire_ibuf(ima, &iuser, &lock); if (!ibuf) { @@ -161,8 +161,8 @@ void ViewerOperation::initImage() padding_y = MAX_VIEWER_TRANSLATION_PADDING; } - display_width_ = getWidth() + padding_x; - display_height_ = getHeight() + padding_y; + display_width_ = get_width() + padding_x; + display_height_ = get_height() + padding_y; if (ibuf->x != display_width_ || ibuf->y != display_height_) { imb_freerectImBuf(ibuf); imb_freerectfloatImBuf(ibuf); @@ -179,18 +179,18 @@ void ViewerOperation::initImage() ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; } - if (doDepthBuffer_) { + if (do_depth_buffer_) { addzbuffloatImBuf(ibuf); } /* now we combine the input with ibuf */ - outputBuffer_ = ibuf->rect_float; + output_buffer_ = ibuf->rect_float; /* needed for display buffer update */ ibuf_ = ibuf; - if (doDepthBuffer_) { - depthBuffer_ = ibuf->zbuf_float; + if (do_depth_buffer_) { + depth_buffer_ = ibuf->zbuf_float; } BKE_image_release_ibuf(image_, ibuf_, lock); @@ -198,32 +198,32 @@ void ViewerOperation::initImage() BLI_thread_unlock(LOCK_DRAW_IMAGE); } -void ViewerOperation::updateImage(const rcti *rect) +void ViewerOperation::update_image(const rcti *rect) { if (exec_system_->is_breaked()) { return; } - float *buffer = outputBuffer_; + float *buffer = output_buffer_; IMB_partial_display_buffer_update(ibuf_, buffer, nullptr, display_width_, 0, 0, - viewSettings_, - displaySettings_, + view_settings_, + display_settings_, rect->xmin, rect->ymin, rect->xmax, rect->ymax); image_->gpuflag |= IMA_GPU_REFRESH; - this->updateDraw(); + this->update_draw(); } -eCompositorPriority ViewerOperation::getRenderPriority() const +eCompositorPriority ViewerOperation::get_render_priority() const { - if (this->isActiveViewerOutput()) { + if (this->is_active_viewer_output()) { return eCompositorPriority::High; } @@ -234,25 +234,25 @@ void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), const rcti &area, Span inputs) { - if (!outputBuffer_) { + if (!output_buffer_) { return; } const int offset_x = area.xmin + (canvas_.xmin > 0 ? canvas_.xmin * 2 : 0); const int offset_y = area.ymin + (canvas_.ymin > 0 ? canvas_.ymin * 2 : 0); MemoryBuffer output_buffer( - outputBuffer_, COM_DATA_TYPE_COLOR_CHANNELS, display_width_, display_height_); + output_buffer_, COM_DATA_TYPE_COLOR_CHANNELS, display_width_, display_height_); const MemoryBuffer *input_image = inputs[0]; output_buffer.copy_from(input_image, area, offset_x, offset_y); - if (useAlphaInput_) { + if (use_alpha_input_) { const MemoryBuffer *input_alpha = inputs[1]; output_buffer.copy_from( input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, offset_x, offset_y, 3); } - if (depthBuffer_) { + if (depth_buffer_) { MemoryBuffer depth_buffer( - depthBuffer_, COM_DATA_TYPE_VALUE_CHANNELS, display_width_, display_height_); + depth_buffer_, COM_DATA_TYPE_VALUE_CHANNELS, display_width_, display_height_); const MemoryBuffer *input_depth = inputs[2]; depth_buffer.copy_from(input_depth, area, offset_x, offset_y); } @@ -263,27 +263,27 @@ void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), offset_x + BLI_rcti_size_x(&area), offset_y, offset_y + BLI_rcti_size_y(&area)); - updateImage(&display_area); + update_image(&display_area); } void ViewerOperation::clear_display_buffer() { - BLI_assert(isActiveViewerOutput()); + BLI_assert(is_active_viewer_output()); if (exec_system_->is_breaked()) { return; } - initImage(); - if (outputBuffer_ == nullptr) { + init_image(); + if (output_buffer_ == nullptr) { return; } size_t buf_bytes = (size_t)ibuf_->y * ibuf_->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float); if (buf_bytes > 0) { - memset(outputBuffer_, 0, buf_bytes); + memset(output_buffer_, 0, buf_bytes); rcti display_area; BLI_rcti_init(&display_area, 0, ibuf_->x, 0, ibuf_->y); - updateImage(&display_area); + update_image(&display_area); } } diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h index 9a812c8d87d..7fc5ae36ad9 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.h +++ b/source/blender/compositor/operations/COM_ViewerOperation.h @@ -28,105 +28,105 @@ namespace blender::compositor { class ViewerOperation : public MultiThreadedOperation { private: /* TODO(manzanilla): To be removed together with tiled implementation. */ - float *outputBuffer_; - float *depthBuffer_; + float *output_buffer_; + float *depth_buffer_; Image *image_; - ImageUser *imageUser_; + ImageUser *image_user_; bool active_; - float centerX_; - float centerY_; - ChunkOrdering chunkOrder_; - bool doDepthBuffer_; + float center_x_; + float center_y_; + ChunkOrdering chunk_order_; + bool do_depth_buffer_; ImBuf *ibuf_; - bool useAlphaInput_; + bool use_alpha_input_; const RenderData *rd_; - const char *viewName_; + const char *view_name_; - const ColorManagedViewSettings *viewSettings_; - const ColorManagedDisplaySettings *displaySettings_; + const ColorManagedViewSettings *view_settings_; + const ColorManagedDisplaySettings *display_settings_; - SocketReader *imageInput_; - SocketReader *alphaInput_; - SocketReader *depthInput_; + SocketReader *image_input_; + SocketReader *alpha_input_; + SocketReader *depth_input_; int display_width_; int display_height_; public: ViewerOperation(); - void initExecution() override; - void deinitExecution() override; - void executeRegion(rcti *rect, unsigned int tileNumber) override; + void init_execution() override; + void deinit_execution() override; + void execute_region(rcti *rect, unsigned int tile_number) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - bool isOutputOperation(bool /*rendering*/) const override + bool is_output_operation(bool /*rendering*/) const override { if (G.background) { return false; } - return isActiveViewerOutput(); + return is_active_viewer_output(); } - void setImage(Image *image) + void set_image(Image *image) { image_ = image; } - void setImageUser(ImageUser *imageUser) + void set_image_user(ImageUser *image_user) { - imageUser_ = imageUser; + image_user_ = image_user; } - bool isActiveViewerOutput() const override + bool is_active_viewer_output() const override { return active_; } - void setActive(bool active) + void set_active(bool active) { active_ = active; } void setCenterX(float centerX) { - centerX_ = centerX; + center_x_ = centerX; } void setCenterY(float centerY) { - centerY_ = centerY; + center_y_ = centerY; } - void setChunkOrder(ChunkOrdering tileOrder) + void set_chunk_order(ChunkOrdering tile_order) { - chunkOrder_ = tileOrder; + chunk_order_ = tile_order; } float getCenterX() const { - return centerX_; + return center_x_; } float getCenterY() const { - return centerY_; + return center_y_; } - ChunkOrdering getChunkOrder() const + ChunkOrdering get_chunk_order() const { - return chunkOrder_; + return chunk_order_; } - eCompositorPriority getRenderPriority() const override; - void setUseAlphaInput(bool value) + eCompositorPriority get_render_priority() const override; + void set_use_alpha_input(bool value) { - useAlphaInput_ = value; + use_alpha_input_ = value; } - void setRenderData(const RenderData *rd) + void set_render_data(const RenderData *rd) { rd_ = rd; } - void setViewName(const char *viewName) + void set_view_name(const char *view_name) { - viewName_ = viewName; + view_name_ = view_name; } - void setViewSettings(const ColorManagedViewSettings *viewSettings) + void set_view_settings(const ColorManagedViewSettings *view_settings) { - viewSettings_ = viewSettings; + view_settings_ = view_settings; } - void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) + void set_display_settings(const ColorManagedDisplaySettings *display_settings) { - displaySettings_ = displaySettings; + display_settings_ = display_settings; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -136,8 +136,8 @@ class ViewerOperation : public MultiThreadedOperation { void clear_display_buffer(); private: - void updateImage(const rcti *rect); - void initImage(); + void update_image(const rcti *rect); + void init_image(); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc index be4798c605f..336e1b72520 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.cc +++ b/source/blender/compositor/operations/COM_WrapOperation.cc @@ -24,98 +24,99 @@ namespace blender::compositor { WrapOperation::WrapOperation(DataType datatype) : ReadBufferOperation(datatype) { - wrappingType_ = CMP_NODE_WRAP_NONE; + wrapping_type_ = CMP_NODE_WRAP_NONE; } -inline float WrapOperation::getWrappedOriginalXPos(float x) +inline float WrapOperation::get_wrapped_original_xpos(float x) { - if (this->getWidth() == 0) { + if (this->get_width() == 0) { return 0; } while (x < 0) { - x += this->getWidth(); + x += this->get_width(); } - return fmodf(x, this->getWidth()); + return fmodf(x, this->get_width()); } -inline float WrapOperation::getWrappedOriginalYPos(float y) +inline float WrapOperation::get_wrapped_original_ypos(float y) { - if (this->getHeight() == 0) { + if (this->get_height() == 0) { return 0; } while (y < 0) { - y += this->getHeight(); + y += this->get_height(); } - return fmodf(y, this->getHeight()); + return fmodf(y, this->get_height()); } -void WrapOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void WrapOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { float nx, ny; nx = x; ny = y; MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, extend_y = MemoryBufferExtend::Clip; - switch (wrappingType_) { + switch (wrapping_type_) { case CMP_NODE_WRAP_NONE: - /* Intentionally empty, originalXPos and originalYPos have been set before. */ + /* Intentionally empty, original_xpos and original_ypos have been set before. */ break; case CMP_NODE_WRAP_X: /* Wrap only on the x-axis. */ - nx = this->getWrappedOriginalXPos(x); + nx = this->get_wrapped_original_xpos(x); extend_x = MemoryBufferExtend::Repeat; break; case CMP_NODE_WRAP_Y: /* Wrap only on the y-axis. */ - ny = this->getWrappedOriginalYPos(y); + ny = this->get_wrapped_original_ypos(y); extend_y = MemoryBufferExtend::Repeat; break; case CMP_NODE_WRAP_XY: /* Wrap on both. */ - nx = this->getWrappedOriginalXPos(x); - ny = this->getWrappedOriginalYPos(y); + nx = this->get_wrapped_original_xpos(x); + ny = this->get_wrapped_original_ypos(y); extend_x = MemoryBufferExtend::Repeat; extend_y = MemoryBufferExtend::Repeat; break; } - executePixelExtend(output, nx, ny, sampler, extend_x, extend_y); + execute_pixel_extend(output, nx, ny, sampler, extend_x, extend_y); } -bool WrapOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool WrapOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; - newInput.xmin = input->xmin; - newInput.xmax = input->xmax; - newInput.ymin = input->ymin; - newInput.ymax = input->ymax; + rcti new_input; + new_input.xmin = input->xmin; + new_input.xmax = input->xmax; + new_input.ymin = input->ymin; + new_input.ymax = input->ymax; - if (ELEM(wrappingType_, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY)) { + if (ELEM(wrapping_type_, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY)) { /* Wrap only on the x-axis if tile is wrapping. */ - newInput.xmin = getWrappedOriginalXPos(input->xmin); - newInput.xmax = roundf(getWrappedOriginalXPos(input->xmax)); - if (newInput.xmin >= newInput.xmax) { - newInput.xmin = 0; - newInput.xmax = this->getWidth(); + new_input.xmin = get_wrapped_original_xpos(input->xmin); + new_input.xmax = roundf(get_wrapped_original_xpos(input->xmax)); + if (new_input.xmin >= new_input.xmax) { + new_input.xmin = 0; + new_input.xmax = this->get_width(); } } - if (ELEM(wrappingType_, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY)) { + if (ELEM(wrapping_type_, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY)) { /* Wrap only on the y-axis if tile is wrapping. */ - newInput.ymin = getWrappedOriginalYPos(input->ymin); - newInput.ymax = roundf(getWrappedOriginalYPos(input->ymax)); - if (newInput.ymin >= newInput.ymax) { - newInput.ymin = 0; - newInput.ymax = this->getHeight(); + new_input.ymin = get_wrapped_original_ypos(input->ymin); + new_input.ymax = roundf(get_wrapped_original_ypos(input->ymax)); + if (new_input.ymin >= new_input.ymax) { + new_input.ymin = 0; + new_input.ymax = this->get_height(); } } - return ReadBufferOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return ReadBufferOperation::determine_depending_area_of_interest( + &new_input, read_operation, output); } -void WrapOperation::setWrapping(int wrapping_type) +void WrapOperation::set_wrapping(int wrapping_type) { - wrappingType_ = wrapping_type; + wrapping_type_ = wrapping_type; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_WrapOperation.h b/source/blender/compositor/operations/COM_WrapOperation.h index 15fc43cc65a..e8ec97fa5f2 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.h +++ b/source/blender/compositor/operations/COM_WrapOperation.h @@ -24,18 +24,18 @@ namespace blender::compositor { class WrapOperation : public ReadBufferOperation { private: - int wrappingType_; + int wrapping_type_; public: WrapOperation(DataType datatype); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void setWrapping(int wrapping_type); - float getWrappedOriginalXPos(float x); - float getWrappedOriginalYPos(float y); + void set_wrapping(int wrapping_type); + float get_wrapped_original_xpos(float x); + float get_wrapped_original_ypos(float y); void setFactorXY(float factorX, float factorY); }; diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cc b/source/blender/compositor/operations/COM_WriteBufferOperation.cc index 26c1d71c9da..1c728772fdd 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.cc +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cc @@ -23,47 +23,47 @@ namespace blender::compositor { WriteBufferOperation::WriteBufferOperation(DataType datatype) { - this->addInputSocket(datatype); - memoryProxy_ = new MemoryProxy(datatype); - memoryProxy_->setWriteBufferOperation(this); - memoryProxy_->setExecutor(nullptr); + this->add_input_socket(datatype); + memory_proxy_ = new MemoryProxy(datatype); + memory_proxy_->set_write_buffer_operation(this); + memory_proxy_->set_executor(nullptr); flags.is_write_buffer_operation = true; } WriteBufferOperation::~WriteBufferOperation() { - if (memoryProxy_) { - delete memoryProxy_; - memoryProxy_ = nullptr; + if (memory_proxy_) { + delete memory_proxy_; + memory_proxy_ = nullptr; } } -void WriteBufferOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void WriteBufferOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - input_->readSampled(output, x, y, sampler); + input_->read_sampled(output, x, y, sampler); } -void WriteBufferOperation::initExecution() +void WriteBufferOperation::init_execution() { - input_ = this->getInputOperation(0); - memoryProxy_->allocate(this->getWidth(), this->getHeight()); + input_ = this->get_input_operation(0); + memory_proxy_->allocate(this->get_width(), this->get_height()); } -void WriteBufferOperation::deinitExecution() +void WriteBufferOperation::deinit_execution() { input_ = nullptr; - memoryProxy_->free(); + memory_proxy_->free(); } -void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void WriteBufferOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { - MemoryBuffer *memoryBuffer = memoryProxy_->getBuffer(); - float *buffer = memoryBuffer->getBuffer(); - const uint8_t num_channels = memoryBuffer->get_num_channels(); + MemoryBuffer *memory_buffer = memory_proxy_->get_buffer(); + float *buffer = memory_buffer->get_buffer(); + const uint8_t num_channels = memory_buffer->get_num_channels(); if (input_->get_flags().complex) { - void *data = input_->initializeTileData(rect); + void *data = input_->initialize_tile_data(rect); int x1 = rect->xmin; int y1 = rect->ymin; int x2 = rect->xmax; @@ -72,17 +72,17 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ int y; bool breaked = false; for (y = y1; y < y2 && (!breaked); y++) { - int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels; + int offset4 = (y * memory_buffer->get_width() + x1) * num_channels; for (x = x1; x < x2; x++) { input_->read(&(buffer[offset4]), x, y, data); offset4 += num_channels; } - if (isBraked()) { + if (is_braked()) { breaked = true; } } if (data) { - input_->deinitializeTileData(rect, data); + input_->deinitialize_tile_data(rect, data); data = nullptr; } } @@ -96,25 +96,25 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ int y; bool breaked = false; for (y = y1; y < y2 && (!breaked); y++) { - int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels; + int offset4 = (y * memory_buffer->get_width() + x1) * num_channels; for (x = x1; x < x2; x++) { - input_->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); + input_->read_sampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); offset4 += num_channels; } - if (isBraked()) { + if (is_braked()) { breaked = true; } } } } -void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, - rcti * /*rect*/, - unsigned int /*chunkNumber*/, - MemoryBuffer **inputMemoryBuffers, - MemoryBuffer *outputBuffer) +void WriteBufferOperation::execute_opencl_region(OpenCLDevice *device, + rcti * /*rect*/, + unsigned int /*chunk_number*/, + MemoryBuffer **input_memory_buffers, + MemoryBuffer *output_buffer) { - float *outputFloatBuffer = outputBuffer->getBuffer(); + float *output_float_buffer = output_buffer->get_buffer(); cl_int error; /* * 1. create cl_mem from outputbuffer @@ -125,55 +125,55 @@ void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, * NOTE: list of cl_mem will be filled by 2, and needs to be cleaned up by 4 */ /* STEP 1 */ - const unsigned int outputBufferWidth = outputBuffer->getWidth(); - const unsigned int outputBufferHeight = outputBuffer->getHeight(); + const unsigned int output_buffer_width = output_buffer->get_width(); + const unsigned int output_buffer_height = output_buffer->get_height(); - const cl_image_format *imageFormat = OpenCLDevice::determineImageFormat(outputBuffer); + const cl_image_format *image_format = OpenCLDevice::determine_image_format(output_buffer); - cl_mem clOutputBuffer = clCreateImage2D(device->getContext(), - CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, - imageFormat, - outputBufferWidth, - outputBufferHeight, - 0, - outputFloatBuffer, - &error); + cl_mem cl_output_buffer = clCreateImage2D(device->get_context(), + CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, + image_format, + output_buffer_width, + output_buffer_height, + 0, + output_float_buffer, + &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } /* STEP 2 */ - std::list *clMemToCleanUp = new std::list(); - clMemToCleanUp->push_back(clOutputBuffer); - std::list *clKernelsToCleanUp = new std::list(); + std::list *cl_mem_to_clean_up = new std::list(); + cl_mem_to_clean_up->push_back(cl_output_buffer); + std::list *cl_kernels_to_clean_up = new std::list(); - input_->executeOpenCL(device, - outputBuffer, - clOutputBuffer, - inputMemoryBuffers, - clMemToCleanUp, - clKernelsToCleanUp); + input_->execute_opencl(device, + output_buffer, + cl_output_buffer, + input_memory_buffers, + cl_mem_to_clean_up, + cl_kernels_to_clean_up); /* STEP 3 */ size_t origin[3] = {0, 0, 0}; - size_t region[3] = {outputBufferWidth, outputBufferHeight, 1}; + size_t region[3] = {output_buffer_width, output_buffer_height, 1}; // clFlush(queue); // clFinish(queue); - error = clEnqueueBarrier(device->getQueue()); + error = clEnqueueBarrier(device->get_queue()); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - error = clEnqueueReadImage(device->getQueue(), - clOutputBuffer, + error = clEnqueueReadImage(device->get_queue(), + cl_output_buffer, CL_TRUE, origin, region, 0, 0, - outputFloatBuffer, + output_float_buffer, 0, nullptr, nullptr); @@ -181,27 +181,27 @@ void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - this->getMemoryProxy()->getBuffer()->fill_from(*outputBuffer); + this->get_memory_proxy()->get_buffer()->fill_from(*output_buffer); /* STEP 4 */ - while (!clMemToCleanUp->empty()) { - cl_mem mem = clMemToCleanUp->front(); + while (!cl_mem_to_clean_up->empty()) { + cl_mem mem = cl_mem_to_clean_up->front(); error = clReleaseMemObject(mem); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - clMemToCleanUp->pop_front(); + cl_mem_to_clean_up->pop_front(); } - while (!clKernelsToCleanUp->empty()) { - cl_kernel kernel = clKernelsToCleanUp->front(); + while (!cl_kernels_to_clean_up->empty()) { + cl_kernel kernel = cl_kernels_to_clean_up->front(); error = clReleaseKernel(kernel); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - clKernelsToCleanUp->pop_front(); + cl_kernels_to_clean_up->pop_front(); } - delete clKernelsToCleanUp; + delete cl_kernels_to_clean_up; } void WriteBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) @@ -219,11 +219,11 @@ void WriteBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_ } } -void WriteBufferOperation::readResolutionFromInputSocket() +void WriteBufferOperation::read_resolution_from_input_socket() { - NodeOperation *inputOperation = this->getInputOperation(0); - this->setWidth(inputOperation->getWidth()); - this->setHeight(inputOperation->getHeight()); + NodeOperation *input_operation = this->get_input_operation(0); + this->set_width(input_operation->get_width()); + this->set_height(input_operation->get_height()); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.h b/source/blender/compositor/operations/COM_WriteBufferOperation.h index bd8a2876a42..cf6b7f655ce 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.h +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.h @@ -31,34 +31,34 @@ class MemoryProxy; * \ingroup Operation */ class WriteBufferOperation : public NodeOperation { - MemoryProxy *memoryProxy_; + MemoryProxy *memory_proxy_; bool single_value_; /* single value stored in buffer */ NodeOperation *input_; public: WriteBufferOperation(DataType datatype); ~WriteBufferOperation(); - MemoryProxy *getMemoryProxy() + MemoryProxy *get_memory_proxy() { - return memoryProxy_; + return memory_proxy_; } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - bool isSingleValue() const + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + bool is_single_value() const { return single_value_; } - void executeRegion(rcti *rect, unsigned int tileNumber) override; - void initExecution() override; - void deinitExecution() override; - void executeOpenCLRegion(OpenCLDevice *device, - rcti *rect, - unsigned int chunkNumber, - MemoryBuffer **memoryBuffers, - MemoryBuffer *outputBuffer) override; + void execute_region(rcti *rect, unsigned int tile_number) override; + void init_execution() override; + void deinit_execution() override; + void execute_opencl_region(OpenCLDevice *device, + rcti *rect, + unsigned int chunk_number, + MemoryBuffer **memory_buffers, + MemoryBuffer *output_buffer) override; void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void readResolutionFromInputSocket(); - inline NodeOperation *getInput() + void read_resolution_from_input_socket(); + inline NodeOperation *get_input() { return input_; } diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc index 3a0126a12d4..9593e909d5a 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.cc +++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc @@ -22,11 +22,11 @@ namespace blender::compositor { ZCombineOperation::ZCombineOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); image1Reader_ = nullptr; depth1Reader_ = nullptr; @@ -35,29 +35,29 @@ ZCombineOperation::ZCombineOperation() this->flags.can_be_constant = true; } -void ZCombineOperation::initExecution() +void ZCombineOperation::init_execution() { - image1Reader_ = this->getInputSocketReader(0); - depth1Reader_ = this->getInputSocketReader(1); - image2Reader_ = this->getInputSocketReader(2); - depth2Reader_ = this->getInputSocketReader(3); + image1Reader_ = this->get_input_socket_reader(0); + depth1Reader_ = this->get_input_socket_reader(1); + image2Reader_ = this->get_input_socket_reader(2); + depth2Reader_ = this->get_input_socket_reader(3); } -void ZCombineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ZCombineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float depth1[4]; float depth2[4]; - depth1Reader_->readSampled(depth1, x, y, sampler); - depth2Reader_->readSampled(depth2, x, y, sampler); + depth1Reader_->read_sampled(depth1, x, y, sampler); + depth2Reader_->read_sampled(depth2, x, y, sampler); if (depth1[0] < depth2[0]) { - image1Reader_->readSampled(output, x, y, sampler); + image1Reader_->read_sampled(output, x, y, sampler); } else { - image2Reader_->readSampled(output, x, y, sampler); + image2Reader_->read_sampled(output, x, y, sampler); } } @@ -73,25 +73,25 @@ void ZCombineOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void ZCombineAlphaOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ZCombineAlphaOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float depth1[4]; float depth2[4]; float color1[4]; float color2[4]; - depth1Reader_->readSampled(depth1, x, y, sampler); - depth2Reader_->readSampled(depth2, x, y, sampler); + depth1Reader_->read_sampled(depth1, x, y, sampler); + depth2Reader_->read_sampled(depth2, x, y, sampler); if (depth1[0] <= depth2[0]) { - image1Reader_->readSampled(color1, x, y, sampler); - image2Reader_->readSampled(color2, x, y, sampler); + image1Reader_->read_sampled(color1, x, y, sampler); + image2Reader_->read_sampled(color2, x, y, sampler); } else { - image1Reader_->readSampled(color2, x, y, sampler); - image2Reader_->readSampled(color1, x, y, sampler); + image1Reader_->read_sampled(color2, x, y, sampler); + image2Reader_->read_sampled(color1, x, y, sampler); } float fac = color1[3]; float ifac = 1.0f - fac; @@ -127,7 +127,7 @@ void ZCombineAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void ZCombineOperation::deinitExecution() +void ZCombineOperation::deinit_execution() { image1Reader_ = nullptr; depth1Reader_ = nullptr; @@ -138,35 +138,35 @@ void ZCombineOperation::deinitExecution() // MASK combine ZCombineMaskOperation::ZCombineMaskOperation() { - this->addInputSocket(DataType::Value); // mask - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); // mask + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); - maskReader_ = nullptr; + mask_reader_ = nullptr; image1Reader_ = nullptr; image2Reader_ = nullptr; } -void ZCombineMaskOperation::initExecution() +void ZCombineMaskOperation::init_execution() { - maskReader_ = this->getInputSocketReader(0); - image1Reader_ = this->getInputSocketReader(1); - image2Reader_ = this->getInputSocketReader(2); + mask_reader_ = this->get_input_socket_reader(0); + image1Reader_ = this->get_input_socket_reader(1); + image2Reader_ = this->get_input_socket_reader(2); } -void ZCombineMaskOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ZCombineMaskOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float mask[4]; float color1[4]; float color2[4]; - maskReader_->readSampled(mask, x, y, sampler); - image1Reader_->readSampled(color1, x, y, sampler); - image2Reader_->readSampled(color2, x, y, sampler); + mask_reader_->read_sampled(mask, x, y, sampler); + image1Reader_->read_sampled(color1, x, y, sampler); + image2Reader_->read_sampled(color2, x, y, sampler); interp_v4_v4v4(output, color1, color2, 1.0f - mask[0]); } @@ -183,18 +183,18 @@ void ZCombineMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void ZCombineMaskAlphaOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ZCombineMaskAlphaOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float mask[4]; float color1[4]; float color2[4]; - maskReader_->readSampled(mask, x, y, sampler); - image1Reader_->readSampled(color1, x, y, sampler); - image2Reader_->readSampled(color2, x, y, sampler); + mask_reader_->read_sampled(mask, x, y, sampler); + image1Reader_->read_sampled(color1, x, y, sampler); + image2Reader_->read_sampled(color2, x, y, sampler); float fac = (1.0f - mask[0]) * (1.0f - color1[3]) + mask[0] * color2[3]; float mfac = 1.0f - fac; @@ -223,10 +223,10 @@ void ZCombineMaskAlphaOperation::update_memory_buffer_partial(MemoryBuffer *outp } } -void ZCombineMaskOperation::deinitExecution() +void ZCombineMaskOperation::deinit_execution() { image1Reader_ = nullptr; - maskReader_ = nullptr; + mask_reader_ = nullptr; image2Reader_ = nullptr; } diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.h b/source/blender/compositor/operations/COM_ZCombineOperation.h index 612107e3c77..ffc1bdd93b4 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.h +++ b/source/blender/compositor/operations/COM_ZCombineOperation.h @@ -39,13 +39,13 @@ class ZCombineOperation : public MultiThreadedOperation { */ ZCombineOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -53,7 +53,7 @@ class ZCombineOperation : public MultiThreadedOperation { }; class ZCombineAlphaOperation : public ZCombineOperation { - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -62,23 +62,23 @@ class ZCombineAlphaOperation : public ZCombineOperation { class ZCombineMaskOperation : public MultiThreadedOperation { protected: - SocketReader *maskReader_; + SocketReader *mask_reader_; SocketReader *image1Reader_; SocketReader *image2Reader_; public: ZCombineMaskOperation(); - void initExecution() override; - void deinitExecution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_execution() override; + void deinit_execution() override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; }; class ZCombineMaskAlphaOperation : public ZCombineMaskOperation { - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/tests/COM_NodeOperation_test.cc b/source/blender/compositor/tests/COM_NodeOperation_test.cc index 94e9fdeedb1..ea61df22f88 100644 --- a/source/blender/compositor/tests/COM_NodeOperation_test.cc +++ b/source/blender/compositor/tests/COM_NodeOperation_test.cc @@ -27,9 +27,9 @@ class NonHashedOperation : public NodeOperation { NonHashedOperation(int id) { set_id(id); - addOutputSocket(DataType::Value); - setWidth(2); - setHeight(3); + add_output_socket(DataType::Value); + set_width(2); + set_height(3); } }; @@ -40,9 +40,9 @@ class NonHashedConstantOperation : public ConstantOperation { NonHashedConstantOperation(int id) { set_id(id); - addOutputSocket(DataType::Value); - setWidth(2); - setHeight(3); + add_output_socket(DataType::Value); + set_width(2); + set_height(3); constant_ = 1.0f; } @@ -65,14 +65,14 @@ class HashedOperation : public NodeOperation { public: HashedOperation(NodeOperation &input, int width, int height) { - addInputSocket(DataType::Value); - addOutputSocket(DataType::Color); - setWidth(width); - setHeight(height); + add_input_socket(DataType::Value); + add_output_socket(DataType::Color); + set_width(width); + set_height(height); param1 = 2; param2 = 7.0f; - getInputSocket(0)->setLink(input.getOutputSocket()); + get_input_socket(0)->set_link(input.get_output_socket()); } void set_param1(int value) From f609b05b11cedb021fb05a89db124d979ab9de9b Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Wed, 13 Oct 2021 23:01:53 +0200 Subject: [PATCH 0769/1500] Cleanup: use `_` suffix for non-public data members in Compositor For code style and clarity. --- .../compositor/intern/COM_BufferOperation.cc | 4 ++-- .../intern/COM_MultiThreadedOperation.cc | 2 +- source/blender/compositor/intern/COM_Node.cc | 16 ++++++++-------- source/blender/compositor/intern/COM_Node.h | 8 ++++---- .../compositor/intern/COM_NodeOperation.cc | 4 ++-- .../compositor/intern/COM_NodeOperation.h | 10 +++++----- .../intern/COM_SingleThreadedOperation.cc | 4 ++-- .../compositor/nodes/COM_CryptomatteNode.cc | 2 +- source/blender/compositor/nodes/COM_ImageNode.cc | 4 ++-- .../compositor/nodes/COM_OutputFileNode.cc | 6 +++--- .../compositor/nodes/COM_RenderLayersNode.cc | 2 +- .../operations/COM_AlphaOverKeyOperation.cc | 2 +- .../operations/COM_AlphaOverMixedOperation.cc | 2 +- .../COM_AlphaOverPremultiplyOperation.cc | 2 +- .../operations/COM_AntiAliasOperation.cc | 2 +- .../operations/COM_BilateralBlurOperation.cc | 2 +- .../operations/COM_BlurBaseOperation.cc | 2 +- .../operations/COM_BokehBlurOperation.cc | 4 ++-- .../operations/COM_BrightnessOperation.cc | 2 +- .../operations/COM_CalculateMeanOperation.cc | 2 +- .../operations/COM_ChangeHSVOperation.cc | 2 +- .../operations/COM_ChannelMatteOperation.cc | 2 +- .../operations/COM_ChromaMatteOperation.cc | 2 +- .../COM_ColorBalanceASCCDLOperation.cc | 2 +- .../operations/COM_ColorBalanceLGGOperation.cc | 2 +- .../operations/COM_ColorCorrectionOperation.cc | 2 +- .../operations/COM_ColorExposureOperation.cc | 2 +- .../operations/COM_ColorMatteOperation.cc | 2 +- .../operations/COM_ColorRampOperation.cc | 2 +- .../operations/COM_ColorSpillOperation.cc | 2 +- .../operations/COM_CompositorOperation.cc | 2 +- .../operations/COM_ConstantOperation.cc | 6 +++--- .../operations/COM_ConvertOperation.cc | 2 +- .../operations/COM_ConvolutionFilterOperation.cc | 2 +- .../operations/COM_CryptomatteOperation.cc | 2 +- .../operations/COM_CurveBaseOperation.cc | 2 +- .../operations/COM_DenoiseOperation.cc | 2 +- .../operations/COM_DespeckleOperation.cc | 2 +- .../operations/COM_DifferenceMatteOperation.cc | 2 +- .../operations/COM_DilateErodeOperation.cc | 8 ++++---- .../operations/COM_DirectionalBlurOperation.cc | 4 ++-- .../operations/COM_DisplaceOperation.cc | 2 +- .../operations/COM_DistanceRGBMatteOperation.cc | 2 +- .../operations/COM_DotproductOperation.cc | 2 +- .../operations/COM_DoubleEdgeMaskOperation.cc | 2 +- .../operations/COM_FastGaussianBlurOperation.cc | 2 +- .../operations/COM_GammaCorrectOperation.cc | 4 ++-- .../compositor/operations/COM_GammaOperation.cc | 2 +- .../operations/COM_GaussianXBlurOperation.h | 2 +- .../operations/COM_GaussianYBlurOperation.h | 2 +- .../operations/COM_GlareBaseOperation.cc | 2 +- .../compositor/operations/COM_IDMaskOperation.cc | 4 ++-- .../operations/COM_InpaintOperation.cc | 4 ++-- .../compositor/operations/COM_InvertOperation.cc | 2 +- .../operations/COM_KeyingBlurOperation.cc | 2 +- .../operations/COM_KeyingClipOperation.cc | 2 +- .../operations/COM_KeyingDespillOperation.cc | 2 +- .../operations/COM_KeyingScreenOperation.cc | 2 +- .../operations/COM_LuminanceMatteOperation.cc | 2 +- .../operations/COM_MapRangeOperation.cc | 2 +- .../compositor/operations/COM_MapUVOperation.cc | 2 +- .../operations/COM_MapValueOperation.cc | 2 +- .../operations/COM_MathBaseOperation.cc | 2 +- .../compositor/operations/COM_MixOperation.cc | 2 +- .../operations/COM_NormalizeOperation.cc | 4 ++-- .../operations/COM_PlaneCornerPinOperation.cc | 2 +- .../COM_PlaneDistortCommonOperation.cc | 2 +- .../operations/COM_PosterizeOperation.cc | 2 +- .../operations/COM_PreviewOperation.cc | 4 ++-- .../COM_ProjectorLensDistortionOperation.cc | 2 +- .../operations/COM_ReadBufferOperation.cc | 2 +- .../compositor/operations/COM_SMAAOperation.cc | 6 +++--- .../COM_ScreenLensDistortionOperation.cc | 2 +- .../operations/COM_SetAlphaMultiplyOperation.cc | 2 +- .../operations/COM_SetAlphaReplaceOperation.cc | 2 +- .../operations/COM_SetColorOperation.cc | 2 +- .../operations/COM_SetValueOperation.cc | 2 +- .../operations/COM_SetVectorOperation.cc | 2 +- .../operations/COM_SocketProxyOperation.cc | 4 ++-- .../operations/COM_SunBeamsOperation.cc | 2 +- .../operations/COM_TextureOperation.cc | 2 +- .../operations/COM_TonemapOperation.cc | 2 +- .../operations/COM_TrackPositionOperation.cc | 2 +- .../COM_VariableSizeBokehBlurOperation.cc | 4 ++-- .../operations/COM_VectorBlurOperation.cc | 4 ++-- .../compositor/operations/COM_ViewerOperation.cc | 4 ++-- .../operations/COM_WriteBufferOperation.cc | 2 +- .../operations/COM_ZCombineOperation.cc | 2 +- 88 files changed, 126 insertions(+), 126 deletions(-) diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc index 21238733925..81ea645e482 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.cc +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -26,8 +26,8 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) inflated_buffer_ = nullptr; set_canvas(buffer->get_rect()); add_output_socket(data_type); - flags.is_constant_operation = buffer_->is_a_single_elem(); - flags.is_fullframe_operation = false; + flags_.is_constant_operation = buffer_->is_a_single_elem(); + flags_.is_fullframe_operation = false; } const float *BufferOperation::get_constant_elem() diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc index 7ccf6f76d9f..d118027202a 100644 --- a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc @@ -7,7 +7,7 @@ MultiThreadedOperation::MultiThreadedOperation() { num_passes_ = 1; current_pass_ = 0; - flags.is_fullframe_operation = true; + flags_.is_fullframe_operation = true; } void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output, diff --git a/source/blender/compositor/intern/COM_Node.cc b/source/blender/compositor/intern/COM_Node.cc index 58b2b195579..67af9496ca4 100644 --- a/source/blender/compositor/intern/COM_Node.cc +++ b/source/blender/compositor/intern/COM_Node.cc @@ -66,11 +66,11 @@ Node::Node(bNode *editor_node, bool create_sockets) Node::~Node() { - while (!this->outputs.is_empty()) { - delete (this->outputs.pop_last()); + while (!outputs_.is_empty()) { + delete (outputs_.pop_last()); } - while (!this->inputs.is_empty()) { - delete (this->inputs.pop_last()); + while (!inputs_.is_empty()) { + delete (inputs_.pop_last()); } } @@ -82,7 +82,7 @@ void Node::add_input_socket(DataType datatype) void Node::add_input_socket(DataType datatype, bNodeSocket *bSocket) { NodeInput *socket = new NodeInput(this, bSocket, datatype); - this->inputs.append(socket); + inputs_.append(socket); } void Node::add_output_socket(DataType datatype) @@ -92,17 +92,17 @@ void Node::add_output_socket(DataType datatype) void Node::add_output_socket(DataType datatype, bNodeSocket *bSocket) { NodeOutput *socket = new NodeOutput(this, bSocket, datatype); - outputs.append(socket); + outputs_.append(socket); } NodeOutput *Node::get_output_socket(unsigned int index) const { - return outputs[index]; + return outputs_[index]; } NodeInput *Node::get_input_socket(unsigned int index) const { - return inputs[index]; + return inputs_[index]; } bNodeSocket *Node::get_editor_input_socket(int editor_node_input_socket_index) diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h index 41283b821bb..bd8b37276a0 100644 --- a/source/blender/compositor/intern/COM_Node.h +++ b/source/blender/compositor/intern/COM_Node.h @@ -62,12 +62,12 @@ class Node { /** * \brief the list of actual input-sockets \see NodeInput */ - Vector inputs; + Vector inputs_; /** * \brief the list of actual output-sockets \see NodeOutput */ - Vector outputs; + Vector outputs_; public: Node(bNode *editor_node, bool create_sockets = true); @@ -114,7 +114,7 @@ class Node { */ const Vector &get_input_sockets() const { - return this->inputs; + return inputs_; } /** @@ -122,7 +122,7 @@ class Node { */ const Vector &get_output_sockets() const { - return this->outputs; + return outputs_; } /** diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 41c645b9eea..8a7ae1f4fcb 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -205,7 +205,7 @@ void NodeOperation::deinit_execution() void NodeOperation::set_canvas(const rcti &canvas_area) { canvas_ = canvas_area; - flags.is_canvas_set = true; + flags_.is_canvas_set = true; } const rcti &NodeOperation::get_canvas() const @@ -220,7 +220,7 @@ const rcti &NodeOperation::get_canvas() const void NodeOperation::unset_canvas() { BLI_assert(inputs_.size() == 0); - flags.is_canvas_set = false; + flags_.is_canvas_set = false; } SocketReader *NodeOperation::get_input_socket_reader(unsigned int index) diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index 3c172faca50..627fffb1ec7 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -356,7 +356,7 @@ class NodeOperation { /** * Flags how to evaluate this operation. */ - NodeOperationFlags flags; + NodeOperationFlags flags_; ExecutionSystem *exec_system_; @@ -390,7 +390,7 @@ class NodeOperation { const NodeOperationFlags get_flags() const { - return flags; + return flags_; } std::optional generate_hash(); @@ -674,12 +674,12 @@ class NodeOperation { void set_width(unsigned int width) { canvas_.xmax = canvas_.xmin + width; - this->flags.is_canvas_set = true; + flags_.is_canvas_set = true; } void set_height(unsigned int height) { canvas_.ymax = canvas_.ymin + height; - this->flags.is_canvas_set = true; + flags_.is_canvas_set = true; } SocketReader *get_input_socket_reader(unsigned int index); @@ -697,7 +697,7 @@ class NodeOperation { */ void set_complex(bool complex) { - this->flags.complex = complex; + flags_.complex = complex; } /** diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc index fab6c352a02..5ad324459fd 100644 --- a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc +++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc @@ -23,8 +23,8 @@ namespace blender::compositor { SingleThreadedOperation::SingleThreadedOperation() { cached_instance_ = nullptr; - flags.complex = true; - flags.single_threaded = true; + flags_.complex = true; + flags_.single_threaded = true; } void SingleThreadedOperation::init_execution() diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index f54088627e1..605dc1dc84d 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -255,7 +255,7 @@ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation( const bNode &UNUSED(node), const NodeCryptomatte *cryptomatte_settings) const { - const int num_inputs = inputs.size() - 1; + const int num_inputs = inputs_.size() - 1; CryptomatteOperation *operation = new CryptomatteOperation(num_inputs); if (cryptomatte_settings) { LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) { diff --git a/source/blender/compositor/nodes/COM_ImageNode.cc b/source/blender/compositor/nodes/COM_ImageNode.cc index 928a7c735c7..cc1c4109d9e 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cc +++ b/source/blender/compositor/nodes/COM_ImageNode.cc @@ -85,8 +85,8 @@ void ImageNode::convert_to_operations(NodeConverter &converter, if (rl) { is_multilayer_ok = true; - for (int64_t index = 0; index < outputs.size(); index++) { - NodeOutput *socket = outputs[index]; + for (int64_t index = 0; index < outputs_.size(); index++) { + NodeOutput *socket = outputs_[index]; NodeOperation *operation = nullptr; bNodeSocket *bnode_socket = socket->get_bnode_socket(); NodeImageLayer *storage = (NodeImageLayer *)bnode_socket->storage; diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc index 25d201fc9c4..1a9908d4a93 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cc +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc @@ -27,7 +27,7 @@ OutputFileNode::OutputFileNode(bNode *editor_node) : Node(editor_node) void OutputFileNode::add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const { - for (NodeInput *input : inputs) { + for (NodeInput *input : inputs_) { NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)input->get_bnode_socket()->storage; /* NOTE: layer becomes an empty placeholder if the input is not linked. */ @@ -40,7 +40,7 @@ void OutputFileNode::map_input_sockets(NodeConverter &converter, { bool preview_added = false; int index = 0; - for (NodeInput *input : inputs) { + for (NodeInput *input : inputs_) { converter.map_input_socket(input, operation.get_input_socket(index++)); if (!preview_added) { @@ -97,7 +97,7 @@ void OutputFileNode::convert_to_operations(NodeConverter &converter, } else { /* single layer format */ bool preview_added = false; - for (NodeInput *input : inputs) { + for (NodeInput *input : inputs_) { if (input->is_linked()) { NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)input->get_bnode_socket()->storage; diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cc b/source/blender/compositor/nodes/COM_RenderLayersNode.cc index 92033c3fbe8..4b82c5cbc21 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.cc +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cc @@ -150,7 +150,7 @@ void RenderLayersNode::missing_socket_link(NodeConverter &converter, NodeOutput void RenderLayersNode::missing_render_link(NodeConverter &converter) const { - for (NodeOutput *output : outputs) { + for (NodeOutput *output : outputs_) { missing_socket_link(converter, output); } } diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc index 3c5053d8357..5b8d376cf79 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc @@ -22,7 +22,7 @@ namespace blender::compositor { AlphaOverKeyOperation::AlphaOverKeyOperation() { - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void AlphaOverKeyOperation::execute_pixel_sampled(float output[4], diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc index 102d031d9be..42efca996ab 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc @@ -23,7 +23,7 @@ namespace blender::compositor { AlphaOverMixedOperation::AlphaOverMixedOperation() { x_ = 0.0f; - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void AlphaOverMixedOperation::execute_pixel_sampled(float output[4], diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc index 2d04c22533b..a627705fab5 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc @@ -22,7 +22,7 @@ namespace blender::compositor { AlphaOverPremultiplyOperation::AlphaOverPremultiplyOperation() { - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void AlphaOverPremultiplyOperation::execute_pixel_sampled(float output[4], diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.cc b/source/blender/compositor/operations/COM_AntiAliasOperation.cc index 28fea7b406c..2695e47aa60 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.cc +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.cc @@ -113,7 +113,7 @@ AntiAliasOperation::AntiAliasOperation() this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Value); value_reader_ = nullptr; - this->flags.complex = true; + flags_.complex = true; } void AntiAliasOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc index 5f7d2432f05..32780ad4ea8 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc @@ -25,7 +25,7 @@ BilateralBlurOperation::BilateralBlurOperation() this->add_input_socket(DataType::Color); this->add_input_socket(DataType::Color); this->add_output_socket(DataType::Color); - this->flags.complex = true; + flags_.complex = true; input_color_program_ = nullptr; input_determinator_program_ = nullptr; diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc index 15e2ce2d1e0..aefe74a7e26 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc @@ -29,7 +29,7 @@ BlurBaseOperation::BlurBaseOperation(DataType data_type) this->add_input_socket(data_type); this->add_input_socket(DataType::Value); this->add_output_socket(data_type); - this->flags.complex = true; + flags_.complex = true; input_program_ = nullptr; memset(&data_, 0, sizeof(NodeBlurData)); size_ = 1.0f; diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc index 80e7390e02f..3c8dd94c094 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc @@ -36,8 +36,8 @@ BokehBlurOperation::BokehBlurOperation() this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Color); - flags.complex = true; - flags.open_cl = true; + flags_.complex = true; + flags_.open_cl = true; size_ = 1.0f; sizeavailable_ = false; diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc index a445bb122c2..cebcc14b13a 100644 --- a/source/blender/compositor/operations/COM_BrightnessOperation.cc +++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc @@ -28,7 +28,7 @@ BrightnessOperation::BrightnessOperation() this->add_output_socket(DataType::Color); input_program_ = nullptr; use_premultiply_ = false; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void BrightnessOperation::set_use_premultiply(bool use_premultiply) diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc index f759ba5ccef..b8d85675041 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc @@ -31,7 +31,7 @@ CalculateMeanOperation::CalculateMeanOperation() image_reader_ = nullptr; iscalculated_ = false; setting_ = 1; - this->flags.complex = true; + flags_.complex = true; } void CalculateMeanOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc index b4effc087b1..7c64b1954d4 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc @@ -28,7 +28,7 @@ ChangeHSVOperation::ChangeHSVOperation() this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Color); input_operation_ = nullptr; - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void ChangeHSVOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc index 0a6b8255c42..af20193118c 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc @@ -26,7 +26,7 @@ ChannelMatteOperation::ChannelMatteOperation() add_output_socket(DataType::Value); input_image_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void ChannelMatteOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc index fceb0ea1c23..d0fc65ef331 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc @@ -28,7 +28,7 @@ ChromaMatteOperation::ChromaMatteOperation() input_image_program_ = nullptr; input_key_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void ChromaMatteOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc index e12138c1f00..533854b62dd 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc @@ -40,7 +40,7 @@ ColorBalanceASCCDLOperation::ColorBalanceASCCDLOperation() input_value_operation_ = nullptr; input_color_operation_ = nullptr; this->set_canvas_input_index(1); - flags.can_be_constant = true; + flags_.can_be_constant = true; } void ColorBalanceASCCDLOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc index d9af1b0a074..606fd560cff 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc @@ -45,7 +45,7 @@ ColorBalanceLGGOperation::ColorBalanceLGGOperation() input_value_operation_ = nullptr; input_color_operation_ = nullptr; this->set_canvas_input_index(1); - flags.can_be_constant = true; + flags_.can_be_constant = true; } void ColorBalanceLGGOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc index 538227d899b..2e7ce6b8497 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc @@ -32,7 +32,7 @@ ColorCorrectionOperation::ColorCorrectionOperation() red_channel_enabled_ = true; green_channel_enabled_ = true; blue_channel_enabled_ = true; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void ColorCorrectionOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.cc b/source/blender/compositor/operations/COM_ColorExposureOperation.cc index 9a785962bd5..890a0ab4ccb 100644 --- a/source/blender/compositor/operations/COM_ColorExposureOperation.cc +++ b/source/blender/compositor/operations/COM_ColorExposureOperation.cc @@ -26,7 +26,7 @@ ExposureOperation::ExposureOperation() this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Color); input_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void ExposureOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc index c4bf1bdf1cf..14123d99b0f 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc @@ -28,7 +28,7 @@ ColorMatteOperation::ColorMatteOperation() input_image_program_ = nullptr; input_key_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void ColorMatteOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.cc b/source/blender/compositor/operations/COM_ColorRampOperation.cc index bd86528d143..94b840a6f73 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.cc +++ b/source/blender/compositor/operations/COM_ColorRampOperation.cc @@ -29,7 +29,7 @@ ColorRampOperation::ColorRampOperation() input_program_ = nullptr; color_band_ = nullptr; - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void ColorRampOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc index 39be05ece38..d614c963488 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc @@ -31,7 +31,7 @@ ColorSpillOperation::ColorSpillOperation() input_fac_reader_ = nullptr; spill_channel_ = 1; /* GREEN */ spill_method_ = 0; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void ColorSpillOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index e27fc971a87..354997ebd2e 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -45,7 +45,7 @@ CompositorOperation::CompositorOperation() scene_name_[0] = '\0'; view_name_ = nullptr; - flags.use_render_border = true; + flags_.use_render_border = true; } void CompositorOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_ConstantOperation.cc b/source/blender/compositor/operations/COM_ConstantOperation.cc index c127860a89c..400c04d9592 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.cc +++ b/source/blender/compositor/operations/COM_ConstantOperation.cc @@ -23,13 +23,13 @@ namespace blender::compositor { ConstantOperation::ConstantOperation() { needs_canvas_to_get_constant_ = false; - flags.is_constant_operation = true; - flags.is_fullframe_operation = true; + flags_.is_constant_operation = true; + flags_.is_fullframe_operation = true; } bool ConstantOperation::can_get_constant_elem() const { - return !needs_canvas_to_get_constant_ || this->flags.is_canvas_set; + return !needs_canvas_to_get_constant_ || flags_.is_canvas_set; } void ConstantOperation::update_memory_buffer(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 7f1018ca71e..ec321494c6f 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -27,7 +27,7 @@ namespace blender::compositor { ConvertBaseOperation::ConvertBaseOperation() { input_operation_ = nullptr; - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void ConvertBaseOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc index 6ee5a1c86f9..2e9990348c1 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc @@ -27,7 +27,7 @@ ConvolutionFilterOperation::ConvolutionFilterOperation() this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); input_operation_ = nullptr; - this->flags.complex = true; + flags_.complex = true; } void ConvolutionFilterOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc index 078ec9b6dfe..8a246269691 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc @@ -27,7 +27,7 @@ CryptomatteOperation::CryptomatteOperation(size_t num_inputs) this->add_input_socket(DataType::Color); } this->add_output_socket(DataType::Color); - this->flags.complex = true; + flags_.complex = true; } void CryptomatteOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc index c65a1a45750..a014fb17750 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc @@ -25,7 +25,7 @@ namespace blender::compositor { CurveBaseOperation::CurveBaseOperation() { curve_mapping_ = nullptr; - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } CurveBaseOperation::~CurveBaseOperation() diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index dd8e6df69fc..53417112974 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -127,7 +127,7 @@ class DenoiseFilter { DenoiseBaseOperation::DenoiseBaseOperation() { - flags.is_fullframe_operation = true; + flags_.is_fullframe_operation = true; output_rendered_ = false; } diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc index be0dc06b8ec..c60a4167dfc 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.cc +++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc @@ -29,7 +29,7 @@ DespeckleOperation::DespeckleOperation() this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); input_operation_ = nullptr; - this->flags.complex = true; + flags_.complex = true; } void DespeckleOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc index 8391bbb799a..251dc4cc161 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc @@ -28,7 +28,7 @@ DifferenceMatteOperation::DifferenceMatteOperation() input_image1_program_ = nullptr; input_image2_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void DifferenceMatteOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index 7fc186df35f..2abc4f71b5e 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -26,7 +26,7 @@ DilateErodeThresholdOperation::DilateErodeThresholdOperation() { this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Value); - this->flags.complex = true; + flags_.complex = true; input_program_ = nullptr; inset_ = 0.0f; switch_ = 0.5f; @@ -275,8 +275,8 @@ DilateDistanceOperation::DilateDistanceOperation() this->add_output_socket(DataType::Value); input_program_ = nullptr; distance_ = 0.0f; - flags.complex = true; - flags.open_cl = true; + flags_.complex = true; + flags_.open_cl = true; } void DilateDistanceOperation::init_data() @@ -536,7 +536,7 @@ DilateStepOperation::DilateStepOperation() { this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Value); - this->flags.complex = true; + flags_.complex = true; input_program_ = nullptr; } void DilateStepOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc index eff7fa84475..1104ef6f71f 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc @@ -25,8 +25,8 @@ DirectionalBlurOperation::DirectionalBlurOperation() { this->add_input_socket(DataType::Color); this->add_output_socket(DataType::Color); - flags.complex = true; - flags.open_cl = true; + flags_.complex = true; + flags_.open_cl = true; input_program_ = nullptr; } diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc index 0c5b8dea2e6..4742692b75d 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc @@ -27,7 +27,7 @@ DisplaceOperation::DisplaceOperation() this->add_input_socket(DataType::Value); this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Color); - this->flags.complex = true; + flags_.complex = true; input_color_program_ = nullptr; } diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc index 95593d7d49f..7618e318c77 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc @@ -28,7 +28,7 @@ DistanceRGBMatteOperation::DistanceRGBMatteOperation() input_image_program_ = nullptr; input_key_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void DistanceRGBMatteOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc index 9507f991b52..ce1432547ae 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.cc +++ b/source/blender/compositor/operations/COM_DotproductOperation.cc @@ -28,7 +28,7 @@ DotproductOperation::DotproductOperation() this->set_canvas_input_index(0); input1Operation_ = nullptr; input2Operation_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void DotproductOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc index 559df4abb0c..22899bd396a 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc @@ -1326,7 +1326,7 @@ DoubleEdgeMaskOperation::DoubleEdgeMaskOperation() input_outer_mask_ = nullptr; adjacent_only_ = false; keep_inside_ = false; - this->flags.complex = true; + flags_.complex = true; is_output_rendered_ = false; } diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index e39db9e06eb..850fad1d96a 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -326,7 +326,7 @@ FastGaussianBlurValueOperation::FastGaussianBlurValueOperation() inputprogram_ = nullptr; sigma_ = 1.0f; overlay_ = 0; - flags.complex = true; + flags_.complex = true; } void FastGaussianBlurValueOperation::execute_pixel(float output[4], int x, int y, void *data) diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc index 0271e57b2c4..57d2ebd1b84 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc @@ -25,7 +25,7 @@ GammaCorrectOperation::GammaCorrectOperation() this->add_input_socket(DataType::Color); this->add_output_socket(DataType::Color); input_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void GammaCorrectOperation::init_execution() { @@ -96,7 +96,7 @@ GammaUncorrectOperation::GammaUncorrectOperation() this->add_input_socket(DataType::Color); this->add_output_socket(DataType::Color); input_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void GammaUncorrectOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_GammaOperation.cc b/source/blender/compositor/operations/COM_GammaOperation.cc index fb8f18bcc28..3ea7f35f1b5 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.cc +++ b/source/blender/compositor/operations/COM_GammaOperation.cc @@ -27,7 +27,7 @@ GammaOperation::GammaOperation() this->add_output_socket(DataType::Color); input_program_ = nullptr; input_gamma_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void GammaOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h index b9bbe1df900..72356381da3 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h @@ -59,7 +59,7 @@ class GaussianXBlurOperation : public GaussianBlurBaseOperation { void check_opencl() { - flags.open_cl = (data_.sizex >= 128); + flags_.open_cl = (data_.sizex >= 128); } }; diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h index 4390c9544e4..817e5a137c2 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h @@ -59,7 +59,7 @@ class GaussianYBlurOperation : public GaussianBlurBaseOperation { void check_opencl() { - flags.open_cl = (data_.sizex >= 128); + flags_.open_cl = (data_.sizex >= 128); } }; diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cc b/source/blender/compositor/operations/COM_GlareBaseOperation.cc index 37082f29579..766b20326db 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cc @@ -25,7 +25,7 @@ GlareBaseOperation::GlareBaseOperation() this->add_input_socket(DataType::Color); this->add_output_socket(DataType::Color); settings_ = nullptr; - flags.is_fullframe_operation = true; + flags_.is_fullframe_operation = true; is_output_rendered_ = false; } void GlareBaseOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.cc b/source/blender/compositor/operations/COM_IDMaskOperation.cc index 5837cb1fda9..503cbf5eb31 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.cc +++ b/source/blender/compositor/operations/COM_IDMaskOperation.cc @@ -24,8 +24,8 @@ IDMaskOperation::IDMaskOperation() { this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Value); - this->flags.complex = true; - flags.can_be_constant = true; + flags_.complex = true; + flags_.can_be_constant = true; } void *IDMaskOperation::initialize_tile_data(rcti *rect) diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc index 96cd0043470..2d632bb3681 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cc +++ b/source/blender/compositor/operations/COM_InpaintOperation.cc @@ -30,13 +30,13 @@ InpaintSimpleOperation::InpaintSimpleOperation() { this->add_input_socket(DataType::Color); this->add_output_socket(DataType::Color); - this->flags.complex = true; + flags_.complex = true; input_image_program_ = nullptr; pixelorder_ = nullptr; manhattan_distance_ = nullptr; cached_buffer_ = nullptr; cached_buffer_ready_ = false; - flags.is_fullframe_operation = true; + flags_.is_fullframe_operation = true; } void InpaintSimpleOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc index cbc15c95f46..3722282bd73 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.cc +++ b/source/blender/compositor/operations/COM_InvertOperation.cc @@ -30,7 +30,7 @@ InvertOperation::InvertOperation() color_ = true; alpha_ = false; set_canvas_input_index(1); - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void InvertOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc index bb82e74f92a..7fbe96d382b 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc @@ -28,7 +28,7 @@ KeyingBlurOperation::KeyingBlurOperation() size_ = 0; axis_ = BLUR_AXIS_X; - this->flags.complex = true; + flags_.complex = true; } void *KeyingBlurOperation::initialize_tile_data(rcti *rect) diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc index 03e3f0611c0..d52b2801811 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc @@ -33,7 +33,7 @@ KeyingClipOperation::KeyingClipOperation() is_edge_matte_ = false; - this->flags.complex = true; + flags_.complex = true; } void *KeyingClipOperation::initialize_tile_data(rcti *rect) diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc index f819a39e9e0..d200d1e5eb3 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc @@ -31,7 +31,7 @@ KeyingDespillOperation::KeyingDespillOperation() pixel_reader_ = nullptr; screen_reader_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void KeyingDespillOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc index d7e56aaa7c6..c34eaad87ef 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc @@ -32,7 +32,7 @@ KeyingScreenOperation::KeyingScreenOperation() movie_clip_ = nullptr; framenumber_ = 0; tracking_object_[0] = 0; - flags.complex = true; + flags_.complex = true; cached_triangulation_ = nullptr; } diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc index 36dc9c42b8b..f2543d0d159 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc @@ -28,7 +28,7 @@ LuminanceMatteOperation::LuminanceMatteOperation() add_output_socket(DataType::Value); input_image_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void LuminanceMatteOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.cc b/source/blender/compositor/operations/COM_MapRangeOperation.cc index e8ce1f100c3..7d63a473500 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.cc +++ b/source/blender/compositor/operations/COM_MapRangeOperation.cc @@ -30,7 +30,7 @@ MapRangeOperation::MapRangeOperation() this->add_output_socket(DataType::Value); input_operation_ = nullptr; use_clamp_ = false; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void MapRangeOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index fb4fcff381d..8f46332c9bc 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -26,7 +26,7 @@ MapUVOperation::MapUVOperation() this->add_input_socket(DataType::Vector); this->add_output_socket(DataType::Color); alpha_ = 0.0f; - this->flags.complex = true; + flags_.complex = true; set_canvas_input_index(UV_INPUT_INDEX); inputUVProgram_ = nullptr; diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc index da5ab04cc98..1e6570f2f64 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.cc +++ b/source/blender/compositor/operations/COM_MapValueOperation.cc @@ -25,7 +25,7 @@ MapValueOperation::MapValueOperation() this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Value); input_operation_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void MapValueOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc index 55151b70050..86993ffe887 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.cc +++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc @@ -32,7 +32,7 @@ MathBaseOperation::MathBaseOperation() input_value2_operation_ = nullptr; input_value3_operation_ = nullptr; use_clamp_ = false; - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void MathBaseOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc index a40979fc1e6..09bbb633459 100644 --- a/source/blender/compositor/operations/COM_MixOperation.cc +++ b/source/blender/compositor/operations/COM_MixOperation.cc @@ -33,7 +33,7 @@ MixBaseOperation::MixBaseOperation() input_color2_operation_ = nullptr; this->set_use_value_alpha_multiply(false); this->set_use_clamp(false); - flags.can_be_constant = true; + flags_.can_be_constant = true; } void MixBaseOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc index 89793897a58..0625cdb5ddb 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.cc +++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc @@ -26,8 +26,8 @@ NormalizeOperation::NormalizeOperation() this->add_output_socket(DataType::Value); image_reader_ = nullptr; cached_instance_ = nullptr; - this->flags.complex = true; - flags.can_be_constant = true; + flags_.complex = true; + flags_.can_be_constant = true; } void NormalizeOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc index 6a6692c977d..8467a5d9d97 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc @@ -145,7 +145,7 @@ PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : corners_ready_(fals * so we can use the initialize_tile_data function * to read corners from input sockets ... */ - flags.complex = true; + flags_.complex = true; } void PlaneCornerPinMaskOperation::init_data() diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 21757e4c97d..a11a358c71d 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -69,7 +69,7 @@ PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortB this->add_input_socket(DataType::Color, ResizeMode::Align); this->add_output_socket(DataType::Color); pixel_reader_ = nullptr; - this->flags.complex = true; + flags_.complex = true; } void PlaneDistortWarpImageOperation::calculate_corners(const float corners[4][2], diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.cc b/source/blender/compositor/operations/COM_PosterizeOperation.cc index c43520d103c..585acb31e68 100644 --- a/source/blender/compositor/operations/COM_PosterizeOperation.cc +++ b/source/blender/compositor/operations/COM_PosterizeOperation.cc @@ -27,7 +27,7 @@ PosterizeOperation::PosterizeOperation() this->add_output_socket(DataType::Color); input_program_ = nullptr; input_steps_program_ = nullptr; - flags.can_be_constant = true; + flags_.can_be_constant = true; } void PosterizeOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc index f46f0f5b13f..a3fe0acebdf 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cc +++ b/source/blender/compositor/operations/COM_PreviewOperation.cc @@ -38,8 +38,8 @@ PreviewOperation::PreviewOperation(const ColorManagedViewSettings *view_settings display_settings_ = display_settings; default_width_ = default_width; default_height_ = default_height; - flags.use_viewer_border = true; - flags.is_preview_operation = true; + flags_.use_viewer_border = true; + flags_.is_preview_operation = true; } void PreviewOperation::verify_preview(bNodeInstanceHash *previews, bNodeInstanceKey key) diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc index 436fdb16e8d..4813af511f7 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc @@ -26,7 +26,7 @@ ProjectorLensDistortionOperation::ProjectorLensDistortionOperation() this->add_input_socket(DataType::Color); this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Color); - this->flags.complex = true; + flags_.complex = true; input_program_ = nullptr; dispersion_available_ = false; dispersion_ = 0.0f; diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cc b/source/blender/compositor/operations/COM_ReadBufferOperation.cc index 75b4c61a376..b0e0b2c219f 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.cc +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cc @@ -29,7 +29,7 @@ ReadBufferOperation::ReadBufferOperation(DataType datatype) single_value_ = false; offset_ = 0; buffer_ = nullptr; - flags.is_read_buffer_operation = true; + flags_.is_read_buffer_operation = true; } void *ReadBufferOperation::initialize_tile_data(rcti * /*rect*/) diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index c73462d2e6d..ef67b6ca5f9 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -170,7 +170,7 @@ SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation() this->add_input_socket(DataType::Color); /* image */ this->add_input_socket(DataType::Value); /* Depth, material ID, etc. TODO: currently unused. */ this->add_output_socket(DataType::Color); - this->flags.complex = true; + flags_.complex = true; image_reader_ = nullptr; value_reader_ = nullptr; this->set_threshold(CMP_DEFAULT_SMAA_THRESHOLD); @@ -400,7 +400,7 @@ SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation() { this->add_input_socket(DataType::Color); /* edges */ this->add_output_socket(DataType::Color); - this->flags.complex = true; + flags_.complex = true; image_reader_ = nullptr; this->set_corner_rounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING); } @@ -1010,7 +1010,7 @@ SMAANeighborhoodBlendingOperation::SMAANeighborhoodBlendingOperation() this->add_input_socket(DataType::Color); /* image */ this->add_input_socket(DataType::Color); /* blend */ this->add_output_socket(DataType::Color); - this->flags.complex = true; + flags_.complex = true; image1Reader_ = nullptr; image2Reader_ = nullptr; } diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index 1ce0378c335..e36770b11a1 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -32,7 +32,7 @@ ScreenLensDistortionOperation::ScreenLensDistortionOperation() this->add_input_socket(DataType::Value); this->add_input_socket(DataType::Value); this->add_output_socket(DataType::Color); - this->flags.complex = true; + flags_.complex = true; input_program_ = nullptr; distortion_ = 0.0f; dispersion_ = 0.0f; diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc index a1ea5758a83..9f193985d38 100644 --- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc @@ -28,7 +28,7 @@ SetAlphaMultiplyOperation::SetAlphaMultiplyOperation() input_color_ = nullptr; input_alpha_ = nullptr; - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void SetAlphaMultiplyOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc index 649e6ce0a4e..c4490cab178 100644 --- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc @@ -28,7 +28,7 @@ SetAlphaReplaceOperation::SetAlphaReplaceOperation() input_color_ = nullptr; input_alpha_ = nullptr; - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void SetAlphaReplaceOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index 2f89e6722fa..b7fee856053 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -23,7 +23,7 @@ namespace blender::compositor { SetColorOperation::SetColorOperation() { this->add_output_socket(DataType::Color); - flags.is_set_operation = true; + flags_.is_set_operation = true; } void SetColorOperation::execute_pixel_sampled(float output[4], diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index 9cd4bed0750..d11a35aefc3 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -23,7 +23,7 @@ namespace blender::compositor { SetValueOperation::SetValueOperation() { this->add_output_socket(DataType::Value); - flags.is_set_operation = true; + flags_.is_set_operation = true; } void SetValueOperation::execute_pixel_sampled(float output[4], diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.cc b/source/blender/compositor/operations/COM_SetVectorOperation.cc index 47f13f10fee..c1db429b41e 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.cc +++ b/source/blender/compositor/operations/COM_SetVectorOperation.cc @@ -23,7 +23,7 @@ namespace blender::compositor { SetVectorOperation::SetVectorOperation() { this->add_output_socket(DataType::Vector); - flags.is_set_operation = true; + flags_.is_set_operation = true; } void SetVectorOperation::execute_pixel_sampled(float output[4], diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.cc b/source/blender/compositor/operations/COM_SocketProxyOperation.cc index 18a09362591..fd37f0b5f4f 100644 --- a/source/blender/compositor/operations/COM_SocketProxyOperation.cc +++ b/source/blender/compositor/operations/COM_SocketProxyOperation.cc @@ -24,8 +24,8 @@ SocketProxyOperation::SocketProxyOperation(DataType type, bool use_conversion) { this->add_input_socket(type); this->add_output_socket(type); - flags.is_proxy_operation = true; - flags.use_datatype_conversion = use_conversion; + flags_.is_proxy_operation = true; + flags_.use_datatype_conversion = use_conversion; } std::unique_ptr SocketProxyOperation::get_meta_data() diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index 5842fcc2278..b673a1ac754 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -27,7 +27,7 @@ SunBeamsOperation::SunBeamsOperation() this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); - this->flags.complex = true; + flags_.complex = true; } void SunBeamsOperation::calc_rays_common_data() diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index da1c90cd1c5..2ed83e87569 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -34,7 +34,7 @@ TextureBaseOperation::TextureBaseOperation() rd_ = nullptr; pool_ = nullptr; scene_color_manage_ = false; - flags.complex = true; + flags_.complex = true; } TextureOperation::TextureOperation() : TextureBaseOperation() { diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index 6d3fc9380c1..bc370e3496b 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -31,7 +31,7 @@ TonemapOperation::TonemapOperation() image_reader_ = nullptr; data_ = nullptr; cached_instance_ = nullptr; - this->flags.complex = true; + flags_.complex = true; } void TonemapOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index f329bfb0656..fc6be97a29c 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -35,7 +35,7 @@ TrackPositionOperation::TrackPositionOperation() position_ = CMP_TRACKPOS_ABSOLUTE; relative_frame_ = 0; speed_output_ = false; - flags.is_set_operation = true; + flags_.is_set_operation = true; is_track_position_calculated_ = false; } diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc index 07baf370d1d..891518d53bf 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc @@ -31,8 +31,8 @@ VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation() this->add_input_socket(DataType::Color, ResizeMode::None); #endif this->add_output_socket(DataType::Color); - flags.complex = true; - flags.open_cl = true; + flags_.complex = true; + flags_.open_cl = true; input_program_ = nullptr; input_bokeh_program_ = nullptr; diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc index 8fd074749cd..b220990db76 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc @@ -51,8 +51,8 @@ VectorBlurOperation::VectorBlurOperation() input_image_program_ = nullptr; input_speed_program_ = nullptr; input_zprogram_ = nullptr; - flags.complex = true; - flags.is_fullframe_operation = true; + flags_.complex = true; + flags_.is_fullframe_operation = true; } void VectorBlurOperation::init_execution() { diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index 440deea7796..be8fe1416d0 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -50,8 +50,8 @@ ViewerOperation::ViewerOperation() depth_input_ = nullptr; rd_ = nullptr; view_name_ = nullptr; - flags.use_viewer_border = true; - flags.is_viewer_operation = true; + flags_.use_viewer_border = true; + flags_.is_viewer_operation = true; } void ViewerOperation::init_execution() diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cc b/source/blender/compositor/operations/COM_WriteBufferOperation.cc index 1c728772fdd..2511488615e 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.cc +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cc @@ -27,7 +27,7 @@ WriteBufferOperation::WriteBufferOperation(DataType datatype) memory_proxy_ = new MemoryProxy(datatype); memory_proxy_->set_write_buffer_operation(this); memory_proxy_->set_executor(nullptr); - flags.is_write_buffer_operation = true; + flags_.is_write_buffer_operation = true; } WriteBufferOperation::~WriteBufferOperation() { diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc index 9593e909d5a..c9b3e70a9cd 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.cc +++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc @@ -32,7 +32,7 @@ ZCombineOperation::ZCombineOperation() depth1Reader_ = nullptr; image2Reader_ = nullptr; depth2Reader_ = nullptr; - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } void ZCombineOperation::init_execution() From a3610c451aa11ac6e8756a14db3af4ed5676e794 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Wed, 13 Oct 2021 23:02:03 +0200 Subject: [PATCH 0770/1500] Fix Compositor stack use after scope Caused by {rBf84fb12f5d72433780a96} after changing `get_areas_to_render` to return a vector instead of a span. --- source/blender/compositor/intern/COM_FullFrameExecutionModel.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index 80c10aec00a..1295437785f 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -122,7 +122,7 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op) Vector input_bufs = get_input_buffers(op, output_x, output_y); const int op_offset_x = output_x - op->get_canvas().xmin; const int op_offset_y = output_y - op->get_canvas().ymin; - Span areas = active_buffers_.get_areas_to_render(op, op_offset_x, op_offset_y); + Vector areas = active_buffers_.get_areas_to_render(op, op_offset_x, op_offset_y); op->render(op_buf, areas, input_bufs); DebugInfo::operation_rendered(op, op_buf); From 8278ad3dfb326359f4f8196dc375d952d43c93c7 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Wed, 13 Oct 2021 23:02:15 +0200 Subject: [PATCH 0771/1500] Fix T90799: Box/Ellipse Mask node masking is off-centered This is especially noticeable when using the default center with full width and height as some borders are masked by 1 pixel. The relative coordinates are now calculated respect the last ones instead of the dimensions for centering, and the limits are inclusive to mask more accurately. --- .../operations/COM_BoxMaskOperation.cc | 28 +++++++++---------- .../operations/COM_EllipseMaskOperation.cc | 16 +++++------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.cc b/source/blender/compositor/operations/COM_BoxMaskOperation.cc index adfe7fcd87a..c79179a3e0a 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.cc +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.cc @@ -48,8 +48,8 @@ void BoxMaskOperation::execute_pixel_sampled(float output[4], float input_mask[4]; float input_value[4]; - float rx = x / this->get_width(); - float ry = y / this->get_height(); + float rx = x / MAX2(this->get_width() - 1.0f, FLT_EPSILON); + float ry = y / MAX2(this->get_height() - 1.0f, FLT_EPSILON); const float dy = (ry - data_->y) / aspect_ratio_; const float dx = rx - data_->x; @@ -59,10 +59,10 @@ void BoxMaskOperation::execute_pixel_sampled(float output[4], input_mask_->read_sampled(input_mask, x, y, sampler); input_value_->read_sampled(input_value, x, y, sampler); - float half_height = data_->height / 2.0f; - float half_width = data_->width / 2.0f; - bool inside = (rx > data_->x - half_width && rx < data_->x + half_width && - ry > data_->y - half_height && ry < data_->y + half_height); + float half_height = data_->height / 2.0f + FLT_EPSILON; + float half_width = data_->width / 2.0f + FLT_EPSILON; + bool inside = (rx >= data_->x - half_width && rx <= data_->x + half_width && + ry >= data_->y - half_height && ry <= data_->y + half_height); switch (mask_type_) { case CMP_NODE_MASKTYPE_ADD: @@ -144,20 +144,20 @@ void BoxMaskOperation::apply_mask(MemoryBuffer *output, Span inputs, MaskFunc mask_func) { - const float op_w = this->get_width(); - const float op_h = this->get_height(); - const float half_w = data_->width / 2.0f; - const float half_h = data_->height / 2.0f; + const float op_last_x = MAX2(this->get_width() - 1.0f, FLT_EPSILON); + const float op_last_y = MAX2(this->get_height() - 1.0f, FLT_EPSILON); + const float half_w = data_->width / 2.0f + FLT_EPSILON; + const float half_h = data_->height / 2.0f + FLT_EPSILON; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - const float op_ry = it.y / op_h; + const float op_ry = it.y / op_last_y; const float dy = (op_ry - data_->y) / aspect_ratio_; - const float op_rx = it.x / op_w; + const float op_rx = it.x / op_last_x; const float dx = op_rx - data_->x; const float rx = data_->x + (cosine_ * dx + sine_ * dy); const float ry = data_->y + (-sine_ * dx + cosine_ * dy); - const bool inside = (rx > data_->x - half_w && rx < data_->x + half_w && - ry > data_->y - half_h && ry < data_->y + half_h); + const bool inside = (rx >= data_->x - half_w && rx <= data_->x + half_w && + ry >= data_->y - half_h && ry <= data_->y + half_h); const float *mask = it.in(0); const float *value = it.in(1); *it.out = mask_func(inside, mask, value); diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc index 19497c38131..a051e06d15e 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc @@ -48,8 +48,8 @@ void EllipseMaskOperation::execute_pixel_sampled(float output[4], float input_mask[4]; float input_value[4]; - float rx = x / this->get_width(); - float ry = y / this->get_height(); + float rx = x / MAX2(this->get_width() - 1.0f, FLT_EPSILON); + float ry = y / MAX2(this->get_height() - 1.0f, FLT_EPSILON); const float dy = (ry - data_->y) / aspect_ratio_; const float dx = rx - data_->x; @@ -68,7 +68,7 @@ void EllipseMaskOperation::execute_pixel_sampled(float output[4], sy *= sy; const float ty = half_height * half_height; - bool inside = ((sx / tx) + (sy / ty)) < 1.0f; + bool inside = ((sx / tx) + (sy / ty)) <= (1.0f + FLT_EPSILON); switch (mask_type_) { case CMP_NODE_MASKTYPE_ADD: @@ -152,20 +152,20 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, { const MemoryBuffer *input_mask = inputs[0]; const MemoryBuffer *input_value = inputs[1]; - const float op_w = this->get_width(); - const float op_h = this->get_height(); + const float op_last_x = MAX2(this->get_width() - 1.0f, FLT_EPSILON); + const float op_last_y = MAX2(this->get_height() - 1.0f, FLT_EPSILON); const float half_w = data_->width / 2.0f; const float half_h = data_->height / 2.0f; const float tx = half_w * half_w; const float ty = half_h * half_h; for (int y = area.ymin; y < area.ymax; y++) { - const float op_ry = y / op_h; + const float op_ry = y / op_last_y; const float dy = (op_ry - data_->y) / aspect_ratio_; float *out = output->get_elem(area.xmin, y); const float *mask = input_mask->get_elem(area.xmin, y); const float *value = input_value->get_elem(area.xmin, y); for (int x = area.xmin; x < area.xmax; x++) { - const float op_rx = x / op_w; + const float op_rx = x / op_last_x; const float dx = op_rx - data_->x; const float rx = data_->x + (cosine_ * dx + sine_ * dy); const float ry = data_->y + (-sine_ * dx + cosine_ * dy); @@ -173,7 +173,7 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, sx *= sx; float sy = ry - data_->y; sy *= sy; - const bool inside = ((sx / tx) + (sy / ty)) < 1.0f; + const bool inside = ((sx / tx) + (sy / ty)) <= (1.0f + FLT_EPSILON); out[0] = mask_func(inside, mask, value); mask += input_mask->elem_stride; From 8434aa1b78ee5b0e749a12fcdd6a36d2a819fdb0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Oct 2021 08:57:13 +1100 Subject: [PATCH 0772/1500] Fix invalid arguments to ED_gizmotypes_snap_3d_context_ensure --- source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index f673d3e85ef..0c1e6e91cef 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -379,7 +379,7 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz, float dist_px = 12.0f * U.pixelsize; - ED_gizmotypes_snap_3d_context_ensure(scene, region, v3d, gz); + ED_gizmotypes_snap_3d_context_ensure(scene, gz); snap_elem = ED_transform_snap_object_project_view3d_ex( snap_gizmo->snap_context_v3d, depsgraph, From 685ceaa2f7236a9a76a42609f568b161d5d6d479 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Oct 2021 08:57:14 +1100 Subject: [PATCH 0773/1500] Fix reference counting error for world drag & drop Error in 986d60490c0694941e27c070780c55f07b7b4842 --- source/blender/editors/space_view3d/view3d_edit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 302862b1a8f..d34fbc4562a 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4891,6 +4891,7 @@ static int drop_world_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + id_us_min((ID *)scene->world); id_us_plus(&world->id); scene->world = world; From 9dff3de6acb30193dafd292571858770ee385e51 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Oct 2021 08:57:15 +1100 Subject: [PATCH 0774/1500] Cleanup: clang-tidy, clang-format & spelling --- source/blender/draw/engines/eevee/eevee_lightprobes.c | 4 ++-- .../blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c | 2 +- .../editors/include/ED_transform_snap_object_context.h | 4 ++-- source/blender/nodes/geometry/nodes/node_geo_edge_split.cc | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 56227bc95ec..ab083253499 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -1214,7 +1214,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v common_data->ray_type = EEVEE_RAY_GLOSSY; common_data->ray_depth = 1.0f; - /* Planar reflections are rendered at the hiz resolution, so no need to scalling. */ + /* Planar reflections are rendered at the `hiz` resolution, so no need to scaling. */ copy_v2_fl(common_data->hiz_uv_scale, 1.0f); GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); @@ -1240,7 +1240,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v } if (DRW_state_is_image_render()) { - /* Sort transparents because planar reflections could have re-sorted them. */ + /* Sort the transparent passes because planar reflections could have re-sorted them. */ DRW_pass_sort_shgroup_z(vedata->psl->transparent_pass); } diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index 0c1e6e91cef..322ae87befa 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -576,7 +576,7 @@ static void snap_gizmo_setup(wmGizmo *gz) SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; wmKeyConfig *keyconf = gz->parent_gzgroup->type->keyconf; if (!keyconf) { - /* It can happen when gizmogrouptype is not linked at startup. */ + /* It can happen when gizmo-group-type is not linked at startup. */ keyconf = ((wmWindowManager *)G.main->wm.first)->defaultconf; } snap_gizmo->keymap = WM_modalkeymap_find(keyconf, "Generic Gizmo Tweak Modal Map"); diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index c8e8513d104..7311303d7cd 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -128,8 +128,8 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, struct Depsgraph *depsgraph, - const ARegion *region, - const View3D *v3d, + const ARegion *region, + const View3D *v3d, const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index fcc95d6430c..efac0667ffa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -48,7 +48,7 @@ static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection) BM_mesh_edgesplit(bm, false, true, false); - Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, &mesh); + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, &mesh); BM_mesh_free(bm); BKE_mesh_normals_tag_dirty(result); From c6e956bbb148b80cbe03d59198f8257062434cff Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 13 Oct 2021 22:00:21 -0300 Subject: [PATCH 0775/1500] Fix compile error with 'WITH_CXX_GUARDEDALLOC' --- source/blender/compositor/intern/COM_Device.h | 4 ++++ source/blender/compositor/intern/COM_WorkScheduler.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/source/blender/compositor/intern/COM_Device.h b/source/blender/compositor/intern/COM_Device.h index 1e3618e6c5e..a409ddf97a5 100644 --- a/source/blender/compositor/intern/COM_Device.h +++ b/source/blender/compositor/intern/COM_Device.h @@ -18,6 +18,10 @@ #pragma once +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + namespace blender::compositor { struct WorkPackage; diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h index 2c60a6f2a8a..dfba5a03256 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.h +++ b/source/blender/compositor/intern/COM_WorkScheduler.h @@ -18,6 +18,10 @@ #pragma once +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + namespace blender::compositor { struct WorkPackage; From 576142dc85c78ced792e3440a7665ea57e45a0cc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Oct 2021 10:17:33 +1100 Subject: [PATCH 0776/1500] Cleanup: pass the sizeof(..) as the second arg for array allocation By argument naming and convention this is the intended argument order. --- source/blender/blenkernel/intern/anim_sys.c | 4 ++-- source/blender/blenkernel/intern/armature.c | 8 ++++---- source/blender/blenkernel/intern/lattice_deform.c | 2 +- .../blenkernel/intern/lattice_deform_test.cc | 2 +- source/blender/blenkernel/intern/mesh_fair.cc | 4 ++-- source/blender/blenkernel/intern/multires.c | 2 +- .../blenkernel/intern/multires_reshape_smooth.c | 10 +++++----- .../blenkernel/intern/multires_reshape_util.c | 6 +++--- .../blenkernel/intern/multires_unsubdivide.c | 14 +++++++------- source/blender/blenkernel/intern/subdiv_ccg.c | 4 ++-- source/blender/blenkernel/intern/subdiv_deform.c | 2 +- source/blender/blenkernel/intern/subdiv_mesh.c | 4 ++-- source/blender/blenkernel/intern/tracking_auto.c | 4 ++-- source/blender/blenlib/intern/edgehash.c | 8 ++++---- .../blender/draw/engines/eevee/eevee_cryptomatte.c | 4 ++-- .../blender/editors/animation/anim_motion_paths.c | 2 +- source/blender/editors/animation/keyframing.c | 6 +++--- source/blender/editors/gpencil/gpencil_edit.c | 2 +- .../blender/editors/sculpt_paint/sculpt_expand.c | 12 ++++++------ .../blender/editors/sculpt_paint/sculpt_face_set.c | 2 +- .../editors/sculpt_paint/sculpt_filter_mesh.c | 4 ++-- source/blender/makesdna/intern/dna_genfile.c | 4 ++-- source/blender/makesrna/intern/rna_key.c | 2 +- 23 files changed, 56 insertions(+), 56 deletions(-) diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 92b0db5b214..cbdcf43c039 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1383,7 +1383,7 @@ static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values) switch (RNA_property_type(prop)) { case PROP_BOOLEAN: - tmp_bool = MEM_malloc_arrayN(sizeof(*tmp_bool), length, __func__); + tmp_bool = MEM_malloc_arrayN(length, sizeof(*tmp_bool), __func__); RNA_property_boolean_get_default_array(ptr, prop, tmp_bool); for (int i = 0; i < length; i++) { r_values[i] = (float)tmp_bool[i]; @@ -1391,7 +1391,7 @@ static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values) MEM_freeN(tmp_bool); break; case PROP_INT: - tmp_int = MEM_malloc_arrayN(sizeof(*tmp_int), length, __func__); + tmp_int = MEM_malloc_arrayN(length, sizeof(*tmp_int), __func__); RNA_property_int_get_default_array(ptr, prop, tmp_int); for (int i = 0; i < length; i++) { r_values[i] = (float)tmp_int[i]; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index a86f436185e..e8a3db11219 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -1495,13 +1495,13 @@ static void allocate_bbone_cache(bPoseChannel *pchan, int segments) runtime->bbone_segments = segments; runtime->bbone_rest_mats = MEM_malloc_arrayN( - sizeof(Mat4), 1 + (uint)segments, "bPoseChannel_Runtime::bbone_rest_mats"); + 1 + (uint)segments, sizeof(Mat4), "bPoseChannel_Runtime::bbone_rest_mats"); runtime->bbone_pose_mats = MEM_malloc_arrayN( - sizeof(Mat4), 1 + (uint)segments, "bPoseChannel_Runtime::bbone_pose_mats"); + 1 + (uint)segments, sizeof(Mat4), "bPoseChannel_Runtime::bbone_pose_mats"); runtime->bbone_deform_mats = MEM_malloc_arrayN( - sizeof(Mat4), 2 + (uint)segments, "bPoseChannel_Runtime::bbone_deform_mats"); + 2 + (uint)segments, sizeof(Mat4), "bPoseChannel_Runtime::bbone_deform_mats"); runtime->bbone_dual_quats = MEM_malloc_arrayN( - sizeof(DualQuat), 1 + (uint)segments, "bPoseChannel_Runtime::bbone_dual_quats"); + 1 + (uint)segments, sizeof(DualQuat), "bPoseChannel_Runtime::bbone_dual_quats"); } } diff --git a/source/blender/blenkernel/intern/lattice_deform.c b/source/blender/blenkernel/intern/lattice_deform.c index f9437eeaffa..af721412472 100644 --- a/source/blender/blenkernel/intern/lattice_deform.c +++ b/source/blender/blenkernel/intern/lattice_deform.c @@ -115,7 +115,7 @@ LatticeDeformData *BKE_lattice_deform_data_create(const Object *oblatt, const Ob defgrp_index = BKE_id_defgroup_name_index(<->id, lt->vgroup); if (defgrp_index != -1) { - lattice_weights = MEM_malloc_arrayN(sizeof(float), num_points, "lattice_weights"); + lattice_weights = MEM_malloc_arrayN(num_points, sizeof(float), "lattice_weights"); for (int index = 0; index < num_points; index++) { lattice_weights[index] = BKE_defvert_find_weight(dvert + index, defgrp_index); } diff --git a/source/blender/blenkernel/intern/lattice_deform_test.cc b/source/blender/blenkernel/intern/lattice_deform_test.cc index a7cd5c36ec2..bface94d9d4 100644 --- a/source/blender/blenkernel/intern/lattice_deform_test.cc +++ b/source/blender/blenkernel/intern/lattice_deform_test.cc @@ -44,7 +44,7 @@ static void test_lattice_deform_init(LatticeDeformTestContext *ctx, int32_t num_items) { /* Generate random input data between -5 and 5. */ - ctx->coords = (float(*)[3])MEM_malloc_arrayN(sizeof(float[3]), num_items, __func__); + ctx->coords = (float(*)[3])MEM_malloc_arrayN(num_items, sizeof(float[3]), __func__); for (uint32_t index = 0; index < num_items; index++) { ctx->coords[index][0] = (rng->get_float() - 0.5f) * 10; ctx->coords[index][1] = (rng->get_float() - 0.5f) * 10; diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc index 6ac1aa9b2b9..50db1bc1564 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -293,8 +293,8 @@ class BMeshFairingContext : public FairingContext { } bmloop_.reserve(bm->totloop); - vlmap_ = (MeshElemMap *)MEM_calloc_arrayN(sizeof(MeshElemMap), bm->totvert, "bmesh loop map"); - vlmap_mem_ = (int *)MEM_malloc_arrayN(sizeof(int), bm->totloop, "bmesh loop map mempool"); + vlmap_ = (MeshElemMap *)MEM_calloc_arrayN(bm->totvert, sizeof(MeshElemMap), "bmesh loop map"); + vlmap_mem_ = (int *)MEM_malloc_arrayN(bm->totloop, sizeof(int), "bmesh loop map mempool"); BMVert *v; BMLoop *l; diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index eaa11a6683a..dc418545e23 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -1496,7 +1496,7 @@ void multires_topology_changed(Mesh *me) if (!mdisp->totdisp || !mdisp->disps) { if (grid) { mdisp->totdisp = grid; - mdisp->disps = MEM_calloc_arrayN(sizeof(float[3]), mdisp->totdisp, "mdisp topology"); + mdisp->disps = MEM_calloc_arrayN(mdisp->totdisp, sizeof(float[3]), "mdisp topology"); } continue; diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c index 9fb158d2f84..3665d01926b 100644 --- a/source/blender/blenkernel/intern/multires_reshape_smooth.c +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c @@ -271,7 +271,7 @@ static void base_surface_grids_allocate(MultiresReshapeSmoothContext *reshape_sm for (int grid_index = 0; grid_index < num_grids; ++grid_index) { surface_grid[grid_index].points = MEM_calloc_arrayN( - sizeof(SurfacePoint), grid_area, "delta grid displacement"); + grid_area, sizeof(SurfacePoint), "delta grid displacement"); } reshape_smooth_context->base_surface_grids = surface_grid; @@ -576,19 +576,19 @@ static bool foreach_topology_info(const SubdivForeachContext *foreach_context, /* NOTE: Calloc so the counters are re-set to 0 "for free". */ reshape_smooth_context->geometry.num_vertices = num_vertices; reshape_smooth_context->geometry.vertices = MEM_calloc_arrayN( - sizeof(Vertex), num_vertices, "smooth vertices"); + num_vertices, sizeof(Vertex), "smooth vertices"); reshape_smooth_context->geometry.max_edges = max_edges; reshape_smooth_context->geometry.edges = MEM_malloc_arrayN( - sizeof(Edge), max_edges, "smooth edges"); + max_edges, sizeof(Edge), "smooth edges"); reshape_smooth_context->geometry.num_corners = num_loops; reshape_smooth_context->geometry.corners = MEM_malloc_arrayN( - sizeof(Corner), num_loops, "smooth corners"); + num_loops, sizeof(Corner), "smooth corners"); reshape_smooth_context->geometry.num_faces = num_polygons; reshape_smooth_context->geometry.faces = MEM_malloc_arrayN( - sizeof(Face), num_polygons, "smooth faces"); + num_polygons, sizeof(Face), "smooth faces"); return true; } diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c index 8fb406e54a5..79a2b9eb002 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.c +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -86,7 +86,7 @@ static void context_init_lookup(MultiresReshapeContext *reshape_context) const int num_faces = base_mesh->totpoly; reshape_context->face_start_grid_index = MEM_malloc_arrayN( - sizeof(int), num_faces, "face_start_grid_index"); + num_faces, sizeof(int), "face_start_grid_index"); int num_grids = 0; int num_ptex_faces = 0; for (int face_index = 0; face_index < num_faces; ++face_index) { @@ -97,9 +97,9 @@ static void context_init_lookup(MultiresReshapeContext *reshape_context) } reshape_context->grid_to_face_index = MEM_malloc_arrayN( - sizeof(int), num_grids, "grid_to_face_index"); + num_grids, sizeof(int), "grid_to_face_index"); reshape_context->ptex_start_grid_index = MEM_malloc_arrayN( - sizeof(int), num_ptex_faces, "ptex_start_grid_index"); + num_ptex_faces, sizeof(int), "ptex_start_grid_index"); for (int face_index = 0, grid_index = 0, ptex_index = 0; face_index < num_faces; ++face_index) { const int num_corners = mpoly[face_index].totloop; const int num_face_ptex_faces = (num_corners == 4) ? 1 : num_corners; diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c index 501e3f27389..643e1a50fd5 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.c +++ b/source/blender/blenkernel/intern/multires_unsubdivide.c @@ -177,7 +177,7 @@ static bool is_vertex_diagonal(BMVert *from_v, BMVert *to_v) */ static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex) { - bool *visited_vertices = MEM_calloc_arrayN(sizeof(bool), bm->totvert, "visited vertices"); + bool *visited_vertices = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices"); GSQueue *queue; queue = BLI_gsqueue_new(sizeof(BMVert *)); @@ -368,7 +368,7 @@ static bool unsubdivide_tag_disconnected_mesh_element(BMesh *bm, int *elem_id, i */ static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id) { - bool *visited_vertices = MEM_calloc_arrayN(sizeof(bool), bm->totvert, "visited vertices"); + bool *visited_vertices = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices"); int current_id = 0; for (int i = 0; i < bm->totvert; i++) { if (!visited_vertices[i]) { @@ -475,7 +475,7 @@ static bool multires_unsubdivide_single_level(BMesh *bm) BM_mesh_elem_table_ensure(bm, BM_VERT); /* Build disconnected elements IDs. Each disconnected mesh element is evaluated separately. */ - int *elem_id = MEM_calloc_arrayN(sizeof(int), bm->totvert, " ELEM ID"); + int *elem_id = MEM_calloc_arrayN(bm->totvert, sizeof(int), " ELEM ID"); const int tot_ids = unsubdivide_init_elem_ids(bm, elem_id); bool valid_tag_found = true; @@ -961,7 +961,7 @@ static void multires_unsubdivide_prepare_original_bmesh_for_extract( } /* Create a map from loop index to poly index for the original mesh. */ - context->loop_to_face_map = MEM_calloc_arrayN(sizeof(int), original_mesh->totloop, "loop map"); + context->loop_to_face_map = MEM_calloc_arrayN(original_mesh->totloop, sizeof(int), "loop map"); for (int i = 0; i < original_mesh->totpoly; i++) { MPoly *poly = &original_mesh->mpoly[i]; @@ -1005,13 +1005,13 @@ static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *conte context->num_grids = base_mesh->totloop; context->base_mesh_grids = MEM_calloc_arrayN( - sizeof(MultiresUnsubdivideGrid), base_mesh->totloop, "grids"); + base_mesh->totloop, sizeof(MultiresUnsubdivideGrid), "grids"); /* Based on the existing indices in the data-layers, generate two vertex indices maps. */ /* From vertex index in original to vertex index in base and from vertex index in base to vertex * index in original. */ - int *orig_to_base_vmap = MEM_calloc_arrayN(sizeof(int), bm_original_mesh->totvert, "orig vmap"); - int *base_to_orig_vmap = MEM_calloc_arrayN(sizeof(int), base_mesh->totvert, "base vmap"); + int *orig_to_base_vmap = MEM_calloc_arrayN(bm_original_mesh->totvert, sizeof(int), "orig vmap"); + int *base_to_orig_vmap = MEM_calloc_arrayN(base_mesh->totvert, sizeof(int), "base vmap"); context->base_to_orig_vmap = CustomData_get_layer_named(&base_mesh->vdata, CD_PROP_INT32, vname); for (int i = 0; i < base_mesh->totvert; i++) { diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index 07c4e8c2316..c7c77b83e25 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -1062,7 +1062,7 @@ static void subdiv_ccg_average_grids_boundary(SubdivCCG *subdiv_ccg, } if (tls->accumulators == NULL) { tls->accumulators = MEM_calloc_arrayN( - sizeof(GridElementAccumulator), grid_size2, "average accumulators"); + grid_size2, sizeof(GridElementAccumulator), "average accumulators"); } else { for (int i = 1; i < grid_size2 - 1; i++) { @@ -1972,7 +1972,7 @@ const int *BKE_subdiv_ccg_start_face_grid_index_ensure(SubdivCCG *subdiv_ccg) const int num_coarse_faces = topology_refiner->getNumFaces(topology_refiner); subdiv_ccg->cache_.start_face_grid_index = MEM_malloc_arrayN( - sizeof(int), num_coarse_faces, "start_face_grid_index"); + num_coarse_faces, sizeof(int), "start_face_grid_index"); int start_grid_index = 0; for (int face_index = 0; face_index < num_coarse_faces; face_index++) { diff --git a/source/blender/blenkernel/intern/subdiv_deform.c b/source/blender/blenkernel/intern/subdiv_deform.c index 2c900fbd600..7a2d639e4e5 100644 --- a/source/blender/blenkernel/intern/subdiv_deform.c +++ b/source/blender/blenkernel/intern/subdiv_deform.c @@ -69,7 +69,7 @@ static void subdiv_mesh_prepare_accumulator(SubdivDeformContext *ctx, int num_ve return; } ctx->accumulated_counters = MEM_calloc_arrayN( - sizeof(*ctx->accumulated_counters), num_vertices, "subdiv accumulated counters"); + num_vertices, sizeof(*ctx->accumulated_counters), "subdiv accumulated counters"); } static void subdiv_mesh_context_free(SubdivDeformContext *ctx) diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c index 01bccab1bbd..29e7d0a1a3c 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -108,9 +108,9 @@ static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vert /* TODO(sergey): Technically, this is overallocating, we don't need memory * for an inner subdivision vertices. */ ctx->accumulated_normals = MEM_calloc_arrayN( - sizeof(*ctx->accumulated_normals), num_vertices, "subdiv accumulated normals"); + num_vertices, sizeof(*ctx->accumulated_normals), "subdiv accumulated normals"); ctx->accumulated_counters = MEM_calloc_arrayN( - sizeof(*ctx->accumulated_counters), num_vertices, "subdiv accumulated counters"); + num_vertices, sizeof(*ctx->accumulated_counters), "subdiv accumulated counters"); } static void subdiv_mesh_context_free(SubdivMeshContext *ctx) diff --git a/source/blender/blenkernel/intern/tracking_auto.c b/source/blender/blenkernel/intern/tracking_auto.c index 92ff0911cf3..e2a29d7858d 100644 --- a/source/blender/blenkernel/intern/tracking_auto.c +++ b/source/blender/blenkernel/intern/tracking_auto.c @@ -475,7 +475,7 @@ static void autotrack_context_init_autotrack(AutoTrackContext *context) /* Allocate memory for all the markers. */ libmv_Marker *libmv_markers = MEM_malloc_arrayN( - sizeof(libmv_Marker), num_trackable_markers, "libmv markers array"); + num_trackable_markers, sizeof(libmv_Marker), "libmv markers array"); /* Fill in markers array. */ int num_filled_libmv_markers = 0; @@ -516,7 +516,7 @@ static void autotrack_context_init_markers(AutoTrackContext *context) /* Allocate required memory. */ context->autotrack_markers = MEM_calloc_arrayN( - sizeof(AutoTrackMarker), context->num_autotrack_markers, "auto track options"); + context->num_autotrack_markers, sizeof(AutoTrackMarker), "auto track options"); /* Fill in all the markers. */ int autotrack_marker_index = 0; diff --git a/source/blender/blenlib/intern/edgehash.c b/source/blender/blenlib/intern/edgehash.c index f95619803bf..6c397fae836 100644 --- a/source/blender/blenlib/intern/edgehash.c +++ b/source/blender/blenlib/intern/edgehash.c @@ -230,8 +230,8 @@ EdgeHash *BLI_edgehash_new_ex(const char *info, const uint reserve) UPDATE_SLOT_MASK(eh); eh->length = 0; eh->dummy_count = 0; - eh->entries = MEM_calloc_arrayN(sizeof(EdgeHashEntry), ENTRIES_CAPACITY(eh), "eh entries"); - eh->map = MEM_malloc_arrayN(sizeof(int32_t), MAP_CAPACITY(eh), "eh map"); + eh->entries = MEM_calloc_arrayN(ENTRIES_CAPACITY(eh), sizeof(EdgeHashEntry), "eh entries"); + eh->map = MEM_malloc_arrayN(MAP_CAPACITY(eh), sizeof(int32_t), "eh map"); CLEAR_MAP(eh); return eh; } @@ -515,8 +515,8 @@ EdgeSet *BLI_edgeset_new_ex(const char *info, const uint reserve) es->capacity_exp = calc_capacity_exp_for_reserve(reserve); UPDATE_SLOT_MASK(es); es->length = 0; - es->entries = MEM_malloc_arrayN(sizeof(Edge), ENTRIES_CAPACITY(es), "es entries"); - es->map = MEM_malloc_arrayN(sizeof(int32_t), MAP_CAPACITY(es), "es map"); + es->entries = MEM_malloc_arrayN(ENTRIES_CAPACITY(es), sizeof(Edge), "es entries"); + es->map = MEM_malloc_arrayN(MAP_CAPACITY(es), sizeof(int32_t), "es map"); CLEAR_MAP(es); return es; } diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index ea60dd0753e..4c9ce9dbd65 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -164,12 +164,12 @@ void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata), if (g_data->cryptomatte_accum_buffer == NULL) { g_data->cryptomatte_accum_buffer = MEM_calloc_arrayN( - sizeof(EEVEE_CryptomatteSample), buffer_size * eevee_cryptomatte_pixel_stride(view_layer), + sizeof(EEVEE_CryptomatteSample), __func__); /* Download buffer should store a float per active cryptomatte layer. */ g_data->cryptomatte_download_buffer = MEM_malloc_arrayN( - sizeof(float), buffer_size * num_cryptomatte_layers, __func__); + buffer_size * num_cryptomatte_layers, sizeof(float), __func__); } else { /* During multiview rendering the `cryptomatte_accum_buffer` is deallocated after all views diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index d130941b9bc..0d812198d04 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -84,7 +84,7 @@ Depsgraph *animviz_depsgraph_build(Main *bmain, /* Make a flat array of IDs for the DEG API. */ const int num_ids = BLI_listbase_count(targets); - ID **ids = MEM_malloc_arrayN(sizeof(ID *), num_ids, "animviz IDS"); + ID **ids = MEM_malloc_arrayN(num_ids, sizeof(ID *), "animviz IDS"); int current_id_index = 0; for (MPathTarget *mpt = targets->first; mpt != NULL; mpt = mpt->next) { ids[current_id_index++] = &mpt->ob->id; diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 95df8b69cd4..2c6b5cc594b 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -792,12 +792,12 @@ static float *setting_get_rna_values( int *tmp_int; if (length > buffer_size) { - values = MEM_malloc_arrayN(sizeof(float), length, __func__); + values = MEM_malloc_arrayN(length, sizeof(float), __func__); } switch (RNA_property_type(prop)) { case PROP_BOOLEAN: - tmp_bool = MEM_malloc_arrayN(sizeof(*tmp_bool), length, __func__); + tmp_bool = MEM_malloc_arrayN(length, sizeof(*tmp_bool), __func__); RNA_property_boolean_get_array(ptr, prop, tmp_bool); for (int i = 0; i < length; i++) { values[i] = (float)tmp_bool[i]; @@ -805,7 +805,7 @@ static float *setting_get_rna_values( MEM_freeN(tmp_bool); break; case PROP_INT: - tmp_int = MEM_malloc_arrayN(sizeof(*tmp_int), length, __func__); + tmp_int = MEM_malloc_arrayN(length, sizeof(*tmp_int), __func__); RNA_property_int_get_array(ptr, prop, tmp_int); for (int i = 0; i < length; i++) { values[i] = (float)tmp_int[i]; diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 90b6db82838..f6012883e1f 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3556,7 +3556,7 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op) int tot_strokes = 0; /** Alloc memory. */ - tJoinStrokes *strokes_list = MEM_malloc_arrayN(sizeof(tJoinStrokes), max_join_strokes, __func__); + tJoinStrokes *strokes_list = MEM_malloc_arrayN(max_join_strokes, sizeof(tJoinStrokes), __func__); tJoinStrokes *elem = NULL; /* Read all selected strokes to create a list. */ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 40874375772..2ba03969f38 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -465,7 +465,7 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "topology dist"); + float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "topology dist"); SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); @@ -515,7 +515,7 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd, { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_malloc_arrayN(sizeof(float), totvert, "normal dist"); + float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "normal dist"); float *edge_factor = MEM_callocN(sizeof(float) * totvert, "mask edge factor"); for (int i = 0; i < totvert; i++) { edge_factor[i] = 1.0f; @@ -560,7 +560,7 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_malloc_arrayN(sizeof(float), totvert, "spherical dist"); + float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "spherical dist"); for (int i = 0; i < totvert; i++) { dists[i] = FLT_MAX; } @@ -591,7 +591,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist"); + float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "spherical dist"); BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); GSQueue *queue = BLI_gsqueue_new(sizeof(int)); @@ -652,7 +652,7 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist"); + float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "spherical dist"); /* This algorithm uses mesh data (polys and loops), so this falloff type can't be initialized for * Multires. It also does not make sense to implement it for dyntopo as the result will be the @@ -863,7 +863,7 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob, SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "topology dist"); + float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "topology dist"); BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false); SculptFloodFill flood; diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index bdbdb75732a..89e07081802 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -1233,7 +1233,7 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob, const int totvert = SCULPT_vertex_count_get(ss); Mesh *mesh = ob->data; - bool *fair_vertices = MEM_malloc_arrayN(sizeof(bool), totvert, "fair vertices"); + bool *fair_vertices = MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices"); SCULPT_boundary_info_ensure(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 3fc1a7674f7..2a7c2a46818 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -511,7 +511,7 @@ static void mesh_filter_init_limit_surface_co(SculptSession *ss) FilterCache *filter_cache = ss->filter_cache; filter_cache->limit_surface_co = MEM_malloc_arrayN( - sizeof(float[3]), totvert, "limit surface co"); + totvert, sizeof(float[3]), "limit surface co"); for (int i = 0; i < totvert; i++) { SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]); } @@ -528,7 +528,7 @@ static void mesh_filter_sharpen_init(SculptSession *ss, filter_cache->sharpen_smooth_ratio = smooth_ratio; filter_cache->sharpen_intensify_detail_strength = intensify_detail_strength; filter_cache->sharpen_curvature_smooth_iterations = curvature_smooth_iterations; - filter_cache->sharpen_factor = MEM_malloc_arrayN(sizeof(float), totvert, "sharpen factor"); + filter_cache->sharpen_factor = MEM_malloc_arrayN(totvert, sizeof(float), "sharpen factor"); filter_cache->detail_directions = MEM_malloc_arrayN( totvert, sizeof(float[3]), "sharpen detail direction"); diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c index 24d0d39292e..34d2260d35b 100644 --- a/source/blender/makesdna/intern/dna_genfile.c +++ b/source/blender/makesdna/intern/dna_genfile.c @@ -1546,9 +1546,9 @@ DNA_ReconstructInfo *DNA_reconstruct_info_create(const SDNA *oldsdna, reconstruct_info->oldsdna = oldsdna; reconstruct_info->newsdna = newsdna; reconstruct_info->compare_flags = compare_flags; - reconstruct_info->step_counts = MEM_malloc_arrayN(sizeof(int), newsdna->structs_len, __func__); + reconstruct_info->step_counts = MEM_malloc_arrayN(newsdna->structs_len, sizeof(int), __func__); reconstruct_info->steps = MEM_malloc_arrayN( - sizeof(ReconstructStep *), newsdna->structs_len, __func__); + newsdna->structs_len, sizeof(ReconstructStep *), __func__); /* Generate reconstruct steps for all structs. */ for (int new_struct_nr = 0; new_struct_nr < newsdna->structs_len; new_struct_nr++) { diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index 9d8ed161738..c7a1c89af63 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -541,7 +541,7 @@ static void rna_ShapeKey_data_begin_mixed(CollectionPropertyIterator *iter, int point_count = rna_ShapeKey_curve_find_index(key, kb->totelem); ShapeKeyCurvePoint *points = MEM_malloc_arrayN( - sizeof(ShapeKeyCurvePoint), point_count, __func__); + point_count, sizeof(ShapeKeyCurvePoint), __func__); char *databuf = kb->data; int items_left = point_count; From 5401fda41244175b491ab5aad8c9693b3eb09f33 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Oct 2021 10:22:51 +1100 Subject: [PATCH 0777/1500] Cleanup: avoid using size for array length in naming Confusing when array allocation takes two kinds of size arguments. --- source/blender/blenkernel/intern/subdiv_ccg.c | 19 +++++++++---------- .../blenloader/intern/versioning_300.c | 10 +++++----- .../intern/MOD_solidify_nonmanifold.c | 14 +++++++------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index c7c77b83e25..77962ec924c 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -359,30 +359,29 @@ static void subdiv_ccg_init_faces(SubdivCCG *subdiv_ccg) /* TODO(sergey): Consider making it generic enough to be fit into BLI. */ typedef struct StaticOrHeapIntStorage { int static_storage[64]; - int static_storage_size; + int static_storage_len; int *heap_storage; - int heap_storage_size; + int heap_storage_len; } StaticOrHeapIntStorage; static void static_or_heap_storage_init(StaticOrHeapIntStorage *storage) { - storage->static_storage_size = sizeof(storage->static_storage) / - sizeof(*storage->static_storage); + storage->static_storage_len = sizeof(storage->static_storage) / sizeof(*storage->static_storage); storage->heap_storage = NULL; - storage->heap_storage_size = 0; + storage->heap_storage_len = 0; } -static int *static_or_heap_storage_get(StaticOrHeapIntStorage *storage, int size) +static int *static_or_heap_storage_get(StaticOrHeapIntStorage *storage, int heap_len) { /* Requested size small enough to be fit into stack allocated memory. */ - if (size <= storage->static_storage_size) { + if (heap_len <= storage->static_storage_len) { return storage->static_storage; } /* Make sure heap ius big enough. */ - if (size > storage->heap_storage_size) { + if (heap_len > storage->heap_storage_len) { MEM_SAFE_FREE(storage->heap_storage); - storage->heap_storage = MEM_malloc_arrayN(size, sizeof(int), "int storage"); - storage->heap_storage_size = size; + storage->heap_storage = MEM_malloc_arrayN(heap_len, sizeof(int), "int storage"); + storage->heap_storage_len = heap_len; } return storage->heap_storage; } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index e5b2536f123..b753ab484af 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -153,18 +153,18 @@ static void version_idproperty_move_data_float(IDPropertyUIDataFloat *ui_data, IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default"); if (default_value != NULL) { if (default_value->type == IDP_ARRAY) { - const int size = default_value->len; - ui_data->default_array_len = size; + const int array_len = default_value->len; + ui_data->default_array_len = array_len; if (default_value->subtype == IDP_FLOAT) { - ui_data->default_array = MEM_malloc_arrayN(size, sizeof(double), __func__); + ui_data->default_array = MEM_malloc_arrayN(array_len, sizeof(double), __func__); const float *old_default_array = IDP_Array(default_value); for (int i = 0; i < ui_data->default_array_len; i++) { ui_data->default_array[i] = (double)old_default_array[i]; } } else if (default_value->subtype == IDP_DOUBLE) { - ui_data->default_array = MEM_malloc_arrayN(size, sizeof(double), __func__); - memcpy(ui_data->default_array, IDP_Array(default_value), sizeof(double) * size); + ui_data->default_array = MEM_malloc_arrayN(array_len, sizeof(double), __func__); + memcpy(ui_data->default_array, IDP_Array(default_value), sizeof(double) * array_len); } } else if (ELEM(default_value->type, IDP_DOUBLE, IDP_FLOAT)) { diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index f654b69841e..d4aaefcfe05 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -1168,9 +1168,9 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, add_index++; } if (last_split > split) { - const uint size = (split + edges_len) - (uint)last_split; + const uint edges_len_group = (split + edges_len) - (uint)last_split; NewEdgeRef **edges = MEM_malloc_arrayN( - size, sizeof(*edges), "edge_group split in solidify"); + edges_len_group, sizeof(*edges), "edge_group split in solidify"); memcpy(edges, g.edges + last_split, (edges_len - (uint)last_split) * sizeof(*edges)); @@ -1180,7 +1180,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, edge_groups[j + add_index] = (EdgeGroup){ .valid = true, .edges = edges, - .edges_len = size, + .edges_len = edges_len_group, .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG, .is_orig_closed = g.is_orig_closed, .is_even_split = is_even_split, @@ -1193,14 +1193,14 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, }; } else { - const uint size = split - (uint)last_split; + const uint edges_len_group = split - (uint)last_split; NewEdgeRef **edges = MEM_malloc_arrayN( - size, sizeof(*edges), "edge_group split in solidify"); - memcpy(edges, g.edges + last_split, size * sizeof(*edges)); + edges_len_group, sizeof(*edges), "edge_group split in solidify"); + memcpy(edges, g.edges + last_split, edges_len_group * sizeof(*edges)); edge_groups[j + add_index] = (EdgeGroup){ .valid = true, .edges = edges, - .edges_len = size, + .edges_len = edges_len_group, .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG, .is_orig_closed = g.is_orig_closed, .is_even_split = is_even_split, From a059d16f65c50fe8200fa4f730da42a86cda92d0 Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Wed, 13 Oct 2021 23:33:46 -0500 Subject: [PATCH 0778/1500] Geometry Nodes: Add Offset Option to Set Postion Add a boolean field to the Set Position Node. This value allows for each point to either have its position set to the input position value or have the input value added to the current position. Differential Revision: https://developer.blender.org/D12773 --- .../geometry/nodes/node_geo_set_position.cc | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 8caf961fc04..15930508e78 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -23,14 +23,16 @@ namespace blender::nodes { static void geo_node_set_position_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Position").implicit_field(); b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Position").implicit_field(); + b.add_input("Offset").default_value(false).supports_field(); b.add_output("Geometry"); } static void set_position_in_component(GeometryComponent &component, const Field &selection_field, - const Field &position_field) + const Field &position_field, + const Field &offset_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); @@ -45,9 +47,23 @@ static void set_position_in_component(GeometryComponent &component, OutputAttribute_Typed positions = component.attribute_try_get_for_output( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + MutableSpan position_mutable = positions.as_span(); + fn::FieldEvaluator position_evaluator{field_context, &selection}; - position_evaluator.add_with_destination(position_field, positions.varray()); + position_evaluator.add(position_field); + position_evaluator.add(offset_field); position_evaluator.evaluate(); + + /* TODO: We could have different code paths depending on whether the offset input is a single + * value or not */ + + const VArray &positions_input = position_evaluator.get_evaluated(0); + const VArray &offsets_input = position_evaluator.get_evaluated(1); + + for (int i : selection) { + position_mutable[i] = offsets_input[i] ? position_mutable[i] + positions_input[i] : + positions_input[i]; + } positions.save(); } @@ -55,6 +71,7 @@ static void geo_node_set_position_exec(GeoNodeExecParams params) { GeometrySet geometry = params.extract_input("Geometry"); Field selection_field = params.extract_input>("Selection"); + Field offset_field = params.extract_input>("Offset"); Field position_field = params.extract_input>("Position"); for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, @@ -63,7 +80,7 @@ static void geo_node_set_position_exec(GeoNodeExecParams params) GEO_COMPONENT_TYPE_INSTANCES}) { if (geometry.has(type)) { set_position_in_component( - geometry.get_component_for_write(type), selection_field, position_field); + geometry.get_component_for_write(type), selection_field, position_field, offset_field); } } From 5fec6eda551470d320dd2ab586e13dcc06de1d2f Mon Sep 17 00:00:00 2001 From: Ankit Meel Date: Thu, 14 Oct 2021 10:06:16 +0530 Subject: [PATCH 0779/1500] Cleanup: silence Clang missing-braces warning. --- source/blender/blenlib/intern/uuid.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index 3c86238036c..de4602bf3ed 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -83,7 +83,7 @@ bUUID BLI_uuid_generate_random() bUUID BLI_uuid_nil(void) { - const bUUID nil = {0, 0, 0, 0, 0, 0}; + const bUUID nil = {0, 0, 0, 0, 0, {0}}; return nil; } From f12ea3d52e64dfba3170ac1465a9a451d0cb20bd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Oct 2021 16:41:35 +1100 Subject: [PATCH 0780/1500] Cleanup: reserve C++ comments for disabled code --- .../blender/blenkernel/intern/asset_catalog.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index f66fda1a0bc..f3a4f88ef38 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -243,7 +243,7 @@ void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_director { BLI_stat_t status; if (BLI_stat(file_or_directory_path.data(), &status) == -1) { - // TODO(@sybren): throw an appropriate exception. + /* TODO(@sybren): throw an appropriate exception. */ return; } @@ -254,7 +254,7 @@ void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_director load_directory_recursive(file_or_directory_path); } else { - // TODO(@sybren): throw an appropriate exception. + /* TODO(@sybren): throw an appropriate exception. */ } /* TODO: Should there be a sanitize step? E.g. to remove catalogs with identical paths? */ @@ -264,8 +264,8 @@ void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_director void AssetCatalogService::load_directory_recursive(const CatalogFilePath &directory_path) { - // TODO(@sybren): implement proper multi-file support. For now, just load - // the default file if it is there. + /* TODO(@sybren): implement proper multi-file support. For now, just load + * the default file if it is there. */ CatalogFilePath file_path = asset_definition_default_file_path_from_dir(directory_path); if (!BLI_exists(file_path.data())) { @@ -297,7 +297,7 @@ std::unique_ptr AssetCatalogService::parse_catalog_f auto catalog_parsed_callback = [this, catalog_definition_file_path]( std::unique_ptr catalog) { if (catalog_collection_->catalogs_.contains(catalog->catalog_id)) { - // TODO(@sybren): apparently another CDF was already loaded. This is not supported yet. + /* TODO(@sybren): apparently another CDF was already loaded. This is not supported yet. */ std::cerr << catalog_definition_file_path << ": multiple definitions of catalog " << catalog->catalog_id << " in multiple files, ignoring this one." << std::endl; /* Don't store 'catalog'; unique_ptr will free its memory. */ @@ -805,16 +805,16 @@ bool AssetCatalogDefinitionFile::write_to_disk_unsafe(const CatalogFilePath &des std::ofstream output(dest_file_path); - // TODO(@sybren): remember the line ending style that was originally read, then use that to write - // the file again. + /* TODO(@sybren): remember the line ending style that was originally read, then use that to write + * the file again. */ - // Write the header. + /* Write the header. */ output << HEADER; output << "" << std::endl; output << VERSION_MARKER << SUPPORTED_VERSION << std::endl; output << "" << std::endl; - // Write the catalogs, ordered by path (primary) and UUID (secondary). + /* Write the catalogs, ordered by path (primary) and UUID (secondary). */ AssetCatalogOrderedSet catalogs_by_path; for (const AssetCatalog *catalog : catalogs_.values()) { if (catalog->flags.is_deleted) { From a620ce7e5430b829ad6b00c8c1565226f3fbd9b5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Oct 2021 16:41:37 +1100 Subject: [PATCH 0781/1500] Cleanup: use dot-points for appdir platform specific paths --- source/blender/blenkernel/intern/appdir.c | 28 ++++++++++++++--------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 0ecf6238d3d..ce4ab8a4ba1 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -148,11 +148,12 @@ static char *blender_version_decimal(const int version) * \{ */ /** - * Get the folder that's the "natural" starting point for browsing files on an OS. On Unix that is - * $HOME, on Windows it is %userprofile%/Documents. + * Get the folder that's the "natural" starting point for browsing files on an OS. + * - Unix: `$HOME` + * - Windows: `%userprofile%/Documents` * * \note On Windows `Users/{MyUserName}/Documents` is used as it's the default location to save - * documents. + * documents. */ const char *BKE_appdir_folder_default(void) { @@ -170,7 +171,9 @@ const char *BKE_appdir_folder_default(void) } /** - * Get the user's home directory, i.e. $HOME on UNIX, %userprofile% on Windows. + * Get the user's home directory, i.e. + * - Unix: `$HOME` + * - Windows: `%userprofile%` */ const char *BKE_appdir_folder_home(void) { @@ -182,8 +185,11 @@ const char *BKE_appdir_folder_home(void) } /** - * Get the user's document directory, i.e. $HOME/Documents on Linux, %userprofile%/Documents on - * Windows. If this can't be found using OS queries (via Ghost), try manually finding it. + * Get the user's document directory, i.e. + * - Linux: `$HOME/Documents` + * - Windows: `%userprofile%/Documents` + * + * If this can't be found using OS queries (via Ghost), try manually finding it. * * \returns True if the path is valid and points to an existing directory. */ @@ -218,13 +224,13 @@ bool BKE_appdir_folder_documents(char *dir) } /** - * Get the user's cache directory, i.e. $HOME/.cache/blender/ on Linux, - * %USERPROFILE%\AppData\Local\Blender Foundation\Blender\ on Windows and - * /Library/Caches/Blender on MacOS. + * Get the user's cache directory, i.e. + * - Linux: `$HOME/.cache/blender/` + * - Windows: `%USERPROFILE%\AppData\Local\Blender Foundation\Blender\` + * - MacOS: `/Library/Caches/Blender` * * \returns True if the path is valid. It doesn't create or checks format - * if the `blender` folder exists. It does check if the parent of the - * path exists. + * if the `blender` folder exists. It does check if the parent of the path exists. */ bool BKE_appdir_folder_caches(char *r_path, const size_t path_len) { From 3be2d6078f8c7831db59b4c0095fe3dd8bbabf16 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Oct 2021 16:41:38 +1100 Subject: [PATCH 0782/1500] Cleanup: remove historic reference from makesdna.c We have mostly removed information about original authors, as this information isn't so useful. --- source/blender/makesdna/intern/makesdna.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 9957808b63d..114c0b40407 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -23,10 +23,8 @@ * \brief Struct muncher for making SDNA. * * \section aboutmakesdnac About makesdna tool - * Originally by Ton, some mods by Frank, and some cleaning and - * extension by Nzc. * - * Makesdna creates a .c file with a long string of numbers that + * `makesdna` creates a .c file with a long string of numbers that * encode the Blender file format. It is fast, because it is basically * a binary dump. There are some details to mind when reconstructing * the file (endianness and byte-alignment). @@ -36,9 +34,9 @@ * how much memory (on disk or in ram) is needed to store that struct, * and the offsets for reaching a particular one. * - * There is a facility to get verbose output from sdna. Search for - * \ref debugSDNA. This int can be set to 0 (no output) to some int. Higher - * numbers give more output. + * There is a facility to get verbose output from `sdna`. Search for + * \ref debugSDNA. This int can be set to 0 (no output) to some int. + * Higher numbers give more output. */ #define DNA_DEPRECATED_ALLOW From 4f5ef3b01817ec8b73dca9e6fa1672fbe09167e4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Oct 2021 16:41:40 +1100 Subject: [PATCH 0783/1500] WM: quiet output of delete object operator Object deletion was reporting the number of objects deleted, causing tests to print noisy output. Now this is information is only included when invoked. --- source/blender/editors/object/object_add.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 4b2315c0552..cf7c848ae24 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1997,6 +1997,7 @@ static int object_delete_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); const bool use_global = RNA_boolean_get(op->ptr, "use_global"); + const bool confirm = op->flag & OP_IS_INVOKE; uint changed_count = 0; uint tagged_count = 0; @@ -2075,7 +2076,9 @@ static int object_delete_exec(bContext *C, wmOperator *op) BKE_id_multi_tagged_delete(bmain); } - BKE_reportf(op->reports, RPT_INFO, "Deleted %u object(s)", (changed_count + tagged_count)); + if (confirm) { + BKE_reportf(op->reports, RPT_INFO, "Deleted %u object(s)", (changed_count + tagged_count)); + } /* delete has to handle all open scenes */ BKE_main_id_tag_listbase(&bmain->scenes, LIB_TAG_DOIT, true); From aa46459543e7ff26ad5d2ba7801ad0a0a0bc99fc Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 13 Oct 2021 15:55:54 +0200 Subject: [PATCH 0784/1500] Fix shadow catcher behind transparent object on GPU The assumption about absent shadow path was wrong. The rest of the changes are to ensure shadow paths are finished prior to the split, so that they write to the proper passes. The issue was caught by running regression tests on OptiX. Differential Revision: https://developer.blender.org/D12857 --- intern/cycles/integrator/path_trace_work_gpu.cpp | 8 ++++++-- .../integrator/integrator_intersect_closest.h | 14 ++++++++------ .../kernel/integrator/integrator_state_util.h | 6 ------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index df393bac0de..02830a00405 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -342,10 +342,14 @@ bool PathTraceWorkGPU::enqueue_path_iteration() } /* Finish shadows before potentially adding more shadow rays. We can only - * store one shadow ray in the integrator state. */ + * store one shadow ray in the integrator state. + * + * When there is a shadow catcher in the scene finish shadow rays before invoking interesect + * closest kernel since so that the shadow paths are writing to the pre-split state. */ if (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE || - kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME) { + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME || + (has_shadow_catcher() && kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST)) { if (queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW]) { enqueue_path_iteration(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); return true; diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/integrator_intersect_closest.h index 579a9c4d200..cd9af1c62fc 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_closest.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_closest.h @@ -126,12 +126,7 @@ ccl_device_forceinline void integrator_intersect_shader_next_kernel( if (kernel_data.film.pass_background != PASS_UNUSED && !kernel_data.background.transparent) { INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_SHADOW_CATCHER_BACKGROUND; - if (use_raytrace_kernel) { - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); - } - else { - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); - } + INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); } else if (use_raytrace_kernel) { INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); @@ -139,6 +134,13 @@ ccl_device_forceinline void integrator_intersect_shader_next_kernel( else { INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } + + /* If the split happened after bounce through a transparent object it's possible to have shadow + * patch. Make sure it is properly re-scheduled on the split path. */ + const int shadow_kernel = INTEGRATOR_STATE(shadow_path, queued_kernel); + if (shadow_kernel != 0) { + INTEGRATOR_SHADOW_PATH_INIT(shadow_kernel); + } } #endif } diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index 01d596b690a..037c7533943 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -262,12 +262,6 @@ ccl_device_inline void integrator_state_shadow_catcher_split(INTEGRATOR_STATE_AR integrator_state_copy_only(to_state, state); kernel_integrator_state.path.flag[to_state] |= PATH_RAY_SHADOW_CATCHER_PASS; - - /* Sanity check: expect to split in the intersect-closest kernel, where there is no shadow ray - * and no sorting yet. */ - kernel_assert(INTEGRATOR_STATE(shadow_path, queued_kernel) == 0); - kernel_assert(kernel_integrator_state.sort_key_counter[INTEGRATOR_STATE(path, queued_kernel)] == - nullptr); #else IntegratorStateCPU *ccl_restrict split_state = state + 1; From 583939c54deadbb224f77412c09b58a86e66882c Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 14 Oct 2021 10:54:40 +0200 Subject: [PATCH 0785/1500] Fix T92200: VSE: 2D Cursor position missing viewport update Was using notifier from wrong space (copy-paste error in rBd04d27b406b8). --- source/blender/makesrna/intern/rna_space.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 8a2f48ba991..8f9d4addd30 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5717,7 +5717,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "cursor"); RNA_def_property_array(prop, 2); RNA_def_property_ui_text(prop, "2D Cursor Location", "2D cursor location for this view"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } static void rna_def_space_text(BlenderRNA *brna) From 42d79a6041a7e45efd9eee4613994bdb6090e028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 14 Oct 2021 11:27:30 +0200 Subject: [PATCH 0786/1500] Fix T91398 Overlay: Camera BG jitter offset (regression) This was caused by camera background being rendered in world space, causing floating point imprecision issues when camera was far from origin. Adding a uniform to change vertex shader to process everything in viewspace to fix the problem. --- source/blender/draw/engines/overlay/overlay_image.c | 3 ++- .../draw/engines/overlay/shaders/image_vert.glsl | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/engines/overlay/overlay_image.c b/source/blender/draw/engines/overlay/overlay_image.c index ada07499d1c..2e9a9bc5c64 100644 --- a/source/blender/draw/engines/overlay/overlay_image.c +++ b/source/blender/draw/engines/overlay/overlay_image.c @@ -344,7 +344,6 @@ void OVERLAY_image_camera_cache_populate(OVERLAY_Data *vedata, Object *ob) if (tex) { image_camera_background_matrix_get(cam, bgpic, draw_ctx, aspect, mat); - mul_m4_m4m4(mat, modelmat, mat); const bool is_foreground = (bgpic->flag & CAM_BGIMG_FLAG_FOREGROUND) != 0; /* Alpha is clamped just below 1.0 to fix background images to interfere with foreground * images. Without this a background image with 1.0 will be rendered on top of a transparent @@ -361,6 +360,7 @@ void OVERLAY_image_camera_cache_populate(OVERLAY_Data *vedata, Object *ob) DRW_shgroup_uniform_texture(grp, "imgTexture", tex); DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", use_alpha_premult); DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", true); + DRW_shgroup_uniform_bool_copy(grp, "isCameraBackground", true); DRW_shgroup_uniform_bool_copy(grp, "depthSet", true); DRW_shgroup_uniform_vec4_copy(grp, "color", color_premult_alpha); DRW_shgroup_call_obmat(grp, DRW_cache_quad_get(), mat); @@ -446,6 +446,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob) DRW_shgroup_uniform_texture(grp, "imgTexture", tex); DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", use_alpha_premult); DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", use_alpha_blend); + DRW_shgroup_uniform_bool_copy(grp, "isCameraBackground", false); DRW_shgroup_uniform_bool_copy(grp, "depthSet", depth_mode != OB_EMPTY_IMAGE_DEPTH_DEFAULT); DRW_shgroup_uniform_vec4_copy(grp, "color", ob->color); DRW_shgroup_call_obmat(grp, DRW_cache_quad_get(), mat); diff --git a/source/blender/draw/engines/overlay/shaders/image_vert.glsl b/source/blender/draw/engines/overlay/shaders/image_vert.glsl index 621e1d8068b..a44ea7081ba 100644 --- a/source/blender/draw/engines/overlay/shaders/image_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/image_vert.glsl @@ -1,5 +1,6 @@ uniform bool depthSet; +uniform bool isCameraBackground; in vec3 pos; @@ -7,8 +8,14 @@ out vec2 uvs; void main() { - vec3 world_pos = point_object_to_world(pos); - gl_Position = point_world_to_ndc(world_pos); + if (isCameraBackground) { + vec3 vP = (ModelMatrix * vec4(pos, 1.0)).xyz; + gl_Position = point_view_to_ndc(vP); + } + else { + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + } if (depthSet) { /* Result in a position at 1.0 (far plane). Small epsilon to avoid precision issue. From 56b35991bcdf8e8466e6e8b686fa2ebc49173114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 14 Oct 2021 12:55:22 +0200 Subject: [PATCH 0787/1500] Alembic: avoid crash when Cycles is not enabled The Alembic/CacheFile modifier supports Cycles procedurals when Cycles is configured to use experimental features; the check for this would segfault on builds with `WITH_CYCLES=OFF`. This is now fixed by adding an extra NULL check. --- source/blender/blenkernel/intern/scene.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index a9a8cd93b1d..397bd430fd9 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -2787,6 +2787,12 @@ bool BKE_scene_uses_cycles_experimental_features(Scene *scene) PointerRNA scene_ptr; RNA_id_pointer_create(&scene->id, &scene_ptr); PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles"); + + if (RNA_pointer_is_null(&cycles_ptr)) { + /* The pointer only exists if Cycles is enabled. */ + return false; + } + return RNA_enum_get(&cycles_ptr, "feature_set") == CYCLES_FEATURES_EXPERIMENTAL; } From 9ca567bc4e99403c2574f823922933da5cbc20ec Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Thu, 14 Oct 2021 06:41:52 -0500 Subject: [PATCH 0788/1500] Geometry Nodes: Reorganize Add Menu - Move Converters to 'From' menus - Create Instances Menu - Realphabetize the Curve Menu Differential Revision: https://developer.blender.org/D12860 --- release/scripts/startup/nodeitems_builtins.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 784a7b1eb56..1e662712f7e 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -101,15 +101,15 @@ def curve_node_items(context): yield NodeItem("GeometryNodeLegacyCurveSplineType") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeCurveLength") + yield NodeItem("GeometryNodeCurveToMesh") yield NodeItem("GeometryNodeCurveFill") yield NodeItem("GeometryNodeCurveFillet") - yield NodeItem("GeometryNodeCurveLength") + yield NodeItem("GeometryNodeCurveResample") yield NodeItem("GeometryNodeCurveReverse") yield NodeItem("GeometryNodeCurveSample") yield NodeItem("GeometryNodeCurveSubdivide") - yield NodeItem("GeometryNodeCurveToMesh") yield NodeItem("GeometryNodeCurveTrim") - yield NodeItem("GeometryNodeCurveResample") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeInputCurveHandlePositions") yield NodeItem("GeometryNodeCurveParameter") @@ -128,7 +128,7 @@ def curve_node_items(context): yield NodeItem("GeometryNodeSetSplineResolution") yield NodeItem("GeometryNodeCurveSplineType") -# Custom Menu for Geometry Node Curves +# Custom Menu for Geometry Node Mesh def mesh_node_items(context): if context is None: return @@ -145,8 +145,8 @@ def mesh_node_items(context): yield NodeItem("GeometryNodeEdgeSplit") yield NodeItem("GeometryNodeBoolean") + yield NodeItem("GeometryNodeMeshToPoints") yield NodeItem("GeometryNodeMeshSubdivide") - yield NodeItem("GeometryNodePointsToVertices") yield NodeItem("GeometryNodeTriangulate") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeInputShadeSmooth") @@ -181,7 +181,7 @@ def geometry_input_node_items(context): yield NodeItem("GeometryNodeInputPosition") yield NodeItem("GeometryNodeInputRadius") -# Custom Menu for Material Node Input Nodes +# Custom Menu for Material Nodes def geometry_material_node_items(context): if context is None: return @@ -204,7 +204,7 @@ def geometry_material_node_items(context): yield NodeItem("GeometryNodeSetMaterial") yield NodeItem("GeometryNodeSetMaterialIndex") -# Custom Menu for Geometry Node Curves +# Custom Menu for Geometry Node Points def point_node_items(context): if context is None: return @@ -225,8 +225,8 @@ def point_node_items(context): yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeDistributePointsOnFaces") - yield NodeItem("GeometryNodeInstanceOnPoints") - yield NodeItem("GeometryNodeMeshToPoints") + yield NodeItem("GeometryNodePointsToVertices") + yield NodeItem("GeometryNodePointsToVolume") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeSetPointRadius") @@ -683,12 +683,15 @@ geometry_node_categories = [ NodeItem("GeometryNodeSeparateComponents"), NodeItem("GeometryNodeSeparateGeometry"), NodeItem("GeometryNodeSetPosition"), + ]), + GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items), + GeometryNodeCategory("GEO_INSTANCE", "Instances", items=[ + NodeItem("GeometryNodeInstanceOnPoints"), NodeItem("GeometryNodeRealizeInstances"), NodeItem("GeometryNodeRotateInstances"), NodeItem("GeometryNodeScaleInstances"), NodeItem("GeometryNodeTranslateInstances"), ]), - GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items), GeometryNodeCategory("GEO_MATERIAL", "Material", items=geometry_material_node_items), GeometryNodeCategory("GEO_MESH", "Mesh", items=mesh_node_items), GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[ @@ -739,7 +742,6 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodePointsToVolume"), NodeItem("GeometryNodeVolumeToMesh"), ]), GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items), From 240345842dd1594709433df1ff3448c55968254d Mon Sep 17 00:00:00 2001 From: Paul Golter Date: Thu, 14 Oct 2021 14:40:48 +0200 Subject: [PATCH 0789/1500] Filebrowser: Expose file select functions for File Browser to Python API. This patch adds activate_file_by_relative_path(relative_path="") and deselect_all() function to the space api of the File Browser. While the first sets the active file and adds it to the selection based on a relative path to the current File Browser directory the second one deselects all files but does not change the active file. Differential Revision: https://developer.blender.org/D12826 Reviewed by: Julian Eisel --- .../blender/editors/include/ED_fileselect.h | 3 ++ source/blender/editors/space_file/filesel.c | 37 +++++++++++++++++++ .../blender/makesrna/intern/rna_space_api.c | 12 ++++++ 3 files changed, 52 insertions(+) diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 423d619f41a..3beabaf2d1d 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -151,6 +151,9 @@ void ED_fileselect_activate_by_id(struct SpaceFile *sfile, struct ID *asset_id, const bool deferred); +void ED_fileselect_deselect_all(struct SpaceFile *sfile); +void ED_fileselect_activate_by_relpath(struct SpaceFile *sfile, const char *relative_path); + void ED_fileselect_window_params_get(const struct wmWindow *win, int win_size[2], bool *is_maximized); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 83b33fe8aa9..fc9fc6799e1 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -528,6 +528,43 @@ void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool def WM_main_add_notifier(NC_ASSET | NA_SELECTED, NULL); } +static void on_reload_select_by_relpath(SpaceFile *sfile, onReloadFnData custom_data) +{ + char *relative_path = (char *)custom_data; + ED_fileselect_activate_by_relpath(sfile, relative_path); +} + +void ED_fileselect_activate_by_relpath(SpaceFile *sfile, const char *relative_path) +{ + /* If there are filelist operations running now ("pending" true) or soon ("force reset" true), + * there is a fair chance that the to-be-activated file at relative_path will only be present + * after these operations have completed. Defer activation until then. */ + struct FileList *files = sfile->files; + if (files == NULL || filelist_pending(files) || filelist_needs_force_reset(files)) { + file_on_reload_callback_register(sfile, on_reload_select_by_relpath, relative_path); + return; + } + + FileSelectParams *params = ED_fileselect_get_active_params(sfile); + const int num_files_filtered = filelist_files_ensure(files); + + for (int file_index = 0; file_index < num_files_filtered; ++file_index) { + const FileDirEntry *file = filelist_file(files, file_index); + + if (STREQ(file->relpath, relative_path)) { + params->active_file = file_index; + filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + } + } + WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); +} + +void ED_fileselect_deselect_all(SpaceFile *sfile) +{ + file_select_deselect_all(sfile, FILE_SEL_SELECTED); + WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); +} + /* The subset of FileSelectParams.flag items we store into preferences. Note that FILE_SORT_ALPHA * may also be remembered, but only conditionally. */ #define PARAMS_FLAGS_REMEMBERED (FILE_HIDE_DOT) diff --git a/source/blender/makesrna/intern/rna_space_api.c b/source/blender/makesrna/intern/rna_space_api.c index 205a88edf84..295ecf590bb 100644 --- a/source/blender/makesrna/intern/rna_space_api.c +++ b/source/blender/makesrna/intern/rna_space_api.c @@ -134,6 +134,18 @@ void RNA_api_space_filebrowser(StructRNA *srna) 0, "", "Whether to activate the ID immediately (false) or after the file browser refreshes (true)"); + + /* Select file by relative path. */ + func = RNA_def_function( + srna, "activate_file_by_relative_path", "ED_fileselect_activate_by_relpath"); + RNA_def_function_ui_description(func, + "Set active file and add to selection based on relative path to " + "current File Browser directory"); + RNA_def_property(func, "relative_path", PROP_STRING, PROP_FILEPATH); + + /* Deselect all files. */ + func = RNA_def_function(srna, "deselect_all", "ED_fileselect_deselect_all"); + RNA_def_function_ui_description(func, "Deselect all files"); } #endif From 138aa209592ef3c2232007a9723659116d986d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 14 Oct 2021 15:16:02 +0200 Subject: [PATCH 0790/1500] Cleanup: asset browser, remove invalid assertion Remove `BLI_assert_unreachable()` in a spot that is actually easily reachable. To reach, follow these steps: - Configure three asset libraries (say A, B, and C) in preferences - Set the asset browser to library C and save the file - Remove asset library C from the preferences - Reopen the file. --- .../blender/editors/asset/intern/asset_library_reference_enum.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc index c7c0f35c12d..b74fe40ec2e 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -55,7 +55,6 @@ int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *librar return ASSET_LIBRARY_CUSTOM + library->custom_library_index; } - BLI_assert_unreachable(); return ASSET_LIBRARY_LOCAL; } From 25a255c32a73b9d964d9f8a4733c4a42dfdc2f4f Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Thu, 14 Oct 2021 14:18:24 +0100 Subject: [PATCH 0791/1500] Nodes: add boilerplate for image sockets The sockets are not exposed in any nodes yet. They work similar to the Object/Collection/Texture sockets, which also just reference a data block. Based on rB207472930834 Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12861 --- .../blender/editors/space_node/node_draw.cc | 3 +++ source/blender/modifiers/intern/MOD_nodes.cc | 24 +++++++++++++++++++ .../blender/nodes/NOD_socket_declarations.hh | 11 +++++++++ .../nodes/geometry/node_geometry_tree.cc | 1 + source/blender/nodes/intern/node_socket.cc | 17 +++++++++++-- source/blender/nodes/intern/node_util.c | 8 +++++++ 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 97e5bdd93c1..4e879d6c2f7 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -916,6 +916,9 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal else if (type.is()) { id_to_inspection_string((ID *)*value.get(), ID_TE); } + else if (type.is()) { + id_to_inspection_string((ID *)*value.get(), ID_IM); + } else if (type.is()) { id_to_inspection_string((ID *)*value.get(), ID_GR); } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index d294491d51c..ebccbb3d2cd 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -151,6 +151,12 @@ static void addIdsUsedBySocket(const ListBase *sockets, Set &ids) ids.add(&texture->id); } } + else if (socket->type == SOCK_IMAGE) { + Image *image = ((bNodeSocketValueImage *)socket->default_value)->value; + if (image != nullptr) { + ids.add(&image->id); + } + } } } @@ -236,6 +242,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte add_collection_relation(ctx, *collection); break; } + case ID_IM: case ID_TE: { DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier"); } @@ -420,6 +427,12 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) idprop.id = (ID *)value->value; return IDP_New(IDP_ID, &idprop, socket.identifier); } + case SOCK_IMAGE: { + bNodeSocketValueImage *value = (bNodeSocketValueImage *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.id = (ID *)value->value; + return IDP_New(IDP_ID, &idprop, socket.identifier); + } case SOCK_MATERIAL: { bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value; IDPropertyTemplate idprop = {0}; @@ -448,6 +461,7 @@ static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDP case SOCK_OBJECT: case SOCK_COLLECTION: case SOCK_TEXTURE: + case SOCK_IMAGE: case SOCK_MATERIAL: return property.type == IDP_ID; } @@ -517,6 +531,12 @@ static void init_socket_cpp_value_from_property(const IDProperty &property, *(Tex **)r_value = texture; break; } + case SOCK_IMAGE: { + ID *id = IDP_Id(&property); + Image *image = (id && GS(id->name) == ID_IM) ? (Image *)id : nullptr; + *(Image **)r_value = image; + break; + } case SOCK_MATERIAL: { ID *id = IDP_Id(&property); Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr; @@ -1145,6 +1165,10 @@ static void draw_property_for_socket(uiLayout *layout, uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE); break; } + case SOCK_IMAGE: { + uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "images", socket.name, ICON_IMAGE); + break; + } default: { if (input_has_attribute_toggle(*nmd->node_group, socket_index)) { const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 00874cad766..e22b96cd1ff 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -193,6 +193,13 @@ class Texture : public IDSocketDeclaration { Texture(); }; +class Image : public IDSocketDeclaration { + public: + using Builder = SocketDeclarationBuilder; + + Image(); +}; + class Geometry : public SocketDeclaration { public: using Builder = SocketDeclarationBuilder; @@ -339,6 +346,10 @@ inline Texture::Texture() : IDSocketDeclaration("NodeSocketTexture") { } +inline Image::Image() : IDSocketDeclaration("NodeSocketImage") +{ +} + /** \} */ } // namespace blender::nodes::decl diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index 20b610a4db9..9747bb63773 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -109,6 +109,7 @@ static bool geometry_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype SOCK_GEOMETRY, SOCK_COLLECTION, SOCK_TEXTURE, + SOCK_IMAGE, SOCK_MATERIAL); } diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 4334f1b5030..11356178d87 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -808,6 +808,7 @@ static bNodeSocketType *make_socket_type_string() MAKE_CPP_TYPE(Object, Object *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Collection, Collection *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Texture, Tex *, CPPTypeFlags::BasicType) +MAKE_CPP_TYPE(Image, Image *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Material, Material *, CPPTypeFlags::BasicType) static bNodeSocketType *make_socket_type_object() @@ -858,6 +859,18 @@ static bNodeSocketType *make_socket_type_texture() return socktype; } +static bNodeSocketType *make_socket_type_image() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_IMAGE, PROP_NONE); + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { + *(Image **)r_value = ((bNodeSocketValueImage *)socket.default_value)->value; + }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + return socktype; +} + static bNodeSocketType *make_socket_type_material() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE); @@ -906,14 +919,14 @@ void register_standard_node_socket_types(void) nodeRegisterSocketType(make_socket_type_object()); - nodeRegisterSocketType(make_standard_socket_type(SOCK_IMAGE, PROP_NONE)); - nodeRegisterSocketType(make_socket_type_geometry()); nodeRegisterSocketType(make_socket_type_collection()); nodeRegisterSocketType(make_socket_type_texture()); + nodeRegisterSocketType(make_socket_type_image()); + nodeRegisterSocketType(make_socket_type_material()); nodeRegisterSocketType(make_socket_type_virtual()); diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 1aec280fd2b..ba0cfeacb83 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -467,6 +467,14 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype return -1; } } + case SOCK_IMAGE: { + switch (from) { + case SOCK_IMAGE: + return 1; + default: + return -1; + } + } case SOCK_MATERIAL: { switch (from) { case SOCK_MATERIAL: From 55cf9bb5e653ef824cf32aae899437525d1f16e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 14 Oct 2021 15:34:22 +0200 Subject: [PATCH 0792/1500] Cleanup: fix const discard warning No functional changes. --- source/blender/editors/space_file/filesel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index fc9fc6799e1..2b4f14451e7 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -530,7 +530,7 @@ void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool def static void on_reload_select_by_relpath(SpaceFile *sfile, onReloadFnData custom_data) { - char *relative_path = (char *)custom_data; + const char *relative_path = custom_data; ED_fileselect_activate_by_relpath(sfile, relative_path); } @@ -541,7 +541,9 @@ void ED_fileselect_activate_by_relpath(SpaceFile *sfile, const char *relative_pa * after these operations have completed. Defer activation until then. */ struct FileList *files = sfile->files; if (files == NULL || filelist_pending(files) || filelist_needs_force_reset(files)) { - file_on_reload_callback_register(sfile, on_reload_select_by_relpath, relative_path); + /* Casting away the constness of `relative_path` is safe here, because eventually it just ends + * up in another call to this function, and then it's a const char* again. */ + file_on_reload_callback_register(sfile, on_reload_select_by_relpath, (char *)relative_path); return; } From 2341ca990c17df7a77d4527a707f7b9e3d5f0f1a Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Thu, 14 Oct 2021 14:31:34 +0100 Subject: [PATCH 0793/1500] Geometry Nodes: Add White Noise texture Port White Noise shader to geometry nodes. Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12719 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenlib/BLI_noise.hh | 9 ++ source/blender/blenlib/intern/noise.cc | 41 ++++++ .../modifiers/intern/MOD_nodes_evaluator.cc | 1 + .../nodes/node_shader_tex_white_noise.cc | 132 +++++++++++++++++- 5 files changed, 182 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 1e662712f7e..b2faff56656 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -728,6 +728,7 @@ geometry_node_categories = [ ]), GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ NodeItem("ShaderNodeTexNoise"), + NodeItem("ShaderNodeTexWhiteNoise"), ]), GeometryNodeCategory("GEO_VECTOR", "Vector", items=[ NodeItem("ShaderNodeVectorCurve"), diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh index 839bee0f2f4..12e7aa57ab0 100644 --- a/source/blender/blenlib/BLI_noise.hh +++ b/source/blender/blenlib/BLI_noise.hh @@ -53,6 +53,15 @@ float hash_float_to_float(float2 k); float hash_float_to_float(float3 k); float hash_float_to_float(float4 k); +float2 hash_float_to_float2(float2 k); + +float3 hash_float_to_float3(float k); +float3 hash_float_to_float3(float2 k); +float3 hash_float_to_float3(float3 k); +float3 hash_float_to_float3(float4 k); + +float4 hash_float_to_float4(float4 k); + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index 6ed1fae71ad..ce2e9594059 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -240,6 +240,47 @@ float hash_float_to_float(float4 k) return uint_to_float_01(hash_float(k)); } +float2 hash_float_to_float2(float2 k) +{ + return float2(hash_float_to_float(k), hash_float_to_float(float3(k.x, k.y, 1.0))); +} + +float3 hash_float_to_float3(float k) +{ + return float3(hash_float_to_float(k), + hash_float_to_float(float2(k, 1.0)), + hash_float_to_float(float2(k, 2.0))); +} + +float3 hash_float_to_float3(float2 k) +{ + return float3(hash_float_to_float(k), + hash_float_to_float(float3(k.x, k.y, 1.0)), + hash_float_to_float(float3(k.x, k.y, 2.0))); +} + +float3 hash_float_to_float3(float3 k) +{ + return float3(hash_float_to_float(k), + hash_float_to_float(float4(k.x, k.y, k.z, 1.0)), + hash_float_to_float(float4(k.x, k.y, k.z, 2.0))); +} + +float3 hash_float_to_float3(float4 k) +{ + return float3(hash_float_to_float(k), + hash_float_to_float(float4(k.z, k.x, k.w, k.y)), + hash_float_to_float(float4(k.w, k.z, k.y, k.x))); +} + +float4 hash_float_to_float4(float4 k) +{ + return float4(hash_float_to_float(k), + hash_float_to_float(float4(k.w, k.x, k.y, k.z)), + hash_float_to_float(float4(k.z, k.w, k.x, k.y)), + hash_float_to_float(float4(k.y, k.z, k.w, k.x))); +} + /* ------------ * Perlin Noise * ------------ diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index a85fc29430f..d600e708167 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -332,6 +332,7 @@ static void get_socket_value(const SocketRef &socket, void *r_value) if (ELEM(bnode.type, GEO_NODE_SET_POSITION, SH_NODE_TEX_NOISE, + SH_NODE_TEX_WHITE_NOISE, GEO_NODE_MESH_TO_POINTS, GEO_NODE_PROXIMITY)) { new (r_value) Field(bke::AttributeFieldInput::Create("position")); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index 03543e5f7fe..445b201e419 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -19,12 +19,14 @@ #include "../node_shader_util.h" +#include "BLI_noise.hh" + namespace blender::nodes { static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").min(-10000.0f).max(10000.0f); + b.add_input("Vector").min(-10000.0f).max(10000.0f).implicit_field(); b.add_input("W").min(-10000.0f).max(10000.0f); b.add_output("Value"); b.add_output("Color"); @@ -65,15 +67,141 @@ static void node_shader_update_tex_white_noise(bNodeTree *UNUSED(ntree), bNode * nodeSetSocketAvailability(sockW, node->custom1 == 1 || node->custom1 == 4); } +namespace blender::nodes { + +class WhiteNoiseFunction : public fn::MultiFunction { + private: + int dimensions_; + + public: + WhiteNoiseFunction(int dimensions) : dimensions_(dimensions) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + static std::array signatures{ + create_signature(1), + create_signature(2), + create_signature(3), + create_signature(4), + }; + this->set_signature(&signatures[dimensions - 1]); + } + + static fn::MFSignature create_signature(int dimensions) + { + fn::MFSignatureBuilder signature{"WhiteNoise"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + + signature.single_output("Value"); + signature.single_output("Color"); + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4); + + MutableSpan r_value = params.uninitialized_single_output_if_required(param++, + "Value"); + MutableSpan r_color = + params.uninitialized_single_output_if_required(param++, "Color"); + + const bool compute_value = !r_value.is_empty(); + const bool compute_color = !r_color.is_empty(); + + switch (dimensions_) { + case 1: { + const VArray &w = params.readonly_single_input(0, "W"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3(w[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float(w[i]); + } + } + break; + } + case 2: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3(float2(vector[i].x, vector[i].y)); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float(float2(vector[i].x, vector[i].y)); + } + } + break; + } + case 3: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3(vector[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float(vector[i]); + } + } + break; + } + case 4: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &w = params.readonly_single_input(1, "W"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3( + float4(vector[i].x, vector[i].y, vector[i].z, w[i])); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float( + float4(vector[i].x, vector[i].y, vector[i].z, w[i])); + } + } + break; + } + } + } +}; + +static void sh_node_noise_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + builder.construct_and_set_matching_fn((int)node.custom1); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_white_noise(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_WHITE_NOISE, "White Noise Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base( + &ntype, SH_NODE_TEX_WHITE_NOISE, "White Noise Texture", NODE_CLASS_TEXTURE, 0); ntype.declare = blender::nodes::sh_node_tex_white_noise_declare; node_type_init(&ntype, node_shader_init_tex_white_noise); node_type_gpu(&ntype, gpu_shader_tex_white_noise); node_type_update(&ntype, node_shader_update_tex_white_noise); + ntype.build_multi_function = blender::nodes::sh_node_noise_build_multi_function; nodeRegisterType(&ntype); } From 47caeb8c26686e24ea7e694f94fabee44f3d2dca Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 27 Sep 2021 17:52:59 +0200 Subject: [PATCH 0794/1500] Fix T91743: Unify behavior of 'Auto Set Preview Range' This is available in the DopeSheet, GraphEditor, and NLA Editor. Currently: - Dopesheet advertises to take selection into account... -- ...but doesnt - which might be a mistake in rBe3842d1ca4dd - Graph Editor does not mention selection... -- ...and also does not take it into account - NLA does not mention selection... -- ...but takes it into account Now: - make them **all** take selection into account (you can still do a quick 'Select All' prior to get the full range -- better than not being able to set this based on selection) - mention this for all in the tooltip - also reword to 'Set Preview Range to Selected' since using the term 'Auto' impilies this would change on selection change. Maniphest Tasks: T91743 Differential Revision: https://developer.blender.org/D12651 --- source/blender/editors/space_action/action_edit.c | 4 ++-- source/blender/editors/space_graph/graph_view.c | 6 +++--- source/blender/editors/space_nla/nla_edit.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 3e38be243c9..f68446b1cae 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -276,7 +276,7 @@ static int actkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) scene = ac.scene; /* set the range directly */ - get_keyframe_extents(&ac, &min, &max, false); + get_keyframe_extents(&ac, &min, &max, true); scene->r.flag |= SCER_PRV_RANGE; scene->r.psfra = floorf(min); scene->r.pefra = ceilf(max); @@ -295,7 +295,7 @@ static int actkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) void ACTION_OT_previewrange_set(wmOperatorType *ot) { /* identifiers */ - ot->name = "Auto-Set Preview Range"; + ot->name = "Set Preview Range to Selected"; ot->idname = "ACTION_OT_previewrange_set"; ot->description = "Set Preview Range based on extents of selected Keyframes"; diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c index c38c5f09a2a..56649c50cfd 100644 --- a/source/blender/editors/space_graph/graph_view.c +++ b/source/blender/editors/space_graph/graph_view.c @@ -213,7 +213,7 @@ static int graphkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) scene = ac.scene; /* Set the range directly. */ - get_graph_keyframe_extents(&ac, &min, &max, NULL, NULL, false, false); + get_graph_keyframe_extents(&ac, &min, &max, NULL, NULL, true, false); scene->r.flag |= SCER_PRV_RANGE; scene->r.psfra = round_fl_to_int(min); scene->r.pefra = round_fl_to_int(max); @@ -228,9 +228,9 @@ static int graphkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) void GRAPH_OT_previewrange_set(wmOperatorType *ot) { /* Identifiers */ - ot->name = "Auto-Set Preview Range"; + ot->name = "Set Preview Range to Selected"; ot->idname = "GRAPH_OT_previewrange_set"; - ot->description = "Automatically set Preview Range based on range of keyframes"; + ot->description = "Set Preview Range based on range of selected keyframes"; /* API callbacks */ ot->exec = graphkeys_previewrange_exec; diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index c75b874833a..62f8906d136 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -402,9 +402,9 @@ static int nlaedit_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) void NLA_OT_previewrange_set(wmOperatorType *ot) { /* identifiers */ - ot->name = "Auto-Set Preview Range"; + ot->name = "Set Preview Range to Selected"; ot->idname = "NLA_OT_previewrange_set"; - ot->description = "Automatically set Preview Range based on range of keyframes"; + ot->description = "Set Preview Range based on extends of selected strips"; /* api callbacks */ ot->exec = nlaedit_previewrange_exec; From a0f269f682dab848afc80cd322d04a0c4a815cae Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Thu, 14 Oct 2021 13:53:40 +0100 Subject: [PATCH 0795/1500] Cycles: Kernel address space changes for MSL This is the first of a sequence of changes to support compiling Cycles kernels as MSL (Metal Shading Language) in preparation for a Metal GPU device implementation. MSL requires that all pointer types be declared with explicit address space attributes (device, thread, etc...). There is already precedent for this with Cycles' address space macros (ccl_global, ccl_private, etc...), therefore the first step of MSL-enablement is to apply these consistently. Line-for-line this represents the largest change required to enable MSL. Applying this change first will simplify future patches as well as offering the emergent benefit of enhanced descriptiveness. The vast majority of deltas in this patch fall into one of two cases: - Ensuring ccl_private is specified for thread-local pointer types - Ensuring ccl_global is specified for device-wide pointer types Additionally, the ccl_addr_space qualifier can be removed. Prior to Cycles X, ccl_addr_space was used as a context-dependent address space qualifier, but now it is either redundant (e.g. in struct typedefs), or can be replaced by ccl_global in the case of pointer types. Associated function variants (e.g. lcg_step_float_addrspace) are also redundant. In cases where address space qualifiers are chained with "const", this patch places the address space qualifier first. The rationale for this is that the choice of address space is likely to have the greater impact on runtime performance and overall architecture. The final part of this patch is the addition of a metal/compat.h header. This is partially complete and will be extended in future patches, paving the way for the full Metal implementation. Ref T92212 Reviewed By: brecht Maniphest Tasks: T92212 Differential Revision: https://developer.blender.org/D12864 --- intern/cycles/kernel/bvh/bvh.h | 36 +-- intern/cycles/kernel/bvh/bvh_local.h | 16 +- intern/cycles/kernel/bvh/bvh_nodes.h | 10 +- intern/cycles/kernel/bvh/bvh_shadow_all.h | 18 +- intern/cycles/kernel/bvh/bvh_traversal.h | 12 +- intern/cycles/kernel/bvh/bvh_util.h | 23 +- intern/cycles/kernel/bvh/bvh_volume.h | 12 +- intern/cycles/kernel/bvh/bvh_volume_all.h | 8 +- intern/cycles/kernel/closure/alloc.h | 17 +- intern/cycles/kernel/closure/bsdf.h | 36 +-- .../kernel/closure/bsdf_ashikhmin_shirley.h | 43 ++-- .../kernel/closure/bsdf_ashikhmin_velvet.h | 28 +-- intern/cycles/kernel/closure/bsdf_diffuse.h | 54 ++--- .../cycles/kernel/closure/bsdf_diffuse_ramp.h | 26 +-- intern/cycles/kernel/closure/bsdf_hair.h | 54 ++--- .../kernel/closure/bsdf_hair_principled.h | 58 ++--- .../cycles/kernel/closure/bsdf_microfacet.h | 114 ++++----- .../kernel/closure/bsdf_microfacet_multi.h | 107 +++++---- .../closure/bsdf_microfacet_multi_impl.h | 22 +- .../cycles/kernel/closure/bsdf_oren_nayar.h | 32 +-- .../cycles/kernel/closure/bsdf_phong_ramp.h | 30 +-- .../kernel/closure/bsdf_principled_diffuse.h | 41 ++-- .../kernel/closure/bsdf_principled_sheen.h | 31 +-- .../cycles/kernel/closure/bsdf_reflection.h | 24 +- .../cycles/kernel/closure/bsdf_refraction.h | 24 +- intern/cycles/kernel/closure/bsdf_toon.h | 54 ++--- .../cycles/kernel/closure/bsdf_transparent.h | 28 +-- intern/cycles/kernel/closure/bsdf_util.h | 14 +- intern/cycles/kernel/closure/bssrdf.h | 35 ++- intern/cycles/kernel/closure/emissive.h | 11 +- intern/cycles/kernel/closure/volume.h | 47 ++-- intern/cycles/kernel/device/cpu/compat.h | 2 - intern/cycles/kernel/device/cuda/compat.h | 1 - intern/cycles/kernel/device/hip/compat.h | 1 - intern/cycles/kernel/device/metal/compat.h | 126 ++++++++++ intern/cycles/kernel/device/optix/compat.h | 1 - intern/cycles/kernel/geom/geom_attribute.h | 16 +- intern/cycles/kernel/geom/geom_curve.h | 53 +++-- .../cycles/kernel/geom/geom_curve_intersect.h | 32 +-- intern/cycles/kernel/geom/geom_motion_curve.h | 19 +- .../cycles/kernel/geom/geom_motion_triangle.h | 19 +- .../geom/geom_motion_triangle_intersect.h | 20 +- .../kernel/geom/geom_motion_triangle_shader.h | 4 +- intern/cycles/kernel/geom/geom_object.h | 185 ++++++++------- intern/cycles/kernel/geom/geom_patch.h | 68 +++--- intern/cycles/kernel/geom/geom_primitive.h | 62 ++--- intern/cycles/kernel/geom/geom_shader_data.h | 31 +-- .../cycles/kernel/geom/geom_subd_triangle.h | 48 ++-- intern/cycles/kernel/geom/geom_triangle.h | 63 ++--- .../kernel/geom/geom_triangle_intersect.h | 20 +- intern/cycles/kernel/geom/geom_volume.h | 8 +- .../integrator/integrator_init_from_bake.h | 2 +- .../integrator/integrator_init_from_camera.h | 6 +- .../integrator/integrator_intersect_closest.h | 2 +- .../integrator/integrator_intersect_shadow.h | 4 +- .../integrator_intersect_volume_stack.h | 4 +- .../integrator/integrator_shade_background.h | 4 +- .../integrator/integrator_shade_light.h | 2 +- .../integrator/integrator_shade_shadow.h | 7 +- .../integrator/integrator_shade_surface.h | 39 ++-- .../integrator/integrator_shade_volume.h | 108 ++++----- .../kernel/integrator/integrator_state.h | 13 +- .../kernel/integrator/integrator_state_util.h | 25 +- .../kernel/integrator/integrator_subsurface.h | 13 +- .../integrator/integrator_subsurface_disk.h | 4 +- .../integrator_subsurface_random_walk.h | 19 +- .../integrator/integrator_volume_stack.h | 7 +- intern/cycles/kernel/kernel_accumulate.h | 20 +- .../cycles/kernel/kernel_adaptive_sampling.h | 6 +- intern/cycles/kernel/kernel_bake.h | 4 +- intern/cycles/kernel/kernel_camera.h | 30 +-- intern/cycles/kernel/kernel_color.h | 4 +- intern/cycles/kernel/kernel_differential.h | 12 +- intern/cycles/kernel/kernel_emission.h | 56 ++--- intern/cycles/kernel/kernel_film.h | 117 +++++----- intern/cycles/kernel/kernel_id_passes.h | 2 +- intern/cycles/kernel/kernel_jitter.h | 13 +- intern/cycles/kernel/kernel_light.h | 72 +++--- .../cycles/kernel/kernel_light_background.h | 41 ++-- intern/cycles/kernel/kernel_light_common.h | 13 +- intern/cycles/kernel/kernel_lookup_table.h | 7 +- intern/cycles/kernel/kernel_montecarlo.h | 19 +- intern/cycles/kernel/kernel_passes.h | 18 +- intern/cycles/kernel/kernel_path_state.h | 39 ++-- intern/cycles/kernel/kernel_projection.h | 4 +- intern/cycles/kernel/kernel_random.h | 21 +- intern/cycles/kernel/kernel_shader.h | 216 ++++++++++-------- intern/cycles/kernel/kernel_types.h | 17 +- intern/cycles/kernel/svm/svm.h | 39 ++-- intern/cycles/kernel/svm/svm_ao.h | 7 +- intern/cycles/kernel/svm/svm_aov.h | 8 +- intern/cycles/kernel/svm/svm_attribute.h | 26 +-- intern/cycles/kernel/svm/svm_bevel.h | 12 +- intern/cycles/kernel/svm/svm_blackbody.h | 6 +- intern/cycles/kernel/svm/svm_brick.h | 7 +- intern/cycles/kernel/svm/svm_brightness.h | 2 +- intern/cycles/kernel/svm/svm_bump.h | 12 +- intern/cycles/kernel/svm/svm_camera.h | 6 +- intern/cycles/kernel/svm/svm_checker.h | 6 +- intern/cycles/kernel/svm/svm_clamp.h | 6 +- intern/cycles/kernel/svm/svm_closure.h | 164 ++++++++----- intern/cycles/kernel/svm/svm_convert.h | 8 +- intern/cycles/kernel/svm/svm_displace.h | 25 +- intern/cycles/kernel/svm/svm_fresnel.h | 11 +- intern/cycles/kernel/svm/svm_gamma.h | 7 +- intern/cycles/kernel/svm/svm_geometry.h | 42 +++- intern/cycles/kernel/svm/svm_gradient.h | 4 +- intern/cycles/kernel/svm/svm_hsv.h | 6 +- intern/cycles/kernel/svm/svm_ies.h | 10 +- intern/cycles/kernel/svm/svm_image.h | 22 +- intern/cycles/kernel/svm/svm_invert.h | 7 +- intern/cycles/kernel/svm/svm_light_path.h | 8 +- intern/cycles/kernel/svm/svm_magic.h | 7 +- intern/cycles/kernel/svm/svm_map_range.h | 6 +- intern/cycles/kernel/svm/svm_mapping.h | 18 +- intern/cycles/kernel/svm/svm_math.h | 12 +- intern/cycles/kernel/svm/svm_math_util.h | 4 +- intern/cycles/kernel/svm/svm_mix.h | 6 +- intern/cycles/kernel/svm/svm_musgrave.h | 6 +- intern/cycles/kernel/svm/svm_noisetex.h | 22 +- intern/cycles/kernel/svm/svm_normal.h | 6 +- intern/cycles/kernel/svm/svm_ramp.h | 27 ++- intern/cycles/kernel/svm/svm_sepcomb_hsv.h | 12 +- intern/cycles/kernel/svm/svm_sepcomb_vector.h | 14 +- intern/cycles/kernel/svm/svm_sky.h | 31 +-- intern/cycles/kernel/svm/svm_tex_coord.h | 36 ++- intern/cycles/kernel/svm/svm_value.h | 14 +- intern/cycles/kernel/svm/svm_vector_rotate.h | 4 +- .../cycles/kernel/svm/svm_vector_transform.h | 6 +- intern/cycles/kernel/svm/svm_vertex_color.h | 18 +- intern/cycles/kernel/svm/svm_voronoi.h | 108 +++++---- intern/cycles/kernel/svm/svm_voxel.h | 7 +- intern/cycles/kernel/svm/svm_wave.h | 7 +- intern/cycles/kernel/svm/svm_wavelength.h | 7 +- intern/cycles/kernel/svm/svm_white_noise.h | 6 +- intern/cycles/kernel/svm/svm_wireframe.h | 13 +- intern/cycles/util/util_color.h | 2 +- intern/cycles/util/util_half.h | 4 +- intern/cycles/util/util_math.h | 10 +- intern/cycles/util/util_math_fast.h | 2 +- intern/cycles/util/util_math_float2.h | 2 +- intern/cycles/util/util_math_float3.h | 4 +- intern/cycles/util/util_math_float4.h | 2 +- intern/cycles/util/util_math_intersect.h | 22 +- intern/cycles/util/util_math_matrix.h | 34 +-- intern/cycles/util/util_projection.h | 5 +- intern/cycles/util/util_rect.h | 5 +- intern/cycles/util/util_transform.h | 20 +- 148 files changed, 2146 insertions(+), 1648 deletions(-) create mode 100644 intern/cycles/kernel/device/metal/compat.h diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index 0b44cc5db34..8f6dcd0adb9 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -139,7 +139,7 @@ CCL_NAMESPACE_BEGIN #endif /* __KERNEL_OPTIX__ */ -ccl_device_inline bool scene_intersect_valid(const Ray *ray) +ccl_device_inline bool scene_intersect_valid(ccl_private const Ray *ray) { /* NOTE: Due to some vectorization code non-finite origin point might * cause lots of false-positive intersections which will overflow traversal @@ -154,10 +154,10 @@ ccl_device_inline bool scene_intersect_valid(const Ray *ray) return isfinite_safe(ray->P.x) && isfinite_safe(ray->D.x) && len_squared(ray->D) != 0.0f; } -ccl_device_intersect bool scene_intersect(const KernelGlobals *kg, - const Ray *ray, +ccl_device_intersect bool scene_intersect(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, const uint visibility, - Intersection *isect) + ccl_private Intersection *isect) { #ifdef __KERNEL_OPTIX__ uint p0 = 0; @@ -248,11 +248,11 @@ ccl_device_intersect bool scene_intersect(const KernelGlobals *kg, } #ifdef __BVH_LOCAL__ -ccl_device_intersect bool scene_intersect_local(const KernelGlobals *kg, - const Ray *ray, - LocalIntersection *local_isect, +ccl_device_intersect bool scene_intersect_local(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private LocalIntersection *local_isect, int local_object, - uint *lcg_state, + ccl_private uint *lcg_state, int max_hits) { # ifdef __KERNEL_OPTIX__ @@ -360,12 +360,12 @@ ccl_device_intersect bool scene_intersect_local(const KernelGlobals *kg, #endif #ifdef __SHADOW_RECORD_ALL__ -ccl_device_intersect bool scene_intersect_shadow_all(const KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_intersect bool scene_intersect_shadow_all(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, uint visibility, uint max_hits, - uint *num_hits) + ccl_private uint *num_hits) { # ifdef __KERNEL_OPTIX__ uint p0 = ((uint64_t)isect) & 0xFFFFFFFF; @@ -445,9 +445,9 @@ ccl_device_intersect bool scene_intersect_shadow_all(const KernelGlobals *kg, #endif /* __SHADOW_RECORD_ALL__ */ #ifdef __VOLUME__ -ccl_device_intersect bool scene_intersect_volume(const KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_intersect bool scene_intersect_volume(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint visibility) { # ifdef __KERNEL_OPTIX__ @@ -507,9 +507,9 @@ ccl_device_intersect bool scene_intersect_volume(const KernelGlobals *kg, #endif /* __VOLUME__ */ #ifdef __VOLUME_RECORD_ALL__ -ccl_device_intersect uint scene_intersect_volume_all(const KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_intersect uint scene_intersect_volume_all(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint max_hits, const uint visibility) { diff --git a/intern/cycles/kernel/bvh/bvh_local.h b/intern/cycles/kernel/bvh/bvh_local.h index 90b9f410b29..78ad4a34da9 100644 --- a/intern/cycles/kernel/bvh/bvh_local.h +++ b/intern/cycles/kernel/bvh/bvh_local.h @@ -36,11 +36,11 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, - const Ray *ray, - LocalIntersection *local_isect, + bool BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private LocalIntersection *local_isect, int local_object, - uint *lcg_state, + ccl_private uint *lcg_state, int max_hits) { /* todo: @@ -196,11 +196,11 @@ ccl_device_inline return false; } -ccl_device_inline bool BVH_FUNCTION_NAME(const KernelGlobals *kg, - const Ray *ray, - LocalIntersection *local_isect, +ccl_device_inline bool BVH_FUNCTION_NAME(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private LocalIntersection *local_isect, int local_object, - uint *lcg_state, + ccl_private uint *lcg_state, int max_hits) { return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, local_isect, local_object, lcg_state, max_hits); diff --git a/intern/cycles/kernel/bvh/bvh_nodes.h b/intern/cycles/kernel/bvh/bvh_nodes.h index 15cd0f22213..49b37f39671 100644 --- a/intern/cycles/kernel/bvh/bvh_nodes.h +++ b/intern/cycles/kernel/bvh/bvh_nodes.h @@ -16,7 +16,7 @@ // TODO(sergey): Look into avoid use of full Transform and use 3x3 matrix and // 3-vector which might be faster. -ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(const KernelGlobals *kg, +ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(ccl_global const KernelGlobals *kg, int node_addr, int child) { @@ -28,7 +28,7 @@ ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(const KernelGlob return space; } -ccl_device_forceinline int bvh_aligned_node_intersect(const KernelGlobals *kg, +ccl_device_forceinline int bvh_aligned_node_intersect(ccl_global const KernelGlobals *kg, const float3 P, const float3 idir, const float t, @@ -76,7 +76,7 @@ ccl_device_forceinline int bvh_aligned_node_intersect(const KernelGlobals *kg, #endif } -ccl_device_forceinline bool bvh_unaligned_node_intersect_child(const KernelGlobals *kg, +ccl_device_forceinline bool bvh_unaligned_node_intersect_child(ccl_global const KernelGlobals *kg, const float3 P, const float3 dir, const float t, @@ -102,7 +102,7 @@ ccl_device_forceinline bool bvh_unaligned_node_intersect_child(const KernelGloba return tnear <= tfar; } -ccl_device_forceinline int bvh_unaligned_node_intersect(const KernelGlobals *kg, +ccl_device_forceinline int bvh_unaligned_node_intersect(ccl_global const KernelGlobals *kg, const float3 P, const float3 dir, const float3 idir, @@ -134,7 +134,7 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(const KernelGlobals *kg, return mask; } -ccl_device_forceinline int bvh_node_intersect(const KernelGlobals *kg, +ccl_device_forceinline int bvh_node_intersect(ccl_global const KernelGlobals *kg, const float3 P, const float3 dir, const float3 idir, diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index 82c7c1a8a6c..c67c820edbc 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -36,12 +36,12 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, - const Ray *ray, - Intersection *isect_array, + bool BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect_array, const uint visibility, const uint max_hits, - uint *num_hits) + ccl_private uint *num_hits) { /* todo: * - likely and unlikely for if() statements @@ -71,7 +71,7 @@ ccl_device_inline float t_world_to_instance = 1.0f; *num_hits = 0; - Intersection *isect = isect_array; + ccl_private Intersection *isect = isect_array; /* traversal loop */ do { @@ -284,12 +284,12 @@ ccl_device_inline return false; } -ccl_device_inline bool BVH_FUNCTION_NAME(const KernelGlobals *kg, - const Ray *ray, - Intersection *isect_array, +ccl_device_inline bool BVH_FUNCTION_NAME(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect_array, const uint visibility, const uint max_hits, - uint *num_hits) + ccl_private uint *num_hits) { return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect_array, visibility, max_hits, num_hits); } diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/bvh_traversal.h index 2feff593c10..a46c45d3529 100644 --- a/intern/cycles/kernel/bvh/bvh_traversal.h +++ b/intern/cycles/kernel/bvh/bvh_traversal.h @@ -31,9 +31,9 @@ * BVH_MOTION: motion blur rendering */ -ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint visibility) { /* todo: @@ -226,9 +226,9 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, return (isect->prim != PRIM_NONE); } -ccl_device_inline bool BVH_FUNCTION_NAME(const KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_inline bool BVH_FUNCTION_NAME(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint visibility) { return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect, visibility); diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index d143fe4aeab..fb546f568f3 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -88,7 +88,7 @@ ccl_device int intersections_compare(const void *a, const void *b) #endif #if defined(__SHADOW_RECORD_ALL__) -ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits) +ccl_device_inline void sort_intersections(ccl_private Intersection *hits, uint num_hits) { kernel_assert(num_hits > 0); @@ -115,8 +115,8 @@ ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits) /* For subsurface scattering, only sorting a small amount of intersections * so bubble sort is fine for CPU and GPU. */ -ccl_device_inline void sort_intersections_and_normals(Intersection *hits, - float3 *Ng, +ccl_device_inline void sort_intersections_and_normals(ccl_private Intersection *hits, + ccl_private float3 *Ng, uint num_hits) { bool swapped; @@ -139,8 +139,9 @@ ccl_device_inline void sort_intersections_and_normals(Intersection *hits, /* Utility to quickly get flags from an intersection. */ -ccl_device_forceinline int intersection_get_shader_flags(const KernelGlobals *ccl_restrict kg, - const Intersection *ccl_restrict isect) +ccl_device_forceinline int intersection_get_shader_flags( + ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private const Intersection *ccl_restrict isect) { const int prim = isect->prim; int shader = 0; @@ -161,7 +162,7 @@ ccl_device_forceinline int intersection_get_shader_flags(const KernelGlobals *cc } ccl_device_forceinline int intersection_get_shader_from_isect_prim( - const KernelGlobals *ccl_restrict kg, const int prim, const int isect_type) + ccl_global const KernelGlobals *ccl_restrict kg, const int prim, const int isect_type) { int shader = 0; @@ -180,14 +181,16 @@ ccl_device_forceinline int intersection_get_shader_from_isect_prim( return shader & SHADER_MASK; } -ccl_device_forceinline int intersection_get_shader(const KernelGlobals *ccl_restrict kg, - const Intersection *ccl_restrict isect) +ccl_device_forceinline int intersection_get_shader(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private const Intersection *ccl_restrict + isect) { return intersection_get_shader_from_isect_prim(kg, isect->prim, isect->type); } -ccl_device_forceinline int intersection_get_object_flags(const KernelGlobals *ccl_restrict kg, - const Intersection *ccl_restrict isect) +ccl_device_forceinline int intersection_get_object_flags( + ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private const Intersection *ccl_restrict isect) { return kernel_tex_fetch(__object_flag, isect->object); } diff --git a/intern/cycles/kernel/bvh/bvh_volume.h b/intern/cycles/kernel/bvh/bvh_volume.h index 0411d9c522d..d3bfce2d96b 100644 --- a/intern/cycles/kernel/bvh/bvh_volume.h +++ b/intern/cycles/kernel/bvh/bvh_volume.h @@ -35,9 +35,9 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, - const Ray *ray, - Intersection *isect, + bool BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint visibility) { /* todo: @@ -221,9 +221,9 @@ ccl_device_inline return (isect->prim != PRIM_NONE); } -ccl_device_inline bool BVH_FUNCTION_NAME(const KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_inline bool BVH_FUNCTION_NAME(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint visibility) { return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect, visibility); diff --git a/intern/cycles/kernel/bvh/bvh_volume_all.h b/intern/cycles/kernel/bvh/bvh_volume_all.h index 4874270f15d..f0fe95924cf 100644 --- a/intern/cycles/kernel/bvh/bvh_volume_all.h +++ b/intern/cycles/kernel/bvh/bvh_volume_all.h @@ -35,8 +35,8 @@ ccl_device #else ccl_device_inline #endif - uint BVH_FUNCTION_FULL_NAME(BVH)(const KernelGlobals *kg, - const Ray *ray, + uint BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, Intersection *isect_array, const uint max_hits, const uint visibility) @@ -289,8 +289,8 @@ ccl_device_inline return num_hits; } -ccl_device_inline uint BVH_FUNCTION_NAME(const KernelGlobals *kg, - const Ray *ray, +ccl_device_inline uint BVH_FUNCTION_NAME(ccl_global const KernelGlobals *kg, + ccl_private const Ray *ray, Intersection *isect_array, const uint max_hits, const uint visibility) diff --git a/intern/cycles/kernel/closure/alloc.h b/intern/cycles/kernel/closure/alloc.h index 72a8c2ba090..211eedbddbd 100644 --- a/intern/cycles/kernel/closure/alloc.h +++ b/intern/cycles/kernel/closure/alloc.h @@ -18,14 +18,17 @@ CCL_NAMESPACE_BEGIN -ccl_device ShaderClosure *closure_alloc(ShaderData *sd, int size, ClosureType type, float3 weight) +ccl_device ccl_private ShaderClosure *closure_alloc(ccl_private ShaderData *sd, + int size, + ClosureType type, + float3 weight) { kernel_assert(size <= sizeof(ShaderClosure)); if (sd->num_closure_left == 0) return NULL; - ShaderClosure *sc = &sd->closure[sd->num_closure]; + ccl_private ShaderClosure *sc = &sd->closure[sd->num_closure]; sc->type = type; sc->weight = weight; @@ -36,7 +39,7 @@ ccl_device ShaderClosure *closure_alloc(ShaderData *sd, int size, ClosureType ty return sc; } -ccl_device ccl_addr_space void *closure_alloc_extra(ShaderData *sd, int size) +ccl_device ccl_private void *closure_alloc_extra(ccl_private ShaderData *sd, int size) { /* Allocate extra space for closure that need more parameters. We allocate * in chunks of sizeof(ShaderClosure) starting from the end of the closure @@ -54,10 +57,12 @@ ccl_device ccl_addr_space void *closure_alloc_extra(ShaderData *sd, int size) } sd->num_closure_left -= num_extra; - return (ccl_addr_space void *)(sd->closure + sd->num_closure + sd->num_closure_left); + return (ccl_private void *)(sd->closure + sd->num_closure + sd->num_closure_left); } -ccl_device_inline ShaderClosure *bsdf_alloc(ShaderData *sd, int size, float3 weight) +ccl_device_inline ccl_private ShaderClosure *bsdf_alloc(ccl_private ShaderData *sd, + int size, + float3 weight) { kernel_assert(isfinite3_safe(weight)); @@ -66,7 +71,7 @@ ccl_device_inline ShaderClosure *bsdf_alloc(ShaderData *sd, int size, float3 wei /* Use comparison this way to help dealing with non-finite weight: if the average is not finite * we will not allocate new closure. */ if (sample_weight >= CLOSURE_WEIGHT_CUTOFF) { - ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight); + ccl_private ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight); if (sc == NULL) { return NULL; } diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index bb80b9636bb..e115bef3170 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -41,32 +41,32 @@ CCL_NAMESPACE_BEGIN /* Returns the square of the roughness of the closure if it has roughness, * 0 for singular closures and 1 otherwise. */ -ccl_device_inline float bsdf_get_specular_roughness_squared(const ShaderClosure *sc) +ccl_device_inline float bsdf_get_specular_roughness_squared(ccl_private const ShaderClosure *sc) { if (CLOSURE_IS_BSDF_SINGULAR(sc->type)) { return 0.0f; } if (CLOSURE_IS_BSDF_MICROFACET(sc->type)) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; return bsdf->alpha_x * bsdf->alpha_y; } return 1.0f; } -ccl_device_inline float bsdf_get_roughness_squared(const ShaderClosure *sc) +ccl_device_inline float bsdf_get_roughness_squared(ccl_private const ShaderClosure *sc) { /* This version includes diffuse, mainly for baking Principled BSDF * where specular and metallic zero otherwise does not bake the * specified roughness parameter. */ if (sc->type == CLOSURE_BSDF_OREN_NAYAR_ID) { - OrenNayarBsdf *bsdf = (OrenNayarBsdf *)sc; + ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)sc; return sqr(sqr(bsdf->roughness)); } if (sc->type == CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID) { - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)sc; + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)sc; return sqr(sqr(bsdf->roughness)); } @@ -111,15 +111,15 @@ ccl_device_inline float shift_cos_in(float cos_in, const float frequency_multipl return val; } -ccl_device_inline int bsdf_sample(const KernelGlobals *kg, - ShaderData *sd, - const ShaderClosure *sc, +ccl_device_inline int bsdf_sample(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private const ShaderClosure *sc, float randu, float randv, - float3 *eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private differential3 *domega_in, + ccl_private float *pdf) { /* For curves use the smooth normal, particularly for ribbons the geometric * normal gives too much darkening otherwise. */ @@ -467,12 +467,12 @@ ccl_device ccl_device_inline #endif float3 - bsdf_eval(const KernelGlobals *kg, - ShaderData *sd, - const ShaderClosure *sc, + bsdf_eval(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private const ShaderClosure *sc, const float3 omega_in, const bool is_transmission, - float *pdf) + ccl_private float *pdf) { float3 eval = zero_float3(); @@ -652,7 +652,9 @@ ccl_device_inline return eval; } -ccl_device void bsdf_blur(const KernelGlobals *kg, ShaderClosure *sc, float roughness) +ccl_device void bsdf_blur(ccl_global const KernelGlobals *kg, + ccl_private ShaderClosure *sc, + float roughness) { /* TODO: do we want to blur volume closures? */ #ifdef __SVM__ diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h index be6383e521a..6cd8739ce39 100644 --- a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h +++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h @@ -30,7 +30,7 @@ CCL_NAMESPACE_BEGIN -ccl_device int bsdf_ashikhmin_shirley_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_ashikhmin_shirley_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f); bsdf->alpha_y = clamp(bsdf->alpha_y, 1e-4f, 1.0f); @@ -39,9 +39,9 @@ ccl_device int bsdf_ashikhmin_shirley_setup(MicrofacetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device void bsdf_ashikhmin_shirley_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_ashikhmin_shirley_blur(ccl_private ShaderClosure *sc, float roughness) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x); bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y); @@ -52,12 +52,13 @@ ccl_device_inline float bsdf_ashikhmin_shirley_roughness_to_exponent(float rough return 2.0f / (roughness * roughness) - 2.0f; } -ccl_device_forceinline float3 bsdf_ashikhmin_shirley_eval_reflect(const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - float *pdf) +ccl_device_forceinline float3 +bsdf_ashikhmin_shirley_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float3 N = bsdf->N; float NdotI = dot(N, I); /* in Cycles/OSL convention I is omega_out */ @@ -119,16 +120,20 @@ ccl_device_forceinline float3 bsdf_ashikhmin_shirley_eval_reflect(const ShaderCl return make_float3(out, out, out); } -ccl_device float3 bsdf_ashikhmin_shirley_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_ashikhmin_shirley_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant( - float n_x, float n_y, float randu, float randv, float *phi, float *cos_theta) +ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant(float n_x, + float n_y, + float randu, + float randv, + ccl_private float *phi, + ccl_private float *cos_theta) { *phi = atanf(sqrtf((n_x + 1.0f) / (n_y + 1.0f)) * tanf(M_PI_2_F * randu)); float cos_phi = cosf(*phi); @@ -136,20 +141,20 @@ ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant( *cos_theta = powf(randv, 1.0f / (n_x * cos_phi * cos_phi + n_y * sin_phi * sin_phi + 1.0f)); } -ccl_device int bsdf_ashikhmin_shirley_sample(const ShaderClosure *sc, +ccl_device int bsdf_ashikhmin_shirley_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float3 N = bsdf->N; int label = LABEL_REFLECT | LABEL_GLOSSY; diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h index f51027f5701..c00890be54c 100644 --- a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h +++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h @@ -36,7 +36,7 @@ CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct VelvetBsdf { +typedef struct VelvetBsdf { SHADER_CLOSURE_BASE; float sigma; @@ -45,7 +45,7 @@ typedef ccl_addr_space struct VelvetBsdf { static_assert(sizeof(ShaderClosure) >= sizeof(VelvetBsdf), "VelvetBsdf is too large!"); -ccl_device int bsdf_ashikhmin_velvet_setup(VelvetBsdf *bsdf) +ccl_device int bsdf_ashikhmin_velvet_setup(ccl_private VelvetBsdf *bsdf) { float sigma = fmaxf(bsdf->sigma, 0.01f); bsdf->invsigma2 = 1.0f / (sigma * sigma); @@ -55,12 +55,12 @@ ccl_device int bsdf_ashikhmin_velvet_setup(VelvetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const VelvetBsdf *bsdf = (const VelvetBsdf *)sc; + ccl_private const VelvetBsdf *bsdf = (ccl_private const VelvetBsdf *)sc; float m_invsigma2 = bsdf->invsigma2; float3 N = bsdf->N; @@ -97,28 +97,28 @@ ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_ashikhmin_velvet_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_ashikhmin_velvet_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_ashikhmin_velvet_sample(const ShaderClosure *sc, +ccl_device int bsdf_ashikhmin_velvet_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const VelvetBsdf *bsdf = (const VelvetBsdf *)sc; + ccl_private const VelvetBsdf *bsdf = (ccl_private const VelvetBsdf *)sc; float m_invsigma2 = bsdf->invsigma2; float3 N = bsdf->N; diff --git a/intern/cycles/kernel/closure/bsdf_diffuse.h b/intern/cycles/kernel/closure/bsdf_diffuse.h index 1555aa30304..16c9b428004 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse.h @@ -34,7 +34,7 @@ CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct DiffuseBsdf { +typedef struct DiffuseBsdf { SHADER_CLOSURE_BASE; } DiffuseBsdf; @@ -42,18 +42,18 @@ static_assert(sizeof(ShaderClosure) >= sizeof(DiffuseBsdf), "DiffuseBsdf is too /* DIFFUSE */ -ccl_device int bsdf_diffuse_setup(DiffuseBsdf *bsdf) +ccl_device int bsdf_diffuse_setup(ccl_private DiffuseBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_DIFFUSE_ID; return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_diffuse_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; float3 N = bsdf->N; float cos_pi = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F; @@ -61,28 +61,28 @@ ccl_device float3 bsdf_diffuse_eval_reflect(const ShaderClosure *sc, return make_float3(cos_pi, cos_pi, cos_pi); } -ccl_device float3 bsdf_diffuse_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_diffuse_sample(const ShaderClosure *sc, +ccl_device int bsdf_diffuse_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; float3 N = bsdf->N; // distribution over the hemisphere @@ -104,26 +104,26 @@ ccl_device int bsdf_diffuse_sample(const ShaderClosure *sc, /* TRANSLUCENT */ -ccl_device int bsdf_translucent_setup(DiffuseBsdf *bsdf) +ccl_device int bsdf_translucent_setup(ccl_private DiffuseBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_TRANSLUCENT_ID; return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_translucent_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_translucent_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_translucent_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_translucent_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; float3 N = bsdf->N; float cos_pi = fmaxf(-dot(N, omega_in), 0.0f) * M_1_PI_F; @@ -131,20 +131,20 @@ ccl_device float3 bsdf_translucent_eval_transmit(const ShaderClosure *sc, return make_float3(cos_pi, cos_pi, cos_pi); } -ccl_device int bsdf_translucent_sample(const ShaderClosure *sc, +ccl_device int bsdf_translucent_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; float3 N = bsdf->N; // we are viewing the surface from the right side - send a ray out with cosine diff --git a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h index b06dd196b9e..8bff7709a32 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h @@ -36,10 +36,10 @@ CCL_NAMESPACE_BEGIN #ifdef __OSL__ -typedef ccl_addr_space struct DiffuseRampBsdf { +typedef struct DiffuseRampBsdf { SHADER_CLOSURE_BASE; - float3 *colors; + ccl_private float3 *colors; } DiffuseRampBsdf; static_assert(sizeof(ShaderClosure) >= sizeof(DiffuseRampBsdf), "DiffuseRampBsdf is too large!"); @@ -64,14 +64,14 @@ ccl_device int bsdf_diffuse_ramp_setup(DiffuseRampBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device void bsdf_diffuse_ramp_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_diffuse_ramp_blur(ccl_private ShaderClosure *sc, float roughness) { } -ccl_device float3 bsdf_diffuse_ramp_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_ramp_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf *)sc; float3 N = bsdf->N; @@ -81,26 +81,26 @@ ccl_device float3 bsdf_diffuse_ramp_eval_reflect(const ShaderClosure *sc, return bsdf_diffuse_ramp_get_color(bsdf->colors, cos_pi) * M_1_PI_F; } -ccl_device float3 bsdf_diffuse_ramp_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_ramp_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_diffuse_ramp_sample(const ShaderClosure *sc, +ccl_device int bsdf_diffuse_ramp_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf *)sc; float3 N = bsdf->N; diff --git a/intern/cycles/kernel/closure/bsdf_hair.h b/intern/cycles/kernel/closure/bsdf_hair.h index f56f78aa1f0..449a314a90e 100644 --- a/intern/cycles/kernel/closure/bsdf_hair.h +++ b/intern/cycles/kernel/closure/bsdf_hair.h @@ -34,7 +34,7 @@ CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct HairBsdf { +typedef struct HairBsdf { SHADER_CLOSURE_BASE; float3 T; @@ -45,7 +45,7 @@ typedef ccl_addr_space struct HairBsdf { static_assert(sizeof(ShaderClosure) >= sizeof(HairBsdf), "HairBsdf is too large!"); -ccl_device int bsdf_hair_reflection_setup(HairBsdf *bsdf) +ccl_device int bsdf_hair_reflection_setup(ccl_private HairBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_HAIR_REFLECTION_ID; bsdf->roughness1 = clamp(bsdf->roughness1, 0.001f, 1.0f); @@ -53,7 +53,7 @@ ccl_device int bsdf_hair_reflection_setup(HairBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device int bsdf_hair_transmission_setup(HairBsdf *bsdf) +ccl_device int bsdf_hair_transmission_setup(ccl_private HairBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_HAIR_TRANSMISSION_ID; bsdf->roughness1 = clamp(bsdf->roughness1, 0.001f, 1.0f); @@ -61,12 +61,12 @@ ccl_device int bsdf_hair_transmission_setup(HairBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_hair_reflection_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_hair_reflection_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const HairBsdf *bsdf = (const HairBsdf *)sc; + ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; float offset = bsdf->offset; float3 Tg = bsdf->T; float roughness1 = bsdf->roughness1; @@ -108,28 +108,28 @@ ccl_device float3 bsdf_hair_reflection_eval_reflect(const ShaderClosure *sc, return make_float3(*pdf, *pdf, *pdf); } -ccl_device float3 bsdf_hair_transmission_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_hair_transmission_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_hair_reflection_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_hair_reflection_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_hair_transmission_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_hair_transmission_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const HairBsdf *bsdf = (const HairBsdf *)sc; + ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; float offset = bsdf->offset; float3 Tg = bsdf->T; float roughness1 = bsdf->roughness1; @@ -170,20 +170,20 @@ ccl_device float3 bsdf_hair_transmission_eval_transmit(const ShaderClosure *sc, return make_float3(*pdf, *pdf, *pdf); } -ccl_device int bsdf_hair_reflection_sample(const ShaderClosure *sc, +ccl_device int bsdf_hair_reflection_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const HairBsdf *bsdf = (const HairBsdf *)sc; + ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; float offset = bsdf->offset; float3 Tg = bsdf->T; float roughness1 = bsdf->roughness1; @@ -231,20 +231,20 @@ ccl_device int bsdf_hair_reflection_sample(const ShaderClosure *sc, return LABEL_REFLECT | LABEL_GLOSSY; } -ccl_device int bsdf_hair_transmission_sample(const ShaderClosure *sc, +ccl_device int bsdf_hair_transmission_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const HairBsdf *bsdf = (const HairBsdf *)sc; + ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; float offset = bsdf->offset; float3 Tg = bsdf->T; float roughness1 = bsdf->roughness1; diff --git a/intern/cycles/kernel/closure/bsdf_hair_principled.h b/intern/cycles/kernel/closure/bsdf_hair_principled.h index bfe56e5ab0e..17097b0739b 100644 --- a/intern/cycles/kernel/closure/bsdf_hair_principled.h +++ b/intern/cycles/kernel/closure/bsdf_hair_principled.h @@ -24,12 +24,12 @@ CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct PrincipledHairExtra { +typedef struct PrincipledHairExtra { /* Geometry data. */ float4 geom; } PrincipledHairExtra; -typedef ccl_addr_space struct PrincipledHairBSDF { +typedef struct PrincipledHairBSDF { SHADER_CLOSURE_BASE; /* Absorption coefficient. */ @@ -46,7 +46,7 @@ typedef ccl_addr_space struct PrincipledHairBSDF { float m0_roughness; /* Extra closure. */ - PrincipledHairExtra *extra; + ccl_private PrincipledHairExtra *extra; } PrincipledHairBSDF; static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledHairBSDF), @@ -180,14 +180,15 @@ ccl_device_inline float longitudinal_scattering( } /* Combine the three values using their luminances. */ -ccl_device_inline float4 combine_with_energy(const KernelGlobals *kg, float3 c) +ccl_device_inline float4 combine_with_energy(ccl_global const KernelGlobals *kg, float3 c) { return make_float4(c.x, c.y, c.z, linear_rgb_to_gray(kg, c)); } #ifdef __HAIR__ /* Set up the hair closure. */ -ccl_device int bsdf_principled_hair_setup(ShaderData *sd, PrincipledHairBSDF *bsdf) +ccl_device int bsdf_principled_hair_setup(ccl_private ShaderData *sd, + ccl_private PrincipledHairBSDF *bsdf) { bsdf->type = CLOSURE_BSDF_HAIR_PRINCIPLED_ID; bsdf->v = clamp(bsdf->v, 0.001f, 1.0f); @@ -228,7 +229,10 @@ ccl_device int bsdf_principled_hair_setup(ShaderData *sd, PrincipledHairBSDF *bs #endif /* __HAIR__ */ /* Given the Fresnel term and transmittance, generate the attenuation terms for each bounce. */ -ccl_device_inline void hair_attenuation(const KernelGlobals *kg, float f, float3 T, float4 *Ap) +ccl_device_inline void hair_attenuation(ccl_global const KernelGlobals *kg, + float f, + float3 T, + ccl_private float4 *Ap) { /* Primary specular (R). */ Ap[0] = make_float4(f, f, f, f); @@ -259,7 +263,7 @@ ccl_device_inline void hair_attenuation(const KernelGlobals *kg, float f, float3 ccl_device_inline void hair_alpha_angles(float sin_theta_i, float cos_theta_i, float alpha, - float *angles) + ccl_private float *angles) { float sin_1alpha = sinf(alpha); float cos_1alpha = cos_from_sin(sin_1alpha); @@ -277,15 +281,15 @@ ccl_device_inline void hair_alpha_angles(float sin_theta_i, } /* Evaluation function for our shader. */ -ccl_device float3 bsdf_principled_hair_eval(const KernelGlobals *kg, - const ShaderData *sd, - const ShaderClosure *sc, +ccl_device float3 bsdf_principled_hair_eval(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderClosure *sc, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { kernel_assert(isfinite3_safe(sd->P) && isfinite_safe(sd->ray_length)); - const PrincipledHairBSDF *bsdf = (const PrincipledHairBSDF *)sc; + ccl_private const PrincipledHairBSDF *bsdf = (ccl_private const PrincipledHairBSDF *)sc; float3 Y = float4_to_float3(bsdf->extra->geom); float3 X = safe_normalize(sd->dPdu); @@ -355,18 +359,18 @@ ccl_device float3 bsdf_principled_hair_eval(const KernelGlobals *kg, } /* Sampling function for the hair shader. */ -ccl_device int bsdf_principled_hair_sample(const KernelGlobals *kg, - const ShaderClosure *sc, - ShaderData *sd, +ccl_device int bsdf_principled_hair_sample(ccl_global const KernelGlobals *kg, + ccl_private const ShaderClosure *sc, + ccl_private ShaderData *sd, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)sc; + ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)sc; float3 Y = float4_to_float3(bsdf->extra->geom); @@ -378,8 +382,8 @@ ccl_device int bsdf_principled_hair_sample(const KernelGlobals *kg, float2 u[2]; u[0] = make_float2(randu, randv); - u[1].x = lcg_step_float_addrspace(&sd->lcg_state); - u[1].y = lcg_step_float_addrspace(&sd->lcg_state); + u[1].x = lcg_step_float(&sd->lcg_state); + u[1].y = lcg_step_float(&sd->lcg_state); float sin_theta_o = wo.x; float cos_theta_o = cos_from_sin(sin_theta_o); @@ -482,9 +486,9 @@ ccl_device int bsdf_principled_hair_sample(const KernelGlobals *kg, } /* Implements Filter Glossy by capping the effective roughness. */ -ccl_device void bsdf_principled_hair_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_principled_hair_blur(ccl_private ShaderClosure *sc, float roughness) { - PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)sc; + ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)sc; bsdf->v = fmaxf(roughness, bsdf->v); bsdf->s = fmaxf(roughness, bsdf->s); @@ -500,9 +504,9 @@ ccl_device_inline float bsdf_principled_hair_albedo_roughness_scale( return (((((0.245f * x) + 5.574f) * x - 10.73f) * x + 2.532f) * x - 0.215f) * x + 5.969f; } -ccl_device float3 bsdf_principled_hair_albedo(const ShaderClosure *sc) +ccl_device float3 bsdf_principled_hair_albedo(ccl_private const ShaderClosure *sc) { - PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)sc; + ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)sc; return exp3(-sqrt(bsdf->sigma) * bsdf_principled_hair_albedo_roughness_scale(bsdf->v)); } diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index 227cb448b47..41c35867a6b 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -37,17 +37,17 @@ CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct MicrofacetExtra { +typedef struct MicrofacetExtra { float3 color, cspec0; float3 fresnel_color; float clearcoat; } MicrofacetExtra; -typedef ccl_addr_space struct MicrofacetBsdf { +typedef struct MicrofacetBsdf { SHADER_CLOSURE_BASE; float alpha_x, alpha_y, ior; - MicrofacetExtra *extra; + ccl_private MicrofacetExtra *extra; float3 T; } MicrofacetBsdf; @@ -55,14 +55,14 @@ static_assert(sizeof(ShaderClosure) >= sizeof(MicrofacetBsdf), "MicrofacetBsdf i /* Beckmann and GGX microfacet importance sampling. */ -ccl_device_inline void microfacet_beckmann_sample_slopes(const KernelGlobals *kg, +ccl_device_inline void microfacet_beckmann_sample_slopes(ccl_global const KernelGlobals *kg, const float cos_theta_i, const float sin_theta_i, float randu, float randv, - float *slope_x, - float *slope_y, - float *G1i) + ccl_private float *slope_x, + ccl_private float *slope_y, + ccl_private float *G1i) { /* special case (normal incidence) */ if (cos_theta_i >= 0.99999f) { @@ -146,9 +146,9 @@ ccl_device_inline void microfacet_ggx_sample_slopes(const float cos_theta_i, const float sin_theta_i, float randu, float randv, - float *slope_x, - float *slope_y, - float *G1i) + ccl_private float *slope_x, + ccl_private float *slope_y, + ccl_private float *G1i) { /* special case (normal incidence) */ if (cos_theta_i >= 0.99999f) { @@ -195,14 +195,14 @@ ccl_device_inline void microfacet_ggx_sample_slopes(const float cos_theta_i, *slope_y = S * z * safe_sqrtf(1.0f + (*slope_x) * (*slope_x)); } -ccl_device_forceinline float3 microfacet_sample_stretched(const KernelGlobals *kg, +ccl_device_forceinline float3 microfacet_sample_stretched(ccl_global const KernelGlobals *kg, const float3 omega_i, const float alpha_x, const float alpha_y, const float randu, const float randv, bool beckmann, - float *G1i) + ccl_private float *G1i) { /* 1. stretch omega_i */ float3 omega_i_ = make_float3(alpha_x * omega_i.x, alpha_y * omega_i.y, omega_i.z); @@ -254,7 +254,9 @@ ccl_device_forceinline float3 microfacet_sample_stretched(const KernelGlobals *k * * Else it is simply white */ -ccl_device_forceinline float3 reflection_color(const MicrofacetBsdf *bsdf, float3 L, float3 H) +ccl_device_forceinline float3 reflection_color(ccl_private const MicrofacetBsdf *bsdf, + float3 L, + float3 H) { float3 F = make_float3(1.0f, 1.0f, 1.0f); bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID || @@ -277,8 +279,8 @@ ccl_device_forceinline float D_GTR1(float NdotH, float alpha) return (alpha2 - 1.0f) / (M_PI_F * logf(alpha2) * t); } -ccl_device_forceinline void bsdf_microfacet_fresnel_color(const ShaderData *sd, - MicrofacetBsdf *bsdf) +ccl_device_forceinline void bsdf_microfacet_fresnel_color(ccl_private const ShaderData *sd, + ccl_private MicrofacetBsdf *bsdf) { kernel_assert(CLOSURE_IS_BSDF_MICROFACET_FRESNEL(bsdf->type)); @@ -306,7 +308,7 @@ ccl_device_forceinline void bsdf_microfacet_fresnel_color(const ShaderData *sd, * Anisotropy is only supported for reflection currently, but adding it for * transmission is just a matter of copying code from reflection if needed. */ -ccl_device int bsdf_microfacet_ggx_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_ggx_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->extra = NULL; @@ -319,14 +321,15 @@ ccl_device int bsdf_microfacet_ggx_setup(MicrofacetBsdf *bsdf) } /* Required to maintain OSL interface. */ -ccl_device int bsdf_microfacet_ggx_isotropic_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_ggx_isotropic_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_y = bsdf->alpha_x; return bsdf_microfacet_ggx_setup(bsdf); } -ccl_device int bsdf_microfacet_ggx_fresnel_setup(MicrofacetBsdf *bsdf, const ShaderData *sd) +ccl_device int bsdf_microfacet_ggx_fresnel_setup(ccl_private MicrofacetBsdf *bsdf, + ccl_private const ShaderData *sd) { bsdf->extra->cspec0 = saturate3(bsdf->extra->cspec0); @@ -340,7 +343,8 @@ ccl_device int bsdf_microfacet_ggx_fresnel_setup(MicrofacetBsdf *bsdf, const Sha return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device int bsdf_microfacet_ggx_clearcoat_setup(MicrofacetBsdf *bsdf, const ShaderData *sd) +ccl_device int bsdf_microfacet_ggx_clearcoat_setup(ccl_private MicrofacetBsdf *bsdf, + ccl_private const ShaderData *sd) { bsdf->extra->cspec0 = saturate3(bsdf->extra->cspec0); @@ -354,7 +358,7 @@ ccl_device int bsdf_microfacet_ggx_clearcoat_setup(MicrofacetBsdf *bsdf, const S return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device int bsdf_microfacet_ggx_refraction_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_ggx_refraction_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->extra = NULL; @@ -366,20 +370,20 @@ ccl_device int bsdf_microfacet_ggx_refraction_setup(MicrofacetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device void bsdf_microfacet_ggx_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_microfacet_ggx_blur(ccl_private ShaderClosure *sc, float roughness) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x); bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y); } -ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_ggx_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; @@ -487,12 +491,12 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_microfacet_ggx_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_ggx_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; float m_eta = bsdf->ior; @@ -545,21 +549,21 @@ ccl_device float3 bsdf_microfacet_ggx_eval_transmit(const ShaderClosure *sc, return make_float3(out, out, out); } -ccl_device int bsdf_microfacet_ggx_sample(const KernelGlobals *kg, - const ShaderClosure *sc, +ccl_device int bsdf_microfacet_ggx_sample(ccl_global const KernelGlobals *kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; @@ -774,7 +778,7 @@ ccl_device int bsdf_microfacet_ggx_sample(const KernelGlobals *kg, * Microfacet Models for Refraction through Rough Surfaces * B. Walter, S. R. Marschner, H. Li, K. E. Torrance, EGSR 2007 */ -ccl_device int bsdf_microfacet_beckmann_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_beckmann_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_x = saturate(bsdf->alpha_x); bsdf->alpha_y = saturate(bsdf->alpha_y); @@ -784,14 +788,14 @@ ccl_device int bsdf_microfacet_beckmann_setup(MicrofacetBsdf *bsdf) } /* Required to maintain OSL interface. */ -ccl_device int bsdf_microfacet_beckmann_isotropic_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_beckmann_isotropic_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_y = bsdf->alpha_x; return bsdf_microfacet_beckmann_setup(bsdf); } -ccl_device int bsdf_microfacet_beckmann_refraction_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_beckmann_refraction_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_x = saturate(bsdf->alpha_x); bsdf->alpha_y = bsdf->alpha_x; @@ -800,9 +804,9 @@ ccl_device int bsdf_microfacet_beckmann_refraction_setup(MicrofacetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device void bsdf_microfacet_beckmann_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_microfacet_beckmann_blur(ccl_private ShaderClosure *sc, float roughness) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x); bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y); @@ -839,12 +843,12 @@ ccl_device_inline float bsdf_beckmann_aniso_G1( return ((2.181f * a + 3.535f) * a) / ((2.577f * a + 2.276f) * a + 1.0f); } -ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID; @@ -918,12 +922,12 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; float m_eta = bsdf->ior; @@ -973,21 +977,21 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(const ShaderClosure *sc return make_float3(out, out, out); } -ccl_device int bsdf_microfacet_beckmann_sample(const KernelGlobals *kg, - const ShaderClosure *sc, +ccl_device int bsdf_microfacet_beckmann_sample(ccl_global const KernelGlobals *kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID; diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index 68d5071dbce..6ee1139ddbb 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -105,7 +105,7 @@ ccl_device_forceinline float3 mf_sample_vndf(const float3 wi, /* Phase function for reflective materials. */ ccl_device_forceinline float3 mf_sample_phase_glossy(const float3 wi, - float3 *weight, + ccl_private float3 *weight, const float3 wm) { return -wi + 2.0f * wm * dot(wi, wm); @@ -140,8 +140,11 @@ ccl_device_forceinline float3 mf_eval_phase_glossy(const float3 w, /* Phase function for dielectric transmissive materials, including both reflection and refraction * according to the dielectric fresnel term. */ -ccl_device_forceinline float3 mf_sample_phase_glass( - const float3 wi, const float eta, const float3 wm, const float randV, bool *outside) +ccl_device_forceinline float3 mf_sample_phase_glass(const float3 wi, + const float eta, + const float3 wm, + const float randV, + ccl_private bool *outside) { float cosI = dot(wi, wm); float f = fresnel_dielectric_cos(cosI, eta); @@ -234,8 +237,12 @@ ccl_device_forceinline float mf_G1(const float3 w, const float C1, const float l /* Sampling from the visible height distribution (based on page 17 of the supplemental * implementation). */ -ccl_device_forceinline bool mf_sample_height( - const float3 w, float *h, float *C1, float *G1, float *lambda, const float U) +ccl_device_forceinline bool mf_sample_height(const float3 w, + ccl_private float *h, + ccl_private float *C1, + ccl_private float *G1, + ccl_private float *lambda, + const float U) { if (w.z > 0.9999f) return false; @@ -364,9 +371,9 @@ ccl_device_forceinline float mf_glass_pdf(const float3 wi, #define MF_MULTI_GLOSSY #include "kernel/closure/bsdf_microfacet_multi_impl.h" -ccl_device void bsdf_microfacet_multi_ggx_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_microfacet_multi_ggx_blur(ccl_private ShaderClosure *sc, float roughness) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x); bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y); @@ -376,7 +383,7 @@ ccl_device void bsdf_microfacet_multi_ggx_blur(ShaderClosure *sc, float roughnes /* Multiscattering GGX Glossy closure */ -ccl_device int bsdf_microfacet_multi_ggx_common_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_multi_ggx_common_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f); bsdf->alpha_y = clamp(bsdf->alpha_y, 1e-4f, 1.0f); @@ -386,7 +393,7 @@ ccl_device int bsdf_microfacet_multi_ggx_common_setup(MicrofacetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; } -ccl_device int bsdf_microfacet_multi_ggx_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_multi_ggx_setup(ccl_private MicrofacetBsdf *bsdf) { if (is_zero(bsdf->T)) bsdf->T = make_float3(1.0f, 0.0f, 0.0f); @@ -396,7 +403,8 @@ ccl_device int bsdf_microfacet_multi_ggx_setup(MicrofacetBsdf *bsdf) return bsdf_microfacet_multi_ggx_common_setup(bsdf); } -ccl_device int bsdf_microfacet_multi_ggx_fresnel_setup(MicrofacetBsdf *bsdf, const ShaderData *sd) +ccl_device int bsdf_microfacet_multi_ggx_fresnel_setup(ccl_private MicrofacetBsdf *bsdf, + ccl_private const ShaderData *sd) { if (is_zero(bsdf->T)) bsdf->T = make_float3(1.0f, 0.0f, 0.0f); @@ -408,7 +416,7 @@ ccl_device int bsdf_microfacet_multi_ggx_fresnel_setup(MicrofacetBsdf *bsdf, con return bsdf_microfacet_multi_ggx_common_setup(bsdf); } -ccl_device int bsdf_microfacet_multi_ggx_refraction_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_multi_ggx_refraction_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_y = bsdf->alpha_x; @@ -417,23 +425,23 @@ ccl_device int bsdf_microfacet_multi_ggx_refraction_setup(MicrofacetBsdf *bsdf) return bsdf_microfacet_multi_ggx_common_setup(bsdf); } -ccl_device float3 bsdf_microfacet_multi_ggx_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_multi_ggx_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf, - ccl_addr_space uint *lcg_state) + ccl_private float *pdf, + ccl_private uint *lcg_state) { *pdf = 0.0f; return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf, - ccl_addr_space uint *lcg_state) + ccl_private float *pdf, + ccl_private uint *lcg_state) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) { return make_float3(0.0f, 0.0f, 0.0f); @@ -468,22 +476,22 @@ ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(const ShaderClosure *sc bsdf->extra->cspec0); } -ccl_device int bsdf_microfacet_multi_ggx_sample(const KernelGlobals *kg, - const ShaderClosure *sc, +ccl_device int bsdf_microfacet_multi_ggx_sample(ccl_global const KernelGlobals *kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf, - ccl_addr_space uint *lcg_state) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf, + ccl_private uint *lcg_state) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float3 X, Y, Z; Z = bsdf->N; @@ -536,7 +544,7 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(const KernelGlobals *kg, /* Multiscattering GGX Glass closure */ -ccl_device int bsdf_microfacet_multi_ggx_glass_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_multi_ggx_glass_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f); bsdf->alpha_y = bsdf->alpha_x; @@ -548,8 +556,8 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_setup(MicrofacetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; } -ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(MicrofacetBsdf *bsdf, - const ShaderData *sd) +ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(ccl_private MicrofacetBsdf *bsdf, + ccl_private const ShaderData *sd) { bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f); bsdf->alpha_y = bsdf->alpha_x; @@ -564,13 +572,14 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(MicrofacetBsdf *bsd return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; } -ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_transmit(const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - float *pdf, - ccl_addr_space uint *lcg_state) +ccl_device float3 +bsdf_microfacet_multi_ggx_glass_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf, + ccl_private uint *lcg_state) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) { return make_float3(0.0f, 0.0f, 0.0f); @@ -596,13 +605,13 @@ ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_transmit(const ShaderClos bsdf->extra->color); } -ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf, - ccl_addr_space uint *lcg_state) + ccl_private float *pdf, + ccl_private uint *lcg_state) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) { return make_float3(0.0f, 0.0f, 0.0f); @@ -630,22 +639,22 @@ ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(const ShaderClosu bsdf->extra->cspec0); } -ccl_device int bsdf_microfacet_multi_ggx_glass_sample(const KernelGlobals *kg, - const ShaderClosure *sc, +ccl_device int bsdf_microfacet_multi_ggx_glass_sample(ccl_global const KernelGlobals *kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf, - ccl_addr_space uint *lcg_state) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf, + ccl_private uint *lcg_state) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float3 X, Y, Z; Z = bsdf->N; diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h index 04d9b22d7d2..d23cc16cff3 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h @@ -31,7 +31,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, const float3 color, const float alpha_x, const float alpha_y, - ccl_addr_space uint *lcg_state, + ccl_private uint *lcg_state, const float eta, bool use_fresnel, const float3 cspec0) @@ -101,12 +101,12 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, for (int order = 0; order < 10; order++) { /* Sample microfacet height. */ - float height_rand = lcg_step_float_addrspace(lcg_state); + float height_rand = lcg_step_float(lcg_state); if (!mf_sample_height(wr, &hr, &C1_r, &G1_r, &lambda_r, height_rand)) break; /* Sample microfacet normal. */ - float vndf_rand_y = lcg_step_float_addrspace(lcg_state); - float vndf_rand_x = lcg_step_float_addrspace(lcg_state); + float vndf_rand_y = lcg_step_float(lcg_state); + float vndf_rand_x = lcg_step_float(lcg_state); float3 wm = mf_sample_vndf(-wr, alpha, vndf_rand_x, vndf_rand_y); #ifdef MF_MULTI_GLASS @@ -145,7 +145,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, #ifdef MF_MULTI_GLASS bool next_outside; float3 wi_prev = -wr; - float phase_rand = lcg_step_float_addrspace(lcg_state); + float phase_rand = lcg_step_float(lcg_state); wr = mf_sample_phase_glass(-wr, outside ? eta : 1.0f / eta, wm, phase_rand, &next_outside); if (!next_outside) { outside = !outside; @@ -186,11 +186,11 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, * reflection losses due to coloring or fresnel absorption in conductors, the sampling is optimal. */ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, - float3 *wo, + ccl_private float3 *wo, const float3 color, const float alpha_x, const float alpha_y, - ccl_addr_space uint *lcg_state, + ccl_private uint *lcg_state, const float eta, bool use_fresnel, const float3 cspec0) @@ -213,15 +213,15 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, int order; for (order = 0; order < 10; order++) { /* Sample microfacet height. */ - float height_rand = lcg_step_float_addrspace(lcg_state); + float height_rand = lcg_step_float(lcg_state); if (!mf_sample_height(wr, &hr, &C1_r, &G1_r, &lambda_r, height_rand)) { /* The random walk has left the surface. */ *wo = outside ? wr : -wr; return throughput; } /* Sample microfacet normal. */ - float vndf_rand_y = lcg_step_float_addrspace(lcg_state); - float vndf_rand_x = lcg_step_float_addrspace(lcg_state); + float vndf_rand_y = lcg_step_float(lcg_state); + float vndf_rand_x = lcg_step_float(lcg_state); float3 wm = mf_sample_vndf(-wr, alpha, vndf_rand_x, vndf_rand_y); /* First-bounce color is already accounted for in mix weight. */ @@ -232,7 +232,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, #ifdef MF_MULTI_GLASS bool next_outside; float3 wi_prev = -wr; - float phase_rand = lcg_step_float_addrspace(lcg_state); + float phase_rand = lcg_step_float(lcg_state); wr = mf_sample_phase_glass(-wr, outside ? eta : 1.0f / eta, wm, phase_rand, &next_outside); if (!next_outside) { hr = -hr; diff --git a/intern/cycles/kernel/closure/bsdf_oren_nayar.h b/intern/cycles/kernel/closure/bsdf_oren_nayar.h index be12d47f0ea..00c2678f0a0 100644 --- a/intern/cycles/kernel/closure/bsdf_oren_nayar.h +++ b/intern/cycles/kernel/closure/bsdf_oren_nayar.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct OrenNayarBsdf { +typedef struct OrenNayarBsdf { SHADER_CLOSURE_BASE; float roughness; @@ -28,12 +28,12 @@ typedef ccl_addr_space struct OrenNayarBsdf { static_assert(sizeof(ShaderClosure) >= sizeof(OrenNayarBsdf), "OrenNayarBsdf is too large!"); -ccl_device float3 bsdf_oren_nayar_get_intensity(const ShaderClosure *sc, +ccl_device float3 bsdf_oren_nayar_get_intensity(ccl_private const ShaderClosure *sc, float3 n, float3 v, float3 l) { - const OrenNayarBsdf *bsdf = (const OrenNayarBsdf *)sc; + ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc; float nl = max(dot(n, l), 0.0f); float nv = max(dot(n, v), 0.0f); float t = dot(l, v) - nl * nv; @@ -44,7 +44,7 @@ ccl_device float3 bsdf_oren_nayar_get_intensity(const ShaderClosure *sc, return make_float3(is, is, is); } -ccl_device int bsdf_oren_nayar_setup(OrenNayarBsdf *bsdf) +ccl_device int bsdf_oren_nayar_setup(ccl_private OrenNayarBsdf *bsdf) { float sigma = bsdf->roughness; @@ -60,12 +60,12 @@ ccl_device int bsdf_oren_nayar_setup(OrenNayarBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_oren_nayar_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_oren_nayar_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const OrenNayarBsdf *bsdf = (const OrenNayarBsdf *)sc; + ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc; if (dot(bsdf->N, omega_in) > 0.0f) { *pdf = 0.5f * M_1_PI_F; return bsdf_oren_nayar_get_intensity(sc, bsdf->N, I, omega_in); @@ -76,28 +76,28 @@ ccl_device float3 bsdf_oren_nayar_eval_reflect(const ShaderClosure *sc, } } -ccl_device float3 bsdf_oren_nayar_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_oren_nayar_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_oren_nayar_sample(const ShaderClosure *sc, +ccl_device int bsdf_oren_nayar_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const OrenNayarBsdf *bsdf = (const OrenNayarBsdf *)sc; + ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc; sample_uniform_hemisphere(bsdf->N, randu, randv, omega_in, pdf); if (dot(Ng, *omega_in) > 0.0f) { diff --git a/intern/cycles/kernel/closure/bsdf_phong_ramp.h b/intern/cycles/kernel/closure/bsdf_phong_ramp.h index 43f8cf71c59..74cc62d917b 100644 --- a/intern/cycles/kernel/closure/bsdf_phong_ramp.h +++ b/intern/cycles/kernel/closure/bsdf_phong_ramp.h @@ -36,11 +36,11 @@ CCL_NAMESPACE_BEGIN #ifdef __OSL__ -typedef ccl_addr_space struct PhongRampBsdf { +typedef struct PhongRampBsdf { SHADER_CLOSURE_BASE; float exponent; - float3 *colors; + ccl_private float3 *colors; } PhongRampBsdf; static_assert(sizeof(ShaderClosure) >= sizeof(PhongRampBsdf), "PhongRampBsdf is too large!"); @@ -59,19 +59,19 @@ ccl_device float3 bsdf_phong_ramp_get_color(const float3 colors[8], float pos) return colors[ipos] * (1.0f - offset) + colors[ipos + 1] * offset; } -ccl_device int bsdf_phong_ramp_setup(PhongRampBsdf *bsdf) +ccl_device int bsdf_phong_ramp_setup(ccl_private PhongRampBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_PHONG_RAMP_ID; bsdf->exponent = max(bsdf->exponent, 0.0f); return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_phong_ramp_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_phong_ramp_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const PhongRampBsdf *bsdf = (const PhongRampBsdf *)sc; + ccl_private const PhongRampBsdf *bsdf = (ccl_private const PhongRampBsdf *)sc; float m_exponent = bsdf->exponent; float cosNI = dot(bsdf->N, omega_in); float cosNO = dot(bsdf->N, I); @@ -92,28 +92,28 @@ ccl_device float3 bsdf_phong_ramp_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_phong_ramp_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_phong_ramp_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_phong_ramp_sample(const ShaderClosure *sc, +ccl_device int bsdf_phong_ramp_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const PhongRampBsdf *bsdf = (const PhongRampBsdf *)sc; + ccl_private const PhongRampBsdf *bsdf = (ccl_private const PhongRampBsdf *)sc; float cosNO = dot(bsdf->N, I); float m_exponent = bsdf->exponent; diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index 52a37eafd9f..6d25daa2356 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -36,7 +36,7 @@ enum PrincipledDiffuseBsdfComponents { PRINCIPLED_DIFFUSE_RETRO_REFLECTION = 8, }; -typedef ccl_addr_space struct PrincipledDiffuseBsdf { +typedef struct PrincipledDiffuseBsdf { SHADER_CLOSURE_BASE; float roughness; @@ -46,14 +46,18 @@ typedef ccl_addr_space struct PrincipledDiffuseBsdf { static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledDiffuseBsdf), "PrincipledDiffuseBsdf is too large!"); -ccl_device int bsdf_principled_diffuse_setup(PrincipledDiffuseBsdf *bsdf) +ccl_device int bsdf_principled_diffuse_setup(ccl_private PrincipledDiffuseBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID; return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_principled_diffuse_compute_brdf( - const PrincipledDiffuseBsdf *bsdf, float3 N, float3 V, float3 L, float *pdf) +ccl_device float3 +bsdf_principled_diffuse_compute_brdf(ccl_private const PrincipledDiffuseBsdf *bsdf, + float3 N, + float3 V, + float3 L, + ccl_private float *pdf) { const float NdotL = dot(N, L); @@ -102,24 +106,25 @@ ccl_device_inline float bsdf_principled_diffuse_compute_entry_fresnel(const floa /* Ad-hoc weight adjustment to avoid retro-reflection taking away half the * samples from BSSRDF. */ ccl_device_inline float bsdf_principled_diffuse_retro_reflection_sample_weight( - PrincipledDiffuseBsdf *bsdf, const float3 I) + ccl_private PrincipledDiffuseBsdf *bsdf, const float3 I) { return bsdf->roughness * schlick_fresnel(dot(bsdf->N, I)); } -ccl_device int bsdf_principled_diffuse_setup(PrincipledDiffuseBsdf *bsdf, int components) +ccl_device int bsdf_principled_diffuse_setup(ccl_private PrincipledDiffuseBsdf *bsdf, + int components) { bsdf->type = CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID; bsdf->components = components; return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_principled_diffuse_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_principled_diffuse_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const PrincipledDiffuseBsdf *bsdf = (const PrincipledDiffuseBsdf *)sc; + ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *)sc; float3 N = bsdf->N; float3 V = I; // outgoing @@ -135,28 +140,28 @@ ccl_device float3 bsdf_principled_diffuse_eval_reflect(const ShaderClosure *sc, } } -ccl_device float3 bsdf_principled_diffuse_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_principled_diffuse_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_principled_diffuse_sample(const ShaderClosure *sc, +ccl_device int bsdf_principled_diffuse_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const PrincipledDiffuseBsdf *bsdf = (const PrincipledDiffuseBsdf *)sc; + ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *)sc; float3 N = bsdf->N; diff --git a/intern/cycles/kernel/closure/bsdf_principled_sheen.h b/intern/cycles/kernel/closure/bsdf_principled_sheen.h index 60ce7e4eb75..cc0a5accb95 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_sheen.h +++ b/intern/cycles/kernel/closure/bsdf_principled_sheen.h @@ -25,7 +25,7 @@ CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct PrincipledSheenBsdf { +typedef struct PrincipledSheenBsdf { SHADER_CLOSURE_BASE; float avg_value; } PrincipledSheenBsdf; @@ -46,7 +46,7 @@ ccl_device_inline float calculate_avg_principled_sheen_brdf(float3 N, float3 I) } ccl_device float3 -calculate_principled_sheen_brdf(float3 N, float3 V, float3 L, float3 H, float *pdf) +calculate_principled_sheen_brdf(float3 N, float3 V, float3 L, float3 H, ccl_private float *pdf) { float NdotL = dot(N, L); float NdotV = dot(N, V); @@ -63,7 +63,8 @@ calculate_principled_sheen_brdf(float3 N, float3 V, float3 L, float3 H, float *p return make_float3(value, value, value); } -ccl_device int bsdf_principled_sheen_setup(const ShaderData *sd, PrincipledSheenBsdf *bsdf) +ccl_device int bsdf_principled_sheen_setup(ccl_private const ShaderData *sd, + ccl_private PrincipledSheenBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_PRINCIPLED_SHEEN_ID; bsdf->avg_value = calculate_avg_principled_sheen_brdf(bsdf->N, sd->I); @@ -71,12 +72,12 @@ ccl_device int bsdf_principled_sheen_setup(const ShaderData *sd, PrincipledSheen return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_principled_sheen_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_principled_sheen_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const PrincipledSheenBsdf *bsdf = (const PrincipledSheenBsdf *)sc; + ccl_private const PrincipledSheenBsdf *bsdf = (ccl_private const PrincipledSheenBsdf *)sc; float3 N = bsdf->N; float3 V = I; // outgoing @@ -93,28 +94,28 @@ ccl_device float3 bsdf_principled_sheen_eval_reflect(const ShaderClosure *sc, } } -ccl_device float3 bsdf_principled_sheen_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_principled_sheen_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_principled_sheen_sample(const ShaderClosure *sc, +ccl_device int bsdf_principled_sheen_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const PrincipledSheenBsdf *bsdf = (const PrincipledSheenBsdf *)sc; + ccl_private const PrincipledSheenBsdf *bsdf = (ccl_private const PrincipledSheenBsdf *)sc; float3 N = bsdf->N; diff --git a/intern/cycles/kernel/closure/bsdf_reflection.h b/intern/cycles/kernel/closure/bsdf_reflection.h index 31283971d5a..758bfd2b2d0 100644 --- a/intern/cycles/kernel/closure/bsdf_reflection.h +++ b/intern/cycles/kernel/closure/bsdf_reflection.h @@ -36,42 +36,42 @@ CCL_NAMESPACE_BEGIN /* REFLECTION */ -ccl_device int bsdf_reflection_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_reflection_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_REFLECTION_ID; return SD_BSDF; } -ccl_device float3 bsdf_reflection_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_reflection_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_reflection_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_reflection_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_reflection_sample(const ShaderClosure *sc, +ccl_device int bsdf_reflection_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float3 N = bsdf->N; // only one direction is possible diff --git a/intern/cycles/kernel/closure/bsdf_refraction.h b/intern/cycles/kernel/closure/bsdf_refraction.h index cfedb5dfe2c..74e149b059e 100644 --- a/intern/cycles/kernel/closure/bsdf_refraction.h +++ b/intern/cycles/kernel/closure/bsdf_refraction.h @@ -36,42 +36,42 @@ CCL_NAMESPACE_BEGIN /* REFRACTION */ -ccl_device int bsdf_refraction_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_refraction_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_REFRACTION_ID; return SD_BSDF; } -ccl_device float3 bsdf_refraction_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_refraction_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_refraction_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_refraction_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_refraction_sample(const ShaderClosure *sc, +ccl_device int bsdf_refraction_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float m_eta = bsdf->ior; float3 N = bsdf->N; diff --git a/intern/cycles/kernel/closure/bsdf_toon.h b/intern/cycles/kernel/closure/bsdf_toon.h index acdafe0f735..7f20a328b5e 100644 --- a/intern/cycles/kernel/closure/bsdf_toon.h +++ b/intern/cycles/kernel/closure/bsdf_toon.h @@ -34,7 +34,7 @@ CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct ToonBsdf { +typedef struct ToonBsdf { SHADER_CLOSURE_BASE; float size; @@ -45,7 +45,7 @@ static_assert(sizeof(ShaderClosure) >= sizeof(ToonBsdf), "ToonBsdf is too large! /* DIFFUSE TOON */ -ccl_device int bsdf_diffuse_toon_setup(ToonBsdf *bsdf) +ccl_device int bsdf_diffuse_toon_setup(ccl_private ToonBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_DIFFUSE_TOON_ID; bsdf->size = saturate(bsdf->size); @@ -73,12 +73,12 @@ ccl_device float bsdf_toon_get_sample_angle(float max_angle, float smooth) return fminf(max_angle + smooth, M_PI_2_F); } -ccl_device float3 bsdf_diffuse_toon_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_toon_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const ToonBsdf *bsdf = (const ToonBsdf *)sc; + ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; float max_angle = bsdf->size * M_PI_2_F; float smooth = bsdf->smooth * M_PI_2_F; float angle = safe_acosf(fmaxf(dot(bsdf->N, omega_in), 0.0f)); @@ -95,28 +95,28 @@ ccl_device float3 bsdf_diffuse_toon_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_diffuse_toon_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_toon_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_diffuse_toon_sample(const ShaderClosure *sc, +ccl_device int bsdf_diffuse_toon_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const ToonBsdf *bsdf = (const ToonBsdf *)sc; + ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; float max_angle = bsdf->size * M_PI_2_F; float smooth = bsdf->smooth * M_PI_2_F; float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth); @@ -143,7 +143,7 @@ ccl_device int bsdf_diffuse_toon_sample(const ShaderClosure *sc, /* GLOSSY TOON */ -ccl_device int bsdf_glossy_toon_setup(ToonBsdf *bsdf) +ccl_device int bsdf_glossy_toon_setup(ccl_private ToonBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_GLOSSY_TOON_ID; bsdf->size = saturate(bsdf->size); @@ -152,12 +152,12 @@ ccl_device int bsdf_glossy_toon_setup(ToonBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_glossy_toon_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_glossy_toon_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const ToonBsdf *bsdf = (const ToonBsdf *)sc; + ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; float max_angle = bsdf->size * M_PI_2_F; float smooth = bsdf->smooth * M_PI_2_F; float cosNI = dot(bsdf->N, omega_in); @@ -180,28 +180,28 @@ ccl_device float3 bsdf_glossy_toon_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_glossy_toon_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_glossy_toon_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_glossy_toon_sample(const ShaderClosure *sc, +ccl_device int bsdf_glossy_toon_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const ToonBsdf *bsdf = (const ToonBsdf *)sc; + ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; float max_angle = bsdf->size * M_PI_2_F; float smooth = bsdf->smooth * M_PI_2_F; float cosNO = dot(bsdf->N, I); diff --git a/intern/cycles/kernel/closure/bsdf_transparent.h b/intern/cycles/kernel/closure/bsdf_transparent.h index f1dc7efb345..8313ab964d7 100644 --- a/intern/cycles/kernel/closure/bsdf_transparent.h +++ b/intern/cycles/kernel/closure/bsdf_transparent.h @@ -34,7 +34,9 @@ CCL_NAMESPACE_BEGIN -ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight, int path_flag) +ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd, + const float3 weight, + int path_flag) { /* Check cutoff weight. */ float sample_weight = fabsf(average(weight)); @@ -47,7 +49,7 @@ ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight, int /* Add weight to existing transparent BSDF. */ for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + ccl_private ShaderClosure *sc = &sd->closure[i]; if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) { sc->weight += weight; @@ -68,7 +70,7 @@ ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight, int } /* Create new transparent BSDF. */ - ShaderClosure *bsdf = closure_alloc( + ccl_private ShaderClosure *bsdf = closure_alloc( sd, sizeof(ShaderClosure), CLOSURE_BSDF_TRANSPARENT_ID, weight); if (bsdf) { @@ -81,34 +83,34 @@ ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight, int } } -ccl_device float3 bsdf_transparent_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_transparent_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_transparent_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_transparent_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_transparent_sample(const ShaderClosure *sc, +ccl_device int bsdf_transparent_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { // only one direction is possible *omega_in = -I; diff --git a/intern/cycles/kernel/closure/bsdf_util.h b/intern/cycles/kernel/closure/bsdf_util.h index beec5f768a1..873494c1e03 100644 --- a/intern/cycles/kernel/closure/bsdf_util.h +++ b/intern/cycles/kernel/closure/bsdf_util.h @@ -37,17 +37,17 @@ CCL_NAMESPACE_BEGIN ccl_device float fresnel_dielectric(float eta, const float3 N, const float3 I, - float3 *R, - float3 *T, + ccl_private float3 *R, + ccl_private float3 *T, #ifdef __RAY_DIFFERENTIALS__ const float3 dIdx, const float3 dIdy, - float3 *dRdx, - float3 *dRdy, - float3 *dTdx, - float3 *dTdy, + ccl_private float3 *dRdx, + ccl_private float3 *dRdy, + ccl_private float3 *dTdx, + ccl_private float3 *dTdy, #endif - bool *is_inside) + ccl_private bool *is_inside) { float cos = dot(N, I), neta; float3 Nn; diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index 07415c53ec5..9df69e073c1 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct Bssrdf { +typedef struct Bssrdf { SHADER_CLOSURE_BASE; float3 radius; @@ -66,7 +66,9 @@ ccl_device float bssrdf_dipole_compute_alpha_prime(float rd, float fourthirdA) return xmid; } -ccl_device void bssrdf_setup_radius(Bssrdf *bssrdf, const ClosureType type, const float eta) +ccl_device void bssrdf_setup_radius(ccl_private Bssrdf *bssrdf, + const ClosureType type, + const float eta) { if (type == CLOSURE_BSSRDF_BURLEY_ID || type == CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) { /* Scale mean free path length so it gives similar looking result to older @@ -114,7 +116,7 @@ ccl_device_inline float3 bssrdf_burley_compatible_mfp(float3 r) return 0.25f * M_1_PI_F * r; } -ccl_device void bssrdf_burley_setup(Bssrdf *bssrdf) +ccl_device void bssrdf_burley_setup(ccl_private Bssrdf *bssrdf) { /* Mean free path length. */ const float3 l = bssrdf_burley_compatible_mfp(bssrdf->radius); @@ -195,7 +197,10 @@ ccl_device_forceinline float bssrdf_burley_root_find(float xi) return r; } -ccl_device void bssrdf_burley_sample(const float d, float xi, float *r, float *h) +ccl_device void bssrdf_burley_sample(const float d, + float xi, + ccl_private float *r, + ccl_private float *h) { const float Rm = BURLEY_TRUNCATE * d; const float r_ = bssrdf_burley_root_find(xi * BURLEY_TRUNCATE_CDF) * d; @@ -221,7 +226,10 @@ ccl_device float bssrdf_num_channels(const float3 radius) return channels; } -ccl_device void bssrdf_sample(const float3 radius, float xi, float *r, float *h) +ccl_device void bssrdf_sample(const float3 radius, + float xi, + ccl_private float *r, + ccl_private float *h) { const float num_channels = bssrdf_num_channels(radius); float sampled_radius; @@ -261,9 +269,10 @@ ccl_device_forceinline float bssrdf_pdf(const float3 radius, float r) /* Setup */ -ccl_device_inline Bssrdf *bssrdf_alloc(ShaderData *sd, float3 weight) +ccl_device_inline ccl_private Bssrdf *bssrdf_alloc(ccl_private ShaderData *sd, float3 weight) { - Bssrdf *bssrdf = (Bssrdf *)closure_alloc(sd, sizeof(Bssrdf), CLOSURE_NONE_ID, weight); + ccl_private Bssrdf *bssrdf = (ccl_private Bssrdf *)closure_alloc( + sd, sizeof(Bssrdf), CLOSURE_NONE_ID, weight); if (bssrdf == NULL) { return NULL; @@ -274,13 +283,16 @@ ccl_device_inline Bssrdf *bssrdf_alloc(ShaderData *sd, float3 weight) return (sample_weight >= CLOSURE_WEIGHT_CUTOFF) ? bssrdf : NULL; } -ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type, const float ior) +ccl_device int bssrdf_setup(ccl_private ShaderData *sd, + ccl_private Bssrdf *bssrdf, + ClosureType type, + const float ior) { int flag = 0; /* Add retro-reflection component as separate diffuse BSDF. */ if (bssrdf->roughness != FLT_MAX) { - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), bssrdf->weight); if (bsdf) { @@ -321,7 +333,7 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type, co /* Add diffuse BSDF if any radius too small. */ #ifdef __PRINCIPLED__ if (bssrdf->roughness != FLT_MAX) { - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), diffuse_weight); if (bsdf) { @@ -333,7 +345,8 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type, co else #endif /* __PRINCIPLED__ */ { - DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc(sd, sizeof(DiffuseBsdf), diffuse_weight); + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( + sd, sizeof(DiffuseBsdf), diffuse_weight); if (bsdf) { bsdf->N = bssrdf->N; diff --git a/intern/cycles/kernel/closure/emissive.h b/intern/cycles/kernel/closure/emissive.h index a2519d97618..3d56e989522 100644 --- a/intern/cycles/kernel/closure/emissive.h +++ b/intern/cycles/kernel/closure/emissive.h @@ -36,7 +36,7 @@ CCL_NAMESPACE_BEGIN /* BACKGROUND CLOSURE */ -ccl_device void background_setup(ShaderData *sd, const float3 weight) +ccl_device void background_setup(ccl_private ShaderData *sd, const float3 weight) { if (sd->flag & SD_EMISSION) { sd->closure_emission_background += weight; @@ -49,7 +49,7 @@ ccl_device void background_setup(ShaderData *sd, const float3 weight) /* EMISSION CLOSURE */ -ccl_device void emission_setup(ShaderData *sd, const float3 weight) +ccl_device void emission_setup(ccl_private ShaderData *sd, const float3 weight) { if (sd->flag & SD_EMISSION) { sd->closure_emission_background += weight; @@ -69,8 +69,11 @@ ccl_device float emissive_pdf(const float3 Ng, const float3 I) return (cosNO > 0.0f) ? 1.0f : 0.0f; } -ccl_device void emissive_sample( - const float3 Ng, float randu, float randv, float3 *omega_out, float *pdf) +ccl_device void emissive_sample(const float3 Ng, + float randu, + float randv, + ccl_private float3 *omega_out, + ccl_private float *pdf) { /* todo: not implemented and used yet */ } diff --git a/intern/cycles/kernel/closure/volume.h b/intern/cycles/kernel/closure/volume.h index 69959a3f21b..023fb3ac4ea 100644 --- a/intern/cycles/kernel/closure/volume.h +++ b/intern/cycles/kernel/closure/volume.h @@ -20,7 +20,7 @@ CCL_NAMESPACE_BEGIN /* VOLUME EXTINCTION */ -ccl_device void volume_extinction_setup(ShaderData *sd, float3 weight) +ccl_device void volume_extinction_setup(ccl_private ShaderData *sd, float3 weight) { if (sd->flag & SD_EXTINCTION) { sd->closure_transparent_extinction += weight; @@ -33,7 +33,7 @@ ccl_device void volume_extinction_setup(ShaderData *sd, float3 weight) /* HENYEY-GREENSTEIN CLOSURE */ -typedef ccl_addr_space struct HenyeyGreensteinVolume { +typedef struct HenyeyGreensteinVolume { SHADER_CLOSURE_BASE; float g; @@ -51,7 +51,7 @@ ccl_device float single_peaked_henyey_greenstein(float cos_theta, float g) (M_1_PI_F * 0.25f); }; -ccl_device int volume_henyey_greenstein_setup(HenyeyGreensteinVolume *volume) +ccl_device int volume_henyey_greenstein_setup(ccl_private HenyeyGreensteinVolume *volume) { volume->type = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; @@ -61,10 +61,10 @@ ccl_device int volume_henyey_greenstein_setup(HenyeyGreensteinVolume *volume) return SD_SCATTER; } -ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderVolumeClosure *svc, +ccl_device float3 volume_henyey_greenstein_eval_phase(ccl_private const ShaderVolumeClosure *svc, const float3 I, float3 omega_in, - float *pdf) + ccl_private float *pdf) { float g = svc->g; @@ -81,7 +81,7 @@ ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderVolumeClosure } ccl_device float3 -henyey_greenstrein_sample(float3 D, float g, float randu, float randv, float *pdf) +henyey_greenstrein_sample(float3 D, float g, float randu, float randv, ccl_private float *pdf) { /* match pdf for small g */ float cos_theta; @@ -112,17 +112,17 @@ henyey_greenstrein_sample(float3 D, float g, float randu, float randv, float *pd return dir; } -ccl_device int volume_henyey_greenstein_sample(const ShaderVolumeClosure *svc, +ccl_device int volume_henyey_greenstein_sample(ccl_private const ShaderVolumeClosure *svc, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { float g = svc->g; @@ -141,22 +141,22 @@ ccl_device int volume_henyey_greenstein_sample(const ShaderVolumeClosure *svc, /* VOLUME CLOSURE */ -ccl_device float3 volume_phase_eval(const ShaderData *sd, - const ShaderVolumeClosure *svc, +ccl_device float3 volume_phase_eval(ccl_private const ShaderData *sd, + ccl_private const ShaderVolumeClosure *svc, float3 omega_in, - float *pdf) + ccl_private float *pdf) { return volume_henyey_greenstein_eval_phase(svc, sd->I, omega_in, pdf); } -ccl_device int volume_phase_sample(const ShaderData *sd, - const ShaderVolumeClosure *svc, +ccl_device int volume_phase_sample(ccl_private const ShaderData *sd, + ccl_private const ShaderVolumeClosure *svc, float randu, float randv, - float3 *eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private differential3 *domega_in, + ccl_private float *pdf) { return volume_henyey_greenstein_sample(svc, sd->I, @@ -187,7 +187,10 @@ ccl_device float volume_channel_get(float3 value, int channel) return (channel == 0) ? value.x : ((channel == 1) ? value.y : value.z); } -ccl_device int volume_sample_channel(float3 albedo, float3 throughput, float rand, float3 *pdf) +ccl_device int volume_sample_channel(float3 albedo, + float3 throughput, + float rand, + ccl_private float3 *pdf) { /* Sample color channel proportional to throughput and single scattering * albedo, to significantly reduce noise with many bounce, following: diff --git a/intern/cycles/kernel/device/cpu/compat.h b/intern/cycles/kernel/device/cpu/compat.h index bfd936c7bbd..888c0d5d872 100644 --- a/intern/cycles/kernel/device/cpu/compat.h +++ b/intern/cycles/kernel/device/cpu/compat.h @@ -32,8 +32,6 @@ #include "util/util_texture.h" #include "util/util_types.h" -#define ccl_addr_space - /* On x86_64, versions of glibc < 2.16 have an issue where expf is * much slower than the double version. This was fixed in glibc 2.16. */ diff --git a/intern/cycles/kernel/device/cuda/compat.h b/intern/cycles/kernel/device/cuda/compat.h index 3c85a8e7bd2..685c7a5b753 100644 --- a/intern/cycles/kernel/device/cuda/compat.h +++ b/intern/cycles/kernel/device/cuda/compat.h @@ -59,7 +59,6 @@ typedef unsigned long long uint64_t; #define ccl_gpu_shared __shared__ #define ccl_private #define ccl_may_alias -#define ccl_addr_space #define ccl_restrict __restrict__ #define ccl_loop_no_unroll #define ccl_align(n) __align__(n) diff --git a/intern/cycles/kernel/device/hip/compat.h b/intern/cycles/kernel/device/hip/compat.h index 95338fe7d6e..089976d84e4 100644 --- a/intern/cycles/kernel/device/hip/compat.h +++ b/intern/cycles/kernel/device/hip/compat.h @@ -52,7 +52,6 @@ typedef unsigned long long uint64_t; #define ccl_gpu_shared __shared__ #define ccl_private #define ccl_may_alias -#define ccl_addr_space #define ccl_restrict __restrict__ #define ccl_loop_no_unroll #define ccl_align(n) __align__(n) diff --git a/intern/cycles/kernel/device/metal/compat.h b/intern/cycles/kernel/device/metal/compat.h new file mode 100644 index 00000000000..77cea30914c --- /dev/null +++ b/intern/cycles/kernel/device/metal/compat.h @@ -0,0 +1,126 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#define __KERNEL_GPU__ +#define __KERNEL_METAL__ +#define CCL_NAMESPACE_BEGIN +#define CCL_NAMESPACE_END + +#ifndef ATTR_FALLTHROUGH +# define ATTR_FALLTHROUGH +#endif + +#include +#include +#include +#include + +using namespace metal; + +#pragma clang diagnostic ignored "-Wunused-variable" +#pragma clang diagnostic ignored "-Wsign-compare" + +/* Qualifiers */ + +#define ccl_device +#define ccl_device_inline ccl_device +#define ccl_device_forceinline ccl_device +#define ccl_device_noinline ccl_device __attribute__((noinline)) +#define ccl_device_noinline_cpu ccl_device +#define ccl_global device +#define ccl_static_constant static constant constexpr +#define ccl_device_constant constant +#define ccl_constant const device +#define ccl_gpu_shared threadgroup +#define ccl_private thread +#define ccl_may_alias +#define ccl_restrict __restrict +#define ccl_loop_no_unroll +#define ccl_align(n) alignas(n) +#define ccl_optional_struct_init + +/* No assert supported for Metal */ + +#define kernel_assert(cond) + +/* make_type definitions with Metal style element initializers */ +#ifdef make_float2 +# undef make_float2 +#endif +#ifdef make_float3 +# undef make_float3 +#endif +#ifdef make_float4 +# undef make_float4 +#endif +#ifdef make_int2 +# undef make_int2 +#endif +#ifdef make_int3 +# undef make_int3 +#endif +#ifdef make_int4 +# undef make_int4 +#endif +#ifdef make_uchar4 +# undef make_uchar4 +#endif + +#define make_float2(x, y) float2(x, y) +#define make_float3(x, y, z) float3(x, y, z) +#define make_float4(x, y, z, w) float4(x, y, z, w) +#define make_int2(x, y) int2(x, y) +#define make_int3(x, y, z) int3(x, y, z) +#define make_int4(x, y, z, w) int4(x, y, z, w) +#define make_uchar4(x, y, z, w) uchar4(x, y, z, w) + +/* Math functions */ + +#define __uint_as_float(x) as_type(x) +#define __float_as_uint(x) as_type(x) +#define __int_as_float(x) as_type(x) +#define __float_as_int(x) as_type(x) +#define __float2half(x) half(x) +#define powf(x, y) pow(float(x), float(y)) +#define fabsf(x) fabs(float(x)) +#define copysignf(x, y) copysign(float(x), float(y)) +#define asinf(x) asin(float(x)) +#define acosf(x) acos(float(x)) +#define atanf(x) atan(float(x)) +#define floorf(x) floor(float(x)) +#define ceilf(x) ceil(float(x)) +#define hypotf(x, y) hypot(float(x), float(y)) +#define atan2f(x, y) atan2(float(x), float(y)) +#define fmaxf(x, y) fmax(float(x), float(y)) +#define fminf(x, y) fmin(float(x), float(y)) +#define fmodf(x, y) fmod(float(x), float(y)) +#define sinhf(x) sinh(float(x)) +#define coshf(x) cosh(float(x)) +#define tanhf(x) tanh(float(x)) + +/* Use native functions with possibly lower precision for performance, + * no issues found so far. */ +#define trigmode fast +#define sinf(x) trigmode::sin(float(x)) +#define cosf(x) trigmode::cos(float(x)) +#define tanf(x) trigmode::tan(float(x)) +#define expf(x) trigmode::exp(float(x)) +#define sqrtf(x) trigmode::sqrt(float(x)) +#define logf(x) trigmode::log(float(x)) + +#define NULL 0 diff --git a/intern/cycles/kernel/device/optix/compat.h b/intern/cycles/kernel/device/optix/compat.h index fb9e094b535..c9ec9be05df 100644 --- a/intern/cycles/kernel/device/optix/compat.h +++ b/intern/cycles/kernel/device/optix/compat.h @@ -58,7 +58,6 @@ typedef unsigned long long uint64_t; #define ccl_gpu_shared __shared__ #define ccl_private #define ccl_may_alias -#define ccl_addr_space #define ccl_restrict __restrict__ #define ccl_loop_no_unroll #define ccl_align(n) __align__(n) diff --git a/intern/cycles/kernel/geom/geom_attribute.h b/intern/cycles/kernel/geom/geom_attribute.h index 9532a21fec7..850ac44e6e0 100644 --- a/intern/cycles/kernel/geom/geom_attribute.h +++ b/intern/cycles/kernel/geom/geom_attribute.h @@ -27,9 +27,11 @@ CCL_NAMESPACE_BEGIN * Lookup of attributes is different between OSL and SVM, as OSL is ustring * based while for SVM we use integer ids. */ -ccl_device_inline uint subd_triangle_patch(const KernelGlobals *kg, const ShaderData *sd); +ccl_device_inline uint subd_triangle_patch(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd); -ccl_device_inline uint attribute_primitive_type(const KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline uint attribute_primitive_type(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { if ((sd->type & PRIMITIVE_ALL_TRIANGLE) && subd_triangle_patch(kg, sd) != ~0) { return ATTR_PRIM_SUBD; @@ -48,13 +50,13 @@ ccl_device_inline AttributeDescriptor attribute_not_found() /* Find attribute based on ID */ -ccl_device_inline uint object_attribute_map_offset(const KernelGlobals *kg, int object) +ccl_device_inline uint object_attribute_map_offset(ccl_global const KernelGlobals *kg, int object) { return kernel_tex_fetch(__objects, object).attribute_map_offset; } -ccl_device_inline AttributeDescriptor find_attribute(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline AttributeDescriptor find_attribute(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, uint id) { if (sd->object == OBJECT_NONE) { @@ -100,8 +102,8 @@ ccl_device_inline AttributeDescriptor find_attribute(const KernelGlobals *kg, /* Transform matrix attribute on meshes */ -ccl_device Transform primitive_attribute_matrix(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device Transform primitive_attribute_matrix(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc) { Transform tfm; diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h index 811558edae9..07f218d781b 100644 --- a/intern/cycles/kernel/geom/geom_curve.h +++ b/intern/cycles/kernel/geom/geom_curve.h @@ -27,11 +27,11 @@ CCL_NAMESPACE_BEGIN /* Reading attributes on various curve elements */ -ccl_device float curve_attribute_float(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float curve_attribute_float(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float *dx, - float *dy) + ccl_private float *dx, + ccl_private float *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); @@ -69,11 +69,11 @@ ccl_device float curve_attribute_float(const KernelGlobals *kg, } } -ccl_device float2 curve_attribute_float2(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float2 curve_attribute_float2(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float2 *dx, - float2 *dy) + ccl_private float2 *dx, + ccl_private float2 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); @@ -115,11 +115,11 @@ ccl_device float2 curve_attribute_float2(const KernelGlobals *kg, } } -ccl_device float3 curve_attribute_float3(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float3 curve_attribute_float3(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float3 *dx, - float3 *dy) + ccl_private float3 *dx, + ccl_private float3 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); @@ -157,11 +157,11 @@ ccl_device float3 curve_attribute_float3(const KernelGlobals *kg, } } -ccl_device float4 curve_attribute_float4(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float4 curve_attribute_float4(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float4 *dx, - float4 *dy) + ccl_private float4 *dx, + ccl_private float4 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); @@ -201,7 +201,8 @@ ccl_device float4 curve_attribute_float4(const KernelGlobals *kg, /* Curve thickness */ -ccl_device float curve_thickness(const KernelGlobals *kg, const ShaderData *sd) +ccl_device float curve_thickness(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { float r = 0.0f; @@ -229,7 +230,8 @@ ccl_device float curve_thickness(const KernelGlobals *kg, const ShaderData *sd) /* Curve location for motion pass, linear interpolation between keys and * ignoring radius because we do the same for the motion keys */ -ccl_device float3 curve_motion_center_location(const KernelGlobals *kg, const ShaderData *sd) +ccl_device float3 curve_motion_center_location(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); @@ -245,7 +247,8 @@ ccl_device float3 curve_motion_center_location(const KernelGlobals *kg, const Sh /* Curve tangent normal */ -ccl_device float3 curve_tangent_normal(const KernelGlobals *kg, const ShaderData *sd) +ccl_device float3 curve_tangent_normal(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { float3 tgN = make_float3(0.0f, 0.0f, 0.0f); @@ -265,12 +268,12 @@ ccl_device float3 curve_tangent_normal(const KernelGlobals *kg, const ShaderData /* Curve bounds utility function */ -ccl_device_inline void curvebounds(float *lower, - float *upper, - float *extremta, - float *extrema, - float *extremtb, - float *extremb, +ccl_device_inline void curvebounds(ccl_private float *lower, + ccl_private float *upper, + ccl_private float *extremta, + ccl_private float *extrema, + ccl_private float *extremtb, + ccl_private float *extremb, float p0, float p1, float p2, diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h index 30addb9616d..04af8ea1421 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/geom_curve_intersect.h @@ -86,11 +86,11 @@ ccl_device_inline bool cylinder_intersect(const float3 cylinder_start, const float3 cylinder_end, const float cylinder_radius, const float3 ray_dir, - float2 *t_o, - float *u0_o, - float3 *Ng0_o, - float *u1_o, - float3 *Ng1_o) + ccl_private float2 *t_o, + ccl_private float *u0_o, + ccl_private float3 *Ng0_o, + ccl_private float *u1_o, + ccl_private float3 *Ng1_o) { /* Calculate quadratic equation to solve. */ const float rl = 1.0f / len(cylinder_end - cylinder_start); @@ -169,13 +169,13 @@ ccl_device_inline float2 half_plane_intersect(const float3 P, const float3 N, co } ccl_device bool curve_intersect_iterative(const float3 ray_dir, - float *ray_tfar, + ccl_private float *ray_tfar, const float dt, const float4 curve[4], float u, float t, const bool use_backfacing, - Intersection *isect) + ccl_private Intersection *isect) { const float length_ray_dir = len(ray_dir); @@ -265,7 +265,7 @@ ccl_device bool curve_intersect_recursive(const float3 ray_orig, const float3 ray_dir, float ray_tfar, float4 curve[4], - Intersection *isect) + ccl_private Intersection *isect) { /* Move ray closer to make intersection stable. */ const float3 center = float4_to_float3(0.25f * (curve[0] + curve[1] + curve[2] + curve[3])); @@ -474,9 +474,9 @@ ccl_device_inline bool ribbon_intersect_quad(const float ray_tfar, const float3 quad_v1, const float3 quad_v2, const float3 quad_v3, - float *u_o, - float *v_o, - float *t_o) + ccl_private float *u_o, + ccl_private float *v_o, + ccl_private float *t_o) { /* Calculate vertices relative to ray origin? */ const float3 O = make_float3(0.0f, 0.0f, 0.0f); @@ -550,7 +550,7 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, float ray_tfar, const int N, float4 curve[4], - Intersection *isect) + ccl_private Intersection *isect) { /* Transform control points into ray space. */ float3 ray_space[3]; @@ -625,8 +625,8 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, return false; } -ccl_device_forceinline bool curve_intersect(const KernelGlobals *kg, - Intersection *isect, +ccl_device_forceinline bool curve_intersect(ccl_global const KernelGlobals *kg, + ccl_private Intersection *isect, const float3 P, const float3 dir, const float tmax, @@ -679,8 +679,8 @@ ccl_device_forceinline bool curve_intersect(const KernelGlobals *kg, } } -ccl_device_inline void curve_shader_setup(const KernelGlobals *kg, - ShaderData *sd, +ccl_device_inline void curve_shader_setup(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, float3 P, float3 D, float t, diff --git a/intern/cycles/kernel/geom/geom_motion_curve.h b/intern/cycles/kernel/geom/geom_motion_curve.h index 5294da03145..8e32df439cd 100644 --- a/intern/cycles/kernel/geom/geom_motion_curve.h +++ b/intern/cycles/kernel/geom/geom_motion_curve.h @@ -27,10 +27,10 @@ CCL_NAMESPACE_BEGIN #ifdef __HAIR__ -ccl_device_inline int find_attribute_curve_motion(const KernelGlobals *kg, +ccl_device_inline int find_attribute_curve_motion(ccl_global const KernelGlobals *kg, int object, uint id, - AttributeElement *elem) + ccl_private AttributeElement *elem) { /* todo: find a better (faster) solution for this, maybe store offset per object. * @@ -52,7 +52,7 @@ ccl_device_inline int find_attribute_curve_motion(const KernelGlobals *kg, return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; } -ccl_device_inline void motion_curve_keys_for_step_linear(const KernelGlobals *kg, +ccl_device_inline void motion_curve_keys_for_step_linear(ccl_global const KernelGlobals *kg, int offset, int numkeys, int numsteps, @@ -79,8 +79,13 @@ ccl_device_inline void motion_curve_keys_for_step_linear(const KernelGlobals *kg } /* return 2 curve key locations */ -ccl_device_inline void motion_curve_keys_linear( - const KernelGlobals *kg, int object, int prim, float time, int k0, int k1, float4 keys[2]) +ccl_device_inline void motion_curve_keys_linear(ccl_global const KernelGlobals *kg, + int object, + int prim, + float time, + int k0, + int k1, + float4 keys[2]) { /* get motion info */ int numsteps, numkeys; @@ -107,7 +112,7 @@ ccl_device_inline void motion_curve_keys_linear( keys[1] = (1.0f - t) * keys[1] + t * next_keys[1]; } -ccl_device_inline void motion_curve_keys_for_step(const KernelGlobals *kg, +ccl_device_inline void motion_curve_keys_for_step(ccl_global const KernelGlobals *kg, int offset, int numkeys, int numsteps, @@ -140,7 +145,7 @@ ccl_device_inline void motion_curve_keys_for_step(const KernelGlobals *kg, } /* return 2 curve key locations */ -ccl_device_inline void motion_curve_keys(const KernelGlobals *kg, +ccl_device_inline void motion_curve_keys(ccl_global const KernelGlobals *kg, int object, int prim, float time, diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/geom_motion_triangle.h index b7f182090aa..161b358110d 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle.h @@ -31,10 +31,10 @@ CCL_NAMESPACE_BEGIN /* Time interpolation of vertex positions and normals */ -ccl_device_inline int find_attribute_motion(const KernelGlobals *kg, +ccl_device_inline int find_attribute_motion(ccl_global const KernelGlobals *kg, int object, uint id, - AttributeElement *elem) + ccl_private AttributeElement *elem) { /* todo: find a better (faster) solution for this, maybe store offset per object */ uint attr_offset = object_attribute_map_offset(kg, object); @@ -62,7 +62,7 @@ ccl_device_inline int find_attribute_motion(const KernelGlobals *kg, return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; } -ccl_device_inline void motion_triangle_verts_for_step(const KernelGlobals *kg, +ccl_device_inline void motion_triangle_verts_for_step(ccl_global const KernelGlobals *kg, uint4 tri_vindex, int offset, int numverts, @@ -89,7 +89,7 @@ ccl_device_inline void motion_triangle_verts_for_step(const KernelGlobals *kg, } } -ccl_device_inline void motion_triangle_normals_for_step(const KernelGlobals *kg, +ccl_device_inline void motion_triangle_normals_for_step(ccl_global const KernelGlobals *kg, uint4 tri_vindex, int offset, int numverts, @@ -117,7 +117,7 @@ ccl_device_inline void motion_triangle_normals_for_step(const KernelGlobals *kg, } ccl_device_inline void motion_triangle_vertices( - const KernelGlobals *kg, int object, int prim, float time, float3 verts[3]) + ccl_global const KernelGlobals *kg, int object, int prim, float time, float3 verts[3]) { /* get motion info */ int numsteps, numverts; @@ -146,8 +146,13 @@ ccl_device_inline void motion_triangle_vertices( verts[2] = (1.0f - t) * verts[2] + t * next_verts[2]; } -ccl_device_inline float3 motion_triangle_smooth_normal( - const KernelGlobals *kg, float3 Ng, int object, int prim, float u, float v, float time) +ccl_device_inline float3 motion_triangle_smooth_normal(ccl_global const KernelGlobals *kg, + float3 Ng, + int object, + int prim, + float u, + float v, + float time) { /* get motion info */ int numsteps, numverts; diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h index 6fb9756ff92..94d00875f0a 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h @@ -34,8 +34,8 @@ CCL_NAMESPACE_BEGIN * a closer distance. */ -ccl_device_inline float3 motion_triangle_refine(const KernelGlobals *kg, - ShaderData *sd, +ccl_device_inline float3 motion_triangle_refine(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, float3 P, float3 D, float t, @@ -92,8 +92,8 @@ ccl_device_noinline ccl_device_inline # endif float3 - motion_triangle_refine_local(const KernelGlobals *kg, - ShaderData *sd, + motion_triangle_refine_local(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, float3 P, float3 D, float t, @@ -145,8 +145,8 @@ ccl_device_inline * time and do a ray intersection with the resulting triangle. */ -ccl_device_inline bool motion_triangle_intersect(const KernelGlobals *kg, - Intersection *isect, +ccl_device_inline bool motion_triangle_intersect(ccl_global const KernelGlobals *kg, + ccl_private Intersection *isect, float3 P, float3 dir, float tmax, @@ -202,8 +202,8 @@ ccl_device_inline bool motion_triangle_intersect(const KernelGlobals *kg, * Returns whether traversal should be stopped. */ #ifdef __BVH_LOCAL__ -ccl_device_inline bool motion_triangle_intersect_local(const KernelGlobals *kg, - LocalIntersection *local_isect, +ccl_device_inline bool motion_triangle_intersect_local(ccl_global const KernelGlobals *kg, + ccl_private LocalIntersection *local_isect, float3 P, float3 dir, float time, @@ -211,7 +211,7 @@ ccl_device_inline bool motion_triangle_intersect_local(const KernelGlobals *kg, int local_object, int prim_addr, float tmax, - uint *lcg_state, + ccl_private uint *lcg_state, int max_hits) { /* Only intersect with matching object, for instanced objects we @@ -285,7 +285,7 @@ ccl_device_inline bool motion_triangle_intersect_local(const KernelGlobals *kg, } /* Record intersection. */ - Intersection *isect = &local_isect->hits[hit]; + ccl_private Intersection *isect = &local_isect->hits[hit]; isect->t = t; isect->u = u; isect->v = v; diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h b/intern/cycles/kernel/geom/geom_motion_triangle_shader.h index 85c4f0ca522..03bb1fba2a2 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle_shader.h @@ -34,8 +34,8 @@ CCL_NAMESPACE_BEGIN * normals */ /* return 3 triangle vertex normals */ -ccl_device_noinline void motion_triangle_shader_setup(const KernelGlobals *kg, - ShaderData *sd, +ccl_device_noinline void motion_triangle_shader_setup(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, const float3 P, const float3 D, const float ray_t, diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 7d6ad7b4fe3..730c01d4709 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -37,7 +37,7 @@ enum ObjectVectorTransform { OBJECT_PASS_MOTION_PRE = 0, OBJECT_PASS_MOTION_POST /* Object to world space transformation */ -ccl_device_inline Transform object_fetch_transform(const KernelGlobals *kg, +ccl_device_inline Transform object_fetch_transform(ccl_global const KernelGlobals *kg, int object, enum ObjectTransform type) { @@ -51,7 +51,9 @@ ccl_device_inline Transform object_fetch_transform(const KernelGlobals *kg, /* Lamp to world space transformation */ -ccl_device_inline Transform lamp_fetch_transform(const KernelGlobals *kg, int lamp, bool inverse) +ccl_device_inline Transform lamp_fetch_transform(ccl_global const KernelGlobals *kg, + int lamp, + bool inverse) { if (inverse) { return kernel_tex_fetch(__lights, lamp).itfm; @@ -63,7 +65,7 @@ ccl_device_inline Transform lamp_fetch_transform(const KernelGlobals *kg, int la /* Object to world space transformation for motion vectors */ -ccl_device_inline Transform object_fetch_motion_pass_transform(const KernelGlobals *kg, +ccl_device_inline Transform object_fetch_motion_pass_transform(ccl_global const KernelGlobals *kg, int object, enum ObjectVectorTransform type) { @@ -74,12 +76,12 @@ ccl_device_inline Transform object_fetch_motion_pass_transform(const KernelGloba /* Motion blurred object transformations */ #ifdef __OBJECT_MOTION__ -ccl_device_inline Transform object_fetch_transform_motion(const KernelGlobals *kg, +ccl_device_inline Transform object_fetch_transform_motion(ccl_global const KernelGlobals *kg, int object, float time) { const uint motion_offset = kernel_tex_fetch(__objects, object).motion_offset; - const ccl_global DecomposedTransform *motion = &kernel_tex_fetch(__object_motion, motion_offset); + ccl_global const DecomposedTransform *motion = &kernel_tex_fetch(__object_motion, motion_offset); const uint num_steps = kernel_tex_fetch(__objects, object).numsteps * 2 + 1; Transform tfm; @@ -88,10 +90,10 @@ ccl_device_inline Transform object_fetch_transform_motion(const KernelGlobals *k return tfm; } -ccl_device_inline Transform object_fetch_transform_motion_test(const KernelGlobals *kg, +ccl_device_inline Transform object_fetch_transform_motion_test(ccl_global const KernelGlobals *kg, int object, float time, - Transform *itfm) + ccl_private Transform *itfm) { int object_flag = kernel_tex_fetch(__object_flag, object); if (object_flag & SD_OBJECT_MOTION) { @@ -115,7 +117,8 @@ ccl_device_inline Transform object_fetch_transform_motion_test(const KernelGloba /* Get transform matrix for shading point. */ -ccl_device_inline Transform object_get_transform(const KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline Transform object_get_transform(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { #ifdef __OBJECT_MOTION__ return (sd->object_flag & SD_OBJECT_MOTION) ? @@ -126,8 +129,8 @@ ccl_device_inline Transform object_get_transform(const KernelGlobals *kg, const #endif } -ccl_device_inline Transform object_get_inverse_transform(const KernelGlobals *kg, - const ShaderData *sd) +ccl_device_inline Transform object_get_inverse_transform(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { #ifdef __OBJECT_MOTION__ return (sd->object_flag & SD_OBJECT_MOTION) ? @@ -139,9 +142,9 @@ ccl_device_inline Transform object_get_inverse_transform(const KernelGlobals *kg } /* Transform position from object to world space */ -ccl_device_inline void object_position_transform(const KernelGlobals *kg, - const ShaderData *sd, - float3 *P) +ccl_device_inline void object_position_transform(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + ccl_private float3 *P) { #ifdef __OBJECT_MOTION__ if (sd->object_flag & SD_OBJECT_MOTION) { @@ -156,9 +159,9 @@ ccl_device_inline void object_position_transform(const KernelGlobals *kg, /* Transform position from world to object space */ -ccl_device_inline void object_inverse_position_transform(const KernelGlobals *kg, - const ShaderData *sd, - float3 *P) +ccl_device_inline void object_inverse_position_transform(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + ccl_private float3 *P) { #ifdef __OBJECT_MOTION__ if (sd->object_flag & SD_OBJECT_MOTION) { @@ -173,9 +176,9 @@ ccl_device_inline void object_inverse_position_transform(const KernelGlobals *kg /* Transform normal from world to object space */ -ccl_device_inline void object_inverse_normal_transform(const KernelGlobals *kg, - const ShaderData *sd, - float3 *N) +ccl_device_inline void object_inverse_normal_transform(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + ccl_private float3 *N) { #ifdef __OBJECT_MOTION__ if (sd->object_flag & SD_OBJECT_MOTION) { @@ -198,9 +201,9 @@ ccl_device_inline void object_inverse_normal_transform(const KernelGlobals *kg, /* Transform normal from object to world space */ -ccl_device_inline void object_normal_transform(const KernelGlobals *kg, - const ShaderData *sd, - float3 *N) +ccl_device_inline void object_normal_transform(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + ccl_private float3 *N) { #ifdef __OBJECT_MOTION__ if (sd->object_flag & SD_OBJECT_MOTION) { @@ -215,9 +218,9 @@ ccl_device_inline void object_normal_transform(const KernelGlobals *kg, /* Transform direction vector from object to world space */ -ccl_device_inline void object_dir_transform(const KernelGlobals *kg, - const ShaderData *sd, - float3 *D) +ccl_device_inline void object_dir_transform(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + ccl_private float3 *D) { #ifdef __OBJECT_MOTION__ if (sd->object_flag & SD_OBJECT_MOTION) { @@ -232,9 +235,9 @@ ccl_device_inline void object_dir_transform(const KernelGlobals *kg, /* Transform direction vector from world to object space */ -ccl_device_inline void object_inverse_dir_transform(const KernelGlobals *kg, - const ShaderData *sd, - float3 *D) +ccl_device_inline void object_inverse_dir_transform(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + ccl_private float3 *D) { #ifdef __OBJECT_MOTION__ if (sd->object_flag & SD_OBJECT_MOTION) { @@ -249,7 +252,8 @@ ccl_device_inline void object_inverse_dir_transform(const KernelGlobals *kg, /* Object center position */ -ccl_device_inline float3 object_location(const KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline float3 object_location(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { if (sd->object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); @@ -266,18 +270,18 @@ ccl_device_inline float3 object_location(const KernelGlobals *kg, const ShaderDa /* Color of the object */ -ccl_device_inline float3 object_color(const KernelGlobals *kg, int object) +ccl_device_inline float3 object_color(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); - const ccl_global KernelObject *kobject = &kernel_tex_fetch(__objects, object); + ccl_global const KernelObject *kobject = &kernel_tex_fetch(__objects, object); return make_float3(kobject->color[0], kobject->color[1], kobject->color[2]); } /* Pass ID number of object */ -ccl_device_inline float object_pass_id(const KernelGlobals *kg, int object) +ccl_device_inline float object_pass_id(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -287,7 +291,7 @@ ccl_device_inline float object_pass_id(const KernelGlobals *kg, int object) /* Per lamp random number for shader variation */ -ccl_device_inline float lamp_random_number(const KernelGlobals *kg, int lamp) +ccl_device_inline float lamp_random_number(ccl_global const KernelGlobals *kg, int lamp) { if (lamp == LAMP_NONE) return 0.0f; @@ -297,7 +301,7 @@ ccl_device_inline float lamp_random_number(const KernelGlobals *kg, int lamp) /* Per object random number for shader variation */ -ccl_device_inline float object_random_number(const KernelGlobals *kg, int object) +ccl_device_inline float object_random_number(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -307,7 +311,7 @@ ccl_device_inline float object_random_number(const KernelGlobals *kg, int object /* Particle ID from which this object was generated */ -ccl_device_inline int object_particle_id(const KernelGlobals *kg, int object) +ccl_device_inline int object_particle_id(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0; @@ -317,31 +321,34 @@ ccl_device_inline int object_particle_id(const KernelGlobals *kg, int object) /* Generated texture coordinate on surface from where object was instanced */ -ccl_device_inline float3 object_dupli_generated(const KernelGlobals *kg, int object) +ccl_device_inline float3 object_dupli_generated(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); - const ccl_global KernelObject *kobject = &kernel_tex_fetch(__objects, object); + ccl_global const KernelObject *kobject = &kernel_tex_fetch(__objects, object); return make_float3( kobject->dupli_generated[0], kobject->dupli_generated[1], kobject->dupli_generated[2]); } /* UV texture coordinate on surface from where object was instanced */ -ccl_device_inline float3 object_dupli_uv(const KernelGlobals *kg, int object) +ccl_device_inline float3 object_dupli_uv(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); - const ccl_global KernelObject *kobject = &kernel_tex_fetch(__objects, object); + ccl_global const KernelObject *kobject = &kernel_tex_fetch(__objects, object); return make_float3(kobject->dupli_uv[0], kobject->dupli_uv[1], 0.0f); } /* Information about mesh for motion blurred triangles and curves */ -ccl_device_inline void object_motion_info( - const KernelGlobals *kg, int object, int *numsteps, int *numverts, int *numkeys) +ccl_device_inline void object_motion_info(ccl_global const KernelGlobals *kg, + int object, + ccl_private int *numsteps, + ccl_private int *numverts, + ccl_private int *numkeys) { if (numkeys) { *numkeys = kernel_tex_fetch(__objects, object).numkeys; @@ -355,7 +362,7 @@ ccl_device_inline void object_motion_info( /* Offset to an objects patch map */ -ccl_device_inline uint object_patch_map_offset(const KernelGlobals *kg, int object) +ccl_device_inline uint object_patch_map_offset(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0; @@ -365,7 +372,7 @@ ccl_device_inline uint object_patch_map_offset(const KernelGlobals *kg, int obje /* Volume step size */ -ccl_device_inline float object_volume_density(const KernelGlobals *kg, int object) +ccl_device_inline float object_volume_density(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) { return 1.0f; @@ -374,7 +381,7 @@ ccl_device_inline float object_volume_density(const KernelGlobals *kg, int objec return kernel_tex_fetch(__objects, object).volume_density; } -ccl_device_inline float object_volume_step_size(const KernelGlobals *kg, int object) +ccl_device_inline float object_volume_step_size(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) { return kernel_data.background.volume_step_size; @@ -385,14 +392,14 @@ ccl_device_inline float object_volume_step_size(const KernelGlobals *kg, int obj /* Pass ID for shader */ -ccl_device int shader_pass_id(const KernelGlobals *kg, const ShaderData *sd) +ccl_device int shader_pass_id(ccl_global const KernelGlobals *kg, ccl_private const ShaderData *sd) { return kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).pass_id; } /* Cryptomatte ID */ -ccl_device_inline float object_cryptomatte_id(const KernelGlobals *kg, int object) +ccl_device_inline float object_cryptomatte_id(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -400,7 +407,7 @@ ccl_device_inline float object_cryptomatte_id(const KernelGlobals *kg, int objec return kernel_tex_fetch(__objects, object).cryptomatte_object; } -ccl_device_inline float object_cryptomatte_asset_id(const KernelGlobals *kg, int object) +ccl_device_inline float object_cryptomatte_asset_id(ccl_global const KernelGlobals *kg, int object) { if (object == OBJECT_NONE) return 0; @@ -410,42 +417,42 @@ ccl_device_inline float object_cryptomatte_asset_id(const KernelGlobals *kg, int /* Particle data from which object was instanced */ -ccl_device_inline uint particle_index(const KernelGlobals *kg, int particle) +ccl_device_inline uint particle_index(ccl_global const KernelGlobals *kg, int particle) { return kernel_tex_fetch(__particles, particle).index; } -ccl_device float particle_age(const KernelGlobals *kg, int particle) +ccl_device float particle_age(ccl_global const KernelGlobals *kg, int particle) { return kernel_tex_fetch(__particles, particle).age; } -ccl_device float particle_lifetime(const KernelGlobals *kg, int particle) +ccl_device float particle_lifetime(ccl_global const KernelGlobals *kg, int particle) { return kernel_tex_fetch(__particles, particle).lifetime; } -ccl_device float particle_size(const KernelGlobals *kg, int particle) +ccl_device float particle_size(ccl_global const KernelGlobals *kg, int particle) { return kernel_tex_fetch(__particles, particle).size; } -ccl_device float4 particle_rotation(const KernelGlobals *kg, int particle) +ccl_device float4 particle_rotation(ccl_global const KernelGlobals *kg, int particle) { return kernel_tex_fetch(__particles, particle).rotation; } -ccl_device float3 particle_location(const KernelGlobals *kg, int particle) +ccl_device float3 particle_location(ccl_global const KernelGlobals *kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).location); } -ccl_device float3 particle_velocity(const KernelGlobals *kg, int particle) +ccl_device float3 particle_velocity(ccl_global const KernelGlobals *kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).velocity); } -ccl_device float3 particle_angular_velocity(const KernelGlobals *kg, int particle) +ccl_device float3 particle_angular_velocity(ccl_global const KernelGlobals *kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).angular_velocity); } @@ -467,8 +474,12 @@ ccl_device_inline float3 bvh_inverse_direction(float3 dir) /* Transform ray into object space to enter static object in BVH */ -ccl_device_inline float bvh_instance_push( - const KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *dir, float3 *idir) +ccl_device_inline float bvh_instance_push(ccl_global const KernelGlobals *kg, + int object, + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir) { Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); @@ -483,12 +494,12 @@ ccl_device_inline float bvh_instance_push( /* Transform ray to exit static object in BVH. */ -ccl_device_inline float bvh_instance_pop(const KernelGlobals *kg, +ccl_device_inline float bvh_instance_pop(ccl_global const KernelGlobals *kg, int object, - const Ray *ray, - float3 *P, - float3 *dir, - float3 *idir, + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir, float t) { if (t != FLT_MAX) { @@ -505,13 +516,13 @@ ccl_device_inline float bvh_instance_pop(const KernelGlobals *kg, /* Same as above, but returns scale factor to apply to multiple intersection distances */ -ccl_device_inline void bvh_instance_pop_factor(const KernelGlobals *kg, +ccl_device_inline void bvh_instance_pop_factor(ccl_global const KernelGlobals *kg, int object, - const Ray *ray, - float3 *P, - float3 *dir, - float3 *idir, - float *t_fac) + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir, + ccl_private float *t_fac) { Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); *t_fac = 1.0f / len(transform_direction(&tfm, ray->D)); @@ -524,13 +535,13 @@ ccl_device_inline void bvh_instance_pop_factor(const KernelGlobals *kg, #ifdef __OBJECT_MOTION__ /* Transform ray into object space to enter motion blurred object in BVH */ -ccl_device_inline float bvh_instance_motion_push(const KernelGlobals *kg, +ccl_device_inline float bvh_instance_motion_push(ccl_global const KernelGlobals *kg, int object, - const Ray *ray, - float3 *P, - float3 *dir, - float3 *idir, - Transform *itfm) + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir, + ccl_private Transform *itfm) { object_fetch_transform_motion_test(kg, object, ray->time, itfm); @@ -545,14 +556,14 @@ ccl_device_inline float bvh_instance_motion_push(const KernelGlobals *kg, /* Transform ray to exit motion blurred object in BVH. */ -ccl_device_inline float bvh_instance_motion_pop(const KernelGlobals *kg, +ccl_device_inline float bvh_instance_motion_pop(ccl_global const KernelGlobals *kg, int object, - const Ray *ray, - float3 *P, - float3 *dir, - float3 *idir, + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir, float t, - Transform *itfm) + ccl_private Transform *itfm) { if (t != FLT_MAX) { t /= len(transform_direction(itfm, ray->D)); @@ -567,14 +578,14 @@ ccl_device_inline float bvh_instance_motion_pop(const KernelGlobals *kg, /* Same as above, but returns scale factor to apply to multiple intersection distances */ -ccl_device_inline void bvh_instance_motion_pop_factor(const KernelGlobals *kg, +ccl_device_inline void bvh_instance_motion_pop_factor(ccl_global const KernelGlobals *kg, int object, - const Ray *ray, - float3 *P, - float3 *dir, - float3 *idir, - float *t_fac, - Transform *itfm) + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir, + ccl_private float *t_fac, + ccl_private Transform *itfm) { *t_fac = 1.0f / len(transform_direction(itfm, ray->D)); *P = ray->P; diff --git a/intern/cycles/kernel/geom/geom_patch.h b/intern/cycles/kernel/geom/geom_patch.h index ce0fc15f196..b54eafd6220 100644 --- a/intern/cycles/kernel/geom/geom_patch.h +++ b/intern/cycles/kernel/geom/geom_patch.h @@ -32,7 +32,9 @@ typedef struct PatchHandle { int array_index, patch_index, vert_index; } PatchHandle; -ccl_device_inline int patch_map_resolve_quadrant(float median, float *u, float *v) +ccl_device_inline int patch_map_resolve_quadrant(float median, + ccl_private float *u, + ccl_private float *v) { int quadrant = -1; @@ -62,7 +64,7 @@ ccl_device_inline int patch_map_resolve_quadrant(float median, float *u, float * /* retrieve PatchHandle from patch coords */ ccl_device_inline PatchHandle -patch_map_find_patch(const KernelGlobals *kg, int object, int patch, float u, float v) +patch_map_find_patch(ccl_global const KernelGlobals *kg, int object, int patch, float u, float v) { PatchHandle handle; @@ -108,7 +110,9 @@ patch_map_find_patch(const KernelGlobals *kg, int object, int patch, float u, fl return handle; } -ccl_device_inline void patch_eval_bspline_weights(float t, float *point, float *deriv) +ccl_device_inline void patch_eval_bspline_weights(float t, + ccl_private float *point, + ccl_private float *deriv) { /* The four uniform cubic B-Spline basis functions evaluated at t */ float inv_6 = 1.0f / 6.0f; @@ -128,7 +132,9 @@ ccl_device_inline void patch_eval_bspline_weights(float t, float *point, float * deriv[3] = 0.5f * t2; } -ccl_device_inline void patch_eval_adjust_boundary_weights(uint bits, float *s, float *t) +ccl_device_inline void patch_eval_adjust_boundary_weights(uint bits, + ccl_private float *s, + ccl_private float *t) { int boundary = ((bits >> 8) & 0xf); @@ -175,7 +181,9 @@ ccl_device_inline float patch_eval_param_fraction(uint patch_bits) } } -ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, float *u, float *v) +ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, + ccl_private float *u, + ccl_private float *v) { float frac = patch_eval_param_fraction(patch_bits); @@ -193,8 +201,8 @@ ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, float *u, fl /* retrieve patch control indices */ -ccl_device_inline int patch_eval_indices(const KernelGlobals *kg, - const PatchHandle *handle, +ccl_device_inline int patch_eval_indices(ccl_global const KernelGlobals *kg, + ccl_private const PatchHandle *handle, int channel, int indices[PATCH_MAX_CONTROL_VERTS]) { @@ -210,8 +218,8 @@ ccl_device_inline int patch_eval_indices(const KernelGlobals *kg, /* evaluate patch basis functions */ -ccl_device_inline void patch_eval_basis(const KernelGlobals *kg, - const PatchHandle *handle, +ccl_device_inline void patch_eval_basis(ccl_global const KernelGlobals *kg, + ccl_private const PatchHandle *handle, float u, float v, float weights[PATCH_MAX_CONTROL_VERTS], @@ -249,7 +257,7 @@ ccl_device_inline void patch_eval_basis(const KernelGlobals *kg, /* generic function for evaluating indices and weights from patch coords */ -ccl_device_inline int patch_eval_control_verts(const KernelGlobals *kg, +ccl_device_inline int patch_eval_control_verts(ccl_global const KernelGlobals *kg, int object, int patch, float u, @@ -271,15 +279,15 @@ ccl_device_inline int patch_eval_control_verts(const KernelGlobals *kg, /* functions for evaluating attributes on patches */ -ccl_device float patch_eval_float(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float patch_eval_float(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, int offset, int patch, float u, float v, int channel, - float *du, - float *dv) + ccl_private float *du, + ccl_private float *dv) { int indices[PATCH_MAX_CONTROL_VERTS]; float weights[PATCH_MAX_CONTROL_VERTS]; @@ -308,15 +316,15 @@ ccl_device float patch_eval_float(const KernelGlobals *kg, return val; } -ccl_device float2 patch_eval_float2(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float2 patch_eval_float2(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, int offset, int patch, float u, float v, int channel, - float2 *du, - float2 *dv) + ccl_private float2 *du, + ccl_private float2 *dv) { int indices[PATCH_MAX_CONTROL_VERTS]; float weights[PATCH_MAX_CONTROL_VERTS]; @@ -345,15 +353,15 @@ ccl_device float2 patch_eval_float2(const KernelGlobals *kg, return val; } -ccl_device float3 patch_eval_float3(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float3 patch_eval_float3(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, int offset, int patch, float u, float v, int channel, - float3 *du, - float3 *dv) + ccl_private float3 *du, + ccl_private float3 *dv) { int indices[PATCH_MAX_CONTROL_VERTS]; float weights[PATCH_MAX_CONTROL_VERTS]; @@ -382,15 +390,15 @@ ccl_device float3 patch_eval_float3(const KernelGlobals *kg, return val; } -ccl_device float4 patch_eval_float4(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float4 patch_eval_float4(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, int offset, int patch, float u, float v, int channel, - float4 *du, - float4 *dv) + ccl_private float4 *du, + ccl_private float4 *dv) { int indices[PATCH_MAX_CONTROL_VERTS]; float weights[PATCH_MAX_CONTROL_VERTS]; @@ -419,15 +427,15 @@ ccl_device float4 patch_eval_float4(const KernelGlobals *kg, return val; } -ccl_device float4 patch_eval_uchar4(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float4 patch_eval_uchar4(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, int offset, int patch, float u, float v, int channel, - float4 *du, - float4 *dv) + ccl_private float4 *du, + ccl_private float4 *dv) { int indices[PATCH_MAX_CONTROL_VERTS]; float weights[PATCH_MAX_CONTROL_VERTS]; diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h index ba31b12e817..869b911f76f 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/geom_primitive.h @@ -31,11 +31,11 @@ CCL_NAMESPACE_BEGIN * attributes for performance, mainly for GPU performance to avoid bringing in * heavy volume interpolation code. */ -ccl_device_inline float primitive_surface_attribute_float(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float primitive_surface_attribute_float(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float *dx, - float *dy) + ccl_private float *dx, + ccl_private float *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -57,11 +57,11 @@ ccl_device_inline float primitive_surface_attribute_float(const KernelGlobals *k } } -ccl_device_inline float2 primitive_surface_attribute_float2(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float2 primitive_surface_attribute_float2(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float2 *dx, - float2 *dy) + ccl_private float2 *dx, + ccl_private float2 *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -83,11 +83,11 @@ ccl_device_inline float2 primitive_surface_attribute_float2(const KernelGlobals } } -ccl_device_inline float3 primitive_surface_attribute_float3(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float3 primitive_surface_attribute_float3(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float3 *dx, - float3 *dy) + ccl_private float3 *dx, + ccl_private float3 *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -109,11 +109,12 @@ ccl_device_inline float3 primitive_surface_attribute_float3(const KernelGlobals } } -ccl_device_forceinline float4 primitive_surface_attribute_float4(const KernelGlobals *kg, - const ShaderData *sd, - const AttributeDescriptor desc, - float4 *dx, - float4 *dy) +ccl_device_forceinline float4 +primitive_surface_attribute_float4(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + const AttributeDescriptor desc, + ccl_private float4 *dx, + ccl_private float4 *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -142,14 +143,14 @@ ccl_device_forceinline float4 primitive_surface_attribute_float4(const KernelGlo * attributes for performance, mainly for GPU performance to avoid bringing in * heavy volume interpolation code. */ -ccl_device_inline bool primitive_is_volume_attribute(const ShaderData *sd, +ccl_device_inline bool primitive_is_volume_attribute(ccl_private const ShaderData *sd, const AttributeDescriptor desc) { return sd->type == PRIMITIVE_VOLUME; } -ccl_device_inline float primitive_volume_attribute_float(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float primitive_volume_attribute_float(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc) { if (primitive_is_volume_attribute(sd, desc)) { @@ -160,8 +161,8 @@ ccl_device_inline float primitive_volume_attribute_float(const KernelGlobals *kg } } -ccl_device_inline float3 primitive_volume_attribute_float3(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float3 primitive_volume_attribute_float3(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc) { if (primitive_is_volume_attribute(sd, desc)) { @@ -172,8 +173,8 @@ ccl_device_inline float3 primitive_volume_attribute_float3(const KernelGlobals * } } -ccl_device_inline float4 primitive_volume_attribute_float4(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float4 primitive_volume_attribute_float4(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc) { if (primitive_is_volume_attribute(sd, desc)) { @@ -187,7 +188,8 @@ ccl_device_inline float4 primitive_volume_attribute_float4(const KernelGlobals * /* Default UV coordinate */ -ccl_device_inline float3 primitive_uv(const KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline float3 primitive_uv(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_UV); @@ -200,7 +202,10 @@ ccl_device_inline float3 primitive_uv(const KernelGlobals *kg, const ShaderData /* Ptex coordinates */ -ccl_device bool primitive_ptex(const KernelGlobals *kg, ShaderData *sd, float2 *uv, int *face_id) +ccl_device bool primitive_ptex(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float2 *uv, + ccl_private int *face_id) { /* storing ptex data as attributes is not memory efficient but simple for tests */ const AttributeDescriptor desc_face_id = find_attribute(kg, sd, ATTR_STD_PTEX_FACE_ID); @@ -220,7 +225,7 @@ ccl_device bool primitive_ptex(const KernelGlobals *kg, ShaderData *sd, float2 * /* Surface tangent */ -ccl_device float3 primitive_tangent(const KernelGlobals *kg, ShaderData *sd) +ccl_device float3 primitive_tangent(ccl_global const KernelGlobals *kg, ccl_private ShaderData *sd) { #ifdef __HAIR__ if (sd->type & PRIMITIVE_ALL_CURVE) @@ -252,7 +257,8 @@ ccl_device float3 primitive_tangent(const KernelGlobals *kg, ShaderData *sd) /* Motion vector for motion pass */ -ccl_device_inline float4 primitive_motion_vector(const KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline float4 primitive_motion_vector(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { /* center position */ float3 center; diff --git a/intern/cycles/kernel/geom/geom_shader_data.h b/intern/cycles/kernel/geom/geom_shader_data.h index f78d194359d..2cf60e263c3 100644 --- a/intern/cycles/kernel/geom/geom_shader_data.h +++ b/intern/cycles/kernel/geom/geom_shader_data.h @@ -25,8 +25,8 @@ CCL_NAMESPACE_BEGIN /* ShaderData setup from incoming ray */ #ifdef __OBJECT_MOTION__ -ccl_device void shader_setup_object_transforms(const KernelGlobals *ccl_restrict kg, - ShaderData *ccl_restrict sd, +ccl_device void shader_setup_object_transforms(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private ShaderData *ccl_restrict sd, float time) { if (sd->object_flag & SD_OBJECT_MOTION) { @@ -38,10 +38,10 @@ ccl_device void shader_setup_object_transforms(const KernelGlobals *ccl_restrict /* TODO: break this up if it helps reduce register pressure to load data from * global memory as we write it to shader-data. */ -ccl_device_inline void shader_setup_from_ray(const KernelGlobals *ccl_restrict kg, - ShaderData *ccl_restrict sd, - const Ray *ccl_restrict ray, - const Intersection *ccl_restrict isect) +ccl_device_inline void shader_setup_from_ray(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private ShaderData *ccl_restrict sd, + ccl_private const Ray *ccl_restrict ray, + ccl_private const Intersection *ccl_restrict isect) { /* Read intersection data into shader globals. * @@ -135,8 +135,8 @@ ccl_device_inline void shader_setup_from_ray(const KernelGlobals *ccl_restrict k /* ShaderData setup from position sampled on mesh */ -ccl_device_inline void shader_setup_from_sample(const KernelGlobals *ccl_restrict kg, - ShaderData *ccl_restrict sd, +ccl_device_inline void shader_setup_from_sample(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private ShaderData *ccl_restrict sd, const float3 P, const float3 Ng, const float3 I, @@ -247,8 +247,8 @@ ccl_device_inline void shader_setup_from_sample(const KernelGlobals *ccl_restric /* ShaderData setup for displacement */ -ccl_device void shader_setup_from_displace(const KernelGlobals *ccl_restrict kg, - ShaderData *ccl_restrict sd, +ccl_device void shader_setup_from_displace(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private ShaderData *ccl_restrict sd, int object, int prim, float u, @@ -281,8 +281,9 @@ ccl_device void shader_setup_from_displace(const KernelGlobals *ccl_restrict kg, /* ShaderData setup from ray into background */ -ccl_device_inline void shader_setup_from_background(const KernelGlobals *ccl_restrict kg, - ShaderData *ccl_restrict sd, +ccl_device_inline void shader_setup_from_background(ccl_global const KernelGlobals *ccl_restrict + kg, + ccl_private ShaderData *ccl_restrict sd, const float3 ray_P, const float3 ray_D, const float ray_time) @@ -325,9 +326,9 @@ ccl_device_inline void shader_setup_from_background(const KernelGlobals *ccl_res /* ShaderData setup from point inside volume */ #ifdef __VOLUME__ -ccl_device_inline void shader_setup_from_volume(const KernelGlobals *ccl_restrict kg, - ShaderData *ccl_restrict sd, - const Ray *ccl_restrict ray) +ccl_device_inline void shader_setup_from_volume(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private ShaderData *ccl_restrict sd, + ccl_private const Ray *ccl_restrict ray) { /* vectors */ diff --git a/intern/cycles/kernel/geom/geom_subd_triangle.h b/intern/cycles/kernel/geom/geom_subd_triangle.h index 877b2ece15b..927d630fe91 100644 --- a/intern/cycles/kernel/geom/geom_subd_triangle.h +++ b/intern/cycles/kernel/geom/geom_subd_triangle.h @@ -22,15 +22,16 @@ CCL_NAMESPACE_BEGIN /* Patch index for triangle, -1 if not subdivision triangle */ -ccl_device_inline uint subd_triangle_patch(const KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline uint subd_triangle_patch(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { return (sd->prim != PRIM_NONE) ? kernel_tex_fetch(__tri_patch, sd->prim) : ~0; } /* UV coords of triangle within patch */ -ccl_device_inline void subd_triangle_patch_uv(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline void subd_triangle_patch_uv(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, float2 uv[3]) { uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, sd->prim); @@ -42,7 +43,7 @@ ccl_device_inline void subd_triangle_patch_uv(const KernelGlobals *kg, /* Vertex indices of patch */ -ccl_device_inline uint4 subd_triangle_patch_indices(const KernelGlobals *kg, int patch) +ccl_device_inline uint4 subd_triangle_patch_indices(ccl_global const KernelGlobals *kg, int patch) { uint4 indices; @@ -56,21 +57,22 @@ ccl_device_inline uint4 subd_triangle_patch_indices(const KernelGlobals *kg, int /* Originating face for patch */ -ccl_device_inline uint subd_triangle_patch_face(const KernelGlobals *kg, int patch) +ccl_device_inline uint subd_triangle_patch_face(ccl_global const KernelGlobals *kg, int patch) { return kernel_tex_fetch(__patches, patch + 4); } /* Number of corners on originating face */ -ccl_device_inline uint subd_triangle_patch_num_corners(const KernelGlobals *kg, int patch) +ccl_device_inline uint subd_triangle_patch_num_corners(ccl_global const KernelGlobals *kg, + int patch) { return kernel_tex_fetch(__patches, patch + 5) & 0xffff; } /* Indices of the four corners that are used by the patch */ -ccl_device_inline void subd_triangle_patch_corners(const KernelGlobals *kg, +ccl_device_inline void subd_triangle_patch_corners(ccl_global const KernelGlobals *kg, int patch, int corners[4]) { @@ -103,11 +105,11 @@ ccl_device_inline void subd_triangle_patch_corners(const KernelGlobals *kg, /* Reading attributes on various subdivision triangle elements */ -ccl_device_noinline float subd_triangle_attribute_float(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_noinline float subd_triangle_attribute_float(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float *dx, - float *dy) + ccl_private float *dx, + ccl_private float *dy) { int patch = subd_triangle_patch(kg, sd); @@ -242,11 +244,11 @@ ccl_device_noinline float subd_triangle_attribute_float(const KernelGlobals *kg, } } -ccl_device_noinline float2 subd_triangle_attribute_float2(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_noinline float2 subd_triangle_attribute_float2(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float2 *dx, - float2 *dy) + ccl_private float2 *dx, + ccl_private float2 *dy) { int patch = subd_triangle_patch(kg, sd); @@ -385,11 +387,11 @@ ccl_device_noinline float2 subd_triangle_attribute_float2(const KernelGlobals *k } } -ccl_device_noinline float3 subd_triangle_attribute_float3(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_noinline float3 subd_triangle_attribute_float3(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float3 *dx, - float3 *dy) + ccl_private float3 *dx, + ccl_private float3 *dy) { int patch = subd_triangle_patch(kg, sd); @@ -527,11 +529,11 @@ ccl_device_noinline float3 subd_triangle_attribute_float3(const KernelGlobals *k } } -ccl_device_noinline float4 subd_triangle_attribute_float4(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_noinline float4 subd_triangle_attribute_float4(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float4 *dx, - float4 *dy) + ccl_private float4 *dx, + ccl_private float4 *dy) { int patch = subd_triangle_patch(kg, sd); diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/geom_triangle.h index 8edba46fd39..17f87b7c570 100644 --- a/intern/cycles/kernel/geom/geom_triangle.h +++ b/intern/cycles/kernel/geom/geom_triangle.h @@ -25,7 +25,8 @@ CCL_NAMESPACE_BEGIN /* Normal on triangle. */ -ccl_device_inline float3 triangle_normal(const KernelGlobals *kg, ShaderData *sd) +ccl_device_inline float3 triangle_normal(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, sd->prim); @@ -43,14 +44,14 @@ ccl_device_inline float3 triangle_normal(const KernelGlobals *kg, ShaderData *sd } /* Point and normal on triangle. */ -ccl_device_inline void triangle_point_normal(const KernelGlobals *kg, +ccl_device_inline void triangle_point_normal(ccl_global const KernelGlobals *kg, int object, int prim, float u, float v, - float3 *P, - float3 *Ng, - int *shader) + ccl_private float3 *P, + ccl_private float3 *Ng, + ccl_private int *shader) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -75,7 +76,7 @@ ccl_device_inline void triangle_point_normal(const KernelGlobals *kg, /* Triangle vertex locations */ -ccl_device_inline void triangle_vertices(const KernelGlobals *kg, int prim, float3 P[3]) +ccl_device_inline void triangle_vertices(ccl_global const KernelGlobals *kg, int prim, float3 P[3]) { const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); P[0] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); @@ -85,7 +86,7 @@ ccl_device_inline void triangle_vertices(const KernelGlobals *kg, int prim, floa /* Triangle vertex locations and vertex normals */ -ccl_device_inline void triangle_vertices_and_normals(const KernelGlobals *kg, +ccl_device_inline void triangle_vertices_and_normals(ccl_global const KernelGlobals *kg, int prim, float3 P[3], float3 N[3]) @@ -102,7 +103,7 @@ ccl_device_inline void triangle_vertices_and_normals(const KernelGlobals *kg, /* Interpolate smooth vertex normal from vertices */ ccl_device_inline float3 -triangle_smooth_normal(const KernelGlobals *kg, float3 Ng, int prim, float u, float v) +triangle_smooth_normal(ccl_global const KernelGlobals *kg, float3 Ng, int prim, float u, float v) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -115,8 +116,12 @@ triangle_smooth_normal(const KernelGlobals *kg, float3 Ng, int prim, float u, fl return is_zero(N) ? Ng : N; } -ccl_device_inline float3 triangle_smooth_normal_unnormalized( - const KernelGlobals *kg, const ShaderData *sd, float3 Ng, int prim, float u, float v) +ccl_device_inline float3 triangle_smooth_normal_unnormalized(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + float3 Ng, + int prim, + float u, + float v) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -138,10 +143,10 @@ ccl_device_inline float3 triangle_smooth_normal_unnormalized( /* Ray differentials on triangle */ -ccl_device_inline void triangle_dPdudv(const KernelGlobals *kg, +ccl_device_inline void triangle_dPdudv(ccl_global const KernelGlobals *kg, int prim, - ccl_addr_space float3 *dPdu, - ccl_addr_space float3 *dPdv) + ccl_private float3 *dPdu, + ccl_private float3 *dPdv) { /* fetch triangle vertex coordinates */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -156,11 +161,11 @@ ccl_device_inline void triangle_dPdudv(const KernelGlobals *kg, /* Reading attributes on various triangle elements */ -ccl_device float triangle_attribute_float(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float triangle_attribute_float(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float *dx, - float *dy) + ccl_private float *dx, + ccl_private float *dy) { if (desc.element & (ATTR_ELEMENT_VERTEX | ATTR_ELEMENT_VERTEX_MOTION | ATTR_ELEMENT_CORNER)) { float f0, f1, f2; @@ -206,11 +211,11 @@ ccl_device float triangle_attribute_float(const KernelGlobals *kg, } } -ccl_device float2 triangle_attribute_float2(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float2 triangle_attribute_float2(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float2 *dx, - float2 *dy) + ccl_private float2 *dx, + ccl_private float2 *dy) { if (desc.element & (ATTR_ELEMENT_VERTEX | ATTR_ELEMENT_VERTEX_MOTION | ATTR_ELEMENT_CORNER)) { float2 f0, f1, f2; @@ -256,11 +261,11 @@ ccl_device float2 triangle_attribute_float2(const KernelGlobals *kg, } } -ccl_device float3 triangle_attribute_float3(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float3 triangle_attribute_float3(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float3 *dx, - float3 *dy) + ccl_private float3 *dx, + ccl_private float3 *dy) { if (desc.element & (ATTR_ELEMENT_VERTEX | ATTR_ELEMENT_VERTEX_MOTION | ATTR_ELEMENT_CORNER)) { float3 f0, f1, f2; @@ -306,11 +311,11 @@ ccl_device float3 triangle_attribute_float3(const KernelGlobals *kg, } } -ccl_device float4 triangle_attribute_float4(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float4 triangle_attribute_float4(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float4 *dx, - float4 *dy) + ccl_private float4 *dx, + ccl_private float4 *dy) { if (desc.element & (ATTR_ELEMENT_VERTEX | ATTR_ELEMENT_VERTEX_MOTION | ATTR_ELEMENT_CORNER | ATTR_ELEMENT_CORNER_BYTE)) { diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h index b784cc75d08..f637206da19 100644 --- a/intern/cycles/kernel/geom/geom_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h @@ -26,8 +26,8 @@ CCL_NAMESPACE_BEGIN -ccl_device_inline bool triangle_intersect(const KernelGlobals *kg, - Intersection *isect, +ccl_device_inline bool triangle_intersect(ccl_global const KernelGlobals *kg, + ccl_private Intersection *isect, float3 P, float3 dir, float tmax, @@ -85,15 +85,15 @@ ccl_device_inline bool triangle_intersect(const KernelGlobals *kg, */ #ifdef __BVH_LOCAL__ -ccl_device_inline bool triangle_intersect_local(const KernelGlobals *kg, - LocalIntersection *local_isect, +ccl_device_inline bool triangle_intersect_local(ccl_global const KernelGlobals *kg, + ccl_private LocalIntersection *local_isect, float3 P, float3 dir, int object, int local_object, int prim_addr, float tmax, - uint *lcg_state, + ccl_private uint *lcg_state, int max_hits) { /* Only intersect with matching object, for instanced objects we @@ -169,7 +169,7 @@ ccl_device_inline bool triangle_intersect_local(const KernelGlobals *kg, } /* Record intersection. */ - Intersection *isect = &local_isect->hits[hit]; + ccl_private Intersection *isect = &local_isect->hits[hit]; isect->prim = prim; isect->object = local_object; isect->type = PRIMITIVE_TRIANGLE; @@ -200,8 +200,8 @@ ccl_device_inline bool triangle_intersect_local(const KernelGlobals *kg, * http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf */ -ccl_device_inline float3 triangle_refine(const KernelGlobals *kg, - ShaderData *sd, +ccl_device_inline float3 triangle_refine(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, float3 P, float3 D, float t, @@ -256,8 +256,8 @@ ccl_device_inline float3 triangle_refine(const KernelGlobals *kg, /* Same as above, except that t is assumed to be in object space for * instancing. */ -ccl_device_inline float3 triangle_refine_local(const KernelGlobals *kg, - ShaderData *sd, +ccl_device_inline float3 triangle_refine_local(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, float3 P, float3 D, float t, diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h index 2bcd7e56b5f..c466c3fb07a 100644 --- a/intern/cycles/kernel/geom/geom_volume.h +++ b/intern/cycles/kernel/geom/geom_volume.h @@ -31,8 +31,8 @@ CCL_NAMESPACE_BEGIN /* Return position normalized to 0..1 in mesh bounds */ -ccl_device_inline float3 volume_normalized_position(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float3 volume_normalized_position(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, float3 P) { /* todo: optimize this so it's just a single matrix multiplication when @@ -70,8 +70,8 @@ ccl_device float3 volume_attribute_value_to_float3(const float4 value) } } -ccl_device float4 volume_attribute_float4(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device float4 volume_attribute_float4(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc) { if (desc.element & (ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) { diff --git a/intern/cycles/kernel/integrator/integrator_init_from_bake.h b/intern/cycles/kernel/integrator/integrator_init_from_bake.h index 6e4e1be55fa..c822823de9c 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_bake.h +++ b/intern/cycles/kernel/integrator/integrator_init_from_bake.h @@ -44,7 +44,7 @@ ccl_device_inline float bake_clamp_mirror_repeat(float u, float max) * Used by CPU implementation to not attempt to sample pixel for multiple samples once its known * that the pixel did converge. */ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, - const ccl_global KernelWorkTile *ccl_restrict tile, + ccl_global const KernelWorkTile *ccl_restrict tile, ccl_global float *render_buffer, const int x, const int y, diff --git a/intern/cycles/kernel/integrator/integrator_init_from_camera.h b/intern/cycles/kernel/integrator/integrator_init_from_camera.h index 58e7bde4c94..291f0f106f0 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_camera.h +++ b/intern/cycles/kernel/integrator/integrator_init_from_camera.h @@ -25,12 +25,12 @@ CCL_NAMESPACE_BEGIN -ccl_device_inline void integrate_camera_sample(const KernelGlobals *ccl_restrict kg, +ccl_device_inline void integrate_camera_sample(ccl_global const KernelGlobals *ccl_restrict kg, const int sample, const int x, const int y, const uint rng_hash, - Ray *ray) + ccl_private Ray *ray) { /* Filter sampling. */ float filter_u, filter_v; @@ -64,7 +64,7 @@ ccl_device_inline void integrate_camera_sample(const KernelGlobals *ccl_restrict * Used by CPU implementation to not attempt to sample pixel for multiple samples once its known * that the pixel did converge. */ ccl_device bool integrator_init_from_camera(INTEGRATOR_STATE_ARGS, - const ccl_global KernelWorkTile *ccl_restrict tile, + ccl_global const KernelWorkTile *ccl_restrict tile, ccl_global float *render_buffer, const int x, const int y, diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/integrator_intersect_closest.h index cd9af1c62fc..760c08159e3 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_closest.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_closest.h @@ -86,7 +86,7 @@ ccl_device_forceinline bool integrator_intersect_terminate(INTEGRATOR_STATE_ARGS template ccl_device_forceinline void integrator_intersect_shader_next_kernel( INTEGRATOR_STATE_ARGS, - const Intersection *ccl_restrict isect, + ccl_private const Intersection *ccl_restrict isect, const int shader, const int shader_flags) { diff --git a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h index 5bd9cfda4a4..00d44f0e5ed 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h @@ -32,7 +32,7 @@ ccl_device_forceinline uint integrate_intersect_shadow_visibility(INTEGRATOR_STA } ccl_device bool integrate_intersect_shadow_opaque(INTEGRATOR_STATE_ARGS, - const Ray *ray, + ccl_private const Ray *ray, const uint visibility) { /* Mask which will pick only opaque visibility bits from the `visibility`. @@ -62,7 +62,7 @@ ccl_device_forceinline int integrate_shadow_max_transparent_hits(INTEGRATOR_STAT #ifdef __TRANSPARENT_SHADOWS__ ccl_device bool integrate_intersect_shadow_transparent(INTEGRATOR_STATE_ARGS, - const Ray *ray, + ccl_private const Ray *ray, const uint visibility) { Intersection isect[INTEGRATOR_SHADOW_ISECT_SIZE]; diff --git a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h index 33a77d0fe29..192e9c6ab43 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h @@ -30,7 +30,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_A PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_STACK); ShaderDataTinyStorage stack_sd_storage; - ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage); + ccl_private ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage); kernel_assert(kernel_data.integrator.use_volumes); @@ -78,7 +78,7 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_STACK); ShaderDataTinyStorage stack_sd_storage; - ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage); + ccl_private ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage); Ray volume_ray ccl_optional_struct_init; integrator_state_read_ray(INTEGRATOR_STATE_PASS, &volume_ray); diff --git a/intern/cycles/kernel/integrator/integrator_shade_background.h b/intern/cycles/kernel/integrator/integrator_shade_background.h index 234aa7cae63..a898f3fb2fc 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_background.h +++ b/intern/cycles/kernel/integrator/integrator_shade_background.h @@ -49,7 +49,7 @@ ccl_device float3 integrator_eval_background_shader(INTEGRATOR_STATE_ARGS, /* TODO: does aliasing like this break automatic SoA in CUDA? * Should we instead store closures separate from ShaderData? */ ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_LIGHT_SETUP); shader_setup_from_background(kg, @@ -155,7 +155,7 @@ ccl_device_inline void integrate_distant_lights(INTEGRATOR_STATE_ARGS, /* Evaluate light shader. */ /* TODO: does aliasing like this break automatic SoA in CUDA? */ ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); float3 light_eval = light_sample_shader_eval( INTEGRATOR_STATE_PASS, emission_sd, &ls, ray_time); if (is_zero(light_eval)) { diff --git a/intern/cycles/kernel/integrator/integrator_shade_light.h b/intern/cycles/kernel/integrator/integrator_shade_light.h index 05b530f9665..d8f8da63023 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_light.h +++ b/intern/cycles/kernel/integrator/integrator_shade_light.h @@ -72,7 +72,7 @@ ccl_device_inline void integrate_light(INTEGRATOR_STATE_ARGS, /* Evaluate light shader. */ /* TODO: does aliasing like this break automatic SoA in CUDA? */ ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); float3 light_eval = light_sample_shader_eval(INTEGRATOR_STATE_PASS, emission_sd, &ls, ray_time); if (is_zero(light_eval)) { return; diff --git a/intern/cycles/kernel/integrator/integrator_shade_shadow.h b/intern/cycles/kernel/integrator/integrator_shade_shadow.h index fd3c3ae1653..3857b522b25 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_shade_shadow.h @@ -39,7 +39,7 @@ ccl_device_inline float3 integrate_transparent_surface_shadow(INTEGRATOR_STATE_A * TODO: is it better to declare this outside the loop or keep it local * so the compiler can see there is no dependency between iterations? */ ShaderDataTinyStorage shadow_sd_storage; - ShaderData *shadow_sd = AS_SHADER_DATA(&shadow_sd_storage); + ccl_private ShaderData *shadow_sd = AS_SHADER_DATA(&shadow_sd_storage); /* Setup shader data at surface. */ Intersection isect ccl_optional_struct_init; @@ -69,13 +69,14 @@ ccl_device_inline float3 integrate_transparent_surface_shadow(INTEGRATOR_STATE_A ccl_device_inline void integrate_transparent_volume_shadow(INTEGRATOR_STATE_ARGS, const int hit, const int num_recorded_hits, - float3 *ccl_restrict throughput) + ccl_private float3 *ccl_restrict + throughput) { PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_VOLUME); /* TODO: deduplicate with surface, or does it not matter for memory usage? */ ShaderDataTinyStorage shadow_sd_storage; - ShaderData *shadow_sd = AS_SHADER_DATA(&shadow_sd_storage); + ccl_private ShaderData *shadow_sd = AS_SHADER_DATA(&shadow_sd_storage); /* Setup shader data. */ Ray ray ccl_optional_struct_init; diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index 27338f824c0..0d739517592 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -29,7 +29,7 @@ CCL_NAMESPACE_BEGIN ccl_device_forceinline void integrate_surface_shader_setup(INTEGRATOR_STATE_CONST_ARGS, - ShaderData *sd) + ccl_private ShaderData *sd) { Intersection isect ccl_optional_struct_init; integrator_state_read_isect(INTEGRATOR_STATE_PASS, &isect); @@ -42,7 +42,7 @@ ccl_device_forceinline void integrate_surface_shader_setup(INTEGRATOR_STATE_CONS #ifdef __HOLDOUT__ ccl_device_forceinline bool integrate_surface_holdout(INTEGRATOR_STATE_CONST_ARGS, - ShaderData *sd, + ccl_private ShaderData *sd, ccl_global float *ccl_restrict render_buffer) { /* Write holdout transparency to render buffer and stop if fully holdout. */ @@ -67,7 +67,7 @@ ccl_device_forceinline bool integrate_surface_holdout(INTEGRATOR_STATE_CONST_ARG #ifdef __EMISSION__ ccl_device_forceinline void integrate_surface_emission(INTEGRATOR_STATE_CONST_ARGS, - const ShaderData *sd, + ccl_private const ShaderData *sd, ccl_global float *ccl_restrict render_buffer) { @@ -103,8 +103,8 @@ ccl_device_forceinline void integrate_surface_emission(INTEGRATOR_STATE_CONST_AR /* Path tracing: sample point on light and evaluate light shader, then * queue shadow ray to be traced. */ ccl_device_forceinline void integrate_surface_direct_light(INTEGRATOR_STATE_ARGS, - ShaderData *sd, - const RNGState *rng_state) + ccl_private ShaderData *sd, + ccl_private const RNGState *rng_state) { /* Test if there is a light or BSDF that needs direct light. */ if (!(kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL))) { @@ -134,7 +134,7 @@ ccl_device_forceinline void integrate_surface_direct_light(INTEGRATOR_STATE_ARGS * the light shader. This could also move to its own kernel, for * non-constant light sources. */ ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); const float3 light_eval = light_sample_shader_eval( INTEGRATOR_STATE_PASS, emission_sd, &ls, sd->time); if (is_zero(light_eval)) { @@ -206,9 +206,8 @@ ccl_device_forceinline void integrate_surface_direct_light(INTEGRATOR_STATE_ARGS #endif /* Path tracing: bounce off or through surface with new direction. */ -ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(INTEGRATOR_STATE_ARGS, - ShaderData *sd, - const RNGState *rng_state) +ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( + INTEGRATOR_STATE_ARGS, ccl_private ShaderData *sd, ccl_private const RNGState *rng_state) { /* Sample BSDF or BSSRDF. */ if (!(sd->flag & (SD_BSDF | SD_BSSRDF))) { @@ -217,7 +216,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(INTEGRATOR_STATE float bsdf_u, bsdf_v; path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - const ShaderClosure *sc = shader_bsdf_bssrdf_pick(sd, &bsdf_u); + ccl_private const ShaderClosure *sc = shader_bsdf_bssrdf_pick(sd, &bsdf_u); #ifdef __SUBSURFACE__ /* BSSRDF closure, we schedule subsurface intersection kernel. */ @@ -281,7 +280,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(INTEGRATOR_STATE #ifdef __VOLUME__ ccl_device_forceinline bool integrate_surface_volume_only_bounce(INTEGRATOR_STATE_ARGS, - ShaderData *sd) + ccl_private ShaderData *sd) { if (!path_state_volume_next(INTEGRATOR_STATE_PASS)) { return LABEL_NONE; @@ -304,19 +303,21 @@ ccl_device_forceinline bool integrate_surface_volume_only_bounce(INTEGRATOR_STAT #endif #if defined(__AO__) && defined(__SHADER_RAYTRACE__) -ccl_device_forceinline void integrate_surface_ao_pass(INTEGRATOR_STATE_CONST_ARGS, - const ShaderData *ccl_restrict sd, - const RNGState *ccl_restrict rng_state, - ccl_global float *ccl_restrict render_buffer) +ccl_device_forceinline void integrate_surface_ao_pass( + INTEGRATOR_STATE_CONST_ARGS, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const RNGState *ccl_restrict rng_state, + ccl_global float *ccl_restrict render_buffer) { # ifdef __KERNEL_OPTIX__ optixDirectCall(2, INTEGRATOR_STATE_PASS, sd, rng_state, render_buffer); } -extern "C" __device__ void __direct_callable__ao_pass(INTEGRATOR_STATE_CONST_ARGS, - const ShaderData *ccl_restrict sd, - const RNGState *ccl_restrict rng_state, - ccl_global float *ccl_restrict render_buffer) +extern "C" __device__ void __direct_callable__ao_pass( + INTEGRATOR_STATE_CONST_ARGS, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const RNGState *ccl_restrict rng_state, + ccl_global float *ccl_restrict render_buffer) { # endif /* __KERNEL_OPTIX__ */ float bsdf_u, bsdf_v; diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index aa4c652c037..72c609751f7 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -71,8 +71,8 @@ typedef struct VolumeShaderCoefficients { /* Evaluate shader to get extinction coefficient at P. */ ccl_device_inline bool shadow_volume_shader_sample(INTEGRATOR_STATE_ARGS, - ShaderData *ccl_restrict sd, - float3 *ccl_restrict extinction) + ccl_private ShaderData *ccl_restrict sd, + ccl_private float3 *ccl_restrict extinction) { shader_eval_volume(INTEGRATOR_STATE_PASS, sd, PATH_RAY_SHADOW, [=](const int i) { return integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_PASS, i); @@ -89,8 +89,8 @@ ccl_device_inline bool shadow_volume_shader_sample(INTEGRATOR_STATE_ARGS, /* Evaluate shader to get absorption, scattering and emission at P. */ ccl_device_inline bool volume_shader_sample(INTEGRATOR_STATE_ARGS, - ShaderData *ccl_restrict sd, - VolumeShaderCoefficients *coeff) + ccl_private ShaderData *ccl_restrict sd, + ccl_private VolumeShaderCoefficients *coeff) { const int path_flag = INTEGRATOR_STATE(path, flag); shader_eval_volume(INTEGRATOR_STATE_PASS, sd, path_flag, [=](const int i) { @@ -107,7 +107,7 @@ ccl_device_inline bool volume_shader_sample(INTEGRATOR_STATE_ARGS, if (sd->flag & SD_SCATTER) { for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_VOLUME(sc->type)) { coeff->sigma_s += sc->weight; @@ -123,14 +123,14 @@ ccl_device_inline bool volume_shader_sample(INTEGRATOR_STATE_ARGS, return true; } -ccl_device_forceinline void volume_step_init(const KernelGlobals *kg, - const RNGState *rng_state, +ccl_device_forceinline void volume_step_init(ccl_global const KernelGlobals *kg, + ccl_private const RNGState *rng_state, const float object_step_size, float t, - float *step_size, - float *step_shade_offset, - float *steps_offset, - int *max_steps) + ccl_private float *step_size, + ccl_private float *step_shade_offset, + ccl_private float *steps_offset, + ccl_private int *max_steps) { if (object_step_size == FLT_MAX) { /* Homogeneous volume. */ @@ -170,9 +170,9 @@ ccl_device_forceinline void volume_step_init(const KernelGlobals *kg, /* homogeneous volume: assume shader evaluation at the starts gives * the extinction coefficient for the entire line segment */ ccl_device void volume_shadow_homogeneous(INTEGRATOR_STATE_ARGS, - Ray *ccl_restrict ray, - ShaderData *ccl_restrict sd, - float3 *ccl_restrict throughput) + ccl_private Ray *ccl_restrict ray, + ccl_private ShaderData *ccl_restrict sd, + ccl_global float3 *ccl_restrict throughput) { float3 sigma_t = zero_float3(); @@ -185,9 +185,9 @@ ccl_device void volume_shadow_homogeneous(INTEGRATOR_STATE_ARGS, /* heterogeneous volume: integrate stepping through the volume until we * reach the end, get absorbed entirely, or run out of iterations */ ccl_device void volume_shadow_heterogeneous(INTEGRATOR_STATE_ARGS, - Ray *ccl_restrict ray, - ShaderData *ccl_restrict sd, - float3 *ccl_restrict throughput, + ccl_private Ray *ccl_restrict ray, + ccl_private ShaderData *ccl_restrict sd, + ccl_private float3 *ccl_restrict throughput, const float object_step_size) { /* Load random number state. */ @@ -257,10 +257,10 @@ ccl_device void volume_shadow_heterogeneous(INTEGRATOR_STATE_ARGS, /* Equi-angular sampling as in: * "Importance Sampling Techniques for Path Tracing in Participating Media" */ -ccl_device float volume_equiangular_sample(const Ray *ccl_restrict ray, +ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict ray, const float3 light_P, const float xi, - float *pdf) + ccl_private float *pdf) { const float t = ray->t; const float delta = dot((light_P - ray->P), ray->D); @@ -281,7 +281,7 @@ ccl_device float volume_equiangular_sample(const Ray *ccl_restrict ray, return min(t, delta + t_); /* min is only for float precision errors */ } -ccl_device float volume_equiangular_pdf(const Ray *ccl_restrict ray, +ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray, const float3 light_P, const float sample_t) { @@ -305,7 +305,7 @@ ccl_device float volume_equiangular_pdf(const Ray *ccl_restrict ray, return pdf; } -ccl_device float volume_equiangular_cdf(const Ray *ccl_restrict ray, +ccl_device float volume_equiangular_cdf(ccl_private const Ray *ccl_restrict ray, const float3 light_P, const float sample_t) { @@ -332,8 +332,12 @@ ccl_device float volume_equiangular_cdf(const Ray *ccl_restrict ray, /* Distance sampling */ -ccl_device float volume_distance_sample( - float max_t, float3 sigma_t, int channel, float xi, float3 *transmittance, float3 *pdf) +ccl_device float volume_distance_sample(float max_t, + float3 sigma_t, + int channel, + float xi, + ccl_private float3 *transmittance, + ccl_private float3 *pdf) { /* xi is [0, 1[ so log(0) should never happen, division by zero is * avoided because sample_sigma_t > 0 when SD_SCATTER is set */ @@ -363,7 +367,7 @@ ccl_device float3 volume_distance_pdf(float max_t, float3 sigma_t, float sample_ /* Emission */ -ccl_device float3 volume_emission_integrate(VolumeShaderCoefficients *coeff, +ccl_device float3 volume_emission_integrate(ccl_private VolumeShaderCoefficients *coeff, int closure_flag, float3 transmittance, float t) @@ -410,13 +414,13 @@ typedef struct VolumeIntegrateState { } VolumeIntegrateState; ccl_device_forceinline void volume_integrate_step_scattering( - const ShaderData *sd, - const Ray *ray, + ccl_private const ShaderData *sd, + ccl_private const Ray *ray, const float3 equiangular_light_P, - const VolumeShaderCoefficients &ccl_restrict coeff, + ccl_private const VolumeShaderCoefficients &ccl_restrict coeff, const float3 transmittance, - VolumeIntegrateState &ccl_restrict vstate, - VolumeIntegrateResult &ccl_restrict result) + ccl_private VolumeIntegrateState &ccl_restrict vstate, + ccl_private VolumeIntegrateResult &ccl_restrict result) { /* Pick random color channel, we use the Veach one-sample * model with balance heuristic for the channels. */ @@ -507,14 +511,14 @@ ccl_device_forceinline void volume_integrate_step_scattering( * for path tracing where we don't want to branch. */ ccl_device_forceinline void volume_integrate_heterogeneous( INTEGRATOR_STATE_ARGS, - Ray *ccl_restrict ray, - ShaderData *ccl_restrict sd, - const RNGState *rng_state, + ccl_private Ray *ccl_restrict ray, + ccl_private ShaderData *ccl_restrict sd, + ccl_private const RNGState *rng_state, ccl_global float *ccl_restrict render_buffer, const float object_step_size, const VolumeSampleMethod direct_sample_method, const float3 equiangular_light_P, - VolumeIntegrateResult &result) + ccl_private VolumeIntegrateResult &result) { PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INTEGRATE); @@ -666,10 +670,11 @@ ccl_device_forceinline void volume_integrate_heterogeneous( # ifdef __EMISSION__ /* Path tracing: sample point on light and evaluate light shader, then * queue shadow ray to be traced. */ -ccl_device_forceinline bool integrate_volume_sample_light(INTEGRATOR_STATE_ARGS, - const ShaderData *ccl_restrict sd, - const RNGState *ccl_restrict rng_state, - LightSample *ccl_restrict ls) +ccl_device_forceinline bool integrate_volume_sample_light( + INTEGRATOR_STATE_ARGS, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const RNGState *ccl_restrict rng_state, + ccl_private LightSample *ccl_restrict ls) { /* Test if there is a light or BSDF that needs direct light. */ if (!kernel_data.integrator.use_direct_light) { @@ -694,14 +699,14 @@ ccl_device_forceinline bool integrate_volume_sample_light(INTEGRATOR_STATE_ARGS, /* Path tracing: sample point on light and evaluate light shader, then * queue shadow ray to be traced. */ -ccl_device_forceinline void integrate_volume_direct_light(INTEGRATOR_STATE_ARGS, - const ShaderData *ccl_restrict sd, - const RNGState *ccl_restrict rng_state, - const float3 P, - const ShaderVolumePhases *ccl_restrict - phases, - const float3 throughput, - LightSample *ccl_restrict ls) +ccl_device_forceinline void integrate_volume_direct_light( + INTEGRATOR_STATE_ARGS, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const RNGState *ccl_restrict rng_state, + const float3 P, + ccl_private const ShaderVolumePhases *ccl_restrict phases, + ccl_private const float3 throughput, + ccl_private LightSample *ccl_restrict ls) { PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_DIRECT_LIGHT); @@ -737,7 +742,7 @@ ccl_device_forceinline void integrate_volume_direct_light(INTEGRATOR_STATE_ARGS, * the light shader. This could also move to its own kernel, for * non-constant light sources. */ ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); const float3 light_eval = light_sample_shader_eval( INTEGRATOR_STATE_PASS, emission_sd, ls, sd->time); if (is_zero(light_eval)) { @@ -801,10 +806,11 @@ ccl_device_forceinline void integrate_volume_direct_light(INTEGRATOR_STATE_ARGS, # endif /* Path tracing: scatter in new direction using phase function */ -ccl_device_forceinline bool integrate_volume_phase_scatter(INTEGRATOR_STATE_ARGS, - ShaderData *sd, - const RNGState *rng_state, - const ShaderVolumePhases *phases) +ccl_device_forceinline bool integrate_volume_phase_scatter( + INTEGRATOR_STATE_ARGS, + ccl_private ShaderData *sd, + ccl_private const RNGState *rng_state, + ccl_private const ShaderVolumePhases *phases) { PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INDIRECT_LIGHT); @@ -865,7 +871,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(INTEGRATOR_STATE_ARGS * between the endpoints. distance sampling is used to decide if we will * scatter or not. */ ccl_device VolumeIntegrateEvent volume_integrate(INTEGRATOR_STATE_ARGS, - Ray *ccl_restrict ray, + ccl_private Ray *ccl_restrict ray, ccl_global float *ccl_restrict render_buffer) { ShaderData sd; diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h index efc7576d95b..517e2891769 100644 --- a/intern/cycles/kernel/integrator/integrator_state.h +++ b/intern/cycles/kernel/integrator/integrator_state.h @@ -106,7 +106,7 @@ typedef struct IntegratorQueueCounter { * GPU rendering path state with SoA layout. */ typedef struct IntegratorStateGPU { #define KERNEL_STRUCT_BEGIN(name) struct { -#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) type *name; +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) ccl_global type *name; #define KERNEL_STRUCT_ARRAY_MEMBER KERNEL_STRUCT_MEMBER #define KERNEL_STRUCT_END(name) \ } \ @@ -124,13 +124,13 @@ typedef struct IntegratorStateGPU { #undef KERNEL_STRUCT_VOLUME_STACK_SIZE /* Count number of queued kernels. */ - IntegratorQueueCounter *queue_counter; + ccl_global IntegratorQueueCounter *queue_counter; /* Count number of kernels queued for specific shaders. */ - int *sort_key_counter[DEVICE_KERNEL_INTEGRATOR_NUM]; + ccl_global int *sort_key_counter[DEVICE_KERNEL_INTEGRATOR_NUM]; /* Index of path which will be used by a next shadow catcher split. */ - int *next_shadow_catcher_path_index; + ccl_global int *next_shadow_catcher_path_index; } IntegratorStateGPU; /* Abstraction @@ -173,9 +173,10 @@ typedef IntegratorStateCPU *ccl_restrict IntegratorState; typedef int IntegratorState; -# define INTEGRATOR_STATE_ARGS const KernelGlobals *ccl_restrict kg, const IntegratorState state +# define INTEGRATOR_STATE_ARGS \ + ccl_global const KernelGlobals *ccl_restrict kg, const IntegratorState state # define INTEGRATOR_STATE_CONST_ARGS \ - const KernelGlobals *ccl_restrict kg, const IntegratorState state + ccl_global const KernelGlobals *ccl_restrict kg, const IntegratorState state # define INTEGRATOR_STATE_PASS kg, state # define INTEGRATOR_STATE_PASS_NULL kg, -1 diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index 037c7533943..fddd9eb5ac8 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -24,7 +24,7 @@ CCL_NAMESPACE_BEGIN /* Ray */ ccl_device_forceinline void integrator_state_write_ray(INTEGRATOR_STATE_ARGS, - const Ray *ccl_restrict ray) + ccl_private const Ray *ccl_restrict ray) { INTEGRATOR_STATE_WRITE(ray, P) = ray->P; INTEGRATOR_STATE_WRITE(ray, D) = ray->D; @@ -35,7 +35,7 @@ ccl_device_forceinline void integrator_state_write_ray(INTEGRATOR_STATE_ARGS, } ccl_device_forceinline void integrator_state_read_ray(INTEGRATOR_STATE_CONST_ARGS, - Ray *ccl_restrict ray) + ccl_private Ray *ccl_restrict ray) { ray->P = INTEGRATOR_STATE(ray, P); ray->D = INTEGRATOR_STATE(ray, D); @@ -47,8 +47,8 @@ ccl_device_forceinline void integrator_state_read_ray(INTEGRATOR_STATE_CONST_ARG /* Shadow Ray */ -ccl_device_forceinline void integrator_state_write_shadow_ray(INTEGRATOR_STATE_ARGS, - const Ray *ccl_restrict ray) +ccl_device_forceinline void integrator_state_write_shadow_ray( + INTEGRATOR_STATE_ARGS, ccl_private const Ray *ccl_restrict ray) { INTEGRATOR_STATE_WRITE(shadow_ray, P) = ray->P; INTEGRATOR_STATE_WRITE(shadow_ray, D) = ray->D; @@ -58,7 +58,7 @@ ccl_device_forceinline void integrator_state_write_shadow_ray(INTEGRATOR_STATE_A } ccl_device_forceinline void integrator_state_read_shadow_ray(INTEGRATOR_STATE_CONST_ARGS, - Ray *ccl_restrict ray) + ccl_private Ray *ccl_restrict ray) { ray->P = INTEGRATOR_STATE(shadow_ray, P); ray->D = INTEGRATOR_STATE(shadow_ray, D); @@ -70,8 +70,8 @@ ccl_device_forceinline void integrator_state_read_shadow_ray(INTEGRATOR_STATE_CO /* Intersection */ -ccl_device_forceinline void integrator_state_write_isect(INTEGRATOR_STATE_ARGS, - const Intersection *ccl_restrict isect) +ccl_device_forceinline void integrator_state_write_isect( + INTEGRATOR_STATE_ARGS, ccl_private const Intersection *ccl_restrict isect) { INTEGRATOR_STATE_WRITE(isect, t) = isect->t; INTEGRATOR_STATE_WRITE(isect, u) = isect->u; @@ -84,8 +84,8 @@ ccl_device_forceinline void integrator_state_write_isect(INTEGRATOR_STATE_ARGS, #endif } -ccl_device_forceinline void integrator_state_read_isect(INTEGRATOR_STATE_CONST_ARGS, - Intersection *ccl_restrict isect) +ccl_device_forceinline void integrator_state_read_isect( + INTEGRATOR_STATE_CONST_ARGS, ccl_private Intersection *ccl_restrict isect) { isect->prim = INTEGRATOR_STATE(isect, prim); isect->object = INTEGRATOR_STATE(isect, object); @@ -124,7 +124,7 @@ ccl_device_forceinline bool integrator_state_volume_stack_is_empty(INTEGRATOR_ST /* Shadow Intersection */ ccl_device_forceinline void integrator_state_write_shadow_isect( - INTEGRATOR_STATE_ARGS, const Intersection *ccl_restrict isect, const int index) + INTEGRATOR_STATE_ARGS, ccl_private const Intersection *ccl_restrict isect, const int index) { INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, t) = isect->t; INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, u) = isect->u; @@ -137,9 +137,8 @@ ccl_device_forceinline void integrator_state_write_shadow_isect( #endif } -ccl_device_forceinline void integrator_state_read_shadow_isect(INTEGRATOR_STATE_CONST_ARGS, - Intersection *ccl_restrict isect, - const int index) +ccl_device_forceinline void integrator_state_read_shadow_isect( + INTEGRATOR_STATE_CONST_ARGS, ccl_private Intersection *ccl_restrict isect, const int index) { isect->prim = INTEGRATOR_STATE_ARRAY(shadow_isect, index, prim); isect->object = INTEGRATOR_STATE_ARRAY(shadow_isect, index, object); diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/integrator_subsurface.h index 2d15c82322a..153f9b79743 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface.h @@ -36,14 +36,16 @@ CCL_NAMESPACE_BEGIN #ifdef __SUBSURFACE__ -ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, ShaderData *sd, const ShaderClosure *sc) +ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, + ccl_private ShaderData *sd, + ccl_private const ShaderClosure *sc) { /* We should never have two consecutive BSSRDF bounces, the second one should * be converted to a diffuse BSDF to avoid this. */ kernel_assert(!(INTEGRATOR_STATE(path, flag) & PATH_RAY_DIFFUSE_ANCESTOR)); /* Setup path state for intersect_subsurface kernel. */ - const Bssrdf *bssrdf = (const Bssrdf *)sc; + ccl_private const Bssrdf *bssrdf = (ccl_private const Bssrdf *)sc; /* Setup ray into surface. */ INTEGRATOR_STATE_WRITE(ray, P) = sd->P; @@ -89,7 +91,7 @@ ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, ShaderData *sd, const Sh } ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, - ShaderData *sd, + ccl_private ShaderData *sd, const uint32_t path_flag) { /* Get bump mapped normal from shader evaluation at exit point. */ @@ -107,7 +109,7 @@ ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, # ifdef __PRINCIPLED__ if (path_flag & PATH_RAY_SUBSURFACE_USE_FRESNEL) { - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), weight); if (bsdf) { @@ -119,7 +121,8 @@ ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, else # endif /* __PRINCIPLED__ */ { - DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight); + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( + sd, sizeof(DiffuseBsdf), weight); if (bsdf) { bsdf->N = N; diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_disk.h b/intern/cycles/kernel/integrator/integrator_subsurface_disk.h index 3f685e3a2e9..788a5e9b929 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface_disk.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface_disk.h @@ -33,8 +33,8 @@ ccl_device_inline float3 subsurface_disk_eval(const float3 radius, float disk_r, * nearby points on the same object. */ ccl_device_inline bool subsurface_disk(INTEGRATOR_STATE_ARGS, RNGState rng_state, - Ray &ray, - LocalIntersection &ss_isect) + ccl_private Ray &ray, + ccl_private LocalIntersection &ss_isect) { float disk_u, disk_v; diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h index d4935b0ce4a..45a43ea67a9 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h @@ -31,8 +31,11 @@ CCL_NAMESPACE_BEGIN * Magnus Wrenninge, Ryusuke Villemin, Christophe Hery. * https://graphics.pixar.com/library/PathTracedSubsurface/ */ -ccl_device void subsurface_random_walk_remap( - const float albedo, const float d, float g, float *sigma_t, float *alpha) +ccl_device void subsurface_random_walk_remap(const float albedo, + const float d, + float g, + ccl_private float *sigma_t, + ccl_private float *alpha) { /* Compute attenuation and scattering coefficients from albedo. */ const float g2 = g * g; @@ -78,9 +81,9 @@ ccl_device void subsurface_random_walk_remap( ccl_device void subsurface_random_walk_coefficients(const float3 albedo, const float3 radius, const float anisotropy, - float3 *sigma_t, - float3 *alpha, - float3 *throughput) + ccl_private float3 *sigma_t, + ccl_private float3 *alpha, + ccl_private float3 *throughput) { float sigma_t_x, sigma_t_y, sigma_t_z; float alpha_x, alpha_y, alpha_z; @@ -164,7 +167,7 @@ ccl_device_forceinline float3 direction_from_cosine(float3 D, float cos_theta, f ccl_device_forceinline float3 subsurface_random_walk_pdf(float3 sigma_t, float t, bool hit, - float3 *transmittance) + ccl_private float3 *transmittance) { float3 T = volume_color_transmittance(sigma_t, t); if (transmittance) { @@ -179,8 +182,8 @@ ccl_device_forceinline float3 subsurface_random_walk_pdf(float3 sigma_t, ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, RNGState rng_state, - Ray &ray, - LocalIntersection &ss_isect) + ccl_private Ray &ray, + ccl_private LocalIntersection &ss_isect) { float bssrdf_u, bssrdf_v; path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); diff --git a/intern/cycles/kernel/integrator/integrator_volume_stack.h b/intern/cycles/kernel/integrator/integrator_volume_stack.h index 01ebf8376b1..0c4a723de6f 100644 --- a/intern/cycles/kernel/integrator/integrator_volume_stack.h +++ b/intern/cycles/kernel/integrator/integrator_volume_stack.h @@ -25,7 +25,7 @@ CCL_NAMESPACE_BEGIN template ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, - const ShaderData *sd, + ccl_private const ShaderData *sd, StackReadOp stack_read, StackWriteOp stack_write) { @@ -84,7 +84,7 @@ ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, } } -ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, const ShaderData *sd) +ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, ccl_private const ShaderData *sd) { volume_stack_enter_exit( INTEGRATOR_STATE_PASS, @@ -95,7 +95,8 @@ ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, const ShaderData }); } -ccl_device void shadow_volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, const ShaderData *sd) +ccl_device void shadow_volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, + ccl_private const ShaderData *sd) { volume_stack_enter_exit( INTEGRATOR_STATE_PASS, diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index f4d00e4c20c..dc0aa9356f7 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -32,7 +32,9 @@ CCL_NAMESPACE_BEGIN * that only one of those can happen at a bounce, and so do not need to accumulate * them separately. */ -ccl_device_inline void bsdf_eval_init(BsdfEval *eval, const bool is_diffuse, float3 value) +ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval, + const bool is_diffuse, + float3 value) { eval->diffuse = zero_float3(); eval->glossy = zero_float3(); @@ -45,7 +47,7 @@ ccl_device_inline void bsdf_eval_init(BsdfEval *eval, const bool is_diffuse, flo } } -ccl_device_inline void bsdf_eval_accum(BsdfEval *eval, +ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval, const bool is_diffuse, float3 value, float mis_weight) @@ -60,29 +62,29 @@ ccl_device_inline void bsdf_eval_accum(BsdfEval *eval, } } -ccl_device_inline bool bsdf_eval_is_zero(BsdfEval *eval) +ccl_device_inline bool bsdf_eval_is_zero(ccl_private BsdfEval *eval) { return is_zero(eval->diffuse) && is_zero(eval->glossy); } -ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float value) +ccl_device_inline void bsdf_eval_mul(ccl_private BsdfEval *eval, float value) { eval->diffuse *= value; eval->glossy *= value; } -ccl_device_inline void bsdf_eval_mul3(BsdfEval *eval, float3 value) +ccl_device_inline void bsdf_eval_mul3(ccl_private BsdfEval *eval, float3 value) { eval->diffuse *= value; eval->glossy *= value; } -ccl_device_inline float3 bsdf_eval_sum(const BsdfEval *eval) +ccl_device_inline float3 bsdf_eval_sum(ccl_private const BsdfEval *eval) { return eval->diffuse + eval->glossy; } -ccl_device_inline float3 bsdf_eval_diffuse_glossy_ratio(const BsdfEval *eval) +ccl_device_inline float3 bsdf_eval_diffuse_glossy_ratio(ccl_private const BsdfEval *eval) { /* Ratio of diffuse and glossy to recover proportions for writing to render pass. * We assume reflection, transmission and volume scatter to be exclusive. */ @@ -96,7 +98,9 @@ ccl_device_inline float3 bsdf_eval_diffuse_glossy_ratio(const BsdfEval *eval) * to render buffers instead of using per-thread memory, and to avoid the * impact of clamping on other contributions. */ -ccl_device_forceinline void kernel_accum_clamp(const KernelGlobals *kg, float3 *L, int bounce) +ccl_device_forceinline void kernel_accum_clamp(ccl_global const KernelGlobals *kg, + ccl_private float3 *L, + int bounce) { #ifdef __KERNEL_DEBUG_NAN__ if (!isfinite3_safe(*L)) { diff --git a/intern/cycles/kernel/kernel_adaptive_sampling.h b/intern/cycles/kernel/kernel_adaptive_sampling.h index 7d71907effe..cdf2601f6c3 100644 --- a/intern/cycles/kernel/kernel_adaptive_sampling.h +++ b/intern/cycles/kernel/kernel_adaptive_sampling.h @@ -40,7 +40,7 @@ ccl_device_forceinline bool kernel_need_sample_pixel(INTEGRATOR_STATE_CONST_ARGS /* Determines whether to continue sampling a given pixel or if it has sufficiently converged. */ -ccl_device bool kernel_adaptive_sampling_convergence_check(const KernelGlobals *kg, +ccl_device bool kernel_adaptive_sampling_convergence_check(ccl_global const KernelGlobals *kg, ccl_global float *render_buffer, int x, int y, @@ -90,7 +90,7 @@ ccl_device bool kernel_adaptive_sampling_convergence_check(const KernelGlobals * /* This is a simple box filter in two passes. * When a pixel demands more adaptive samples, let its neighboring pixels draw more samples too. */ -ccl_device void kernel_adaptive_sampling_filter_x(const KernelGlobals *kg, +ccl_device void kernel_adaptive_sampling_filter_x(ccl_global const KernelGlobals *kg, ccl_global float *render_buffer, int y, int start_x, @@ -123,7 +123,7 @@ ccl_device void kernel_adaptive_sampling_filter_x(const KernelGlobals *kg, } } -ccl_device void kernel_adaptive_sampling_filter_y(const KernelGlobals *kg, +ccl_device void kernel_adaptive_sampling_filter_y(ccl_global const KernelGlobals *kg, ccl_global float *render_buffer, int x, int start_y, diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index abb1ba455e6..cfff727d007 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -24,7 +24,7 @@ CCL_NAMESPACE_BEGIN -ccl_device void kernel_displace_evaluate(const KernelGlobals *kg, +ccl_device void kernel_displace_evaluate(ccl_global const KernelGlobals *kg, ccl_global const KernelShaderEvalInput *input, ccl_global float4 *output, const int offset) @@ -56,7 +56,7 @@ ccl_device void kernel_displace_evaluate(const KernelGlobals *kg, output[offset] += make_float4(D.x, D.y, D.z, 0.0f); } -ccl_device void kernel_background_evaluate(const KernelGlobals *kg, +ccl_device void kernel_background_evaluate(ccl_global const KernelGlobals *kg, ccl_global const KernelShaderEvalInput *input, ccl_global float4 *output, const int offset) diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index 7be5da8fe6d..73683a15c5d 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -46,12 +46,12 @@ ccl_device float2 camera_sample_aperture(ccl_constant KernelCamera *cam, float u return bokeh; } -ccl_device void camera_sample_perspective(const KernelGlobals *ccl_restrict kg, +ccl_device void camera_sample_perspective(ccl_global const KernelGlobals *ccl_restrict kg, float raster_x, float raster_y, float lens_u, float lens_v, - ccl_addr_space Ray *ray) + ccl_private Ray *ray) { /* create ray form raster position */ ProjectionTransform rastertocamera = kernel_data.cam.rastertocamera; @@ -185,12 +185,12 @@ ccl_device void camera_sample_perspective(const KernelGlobals *ccl_restrict kg, } /* Orthographic Camera */ -ccl_device void camera_sample_orthographic(const KernelGlobals *ccl_restrict kg, +ccl_device void camera_sample_orthographic(ccl_global const KernelGlobals *ccl_restrict kg, float raster_x, float raster_y, float lens_u, float lens_v, - ccl_addr_space Ray *ray) + ccl_private Ray *ray) { /* create ray form raster position */ ProjectionTransform rastertocamera = kernel_data.cam.rastertocamera; @@ -254,13 +254,13 @@ ccl_device void camera_sample_orthographic(const KernelGlobals *ccl_restrict kg, ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, #ifdef __CAMERA_MOTION__ - const ccl_global DecomposedTransform *cam_motion, + ccl_global const DecomposedTransform *cam_motion, #endif float raster_x, float raster_y, float lens_u, float lens_v, - ccl_addr_space Ray *ray) + ccl_private Ray *ray) { ProjectionTransform rastertocamera = cam->rastertocamera; float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f)); @@ -370,7 +370,7 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, /* Common */ -ccl_device_inline void camera_sample(const KernelGlobals *ccl_restrict kg, +ccl_device_inline void camera_sample(ccl_global const KernelGlobals *ccl_restrict kg, int x, int y, float filter_u, @@ -378,7 +378,7 @@ ccl_device_inline void camera_sample(const KernelGlobals *ccl_restrict kg, float lens_u, float lens_v, float time, - ccl_addr_space Ray *ray) + ccl_private Ray *ray) { /* pixel filter */ int filter_table_offset = kernel_data.film.filter_table_offset; @@ -434,7 +434,7 @@ ccl_device_inline void camera_sample(const KernelGlobals *ccl_restrict kg, } else { #ifdef __CAMERA_MOTION__ - const ccl_global DecomposedTransform *cam_motion = kernel_tex_array(__camera_motion); + ccl_global const DecomposedTransform *cam_motion = kernel_tex_array(__camera_motion); camera_sample_panorama(&kernel_data.cam, cam_motion, raster_x, raster_y, lens_u, lens_v, ray); #else camera_sample_panorama(&kernel_data.cam, raster_x, raster_y, lens_u, lens_v, ray); @@ -444,13 +444,13 @@ ccl_device_inline void camera_sample(const KernelGlobals *ccl_restrict kg, /* Utilities */ -ccl_device_inline float3 camera_position(const KernelGlobals *kg) +ccl_device_inline float3 camera_position(ccl_global const KernelGlobals *kg) { Transform cameratoworld = kernel_data.cam.cameratoworld; return make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); } -ccl_device_inline float camera_distance(const KernelGlobals *kg, float3 P) +ccl_device_inline float camera_distance(ccl_global const KernelGlobals *kg, float3 P) { Transform cameratoworld = kernel_data.cam.cameratoworld; float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); @@ -464,7 +464,7 @@ ccl_device_inline float camera_distance(const KernelGlobals *kg, float3 P) } } -ccl_device_inline float camera_z_depth(const KernelGlobals *kg, float3 P) +ccl_device_inline float camera_z_depth(ccl_global const KernelGlobals *kg, float3 P) { if (kernel_data.cam.type != CAMERA_PANORAMA) { Transform worldtocamera = kernel_data.cam.worldtocamera; @@ -477,7 +477,7 @@ ccl_device_inline float camera_z_depth(const KernelGlobals *kg, float3 P) } } -ccl_device_inline float3 camera_direction_from_point(const KernelGlobals *kg, float3 P) +ccl_device_inline float3 camera_direction_from_point(ccl_global const KernelGlobals *kg, float3 P) { Transform cameratoworld = kernel_data.cam.cameratoworld; @@ -491,7 +491,9 @@ ccl_device_inline float3 camera_direction_from_point(const KernelGlobals *kg, fl } } -ccl_device_inline float3 camera_world_to_ndc(const KernelGlobals *kg, ShaderData *sd, float3 P) +ccl_device_inline float3 camera_world_to_ndc(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + float3 P) { if (kernel_data.cam.type != CAMERA_PANORAMA) { /* perspective / ortho */ diff --git a/intern/cycles/kernel/kernel_color.h b/intern/cycles/kernel/kernel_color.h index 960774e0741..9e8e0e68b8f 100644 --- a/intern/cycles/kernel/kernel_color.h +++ b/intern/cycles/kernel/kernel_color.h @@ -20,14 +20,14 @@ CCL_NAMESPACE_BEGIN -ccl_device float3 xyz_to_rgb(const KernelGlobals *kg, float3 xyz) +ccl_device float3 xyz_to_rgb(ccl_global const KernelGlobals *kg, float3 xyz) { return make_float3(dot(float4_to_float3(kernel_data.film.xyz_to_r), xyz), dot(float4_to_float3(kernel_data.film.xyz_to_g), xyz), dot(float4_to_float3(kernel_data.film.xyz_to_b), xyz)); } -ccl_device float linear_rgb_to_gray(const KernelGlobals *kg, float3 c) +ccl_device float linear_rgb_to_gray(ccl_global const KernelGlobals *kg, float3 c) { return dot(c, float4_to_float3(kernel_data.film.rgb_to_y)); } diff --git a/intern/cycles/kernel/kernel_differential.h b/intern/cycles/kernel/kernel_differential.h index db4e110bd10..17187083019 100644 --- a/intern/cycles/kernel/kernel_differential.h +++ b/intern/cycles/kernel/kernel_differential.h @@ -20,7 +20,7 @@ CCL_NAMESPACE_BEGIN /* See "Tracing Ray Differentials", Homan Igehy, 1999. */ -ccl_device void differential_transfer(ccl_addr_space differential3 *surface_dP, +ccl_device void differential_transfer(ccl_private differential3 *surface_dP, const differential3 ray_dP, float3 ray_D, const differential3 ray_dD, @@ -38,7 +38,7 @@ ccl_device void differential_transfer(ccl_addr_space differential3 *surface_dP, surface_dP->dy = tmpy - dot(tmpy, surface_Ng) * tmp; } -ccl_device void differential_incoming(ccl_addr_space differential3 *dI, const differential3 dD) +ccl_device void differential_incoming(ccl_private differential3 *dI, const differential3 dD) { /* compute dIdx/dy at a shading point, we just need to negate the * differential of the ray direction */ @@ -47,8 +47,8 @@ ccl_device void differential_incoming(ccl_addr_space differential3 *dI, const di dI->dy = -dD.dy; } -ccl_device void differential_dudv(ccl_addr_space differential *du, - ccl_addr_space differential *dv, +ccl_device void differential_dudv(ccl_private differential *du, + ccl_private differential *dv, float3 dPdu, float3 dPdv, differential3 dP, @@ -132,7 +132,7 @@ ccl_device_forceinline float differential_make_compact(const differential3 D) return 0.5f * (len(D.dx) + len(D.dy)); } -ccl_device_forceinline void differential_transfer_compact(ccl_addr_space differential3 *surface_dP, +ccl_device_forceinline void differential_transfer_compact(ccl_private differential3 *surface_dP, const float ray_dP, const float3 /* ray_D */, const float ray_dD, @@ -149,7 +149,7 @@ ccl_device_forceinline void differential_transfer_compact(ccl_addr_space differe surface_dP->dy = dy * scale; } -ccl_device_forceinline void differential_incoming_compact(ccl_addr_space differential3 *dI, +ccl_device_forceinline void differential_incoming_compact(ccl_private differential3 *dI, const float3 D, const float dD) { diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h index d62285d173d..015587ccbbd 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/kernel_emission.h @@ -24,10 +24,11 @@ CCL_NAMESPACE_BEGIN /* Evaluate shader on light. */ -ccl_device_noinline_cpu float3 light_sample_shader_eval(INTEGRATOR_STATE_ARGS, - ShaderData *ccl_restrict emission_sd, - LightSample *ccl_restrict ls, - float time) +ccl_device_noinline_cpu float3 +light_sample_shader_eval(INTEGRATOR_STATE_ARGS, + ccl_private ShaderData *ccl_restrict emission_sd, + ccl_private LightSample *ccl_restrict ls, + float time) { /* setup shading at emitter */ float3 eval = zero_float3(); @@ -89,7 +90,7 @@ ccl_device_noinline_cpu float3 light_sample_shader_eval(INTEGRATOR_STATE_ARGS, eval *= ls->eval_fac; if (ls->lamp != LAMP_NONE) { - const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, ls->lamp); + ccl_global const KernelLight *klight = &kernel_tex_fetch(__lights, ls->lamp); eval *= make_float3(klight->strength[0], klight->strength[1], klight->strength[2]); } @@ -97,16 +98,16 @@ ccl_device_noinline_cpu float3 light_sample_shader_eval(INTEGRATOR_STATE_ARGS, } /* Test if light sample is from a light or emission from geometry. */ -ccl_device_inline bool light_sample_is_light(const LightSample *ccl_restrict ls) +ccl_device_inline bool light_sample_is_light(ccl_private const LightSample *ccl_restrict ls) { /* return if it's a lamp for shadow pass */ return (ls->prim == PRIM_NONE && ls->type != LIGHT_BACKGROUND); } /* Early path termination of shadow rays. */ -ccl_device_inline bool light_sample_terminate(const KernelGlobals *ccl_restrict kg, - const LightSample *ccl_restrict ls, - BsdfEval *ccl_restrict eval, +ccl_device_inline bool light_sample_terminate(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private const LightSample *ccl_restrict ls, + ccl_private BsdfEval *ccl_restrict eval, const float rand_terminate) { if (bsdf_eval_is_zero(eval)) { @@ -132,9 +133,10 @@ ccl_device_inline bool light_sample_terminate(const KernelGlobals *ccl_restrict * of a triangle. Surface is lifted by amount h along normal n in the incident * point. */ -ccl_device_inline float3 shadow_ray_smooth_surface_offset(const KernelGlobals *ccl_restrict kg, - const ShaderData *ccl_restrict sd, - float3 Ng) +ccl_device_inline float3 +shadow_ray_smooth_surface_offset(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private const ShaderData *ccl_restrict sd, + float3 Ng) { float3 V[3], N[3]; triangle_vertices_and_normals(kg, sd->prim, V, N); @@ -178,8 +180,8 @@ ccl_device_inline float3 shadow_ray_smooth_surface_offset(const KernelGlobals *c /* Ray offset to avoid shadow terminator artifact. */ -ccl_device_inline float3 shadow_ray_offset(const KernelGlobals *ccl_restrict kg, - const ShaderData *ccl_restrict sd, +ccl_device_inline float3 shadow_ray_offset(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private const ShaderData *ccl_restrict sd, float3 L) { float NL = dot(sd->N, L); @@ -211,10 +213,10 @@ ccl_device_inline float3 shadow_ray_offset(const KernelGlobals *ccl_restrict kg, return P; } -ccl_device_inline void shadow_ray_setup(const ShaderData *ccl_restrict sd, - const LightSample *ccl_restrict ls, +ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restrict sd, + ccl_private const LightSample *ccl_restrict ls, const float3 P, - Ray *ray) + ccl_private Ray *ray) { if (ls->shader & SHADER_CAST_SHADOW) { /* setup ray */ @@ -244,21 +246,23 @@ ccl_device_inline void shadow_ray_setup(const ShaderData *ccl_restrict sd, } /* Create shadow ray towards light sample. */ -ccl_device_inline void light_sample_to_surface_shadow_ray(const KernelGlobals *ccl_restrict kg, - const ShaderData *ccl_restrict sd, - const LightSample *ccl_restrict ls, - Ray *ray) +ccl_device_inline void light_sample_to_surface_shadow_ray( + ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const LightSample *ccl_restrict ls, + ccl_private Ray *ray) { const float3 P = shadow_ray_offset(kg, sd, ls->D); shadow_ray_setup(sd, ls, P, ray); } /* Create shadow ray towards light sample. */ -ccl_device_inline void light_sample_to_volume_shadow_ray(const KernelGlobals *ccl_restrict kg, - const ShaderData *ccl_restrict sd, - const LightSample *ccl_restrict ls, - const float3 P, - Ray *ray) +ccl_device_inline void light_sample_to_volume_shadow_ray( + ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const LightSample *ccl_restrict ls, + const float3 P, + ccl_private Ray *ray) { shadow_ray_setup(sd, ls, P, ray); } diff --git a/intern/cycles/kernel/kernel_film.h b/intern/cycles/kernel/kernel_film.h index e8f4a21878e..a87eff3832e 100644 --- a/intern/cycles/kernel/kernel_film.h +++ b/intern/cycles/kernel/kernel_film.h @@ -30,7 +30,8 @@ ccl_device_forceinline float film_transparency_to_alpha(float transparency) return saturate(1.0f - transparency); } -ccl_device_inline float film_get_scale(const KernelFilmConvert *ccl_restrict kfilm_convert, +ccl_device_inline float film_get_scale(ccl_global const KernelFilmConvert *ccl_restrict + kfilm_convert, ccl_global const float *ccl_restrict buffer) { if (kfilm_convert->pass_sample_count == PASS_UNUSED) { @@ -38,14 +39,15 @@ ccl_device_inline float film_get_scale(const KernelFilmConvert *ccl_restrict kfi } if (kfilm_convert->pass_use_filter) { - const uint sample_count = *((const uint *)(buffer + kfilm_convert->pass_sample_count)); + const uint sample_count = *( + (ccl_global const uint *)(buffer + kfilm_convert->pass_sample_count)); return 1.0f / sample_count; } return 1.0f; } -ccl_device_inline float film_get_scale_exposure(const KernelFilmConvert *ccl_restrict +ccl_device_inline float film_get_scale_exposure(ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer) { @@ -63,10 +65,10 @@ ccl_device_inline float film_get_scale_exposure(const KernelFilmConvert *ccl_res } ccl_device_inline bool film_get_scale_and_scale_exposure( - const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict scale, - float *ccl_restrict scale_exposure) + ccl_private float *ccl_restrict scale, + ccl_private float *ccl_restrict scale_exposure) { if (kfilm_convert->pass_sample_count == PASS_UNUSED) { *scale = kfilm_convert->scale; @@ -74,7 +76,8 @@ ccl_device_inline bool film_get_scale_and_scale_exposure( return true; } - const uint sample_count = *((const uint *)(buffer + kfilm_convert->pass_sample_count)); + const uint sample_count = *( + (ccl_global const uint *)(buffer + kfilm_convert->pass_sample_count)); if (!sample_count) { *scale = 0.0f; *scale_exposure = 0.0f; @@ -102,33 +105,33 @@ ccl_device_inline bool film_get_scale_and_scale_exposure( * Float (scalar) passes. */ -ccl_device_inline void film_get_pass_pixel_depth(const KernelFilmConvert *ccl_restrict +ccl_device_inline void film_get_pass_pixel_depth(ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components >= 1); kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); - const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in = buffer + kfilm_convert->pass_offset; const float f = *in; pixel[0] = (f == 0.0f) ? 1e10f : f * scale_exposure; } -ccl_device_inline void film_get_pass_pixel_mist(const KernelFilmConvert *ccl_restrict +ccl_device_inline void film_get_pass_pixel_mist(ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components >= 1); kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); - const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in = buffer + kfilm_convert->pass_offset; const float f = *in; /* Note that we accumulate 1 - mist in the kernel to avoid having to @@ -137,9 +140,9 @@ ccl_device_inline void film_get_pass_pixel_mist(const KernelFilmConvert *ccl_res } ccl_device_inline void film_get_pass_pixel_sample_count( - const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) + ccl_private float *ccl_restrict pixel) { /* TODO(sergey): Consider normalizing into the [0..1] range, so that it is possible to see * meaningful value when adaptive sampler stopped rendering image way before the maximum @@ -149,23 +152,23 @@ ccl_device_inline void film_get_pass_pixel_sample_count( kernel_assert(kfilm_convert->num_components >= 1); kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); - const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in = buffer + kfilm_convert->pass_offset; const float f = *in; pixel[0] = __float_as_uint(f) * kfilm_convert->scale; } -ccl_device_inline void film_get_pass_pixel_float(const KernelFilmConvert *ccl_restrict +ccl_device_inline void film_get_pass_pixel_float(ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components >= 1); kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); - const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in = buffer + kfilm_convert->pass_offset; const float f = *in; pixel[0] = f * scale_exposure; @@ -175,28 +178,28 @@ ccl_device_inline void film_get_pass_pixel_float(const KernelFilmConvert *ccl_re * Float 3 passes. */ -ccl_device_inline void film_get_pass_pixel_light_path(const KernelFilmConvert *ccl_restrict - kfilm_convert, - ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) +ccl_device_inline void film_get_pass_pixel_light_path( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components >= 3); kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); /* Read light pass. */ - const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in = buffer + kfilm_convert->pass_offset; float3 f = make_float3(in[0], in[1], in[2]); /* Optionally add indirect light pass. */ if (kfilm_convert->pass_indirect != PASS_UNUSED) { - const float *in_indirect = buffer + kfilm_convert->pass_indirect; + ccl_global const float *in_indirect = buffer + kfilm_convert->pass_indirect; const float3 f_indirect = make_float3(in_indirect[0], in_indirect[1], in_indirect[2]); f += f_indirect; } /* Optionally divide out color. */ if (kfilm_convert->pass_divide != PASS_UNUSED) { - const float *in_divide = buffer + kfilm_convert->pass_divide; + ccl_global const float *in_divide = buffer + kfilm_convert->pass_divide; const float3 f_divide = make_float3(in_divide[0], in_divide[1], in_divide[2]); f = safe_divide_even_color(f, f_divide); @@ -213,17 +216,17 @@ ccl_device_inline void film_get_pass_pixel_light_path(const KernelFilmConvert *c pixel[2] = f.z; } -ccl_device_inline void film_get_pass_pixel_float3(const KernelFilmConvert *ccl_restrict +ccl_device_inline void film_get_pass_pixel_float3(ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components >= 3); kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); - const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in = buffer + kfilm_convert->pass_offset; const float3 f = make_float3(in[0], in[1], in[2]) * scale_exposure; @@ -236,17 +239,17 @@ ccl_device_inline void film_get_pass_pixel_float3(const KernelFilmConvert *ccl_r * Float4 passes. */ -ccl_device_inline void film_get_pass_pixel_motion(const KernelFilmConvert *ccl_restrict +ccl_device_inline void film_get_pass_pixel_motion(ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components == 4); kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); kernel_assert(kfilm_convert->pass_motion_weight != PASS_UNUSED); - const float *in = buffer + kfilm_convert->pass_offset; - const float *in_weight = buffer + kfilm_convert->pass_motion_weight; + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in_weight = buffer + kfilm_convert->pass_motion_weight; const float weight = in_weight[0]; const float weight_inv = (weight > 0.0f) ? 1.0f / weight : 0.0f; @@ -259,17 +262,17 @@ ccl_device_inline void film_get_pass_pixel_motion(const KernelFilmConvert *ccl_r pixel[3] = motion.w; } -ccl_device_inline void film_get_pass_pixel_cryptomatte(const KernelFilmConvert *ccl_restrict - kfilm_convert, - ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) +ccl_device_inline void film_get_pass_pixel_cryptomatte( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components == 4); kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); const float scale = film_get_scale(kfilm_convert, buffer); - const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in = buffer + kfilm_convert->pass_offset; const float4 f = make_float4(in[0], in[1], in[2], in[3]); @@ -281,10 +284,10 @@ ccl_device_inline void film_get_pass_pixel_cryptomatte(const KernelFilmConvert * pixel[3] = f.w * scale; } -ccl_device_inline void film_get_pass_pixel_float4(const KernelFilmConvert *ccl_restrict +ccl_device_inline void film_get_pass_pixel_float4(ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components == 4); kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); @@ -292,7 +295,7 @@ ccl_device_inline void film_get_pass_pixel_float4(const KernelFilmConvert *ccl_r float scale, scale_exposure; film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure); - const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in = buffer + kfilm_convert->pass_offset; const float3 color = make_float3(in[0], in[1], in[2]) * scale_exposure; const float alpha = in[3] * scale; @@ -303,10 +306,10 @@ ccl_device_inline void film_get_pass_pixel_float4(const KernelFilmConvert *ccl_r pixel[3] = alpha; } -ccl_device_inline void film_get_pass_pixel_combined(const KernelFilmConvert *ccl_restrict - kfilm_convert, - ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) +ccl_device_inline void film_get_pass_pixel_combined( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components == 4); @@ -324,7 +327,7 @@ ccl_device_inline void film_get_pass_pixel_combined(const KernelFilmConvert *ccl return; } - const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in = buffer + kfilm_convert->pass_offset; const float3 color = make_float3(in[0], in[1], in[2]) * scale_exposure; const float alpha = in[3] * scale; @@ -339,9 +342,9 @@ ccl_device_inline void film_get_pass_pixel_combined(const KernelFilmConvert *ccl * Shadow catcher. */ -ccl_device_inline float3 -film_calculate_shadow_catcher_denoised(const KernelFilmConvert *ccl_restrict kfilm_convert, - ccl_global const float *ccl_restrict buffer) +ccl_device_inline float3 film_calculate_shadow_catcher_denoised( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer) { kernel_assert(kfilm_convert->pass_shadow_catcher != PASS_UNUSED); @@ -367,7 +370,7 @@ ccl_device_inline float3 safe_divide_shadow_catcher(float3 a, float3 b) } ccl_device_inline float3 -film_calculate_shadow_catcher(const KernelFilmConvert *ccl_restrict kfilm_convert, +film_calculate_shadow_catcher(ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer) { /* For the shadow catcher pass we divide combined pass by the shadow catcher. @@ -431,7 +434,7 @@ film_calculate_shadow_catcher(const KernelFilmConvert *ccl_restrict kfilm_conver } ccl_device_inline float4 film_calculate_shadow_catcher_matte_with_shadow( - const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer) { /* The approximation of the shadow is 1 - average(shadow_catcher_pass). A better approximation @@ -474,9 +477,9 @@ ccl_device_inline float4 film_calculate_shadow_catcher_matte_with_shadow( } ccl_device_inline void film_get_pass_pixel_shadow_catcher( - const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components >= 3); @@ -488,9 +491,9 @@ ccl_device_inline void film_get_pass_pixel_shadow_catcher( } ccl_device_inline void film_get_pass_pixel_shadow_catcher_matte_with_shadow( - const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) + ccl_private float *ccl_restrict pixel) { kernel_assert(kfilm_convert->num_components == 3 || kfilm_convert->num_components == 4); @@ -510,9 +513,9 @@ ccl_device_inline void film_get_pass_pixel_shadow_catcher_matte_with_shadow( */ ccl_device_inline void film_apply_pass_pixel_overlays_rgba( - const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, ccl_global const float *ccl_restrict buffer, - float *ccl_restrict pixel) + ccl_private float *ccl_restrict pixel) { if (kfilm_convert->show_active_pixels && kfilm_convert->pass_adaptive_aux_buffer != PASS_UNUSED) { diff --git a/intern/cycles/kernel/kernel_id_passes.h b/intern/cycles/kernel/kernel_id_passes.h index ed01f494f98..07b96d0e1a8 100644 --- a/intern/cycles/kernel/kernel_id_passes.h +++ b/intern/cycles/kernel/kernel_id_passes.h @@ -92,7 +92,7 @@ ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_sl } /* post-sorting for Cryptomatte */ -ccl_device_inline void kernel_cryptomatte_post(const KernelGlobals *kg, +ccl_device_inline void kernel_cryptomatte_post(ccl_global const KernelGlobals *kg, ccl_global float *render_buffer, int pixel_index) { diff --git a/intern/cycles/kernel/kernel_jitter.h b/intern/cycles/kernel/kernel_jitter.h index 1beaf3cc2b2..1f745ab1da9 100644 --- a/intern/cycles/kernel/kernel_jitter.h +++ b/intern/cycles/kernel/kernel_jitter.h @@ -72,7 +72,10 @@ ccl_device_inline float cmj_randfloat_simple(uint i, uint p) return cmj_hash_simple(i, p) * (1.0f / (float)0xFFFFFFFF); } -ccl_device float pmj_sample_1D(const KernelGlobals *kg, uint sample, uint rng_hash, uint dimension) +ccl_device float pmj_sample_1D(ccl_global const KernelGlobals *kg, + uint sample, + uint rng_hash, + uint dimension) { /* Perform Owen shuffle of the sample number to reorder the samples. */ #ifdef _SIMPLE_HASH_ @@ -115,8 +118,12 @@ ccl_device float pmj_sample_1D(const KernelGlobals *kg, uint sample, uint rng_ha return fx; } -ccl_device void pmj_sample_2D( - const KernelGlobals *kg, uint sample, uint rng_hash, uint dimension, float *x, float *y) +ccl_device void pmj_sample_2D(ccl_global const KernelGlobals *kg, + uint sample, + uint rng_hash, + uint dimension, + ccl_private float *x, + ccl_private float *y) { /* Perform a shuffle on the sample number to reorder the samples. */ #ifdef _SIMPLE_HASH_ diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index 52f641634b9..33d0c09a32a 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -45,13 +45,13 @@ typedef struct LightSample { /* Regular Light */ template -ccl_device_inline bool light_sample(const KernelGlobals *kg, +ccl_device_inline bool light_sample(ccl_global const KernelGlobals *kg, const int lamp, const float randu, const float randv, const float3 P, const int path_flag, - LightSample *ls) + ccl_private LightSample *ls) { const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) { @@ -209,9 +209,9 @@ ccl_device_inline bool light_sample(const KernelGlobals *kg, return (ls->pdf > 0.0f); } -ccl_device bool lights_intersect(const KernelGlobals *ccl_restrict kg, - const Ray *ccl_restrict ray, - Intersection *ccl_restrict isect, +ccl_device bool lights_intersect(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private const Ray *ccl_restrict ray, + ccl_private Intersection *ccl_restrict isect, const int last_prim, const int last_object, const int last_type, @@ -298,12 +298,12 @@ ccl_device bool lights_intersect(const KernelGlobals *ccl_restrict kg, return isect->prim != PRIM_NONE; } -ccl_device bool light_sample_from_distant_ray(const KernelGlobals *ccl_restrict kg, +ccl_device bool light_sample_from_distant_ray(ccl_global const KernelGlobals *ccl_restrict kg, const float3 ray_D, const int lamp, - LightSample *ccl_restrict ls) + ccl_private LightSample *ccl_restrict ls) { - const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + ccl_global const KernelLight *klight = &kernel_tex_fetch(__lights, lamp); const int shader = klight->shader_id; const float radius = klight->distant.radius; const LightType type = (LightType)klight->type; @@ -362,14 +362,14 @@ ccl_device bool light_sample_from_distant_ray(const KernelGlobals *ccl_restrict return true; } -ccl_device bool light_sample_from_intersection(const KernelGlobals *ccl_restrict kg, - const Intersection *ccl_restrict isect, +ccl_device bool light_sample_from_intersection(ccl_global const KernelGlobals *ccl_restrict kg, + ccl_private const Intersection *ccl_restrict isect, const float3 ray_P, const float3 ray_D, - LightSample *ccl_restrict ls) + ccl_private LightSample *ccl_restrict ls) { const int lamp = isect->prim; - const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + ccl_global const KernelLight *klight = &kernel_tex_fetch(__lights, lamp); LightType type = (LightType)klight->type; ls->type = type; ls->shader = klight->shader_id; @@ -464,7 +464,7 @@ ccl_device bool light_sample_from_intersection(const KernelGlobals *ccl_restrict /* returns true if the triangle is has motion blur or an instancing transform applied */ ccl_device_inline bool triangle_world_space_vertices( - const KernelGlobals *kg, int object, int prim, float time, float3 V[3]) + ccl_global const KernelGlobals *kg, int object, int prim, float time, float3 V[3]) { bool has_motion = false; const int object_flag = kernel_tex_fetch(__object_flag, object); @@ -492,7 +492,7 @@ ccl_device_inline bool triangle_world_space_vertices( return has_motion; } -ccl_device_inline float triangle_light_pdf_area(const KernelGlobals *kg, +ccl_device_inline float triangle_light_pdf_area(ccl_global const KernelGlobals *kg, const float3 Ng, const float3 I, float t) @@ -506,8 +506,8 @@ ccl_device_inline float triangle_light_pdf_area(const KernelGlobals *kg, return t * t * pdf / cos_pi; } -ccl_device_forceinline float triangle_light_pdf(const KernelGlobals *kg, - const ShaderData *sd, +ccl_device_forceinline float triangle_light_pdf(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, float t) { /* A naive heuristic to decide between costly solid angle sampling @@ -578,13 +578,13 @@ ccl_device_forceinline float triangle_light_pdf(const KernelGlobals *kg, } template -ccl_device_forceinline void triangle_light_sample(const KernelGlobals *kg, +ccl_device_forceinline void triangle_light_sample(ccl_global const KernelGlobals *kg, int prim, int object, float randu, float randv, float time, - LightSample *ls, + ccl_private LightSample *ls, const float3 P) { /* A naive heuristic to decide between costly solid angle sampling @@ -747,7 +747,8 @@ ccl_device_forceinline void triangle_light_sample(const KernelGlobals *kg, /* Light Distribution */ -ccl_device int light_distribution_sample(const KernelGlobals *kg, float *randu) +ccl_device int light_distribution_sample(ccl_global const KernelGlobals *kg, + ccl_private float *randu) { /* This is basically std::upper_bound as used by PBRT, to find a point light or * triangle to emit from, proportional to area. a good improvement would be to @@ -785,7 +786,7 @@ ccl_device int light_distribution_sample(const KernelGlobals *kg, float *randu) /* Generic Light */ -ccl_device_inline bool light_select_reached_max_bounces(const KernelGlobals *kg, +ccl_device_inline bool light_select_reached_max_bounces(ccl_global const KernelGlobals *kg, int index, int bounce) { @@ -793,18 +794,18 @@ ccl_device_inline bool light_select_reached_max_bounces(const KernelGlobals *kg, } template -ccl_device_noinline bool light_distribution_sample(const KernelGlobals *kg, +ccl_device_noinline bool light_distribution_sample(ccl_global const KernelGlobals *kg, float randu, const float randv, const float time, const float3 P, const int bounce, const int path_flag, - LightSample *ls) + ccl_private LightSample *ls) { /* Sample light index from distribution. */ const int index = light_distribution_sample(kg, &randu); - const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch(__light_distribution, + ccl_global const KernelLightDistribution *kdistribution = &kernel_tex_fetch(__light_distribution, index); const int prim = kdistribution->prim; @@ -833,36 +834,37 @@ ccl_device_noinline bool light_distribution_sample(const KernelGlobals *kg, return light_sample(kg, lamp, randu, randv, P, path_flag, ls); } -ccl_device_inline bool light_distribution_sample_from_volume_segment(const KernelGlobals *kg, - float randu, - const float randv, - const float time, - const float3 P, - const int bounce, - const int path_flag, - LightSample *ls) +ccl_device_inline bool light_distribution_sample_from_volume_segment( + ccl_global const KernelGlobals *kg, + float randu, + const float randv, + const float time, + const float3 P, + const int bounce, + const int path_flag, + ccl_private LightSample *ls) { return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); } -ccl_device_inline bool light_distribution_sample_from_position(const KernelGlobals *kg, +ccl_device_inline bool light_distribution_sample_from_position(ccl_global const KernelGlobals *kg, float randu, const float randv, const float time, const float3 P, const int bounce, const int path_flag, - LightSample *ls) + ccl_private LightSample *ls) { return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); } -ccl_device_inline bool light_distribution_sample_new_position(const KernelGlobals *kg, +ccl_device_inline bool light_distribution_sample_new_position(ccl_global const KernelGlobals *kg, const float randu, const float randv, const float time, const float3 P, - LightSample *ls) + ccl_private LightSample *ls) { /* Sample a new position on the same light, for volume sampling. */ if (ls->type == LIGHT_TRIANGLE) { diff --git a/intern/cycles/kernel/kernel_light_background.h b/intern/cycles/kernel/kernel_light_background.h index 493ed560bc6..3669ff50455 100644 --- a/intern/cycles/kernel/kernel_light_background.h +++ b/intern/cycles/kernel/kernel_light_background.h @@ -24,10 +24,10 @@ CCL_NAMESPACE_BEGIN #ifdef __BACKGROUND_MIS__ -ccl_device float3 background_map_sample(const KernelGlobals *kg, +ccl_device float3 background_map_sample(ccl_global const KernelGlobals *kg, float randu, float randv, - float *pdf) + ccl_private float *pdf) { /* for the following, the CDF values are actually a pair of floats, with the * function value as X and the actual CDF as Y. The last entry's function @@ -109,7 +109,7 @@ ccl_device float3 background_map_sample(const KernelGlobals *kg, /* TODO(sergey): Same as above, after the release we should consider using * 'noinline' for all devices. */ -ccl_device float background_map_pdf(const KernelGlobals *kg, float3 direction) +ccl_device float background_map_pdf(ccl_global const KernelGlobals *kg, float3 direction) { float2 uv = direction_to_equirectangular(direction); int res_x = kernel_data.background.map_res_x; @@ -143,7 +143,11 @@ ccl_device float background_map_pdf(const KernelGlobals *kg, float3 direction) } ccl_device_inline bool background_portal_data_fetch_and_check_side( - const KernelGlobals *kg, float3 P, int index, float3 *lightpos, float3 *dir) + ccl_global const KernelGlobals *kg, + float3 P, + int index, + ccl_private float3 *lightpos, + ccl_private float3 *dir) { int portal = kernel_data.background.portal_offset + index; const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); @@ -158,8 +162,11 @@ ccl_device_inline bool background_portal_data_fetch_and_check_side( return false; } -ccl_device_inline float background_portal_pdf( - const KernelGlobals *kg, float3 P, float3 direction, int ignore_portal, bool *is_possible) +ccl_device_inline float background_portal_pdf(ccl_global const KernelGlobals *kg, + float3 P, + float3 direction, + int ignore_portal, + ccl_private bool *is_possible) { float portal_pdf = 0.0f; @@ -219,7 +226,7 @@ ccl_device_inline float background_portal_pdf( return (num_possible > 0) ? portal_pdf / num_possible : 0.0f; } -ccl_device int background_num_possible_portals(const KernelGlobals *kg, float3 P) +ccl_device int background_num_possible_portals(ccl_global const KernelGlobals *kg, float3 P) { int num_possible_portals = 0; for (int p = 0; p < kernel_data.background.num_portals; p++) { @@ -230,13 +237,13 @@ ccl_device int background_num_possible_portals(const KernelGlobals *kg, float3 P return num_possible_portals; } -ccl_device float3 background_portal_sample(const KernelGlobals *kg, +ccl_device float3 background_portal_sample(ccl_global const KernelGlobals *kg, float3 P, float randu, float randv, int num_possible, - int *sampled_portal, - float *pdf) + ccl_private int *sampled_portal, + ccl_private float *pdf) { /* Pick a portal, then re-normalize randv. */ randv *= num_possible; @@ -285,10 +292,10 @@ ccl_device float3 background_portal_sample(const KernelGlobals *kg, return zero_float3(); } -ccl_device_inline float3 background_sun_sample(const KernelGlobals *kg, +ccl_device_inline float3 background_sun_sample(ccl_global const KernelGlobals *kg, float randu, float randv, - float *pdf) + ccl_private float *pdf) { float3 D; const float3 N = float4_to_float3(kernel_data.background.sun); @@ -297,15 +304,15 @@ ccl_device_inline float3 background_sun_sample(const KernelGlobals *kg, return D; } -ccl_device_inline float background_sun_pdf(const KernelGlobals *kg, float3 D) +ccl_device_inline float background_sun_pdf(ccl_global const KernelGlobals *kg, float3 D) { const float3 N = float4_to_float3(kernel_data.background.sun); const float angle = kernel_data.background.sun.w; return pdf_uniform_cone(N, D, angle); } -ccl_device_inline float3 -background_light_sample(const KernelGlobals *kg, float3 P, float randu, float randv, float *pdf) +ccl_device_inline float3 background_light_sample( + ccl_global const KernelGlobals *kg, float3 P, float randu, float randv, ccl_private float *pdf) { float portal_method_pdf = kernel_data.background.portal_weight; float sun_method_pdf = kernel_data.background.sun_weight; @@ -405,7 +412,9 @@ background_light_sample(const KernelGlobals *kg, float3 P, float randu, float ra return D; } -ccl_device float background_light_pdf(const KernelGlobals *kg, float3 P, float3 direction) +ccl_device float background_light_pdf(ccl_global const KernelGlobals *kg, + float3 P, + float3 direction) { float portal_method_pdf = kernel_data.background.portal_weight; float sun_method_pdf = kernel_data.background.sun_weight; diff --git a/intern/cycles/kernel/kernel_light_common.h b/intern/cycles/kernel/kernel_light_common.h index 765d8f5338e..9421ac462e2 100644 --- a/intern/cycles/kernel/kernel_light_common.h +++ b/intern/cycles/kernel/kernel_light_common.h @@ -32,7 +32,7 @@ CCL_NAMESPACE_BEGIN * Note: light_p is modified when sample_coord is true. */ ccl_device_inline float rect_light_sample(float3 P, - float3 *light_p, + ccl_private float3 *light_p, float3 axisu, float3 axisv, float randu, @@ -167,9 +167,9 @@ ccl_device float light_spread_attenuation(const float3 D, * reduce noise with low spread. */ ccl_device bool light_spread_clamp_area_light(const float3 P, const float3 lightNg, - float3 *lightP, - float3 *axisu, - float3 *axisv, + ccl_private float3 *lightP, + ccl_private float3 *axisu, + ccl_private float3 *axisv, const float tan_spread) { /* Closest point in area light plane and distance to that plane. */ @@ -214,7 +214,10 @@ ccl_device bool light_spread_clamp_area_light(const float3 P, return true; } -ccl_device float lamp_light_pdf(const KernelGlobals *kg, const float3 Ng, const float3 I, float t) +ccl_device float lamp_light_pdf(ccl_global const KernelGlobals *kg, + const float3 Ng, + const float3 I, + float t) { float cos_pi = dot(Ng, I); diff --git a/intern/cycles/kernel/kernel_lookup_table.h b/intern/cycles/kernel/kernel_lookup_table.h index 33d9d5ae1f0..3c8577af417 100644 --- a/intern/cycles/kernel/kernel_lookup_table.h +++ b/intern/cycles/kernel/kernel_lookup_table.h @@ -20,7 +20,10 @@ CCL_NAMESPACE_BEGIN /* Interpolated lookup table access */ -ccl_device float lookup_table_read(const KernelGlobals *kg, float x, int offset, int size) +ccl_device float lookup_table_read(ccl_global const KernelGlobals *kg, + float x, + int offset, + int size) { x = saturate(x) * (size - 1); @@ -37,7 +40,7 @@ ccl_device float lookup_table_read(const KernelGlobals *kg, float x, int offset, } ccl_device float lookup_table_read_2D( - const KernelGlobals *kg, float x, float y, int offset, int xsize, int ysize) + ccl_global const KernelGlobals *kg, float x, float y, int offset, int xsize, int ysize) { y = saturate(y) * (ysize - 1); diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/kernel_montecarlo.h index b158f4c4fd3..c931aa45276 100644 --- a/intern/cycles/kernel/kernel_montecarlo.h +++ b/intern/cycles/kernel/kernel_montecarlo.h @@ -35,7 +35,7 @@ CCL_NAMESPACE_BEGIN /* distribute uniform xy on [0,1] over unit disk [-1,1] */ -ccl_device void to_unit_disk(float *x, float *y) +ccl_device void to_unit_disk(ccl_private float *x, ccl_private float *y) { float phi = M_2PI_F * (*x); float r = sqrtf(*y); @@ -46,7 +46,10 @@ ccl_device void to_unit_disk(float *x, float *y) /* return an orthogonal tangent and bitangent given a normal and tangent that * may not be exactly orthogonal */ -ccl_device void make_orthonormals_tangent(const float3 N, const float3 T, float3 *a, float3 *b) +ccl_device void make_orthonormals_tangent(const float3 N, + const float3 T, + ccl_private float3 *a, + ccl_private float3 *b) { *b = normalize(cross(N, T)); *a = cross(*b, N); @@ -54,7 +57,7 @@ ccl_device void make_orthonormals_tangent(const float3 N, const float3 T, float3 /* sample direction with cosine weighted distributed in hemisphere */ ccl_device_inline void sample_cos_hemisphere( - const float3 N, float randu, float randv, float3 *omega_in, float *pdf) + const float3 N, float randu, float randv, ccl_private float3 *omega_in, ccl_private float *pdf) { to_unit_disk(&randu, &randv); float costheta = sqrtf(max(1.0f - randu * randu - randv * randv, 0.0f)); @@ -66,7 +69,7 @@ ccl_device_inline void sample_cos_hemisphere( /* sample direction uniformly distributed in hemisphere */ ccl_device_inline void sample_uniform_hemisphere( - const float3 N, float randu, float randv, float3 *omega_in, float *pdf) + const float3 N, float randu, float randv, ccl_private float3 *omega_in, ccl_private float *pdf) { float z = randu; float r = sqrtf(max(0.0f, 1.0f - z * z)); @@ -81,8 +84,12 @@ ccl_device_inline void sample_uniform_hemisphere( } /* sample direction uniformly distributed in cone */ -ccl_device_inline void sample_uniform_cone( - const float3 N, float angle, float randu, float randv, float3 *omega_in, float *pdf) +ccl_device_inline void sample_uniform_cone(const float3 N, + float angle, + float randu, + float randv, + ccl_private float3 *omega_in, + ccl_private float *pdf) { float zMin = cosf(angle); float z = zMin - zMin * randu + randu; diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h index 67466b28170..b981e750dda 100644 --- a/intern/cycles/kernel/kernel_passes.h +++ b/intern/cycles/kernel/kernel_passes.h @@ -36,7 +36,9 @@ ccl_device_forceinline ccl_global float *kernel_pass_pixel_render_buffer( #ifdef __DENOISING_FEATURES__ ccl_device_forceinline void kernel_write_denoising_features_surface( - INTEGRATOR_STATE_ARGS, const ShaderData *sd, ccl_global float *ccl_restrict render_buffer) + INTEGRATOR_STATE_ARGS, + ccl_private const ShaderData *sd, + ccl_global float *ccl_restrict render_buffer) { if (!(INTEGRATOR_STATE(path, flag) & PATH_RAY_DENOISING_FEATURES)) { return; @@ -55,7 +57,7 @@ ccl_device_forceinline void kernel_write_denoising_features_surface( float sum_weight = 0.0f, sum_nonspecular_weight = 0.0f; for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { continue; @@ -71,11 +73,11 @@ ccl_device_forceinline void kernel_write_denoising_features_surface( * To account for this, we scale their weight by the average fresnel factor (the same is also * done for the sample weight in the BSDF setup, so we don't need to scale that here). */ if (CLOSURE_IS_BSDF_MICROFACET_FRESNEL(sc->type)) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; closure_albedo *= bsdf->extra->fresnel_color; } else if (sc->type == CLOSURE_BSDF_PRINCIPLED_SHEEN_ID) { - PrincipledSheenBsdf *bsdf = (PrincipledSheenBsdf *)sc; + ccl_private PrincipledSheenBsdf *bsdf = (ccl_private PrincipledSheenBsdf *)sc; closure_albedo *= bsdf->avg_value; } else if (sc->type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) { @@ -151,7 +153,9 @@ ccl_device_forceinline void kernel_write_denoising_features_volume(INTEGRATOR_ST /* Write shadow catcher passes on a bounce from the shadow catcher object. */ ccl_device_forceinline void kernel_write_shadow_catcher_bounce_data( - INTEGRATOR_STATE_ARGS, const ShaderData *sd, ccl_global float *ccl_restrict render_buffer) + INTEGRATOR_STATE_ARGS, + ccl_private const ShaderData *sd, + ccl_global float *ccl_restrict render_buffer) { if (!kernel_data.integrator.has_shadow_catcher) { return; @@ -178,7 +182,7 @@ ccl_device_forceinline void kernel_write_shadow_catcher_bounce_data( #endif /* __SHADOW_CATCHER__ */ -ccl_device_inline size_t kernel_write_id_pass(float *ccl_restrict buffer, +ccl_device_inline size_t kernel_write_id_pass(ccl_global float *ccl_restrict buffer, size_t depth, float id, float matte_weight) @@ -188,7 +192,7 @@ ccl_device_inline size_t kernel_write_id_pass(float *ccl_restrict buffer, } ccl_device_inline void kernel_write_data_passes(INTEGRATOR_STATE_ARGS, - const ShaderData *sd, + ccl_private const ShaderData *sd, ccl_global float *ccl_restrict render_buffer) { #ifdef __PASSES__ diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h index ebb2c0df4f1..e04ed5b1cc1 100644 --- a/intern/cycles/kernel/kernel_path_state.h +++ b/intern/cycles/kernel/kernel_path_state.h @@ -32,7 +32,7 @@ ccl_device_inline void path_state_init_queues(INTEGRATOR_STATE_ARGS) /* Minimalistic initialization of the path state, which is needed for early outputs in the * integrator initialization to work. */ ccl_device_inline void path_state_init(INTEGRATOR_STATE_ARGS, - const ccl_global KernelWorkTile *ccl_restrict tile, + ccl_global const KernelWorkTile *ccl_restrict tile, const int x, const int y) { @@ -281,14 +281,16 @@ typedef struct RNGState { int sample; } RNGState; -ccl_device_inline void path_state_rng_load(INTEGRATOR_STATE_CONST_ARGS, RNGState *rng_state) +ccl_device_inline void path_state_rng_load(INTEGRATOR_STATE_CONST_ARGS, + ccl_private RNGState *rng_state) { rng_state->rng_hash = INTEGRATOR_STATE(path, rng_hash); rng_state->rng_offset = INTEGRATOR_STATE(path, rng_offset); rng_state->sample = INTEGRATOR_STATE(path, sample); } -ccl_device_inline void shadow_path_state_rng_load(INTEGRATOR_STATE_CONST_ARGS, RNGState *rng_state) +ccl_device_inline void shadow_path_state_rng_load(INTEGRATOR_STATE_CONST_ARGS, + ccl_private RNGState *rng_state) { const uint shadow_bounces = INTEGRATOR_STATE(shadow_path, transparent_bounce) - INTEGRATOR_STATE(path, transparent_bounce); @@ -298,23 +300,26 @@ ccl_device_inline void shadow_path_state_rng_load(INTEGRATOR_STATE_CONST_ARGS, R rng_state->sample = INTEGRATOR_STATE(path, sample); } -ccl_device_inline float path_state_rng_1D(const KernelGlobals *kg, - const RNGState *rng_state, +ccl_device_inline float path_state_rng_1D(ccl_global const KernelGlobals *kg, + ccl_private const RNGState *rng_state, int dimension) { return path_rng_1D( kg, rng_state->rng_hash, rng_state->sample, rng_state->rng_offset + dimension); } -ccl_device_inline void path_state_rng_2D( - const KernelGlobals *kg, const RNGState *rng_state, int dimension, float *fx, float *fy) +ccl_device_inline void path_state_rng_2D(ccl_global const KernelGlobals *kg, + ccl_private const RNGState *rng_state, + int dimension, + ccl_private float *fx, + ccl_private float *fy) { path_rng_2D( kg, rng_state->rng_hash, rng_state->sample, rng_state->rng_offset + dimension, fx, fy); } -ccl_device_inline float path_state_rng_1D_hash(const KernelGlobals *kg, - const RNGState *rng_state, +ccl_device_inline float path_state_rng_1D_hash(ccl_global const KernelGlobals *kg, + ccl_private const RNGState *rng_state, uint hash) { /* Use a hash instead of dimension, this is not great but avoids adding @@ -324,8 +329,8 @@ ccl_device_inline float path_state_rng_1D_hash(const KernelGlobals *kg, kg, cmj_hash_simple(rng_state->rng_hash, hash), rng_state->sample, rng_state->rng_offset); } -ccl_device_inline float path_branched_rng_1D(const KernelGlobals *kg, - const RNGState *rng_state, +ccl_device_inline float path_branched_rng_1D(ccl_global const KernelGlobals *kg, + ccl_private const RNGState *rng_state, int branch, int num_branches, int dimension) @@ -336,13 +341,13 @@ ccl_device_inline float path_branched_rng_1D(const KernelGlobals *kg, rng_state->rng_offset + dimension); } -ccl_device_inline void path_branched_rng_2D(const KernelGlobals *kg, - const RNGState *rng_state, +ccl_device_inline void path_branched_rng_2D(ccl_global const KernelGlobals *kg, + ccl_private const RNGState *rng_state, int branch, int num_branches, int dimension, - float *fx, - float *fy) + ccl_private float *fx, + ccl_private float *fy) { path_rng_2D(kg, rng_state->rng_hash, @@ -355,8 +360,8 @@ ccl_device_inline void path_branched_rng_2D(const KernelGlobals *kg, /* Utility functions to get light termination value, * since it might not be needed in many cases. */ -ccl_device_inline float path_state_rng_light_termination(const KernelGlobals *kg, - const RNGState *state) +ccl_device_inline float path_state_rng_light_termination(ccl_global const KernelGlobals *kg, + ccl_private const RNGState *state) { if (kernel_data.integrator.light_inv_rr_threshold > 0.0f) { return path_state_rng_1D(kg, state, PRNG_LIGHT_TERMINATE); diff --git a/intern/cycles/kernel/kernel_projection.h b/intern/cycles/kernel/kernel_projection.h index 192bf7ca5aa..0aea82fa812 100644 --- a/intern/cycles/kernel/kernel_projection.h +++ b/intern/cycles/kernel/kernel_projection.h @@ -215,8 +215,8 @@ ccl_device_inline float2 direction_to_panorama(ccl_constant KernelCamera *cam, f } ccl_device_inline void spherical_stereo_transform(ccl_constant KernelCamera *cam, - float3 *P, - float3 *D) + ccl_private float3 *P, + ccl_private float3 *D) { float interocular_offset = cam->interocular_offset; diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h index 240c92bf9d0..7db4289acec 100644 --- a/intern/cycles/kernel/kernel_random.h +++ b/intern/cycles/kernel/kernel_random.h @@ -38,7 +38,7 @@ CCL_NAMESPACE_BEGIN */ # define SOBOL_SKIP 64 -ccl_device uint sobol_dimension(const KernelGlobals *kg, int index, int dimension) +ccl_device uint sobol_dimension(ccl_global const KernelGlobals *kg, int index, int dimension) { uint result = 0; uint i = index + SOBOL_SKIP; @@ -51,7 +51,7 @@ ccl_device uint sobol_dimension(const KernelGlobals *kg, int index, int dimensio #endif /* __SOBOL__ */ -ccl_device_forceinline float path_rng_1D(const KernelGlobals *kg, +ccl_device_forceinline float path_rng_1D(ccl_global const KernelGlobals *kg, uint rng_hash, int sample, int dimension) @@ -85,8 +85,12 @@ ccl_device_forceinline float path_rng_1D(const KernelGlobals *kg, #endif } -ccl_device_forceinline void path_rng_2D( - const KernelGlobals *kg, uint rng_hash, int sample, int dimension, float *fx, float *fy) +ccl_device_forceinline void path_rng_2D(ccl_global const KernelGlobals *kg, + uint rng_hash, + int sample, + int dimension, + ccl_private float *fx, + ccl_private float *fy) { #ifdef __DEBUG_CORRELATION__ *fx = (float)drand48(); @@ -137,7 +141,7 @@ ccl_device_inline uint hash_iqnt2d(const uint x, const uint y) return n; } -ccl_device_inline uint path_rng_hash_init(const KernelGlobals *ccl_restrict kg, +ccl_device_inline uint path_rng_hash_init(ccl_global const KernelGlobals *ccl_restrict kg, const int sample, const int x, const int y) @@ -184,13 +188,6 @@ ccl_device_inline uint lcg_state_init(const uint rng_hash, return lcg_init(rng_hash + rng_offset + sample * scramble); } -ccl_device float lcg_step_float_addrspace(ccl_addr_space uint *rng) -{ - /* Implicit mod 2^32 */ - *rng = (1103515245 * (*rng) + 12345); - return (float)*rng * (1.0f / (float)0xFFFFFFFF); -} - ccl_device_inline bool sample_is_even(int pattern, int sample) { if (pattern == SAMPLING_PATTERN_PMJ) { diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index d1b53832793..4174a27406b 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -37,24 +37,26 @@ CCL_NAMESPACE_BEGIN /* Merging */ #if defined(__VOLUME__) -ccl_device_inline void shader_merge_volume_closures(ShaderData *sd) +ccl_device_inline void shader_merge_volume_closures(ccl_private ShaderData *sd) { /* Merge identical closures to save closure space with stacked volumes. */ for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sci = &sd->closure[i]; + ccl_private ShaderClosure *sci = &sd->closure[i]; if (sci->type != CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { continue; } for (int j = i + 1; j < sd->num_closure; j++) { - ShaderClosure *scj = &sd->closure[j]; + ccl_private ShaderClosure *scj = &sd->closure[j]; if (sci->type != scj->type) { continue; } - const HenyeyGreensteinVolume *hgi = (const HenyeyGreensteinVolume *)sci; - const HenyeyGreensteinVolume *hgj = (const HenyeyGreensteinVolume *)scj; + ccl_private const HenyeyGreensteinVolume *hgi = (ccl_private const HenyeyGreensteinVolume *) + sci; + ccl_private const HenyeyGreensteinVolume *hgj = (ccl_private const HenyeyGreensteinVolume *) + scj; if (!(hgi->g == hgj->g)) { continue; } @@ -76,17 +78,19 @@ ccl_device_inline void shader_merge_volume_closures(ShaderData *sd) } } -ccl_device_inline void shader_copy_volume_phases(ShaderVolumePhases *ccl_restrict phases, - const ShaderData *ccl_restrict sd) +ccl_device_inline void shader_copy_volume_phases(ccl_private ShaderVolumePhases *ccl_restrict + phases, + ccl_private const ShaderData *ccl_restrict sd) { phases->num_closure = 0; for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *from_sc = &sd->closure[i]; - const HenyeyGreensteinVolume *from_hg = (const HenyeyGreensteinVolume *)from_sc; + ccl_private const ShaderClosure *from_sc = &sd->closure[i]; + ccl_private const HenyeyGreensteinVolume *from_hg = + (ccl_private const HenyeyGreensteinVolume *)from_sc; if (from_sc->type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { - ShaderVolumeClosure *to_sc = &phases->closure[phases->num_closure]; + ccl_private ShaderVolumeClosure *to_sc = &phases->closure[phases->num_closure]; to_sc->weight = from_sc->weight; to_sc->sample_weight = from_sc->sample_weight; @@ -100,7 +104,8 @@ ccl_device_inline void shader_copy_volume_phases(ShaderVolumePhases *ccl_restric } #endif /* __VOLUME__ */ -ccl_device_inline void shader_prepare_surface_closures(INTEGRATOR_STATE_CONST_ARGS, ShaderData *sd) +ccl_device_inline void shader_prepare_surface_closures(INTEGRATOR_STATE_CONST_ARGS, + ccl_private ShaderData *sd) { /* Defensive sampling. * @@ -112,14 +117,14 @@ ccl_device_inline void shader_prepare_surface_closures(INTEGRATOR_STATE_CONST_AR float sum = 0.0f; for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + ccl_private ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { sum += sc->sample_weight; } } for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + ccl_private ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { sc->sample_weight = max(sc->sample_weight, 0.125f * sum); } @@ -137,7 +142,7 @@ ccl_device_inline void shader_prepare_surface_closures(INTEGRATOR_STATE_CONST_AR float blur_roughness = sqrtf(1.0f - blur_pdf) * 0.5f; for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + ccl_private ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF(sc->type)) { bsdf_blur(kg, sc, blur_roughness); } @@ -148,7 +153,8 @@ ccl_device_inline void shader_prepare_surface_closures(INTEGRATOR_STATE_CONST_AR /* BSDF */ -ccl_device_inline bool shader_bsdf_is_transmission(const ShaderData *sd, const float3 omega_in) +ccl_device_inline bool shader_bsdf_is_transmission(ccl_private const ShaderData *sd, + const float3 omega_in) { return dot(sd->N, omega_in) < 0.0f; } @@ -176,12 +182,12 @@ ccl_device_forceinline bool _shader_bsdf_exclude(ClosureType type, uint light_sh return false; } -ccl_device_inline float _shader_bsdf_multi_eval(const KernelGlobals *kg, - ShaderData *sd, +ccl_device_inline float _shader_bsdf_multi_eval(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, const float3 omega_in, const bool is_transmission, - const ShaderClosure *skip_sc, - BsdfEval *result_eval, + ccl_private const ShaderClosure *skip_sc, + ccl_private BsdfEval *result_eval, float sum_pdf, float sum_sample_weight, const uint light_shader_flags) @@ -189,7 +195,7 @@ ccl_device_inline float _shader_bsdf_multi_eval(const KernelGlobals *kg, /* This is the veach one-sample model with balance heuristic, * some PDF factors drop out when using balance heuristic weighting. */ for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (sc == skip_sc) { continue; @@ -220,11 +226,11 @@ ccl_device ccl_device_inline #endif float - shader_bsdf_eval(const KernelGlobals *kg, - ShaderData *sd, + shader_bsdf_eval(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, const float3 omega_in, const bool is_transmission, - BsdfEval *bsdf_eval, + ccl_private BsdfEval *bsdf_eval, const uint light_shader_flags) { bsdf_eval_init(bsdf_eval, false, zero_float3()); @@ -234,8 +240,8 @@ ccl_device_inline } /* Randomly sample a BSSRDF or BSDF proportional to ShaderClosure.sample_weight. */ -ccl_device_inline const ShaderClosure *shader_bsdf_bssrdf_pick(const ShaderData *ccl_restrict sd, - float *randu) +ccl_device_inline ccl_private const ShaderClosure *shader_bsdf_bssrdf_pick( + ccl_private const ShaderData *ccl_restrict sd, ccl_private float *randu) { int sampled = 0; @@ -244,7 +250,7 @@ ccl_device_inline const ShaderClosure *shader_bsdf_bssrdf_pick(const ShaderData float sum = 0.0f; for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { sum += sc->sample_weight; @@ -255,7 +261,7 @@ ccl_device_inline const ShaderClosure *shader_bsdf_bssrdf_pick(const ShaderData float partial_sum = 0.0f; for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { float next_sum = partial_sum + sc->sample_weight; @@ -277,15 +283,16 @@ ccl_device_inline const ShaderClosure *shader_bsdf_bssrdf_pick(const ShaderData } /* Return weight for picked BSSRDF. */ -ccl_device_inline float3 shader_bssrdf_sample_weight(const ShaderData *ccl_restrict sd, - const ShaderClosure *ccl_restrict bssrdf_sc) +ccl_device_inline float3 +shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict sd, + ccl_private const ShaderClosure *ccl_restrict bssrdf_sc) { float3 weight = bssrdf_sc->weight; if (sd->num_closure > 1) { float sum = 0.0f; for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { sum += sc->sample_weight; @@ -299,15 +306,15 @@ ccl_device_inline float3 shader_bssrdf_sample_weight(const ShaderData *ccl_restr /* Sample direction for picked BSDF, and return evaluation and pdf for all * BSDFs combined using MIS. */ -ccl_device int shader_bsdf_sample_closure(const KernelGlobals *kg, - ShaderData *sd, - const ShaderClosure *sc, +ccl_device int shader_bsdf_sample_closure(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private const ShaderClosure *sc, float randu, float randv, - BsdfEval *bsdf_eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) + ccl_private BsdfEval *bsdf_eval, + ccl_private float3 *omega_in, + ccl_private differential3 *domega_in, + ccl_private float *pdf) { /* BSSRDF should already have been handled elsewhere. */ kernel_assert(CLOSURE_IS_BSDF(sc->type)); @@ -333,13 +340,13 @@ ccl_device int shader_bsdf_sample_closure(const KernelGlobals *kg, return label; } -ccl_device float shader_bsdf_average_roughness(const ShaderData *sd) +ccl_device float shader_bsdf_average_roughness(ccl_private const ShaderData *sd) { float roughness = 0.0f; float sum_weight = 0.0f; for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF(sc->type)) { /* sqrt once to undo the squaring from multiplying roughness on the @@ -353,7 +360,8 @@ ccl_device float shader_bsdf_average_roughness(const ShaderData *sd) return (sum_weight > 0.0f) ? roughness / sum_weight : 0.0f; } -ccl_device float3 shader_bsdf_transparency(const KernelGlobals *kg, const ShaderData *sd) +ccl_device float3 shader_bsdf_transparency(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { if (sd->flag & SD_HAS_ONLY_VOLUME) { return one_float3(); @@ -366,11 +374,12 @@ ccl_device float3 shader_bsdf_transparency(const KernelGlobals *kg, const Shader } } -ccl_device void shader_bsdf_disable_transparency(const KernelGlobals *kg, ShaderData *sd) +ccl_device void shader_bsdf_disable_transparency(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd) { if (sd->flag & SD_TRANSPARENT) { for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + ccl_private ShaderClosure *sc = &sd->closure[i]; if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) { sc->sample_weight = 0.0f; @@ -382,7 +391,8 @@ ccl_device void shader_bsdf_disable_transparency(const KernelGlobals *kg, Shader } } -ccl_device float3 shader_bsdf_alpha(const KernelGlobals *kg, const ShaderData *sd) +ccl_device float3 shader_bsdf_alpha(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { float3 alpha = one_float3() - shader_bsdf_transparency(kg, sd); @@ -392,12 +402,13 @@ ccl_device float3 shader_bsdf_alpha(const KernelGlobals *kg, const ShaderData *s return alpha; } -ccl_device float3 shader_bsdf_diffuse(const KernelGlobals *kg, const ShaderData *sd) +ccl_device float3 shader_bsdf_diffuse(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { float3 eval = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type)) eval += sc->weight; @@ -406,12 +417,13 @@ ccl_device float3 shader_bsdf_diffuse(const KernelGlobals *kg, const ShaderData return eval; } -ccl_device float3 shader_bsdf_glossy(const KernelGlobals *kg, const ShaderData *sd) +ccl_device float3 shader_bsdf_glossy(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { float3 eval = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) eval += sc->weight; @@ -420,12 +432,13 @@ ccl_device float3 shader_bsdf_glossy(const KernelGlobals *kg, const ShaderData * return eval; } -ccl_device float3 shader_bsdf_transmission(const KernelGlobals *kg, const ShaderData *sd) +ccl_device float3 shader_bsdf_transmission(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { float3 eval = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type)) eval += sc->weight; @@ -434,12 +447,13 @@ ccl_device float3 shader_bsdf_transmission(const KernelGlobals *kg, const Shader return eval; } -ccl_device float3 shader_bsdf_average_normal(const KernelGlobals *kg, const ShaderData *sd) +ccl_device float3 shader_bsdf_average_normal(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { float3 N = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) N += sc->N * fabsf(average(sc->weight)); } @@ -447,14 +461,15 @@ ccl_device float3 shader_bsdf_average_normal(const KernelGlobals *kg, const Shad return (is_zero(N)) ? sd->N : normalize(N); } -ccl_device float3 shader_bsdf_ao_normal(const KernelGlobals *kg, const ShaderData *sd) +ccl_device float3 shader_bsdf_ao_normal(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd) { float3 N = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { - const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; N += bsdf->N * fabsf(average(sc->weight)); } } @@ -463,15 +478,15 @@ ccl_device float3 shader_bsdf_ao_normal(const KernelGlobals *kg, const ShaderDat } #ifdef __SUBSURFACE__ -ccl_device float3 shader_bssrdf_normal(const ShaderData *sd) +ccl_device float3 shader_bssrdf_normal(ccl_private const ShaderData *sd) { float3 N = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_BSSRDF(sc->type)) { - const Bssrdf *bssrdf = (const Bssrdf *)sc; + ccl_private const Bssrdf *bssrdf = (ccl_private const Bssrdf *)sc; float avg_weight = fabsf(average(sc->weight)); N += bssrdf->N * avg_weight; @@ -484,7 +499,9 @@ ccl_device float3 shader_bssrdf_normal(const ShaderData *sd) /* Constant emission optimization */ -ccl_device bool shader_constant_emission_eval(const KernelGlobals *kg, int shader, float3 *eval) +ccl_device bool shader_constant_emission_eval(ccl_global const KernelGlobals *kg, + int shader, + ccl_private float3 *eval) { int shader_index = shader & SHADER_MASK; int shader_flag = kernel_tex_fetch(__shaders, shader_index).flags; @@ -502,7 +519,7 @@ ccl_device bool shader_constant_emission_eval(const KernelGlobals *kg, int shade /* Background */ -ccl_device float3 shader_background_eval(const ShaderData *sd) +ccl_device float3 shader_background_eval(ccl_private const ShaderData *sd) { if (sd->flag & SD_EMISSION) { return sd->closure_emission_background; @@ -514,7 +531,7 @@ ccl_device float3 shader_background_eval(const ShaderData *sd) /* Emission */ -ccl_device float3 shader_emissive_eval(const ShaderData *sd) +ccl_device float3 shader_emissive_eval(ccl_private const ShaderData *sd) { if (sd->flag & SD_EMISSION) { return emissive_simple_eval(sd->Ng, sd->I) * sd->closure_emission_background; @@ -526,7 +543,8 @@ ccl_device float3 shader_emissive_eval(const ShaderData *sd) /* Holdout */ -ccl_device float3 shader_holdout_apply(const KernelGlobals *kg, ShaderData *sd) +ccl_device float3 shader_holdout_apply(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd) { float3 weight = zero_float3(); @@ -537,7 +555,7 @@ ccl_device float3 shader_holdout_apply(const KernelGlobals *kg, ShaderData *sd) weight = one_float3() - sd->closure_transparent_extinction; for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + ccl_private ShaderClosure *sc = &sd->closure[i]; if (!CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { sc->type = NBUILTIN_CLOSURES; } @@ -551,7 +569,7 @@ ccl_device float3 shader_holdout_apply(const KernelGlobals *kg, ShaderData *sd) } else { for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; + ccl_private const ShaderClosure *sc = &sd->closure[i]; if (CLOSURE_IS_HOLDOUT(sc->type)) { weight += sc->weight; } @@ -565,7 +583,7 @@ ccl_device float3 shader_holdout_apply(const KernelGlobals *kg, ShaderData *sd) template ccl_device void shader_eval_surface(INTEGRATOR_STATE_CONST_ARGS, - ShaderData *ccl_restrict sd, + ccl_private ShaderData *ccl_restrict sd, ccl_global float *ccl_restrict buffer, int path_flag) { @@ -604,7 +622,7 @@ ccl_device void shader_eval_surface(INTEGRATOR_STATE_CONST_ARGS, sd->flag |= SD_EMISSION; } else { - DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc( + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( sd, sizeof(DiffuseBsdf), make_float3(0.8f, 0.8f, 0.8f)); if (bsdf != NULL) { bsdf->N = sd->N; @@ -626,19 +644,20 @@ ccl_device void shader_eval_surface(INTEGRATOR_STATE_CONST_ARGS, #ifdef __VOLUME__ -ccl_device_inline float _shader_volume_phase_multi_eval(const ShaderData *sd, - const ShaderVolumePhases *phases, - const float3 omega_in, - int skip_phase, - BsdfEval *result_eval, - float sum_pdf, - float sum_sample_weight) +ccl_device_inline float _shader_volume_phase_multi_eval( + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumePhases *phases, + const float3 omega_in, + int skip_phase, + ccl_private BsdfEval *result_eval, + float sum_pdf, + float sum_sample_weight) { for (int i = 0; i < phases->num_closure; i++) { if (i == skip_phase) continue; - const ShaderVolumeClosure *svc = &phases->closure[i]; + ccl_private const ShaderVolumeClosure *svc = &phases->closure[i]; float phase_pdf = 0.0f; float3 eval = volume_phase_eval(sd, svc, omega_in, &phase_pdf); @@ -653,26 +672,26 @@ ccl_device_inline float _shader_volume_phase_multi_eval(const ShaderData *sd, return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; } -ccl_device float shader_volume_phase_eval(const KernelGlobals *kg, - const ShaderData *sd, - const ShaderVolumePhases *phases, +ccl_device float shader_volume_phase_eval(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumePhases *phases, const float3 omega_in, - BsdfEval *phase_eval) + ccl_private BsdfEval *phase_eval) { bsdf_eval_init(phase_eval, false, zero_float3()); return _shader_volume_phase_multi_eval(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f); } -ccl_device int shader_volume_phase_sample(const KernelGlobals *kg, - const ShaderData *sd, - const ShaderVolumePhases *phases, +ccl_device int shader_volume_phase_sample(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumePhases *phases, float randu, float randv, - BsdfEval *phase_eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) + ccl_private BsdfEval *phase_eval, + ccl_private float3 *omega_in, + ccl_private differential3 *domega_in, + ccl_private float *pdf) { int sampled = 0; @@ -681,7 +700,7 @@ ccl_device int shader_volume_phase_sample(const KernelGlobals *kg, float sum = 0.0f; for (sampled = 0; sampled < phases->num_closure; sampled++) { - const ShaderVolumeClosure *svc = &phases->closure[sampled]; + ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; sum += svc->sample_weight; } @@ -689,7 +708,7 @@ ccl_device int shader_volume_phase_sample(const KernelGlobals *kg, float partial_sum = 0.0f; for (sampled = 0; sampled < phases->num_closure; sampled++) { - const ShaderVolumeClosure *svc = &phases->closure[sampled]; + ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; float next_sum = partial_sum + svc->sample_weight; if (r <= next_sum) { @@ -709,7 +728,7 @@ ccl_device int shader_volume_phase_sample(const KernelGlobals *kg, /* todo: this isn't quite correct, we don't weight anisotropy properly * depending on color channels, even if this is perhaps not a common case */ - const ShaderVolumeClosure *svc = &phases->closure[sampled]; + ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; int label; float3 eval = zero_float3(); @@ -723,15 +742,15 @@ ccl_device int shader_volume_phase_sample(const KernelGlobals *kg, return label; } -ccl_device int shader_phase_sample_closure(const KernelGlobals *kg, - const ShaderData *sd, - const ShaderVolumeClosure *sc, +ccl_device int shader_phase_sample_closure(ccl_global const KernelGlobals *kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumeClosure *sc, float randu, float randv, - BsdfEval *phase_eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) + ccl_private BsdfEval *phase_eval, + ccl_private float3 *omega_in, + ccl_private differential3 *domega_in, + ccl_private float *pdf) { int label; float3 eval = zero_float3(); @@ -749,7 +768,7 @@ ccl_device int shader_phase_sample_closure(const KernelGlobals *kg, template ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS, - ShaderData *ccl_restrict sd, + ccl_private ShaderData *ccl_restrict sd, const int path_flag, StackReadOp stack_read) { @@ -824,7 +843,7 @@ ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS, /* Displacement Evaluation */ -ccl_device void shader_eval_displacement(INTEGRATOR_STATE_CONST_ARGS, ShaderData *sd) +ccl_device void shader_eval_displacement(INTEGRATOR_STATE_CONST_ARGS, ccl_private ShaderData *sd) { sd->num_closure = 0; sd->num_closure_left = 0; @@ -846,13 +865,14 @@ ccl_device void shader_eval_displacement(INTEGRATOR_STATE_CONST_ARGS, ShaderData /* Transparent Shadows */ #ifdef __TRANSPARENT_SHADOWS__ -ccl_device bool shader_transparent_shadow(const KernelGlobals *kg, Intersection *isect) +ccl_device bool shader_transparent_shadow(ccl_global const KernelGlobals *kg, + ccl_private Intersection *isect) { return (intersection_get_shader_flags(kg, isect) & SD_HAS_TRANSPARENT_SHADOW) != 0; } #endif /* __TRANSPARENT_SHADOWS__ */ -ccl_device float shader_cryptomatte_id(const KernelGlobals *kg, int shader) +ccl_device float shader_cryptomatte_id(ccl_global const KernelGlobals *kg, int shader) { return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).cryptomatte_id; } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 00457695e53..3a5a11d2c10 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -636,7 +636,7 @@ typedef struct AttributeDescriptor { float sample_weight; \ float3 N -typedef ccl_addr_space struct ccl_align(16) ShaderClosure +typedef struct ccl_align(16) ShaderClosure { SHADER_CLOSURE_BASE; @@ -747,7 +747,7 @@ enum ShaderDataObjectFlag { SD_OBJECT_HAS_VOLUME_ATTRIBUTES) }; -typedef ccl_addr_space struct ccl_align(16) ShaderData +typedef struct ccl_align(16) ShaderData { /* position */ float3 P; @@ -837,27 +837,28 @@ ShaderData; /* ShaderDataTinyStorage needs the same alignment as ShaderData, or else * the pointer cast in AS_SHADER_DATA invokes undefined behavior. */ -typedef ccl_addr_space struct ccl_align(16) ShaderDataTinyStorage +typedef struct ccl_align(16) ShaderDataTinyStorage { char pad[sizeof(ShaderData) - sizeof(ShaderClosure) * MAX_CLOSURE]; } ShaderDataTinyStorage; -#define AS_SHADER_DATA(shader_data_tiny_storage) ((ShaderData *)shader_data_tiny_storage) +#define AS_SHADER_DATA(shader_data_tiny_storage) \ + ((ccl_private ShaderData *)shader_data_tiny_storage) /* Compact volume closures storage. * * Used for decoupled direct/indirect light closure storage. */ -ccl_addr_space struct ShaderVolumeClosure { +typedef struct ShaderVolumeClosure { float3 weight; float sample_weight; float g; -}; +} ShaderVolumeClosure; -ccl_addr_space struct ShaderVolumePhases { +typedef struct ShaderVolumePhases { ShaderVolumeClosure closure[MAX_VOLUME_CLOSURE]; int num_closure; -}; +} ShaderVolumePhases; /* Volume Stack */ diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index ad609b15f86..871e370123e 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -44,56 +44,56 @@ CCL_NAMESPACE_BEGIN /* Stack */ -ccl_device_inline float3 stack_load_float3(float *stack, uint a) +ccl_device_inline float3 stack_load_float3(ccl_private float *stack, uint a) { kernel_assert(a + 2 < SVM_STACK_SIZE); - float *stack_a = stack + a; + ccl_private float *stack_a = stack + a; return make_float3(stack_a[0], stack_a[1], stack_a[2]); } -ccl_device_inline void stack_store_float3(float *stack, uint a, float3 f) +ccl_device_inline void stack_store_float3(ccl_private float *stack, uint a, float3 f) { kernel_assert(a + 2 < SVM_STACK_SIZE); - float *stack_a = stack + a; + ccl_private float *stack_a = stack + a; stack_a[0] = f.x; stack_a[1] = f.y; stack_a[2] = f.z; } -ccl_device_inline float stack_load_float(float *stack, uint a) +ccl_device_inline float stack_load_float(ccl_private float *stack, uint a) { kernel_assert(a < SVM_STACK_SIZE); return stack[a]; } -ccl_device_inline float stack_load_float_default(float *stack, uint a, uint value) +ccl_device_inline float stack_load_float_default(ccl_private float *stack, uint a, uint value) { return (a == (uint)SVM_STACK_INVALID) ? __uint_as_float(value) : stack_load_float(stack, a); } -ccl_device_inline void stack_store_float(float *stack, uint a, float f) +ccl_device_inline void stack_store_float(ccl_private float *stack, uint a, float f) { kernel_assert(a < SVM_STACK_SIZE); stack[a] = f; } -ccl_device_inline int stack_load_int(float *stack, uint a) +ccl_device_inline int stack_load_int(ccl_private float *stack, uint a) { kernel_assert(a < SVM_STACK_SIZE); return __float_as_int(stack[a]); } -ccl_device_inline int stack_load_int_default(float *stack, uint a, uint value) +ccl_device_inline int stack_load_int_default(ccl_private float *stack, uint a, uint value) { return (a == (uint)SVM_STACK_INVALID) ? (int)value : stack_load_int(stack, a); } -ccl_device_inline void stack_store_int(float *stack, uint a, int i) +ccl_device_inline void stack_store_int(ccl_private float *stack, uint a, int i) { kernel_assert(a < SVM_STACK_SIZE); @@ -107,14 +107,15 @@ ccl_device_inline bool stack_valid(uint a) /* Reading Nodes */ -ccl_device_inline uint4 read_node(const KernelGlobals *kg, int *offset) +ccl_device_inline uint4 read_node(ccl_global const KernelGlobals *kg, ccl_private int *offset) { uint4 node = kernel_tex_fetch(__svm_nodes, *offset); (*offset)++; return node; } -ccl_device_inline float4 read_node_float(const KernelGlobals *kg, int *offset) +ccl_device_inline float4 read_node_float(ccl_global const KernelGlobals *kg, + ccl_private int *offset) { uint4 node = kernel_tex_fetch(__svm_nodes, *offset); float4 f = make_float4(__uint_as_float(node.x), @@ -125,7 +126,7 @@ ccl_device_inline float4 read_node_float(const KernelGlobals *kg, int *offset) return f; } -ccl_device_inline float4 fetch_node_float(const KernelGlobals *kg, int offset) +ccl_device_inline float4 fetch_node_float(ccl_global const KernelGlobals *kg, int offset) { uint4 node = kernel_tex_fetch(__svm_nodes, offset); return make_float4(__uint_as_float(node.x), @@ -134,20 +135,26 @@ ccl_device_inline float4 fetch_node_float(const KernelGlobals *kg, int offset) __uint_as_float(node.w)); } -ccl_device_forceinline void svm_unpack_node_uchar2(uint i, uint *x, uint *y) +ccl_device_forceinline void svm_unpack_node_uchar2(uint i, + ccl_private uint *x, + ccl_private uint *y) { *x = (i & 0xFF); *y = ((i >> 8) & 0xFF); } -ccl_device_forceinline void svm_unpack_node_uchar3(uint i, uint *x, uint *y, uint *z) +ccl_device_forceinline void svm_unpack_node_uchar3(uint i, + ccl_private uint *x, + ccl_private uint *y, + ccl_private uint *z) { *x = (i & 0xFF); *y = ((i >> 8) & 0xFF); *z = ((i >> 16) & 0xFF); } -ccl_device_forceinline void svm_unpack_node_uchar4(uint i, uint *x, uint *y, uint *z, uint *w) +ccl_device_forceinline void svm_unpack_node_uchar4( + uint i, ccl_private uint *x, ccl_private uint *y, ccl_private uint *z, ccl_private uint *w) { *x = (i & 0xFF); *y = ((i >> 8) & 0xFF); diff --git a/intern/cycles/kernel/svm/svm_ao.h b/intern/cycles/kernel/svm/svm_ao.h index 34ac2cb8fbf..092f3817fd8 100644 --- a/intern/cycles/kernel/svm/svm_ao.h +++ b/intern/cycles/kernel/svm/svm_ao.h @@ -25,7 +25,7 @@ extern "C" __device__ float __direct_callable__svm_node_ao(INTEGRATOR_STATE_CONS # else ccl_device float svm_ao(INTEGRATOR_STATE_CONST_ARGS, # endif - ShaderData *sd, + ccl_private ShaderData *sd, float3 N, float max_dist, int num_samples, @@ -96,7 +96,10 @@ ccl_device_inline ccl_device_noinline # endif void - svm_node_ao(INTEGRATOR_STATE_CONST_ARGS, ShaderData *sd, float *stack, uint4 node) + svm_node_ao(INTEGRATOR_STATE_CONST_ARGS, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint flags, dist_offset, normal_offset, out_ao_offset; svm_unpack_node_uchar4(node.y, &flags, &dist_offset, &normal_offset, &out_ao_offset); diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h index 26dec9717b3..640bec87ac9 100644 --- a/intern/cycles/kernel/svm/svm_aov.h +++ b/intern/cycles/kernel/svm/svm_aov.h @@ -26,8 +26,8 @@ ccl_device_inline bool svm_node_aov_check(const int path_flag, ccl_global float } ccl_device void svm_node_aov_color(INTEGRATOR_STATE_CONST_ARGS, - ShaderData *sd, - float *stack, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node, ccl_global float *render_buffer) { @@ -44,8 +44,8 @@ ccl_device void svm_node_aov_color(INTEGRATOR_STATE_CONST_ARGS, } ccl_device void svm_node_aov_value(INTEGRATOR_STATE_CONST_ARGS, - ShaderData *sd, - float *stack, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node, ccl_global float *render_buffer) { diff --git a/intern/cycles/kernel/svm/svm_attribute.h b/intern/cycles/kernel/svm/svm_attribute.h index 5f94b20af73..9fd401ba1c3 100644 --- a/intern/cycles/kernel/svm/svm_attribute.h +++ b/intern/cycles/kernel/svm/svm_attribute.h @@ -18,11 +18,11 @@ CCL_NAMESPACE_BEGIN /* Attribute Node */ -ccl_device AttributeDescriptor svm_node_attr_init(const KernelGlobals *kg, - ShaderData *sd, +ccl_device AttributeDescriptor svm_node_attr_init(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, uint4 node, - NodeAttributeOutputType *type, - uint *out_offset) + ccl_private NodeAttributeOutputType *type, + ccl_private uint *out_offset) { *out_offset = node.z; *type = (NodeAttributeOutputType)node.w; @@ -48,9 +48,9 @@ ccl_device AttributeDescriptor svm_node_attr_init(const KernelGlobals *kg, } template -ccl_device_noinline void svm_node_attr(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_attr(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT; @@ -148,9 +148,9 @@ ccl_device_noinline void svm_node_attr(const KernelGlobals *kg, } } -ccl_device_noinline void svm_node_attr_bump_dx(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_attr_bump_dx(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT; @@ -244,9 +244,9 @@ ccl_device_noinline void svm_node_attr_bump_dx(const KernelGlobals *kg, } } -ccl_device_noinline void svm_node_attr_bump_dy(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_attr_bump_dy(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT; diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/svm_bevel.h index 60302b8e3d7..a76584e6bc8 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/svm_bevel.h @@ -77,7 +77,10 @@ ccl_device_forceinline float svm_bevel_cubic_quintic_root_find(float xi) return x; } -ccl_device void svm_bevel_cubic_sample(const float radius, float xi, float *r, float *h) +ccl_device void svm_bevel_cubic_sample(const float radius, + float xi, + ccl_private float *r, + ccl_private float *h) { float Rm = radius; float r_ = svm_bevel_cubic_quintic_root_find(xi); @@ -100,7 +103,7 @@ extern "C" __device__ float3 __direct_callable__svm_node_bevel(INTEGRATOR_STATE_ # else ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, # endif - ShaderData *sd, + ccl_private ShaderData *sd, float radius, int num_samples) { @@ -284,7 +287,10 @@ ccl_device_inline ccl_device_noinline # endif void - svm_node_bevel(INTEGRATOR_STATE_CONST_ARGS, ShaderData *sd, float *stack, uint4 node) + svm_node_bevel(INTEGRATOR_STATE_CONST_ARGS, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint num_samples, radius_offset, normal_offset, out_offset; svm_unpack_node_uchar4(node.y, &num_samples, &radius_offset, &normal_offset, &out_offset); diff --git a/intern/cycles/kernel/svm/svm_blackbody.h b/intern/cycles/kernel/svm/svm_blackbody.h index 96b3703b954..521afb42adc 100644 --- a/intern/cycles/kernel/svm/svm_blackbody.h +++ b/intern/cycles/kernel/svm/svm_blackbody.h @@ -34,9 +34,9 @@ CCL_NAMESPACE_BEGIN /* Blackbody Node */ -ccl_device_noinline void svm_node_blackbody(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_blackbody(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint temperature_offset, uint col_offset) { diff --git a/intern/cycles/kernel/svm/svm_brick.h b/intern/cycles/kernel/svm/svm_brick.h index dca1b220dd5..29a8350f1c1 100644 --- a/intern/cycles/kernel/svm/svm_brick.h +++ b/intern/cycles/kernel/svm/svm_brick.h @@ -72,8 +72,11 @@ ccl_device_noinline_cpu float2 svm_brick(float3 p, return make_float2(tint, mortar); } -ccl_device_noinline int svm_node_tex_brick( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_tex_brick(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int offset) { uint4 node2 = read_node(kg, &offset); uint4 node3 = read_node(kg, &offset); diff --git a/intern/cycles/kernel/svm/svm_brightness.h b/intern/cycles/kernel/svm/svm_brightness.h index 2ed812acd71..0a44ffe6359 100644 --- a/intern/cycles/kernel/svm/svm_brightness.h +++ b/intern/cycles/kernel/svm/svm_brightness.h @@ -17,7 +17,7 @@ CCL_NAMESPACE_BEGIN ccl_device_noinline void svm_node_brightness( - ShaderData *sd, float *stack, uint in_color, uint out_color, uint node) + ccl_private ShaderData *sd, ccl_private float *stack, uint in_color, uint out_color, uint node) { uint bright_offset, contrast_offset; float3 color = stack_load_float3(stack, in_color); diff --git a/intern/cycles/kernel/svm/svm_bump.h b/intern/cycles/kernel/svm/svm_bump.h index 8672839dbab..70935c730f4 100644 --- a/intern/cycles/kernel/svm/svm_bump.h +++ b/intern/cycles/kernel/svm/svm_bump.h @@ -18,9 +18,9 @@ CCL_NAMESPACE_BEGIN /* Bump Eval Nodes */ -ccl_device_noinline void svm_node_enter_bump_eval(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_enter_bump_eval(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint offset) { /* save state */ @@ -45,9 +45,9 @@ ccl_device_noinline void svm_node_enter_bump_eval(const KernelGlobals *kg, } } -ccl_device_noinline void svm_node_leave_bump_eval(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_leave_bump_eval(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint offset) { /* restore state */ diff --git a/intern/cycles/kernel/svm/svm_camera.h b/intern/cycles/kernel/svm/svm_camera.h index 40c0edcdad0..2b786757af8 100644 --- a/intern/cycles/kernel/svm/svm_camera.h +++ b/intern/cycles/kernel/svm/svm_camera.h @@ -16,9 +16,9 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_camera(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_camera(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint out_vector, uint out_zdepth, uint out_distance) diff --git a/intern/cycles/kernel/svm/svm_checker.h b/intern/cycles/kernel/svm/svm_checker.h index a9919c9ddc9..e22367f4f59 100644 --- a/intern/cycles/kernel/svm/svm_checker.h +++ b/intern/cycles/kernel/svm/svm_checker.h @@ -32,9 +32,9 @@ ccl_device float svm_checker(float3 p) return ((xi % 2 == yi % 2) == (zi % 2)) ? 1.0f : 0.0f; } -ccl_device_noinline void svm_node_tex_checker(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_tex_checker(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { uint co_offset, color1_offset, color2_offset, scale_offset; diff --git a/intern/cycles/kernel/svm/svm_clamp.h b/intern/cycles/kernel/svm/svm_clamp.h index 656bd31c085..cb5224aebb2 100644 --- a/intern/cycles/kernel/svm/svm_clamp.h +++ b/intern/cycles/kernel/svm/svm_clamp.h @@ -18,9 +18,9 @@ CCL_NAMESPACE_BEGIN /* Clamp Node */ -ccl_device_noinline int svm_node_clamp(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_clamp(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint value_stack_offset, uint parameters_stack_offsets, uint result_stack_offset, diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h index e55f76a4400..87be73bb2cc 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/svm_closure.h @@ -18,8 +18,12 @@ CCL_NAMESPACE_BEGIN /* Closure Nodes */ -ccl_device void svm_node_glass_setup( - ShaderData *sd, MicrofacetBsdf *bsdf, int type, float eta, float roughness, bool refract) +ccl_device void svm_node_glass_setup(ccl_private ShaderData *sd, + ccl_private MicrofacetBsdf *bsdf, + int type, + float eta, + float roughness, + bool refract) { if (type == CLOSURE_BSDF_SHARP_GLASS_ID) { if (refract) { @@ -58,8 +62,12 @@ ccl_device void svm_node_glass_setup( } template -ccl_device_noinline int svm_node_closure_bsdf( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int path_flag, int offset) +ccl_device_noinline int svm_node_closure_bsdf(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int path_flag, + int offset) { uint type, param1_offset, param2_offset; @@ -213,8 +221,8 @@ ccl_device_noinline int svm_node_closure_bsdf( if (subsurface <= CLOSURE_WEIGHT_CUTOFF && diffuse_weight > CLOSURE_WEIGHT_CUTOFF) { float3 diff_weight = weight * base_color * diffuse_weight; - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( - sd, sizeof(PrincipledDiffuseBsdf), diff_weight); + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *) + bsdf_alloc(sd, sizeof(PrincipledDiffuseBsdf), diff_weight); if (bsdf) { bsdf->N = N; @@ -225,7 +233,7 @@ ccl_device_noinline int svm_node_closure_bsdf( } } else if (subsurface > CLOSURE_WEIGHT_CUTOFF) { - Bssrdf *bssrdf = bssrdf_alloc(sd, subsurf_weight); + ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, subsurf_weight); if (bssrdf) { bssrdf->radius = subsurface_radius * subsurface; @@ -247,7 +255,7 @@ ccl_device_noinline int svm_node_closure_bsdf( if (diffuse_weight > CLOSURE_WEIGHT_CUTOFF) { float3 diff_weight = weight * base_color * diffuse_weight; - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), diff_weight); if (bsdf) { @@ -273,7 +281,7 @@ ccl_device_noinline int svm_node_closure_bsdf( float3 sheen_weight = weight * sheen * sheen_color * diffuse_weight; - PrincipledSheenBsdf *bsdf = (PrincipledSheenBsdf *)bsdf_alloc( + ccl_private PrincipledSheenBsdf *bsdf = (ccl_private PrincipledSheenBsdf *)bsdf_alloc( sd, sizeof(PrincipledSheenBsdf), sheen_weight); if (bsdf) { @@ -292,11 +300,12 @@ ccl_device_noinline int svm_node_closure_bsdf( (specular > CLOSURE_WEIGHT_CUTOFF || metallic > CLOSURE_WEIGHT_CUTOFF)) { float3 spec_weight = weight * specular_weight; - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), spec_weight); - MicrofacetExtra *extra = (bsdf != NULL) ? (MicrofacetExtra *)closure_alloc_extra( - sd, sizeof(MicrofacetExtra)) : - NULL; + ccl_private MicrofacetExtra *extra = + (bsdf != NULL) ? + (ccl_private MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)) : + NULL; if (bsdf && extra) { bsdf->N = N; @@ -355,11 +364,12 @@ ccl_device_noinline int svm_node_closure_bsdf( if (kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) # endif { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), glass_weight * fresnel); - MicrofacetExtra *extra = (bsdf != NULL) ? (MicrofacetExtra *)closure_alloc_extra( - sd, sizeof(MicrofacetExtra)) : - NULL; + ccl_private MicrofacetExtra *extra = + (bsdf != NULL) ? (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)) : + NULL; if (bsdf && extra) { bsdf->N = N; @@ -384,7 +394,7 @@ ccl_device_noinline int svm_node_closure_bsdf( if (kernel_data.integrator.caustics_refractive || (path_flag & PATH_RAY_DIFFUSE) == 0) # endif { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), base_color * glass_weight * (1.0f - fresnel)); if (bsdf) { bsdf->N = N; @@ -407,11 +417,12 @@ ccl_device_noinline int svm_node_closure_bsdf( } } else { /* use multi-scatter GGX */ - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), glass_weight); - MicrofacetExtra *extra = (bsdf != NULL) ? (MicrofacetExtra *)closure_alloc_extra( - sd, sizeof(MicrofacetExtra)) : - NULL; + ccl_private MicrofacetExtra *extra = + (bsdf != NULL) ? (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)) : + NULL; if (bsdf && extra) { bsdf->N = N; @@ -440,10 +451,12 @@ ccl_device_noinline int svm_node_closure_bsdf( if (kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) { # endif if (clearcoat > CLOSURE_WEIGHT_CUTOFF) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight); - MicrofacetExtra *extra = (bsdf != NULL) ? (MicrofacetExtra *)closure_alloc_extra( - sd, sizeof(MicrofacetExtra)) : - NULL; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), weight); + ccl_private MicrofacetExtra *extra = + (bsdf != NULL) ? + (ccl_private MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)) : + NULL; if (bsdf && extra) { bsdf->N = clearcoat_normal; @@ -471,7 +484,8 @@ ccl_device_noinline int svm_node_closure_bsdf( #endif /* __PRINCIPLED__ */ case CLOSURE_BSDF_DIFFUSE_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - OrenNayarBsdf *bsdf = (OrenNayarBsdf *)bsdf_alloc(sd, sizeof(OrenNayarBsdf), weight); + ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)bsdf_alloc( + sd, sizeof(OrenNayarBsdf), weight); if (bsdf) { bsdf->N = N; @@ -479,7 +493,7 @@ ccl_device_noinline int svm_node_closure_bsdf( float roughness = param1; if (roughness == 0.0f) { - sd->flag |= bsdf_diffuse_setup((DiffuseBsdf *)bsdf); + sd->flag |= bsdf_diffuse_setup((ccl_private DiffuseBsdf *)bsdf); } else { bsdf->roughness = roughness; @@ -490,7 +504,8 @@ ccl_device_noinline int svm_node_closure_bsdf( } case CLOSURE_BSDF_TRANSLUCENT_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight); + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( + sd, sizeof(DiffuseBsdf), weight); if (bsdf) { bsdf->N = N; @@ -513,7 +528,8 @@ ccl_device_noinline int svm_node_closure_bsdf( break; #endif float3 weight = sd->svm_closure_weight * mix_weight; - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight); + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), weight); if (!bsdf) { break; @@ -559,7 +575,8 @@ ccl_device_noinline int svm_node_closure_bsdf( sd->flag |= bsdf_microfacet_ggx_setup(bsdf); else if (type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID) { kernel_assert(stack_valid(data_node.w)); - bsdf->extra = (MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + bsdf->extra = (ccl_private MicrofacetExtra *)closure_alloc_extra(sd, + sizeof(MicrofacetExtra)); if (bsdf->extra) { bsdf->extra->color = stack_load_float3(stack, data_node.w); bsdf->extra->cspec0 = make_float3(0.0f, 0.0f, 0.0f); @@ -581,7 +598,8 @@ ccl_device_noinline int svm_node_closure_bsdf( break; #endif float3 weight = sd->svm_closure_weight * mix_weight; - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight); + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), weight); if (bsdf) { bsdf->N = N; @@ -639,7 +657,7 @@ ccl_device_noinline int svm_node_closure_bsdf( if (kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) #endif { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), weight * fresnel); if (bsdf) { @@ -655,7 +673,7 @@ ccl_device_noinline int svm_node_closure_bsdf( if (kernel_data.integrator.caustics_refractive || (path_flag & PATH_RAY_DIFFUSE) == 0) #endif { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), weight * (1.0f - fresnel)); if (bsdf) { @@ -675,12 +693,14 @@ ccl_device_noinline int svm_node_closure_bsdf( break; #endif float3 weight = sd->svm_closure_weight * mix_weight; - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight); + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), weight); if (!bsdf) { break; } - MicrofacetExtra *extra = (MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + ccl_private MicrofacetExtra *extra = (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)); if (!extra) { break; } @@ -706,7 +726,8 @@ ccl_device_noinline int svm_node_closure_bsdf( } case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - VelvetBsdf *bsdf = (VelvetBsdf *)bsdf_alloc(sd, sizeof(VelvetBsdf), weight); + ccl_private VelvetBsdf *bsdf = (ccl_private VelvetBsdf *)bsdf_alloc( + sd, sizeof(VelvetBsdf), weight); if (bsdf) { bsdf->N = N; @@ -724,7 +745,8 @@ ccl_device_noinline int svm_node_closure_bsdf( #endif case CLOSURE_BSDF_DIFFUSE_TOON_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - ToonBsdf *bsdf = (ToonBsdf *)bsdf_alloc(sd, sizeof(ToonBsdf), weight); + ccl_private ToonBsdf *bsdf = (ccl_private ToonBsdf *)bsdf_alloc( + sd, sizeof(ToonBsdf), weight); if (bsdf) { bsdf->N = N; @@ -771,11 +793,11 @@ ccl_device_noinline int svm_node_closure_bsdf( random = stack_load_float_default(stack, random_ofs, data_node3.y); } - PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)bsdf_alloc( + ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)bsdf_alloc( sd, sizeof(PrincipledHairBSDF), weight); if (bsdf) { - PrincipledHairExtra *extra = (PrincipledHairExtra *)closure_alloc_extra( - sd, sizeof(PrincipledHairExtra)); + ccl_private PrincipledHairExtra *extra = (ccl_private PrincipledHairExtra *) + closure_alloc_extra(sd, sizeof(PrincipledHairExtra)); if (!extra) break; @@ -854,7 +876,8 @@ ccl_device_noinline int svm_node_closure_bsdf( case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - HairBsdf *bsdf = (HairBsdf *)bsdf_alloc(sd, sizeof(HairBsdf), weight); + ccl_private HairBsdf *bsdf = (ccl_private HairBsdf *)bsdf_alloc( + sd, sizeof(HairBsdf), weight); if (bsdf) { bsdf->N = N; @@ -889,7 +912,7 @@ ccl_device_noinline int svm_node_closure_bsdf( case CLOSURE_BSSRDF_RANDOM_WALK_ID: case CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - Bssrdf *bssrdf = bssrdf_alloc(sd, weight); + ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, weight); if (bssrdf) { /* disable in case of diffuse ancestor, can't see it well then and @@ -921,9 +944,9 @@ ccl_device_noinline int svm_node_closure_bsdf( } template -ccl_device_noinline void svm_node_closure_volume(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_closure_volume(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { #ifdef __VOLUME__ @@ -958,7 +981,7 @@ ccl_device_noinline void svm_node_closure_volume(const KernelGlobals *kg, /* Add closure for volume scattering. */ if (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { - HenyeyGreensteinVolume *volume = (HenyeyGreensteinVolume *)bsdf_alloc( + ccl_private HenyeyGreensteinVolume *volume = (ccl_private HenyeyGreensteinVolume *)bsdf_alloc( sd, sizeof(HenyeyGreensteinVolume), weight); if (volume) { @@ -976,8 +999,12 @@ ccl_device_noinline void svm_node_closure_volume(const KernelGlobals *kg, } template -ccl_device_noinline int svm_node_principled_volume( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int path_flag, int offset) +ccl_device_noinline int svm_node_principled_volume(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int path_flag, + int offset) { #ifdef __VOLUME__ uint4 value_node = read_node(kg, &offset); @@ -1023,7 +1050,7 @@ ccl_device_noinline int svm_node_principled_volume( } /* Add closure for volume scattering. */ - HenyeyGreensteinVolume *volume = (HenyeyGreensteinVolume *)bsdf_alloc( + ccl_private HenyeyGreensteinVolume *volume = (ccl_private HenyeyGreensteinVolume *)bsdf_alloc( sd, sizeof(HenyeyGreensteinVolume), color * density); if (volume) { float anisotropy = (stack_valid(anisotropy_offset)) ? @@ -1087,7 +1114,9 @@ ccl_device_noinline int svm_node_principled_volume( return offset; } -ccl_device_noinline void svm_node_closure_emission(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_closure_emission(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint mix_weight_offset = node.y; float3 weight = sd->svm_closure_weight; @@ -1104,7 +1133,9 @@ ccl_device_noinline void svm_node_closure_emission(ShaderData *sd, float *stack, emission_setup(sd, weight); } -ccl_device_noinline void svm_node_closure_background(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_closure_background(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint mix_weight_offset = node.y; float3 weight = sd->svm_closure_weight; @@ -1121,7 +1152,9 @@ ccl_device_noinline void svm_node_closure_background(ShaderData *sd, float *stac background_setup(sd, weight); } -ccl_device_noinline void svm_node_closure_holdout(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_closure_holdout(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint mix_weight_offset = node.y; @@ -1142,26 +1175,28 @@ ccl_device_noinline void svm_node_closure_holdout(ShaderData *sd, float *stack, /* Closure Nodes */ -ccl_device_inline void svm_node_closure_store_weight(ShaderData *sd, float3 weight) +ccl_device_inline void svm_node_closure_store_weight(ccl_private ShaderData *sd, float3 weight) { sd->svm_closure_weight = weight; } -ccl_device void svm_node_closure_set_weight(ShaderData *sd, uint r, uint g, uint b) +ccl_device void svm_node_closure_set_weight(ccl_private ShaderData *sd, uint r, uint g, uint b) { float3 weight = make_float3(__uint_as_float(r), __uint_as_float(g), __uint_as_float(b)); svm_node_closure_store_weight(sd, weight); } -ccl_device void svm_node_closure_weight(ShaderData *sd, float *stack, uint weight_offset) +ccl_device void svm_node_closure_weight(ccl_private ShaderData *sd, + ccl_private float *stack, + uint weight_offset) { float3 weight = stack_load_float3(stack, weight_offset); svm_node_closure_store_weight(sd, weight); } -ccl_device_noinline void svm_node_emission_weight(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_emission_weight(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { uint color_offset = node.y; @@ -1173,7 +1208,9 @@ ccl_device_noinline void svm_node_emission_weight(const KernelGlobals *kg, svm_node_closure_store_weight(sd, weight); } -ccl_device_noinline void svm_node_mix_closure(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_mix_closure(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { /* fetch weight from blend input, previous mix closures, * and write to stack to be used by closure nodes later */ @@ -1195,8 +1232,11 @@ ccl_device_noinline void svm_node_mix_closure(ShaderData *sd, float *stack, uint /* (Bump) normal */ -ccl_device void svm_node_set_normal( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint in_direction, uint out_normal) +ccl_device void svm_node_set_normal(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint in_direction, + uint out_normal) { float3 normal = stack_load_float3(stack, in_direction); sd->N = normal; diff --git a/intern/cycles/kernel/svm/svm_convert.h b/intern/cycles/kernel/svm/svm_convert.h index 37d40167ccc..0d53779a5c8 100644 --- a/intern/cycles/kernel/svm/svm_convert.h +++ b/intern/cycles/kernel/svm/svm_convert.h @@ -18,8 +18,12 @@ CCL_NAMESPACE_BEGIN /* Conversion Nodes */ -ccl_device_noinline void svm_node_convert( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint from, uint to) +ccl_device_noinline void svm_node_convert(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint from, + uint to) { switch (type) { case NODE_CONVERT_FI: { diff --git a/intern/cycles/kernel/svm/svm_displace.h b/intern/cycles/kernel/svm/svm_displace.h index a1d952173d8..7a3c8a6d36d 100644 --- a/intern/cycles/kernel/svm/svm_displace.h +++ b/intern/cycles/kernel/svm/svm_displace.h @@ -20,9 +20,9 @@ CCL_NAMESPACE_BEGIN /* Bump Node */ -ccl_device_noinline void svm_node_set_bump(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_set_bump(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { #ifdef __RAY_DIFFERENTIALS__ @@ -88,18 +88,18 @@ ccl_device_noinline void svm_node_set_bump(const KernelGlobals *kg, /* Displacement Node */ -ccl_device void svm_node_set_displacement(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device void svm_node_set_displacement(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint fac_offset) { float3 dP = stack_load_float3(stack, fac_offset); sd->P += dP; } -ccl_device_noinline void svm_node_displacement(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_displacement(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { uint height_offset, midlevel_offset, scale_offset, normal_offset; @@ -127,8 +127,11 @@ ccl_device_noinline void svm_node_displacement(const KernelGlobals *kg, stack_store_float3(stack, node.z, dP); } -ccl_device_noinline int svm_node_vector_displacement( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_vector_displacement(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int offset) { uint4 data_node = read_node(kg, &offset); uint space = data_node.x; diff --git a/intern/cycles/kernel/svm/svm_fresnel.h b/intern/cycles/kernel/svm/svm_fresnel.h index b5ecdbe2abf..449ec84370f 100644 --- a/intern/cycles/kernel/svm/svm_fresnel.h +++ b/intern/cycles/kernel/svm/svm_fresnel.h @@ -18,8 +18,11 @@ CCL_NAMESPACE_BEGIN /* Fresnel Node */ -ccl_device_noinline void svm_node_fresnel( - ShaderData *sd, float *stack, uint ior_offset, uint ior_value, uint node) +ccl_device_noinline void svm_node_fresnel(ccl_private ShaderData *sd, + ccl_private float *stack, + uint ior_offset, + uint ior_value, + uint node) { uint normal_offset, out_offset; svm_unpack_node_uchar2(node, &normal_offset, &out_offset); @@ -37,7 +40,9 @@ ccl_device_noinline void svm_node_fresnel( /* Layer Weight Node */ -ccl_device_noinline void svm_node_layer_weight(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_layer_weight(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint blend_offset = node.y; uint blend_value = node.z; diff --git a/intern/cycles/kernel/svm/svm_gamma.h b/intern/cycles/kernel/svm/svm_gamma.h index f6fafdee941..7ec6c31065d 100644 --- a/intern/cycles/kernel/svm/svm_gamma.h +++ b/intern/cycles/kernel/svm/svm_gamma.h @@ -16,8 +16,11 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_gamma( - ShaderData *sd, float *stack, uint in_gamma, uint in_color, uint out_color) +ccl_device_noinline void svm_node_gamma(ccl_private ShaderData *sd, + ccl_private float *stack, + uint in_gamma, + uint in_color, + uint out_color) { float3 color = stack_load_float3(stack, in_color); float gamma = stack_load_float(stack, in_gamma); diff --git a/intern/cycles/kernel/svm/svm_geometry.h b/intern/cycles/kernel/svm/svm_geometry.h index 432529eb061..a94464d3a52 100644 --- a/intern/cycles/kernel/svm/svm_geometry.h +++ b/intern/cycles/kernel/svm/svm_geometry.h @@ -18,8 +18,11 @@ CCL_NAMESPACE_BEGIN /* Geometry Node */ -ccl_device_noinline void svm_node_geometry( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_geometry(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { float3 data; @@ -51,8 +54,11 @@ ccl_device_noinline void svm_node_geometry( stack_store_float3(stack, out_offset, data); } -ccl_device_noinline void svm_node_geometry_bump_dx( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_geometry_bump_dx(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -75,8 +81,11 @@ ccl_device_noinline void svm_node_geometry_bump_dx( #endif } -ccl_device_noinline void svm_node_geometry_bump_dy( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_geometry_bump_dy(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -101,8 +110,11 @@ ccl_device_noinline void svm_node_geometry_bump_dy( /* Object Info */ -ccl_device_noinline void svm_node_object_info( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_object_info(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { float data; @@ -140,8 +152,11 @@ ccl_device_noinline void svm_node_object_info( /* Particle Info */ -ccl_device_noinline void svm_node_particle_info( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_particle_info(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { switch (type) { case NODE_INFO_PAR_INDEX: { @@ -199,8 +214,11 @@ ccl_device_noinline void svm_node_particle_info( /* Hair Info */ -ccl_device_noinline void svm_node_hair_info( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_hair_info(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { float data; float3 data3; diff --git a/intern/cycles/kernel/svm/svm_gradient.h b/intern/cycles/kernel/svm/svm_gradient.h index cd15f7097e7..8cc37be606f 100644 --- a/intern/cycles/kernel/svm/svm_gradient.h +++ b/intern/cycles/kernel/svm/svm_gradient.h @@ -60,7 +60,9 @@ ccl_device float svm_gradient(float3 p, NodeGradientType type) return 0.0f; } -ccl_device_noinline void svm_node_tex_gradient(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_tex_gradient(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint type, co_offset, color_offset, fac_offset; diff --git a/intern/cycles/kernel/svm/svm_hsv.h b/intern/cycles/kernel/svm/svm_hsv.h index 6f49a8385aa..feb85eda122 100644 --- a/intern/cycles/kernel/svm/svm_hsv.h +++ b/intern/cycles/kernel/svm/svm_hsv.h @@ -19,9 +19,9 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_hsv(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_hsv(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { uint in_color_offset, fac_offset, out_color_offset; diff --git a/intern/cycles/kernel/svm/svm_ies.h b/intern/cycles/kernel/svm/svm_ies.h index 9c13734ecf0..7d41205c9ef 100644 --- a/intern/cycles/kernel/svm/svm_ies.h +++ b/intern/cycles/kernel/svm/svm_ies.h @@ -19,7 +19,7 @@ CCL_NAMESPACE_BEGIN /* IES Light */ ccl_device_inline float interpolate_ies_vertical( - const KernelGlobals *kg, int ofs, int v, int v_num, float v_frac, int h) + ccl_global const KernelGlobals *kg, int ofs, int v, int v_num, float v_frac, int h) { /* Since lookups are performed in spherical coordinates, clamping the coordinates at the low end * of v (corresponding to the north pole) would result in artifacts. The proper way of dealing @@ -39,7 +39,7 @@ ccl_device_inline float interpolate_ies_vertical( return cubic_interp(a, b, c, d, v_frac); } -ccl_device_inline float kernel_ies_interp(const KernelGlobals *kg, +ccl_device_inline float kernel_ies_interp(ccl_global const KernelGlobals *kg, int slot, float h_angle, float v_angle) @@ -98,9 +98,9 @@ ccl_device_inline float kernel_ies_interp(const KernelGlobals *kg, return max(cubic_interp(a, b, c, d, h_frac), 0.0f); } -ccl_device_noinline void svm_node_ies(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_ies(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { uint vector_offset, strength_offset, fac_offset, slot = node.z; diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index ce70109392b..2de80d5fc29 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -16,7 +16,8 @@ CCL_NAMESPACE_BEGIN -ccl_device float4 svm_image_texture(const KernelGlobals *kg, int id, float x, float y, uint flags) +ccl_device float4 +svm_image_texture(ccl_global const KernelGlobals *kg, int id, float x, float y, uint flags) { if (id == -1) { return make_float4( @@ -44,8 +45,11 @@ ccl_device_inline float3 texco_remap_square(float3 co) return (co - make_float3(0.5f, 0.5f, 0.5f)) * 2.0f; } -ccl_device_noinline int svm_node_tex_image( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_tex_image(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int offset) { uint co_offset, out_offset, alpha_offset, flags; @@ -117,9 +121,9 @@ ccl_device_noinline int svm_node_tex_image( return offset; } -ccl_device_noinline void svm_node_tex_image_box(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_tex_image_box(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { /* get object space normal */ @@ -219,9 +223,9 @@ ccl_device_noinline void svm_node_tex_image_box(const KernelGlobals *kg, stack_store_float(stack, alpha_offset, f.w); } -ccl_device_noinline void svm_node_tex_environment(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_tex_environment(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { uint id = node.y; diff --git a/intern/cycles/kernel/svm/svm_invert.h b/intern/cycles/kernel/svm/svm_invert.h index 27cdaaff473..60668ec00f1 100644 --- a/intern/cycles/kernel/svm/svm_invert.h +++ b/intern/cycles/kernel/svm/svm_invert.h @@ -21,8 +21,11 @@ ccl_device float invert(float color, float factor) return factor * (1.0f - color) + (1.0f - factor) * color; } -ccl_device_noinline void svm_node_invert( - ShaderData *sd, float *stack, uint in_fac, uint in_color, uint out_color) +ccl_device_noinline void svm_node_invert(ccl_private ShaderData *sd, + ccl_private float *stack, + uint in_fac, + uint in_color, + uint out_color) { float factor = stack_load_float(stack, in_fac); float3 color = stack_load_float3(stack, in_color); diff --git a/intern/cycles/kernel/svm/svm_light_path.h b/intern/cycles/kernel/svm/svm_light_path.h index 49fabad1cc5..aaff8376c7c 100644 --- a/intern/cycles/kernel/svm/svm_light_path.h +++ b/intern/cycles/kernel/svm/svm_light_path.h @@ -19,8 +19,8 @@ CCL_NAMESPACE_BEGIN /* Light Path Node */ ccl_device_noinline void svm_node_light_path(INTEGRATOR_STATE_CONST_ARGS, - const ShaderData *sd, - float *stack, + ccl_private const ShaderData *sd, + ccl_private float *stack, uint type, uint out_offset, int path_flag) @@ -106,7 +106,9 @@ ccl_device_noinline void svm_node_light_path(INTEGRATOR_STATE_CONST_ARGS, /* Light Falloff Node */ -ccl_device_noinline void svm_node_light_falloff(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_light_falloff(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint strength_offset, out_offset, smooth_offset; diff --git a/intern/cycles/kernel/svm/svm_magic.h b/intern/cycles/kernel/svm/svm_magic.h index 8784c760860..4c4f3bcf523 100644 --- a/intern/cycles/kernel/svm/svm_magic.h +++ b/intern/cycles/kernel/svm/svm_magic.h @@ -87,8 +87,11 @@ ccl_device_noinline_cpu float3 svm_magic(float3 p, int n, float distortion) return make_float3(0.5f - x, 0.5f - y, 0.5f - z); } -ccl_device_noinline int svm_node_tex_magic( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_tex_magic(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int offset) { uint depth; uint scale_offset, distortion_offset, co_offset, fac_offset, color_offset; diff --git a/intern/cycles/kernel/svm/svm_map_range.h b/intern/cycles/kernel/svm/svm_map_range.h index c8684981e31..f4f7d3ca76f 100644 --- a/intern/cycles/kernel/svm/svm_map_range.h +++ b/intern/cycles/kernel/svm/svm_map_range.h @@ -24,9 +24,9 @@ ccl_device_inline float smootherstep(float edge0, float edge1, float x) return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); } -ccl_device_noinline int svm_node_map_range(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_map_range(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint value_stack_offset, uint parameters_stack_offsets, uint results_stack_offsets, diff --git a/intern/cycles/kernel/svm/svm_mapping.h b/intern/cycles/kernel/svm/svm_mapping.h index fcc724405f5..8102afc637e 100644 --- a/intern/cycles/kernel/svm/svm_mapping.h +++ b/intern/cycles/kernel/svm/svm_mapping.h @@ -18,9 +18,9 @@ CCL_NAMESPACE_BEGIN /* Mapping Node */ -ccl_device_noinline void svm_node_mapping(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_mapping(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint type, uint inputs_stack_offsets, uint result_stack_offset) @@ -43,9 +43,9 @@ ccl_device_noinline void svm_node_mapping(const KernelGlobals *kg, /* Texture Mapping */ -ccl_device_noinline int svm_node_texture_mapping(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_texture_mapping(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint vec_offset, uint out_offset, int offset) @@ -62,9 +62,9 @@ ccl_device_noinline int svm_node_texture_mapping(const KernelGlobals *kg, return offset; } -ccl_device_noinline int svm_node_min_max(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_min_max(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint vec_offset, uint out_offset, int offset) diff --git a/intern/cycles/kernel/svm/svm_math.h b/intern/cycles/kernel/svm/svm_math.h index 99e7a8f2bda..3897a453873 100644 --- a/intern/cycles/kernel/svm/svm_math.h +++ b/intern/cycles/kernel/svm/svm_math.h @@ -16,9 +16,9 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_math(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_math(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint type, uint inputs_stack_offsets, uint result_stack_offset) @@ -34,9 +34,9 @@ ccl_device_noinline void svm_node_math(const KernelGlobals *kg, stack_store_float(stack, result_stack_offset, result); } -ccl_device_noinline int svm_node_vector_math(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_vector_math(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint type, uint inputs_stack_offsets, uint outputs_stack_offsets, diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/svm_math_util.h index 11b1e8f57f8..d3225b55ef0 100644 --- a/intern/cycles/kernel/svm/svm_math_util.h +++ b/intern/cycles/kernel/svm/svm_math_util.h @@ -16,8 +16,8 @@ CCL_NAMESPACE_BEGIN -ccl_device void svm_vector_math(float *value, - float3 *vector, +ccl_device void svm_vector_math(ccl_private float *value, + ccl_private float3 *vector, NodeVectorMathType type, float3 a, float3 b, diff --git a/intern/cycles/kernel/svm/svm_mix.h b/intern/cycles/kernel/svm/svm_mix.h index 3e38080977f..0064c5e643c 100644 --- a/intern/cycles/kernel/svm/svm_mix.h +++ b/intern/cycles/kernel/svm/svm_mix.h @@ -18,9 +18,9 @@ CCL_NAMESPACE_BEGIN /* Node */ -ccl_device_noinline int svm_node_mix(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_mix(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint fac_offset, uint c1_offset, uint c2_offset, diff --git a/intern/cycles/kernel/svm/svm_musgrave.h b/intern/cycles/kernel/svm/svm_musgrave.h index 03a8b68b3ef..8523f45b95f 100644 --- a/intern/cycles/kernel/svm/svm_musgrave.h +++ b/intern/cycles/kernel/svm/svm_musgrave.h @@ -700,9 +700,9 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_4d( return value; } -ccl_device_noinline int svm_node_tex_musgrave(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_tex_musgrave(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint offsets1, uint offsets2, uint offsets3, diff --git a/intern/cycles/kernel/svm/svm_noisetex.h b/intern/cycles/kernel/svm/svm_noisetex.h index 29b262ac06e..61da8227efa 100644 --- a/intern/cycles/kernel/svm/svm_noisetex.h +++ b/intern/cycles/kernel/svm/svm_noisetex.h @@ -55,8 +55,8 @@ ccl_device void noise_texture_1d(float co, float roughness, float distortion, bool color_is_needed, - float *value, - float3 *color) + ccl_private float *value, + ccl_private float3 *color) { float p = co; if (distortion != 0.0f) { @@ -76,8 +76,8 @@ ccl_device void noise_texture_2d(float2 co, float roughness, float distortion, bool color_is_needed, - float *value, - float3 *color) + ccl_private float *value, + ccl_private float3 *color) { float2 p = co; if (distortion != 0.0f) { @@ -98,8 +98,8 @@ ccl_device void noise_texture_3d(float3 co, float roughness, float distortion, bool color_is_needed, - float *value, - float3 *color) + ccl_private float *value, + ccl_private float3 *color) { float3 p = co; if (distortion != 0.0f) { @@ -121,8 +121,8 @@ ccl_device void noise_texture_4d(float4 co, float roughness, float distortion, bool color_is_needed, - float *value, - float3 *color) + ccl_private float *value, + ccl_private float3 *color) { float4 p = co; if (distortion != 0.0f) { @@ -140,9 +140,9 @@ ccl_device void noise_texture_4d(float4 co, } } -ccl_device_noinline int svm_node_tex_noise(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_tex_noise(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint dimensions, uint offsets1, uint offsets2, diff --git a/intern/cycles/kernel/svm/svm_normal.h b/intern/cycles/kernel/svm/svm_normal.h index 724b5f281f9..0d1b4200d54 100644 --- a/intern/cycles/kernel/svm/svm_normal.h +++ b/intern/cycles/kernel/svm/svm_normal.h @@ -16,9 +16,9 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline int svm_node_normal(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_normal(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint in_normal_offset, uint out_normal_offset, uint out_dot_offset, diff --git a/intern/cycles/kernel/svm/svm_ramp.h b/intern/cycles/kernel/svm/svm_ramp.h index 563e5bcb5e4..ef8b0d103c1 100644 --- a/intern/cycles/kernel/svm/svm_ramp.h +++ b/intern/cycles/kernel/svm/svm_ramp.h @@ -21,13 +21,13 @@ CCL_NAMESPACE_BEGIN /* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */ -ccl_device_inline float fetch_float(const KernelGlobals *kg, int offset) +ccl_device_inline float fetch_float(ccl_global const KernelGlobals *kg, int offset) { uint4 node = kernel_tex_fetch(__svm_nodes, offset); return __uint_as_float(node.x); } -ccl_device_inline float float_ramp_lookup(const KernelGlobals *kg, +ccl_device_inline float float_ramp_lookup(ccl_global const KernelGlobals *kg, int offset, float f, bool interpolate, @@ -63,7 +63,7 @@ ccl_device_inline float float_ramp_lookup(const KernelGlobals *kg, return a; } -ccl_device_inline float4 rgb_ramp_lookup(const KernelGlobals *kg, +ccl_device_inline float4 rgb_ramp_lookup(ccl_global const KernelGlobals *kg, int offset, float f, bool interpolate, @@ -99,8 +99,11 @@ ccl_device_inline float4 rgb_ramp_lookup(const KernelGlobals *kg, return a; } -ccl_device_noinline int svm_node_rgb_ramp( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_rgb_ramp(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int offset) { uint fac_offset, color_offset, alpha_offset; uint interpolate = node.z; @@ -121,8 +124,11 @@ ccl_device_noinline int svm_node_rgb_ramp( return offset; } -ccl_device_noinline int svm_node_curves( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_curves(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int offset) { uint fac_offset, color_offset, out_offset; svm_unpack_node_uchar3(node.y, &fac_offset, &color_offset, &out_offset); @@ -147,8 +153,11 @@ ccl_device_noinline int svm_node_curves( return offset; } -ccl_device_noinline int svm_node_curve( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_curve(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int offset) { uint fac_offset, value_in_offset, out_offset; svm_unpack_node_uchar3(node.y, &fac_offset, &value_in_offset, &out_offset); diff --git a/intern/cycles/kernel/svm/svm_sepcomb_hsv.h b/intern/cycles/kernel/svm/svm_sepcomb_hsv.h index 8d52845ea3d..3cd4ba87a55 100644 --- a/intern/cycles/kernel/svm/svm_sepcomb_hsv.h +++ b/intern/cycles/kernel/svm/svm_sepcomb_hsv.h @@ -16,9 +16,9 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline int svm_node_combine_hsv(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_combine_hsv(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint hue_in, uint saturation_in, uint value_in, @@ -39,9 +39,9 @@ ccl_device_noinline int svm_node_combine_hsv(const KernelGlobals *kg, return offset; } -ccl_device_noinline int svm_node_separate_hsv(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_separate_hsv(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint color_in, uint hue_out, uint saturation_out, diff --git a/intern/cycles/kernel/svm/svm_sepcomb_vector.h b/intern/cycles/kernel/svm/svm_sepcomb_vector.h index cbf77f1e640..11e440f2cbf 100644 --- a/intern/cycles/kernel/svm/svm_sepcomb_vector.h +++ b/intern/cycles/kernel/svm/svm_sepcomb_vector.h @@ -18,8 +18,11 @@ CCL_NAMESPACE_BEGIN /* Vector combine / separate, used for the RGB and XYZ nodes */ -ccl_device void svm_node_combine_vector( - ShaderData *sd, float *stack, uint in_offset, uint vector_index, uint out_offset) +ccl_device void svm_node_combine_vector(ccl_private ShaderData *sd, + ccl_private float *stack, + uint in_offset, + uint vector_index, + uint out_offset) { float vector = stack_load_float(stack, in_offset); @@ -27,8 +30,11 @@ ccl_device void svm_node_combine_vector( stack_store_float(stack, out_offset + vector_index, vector); } -ccl_device void svm_node_separate_vector( - ShaderData *sd, float *stack, uint ivector_offset, uint vector_index, uint out_offset) +ccl_device void svm_node_separate_vector(ccl_private ShaderData *sd, + ccl_private float *stack, + uint ivector_offset, + uint vector_index, + uint out_offset) { float3 vector = stack_load_float3(stack, ivector_offset); diff --git a/intern/cycles/kernel/svm/svm_sky.h b/intern/cycles/kernel/svm/svm_sky.h index b77c4311e72..04db8109170 100644 --- a/intern/cycles/kernel/svm/svm_sky.h +++ b/intern/cycles/kernel/svm/svm_sky.h @@ -28,7 +28,7 @@ ccl_device float sky_angle_between(float thetav, float phiv, float theta, float * "A Practical Analytic Model for Daylight" * A. J. Preetham, Peter Shirley, Brian Smits */ -ccl_device float sky_perez_function(float *lam, float theta, float gamma) +ccl_device float sky_perez_function(ccl_private float *lam, float theta, float gamma) { float ctheta = cosf(theta); float cgamma = cosf(gamma); @@ -37,16 +37,16 @@ ccl_device float sky_perez_function(float *lam, float theta, float gamma) (1.0f + lam[2] * expf(lam[3] * gamma) + lam[4] * cgamma * cgamma); } -ccl_device float3 sky_radiance_preetham(const KernelGlobals *kg, +ccl_device float3 sky_radiance_preetham(ccl_global const KernelGlobals *kg, float3 dir, float sunphi, float suntheta, float radiance_x, float radiance_y, float radiance_z, - float *config_x, - float *config_y, - float *config_z) + ccl_private float *config_x, + ccl_private float *config_y, + ccl_private float *config_z) { /* convert vector to spherical coordinates */ float2 spherical = direction_to_spherical(dir); @@ -73,7 +73,7 @@ ccl_device float3 sky_radiance_preetham(const KernelGlobals *kg, * "An Analytic Model for Full Spectral Sky-Dome Radiance" * Lukas Hosek, Alexander Wilkie */ -ccl_device float sky_radiance_internal(float *configuration, float theta, float gamma) +ccl_device float sky_radiance_internal(ccl_private float *configuration, float theta, float gamma) { float ctheta = cosf(theta); float cgamma = cosf(gamma); @@ -90,16 +90,16 @@ ccl_device float sky_radiance_internal(float *configuration, float theta, float configuration[6] * mieM + configuration[7] * zenith); } -ccl_device float3 sky_radiance_hosek(const KernelGlobals *kg, +ccl_device float3 sky_radiance_hosek(ccl_global const KernelGlobals *kg, float3 dir, float sunphi, float suntheta, float radiance_x, float radiance_y, float radiance_z, - float *config_x, - float *config_y, - float *config_z) + ccl_private float *config_x, + ccl_private float *config_y, + ccl_private float *config_z) { /* convert vector to spherical coordinates */ float2 spherical = direction_to_spherical(dir); @@ -127,9 +127,9 @@ ccl_device float3 geographical_to_direction(float lat, float lon) return make_float3(cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat)); } -ccl_device float3 sky_radiance_nishita(const KernelGlobals *kg, +ccl_device float3 sky_radiance_nishita(ccl_global const KernelGlobals *kg, float3 dir, - float *nishita_data, + ccl_private float *nishita_data, uint texture_id) { /* definitions */ @@ -209,8 +209,11 @@ ccl_device float3 sky_radiance_nishita(const KernelGlobals *kg, return xyz_to_rgb(kg, xyz); } -ccl_device_noinline int svm_node_tex_sky( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_tex_sky(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int offset) { /* Load data */ uint dir_offset = node.y; diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index 8869001015b..295d5e9f65b 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -22,8 +22,12 @@ CCL_NAMESPACE_BEGIN /* Texture Coordinate Node */ -ccl_device_noinline int svm_node_tex_coord( - const KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_tex_coord(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + int path_flag, + ccl_private float *stack, + uint4 node, + int offset) { float3 data; uint type = node.y; @@ -99,8 +103,12 @@ ccl_device_noinline int svm_node_tex_coord( return offset; } -ccl_device_noinline int svm_node_tex_coord_bump_dx( - const KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_tex_coord_bump_dx(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + int path_flag, + ccl_private float *stack, + uint4 node, + int offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -180,8 +188,12 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx( #endif } -ccl_device_noinline int svm_node_tex_coord_bump_dy( - const KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_tex_coord_bump_dy(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + int path_flag, + ccl_private float *stack, + uint4 node, + int offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -261,9 +273,9 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy( #endif } -ccl_device_noinline void svm_node_normal_map(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_normal_map(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { uint color_offset, strength_offset, normal_offset, space; @@ -354,9 +366,9 @@ ccl_device_noinline void svm_node_normal_map(const KernelGlobals *kg, stack_store_float3(stack, normal_offset, N); } -ccl_device_noinline void svm_node_tangent(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_tangent(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { uint tangent_offset, direction_type, axis; diff --git a/intern/cycles/kernel/svm/svm_value.h b/intern/cycles/kernel/svm/svm_value.h index d0478660094..d1038bc072d 100644 --- a/intern/cycles/kernel/svm/svm_value.h +++ b/intern/cycles/kernel/svm/svm_value.h @@ -18,14 +18,20 @@ CCL_NAMESPACE_BEGIN /* Value Nodes */ -ccl_device void svm_node_value_f( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint ivalue, uint out_offset) +ccl_device void svm_node_value_f(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint ivalue, + uint out_offset) { stack_store_float(stack, out_offset, __uint_as_float(ivalue)); } -ccl_device int svm_node_value_v( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint out_offset, int offset) +ccl_device int svm_node_value_v(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint out_offset, + int offset) { /* read extra data */ uint4 node1 = read_node(kg, &offset); diff --git a/intern/cycles/kernel/svm/svm_vector_rotate.h b/intern/cycles/kernel/svm/svm_vector_rotate.h index 55e1bce0158..c20f9b2556f 100644 --- a/intern/cycles/kernel/svm/svm_vector_rotate.h +++ b/intern/cycles/kernel/svm/svm_vector_rotate.h @@ -18,8 +18,8 @@ CCL_NAMESPACE_BEGIN /* Vector Rotate */ -ccl_device_noinline void svm_node_vector_rotate(ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_vector_rotate(ccl_private ShaderData *sd, + ccl_private float *stack, uint input_stack_offsets, uint axis_stack_offsets, uint result_stack_offset) diff --git a/intern/cycles/kernel/svm/svm_vector_transform.h b/intern/cycles/kernel/svm/svm_vector_transform.h index 8aedb7e0f54..b6c898c3952 100644 --- a/intern/cycles/kernel/svm/svm_vector_transform.h +++ b/intern/cycles/kernel/svm/svm_vector_transform.h @@ -18,9 +18,9 @@ CCL_NAMESPACE_BEGIN /* Vector Transform */ -ccl_device_noinline void svm_node_vector_transform(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_vector_transform(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { uint itype, ifrom, ito; diff --git a/intern/cycles/kernel/svm/svm_vertex_color.h b/intern/cycles/kernel/svm/svm_vertex_color.h index 986ea244f3a..3641f05ca43 100644 --- a/intern/cycles/kernel/svm/svm_vertex_color.h +++ b/intern/cycles/kernel/svm/svm_vertex_color.h @@ -16,9 +16,9 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_vertex_color(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_vertex_color(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint layer_id, uint color_offset, uint alpha_offset) @@ -35,9 +35,9 @@ ccl_device_noinline void svm_node_vertex_color(const KernelGlobals *kg, } } -ccl_device_noinline void svm_node_vertex_color_bump_dx(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_vertex_color_bump_dx(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint layer_id, uint color_offset, uint alpha_offset) @@ -56,9 +56,9 @@ ccl_device_noinline void svm_node_vertex_color_bump_dx(const KernelGlobals *kg, } } -ccl_device_noinline void svm_node_vertex_color_bump_dy(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_vertex_color_bump_dy(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint layer_id, uint color_offset, uint alpha_offset) diff --git a/intern/cycles/kernel/svm/svm_voronoi.h b/intern/cycles/kernel/svm/svm_voronoi.h index b1d2eff7f37..e7112087e17 100644 --- a/intern/cycles/kernel/svm/svm_voronoi.h +++ b/intern/cycles/kernel/svm/svm_voronoi.h @@ -46,9 +46,9 @@ ccl_device void voronoi_f1_1d(float w, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float *outW) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float *outW) { float cellPosition = floorf(w); float localPosition = w - cellPosition; @@ -76,9 +76,9 @@ ccl_device void voronoi_smooth_f1_1d(float w, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float *outW) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float *outW) { float cellPosition = floorf(w); float localPosition = w - cellPosition; @@ -108,9 +108,9 @@ ccl_device void voronoi_f2_1d(float w, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float *outW) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float *outW) { float cellPosition = floorf(w); float localPosition = w - cellPosition; @@ -144,7 +144,9 @@ ccl_device void voronoi_f2_1d(float w, *outW = positionF2 + cellPosition; } -ccl_device void voronoi_distance_to_edge_1d(float w, float randomness, float *outDistance) +ccl_device void voronoi_distance_to_edge_1d(float w, + float randomness, + ccl_private float *outDistance) { float cellPosition = floorf(w); float localPosition = w - cellPosition; @@ -158,7 +160,7 @@ ccl_device void voronoi_distance_to_edge_1d(float w, float randomness, float *ou *outDistance = min(distanceToMidLeft, distanceToMidRight); } -ccl_device void voronoi_n_sphere_radius_1d(float w, float randomness, float *outRadius) +ccl_device void voronoi_n_sphere_radius_1d(float w, float randomness, ccl_private float *outRadius) { float cellPosition = floorf(w); float localPosition = w - cellPosition; @@ -223,9 +225,9 @@ ccl_device void voronoi_f1_2d(float2 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float2 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float2 *outPosition) { float2 cellPosition = floor(coord); float2 localPosition = coord - cellPosition; @@ -256,9 +258,9 @@ ccl_device void voronoi_smooth_f1_2d(float2 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float2 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float2 *outPosition) { float2 cellPosition = floor(coord); float2 localPosition = coord - cellPosition; @@ -291,9 +293,9 @@ ccl_device void voronoi_f2_2d(float2 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float2 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float2 *outPosition) { float2 cellPosition = floor(coord); float2 localPosition = coord - cellPosition; @@ -330,7 +332,9 @@ ccl_device void voronoi_f2_2d(float2 coord, *outPosition = positionF2 + cellPosition; } -ccl_device void voronoi_distance_to_edge_2d(float2 coord, float randomness, float *outDistance) +ccl_device void voronoi_distance_to_edge_2d(float2 coord, + float randomness, + ccl_private float *outDistance) { float2 cellPosition = floor(coord); float2 localPosition = coord - cellPosition; @@ -369,7 +373,9 @@ ccl_device void voronoi_distance_to_edge_2d(float2 coord, float randomness, floa *outDistance = minDistance; } -ccl_device void voronoi_n_sphere_radius_2d(float2 coord, float randomness, float *outRadius) +ccl_device void voronoi_n_sphere_radius_2d(float2 coord, + float randomness, + ccl_private float *outRadius) { float2 cellPosition = floor(coord); float2 localPosition = coord - cellPosition; @@ -441,9 +447,9 @@ ccl_device void voronoi_f1_3d(float3 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float3 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float3 *outPosition) { float3 cellPosition = floor(coord); float3 localPosition = coord - cellPosition; @@ -477,9 +483,9 @@ ccl_device void voronoi_smooth_f1_3d(float3 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float3 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float3 *outPosition) { float3 cellPosition = floor(coord); float3 localPosition = coord - cellPosition; @@ -515,9 +521,9 @@ ccl_device void voronoi_f2_3d(float3 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float3 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float3 *outPosition) { float3 cellPosition = floor(coord); float3 localPosition = coord - cellPosition; @@ -557,7 +563,9 @@ ccl_device void voronoi_f2_3d(float3 coord, *outPosition = positionF2 + cellPosition; } -ccl_device void voronoi_distance_to_edge_3d(float3 coord, float randomness, float *outDistance) +ccl_device void voronoi_distance_to_edge_3d(float3 coord, + float randomness, + ccl_private float *outDistance) { float3 cellPosition = floor(coord); float3 localPosition = coord - cellPosition; @@ -600,7 +608,9 @@ ccl_device void voronoi_distance_to_edge_3d(float3 coord, float randomness, floa *outDistance = minDistance; } -ccl_device void voronoi_n_sphere_radius_3d(float3 coord, float randomness, float *outRadius) +ccl_device void voronoi_n_sphere_radius_3d(float3 coord, + float randomness, + ccl_private float *outRadius) { float3 cellPosition = floor(coord); float3 localPosition = coord - cellPosition; @@ -676,9 +686,9 @@ ccl_device void voronoi_f1_4d(float4 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float4 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float4 *outPosition) { float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; @@ -715,9 +725,9 @@ ccl_device void voronoi_smooth_f1_4d(float4 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float4 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float4 *outPosition) { float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; @@ -756,9 +766,9 @@ ccl_device void voronoi_f2_4d(float4 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float4 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float4 *outPosition) { float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; @@ -801,7 +811,9 @@ ccl_device void voronoi_f2_4d(float4 coord, *outPosition = positionF2 + cellPosition; } -ccl_device void voronoi_distance_to_edge_4d(float4 coord, float randomness, float *outDistance) +ccl_device void voronoi_distance_to_edge_4d(float4 coord, + float randomness, + ccl_private float *outDistance) { float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; @@ -850,7 +862,9 @@ ccl_device void voronoi_distance_to_edge_4d(float4 coord, float randomness, floa *outDistance = minDistance; } -ccl_device void voronoi_n_sphere_radius_4d(float4 coord, float randomness, float *outRadius) +ccl_device void voronoi_n_sphere_radius_4d(float4 coord, + float randomness, + ccl_private float *outRadius) { float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; @@ -903,9 +917,9 @@ ccl_device void voronoi_n_sphere_radius_4d(float4 coord, float randomness, float } template -ccl_device_noinline int svm_node_tex_voronoi(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline int svm_node_tex_voronoi(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint dimensions, uint feature, uint metric, diff --git a/intern/cycles/kernel/svm/svm_voxel.h b/intern/cycles/kernel/svm/svm_voxel.h index 78b75405356..764fb71ba72 100644 --- a/intern/cycles/kernel/svm/svm_voxel.h +++ b/intern/cycles/kernel/svm/svm_voxel.h @@ -19,8 +19,11 @@ CCL_NAMESPACE_BEGIN /* TODO(sergey): Think of making it more generic volume-type attribute * sampler. */ -ccl_device_noinline int svm_node_tex_voxel( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_tex_voxel(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int offset) { uint co_offset, density_out_offset, color_out_offset, space; svm_unpack_node_uchar4(node.z, &co_offset, &density_out_offset, &color_out_offset, &space); diff --git a/intern/cycles/kernel/svm/svm_wave.h b/intern/cycles/kernel/svm/svm_wave.h index 00f980c16df..1ac130e2006 100644 --- a/intern/cycles/kernel/svm/svm_wave.h +++ b/intern/cycles/kernel/svm/svm_wave.h @@ -82,8 +82,11 @@ ccl_device_noinline_cpu float svm_wave(NodeWaveType type, } } -ccl_device_noinline int svm_node_tex_wave( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset) +ccl_device_noinline int svm_node_tex_wave(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + int offset) { uint4 node2 = read_node(kg, &offset); uint4 node3 = read_node(kg, &offset); diff --git a/intern/cycles/kernel/svm/svm_wavelength.h b/intern/cycles/kernel/svm/svm_wavelength.h index aa291fd2741..e891744f276 100644 --- a/intern/cycles/kernel/svm/svm_wavelength.h +++ b/intern/cycles/kernel/svm/svm_wavelength.h @@ -34,8 +34,11 @@ CCL_NAMESPACE_BEGIN /* Wavelength to RGB */ -ccl_device_noinline void svm_node_wavelength( - const KernelGlobals *kg, ShaderData *sd, float *stack, uint wavelength, uint color_out) +ccl_device_noinline void svm_node_wavelength(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint wavelength, + uint color_out) { // CIE colour matching functions xBar, yBar, and zBar for // wavelengths from 380 through 780 nanometers, every 5 diff --git a/intern/cycles/kernel/svm/svm_white_noise.h b/intern/cycles/kernel/svm/svm_white_noise.h index 0306d2e7b9c..ccc49bf1a7c 100644 --- a/intern/cycles/kernel/svm/svm_white_noise.h +++ b/intern/cycles/kernel/svm/svm_white_noise.h @@ -16,9 +16,9 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_tex_white_noise(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_tex_white_noise(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint dimensions, uint inputs_stack_offsets, uint ouptuts_stack_offsets) diff --git a/intern/cycles/kernel/svm/svm_wireframe.h b/intern/cycles/kernel/svm/svm_wireframe.h index 7ec913789d2..70d1211aa4a 100644 --- a/intern/cycles/kernel/svm/svm_wireframe.h +++ b/intern/cycles/kernel/svm/svm_wireframe.h @@ -34,8 +34,11 @@ CCL_NAMESPACE_BEGIN /* Wireframe Node */ -ccl_device_inline float wireframe( - const KernelGlobals *kg, ShaderData *sd, float size, int pixel_size, float3 *P) +ccl_device_inline float wireframe(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + float size, + int pixel_size, + ccl_private float3 *P) { #ifdef __HAIR__ if (sd->prim != PRIM_NONE && sd->type & PRIMITIVE_ALL_TRIANGLE) @@ -88,9 +91,9 @@ ccl_device_inline float wireframe( return 0.0f; } -ccl_device_noinline void svm_node_wireframe(const KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device_noinline void svm_node_wireframe(ccl_global const KernelGlobals *kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint4 node) { uint in_size = node.y; diff --git a/intern/cycles/util/util_color.h b/intern/cycles/util/util_color.h index 7b67b90e44d..361c36d9061 100644 --- a/intern/cycles/util/util_color.h +++ b/intern/cycles/util/util_color.h @@ -277,7 +277,7 @@ ccl_device float4 color_srgb_to_linear_v4(float4 c) #endif } -ccl_device float3 color_highlight_compress(float3 color, float3 *variance) +ccl_device float3 color_highlight_compress(float3 color, ccl_private float3 *variance) { color += one_float3(); if (variance) { diff --git a/intern/cycles/util/util_half.h b/intern/cycles/util/util_half.h index f36a492a1b0..81723abe1e2 100644 --- a/intern/cycles/util/util_half.h +++ b/intern/cycles/util/util_half.h @@ -61,7 +61,7 @@ struct half4 { #if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__) -ccl_device_inline void float4_store_half(half *h, float4 f) +ccl_device_inline void float4_store_half(ccl_private half *h, float4 f) { h[0] = __float2half(f.x); h[1] = __float2half(f.y); @@ -71,7 +71,7 @@ ccl_device_inline void float4_store_half(half *h, float4 f) #else -ccl_device_inline void float4_store_half(half *h, float4 f) +ccl_device_inline void float4_store_half(ccl_private half *h, float4 f) { # ifndef __KERNEL_SSE2__ diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index cb1e94c838c..f834011a032 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -338,7 +338,7 @@ ccl_device_inline int quick_floor_to_int(float x) return float_to_int(x) - ((x < 0) ? 1 : 0); } -ccl_device_inline float floorfrac(float x, int *i) +ccl_device_inline float floorfrac(float x, ccl_private int *i) { *i = quick_floor_to_int(x); return x - *i; @@ -465,14 +465,18 @@ template A lerp(const A &a, const A &b, const B &t) /* Triangle */ -ccl_device_inline float triangle_area(const float3 &v1, const float3 &v2, const float3 &v3) +ccl_device_inline float triangle_area(ccl_private const float3 &v1, + ccl_private const float3 &v2, + ccl_private const float3 &v3) { return len(cross(v3 - v2, v1 - v2)) * 0.5f; } /* Orthonormal vectors */ -ccl_device_inline void make_orthonormals(const float3 N, float3 *a, float3 *b) +ccl_device_inline void make_orthonormals(const float3 N, + ccl_private float3 *a, + ccl_private float3 *b) { #if 0 if (fabsf(N.y) >= 0.999f) { diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/util_math_fast.h index 38afa163db5..cc924f36a71 100644 --- a/intern/cycles/util/util_math_fast.h +++ b/intern/cycles/util/util_math_fast.h @@ -156,7 +156,7 @@ ccl_device float fast_cosf(float x) return u; } -ccl_device void fast_sincosf(float x, float *sine, float *cosine) +ccl_device void fast_sincosf(float x, ccl_private float *sine, ccl_private float *cosine) { /* Same argument reduction as fast_sin. */ int q = fast_rint(x * M_1_PI_F); diff --git a/intern/cycles/util/util_math_float2.h b/intern/cycles/util/util_math_float2.h index 70b80c33544..25eda840214 100644 --- a/intern/cycles/util/util_math_float2.h +++ b/intern/cycles/util/util_math_float2.h @@ -207,7 +207,7 @@ ccl_device_inline float2 normalize(const float2 &a) return a / len(a); } -ccl_device_inline float2 normalize_len(const float2 &a, float *t) +ccl_device_inline float2 normalize_len(const float2 &a, ccl_private float *t) { *t = len(a); return a / (*t); diff --git a/intern/cycles/util/util_math_float3.h b/intern/cycles/util/util_math_float3.h index 30a1b4c3f77..c3230a8068c 100644 --- a/intern/cycles/util/util_math_float3.h +++ b/intern/cycles/util/util_math_float3.h @@ -411,7 +411,7 @@ ccl_device_inline float3 saturate3(float3 a) return make_float3(saturate(a.x), saturate(a.y), saturate(a.z)); } -ccl_device_inline float3 normalize_len(const float3 a, float *t) +ccl_device_inline float3 normalize_len(const float3 a, ccl_private float *t) { *t = len(a); float x = 1.0f / *t; @@ -424,7 +424,7 @@ ccl_device_inline float3 safe_normalize(const float3 a) return (t != 0.0f) ? a * (1.0f / t) : a; } -ccl_device_inline float3 safe_normalize_len(const float3 a, float *t) +ccl_device_inline float3 safe_normalize_len(const float3 a, ccl_private float *t) { *t = len(a); return (*t != 0.0f) ? a / (*t) : a; diff --git a/intern/cycles/util/util_math_float4.h b/intern/cycles/util/util_math_float4.h index 19af5c8c638..f30a78cfc69 100644 --- a/intern/cycles/util/util_math_float4.h +++ b/intern/cycles/util/util_math_float4.h @@ -497,7 +497,7 @@ ccl_device_inline float4 reduce_max(const float4 &a) # endif } -ccl_device_inline float4 load_float4(const float *v) +ccl_device_inline float4 load_float4(ccl_private const float *v) { # ifdef __KERNEL_SSE__ return float4(_mm_loadu_ps(v)); diff --git a/intern/cycles/util/util_math_intersect.h b/intern/cycles/util/util_math_intersect.h index fd0c9124345..0c431a36afb 100644 --- a/intern/cycles/util/util_math_intersect.h +++ b/intern/cycles/util/util_math_intersect.h @@ -26,8 +26,8 @@ ccl_device bool ray_sphere_intersect(float3 ray_P, float ray_t, float3 sphere_P, float sphere_radius, - float3 *isect_P, - float *isect_t) + ccl_private float3 *isect_P, + ccl_private float *isect_t) { const float3 d = sphere_P - ray_P; const float radiussq = sphere_radius * sphere_radius; @@ -60,8 +60,8 @@ ccl_device bool ray_aligned_disk_intersect(float3 ray_P, float ray_t, float3 disk_P, float disk_radius, - float3 *isect_P, - float *isect_t) + ccl_private float3 *isect_P, + ccl_private float *isect_t) { /* Aligned disk normal. */ float disk_t; @@ -95,9 +95,9 @@ ccl_device_forceinline bool ray_triangle_intersect(float3 ray_P, const float3 tri_b, const float3 tri_c, #endif - float *isect_u, - float *isect_v, - float *isect_t) + ccl_private float *isect_u, + ccl_private float *isect_v, + ccl_private float *isect_t) { #if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) typedef ssef float3; @@ -207,10 +207,10 @@ ccl_device bool ray_quad_intersect(float3 ray_P, float3 quad_u, float3 quad_v, float3 quad_n, - float3 *isect_P, - float *isect_t, - float *isect_u, - float *isect_v, + ccl_private float3 *isect_P, + ccl_private float *isect_t, + ccl_private float *isect_u, + ccl_private float *isect_v, bool ellipse) { /* Perform intersection test. */ diff --git a/intern/cycles/util/util_math_matrix.h b/intern/cycles/util/util_math_matrix.h index 123736f75a6..bff7ddb4cee 100644 --- a/intern/cycles/util/util_math_matrix.h +++ b/intern/cycles/util/util_math_matrix.h @@ -35,14 +35,14 @@ CCL_NAMESPACE_BEGIN /* Zeroing helpers. */ -ccl_device_inline void math_vector_zero(float *v, int n) +ccl_device_inline void math_vector_zero(ccl_private float *v, int n) { for (int i = 0; i < n; i++) { v[i] = 0.0f; } } -ccl_device_inline void math_matrix_zero(float *A, int n) +ccl_device_inline void math_matrix_zero(ccl_private float *A, int n) { for (int row = 0; row < n; row++) { for (int col = 0; col <= row; col++) { @@ -53,14 +53,18 @@ ccl_device_inline void math_matrix_zero(float *A, int n) /* Elementary vector operations. */ -ccl_device_inline void math_vector_add(float *a, const float *ccl_restrict b, int n) +ccl_device_inline void math_vector_add(ccl_private float *a, + ccl_private const float *ccl_restrict b, + int n) { for (int i = 0; i < n; i++) { a[i] += b[i]; } } -ccl_device_inline void math_vector_mul(float *a, const float *ccl_restrict b, int n) +ccl_device_inline void math_vector_mul(ccl_private float *a, + ccl_private const float *ccl_restrict b, + int n) { for (int i = 0; i < n; i++) { a[i] *= b[i]; @@ -68,7 +72,7 @@ ccl_device_inline void math_vector_mul(float *a, const float *ccl_restrict b, in } ccl_device_inline void math_vector_mul_strided(ccl_global float *a, - const float *ccl_restrict b, + ccl_private const float *ccl_restrict b, int astride, int n) { @@ -77,21 +81,23 @@ ccl_device_inline void math_vector_mul_strided(ccl_global float *a, } } -ccl_device_inline void math_vector_scale(float *a, float b, int n) +ccl_device_inline void math_vector_scale(ccl_private float *a, float b, int n) { for (int i = 0; i < n; i++) { a[i] *= b; } } -ccl_device_inline void math_vector_max(float *a, const float *ccl_restrict b, int n) +ccl_device_inline void math_vector_max(ccl_private float *a, + ccl_private const float *ccl_restrict b, + int n) { for (int i = 0; i < n; i++) { a[i] = max(a[i], b[i]); } } -ccl_device_inline void math_vec3_add(float3 *v, int n, float *x, float3 w) +ccl_device_inline void math_vec3_add(ccl_private float3 *v, int n, ccl_private float *x, float3 w) { for (int i = 0; i < n; i++) { v[i] += w * x[i]; @@ -99,7 +105,7 @@ ccl_device_inline void math_vec3_add(float3 *v, int n, float *x, float3 w) } ccl_device_inline void math_vec3_add_strided( - ccl_global float3 *v, int n, float *x, float3 w, int stride) + ccl_global float3 *v, int n, ccl_private float *x, float3 w, int stride) { for (int i = 0; i < n; i++) { ccl_global float *elem = (ccl_global float *)(v + i * stride); @@ -125,9 +131,9 @@ ccl_device_inline void math_trimatrix_add_diagonal(ccl_global float *A, /* Add Gramian matrix of v to A. * The Gramian matrix of v is vt*v, so element (i,j) is v[i]*v[j]. */ -ccl_device_inline void math_matrix_add_gramian(float *A, +ccl_device_inline void math_matrix_add_gramian(ccl_private float *A, int n, - const float *ccl_restrict v, + ccl_private const float *ccl_restrict v, float weight) { for (int row = 0; row < n; row++) { @@ -140,7 +146,7 @@ ccl_device_inline void math_matrix_add_gramian(float *A, /* Add Gramian matrix of v to A. * The Gramian matrix of v is vt*v, so element (i,j) is v[i]*v[j]. */ ccl_device_inline void math_trimatrix_add_gramian_strided( - ccl_global float *A, int n, const float *ccl_restrict v, float weight, int stride) + ccl_global float *A, int n, ccl_private const float *ccl_restrict v, float weight, int stride) { for (int row = 0; row < n; row++) { for (int col = 0; col <= row; col++) { @@ -151,7 +157,7 @@ ccl_device_inline void math_trimatrix_add_gramian_strided( ccl_device_inline void math_trimatrix_add_gramian(ccl_global float *A, int n, - const float *ccl_restrict v, + ccl_private const float *ccl_restrict v, float weight) { for (int row = 0; row < n; row++) { @@ -244,7 +250,7 @@ ccl_device_inline void math_trimatrix_vec3_solve(ccl_global float *A, * and V will contain the eigenvectors of the original A in its rows (!), * so that A = V^T*D*V. Therefore, the diagonal elements of D are the (sorted) eigenvalues of A. */ -ccl_device void math_matrix_jacobi_eigendecomposition(float *A, +ccl_device void math_matrix_jacobi_eigendecomposition(ccl_private float *A, ccl_global float *V, int n, int v_stride) diff --git a/intern/cycles/util/util_projection.h b/intern/cycles/util/util_projection.h index 9c7e0061c82..04b4574d75b 100644 --- a/intern/cycles/util/util_projection.h +++ b/intern/cycles/util/util_projection.h @@ -45,7 +45,8 @@ typedef struct PerspectiveMotionTransform { /* Functions */ -ccl_device_inline float3 transform_perspective(const ProjectionTransform *t, const float3 a) +ccl_device_inline float3 transform_perspective(ccl_private const ProjectionTransform *t, + const float3 a) { float4 b = make_float4(a.x, a.y, a.z, 1.0f); float3 c = make_float3(dot(t->x, b), dot(t->y, b), dot(t->z, b)); @@ -54,7 +55,7 @@ ccl_device_inline float3 transform_perspective(const ProjectionTransform *t, con return (w != 0.0f) ? c / w : zero_float3(); } -ccl_device_inline float3 transform_perspective_direction(const ProjectionTransform *t, +ccl_device_inline float3 transform_perspective_direction(ccl_private const ProjectionTransform *t, const float3 a) { float3 c = make_float3(a.x * t->x.x + a.y * t->x.y + a.z * t->x.z, diff --git a/intern/cycles/util/util_rect.h b/intern/cycles/util/util_rect.h index 36f02a01f7b..32df9327cbd 100644 --- a/intern/cycles/util/util_rect.h +++ b/intern/cycles/util/util_rect.h @@ -54,7 +54,10 @@ ccl_device_inline int coord_to_local_index(int4 rect, int x, int y) /* Finds the coordinates of a pixel given by its row-major index in the rect, * and returns whether the pixel is inside it. */ -ccl_device_inline bool local_index_to_coord(int4 rect, int idx, int *x, int *y) +ccl_device_inline bool local_index_to_coord(int4 rect, + int idx, + ccl_private int *x, + ccl_private int *y) { int w = rect.z - rect.x; *x = (idx % w) + rect.x; diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index e9cd3b0b483..fc04f9aab46 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -53,7 +53,7 @@ typedef struct DecomposedTransform { /* Functions */ -ccl_device_inline float3 transform_point(const Transform *t, const float3 a) +ccl_device_inline float3 transform_point(ccl_private const Transform *t, const float3 a) { /* TODO(sergey): Disabled for now, causes crashes in certain cases. */ #if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE2__) @@ -82,7 +82,7 @@ ccl_device_inline float3 transform_point(const Transform *t, const float3 a) #endif } -ccl_device_inline float3 transform_direction(const Transform *t, const float3 a) +ccl_device_inline float3 transform_direction(ccl_private const Transform *t, const float3 a) { #if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE2__) ssef x, y, z, w, aa; @@ -108,7 +108,8 @@ ccl_device_inline float3 transform_direction(const Transform *t, const float3 a) #endif } -ccl_device_inline float3 transform_direction_transposed(const Transform *t, const float3 a) +ccl_device_inline float3 transform_direction_transposed(ccl_private const Transform *t, + const float3 a) { float3 x = make_float3(t->x.x, t->y.x, t->z.x); float3 y = make_float3(t->x.y, t->y.y, t->z.y); @@ -409,7 +410,8 @@ ccl_device_inline Transform transform_quick_inverse(Transform M) return R; } -ccl_device_inline void transform_compose(Transform *tfm, const DecomposedTransform *decomp) +ccl_device_inline void transform_compose(ccl_private Transform *tfm, + ccl_private const DecomposedTransform *decomp) { /* rotation */ float q0, q1, q2, q3, qda, qdb, qdc, qaa, qab, qac, qbb, qbc, qcc; @@ -449,7 +451,7 @@ ccl_device_inline void transform_compose(Transform *tfm, const DecomposedTransfo /* Interpolate from array of decomposed transforms. */ ccl_device void transform_motion_array_interpolate(Transform *tfm, - const ccl_global DecomposedTransform *motion, + const DecomposedTransform *motion, uint numsteps, float time) { @@ -458,8 +460,8 @@ ccl_device void transform_motion_array_interpolate(Transform *tfm, int step = min((int)(time * maxstep), maxstep - 1); float t = time * maxstep - step; - const ccl_global DecomposedTransform *a = motion + step; - const ccl_global DecomposedTransform *b = motion + step + 1; + const DecomposedTransform *a = motion + step; + const DecomposedTransform *b = motion + step + 1; /* Interpolate rotation, translation and scale. */ DecomposedTransform decomp; @@ -472,12 +474,12 @@ ccl_device void transform_motion_array_interpolate(Transform *tfm, transform_compose(tfm, &decomp); } -ccl_device_inline bool transform_isfinite_safe(Transform *tfm) +ccl_device_inline bool transform_isfinite_safe(ccl_private Transform *tfm) { return isfinite4_safe(tfm->x) && isfinite4_safe(tfm->y) && isfinite4_safe(tfm->z); } -ccl_device_inline bool transform_decomposed_isfinite_safe(DecomposedTransform *decomp) +ccl_device_inline bool transform_decomposed_isfinite_safe(ccl_private DecomposedTransform *decomp) { return isfinite4_safe(decomp->x) && isfinite4_safe(decomp->y) && isfinite4_safe(decomp->z) && isfinite4_safe(decomp->w); From 5e8775a8da93292a6d8ef0eed3b89dea40c94399 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 14 Oct 2021 18:02:09 +0200 Subject: [PATCH 0796/1500] Fix T92030: crash when hovering over socket This is the same fix as in rB24a965bb16c22e33752dfb6c22105b96a8649aeb. --- source/blender/nodes/geometry/nodes/node_geo_proximity.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index 30d025953af..02694a4a496 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -207,6 +207,7 @@ class ProximityFunction : public fn::MultiFunction { static void geo_node_proximity_exec(GeoNodeExecParams params) { GeometrySet geometry_set_target = params.extract_input("Target"); + geometry_set_target.ensure_owns_direct_data(); auto return_default = [&]() { params.set_output("Position", fn::make_constant_field({0.0f, 0.0f, 0.0f})); From b42ce0c54cab8ff5f85ca795cc1f0dab4308449b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 14 Oct 2021 11:06:18 -0500 Subject: [PATCH 0797/1500] Functions: Generic array data structure Sometimes it's useful to pass around a set of values with a generic type. The virtual array data structures allow this, but they don't have logical ownership. My initial use case for this is as a return type for the functions that interpolate curve attributes to evaluated points, but a need for this data structure has come up in a few other places as well. It also reduced the need for templates. Differential Revision: https://developer.blender.org/D11103 --- source/blender/functions/CMakeLists.txt | 2 + source/blender/functions/FN_generic_array.hh | 270 ++++++++++++++++++ .../functions/FN_generic_virtual_array.hh | 11 + .../functions/tests/FN_generic_array_test.cc | 118 ++++++++ 4 files changed, 401 insertions(+) create mode 100644 source/blender/functions/FN_generic_array.hh create mode 100644 source/blender/functions/tests/FN_generic_array_test.cc diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 309b92f1cb4..844e21f5e2c 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC FN_cpp_type.hh FN_cpp_type_make.hh + FN_generic_array.hh FN_field.hh FN_field_cpp_type.hh FN_generic_pointer.hh @@ -87,6 +88,7 @@ blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC tests/FN_cpp_type_test.cc + tests/FN_generic_array_test.cc tests/FN_field_test.cc tests/FN_generic_span_test.cc tests/FN_generic_vector_array_test.cc diff --git a/source/blender/functions/FN_generic_array.hh b/source/blender/functions/FN_generic_array.hh new file mode 100644 index 00000000000..401e496a66c --- /dev/null +++ b/source/blender/functions/FN_generic_array.hh @@ -0,0 +1,270 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup fn + * + * This is a generic counterpart to #blender::Array, used when the type is not known at runtime. + * + * `GArray` should generally only be used for passing data around in dynamic contexts. + * It does not support a few things that #blender::Array supports: + * - Small object optimization / inline buffer. + * - Exception safety and various more specific constructors. + */ + +#include "BLI_allocator.hh" + +#include "FN_cpp_type.hh" +#include "FN_generic_span.hh" + +namespace blender::fn { + +template< + /** + * The allocator used by this array. Should rarely be changed, except when you don't want that + * MEM_* functions are used internally. + */ + typename Allocator = GuardedAllocator> +class GArray { + protected: + /** The type of the data in the array, will be null after the array is default constructed, + * but a value should be assigned before any other interaction with the array. */ + const CPPType *type_ = nullptr; + void *data_ = nullptr; + int64_t size_ = 0; + + Allocator allocator_; + + public: + /** + * The default constructor creates an empty array, the only situation in which the type is + * allowed to be null. This default constructor exists so `GArray` can be used in containers, + * but the type should be supplied before doing anything else to the array. + */ + GArray(Allocator allocator = {}) noexcept : allocator_(allocator) + { + } + + GArray(NoExceptConstructor, Allocator allocator = {}) noexcept : GArray(allocator) + { + } + + /** + * Create and allocate a new array, with elements default constructed + * (which does not do anything for trivial types). + */ + GArray(const CPPType &type, int64_t size, Allocator allocator = {}) : GArray(type, allocator) + { + BLI_assert(size >= 0); + size_ = size; + data_ = this->allocate(size_); + type_->default_construct_n(data_, size_); + } + + /** + * Create an empty array with just a type. + */ + GArray(const CPPType &type, Allocator allocator = {}) : GArray(allocator) + { + type_ = &type; + } + + /** + * Take ownership of a buffer with a provided size. The buffer should be + * allocated with the same allocator provided to the constructor. + */ + GArray(const CPPType &type, void *buffer, int64_t size, Allocator allocator = {}) + : GArray(type, allocator) + { + BLI_assert(size >= 0); + BLI_assert(buffer != nullptr || size == 0); + BLI_assert(type_->pointer_has_valid_alignment(buffer)); + + data_ = buffer; + size_ = size; + } + + /** + * Create an array by copying values from a generic span. + */ + GArray(const GSpan span, Allocator allocator = {}) : GArray(span.type(), span.size(), allocator) + { + if (span.data() != nullptr) { + BLI_assert(span.size() != 0); + /* Use copy assign rather than construct since the memory is already initialized. */ + type_->copy_assign_n(span.data(), data_, size_); + } + } + + /** + * Create an array by copying values from another generic array. + */ + GArray(const GArray &other) : GArray(other.as_span(), other.allocator()) + { + } + + /** + * Create an array by taking ownership of another array's data, clearing the data in the other. + */ + GArray(GArray &&other) : GArray(other.type(), other.data(), other.size(), other.allocator()) + { + other.data_ = nullptr; + other.size_ = 0; + } + + ~GArray() + { + if (data_ != nullptr) { + type_->destruct_n(data_, size_); + this->deallocate(data_); + } + } + + GArray &operator=(const GArray &other) + { + return copy_assign_container(*this, other); + } + + GArray &operator=(GArray &&other) + { + return move_assign_container(*this, std::move(other)); + } + + const CPPType &type() const + { + BLI_assert(type_ != nullptr); + return *type_; + } + + bool is_empty() const + { + return size_ == 0; + } + + /** + * Return the number of elements in the array (not the size in bytes). + */ + int64_t size() const + { + return size_; + } + + /** + * Get a pointer to the beginning of the array. + */ + const void *data() const + { + return data_; + } + void *data() + { + return data_; + } + + const void *operator[](int64_t index) const + { + BLI_assert(index < size_); + return POINTER_OFFSET(data_, type_->size() * index); + } + + void *operator[](int64_t index) + { + BLI_assert(index < size_); + return POINTER_OFFSET(data_, type_->size() * index); + } + + operator GSpan() const + { + BLI_assert(type_ != nullptr); + return GSpan(*type_, data_, size_); + } + + operator GMutableSpan() + { + BLI_assert(type_ != nullptr); + return GMutableSpan(*type_, data_, size_); + } + + GSpan as_span() const + { + return *this; + } + + GMutableSpan as_mutable_span() + { + return *this; + } + + /** + * Access the allocator used by this array. + */ + Allocator &allocator() + { + return allocator_; + } + const Allocator &allocator() const + { + return allocator_; + } + + /** + * Destruct values and create a new array of the given size. The values in the new array are + * default constructed. + */ + void reinitialize(const int64_t new_size) + { + BLI_assert(new_size >= 0); + int64_t old_size = size_; + + type_->destruct_n(data_, size_); + size_ = 0; + + if (new_size <= old_size) { + type_->default_construct_n(data_, new_size); + } + else { + void *new_data = this->allocate(new_size); + try { + type_->default_construct_n(new_data, new_size); + } + catch (...) { + this->deallocate(new_data); + throw; + } + this->deallocate(data_); + data_ = new_data; + } + + size_ = new_size; + } + + private: + void *allocate(int64_t size) + { + const int64_t item_size = type_->size(); + const int64_t alignment = type_->alignment(); + return allocator_.allocate(static_cast(size) * item_size, alignment, AT); + } + + void deallocate(void *ptr) + { + allocator_.deallocate(ptr); + } +}; + +} // namespace blender::fn diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index 703118ba23e..8aad017e68b 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -27,6 +27,7 @@ #include "BLI_virtual_array.hh" +#include "FN_generic_array.hh" #include "FN_generic_span.hh" namespace blender::fn { @@ -398,6 +399,16 @@ template class GVArray_For_VArray : public GVArray { } }; +class GVArray_For_GArray : public GVArray_For_GSpan { + protected: + GArray<> array_; + + public: + GVArray_For_GArray(GArray<> array) : GVArray_For_GSpan(array.as_span()), array_(std::move(array)) + { + } +}; + /* Used to convert any generic virtual array into a typed one. */ template class VArray_For_GVArray : public VArray { protected: diff --git a/source/blender/functions/tests/FN_generic_array_test.cc b/source/blender/functions/tests/FN_generic_array_test.cc new file mode 100644 index 00000000000..417ab479cca --- /dev/null +++ b/source/blender/functions/tests/FN_generic_array_test.cc @@ -0,0 +1,118 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_array.hh" + +#include "FN_generic_array.hh" + +namespace blender::fn::tests { + +TEST(generic_array, TypeConstructor) +{ + GArray array(CPPType::get()); + EXPECT_TRUE(array.data() == nullptr); + EXPECT_EQ(array.size(), 0); + EXPECT_EQ(array.as_span().typed().size(), 0); + EXPECT_TRUE(array.is_empty()); +} + +TEST(generic_array, MoveConstructor) +{ + GArray array_a(CPPType::get(), (int64_t)10); + GMutableSpan span_a = array_a.as_mutable_span(); + MutableSpan typed_span_a = span_a.typed(); + typed_span_a.fill(42); + + const GArray array_b = std::move(array_a); + Span typed_span_b = array_b.as_span().typed(); + EXPECT_FALSE(array_b.data() == nullptr); + EXPECT_EQ(array_b.size(), 10); + EXPECT_EQ(typed_span_b[4], 42); + + /* Make sure the copy constructor cleaned up the original, but it shouldn't clear the type. */ + EXPECT_TRUE(array_a.data() == nullptr); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(array_a.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_TRUE(array_a.is_empty()); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(array_b.type(), array_a.type()); /* NOLINT: bugprone-use-after-move */ +} + +TEST(generic_array, CopyConstructor) +{ + GArray array_a(CPPType::get(), (int64_t)10); + GMutableSpan span_a = array_a.as_mutable_span(); + MutableSpan typed_span_a = span_a.typed(); + typed_span_a.fill(42); + + /* From span directly. */ + const GArray array_b = array_a.as_span(); + Span typed_span_b = array_b.as_span().typed(); + EXPECT_FALSE(array_b.data() == nullptr); + EXPECT_EQ(array_b.size(), 10); + EXPECT_EQ(typed_span_b[4], 42); + EXPECT_FALSE(array_a.is_empty()); + + /* From array. */ + const GArray array_c = array_a; + Span typed_span_c = array_c.as_span().typed(); + EXPECT_FALSE(array_c.data() == nullptr); + EXPECT_EQ(array_c.size(), 10); + EXPECT_EQ(typed_span_c[4], 42); + EXPECT_FALSE(array_a.is_empty()); +} + +TEST(generic_array, BufferAndSizeConstructor) +{ + int32_t *values = (int32_t *)MEM_malloc_arrayN(12, sizeof(int32_t), __func__); + void *buffer = (void *)values; + GArray array(CPPType::get(), buffer, 4); + EXPECT_FALSE(array.data() == nullptr); + EXPECT_EQ(array.size(), 4); + EXPECT_FALSE(array.is_empty()); + EXPECT_EQ(array.as_span().typed().size(), 4); + EXPECT_EQ(array[0], &values[0]); + EXPECT_EQ(array[1], &values[1]); + EXPECT_EQ(array[2], &values[2]); + EXPECT_EQ(array[3], &values[3]); +} + +TEST(generic_array, Reinitialize) +{ + GArray array(CPPType::get(), (int64_t)5); + EXPECT_FALSE(array.data() == nullptr); + GMutableSpan span = array.as_mutable_span(); + MutableSpan typed_span = span.typed(); + typed_span.fill(77); + EXPECT_FALSE(typed_span.data() == nullptr); + typed_span[2] = 8; + EXPECT_EQ(array[2], &typed_span[2]); + EXPECT_EQ(typed_span[0], 77); + EXPECT_EQ(typed_span[1], 77); + + array.reinitialize(10); + EXPECT_EQ(array.size(), 10); + span = array.as_mutable_span(); + EXPECT_EQ(span.size(), 10); + + typed_span = span.typed(); + EXPECT_FALSE(typed_span.data() == nullptr); + + array.reinitialize(0); + EXPECT_EQ(array.size(), 0); +} + +TEST(generic_array, InContainer) +{ + blender::Array> arrays; + for (GArray<> &array : arrays) { + array = GArray(CPPType::get(), (int64_t)5); + array.as_mutable_span().typed().fill(55); + } + for (GArray<> &array : arrays) { + EXPECT_EQ(array.as_span().typed()[3], 55); + } +} + +} // namespace blender::fn::tests From 5e12e62a6a4e333d0d93d50b0dc943e2d19ee3e5 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 14 Oct 2021 13:03:26 -0300 Subject: [PATCH 0798/1500] Fix regression with incremental snap in Graph Editor Regression introduced in {rBb0d9e6797fb8}. Previously the Graphics Editor had a conflict with two different snap types. Auto-Snap and Snap with Ctrl. It is now clearer which snap should prevail. --- source/blender/editors/transform/transform_snap.c | 2 +- .../editors/transform/transform_snap_animation.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index b8a35cb51e3..5eab059e049 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -615,7 +615,7 @@ static void initSnappingMode(TransInfo *t) t->tsnap.mode |= SCE_SNAP_MODE_GRID; } } - else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) { + else if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA)) { /* No incremental snapping. */ t->tsnap.mode = 0; } diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c index 08335924ddf..93ae68857a4 100644 --- a/source/blender/editors/transform/transform_snap_animation.c +++ b/source/blender/editors/transform/transform_snap_animation.c @@ -56,10 +56,15 @@ short getAnimEdit_SnapMode(TransInfo *t) } } else if (t->spacetype == SPACE_GRAPH) { - SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; + if ((t->data_type == TFM_TRANSLATION) && activeSnap(t)) { + /* Use the translate mode snap. */ + } + else { + SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; - if (sipo) { - autosnap = sipo->autosnap; + if (sipo) { + autosnap = sipo->autosnap; + } } } else if (t->spacetype == SPACE_NLA) { From 22892955553e86b393dd6e6d74b2b13690c1d36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 14 Oct 2021 18:16:22 +0200 Subject: [PATCH 0799/1500] GPUTexture: Fix assert when using stereo viewport with EEVEE Stereo viewport means the depth buffer is use twice as often as a framebuffer attachment. --- source/blender/draw/engines/eevee/eevee_engine.c | 3 +++ source/blender/gpu/intern/gpu_texture_private.hh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index f8e1cc9c923..68975cff48c 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -468,6 +468,9 @@ static void eevee_render_to_image(void *vedata, g_data->render_sample_count_per_timestep = EEVEE_temporal_sampling_sample_count_get(scene, ved->stl); + /* Reset in case the same engine is used on multiple views. */ + EEVEE_temporal_sampling_reset(vedata); + /* Compute start time. The motion blur will cover `[time ...time + shuttertime]`. */ float time = initial_frame + initial_subframe; switch (scene->eevee.motion_blur_position) { diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index 26be6f57312..19d83366e6f 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -66,7 +66,7 @@ ENUM_OPERATORS(eGPUTextureType, GPU_TEXTURE_CUBE_ARRAY) #endif /* Maximum number of FBOs a texture can be attached to. */ -#define GPU_TEX_MAX_FBO_ATTACHED 16 +#define GPU_TEX_MAX_FBO_ATTACHED 32 /** * Implementation of Textures. From 328b6f672bce989a25f139df121b3b89dd560671 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 14 Oct 2021 13:26:35 -0300 Subject: [PATCH 0800/1500] Fix error in rB5e12e62a6a4e --- source/blender/editors/transform/transform_snap_animation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c index 93ae68857a4..d0d8f739b80 100644 --- a/source/blender/editors/transform/transform_snap_animation.c +++ b/source/blender/editors/transform/transform_snap_animation.c @@ -56,7 +56,7 @@ short getAnimEdit_SnapMode(TransInfo *t) } } else if (t->spacetype == SPACE_GRAPH) { - if ((t->data_type == TFM_TRANSLATION) && activeSnap(t)) { + if ((t->mode == TFM_TRANSLATION) && activeSnap(t)) { /* Use the translate mode snap. */ } else { From 497d0400bdcd9fc932b7a875c5364934b1a96b07 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 14 Oct 2021 13:31:17 -0300 Subject: [PATCH 0801/1500] Fix another error in rB5e12e62a6a4e The code was ignoring the icremental with small distances. --- source/blender/editors/transform/transform_snap_animation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c index d0d8f739b80..6856dcd9586 100644 --- a/source/blender/editors/transform/transform_snap_animation.c +++ b/source/blender/editors/transform/transform_snap_animation.c @@ -57,7 +57,7 @@ short getAnimEdit_SnapMode(TransInfo *t) } else if (t->spacetype == SPACE_GRAPH) { if ((t->mode == TFM_TRANSLATION) && activeSnap(t)) { - /* Use the translate mode snap. */ + return autosnap; } else { SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; From 1996efe7aa2e0edc9887ad34bc59e2ab911e2d02 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 1 Oct 2021 15:30:12 +0300 Subject: [PATCH 0802/1500] Python API: implement `PoseBone.children` via `Bone.children`. Currently `PoseBone.children` is implemented by a linear scan of the list of armature bones. This is doubly inefficient, since not only is it scanning all bones, the `obj.data.bones` list is actually synthetic and generated from Bone children lists. Instead, use the `Bone.children` native RNA property. Differential Revision: https://developer.blender.org/D12727 --- release/scripts/modules/bpy_types.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index 8a1615ad99f..26efb6e3307 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -378,10 +378,9 @@ class PoseBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup): def children(self): obj = self.id_data pbones = obj.pose.bones - self_bone = self.bone - return tuple(pbones[bone.name] for bone in obj.data.bones - if bone.parent == self_bone) + # Use Bone.children, which is a native RNA property. + return tuple(pbones[bone.name] for bone in self.bone.children) class Bone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup): From 17b8da719606abfc9e3076555c626e6fc38dd7c5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 14 Oct 2021 12:06:48 -0500 Subject: [PATCH 0803/1500] Geometry Nodes: Field version of mesh to curve node This commit adds a fields version of the mesh to curve node, with a field for the input selection. In order to reduce code duplication, it adds the mesh to curve conversion to the new geometry module and calls that implementation from both places. More details on the geometry module can be found here: T86869 Differential Revision: https://developer.blender.org/D12579 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/CMakeLists.txt | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/geometry/CMakeLists.txt | 42 +++ source/blender/geometry/GEO_mesh_to_curve.hh | 35 +++ .../geometry/intern/mesh_to_curve_convert.cc | 283 +++++++++++++++++ source/blender/nodes/CMakeLists.txt | 3 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/legacy/node_geo_mesh_to_curve.cc | 285 ++---------------- .../geometry/nodes/node_geo_mesh_to_curve.cc | 69 +++++ 12 files changed, 463 insertions(+), 260 deletions(-) create mode 100644 source/blender/geometry/CMakeLists.txt create mode 100644 source/blender/geometry/GEO_mesh_to_curve.hh create mode 100644 source/blender/geometry/intern/mesh_to_curve_convert.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index b2faff56656..d4885f7bad8 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -145,6 +145,7 @@ def mesh_node_items(context): yield NodeItem("GeometryNodeEdgeSplit") yield NodeItem("GeometryNodeBoolean") + yield NodeItem("GeometryNodeMeshToCurve") yield NodeItem("GeometryNodeMeshToPoints") yield NodeItem("GeometryNodeMeshSubdivide") yield NodeItem("GeometryNodeTriangulate") diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index fbc0ec440cf..84d31bccc53 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -119,6 +119,7 @@ add_subdirectory(blenloader) add_subdirectory(depsgraph) add_subdirectory(ikplugin) add_subdirectory(simulation) +add_subdirectory(geometry) add_subdirectory(gpu) add_subdirectory(imbuf) add_subdirectory(nodes) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 19215e75d95..9429da9d6a0 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1536,6 +1536,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_SCALE_INSTANCES 1121 #define GEO_NODE_ROTATE_INSTANCES 1122 #define GEO_NODE_EDGE_SPLIT 1123 +#define GEO_NODE_MESH_TO_CURVE 1124 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index cb51249ac27..e526475fa95 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5714,6 +5714,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_attribute_randomize(); register_node_type_geo_legacy_delete_geometry(); register_node_type_geo_legacy_material_assign(); + register_node_type_geo_legacy_mesh_to_curve(); register_node_type_geo_legacy_points_to_volume(); register_node_type_geo_legacy_select_by_material(); register_node_type_geo_legacy_curve_spline_type(); diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt new file mode 100644 index 00000000000..4e7e0b1ea58 --- /dev/null +++ b/source/blender/geometry/CMakeLists.txt @@ -0,0 +1,42 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2006, Blender Foundation +# All rights reserved. + +set(INC + . + ../blenkernel + ../blenlib + ../blentranslation + ../functions + ../makesdna + ../makesrna + ../../../intern/guardedalloc + ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern +) + +set(SRC + intern/mesh_to_curve_convert.cc + GEO_mesh_to_curve.hh +) + +set(LIB + bf_blenkernel + bf_blenlib +) + +blender_add_lib(bf_geometry "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/geometry/GEO_mesh_to_curve.hh b/source/blender/geometry/GEO_mesh_to_curve.hh new file mode 100644 index 00000000000..66459ab79a9 --- /dev/null +++ b/source/blender/geometry/GEO_mesh_to_curve.hh @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_index_mask.hh" + +#include "BKE_spline.hh" + +#pragma once + +struct Mesh; +class MeshComponent; + +/** \file + * \ingroup geo + */ + +namespace blender::geometry { + +std::unique_ptr mesh_to_curve_convert(const MeshComponent &mesh_component, + const IndexMask selection); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc new file mode 100644 index 00000000000..64b9fa5c01d --- /dev/null +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -0,0 +1,283 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_set.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_access.hh" +#include "BKE_attribute_math.hh" +#include "BKE_geometry_set.hh" +#include "BKE_spline.hh" + +#include "GEO_mesh_to_curve.hh" + +namespace blender::geometry { + +template +static void copy_attribute_to_points(const VArray &source_data, + Span map, + MutableSpan dest_data) +{ + for (const int point_index : map.index_range()) { + const int vert_index = map[point_index]; + dest_data[point_index] = source_data[vert_index]; + } +} + +static void copy_attributes_to_points(CurveEval &curve, + const MeshComponent &mesh_component, + Span> point_to_vert_maps) +{ + MutableSpan splines = curve.splines(); + Set source_attribute_ids = mesh_component.attribute_ids(); + + /* Copy builtin control point attributes. */ + if (source_attribute_ids.contains("tilt")) { + const fn::GVArray_Typed tilt_attribute = mesh_component.attribute_get_for_read( + "tilt", ATTR_DOMAIN_POINT, 0.0f); + threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points( + *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); + } + }); + source_attribute_ids.remove_contained("tilt"); + } + if (source_attribute_ids.contains("radius")) { + const fn::GVArray_Typed radius_attribute = mesh_component.attribute_get_for_read( + "radius", ATTR_DOMAIN_POINT, 1.0f); + threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points( + *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); + } + }); + source_attribute_ids.remove_contained("radius"); + } + + for (const bke::AttributeIDRef &attribute_id : source_attribute_ids) { + /* Don't copy attributes that are built-in on meshes but not on curves. */ + if (mesh_component.attribute_is_builtin(attribute_id)) { + continue; + } + + /* Don't copy anonymous attributes with no references anymore. */ + if (attribute_id.is_anonymous()) { + const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); + if (!BKE_anonymous_attribute_id_has_strong_references(&anonymous_id)) { + continue; + } + } + + const fn::GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read( + attribute_id, ATTR_DOMAIN_POINT); + /* Some attributes might not exist if they were builtin attribute on domains that don't + * have any elements, i.e. a face attribute on the output of the line primitive node. */ + if (!mesh_attribute) { + continue; + } + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + /* Create attribute on the spline points. */ + splines[i]->attributes.create(attribute_id, data_type); + std::optional spline_attribute = splines[i]->attributes.get_for_write( + attribute_id); + BLI_assert(spline_attribute); + + /* Copy attribute based on the map for this spline. */ + attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_attribute_to_points( + mesh_attribute->typed(), point_to_vert_maps[i], spline_attribute->typed()); + }); + } + }); + } + + curve.assert_valid_point_attributes(); +} + +struct CurveFromEdgesOutput { + std::unique_ptr curve; + Vector> point_to_vert_maps; +}; + +static CurveFromEdgesOutput edges_to_curve(Span verts, Span> edges) +{ + std::unique_ptr curve = std::make_unique(); + Vector> point_to_vert_maps; + + /* Compute the number of edges connecting to each vertex. */ + Array neighbor_count(verts.size(), 0); + for (const std::pair &edge : edges) { + neighbor_count[edge.first]++; + neighbor_count[edge.second]++; + } + + /* Compute an offset into the array of neighbor edges based on the counts. */ + Array neighbor_offsets(verts.size()); + int start = 0; + for (const int i : verts.index_range()) { + neighbor_offsets[i] = start; + start += neighbor_count[i]; + } + + /* Use as an index into the "neighbor group" for each vertex. */ + Array used_slots(verts.size(), 0); + /* Calculate the indices of each vertex's neighboring edges. */ + Array neighbors(edges.size() * 2); + for (const int i : edges.index_range()) { + const int v1 = edges[i].first; + const int v2 = edges[i].second; + neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2; + neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1; + used_slots[v1]++; + used_slots[v2]++; + } + + /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */ + Array unused_edges = std::move(used_slots); + + for (const int start_vert : verts.index_range()) { + /* The vertex will be part of a cyclic spline. */ + if (neighbor_count[start_vert] == 2) { + continue; + } + + /* The vertex has no connected edges, or they were already used. */ + if (unused_edges[start_vert] == 0) { + continue; + } + + for (const int i : IndexRange(neighbor_count[start_vert])) { + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert] + i]; + + if (unused_edges[next_vert] == 0) { + continue; + } + + std::unique_ptr spline = std::make_unique(); + Vector point_to_vert_map; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we read a vertex with more than two connected edges. */ + while (true) { + int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + if (neighbor_count[current_vert] != 2) { + break; + } + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + } + + /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ + for (const int start_vert : verts.index_range()) { + if (unused_edges[start_vert] != 2) { + continue; + } + + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert]]; + + std::unique_ptr spline = std::make_unique(); + Vector point_to_vert_map; + spline->set_cyclic(true); + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we loop back to the start vertex. */ + while (next_vert != start_vert) { + const int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + + curve->attributes.reallocate(curve->splines().size()); + return {std::move(curve), std::move(point_to_vert_maps)}; +} + +/** + * Get a separate array of the indices for edges in a selection (a boolean attribute). + * This helps to make the above algorithm simpler by removing the need to check for selection + * in many places. + */ +static Vector> get_selected_edges(const Mesh &mesh, const IndexMask selection) +{ + Vector> selected_edges; + for (const int i : selection) { + selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); + } + return selected_edges; +} + +/** + * Convert the mesh into one or many poly splines. Since splines cannot have branches, + * intersections of more than three edges will become breaks in splines. Attributes that + * are not built-in on meshes and not curves are transferred to the result curve. + */ +std::unique_ptr mesh_to_curve_convert(const MeshComponent &mesh_component, + const IndexMask selection) +{ + const Mesh &mesh = *mesh_component.get_for_read(); + Vector> selected_edges = get_selected_edges(*mesh_component.get_for_read(), + selection); + CurveFromEdgesOutput output = edges_to_curve({mesh.mvert, mesh.totvert}, selected_edges); + copy_attributes_to_points(*output.curve, mesh_component, output.point_to_vert_maps); + return std::move(output.curve); +} + +} // namespace blender::geometry diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index f748c89c005..850c5fbffff 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -33,6 +33,7 @@ set(INC ../bmesh ../depsgraph ../functions + ../geometry ../gpu ../imbuf ../makesdna @@ -248,6 +249,7 @@ set(SRC geometry/nodes/node_geo_mesh_primitive_line.cc geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc geometry/nodes/node_geo_mesh_subdivide.cc + geometry/nodes/node_geo_mesh_to_curve.cc geometry/nodes/node_geo_mesh_to_points.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_points_to_vertices.cc @@ -446,6 +448,7 @@ set(SRC set(LIB bf_bmesh bf_functions + bf_geometry bf_intern_sky ) diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d34acd6e9aa..3b78912fbaa 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -34,6 +34,7 @@ void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); void register_node_type_geo_legacy_delete_geometry(void); void register_node_type_geo_legacy_material_assign(void); +void register_node_type_geo_legacy_mesh_to_curve(void); void register_node_type_geo_legacy_points_to_volume(void); void register_node_type_geo_legacy_select_by_material(void); void register_node_type_geo_legacy_curve_spline_type(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index b5640207d04..f3435079563 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -376,6 +376,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Subdivide Mesh", "") +DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc index 11349dc7d42..7a27e856cef 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc @@ -14,267 +14,20 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BLI_array.hh" -#include "BLI_task.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_attribute_math.hh" -#include "BKE_spline.hh" +#include "GEO_mesh_to_curve.hh" #include "node_geometry_util.hh" -using blender::Array; - namespace blender::nodes { -static void geo_node_mesh_to_curve_declare(NodeDeclarationBuilder &b) +static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b) { b.add_input("Mesh"); b.add_input("Selection"); b.add_output("Curve"); } -template -static void copy_attribute_to_points(const VArray &source_data, - Span map, - MutableSpan dest_data) -{ - for (const int point_index : map.index_range()) { - const int vert_index = map[point_index]; - dest_data[point_index] = source_data[vert_index]; - } -} - -static void copy_attributes_to_points(CurveEval &curve, - const MeshComponent &mesh_component, - Span> point_to_vert_maps) -{ - MutableSpan splines = curve.splines(); - Set source_attribute_ids = mesh_component.attribute_ids(); - - /* Copy builtin control point attributes. */ - if (source_attribute_ids.contains("tilt")) { - const GVArray_Typed tilt_attribute = mesh_component.attribute_get_for_read( - "tilt", ATTR_DOMAIN_POINT, 0.0f); - threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points( - *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); - } - }); - source_attribute_ids.remove_contained("tilt"); - } - if (source_attribute_ids.contains("radius")) { - const GVArray_Typed radius_attribute = mesh_component.attribute_get_for_read( - "radius", ATTR_DOMAIN_POINT, 1.0f); - threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points( - *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); - } - }); - source_attribute_ids.remove_contained("radius"); - } - - /* Don't copy other builtin control point attributes. */ - source_attribute_ids.remove("position"); - - /* Copy dynamic control point attributes. */ - for (const AttributeIDRef &attribute_id : source_attribute_ids) { - const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id, - ATTR_DOMAIN_POINT); - /* Some attributes might not exist if they were builtin attribute on domains that don't - * have any elements, i.e. a face attribute on the output of the line primitive node. */ - if (!mesh_attribute) { - continue; - } - - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - /* Create attribute on the spline points. */ - splines[i]->attributes.create(attribute_id, data_type); - std::optional spline_attribute = splines[i]->attributes.get_for_write( - attribute_id); - BLI_assert(spline_attribute); - - /* Copy attribute based on the map for this spline. */ - attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { - using T = decltype(dummy); - copy_attribute_to_points( - mesh_attribute->typed(), point_to_vert_maps[i], spline_attribute->typed()); - }); - } - }); - } - - curve.assert_valid_point_attributes(); -} - -struct CurveFromEdgesOutput { - std::unique_ptr curve; - Vector> point_to_vert_maps; -}; - -static CurveFromEdgesOutput mesh_to_curve(Span verts, Span> edges) -{ - std::unique_ptr curve = std::make_unique(); - Vector> point_to_vert_maps; - - /* Compute the number of edges connecting to each vertex. */ - Array neighbor_count(verts.size(), 0); - for (const std::pair &edge : edges) { - neighbor_count[edge.first]++; - neighbor_count[edge.second]++; - } - - /* Compute an offset into the array of neighbor edges based on the counts. */ - Array neighbor_offsets(verts.size()); - int start = 0; - for (const int i : verts.index_range()) { - neighbor_offsets[i] = start; - start += neighbor_count[i]; - } - - /* Use as an index into the "neighbor group" for each vertex. */ - Array used_slots(verts.size(), 0); - /* Calculate the indices of each vertex's neighboring edges. */ - Array neighbors(edges.size() * 2); - for (const int i : edges.index_range()) { - const int v1 = edges[i].first; - const int v2 = edges[i].second; - neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2; - neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1; - used_slots[v1]++; - used_slots[v2]++; - } - - /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */ - Array unused_edges = std::move(used_slots); - - for (const int start_vert : verts.index_range()) { - /* The vertex will be part of a cyclic spline. */ - if (neighbor_count[start_vert] == 2) { - continue; - } - - /* The vertex has no connected edges, or they were already used. */ - if (unused_edges[start_vert] == 0) { - continue; - } - - for (const int i : IndexRange(neighbor_count[start_vert])) { - int current_vert = start_vert; - int next_vert = neighbors[neighbor_offsets[current_vert] + i]; - - if (unused_edges[next_vert] == 0) { - continue; - } - - std::unique_ptr spline = std::make_unique(); - Vector point_to_vert_map; - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - - /* Follow connected edges until we read a vertex with more than two connected edges. */ - while (true) { - int last_vert = current_vert; - current_vert = next_vert; - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - unused_edges[current_vert]--; - unused_edges[last_vert]--; - - if (neighbor_count[current_vert] != 2) { - break; - } - - const int offset = neighbor_offsets[current_vert]; - const int next_a = neighbors[offset]; - const int next_b = neighbors[offset + 1]; - next_vert = (last_vert == next_a) ? next_b : next_a; - } - - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - point_to_vert_maps.append(std::move(point_to_vert_map)); - } - } - - /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ - for (const int start_vert : verts.index_range()) { - if (unused_edges[start_vert] != 2) { - continue; - } - - int current_vert = start_vert; - int next_vert = neighbors[neighbor_offsets[current_vert]]; - - std::unique_ptr spline = std::make_unique(); - Vector point_to_vert_map; - spline->set_cyclic(true); - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - - /* Follow connected edges until we loop back to the start vertex. */ - while (next_vert != start_vert) { - const int last_vert = current_vert; - current_vert = next_vert; - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - unused_edges[current_vert]--; - unused_edges[last_vert]--; - - const int offset = neighbor_offsets[current_vert]; - const int next_a = neighbors[offset]; - const int next_b = neighbors[offset + 1]; - next_vert = (last_vert == next_a) ? next_b : next_a; - } - - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - point_to_vert_maps.append(std::move(point_to_vert_map)); - } - - curve->attributes.reallocate(curve->splines().size()); - return {std::move(curve), std::move(point_to_vert_maps)}; -} - -/** - * Get a separate array of the indices for edges in a selection (a boolean attribute). - * This helps to make the above algorithm simpler by removing the need to check for selection - * in many places. - */ -static Vector> get_selected_edges(GeoNodeExecParams params, - const MeshComponent &component) -{ - const Mesh &mesh = *component.get_for_read(); - const std::string selection_name = params.extract_input("Selection"); - if (!selection_name.empty() && !component.attribute_exists(selection_name)) { - params.error_message_add(NodeWarningType::Error, - TIP_("No attribute with name \"") + selection_name + "\""); - } - GVArray_Typed selection = component.attribute_get_for_read( - selection_name, ATTR_DOMAIN_EDGE, true); - - Vector> selected_edges; - for (const int i : IndexRange(mesh.totedge)) { - if (selection[i]) { - selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); - } - } - - return selected_edges; -} - -static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) +static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Mesh"); @@ -286,29 +39,41 @@ static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) } const MeshComponent &component = *geometry_set.get_component_for_read(); - const Mesh &mesh = *component.get_for_read(); - Span verts = Span{mesh.mvert, mesh.totvert}; - Vector> selected_edges = get_selected_edges(params, component); - if (selected_edges.size() == 0) { + const std::string selection_name = params.extract_input("Selection"); + if (!selection_name.empty() && !component.attribute_exists(selection_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + selection_name + "\""); + } + GVArray_Typed selection = component.attribute_get_for_read( + selection_name, ATTR_DOMAIN_EDGE, true); + + Vector selected_edge_indices; + for (const int64_t i : IndexRange(component.attribute_domain_size(ATTR_DOMAIN_EDGE))) { + if (selection[i]) { + selected_edge_indices.append(i); + } + } + + if (selected_edge_indices.size() == 0) { params.set_output("Curve", GeometrySet()); return; } - CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges); - copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps); + std::unique_ptr curve = geometry::mesh_to_curve_convert( + component, IndexMask(selected_edge_indices)); - params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release())); + params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); } } // namespace blender::nodes -void register_node_type_geo_mesh_to_curve() +void register_node_type_geo_legacy_mesh_to_curve() { static bNodeType ntype; geo_node_type_base( &ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_mesh_to_curve_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; + ntype.declare = blender::nodes::geo_node_legacy_mesh_to_curve_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_mesh_to_curve_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc new file mode 100644 index 00000000000..7bca9ec141b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -0,0 +1,69 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "GEO_mesh_to_curve.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Mesh"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_output("Curve"); +} + +static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Mesh"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + const MeshComponent &component = *geometry_set.get_component_for_read(); + GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)}; + evaluator.add(params.get_input>("Selection")); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + if (selection.size() == 0) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + std::unique_ptr curve = geometry::mesh_to_curve_convert(component, selection); + geometry_set.replace_curve(curve.release()); + geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_to_curve() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_legacy_mesh_to_curve_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_mesh_to_curve_exec; + nodeRegisterType(&ntype); +} From 42a05ff6ea2a39d1be47143cf5bcb58c53af7a05 Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Thu, 14 Oct 2021 12:06:42 -0500 Subject: [PATCH 0804/1500] Geometry Nodes: Rename Nodes ID Names + Menu Org Re-alphabetize the main add menu. Rename Node ID Names: FloatCompare => CompareFloats AttributeCapture => CaptureAttribute Boolean => MeshBoolean CurveFill => FillCurve CurveFillet => FilletCurve CurveReverse => ReverseCurve CurveSample => SampleCurve CurveResmaple => ResampleCurve CurveSubdivide => SubdivideCurve CurveTrim => TrimCurve MaterialReplace => ReplaceMaterial MeshSubdivide => SubdivideMesh EdgeSplit => SplitEdges Differential Revision: https://developer.blender.org/D12865 --- release/scripts/startup/nodeitems_builtins.py | 39 ++++++------- source/blender/blenkernel/BKE_node.h | 28 +++++----- .../blenloader/intern/versioning_290.c | 4 +- .../blenloader/intern/versioning_300.c | 56 ++++++++++--------- .../blenloader/intern/versioning_common.cc | 14 +++++ .../blenloader/intern/versioning_common.h | 2 + source/blender/nodes/NOD_static_types.h | 26 ++++----- .../function/nodes/node_fn_float_compare.cc | 2 +- .../nodes/node_geo_attribute_capture.cc | 2 +- .../nodes/geometry/nodes/node_geo_boolean.cc | 2 +- .../geometry/nodes/node_geo_curve_fill.cc | 2 +- .../geometry/nodes/node_geo_curve_fillet.cc | 2 +- .../geometry/nodes/node_geo_curve_resample.cc | 2 +- .../geometry/nodes/node_geo_curve_reverse.cc | 2 +- .../geometry/nodes/node_geo_curve_sample.cc | 2 +- .../nodes/node_geo_curve_subdivide.cc | 2 +- .../geometry/nodes/node_geo_curve_trim.cc | 2 +- .../geometry/nodes/node_geo_edge_split.cc | 2 +- .../nodes/node_geo_material_replace.cc | 2 +- .../geometry/nodes/node_geo_mesh_subdivide.cc | 2 +- 20 files changed, 108 insertions(+), 87 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index d4885f7bad8..b26454e0313 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -103,13 +103,13 @@ def curve_node_items(context): yield NodeItem("GeometryNodeCurveLength") yield NodeItem("GeometryNodeCurveToMesh") - yield NodeItem("GeometryNodeCurveFill") - yield NodeItem("GeometryNodeCurveFillet") - yield NodeItem("GeometryNodeCurveResample") - yield NodeItem("GeometryNodeCurveReverse") - yield NodeItem("GeometryNodeCurveSample") - yield NodeItem("GeometryNodeCurveSubdivide") - yield NodeItem("GeometryNodeCurveTrim") + yield NodeItem("GeometryNodeFillCurve") + yield NodeItem("GeometryNodeFilletCurve") + yield NodeItem("GeometryNodeResampleCurve") + yield NodeItem("GeometryNodeReverseCurve") + yield NodeItem("GeometryNodeSampleCurve") + yield NodeItem("GeometryNodeSubdivideCurve") + yield NodeItem("GeometryNodeTrimCurve") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeInputCurveHandlePositions") yield NodeItem("GeometryNodeCurveParameter") @@ -144,10 +144,11 @@ def mesh_node_items(context): yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeEdgeSplit") - yield NodeItem("GeometryNodeBoolean") + yield NodeItem("GeometryNodeMeshBoolean") yield NodeItem("GeometryNodeMeshToCurve") yield NodeItem("GeometryNodeMeshToPoints") - yield NodeItem("GeometryNodeMeshSubdivide") + yield NodeItem("GeometryNodeSplitEdges") + yield NodeItem("GeometryNodeSubdivideMesh") yield NodeItem("GeometryNodeTriangulate") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeInputShadeSmooth") @@ -197,7 +198,7 @@ def geometry_material_node_items(context): yield NodeItem("GeometryNodeLegacySelectByMaterial") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) - yield NodeItem("GeometryNodeMaterialReplace") + yield NodeItem("GeometryNodeReplaceMaterial") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeInputMaterialIndex") yield NodeItem("GeometryNodeMaterialSelection") @@ -651,7 +652,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_legacy_poll), NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeAttributeCapture"), + NodeItem("GeometryNodeCaptureAttribute"), NodeItem("GeometryNodeAttributeStatistic"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ @@ -705,6 +706,9 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshLine"), NodeItem("GeometryNodeMeshUVSphere"), ]), + GeometryNodeCategory("GEO_OUTPUT", "Output", items=[ + NodeItem("GeometryNodeViewer"), + ]), GeometryNodeCategory("GEO_POINT", "Point", items=point_node_items), GeometryNodeCategory("GEO_TEXT", "Text", items=[ NodeItem("FunctionNodeStringLength"), @@ -714,6 +718,10 @@ geometry_node_categories = [ NodeItem("FunctionNodeInputSpecialCharacters"), NodeItem("GeometryNodeStringToCurves"), ]), + GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ + NodeItem("ShaderNodeTexNoise"), + NodeItem("ShaderNodeTexWhiteNoise"), + ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), NodeItem("ShaderNodeFloatCurve"), @@ -721,16 +729,12 @@ geometry_node_categories = [ NodeItem("ShaderNodeMath"), NodeItem("FunctionNodeBooleanMath"), NodeItem("FunctionNodeRotateEuler"), - NodeItem("FunctionNodeFloatCompare"), + NodeItem("FunctionNodeCompareFloats"), NodeItem("FunctionNodeFloatToInt"), NodeItem("GeometryNodeSwitch"), NodeItem("FunctionNodeRandomValue"), NodeItem("FunctionNodeAlignEulerToVector"), ]), - GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ - NodeItem("ShaderNodeTexNoise"), - NodeItem("ShaderNodeTexWhiteNoise"), - ]), GeometryNodeCategory("GEO_VECTOR", "Vector", items=[ NodeItem("ShaderNodeVectorCurve"), NodeItem("ShaderNodeSeparateXYZ"), @@ -738,9 +742,6 @@ geometry_node_categories = [ NodeItem("ShaderNodeVectorMath"), NodeItem("ShaderNodeVectorRotate"), ]), - GeometryNodeCategory("GEO_OUTPUT", "Output", items=[ - NodeItem("GeometryNodeViewer"), - ]), GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_legacy_poll), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 9429da9d6a0..f352fa37eab 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -897,7 +897,7 @@ bool BKE_node_is_connected_to_output(struct bNodeTree *ntree, struct bNode *node /* ************** COMMON NODES *************** */ #define NODE_UNDEFINED -2 /* node type is not registered */ -#define NODE_CUSTOM -1 /* for dynamically registered custom types */ +#define NODE_CUSTOM -1 /* for dynamically registered custom types */ #define NODE_GROUP 2 // #define NODE_FORLOOP 3 /* deprecated */ // #define NODE_WHILELOOP 4 /* deprecated */ @@ -1415,7 +1415,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_TRIANGULATE 1000 #define GEO_NODE_LEGACY_EDGE_SPLIT 1001 #define GEO_NODE_TRANSFORM 1002 -#define GEO_NODE_BOOLEAN 1003 +#define GEO_NODE_MESH_BOOLEAN 1003 #define GEO_NODE_LEGACY_POINT_DISTRIBUTE 1004 #define GEO_NODE_LEGACY_POINT_INSTANCE 1005 #define GEO_NODE_LEGACY_SUBDIVISION_SURFACE 1006 @@ -1441,7 +1441,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_VOLUME_TO_MESH 1026 #define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027 #define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028 -#define GEO_NODE_MESH_SUBDIVIDE 1029 +#define GEO_NODE_SUBDIVIDE_MESH 1029 #define GEO_NODE_ATTRIBUTE_REMOVE 1030 #define GEO_NODE_LEGACY_ATTRIBUTE_CONVERT 1031 #define GEO_NODE_MESH_PRIMITIVE_CUBE 1032 @@ -1459,11 +1459,11 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER 1044 #define GEO_NODE_CURVE_TO_MESH 1045 #define GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP 1046 -#define GEO_NODE_CURVE_RESAMPLE 1047 +#define GEO_NODE_RESAMPLE_CURVE 1047 #define GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE 1048 #define GEO_NODE_LEGACY_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 -#define GEO_NODE_MATERIAL_REPLACE 1051 +#define GEO_NODE_REPLACE_MATERIAL 1051 #define GEO_NODE_LEGACY_MESH_TO_CURVE 1052 #define GEO_NODE_LEGACY_DELETE_GEOMETRY 1053 #define GEO_NODE_CURVE_LENGTH 1054 @@ -1483,33 +1483,33 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_PRIMITIVE_LINE 1068 #define GEO_NODE_LEGACY_CURVE_ENDPOINTS 1069 #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 -#define GEO_NODE_CURVE_TRIM 1071 +#define GEO_NODE_TRIM_CURVE 1071 #define GEO_NODE_LEGACY_CURVE_SET_HANDLES 1072 #define GEO_NODE_LEGACY_CURVE_SPLINE_TYPE 1073 #define GEO_NODE_LEGACY_CURVE_SELECT_HANDLES 1074 -#define GEO_NODE_CURVE_FILL 1075 +#define GEO_NODE_FILL_CURVE 1075 #define GEO_NODE_INPUT_POSITION 1076 #define GEO_NODE_SET_POSITION 1077 #define GEO_NODE_INPUT_INDEX 1078 #define GEO_NODE_INPUT_NORMAL 1079 -#define GEO_NODE_ATTRIBUTE_CAPTURE 1080 +#define GEO_NODE_CAPTURE_ATTRIBUTE 1080 #define GEO_NODE_MATERIAL_SELECTION 1081 #define GEO_NODE_SET_MATERIAL 1082 #define GEO_NODE_REALIZE_INSTANCES 1083 #define GEO_NODE_ATTRIBUTE_STATISTIC 1084 -#define GEO_NODE_CURVE_SAMPLE 1085 +#define GEO_NODE_SAMPLE_CURVE 1085 #define GEO_NODE_INPUT_TANGENT 1086 #define GEO_NODE_STRING_JOIN 1087 #define GEO_NODE_CURVE_PARAMETER 1088 -#define GEO_NODE_CURVE_FILLET 1089 +#define GEO_NODE_FILLET_CURVE 1089 #define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES 1090 #define GEO_NODE_STRING_TO_CURVES 1091 #define GEO_NODE_INSTANCE_ON_POINTS 1092 #define GEO_NODE_MESH_TO_POINTS 1093 #define GEO_NODE_POINTS_TO_VERTICES 1094 -#define GEO_NODE_CURVE_REVERSE 1095 +#define GEO_NODE_REVERSE_CURVE 1095 #define GEO_NODE_PROXIMITY 1096 -#define GEO_NODE_CURVE_SUBDIVIDE 1097 +#define GEO_NODE_SUBDIVIDE_CURVE 1097 #define GEO_NODE_INPUT_SPLINE_LENGTH 1098 #define GEO_NODE_CURVE_SPLINE_TYPE 1099 #define GEO_NODE_CURVE_SET_HANDLES 1100 @@ -1535,7 +1535,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_TRANSLATE_INSTANCES 1120 #define GEO_NODE_SCALE_INSTANCES 1121 #define GEO_NODE_ROTATE_INSTANCES 1122 -#define GEO_NODE_EDGE_SPLIT 1123 +#define GEO_NODE_SPLIT_EDGES 1123 #define GEO_NODE_MESH_TO_CURVE 1124 /** \} */ @@ -1545,7 +1545,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, * \{ */ #define FN_NODE_BOOLEAN_MATH 1200 -#define FN_NODE_FLOAT_COMPARE 1202 +#define FN_NODE_COMPARE_FLOATS 1202 #define FN_NODE_LEGACY_RANDOM_FLOAT 1206 #define FN_NODE_INPUT_VECTOR 1207 #define FN_NODE_INPUT_STRING 1208 diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index bf5b0bdbf3c..d2c722f8be7 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1650,8 +1650,8 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 293, 1)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_BOOLEAN, "Geometry A", "Geometry 1"); - version_node_socket_name(ntree, GEO_NODE_BOOLEAN, "Geometry B", "Geometry 2"); + version_node_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry A", "Geometry 1"); + version_node_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry B", "Geometry 2"); } } FOREACH_NODETREE_END; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index b753ab484af..a89a5a9b989 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -499,17 +499,17 @@ static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree) { LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { if (ELEM(node->type, - GEO_NODE_ATTRIBUTE_CAPTURE, + GEO_NODE_CAPTURE_ATTRIBUTE, GEO_NODE_SEPARATE_COMPONENTS, GEO_NODE_CONVEX_HULL, GEO_NODE_CURVE_LENGTH, - GEO_NODE_BOOLEAN, - GEO_NODE_CURVE_FILLET, - GEO_NODE_CURVE_RESAMPLE, + GEO_NODE_MESH_BOOLEAN, + GEO_NODE_FILLET_CURVE, + GEO_NODE_RESAMPLE_CURVE, GEO_NODE_CURVE_TO_MESH, - GEO_NODE_CURVE_TRIM, - GEO_NODE_MATERIAL_REPLACE, - GEO_NODE_MESH_SUBDIVIDE, + GEO_NODE_TRIM_CURVE, + GEO_NODE_REPLACE_MATERIAL, + GEO_NODE_SUBDIVIDE_MESH, GEO_NODE_ATTRIBUTE_REMOVE, GEO_NODE_TRIANGULATE)) { bNodeSocket *geometry_socket = node->inputs.first; @@ -807,9 +807,9 @@ static bool geometry_node_is_293_legacy(const short node_type) /* Not legacy: No attribute inputs or outputs. */ case GEO_NODE_TRIANGULATE: case GEO_NODE_TRANSFORM: - case GEO_NODE_BOOLEAN: + case GEO_NODE_MESH_BOOLEAN: case GEO_NODE_IS_VIEWPORT: - case GEO_NODE_MESH_SUBDIVIDE: + case GEO_NODE_SUBDIVIDE_MESH: case GEO_NODE_MESH_PRIMITIVE_CUBE: case GEO_NODE_MESH_PRIMITIVE_CIRCLE: case GEO_NODE_MESH_PRIMITIVE_UV_SPHERE: @@ -819,9 +819,9 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_MESH_PRIMITIVE_LINE: case GEO_NODE_MESH_PRIMITIVE_GRID: case GEO_NODE_BOUNDING_BOX: - case GEO_NODE_CURVE_RESAMPLE: + case GEO_NODE_RESAMPLE_CURVE: case GEO_NODE_INPUT_MATERIAL: - case GEO_NODE_MATERIAL_REPLACE: + case GEO_NODE_REPLACE_MATERIAL: case GEO_NODE_CURVE_LENGTH: case GEO_NODE_CONVEX_HULL: case GEO_NODE_SEPARATE_COMPONENTS: @@ -833,8 +833,8 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_VIEWER: case GEO_NODE_CURVE_PRIMITIVE_LINE: case GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL: - case GEO_NODE_CURVE_FILL: - case GEO_NODE_CURVE_TRIM: + case GEO_NODE_FILL_CURVE: + case GEO_NODE_TRIM_CURVE: case GEO_NODE_CURVE_TO_MESH: return false; @@ -843,7 +843,7 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_SET_POSITION: case GEO_NODE_INPUT_INDEX: case GEO_NODE_INPUT_NORMAL: - case GEO_NODE_ATTRIBUTE_CAPTURE: + case GEO_NODE_CAPTURE_ATTRIBUTE: return false; /* Maybe legacy: Might need special attribute handling, depending on design. */ @@ -1213,7 +1213,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_MESH_SUBDIVIDE) { + if (node->type == GEO_NODE_SUBDIVIDE_MESH) { strcpy(node->idname, "GeometryNodeMeshSubdivide"); } } @@ -1747,21 +1747,25 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { /* Keep this block, even when empty. */ - /* Update the idname for the Assign Material Node to SetMaterial */ + /* Update the idnames for renamed geo and function nodes */ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type != NTREE_GEOMETRY) { continue; } - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type != GEO_NODE_SET_MATERIAL) { - continue; - } - if (strstr(node->idname, "SetMaterial")) { - /* Make sure we haven't changed this idname already. */ - continue; - } - strcpy(node->idname, "GeometryNodeSetMaterial"); - } + version_node_id(ntree, FN_NODE_COMPARE_FLOATS, "FunctionNodeCompareFloats"); + version_node_id(ntree, GEO_NODE_CAPTURE_ATTRIBUTE, "GeometryNodeCaptureAttribute"); + version_node_id(ntree, GEO_NODE_MESH_BOOLEAN, "GeometryNodeMeshBoolean"); + version_node_id(ntree, GEO_NODE_FILL_CURVE, "GeometryNodeFillCurve"); + version_node_id(ntree, GEO_NODE_FILLET_CURVE, "GeometryNodeFilletCurve"); + version_node_id(ntree, GEO_NODE_REVERSE_CURVE, "GeometryNodeReverseCurve"); + version_node_id(ntree, GEO_NODE_SAMPLE_CURVE, "GeometryNodeSampleCurve"); + version_node_id(ntree, GEO_NODE_RESAMPLE_CURVE, "GeometryNodeResampleCurve"); + version_node_id(ntree, GEO_NODE_SUBDIVIDE_CURVE, "GeometryNodeSubdivideCurve"); + version_node_id(ntree, GEO_NODE_TRIM_CURVE, "GeometryNodeTrimCurve"); + version_node_id(ntree, GEO_NODE_REPLACE_MATERIAL, "GeometryNodeReplaceMaterial"); + version_node_id(ntree, GEO_NODE_SUBDIVIDE_MESH, "GeometryNodeSubdivideMesh"); + version_node_id(ntree, GEO_NODE_SET_MATERIAL, "GeometryNodeSetMaterial"); + version_node_id(ntree, GEO_NODE_SPLIT_EDGES, "GeometryNodeSplitEdges"); } } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 3f13d1ec12e..6c4996ba9b2 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -113,3 +113,17 @@ void version_node_socket_name(bNodeTree *ntree, } } } + +/** + * Replace the ID name of all nodes in the tree with the given type with the new name. + */ +void version_node_id(bNodeTree *ntree, const int node_type, const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + if (!STREQ(node->idname, new_name)) { + strcpy(node->idname, new_name); + } + } + } +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index c1fe2b591cd..1826182be21 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -44,6 +44,8 @@ void version_node_socket_name(struct bNodeTree *ntree, const char *old_name, const char *new_name); +void version_node_id(struct bNodeTree *ntree, const int node_type, const char *new_name); + #ifdef __cplusplus } #endif diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index f3435079563..eefe901b589 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -267,7 +267,7 @@ DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", Leg DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") -DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Compare Floats", "") +DefNode(FunctionNode, FN_NODE_COMPARE_FLOATS, def_float_compare, "COMPARE_FLOATS", CompareFloats, "Compare Floats", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") DefNode(FunctionNode, FN_NODE_INPUT_COLOR, def_fn_input_color, "INPUT_COLOR", InputColor, "Color", "") DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "") @@ -319,15 +319,15 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST" DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Capture Attribute", "") +DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "") -DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Mesh Boolean", "") +DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") -DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Fill Curve", "") -DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Fillet Curve", "") +DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "") +DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") @@ -338,17 +338,17 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMI DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") -DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") -DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Reverse Curve", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Sample Curve", "") +DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "") +DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Subdivide Curve", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") -DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Trim Curve", "") +DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") -DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") +DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") @@ -365,7 +365,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") -DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Replace Material", "") +DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") @@ -375,7 +375,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") -DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Subdivide Mesh", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 72c85de455a..7905419ddb1 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -110,7 +110,7 @@ void register_node_type_fn_float_compare() { static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Compare Floats", NODE_CLASS_CONVERTER, 0); + fn_node_type_base(&ntype, FN_NODE_COMPARE_FLOATS, "Compare Floats", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_float_compare_declare; node_type_label(&ntype, node_float_compare_label); node_type_update(&ntype, node_float_compare_update); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 7f12b816023..b7352160f89 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -196,7 +196,7 @@ void register_node_type_geo_attribute_capture() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_CAPTURE, "Capture Attribute", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_CAPTURE_ATTRIBUTE, "Capture Attribute", NODE_CLASS_ATTRIBUTE, 0); node_type_storage(&ntype, "NodeGeometryAttributeCapture", node_free_standard_storage, diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index ee72d27a87b..27e18f16bad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -128,7 +128,7 @@ void register_node_type_geo_boolean() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Mesh Boolean", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_MESH_BOOLEAN, "Mesh Boolean", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_boolean_declare; ntype.draw_buttons = blender::nodes::geo_node_boolean_layout; ntype.updatefunc = blender::nodes::geo_node_boolean_update; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 0c1fe1fa6a2..953922531c1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -166,7 +166,7 @@ void register_node_type_geo_curve_fill() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_FILL, "Fill Curve", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_FILL_CURVE, "Fill Curve", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_curve_fill_init); node_type_storage( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index bdf122aff29..67ce5b00d6b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -635,7 +635,7 @@ void register_node_type_geo_curve_fillet() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_FILLET, "Fillet Curve", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_FILLET_CURVE, "Fillet Curve", NODE_CLASS_GEOMETRY, 0); ntype.draw_buttons = blender::nodes::geo_node_curve_fillet_layout; node_type_storage( &ntype, "NodeGeometryCurveFillet", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index fe52d0e736c..3db18992c93 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -266,7 +266,7 @@ void register_node_type_geo_curve_resample() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_RESAMPLE_CURVE, "Resample Curve", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_resample_declare; ntype.draw_buttons = blender::nodes::geo_node_curve_resample_layout; node_type_storage( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index adeaa67e1b6..d4ccb768713 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -65,7 +65,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) void register_node_type_geo_curve_reverse() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Reverse Curve", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_REVERSE_CURVE, "Reverse Curve", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_reverse_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index e4043d9408c..6abb7cc31ac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -275,7 +275,7 @@ void register_node_type_geo_curve_sample() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_SAMPLE, " Sample Curve", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SAMPLE_CURVE, " Sample Curve", NODE_CLASS_GEOMETRY, 0); ntype.geometry_node_execute = blender::nodes::geo_node_curve_sample_exec; ntype.declare = blender::nodes::geo_node_curve_sample_declare; node_type_init(&ntype, blender::nodes::geo_node_curve_sample_type_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 0d934108738..a856d593bc2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -357,7 +357,7 @@ void register_node_type_geo_curve_subdivide() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Subdivide Curve", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE_CURVE, "Subdivide Curve", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_subdivide_declare; ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index fd340afabbb..2963b2c4bbb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -424,7 +424,7 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params) void register_node_type_geo_curve_trim() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Trim Curve", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_TRIM_CURVE, "Trim Curve", NODE_CLASS_GEOMETRY, 0); ntype.geometry_node_execute = blender::nodes::geo_node_curve_trim_exec; ntype.draw_buttons = blender::nodes::geo_node_curve_trim_layout; ntype.declare = blender::nodes::geo_node_curve_trim_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index efac0667ffa..e97fc5c2c83 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -87,7 +87,7 @@ void register_node_type_geo_edge_split() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SPLIT_EDGES, "Split Edges", NODE_CLASS_GEOMETRY, 0); ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec; ntype.declare = blender::nodes::geo_node_edge_split_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc index e2a9510c3cb..f3562bed6e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc @@ -62,7 +62,7 @@ void register_node_type_geo_material_replace() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_MATERIAL_REPLACE, "Replace Material", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_REPLACE_MATERIAL, "Replace Material", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_material_replace_declare; ntype.geometry_node_execute = blender::nodes::geo_node_material_replace_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index 79324b38241..d1dd5b1bf8b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -103,7 +103,7 @@ void register_node_type_geo_mesh_subdivide() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_SUBDIVIDE, "Subdivide Mesh", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE_MESH, "Subdivide Mesh", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_mesh_subdivide_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_subdivide_exec; nodeRegisterType(&ntype); From cddda706188a9ebb14ddbfbed5651f321ef31522 Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Thu, 14 Oct 2021 12:22:33 -0500 Subject: [PATCH 0805/1500] Geometry Nodes: Merge Conflict Cleanup Removing a line that remained from a merge. --- release/scripts/startup/nodeitems_builtins.py | 1 - 1 file changed, 1 deletion(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index b26454e0313..932a6ba853d 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -143,7 +143,6 @@ def mesh_node_items(context): yield NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_legacy_poll) yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) - yield NodeItem("GeometryNodeEdgeSplit") yield NodeItem("GeometryNodeMeshBoolean") yield NodeItem("GeometryNodeMeshToCurve") yield NodeItem("GeometryNodeMeshToPoints") From 89c7c115cebb2ac0aaaeb98eb95decc7b5a129c8 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 14 Oct 2021 12:43:36 -0500 Subject: [PATCH 0806/1500] Geometry Nodes: Create empty components less often Avoiding creating empty components can be a hassle for code that interacts with a geometry set. One easy way to do that was calling the functions that retrieved mutable access to geometry data directly, like get_mesh_for_write. This commit makes it so that sort of direct function does not create an empty component if there is no data. Another way to create an empty component was calling the replace_* methods with a null pointer. It's more convenient to have a nice API that handles those cases without creating an empty component. It's still convenient that the regular get_component_for_write adds the component if it doesn't exist, because that's often a nice way to add data to the geometry set. Differential Revision: https://developer.blender.org/D12862 --- source/blender/blenkernel/BKE_geometry_set.hh | 9 +++ .../blender/blenkernel/intern/geometry_set.cc | 80 ++++++++++++++----- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index b36d15578a7..66466e3972e 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -357,6 +357,15 @@ struct GeometrySet { GeometryOwnershipType ownership = GeometryOwnershipType::Owned); void replace_curve(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + + private: + /* Utility to retrieve a mutable component without creating it. */ + GeometryComponent *get_component_ptr(GeometryComponentType type); + template Component *get_component_ptr() + { + BLI_STATIC_ASSERT(is_geometry_component_v, ""); + return static_cast(get_component_ptr(Component::static_type)); + } }; /** A geometry component that can store a mesh. */ diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 84daa06554a..33ead37979d 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -139,6 +139,16 @@ GeometryComponent &GeometrySet::get_component_for_write(GeometryComponentType co }); } +/** + * Retrieve the pointer to a component without creating it if it does not exist, + * unlike #get_component_for_write. + */ +GeometryComponent *GeometrySet::get_component_ptr(GeometryComponentType type) +{ + GeometryComponentPtr *component_ptr = components_.lookup_ptr(type); + return component_ptr == nullptr ? nullptr : component_ptr->get(); +} + /* Get the component of the given type. Might return null if the component does not exist yet. */ const GeometryComponent *GeometrySet::get_component_for_read( GeometryComponentType component_type) const @@ -340,8 +350,10 @@ bool GeometrySet::is_empty() const GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership) { GeometrySet geometry_set; - MeshComponent &component = geometry_set.get_component_for_write(); - component.replace(mesh, ownership); + if (mesh != nullptr) { + MeshComponent &component = geometry_set.get_component_for_write(); + component.replace(mesh, ownership); + } return geometry_set; } @@ -350,8 +362,10 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { GeometrySet geometry_set; - PointCloudComponent &component = geometry_set.get_component_for_write(); - component.replace(pointcloud, ownership); + if (pointcloud != nullptr) { + PointCloudComponent &component = geometry_set.get_component_for_write(); + component.replace(pointcloud, ownership); + } return geometry_set; } @@ -359,65 +373,87 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud, GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership) { GeometrySet geometry_set; - CurveComponent &component = geometry_set.get_component_for_write(); - component.replace(curve, ownership); + if (curve != nullptr) { + CurveComponent &component = geometry_set.get_component_for_write(); + component.replace(curve, ownership); + } return geometry_set; } /* Clear the existing mesh and replace it with the given one. */ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) { - MeshComponent &component = this->get_component_for_write(); - component.replace(mesh, ownership); + if (mesh == nullptr) { + this->remove(); + } + else { + MeshComponent &component = this->get_component_for_write(); + component.replace(mesh, ownership); + } } /* Clear the existing curve and replace it with the given one. */ void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership) { - CurveComponent &component = this->get_component_for_write(); - component.replace(curve, ownership); + if (curve == nullptr) { + this->remove(); + } + else { + CurveComponent &component = this->get_component_for_write(); + component.replace(curve, ownership); + } } /* Clear the existing point cloud and replace with the given one. */ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { - PointCloudComponent &pointcloud_component = this->get_component_for_write(); - pointcloud_component.replace(pointcloud, ownership); + if (pointcloud == nullptr) { + this->remove(); + } + else { + PointCloudComponent &component = this->get_component_for_write(); + component.replace(pointcloud, ownership); + } } /* Clear the existing volume and replace with the given one. */ void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership) { - VolumeComponent &volume_component = this->get_component_for_write(); - volume_component.replace(volume, ownership); + if (volume == nullptr) { + this->remove(); + } + else { + VolumeComponent &component = this->get_component_for_write(); + component.replace(volume, ownership); + } } /* Returns a mutable mesh or null. No ownership is transferred. */ Mesh *GeometrySet::get_mesh_for_write() { - MeshComponent &component = this->get_component_for_write(); - return component.get_for_write(); + MeshComponent *component = this->get_component_ptr(); + return component == nullptr ? nullptr : component->get_for_write(); } /* Returns a mutable point cloud or null. No ownership is transferred. */ PointCloud *GeometrySet::get_pointcloud_for_write() { - PointCloudComponent &component = this->get_component_for_write(); - return component.get_for_write(); + PointCloudComponent *component = this->get_component_ptr(); + return component == nullptr ? nullptr : component->get_for_write(); } /* Returns a mutable volume or null. No ownership is transferred. */ Volume *GeometrySet::get_volume_for_write() { - VolumeComponent &component = this->get_component_for_write(); - return component.get_for_write(); + VolumeComponent *component = this->get_component_ptr(); + return component == nullptr ? nullptr : component->get_for_write(); } /* Returns a mutable curve or null. No ownership is transferred. */ CurveEval *GeometrySet::get_curve_for_write() { - CurveComponent &component = this->get_component_for_write(); - return component.get_for_write(); + CurveComponent *component = this->get_component_ptr(); + return component == nullptr ? nullptr : component->get_for_write(); } void GeometrySet::attribute_foreach(const Span component_types, From 2055ef107a7cb4e5aeeffb5ccc8a2908a59cd7d0 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 14 Oct 2021 15:36:24 -0500 Subject: [PATCH 0807/1500] Geometry Nodes: Order selection inputs after geometry inputs While there may be arguments for different positions of the selection inputs, it's important to be consistent, and putting them right after the corresponding geometry works well when there are multiple geometry inputs. Addresses T91646. --- .../geometry/nodes/node_geo_distribute_points_on_faces.cc | 4 ++-- .../blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc | 2 +- .../nodes/geometry/nodes/node_geo_set_curve_handles.cc | 2 +- .../blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc | 2 +- .../blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_set_material.cc | 2 +- .../nodes/geometry/nodes/node_geo_set_material_index.cc | 2 +- .../blender/nodes/geometry/nodes/node_geo_set_point_radius.cc | 2 +- .../blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc | 2 +- .../nodes/geometry/nodes/node_geo_set_spline_cyclic.cc | 2 +- .../nodes/geometry/nodes/node_geo_set_spline_resolution.cc | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 0c8db0d8912..dec86bd33d3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -43,6 +43,7 @@ namespace blender::nodes { static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Distance Min").min(0.0f).subtype(PROP_DISTANCE); b.add_input("Density Max").default_value(10.0f).min(0.0f); b.add_input("Density").default_value(10.0f).supports_field(); @@ -53,7 +54,6 @@ static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBui .subtype(PROP_FACTOR) .supports_field(); b.add_input("Seed"); - b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Points"); b.add_output("Normal").field_source(); @@ -70,7 +70,7 @@ static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout, static void node_point_distribute_points_on_faces_update(bNodeTree *UNUSED(ntree), bNode *node) { - bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 2); bNodeSocket *sock_density_max = (bNodeSocket *)sock_distance_min->next; bNodeSocket *sock_density = sock_density_max->next; bNodeSocket *sock_density_factor = sock_density->next; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 6863f685eae..df0fdd8eccd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -31,13 +31,13 @@ namespace blender::nodes { static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b) { b.add_input("Mesh"); + b.add_input("Selection").default_value(true).supports_field().hide_value(); b.add_input("Position").implicit_field(); b.add_input("Radius") .default_value(0.05f) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_input("Selection").default_value(true).supports_field().hide_value(); b.add_output("Points"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index 3e106a92b14..cb1c57fa476 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -26,8 +26,8 @@ namespace blender::nodes { static void geo_node_set_curve_handles_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Position").implicit_field(); b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Position").implicit_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index 73599ed4f50..8fa4ff1a808 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -21,9 +21,9 @@ namespace blender::nodes { static void geo_node_set_curve_radius_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Radius").min(0.0f).default_value(1.0f).supports_field().subtype( PROP_DISTANCE); - b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index ee88b24fb04..113149613ef 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -21,8 +21,8 @@ namespace blender::nodes { static void geo_node_set_curve_tilt_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Tilt").subtype(PROP_ANGLE).supports_field(); b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Tilt").subtype(PROP_ANGLE).supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc index ab7d2ab250d..0d85af60944 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -29,8 +29,8 @@ namespace blender::nodes { static void geo_node_set_material_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Material").hide_label(); b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Material").hide_label(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index 66ca0d5b979..a25fe332916 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -21,8 +21,8 @@ namespace blender::nodes { static void geo_node_set_material_index_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Material Index").supports_field().min(0); b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Material Index").supports_field().min(0); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index c98976dd490..b59c9a9e8f5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -21,9 +21,9 @@ namespace blender::nodes { static void geo_node_set_point_radius_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Radius").default_value(0.05f).min(0.0f).supports_field().subtype( PROP_DISTANCE); - b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index 1d7bab4a6bb..ca77041ba7c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -21,8 +21,8 @@ namespace blender::nodes { static void geo_node_set_shade_smooth_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Shade Smooth").supports_field(); b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Shade Smooth").supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index c013e6f0ba4..50e00ff3758 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -21,8 +21,8 @@ namespace blender::nodes { static void geo_node_set_spline_cyclic_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Cyclic").supports_field(); b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Cyclic").supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index ce50f1a29c6..dccb0b1a969 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -23,8 +23,8 @@ namespace blender::nodes { static void geo_node_set_spline_resolution_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Resolution").default_value(12).supports_field(); b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Resolution").default_value(12).supports_field(); b.add_output("Geometry"); } From 3ccdee75328bf8c5f0846f27d66407fcef0ee3e9 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Fri, 15 Oct 2021 02:51:17 +0200 Subject: [PATCH 0808/1500] UI: View2D: Align vertical indicators to view In editors with vertical scale indicators, such as Graph Editor, Drivers, or VSE, display the values aligned to the view. Also add a shadow (similar to the 3D View info) to improve readability when the text is on top of curves, strips, or other content. {F10987240, size=full} Reviewed By: Severin Differential Revision: https://developer.blender.org/D12809 --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- .../blender/editors/interface/view2d_draw.c | 20 +++++++++++-------- source/tools | 2 +- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/release/datafiles/locale b/release/datafiles/locale index 4833954c0ac..75e46177f36 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 4833954c0ac85cc407e1d5a153aa11b1d1823ec0 +Subproject commit 75e46177f36a49ad36b917e641ee1586ddef7092 diff --git a/release/scripts/addons b/release/scripts/addons index 67f1fbca148..f10ca8c1561 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 67f1fbca1482d9d9362a4001332e785c3fd5d230 +Subproject commit f10ca8c156169b24e70027a43f718f99571d280f diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 5a82baad9f9..42da56aa737 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 5a82baad9f986722104280e8354a4427d8e9eab1 +Subproject commit 42da56aa73726710107031787af5eea186797984 diff --git a/source/blender/editors/interface/view2d_draw.c b/source/blender/editors/interface/view2d_draw.c index fd4dba30c1c..a4b37e571d7 100644 --- a/source/blender/editors/interface/view2d_draw.c +++ b/source/blender/editors/interface/view2d_draw.c @@ -394,29 +394,33 @@ static void draw_vertical_scale_indicators(const ARegion *region, const int font_id = BLF_default(); UI_FontThemeColor(font_id, colorid); - BLF_enable(font_id, BLF_ROTATION); - BLF_rotation(font_id, M_PI_2); - BLF_batch_draw_begin(); - const float xpos = rect->xmax - 2.0f * UI_DPI_FAC; + BLF_enable(font_id, BLF_SHADOW); + const float shadow_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + BLF_shadow(font_id, 5, shadow_color); + BLF_shadow_offset(font_id, 1, -1); + + const float x_offset = 8.0f; + const float xpos = (rect->xmin + x_offset) * UI_DPI_FAC; const float ymin = rect->ymin; const float ymax = rect->ymax; + const float y_offset = (BLF_height_max(font_id) / 2.0f) - U.pixelsize; for (uint i = 0; i < steps; i++) { const float ypos_view = start + i * distance; const float ypos_region = UI_view2d_view_to_region_y(v2d, ypos_view + display_offset); char text[32]; to_string(to_string_data, ypos_view, distance, sizeof(text), text); - const float text_width = BLF_width(font_id, text, strlen(text)); - if (ypos_region - text_width / 2.0f >= ymin && ypos_region + text_width / 2.0f <= ymax) { - BLF_draw_default(xpos, ypos_region - text_width / 2.0f, 0.0f, text, sizeof(text)); + if (ypos_region - y_offset >= ymin && ypos_region + y_offset <= ymax) { + BLF_draw_default(xpos, ypos_region - y_offset, 0.0f, text, sizeof(text)); } } + BLF_disable(font_id, BLF_SHADOW); + BLF_batch_draw_end(); - BLF_disable(font_id, BLF_ROTATION); GPU_matrix_pop_projection(); } diff --git a/source/tools b/source/tools index 01f51a0e551..5061594ee62 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 01f51a0e551ab730f0934dc6488613690ac4bf8f +Subproject commit 5061594ee62b8eabf705443a5483c7a1dfaa8789 From 3ca26970010111abf59951f6b07507ea23159a15 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 15 Oct 2021 16:23:52 +1100 Subject: [PATCH 0809/1500] Cleanup: clang-tidy --- .../blender/editors/transform/transform_snap_animation.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c index 6856dcd9586..8828f96247d 100644 --- a/source/blender/editors/transform/transform_snap_animation.c +++ b/source/blender/editors/transform/transform_snap_animation.c @@ -59,12 +59,9 @@ short getAnimEdit_SnapMode(TransInfo *t) if ((t->mode == TFM_TRANSLATION) && activeSnap(t)) { return autosnap; } - else { - SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; - - if (sipo) { - autosnap = sipo->autosnap; - } + SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; + if (sipo) { + autosnap = sipo->autosnap; } } else if (t->spacetype == SPACE_NLA) { From beecd24fc68b0b587183bde8cfd4cf49da2d998b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 15 Oct 2021 16:24:50 +1100 Subject: [PATCH 0810/1500] Cleanup: use const for context argument --- source/blender/windowmanager/WM_api.h | 2 +- source/blender/windowmanager/intern/wm_operators.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 68e2c6b38f0..26406966952 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -592,7 +592,7 @@ void WM_operator_py_idname(char *to, const char *from); bool WM_operator_py_idname_ok_or_report(struct ReportList *reports, const char *classname, const char *idname); -char *WM_context_path_resolve_property_full(struct bContext *C, +char *WM_context_path_resolve_property_full(const struct bContext *C, const PointerRNA *ptr, PropertyRNA *prop, int index); diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 81dcc5ccea0..dff8e72a5fe 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -423,7 +423,9 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr * `object.data.bones["Bones"].use_deform` such paths are not useful for key-shortcuts, * so this function supports returning data-paths directly to context members that aren't ID types. */ -static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr, bool *r_is_id) +static const char *wm_context_member_from_ptr(const bContext *C, + const PointerRNA *ptr, + bool *r_is_id) { const char *member_id = NULL; bool is_id = false; @@ -607,7 +609,7 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr /** * Calculate the path to `ptr` from context `C`, or return NULL if it can't be calculated. */ -char *WM_context_path_resolve_property_full(bContext *C, +char *WM_context_path_resolve_property_full(const bContext *C, const PointerRNA *ptr, PropertyRNA *prop, int index) From 9a76dd2454c2d6f84e4f15c0235669b317599fbd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 15 Oct 2021 16:26:55 +1100 Subject: [PATCH 0811/1500] Fix some property shortcuts not showing in tooltip Shortcut lookups for property buttons were only supported for a subset of RNA types. Replace inline data-path calculation with WM_context_path_resolve_property_full. Now the shortcut for the 3D View's overlay toggle (for e.g.) is shown. --- source/blender/editors/interface/interface.c | 67 +++----------------- 1 file changed, 8 insertions(+), 59 deletions(-) diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 07bb9040da8..b47b63aa14c 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1392,6 +1392,7 @@ static bool ui_but_event_property_operator_string(const bContext *C, const char *prop_enum_value_id = "value"; PointerRNA *ptr = &but->rnapoin; PropertyRNA *prop = but->rnaprop; + int prop_index = but->rnaindex; if ((but->type == UI_BTYPE_BUT_MENU) && (but->block->handle != NULL)) { uiBut *but_parent = but->block->handle->popup_create_vars.but; if ((but->type == UI_BTYPE_BUT_MENU) && (but_parent && but_parent->rnaprop) && @@ -1416,28 +1417,15 @@ static bool ui_but_event_property_operator_string(const bContext *C, return false; } - /* this version is only for finding hotkeys for properties - * (which get set via context using operators) */ - /* to avoid massive slowdowns on property panels, for now, we only check the - * hotkeys for Editor / Scene settings... - * - * TODO: userpref settings? - */ - char *data_path = NULL; + /* This version is only for finding hotkeys for properties. + * These are set set via a data-path which is appended to the context, + * manipulated using operators (see #ctx_toggle_opnames). */ if (ptr->owner_id) { ID *id = ptr->owner_id; if (GS(id->name) == ID_SCR) { - /* screen/editor property - * NOTE: in most cases, there is actually no info for backwards tracing - * how to get back to ID from the editor data we may be dealing with - */ - if (RNA_struct_is_a(ptr->type, &RNA_Space)) { - /* data should be directly on here... */ - data_path = BLI_sprintfN("space_data.%s", RNA_property_identifier(prop)); - } - else if (RNA_struct_is_a(ptr->type, &RNA_Area)) { + if (RNA_struct_is_a(ptr->type, &RNA_Area)) { /* data should be directly on here... */ const char *prop_id = RNA_property_identifier(prop); /* Hack since keys access 'type', UI shows 'ui_type'. */ @@ -1445,58 +1433,19 @@ static bool ui_but_event_property_operator_string(const bContext *C, prop_id = "type"; prop_enum_value >>= 16; prop = RNA_struct_find_property(ptr, prop_id); + prop_index = -1; opnames = ctx_enum_opnames_for_Area_ui_type; opnames_len = ARRAY_SIZE(ctx_enum_opnames_for_Area_ui_type); prop_enum_value_id = "space_type"; prop_enum_value_is_int = true; } - else { - data_path = BLI_sprintfN("area.%s", prop_id); - } - } - else { - /* special exceptions for common nested data in editors... */ - if (RNA_struct_is_a(ptr->type, &RNA_DopeSheet)) { - /* Dope-sheet filtering options. */ - data_path = BLI_sprintfN("space_data.dopesheet.%s", RNA_property_identifier(prop)); - } - else if (RNA_struct_is_a(ptr->type, &RNA_FileSelectParams)) { - /* File-browser options. */ - data_path = BLI_sprintfN("space_data.params.%s", RNA_property_identifier(prop)); - } } } - else if (GS(id->name) == ID_SCE) { - if (RNA_struct_is_a(ptr->type, &RNA_ToolSettings)) { - /* Tool-settings property: - * NOTE: tool-settings is usually accessed directly (i.e. not through scene). */ - data_path = RNA_path_from_ID_to_property(ptr, prop); - } - else { - /* scene property */ - char *path = RNA_path_from_ID_to_property(ptr, prop); - - if (path) { - data_path = BLI_sprintfN("scene.%s", path); - MEM_freeN(path); - } -#if 0 - else { - printf("ERROR in %s(): Couldn't get path for scene property - %s\n", - __func__, - RNA_property_identifier(prop)); - } -#endif - } - } - else { - // puts("other id"); - } - - // printf("prop shortcut: '%s' (%s)\n", RNA_property_identifier(prop), data_path); } + char *data_path = WM_context_path_resolve_property_full(C, ptr, prop, prop_index); + /* We have a data-path! */ bool found = false; if (data_path || (prop_enum_value_ok && prop_enum_value_id)) { From d649b4b066f48bde3b8eee30c5a401164849e430 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 15 Oct 2021 17:48:51 +1100 Subject: [PATCH 0812/1500] Fix crash using menu search without an active area --- .../windowmanager/intern/wm_operators.c | 82 ++++++++++--------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index dff8e72a5fe..0e58e1c0466 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -543,50 +543,52 @@ static const char *wm_context_member_from_ptr(const bContext *C, case ID_SCR: { CTX_TEST_PTR_ID(C, "screen", ptr->owner_id); - SpaceLink *space_data = CTX_wm_space_data(C); - - TEST_PTR_DATA_TYPE("space_data", RNA_Space, ptr, space_data); TEST_PTR_DATA_TYPE("area", RNA_Area, ptr, CTX_wm_area(C)); TEST_PTR_DATA_TYPE("region", RNA_Region, ptr, CTX_wm_region(C)); - switch (space_data->spacetype) { - case SPACE_VIEW3D: { - const View3D *v3d = (View3D *)space_data; - const View3DShading *shading = &v3d->shading; + SpaceLink *space_data = CTX_wm_space_data(C); + if (space_data != NULL) { + TEST_PTR_DATA_TYPE("space_data", RNA_Space, ptr, space_data); - TEST_PTR_DATA_TYPE("space_data.overlay", RNA_View3DOverlay, ptr, v3d); - TEST_PTR_DATA_TYPE("space_data.shading", RNA_View3DShading, ptr, shading); - break; - } - case SPACE_GRAPH: { - const SpaceGraph *sipo = (SpaceGraph *)space_data; - const bDopeSheet *ads = sipo->ads; - TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); - break; - } - case SPACE_FILE: { - const SpaceFile *sfile = (SpaceFile *)space_data; - const FileSelectParams *params = ED_fileselect_get_active_params(sfile); - TEST_PTR_DATA_TYPE("space_data.params", RNA_FileSelectParams, ptr, params); - break; - } - case SPACE_IMAGE: { - const SpaceImage *sima = (SpaceImage *)space_data; - TEST_PTR_DATA_TYPE("space_data.overlay", RNA_SpaceImageOverlay, ptr, sima); - TEST_PTR_DATA_TYPE("space_data.uv_editor", RNA_SpaceUVEditor, ptr, sima); - break; - } - case SPACE_NLA: { - const SpaceNla *snla = (SpaceNla *)space_data; - const bDopeSheet *ads = snla->ads; - TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); - break; - } - case SPACE_ACTION: { - const SpaceAction *sact = (SpaceAction *)space_data; - const bDopeSheet *ads = &sact->ads; - TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); - break; + switch (space_data->spacetype) { + case SPACE_VIEW3D: { + const View3D *v3d = (View3D *)space_data; + const View3DShading *shading = &v3d->shading; + + TEST_PTR_DATA_TYPE("space_data.overlay", RNA_View3DOverlay, ptr, v3d); + TEST_PTR_DATA_TYPE("space_data.shading", RNA_View3DShading, ptr, shading); + break; + } + case SPACE_GRAPH: { + const SpaceGraph *sipo = (SpaceGraph *)space_data; + const bDopeSheet *ads = sipo->ads; + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); + break; + } + case SPACE_FILE: { + const SpaceFile *sfile = (SpaceFile *)space_data; + const FileSelectParams *params = ED_fileselect_get_active_params(sfile); + TEST_PTR_DATA_TYPE("space_data.params", RNA_FileSelectParams, ptr, params); + break; + } + case SPACE_IMAGE: { + const SpaceImage *sima = (SpaceImage *)space_data; + TEST_PTR_DATA_TYPE("space_data.overlay", RNA_SpaceImageOverlay, ptr, sima); + TEST_PTR_DATA_TYPE("space_data.uv_editor", RNA_SpaceUVEditor, ptr, sima); + break; + } + case SPACE_NLA: { + const SpaceNla *snla = (SpaceNla *)space_data; + const bDopeSheet *ads = snla->ads; + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); + break; + } + case SPACE_ACTION: { + const SpaceAction *sact = (SpaceAction *)space_data; + const bDopeSheet *ads = &sact->ads; + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); + break; + } } } From 3022e190a2374fd62e30145e9d3091951e0f21a0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 15 Oct 2021 20:16:54 +1100 Subject: [PATCH 0813/1500] Fix is_repeat being set for in between mouse-move events --- source/blender/windowmanager/intern/wm_event_system.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 785434c301c..ef86df4a8e8 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -4724,6 +4724,7 @@ static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) * them for better performance. */ if (event_last && event_last->type == MOUSEMOVE) { event_last->type = INBETWEEN_MOUSEMOVE; + event_last->is_repeat = false; } wmEvent *event_new = wm_event_add(win, event); From 30bed8761dfb0c6ddb5ecd4b7c31c2e721a57535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 15 Oct 2021 11:22:28 +0200 Subject: [PATCH 0814/1500] Fix T92226 EEVEE: AO misaligned on first sample Caused by tricky state tracking. `GPU_framebuffer_bind()` is updating the framebuffer on first time and will reset the viewport state of it. --- source/blender/draw/engines/eevee/eevee_occlusion.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c index 955cfd990ef..1acd9950012 100644 --- a/source/blender/draw/engines/eevee/eevee_occlusion.c +++ b/source/blender/draw/engines/eevee/eevee_occlusion.c @@ -199,6 +199,8 @@ void EEVEE_occlusion_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) if ((effects->enabled_effects & EFFECT_GTAO) != 0) { DRW_stats_group_start("GTAO Horizon Scan"); + GPU_framebuffer_bind(fbl->gtao_fb); + /** NOTE(fclem): Kind of fragile. We need this to make sure everything lines up * nicely during planar reflection. */ if (common_data->ray_type != EEVEE_RAY_GLOSSY) { @@ -206,8 +208,6 @@ void EEVEE_occlusion_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) GPU_framebuffer_viewport_set(fbl->gtao_fb, 0, 0, UNPACK2(viewport_size)); } - GPU_framebuffer_bind(fbl->gtao_fb); - DRW_draw_pass(psl->ao_horizon_search); if (common_data->ray_type != EEVEE_RAY_GLOSSY) { From 93a8fd1249ffc64ee089b8c3192908dec247fc07 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Fri, 15 Oct 2021 11:42:21 +0200 Subject: [PATCH 0815/1500] Cleanup: Commonize code for checking scene lights/world settings There were several places attempting to check to see if scene lights and world were enabled for display. This tries to find a common place for both of these to reduce duplication. Honestly, I couldn't find a really good spot for these and settled on DRW_engine. It's not the best spot since they're not strictly drawing related, but let's start here. Reviewed By: fclem Differential Revision: https://developer.blender.org/D12658 --- release/scripts/addons | 2 +- source/blender/draw/engines/gpencil/gpencil_engine.c | 10 ++-------- source/blender/draw/intern/draw_color_management.cc | 11 +++-------- source/blender/editors/space_view3d/space_view3d.c | 6 ++---- source/blender/makesdna/DNA_view3d_types.h | 12 ++++++++++++ 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/release/scripts/addons b/release/scripts/addons index f10ca8c1561..67f1fbca148 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit f10ca8c156169b24e70027a43f718f99571d280f +Subproject commit 67f1fbca1482d9d9362a4001332e785c3fd5d230 diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 1078cebdbff..50e32040522 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -120,15 +120,9 @@ void GPENCIL_engine_init(void *ved) bool use_scene_world = false; if (v3d) { - use_scene_lights = ((v3d->shading.type == OB_MATERIAL) && - (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || - ((v3d->shading.type == OB_RENDER) && - (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)); + use_scene_lights = V3D_USES_SCENE_LIGHTS(v3d); - use_scene_world = ((v3d->shading.type == OB_MATERIAL) && - (v3d->shading.flag & V3D_SHADING_SCENE_WORLD)) || - ((v3d->shading.type == OB_RENDER) && - (v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)); + use_scene_world = V3D_USES_SCENE_WORLD(v3d); stl->pd->v3d_color_type = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1; /* Special case: If Vertex Paint mode, use always Vertex mode. */ diff --git a/source/blender/draw/intern/draw_color_management.cc b/source/blender/draw/intern/draw_color_management.cc index 23fa18c83c5..70035c7c6f8 100644 --- a/source/blender/draw/intern/draw_color_management.cc +++ b/source/blender/draw/intern/draw_color_management.cc @@ -30,6 +30,7 @@ #include "GPU_texture.h" #include "DNA_space_types.h" +#include "DNA_view3d_types.h" #include "BKE_colortools.h" @@ -60,14 +61,8 @@ static eDRWColorManagementType drw_color_management_type_for_v3d(const Scene &sc { const bool use_workbench = BKE_scene_uses_blender_workbench(&scene); - const bool use_scene_lights = ((v3d.shading.type == OB_MATERIAL) && - (v3d.shading.flag & V3D_SHADING_SCENE_LIGHTS)) || - ((v3d.shading.type == OB_RENDER) && - (v3d.shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)); - const bool use_scene_world = ((v3d.shading.type == OB_MATERIAL) && - (v3d.shading.flag & V3D_SHADING_SCENE_WORLD)) || - ((v3d.shading.type == OB_RENDER) && - (v3d.shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)); + const bool use_scene_lights = V3D_USES_SCENE_LIGHTS(&v3d); + const bool use_scene_world = V3D_USES_SCENE_WORLD(&v3d); if ((use_workbench && v3d.shading.type == OB_RENDER) || use_scene_lights || use_scene_world) { return eDRWColorManagementType::UseRenderSettings; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index bda6fa05a63..787cf529483 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -30,6 +30,7 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_view3d_types.h" #include "MEM_guardedalloc.h" @@ -1569,10 +1570,7 @@ static void space_view3d_listener(const wmSpaceTypeListenerParams *params) case NC_SCENE: switch (wmn->data) { case ND_WORLD: { - const bool use_scene_world = ((v3d->shading.type == OB_MATERIAL) && - (v3d->shading.flag & V3D_SHADING_SCENE_WORLD)) || - ((v3d->shading.type == OB_RENDER) && - (v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)); + const bool use_scene_world = V3D_USES_SCENE_WORLD(v3d); if (v3d->flag2 & V3D_HIDE_OVERLAYS || use_scene_world) { ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW); } diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index fafc470b95a..d5db72ea85a 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -500,6 +500,18 @@ enum { V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION = (1 << 14), }; +#define V3D_USES_SCENE_LIGHTS(v3d) \ + ((v3d) && \ + ((((v3d)->shading.type == OB_MATERIAL) && ((v3d)->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || \ + (((v3d)->shading.type == OB_RENDER) && \ + ((v3d)->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)))) + +#define V3D_USES_SCENE_WORLD(v3d) \ + ((v3d) && \ + ((((v3d)->shading.type == OB_MATERIAL) && ((v3d)->shading.flag & V3D_SHADING_SCENE_WORLD)) || \ + (((v3d)->shading.type == OB_RENDER) && \ + ((v3d)->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)))) + /** #View3DShading.cavity_type */ enum { V3D_SHADING_CAVITY_SSAO = 0, From e46055ae9dbd77271878b35cdd061cb7cc7e1afc Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Fri, 15 Oct 2021 11:54:07 +0200 Subject: [PATCH 0816/1500] UI: Fix offset of vertical scale indicators `BLF_height_max()` uses the tallest character in the font, and many characters in our font are taller than numbers. Use `BLF_height` with `0` as reference instead. Fix by @harley, thanks! --- source/blender/editors/interface/view2d_draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/view2d_draw.c b/source/blender/editors/interface/view2d_draw.c index a4b37e571d7..b1869fbf2f9 100644 --- a/source/blender/editors/interface/view2d_draw.c +++ b/source/blender/editors/interface/view2d_draw.c @@ -405,7 +405,7 @@ static void draw_vertical_scale_indicators(const ARegion *region, const float xpos = (rect->xmin + x_offset) * UI_DPI_FAC; const float ymin = rect->ymin; const float ymax = rect->ymax; - const float y_offset = (BLF_height_max(font_id) / 2.0f) - U.pixelsize; + const float y_offset = (BLF_height(font_id, "0", 1) / 2.0f) - U.pixelsize; for (uint i = 0; i < steps; i++) { const float ypos_view = start + i * distance; From aca38148ad56c27840de1ee6ebb7bd1841ab0ca4 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 15 Oct 2021 12:12:36 +0200 Subject: [PATCH 0817/1500] Fix T92131: handle node declaration in material properties The issue was that this menu was only looking at socket templates, but not at the new node declarations. This fix is to just check those as well. The fix comes with a small refactor that makes the memory management a bit simpler. Differential Revision: https://developer.blender.org/D12866 --- .../editors/space_node/node_templates.cc | 170 ++++++++++-------- 1 file changed, 93 insertions(+), 77 deletions(-) diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index 648ede7abd5..f68d8589624 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -20,6 +20,7 @@ #include #include +#include #include "MEM_guardedalloc.h" @@ -39,7 +40,9 @@ #include "RNA_access.h" +#include "NOD_node_declaration.hh" #include "NOD_socket.h" +#include "NOD_socket_declarations.hh" #include "../interface/interface_intern.h" /* XXX bad level */ #include "UI_interface.h" @@ -49,17 +52,20 @@ #include "ED_undo.h" +using blender::Vector; +using blender::nodes::NodeDeclaration; + /************************* Node Socket Manipulation **************************/ /* describes an instance of a node type and a specific socket to link */ struct NodeLinkItem { - int socket_index; /* index for linking */ - int socket_type; /* socket type for compatibility check */ - const char *socket_name; /* ui label of the socket */ - const char *node_name; /* ui label of the node */ + int socket_index = -1; /* index for linking */ + int socket_type = SOCK_CUSTOM; /* socket type for compatibility check */ + const char *socket_name = nullptr; /* ui label of the socket */ + const char *node_name = nullptr; /* ui label of the node */ /* extra settings */ - bNodeTree *ngroup; /* group node tree */ + bNodeTree *ngroup = nullptr; /* group node tree */ }; /* Compare an existing node to a link item to see if it can be reused. @@ -319,15 +325,13 @@ struct NodeLinkArg { uiLayout *layout; }; -static void ui_node_link_items(NodeLinkArg *arg, - int in_out, - NodeLinkItem **r_items, - int *r_totitems) +static Vector ui_node_link_items(NodeLinkArg *arg, + int in_out, + std::optional &r_node_decl) { - /* XXX this should become a callback for node types! */ - NodeLinkItem *items = nullptr; - int totitems = 0; + Vector items; + /* XXX this should become a callback for node types! */ if (arg->node_type->type == NODE_GROUP) { bNodeTree *ngroup; int i; @@ -339,40 +343,66 @@ static void ui_node_link_items(NodeLinkArg *arg, !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { continue; } - - ListBase *lb = ((in_out == SOCK_IN) ? &ngroup->inputs : &ngroup->outputs); - totitems += BLI_listbase_count(lb); } - if (totitems > 0) { - items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); - - i = 0; - for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; - ngroup = (bNodeTree *)ngroup->id.next) { - const char *disabled_hint; - if ((ngroup->type != arg->ntree->type) || - !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { - continue; - } - - ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs); - bNodeSocket *stemp; - int index; - for (stemp = (bNodeSocket *)lb->first, index = 0; stemp; - stemp = stemp->next, index++, i++) { - NodeLinkItem *item = &items[i]; - - item->socket_index = index; - /* NOTE: int stemp->type is not fully reliable, not used for node group - * interface sockets. use the typeinfo->type instead. - */ - item->socket_type = stemp->typeinfo->type; - item->socket_name = stemp->name; - item->node_name = ngroup->id.name + 2; - item->ngroup = ngroup; - } + i = 0; + for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; + ngroup = (bNodeTree *)ngroup->id.next) { + const char *disabled_hint; + if ((ngroup->type != arg->ntree->type) || + !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { + continue; } + + ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs); + bNodeSocket *stemp; + int index; + for (stemp = (bNodeSocket *)lb->first, index = 0; stemp; stemp = stemp->next, index++, i++) { + NodeLinkItem item; + item.socket_index = index; + /* NOTE: int stemp->type is not fully reliable, not used for node group + * interface sockets. use the typeinfo->type instead. + */ + item.socket_type = stemp->typeinfo->type; + item.socket_name = stemp->name; + item.node_name = ngroup->id.name + 2; + item.ngroup = ngroup; + + items.append(item); + } + } + } + else if (arg->node_type->declare != nullptr) { + using namespace blender; + using namespace blender::nodes; + + r_node_decl.emplace(NodeDeclaration()); + NodeDeclarationBuilder node_decl_builder{*r_node_decl}; + arg->node_type->declare(node_decl_builder); + Span socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs() : + r_node_decl->outputs(); + int index = 0; + for (const SocketDeclarationPtr &socket_decl_ptr : socket_decls) { + const SocketDeclaration &socket_decl = *socket_decl_ptr; + NodeLinkItem item; + item.socket_index = index++; + /* A socket declaration does not necessarily map to exactly one built-in socket type. So only + * check for the types that matter here. */ + if (dynamic_cast(&socket_decl)) { + item.socket_type = SOCK_RGBA; + } + else if (dynamic_cast(&socket_decl)) { + item.socket_type = SOCK_FLOAT; + } + else if (dynamic_cast(&socket_decl)) { + item.socket_type = SOCK_VECTOR; + } + else { + item.socket_type = SOCK_CUSTOM; + } + item.socket_name = socket_decl.name().c_str(); + item.node_name = arg->node_type->ui_name; + items.append(item); } } else { @@ -381,27 +411,18 @@ static void ui_node_link_items(NodeLinkArg *arg, bNodeSocketTemplate *stemp; int i; - for (stemp = socket_templates; stemp && stemp->type != -1; stemp++) { - totitems++; - } - - if (totitems > 0) { - items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); - - i = 0; - for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) { - NodeLinkItem *item = &items[i]; - - item->socket_index = i; - item->socket_type = stemp->type; - item->socket_name = stemp->name; - item->node_name = arg->node_type->ui_name; - } + i = 0; + for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) { + NodeLinkItem item; + item.socket_index = i; + item.socket_type = stemp->type; + item.socket_name = stemp->name; + item.node_name = arg->node_type->ui_name; + items.append(item); } } - *r_items = items; - *r_totitems = totitems; + return items; } static void ui_node_link(bContext *C, void *arg_p, void *event_p) @@ -513,8 +534,6 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) /* generate UI */ for (int j = 0; j < sorted_ntypes.size(); j++) { bNodeType *ntype = sorted_ntypes[j]; - NodeLinkItem *items; - int totitems; char name[UI_MAX_NAME_STR]; const char *cur_node_name = nullptr; int num = 0; @@ -522,16 +541,17 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) arg->node_type = ntype; - ui_node_link_items(arg, SOCK_OUT, &items, &totitems); + std::optional node_decl; + Vector items = ui_node_link_items(arg, SOCK_OUT, node_decl); - for (int i = 0; i < totitems; i++) { - if (ui_compatible_sockets(items[i].socket_type, sock->type)) { + for (const NodeLinkItem &item : items) { + if (ui_compatible_sockets(item.socket_type, sock->type)) { num++; } } - for (int i = 0; i < totitems; i++) { - if (!ui_compatible_sockets(items[i].socket_type, sock->type)) { + for (const NodeLinkItem &item : items) { + if (!ui_compatible_sockets(item.socket_type, sock->type)) { continue; } @@ -546,8 +566,8 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) } if (num > 1) { - if (!cur_node_name || !STREQ(cur_node_name, items[i].node_name)) { - cur_node_name = items[i].node_name; + if (!cur_node_name || !STREQ(cur_node_name, item.node_name)) { + cur_node_name = item.node_name; /* XXX Do not use uiItemL here, * it would add an empty icon as we are in a menu! */ uiDefBut(block, @@ -566,11 +586,11 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) ""); } - BLI_snprintf(name, UI_MAX_NAME_STR, "%s", IFACE_(items[i].socket_name)); + BLI_snprintf(name, UI_MAX_NAME_STR, "%s", IFACE_(item.socket_name)); icon = ICON_BLANK1; } else { - BLI_strncpy(name, IFACE_(items[i].node_name), UI_MAX_NAME_STR); + BLI_strncpy(name, IFACE_(item.node_name), UI_MAX_NAME_STR); icon = ICON_NONE; } @@ -591,13 +611,9 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) TIP_("Add node to input")); argN = (NodeLinkArg *)MEM_dupallocN(arg); - argN->item = items[i]; + argN->item = item; UI_but_funcN_set(but, ui_node_link, argN, nullptr); } - - if (items) { - MEM_freeN(items); - } } } From da3946b7108dc43e2783031ad8d231b65c6145ff Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 15 Oct 2021 12:36:09 +0200 Subject: [PATCH 0818/1500] Fix wrong DNA struct element lookup in versioning The type of the element is `short`, not `int`. Harmless since this was checking for a specific version anyway. --- source/blender/blenloader/intern/versioning_300.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index a89a5a9b989..af61447e5ad 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1079,7 +1079,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } FOREACH_NODETREE_END; - if (!DNA_struct_elem_find(fd->filesdna, "FileAssetSelectParams", "int", "import_type")) { + if (!DNA_struct_elem_find(fd->filesdna, "FileAssetSelectParams", "short", "import_type")) { LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { From f834939cebc7c608939d73c5a17a2aaea320c85e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 15 Oct 2021 13:39:11 +0200 Subject: [PATCH 0819/1500] Geometry Nodes: fix getting mutable geometry component The previous code did not take into account that they geometry component may not be mutable because it is shared between multiple geometry sets. --- source/blender/blenkernel/intern/geometry_set.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 33ead37979d..e439cc838e3 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -145,8 +145,10 @@ GeometryComponent &GeometrySet::get_component_for_write(GeometryComponentType co */ GeometryComponent *GeometrySet::get_component_ptr(GeometryComponentType type) { - GeometryComponentPtr *component_ptr = components_.lookup_ptr(type); - return component_ptr == nullptr ? nullptr : component_ptr->get(); + if (this->has(type)) { + return &this->get_component_for_write(type); + } + return nullptr; } /* Get the component of the given type. Might return null if the component does not exist yet. */ From 53f25df5bcc59dbaee085d3a319df2a685b67d1a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Oct 2021 15:31:31 +0200 Subject: [PATCH 0820/1500] Fix T92128: Cycles CUDA wrong hair attributes, after recent changes --- intern/cycles/kernel/bvh/bvh_shadow_all.h | 4 +++- intern/cycles/kernel/bvh/bvh_traversal.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index c67c820edbc..ea1ee26b863 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -171,7 +171,9 @@ ccl_device_inline } } - const int curve_object = kernel_tex_fetch(__prim_object, prim_addr); + const int curve_object = (object == OBJECT_NONE) ? + kernel_tex_fetch(__prim_object, prim_addr) : + object; const int curve_type = kernel_tex_fetch(__prim_type, prim_addr); const int curve_prim = kernel_tex_fetch(__prim_index, prim_addr); hit = curve_intersect( diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/bvh_traversal.h index a46c45d3529..9f271a4730c 100644 --- a/intern/cycles/kernel/bvh/bvh_traversal.h +++ b/intern/cycles/kernel/bvh/bvh_traversal.h @@ -172,7 +172,9 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlob } } - const int curve_object = kernel_tex_fetch(__prim_object, prim_addr); + const int curve_object = (object == OBJECT_NONE) ? + kernel_tex_fetch(__prim_object, prim_addr) : + object; const int curve_prim = kernel_tex_fetch(__prim_index, prim_addr); const int curve_type = kernel_tex_fetch(__prim_type, prim_addr); const bool hit = curve_intersect( From 70376154a0b09dc05fcc5bd79c33fdf7c6acbd9a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 13 Oct 2021 18:47:31 +0200 Subject: [PATCH 0821/1500] Fix Cycles Python error with pinned materials in properties editor --- intern/cycles/blender/addon/ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 55782088444..598f6f083ac 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -981,8 +981,8 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel): row.prop(slot, "link", text="", icon=icon_link, icon_only=True) elif mat: - split.template_ID(space, "pin_id") - split.separator() + layout.template_ID(space, "pin_id") + layout.separator() class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): From 2ba7c3aa650c3c795d903a24998204f67c75b017 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 13 Oct 2021 19:13:35 +0200 Subject: [PATCH 0822/1500] Cleanup: refactor to make number of channels for shader evaluation variable --- intern/cycles/device/cpu/kernel.h | 2 +- intern/cycles/integrator/shader_eval.cpp | 26 ++++++++++--------- intern/cycles/integrator/shader_eval.h | 11 +++++--- intern/cycles/kernel/device/cpu/kernel_arch.h | 4 +-- .../kernel/device/cpu/kernel_arch_impl.h | 4 +-- intern/cycles/kernel/device/gpu/kernel.h | 4 +-- .../integrator/integrator_intersect_shadow.h | 3 ++- intern/cycles/kernel/kernel_bake.h | 12 ++++++--- intern/cycles/render/light.cpp | 12 +++++---- intern/cycles/render/mesh_displace.cpp | 11 +++++--- 10 files changed, 53 insertions(+), 36 deletions(-) diff --git a/intern/cycles/device/cpu/kernel.h b/intern/cycles/device/cpu/kernel.h index 54b18308544..b5f0d873f30 100644 --- a/intern/cycles/device/cpu/kernel.h +++ b/intern/cycles/device/cpu/kernel.h @@ -54,7 +54,7 @@ class CPUKernels { /* Shader evaluation. */ using ShaderEvalFunction = CPUKernelFunction; + const KernelGlobals *kg, const KernelShaderEvalInput *, float *, const int)>; ShaderEvalFunction shader_eval_displace; ShaderEvalFunction shader_eval_background; diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp index a14e41ec5be..53546c03872 100644 --- a/intern/cycles/integrator/shader_eval.cpp +++ b/intern/cycles/integrator/shader_eval.cpp @@ -34,9 +34,10 @@ ShaderEval::ShaderEval(Device *device, Progress &progress) : device_(device), pr } bool ShaderEval::eval(const ShaderEvalType type, - const int max_num_points, + const int max_num_inputs, + const int num_channels, const function &)> &fill_input, - const function &)> &read_output) + const function &)> &read_output) { bool first_device = true; bool success = true; @@ -50,26 +51,27 @@ bool ShaderEval::eval(const ShaderEvalType type, first_device = false; device_vector input(device, "ShaderEval input", MEM_READ_ONLY); - device_vector output(device, "ShaderEval output", MEM_READ_WRITE); + device_vector output(device, "ShaderEval output", MEM_READ_WRITE); /* Allocate and copy device buffers. */ DCHECK_EQ(input.device, device); DCHECK_EQ(output.device, device); DCHECK_LE(output.size(), input.size()); - input.alloc(max_num_points); + input.alloc(max_num_inputs); int num_points = fill_input(input); if (num_points == 0) { return; } input.copy_to_device(); - output.alloc(num_points); + output.alloc(num_points * num_channels); output.zero_to_device(); /* Evaluate on CPU or GPU. */ - success = (device->info.type == DEVICE_CPU) ? eval_cpu(device, type, input, output) : - eval_gpu(device, type, input, output); + success = (device->info.type == DEVICE_CPU) ? + eval_cpu(device, type, input, output, num_points) : + eval_gpu(device, type, input, output, num_points); /* Copy data back from device if not canceled. */ if (success) { @@ -87,7 +89,8 @@ bool ShaderEval::eval(const ShaderEvalType type, bool ShaderEval::eval_cpu(Device *device, const ShaderEvalType type, device_vector &input, - device_vector &output) + device_vector &output, + const int64_t work_size) { vector kernel_thread_globals; device->get_cpu_kernel_thread_globals(kernel_thread_globals); @@ -96,9 +99,8 @@ bool ShaderEval::eval_cpu(Device *device, const CPUKernels &kernels = *(device->get_cpu_kernels()); /* Simple parallel_for over all work items. */ - const int64_t work_size = output.size(); KernelShaderEvalInput *input_data = input.data(); - float4 *output_data = output.data(); + float *output_data = output.data(); bool success = true; tbb::task_arena local_arena(device->info.cpu_threads); @@ -130,7 +132,8 @@ bool ShaderEval::eval_cpu(Device *device, bool ShaderEval::eval_gpu(Device *device, const ShaderEvalType type, device_vector &input, - device_vector &output) + device_vector &output, + const int64_t work_size) { /* Find required kernel function. */ DeviceKernel kernel; @@ -151,7 +154,6 @@ bool ShaderEval::eval_gpu(Device *device, * TODO : query appropriate size from device.*/ const int64_t chunk_size = 65536; - const int64_t work_size = output.size(); void *d_input = (void *)input.device_pointer; void *d_output = (void *)output.device_pointer; diff --git a/intern/cycles/integrator/shader_eval.h b/intern/cycles/integrator/shader_eval.h index 7dbf334b8d7..013fad17d4f 100644 --- a/intern/cycles/integrator/shader_eval.h +++ b/intern/cycles/integrator/shader_eval.h @@ -40,19 +40,22 @@ class ShaderEval { /* Evaluate shader at points specified by KernelShaderEvalInput and write out * RGBA colors to output. */ bool eval(const ShaderEvalType type, - const int max_num_points, + const int max_num_inputs, + const int num_channels, const function &)> &fill_input, - const function &)> &read_output); + const function &)> &read_output); protected: bool eval_cpu(Device *device, const ShaderEvalType type, device_vector &input, - device_vector &output); + device_vector &output, + const int64_t work_size); bool eval_gpu(Device *device, const ShaderEvalType type, device_vector &input, - device_vector &output); + device_vector &output, + const int64_t work_size); Device *device_; Progress &progress_; diff --git a/intern/cycles/kernel/device/cpu/kernel_arch.h b/intern/cycles/kernel/device/cpu/kernel_arch.h index 81f328c710b..8b7b0ec0548 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch.h @@ -58,11 +58,11 @@ KERNEL_INTEGRATOR_SHADE_FUNCTION(megakernel); void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobals *kg, const KernelShaderEvalInput *input, - float4 *output, + float *output, const int offset); void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobals *kg, const KernelShaderEvalInput *input, - float4 *output, + float *output, const int offset); /* -------------------------------------------------------------------- diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h index 1432abfd330..23e371f165f 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h @@ -114,7 +114,7 @@ DEFINE_INTEGRATOR_SHADE_KERNEL(megakernel) void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobals *kg, const KernelShaderEvalInput *input, - float4 *output, + float *output, const int offset) { #ifdef KERNEL_STUB @@ -126,7 +126,7 @@ void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobals *kg, void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobals *kg, const KernelShaderEvalInput *input, - float4 *output, + float *output, const int offset) { #ifdef KERNEL_STUB diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index 3379114fc62..21901215757 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -615,7 +615,7 @@ KERNEL_FILM_CONVERT_DEFINE(float4, rgba) ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) kernel_gpu_shader_eval_displace(KernelShaderEvalInput *input, - float4 *output, + float *output, const int offset, const int work_size) { @@ -629,7 +629,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) kernel_gpu_shader_eval_background(KernelShaderEvalInput *input, - float4 *output, + float *output, const int offset, const int work_size) { diff --git a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h index 00d44f0e5ed..3ebd21e4651 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h @@ -85,7 +85,8 @@ ccl_device bool integrate_intersect_shadow_transparent(INTEGRATOR_STATE_ARGS, if (num_recorded_hits > 0) { sort_intersections(isect, num_recorded_hits); - /* Write intersection result into global integrator state memory. */ + /* Write intersection result into global integrator state memory. + * More efficient may be to do this directly from the intersection kernel. */ for (int hit = 0; hit < num_recorded_hits; hit++) { integrator_state_write_shadow_isect(INTEGRATOR_STATE_PASS, &isect[hit], hit); } diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index cfff727d007..6cbb8dcc291 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -26,7 +26,7 @@ CCL_NAMESPACE_BEGIN ccl_device void kernel_displace_evaluate(ccl_global const KernelGlobals *kg, ccl_global const KernelShaderEvalInput *input, - ccl_global float4 *output, + ccl_global float *output, const int offset) { /* Setup shader data. */ @@ -53,12 +53,14 @@ ccl_device void kernel_displace_evaluate(ccl_global const KernelGlobals *kg, D = ensure_finite3(D); /* Write output. */ - output[offset] += make_float4(D.x, D.y, D.z, 0.0f); + output[offset * 3 + 0] += D.x; + output[offset * 3 + 1] += D.y; + output[offset * 3 + 2] += D.z; } ccl_device void kernel_background_evaluate(ccl_global const KernelGlobals *kg, ccl_global const KernelShaderEvalInput *input, - ccl_global float4 *output, + ccl_global float *output, const int offset) { /* Setup ray */ @@ -88,7 +90,9 @@ ccl_device void kernel_background_evaluate(ccl_global const KernelGlobals *kg, color = ensure_finite3(color); /* Write output. */ - output[offset] += make_float4(color.x, color.y, color.z, 0.0f); + output[offset * 3 + 0] += color.x; + output[offset * 3 + 1] += color.y; + output[offset * 3 + 2] += color.z; } CCL_NAMESPACE_END diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index ae1150fc07b..400ed0802a6 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -50,6 +50,7 @@ static void shade_background_pixels(Device *device, device->const_copy_to("__data", &dscene->data, sizeof(dscene->data)); const int size = width * height; + const int num_channels = 3; pixels.resize(size); /* Evaluate shader on device. */ @@ -57,6 +58,7 @@ static void shade_background_pixels(Device *device, shader_eval.eval( SHADER_EVAL_BACKGROUND, size, + num_channels, [&](device_vector &d_input) { /* Fill coordinates for shading. */ KernelShaderEvalInput *d_input_data = d_input.data(); @@ -77,15 +79,15 @@ static void shade_background_pixels(Device *device, return size; }, - [&](device_vector &d_output) { + [&](device_vector &d_output) { /* Copy output to pixel buffer. */ - float4 *d_output_data = d_output.data(); + float *d_output_data = d_output.data(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - pixels[y * width + x].x = d_output_data[y * width + x].x; - pixels[y * width + x].y = d_output_data[y * width + x].y; - pixels[y * width + x].z = d_output_data[y * width + x].z; + pixels[y * width + x].x = d_output_data[(y * width + x) * num_channels + 0]; + pixels[y * width + x].y = d_output_data[(y * width + x) * num_channels + 1]; + pixels[y * width + x].z = d_output_data[(y * width + x) * num_channels + 2]; } } }); diff --git a/intern/cycles/render/mesh_displace.cpp b/intern/cycles/render/mesh_displace.cpp index c00c4c24211..bf8a4585907 100644 --- a/intern/cycles/render/mesh_displace.cpp +++ b/intern/cycles/render/mesh_displace.cpp @@ -115,7 +115,7 @@ static int fill_shader_input(const Scene *scene, /* Read back mesh displacement shader output. */ static void read_shader_output(const Scene *scene, Mesh *mesh, - const device_vector &d_output) + const device_vector &d_output) { const array &mesh_shaders = mesh->get_shader(); const array &mesh_used_shaders = mesh->get_used_shaders(); @@ -125,7 +125,7 @@ static void read_shader_output(const Scene *scene, const int num_motion_steps = mesh->get_motion_steps(); vector done(num_verts, false); - const float4 *d_output_data = d_output.data(); + const float *d_output_data = d_output.data(); int d_output_index = 0; Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); @@ -144,7 +144,11 @@ static void read_shader_output(const Scene *scene, for (int j = 0; j < 3; j++) { if (!done[t.v[j]]) { done[t.v[j]] = true; - float3 off = float4_to_float3(d_output_data[d_output_index++]); + float3 off = make_float3(d_output_data[d_output_index + 0], + d_output_data[d_output_index + 1], + d_output_data[d_output_index + 2]); + d_output_index += 3; + /* Avoid illegal vertex coordinates. */ off = ensure_finite3(off); mesh_verts[t.v[j]] += off; @@ -194,6 +198,7 @@ bool GeometryManager::displace( ShaderEval shader_eval(device, progress); if (!shader_eval.eval(SHADER_EVAL_DISPLACE, num_verts, + 3, function_bind(&fill_shader_input, scene, mesh, object_index, _1), function_bind(&read_shader_output, scene, mesh, _1))) { return false; From eb71157e2a9c7abdeb7045bdf9b79d8ca27ba263 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 14 Oct 2021 17:51:27 +0200 Subject: [PATCH 0823/1500] Cleanup: add utility functions for packing integers --- intern/cycles/kernel/bvh/bvh.h | 17 +++++++----- intern/cycles/kernel/device/optix/kernel.cu | 4 +-- intern/cycles/util/util_math.h | 30 +++++++++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index 8f6dcd0adb9..a501cbe7a4b 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -256,10 +256,10 @@ ccl_device_intersect bool scene_intersect_local(ccl_global const KernelGlobals * int max_hits) { # ifdef __KERNEL_OPTIX__ - uint p0 = ((uint64_t)lcg_state) & 0xFFFFFFFF; - uint p1 = (((uint64_t)lcg_state) >> 32) & 0xFFFFFFFF; - uint p2 = ((uint64_t)local_isect) & 0xFFFFFFFF; - uint p3 = (((uint64_t)local_isect) >> 32) & 0xFFFFFFFF; + uint p0 = pointer_pack_to_uint_0(lcg_state); + uint p1 = pointer_pack_to_uint_1(lcg_state); + uint p2 = pointer_pack_to_uint_0(local_isect); + uint p3 = pointer_pack_to_uint_1(local_isect); uint p4 = local_object; /* Is set to zero on miss or if ray is aborted, so can be used as return value. */ uint p5 = max_hits; @@ -368,8 +368,9 @@ ccl_device_intersect bool scene_intersect_shadow_all(ccl_global const KernelGlob ccl_private uint *num_hits) { # ifdef __KERNEL_OPTIX__ - uint p0 = ((uint64_t)isect) & 0xFFFFFFFF; - uint p1 = (((uint64_t)isect) >> 32) & 0xFFFFFFFF; + uint p0 = pointer_pack_to_uint_0(isect); + uint p1 = pointer_pack_to_uint_1(isect); + uint p2 = 0; /* Number of hits. */ uint p3 = max_hits; uint p4 = visibility; uint p5 = false; @@ -394,11 +395,13 @@ ccl_device_intersect bool scene_intersect_shadow_all(ccl_global const KernelGlob 0, p0, p1, - *num_hits, + p2, p3, p4, p5); + *num_hits = p2; + return p5; # else /* __KERNEL_OPTIX__ */ if (!scene_intersect_valid(ray)) { diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu index 736f30d93ef..c9577bb2aa2 100644 --- a/intern/cycles/kernel/device/optix/kernel.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -34,11 +34,11 @@ template ccl_device_forceinline T *get_payload_ptr_0() { - return (T *)(((uint64_t)optixGetPayload_1() << 32) | optixGetPayload_0()); + return pointer_unpack_from_uint(optixGetPayload_0(), optixGetPayload_1()); } template ccl_device_forceinline T *get_payload_ptr_2() { - return (T *)(((uint64_t)optixGetPayload_3() << 32) | optixGetPayload_2()); + return pointer_unpack_from_uint(optixGetPayload_2(), optixGetPayload_3()); } ccl_device_forceinline int get_object_id() diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index f834011a032..535b6881d3f 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -268,6 +268,36 @@ ccl_device_inline float4 __int4_as_float4(int4 i) #endif } +template ccl_device_inline uint pointer_pack_to_uint_0(T *ptr) +{ + return ((uint64_t)ptr) & 0xFFFFFFFF; +} + +template ccl_device_inline uint pointer_pack_to_uint_1(T *ptr) +{ + return (((uint64_t)ptr) >> 32) & 0xFFFFFFFF; +} + +template ccl_device_inline T *pointer_unpack_from_uint(const uint a, const uint b) +{ + return (T *)(((uint64_t)b << 32) | a); +} + +ccl_device_inline uint uint16_pack_to_uint(const uint a, const uint b) +{ + return (a << 16) | b; +} + +ccl_device_inline uint uint16_unpack_from_uint_0(const uint i) +{ + return i >> 16; +} + +ccl_device_inline uint uint16_unpack_from_uint_1(const uint i) +{ + return i & 0xFFFF; +} + /* Versions of functions which are safe for fast math. */ ccl_device_inline bool isnan_safe(float f) { From 509b637d594f97ce1504c65430d0643ecb4c6f9a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 14 Oct 2021 17:52:19 +0200 Subject: [PATCH 0824/1500] Cleanup: don't copy constant memory to GPU multiple times for displacement --- intern/cycles/render/geometry.cpp | 3 +++ intern/cycles/render/mesh_displace.cpp | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 39681ef3d66..0304f168187 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -1800,6 +1800,9 @@ void GeometryManager::device_update(Device *device, size_t num_bvh = 0; { + /* Copy constant data needed by shader evaluation. */ + device->const_copy_to("__data", &dscene->data, sizeof(dscene->data)); + scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { scene->update_stats->geometry.times.add_entry({"device_update (displacement)", time}); diff --git a/intern/cycles/render/mesh_displace.cpp b/intern/cycles/render/mesh_displace.cpp index bf8a4585907..a08874e6fa8 100644 --- a/intern/cycles/render/mesh_displace.cpp +++ b/intern/cycles/render/mesh_displace.cpp @@ -191,9 +191,6 @@ bool GeometryManager::displace( } } - /* Needs to be up to data for attribute access. */ - device->const_copy_to("__data", &dscene->data, sizeof(dscene->data)); - /* Evaluate shader on device. */ ShaderEval shader_eval(device, progress); if (!shader_eval.eval(SHADER_EVAL_DISPLACE, From 5d565062edc25575bbabf173a4e26f184103944b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 13 Oct 2021 18:19:51 +0200 Subject: [PATCH 0825/1500] Cleanup: refactor OptiX shadow intersection for upcoming changes --- intern/cycles/bvh/bvh_embree.cpp | 2 +- intern/cycles/kernel/bvh/bvh_shadow_all.h | 2 +- intern/cycles/kernel/bvh/bvh_util.h | 35 ++++++++-- intern/cycles/kernel/device/optix/kernel.cu | 68 +++++++++---------- intern/cycles/kernel/geom/geom_motion_curve.h | 43 +++--------- .../cycles/kernel/geom/geom_motion_triangle.h | 39 ++--------- .../kernel/geom/geom_motion_triangle_shader.h | 6 +- intern/cycles/kernel/kernel_shader.h | 10 +-- 8 files changed, 79 insertions(+), 126 deletions(-) diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index ae5b7dd426a..76fcdf539ea 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -81,7 +81,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); /* If no transparent shadows, all light is blocked. */ - const int flags = intersection_get_shader_flags(kg, ¤t_isect); + const int flags = intersection_get_shader_flags(kg, current_isect.prim, current_isect.type); if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->max_hits == 0) { ctx->opaque_hit = true; return; diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index ea1ee26b863..4f2164a86ae 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -197,7 +197,7 @@ ccl_device_inline /* todo: optimize so primitive visibility flag indicates if * the primitive has a transparent shadow shader? */ - const int flags = intersection_get_shader_flags(kg, isect); + const int flags = intersection_get_shader_flags(kg, isect->prim, isect->type); if (!(flags & SD_HAS_TRANSPARENT_SHADOW) || max_hits == 0) { /* If no transparent shadows, all light is blocked and we can diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index fb546f568f3..31aae389da0 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -140,14 +140,12 @@ ccl_device_inline void sort_intersections_and_normals(ccl_private Intersection * /* Utility to quickly get flags from an intersection. */ ccl_device_forceinline int intersection_get_shader_flags( - ccl_global const KernelGlobals *ccl_restrict kg, - ccl_private const Intersection *ccl_restrict isect) + ccl_global const KernelGlobals *ccl_restrict kg, const int prim, const int type) { - const int prim = isect->prim; int shader = 0; #ifdef __HAIR__ - if (isect->type & PRIMITIVE_ALL_TRIANGLE) + if (type & PRIMITIVE_ALL_TRIANGLE) #endif { shader = kernel_tex_fetch(__tri_shader, prim); @@ -195,4 +193,33 @@ ccl_device_forceinline int intersection_get_object_flags( return kernel_tex_fetch(__object_flag, isect->object); } +/* TODO: find a better (faster) solution for this. Maybe store offset per object for + * attributes needed in intersection? */ +ccl_device_inline int intersection_find_attribute(ccl_global const KernelGlobals *kg, + const int object, + const uint id) +{ + uint attr_offset = kernel_tex_fetch(__objects, object).attribute_map_offset; + uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset); + + while (attr_map.x != id) { + if (UNLIKELY(attr_map.x == ATTR_STD_NONE)) { + if (UNLIKELY(attr_map.y == 0)) { + return (int)ATTR_STD_NOT_FOUND; + } + else { + /* Chain jump to a different part of the table. */ + attr_offset = attr_map.z; + } + } + else { + attr_offset += ATTR_PRIM_TYPES; + } + attr_map = kernel_tex_fetch(__attributes_map, attr_offset); + } + + /* return result */ + return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu index c9577bb2aa2..e97b25d31a2 100644 --- a/intern/cycles/kernel/device/optix/kernel.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -172,14 +172,12 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit() extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() { #ifdef __SHADOW_RECORD_ALL__ - bool ignore_intersection = false; - int prim = optixGetPrimitiveIndex(); const uint object = get_object_id(); # ifdef __VISIBILITY_FLAG__ const uint visibility = optixGetPayload_4(); if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { - ignore_intersection = true; + return optixIgnoreIntersection(); } # endif @@ -202,29 +200,39 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() /* Filter out curve endcaps. */ if (u == 0.0f || u == 1.0f) { - ignore_intersection = true; + return optixIgnoreIntersection(); } } # endif - int num_hits = optixGetPayload_2(); - int record_index = num_hits; +# ifndef __TRANSPARENT_SHADOWS__ + /* No transparent shadows support compiled in, make opaque. */ + optixSetPayload_5(true); + return optixTerminateRay(); +# else const int max_hits = optixGetPayload_3(); - if (!ignore_intersection) { - optixSetPayload_2(num_hits + 1); + /* If no transparent shadows, all light is blocked and we can stop immediately. */ + if (max_hits == 0 || + !(intersection_get_shader_flags(NULL, prim, type) & SD_HAS_TRANSPARENT_SHADOW)) { + optixSetPayload_5(true); + return optixTerminateRay(); } + /* Record transparent intersection. */ + const int num_hits = optixGetPayload_2(); + int record_index = num_hits; + + optixSetPayload_2(num_hits + 1); + Intersection *const isect_array = get_payload_ptr_0(); -# ifdef __TRANSPARENT_SHADOWS__ - if (num_hits >= max_hits) { + if (record_index >= max_hits) { /* If maximum number of hits reached, find a hit to replace. */ - const int num_recorded_hits = min(max_hits, num_hits); float max_recorded_t = isect_array[0].t; int max_recorded_hit = 0; - for (int i = 1; i < num_recorded_hits; i++) { + for (int i = 1; i < max_hits; i++) { if (isect_array[i].t > max_recorded_t) { max_recorded_t = isect_array[i].t; max_recorded_hit = i; @@ -232,39 +240,25 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() } if (optixGetRayTmax() >= max_recorded_t) { - /* Accept hit, so that OptiX won't consider any more hits beyond the distance of the current - * hit anymore. */ + /* Accept hit, so that OptiX won't consider any more hits beyond the distance of the + * current hit anymore. */ return; } record_index = max_recorded_hit; } -# endif - if (!ignore_intersection) { - Intersection *const isect = isect_array + record_index; - isect->u = u; - isect->v = v; - isect->t = optixGetRayTmax(); - isect->prim = prim; - isect->object = object; - isect->type = type; + Intersection *const isect = isect_array + record_index; + isect->u = u; + isect->v = v; + isect->t = optixGetRayTmax(); + isect->prim = prim; + isect->object = object; + isect->type = type; -# ifdef __TRANSPARENT_SHADOWS__ - /* Detect if this surface has a shader with transparent shadows. */ - if (!shader_transparent_shadow(NULL, isect) || max_hits == 0) { -# endif - /* If no transparent shadows, all light is blocked and we can stop immediately. */ - optixSetPayload_5(true); - return optixTerminateRay(); -# ifdef __TRANSPARENT_SHADOWS__ - } -# endif - } - - /* Continue tracing. */ optixIgnoreIntersection(); -#endif +# endif /* __TRANSPARENT_SHADOWS__ */ +#endif /* __SHADOW_RECORD_ALL__ */ } extern "C" __global__ void __anyhit__kernel_optix_volume_test() diff --git a/intern/cycles/kernel/geom/geom_motion_curve.h b/intern/cycles/kernel/geom/geom_motion_curve.h index 8e32df439cd..5754608a69b 100644 --- a/intern/cycles/kernel/geom/geom_motion_curve.h +++ b/intern/cycles/kernel/geom/geom_motion_curve.h @@ -27,31 +27,6 @@ CCL_NAMESPACE_BEGIN #ifdef __HAIR__ -ccl_device_inline int find_attribute_curve_motion(ccl_global const KernelGlobals *kg, - int object, - uint id, - ccl_private AttributeElement *elem) -{ - /* todo: find a better (faster) solution for this, maybe store offset per object. - * - * NOTE: currently it's not a bottleneck because in test scenes the loop below runs - * zero iterations and rendering is really slow with motion curves. For until other - * areas are speed up it's probably not so crucial to optimize this out. - */ - uint attr_offset = object_attribute_map_offset(kg, object) + ATTR_PRIM_GEOMETRY; - uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset); - - while (attr_map.x != id) { - attr_offset += ATTR_PRIM_TYPES; - attr_map = kernel_tex_fetch(__attributes_map, attr_offset); - } - - *elem = (AttributeElement)attr_map.y; - - /* return result */ - return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; -} - ccl_device_inline void motion_curve_keys_for_step_linear(ccl_global const KernelGlobals *kg, int offset, int numkeys, @@ -92,13 +67,12 @@ ccl_device_inline void motion_curve_keys_linear(ccl_global const KernelGlobals * object_motion_info(kg, object, &numsteps, NULL, &numkeys); /* figure out which steps we need to fetch and their interpolation factor */ - int maxstep = numsteps * 2; - int step = min((int)(time * maxstep), maxstep - 1); - float t = time * maxstep - step; + const int maxstep = numsteps * 2; + const int step = min((int)(time * maxstep), maxstep - 1); + const float t = time * maxstep - step; /* find attribute */ - AttributeElement elem; - int offset = find_attribute_curve_motion(kg, object, ATTR_STD_MOTION_VERTEX_POSITION, &elem); + const int offset = intersection_find_attribute(kg, object, ATTR_STD_MOTION_VERTEX_POSITION); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* fetch key coordinates */ @@ -160,13 +134,12 @@ ccl_device_inline void motion_curve_keys(ccl_global const KernelGlobals *kg, object_motion_info(kg, object, &numsteps, NULL, &numkeys); /* figure out which steps we need to fetch and their interpolation factor */ - int maxstep = numsteps * 2; - int step = min((int)(time * maxstep), maxstep - 1); - float t = time * maxstep - step; + const int maxstep = numsteps * 2; + const int step = min((int)(time * maxstep), maxstep - 1); + const float t = time * maxstep - step; /* find attribute */ - AttributeElement elem; - int offset = find_attribute_curve_motion(kg, object, ATTR_STD_MOTION_VERTEX_POSITION, &elem); + const int offset = intersection_find_attribute(kg, object, ATTR_STD_MOTION_VERTEX_POSITION); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* fetch key coordinates */ diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/geom_motion_triangle.h index 161b358110d..547f03af47c 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle.h @@ -27,41 +27,12 @@ #pragma once +#include "kernel/bvh/bvh_util.h" + CCL_NAMESPACE_BEGIN /* Time interpolation of vertex positions and normals */ -ccl_device_inline int find_attribute_motion(ccl_global const KernelGlobals *kg, - int object, - uint id, - ccl_private AttributeElement *elem) -{ - /* todo: find a better (faster) solution for this, maybe store offset per object */ - uint attr_offset = object_attribute_map_offset(kg, object); - uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset); - - while (attr_map.x != id) { - if (UNLIKELY(attr_map.x == ATTR_STD_NONE)) { - if (UNLIKELY(attr_map.y == 0)) { - return (int)ATTR_STD_NOT_FOUND; - } - else { - /* Chain jump to a different part of the table. */ - attr_offset = attr_map.z; - } - } - else { - attr_offset += ATTR_PRIM_TYPES; - } - attr_map = kernel_tex_fetch(__attributes_map, attr_offset); - } - - *elem = (AttributeElement)attr_map.y; - - /* return result */ - return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; -} - ccl_device_inline void motion_triangle_verts_for_step(ccl_global const KernelGlobals *kg, uint4 tri_vindex, int offset, @@ -129,8 +100,7 @@ ccl_device_inline void motion_triangle_vertices( float t = time * maxstep - step; /* find attribute */ - AttributeElement elem; - int offset = find_attribute_motion(kg, object, ATTR_STD_MOTION_VERTEX_POSITION, &elem); + int offset = intersection_find_attribute(kg, object, ATTR_STD_MOTION_VERTEX_POSITION); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* fetch vertex coordinates */ @@ -164,8 +134,7 @@ ccl_device_inline float3 motion_triangle_smooth_normal(ccl_global const KernelGl float t = time * maxstep - step; /* find attribute */ - AttributeElement elem; - int offset = find_attribute_motion(kg, object, ATTR_STD_MOTION_VERTEX_NORMAL, &elem); + int offset = intersection_find_attribute(kg, object, ATTR_STD_MOTION_VERTEX_NORMAL); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* fetch normals */ diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h b/intern/cycles/kernel/geom/geom_motion_triangle_shader.h index 03bb1fba2a2..25a68fa7781 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle_shader.h @@ -56,8 +56,7 @@ ccl_device_noinline void motion_triangle_shader_setup(ccl_global const KernelGlo int step = min((int)(sd->time * maxstep), maxstep - 1); float t = sd->time * maxstep - step; /* Find attribute. */ - AttributeElement elem; - int offset = find_attribute_motion(kg, sd->object, ATTR_STD_MOTION_VERTEX_POSITION, &elem); + int offset = intersection_find_attribute(kg, sd->object, ATTR_STD_MOTION_VERTEX_POSITION); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* Fetch vertex coordinates. */ float3 verts[3], next_verts[3]; @@ -96,8 +95,7 @@ ccl_device_noinline void motion_triangle_shader_setup(ccl_global const KernelGlo /* Compute smooth normal. */ if (sd->shader & SHADER_SMOOTH_NORMAL) { /* Find attribute. */ - AttributeElement elem; - int offset = find_attribute_motion(kg, sd->object, ATTR_STD_MOTION_VERTEX_NORMAL, &elem); + int offset = intersection_find_attribute(kg, sd->object, ATTR_STD_MOTION_VERTEX_NORMAL); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* Fetch vertex coordinates. */ float3 normals[3], next_normals[3]; diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 4174a27406b..b5a52ff866d 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -862,15 +862,7 @@ ccl_device void shader_eval_displacement(INTEGRATOR_STATE_CONST_ARGS, ccl_privat #endif } -/* Transparent Shadows */ - -#ifdef __TRANSPARENT_SHADOWS__ -ccl_device bool shader_transparent_shadow(ccl_global const KernelGlobals *kg, - ccl_private Intersection *isect) -{ - return (intersection_get_shader_flags(kg, isect) & SD_HAS_TRANSPARENT_SHADOW) != 0; -} -#endif /* __TRANSPARENT_SHADOWS__ */ +/* Cryptomatte */ ccl_device float shader_cryptomatte_id(ccl_global const KernelGlobals *kg, int shader) { From 2f36762defba12f2fc27e538a0c4002c36889ca3 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 14 Oct 2021 19:08:52 +0200 Subject: [PATCH 0826/1500] Cleanup: refactor BVH2 shadow intersection for upcoming changes --- intern/cycles/kernel/bvh/bvh_shadow_all.h | 62 ++++++++++++++--------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index 4f2164a86ae..7e2edd2684c 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -57,21 +57,27 @@ ccl_device_inline int node_addr = kernel_data.bvh.root; /* ray parameters in registers */ - const float tmax = ray->t; float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); int object = OBJECT_NONE; - float isect_t = tmax; #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; #endif + /* Max distance in world space. May be dynamically reduced when max number of + * recorded hits is exceeded and we no longer need to find hits beyond the max + * distance found. */ + float t_max_world = ray->t; + /* Equal to t_max_world when traversing top level BVH, transformed into local + * space when entering instances. */ + float t_max_current = t_max_world; + /* Conversion from world to local space for the current instance if any, 1.0 + * otherwise. */ float t_world_to_instance = 1.0f; *num_hits = 0; - ccl_private Intersection *isect = isect_array; /* traversal loop */ do { @@ -88,7 +94,7 @@ ccl_device_inline dir, #endif idir, - isect_t, + t_max_current, node_addr, visibility, dist); @@ -144,17 +150,18 @@ ccl_device_inline /* todo: specialized intersect functions which don't fill in * isect unless needed and check SD_HAS_TRANSPARENT_SHADOW? * might give a few % performance improvement */ + Intersection isect ccl_optional_struct_init; switch (type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { hit = triangle_intersect( - kg, isect, P, dir, isect_t, visibility, object, prim_addr); + kg, &isect, P, dir, t_max_current, visibility, object, prim_addr); break; } #if BVH_FEATURE(BVH_MOTION) case PRIMITIVE_MOTION_TRIANGLE: { hit = motion_triangle_intersect( - kg, isect, P, dir, isect_t, ray->time, visibility, object, prim_addr); + kg, &isect, P, dir, t_max_current, ray->time, visibility, object, prim_addr); break; } #endif @@ -176,8 +183,15 @@ ccl_device_inline object; const int curve_type = kernel_tex_fetch(__prim_type, prim_addr); const int curve_prim = kernel_tex_fetch(__prim_index, prim_addr); - hit = curve_intersect( - kg, isect, P, dir, isect_t, curve_object, curve_prim, ray->time, curve_type); + hit = curve_intersect(kg, + &isect, + P, + dir, + t_max_current, + curve_object, + curve_prim, + ray->time, + curve_type); break; } @@ -191,13 +205,12 @@ ccl_device_inline /* shadow ray early termination */ if (hit) { /* Convert intersection distance to world space. */ - isect->t /= t_world_to_instance; + isect.t /= t_world_to_instance; /* detect if this surface has a shader with transparent shadows */ - /* todo: optimize so primitive visibility flag indicates if * the primitive has a transparent shadow shader? */ - const int flags = intersection_get_shader_flags(kg, isect->prim, isect->type); + const int flags = intersection_get_shader_flags(kg, isect.prim, isect.type); if (!(flags & SD_HAS_TRANSPARENT_SHADOW) || max_hits == 0) { /* If no transparent shadows, all light is blocked and we can @@ -207,13 +220,13 @@ ccl_device_inline /* Increase the number of hits, possibly beyond max_hits, we will * simply not record those and only keep the max_hits closest. */ - (*num_hits)++; + uint record_index = (*num_hits)++; - if (*num_hits >= max_hits) { + if (record_index >= max_hits - 1) { /* If maximum number of hits reached, find the intersection with * the largest distance to potentially replace when another hit * is found. */ - const int num_recorded_hits = min(max_hits, *num_hits); + const int num_recorded_hits = min(max_hits, record_index); float max_recorded_t = isect_array[0].t; int max_recorded_hit = 0; @@ -224,15 +237,16 @@ ccl_device_inline } } - isect = isect_array + max_recorded_hit; + if (record_index >= max_hits) { + record_index = max_recorded_hit; + } /* Limit the ray distance and stop counting hits beyond this. */ - isect_t = max_recorded_t * t_world_to_instance; - } - else { - /* Still have space for intersection, use next hit. */ - isect = isect + 1; + t_max_world = max(max_recorded_t, isect.t); + t_max_current = t_max_world * t_world_to_instance; } + + isect_array[record_index] = isect; } prim_addr++; @@ -250,7 +264,7 @@ ccl_device_inline #endif /* Convert intersection to object space. */ - isect_t *= t_world_to_instance; + t_max_current *= t_world_to_instance; ++stack_ptr; kernel_assert(stack_ptr < BVH_STACK_SIZE); @@ -271,10 +285,8 @@ ccl_device_inline bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX); #endif - /* Restore world space ray length. If max number of hits exceeded this - * distance is reduced to recorded only the closest hits. If not use - * the original ray length. */ - isect_t = (max_hits && *num_hits > max_hits) ? isect->t : tmax; + /* Restore world space ray length. */ + t_max_current = t_max_world; object = OBJECT_NONE; t_world_to_instance = 1.0f; From 78b5050ff46646af748272bddd7a78506defab11 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Fri, 15 Oct 2021 15:01:03 +0100 Subject: [PATCH 0827/1500] Cycles: Voronoi noise, fix uninitialised variable Caused a debug crash in Windows MSVS. Reviewed By: brecht Differential Revision: https://developer.blender.org/D12873 --- intern/cycles/kernel/svm/svm_voronoi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/kernel/svm/svm_voronoi.h b/intern/cycles/kernel/svm/svm_voronoi.h index e7112087e17..062a8bde415 100644 --- a/intern/cycles/kernel/svm/svm_voronoi.h +++ b/intern/cycles/kernel/svm/svm_voronoi.h @@ -1001,7 +1001,7 @@ ccl_device_noinline int svm_node_tex_voronoi(ccl_global const KernelGlobals *kg, } case 2: { float2 coord_2d = make_float2(coord.x, coord.y); - float2 position_out_2d; + float2 position_out_2d = zero_float2(); switch (voronoi_feature) { case NODE_VORONOI_F1: voronoi_f1_2d(coord_2d, From d4f1bc5f39b219466978a1c9e74618ff8fa27433 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Fri, 15 Oct 2021 15:03:14 +0100 Subject: [PATCH 0828/1500] Geometry Nodes: Port shader gradient texture node Reviewed By: HooglyBoogly, JacquesLucke Differential Revision: https://developer.blender.org/D12717 --- release/scripts/startup/nodeitems_builtins.py | 1 + .../modifiers/intern/MOD_nodes_evaluator.cc | 1 + .../shader/nodes/node_shader_tex_gradient.cc | 111 +++++++++++++++++- 3 files changed, 110 insertions(+), 3 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 932a6ba853d..04e9fba02f2 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -718,6 +718,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeStringToCurves"), ]), GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ + NodeItem("ShaderNodeTexGradient"), NodeItem("ShaderNodeTexNoise"), NodeItem("ShaderNodeTexWhiteNoise"), ]), diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index d600e708167..e0803e546b3 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -331,6 +331,7 @@ static void get_socket_value(const SocketRef &socket, void *r_value) if (bsocket.type == SOCK_VECTOR) { if (ELEM(bnode.type, GEO_NODE_SET_POSITION, + SH_NODE_TEX_GRADIENT, SH_NODE_TEX_NOISE, SH_NODE_TEX_WHITE_NOISE, GEO_NODE_MESH_TO_POINTS, diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc index e0520ee49d3..4796af02361 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc @@ -23,7 +23,8 @@ namespace blender::nodes { static void sh_node_tex_gradient_declare(NodeDeclarationBuilder &b) { - b.add_input("Vector").hide_value(); + b.is_function_node(); + b.add_input("Vector").hide_value().implicit_field(); b.add_output("Color").no_muted_links(); b.add_output("Fac").no_muted_links(); }; @@ -55,17 +56,121 @@ static int node_shader_gpu_tex_gradient(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_tex_gradient", in, out, GPU_constant(&gradient_type)); } -/* node type definition */ +namespace blender::nodes { + +class GradientFunction : public fn::MultiFunction { + private: + int gradient_type_; + + public: + GradientFunction(int gradient_type) : gradient_type_(gradient_type) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"GradientFunction"}; + signature.single_input("Vector"); + signature.single_output("Color"); + signature.single_output("Fac"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &vector = params.readonly_single_input(0, "Vector"); + + MutableSpan r_color = + params.uninitialized_single_output_if_required(1, "Color"); + MutableSpan fac = params.uninitialized_single_output(2, "Fac"); + + const bool compute_color = !r_color.is_empty(); + + switch (gradient_type_) { + case SHD_BLEND_LINEAR: { + for (int64_t i : mask) { + fac[i] = vector[i].x; + } + break; + } + case SHD_BLEND_QUADRATIC: { + for (int64_t i : mask) { + const float r = std::max(vector[i].x, 0.0f); + fac[i] = r * r; + } + break; + } + case SHD_BLEND_EASING: { + for (int64_t i : mask) { + const float r = std::min(std::max(vector[i].x, 0.0f), 1.0f); + const float t = r * r; + fac[i] = (3.0f * t - 2.0f * t * r); + } + break; + } + case SHD_BLEND_DIAGONAL: { + for (int64_t i : mask) { + fac[i] = (vector[i].x + vector[i].y) * 0.5f; + } + break; + } + case SHD_BLEND_RADIAL: { + for (int64_t i : mask) { + fac[i] = atan2f(vector[i].y, vector[i].x) / (M_PI * 2.0f) + 0.5f; + } + break; + } + case SHD_BLEND_QUADRATIC_SPHERE: { + for (int64_t i : mask) { + /* Bias a little bit for the case where input is a unit length vector, + * to get exactly zero instead of a small random value depending + * on float precision. */ + const float r = std::max(0.999999f - vector[i].length(), 0.0f); + fac[i] = r * r; + } + break; + } + case SHD_BLEND_SPHERICAL: { + for (int64_t i : mask) { + /* Bias a little bit for the case where input is a unit length vector, + * to get exactly zero instead of a small random value depending + * on float precision. */ + fac[i] = std::max(0.999999f - vector[i].length(), 0.0f); + } + break; + } + } + if (compute_color) { + for (int64_t i : mask) { + r_color[i] = ColorGeometry4f(fac[i], fac[i], fac[i], 1.0f); + } + } + } +}; + +static void sh_node_gradient_tex_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexGradient *tex = (NodeTexGradient *)node.storage; + builder.construct_and_set_matching_fn(tex->gradient_type); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_gradient(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_GRADIENT, "Gradient Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_GRADIENT, "Gradient Texture", NODE_CLASS_TEXTURE, 0); ntype.declare = blender::nodes::sh_node_tex_gradient_declare; node_type_init(&ntype, node_shader_init_tex_gradient); node_type_storage( &ntype, "NodeTexGradient", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_gradient); + ntype.build_multi_function = blender::nodes::sh_node_gradient_tex_build_multi_function; nodeRegisterType(&ntype); } From 6e4ab5b761b03b52177985ecbeb2c2f576159c74 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 16 Oct 2021 00:42:19 +1100 Subject: [PATCH 0829/1500] Fix crash handling tool-keymap events There was a rare crash in WM_event_get_keymap_from_toolsystem_fallback when wm->winactive was NULL. This could happen when the event was handled immediately after closing a window. --- .../interface_template_search_menu.c | 2 +- source/blender/windowmanager/WM_api.h | 5 ++ .../windowmanager/intern/wm_event_system.c | 40 ++++++----- .../blender/windowmanager/intern/wm_keymap.c | 71 +++++++++++++++---- 4 files changed, 86 insertions(+), 32 deletions(-) diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index 3a5d65475f7..bbb04a9911c 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -351,7 +351,7 @@ static void menu_types_add_from_keymap_items(bContext *C, if (handler_base->poll == NULL || handler_base->poll(region, win->eventstate)) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; wmEventHandler_KeymapResult km_result; - WM_event_get_keymaps_from_handler(wm, handler, &km_result); + WM_event_get_keymaps_from_handler(wm, win, handler, &km_result); for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { wmKeyMap *keymap = km_result.keymaps[km_index]; if (keymap && WM_keymap_poll(C, keymap)) { diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 26406966952..4ee514edb3c 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -272,13 +272,16 @@ typedef struct wmEventHandler_KeymapResult { } wmEventHandler_KeymapResult; typedef void(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm, + struct wmWindow *win, struct wmEventHandler_Keymap *handler, struct wmEventHandler_KeymapResult *km_result); void WM_event_get_keymap_from_toolsystem_fallback(struct wmWindowManager *wm, + struct wmWindow *win, struct wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result); void WM_event_get_keymap_from_toolsystem(struct wmWindowManager *wm, + struct wmWindow *win, struct wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result); @@ -293,6 +296,7 @@ void WM_event_set_keymap_handler_post_callback(struct wmEventHandler_Keymap *han void *user_data), void *user_data); void WM_event_get_keymaps_from_handler(wmWindowManager *wm, + struct wmWindow *win, struct wmEventHandler_Keymap *handler, struct wmEventHandler_KeymapResult *km_result); @@ -302,6 +306,7 @@ wmKeyMapItem *WM_event_match_keymap_item(struct bContext *C, wmKeyMapItem *WM_event_match_keymap_item_from_handlers(struct bContext *C, struct wmWindowManager *wm, + struct wmWindow *win, struct ListBase *handlers, const struct wmEvent *event); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index ef86df4a8e8..df4d2c13ba7 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -2977,7 +2977,7 @@ static int wm_handlers_do_gizmo_handler(bContext *C, /** \name Handle Single Event (All Handler Types) * \{ */ -static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers) +static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, ListBase *handlers) { const bool do_debug_handler = (G.debug & G_DEBUG_HANDLERS) && @@ -3020,7 +3020,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; wmEventHandler_KeymapResult km_result; - WM_event_get_keymaps_from_handler(wm, handler, &km_result); + WM_event_get_keymaps_from_handler(wm, win, handler, &km_result); int action_iter = WM_HANDLER_CONTINUE; for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { wmKeyMap *keymap = km_result.keymaps[km_index]; @@ -3161,10 +3161,10 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers /* This calls handlers twice - to solve (double-)click events. */ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) { - int action = wm_handlers_do_intern(C, event, handlers); - /* Will be NULL in the file read case. */ wmWindow *win = CTX_wm_window(C); + int action = wm_handlers_do_intern(C, win, event, handlers); + if (win == NULL) { return action; } @@ -3188,7 +3188,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG"); - action |= wm_handlers_do_intern(C, event, handlers); + action |= wm_handlers_do_intern(C, win, event, handlers); event->val = val; event->type = type; @@ -3247,7 +3247,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK"); - action |= wm_handlers_do_intern(C, event, handlers); + action |= wm_handlers_do_intern(C, win, event, handlers); event->val = KM_RELEASE; event->x = x; @@ -3259,7 +3259,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) else if (event->val == KM_DBL_CLICK) { /* The underlying event is a press, so try and handle this. */ event->val = KM_PRESS; - action |= wm_handlers_do_intern(C, event, handlers); + action |= wm_handlers_do_intern(C, win, event, handlers); /* revert value if not handled */ if (wm_action_not_handled(action)) { @@ -4020,6 +4020,7 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap * Follow #wmEventHandler_KeymapDynamicFn signature. */ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, + wmWindow *win, wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result) { @@ -4028,7 +4029,13 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, const char *keymap_id_list[ARRAY_SIZE(km_result->keymaps)]; int keymap_id_list_len = 0; - const Scene *scene = wm->winactive->scene; + /* NOTE(@campbellbarton): If `win` is NULL, this function may not behave as expected. + * Assert since this should not happen in practice. + * If it does, the window could be looked up in `wm` using the `area`. + * Keep NULL checks in run-time code since any crashes here are difficult to redo. */ + BLI_assert_msg(win != NULL, "The window should always be set for tool interactions!"); + const Scene *scene = win ? win->scene : NULL; + ScrArea *area = handler->dynamic.user_data; handler->keymap_tool = NULL; bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL; @@ -4041,7 +4048,7 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, bool is_gizmo_highlight = false; if ((tref_rt && tref_rt->keymap_fallback[0]) && - (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK)) { + (scene && (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK))) { bool add_keymap = false; /* Support for the gizmo owning the tool keymap. */ @@ -4100,6 +4107,7 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, } void WM_event_get_keymap_from_toolsystem(wmWindowManager *wm, + wmWindow *UNUSED(win), wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result) { @@ -5257,11 +5265,12 @@ void WM_set_locked_interface(wmWindowManager *wm, bool lock) * \{ */ void WM_event_get_keymaps_from_handler(wmWindowManager *wm, + wmWindow *win, wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result) { if (handler->dynamic.keymap_fn != NULL) { - handler->dynamic.keymap_fn(wm, handler, km_result); + handler->dynamic.keymap_fn(wm, win, handler, km_result); BLI_assert(handler->keymap == NULL); } else { @@ -5287,10 +5296,8 @@ wmKeyMapItem *WM_event_match_keymap_item(bContext *C, wmKeyMap *keymap, const wm return NULL; } -wmKeyMapItem *WM_event_match_keymap_item_from_handlers(bContext *C, - wmWindowManager *wm, - ListBase *handlers, - const wmEvent *event) +wmKeyMapItem *WM_event_match_keymap_item_from_handlers( + bContext *C, wmWindowManager *wm, wmWindow *win, ListBase *handlers, const wmEvent *event) { LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { /* During this loop, UI handlers for nested menus can tag multiple handlers free. */ @@ -5301,7 +5308,7 @@ wmKeyMapItem *WM_event_match_keymap_item_from_handlers(bContext *C, if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; wmEventHandler_KeymapResult km_result; - WM_event_get_keymaps_from_handler(wm, handler, &km_result); + WM_event_get_keymaps_from_handler(wm, win, handler, &km_result); for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { wmKeyMap *keymap = km_result.keymaps[km_index]; if (WM_keymap_poll(C, keymap)) { @@ -5531,7 +5538,8 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) wm_eventemulation(&test_event, true); wmKeyMapItem *kmi = NULL; for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { - kmi = WM_event_match_keymap_item_from_handlers(C, wm, handlers[handler_index], &test_event); + kmi = WM_event_match_keymap_item_from_handlers( + C, wm, win, handlers[handler_index], &test_event); if (kmi) { break; } diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index e5aedfc7f47..658424b84a6 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -1390,6 +1390,8 @@ static wmKeyMapItem *wm_keymap_item_find_in_keymap(wmKeyMap *keymap, } static wmKeyMapItem *wm_keymap_item_find_handlers(const bContext *C, + wmWindowManager *wm, + wmWindow *win, ListBase *handlers, const char *opname, int UNUSED(opcontext), @@ -1398,14 +1400,12 @@ static wmKeyMapItem *wm_keymap_item_find_handlers(const bContext *C, const struct wmKeyMapItemFind_Params *params, wmKeyMap **r_keymap) { - wmWindowManager *wm = CTX_wm_manager(C); - /* find keymap item in handlers */ LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; wmEventHandler_KeymapResult km_result; - WM_event_get_keymaps_from_handler(wm, handler, &km_result); + WM_event_get_keymaps_from_handler(wm, win, handler, &km_result); for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { wmKeyMap *keymap = km_result.keymaps[km_index]; if (WM_keymap_poll((bContext *)C, keymap)) { @@ -1436,6 +1436,7 @@ static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, const struct wmKeyMapItemFind_Params *params, wmKeyMap **r_keymap) { + wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); @@ -1443,17 +1444,25 @@ static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, /* look into multiple handler lists to find the item */ if (win) { - found = wm_keymap_item_find_handlers( - C, &win->modalhandlers, opname, opcontext, properties, is_strict, params, r_keymap); + found = wm_keymap_item_find_handlers(C, + wm, + win, + &win->modalhandlers, + opname, + opcontext, + properties, + is_strict, + params, + r_keymap); if (found == NULL) { found = wm_keymap_item_find_handlers( - C, &win->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + C, wm, win, &win->handlers, opname, opcontext, properties, is_strict, params, r_keymap); } } if (area && found == NULL) { found = wm_keymap_item_find_handlers( - C, &area->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + C, wm, win, &area->handlers, opname, opcontext, properties, is_strict, params, r_keymap); } if (found == NULL) { @@ -1464,8 +1473,16 @@ static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, } if (region) { - found = wm_keymap_item_find_handlers( - C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + found = wm_keymap_item_find_handlers(C, + wm, + win, + ®ion->handlers, + opname, + opcontext, + properties, + is_strict, + params, + r_keymap); } } } @@ -1475,8 +1492,16 @@ static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, } if (region) { - found = wm_keymap_item_find_handlers( - C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + found = wm_keymap_item_find_handlers(C, + wm, + win, + ®ion->handlers, + opname, + opcontext, + properties, + is_strict, + params, + r_keymap); } } else if (ELEM(opcontext, WM_OP_EXEC_REGION_PREVIEW, WM_OP_INVOKE_REGION_PREVIEW)) { @@ -1485,14 +1510,30 @@ static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, } if (region) { - found = wm_keymap_item_find_handlers( - C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + found = wm_keymap_item_find_handlers(C, + wm, + win, + ®ion->handlers, + opname, + opcontext, + properties, + is_strict, + params, + r_keymap); } } else { if (region) { - found = wm_keymap_item_find_handlers( - C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + found = wm_keymap_item_find_handlers(C, + wm, + win, + ®ion->handlers, + opname, + opcontext, + properties, + is_strict, + params, + r_keymap); } } } From 104887800c0f221fbcffa84bb360dd9ff001d7f1 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Fri, 15 Oct 2021 15:27:16 +0100 Subject: [PATCH 0830/1500] Geometry Nodes: Add Voronoi Texture Port shader Voronoi to GN Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12725 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenlib/BLI_float2.hh | 25 + source/blender/blenlib/BLI_float3.hh | 15 + source/blender/blenlib/BLI_float4.hh | 52 + source/blender/blenlib/BLI_noise.hh | 94 ++ .../blender/blenlib/intern/math_base_inline.c | 16 + .../blenlib/intern/math_vector_inline.c | 13 + source/blender/blenlib/intern/noise.cc | 890 +++++++++++++++++ .../modifiers/intern/MOD_nodes_evaluator.cc | 1 + .../shader/nodes/node_shader_tex_voronoi.cc | 911 +++++++++++++++++- 10 files changed, 2016 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 04e9fba02f2..26d586ac859 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -720,6 +720,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ NodeItem("ShaderNodeTexGradient"), NodeItem("ShaderNodeTexNoise"), + NodeItem("ShaderNodeTexVoronoi"), NodeItem("ShaderNodeTexWhiteNoise"), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ diff --git a/source/blender/blenlib/BLI_float2.hh b/source/blender/blenlib/BLI_float2.hh index cf6e00ba938..bb4229db86e 100644 --- a/source/blender/blenlib/BLI_float2.hh +++ b/source/blender/blenlib/BLI_float2.hh @@ -115,6 +115,11 @@ struct float2 { return {a.x - b.x, a.y - b.y}; } + friend float2 operator-(const float2 &a, const float &b) + { + return {a.x - b, a.y - b}; + } + friend float2 operator*(const float2 &a, float b) { return {a.x * b, a.y * b}; @@ -137,6 +142,26 @@ struct float2 { return stream; } + static float2 safe_divide(const float2 &a, const float b) + { + return (b != 0.0f) ? a / b : float2(0.0f); + } + + static float2 floor(const float2 &a) + { + return float2(floorf(a.x), floorf(a.y)); + } + + /** + * Returns a normalized vector. The original vector is not changed. + */ + float2 normalized() const + { + float2 result; + normalize_v2_v2(result, *this); + return result; + } + static float dot(const float2 &a, const float2 &b) { return a.x * b.x + a.y * b.y; diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index 04aae375889..8263ef72584 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -80,6 +80,11 @@ struct float3 { return {-a.x, -a.y, -a.z}; } + friend float3 operator-(const float3 &a, const float &b) + { + return {a.x - b, a.y - b, a.z - b}; + } + float3 &operator-=(const float3 &b) { this->x -= b.x; @@ -218,6 +223,16 @@ struct float3 { return result; } + static float3 safe_divide(const float3 &a, const float b) + { + return (b != 0.0f) ? a / b : float3(0.0f); + } + + static float3 floor(const float3 &a) + { + return float3(floorf(a.x), floorf(a.y), floorf(a.z)); + } + void invert() { x = -x; diff --git a/source/blender/blenlib/BLI_float4.hh b/source/blender/blenlib/BLI_float4.hh index b1feee3121b..5b487f6d029 100644 --- a/source/blender/blenlib/BLI_float4.hh +++ b/source/blender/blenlib/BLI_float4.hh @@ -44,6 +44,11 @@ struct float4 { return &x; } + friend float4 operator+(const float4 &a, const float &b) + { + return {a.x + b, a.y + b, a.z + b, a.w + b}; + } + operator const float *() const { return &x; @@ -58,11 +63,27 @@ struct float4 { return *this; } + friend float4 operator-(const float4 &a, const float4 &b) + { + return {a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w}; + } + + friend float4 operator-(const float4 &a, const float &b) + { + return {a.x - b, a.y - b, a.z - b, a.w - b}; + } + friend float4 operator+(const float4 &a, const float4 &b) { return {a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w}; } + friend float4 operator/(const float4 &a, float f) + { + BLI_assert(f != 0.0f); + return a * (1.0f / f); + } + float4 &operator*=(float factor) { x *= factor; @@ -81,6 +102,37 @@ struct float4 { { return b * a; } + + float length() const + { + return len_v4(*this); + } + + static float distance(const float4 &a, const float4 &b) + { + return (a - b).length(); + } + + static float4 safe_divide(const float4 &a, const float b) + { + return (b != 0.0f) ? a / b : float4(0.0f); + } + + static float4 interpolate(const float4 &a, const float4 &b, float t) + { + return a * (1 - t) + b * t; + } + + static float4 floor(const float4 &a) + { + return float4(floorf(a.x), floorf(a.y), floorf(a.z), floorf(a.w)); + } + + static float4 normalize(const float4 &a) + { + const float t = len_v4(a); + return (t != 0.0f) ? a / t : float4(0.0f); + } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh index 12e7aa57ab0..93980e3569e 100644 --- a/source/blender/blenlib/BLI_noise.hh +++ b/source/blender/blenlib/BLI_noise.hh @@ -112,4 +112,98 @@ float3 perlin_float3_fractal_distorted(float4 position, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Voronoi Noise + * \{ */ + +void voronoi_f1( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w); +void voronoi_smooth_f1(const float w, + const float smoothness, + const float randomness, + float *r_distance, + float3 *r_color, + float *r_w); +void voronoi_f2( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w); +void voronoi_distance_to_edge(const float w, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float w, const float randomness, float *r_radius); + +void voronoi_f1(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position); +void voronoi_smooth_f1(const float2 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position); +void voronoi_f2(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position); +void voronoi_distance_to_edge(const float2 coord, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float2 coord, const float randomness, float *r_radius); + +void voronoi_f1(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position); +void voronoi_smooth_f1(const float3 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position); +void voronoi_f2(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position); +void voronoi_distance_to_edge(const float3 coord, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float3 coord, const float randomness, float *r_radius); + +void voronoi_f1(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position); +void voronoi_smooth_f1(const float4 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position); +void voronoi_f2(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position); +void voronoi_distance_to_edge(const float4 coord, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float4 coord, const float randomness, float *r_radius); + +/** \} */ + } // namespace blender::noise diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 49f9faf1704..f609d5f8e8b 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -511,6 +511,22 @@ MINLINE float smoothminf(float a, float b, float c) } } +MINLINE float smoothstep(float edge0, float edge1, float x) +{ + float result; + if (x < edge0) { + result = 0.0f; + } + else if (x >= edge1) { + result = 1.0f; + } + else { + float t = (x - edge0) / (edge1 - edge0); + result = (3.0f - 2.0f * t) * (t * t); + } + return result; +} + MINLINE double min_dd(double a, double b) { return (a < b) ? a : b; diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 8be066bb0e9..bb32b511005 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -1145,6 +1145,19 @@ MINLINE float len_v3v3(const float a[3], const float b[3]) return len_v3(d); } +MINLINE float len_v4(const float a[4]) +{ + return sqrtf(dot_v4v4(a, a)); +} + +MINLINE float len_v4v4(const float a[4], const float b[4]) +{ + float d[4]; + + sub_v4_v4v4(d, b, a); + return len_v4(d); +} + /** * \note any vectors containing `nan` will be zeroed out. */ diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index ce2e9594059..a6c3377b71f 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -52,6 +52,7 @@ #include "BLI_float2.hh" #include "BLI_float3.hh" #include "BLI_float4.hh" +#include "BLI_math_base_safe.h" #include "BLI_noise.hh" #include "BLI_utildefines.h" @@ -755,4 +756,893 @@ float3 perlin_float3_fractal_distorted(float4 position, perlin_fractal(position + random_float4_offset(5.0f), octaves, roughness)); } +/* + * Voronoi: Ported from Cycles code. + * + * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez. + * + * Smooth Voronoi: + * + * - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi + * + * Distance To Edge based on: + * + * - https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm + * - https://www.shadertoy.com/view/ldl3W8 + * + * With optimization to change -2..2 scan window to -1..1 for better performance, + * as explained in https://www.shadertoy.com/view/llG3zy. + */ + +/* **** 1D Voronoi **** */ + +/* Ensure to align with DNA. */ +enum { + NOISE_SHD_VORONOI_EUCLIDEAN = 0, + NOISE_SHD_VORONOI_MANHATTAN = 1, + NOISE_SHD_VORONOI_CHEBYCHEV = 2, + NOISE_SHD_VORONOI_MINKOWSKI = 3, +}; + +BLI_INLINE float voronoi_distance(const float a, const float b) +{ + return fabsf(b - a); +} + +void voronoi_f1( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float minDistance = 8.0f; + float targetOffset = 0.0f; + float targetPosition = 0.0f; + for (int i = -1; i <= 1; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + *r_distance = minDistance; + *r_color = hash_float_to_float3(cellPosition + targetOffset); + *r_w = targetPosition + cellPosition; +} + +void voronoi_smooth_f1(const float w, + const float smoothness, + const float randomness, + float *r_distance, + float3 *r_color, + float *r_w) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float smoothDistance = 8.0f; + float smoothPosition = 0.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + for (int i = -2; i <= 2; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance(pointPosition, localPosition); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + correctionFactor /= 1.0f + 3.0f * smoothness; + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor; + } + *r_distance = smoothDistance; + *r_color = smoothColor; + *r_w = cellPosition + smoothPosition; +} + +void voronoi_f2( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float offsetF1 = 0.0f; + float positionF1 = 0.0f; + float offsetF2 = 0.0f; + float positionF2 = 0.0f; + for (int i = -1; i <= 1; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance(pointPosition, localPosition); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + *r_distance = distanceF2; + *r_color = hash_float_to_float3(cellPosition + offsetF2); + *r_w = positionF2 + cellPosition; +} + +void voronoi_distance_to_edge(const float w, const float randomness, float *r_distance) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + const float midPointPosition = hash_float_to_float(cellPosition) * randomness; + const float leftPointPosition = -1.0f + hash_float_to_float(cellPosition - 1.0f) * randomness; + const float rightPointPosition = 1.0f + hash_float_to_float(cellPosition + 1.0f) * randomness; + const float distanceToMidLeft = fabsf((midPointPosition + leftPointPosition) / 2.0f - + localPosition); + const float distanceToMidRight = fabsf((midPointPosition + rightPointPosition) / 2.0f - + localPosition); + + *r_distance = std::min(distanceToMidLeft, distanceToMidRight); +} + +void voronoi_n_sphere_radius(const float w, const float randomness, float *r_radius) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float closestPoint = 0.0f; + float closestPointOffset = 0.0f; + float minDistance = 8.0f; + for (int i = -1; i <= 1; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = fabsf(pointPosition - localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + + minDistance = 8.0f; + float closestPointToClosestPoint = 0.0f; + for (int i = -1; i <= 1; i++) { + if (i == 0) { + continue; + } + const float cellOffset = i + closestPointOffset; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = fabsf(closestPoint - pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + *r_radius = fabsf(closestPointToClosestPoint - closestPoint) / 2.0f; +} + +/* **** 2D Voronoi **** */ + +static float voronoi_distance(const float2 a, + const float2 b, + const int metric, + const float exponent) +{ + switch (metric) { + case NOISE_SHD_VORONOI_EUCLIDEAN: + return float2::distance(a, b); + case NOISE_SHD_VORONOI_MANHATTAN: + return fabsf(a.x - b.x) + fabsf(a.y - b.y); + case NOISE_SHD_VORONOI_CHEBYCHEV: + return std::max(fabsf(a.x - b.x), fabsf(a.y - b.y)); + case NOISE_SHD_VORONOI_MINKOWSKI: + return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent), + 1.0f / exponent); + default: + BLI_assert_unreachable(); + break; + } + return 0.0f; +} + +void voronoi_f1(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float minDistance = 8.0f; + float2 targetOffset = float2(0.0f, 0.0f); + float2 targetPosition = float2(0.0f, 0.0f); + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + } + *r_distance = minDistance; + *r_color = hash_float_to_float3(cellPosition + targetOffset); + *r_position = targetPosition + cellPosition; +} + +void voronoi_smooth_f1(const float2 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float smoothDistance = 8.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + float2 smoothPosition = float2(0.0f, 0.0f); + for (int j = -2; j <= 2; j++) { + for (int i = -2; i <= 2; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + correctionFactor /= 1.0f + 3.0f * smoothness; + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + smoothPosition = float2::interpolate(smoothPosition, pointPosition, h) - correctionFactor; + } + } + *r_distance = smoothDistance; + *r_color = smoothColor; + *r_position = cellPosition + smoothPosition; +} + +void voronoi_f2(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float2 offsetF1 = float2(0.0f, 0.0f); + float2 positionF1 = float2(0.0f, 0.0f); + float2 offsetF2 = float2(0.0f, 0.0f); + float2 positionF2 = float2(0.0f, 0.0f); + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + } + *r_distance = distanceF2; + *r_color = hash_float_to_float3(cellPosition + offsetF2); + *r_position = positionF2 + cellPosition; +} + +void voronoi_distance_to_edge(const float2 coord, const float randomness, float *r_distance) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float2 vectorToClosest = float2(0.0f, 0.0f); + float minDistance = 8.0f; + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 vectorToPoint = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness - + localPosition; + const float distanceToPoint = dot_v2v2(vectorToPoint, vectorToPoint); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + vectorToClosest = vectorToPoint; + } + } + } + + minDistance = 8.0f; + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 vectorToPoint = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness - + localPosition; + const float2 perpendicularToEdge = vectorToPoint - vectorToClosest; + if (dot_v2v2(perpendicularToEdge, perpendicularToEdge) > 0.0001f) { + const float distanceToEdge = dot_v2v2((vectorToClosest + vectorToPoint) / 2.0f, + perpendicularToEdge.normalized()); + minDistance = std::min(minDistance, distanceToEdge); + } + } + } + *r_distance = minDistance; +} + +void voronoi_n_sphere_radius(const float2 coord, const float randomness, float *r_radius) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float2 closestPoint = float2(0.0f, 0.0f); + float2 closestPointOffset = float2(0.0f, 0.0f); + float minDistance = 8.0f; + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float2::distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + } + + minDistance = 8.0f; + float2 closestPointToClosestPoint = float2(0.0f, 0.0f); + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0) { + continue; + } + const float2 cellOffset = float2(i, j) + closestPointOffset; + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float2::distance(closestPoint, pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + } + *r_radius = float2::distance(closestPointToClosestPoint, closestPoint) / 2.0f; +} + +/* **** 3D Voronoi **** */ + +static float voronoi_distance(const float3 a, + const float3 b, + const int metric, + const float exponent) +{ + switch (metric) { + case NOISE_SHD_VORONOI_EUCLIDEAN: + return float3::distance(a, b); + case NOISE_SHD_VORONOI_MANHATTAN: + return fabsf(a.x - b.x) + fabsf(a.y - b.y) + fabsf(a.z - b.z); + case NOISE_SHD_VORONOI_CHEBYCHEV: + return std::max(fabsf(a.x - b.x), std::max(fabsf(a.y - b.y), fabsf(a.z - b.z))); + case NOISE_SHD_VORONOI_MINKOWSKI: + return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent) + + powf(fabsf(a.z - b.z), exponent), + 1.0f / exponent); + default: + BLI_assert_unreachable(); + break; + } + return 0.0f; +} + +void voronoi_f1(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float minDistance = 8.0f; + float3 targetOffset = float3(0.0f, 0.0f, 0.0f); + float3 targetPosition = float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + } + } + *r_distance = minDistance; + *r_color = hash_float_to_float3(cellPosition + targetOffset); + *r_position = targetPosition + cellPosition; +} + +void voronoi_smooth_f1(const float3 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float smoothDistance = 8.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + float3 smoothPosition = float3(0.0f, 0.0f, 0.0f); + for (int k = -2; k <= 2; k++) { + for (int j = -2; j <= 2; j++) { + for (int i = -2; i <= 2; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + correctionFactor /= 1.0f + 3.0f * smoothness; + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + smoothPosition = float3::interpolate(smoothPosition, pointPosition, h) - correctionFactor; + } + } + } + *r_distance = smoothDistance; + *r_color = smoothColor; + *r_position = cellPosition + smoothPosition; +} + +void voronoi_f2(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float3 offsetF1 = float3(0.0f, 0.0f, 0.0f); + float3 positionF1 = float3(0.0f, 0.0f, 0.0f); + float3 offsetF2 = float3(0.0f, 0.0f, 0.0f); + float3 positionF2 = float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + } + } + *r_distance = distanceF2; + *r_color = hash_float_to_float3(cellPosition + offsetF2); + *r_position = positionF2 + cellPosition; +} + +void voronoi_distance_to_edge(const float3 coord, const float randomness, float *r_distance) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float3 vectorToClosest = float3(0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 vectorToPoint = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness - + localPosition; + const float distanceToPoint = dot_v3v3(vectorToPoint, vectorToPoint); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + vectorToClosest = vectorToPoint; + } + } + } + } + + minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 vectorToPoint = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness - + localPosition; + const float3 perpendicularToEdge = vectorToPoint - vectorToClosest; + if (dot_v3v3(perpendicularToEdge, perpendicularToEdge) > 0.0001f) { + const float distanceToEdge = dot_v3v3((vectorToClosest + vectorToPoint) / 2.0f, + perpendicularToEdge.normalized()); + minDistance = std::min(minDistance, distanceToEdge); + } + } + } + } + *r_distance = minDistance; +} + +void voronoi_n_sphere_radius(const float3 coord, const float randomness, float *r_radius) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float3 closestPoint = float3(0.0f, 0.0f, 0.0f); + float3 closestPointOffset = float3(0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float3::distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + } + } + + minDistance = 8.0f; + float3 closestPointToClosestPoint = float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0 && k == 0) { + continue; + } + const float3 cellOffset = float3(i, j, k) + closestPointOffset; + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float3::distance(closestPoint, pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + } + } + *r_radius = float3::distance(closestPointToClosestPoint, closestPoint) / 2.0f; +} + +/* **** 4D Voronoi **** */ + +static float voronoi_distance(const float4 a, + const float4 b, + const int metric, + const float exponent) +{ + switch (metric) { + case NOISE_SHD_VORONOI_EUCLIDEAN: + return float4::distance(a, b); + case NOISE_SHD_VORONOI_MANHATTAN: + return fabsf(a.x - b.x) + fabsf(a.y - b.y) + fabsf(a.z - b.z) + fabsf(a.w - b.w); + case NOISE_SHD_VORONOI_CHEBYCHEV: + return std::max(fabsf(a.x - b.x), + std::max(fabsf(a.y - b.y), std::max(fabsf(a.z - b.z), fabsf(a.w - b.w)))); + case NOISE_SHD_VORONOI_MINKOWSKI: + return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent) + + powf(fabsf(a.z - b.z), exponent) + powf(fabsf(a.w - b.w), exponent), + 1.0f / exponent); + default: + BLI_assert_unreachable(); + break; + } + return 0.0f; +} + +void voronoi_f1(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float minDistance = 8.0f; + float4 targetOffset = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 targetPosition = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + } + } + } + *r_distance = minDistance; + *r_color = hash_float_to_float3(cellPosition + targetOffset); + *r_position = targetPosition + cellPosition; +} + +void voronoi_smooth_f1(const float4 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float smoothDistance = 8.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + float4 smoothPosition = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -2; u <= 2; u++) { + for (int k = -2; k <= 2; k++) { + for (int j = -2; j <= 2; j++) { + for (int i = -2; i <= 2; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + correctionFactor /= 1.0f + 3.0f * smoothness; + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + smoothPosition = float4::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } + } + } + *r_distance = smoothDistance; + *r_color = smoothColor; + *r_position = cellPosition + smoothPosition; +} + +void voronoi_f2(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float4 offsetF1 = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 positionF1 = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 offsetF2 = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 positionF2 = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + } + } + } + *r_distance = distanceF2; + *r_color = hash_float_to_float3(cellPosition + offsetF2); + *r_position = positionF2 + cellPosition; +} + +void voronoi_distance_to_edge(const float4 coord, const float randomness, float *r_distance) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float4 vectorToClosest = float4(0.0f, 0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 vectorToPoint = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness - + localPosition; + const float distanceToPoint = dot_v4v4(vectorToPoint, vectorToPoint); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + vectorToClosest = vectorToPoint; + } + } + } + } + } + + minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 vectorToPoint = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness - + localPosition; + const float4 perpendicularToEdge = vectorToPoint - vectorToClosest; + if (dot_v4v4(perpendicularToEdge, perpendicularToEdge) > 0.0001f) { + const float distanceToEdge = dot_v4v4((vectorToClosest + vectorToPoint) / 2.0f, + float4::normalize(perpendicularToEdge)); + minDistance = std::min(minDistance, distanceToEdge); + } + } + } + } + } + *r_distance = minDistance; +} + +void voronoi_n_sphere_radius(const float4 coord, const float randomness, float *r_radius) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float4 closestPoint = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 closestPointOffset = float4(0.0f, 0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = float4::distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + } + } + } + + minDistance = 8.0f; + float4 closestPointToClosestPoint = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0 && k == 0 && u == 0) { + continue; + } + const float4 cellOffset = float4(i, j, k, u) + closestPointOffset; + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = float4::distance(closestPoint, pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + } + } + } + *r_radius = float4::distance(closestPointToClosestPoint, closestPoint) / 2.0f; +} + } // namespace blender::noise diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index e0803e546b3..3c51ef83311 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -333,6 +333,7 @@ static void get_socket_value(const SocketRef &socket, void *r_value) GEO_NODE_SET_POSITION, SH_NODE_TEX_GRADIENT, SH_NODE_TEX_NOISE, + SH_NODE_TEX_VORONOI, SH_NODE_TEX_WHITE_NOISE, GEO_NODE_MESH_TO_POINTS, GEO_NODE_PROXIMITY)) { diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index e12e5724e8e..07904123ace 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -19,12 +19,14 @@ #include "../node_shader_util.h" +#include "BLI_noise.hh" + namespace blender::nodes { static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").hide_value(); + b.add_input("Vector").hide_value().implicit_field(); b.add_input("W").min(-1000.0f).max(1000.0f); b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); b.add_input("Smoothness") @@ -143,6 +145,7 @@ static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 && !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); nodeSetSocketAvailability(inSmoothnessSock, tex->feature == SHD_VORONOI_SMOOTH_F1); + nodeSetSocketAvailability(outDistanceSock, tex->feature != SHD_VORONOI_N_SPHERE_RADIUS); nodeSetSocketAvailability(outColorSock, tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && @@ -158,17 +161,921 @@ static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node nodeSetSocketAvailability(outRadiusSock, tex->feature == SHD_VORONOI_N_SPHERE_RADIUS); } +namespace blender::nodes { + +class VoronoiMinowskiFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + + public: + VoronoiMinowskiFunction(int dimensions, int feature) : dimensions_(dimensions), feature_(feature) + { + BLI_assert(dimensions >= 2 && dimensions <= 4); + BLI_assert(feature >= 0 && feature <= 2); + static std::array signatures{ + create_signature(2, SHD_VORONOI_F1), + create_signature(3, SHD_VORONOI_F1), + create_signature(4, SHD_VORONOI_F1), + + create_signature(2, SHD_VORONOI_F2), + create_signature(3, SHD_VORONOI_F2), + create_signature(4, SHD_VORONOI_F2), + + create_signature(2, SHD_VORONOI_SMOOTH_F1), + create_signature(3, SHD_VORONOI_SMOOTH_F1), + create_signature(4, SHD_VORONOI_SMOOTH_F1), + }; + this->set_signature(&signatures[(dimensions - 1) + feature * 3 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_minowski"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + if (feature == SHD_VORONOI_SMOOTH_F1) { + signature.single_input("Smoothness"); + } + signature.single_input("Exponent"); + signature.single_input("Randomness"); + signature.single_output("Distance"); + signature.single_output("Color"); + + if (dimensions != 1) { + signature.single_output("Position"); + } + if ((dimensions == 1 || dimensions == 4)) { + signature.single_output("W"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_smoothness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Smoothness"); + }; + auto get_exponent = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Exponent"); + }; + auto get_randomness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Distance"); + }; + auto get_r_color = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Color"); + }; + auto get_r_position = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Position"); + }; + auto get_r_w = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "W"); + }; + + int param = 0; + switch (dimensions_) { + case 2: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f1(float2(vector[i].x, vector[i].y) * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f2(float2(vector[i].x, vector[i].y) * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_smooth_f1(float2(vector[i].x, vector[i].y) * scale[i], + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + } + break; + } + case 3: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1(vector[i] * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2(vector[i] * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(vector[i] * scale[i], + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + } + break; + } + case 4: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f1(p, exponent[i], rand, SHD_VORONOI_F1, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f2( + p, exponent[i], rand, SHD_VORONOI_MINKOWSKI, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_smooth_f1( + p, smth, exponent[i], rand, SHD_VORONOI_MINKOWSKI, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + } + break; + } + } + } +}; + +class VoronoiMetricFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + int metric_; + + public: + VoronoiMetricFunction(int dimensions, int feature, int metric) + : dimensions_(dimensions), feature_(feature), metric_(metric) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(feature >= 0 && feature <= 4); + static std::array signatures{ + create_signature(1, SHD_VORONOI_F1), + create_signature(2, SHD_VORONOI_F1), + create_signature(3, SHD_VORONOI_F1), + create_signature(4, SHD_VORONOI_F1), + + create_signature(1, SHD_VORONOI_F2), + create_signature(2, SHD_VORONOI_F2), + create_signature(3, SHD_VORONOI_F2), + create_signature(4, SHD_VORONOI_F2), + + create_signature(1, SHD_VORONOI_SMOOTH_F1), + create_signature(2, SHD_VORONOI_SMOOTH_F1), + create_signature(3, SHD_VORONOI_SMOOTH_F1), + create_signature(4, SHD_VORONOI_SMOOTH_F1), + }; + this->set_signature(&signatures[dimensions + feature * 4 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_metric"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + if (feature == SHD_VORONOI_SMOOTH_F1) { + signature.single_input("Smoothness"); + } + signature.single_input("Randomness"); + signature.single_output("Distance"); + signature.single_output("Color"); + + if (dimensions != 1) { + signature.single_output("Position"); + } + if ((dimensions == 1 || dimensions == 4)) { + signature.single_output("W"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_smoothness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Smoothness"); + }; + auto get_randomness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Distance"); + }; + auto get_r_color = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Color"); + }; + auto get_r_position = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Position"); + }; + auto get_r_w = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "W"); + }; + + int param = 0; + switch (dimensions_) { + case 1: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1(p, rand, &r_distance[i], &col, &r_w[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_w[i] = safe_divide(r_w[i], scale[i]); + } + break; + } + case SHD_VORONOI_F2: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2(p, rand, &r_distance[i], &col, &r_w[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_w[i] = safe_divide(r_w[i], scale[i]); + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(p, smth, rand, &r_distance[i], &col, &r_w[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_w[i] = safe_divide(r_w[i], scale[i]); + } + break; + } + } + break; + } + case 2: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f1(float2(vector[i].x, vector[i].y) * scale[i], + 0.0f, + rand, + metric_, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f2(float2(vector[i].x, vector[i].y) * scale[i], + 0.0f, + rand, + metric_, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_smooth_f1(float2(vector[i].x, vector[i].y) * scale[i], + smth, + 0.0f, + rand, + metric_, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + } + break; + } + case 3: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1( + vector[i] * scale[i], 0.0f, rand, metric_, &r_distance[i], &col, &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2( + vector[i] * scale[i], 0.0f, rand, metric_, &r_distance[i], &col, &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(vector[i] * scale[i], + smth, + 0.0f, + rand, + metric_, + &r_distance[i], + &col, + &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + } + break; + } + case 4: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f1(p, 0.0f, rand, metric_, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f2(p, 0.0f, rand, metric_, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_smooth_f1(p, smth, 0.0f, rand, metric_, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + } + break; + } + } + } +}; + +class VoronoiEdgeFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + + public: + VoronoiEdgeFunction(int dimensions, int feature) : dimensions_(dimensions), feature_(feature) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(feature >= 3 && feature <= 4); + static std::array signatures{ + create_signature(1, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(2, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(3, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(4, SHD_VORONOI_DISTANCE_TO_EDGE), + + create_signature(1, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(2, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(3, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(4, SHD_VORONOI_N_SPHERE_RADIUS), + }; + this->set_signature(&signatures[dimensions + (feature - 3) * 4 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_edge"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + signature.single_input("Randomness"); + + if (feature == SHD_VORONOI_DISTANCE_TO_EDGE) { + signature.single_output("Distance"); + } + if (feature == SHD_VORONOI_N_SPHERE_RADIUS) { + signature.single_output("Radius"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_randomness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Distance"); + }; + auto get_r_radius = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Radius"); + }; + + int param = 0; + switch (dimensions_) { + case 1: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float p = w[i] * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float p = w[i] * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + case 2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float2 p = float2(vector[i].x, vector[i].y) * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float2 p = float2(vector[i].x, vector[i].y) * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + case 3: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + noise::voronoi_distance_to_edge(vector[i] * scale[i], rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + noise::voronoi_n_sphere_radius(vector[i] * scale[i], rand, &r_radius[i]); + } + break; + } + } + break; + } + case 4: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + } + }; +}; + +static void sh_node_voronoi_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexVoronoi *tex = (NodeTexVoronoi *)node.storage; + bool minowski = (tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 && + !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); + bool dist_radius = (tex->feature == SHD_VORONOI_DISTANCE_TO_EDGE || + tex->feature == SHD_VORONOI_N_SPHERE_RADIUS); + if (dist_radius) { + builder.construct_and_set_matching_fn(tex->dimensions, tex->feature); + } + else if (minowski) { + builder.construct_and_set_matching_fn(tex->dimensions, tex->feature); + } + else { + builder.construct_and_set_matching_fn( + tex->dimensions, tex->feature, tex->distance); + } +} + +} // namespace blender::nodes + void register_node_type_sh_tex_voronoi(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0); ntype.declare = blender::nodes::sh_node_tex_voronoi_declare; node_type_init(&ntype, node_shader_init_tex_voronoi); node_type_storage( &ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_voronoi); node_type_update(&ntype, node_shader_update_tex_voronoi); + ntype.build_multi_function = blender::nodes::sh_node_voronoi_build_multi_function; nodeRegisterType(&ntype); } From 7bf9c70b1402ba8d11889ea1adaca688bc58bb21 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 15 Oct 2021 16:57:15 +0200 Subject: [PATCH 0831/1500] Fix T92083: Crash renaming bone used in Armature modifier on curve This is caused by {rB3b6ee8cee708}. Since rigging curves with armatures only works with envelopes (if I am not mistaken), this stirs up the question again why we actually give the choice for vertex groups in parenting. Anyways, curves can have armature modifiers and renaming bones should not crash. Now make sure we only go down the route of `BKE_object_defgroup_list` and `BKE_object_defgroup_find_name` if vertex groups are actually supported. Maniphest Tasks: T92083 Differential Revision: https://developer.blender.org/D12876 --- source/blender/editors/armature/armature_naming.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 35bd30377c8..de1c14a15ce 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -265,7 +265,7 @@ void ED_armature_bone_rename(Main *bmain, } } - if (BKE_modifiers_uses_armature(ob, arm)) { + if (BKE_modifiers_uses_armature(ob, arm) && BKE_object_supports_vertex_groups(ob)) { bDeformGroup *dg = BKE_object_defgroup_find_name(ob, oldname); if (dg) { BLI_strncpy(dg->name, newname, MAXBONENAME); From b66b3f547c43e841a7d5da0ecb2c911628339f56 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 11 Oct 2021 20:05:25 +0200 Subject: [PATCH 0832/1500] Fix T92032: Cycles panoramic cameras do not support shift --- intern/cycles/blender/blender_camera.cpp | 57 ++++++++++++++++++------ 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index 93f19b73f53..52acc2573f5 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -80,8 +80,9 @@ struct BlenderCamera { int render_height; BoundBox2D border; - BoundBox2D pano_viewplane; BoundBox2D viewport_camera_border; + BoundBox2D pano_viewplane; + float pano_aspectratio; float passepartout_alpha; @@ -123,10 +124,11 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende bcam->motion_position = Camera::MOTION_POSITION_CENTER; bcam->border.right = 1.0f; bcam->border.top = 1.0f; - bcam->pano_viewplane.right = 1.0f; - bcam->pano_viewplane.top = 1.0f; bcam->viewport_camera_border.right = 1.0f; bcam->viewport_camera_border.top = 1.0f; + bcam->pano_viewplane.right = 1.0f; + bcam->pano_viewplane.top = 1.0f; + bcam->pano_aspectratio = 0.0f; bcam->passepartout_alpha = 0.5f; bcam->offscreen_dicing_scale = 1.0f; bcam->matrix = transform_identity(); @@ -358,9 +360,21 @@ static void blender_camera_viewplane(BlenderCamera *bcam, } if (bcam->type == CAMERA_PANORAMA) { - /* set viewplane */ + /* Set viewplane for panoramic camera. */ if (viewplane != NULL) { *viewplane = bcam->pano_viewplane; + + /* Modify viewplane for camera shift. */ + const float shift_factor = (bcam->pano_aspectratio == 0.0f) ? + 1.0f : + *aspectratio / bcam->pano_aspectratio; + const float dx = bcam->shift.x * shift_factor; + const float dy = bcam->shift.y * shift_factor; + + viewplane->left += dx; + viewplane->right += dx; + viewplane->bottom += dy; + viewplane->top += dy; } } else { @@ -375,8 +389,8 @@ static void blender_camera_viewplane(BlenderCamera *bcam, *viewplane = (*viewplane) * bcam->zoom; /* modify viewplane with camera shift and 3d camera view offset */ - float dx = 2.0f * (*aspectratio * bcam->shift.x + bcam->offset.x * xaspect * 2.0f); - float dy = 2.0f * (*aspectratio * bcam->shift.y + bcam->offset.y * yaspect * 2.0f); + const float dx = 2.0f * (*aspectratio * bcam->shift.x + bcam->offset.x * xaspect * 2.0f); + const float dy = 2.0f * (*aspectratio * bcam->shift.y + bcam->offset.y * yaspect * 2.0f); viewplane->left += dx; viewplane->right += dx; @@ -652,7 +666,8 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine, int width, int height, BoundBox2D *view_box, - BoundBox2D *cam_box); + BoundBox2D *cam_box, + float *view_aspect); static void blender_camera_from_view(BlenderCamera *bcam, BL::RenderEngine &b_engine, @@ -682,6 +697,7 @@ static void blender_camera_from_view(BlenderCamera *bcam, if (!skip_panorama && bcam->type == CAMERA_PANORAMA) { /* in panorama camera view, we map viewplane to camera border */ BoundBox2D view_box, cam_box; + float view_aspect; BL::RenderSettings b_render_settings(b_scene.render()); blender_camera_view_subset(b_engine, @@ -693,9 +709,11 @@ static void blender_camera_from_view(BlenderCamera *bcam, width, height, &view_box, - &cam_box); + &cam_box, + &view_aspect); bcam->pano_viewplane = view_box.make_relative_to(cam_box); + bcam->pano_aspectratio = view_aspect; } else { /* magic zoom formula */ @@ -743,17 +761,18 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine, int width, int height, BoundBox2D *view_box, - BoundBox2D *cam_box) + BoundBox2D *cam_box, + float *view_aspect) { BoundBox2D cam, view; - float view_aspect, cam_aspect, sensor_size; + float cam_aspect, sensor_size; /* Get viewport viewplane. */ BlenderCamera view_bcam; blender_camera_init(&view_bcam, b_render); blender_camera_from_view(&view_bcam, b_engine, b_scene, b_v3d, b_rv3d, width, height, true); - blender_camera_viewplane(&view_bcam, width, height, &view, &view_aspect, &sensor_size); + blender_camera_viewplane(&view_bcam, width, height, &view, view_aspect, &sensor_size); /* Get camera viewplane. */ BlenderCamera cam_bcam; @@ -768,7 +787,7 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine, &cam_bcam, cam_bcam.full_width, cam_bcam.full_height, &cam, &cam_aspect, &sensor_size); /* Return */ - *view_box = view * (1.0f / view_aspect); + *view_box = view * (1.0f / *view_aspect); *cam_box = cam * (1.0f / cam_aspect); } @@ -785,8 +804,18 @@ static void blender_camera_border_subset(BL::RenderEngine &b_engine, { /* Determine camera viewport subset. */ BoundBox2D view_box, cam_box; - blender_camera_view_subset( - b_engine, b_render, b_scene, b_ob, b_v3d, b_rv3d, width, height, &view_box, &cam_box); + float view_aspect; + blender_camera_view_subset(b_engine, + b_render, + b_scene, + b_ob, + b_v3d, + b_rv3d, + width, + height, + &view_box, + &cam_box, + &view_aspect); /* Determine viewport subset matching given border. */ cam_box = cam_box.make_relative_to(view_box); From 4ba72015465db2a163602fbc0001b4d8efaf69df Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 15 Oct 2021 18:51:02 +0200 Subject: [PATCH 0833/1500] Asset Browser: Hide Import Types menu for "Current File" asset library This menu doesn't have an effect on the importing while in the "Current File" asset library. This can be quite confusing. However, just hiding the menu may be a temporary solution. Decision actually to instead show a different menu, that allows choosing between duplicating and reusing data on drop. This is being reviewed here https://developer.blender.org/D12879. Meanwhile (or in case we end up rejecting that), this change should avoid some confusion. Differential Revision: https://developer.blender.org/D12752 --- release/scripts/startup/bl_ui/space_filebrowser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index a087361780c..f916a9988fd 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -38,7 +38,8 @@ class FILEBROWSER_HT_header(Header): layout.separator_spacer() - layout.prop(params, "import_type", text="") + if params.asset_library_ref != 'LOCAL': + layout.prop(params, "import_type", text="") layout.separator_spacer() From 1b6752e599b5ed70823d09f90c418e448516d4b4 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Oct 2021 18:55:06 +0200 Subject: [PATCH 0834/1500] Fix T62325, T91990: changing Cycles presets does not update the Blender UI Checking RNA_MAGIC is not enough to identify the ID property case which always needs updates. If the property is already resolved to an RNA property we need to check the flag too. --- source/blender/makesrna/intern/rna_access.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 40b5f3ed1da..88c3db0c65b 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -2199,7 +2199,11 @@ static void rna_property_update( * but this isn't likely to be a performance problem. */ bool RNA_property_update_check(PropertyRNA *prop) { - return (prop->magic != RNA_MAGIC || prop->update || prop->noteflag); + return + /* Always update ID properties. */ + (prop->magic != RNA_MAGIC || (prop->flag & PROP_IDPROPERTY)) || + /* For native RNA properties only update if there is a callback or notifier. */ + (prop->update || prop->noteflag); } void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop) From d9e697fbbdea9128dc3b0b3318c49eb061a1c884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 15 Oct 2021 18:44:00 +0200 Subject: [PATCH 0835/1500] Cleanup: remove accidental comment --- source/blender/blenkernel/BKE_asset_library.h | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_asset_library.h b/source/blender/blenkernel/BKE_asset_library.h index 70949f40430..12ca55c0ef4 100644 --- a/source/blender/blenkernel/BKE_asset_library.h +++ b/source/blender/blenkernel/BKE_asset_library.h @@ -21,7 +21,6 @@ #pragma once struct Main; -// #ifdef __cplusplus extern "C" { From 45e16a6c966eb0383d24840fd0fcd1314435eb54 Mon Sep 17 00:00:00 2001 From: Leon Leno Date: Fri, 15 Oct 2021 13:30:50 -0500 Subject: [PATCH 0836/1500] UI: Remove extra padding around curve widget This commit removes the constant padding around to the left and right of the curve widget. The padding worked in screen space and didn't take UI scale/zoom into account. This makes the curve widget consistent with the more recently added curve profile widget used for bevel profiles. Differential Revision: https://developer.blender.org/D12883 --- source/blender/editors/interface/interface_widgets.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 6727d812e1e..a26d409037d 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -4831,9 +4831,6 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu break; case UI_BTYPE_CURVE: - /* do not draw right to edge of rect */ - rect->xmin += (0.2f * UI_UNIT_X); - rect->xmax -= (0.2f * UI_UNIT_X); ui_draw_but_CURVE(region, but, &tui->wcol_regular, rect); break; From 4682aad43236a70be1d3c591b7cd73d79d9e2035 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 15 Oct 2021 13:54:08 -0500 Subject: [PATCH 0837/1500] Fix: Field type inference considers unavailable sockets If a node had unused/unavailable inputs, they were still considered when deciding whether the output is a field or not. --- source/blender/blenkernel/intern/node.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index e526475fa95..dedf65c7f64 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4858,6 +4858,9 @@ static void propagate_field_status_from_left_to_right( case OutputSocketFieldType::DependentField: { for (const InputSocketRef *input_socket : gather_input_socket_dependencies(field_dependency, *node)) { + if (!input_socket->is_available()) { + continue; + } if (!field_state_by_socket_id[input_socket->id()].is_single) { state.is_single = false; break; From 47a72ac4fdf777f2177037df3111a5cba2697bae Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 15 Oct 2021 13:57:00 -0500 Subject: [PATCH 0838/1500] Cleanup: Refactor use of implicit inputs in geometry nodes Instead of checking whether the socket value was hidden, use the proper node declaration to check whether the socket has an implicit input. The remaining larger change to make is allowing nodes to specify what their implicit input should actually be. --- .../modifiers/intern/MOD_nodes_evaluator.cc | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 3c51ef83311..20ee6127504 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -17,6 +17,7 @@ #include "MOD_nodes_evaluator.hh" #include "NOD_geometry_exec.hh" +#include "NOD_socket_declarations.hh" #include "NOD_type_conversions.hh" #include "DEG_depsgraph_query.h" @@ -321,41 +322,45 @@ static const CPPType *get_socket_cpp_type(const DSocket socket) return get_socket_cpp_type(*socket.socket_ref()); } -static void get_socket_value(const SocketRef &socket, void *r_value) +/** + * \note This is not supposed to be a long term solution. Eventually we want that nodes can + * specify more complex defaults (other than just single values) in their socket declarations. + */ +static bool get_implicit_socket_input(const SocketRef &socket, void *r_value) { - const bNodeSocket &bsocket = *socket.bsocket(); - /* This is not supposed to be a long term solution. Eventually we want that nodes can specify - * more complex defaults (other than just single values) in their socket declarations. */ - if (bsocket.flag & SOCK_HIDE_VALUE) { - const bNode &bnode = *socket.bnode(); - if (bsocket.type == SOCK_VECTOR) { - if (ELEM(bnode.type, - GEO_NODE_SET_POSITION, - SH_NODE_TEX_GRADIENT, - SH_NODE_TEX_NOISE, - SH_NODE_TEX_VORONOI, - SH_NODE_TEX_WHITE_NOISE, - GEO_NODE_MESH_TO_POINTS, - GEO_NODE_PROXIMITY)) { - new (r_value) Field(bke::AttributeFieldInput::Create("position")); - return; - } + const NodeRef &node = socket.node(); + const nodes::NodeDeclaration *node_declaration = node.declaration(); + if (node_declaration == nullptr) { + return false; + } + const nodes::SocketDeclaration &socket_declaration = *node_declaration->inputs()[socket.index()]; + if (socket_declaration.input_field_type() == nodes::InputSocketFieldType::Implicit) { + if (socket.typeinfo()->type == SOCK_VECTOR) { + const bNode &bnode = *socket.bnode(); if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) { StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode == GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : "handle_right"; new (r_value) Field(bke::AttributeFieldInput::Create(side)); - return; + return true; } + new (r_value) Field(bke::AttributeFieldInput::Create("position")); + return true; } - else if (bsocket.type == SOCK_INT) { - if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) { - new (r_value) Field(std::make_shared()); - return; - } + if (socket.typeinfo()->type == SOCK_INT) { + new (r_value) Field(std::make_shared()); + return true; } } + return false; +} + +static void get_socket_value(const SocketRef &socket, void *r_value) +{ + if (get_implicit_socket_input(socket, r_value)) { + return; + } const bNodeSocketType *typeinfo = socket.typeinfo(); typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value); From 76f386a37a9cf14ac729be048230f81a0a397fc8 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 15 Oct 2021 14:08:52 -0500 Subject: [PATCH 0839/1500] Geometry Nodes: Fields transfer attribute node This commit adds an updated version of the old attribute transfer node. It works like a function node, so it works in the context of a geometry, with a simple data output. The "Nearest" mode finds the nearest element of the specified domain on the target geometry and copies the value directly from the target input. The "Nearest Face Interpolated" finds the nearest point on anywhere on the surface of the target mesh and linearly interpolates the value on the target from the face's corners. The node also has a new "Index" mode, which can pick data from specific indices on the target geometry. The implicit default is to do a simple copy from the target geometry, but any indices could be used. It is also possible to use a single value for the index to to retrieve a single value from an attribute at a certain index. Differential Revision: https://developer.blender.org/D12785 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_mesh_sample.hh | 9 + source/blender/blenkernel/BKE_node.h | 3 +- .../blender/blenkernel/intern/mesh_sample.cc | 56 +- source/blender/blenkernel/intern/node.cc | 3 +- source/blender/makesdna/DNA_node_types.h | 16 + source/blender/makesrna/intern/rna_nodetree.c | 60 ++ source/blender/nodes/CMakeLists.txt | 2 +- source/blender/nodes/NOD_geometry.h | 3 +- source/blender/nodes/NOD_static_types.h | 1 + .../legacy/node_geo_attribute_transfer.cc | 2 +- .../nodes/legacy/node_geo_point_distribute.cc | 18 +- .../node_geo_distribute_points_on_faces.cc | 18 +- .../nodes/node_geo_transfer_attribute.cc | 832 ++++++++++++++++++ 14 files changed, 989 insertions(+), 35 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 26d586ac859..2d03fbddd48 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -653,6 +653,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCaptureAttribute"), NodeItem("GeometryNodeAttributeStatistic"), + NodeItem("GeometryNodeAttributeTransfer"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ NodeItem("ShaderNodeMixRGB"), diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh index 2fbf7372a09..4845876751b 100644 --- a/source/blender/blenkernel/BKE_mesh_sample.hh +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -44,17 +44,20 @@ void sample_point_attribute(const Mesh &mesh, Span looptri_indices, Span bary_coords, const GVArray &data_in, + const IndexMask mask, GMutableSpan data_out); void sample_corner_attribute(const Mesh &mesh, Span looptri_indices, Span bary_coords, const GVArray &data_in, + const IndexMask mask, GMutableSpan data_out); void sample_face_attribute(const Mesh &mesh, Span looptri_indices, const GVArray &data_in, + const IndexMask mask, GMutableSpan data_out); enum class eAttributeMapMode { @@ -83,6 +86,12 @@ class MeshAttributeInterpolator { const Span positions, const Span looptri_indices); + void sample_data(const GVArray &src, + const AttributeDomain domain, + const eAttributeMapMode mode, + const IndexMask mask, + const GMutableSpan dst); + void sample_attribute(const ReadAttributeLookup &src_attribute, OutputAttribute &dst_attribute, eAttributeMapMode mode); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index f352fa37eab..a68edfca2d3 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -897,7 +897,7 @@ bool BKE_node_is_connected_to_output(struct bNodeTree *ntree, struct bNode *node /* ************** COMMON NODES *************** */ #define NODE_UNDEFINED -2 /* node type is not registered */ -#define NODE_CUSTOM -1 /* for dynamically registered custom types */ +#define NODE_CUSTOM -1 /* for dynamically registered custom types */ #define NODE_GROUP 2 // #define NODE_FORLOOP 3 /* deprecated */ // #define NODE_WHILELOOP 4 /* deprecated */ @@ -1537,6 +1537,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ROTATE_INSTANCES 1122 #define GEO_NODE_SPLIT_EDGES 1123 #define GEO_NODE_MESH_TO_CURVE 1124 +#define GEO_NODE_TRANSFER_ATTRIBUTE 1125 /** \} */ diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc index 5388f6e530e..9842b3aff31 100644 --- a/source/blender/blenkernel/intern/mesh_sample.cc +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -29,12 +29,13 @@ BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, const Span looptri_indices, const Span bary_coords, const VArray &data_in, + const IndexMask mask, const MutableSpan data_out) { const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; - for (const int i : bary_coords.index_range()) { + for (const int i : mask) { const int looptri_index = looptri_indices[i]; const MLoopTri &looptri = looptris[looptri_index]; const float3 &bary_coord = bary_coords[i]; @@ -56,6 +57,7 @@ void sample_point_attribute(const Mesh &mesh, const Span looptri_indices, const Span bary_coords, const GVArray &data_in, + const IndexMask mask, const GMutableSpan data_out) { BLI_assert(data_out.size() == looptri_indices.size()); @@ -67,7 +69,7 @@ void sample_point_attribute(const Mesh &mesh, attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); sample_point_attribute( - mesh, looptri_indices, bary_coords, data_in.typed(), data_out.typed()); + mesh, looptri_indices, bary_coords, data_in.typed(), mask, data_out.typed()); }); } @@ -76,12 +78,13 @@ BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, const Span looptri_indices, const Span bary_coords, const VArray &data_in, + const IndexMask mask, const MutableSpan data_out) { const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; - for (const int i : bary_coords.index_range()) { + for (const int i : mask) { const int looptri_index = looptri_indices[i]; const MLoopTri &looptri = looptris[looptri_index]; const float3 &bary_coord = bary_coords[i]; @@ -103,6 +106,7 @@ void sample_corner_attribute(const Mesh &mesh, const Span looptri_indices, const Span bary_coords, const GVArray &data_in, + const IndexMask mask, const GMutableSpan data_out) { BLI_assert(data_out.size() == looptri_indices.size()); @@ -114,7 +118,7 @@ void sample_corner_attribute(const Mesh &mesh, attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); sample_corner_attribute( - mesh, looptri_indices, bary_coords, data_in.typed(), data_out.typed()); + mesh, looptri_indices, bary_coords, data_in.typed(), mask, data_out.typed()); }); } @@ -122,12 +126,13 @@ template void sample_face_attribute(const Mesh &mesh, const Span looptri_indices, const VArray &data_in, + const IndexMask mask, const MutableSpan data_out) { const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; - for (const int i : data_out.index_range()) { + for (const int i : mask) { const int looptri_index = looptri_indices[i]; const MLoopTri &looptri = looptris[looptri_index]; const int poly_index = looptri.poly; @@ -138,6 +143,7 @@ void sample_face_attribute(const Mesh &mesh, void sample_face_attribute(const Mesh &mesh, const Span looptri_indices, const GVArray &data_in, + const IndexMask mask, const GMutableSpan data_out) { BLI_assert(data_out.size() == looptri_indices.size()); @@ -147,7 +153,7 @@ void sample_face_attribute(const Mesh &mesh, const CPPType &type = data_in.type(); attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); - sample_face_attribute(mesh, looptri_indices, data_in.typed(), data_out.typed()); + sample_face_attribute(mesh, looptri_indices, data_in.typed(), mask, data_out.typed()); }); } @@ -215,22 +221,19 @@ Span MeshAttributeInterpolator::ensure_nearest_weights() return nearest_weights_; } -void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_attribute, - OutputAttribute &dst_attribute, - eAttributeMapMode mode) +void MeshAttributeInterpolator::sample_data(const GVArray &src, + const AttributeDomain domain, + const eAttributeMapMode mode, + const IndexMask mask, + const GMutableSpan dst) { - if (!src_attribute || !dst_attribute) { - return; - } - const GVArray &src_varray = *src_attribute.varray; - GMutableSpan dst_span = dst_attribute.as_span(); - if (src_varray.is_empty() || dst_span.is_empty()) { + if (src.is_empty() || dst.is_empty()) { return; } /* Compute barycentric coordinates only when they are needed. */ Span weights; - if (ELEM(src_attribute.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { + if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { switch (mode) { case eAttributeMapMode::INTERPOLATED: weights = ensure_barycentric_coords(); @@ -242,17 +245,17 @@ void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_ } /* Interpolate the source attributes on the surface. */ - switch (src_attribute.domain) { + switch (domain) { case ATTR_DOMAIN_POINT: { - sample_point_attribute(*mesh_, looptri_indices_, weights, src_varray, dst_span); + sample_point_attribute(*mesh_, looptri_indices_, weights, src, mask, dst); break; } case ATTR_DOMAIN_FACE: { - sample_face_attribute(*mesh_, looptri_indices_, src_varray, dst_span); + sample_face_attribute(*mesh_, looptri_indices_, src, mask, dst); break; } case ATTR_DOMAIN_CORNER: { - sample_corner_attribute(*mesh_, looptri_indices_, weights, src_varray, dst_span); + sample_corner_attribute(*mesh_, looptri_indices_, weights, src, mask, dst); break; } case ATTR_DOMAIN_EDGE: { @@ -266,4 +269,17 @@ void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_ } } +void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_attribute, + OutputAttribute &dst_attribute, + eAttributeMapMode mode) +{ + if (src_attribute && dst_attribute) { + this->sample_data(*src_attribute.varray, + src_attribute.domain, + mode, + IndexMask(dst_attribute->size()), + dst_attribute.as_span()); + } +} + } // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index dedf65c7f64..491e29b8c4d 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5712,6 +5712,7 @@ static void registerGeometryNodes() { register_node_type_geo_group(); + register_node_type_geo_legacy_attribute_transfer(); register_node_type_geo_legacy_curve_set_handles(); register_node_type_geo_legacy_attribute_proximity(); register_node_type_geo_legacy_attribute_randomize(); @@ -5741,7 +5742,6 @@ static void registerGeometryNodes() register_node_type_geo_attribute_remove(); register_node_type_geo_attribute_separate_xyz(); register_node_type_geo_attribute_statistic(); - register_node_type_geo_attribute_transfer(); register_node_type_geo_attribute_vector_math(); register_node_type_geo_attribute_vector_rotate(); register_node_type_geo_boolean(); @@ -5833,6 +5833,7 @@ static void registerGeometryNodes() register_node_type_geo_string_to_curves(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); + register_node_type_geo_transfer_attribute(); register_node_type_geo_transform(); register_node_type_geo_translate_instances(); register_node_type_geo_triangulate(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index affe017feed..d4a2b3449e5 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1512,6 +1512,16 @@ typedef struct NodeGeometryAttributeTransfer { uint8_t mapping; } NodeGeometryAttributeTransfer; +typedef struct NodeGeometryTransferAttribute { + /* CustomDataType. */ + int8_t data_type; + /* AttributeDomain. */ + int8_t domain; + /* GeometryNodeAttributeTransferMode. */ + uint8_t mode; + char _pad[1]; +} NodeGeometryTransferAttribute; + typedef struct NodeGeometryRaycast { /* GeometryNodeRaycastMapMode. */ uint8_t mapping; @@ -2174,6 +2184,12 @@ typedef enum GeometryNodeAttributeTransferMapMode { GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST = 1, } GeometryNodeAttributeTransferMapMode; +typedef enum GeometryNodeAttributeTransferMode { + GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, + GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1, + GEO_NODE_ATTRIBUTE_TRANSFER_INDEX = 2, +} GeometryNodeAttributeTransferMode; + typedef enum GeometryNodeRaycastMapMode { GEO_NODE_RAYCAST_INTERPOLATED = 0, GEO_NODE_RAYCAST_NEAREST = 1, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index f52173b45d4..3ca5e562a5e 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2181,6 +2181,19 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeFill_type_itemf(bContext return itemf_function_check(rna_enum_attribute_type_items, attribute_fill_type_supported); } +static bool transfer_attribute_type_supported(const EnumPropertyItem *item) +{ + return ELEM( + item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL, CD_PROP_INT32); +} + +static const EnumPropertyItem *rna_NodeGeometryTransferAttribute_type_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_type_items, transfer_attribute_type_supported); +} + static bool attribute_statistic_type_supported(const EnumPropertyItem *item) { return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3); @@ -10643,6 +10656,53 @@ static void def_geo_attribute_transfer(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_transfer_attribute(StructRNA *srna) +{ + static EnumPropertyItem mapping_items[] = { + {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED, + "NEAREST_FACE_INTERPOLATED", + 0, + "Nearest Face Interpolated", + "Transfer the attribute from the nearest face on a surface (loose points and edges are " + "ignored)"}, + {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST, + "NEAREST", + 0, + "Nearest", + "Transfer the element from the nearest element (using face and edge centers for the " + "distance computation)"}, + {GEO_NODE_ATTRIBUTE_TRANSFER_INDEX, + "INDEX", + 0, + "Index", + "Transfer the data from the element with the corresponding index in the target geometry"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryTransferAttribute", "storage"); + + prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, mapping_items); + RNA_def_property_ui_text(prop, "Mapping", "Mapping between geometries"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_NodeGeometryTransferAttribute_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", "The type for the source and result data"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "The domain to use on the target geometry"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_input_material(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 850c5fbffff..5a8430752ca 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -190,7 +190,6 @@ set(SRC geometry/nodes/legacy/node_geo_raycast.cc geometry/nodes/legacy/node_geo_select_by_material.cc geometry/nodes/legacy/node_geo_subdivision_surface.cc - geometry/nodes/node_geo_attribute_capture.cc geometry/nodes/node_geo_attribute_remove.cc geometry/nodes/node_geo_attribute_statistic.cc @@ -273,6 +272,7 @@ set(SRC geometry/nodes/node_geo_string_join.cc geometry/nodes/node_geo_string_to_curves.cc geometry/nodes/node_geo_switch.cc + geometry/nodes/node_geo_transfer_attribute.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_translate_instances.cc geometry/nodes/node_geo_triangulate.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 3b78912fbaa..d3cb9beef26 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -29,6 +29,7 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); void register_node_type_geo_custom_group(bNodeType *ntype); +void register_node_type_geo_legacy_attribute_transfer(void); void register_node_type_geo_legacy_curve_set_handles(void); void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); @@ -58,7 +59,6 @@ void register_node_type_geo_attribute_mix(void); void register_node_type_geo_attribute_remove(void); void register_node_type_geo_attribute_separate_xyz(void); void register_node_type_geo_attribute_statistic(void); -void register_node_type_geo_attribute_transfer(void); void register_node_type_geo_attribute_vector_math(void); void register_node_type_geo_attribute_vector_rotate(void); void register_node_type_geo_boolean(void); @@ -151,6 +151,7 @@ void register_node_type_geo_string_join(void); void register_node_type_geo_string_to_curves(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); +void register_node_type_geo_transfer_attribute(void); void register_node_type_geo_transform(void); void register_node_type_geo_translate_instances(void); void register_node_type_geo_triangulate(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index eefe901b589..9022e56c910 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -400,6 +400,7 @@ DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION" DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "") DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") +DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc index 0006905a445..6bd39bc9145 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc @@ -511,7 +511,7 @@ static void geo_node_attribute_transfer_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_attribute_transfer() +void register_node_type_geo_legacy_attribute_transfer() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc index f95b0da86ed..892bf12d9c0 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc @@ -252,18 +252,26 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, { switch (source_domain) { case ATTR_DOMAIN_POINT: { - bke::mesh_surface_sample::sample_point_attribute( - mesh, looptri_indices, bary_coords, source_data, output_data); + bke::mesh_surface_sample::sample_point_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); break; } case ATTR_DOMAIN_CORNER: { - bke::mesh_surface_sample::sample_corner_attribute( - mesh, looptri_indices, bary_coords, source_data, output_data); + bke::mesh_surface_sample::sample_corner_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); break; } case ATTR_DOMAIN_FACE: { bke::mesh_surface_sample::sample_face_attribute( - mesh, looptri_indices, source_data, output_data); + mesh, looptri_indices, source_data, IndexMask(output_data.size()), output_data); break; } default: { diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index dec86bd33d3..44beead86ad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -255,18 +255,26 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, { switch (source_domain) { case ATTR_DOMAIN_POINT: { - bke::mesh_surface_sample::sample_point_attribute( - mesh, looptri_indices, bary_coords, source_data, output_data); + bke::mesh_surface_sample::sample_point_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); break; } case ATTR_DOMAIN_CORNER: { - bke::mesh_surface_sample::sample_corner_attribute( - mesh, looptri_indices, bary_coords, source_data, output_data); + bke::mesh_surface_sample::sample_corner_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); break; } case ATTR_DOMAIN_FACE: { bke::mesh_surface_sample::sample_face_attribute( - mesh, looptri_indices, source_data, output_data); + mesh, looptri_indices, source_data, IndexMask(output_data.size()), output_data); break; } default: { diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc new file mode 100644 index 00000000000..b529ebbdde8 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -0,0 +1,832 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_kdopbvh.h" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_bvhutils.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" + +#include "FN_generic_array.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using namespace blender::bke::mesh_surface_sample; +using blender::fn::GArray; + +namespace blender::nodes { + +static void geo_node_transfer_attribute_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Target"); + + b.add_input("Attribute").hide_value().supports_field(); + b.add_input("Attribute", "Attribute_001").hide_value().supports_field(); + b.add_input("Attribute", "Attribute_002").hide_value().supports_field(); + b.add_input("Attribute", "Attribute_003").hide_value().supports_field(); + b.add_input("Attribute", "Attribute_004").hide_value().supports_field(); + + b.add_input("Source Position").implicit_field(); + b.add_input("Index").implicit_field(); + + b.add_output("Attribute").dependent_field({6, 7}); + b.add_output("Attribute", "Attribute_001").dependent_field({6, 7}); + b.add_output("Attribute", "Attribute_002").dependent_field({6, 7}); + b.add_output("Attribute", "Attribute_003").dependent_field({6, 7}); + b.add_output("Attribute", "Attribute_004").dependent_field({6, 7}); +} + +static void geo_node_transfer_attribute_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + const bNode &node = *static_cast(ptr->data); + const NodeGeometryTransferAttribute &data = *static_cast( + node.storage); + const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)data.mode; + + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE); + if (mapping != GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED) { + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + } +} + +static void geo_node_transfer_attribute_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryTransferAttribute *data = (NodeGeometryTransferAttribute *)MEM_callocN( + sizeof(NodeGeometryTransferAttribute), __func__); + data->data_type = CD_PROP_FLOAT; + data->mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED; + node->storage = data; +} + +static void geo_node_transfer_attribute_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryTransferAttribute &data = *(const NodeGeometryTransferAttribute *) + node->storage; + const CustomDataType data_type = static_cast(data.data_type); + const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)data.mode; + + bNodeSocket *socket_geometry = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_vector = socket_geometry->next; + bNodeSocket *socket_float = socket_vector->next; + bNodeSocket *socket_color4f = socket_float->next; + bNodeSocket *socket_boolean = socket_color4f->next; + bNodeSocket *socket_int32 = socket_boolean->next; + + bNodeSocket *socket_positions = socket_int32->next; + bNodeSocket *socket_indices = socket_positions->next; + + nodeSetSocketAvailability(socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(socket_int32, data_type == CD_PROP_INT32); + + nodeSetSocketAvailability(socket_positions, mapping != GEO_NODE_ATTRIBUTE_TRANSFER_INDEX); + nodeSetSocketAvailability(socket_indices, mapping == GEO_NODE_ATTRIBUTE_TRANSFER_INDEX); + + bNodeSocket *out_socket_vector = (bNodeSocket *)node->outputs.first; + bNodeSocket *out_socket_float = out_socket_vector->next; + bNodeSocket *out_socket_color4f = out_socket_float->next; + bNodeSocket *out_socket_boolean = out_socket_color4f->next; + bNodeSocket *out_socket_int32 = out_socket_boolean->next; + + nodeSetSocketAvailability(out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(out_socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(out_socket_int32, data_type == CD_PROP_INT32); +} + +static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(positions.size() == r_indices.size() || r_indices.is_empty()); + BLI_assert(positions.size() == r_distances_sq.size() || r_distances_sq.is_empty()); + BLI_assert(positions.size() == r_positions.size() || r_positions.is_empty()); + + for (const int i : mask) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + if (!r_indices.is_empty()) { + r_indices[i] = nearest.index; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + if (!r_positions.is_empty()) { + r_positions[i] = nearest.co; + } + } +} + +static void get_closest_pointcloud_points(const PointCloud &pointcloud, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_indices, + const MutableSpan r_distances_sq) +{ + BLI_assert(positions.size() == r_indices.size()); + BLI_assert(pointcloud.totpoint > 0); + + BVHTreeFromPointCloud tree_data; + BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2); + + for (const int i : mask) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + r_indices[i] = nearest.index; + r_distances_sq[i] = nearest.dist_sq; + } + + free_bvhtree_from_pointcloud(&tree_data); +} + +static void get_closest_mesh_points(const Mesh &mesh, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_point_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(mesh.totvert > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2); + get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_edges(const Mesh &mesh, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_edge_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(mesh.totedge > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2); + get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_looptris(const Mesh &mesh, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_looptri_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(mesh.totpoly > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); + get_closest_in_bvhtree( + tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_polygons(const Mesh &mesh, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_poly_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(mesh.totpoly > 0); + + Array looptri_indices(positions.size()); + get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, r_distances_sq, r_positions); + + const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; + + for (const int i : mask) { + const MLoopTri &looptri = looptris[looptri_indices[i]]; + r_poly_indices[i] = looptri.poly; + } +} + +/* The closest corner is defined to be the closest corner on the closest face. */ +static void get_closest_mesh_corners(const Mesh &mesh, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_corner_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(mesh.totloop > 0); + Array poly_indices(positions.size()); + get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {}); + + for (const int i : mask) { + const float3 position = positions[i]; + const int poly_index = poly_indices[i]; + const MPoly &poly = mesh.mpoly[poly_index]; + + /* Find the closest vertex in the polygon. */ + float min_distance_sq = FLT_MAX; + const MVert *closest_mvert; + int closest_loop_index = 0; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int vertex_index = loop.v; + const MVert &mvert = mesh.mvert[vertex_index]; + const float distance_sq = float3::distance_squared(position, mvert.co); + if (distance_sq < min_distance_sq) { + min_distance_sq = distance_sq; + closest_loop_index = loop_index; + closest_mvert = &mvert; + } + } + if (!r_corner_indices.is_empty()) { + r_corner_indices[i] = closest_loop_index; + } + if (!r_positions.is_empty()) { + r_positions[i] = closest_mvert->co; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = min_distance_sq; + } + } +} + +template +void copy_with_indices(const VArray &src, + const IndexMask mask, + const Span indices, + const MutableSpan dst) +{ + if (src.is_empty()) { + return; + } + for (const int i : mask) { + dst[i] = src[indices[i]]; + } +} + +template +void copy_with_indices_clamped(const VArray &src, + const IndexMask mask, + const Span indices, + const MutableSpan dst) +{ + if (src.is_empty()) { + return; + } + const int max_index = src.size() - 1; + threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + const int index = mask[i]; + dst[index] = src[std::clamp(indices[index], 0, max_index)]; + } + }); +} + +template +void copy_with_indices_and_comparison(const VArray &src_1, + const VArray &src_2, + const Span distances_1, + const Span distances_2, + const IndexMask mask, + const Span indices_1, + const Span indices_2, + const MutableSpan dst) +{ + if (src_1.is_empty() || src_2.is_empty()) { + return; + } + for (const int i : mask) { + if (distances_1[i] < distances_2[i]) { + dst[i] = src_1[indices_1[i]]; + } + else { + dst[i] = src_2[indices_2[i]]; + } + } +} + +static bool component_is_available(const GeometrySet &geometry, + const GeometryComponentType type, + const AttributeDomain domain) +{ + if (!geometry.has(type)) { + return false; + } + const GeometryComponent &component = *geometry.get_component_for_read(type); + if (component.is_empty()) { + return false; + } + return component.attribute_domain_size(domain) != 0; +} + +/** + * \note Multi-threading for this function is provided by the field evaluator. Since the #call + * function could be called many times, calculate the data from the target geometry once and store + * it for later. + */ +class NearestInterpolatedTransferFunction : public fn::MultiFunction { + GeometrySet target_; + GField src_field_; + + /** + * This function is meant to sample the surface of a mesh rather than take the value from + * individual elements, so use the most complex domain, ensuring no information is lost. In the + * future, it should be possible to use the most complex domain required by the field inputs, to + * simplify sampling and avoid domain conversions. + */ + AttributeDomain domain_ = ATTR_DOMAIN_CORNER; + + fn::MFSignature signature_; + + std::optional target_context_; + std::unique_ptr target_evaluator_; + const GVArray *target_data_; + + public: + NearestInterpolatedTransferFunction(GeometrySet geometry, GField src_field) + : target_(std::move(geometry)), src_field_(std::move(src_field)) + { + target_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + this->evaluate_target_field(); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest Interpolated"}; + signature.single_input("Position"); + signature.single_output("Attribute", src_field_.cpp_type()); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &positions = params.readonly_single_input(0, "Position"); + GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); + + const MeshComponent &mesh_component = *target_.get_component_for_read(); + BLI_assert(mesh_component.has_mesh()); + const Mesh &mesh = *mesh_component.get_for_read(); + BLI_assert(mesh.totpoly > 0); + + /* Find closest points on the mesh surface. */ + Array looptri_indices(mask.min_array_size()); + Array sampled_positions(mask.min_array_size()); + get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions); + + MeshAttributeInterpolator interp(&mesh, sampled_positions, looptri_indices); + interp.sample_data(*target_data_, domain_, eAttributeMapMode::INTERPOLATED, mask, dst); + } + + private: + void evaluate_target_field() + { + const MeshComponent &mesh_component = *target_.get_component_for_read(); + target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); + const int domain_size = mesh_component.attribute_domain_size(domain_); + target_evaluator_ = std::make_unique(*target_context_, domain_size); + target_evaluator_->add(src_field_); + target_evaluator_->evaluate(); + target_data_ = &target_evaluator_->get_evaluated(0); + } +}; + +/** + * \note Multi-threading for this function is provided by the field evaluator. Since the #call + * function could be called many times, calculate the data from the target geometry once and store + * it for later. + */ +class NearestTransferFunction : public fn::MultiFunction { + GeometrySet target_; + GField src_field_; + AttributeDomain domain_; + + fn::MFSignature signature_; + + bool use_mesh_; + bool use_points_; + + /* Store data from the target as a virtual array, since we may only access a few indices. */ + std::optional mesh_context_; + std::unique_ptr mesh_evaluator_; + const GVArray *mesh_data_; + + std::optional point_context_; + std::unique_ptr point_evaluator_; + const GVArray *point_data_; + + public: + NearestTransferFunction(GeometrySet geometry, GField src_field, AttributeDomain domain) + : target_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain) + { + target_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + + this->use_mesh_ = component_is_available(target_, GEO_COMPONENT_TYPE_MESH, domain_); + this->use_points_ = component_is_available(target_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_); + + this->evaluate_target_field(); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest"}; + signature.single_input("Position"); + signature.single_output("Attribute", src_field_.cpp_type()); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &positions = params.readonly_single_input(0, "Position"); + GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); + + if (!use_mesh_ && !use_points_) { + dst.type().fill_construct_indices(dst.type().default_value(), dst.data(), mask); + return; + } + + const Mesh *mesh = use_mesh_ ? target_.get_mesh_for_read() : nullptr; + const PointCloud *pointcloud = use_points_ ? target_.get_pointcloud_for_read() : nullptr; + + const int tot_samples = mask.min_array_size(); + + Array point_indices; + Array point_distances; + + /* Depending on where what domain the source attribute lives, these indices are either vertex, + * corner, edge or polygon indices. */ + Array mesh_indices; + Array mesh_distances; + + /* If there is a pointcloud, find the closest points. */ + if (use_points_) { + point_indices.reinitialize(tot_samples); + if (use_mesh_) { + point_distances.reinitialize(tot_samples); + } + get_closest_pointcloud_points(*pointcloud, positions, mask, point_indices, point_distances); + } + + /* If there is a mesh, find the closest mesh elements. */ + if (use_mesh_) { + mesh_indices.reinitialize(tot_samples); + if (use_points_) { + mesh_distances.reinitialize(tot_samples); + } + switch (domain_) { + case ATTR_DOMAIN_POINT: { + get_closest_mesh_points(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + case ATTR_DOMAIN_EDGE: { + get_closest_mesh_edges(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + case ATTR_DOMAIN_FACE: { + get_closest_mesh_polygons(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + case ATTR_DOMAIN_CORNER: { + get_closest_mesh_corners(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + default: { + break; + } + } + } + + attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) { + using T = decltype(dummy); + if (use_mesh_ && use_points_) { + GVArray_Typed src_mesh{*mesh_data_}; + GVArray_Typed src_point{*point_data_}; + copy_with_indices_and_comparison(*src_mesh, + *src_point, + mesh_distances, + point_distances, + mask, + mesh_indices, + point_indices, + dst.typed()); + } + else if (use_points_) { + GVArray_Typed src_point{*point_data_}; + copy_with_indices(*src_point, mask, point_indices, dst.typed()); + } + else if (use_mesh_) { + GVArray_Typed src_mesh{*mesh_data_}; + copy_with_indices(*src_mesh, mask, mesh_indices, dst.typed()); + } + }); + } + + private: + void evaluate_target_field() + { + if (use_mesh_) { + const MeshComponent &mesh = *target_.get_component_for_read(); + const int domain_size = mesh.attribute_domain_size(domain_); + mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_)); + mesh_evaluator_ = std::make_unique(*mesh_context_, domain_size); + mesh_evaluator_->add(src_field_); + mesh_evaluator_->evaluate(); + mesh_data_ = &mesh_evaluator_->get_evaluated(0); + } + + if (use_points_) { + const PointCloudComponent &points = *target_.get_component_for_read(); + const int domain_size = points.attribute_domain_size(domain_); + point_context_.emplace(GeometryComponentFieldContext(points, domain_)); + point_evaluator_ = std::make_unique(*point_context_, domain_size); + point_evaluator_->add(src_field_); + point_evaluator_->evaluate(); + point_data_ = &point_evaluator_->get_evaluated(0); + } + } +}; + +static const GeometryComponent *find_best_match_component(const GeometrySet &geometry, + const GeometryComponentType type, + const AttributeDomain domain) +{ + /* Prefer transferring from the same component type, if it exists. */ + if (component_is_available(geometry, type, domain)) { + return geometry.get_component_for_read(type); + } + + /* If there is no component of the same type, choose the other component based on a consistent + * order, rather than some more complicated heuristic. This is the same order visible in the + * spreadsheet and used in the raycast node. */ + static const Array supported_types = { + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + for (const GeometryComponentType src_type : supported_types) { + if (component_is_available(geometry, src_type, domain)) { + return geometry.get_component_for_read(src_type); + } + } + + return nullptr; +} + +/** + * Use a FieldInput because it's necessary to know the field context in order to choose the + * corresponding component type from the input geometry, and only a FieldInput receives the + * evaluation context to provide its data. + * + * The index-based transfer theoretically does not need realized data when there is only one + * instance geometry set in the target. A future optimization could be removing that limitation + * internally. + */ +class IndexTransferFieldInput : public FieldInput { + GeometrySet src_geometry_; + GField src_field_; + Field index_field_; + AttributeDomain domain_; + + public: + IndexTransferFieldInput(GeometrySet geometry, + GField src_field, + Field index_field, + const AttributeDomain domain) + : FieldInput(src_field.cpp_type(), "Attribute Transfer Index"), + src_geometry_(std::move(geometry)), + src_field_(std::move(src_field)), + index_field_(std::move(index_field)), + domain_(domain) + { + src_geometry_.ensure_owns_direct_data(); + } + + const GVArray *get_varray_for_context(const FieldContext &context, + const IndexMask mask, + ResourceScope &scope) const final + { + const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context); + if (geometry_context == nullptr) { + return nullptr; + } + + FieldEvaluator index_evaluator{*geometry_context, &mask}; + index_evaluator.add(index_field_); + index_evaluator.evaluate(); + const VArray &index_varray = index_evaluator.get_evaluated(0); + /* The index virtual array is expected to be a span, since transferring the same index for + * every element is not very useful. */ + VArray_Span indices{index_varray}; + + const GeometryComponent *component = find_best_match_component( + src_geometry_, geometry_context->geometry_component().type(), domain_); + if (component == nullptr) { + return nullptr; + } + + GeometryComponentFieldContext target_context{*component, domain_}; + /* A potential improvement is to only copy the necessary values + * based on the indices retrieved from the index input field. */ + FieldEvaluator target_evaluator{target_context, component->attribute_domain_size(domain_)}; + target_evaluator.add(src_field_); + target_evaluator.evaluate(); + const GVArray &src_data = target_evaluator.get_evaluated(0); + + GArray dst(src_field_.cpp_type(), mask.min_array_size()); + + attribute_math::convert_to_static_type(src_data.type(), [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed src{src_data}; + copy_with_indices_clamped(*src, mask, indices, dst.as_mutable_span().typed()); + }); + + return &scope.construct(std::move(dst)); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const CustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + return params.extract_input>("Attribute_001"); + case CD_PROP_FLOAT3: + return params.extract_input>("Attribute"); + case CD_PROP_COLOR: + return params.extract_input>("Attribute_002"); + case CD_PROP_BOOL: + return params.extract_input>("Attribute_003"); + case CD_PROP_INT32: + return params.extract_input>("Attribute_004"); + default: + BLI_assert_unreachable(); + } + return {}; +} + +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) +{ + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Attribute_001", Field(field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Attribute", Field(field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Attribute_002", Field(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Attribute_003", Field(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Attribute_004", Field(field)); + break; + } + default: + break; + } +} + +static void geo_node_transfer_attribute_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input("Target"); + const bNode &node = params.node(); + const NodeGeometryTransferAttribute &data = *(const NodeGeometryTransferAttribute *)node.storage; + const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)data.mode; + const CustomDataType data_type = static_cast(data.data_type); + const AttributeDomain domain = static_cast(data.domain); + + GField field = get_input_attribute_field(params, data_type); + + auto return_default = [&]() { + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + output_attribute_field(params, fn::make_constant_field(T())); + }); + }; + + if (geometry.has_instances()) { + if (geometry.has_realized_data()) { + params.error_message_add( + NodeWarningType::Info, + TIP_("Only realized geometry is supported, instances will not be used")); + } + else { + params.error_message_add(NodeWarningType::Error, + TIP_("Target geometry must contain realized data")); + return return_default(); + } + /* Since the instances are not used, there is no point in keeping + * a reference to them while the field is passed around. */ + geometry.remove(GEO_COMPONENT_TYPE_INSTANCES); + } + + GField output_field; + switch (mapping) { + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { + const Mesh *mesh = geometry.get_mesh_for_read(); + if (mesh == nullptr) { + if (!geometry.is_empty()) { + params.error_message_add(NodeWarningType::Error, + TIP_("The target geometry must contain a mesh")); + } + return return_default(); + } + if (mesh->totpoly == 0) { + /* Don't add a warning for empty meshes. */ + if (mesh->totvert != 0) { + params.error_message_add(NodeWarningType::Error, + TIP_("The target mesh must have faces")); + } + return return_default(); + } + auto fn = std::make_unique(std::move(geometry), + std::move(field)); + auto op = std::make_shared( + FieldOperation(std::move(fn), {params.extract_input>("Source Position")})); + output_field = GField(std::move(op)); + break; + } + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { + if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) { + params.error_message_add(NodeWarningType::Warning, + TIP_("Curve targets are not currently supported")); + return return_default(); + } + auto fn = std::make_unique( + std::move(geometry), std::move(field), domain); + auto op = std::make_shared( + FieldOperation(std::move(fn), {params.extract_input>("Source Position")})); + output_field = GField(std::move(op)); + break; + } + case GEO_NODE_ATTRIBUTE_TRANSFER_INDEX: { + Field indices = params.extract_input>("Index"); + std::shared_ptr input = std::make_shared( + std::move(geometry), std::move(field), std::move(indices), domain); + output_field = GField(std::move(input)); + break; + } + } + + output_attribute_field(params, std::move(output_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_transfer_attribute() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_TRANSFER_ATTRIBUTE, "Transfer Attribute", NODE_CLASS_ATTRIBUTE, 0); + node_type_init(&ntype, blender::nodes::geo_node_transfer_attribute_init); + node_type_update(&ntype, blender::nodes::geo_node_transfer_attribute_update); + node_type_storage(&ntype, + "NodeGeometryTransferAttribute", + node_free_standard_storage, + node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_transfer_attribute_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_transfer_attribute_exec; + ntype.draw_buttons = blender::nodes::geo_node_transfer_attribute_layout; + nodeRegisterType(&ntype); +} From 19bab2a5360708242a5f6ea11b6e2ff03568a679 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 15 Oct 2021 14:20:53 -0500 Subject: [PATCH 0840/1500] Geometry Nodes: Object info node optional instance output The object info node output an instance as a performance optimization. Before that optimization was (almost) invisible to the user, but now that we aren't automatically realizing instances, it isn't intuitive for a single object to become an instance. I refactored the transform node so its ability to translate/transform an entire geometry set was more usable from elsewhere and exposed the function to get a geometry set from an object. Differential Revision: https://developer.blender.org/D12833 --- .../blenkernel/BKE_geometry_set_instances.hh | 2 + .../intern/geometry_set_instances.cc | 10 +- .../nodes/geometry/node_geometry_util.hh | 6 +- .../geometry/nodes/node_geo_bounding_box.cc | 2 +- .../nodes/node_geo_mesh_primitive_cube.cc | 4 +- .../geometry/nodes/node_geo_object_info.cc | 32 ++- .../geometry/nodes/node_geo_transform.cc | 190 ++++++++++-------- 7 files changed, 141 insertions(+), 105 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh index 653450c7d8e..d7a60162e81 100644 --- a/source/blender/blenkernel/BKE_geometry_set_instances.hh +++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh @@ -20,6 +20,8 @@ namespace blender::bke { +GeometrySet object_get_evaluated_geometry_set(const Object &object); + /** * Used to keep track of a group of instances using the same geometry data. */ diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 77348c3d22c..d92df386575 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -56,7 +56,7 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS /** * \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances. */ -static GeometrySet object_get_geometry_set_for_read(const Object &object) +GeometrySet object_get_evaluated_geometry_set(const Object &object) { if (object.type == OB_MESH && object.mode == OB_MODE_EDIT) { GeometrySet geometry_set; @@ -100,7 +100,7 @@ static void geometry_set_collect_recursive_object(const Object &object, const float4x4 &transform, Vector &r_sets) { - GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object); + GeometrySet instance_geometry_set = object_get_evaluated_geometry_set(object); geometry_set_collect_recursive(instance_geometry_set, transform, r_sets); if (object.type == OB_EMPTY) { @@ -628,14 +628,14 @@ void InstancesComponent::foreach_referenced_geometry( switch (reference.type()) { case InstanceReference::Type::Object: { const Object &object = reference.object(); - const GeometrySet object_geometry_set = object_get_geometry_set_for_read(object); + const GeometrySet object_geometry_set = object_get_evaluated_geometry_set(object); callback(object_geometry_set); break; } case InstanceReference::Type::Collection: { Collection &collection = reference.collection(); FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) { - const GeometrySet object_geometry_set = object_get_geometry_set_for_read(*object); + const GeometrySet object_geometry_set = object_get_evaluated_geometry_set(*object); callback(object_geometry_set); } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; @@ -676,7 +676,7 @@ void InstancesComponent::ensure_geometry_instances() /* Create a new reference that contains the geometry set of the object. We may want to * treat e.g. lamps and similar object types separately here. */ const Object &object = reference.object(); - GeometrySet object_geometry_set = object_get_geometry_set_for_read(object); + GeometrySet object_geometry_set = object_get_evaluated_geometry_set(object); if (object_geometry_set.has_instances()) { InstancesComponent &component = object_geometry_set.get_component_for_write(); diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 21404525748..f382ff6c132 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -50,11 +50,15 @@ void update_attribute_input_socket_availabilities(bNode &node, Array get_geometry_element_ids_as_uints(const GeometryComponent &component, const AttributeDomain domain); -void transform_mesh(Mesh *mesh, +void transform_mesh(Mesh &mesh, const float3 translation, const float3 rotation, const float3 scale); +void transform_geometry_set(GeometrySet &geometry, + const float4x4 &transform, + const Depsgraph &depsgraph); + Mesh *create_line_mesh(const float3 start, const float3 delta, const int count); Mesh *create_grid_mesh(const int verts_x, diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index fdc6b12095c..8b90595a641 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -153,7 +153,7 @@ static void geo_node_bounding_box_exec(GeoNodeExecParams params) const float3 scale = max - min; const float3 center = min + scale / 2.0f; Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); - transform_mesh(mesh, center, float3(0), float3(1)); + transform_mesh(*mesh, center, float3(0), float3(1)); params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh)); params.set_output("Min", min); params.set_output("Max", max); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index af8ce02b3c1..5a520d36296 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -456,12 +456,12 @@ static Mesh *create_cube_mesh(const float3 size, } if (verts_y == 1) { /* XZ plane. */ Mesh *mesh = create_grid_mesh(verts_x, verts_z, size.x, size.z); - transform_mesh(mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1)); + transform_mesh(*mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1)); return mesh; } /* YZ plane. */ Mesh *mesh = create_grid_mesh(verts_z, verts_y, size.z, size.y); - transform_mesh(mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1)); + transform_mesh(*mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1)); return mesh; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index 389acc40f0f..e61709ed86a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -26,6 +26,10 @@ namespace blender::nodes { static void geo_node_object_info_declare(NodeDeclarationBuilder &b) { b.add_input("Object").hide_label(); + b.add_input("As Instance") + .description( + "Output the entire object as single instance. " + "This allows instancing non-geometry object types"); b.add_output("Location"); b.add_output("Rotation"); b.add_output("Scale"); @@ -54,12 +58,11 @@ static void geo_node_object_info_exec(GeoNodeExecParams params) const Object *self_object = params.self_object(); if (object != nullptr) { - float transform[4][4]; - mul_m4_m4m4(transform, self_object->imat, object->obmat); + const float4x4 transform = float4x4(self_object->imat) * float4x4(object->obmat); float quaternion[4]; if (transform_space_relative) { - mat4_decompose(location, quaternion, scale, transform); + mat4_decompose(location, quaternion, scale, transform.values); } else { mat4_decompose(location, quaternion, scale, object->obmat); @@ -67,16 +70,23 @@ static void geo_node_object_info_exec(GeoNodeExecParams params) quat_to_eul(rotation, quaternion); if (object != self_object) { - InstancesComponent &instances = geometry_set.get_component_for_write(); - const int handle = instances.add_reference(*object); - - if (transform_space_relative) { - instances.add_instance(handle, transform); + if (params.get_input("As Instance")) { + InstancesComponent &instances = geometry_set.get_component_for_write(); + const int handle = instances.add_reference(*object); + if (transform_space_relative) { + instances.add_instance(handle, transform); + } + else { + float unit_transform[4][4]; + unit_m4(unit_transform); + instances.add_instance(handle, unit_transform); + } } else { - float unit_transform[4][4]; - unit_m4(unit_transform); - instances.add_instance(handle, unit_transform); + geometry_set = bke::object_get_evaluated_geometry_set(*object); + if (transform_space_relative) { + transform_geometry_set(geometry_set, transform, *params.depsgraph()); + } } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index d5eb067cad0..005714a9580 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -25,6 +25,7 @@ #include "DNA_volume_types.h" #include "BKE_mesh.h" +#include "BKE_pointcloud.h" #include "BKE_spline.hh" #include "BKE_volume.h" @@ -55,112 +56,142 @@ static bool use_translate(const float3 rotation, const float3 scale) return true; } -void transform_mesh(Mesh *mesh, +static void translate_mesh(Mesh &mesh, const float3 translation) +{ + if (!translation.is_zero()) { + BKE_mesh_translate(&mesh, translation, false); + } +} + +static void transform_mesh(Mesh &mesh, const float4x4 &transform) +{ + BKE_mesh_transform(&mesh, transform.values, false); + BKE_mesh_normals_tag_dirty(&mesh); +} + +void transform_mesh(Mesh &mesh, const float3 translation, const float3 rotation, const float3 scale) { - /* Use only translation if rotation and scale are zero. */ - if (use_translate(rotation, scale)) { - if (!translation.is_zero()) { - BKE_mesh_translate(mesh, translation, false); - } - } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - BKE_mesh_transform(mesh, matrix.values, false); - BKE_mesh_normals_tag_dirty(mesh); - } + const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); + transform_mesh(mesh, matrix); } -static void transform_pointcloud(PointCloud *pointcloud, - const float3 translation, - const float3 rotation, - const float3 scale) +static void translate_pointcloud(PointCloud &pointcloud, const float3 translation) { - /* Use only translation if rotation and scale don't apply. */ - if (use_translate(rotation, scale)) { - for (const int i : IndexRange(pointcloud->totpoint)) { - add_v3_v3(pointcloud->co[i], translation); - } - } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - for (const int i : IndexRange(pointcloud->totpoint)) { - float3 &co = *(float3 *)pointcloud->co[i]; - co = matrix * co; - } + CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint); + BKE_pointcloud_update_customdata_pointers(&pointcloud); + for (const int i : IndexRange(pointcloud.totpoint)) { + add_v3_v3(pointcloud.co[i], translation); } } -static void transform_instances(InstancesComponent &instances, - const float3 translation, - const float3 rotation, - const float3 scale) +static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform) +{ + CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint); + BKE_pointcloud_update_customdata_pointers(&pointcloud); + for (const int i : IndexRange(pointcloud.totpoint)) { + float3 &co = *(float3 *)pointcloud.co[i]; + co = transform * co; + } +} + +static void translate_instances(InstancesComponent &instances, const float3 translation) { MutableSpan transforms = instances.instance_transforms(); - - /* Use only translation if rotation and scale don't apply. */ - if (use_translate(rotation, scale)) { - for (float4x4 &transform : transforms) { - add_v3_v3(transform.ptr()[3], translation); - } - } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - for (float4x4 &transform : transforms) { - transform = matrix * transform; - } + for (float4x4 &transform : transforms) { + add_v3_v3(transform.ptr()[3], translation); } } -static void transform_volume(Volume *volume, - const float3 translation, - const float3 rotation, - const float3 scale, - GeoNodeExecParams ¶ms) +static void transform_instances(InstancesComponent &instances, const float4x4 &transform) +{ + MutableSpan instance_transforms = instances.instance_transforms(); + for (float4x4 &instance_transform : instance_transforms) { + instance_transform = transform * instance_transform; + } +} + +static void transform_volume(Volume &volume, const float4x4 &transform, const Depsgraph &depsgraph) { #ifdef WITH_OPENVDB /* Scaling an axis to zero is not supported for volumes. */ + const float3 translation = transform.translation(); + const float3 rotation = transform.to_euler(); + const float3 scale = transform.scale(); const float3 limited_scale = { (scale.x == 0.0f) ? FLT_EPSILON : scale.x, (scale.y == 0.0f) ? FLT_EPSILON : scale.y, (scale.z == 0.0f) ? FLT_EPSILON : scale.z, }; + const float4x4 scale_limited_transform = float4x4::from_loc_eul_scale( + translation, rotation, limited_scale); - const Main *bmain = DEG_get_bmain(params.depsgraph()); - BKE_volume_load(volume, bmain); - - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, limited_scale); + const Main *bmain = DEG_get_bmain(&depsgraph); + BKE_volume_load(&volume, bmain); openvdb::Mat4s vdb_matrix; - memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4])); + memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4])); openvdb::Mat4d vdb_matrix_d{vdb_matrix}; - const int num_grids = BKE_volume_num_grids(volume); + const int num_grids = BKE_volume_num_grids(&volume); for (const int i : IndexRange(num_grids)) { - VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, i); + VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, i); - openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false); + openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid, false); openvdb::math::Transform &grid_transform = grid->transform(); grid_transform.postMult(vdb_matrix_d); } #else - UNUSED_VARS(volume, translation, rotation, scale, params); + UNUSED_VARS(volume, transform, depsgraph); #endif } -static void transform_curve(CurveEval &curve, - const float3 translation, - const float3 rotation, - const float3 scale) +static void translate_volume(Volume &volume, const float3 translation, const Depsgraph &depsgraph) { - if (use_translate(rotation, scale)) { - curve.translate(translation); + transform_volume(volume, float4x4::from_location(translation), depsgraph); +} + +void transform_geometry_set(GeometrySet &geometry, + const float4x4 &transform, + const Depsgraph &depsgraph) +{ + if (CurveEval *curve = geometry.get_curve_for_write()) { + curve->transform(transform); } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - curve.transform(matrix); + if (Mesh *mesh = geometry.get_mesh_for_write()) { + transform_mesh(*mesh, transform); + } + if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) { + transform_pointcloud(*pointcloud, transform); + } + if (Volume *volume = geometry.get_volume_for_write()) { + transform_volume(*volume, transform, depsgraph); + } + if (geometry.has_instances()) { + transform_instances(geometry.get_component_for_write(), transform); + } +} + +static void translate_geometry_set(GeometrySet &geometry, + const float3 translation, + const Depsgraph &depsgraph) +{ + if (CurveEval *curve = geometry.get_curve_for_write()) { + curve->translate(translation); + } + if (Mesh *mesh = geometry.get_mesh_for_write()) { + translate_mesh(*mesh, translation); + } + if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) { + translate_pointcloud(*pointcloud, translation); + } + if (Volume *volume = geometry.get_volume_for_write()) { + translate_volume(*volume, translation, depsgraph); + } + if (geometry.has_instances()) { + translate_instances(geometry.get_component_for_write(), translation); } } @@ -171,25 +202,14 @@ static void geo_node_transform_exec(GeoNodeExecParams params) const float3 rotation = params.extract_input("Rotation"); const float3 scale = params.extract_input("Scale"); - if (geometry_set.has_mesh()) { - Mesh *mesh = geometry_set.get_mesh_for_write(); - transform_mesh(mesh, translation, rotation, scale); + /* Use only translation if rotation and scale don't apply. */ + if (use_translate(rotation, scale)) { + translate_geometry_set(geometry_set, translation, *params.depsgraph()); } - if (geometry_set.has_pointcloud()) { - PointCloud *pointcloud = geometry_set.get_pointcloud_for_write(); - transform_pointcloud(pointcloud, translation, rotation, scale); - } - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write(); - transform_instances(instances, translation, rotation, scale); - } - if (geometry_set.has_volume()) { - Volume *volume = geometry_set.get_volume_for_write(); - transform_volume(volume, translation, rotation, scale, params); - } - if (geometry_set.has_curve()) { - CurveEval *curve = geometry_set.get_curve_for_write(); - transform_curve(*curve, translation, rotation, scale); + else { + transform_geometry_set(geometry_set, + float4x4::from_loc_eul_scale(translation, rotation, scale), + *params.depsgraph()); } params.set_output("Geometry", std::move(geometry_set)); From 88d295f95260f063d65e23460d36eb13bb884fa8 Mon Sep 17 00:00:00 2001 From: Jarrett Johnson Date: Fri, 15 Oct 2021 15:12:04 -0500 Subject: [PATCH 0841/1500] Geometry Nodes: Updated Subdivision Surface Node Replaces the old Subdivision Surface Node. Changes: - Removes implicit instance realization, instead the node runs once per unique instance. - "Use Creases" becomes a crease field input applied to edges. The values are clamped between zero and one. Addresses T91763 Differential Revision: https://developer.blender.org/D12830 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../legacy/node_geo_subdivision_surface.cc | 2 +- .../nodes/node_geo_subdivision_surface.cc | 169 ++++++++++++++++++ 8 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 2d03fbddd48..99be554184b 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -148,6 +148,7 @@ def mesh_node_items(context): yield NodeItem("GeometryNodeMeshToPoints") yield NodeItem("GeometryNodeSplitEdges") yield NodeItem("GeometryNodeSubdivideMesh") + yield NodeItem("GeometryNodeSubdivisionSurface") yield NodeItem("GeometryNodeTriangulate") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeInputShadeSmooth") diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a68edfca2d3..6dcb35de3af 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1538,6 +1538,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_SPLIT_EDGES 1123 #define GEO_NODE_MESH_TO_CURVE 1124 #define GEO_NODE_TRANSFER_ATTRIBUTE 1125 +#define GEO_NODE_SUBDIVISION_SURFACE 1126 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 491e29b8c4d..52ba6d1ccb8 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5726,6 +5726,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_select_by_handle_type(); register_node_type_geo_legacy_curve_subdivide(); register_node_type_geo_legacy_edge_split(); + register_node_type_geo_legacy_subdivision_surface(); register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 5a8430752ca..a202d247e60 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -278,6 +278,7 @@ set(SRC geometry/nodes/node_geo_triangulate.cc geometry/nodes/node_geo_viewer.cc geometry/nodes/node_geo_volume_to_mesh.cc + geometry/nodes/node_geo_subdivision_surface.cc geometry/node_geometry_exec.cc geometry/node_geometry_tree.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d3cb9beef26..94fed3c3975 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -43,6 +43,7 @@ void register_node_type_geo_legacy_curve_reverse(void); void register_node_type_geo_legacy_select_by_handle_type(void); void register_node_type_geo_legacy_curve_subdivide(void); void register_node_type_geo_legacy_edge_split(void); +void register_node_type_geo_legacy_subdivision_surface(void); void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_capture(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 9022e56c910..827a12ff812 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -399,6 +399,7 @@ DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSpl DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "") DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "") DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc index 07d3f89bdb7..101c915eb77 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc @@ -128,7 +128,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_subdivision_surface() +void register_node_type_geo_legacy_subdivision_surface() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc new file mode 100644 index 00000000000..d37f968caa8 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -0,0 +1,169 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "BKE_mesh.h" +#include "BKE_subdiv.h" +#include "BKE_subdiv_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Level").default_value(1).min(0).max(6); + b.add_input("Crease") + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .supports_field() + .subtype(PROP_FACTOR); + b.add_output("Geometry"); +} + +static void geo_node_subdivision_surface_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ +#ifdef WITH_OPENSUBDIV + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); +#else + UNUSED_VARS(layout, ptr); +#endif +} + +static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometrySubdivisionSurface *data = (NodeGeometrySubdivisionSurface *)MEM_callocN( + sizeof(NodeGeometrySubdivisionSurface), __func__); + data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; + data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; + node->storage = data; +} + +static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + Field crease_field = params.extract_input>("Crease"); + + const NodeGeometrySubdivisionSurface &storage = + *(const NodeGeometrySubdivisionSurface *)params.node().storage; + const int uv_smooth = storage.uv_smooth; + const int boundary_smooth = storage.boundary_smooth; + const int subdiv_level = clamp_i(params.extract_input("Level"), 0, 30); + + /* Only process subdivision if level is greater than 0. */ + if (subdiv_level == 0) { + params.set_output("Geometry", std::move(geometry_set)); + return; + } + +#ifndef WITH_OPENSUBDIV + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenSubdiv")); +#else + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + return; + } + + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + AttributeDomain domain = ATTR_DOMAIN_EDGE; + GeometryComponentFieldContext field_context{mesh_component, domain}; + const int domain_size = mesh_component.attribute_domain_size(domain); + + FieldEvaluator evaluator(field_context, domain_size); + evaluator.add(crease_field); + evaluator.evaluate(); + const VArray &creases = evaluator.get_evaluated(0); + + OutputAttribute_Typed crease = mesh_component.attribute_try_get_for_output_only( + "crease", domain); + MutableSpan crease_span = crease.as_span(); + for (auto i : creases.index_range()) { + crease_span[i] = std::clamp(creases[i], 0.0f, 1.0f); + } + crease.save(); + + /* Initialize mesh settings. */ + SubdivToMeshSettings mesh_settings; + mesh_settings.resolution = (1 << subdiv_level) + 1; + mesh_settings.use_optimal_display = false; + + /* Initialize subdivision settings. */ + SubdivSettings subdiv_settings; + subdiv_settings.is_simple = false; + subdiv_settings.is_adaptive = false; + subdiv_settings.use_creases = !(creases.is_single() && creases.get_internal_single() == 0.0f); + subdiv_settings.level = subdiv_level; + + subdiv_settings.vtx_boundary_interpolation = + BKE_subdiv_vtx_boundary_interpolation_from_subsurf(boundary_smooth); + subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( + uv_smooth); + + Mesh *mesh_in = mesh_component.get_for_write(); + + /* Apply subdivision to mesh. */ + Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); + + /* In case of bad topology, skip to input mesh. */ + if (subdiv == nullptr) { + return; + } + + Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); + BKE_mesh_normals_tag_dirty(mesh_out); + + mesh_component.replace(mesh_out); + + BKE_subdiv_free(subdiv); + }); +#endif + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_subdivision_surface() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_subdivision_surface_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec; + ntype.draw_buttons = blender::nodes::geo_node_subdivision_surface_layout; + node_type_init(&ntype, blender::nodes::geo_node_subdivision_surface_init); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_storage(&ntype, + "NodeGeometrySubdivisionSurface", + node_free_standard_storage, + node_copy_standard_storage); + nodeRegisterType(&ntype); +} From 5b9a911c4b3c888e80e314a987e34642eb317310 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Fri, 15 Oct 2021 20:01:30 +0200 Subject: [PATCH 0842/1500] Fix T72583: Sun Beams node artifacts The artifacts are due to the loss of precision when doing some calculations with float precision. --- .../compositor/operations/COM_SunBeamsOperation.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index b673a1ac754..38e9599f7e6 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -127,9 +127,9 @@ template struct BufferLineAccumulator { buffer_to_sector(source, co[0], co[1], pu, pv); /* line angle */ - float tan_phi = pv / pu; - float dr = sqrtf(tan_phi * tan_phi + 1.0f); - float cos_phi = 1.0f / dr; + double tan_phi = pv / (double)pu; + double dr = sqrt(tan_phi * tan_phi + 1.0); + double cos_phi = 1.0 / dr; /* clamp u range to avoid influence of pixels "behind" the source */ float umin = max_ff(pu - cos_phi * dist_min, 0.0f); @@ -143,7 +143,7 @@ template struct BufferLineAccumulator { sector_to_buffer(source, end, (int)ceilf(v), x, y); - falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f; + falloff_factor = dist_max > dist_min ? dr / (double)(dist_max - dist_min) : 0.0f; float *iter = input->get_buffer() + input->get_coords_offset(x, y); return iter; From 9b1b1d9269ed3e76faf5c87dc3eff861aa5193dc Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Fri, 15 Oct 2021 22:12:25 +0200 Subject: [PATCH 0843/1500] Cleanup: remove unused functions --- .../operations/COM_SunBeamsOperation.cc | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index 38e9599f7e6..ba1363d1193 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -64,16 +64,6 @@ template struct BufferLineAccumulator { /* utility functions implementing the matrix transform to/from sector space */ - static inline void buffer_to_sector(const float source[2], int x, int y, int &u, int &v) - { - int x0 = (int)source[0]; - int y0 = (int)source[1]; - x -= x0; - y -= y0; - u = x * fxu + y * fyu; - v = x * fxv + y * fyv; - } - static inline void buffer_to_sector(const float source[2], float x, float y, float &u, float &v) { int x0 = (int)source[0]; @@ -92,14 +82,6 @@ template struct BufferLineAccumulator { y = y0 + u * fyu + v * fyv; } - static inline void sector_to_buffer(const float source[2], float u, float v, float &x, float &y) - { - int x0 = (int)source[0]; - int y0 = (int)source[1]; - x = (float)x0 + u * fxu + v * fxv; - y = (float)y0 + u * fyu + v * fyv; - } - /** * Set up the initial buffer pointer and calculate necessary variables for looping. * From 9a3c7da934906f54caff8896bd1a27287d785ed5 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 15 Oct 2021 22:44:31 +0200 Subject: [PATCH 0844/1500] Fix T92184: Cage gizmo doesn't with area light This is caused by removing `gizmo_cage2d_modal()` code in 482806c8167. Some areas use cage gizmo to modify RNA properties without using transform operator like area light, image empty, and compositor preview. This functionality is implemented in code that was removed. Add this code back. Reviewed By: zeddb, campbellbarton Differential Revision: https://developer.blender.org/D12859 --- .../gizmo_library/gizmo_types/cage2d_gizmo.c | 219 +++++++++++++++++- 1 file changed, 216 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c index 08dbdd021d3..34d3f737f58 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -886,11 +886,224 @@ static int gizmo_cage2d_invoke(bContext *C, wmGizmo *gz, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -static int gizmo_cage2d_modal(bContext *UNUSED(C), - wmGizmo *UNUSED(gz), - const wmEvent *UNUSED(event), +static void gizmo_rect_pivot_from_scale_part(int part, float r_pt[2], bool r_constrain_axis[2]) +{ + bool x = true, y = true; + switch (part) { + case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X: { + ARRAY_SET_ITEMS(r_pt, 0.5, 0.0); + x = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X: { + ARRAY_SET_ITEMS(r_pt, -0.5, 0.0); + x = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y: { + ARRAY_SET_ITEMS(r_pt, 0.0, 0.5); + y = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y: { + ARRAY_SET_ITEMS(r_pt, 0.0, -0.5); + y = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y: { + ARRAY_SET_ITEMS(r_pt, 0.5, 0.5); + x = y = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y: { + ARRAY_SET_ITEMS(r_pt, 0.5, -0.5); + x = y = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y: { + ARRAY_SET_ITEMS(r_pt, -0.5, 0.5); + x = y = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y: { + ARRAY_SET_ITEMS(r_pt, -0.5, -0.5); + x = y = false; + break; + } + default: + BLI_assert(0); + } + r_constrain_axis[0] = x; + r_constrain_axis[1] = y; +} + +static int gizmo_cage2d_modal(bContext *C, + wmGizmo *gz, + const wmEvent *event, eWM_GizmoFlagTweak UNUSED(tweak_flag)) { + if (event->type != MOUSEMOVE) { + return OPERATOR_RUNNING_MODAL; + } + /* For transform logic to be manageable we operate in -0.5..0.5 2D space, + * no matter the size of the rectangle, mouse coords are scaled to unit space. + * The mouse coords have been projected into the matrix + * so we don't need to worry about axis alignment. + * + * - The cursor offset are multiplied by 'dims'. + * - Matrix translation is also multiplied by 'dims'. + */ + RectTransformInteraction *data = gz->interaction_data; + float point_local[2]; + + float dims[2]; + RNA_float_get_array(gz->ptr, "dimensions", dims); + + { + float matrix_back[4][4]; + copy_m4_m4(matrix_back, gz->matrix_offset); + copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset); + + bool ok = gizmo_window_project_2d( + C, gz, (const float[2]){UNPACK2(event->mval)}, 2, false, point_local); + copy_m4_m4(gz->matrix_offset, matrix_back); + if (!ok) { + return OPERATOR_RUNNING_MODAL; + } + } + + const int transform_flag = RNA_enum_get(gz->ptr, "transform"); + wmGizmoProperty *gz_prop; + + gz_prop = WM_gizmo_target_property_find(gz, "matrix"); + if (gz_prop->type != NULL) { + WM_gizmo_target_property_float_get_array(gz, gz_prop, &gz->matrix_offset[0][0]); + } + + if (gz->highlight_part == ED_GIZMO_CAGE2D_PART_TRANSLATE) { + /* do this to prevent clamping from changing size */ + copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset); + gz->matrix_offset[3][0] = data->orig_matrix_offset[3][0] + + (point_local[0] - data->orig_mouse[0]); + gz->matrix_offset[3][1] = data->orig_matrix_offset[3][1] + + (point_local[1] - data->orig_mouse[1]); + } + else if (gz->highlight_part == ED_GIZMO_CAGE2D_PART_ROTATE) { + +#define MUL_V2_V3_M4_FINAL(test_co, mouse_co) \ + mul_v3_m4v3( \ + test_co, data->orig_matrix_final_no_offset, ((const float[3]){UNPACK2(mouse_co), 0.0})) + + float test_co[3]; + + if (data->dial == NULL) { + MUL_V2_V3_M4_FINAL(test_co, data->orig_matrix_offset[3]); + + data->dial = BLI_dial_init(test_co, FLT_EPSILON); + + MUL_V2_V3_M4_FINAL(test_co, data->orig_mouse); + BLI_dial_angle(data->dial, test_co); + } + + /* rotate */ + MUL_V2_V3_M4_FINAL(test_co, point_local); + const float angle = BLI_dial_angle(data->dial, test_co); + + float matrix_space_inv[4][4]; + float matrix_rotate[4][4]; + float pivot[3]; + + copy_v3_v3(pivot, data->orig_matrix_offset[3]); + + invert_m4_m4(matrix_space_inv, gz->matrix_space); + + unit_m4(matrix_rotate); + mul_m4_m4m4(matrix_rotate, matrix_rotate, matrix_space_inv); + rotate_m4(matrix_rotate, 'Z', -angle); + mul_m4_m4m4(matrix_rotate, matrix_rotate, gz->matrix_space); + + zero_v3(matrix_rotate[3]); + transform_pivot_set_m4(matrix_rotate, pivot); + + mul_m4_m4m4(gz->matrix_offset, matrix_rotate, data->orig_matrix_offset); + +#undef MUL_V2_V3_M4_FINAL + } + else { + /* scale */ + copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset); + float pivot[2]; + bool constrain_axis[2] = {false}; + + if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) { + gizmo_rect_pivot_from_scale_part(gz->highlight_part, pivot, constrain_axis); + } + else { + zero_v2(pivot); + } + + /* Cursor deltas scaled to (-0.5..0.5). */ + float delta_orig[2], delta_curr[2]; + for (int i = 0; i < 2; i++) { + delta_orig[i] = ((data->orig_mouse[i] - data->orig_matrix_offset[3][i]) / dims[i]) - + pivot[i]; + delta_curr[i] = ((point_local[i] - data->orig_matrix_offset[3][i]) / dims[i]) - pivot[i]; + } + + float scale[2] = {1.0f, 1.0f}; + for (int i = 0; i < 2; i++) { + if (constrain_axis[i] == false) { + if (delta_orig[i] < 0.0f) { + delta_orig[i] *= -1.0f; + delta_curr[i] *= -1.0f; + } + const int sign = signum_i(scale[i]); + + scale[i] = 1.0f + ((delta_curr[i] - delta_orig[i]) / len_v3(data->orig_matrix_offset[i])); + + if ((transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_SIGNED) == 0) { + if (sign != signum_i(scale[i])) { + scale[i] = 0.0f; + } + } + } + } + + if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_UNIFORM) { + if (constrain_axis[0] == false && constrain_axis[1] == false) { + scale[1] = scale[0] = (scale[1] + scale[0]) / 2.0f; + } + else if (constrain_axis[0] == false) { + scale[1] = scale[0]; + } + else if (constrain_axis[1] == false) { + scale[0] = scale[1]; + } + else { + BLI_assert(0); + } + } + + /* scale around pivot */ + float matrix_scale[4][4]; + unit_m4(matrix_scale); + + mul_v3_fl(matrix_scale[0], scale[0]); + mul_v3_fl(matrix_scale[1], scale[1]); + + transform_pivot_set_m4(matrix_scale, + (const float[3]){pivot[0] * dims[0], pivot[1] * dims[1], 0.0f}); + mul_m4_m4m4(gz->matrix_offset, data->orig_matrix_offset, matrix_scale); + } + + if (gz_prop->type != NULL) { + WM_gizmo_target_property_float_set_array(C, gz, gz_prop, &gz->matrix_offset[0][0]); + } + + /* tag the region for redraw */ + ED_region_tag_redraw_editor_overlays(CTX_wm_region(C)); + WM_event_add_mousemove(CTX_wm_window(C)); + return OPERATOR_RUNNING_MODAL; } From 81514b0e913b03ab7fb6aa779d23a1d651ad82b7 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 15 Oct 2021 22:49:19 +0200 Subject: [PATCH 0845/1500] Fix T91012: Scene strip doesn't play audio Issue was caused by adding `seq->sound` check in ded68fb10275 in function `BKE_sound_scene_add_scene_sound` as `offset_time` field was introduced to resolve sub-frame a/v misalignment. Scene strips don't have `bSound` allocated but also don't suffer from a/v misalignment. Remove `seq->sound` check and don't apply any offset for scene strips. Reviewed By: zeddb, sergey Differential Revision: https://developer.blender.org/D12819 --- source/blender/blenkernel/intern/sound.c | 8 ++++---- source/blender/sequencer/intern/sound.c | 8 ++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 8feda76cc5b..f523c5e02bd 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -702,13 +702,13 @@ void *BKE_sound_scene_add_scene_sound( Scene *scene, Sequence *sequence, int startframe, int endframe, int frameskip) { sound_verify_evaluated_id(&scene->id); - if (sequence->scene && scene != sequence->scene && sequence->sound) { + if (sequence->scene && scene != sequence->scene) { const double fps = FPS; return AUD_Sequence_add(scene->sound_scene, sequence->scene->sound_scene, startframe / fps, endframe / fps, - frameskip / fps + sequence->sound->offset_time); + frameskip / fps); } return NULL; } @@ -774,13 +774,13 @@ void BKE_sound_move_scene_sound( void BKE_sound_move_scene_sound_defaults(Scene *scene, Sequence *sequence) { sound_verify_evaluated_id(&scene->id); - if (sequence->scene_sound && sequence->sound) { + if (sequence->scene_sound) { BKE_sound_move_scene_sound(scene, sequence->scene_sound, sequence->startdisp, sequence->enddisp, sequence->startofs + sequence->anim_startofs, - sequence->sound->offset_time); + 0.0); } } diff --git a/source/blender/sequencer/intern/sound.c b/source/blender/sequencer/intern/sound.c index 9fe9e644a74..86a37aca4a9 100644 --- a/source/blender/sequencer/intern/sound.c +++ b/source/blender/sequencer/intern/sound.c @@ -111,12 +111,8 @@ void SEQ_sound_update_bounds(Scene *scene, Sequence *seq) /* We have to take into account start frame of the sequence's scene! */ int startofs = seq->startofs + seq->anim_startofs + seq->scene->r.sfra; - BKE_sound_move_scene_sound(scene, - seq->scene_sound, - seq->startdisp, - seq->enddisp, - startofs, - seq->sound->offset_time); + BKE_sound_move_scene_sound( + scene, seq->scene_sound, seq->startdisp, seq->enddisp, startofs, 0.0); } } else { From e11b33fec33392640e74b9f180572fc0a504287e Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 15 Oct 2021 23:02:08 +0200 Subject: [PATCH 0846/1500] Remove math for 2D affine transform Commit e1665c3d3190 added math to do 2D affine transformations with 3x3 matrices, but these matrices are also used for 3D transformations. Remove added functions and use 4x4 matrices for 2D transformation. Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D12510 --- source/blender/blenlib/BLI_math_matrix.h | 8 --- source/blender/blenlib/intern/math_matrix.c | 53 ------------------- .../transform_convert_sequencer_image.c | 3 +- .../editors/transform/transform_gizmo_2d.c | 3 +- .../transform/transform_orientations.c | 3 +- source/blender/imbuf/IMB_imbuf.h | 2 +- source/blender/imbuf/intern/imageprocess.c | 27 +++++----- source/blender/sequencer/intern/render.c | 40 ++++++++------ .../sequencer/intern/strip_transform.c | 51 ++++++++++-------- 9 files changed, 70 insertions(+), 120 deletions(-) diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index e38df58c1ca..2b0c3db21ee 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -326,13 +326,9 @@ void mat4_to_size(float size[3], const float M[4][4]); void mat4_to_size_fix_shear(float size[3], const float M[4][4]); -void translate_m3(float mat[3][3], float tx, float ty); void translate_m4(float mat[4][4], float tx, float ty, float tz); -void rotate_m3(float mat[3][3], const float angle); void rotate_m4(float mat[4][4], const char axis, const float angle); -void rescale_m3(float mat[3][3], const float scale[2]); void rescale_m4(float mat[4][4], const float scale[3]); -void transform_pivot_set_m3(float mat[3][3], const float pivot[2]); void transform_pivot_set_m4(float mat[4][4], const float pivot[3]); void mat4_to_rot(float rot[3][3], const float wmat[4][4]); @@ -343,10 +339,6 @@ void mat4_decompose(float loc[3], float quat[4], float size[3], const float wmat void mat3_polar_decompose(const float mat3[3][3], float r_U[3][3], float r_P[3][3]); -void loc_rot_size_to_mat3(float R[3][3], - const float loc[2], - const float angle, - const float size[2]); void loc_rot_size_to_mat4(float R[4][4], const float loc[3], const float rot[3][3], diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index b605c3eeead..554506e90e7 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -2338,12 +2338,6 @@ void scale_m4_fl(float R[4][4], float scale) R[3][0] = R[3][1] = R[3][2] = 0.0; } -void translate_m3(float mat[3][3], float tx, float ty) -{ - mat[2][0] += (tx * mat[0][0] + ty * mat[1][0]); - mat[2][1] += (tx * mat[0][1] + ty * mat[1][1]); -} - void translate_m4(float mat[4][4], float Tx, float Ty, float Tz) { mat[3][0] += (Tx * mat[0][0] + Ty * mat[1][0] + Tz * mat[2][0]); @@ -2351,18 +2345,6 @@ void translate_m4(float mat[4][4], float Tx, float Ty, float Tz) mat[3][2] += (Tx * mat[0][2] + Ty * mat[1][2] + Tz * mat[2][2]); } -void rotate_m3(float mat[3][3], const float angle) -{ - const float angle_cos = cosf(angle); - const float angle_sin = sinf(angle); - - for (int col = 0; col < 3; col++) { - float temp = angle_cos * mat[0][col] + angle_sin * mat[1][col]; - mat[1][col] = -angle_sin * mat[0][col] + angle_cos * mat[1][col]; - mat[0][col] = temp; - } -} - /* TODO: enum for axis? */ /** * Rotate a matrix in-place. @@ -2408,12 +2390,6 @@ void rotate_m4(float mat[4][4], const char axis, const float angle) } } -void rescale_m3(float mat[3][3], const float scale[2]) -{ - mul_v3_fl(mat[0], scale[0]); - mul_v3_fl(mat[1], scale[1]); -} - /** Scale a matrix in-place. */ void rescale_m4(float mat[4][4], const float scale[3]) { @@ -2444,20 +2420,6 @@ void transform_pivot_set_m4(float mat[4][4], const float pivot[3]) mul_m4_m4m4(mat, mat, tmat); } -void transform_pivot_set_m3(float mat[3][3], const float pivot[2]) -{ - float tmat[3][3]; - - unit_m3(tmat); - - copy_v2_v2(tmat[2], pivot); - mul_m3_m3m3(mat, tmat, mat); - - /* invert the matrix */ - negate_v2(tmat[2]); - mul_m3_m3m3(mat, mat, tmat); -} - void blend_m3_m3m3(float out[3][3], const float dst[3][3], const float src[3][3], @@ -2637,21 +2599,6 @@ bool equals_m4m4(const float mat1[4][4], const float mat2[4][4]) equals_v4v4(mat1[2], mat2[2]) && equals_v4v4(mat1[3], mat2[3])); } -/** - * Make a 3x3 matrix out of 3 transform components. - * Matrices are made in the order: `loc * rot * scale` - */ -void loc_rot_size_to_mat3(float R[3][3], - const float loc[2], - const float angle, - const float size[2]) -{ - unit_m3(R); - translate_m3(R, loc[0], loc[1]); - rotate_m3(R, angle); - rescale_m3(R, size); -} - /** * Make a 4x4 matrix out of 3 transform components. * Matrices are made in the order: `scale * rot * loc` diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index 2b5ed268504..c0dbd5404a4 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -87,9 +87,8 @@ static TransData *SeqToTransData(const Scene *scene, unit_m3(td->mtx); unit_m3(td->smtx); - unit_m3(td->axismtx); - rotate_m3(td->axismtx, transform->rotation); + axis_angle_to_mat3_single(td->axismtx, 'Z', transform->rotation); normalize_m3(td->axismtx); tdseq->seq = seq; diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 7ba52ec823d..84f7900e31c 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -652,7 +652,6 @@ static void gizmo2d_xform_invoke_prepare(const bContext *C, float c[3] = {mid[0], mid[1], 0.0f}; float orient_matrix[3][3]; - unit_m3(orient_matrix); ScrArea *area = CTX_wm_area(C); @@ -673,7 +672,7 @@ static void gizmo2d_xform_invoke_prepare(const bContext *C, rotate_around_center_v2(c, origin, ggd->rotation); - rotate_m3(orient_matrix, ggd->rotation); + axis_angle_to_mat3_single(orient_matrix, 'Z', ggd->rotation); } int orient_type = gizmo2d_calc_transform_orientation(C); diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 298cd00bb46..a1ed66c96a3 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -609,8 +609,7 @@ short transform_orientation_matrix_get(bContext *C, Scene *scene = t->scene; Sequence *seq = SEQ_select_active_get(scene); if (seq && seq->strip->transform && orient_index == V3D_ORIENT_LOCAL) { - unit_m3(r_spacemtx); - rotate_m3(r_spacemtx, seq->strip->transform->rotation); + axis_angle_to_mat3_single(r_spacemtx, 'Z', seq->strip->transform->rotation); return orient_index; } } diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index dd8e6549e24..7bfd1074ac6 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -758,7 +758,7 @@ void IMB_processor_apply_threaded_scanlines(int total_scanlines, void IMB_transform(struct ImBuf *src, struct ImBuf *dst, - float transform_matrix[3][3], + float transform_matrix[4][4], struct rctf *src_crop, const eIMBInterpolationFilterMode filter); diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index e7ad6153cd2..f01d2efa3ed 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -374,36 +374,39 @@ typedef struct TransformUserData { rctf src_crop; } TransformUserData; -static void imb_transform_calc_start_uv(const float transform_matrix[3][3], float r_start_uv[2]) +static void imb_transform_calc_start_uv(const float transform_matrix[4][4], float r_start_uv[2]) { - float orig[2]; - orig[0] = 0.0f; - orig[1] = 0.0f; - mul_v2_m3v2(r_start_uv, transform_matrix, orig); + float r_start_uv_temp[3]; + float orig[3]; + zero_v3(orig); + mul_v3_m4v3(r_start_uv_temp, transform_matrix, orig); + copy_v2_v2(r_start_uv, r_start_uv_temp); } -static void imb_transform_calc_add_x(const float transform_matrix[3][3], +static void imb_transform_calc_add_x(const float transform_matrix[4][4], const float start_uv[2], const int width, float r_add_x[2]) { - float uv_max_x[2]; + float uv_max_x[3]; + zero_v3(uv_max_x); uv_max_x[0] = width; uv_max_x[1] = 0.0f; - mul_v2_m3v2(r_add_x, transform_matrix, uv_max_x); + mul_v3_m4v3(r_add_x, transform_matrix, uv_max_x); sub_v2_v2(r_add_x, start_uv); mul_v2_fl(r_add_x, 1.0f / width); } -static void imb_transform_calc_add_y(const float transform_matrix[3][3], +static void imb_transform_calc_add_y(const float transform_matrix[4][4], const float start_uv[2], const int height, float r_add_y[2]) { - float uv_max_y[2]; + float uv_max_y[3]; + zero_v3(uv_max_y); uv_max_y[0] = 0.0f; uv_max_y[1] = height; - mul_v2_m3v2(r_add_y, transform_matrix, uv_max_y); + mul_v3_m4v3(r_add_y, transform_matrix, uv_max_y); sub_v2_v2(r_add_y, start_uv); mul_v2_fl(r_add_y, 1.0f / height); } @@ -480,7 +483,7 @@ static ScanlineThreadFunc imb_transform_scanline_func(const eIMBInterpolationFil void IMB_transform(struct ImBuf *src, struct ImBuf *dst, - float transform_matrix[3][3], + float transform_matrix[4][4], struct rctf *src_crop, const eIMBInterpolationFilterMode filter) { diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 6b233cd20fb..4405253586b 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -403,7 +403,7 @@ static void sequencer_image_crop_transform_matrix(const Sequence *seq, const ImBuf *out, const float image_scale_factor, const float preview_scale_factor, - float r_transform_matrix[3][3]) + float r_transform_matrix[4][4]) { const StripTransform *transform = seq->strip->transform; const float scale_x = transform->scale_x * image_scale_factor; @@ -412,13 +412,16 @@ static void sequencer_image_crop_transform_matrix(const Sequence *seq, const float image_center_offs_y = (out->y - in->y) / 2; const float translate_x = transform->xofs * preview_scale_factor + image_center_offs_x; const float translate_y = transform->yofs * preview_scale_factor + image_center_offs_y; - const float pivot[2] = {in->x * transform->origin[0], in->y * transform->origin[1]}; - loc_rot_size_to_mat3(r_transform_matrix, - (const float[]){translate_x, translate_y}, - transform->rotation, - (const float[]){scale_x, scale_y}); - transform_pivot_set_m3(r_transform_matrix, pivot); - invert_m3(r_transform_matrix); + const float pivot[3] = {in->x * transform->origin[0], in->y * transform->origin[1], 0.0f}; + + float rotation_matrix[3][3]; + axis_angle_to_mat3_single(rotation_matrix, 'Z', transform->rotation); + loc_rot_size_to_mat4(r_transform_matrix, + (const float[]){translate_x, translate_y, 0.0f}, + rotation_matrix, + (const float[]){scale_x, scale_y, 1.0f}); + transform_pivot_set_m4(r_transform_matrix, pivot); + invert_m4(r_transform_matrix); } static void sequencer_image_crop_init(const Sequence *seq, @@ -438,20 +441,23 @@ static void sequencer_image_crop_init(const Sequence *seq, static void sequencer_thumbnail_transform(ImBuf *in, ImBuf *out) { float image_scale_factor = (float)out->x / in->x; - float transform_matrix[3][3]; + float transform_matrix[4][4]; /* Set to keep same loc,scale,rot but change scale to thumb size limit. */ const float scale_x = 1 * image_scale_factor; const float scale_y = 1 * image_scale_factor; const float image_center_offs_x = (out->x - in->x) / 2; const float image_center_offs_y = (out->y - in->y) / 2; - const float pivot[2] = {in->x / 2, in->y / 2}; - loc_rot_size_to_mat3(transform_matrix, - (const float[]){image_center_offs_x, image_center_offs_y}, - 0, - (const float[]){scale_x, scale_y}); - transform_pivot_set_m3(transform_matrix, pivot); - invert_m3(transform_matrix); + const float pivot[3] = {in->x / 2, in->y / 2, 0.0f}; + + float rotation_matrix[3][3]; + unit_m3(rotation_matrix); + loc_rot_size_to_mat4(transform_matrix, + (const float[]){image_center_offs_x, image_center_offs_y, 0.0f}, + rotation_matrix, + (const float[]){scale_x, scale_y, 1.0f}); + transform_pivot_set_m4(transform_matrix, pivot); + invert_m4(transform_matrix); /* No crop. */ rctf source_crop; @@ -471,7 +477,7 @@ static void sequencer_preprocess_transform_crop( const bool do_scale_to_render_size = seq_need_scale_to_render_size(seq, is_proxy_image); const float image_scale_factor = do_scale_to_render_size ? 1.0f : preview_scale_factor; - float transform_matrix[3][3]; + float transform_matrix[4][4]; sequencer_image_crop_transform_matrix( seq, in, out, image_scale_factor, preview_scale_factor, transform_matrix); diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index ec504c0c9b6..c70f8d646af 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -490,36 +490,41 @@ static void seq_image_transform_quad_get_ex(const Scene *scene, image_size[1] = seq->strip->stripdata->orig_height; } - float transform_matrix[3][3]; - loc_rot_size_to_mat3(transform_matrix, - (const float[]){transform->xofs, transform->yofs}, - apply_rotation ? transform->rotation : 0.0f, - (const float[]){transform->scale_x, transform->scale_y}); + float transform_matrix[4][4]; + + float rotation_matrix[3][3]; + axis_angle_to_mat3_single(rotation_matrix, 'Z', apply_rotation ? transform->rotation : 0.0f); + loc_rot_size_to_mat4(transform_matrix, + (const float[]){transform->xofs, transform->yofs, 0.0f}, + rotation_matrix, + (const float[]){transform->scale_x, transform->scale_y, 1.0f}); const float origin[2] = {image_size[0] * transform->origin[0], image_size[1] * transform->origin[1]}; - const float pivot[2] = {origin[0] - (image_size[0] / 2), origin[1] - (image_size[1] / 2)}; - transform_pivot_set_m3(transform_matrix, pivot); + const float pivot[3] = {origin[0] - (image_size[0] / 2), origin[1] - (image_size[1] / 2), 0.0f}; + transform_pivot_set_m4(transform_matrix, pivot); - r_quad[0][0] = (image_size[0] / 2) - crop->right; - r_quad[0][1] = (image_size[1] / 2) - crop->top; - r_quad[1][0] = (image_size[0] / 2) - crop->right; - r_quad[1][1] = (-image_size[1] / 2) + crop->bottom; - r_quad[2][0] = (-image_size[0] / 2) + crop->left; - r_quad[2][1] = (-image_size[1] / 2) + crop->bottom; - r_quad[3][0] = (-image_size[0] / 2) + crop->left; - r_quad[3][1] = (image_size[1] / 2) - crop->top; + float quad_temp[4][3]; + for (int i = 0; i < 4; i++) { + zero_v2(quad_temp[i]); + } - mul_m3_v2(transform_matrix, r_quad[0]); - mul_m3_v2(transform_matrix, r_quad[1]); - mul_m3_v2(transform_matrix, r_quad[2]); - mul_m3_v2(transform_matrix, r_quad[3]); + quad_temp[0][0] = (image_size[0] / 2) - crop->right; + quad_temp[0][1] = (image_size[1] / 2) - crop->top; + quad_temp[1][0] = (image_size[0] / 2) - crop->right; + quad_temp[1][1] = (-image_size[1] / 2) + crop->bottom; + quad_temp[2][0] = (-image_size[0] / 2) + crop->left; + quad_temp[2][1] = (-image_size[1] / 2) + crop->bottom; + quad_temp[3][0] = (-image_size[0] / 2) + crop->left; + quad_temp[3][1] = (image_size[1] / 2) - crop->top; float mirror[2]; SEQ_image_transform_mirror_factor_get(seq, mirror); - mul_v2_v2(r_quad[0], mirror); - mul_v2_v2(r_quad[1], mirror); - mul_v2_v2(r_quad[2], mirror); - mul_v2_v2(r_quad[3], mirror); + + for (int i = 0; i < 4; i++) { + mul_m4_v3(transform_matrix, quad_temp[i]); + mul_v2_v2(quad_temp[i], mirror); + copy_v2_v2(r_quad[i], quad_temp[i]); + } } void SEQ_image_transform_quad_get(const Scene *scene, From c383397d072f3fdc10857bd4b0e484c0de469e2f Mon Sep 17 00:00:00 2001 From: Josef Raschen Date: Fri, 15 Oct 2021 23:13:31 +0200 Subject: [PATCH 0847/1500] Fix versioning for sequencer color balance modifier. Commit 213554f24a17 added slope/offset/power controls to the sequencer color balance modifier, but colors in this mode were not initialized with old files. Initialize colors to default values. Reviewed By: ISS Differential Revision: https://developer.blender.org/D12806 --- .../blenloader/intern/versioning_300.c | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index af61447e5ad..93c299a48aa 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -456,6 +456,22 @@ static bool do_versions_sequencer_color_tags(Sequence *seq, void *UNUSED(user_da return true; } +static bool do_versions_sequencer_color_balance_sop(Sequence *seq, void *UNUSED(user_data)) +{ + LISTBASE_FOREACH (SequenceModifierData *, smd, &seq->modifiers) { + if (smd->type == seqModifierType_ColorBalance) { + StripColorBalance *cb = &((ColorBalanceModifierData *)smd)->color_balance; + cb->method = SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN; + for (int i = 0; i < 3; i++) { + copy_v3_fl(cb->slope, 1.0f); + copy_v3_fl(cb->offset, 1.0f); + copy_v3_fl(cb->power, 1.0f); + } + } + } + return true; +} + static bNodeLink *find_connected_link(bNodeTree *ntree, bNodeSocket *in_socket) { LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { @@ -1709,6 +1725,13 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Set defaults for new color balance modifier parameters. */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != NULL) { + SEQ_for_each_callback(&scene->ed->seqbase, do_versions_sequencer_color_balance_sop, NULL); + } + } } if (!MAIN_VERSION_ATLEAST(bmain, 300, 33)) { From 41dc55874742e8ee9d1361d729bec25514e1e3ae Mon Sep 17 00:00:00 2001 From: Jarrett Johnson Date: Fri, 15 Oct 2021 16:33:27 -0500 Subject: [PATCH 0848/1500] Geometry Nodes: Rotate Euler: Use "Local" instead of "Point" Since points aren't relevant in function nodes, replace all mentions of it with "local" to illustrate rotations done in local-space instead. Differential Revision: https://developer.blender.org/D12881 --- source/blender/makesdna/DNA_node_types.h | 2 +- source/blender/makesrna/intern/rna_nodetree.c | 8 ++++---- .../nodes/function/nodes/node_fn_rotate_euler.cc | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index d4a2b3449e5..dd749a9dc60 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -2090,7 +2090,7 @@ typedef enum GeometryNodeRotatePointsSpace { typedef enum FunctionNodeRotateEulerSpace { FN_NODE_ROTATE_EULER_SPACE_OBJECT = 0, - FN_NODE_ROTATE_EULER_SPACE_POINT = 1, + FN_NODE_ROTATE_EULER_SPACE_LOCAL = 1, } FunctionNodeRotateEulerSpace; typedef enum GeometryNodeAlignRotationToVectorAxis { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 3ca5e562a5e..9571d889f6d 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9902,10 +9902,10 @@ static void def_fn_rotate_euler(StructRNA *srna) ICON_NONE, "Object", "Rotate the input rotation in the local space of the object"}, - {FN_NODE_ROTATE_EULER_SPACE_POINT, - "POINT", + {FN_NODE_ROTATE_EULER_SPACE_LOCAL, + "LOCAL", ICON_NONE, - "Point", + "Local", "Rotate the input rotation in its local space"}, {0, NULL, 0, NULL, NULL}, }; @@ -9921,7 +9921,7 @@ static void def_fn_rotate_euler(StructRNA *srna) prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom2"); RNA_def_property_enum_items(prop, space_items); - RNA_def_property_ui_text(prop, "Space", "Base orientation of the points"); + RNA_def_property_ui_text(prop, "Space", "Base orientation for rotation"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc index cbae1648663..7db566e96b5 100644 --- a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc +++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc @@ -82,8 +82,8 @@ static const fn::MultiFunction *get_multi_function(bNode &bnode) mat3_to_eul(result, mat_res); return result; }}; - static fn::CustomMF_SI_SI_SO point_euler_rot{ - "Rotate Euler by Euler/Point", [](const float3 &input, const float3 &rotation) { + static fn::CustomMF_SI_SI_SO local_euler_rot{ + "Rotate Euler by Euler/Local", [](const float3 &input, const float3 &rotation) { float input_mat[3][3]; eul_to_mat3(input_mat, input); float rot_mat[3][3]; @@ -94,8 +94,8 @@ static const fn::MultiFunction *get_multi_function(bNode &bnode) mat3_to_eul(result, mat_res); return result; }}; - static fn::CustomMF_SI_SI_SI_SO point_AA_rot{ - "Rotate Euler by AxisAngle/Point", [](const float3 &input, const float3 &axis, float angle) { + static fn::CustomMF_SI_SI_SI_SO local_AA_rot{ + "Rotate Euler by AxisAngle/Local", [](const float3 &input, const float3 &axis, float angle) { float input_mat[3][3]; eul_to_mat3(input_mat, input); float rot_mat[3][3]; @@ -109,10 +109,10 @@ static const fn::MultiFunction *get_multi_function(bNode &bnode) short type = bnode.custom1; short space = bnode.custom2; if (type == FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE) { - return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_AA_rot : &point_AA_rot; + return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_AA_rot : &local_AA_rot; } if (type == FN_NODE_ROTATE_EULER_TYPE_EULER) { - return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_euler_rot : &point_euler_rot; + return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_euler_rot : &local_euler_rot; } BLI_assert_unreachable(); return nullptr; From 73fbd3eebd3f47311785a488dfdeb7353ca10563 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 15 Oct 2021 23:35:49 +0200 Subject: [PATCH 0849/1500] VSE: Hide tool header by default Tools currently used by VSE don't have much useful settings, but they use a lot of space. Therefore these headers will be hidden by default. Property `show_region_tool_header` was added to view menu to enable tool settings. This could be resolved by region overlap, but it isn't working well currently. Differential Revision: https://developer.blender.org/D12875 --- release/scripts/startup/bl_ui/space_sequencer.py | 3 +-- source/blender/blenloader/intern/versioning_defaults.c | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 665e7c54fa7..bbb962e6611 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -169,8 +169,6 @@ class SEQUENCER_HT_header(Header): st = context.space_data - show_region_tool_header = st.show_region_tool_header - layout.template_header() layout.prop(st, "view_type", text="") @@ -421,6 +419,7 @@ class SEQUENCER_MT_view(Menu): # wm_keymap_item_find_props() (see T32595). layout.operator_context = 'INVOKE_REGION_PREVIEW' layout.prop(st, "show_region_ui") + layout.prop(st, "show_region_tool_header") layout.prop(st, "show_region_toolbar") layout.operator_context = 'INVOKE_DEFAULT' diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index c383c1cc4e5..2dcb2c35b22 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -208,7 +208,8 @@ static void blo_update_defaults_screen(bScreen *screen, LISTBASE_FOREACH (ARegion *, region, regionbase) { if (region->regiontype == RGN_TYPE_TOOL_HEADER) { - if ((sl->spacetype == SPACE_IMAGE) && hide_image_tool_header) { + if (((sl->spacetype == SPACE_IMAGE) && hide_image_tool_header) || + sl->spacetype == SPACE_SEQ) { region->flag |= RGN_FLAG_HIDDEN; } else { From 138fdf78ba4c4d691e1ed3a474735b5cffa553b7 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Fri, 15 Oct 2021 17:55:21 -0400 Subject: [PATCH 0850/1500] PyAPI Docs: Fix example not using keyword parameter Fixes T92238 --- doc/python_api/examples/gpu.9.py | 2 +- release/scripts/addons | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/python_api/examples/gpu.9.py b/doc/python_api/examples/gpu.9.py index b0400ce7809..318c0a74ceb 100644 --- a/doc/python_api/examples/gpu.9.py +++ b/doc/python_api/examples/gpu.9.py @@ -32,7 +32,7 @@ def draw(): context.region, view_matrix, projection_matrix, - True) + do_color_management=True) gpu.state.depth_mask_set(False) draw_texture_2d(offscreen.texture_color, (10, 10), WIDTH, HEIGHT) diff --git a/release/scripts/addons b/release/scripts/addons index 67f1fbca148..f10ca8c1561 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 67f1fbca1482d9d9362a4001332e785c3fd5d230 +Subproject commit f10ca8c156169b24e70027a43f718f99571d280f From 5fed3aec4a077b1d14266fc3068241ee8fdd1d7d Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sat, 16 Oct 2021 01:25:27 +0200 Subject: [PATCH 0851/1500] VSE: Fix crash when scene strip is added to meta Caused by 81514b0e913b, missed sanitizing `sound->offset_time` usage in `seq_update_sound_bounds_recursive_impl()`. --- source/blender/sequencer/intern/strip_time.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 3c80e1dba27..1c5f4c3ab76 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -131,12 +131,17 @@ static void seq_update_sound_bounds_recursive_impl(Scene *scene, endofs = seq->start + seq->len - end; } + double offset_time = 0.0f; + if (seq->sound != NULL) { + offset_time = seq->sound->offset_time; + } + BKE_sound_move_scene_sound(scene, seq->scene_sound, seq->start + startofs, seq->start + seq->len - endofs, startofs + seq->anim_startofs, - seq->sound->offset_time); + offset_time); } } } From 458668832370d3aa98b0f75c4587534e75d60fe4 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Fri, 15 Oct 2021 19:17:06 -0700 Subject: [PATCH 0852/1500] Fix nonnull-compare warning in DNA_view3d_types.h Was introduced in rB93a8fd1249f --- source/blender/makesdna/DNA_view3d_types.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index d5db72ea85a..9b5ed133feb 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -501,16 +501,14 @@ enum { }; #define V3D_USES_SCENE_LIGHTS(v3d) \ - ((v3d) && \ - ((((v3d)->shading.type == OB_MATERIAL) && ((v3d)->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || \ - (((v3d)->shading.type == OB_RENDER) && \ - ((v3d)->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)))) + ((((v3d)->shading.type == OB_MATERIAL) && ((v3d)->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || \ + (((v3d)->shading.type == OB_RENDER) && \ + ((v3d)->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER))) #define V3D_USES_SCENE_WORLD(v3d) \ - ((v3d) && \ - ((((v3d)->shading.type == OB_MATERIAL) && ((v3d)->shading.flag & V3D_SHADING_SCENE_WORLD)) || \ - (((v3d)->shading.type == OB_RENDER) && \ - ((v3d)->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)))) + ((((v3d)->shading.type == OB_MATERIAL) && ((v3d)->shading.flag & V3D_SHADING_SCENE_WORLD)) || \ + (((v3d)->shading.type == OB_RENDER) && \ + ((v3d)->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER))) /** #View3DShading.cavity_type */ enum { From 1c18f05f0be0e31c135462d3dea763e97dd26698 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 16 Oct 2021 15:35:36 +1100 Subject: [PATCH 0853/1500] Fix T92252: User after free when opening file after Blender starts Oversight in 6e4ab5b761b03b52177985ecbeb2c2f576159c74 --- source/blender/windowmanager/intern/wm_event_system.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index df4d2c13ba7..d05076bafe2 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3161,10 +3161,10 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis /* This calls handlers twice - to solve (double-)click events. */ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) { + int action = wm_handlers_do_intern(C, CTX_wm_window(C), event, handlers); + /* Will be NULL in the file read case. */ wmWindow *win = CTX_wm_window(C); - int action = wm_handlers_do_intern(C, win, event, handlers); - if (win == NULL) { return action; } From 73753e1a6763d02cf9a77584abd1bd402045d83a Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sat, 16 Oct 2021 09:28:05 +0200 Subject: [PATCH 0854/1500] Asset Browser: Change default name of tags Use "Tag" instead of "Unnamed Tag" as default name for tags. Other default names in Blender also don't add "Unnamed" or similar. --- release/scripts/startup/bl_operators/assets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py index de46e5c85fb..a8ee44037d6 100644 --- a/release/scripts/startup/bl_operators/assets.py +++ b/release/scripts/startup/bl_operators/assets.py @@ -51,7 +51,7 @@ class ASSET_OT_tag_add(AssetBrowserMetadataOperator, Operator): def execute(self, context): active_asset = SpaceAssetInfo.get_active_asset(context) - active_asset.tags.new("Unnamed Tag") + active_asset.tags.new("Tag") return {'FINISHED'} From b3c469153efe0e3f4f36a6bf6667d47727a5a743 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sat, 16 Oct 2021 09:55:31 +0200 Subject: [PATCH 0855/1500] Asset Browser: Use single column for asset library menu Feedback was that the two column menu felt odd, and that the "Custom" and "Built-in" headings for each column were more confusing than helpful. So changing this to a single column menu with separator lines instead of headings. --- .../asset/intern/asset_library_reference_enum.cc | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc index b74fe40ec2e..c57d121a18f 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -116,10 +116,12 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf() EnumPropertyItem *item = nullptr; int totitem = 0; + /* Add predefined items. */ + RNA_enum_items_add(&item, &totitem, predefined_items); + /* Add separator if needed. */ if (!BLI_listbase_is_empty(&U.asset_libraries)) { - const EnumPropertyItem sepr = {0, "", 0, "Custom", nullptr}; - RNA_enum_item_add(&item, &totitem, &sepr); + RNA_enum_item_add_separator(&item, &totitem); } int i = 0; @@ -144,14 +146,6 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf() RNA_enum_item_add(&item, &totitem, &tmp); } - if (totitem) { - const EnumPropertyItem sepr = {0, "", 0, "Built-in", nullptr}; - RNA_enum_item_add(&item, &totitem, &sepr); - } - - /* Add predefined items. */ - RNA_enum_items_add(&item, &totitem, predefined_items); - RNA_enum_item_end(&item, &totitem); return item; } From 5c961b3b582f77f9f7e9fa99476217753c424d32 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 16 Oct 2021 13:54:29 +0200 Subject: [PATCH 0856/1500] Revert "Fix T62325, T91990: changing Cycles presets does not update the Blender UI" This reverts commit 1b6752e599b5ed70823d09f90c418e448516d4b4. It is causing constant redraws due to some ID properties seemingly being edited on every redraw. --- source/blender/makesrna/intern/rna_access.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 88c3db0c65b..40b5f3ed1da 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -2199,11 +2199,7 @@ static void rna_property_update( * but this isn't likely to be a performance problem. */ bool RNA_property_update_check(PropertyRNA *prop) { - return - /* Always update ID properties. */ - (prop->magic != RNA_MAGIC || (prop->flag & PROP_IDPROPERTY)) || - /* For native RNA properties only update if there is a callback or notifier. */ - (prop->update || prop->noteflag); + return (prop->magic != RNA_MAGIC || prop->update || prop->noteflag); } void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop) From 4a00faca1a5e86ee3fb2d55ec98bf5f9ef0ae66a Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Sat, 16 Oct 2021 07:24:30 -0500 Subject: [PATCH 0857/1500] Fix: Geometry Nodes Subdiv Surface Crash If there are no edges in the mesh, the process would crash. Returning in this case. --- .../nodes/geometry/nodes/node_geo_subdivision_surface.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index d37f968caa8..5b480f2dc22 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -97,6 +97,10 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) GeometryComponentFieldContext field_context{mesh_component, domain}; const int domain_size = mesh_component.attribute_domain_size(domain); + if (domain_size == 0) { + return; + } + FieldEvaluator evaluator(field_context, domain_size); evaluator.add(crease_field); evaluator.evaluate(); From 3c3680318932b7e7b4f99cfb1ecae1cd63baf14b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sat, 16 Oct 2021 22:58:30 -0500 Subject: [PATCH 0858/1500] Cleanup: Fix unused variable warning in lite build --- .../nodes/geometry/nodes/node_geo_subdivision_surface.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 5b480f2dc22..410c9a8bb35 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -68,7 +68,10 @@ static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *n static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); - +#ifndef WITH_OPENSUBDIV + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenSubdiv")); +#else Field crease_field = params.extract_input>("Crease"); const NodeGeometrySubdivisionSurface &storage = @@ -83,10 +86,6 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) return; } -#ifndef WITH_OPENSUBDIV - params.error_message_add(NodeWarningType::Error, - TIP_("Disabled, Blender was compiled without OpenSubdiv")); -#else geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (!geometry_set.has_mesh()) { return; From 6b0719c0f3ff43ecf92ef88cdcfb87bf1532f107 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 17 Oct 2021 15:51:33 +0200 Subject: [PATCH 0859/1500] Geometry Nodes: change field visualization when there is an error This does not change the behavior when there are no mistakes in the node tree. The visualization does change when a field is connected to an input that cannot be a field. Differential Revision: https://developer.blender.org/D12877 --- source/blender/blenkernel/intern/node.cc | 49 +++++++++++++----------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 52ba6d1ccb8..d29762e2af4 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4595,12 +4595,16 @@ static FieldInferencingInterface get_node_field_inferencing_interface(const Node * network. */ struct SocketFieldState { - /* This socket is currently a single value. It could become a field though. */ - bool is_single = true; - /* This socket is required to be a single value. It must not be a field. */ - bool requires_single = false; /* This socket starts a new field. */ bool is_field_source = false; + /* This socket can never become a field, because the node itself does not support it. */ + bool is_always_single = false; + /* This socket is currently a single value. It could become a field though. */ + bool is_single = true; + /* This socket is required to be a single value. This can be because the node itself only + * supports this socket to be a single value, or because a node afterwards requires this to be a + * single value. */ + bool requires_single = false; }; static Vector gather_input_socket_dependencies( @@ -4711,6 +4715,7 @@ static void propagate_data_requirements_from_right_to_left( } if (field_dependency.field_type() == OutputSocketFieldType::None) { state.requires_single = true; + state.is_always_single = true; continue; } @@ -4752,6 +4757,7 @@ static void propagate_data_requirements_from_right_to_left( SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) { state.requires_single = true; + state.is_always_single = true; } } } @@ -4817,7 +4823,7 @@ static void propagate_field_status_from_left_to_right( /* Update field state of input sockets, also taking into account linked origin sockets. */ for (const InputSocketRef *input_socket : node->inputs()) { SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; - if (state.requires_single) { + if (state.is_always_single) { state.is_single = true; continue; } @@ -4900,31 +4906,28 @@ static void update_socket_shapes(const NodeTreeRef &tree, const eNodeSocketDisplayShape data_but_can_be_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT; const eNodeSocketDisplayShape is_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND; + auto get_shape_for_state = [&](const SocketFieldState &state) { + if (state.is_always_single) { + return requires_data_shape; + } + if (!state.is_single) { + return is_field_shape; + } + if (state.requires_single) { + return requires_data_shape; + } + return data_but_can_be_field_shape; + }; + for (const InputSocketRef *socket : tree.input_sockets()) { bNodeSocket *bsocket = socket->bsocket(); const SocketFieldState &state = field_state_by_socket_id[socket->id()]; - if (state.requires_single) { - bsocket->display_shape = requires_data_shape; - } - else if (state.is_single) { - bsocket->display_shape = data_but_can_be_field_shape; - } - else { - bsocket->display_shape = is_field_shape; - } + bsocket->display_shape = get_shape_for_state(state); } for (const OutputSocketRef *socket : tree.output_sockets()) { bNodeSocket *bsocket = socket->bsocket(); const SocketFieldState &state = field_state_by_socket_id[socket->id()]; - if (state.requires_single) { - bsocket->display_shape = requires_data_shape; - } - else if (state.is_single) { - bsocket->display_shape = data_but_can_be_field_shape; - } - else { - bsocket->display_shape = is_field_shape; - } + bsocket->display_shape = get_shape_for_state(state); } } From 19740b25c778ccd3fccd0ab33c7efd9f761dc001 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 17 Oct 2021 15:56:37 +0200 Subject: [PATCH 0860/1500] Geometry Nodes: bring back lazy evaluation for field types in Switch node Differential Revision: https://developer.blender.org/D12878 --- .../nodes/geometry/nodes/node_geo_switch.cc | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 05df927fb39..c01fcf5bb5f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -157,23 +157,43 @@ template void switch_fields(GeoNodeExecParams ¶ms, const StringR const std::string name_true = "True" + suffix; const std::string name_output = "Output" + suffix; - /* TODO: Allow for Laziness when the switch field is constant. */ - const bool require_false = params.lazy_require_input(name_false); - const bool require_true = params.lazy_require_input(name_true); - if (require_false | require_true) { - return; + Field switches_field = params.get_input>("Switch"); + if (switches_field.node().depends_on_input()) { + /* The switch has to be incorporated into the field. Both inputs have to be evaluated. */ + const bool require_false = params.lazy_require_input(name_false); + const bool require_true = params.lazy_require_input(name_true); + if (require_false | require_true) { + return; + } + + Field falses_field = params.extract_input>(name_false); + Field trues_field = params.extract_input>(name_true); + + auto switch_fn = std::make_unique>(); + auto switch_op = std::make_shared(FieldOperation( + std::move(switch_fn), + {std::move(switches_field), std::move(falses_field), std::move(trues_field)})); + + params.set_output(name_output, Field(switch_op, 0)); + } + else { + /* The switch input is constant, so just evaluate and forward one of the inputs. */ + const bool switch_value = fn::evaluate_constant_field(switches_field); + if (switch_value) { + params.set_input_unused(name_false); + if (params.lazy_require_input(name_true)) { + return; + } + params.set_output(name_output, params.extract_input>(name_true)); + } + else { + params.set_input_unused(name_true); + if (params.lazy_require_input(name_false)) { + return; + } + params.set_output(name_output, params.extract_input>(name_false)); + } } - - Field switches_field = params.extract_input>("Switch"); - Field falses_field = params.extract_input>(name_false); - Field trues_field = params.extract_input>(name_true); - - auto switch_fn = std::make_unique>(); - auto switch_op = std::make_shared(FieldOperation( - std::move(switch_fn), - {std::move(switches_field), std::move(falses_field), std::move(trues_field)})); - - params.set_output(name_output, Field(switch_op, 0)); } template void switch_no_fields(GeoNodeExecParams ¶ms, const StringRef suffix) From 93544b641bd6687c9b0af3e203a4069977116a78 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Sun, 17 Oct 2021 18:22:53 +0200 Subject: [PATCH 0861/1500] UI: Visual style update to panels Back in Blender 2.30, the GUI project brought panels into Blender among other important visual updates. For the first time it was possible to move the wall of buttons around. Providing a clear separation between sections (it even allowed the grouping of panels in tabs!) During the 2.5 redesign, the separation between panels became a line on top of each panel, and panels received theme settings for background and header colors. The default theme used the same color for both. In 2.8 the background color of panels was different from headers in the default theme, so the separator line was removed. While the separator line wasn't elegant (only on top, non-themeable, hard-coded emboss effect), it provided a sort of separation between panels. This patch solves the panels-separation by simply adding a margin space around them (not visible in default theme yet). Even though the margin reduces the width of the working area slightly, it makes room for the upcoming always-visible scrollbars. Other adjustments: * Use arrow icon instead of triangle to collapse/expand * Use rounded corners to match the rest of the UI (editor corners, nodes, etc). {F10953929, size=full} Margin on panels makes use of the `style->panelouter` property that hasn't been used in a while. Also slight tweaks to `boxspace` and `templatespace` style properties so they are multiples of 2 and operations on them round better. There is technically no need to update the themes for them to work, so no theme changes are included in this patch. {F10953931, size=full} {F10953933, size=full} {F10953934, size=full} {F10954003, size=full} ---- A new theme setting under Style controls the roundness of all panels (added it to Style instead of ThemeSpace because I think controlling the panel roundness per editor is a bit overkill): {F11091561, size=full, autoplay, loop} Reviewed By: HooglyBoogly Differential Revision: https://developer.blender.org/D12814 --- .../datafiles/userdef/userdef_default_theme.c | 1 + .../presets/interface_theme/Blender_Light.xml | 1 + .../startup/bl_ui/properties_constraint.py | 3 +- .../scripts/startup/bl_ui/space_userpref.py | 1 + .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/BKE_screen.h | 2 - .../blenloader/intern/versioning_userdef.c | 4 + .../blender/editors/animation/fmodifier_ui.c | 4 +- source/blender/editors/include/UI_interface.h | 3 +- .../editors/interface/interface_intern.h | 2 +- .../editors/interface/interface_panel.c | 164 ++++++------------ source/blender/editors/screen/area.c | 9 +- .../spreadsheet_row_filter_ui.cc | 2 +- .../intern/MOD_gpencil_ui_common.c | 4 +- source/blender/makesdna/DNA_userdef_types.h | 4 + source/blender/makesrna/intern/rna_ui.c | 1 - source/blender/makesrna/intern/rna_userdef.c | 7 + .../blender/modifiers/intern/MOD_ui_common.c | 4 +- .../blender/shader_fx/intern/FX_ui_common.c | 4 +- 19 files changed, 92 insertions(+), 130 deletions(-) diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 1f9316cacfd..eebdcf84fd2 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -260,6 +260,7 @@ const bTheme U_theme_default = { .icon_modifier = RGBA(0x84b8ffff), .icon_shading = RGBA(0xea7581ff), .icon_folder = RGBA(0xe3c16eff), + .panel_roundness = 0.4f, }, .space_properties = { .back = RGBA(0x42424200), diff --git a/release/scripts/presets/interface_theme/Blender_Light.xml b/release/scripts/presets/interface_theme/Blender_Light.xml index 834139458d5..8b7995cef4c 100644 --- a/release/scripts/presets/interface_theme/Blender_Light.xml +++ b/release/scripts/presets/interface_theme/Blender_Light.xml @@ -9,6 +9,7 @@ widget_emboss="#00000026" editor_outline="#1f1f1f" widget_text_cursor="#3399e6" + panel_roundness="0.4" transparent_checker_primary="#333333" transparent_checker_secondary="#262626" transparent_checker_size="8" diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index 2a0cf56534c..85381f5ee3c 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -70,7 +70,7 @@ class ConstraintButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_label = "" - bl_options = {'INSTANCED', 'HEADER_LAYOUT_EXPAND', 'DRAW_BOX'} + bl_options = {'INSTANCED', 'HEADER_LAYOUT_EXPAND'} @staticmethod def draw_influence(layout, con): @@ -976,7 +976,6 @@ class ConstraintButtonsSubPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_label = "" - bl_options = {'DRAW_BOX'} def get_constraint(self, _context): con = self.custom_data diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 7a8b6d42cad..e5f6cff79ee 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -920,6 +920,7 @@ class USERPREF_PT_theme_interface_styles(ThemePanel, CenterAlignMixIn, Panel): flow.prop(ui, "editor_outline") flow.prop(ui, "widget_text_cursor") flow.prop(ui, "widget_emboss") + flow.prop(ui, "panel_roundness") class USERPREF_PT_theme_interface_transparent_checker(ThemePanel, CenterAlignMixIn, Panel): diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index ceb19e87b40..d8112de760d 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 33 +#define BLENDER_FILE_SUBVERSION 34 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 6f341a12b82..5c913ed851f 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -301,8 +301,6 @@ enum { PANEL_TYPE_LAYOUT_VERT_BAR = (1 << 3), /** This panel type represents data external to the UI. */ PANEL_TYPE_INSTANCED = (1 << 4), - /** Draw panel like a box widget. */ - PANEL_TYPE_DRAW_BOX = (1 << 6), /** Don't search panels with this type during property search. */ PANEL_TYPE_NO_SEARCH = (1 << 7), }; diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 60e202746ff..2c01fae3d6a 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -306,6 +306,10 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) btheme->space_node.movie[3] = U_theme_default.space_node.movie[3]; } + if (!USER_VERSION_ATLEAST(300, 34)) { + btheme->tui.panel_roundness = 0.4f; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 40871fba2be..cea0e50a21f 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -187,7 +187,7 @@ static PanelType *fmodifier_panel_register(ARegionType *region_type, /* Give the panel the special flag that says it was built here and corresponds to a * modifier rather than a #PanelType. */ - panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_INSTANCED; + panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; panel_type->reorder = fmodifier_reorder; panel_type->get_list_data_expand_flag = get_fmodifier_expand_flag; panel_type->set_list_data_expand_flag = set_fmodifier_expand_flag; @@ -221,7 +221,7 @@ static PanelType *fmodifier_subpanel_register(ARegionType *region_type, panel_type->draw_header = draw_header; panel_type->draw = draw; panel_type->poll = poll; - panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED | PANEL_TYPE_DRAW_BOX; + panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED ; BLI_assert(parent != NULL); BLI_strncpy(panel_type->parent_id, parent->idname, BKE_ST_MAXNAME); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index ddd5e77cbb6..c808a42a44a 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -252,7 +252,8 @@ enum { #define UI_PANEL_CATEGORY_MARGIN_WIDTH (U.widget_unit * 1.0f) -#define UI_PANEL_BOX_STYLE_MARGIN (U.widget_unit * 0.2f) +#define UI_PANEL_MARGIN_X (U.widget_unit * 0.4f) +#define UI_PANEL_MARGIN_Y (U.widget_unit * 0.2f) /* but->drawflag - these flags should only affect how the button is drawn. */ /* NOTE: currently, these flags *are not passed* to the widget's state() or draw() functions diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index c7781d65058..0301ab9156e 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -116,7 +116,7 @@ extern const char ui_radial_dir_to_numpad[8]; extern const short ui_radial_dir_to_angle[8]; /* internal panel drawing defines */ -#define PNL_HEADER (UI_UNIT_Y * 1.2) /* 24 default */ +#define PNL_HEADER (UI_UNIT_Y * 1.25) /* 24 default */ /* bit button defines */ /* Bit operations */ diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index a64797af24f..e5f84f63d35 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1112,23 +1112,14 @@ static void panel_draw_highlight_border(const Panel *panel, const rcti *rect, const rcti *header_rect) { - const bool draw_box_style = panel->type->flag & PANEL_TYPE_DRAW_BOX; const bool is_subpanel = panel->type->parent != NULL; if (is_subpanel) { return; } - float radius; - if (draw_box_style) { - /* Use the theme for box widgets. */ - const uiWidgetColors *box_wcol = &UI_GetTheme()->tui.wcol_box; - UI_draw_roundbox_corner_set(UI_CNR_ALL); - radius = box_wcol->roundness * U.widget_unit; - } - else { - UI_draw_roundbox_corner_set(UI_CNR_NONE); - radius = 0.0f; - } + const bTheme *btheme = UI_GetTheme(); + const float radius = btheme->tui.panel_roundness * U.widget_unit * 0.5f; + UI_draw_roundbox_corner_set(UI_CNR_ALL); float color[4]; UI_GetThemeColor4fv(TH_SELECT_ACTIVE, color); @@ -1172,18 +1163,17 @@ static void panel_draw_aligned_widgets(const uiStyle *style, /* Draw collapse icon. */ { - rctf collapse_rect = { - .xmin = widget_rect.xmin, - .xmax = widget_rect.xmin + header_height, - .ymin = widget_rect.ymin, - .ymax = widget_rect.ymax, - }; - BLI_rctf_scale(&collapse_rect, 0.25f); - - float triangle_color[4]; - rgba_uchar_to_float(triangle_color, title_color); - - ui_draw_anti_tria_rect(&collapse_rect, UI_panel_is_closed(panel) ? 'h' : 'v', triangle_color); + const float size_y = BLI_rcti_size_y(&widget_rect); + GPU_blend(GPU_BLEND_ALPHA); + UI_icon_draw_ex(widget_rect.xmin + size_y * 0.2f, + widget_rect.ymin + size_y * 0.2f, + UI_panel_is_closed(panel) ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT, + aspect * U.inv_dpi_fac, + 0.7f, + 0.0f, + title_color, + false); + GPU_blend(GPU_BLEND_NONE); } /* Draw text label. */ @@ -1243,7 +1233,6 @@ static void panel_draw_aligned_backdrop(const Panel *panel, const rcti *rect, const rcti *header_rect) { - const bool draw_box_style = panel->type->flag & PANEL_TYPE_DRAW_BOX; const bool is_subpanel = panel->type->parent != NULL; const bool is_open = !UI_panel_is_closed(panel); @@ -1251,90 +1240,52 @@ static void panel_draw_aligned_backdrop(const Panel *panel, return; } - const uint pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const bTheme *btheme = UI_GetTheme(); + const float radius = btheme->tui.panel_roundness * U.widget_unit * 0.5f; - /* Draw with an opaque box backdrop for box style panels. */ - if (draw_box_style) { - /* Use the theme for box widgets. */ - const uiWidgetColors *box_wcol = &UI_GetTheme()->tui.wcol_box; + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_blend(GPU_BLEND_ALPHA); - if (is_subpanel) { - /* Use rounded bottom corners for the last subpanel. */ - if (panel->next == NULL) { - UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT); - float color[4]; - UI_GetThemeColor4fv(TH_PANEL_SUB_BACK, color); - /* Change the width a little bit to line up with sides. */ - UI_draw_roundbox_aa( - &(const rctf){ - .xmin = rect->xmin + U.pixelsize, - .xmax = rect->xmax - U.pixelsize, - .ymin = rect->ymin + U.pixelsize, - .ymax = rect->ymax, - }, - true, - box_wcol->roundness * U.widget_unit, - color); - } - else { - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColor(TH_PANEL_SUB_BACK); - immRectf(pos, rect->xmin + U.pixelsize, rect->ymin, rect->xmax - U.pixelsize, rect->ymax); - immUnbindProgram(); - } - } - else { - /* Expand the top a tiny bit to give header buttons equal size above and below. */ - rcti box_rect = { - .xmin = rect->xmin, - .xmax = rect->xmax, - .ymin = is_open ? rect->ymin : header_rect->ymin, - .ymax = header_rect->ymax + U.pixelsize, - }; - ui_draw_box_opaque(&box_rect, UI_CNR_ALL); + /* Panel backdrop. */ + if (is_open || panel->type->flag & PANEL_TYPE_NO_HEADER) { + float panel_backcolor[4]; + UI_draw_roundbox_corner_set(is_open ? UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT : UI_CNR_ALL); + UI_GetThemeColor4fv((is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK), panel_backcolor); - /* Mimic the border between aligned box widgets for the bottom of the header. */ - if (is_open) { - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(GPU_BLEND_ALPHA); - - /* Top line. */ - immUniformColor4ubv(box_wcol->outline); - immRectf(pos, rect->xmin, header_rect->ymin - U.pixelsize, rect->xmax, header_rect->ymin); - - /* Bottom "shadow" line. */ - immUniformThemeColor(TH_WIDGET_EMBOSS); - immRectf(pos, - rect->xmin, - header_rect->ymin - U.pixelsize, - rect->xmax, - header_rect->ymin - U.pixelsize - 1); - - GPU_blend(GPU_BLEND_NONE); - immUnbindProgram(); - } - } + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = rect->xmin, + .xmax = rect->xmax, + .ymin = rect->ymin, + .ymax = rect->ymax, + }, + true, + radius, + panel_backcolor); } - else { - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(GPU_BLEND_ALPHA); - /* Panel backdrop. */ - if (is_open || panel->type->flag & PANEL_TYPE_NO_HEADER) { - immUniformThemeColor(is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK); - immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); - } + /* Panel header backdrops for non sub-panels. */ + if (!is_subpanel) { + float panel_headercolor[4]; + UI_GetThemeColor4fv(UI_panel_matches_search_filter(panel) ? TH_MATCH : TH_PANEL_HEADER, + panel_headercolor); + UI_draw_roundbox_corner_set(is_open ? UI_CNR_TOP_RIGHT | UI_CNR_TOP_LEFT : UI_CNR_ALL); - /* Panel header backdrops for non sub-panels. */ - if (!is_subpanel) { - immUniformThemeColor(UI_panel_matches_search_filter(panel) ? TH_MATCH : TH_PANEL_HEADER); - immRectf(pos, rect->xmin, header_rect->ymin, rect->xmax, header_rect->ymax); - } - - GPU_blend(GPU_BLEND_NONE); - immUnbindProgram(); + /* Change the width a little bit to line up with the sides. */ + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = rect->xmin, + .xmax = rect->xmax, + .ymin = header_rect->ymin, + .ymax = header_rect->ymax, + }, + true, + radius, + panel_headercolor); } + + GPU_blend(GPU_BLEND_NONE); + immUnbindProgram(); } /** @@ -1789,9 +1740,9 @@ static bool uiAlignPanelStep(ARegion *region, const float factor, const bool dra const int region_offset_x = panel_region_offset_x_get(region); for (int i = 0; i < active_panels_len; i++) { PanelSort *ps = &panel_sort[i]; - const bool use_box = ps->panel->type->flag & PANEL_TYPE_DRAW_BOX; + const bool no_header = ps->panel->type->flag & PANEL_TYPE_NO_HEADER; ps->panel->runtime.region_ofsx = region_offset_x; - ps->new_offset_x = region_offset_x + ((use_box) ? UI_PANEL_BOX_STYLE_MARGIN : 0); + ps->new_offset_x = region_offset_x + (no_header ? 0 : UI_PANEL_MARGIN_X); } /* Y offset. */ @@ -1799,10 +1750,7 @@ static bool uiAlignPanelStep(ARegion *region, const float factor, const bool dra PanelSort *ps = &panel_sort[i]; y -= get_panel_real_size_y(ps->panel); - const bool use_box = ps->panel->type->flag & PANEL_TYPE_DRAW_BOX; - if (use_box) { - y -= UI_PANEL_BOX_STYLE_MARGIN; - } + y -= UI_PANEL_MARGIN_Y; ps->new_offset_y = y; /* The header still draws offset by the size of closed panels, so apply the offset here. */ if (UI_panel_is_closed(ps->panel)) { diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 473674f1059..406f5c0904f 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -2977,12 +2977,11 @@ void ED_region_panels_layout_ex(const bContext *C, margin_x = category_tabs_width; } - const int w = BLI_rctf_size_x(&v2d->cur) - margin_x; + const int width_no_header = BLI_rctf_size_x(&v2d->cur) - margin_x; + const int width = width_no_header - UI_PANEL_MARGIN_X * 2.0f; /* Works out to 10 * UI_UNIT_X or 20 * UI_UNIT_X. */ const int em = (region->type->prefsizex) ? 10 : 20; - const int w_box_panel = w - UI_PANEL_BOX_STYLE_MARGIN * 2.0f; - /* create panels */ UI_panels_begin(C, region); @@ -3018,7 +3017,7 @@ void ED_region_panels_layout_ex(const bContext *C, ®ion->panels, pt, panel, - (pt->flag & PANEL_TYPE_DRAW_BOX) ? w_box_panel : w, + (pt->flag & PANEL_TYPE_NO_HEADER) ? width_no_header : width, em, NULL, search_filter); @@ -3052,7 +3051,7 @@ void ED_region_panels_layout_ex(const bContext *C, ®ion->panels, panel->type, panel, - (panel->type->flag & PANEL_TYPE_DRAW_BOX) ? w_box_panel : w, + (panel->type->flag & PANEL_TYPE_NO_HEADER) ? width_no_header : width, em, unique_panel_str, search_filter); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc index 219d03c1dcd..78804172981 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc @@ -336,7 +336,7 @@ void register_row_filter_panels(ARegionType ®ion_type) strcpy(panel_type->label, ""); strcpy(panel_type->category, "Filters"); strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); - panel_type->flag = PANEL_TYPE_INSTANCED | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_HEADER_EXPAND; + panel_type->flag = PANEL_TYPE_INSTANCED | PANEL_TYPE_HEADER_EXPAND; panel_type->draw_header = spreadsheet_filter_panel_draw_header; panel_type->draw = spreadsheet_filter_panel_draw; panel_type->get_list_data_expand_flag = get_filter_expand_flag; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c index e750c22f0e8..7dcf887bc45 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c @@ -380,7 +380,7 @@ PanelType *gpencil_modifier_panel_register(ARegionType *region_type, /* Give the panel the special flag that says it was built here and corresponds to a * modifier rather than a #PanelType. */ - panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_INSTANCED; + panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; panel_type->reorder = gpencil_modifier_reorder; panel_type->get_list_data_expand_flag = get_gpencil_modifier_expand_flag; panel_type->set_list_data_expand_flag = set_gpencil_modifier_expand_flag; @@ -413,7 +413,7 @@ PanelType *gpencil_modifier_subpanel_register(ARegionType *region_type, panel_type->draw_header = draw_header; panel_type->draw = draw; panel_type->poll = gpencil_modifier_ui_poll; - panel_type->flag = (PANEL_TYPE_DEFAULT_CLOSED | PANEL_TYPE_DRAW_BOX); + panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED ; BLI_assert(parent != NULL); BLI_strncpy(panel_type->parent_id, parent->idname, BKE_ST_MAXNAME); diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 291f6de5ba2..4a4d0ea2586 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -206,6 +206,10 @@ typedef struct ThemeUI { /** Intensity of the border icons. >0 will render an border around themed * icons. */ float icon_border_intensity; + + float panel_roundness; + char _pad2[4]; + } ThemeUI; /* try to put them all in one, if needed a special struct can be created as well diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index c506a533032..21ffba074fa 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -1373,7 +1373,6 @@ static void rna_def_panel(BlenderRNA *brna) 0, "Expand Header Layout", "Allow buttons in the header to stretch and shrink to fill the entire layout width"}, - {PANEL_TYPE_DRAW_BOX, "DRAW_BOX", 0, "Box Style", "Display panel with the box widget theme"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index ccfc1222d4c..6d7bb9118b1 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -1560,6 +1560,13 @@ static void rna_def_userdef_theme_ui(BlenderRNA *brna) prop, "Text Cursor", "Color of the interface widgets text insertion cursor (caret)"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "panel_roundness", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_text( + prop, "Panel Roundness", "Roundness of the corners of panels and sub-panels"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_default(prop, 0.4f); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + /* Transparent Grid */ prop = RNA_def_property(srna, "transparent_checker_primary", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_float_sdna(prop, NULL, "transparent_checker_primary"); diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 6239ee45e59..5d564464e20 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -448,7 +448,7 @@ PanelType *modifier_panel_register(ARegionType *region_type, ModifierType type, /* Give the panel the special flag that says it was built here and corresponds to a * modifier rather than a #PanelType. */ - panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_INSTANCED; + panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; panel_type->reorder = modifier_reorder; panel_type->get_list_data_expand_flag = get_modifier_expand_flag; panel_type->set_list_data_expand_flag = set_modifier_expand_flag; @@ -482,7 +482,7 @@ PanelType *modifier_subpanel_register(ARegionType *region_type, panel_type->draw_header = draw_header; panel_type->draw = draw; panel_type->poll = modifier_ui_poll; - panel_type->flag = (PANEL_TYPE_DEFAULT_CLOSED | PANEL_TYPE_DRAW_BOX); + panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED; BLI_assert(parent != NULL); BLI_strncpy(panel_type->parent_id, parent->idname, BKE_ST_MAXNAME); diff --git a/source/blender/shader_fx/intern/FX_ui_common.c b/source/blender/shader_fx/intern/FX_ui_common.c index 8cb1ea6b66c..86240171bf9 100644 --- a/source/blender/shader_fx/intern/FX_ui_common.c +++ b/source/blender/shader_fx/intern/FX_ui_common.c @@ -254,7 +254,7 @@ PanelType *shaderfx_panel_register(ARegionType *region_type, ShaderFxType type, /* Give the panel the special flag that says it was built here and corresponds to a * shader effect rather than a PanelType. */ - panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_INSTANCED; + panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; panel_type->reorder = shaderfx_reorder; panel_type->get_list_data_expand_flag = get_shaderfx_expand_flag; panel_type->set_list_data_expand_flag = set_shaderfx_expand_flag; @@ -287,7 +287,7 @@ PanelType *shaderfx_subpanel_register(ARegionType *region_type, panel_type->draw_header = draw_header; panel_type->draw = draw; panel_type->poll = shaderfx_ui_poll; - panel_type->flag = (PANEL_TYPE_DEFAULT_CLOSED | PANEL_TYPE_DRAW_BOX); + panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED; BLI_assert(parent != NULL); BLI_strncpy(panel_type->parent_id, parent->idname, BKE_ST_MAXNAME); From 962b17b3ca140aca3ccce94e0e39c6631f830f8d Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Sun, 17 Oct 2021 18:43:25 +0200 Subject: [PATCH 0862/1500] UI: Adjust header color when active instead of inactive Currently, the background color of headers gets darkened when the editor is not active, this makes it hard to theme, and adds contrast/noise when it's not needed. This patch makes headers use the regular theme color when the editor is not active, so it can be made to flush with the background more easily. And lightens the header (by +10, same value as before) when the editor is active, providing the wanted highlight. The motivations behind this change are: * Simplify picking a theme color for headers. * Widgets already become lighter on mouse hover, this change creates a connection with that concept. Left: current master, inactive header is darkened. Right: this patch, inactive header gets the theme color, active editor gets header in a slightly lighter color (like most widgets) {F11052503, size=full, loop, autoplay} Reviewed By: #user_interface, HooglyBoogly Differential Revision: https://developer.blender.org/D12856 --- source/blender/editors/include/UI_resources.h | 2 +- source/blender/editors/interface/resources.c | 18 ++++++++++-------- source/blender/editors/screen/area.c | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index dde8a637e05..61da496d344 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -64,7 +64,7 @@ typedef enum ThemeColorID { TH_TAB_OUTLINE, TH_HEADER, - TH_HEADERDESEL, + TH_HEADER_ACTIVE, TH_HEADER_TEXT, TH_HEADER_TEXT_HI, diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index e13b69a9763..ad7c6332ee9 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -85,7 +85,7 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) ThemeSpace *ts = NULL; static uchar error[4] = {240, 0, 240, 255}; static uchar alert[4] = {240, 60, 60, 255}; - static uchar headerdesel[4] = {0, 0, 0, 255}; + static uchar header_active[4] = {0, 0, 0, 255}; static uchar back[4] = {0, 0, 0, 255}; static uchar setting = 0; const uchar *cp = error; @@ -249,15 +249,17 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_HEADER: cp = ts->header; break; - case TH_HEADERDESEL: - /* We calculate a dynamic builtin header deselect color, also for pull-downs. */ + + case TH_HEADER_ACTIVE: cp = ts->header; - headerdesel[0] = cp[0] > 10 ? cp[0] - 10 : 0; - headerdesel[1] = cp[1] > 10 ? cp[1] - 10 : 0; - headerdesel[2] = cp[2] > 10 ? cp[2] - 10 : 0; - headerdesel[3] = cp[3]; - cp = headerdesel; + /* Lighten the header color when editor is active. */ + header_active[0] = cp[0] > 245 ? cp[0] - 10 : cp[0] + 10; + header_active[1] = cp[1] > 245 ? cp[1] - 10 : cp[1] + 10; + header_active[2] = cp[2] > 245 ? cp[2] - 10 : cp[2] + 10; + header_active[3] = cp[3]; + cp = header_active; break; + case TH_HEADER_TEXT: cp = ts->header_text; break; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 406f5c0904f..6b0b4d911cc 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -2658,10 +2658,10 @@ static ThemeColorID region_background_color_id(const bContext *C, const ARegion case RGN_TYPE_HEADER: case RGN_TYPE_TOOL_HEADER: if (ED_screen_area_active(C) || ED_area_is_global(area)) { - return TH_HEADER; + return TH_HEADER_ACTIVE; } else { - return TH_HEADERDESEL; + return TH_HEADER; } case RGN_TYPE_PREVIEW: return TH_PREVIEW_BACK; From 452c78757f44fe456dd10ee16bc509ab5455526b Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Sun, 17 Oct 2021 19:06:45 +0200 Subject: [PATCH 0863/1500] UI: Improve contrast on playhead Add an outine around the playhead, matching the color of the background (slightly darkened) to improve the readability of the current frame line when placed against curves or strips with a similar color. {F10944336, size=full} Differential Revision: https://developer.blender.org/D12810 --- source/blender/editors/animation/time_scrub_ui.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index b0eb014c5d9..94366a5e852 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -101,6 +101,7 @@ static void draw_current_frame(const Scene *scene, float text_width = UI_fontstyle_string_width(fstyle, frame_str); float box_width = MAX2(text_width + 8 * UI_DPI_FAC, 24 * UI_DPI_FAC); float box_padding = 3 * UI_DPI_FAC; + const int line_outline = max_ii(1, round_fl_to_int(1 * UI_DPI_FAC)); float bg_color[4]; UI_GetThemeColorShade4fv(TH_CFRAME, -5, bg_color); @@ -109,7 +110,19 @@ static void draw_current_frame(const Scene *scene, const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene)); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + GPU_blend(GPU_BLEND_ALPHA); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + /* Outline. */ + immUniformThemeColorShadeAlpha(TH_BACK, -25, -100); + immRectf(pos, + subframe_x - (line_outline + U.pixelsize), + scrub_region_rect->ymax - box_padding, + subframe_x + (line_outline + U.pixelsize), + 0.0f); + + /* Line. */ immUniformThemeColor(TH_CFRAME); immRectf(pos, subframe_x - U.pixelsize, @@ -117,6 +130,7 @@ static void draw_current_frame(const Scene *scene, subframe_x + U.pixelsize, 0.0f); immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); UI_draw_roundbox_corner_set(UI_CNR_ALL); From c5a13ffcb4b98bbd46dca7637051a966d18cd46f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Oct 2021 11:16:24 +1100 Subject: [PATCH 0864/1500] Cleanup: spelling in comments --- intern/cycles/integrator/path_trace_work_gpu.cpp | 2 +- source/blender/blenkernel/BKE_paint.h | 2 +- source/blender/blenkernel/BKE_pbvh.h | 6 +++--- source/blender/blenkernel/intern/action.c | 2 +- source/blender/blenkernel/intern/armature.c | 2 +- source/blender/blenkernel/intern/blendfile.c | 2 +- source/blender/blenkernel/intern/dynamicpaint.c | 8 ++++---- source/blender/blenkernel/intern/pbvh.c | 4 ++-- source/blender/blenkernel/intern/shrinkwrap.c | 2 +- source/blender/blenkernel/intern/text.c | 2 +- source/blender/blenlib/BLI_filereader.h | 10 +++++----- source/blender/blenlib/BLI_math_geom.h | 4 ++-- source/blender/blenlib/intern/fileops.c | 2 +- source/blender/blenlib/intern/mesh_intersect.cc | 2 +- source/blender/blenloader/intern/readfile.c | 4 ++-- source/blender/blenloader/intern/versioning_300.c | 2 +- source/blender/draw/engines/eevee/eevee_effects.c | 4 ++-- source/blender/editors/screen/screen_edit.c | 2 +- source/blender/editors/sculpt_paint/sculpt_transform.c | 2 +- source/blender/editors/space_text/text_format_pov.c | 6 +++--- .../blender/editors/space_text/text_format_pov_ini.c | 4 ++-- source/blender/editors/transform/transform_snap.c | 2 +- source/blender/editors/util/numinput.c | 3 +-- source/blender/makesdna/DNA_constraint_types.h | 2 +- source/blender/makesdna/DNA_gpu_types.h | 2 +- source/blender/makesdna/DNA_scene_types.h | 2 +- source/blender/makesdna/DNA_space_types.h | 2 +- source/blender/makesdna/intern/dna_genfile.c | 2 +- .../geometry/nodes/node_geo_transfer_attribute.cc | 8 ++++---- source/blender/windowmanager/intern/wm_event_system.c | 2 +- source/blender/windowmanager/intern/wm_files.c | 2 +- source/creator/creator_signals.c | 4 +++- 32 files changed, 53 insertions(+), 52 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 02830a00405..bc380f269ad 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -344,7 +344,7 @@ bool PathTraceWorkGPU::enqueue_path_iteration() /* Finish shadows before potentially adding more shadow rays. We can only * store one shadow ray in the integrator state. * - * When there is a shadow catcher in the scene finish shadow rays before invoking interesect + * When there is a shadow catcher in the scene finish shadow rays before invoking intersect * closest kernel since so that the shadow paths are writing to the pre-split state. */ if (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE || diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 73413b61456..6fc5ef4d870 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -538,7 +538,7 @@ typedef struct SculptSession { float cursor_sampled_normal[3]; float cursor_view_normal[3]; - /* For Sculpt trimming gesture tools, initial raycast data from the position of the mouse when + /* For Sculpt trimming gesture tools, initial ray-cast data from the position of the mouse when * the gesture starts (intersection with the surface and if they ray hit the surface or not). */ float gesture_initial_location[3]; float gesture_initial_normal[3]; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 056a7e2d897..3a0e9d48af7 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -128,8 +128,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, void BKE_pbvh_free(PBVH *pbvh); /* Hierarchical Search in the BVH, two methods: - * - for each hit calling a callback - * - gather nodes in an array (easy to multithread) */ + * - For each hit calling a callback. + * - Gather nodes in an array (easy to multi-thread). */ void BKE_pbvh_search_callback(PBVH *pbvh, BKE_pbvh_SearchCallback scb, @@ -140,7 +140,7 @@ void BKE_pbvh_search_callback(PBVH *pbvh, void BKE_pbvh_search_gather( PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***array, int *tot); -/* Raycast +/* Ray-cast * the hit callback is called for all leaf nodes intersecting the ray; * it's up to the callback to find the primitive within the leaves that is * hit first */ diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 16d269f9e26..65900ec0f4b 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -1211,7 +1211,7 @@ void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_f /* copy bone group */ pchan->agrp_index = pchan_from->agrp_index; - /* ik (dof) settings */ + /* IK (DOF) settings. */ pchan->ikflag = pchan_from->ikflag; copy_v3_v3(pchan->limitmin, pchan_from->limitmin); copy_v3_v3(pchan->limitmax, pchan_from->limitmax); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index e8a3db11219..a60cba3c892 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2763,7 +2763,7 @@ void BKE_pose_where_is_bone(struct Depsgraph *depsgraph, float ctime, bool do_extra) { - /* This gives a chan_mat with actions (ipos) results. */ + /* This gives a chan_mat with actions (F-curve) results. */ if (do_extra) { BKE_pchan_calc_mat(pchan); } diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 1213ecc20ee..fc535fc2ad1 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -251,7 +251,7 @@ static void setup_app_data(bContext *C, * replace it with 'curscene' if its needed */ } /* and we enforce curscene to be in current screen */ - else if (win) { /* can run in bgmode */ + else if (win) { /* The window may be NULL in background-mode. */ win->scene = curscene; } diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 9083c507160..6fc3e0fa8e2 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -112,7 +112,7 @@ static int neighStraightY[8] = {0, 1, 0, -1, 1, 1, -1, -1}; #define SUBFRAME_RECURSION 5 /* surface_getBrushFlags() return vals */ #define BRUSH_USES_VELOCITY (1 << 0) -/* brush mesh raycast status */ +/* Brush mesh ray-cast status. */ #define HIT_VOLUME 1 #define HIT_PROXIMITY 2 /* dynamicPaint_findNeighborPixel() return codes */ @@ -3436,7 +3436,7 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, /***************************** Ray / Nearest Point Utils ******************************/ -/* A modified callback to bvh tree raycast. +/* A modified callback to bvh tree ray-cast. * The tree must have been built using bvhtree_from_mesh_looptri. * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. * @@ -4107,7 +4107,7 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex( hit.index = -1; hit.dist = brush_radius; - /* Do a face normal directional raycast, and use that distance */ + /* Do a face normal directional ray-cast, and use that distance. */ BLI_bvhtree_ray_cast( treeData->tree, ray_start, proj_ray, 0.0f, &hit, mesh_tris_spherecast_dp, treeData); if (hit.index != -1) { @@ -4597,7 +4597,7 @@ static bool dynamicPaint_paintParticles(DynamicPaintSurface *surface, } /* - * Build a kd-tree to optimize distance search + * Build a KD-tree to optimize distance search */ tree = BLI_kdtree_3d_new(psys->totpart); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index a24b53e48b7..3358f3e6dea 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1379,7 +1379,7 @@ static int pbvh_flush_bb(PBVH *pbvh, PBVHNode *node, int flag) { int update = 0; - /* difficult to multithread well, we just do single threaded recursive */ + /* Difficult to multi-thread well, we just do single threaded recursive. */ if (node->flag & PBVH_Leaf) { if (flag & PBVH_UpdateBB) { update |= (node->flag & PBVH_UpdateBB); @@ -1977,7 +1977,7 @@ bool BKE_pbvh_node_vert_update_check_any(PBVH *pbvh, PBVHNode *node) return false; } -/********************************* Raycast ***********************************/ +/********************************* Ray-cast ***********************************/ typedef struct { struct IsectRayAABB_Precalc ray; diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index 7c0c28d664e..dd863f1ce06 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -644,7 +644,7 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) /* Options about projection direction */ float proj_axis[3] = {0.0f, 0.0f, 0.0f}; - /* Raycast and tree stuff */ + /* Ray-cast and tree stuff. */ /** \note 'hit.dist' is kept in the targets space, this is only used * for finding the best hit, to get the real dist, diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 5eb40b6624a..0cb2218e7e0 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -480,7 +480,7 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const BLI_stat_t st; BLI_strncpy(filepath_abs, file, FILE_MAX); - if (relpath) { /* can be NULL (bg mode) */ + if (relpath) { /* Can be NULL (background mode). */ BLI_path_abs(filepath_abs, relpath); } diff --git a/source/blender/blenlib/BLI_filereader.h b/source/blender/blenlib/BLI_filereader.h index 8d1fa3d1596..da223cddf40 100644 --- a/source/blender/blenlib/BLI_filereader.h +++ b/source/blender/blenlib/BLI_filereader.h @@ -59,9 +59,9 @@ typedef struct FileReader { /* Functions for opening the various types of FileReader. * They either succeed and return a valid FileReader, or fail and return NULL. * - * If a FileReader is created, it has to be cleaned up and freed by calling - * its close() function unless another FileReader has taken ownership - for example, - * Zstd and Gzip take over the base FileReader and will clean it up when their clean() is called. + * If a FileReader is created, it has to be cleaned up and freed by calling its close() + * function unless another FileReader has taken ownership - for example, `Zstd` & `Gzip` + * take over the base FileReader and will clean it up when their clean() is called. */ /* Create FileReader from raw file descriptor. */ @@ -71,9 +71,9 @@ FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT; /* Create FileReader from a region of memory. */ FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -/* Create FileReader from applying Zstd decompression on an underlying file. */ +/* Create FileReader from applying `Zstd` decompression on an underlying file. */ FileReader *BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -/* Create FileReader from applying Gzip decompression on an underlying file. */ +/* Create FileReader from applying `Gzip` decompression on an underlying file. */ FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); #ifdef __cplusplus diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index fa9a30467ac..be10b302144 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -438,9 +438,9 @@ bool isect_tri_tri_v2(const float p1[2], const float q2[2], const float r2[2]); -/* water-tight raycast (requires pre-calculation) */ +/* water-tight ray-cast (requires pre-calculation). */ struct IsectRayPrecalc { - /* Maximal dimension kz, and orthogonal dimensions. */ + /* Maximal dimension `kz`, and orthogonal dimensions. */ int kx, ky, kz; /* Shear constants. */ diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 7019acfbbdc..c52feb097d2 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -89,7 +89,7 @@ size_t BLI_file_zstd_from_mem_at_pos( total_written += output.pos; } - /* Finalize the Zstd frame. */ + /* Finalize the `Zstd` frame. */ size_t ret = 1; while (ret != 0) { ZSTD_outBuffer output = {out_buf, out_len, 0}; diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 8276890eec1..feb7b64f766 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -1162,7 +1162,7 @@ static int filter_plane_side(const double3 &p, } /* - * interesect_tri_tri and helper functions. + * #intersect_tri_tri and helper functions. * This code uses the algorithm of Guigue and Devillers, as described * in "Faster Triangle-Triangle Intersection Tests". * Adapted from code by Eric Haines: diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index a41b0641fc7..f82c97443c1 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1230,13 +1230,13 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, else if (BLI_file_magic_is_gzip(header)) { file = BLI_filereader_new_gzip(rawfile); if (file != NULL) { - rawfile = NULL; /* The Gzip FileReader takes ownership of `rawfile`. */ + rawfile = NULL; /* The `Gzip` #FileReader takes ownership of `rawfile`. */ } } else if (BLI_file_magic_is_zstd(header)) { file = BLI_filereader_new_zstd(rawfile); if (file != NULL) { - rawfile = NULL; /* The Zstd FileReader takes ownership of `rawfile`. */ + rawfile = NULL; /* The `Zstd` #FileReader takes ownership of `rawfile`. */ } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 93c299a48aa..035642a561a 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1770,7 +1770,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { /* Keep this block, even when empty. */ - /* Update the idnames for renamed geo and function nodes */ + /* Update the `idnames` for renamed geometry and function nodes. */ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type != NTREE_GEOMETRY) { continue; diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index 87df5f11c80..c2f3e3a6d95 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -488,7 +488,7 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) EEVEE_depth_of_field_draw(vedata); /* NOTE: Lookdev drawing happens before TAA but after - * motion blur and dof to avoid distortions. + * motion blur and DOF to avoid distortions. * Velocity resolve use a hack to exclude lookdev * spheres from creating shimmering re-projection vectors. */ EEVEE_lookdev_draw(vedata); @@ -500,7 +500,7 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) * the swapping of the buffers. */ EEVEE_renderpasses_output_accumulate(sldata, vedata, true); - /* Save the final texture and framebuffer for final transformation or read. */ + /* Save the final texture and frame-buffer for final transformation or read. */ effects->final_tx = effects->source_buffer; effects->final_fb = (effects->target_buffer != fbl->main_color_fb) ? fbl->main_fb : fbl->effect_fb; diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 02b1e002d86..79ef1c99b1d 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -645,7 +645,7 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) { bScreen *screen = WM_window_get_active_screen(win); - /* exception for bg mode, we only need the screen context */ + /* Exception for background mode, we only need the screen context. */ if (!G.background) { /* header size depends on DPI, let's verify */ WM_window_set_dpi(win); diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 3c0a591e8a7..bfbe545d1ef 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -311,7 +311,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) else if (mode == SCULPT_PIVOT_POSITION_ACTIVE_VERTEX) { copy_v3_v3(ss->pivot_pos, SCULPT_active_vertex_co_get(ss)); } - /* Pivot to raycast surface. */ + /* Pivot to ray-cast surface. */ else if (mode == SCULPT_PIVOT_POSITION_CURSOR_SURFACE) { float stroke_location[3]; float mouse[2]; diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c index ea3d0ec1478..7af59c00499 100644 --- a/source/blender/editors/space_text/text_format_pov.c +++ b/source/blender/editors/space_text/text_format_pov.c @@ -709,7 +709,7 @@ static int txtfmt_pov_find_bool(const char *string) /* Keep aligned args for readability. */ /* clang-format off */ - /* Built-in Constants */ + /* Built-in Constants. */ if (STR_LITERAL_STARTSWITH(string, "unofficial", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "false", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "no", len)) { i = len; @@ -719,7 +719,7 @@ static int txtfmt_pov_find_bool(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "on", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "pi", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "tau", len)) { i = len; - /* Encodings */ + /* Encodings. */ } else if (STR_LITERAL_STARTSWITH(string, "sint16be", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "sint16le", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "sint32be", len)) { i = len; @@ -732,7 +732,7 @@ static int txtfmt_pov_find_bool(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "uint8", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "ascii", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "utf8", len)) { i = len; - /* Filetypes */ + /* File-types. */ } else if (STR_LITERAL_STARTSWITH(string, "tiff", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "df3", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "exr", len)) { i = len; diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c index 259ad02a6b7..4e6945a279c 100644 --- a/source/blender/editors/space_text/text_format_pov_ini.c +++ b/source/blender/editors/space_text/text_format_pov_ini.c @@ -279,7 +279,7 @@ static int txtfmt_ini_find_reserved(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "Dither", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "Flags", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "Font", len)) { i = len; - /* Filetypes */ + /* File-types. */ } else if (STR_LITERAL_STARTSWITH(string, "df3", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "exr", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "gif", len)) { i = len; @@ -292,7 +292,7 @@ static int txtfmt_ini_find_reserved(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "sys", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "tga", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "tiff", len)) { i = len; - /* Encodings */ + /* Encodings. */ } else if (STR_LITERAL_STARTSWITH(string, "ascii", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "utf8", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "uint8", len)) { i = len; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 5eab059e049..2e1e8eb4ca4 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -1325,7 +1325,7 @@ bool peelObjectsTransform(TransInfo *t, } } } - /* in this case has only one hit. treat as raycast */ + /* In this case has only one hit. treat as ray-cast. */ if (hit_max == NULL) { hit_max = hit_min; } diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index d0eed6a6eb1..d0f27770d9b 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -472,8 +472,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) return true; case EVT_PADPERIOD: case EVT_PERIODKEY: - /* Force numdot, some OSs/countries generate a comma char in this case, - * sic... (T37992) */ + /* Force number-pad "." since some OS's/countries generate a comma char, see: T37992 */ ascii[0] = '.'; utf8_buf = ascii; break; diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 28756395f7d..6dc2d00252f 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -939,7 +939,7 @@ typedef enum eTrackToAxis_Modes { /* Shrinkwrap flags */ typedef enum eShrinkwrap_Flags { - /* Also raycast in the opposite direction. */ + /* Also ray-cast in the opposite direction. */ CON_SHRINKWRAP_PROJECT_OPPOSITE = (1 << 0), /* Invert the cull mode when projecting opposite. */ CON_SHRINKWRAP_PROJECT_INVERT_CULL = (1 << 1), diff --git a/source/blender/makesdna/DNA_gpu_types.h b/source/blender/makesdna/DNA_gpu_types.h index 8cea1451525..7d1e7d4e4f2 100644 --- a/source/blender/makesdna/DNA_gpu_types.h +++ b/source/blender/makesdna/DNA_gpu_types.h @@ -28,7 +28,7 @@ extern "C" { #endif /* Keep for 'Camera' versioning. */ -/** Properties for dof effect. */ +/** Properties for DOF effect. */ typedef struct GPUDOFSettings { /** Focal distance for depth of field. */ float focus_distance; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 9a5c51825e1..eeff5473d16 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -34,7 +34,7 @@ #include "DNA_ID.h" #include "DNA_color_types.h" /* color management */ -#include "DNA_customdata_types.h" /* Scene's runtime cddata masks. */ +#include "DNA_customdata_types.h" /* Scene's runtime custom-data masks. */ #include "DNA_layer_types.h" #include "DNA_listBase.h" #include "DNA_vec_types.h" diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index ac55fa0df01..ff06e88ab39 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -772,7 +772,7 @@ typedef struct FileSelectParams { const ID *rename_id; void *_pad3; - /** List of filetypes to filter (FILE_MAXFILE). */ + /** List of file-types to filter (#FILE_MAXFILE). */ char filter_glob[256]; /** Text items name must match to be shown. */ diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c index 34d2260d35b..83e55f3bf0d 100644 --- a/source/blender/makesdna/intern/dna_genfile.c +++ b/source/blender/makesdna/intern/dna_genfile.c @@ -1800,7 +1800,7 @@ static void sdna_expand_names(SDNA *sdna) int names_expand_index = 0; for (int struct_nr = 0; struct_nr < sdna->structs_len; struct_nr++) { - /* We can't edit this memory 'sdna->structs' points to (readonly datatoc file). */ + /* We can't edit this memory 'sdna->structs' points to (read-only `datatoc` file). */ const SDNA_Struct *struct_old = sdna->structs[struct_nr]; const int array_size = sizeof(short) * 2 + sizeof(SDNA_StructMember) * struct_old->members_len; diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index b529ebbdde8..63792304726 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -495,7 +495,7 @@ class NearestTransferFunction : public fn::MultiFunction { Array mesh_indices; Array mesh_distances; - /* If there is a pointcloud, find the closest points. */ + /* If there is a point-cloud, find the closest points. */ if (use_points_) { point_indices.reinitialize(tot_samples); if (use_mesh_) { @@ -594,7 +594,7 @@ static const GeometryComponent *find_best_match_component(const GeometrySet &geo /* If there is no component of the same type, choose the other component based on a consistent * order, rather than some more complicated heuristic. This is the same order visible in the - * spreadsheet and used in the raycast node. */ + * spreadsheet and used in the ray-cast node. */ static const Array supported_types = { GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; for (const GeometryComponentType src_type : supported_types) { @@ -607,8 +607,8 @@ static const GeometryComponent *find_best_match_component(const GeometrySet &geo } /** - * Use a FieldInput because it's necessary to know the field context in order to choose the - * corresponding component type from the input geometry, and only a FieldInput receives the + * Use a #FieldInput because it's necessary to know the field context in order to choose the + * corresponding component type from the input geometry, and only a #FieldInput receives the * evaluation context to provide its data. * * The index-based transfer theoretically does not need realized data when there is only one diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index d05076bafe2..b86d639dc4c 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3837,7 +3837,7 @@ void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval } /* Operator is supposed to have a filled "path" property. */ -/* Optional property: filetype (XXX enum?) */ +/* Optional property: file-type (XXX enum?) */ /** * The idea here is to keep a handler alive on window queue, owning the operator. diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 564f869581a..9d3fd9b2ec9 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -526,7 +526,7 @@ static int wm_read_exotic(const char *name) /* check for compressed .blend */ FileReader *compressed_file = NULL; if (BLI_file_magic_is_gzip(header)) { - /* In earlier versions of Blender (before 3.0), compressed files used Gzip instead of Zstd. + /* In earlier versions of Blender (before 3.0), compressed files used `Gzip` instead of `Zstd`. * While these files will no longer be written, there still needs to be reading support. */ compressed_file = BLI_filereader_new_gzip(rawfile); } diff --git a/source/creator/creator_signals.c b/source/creator/creator_signals.c index 5604fb4c58d..b74264fdddd 100644 --- a/source/creator/creator_signals.c +++ b/source/creator/creator_signals.c @@ -258,7 +258,9 @@ void main_signal_setup_background(void) BLI_assert(G.background); # if !defined(WITH_HEADLESS) - signal(SIGINT, sig_handle_blender_esc); /* ctrl c out bg render */ + /* Support pressing `Ctrl-C` to close Blender in background-mode. + * Useful to be able to cancel a render operation. */ + signal(SIGINT, sig_handle_blender_esc); # endif } From e538b2c3a38f130787ca604b0e0182a74e9d6976 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Oct 2021 15:47:28 +1100 Subject: [PATCH 0865/1500] Cleanup: internal sequencer naming for overlays - Rename RNA SpaceSeq.show_strip_overlay to show_overlays matching the 3D View, the term "strip" was misleading as this is used for the preview as well. - Rename various RNA overlay settings to overlay_frame since "Frame Offset" is a specific feature, avoid having both Editor.show_overlay and SpaceSeq.show_overlays. - Rename Editing `over_*` -> `overlay_frame_*` in DNA, as well as flags. --- .../keyconfig/keymap_data/blender_default.py | 2 +- .../scripts/startup/bl_ui/space_sequencer.py | 18 +++++------ .../space_sequencer/sequencer_buttons.c | 5 +-- .../editors/space_sequencer/sequencer_draw.c | 25 ++++++++------- .../editors/space_sequencer/sequencer_view.c | 2 +- .../editors/space_sequencer/space_sequencer.c | 15 +++++---- source/blender/makesdna/DNA_sequence_types.h | 14 ++++---- source/blender/makesdna/DNA_space_types.h | 12 +++---- .../blender/makesdna/intern/dna_rename_defs.h | 5 +++ .../blender/makesrna/intern/rna_sequencer.c | 32 +++++++++---------- source/blender/makesrna/intern/rna_space.c | 16 +++++----- 11 files changed, 78 insertions(+), 68 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 07f4a40e118..1e25b33cc20 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2684,7 +2684,7 @@ def km_sequencercommon(params): sidebar_key={"type": 'N', "value": 'PRESS'}, ), ("wm.context_toggle", {"type": 'O', "value": 'PRESS', "shift": True}, - {"properties": [("data_path", 'scene.sequence_editor.show_overlay')]}), + {"properties": [("data_path", 'scene.sequence_editor.show_overlay_frame')]}), ("wm.context_toggle_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, {"properties": [("data_path", 'space_data.view_type'), ("value_1", 'SEQUENCER'), ("value_2", 'PREVIEW')]}), ("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index bbb962e6611..01c33db2ddc 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -215,10 +215,10 @@ class SEQUENCER_HT_header(Header): ) row = layout.row(align=True) - row.prop(st, "show_strip_overlay", text="", icon='OVERLAY') + row.prop(st, "show_overlays", text="", icon='OVERLAY') sub = row.row(align=True) sub.popover(panel="SEQUENCER_PT_overlay", text="") - sub.active = st.show_strip_overlay + sub.active = st.show_overlays class SEQUENCER_MT_editor_menus(Menu): @@ -289,9 +289,9 @@ class SEQUENCER_PT_preview_overlay(Panel): overlay_settings = st.preview_overlay layout = self.layout - layout.active = st.show_strip_overlay + layout.active = st.show_overlays layout.prop(overlay_settings, "show_image_outline") - layout.prop(ed, "show_overlay", text="Frame Overlay") + layout.prop(ed, "show_overlay_frame", text="Frame Overlay") layout.prop(overlay_settings, "show_safe_areas", text="Safe Areas") layout.prop(overlay_settings, "show_metadata", text="Metadata") layout.prop(overlay_settings, "show_annotation", text="Annotations") @@ -313,7 +313,7 @@ class SEQUENCER_PT_sequencer_overlay(Panel): overlay_settings = st.timeline_overlay layout = self.layout - layout.active = st.show_strip_overlay + layout.active = st.show_overlays layout.prop(overlay_settings, "show_strip_name", text="Name") layout.prop(overlay_settings, "show_strip_source", text="Source") @@ -2265,7 +2265,7 @@ class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel): scene = context.scene ed = scene.sequence_editor - self.layout.prop(ed, "show_overlay", text="") + self.layout.prop(ed, "show_overlay_frame", text="") def draw(self, context): layout = self.layout @@ -2281,12 +2281,12 @@ class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel): scene = context.scene ed = scene.sequence_editor - layout.active = ed.show_overlay + layout.active = ed.show_overlay_frame col = layout.column() col.prop(ed, "overlay_frame", text="Frame Offset") - col.prop(st, "overlay_type") - col.prop(ed, "use_overlay_lock") + col.prop(st, "overlay_frame_type") + col.prop(ed, "use_overlay_frame_lock") class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel): diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index f0940cd9f55..4ece7f6a481 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -78,9 +78,10 @@ static void metadata_panel_context_draw(const bContext *C, Panel *panel) SpaceSeq *space_sequencer = CTX_wm_space_seq(C); /* NOTE: We can only reliably show metadata for the original (current) * frame when split view is used. */ - const bool show_split = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_USE_FRAME_OVERLAY) && + const bool show_split = (scene->ed && + (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_SHOW) && (space_sequencer->mainb == SEQ_DRAW_IMG_IMBUF)); - if (show_split && space_sequencer->overlay_type == SEQ_DRAW_OVERLAY_REFERENCE) { + if (show_split && (space_sequencer->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_REFERENCE)) { return; } /* NOTE: We disable multiview for drawing, since we don't know what is the diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 922b6b251ed..7117323c214 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1862,15 +1862,15 @@ static void sequencer_preview_get_rect(rctf *preview, sequencer_display_size(scene, viewrect); BLI_rctf_init(preview, -1.0f, 1.0f, -1.0f, 1.0f); - if (draw_overlay && sseq->overlay_type == SEQ_DRAW_OVERLAY_RECT) { + if (draw_overlay && (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_RECT)) { preview->xmax = v2d->tot.xmin + - (fabsf(BLI_rctf_size_x(&v2d->tot)) * scene->ed->over_border.xmax); + (fabsf(BLI_rctf_size_x(&v2d->tot)) * scene->ed->overlay_frame_rect.xmax); preview->xmin = v2d->tot.xmin + - (fabsf(BLI_rctf_size_x(&v2d->tot)) * scene->ed->over_border.xmin); + (fabsf(BLI_rctf_size_x(&v2d->tot)) * scene->ed->overlay_frame_rect.xmin); preview->ymax = v2d->tot.ymin + - (fabsf(BLI_rctf_size_y(&v2d->tot)) * scene->ed->over_border.ymax); + (fabsf(BLI_rctf_size_y(&v2d->tot)) * scene->ed->overlay_frame_rect.ymax); preview->ymin = v2d->tot.ymin + - (fabsf(BLI_rctf_size_y(&v2d->tot)) * scene->ed->over_border.ymin); + (fabsf(BLI_rctf_size_y(&v2d->tot)) * scene->ed->overlay_frame_rect.ymin); } else if (draw_backdrop) { float aspect = BLI_rcti_size_x(®ion->winrct) / (float)BLI_rcti_size_y(®ion->winrct); @@ -1958,8 +1958,8 @@ static void sequencer_draw_display_buffer(const bContext *C, rctf canvas; sequencer_preview_get_rect(&preview, scene, region, sseq, draw_overlay, draw_backdrop); - if (draw_overlay && sseq->overlay_type == SEQ_DRAW_OVERLAY_RECT) { - canvas = scene->ed->over_border; + if (draw_overlay && (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_RECT)) { + canvas = scene->ed->overlay_frame_rect; } else { BLI_rctf_init(&canvas, 0.0f, 1.0f, 0.0f, 1.0f); @@ -2207,7 +2207,8 @@ void sequencer_draw_preview(const bContext *C, UI_view2d_view_ortho(v2d); /* Draw background. */ - if (!draw_backdrop && (!draw_overlay || sseq->overlay_type == SEQ_DRAW_OVERLAY_REFERENCE)) { + if (!draw_backdrop && + (!draw_overlay || (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_REFERENCE))) { sequencer_preview_clear(); if (sseq->flag & SEQ_USE_ALPHA) { @@ -2685,9 +2686,9 @@ static void draw_cache_view(const bContext *C) /* Draw sequencer timeline. */ static void draw_overlap_frame_indicator(const struct Scene *scene, const View2D *v2d) { - int overlap_frame = (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) ? - scene->ed->over_cfra : - scene->r.cfra + scene->ed->over_ofs; + int overlap_frame = (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) ? + scene->ed->overlay_frame_abs : + scene->r.cfra + scene->ed->overlay_frame_ofs; uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); @@ -2807,7 +2808,7 @@ void draw_timeline_seq_display(const bContext *C, ARegion *region) if (scene->ed != NULL) { UI_view2d_view_ortho(v2d); draw_cache_view(C); - if (scene->ed->over_flag & SEQ_EDIT_USE_FRAME_OVERLAY) { + if (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_SHOW) { draw_overlap_frame_indicator(scene, v2d); } UI_view2d_view_restore(C); diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 981f793c896..79593b0bbb0 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -380,7 +380,7 @@ static int view_ghost_border_exec(bContext *C, wmOperator *op) CLAMP(rect.xmax, 0.0f, 1.0f); CLAMP(rect.ymax, 0.0f, 1.0f); - scene->ed->over_border = rect; + scene->ed->overlay_frame_rect = rect; WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 3ed8b35c9bf..c876bf7d483 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -783,25 +783,26 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) wmWindowManager *wm = CTX_wm_manager(C); const bool draw_overlay = sseq->flag & SEQ_SHOW_OVERLAY; const bool draw_frame_overlay = (scene->ed && - (scene->ed->over_flag & SEQ_EDIT_USE_FRAME_OVERLAY) && + (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_SHOW) && draw_overlay); const bool is_playing = ED_screen_animation_playing(wm); - if (!draw_frame_overlay || sseq->overlay_type != SEQ_DRAW_OVERLAY_REFERENCE) { + if (!(draw_frame_overlay && (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_REFERENCE))) { sequencer_draw_preview(C, scene, region, sseq, scene->r.cfra, 0, false, false); } - if (draw_frame_overlay && sseq->overlay_type != SEQ_DRAW_OVERLAY_CURRENT) { + if (draw_frame_overlay && sseq->overlay_frame_type != SEQ_OVERLAY_FRAME_TYPE_CURRENT) { int over_cfra; - if (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) { - over_cfra = scene->ed->over_cfra; + if (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) { + over_cfra = scene->ed->overlay_frame_abs; } else { - over_cfra = scene->r.cfra + scene->ed->over_ofs; + over_cfra = scene->r.cfra + scene->ed->overlay_frame_ofs; } - if (over_cfra != scene->r.cfra || sseq->overlay_type != SEQ_DRAW_OVERLAY_RECT) { + if ((over_cfra != scene->r.cfra) || + (sseq->overlay_frame_type != SEQ_OVERLAY_FRAME_TYPE_RECT)) { sequencer_draw_preview( C, scene, region, sseq, scene->r.cfra, over_cfra - scene->r.cfra, true, false); } diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index e363ed5ddfd..af01bb76680 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -289,9 +289,11 @@ typedef struct Editing { /** 1024 = FILE_MAX. */ char proxy_dir[1024]; - int over_ofs, over_cfra; - int over_flag, proxy_storage; - rctf over_border; + int proxy_storage; + + int overlay_frame_ofs, overlay_frame_abs; + int overlay_frame_flag; + rctf overlay_frame_rect; struct SeqCache *cache; @@ -502,9 +504,9 @@ typedef struct SequencerScopes { #define SELECT 1 -/* Editor->over_flag */ -#define SEQ_EDIT_USE_FRAME_OVERLAY 1 -#define SEQ_EDIT_OVERLAY_ABS 2 +/** #Editor.overlay_frame_flag */ +#define SEQ_EDIT_OVERLAY_FRAME_SHOW 1 +#define SEQ_EDIT_OVERLAY_FRAME_ABS 2 #define SEQ_STRIP_OFSBOTTOM 0.05f #define SEQ_STRIP_OFSTOP 0.95f diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index ff06e88ab39..a75f52a5036 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -642,7 +642,7 @@ typedef struct SpaceSeq { float zoom DNA_DEPRECATED; /** See SEQ_VIEW_* below. */ char view; - char overlay_type; + char overlay_frame_type; /** Overlay an image of the editing on below the strips. */ char draw_flag; char gizmo_flag; @@ -740,11 +740,11 @@ enum { }; /* SpaceSeq.mainb */ -typedef enum eSpaceSeq_OverlayType { - SEQ_DRAW_OVERLAY_RECT = 0, - SEQ_DRAW_OVERLAY_REFERENCE = 1, - SEQ_DRAW_OVERLAY_CURRENT = 2, -} eSpaceSeq_OverlayType; +typedef enum eSpaceSeq_OverlayFrameType { + SEQ_OVERLAY_FRAME_TYPE_RECT = 0, + SEQ_OVERLAY_FRAME_TYPE_REFERENCE = 1, + SEQ_OVERLAY_FRAME_TYPE_CURRENT = 2, +} eSpaceSeq_OverlayFrameType; /** \} */ diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 2feebbfd4f4..9b8bfb5af15 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -73,6 +73,10 @@ DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32) DNA_STRUCT_RENAME_ELEM(Camera, clipend, clip_end) DNA_STRUCT_RENAME_ELEM(Camera, clipsta, clip_start) DNA_STRUCT_RENAME_ELEM(Collection, dupli_ofs, instance_offset) +DNA_STRUCT_RENAME_ELEM(Editing, over_border, overlay_frame_rect) +DNA_STRUCT_RENAME_ELEM(Editing, over_cfra, overlay_frame_abs) +DNA_STRUCT_RENAME_ELEM(Editing, over_flag, overlay_frame_flag) +DNA_STRUCT_RENAME_ELEM(Editing, over_ofs, overlay_frame_ofs) DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, cache_frame_pause_guiding, cache_frame_pause_guide) DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, guiding_alpha, guide_alpha) DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, guiding_beta, guide_beta) @@ -92,6 +96,7 @@ DNA_STRUCT_RENAME_ELEM(Object, restrictflag, visibility_flag) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_group, instance_collection) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_ob, instance_object) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights) +DNA_STRUCT_RENAME_ELEM(SpaceSeq, overlay_type, overlay_frame_type) DNA_STRUCT_RENAME_ELEM(Text, name, filepath) DNA_STRUCT_RENAME_ELEM(ThemeSpace, scrubbing_background, time_scrub_background) DNA_STRUCT_RENAME_ELEM(ThemeSpace, show_back_grad, background_type) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index c9bcd5e0a0d..cc302c4dd89 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1121,13 +1121,13 @@ static void rna_SequenceEditor_overlay_lock_set(PointerRNA *ptr, bool value) } /* convert from abs to relative and back */ - if ((ed->over_flag & SEQ_EDIT_OVERLAY_ABS) == 0 && value) { - ed->over_cfra = scene->r.cfra + ed->over_ofs; - ed->over_flag |= SEQ_EDIT_OVERLAY_ABS; + if ((ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) == 0 && value) { + ed->overlay_frame_abs = scene->r.cfra + ed->overlay_frame_ofs; + ed->overlay_frame_flag |= SEQ_EDIT_OVERLAY_FRAME_ABS; } - else if ((ed->over_flag & SEQ_EDIT_OVERLAY_ABS) && !value) { - ed->over_ofs = ed->over_cfra - scene->r.cfra; - ed->over_flag &= ~SEQ_EDIT_OVERLAY_ABS; + else if ((ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) && !value) { + ed->overlay_frame_ofs = ed->overlay_frame_abs - scene->r.cfra; + ed->overlay_frame_flag &= ~SEQ_EDIT_OVERLAY_FRAME_ABS; } } @@ -1140,11 +1140,11 @@ static int rna_SequenceEditor_overlay_frame_get(PointerRNA *ptr) return scene->r.cfra; } - if (ed->over_flag & SEQ_EDIT_OVERLAY_ABS) { - return ed->over_cfra - scene->r.cfra; + if (ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) { + return ed->overlay_frame_abs - scene->r.cfra; } else { - return ed->over_ofs; + return ed->overlay_frame_ofs; } } @@ -1157,11 +1157,11 @@ static void rna_SequenceEditor_overlay_frame_set(PointerRNA *ptr, int value) return; } - if (ed->over_flag & SEQ_EDIT_OVERLAY_ABS) { - ed->over_cfra = (scene->r.cfra + value); + if (ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) { + ed->overlay_frame_abs = (scene->r.cfra + value); } else { - ed->over_ofs = value; + ed->overlay_frame_ofs = value; } } @@ -2113,14 +2113,14 @@ static void rna_def_editor(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Active Strip", "Sequencer's active strip"); - prop = RNA_def_property(srna, "show_overlay", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "over_flag", SEQ_EDIT_USE_FRAME_OVERLAY); + prop = RNA_def_property(srna, "show_overlay_frame", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay_frame_flag", SEQ_EDIT_OVERLAY_FRAME_SHOW); RNA_def_property_ui_text( prop, "Show Overlay", "Partial overlay on top of the sequencer with a frame offset"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "use_overlay_lock", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "over_flag", SEQ_EDIT_OVERLAY_ABS); + prop = RNA_def_property(srna, "use_overlay_frame_lock", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay_frame_flag", SEQ_EDIT_OVERLAY_FRAME_ABS); RNA_def_property_ui_text(prop, "Overlay Lock", ""); RNA_def_property_boolean_funcs(prop, NULL, "rna_SequenceEditor_overlay_lock_set"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 8f9d4addd30..3255bc30d4c 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5531,10 +5531,10 @@ static void rna_def_space_sequencer(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem overlay_type_items[] = { - {SEQ_DRAW_OVERLAY_RECT, "RECTANGLE", 0, "Rectangle", "Show rectangle area overlay"}, - {SEQ_DRAW_OVERLAY_REFERENCE, "REFERENCE", 0, "Reference", "Show reference frame only"}, - {SEQ_DRAW_OVERLAY_CURRENT, "CURRENT", 0, "Current", "Show current frame only"}, + static const EnumPropertyItem overlay_frame_type_items[] = { + {SEQ_OVERLAY_FRAME_TYPE_RECT, "RECTANGLE", 0, "Rectangle", "Show rectangle area overlay"}, + {SEQ_OVERLAY_FRAME_TYPE_REFERENCE, "REFERENCE", 0, "Reference", "Show reference frame only"}, + {SEQ_OVERLAY_FRAME_TYPE_CURRENT, "CURRENT", 0, "Current", "Show current frame only"}, {0, NULL, 0, NULL, NULL}, }; @@ -5654,9 +5654,9 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Grease Pencil", "Grease Pencil data for this Preview region"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "overlay_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "overlay_type"); - RNA_def_property_enum_items(prop, overlay_type_items); + prop = RNA_def_property(srna, "overlay_frame_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "overlay_frame_type"); + RNA_def_property_enum_items(prop, overlay_frame_type_items); RNA_def_property_ui_text(prop, "Overlay Type", "Overlay display method"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); @@ -5692,7 +5692,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); /* Overlay settings. */ - prop = RNA_def_property(srna, "show_strip_overlay", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "show_overlays", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_OVERLAY); RNA_def_property_ui_text(prop, "Show Overlay", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); From f5edc6915094c6632580a0340a4ed95fdfd865ab Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Oct 2021 15:47:29 +1100 Subject: [PATCH 0866/1500] Fix frame overlay not refreshing the sequencer preview Regression in 46aa70cb486d719139ac43e5c9ac4b0fe998e202 --- source/blender/windowmanager/intern/wm_event_system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index b86d639dc4c..f07f2637a74 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -581,7 +581,7 @@ void wm_event_do_notifiers(bContext *C) if ((note->category == NC_SPACE) && note->reference) { /* Filter out notifiers sent to other spaces. RNA sets the reference to the owning ID * though, the screen, so let notifiers through that reference the entire screen. */ - if (!ELEM(note->reference, area->spacedata.first, screen)) { + if (!ELEM(note->reference, area->spacedata.first, screen, scene)) { continue; } } From be22e36692f0989e52b4a4f286d7d3d3fe8186bd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Oct 2021 15:47:31 +1100 Subject: [PATCH 0867/1500] Fix key-shortcut path for sequencer overlay --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 1e25b33cc20..d0c20bb8932 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2873,7 +2873,7 @@ def km_sequencerpreview(params): {"properties": [("data_path", 'space_data.show_gizmo')]}), op_menu_pie("SEQUENCER_MT_pivot_pie", {"type": 'PERIOD', "value": 'PRESS'}), ("wm.context_toggle", {"type": 'Z', "value": 'PRESS', "alt": True, "shift": True}, - {"properties": [("data_path", "space_data.overlay.show_overlays")]}), + {"properties": [("data_path", "space_data.show_overlays")]}), ]) # 2D cursor. From 69d6222481b4342dc2a153e62752145aa37ea101 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 18 Oct 2021 00:32:23 -0300 Subject: [PATCH 0868/1500] Snap and Placement Gizmo Refactor Move most of the gizmo snap and placement code to `view_cursor_snap.c`. Simplify and extend the snap API. Differential Revision: https://developer.blender.org/D12868 --- .../gizmo_library/gizmo_types/snap3d_gizmo.c | 585 ++--------- .../editors/include/ED_gizmo_library.h | 41 +- .../ED_transform_snap_object_context.h | 3 +- source/blender/editors/include/ED_view3d.h | 68 ++ .../editors/space_view3d/CMakeLists.txt | 1 + .../editors/space_view3d/space_view3d.c | 4 + .../editors/space_view3d/view3d_cursor_snap.c | 954 ++++++++++++++++++ .../editors/space_view3d/view3d_edit.c | 3 +- .../editors/space_view3d/view3d_gizmo_ruler.c | 20 +- .../editors/space_view3d/view3d_placement.c | 878 +++------------- .../editors/transform/transform_snap.c | 10 +- .../editors/transform/transform_snap_object.c | 47 +- 12 files changed, 1300 insertions(+), 1314 deletions(-) create mode 100644 source/blender/editors/space_view3d/view3d_cursor_snap.c diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index 322ae87befa..fccb65bad68 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -27,420 +27,122 @@ * \brief Snap gizmo which exposes the location, normal and index in the props. */ +#include "MEM_guardedalloc.h" + #include "BLI_listbase.h" #include "BLI_math.h" -#include "DNA_scene_types.h" - #include "BKE_context.h" #include "BKE_global.h" #include "BKE_main.h" -#include "GPU_immediate.h" -#include "GPU_state.h" - #include "ED_gizmo_library.h" #include "ED_screen.h" #include "ED_transform_snap_object_context.h" #include "ED_view3d.h" -#include "UI_resources.h" /* icons */ +#include "UI_resources.h" #include "RNA_access.h" #include "RNA_define.h" -#include "DEG_depsgraph_query.h" - #include "WM_api.h" -#include "WM_types.h" /* own includes */ -#include "../gizmo_geometry.h" #include "../gizmo_library_intern.h" typedef struct SnapGizmo3D { wmGizmo gizmo; - - /* We could have other snap contexts, for now only support 3D view. */ - SnapObjectContext *snap_context_v3d; - - /* Copy of the parameters of the last event state in order to detect updates. */ - struct { - int x; - int y; -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - short shift, ctrl, alt, oskey; -#endif - } last_eventstate; - -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - wmKeyMap *keymap; - int snap_on; - bool invert_snap; -#endif - - /* Setup. */ - eSnapGizmo flag; - float *prevpoint; - float prevpoint_stack[3]; - short snap_elem_force; - - /* Return values. */ - short snap_elem; - float loc[3]; - float nor[3]; - int elem_index[3]; - - /** Enabled when snap is activated, even if it didn't find anything. */ - bool is_enabled; + V3DSnapCursorData *cursor_handle; } SnapGizmo3D; -/* Checks if the current event is different from the one captured in the last update. */ -static bool eventstate_has_changed(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) +static void snap_gizmo_snap_elements_update(SnapGizmo3D *snap_gizmo) { - if (wm && wm->winactive) { - const wmEvent *event = wm->winactive->eventstate; - if ((event->x != snap_gizmo->last_eventstate.x) || - (event->y != snap_gizmo->last_eventstate.y)) { - return true; - } -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) { - if ((event->ctrl != snap_gizmo->last_eventstate.ctrl) || - (event->shift != snap_gizmo->last_eventstate.shift) || - (event->alt != snap_gizmo->last_eventstate.alt) || - (event->oskey != snap_gizmo->last_eventstate.oskey)) { - return true; - } - } -#endif - } - return false; -} + V3DSnapCursorData *snap_data = snap_gizmo->cursor_handle; + wmGizmoProperty *gz_prop_snap; + gz_prop_snap = WM_gizmo_target_property_find(&snap_gizmo->gizmo, "snap_elements"); -/* Copies the current eventstate. */ -static void eventstate_save_xy(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) -{ - if (wm && wm->winactive) { - const wmEvent *event = wm->winactive->eventstate; - snap_gizmo->last_eventstate.x = event->x; - snap_gizmo->last_eventstate.y = event->y; - } -} - -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK -static bool invert_snap(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) -{ - if (!wm || !wm->winactive) { - return false; + if (gz_prop_snap->prop) { + snap_data->snap_elem_force |= RNA_property_enum_get(&gz_prop_snap->ptr, gz_prop_snap->prop); } - const wmEvent *event = wm->winactive->eventstate; - if ((event->ctrl == snap_gizmo->last_eventstate.ctrl) && - (event->shift == snap_gizmo->last_eventstate.shift) && - (event->alt == snap_gizmo->last_eventstate.alt) && - (event->oskey == snap_gizmo->last_eventstate.oskey)) { - /* Nothing has changed. */ - return snap_gizmo->invert_snap; - } - - /* Save new eventstate. */ - snap_gizmo->last_eventstate.ctrl = event->ctrl; - snap_gizmo->last_eventstate.shift = event->shift; - snap_gizmo->last_eventstate.alt = event->alt; - snap_gizmo->last_eventstate.oskey = event->oskey; - - const int snap_on = snap_gizmo->snap_on; - - wmKeyMap *keymap = WM_keymap_active(wm, snap_gizmo->keymap); - for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) { - if (kmi->flag & KMI_INACTIVE) { - continue; - } - - if (kmi->propvalue == snap_on) { - if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && event->ctrl) || - (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && event->shift) || - (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && event->alt) || - ((kmi->type == EVT_OSKEY) && event->oskey)) { - return true; - } - } - } - return false; -} -#endif - -static short snap_gizmo_snap_elements(SnapGizmo3D *snap_gizmo) -{ - int snap_elements = snap_gizmo->snap_elem_force; - - wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(&snap_gizmo->gizmo, "snap_elements"); - if (gz_prop->prop) { - snap_elements |= RNA_property_enum_get(&gz_prop->ptr, gz_prop->prop); - } - snap_elements &= (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR); - return (ushort)snap_elements; + UI_GetThemeColor3ubv(TH_TRANSFORM, snap_data->color_line); + snap_data->color_line[3] = 128; + rgba_float_to_uchar(snap_data->color_point, snap_gizmo->gizmo.color); } /* -------------------------------------------------------------------- */ /** \name ED_gizmo_library specific API * \{ */ -void ED_gizmotypes_snap_3d_draw_util(RegionView3D *rv3d, - const float loc_prev[3], - const float loc_curr[3], - const float normal[3], - const uchar color_line[4], - const uchar color_point[4], - const short snap_elem_type) +SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(Scene *scene, wmGizmo *UNUSED(gz)) { - if (!loc_prev && !loc_curr) { - return; - } - - float view_inv[4][4]; - copy_m4_m4(view_inv, rv3d->viewinv); - - /* The size of the circle is larger than the vertex size. - * This prevents a drawing overlaps the other. */ - float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - - if (loc_curr) { - immUniformColor4ubv(color_point); - imm_drawcircball(loc_curr, ED_view3d_pixel_size(rv3d, loc_curr) * radius, view_inv, pos); - - /* draw normal if needed */ - if (normal) { - immBegin(GPU_PRIM_LINES, 2); - immVertex3fv(pos, loc_curr); - immVertex3f(pos, loc_curr[0] + normal[0], loc_curr[1] + normal[1], loc_curr[2] + normal[2]); - immEnd(); - } - } - - if (loc_prev) { - /* Draw an "X" indicating where the previous snap point is. - * This is useful for indicating perpendicular snap. */ - - /* v1, v2, v3 and v4 indicate the coordinates of the ends of the "X". */ - float vx[3], vy[3], v1[3], v2[3], v3[3], v4[4]; - - /* Multiply by 0.75f so that the final size of the "X" is close to that of - * the circle. - * (A closer value is 0.7071f, but we don't need to be exact here). */ - float x_size = 0.75f * radius * ED_view3d_pixel_size(rv3d, loc_prev); - - mul_v3_v3fl(vx, view_inv[0], x_size); - mul_v3_v3fl(vy, view_inv[1], x_size); - - add_v3_v3v3(v1, vx, vy); - sub_v3_v3v3(v2, vx, vy); - negate_v3_v3(v3, v1); - negate_v3_v3(v4, v2); - - add_v3_v3(v1, loc_prev); - add_v3_v3(v2, loc_prev); - add_v3_v3(v3, loc_prev); - add_v3_v3(v4, loc_prev); - - immUniformColor4ubv(color_line); - immBegin(GPU_PRIM_LINES, 4); - immVertex3fv(pos, v3); - immVertex3fv(pos, v1); - immVertex3fv(pos, v4); - immVertex3fv(pos, v2); - immEnd(); - - if (loc_curr && (snap_elem_type & SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { - /* Dashed line. */ - immUnbindProgram(); - - immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); - float viewport_size[4]; - GPU_viewport_size_get_f(viewport_size); - immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); - immUniform1f("dash_width", 6.0f * U.pixelsize); - immUniform1f("dash_factor", 1.0f / 4.0f); - immUniformColor4ubv(color_line); - - immBegin(GPU_PRIM_LINES, 2); - immVertex3fv(pos, loc_prev); - immVertex3fv(pos, loc_curr); - immEnd(); - } - } - - immUnbindProgram(); + ED_view3d_cursor_snap_activate_point(); + return ED_view3d_cursor_snap_context_ensure(scene); } -SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(Scene *scene, wmGizmo *gz) +void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, int flag) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - if (snap_gizmo->snap_context_v3d == NULL) { - snap_gizmo->snap_context_v3d = ED_transform_snap_object_context_create(scene, 0); - } - return snap_gizmo->snap_context_v3d; + snap_gizmo->cursor_handle->flag |= flag; } -void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag) +void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, int flag) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - snap_gizmo->flag |= flag; + snap_gizmo->cursor_handle->flag &= ~flag; } -void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, eSnapGizmo flag) +bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, int flag) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - snap_gizmo->flag &= ~flag; -} - -bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, eSnapGizmo flag) -{ - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - return (snap_gizmo->flag & flag) != 0; + return (snap_gizmo->cursor_handle->flag & flag) != 0; } bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz) { -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - return snap_gizmo->invert_snap; -#else - return false; -#endif + return snap_gizmo->cursor_handle->is_snap_invert; } bool ED_gizmotypes_snap_3d_is_enabled(const wmGizmo *gz) { const SnapGizmo3D *snap_gizmo = (const SnapGizmo3D *)gz; - return snap_gizmo->is_enabled; + return snap_gizmo->cursor_handle->is_enabled; } -short ED_gizmotypes_snap_3d_update(wmGizmo *gz, - struct Depsgraph *depsgraph, - const ARegion *region, - const View3D *v3d, - const wmWindowManager *wm, - const float mval_fl[2]) +void ED_gizmotypes_snap_3d_data_get(const struct bContext *C, + wmGizmo *gz, + float r_loc[3], + float r_nor[3], + int r_elem_index[3], + int *r_snap_elem) { - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - snap_gizmo->is_enabled = false; + V3DSnapCursorData *snap_data = ((SnapGizmo3D *)gz)->cursor_handle; - Scene *scene = DEG_get_input_scene(depsgraph); - -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) { - snap_gizmo->invert_snap = invert_snap(snap_gizmo, wm); - - const ToolSettings *ts = scene->toolsettings; - if (snap_gizmo->invert_snap != !(ts->snap_flag & SCE_SNAP)) { - snap_gizmo->snap_elem = 0; - return 0; + if (C) { + /* Snap values are updated too late at the cursor. Be sure to update ahead of time. */ + wmWindowManager *wm = CTX_wm_manager(C); + const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; + if (event) { + ARegion *region = CTX_wm_region(C); + int x = event->x - region->winrct.xmin; + int y = event->y - region->winrct.ymin; + ED_view3d_cursor_snap_update(C, x, y, snap_data); } } -#endif - eventstate_save_xy(snap_gizmo, wm); - snap_gizmo->is_enabled = true; - - float co[3], no[3]; - short snap_elem = 0; - int snap_elem_index[3] = {-1, -1, -1}; - int index = -1; - - ushort snap_elements = snap_gizmo_snap_elements(snap_gizmo); - - if (snap_elements) { - float prev_co[3] = {0.0f}; - if (snap_gizmo->prevpoint) { - copy_v3_v3(prev_co, snap_gizmo->prevpoint); - } - else { - snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR; - } - - eSnapSelect snap_select = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_ONLY_ACTIVE) ? - SNAP_ONLY_ACTIVE : - SNAP_ALL; - - eSnapEditType edit_mode_type = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL) ? - SNAP_GEOM_FINAL : - (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE) ? - SNAP_GEOM_CAGE : - SNAP_GEOM_EDIT; - - bool use_occlusion_test = (snap_gizmo->flag & ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE) ? false : - true; - - float dist_px = 12.0f * U.pixelsize; - - ED_gizmotypes_snap_3d_context_ensure(scene, gz); - snap_elem = ED_transform_snap_object_project_view3d_ex( - snap_gizmo->snap_context_v3d, - depsgraph, - region, - v3d, - snap_elements, - &(const struct SnapObjectParams){ - .snap_select = snap_select, - .edit_mode_type = edit_mode_type, - .use_occlusion_test = use_occlusion_test, - }, - mval_fl, - prev_co, - &dist_px, - co, - no, - &index, - NULL, - NULL); - } - - if (snap_elem == 0) { - RegionView3D *rv3d = region->regiondata; - ED_view3d_win_to_3d(v3d, region, rv3d->ofs, mval_fl, co); - zero_v3(no); - } - else if (snap_elem == SCE_SNAP_MODE_VERTEX) { - snap_elem_index[0] = index; - } - else if (snap_elem & - (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { - snap_elem_index[1] = index; - } - else if (snap_elem == SCE_SNAP_MODE_FACE) { - snap_elem_index[2] = index; - } - - snap_gizmo->snap_elem = snap_elem; - copy_v3_v3(snap_gizmo->loc, co); - copy_v3_v3(snap_gizmo->nor, no); - copy_v3_v3_int(snap_gizmo->elem_index, snap_elem_index); - - return snap_elem; -} - -void ED_gizmotypes_snap_3d_data_get( - wmGizmo *gz, float r_loc[3], float r_nor[3], int r_elem_index[3], int *r_snap_elem) -{ - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - BLI_assert(snap_gizmo->is_enabled); if (r_loc) { - copy_v3_v3(r_loc, snap_gizmo->loc); + copy_v3_v3(r_loc, snap_data->loc); } if (r_nor) { - copy_v3_v3(r_nor, snap_gizmo->nor); + copy_v3_v3(r_nor, snap_data->nor); } if (r_elem_index) { - copy_v3_v3_int(r_elem_index, snap_gizmo->elem_index); + copy_v3_v3_int(r_elem_index, snap_data->elem_index); } if (r_snap_elem) { - *r_snap_elem = snap_gizmo->snap_elem; + *r_snap_elem = snap_data->snap_elem; } } @@ -450,115 +152,80 @@ void ED_gizmotypes_snap_3d_data_get( /** \name RNA callbacks * \{ */ -/* Based on 'rna_GizmoProperties_find_operator'. */ -static struct SnapGizmo3D *gizmo_snap_rna_find_operator(PointerRNA *ptr) -{ - IDProperty *properties = ptr->data; - for (bScreen *screen = G_MAIN->screens.first; screen; screen = screen->id.next) { - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - if (area->spacetype != SPACE_VIEW3D) { - continue; - } - LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->regiontype == RGN_TYPE_WINDOW && region->gizmo_map) { - wmGizmoMap *gzmap = region->gizmo_map; - LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, WM_gizmomap_group_list(gzmap)) { - LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) { - if (gz->properties == properties) { - return (SnapGizmo3D *)gz; - } - } - } - } - } - } - } - return NULL; -} - -static int gizmo_snap_rna_snap_elements_force_get_fn(struct PointerRNA *ptr, +static int gizmo_snap_rna_snap_elements_force_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop)) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - return snap_gizmo->snap_elem_force; + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); + if (snap_data) { + return snap_data->snap_elem_force; } return 0; } -static void gizmo_snap_rna_snap_elements_force_set_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_snap_elements_force_set_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), int value) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - snap_gizmo->snap_elem_force = (short)value; + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); + if (snap_data) { + snap_data->snap_elem_force = (short)value; } } -static void gizmo_snap_rna_prevpoint_get_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_prevpoint_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), float *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - copy_v3_v3(values, snap_gizmo->prevpoint_stack); + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); + if (snap_data && snap_data->prevpoint) { + copy_v3_v3(values, snap_data->prevpoint); } } -static void gizmo_snap_rna_prevpoint_set_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_prevpoint_set_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), const float *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - if (values) { - copy_v3_v3(snap_gizmo->prevpoint_stack, values); - snap_gizmo->prevpoint = snap_gizmo->prevpoint_stack; - } - else { - snap_gizmo->prevpoint = NULL; - } - } + ED_view3d_cursor_snap_prevpoint_set(values); } -static void gizmo_snap_rna_location_get_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_location_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), float *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - copy_v3_v3(values, snap_gizmo->loc); + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); + if (snap_data) { + copy_v3_v3(values, snap_data->loc); } } -static void gizmo_snap_rna_location_set_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_location_set_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), const float *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - copy_v3_v3(snap_gizmo->loc, values); + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); + if (snap_data) { + copy_v3_v3(snap_data->loc, values); } } -static void gizmo_snap_rna_normal_get_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_normal_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), float *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - copy_v3_v3(values, snap_gizmo->nor); + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); + if (snap_data) { + copy_v3_v3(values, snap_data->nor); } } -static void gizmo_snap_rna_snap_elem_index_get_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_snap_elem_index_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), int *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - copy_v3_v3_int(values, snap_gizmo->elem_index); + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); + if (snap_data) { + copy_v3_v3_int(values, snap_data->elem_index); } } @@ -571,91 +238,44 @@ static void gizmo_snap_rna_snap_elem_index_get_fn(struct PointerRNA *ptr, static void snap_gizmo_setup(wmGizmo *gz) { gz->flag |= WM_GIZMO_NO_TOOLTIP; - -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - wmKeyConfig *keyconf = gz->parent_gzgroup->type->keyconf; - if (!keyconf) { - /* It can happen when gizmo-group-type is not linked at startup. */ - keyconf = ((wmWindowManager *)G.main->wm.first)->defaultconf; - } - snap_gizmo->keymap = WM_modalkeymap_find(keyconf, "Generic Gizmo Tweak Modal Map"); - RNA_enum_value_from_id(snap_gizmo->keymap->modal_items, "SNAP_ON", &snap_gizmo->snap_on); -#endif + ED_view3d_cursor_snap_activate_point(); + snap_gizmo->cursor_handle = ED_view3d_cursor_snap_data_get(); } -static void snap_gizmo_draw(const bContext *C, wmGizmo *gz) +static void snap_gizmo_draw(const bContext *UNUSED(C), wmGizmo *UNUSED(gz)) { - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - if (snap_gizmo->snap_elem == 0) { - return; - } - - wmWindowManager *wm = CTX_wm_manager(C); - if (eventstate_has_changed(snap_gizmo, wm)) { - /* The eventstate has changed but the snap has not been updated. - * This means that the current position is no longer valid. */ - snap_gizmo->snap_elem = 0; - return; - } - - RegionView3D *rv3d = CTX_wm_region_data(C); - if (rv3d->rflag & RV3D_NAVIGATING) { - /* Don't draw the gizmo while navigating. It can be distracting. */ - snap_gizmo->snap_elem = 0; - return; - } - - uchar color_line[4], color_point[4]; - UI_GetThemeColor3ubv(TH_TRANSFORM, color_line); - color_line[3] = 128; - - rgba_float_to_uchar(color_point, gz->color); - - GPU_line_smooth(false); - - GPU_line_width(1.0f); - - const float *prev_point = (snap_gizmo_snap_elements(snap_gizmo) & - SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? - snap_gizmo->prevpoint : - NULL; - - ED_gizmotypes_snap_3d_draw_util( - rv3d, prev_point, snap_gizmo->loc, NULL, color_line, color_point, snap_gizmo->snap_elem); + /* All drawing is handled at the paint cursor. */ } static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2]) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - wmWindowManager *wm = CTX_wm_manager(C); - ARegion *region = CTX_wm_region(C); + V3DSnapCursorData *snap_data = snap_gizmo->cursor_handle; - /* FIXME: this hack is to ignore drag events, otherwise drag events - * cause momentary snap gizmo re-positioning at the drag-start location, see: T87511. */ - if (wm && wm->winactive) { - const wmEvent *event = wm->winactive->eventstate; - int mval_compare[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; - if (!equals_v2v2_int(mval_compare, mval)) { - return snap_gizmo->snap_elem ? 0 : -1; + /* Snap Elements can change while the gizmo is active. Need to be updated somewhere. */ + snap_gizmo_snap_elements_update(snap_gizmo); + + /* Snap values are updated too late at the cursor. Be sure to update ahead of time. */ + int x, y; + { + wmWindowManager *wm = CTX_wm_manager(C); + const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; + if (event) { + ARegion *region = CTX_wm_region(C); + x = event->x - region->winrct.xmin; + y = event->y - region->winrct.ymin; + } + else { + x = mval[0]; + y = mval[1]; } } + ED_view3d_cursor_snap_update(C, x, y, snap_data); - if (!eventstate_has_changed(snap_gizmo, wm)) { - /* Performance, do not update. */ - return snap_gizmo->snap_elem ? 0 : -1; - } - - View3D *v3d = CTX_wm_view3d(C); - const float mval_fl[2] = {UNPACK2(mval)}; - short snap_elem = ED_gizmotypes_snap_3d_update( - gz, CTX_data_ensure_evaluated_depsgraph(C), region, v3d, wm, mval_fl); - - if (snap_elem) { - ED_region_tag_redraw_editor_overlays(region); + if (snap_data && snap_data->snap_elem) { return 0; } - return -1; } @@ -677,9 +297,9 @@ static int snap_gizmo_invoke(bContext *UNUSED(C), static void snap_gizmo_free(wmGizmo *gz) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - if (snap_gizmo->snap_context_v3d) { - ED_transform_snap_object_context_destroy(snap_gizmo->snap_context_v3d); - snap_gizmo->snap_context_v3d = NULL; + V3DSnapCursorData *snap_data = snap_gizmo->cursor_handle; + if (snap_data) { + ED_view3d_cursor_snap_deactivate_point(); } } @@ -719,7 +339,6 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE, "Snap Elements", ""); - RNA_def_property_enum_funcs_runtime(prop, gizmo_snap_rna_snap_elements_force_get_fn, gizmo_snap_rna_snap_elements_force_set_fn, @@ -735,7 +354,6 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) "Point that defines the location of the perpendicular snap", FLT_MIN, FLT_MAX); - RNA_def_property_float_array_funcs_runtime( prop, gizmo_snap_rna_prevpoint_get_fn, gizmo_snap_rna_prevpoint_set_fn, NULL); @@ -750,7 +368,6 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) "Snap Point Location", FLT_MIN, FLT_MAX); - RNA_def_property_float_array_funcs_runtime( prop, gizmo_snap_rna_location_get_fn, gizmo_snap_rna_location_set_fn, NULL); @@ -764,7 +381,6 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) "Snap Point Normal", FLT_MIN, FLT_MAX); - RNA_def_property_float_array_funcs_runtime(prop, gizmo_snap_rna_normal_get_fn, NULL, NULL); prop = RNA_def_int_vector(gzt->srna, @@ -777,7 +393,6 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) "Array index of face, edge and vert snapped", INT_MIN, INT_MAX); - RNA_def_property_int_array_funcs_runtime( prop, gizmo_snap_rna_snap_elem_index_get_fn, NULL, NULL); diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h index 9bef5a17d12..4d922162ee9 100644 --- a/source/blender/editors/include/ED_gizmo_library.h +++ b/source/blender/editors/include/ED_gizmo_library.h @@ -41,11 +41,7 @@ void ED_gizmotypes_primitive_3d(void); void ED_gizmotypes_blank_3d(void); void ED_gizmotypes_snap_3d(void); -struct ARegion; -struct Depsgraph; struct Object; -struct SnapObjectContext; -struct View3D; struct bContext; struct wmGizmo; struct wmWindowManager; @@ -248,41 +244,22 @@ void ED_gizmotypes_dial_3d_draw_util(const float matrix_basis[4][4], struct Dial3dParams *params); /* snap3d_gizmo.c */ -#define USE_SNAP_DETECT_FROM_KEYMAP_HACK -void ED_gizmotypes_snap_3d_draw_util(struct RegionView3D *rv3d, - const float loc_prev[3], - const float loc_curr[3], - const float normal[3], - const uchar color_line[4], - const uchar color_point[4], - const short snap_elem_type); struct SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(struct Scene *scene, struct wmGizmo *gz); -typedef enum { - ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE = 1 << 0, - ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE = 1 << 1, - ED_SNAPGIZMO_OCCLUSION_ALWAYS_FALSE = 1 << 2, /* TODO. */ - ED_SNAPGIZMO_SNAP_ONLY_ACTIVE = 1 << 3, - ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL = 1 << 4, - ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE = 1 << 5, -} eSnapGizmo; - -void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag); -void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, eSnapGizmo flag); -bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, eSnapGizmo flag); +void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, int flag); +void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, int flag); +bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, int flag); bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz); bool ED_gizmotypes_snap_3d_is_enabled(const struct wmGizmo *gz); -short ED_gizmotypes_snap_3d_update(struct wmGizmo *gz, - struct Depsgraph *depsgraph, - const struct ARegion *region, - const struct View3D *v3d, - const struct wmWindowManager *wm, - const float mval_fl[2]); -void ED_gizmotypes_snap_3d_data_get( - struct wmGizmo *gz, float r_loc[3], float r_nor[3], int r_elem_index[3], int *r_snap_elem); +void ED_gizmotypes_snap_3d_data_get(const struct bContext *C, + struct wmGizmo *gz, + float r_loc[3], + float r_nor[3], + int r_elem_index[3], + int *r_snap_elem); #ifdef __cplusplus } diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index 7311303d7cd..7002db163b6 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -139,7 +139,8 @@ short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, float r_no[3], int *r_index, struct Object **r_ob, - float r_obmat[4][4]); + float r_obmat[4][4], + float r_face_nor[3]); bool ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, struct Depsgraph *depsgraph, const ARegion *region, diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index cf8dcbd7995..593d1be9444 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -52,6 +52,7 @@ struct RegionView3D; struct RenderEngineType; struct Scene; struct ScrArea; +struct SnapObjectContext; struct View3D; struct ViewContext; struct ViewLayer; @@ -228,6 +229,73 @@ typedef enum { (V3D_PROJ_TEST_CLIP_CONTENT | V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_FAR | \ V3D_PROJ_TEST_CLIP_WIN) +/* view3d_cursor_snap.c */ +#define USE_SNAP_DETECT_FROM_KEYMAP_HACK +typedef enum { + V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE = 1 << 0, + V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE = 1 << 1, + V3D_SNAPCURSOR_OCCLUSION_ALWAYS_FALSE = 1 << 2, /* TODO. */ + V3D_SNAPCURSOR_SNAP_ONLY_ACTIVE = 1 << 3, + V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL = 1 << 4, + V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE = 1 << 5, +} eV3DSnapCursor; + +typedef enum { + V3D_PLACE_DEPTH_SURFACE = 0, + V3D_PLACE_DEPTH_CURSOR_PLANE = 1, + V3D_PLACE_DEPTH_CURSOR_VIEW = 2, +} eV3DPlaceDepth; + +typedef enum { + V3D_PLACE_ORIENT_SURFACE = 0, + V3D_PLACE_ORIENT_DEFAULT = 1, +} eV3DPlaceOrient; + +typedef struct V3DSnapCursorData { + /* Setup. */ + eV3DSnapCursor flag; + eV3DPlaceDepth plane_depth; + eV3DPlaceOrient plane_orient; + uchar color_line[4]; + uchar color_point[4]; + float *prevpoint; + short snap_elem_force; /* If zero, use scene settings. */ + short plane_axis; + bool use_plane_axis_auto; + + /* Return values. */ + short snap_elem; + float loc[3]; + float nor[3]; + float face_nor[3]; + float obmat[4][4]; + int elem_index[3]; + float plane_omat[3][3]; + bool is_snap_invert; + + /** Enabled when snap is activated, even if it didn't find anything. */ + bool is_enabled; +} V3DSnapCursorData; + +V3DSnapCursorData *ED_view3d_cursor_snap_data_get(void); +void ED_view3d_cursor_snap_activate_point(void); +void ED_view3d_cursor_snap_activate_plane(void); +void ED_view3d_cursor_snap_deactivate_point(void); +void ED_view3d_cursor_snap_deactivate_plane(void); +void ED_view3d_cursor_snap_update(const struct bContext *C, + const int x, + const int y, + V3DSnapCursorData *snap_data); +void ED_view3d_cursor_snap_prevpoint_set(const float prev_point[3]); +struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *scene); +void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d, + const float loc_prev[3], + const float loc_curr[3], + const float normal[3], + const uchar color_line[4], + const uchar color_point[4], + const short snap_elem_type); + /* view3d_iterators.c */ /* foreach iterators */ diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index fe84a3b8ae9..19f869ed50b 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -44,6 +44,7 @@ set(SRC space_view3d.c view3d_buttons.c view3d_camera_control.c + view3d_cursor_snap.c view3d_draw.c view3d_edit.c view3d_gizmo_armature.c diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 787cf529483..83aa2d93fbb 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -355,6 +355,10 @@ static void view3d_exit(wmWindowManager *UNUSED(wm), ScrArea *area) BLI_assert(area->spacetype == SPACE_VIEW3D); View3D *v3d = area->spacedata.first; MEM_SAFE_FREE(v3d->runtime.local_stats); + + /* Be sure to release the #V3DSnapCursorData from the cursor, or it will get lost. */ + ED_view3d_cursor_snap_deactivate_point(); + ED_view3d_cursor_snap_deactivate_plane(); } static SpaceLink *view3d_duplicate(SpaceLink *sl) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c new file mode 100644 index 00000000000..03aa9316a38 --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -0,0 +1,954 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup wm + * + * \brief Snap cursor. + */ + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_listbase.h" +#include "BLI_rect.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_scene.h" + +#include "GPU_immediate.h" +#include "GPU_matrix.h" + +#include "ED_screen.h" +#include "ED_transform.h" +#include "ED_transform_snap_object_context.h" +#include "ED_view3d.h" + +#include "UI_resources.h" + +#include "RNA_access.h" + +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" +#include "wm.h" + +typedef struct SnapCursorDataIntern { + /* Keep as first member. */ + struct V3DSnapCursorData snap_data; + + struct SnapObjectContext *snap_context_v3d; + float prevpoint_stack[3]; + short snap_elem_hidden; + + /* Copy of the parameters of the last event state in order to detect updates. */ + struct { + int x; + int y; +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + short shift, ctrl, alt, oskey; +#endif + } last_eventstate; + +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + struct wmKeyMap *keymap; + int snap_on; +#endif + + struct wmPaintCursor *handle; + + bool draw_point; + bool draw_plane; +} SnapCursorDataIntern; + +/** + * Calculate a 3x3 orientation matrix from the surface under the cursor. + */ +static void cursor_poject_surface_normal(const float normal[3], + const float obmat[4][4], + float r_mat[3][3]) +{ + float mat[3][3]; + copy_m3_m4(mat, obmat); + normalize_m3(mat); + + float dot_best = fabsf(dot_v3v3(mat[0], normal)); + int i_best = 0; + for (int i = 1; i < 3; i++) { + float dot_test = fabsf(dot_v3v3(mat[i], normal)); + if (dot_test > dot_best) { + i_best = i; + dot_best = dot_test; + } + } + if (dot_v3v3(mat[i_best], normal) < 0.0f) { + negate_v3(mat[(i_best + 1) % 3]); + negate_v3(mat[(i_best + 2) % 3]); + } + copy_v3_v3(mat[i_best], normal); + orthogonalize_m3(mat, i_best); + normalize_m3(mat); + + copy_v3_v3(r_mat[0], mat[(i_best + 1) % 3]); + copy_v3_v3(r_mat[1], mat[(i_best + 2) % 3]); + copy_v3_v3(r_mat[2], mat[i_best]); +} + +/** + * Calculate 3D view incremental (grid) snapping. + * + * \note This could be moved to a public function. + */ +static bool cursor_snap_calc_incremental( + Scene *scene, View3D *v3d, ARegion *region, const float co_relative[3], float co[3]) +{ + const float grid_size = ED_view3d_grid_view_scale(scene, v3d, region, NULL); + if (UNLIKELY(grid_size == 0.0f)) { + return false; + } + + if (scene->toolsettings->snap_flag & SCE_SNAP_ABS_GRID) { + co_relative = NULL; + } + + if (co_relative != NULL) { + sub_v3_v3(co, co_relative); + } + mul_v3_fl(co, 1.0f / grid_size); + co[0] = roundf(co[0]); + co[1] = roundf(co[1]); + co[2] = roundf(co[2]); + mul_v3_fl(co, grid_size); + if (co_relative != NULL) { + add_v3_v3(co, co_relative); + } + + return true; +} + +/** + * Re-order \a mat so \a axis_align uses its own axis which is closest to \a v. + */ +static bool mat3_align_axis_to_v3(float mat[3][3], const int axis_align, const float v[3]) +{ + float dot_best = -1.0f; + int axis_found = axis_align; + for (int i = 0; i < 3; i++) { + const float dot_test = fabsf(dot_v3v3(mat[i], v)); + if (dot_test > dot_best) { + dot_best = dot_test; + axis_found = i; + } + } + + if (axis_align != axis_found) { + float tmat[3][3]; + copy_m3_m3(tmat, mat); + const int offset = mod_i(axis_found - axis_align, 3); + for (int i = 0; i < 3; i++) { + copy_v3_v3(mat[i], tmat[(i + offset) % 3]); + } + return true; + } + return false; +} + +/* -------------------------------------------------------------------- */ +/** \name Drawings + * \{ */ + +static void cursor_plane_draw_grid(const int resolution, + const float scale, + const float scale_fade, + const float matrix[4][4], + const int plane_axis, + const float color[4]) +{ + BLI_assert(scale_fade <= scale); + const int resolution_min = resolution - 1; + float color_fade[4] = {UNPACK4(color)}; + const float *center = matrix[3]; + + GPU_blend(GPU_BLEND_ADDITIVE); + GPU_line_smooth(true); + GPU_line_width(1.0f); + + GPUVertFormat *format = immVertexFormat(); + const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + const uint col_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR); + + const size_t coords_len = resolution * resolution; + float(*coords)[3] = MEM_mallocN(sizeof(*coords) * coords_len, __func__); + + const int axis_x = (plane_axis + 0) % 3; + const int axis_y = (plane_axis + 1) % 3; + const int axis_z = (plane_axis + 2) % 3; + + int i; + const float resolution_div = (float)1.0f / (float)resolution; + i = 0; + for (int x = 0; x < resolution; x++) { + const float x_fl = (x * resolution_div) - 0.5f; + for (int y = 0; y < resolution; y++) { + const float y_fl = (y * resolution_div) - 0.5f; + coords[i][axis_x] = 0.0f; + coords[i][axis_y] = x_fl * scale; + coords[i][axis_z] = y_fl * scale; + mul_m4_v3(matrix, coords[i]); + i += 1; + } + } + BLI_assert(i == coords_len); + immBeginAtMost(GPU_PRIM_LINES, coords_len * 4); + i = 0; + for (int x = 0; x < resolution_min; x++) { + for (int y = 0; y < resolution_min; y++) { + + /* Add #resolution_div to ensure we fade-out entirely. */ +#define FADE(v) \ + max_ff(0.0f, (1.0f - square_f(((len_v3v3(v, center) / scale_fade) + resolution_div) * 2.0f))) + + const float *v0 = coords[(resolution * x) + y]; + const float *v1 = coords[(resolution * (x + 1)) + y]; + const float *v2 = coords[(resolution * x) + (y + 1)]; + + const float f0 = FADE(v0); + const float f1 = FADE(v1); + const float f2 = FADE(v2); + + if (f0 > 0.0f || f1 > 0.0f) { + color_fade[3] = color[3] * f0; + immAttr4fv(col_id, color_fade); + immVertex3fv(pos_id, v0); + color_fade[3] = color[3] * f1; + immAttr4fv(col_id, color_fade); + immVertex3fv(pos_id, v1); + } + if (f0 > 0.0f || f2 > 0.0f) { + color_fade[3] = color[3] * f0; + immAttr4fv(col_id, color_fade); + immVertex3fv(pos_id, v0); + + color_fade[3] = color[3] * f2; + immAttr4fv(col_id, color_fade); + immVertex3fv(pos_id, v2); + } + +#undef FADE + + i++; + } + } + + MEM_freeN(coords); + + immEnd(); + + immUnbindProgram(); + + GPU_line_smooth(false); + GPU_blend(GPU_BLEND_NONE); +} + +static void cursor_plane_draw(const RegionView3D *rv3d, + const int plane_axis, + const float matrix[4][4]) +{ + /* Draw */ + float pixel_size; + + if (rv3d->is_persp) { + float center[3]; + negate_v3_v3(center, rv3d->ofs); + pixel_size = ED_view3d_pixel_size(rv3d, center); + } + else { + pixel_size = ED_view3d_pixel_size(rv3d, matrix[3]); + } + + if (pixel_size > FLT_EPSILON) { + + /* Arbitrary, 1.0 is a little too strong though. */ + float color_alpha = 0.75f; + if (rv3d->is_persp) { + /* Scale down the alpha when this is drawn very small, + * since the add shader causes the small size to show too dense & bright. */ + const float relative_pixel_scale = pixel_size / ED_view3d_pixel_size(rv3d, matrix[3]); + if (relative_pixel_scale < 1.0f) { + color_alpha *= max_ff(square_f(relative_pixel_scale), 0.3f); + } + } + + { + /* Extra adjustment when it's near view-aligned as it seems overly bright. */ + float view_vector[3]; + ED_view3d_global_to_vector(rv3d, matrix[3], view_vector); + float view_dot = fabsf(dot_v3v3(matrix[plane_axis], view_vector)); + color_alpha *= max_ff(0.3f, 1.0f - square_f(square_f(1.0f - view_dot))); + } + + const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize; + + float final_scale = (scale_mod * pixel_size); + + const int lines_subdiv = 10; + int lines = lines_subdiv; + + float final_scale_fade = final_scale; + final_scale = ceil_power_of_10(final_scale); + + float fac = final_scale_fade / final_scale; + + float color[4] = {1, 1, 1, color_alpha}; + color[3] *= square_f(1.0f - fac); + if (color[3] > 0.0f) { + cursor_plane_draw_grid( + lines * lines_subdiv, final_scale, final_scale_fade, matrix, plane_axis, color); + } + + color[3] = color_alpha; + /* When the grid is large, we only need the 2x lines in the middle. */ + if (fac < 0.2f) { + lines = 1; + final_scale = final_scale_fade; + } + cursor_plane_draw_grid(lines, final_scale, final_scale_fade, matrix, plane_axis, color); + } +} + +void ED_view3d_cursor_snap_draw_util(RegionView3D *rv3d, + const float loc_prev[3], + const float loc_curr[3], + const float normal[3], + const uchar color_line[4], + const uchar color_point[4], + const short snap_elem_type) +{ + if (!loc_prev && !loc_curr) { + return; + } + + float view_inv[4][4]; + copy_m4_m4(view_inv, rv3d->viewinv); + + /* The size of the circle is larger than the vertex size. + * This prevents a drawing overlaps the other. */ + float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + if (loc_curr) { + immUniformColor4ubv(color_point); + imm_drawcircball(loc_curr, ED_view3d_pixel_size(rv3d, loc_curr) * radius, view_inv, pos); + + /* draw normal if needed */ + if (normal) { + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, loc_curr); + immVertex3f(pos, loc_curr[0] + normal[0], loc_curr[1] + normal[1], loc_curr[2] + normal[2]); + immEnd(); + } + } + + if (loc_prev) { + /* Draw an "X" indicating where the previous snap point is. + * This is useful for indicating perpendicular snap. */ + + /* v1, v2, v3 and v4 indicate the coordinates of the ends of the "X". */ + float vx[3], vy[3], v1[3], v2[3], v3[3], v4[4]; + + /* Multiply by 0.75f so that the final size of the "X" is close to that of + * the circle. + * (A closer value is 0.7071f, but we don't need to be exact here). */ + float x_size = 0.75f * radius * ED_view3d_pixel_size(rv3d, loc_prev); + + mul_v3_v3fl(vx, view_inv[0], x_size); + mul_v3_v3fl(vy, view_inv[1], x_size); + + add_v3_v3v3(v1, vx, vy); + sub_v3_v3v3(v2, vx, vy); + negate_v3_v3(v3, v1); + negate_v3_v3(v4, v2); + + add_v3_v3(v1, loc_prev); + add_v3_v3(v2, loc_prev); + add_v3_v3(v3, loc_prev); + add_v3_v3(v4, loc_prev); + + immUniformColor4ubv(color_line); + immBegin(GPU_PRIM_LINES, 4); + immVertex3fv(pos, v3); + immVertex3fv(pos, v1); + immVertex3fv(pos, v4); + immVertex3fv(pos, v2); + immEnd(); + + if (loc_curr && (snap_elem_type & SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + /* Dashed line. */ + immUnbindProgram(); + + immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + immUniform1f("dash_width", 6.0f * U.pixelsize); + immUniform1f("dash_factor", 1.0f / 4.0f); + immUniformColor4ubv(color_line); + + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, loc_prev); + immVertex3fv(pos, loc_curr); + immEnd(); + } + } + + immUnbindProgram(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event State + * \{ */ + +/* Checks if the current event is different from the one captured in the last update. */ +static bool eventstate_has_changed(SnapCursorDataIntern *sdata_intern, + const wmWindowManager *wm, + const int x, + const int y) +{ + V3DSnapCursorData *snap_data = (V3DSnapCursorData *)sdata_intern; + if (wm && wm->winactive) { + const wmEvent *event = wm->winactive->eventstate; + if ((x != sdata_intern->last_eventstate.x) || (y != sdata_intern->last_eventstate.y)) { + return true; + } +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + if (!(snap_data && (snap_data->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE))) { + if ((event->ctrl != sdata_intern->last_eventstate.ctrl) || + (event->shift != sdata_intern->last_eventstate.shift) || + (event->alt != sdata_intern->last_eventstate.alt) || + (event->oskey != sdata_intern->last_eventstate.oskey)) { + return true; + } + } +#endif + } + return false; +} + +/* Copies the current eventstate. */ +static void eventstate_save_xy(SnapCursorDataIntern *cursor_snap, const int x, const int y) +{ + cursor_snap->last_eventstate.x = x; + cursor_snap->last_eventstate.y = y; +} + +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK +static bool invert_snap(SnapCursorDataIntern *sdata_intern, const wmWindowManager *wm) +{ + if (!wm || !wm->winactive) { + return false; + } + + const wmEvent *event = wm->winactive->eventstate; + if ((event->ctrl == sdata_intern->last_eventstate.ctrl) && + (event->shift == sdata_intern->last_eventstate.shift) && + (event->alt == sdata_intern->last_eventstate.alt) && + (event->oskey == sdata_intern->last_eventstate.oskey)) { + /* Nothing has changed. */ + return sdata_intern->snap_data.is_snap_invert; + } + + /* Save new eventstate. */ + sdata_intern->last_eventstate.ctrl = event->ctrl; + sdata_intern->last_eventstate.shift = event->shift; + sdata_intern->last_eventstate.alt = event->alt; + sdata_intern->last_eventstate.oskey = event->oskey; + + const int snap_on = sdata_intern->snap_on; + + wmKeyMap *keymap = WM_keymap_active(wm, sdata_intern->keymap); + for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) { + if (kmi->flag & KMI_INACTIVE) { + continue; + } + + if (kmi->propvalue == snap_on) { + if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && event->ctrl) || + (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && event->shift) || + (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && event->alt) || + ((kmi->type == EVT_OSKEY) && event->oskey)) { + return true; + } + } + } + return false; +} +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Update + * \{ */ + +static short cursor_snap_elements(V3DSnapCursorData *cursor_snap, Scene *scene) +{ + short snap_elements = cursor_snap->snap_elem_force; + if (!snap_elements) { + return scene->toolsettings->snap_mode; + } + return snap_elements; +} + +static void wm_paint_cursor_snap_context_ensure(SnapCursorDataIntern *sdata_intern, Scene *scene) +{ + if (sdata_intern->snap_context_v3d == NULL) { + sdata_intern->snap_context_v3d = ED_transform_snap_object_context_create(scene, 0); + } +} + +static void cursor_snap_update(const bContext *C, + wmWindowManager *wm, + Depsgraph *depsgraph, + Scene *scene, + ARegion *region, + View3D *v3d, + int x, + int y, + SnapCursorDataIntern *sdata_intern) +{ + wm_paint_cursor_snap_context_ensure(sdata_intern, scene); + V3DSnapCursorData *snap_data = (V3DSnapCursorData *)sdata_intern; + + float co[3], no[3], face_nor[3], obmat[4][4], omat[3][3]; + short snap_elem = 0; + int snap_elem_index[3] = {-1, -1, -1}; + int index = -1; + + const float mval_fl[2] = {x, y}; + zero_v3(no); + zero_v3(face_nor); + unit_m3(omat); + + ushort snap_elements = cursor_snap_elements(snap_data, scene); + sdata_intern->snap_elem_hidden = 0; + const bool draw_plane = sdata_intern->draw_plane; + if (draw_plane && !(snap_elements & SCE_SNAP_MODE_FACE)) { + sdata_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; + snap_elements |= SCE_SNAP_MODE_FACE; + } + + snap_data->is_enabled = true; +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + if (!(snap_data->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) { + snap_data->is_snap_invert = invert_snap(sdata_intern, wm); + + const ToolSettings *ts = scene->toolsettings; + if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) { + snap_data->is_enabled = false; + if (!draw_plane) { + snap_data->snap_elem = 0; + return; + } + snap_elements = sdata_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; + } + } +#endif + + if (snap_elements & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | + SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + float prev_co[3] = {0.0f}; + if (snap_data->prevpoint) { + copy_v3_v3(prev_co, snap_data->prevpoint); + } + else { + snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR; + } + + eSnapSelect snap_select = (snap_data->flag & V3D_SNAPCURSOR_SNAP_ONLY_ACTIVE) ? + SNAP_ONLY_ACTIVE : + SNAP_ALL; + + eSnapEditType edit_mode_type = (snap_data->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL) ? + SNAP_GEOM_FINAL : + (snap_data->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE) ? + SNAP_GEOM_CAGE : + SNAP_GEOM_EDIT; + + bool use_occlusion_test = (snap_data->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false : + true; + + float dist_px = 12.0f * U.pixelsize; + + snap_elem = ED_transform_snap_object_project_view3d_ex( + sdata_intern->snap_context_v3d, + depsgraph, + region, + v3d, + snap_elements, + &(const struct SnapObjectParams){ + .snap_select = snap_select, + .edit_mode_type = edit_mode_type, + .use_occlusion_test = use_occlusion_test, + }, + mval_fl, + prev_co, + &dist_px, + co, + no, + &index, + NULL, + obmat, + face_nor); + } + + if (is_zero_v3(face_nor)) { + face_nor[snap_data->plane_axis] = 1.0f; + } + + if (draw_plane) { + bool orient_surface = snap_elem && (snap_data->plane_orient == V3D_PLACE_ORIENT_SURFACE); + if (orient_surface) { + copy_m3_m4(omat, obmat); + } + else { + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + const short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); + const int pivot_point = scene->toolsettings->transform_pivot_point; + ED_transform_calc_orientation_from_type_ex( + scene, view_layer, v3d, region->regiondata, ob, ob, orient_index, pivot_point, omat); + + RegionView3D *rv3d = region->regiondata; + if (snap_data->use_plane_axis_auto) { + mat3_align_axis_to_v3(omat, snap_data->plane_axis, rv3d->viewinv[2]); + } + } + + /* Non-orthogonal matrices cause the preview and final result not to match. + * + * While making orthogonal doesn't always work well (especially with gimbal orientation for + * e.g.) it's a corner case, without better alternatives as objects don't support shear. */ + orthogonalize_m3(omat, snap_data->plane_axis); + + if (orient_surface) { + cursor_poject_surface_normal(snap_data->face_nor, obmat, omat); + } + } + + float *co_depth = snap_elem ? co : scene->cursor.location; + snap_elem &= ~sdata_intern->snap_elem_hidden; + if (snap_elem == 0) { + float plane[4]; + if (snap_data->plane_depth != V3D_PLACE_DEPTH_CURSOR_VIEW) { + const float *plane_normal = omat[snap_data->plane_axis]; + plane_from_point_normal_v3(plane, co_depth, plane_normal); + } + + if ((snap_data->plane_depth == V3D_PLACE_DEPTH_CURSOR_VIEW) || + !ED_view3d_win_to_3d_on_plane(region, plane, mval_fl, true, co)) { + ED_view3d_win_to_3d(v3d, region, co_depth, mval_fl, co); + } + + if (snap_data->is_enabled && (snap_elements & SCE_SNAP_MODE_INCREMENT)) { + cursor_snap_calc_incremental(scene, v3d, region, snap_data->prevpoint, co); + } + } + else if (snap_elem == SCE_SNAP_MODE_VERTEX) { + snap_elem_index[0] = index; + } + else if (snap_elem & + (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + snap_elem_index[1] = index; + } + else if (snap_elem == SCE_SNAP_MODE_FACE) { + snap_elem_index[2] = index; + } + + snap_data->snap_elem = snap_elem; + copy_v3_v3(snap_data->loc, co); + copy_v3_v3(snap_data->nor, no); + copy_v3_v3(snap_data->face_nor, face_nor); + copy_m4_m4(snap_data->obmat, obmat); + copy_v3_v3_int(snap_data->elem_index, snap_elem_index); + + copy_m3_m3(snap_data->plane_omat, omat); + + eventstate_save_xy(sdata_intern, x, y); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks + * \{ */ + +static bool cursor_snap_pool_fn(bContext *C) +{ + if (G.moving) { + return false; + } + + ARegion *region = CTX_wm_region(C); + if (region->regiontype != RGN_TYPE_WINDOW) { + return false; + } + + ScrArea *area = CTX_wm_area(C); + if (area->spacetype != SPACE_VIEW3D) { + return false; + } + + RegionView3D *rv3d = region->regiondata; + if (rv3d->rflag & RV3D_NAVIGATING) { + /* Don't draw the cursor while navigating. It can be distracting. */ + return false; + }; + + return true; +} + +static void cursor_snap_draw_fn(bContext *C, int x, int y, void *customdata) +{ + V3DSnapCursorData *snap_data = customdata; + SnapCursorDataIntern *sdata_intern = customdata; + + wmWindowManager *wm = CTX_wm_manager(C); + ARegion *region = CTX_wm_region(C); + x -= region->winrct.xmin; + y -= region->winrct.ymin; + if (eventstate_has_changed(sdata_intern, wm, x, y)) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = DEG_get_input_scene(depsgraph); + View3D *v3d = CTX_wm_view3d(C); + cursor_snap_update(C, wm, depsgraph, scene, region, v3d, x, y, sdata_intern); + } + + const bool draw_plane = sdata_intern->draw_plane; + if (!snap_data->snap_elem && !draw_plane) { + return; + } + + /* Setup viewport & matrix. */ + RegionView3D *rv3d = region->regiondata; + wmViewport(®ion->winrct); + GPU_matrix_push_projection(); + GPU_matrix_push(); + GPU_matrix_projection_set(rv3d->winmat); + GPU_matrix_set(rv3d->viewmat); + + GPU_blend(GPU_BLEND_ALPHA); + + float matrix[4][4]; + if (draw_plane) { + copy_m4_m3(matrix, snap_data->plane_omat); + copy_v3_v3(matrix[3], snap_data->loc); + + cursor_plane_draw(rv3d, snap_data->plane_axis, matrix); + } + + if (snap_data->snap_elem && sdata_intern->draw_point) { + const float *prev_point = (snap_data->snap_elem & SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? + snap_data->prevpoint : + NULL; + + GPU_line_smooth(false); + GPU_line_width(1.0f); + + ED_view3d_cursor_snap_draw_util(rv3d, + prev_point, + snap_data->loc, + NULL, + snap_data->color_line, + snap_data->color_point, + snap_data->snap_elem); + } + + GPU_blend(GPU_BLEND_NONE); + + /* Restore matrix. */ + GPU_matrix_pop(); + GPU_matrix_pop_projection(); +} + +/** \} */ + +V3DSnapCursorData *ED_view3d_cursor_snap_data_get(void) +{ + LISTBASE_FOREACH_MUTABLE (wmWindowManager *, wm, &G.main->wm) { + LISTBASE_FOREACH_MUTABLE (wmPaintCursor *, pc, &wm->paintcursors) { + if (pc->draw == cursor_snap_draw_fn) { + return (V3DSnapCursorData *)pc->customdata; + } + } + } + return NULL; +} + +static void wm_paint_cursor_snap_data_init(SnapCursorDataIntern *sdata_intern) +{ +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + struct wmKeyConfig *keyconf = ((wmWindowManager *)G.main->wm.first)->defaultconf; + + sdata_intern->keymap = WM_modalkeymap_find(keyconf, "Generic Gizmo Tweak Modal Map"); + RNA_enum_value_from_id(sdata_intern->keymap->modal_items, "SNAP_ON", &sdata_intern->snap_on); +#endif + + V3DSnapCursorData *snap_data = &sdata_intern->snap_data; + snap_data->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | + SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT); + snap_data->plane_axis = 2; + rgba_uchar_args_set(snap_data->color_point, 255, 255, 255, 255); + UI_GetThemeColor3ubv(TH_TRANSFORM, snap_data->color_line); + snap_data->color_line[3] = 128; +} + +static SnapCursorDataIntern *wm_paint_cursor_snap_ensure(void) +{ + SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); + if (!sdata_intern) { + sdata_intern = MEM_callocN(sizeof(*sdata_intern), __func__); + wm_paint_cursor_snap_data_init(sdata_intern); + + struct wmPaintCursor *pc = WM_paint_cursor_activate( + SPACE_VIEW3D, RGN_TYPE_WINDOW, cursor_snap_pool_fn, cursor_snap_draw_fn, sdata_intern); + sdata_intern->handle = pc; + } + return sdata_intern; +} + +void ED_view3d_cursor_snap_activate_point(void) +{ + SnapCursorDataIntern *sdata_intern = wm_paint_cursor_snap_ensure(); + sdata_intern->draw_point = true; +} + +void ED_view3d_cursor_snap_activate_plane(void) +{ + SnapCursorDataIntern *sdata_intern = wm_paint_cursor_snap_ensure(); + sdata_intern->draw_plane = true; +} + +static void wm_paint_cursor_snap_free(SnapCursorDataIntern *sdata_intern) +{ + WM_paint_cursor_end(sdata_intern->handle); + if (sdata_intern->snap_context_v3d) { + ED_transform_snap_object_context_destroy(sdata_intern->snap_context_v3d); + } + MEM_freeN(sdata_intern); +} + +void ED_view3d_cursor_snap_deactivate_point(void) +{ + SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); + if (!sdata_intern) { + return; + } + + sdata_intern->draw_point = false; + sdata_intern->snap_data.prevpoint = NULL; + if (sdata_intern->draw_plane) { + return; + } + + wm_paint_cursor_snap_free(sdata_intern); +} + +void ED_view3d_cursor_snap_deactivate_plane(void) +{ + SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); + if (!sdata_intern) { + return; + } + + sdata_intern->draw_plane = false; + sdata_intern->snap_data.prevpoint = NULL; + if (sdata_intern->draw_point) { + return; + } + + wm_paint_cursor_snap_free(sdata_intern); +} + +void ED_view3d_cursor_snap_update(const bContext *C, + const int x, + const int y, + V3DSnapCursorData *snap_data) +{ + SnapCursorDataIntern stack = {0}, *sdata_intern; + sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); + if (!sdata_intern) { + sdata_intern = &stack; + wm_paint_cursor_snap_data_init(sdata_intern); + sdata_intern->draw_plane = true; + } + + wmWindowManager *wm = CTX_wm_manager(C); + if (eventstate_has_changed(sdata_intern, wm, x, y)) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = DEG_get_input_scene(depsgraph); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + + cursor_snap_update(C, wm, depsgraph, scene, region, v3d, x, y, sdata_intern); + } + if ((void *)snap_data != (void *)sdata_intern) { + if ((sdata_intern == &stack) && sdata_intern->snap_context_v3d) { + ED_transform_snap_object_context_destroy(sdata_intern->snap_context_v3d); + sdata_intern->snap_context_v3d = NULL; + } + *snap_data = *(V3DSnapCursorData *)sdata_intern; + } +} + +void ED_view3d_cursor_snap_prevpoint_set(const float prev_point[3]) +{ + SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); + if (!sdata_intern) { + return; + } + if (prev_point) { + copy_v3_v3(sdata_intern->prevpoint_stack, prev_point); + sdata_intern->snap_data.prevpoint = sdata_intern->prevpoint_stack; + } + else { + sdata_intern->snap_data.prevpoint = NULL; + } +} + +struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *scene) +{ + SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); + if (!sdata_intern) { + return NULL; + } + wm_paint_cursor_snap_context_ensure(sdata_intern, scene); + return sdata_intern->snap_context_v3d; +} diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index d34fbc4562a..ba9b104a9cc 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -5122,7 +5122,8 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, ray_no, NULL, &ob_dummy, - obmat) != 0) { + obmat, + NULL) != 0) { if (use_depth) { copy_v3_v3(cursor_co, ray_co); } diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 573f5348b5e..34e3b808b50 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -332,7 +332,8 @@ static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3], cons /** * Use for mouse-move events. */ -static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, +static bool view3d_ruler_item_mousemove(const bContext *C, + struct Depsgraph *depsgraph, RulerInfo *ruler_info, RulerItem *ruler_item, const int mval[2], @@ -401,7 +402,6 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, #endif { View3D *v3d = ruler_info->area->spacedata.first; - const float mval_fl[2] = {UNPACK2(mval)}; float *prev_point = NULL; if (inter->co_index != 1) { @@ -420,11 +420,8 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, snap_gizmo->ptr, ruler_info->snap_data.prop_prevpoint, prev_point); } - ED_gizmotypes_snap_3d_update( - snap_gizmo, depsgraph, ruler_info->region, v3d, ruler_info->wm, mval_fl); - if (ED_gizmotypes_snap_3d_is_enabled(snap_gizmo)) { - ED_gizmotypes_snap_3d_data_get(snap_gizmo, co, NULL, NULL, NULL); + ED_gizmotypes_snap_3d_data_get(C, snap_gizmo, co, NULL, NULL, NULL); } #ifdef USE_AXIS_CONSTRAINTS @@ -1050,7 +1047,8 @@ static int gizmo_ruler_modal(bContext *C, if (do_cursor_update) { if (ruler_info->state == RULER_STATE_DRAG) { struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - if (view3d_ruler_item_mousemove(depsgraph, + if (view3d_ruler_item_mousemove(C, + depsgraph, ruler_info, ruler_item, event->mval, @@ -1117,7 +1115,8 @@ static int gizmo_ruler_invoke(bContext *C, wmGizmo *gz, const wmEvent *event) /* update the new location */ struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - view3d_ruler_item_mousemove(depsgraph, + view3d_ruler_item_mousemove(C, + depsgraph, ruler_info, ruler_item_pick, event->mval, @@ -1236,7 +1235,7 @@ static void WIDGETGROUP_ruler_setup(const bContext *C, wmGizmoGroup *gzgroup) (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | /* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */ SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT)); - ED_gizmotypes_snap_3d_flag_set(gizmo, ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE); + ED_gizmotypes_snap_3d_flag_set(gizmo, V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE); WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f}); wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_ruler_add", true); @@ -1321,7 +1320,8 @@ static int view3d_ruler_add_invoke(bContext *C, wmOperator *op, const wmEvent *e struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); /* snap the first point added, not essential but handy */ inter->co_index = 0; - view3d_ruler_item_mousemove(depsgraph, + view3d_ruler_item_mousemove(C, + depsgraph, ruler_info, ruler_item, event->mval, diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 6cd6d85d204..adff75b571d 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -25,17 +25,7 @@ #include "MEM_guardedalloc.h" -#include "DNA_collection_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_vfont_types.h" - -#include "BLI_math.h" -#include "BLI_utildefines.h" - #include "BKE_context.h" -#include "BKE_global.h" -#include "BKE_main.h" #include "RNA_access.h" #include "RNA_define.h" @@ -43,29 +33,23 @@ #include "WM_api.h" #include "WM_toolsystem.h" -#include "WM_types.h" -#include "ED_gizmo_library.h" #include "ED_gizmo_utils.h" #include "ED_screen.h" #include "ED_space_api.h" -#include "ED_transform.h" -#include "ED_transform_snap_object_context.h" #include "ED_view3d.h" #include "UI_resources.h" -#include "GPU_batch.h" #include "GPU_immediate.h" -#include "GPU_matrix.h" -#include "GPU_state.h" #include "view3d_intern.h" static const char *view3d_gzgt_placement_id = "VIEW3D_GGT_placement"; -static void preview_plane_cursor_setup(wmGizmoGroup *gzgroup); -static void preview_plane_cursor_visible_set(wmGizmoGroup *gzgroup, bool do_draw); +static void preview_plane_cursor_visible_set(wmGizmoGroup *gzgroup, + bool do_draw_plane, + bool do_draw_point); /** * Dot products below this will be considered view aligned. @@ -96,17 +80,6 @@ enum ePlace_Aspect { PLACE_ASPECT_FIXED = 2, }; -enum ePlace_Depth { - PLACE_DEPTH_SURFACE = 1, - PLACE_DEPTH_CURSOR_PLANE = 2, - PLACE_DEPTH_CURSOR_VIEW = 3, -}; - -enum ePlace_Orient { - PLACE_ORIENT_SURFACE = 1, - PLACE_ORIENT_DEFAULT = 2, -}; - enum ePlace_SnapTo { PLACE_SNAP_TO_GEOMETRY = 1, PLACE_SNAP_TO_DEFAULT = 2, @@ -198,9 +171,6 @@ struct InteractivePlaceData { /** When activated without a tool. */ bool wait_for_input; - /** Optional snap gizmo, needed for snapping. */ - wmGizmo *snap_gizmo; - enum ePlace_SnapTo snap_to; }; @@ -251,130 +221,6 @@ static int dot_v3_array_find_max_index(const float dirs[][3], return index_found; } -/** - * Re-order \a mat so \a axis_align uses its own axis which is closest to \a v. - */ -static bool mat3_align_axis_to_v3(float mat[3][3], const int axis_align, const float v[3]) -{ - float dot_best = -1.0f; - int axis_found = axis_align; - for (int i = 0; i < 3; i++) { - const float dot_test = fabsf(dot_v3v3(mat[i], v)); - if (dot_test > dot_best) { - dot_best = dot_test; - axis_found = i; - } - } - - if (axis_align != axis_found) { - float tmat[3][3]; - copy_m3_m3(tmat, mat); - const int offset = mod_i(axis_found - axis_align, 3); - for (int i = 0; i < 3; i++) { - copy_v3_v3(mat[i], tmat[(i + offset) % 3]); - } - return true; - } - return false; -} - -/* On-screen snap distance. */ -#define MVAL_MAX_PX_DIST 12.0f - -static bool idp_snap_point_from_gizmo_ex(wmGizmo *gz, const char *prop_id, float r_location[3]) -{ - if (gz->state & WM_GIZMO_STATE_HIGHLIGHT) { - PropertyRNA *prop_location = RNA_struct_find_property(gz->ptr, prop_id); - RNA_property_float_get_array(gz->ptr, prop_location, r_location); - return true; - } - return false; -} - -static bool idp_snap_point_from_gizmo(wmGizmo *gz, float r_location[3]) -{ - return idp_snap_point_from_gizmo_ex(gz, "location", r_location); -} - -static bool idp_snap_normal_from_gizmo(wmGizmo *gz, float r_normal[3]) -{ - return idp_snap_point_from_gizmo_ex(gz, "normal", r_normal); -} - -/** - * Calculate a 3x3 orientation matrix from the surface under the cursor. - */ -static bool idp_poject_surface_normal(SnapObjectContext *snap_context, - struct Depsgraph *depsgraph, - ARegion *region, - View3D *v3d, - const float mval_fl[2], - const float mat_fallback[3][3], - const float normal_fallback[3], - float r_mat[3][3]) -{ - bool success = false; - float normal[3] = {0.0f}; - float co_dummy[3]; - /* We could use the index to get the orientation from the face. */ - Object *ob_snap; - float obmat[4][4]; - - if (ED_transform_snap_object_project_view3d_ex(snap_context, - depsgraph, - region, - v3d, - SCE_SNAP_MODE_FACE, - &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, - .edit_mode_type = SNAP_GEOM_EDIT, - }, - mval_fl, - NULL, - NULL, - co_dummy, - normal, - NULL, - &ob_snap, - obmat)) { - /* pass */ - } - else if (normal_fallback != NULL) { - copy_m4_m3(obmat, mat_fallback); - copy_v3_v3(normal, normal_fallback); - } - - if (!is_zero_v3(normal)) { - float mat[3][3]; - copy_m3_m4(mat, obmat); - normalize_m3(mat); - - float dot_best = fabsf(dot_v3v3(mat[0], normal)); - int i_best = 0; - for (int i = 1; i < 3; i++) { - float dot_test = fabsf(dot_v3v3(mat[i], normal)); - if (dot_test > dot_best) { - i_best = i; - dot_best = dot_test; - } - } - if (dot_v3v3(mat[i_best], normal) < 0.0f) { - negate_v3(mat[(i_best + 1) % 3]); - negate_v3(mat[(i_best + 2) % 3]); - } - copy_v3_v3(mat[i_best], normal); - orthogonalize_m3(mat, i_best); - normalize_m3(mat); - - copy_v3_v3(r_mat[0], mat[(i_best + 1) % 3]); - copy_v3_v3(r_mat[1], mat[(i_best + 2) % 3]); - copy_v3_v3(r_mat[2], mat[i_best]); - success = true; - } - - return success; -} - static wmGizmoGroup *idp_gizmogroup_from_region(ARegion *region) { wmGizmoMap *gzmap = region->gizmo_map; @@ -417,20 +263,6 @@ static bool idp_snap_calc_incremental( return true; } -static void idp_snap_gizmo_update_snap_elements(Scene *scene, - enum ePlace_SnapTo snap_to, - wmGizmo *gizmo) -{ - const int snap_mode = - (snap_to == PLACE_SNAP_TO_GEOMETRY) ? - (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - /* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */ - SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT) : - scene->toolsettings->snap_mode; - - RNA_enum_set(gizmo->ptr, "snap_elements_force", snap_mode); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -861,153 +693,18 @@ static void draw_primitive_view(const struct bContext *C, ARegion *UNUSED(region * Use by both the operator and placement cursor. * \{ */ -static void view3d_interactive_add_calc_plane(bContext *C, - Scene *scene, - View3D *v3d, - ARegion *region, - const float mval_fl[2], - wmGizmo *snap_gizmo, - const enum ePlace_SnapTo snap_to, - const enum ePlace_Depth plane_depth, - const enum ePlace_Orient plane_orient, - const int plane_axis, - const bool plane_axis_auto, +static bool view3d_interactive_add_calc_plane(bContext *C, + const wmEvent *event, float r_co_src[3], float r_matrix_orient[3][3]) { - const RegionView3D *rv3d = region->regiondata; - ED_transform_calc_orientation_from_type(C, r_matrix_orient); - - /* Non-orthogonal matrices cause the preview and final result not to match. - * - * While making orthogonal doesn't always work well (especially with gimbal orientation for e.g.) - * it's a corner case, without better alternatives as objects don't support shear. */ - orthogonalize_m3(r_matrix_orient, plane_axis); - - SnapObjectContext *snap_context = NULL; - bool snap_context_free = false; - - /* Set the orientation. */ - if ((plane_orient == PLACE_ORIENT_SURFACE) || (plane_depth == PLACE_DEPTH_SURFACE)) { - snap_context = (snap_gizmo ? ED_gizmotypes_snap_3d_context_ensure(scene, snap_gizmo) : NULL); - if (snap_context == NULL) { - snap_context = ED_transform_snap_object_context_create(scene, 0); - snap_context_free = true; - } - } - - if (plane_orient == PLACE_ORIENT_SURFACE) { - bool found_surface_or_normal = false; - float matrix_orient_surface[3][3]; - - /* Use the snap normal as a fallback in case the cursor isn't over a surface - * but snapping is enabled. */ - float normal_fallback[3]; - bool use_normal_fallback = snap_gizmo ? - idp_snap_normal_from_gizmo(snap_gizmo, normal_fallback) : - false; - - if ((snap_context != NULL) && - idp_poject_surface_normal(snap_context, - CTX_data_ensure_evaluated_depsgraph(C), - region, - v3d, - mval_fl, - use_normal_fallback ? r_matrix_orient : NULL, - use_normal_fallback ? normal_fallback : NULL, - matrix_orient_surface)) { - copy_m3_m3(r_matrix_orient, matrix_orient_surface); - found_surface_or_normal = true; - } - - if (!found_surface_or_normal && plane_axis_auto) { - /* Drawing into empty space, draw onto the plane most aligned to the view direction. */ - mat3_align_axis_to_v3(r_matrix_orient, plane_axis, rv3d->viewinv[2]); - } - } - - const bool is_snap_found = snap_gizmo ? idp_snap_point_from_gizmo(snap_gizmo, r_co_src) : false; - - if (is_snap_found) { - /* pass */ - } - else { - bool use_depth_fallback = true; - if (plane_depth == PLACE_DEPTH_CURSOR_VIEW) { - /* View plane. */ - ED_view3d_win_to_3d(v3d, region, scene->cursor.location, mval_fl, r_co_src); - use_depth_fallback = false; - } - else if (plane_depth == PLACE_DEPTH_SURFACE) { - if ((snap_context != NULL) && - ED_transform_snap_object_project_view3d(snap_context, - CTX_data_ensure_evaluated_depsgraph(C), - region, - v3d, - SCE_SNAP_MODE_FACE, - &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, - .edit_mode_type = SNAP_GEOM_EDIT, - }, - mval_fl, - NULL, - NULL, - r_co_src, - NULL)) { - use_depth_fallback = false; - } - } - - /* Use as fallback to surface. */ - if (use_depth_fallback || (plane_depth == PLACE_DEPTH_CURSOR_PLANE)) { - /* Cursor plane. */ - float plane[4]; - const float *plane_normal = r_matrix_orient[plane_axis]; - - const float view_axis_dot = fabsf(dot_v3v3(rv3d->viewinv[2], r_matrix_orient[plane_axis])); - if (view_axis_dot < eps_view_align) { - /* In this case, just project onto the view plane as it's important the location - * is _always_ under the mouse cursor, even if it turns out that won't lie on - * the original 'plane' that's been calculated for us. */ - plane_normal = rv3d->viewinv[2]; - } - - plane_from_point_normal_v3(plane, scene->cursor.location, plane_normal); - - if (view3d_win_to_3d_on_plane_maybe_fallback(region, plane, mval_fl, NULL, r_co_src)) { - use_depth_fallback = false; - } - - /* Even if the calculation works, it's possible the point found is behind the view, - * or very far away (past the far clipping). - * In either case creating objects won't be useful. */ - if (rv3d->is_persp) { - float dir[3]; - sub_v3_v3v3(dir, rv3d->viewinv[3], r_co_src); - const float dot = dot_v3v3(dir, rv3d->viewinv[2]); - if (dot < v3d->clip_start || dot > v3d->clip_end) { - use_depth_fallback = true; - } - } - } - - if (use_depth_fallback) { - float co_depth[3]; - /* Fallback to view center. */ - negate_v3_v3(co_depth, rv3d->ofs); - ED_view3d_win_to_3d(v3d, region, co_depth, mval_fl, r_co_src); - } - } - - if (!is_snap_found && ((snap_gizmo != NULL) && ED_gizmotypes_snap_3d_is_enabled(snap_gizmo))) { - if (snap_to == PLACE_SNAP_TO_DEFAULT) { - idp_snap_calc_incremental(scene, v3d, region, NULL, r_co_src); - } - } - - if (snap_context_free) { - ED_transform_snap_object_context_destroy(snap_context); + V3DSnapCursorData snap_data; + ED_view3d_cursor_snap_update(C, UNPACK2(event->mval), &snap_data); + copy_v3_v3(r_co_src, snap_data.loc); + if (r_matrix_orient) { + copy_m3_m3(r_matrix_orient, snap_data.plane_omat); } + return snap_data.snap_elem != 0; } /** \} */ @@ -1022,7 +719,8 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv const int plane_axis = RNA_enum_get(op->ptr, "plane_axis"); const bool plane_axis_auto = RNA_boolean_get(op->ptr, "plane_axis_auto"); const enum ePlace_SnapTo snap_to = RNA_enum_get(op->ptr, "snap_target"); - const enum ePlace_Depth plane_depth = RNA_enum_get(op->ptr, "plane_depth"); + const eV3DPlaceDepth plane_depth = RNA_enum_get(op->ptr, "plane_depth"); + const eV3DPlaceOrient plane_orient = RNA_enum_get(op->ptr, "plane_orient"); const enum ePlace_Origin plane_origin[2] = { RNA_enum_get(op->ptr, "plane_origin_base"), RNA_enum_get(op->ptr, "plane_origin_depth"), @@ -1031,58 +729,29 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv RNA_enum_get(op->ptr, "plane_aspect_base"), RNA_enum_get(op->ptr, "plane_aspect_depth"), }; - const enum ePlace_Orient plane_orient = RNA_enum_get(op->ptr, "plane_orientation"); - - const float mval_fl[2] = {UNPACK2(event->mval)}; struct InteractivePlaceData *ipd = op->customdata; - /* Assign snap gizmo which is may be used as part of the tool. */ - { - wmGizmoGroup *gzgroup = idp_gizmogroup_from_region(ipd->region); - if (gzgroup != NULL) { - if (gzgroup->gizmos.first) { - ipd->snap_gizmo = gzgroup->gizmos.first; - } - - /* Can be NULL when gizmos are disabled. */ - if (gzgroup->customdata != NULL) { - preview_plane_cursor_visible_set(gzgroup, false); - } - } - } - - /* For tweak events the snap target may have changed since dragging, - * update the snap target at the cursor location where tweak began. - * - * NOTE: we could investigating solving this in a more generic way, - * so each operator doesn't have to account for it. */ - if (ISTWEAK(event->type)) { - if (ipd->snap_gizmo != NULL) { - ED_gizmotypes_snap_3d_update(ipd->snap_gizmo, - CTX_data_ensure_evaluated_depsgraph(C), - ipd->region, - ipd->v3d, - G_MAIN->wm.first, - mval_fl); - } - } - ipd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); - view3d_interactive_add_calc_plane(C, - ipd->scene, - ipd->v3d, - ipd->region, - mval_fl, - ipd->snap_gizmo, - snap_to, - plane_depth, - plane_orient, - plane_axis, - plane_axis_auto, - ipd->co_src, - ipd->matrix_orient); + ED_view3d_cursor_snap_activate_point(); + { + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); + snap_data->plane_depth = plane_depth; + snap_data->plane_orient = plane_orient; + snap_data->plane_axis = plane_axis; + snap_data->use_plane_axis_auto = plane_axis_auto; + + ED_view3d_cursor_snap_activate_plane(); + view3d_interactive_add_calc_plane(C, event, ipd->co_src, ipd->matrix_orient); + ED_view3d_cursor_snap_deactivate_plane(); + + ED_view3d_cursor_snap_prevpoint_set(ipd->co_src); + + ipd->use_snap = snap_data->is_enabled; + ipd->is_snap_invert = snap_data->is_snap_invert; + ipd->is_snap_found = snap_data->snap_elem != 0; + } ipd->orient_axis = plane_axis; for (int i = 0; i < 2; i++) { @@ -1161,13 +830,6 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv } } - ipd->is_snap_invert = ipd->snap_gizmo ? ED_gizmotypes_snap_3d_invert_snap_get(ipd->snap_gizmo) : - false; - { - const ToolSettings *ts = ipd->scene->toolsettings; - ipd->use_snap = (ipd->is_snap_invert == !(ts->snap_flag & SCE_SNAP)); - } - ipd->draw_handle_view = ED_region_draw_cb_activate( ipd->region->type, draw_primitive_view, ipd, REGION_DRAW_POST_VIEW); @@ -1244,19 +906,18 @@ static void view3d_interactive_add_exit(bContext *C, wmOperator *op) struct InteractivePlaceData *ipd = op->customdata; + wmGizmoGroup *gzgroup = idp_gizmogroup_from_region(ipd->region); + if (gzgroup != NULL) { + preview_plane_cursor_visible_set(gzgroup, true, true); + } + else { + ED_view3d_cursor_snap_deactivate_point(); + } + ED_region_draw_cb_exit(ipd->region->type, ipd->draw_handle_view); ED_region_tag_redraw(ipd->region); - { - wmGizmoGroup *gzgroup = idp_gizmogroup_from_region(ipd->region); - if (gzgroup != NULL) { - if (gzgroup->customdata != NULL) { - preview_plane_cursor_visible_set(gzgroup, true); - } - } - } - MEM_freeN(ipd); } @@ -1374,6 +1035,8 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve if (ipd->step_index == STEP_BASE) { if (ELEM(event->type, ipd->launch_event, LEFTMOUSE)) { if (event->val == KM_RELEASE) { + ED_view3d_cursor_snap_prevpoint_set(ipd->co_src); + /* Set secondary plane. */ /* Create normal. */ @@ -1521,19 +1184,7 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve /* Calculate the snap location on mouse-move or when toggling snap. */ ipd->is_snap_found = false; if (ipd->use_snap) { - if (ipd->snap_gizmo != NULL) { - ED_gizmotypes_snap_3d_flag_set(ipd->snap_gizmo, ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE); - if (ED_gizmotypes_snap_3d_update(ipd->snap_gizmo, - CTX_data_ensure_evaluated_depsgraph(C), - ipd->region, - ipd->v3d, - G_MAIN->wm.first, - mval_fl)) { - ED_gizmotypes_snap_3d_data_get(ipd->snap_gizmo, ipd->snap_co, NULL, NULL, NULL); - ipd->is_snap_found = true; - } - ED_gizmotypes_snap_3d_flag_clear(ipd->snap_gizmo, ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE); - } + ipd->is_snap_found = view3d_interactive_add_calc_plane(C, event, ipd->snap_co, NULL); } if (ipd->step_index == STEP_BASE) { @@ -1632,6 +1283,7 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) {PLACE_PRIMITIVE_TYPE_SPHERE_ICO, "SPHERE_ICO", 0, "ICO Sphere", ""}, {0, NULL, 0, NULL, NULL}, }; + prop = RNA_def_property(ot->srna, "primitive_type", PROP_ENUM, PROP_NONE); RNA_def_property_ui_text(prop, "Primitive", ""); RNA_def_property_enum_items(prop, primitive_type); @@ -1652,18 +1304,18 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); static const EnumPropertyItem plane_depth_items[] = { - {PLACE_DEPTH_SURFACE, + {V3D_PLACE_DEPTH_SURFACE, "SURFACE", 0, "Surface", "Start placing on the surface, using the 3D cursor position as a fallback"}, - {PLACE_DEPTH_CURSOR_PLANE, + {V3D_PLACE_DEPTH_CURSOR_PLANE, "CURSOR_PLANE", 0, "Cursor Plane", "Start placement using a point projected onto the orientation axis " "at the 3D cursor position"}, - {PLACE_DEPTH_CURSOR_VIEW, + {V3D_PLACE_DEPTH_CURSOR_VIEW, "CURSOR_VIEW", 0, "Cursor View", @@ -1672,17 +1324,17 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) }; prop = RNA_def_property(ot->srna, "plane_depth", PROP_ENUM, PROP_NONE); RNA_def_property_ui_text(prop, "Position", "The initial depth used when placing the cursor"); - RNA_def_property_enum_default(prop, PLACE_DEPTH_SURFACE); + RNA_def_property_enum_default(prop, V3D_PLACE_DEPTH_SURFACE); RNA_def_property_enum_items(prop, plane_depth_items); RNA_def_property_flag(prop, PROP_SKIP_SAVE); static const EnumPropertyItem plane_orientation_items[] = { - {PLACE_ORIENT_SURFACE, + {V3D_PLACE_ORIENT_SURFACE, "SURFACE", ICON_SNAP_NORMAL, "Surface", "Use the surface normal (using the transform orientation as a fallback)"}, - {PLACE_ORIENT_DEFAULT, + {V3D_PLACE_ORIENT_DEFAULT, "DEFAULT", ICON_ORIENTATION_GLOBAL, "Default", @@ -1691,7 +1343,7 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) }; prop = RNA_def_property(ot->srna, "plane_orientation", PROP_ENUM, PROP_NONE); RNA_def_property_ui_text(prop, "Orientation", "The initial depth used when placing the cursor"); - RNA_def_property_enum_default(prop, PLACE_ORIENT_SURFACE); + RNA_def_property_enum_default(prop, V3D_PLACE_ORIENT_SURFACE); RNA_def_property_enum_items(prop, plane_orientation_items); RNA_def_property_flag(prop, PROP_SKIP_SAVE); @@ -1752,24 +1404,75 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) * we could show a placement plane here. * \{ */ -static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +static void preview_plane_cursor_visible_set(wmGizmoGroup *UNUSED(gzgroup), + bool do_draw_plane, + bool do_draw_point) { - wmGizmo *gizmo; + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - { - /* The gizmo snap has to be the first gizmo. */ - const wmGizmoType *gzt_snap; - gzt_snap = WM_gizmotype_find("GIZMO_GT_snap_3d", true); - gizmo = WM_gizmo_new_ptr(gzt_snap, gzgroup, NULL); - - WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f}); - - /* Don't handle any events, this is for display only. */ - gizmo->flag |= WM_GIZMO_HIDDEN_KEYMAP; + if (do_draw_point) { + ED_view3d_cursor_snap_activate_point(); } - /* Sets the gizmos custom-data which has its own free callback. */ - preview_plane_cursor_setup(gzgroup); + if (do_draw_plane) { + ED_view3d_cursor_snap_activate_plane(); + } + else { + ED_view3d_cursor_snap_deactivate_plane(); + } + + if (!do_draw_point) { + ED_view3d_cursor_snap_deactivate_point(); + } + + if (!snap_data && (do_draw_point || do_draw_plane)) { + snap_data = ED_view3d_cursor_snap_data_get(); + UI_GetThemeColor3ubv(TH_TRANSFORM, snap_data->color_line); + snap_data->color_line[3] = 128; + rgba_uchar_args_set(snap_data->color_point, 255, 255, 255, 255); + } +} + +static void preview_plane_free_fn(void *customdata) +{ + preview_plane_cursor_visible_set(customdata, false, false); +} + +static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +{ + preview_plane_cursor_visible_set(gzgroup, true, true); + gzgroup->customdata = gzgroup; + gzgroup->customdata_free = preview_plane_free_fn; +} + +static void WIDGETGROUP_placement_draw_prepare(const bContext *C, wmGizmoGroup *UNUSED(gzgroup)) +{ + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); + if (!snap_data) { + return; + } + + PointerRNA ptr; + { + wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_interactive_add", true); + BLI_assert(ot != NULL); + + ScrArea *area = CTX_wm_area(C); + bToolRef *tref = area->runtime.tool; + WM_toolsystem_ref_properties_ensure_from_operator(tref, ot, &ptr); + } + + short snap_mode = 0; /* #toolsettings->snap_mode. */ + const enum ePlace_SnapTo snap_to = RNA_enum_get(&ptr, "snap_target"); + if (snap_to == PLACE_SNAP_TO_GEOMETRY) { + snap_mode = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | + SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT); + } + snap_data->snap_elem_force = snap_mode; + snap_data->plane_depth = (eV3DPlaceDepth)RNA_enum_get(&ptr, "plane_depth"); + snap_data->plane_orient = (eV3DPlaceOrient)RNA_enum_get(&ptr, "plane_orientation"); + snap_data->plane_axis = RNA_enum_get(&ptr, "plane_axis"); + snap_data->use_plane_axis_auto = RNA_boolean_get(&ptr, "plane_axis_auto"); } void VIEW3D_GGT_placement(wmGizmoGroupType *gzgt) @@ -1784,356 +1487,7 @@ void VIEW3D_GGT_placement(wmGizmoGroupType *gzgt) gzgt->poll = ED_gizmo_poll_or_unlink_delayed_from_tool; gzgt->setup = WIDGETGROUP_placement_setup; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Placement Preview Plane - * - * Preview the plane that will be used for placement. - * - * Note that we might want to split this into its own file, - * for now this is coupled with the 3D view placement gizmo. - * \{ */ - -static void gizmo_plane_update_cursor(const bContext *C, - ARegion *region, - const int mval[2], - float r_co[3], - float r_matrix_orient[3][3], - int *r_plane_axis) -{ - wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_interactive_add", true); - BLI_assert(ot != NULL); - PointerRNA ptr; - - ScrArea *area = CTX_wm_area(C); - BLI_assert(region == CTX_wm_region(C)); - bToolRef *tref = area->runtime.tool; - WM_toolsystem_ref_properties_ensure_from_operator(tref, ot, &ptr); - - const enum ePlace_SnapTo snap_to = RNA_enum_get(&ptr, "snap_target"); - const int plane_axis = RNA_enum_get(&ptr, "plane_axis"); - const bool plane_axis_auto = RNA_boolean_get(&ptr, "plane_axis_auto"); - const enum ePlace_Depth plane_depth = RNA_enum_get(&ptr, "plane_depth"); - const enum ePlace_Orient plane_orient = RNA_enum_get(&ptr, "plane_orientation"); - - const float mval_fl[2] = {UNPACK2(mval)}; - - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - - /* Assign snap gizmo which is may be used as part of the tool. */ - wmGizmo *snap_gizmo = NULL; - { - wmGizmoGroup *gzgroup = idp_gizmogroup_from_region(region); - if ((gzgroup != NULL) && gzgroup->gizmos.first) { - snap_gizmo = gzgroup->gizmos.first; - } - } - - /* This ensures the snap gizmo has settings from this tool. - * This function call could be moved a more appropriate place, - * responding to the setting being changed for example, - * however setting the value isn't expensive, so do it here. */ - idp_snap_gizmo_update_snap_elements(scene, snap_to, snap_gizmo); - - view3d_interactive_add_calc_plane((bContext *)C, - scene, - v3d, - region, - mval_fl, - snap_gizmo, - snap_to, - plane_depth, - plane_orient, - plane_axis, - plane_axis_auto, - r_co, - r_matrix_orient); - *r_plane_axis = plane_axis; -} - -static void gizmo_plane_draw_grid(const int resolution, - const float scale, - const float scale_fade, - const float matrix[4][4], - const int plane_axis, - const float color[4]) -{ - BLI_assert(scale_fade <= scale); - const int resolution_min = resolution - 1; - float color_fade[4] = {UNPACK4(color)}; - const float *center = matrix[3]; - - GPU_blend(GPU_BLEND_ADDITIVE); - GPU_line_smooth(true); - GPU_line_width(1.0f); - - GPUVertFormat *format = immVertexFormat(); - const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - const uint col_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR); - - const size_t coords_len = resolution * resolution; - float(*coords)[3] = MEM_mallocN(sizeof(*coords) * coords_len, __func__); - - const int axis_x = (plane_axis + 0) % 3; - const int axis_y = (plane_axis + 1) % 3; - const int axis_z = (plane_axis + 2) % 3; - - int i; - const float resolution_div = (float)1.0f / (float)resolution; - i = 0; - for (int x = 0; x < resolution; x++) { - const float x_fl = (x * resolution_div) - 0.5f; - for (int y = 0; y < resolution; y++) { - const float y_fl = (y * resolution_div) - 0.5f; - coords[i][axis_x] = 0.0f; - coords[i][axis_y] = x_fl * scale; - coords[i][axis_z] = y_fl * scale; - mul_m4_v3(matrix, coords[i]); - i += 1; - } - } - BLI_assert(i == coords_len); - immBeginAtMost(GPU_PRIM_LINES, coords_len * 4); - i = 0; - for (int x = 0; x < resolution_min; x++) { - for (int y = 0; y < resolution_min; y++) { - - /* Add #resolution_div to ensure we fade-out entirely. */ -#define FADE(v) \ - max_ff(0.0f, (1.0f - square_f(((len_v3v3(v, center) / scale_fade) + resolution_div) * 2.0f))) - - const float *v0 = coords[(resolution * x) + y]; - const float *v1 = coords[(resolution * (x + 1)) + y]; - const float *v2 = coords[(resolution * x) + (y + 1)]; - - const float f0 = FADE(v0); - const float f1 = FADE(v1); - const float f2 = FADE(v2); - - if (f0 > 0.0f || f1 > 0.0f) { - color_fade[3] = color[3] * f0; - immAttr4fv(col_id, color_fade); - immVertex3fv(pos_id, v0); - color_fade[3] = color[3] * f1; - immAttr4fv(col_id, color_fade); - immVertex3fv(pos_id, v1); - } - if (f0 > 0.0f || f2 > 0.0f) { - color_fade[3] = color[3] * f0; - immAttr4fv(col_id, color_fade); - immVertex3fv(pos_id, v0); - - color_fade[3] = color[3] * f2; - immAttr4fv(col_id, color_fade); - immVertex3fv(pos_id, v2); - } - -#undef FADE - - i++; - } - } - - MEM_freeN(coords); - - immEnd(); - - immUnbindProgram(); - - GPU_line_smooth(false); - GPU_blend(GPU_BLEND_NONE); -} - -/* -------------------------------------------------------------------- */ -/** \name Preview Plane Cursor - * \{ */ - -struct PlacementCursor { - /** - * Back-pointer to the gizmo-group that uses this cursor. - * Needed so we know that the cursor belongs to the region. - */ - wmGizmoGroup *gzgroup; - - /** - * Enable this while the modal operator is running, - * so the preview-plane doesn't show at the same time as add-object preview shape - * since it's distracting & not helpful. - */ - bool do_draw; - - void *paintcursor; - - int plane_axis; - float matrix[4][4]; - - /* Check if we need to re-calculate the plane matrix. */ - int mval_prev[2]; - float persmat_prev[4][4]; -}; - -static void cursor_plane_draw(bContext *C, int x, int y, void *customdata) -{ - struct PlacementCursor *plc = (struct PlacementCursor *)customdata; - ARegion *region = CTX_wm_region(C); - const RegionView3D *rv3d = region->regiondata; - - /* Early exit. - * Note that we can't do most of these checks in the poll function (besides global checks) - * so test them here instead. - * - * This cursor is only active while the gizmo is being used - * so it's not so important to have a poll function. */ - if (plc->do_draw == false) { - return; - } - if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) { - return; - } - if (rv3d->rflag & RV3D_NAVIGATING) { - return; - } - - /* Check this gizmo group is in the region. */ - { - wmGizmoMap *gzmap = region->gizmo_map; - wmGizmoGroup *gzgroup_test = WM_gizmomap_group_find_ptr(gzmap, plc->gzgroup->type); - if (gzgroup_test != plc->gzgroup) { - /* Wrong viewport. */ - return; - } - } - - const int mval[2] = {x - region->winrct.xmin, y - region->winrct.ymin}; - - /* Update matrix? */ - if ((plc->mval_prev[0] != mval[0]) || (plc->mval_prev[1] != mval[1]) || - !equals_m4m4(plc->persmat_prev, rv3d->persmat)) { - plc->mval_prev[0] = mval[0]; - plc->mval_prev[1] = mval[1]; - - float orient_matrix[3][3]; - float co[3]; - gizmo_plane_update_cursor(C, region, mval, co, orient_matrix, &plc->plane_axis); - copy_m4_m3(plc->matrix, orient_matrix); - copy_v3_v3(plc->matrix[3], co); - - copy_m4_m4(plc->persmat_prev, rv3d->persmat); - } - - /* Draw */ - float pixel_size; - - if (rv3d->is_persp) { - float center[3]; - negate_v3_v3(center, rv3d->ofs); - pixel_size = ED_view3d_pixel_size(rv3d, center); - } - else { - pixel_size = ED_view3d_pixel_size(rv3d, plc->matrix[3]); - } - - if (pixel_size > FLT_EPSILON) { - - /* Arbitrary, 1.0 is a little too strong though. */ - float color_alpha = 0.75f; - if (rv3d->is_persp) { - /* Scale down the alpha when this is drawn very small, - * since the add shader causes the small size to show too dense & bright. */ - const float relative_pixel_scale = pixel_size / ED_view3d_pixel_size(rv3d, plc->matrix[3]); - if (relative_pixel_scale < 1.0f) { - color_alpha *= max_ff(square_f(relative_pixel_scale), 0.3f); - } - } - - { - /* Extra adjustment when it's near view-aligned as it seems overly bright. */ - float view_vector[3]; - ED_view3d_global_to_vector(rv3d, plc->matrix[3], view_vector); - float view_dot = fabsf(dot_v3v3(plc->matrix[plc->plane_axis], view_vector)); - color_alpha *= max_ff(0.3f, 1.0f - square_f(square_f(1.0f - view_dot))); - } - - /* Setup viewport & matrix. */ - wmViewport(®ion->winrct); - GPU_matrix_push_projection(); - GPU_matrix_push(); - GPU_matrix_projection_set(rv3d->winmat); - GPU_matrix_set(rv3d->viewmat); - - const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize; - - float final_scale = (scale_mod * pixel_size); - - const int lines_subdiv = 10; - int lines = lines_subdiv; - - float final_scale_fade = final_scale; - final_scale = ceil_power_of_10(final_scale); - - float fac = final_scale_fade / final_scale; - - float color[4] = {1, 1, 1, color_alpha}; - color[3] *= square_f(1.0f - fac); - if (color[3] > 0.0f) { - gizmo_plane_draw_grid(lines * lines_subdiv, - final_scale, - final_scale_fade, - plc->matrix, - plc->plane_axis, - color); - } - - color[3] = color_alpha; - /* When the grid is large, we only need the 2x lines in the middle. */ - if (fac < 0.2f) { - lines = 1; - final_scale = final_scale_fade; - } - gizmo_plane_draw_grid( - lines, final_scale, final_scale_fade, plc->matrix, plc->plane_axis, color); - - /* Restore matrix. */ - GPU_matrix_pop(); - GPU_matrix_pop_projection(); - } -} - -static void preview_plane_cursor_free(void *customdata) -{ - struct PlacementCursor *plc = customdata; - - /* The window manager is freed first on exit. */ - wmWindowManager *wm = G_MAIN->wm.first; - if (UNLIKELY(wm != NULL)) { - WM_paint_cursor_end(plc->paintcursor); - } - MEM_freeN(plc); -} - -static void preview_plane_cursor_setup(wmGizmoGroup *gzgroup) -{ - BLI_assert(gzgroup->customdata == NULL); - struct PlacementCursor *plc = MEM_callocN(sizeof(*plc), __func__); - plc->gzgroup = gzgroup; - plc->paintcursor = WM_paint_cursor_activate( - SPACE_VIEW3D, RGN_TYPE_WINDOW, NULL, cursor_plane_draw, plc); - gzgroup->customdata = plc; - gzgroup->customdata_free = preview_plane_cursor_free; - - preview_plane_cursor_visible_set(gzgroup, true); -} - -static void preview_plane_cursor_visible_set(wmGizmoGroup *gzgroup, bool do_draw) -{ - struct PlacementCursor *plc = gzgroup->customdata; - plc->do_draw = do_draw; + gzgt->draw_prepare = WIDGETGROUP_placement_draw_prepare; } /** \} */ diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 2e1e8eb4ca4..7f27d5fb180 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -48,6 +48,7 @@ #include "ED_node.h" #include "ED_transform_snap_object_context.h" #include "ED_uvedit.h" +#include "ED_view3d.h" #include "UI_resources.h" #include "UI_view2d.h" @@ -247,7 +248,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t) loc_cur = t->tsnap.snapPoint; } - ED_gizmotypes_snap_3d_draw_util( + ED_view3d_cursor_snap_draw_util( rv3d, loc_prev, loc_cur, normal, col, activeCol, t->tsnap.snapElem); GPU_depth_test(GPU_DEPTH_LESS_EQUAL); @@ -1243,7 +1244,7 @@ short snapObjectsTransform( TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3]) { float *target = (t->tsnap.status & TARGET_INIT) ? t->tsnap.snapTarget : t->center_global; - return ED_transform_snap_object_project_view3d_ex( + return ED_transform_snap_object_project_view3d( t->tsnap.object_context, t->depsgraph, t->region, @@ -1259,10 +1260,7 @@ short snapObjectsTransform( target, dist_px, r_loc, - r_no, - NULL, - NULL, - NULL); + r_no); } /** \} */ diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 5f9250e87f7..17326001a99 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -3020,7 +3020,8 @@ static short transform_snap_context_project_view3d_mixed_impl( float r_no[3], int *r_index, Object **r_ob, - float r_obmat[4][4]) + float r_obmat[4][4], + float r_face_nor[3]) { BLI_assert((snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) != @@ -3067,22 +3068,27 @@ static short transform_snap_context_project_view3d_mixed_impl( &ob_eval, obmat, NULL); + if (has_hit) { + if (r_face_nor) { + copy_v3_v3(r_face_nor, no); + } - if (has_hit && (snap_to_flag & SCE_SNAP_MODE_FACE)) { - retval = SCE_SNAP_MODE_FACE; + if ((snap_to_flag & SCE_SNAP_MODE_FACE)) { + retval = SCE_SNAP_MODE_FACE; - copy_v3_v3(r_loc, loc); - if (r_no) { - copy_v3_v3(r_no, no); - } - if (r_ob) { - *r_ob = ob_eval; - } - if (r_obmat) { - copy_m4_m4(r_obmat, obmat); - } - if (r_index) { - *r_index = index; + copy_v3_v3(r_loc, loc); + if (r_no) { + copy_v3_v3(r_no, no); + } + if (r_ob) { + *r_ob = ob_eval; + } + if (r_obmat) { + copy_m4_m4(r_obmat, obmat); + } + if (r_index) { + *r_index = index; + } } } } @@ -3182,6 +3188,10 @@ static short transform_snap_context_project_view3d_mixed_impl( if (r_index) { *r_index = index; } + if (r_face_nor && !has_hit) { + /* Fallback. */ + copy_v3_v3(r_face_nor, no); + } *dist_px = dist_px_tmp; } @@ -3203,7 +3213,8 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, float r_no[3], int *r_index, Object **r_ob, - float r_obmat[4][4]) + float r_obmat[4][4], + float r_face_nor[3]) { return transform_snap_context_project_view3d_mixed_impl(sctx, depsgraph, @@ -3218,7 +3229,8 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, r_no, r_index, r_ob, - r_obmat); + r_obmat, + r_face_nor); } /** @@ -3259,6 +3271,7 @@ bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, r_no, NULL, NULL, + NULL, NULL) != 0; } From 59c95a8ca27f13ba972dfd3ee29d3a633b4012ec Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Oct 2021 16:18:53 +1100 Subject: [PATCH 0869/1500] Cleanup: de-duplicate operator registration Operators and operator-macros duplicated RNA properties. --- source/blender/makesrna/intern/rna_wm.c | 117 +++++++----------------- 1 file changed, 31 insertions(+), 86 deletions(-) diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index b45cfb04bc1..f82b6d7c691 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -1883,23 +1883,10 @@ static void rna_def_operator_options_runtime(BlenderRNA *brna) prop, "Focus Region", "Enable to use the region under the cursor for modal execution"); } -static void rna_def_operator(BlenderRNA *brna) +static void rna_def_operator_common(StructRNA *srna) { - StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "Operator", NULL); - RNA_def_struct_ui_text( - srna, "Operator", "Storage of an operator being executed, or registered after execution"); - RNA_def_struct_sdna(srna, "wmOperator"); - RNA_def_struct_refine_func(srna, "rna_Operator_refine"); -# ifdef WITH_PYTHON - RNA_def_struct_register_funcs( - srna, "rna_Operator_register", "rna_Operator_unregister", "rna_Operator_instance"); -# endif - RNA_def_struct_translation_context(srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); - RNA_def_struct_flag(srna, STRUCT_PUBLIC_NAMESPACE_INHERIT); - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_string_funcs(prop, "rna_Operator_name_get", "rna_Operator_name_length", NULL); @@ -1919,15 +1906,6 @@ static void rna_def_operator(BlenderRNA *brna) "Has Reports", "Operator has a set of reports (warnings and errors) from last execution"); - prop = RNA_def_property(srna, "layout", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "UILayout"); - - prop = RNA_def_property(srna, "options", PROP_POINTER, PROP_NONE); - RNA_def_property_flag(prop, PROP_NEVER_NULL); - RNA_def_property_struct_type(prop, "OperatorOptions"); - RNA_def_property_pointer_funcs(prop, "rna_Operator_options_get", NULL, NULL, NULL); - RNA_def_property_ui_text(prop, "Options", "Runtime options"); - /* Registration */ prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->idname"); @@ -1980,6 +1958,35 @@ static void rna_def_operator(BlenderRNA *brna) RNA_def_property_enum_items(prop, rna_enum_operator_type_flag_items); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); RNA_def_property_ui_text(prop, "Options", "Options for this operator type"); +} + +static void rna_def_operator(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "Operator", NULL); + RNA_def_struct_ui_text( + srna, "Operator", "Storage of an operator being executed, or registered after execution"); + RNA_def_struct_sdna(srna, "wmOperator"); + RNA_def_struct_refine_func(srna, "rna_Operator_refine"); +# ifdef WITH_PYTHON + RNA_def_struct_register_funcs( + srna, "rna_Operator_register", "rna_Operator_unregister", "rna_Operator_instance"); +# endif + RNA_def_struct_translation_context(srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); + RNA_def_struct_flag(srna, STRUCT_PUBLIC_NAMESPACE_INHERIT); + + rna_def_operator_common(srna); + + prop = RNA_def_property(srna, "layout", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "UILayout"); + + prop = RNA_def_property(srna, "options", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "OperatorOptions"); + RNA_def_property_pointer_funcs(prop, "rna_Operator_options_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Options", "Runtime options"); prop = RNA_def_property(srna, "macros", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "macro", NULL); @@ -2000,7 +2007,6 @@ static void rna_def_operator(BlenderRNA *brna) static void rna_def_macro_operator(BlenderRNA *brna) { StructRNA *srna; - PropertyRNA *prop; srna = RNA_def_struct(brna, "Macro", NULL); RNA_def_struct_ui_text( @@ -2016,68 +2022,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) RNA_def_struct_translation_context(srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); RNA_def_struct_flag(srna, STRUCT_PUBLIC_NAMESPACE_INHERIT); - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_string_funcs(prop, "rna_Operator_name_get", "rna_Operator_name_length", NULL); - RNA_def_property_ui_text(prop, "Name", ""); - - prop = RNA_def_property(srna, "properties", PROP_POINTER, PROP_NONE); - RNA_def_property_flag(prop, PROP_NEVER_NULL); - RNA_def_property_struct_type(prop, "OperatorProperties"); - RNA_def_property_ui_text(prop, "Properties", ""); - RNA_def_property_pointer_funcs(prop, "rna_Operator_properties_get", NULL, NULL, NULL); - - /* Registration */ - prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->idname"); - RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */ - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set"); - // RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_flag(prop, PROP_REGISTER); - RNA_def_struct_name_property(srna, prop); - - prop = RNA_def_property(srna, "bl_label", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->name"); - RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_label_set"); - // RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_flag(prop, PROP_REGISTER); - - prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->translation_context"); - RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ - RNA_def_property_string_funcs(prop, - "rna_Operator_bl_translation_context_get", - "rna_Operator_bl_translation_context_length", - "rna_Operator_bl_translation_context_set"); - RNA_def_property_string_default(prop, BLT_I18NCONTEXT_OPERATOR_DEFAULT); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - - prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->description"); - RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ - RNA_def_property_string_funcs(prop, - "rna_Operator_bl_description_get", - "rna_Operator_bl_description_length", - "rna_Operator_bl_description_set"); - // RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - - prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->undo_group"); - RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */ - RNA_def_property_string_funcs(prop, - "rna_Operator_bl_undo_group_get", - "rna_Operator_bl_undo_group_length", - "rna_Operator_bl_undo_group_set"); - // RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - - prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "type->flag"); - RNA_def_property_enum_items(prop, rna_enum_operator_type_flag_items); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); - RNA_def_property_ui_text(prop, "Options", "Options for this operator type"); + rna_def_operator_common(srna); RNA_api_macro(srna); } From 6bf8c95e521d6effe9e1c426e14efe20dac81175 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Oct 2021 16:21:33 +1100 Subject: [PATCH 0870/1500] UI: expose additional cursors to the Python API --- source/blender/makesrna/intern/rna_wm_api.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index 7c3b119abb9..09ed525fd87 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -64,6 +64,13 @@ const EnumPropertyItem rna_enum_window_cursor_items[] = { {WM_CURSOR_NS_SCROLL, "SCROLL_Y", 0, "Scroll-Y", ""}, {WM_CURSOR_NSEW_SCROLL, "SCROLL_XY", 0, "Scroll-XY", ""}, {WM_CURSOR_EYEDROPPER, "EYEDROPPER", 0, "Eyedropper", ""}, + {WM_CURSOR_PICK_AREA, "PICK_AREA", 0, "Pick Area", ""}, + {WM_CURSOR_STOP, "STOP", 0, "Stop", ""}, + {WM_CURSOR_COPY, "COPY", 0, "Copy", ""}, + {WM_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {WM_CURSOR_MUTE, "MUTE", 0, "Mute", ""}, + {WM_CURSOR_ZOOM_IN, "ZOOM_IN", 0, "Zoom In", ""}, + {WM_CURSOR_ZOOM_OUT, "ZOOM_OUT", 0, "Zoom Out", ""}, {0, NULL, 0, NULL, NULL}, }; From 2a8e5128c16c17a7b2f6fc5325dc8f5abb4427d4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Oct 2021 17:01:32 +1100 Subject: [PATCH 0871/1500] WM: support setting the operators idle cursor Support setting a cursor when an operator is waiting for input. --- source/blender/makesrna/intern/rna_wm.c | 10 ++++++++++ source/blender/windowmanager/WM_types.h | 3 +++ source/blender/windowmanager/intern/wm_event_system.c | 2 +- source/blender/windowmanager/intern/wm_operator_type.c | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index f82b6d7c691..f46e4a0e7a6 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -1958,6 +1958,16 @@ static void rna_def_operator_common(StructRNA *srna) RNA_def_property_enum_items(prop, rna_enum_operator_type_flag_items); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); RNA_def_property_ui_text(prop, "Options", "Options for this operator type"); + + prop = RNA_def_property(srna, "bl_cursor_pending", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type->cursor_pending"); + RNA_def_property_enum_items(prop, rna_enum_window_cursor_items); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text( + prop, + "Idle Cursor", + "Cursor to use when waiting for the user to select a location to activate the operator " + "(when ``bl_options`` has ``DEPENDS_ON_CURSOR`` set)"); } static void rna_def_operator(BlenderRNA *brna) diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index c4612485e5a..b5f6caf4cb7 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -903,6 +903,9 @@ typedef struct wmOperatorType { /** RNA integration */ ExtensionRNA rna_ext; + /** Cursor to use when waiting for cursor input, see: #OPTYPE_DEPENDS_ON_CURSOR. */ + int cursor_pending; + /** Flag last for padding */ short flag; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index f07f2637a74..3ea61812b8a 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1809,7 +1809,7 @@ void WM_operator_name_call_ptr_with_depends_on_cursor( } } - WM_cursor_modal_set(win, WM_CURSOR_PICK_AREA); + WM_cursor_modal_set(win, ot->cursor_pending); uiOperatorWaitForInput *opwait = MEM_callocN(sizeof(*opwait), __func__); opwait->optype_params.optype = ot; diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c index 39435721d1a..0e30df4ec99 100644 --- a/source/blender/windowmanager/intern/wm_operator_type.c +++ b/source/blender/windowmanager/intern/wm_operator_type.c @@ -110,6 +110,7 @@ static wmOperatorType *wm_operatortype_append__begin(void) /* Set the default i18n context now, so that opfunc can redefine it if needed! */ RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; + ot->cursor_pending = WM_CURSOR_PICK_AREA; return ot; } From 3f4ba64ae3645c82792fcb35af0e2d94a8f0a6b3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Oct 2021 17:07:24 +1100 Subject: [PATCH 0872/1500] Cleanup: use depends-on-cursor for "Object Transfer Mode" operator Replace modal operator with the OPTYPE_DEPENDS_ON_CURSOR flag. This has the advantage of showing the shortcut in the menu. --- release/scripts/startup/bl_ui/space_view3d.py | 3 +- source/blender/editors/object/object_modes.c | 52 +------------------ 2 files changed, 3 insertions(+), 52 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 59991eac92a..64652dc8ff8 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -3049,8 +3049,7 @@ class VIEW3D_MT_sculpt(Menu): layout.separator() - props = layout.operator("object.transfer_mode", text="Transfer Sculpt Mode") - props.use_eyedropper = True + layout.operator("object.transfer_mode", text="Transfer Sculpt Mode") class VIEW3D_MT_mask(Menu): diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index 2c58ef02486..0c1b394a916 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -497,51 +497,8 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base return mode_transfered; } -static int object_transfer_mode_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - switch (event->type) { - case LEFTMOUSE: - if (event->val == KM_PRESS) { - WM_cursor_modal_restore(CTX_wm_window(C)); - ED_workspace_status_text(C, NULL); - - /* This ensures that the click was done in an viewport region. */ - bScreen *screen = CTX_wm_screen(C); - ARegion *region = BKE_screen_find_main_region_at_xy( - screen, SPACE_VIEW3D, event->x, event->y); - if (!region) { - return OPERATOR_CANCELLED; - } - - const int mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; - Base *base_dst = ED_view3d_give_base_under_cursor(C, mval); - const bool mode_transfered = object_transfer_mode_to_base(C, op, base_dst); - if (!mode_transfered) { - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; - } - break; - case RIGHTMOUSE: { - WM_cursor_modal_restore(CTX_wm_window(C)); - ED_workspace_status_text(C, NULL); - return OPERATOR_CANCELLED; - } - } - return OPERATOR_RUNNING_MODAL; -} - static int object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - const bool use_eyedropper = RNA_boolean_get(op->ptr, "use_eyedropper"); - if (use_eyedropper) { - ED_workspace_status_text(C, TIP_("Click in the viewport to select an object")); - WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; - } - Object *ob_src = CTX_data_active_object(C); const eObjectMode src_mode = (eObjectMode)ob_src->mode; @@ -569,17 +526,12 @@ void OBJECT_OT_transfer_mode(wmOperatorType *ot) /* api callbacks */ ot->invoke = object_transfer_mode_invoke; - ot->modal = object_transfer_mode_modal; ot->poll = object_transfer_mode_poll; /* Undo push is handled by the operator. */ - ot->flag = OPTYPE_REGISTER; + ot->flag = OPTYPE_REGISTER | OPTYPE_DEPENDS_ON_CURSOR; - RNA_def_boolean(ot->srna, - "use_eyedropper", - false, - "Use Eyedropper", - "Pick the object to switch to using an eyedropper"); + ot->cursor_pending = WM_CURSOR_EYEDROPPER; RNA_def_boolean(ot->srna, "use_flash_on_transfer", From 4f3f79c38205c35c304b34122a91b4f102ce992e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Oct 2021 18:42:35 +1100 Subject: [PATCH 0873/1500] Keymap: support key-activates-tools for shrink-fatten --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index d0c20bb8932..3ba848e6caf 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -5028,6 +5028,9 @@ def km_mesh(params): ("mesh.bevel", {"type": 'B', "value": 'PRESS', "ctrl": True}, {"properties": [("affect", 'EDGES')]}), (op_tool_cycle, "builtin.bevel"), params), + op_tool_optional( + ("transform.shrink_fatten", {"type": 'S', "value": 'PRESS', "alt": True}, None), + (op_tool_cycle, "builtin.shrink_fatten"), params), ("mesh.bevel", {"type": 'B', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("affect", 'VERTICES')]}), # Selection modes. @@ -5092,7 +5095,6 @@ def km_mesh(params): ("mesh.rip_edge_move", {"type": 'D', "value": 'PRESS', "alt": True}, None), op_menu("VIEW3D_MT_edit_mesh_merge", {"type": 'M', "value": 'PRESS'}), op_menu("VIEW3D_MT_edit_mesh_split", {"type": 'M', "value": 'PRESS', "alt": True}), - ("transform.shrink_fatten", {"type": 'S', "value": 'PRESS', "alt": True}, None), ("mesh.edge_face_add", {"type": 'F', "value": 'PRESS', "repeat": True}, None), ("mesh.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), op_menu("VIEW3D_MT_mesh_add", {"type": 'A', "value": 'PRESS', "shift": True}), From 746ee29d3638402e2435f47787087b6458f026c7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Oct 2021 19:28:17 +1100 Subject: [PATCH 0874/1500] Fix T91700: Strips can be transformed in scope display modes Hide gizmos & prevent transform & selection in scope display. --- .../blender/editors/space_sequencer/sequencer_select.c | 9 ++++++++- .../blender/editors/transform/transform_convert_cursor.c | 4 +++- .../transform/transform_convert_sequencer_image.c | 7 +++++++ source/blender/editors/transform/transform_gizmo_2d.c | 3 +++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index e193cde4535..b4271ebd812 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -786,11 +786,19 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) View2D *v2d = UI_view2d_fromcontext(C); Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); + ARegion *region = CTX_wm_region(C); if (ed == NULL) { return OPERATOR_CANCELLED; } + if (region->regiontype == RGN_TYPE_PREVIEW) { + const SpaceSeq *sseq = CTX_wm_space_seq(C); + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return OPERATOR_CANCELLED; + } + } + bool extend = RNA_boolean_get(op->ptr, "extend"); bool deselect = RNA_boolean_get(op->ptr, "deselect"); bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); @@ -801,7 +809,6 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) mval[0] = RNA_int_get(op->ptr, "mouse_x"); mval[1] = RNA_int_get(op->ptr, "mouse_y"); - ARegion *region = CTX_wm_region(C); int handle_clicked = SEQ_SIDE_NONE; Sequence *seq = NULL; if (region->regiontype == RGN_TYPE_PREVIEW) { diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c index 8a4a13eb4db..ed96eba7f6c 100644 --- a/source/blender/editors/transform/transform_convert_cursor.c +++ b/source/blender/editors/transform/transform_convert_cursor.c @@ -111,7 +111,9 @@ void recalcData_cursor_image(TransInfo *t) void createTransCursor_sequencer(TransInfo *t) { SpaceSeq *sseq = t->area->spacedata.first; - + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return; + } createTransCursor_2D_impl(t, sseq->cursor); } diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index c0dbd5404a4..e8af1680a41 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -123,6 +123,13 @@ void createTransSeqImageData(TransInfo *t) return; } + { + const SpaceSeq *sseq = t->area->spacedata.first; + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return; + } + } + ListBase *seqbase = SEQ_active_seqbase_get(ed); SeqCollection *strips = SEQ_query_rendered_strips(seqbase, t->scene->r.cfra, 0); SEQ_filter_selected_strips(strips); diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 84f7900e31c..7a23a4a92ce 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -96,6 +96,9 @@ static bool gizmo2d_generic_poll(const bContext *C, wmGizmoGroupType *gzgt) if (sseq->gizmo_flag & (SEQ_GIZMO_HIDE | SEQ_GIZMO_HIDE_TOOL)) { return false; } + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return false; + } Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { From eb0d216dc1caab515eb7cf1ef6bb1632e5ca8fae Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 18 Oct 2021 11:40:00 +0200 Subject: [PATCH 0875/1500] Geometry Nodes: decouple multi-function lifetimes from modifier Previously, some multi-functions were allocated in a resource scope. This was fine as long as the multi-functions were only needed during the current evaluation of the node tree. However, now cases arise that require the multi-functions to be alive after the modifier is finished. For example, we want to evaluate fields created with geometry nodes outside of geometry nodes. To make this work, `std::shared_ptr` has to be used in a few more places. Realistically, this shouldn't have a noticable impact on performance. If this does become a bottleneck in the future, we can think about ways to make this work without using `shared_ptr` for multi-functions that are only used once. --- source/blender/functions/FN_field.hh | 4 +- source/blender/functions/intern/field.cc | 2 +- source/blender/modifiers/intern/MOD_nodes.cc | 2 +- .../modifiers/intern/MOD_nodes_evaluator.cc | 16 ++++--- source/blender/nodes/NOD_multi_function.hh | 44 ++++++++++--------- .../nodes/intern/node_multi_function.cc | 9 ++-- 6 files changed, 42 insertions(+), 35 deletions(-) diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index f65c4e443f2..2fca78fa6e7 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -204,14 +204,14 @@ class FieldOperation : public FieldNode { * The multi-function used by this node. It is optionally owned. * Multi-functions with mutable or vector parameters are not supported currently. */ - std::unique_ptr owned_function_; + std::shared_ptr owned_function_; const MultiFunction *function_; /** Inputs to the operation. */ blender::Vector inputs_; public: - FieldOperation(std::unique_ptr function, Vector inputs = {}); + FieldOperation(std::shared_ptr function, Vector inputs = {}); FieldOperation(const MultiFunction &function, Vector inputs = {}); Span inputs() const; diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 39688ef3daf..03af3f53065 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -534,7 +534,7 @@ const GVArray *IndexFieldInput::get_varray_for_context(const fn::FieldContext &U * FieldOperation. */ -FieldOperation::FieldOperation(std::unique_ptr function, +FieldOperation::FieldOperation(std::shared_ptr function, Vector inputs) : FieldOperation(*function, std::move(inputs)) { diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index ebccbb3d2cd..14b40582516 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -924,7 +924,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, { blender::ResourceScope scope; blender::LinearAllocator<> &allocator = scope.linear_allocator(); - blender::nodes::NodeMultiFunctions mf_by_node{tree, scope}; + blender::nodes::NodeMultiFunctions mf_by_node{tree}; Map group_inputs; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 20ee6127504..0e7f5fe155b 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -882,9 +882,9 @@ class GeometryNodesEvaluator { } /* Use the multi-function implementation if it exists. */ - const MultiFunction *multi_function = params_.mf_by_node->try_get(node); - if (multi_function != nullptr) { - this->execute_multi_function_node(node, *multi_function, node_state); + const nodes::NodeMultiFunctions::Item &fn_item = params_.mf_by_node->try_get(node); + if (fn_item.fn != nullptr) { + this->execute_multi_function_node(node, fn_item, node_state); return; } @@ -905,7 +905,7 @@ class GeometryNodesEvaluator { } void execute_multi_function_node(const DNode node, - const MultiFunction &fn, + const nodes::NodeMultiFunctions::Item &fn_item, NodeState &node_state) { if (node->idname().find("Legacy") != StringRef::not_found) { @@ -933,7 +933,13 @@ class GeometryNodesEvaluator { input_fields.append(std::move(*(GField *)single_value.value)); } - auto operation = std::make_shared(fn, std::move(input_fields)); + std::shared_ptr operation; + if (fn_item.owned_fn) { + operation = std::make_shared(fn_item.owned_fn, std::move(input_fields)); + } + else { + operation = std::make_shared(*fn_item.fn, std::move(input_fields)); + } /* Forward outputs. */ int output_index = 0; diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh index c1952cf8014..367ceaab9f7 100644 --- a/source/blender/nodes/NOD_multi_function.hh +++ b/source/blender/nodes/NOD_multi_function.hh @@ -33,15 +33,15 @@ class NodeMultiFunctions; */ class NodeMultiFunctionBuilder : NonCopyable, NonMovable { private: - ResourceScope &resource_scope_; bNode &node_; bNodeTree &tree_; + std::shared_ptr owned_built_fn_; const MultiFunction *built_fn_ = nullptr; friend NodeMultiFunctions; public: - NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, bNodeTree &tree); + NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree); /** * Assign a multi-function for the current node. The input and output parameters of the function @@ -58,31 +58,33 @@ class NodeMultiFunctionBuilder : NonCopyable, NonMovable { bNode &node(); bNodeTree &tree(); - - ResourceScope &resource_scope(); }; /** * Gives access to multi-functions for all nodes in a node tree that support them. */ class NodeMultiFunctions { + public: + struct Item { + const MultiFunction *fn = nullptr; + std::shared_ptr owned_fn; + }; + private: - Map map_; + Map map_; public: - NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope); + NodeMultiFunctions(const DerivedNodeTree &tree); - const MultiFunction *try_get(const DNode &node) const; + const Item &try_get(const DNode &node) const; }; /* -------------------------------------------------------------------- */ /** \name #NodeMultiFunctionBuilder Inline Methods * \{ */ -inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope, - bNode &node, - bNodeTree &tree) - : resource_scope_(resource_scope), node_(node), tree_(tree) +inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree) + : node_(node), tree_(tree) { } @@ -96,11 +98,6 @@ inline bNodeTree &NodeMultiFunctionBuilder::tree() return tree_; } -inline ResourceScope &NodeMultiFunctionBuilder::resource_scope() -{ - return resource_scope_; -} - inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) { built_fn_ = fn; @@ -108,14 +105,14 @@ inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn) { - this->set_matching_fn(&fn); + built_fn_ = &fn; } template inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) { - const T &fn = resource_scope_.construct(std::forward(args)...); - this->set_matching_fn(&fn); + owned_built_fn_ = std::make_shared(std::forward(args)...); + built_fn_ = &*owned_built_fn_; } /** \} */ @@ -124,9 +121,14 @@ inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...ar /** \name #NodeMultiFunctions Inline Methods * \{ */ -inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const +inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const DNode &node) const { - return map_.lookup_default(node->bnode(), nullptr); + static Item empty_item; + const Item *item = map_.lookup_ptr(node->bnode()); + if (item == nullptr) { + return empty_item; + } + return *item; } /** \} */ diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc index c91899ed8c2..6d79ed839b2 100644 --- a/source/blender/nodes/intern/node_multi_function.cc +++ b/source/blender/nodes/intern/node_multi_function.cc @@ -18,7 +18,7 @@ namespace blender::nodes { -NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope) +NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree) { for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) { bNodeTree *btree = tree_ref->btree(); @@ -27,11 +27,10 @@ NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScop if (bnode->typeinfo->build_multi_function == nullptr) { continue; } - NodeMultiFunctionBuilder builder{resource_scope, *bnode, *btree}; + NodeMultiFunctionBuilder builder{*bnode, *btree}; bnode->typeinfo->build_multi_function(builder); - const MultiFunction *fn = builder.built_fn_; - if (fn != nullptr) { - map_.add_new(bnode, fn); + if (builder.built_fn_ != nullptr) { + map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)}); } } } From f9fe755dba8e99d3b7ee1fb9b0a1197c5eb5c687 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Mon, 18 Oct 2021 11:59:49 +0200 Subject: [PATCH 0876/1500] Nodes: add default value to string socket declaration Differential Revision: https://developer.blender.org/D12758 --- .../blender/nodes/NOD_socket_declarations.hh | 26 ++++++++++++++++++- .../nodes/intern/node_socket_declarations.cc | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index e22b96cd1ff..d4958f433d6 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -145,14 +145,26 @@ class ColorBuilder : public SocketDeclarationBuilder { ColorBuilder &default_value(const ColorGeometry4f value); }; +class StringBuilder; + class String : public SocketDeclaration { + private: + std::string default_value_; + + friend StringBuilder; + public: - using Builder = SocketDeclarationBuilder; + using Builder = StringBuilder; bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; bool matches(const bNodeSocket &socket) const override; }; +class StringBuilder : public SocketDeclarationBuilder { + public: + StringBuilder &default_value(const std::string value); +}; + class IDSocketDeclaration : public SocketDeclaration { private: const char *idname_; @@ -322,6 +334,18 @@ inline ColorBuilder &ColorBuilder::default_value(const ColorGeometry4f value) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name #StringBuilder Inline Methods + * \{ */ + +inline StringBuilder &StringBuilder::default_value(std::string value) +{ + decl_->default_value_ = std::move(value); + return *this; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name #IDSocketDeclaration and Children Inline Methods * \{ */ diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index f910679d492..e823476f9e4 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -254,6 +254,7 @@ bNodeSocket &String::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_ou { bNodeSocket &socket = *nodeAddStaticSocket( &ntree, &node, in_out, SOCK_STRING, PROP_NONE, identifier_.c_str(), name_.c_str()); + STRNCPY(((bNodeSocketValueString *)socket.default_value)->value, default_value_.c_str()); this->set_common_flags(socket); return socket; } From 6f76bcc12c179669a819286d289e409d0b63f981 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 18 Oct 2021 12:58:30 +0200 Subject: [PATCH 0877/1500] Fix: missing use-attribute property in geometry nodes modifier The property was missing when a group input changed from not supporting fields to supporting fields. --- source/blender/modifiers/intern/MOD_nodes.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 14b40582516..b28fead299d 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -599,7 +599,7 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) } } - if (input_has_attribute_toggle(*nmd->node_group, socket_index)) { + if (socket_type_has_attribute_toggle(*socket)) { const std::string use_attribute_id = socket->identifier + use_attribute_suffix; const std::string attribute_name_id = socket->identifier + attribute_name_suffix; From 1f510376764debe3e91f736aec6b3af70567243f Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Mon, 18 Oct 2021 06:45:16 -0500 Subject: [PATCH 0878/1500] Geometry Nodes: Endpoint Selection Nodes The Endpoint Selection node allows for the Selection of an aribitrary number of endpoints from each spline in a curve. The start and end inputs are evaluated on the spline domain. The result is outputted as a boolean field on the point domain. Differential Revision: https://developer.blender.org/D12846 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 3 +- source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 3 +- source/blender/nodes/NOD_static_types.h | 1 + .../nodes/legacy/node_geo_curve_endpoints.cc | 2 +- .../node_geo_curve_endpoint_selection.cc | 149 ++++++++++++++++++ 8 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 99be554184b..68dc7103f3a 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -115,6 +115,7 @@ def curve_node_items(context): yield NodeItem("GeometryNodeCurveParameter") yield NodeItem("GeometryNodeInputTangent") yield NodeItem("GeometryNodeInputCurveTilt") + yield NodeItem("GeometryNodeCurveEndpointSelection") yield NodeItem("GeometryNodeCurveHandleTypeSelection") yield NodeItem("GeometryNodeInputSplineCyclic") yield NodeItem("GeometryNodeSplineLength") diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 6dcb35de3af..d33c5e9940c 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1539,6 +1539,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MESH_TO_CURVE 1124 #define GEO_NODE_TRANSFER_ATTRIBUTE 1125 #define GEO_NODE_SUBDIVISION_SURFACE 1126 +#define GEO_NODE_CURVE_ENDPOINT_SELECTION 1127 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index d29762e2af4..145a40d30cc 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5724,6 +5724,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_mesh_to_curve(); register_node_type_geo_legacy_points_to_volume(); register_node_type_geo_legacy_select_by_material(); + register_node_type_geo_legacy_curve_endpoints(); register_node_type_geo_legacy_curve_spline_type(); register_node_type_geo_legacy_curve_reverse(); register_node_type_geo_legacy_select_by_handle_type(); @@ -5752,7 +5753,7 @@ static void registerGeometryNodes() register_node_type_geo_bounding_box(); register_node_type_geo_collection_info(); register_node_type_geo_convex_hull(); - register_node_type_geo_curve_endpoints(); + register_node_type_geo_curve_endpoint_selection(); register_node_type_geo_curve_fill(); register_node_type_geo_curve_fillet(); register_node_type_geo_curve_handle_type_selection(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index a202d247e60..35cd1c8c5a9 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -198,6 +198,7 @@ set(SRC geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_convex_hull.cc + geometry/nodes/node_geo_curve_endpoint_selection.cc geometry/nodes/node_geo_curve_fill.cc geometry/nodes/node_geo_curve_fillet.cc geometry/nodes/node_geo_curve_handle_type_selection.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 94fed3c3975..3ca8c0d02d7 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -38,6 +38,7 @@ void register_node_type_geo_legacy_material_assign(void); void register_node_type_geo_legacy_mesh_to_curve(void); void register_node_type_geo_legacy_points_to_volume(void); void register_node_type_geo_legacy_select_by_material(void); +void register_node_type_geo_legacy_curve_endpoints(void); void register_node_type_geo_legacy_curve_spline_type(void); void register_node_type_geo_legacy_curve_reverse(void); void register_node_type_geo_legacy_select_by_handle_type(void); @@ -66,7 +67,7 @@ void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); void register_node_type_geo_convex_hull(void); -void register_node_type_geo_curve_endpoints(void); +void register_node_type_geo_curve_endpoint_selection(void); void register_node_type_geo_curve_fill(void); void register_node_type_geo_curve_fillet(void); void register_node_type_geo_curve_handle_type_selection(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 827a12ff812..9a361c3a3f1 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -326,6 +326,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", Me DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") +DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "") DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "") DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc index b226cc2d3be..85d1392aa35 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc @@ -208,7 +208,7 @@ static void geo_node_curve_endpoints_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_curve_endpoints() +void register_node_type_geo_legacy_curve_endpoints() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc new file mode 100644 index 00000000000..ee6cf055ecb --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -0,0 +1,149 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_endpoint_selection_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Start Size") + .min(0) + .default_value(1) + .supports_field() + .description("The amount of points to select from the start of each spline"); + b.add_input("End Size") + .min(0) + .default_value(1) + .supports_field() + .description("The amount of points to select from the end of each spline"); + b.add_output("Selection") + .field_source() + .description("The selection from the start and end of the splines based on the input sizes"); +} + +static void select_by_spline(const int start, const int end, MutableSpan r_selection) +{ + const int size = r_selection.size(); + const int start_use = std::min(start, size); + const int end_use = std::min(end, size); + + r_selection.slice(0, start_use).fill(true); + r_selection.slice(size - end_use, end_use).fill(true); +} + +class EndpointFieldInput final : public fn::FieldInput { + Field start_size_; + Field end_size_; + + public: + EndpointFieldInput(Field start_size, Field end_size) + : FieldInput(CPPType::get(), "Selection"), start_size_(start_size), end_size_(end_size) + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask UNUSED(mask), + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) { + return nullptr; + } + + const CurveComponent &curve_component = static_cast(component); + const CurveEval *curve = curve_component.get_for_read(); + + Array control_point_offsets = curve->control_point_offsets(); + + if (curve == nullptr || control_point_offsets.last() == 0) { + return nullptr; + } + + GeometryComponentFieldContext size_context{curve_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{size_context, curve->splines().size()}; + evaluator.add(start_size_); + evaluator.add(end_size_); + evaluator.evaluate(); + const VArray &start_size = evaluator.get_evaluated(0); + const VArray &end_size = evaluator.get_evaluated(1); + + const int point_size = control_point_offsets.last(); + Array selection(point_size, false); + int current_point = 0; + MutableSpan selection_span = selection.as_mutable_span(); + for (int i : IndexRange(curve->splines().size())) { + const SplinePtr &spline = curve->splines()[i]; + if (start_size[i] <= 0 && end_size[i] <= 0) { + selection_span.slice(current_point, spline->size()).fill(false); + } + else { + int start_use = std::max(start_size[i], 0); + int end_use = std::max(end_size[i], 0); + select_by_spline( + start_use, end_use, selection_span.slice(current_point, spline->size())); + } + current_point += spline->size(); + } + return &scope.construct>>(std::move(selection)); + } + return nullptr; + }; + + uint64_t hash() const override + { + return get_default_hash_2(start_size_, end_size_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const EndpointFieldInput *other_endpoint = dynamic_cast( + &other)) { + return start_size_ == other_endpoint->start_size_ && end_size_ == other_endpoint->end_size_; + } + return false; + } +}; + +static void geo_node_curve_endpoint_selection_exec(GeoNodeExecParams params) +{ + Field start_size = params.extract_input>("Start Size"); + Field end_size = params.extract_input>("End Size"); + Field selection_field{std::make_shared(start_size, end_size)}; + params.set_output("Selection", std::move(selection_field)); +} +} // namespace blender::nodes + +void register_node_type_geo_curve_endpoint_selection() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_CURVE_ENDPOINT_SELECTION, "Endpoint Selection", NODE_CLASS_INPUT, 0); + ntype.declare = blender::nodes::geo_node_curve_endpoint_selection_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_endpoint_selection_exec; + + nodeRegisterType(&ntype); +} From f9113c4be836691ba599aab9b2f43e26333f8133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 18 Oct 2021 12:33:53 +0200 Subject: [PATCH 0879/1500] Assets: add global `bke::AssetLibraryService` class Add `blender::bke::AssetLibraryService` class that acts like a blendfile-scoped singleton. It's allocated upon the first call to `BKE_asset_library_load` and destroyed in the LOAD-PRE handler. The `AssetLibraryService` ensures that edits to asset catalogs are not lost when the asset browser editor closes (or even reloads). Instead, the `AssetLibrary` pointers it owns are kept around as long as the blend file is open. Reviewed By: Severin Maniphest Tasks: T92151 Differential Revision: https://developer.blender.org/D12885 --- source/blender/blenkernel/BKE_asset_library.h | 9 +- .../blender/blenkernel/BKE_asset_library.hh | 8 +- source/blender/blenkernel/CMakeLists.txt | 3 + .../blenkernel/intern/asset_library.cc | 29 ++-- .../intern/asset_library_service.cc | 136 ++++++++++++++++++ .../intern/asset_library_service.hh | 90 ++++++++++++ .../intern/asset_library_service_test.cc | 101 +++++++++++++ .../blenkernel/intern/asset_library_test.cc | 29 +++- source/blender/editors/space_file/filelist.c | 6 +- 9 files changed, 388 insertions(+), 23 deletions(-) create mode 100644 source/blender/blenkernel/intern/asset_library_service.cc create mode 100644 source/blender/blenkernel/intern/asset_library_service.hh create mode 100644 source/blender/blenkernel/intern/asset_library_service_test.cc diff --git a/source/blender/blenkernel/BKE_asset_library.h b/source/blender/blenkernel/BKE_asset_library.h index 12ca55c0ef4..b4674a8d932 100644 --- a/source/blender/blenkernel/BKE_asset_library.h +++ b/source/blender/blenkernel/BKE_asset_library.h @@ -29,9 +29,14 @@ extern "C" { /** Forward declaration, defined in intern/asset_library.hh */ typedef struct AssetLibrary AssetLibrary; -/** TODO(@sybren): properly have a think/discussion about the API for this. */ +/** + * Return the #AssetLibrary rooted at the given directory path. + * + * Will return the same pointer for repeated calls, until another blend file is loaded. + * + * To get the in-memory-only "current file" asset library, pass an empty path. + */ struct AssetLibrary *BKE_asset_library_load(const char *library_path); -void BKE_asset_library_free(struct AssetLibrary *asset_library); /** * Try to find an appropriate location for an asset library root from a file or directory path. diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh index 419df2a1061..b8b4b0f8447 100644 --- a/source/blender/blenkernel/BKE_asset_library.hh +++ b/source/blender/blenkernel/BKE_asset_library.hh @@ -33,9 +33,15 @@ namespace blender::bke { +/** + * AssetLibrary provides access to an asset library's data. + * For now this is only for catalogs, later this can be expanded to indexes/caches/more. + */ struct AssetLibrary { std::unique_ptr catalog_service; + ~AssetLibrary(); + void load(StringRefNull library_root_directory); /** @@ -52,7 +58,7 @@ struct AssetLibrary { void on_save_post(struct Main *, struct PointerRNA **pointers, const int num_pointers); private: - bCallbackFuncStore on_save_callback_store_; + bCallbackFuncStore on_save_callback_store_{}; }; } // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 47c1d698360..a86c6fbd3dd 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -90,6 +90,7 @@ set(SRC intern/asset_catalog.cc intern/asset_catalog_path.cc intern/asset_library.cc + intern/asset_library_service.cc intern/attribute.c intern/attribute_access.cc intern/attribute_math.cc @@ -472,6 +473,7 @@ set(SRC intern/CCGSubSurf_inline.h intern/CCGSubSurf_intern.h intern/attribute_access_intern.hh + intern/asset_library_service.hh intern/data_transfer_intern.h intern/lib_intern.h intern/multires_inline.h @@ -801,6 +803,7 @@ if(WITH_GTESTS) intern/armature_test.cc intern/asset_catalog_test.cc intern/asset_catalog_path_test.cc + intern/asset_library_service_test.cc intern/asset_library_test.cc intern/asset_test.cc intern/cryptomatte_test.cc diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index 5956a4af0cb..48ace8efea1 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -31,6 +31,8 @@ #include "MEM_guardedalloc.h" +#include "asset_library_service.hh" + #include /** @@ -39,19 +41,17 @@ */ struct AssetLibrary *BKE_asset_library_load(const char *library_path) { - blender::bke::AssetLibrary *lib = new blender::bke::AssetLibrary(); - lib->on_save_handler_register(); - lib->load(library_path); + blender::bke::AssetLibraryService *service = blender::bke::AssetLibraryService::get(); + blender::bke::AssetLibrary *lib; + if (library_path == nullptr || library_path[0] == '\0') { + lib = service->get_asset_library_current_file(); + } + else { + lib = service->get_asset_library_on_disk(library_path); + } return reinterpret_cast(lib); } -void BKE_asset_library_free(struct AssetLibrary *asset_library) -{ - blender::bke::AssetLibrary *lib = reinterpret_cast(asset_library); - lib->on_save_handler_unregister(); - delete lib; -} - bool BKE_asset_library_find_suitable_root_path_from_path(const char *input_path, char *r_library_path) { @@ -102,6 +102,13 @@ void BKE_asset_library_refresh_catalog_simplename(struct AssetLibrary *asset_lib namespace blender::bke { +AssetLibrary::~AssetLibrary() +{ + if (on_save_callback_store_.func) { + on_save_handler_unregister(); + } +} + void AssetLibrary::load(StringRefNull library_root_directory) { auto catalog_service = std::make_unique(library_root_directory); @@ -134,6 +141,8 @@ void AssetLibrary::on_save_handler_register() void AssetLibrary::on_save_handler_unregister() { BKE_callback_remove(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST); + on_save_callback_store_.func = nullptr; + on_save_callback_store_.arg = nullptr; } void AssetLibrary::on_save_post(struct Main *main, diff --git a/source/blender/blenkernel/intern/asset_library_service.cc b/source/blender/blenkernel/intern/asset_library_service.cc new file mode 100644 index 00000000000..14604def0d3 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_library_service.cc @@ -0,0 +1,136 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#include "asset_library_service.hh" + +#include "BKE_asset_library.hh" +#include "BKE_blender.h" +#include "BKE_callbacks.h" + +#include "BLI_string_ref.hh" + +#include "MEM_guardedalloc.h" + +#include "CLG_log.h" + +static CLG_LogRef LOG = {"bke.asset_service"}; + +namespace blender::bke { + +std::unique_ptr AssetLibraryService::instance_; +bool AssetLibraryService::atexit_handler_registered_ = false; + +AssetLibraryService *AssetLibraryService::get() +{ + if (!instance_) { + allocate_service_instance(); + } + return instance_.get(); +} + +void AssetLibraryService::destroy() +{ + if (!instance_) { + return; + } + instance_->app_handler_unregister(); + instance_.reset(); +} + +AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull top_level_directory) +{ + BLI_assert_msg(!top_level_directory.is_empty(), + "top level directory must be given for on-disk asset library"); + + AssetLibraryPtr *lib_uptr_ptr = on_disk_libraries_.lookup_ptr(top_level_directory); + if (lib_uptr_ptr != nullptr) { + CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", top_level_directory.c_str()); + return lib_uptr_ptr->get(); + } + + AssetLibraryPtr lib_uptr = std::make_unique(); + AssetLibrary *lib = lib_uptr.get(); + + lib->on_save_handler_register(); + lib->load(top_level_directory); + + on_disk_libraries_.add_new(top_level_directory, std::move(lib_uptr)); + CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", top_level_directory.c_str()); + return lib; +} + +AssetLibrary *AssetLibraryService::get_asset_library_current_file() +{ + if (current_file_library_) { + CLOG_INFO(&LOG, 2, "get current file lib (cached)"); + } + else { + CLOG_INFO(&LOG, 2, "get current file lib (loaded)"); + current_file_library_ = std::make_unique(); + current_file_library_->on_save_handler_register(); + } + + AssetLibrary *lib = current_file_library_.get(); + return lib; +} + +void AssetLibraryService::allocate_service_instance() +{ + instance_ = std::make_unique(); + instance_->app_handler_register(); + + if (!atexit_handler_registered_) { + /* Ensure the instance gets freed before Blender's memory leak detector runs. */ + BKE_blender_atexit_register([](void * /*user_data*/) { AssetLibraryService::destroy(); }, + nullptr); + atexit_handler_registered_ = true; + } +} + +static void on_blendfile_load(struct Main * /*bMain*/, + struct PointerRNA ** /*pointers*/, + const int /*num_pointers*/, + void * /*arg*/) +{ + AssetLibraryService::destroy(); +} + +/** + * Ensure the AssetLibraryService instance is destroyed before a new blend file is loaded. + * This makes memory management simple, and ensures a fresh start for every blend file. */ +void AssetLibraryService::app_handler_register() +{ + /* The callback system doesn't own `on_load_callback_store_`. */ + on_load_callback_store_.alloc = false; + + on_load_callback_store_.func = &on_blendfile_load; + on_load_callback_store_.arg = this; + + BKE_callback_add(&on_load_callback_store_, BKE_CB_EVT_LOAD_PRE); +} + +void AssetLibraryService::app_handler_unregister() +{ + BKE_callback_remove(&on_load_callback_store_, BKE_CB_EVT_LOAD_PRE); + on_load_callback_store_.func = nullptr; + on_load_callback_store_.arg = nullptr; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_library_service.hh b/source/blender/blenkernel/intern/asset_library_service.hh new file mode 100644 index 00000000000..63ffe56ab74 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_library_service.hh @@ -0,0 +1,90 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#pragma once + +#ifndef __cplusplus +# error This is a C++-only header file. +#endif + +#include "BKE_asset_library.hh" + +#include "BLI_map.hh" + +#include + +namespace blender::bke { + +/** + * Global singleton-ish that provides access to individual #AssetLibrary instances. + * + * Whenever a blend file is loaded, the existing instance of AssetLibraryService is destructed, and + * a new one is created -- hence the "singleton-ish". This ensures only information about relevant + * asset libraries is loaded. + * + * \note How Asset libraries are identified may change in the future. + * For now they are assumed to be: + * - on disk (identified by the absolute directory), or + * - the "current file" library (which is in memory but could have catalogs + * loaded from a file on disk). + */ +class AssetLibraryService { + public: + using AssetLibraryPtr = std::unique_ptr; + + AssetLibraryService() = default; + ~AssetLibraryService() = default; + + /** Return the AssetLibraryService singleton, allocating it if necessary. */ + static AssetLibraryService *get(); + + /** Destroy the AssetLibraryService singleton. It will be reallocated by #get() if necessary. */ + static void destroy(); + + /** + * Get the given asset library. Opens it (i.e. creates a new AssetLibrary instance) if necessary. + */ + AssetLibrary *get_asset_library_on_disk(StringRefNull top_level_directory); + + /** Get the "Current File" asset library. */ + AssetLibrary *get_asset_library_current_file(); + + protected: + static std::unique_ptr instance_; + + /* Mapping absolute path of the library's top-level directory to the AssetLibrary instance. */ + Map on_disk_libraries_; + AssetLibraryPtr current_file_library_; + + /* Handlers for managing the life cycle of the AssetLibraryService instance. */ + bCallbackFuncStore on_load_callback_store_; + static bool atexit_handler_registered_; + + /** Allocate a new instance of the service and assign it to `instance_`. */ + static void allocate_service_instance(); + + /** + * Ensure the AssetLibraryService instance is destroyed before a new blend file is loaded. + * This makes memory management simple, and ensures a fresh start for every blend file. */ + void app_handler_register(); + void app_handler_unregister(); +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc new file mode 100644 index 00000000000..6acbe193eb7 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -0,0 +1,101 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation + * All rights reserved. + */ + +#include "asset_library_service.hh" + +#include "CLG_log.h" + +#include "testing/testing.h" + +namespace blender::bke::tests { + +class AssetLibraryServiceTest : public testing::Test { + public: + CatalogFilePath asset_library_root_; + + static void SetUpTestSuite() + { + CLG_init(); + } + static void TearDownTestSuite() + { + CLG_exit(); + } + + void SetUp() override + { + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + asset_library_root_ = test_files_dir + "/" + "asset_library"; + } + + void TearDown() override + { + AssetLibraryService::destroy(); + } +}; + +TEST_F(AssetLibraryServiceTest, get_destroy) +{ + AssetLibraryService *const service = AssetLibraryService::get(); + EXPECT_EQ(service, AssetLibraryService::get()) + << "Calling twice without destroying in between should return the same instance."; + + AssetLibraryService::destroy(); + EXPECT_NE(service, AssetLibraryService::get()) + << "Calling twice with destroying in between should return a new instance."; + + /* This should not crash. */ + AssetLibraryService::destroy(); + AssetLibraryService::destroy(); +} + +TEST_F(AssetLibraryServiceTest, library_pointers) +{ + AssetLibraryService *service = AssetLibraryService::get(); + AssetLibrary *const lib = service->get_asset_library_on_disk(asset_library_root_); + AssetLibrary *const curfile_lib = service->get_asset_library_current_file(); + + EXPECT_EQ(lib, service->get_asset_library_on_disk(asset_library_root_)) + << "Calling twice without destroying in between should return the same instance."; + EXPECT_EQ(curfile_lib, service->get_asset_library_current_file()) + << "Calling twice without destroying in between should return the same instance."; + + AssetLibraryService::destroy(); + service = AssetLibraryService::get(); + EXPECT_NE(lib, service->get_asset_library_on_disk(asset_library_root_)) + << "Calling twice with destroying in between should return a new instance."; + EXPECT_NE(curfile_lib, service->get_asset_library_current_file()) + << "Calling twice with destroying in between should return a new instance."; +} + +TEST_F(AssetLibraryServiceTest, catalogs_loaded) +{ + AssetLibraryService *const service = AssetLibraryService::get(); + AssetLibrary *const lib = service->get_asset_library_on_disk(asset_library_root_); + AssetCatalogService *const cat_service = lib->catalog_service.get(); + + const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); + EXPECT_NE(nullptr, cat_service->find_catalog(UUID_POSES_ELLIE)) + << "Catalogs should be loaded after getting an asset library from disk."; +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc index 30ac4dc6ad8..7c3587c6dfa 100644 --- a/source/blender/blenkernel/intern/asset_library_test.cc +++ b/source/blender/blenkernel/intern/asset_library_test.cc @@ -21,11 +21,32 @@ #include "BKE_asset_catalog.hh" #include "BKE_asset_library.hh" +#include "asset_library_service.hh" + +#include "CLG_log.h" + #include "testing/testing.h" namespace blender::bke::tests { -TEST(AssetLibraryTest, load_and_free_c_functions) +class AssetLibraryServiceTest : public testing::Test { + public: + static void SetUpTestSuite() + { + CLG_init(); + } + static void TearDownTestSuite() + { + CLG_exit(); + } + + void TearDown() override + { + AssetLibraryService::destroy(); + } +}; + +TEST_F(AssetLibraryServiceTest, bke_asset_library_load) { const std::string test_files_dir = blender::tests::flags_test_asset_dir(); if (test_files_dir.empty()) { @@ -50,11 +71,9 @@ TEST(AssetLibraryTest, load_and_free_c_functions) AssetCatalog *poses_ellie = service->find_catalog(uuid_poses_ellie); ASSERT_NE(nullptr, poses_ellie) << "unable to find POSES_ELLIE catalog"; EXPECT_EQ("character/Ellie/poselib", poses_ellie->path.str()); - - BKE_asset_library_free(library_c_ptr); } -TEST(AssetLibraryTest, load_nonexistent_directory) +TEST_F(AssetLibraryServiceTest, load_nonexistent_directory) { const std::string test_files_dir = blender::tests::flags_test_asset_dir(); if (test_files_dir.empty()) { @@ -75,8 +94,6 @@ TEST(AssetLibraryTest, load_nonexistent_directory) /* Check that the catalog service doesn't have any catalogs. */ EXPECT_TRUE(service->is_empty()); - - BKE_asset_library_free(library_c_ptr); } } // namespace blender::bke::tests diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 773a321da5c..fc502b065f3 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -391,7 +391,7 @@ typedef struct FileList { eFileSelectType type; /* The library this list was created for. Stored here so we know when to re-read. */ AssetLibraryReference *asset_library_ref; - struct AssetLibrary *asset_library; + struct AssetLibrary *asset_library; /* Non-owning pointer. */ short flags; @@ -1847,9 +1847,7 @@ void filelist_clear_ex(struct FileList *filelist, } if (do_asset_library && (filelist->asset_library != NULL)) { - /* There is no way to refresh the catalogs stored by the AssetLibrary struct, so instead of - * "clearing" it, the entire struct is freed. It will be reallocated when needed. */ - BKE_asset_library_free(filelist->asset_library); + /* The AssetLibraryService owns the AssetLibrary pointer, so no need for us to free it. */ filelist->asset_library = NULL; } } From 4de0e2e7717f458b496fdf2b00a724412abb88a0 Mon Sep 17 00:00:00 2001 From: Jan-Willem van Dronkelaar Date: Mon, 18 Oct 2021 14:23:23 +0200 Subject: [PATCH 0880/1500] Animation: Motion Paths Refresh All Adds a button, Update All Paths, to the Motion Paths property tabs and will always show. The operator goes through all visible objects and updates their motion paths. The current implementation has a subtle functional change. Calculating or updating motion paths for armature objects (through the Object tab, not Armature tab) now also updates the paths for its bones. We could preserve the old behavior but it doesn't seem necessary. It seems more likely that the animator wants to update both anyways. Reviewed by: sybren Maniphest Tasks: T83068 Differential Revision: https://developer.blender.org/D11667 --- .../startup/bl_ui/properties_animviz.py | 7 +- release/scripts/startup/bl_ui/space_view3d.py | 2 + source/blender/editors/include/ED_object.h | 11 +- source/blender/editors/object/object_edit.c | 114 ++++++++++++++++-- source/blender/editors/object/object_intern.h | 1 + source/blender/editors/object/object_ops.c | 1 + .../transform/transform_convert_object.c | 5 +- 7 files changed, 124 insertions(+), 17 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_animviz.py b/release/scripts/startup/bl_ui/properties_animviz.py index 6c3c1fd1721..92e78cd32b6 100644 --- a/release/scripts/startup/bl_ui/properties_animviz.py +++ b/release/scripts/startup/bl_ui/properties_animviz.py @@ -68,20 +68,25 @@ class MotionPathButtonsPanel: col.prop(mpath, "frame_start", text="Cache From") col.prop(mpath, "frame_end", text="To") - row = layout.row(align=True) + col = layout.column(align=True) + + row = col.row(align=True) if bones: row.operator("pose.paths_update", text="Update Paths", icon='BONE_DATA') row.operator("pose.paths_clear", text="", icon='X') else: row.operator("object.paths_update", text="Update Paths", icon='OBJECT_DATA') row.operator("object.paths_clear", text="", icon='X') + col.operator("object.paths_update_visible", text="Update All Paths", icon="WORLD") else: col = layout.column(align=True) col.label(text="Nothing to show yet...", icon='ERROR') + if bones: col.operator("pose.paths_calculate", text="Calculate...", icon='BONE_DATA') else: col.operator("object.paths_calculate", text="Calculate...", icon='OBJECT_DATA') + col.operator("object.paths_update_visible", text="Update All Paths", icon="WORLD") class MotionPathButtonsPanel_display: diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 64652dc8ff8..1808287f7a9 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -3601,6 +3601,8 @@ class VIEW3D_MT_pose_context_menu(Menu): layout.operator("pose.paths_calculate", text="Calculate Motion Paths") layout.operator("pose.paths_clear", text="Clear Motion Paths") + layout.operator("pose.paths_update", text="Update Armature Motion Paths") + layout.operator("object.paths_update_visible", text="Update All Motion Paths") layout.separator() diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 5397cd95ace..083d167c573 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -339,7 +339,16 @@ typedef enum eObjectPathCalcRange { void ED_objects_recalculate_paths(struct bContext *C, struct Scene *scene, - eObjectPathCalcRange range); + eObjectPathCalcRange range, + struct ListBase *ld_objects); + +void ED_objects_recalculate_paths_selected(struct bContext *C, + struct Scene *scene, + eObjectPathCalcRange range); + +void ED_objects_recalculate_paths_visible(struct bContext *C, + struct Scene *scene, + eObjectPathCalcRange range); /* constraints */ struct ListBase *ED_object_constraint_active_list(struct Object *ob); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 2bd0ae5f121..78440f52160 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1125,12 +1125,51 @@ static eAnimvizCalcRange object_path_convert_range(eObjectPathCalcRange range) return ANIMVIZ_CALC_RANGE_FULL; } +void ED_objects_recalculate_paths_selected(bContext *C, Scene *scene, eObjectPathCalcRange range) +{ + ListBase selected_objects = {NULL, NULL}; + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + BLI_addtail(&selected_objects, BLI_genericNodeN(ob)); + } + CTX_DATA_END; + + ED_objects_recalculate_paths(C, scene, range, &selected_objects); + + BLI_freelistN(&selected_objects); +} + +void ED_objects_recalculate_paths_visible(bContext *C, Scene *scene, eObjectPathCalcRange range) +{ + ListBase visible_objects = {NULL, NULL}; + CTX_DATA_BEGIN (C, Object *, ob, visible_objects) { + BLI_addtail(&visible_objects, BLI_genericNodeN(ob)); + } + CTX_DATA_END; + + ED_objects_recalculate_paths(C, scene, range, &visible_objects); + + BLI_freelistN(&visible_objects); +} + +static bool has_object_motion_paths(Object *ob) +{ + return (ob->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0; +} + +static bool has_pose_motion_paths(Object *ob) +{ + return ob->pose && (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0; +} + /* For the objects with animation: update paths for those that have got them * This should selectively update paths that exist... * * To be called from various tools that do incremental updates */ -void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRange range) +void ED_objects_recalculate_paths(bContext *C, + Scene *scene, + eObjectPathCalcRange range, + ListBase *ld_objects) { /* Transform doesn't always have context available to do update. */ if (C == NULL) { @@ -1141,13 +1180,20 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRang ViewLayer *view_layer = CTX_data_view_layer(C); ListBase targets = {NULL, NULL}; - /* loop over objects in scene */ - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + LISTBASE_FOREACH (LinkData *, link, ld_objects) { + Object *ob = link->data; + /* set flag to force recalc, then grab path(s) from object */ - ob->avs.recalc |= ANIMVIZ_RECALC_PATHS; + if (has_object_motion_paths(ob)) { + ob->avs.recalc |= ANIMVIZ_RECALC_PATHS; + } + + if (has_pose_motion_paths(ob)) { + ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS; + } + animviz_get_object_motionpaths(ob, &targets); } - CTX_DATA_END; Depsgraph *depsgraph; bool free_depsgraph = false; @@ -1172,12 +1218,13 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRang if (range != OBJECT_PATH_CALC_RANGE_CURRENT_FRAME) { /* Tag objects for copy on write - so paths will draw/redraw * For currently frame only we update evaluated object directly. */ - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - if (ob->mpath) { + LISTBASE_FOREACH (LinkData *, link, ld_objects) { + Object *ob = link->data; + + if (has_object_motion_paths(ob) || has_pose_motion_paths(ob)) { DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } } - CTX_DATA_END; } /* Free temporary depsgraph. */ @@ -1229,10 +1276,10 @@ static int object_calculate_paths_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* calculate the paths for objects that have them (and are tagged to get refreshed) */ - ED_objects_recalculate_paths(C, scene, OBJECT_PATH_CALC_RANGE_FULL); + ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL); /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL); return OPERATOR_FINISHED; } @@ -1298,10 +1345,10 @@ static int object_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) } /* calculate the paths for objects that have them (and are tagged to get refreshed) */ - ED_objects_recalculate_paths(C, scene, OBJECT_PATH_CALC_RANGE_FULL); + ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL); /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL); return OPERATOR_FINISHED; } @@ -1311,7 +1358,7 @@ void OBJECT_OT_paths_update(wmOperatorType *ot) /* identifiers */ ot->name = "Update Object Paths"; ot->idname = "OBJECT_OT_paths_update"; - ot->description = "Recalculate paths for selected objects"; + ot->description = "Recalculate motion paths for selected objects"; /* api callbacks */ ot->exec = object_update_paths_exec; @@ -1323,6 +1370,47 @@ void OBJECT_OT_paths_update(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Update All Motion Paths Operator + * \{ */ + +static bool object_update_all_paths_poll(bContext *UNUSED(C)) +{ + return true; +} + +static int object_update_all_paths_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + + if (scene == NULL) { + return OPERATOR_CANCELLED; + } + + ED_objects_recalculate_paths_visible(C, scene, OBJECT_PATH_CALC_RANGE_FULL); + + WM_event_add_notifier(C, NC_OBJECT | ND_POSE | ND_TRANSFORM, NULL); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_paths_update_visible(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Update All Object Paths"; + ot->idname = "OBJECT_OT_paths_update_visible"; + ot->description = "Recalculate all visible motion paths for objects and poses"; + + /* api callbacks */ + ot->exec = object_update_all_paths_exec; + ot->poll = object_update_all_paths_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Clear Motion Paths Operator * \{ */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index d00e6efeb29..ea9a2de090b 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -84,6 +84,7 @@ void OBJECT_OT_paths_calculate(struct wmOperatorType *ot); void OBJECT_OT_paths_update(struct wmOperatorType *ot); void OBJECT_OT_paths_clear(struct wmOperatorType *ot); void OBJECT_OT_paths_range_update(struct wmOperatorType *ot); +void OBJECT_OT_paths_update_visible(struct wmOperatorType *ot); void OBJECT_OT_forcefield_toggle(struct wmOperatorType *ot); void OBJECT_OT_move_to_collection(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index fa0208a7022..b3bf2c64a91 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -62,6 +62,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_paths_update); WM_operatortype_append(OBJECT_OT_paths_clear); WM_operatortype_append(OBJECT_OT_paths_range_update); + WM_operatortype_append(OBJECT_OT_paths_update_visible); WM_operatortype_append(OBJECT_OT_forcefield_toggle); WM_operatortype_append(OBJECT_OT_transfer_mode); diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 1acd8787f51..947fbb00fad 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -910,7 +910,8 @@ void recalcData_objects(TransInfo *t) if (motionpath_update) { /* Update motion paths once for all transformed objects. */ - ED_objects_recalculate_paths(t->context, t->scene, OBJECT_PATH_CALC_RANGE_CURRENT_FRAME); + ED_objects_recalculate_paths_selected( + t->context, t->scene, OBJECT_PATH_CALC_RANGE_CURRENT_FRAME); } if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) { @@ -994,7 +995,7 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t) /* Update motion paths once for all transformed objects. */ const eObjectPathCalcRange range = canceled ? OBJECT_PATH_CALC_RANGE_CURRENT_FRAME : OBJECT_PATH_CALC_RANGE_CHANGED; - ED_objects_recalculate_paths(C, t->scene, range); + ED_objects_recalculate_paths_selected(C, t->scene, range); } clear_trans_object_base_flags(t); From 765b1c6b53da655d55af18f883d91fd1238fb773 Mon Sep 17 00:00:00 2001 From: Pablo Dobarro Date: Mon, 18 Oct 2021 14:52:06 +0200 Subject: [PATCH 0881/1500] Fix T79005: Vertex color conversion operators changing the colors CD_PROP_COLOR vertex data is stored in scene linear while legacy vertex colors are srgb, so both operators also need to do this conversion Reviewed By: sergey Maniphest Tasks: T79005 Differential Revision: https://developer.blender.org/D8320 --- source/blender/editors/sculpt_paint/sculpt.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 7bde864e73f..95be866adf1 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -30,6 +30,7 @@ #include "BLI_gsqueue.h" #include "BLI_hash.h" #include "BLI_math.h" +#include "BLI_math_color.h" #include "BLI_math_color_blend.h" #include "BLI_task.h" #include "BLI_utildefines.h" @@ -8684,10 +8685,12 @@ static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) for (int j = 0; j < c_poly->totloop; j++) { int loop_index = c_poly->loopstart + j; MLoop *c_loop = &loops[c_poly->loopstart + j]; - loopcols[loop_index].r = (char)(vertcols[c_loop->v].color[0] * 255); - loopcols[loop_index].g = (char)(vertcols[c_loop->v].color[1] * 255); - loopcols[loop_index].b = (char)(vertcols[c_loop->v].color[2] * 255); - loopcols[loop_index].a = (char)(vertcols[c_loop->v].color[3] * 255); + float srgb_color[4]; + linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color); + loopcols[loop_index].r = (char)(srgb_color[0] * 255); + loopcols[loop_index].g = (char)(srgb_color[1] * 255); + loopcols[loop_index].b = (char)(srgb_color[2] * 255); + loopcols[loop_index].a = (char)(srgb_color[3] * 255); } } @@ -8751,6 +8754,7 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f); vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f); vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f); + srgb_to_linearrgb_v4(vertcols[c_loop->v].color, vertcols[c_loop->v].color); } } From e150f171d5fb2b93277e55329b08e1ebd6dff631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Mon, 18 Oct 2021 15:14:50 +0200 Subject: [PATCH 0882/1500] Fix T92250 EEVEE: Render crash with Motion Blur and Overscan This was caused by `DRW_view_data_texture_list_size_validate` which now delete everything from the render engine. This might change in the future but for now we just avoid calling it from the render loop (when using DRW_cache_restart). --- source/blender/draw/intern/draw_manager.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 4761e8b755f..1d9bc607590 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -565,6 +565,7 @@ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int s drw_viewport_data_reset(dst->vmempool); + bool do_validation = true; if (size == NULL && viewport == NULL) { /* Avoid division by 0. Engines will either override this or not use it. */ dst->size[0] = 1.0f; @@ -580,11 +581,15 @@ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int s BLI_assert(size); dst->size[0] = size[0]; dst->size[1] = size[1]; + /* Fix case when used in DRW_cache_restart(). */ + do_validation = false; } dst->inv_size[0] = 1.0f / dst->size[0]; dst->inv_size[1] = 1.0f / dst->size[1]; - DRW_view_data_texture_list_size_validate(dst->view_data_active, (int[2]){UNPACK2(dst->size)}); + if (do_validation) { + DRW_view_data_texture_list_size_validate(dst->view_data_active, (int[2]){UNPACK2(dst->size)}); + } if (viewport) { DRW_view_data_default_lists_from_viewport(dst->view_data_active, viewport); From de6bf5d4d2f1f832f8305c519fc88d8896ea9a0b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 18 Oct 2021 15:21:51 +0200 Subject: [PATCH 0883/1500] Nodes: support sharing node declarations between nodes Previously, every node had its own declaration. This isn't ideal, because it's often the case that all nodes of the same type have the same declaration. That's the case for all nodes using declarations currently. It will not be true for e.g. group nodes in the future. Sharing node declarations between nodes makes it a bit more efficient. Differential Revision: https://developer.blender.org/D12898 --- source/blender/blenkernel/BKE_node.h | 4 +++ source/blender/blenkernel/intern/node.cc | 41 +++++++++++++++++------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index d33c5e9940c..65e54be7db4 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -339,6 +339,10 @@ typedef struct bNodeType { /* Declares which sockets the node has. */ NodeDeclareFunction declare; + /* Different nodes of this type can have different declarations. */ + bool declaration_is_dynamic; + /* Declaration to be used when it is not dynamic. */ + NodeDeclarationHandle *fixed_declaration; /* RNA integration */ ExtensionRNA rna_ext; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 145a40d30cc..5a4849f1d05 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -1379,6 +1379,8 @@ static void node_free_type(void *nodetype_v) free_dynamic_typeinfo(nodetype); } + delete nodetype->fixed_declaration; + /* Can be null when the type is not dynamically allocated. */ if (nodetype->free_self) { nodetype->free_self(nodetype); @@ -1391,6 +1393,14 @@ void nodeRegisterType(bNodeType *nt) BLI_assert(nt->idname[0] != '\0'); BLI_assert(nt->poll != nullptr); + if (nt->declare && !nt->declaration_is_dynamic) { + if (nt->fixed_declaration == nullptr) { + nt->fixed_declaration = new blender::nodes::NodeDeclaration(); + blender::nodes::NodeDeclarationBuilder builder{*nt->fixed_declaration}; + nt->declare(builder); + } + } + BLI_ghash_insert(nodetypes_hash, nt->idname, nt); /* XXX pass Main to register function? */ /* Probably not. It is pretty much expected we want to update G_MAIN here I think - @@ -2254,9 +2264,6 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree, *node_dst = *node_src; - /* Reset the declaration of the new node. */ - node_dst->declaration = nullptr; - /* can be called for nodes outside a node tree (e.g. clipboard) */ if (ntree) { if (unique_name) { @@ -2327,6 +2334,10 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree, ntree->update |= NTREE_UPDATE_NODES; } + /* Reset the declaration of the new node. */ + node_dst->declaration = nullptr; + nodeDeclarationEnsure(ntree, node_dst); + return node_dst; } @@ -3144,7 +3155,9 @@ static void node_free_node(bNodeTree *ntree, bNode *node) MEM_freeN(node->prop); } - delete node->declaration; + if (node->typeinfo->declaration_is_dynamic) { + delete node->declaration; + } MEM_freeN(node); @@ -3982,16 +3995,22 @@ int nodeSocketLinkLimit(const bNodeSocket *sock) */ void nodeDeclarationEnsure(bNodeTree *UNUSED(ntree), bNode *node) { - if (node->typeinfo->declare == nullptr) { - return; - } if (node->declaration != nullptr) { return; } - - node->declaration = new blender::nodes::NodeDeclaration(); - blender::nodes::NodeDeclarationBuilder builder{*node->declaration}; - node->typeinfo->declare(builder); + if (node->typeinfo->declare == nullptr) { + return; + } + if (node->typeinfo->declaration_is_dynamic) { + node->declaration = new blender::nodes::NodeDeclaration(); + blender::nodes::NodeDeclarationBuilder builder{*node->declaration}; + node->typeinfo->declare(builder); + } + else { + /* Declaration should have been created in #nodeRegisterType. */ + BLI_assert(node->typeinfo->fixed_declaration != nullptr); + node->declaration = node->typeinfo->fixed_declaration; + } } /* ************** Node Clipboard *********** */ From 729b2d026d1379de92908b16e7492a509721c796 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Mon, 18 Oct 2021 10:12:22 +0100 Subject: [PATCH 0884/1500] Geometry Nodes: Add shader Musgrave texture node Port shader node musgrave texture Differential Revision: https://developer.blender.org/D12701 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenlib/BLI_noise.hh | 99 +++ source/blender/blenlib/intern/noise.cc | 718 ++++++++++++++++++ .../shader/nodes/node_shader_tex_musgrave.cc | 410 +++++++++- 4 files changed, 1226 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 68dc7103f3a..8087f64c5ab 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -722,6 +722,7 @@ geometry_node_categories = [ ]), GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ NodeItem("ShaderNodeTexGradient"), + NodeItem("ShaderNodeTexMusgrave"), NodeItem("ShaderNodeTexNoise"), NodeItem("ShaderNodeTexVoronoi"), NodeItem("ShaderNodeTexWhiteNoise"), diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh index 93980e3569e..a7af69f42a9 100644 --- a/source/blender/blenlib/BLI_noise.hh +++ b/source/blender/blenlib/BLI_noise.hh @@ -112,6 +112,105 @@ float3 perlin_float3_fractal_distorted(float4 position, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Musgrave Multi Fractal + * \{ */ + +float musgrave_ridged_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_ridged_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_ridged_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_ridged_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); + +float musgrave_hybrid_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_hybrid_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_hybrid_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_hybrid_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); + +float musgrave_fBm(const float co, const float H, const float lacunarity, const float octaves); +float musgrave_fBm(const float2 co, const float H, const float lacunarity, const float octaves); +float musgrave_fBm(const float3 co, const float H, const float lacunarity, const float octaves); +float musgrave_fBm(const float4 co, const float H, const float lacunarity, const float octaves); + +float musgrave_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves); +float musgrave_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves); +float musgrave_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves); +float musgrave_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves); + +float musgrave_hetero_terrain(const float co, + const float H, + const float lacunarity, + const float octaves, + const float offset); +float musgrave_hetero_terrain(const float2 co, + const float H, + const float lacunarity, + const float octaves, + const float offset); +float musgrave_hetero_terrain(const float3 co, + const float H, + const float lacunarity, + const float octaves, + const float offset); +float musgrave_hetero_terrain(const float4 co, + const float H, + const float lacunarity, + const float octaves, + const float offset); + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Voronoi Noise * \{ */ diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index a6c3377b71f..812e6ddd181 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -756,6 +756,724 @@ float3 perlin_float3_fractal_distorted(float4 position, perlin_fractal(position + random_float4_offset(5.0f), octaves, roughness)); } +/* -------------- + * Musgrave Noise + * -------------- + */ + +/* 1D Musgrave fBm + * + * H: fractal increment parameter + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * + * from "Texturing and Modelling: A procedural approach" + */ + +float musgrave_fBm(const float co, const float H, const float lacunarity, const float octaves) +{ + float p = co; + float value = 0.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + for (int i = 0; i < (int)octaves; i++) { + value += perlin_signed(p) * pwr; + pwr *= pwHL; + p *= lacunarity; + } + + float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * perlin_signed(p) * pwr; + } + + return value; +} + +/* 1D Musgrave Multifractal + * + * H: highest fractal dimension + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + */ + +float musgrave_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves) +{ + float p = co; + float value = 1.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + for (int i = 0; i < (int)octaves; i++) { + value *= (pwr * perlin_signed(p) + 1.0f); + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */ + } + + return value; +} + +/* 1D Musgrave Heterogeneous Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hetero_terrain( + const float co, const float H, const float lacunarity, const float octaves, const float offset) +{ + float p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + /* first unscaled octave of function; later octaves are scaled */ + float value = offset + perlin_signed(p); + p *= lacunarity; + + for (int i = 1; i < (int)octaves; i++) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += increment; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += rmd * increment; + } + + return value; +} + +/* 1D Hybrid Additive/Multiplicative Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hybrid_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain) +{ + float p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float value = perlin_signed(p) + offset; + float weight = gain * value; + p *= lacunarity; + + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { + if (weight > 1.0f) { + weight = 1.0f; + } + + float signal = (perlin_signed(p) + offset) * pwr; + pwr *= pwHL; + value += weight * signal; + weight *= gain * signal; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * ((perlin_signed(p) + offset) * pwr); + } + + return value; +} + +/* 1D Ridged Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_ridged_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain) +{ + float p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + float value = signal; + float weight = 1.0f; + + for (int i = 1; i < (int)octaves; i++) { + p *= lacunarity; + weight = CLAMPIS(signal * gain, 0.0f, 1.0f); + signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + signal *= weight; + value += signal * pwr; + pwr *= pwHL; + } + + return value; +} + +/* 2D Musgrave fBm + * + * H: fractal increment parameter + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * + * from "Texturing and Modelling: A procedural approach" + */ + +float musgrave_fBm(const float2 co, const float H, const float lacunarity, const float octaves) +{ + float2 p = co; + float value = 0.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + for (int i = 0; i < (int)octaves; i++) { + value += perlin_signed(p) * pwr; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * perlin_signed(p) * pwr; + } + + return value; +} + +/* 2D Musgrave Multifractal + * + * H: highest fractal dimension + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + */ + +float musgrave_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves) +{ + float2 p = co; + float value = 1.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + for (int i = 0; i < (int)octaves; i++) { + value *= (pwr * perlin_signed(p) + 1.0f); + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */ + } + + return value; +} + +/* 2D Musgrave Heterogeneous Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hetero_terrain(const float2 co, + const float H, + const float lacunarity, + const float octaves, + const float offset) +{ + float2 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + /* first unscaled octave of function; later octaves are scaled */ + float value = offset + perlin_signed(p); + p *= lacunarity; + + for (int i = 1; i < (int)octaves; i++) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += increment; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += rmd * increment; + } + + return value; +} + +/* 2D Hybrid Additive/Multiplicative Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hybrid_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain) +{ + float2 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float value = perlin_signed(p) + offset; + float weight = gain * value; + p *= lacunarity; + + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { + if (weight > 1.0f) { + weight = 1.0f; + } + + float signal = (perlin_signed(p) + offset) * pwr; + pwr *= pwHL; + value += weight * signal; + weight *= gain * signal; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * ((perlin_signed(p) + offset) * pwr); + } + + return value; +} + +/* 2D Ridged Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_ridged_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain) +{ + float2 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + float value = signal; + float weight = 1.0f; + + for (int i = 1; i < (int)octaves; i++) { + p *= lacunarity; + weight = CLAMPIS(signal * gain, 0.0f, 1.0f); + signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + signal *= weight; + value += signal * pwr; + pwr *= pwHL; + } + + return value; +} + +/* 3D Musgrave fBm + * + * H: fractal increment parameter + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * + * from "Texturing and Modelling: A procedural approach" + */ + +float musgrave_fBm(const float3 co, const float H, const float lacunarity, const float octaves) +{ + float3 p = co; + float value = 0.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + for (int i = 0; i < (int)octaves; i++) { + value += perlin_signed(p) * pwr; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * perlin_signed(p) * pwr; + } + + return value; +} + +/* 3D Musgrave Multifractal + * + * H: highest fractal dimension + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + */ + +float musgrave_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves) +{ + float3 p = co; + float value = 1.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + for (int i = 0; i < (int)octaves; i++) { + value *= (pwr * perlin_signed(p) + 1.0f); + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */ + } + + return value; +} + +/* 3D Musgrave Heterogeneous Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hetero_terrain(const float3 co, + const float H, + const float lacunarity, + const float octaves, + const float offset) +{ + float3 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + /* first unscaled octave of function; later octaves are scaled */ + float value = offset + perlin_signed(p); + p *= lacunarity; + + for (int i = 1; i < (int)octaves; i++) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += increment; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += rmd * increment; + } + + return value; +} + +/* 3D Hybrid Additive/Multiplicative Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hybrid_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain) +{ + float3 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float value = perlin_signed(p) + offset; + float weight = gain * value; + p *= lacunarity; + + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { + if (weight > 1.0f) { + weight = 1.0f; + } + + float signal = (perlin_signed(p) + offset) * pwr; + pwr *= pwHL; + value += weight * signal; + weight *= gain * signal; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * ((perlin_signed(p) + offset) * pwr); + } + + return value; +} + +/* 3D Ridged Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_ridged_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain) +{ + float3 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + float value = signal; + float weight = 1.0f; + + for (int i = 1; i < (int)octaves; i++) { + p *= lacunarity; + weight = CLAMPIS(signal * gain, 0.0f, 1.0f); + signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + signal *= weight; + value += signal * pwr; + pwr *= pwHL; + } + + return value; +} + +/* 4D Musgrave fBm + * + * H: fractal increment parameter + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * + * from "Texturing and Modelling: A procedural approach" + */ + +float musgrave_fBm(const float4 co, const float H, const float lacunarity, const float octaves) +{ + float4 p = co; + float value = 0.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + for (int i = 0; i < (int)octaves; i++) { + value += perlin_signed(p) * pwr; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * perlin_signed(p) * pwr; + } + + return value; +} + +/* 4D Musgrave Multifractal + * + * H: highest fractal dimension + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + */ + +float musgrave_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves) +{ + float4 p = co; + float value = 1.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + for (int i = 0; i < (int)octaves; i++) { + value *= (pwr * perlin_signed(p) + 1.0f); + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */ + } + + return value; +} + +/* 4D Musgrave Heterogeneous Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hetero_terrain(const float4 co, + const float H, + const float lacunarity, + const float octaves, + const float offset) +{ + float4 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + /* first unscaled octave of function; later octaves are scaled */ + float value = offset + perlin_signed(p); + p *= lacunarity; + + for (int i = 1; i < (int)octaves; i++) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += increment; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += rmd * increment; + } + + return value; +} + +/* 4D Hybrid Additive/Multiplicative Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hybrid_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain) +{ + float4 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float value = perlin_signed(p) + offset; + float weight = gain * value; + p *= lacunarity; + + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { + if (weight > 1.0f) { + weight = 1.0f; + } + + float signal = (perlin_signed(p) + offset) * pwr; + pwr *= pwHL; + value += weight * signal; + weight *= gain * signal; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * ((perlin_signed(p) + offset) * pwr); + } + + return value; +} + +/* 4D Ridged Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_ridged_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain) +{ + float4 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + float value = signal; + float weight = 1.0f; + + for (int i = 1; i < (int)octaves; i++) { + p *= lacunarity; + weight = CLAMPIS(signal * gain, 0.0f, 1.0f); + signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + signal *= weight; + value += signal * pwr; + pwr *= pwHL; + } + + return value; +} + /* * Voronoi: Ported from Cycles code. * diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index 23f150d8135..61c26d07e2f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -19,12 +19,14 @@ #include "../node_shader_util.h" +#include "BLI_noise.hh" + namespace blender::nodes { static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").hide_value(); + b.add_input("Vector").hide_value().implicit_field(); b.add_input("W").min(-1000.0f).max(1000.0f); b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); b.add_input("Detail").min(0.0f).max(16.0f).default_value(2.0f); @@ -124,11 +126,414 @@ static void node_shader_update_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *nod node_sock_label(outFacSock, "Height"); } +namespace blender::nodes { + +class MusgraveFunction : public fn::MultiFunction { + private: + const int dimensions_; + const int musgrave_type_; + + public: + MusgraveFunction(const int dimensions, const int musgrave_type) + : dimensions_(dimensions), musgrave_type_(musgrave_type) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(musgrave_type >= 0 && musgrave_type <= 4); + static std::array signatures{ + create_signature(1, SHD_MUSGRAVE_MULTIFRACTAL), + create_signature(2, SHD_MUSGRAVE_MULTIFRACTAL), + create_signature(3, SHD_MUSGRAVE_MULTIFRACTAL), + create_signature(4, SHD_MUSGRAVE_MULTIFRACTAL), + + create_signature(1, SHD_MUSGRAVE_FBM), + create_signature(2, SHD_MUSGRAVE_FBM), + create_signature(3, SHD_MUSGRAVE_FBM), + create_signature(4, SHD_MUSGRAVE_FBM), + + create_signature(1, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + create_signature(2, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + create_signature(3, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + create_signature(4, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + + create_signature(1, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + create_signature(2, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + create_signature(3, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + create_signature(4, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + + create_signature(1, SHD_MUSGRAVE_HETERO_TERRAIN), + create_signature(2, SHD_MUSGRAVE_HETERO_TERRAIN), + create_signature(3, SHD_MUSGRAVE_HETERO_TERRAIN), + create_signature(4, SHD_MUSGRAVE_HETERO_TERRAIN), + }; + this->set_signature(&signatures[dimensions + musgrave_type * 4 - 1]); + } + + static fn::MFSignature create_signature(const int dimensions, const int musgrave_type) + { + fn::MFSignatureBuilder signature{"Musgrave"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + signature.single_input("Detail"); + signature.single_input("Dimension"); + signature.single_input("Lacunarity"); + if (ELEM(musgrave_type, + SHD_MUSGRAVE_RIDGED_MULTIFRACTAL, + SHD_MUSGRAVE_HYBRID_MULTIFRACTAL, + SHD_MUSGRAVE_HETERO_TERRAIN)) { + signature.single_input("Offset"); + } + if (ELEM(musgrave_type, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL)) { + signature.single_input("Gain"); + } + + signature.single_output("Fac"); + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_detail = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Detail"); + }; + auto get_dimension = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Dimension"); + }; + auto get_lacunarity = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Lacunarity"); + }; + auto get_offset = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Offset"); + }; + auto get_gain = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Gain"); + }; + + auto get_r_factor = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output_if_required(param_index, "Fac"); + }; + + int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4); + const VArray &scale = get_scale(param++); + const VArray &detail = get_detail(param++); + const VArray &dimension = get_dimension(param++); + const VArray &lacunarity = get_lacunarity(param++); + + switch (musgrave_type_) { + case SHD_MUSGRAVE_MULTIFRACTAL: { + MutableSpan r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 2: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 3: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 4: { + const VArray &vector = get_vector(0); + const VArray &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_RIDGED_MULTIFRACTAL: { + const VArray &offset = get_offset(param++); + const VArray &gain = get_gain(param++); + MutableSpan r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 2: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 3: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 4: { + const VArray &vector = get_vector(0); + const VArray &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_HYBRID_MULTIFRACTAL: { + const VArray &offset = get_offset(param++); + const VArray &gain = get_gain(param++); + MutableSpan r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 2: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 3: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 4: { + const VArray &vector = get_vector(0); + const VArray &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_FBM: { + MutableSpan r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 2: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 3: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 4: { + const VArray &vector = get_vector(0); + const VArray &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_HETERO_TERRAIN: { + const VArray &offset = get_offset(param++); + MutableSpan r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + case 2: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + case 3: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + case 4: { + const VArray &vector = get_vector(0); + const VArray &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + } + break; + } + } + } +}; // namespace blender::nodes + +static void sh_node_musgrave_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexMusgrave *tex = (NodeTexMusgrave *)node.storage; + builder.construct_and_set_matching_fn(tex->dimensions, tex->musgrave_type); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_musgrave(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_MUSGRAVE, "Musgrave Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_MUSGRAVE, "Musgrave Texture", NODE_CLASS_TEXTURE, 0); ntype.declare = blender::nodes::sh_node_tex_musgrave_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, node_shader_init_tex_musgrave); @@ -136,6 +541,7 @@ void register_node_type_sh_tex_musgrave(void) &ntype, "NodeTexMusgrave", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_musgrave); node_type_update(&ntype, node_shader_update_tex_musgrave); + ntype.build_multi_function = blender::nodes::sh_node_musgrave_build_multi_function; nodeRegisterType(&ntype); } From 3edae09eaaa6e4b7dca9bac4c98ad23edf9d08e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 18 Oct 2021 15:45:02 +0200 Subject: [PATCH 0885/1500] Asset Library Service: fix failing unit test On GCC in release mode (and maybe also debug mode without ASAN enabled), allocating an `AssetLibraryService` will reuse the space that should have just been freed. This made a test fail, as it was testing that new memory was allocated and not some old instance reused. To ensure that the calls that should allocate a new block of memory return a unique pointer, I added some dummy allocation to the test. No functional changes to Blender --- .../intern/asset_library_service_test.cc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index 6acbe193eb7..e2d7a7680b6 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -60,12 +60,21 @@ TEST_F(AssetLibraryServiceTest, get_destroy) << "Calling twice without destroying in between should return the same instance."; AssetLibraryService::destroy(); + + /* On GCC in release mode (and maybe also debug mode without ASAN enabled), allocating an + * #AssetLibraryService will reuse the space that should have just been freed in the above + * destroy() call. To see that the get() call below really allocates a new object, allocate a + * dummy block of memory first. */ + AssetLibraryService *dummy_pointer = new AssetLibraryService(); + EXPECT_NE(service, AssetLibraryService::get()) << "Calling twice with destroying in between should return a new instance."; /* This should not crash. */ AssetLibraryService::destroy(); AssetLibraryService::destroy(); + + delete dummy_pointer; } TEST_F(AssetLibraryServiceTest, library_pointers) @@ -80,11 +89,20 @@ TEST_F(AssetLibraryServiceTest, library_pointers) << "Calling twice without destroying in between should return the same instance."; AssetLibraryService::destroy(); + + /* On GCC in release mode (and maybe also debug mode without ASAN enabled), allocating an + * #AssetLibraryService will reuse the space that should have just been freed in the above + * destroy() call. To see that the get() call below really allocates a new object, allocate a + * dummy block of memory first. */ + AssetLibrary *dummy_pointer = new AssetLibrary(); + service = AssetLibraryService::get(); EXPECT_NE(lib, service->get_asset_library_on_disk(asset_library_root_)) << "Calling twice with destroying in between should return a new instance."; EXPECT_NE(curfile_lib, service->get_asset_library_current_file()) << "Calling twice with destroying in between should return a new instance."; + + delete dummy_pointer; } TEST_F(AssetLibraryServiceTest, catalogs_loaded) From ef7e21fd4a1cea9db1191bd8e00fcc6df105ab33 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Mon, 18 Oct 2021 16:16:34 +0200 Subject: [PATCH 0886/1500] UI: Reduce vertical margin between panels In an attempt to reduce scrolling. This can be re-visited if a kind of switch between "compact" and "comfortable" UI size is implemented in the future. --- source/blender/editors/include/UI_interface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index c808a42a44a..047340ac304 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -253,7 +253,7 @@ enum { #define UI_PANEL_CATEGORY_MARGIN_WIDTH (U.widget_unit * 1.0f) #define UI_PANEL_MARGIN_X (U.widget_unit * 0.4f) -#define UI_PANEL_MARGIN_Y (U.widget_unit * 0.2f) +#define UI_PANEL_MARGIN_Y (U.widget_unit * 0.1f) /* but->drawflag - these flags should only affect how the button is drawn. */ /* NOTE: currently, these flags *are not passed* to the widget's state() or draw() functions From dd689eeda4aad172d0e543f4b7bc44a87ef6e1c5 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 18 Oct 2021 16:17:56 +0200 Subject: [PATCH 0887/1500] Fix: dangling pointer caused use-after-free The old code only worked when built-in nodes are only unregistered at most once while Blender is running. However, this is not the case when running certain unit tests such as `AbstractHierarchy*` in `blender_test`. Found by Sybren, thanks. --- source/blender/blenkernel/intern/node.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 5a4849f1d05..c5fb9030847 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -1380,6 +1380,7 @@ static void node_free_type(void *nodetype_v) } delete nodetype->fixed_declaration; + nodetype->fixed_declaration = nullptr; /* Can be null when the type is not dynamically allocated. */ if (nodetype->free_self) { From b246f8141236712b1a08c9a5ee2feda9bf4e2527 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 18 Oct 2021 11:41:16 -0300 Subject: [PATCH 0888/1500] Fix T92311: Snap to faces in edit mode with x-ray enabled doesn't work The `use_occlusion_test` parameter test was accidentally removed in {rB91c33c8b9952} --- source/blender/editors/transform/transform_snap_object.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 17326001a99..dea8a7c6f03 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -1151,9 +1151,12 @@ static bool raycastObjects(SnapObjectContext *sctx, float r_obmat[4][4], ListBase *r_hit_list) { - if (v3d && (params->edit_mode_type == SNAP_GEOM_EDIT) && XRAY_FLAG_ENABLED(v3d)) { - /* Use of occlude geometry in editing mode disabled. */ - return false; + if (params->use_occlusion_test && v3d && XRAY_FLAG_ENABLED(v3d)) { + /* General testing of occlusion geometry is disabled if the snap is not intended for the edit + * cage. */ + if (params->edit_mode_type == SNAP_GEOM_EDIT) { + return false; + } } sctx->runtime.depsgraph = depsgraph; From fb88ff8f0c72be410c12a2f3b0226949d5041448 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Oct 2021 23:39:17 +0200 Subject: [PATCH 0889/1500] Cleanup: fix compiler warning --- intern/cycles/render/geometry.cpp | 2 +- intern/cycles/render/geometry.h | 2 +- intern/cycles/render/mesh_displace.cpp | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 0304f168187..5d89060c1a1 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -1813,7 +1813,7 @@ void GeometryManager::device_update(Device *device, if (geom->is_modified()) { if (geom->is_mesh()) { Mesh *mesh = static_cast(geom); - if (displace(device, dscene, scene, mesh, progress)) { + if (displace(device, scene, mesh, progress)) { displacement_done = true; } } diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h index cd42f62c669..5c45754ad90 100644 --- a/intern/cycles/render/geometry.h +++ b/intern/cycles/render/geometry.h @@ -219,7 +219,7 @@ class GeometryManager { void collect_statistics(const Scene *scene, RenderStats *stats); protected: - bool displace(Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress &progress); + bool displace(Device *device, Scene *scene, Mesh *mesh, Progress &progress); void create_volume_mesh(Volume *volume, Progress &progress); diff --git a/intern/cycles/render/mesh_displace.cpp b/intern/cycles/render/mesh_displace.cpp index a08874e6fa8..3a11f39d977 100644 --- a/intern/cycles/render/mesh_displace.cpp +++ b/intern/cycles/render/mesh_displace.cpp @@ -163,8 +163,7 @@ static void read_shader_output(const Scene *scene, } } -bool GeometryManager::displace( - Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress &progress) +bool GeometryManager::displace(Device *device, Scene *scene, Mesh *mesh, Progress &progress) { /* verify if we have a displacement shader */ if (!mesh->has_true_displacement()) { From 00710018790b9bbb18fe4fe588976b631c688a3b Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 18 Oct 2021 11:03:47 +0200 Subject: [PATCH 0890/1500] Fix T92246: sculpt crash displaying statistics in certain situations It seems possible to switch object selection (if `Lock Object Modes` is turned off) and end up with an object that has a SculptSession but a NULL PBVH. (I was not able to repro from scratch, but file from the report was clearly in that state). This would crash in displaying scene statistics. While there might be a deeper fix (making sure PBVH is available early enough -- possibly using `BKE_sculpt_object_pbvh_ensure`, `sculpt_update_object` or friends), there are also many checks in tools for PBVH, so the situation seems to be somewhat vaild/expected also in other places. So to fix this, just check for a non-NULL PBVH, returning early otherwise. Note: this leaves us with displaying 0/0 Faces & Vertices in the borked case until an operation takes place that updates the PBVH. Maniphest Tasks: T92246 Differential Revision: https://developer.blender.org/D12904 --- source/blender/editors/space_info/info_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index e749e1a7947..13d15bc50a6 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -372,7 +372,7 @@ static void stats_object_sculpt(const Object *ob, SceneStats *stats) SculptSession *ss = ob->sculpt; - if (!ss) { + if (ss == NULL || ss->pbvh == NULL) { return; } From aef8ac7db830a47950ca4115eff5d0d72a4d6fcf Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 18 Oct 2021 17:00:25 +0200 Subject: [PATCH 0891/1500] Cleanup: clang format --- source/blender/editors/animation/fmodifier_ui.c | 4 ++-- .../editors/space_spreadsheet/spreadsheet_row_filter_ui.cc | 2 +- .../blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index cea0e50a21f..105bb54cee3 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -187,7 +187,7 @@ static PanelType *fmodifier_panel_register(ARegionType *region_type, /* Give the panel the special flag that says it was built here and corresponds to a * modifier rather than a #PanelType. */ - panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; + panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; panel_type->reorder = fmodifier_reorder; panel_type->get_list_data_expand_flag = get_fmodifier_expand_flag; panel_type->set_list_data_expand_flag = set_fmodifier_expand_flag; @@ -221,7 +221,7 @@ static PanelType *fmodifier_subpanel_register(ARegionType *region_type, panel_type->draw_header = draw_header; panel_type->draw = draw; panel_type->poll = poll; - panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED ; + panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED; BLI_assert(parent != NULL); BLI_strncpy(panel_type->parent_id, parent->idname, BKE_ST_MAXNAME); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc index 78804172981..d56049990b4 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc @@ -336,7 +336,7 @@ void register_row_filter_panels(ARegionType ®ion_type) strcpy(panel_type->label, ""); strcpy(panel_type->category, "Filters"); strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); - panel_type->flag = PANEL_TYPE_INSTANCED | PANEL_TYPE_HEADER_EXPAND; + panel_type->flag = PANEL_TYPE_INSTANCED | PANEL_TYPE_HEADER_EXPAND; panel_type->draw_header = spreadsheet_filter_panel_draw_header; panel_type->draw = spreadsheet_filter_panel_draw; panel_type->get_list_data_expand_flag = get_filter_expand_flag; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c index 7dcf887bc45..2e241ea5848 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c @@ -380,7 +380,7 @@ PanelType *gpencil_modifier_panel_register(ARegionType *region_type, /* Give the panel the special flag that says it was built here and corresponds to a * modifier rather than a #PanelType. */ - panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; + panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; panel_type->reorder = gpencil_modifier_reorder; panel_type->get_list_data_expand_flag = get_gpencil_modifier_expand_flag; panel_type->set_list_data_expand_flag = set_gpencil_modifier_expand_flag; @@ -413,7 +413,7 @@ PanelType *gpencil_modifier_subpanel_register(ARegionType *region_type, panel_type->draw_header = draw_header; panel_type->draw = draw; panel_type->poll = gpencil_modifier_ui_poll; - panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED ; + panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED; BLI_assert(parent != NULL); BLI_strncpy(panel_type->parent_id, parent->idname, BKE_ST_MAXNAME); From 3cbe9218994aec59e417a595b9a1f7256108e693 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 18 Oct 2021 10:08:57 -0500 Subject: [PATCH 0892/1500] Cleanup: Use simpler method to create attribute lookups Instead of switch statements, make use of generic virtual arrays so the code is shorter and easier to read. Differential Revision: https://developer.blender.org/D12908 --- .../blenkernel/intern/attribute_access.cc | 43 +++++-------------- .../intern/attribute_access_intern.hh | 18 -------- 2 files changed, 11 insertions(+), 50 deletions(-) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d686cd8fa55..289c458c1e4 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -51,6 +51,7 @@ using blender::bke::OutputAttribute; using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray_For_GSpan; +using blender::fn::GVMutableArray_For_GMutableSpan; using blender::fn::GVArray_For_SingleValue; namespace blender::bke { @@ -388,23 +389,12 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } - const CustomDataType data_type = (CustomDataType)layer.type; - switch (data_type) { - case CD_PROP_FLOAT: - return this->layer_to_read_attribute(layer, domain_size); - case CD_PROP_FLOAT2: - return this->layer_to_read_attribute(layer, domain_size); - case CD_PROP_FLOAT3: - return this->layer_to_read_attribute(layer, domain_size); - case CD_PROP_INT32: - return this->layer_to_read_attribute(layer, domain_size); - case CD_PROP_COLOR: - return this->layer_to_read_attribute(layer, domain_size); - case CD_PROP_BOOL: - return this->layer_to_read_attribute(layer, domain_size); - default: - break; + const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type); + if (type == nullptr) { + continue; } + GSpan data{*type, layer.data, domain_size}; + return {std::make_unique(data), domain_}; } return {}; } @@ -429,23 +419,12 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( CustomData_duplicate_referenced_layer_anonymous( custom_data, layer.type, &attribute_id.anonymous_id(), domain_size); } - const CustomDataType data_type = (CustomDataType)layer.type; - switch (data_type) { - case CD_PROP_FLOAT: - return this->layer_to_write_attribute(layer, domain_size); - case CD_PROP_FLOAT2: - return this->layer_to_write_attribute(layer, domain_size); - case CD_PROP_FLOAT3: - return this->layer_to_write_attribute(layer, domain_size); - case CD_PROP_INT32: - return this->layer_to_write_attribute(layer, domain_size); - case CD_PROP_COLOR: - return this->layer_to_write_attribute(layer, domain_size); - case CD_PROP_BOOL: - return this->layer_to_write_attribute(layer, domain_size); - default: - break; + const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type); + if (type == nullptr) { + continue; } + GMutableSpan data{*type, layer.data, domain_size}; + return {std::make_unique(data), domain_}; } return {}; } diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 261cb26d4e5..6e5cdd6faba 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -177,24 +177,6 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { } private: - template - ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer, - const int domain_size) const - { - return {std::make_unique>( - Span(static_cast(layer.data), domain_size)), - domain_}; - } - - template - WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer, - const int domain_size) const - { - return {std::make_unique>( - MutableSpan(static_cast(layer.data), domain_size)), - domain_}; - } - bool type_is_supported(CustomDataType data_type) const { return ((1ULL << data_type) & supported_types_mask) != 0; From 46fe43feca6924dabde37b79cd6239bfbbeabeb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 18 Oct 2021 17:29:24 +0200 Subject: [PATCH 0893/1500] Fix failing `AssetLibraryService` test by removing test code Remove the code I had hoped to fix in rB3edae09e, the fix was unreliable. No functional changes to Blender. --- .../intern/asset_library_service_test.cc | 33 ++++--------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index e2d7a7680b6..b4bb1dc0895 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -59,22 +59,13 @@ TEST_F(AssetLibraryServiceTest, get_destroy) EXPECT_EQ(service, AssetLibraryService::get()) << "Calling twice without destroying in between should return the same instance."; - AssetLibraryService::destroy(); - - /* On GCC in release mode (and maybe also debug mode without ASAN enabled), allocating an - * #AssetLibraryService will reuse the space that should have just been freed in the above - * destroy() call. To see that the get() call below really allocates a new object, allocate a - * dummy block of memory first. */ - AssetLibraryService *dummy_pointer = new AssetLibraryService(); - - EXPECT_NE(service, AssetLibraryService::get()) - << "Calling twice with destroying in between should return a new instance."; - /* This should not crash. */ AssetLibraryService::destroy(); AssetLibraryService::destroy(); - delete dummy_pointer; + /* Note: there used to be a test for the opposite here, that after a call to + * AssetLibraryService::destroy() the above calls should return freshly allocated objects. This + * cannot be reliably tested by just pointer comparison, though. */ } TEST_F(AssetLibraryServiceTest, library_pointers) @@ -88,21 +79,9 @@ TEST_F(AssetLibraryServiceTest, library_pointers) EXPECT_EQ(curfile_lib, service->get_asset_library_current_file()) << "Calling twice without destroying in between should return the same instance."; - AssetLibraryService::destroy(); - - /* On GCC in release mode (and maybe also debug mode without ASAN enabled), allocating an - * #AssetLibraryService will reuse the space that should have just been freed in the above - * destroy() call. To see that the get() call below really allocates a new object, allocate a - * dummy block of memory first. */ - AssetLibrary *dummy_pointer = new AssetLibrary(); - - service = AssetLibraryService::get(); - EXPECT_NE(lib, service->get_asset_library_on_disk(asset_library_root_)) - << "Calling twice with destroying in between should return a new instance."; - EXPECT_NE(curfile_lib, service->get_asset_library_current_file()) - << "Calling twice with destroying in between should return a new instance."; - - delete dummy_pointer; + /* Note: there used to be a test for the opposite here, that after a call to + * AssetLibraryService::destroy() the above calls should return freshly allocated objects. This + * cannot be reliably tested by just pointer comparison, though. */ } TEST_F(AssetLibraryServiceTest, catalogs_loaded) From 2944f3e92b68301b75c133359dc3fe0891823bff Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 18 Oct 2021 13:32:15 -0300 Subject: [PATCH 0894/1500] Fix 1 frame delayed orientation in Placement Gizmo Error in {rB69d6222481b4} --- source/blender/editors/space_view3d/view3d_cursor_snap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 03aa9316a38..712e68501c5 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -659,7 +659,7 @@ static void cursor_snap_update(const bContext *C, orthogonalize_m3(omat, snap_data->plane_axis); if (orient_surface) { - cursor_poject_surface_normal(snap_data->face_nor, obmat, omat); + cursor_poject_surface_normal(face_nor, obmat, omat); } } From 44c3bb729be42d6d67eaf8918d7cbcb2ff0b315d Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 18 Oct 2021 13:42:06 -0300 Subject: [PATCH 0895/1500] Cleanup: use a common prefix in static functions This helps identify where the function came from. --- .../editors/space_view3d/view3d_cursor_snap.c | 122 +++++++++--------- 1 file changed, 64 insertions(+), 58 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 712e68501c5..5a7d8113528 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -86,9 +86,9 @@ typedef struct SnapCursorDataIntern { /** * Calculate a 3x3 orientation matrix from the surface under the cursor. */ -static void cursor_poject_surface_normal(const float normal[3], - const float obmat[4][4], - float r_mat[3][3]) +static void v3d_cursor_poject_surface_normal(const float normal[3], + const float obmat[4][4], + float r_mat[3][3]) { float mat[3][3]; copy_m3_m4(mat, obmat); @@ -121,7 +121,7 @@ static void cursor_poject_surface_normal(const float normal[3], * * \note This could be moved to a public function. */ -static bool cursor_snap_calc_incremental( +static bool v3d_cursor_snap_calc_incremental( Scene *scene, View3D *v3d, ARegion *region, const float co_relative[3], float co[3]) { const float grid_size = ED_view3d_grid_view_scale(scene, v3d, region, NULL); @@ -179,12 +179,12 @@ static bool mat3_align_axis_to_v3(float mat[3][3], const int axis_align, const f /** \name Drawings * \{ */ -static void cursor_plane_draw_grid(const int resolution, - const float scale, - const float scale_fade, - const float matrix[4][4], - const int plane_axis, - const float color[4]) +static void v3d_cursor_plane_draw_grid(const int resolution, + const float scale, + const float scale_fade, + const float matrix[4][4], + const int plane_axis, + const float color[4]) { BLI_assert(scale_fade <= scale); const int resolution_min = resolution - 1; @@ -274,9 +274,9 @@ static void cursor_plane_draw_grid(const int resolution, GPU_blend(GPU_BLEND_NONE); } -static void cursor_plane_draw(const RegionView3D *rv3d, - const int plane_axis, - const float matrix[4][4]) +static void v3d_cursor_plane_draw(const RegionView3D *rv3d, + const int plane_axis, + const float matrix[4][4]) { /* Draw */ float pixel_size; @@ -326,7 +326,7 @@ static void cursor_plane_draw(const RegionView3D *rv3d, float color[4] = {1, 1, 1, color_alpha}; color[3] *= square_f(1.0f - fac); if (color[3] > 0.0f) { - cursor_plane_draw_grid( + v3d_cursor_plane_draw_grid( lines * lines_subdiv, final_scale, final_scale_fade, matrix, plane_axis, color); } @@ -336,7 +336,7 @@ static void cursor_plane_draw(const RegionView3D *rv3d, lines = 1; final_scale = final_scale_fade; } - cursor_plane_draw_grid(lines, final_scale, final_scale_fade, matrix, plane_axis, color); + v3d_cursor_plane_draw_grid(lines, final_scale, final_scale_fade, matrix, plane_axis, color); } } @@ -437,10 +437,10 @@ void ED_view3d_cursor_snap_draw_util(RegionView3D *rv3d, * \{ */ /* Checks if the current event is different from the one captured in the last update. */ -static bool eventstate_has_changed(SnapCursorDataIntern *sdata_intern, - const wmWindowManager *wm, - const int x, - const int y) +static bool v3d_cursor_eventstate_has_changed(SnapCursorDataIntern *sdata_intern, + const wmWindowManager *wm, + const int x, + const int y) { V3DSnapCursorData *snap_data = (V3DSnapCursorData *)sdata_intern; if (wm && wm->winactive) { @@ -463,14 +463,17 @@ static bool eventstate_has_changed(SnapCursorDataIntern *sdata_intern, } /* Copies the current eventstate. */ -static void eventstate_save_xy(SnapCursorDataIntern *cursor_snap, const int x, const int y) +static void v3d_cursor_eventstate_save_xy(SnapCursorDataIntern *cursor_snap, + const int x, + const int y) { cursor_snap->last_eventstate.x = x; cursor_snap->last_eventstate.y = y; } #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK -static bool invert_snap(SnapCursorDataIntern *sdata_intern, const wmWindowManager *wm) +static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *sdata_intern, + const wmWindowManager *wm) { if (!wm || !wm->winactive) { return false; @@ -518,7 +521,7 @@ static bool invert_snap(SnapCursorDataIntern *sdata_intern, const wmWindowManage /** \name Update * \{ */ -static short cursor_snap_elements(V3DSnapCursorData *cursor_snap, Scene *scene) +static short v3d_cursor_snap_elements(V3DSnapCursorData *cursor_snap, Scene *scene) { short snap_elements = cursor_snap->snap_elem_force; if (!snap_elements) { @@ -527,24 +530,24 @@ static short cursor_snap_elements(V3DSnapCursorData *cursor_snap, Scene *scene) return snap_elements; } -static void wm_paint_cursor_snap_context_ensure(SnapCursorDataIntern *sdata_intern, Scene *scene) +static void v3d_cursor_snap_context_ensure(SnapCursorDataIntern *sdata_intern, Scene *scene) { if (sdata_intern->snap_context_v3d == NULL) { sdata_intern->snap_context_v3d = ED_transform_snap_object_context_create(scene, 0); } } -static void cursor_snap_update(const bContext *C, - wmWindowManager *wm, - Depsgraph *depsgraph, - Scene *scene, - ARegion *region, - View3D *v3d, - int x, - int y, - SnapCursorDataIntern *sdata_intern) +static void v3d_cursor_snap_update(const bContext *C, + wmWindowManager *wm, + Depsgraph *depsgraph, + Scene *scene, + ARegion *region, + View3D *v3d, + int x, + int y, + SnapCursorDataIntern *sdata_intern) { - wm_paint_cursor_snap_context_ensure(sdata_intern, scene); + v3d_cursor_snap_context_ensure(sdata_intern, scene); V3DSnapCursorData *snap_data = (V3DSnapCursorData *)sdata_intern; float co[3], no[3], face_nor[3], obmat[4][4], omat[3][3]; @@ -557,7 +560,7 @@ static void cursor_snap_update(const bContext *C, zero_v3(face_nor); unit_m3(omat); - ushort snap_elements = cursor_snap_elements(snap_data, scene); + ushort snap_elements = v3d_cursor_snap_elements(snap_data, scene); sdata_intern->snap_elem_hidden = 0; const bool draw_plane = sdata_intern->draw_plane; if (draw_plane && !(snap_elements & SCE_SNAP_MODE_FACE)) { @@ -568,7 +571,7 @@ static void cursor_snap_update(const bContext *C, snap_data->is_enabled = true; #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK if (!(snap_data->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) { - snap_data->is_snap_invert = invert_snap(sdata_intern, wm); + snap_data->is_snap_invert = v3d_cursor_is_snap_invert(sdata_intern, wm); const ToolSettings *ts = scene->toolsettings; if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) { @@ -659,7 +662,7 @@ static void cursor_snap_update(const bContext *C, orthogonalize_m3(omat, snap_data->plane_axis); if (orient_surface) { - cursor_poject_surface_normal(face_nor, obmat, omat); + v3d_cursor_poject_surface_normal(face_nor, obmat, omat); } } @@ -678,7 +681,7 @@ static void cursor_snap_update(const bContext *C, } if (snap_data->is_enabled && (snap_elements & SCE_SNAP_MODE_INCREMENT)) { - cursor_snap_calc_incremental(scene, v3d, region, snap_data->prevpoint, co); + v3d_cursor_snap_calc_incremental(scene, v3d, region, snap_data->prevpoint, co); } } else if (snap_elem == SCE_SNAP_MODE_VERTEX) { @@ -701,7 +704,7 @@ static void cursor_snap_update(const bContext *C, copy_m3_m3(snap_data->plane_omat, omat); - eventstate_save_xy(sdata_intern, x, y); + v3d_cursor_eventstate_save_xy(sdata_intern, x, y); } /** \} */ @@ -710,7 +713,7 @@ static void cursor_snap_update(const bContext *C, /** \name Callbacks * \{ */ -static bool cursor_snap_pool_fn(bContext *C) +static bool v3d_cursor_snap_pool_fn(bContext *C) { if (G.moving) { return false; @@ -735,7 +738,7 @@ static bool cursor_snap_pool_fn(bContext *C) return true; } -static void cursor_snap_draw_fn(bContext *C, int x, int y, void *customdata) +static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *customdata) { V3DSnapCursorData *snap_data = customdata; SnapCursorDataIntern *sdata_intern = customdata; @@ -744,11 +747,11 @@ static void cursor_snap_draw_fn(bContext *C, int x, int y, void *customdata) ARegion *region = CTX_wm_region(C); x -= region->winrct.xmin; y -= region->winrct.ymin; - if (eventstate_has_changed(sdata_intern, wm, x, y)) { + if (v3d_cursor_eventstate_has_changed(sdata_intern, wm, x, y)) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = DEG_get_input_scene(depsgraph); View3D *v3d = CTX_wm_view3d(C); - cursor_snap_update(C, wm, depsgraph, scene, region, v3d, x, y, sdata_intern); + v3d_cursor_snap_update(C, wm, depsgraph, scene, region, v3d, x, y, sdata_intern); } const bool draw_plane = sdata_intern->draw_plane; @@ -771,7 +774,7 @@ static void cursor_snap_draw_fn(bContext *C, int x, int y, void *customdata) copy_m4_m3(matrix, snap_data->plane_omat); copy_v3_v3(matrix[3], snap_data->loc); - cursor_plane_draw(rv3d, snap_data->plane_axis, matrix); + v3d_cursor_plane_draw(rv3d, snap_data->plane_axis, matrix); } if (snap_data->snap_elem && sdata_intern->draw_point) { @@ -804,7 +807,7 @@ V3DSnapCursorData *ED_view3d_cursor_snap_data_get(void) { LISTBASE_FOREACH_MUTABLE (wmWindowManager *, wm, &G.main->wm) { LISTBASE_FOREACH_MUTABLE (wmPaintCursor *, pc, &wm->paintcursors) { - if (pc->draw == cursor_snap_draw_fn) { + if (pc->draw == v3d_cursor_snap_draw_fn) { return (V3DSnapCursorData *)pc->customdata; } } @@ -812,7 +815,7 @@ V3DSnapCursorData *ED_view3d_cursor_snap_data_get(void) return NULL; } -static void wm_paint_cursor_snap_data_init(SnapCursorDataIntern *sdata_intern) +static void v3d_cursor_snap_data_init(SnapCursorDataIntern *sdata_intern) { #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK struct wmKeyConfig *keyconf = ((wmWindowManager *)G.main->wm.first)->defaultconf; @@ -830,15 +833,18 @@ static void wm_paint_cursor_snap_data_init(SnapCursorDataIntern *sdata_intern) snap_data->color_line[3] = 128; } -static SnapCursorDataIntern *wm_paint_cursor_snap_ensure(void) +static SnapCursorDataIntern *v3d_cursor_snap_ensure(void) { SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); if (!sdata_intern) { sdata_intern = MEM_callocN(sizeof(*sdata_intern), __func__); - wm_paint_cursor_snap_data_init(sdata_intern); + v3d_cursor_snap_data_init(sdata_intern); - struct wmPaintCursor *pc = WM_paint_cursor_activate( - SPACE_VIEW3D, RGN_TYPE_WINDOW, cursor_snap_pool_fn, cursor_snap_draw_fn, sdata_intern); + struct wmPaintCursor *pc = WM_paint_cursor_activate(SPACE_VIEW3D, + RGN_TYPE_WINDOW, + v3d_cursor_snap_pool_fn, + v3d_cursor_snap_draw_fn, + sdata_intern); sdata_intern->handle = pc; } return sdata_intern; @@ -846,17 +852,17 @@ static SnapCursorDataIntern *wm_paint_cursor_snap_ensure(void) void ED_view3d_cursor_snap_activate_point(void) { - SnapCursorDataIntern *sdata_intern = wm_paint_cursor_snap_ensure(); + SnapCursorDataIntern *sdata_intern = v3d_cursor_snap_ensure(); sdata_intern->draw_point = true; } void ED_view3d_cursor_snap_activate_plane(void) { - SnapCursorDataIntern *sdata_intern = wm_paint_cursor_snap_ensure(); + SnapCursorDataIntern *sdata_intern = v3d_cursor_snap_ensure(); sdata_intern->draw_plane = true; } -static void wm_paint_cursor_snap_free(SnapCursorDataIntern *sdata_intern) +static void v3d_cursor_snap_free(SnapCursorDataIntern *sdata_intern) { WM_paint_cursor_end(sdata_intern->handle); if (sdata_intern->snap_context_v3d) { @@ -878,7 +884,7 @@ void ED_view3d_cursor_snap_deactivate_point(void) return; } - wm_paint_cursor_snap_free(sdata_intern); + v3d_cursor_snap_free(sdata_intern); } void ED_view3d_cursor_snap_deactivate_plane(void) @@ -894,7 +900,7 @@ void ED_view3d_cursor_snap_deactivate_plane(void) return; } - wm_paint_cursor_snap_free(sdata_intern); + v3d_cursor_snap_free(sdata_intern); } void ED_view3d_cursor_snap_update(const bContext *C, @@ -906,18 +912,18 @@ void ED_view3d_cursor_snap_update(const bContext *C, sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); if (!sdata_intern) { sdata_intern = &stack; - wm_paint_cursor_snap_data_init(sdata_intern); + v3d_cursor_snap_data_init(sdata_intern); sdata_intern->draw_plane = true; } wmWindowManager *wm = CTX_wm_manager(C); - if (eventstate_has_changed(sdata_intern, wm, x, y)) { + if (v3d_cursor_eventstate_has_changed(sdata_intern, wm, x, y)) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = DEG_get_input_scene(depsgraph); ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); - cursor_snap_update(C, wm, depsgraph, scene, region, v3d, x, y, sdata_intern); + v3d_cursor_snap_update(C, wm, depsgraph, scene, region, v3d, x, y, sdata_intern); } if ((void *)snap_data != (void *)sdata_intern) { if ((sdata_intern == &stack) && sdata_intern->snap_context_v3d) { @@ -949,6 +955,6 @@ struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *sce if (!sdata_intern) { return NULL; } - wm_paint_cursor_snap_context_ensure(sdata_intern, scene); + v3d_cursor_snap_context_ensure(sdata_intern, scene); return sdata_intern->snap_context_v3d; } From 1df3b51988852fa8ee6b530a64aa23346db9acd4 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 17 Oct 2021 16:10:10 +0200 Subject: [PATCH 0896/1500] Cycles: replace integrator state argument macros * Rename struct KernelGlobals to struct KernelGlobalsCPU * Add KernelGlobals, IntegratorState and ConstIntegratorState typedefs that every device can define in its own way. * Remove INTEGRATOR_STATE_ARGS and INTEGRATOR_STATE_PASS macros and replace with these new typedefs. * Add explicit state argument to INTEGRATOR_STATE and similar macros In preparation for decoupling main and shadow paths. Differential Revision: https://developer.blender.org/D12888 --- intern/cycles/bvh/bvh_embree.cpp | 2 +- intern/cycles/device/cpu/device_impl.h | 2 +- intern/cycles/device/cpu/kernel.h | 20 +- .../device/cpu/kernel_thread_globals.cpp | 8 +- .../cycles/device/cpu/kernel_thread_globals.h | 8 +- .../cycles/integrator/path_trace_work_cpu.cpp | 4 +- .../cycles/integrator/path_trace_work_cpu.h | 4 +- intern/cycles/integrator/shader_eval.cpp | 2 +- intern/cycles/kernel/bvh/bvh.h | 10 +- intern/cycles/kernel/bvh/bvh_embree.h | 8 +- intern/cycles/kernel/bvh/bvh_local.h | 4 +- intern/cycles/kernel/bvh/bvh_nodes.h | 10 +- intern/cycles/kernel/bvh/bvh_shadow_all.h | 4 +- intern/cycles/kernel/bvh/bvh_traversal.h | 4 +- intern/cycles/kernel/bvh/bvh_util.h | 20 +- intern/cycles/kernel/bvh/bvh_volume.h | 4 +- intern/cycles/kernel/bvh/bvh_volume_all.h | 4 +- intern/cycles/kernel/closure/bsdf.h | 8 +- .../kernel/closure/bsdf_hair_principled.h | 8 +- .../cycles/kernel/closure/bsdf_microfacet.h | 8 +- .../kernel/closure/bsdf_microfacet_multi.h | 4 +- intern/cycles/kernel/device/cpu/globals.h | 6 +- intern/cycles/kernel/device/cpu/image.h | 4 +- intern/cycles/kernel/device/cpu/kernel.cpp | 4 +- intern/cycles/kernel/device/cpu/kernel.h | 14 +- intern/cycles/kernel/device/cpu/kernel_arch.h | 20 +- .../kernel/device/cpu/kernel_arch_impl.h | 20 +- intern/cycles/kernel/device/cuda/globals.h | 3 +- intern/cycles/kernel/device/gpu/image.h | 4 +- intern/cycles/kernel/device/gpu/kernel.h | 24 +-- intern/cycles/kernel/device/optix/globals.h | 3 +- intern/cycles/kernel/geom/geom_attribute.h | 12 +- intern/cycles/kernel/geom/geom_curve.h | 17 +- .../cycles/kernel/geom/geom_curve_intersect.h | 4 +- intern/cycles/kernel/geom/geom_motion_curve.h | 15 +- .../cycles/kernel/geom/geom_motion_triangle.h | 15 +- .../geom/geom_motion_triangle_intersect.h | 8 +- .../kernel/geom/geom_motion_triangle_shader.h | 2 +- intern/cycles/kernel/geom/geom_object.h | 89 ++++---- intern/cycles/kernel/geom/geom_patch.h | 18 +- intern/cycles/kernel/geom/geom_primitive.h | 32 ++- intern/cycles/kernel/geom/geom_shader_data.h | 13 +- .../cycles/kernel/geom/geom_subd_triangle.h | 24 +-- intern/cycles/kernel/geom/geom_triangle.h | 29 ++- .../kernel/geom/geom_triangle_intersect.h | 8 +- intern/cycles/kernel/geom/geom_volume.h | 4 +- .../integrator/integrator_init_from_bake.h | 19 +- .../integrator/integrator_init_from_camera.h | 15 +- .../integrator/integrator_intersect_closest.h | 58 +++--- .../integrator/integrator_intersect_shadow.h | 42 ++-- .../integrator_intersect_subsurface.h | 4 +- .../integrator_intersect_volume_stack.h | 25 +-- .../kernel/integrator/integrator_megakernel.h | 31 +-- .../integrator/integrator_shade_background.h | 74 +++---- .../integrator/integrator_shade_light.h | 38 ++-- .../integrator/integrator_shade_shadow.h | 72 ++++--- .../integrator/integrator_shade_surface.h | 196 +++++++++--------- .../integrator/integrator_shade_volume.h | 158 +++++++------- .../kernel/integrator/integrator_state.h | 69 +++--- .../kernel/integrator/integrator_state_flow.h | 41 ++-- .../kernel/integrator/integrator_state_util.h | 170 +++++++-------- .../kernel/integrator/integrator_subsurface.h | 62 +++--- .../integrator/integrator_subsurface_disk.h | 17 +- .../integrator_subsurface_random_walk.h | 25 +-- .../integrator/integrator_volume_stack.h | 37 ++-- intern/cycles/kernel/kernel_accumulate.h | 150 +++++++------- .../cycles/kernel/kernel_adaptive_sampling.h | 11 +- intern/cycles/kernel/kernel_bake.h | 8 +- intern/cycles/kernel/kernel_camera.h | 16 +- intern/cycles/kernel/kernel_color.h | 4 +- intern/cycles/kernel/kernel_emission.h | 19 +- intern/cycles/kernel/kernel_id_passes.h | 2 +- intern/cycles/kernel/kernel_jitter.h | 7 +- intern/cycles/kernel/kernel_light.h | 46 ++-- .../cycles/kernel/kernel_light_background.h | 31 +-- intern/cycles/kernel/kernel_light_common.h | 5 +- intern/cycles/kernel/kernel_lookup_table.h | 7 +- intern/cycles/kernel/kernel_passes.h | 59 +++--- intern/cycles/kernel/kernel_path_state.h | 157 +++++++------- intern/cycles/kernel/kernel_random.h | 8 +- intern/cycles/kernel/kernel_shader.h | 90 ++++---- intern/cycles/kernel/kernel_shadow_catcher.h | 30 +-- intern/cycles/kernel/kernel_types.h | 56 +++-- intern/cycles/kernel/osl/osl_closures.cpp | 2 +- intern/cycles/kernel/osl/osl_services.cpp | 38 ++-- intern/cycles/kernel/osl/osl_services.h | 6 +- intern/cycles/kernel/osl/osl_shader.cpp | 16 +- intern/cycles/kernel/osl/osl_shader.h | 16 +- intern/cycles/kernel/svm/svm.h | 86 +++++--- intern/cycles/kernel/svm/svm_ao.h | 18 +- intern/cycles/kernel/svm/svm_aov.h | 18 +- intern/cycles/kernel/svm/svm_attribute.h | 11 +- intern/cycles/kernel/svm/svm_bevel.h | 26 ++- intern/cycles/kernel/svm/svm_blackbody.h | 2 +- intern/cycles/kernel/svm/svm_brick.h | 7 +- intern/cycles/kernel/svm/svm_bump.h | 4 +- intern/cycles/kernel/svm/svm_camera.h | 2 +- intern/cycles/kernel/svm/svm_checker.h | 2 +- intern/cycles/kernel/svm/svm_clamp.h | 2 +- intern/cycles/kernel/svm/svm_closure.h | 40 ++-- intern/cycles/kernel/svm/svm_convert.h | 2 +- intern/cycles/kernel/svm/svm_displace.h | 13 +- intern/cycles/kernel/svm/svm_geometry.h | 12 +- intern/cycles/kernel/svm/svm_hsv.h | 2 +- intern/cycles/kernel/svm/svm_ies.h | 9 +- intern/cycles/kernel/svm/svm_image.h | 14 +- intern/cycles/kernel/svm/svm_light_path.h | 25 ++- intern/cycles/kernel/svm/svm_magic.h | 7 +- intern/cycles/kernel/svm/svm_map_range.h | 2 +- intern/cycles/kernel/svm/svm_mapping.h | 6 +- intern/cycles/kernel/svm/svm_math.h | 4 +- intern/cycles/kernel/svm/svm_mix.h | 2 +- intern/cycles/kernel/svm/svm_musgrave.h | 2 +- intern/cycles/kernel/svm/svm_noisetex.h | 2 +- intern/cycles/kernel/svm/svm_normal.h | 2 +- intern/cycles/kernel/svm/svm_ramp.h | 39 +--- intern/cycles/kernel/svm/svm_sepcomb_hsv.h | 4 +- intern/cycles/kernel/svm/svm_sky.h | 13 +- intern/cycles/kernel/svm/svm_tex_coord.h | 10 +- intern/cycles/kernel/svm/svm_value.h | 4 +- .../cycles/kernel/svm/svm_vector_transform.h | 2 +- intern/cycles/kernel/svm/svm_vertex_color.h | 6 +- intern/cycles/kernel/svm/svm_voronoi.h | 11 +- intern/cycles/kernel/svm/svm_voxel.h | 7 +- intern/cycles/kernel/svm/svm_wave.h | 7 +- intern/cycles/kernel/svm/svm_wavelength.h | 2 +- intern/cycles/kernel/svm/svm_white_noise.h | 2 +- intern/cycles/kernel/svm/svm_wireframe.h | 4 +- 128 files changed, 1467 insertions(+), 1441 deletions(-) diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index 76fcdf539ea..343d62dedf4 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -73,7 +73,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) const RTCRay *ray = (RTCRay *)args->ray; RTCHit *hit = (RTCHit *)args->hit; CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt; - const KernelGlobals *kg = ctx->kg; + const KernelGlobalsCPU *kg = ctx->kg; switch (ctx->type) { case CCLIntersectContext::RAY_SHADOW_ALL: { diff --git a/intern/cycles/device/cpu/device_impl.h b/intern/cycles/device/cpu/device_impl.h index 371d2258104..944c61e29f7 100644 --- a/intern/cycles/device/cpu/device_impl.h +++ b/intern/cycles/device/cpu/device_impl.h @@ -44,7 +44,7 @@ CCL_NAMESPACE_BEGIN class CPUDevice : public Device { public: - KernelGlobals kernel_globals; + KernelGlobalsCPU kernel_globals; device_vector texture_info; bool need_texture_info; diff --git a/intern/cycles/device/cpu/kernel.h b/intern/cycles/device/cpu/kernel.h index b5f0d873f30..2db09057e44 100644 --- a/intern/cycles/device/cpu/kernel.h +++ b/intern/cycles/device/cpu/kernel.h @@ -21,7 +21,7 @@ CCL_NAMESPACE_BEGIN -struct KernelGlobals; +struct KernelGlobalsCPU; struct IntegratorStateCPU; struct TileInfo; @@ -30,10 +30,10 @@ class CPUKernels { /* Integrator. */ using IntegratorFunction = - CPUKernelFunction; + CPUKernelFunction; using IntegratorShadeFunction = CPUKernelFunction; - using IntegratorInitFunction = CPUKernelFunction; + using IntegratorInitFunction = CPUKernelFunction; @@ -54,7 +54,7 @@ class CPUKernels { /* Shader evaluation. */ using ShaderEvalFunction = CPUKernelFunction; + const KernelGlobalsCPU *kg, const KernelShaderEvalInput *, float *, const int)>; ShaderEvalFunction shader_eval_displace; ShaderEvalFunction shader_eval_background; @@ -62,7 +62,7 @@ class CPUKernels { /* Adaptive stopping. */ using AdaptiveSamplingConvergenceCheckFunction = - CPUKernelFunction; using AdaptiveSamplingFilterXFunction = - CPUKernelFunction; using AdaptiveSamplingFilterYFunction = - CPUKernelFunction; + const KernelGlobalsCPU *kg, ccl_global float *render_buffer, int pixel_index)>; CryptomattePostprocessFunction cryptomatte_postprocess; /* Bake. */ - CPUKernelFunction bake; + CPUKernelFunction bake; CPUKernels(); }; diff --git a/intern/cycles/device/cpu/kernel_thread_globals.cpp b/intern/cycles/device/cpu/kernel_thread_globals.cpp index 988b00cd1f0..44735beb88d 100644 --- a/intern/cycles/device/cpu/kernel_thread_globals.cpp +++ b/intern/cycles/device/cpu/kernel_thread_globals.cpp @@ -25,10 +25,10 @@ CCL_NAMESPACE_BEGIN -CPUKernelThreadGlobals::CPUKernelThreadGlobals(const KernelGlobals &kernel_globals, +CPUKernelThreadGlobals::CPUKernelThreadGlobals(const KernelGlobalsCPU &kernel_globals, void *osl_globals_memory, Profiler &cpu_profiler) - : KernelGlobals(kernel_globals), cpu_profiler_(cpu_profiler) + : KernelGlobalsCPU(kernel_globals), cpu_profiler_(cpu_profiler) { reset_runtime_memory(); @@ -40,7 +40,7 @@ CPUKernelThreadGlobals::CPUKernelThreadGlobals(const KernelGlobals &kernel_globa } CPUKernelThreadGlobals::CPUKernelThreadGlobals(CPUKernelThreadGlobals &&other) noexcept - : KernelGlobals(std::move(other)), cpu_profiler_(other.cpu_profiler_) + : KernelGlobalsCPU(std::move(other)), cpu_profiler_(other.cpu_profiler_) { other.reset_runtime_memory(); } @@ -58,7 +58,7 @@ CPUKernelThreadGlobals &CPUKernelThreadGlobals::operator=(CPUKernelThreadGlobals return *this; } - *static_cast(this) = *static_cast(&other); + *static_cast(this) = *static_cast(&other); other.reset_runtime_memory(); diff --git a/intern/cycles/device/cpu/kernel_thread_globals.h b/intern/cycles/device/cpu/kernel_thread_globals.h index d005c3bb56c..5aeeaf678d0 100644 --- a/intern/cycles/device/cpu/kernel_thread_globals.h +++ b/intern/cycles/device/cpu/kernel_thread_globals.h @@ -23,17 +23,17 @@ CCL_NAMESPACE_BEGIN class Profiler; -/* A special class which extends memory ownership of the `KernelGlobals` decoupling any resource +/* A special class which extends memory ownership of the `KernelGlobalsCPU` decoupling any resource * which is not thread-safe for access. Every worker thread which needs to operate on - * `KernelGlobals` needs to initialize its own copy of this object. + * `KernelGlobalsCPU` needs to initialize its own copy of this object. * * NOTE: Only minimal subset of objects are copied: `KernelData` is never copied. This means that * there is no unnecessary data duplication happening when using this object. */ -class CPUKernelThreadGlobals : public KernelGlobals { +class CPUKernelThreadGlobals : public KernelGlobalsCPU { public: /* TODO(sergey): Would be nice to have properly typed OSLGlobals even in the case when building * without OSL support. Will avoid need to those unnamed pointers and casts. */ - CPUKernelThreadGlobals(const KernelGlobals &kernel_globals, + CPUKernelThreadGlobals(const KernelGlobalsCPU &kernel_globals, void *osl_globals_memory, Profiler &cpu_profiler); diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp index 18a5365453d..1cadcd9ec5c 100644 --- a/intern/cycles/integrator/path_trace_work_cpu.cpp +++ b/intern/cycles/integrator/path_trace_work_cpu.cpp @@ -114,7 +114,7 @@ void PathTraceWorkCPU::render_samples(RenderStatistics &statistics, statistics.occupancy = 1.0f; } -void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_globals, +void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobalsCPU *kernel_globals, const KernelWorkTile &work_tile, const int samples_num) { @@ -127,7 +127,7 @@ void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_global if (device_scene_->data.integrator.has_shadow_catcher) { shadow_catcher_state = &integrator_states[1]; - path_state_init_queues(kernel_globals, shadow_catcher_state); + path_state_init_queues(shadow_catcher_state); } KernelWorkTile sample_work_tile = work_tile; diff --git a/intern/cycles/integrator/path_trace_work_cpu.h b/intern/cycles/integrator/path_trace_work_cpu.h index d011e8d05bd..91c024f4e4a 100644 --- a/intern/cycles/integrator/path_trace_work_cpu.h +++ b/intern/cycles/integrator/path_trace_work_cpu.h @@ -28,7 +28,7 @@ CCL_NAMESPACE_BEGIN struct KernelWorkTile; -struct KernelGlobals; +struct KernelGlobalsCPU; class CPUKernels; @@ -64,7 +64,7 @@ class PathTraceWorkCPU : public PathTraceWork { protected: /* Core path tracing routine. Renders given work time on the given queue. */ - void render_samples_full_pipeline(KernelGlobals *kernel_globals, + void render_samples_full_pipeline(KernelGlobalsCPU *kernel_globals, const KernelWorkTile &work_tile, const int samples_num); diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp index 53546c03872..cfc30056f7d 100644 --- a/intern/cycles/integrator/shader_eval.cpp +++ b/intern/cycles/integrator/shader_eval.cpp @@ -113,7 +113,7 @@ bool ShaderEval::eval_cpu(Device *device, } const int thread_index = tbb::this_task_arena::current_thread_index(); - KernelGlobals *kg = &kernel_thread_globals[thread_index]; + const KernelGlobalsCPU *kg = &kernel_thread_globals[thread_index]; switch (type) { case SHADER_EVAL_DISPLACE: diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index a501cbe7a4b..bdbd574bf0f 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -154,7 +154,7 @@ ccl_device_inline bool scene_intersect_valid(ccl_private const Ray *ray) return isfinite_safe(ray->P.x) && isfinite_safe(ray->D.x) && len_squared(ray->D) != 0.0f; } -ccl_device_intersect bool scene_intersect(ccl_global const KernelGlobals *kg, +ccl_device_intersect bool scene_intersect(KernelGlobals kg, ccl_private const Ray *ray, const uint visibility, ccl_private Intersection *isect) @@ -248,7 +248,7 @@ ccl_device_intersect bool scene_intersect(ccl_global const KernelGlobals *kg, } #ifdef __BVH_LOCAL__ -ccl_device_intersect bool scene_intersect_local(ccl_global const KernelGlobals *kg, +ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, ccl_private const Ray *ray, ccl_private LocalIntersection *local_isect, int local_object, @@ -360,7 +360,7 @@ ccl_device_intersect bool scene_intersect_local(ccl_global const KernelGlobals * #endif #ifdef __SHADOW_RECORD_ALL__ -ccl_device_intersect bool scene_intersect_shadow_all(ccl_global const KernelGlobals *kg, +ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, ccl_private const Ray *ray, ccl_private Intersection *isect, uint visibility, @@ -448,7 +448,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(ccl_global const KernelGlob #endif /* __SHADOW_RECORD_ALL__ */ #ifdef __VOLUME__ -ccl_device_intersect bool scene_intersect_volume(ccl_global const KernelGlobals *kg, +ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, ccl_private const Ray *ray, ccl_private Intersection *isect, const uint visibility) @@ -510,7 +510,7 @@ ccl_device_intersect bool scene_intersect_volume(ccl_global const KernelGlobals #endif /* __VOLUME__ */ #ifdef __VOLUME_RECORD_ALL__ -ccl_device_intersect uint scene_intersect_volume_all(ccl_global const KernelGlobals *kg, +ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals kg, ccl_private const Ray *ray, ccl_private Intersection *isect, const uint max_hits, diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/bvh_embree.h index d3db6295ea5..7fa0cfdc510 100644 --- a/intern/cycles/kernel/bvh/bvh_embree.h +++ b/intern/cycles/kernel/bvh/bvh_embree.h @@ -35,7 +35,7 @@ struct CCLIntersectContext { RAY_VOLUME_ALL = 4, } RayType; - const KernelGlobals *kg; + KernelGlobals kg; RayType type; /* for shadow rays */ @@ -50,7 +50,7 @@ struct CCLIntersectContext { int local_object_id; uint *lcg_state; - CCLIntersectContext(const KernelGlobals *kg_, RayType type_) + CCLIntersectContext(KernelGlobals kg_, RayType type_) { kg = kg_; type = type_; @@ -101,7 +101,7 @@ ccl_device_inline void kernel_embree_setup_rayhit(const Ray &ray, rayhit.hit.primID = RTC_INVALID_GEOMETRY_ID; } -ccl_device_inline void kernel_embree_convert_hit(const KernelGlobals *kg, +ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg, const RTCRay *ray, const RTCHit *hit, Intersection *isect) @@ -137,7 +137,7 @@ ccl_device_inline void kernel_embree_convert_hit(const KernelGlobals *kg, } ccl_device_inline void kernel_embree_convert_sss_hit( - const KernelGlobals *kg, const RTCRay *ray, const RTCHit *hit, Intersection *isect, int object) + KernelGlobals kg, const RTCRay *ray, const RTCHit *hit, Intersection *isect, int object) { isect->u = 1.0f - hit->v - hit->u; isect->v = hit->u; diff --git a/intern/cycles/kernel/bvh/bvh_local.h b/intern/cycles/kernel/bvh/bvh_local.h index 78ad4a34da9..79cde69699e 100644 --- a/intern/cycles/kernel/bvh/bvh_local.h +++ b/intern/cycles/kernel/bvh/bvh_local.h @@ -36,7 +36,7 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlobals *kg, + bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, ccl_private const Ray *ray, ccl_private LocalIntersection *local_isect, int local_object, @@ -196,7 +196,7 @@ ccl_device_inline return false; } -ccl_device_inline bool BVH_FUNCTION_NAME(ccl_global const KernelGlobals *kg, +ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg, ccl_private const Ray *ray, ccl_private LocalIntersection *local_isect, int local_object, diff --git a/intern/cycles/kernel/bvh/bvh_nodes.h b/intern/cycles/kernel/bvh/bvh_nodes.h index 49b37f39671..71122085f69 100644 --- a/intern/cycles/kernel/bvh/bvh_nodes.h +++ b/intern/cycles/kernel/bvh/bvh_nodes.h @@ -16,7 +16,7 @@ // TODO(sergey): Look into avoid use of full Transform and use 3x3 matrix and // 3-vector which might be faster. -ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(ccl_global const KernelGlobals *kg, +ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals kg, int node_addr, int child) { @@ -28,7 +28,7 @@ ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(ccl_global const return space; } -ccl_device_forceinline int bvh_aligned_node_intersect(ccl_global const KernelGlobals *kg, +ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg, const float3 P, const float3 idir, const float t, @@ -76,7 +76,7 @@ ccl_device_forceinline int bvh_aligned_node_intersect(ccl_global const KernelGlo #endif } -ccl_device_forceinline bool bvh_unaligned_node_intersect_child(ccl_global const KernelGlobals *kg, +ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals kg, const float3 P, const float3 dir, const float t, @@ -102,7 +102,7 @@ ccl_device_forceinline bool bvh_unaligned_node_intersect_child(ccl_global const return tnear <= tfar; } -ccl_device_forceinline int bvh_unaligned_node_intersect(ccl_global const KernelGlobals *kg, +ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals kg, const float3 P, const float3 dir, const float3 idir, @@ -134,7 +134,7 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(ccl_global const KernelG return mask; } -ccl_device_forceinline int bvh_node_intersect(ccl_global const KernelGlobals *kg, +ccl_device_forceinline int bvh_node_intersect(KernelGlobals kg, const float3 P, const float3 dir, const float3 idir, diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index 7e2edd2684c..42ab9eda37e 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -36,7 +36,7 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlobals *kg, + bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, ccl_private const Ray *ray, ccl_private Intersection *isect_array, const uint visibility, @@ -298,7 +298,7 @@ ccl_device_inline return false; } -ccl_device_inline bool BVH_FUNCTION_NAME(ccl_global const KernelGlobals *kg, +ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg, ccl_private const Ray *ray, ccl_private Intersection *isect_array, const uint visibility, diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/bvh_traversal.h index 9f271a4730c..1c17ebf767f 100644 --- a/intern/cycles/kernel/bvh/bvh_traversal.h +++ b/intern/cycles/kernel/bvh/bvh_traversal.h @@ -31,7 +31,7 @@ * BVH_MOTION: motion blur rendering */ -ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlobals *kg, +ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, ccl_private const Ray *ray, ccl_private Intersection *isect, const uint visibility) @@ -228,7 +228,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlob return (isect->prim != PRIM_NONE); } -ccl_device_inline bool BVH_FUNCTION_NAME(ccl_global const KernelGlobals *kg, +ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg, ccl_private const Ray *ray, ccl_private Intersection *isect, const uint visibility) diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index 31aae389da0..d45eeec4815 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -139,8 +139,9 @@ ccl_device_inline void sort_intersections_and_normals(ccl_private Intersection * /* Utility to quickly get flags from an intersection. */ -ccl_device_forceinline int intersection_get_shader_flags( - ccl_global const KernelGlobals *ccl_restrict kg, const int prim, const int type) +ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals kg, + const int prim, + const int type) { int shader = 0; @@ -159,8 +160,9 @@ ccl_device_forceinline int intersection_get_shader_flags( return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; } -ccl_device_forceinline int intersection_get_shader_from_isect_prim( - ccl_global const KernelGlobals *ccl_restrict kg, const int prim, const int isect_type) +ccl_device_forceinline int intersection_get_shader_from_isect_prim(KernelGlobals kg, + const int prim, + const int isect_type) { int shader = 0; @@ -179,23 +181,21 @@ ccl_device_forceinline int intersection_get_shader_from_isect_prim( return shader & SHADER_MASK; } -ccl_device_forceinline int intersection_get_shader(ccl_global const KernelGlobals *ccl_restrict kg, - ccl_private const Intersection *ccl_restrict - isect) +ccl_device_forceinline int intersection_get_shader( + KernelGlobals kg, ccl_private const Intersection *ccl_restrict isect) { return intersection_get_shader_from_isect_prim(kg, isect->prim, isect->type); } ccl_device_forceinline int intersection_get_object_flags( - ccl_global const KernelGlobals *ccl_restrict kg, - ccl_private const Intersection *ccl_restrict isect) + KernelGlobals kg, ccl_private const Intersection *ccl_restrict isect) { return kernel_tex_fetch(__object_flag, isect->object); } /* TODO: find a better (faster) solution for this. Maybe store offset per object for * attributes needed in intersection? */ -ccl_device_inline int intersection_find_attribute(ccl_global const KernelGlobals *kg, +ccl_device_inline int intersection_find_attribute(KernelGlobals kg, const int object, const uint id) { diff --git a/intern/cycles/kernel/bvh/bvh_volume.h b/intern/cycles/kernel/bvh/bvh_volume.h index d3bfce2d96b..fa56bd02bef 100644 --- a/intern/cycles/kernel/bvh/bvh_volume.h +++ b/intern/cycles/kernel/bvh/bvh_volume.h @@ -35,7 +35,7 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlobals *kg, + bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, ccl_private const Ray *ray, ccl_private Intersection *isect, const uint visibility) @@ -221,7 +221,7 @@ ccl_device_inline return (isect->prim != PRIM_NONE); } -ccl_device_inline bool BVH_FUNCTION_NAME(ccl_global const KernelGlobals *kg, +ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg, ccl_private const Ray *ray, ccl_private Intersection *isect, const uint visibility) diff --git a/intern/cycles/kernel/bvh/bvh_volume_all.h b/intern/cycles/kernel/bvh/bvh_volume_all.h index f0fe95924cf..1d7d942e736 100644 --- a/intern/cycles/kernel/bvh/bvh_volume_all.h +++ b/intern/cycles/kernel/bvh/bvh_volume_all.h @@ -35,7 +35,7 @@ ccl_device #else ccl_device_inline #endif - uint BVH_FUNCTION_FULL_NAME(BVH)(ccl_global const KernelGlobals *kg, + uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, ccl_private const Ray *ray, Intersection *isect_array, const uint max_hits, @@ -289,7 +289,7 @@ ccl_device_inline return num_hits; } -ccl_device_inline uint BVH_FUNCTION_NAME(ccl_global const KernelGlobals *kg, +ccl_device_inline uint BVH_FUNCTION_NAME(KernelGlobals kg, ccl_private const Ray *ray, Intersection *isect_array, const uint max_hits, diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index e115bef3170..28c889f2841 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -111,7 +111,7 @@ ccl_device_inline float shift_cos_in(float cos_in, const float frequency_multipl return val; } -ccl_device_inline int bsdf_sample(ccl_global const KernelGlobals *kg, +ccl_device_inline int bsdf_sample(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private const ShaderClosure *sc, float randu, @@ -467,7 +467,7 @@ ccl_device ccl_device_inline #endif float3 - bsdf_eval(ccl_global const KernelGlobals *kg, + bsdf_eval(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private const ShaderClosure *sc, const float3 omega_in, @@ -652,9 +652,7 @@ ccl_device_inline return eval; } -ccl_device void bsdf_blur(ccl_global const KernelGlobals *kg, - ccl_private ShaderClosure *sc, - float roughness) +ccl_device void bsdf_blur(KernelGlobals kg, ccl_private ShaderClosure *sc, float roughness) { /* TODO: do we want to blur volume closures? */ #ifdef __SVM__ diff --git a/intern/cycles/kernel/closure/bsdf_hair_principled.h b/intern/cycles/kernel/closure/bsdf_hair_principled.h index 17097b0739b..a474c5661b3 100644 --- a/intern/cycles/kernel/closure/bsdf_hair_principled.h +++ b/intern/cycles/kernel/closure/bsdf_hair_principled.h @@ -180,7 +180,7 @@ ccl_device_inline float longitudinal_scattering( } /* Combine the three values using their luminances. */ -ccl_device_inline float4 combine_with_energy(ccl_global const KernelGlobals *kg, float3 c) +ccl_device_inline float4 combine_with_energy(KernelGlobals kg, float3 c) { return make_float4(c.x, c.y, c.z, linear_rgb_to_gray(kg, c)); } @@ -229,7 +229,7 @@ ccl_device int bsdf_principled_hair_setup(ccl_private ShaderData *sd, #endif /* __HAIR__ */ /* Given the Fresnel term and transmittance, generate the attenuation terms for each bounce. */ -ccl_device_inline void hair_attenuation(ccl_global const KernelGlobals *kg, +ccl_device_inline void hair_attenuation(KernelGlobals kg, float f, float3 T, ccl_private float4 *Ap) @@ -281,7 +281,7 @@ ccl_device_inline void hair_alpha_angles(float sin_theta_i, } /* Evaluation function for our shader. */ -ccl_device float3 bsdf_principled_hair_eval(ccl_global const KernelGlobals *kg, +ccl_device float3 bsdf_principled_hair_eval(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private const ShaderClosure *sc, const float3 omega_in, @@ -359,7 +359,7 @@ ccl_device float3 bsdf_principled_hair_eval(ccl_global const KernelGlobals *kg, } /* Sampling function for the hair shader. */ -ccl_device int bsdf_principled_hair_sample(ccl_global const KernelGlobals *kg, +ccl_device int bsdf_principled_hair_sample(KernelGlobals kg, ccl_private const ShaderClosure *sc, ccl_private ShaderData *sd, float randu, diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index 41c35867a6b..a4e1b7a491c 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -55,7 +55,7 @@ static_assert(sizeof(ShaderClosure) >= sizeof(MicrofacetBsdf), "MicrofacetBsdf i /* Beckmann and GGX microfacet importance sampling. */ -ccl_device_inline void microfacet_beckmann_sample_slopes(ccl_global const KernelGlobals *kg, +ccl_device_inline void microfacet_beckmann_sample_slopes(KernelGlobals kg, const float cos_theta_i, const float sin_theta_i, float randu, @@ -195,7 +195,7 @@ ccl_device_inline void microfacet_ggx_sample_slopes(const float cos_theta_i, *slope_y = S * z * safe_sqrtf(1.0f + (*slope_x) * (*slope_x)); } -ccl_device_forceinline float3 microfacet_sample_stretched(ccl_global const KernelGlobals *kg, +ccl_device_forceinline float3 microfacet_sample_stretched(KernelGlobals kg, const float3 omega_i, const float alpha_x, const float alpha_y, @@ -549,7 +549,7 @@ ccl_device float3 bsdf_microfacet_ggx_eval_transmit(ccl_private const ShaderClos return make_float3(out, out, out); } -ccl_device int bsdf_microfacet_ggx_sample(ccl_global const KernelGlobals *kg, +ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg, ccl_private const ShaderClosure *sc, float3 Ng, float3 I, @@ -977,7 +977,7 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(ccl_private const Shade return make_float3(out, out, out); } -ccl_device int bsdf_microfacet_beckmann_sample(ccl_global const KernelGlobals *kg, +ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg, ccl_private const ShaderClosure *sc, float3 Ng, float3 I, diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index 6ee1139ddbb..b7bd7faaa54 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -476,7 +476,7 @@ ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(ccl_private const Shade bsdf->extra->cspec0); } -ccl_device int bsdf_microfacet_multi_ggx_sample(ccl_global const KernelGlobals *kg, +ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg, ccl_private const ShaderClosure *sc, float3 Ng, float3 I, @@ -639,7 +639,7 @@ ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(ccl_private const bsdf->extra->cspec0); } -ccl_device int bsdf_microfacet_multi_ggx_glass_sample(ccl_global const KernelGlobals *kg, +ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg, ccl_private const ShaderClosure *sc, float3 Ng, float3 I, diff --git a/intern/cycles/kernel/device/cpu/globals.h b/intern/cycles/kernel/device/cpu/globals.h index 98b036e269d..fb9aae38cfc 100644 --- a/intern/cycles/kernel/device/cpu/globals.h +++ b/intern/cycles/kernel/device/cpu/globals.h @@ -34,7 +34,7 @@ struct OSLThreadData; struct OSLShadingSystem; #endif -typedef struct KernelGlobals { +typedef struct KernelGlobalsCPU { #define KERNEL_TEX(type, name) texture name; #include "kernel/kernel_textures.h" @@ -51,7 +51,9 @@ typedef struct KernelGlobals { /* **** Run-time data **** */ ProfilingState profiler; -} KernelGlobals; +} KernelGlobalsCPU; + +typedef const KernelGlobalsCPU *ccl_restrict KernelGlobals; /* Abstraction macros */ #define kernel_tex_fetch(tex, index) (kg->tex.fetch(index)) diff --git a/intern/cycles/kernel/device/cpu/image.h b/intern/cycles/kernel/device/cpu/image.h index 57e81ab186d..44c5d7ef065 100644 --- a/intern/cycles/kernel/device/cpu/image.h +++ b/intern/cycles/kernel/device/cpu/image.h @@ -583,7 +583,7 @@ template struct NanoVDBInterpolator { #undef SET_CUBIC_SPLINE_WEIGHTS -ccl_device float4 kernel_tex_image_interp(const KernelGlobals *kg, int id, float x, float y) +ccl_device float4 kernel_tex_image_interp(KernelGlobals kg, int id, float x, float y) { const TextureInfo &info = kernel_tex_fetch(__texture_info, id); @@ -611,7 +611,7 @@ ccl_device float4 kernel_tex_image_interp(const KernelGlobals *kg, int id, float } } -ccl_device float4 kernel_tex_image_interp_3d(const KernelGlobals *kg, +ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg, int id, float3 P, InterpolationType interp) diff --git a/intern/cycles/kernel/device/cpu/kernel.cpp b/intern/cycles/kernel/device/cpu/kernel.cpp index ac1cdf5fffe..8519b77aa08 100644 --- a/intern/cycles/kernel/device/cpu/kernel.cpp +++ b/intern/cycles/kernel/device/cpu/kernel.cpp @@ -64,7 +64,7 @@ CCL_NAMESPACE_BEGIN /* Memory Copy */ -void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t) +void kernel_const_copy(KernelGlobalsCPU *kg, const char *name, void *host, size_t) { if (strcmp(name, "__data") == 0) { kg->__data = *(KernelData *)host; @@ -74,7 +74,7 @@ void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t) } } -void kernel_global_memory_copy(KernelGlobals *kg, const char *name, void *mem, size_t size) +void kernel_global_memory_copy(KernelGlobalsCPU *kg, const char *name, void *mem, size_t size) { if (0) { } diff --git a/intern/cycles/kernel/device/cpu/kernel.h b/intern/cycles/kernel/device/cpu/kernel.h index ae2a841835a..28337a58898 100644 --- a/intern/cycles/kernel/device/cpu/kernel.h +++ b/intern/cycles/kernel/device/cpu/kernel.h @@ -29,17 +29,17 @@ CCL_NAMESPACE_BEGIN #define KERNEL_FUNCTION_FULL_NAME(name) KERNEL_NAME_EVAL(KERNEL_ARCH, name) struct IntegratorStateCPU; -struct KernelGlobals; +struct KernelGlobalsCPU; struct KernelData; -KernelGlobals *kernel_globals_create(); -void kernel_globals_free(KernelGlobals *kg); +KernelGlobalsCPU *kernel_globals_create(); +void kernel_globals_free(KernelGlobalsCPU *kg); -void *kernel_osl_memory(const KernelGlobals *kg); -bool kernel_osl_use(const KernelGlobals *kg); +void *kernel_osl_memory(const KernelGlobalsCPU *kg); +bool kernel_osl_use(const KernelGlobalsCPU *kg); -void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t size); -void kernel_global_memory_copy(KernelGlobals *kg, const char *name, void *mem, size_t size); +void kernel_const_copy(KernelGlobalsCPU *kg, const char *name, void *host, size_t size); +void kernel_global_memory_copy(KernelGlobalsCPU *kg, const char *name, void *mem, size_t size); #define KERNEL_ARCH cpu #include "kernel/device/cpu/kernel_arch.h" diff --git a/intern/cycles/kernel/device/cpu/kernel_arch.h b/intern/cycles/kernel/device/cpu/kernel_arch.h index 8b7b0ec0548..ae7fab65100 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch.h @@ -21,16 +21,16 @@ */ #define KERNEL_INTEGRATOR_FUNCTION(name) \ - void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobals *ccl_restrict kg, \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *ccl_restrict kg, \ IntegratorStateCPU *state) #define KERNEL_INTEGRATOR_SHADE_FUNCTION(name) \ - void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobals *ccl_restrict kg, \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *ccl_restrict kg, \ IntegratorStateCPU *state, \ ccl_global float *render_buffer) #define KERNEL_INTEGRATOR_INIT_FUNCTION(name) \ - bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobals *ccl_restrict kg, \ + bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *ccl_restrict kg, \ IntegratorStateCPU *state, \ KernelWorkTile *tile, \ ccl_global float *render_buffer) @@ -56,11 +56,11 @@ KERNEL_INTEGRATOR_SHADE_FUNCTION(megakernel); * Shader evaluation. */ -void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobals *kg, +void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobalsCPU *kg, const KernelShaderEvalInput *input, float *output, const int offset); -void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobals *kg, +void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobalsCPU *kg, const KernelShaderEvalInput *input, float *output, const int offset); @@ -70,7 +70,7 @@ void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobals *kg, */ bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)( - const KernelGlobals *kg, + const KernelGlobalsCPU *kg, ccl_global float *render_buffer, int x, int y, @@ -79,14 +79,14 @@ bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)( int offset, int stride); -void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const KernelGlobals *kg, +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const KernelGlobalsCPU *kg, ccl_global float *render_buffer, int y, int start_x, int width, int offset, int stride); -void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobals *kg, +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobalsCPU *kg, ccl_global float *render_buffer, int x, int start_y, @@ -98,7 +98,7 @@ void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobals * * Cryptomatte. */ -void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobals *kg, +void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobalsCPU *kg, ccl_global float *render_buffer, int pixel_index); @@ -108,6 +108,6 @@ void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobals *kg, /* TODO(sergey): Needs to be re-implemented. Or not? Brecht did it already :) */ void KERNEL_FUNCTION_FULL_NAME(bake)( - const KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride); + const KernelGlobalsCPU *kg, float *buffer, int sample, int x, int y, int offset, int stride); #undef KERNEL_ARCH diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h index 23e371f165f..bf8667ac045 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h @@ -70,7 +70,7 @@ CCL_NAMESPACE_BEGIN #endif #define DEFINE_INTEGRATOR_KERNEL(name) \ - void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobals *kg, \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *kg, \ IntegratorStateCPU *state) \ { \ KERNEL_INVOKE(name, kg, state); \ @@ -78,7 +78,7 @@ CCL_NAMESPACE_BEGIN #define DEFINE_INTEGRATOR_SHADE_KERNEL(name) \ void KERNEL_FUNCTION_FULL_NAME(integrator_##name)( \ - const KernelGlobals *kg, IntegratorStateCPU *state, ccl_global float *render_buffer) \ + const KernelGlobalsCPU *kg, IntegratorStateCPU *state, ccl_global float *render_buffer) \ { \ KERNEL_INVOKE(name, kg, state, render_buffer); \ } @@ -86,7 +86,7 @@ CCL_NAMESPACE_BEGIN /* TODO: Either use something like get_work_pixel(), or simplify tile which is passed here, so * that it does not contain unused fields. */ #define DEFINE_INTEGRATOR_INIT_KERNEL(name) \ - bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobals *kg, \ + bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *kg, \ IntegratorStateCPU *state, \ KernelWorkTile *tile, \ ccl_global float *render_buffer) \ @@ -112,7 +112,7 @@ DEFINE_INTEGRATOR_SHADE_KERNEL(megakernel) * Shader evaluation. */ -void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobals *kg, +void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobalsCPU *kg, const KernelShaderEvalInput *input, float *output, const int offset) @@ -124,7 +124,7 @@ void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobals *kg, #endif } -void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobals *kg, +void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobalsCPU *kg, const KernelShaderEvalInput *input, float *output, const int offset) @@ -141,7 +141,7 @@ void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobals *kg, */ bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)( - const KernelGlobals *kg, + const KernelGlobalsCPU *kg, ccl_global float *render_buffer, int x, int y, @@ -159,7 +159,7 @@ bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)( #endif } -void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const KernelGlobals *kg, +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const KernelGlobalsCPU *kg, ccl_global float *render_buffer, int y, int start_x, @@ -174,7 +174,7 @@ void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const KernelGlobals * #endif } -void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobals *kg, +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobalsCPU *kg, ccl_global float *render_buffer, int x, int start_y, @@ -193,7 +193,7 @@ void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobals * * Cryptomatte. */ -void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobals *kg, +void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobalsCPU *kg, ccl_global float *render_buffer, int pixel_index) { @@ -210,7 +210,7 @@ void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobals *kg, /* TODO(sergey): Needs to be re-implemented. Or not? Brecht did it already :) */ void KERNEL_FUNCTION_FULL_NAME(bake)( - const KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride) + const KernelGlobalsCPU *kg, float *buffer, int sample, int x, int y, int offset, int stride) { #if 0 # ifdef KERNEL_STUB diff --git a/intern/cycles/kernel/device/cuda/globals.h b/intern/cycles/kernel/device/cuda/globals.h index 169047175f5..2c187cf8a23 100644 --- a/intern/cycles/kernel/device/cuda/globals.h +++ b/intern/cycles/kernel/device/cuda/globals.h @@ -27,9 +27,10 @@ CCL_NAMESPACE_BEGIN /* Not actually used, just a NULL pointer that gets passed everywhere, which we * hope gets optimized out by the compiler. */ -struct KernelGlobals { +struct KernelGlobalsGPU { int unused[1]; }; +typedef ccl_global const KernelGlobalsGPU *ccl_restrict KernelGlobals; /* Global scene data and textures */ __constant__ KernelData __data; diff --git a/intern/cycles/kernel/device/gpu/image.h b/intern/cycles/kernel/device/gpu/image.h index b015c78a8f5..95a37c693ae 100644 --- a/intern/cycles/kernel/device/gpu/image.h +++ b/intern/cycles/kernel/device/gpu/image.h @@ -189,7 +189,7 @@ ccl_device_noinline T kernel_tex_image_interp_nanovdb( } #endif -ccl_device float4 kernel_tex_image_interp(const KernelGlobals *kg, int id, float x, float y) +ccl_device float4 kernel_tex_image_interp(KernelGlobals kg, int id, float x, float y) { const TextureInfo &info = kernel_tex_fetch(__texture_info, id); @@ -221,7 +221,7 @@ ccl_device float4 kernel_tex_image_interp(const KernelGlobals *kg, int id, float } } -ccl_device float4 kernel_tex_image_interp_3d(const KernelGlobals *kg, +ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg, int id, float3 P, InterpolationType interp) diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index 21901215757..56beaf1fd91 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -51,8 +51,8 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) const int state = ccl_gpu_global_id_x(); if (state < num_states) { - INTEGRATOR_STATE_WRITE(path, queued_kernel) = 0; - INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; } } @@ -244,7 +244,7 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_B { gpu_parallel_active_index_array( num_states, indices, num_indices, [kernel](const int state) { - return (INTEGRATOR_STATE(path, queued_kernel) == kernel); + return (INTEGRATOR_STATE(state, path, queued_kernel) == kernel); }); } @@ -256,7 +256,7 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_B { gpu_parallel_active_index_array( num_states, indices, num_indices, [kernel](const int state) { - return (INTEGRATOR_STATE(shadow_path, queued_kernel) == kernel); + return (INTEGRATOR_STATE(state, shadow_path, queued_kernel) == kernel); }); } @@ -265,8 +265,8 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_B { gpu_parallel_active_index_array( num_states, indices, num_indices, [](const int state) { - return (INTEGRATOR_STATE(path, queued_kernel) != 0) || - (INTEGRATOR_STATE(shadow_path, queued_kernel) != 0); + return (INTEGRATOR_STATE(state, path, queued_kernel) != 0) || + (INTEGRATOR_STATE(state, shadow_path, queued_kernel) != 0); }); } @@ -278,8 +278,8 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_B { gpu_parallel_active_index_array( num_states, indices + indices_offset, num_indices, [](const int state) { - return (INTEGRATOR_STATE(path, queued_kernel) == 0) && - (INTEGRATOR_STATE(shadow_path, queued_kernel) == 0); + return (INTEGRATOR_STATE(state, path, queued_kernel) == 0) && + (INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0); }); } @@ -289,8 +289,8 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_B { gpu_parallel_sorted_index_array( num_states, indices, num_indices, key_prefix_sum, [kernel](const int state) { - return (INTEGRATOR_STATE(path, queued_kernel) == kernel) ? - INTEGRATOR_STATE(path, shader_sort_key) : + return (INTEGRATOR_STATE(state, path, queued_kernel) == kernel) ? + INTEGRATOR_STATE(state, path, shader_sort_key) : GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY; }); } @@ -304,8 +304,8 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_B gpu_parallel_active_index_array( num_states, indices, num_indices, [num_active_paths](const int state) { return (state >= num_active_paths) && - ((INTEGRATOR_STATE(path, queued_kernel) != 0) || - (INTEGRATOR_STATE(shadow_path, queued_kernel) != 0)); + ((INTEGRATOR_STATE(state, path, queued_kernel) != 0) || + (INTEGRATOR_STATE(state, shadow_path, queued_kernel) != 0)); }); } diff --git a/intern/cycles/kernel/device/optix/globals.h b/intern/cycles/kernel/device/optix/globals.h index 7d898ed5d91..7b8ebfe50e6 100644 --- a/intern/cycles/kernel/device/optix/globals.h +++ b/intern/cycles/kernel/device/optix/globals.h @@ -27,9 +27,10 @@ CCL_NAMESPACE_BEGIN /* Not actually used, just a NULL pointer that gets passed everywhere, which we * hope gets optimized out by the compiler. */ -struct KernelGlobals { +struct KernelGlobalsGPU { int unused[1]; }; +typedef ccl_global const KernelGlobalsGPU *ccl_restrict KernelGlobals; /* Launch parameters */ struct KernelParamsOptiX { diff --git a/intern/cycles/kernel/geom/geom_attribute.h b/intern/cycles/kernel/geom/geom_attribute.h index 850ac44e6e0..848e0430caa 100644 --- a/intern/cycles/kernel/geom/geom_attribute.h +++ b/intern/cycles/kernel/geom/geom_attribute.h @@ -27,11 +27,9 @@ CCL_NAMESPACE_BEGIN * Lookup of attributes is different between OSL and SVM, as OSL is ustring * based while for SVM we use integer ids. */ -ccl_device_inline uint subd_triangle_patch(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd); +ccl_device_inline uint subd_triangle_patch(KernelGlobals kg, ccl_private const ShaderData *sd); -ccl_device_inline uint attribute_primitive_type(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device_inline uint attribute_primitive_type(KernelGlobals kg, ccl_private const ShaderData *sd) { if ((sd->type & PRIMITIVE_ALL_TRIANGLE) && subd_triangle_patch(kg, sd) != ~0) { return ATTR_PRIM_SUBD; @@ -50,12 +48,12 @@ ccl_device_inline AttributeDescriptor attribute_not_found() /* Find attribute based on ID */ -ccl_device_inline uint object_attribute_map_offset(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline uint object_attribute_map_offset(KernelGlobals kg, int object) { return kernel_tex_fetch(__objects, object).attribute_map_offset; } -ccl_device_inline AttributeDescriptor find_attribute(ccl_global const KernelGlobals *kg, +ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals kg, ccl_private const ShaderData *sd, uint id) { @@ -102,7 +100,7 @@ ccl_device_inline AttributeDescriptor find_attribute(ccl_global const KernelGlob /* Transform matrix attribute on meshes */ -ccl_device Transform primitive_attribute_matrix(ccl_global const KernelGlobals *kg, +ccl_device Transform primitive_attribute_matrix(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc) { diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h index 07f218d781b..7271193eef8 100644 --- a/intern/cycles/kernel/geom/geom_curve.h +++ b/intern/cycles/kernel/geom/geom_curve.h @@ -27,7 +27,7 @@ CCL_NAMESPACE_BEGIN /* Reading attributes on various curve elements */ -ccl_device float curve_attribute_float(ccl_global const KernelGlobals *kg, +ccl_device float curve_attribute_float(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float *dx, @@ -69,7 +69,7 @@ ccl_device float curve_attribute_float(ccl_global const KernelGlobals *kg, } } -ccl_device float2 curve_attribute_float2(ccl_global const KernelGlobals *kg, +ccl_device float2 curve_attribute_float2(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float2 *dx, @@ -115,7 +115,7 @@ ccl_device float2 curve_attribute_float2(ccl_global const KernelGlobals *kg, } } -ccl_device float3 curve_attribute_float3(ccl_global const KernelGlobals *kg, +ccl_device float3 curve_attribute_float3(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float3 *dx, @@ -157,7 +157,7 @@ ccl_device float3 curve_attribute_float3(ccl_global const KernelGlobals *kg, } } -ccl_device float4 curve_attribute_float4(ccl_global const KernelGlobals *kg, +ccl_device float4 curve_attribute_float4(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float4 *dx, @@ -201,8 +201,7 @@ ccl_device float4 curve_attribute_float4(ccl_global const KernelGlobals *kg, /* Curve thickness */ -ccl_device float curve_thickness(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device float curve_thickness(KernelGlobals kg, ccl_private const ShaderData *sd) { float r = 0.0f; @@ -230,8 +229,7 @@ ccl_device float curve_thickness(ccl_global const KernelGlobals *kg, /* Curve location for motion pass, linear interpolation between keys and * ignoring radius because we do the same for the motion keys */ -ccl_device float3 curve_motion_center_location(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device float3 curve_motion_center_location(KernelGlobals kg, ccl_private const ShaderData *sd) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); @@ -247,8 +245,7 @@ ccl_device float3 curve_motion_center_location(ccl_global const KernelGlobals *k /* Curve tangent normal */ -ccl_device float3 curve_tangent_normal(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device float3 curve_tangent_normal(KernelGlobals kg, ccl_private const ShaderData *sd) { float3 tgN = make_float3(0.0f, 0.0f, 0.0f); diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h index 04af8ea1421..fb0b80b281f 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/geom_curve_intersect.h @@ -625,7 +625,7 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, return false; } -ccl_device_forceinline bool curve_intersect(ccl_global const KernelGlobals *kg, +ccl_device_forceinline bool curve_intersect(KernelGlobals kg, ccl_private Intersection *isect, const float3 P, const float3 dir, @@ -679,7 +679,7 @@ ccl_device_forceinline bool curve_intersect(ccl_global const KernelGlobals *kg, } } -ccl_device_inline void curve_shader_setup(ccl_global const KernelGlobals *kg, +ccl_device_inline void curve_shader_setup(KernelGlobals kg, ccl_private ShaderData *sd, float3 P, float3 D, diff --git a/intern/cycles/kernel/geom/geom_motion_curve.h b/intern/cycles/kernel/geom/geom_motion_curve.h index 5754608a69b..2dd213d43f6 100644 --- a/intern/cycles/kernel/geom/geom_motion_curve.h +++ b/intern/cycles/kernel/geom/geom_motion_curve.h @@ -27,7 +27,7 @@ CCL_NAMESPACE_BEGIN #ifdef __HAIR__ -ccl_device_inline void motion_curve_keys_for_step_linear(ccl_global const KernelGlobals *kg, +ccl_device_inline void motion_curve_keys_for_step_linear(KernelGlobals kg, int offset, int numkeys, int numsteps, @@ -54,13 +54,8 @@ ccl_device_inline void motion_curve_keys_for_step_linear(ccl_global const Kernel } /* return 2 curve key locations */ -ccl_device_inline void motion_curve_keys_linear(ccl_global const KernelGlobals *kg, - int object, - int prim, - float time, - int k0, - int k1, - float4 keys[2]) +ccl_device_inline void motion_curve_keys_linear( + KernelGlobals kg, int object, int prim, float time, int k0, int k1, float4 keys[2]) { /* get motion info */ int numsteps, numkeys; @@ -86,7 +81,7 @@ ccl_device_inline void motion_curve_keys_linear(ccl_global const KernelGlobals * keys[1] = (1.0f - t) * keys[1] + t * next_keys[1]; } -ccl_device_inline void motion_curve_keys_for_step(ccl_global const KernelGlobals *kg, +ccl_device_inline void motion_curve_keys_for_step(KernelGlobals kg, int offset, int numkeys, int numsteps, @@ -119,7 +114,7 @@ ccl_device_inline void motion_curve_keys_for_step(ccl_global const KernelGlobals } /* return 2 curve key locations */ -ccl_device_inline void motion_curve_keys(ccl_global const KernelGlobals *kg, +ccl_device_inline void motion_curve_keys(KernelGlobals kg, int object, int prim, float time, diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/geom_motion_triangle.h index 547f03af47c..69d15f950ec 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle.h @@ -33,7 +33,7 @@ CCL_NAMESPACE_BEGIN /* Time interpolation of vertex positions and normals */ -ccl_device_inline void motion_triangle_verts_for_step(ccl_global const KernelGlobals *kg, +ccl_device_inline void motion_triangle_verts_for_step(KernelGlobals kg, uint4 tri_vindex, int offset, int numverts, @@ -60,7 +60,7 @@ ccl_device_inline void motion_triangle_verts_for_step(ccl_global const KernelGlo } } -ccl_device_inline void motion_triangle_normals_for_step(ccl_global const KernelGlobals *kg, +ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals kg, uint4 tri_vindex, int offset, int numverts, @@ -88,7 +88,7 @@ ccl_device_inline void motion_triangle_normals_for_step(ccl_global const KernelG } ccl_device_inline void motion_triangle_vertices( - ccl_global const KernelGlobals *kg, int object, int prim, float time, float3 verts[3]) + KernelGlobals kg, int object, int prim, float time, float3 verts[3]) { /* get motion info */ int numsteps, numverts; @@ -116,13 +116,8 @@ ccl_device_inline void motion_triangle_vertices( verts[2] = (1.0f - t) * verts[2] + t * next_verts[2]; } -ccl_device_inline float3 motion_triangle_smooth_normal(ccl_global const KernelGlobals *kg, - float3 Ng, - int object, - int prim, - float u, - float v, - float time) +ccl_device_inline float3 motion_triangle_smooth_normal( + KernelGlobals kg, float3 Ng, int object, int prim, float u, float v, float time) { /* get motion info */ int numsteps, numverts; diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h index 94d00875f0a..256e7add21e 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h @@ -34,7 +34,7 @@ CCL_NAMESPACE_BEGIN * a closer distance. */ -ccl_device_inline float3 motion_triangle_refine(ccl_global const KernelGlobals *kg, +ccl_device_inline float3 motion_triangle_refine(KernelGlobals kg, ccl_private ShaderData *sd, float3 P, float3 D, @@ -92,7 +92,7 @@ ccl_device_noinline ccl_device_inline # endif float3 - motion_triangle_refine_local(ccl_global const KernelGlobals *kg, + motion_triangle_refine_local(KernelGlobals kg, ccl_private ShaderData *sd, float3 P, float3 D, @@ -145,7 +145,7 @@ ccl_device_inline * time and do a ray intersection with the resulting triangle. */ -ccl_device_inline bool motion_triangle_intersect(ccl_global const KernelGlobals *kg, +ccl_device_inline bool motion_triangle_intersect(KernelGlobals kg, ccl_private Intersection *isect, float3 P, float3 dir, @@ -202,7 +202,7 @@ ccl_device_inline bool motion_triangle_intersect(ccl_global const KernelGlobals * Returns whether traversal should be stopped. */ #ifdef __BVH_LOCAL__ -ccl_device_inline bool motion_triangle_intersect_local(ccl_global const KernelGlobals *kg, +ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals kg, ccl_private LocalIntersection *local_isect, float3 P, float3 dir, diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h b/intern/cycles/kernel/geom/geom_motion_triangle_shader.h index 25a68fa7781..fc7c181882e 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle_shader.h @@ -34,7 +34,7 @@ CCL_NAMESPACE_BEGIN * normals */ /* return 3 triangle vertex normals */ -ccl_device_noinline void motion_triangle_shader_setup(ccl_global const KernelGlobals *kg, +ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals kg, ccl_private ShaderData *sd, const float3 P, const float3 D, diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 730c01d4709..34a9d639d9d 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -37,7 +37,7 @@ enum ObjectVectorTransform { OBJECT_PASS_MOTION_PRE = 0, OBJECT_PASS_MOTION_POST /* Object to world space transformation */ -ccl_device_inline Transform object_fetch_transform(ccl_global const KernelGlobals *kg, +ccl_device_inline Transform object_fetch_transform(KernelGlobals kg, int object, enum ObjectTransform type) { @@ -51,9 +51,7 @@ ccl_device_inline Transform object_fetch_transform(ccl_global const KernelGlobal /* Lamp to world space transformation */ -ccl_device_inline Transform lamp_fetch_transform(ccl_global const KernelGlobals *kg, - int lamp, - bool inverse) +ccl_device_inline Transform lamp_fetch_transform(KernelGlobals kg, int lamp, bool inverse) { if (inverse) { return kernel_tex_fetch(__lights, lamp).itfm; @@ -65,7 +63,7 @@ ccl_device_inline Transform lamp_fetch_transform(ccl_global const KernelGlobals /* Object to world space transformation for motion vectors */ -ccl_device_inline Transform object_fetch_motion_pass_transform(ccl_global const KernelGlobals *kg, +ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals kg, int object, enum ObjectVectorTransform type) { @@ -76,9 +74,7 @@ ccl_device_inline Transform object_fetch_motion_pass_transform(ccl_global const /* Motion blurred object transformations */ #ifdef __OBJECT_MOTION__ -ccl_device_inline Transform object_fetch_transform_motion(ccl_global const KernelGlobals *kg, - int object, - float time) +ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals kg, int object, float time) { const uint motion_offset = kernel_tex_fetch(__objects, object).motion_offset; ccl_global const DecomposedTransform *motion = &kernel_tex_fetch(__object_motion, motion_offset); @@ -90,7 +86,7 @@ ccl_device_inline Transform object_fetch_transform_motion(ccl_global const Kerne return tfm; } -ccl_device_inline Transform object_fetch_transform_motion_test(ccl_global const KernelGlobals *kg, +ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals kg, int object, float time, ccl_private Transform *itfm) @@ -117,7 +113,7 @@ ccl_device_inline Transform object_fetch_transform_motion_test(ccl_global const /* Get transform matrix for shading point. */ -ccl_device_inline Transform object_get_transform(ccl_global const KernelGlobals *kg, +ccl_device_inline Transform object_get_transform(KernelGlobals kg, ccl_private const ShaderData *sd) { #ifdef __OBJECT_MOTION__ @@ -129,7 +125,7 @@ ccl_device_inline Transform object_get_transform(ccl_global const KernelGlobals #endif } -ccl_device_inline Transform object_get_inverse_transform(ccl_global const KernelGlobals *kg, +ccl_device_inline Transform object_get_inverse_transform(KernelGlobals kg, ccl_private const ShaderData *sd) { #ifdef __OBJECT_MOTION__ @@ -142,7 +138,7 @@ ccl_device_inline Transform object_get_inverse_transform(ccl_global const Kernel } /* Transform position from object to world space */ -ccl_device_inline void object_position_transform(ccl_global const KernelGlobals *kg, +ccl_device_inline void object_position_transform(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float3 *P) { @@ -159,7 +155,7 @@ ccl_device_inline void object_position_transform(ccl_global const KernelGlobals /* Transform position from world to object space */ -ccl_device_inline void object_inverse_position_transform(ccl_global const KernelGlobals *kg, +ccl_device_inline void object_inverse_position_transform(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float3 *P) { @@ -176,7 +172,7 @@ ccl_device_inline void object_inverse_position_transform(ccl_global const Kernel /* Transform normal from world to object space */ -ccl_device_inline void object_inverse_normal_transform(ccl_global const KernelGlobals *kg, +ccl_device_inline void object_inverse_normal_transform(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float3 *N) { @@ -201,7 +197,7 @@ ccl_device_inline void object_inverse_normal_transform(ccl_global const KernelGl /* Transform normal from object to world space */ -ccl_device_inline void object_normal_transform(ccl_global const KernelGlobals *kg, +ccl_device_inline void object_normal_transform(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float3 *N) { @@ -218,7 +214,7 @@ ccl_device_inline void object_normal_transform(ccl_global const KernelGlobals *k /* Transform direction vector from object to world space */ -ccl_device_inline void object_dir_transform(ccl_global const KernelGlobals *kg, +ccl_device_inline void object_dir_transform(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float3 *D) { @@ -235,7 +231,7 @@ ccl_device_inline void object_dir_transform(ccl_global const KernelGlobals *kg, /* Transform direction vector from world to object space */ -ccl_device_inline void object_inverse_dir_transform(ccl_global const KernelGlobals *kg, +ccl_device_inline void object_inverse_dir_transform(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float3 *D) { @@ -252,8 +248,7 @@ ccl_device_inline void object_inverse_dir_transform(ccl_global const KernelGloba /* Object center position */ -ccl_device_inline float3 object_location(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device_inline float3 object_location(KernelGlobals kg, ccl_private const ShaderData *sd) { if (sd->object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); @@ -270,7 +265,7 @@ ccl_device_inline float3 object_location(ccl_global const KernelGlobals *kg, /* Color of the object */ -ccl_device_inline float3 object_color(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline float3 object_color(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); @@ -281,7 +276,7 @@ ccl_device_inline float3 object_color(ccl_global const KernelGlobals *kg, int ob /* Pass ID number of object */ -ccl_device_inline float object_pass_id(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline float object_pass_id(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -291,7 +286,7 @@ ccl_device_inline float object_pass_id(ccl_global const KernelGlobals *kg, int o /* Per lamp random number for shader variation */ -ccl_device_inline float lamp_random_number(ccl_global const KernelGlobals *kg, int lamp) +ccl_device_inline float lamp_random_number(KernelGlobals kg, int lamp) { if (lamp == LAMP_NONE) return 0.0f; @@ -301,7 +296,7 @@ ccl_device_inline float lamp_random_number(ccl_global const KernelGlobals *kg, i /* Per object random number for shader variation */ -ccl_device_inline float object_random_number(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline float object_random_number(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -311,7 +306,7 @@ ccl_device_inline float object_random_number(ccl_global const KernelGlobals *kg, /* Particle ID from which this object was generated */ -ccl_device_inline int object_particle_id(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline int object_particle_id(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0; @@ -321,7 +316,7 @@ ccl_device_inline int object_particle_id(ccl_global const KernelGlobals *kg, int /* Generated texture coordinate on surface from where object was instanced */ -ccl_device_inline float3 object_dupli_generated(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline float3 object_dupli_generated(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); @@ -333,7 +328,7 @@ ccl_device_inline float3 object_dupli_generated(ccl_global const KernelGlobals * /* UV texture coordinate on surface from where object was instanced */ -ccl_device_inline float3 object_dupli_uv(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline float3 object_dupli_uv(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); @@ -344,7 +339,7 @@ ccl_device_inline float3 object_dupli_uv(ccl_global const KernelGlobals *kg, int /* Information about mesh for motion blurred triangles and curves */ -ccl_device_inline void object_motion_info(ccl_global const KernelGlobals *kg, +ccl_device_inline void object_motion_info(KernelGlobals kg, int object, ccl_private int *numsteps, ccl_private int *numverts, @@ -362,7 +357,7 @@ ccl_device_inline void object_motion_info(ccl_global const KernelGlobals *kg, /* Offset to an objects patch map */ -ccl_device_inline uint object_patch_map_offset(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline uint object_patch_map_offset(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0; @@ -372,7 +367,7 @@ ccl_device_inline uint object_patch_map_offset(ccl_global const KernelGlobals *k /* Volume step size */ -ccl_device_inline float object_volume_density(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline float object_volume_density(KernelGlobals kg, int object) { if (object == OBJECT_NONE) { return 1.0f; @@ -381,7 +376,7 @@ ccl_device_inline float object_volume_density(ccl_global const KernelGlobals *kg return kernel_tex_fetch(__objects, object).volume_density; } -ccl_device_inline float object_volume_step_size(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline float object_volume_step_size(KernelGlobals kg, int object) { if (object == OBJECT_NONE) { return kernel_data.background.volume_step_size; @@ -392,14 +387,14 @@ ccl_device_inline float object_volume_step_size(ccl_global const KernelGlobals * /* Pass ID for shader */ -ccl_device int shader_pass_id(ccl_global const KernelGlobals *kg, ccl_private const ShaderData *sd) +ccl_device int shader_pass_id(KernelGlobals kg, ccl_private const ShaderData *sd) { return kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).pass_id; } /* Cryptomatte ID */ -ccl_device_inline float object_cryptomatte_id(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline float object_cryptomatte_id(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -407,7 +402,7 @@ ccl_device_inline float object_cryptomatte_id(ccl_global const KernelGlobals *kg return kernel_tex_fetch(__objects, object).cryptomatte_object; } -ccl_device_inline float object_cryptomatte_asset_id(ccl_global const KernelGlobals *kg, int object) +ccl_device_inline float object_cryptomatte_asset_id(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0; @@ -417,42 +412,42 @@ ccl_device_inline float object_cryptomatte_asset_id(ccl_global const KernelGloba /* Particle data from which object was instanced */ -ccl_device_inline uint particle_index(ccl_global const KernelGlobals *kg, int particle) +ccl_device_inline uint particle_index(KernelGlobals kg, int particle) { return kernel_tex_fetch(__particles, particle).index; } -ccl_device float particle_age(ccl_global const KernelGlobals *kg, int particle) +ccl_device float particle_age(KernelGlobals kg, int particle) { return kernel_tex_fetch(__particles, particle).age; } -ccl_device float particle_lifetime(ccl_global const KernelGlobals *kg, int particle) +ccl_device float particle_lifetime(KernelGlobals kg, int particle) { return kernel_tex_fetch(__particles, particle).lifetime; } -ccl_device float particle_size(ccl_global const KernelGlobals *kg, int particle) +ccl_device float particle_size(KernelGlobals kg, int particle) { return kernel_tex_fetch(__particles, particle).size; } -ccl_device float4 particle_rotation(ccl_global const KernelGlobals *kg, int particle) +ccl_device float4 particle_rotation(KernelGlobals kg, int particle) { return kernel_tex_fetch(__particles, particle).rotation; } -ccl_device float3 particle_location(ccl_global const KernelGlobals *kg, int particle) +ccl_device float3 particle_location(KernelGlobals kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).location); } -ccl_device float3 particle_velocity(ccl_global const KernelGlobals *kg, int particle) +ccl_device float3 particle_velocity(KernelGlobals kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).velocity); } -ccl_device float3 particle_angular_velocity(ccl_global const KernelGlobals *kg, int particle) +ccl_device float3 particle_angular_velocity(KernelGlobals kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).angular_velocity); } @@ -474,7 +469,7 @@ ccl_device_inline float3 bvh_inverse_direction(float3 dir) /* Transform ray into object space to enter static object in BVH */ -ccl_device_inline float bvh_instance_push(ccl_global const KernelGlobals *kg, +ccl_device_inline float bvh_instance_push(KernelGlobals kg, int object, ccl_private const Ray *ray, ccl_private float3 *P, @@ -494,7 +489,7 @@ ccl_device_inline float bvh_instance_push(ccl_global const KernelGlobals *kg, /* Transform ray to exit static object in BVH. */ -ccl_device_inline float bvh_instance_pop(ccl_global const KernelGlobals *kg, +ccl_device_inline float bvh_instance_pop(KernelGlobals kg, int object, ccl_private const Ray *ray, ccl_private float3 *P, @@ -516,7 +511,7 @@ ccl_device_inline float bvh_instance_pop(ccl_global const KernelGlobals *kg, /* Same as above, but returns scale factor to apply to multiple intersection distances */ -ccl_device_inline void bvh_instance_pop_factor(ccl_global const KernelGlobals *kg, +ccl_device_inline void bvh_instance_pop_factor(KernelGlobals kg, int object, ccl_private const Ray *ray, ccl_private float3 *P, @@ -535,7 +530,7 @@ ccl_device_inline void bvh_instance_pop_factor(ccl_global const KernelGlobals *k #ifdef __OBJECT_MOTION__ /* Transform ray into object space to enter motion blurred object in BVH */ -ccl_device_inline float bvh_instance_motion_push(ccl_global const KernelGlobals *kg, +ccl_device_inline float bvh_instance_motion_push(KernelGlobals kg, int object, ccl_private const Ray *ray, ccl_private float3 *P, @@ -556,7 +551,7 @@ ccl_device_inline float bvh_instance_motion_push(ccl_global const KernelGlobals /* Transform ray to exit motion blurred object in BVH. */ -ccl_device_inline float bvh_instance_motion_pop(ccl_global const KernelGlobals *kg, +ccl_device_inline float bvh_instance_motion_pop(KernelGlobals kg, int object, ccl_private const Ray *ray, ccl_private float3 *P, @@ -578,7 +573,7 @@ ccl_device_inline float bvh_instance_motion_pop(ccl_global const KernelGlobals * /* Same as above, but returns scale factor to apply to multiple intersection distances */ -ccl_device_inline void bvh_instance_motion_pop_factor(ccl_global const KernelGlobals *kg, +ccl_device_inline void bvh_instance_motion_pop_factor(KernelGlobals kg, int object, ccl_private const Ray *ray, ccl_private float3 *P, diff --git a/intern/cycles/kernel/geom/geom_patch.h b/intern/cycles/kernel/geom/geom_patch.h index b54eafd6220..bd797ef52ab 100644 --- a/intern/cycles/kernel/geom/geom_patch.h +++ b/intern/cycles/kernel/geom/geom_patch.h @@ -64,7 +64,7 @@ ccl_device_inline int patch_map_resolve_quadrant(float median, /* retrieve PatchHandle from patch coords */ ccl_device_inline PatchHandle -patch_map_find_patch(ccl_global const KernelGlobals *kg, int object, int patch, float u, float v) +patch_map_find_patch(KernelGlobals kg, int object, int patch, float u, float v) { PatchHandle handle; @@ -201,7 +201,7 @@ ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, /* retrieve patch control indices */ -ccl_device_inline int patch_eval_indices(ccl_global const KernelGlobals *kg, +ccl_device_inline int patch_eval_indices(KernelGlobals kg, ccl_private const PatchHandle *handle, int channel, int indices[PATCH_MAX_CONTROL_VERTS]) @@ -218,7 +218,7 @@ ccl_device_inline int patch_eval_indices(ccl_global const KernelGlobals *kg, /* evaluate patch basis functions */ -ccl_device_inline void patch_eval_basis(ccl_global const KernelGlobals *kg, +ccl_device_inline void patch_eval_basis(KernelGlobals kg, ccl_private const PatchHandle *handle, float u, float v, @@ -257,7 +257,7 @@ ccl_device_inline void patch_eval_basis(ccl_global const KernelGlobals *kg, /* generic function for evaluating indices and weights from patch coords */ -ccl_device_inline int patch_eval_control_verts(ccl_global const KernelGlobals *kg, +ccl_device_inline int patch_eval_control_verts(KernelGlobals kg, int object, int patch, float u, @@ -279,7 +279,7 @@ ccl_device_inline int patch_eval_control_verts(ccl_global const KernelGlobals *k /* functions for evaluating attributes on patches */ -ccl_device float patch_eval_float(ccl_global const KernelGlobals *kg, +ccl_device float patch_eval_float(KernelGlobals kg, ccl_private const ShaderData *sd, int offset, int patch, @@ -316,7 +316,7 @@ ccl_device float patch_eval_float(ccl_global const KernelGlobals *kg, return val; } -ccl_device float2 patch_eval_float2(ccl_global const KernelGlobals *kg, +ccl_device float2 patch_eval_float2(KernelGlobals kg, ccl_private const ShaderData *sd, int offset, int patch, @@ -353,7 +353,7 @@ ccl_device float2 patch_eval_float2(ccl_global const KernelGlobals *kg, return val; } -ccl_device float3 patch_eval_float3(ccl_global const KernelGlobals *kg, +ccl_device float3 patch_eval_float3(KernelGlobals kg, ccl_private const ShaderData *sd, int offset, int patch, @@ -390,7 +390,7 @@ ccl_device float3 patch_eval_float3(ccl_global const KernelGlobals *kg, return val; } -ccl_device float4 patch_eval_float4(ccl_global const KernelGlobals *kg, +ccl_device float4 patch_eval_float4(KernelGlobals kg, ccl_private const ShaderData *sd, int offset, int patch, @@ -427,7 +427,7 @@ ccl_device float4 patch_eval_float4(ccl_global const KernelGlobals *kg, return val; } -ccl_device float4 patch_eval_uchar4(ccl_global const KernelGlobals *kg, +ccl_device float4 patch_eval_uchar4(KernelGlobals kg, ccl_private const ShaderData *sd, int offset, int patch, diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h index 869b911f76f..91b29c7f990 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/geom_primitive.h @@ -31,7 +31,7 @@ CCL_NAMESPACE_BEGIN * attributes for performance, mainly for GPU performance to avoid bringing in * heavy volume interpolation code. */ -ccl_device_inline float primitive_surface_attribute_float(ccl_global const KernelGlobals *kg, +ccl_device_inline float primitive_surface_attribute_float(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float *dx, @@ -57,7 +57,7 @@ ccl_device_inline float primitive_surface_attribute_float(ccl_global const Kerne } } -ccl_device_inline float2 primitive_surface_attribute_float2(ccl_global const KernelGlobals *kg, +ccl_device_inline float2 primitive_surface_attribute_float2(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float2 *dx, @@ -83,7 +83,7 @@ ccl_device_inline float2 primitive_surface_attribute_float2(ccl_global const Ker } } -ccl_device_inline float3 primitive_surface_attribute_float3(ccl_global const KernelGlobals *kg, +ccl_device_inline float3 primitive_surface_attribute_float3(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float3 *dx, @@ -109,12 +109,11 @@ ccl_device_inline float3 primitive_surface_attribute_float3(ccl_global const Ker } } -ccl_device_forceinline float4 -primitive_surface_attribute_float4(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd, - const AttributeDescriptor desc, - ccl_private float4 *dx, - ccl_private float4 *dy) +ccl_device_forceinline float4 primitive_surface_attribute_float4(KernelGlobals kg, + ccl_private const ShaderData *sd, + const AttributeDescriptor desc, + ccl_private float4 *dx, + ccl_private float4 *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -149,7 +148,7 @@ ccl_device_inline bool primitive_is_volume_attribute(ccl_private const ShaderDat return sd->type == PRIMITIVE_VOLUME; } -ccl_device_inline float primitive_volume_attribute_float(ccl_global const KernelGlobals *kg, +ccl_device_inline float primitive_volume_attribute_float(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc) { @@ -161,7 +160,7 @@ ccl_device_inline float primitive_volume_attribute_float(ccl_global const Kernel } } -ccl_device_inline float3 primitive_volume_attribute_float3(ccl_global const KernelGlobals *kg, +ccl_device_inline float3 primitive_volume_attribute_float3(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc) { @@ -173,7 +172,7 @@ ccl_device_inline float3 primitive_volume_attribute_float3(ccl_global const Kern } } -ccl_device_inline float4 primitive_volume_attribute_float4(ccl_global const KernelGlobals *kg, +ccl_device_inline float4 primitive_volume_attribute_float4(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc) { @@ -188,8 +187,7 @@ ccl_device_inline float4 primitive_volume_attribute_float4(ccl_global const Kern /* Default UV coordinate */ -ccl_device_inline float3 primitive_uv(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device_inline float3 primitive_uv(KernelGlobals kg, ccl_private const ShaderData *sd) { const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_UV); @@ -202,7 +200,7 @@ ccl_device_inline float3 primitive_uv(ccl_global const KernelGlobals *kg, /* Ptex coordinates */ -ccl_device bool primitive_ptex(ccl_global const KernelGlobals *kg, +ccl_device bool primitive_ptex(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float2 *uv, ccl_private int *face_id) @@ -225,7 +223,7 @@ ccl_device bool primitive_ptex(ccl_global const KernelGlobals *kg, /* Surface tangent */ -ccl_device float3 primitive_tangent(ccl_global const KernelGlobals *kg, ccl_private ShaderData *sd) +ccl_device float3 primitive_tangent(KernelGlobals kg, ccl_private ShaderData *sd) { #ifdef __HAIR__ if (sd->type & PRIMITIVE_ALL_CURVE) @@ -257,7 +255,7 @@ ccl_device float3 primitive_tangent(ccl_global const KernelGlobals *kg, ccl_priv /* Motion vector for motion pass */ -ccl_device_inline float4 primitive_motion_vector(ccl_global const KernelGlobals *kg, +ccl_device_inline float4 primitive_motion_vector(KernelGlobals kg, ccl_private const ShaderData *sd) { /* center position */ diff --git a/intern/cycles/kernel/geom/geom_shader_data.h b/intern/cycles/kernel/geom/geom_shader_data.h index 2cf60e263c3..e6a5b8f7923 100644 --- a/intern/cycles/kernel/geom/geom_shader_data.h +++ b/intern/cycles/kernel/geom/geom_shader_data.h @@ -25,7 +25,7 @@ CCL_NAMESPACE_BEGIN /* ShaderData setup from incoming ray */ #ifdef __OBJECT_MOTION__ -ccl_device void shader_setup_object_transforms(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device void shader_setup_object_transforms(KernelGlobals kg, ccl_private ShaderData *ccl_restrict sd, float time) { @@ -38,7 +38,7 @@ ccl_device void shader_setup_object_transforms(ccl_global const KernelGlobals *c /* TODO: break this up if it helps reduce register pressure to load data from * global memory as we write it to shader-data. */ -ccl_device_inline void shader_setup_from_ray(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device_inline void shader_setup_from_ray(KernelGlobals kg, ccl_private ShaderData *ccl_restrict sd, ccl_private const Ray *ccl_restrict ray, ccl_private const Intersection *ccl_restrict isect) @@ -135,7 +135,7 @@ ccl_device_inline void shader_setup_from_ray(ccl_global const KernelGlobals *ccl /* ShaderData setup from position sampled on mesh */ -ccl_device_inline void shader_setup_from_sample(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device_inline void shader_setup_from_sample(KernelGlobals kg, ccl_private ShaderData *ccl_restrict sd, const float3 P, const float3 Ng, @@ -247,7 +247,7 @@ ccl_device_inline void shader_setup_from_sample(ccl_global const KernelGlobals * /* ShaderData setup for displacement */ -ccl_device void shader_setup_from_displace(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device void shader_setup_from_displace(KernelGlobals kg, ccl_private ShaderData *ccl_restrict sd, int object, int prim, @@ -281,8 +281,7 @@ ccl_device void shader_setup_from_displace(ccl_global const KernelGlobals *ccl_r /* ShaderData setup from ray into background */ -ccl_device_inline void shader_setup_from_background(ccl_global const KernelGlobals *ccl_restrict - kg, +ccl_device_inline void shader_setup_from_background(KernelGlobals kg, ccl_private ShaderData *ccl_restrict sd, const float3 ray_P, const float3 ray_D, @@ -326,7 +325,7 @@ ccl_device_inline void shader_setup_from_background(ccl_global const KernelGloba /* ShaderData setup from point inside volume */ #ifdef __VOLUME__ -ccl_device_inline void shader_setup_from_volume(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device_inline void shader_setup_from_volume(KernelGlobals kg, ccl_private ShaderData *ccl_restrict sd, ccl_private const Ray *ccl_restrict ray) { diff --git a/intern/cycles/kernel/geom/geom_subd_triangle.h b/intern/cycles/kernel/geom/geom_subd_triangle.h index 927d630fe91..8a9a3f71231 100644 --- a/intern/cycles/kernel/geom/geom_subd_triangle.h +++ b/intern/cycles/kernel/geom/geom_subd_triangle.h @@ -22,15 +22,14 @@ CCL_NAMESPACE_BEGIN /* Patch index for triangle, -1 if not subdivision triangle */ -ccl_device_inline uint subd_triangle_patch(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device_inline uint subd_triangle_patch(KernelGlobals kg, ccl_private const ShaderData *sd) { return (sd->prim != PRIM_NONE) ? kernel_tex_fetch(__tri_patch, sd->prim) : ~0; } /* UV coords of triangle within patch */ -ccl_device_inline void subd_triangle_patch_uv(ccl_global const KernelGlobals *kg, +ccl_device_inline void subd_triangle_patch_uv(KernelGlobals kg, ccl_private const ShaderData *sd, float2 uv[3]) { @@ -43,7 +42,7 @@ ccl_device_inline void subd_triangle_patch_uv(ccl_global const KernelGlobals *kg /* Vertex indices of patch */ -ccl_device_inline uint4 subd_triangle_patch_indices(ccl_global const KernelGlobals *kg, int patch) +ccl_device_inline uint4 subd_triangle_patch_indices(KernelGlobals kg, int patch) { uint4 indices; @@ -57,24 +56,21 @@ ccl_device_inline uint4 subd_triangle_patch_indices(ccl_global const KernelGloba /* Originating face for patch */ -ccl_device_inline uint subd_triangle_patch_face(ccl_global const KernelGlobals *kg, int patch) +ccl_device_inline uint subd_triangle_patch_face(KernelGlobals kg, int patch) { return kernel_tex_fetch(__patches, patch + 4); } /* Number of corners on originating face */ -ccl_device_inline uint subd_triangle_patch_num_corners(ccl_global const KernelGlobals *kg, - int patch) +ccl_device_inline uint subd_triangle_patch_num_corners(KernelGlobals kg, int patch) { return kernel_tex_fetch(__patches, patch + 5) & 0xffff; } /* Indices of the four corners that are used by the patch */ -ccl_device_inline void subd_triangle_patch_corners(ccl_global const KernelGlobals *kg, - int patch, - int corners[4]) +ccl_device_inline void subd_triangle_patch_corners(KernelGlobals kg, int patch, int corners[4]) { uint4 data; @@ -105,7 +101,7 @@ ccl_device_inline void subd_triangle_patch_corners(ccl_global const KernelGlobal /* Reading attributes on various subdivision triangle elements */ -ccl_device_noinline float subd_triangle_attribute_float(ccl_global const KernelGlobals *kg, +ccl_device_noinline float subd_triangle_attribute_float(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float *dx, @@ -244,7 +240,7 @@ ccl_device_noinline float subd_triangle_attribute_float(ccl_global const KernelG } } -ccl_device_noinline float2 subd_triangle_attribute_float2(ccl_global const KernelGlobals *kg, +ccl_device_noinline float2 subd_triangle_attribute_float2(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float2 *dx, @@ -387,7 +383,7 @@ ccl_device_noinline float2 subd_triangle_attribute_float2(ccl_global const Kerne } } -ccl_device_noinline float3 subd_triangle_attribute_float3(ccl_global const KernelGlobals *kg, +ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float3 *dx, @@ -529,7 +525,7 @@ ccl_device_noinline float3 subd_triangle_attribute_float3(ccl_global const Kerne } } -ccl_device_noinline float4 subd_triangle_attribute_float4(ccl_global const KernelGlobals *kg, +ccl_device_noinline float4 subd_triangle_attribute_float4(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float4 *dx, diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/geom_triangle.h index 17f87b7c570..233e901c7ca 100644 --- a/intern/cycles/kernel/geom/geom_triangle.h +++ b/intern/cycles/kernel/geom/geom_triangle.h @@ -25,8 +25,7 @@ CCL_NAMESPACE_BEGIN /* Normal on triangle. */ -ccl_device_inline float3 triangle_normal(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd) +ccl_device_inline float3 triangle_normal(KernelGlobals kg, ccl_private ShaderData *sd) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, sd->prim); @@ -44,7 +43,7 @@ ccl_device_inline float3 triangle_normal(ccl_global const KernelGlobals *kg, } /* Point and normal on triangle. */ -ccl_device_inline void triangle_point_normal(ccl_global const KernelGlobals *kg, +ccl_device_inline void triangle_point_normal(KernelGlobals kg, int object, int prim, float u, @@ -76,7 +75,7 @@ ccl_device_inline void triangle_point_normal(ccl_global const KernelGlobals *kg, /* Triangle vertex locations */ -ccl_device_inline void triangle_vertices(ccl_global const KernelGlobals *kg, int prim, float3 P[3]) +ccl_device_inline void triangle_vertices(KernelGlobals kg, int prim, float3 P[3]) { const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); P[0] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); @@ -86,7 +85,7 @@ ccl_device_inline void triangle_vertices(ccl_global const KernelGlobals *kg, int /* Triangle vertex locations and vertex normals */ -ccl_device_inline void triangle_vertices_and_normals(ccl_global const KernelGlobals *kg, +ccl_device_inline void triangle_vertices_and_normals(KernelGlobals kg, int prim, float3 P[3], float3 N[3]) @@ -103,7 +102,7 @@ ccl_device_inline void triangle_vertices_and_normals(ccl_global const KernelGlob /* Interpolate smooth vertex normal from vertices */ ccl_device_inline float3 -triangle_smooth_normal(ccl_global const KernelGlobals *kg, float3 Ng, int prim, float u, float v) +triangle_smooth_normal(KernelGlobals kg, float3 Ng, int prim, float u, float v) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -116,12 +115,8 @@ triangle_smooth_normal(ccl_global const KernelGlobals *kg, float3 Ng, int prim, return is_zero(N) ? Ng : N; } -ccl_device_inline float3 triangle_smooth_normal_unnormalized(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd, - float3 Ng, - int prim, - float u, - float v) +ccl_device_inline float3 triangle_smooth_normal_unnormalized( + KernelGlobals kg, ccl_private const ShaderData *sd, float3 Ng, int prim, float u, float v) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -143,7 +138,7 @@ ccl_device_inline float3 triangle_smooth_normal_unnormalized(ccl_global const Ke /* Ray differentials on triangle */ -ccl_device_inline void triangle_dPdudv(ccl_global const KernelGlobals *kg, +ccl_device_inline void triangle_dPdudv(KernelGlobals kg, int prim, ccl_private float3 *dPdu, ccl_private float3 *dPdv) @@ -161,7 +156,7 @@ ccl_device_inline void triangle_dPdudv(ccl_global const KernelGlobals *kg, /* Reading attributes on various triangle elements */ -ccl_device float triangle_attribute_float(ccl_global const KernelGlobals *kg, +ccl_device float triangle_attribute_float(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float *dx, @@ -211,7 +206,7 @@ ccl_device float triangle_attribute_float(ccl_global const KernelGlobals *kg, } } -ccl_device float2 triangle_attribute_float2(ccl_global const KernelGlobals *kg, +ccl_device float2 triangle_attribute_float2(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float2 *dx, @@ -261,7 +256,7 @@ ccl_device float2 triangle_attribute_float2(ccl_global const KernelGlobals *kg, } } -ccl_device float3 triangle_attribute_float3(ccl_global const KernelGlobals *kg, +ccl_device float3 triangle_attribute_float3(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float3 *dx, @@ -311,7 +306,7 @@ ccl_device float3 triangle_attribute_float3(ccl_global const KernelGlobals *kg, } } -ccl_device float4 triangle_attribute_float4(ccl_global const KernelGlobals *kg, +ccl_device float4 triangle_attribute_float4(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float4 *dx, diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h index f637206da19..fee629cc75a 100644 --- a/intern/cycles/kernel/geom/geom_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h @@ -26,7 +26,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_inline bool triangle_intersect(ccl_global const KernelGlobals *kg, +ccl_device_inline bool triangle_intersect(KernelGlobals kg, ccl_private Intersection *isect, float3 P, float3 dir, @@ -85,7 +85,7 @@ ccl_device_inline bool triangle_intersect(ccl_global const KernelGlobals *kg, */ #ifdef __BVH_LOCAL__ -ccl_device_inline bool triangle_intersect_local(ccl_global const KernelGlobals *kg, +ccl_device_inline bool triangle_intersect_local(KernelGlobals kg, ccl_private LocalIntersection *local_isect, float3 P, float3 dir, @@ -200,7 +200,7 @@ ccl_device_inline bool triangle_intersect_local(ccl_global const KernelGlobals * * http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf */ -ccl_device_inline float3 triangle_refine(ccl_global const KernelGlobals *kg, +ccl_device_inline float3 triangle_refine(KernelGlobals kg, ccl_private ShaderData *sd, float3 P, float3 D, @@ -256,7 +256,7 @@ ccl_device_inline float3 triangle_refine(ccl_global const KernelGlobals *kg, /* Same as above, except that t is assumed to be in object space for * instancing. */ -ccl_device_inline float3 triangle_refine_local(ccl_global const KernelGlobals *kg, +ccl_device_inline float3 triangle_refine_local(KernelGlobals kg, ccl_private ShaderData *sd, float3 P, float3 D, diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h index c466c3fb07a..4e83ad6acb3 100644 --- a/intern/cycles/kernel/geom/geom_volume.h +++ b/intern/cycles/kernel/geom/geom_volume.h @@ -31,7 +31,7 @@ CCL_NAMESPACE_BEGIN /* Return position normalized to 0..1 in mesh bounds */ -ccl_device_inline float3 volume_normalized_position(ccl_global const KernelGlobals *kg, +ccl_device_inline float3 volume_normalized_position(KernelGlobals kg, ccl_private const ShaderData *sd, float3 P) { @@ -70,7 +70,7 @@ ccl_device float3 volume_attribute_value_to_float3(const float4 value) } } -ccl_device float4 volume_attribute_float4(ccl_global const KernelGlobals *kg, +ccl_device float4 volume_attribute_float4(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc) { diff --git a/intern/cycles/kernel/integrator/integrator_init_from_bake.h b/intern/cycles/kernel/integrator/integrator_init_from_bake.h index c822823de9c..df3c2103c5b 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_bake.h +++ b/intern/cycles/kernel/integrator/integrator_init_from_bake.h @@ -43,7 +43,8 @@ ccl_device_inline float bake_clamp_mirror_repeat(float u, float max) /* Return false to indicate that this pixel is finished. * Used by CPU implementation to not attempt to sample pixel for multiple samples once its known * that the pixel did converge. */ -ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, +ccl_device bool integrator_init_from_bake(KernelGlobals kg, + IntegratorState state, ccl_global const KernelWorkTile *ccl_restrict tile, ccl_global float *render_buffer, const int x, @@ -53,18 +54,18 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, PROFILING_INIT(kg, PROFILING_RAY_SETUP); /* Initialize path state to give basic buffer access and allow early outputs. */ - path_state_init(INTEGRATOR_STATE_PASS, tile, x, y); + path_state_init(state, tile, x, y); /* Check whether the pixel has converged and should not be sampled anymore. */ - if (!kernel_need_sample_pixel(INTEGRATOR_STATE_PASS, render_buffer)) { + if (!kernel_need_sample_pixel(kg, state, render_buffer)) { return false; } /* Always count the sample, even if the camera sample will reject the ray. */ - const int sample = kernel_accum_sample(INTEGRATOR_STATE_PASS, render_buffer, scheduled_sample); + const int sample = kernel_accum_sample(kg, state, render_buffer, scheduled_sample); /* Setup render buffers. */ - const int index = INTEGRATOR_STATE(path, render_pixel_index); + const int index = INTEGRATOR_STATE(state, path, render_pixel_index); const int pass_stride = kernel_data.film.pass_stride; render_buffer += index * pass_stride; @@ -91,7 +92,7 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, } /* Initialize path state for path integration. */ - path_state_init_integrator(INTEGRATOR_STATE_PASS, sample, rng_hash); + path_state_init_integrator(kg, state, sample, rng_hash); /* Barycentric UV with sub-pixel offset. */ float u = primitive[2]; @@ -131,7 +132,7 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, ray.time = 0.5f; ray.dP = differential_zero_compact(); ray.dD = differential_zero_compact(); - integrator_state_write_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_write_ray(kg, state, &ray); /* Setup next kernel to execute. */ INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); @@ -169,7 +170,7 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, ray.dD = differential_zero_compact(); /* Write ray. */ - integrator_state_write_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_write_ray(kg, state, &ray); /* Setup and write intersection. */ Intersection isect ccl_optional_struct_init; @@ -182,7 +183,7 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS, #ifdef __EMBREE__ isect.Ng = Ng; #endif - integrator_state_write_isect(INTEGRATOR_STATE_PASS, &isect); + integrator_state_write_isect(kg, state, &isect); /* Setup next kernel to execute. */ const int shader_index = shader & SHADER_MASK; diff --git a/intern/cycles/kernel/integrator/integrator_init_from_camera.h b/intern/cycles/kernel/integrator/integrator_init_from_camera.h index 291f0f106f0..5bab6b2e2fd 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_camera.h +++ b/intern/cycles/kernel/integrator/integrator_init_from_camera.h @@ -25,7 +25,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_inline void integrate_camera_sample(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device_inline void integrate_camera_sample(KernelGlobals kg, const int sample, const int x, const int y, @@ -63,7 +63,8 @@ ccl_device_inline void integrate_camera_sample(ccl_global const KernelGlobals *c /* Return false to indicate that this pixel is finished. * Used by CPU implementation to not attempt to sample pixel for multiple samples once its known * that the pixel did converge. */ -ccl_device bool integrator_init_from_camera(INTEGRATOR_STATE_ARGS, +ccl_device bool integrator_init_from_camera(KernelGlobals kg, + IntegratorState state, ccl_global const KernelWorkTile *ccl_restrict tile, ccl_global float *render_buffer, const int x, @@ -73,10 +74,10 @@ ccl_device bool integrator_init_from_camera(INTEGRATOR_STATE_ARGS, PROFILING_INIT(kg, PROFILING_RAY_SETUP); /* Initialize path state to give basic buffer access and allow early outputs. */ - path_state_init(INTEGRATOR_STATE_PASS, tile, x, y); + path_state_init(state, tile, x, y); /* Check whether the pixel has converged and should not be sampled anymore. */ - if (!kernel_need_sample_pixel(INTEGRATOR_STATE_PASS, render_buffer)) { + if (!kernel_need_sample_pixel(kg, state, render_buffer)) { return false; } @@ -85,7 +86,7 @@ ccl_device bool integrator_init_from_camera(INTEGRATOR_STATE_ARGS, * This logic allows to both count actual number of samples per pixel, and to add samples to this * pixel after it was converged and samples were added somewhere else (in which case the * `scheduled_sample` will be different from actual number of samples in this pixel). */ - const int sample = kernel_accum_sample(INTEGRATOR_STATE_PASS, render_buffer, scheduled_sample); + const int sample = kernel_accum_sample(kg, state, render_buffer, scheduled_sample); /* Initialize random number seed for path. */ const uint rng_hash = path_rng_hash_init(kg, sample, x, y); @@ -99,11 +100,11 @@ ccl_device bool integrator_init_from_camera(INTEGRATOR_STATE_ARGS, } /* Write camera ray to state. */ - integrator_state_write_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_write_ray(kg, state, &ray); } /* Initialize path state for path integration. */ - path_state_init_integrator(INTEGRATOR_STATE_PASS, sample, rng_hash); + path_state_init_integrator(kg, state, sample, rng_hash); /* Continue with intersect_closest kernel, optionally initializing volume * stack before that if the camera may be inside a volume. */ diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/integrator_intersect_closest.h index 760c08159e3..e915d984e1d 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_closest.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_closest.h @@ -29,7 +29,8 @@ CCL_NAMESPACE_BEGIN template -ccl_device_forceinline bool integrator_intersect_terminate(INTEGRATOR_STATE_ARGS, +ccl_device_forceinline bool integrator_intersect_terminate(KernelGlobals kg, + IntegratorState state, const int shader_flags) { @@ -37,12 +38,12 @@ ccl_device_forceinline bool integrator_intersect_terminate(INTEGRATOR_STATE_ARGS * We continue evaluating emissive/transparent surfaces and volumes, similar * to direct lighting. Only if we know there are none can we terminate the * path immediately. */ - if (path_state_ao_bounce(INTEGRATOR_STATE_PASS)) { + if (path_state_ao_bounce(kg, state)) { if (shader_flags & (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) { - INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } - else if (!integrator_state_volume_stack_is_empty(INTEGRATOR_STATE_PASS)) { - INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_TERMINATE_AFTER_VOLUME; + else if (!integrator_state_volume_stack_is_empty(kg, state)) { + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_AFTER_VOLUME; } else { return true; @@ -51,14 +52,14 @@ ccl_device_forceinline bool integrator_intersect_terminate(INTEGRATOR_STATE_ARGS /* Load random number state. */ RNGState rng_state; - path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + path_state_rng_load(state, &rng_state); /* We perform path termination in this kernel to avoid launching shade_surface * and evaluating the shader when not needed. Only for emission and transparent * surfaces in front of emission do we need to evaluate the shader, since we * perform MIS as part of indirect rays. */ - const int path_flag = INTEGRATOR_STATE(path, flag); - const float probability = path_state_continuation_probability(INTEGRATOR_STATE_PASS, path_flag); + const int path_flag = INTEGRATOR_STATE(state, path, flag); + const float probability = path_state_continuation_probability(kg, state, path_flag); if (probability != 1.0f) { const float terminate = path_state_rng_1D(kg, &rng_state, PRNG_TERMINATE); @@ -66,11 +67,11 @@ ccl_device_forceinline bool integrator_intersect_terminate(INTEGRATOR_STATE_ARGS if (probability == 0.0f || terminate >= probability) { if (shader_flags & SD_HAS_EMISSION) { /* Mark path to be terminated right after shader evaluation on the surface. */ - INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_TERMINATE_ON_NEXT_SURFACE; + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_ON_NEXT_SURFACE; } - else if (!integrator_state_volume_stack_is_empty(INTEGRATOR_STATE_PASS)) { + else if (!integrator_state_volume_stack_is_empty(kg, state)) { /* TODO: only do this for emissive volumes. */ - INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_TERMINATE_IN_NEXT_VOLUME; + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_IN_NEXT_VOLUME; } else { return true; @@ -85,7 +86,8 @@ ccl_device_forceinline bool integrator_intersect_terminate(INTEGRATOR_STATE_ARGS * leads to poor performance with CUDA atomics. */ template ccl_device_forceinline void integrator_intersect_shader_next_kernel( - INTEGRATOR_STATE_ARGS, + KernelGlobals kg, + IntegratorState state, ccl_private const Intersection *ccl_restrict isect, const int shader, const int shader_flags) @@ -122,9 +124,9 @@ ccl_device_forceinline void integrator_intersect_shader_next_kernel( #ifdef __SHADOW_CATCHER__ const int object_flags = intersection_get_object_flags(kg, isect); - if (kernel_shadow_catcher_split(INTEGRATOR_STATE_PASS, object_flags)) { + if (kernel_shadow_catcher_split(kg, state, object_flags)) { if (kernel_data.film.pass_background != PASS_UNUSED && !kernel_data.background.transparent) { - INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_SHADOW_CATCHER_BACKGROUND; + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SHADOW_CATCHER_BACKGROUND; INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); } @@ -137,7 +139,7 @@ ccl_device_forceinline void integrator_intersect_shader_next_kernel( /* If the split happened after bounce through a transparent object it's possible to have shadow * patch. Make sure it is properly re-scheduled on the split path. */ - const int shadow_kernel = INTEGRATOR_STATE(shadow_path, queued_kernel); + const int shadow_kernel = INTEGRATOR_STATE(state, shadow_path, queued_kernel); if (shadow_kernel != 0) { INTEGRATOR_SHADOW_PATH_INIT(shadow_kernel); } @@ -145,21 +147,21 @@ ccl_device_forceinline void integrator_intersect_shader_next_kernel( #endif } -ccl_device void integrator_intersect_closest(INTEGRATOR_STATE_ARGS) +ccl_device void integrator_intersect_closest(KernelGlobals kg, IntegratorState state) { PROFILING_INIT(kg, PROFILING_INTERSECT_CLOSEST); /* Read ray from integrator state into local memory. */ Ray ray ccl_optional_struct_init; - integrator_state_read_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_read_ray(kg, state, &ray); kernel_assert(ray.t != 0.0f); - const uint visibility = path_state_ray_visibility(INTEGRATOR_STATE_PASS); - const int last_isect_prim = INTEGRATOR_STATE(isect, prim); - const int last_isect_object = INTEGRATOR_STATE(isect, object); + const uint visibility = path_state_ray_visibility(state); + const int last_isect_prim = INTEGRATOR_STATE(state, isect, prim); + const int last_isect_object = INTEGRATOR_STATE(state, isect, object); /* Trick to use short AO rays to approximate indirect light at the end of the path. */ - if (path_state_ao_bounce(INTEGRATOR_STATE_PASS)) { + if (path_state_ao_bounce(kg, state)) { ray.t = kernel_data.integrator.ao_bounces_distance; const float object_ao_distance = kernel_tex_fetch(__objects, last_isect_object).ao_distance; @@ -181,8 +183,8 @@ ccl_device void integrator_intersect_closest(INTEGRATOR_STATE_ARGS) if (kernel_data.integrator.use_lamp_mis) { /* NOTE: if we make lights visible to camera rays, we'll need to initialize * these in the path_state_init. */ - const int last_type = INTEGRATOR_STATE(isect, type); - const int path_flag = INTEGRATOR_STATE(path, flag); + const int last_type = INTEGRATOR_STATE(state, isect, type); + const int path_flag = INTEGRATOR_STATE(state, path, flag); hit = lights_intersect( kg, &ray, &isect, last_isect_prim, last_isect_object, last_type, path_flag) || @@ -190,16 +192,16 @@ ccl_device void integrator_intersect_closest(INTEGRATOR_STATE_ARGS) } /* Write intersection result into global integrator state memory. */ - integrator_state_write_isect(INTEGRATOR_STATE_PASS, &isect); + integrator_state_write_isect(kg, state, &isect); #ifdef __VOLUME__ - if (!integrator_state_volume_stack_is_empty(INTEGRATOR_STATE_PASS)) { + if (!integrator_state_volume_stack_is_empty(kg, state)) { const bool hit_surface = hit && !(isect.type & PRIMITIVE_LAMP); const int shader = (hit_surface) ? intersection_get_shader(kg, &isect) : SHADER_NONE; const int flags = (hit_surface) ? kernel_tex_fetch(__shaders, shader).flags : 0; if (!integrator_intersect_terminate( - INTEGRATOR_STATE_PASS, flags)) { + kg, state, flags)) { /* Continue with volume kernel if we are inside a volume, regardless * if we hit anything. */ INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST, @@ -225,9 +227,9 @@ ccl_device void integrator_intersect_closest(INTEGRATOR_STATE_ARGS) const int flags = kernel_tex_fetch(__shaders, shader).flags; if (!integrator_intersect_terminate( - INTEGRATOR_STATE_PASS, flags)) { + kg, state, flags)) { integrator_intersect_shader_next_kernel( - INTEGRATOR_STATE_PASS, &isect, shader, flags); + kg, state, &isect, shader, flags); return; } else { diff --git a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h index 3ebd21e4651..06f58f88bc8 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h @@ -19,19 +19,21 @@ CCL_NAMESPACE_BEGIN /* Visibility for the shadow ray. */ -ccl_device_forceinline uint integrate_intersect_shadow_visibility(INTEGRATOR_STATE_CONST_ARGS) +ccl_device_forceinline uint integrate_intersect_shadow_visibility(KernelGlobals kg, + ConstIntegratorState state) { uint visibility = PATH_RAY_SHADOW; #ifdef __SHADOW_CATCHER__ - const uint32_t path_flag = INTEGRATOR_STATE(shadow_path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag); visibility = SHADOW_CATCHER_PATH_VISIBILITY(path_flag, visibility); #endif return visibility; } -ccl_device bool integrate_intersect_shadow_opaque(INTEGRATOR_STATE_ARGS, +ccl_device bool integrate_intersect_shadow_opaque(KernelGlobals kg, + IntegratorState state, ccl_private const Ray *ray, const uint visibility) { @@ -46,22 +48,24 @@ ccl_device bool integrate_intersect_shadow_opaque(INTEGRATOR_STATE_ARGS, const bool opaque_hit = scene_intersect(kg, ray, visibility & opaque_mask, &isect); if (!opaque_hit) { - INTEGRATOR_STATE_WRITE(shadow_path, num_hits) = 0; + INTEGRATOR_STATE_WRITE(state, shadow_path, num_hits) = 0; } return opaque_hit; } -ccl_device_forceinline int integrate_shadow_max_transparent_hits(INTEGRATOR_STATE_CONST_ARGS) +ccl_device_forceinline int integrate_shadow_max_transparent_hits(KernelGlobals kg, + ConstIntegratorState state) { const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce; - const int transparent_bounce = INTEGRATOR_STATE(shadow_path, transparent_bounce); + const int transparent_bounce = INTEGRATOR_STATE(state, shadow_path, transparent_bounce); return max(transparent_max_bounce - transparent_bounce - 1, 0); } #ifdef __TRANSPARENT_SHADOWS__ -ccl_device bool integrate_intersect_shadow_transparent(INTEGRATOR_STATE_ARGS, +ccl_device bool integrate_intersect_shadow_transparent(KernelGlobals kg, + IntegratorState state, ccl_private const Ray *ray, const uint visibility) { @@ -69,7 +73,7 @@ ccl_device bool integrate_intersect_shadow_transparent(INTEGRATOR_STATE_ARGS, /* Limit the number hits to the max transparent bounces allowed and the size that we * have available in the integrator state. */ - const uint max_transparent_hits = integrate_shadow_max_transparent_hits(INTEGRATOR_STATE_PASS); + const uint max_transparent_hits = integrate_shadow_max_transparent_hits(kg, state); const uint max_hits = min(max_transparent_hits, (uint)INTEGRATOR_SHADOW_ISECT_SIZE); uint num_hits = 0; bool opaque_hit = scene_intersect_shadow_all(kg, ray, isect, visibility, max_hits, &num_hits); @@ -88,41 +92,39 @@ ccl_device bool integrate_intersect_shadow_transparent(INTEGRATOR_STATE_ARGS, /* Write intersection result into global integrator state memory. * More efficient may be to do this directly from the intersection kernel. */ for (int hit = 0; hit < num_recorded_hits; hit++) { - integrator_state_write_shadow_isect(INTEGRATOR_STATE_PASS, &isect[hit], hit); + integrator_state_write_shadow_isect(state, &isect[hit], hit); } } - INTEGRATOR_STATE_WRITE(shadow_path, num_hits) = num_hits; + INTEGRATOR_STATE_WRITE(state, shadow_path, num_hits) = num_hits; } else { - INTEGRATOR_STATE_WRITE(shadow_path, num_hits) = 0; + INTEGRATOR_STATE_WRITE(state, shadow_path, num_hits) = 0; } return opaque_hit; } #endif -ccl_device void integrator_intersect_shadow(INTEGRATOR_STATE_ARGS) +ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorState state) { PROFILING_INIT(kg, PROFILING_INTERSECT_SHADOW); /* Read ray from integrator state into local memory. */ Ray ray ccl_optional_struct_init; - integrator_state_read_shadow_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_read_shadow_ray(kg, state, &ray); /* Compute visibility. */ - const uint visibility = integrate_intersect_shadow_visibility(INTEGRATOR_STATE_PASS); + const uint visibility = integrate_intersect_shadow_visibility(kg, state); #ifdef __TRANSPARENT_SHADOWS__ /* TODO: compile different kernels depending on this? Especially for OptiX * conditional trace calls are bad. */ - const bool opaque_hit = - (kernel_data.integrator.transparent_shadows) ? - integrate_intersect_shadow_transparent(INTEGRATOR_STATE_PASS, &ray, visibility) : - integrate_intersect_shadow_opaque(INTEGRATOR_STATE_PASS, &ray, visibility); + const bool opaque_hit = (kernel_data.integrator.transparent_shadows) ? + integrate_intersect_shadow_transparent(kg, state, &ray, visibility) : + integrate_intersect_shadow_opaque(kg, state, &ray, visibility); #else - const bool opaque_hit = integrate_intersect_shadow_opaque( - INTEGRATOR_STATE_PASS, &ray, visibility); + const bool opaque_hit = integrate_intersect_shadow_opaque(kg, state, &ray, visibility); #endif if (opaque_hit) { diff --git a/intern/cycles/kernel/integrator/integrator_intersect_subsurface.h b/intern/cycles/kernel/integrator/integrator_intersect_subsurface.h index 7c090952dc7..b575e7fd1e6 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_subsurface.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_subsurface.h @@ -20,12 +20,12 @@ CCL_NAMESPACE_BEGIN -ccl_device void integrator_intersect_subsurface(INTEGRATOR_STATE_ARGS) +ccl_device void integrator_intersect_subsurface(KernelGlobals kg, IntegratorState state) { PROFILING_INIT(kg, PROFILING_INTERSECT_SUBSURFACE); #ifdef __SUBSURFACE__ - if (subsurface_scatter(INTEGRATOR_STATE_PASS)) { + if (subsurface_scatter(kg, state)) { return; } #endif diff --git a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h index 192e9c6ab43..7def3e2f3f3 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h @@ -23,7 +23,8 @@ CCL_NAMESPACE_BEGIN -ccl_device void integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_ARGS, +ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, + IntegratorState state, const float3 from_P, const float3 to_P) { @@ -52,7 +53,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_A for (uint hit = 0; hit < num_hits; ++hit, ++isect) { shader_setup_from_ray(kg, stack_sd, &volume_ray, isect); - volume_stack_enter_exit(INTEGRATOR_STATE_PASS, stack_sd); + volume_stack_enter_exit(kg, state, stack_sd); } } #else @@ -61,7 +62,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_A while (step < 2 * volume_stack_size && scene_intersect_volume(kg, &volume_ray, &isect, PATH_RAY_ALL_VISIBILITY)) { shader_setup_from_ray(kg, stack_sd, &volume_ray, &isect); - volume_stack_enter_exit(INTEGRATOR_STATE_PASS, stack_sd); + volume_stack_enter_exit(kg, state, stack_sd); /* Move ray forward. */ volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); @@ -73,7 +74,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_A #endif } -ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) +ccl_device void integrator_intersect_volume_stack(KernelGlobals kg, IntegratorState state) { PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_STACK); @@ -81,16 +82,16 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) ccl_private ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage); Ray volume_ray ccl_optional_struct_init; - integrator_state_read_ray(INTEGRATOR_STATE_PASS, &volume_ray); + integrator_state_read_ray(kg, state, &volume_ray); volume_ray.t = FLT_MAX; - const uint visibility = (INTEGRATOR_STATE(path, flag) & PATH_RAY_ALL_VISIBILITY); + const uint visibility = (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_ALL_VISIBILITY); int stack_index = 0, enclosed_index = 0; /* Write background shader. */ if (kernel_data.background.volume_shader != SHADER_NONE) { const VolumeStack new_entry = {OBJECT_NONE, kernel_data.background.volume_shader}; - integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, stack_index, new_entry); + integrator_state_write_volume_stack(state, stack_index, new_entry); stack_index++; } @@ -121,7 +122,7 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) } for (int i = 0; i < stack_index && need_add; ++i) { /* Don't add intersections twice. */ - VolumeStack entry = integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); + VolumeStack entry = integrator_state_read_volume_stack(state, i); if (entry.object == stack_sd->object) { need_add = false; break; @@ -129,7 +130,7 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) } if (need_add && stack_index < volume_stack_size - 1) { const VolumeStack new_entry = {stack_sd->object, stack_sd->shader}; - integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, stack_index, new_entry); + integrator_state_write_volume_stack(state, stack_index, new_entry); ++stack_index; } } @@ -169,7 +170,7 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) } for (int i = 0; i < stack_index && need_add; ++i) { /* Don't add intersections twice. */ - VolumeStack entry = integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); + VolumeStack entry = integrator_state_read_volume_stack(state, i); if (entry.object == stack_sd->object) { need_add = false; break; @@ -177,7 +178,7 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) } if (need_add) { const VolumeStack new_entry = {stack_sd->object, stack_sd->shader}; - integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, stack_index, new_entry); + integrator_state_write_volume_stack(state, stack_index, new_entry); ++stack_index; } } @@ -196,7 +197,7 @@ ccl_device void integrator_intersect_volume_stack(INTEGRATOR_STATE_ARGS) /* Write terminator. */ const VolumeStack new_entry = {OBJECT_NONE, SHADER_NONE}; - integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, stack_index, new_entry); + integrator_state_write_volume_stack(state, stack_index, new_entry); INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); diff --git a/intern/cycles/kernel/integrator/integrator_megakernel.h b/intern/cycles/kernel/integrator/integrator_megakernel.h index 91363ea1c7f..a3b2b1f9e90 100644 --- a/intern/cycles/kernel/integrator/integrator_megakernel.h +++ b/intern/cycles/kernel/integrator/integrator_megakernel.h @@ -29,7 +29,8 @@ CCL_NAMESPACE_BEGIN -ccl_device void integrator_megakernel(INTEGRATOR_STATE_ARGS, +ccl_device void integrator_megakernel(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { /* Each kernel indicates the next kernel to execute, so here we simply @@ -38,46 +39,46 @@ ccl_device void integrator_megakernel(INTEGRATOR_STATE_ARGS, * TODO: investigate if we can use device side enqueue for GPUs to avoid * having to compile this big kernel. */ while (true) { - if (INTEGRATOR_STATE(shadow_path, queued_kernel)) { + if (INTEGRATOR_STATE(state, shadow_path, queued_kernel)) { /* First handle any shadow paths before we potentially create more shadow paths. */ - switch (INTEGRATOR_STATE(shadow_path, queued_kernel)) { + switch (INTEGRATOR_STATE(state, shadow_path, queued_kernel)) { case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: - integrator_intersect_shadow(INTEGRATOR_STATE_PASS); + integrator_intersect_shadow(kg, state); break; case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: - integrator_shade_shadow(INTEGRATOR_STATE_PASS, render_buffer); + integrator_shade_shadow(kg, state, render_buffer); break; default: kernel_assert(0); break; } } - else if (INTEGRATOR_STATE(path, queued_kernel)) { + else if (INTEGRATOR_STATE(state, path, queued_kernel)) { /* Then handle regular path kernels. */ - switch (INTEGRATOR_STATE(path, queued_kernel)) { + switch (INTEGRATOR_STATE(state, path, queued_kernel)) { case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: - integrator_intersect_closest(INTEGRATOR_STATE_PASS); + integrator_intersect_closest(kg, state); break; case DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND: - integrator_shade_background(INTEGRATOR_STATE_PASS, render_buffer); + integrator_shade_background(kg, state, render_buffer); break; case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE: - integrator_shade_surface(INTEGRATOR_STATE_PASS, render_buffer); + integrator_shade_surface(kg, state, render_buffer); break; case DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME: - integrator_shade_volume(INTEGRATOR_STATE_PASS, render_buffer); + integrator_shade_volume(kg, state, render_buffer); break; case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE: - integrator_shade_surface_raytrace(INTEGRATOR_STATE_PASS, render_buffer); + integrator_shade_surface_raytrace(kg, state, render_buffer); break; case DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT: - integrator_shade_light(INTEGRATOR_STATE_PASS, render_buffer); + integrator_shade_light(kg, state, render_buffer); break; case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE: - integrator_intersect_subsurface(INTEGRATOR_STATE_PASS); + integrator_intersect_subsurface(kg, state); break; case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: - integrator_intersect_volume_stack(INTEGRATOR_STATE_PASS); + integrator_intersect_volume_stack(kg, state); break; default: kernel_assert(0); diff --git a/intern/cycles/kernel/integrator/integrator_shade_background.h b/intern/cycles/kernel/integrator/integrator_shade_background.h index a898f3fb2fc..d98e53e6bbf 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_background.h +++ b/intern/cycles/kernel/integrator/integrator_shade_background.h @@ -23,12 +23,13 @@ CCL_NAMESPACE_BEGIN -ccl_device float3 integrator_eval_background_shader(INTEGRATOR_STATE_ARGS, +ccl_device float3 integrator_eval_background_shader(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { #ifdef __BACKGROUND__ const int shader = kernel_data.background.surface_shader; - const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); /* Use visibility flag to skip lights. */ if (shader & SHADER_EXCLUDE_ANY) { @@ -54,14 +55,14 @@ ccl_device float3 integrator_eval_background_shader(INTEGRATOR_STATE_ARGS, PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_LIGHT_SETUP); shader_setup_from_background(kg, emission_sd, - INTEGRATOR_STATE(ray, P), - INTEGRATOR_STATE(ray, D), - INTEGRATOR_STATE(ray, time)); + INTEGRATOR_STATE(state, ray, P), + INTEGRATOR_STATE(state, ray, D), + INTEGRATOR_STATE(state, ray, time)); PROFILING_SHADER(emission_sd->object, emission_sd->shader); PROFILING_EVENT(PROFILING_SHADE_LIGHT_EVAL); shader_eval_surface( - INTEGRATOR_STATE_PASS, emission_sd, render_buffer, path_flag | PATH_RAY_EMISSION); + kg, state, emission_sd, render_buffer, path_flag | PATH_RAY_EMISSION); L = shader_background_eval(emission_sd); } @@ -69,11 +70,12 @@ ccl_device float3 integrator_eval_background_shader(INTEGRATOR_STATE_ARGS, /* Background MIS weights. */ # ifdef __BACKGROUND_MIS__ /* Check if background light exists or if we should skip pdf. */ - if (!(INTEGRATOR_STATE(path, flag) & PATH_RAY_MIS_SKIP) && kernel_data.background.use_mis) { - const float3 ray_P = INTEGRATOR_STATE(ray, P); - const float3 ray_D = INTEGRATOR_STATE(ray, D); - const float mis_ray_pdf = INTEGRATOR_STATE(path, mis_ray_pdf); - const float mis_ray_t = INTEGRATOR_STATE(path, mis_ray_t); + if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_MIS_SKIP) && + kernel_data.background.use_mis) { + const float3 ray_P = INTEGRATOR_STATE(state, ray, P); + const float3 ray_D = INTEGRATOR_STATE(state, ray, D); + const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); + const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); /* multiple importance sampling, get background light pdf for ray * direction, and compute weight with respect to BSDF pdf */ @@ -90,7 +92,8 @@ ccl_device float3 integrator_eval_background_shader(INTEGRATOR_STATE_ARGS, #endif } -ccl_device_inline void integrate_background(INTEGRATOR_STATE_ARGS, +ccl_device_inline void integrate_background(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { /* Accumulate transparency for transparent background. We can skip background @@ -99,11 +102,11 @@ ccl_device_inline void integrate_background(INTEGRATOR_STATE_ARGS, float transparent = 0.0f; const bool is_transparent_background_ray = kernel_data.background.transparent && - (INTEGRATOR_STATE(path, flag) & + (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TRANSPARENT_BACKGROUND); if (is_transparent_background_ray) { - transparent = average(INTEGRATOR_STATE(path, throughput)); + transparent = average(INTEGRATOR_STATE(state, path, throughput)); #ifdef __PASSES__ eval_background = (kernel_data.film.light_pass_flag & PASSMASK(BACKGROUND)); @@ -113,32 +116,31 @@ ccl_device_inline void integrate_background(INTEGRATOR_STATE_ARGS, } /* Evaluate background shader. */ - float3 L = (eval_background) ? - integrator_eval_background_shader(INTEGRATOR_STATE_PASS, render_buffer) : - zero_float3(); + float3 L = (eval_background) ? integrator_eval_background_shader(kg, state, render_buffer) : + zero_float3(); /* When using the ao bounces approximation, adjust background * shader intensity with ao factor. */ - if (path_state_ao_bounce(INTEGRATOR_STATE_PASS)) { + if (path_state_ao_bounce(kg, state)) { L *= kernel_data.integrator.ao_bounces_factor; } /* Write to render buffer. */ - kernel_accum_background( - INTEGRATOR_STATE_PASS, L, transparent, is_transparent_background_ray, render_buffer); + kernel_accum_background(kg, state, L, transparent, is_transparent_background_ray, render_buffer); } -ccl_device_inline void integrate_distant_lights(INTEGRATOR_STATE_ARGS, +ccl_device_inline void integrate_distant_lights(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { - const float3 ray_D = INTEGRATOR_STATE(ray, D); - const float ray_time = INTEGRATOR_STATE(ray, time); + const float3 ray_D = INTEGRATOR_STATE(state, ray, D); + const float ray_time = INTEGRATOR_STATE(state, ray, time); LightSample ls ccl_optional_struct_init; for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { if (light_sample_from_distant_ray(kg, ray_D, lamp, &ls)) { /* Use visibility flag to skip lights. */ #ifdef __PASSES__ - const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (ls.shader & SHADER_EXCLUDE_ANY) { if (((ls.shader & SHADER_EXCLUDE_DIFFUSE) && (path_flag & PATH_RAY_DIFFUSE)) || @@ -156,8 +158,7 @@ ccl_device_inline void integrate_distant_lights(INTEGRATOR_STATE_ARGS, /* TODO: does aliasing like this break automatic SoA in CUDA? */ ShaderDataTinyStorage emission_sd_storage; ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - float3 light_eval = light_sample_shader_eval( - INTEGRATOR_STATE_PASS, emission_sd, &ls, ray_time); + float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, ray_time); if (is_zero(light_eval)) { return; } @@ -166,33 +167,34 @@ ccl_device_inline void integrate_distant_lights(INTEGRATOR_STATE_ARGS, if (!(path_flag & PATH_RAY_MIS_SKIP)) { /* multiple importance sampling, get regular light pdf, * and compute weight with respect to BSDF pdf */ - const float mis_ray_pdf = INTEGRATOR_STATE(path, mis_ray_pdf); + const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); const float mis_weight = power_heuristic(mis_ray_pdf, ls.pdf); light_eval *= mis_weight; } /* Write to render buffer. */ - const float3 throughput = INTEGRATOR_STATE(path, throughput); - kernel_accum_emission(INTEGRATOR_STATE_PASS, throughput, light_eval, render_buffer); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_accum_emission(kg, state, throughput, light_eval, render_buffer); } } } -ccl_device void integrator_shade_background(INTEGRATOR_STATE_ARGS, +ccl_device void integrator_shade_background(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { PROFILING_INIT(kg, PROFILING_SHADE_LIGHT_SETUP); /* TODO: unify these in a single loop to only have a single shader evaluation call. */ - integrate_distant_lights(INTEGRATOR_STATE_PASS, render_buffer); - integrate_background(INTEGRATOR_STATE_PASS, render_buffer); + integrate_distant_lights(kg, state, render_buffer); + integrate_background(kg, state, render_buffer); #ifdef __SHADOW_CATCHER__ - if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { - INTEGRATOR_STATE_WRITE(path, flag) &= ~PATH_RAY_SHADOW_CATCHER_BACKGROUND; + if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { + INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_SHADOW_CATCHER_BACKGROUND; - const int isect_prim = INTEGRATOR_STATE(isect, prim); - const int isect_type = INTEGRATOR_STATE(isect, type); + const int isect_prim = INTEGRATOR_STATE(state, isect, prim); + const int isect_type = INTEGRATOR_STATE(state, isect, type); const int shader = intersection_get_shader_from_isect_prim(kg, isect_prim, isect_type); const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; diff --git a/intern/cycles/kernel/integrator/integrator_shade_light.h b/intern/cycles/kernel/integrator/integrator_shade_light.h index d8f8da63023..4f0f5a39756 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_light.h +++ b/intern/cycles/kernel/integrator/integrator_shade_light.h @@ -23,29 +23,30 @@ CCL_NAMESPACE_BEGIN -ccl_device_inline void integrate_light(INTEGRATOR_STATE_ARGS, +ccl_device_inline void integrate_light(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { /* Setup light sample. */ Intersection isect ccl_optional_struct_init; - integrator_state_read_isect(INTEGRATOR_STATE_PASS, &isect); + integrator_state_read_isect(kg, state, &isect); - float3 ray_P = INTEGRATOR_STATE(ray, P); - const float3 ray_D = INTEGRATOR_STATE(ray, D); - const float ray_time = INTEGRATOR_STATE(ray, time); + float3 ray_P = INTEGRATOR_STATE(state, ray, P); + const float3 ray_D = INTEGRATOR_STATE(state, ray, D); + const float ray_time = INTEGRATOR_STATE(state, ray, time); /* Advance ray beyond light. */ /* TODO: can we make this more numerically robust to avoid reintersecting the * same light in some cases? */ const float3 new_ray_P = ray_offset(ray_P + ray_D * isect.t, ray_D); - INTEGRATOR_STATE_WRITE(ray, P) = new_ray_P; - INTEGRATOR_STATE_WRITE(ray, t) -= isect.t; + INTEGRATOR_STATE_WRITE(state, ray, P) = new_ray_P; + INTEGRATOR_STATE_WRITE(state, ray, t) -= isect.t; /* Set position to where the BSDF was sampled, for correct MIS PDF. */ - const float mis_ray_t = INTEGRATOR_STATE(path, mis_ray_t); + const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); ray_P -= ray_D * mis_ray_t; isect.t += mis_ray_t; - INTEGRATOR_STATE_WRITE(path, mis_ray_t) = mis_ray_t + isect.t; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = mis_ray_t + isect.t; LightSample ls ccl_optional_struct_init; const bool use_light_sample = light_sample_from_intersection(kg, &isect, ray_P, ray_D, &ls); @@ -56,7 +57,7 @@ ccl_device_inline void integrate_light(INTEGRATOR_STATE_ARGS, /* Use visibility flag to skip lights. */ #ifdef __PASSES__ - const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (ls.shader & SHADER_EXCLUDE_ANY) { if (((ls.shader & SHADER_EXCLUDE_DIFFUSE) && (path_flag & PATH_RAY_DIFFUSE)) || @@ -73,7 +74,7 @@ ccl_device_inline void integrate_light(INTEGRATOR_STATE_ARGS, /* TODO: does aliasing like this break automatic SoA in CUDA? */ ShaderDataTinyStorage emission_sd_storage; ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - float3 light_eval = light_sample_shader_eval(INTEGRATOR_STATE_PASS, emission_sd, &ls, ray_time); + float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, ray_time); if (is_zero(light_eval)) { return; } @@ -82,22 +83,23 @@ ccl_device_inline void integrate_light(INTEGRATOR_STATE_ARGS, if (!(path_flag & PATH_RAY_MIS_SKIP)) { /* multiple importance sampling, get regular light pdf, * and compute weight with respect to BSDF pdf */ - const float mis_ray_pdf = INTEGRATOR_STATE(path, mis_ray_pdf); + const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); const float mis_weight = power_heuristic(mis_ray_pdf, ls.pdf); light_eval *= mis_weight; } /* Write to render buffer. */ - const float3 throughput = INTEGRATOR_STATE(path, throughput); - kernel_accum_emission(INTEGRATOR_STATE_PASS, throughput, light_eval, render_buffer); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_accum_emission(kg, state, throughput, light_eval, render_buffer); } -ccl_device void integrator_shade_light(INTEGRATOR_STATE_ARGS, +ccl_device void integrator_shade_light(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { PROFILING_INIT(kg, PROFILING_SHADE_LIGHT_SETUP); - integrate_light(INTEGRATOR_STATE_PASS, render_buffer); + integrate_light(kg, state, render_buffer); /* TODO: we could get stuck in an infinite loop if there are precision issues * and the same light is hit again. @@ -105,8 +107,8 @@ ccl_device void integrator_shade_light(INTEGRATOR_STATE_ARGS, * As a workaround count this as a transparent bounce. It makes some sense * to interpret lights as transparent surfaces (and support making them opaque), * but this needs to be revisited. */ - uint32_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce) + 1; - INTEGRATOR_STATE_WRITE(path, transparent_bounce) = transparent_bounce; + uint32_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, transparent_bounce) = transparent_bounce; if (transparent_bounce >= kernel_data.integrator.transparent_max_bounce) { INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); diff --git a/intern/cycles/kernel/integrator/integrator_shade_shadow.h b/intern/cycles/kernel/integrator/integrator_shade_shadow.h index 3857b522b25..cdbe85f6b8c 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_shade_shadow.h @@ -29,7 +29,9 @@ ccl_device_inline bool shadow_intersections_has_remaining(const int num_hits) } #ifdef __TRANSPARENT_SHADOWS__ -ccl_device_inline float3 integrate_transparent_surface_shadow(INTEGRATOR_STATE_ARGS, const int hit) +ccl_device_inline float3 integrate_transparent_surface_shadow(KernelGlobals kg, + IntegratorState state, + const int hit) { PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SURFACE); @@ -43,22 +45,22 @@ ccl_device_inline float3 integrate_transparent_surface_shadow(INTEGRATOR_STATE_A /* Setup shader data at surface. */ Intersection isect ccl_optional_struct_init; - integrator_state_read_shadow_isect(INTEGRATOR_STATE_PASS, &isect, hit); + integrator_state_read_shadow_isect(state, &isect, hit); Ray ray ccl_optional_struct_init; - integrator_state_read_shadow_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_read_shadow_ray(kg, state, &ray); shader_setup_from_ray(kg, shadow_sd, &ray, &isect); /* Evaluate shader. */ if (!(shadow_sd->flag & SD_HAS_ONLY_VOLUME)) { shader_eval_surface( - INTEGRATOR_STATE_PASS, shadow_sd, NULL, PATH_RAY_SHADOW); + kg, state, shadow_sd, NULL, PATH_RAY_SHADOW); } # ifdef __VOLUME__ /* Exit/enter volume. */ - shadow_volume_stack_enter_exit(INTEGRATOR_STATE_PASS, shadow_sd); + shadow_volume_stack_enter_exit(kg, state, shadow_sd); # endif /* Compute transparency from closures. */ @@ -66,7 +68,8 @@ ccl_device_inline float3 integrate_transparent_surface_shadow(INTEGRATOR_STATE_A } # ifdef __VOLUME__ -ccl_device_inline void integrate_transparent_volume_shadow(INTEGRATOR_STATE_ARGS, +ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg, + IntegratorState state, const int hit, const int num_recorded_hits, ccl_private float3 *ccl_restrict @@ -80,26 +83,29 @@ ccl_device_inline void integrate_transparent_volume_shadow(INTEGRATOR_STATE_ARGS /* Setup shader data. */ Ray ray ccl_optional_struct_init; - integrator_state_read_shadow_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_read_shadow_ray(kg, state, &ray); /* Modify ray position and length to match current segment. */ - const float start_t = (hit == 0) ? 0.0f : INTEGRATOR_STATE_ARRAY(shadow_isect, hit - 1, t); - const float end_t = (hit < num_recorded_hits) ? INTEGRATOR_STATE_ARRAY(shadow_isect, hit, t) : - ray.t; + const float start_t = (hit == 0) ? 0.0f : + INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t); + const float end_t = (hit < num_recorded_hits) ? + INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit, t) : + ray.t; ray.P += start_t * ray.D; ray.t = end_t - start_t; shader_setup_from_volume(kg, shadow_sd, &ray); - const float step_size = volume_stack_step_size(INTEGRATOR_STATE_PASS, [=](const int i) { - return integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_PASS, i); - }); + const float step_size = volume_stack_step_size( + kg, state, [=](const int i) { return integrator_state_read_shadow_volume_stack(state, i); }); - volume_shadow_heterogeneous(INTEGRATOR_STATE_PASS, &ray, shadow_sd, throughput, step_size); + volume_shadow_heterogeneous(kg, state, &ray, shadow_sd, throughput, step_size); } # endif -ccl_device_inline bool integrate_transparent_shadow(INTEGRATOR_STATE_ARGS, const int num_hits) +ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, + IntegratorState state, + const int num_hits) { /* Accumulate shadow for transparent surfaces. */ const int num_recorded_hits = min(num_hits, INTEGRATOR_SHADOW_ISECT_SIZE); @@ -108,29 +114,28 @@ ccl_device_inline bool integrate_transparent_shadow(INTEGRATOR_STATE_ARGS, const /* Volume shaders. */ if (hit < num_recorded_hits || !shadow_intersections_has_remaining(num_hits)) { # ifdef __VOLUME__ - if (!integrator_state_shadow_volume_stack_is_empty(INTEGRATOR_STATE_PASS)) { - float3 throughput = INTEGRATOR_STATE(shadow_path, throughput); - integrate_transparent_volume_shadow( - INTEGRATOR_STATE_PASS, hit, num_recorded_hits, &throughput); + if (!integrator_state_shadow_volume_stack_is_empty(kg, state)) { + float3 throughput = INTEGRATOR_STATE(state, shadow_path, throughput); + integrate_transparent_volume_shadow(kg, state, hit, num_recorded_hits, &throughput); if (is_zero(throughput)) { return true; } - INTEGRATOR_STATE_WRITE(shadow_path, throughput) = throughput; + INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) = throughput; } # endif } /* Surface shaders. */ if (hit < num_recorded_hits) { - const float3 shadow = integrate_transparent_surface_shadow(INTEGRATOR_STATE_PASS, hit); - const float3 throughput = INTEGRATOR_STATE(shadow_path, throughput) * shadow; + const float3 shadow = integrate_transparent_surface_shadow(kg, state, hit); + const float3 throughput = INTEGRATOR_STATE(state, shadow_path, throughput) * shadow; if (is_zero(throughput)) { return true; } - INTEGRATOR_STATE_WRITE(shadow_path, throughput) = throughput; - INTEGRATOR_STATE_WRITE(shadow_path, transparent_bounce) += 1; + INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) = throughput; + INTEGRATOR_STATE_WRITE(state, shadow_path, transparent_bounce) += 1; } /* Note we do not need to check max_transparent_bounce here, the number @@ -141,26 +146,27 @@ ccl_device_inline bool integrate_transparent_shadow(INTEGRATOR_STATE_ARGS, const if (shadow_intersections_has_remaining(num_hits)) { /* There are more hits that we could not recorded due to memory usage, * adjust ray to intersect again from the last hit. */ - const float last_hit_t = INTEGRATOR_STATE_ARRAY(shadow_isect, num_recorded_hits - 1, t); - const float3 ray_P = INTEGRATOR_STATE(shadow_ray, P); - const float3 ray_D = INTEGRATOR_STATE(shadow_ray, D); - INTEGRATOR_STATE_WRITE(shadow_ray, P) = ray_offset(ray_P + last_hit_t * ray_D, ray_D); - INTEGRATOR_STATE_WRITE(shadow_ray, t) -= last_hit_t; + const float last_hit_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, num_recorded_hits - 1, t); + const float3 ray_P = INTEGRATOR_STATE(state, shadow_ray, P); + const float3 ray_D = INTEGRATOR_STATE(state, shadow_ray, D); + INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_offset(ray_P + last_hit_t * ray_D, ray_D); + INTEGRATOR_STATE_WRITE(state, shadow_ray, t) -= last_hit_t; } return false; } #endif /* __TRANSPARENT_SHADOWS__ */ -ccl_device void integrator_shade_shadow(INTEGRATOR_STATE_ARGS, +ccl_device void integrator_shade_shadow(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SETUP); - const int num_hits = INTEGRATOR_STATE(shadow_path, num_hits); + const int num_hits = INTEGRATOR_STATE(state, shadow_path, num_hits); #ifdef __TRANSPARENT_SHADOWS__ /* Evaluate transparent shadows. */ - const bool opaque = integrate_transparent_shadow(INTEGRATOR_STATE_PASS, num_hits); + const bool opaque = integrate_transparent_shadow(kg, state, num_hits); if (opaque) { INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); return; @@ -174,7 +180,7 @@ ccl_device void integrator_shade_shadow(INTEGRATOR_STATE_ARGS, return; } else { - kernel_accum_light(INTEGRATOR_STATE_PASS, render_buffer); + kernel_accum_light(kg, state, render_buffer); INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); return; } diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index 0d739517592..bc97fde0e4a 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -28,33 +28,35 @@ CCL_NAMESPACE_BEGIN -ccl_device_forceinline void integrate_surface_shader_setup(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_forceinline void integrate_surface_shader_setup(KernelGlobals kg, + ConstIntegratorState state, ccl_private ShaderData *sd) { Intersection isect ccl_optional_struct_init; - integrator_state_read_isect(INTEGRATOR_STATE_PASS, &isect); + integrator_state_read_isect(kg, state, &isect); Ray ray ccl_optional_struct_init; - integrator_state_read_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_read_ray(kg, state, &ray); shader_setup_from_ray(kg, sd, &ray, &isect); } #ifdef __HOLDOUT__ -ccl_device_forceinline bool integrate_surface_holdout(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg, + ConstIntegratorState state, ccl_private ShaderData *sd, ccl_global float *ccl_restrict render_buffer) { /* Write holdout transparency to render buffer and stop if fully holdout. */ - const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) && (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { const float3 holdout_weight = shader_holdout_apply(kg, sd); if (kernel_data.background.transparent) { - const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); const float transparent = average(holdout_weight * throughput); - kernel_accum_transparent(INTEGRATOR_STATE_PASS, transparent, render_buffer); + kernel_accum_transparent(kg, state, transparent, render_buffer); } if (isequal_float3(holdout_weight, one_float3())) { return false; @@ -66,12 +68,13 @@ ccl_device_forceinline bool integrate_surface_holdout(INTEGRATOR_STATE_CONST_ARG #endif /* __HOLDOUT__ */ #ifdef __EMISSION__ -ccl_device_forceinline void integrate_surface_emission(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, + ConstIntegratorState state, ccl_private const ShaderData *sd, ccl_global float *ccl_restrict render_buffer) { - const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); /* Evaluate emissive closure. */ float3 L = shader_emissive_eval(sd); @@ -83,8 +86,8 @@ ccl_device_forceinline void integrate_surface_emission(INTEGRATOR_STATE_CONST_AR if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS)) # endif { - const float bsdf_pdf = INTEGRATOR_STATE(path, mis_ray_pdf); - const float t = sd->ray_length + INTEGRATOR_STATE(path, mis_ray_t); + const float bsdf_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); + const float t = sd->ray_length + INTEGRATOR_STATE(state, path, mis_ray_t); /* Multiple importance sampling, get triangle light pdf, * and compute weight with respect to BSDF pdf. */ @@ -94,15 +97,16 @@ ccl_device_forceinline void integrate_surface_emission(INTEGRATOR_STATE_CONST_AR L *= mis_weight; } - const float3 throughput = INTEGRATOR_STATE(path, throughput); - kernel_accum_emission(INTEGRATOR_STATE_PASS, throughput, L, render_buffer); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_accum_emission(kg, state, throughput, L, render_buffer); } #endif /* __EMISSION__ */ #ifdef __EMISSION__ /* Path tracing: sample point on light and evaluate light shader, then * queue shadow ray to be traced. */ -ccl_device_forceinline void integrate_surface_direct_light(INTEGRATOR_STATE_ARGS, +ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, + IntegratorState state, ccl_private ShaderData *sd, ccl_private const RNGState *rng_state) { @@ -114,8 +118,8 @@ ccl_device_forceinline void integrate_surface_direct_light(INTEGRATOR_STATE_ARGS /* Sample position on a light. */ LightSample ls ccl_optional_struct_init; { - const int path_flag = INTEGRATOR_STATE(path, flag); - const uint bounce = INTEGRATOR_STATE(path, bounce); + const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint bounce = INTEGRATOR_STATE(state, path, bounce); float light_u, light_v; path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); @@ -135,8 +139,7 @@ ccl_device_forceinline void integrate_surface_direct_light(INTEGRATOR_STATE_ARGS * non-constant light sources. */ ShaderDataTinyStorage emission_sd_storage; ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - const float3 light_eval = light_sample_shader_eval( - INTEGRATOR_STATE_PASS, emission_sd, &ls, sd->time); + const float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, sd->time); if (is_zero(light_eval)) { return; } @@ -165,39 +168,39 @@ ccl_device_forceinline void integrate_surface_direct_light(INTEGRATOR_STATE_ARGS const bool is_light = light_sample_is_light(&ls); /* Copy volume stack and enter/exit volume. */ - integrator_state_copy_volume_stack_to_shadow(INTEGRATOR_STATE_PASS); + integrator_state_copy_volume_stack_to_shadow(kg, state); if (is_transmission) { # ifdef __VOLUME__ - shadow_volume_stack_enter_exit(INTEGRATOR_STATE_PASS, sd); + shadow_volume_stack_enter_exit(kg, state, sd); # endif } /* Write shadow ray and associated state to global memory. */ - integrator_state_write_shadow_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_write_shadow_ray(kg, state, &ray); /* Copy state from main path to shadow path. */ - const uint16_t bounce = INTEGRATOR_STATE(path, bounce); - const uint16_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce); - uint32_t shadow_flag = INTEGRATOR_STATE(path, flag); + const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); + const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); + uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag); shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; shadow_flag |= (is_transmission) ? PATH_RAY_TRANSMISSION_PASS : PATH_RAY_REFLECT_PASS; - const float3 throughput = INTEGRATOR_STATE(path, throughput) * bsdf_eval_sum(&bsdf_eval); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput) * bsdf_eval_sum(&bsdf_eval); if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { const float3 diffuse_glossy_ratio = (bounce == 0) ? bsdf_eval_diffuse_glossy_ratio(&bsdf_eval) : - INTEGRATOR_STATE(path, diffuse_glossy_ratio); - INTEGRATOR_STATE_WRITE(shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; + INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); + INTEGRATOR_STATE_WRITE(state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; } - INTEGRATOR_STATE_WRITE(shadow_path, flag) = shadow_flag; - INTEGRATOR_STATE_WRITE(shadow_path, bounce) = bounce; - INTEGRATOR_STATE_WRITE(shadow_path, transparent_bounce) = transparent_bounce; - INTEGRATOR_STATE_WRITE(shadow_path, throughput) = throughput; + INTEGRATOR_STATE_WRITE(state, shadow_path, flag) = shadow_flag; + INTEGRATOR_STATE_WRITE(state, shadow_path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(state, shadow_path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) = throughput; if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) { - INTEGRATOR_STATE_WRITE(shadow_path, unshadowed_throughput) = throughput; + INTEGRATOR_STATE_WRITE(state, shadow_path, unshadowed_throughput) = throughput; } /* Branch off shadow kernel. */ @@ -207,7 +210,10 @@ ccl_device_forceinline void integrate_surface_direct_light(INTEGRATOR_STATE_ARGS /* Path tracing: bounce off or through surface with new direction. */ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( - INTEGRATOR_STATE_ARGS, ccl_private ShaderData *sd, ccl_private const RNGState *rng_state) + KernelGlobals kg, + IntegratorState state, + ccl_private ShaderData *sd, + ccl_private const RNGState *rng_state) { /* Sample BSDF or BSSRDF. */ if (!(sd->flag & (SD_BSDF | SD_BSSRDF))) { @@ -221,7 +227,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( #ifdef __SUBSURFACE__ /* BSSRDF closure, we schedule subsurface intersection kernel. */ if (CLOSURE_IS_BSSRDF(sc->type)) { - return subsurface_bounce(INTEGRATOR_STATE_PASS, sd, sc); + return subsurface_bounce(kg, state, sd, sc); } #endif @@ -240,63 +246,64 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( } /* Setup ray. Note that clipping works through transparent bounces. */ - INTEGRATOR_STATE_WRITE(ray, P) = ray_offset(sd->P, (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng); - INTEGRATOR_STATE_WRITE(ray, D) = normalize(bsdf_omega_in); - INTEGRATOR_STATE_WRITE(ray, t) = (label & LABEL_TRANSPARENT) ? - INTEGRATOR_STATE(ray, t) - sd->ray_length : - FLT_MAX; + INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P, + (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng); + INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(bsdf_omega_in); + INTEGRATOR_STATE_WRITE(state, ray, t) = (label & LABEL_TRANSPARENT) ? + INTEGRATOR_STATE(state, ray, t) - sd->ray_length : + FLT_MAX; #ifdef __RAY_DIFFERENTIALS__ - INTEGRATOR_STATE_WRITE(ray, dP) = differential_make_compact(sd->dP); - INTEGRATOR_STATE_WRITE(ray, dD) = differential_make_compact(bsdf_domega_in); + INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in); #endif /* Update throughput. */ - float3 throughput = INTEGRATOR_STATE(path, throughput); + float3 throughput = INTEGRATOR_STATE(state, path, throughput); throughput *= bsdf_eval_sum(&bsdf_eval) / bsdf_pdf; - INTEGRATOR_STATE_WRITE(path, throughput) = throughput; + INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput; if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { - if (INTEGRATOR_STATE(path, bounce) == 0) { - INTEGRATOR_STATE_WRITE(path, - diffuse_glossy_ratio) = bsdf_eval_diffuse_glossy_ratio(&bsdf_eval); + if (INTEGRATOR_STATE(state, path, bounce) == 0) { + INTEGRATOR_STATE_WRITE(state, path, diffuse_glossy_ratio) = bsdf_eval_diffuse_glossy_ratio( + &bsdf_eval); } } /* Update path state */ if (label & LABEL_TRANSPARENT) { - INTEGRATOR_STATE_WRITE(path, mis_ray_t) += sd->ray_length; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) += sd->ray_length; } else { - INTEGRATOR_STATE_WRITE(path, mis_ray_pdf) = bsdf_pdf; - INTEGRATOR_STATE_WRITE(path, mis_ray_t) = 0.0f; - INTEGRATOR_STATE_WRITE(path, min_ray_pdf) = fminf(bsdf_pdf, - INTEGRATOR_STATE(path, min_ray_pdf)); + INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = bsdf_pdf; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; + INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf( + bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); } - path_state_next(INTEGRATOR_STATE_PASS, label); + path_state_next(kg, state, label); return label; } #ifdef __VOLUME__ -ccl_device_forceinline bool integrate_surface_volume_only_bounce(INTEGRATOR_STATE_ARGS, +ccl_device_forceinline bool integrate_surface_volume_only_bounce(IntegratorState state, ccl_private ShaderData *sd) { - if (!path_state_volume_next(INTEGRATOR_STATE_PASS)) { + if (!path_state_volume_next(state)) { return LABEL_NONE; } /* Setup ray position, direction stays unchanged. */ - INTEGRATOR_STATE_WRITE(ray, P) = ray_offset(sd->P, -sd->Ng); + INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P, -sd->Ng); /* Clipping works through transparent. */ - INTEGRATOR_STATE_WRITE(ray, t) -= sd->ray_length; + INTEGRATOR_STATE_WRITE(state, ray, t) -= sd->ray_length; # ifdef __RAY_DIFFERENTIALS__ - INTEGRATOR_STATE_WRITE(ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); # endif - INTEGRATOR_STATE_WRITE(path, mis_ray_t) += sd->ray_length; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) += sd->ray_length; return LABEL_TRANSMIT | LABEL_TRANSPARENT; } @@ -304,17 +311,19 @@ ccl_device_forceinline bool integrate_surface_volume_only_bounce(INTEGRATOR_STAT #if defined(__AO__) && defined(__SHADER_RAYTRACE__) ccl_device_forceinline void integrate_surface_ao_pass( - INTEGRATOR_STATE_CONST_ARGS, + KernelGlobals kg, + ConstIntegratorState state, ccl_private const ShaderData *ccl_restrict sd, ccl_private const RNGState *ccl_restrict rng_state, ccl_global float *ccl_restrict render_buffer) { # ifdef __KERNEL_OPTIX__ - optixDirectCall(2, INTEGRATOR_STATE_PASS, sd, rng_state, render_buffer); + optixDirectCall(2, kg, state, sd, rng_state, render_buffer); } extern "C" __device__ void __direct_callable__ao_pass( - INTEGRATOR_STATE_CONST_ARGS, + KernelGlobals kg, + ConstIntegratorState state, ccl_private const ShaderData *ccl_restrict sd, ccl_private const RNGState *ccl_restrict rng_state, ccl_global float *ccl_restrict render_buffer) @@ -339,9 +348,8 @@ extern "C" __device__ void __direct_callable__ao_pass( Intersection isect ccl_optional_struct_init; if (!scene_intersect(kg, &ray, PATH_RAY_SHADOW_OPAQUE, &isect)) { - ccl_global float *buffer = kernel_pass_pixel_render_buffer(INTEGRATOR_STATE_PASS, - render_buffer); - const float3 throughput = INTEGRATOR_STATE(path, throughput); + ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); kernel_write_pass_float3(buffer + kernel_data.film.pass_ao, throughput); } } @@ -349,7 +357,8 @@ extern "C" __device__ void __direct_callable__ao_pass( #endif /* defined(__AO__) && defined(__SHADER_RAYTRACE__) */ template -ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, +ccl_device bool integrate_surface(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { @@ -357,7 +366,7 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, /* Setup shader data. */ ShaderData sd; - integrate_surface_shader_setup(INTEGRATOR_STATE_PASS, &sd); + integrate_surface_shader_setup(kg, state, &sd); PROFILING_SHADER(sd.object, sd.shader); int continue_path_label = 0; @@ -366,7 +375,7 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, #ifdef __VOLUME__ if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { #endif - const int path_flag = INTEGRATOR_STATE(path, flag); + const int path_flag = INTEGRATOR_STATE(state, path, flag); #ifdef __SUBSURFACE__ /* Can skip shader evaluation for BSSRDF exit point without bump mapping. */ @@ -375,23 +384,23 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, { /* Evaluate shader. */ PROFILING_EVENT(PROFILING_SHADE_SURFACE_EVAL); - shader_eval_surface(INTEGRATOR_STATE_PASS, &sd, render_buffer, path_flag); + shader_eval_surface(kg, state, &sd, render_buffer, path_flag); } #ifdef __SUBSURFACE__ if (path_flag & PATH_RAY_SUBSURFACE) { /* When coming from inside subsurface scattering, setup a diffuse * closure to perform lighting at the exit point. */ - subsurface_shader_data_setup(INTEGRATOR_STATE_PASS, &sd, path_flag); - INTEGRATOR_STATE_WRITE(path, flag) &= ~PATH_RAY_SUBSURFACE; + subsurface_shader_data_setup(kg, state, &sd, path_flag); + INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_SUBSURFACE; } #endif - shader_prepare_surface_closures(INTEGRATOR_STATE_PASS, &sd); + shader_prepare_surface_closures(kg, state, &sd); #ifdef __HOLDOUT__ /* Evaluate holdout. */ - if (!integrate_surface_holdout(INTEGRATOR_STATE_PASS, &sd, render_buffer)) { + if (!integrate_surface_holdout(kg, state, &sd, render_buffer)) { return false; } #endif @@ -399,19 +408,19 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, #ifdef __EMISSION__ /* Write emission. */ if (sd.flag & SD_EMISSION) { - integrate_surface_emission(INTEGRATOR_STATE_PASS, &sd, render_buffer); + integrate_surface_emission(kg, state, &sd, render_buffer); } #endif #ifdef __PASSES__ /* Write render passes. */ PROFILING_EVENT(PROFILING_SHADE_SURFACE_PASSES); - kernel_write_data_passes(INTEGRATOR_STATE_PASS, &sd, render_buffer); + kernel_write_data_passes(kg, state, &sd, render_buffer); #endif /* Load random number state. */ RNGState rng_state; - path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + path_state_rng_load(state, &rng_state); /* Perform path termination. Most paths have already been terminated in * the intersect_closest kernel, this is just for emission and for dividing @@ -421,52 +430,50 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, if (!(path_flag & PATH_RAY_SUBSURFACE)) { const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ? 0.0f : - path_state_continuation_probability(INTEGRATOR_STATE_PASS, - path_flag); + path_state_continuation_probability(kg, state, path_flag); if (probability == 0.0f) { return false; } else if (probability != 1.0f) { - INTEGRATOR_STATE_WRITE(path, throughput) /= probability; + INTEGRATOR_STATE_WRITE(state, path, throughput) /= probability; } } #ifdef __DENOISING_FEATURES__ - kernel_write_denoising_features_surface(INTEGRATOR_STATE_PASS, &sd, render_buffer); + kernel_write_denoising_features_surface(kg, state, &sd, render_buffer); #endif #ifdef __SHADOW_CATCHER__ - kernel_write_shadow_catcher_bounce_data(INTEGRATOR_STATE_PASS, &sd, render_buffer); + kernel_write_shadow_catcher_bounce_data(kg, state, &sd, render_buffer); #endif /* Direct light. */ PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT); - integrate_surface_direct_light(INTEGRATOR_STATE_PASS, &sd, &rng_state); + integrate_surface_direct_light(kg, state, &sd, &rng_state); #if defined(__AO__) && defined(__SHADER_RAYTRACE__) /* Ambient occlusion pass. */ if (node_feature_mask & KERNEL_FEATURE_NODE_RAYTRACE) { if ((kernel_data.film.pass_ao != PASS_UNUSED) && - (INTEGRATOR_STATE(path, flag) & PATH_RAY_CAMERA)) { + (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_CAMERA)) { PROFILING_EVENT(PROFILING_SHADE_SURFACE_AO); - integrate_surface_ao_pass(INTEGRATOR_STATE_PASS, &sd, &rng_state, render_buffer); + integrate_surface_ao_pass(kg, state, &sd, &rng_state, render_buffer); } } #endif PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT); - continue_path_label = integrate_surface_bsdf_bssrdf_bounce( - INTEGRATOR_STATE_PASS, &sd, &rng_state); + continue_path_label = integrate_surface_bsdf_bssrdf_bounce(kg, state, &sd, &rng_state); #ifdef __VOLUME__ } else { PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT); - continue_path_label = integrate_surface_volume_only_bounce(INTEGRATOR_STATE_PASS, &sd); + continue_path_label = integrate_surface_volume_only_bounce(state, &sd); } if (continue_path_label & LABEL_TRANSMIT) { /* Enter/Exit volume. */ - volume_stack_enter_exit(INTEGRATOR_STATE_PASS, &sd); + volume_stack_enter_exit(kg, state, &sd); } #endif @@ -475,15 +482,16 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS, template -ccl_device_forceinline void integrator_shade_surface(INTEGRATOR_STATE_ARGS, +ccl_device_forceinline void integrator_shade_surface(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { - if (integrate_surface(INTEGRATOR_STATE_PASS, render_buffer)) { - if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SUBSURFACE) { + if (integrate_surface(kg, state, render_buffer)) { + if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SUBSURFACE) { INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); } else { - kernel_assert(INTEGRATOR_STATE(ray, t) != 0.0f); + kernel_assert(INTEGRATOR_STATE(state, ray, t) != 0.0f); INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); } } @@ -493,11 +501,11 @@ ccl_device_forceinline void integrator_shade_surface(INTEGRATOR_STATE_ARGS, } ccl_device_forceinline void integrator_shade_surface_raytrace( - INTEGRATOR_STATE_ARGS, ccl_global float *ccl_restrict render_buffer) + KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) { integrator_shade_surface(INTEGRATOR_STATE_PASS, - render_buffer); + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE>( + kg, state, render_buffer); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index 72c609751f7..e465a993041 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -70,12 +70,13 @@ typedef struct VolumeShaderCoefficients { } VolumeShaderCoefficients; /* Evaluate shader to get extinction coefficient at P. */ -ccl_device_inline bool shadow_volume_shader_sample(INTEGRATOR_STATE_ARGS, +ccl_device_inline bool shadow_volume_shader_sample(KernelGlobals kg, + IntegratorState state, ccl_private ShaderData *ccl_restrict sd, ccl_private float3 *ccl_restrict extinction) { - shader_eval_volume(INTEGRATOR_STATE_PASS, sd, PATH_RAY_SHADOW, [=](const int i) { - return integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_PASS, i); + shader_eval_volume(kg, state, sd, PATH_RAY_SHADOW, [=](const int i) { + return integrator_state_read_shadow_volume_stack(state, i); }); if (!(sd->flag & SD_EXTINCTION)) { @@ -88,13 +89,14 @@ ccl_device_inline bool shadow_volume_shader_sample(INTEGRATOR_STATE_ARGS, } /* Evaluate shader to get absorption, scattering and emission at P. */ -ccl_device_inline bool volume_shader_sample(INTEGRATOR_STATE_ARGS, +ccl_device_inline bool volume_shader_sample(KernelGlobals kg, + IntegratorState state, ccl_private ShaderData *ccl_restrict sd, ccl_private VolumeShaderCoefficients *coeff) { - const int path_flag = INTEGRATOR_STATE(path, flag); - shader_eval_volume(INTEGRATOR_STATE_PASS, sd, path_flag, [=](const int i) { - return integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); + const int path_flag = INTEGRATOR_STATE(state, path, flag); + shader_eval_volume(kg, state, sd, path_flag, [=](const int i) { + return integrator_state_read_volume_stack(state, i); }); if (!(sd->flag & (SD_EXTINCTION | SD_SCATTER | SD_EMISSION))) { @@ -123,7 +125,7 @@ ccl_device_inline bool volume_shader_sample(INTEGRATOR_STATE_ARGS, return true; } -ccl_device_forceinline void volume_step_init(ccl_global const KernelGlobals *kg, +ccl_device_forceinline void volume_step_init(KernelGlobals kg, ccl_private const RNGState *rng_state, const float object_step_size, float t, @@ -169,14 +171,14 @@ ccl_device_forceinline void volume_step_init(ccl_global const KernelGlobals *kg, # if 0 /* homogeneous volume: assume shader evaluation at the starts gives * the extinction coefficient for the entire line segment */ -ccl_device void volume_shadow_homogeneous(INTEGRATOR_STATE_ARGS, +ccl_device void volume_shadow_homogeneous(KernelGlobals kg, IntegratorState state, ccl_private Ray *ccl_restrict ray, ccl_private ShaderData *ccl_restrict sd, ccl_global float3 *ccl_restrict throughput) { float3 sigma_t = zero_float3(); - if (shadow_volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &sigma_t)) { + if (shadow_volume_shader_sample(kg, state, sd, &sigma_t)) { *throughput *= volume_color_transmittance(sigma_t, ray->t); } } @@ -184,7 +186,8 @@ ccl_device void volume_shadow_homogeneous(INTEGRATOR_STATE_ARGS, /* heterogeneous volume: integrate stepping through the volume until we * reach the end, get absorbed entirely, or run out of iterations */ -ccl_device void volume_shadow_heterogeneous(INTEGRATOR_STATE_ARGS, +ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, + IntegratorState state, ccl_private Ray *ccl_restrict ray, ccl_private ShaderData *ccl_restrict sd, ccl_private float3 *ccl_restrict throughput, @@ -192,7 +195,7 @@ ccl_device void volume_shadow_heterogeneous(INTEGRATOR_STATE_ARGS, { /* Load random number state. */ RNGState rng_state; - shadow_path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + shadow_path_state_rng_load(state, &rng_state); float3 tp = *throughput; @@ -227,7 +230,7 @@ ccl_device void volume_shadow_heterogeneous(INTEGRATOR_STATE_ARGS, /* compute attenuation over segment */ sd->P = new_P; - if (shadow_volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &sigma_t)) { + if (shadow_volume_shader_sample(kg, state, sd, &sigma_t)) { /* Compute `expf()` only for every Nth step, to save some calculations * because `exp(a)*exp(b) = exp(a+b)`, also do a quick #VOLUME_THROUGHPUT_EPSILON * check then. */ @@ -510,7 +513,8 @@ ccl_device_forceinline void volume_integrate_step_scattering( * iterations. this does probabilistically scatter or get transmitted through * for path tracing where we don't want to branch. */ ccl_device_forceinline void volume_integrate_heterogeneous( - INTEGRATOR_STATE_ARGS, + KernelGlobals kg, + IntegratorState state, ccl_private Ray *ccl_restrict ray, ccl_private ShaderData *ccl_restrict sd, ccl_private const RNGState *rng_state, @@ -560,7 +564,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous( vstate.distance_pdf = 1.0f; /* Initialize volume integration result. */ - const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); result.direct_throughput = throughput; result.indirect_throughput = throughput; @@ -571,7 +575,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous( } # ifdef __DENOISING_FEATURES__ - const bool write_denoising_features = (INTEGRATOR_STATE(path, flag) & + const bool write_denoising_features = (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_DENOISING_FEATURES); float3 accum_albedo = zero_float3(); # endif @@ -585,7 +589,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous( /* compute segment */ VolumeShaderCoefficients coeff ccl_optional_struct_init; - if (volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &coeff)) { + if (volume_shader_sample(kg, state, sd, &coeff)) { const int closure_flag = sd->flag; /* Evaluate transmittance over segment. */ @@ -654,15 +658,14 @@ ccl_device_forceinline void volume_integrate_heterogeneous( /* Write accumulated emission. */ if (!is_zero(accum_emission)) { - kernel_accum_emission( - INTEGRATOR_STATE_PASS, result.indirect_throughput, accum_emission, render_buffer); + kernel_accum_emission(kg, state, result.indirect_throughput, accum_emission, render_buffer); } # ifdef __DENOISING_FEATURES__ /* Write denoising features. */ if (write_denoising_features) { kernel_write_denoising_features_volume( - INTEGRATOR_STATE_PASS, accum_albedo, result.indirect_scatter, render_buffer); + kg, state, accum_albedo, result.indirect_scatter, render_buffer); } # endif /* __DENOISING_FEATURES__ */ } @@ -671,7 +674,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous( /* Path tracing: sample point on light and evaluate light shader, then * queue shadow ray to be traced. */ ccl_device_forceinline bool integrate_volume_sample_light( - INTEGRATOR_STATE_ARGS, + KernelGlobals kg, + IntegratorState state, ccl_private const ShaderData *ccl_restrict sd, ccl_private const RNGState *ccl_restrict rng_state, ccl_private LightSample *ccl_restrict ls) @@ -682,8 +686,8 @@ ccl_device_forceinline bool integrate_volume_sample_light( } /* Sample position on a light. */ - const int path_flag = INTEGRATOR_STATE(path, flag); - const uint bounce = INTEGRATOR_STATE(path, bounce); + const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint bounce = INTEGRATOR_STATE(state, path, bounce); float light_u, light_v; path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); @@ -700,7 +704,8 @@ ccl_device_forceinline bool integrate_volume_sample_light( /* Path tracing: sample point on light and evaluate light shader, then * queue shadow ray to be traced. */ ccl_device_forceinline void integrate_volume_direct_light( - INTEGRATOR_STATE_ARGS, + KernelGlobals kg, + IntegratorState state, ccl_private const ShaderData *ccl_restrict sd, ccl_private const RNGState *ccl_restrict rng_state, const float3 P, @@ -720,8 +725,8 @@ ccl_device_forceinline void integrate_volume_direct_light( * TODO: decorrelate random numbers and use light_sample_new_position to * avoid resampling the CDF. */ { - const int path_flag = INTEGRATOR_STATE(path, flag); - const uint bounce = INTEGRATOR_STATE(path, bounce); + const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint bounce = INTEGRATOR_STATE(state, path, bounce); float light_u, light_v; path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); @@ -743,8 +748,7 @@ ccl_device_forceinline void integrate_volume_direct_light( * non-constant light sources. */ ShaderDataTinyStorage emission_sd_storage; ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - const float3 light_eval = light_sample_shader_eval( - INTEGRATOR_STATE_PASS, emission_sd, ls, sd->time); + const float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, ls, sd->time); if (is_zero(light_eval)) { return; } @@ -772,12 +776,12 @@ ccl_device_forceinline void integrate_volume_direct_light( const bool is_light = light_sample_is_light(ls); /* Write shadow ray and associated state to global memory. */ - integrator_state_write_shadow_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_write_shadow_ray(kg, state, &ray); /* Copy state from main path to shadow path. */ - const uint16_t bounce = INTEGRATOR_STATE(path, bounce); - const uint16_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce); - uint32_t shadow_flag = INTEGRATOR_STATE(path, flag); + const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); + const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); + uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag); shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; shadow_flag |= PATH_RAY_VOLUME_PASS; const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval); @@ -785,20 +789,20 @@ ccl_device_forceinline void integrate_volume_direct_light( if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { const float3 diffuse_glossy_ratio = (bounce == 0) ? one_float3() : - INTEGRATOR_STATE(path, diffuse_glossy_ratio); - INTEGRATOR_STATE_WRITE(shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; + INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); + INTEGRATOR_STATE_WRITE(state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; } - INTEGRATOR_STATE_WRITE(shadow_path, flag) = shadow_flag; - INTEGRATOR_STATE_WRITE(shadow_path, bounce) = bounce; - INTEGRATOR_STATE_WRITE(shadow_path, transparent_bounce) = transparent_bounce; - INTEGRATOR_STATE_WRITE(shadow_path, throughput) = throughput_phase; + INTEGRATOR_STATE_WRITE(state, shadow_path, flag) = shadow_flag; + INTEGRATOR_STATE_WRITE(state, shadow_path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(state, shadow_path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) = throughput_phase; if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) { - INTEGRATOR_STATE_WRITE(shadow_path, unshadowed_throughput) = throughput; + INTEGRATOR_STATE_WRITE(state, shadow_path, unshadowed_throughput) = throughput; } - integrator_state_copy_volume_stack_to_shadow(INTEGRATOR_STATE_PASS); + integrator_state_copy_volume_stack_to_shadow(kg, state); /* Branch off shadow kernel. */ INTEGRATOR_SHADOW_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); @@ -807,7 +811,8 @@ ccl_device_forceinline void integrate_volume_direct_light( /* Path tracing: scatter in new direction using phase function */ ccl_device_forceinline bool integrate_volume_phase_scatter( - INTEGRATOR_STATE_ARGS, + KernelGlobals kg, + IntegratorState state, ccl_private ShaderData *sd, ccl_private const RNGState *rng_state, ccl_private const ShaderVolumePhases *phases) @@ -838,31 +843,31 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( } /* Setup ray. */ - INTEGRATOR_STATE_WRITE(ray, P) = sd->P; - INTEGRATOR_STATE_WRITE(ray, D) = normalize(phase_omega_in); - INTEGRATOR_STATE_WRITE(ray, t) = FLT_MAX; + INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; + INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_omega_in); + INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX; # ifdef __RAY_DIFFERENTIALS__ - INTEGRATOR_STATE_WRITE(ray, dP) = differential_make_compact(sd->dP); - INTEGRATOR_STATE_WRITE(ray, dD) = differential_make_compact(phase_domega_in); + INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in); # endif /* Update throughput. */ - const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval) / phase_pdf; - INTEGRATOR_STATE_WRITE(path, throughput) = throughput_phase; + INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput_phase; if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { - INTEGRATOR_STATE_WRITE(path, diffuse_glossy_ratio) = one_float3(); + INTEGRATOR_STATE_WRITE(state, path, diffuse_glossy_ratio) = one_float3(); } /* Update path state */ - INTEGRATOR_STATE_WRITE(path, mis_ray_pdf) = phase_pdf; - INTEGRATOR_STATE_WRITE(path, mis_ray_t) = 0.0f; - INTEGRATOR_STATE_WRITE(path, min_ray_pdf) = fminf(phase_pdf, - INTEGRATOR_STATE(path, min_ray_pdf)); + INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; + INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf( + phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); - path_state_next(INTEGRATOR_STATE_PASS, label); + path_state_next(kg, state, label); return true; } @@ -870,7 +875,8 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( * ray, with the assumption that there are no surfaces blocking light * between the endpoints. distance sampling is used to decide if we will * scatter or not. */ -ccl_device VolumeIntegrateEvent volume_integrate(INTEGRATOR_STATE_ARGS, +ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg, + IntegratorState state, ccl_private Ray *ccl_restrict ray, ccl_global float *ccl_restrict render_buffer) { @@ -879,29 +885,29 @@ ccl_device VolumeIntegrateEvent volume_integrate(INTEGRATOR_STATE_ARGS, /* Load random number state. */ RNGState rng_state; - path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + path_state_rng_load(state, &rng_state); /* Sample light ahead of volume stepping, for equiangular sampling. */ /* TODO: distant lights are ignored now, but could instead use even distribution. */ LightSample ls ccl_optional_struct_init; - const bool need_light_sample = !(INTEGRATOR_STATE(path, flag) & PATH_RAY_TERMINATE); + const bool need_light_sample = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE); const bool have_equiangular_sample = need_light_sample && integrate_volume_sample_light( - INTEGRATOR_STATE_PASS, &sd, &rng_state, &ls) && + kg, state, &sd, &rng_state, &ls) && (ls.t != FLT_MAX); VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ? - volume_stack_sample_method(INTEGRATOR_STATE_PASS) : + volume_stack_sample_method(kg, state) : VOLUME_SAMPLE_DISTANCE; /* Step through volume. */ - const float step_size = volume_stack_step_size(INTEGRATOR_STATE_PASS, [=](const int i) { - return integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); - }); + const float step_size = volume_stack_step_size( + kg, state, [=](const int i) { return integrator_state_read_volume_stack(state, i); }); /* TODO: expensive to zero closures? */ VolumeIntegrateResult result = {}; - volume_integrate_heterogeneous(INTEGRATOR_STATE_PASS, + volume_integrate_heterogeneous(kg, + state, ray, &sd, &rng_state, @@ -914,11 +920,10 @@ ccl_device VolumeIntegrateEvent volume_integrate(INTEGRATOR_STATE_ARGS, /* Perform path termination. The intersect_closest will have already marked this path * to be terminated. That will shading evaluating to leave out any scattering closures, * but emission and absorption are still handled for multiple importance sampling. */ - const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); const float probability = (path_flag & PATH_RAY_TERMINATE_IN_NEXT_VOLUME) ? 0.0f : - path_state_continuation_probability(INTEGRATOR_STATE_PASS, - path_flag); + path_state_continuation_probability(kg, state, path_flag); if (probability == 0.0f) { return VOLUME_PATH_MISSED; } @@ -927,7 +932,8 @@ ccl_device VolumeIntegrateEvent volume_integrate(INTEGRATOR_STATE_ARGS, if (result.direct_scatter) { const float3 direct_P = ray->P + result.direct_t * ray->D; result.direct_throughput /= probability; - integrate_volume_direct_light(INTEGRATOR_STATE_PASS, + integrate_volume_direct_light(kg, + state, &sd, &rng_state, direct_P, @@ -943,13 +949,12 @@ ccl_device VolumeIntegrateEvent volume_integrate(INTEGRATOR_STATE_ARGS, if (result.indirect_scatter) { result.indirect_throughput /= probability; } - INTEGRATOR_STATE_WRITE(path, throughput) = result.indirect_throughput; + INTEGRATOR_STATE_WRITE(state, path, throughput) = result.indirect_throughput; if (result.indirect_scatter) { sd.P = ray->P + result.indirect_t * ray->D; - if (integrate_volume_phase_scatter( - INTEGRATOR_STATE_PASS, &sd, &rng_state, &result.indirect_phases)) { + if (integrate_volume_phase_scatter(kg, state, &sd, &rng_state, &result.indirect_phases)) { return VOLUME_PATH_SCATTERED; } else { @@ -963,7 +968,8 @@ ccl_device VolumeIntegrateEvent volume_integrate(INTEGRATOR_STATE_ARGS, #endif -ccl_device void integrator_shade_volume(INTEGRATOR_STATE_ARGS, +ccl_device void integrator_shade_volume(KernelGlobals kg, + IntegratorState state, ccl_global float *ccl_restrict render_buffer) { PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_SETUP); @@ -971,20 +977,20 @@ ccl_device void integrator_shade_volume(INTEGRATOR_STATE_ARGS, #ifdef __VOLUME__ /* Setup shader data. */ Ray ray ccl_optional_struct_init; - integrator_state_read_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_read_ray(kg, state, &ray); Intersection isect ccl_optional_struct_init; - integrator_state_read_isect(INTEGRATOR_STATE_PASS, &isect); + integrator_state_read_isect(kg, state, &isect); /* Set ray length to current segment. */ ray.t = (isect.prim != PRIM_NONE) ? isect.t : FLT_MAX; /* Clean volume stack for background rays. */ if (isect.prim == PRIM_NONE) { - volume_stack_clean(INTEGRATOR_STATE_PASS); + volume_stack_clean(kg, state); } - VolumeIntegrateEvent event = volume_integrate(INTEGRATOR_STATE_PASS, &ray, render_buffer); + VolumeIntegrateEvent event = volume_integrate(kg, state, &ray, render_buffer); if (event == VOLUME_PATH_SCATTERED) { /* Queue intersect_closest kernel. */ @@ -1015,7 +1021,7 @@ ccl_device void integrator_shade_volume(INTEGRATOR_STATE_ARGS, const int flags = kernel_tex_fetch(__shaders, shader).flags; integrator_intersect_shader_next_kernel( - INTEGRATOR_STATE_PASS, &isect, shader, flags); + kg, state, &isect, shader, flags); return; } } diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h index 517e2891769..3aab456a021 100644 --- a/intern/cycles/kernel/integrator/integrator_state.h +++ b/intern/cycles/kernel/integrator/integrator_state.h @@ -27,24 +27,17 @@ * to every kernel, or the pointer may exist at program scope or in constant memory. To abstract * these differences between devices and experiment with different layouts, macros are used. * - * INTEGRATOR_STATE_ARGS: prepend to argument definitions for every function that accesses - * path state. - * INTEGRATOR_STATE_CONST_ARGS: same as INTEGRATOR_STATE_ARGS, when state is read-only - * INTEGRATOR_STATE_PASS: use to pass along state to other functions access it. + * Use IntegratorState to pass a reference to the integrator state for the current path. These are + * defined differently on the CPU and GPU. Use ConstIntegratorState instead of const + * IntegratorState for passing state as read-only, to avoid oddities in typedef behavior. * - * INTEGRATOR_STATE(x, y): read nested struct member x.y of IntegratorState - * INTEGRATOR_STATE_WRITE(x, y): write to nested struct member x.y of IntegratorState + * INTEGRATOR_STATE(state, x, y): read nested struct member x.y of IntegratorState + * INTEGRATOR_STATE_WRITE(state, x, y): write to nested struct member x.y of IntegratorState * - * INTEGRATOR_STATE_ARRAY(x, index, y): read x[index].y - * INTEGRATOR_STATE_ARRAY_WRITE(x, index, y): write x[index].y + * INTEGRATOR_STATE_ARRAY(state, x, index, y): read x[index].y + * INTEGRATOR_STATE_ARRAY_WRITE(state, x, index, y): write x[index].y * - * INTEGRATOR_STATE_COPY(to_x, from_x): copy contents of one nested struct to another - * - * INTEGRATOR_STATE_IS_NULL: test if any integrator state is available, for shader evaluation - * INTEGRATOR_STATE_PASS_NULL: use to pass empty state to other functions. - * - * NOTE: if we end up with a device that passes no arguments, the leading comma will be a problem. - * Can solve it with more macros if we encounter it, but rather ugly so postpone for now. + * INTEGRATOR_STATE_NULL: use to pass empty state to other functions. */ #include "kernel/kernel_types.h" @@ -146,50 +139,36 @@ typedef struct IntegratorStateGPU { /* Scalar access on CPU. */ typedef IntegratorStateCPU *ccl_restrict IntegratorState; +typedef const IntegratorStateCPU *ccl_restrict ConstIntegratorState; -# define INTEGRATOR_STATE_ARGS \ - ccl_attr_maybe_unused const KernelGlobals *ccl_restrict kg, \ - IntegratorStateCPU *ccl_restrict state -# define INTEGRATOR_STATE_CONST_ARGS \ - ccl_attr_maybe_unused const KernelGlobals *ccl_restrict kg, \ - const IntegratorStateCPU *ccl_restrict state -# define INTEGRATOR_STATE_PASS kg, state +# define INTEGRATOR_STATE_NULL nullptr -# define INTEGRATOR_STATE_PASS_NULL kg, NULL -# define INTEGRATOR_STATE_IS_NULL (state == NULL) +# define INTEGRATOR_STATE(state, nested_struct, member) ((state)->nested_struct.member) +# define INTEGRATOR_STATE_WRITE(state, nested_struct, member) ((state)->nested_struct.member) -# define INTEGRATOR_STATE(nested_struct, member) \ - (((const IntegratorStateCPU *)state)->nested_struct.member) -# define INTEGRATOR_STATE_WRITE(nested_struct, member) (state->nested_struct.member) - -# define INTEGRATOR_STATE_ARRAY(nested_struct, array_index, member) \ - (((const IntegratorStateCPU *)state)->nested_struct[array_index].member) -# define INTEGRATOR_STATE_ARRAY_WRITE(nested_struct, array_index, member) \ +# define INTEGRATOR_STATE_ARRAY(state, nested_struct, array_index, member) \ + ((state)->nested_struct[array_index].member) +# define INTEGRATOR_STATE_ARRAY_WRITE(state, nested_struct, array_index, member) \ ((state)->nested_struct[array_index].member) #else /* __KERNEL_CPU__ */ /* Array access on GPU with Structure-of-Arrays. */ -typedef int IntegratorState; +typedef const int IntegratorState; +typedef const int ConstIntegratorState; -# define INTEGRATOR_STATE_ARGS \ - ccl_global const KernelGlobals *ccl_restrict kg, const IntegratorState state -# define INTEGRATOR_STATE_CONST_ARGS \ - ccl_global const KernelGlobals *ccl_restrict kg, const IntegratorState state -# define INTEGRATOR_STATE_PASS kg, state +# define INTEGRATOR_STATE_NULL -1 -# define INTEGRATOR_STATE_PASS_NULL kg, -1 -# define INTEGRATOR_STATE_IS_NULL (state == -1) - -# define INTEGRATOR_STATE(nested_struct, member) \ +# define INTEGRATOR_STATE(state, nested_struct, member) \ kernel_integrator_state.nested_struct.member[state] -# define INTEGRATOR_STATE_WRITE(nested_struct, member) INTEGRATOR_STATE(nested_struct, member) +# define INTEGRATOR_STATE_WRITE(state, nested_struct, member) \ + INTEGRATOR_STATE(state, nested_struct, member) -# define INTEGRATOR_STATE_ARRAY(nested_struct, array_index, member) \ +# define INTEGRATOR_STATE_ARRAY(state, nested_struct, array_index, member) \ kernel_integrator_state.nested_struct[array_index].member[state] -# define INTEGRATOR_STATE_ARRAY_WRITE(nested_struct, array_index, member) \ - INTEGRATOR_STATE_ARRAY(nested_struct, array_index, member) +# define INTEGRATOR_STATE_ARRAY_WRITE(state, nested_struct, array_index, member) \ + INTEGRATOR_STATE_ARRAY(state, nested_struct, array_index, member) #endif /* __KERNEL_CPU__ */ diff --git a/intern/cycles/kernel/integrator/integrator_state_flow.h b/intern/cycles/kernel/integrator/integrator_state_flow.h index 8477efd7b66..9829da875eb 100644 --- a/intern/cycles/kernel/integrator/integrator_state_flow.h +++ b/intern/cycles/kernel/integrator/integrator_state_flow.h @@ -42,48 +42,49 @@ CCL_NAMESPACE_BEGIN * one of them, and only once. */ -#define INTEGRATOR_PATH_IS_TERMINATED (INTEGRATOR_STATE(path, queued_kernel) == 0) -#define INTEGRATOR_SHADOW_PATH_IS_TERMINATED (INTEGRATOR_STATE(shadow_path, queued_kernel) == 0) +#define INTEGRATOR_PATH_IS_TERMINATED (INTEGRATOR_STATE(state, path, queued_kernel) == 0) +#define INTEGRATOR_SHADOW_PATH_IS_TERMINATED \ + (INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0) #ifdef __KERNEL_GPU__ # define INTEGRATOR_PATH_INIT(next_kernel) \ atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ 1); \ - INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; # define INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) \ atomic_fetch_and_sub_uint32( \ &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ 1); \ - INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; # define INTEGRATOR_PATH_TERMINATE(current_kernel) \ atomic_fetch_and_sub_uint32( \ &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - INTEGRATOR_STATE_WRITE(path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; # define INTEGRATOR_SHADOW_PATH_INIT(next_kernel) \ atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ 1); \ - INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; # define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ atomic_fetch_and_sub_uint32( \ &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ 1); \ - INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; # define INTEGRATOR_SHADOW_PATH_TERMINATE(current_kernel) \ atomic_fetch_and_sub_uint32( \ &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; # define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ { \ const int key_ = key; \ atomic_fetch_and_add_uint32( \ &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ - INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; \ - INTEGRATOR_STATE_WRITE(path, shader_sort_key) = key_; \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ + INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; \ atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], \ 1); \ } @@ -94,8 +95,8 @@ CCL_NAMESPACE_BEGIN &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ atomic_fetch_and_add_uint32( \ &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ - INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; \ - INTEGRATOR_STATE_WRITE(path, shader_sort_key) = key_; \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ + INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; \ atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], \ 1); \ } @@ -103,39 +104,39 @@ CCL_NAMESPACE_BEGIN #else # define INTEGRATOR_PATH_INIT(next_kernel) \ - INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; # define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ { \ - INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ (void)key; \ } # define INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) \ { \ - INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ (void)current_kernel; \ } # define INTEGRATOR_PATH_TERMINATE(current_kernel) \ { \ - INTEGRATOR_STATE_WRITE(path, queued_kernel) = 0; \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; \ (void)current_kernel; \ } # define INTEGRATOR_PATH_NEXT_SORTED(current_kernel, next_kernel, key) \ { \ - INTEGRATOR_STATE_WRITE(path, queued_kernel) = next_kernel; \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ (void)key; \ (void)current_kernel; \ } # define INTEGRATOR_SHADOW_PATH_INIT(next_kernel) \ - INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; # define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ { \ - INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = next_kernel; \ + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; \ (void)current_kernel; \ } # define INTEGRATOR_SHADOW_PATH_TERMINATE(current_kernel) \ { \ - INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = 0; \ + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; \ (void)current_kernel; \ } diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index fddd9eb5ac8..fee59e451d9 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -23,145 +23,150 @@ CCL_NAMESPACE_BEGIN /* Ray */ -ccl_device_forceinline void integrator_state_write_ray(INTEGRATOR_STATE_ARGS, +ccl_device_forceinline void integrator_state_write_ray(KernelGlobals kg, + IntegratorState state, ccl_private const Ray *ccl_restrict ray) { - INTEGRATOR_STATE_WRITE(ray, P) = ray->P; - INTEGRATOR_STATE_WRITE(ray, D) = ray->D; - INTEGRATOR_STATE_WRITE(ray, t) = ray->t; - INTEGRATOR_STATE_WRITE(ray, time) = ray->time; - INTEGRATOR_STATE_WRITE(ray, dP) = ray->dP; - INTEGRATOR_STATE_WRITE(ray, dD) = ray->dD; + INTEGRATOR_STATE_WRITE(state, ray, P) = ray->P; + INTEGRATOR_STATE_WRITE(state, ray, D) = ray->D; + INTEGRATOR_STATE_WRITE(state, ray, t) = ray->t; + INTEGRATOR_STATE_WRITE(state, ray, time) = ray->time; + INTEGRATOR_STATE_WRITE(state, ray, dP) = ray->dP; + INTEGRATOR_STATE_WRITE(state, ray, dD) = ray->dD; } -ccl_device_forceinline void integrator_state_read_ray(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_forceinline void integrator_state_read_ray(KernelGlobals kg, + ConstIntegratorState state, ccl_private Ray *ccl_restrict ray) { - ray->P = INTEGRATOR_STATE(ray, P); - ray->D = INTEGRATOR_STATE(ray, D); - ray->t = INTEGRATOR_STATE(ray, t); - ray->time = INTEGRATOR_STATE(ray, time); - ray->dP = INTEGRATOR_STATE(ray, dP); - ray->dD = INTEGRATOR_STATE(ray, dD); + ray->P = INTEGRATOR_STATE(state, ray, P); + ray->D = INTEGRATOR_STATE(state, ray, D); + ray->t = INTEGRATOR_STATE(state, ray, t); + ray->time = INTEGRATOR_STATE(state, ray, time); + ray->dP = INTEGRATOR_STATE(state, ray, dP); + ray->dD = INTEGRATOR_STATE(state, ray, dD); } /* Shadow Ray */ ccl_device_forceinline void integrator_state_write_shadow_ray( - INTEGRATOR_STATE_ARGS, ccl_private const Ray *ccl_restrict ray) + KernelGlobals kg, IntegratorState state, ccl_private const Ray *ccl_restrict ray) { - INTEGRATOR_STATE_WRITE(shadow_ray, P) = ray->P; - INTEGRATOR_STATE_WRITE(shadow_ray, D) = ray->D; - INTEGRATOR_STATE_WRITE(shadow_ray, t) = ray->t; - INTEGRATOR_STATE_WRITE(shadow_ray, time) = ray->time; - INTEGRATOR_STATE_WRITE(shadow_ray, dP) = ray->dP; + INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray->P; + INTEGRATOR_STATE_WRITE(state, shadow_ray, D) = ray->D; + INTEGRATOR_STATE_WRITE(state, shadow_ray, t) = ray->t; + INTEGRATOR_STATE_WRITE(state, shadow_ray, time) = ray->time; + INTEGRATOR_STATE_WRITE(state, shadow_ray, dP) = ray->dP; } -ccl_device_forceinline void integrator_state_read_shadow_ray(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_forceinline void integrator_state_read_shadow_ray(KernelGlobals kg, + ConstIntegratorState state, ccl_private Ray *ccl_restrict ray) { - ray->P = INTEGRATOR_STATE(shadow_ray, P); - ray->D = INTEGRATOR_STATE(shadow_ray, D); - ray->t = INTEGRATOR_STATE(shadow_ray, t); - ray->time = INTEGRATOR_STATE(shadow_ray, time); - ray->dP = INTEGRATOR_STATE(shadow_ray, dP); + ray->P = INTEGRATOR_STATE(state, shadow_ray, P); + ray->D = INTEGRATOR_STATE(state, shadow_ray, D); + ray->t = INTEGRATOR_STATE(state, shadow_ray, t); + ray->time = INTEGRATOR_STATE(state, shadow_ray, time); + ray->dP = INTEGRATOR_STATE(state, shadow_ray, dP); ray->dD = differential_zero_compact(); } /* Intersection */ ccl_device_forceinline void integrator_state_write_isect( - INTEGRATOR_STATE_ARGS, ccl_private const Intersection *ccl_restrict isect) + KernelGlobals kg, IntegratorState state, ccl_private const Intersection *ccl_restrict isect) { - INTEGRATOR_STATE_WRITE(isect, t) = isect->t; - INTEGRATOR_STATE_WRITE(isect, u) = isect->u; - INTEGRATOR_STATE_WRITE(isect, v) = isect->v; - INTEGRATOR_STATE_WRITE(isect, object) = isect->object; - INTEGRATOR_STATE_WRITE(isect, prim) = isect->prim; - INTEGRATOR_STATE_WRITE(isect, type) = isect->type; + INTEGRATOR_STATE_WRITE(state, isect, t) = isect->t; + INTEGRATOR_STATE_WRITE(state, isect, u) = isect->u; + INTEGRATOR_STATE_WRITE(state, isect, v) = isect->v; + INTEGRATOR_STATE_WRITE(state, isect, object) = isect->object; + INTEGRATOR_STATE_WRITE(state, isect, prim) = isect->prim; + INTEGRATOR_STATE_WRITE(state, isect, type) = isect->type; #ifdef __EMBREE__ - INTEGRATOR_STATE_WRITE(isect, Ng) = isect->Ng; + INTEGRATOR_STATE_WRITE(state, isect, Ng) = isect->Ng; #endif } ccl_device_forceinline void integrator_state_read_isect( - INTEGRATOR_STATE_CONST_ARGS, ccl_private Intersection *ccl_restrict isect) + KernelGlobals kg, ConstIntegratorState state, ccl_private Intersection *ccl_restrict isect) { - isect->prim = INTEGRATOR_STATE(isect, prim); - isect->object = INTEGRATOR_STATE(isect, object); - isect->type = INTEGRATOR_STATE(isect, type); - isect->u = INTEGRATOR_STATE(isect, u); - isect->v = INTEGRATOR_STATE(isect, v); - isect->t = INTEGRATOR_STATE(isect, t); + isect->prim = INTEGRATOR_STATE(state, isect, prim); + isect->object = INTEGRATOR_STATE(state, isect, object); + isect->type = INTEGRATOR_STATE(state, isect, type); + isect->u = INTEGRATOR_STATE(state, isect, u); + isect->v = INTEGRATOR_STATE(state, isect, v); + isect->t = INTEGRATOR_STATE(state, isect, t); #ifdef __EMBREE__ - isect->Ng = INTEGRATOR_STATE(isect, Ng); + isect->Ng = INTEGRATOR_STATE(state, isect, Ng); #endif } -ccl_device_forceinline VolumeStack integrator_state_read_volume_stack(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_forceinline VolumeStack integrator_state_read_volume_stack(ConstIntegratorState state, int i) { - VolumeStack entry = {INTEGRATOR_STATE_ARRAY(volume_stack, i, object), - INTEGRATOR_STATE_ARRAY(volume_stack, i, shader)}; + VolumeStack entry = {INTEGRATOR_STATE_ARRAY(state, volume_stack, i, object), + INTEGRATOR_STATE_ARRAY(state, volume_stack, i, shader)}; return entry; } -ccl_device_forceinline void integrator_state_write_volume_stack(INTEGRATOR_STATE_ARGS, +ccl_device_forceinline void integrator_state_write_volume_stack(IntegratorState state, int i, VolumeStack entry) { - INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, i, object) = entry.object; - INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, i, shader) = entry.shader; + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, i, object) = entry.object; + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, i, shader) = entry.shader; } -ccl_device_forceinline bool integrator_state_volume_stack_is_empty(INTEGRATOR_STATE_CONST_ARGS) +ccl_device_forceinline bool integrator_state_volume_stack_is_empty(KernelGlobals kg, + ConstIntegratorState state) { return (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) ? - INTEGRATOR_STATE_ARRAY(volume_stack, 0, shader) == SHADER_NONE : + INTEGRATOR_STATE_ARRAY(state, volume_stack, 0, shader) == SHADER_NONE : true; } /* Shadow Intersection */ ccl_device_forceinline void integrator_state_write_shadow_isect( - INTEGRATOR_STATE_ARGS, ccl_private const Intersection *ccl_restrict isect, const int index) + IntegratorState state, ccl_private const Intersection *ccl_restrict isect, const int index) { - INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, t) = isect->t; - INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, u) = isect->u; - INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, v) = isect->v; - INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, object) = isect->object; - INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, prim) = isect->prim; - INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, type) = isect->type; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, t) = isect->t; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, u) = isect->u; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, v) = isect->v; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, object) = isect->object; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, prim) = isect->prim; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, type) = isect->type; #ifdef __EMBREE__ - INTEGRATOR_STATE_ARRAY_WRITE(shadow_isect, index, Ng) = isect->Ng; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, Ng) = isect->Ng; #endif } ccl_device_forceinline void integrator_state_read_shadow_isect( - INTEGRATOR_STATE_CONST_ARGS, ccl_private Intersection *ccl_restrict isect, const int index) + ConstIntegratorState state, ccl_private Intersection *ccl_restrict isect, const int index) { - isect->prim = INTEGRATOR_STATE_ARRAY(shadow_isect, index, prim); - isect->object = INTEGRATOR_STATE_ARRAY(shadow_isect, index, object); - isect->type = INTEGRATOR_STATE_ARRAY(shadow_isect, index, type); - isect->u = INTEGRATOR_STATE_ARRAY(shadow_isect, index, u); - isect->v = INTEGRATOR_STATE_ARRAY(shadow_isect, index, v); - isect->t = INTEGRATOR_STATE_ARRAY(shadow_isect, index, t); + isect->prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, prim); + isect->object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, object); + isect->type = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, type); + isect->u = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, u); + isect->v = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, v); + isect->t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, t); #ifdef __EMBREE__ - isect->Ng = INTEGRATOR_STATE_ARRAY(shadow_isect, index, Ng); + isect->Ng = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, Ng); #endif } -ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(INTEGRATOR_STATE_ARGS) +ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(KernelGlobals kg, + IntegratorState state) { if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { int index = 0; int shader; do { - shader = INTEGRATOR_STATE_ARRAY(volume_stack, index, shader); + shader = INTEGRATOR_STATE_ARRAY(state, volume_stack, index, shader); - INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, index, object) = INTEGRATOR_STATE_ARRAY( - volume_stack, index, object); - INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, index, shader) = shader; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_volume_stack, index, object) = + INTEGRATOR_STATE_ARRAY(state, volume_stack, index, object); + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_volume_stack, index, shader) = shader; ++index; } while (shader != OBJECT_NONE); @@ -169,27 +174,27 @@ ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(INTEGRA } ccl_device_forceinline VolumeStack -integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_CONST_ARGS, int i) +integrator_state_read_shadow_volume_stack(ConstIntegratorState state, int i) { - VolumeStack entry = {INTEGRATOR_STATE_ARRAY(shadow_volume_stack, i, object), - INTEGRATOR_STATE_ARRAY(shadow_volume_stack, i, shader)}; + VolumeStack entry = {INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, i, object), + INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, i, shader)}; return entry; } ccl_device_forceinline bool integrator_state_shadow_volume_stack_is_empty( - INTEGRATOR_STATE_CONST_ARGS) + KernelGlobals kg, ConstIntegratorState state) { return (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) ? - INTEGRATOR_STATE_ARRAY(shadow_volume_stack, 0, shader) == SHADER_NONE : + INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, 0, shader) == SHADER_NONE : true; } -ccl_device_forceinline void integrator_state_write_shadow_volume_stack(INTEGRATOR_STATE_ARGS, +ccl_device_forceinline void integrator_state_write_shadow_volume_stack(IntegratorState state, int i, VolumeStack entry) { - INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, i, object) = entry.object; - INTEGRATOR_STATE_ARRAY_WRITE(shadow_volume_stack, i, shader) = entry.shader; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_volume_stack, i, object) = entry.object; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_volume_stack, i, shader) = entry.shader; } #if defined(__KERNEL_GPU__) @@ -244,15 +249,16 @@ ccl_device_inline void integrator_state_move(const IntegratorState to_state, { integrator_state_copy_only(to_state, state); - INTEGRATOR_STATE_WRITE(path, queued_kernel) = 0; - INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; } #endif /* NOTE: Leaves kernel scheduling information untouched. Use INIT semantic for one of the paths * after this function. */ -ccl_device_inline void integrator_state_shadow_catcher_split(INTEGRATOR_STATE_ARGS) +ccl_device_inline void integrator_state_shadow_catcher_split(KernelGlobals kg, + IntegratorState state) { #if defined(__KERNEL_GPU__) const IntegratorState to_state = atomic_fetch_and_add_uint32( diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/integrator_subsurface.h index 153f9b79743..448c99765e3 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface.h @@ -36,29 +36,30 @@ CCL_NAMESPACE_BEGIN #ifdef __SUBSURFACE__ -ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, +ccl_device int subsurface_bounce(KernelGlobals kg, + IntegratorState state, ccl_private ShaderData *sd, ccl_private const ShaderClosure *sc) { /* We should never have two consecutive BSSRDF bounces, the second one should * be converted to a diffuse BSDF to avoid this. */ - kernel_assert(!(INTEGRATOR_STATE(path, flag) & PATH_RAY_DIFFUSE_ANCESTOR)); + kernel_assert(!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_DIFFUSE_ANCESTOR)); /* Setup path state for intersect_subsurface kernel. */ ccl_private const Bssrdf *bssrdf = (ccl_private const Bssrdf *)sc; /* Setup ray into surface. */ - INTEGRATOR_STATE_WRITE(ray, P) = sd->P; - INTEGRATOR_STATE_WRITE(ray, D) = bssrdf->N; - INTEGRATOR_STATE_WRITE(ray, t) = FLT_MAX; - INTEGRATOR_STATE_WRITE(ray, dP) = differential_make_compact(sd->dP); - INTEGRATOR_STATE_WRITE(ray, dD) = differential_zero_compact(); + INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; + INTEGRATOR_STATE_WRITE(state, ray, D) = bssrdf->N; + INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX; + INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_zero_compact(); /* Pass along object info, reusing isect to save memory. */ - INTEGRATOR_STATE_WRITE(isect, Ng) = sd->Ng; - INTEGRATOR_STATE_WRITE(isect, object) = sd->object; + INTEGRATOR_STATE_WRITE(state, isect, Ng) = sd->Ng; + INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object; - uint32_t path_flag = (INTEGRATOR_STATE(path, flag) & ~PATH_RAY_CAMERA) | + uint32_t path_flag = (INTEGRATOR_STATE(state, path, flag) & ~PATH_RAY_CAMERA) | ((sc->type == CLOSURE_BSSRDF_BURLEY_ID) ? PATH_RAY_SUBSURFACE_DISK : PATH_RAY_SUBSURFACE_RANDOM_WALK); @@ -70,27 +71,28 @@ ccl_device int subsurface_bounce(INTEGRATOR_STATE_ARGS, } # endif - INTEGRATOR_STATE_WRITE(path, throughput) *= weight; - INTEGRATOR_STATE_WRITE(path, flag) = path_flag; + INTEGRATOR_STATE_WRITE(state, path, throughput) *= weight; + INTEGRATOR_STATE_WRITE(state, path, flag) = path_flag; /* Advance random number offset for bounce. */ - INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; + INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM; if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { - if (INTEGRATOR_STATE(path, bounce) == 0) { - INTEGRATOR_STATE_WRITE(path, diffuse_glossy_ratio) = one_float3(); + if (INTEGRATOR_STATE(state, path, bounce) == 0) { + INTEGRATOR_STATE_WRITE(state, path, diffuse_glossy_ratio) = one_float3(); } } /* Pass BSSRDF parameters. */ - INTEGRATOR_STATE_WRITE(subsurface, albedo) = bssrdf->albedo; - INTEGRATOR_STATE_WRITE(subsurface, radius) = bssrdf->radius; - INTEGRATOR_STATE_WRITE(subsurface, anisotropy) = bssrdf->anisotropy; + INTEGRATOR_STATE_WRITE(state, subsurface, albedo) = bssrdf->albedo; + INTEGRATOR_STATE_WRITE(state, subsurface, radius) = bssrdf->radius; + INTEGRATOR_STATE_WRITE(state, subsurface, anisotropy) = bssrdf->anisotropy; return LABEL_SUBSURFACE_SCATTER; } -ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, +ccl_device void subsurface_shader_data_setup(KernelGlobals kg, + IntegratorState state, ccl_private ShaderData *sd, const uint32_t path_flag) { @@ -131,21 +133,21 @@ ccl_device void subsurface_shader_data_setup(INTEGRATOR_STATE_ARGS, } } -ccl_device_inline bool subsurface_scatter(INTEGRATOR_STATE_ARGS) +ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState state) { RNGState rng_state; - path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + path_state_rng_load(state, &rng_state); Ray ray ccl_optional_struct_init; LocalIntersection ss_isect ccl_optional_struct_init; - if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SUBSURFACE_RANDOM_WALK) { - if (!subsurface_random_walk(INTEGRATOR_STATE_PASS, rng_state, ray, ss_isect)) { + if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SUBSURFACE_RANDOM_WALK) { + if (!subsurface_random_walk(kg, state, rng_state, ray, ss_isect)) { return false; } } else { - if (!subsurface_disk(INTEGRATOR_STATE_PASS, rng_state, ray, ss_isect)) { + if (!subsurface_disk(kg, state, rng_state, ray, ss_isect)) { return false; } } @@ -157,11 +159,11 @@ ccl_device_inline bool subsurface_scatter(INTEGRATOR_STATE_ARGS) const int object_flag = kernel_tex_fetch(__object_flag, object); if (object_flag & SD_OBJECT_INTERSECTS_VOLUME) { - float3 P = INTEGRATOR_STATE(ray, P); - const float3 Ng = INTEGRATOR_STATE(isect, Ng); + float3 P = INTEGRATOR_STATE(state, ray, P); + const float3 Ng = INTEGRATOR_STATE(state, isect, Ng); const float3 offset_P = ray_offset(P, -Ng); - integrator_volume_stack_update_for_subsurface(INTEGRATOR_STATE_PASS, offset_P, ray.P); + integrator_volume_stack_update_for_subsurface(kg, state, offset_P, ray.P); } } # endif /* __VOLUME__ */ @@ -172,11 +174,11 @@ ccl_device_inline bool subsurface_scatter(INTEGRATOR_STATE_ARGS) ray.P += ray.D * ray.t * 2.0f; ray.D = -ray.D; - integrator_state_write_isect(INTEGRATOR_STATE_PASS, &ss_isect.hits[0]); - integrator_state_write_ray(INTEGRATOR_STATE_PASS, &ray); + integrator_state_write_isect(kg, state, &ss_isect.hits[0]); + integrator_state_write_ray(kg, state, &ray); /* Advance random number offset for bounce. */ - INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; + INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM; const int shader = intersection_get_shader(kg, &ss_isect.hits[0]); const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_disk.h b/intern/cycles/kernel/integrator/integrator_subsurface_disk.h index 788a5e9b929..1de05ea2696 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface_disk.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface_disk.h @@ -31,7 +31,8 @@ ccl_device_inline float3 subsurface_disk_eval(const float3 radius, float disk_r, /* Subsurface scattering step, from a point on the surface to other * nearby points on the same object. */ -ccl_device_inline bool subsurface_disk(INTEGRATOR_STATE_ARGS, +ccl_device_inline bool subsurface_disk(KernelGlobals kg, + IntegratorState state, RNGState rng_state, ccl_private Ray &ray, ccl_private LocalIntersection &ss_isect) @@ -41,14 +42,14 @@ ccl_device_inline bool subsurface_disk(INTEGRATOR_STATE_ARGS, path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &disk_u, &disk_v); /* Read shading point info from integrator state. */ - const float3 P = INTEGRATOR_STATE(ray, P); - const float ray_dP = INTEGRATOR_STATE(ray, dP); - const float time = INTEGRATOR_STATE(ray, time); - const float3 Ng = INTEGRATOR_STATE(isect, Ng); - const int object = INTEGRATOR_STATE(isect, object); + const float3 P = INTEGRATOR_STATE(state, ray, P); + const float ray_dP = INTEGRATOR_STATE(state, ray, dP); + const float time = INTEGRATOR_STATE(state, ray, time); + const float3 Ng = INTEGRATOR_STATE(state, isect, Ng); + const int object = INTEGRATOR_STATE(state, isect, object); /* Read subsurface scattering parameters. */ - const float3 radius = INTEGRATOR_STATE(subsurface, radius); + const float3 radius = INTEGRATOR_STATE(state, subsurface, radius); /* Pick random axis in local frame and point on disk. */ float3 disk_N, disk_T, disk_B; @@ -175,7 +176,7 @@ ccl_device_inline bool subsurface_disk(INTEGRATOR_STATE_ARGS, if (r < next_sum) { /* Return exit point. */ - INTEGRATOR_STATE_WRITE(path, throughput) *= weight * sum_weights / sample_weight; + INTEGRATOR_STATE_WRITE(state, path, throughput) *= weight * sum_weights / sample_weight; ss_isect.hits[0] = ss_isect.hits[hit]; ss_isect.Ng[0] = ss_isect.Ng[hit]; diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h index 45a43ea67a9..5365093decf 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h @@ -180,7 +180,8 @@ ccl_device_forceinline float3 subsurface_random_walk_pdf(float3 sigma_t, * and the value represents the cutoff level */ #define SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL 9 -ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, +ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, + IntegratorState state, RNGState rng_state, ccl_private Ray &ray, ccl_private LocalIntersection &ss_isect) @@ -188,12 +189,12 @@ ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, float bssrdf_u, bssrdf_v; path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); - const float3 P = INTEGRATOR_STATE(ray, P); - const float3 N = INTEGRATOR_STATE(ray, D); - const float ray_dP = INTEGRATOR_STATE(ray, dP); - const float time = INTEGRATOR_STATE(ray, time); - const float3 Ng = INTEGRATOR_STATE(isect, Ng); - const int object = INTEGRATOR_STATE(isect, object); + const float3 P = INTEGRATOR_STATE(state, ray, P); + const float3 N = INTEGRATOR_STATE(state, ray, D); + const float ray_dP = INTEGRATOR_STATE(state, ray, dP); + const float time = INTEGRATOR_STATE(state, ray, time); + const float3 Ng = INTEGRATOR_STATE(state, isect, Ng); + const int object = INTEGRATOR_STATE(state, isect, object); /* Sample diffuse surface scatter into the object. */ float3 D; @@ -219,12 +220,12 @@ ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, /* Convert subsurface to volume coefficients. * The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */ - const float3 albedo = INTEGRATOR_STATE(subsurface, albedo); - const float3 radius = INTEGRATOR_STATE(subsurface, radius); - const float anisotropy = INTEGRATOR_STATE(subsurface, anisotropy); + const float3 albedo = INTEGRATOR_STATE(state, subsurface, albedo); + const float3 radius = INTEGRATOR_STATE(state, subsurface, radius); + const float anisotropy = INTEGRATOR_STATE(state, subsurface, anisotropy); float3 sigma_t, alpha; - float3 throughput = INTEGRATOR_STATE_WRITE(path, throughput); + float3 throughput = INTEGRATOR_STATE_WRITE(state, path, throughput); subsurface_random_walk_coefficients(albedo, radius, anisotropy, &sigma_t, &alpha, &throughput); float3 sigma_s = sigma_t * alpha; @@ -459,7 +460,7 @@ ccl_device_inline bool subsurface_random_walk(INTEGRATOR_STATE_ARGS, if (hit) { kernel_assert(isfinite3_safe(throughput)); - INTEGRATOR_STATE_WRITE(path, throughput) = throughput; + INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput; } return hit; diff --git a/intern/cycles/kernel/integrator/integrator_volume_stack.h b/intern/cycles/kernel/integrator/integrator_volume_stack.h index 0c4a723de6f..e3a4546508f 100644 --- a/intern/cycles/kernel/integrator/integrator_volume_stack.h +++ b/intern/cycles/kernel/integrator/integrator_volume_stack.h @@ -24,7 +24,7 @@ CCL_NAMESPACE_BEGIN * is inside of. */ template -ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, +ccl_device void volume_stack_enter_exit(KernelGlobals kg, ccl_private const ShaderData *sd, StackReadOp stack_read, StackWriteOp stack_write) @@ -84,28 +84,29 @@ ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, } } -ccl_device void volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, ccl_private const ShaderData *sd) +ccl_device void volume_stack_enter_exit(KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *sd) { volume_stack_enter_exit( - INTEGRATOR_STATE_PASS, + kg, sd, - [=](const int i) { return integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); }, + [=](const int i) { return integrator_state_read_volume_stack(state, i); }, [=](const int i, const VolumeStack entry) { - integrator_state_write_volume_stack(INTEGRATOR_STATE_PASS, i, entry); + integrator_state_write_volume_stack(state, i, entry); }); } -ccl_device void shadow_volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, +ccl_device void shadow_volume_stack_enter_exit(KernelGlobals kg, + IntegratorState state, ccl_private const ShaderData *sd) { volume_stack_enter_exit( - INTEGRATOR_STATE_PASS, + kg, sd, - [=](const int i) { - return integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_PASS, i); - }, + [=](const int i) { return integrator_state_read_shadow_volume_stack(state, i); }, [=](const int i, const VolumeStack entry) { - integrator_state_write_shadow_volume_stack(INTEGRATOR_STATE_PASS, i, entry); + integrator_state_write_shadow_volume_stack(state, i, entry); }); } @@ -123,19 +124,21 @@ ccl_device void shadow_volume_stack_enter_exit(INTEGRATOR_STATE_ARGS, * Use this function after the last bounce to get rid of all volumes apart from * the world's one after the last bounce to avoid render artifacts. */ -ccl_device_inline void volume_stack_clean(INTEGRATOR_STATE_ARGS) +ccl_device_inline void volume_stack_clean(KernelGlobals kg, IntegratorState state) { if (kernel_data.background.volume_shader != SHADER_NONE) { /* Keep the world's volume in stack. */ - INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 1, shader) = SHADER_NONE; + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 1, shader) = SHADER_NONE; } else { - INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 0, shader) = SHADER_NONE; + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 0, shader) = SHADER_NONE; } } template -ccl_device float volume_stack_step_size(INTEGRATOR_STATE_ARGS, StackReadOp stack_read) +ccl_device float volume_stack_step_size(KernelGlobals kg, + IntegratorState state, + StackReadOp stack_read) { float step_size = FLT_MAX; @@ -182,12 +185,12 @@ typedef enum VolumeSampleMethod { VOLUME_SAMPLE_MIS = (VOLUME_SAMPLE_DISTANCE | VOLUME_SAMPLE_EQUIANGULAR), } VolumeSampleMethod; -ccl_device VolumeSampleMethod volume_stack_sample_method(INTEGRATOR_STATE_ARGS) +ccl_device VolumeSampleMethod volume_stack_sample_method(KernelGlobals kg, IntegratorState state) { VolumeSampleMethod method = VOLUME_SAMPLE_NONE; for (int i = 0;; i++) { - VolumeStack entry = integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); + VolumeStack entry = integrator_state_read_volume_stack(state, i); if (entry.shader == SHADER_NONE) { break; } diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index dc0aa9356f7..bc45bbd5b07 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -98,9 +98,7 @@ ccl_device_inline float3 bsdf_eval_diffuse_glossy_ratio(ccl_private const BsdfEv * to render buffers instead of using per-thread memory, and to avoid the * impact of clamping on other contributions. */ -ccl_device_forceinline void kernel_accum_clamp(ccl_global const KernelGlobals *kg, - ccl_private float3 *L, - int bounce) +ccl_device_forceinline void kernel_accum_clamp(KernelGlobals kg, ccl_private float3 *L, int bounce) { #ifdef __KERNEL_DEBUG_NAN__ if (!isfinite3_safe(*L)) { @@ -128,9 +126,9 @@ ccl_device_forceinline void kernel_accum_clamp(ccl_global const KernelGlobals *k /* Get pointer to pixel in render buffer. */ ccl_device_forceinline ccl_global float *kernel_accum_pixel_render_buffer( - INTEGRATOR_STATE_CONST_ARGS, ccl_global float *ccl_restrict render_buffer) + KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer) { - const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kernel_data.film.pass_stride; return render_buffer + render_buffer_offset; @@ -140,7 +138,8 @@ ccl_device_forceinline ccl_global float *kernel_accum_pixel_render_buffer( * Adaptive sampling. */ -ccl_device_inline int kernel_accum_sample(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline int kernel_accum_sample(KernelGlobals kg, + ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer, int sample) { @@ -148,13 +147,13 @@ ccl_device_inline int kernel_accum_sample(INTEGRATOR_STATE_CONST_ARGS, return sample; } - ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, - render_buffer); + ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); return atomic_fetch_and_add_uint32((uint *)(buffer) + kernel_data.film.pass_sample_count, 1); } -ccl_device void kernel_accum_adaptive_buffer(INTEGRATOR_STATE_CONST_ARGS, +ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, + ConstIntegratorState state, const float3 contribution, ccl_global float *ccl_restrict buffer) { @@ -167,7 +166,7 @@ ccl_device void kernel_accum_adaptive_buffer(INTEGRATOR_STATE_CONST_ARGS, return; } - const int sample = INTEGRATOR_STATE(path, sample); + const int sample = INTEGRATOR_STATE(state, path, sample); if (sample_is_even(kernel_data.integrator.sampling_pattern, sample)) { kernel_write_pass_float4( buffer + kernel_data.film.pass_adaptive_aux_buffer, @@ -186,7 +185,8 @@ ccl_device void kernel_accum_adaptive_buffer(INTEGRATOR_STATE_CONST_ARGS, * Returns truth if the contribution is fully handled here and is not to be added to the other * passes (like combined, adaptive sampling). */ -ccl_device bool kernel_accum_shadow_catcher(INTEGRATOR_STATE_CONST_ARGS, +ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, + ConstIntegratorState state, const float3 contribution, ccl_global float *ccl_restrict buffer) { @@ -198,7 +198,7 @@ ccl_device bool kernel_accum_shadow_catcher(INTEGRATOR_STATE_CONST_ARGS, kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); /* Matte pass. */ - if (kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_PASS)) { + if (kernel_shadow_catcher_is_matte_path(kg, state)) { kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution); /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive * sampling is based on how noisy the combined pass is as if there were no catchers in the @@ -206,7 +206,7 @@ ccl_device bool kernel_accum_shadow_catcher(INTEGRATOR_STATE_CONST_ARGS, } /* Shadow catcher pass. */ - if (kernel_shadow_catcher_is_object_pass(INTEGRATOR_STATE_PASS)) { + if (kernel_shadow_catcher_is_object_pass(kg, state)) { kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); return true; } @@ -214,7 +214,8 @@ ccl_device bool kernel_accum_shadow_catcher(INTEGRATOR_STATE_CONST_ARGS, return false; } -ccl_device bool kernel_accum_shadow_catcher_transparent(INTEGRATOR_STATE_CONST_ARGS, +ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, + ConstIntegratorState state, const float3 contribution, const float transparent, ccl_global float *ccl_restrict buffer) @@ -226,12 +227,12 @@ ccl_device bool kernel_accum_shadow_catcher_transparent(INTEGRATOR_STATE_CONST_A kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED); kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); - if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { + if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { return true; } /* Matte pass. */ - if (kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_PASS)) { + if (kernel_shadow_catcher_is_matte_path(kg, state)) { kernel_write_pass_float4( buffer + kernel_data.film.pass_shadow_catcher_matte, make_float4(contribution.x, contribution.y, contribution.z, transparent)); @@ -241,7 +242,7 @@ ccl_device bool kernel_accum_shadow_catcher_transparent(INTEGRATOR_STATE_CONST_A } /* Shadow catcher pass. */ - if (kernel_shadow_catcher_is_object_pass(INTEGRATOR_STATE_PASS)) { + if (kernel_shadow_catcher_is_object_pass(kg, state)) { /* NOTE: The transparency of the shadow catcher pass is ignored. It is not needed for the * calculation and the alpha channel of the pass contains numbers of samples contributed to a * pixel of the pass. */ @@ -252,7 +253,8 @@ ccl_device bool kernel_accum_shadow_catcher_transparent(INTEGRATOR_STATE_CONST_A return false; } -ccl_device void kernel_accum_shadow_catcher_transparent_only(INTEGRATOR_STATE_CONST_ARGS, +ccl_device void kernel_accum_shadow_catcher_transparent_only(KernelGlobals kg, + ConstIntegratorState state, const float transparent, ccl_global float *ccl_restrict buffer) { @@ -263,7 +265,7 @@ ccl_device void kernel_accum_shadow_catcher_transparent_only(INTEGRATOR_STATE_CO kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); /* Matte pass. */ - if (kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_PASS)) { + if (kernel_shadow_catcher_is_matte_path(kg, state)) { kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, transparent); } } @@ -275,12 +277,13 @@ ccl_device void kernel_accum_shadow_catcher_transparent_only(INTEGRATOR_STATE_CO */ /* Write combined pass. */ -ccl_device_inline void kernel_accum_combined_pass(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void kernel_accum_combined_pass(KernelGlobals kg, + ConstIntegratorState state, const float3 contribution, ccl_global float *ccl_restrict buffer) { #ifdef __SHADOW_CATCHER__ - if (kernel_accum_shadow_catcher(INTEGRATOR_STATE_PASS, contribution, buffer)) { + if (kernel_accum_shadow_catcher(kg, state, contribution, buffer)) { return; } #endif @@ -289,19 +292,19 @@ ccl_device_inline void kernel_accum_combined_pass(INTEGRATOR_STATE_CONST_ARGS, kernel_write_pass_float3(buffer + kernel_data.film.pass_combined, contribution); } - kernel_accum_adaptive_buffer(INTEGRATOR_STATE_PASS, contribution, buffer); + kernel_accum_adaptive_buffer(kg, state, contribution, buffer); } /* Write combined pass with transparency. */ -ccl_device_inline void kernel_accum_combined_transparent_pass(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg, + ConstIntegratorState state, const float3 contribution, const float transparent, ccl_global float *ccl_restrict buffer) { #ifdef __SHADOW_CATCHER__ - if (kernel_accum_shadow_catcher_transparent( - INTEGRATOR_STATE_PASS, contribution, transparent, buffer)) { + if (kernel_accum_shadow_catcher_transparent(kg, state, contribution, transparent, buffer)) { return; } #endif @@ -312,11 +315,12 @@ ccl_device_inline void kernel_accum_combined_transparent_pass(INTEGRATOR_STATE_C make_float4(contribution.x, contribution.y, contribution.z, transparent)); } - kernel_accum_adaptive_buffer(INTEGRATOR_STATE_PASS, contribution, buffer); + kernel_accum_adaptive_buffer(kg, state, contribution, buffer); } /* Write background or emission to appropriate pass. */ -ccl_device_inline void kernel_accum_emission_or_background_pass(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void kernel_accum_emission_or_background_pass(KernelGlobals kg, + ConstIntegratorState state, float3 contribution, ccl_global float *ccl_restrict buffer, @@ -327,15 +331,15 @@ ccl_device_inline void kernel_accum_emission_or_background_pass(INTEGRATOR_STATE } #ifdef __PASSES__ - const int path_flag = INTEGRATOR_STATE(path, flag); + const int path_flag = INTEGRATOR_STATE(state, path, flag); int pass_offset = PASS_UNUSED; /* Denoising albedo. */ # ifdef __DENOISING_FEATURES__ if (path_flag & PATH_RAY_DENOISING_FEATURES) { if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { - const float3 denoising_feature_throughput = INTEGRATOR_STATE(path, - denoising_feature_throughput); + const float3 denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); const float3 denoising_albedo = denoising_feature_throughput * contribution; kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); } @@ -349,32 +353,34 @@ ccl_device_inline void kernel_accum_emission_or_background_pass(INTEGRATOR_STATE else if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { /* Indirectly visible through reflection. */ const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ? - ((INTEGRATOR_STATE(path, bounce) == 1) ? + ((INTEGRATOR_STATE(state, path, bounce) == 1) ? kernel_data.film.pass_glossy_direct : kernel_data.film.pass_glossy_indirect) : - ((INTEGRATOR_STATE(path, bounce) == 1) ? + ((INTEGRATOR_STATE(state, path, bounce) == 1) ? kernel_data.film.pass_transmission_direct : kernel_data.film.pass_transmission_indirect); if (glossy_pass_offset != PASS_UNUSED) { /* Glossy is a subset of the throughput, reconstruct it here using the * diffuse-glossy ratio. */ - const float3 ratio = INTEGRATOR_STATE(path, diffuse_glossy_ratio); + const float3 ratio = INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); const float3 glossy_contribution = (one_float3() - ratio) * contribution; kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution); } /* Reconstruct diffuse subset of throughput. */ - pass_offset = (INTEGRATOR_STATE(path, bounce) == 1) ? kernel_data.film.pass_diffuse_direct : - kernel_data.film.pass_diffuse_indirect; + pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ? + kernel_data.film.pass_diffuse_direct : + kernel_data.film.pass_diffuse_indirect; if (pass_offset != PASS_UNUSED) { - contribution *= INTEGRATOR_STATE(path, diffuse_glossy_ratio); + contribution *= INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); } } else if (path_flag & PATH_RAY_VOLUME_PASS) { /* Indirectly visible through volume. */ - pass_offset = (INTEGRATOR_STATE(path, bounce) == 1) ? kernel_data.film.pass_volume_direct : - kernel_data.film.pass_volume_indirect; + pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ? + kernel_data.film.pass_volume_direct : + kernel_data.film.pass_volume_indirect; } /* Single write call for GPU coherence. */ @@ -385,52 +391,52 @@ ccl_device_inline void kernel_accum_emission_or_background_pass(INTEGRATOR_STATE } /* Write light contribution to render buffer. */ -ccl_device_inline void kernel_accum_light(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void kernel_accum_light(KernelGlobals kg, + ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer) { /* The throughput for shadow paths already contains the light shader evaluation. */ - float3 contribution = INTEGRATOR_STATE(shadow_path, throughput); - kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(shadow_path, bounce)); + float3 contribution = INTEGRATOR_STATE(state, shadow_path, throughput); + kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, shadow_path, bounce)); - ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, - render_buffer); + ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); - kernel_accum_combined_pass(INTEGRATOR_STATE_PASS, contribution, buffer); + kernel_accum_combined_pass(kg, state, contribution, buffer); #ifdef __PASSES__ if (kernel_data.film.light_pass_flag & PASS_ANY) { - const int path_flag = INTEGRATOR_STATE(shadow_path, flag); + const int path_flag = INTEGRATOR_STATE(state, shadow_path, flag); int pass_offset = PASS_UNUSED; if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { /* Indirectly visible through reflection. */ const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ? - ((INTEGRATOR_STATE(shadow_path, bounce) == 0) ? + ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? kernel_data.film.pass_glossy_direct : kernel_data.film.pass_glossy_indirect) : - ((INTEGRATOR_STATE(shadow_path, bounce) == 0) ? + ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? kernel_data.film.pass_transmission_direct : kernel_data.film.pass_transmission_indirect); if (glossy_pass_offset != PASS_UNUSED) { /* Glossy is a subset of the throughput, reconstruct it here using the * diffuse-glossy ratio. */ - const float3 ratio = INTEGRATOR_STATE(shadow_path, diffuse_glossy_ratio); + const float3 ratio = INTEGRATOR_STATE(state, shadow_path, diffuse_glossy_ratio); const float3 glossy_contribution = (one_float3() - ratio) * contribution; kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution); } /* Reconstruct diffuse subset of throughput. */ - pass_offset = (INTEGRATOR_STATE(shadow_path, bounce) == 0) ? + pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? kernel_data.film.pass_diffuse_direct : kernel_data.film.pass_diffuse_indirect; if (pass_offset != PASS_UNUSED) { - contribution *= INTEGRATOR_STATE(shadow_path, diffuse_glossy_ratio); + contribution *= INTEGRATOR_STATE(state, shadow_path, diffuse_glossy_ratio); } } else if (path_flag & PATH_RAY_VOLUME_PASS) { /* Indirectly visible through volume. */ - pass_offset = (INTEGRATOR_STATE(shadow_path, bounce) == 0) ? + pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? kernel_data.film.pass_volume_direct : kernel_data.film.pass_volume_indirect; } @@ -443,8 +449,9 @@ ccl_device_inline void kernel_accum_light(INTEGRATOR_STATE_CONST_ARGS, /* Write shadow pass. */ if (kernel_data.film.pass_shadow != PASS_UNUSED && (path_flag & PATH_RAY_SHADOW_FOR_LIGHT) && (path_flag & PATH_RAY_CAMERA)) { - const float3 unshadowed_throughput = INTEGRATOR_STATE(shadow_path, unshadowed_throughput); - const float3 shadowed_throughput = INTEGRATOR_STATE(shadow_path, throughput); + const float3 unshadowed_throughput = INTEGRATOR_STATE( + state, shadow_path, unshadowed_throughput); + const float3 shadowed_throughput = INTEGRATOR_STATE(state, shadow_path, throughput); const float3 shadow = safe_divide_float3_float3(shadowed_throughput, unshadowed_throughput) * kernel_data.film.pass_shadow_scale; kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow, shadow); @@ -458,61 +465,60 @@ ccl_device_inline void kernel_accum_light(INTEGRATOR_STATE_CONST_ARGS, * Note that we accumulate transparency = 1 - alpha in the render buffer. * Otherwise we'd have to write alpha on path termination, which happens * in many places. */ -ccl_device_inline void kernel_accum_transparent(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void kernel_accum_transparent(KernelGlobals kg, + ConstIntegratorState state, const float transparent, ccl_global float *ccl_restrict render_buffer) { - ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, - render_buffer); + ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { kernel_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent); } - kernel_accum_shadow_catcher_transparent_only(INTEGRATOR_STATE_PASS, transparent, buffer); + kernel_accum_shadow_catcher_transparent_only(kg, state, transparent, buffer); } /* Write background contribution to render buffer. * * Includes transparency, matching kernel_accum_transparent. */ -ccl_device_inline void kernel_accum_background(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void kernel_accum_background(KernelGlobals kg, + ConstIntegratorState state, const float3 L, const float transparent, const bool is_transparent_background_ray, ccl_global float *ccl_restrict render_buffer) { - float3 contribution = INTEGRATOR_STATE(path, throughput) * L; - kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(path, bounce) - 1); + float3 contribution = INTEGRATOR_STATE(state, path, throughput) * L; + kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); - ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, - render_buffer); + ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); if (is_transparent_background_ray) { - kernel_accum_transparent(INTEGRATOR_STATE_PASS, transparent, render_buffer); + kernel_accum_transparent(kg, state, transparent, render_buffer); } else { - kernel_accum_combined_transparent_pass( - INTEGRATOR_STATE_PASS, contribution, transparent, buffer); + kernel_accum_combined_transparent_pass(kg, state, contribution, transparent, buffer); } kernel_accum_emission_or_background_pass( - INTEGRATOR_STATE_PASS, contribution, buffer, kernel_data.film.pass_background); + kg, state, contribution, buffer, kernel_data.film.pass_background); } /* Write emission to render buffer. */ -ccl_device_inline void kernel_accum_emission(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void kernel_accum_emission(KernelGlobals kg, + ConstIntegratorState state, const float3 throughput, const float3 L, ccl_global float *ccl_restrict render_buffer) { float3 contribution = throughput * L; - kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(path, bounce) - 1); + kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); - ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, - render_buffer); + ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); - kernel_accum_combined_pass(INTEGRATOR_STATE_PASS, contribution, buffer); + kernel_accum_combined_pass(kg, state, contribution, buffer); kernel_accum_emission_or_background_pass( - INTEGRATOR_STATE_PASS, contribution, buffer, kernel_data.film.pass_emission); + kg, state, contribution, buffer, kernel_data.film.pass_emission); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_adaptive_sampling.h b/intern/cycles/kernel/kernel_adaptive_sampling.h index cdf2601f6c3..b80853fcc51 100644 --- a/intern/cycles/kernel/kernel_adaptive_sampling.h +++ b/intern/cycles/kernel/kernel_adaptive_sampling.h @@ -22,14 +22,15 @@ CCL_NAMESPACE_BEGIN /* Check whether the pixel has converged and should not be sampled anymore. */ -ccl_device_forceinline bool kernel_need_sample_pixel(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_forceinline bool kernel_need_sample_pixel(KernelGlobals kg, + ConstIntegratorState state, ccl_global float *render_buffer) { if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) { return true; } - const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kernel_data.film.pass_stride; ccl_global float *buffer = render_buffer + render_buffer_offset; @@ -40,7 +41,7 @@ ccl_device_forceinline bool kernel_need_sample_pixel(INTEGRATOR_STATE_CONST_ARGS /* Determines whether to continue sampling a given pixel or if it has sufficiently converged. */ -ccl_device bool kernel_adaptive_sampling_convergence_check(ccl_global const KernelGlobals *kg, +ccl_device bool kernel_adaptive_sampling_convergence_check(KernelGlobals kg, ccl_global float *render_buffer, int x, int y, @@ -90,7 +91,7 @@ ccl_device bool kernel_adaptive_sampling_convergence_check(ccl_global const Kern /* This is a simple box filter in two passes. * When a pixel demands more adaptive samples, let its neighboring pixels draw more samples too. */ -ccl_device void kernel_adaptive_sampling_filter_x(ccl_global const KernelGlobals *kg, +ccl_device void kernel_adaptive_sampling_filter_x(KernelGlobals kg, ccl_global float *render_buffer, int y, int start_x, @@ -123,7 +124,7 @@ ccl_device void kernel_adaptive_sampling_filter_x(ccl_global const KernelGlobals } } -ccl_device void kernel_adaptive_sampling_filter_y(ccl_global const KernelGlobals *kg, +ccl_device void kernel_adaptive_sampling_filter_y(KernelGlobals kg, ccl_global float *render_buffer, int x, int start_y, diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index 6cbb8dcc291..933ee0082c2 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -24,7 +24,7 @@ CCL_NAMESPACE_BEGIN -ccl_device void kernel_displace_evaluate(ccl_global const KernelGlobals *kg, +ccl_device void kernel_displace_evaluate(KernelGlobals kg, ccl_global const KernelShaderEvalInput *input, ccl_global float *output, const int offset) @@ -37,7 +37,7 @@ ccl_device void kernel_displace_evaluate(ccl_global const KernelGlobals *kg, /* Evaluate displacement shader. */ const float3 P = sd.P; - shader_eval_displacement(INTEGRATOR_STATE_PASS_NULL, &sd); + shader_eval_displacement(kg, INTEGRATOR_STATE_NULL, &sd); float3 D = sd.P - P; object_inverse_dir_transform(kg, &sd, &D); @@ -58,7 +58,7 @@ ccl_device void kernel_displace_evaluate(ccl_global const KernelGlobals *kg, output[offset * 3 + 2] += D.z; } -ccl_device void kernel_background_evaluate(ccl_global const KernelGlobals *kg, +ccl_device void kernel_background_evaluate(KernelGlobals kg, ccl_global const KernelShaderEvalInput *input, ccl_global float *output, const int offset) @@ -77,7 +77,7 @@ ccl_device void kernel_background_evaluate(ccl_global const KernelGlobals *kg, * This is being evaluated for all BSDFs, so path flag does not contain a specific type. */ const int path_flag = PATH_RAY_EMISSION; shader_eval_surface( - INTEGRATOR_STATE_PASS_NULL, &sd, NULL, path_flag); + kg, INTEGRATOR_STATE_NULL, &sd, NULL, path_flag); float3 color = shader_background_eval(&sd); #ifdef __KERNEL_DEBUG_NAN__ diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index 73683a15c5d..58a34668f45 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -46,7 +46,7 @@ ccl_device float2 camera_sample_aperture(ccl_constant KernelCamera *cam, float u return bokeh; } -ccl_device void camera_sample_perspective(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device void camera_sample_perspective(KernelGlobals kg, float raster_x, float raster_y, float lens_u, @@ -185,7 +185,7 @@ ccl_device void camera_sample_perspective(ccl_global const KernelGlobals *ccl_re } /* Orthographic Camera */ -ccl_device void camera_sample_orthographic(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device void camera_sample_orthographic(KernelGlobals kg, float raster_x, float raster_y, float lens_u, @@ -370,7 +370,7 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, /* Common */ -ccl_device_inline void camera_sample(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device_inline void camera_sample(KernelGlobals kg, int x, int y, float filter_u, @@ -444,13 +444,13 @@ ccl_device_inline void camera_sample(ccl_global const KernelGlobals *ccl_restric /* Utilities */ -ccl_device_inline float3 camera_position(ccl_global const KernelGlobals *kg) +ccl_device_inline float3 camera_position(KernelGlobals kg) { Transform cameratoworld = kernel_data.cam.cameratoworld; return make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); } -ccl_device_inline float camera_distance(ccl_global const KernelGlobals *kg, float3 P) +ccl_device_inline float camera_distance(KernelGlobals kg, float3 P) { Transform cameratoworld = kernel_data.cam.cameratoworld; float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); @@ -464,7 +464,7 @@ ccl_device_inline float camera_distance(ccl_global const KernelGlobals *kg, floa } } -ccl_device_inline float camera_z_depth(ccl_global const KernelGlobals *kg, float3 P) +ccl_device_inline float camera_z_depth(KernelGlobals kg, float3 P) { if (kernel_data.cam.type != CAMERA_PANORAMA) { Transform worldtocamera = kernel_data.cam.worldtocamera; @@ -477,7 +477,7 @@ ccl_device_inline float camera_z_depth(ccl_global const KernelGlobals *kg, float } } -ccl_device_inline float3 camera_direction_from_point(ccl_global const KernelGlobals *kg, float3 P) +ccl_device_inline float3 camera_direction_from_point(KernelGlobals kg, float3 P) { Transform cameratoworld = kernel_data.cam.cameratoworld; @@ -491,7 +491,7 @@ ccl_device_inline float3 camera_direction_from_point(ccl_global const KernelGlob } } -ccl_device_inline float3 camera_world_to_ndc(ccl_global const KernelGlobals *kg, +ccl_device_inline float3 camera_world_to_ndc(KernelGlobals kg, ccl_private ShaderData *sd, float3 P) { diff --git a/intern/cycles/kernel/kernel_color.h b/intern/cycles/kernel/kernel_color.h index 9e8e0e68b8f..0d7bfecd5f3 100644 --- a/intern/cycles/kernel/kernel_color.h +++ b/intern/cycles/kernel/kernel_color.h @@ -20,14 +20,14 @@ CCL_NAMESPACE_BEGIN -ccl_device float3 xyz_to_rgb(ccl_global const KernelGlobals *kg, float3 xyz) +ccl_device float3 xyz_to_rgb(KernelGlobals kg, float3 xyz) { return make_float3(dot(float4_to_float3(kernel_data.film.xyz_to_r), xyz), dot(float4_to_float3(kernel_data.film.xyz_to_g), xyz), dot(float4_to_float3(kernel_data.film.xyz_to_b), xyz)); } -ccl_device float linear_rgb_to_gray(ccl_global const KernelGlobals *kg, float3 c) +ccl_device float linear_rgb_to_gray(KernelGlobals kg, float3 c) { return dot(c, float4_to_float3(kernel_data.film.rgb_to_y)); } diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h index 015587ccbbd..8d329b8dac3 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/kernel_emission.h @@ -25,7 +25,8 @@ CCL_NAMESPACE_BEGIN /* Evaluate shader on light. */ ccl_device_noinline_cpu float3 -light_sample_shader_eval(INTEGRATOR_STATE_ARGS, +light_sample_shader_eval(KernelGlobals kg, + IntegratorState state, ccl_private ShaderData *ccl_restrict emission_sd, ccl_private LightSample *ccl_restrict ls, float time) @@ -73,7 +74,7 @@ light_sample_shader_eval(INTEGRATOR_STATE_ARGS, /* No proper path flag, we're evaluating this for all closures. that's * weak but we'd have to do multiple evaluations otherwise. */ shader_eval_surface( - INTEGRATOR_STATE_PASS, emission_sd, NULL, PATH_RAY_EMISSION); + kg, state, emission_sd, NULL, PATH_RAY_EMISSION); /* Evaluate closures. */ #ifdef __BACKGROUND_MIS__ @@ -105,7 +106,7 @@ ccl_device_inline bool light_sample_is_light(ccl_private const LightSample *ccl_ } /* Early path termination of shadow rays. */ -ccl_device_inline bool light_sample_terminate(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device_inline bool light_sample_terminate(KernelGlobals kg, ccl_private const LightSample *ccl_restrict ls, ccl_private BsdfEval *ccl_restrict eval, const float rand_terminate) @@ -133,10 +134,8 @@ ccl_device_inline bool light_sample_terminate(ccl_global const KernelGlobals *cc * of a triangle. Surface is lifted by amount h along normal n in the incident * point. */ -ccl_device_inline float3 -shadow_ray_smooth_surface_offset(ccl_global const KernelGlobals *ccl_restrict kg, - ccl_private const ShaderData *ccl_restrict sd, - float3 Ng) +ccl_device_inline float3 shadow_ray_smooth_surface_offset( + KernelGlobals kg, ccl_private const ShaderData *ccl_restrict sd, float3 Ng) { float3 V[3], N[3]; triangle_vertices_and_normals(kg, sd->prim, V, N); @@ -180,7 +179,7 @@ shadow_ray_smooth_surface_offset(ccl_global const KernelGlobals *ccl_restrict kg /* Ray offset to avoid shadow terminator artifact. */ -ccl_device_inline float3 shadow_ray_offset(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device_inline float3 shadow_ray_offset(KernelGlobals kg, ccl_private const ShaderData *ccl_restrict sd, float3 L) { @@ -247,7 +246,7 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri /* Create shadow ray towards light sample. */ ccl_device_inline void light_sample_to_surface_shadow_ray( - ccl_global const KernelGlobals *ccl_restrict kg, + KernelGlobals kg, ccl_private const ShaderData *ccl_restrict sd, ccl_private const LightSample *ccl_restrict ls, ccl_private Ray *ray) @@ -258,7 +257,7 @@ ccl_device_inline void light_sample_to_surface_shadow_ray( /* Create shadow ray towards light sample. */ ccl_device_inline void light_sample_to_volume_shadow_ray( - ccl_global const KernelGlobals *ccl_restrict kg, + KernelGlobals kg, ccl_private const ShaderData *ccl_restrict sd, ccl_private const LightSample *ccl_restrict ls, const float3 P, diff --git a/intern/cycles/kernel/kernel_id_passes.h b/intern/cycles/kernel/kernel_id_passes.h index 07b96d0e1a8..d5b8c90a828 100644 --- a/intern/cycles/kernel/kernel_id_passes.h +++ b/intern/cycles/kernel/kernel_id_passes.h @@ -92,7 +92,7 @@ ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_sl } /* post-sorting for Cryptomatte */ -ccl_device_inline void kernel_cryptomatte_post(ccl_global const KernelGlobals *kg, +ccl_device_inline void kernel_cryptomatte_post(KernelGlobals kg, ccl_global float *render_buffer, int pixel_index) { diff --git a/intern/cycles/kernel/kernel_jitter.h b/intern/cycles/kernel/kernel_jitter.h index 1f745ab1da9..b62ec7fda42 100644 --- a/intern/cycles/kernel/kernel_jitter.h +++ b/intern/cycles/kernel/kernel_jitter.h @@ -72,10 +72,7 @@ ccl_device_inline float cmj_randfloat_simple(uint i, uint p) return cmj_hash_simple(i, p) * (1.0f / (float)0xFFFFFFFF); } -ccl_device float pmj_sample_1D(ccl_global const KernelGlobals *kg, - uint sample, - uint rng_hash, - uint dimension) +ccl_device float pmj_sample_1D(KernelGlobals kg, uint sample, uint rng_hash, uint dimension) { /* Perform Owen shuffle of the sample number to reorder the samples. */ #ifdef _SIMPLE_HASH_ @@ -118,7 +115,7 @@ ccl_device float pmj_sample_1D(ccl_global const KernelGlobals *kg, return fx; } -ccl_device void pmj_sample_2D(ccl_global const KernelGlobals *kg, +ccl_device void pmj_sample_2D(KernelGlobals kg, uint sample, uint rng_hash, uint dimension, diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index 33d0c09a32a..a7a95918b4e 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -45,7 +45,7 @@ typedef struct LightSample { /* Regular Light */ template -ccl_device_inline bool light_sample(ccl_global const KernelGlobals *kg, +ccl_device_inline bool light_sample(KernelGlobals kg, const int lamp, const float randu, const float randv, @@ -209,7 +209,7 @@ ccl_device_inline bool light_sample(ccl_global const KernelGlobals *kg, return (ls->pdf > 0.0f); } -ccl_device bool lights_intersect(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device bool lights_intersect(KernelGlobals kg, ccl_private const Ray *ccl_restrict ray, ccl_private Intersection *ccl_restrict isect, const int last_prim, @@ -298,7 +298,7 @@ ccl_device bool lights_intersect(ccl_global const KernelGlobals *ccl_restrict kg return isect->prim != PRIM_NONE; } -ccl_device bool light_sample_from_distant_ray(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device bool light_sample_from_distant_ray(KernelGlobals kg, const float3 ray_D, const int lamp, ccl_private LightSample *ccl_restrict ls) @@ -362,7 +362,7 @@ ccl_device bool light_sample_from_distant_ray(ccl_global const KernelGlobals *cc return true; } -ccl_device bool light_sample_from_intersection(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device bool light_sample_from_intersection(KernelGlobals kg, ccl_private const Intersection *ccl_restrict isect, const float3 ray_P, const float3 ray_D, @@ -464,7 +464,7 @@ ccl_device bool light_sample_from_intersection(ccl_global const KernelGlobals *c /* returns true if the triangle is has motion blur or an instancing transform applied */ ccl_device_inline bool triangle_world_space_vertices( - ccl_global const KernelGlobals *kg, int object, int prim, float time, float3 V[3]) + KernelGlobals kg, int object, int prim, float time, float3 V[3]) { bool has_motion = false; const int object_flag = kernel_tex_fetch(__object_flag, object); @@ -492,7 +492,7 @@ ccl_device_inline bool triangle_world_space_vertices( return has_motion; } -ccl_device_inline float triangle_light_pdf_area(ccl_global const KernelGlobals *kg, +ccl_device_inline float triangle_light_pdf_area(KernelGlobals kg, const float3 Ng, const float3 I, float t) @@ -506,7 +506,7 @@ ccl_device_inline float triangle_light_pdf_area(ccl_global const KernelGlobals * return t * t * pdf / cos_pi; } -ccl_device_forceinline float triangle_light_pdf(ccl_global const KernelGlobals *kg, +ccl_device_forceinline float triangle_light_pdf(KernelGlobals kg, ccl_private const ShaderData *sd, float t) { @@ -578,7 +578,7 @@ ccl_device_forceinline float triangle_light_pdf(ccl_global const KernelGlobals * } template -ccl_device_forceinline void triangle_light_sample(ccl_global const KernelGlobals *kg, +ccl_device_forceinline void triangle_light_sample(KernelGlobals kg, int prim, int object, float randu, @@ -747,8 +747,7 @@ ccl_device_forceinline void triangle_light_sample(ccl_global const KernelGlobals /* Light Distribution */ -ccl_device int light_distribution_sample(ccl_global const KernelGlobals *kg, - ccl_private float *randu) +ccl_device int light_distribution_sample(KernelGlobals kg, ccl_private float *randu) { /* This is basically std::upper_bound as used by PBRT, to find a point light or * triangle to emit from, proportional to area. a good improvement would be to @@ -786,15 +785,13 @@ ccl_device int light_distribution_sample(ccl_global const KernelGlobals *kg, /* Generic Light */ -ccl_device_inline bool light_select_reached_max_bounces(ccl_global const KernelGlobals *kg, - int index, - int bounce) +ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals kg, int index, int bounce) { return (bounce > kernel_tex_fetch(__lights, index).max_bounces); } template -ccl_device_noinline bool light_distribution_sample(ccl_global const KernelGlobals *kg, +ccl_device_noinline bool light_distribution_sample(KernelGlobals kg, float randu, const float randv, const float time, @@ -834,20 +831,19 @@ ccl_device_noinline bool light_distribution_sample(ccl_global const KernelGlobal return light_sample(kg, lamp, randu, randv, P, path_flag, ls); } -ccl_device_inline bool light_distribution_sample_from_volume_segment( - ccl_global const KernelGlobals *kg, - float randu, - const float randv, - const float time, - const float3 P, - const int bounce, - const int path_flag, - ccl_private LightSample *ls) +ccl_device_inline bool light_distribution_sample_from_volume_segment(KernelGlobals kg, + float randu, + const float randv, + const float time, + const float3 P, + const int bounce, + const int path_flag, + ccl_private LightSample *ls) { return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); } -ccl_device_inline bool light_distribution_sample_from_position(ccl_global const KernelGlobals *kg, +ccl_device_inline bool light_distribution_sample_from_position(KernelGlobals kg, float randu, const float randv, const float time, @@ -859,7 +855,7 @@ ccl_device_inline bool light_distribution_sample_from_position(ccl_global const return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); } -ccl_device_inline bool light_distribution_sample_new_position(ccl_global const KernelGlobals *kg, +ccl_device_inline bool light_distribution_sample_new_position(KernelGlobals kg, const float randu, const float randv, const float time, diff --git a/intern/cycles/kernel/kernel_light_background.h b/intern/cycles/kernel/kernel_light_background.h index 3669ff50455..2e828b8b765 100644 --- a/intern/cycles/kernel/kernel_light_background.h +++ b/intern/cycles/kernel/kernel_light_background.h @@ -24,7 +24,7 @@ CCL_NAMESPACE_BEGIN #ifdef __BACKGROUND_MIS__ -ccl_device float3 background_map_sample(ccl_global const KernelGlobals *kg, +ccl_device float3 background_map_sample(KernelGlobals kg, float randu, float randv, ccl_private float *pdf) @@ -109,7 +109,7 @@ ccl_device float3 background_map_sample(ccl_global const KernelGlobals *kg, /* TODO(sergey): Same as above, after the release we should consider using * 'noinline' for all devices. */ -ccl_device float background_map_pdf(ccl_global const KernelGlobals *kg, float3 direction) +ccl_device float background_map_pdf(KernelGlobals kg, float3 direction) { float2 uv = direction_to_equirectangular(direction); int res_x = kernel_data.background.map_res_x; @@ -143,11 +143,7 @@ ccl_device float background_map_pdf(ccl_global const KernelGlobals *kg, float3 d } ccl_device_inline bool background_portal_data_fetch_and_check_side( - ccl_global const KernelGlobals *kg, - float3 P, - int index, - ccl_private float3 *lightpos, - ccl_private float3 *dir) + KernelGlobals kg, float3 P, int index, ccl_private float3 *lightpos, ccl_private float3 *dir) { int portal = kernel_data.background.portal_offset + index; const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); @@ -162,11 +158,8 @@ ccl_device_inline bool background_portal_data_fetch_and_check_side( return false; } -ccl_device_inline float background_portal_pdf(ccl_global const KernelGlobals *kg, - float3 P, - float3 direction, - int ignore_portal, - ccl_private bool *is_possible) +ccl_device_inline float background_portal_pdf( + KernelGlobals kg, float3 P, float3 direction, int ignore_portal, ccl_private bool *is_possible) { float portal_pdf = 0.0f; @@ -226,7 +219,7 @@ ccl_device_inline float background_portal_pdf(ccl_global const KernelGlobals *kg return (num_possible > 0) ? portal_pdf / num_possible : 0.0f; } -ccl_device int background_num_possible_portals(ccl_global const KernelGlobals *kg, float3 P) +ccl_device int background_num_possible_portals(KernelGlobals kg, float3 P) { int num_possible_portals = 0; for (int p = 0; p < kernel_data.background.num_portals; p++) { @@ -237,7 +230,7 @@ ccl_device int background_num_possible_portals(ccl_global const KernelGlobals *k return num_possible_portals; } -ccl_device float3 background_portal_sample(ccl_global const KernelGlobals *kg, +ccl_device float3 background_portal_sample(KernelGlobals kg, float3 P, float randu, float randv, @@ -292,7 +285,7 @@ ccl_device float3 background_portal_sample(ccl_global const KernelGlobals *kg, return zero_float3(); } -ccl_device_inline float3 background_sun_sample(ccl_global const KernelGlobals *kg, +ccl_device_inline float3 background_sun_sample(KernelGlobals kg, float randu, float randv, ccl_private float *pdf) @@ -304,7 +297,7 @@ ccl_device_inline float3 background_sun_sample(ccl_global const KernelGlobals *k return D; } -ccl_device_inline float background_sun_pdf(ccl_global const KernelGlobals *kg, float3 D) +ccl_device_inline float background_sun_pdf(KernelGlobals kg, float3 D) { const float3 N = float4_to_float3(kernel_data.background.sun); const float angle = kernel_data.background.sun.w; @@ -312,7 +305,7 @@ ccl_device_inline float background_sun_pdf(ccl_global const KernelGlobals *kg, f } ccl_device_inline float3 background_light_sample( - ccl_global const KernelGlobals *kg, float3 P, float randu, float randv, ccl_private float *pdf) + KernelGlobals kg, float3 P, float randu, float randv, ccl_private float *pdf) { float portal_method_pdf = kernel_data.background.portal_weight; float sun_method_pdf = kernel_data.background.sun_weight; @@ -412,9 +405,7 @@ ccl_device_inline float3 background_light_sample( return D; } -ccl_device float background_light_pdf(ccl_global const KernelGlobals *kg, - float3 P, - float3 direction) +ccl_device float background_light_pdf(KernelGlobals kg, float3 P, float3 direction) { float portal_method_pdf = kernel_data.background.portal_weight; float sun_method_pdf = kernel_data.background.sun_weight; diff --git a/intern/cycles/kernel/kernel_light_common.h b/intern/cycles/kernel/kernel_light_common.h index 9421ac462e2..9e2b738f376 100644 --- a/intern/cycles/kernel/kernel_light_common.h +++ b/intern/cycles/kernel/kernel_light_common.h @@ -214,10 +214,7 @@ ccl_device bool light_spread_clamp_area_light(const float3 P, return true; } -ccl_device float lamp_light_pdf(ccl_global const KernelGlobals *kg, - const float3 Ng, - const float3 I, - float t) +ccl_device float lamp_light_pdf(KernelGlobals kg, const float3 Ng, const float3 I, float t) { float cos_pi = dot(Ng, I); diff --git a/intern/cycles/kernel/kernel_lookup_table.h b/intern/cycles/kernel/kernel_lookup_table.h index 3c8577af417..2c26e668d7b 100644 --- a/intern/cycles/kernel/kernel_lookup_table.h +++ b/intern/cycles/kernel/kernel_lookup_table.h @@ -20,10 +20,7 @@ CCL_NAMESPACE_BEGIN /* Interpolated lookup table access */ -ccl_device float lookup_table_read(ccl_global const KernelGlobals *kg, - float x, - int offset, - int size) +ccl_device float lookup_table_read(KernelGlobals kg, float x, int offset, int size) { x = saturate(x) * (size - 1); @@ -40,7 +37,7 @@ ccl_device float lookup_table_read(ccl_global const KernelGlobals *kg, } ccl_device float lookup_table_read_2D( - ccl_global const KernelGlobals *kg, float x, float y, int offset, int xsize, int ysize) + KernelGlobals kg, float x, float y, int offset, int xsize, int ysize) { y = saturate(y) * (ysize - 1); diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h index b981e750dda..4d05b63bfbd 100644 --- a/intern/cycles/kernel/kernel_passes.h +++ b/intern/cycles/kernel/kernel_passes.h @@ -25,9 +25,9 @@ CCL_NAMESPACE_BEGIN /* Get pointer to pixel in render buffer. */ ccl_device_forceinline ccl_global float *kernel_pass_pixel_render_buffer( - INTEGRATOR_STATE_CONST_ARGS, ccl_global float *ccl_restrict render_buffer) + KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer) { - const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kernel_data.film.pass_stride; return render_buffer + render_buffer_offset; @@ -36,11 +36,12 @@ ccl_device_forceinline ccl_global float *kernel_pass_pixel_render_buffer( #ifdef __DENOISING_FEATURES__ ccl_device_forceinline void kernel_write_denoising_features_surface( - INTEGRATOR_STATE_ARGS, + KernelGlobals kg, + IntegratorState state, ccl_private const ShaderData *sd, ccl_global float *ccl_restrict render_buffer) { - if (!(INTEGRATOR_STATE(path, flag) & PATH_RAY_DENOISING_FEATURES)) { + if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_DENOISING_FEATURES)) { return; } @@ -49,7 +50,7 @@ ccl_device_forceinline void kernel_write_denoising_features_surface( return; } - ccl_global float *buffer = kernel_pass_pixel_render_buffer(INTEGRATOR_STATE_PASS, render_buffer); + ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); float3 normal = zero_float3(); float3 diffuse_albedo = zero_float3(); @@ -109,32 +110,34 @@ ccl_device_forceinline void kernel_write_denoising_features_surface( } if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { - const float3 denoising_feature_throughput = INTEGRATOR_STATE(path, - denoising_feature_throughput); + const float3 denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); const float3 denoising_albedo = ensure_finite3(denoising_feature_throughput * diffuse_albedo); kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); } - INTEGRATOR_STATE_WRITE(path, flag) &= ~PATH_RAY_DENOISING_FEATURES; + INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES; } else { - INTEGRATOR_STATE_WRITE(path, denoising_feature_throughput) *= specular_albedo; + INTEGRATOR_STATE_WRITE(state, path, denoising_feature_throughput) *= specular_albedo; } } -ccl_device_forceinline void kernel_write_denoising_features_volume(INTEGRATOR_STATE_ARGS, +ccl_device_forceinline void kernel_write_denoising_features_volume(KernelGlobals kg, + IntegratorState state, const float3 albedo, const bool scatter, ccl_global float *ccl_restrict render_buffer) { - ccl_global float *buffer = kernel_pass_pixel_render_buffer(INTEGRATOR_STATE_PASS, render_buffer); - const float3 denoising_feature_throughput = INTEGRATOR_STATE(path, denoising_feature_throughput); + ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); + const float3 denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); if (scatter && kernel_data.film.pass_denoising_normal != PASS_UNUSED) { /* Assume scatter is sufficiently diffuse to stop writing denoising features. */ - INTEGRATOR_STATE_WRITE(path, flag) &= ~PATH_RAY_DENOISING_FEATURES; + INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES; /* Write view direction as normal. */ const float3 denoising_normal = make_float3(0.0f, 0.0f, -1.0f); @@ -153,7 +156,8 @@ ccl_device_forceinline void kernel_write_denoising_features_volume(INTEGRATOR_ST /* Write shadow catcher passes on a bounce from the shadow catcher object. */ ccl_device_forceinline void kernel_write_shadow_catcher_bounce_data( - INTEGRATOR_STATE_ARGS, + KernelGlobals kg, + IntegratorState state, ccl_private const ShaderData *sd, ccl_global float *ccl_restrict render_buffer) { @@ -164,18 +168,18 @@ ccl_device_forceinline void kernel_write_shadow_catcher_bounce_data( kernel_assert(kernel_data.film.pass_shadow_catcher_sample_count != PASS_UNUSED); kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); - if (!kernel_shadow_catcher_is_path_split_bounce(INTEGRATOR_STATE_PASS, sd->object_flag)) { + if (!kernel_shadow_catcher_is_path_split_bounce(kg, state, sd->object_flag)) { return; } - ccl_global float *buffer = kernel_pass_pixel_render_buffer(INTEGRATOR_STATE_PASS, render_buffer); + ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); /* Count sample for the shadow catcher object. */ kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_sample_count, 1.0f); /* Since the split is done, the sample does not contribute to the matte, so accumulate it as * transparency to the matte. */ - const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, average(throughput)); } @@ -191,12 +195,13 @@ ccl_device_inline size_t kernel_write_id_pass(ccl_global float *ccl_restrict buf return depth * 4; } -ccl_device_inline void kernel_write_data_passes(INTEGRATOR_STATE_ARGS, +ccl_device_inline void kernel_write_data_passes(KernelGlobals kg, + IntegratorState state, ccl_private const ShaderData *sd, ccl_global float *ccl_restrict render_buffer) { #ifdef __PASSES__ - const int path_flag = INTEGRATOR_STATE(path, flag); + const int path_flag = INTEGRATOR_STATE(state, path, flag); if (!(path_flag & PATH_RAY_CAMERA)) { return; @@ -208,12 +213,12 @@ ccl_device_inline void kernel_write_data_passes(INTEGRATOR_STATE_ARGS, return; } - ccl_global float *buffer = kernel_pass_pixel_render_buffer(INTEGRATOR_STATE_PASS, render_buffer); + ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); if (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)) { if (!(sd->flag & SD_TRANSPARENT) || kernel_data.film.pass_alpha_threshold == 0.0f || average(shader_bsdf_alpha(kg, sd)) >= kernel_data.film.pass_alpha_threshold) { - if (INTEGRATOR_STATE(path, sample) == 0) { + if (INTEGRATOR_STATE(state, path, sample) == 0) { if (flag & PASSMASK(DEPTH)) { const float depth = camera_z_depth(kg, sd->P); kernel_write_pass_float(buffer + kernel_data.film.pass_depth, depth); @@ -250,12 +255,12 @@ ccl_device_inline void kernel_write_data_passes(INTEGRATOR_STATE_ARGS, kernel_write_pass_float(buffer + kernel_data.film.pass_motion_weight, 1.0f); } - INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_SINGLE_PASS_DONE; + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SINGLE_PASS_DONE; } } if (kernel_data.film.cryptomatte_passes) { - const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); const float matte_weight = average(throughput) * (1.0f - average(shader_bsdf_transparency(kg, sd))); if (matte_weight > 0.0f) { @@ -279,17 +284,17 @@ ccl_device_inline void kernel_write_data_passes(INTEGRATOR_STATE_ARGS, } if (flag & PASSMASK(DIFFUSE_COLOR)) { - const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); kernel_write_pass_float3(buffer + kernel_data.film.pass_diffuse_color, shader_bsdf_diffuse(kg, sd) * throughput); } if (flag & PASSMASK(GLOSSY_COLOR)) { - const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); kernel_write_pass_float3(buffer + kernel_data.film.pass_glossy_color, shader_bsdf_glossy(kg, sd) * throughput); } if (flag & PASSMASK(TRANSMISSION_COLOR)) { - const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); kernel_write_pass_float3(buffer + kernel_data.film.pass_transmission_color, shader_bsdf_transmission(kg, sd) * throughput); } @@ -314,7 +319,7 @@ ccl_device_inline void kernel_write_data_passes(INTEGRATOR_STATE_ARGS, mist = powf(mist, mist_falloff); /* Modulate by transparency */ - const float3 throughput = INTEGRATOR_STATE(path, throughput); + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); const float3 alpha = shader_bsdf_alpha(kg, sd); const float mist_output = (1.0f - mist) * average(throughput * alpha); diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h index e04ed5b1cc1..66eb468fdca 100644 --- a/intern/cycles/kernel/kernel_path_state.h +++ b/intern/cycles/kernel/kernel_path_state.h @@ -23,71 +23,73 @@ CCL_NAMESPACE_BEGIN /* Initialize queues, so that the this path is considered terminated. * Used for early outputs in the camera ray initialization, as well as initialization of split * states for shadow catcher. */ -ccl_device_inline void path_state_init_queues(INTEGRATOR_STATE_ARGS) +ccl_device_inline void path_state_init_queues(IntegratorState state) { - INTEGRATOR_STATE_WRITE(path, queued_kernel) = 0; - INTEGRATOR_STATE_WRITE(shadow_path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; } /* Minimalistic initialization of the path state, which is needed for early outputs in the * integrator initialization to work. */ -ccl_device_inline void path_state_init(INTEGRATOR_STATE_ARGS, +ccl_device_inline void path_state_init(IntegratorState state, ccl_global const KernelWorkTile *ccl_restrict tile, const int x, const int y) { const uint render_pixel_index = (uint)tile->offset + x + y * tile->stride; - INTEGRATOR_STATE_WRITE(path, render_pixel_index) = render_pixel_index; + INTEGRATOR_STATE_WRITE(state, path, render_pixel_index) = render_pixel_index; - path_state_init_queues(INTEGRATOR_STATE_PASS); + path_state_init_queues(state); } /* Initialize the rest of the path state needed to continue the path integration. */ -ccl_device_inline void path_state_init_integrator(INTEGRATOR_STATE_ARGS, +ccl_device_inline void path_state_init_integrator(KernelGlobals kg, + IntegratorState state, const int sample, const uint rng_hash) { - INTEGRATOR_STATE_WRITE(path, sample) = sample; - INTEGRATOR_STATE_WRITE(path, bounce) = 0; - INTEGRATOR_STATE_WRITE(path, diffuse_bounce) = 0; - INTEGRATOR_STATE_WRITE(path, glossy_bounce) = 0; - INTEGRATOR_STATE_WRITE(path, transmission_bounce) = 0; - INTEGRATOR_STATE_WRITE(path, transparent_bounce) = 0; - INTEGRATOR_STATE_WRITE(path, volume_bounce) = 0; - INTEGRATOR_STATE_WRITE(path, volume_bounds_bounce) = 0; - INTEGRATOR_STATE_WRITE(path, rng_hash) = rng_hash; - INTEGRATOR_STATE_WRITE(path, rng_offset) = PRNG_BASE_NUM; - INTEGRATOR_STATE_WRITE(path, flag) = PATH_RAY_CAMERA | PATH_RAY_MIS_SKIP | - PATH_RAY_TRANSPARENT_BACKGROUND; - INTEGRATOR_STATE_WRITE(path, mis_ray_pdf) = 0.0f; - INTEGRATOR_STATE_WRITE(path, mis_ray_t) = 0.0f; - INTEGRATOR_STATE_WRITE(path, min_ray_pdf) = FLT_MAX; - INTEGRATOR_STATE_WRITE(path, throughput) = make_float3(1.0f, 1.0f, 1.0f); + INTEGRATOR_STATE_WRITE(state, path, sample) = sample; + INTEGRATOR_STATE_WRITE(state, path, bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, diffuse_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, glossy_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, transmission_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, transparent_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, volume_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, volume_bounds_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, rng_hash) = rng_hash; + INTEGRATOR_STATE_WRITE(state, path, rng_offset) = PRNG_BASE_NUM; + INTEGRATOR_STATE_WRITE(state, path, flag) = PATH_RAY_CAMERA | PATH_RAY_MIS_SKIP | + PATH_RAY_TRANSPARENT_BACKGROUND; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = 0.0f; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; + INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = FLT_MAX; + INTEGRATOR_STATE_WRITE(state, path, throughput) = make_float3(1.0f, 1.0f, 1.0f); if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { - INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 0, object) = OBJECT_NONE; - INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 0, shader) = kernel_data.background.volume_shader; - INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 1, object) = OBJECT_NONE; - INTEGRATOR_STATE_ARRAY_WRITE(volume_stack, 1, shader) = SHADER_NONE; + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 0, object) = OBJECT_NONE; + INTEGRATOR_STATE_ARRAY_WRITE( + state, volume_stack, 0, shader) = kernel_data.background.volume_shader; + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 1, object) = OBJECT_NONE; + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 1, shader) = SHADER_NONE; } #ifdef __DENOISING_FEATURES__ if (kernel_data.kernel_features & KERNEL_FEATURE_DENOISING) { - INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_DENOISING_FEATURES; - INTEGRATOR_STATE_WRITE(path, denoising_feature_throughput) = one_float3(); + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_DENOISING_FEATURES; + INTEGRATOR_STATE_WRITE(state, path, denoising_feature_throughput) = one_float3(); } #endif } -ccl_device_inline void path_state_next(INTEGRATOR_STATE_ARGS, int label) +ccl_device_inline void path_state_next(KernelGlobals kg, IntegratorState state, int label) { - uint32_t flag = INTEGRATOR_STATE(path, flag); + uint32_t flag = INTEGRATOR_STATE(state, path, flag); /* ray through transparent keeps same flags from previous ray and is * not counted as a regular bounce, transparent has separate max */ if (label & LABEL_TRANSPARENT) { - uint32_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce) + 1; + uint32_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce) + 1; flag |= PATH_RAY_TRANSPARENT; if (transparent_bounce >= kernel_data.integrator.transparent_max_bounce) { @@ -97,14 +99,14 @@ ccl_device_inline void path_state_next(INTEGRATOR_STATE_ARGS, int label) if (!kernel_data.integrator.transparent_shadows) flag |= PATH_RAY_MIS_SKIP; - INTEGRATOR_STATE_WRITE(path, flag) = flag; - INTEGRATOR_STATE_WRITE(path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(state, path, flag) = flag; + INTEGRATOR_STATE_WRITE(state, path, transparent_bounce) = transparent_bounce; /* Random number generator next bounce. */ - INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; + INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM; return; } - uint32_t bounce = INTEGRATOR_STATE(path, bounce) + 1; + uint32_t bounce = INTEGRATOR_STATE(state, path, bounce) + 1; if (bounce >= kernel_data.integrator.max_bounce) { flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } @@ -120,8 +122,8 @@ ccl_device_inline void path_state_next(INTEGRATOR_STATE_ARGS, int label) flag |= PATH_RAY_VOLUME_PASS; } - const int volume_bounce = INTEGRATOR_STATE(path, volume_bounce) + 1; - INTEGRATOR_STATE_WRITE(path, volume_bounce) = volume_bounce; + const int volume_bounce = INTEGRATOR_STATE(state, path, volume_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, volume_bounce) = volume_bounce; if (volume_bounce >= kernel_data.integrator.max_volume_bounce) { flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } @@ -135,15 +137,15 @@ ccl_device_inline void path_state_next(INTEGRATOR_STATE_ARGS, int label) flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; if (label & LABEL_DIFFUSE) { - const int diffuse_bounce = INTEGRATOR_STATE(path, diffuse_bounce) + 1; - INTEGRATOR_STATE_WRITE(path, diffuse_bounce) = diffuse_bounce; + const int diffuse_bounce = INTEGRATOR_STATE(state, path, diffuse_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, diffuse_bounce) = diffuse_bounce; if (diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) { flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } } else { - const int glossy_bounce = INTEGRATOR_STATE(path, glossy_bounce) + 1; - INTEGRATOR_STATE_WRITE(path, glossy_bounce) = glossy_bounce; + const int glossy_bounce = INTEGRATOR_STATE(state, path, glossy_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, glossy_bounce) = glossy_bounce; if (glossy_bounce >= kernel_data.integrator.max_glossy_bounce) { flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } @@ -158,8 +160,8 @@ ccl_device_inline void path_state_next(INTEGRATOR_STATE_ARGS, int label) flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; } - const int transmission_bounce = INTEGRATOR_STATE(path, transmission_bounce) + 1; - INTEGRATOR_STATE_WRITE(path, transmission_bounce) = transmission_bounce; + const int transmission_bounce = INTEGRATOR_STATE(state, path, transmission_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, transmission_bounce) = transmission_bounce; if (transmission_bounce >= kernel_data.integrator.max_transmission_bounce) { flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } @@ -183,36 +185,36 @@ ccl_device_inline void path_state_next(INTEGRATOR_STATE_ARGS, int label) } } - INTEGRATOR_STATE_WRITE(path, flag) = flag; - INTEGRATOR_STATE_WRITE(path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(state, path, flag) = flag; + INTEGRATOR_STATE_WRITE(state, path, bounce) = bounce; /* Random number generator next bounce. */ - INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; + INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM; } #ifdef __VOLUME__ -ccl_device_inline bool path_state_volume_next(INTEGRATOR_STATE_ARGS) +ccl_device_inline bool path_state_volume_next(IntegratorState state) { /* For volume bounding meshes we pass through without counting transparent * bounces, only sanity check in case self intersection gets us stuck. */ - uint32_t volume_bounds_bounce = INTEGRATOR_STATE(path, volume_bounds_bounce) + 1; - INTEGRATOR_STATE_WRITE(path, volume_bounds_bounce) = volume_bounds_bounce; + uint32_t volume_bounds_bounce = INTEGRATOR_STATE(state, path, volume_bounds_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, volume_bounds_bounce) = volume_bounds_bounce; if (volume_bounds_bounce > VOLUME_BOUNDS_MAX) { return false; } /* Random number generator next bounce. */ if (volume_bounds_bounce > 1) { - INTEGRATOR_STATE_WRITE(path, rng_offset) += PRNG_BOUNCE_NUM; + INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM; } return true; } #endif -ccl_device_inline uint path_state_ray_visibility(INTEGRATOR_STATE_CONST_ARGS) +ccl_device_inline uint path_state_ray_visibility(ConstIntegratorState state) { - const uint32_t path_flag = INTEGRATOR_STATE(path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); uint32_t visibility = path_flag & PATH_RAY_ALL_VISIBILITY; @@ -231,18 +233,19 @@ ccl_device_inline uint path_state_ray_visibility(INTEGRATOR_STATE_CONST_ARGS) return visibility; } -ccl_device_inline float path_state_continuation_probability(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline float path_state_continuation_probability(KernelGlobals kg, + ConstIntegratorState state, const uint32_t path_flag) { if (path_flag & PATH_RAY_TRANSPARENT) { - const uint32_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce); + const uint32_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); /* Do at least specified number of bounces without RR. */ if (transparent_bounce <= kernel_data.integrator.transparent_min_bounce) { return 1.0f; } } else { - const uint32_t bounce = INTEGRATOR_STATE(path, bounce); + const uint32_t bounce = INTEGRATOR_STATE(state, path, bounce); /* Do at least specified number of bounces without RR. */ if (bounce <= kernel_data.integrator.min_bounce) { return 1.0f; @@ -251,17 +254,18 @@ ccl_device_inline float path_state_continuation_probability(INTEGRATOR_STATE_CON /* Probabilistic termination: use sqrt() to roughly match typical view * transform and do path termination a bit later on average. */ - return min(sqrtf(max3(fabs(INTEGRATOR_STATE(path, throughput)))), 1.0f); + return min(sqrtf(max3(fabs(INTEGRATOR_STATE(state, path, throughput)))), 1.0f); } -ccl_device_inline bool path_state_ao_bounce(INTEGRATOR_STATE_CONST_ARGS) +ccl_device_inline bool path_state_ao_bounce(KernelGlobals kg, ConstIntegratorState state) { if (!kernel_data.integrator.ao_bounces) { return false; } - const int bounce = INTEGRATOR_STATE(path, bounce) - INTEGRATOR_STATE(path, transmission_bounce) - - (INTEGRATOR_STATE(path, glossy_bounce) > 0) + 1; + const int bounce = INTEGRATOR_STATE(state, path, bounce) - + INTEGRATOR_STATE(state, path, transmission_bounce) - + (INTEGRATOR_STATE(state, path, glossy_bounce) > 0) + 1; return (bounce > kernel_data.integrator.ao_bounces); } @@ -281,26 +285,27 @@ typedef struct RNGState { int sample; } RNGState; -ccl_device_inline void path_state_rng_load(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void path_state_rng_load(ConstIntegratorState state, ccl_private RNGState *rng_state) { - rng_state->rng_hash = INTEGRATOR_STATE(path, rng_hash); - rng_state->rng_offset = INTEGRATOR_STATE(path, rng_offset); - rng_state->sample = INTEGRATOR_STATE(path, sample); + rng_state->rng_hash = INTEGRATOR_STATE(state, path, rng_hash); + rng_state->rng_offset = INTEGRATOR_STATE(state, path, rng_offset); + rng_state->sample = INTEGRATOR_STATE(state, path, sample); } -ccl_device_inline void shadow_path_state_rng_load(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void shadow_path_state_rng_load(ConstIntegratorState state, ccl_private RNGState *rng_state) { - const uint shadow_bounces = INTEGRATOR_STATE(shadow_path, transparent_bounce) - - INTEGRATOR_STATE(path, transparent_bounce); + const uint shadow_bounces = INTEGRATOR_STATE(state, shadow_path, transparent_bounce) - + INTEGRATOR_STATE(state, path, transparent_bounce); - rng_state->rng_hash = INTEGRATOR_STATE(path, rng_hash); - rng_state->rng_offset = INTEGRATOR_STATE(path, rng_offset) + PRNG_BOUNCE_NUM * shadow_bounces; - rng_state->sample = INTEGRATOR_STATE(path, sample); + rng_state->rng_hash = INTEGRATOR_STATE(state, path, rng_hash); + rng_state->rng_offset = INTEGRATOR_STATE(state, path, rng_offset) + + PRNG_BOUNCE_NUM * shadow_bounces; + rng_state->sample = INTEGRATOR_STATE(state, path, sample); } -ccl_device_inline float path_state_rng_1D(ccl_global const KernelGlobals *kg, +ccl_device_inline float path_state_rng_1D(KernelGlobals kg, ccl_private const RNGState *rng_state, int dimension) { @@ -308,7 +313,7 @@ ccl_device_inline float path_state_rng_1D(ccl_global const KernelGlobals *kg, kg, rng_state->rng_hash, rng_state->sample, rng_state->rng_offset + dimension); } -ccl_device_inline void path_state_rng_2D(ccl_global const KernelGlobals *kg, +ccl_device_inline void path_state_rng_2D(KernelGlobals kg, ccl_private const RNGState *rng_state, int dimension, ccl_private float *fx, @@ -318,7 +323,7 @@ ccl_device_inline void path_state_rng_2D(ccl_global const KernelGlobals *kg, kg, rng_state->rng_hash, rng_state->sample, rng_state->rng_offset + dimension, fx, fy); } -ccl_device_inline float path_state_rng_1D_hash(ccl_global const KernelGlobals *kg, +ccl_device_inline float path_state_rng_1D_hash(KernelGlobals kg, ccl_private const RNGState *rng_state, uint hash) { @@ -329,7 +334,7 @@ ccl_device_inline float path_state_rng_1D_hash(ccl_global const KernelGlobals *k kg, cmj_hash_simple(rng_state->rng_hash, hash), rng_state->sample, rng_state->rng_offset); } -ccl_device_inline float path_branched_rng_1D(ccl_global const KernelGlobals *kg, +ccl_device_inline float path_branched_rng_1D(KernelGlobals kg, ccl_private const RNGState *rng_state, int branch, int num_branches, @@ -341,7 +346,7 @@ ccl_device_inline float path_branched_rng_1D(ccl_global const KernelGlobals *kg, rng_state->rng_offset + dimension); } -ccl_device_inline void path_branched_rng_2D(ccl_global const KernelGlobals *kg, +ccl_device_inline void path_branched_rng_2D(KernelGlobals kg, ccl_private const RNGState *rng_state, int branch, int num_branches, @@ -360,7 +365,7 @@ ccl_device_inline void path_branched_rng_2D(ccl_global const KernelGlobals *kg, /* Utility functions to get light termination value, * since it might not be needed in many cases. */ -ccl_device_inline float path_state_rng_light_termination(ccl_global const KernelGlobals *kg, +ccl_device_inline float path_state_rng_light_termination(KernelGlobals kg, ccl_private const RNGState *state) { if (kernel_data.integrator.light_inv_rr_threshold > 0.0f) { diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h index 7db4289acec..e5e87453611 100644 --- a/intern/cycles/kernel/kernel_random.h +++ b/intern/cycles/kernel/kernel_random.h @@ -38,7 +38,7 @@ CCL_NAMESPACE_BEGIN */ # define SOBOL_SKIP 64 -ccl_device uint sobol_dimension(ccl_global const KernelGlobals *kg, int index, int dimension) +ccl_device uint sobol_dimension(KernelGlobals kg, int index, int dimension) { uint result = 0; uint i = index + SOBOL_SKIP; @@ -51,7 +51,7 @@ ccl_device uint sobol_dimension(ccl_global const KernelGlobals *kg, int index, i #endif /* __SOBOL__ */ -ccl_device_forceinline float path_rng_1D(ccl_global const KernelGlobals *kg, +ccl_device_forceinline float path_rng_1D(KernelGlobals kg, uint rng_hash, int sample, int dimension) @@ -85,7 +85,7 @@ ccl_device_forceinline float path_rng_1D(ccl_global const KernelGlobals *kg, #endif } -ccl_device_forceinline void path_rng_2D(ccl_global const KernelGlobals *kg, +ccl_device_forceinline void path_rng_2D(KernelGlobals kg, uint rng_hash, int sample, int dimension, @@ -141,7 +141,7 @@ ccl_device_inline uint hash_iqnt2d(const uint x, const uint y) return n; } -ccl_device_inline uint path_rng_hash_init(ccl_global const KernelGlobals *ccl_restrict kg, +ccl_device_inline uint path_rng_hash_init(KernelGlobals kg, const int sample, const int x, const int y) diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index b5a52ff866d..4a57d22775a 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -104,7 +104,8 @@ ccl_device_inline void shader_copy_volume_phases(ccl_private ShaderVolumePhases } #endif /* __VOLUME__ */ -ccl_device_inline void shader_prepare_surface_closures(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void shader_prepare_surface_closures(KernelGlobals kg, + ConstIntegratorState state, ccl_private ShaderData *sd) { /* Defensive sampling. @@ -112,7 +113,8 @@ ccl_device_inline void shader_prepare_surface_closures(INTEGRATOR_STATE_CONST_AR * We can likely also do defensive sampling at deeper bounces, particularly * for cases like a perfect mirror but possibly also others. This will need * a good heuristic. */ - if (INTEGRATOR_STATE(path, bounce) + INTEGRATOR_STATE(path, transparent_bounce) == 0 && + if (INTEGRATOR_STATE(state, path, bounce) + INTEGRATOR_STATE(state, path, transparent_bounce) == + 0 && sd->num_closure > 1) { float sum = 0.0f; @@ -136,7 +138,8 @@ ccl_device_inline void shader_prepare_surface_closures(INTEGRATOR_STATE_CONST_AR * Blurring of bsdf after bounces, for rays that have a small likelihood * of following this particular path (diffuse, rough glossy) */ if (kernel_data.integrator.filter_glossy != FLT_MAX) { - float blur_pdf = kernel_data.integrator.filter_glossy * INTEGRATOR_STATE(path, min_ray_pdf); + float blur_pdf = kernel_data.integrator.filter_glossy * + INTEGRATOR_STATE(state, path, min_ray_pdf); if (blur_pdf < 1.0f) { float blur_roughness = sqrtf(1.0f - blur_pdf) * 0.5f; @@ -182,7 +185,7 @@ ccl_device_forceinline bool _shader_bsdf_exclude(ClosureType type, uint light_sh return false; } -ccl_device_inline float _shader_bsdf_multi_eval(ccl_global const KernelGlobals *kg, +ccl_device_inline float _shader_bsdf_multi_eval(KernelGlobals kg, ccl_private ShaderData *sd, const float3 omega_in, const bool is_transmission, @@ -226,7 +229,7 @@ ccl_device ccl_device_inline #endif float - shader_bsdf_eval(ccl_global const KernelGlobals *kg, + shader_bsdf_eval(KernelGlobals kg, ccl_private ShaderData *sd, const float3 omega_in, const bool is_transmission, @@ -306,7 +309,7 @@ shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict sd, /* Sample direction for picked BSDF, and return evaluation and pdf for all * BSDFs combined using MIS. */ -ccl_device int shader_bsdf_sample_closure(ccl_global const KernelGlobals *kg, +ccl_device int shader_bsdf_sample_closure(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private const ShaderClosure *sc, float randu, @@ -360,8 +363,7 @@ ccl_device float shader_bsdf_average_roughness(ccl_private const ShaderData *sd) return (sum_weight > 0.0f) ? roughness / sum_weight : 0.0f; } -ccl_device float3 shader_bsdf_transparency(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device float3 shader_bsdf_transparency(KernelGlobals kg, ccl_private const ShaderData *sd) { if (sd->flag & SD_HAS_ONLY_VOLUME) { return one_float3(); @@ -374,8 +376,7 @@ ccl_device float3 shader_bsdf_transparency(ccl_global const KernelGlobals *kg, } } -ccl_device void shader_bsdf_disable_transparency(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd) +ccl_device void shader_bsdf_disable_transparency(KernelGlobals kg, ccl_private ShaderData *sd) { if (sd->flag & SD_TRANSPARENT) { for (int i = 0; i < sd->num_closure; i++) { @@ -391,8 +392,7 @@ ccl_device void shader_bsdf_disable_transparency(ccl_global const KernelGlobals } } -ccl_device float3 shader_bsdf_alpha(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device float3 shader_bsdf_alpha(KernelGlobals kg, ccl_private const ShaderData *sd) { float3 alpha = one_float3() - shader_bsdf_transparency(kg, sd); @@ -402,8 +402,7 @@ ccl_device float3 shader_bsdf_alpha(ccl_global const KernelGlobals *kg, return alpha; } -ccl_device float3 shader_bsdf_diffuse(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device float3 shader_bsdf_diffuse(KernelGlobals kg, ccl_private const ShaderData *sd) { float3 eval = zero_float3(); @@ -417,8 +416,7 @@ ccl_device float3 shader_bsdf_diffuse(ccl_global const KernelGlobals *kg, return eval; } -ccl_device float3 shader_bsdf_glossy(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device float3 shader_bsdf_glossy(KernelGlobals kg, ccl_private const ShaderData *sd) { float3 eval = zero_float3(); @@ -432,8 +430,7 @@ ccl_device float3 shader_bsdf_glossy(ccl_global const KernelGlobals *kg, return eval; } -ccl_device float3 shader_bsdf_transmission(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device float3 shader_bsdf_transmission(KernelGlobals kg, ccl_private const ShaderData *sd) { float3 eval = zero_float3(); @@ -447,8 +444,7 @@ ccl_device float3 shader_bsdf_transmission(ccl_global const KernelGlobals *kg, return eval; } -ccl_device float3 shader_bsdf_average_normal(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device float3 shader_bsdf_average_normal(KernelGlobals kg, ccl_private const ShaderData *sd) { float3 N = zero_float3(); @@ -461,8 +457,7 @@ ccl_device float3 shader_bsdf_average_normal(ccl_global const KernelGlobals *kg, return (is_zero(N)) ? sd->N : normalize(N); } -ccl_device float3 shader_bsdf_ao_normal(ccl_global const KernelGlobals *kg, - ccl_private const ShaderData *sd) +ccl_device float3 shader_bsdf_ao_normal(KernelGlobals kg, ccl_private const ShaderData *sd) { float3 N = zero_float3(); @@ -499,7 +494,7 @@ ccl_device float3 shader_bssrdf_normal(ccl_private const ShaderData *sd) /* Constant emission optimization */ -ccl_device bool shader_constant_emission_eval(ccl_global const KernelGlobals *kg, +ccl_device bool shader_constant_emission_eval(KernelGlobals kg, int shader, ccl_private float3 *eval) { @@ -543,8 +538,7 @@ ccl_device float3 shader_emissive_eval(ccl_private const ShaderData *sd) /* Holdout */ -ccl_device float3 shader_holdout_apply(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd) +ccl_device float3 shader_holdout_apply(KernelGlobals kg, ccl_private ShaderData *sd) { float3 weight = zero_float3(); @@ -582,7 +576,8 @@ ccl_device float3 shader_holdout_apply(ccl_global const KernelGlobals *kg, /* Surface Evaluation */ template -ccl_device void shader_eval_surface(INTEGRATOR_STATE_CONST_ARGS, +ccl_device void shader_eval_surface(KernelGlobals kg, + ConstIntegratorState state, ccl_private ShaderData *ccl_restrict sd, ccl_global float *ccl_restrict buffer, int path_flag) @@ -604,18 +599,17 @@ ccl_device void shader_eval_surface(INTEGRATOR_STATE_CONST_ARGS, #ifdef __OSL__ if (kg->osl) { if (sd->object == OBJECT_NONE && sd->lamp == LAMP_NONE) { - OSLShader::eval_background(INTEGRATOR_STATE_PASS, sd, path_flag); + OSLShader::eval_background(kg, state, sd, path_flag); } else { - OSLShader::eval_surface(INTEGRATOR_STATE_PASS, sd, path_flag); + OSLShader::eval_surface(kg, state, sd, path_flag); } } else #endif { #ifdef __SVM__ - svm_eval_nodes( - INTEGRATOR_STATE_PASS, sd, buffer, path_flag); + svm_eval_nodes(kg, state, sd, buffer, path_flag); #else if (sd->object == OBJECT_NONE) { sd->closure_emission_background = make_float3(0.8f, 0.8f, 0.8f); @@ -632,11 +626,14 @@ ccl_device void shader_eval_surface(INTEGRATOR_STATE_CONST_ARGS, #endif } - if (KERNEL_NODES_FEATURE(BSDF) && (sd->flag & SD_BSDF_NEEDS_LCG)) { - sd->lcg_state = lcg_state_init(INTEGRATOR_STATE(path, rng_hash), - INTEGRATOR_STATE(path, rng_offset), - INTEGRATOR_STATE(path, sample), - 0xb4bc3953); + IF_KERNEL_NODES_FEATURE(BSDF) + { + if (sd->flag & SD_BSDF_NEEDS_LCG) { + sd->lcg_state = lcg_state_init(INTEGRATOR_STATE(state, path, rng_hash), + INTEGRATOR_STATE(state, path, rng_offset), + INTEGRATOR_STATE(state, path, sample), + 0xb4bc3953); + } } } @@ -672,7 +669,7 @@ ccl_device_inline float _shader_volume_phase_multi_eval( return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; } -ccl_device float shader_volume_phase_eval(ccl_global const KernelGlobals *kg, +ccl_device float shader_volume_phase_eval(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private const ShaderVolumePhases *phases, const float3 omega_in, @@ -683,7 +680,7 @@ ccl_device float shader_volume_phase_eval(ccl_global const KernelGlobals *kg, return _shader_volume_phase_multi_eval(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f); } -ccl_device int shader_volume_phase_sample(ccl_global const KernelGlobals *kg, +ccl_device int shader_volume_phase_sample(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private const ShaderVolumePhases *phases, float randu, @@ -742,7 +739,7 @@ ccl_device int shader_volume_phase_sample(ccl_global const KernelGlobals *kg, return label; } -ccl_device int shader_phase_sample_closure(ccl_global const KernelGlobals *kg, +ccl_device int shader_phase_sample_closure(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private const ShaderVolumeClosure *sc, float randu, @@ -767,7 +764,8 @@ ccl_device int shader_phase_sample_closure(ccl_global const KernelGlobals *kg, /* Volume Evaluation */ template -ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS, +ccl_device_inline void shader_eval_volume(KernelGlobals kg, + ConstIntegratorState state, ccl_private ShaderData *ccl_restrict sd, const int path_flag, StackReadOp stack_read) @@ -820,13 +818,13 @@ ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS, # ifdef __SVM__ # ifdef __OSL__ if (kg->osl) { - OSLShader::eval_volume(INTEGRATOR_STATE_PASS, sd, path_flag); + OSLShader::eval_volume(kg, state, sd, path_flag); } else # endif { svm_eval_nodes( - INTEGRATOR_STATE_PASS, sd, NULL, path_flag); + kg, state, sd, NULL, path_flag); } # endif @@ -843,7 +841,9 @@ ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS, /* Displacement Evaluation */ -ccl_device void shader_eval_displacement(INTEGRATOR_STATE_CONST_ARGS, ccl_private ShaderData *sd) +ccl_device void shader_eval_displacement(KernelGlobals kg, + ConstIntegratorState state, + ccl_private ShaderData *sd) { sd->num_closure = 0; sd->num_closure_left = 0; @@ -852,19 +852,19 @@ ccl_device void shader_eval_displacement(INTEGRATOR_STATE_CONST_ARGS, ccl_privat #ifdef __SVM__ # ifdef __OSL__ if (kg->osl) - OSLShader::eval_displacement(INTEGRATOR_STATE_PASS, sd); + OSLShader::eval_displacement(kg, state, sd); else # endif { svm_eval_nodes( - INTEGRATOR_STATE_PASS, sd, NULL, 0); + kg, state, sd, NULL, 0); } #endif } /* Cryptomatte */ -ccl_device float shader_cryptomatte_id(ccl_global const KernelGlobals *kg, int shader) +ccl_device float shader_cryptomatte_id(KernelGlobals kg, int shader) { return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).cryptomatte_id; } diff --git a/intern/cycles/kernel/kernel_shadow_catcher.h b/intern/cycles/kernel/kernel_shadow_catcher.h index 824749818a4..8dc7a568b33 100644 --- a/intern/cycles/kernel/kernel_shadow_catcher.h +++ b/intern/cycles/kernel/kernel_shadow_catcher.h @@ -22,7 +22,8 @@ CCL_NAMESPACE_BEGIN /* Check whether current surface bounce is where path is to be split for the shadow catcher. */ -ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(INTEGRATOR_STATE_ARGS, +ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(KernelGlobals kg, + IntegratorState state, const int object_flag) { #ifdef __SHADOW_CATCHER__ @@ -38,7 +39,7 @@ ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(INTEGRATOR_STA return false; } - const int path_flag = INTEGRATOR_STATE(path, flag); + const int path_flag = INTEGRATOR_STATE(state, path, flag); if ((path_flag & PATH_RAY_TRANSPARENT_BACKGROUND) == 0) { /* Split only on primary rays, secondary bounces are to treat shadow catcher as a regular @@ -58,13 +59,14 @@ ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(INTEGRATOR_STA } /* Check whether the current path can still split. */ -ccl_device_inline bool kernel_shadow_catcher_path_can_split(INTEGRATOR_STATE_CONST_ARGS) +ccl_device_inline bool kernel_shadow_catcher_path_can_split(KernelGlobals kg, + ConstIntegratorState state) { if (INTEGRATOR_PATH_IS_TERMINATED && INTEGRATOR_SHADOW_PATH_IS_TERMINATED) { return false; } - const int path_flag = INTEGRATOR_STATE(path, flag); + const int path_flag = INTEGRATOR_STATE(state, path, flag); if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) { /* Shadow catcher was already hit and the state was split. No further split is allowed. */ @@ -76,21 +78,23 @@ ccl_device_inline bool kernel_shadow_catcher_path_can_split(INTEGRATOR_STATE_CON /* NOTE: Leaves kernel scheduling information untouched. Use INIT semantic for one of the paths * after this function. */ -ccl_device_inline bool kernel_shadow_catcher_split(INTEGRATOR_STATE_ARGS, const int object_flags) +ccl_device_inline bool kernel_shadow_catcher_split(KernelGlobals kg, + IntegratorState state, + const int object_flags) { #ifdef __SHADOW_CATCHER__ - if (!kernel_shadow_catcher_is_path_split_bounce(INTEGRATOR_STATE_PASS, object_flags)) { + if (!kernel_shadow_catcher_is_path_split_bounce(kg, state, object_flags)) { return false; } /* The split is to be done. Mark the current state as such, so that it stops contributing to the * shadow catcher matte pass, but keeps contributing to the combined pass. */ - INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_SHADOW_CATCHER_HIT; + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SHADOW_CATCHER_HIT; /* Split new state from the current one. This new state will only track contribution of shadow * catcher objects ignoring non-catcher objects. */ - integrator_state_shadow_catcher_split(INTEGRATOR_STATE_PASS); + integrator_state_shadow_catcher_split(kg, state); return true; #else @@ -101,14 +105,16 @@ ccl_device_inline bool kernel_shadow_catcher_split(INTEGRATOR_STATE_ARGS, const #ifdef __SHADOW_CATCHER__ -ccl_device_forceinline bool kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_CONST_ARGS) +ccl_device_forceinline bool kernel_shadow_catcher_is_matte_path(KernelGlobals kg, + ConstIntegratorState state) { - return (INTEGRATOR_STATE(path, flag) & PATH_RAY_SHADOW_CATCHER_HIT) == 0; + return (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_HIT) == 0; } -ccl_device_forceinline bool kernel_shadow_catcher_is_object_pass(INTEGRATOR_STATE_CONST_ARGS) +ccl_device_forceinline bool kernel_shadow_catcher_is_object_pass(KernelGlobals kg, + ConstIntegratorState state) { - return INTEGRATOR_STATE(path, flag) & PATH_RAY_SHADOW_CATCHER_PASS; + return INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS; } #endif /* __SHADOW_CATCHER__ */ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 3a5a11d2c10..5625c0e4d19 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -813,7 +813,7 @@ typedef struct ccl_align(16) ShaderData float ray_dP; #ifdef __OSL__ - const struct KernelGlobals *osl_globals; + const struct KernelGlobalsCPU *osl_globals; const struct IntegratorStateCPU *osl_path_state; #endif @@ -1505,63 +1505,77 @@ enum KernelFeatureFlag : unsigned int { KERNEL_FEATURE_NODE_BUMP_STATE = (1U << 5U), KERNEL_FEATURE_NODE_VORONOI_EXTRA = (1U << 6U), KERNEL_FEATURE_NODE_RAYTRACE = (1U << 7U), + KERNEL_FEATURE_NODE_AOV = (1U << 8U), + KERNEL_FEATURE_NODE_LIGHT_PATH = (1U << 9U), /* Use denoising kernels and output denoising passes. */ - KERNEL_FEATURE_DENOISING = (1U << 8U), + KERNEL_FEATURE_DENOISING = (1U << 10U), /* Use path tracing kernels. */ - KERNEL_FEATURE_PATH_TRACING = (1U << 9U), + KERNEL_FEATURE_PATH_TRACING = (1U << 11U), /* BVH/sampling kernel features. */ - KERNEL_FEATURE_HAIR = (1U << 10U), - KERNEL_FEATURE_HAIR_THICK = (1U << 11U), - KERNEL_FEATURE_OBJECT_MOTION = (1U << 12U), - KERNEL_FEATURE_CAMERA_MOTION = (1U << 13U), + KERNEL_FEATURE_HAIR = (1U << 12U), + KERNEL_FEATURE_HAIR_THICK = (1U << 13U), + KERNEL_FEATURE_OBJECT_MOTION = (1U << 14U), + KERNEL_FEATURE_CAMERA_MOTION = (1U << 15U), /* Denotes whether baking functionality is needed. */ - KERNEL_FEATURE_BAKING = (1U << 14U), + KERNEL_FEATURE_BAKING = (1U << 16U), /* Use subsurface scattering materials. */ - KERNEL_FEATURE_SUBSURFACE = (1U << 15U), + KERNEL_FEATURE_SUBSURFACE = (1U << 17U), /* Use volume materials. */ - KERNEL_FEATURE_VOLUME = (1U << 16U), + KERNEL_FEATURE_VOLUME = (1U << 18U), /* Use OpenSubdiv patch evaluation */ - KERNEL_FEATURE_PATCH_EVALUATION = (1U << 17U), + KERNEL_FEATURE_PATCH_EVALUATION = (1U << 19U), /* Use Transparent shadows */ - KERNEL_FEATURE_TRANSPARENT = (1U << 18U), + KERNEL_FEATURE_TRANSPARENT = (1U << 20U), /* Use shadow catcher. */ - KERNEL_FEATURE_SHADOW_CATCHER = (1U << 19U), + KERNEL_FEATURE_SHADOW_CATCHER = (1U << 21U), /* Per-uber shader usage flags. */ - KERNEL_FEATURE_PRINCIPLED = (1U << 20U), + KERNEL_FEATURE_PRINCIPLED = (1U << 22U), /* Light render passes. */ - KERNEL_FEATURE_LIGHT_PASSES = (1U << 21U), + KERNEL_FEATURE_LIGHT_PASSES = (1U << 23U), /* Shadow render pass. */ - KERNEL_FEATURE_SHADOW_PASS = (1U << 22U), + KERNEL_FEATURE_SHADOW_PASS = (1U << 24U), }; /* Shader node feature mask, to specialize shader evaluation for kernels. */ #define KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT \ - (KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VORONOI_EXTRA) + (KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VORONOI_EXTRA | \ + KERNEL_FEATURE_NODE_LIGHT_PATH) #define KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW \ (KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | \ KERNEL_FEATURE_NODE_HAIR | KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE | \ - KERNEL_FEATURE_NODE_VORONOI_EXTRA) + KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_LIGHT_PATH) #define KERNEL_FEATURE_NODE_MASK_SURFACE \ - (KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW | KERNEL_FEATURE_NODE_RAYTRACE) + (KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW | KERNEL_FEATURE_NODE_RAYTRACE | \ + KERNEL_FEATURE_NODE_AOV | KERNEL_FEATURE_NODE_LIGHT_PATH) #define KERNEL_FEATURE_NODE_MASK_VOLUME \ - (KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | KERNEL_FEATURE_NODE_VORONOI_EXTRA) + (KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | \ + KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_LIGHT_PATH) #define KERNEL_FEATURE_NODE_MASK_DISPLACEMENT \ (KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE) #define KERNEL_FEATURE_NODE_MASK_BUMP KERNEL_FEATURE_NODE_MASK_DISPLACEMENT -#define KERNEL_NODES_FEATURE(feature) ((node_feature_mask & (KERNEL_FEATURE_NODE_##feature)) != 0U) +/* Must be constexpr on the CPU to avoid compile errors because the state types + * are different depending on the main, shadow or null path. For GPU we don't have + * C++17 everywhere so can't use it. */ +#ifdef __KERNEL_CPU__ +# define IF_KERNEL_NODES_FEATURE(feature) \ + if constexpr ((node_feature_mask & (KERNEL_FEATURE_NODE_##feature)) != 0U) +#else +# define IF_KERNEL_NODES_FEATURE(feature) \ + if ((node_feature_mask & (KERNEL_FEATURE_NODE_##feature)) != 0U) +#endif CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp index e814fcca246..94712a4dd13 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/osl_closures.cpp @@ -500,7 +500,7 @@ bool CBSDFClosure::skip(const ShaderData *sd, int path_flag, int scattering) { /* caustic options */ if ((scattering & LABEL_GLOSSY) && (path_flag & PATH_RAY_DIFFUSE)) { - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if ((!kernel_data.integrator.caustics_reflective && (scattering & LABEL_REFLECT)) || (!kernel_data.integrator.caustics_refractive && (scattering & LABEL_TRANSMIT))) { diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 2c7f5eb4948..bb7655fbe9a 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -149,7 +149,7 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { @@ -187,7 +187,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { @@ -222,7 +222,7 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, float time) { ShaderData *sd = (ShaderData *)(sg->renderstate); - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if (from == u_ndc) { copy_matrix(result, kernel_data.cam.ndctoworld); @@ -254,7 +254,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, float time) { ShaderData *sd = (ShaderData *)(sg->renderstate); - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if (to == u_ndc) { copy_matrix(result, kernel_data.cam.worldtondc); @@ -288,7 +288,7 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { @@ -316,7 +316,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { @@ -339,7 +339,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result, ustring from) { ShaderData *sd = (ShaderData *)(sg->renderstate); - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if (from == u_ndc) { copy_matrix(result, kernel_data.cam.ndctoworld); @@ -366,7 +366,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, ustring to) { ShaderData *sd = (ShaderData *)(sg->renderstate); - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if (to == u_ndc) { copy_matrix(result, kernel_data.cam.worldtondc); @@ -745,7 +745,7 @@ static bool set_attribute_matrix(const Transform &tfm, TypeDesc type, void *val) return false; } -static bool get_primitive_attribute(const KernelGlobals *kg, +static bool get_primitive_attribute(const KernelGlobalsCPU *kg, const ShaderData *sd, const OSLGlobals::Attribute &attr, const TypeDesc &type, @@ -806,7 +806,7 @@ static bool get_primitive_attribute(const KernelGlobals *kg, } } -static bool get_mesh_attribute(const KernelGlobals *kg, +static bool get_mesh_attribute(const KernelGlobalsCPU *kg, const ShaderData *sd, const OSLGlobals::Attribute &attr, const TypeDesc &type, @@ -855,7 +855,7 @@ static bool get_object_attribute(const OSLGlobals::Attribute &attr, } } -bool OSLRenderServices::get_object_standard_attribute(const KernelGlobals *kg, +bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg, ShaderData *sd, ustring name, TypeDesc type, @@ -1000,7 +1000,7 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobals *kg, } } -bool OSLRenderServices::get_background_attribute(const KernelGlobals *kg, +bool OSLRenderServices::get_background_attribute(const KernelGlobalsCPU *kg, ShaderData *sd, ustring name, TypeDesc type, @@ -1091,7 +1091,7 @@ bool OSLRenderServices::get_attribute(OSL::ShaderGlobals *sg, bool OSLRenderServices::get_attribute( ShaderData *sd, bool derivatives, ustring object_name, TypeDesc type, ustring name, void *val) { - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; int prim_type = 0; int object; @@ -1220,7 +1220,7 @@ bool OSLRenderServices::texture(ustring filename, OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; OSLTextureHandle::Type texture_type = (handle) ? handle->type : OSLTextureHandle::OIIO; ShaderData *sd = (ShaderData *)(sg->renderstate); - const KernelGlobals *kernel_globals = sd->osl_globals; + KernelGlobals kernel_globals = sd->osl_globals; bool status = false; switch (texture_type) { @@ -1367,7 +1367,7 @@ bool OSLRenderServices::texture3d(ustring filename, case OSLTextureHandle::SVM: { /* Packed texture. */ ShaderData *sd = (ShaderData *)(sg->renderstate); - const KernelGlobals *kernel_globals = sd->osl_globals; + KernelGlobals kernel_globals = sd->osl_globals; int slot = handle->svm_slot; float3 P_float3 = make_float3(P.x, P.y, P.z); float4 rgba = kernel_tex_image_interp_3d(kernel_globals, slot, P_float3, INTERPOLATION_NONE); @@ -1389,7 +1389,7 @@ bool OSLRenderServices::texture3d(ustring filename, if (handle && handle->oiio_handle) { if (texture_thread_info == NULL) { ShaderData *sd = (ShaderData *)(sg->renderstate); - const KernelGlobals *kernel_globals = sd->osl_globals; + KernelGlobals kernel_globals = sd->osl_globals; OSLThreadData *tdata = kernel_globals->osl_tdata; texture_thread_info = tdata->oiio_thread_info; } @@ -1474,7 +1474,7 @@ bool OSLRenderServices::environment(ustring filename, if (handle && handle->oiio_handle) { if (thread_info == NULL) { ShaderData *sd = (ShaderData *)(sg->renderstate); - const KernelGlobals *kernel_globals = sd->osl_globals; + KernelGlobals kernel_globals = sd->osl_globals; OSLThreadData *tdata = kernel_globals->osl_tdata; thread_info = tdata->oiio_thread_info; } @@ -1629,7 +1629,7 @@ bool OSLRenderServices::trace(TraceOpt &options, tracedata->hit = false; tracedata->sd.osl_globals = sd->osl_globals; - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; /* Can't raytrace from shaders like displacement, before BVH exists. */ if (kernel_data.bvh.bvh_layout == BVH_LAYOUT_NONE) { @@ -1662,7 +1662,7 @@ bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg, } else { ShaderData *sd = &tracedata->sd; - const KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if (!tracedata->setup) { /* lazy shader data setup */ diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/osl_services.h index a9671485eda..d9f57c642ad 100644 --- a/intern/cycles/kernel/osl/osl_services.h +++ b/intern/cycles/kernel/osl/osl_services.h @@ -40,7 +40,7 @@ class Scene; class Shader; struct ShaderData; struct float3; -struct KernelGlobals; +struct KernelGlobalsCPU; /* OSL Texture Handle * @@ -250,13 +250,13 @@ class OSLRenderServices : public OSL::RendererServices { void *data) override; #endif - static bool get_background_attribute(const KernelGlobals *kg, + static bool get_background_attribute(const KernelGlobalsCPU *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val); - static bool get_object_standard_attribute(const KernelGlobals *kg, + static bool get_object_standard_attribute(const KernelGlobalsCPU *kg, ShaderData *sd, ustring name, TypeDesc type, diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp index 880ef635c76..a1df63ca8ff 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/osl_shader.cpp @@ -41,7 +41,7 @@ CCL_NAMESPACE_BEGIN /* Threads */ -void OSLShader::thread_init(KernelGlobals *kg, OSLGlobals *osl_globals) +void OSLShader::thread_init(KernelGlobalsCPU *kg, OSLGlobals *osl_globals) { /* no osl used? */ if (!osl_globals->use) { @@ -67,7 +67,7 @@ void OSLShader::thread_init(KernelGlobals *kg, OSLGlobals *osl_globals) kg->osl_tdata = tdata; } -void OSLShader::thread_free(KernelGlobals *kg) +void OSLShader::thread_free(KernelGlobalsCPU *kg) { if (!kg->osl) return; @@ -87,7 +87,7 @@ void OSLShader::thread_free(KernelGlobals *kg) /* Globals */ -static void shaderdata_to_shaderglobals(const KernelGlobals *kg, +static void shaderdata_to_shaderglobals(const KernelGlobalsCPU *kg, ShaderData *sd, const IntegratorStateCPU *state, int path_flag, @@ -174,7 +174,7 @@ static void flatten_surface_closure_tree(ShaderData *sd, } } -void OSLShader::eval_surface(const KernelGlobals *kg, +void OSLShader::eval_surface(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, int path_flag) @@ -282,7 +282,7 @@ static void flatten_background_closure_tree(ShaderData *sd, } } -void OSLShader::eval_background(const KernelGlobals *kg, +void OSLShader::eval_background(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, int path_flag) @@ -340,7 +340,7 @@ static void flatten_volume_closure_tree(ShaderData *sd, } } -void OSLShader::eval_volume(const KernelGlobals *kg, +void OSLShader::eval_volume(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, int path_flag) @@ -366,7 +366,7 @@ void OSLShader::eval_volume(const KernelGlobals *kg, /* Displacement */ -void OSLShader::eval_displacement(const KernelGlobals *kg, +void OSLShader::eval_displacement(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd) { @@ -391,7 +391,7 @@ void OSLShader::eval_displacement(const KernelGlobals *kg, /* Attributes */ -int OSLShader::find_attribute(const KernelGlobals *kg, +int OSLShader::find_attribute(const KernelGlobalsCPU *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc) diff --git a/intern/cycles/kernel/osl/osl_shader.h b/intern/cycles/kernel/osl/osl_shader.h index f1f17b141eb..686a1e1374a 100644 --- a/intern/cycles/kernel/osl/osl_shader.h +++ b/intern/cycles/kernel/osl/osl_shader.h @@ -39,7 +39,7 @@ struct ShaderClosure; struct ShaderData; struct IntegratorStateCPU; struct differential3; -struct KernelGlobals; +struct KernelGlobalsCPU; struct OSLGlobals; struct OSLShadingSystem; @@ -50,28 +50,28 @@ class OSLShader { static void register_closures(OSLShadingSystem *ss); /* per thread data */ - static void thread_init(KernelGlobals *kg, OSLGlobals *osl_globals); - static void thread_free(KernelGlobals *kg); + static void thread_init(KernelGlobalsCPU *kg, OSLGlobals *osl_globals); + static void thread_free(KernelGlobalsCPU *kg); /* eval */ - static void eval_surface(const KernelGlobals *kg, + static void eval_surface(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, int path_flag); - static void eval_background(const KernelGlobals *kg, + static void eval_background(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, int path_flag); - static void eval_volume(const KernelGlobals *kg, + static void eval_volume(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, int path_flag); - static void eval_displacement(const KernelGlobals *kg, + static void eval_displacement(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd); /* attributes */ - static int find_attribute(const KernelGlobals *kg, + static int find_attribute(const KernelGlobalsCPU *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc); diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 871e370123e..9692308c496 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -107,15 +107,14 @@ ccl_device_inline bool stack_valid(uint a) /* Reading Nodes */ -ccl_device_inline uint4 read_node(ccl_global const KernelGlobals *kg, ccl_private int *offset) +ccl_device_inline uint4 read_node(KernelGlobals kg, ccl_private int *offset) { uint4 node = kernel_tex_fetch(__svm_nodes, *offset); (*offset)++; return node; } -ccl_device_inline float4 read_node_float(ccl_global const KernelGlobals *kg, - ccl_private int *offset) +ccl_device_inline float4 read_node_float(KernelGlobals kg, ccl_private int *offset) { uint4 node = kernel_tex_fetch(__svm_nodes, *offset); float4 f = make_float4(__uint_as_float(node.x), @@ -126,7 +125,7 @@ ccl_device_inline float4 read_node_float(ccl_global const KernelGlobals *kg, return f; } -ccl_device_inline float4 fetch_node_float(ccl_global const KernelGlobals *kg, int offset) +ccl_device_inline float4 fetch_node_float(KernelGlobals kg, int offset) { uint4 node = kernel_tex_fetch(__svm_nodes, offset); return make_float4(__uint_as_float(node.x), @@ -227,7 +226,8 @@ CCL_NAMESPACE_BEGIN /* Main Interpreter Loop */ template -ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, +ccl_device void svm_eval_nodes(KernelGlobals kg, + ConstIntegratorState state, ShaderData *sd, ccl_global float *render_buffer, int path_flag) @@ -257,12 +257,14 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, kg, sd, stack, node, path_flag, offset); break; case NODE_CLOSURE_EMISSION: - if (KERNEL_NODES_FEATURE(EMISSION)) { + IF_KERNEL_NODES_FEATURE(EMISSION) + { svm_node_closure_emission(sd, stack, node); } break; case NODE_CLOSURE_BACKGROUND: - if (KERNEL_NODES_FEATURE(EMISSION)) { + IF_KERNEL_NODES_FEATURE(EMISSION) + { svm_node_closure_background(sd, stack, node); } break; @@ -273,7 +275,8 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, svm_node_closure_weight(sd, stack, node.y); break; case NODE_EMISSION_WEIGHT: - if (KERNEL_NODES_FEATURE(EMISSION)) { + IF_KERNEL_NODES_FEATURE(EMISSION) + { svm_node_emission_weight(kg, sd, stack, node); } break; @@ -310,27 +313,32 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, svm_node_vertex_color(kg, sd, stack, node.y, node.z, node.w); break; case NODE_GEOMETRY_BUMP_DX: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z); } break; case NODE_GEOMETRY_BUMP_DY: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z); } break; case NODE_SET_DISPLACEMENT: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { svm_node_set_displacement(kg, sd, stack, node.y); } break; case NODE_DISPLACEMENT: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { svm_node_displacement(kg, sd, stack, node); } break; case NODE_VECTOR_DISPLACEMENT: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { offset = svm_node_vector_displacement(kg, sd, stack, node, offset); } break; @@ -344,52 +352,62 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, offset = svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_SET_BUMP: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { svm_node_set_bump(kg, sd, stack, node); } break; case NODE_ATTR_BUMP_DX: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { svm_node_attr_bump_dx(kg, sd, stack, node); } break; case NODE_ATTR_BUMP_DY: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { svm_node_attr_bump_dy(kg, sd, stack, node); } break; case NODE_VERTEX_COLOR_BUMP_DX: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { svm_node_vertex_color_bump_dx(kg, sd, stack, node.y, node.z, node.w); } break; case NODE_VERTEX_COLOR_BUMP_DY: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { svm_node_vertex_color_bump_dy(kg, sd, stack, node.y, node.z, node.w); } break; case NODE_TEX_COORD_BUMP_DX: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { offset = svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, offset); } break; case NODE_TEX_COORD_BUMP_DY: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { offset = svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, offset); } break; case NODE_CLOSURE_SET_NORMAL: - if (KERNEL_NODES_FEATURE(BUMP)) { + IF_KERNEL_NODES_FEATURE(BUMP) + { svm_node_set_normal(kg, sd, stack, node.y, node.z); } break; case NODE_ENTER_BUMP_EVAL: - if (KERNEL_NODES_FEATURE(BUMP_STATE)) { + IF_KERNEL_NODES_FEATURE(BUMP_STATE) + { svm_node_enter_bump_eval(kg, sd, stack, node.y); } break; case NODE_LEAVE_BUMP_EVAL: - if (KERNEL_NODES_FEATURE(BUMP_STATE)) { + IF_KERNEL_NODES_FEATURE(BUMP_STATE) + { svm_node_leave_bump_eval(kg, sd, stack, node.y); } break; @@ -407,12 +425,14 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, svm_node_layer_weight(sd, stack, node); break; case NODE_CLOSURE_VOLUME: - if (KERNEL_NODES_FEATURE(VOLUME)) { + IF_KERNEL_NODES_FEATURE(VOLUME) + { svm_node_closure_volume(kg, sd, stack, node); } break; case NODE_PRINCIPLED_VOLUME: - if (KERNEL_NODES_FEATURE(VOLUME)) { + IF_KERNEL_NODES_FEATURE(VOLUME) + { offset = svm_node_principled_volume(kg, sd, stack, node, path_flag, offset); } break; @@ -432,7 +452,7 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, svm_node_brightness(sd, stack, node.y, node.z, node.w); break; case NODE_LIGHT_PATH: - svm_node_light_path(INTEGRATOR_STATE_PASS, sd, stack, node.y, node.z, path_flag); + svm_node_light_path(kg, state, sd, stack, node.y, node.z, path_flag); break; case NODE_OBJECT_INFO: svm_node_object_info(kg, sd, stack, node.y, node.z); @@ -442,7 +462,8 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, break; #if defined(__HAIR__) case NODE_HAIR_INFO: - if (KERNEL_NODES_FEATURE(HAIR)) { + IF_KERNEL_NODES_FEATURE(HAIR) + { svm_node_hair_info(kg, sd, stack, node.y, node.z); } break; @@ -554,15 +575,16 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, break; #ifdef __SHADER_RAYTRACE__ case NODE_BEVEL: - svm_node_bevel(INTEGRATOR_STATE_PASS, sd, stack, node); + svm_node_bevel(kg, state, sd, stack, node); break; case NODE_AMBIENT_OCCLUSION: - svm_node_ao(INTEGRATOR_STATE_PASS, sd, stack, node); + svm_node_ao(kg, state, sd, stack, node); break; #endif case NODE_TEX_VOXEL: - if (KERNEL_NODES_FEATURE(VOLUME)) { + IF_KERNEL_NODES_FEATURE(VOLUME) + { offset = svm_node_tex_voxel(kg, sd, stack, node, offset); } break; @@ -572,10 +594,10 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS, } break; case NODE_AOV_COLOR: - svm_node_aov_color(INTEGRATOR_STATE_PASS, sd, stack, node, render_buffer); + svm_node_aov_color(kg, state, sd, stack, node, render_buffer); break; case NODE_AOV_VALUE: - svm_node_aov_value(INTEGRATOR_STATE_PASS, sd, stack, node, render_buffer); + svm_node_aov_value(kg, state, sd, stack, node, render_buffer); break; default: kernel_assert(!"Unknown node type was passed to the SVM machine"); diff --git a/intern/cycles/kernel/svm/svm_ao.h b/intern/cycles/kernel/svm/svm_ao.h index 092f3817fd8..18d60c43b12 100644 --- a/intern/cycles/kernel/svm/svm_ao.h +++ b/intern/cycles/kernel/svm/svm_ao.h @@ -21,9 +21,11 @@ CCL_NAMESPACE_BEGIN #ifdef __SHADER_RAYTRACE__ # ifdef __KERNEL_OPTIX__ -extern "C" __device__ float __direct_callable__svm_node_ao(INTEGRATOR_STATE_CONST_ARGS, +extern "C" __device__ float __direct_callable__svm_node_ao(KernelGlobals kg, + ConstIntegratorState state, # else -ccl_device float svm_ao(INTEGRATOR_STATE_CONST_ARGS, +ccl_device float svm_ao(KernelGlobals kg, + ConstIntegratorState state, # endif ccl_private ShaderData *sd, float3 N, @@ -54,7 +56,7 @@ ccl_device float svm_ao(INTEGRATOR_STATE_CONST_ARGS, /* TODO: support ray-tracing in shadow shader evaluation? */ RNGState rng_state; - path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + path_state_rng_load(state, &rng_state); int unoccluded = 0; for (int sample = 0; sample < num_samples; sample++) { @@ -96,7 +98,8 @@ ccl_device_inline ccl_device_noinline # endif void - svm_node_ao(INTEGRATOR_STATE_CONST_ARGS, + svm_node_ao(KernelGlobals kg, + ConstIntegratorState state, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) @@ -112,11 +115,12 @@ ccl_device_noinline float ao = 1.0f; - if (KERNEL_NODES_FEATURE(RAYTRACE)) { + IF_KERNEL_NODES_FEATURE(RAYTRACE) + { # ifdef __KERNEL_OPTIX__ - ao = optixDirectCall(0, INTEGRATOR_STATE_PASS, sd, normal, dist, samples, flags); + ao = optixDirectCall(0, kg, state, sd, normal, dist, samples, flags); # else - ao = svm_ao(INTEGRATOR_STATE_PASS, sd, normal, dist, samples, flags); + ao = svm_ao(kg, state, sd, normal, dist, samples, flags); # endif } diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h index 640bec87ac9..d09eaa61cc0 100644 --- a/intern/cycles/kernel/svm/svm_aov.h +++ b/intern/cycles/kernel/svm/svm_aov.h @@ -25,7 +25,9 @@ ccl_device_inline bool svm_node_aov_check(const int path_flag, ccl_global float return ((render_buffer != NULL) && is_primary); } -ccl_device void svm_node_aov_color(INTEGRATOR_STATE_CONST_ARGS, +template +ccl_device void svm_node_aov_color(KernelGlobals kg, + ConstIntegratorState state, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, @@ -33,8 +35,9 @@ ccl_device void svm_node_aov_color(INTEGRATOR_STATE_CONST_ARGS, { float3 val = stack_load_float3(stack, node.y); - if (render_buffer && !INTEGRATOR_STATE_IS_NULL) { - const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); + IF_KERNEL_NODES_FEATURE(AOV) + { + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kernel_data.film.pass_stride; ccl_global float *buffer = render_buffer + render_buffer_offset + @@ -43,7 +46,9 @@ ccl_device void svm_node_aov_color(INTEGRATOR_STATE_CONST_ARGS, } } -ccl_device void svm_node_aov_value(INTEGRATOR_STATE_CONST_ARGS, +template +ccl_device void svm_node_aov_value(KernelGlobals kg, + ConstIntegratorState state, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, @@ -51,8 +56,9 @@ ccl_device void svm_node_aov_value(INTEGRATOR_STATE_CONST_ARGS, { float val = stack_load_float(stack, node.y); - if (render_buffer && !INTEGRATOR_STATE_IS_NULL) { - const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); + IF_KERNEL_NODES_FEATURE(AOV) + { + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kernel_data.film.pass_stride; ccl_global float *buffer = render_buffer + render_buffer_offset + diff --git a/intern/cycles/kernel/svm/svm_attribute.h b/intern/cycles/kernel/svm/svm_attribute.h index 9fd401ba1c3..b3c66d29f5c 100644 --- a/intern/cycles/kernel/svm/svm_attribute.h +++ b/intern/cycles/kernel/svm/svm_attribute.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Attribute Node */ -ccl_device AttributeDescriptor svm_node_attr_init(ccl_global const KernelGlobals *kg, +ccl_device AttributeDescriptor svm_node_attr_init(KernelGlobals kg, ccl_private ShaderData *sd, uint4 node, ccl_private NodeAttributeOutputType *type, @@ -48,7 +48,7 @@ ccl_device AttributeDescriptor svm_node_attr_init(ccl_global const KernelGlobals } template -ccl_device_noinline void svm_node_attr(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_attr(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) @@ -58,7 +58,8 @@ ccl_device_noinline void svm_node_attr(ccl_global const KernelGlobals *kg, AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type, &out_offset); #ifdef __VOLUME__ - if (KERNEL_NODES_FEATURE(VOLUME)) { + IF_KERNEL_NODES_FEATURE(VOLUME) + { /* Volumes * NOTE: moving this into its own node type might help improve performance. */ if (primitive_is_volume_attribute(sd, desc)) { @@ -148,7 +149,7 @@ ccl_device_noinline void svm_node_attr(ccl_global const KernelGlobals *kg, } } -ccl_device_noinline void svm_node_attr_bump_dx(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_attr_bump_dx(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) @@ -244,7 +245,7 @@ ccl_device_noinline void svm_node_attr_bump_dx(ccl_global const KernelGlobals *k } } -ccl_device_noinline void svm_node_attr_bump_dy(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_attr_bump_dy(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/svm_bevel.h index a76584e6bc8..197562434f9 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/svm_bevel.h @@ -99,9 +99,11 @@ ccl_device void svm_bevel_cubic_sample(const float radius, */ # ifdef __KERNEL_OPTIX__ -extern "C" __device__ float3 __direct_callable__svm_node_bevel(INTEGRATOR_STATE_CONST_ARGS, +extern "C" __device__ float3 __direct_callable__svm_node_bevel(KernelGlobals kg, + ConstIntegratorState state, # else -ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, +ccl_device float3 svm_bevel(KernelGlobals kg, + ConstIntegratorState state, # endif ccl_private ShaderData *sd, float radius, @@ -118,15 +120,15 @@ ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, } /* Don't bevel for blurry indirect rays. */ - if (INTEGRATOR_STATE(path, min_ray_pdf) < 8.0f) { + if (INTEGRATOR_STATE(state, path, min_ray_pdf) < 8.0f) { return sd->N; } /* Setup for multi intersection. */ LocalIntersection isect; - uint lcg_state = lcg_state_init(INTEGRATOR_STATE(path, rng_hash), - INTEGRATOR_STATE(path, rng_offset), - INTEGRATOR_STATE(path, sample), + uint lcg_state = lcg_state_init(INTEGRATOR_STATE(state, path, rng_hash), + INTEGRATOR_STATE(state, path, rng_offset), + INTEGRATOR_STATE(state, path, sample), 0x64c6a40e); /* Sample normals from surrounding points on surface. */ @@ -134,7 +136,7 @@ ccl_device float3 svm_bevel(INTEGRATOR_STATE_CONST_ARGS, /* TODO: support ray-tracing in shadow shader evaluation? */ RNGState rng_state; - path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); + path_state_rng_load(state, &rng_state); for (int sample = 0; sample < num_samples; sample++) { float disk_u, disk_v; @@ -287,7 +289,8 @@ ccl_device_inline ccl_device_noinline # endif void - svm_node_bevel(INTEGRATOR_STATE_CONST_ARGS, + svm_node_bevel(KernelGlobals kg, + ConstIntegratorState state, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) @@ -299,11 +302,12 @@ ccl_device_noinline float3 bevel_N = sd->N; - if (KERNEL_NODES_FEATURE(RAYTRACE)) { + IF_KERNEL_NODES_FEATURE(RAYTRACE) + { # ifdef __KERNEL_OPTIX__ - bevel_N = optixDirectCall(1, INTEGRATOR_STATE_PASS, sd, radius, num_samples); + bevel_N = optixDirectCall(1, kg, state, sd, radius, num_samples); # else - bevel_N = svm_bevel(INTEGRATOR_STATE_PASS, sd, radius, num_samples); + bevel_N = svm_bevel(kg, state, sd, radius, num_samples); # endif if (stack_valid(normal_offset)) { diff --git a/intern/cycles/kernel/svm/svm_blackbody.h b/intern/cycles/kernel/svm/svm_blackbody.h index 521afb42adc..f1adb0e76af 100644 --- a/intern/cycles/kernel/svm/svm_blackbody.h +++ b/intern/cycles/kernel/svm/svm_blackbody.h @@ -34,7 +34,7 @@ CCL_NAMESPACE_BEGIN /* Blackbody Node */ -ccl_device_noinline void svm_node_blackbody(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_blackbody(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint temperature_offset, diff --git a/intern/cycles/kernel/svm/svm_brick.h b/intern/cycles/kernel/svm/svm_brick.h index 29a8350f1c1..9dc31ef37ec 100644 --- a/intern/cycles/kernel/svm/svm_brick.h +++ b/intern/cycles/kernel/svm/svm_brick.h @@ -72,11 +72,8 @@ ccl_device_noinline_cpu float2 svm_brick(float3 p, return make_float2(tint, mortar); } -ccl_device_noinline int svm_node_tex_brick(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd, - ccl_private float *stack, - uint4 node, - int offset) +ccl_device_noinline int svm_node_tex_brick( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint4 node2 = read_node(kg, &offset); uint4 node3 = read_node(kg, &offset); diff --git a/intern/cycles/kernel/svm/svm_bump.h b/intern/cycles/kernel/svm/svm_bump.h index 70935c730f4..66e5b665532 100644 --- a/intern/cycles/kernel/svm/svm_bump.h +++ b/intern/cycles/kernel/svm/svm_bump.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Bump Eval Nodes */ -ccl_device_noinline void svm_node_enter_bump_eval(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_enter_bump_eval(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint offset) @@ -45,7 +45,7 @@ ccl_device_noinline void svm_node_enter_bump_eval(ccl_global const KernelGlobals } } -ccl_device_noinline void svm_node_leave_bump_eval(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_leave_bump_eval(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint offset) diff --git a/intern/cycles/kernel/svm/svm_camera.h b/intern/cycles/kernel/svm/svm_camera.h index 2b786757af8..787f11f38b5 100644 --- a/intern/cycles/kernel/svm/svm_camera.h +++ b/intern/cycles/kernel/svm/svm_camera.h @@ -16,7 +16,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_camera(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_camera(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint out_vector, diff --git a/intern/cycles/kernel/svm/svm_checker.h b/intern/cycles/kernel/svm/svm_checker.h index e22367f4f59..9251d90c0e1 100644 --- a/intern/cycles/kernel/svm/svm_checker.h +++ b/intern/cycles/kernel/svm/svm_checker.h @@ -32,7 +32,7 @@ ccl_device float svm_checker(float3 p) return ((xi % 2 == yi % 2) == (zi % 2)) ? 1.0f : 0.0f; } -ccl_device_noinline void svm_node_tex_checker(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_tex_checker(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) diff --git a/intern/cycles/kernel/svm/svm_clamp.h b/intern/cycles/kernel/svm/svm_clamp.h index cb5224aebb2..5b5ea784f4a 100644 --- a/intern/cycles/kernel/svm/svm_clamp.h +++ b/intern/cycles/kernel/svm/svm_clamp.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Clamp Node */ -ccl_device_noinline int svm_node_clamp(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_clamp(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint value_stack_offset, diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h index 87be73bb2cc..fb10288da72 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/svm_closure.h @@ -61,8 +61,21 @@ ccl_device void svm_node_glass_setup(ccl_private ShaderData *sd, } } +ccl_device_inline int svm_node_closure_bsdf_skip(KernelGlobals kg, int offset, uint type) +{ + if (type == CLOSURE_BSDF_PRINCIPLED_ID) { + /* Read all principled BSDF extra data to get the right offset. */ + read_node(kg, &offset); + read_node(kg, &offset); + read_node(kg, &offset); + read_node(kg, &offset); + } + + return offset; +} + template -ccl_device_noinline int svm_node_closure_bsdf(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, @@ -80,16 +93,15 @@ ccl_device_noinline int svm_node_closure_bsdf(ccl_global const KernelGlobals *kg uint4 data_node = read_node(kg, &offset); /* Only compute BSDF for surfaces, transparent variable is shared with volume extinction. */ - if ((!KERNEL_NODES_FEATURE(BSDF) || shader_type != SHADER_TYPE_SURFACE) || mix_weight == 0.0f) { - if (type == CLOSURE_BSDF_PRINCIPLED_ID) { - /* Read all principled BSDF extra data to get the right offset. */ - read_node(kg, &offset); - read_node(kg, &offset); - read_node(kg, &offset); - read_node(kg, &offset); + IF_KERNEL_NODES_FEATURE(BSDF) + { + if ((shader_type != SHADER_TYPE_SURFACE) || mix_weight == 0.0f) { + return svm_node_closure_bsdf_skip(kg, offset, type); } - - return offset; + } + else + { + return svm_node_closure_bsdf_skip(kg, offset, type); } float3 N = stack_valid(data_node.x) ? stack_load_float3(stack, data_node.x) : sd->N; @@ -944,7 +956,7 @@ ccl_device_noinline int svm_node_closure_bsdf(ccl_global const KernelGlobals *kg } template -ccl_device_noinline void svm_node_closure_volume(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_closure_volume(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) @@ -999,7 +1011,7 @@ ccl_device_noinline void svm_node_closure_volume(ccl_global const KernelGlobals } template -ccl_device_noinline int svm_node_principled_volume(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, @@ -1194,7 +1206,7 @@ ccl_device void svm_node_closure_weight(ccl_private ShaderData *sd, svm_node_closure_store_weight(sd, weight); } -ccl_device_noinline void svm_node_emission_weight(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_emission_weight(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) @@ -1232,7 +1244,7 @@ ccl_device_noinline void svm_node_mix_closure(ccl_private ShaderData *sd, /* (Bump) normal */ -ccl_device void svm_node_set_normal(ccl_global const KernelGlobals *kg, +ccl_device void svm_node_set_normal(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint in_direction, diff --git a/intern/cycles/kernel/svm/svm_convert.h b/intern/cycles/kernel/svm/svm_convert.h index 0d53779a5c8..ec5745dc78a 100644 --- a/intern/cycles/kernel/svm/svm_convert.h +++ b/intern/cycles/kernel/svm/svm_convert.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Conversion Nodes */ -ccl_device_noinline void svm_node_convert(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_convert(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint type, diff --git a/intern/cycles/kernel/svm/svm_displace.h b/intern/cycles/kernel/svm/svm_displace.h index 7a3c8a6d36d..f2446c3b3ef 100644 --- a/intern/cycles/kernel/svm/svm_displace.h +++ b/intern/cycles/kernel/svm/svm_displace.h @@ -20,7 +20,7 @@ CCL_NAMESPACE_BEGIN /* Bump Node */ -ccl_device_noinline void svm_node_set_bump(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_set_bump(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) @@ -88,7 +88,7 @@ ccl_device_noinline void svm_node_set_bump(ccl_global const KernelGlobals *kg, /* Displacement Node */ -ccl_device void svm_node_set_displacement(ccl_global const KernelGlobals *kg, +ccl_device void svm_node_set_displacement(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint fac_offset) @@ -97,7 +97,7 @@ ccl_device void svm_node_set_displacement(ccl_global const KernelGlobals *kg, sd->P += dP; } -ccl_device_noinline void svm_node_displacement(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_displacement(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) @@ -127,11 +127,8 @@ ccl_device_noinline void svm_node_displacement(ccl_global const KernelGlobals *k stack_store_float3(stack, node.z, dP); } -ccl_device_noinline int svm_node_vector_displacement(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd, - ccl_private float *stack, - uint4 node, - int offset) +ccl_device_noinline int svm_node_vector_displacement( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint4 data_node = read_node(kg, &offset); uint space = data_node.x; diff --git a/intern/cycles/kernel/svm/svm_geometry.h b/intern/cycles/kernel/svm/svm_geometry.h index a94464d3a52..b29bfdbed07 100644 --- a/intern/cycles/kernel/svm/svm_geometry.h +++ b/intern/cycles/kernel/svm/svm_geometry.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Geometry Node */ -ccl_device_noinline void svm_node_geometry(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_geometry(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint type, @@ -54,7 +54,7 @@ ccl_device_noinline void svm_node_geometry(ccl_global const KernelGlobals *kg, stack_store_float3(stack, out_offset, data); } -ccl_device_noinline void svm_node_geometry_bump_dx(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_geometry_bump_dx(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint type, @@ -81,7 +81,7 @@ ccl_device_noinline void svm_node_geometry_bump_dx(ccl_global const KernelGlobal #endif } -ccl_device_noinline void svm_node_geometry_bump_dy(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_geometry_bump_dy(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint type, @@ -110,7 +110,7 @@ ccl_device_noinline void svm_node_geometry_bump_dy(ccl_global const KernelGlobal /* Object Info */ -ccl_device_noinline void svm_node_object_info(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_object_info(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint type, @@ -152,7 +152,7 @@ ccl_device_noinline void svm_node_object_info(ccl_global const KernelGlobals *kg /* Particle Info */ -ccl_device_noinline void svm_node_particle_info(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_particle_info(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint type, @@ -214,7 +214,7 @@ ccl_device_noinline void svm_node_particle_info(ccl_global const KernelGlobals * /* Hair Info */ -ccl_device_noinline void svm_node_hair_info(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_hair_info(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint type, diff --git a/intern/cycles/kernel/svm/svm_hsv.h b/intern/cycles/kernel/svm/svm_hsv.h index feb85eda122..978c4c2d781 100644 --- a/intern/cycles/kernel/svm/svm_hsv.h +++ b/intern/cycles/kernel/svm/svm_hsv.h @@ -19,7 +19,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_hsv(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_hsv(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) diff --git a/intern/cycles/kernel/svm/svm_ies.h b/intern/cycles/kernel/svm/svm_ies.h index 7d41205c9ef..0215670d062 100644 --- a/intern/cycles/kernel/svm/svm_ies.h +++ b/intern/cycles/kernel/svm/svm_ies.h @@ -19,7 +19,7 @@ CCL_NAMESPACE_BEGIN /* IES Light */ ccl_device_inline float interpolate_ies_vertical( - ccl_global const KernelGlobals *kg, int ofs, int v, int v_num, float v_frac, int h) + KernelGlobals kg, int ofs, int v, int v_num, float v_frac, int h) { /* Since lookups are performed in spherical coordinates, clamping the coordinates at the low end * of v (corresponding to the north pole) would result in artifacts. The proper way of dealing @@ -39,10 +39,7 @@ ccl_device_inline float interpolate_ies_vertical( return cubic_interp(a, b, c, d, v_frac); } -ccl_device_inline float kernel_ies_interp(ccl_global const KernelGlobals *kg, - int slot, - float h_angle, - float v_angle) +ccl_device_inline float kernel_ies_interp(KernelGlobals kg, int slot, float h_angle, float v_angle) { /* Find offset of the IES data in the table. */ int ofs = __float_as_int(kernel_tex_fetch(__ies, slot)); @@ -98,7 +95,7 @@ ccl_device_inline float kernel_ies_interp(ccl_global const KernelGlobals *kg, return max(cubic_interp(a, b, c, d, h_frac), 0.0f); } -ccl_device_noinline void svm_node_ies(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_ies(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index 2de80d5fc29..68374fcfb0d 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -16,8 +16,7 @@ CCL_NAMESPACE_BEGIN -ccl_device float4 -svm_image_texture(ccl_global const KernelGlobals *kg, int id, float x, float y, uint flags) +ccl_device float4 svm_image_texture(KernelGlobals kg, int id, float x, float y, uint flags) { if (id == -1) { return make_float4( @@ -45,11 +44,8 @@ ccl_device_inline float3 texco_remap_square(float3 co) return (co - make_float3(0.5f, 0.5f, 0.5f)) * 2.0f; } -ccl_device_noinline int svm_node_tex_image(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd, - ccl_private float *stack, - uint4 node, - int offset) +ccl_device_noinline int svm_node_tex_image( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint co_offset, out_offset, alpha_offset, flags; @@ -121,7 +117,7 @@ ccl_device_noinline int svm_node_tex_image(ccl_global const KernelGlobals *kg, return offset; } -ccl_device_noinline void svm_node_tex_image_box(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_tex_image_box(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) @@ -223,7 +219,7 @@ ccl_device_noinline void svm_node_tex_image_box(ccl_global const KernelGlobals * stack_store_float(stack, alpha_offset, f.w); } -ccl_device_noinline void svm_node_tex_environment(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_tex_environment(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) diff --git a/intern/cycles/kernel/svm/svm_light_path.h b/intern/cycles/kernel/svm/svm_light_path.h index aaff8376c7c..955a1f23379 100644 --- a/intern/cycles/kernel/svm/svm_light_path.h +++ b/intern/cycles/kernel/svm/svm_light_path.h @@ -18,7 +18,9 @@ CCL_NAMESPACE_BEGIN /* Light Path Node */ -ccl_device_noinline void svm_node_light_path(INTEGRATOR_STATE_CONST_ARGS, +template +ccl_device_noinline void svm_node_light_path(KernelGlobals kg, + ConstIntegratorState state, ccl_private const ShaderData *sd, ccl_private float *stack, uint type, @@ -62,9 +64,12 @@ ccl_device_noinline void svm_node_light_path(INTEGRATOR_STATE_CONST_ARGS, /* Read bounce from difference location depending if this is a shadow * path. It's a bit dubious to have integrate state details leak into * this function but hard to avoid currently. */ - int bounce = (INTEGRATOR_STATE_IS_NULL) ? 0 : - (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(shadow_path, bounce) : - INTEGRATOR_STATE(path, bounce); + int bounce = 0; + IF_KERNEL_NODES_FEATURE(LIGHT_PATH) + { + bounce = (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(state, shadow_path, bounce) : + INTEGRATOR_STATE(state, path, bounce); + } /* For background, light emission and shadow evaluation we from a * surface or volume we are effective one bounce further. */ @@ -77,11 +82,13 @@ ccl_device_noinline void svm_node_light_path(INTEGRATOR_STATE_CONST_ARGS, } /* TODO */ case NODE_LP_ray_transparent: { - const int bounce = (INTEGRATOR_STATE_IS_NULL) ? - 0 : - (path_flag & PATH_RAY_SHADOW) ? - INTEGRATOR_STATE(shadow_path, transparent_bounce) : - INTEGRATOR_STATE(path, transparent_bounce); + int bounce = 0; + IF_KERNEL_NODES_FEATURE(LIGHT_PATH) + { + bounce = (path_flag & PATH_RAY_SHADOW) ? + INTEGRATOR_STATE(state, shadow_path, transparent_bounce) : + INTEGRATOR_STATE(state, path, transparent_bounce); + } info = (float)bounce; break; diff --git a/intern/cycles/kernel/svm/svm_magic.h b/intern/cycles/kernel/svm/svm_magic.h index 4c4f3bcf523..d3a429fec56 100644 --- a/intern/cycles/kernel/svm/svm_magic.h +++ b/intern/cycles/kernel/svm/svm_magic.h @@ -87,11 +87,8 @@ ccl_device_noinline_cpu float3 svm_magic(float3 p, int n, float distortion) return make_float3(0.5f - x, 0.5f - y, 0.5f - z); } -ccl_device_noinline int svm_node_tex_magic(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd, - ccl_private float *stack, - uint4 node, - int offset) +ccl_device_noinline int svm_node_tex_magic( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint depth; uint scale_offset, distortion_offset, co_offset, fac_offset, color_offset; diff --git a/intern/cycles/kernel/svm/svm_map_range.h b/intern/cycles/kernel/svm/svm_map_range.h index f4f7d3ca76f..5e89947c6c7 100644 --- a/intern/cycles/kernel/svm/svm_map_range.h +++ b/intern/cycles/kernel/svm/svm_map_range.h @@ -24,7 +24,7 @@ ccl_device_inline float smootherstep(float edge0, float edge1, float x) return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); } -ccl_device_noinline int svm_node_map_range(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_map_range(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint value_stack_offset, diff --git a/intern/cycles/kernel/svm/svm_mapping.h b/intern/cycles/kernel/svm/svm_mapping.h index 8102afc637e..ed420e5bc3d 100644 --- a/intern/cycles/kernel/svm/svm_mapping.h +++ b/intern/cycles/kernel/svm/svm_mapping.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Mapping Node */ -ccl_device_noinline void svm_node_mapping(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_mapping(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint type, @@ -43,7 +43,7 @@ ccl_device_noinline void svm_node_mapping(ccl_global const KernelGlobals *kg, /* Texture Mapping */ -ccl_device_noinline int svm_node_texture_mapping(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_texture_mapping(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint vec_offset, @@ -62,7 +62,7 @@ ccl_device_noinline int svm_node_texture_mapping(ccl_global const KernelGlobals return offset; } -ccl_device_noinline int svm_node_min_max(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_min_max(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint vec_offset, diff --git a/intern/cycles/kernel/svm/svm_math.h b/intern/cycles/kernel/svm/svm_math.h index 3897a453873..97f7d486c09 100644 --- a/intern/cycles/kernel/svm/svm_math.h +++ b/intern/cycles/kernel/svm/svm_math.h @@ -16,7 +16,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_math(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_math(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint type, @@ -34,7 +34,7 @@ ccl_device_noinline void svm_node_math(ccl_global const KernelGlobals *kg, stack_store_float(stack, result_stack_offset, result); } -ccl_device_noinline int svm_node_vector_math(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_vector_math(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint type, diff --git a/intern/cycles/kernel/svm/svm_mix.h b/intern/cycles/kernel/svm/svm_mix.h index 0064c5e643c..568dda3dddc 100644 --- a/intern/cycles/kernel/svm/svm_mix.h +++ b/intern/cycles/kernel/svm/svm_mix.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Node */ -ccl_device_noinline int svm_node_mix(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_mix(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint fac_offset, diff --git a/intern/cycles/kernel/svm/svm_musgrave.h b/intern/cycles/kernel/svm/svm_musgrave.h index 8523f45b95f..decd29bbe13 100644 --- a/intern/cycles/kernel/svm/svm_musgrave.h +++ b/intern/cycles/kernel/svm/svm_musgrave.h @@ -700,7 +700,7 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_4d( return value; } -ccl_device_noinline int svm_node_tex_musgrave(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_tex_musgrave(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint offsets1, diff --git a/intern/cycles/kernel/svm/svm_noisetex.h b/intern/cycles/kernel/svm/svm_noisetex.h index 61da8227efa..3fe33f72b59 100644 --- a/intern/cycles/kernel/svm/svm_noisetex.h +++ b/intern/cycles/kernel/svm/svm_noisetex.h @@ -140,7 +140,7 @@ ccl_device void noise_texture_4d(float4 co, } } -ccl_device_noinline int svm_node_tex_noise(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_tex_noise(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint dimensions, diff --git a/intern/cycles/kernel/svm/svm_normal.h b/intern/cycles/kernel/svm/svm_normal.h index 0d1b4200d54..9bf64ed8823 100644 --- a/intern/cycles/kernel/svm/svm_normal.h +++ b/intern/cycles/kernel/svm/svm_normal.h @@ -16,7 +16,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline int svm_node_normal(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_normal(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint in_normal_offset, diff --git a/intern/cycles/kernel/svm/svm_ramp.h b/intern/cycles/kernel/svm/svm_ramp.h index ef8b0d103c1..d2dddf4c6eb 100644 --- a/intern/cycles/kernel/svm/svm_ramp.h +++ b/intern/cycles/kernel/svm/svm_ramp.h @@ -21,18 +21,14 @@ CCL_NAMESPACE_BEGIN /* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */ -ccl_device_inline float fetch_float(ccl_global const KernelGlobals *kg, int offset) +ccl_device_inline float fetch_float(KernelGlobals kg, int offset) { uint4 node = kernel_tex_fetch(__svm_nodes, offset); return __uint_as_float(node.x); } -ccl_device_inline float float_ramp_lookup(ccl_global const KernelGlobals *kg, - int offset, - float f, - bool interpolate, - bool extrapolate, - int table_size) +ccl_device_inline float float_ramp_lookup( + KernelGlobals kg, int offset, float f, bool interpolate, bool extrapolate, int table_size) { if ((f < 0.0f || f > 1.0f) && extrapolate) { float t0, dy; @@ -63,12 +59,8 @@ ccl_device_inline float float_ramp_lookup(ccl_global const KernelGlobals *kg, return a; } -ccl_device_inline float4 rgb_ramp_lookup(ccl_global const KernelGlobals *kg, - int offset, - float f, - bool interpolate, - bool extrapolate, - int table_size) +ccl_device_inline float4 rgb_ramp_lookup( + KernelGlobals kg, int offset, float f, bool interpolate, bool extrapolate, int table_size) { if ((f < 0.0f || f > 1.0f) && extrapolate) { float4 t0, dy; @@ -99,11 +91,8 @@ ccl_device_inline float4 rgb_ramp_lookup(ccl_global const KernelGlobals *kg, return a; } -ccl_device_noinline int svm_node_rgb_ramp(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd, - ccl_private float *stack, - uint4 node, - int offset) +ccl_device_noinline int svm_node_rgb_ramp( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint fac_offset, color_offset, alpha_offset; uint interpolate = node.z; @@ -124,11 +113,8 @@ ccl_device_noinline int svm_node_rgb_ramp(ccl_global const KernelGlobals *kg, return offset; } -ccl_device_noinline int svm_node_curves(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd, - ccl_private float *stack, - uint4 node, - int offset) +ccl_device_noinline int svm_node_curves( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint fac_offset, color_offset, out_offset; svm_unpack_node_uchar3(node.y, &fac_offset, &color_offset, &out_offset); @@ -153,11 +139,8 @@ ccl_device_noinline int svm_node_curves(ccl_global const KernelGlobals *kg, return offset; } -ccl_device_noinline int svm_node_curve(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd, - ccl_private float *stack, - uint4 node, - int offset) +ccl_device_noinline int svm_node_curve( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint fac_offset, value_in_offset, out_offset; svm_unpack_node_uchar3(node.y, &fac_offset, &value_in_offset, &out_offset); diff --git a/intern/cycles/kernel/svm/svm_sepcomb_hsv.h b/intern/cycles/kernel/svm/svm_sepcomb_hsv.h index 3cd4ba87a55..bafa0456342 100644 --- a/intern/cycles/kernel/svm/svm_sepcomb_hsv.h +++ b/intern/cycles/kernel/svm/svm_sepcomb_hsv.h @@ -16,7 +16,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline int svm_node_combine_hsv(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_combine_hsv(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint hue_in, @@ -39,7 +39,7 @@ ccl_device_noinline int svm_node_combine_hsv(ccl_global const KernelGlobals *kg, return offset; } -ccl_device_noinline int svm_node_separate_hsv(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_separate_hsv(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint color_in, diff --git a/intern/cycles/kernel/svm/svm_sky.h b/intern/cycles/kernel/svm/svm_sky.h index 04db8109170..3ab7bc89c66 100644 --- a/intern/cycles/kernel/svm/svm_sky.h +++ b/intern/cycles/kernel/svm/svm_sky.h @@ -37,7 +37,7 @@ ccl_device float sky_perez_function(ccl_private float *lam, float theta, float g (1.0f + lam[2] * expf(lam[3] * gamma) + lam[4] * cgamma * cgamma); } -ccl_device float3 sky_radiance_preetham(ccl_global const KernelGlobals *kg, +ccl_device float3 sky_radiance_preetham(KernelGlobals kg, float3 dir, float sunphi, float suntheta, @@ -90,7 +90,7 @@ ccl_device float sky_radiance_internal(ccl_private float *configuration, float t configuration[6] * mieM + configuration[7] * zenith); } -ccl_device float3 sky_radiance_hosek(ccl_global const KernelGlobals *kg, +ccl_device float3 sky_radiance_hosek(KernelGlobals kg, float3 dir, float sunphi, float suntheta, @@ -127,7 +127,7 @@ ccl_device float3 geographical_to_direction(float lat, float lon) return make_float3(cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat)); } -ccl_device float3 sky_radiance_nishita(ccl_global const KernelGlobals *kg, +ccl_device float3 sky_radiance_nishita(KernelGlobals kg, float3 dir, ccl_private float *nishita_data, uint texture_id) @@ -209,11 +209,8 @@ ccl_device float3 sky_radiance_nishita(ccl_global const KernelGlobals *kg, return xyz_to_rgb(kg, xyz); } -ccl_device_noinline int svm_node_tex_sky(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd, - ccl_private float *stack, - uint4 node, - int offset) +ccl_device_noinline int svm_node_tex_sky( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { /* Load data */ uint dir_offset = node.y; diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index 295d5e9f65b..657a4bb32a8 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -22,7 +22,7 @@ CCL_NAMESPACE_BEGIN /* Texture Coordinate Node */ -ccl_device_noinline int svm_node_tex_coord(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_tex_coord(KernelGlobals kg, ccl_private ShaderData *sd, int path_flag, ccl_private float *stack, @@ -103,7 +103,7 @@ ccl_device_noinline int svm_node_tex_coord(ccl_global const KernelGlobals *kg, return offset; } -ccl_device_noinline int svm_node_tex_coord_bump_dx(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg, ccl_private ShaderData *sd, int path_flag, ccl_private float *stack, @@ -188,7 +188,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(ccl_global const KernelGlobal #endif } -ccl_device_noinline int svm_node_tex_coord_bump_dy(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg, ccl_private ShaderData *sd, int path_flag, ccl_private float *stack, @@ -273,7 +273,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(ccl_global const KernelGlobal #endif } -ccl_device_noinline void svm_node_normal_map(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_normal_map(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) @@ -366,7 +366,7 @@ ccl_device_noinline void svm_node_normal_map(ccl_global const KernelGlobals *kg, stack_store_float3(stack, normal_offset, N); } -ccl_device_noinline void svm_node_tangent(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_tangent(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) diff --git a/intern/cycles/kernel/svm/svm_value.h b/intern/cycles/kernel/svm/svm_value.h index d1038bc072d..cc72961d0f6 100644 --- a/intern/cycles/kernel/svm/svm_value.h +++ b/intern/cycles/kernel/svm/svm_value.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Value Nodes */ -ccl_device void svm_node_value_f(ccl_global const KernelGlobals *kg, +ccl_device void svm_node_value_f(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint ivalue, @@ -27,7 +27,7 @@ ccl_device void svm_node_value_f(ccl_global const KernelGlobals *kg, stack_store_float(stack, out_offset, __uint_as_float(ivalue)); } -ccl_device int svm_node_value_v(ccl_global const KernelGlobals *kg, +ccl_device int svm_node_value_v(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint out_offset, diff --git a/intern/cycles/kernel/svm/svm_vector_transform.h b/intern/cycles/kernel/svm/svm_vector_transform.h index b6c898c3952..4e0d36647da 100644 --- a/intern/cycles/kernel/svm/svm_vector_transform.h +++ b/intern/cycles/kernel/svm/svm_vector_transform.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Vector Transform */ -ccl_device_noinline void svm_node_vector_transform(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_vector_transform(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) diff --git a/intern/cycles/kernel/svm/svm_vertex_color.h b/intern/cycles/kernel/svm/svm_vertex_color.h index 3641f05ca43..a5fa15ee085 100644 --- a/intern/cycles/kernel/svm/svm_vertex_color.h +++ b/intern/cycles/kernel/svm/svm_vertex_color.h @@ -16,7 +16,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_vertex_color(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_vertex_color(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint layer_id, @@ -35,7 +35,7 @@ ccl_device_noinline void svm_node_vertex_color(ccl_global const KernelGlobals *k } } -ccl_device_noinline void svm_node_vertex_color_bump_dx(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_vertex_color_bump_dx(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint layer_id, @@ -56,7 +56,7 @@ ccl_device_noinline void svm_node_vertex_color_bump_dx(ccl_global const KernelGl } } -ccl_device_noinline void svm_node_vertex_color_bump_dy(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_vertex_color_bump_dy(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint layer_id, diff --git a/intern/cycles/kernel/svm/svm_voronoi.h b/intern/cycles/kernel/svm/svm_voronoi.h index 062a8bde415..b8067520770 100644 --- a/intern/cycles/kernel/svm/svm_voronoi.h +++ b/intern/cycles/kernel/svm/svm_voronoi.h @@ -917,7 +917,7 @@ ccl_device void voronoi_n_sphere_radius_4d(float4 coord, } template -ccl_device_noinline int svm_node_tex_voronoi(ccl_global const KernelGlobals *kg, +ccl_device_noinline int svm_node_tex_voronoi(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint dimensions, @@ -1013,7 +1013,8 @@ ccl_device_noinline int svm_node_tex_voronoi(ccl_global const KernelGlobals *kg, &position_out_2d); break; case NODE_VORONOI_SMOOTH_F1: - if (KERNEL_NODES_FEATURE(VORONOI_EXTRA)) { + IF_KERNEL_NODES_FEATURE(VORONOI_EXTRA) + { voronoi_smooth_f1_2d(coord_2d, smoothness, exponent, @@ -1058,7 +1059,8 @@ ccl_device_noinline int svm_node_tex_voronoi(ccl_global const KernelGlobals *kg, &position_out); break; case NODE_VORONOI_SMOOTH_F1: - if (KERNEL_NODES_FEATURE(VORONOI_EXTRA)) { + IF_KERNEL_NODES_FEATURE(VORONOI_EXTRA) + { voronoi_smooth_f1_3d(coord, smoothness, exponent, @@ -1092,7 +1094,8 @@ ccl_device_noinline int svm_node_tex_voronoi(ccl_global const KernelGlobals *kg, } case 4: { - if (KERNEL_NODES_FEATURE(VORONOI_EXTRA)) { + IF_KERNEL_NODES_FEATURE(VORONOI_EXTRA) + { float4 coord_4d = make_float4(coord.x, coord.y, coord.z, w); float4 position_out_4d; switch (voronoi_feature) { diff --git a/intern/cycles/kernel/svm/svm_voxel.h b/intern/cycles/kernel/svm/svm_voxel.h index 764fb71ba72..be4bb315145 100644 --- a/intern/cycles/kernel/svm/svm_voxel.h +++ b/intern/cycles/kernel/svm/svm_voxel.h @@ -19,11 +19,8 @@ CCL_NAMESPACE_BEGIN /* TODO(sergey): Think of making it more generic volume-type attribute * sampler. */ -ccl_device_noinline int svm_node_tex_voxel(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd, - ccl_private float *stack, - uint4 node, - int offset) +ccl_device_noinline int svm_node_tex_voxel( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint co_offset, density_out_offset, color_out_offset, space; svm_unpack_node_uchar4(node.z, &co_offset, &density_out_offset, &color_out_offset, &space); diff --git a/intern/cycles/kernel/svm/svm_wave.h b/intern/cycles/kernel/svm/svm_wave.h index 1ac130e2006..d04b7aa3476 100644 --- a/intern/cycles/kernel/svm/svm_wave.h +++ b/intern/cycles/kernel/svm/svm_wave.h @@ -82,11 +82,8 @@ ccl_device_noinline_cpu float svm_wave(NodeWaveType type, } } -ccl_device_noinline int svm_node_tex_wave(ccl_global const KernelGlobals *kg, - ccl_private ShaderData *sd, - ccl_private float *stack, - uint4 node, - int offset) +ccl_device_noinline int svm_node_tex_wave( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint4 node2 = read_node(kg, &offset); uint4 node3 = read_node(kg, &offset); diff --git a/intern/cycles/kernel/svm/svm_wavelength.h b/intern/cycles/kernel/svm/svm_wavelength.h index e891744f276..4ef041f68d5 100644 --- a/intern/cycles/kernel/svm/svm_wavelength.h +++ b/intern/cycles/kernel/svm/svm_wavelength.h @@ -34,7 +34,7 @@ CCL_NAMESPACE_BEGIN /* Wavelength to RGB */ -ccl_device_noinline void svm_node_wavelength(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_wavelength(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint wavelength, diff --git a/intern/cycles/kernel/svm/svm_white_noise.h b/intern/cycles/kernel/svm/svm_white_noise.h index ccc49bf1a7c..6c2c3d6a683 100644 --- a/intern/cycles/kernel/svm/svm_white_noise.h +++ b/intern/cycles/kernel/svm/svm_white_noise.h @@ -16,7 +16,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_noinline void svm_node_tex_white_noise(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_tex_white_noise(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint dimensions, diff --git a/intern/cycles/kernel/svm/svm_wireframe.h b/intern/cycles/kernel/svm/svm_wireframe.h index 70d1211aa4a..d75976d23e1 100644 --- a/intern/cycles/kernel/svm/svm_wireframe.h +++ b/intern/cycles/kernel/svm/svm_wireframe.h @@ -34,7 +34,7 @@ CCL_NAMESPACE_BEGIN /* Wireframe Node */ -ccl_device_inline float wireframe(ccl_global const KernelGlobals *kg, +ccl_device_inline float wireframe(KernelGlobals kg, ccl_private ShaderData *sd, float size, int pixel_size, @@ -91,7 +91,7 @@ ccl_device_inline float wireframe(ccl_global const KernelGlobals *kg, return 0.0f; } -ccl_device_noinline void svm_node_wireframe(ccl_global const KernelGlobals *kg, +ccl_device_noinline void svm_node_wireframe(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) From fc4b1fede385687acb2cf7f82591aa43097110a9 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 17 Oct 2021 20:09:45 +0200 Subject: [PATCH 0897/1500] Cleanup: consistently use uint32_t for path flag --- .../cycles/kernel/closure/bsdf_transparent.h | 2 +- .../integrator/integrator_intersect_closest.h | 4 +- .../integrator/integrator_shade_surface.h | 6 +- .../integrator/integrator_shade_volume.h | 6 +- intern/cycles/kernel/kernel_accumulate.h | 58 +++++++++------- intern/cycles/kernel/kernel_bake.h | 2 +- intern/cycles/kernel/kernel_light.h | 10 +-- intern/cycles/kernel/kernel_passes.h | 2 +- intern/cycles/kernel/kernel_shader.h | 4 +- intern/cycles/kernel/kernel_shadow_catcher.h | 14 ++-- intern/cycles/kernel/kernel_types.h | 66 +++++++++---------- intern/cycles/kernel/osl/background.cpp | 4 +- .../cycles/kernel/osl/bsdf_diffuse_ramp.cpp | 2 +- intern/cycles/kernel/osl/bsdf_phong_ramp.cpp | 2 +- intern/cycles/kernel/osl/emissive.cpp | 2 +- intern/cycles/kernel/osl/osl_bssrdf.cpp | 4 +- intern/cycles/kernel/osl/osl_closures.cpp | 42 ++++++------ intern/cycles/kernel/osl/osl_closures.h | 6 +- intern/cycles/kernel/osl/osl_shader.cpp | 10 +-- intern/cycles/kernel/osl/osl_shader.h | 6 +- intern/cycles/kernel/svm/svm.h | 2 +- intern/cycles/kernel/svm/svm_aov.h | 2 +- intern/cycles/kernel/svm/svm_closure.h | 4 +- intern/cycles/kernel/svm/svm_light_path.h | 2 +- intern/cycles/kernel/svm/svm_tex_coord.h | 6 +- 25 files changed, 138 insertions(+), 130 deletions(-) diff --git a/intern/cycles/kernel/closure/bsdf_transparent.h b/intern/cycles/kernel/closure/bsdf_transparent.h index 8313ab964d7..e801b6ea1a3 100644 --- a/intern/cycles/kernel/closure/bsdf_transparent.h +++ b/intern/cycles/kernel/closure/bsdf_transparent.h @@ -36,7 +36,7 @@ CCL_NAMESPACE_BEGIN ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd, const float3 weight, - int path_flag) + uint32_t path_flag) { /* Check cutoff weight. */ float sample_weight = fabsf(average(weight)); diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/integrator_intersect_closest.h index e915d984e1d..317ea76553a 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_closest.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_closest.h @@ -58,7 +58,7 @@ ccl_device_forceinline bool integrator_intersect_terminate(KernelGlobals kg, * and evaluating the shader when not needed. Only for emission and transparent * surfaces in front of emission do we need to evaluate the shader, since we * perform MIS as part of indirect rays. */ - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); const float probability = path_state_continuation_probability(kg, state, path_flag); if (probability != 1.0f) { @@ -184,7 +184,7 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg, IntegratorState s /* NOTE: if we make lights visible to camera rays, we'll need to initialize * these in the path_state_init. */ const int last_type = INTEGRATOR_STATE(state, isect, type); - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); hit = lights_intersect( kg, &ray, &isect, last_isect_prim, last_isect_object, last_type, path_flag) || diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index bc97fde0e4a..bbf613a669e 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -56,7 +56,7 @@ ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg, if (kernel_data.background.transparent) { const float3 throughput = INTEGRATOR_STATE(state, path, throughput); const float transparent = average(holdout_weight * throughput); - kernel_accum_transparent(kg, state, transparent, render_buffer); + kernel_accum_transparent(kg, state, path_flag, transparent, render_buffer); } if (isequal_float3(holdout_weight, one_float3())) { return false; @@ -118,7 +118,7 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, /* Sample position on a light. */ LightSample ls ccl_optional_struct_init; { - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); const uint bounce = INTEGRATOR_STATE(state, path, bounce); float light_u, light_v; path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); @@ -375,7 +375,7 @@ ccl_device bool integrate_surface(KernelGlobals kg, #ifdef __VOLUME__ if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { #endif - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); #ifdef __SUBSURFACE__ /* Can skip shader evaluation for BSSRDF exit point without bump mapping. */ diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index e465a993041..d0dde815b5c 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -94,7 +94,7 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals kg, ccl_private ShaderData *ccl_restrict sd, ccl_private VolumeShaderCoefficients *coeff) { - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); shader_eval_volume(kg, state, sd, path_flag, [=](const int i) { return integrator_state_read_volume_stack(state, i); }); @@ -686,7 +686,7 @@ ccl_device_forceinline bool integrate_volume_sample_light( } /* Sample position on a light. */ - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); const uint bounce = INTEGRATOR_STATE(state, path, bounce); float light_u, light_v; path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); @@ -725,7 +725,7 @@ ccl_device_forceinline void integrate_volume_direct_light( * TODO: decorrelate random numbers and use light_sample_new_position to * avoid resampling the CDF. */ { - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); const uint bounce = INTEGRATOR_STATE(state, path, bounce); float light_u, light_v; path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index bc45bbd5b07..d4bb1ef8685 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -153,7 +153,7 @@ ccl_device_inline int kernel_accum_sample(KernelGlobals kg, } ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, - ConstIntegratorState state, + const int sample, const float3 contribution, ccl_global float *ccl_restrict buffer) { @@ -166,7 +166,6 @@ ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, return; } - const int sample = INTEGRATOR_STATE(state, path, sample); if (sample_is_even(kernel_data.integrator.sampling_pattern, sample)) { kernel_write_pass_float4( buffer + kernel_data.film.pass_adaptive_aux_buffer, @@ -186,7 +185,7 @@ ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, * passes (like combined, adaptive sampling). */ ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, - ConstIntegratorState state, + const uint32_t path_flag, const float3 contribution, ccl_global float *ccl_restrict buffer) { @@ -198,7 +197,7 @@ ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); /* Matte pass. */ - if (kernel_shadow_catcher_is_matte_path(kg, state)) { + if (kernel_shadow_catcher_is_matte_path(path_flag)) { kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution); /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive * sampling is based on how noisy the combined pass is as if there were no catchers in the @@ -206,7 +205,7 @@ ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, } /* Shadow catcher pass. */ - if (kernel_shadow_catcher_is_object_pass(kg, state)) { + if (kernel_shadow_catcher_is_object_pass(path_flag)) { kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); return true; } @@ -215,7 +214,7 @@ ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, } ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, - ConstIntegratorState state, + const uint32_t path_flag, const float3 contribution, const float transparent, ccl_global float *ccl_restrict buffer) @@ -227,12 +226,12 @@ ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED); kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); - if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { + if (path_flag & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { return true; } /* Matte pass. */ - if (kernel_shadow_catcher_is_matte_path(kg, state)) { + if (kernel_shadow_catcher_is_matte_path(path_flag)) { kernel_write_pass_float4( buffer + kernel_data.film.pass_shadow_catcher_matte, make_float4(contribution.x, contribution.y, contribution.z, transparent)); @@ -242,7 +241,7 @@ ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, } /* Shadow catcher pass. */ - if (kernel_shadow_catcher_is_object_pass(kg, state)) { + if (kernel_shadow_catcher_is_object_pass(path_flag)) { /* NOTE: The transparency of the shadow catcher pass is ignored. It is not needed for the * calculation and the alpha channel of the pass contains numbers of samples contributed to a * pixel of the pass. */ @@ -254,7 +253,7 @@ ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, } ccl_device void kernel_accum_shadow_catcher_transparent_only(KernelGlobals kg, - ConstIntegratorState state, + const uint32_t path_flag, const float transparent, ccl_global float *ccl_restrict buffer) { @@ -265,7 +264,7 @@ ccl_device void kernel_accum_shadow_catcher_transparent_only(KernelGlobals kg, kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); /* Matte pass. */ - if (kernel_shadow_catcher_is_matte_path(kg, state)) { + if (kernel_shadow_catcher_is_matte_path(path_flag)) { kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, transparent); } } @@ -278,12 +277,13 @@ ccl_device void kernel_accum_shadow_catcher_transparent_only(KernelGlobals kg, /* Write combined pass. */ ccl_device_inline void kernel_accum_combined_pass(KernelGlobals kg, - ConstIntegratorState state, + const uint32_t path_flag, + const int sample, const float3 contribution, ccl_global float *ccl_restrict buffer) { #ifdef __SHADOW_CATCHER__ - if (kernel_accum_shadow_catcher(kg, state, contribution, buffer)) { + if (kernel_accum_shadow_catcher(kg, path_flag, contribution, buffer)) { return; } #endif @@ -292,19 +292,20 @@ ccl_device_inline void kernel_accum_combined_pass(KernelGlobals kg, kernel_write_pass_float3(buffer + kernel_data.film.pass_combined, contribution); } - kernel_accum_adaptive_buffer(kg, state, contribution, buffer); + kernel_accum_adaptive_buffer(kg, sample, contribution, buffer); } /* Write combined pass with transparency. */ ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg, - ConstIntegratorState state, + const uint32_t path_flag, + const int sample, const float3 contribution, const float transparent, ccl_global float *ccl_restrict buffer) { #ifdef __SHADOW_CATCHER__ - if (kernel_accum_shadow_catcher_transparent(kg, state, contribution, transparent, buffer)) { + if (kernel_accum_shadow_catcher_transparent(kg, path_flag, contribution, transparent, buffer)) { return; } #endif @@ -315,7 +316,7 @@ ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg, make_float4(contribution.x, contribution.y, contribution.z, transparent)); } - kernel_accum_adaptive_buffer(kg, state, contribution, buffer); + kernel_accum_adaptive_buffer(kg, sample, contribution, buffer); } /* Write background or emission to appropriate pass. */ @@ -331,7 +332,7 @@ ccl_device_inline void kernel_accum_emission_or_background_pass(KernelGlobals kg } #ifdef __PASSES__ - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); int pass_offset = PASS_UNUSED; /* Denoising albedo. */ @@ -401,11 +402,14 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); - kernel_accum_combined_pass(kg, state, contribution, buffer); + const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag); + const int sample = INTEGRATOR_STATE(state, path, sample); + + kernel_accum_combined_pass(kg, path_flag, sample, contribution, buffer); #ifdef __PASSES__ if (kernel_data.film.light_pass_flag & PASS_ANY) { - const int path_flag = INTEGRATOR_STATE(state, shadow_path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag); int pass_offset = PASS_UNUSED; if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { @@ -467,6 +471,7 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, * in many places. */ ccl_device_inline void kernel_accum_transparent(KernelGlobals kg, ConstIntegratorState state, + const uint32_t path_flag, const float transparent, ccl_global float *ccl_restrict render_buffer) { @@ -476,7 +481,7 @@ ccl_device_inline void kernel_accum_transparent(KernelGlobals kg, kernel_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent); } - kernel_accum_shadow_catcher_transparent_only(kg, state, transparent, buffer); + kernel_accum_shadow_catcher_transparent_only(kg, path_flag, transparent, buffer); } /* Write background contribution to render buffer. @@ -493,12 +498,15 @@ ccl_device_inline void kernel_accum_background(KernelGlobals kg, kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (is_transparent_background_ray) { - kernel_accum_transparent(kg, state, transparent, render_buffer); + kernel_accum_transparent(kg, state, path_flag, transparent, render_buffer); } else { - kernel_accum_combined_transparent_pass(kg, state, contribution, transparent, buffer); + const int sample = INTEGRATOR_STATE(state, path, sample); + kernel_accum_combined_transparent_pass( + kg, path_flag, sample, contribution, transparent, buffer); } kernel_accum_emission_or_background_pass( kg, state, contribution, buffer, kernel_data.film.pass_background); @@ -515,8 +523,10 @@ ccl_device_inline void kernel_accum_emission(KernelGlobals kg, kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + const int sample = INTEGRATOR_STATE(state, path, sample); - kernel_accum_combined_pass(kg, state, contribution, buffer); + kernel_accum_combined_pass(kg, path_flag, sample, contribution, buffer); kernel_accum_emission_or_background_pass( kg, state, contribution, buffer, kernel_data.film.pass_emission); } diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index 933ee0082c2..25b8ca55ead 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -75,7 +75,7 @@ ccl_device void kernel_background_evaluate(KernelGlobals kg, /* Evaluate shader. * This is being evaluated for all BSDFs, so path flag does not contain a specific type. */ - const int path_flag = PATH_RAY_EMISSION; + const uint32_t path_flag = PATH_RAY_EMISSION; shader_eval_surface( kg, INTEGRATOR_STATE_NULL, &sd, NULL, path_flag); float3 color = shader_background_eval(&sd); diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index a7a95918b4e..b3eaed4fcb0 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -50,7 +50,7 @@ ccl_device_inline bool light_sample(KernelGlobals kg, const float randu, const float randv, const float3 P, - const int path_flag, + const uint32_t path_flag, ccl_private LightSample *ls) { const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); @@ -215,7 +215,7 @@ ccl_device bool lights_intersect(KernelGlobals kg, const int last_prim, const int last_object, const int last_type, - const int path_flag) + const uint32_t path_flag) { for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); @@ -797,7 +797,7 @@ ccl_device_noinline bool light_distribution_sample(KernelGlobals kg, const float time, const float3 P, const int bounce, - const int path_flag, + const uint32_t path_flag, ccl_private LightSample *ls) { /* Sample light index from distribution. */ @@ -837,7 +837,7 @@ ccl_device_inline bool light_distribution_sample_from_volume_segment(KernelGloba const float time, const float3 P, const int bounce, - const int path_flag, + const uint32_t path_flag, ccl_private LightSample *ls) { return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); @@ -849,7 +849,7 @@ ccl_device_inline bool light_distribution_sample_from_position(KernelGlobals kg, const float time, const float3 P, const int bounce, - const int path_flag, + const uint32_t path_flag, ccl_private LightSample *ls) { return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h index 4d05b63bfbd..4e4ffa68d25 100644 --- a/intern/cycles/kernel/kernel_passes.h +++ b/intern/cycles/kernel/kernel_passes.h @@ -201,7 +201,7 @@ ccl_device_inline void kernel_write_data_passes(KernelGlobals kg, ccl_global float *ccl_restrict render_buffer) { #ifdef __PASSES__ - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (!(path_flag & PATH_RAY_CAMERA)) { return; diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 4a57d22775a..4eded9039bd 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -580,7 +580,7 @@ ccl_device void shader_eval_surface(KernelGlobals kg, ConstIntegratorState state, ccl_private ShaderData *ccl_restrict sd, ccl_global float *ccl_restrict buffer, - int path_flag) + uint32_t path_flag) { /* If path is being terminated, we are tracing a shadow ray or evaluating * emission, then we don't need to store closures. The emission and shadow @@ -767,7 +767,7 @@ template ccl_device_inline void shader_eval_volume(KernelGlobals kg, ConstIntegratorState state, ccl_private ShaderData *ccl_restrict sd, - const int path_flag, + const uint32_t path_flag, StackReadOp stack_read) { /* If path is being terminated, we are tracing a shadow ray or evaluating diff --git a/intern/cycles/kernel/kernel_shadow_catcher.h b/intern/cycles/kernel/kernel_shadow_catcher.h index 8dc7a568b33..00dddb5b198 100644 --- a/intern/cycles/kernel/kernel_shadow_catcher.h +++ b/intern/cycles/kernel/kernel_shadow_catcher.h @@ -39,7 +39,7 @@ ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(KernelGlobals return false; } - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if ((path_flag & PATH_RAY_TRANSPARENT_BACKGROUND) == 0) { /* Split only on primary rays, secondary bounces are to treat shadow catcher as a regular @@ -66,7 +66,7 @@ ccl_device_inline bool kernel_shadow_catcher_path_can_split(KernelGlobals kg, return false; } - const int path_flag = INTEGRATOR_STATE(state, path, flag); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) { /* Shadow catcher was already hit and the state was split. No further split is allowed. */ @@ -105,16 +105,14 @@ ccl_device_inline bool kernel_shadow_catcher_split(KernelGlobals kg, #ifdef __SHADOW_CATCHER__ -ccl_device_forceinline bool kernel_shadow_catcher_is_matte_path(KernelGlobals kg, - ConstIntegratorState state) +ccl_device_forceinline bool kernel_shadow_catcher_is_matte_path(const uint32_t path_flag) { - return (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_HIT) == 0; + return (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) == 0; } -ccl_device_forceinline bool kernel_shadow_catcher_is_object_pass(KernelGlobals kg, - ConstIntegratorState state) +ccl_device_forceinline bool kernel_shadow_catcher_is_object_pass(const uint32_t path_flag) { - return INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS; + return path_flag & PATH_RAY_SHADOW_CATCHER_PASS; } #endif /* __SHADOW_CATCHER__ */ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 5625c0e4d19..e478019b25c 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -197,30 +197,30 @@ enum PathRayFlag { * NOTE: Recalculated after a surface bounce. */ - PATH_RAY_CAMERA = (1 << 0), - PATH_RAY_REFLECT = (1 << 1), - PATH_RAY_TRANSMIT = (1 << 2), - PATH_RAY_DIFFUSE = (1 << 3), - PATH_RAY_GLOSSY = (1 << 4), - PATH_RAY_SINGULAR = (1 << 5), - PATH_RAY_TRANSPARENT = (1 << 6), - PATH_RAY_VOLUME_SCATTER = (1 << 7), + PATH_RAY_CAMERA = (1U << 0U), + PATH_RAY_REFLECT = (1U << 1U), + PATH_RAY_TRANSMIT = (1U << 2U), + PATH_RAY_DIFFUSE = (1U << 3U), + PATH_RAY_GLOSSY = (1U << 4U), + PATH_RAY_SINGULAR = (1U << 5U), + PATH_RAY_TRANSPARENT = (1U << 6U), + PATH_RAY_VOLUME_SCATTER = (1U << 7U), /* Shadow ray visibility. */ - PATH_RAY_SHADOW_OPAQUE = (1 << 8), - PATH_RAY_SHADOW_TRANSPARENT = (1 << 9), + PATH_RAY_SHADOW_OPAQUE = (1U << 8U), + PATH_RAY_SHADOW_TRANSPARENT = (1U << 9U), PATH_RAY_SHADOW = (PATH_RAY_SHADOW_OPAQUE | PATH_RAY_SHADOW_TRANSPARENT), /* Special flag to tag unaligned BVH nodes. * Only set and used in BVH nodes to distinguish how to interpret bounding box information stored - * in the node (either it should be intersected as AABB or as OBB). */ - PATH_RAY_NODE_UNALIGNED = (1 << 10), + * in the node (either it should be intersected as AABB or as OBBU). */ + PATH_RAY_NODE_UNALIGNED = (1U << 10U), /* Subset of flags used for ray visibility for intersection. * * NOTE: SHADOW_CATCHER macros below assume there are no more than * 16 visibility bits. */ - PATH_RAY_ALL_VISIBILITY = ((1 << 11) - 1), + PATH_RAY_ALL_VISIBILITY = ((1U << 11U) - 1U), /* -------------------------------------------------------------------- * Path flags. @@ -228,69 +228,69 @@ enum PathRayFlag { /* Don't apply multiple importance sampling weights to emission from * lamp or surface hits, because they were not direct light sampled. */ - PATH_RAY_MIS_SKIP = (1 << 11), + PATH_RAY_MIS_SKIP = (1U << 11U), /* Diffuse bounce earlier in the path, skip SSS to improve performance * and avoid branching twice with disk sampling SSS. */ - PATH_RAY_DIFFUSE_ANCESTOR = (1 << 12), + PATH_RAY_DIFFUSE_ANCESTOR = (1U << 12U), /* Single pass has been written. */ - PATH_RAY_SINGLE_PASS_DONE = (1 << 13), + PATH_RAY_SINGLE_PASS_DONE = (1U << 13U), /* Zero background alpha, for camera or transparent glass rays. */ - PATH_RAY_TRANSPARENT_BACKGROUND = (1 << 14), + PATH_RAY_TRANSPARENT_BACKGROUND = (1U << 14U), /* Terminate ray immediately at next bounce. */ - PATH_RAY_TERMINATE_ON_NEXT_SURFACE = (1 << 15), - PATH_RAY_TERMINATE_IN_NEXT_VOLUME = (1 << 16), + PATH_RAY_TERMINATE_ON_NEXT_SURFACE = (1U << 15U), + PATH_RAY_TERMINATE_IN_NEXT_VOLUME = (1U << 16U), /* Ray is to be terminated, but continue with transparent bounces and * emission as long as we encounter them. This is required to make the * MIS between direct and indirect light rays match, as shadow rays go * through transparent surfaces to reach emission too. */ - PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1 << 17), + PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1U << 17U), /* Terminate ray immediately after volume shading. */ - PATH_RAY_TERMINATE_AFTER_VOLUME = (1 << 18), + PATH_RAY_TERMINATE_AFTER_VOLUME = (1U << 18U), /* Ray is to be terminated. */ PATH_RAY_TERMINATE = (PATH_RAY_TERMINATE_ON_NEXT_SURFACE | PATH_RAY_TERMINATE_IN_NEXT_VOLUME | PATH_RAY_TERMINATE_AFTER_TRANSPARENT | PATH_RAY_TERMINATE_AFTER_VOLUME), /* Path and shader is being evaluated for direct lighting emission. */ - PATH_RAY_EMISSION = (1 << 19), + PATH_RAY_EMISSION = (1U << 19U), /* Perform subsurface scattering. */ - PATH_RAY_SUBSURFACE_RANDOM_WALK = (1 << 20), - PATH_RAY_SUBSURFACE_DISK = (1 << 21), - PATH_RAY_SUBSURFACE_USE_FRESNEL = (1 << 22), + PATH_RAY_SUBSURFACE_RANDOM_WALK = (1U << 20U), + PATH_RAY_SUBSURFACE_DISK = (1U << 21U), + PATH_RAY_SUBSURFACE_USE_FRESNEL = (1U << 22U), PATH_RAY_SUBSURFACE = (PATH_RAY_SUBSURFACE_RANDOM_WALK | PATH_RAY_SUBSURFACE_DISK | PATH_RAY_SUBSURFACE_USE_FRESNEL), /* Contribute to denoising features. */ - PATH_RAY_DENOISING_FEATURES = (1 << 23), + PATH_RAY_DENOISING_FEATURES = (1U << 23U), /* Render pass categories. */ - PATH_RAY_REFLECT_PASS = (1 << 24), - PATH_RAY_TRANSMISSION_PASS = (1 << 25), - PATH_RAY_VOLUME_PASS = (1 << 26), + PATH_RAY_REFLECT_PASS = (1U << 24U), + PATH_RAY_TRANSMISSION_PASS = (1U << 25U), + PATH_RAY_VOLUME_PASS = (1U << 26U), PATH_RAY_ANY_PASS = (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS | PATH_RAY_VOLUME_PASS), /* Shadow ray is for a light or surface. */ - PATH_RAY_SHADOW_FOR_LIGHT = (1 << 27), + PATH_RAY_SHADOW_FOR_LIGHT = (1U << 27U), /* A shadow catcher object was hit and the path was split into two. */ - PATH_RAY_SHADOW_CATCHER_HIT = (1 << 28), + PATH_RAY_SHADOW_CATCHER_HIT = (1U << 28U), /* A shadow catcher object was hit and this path traces only shadow catchers, writing them into * their dedicated pass for later division. * * NOTE: Is not covered with `PATH_RAY_ANY_PASS` because shadow catcher does special handling * which is separate from the light passes. */ - PATH_RAY_SHADOW_CATCHER_PASS = (1 << 29), + PATH_RAY_SHADOW_CATCHER_PASS = (1U << 29U), /* Path is evaluating background for an approximate shadow catcher with non-transparent film. */ - PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1 << 30), + PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1U << 30U), }; /* Configure ray visibility bits for rays and objects respectively, diff --git a/intern/cycles/kernel/osl/background.cpp b/intern/cycles/kernel/osl/background.cpp index 8e497986dcc..bb290a5ced2 100644 --- a/intern/cycles/kernel/osl/background.cpp +++ b/intern/cycles/kernel/osl/background.cpp @@ -54,7 +54,7 @@ using namespace OSL; /// class GenericBackgroundClosure : public CClosurePrimitive { public: - void setup(ShaderData *sd, int /* path_flag */, float3 weight) + void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { background_setup(sd, weight); } @@ -69,7 +69,7 @@ class GenericBackgroundClosure : public CClosurePrimitive { /// class HoldoutClosure : CClosurePrimitive { public: - void setup(ShaderData *sd, int /* path_flag */, float3 weight) + void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_HOLDOUT_ID, weight); sd->flag |= SD_HOLDOUT; diff --git a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp index a2f9d3f759a..45216f4c74d 100644 --- a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp @@ -53,7 +53,7 @@ class DiffuseRampClosure : public CBSDFClosure { DiffuseRampBsdf params; Color3 colors[8]; - void setup(ShaderData *sd, int /* path_flag */, float3 weight) + void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { DiffuseRampBsdf *bsdf = (DiffuseRampBsdf *)bsdf_alloc_osl( sd, sizeof(DiffuseRampBsdf), weight, ¶ms); diff --git a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp index 812c3b6e71b..90160fba962 100644 --- a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp @@ -52,7 +52,7 @@ class PhongRampClosure : public CBSDFClosure { PhongRampBsdf params; Color3 colors[8]; - void setup(ShaderData *sd, int /* path_flag */, float3 weight) + void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { PhongRampBsdf *bsdf = (PhongRampBsdf *)bsdf_alloc_osl( sd, sizeof(PhongRampBsdf), weight, ¶ms); diff --git a/intern/cycles/kernel/osl/emissive.cpp b/intern/cycles/kernel/osl/emissive.cpp index 80dfbee879e..5a7fe14b22e 100644 --- a/intern/cycles/kernel/osl/emissive.cpp +++ b/intern/cycles/kernel/osl/emissive.cpp @@ -56,7 +56,7 @@ using namespace OSL; /// class GenericEmissiveClosure : public CClosurePrimitive { public: - void setup(ShaderData *sd, int /* path_flag */, float3 weight) + void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { emission_setup(sd, weight); } diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/osl_bssrdf.cpp index b6b0d72103a..5bf7b604498 100644 --- a/intern/cycles/kernel/osl/osl_bssrdf.cpp +++ b/intern/cycles/kernel/osl/osl_bssrdf.cpp @@ -67,7 +67,7 @@ class CBSSRDFClosure : public CClosurePrimitive { ior = 1.4f; } - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { if (method == u_burley) { alloc(sd, path_flag, weight, CLOSURE_BSSRDF_BURLEY_ID); @@ -80,7 +80,7 @@ class CBSSRDFClosure : public CClosurePrimitive { } } - void alloc(ShaderData *sd, int path_flag, float3 weight, ClosureType type) + void alloc(ShaderData *sd, uint32_t path_flag, float3 weight, ClosureType type) { Bssrdf *bssrdf = bssrdf_alloc(sd, weight); diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp index 94712a4dd13..b59bf5a1322 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/osl_closures.cpp @@ -203,7 +203,7 @@ CLOSURE_FLOAT3_PARAM(DiffuseClosure, params.N), public: PrincipledSheenBsdf params; - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { if (!skip(sd, path_flag, LABEL_DIFFUSE)) { PrincipledSheenBsdf *bsdf = (PrincipledSheenBsdf *)bsdf_alloc_osl( @@ -228,7 +228,7 @@ class PrincipledHairClosure : public CBSDFClosure { public: PrincipledHairBSDF params; - PrincipledHairBSDF *alloc(ShaderData *sd, int path_flag, float3 weight) + PrincipledHairBSDF *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)bsdf_alloc_osl( sd, sizeof(PrincipledHairBSDF), weight, ¶ms); @@ -246,7 +246,7 @@ class PrincipledHairClosure : public CBSDFClosure { return bsdf; } - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { if (!skip(sd, path_flag, LABEL_GLOSSY)) { PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)alloc(sd, path_flag, weight); @@ -282,7 +282,7 @@ class PrincipledClearcoatClosure : public CBSDFClosure { MicrofacetBsdf params; float clearcoat, clearcoat_roughness; - MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( sd, sizeof(MicrofacetBsdf), weight, ¶ms); @@ -306,7 +306,7 @@ class PrincipledClearcoatClosure : public CBSDFClosure { return bsdf; } - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -496,7 +496,7 @@ void OSLShader::register_closures(OSLShadingSystem *ss_) /* BSDF Closure */ -bool CBSDFClosure::skip(const ShaderData *sd, int path_flag, int scattering) +bool CBSDFClosure::skip(const ShaderData *sd, uint32_t path_flag, int scattering) { /* caustic options */ if ((scattering & LABEL_GLOSSY) && (path_flag & PATH_RAY_DIFFUSE)) { @@ -519,7 +519,7 @@ class MicrofacetClosure : public CBSDFClosure { ustring distribution; int refract; - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { static ustring u_ggx("ggx"); static ustring u_default("default"); @@ -595,7 +595,7 @@ class MicrofacetFresnelClosure : public CBSDFClosure { float3 color; float3 cspec0; - MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { /* Technically, the MultiGGX Glass closure may also transmit. However, * since this is set statically and only used for caustic flags, this @@ -625,7 +625,7 @@ class MicrofacetFresnelClosure : public CBSDFClosure { class MicrofacetGGXFresnelClosure : public MicrofacetFresnelClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -654,7 +654,7 @@ CCLOSURE_PREPARE(closure_bsdf_microfacet_ggx_fresnel_prepare, MicrofacetGGXFresn class MicrofacetGGXAnisoFresnelClosure : public MicrofacetFresnelClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -689,7 +689,7 @@ class MicrofacetMultiClosure : public CBSDFClosure { MicrofacetBsdf params; float3 color; - MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { /* Technically, the MultiGGX closure may also transmit. However, * since this is set statically and only used for caustic flags, this @@ -719,7 +719,7 @@ class MicrofacetMultiClosure : public CBSDFClosure { class MicrofacetMultiGGXClosure : public MicrofacetMultiClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -747,7 +747,7 @@ CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_prepare, MicrofacetMultiGGXCl class MicrofacetMultiGGXAnisoClosure : public MicrofacetMultiClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -779,7 +779,7 @@ class MicrofacetMultiGGXGlassClosure : public MicrofacetMultiClosure { { } - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -813,7 +813,7 @@ class MicrofacetMultiFresnelClosure : public CBSDFClosure { float3 color; float3 cspec0; - MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { /* Technically, the MultiGGX closure may also transmit. However, * since this is set statically and only used for caustic flags, this @@ -843,7 +843,7 @@ class MicrofacetMultiFresnelClosure : public CBSDFClosure { class MicrofacetMultiGGXFresnelClosure : public MicrofacetMultiFresnelClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -873,7 +873,7 @@ CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_fresnel_prepare, class MicrofacetMultiGGXAnisoFresnelClosure : public MicrofacetMultiFresnelClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -907,7 +907,7 @@ class MicrofacetMultiGGXGlassFresnelClosure : public MicrofacetMultiFresnelClosu { } - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -942,7 +942,7 @@ class TransparentClosure : public CBSDFClosure { ShaderClosure params; float3 unused; - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { bsdf_transparent_setup(sd, weight, path_flag); } @@ -961,7 +961,7 @@ CCLOSURE_PREPARE(closure_bsdf_transparent_prepare, TransparentClosure) class VolumeAbsorptionClosure : public CBSDFClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { volume_extinction_setup(sd, weight); } @@ -980,7 +980,7 @@ class VolumeHenyeyGreensteinClosure : public CBSDFClosure { public: HenyeyGreensteinVolume params; - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { volume_extinction_setup(sd, weight); diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/osl_closures.h index e4058e3a746..90a965a3365 100644 --- a/intern/cycles/kernel/osl/osl_closures.h +++ b/intern/cycles/kernel/osl/osl_closures.h @@ -113,7 +113,7 @@ void closure_bsdf_principled_hair_prepare(OSL::RendererServices *, int id, void class CClosurePrimitive { public: - virtual void setup(ShaderData *sd, int path_flag, float3 weight) = 0; + virtual void setup(ShaderData *sd, uint32_t path_flag, float3 weight) = 0; OSL::ustring label; }; @@ -122,7 +122,7 @@ class CClosurePrimitive { class CBSDFClosure : public CClosurePrimitive { public: - bool skip(const ShaderData *sd, int path_flag, int scattering); + bool skip(const ShaderData *sd, uint32_t path_flag, int scattering); }; #define BSDF_CLOSURE_CLASS_BEGIN(Upper, lower, structname, TYPE) \ @@ -132,7 +132,7 @@ class CBSDFClosure : public CClosurePrimitive { structname params; \ float3 unused; \ \ - void setup(ShaderData *sd, int path_flag, float3 weight) \ + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) \ { \ if (!skip(sd, path_flag, TYPE)) { \ structname *bsdf = (structname *)bsdf_alloc_osl(sd, sizeof(structname), weight, ¶ms); \ diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp index a1df63ca8ff..4c067e88ab6 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/osl_shader.cpp @@ -90,7 +90,7 @@ void OSLShader::thread_free(KernelGlobalsCPU *kg) static void shaderdata_to_shaderglobals(const KernelGlobalsCPU *kg, ShaderData *sd, const IntegratorStateCPU *state, - int path_flag, + uint32_t path_flag, OSLThreadData *tdata) { OSL::ShaderGlobals *globals = &tdata->globals; @@ -140,7 +140,7 @@ static void shaderdata_to_shaderglobals(const KernelGlobalsCPU *kg, /* Surface */ static void flatten_surface_closure_tree(ShaderData *sd, - int path_flag, + uint32_t path_flag, const OSL::ClosureColor *closure, float3 weight = make_float3(1.0f, 1.0f, 1.0f)) { @@ -177,7 +177,7 @@ static void flatten_surface_closure_tree(ShaderData *sd, void OSLShader::eval_surface(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, - int path_flag) + uint32_t path_flag) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; @@ -285,7 +285,7 @@ static void flatten_background_closure_tree(ShaderData *sd, void OSLShader::eval_background(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, - int path_flag) + uint32_t path_flag) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; @@ -343,7 +343,7 @@ static void flatten_volume_closure_tree(ShaderData *sd, void OSLShader::eval_volume(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, - int path_flag) + uint32_t path_flag) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; diff --git a/intern/cycles/kernel/osl/osl_shader.h b/intern/cycles/kernel/osl/osl_shader.h index 686a1e1374a..2b3810b0a33 100644 --- a/intern/cycles/kernel/osl/osl_shader.h +++ b/intern/cycles/kernel/osl/osl_shader.h @@ -57,15 +57,15 @@ class OSLShader { static void eval_surface(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, - int path_flag); + uint32_t path_flag); static void eval_background(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, - int path_flag); + uint32_t path_flag); static void eval_volume(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd, - int path_flag); + uint32_t path_flag); static void eval_displacement(const KernelGlobalsCPU *kg, const IntegratorStateCPU *state, ShaderData *sd); diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 9692308c496..57879dc238f 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -230,7 +230,7 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, ConstIntegratorState state, ShaderData *sd, ccl_global float *render_buffer, - int path_flag) + uint32_t path_flag) { float stack[SVM_STACK_SIZE]; int offset = sd->shader & SHADER_MASK; diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h index d09eaa61cc0..aba5bdc0113 100644 --- a/intern/cycles/kernel/svm/svm_aov.h +++ b/intern/cycles/kernel/svm/svm_aov.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_inline bool svm_node_aov_check(const int path_flag, ccl_global float *render_buffer) +ccl_device_inline bool svm_node_aov_check(const uint32_t path_flag, ccl_global float *render_buffer) { bool is_primary = (path_flag & PATH_RAY_CAMERA) && (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)); diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h index fb10288da72..3378832c233 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/svm_closure.h @@ -79,7 +79,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, - int path_flag, + uint32_t path_flag, int offset) { uint type, param1_offset, param2_offset; @@ -1015,7 +1015,7 @@ ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, - int path_flag, + uint32_t path_flag, int offset) { #ifdef __VOLUME__ diff --git a/intern/cycles/kernel/svm/svm_light_path.h b/intern/cycles/kernel/svm/svm_light_path.h index 955a1f23379..c61ace9757a 100644 --- a/intern/cycles/kernel/svm/svm_light_path.h +++ b/intern/cycles/kernel/svm/svm_light_path.h @@ -25,7 +25,7 @@ ccl_device_noinline void svm_node_light_path(KernelGlobals kg, ccl_private float *stack, uint type, uint out_offset, - int path_flag) + uint32_t path_flag) { float info = 0.0f; diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index 657a4bb32a8..fe777eb34c8 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -24,7 +24,7 @@ CCL_NAMESPACE_BEGIN ccl_device_noinline int svm_node_tex_coord(KernelGlobals kg, ccl_private ShaderData *sd, - int path_flag, + uint32_t path_flag, ccl_private float *stack, uint4 node, int offset) @@ -105,7 +105,7 @@ ccl_device_noinline int svm_node_tex_coord(KernelGlobals kg, ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg, ccl_private ShaderData *sd, - int path_flag, + uint32_t path_flag, ccl_private float *stack, uint4 node, int offset) @@ -190,7 +190,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg, ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg, ccl_private ShaderData *sd, - int path_flag, + uint32_t path_flag, ccl_private float *stack, uint4 node, int offset) From a184d0dd023cc0b6fee5e02510addb91d66f8e01 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 18 Oct 2021 18:19:01 +0200 Subject: [PATCH 0898/1500] Cleanup: fix outdated comment and use of atomics This is only used by a single device, not need for thread safety. --- intern/cycles/integrator/work_tile_scheduler.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/intern/cycles/integrator/work_tile_scheduler.cpp b/intern/cycles/integrator/work_tile_scheduler.cpp index e6ada2f46ee..234b1fae915 100644 --- a/intern/cycles/integrator/work_tile_scheduler.cpp +++ b/intern/cycles/integrator/work_tile_scheduler.cpp @@ -88,7 +88,7 @@ bool WorkTileScheduler::get_work(KernelWorkTile *work_tile_, const int max_work_ DCHECK_NE(max_num_path_states_, 0); - const int work_index = atomic_fetch_and_add_int32(&next_work_index_, 1); + const int work_index = next_work_index_++; if (work_index >= total_work_size_) { return false; } @@ -121,12 +121,8 @@ bool WorkTileScheduler::get_work(KernelWorkTile *work_tile_, const int max_work_ if (max_work_size && tile_work_size > max_work_size) { /* The work did not fit into the requested limit of the work size. Unschedule the tile, - * allowing others (or ourselves later one) to pick it up. - * - * TODO: Such temporary decrement is not ideal, since it might lead to situation when another - * device sees there is nothing to be done, finishing its work and leaving all work to be - * done by us. */ - atomic_fetch_and_add_int32(&next_work_index_, -1); + * so it can be picked up again later. */ + next_work_index_--; return false; } From 3065d2609700d14100490a16c91152a6e71790e8 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 17 Oct 2021 20:43:06 +0200 Subject: [PATCH 0899/1500] Cycles: optimize volume stack copying for shadow catcher/compaction Only copy the number of items used instead of the max items. Ref D12889 --- intern/cycles/kernel/device/gpu/kernel.h | 2 +- .../kernel/integrator/integrator_state_util.h | 56 +++++++++++++------ 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index 56beaf1fd91..b5ecab2a4db 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -321,7 +321,7 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_B const int from_state = active_terminated_states[active_states_offset + global_index]; const int to_state = active_terminated_states[terminated_states_offset + global_index]; - integrator_state_move(to_state, from_state); + integrator_state_move(NULL, to_state, from_state); } } diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index fee59e451d9..bb372f9e984 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -173,6 +173,25 @@ ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(KernelG } } +ccl_device_forceinline void integrator_state_copy_volume_stack(KernelGlobals kg, + IntegratorState to_state, + ConstIntegratorState state) +{ + if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { + int index = 0; + int shader; + do { + shader = INTEGRATOR_STATE_ARRAY(state, volume_stack, index, shader); + + INTEGRATOR_STATE_ARRAY_WRITE(to_state, volume_stack, index, object) = INTEGRATOR_STATE_ARRAY( + state, volume_stack, index, object); + INTEGRATOR_STATE_ARRAY_WRITE(to_state, volume_stack, index, shader) = shader; + + ++index; + } while (shader != OBJECT_NONE); + } +} + ccl_device_forceinline VolumeStack integrator_state_read_shadow_volume_stack(ConstIntegratorState state, int i) { @@ -198,8 +217,9 @@ ccl_device_forceinline void integrator_state_write_shadow_volume_stack(Integrato } #if defined(__KERNEL_GPU__) -ccl_device_inline void integrator_state_copy_only(const IntegratorState to_state, - const IntegratorState state) +ccl_device_inline void integrator_state_copy_only(KernelGlobals kg, + ConstIntegratorState to_state, + ConstIntegratorState state) { int index; @@ -232,7 +252,8 @@ ccl_device_inline void integrator_state_copy_only(const IntegratorState to_state while (index < gpu_array_size) \ ; -# define KERNEL_STRUCT_VOLUME_STACK_SIZE kernel_data.volume_stack_size +/* Don't copy volume stack here, do it after with just the number of items needed. */ +# define KERNEL_STRUCT_VOLUME_STACK_SIZE 0 # include "kernel/integrator/integrator_state_template.h" @@ -242,12 +263,15 @@ ccl_device_inline void integrator_state_copy_only(const IntegratorState to_state # undef KERNEL_STRUCT_END # undef KERNEL_STRUCT_END_ARRAY # undef KERNEL_STRUCT_VOLUME_STACK_SIZE + + integrator_state_copy_volume_stack(kg, to_state, state); } -ccl_device_inline void integrator_state_move(const IntegratorState to_state, - const IntegratorState state) +ccl_device_inline void integrator_state_move(KernelGlobals kg, + ConstIntegratorState to_state, + ConstIntegratorState state) { - integrator_state_copy_only(to_state, state); + integrator_state_copy_only(kg, to_state, state); INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; @@ -264,22 +288,20 @@ ccl_device_inline void integrator_state_shadow_catcher_split(KernelGlobals kg, const IntegratorState to_state = atomic_fetch_and_add_uint32( &kernel_integrator_state.next_shadow_catcher_path_index[0], 1); - integrator_state_copy_only(to_state, state); - - kernel_integrator_state.path.flag[to_state] |= PATH_RAY_SHADOW_CATCHER_PASS; + integrator_state_copy_only(kg, to_state, state); #else - IntegratorStateCPU *ccl_restrict split_state = state + 1; + IntegratorStateCPU *ccl_restrict to_state = state + 1; /* Only copy the required subset, since shadow intersections are big and irrelevant here. */ - split_state->path = state->path; - split_state->ray = state->ray; - split_state->isect = state->isect; - memcpy(split_state->volume_stack, state->volume_stack, sizeof(state->volume_stack)); - split_state->shadow_path = state->shadow_path; - - split_state->path.flag |= PATH_RAY_SHADOW_CATCHER_PASS; + to_state->path = state->path; + to_state->ray = state->ray; + to_state->isect = state->isect; + integrator_state_copy_volume_stack(kg, to_state, state); + to_state->shadow_path = state->shadow_path; #endif + + INTEGRATOR_STATE_WRITE(to_state, path, flag) |= PATH_RAY_SHADOW_CATCHER_PASS; } CCL_NAMESPACE_END From 2430f752797b83cd43892f656f5297fd6e0bb619 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 18 Oct 2021 17:53:32 +0200 Subject: [PATCH 0900/1500] Cycles: reduce GPU state memory a little * isect Ng is no longer needed for shadows, for main path needed for SSS only * Reduce rng_offset and queued_kernel to 16 bits Ref D12889 --- intern/cycles/kernel/bvh/bvh_embree.h | 2 -- .../integrator/integrator_init_from_bake.h | 3 --- .../integrator/integrator_state_template.h | 17 ++++++----------- .../kernel/integrator/integrator_state_util.h | 12 ------------ .../kernel/integrator/integrator_subsurface.h | 4 ++-- .../integrator/integrator_subsurface_disk.h | 2 +- .../integrator_subsurface_random_walk.h | 2 +- intern/cycles/kernel/kernel_types.h | 3 --- 8 files changed, 10 insertions(+), 35 deletions(-) diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/bvh_embree.h index 7fa0cfdc510..4f85e8bee4b 100644 --- a/intern/cycles/kernel/bvh/bvh_embree.h +++ b/intern/cycles/kernel/bvh/bvh_embree.h @@ -107,7 +107,6 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg, Intersection *isect) { isect->t = ray->tfar; - isect->Ng = make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z); if (hit->instID[0] != RTC_INVALID_GEOMETRY_ID) { RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); @@ -142,7 +141,6 @@ ccl_device_inline void kernel_embree_convert_sss_hit( isect->u = 1.0f - hit->v - hit->u; isect->v = hit->u; isect->t = ray->tfar; - isect->Ng = make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z); RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( rtcGetGeometry(kernel_data.bvh.scene, object * 2)); isect->prim = hit->primID + diff --git a/intern/cycles/kernel/integrator/integrator_init_from_bake.h b/intern/cycles/kernel/integrator/integrator_init_from_bake.h index df3c2103c5b..9bc115150ff 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_bake.h +++ b/intern/cycles/kernel/integrator/integrator_init_from_bake.h @@ -180,9 +180,6 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, isect.v = v; isect.t = 1.0f; isect.type = PRIMITIVE_TRIANGLE; -#ifdef __EMBREE__ - isect.Ng = Ng; -#endif integrator_state_write_isect(kg, state, &isect); /* Setup next kernel to execute. */ diff --git a/intern/cycles/kernel/integrator/integrator_state_template.h b/intern/cycles/kernel/integrator/integrator_state_template.h index 0fe47cf13bc..d9801574d4f 100644 --- a/intern/cycles/kernel/integrator/integrator_state_template.h +++ b/intern/cycles/kernel/integrator/integrator_state_template.h @@ -40,13 +40,12 @@ KERNEL_STRUCT_MEMBER(path, uint16_t, volume_bounce, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(path, uint16_t, volume_bounds_bounce, KERNEL_FEATURE_PATH_TRACING) /* Current transparent ray bounce depth. */ KERNEL_STRUCT_MEMBER(path, uint16_t, transparent_bounce, KERNEL_FEATURE_PATH_TRACING) -/* DeviceKernel bit indicating queued kernels. - * TODO: reduce size? */ -KERNEL_STRUCT_MEMBER(path, uint32_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) +/* DeviceKernel bit indicating queued kernels. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) /* Random number generator seed. */ KERNEL_STRUCT_MEMBER(path, uint32_t, rng_hash, KERNEL_FEATURE_PATH_TRACING) /* Random number dimension offset. */ -KERNEL_STRUCT_MEMBER(path, uint32_t, rng_offset, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(path, uint16_t, rng_offset, KERNEL_FEATURE_PATH_TRACING) /* enum PathRayFlag */ KERNEL_STRUCT_MEMBER(path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) /* Multiple importance sampling @@ -89,8 +88,6 @@ KERNEL_STRUCT_MEMBER(isect, float, v, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(isect, int, prim, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(isect, int, object, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(isect, int, type, KERNEL_FEATURE_PATH_TRACING) -/* TODO: exclude for GPU. */ -KERNEL_STRUCT_MEMBER(isect, float3, Ng, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_END(isect) /*************** Subsurface closure state for subsurface kernel ***************/ @@ -99,6 +96,7 @@ KERNEL_STRUCT_BEGIN(subsurface) KERNEL_STRUCT_MEMBER(subsurface, float3, albedo, KERNEL_FEATURE_SUBSURFACE) KERNEL_STRUCT_MEMBER(subsurface, float3, radius, KERNEL_FEATURE_SUBSURFACE) KERNEL_STRUCT_MEMBER(subsurface, float, anisotropy, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_MEMBER(subsurface, float3, Ng, KERNEL_FEATURE_SUBSURFACE) KERNEL_STRUCT_END(subsurface) /********************************** Volume Stack ******************************/ @@ -117,9 +115,8 @@ KERNEL_STRUCT_BEGIN(shadow_path) KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, bounce, KERNEL_FEATURE_PATH_TRACING) /* Current transparent ray bounce depth. */ KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, transparent_bounce, KERNEL_FEATURE_PATH_TRACING) -/* DeviceKernel bit indicating queued kernels. - * TODO: reduce size? */ -KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) +/* DeviceKernel bit indicating queued kernels. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) /* enum PathRayFlag */ KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) /* Throughput. */ @@ -152,8 +149,6 @@ KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, v, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, prim, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, object, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, type, KERNEL_FEATURE_PATH_TRACING) -/* TODO: exclude for GPU. */ -KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float3, Ng, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_END_ARRAY(shadow_isect, INTEGRATOR_SHADOW_ISECT_SIZE_CPU, INTEGRATOR_SHADOW_ISECT_SIZE_GPU) diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index bb372f9e984..18dcdff12ad 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -82,9 +82,6 @@ ccl_device_forceinline void integrator_state_write_isect( INTEGRATOR_STATE_WRITE(state, isect, object) = isect->object; INTEGRATOR_STATE_WRITE(state, isect, prim) = isect->prim; INTEGRATOR_STATE_WRITE(state, isect, type) = isect->type; -#ifdef __EMBREE__ - INTEGRATOR_STATE_WRITE(state, isect, Ng) = isect->Ng; -#endif } ccl_device_forceinline void integrator_state_read_isect( @@ -96,9 +93,6 @@ ccl_device_forceinline void integrator_state_read_isect( isect->u = INTEGRATOR_STATE(state, isect, u); isect->v = INTEGRATOR_STATE(state, isect, v); isect->t = INTEGRATOR_STATE(state, isect, t); -#ifdef __EMBREE__ - isect->Ng = INTEGRATOR_STATE(state, isect, Ng); -#endif } ccl_device_forceinline VolumeStack integrator_state_read_volume_stack(ConstIntegratorState state, @@ -136,9 +130,6 @@ ccl_device_forceinline void integrator_state_write_shadow_isect( INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, object) = isect->object; INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, prim) = isect->prim; INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, type) = isect->type; -#ifdef __EMBREE__ - INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, Ng) = isect->Ng; -#endif } ccl_device_forceinline void integrator_state_read_shadow_isect( @@ -150,9 +141,6 @@ ccl_device_forceinline void integrator_state_read_shadow_isect( isect->u = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, u); isect->v = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, v); isect->t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, t); -#ifdef __EMBREE__ - isect->Ng = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, Ng); -#endif } ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(KernelGlobals kg, diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/integrator_subsurface.h index 448c99765e3..e9517a82453 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface.h @@ -56,7 +56,7 @@ ccl_device int subsurface_bounce(KernelGlobals kg, INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_zero_compact(); /* Pass along object info, reusing isect to save memory. */ - INTEGRATOR_STATE_WRITE(state, isect, Ng) = sd->Ng; + INTEGRATOR_STATE_WRITE(state, subsurface, Ng) = sd->Ng; INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object; uint32_t path_flag = (INTEGRATOR_STATE(state, path, flag) & ~PATH_RAY_CAMERA) | @@ -160,7 +160,7 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat if (object_flag & SD_OBJECT_INTERSECTS_VOLUME) { float3 P = INTEGRATOR_STATE(state, ray, P); - const float3 Ng = INTEGRATOR_STATE(state, isect, Ng); + const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng); const float3 offset_P = ray_offset(P, -Ng); integrator_volume_stack_update_for_subsurface(kg, state, offset_P, ray.P); diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_disk.h b/intern/cycles/kernel/integrator/integrator_subsurface_disk.h index 1de05ea2696..e1cce13fb30 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface_disk.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface_disk.h @@ -45,7 +45,7 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, const float3 P = INTEGRATOR_STATE(state, ray, P); const float ray_dP = INTEGRATOR_STATE(state, ray, dP); const float time = INTEGRATOR_STATE(state, ray, time); - const float3 Ng = INTEGRATOR_STATE(state, isect, Ng); + const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng); const int object = INTEGRATOR_STATE(state, isect, object); /* Read subsurface scattering parameters. */ diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h index 5365093decf..2ab6d0961e3 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h @@ -193,7 +193,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, const float3 N = INTEGRATOR_STATE(state, ray, D); const float ray_dP = INTEGRATOR_STATE(state, ray, dP); const float time = INTEGRATOR_STATE(state, ray, time); - const float3 Ng = INTEGRATOR_STATE(state, isect, Ng); + const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng); const int object = INTEGRATOR_STATE(state, isect, object); /* Sample diffuse surface scatter into the object. */ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index e478019b25c..3e276c24cdd 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -489,9 +489,6 @@ typedef struct Ray { /* Intersection */ typedef struct Intersection { -#ifdef __EMBREE__ - float3 Ng; -#endif float t, u, v; int prim; int object; From a9cb33081506f14ffb693e3b82742a940b6c80b2 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 18 Oct 2021 18:25:56 +0200 Subject: [PATCH 0901/1500] Cleanup: minor refactoring in preparation of main and shadow path decoupling Ref D12889 --- .../integrator/integrator_shade_surface.h | 10 +++++++++- intern/cycles/kernel/kernel_accumulate.h | 17 +++++++++++++---- intern/cycles/kernel/kernel_bake.h | 3 ++- intern/cycles/kernel/kernel_shader.h | 10 ---------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index bbf613a669e..08580645984 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -56,7 +56,7 @@ ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg, if (kernel_data.background.transparent) { const float3 throughput = INTEGRATOR_STATE(state, path, throughput); const float transparent = average(holdout_weight * throughput); - kernel_accum_transparent(kg, state, path_flag, transparent, render_buffer); + kernel_accum_holdout(kg, state, path_flag, transparent, render_buffer); } if (isequal_float3(holdout_weight, one_float3())) { return false; @@ -385,6 +385,14 @@ ccl_device bool integrate_surface(KernelGlobals kg, /* Evaluate shader. */ PROFILING_EVENT(PROFILING_SHADE_SURFACE_EVAL); shader_eval_surface(kg, state, &sd, render_buffer, path_flag); + + /* Initialize additional RNG for BSDFs. */ + if (sd.flag & SD_BSDF_NEEDS_LCG) { + sd.lcg_state = lcg_state_init(INTEGRATOR_STATE(state, path, rng_hash), + INTEGRATOR_STATE(state, path, rng_offset), + INTEGRATOR_STATE(state, path, sample), + 0xb4bc3953); + } } #ifdef __SUBSURFACE__ diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index d4bb1ef8685..5f32150d33c 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -473,10 +473,8 @@ ccl_device_inline void kernel_accum_transparent(KernelGlobals kg, ConstIntegratorState state, const uint32_t path_flag, const float transparent, - ccl_global float *ccl_restrict render_buffer) + ccl_global float *ccl_restrict buffer) { - ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); - if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { kernel_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent); } @@ -484,6 +482,17 @@ ccl_device_inline void kernel_accum_transparent(KernelGlobals kg, kernel_accum_shadow_catcher_transparent_only(kg, path_flag, transparent, buffer); } +/* Write holdout to render buffer. */ +ccl_device_inline void kernel_accum_holdout(KernelGlobals kg, + ConstIntegratorState state, + const uint32_t path_flag, + const float transparent, + ccl_global float *ccl_restrict render_buffer) +{ + ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + kernel_accum_transparent(kg, state, path_flag, transparent, buffer); +} + /* Write background contribution to render buffer. * * Includes transparency, matching kernel_accum_transparent. */ @@ -501,7 +510,7 @@ ccl_device_inline void kernel_accum_background(KernelGlobals kg, const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (is_transparent_background_ray) { - kernel_accum_transparent(kg, state, path_flag, transparent, render_buffer); + kernel_accum_transparent(kg, state, path_flag, transparent, buffer); } else { const int sample = INTEGRATOR_STATE(state, path, sample); diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index 25b8ca55ead..1473e9ba8bf 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -76,7 +76,8 @@ ccl_device void kernel_background_evaluate(KernelGlobals kg, /* Evaluate shader. * This is being evaluated for all BSDFs, so path flag does not contain a specific type. */ const uint32_t path_flag = PATH_RAY_EMISSION; - shader_eval_surface( + shader_eval_surface( kg, INTEGRATOR_STATE_NULL, &sd, NULL, path_flag); float3 color = shader_background_eval(&sd); diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 4eded9039bd..4a5a5309c61 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -625,16 +625,6 @@ ccl_device void shader_eval_surface(KernelGlobals kg, } #endif } - - IF_KERNEL_NODES_FEATURE(BSDF) - { - if (sd->flag & SD_BSDF_NEEDS_LCG) { - sd->lcg_state = lcg_state_init(INTEGRATOR_STATE(state, path, rng_hash), - INTEGRATOR_STATE(state, path, rng_offset), - INTEGRATOR_STATE(state, path, sample), - 0xb4bc3953); - } - } } /* Volume */ From 3a2550114344d328d026f6f04b0fbfd7f7ef95c6 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 18 Oct 2021 16:34:16 +0200 Subject: [PATCH 0902/1500] Fix T92314: Auto naming of the Vertex Group doesn't work for Grease Pencil Not naming the auto-generated vertexgroup after the selected bone was just confusing (since the group would not have an effect), so now use similar code that is used for meshes for greasepencil as well. Maniphest Tasks: T92314 Differential Revision: https://developer.blender.org/D12906 --- .../editors/gpencil/gpencil_weight_paint.c | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c index d14322e12b5..b3249294119 100644 --- a/source/blender/editors/gpencil/gpencil_weight_paint.c +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -29,15 +29,18 @@ #include "BLT_translation.h" +#include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" +#include "BKE_action.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_object_deform.h" #include "BKE_report.h" #include "DNA_meshdata_types.h" @@ -246,7 +249,22 @@ static bool brush_draw_apply(tGP_BrushWeightpaintData *gso, /* need a vertex group */ if (gso->vrgroup == -1) { if (gso->object) { - BKE_object_defgroup_add(gso->object); + Object *ob_armature = BKE_modifiers_is_deformed_by_armature(gso->object); + if ((ob_armature != NULL)) { + Bone *actbone = ((bArmature *)ob_armature->data)->act_bone; + if (actbone != NULL) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob_armature->pose, actbone->name); + if (pchan != NULL) { + bDeformGroup *dg = BKE_object_defgroup_find_name(gso->object, pchan->name); + if (dg == NULL) { + dg = BKE_object_defgroup_add_name(gso->object, pchan->name); + } + } + } + } + else { + BKE_object_defgroup_add(gso->object); + } DEG_relations_tag_update(gso->bmain); gso->vrgroup = 0; } From 9a8badc1e4175fb1765486413ba5bc86575e3ea0 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 18 Oct 2021 12:22:44 -0500 Subject: [PATCH 0903/1500] Cleanup: Rearrange code, rename function Move the static functions higher in the file so they are usabl for an upcoming patch, and make it use clearer names instead of overloading a function name. --- .../blenkernel/intern/attribute_access.cc | 210 +++++++++--------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 289c458c1e4..0658569752c 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -51,8 +51,8 @@ using blender::bke::OutputAttribute; using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray_For_GSpan; -using blender::fn::GVMutableArray_For_GMutableSpan; using blender::fn::GVArray_For_SingleValue; +using blender::fn::GVMutableArray_For_GMutableSpan; namespace blender::bke { @@ -234,6 +234,109 @@ OutputAttribute::~OutputAttribute() } } +static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data, + const CustomDataType data_type, + const int domain_size, + const AttributeInit &initializer) +{ + switch (initializer.type) { + case AttributeInit::Type::Default: { + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + return data != nullptr; + } + case AttributeInit::Type::VArray: { + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + if (data == nullptr) { + return false; + } + const GVArray *varray = static_cast(initializer).varray; + varray->materialize_to_uninitialized(IndexRange(varray->size()), data); + return true; + } + case AttributeInit::Type::MoveArray: { + void *source_data = static_cast(initializer).data; + void *data = CustomData_add_layer( + &custom_data, data_type, CD_ASSIGN, source_data, domain_size); + if (data == nullptr) { + MEM_freeN(source_data); + return false; + } + return true; + } + } + + BLI_assert_unreachable(); + return false; +} + +static void *add_generic_custom_data_layer(CustomData &custom_data, + const CustomDataType data_type, + const eCDAllocType alloctype, + void *layer_data, + const int domain_size, + const AttributeIDRef &attribute_id) +{ + if (attribute_id.is_named()) { + char attribute_name_c[MAX_NAME]; + attribute_id.name().copy(attribute_name_c); + return CustomData_add_layer_named( + &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + } + const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); + return CustomData_add_layer_anonymous( + &custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id); +} + +static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id, + CustomData &custom_data, + const CustomDataType data_type, + const int domain_size, + const AttributeInit &initializer) +{ + switch (initializer.type) { + case AttributeInit::Type::Default: { + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); + return data != nullptr; + } + case AttributeInit::Type::VArray: { + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); + if (data == nullptr) { + return false; + } + const GVArray *varray = static_cast(initializer).varray; + varray->materialize_to_uninitialized(IndexRange(varray->size()), data); + return true; + } + case AttributeInit::Type::MoveArray: { + void *source_data = static_cast(initializer).data; + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id); + if (data == nullptr) { + MEM_freeN(source_data); + return false; + } + return true; + } + } + + BLI_assert_unreachable(); + return false; +} + +static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, + const AttributeIDRef &attribute_id) +{ + if (!attribute_id) { + return false; + } + if (attribute_id.is_anonymous()) { + return layer.anonymous_id == &attribute_id.anonymous_id(); + } + return layer.name == attribute_id.name(); +} + GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( const GeometryComponent &component) const { @@ -296,41 +399,6 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co return delete_success; } -static bool add_custom_data_layer_from_attribute_init(CustomData &custom_data, - const CustomDataType data_type, - const int domain_size, - const AttributeInit &initializer) -{ - switch (initializer.type) { - case AttributeInit::Type::Default: { - void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); - return data != nullptr; - } - case AttributeInit::Type::VArray: { - void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); - if (data == nullptr) { - return false; - } - const GVArray *varray = static_cast(initializer).varray; - varray->materialize_to_uninitialized(IndexRange(varray->size()), data); - return true; - } - case AttributeInit::Type::MoveArray: { - void *source_data = static_cast(initializer).data; - void *data = CustomData_add_layer( - &custom_data, data_type, CD_ASSIGN, source_data, domain_size); - if (data == nullptr) { - MEM_freeN(source_data); - return false; - } - return true; - } - } - - BLI_assert_unreachable(); - return false; -} - bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, const AttributeInit &initializer) const { @@ -347,7 +415,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, } const int domain_size = component.attribute_domain_size(domain_); - const bool success = add_custom_data_layer_from_attribute_init( + const bool success = add_builtin_type_custom_data_layer_from_init( *custom_data, stored_type_, domain_size, initializer); if (success) { custom_data_access_.update_custom_data_pointers(component); @@ -365,18 +433,6 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) return data != nullptr; } -static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, - const AttributeIDRef &attribute_id) -{ - if (!attribute_id) { - return false; - } - if (attribute_id.is_anonymous()) { - return layer.anonymous_id == &attribute_id.anonymous_id(); - } - return layer.name == attribute_id.name(); -} - ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( const GeometryComponent &component, const AttributeIDRef &attribute_id) const { @@ -448,62 +504,6 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, return false; } -static void *add_generic_custom_data_layer(CustomData &custom_data, - const CustomDataType data_type, - const eCDAllocType alloctype, - void *layer_data, - const int domain_size, - const AttributeIDRef &attribute_id) -{ - if (attribute_id.is_named()) { - char attribute_name_c[MAX_NAME]; - attribute_id.name().copy(attribute_name_c); - return CustomData_add_layer_named( - &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); - } - const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); - return CustomData_add_layer_anonymous( - &custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id); -} - -static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id, - CustomData &custom_data, - const CustomDataType data_type, - const int domain_size, - const AttributeInit &initializer) -{ - switch (initializer.type) { - case AttributeInit::Type::Default: { - void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); - return data != nullptr; - } - case AttributeInit::Type::VArray: { - void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); - if (data == nullptr) { - return false; - } - const GVArray *varray = static_cast(initializer).varray; - varray->materialize_to_uninitialized(IndexRange(varray->size()), data); - return true; - } - case AttributeInit::Type::MoveArray: { - void *source_data = static_cast(initializer).data; - void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id); - if (data == nullptr) { - MEM_freeN(source_data); - return false; - } - return true; - } - } - - BLI_assert_unreachable(); - return false; -} - bool CustomDataAttributeProvider::try_create(GeometryComponent &component, const AttributeIDRef &attribute_id, const AttributeDomain domain, From 9f895fbb9708eaa1d70abcd3091d44f262380a8a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 18 Oct 2021 12:31:01 -0500 Subject: [PATCH 0904/1500] Geometry Nodes: Optimize curve builtin attribute exists check Calculating the number of points is overkill here, if there are many splines. The `exists` check just needs to know if there are any points at all. --- .../intern/geometry_component_curve.cc | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 73c628d3f0f..8923521d699 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -1055,7 +1055,29 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu bool exists(const GeometryComponent &component) const final { - return component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; + const CurveEval *curve = get_curve_from_component_for_read(component); + if (curve == nullptr) { + return false; + } + + Span splines = curve->splines(); + if (splines.size() == 0) { + return false; + } + + bool has_point = false; + for (const SplinePtr &spline : curve->splines()) { + if (spline->size() != 0) { + has_point = true; + break; + } + } + + if (!has_point) { + return false; + } + + return true; } }; From ec8a9a0d6586d01b144ce4168e0f57d29ef7f738 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 18 Oct 2021 12:35:27 -0500 Subject: [PATCH 0905/1500] Cleanup: Remove unnecessary constructor argument All attributes should be writeable, it is now only needed for the legacy `normal` attribute on meshes. --- .../blenkernel/intern/geometry_component_curve.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 8923521d699..50c0f06b12c 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -980,16 +980,15 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu public: BuiltinPointAttributeProvider(std::string attribute_name, - const WritableEnum writable, const GetSpan get_span, const GetMutableSpan get_mutable_span, const UpdateOnWrite update_on_write) : BuiltinAttributeProvider(std::move(attribute_name), ATTR_DOMAIN_POINT, bke::cpp_type_to_custom_data_type(CPPType::get()), - BuiltinAttributeProvider::NonCreatable, - writable, - BuiltinAttributeProvider::NonDeletable), + CreatableEnum::NonCreatable, + WritableEnum::Writable, + DeletableEnum::NonDeletable), get_span_(get_span), get_mutable_span_(get_mutable_span), update_on_write_(update_on_write) @@ -1091,7 +1090,6 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider radius( "radius", - BuiltinAttributeProvider::Writable, [](const Spline &spline) { return spline.radii(); }, [](Spline &spline) { return spline.radii(); }, nullptr); static BuiltinPointAttributeProvider tilt( "tilt", - BuiltinAttributeProvider::Writable, [](const Spline &spline) { return spline.tilts(); }, [](Spline &spline) { return spline.tilts(); }, [](Spline &spline) { spline.mark_cache_invalid(); }); From 5f59bf00444bf310d0ad0dd20039839912af5122 Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Mon, 18 Oct 2021 15:11:04 -0500 Subject: [PATCH 0906/1500] Geometry Nodes: Sort Children in Collection Info When the 'Separate Children' option is selected, the children of the selected collection are inserted into the geometry output sorted alphabetically by name. One item to note is that the rename function does not trigger a depsgraph update. This means that the changes are not reflected in the spreadsheet until another action triggers the update. Differential Revision: https://developer.blender.org/D12907 --- .../nodes/node_geo_collection_info.cc | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index d03221703f0..eca4e3d2d14 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -25,13 +25,16 @@ #include "node_geometry_util.hh" +#include + namespace blender::nodes { static void geo_node_collection_info_declare(NodeDeclarationBuilder &b) { b.add_input("Collection").hide_label(); b.add_input("Separate Children") - .description("Output each child of the collection as a separate instance"); + .description( + "Output each child of the collection as a separate instance, sorted alphabetically"); b.add_input("Reset Children") .description( "Reset the transforms of every child instance in the output. Only used when Separate " @@ -52,6 +55,12 @@ static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *n node->storage = data; } +struct InstanceListEntry { + int handle; + char *name; + float4x4 transform; +}; + static void geo_node_collection_info_exec(GeoNodeExecParams params) { Collection *collection = params.get_input("Collection"); @@ -85,6 +94,8 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) } instances.reserve(children_collections.size() + children_objects.size()); + Vector entries; + entries.reserve(children_collections.size() + children_objects.size()); for (Collection *child_collection : children_collections) { float4x4 transform = float4x4::identity(); @@ -98,7 +109,7 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) } } const int handle = instances.add_reference(*child_collection); - instances.add_instance(handle, transform); + entries.append({handle, &(child_collection->id.name[2]), transform}); } for (Object *child_object : children_objects) { const int handle = instances.add_reference(*child_object); @@ -112,7 +123,16 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) } mul_m4_m4_post(transform.values, child_object->obmat); } - instances.add_instance(handle, transform); + entries.append({handle, &(child_object->id.name[2]), transform}); + } + + std::sort(entries.begin(), + entries.end(), + [](const InstanceListEntry &a, const InstanceListEntry &b) { + return BLI_strcasecmp_natural(a.name, b.name) <= 0; + }); + for (const InstanceListEntry &entry : entries) { + instances.add_instance(entry.handle, entry.transform); } } else { From 06356115b71fb0668b0b67735f92ba6660434a96 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 18 Oct 2021 22:20:25 +0200 Subject: [PATCH 0907/1500] VSE: Don't draw thumbnails while rendering During rendering VSE cache is invalidated, so thumbnails would be removed and thumbnail job would constantly restart. Even if thumbnails would be preserved, resources should be dedicated for rendering job. --- .../space_sequencer/sequencer_thumbnails.c | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.c b/source/blender/editors/space_sequencer/sequencer_thumbnails.c index 0ea952d0999..557c233744e 100644 --- a/source/blender/editors/space_sequencer/sequencer_thumbnails.c +++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.c @@ -291,20 +291,26 @@ static void sequencer_thumbnail_start_job_if_necessary(const bContext *C, return; } - /* `thumbnail_is_missing` should be set to true if missing image in strip. False when normal call - * to all strips done. */ - if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || - v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax || thumbnail_is_missing) { - - /* Stop the job first as view has changed. Pointless to continue old job. */ - if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || - v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax) { - WM_jobs_stop(CTX_wm_manager(C), NULL, thumbnail_start_job); - } - - sequencer_thumbnail_init_job(C, v2d, ed); - sseq->runtime.last_thumbnail_area = v2d->cur; + /* During rendering, cache is wiped, it doesn't make sense to render thumbnails. */ + if (G.is_rendering) { + return; } + + /* Job start requested, but over area which has been processed. Unless `thumbnail_is_missing` is + * true, ignore this request as all images are in view. */ + if (v2d->cur.xmax == sseq->runtime.last_thumbnail_area.xmax && + v2d->cur.ymax == sseq->runtime.last_thumbnail_area.ymax && !thumbnail_is_missing) { + return; + } + + /* Stop the job first as view has changed. Pointless to continue old job. */ + if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || + v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax) { + WM_jobs_stop(CTX_wm_manager(C), NULL, thumbnail_start_job); + } + + sequencer_thumbnail_init_job(C, v2d, ed); + sseq->runtime.last_thumbnail_area = v2d->cur; } void last_displayed_thumbnails_list_free(void *val) From 41eba47a877e3420246dfe2971eb6bbba2e39a17 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 18 Oct 2021 22:34:09 +0200 Subject: [PATCH 0908/1500] Revert "Cycles: optimize volume stack copying for shadow catcher/compaction" This reverts commit 3065d2609700d14100490a16c91152a6e71790e8. Causing crashes in the spring scene. --- intern/cycles/kernel/integrator/integrator_state_util.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index 18dcdff12ad..dacc21e6eeb 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -240,8 +240,7 @@ ccl_device_inline void integrator_state_copy_only(KernelGlobals kg, while (index < gpu_array_size) \ ; -/* Don't copy volume stack here, do it after with just the number of items needed. */ -# define KERNEL_STRUCT_VOLUME_STACK_SIZE 0 +# define KERNEL_STRUCT_VOLUME_STACK_SIZE kernel_data.volume_stack_size # include "kernel/integrator/integrator_state_template.h" @@ -251,8 +250,6 @@ ccl_device_inline void integrator_state_copy_only(KernelGlobals kg, # undef KERNEL_STRUCT_END # undef KERNEL_STRUCT_END_ARRAY # undef KERNEL_STRUCT_VOLUME_STACK_SIZE - - integrator_state_copy_volume_stack(kg, to_state, state); } ccl_device_inline void integrator_state_move(KernelGlobals kg, From 3a898db3638d2ab13b4aa85e0dcce1fe35ae8695 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 18 Oct 2021 23:13:05 +0200 Subject: [PATCH 0909/1500] VSE: Improve thumbnail loading speed Split thumbnail job in 2 passes. First pass will render visible images and second part renders set of "guaranteed" equally spaced images. When viewing larger amount of strips, it is likely that only 1 or 2 images will be rendered in first pass, while in second pass it is up to 30 images. This results (seemingly) in 3x better performance, but zooming before set of guaranteed images is done will be slightly more inaccurate. --- .../space_sequencer/sequencer_thumbnails.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.c b/source/blender/editors/space_sequencer/sequencer_thumbnails.c index 557c233744e..06db96badc2 100644 --- a/source/blender/editors/space_sequencer/sequencer_thumbnails.c +++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.c @@ -165,6 +165,8 @@ static void thumbnail_start_job(void *data, float start_frame, frame_step; GHashIterator gh_iter; + + /* First pass: render visible images. */ BLI_ghashIterator_init(&gh_iter, tj->sequences_ghash); while (!BLI_ghashIterator_done(&gh_iter) & !*stop) { Sequence *seq_orig = BLI_ghashIterator_getKey(&gh_iter); @@ -176,6 +178,21 @@ static void thumbnail_start_job(void *data, start_frame = seq_thumbnail_get_start_frame(seq_orig, frame_step, tj->view_area); SEQ_render_thumbnails( &tj->context, val->seq_dupli, seq_orig, start_frame, frame_step, tj->view_area, stop); + SEQ_relations_sequence_free_anim(val->seq_dupli); + } + BLI_ghashIterator_step(&gh_iter); + } + + /* Second pass: render "guaranteed" set of images. */ + BLI_ghashIterator_init(&gh_iter, tj->sequences_ghash); + while (!BLI_ghashIterator_done(&gh_iter) & !*stop) { + Sequence *seq_orig = BLI_ghashIterator_getKey(&gh_iter); + ThumbDataItem *val = BLI_ghash_lookup(tj->sequences_ghash, seq_orig); + + if (check_seq_need_thumbnails(seq_orig, tj->view_area)) { + seq_get_thumb_image_dimensions( + val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, NULL, NULL, NULL); + start_frame = seq_thumbnail_get_start_frame(seq_orig, frame_step, tj->view_area); SEQ_render_thumbnails_base_set(&tj->context, val->seq_dupli, seq_orig, tj->view_area, stop); SEQ_relations_sequence_free_anim(val->seq_dupli); } From d7b231baa87e4d89c519059bad96c07163453724 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 18 Oct 2021 18:38:31 -0300 Subject: [PATCH 0910/1500] Cleanup: remove unused member and rearrange function --- source/blender/editors/include/ED_view3d.h | 3 +- .../editors/space_view3d/view3d_cursor_snap.c | 31 +++++++++---------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 593d1be9444..4439ecfdd97 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -267,7 +267,6 @@ typedef struct V3DSnapCursorData { short snap_elem; float loc[3]; float nor[3]; - float face_nor[3]; float obmat[4][4]; int elem_index[3]; float plane_omat[3][3]; @@ -282,11 +281,11 @@ void ED_view3d_cursor_snap_activate_point(void); void ED_view3d_cursor_snap_activate_plane(void); void ED_view3d_cursor_snap_deactivate_point(void); void ED_view3d_cursor_snap_deactivate_plane(void); +void ED_view3d_cursor_snap_prevpoint_set(const float prev_point[3]); void ED_view3d_cursor_snap_update(const struct bContext *C, const int x, const int y, V3DSnapCursorData *snap_data); -void ED_view3d_cursor_snap_prevpoint_set(const float prev_point[3]); struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *scene); void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d, const float loc_prev[3], diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 5a7d8113528..590f0b4563a 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -698,7 +698,6 @@ static void v3d_cursor_snap_update(const bContext *C, snap_data->snap_elem = snap_elem; copy_v3_v3(snap_data->loc, co); copy_v3_v3(snap_data->nor, no); - copy_v3_v3(snap_data->face_nor, face_nor); copy_m4_m4(snap_data->obmat, obmat); copy_v3_v3_int(snap_data->elem_index, snap_elem_index); @@ -903,6 +902,21 @@ void ED_view3d_cursor_snap_deactivate_plane(void) v3d_cursor_snap_free(sdata_intern); } +void ED_view3d_cursor_snap_prevpoint_set(const float prev_point[3]) +{ + SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); + if (!sdata_intern) { + return; + } + if (prev_point) { + copy_v3_v3(sdata_intern->prevpoint_stack, prev_point); + sdata_intern->snap_data.prevpoint = sdata_intern->prevpoint_stack; + } + else { + sdata_intern->snap_data.prevpoint = NULL; + } +} + void ED_view3d_cursor_snap_update(const bContext *C, const int x, const int y, @@ -934,21 +948,6 @@ void ED_view3d_cursor_snap_update(const bContext *C, } } -void ED_view3d_cursor_snap_prevpoint_set(const float prev_point[3]) -{ - SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); - if (!sdata_intern) { - return; - } - if (prev_point) { - copy_v3_v3(sdata_intern->prevpoint_stack, prev_point); - sdata_intern->snap_data.prevpoint = sdata_intern->prevpoint_stack; - } - else { - sdata_intern->snap_data.prevpoint = NULL; - } -} - struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *scene) { SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); From c1d138dd926c1d59be1a77576636cd72c512ab6a Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Mon, 18 Oct 2021 19:08:02 -0500 Subject: [PATCH 0911/1500] Fix T91979: Don't mark string nodes as supporting fields Even though these nodes do support fields internally, there are no input string sockets that support fields currently, so removing the diamond sockets for now helps avoid confusion. Differential Revision: https://developer.blender.org/D12828 --- source/blender/nodes/function/nodes/node_fn_string_length.cc | 1 - source/blender/nodes/function/nodes/node_fn_string_substring.cc | 1 - source/blender/nodes/function/nodes/node_fn_value_to_string.cc | 1 - 3 files changed, 3 deletions(-) diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc index 89038629c3c..a0f85dfd2bf 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_length.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc @@ -24,7 +24,6 @@ namespace blender::nodes { static void fn_node_string_length_declare(NodeDeclarationBuilder &b) { - b.is_function_node(); b.add_input("String"); b.add_output("Length"); }; diff --git a/source/blender/nodes/function/nodes/node_fn_string_substring.cc b/source/blender/nodes/function/nodes/node_fn_string_substring.cc index b91171923d6..55a01093ae9 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_substring.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_substring.cc @@ -22,7 +22,6 @@ namespace blender::nodes { static void fn_node_string_substring_declare(NodeDeclarationBuilder &b) { - b.is_function_node(); b.add_input("String"); b.add_input("Position"); b.add_input("Length").min(0); diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc index 56206af2eb2..c1e6373cb6d 100644 --- a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc @@ -21,7 +21,6 @@ namespace blender::nodes { static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b) { - b.is_function_node(); b.add_input("Value"); b.add_input("Decimals").min(0); b.add_output("String"); From 482c5f001449f6bbb57c4d01363600879607099e Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Mon, 18 Oct 2021 19:01:18 -0600 Subject: [PATCH 0912/1500] Fix: Build error with MSVC noise.cc uses std::min and std::max without including the algorithm header required. Newer MSVC versions and GCC implicitly include it somewhere, which isn't something we should count on. Best to include what you use. --- source/blender/blenlib/intern/noise.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index 812e6ddd181..58b6bb638e9 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -46,6 +46,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include #include #include From a3457704fb63a59045b093dc4499b43f6676fabb Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 18 Oct 2021 20:13:37 -0500 Subject: [PATCH 0913/1500] Geometry Nodes: De-duplicate index input nodes during evaluation We do this in other nodes to reduce overhead of using the same node more than once. I don't think it will make a difference with index nodes currently, but at least it's consistent. --- source/blender/functions/FN_field.hh | 3 +++ source/blender/functions/intern/field.cc | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 2fca78fa6e7..78a49e342a5 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -421,6 +421,9 @@ class IndexFieldInput final : public FieldInput { const GVArray *get_varray_for_context(const FieldContext &context, IndexMask mask, ResourceScope &scope) const final; + + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; }; /** \} */ diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 03af3f53065..1f7bad134a8 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -530,6 +530,17 @@ const GVArray *IndexFieldInput::get_varray_for_context(const fn::FieldContext &U mask.min_array_size(), mask.min_array_size(), index_func); } +uint64_t IndexFieldInput::hash() const +{ + /* Some random constant hash. */ + return 128736487678; +} + +bool IndexFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + return dynamic_cast(&other) != nullptr; +} + /* -------------------------------------------------------------------- * FieldOperation. */ From b74f2c7d74dc18ab9afa105a2cfe547fabb42d57 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 19 Oct 2021 12:22:06 +1100 Subject: [PATCH 0914/1500] Fix image cache margin calculation This margin was inconsistently calculated: only taking the visible region and interface scale into account in some cases. --- source/blender/editors/include/ED_image.h | 9 ++++++--- source/blender/editors/space_image/image_draw.c | 13 ++++++++++++- source/blender/editors/space_image/image_edit.c | 4 ++-- source/blender/editors/space_image/image_ops.c | 9 ++------- source/blender/editors/util/ed_util_imbuf.c | 3 +-- source/blender/editors/uvedit/uvedit_ops.c | 8 +++----- 6 files changed, 26 insertions(+), 20 deletions(-) diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index f0e8f7f0a39..1400bcf5ee3 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -53,13 +53,13 @@ float ED_space_image_increment_snap_value(const int grid_dimesnions, const float zoom_factor); /* image_edit.c, exported for transform */ -struct Image *ED_space_image(struct SpaceImage *sima); +struct Image *ED_space_image(const struct SpaceImage *sima); void ED_space_image_set(struct Main *bmain, struct SpaceImage *sima, struct Image *ima, bool automatic); void ED_space_image_auto_set(const struct bContext *C, struct SpaceImage *sima); -struct Mask *ED_space_image_get_mask(struct SpaceImage *sima); +struct Mask *ED_space_image_get_mask(const struct SpaceImage *sima); void ED_space_image_set_mask(struct bContext *C, struct SpaceImage *sima, struct Mask *mask); bool ED_space_image_get_position(struct SpaceImage *sima, @@ -136,7 +136,10 @@ void ED_image_draw_info(struct Scene *scene, const int *zp, const float *zpf); -bool ED_space_image_show_cache(struct SpaceImage *sima); +bool ED_space_image_show_cache(const struct SpaceImage *sima); +bool ED_space_image_show_cache_and_mval_over(const struct SpaceImage *sima, + struct ARegion *region, + const int mval[2]); bool ED_image_should_save_modified(const struct Main *bmain); int ED_image_save_all_modified_info(const struct Main *bmain, struct ReportList *reports); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index fb87c54c1db..22a43ea3794 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -500,7 +500,7 @@ void draw_image_main_helpers(const bContext *C, ARegion *region) } } -bool ED_space_image_show_cache(SpaceImage *sima) +bool ED_space_image_show_cache(const SpaceImage *sima) { Image *image = ED_space_image(sima); Mask *mask = NULL; @@ -516,6 +516,17 @@ bool ED_space_image_show_cache(SpaceImage *sima) return true; } +bool ED_space_image_show_cache_and_mval_over(const SpaceImage *sima, + ARegion *region, + const int mval[2]) +{ + const rcti *rect_visible = ED_region_visible_rect(region); + if (mval[1] > rect_visible->ymin + (16 * UI_DPI_FAC)) { + return false; + } + return ED_space_image_show_cache(sima); +} + void draw_image_cache(const bContext *C, ARegion *region) { SpaceImage *sima = CTX_wm_space_image(C); diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 594baf67aaf..c1aa2da9e00 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -52,7 +52,7 @@ #include "WM_types.h" /* NOTE: image_panel_properties() uses pointer to sima->image directly. */ -Image *ED_space_image(SpaceImage *sima) +Image *ED_space_image(const SpaceImage *sima) { return sima->image; } @@ -113,7 +113,7 @@ void ED_space_image_auto_set(const bContext *C, SpaceImage *sima) } } -Mask *ED_space_image_get_mask(SpaceImage *sima) +Mask *ED_space_image_get_mask(const SpaceImage *sima) { return sima->mask_info.mask; } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 94d44e047a4..0dbcb1885c2 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -3628,13 +3628,8 @@ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event ARegion *region = CTX_wm_region(C); if (region->regiontype == RGN_TYPE_WINDOW) { - SpaceImage *sima = CTX_wm_space_image(C); - - /* Local coordinate visible rect inside region, to accommodate overlapping ui. */ - const rcti *rect_visible = ED_region_visible_rect(region); - const int region_bottom = rect_visible->ymin; - - if (event->mval[1] > (region_bottom + 16 * UI_DPI_FAC) || !ED_space_image_show_cache(sima)) { + const SpaceImage *sima = CTX_wm_space_image(C); + if (!ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) { return OPERATOR_PASS_THROUGH; } } diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index ca21f88b230..159826568d1 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -491,9 +491,8 @@ int ED_imbuf_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (sa && sa->spacetype == SPACE_IMAGE) { SpaceImage *sima = CTX_wm_space_image(C); - if (region->regiontype == RGN_TYPE_WINDOW) { - if (event->mval[1] <= 16 && ED_space_image_show_cache(sima)) { + if (ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) { return OPERATOR_PASS_THROUGH; } } diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 0757e177235..acdffd5ff98 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -1765,11 +1765,9 @@ static int uv_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *e float location[2]; if (region->regiontype == RGN_TYPE_WINDOW) { - if (event->mval[1] <= 16) { - SpaceImage *sima = CTX_wm_space_image(C); - if (sima && ED_space_image_show_cache(sima)) { - return OPERATOR_PASS_THROUGH; - } + SpaceImage *sima = CTX_wm_space_image(C); + if (sima && ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) { + return OPERATOR_PASS_THROUGH; } } From c4f733a76cd7b99d3a47578eadc83ce064551916 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 19 Oct 2021 12:47:47 +1100 Subject: [PATCH 0915/1500] Fix memory leak in sample tool When there was no image buffer, sample leaked memory. --- source/blender/editors/util/ed_util_imbuf.c | 56 ++++++++++++--------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index 159826568d1..7f1a53cc1e8 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -312,7 +312,6 @@ static void sequencer_sample_apply(bContext *C, wmOperator *op, const wmEvent *e float fx, fy; if (ibuf == NULL) { - IMB_freeImBuf(ibuf); info->draw = 0; return; } @@ -387,13 +386,19 @@ static void sequencer_sample_apply(bContext *C, wmOperator *op, const wmEvent *e static void ed_imbuf_sample_apply(bContext *C, wmOperator *op, const wmEvent *event) { ScrArea *sa = CTX_wm_area(C); - - if (sa && sa->spacetype == SPACE_IMAGE) { - image_sample_apply(C, op, event); + if (sa == NULL) { + return; } - if (sa && sa->spacetype == SPACE_SEQ) { - sequencer_sample_apply(C, op, event); + switch (sa->spacetype) { + case SPACE_IMAGE: { + image_sample_apply(C, op, event); + break; + } + case SPACE_SEQ: { + sequencer_sample_apply(C, op, event); + break; + } } } @@ -477,9 +482,29 @@ void ED_imbuf_sample_exit(bContext *C, wmOperator *op) int ED_imbuf_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *region = CTX_wm_region(C); - ImageSampleInfo *info; + ScrArea *sa = CTX_wm_area(C); + if (sa) { + switch (sa->spacetype) { + case SPACE_IMAGE: { + SpaceImage *sima = sa->spacedata.first; + if (region->regiontype == RGN_TYPE_WINDOW) { + if (ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) { + return OPERATOR_PASS_THROUGH; + } + } + if (!ED_space_image_has_buffer(sima)) { + return OPERATOR_CANCELLED; + } + break; + } + case SPACE_SEQ: { + /* Sequencer checks could be added. */ + break; + } + } + } - info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); + ImageSampleInfo *info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); info->art = region->type; info->draw_handle = ED_region_draw_cb_activate( @@ -487,21 +512,6 @@ int ED_imbuf_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) info->sample_size = RNA_int_get(op->ptr, "size"); op->customdata = info; - ScrArea *sa = CTX_wm_area(C); - - if (sa && sa->spacetype == SPACE_IMAGE) { - SpaceImage *sima = CTX_wm_space_image(C); - if (region->regiontype == RGN_TYPE_WINDOW) { - if (ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) { - return OPERATOR_PASS_THROUGH; - } - } - - if (!ED_space_image_has_buffer(sima)) { - return OPERATOR_CANCELLED; - } - } - ed_imbuf_sample_apply(C, op, event); WM_event_add_modal_handler(C, op); From da949c357405ad01b00e83177335f55d113a8619 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 19 Oct 2021 13:20:48 +1100 Subject: [PATCH 0916/1500] Fix crash on file load in unregistering bke::AssetLibraryService Use mutable iterator to support callbacks removing themselves. --- source/blender/blenkernel/intern/callbacks.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/callbacks.c b/source/blender/blenkernel/intern/callbacks.c index 87d5961b12e..7fd3048b740 100644 --- a/source/blender/blenkernel/intern/callbacks.c +++ b/source/blender/blenkernel/intern/callbacks.c @@ -35,10 +35,9 @@ void BKE_callback_exec(struct Main *bmain, const int num_pointers, eCbEvent evt) { + /* Use mutable iteration so handlers are able to remove themselves. */ ListBase *lb = &callback_slots[evt]; - bCallbackFuncStore *funcstore; - - for (funcstore = lb->first; funcstore; funcstore = funcstore->next) { + LISTBASE_FOREACH_MUTABLE (bCallbackFuncStore *, funcstore, lb) { funcstore->func(bmain, pointers, num_pointers, funcstore->arg); } } From a76bb1a27782317449c04c0ab4916d66c46b6803 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 18 Oct 2021 22:15:52 -0500 Subject: [PATCH 0917/1500] BLI: Support removing keys from a set during iteration This adds the ability to mark slots as removed while iterating through a mutable set. Differential Revision: https://developer.blender.org/D12867 --- source/blender/blenlib/BLI_set.hh | 22 +++++++++++++++++ source/blender/blenlib/tests/BLI_set_test.cc | 26 ++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh index a8ccf957f6c..2e0dfea70e9 100644 --- a/source/blender/blenlib/BLI_set.hh +++ b/source/blender/blenlib/BLI_set.hh @@ -423,6 +423,8 @@ class Set { int64_t total_slots_; int64_t current_slot_; + friend Set; + public: Iterator(const Slot *slots, int64_t total_slots, int64_t current_slot) : slots_(slots), total_slots_(total_slots), current_slot_(current_slot) @@ -467,6 +469,12 @@ class Set { { return !(a != b); } + + protected: + const Slot ¤t_slot() const + { + return slots_[current_slot_]; + } }; Iterator begin() const @@ -484,6 +492,20 @@ class Set { return Iterator(slots_.data(), slots_.size(), slots_.size()); } + /** + * Remove the key that the iterator is currently pointing at. It is valid to call this method + * while iterating over the set. However, after this method has been called, the removed element + * must not be accessed anymore. + */ + void remove(const Iterator &iterator) + { + /* The const cast is valid because this method itself is not const. */ + Slot &slot = const_cast(iterator.current_slot()); + BLI_assert(slot.is_occupied()); + slot.remove(); + removed_slots_++; + } + /** * Print common statistics like size and collision count. This is useful for debugging purposes. */ diff --git a/source/blender/blenlib/tests/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc index 3a4733a218f..28eb4df2ca6 100644 --- a/source/blender/blenlib/tests/BLI_set_test.cc +++ b/source/blender/blenlib/tests/BLI_set_test.cc @@ -544,6 +544,32 @@ TEST(set, GenericAlgorithms) EXPECT_EQ(std::count(set.begin(), set.end(), 20), 1); } +TEST(set, RemoveDuringIteration) +{ + Set set; + set.add(6); + set.add(5); + set.add(2); + set.add(3); + + EXPECT_EQ(set.size(), 4); + + using Iter = Set::Iterator; + Iter begin = set.begin(); + Iter end = set.end(); + for (Iter iter = begin; iter != end; ++iter) { + int item = *iter; + if (item == 2) { + set.remove(iter); + } + } + + EXPECT_EQ(set.size(), 3); + EXPECT_TRUE(set.contains(5)); + EXPECT_TRUE(set.contains(6)); + EXPECT_TRUE(set.contains(3)); +} + /** * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. */ From 695dc07cb12222678f035537c554bee39d4d7d23 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 19 Oct 2021 18:31:06 +1100 Subject: [PATCH 0918/1500] Cleanup: clang-format --- intern/cycles/kernel/svm/svm_aov.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h index aba5bdc0113..a18567fdc3c 100644 --- a/intern/cycles/kernel/svm/svm_aov.h +++ b/intern/cycles/kernel/svm/svm_aov.h @@ -18,7 +18,8 @@ CCL_NAMESPACE_BEGIN -ccl_device_inline bool svm_node_aov_check(const uint32_t path_flag, ccl_global float *render_buffer) +ccl_device_inline bool svm_node_aov_check(const uint32_t path_flag, + ccl_global float *render_buffer) { bool is_primary = (path_flag & PATH_RAY_CAMERA) && (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)); From 9c8255d486c1271f20d33debf2a9e9ce883b5019 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 19 Oct 2021 18:33:42 +1100 Subject: [PATCH 0919/1500] Cleanup: use 'e' prefix for enum types --- source/blender/blenkernel/BKE_main.h | 6 +++--- source/blender/blenkernel/BKE_report.h | 20 +++++++++---------- source/blender/blenkernel/BKE_undo_system.h | 18 ++++++++--------- source/blender/blenkernel/intern/main.c | 2 +- source/blender/blenkernel/intern/report.c | 20 +++++++++---------- .../blender/blenkernel/intern/undo_system.c | 14 ++++++------- source/blender/blenlib/BLI_task.h | 12 +++++------ source/blender/blenlib/intern/task_pool.cc | 14 ++++++------- source/blender/blenloader/BLO_read_write.h | 4 ++-- source/blender/blenloader/intern/readfile.c | 2 +- source/blender/blenloader/intern/readfile.h | 2 +- source/blender/editors/mesh/editmesh_utils.c | 2 +- source/blender/editors/undo/ed_undo.c | 2 +- .../makesdna/DNA_windowmanager_types.h | 10 +++++----- source/blender/windowmanager/WM_api.h | 12 +++++------ .../windowmanager/intern/wm_event_system.c | 4 ++-- .../blender/windowmanager/intern/wm_window.c | 2 +- 17 files changed, 73 insertions(+), 73 deletions(-) diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 3108dfd5d8f..68b1b55f47f 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -90,12 +90,12 @@ typedef struct MainIDRelationsEntry { } MainIDRelationsEntry; /* MainIDRelationsEntry.tags */ -typedef enum MainIDRelationsEntryTags { +typedef enum eMainIDRelationsEntryTags { /* Generic tag marking the entry as to be processed. */ MAINIDRELATIONS_ENTRY_TAGS_DOIT = 1 << 0, /* Generic tag marking the entry as processed. */ MAINIDRELATIONS_ENTRY_TAGS_PROCESSED = 1 << 1, -} MainIDRelationsEntryTags; +} eMainIDRelationsEntryTags; typedef struct MainIDRelations { /* Mapping from an ID pointer to all of its parents (IDs using it) and children (IDs it uses). @@ -209,7 +209,7 @@ void BKE_main_unlock(struct Main *bmain); void BKE_main_relations_create(struct Main *bmain, const short flag); void BKE_main_relations_free(struct Main *bmain); void BKE_main_relations_tag_set(struct Main *bmain, - const MainIDRelationsEntryTags tag, + const eMainIDRelationsEntryTags tag, const bool value); struct GSet *BKE_main_gset_create(struct Main *bmain, struct GSet *gset); diff --git a/source/blender/blenkernel/BKE_report.h b/source/blender/blenkernel/BKE_report.h index 5b22918e84c..ec2e8d0f875 100644 --- a/source/blender/blenkernel/BKE_report.h +++ b/source/blender/blenkernel/BKE_report.h @@ -40,27 +40,27 @@ extern "C" { void BKE_reports_init(ReportList *reports, int flag); void BKE_reports_clear(ReportList *reports); -void BKE_report(ReportList *reports, ReportType type, const char *message); -void BKE_reportf(ReportList *reports, ReportType type, const char *format, ...) +void BKE_report(ReportList *reports, eReportType type, const char *message); +void BKE_reportf(ReportList *reports, eReportType type, const char *format, ...) ATTR_PRINTF_FORMAT(3, 4); void BKE_reports_prepend(ReportList *reports, const char *prepend); void BKE_reports_prependf(ReportList *reports, const char *prepend, ...) ATTR_PRINTF_FORMAT(2, 3); -ReportType BKE_report_print_level(ReportList *reports); -void BKE_report_print_level_set(ReportList *reports, ReportType level); +eReportType BKE_report_print_level(ReportList *reports); +void BKE_report_print_level_set(ReportList *reports, eReportType level); -ReportType BKE_report_store_level(ReportList *reports); -void BKE_report_store_level_set(ReportList *reports, ReportType level); +eReportType BKE_report_store_level(ReportList *reports); +void BKE_report_store_level_set(ReportList *reports, eReportType level); -char *BKE_reports_string(ReportList *reports, ReportType level); -void BKE_reports_print(ReportList *reports, ReportType level); +char *BKE_reports_string(ReportList *reports, eReportType level); +void BKE_reports_print(ReportList *reports, eReportType level); Report *BKE_reports_last_displayable(ReportList *reports); -bool BKE_reports_contain(ReportList *reports, ReportType level); +bool BKE_reports_contain(ReportList *reports, eReportType level); -const char *BKE_report_type_str(ReportType type); +const char *BKE_report_type_str(eReportType type); bool BKE_report_write_file_fp(FILE *fp, ReportList *reports, const char *header); bool BKE_report_write_file(const char *filepath, ReportList *reports, const char *header); diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h index 2973a432723..2a90211f8e0 100644 --- a/source/blender/blenkernel/BKE_undo_system.h +++ b/source/blender/blenkernel/BKE_undo_system.h @@ -102,11 +102,11 @@ typedef enum eUndoStepDir { STEP_INVALID = 0, } eUndoStepDir; -typedef enum UndoPushReturn { +typedef enum eUndoPushReturn { UNDO_PUSH_RET_FAILURE = 0, UNDO_PUSH_RET_SUCCESS = (1 << 0), UNDO_PUSH_RET_OVERRIDE_CHANGED = (1 << 1), -} UndoPushReturn; +} eUndoPushReturn; typedef void (*UndoTypeForEachIDRefFn)(void *user_data, struct UndoRefID *id_ref); @@ -156,7 +156,7 @@ typedef struct UndoType { } UndoType; /** #UndoType.flag bitflags. */ -typedef enum UndoTypeFlags { +typedef enum eUndoTypeFlags { /** * This undo type `encode` callback needs a valid context, it will fail otherwise. * \note Callback is still supposed to properly deal with a NULL context pointer. @@ -169,7 +169,7 @@ typedef enum UndoTypeFlags { * This is typically used for undo systems that store both before/after states. */ UNDOTYPE_FLAG_DECODE_ACTIVE_STEP = 1 << 1, -} UndoTypeFlags; +} eUndoTypeFlags; /* Expose since we need to perform operations on specific undo types (rarely). */ extern const UndoType *BKE_UNDOSYS_TYPE_IMAGE; @@ -204,11 +204,11 @@ UndoStep *BKE_undosys_step_push_init_with_type(UndoStack *ustack, const UndoType *ut); UndoStep *BKE_undosys_step_push_init(UndoStack *ustack, struct bContext *C, const char *name); -UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, - struct bContext *C, - const char *name, - const UndoType *ut); -UndoPushReturn BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name); +eUndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, + struct bContext *C, + const char *name, + const UndoType *ut); +eUndoPushReturn BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name); UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack, const char *name, diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 583feacc04b..a5de0bc99c8 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -328,7 +328,7 @@ void BKE_main_relations_free(Main *bmain) /** Set or clear given `tag` in all relation entries of given `bmain`. */ void BKE_main_relations_tag_set(struct Main *bmain, - const MainIDRelationsEntryTags tag, + const eMainIDRelationsEntryTags tag, const bool value) { if (bmain->relations == NULL) { diff --git a/source/blender/blenkernel/intern/report.c b/source/blender/blenkernel/intern/report.c index c877ec6b6b0..90e7ca3f11a 100644 --- a/source/blender/blenkernel/intern/report.c +++ b/source/blender/blenkernel/intern/report.c @@ -37,7 +37,7 @@ #include "BKE_global.h" /* G.background only */ #include "BKE_report.h" -const char *BKE_report_type_str(ReportType type) +const char *BKE_report_type_str(eReportType type) { switch (type) { case RPT_DEBUG: @@ -101,7 +101,7 @@ void BKE_reports_clear(ReportList *reports) BLI_listbase_clear(&reports->list); } -void BKE_report(ReportList *reports, ReportType type, const char *_message) +void BKE_report(ReportList *reports, eReportType type, const char *_message) { Report *report; int len; @@ -129,7 +129,7 @@ void BKE_report(ReportList *reports, ReportType type, const char *_message) } } -void BKE_reportf(ReportList *reports, ReportType type, const char *_format, ...) +void BKE_reportf(ReportList *reports, eReportType type, const char *_format, ...) { DynStr *ds; Report *report; @@ -215,7 +215,7 @@ void BKE_reports_prependf(ReportList *reports, const char *_prepend, ...) } } -ReportType BKE_report_print_level(ReportList *reports) +eReportType BKE_report_print_level(ReportList *reports) { if (!reports) { return RPT_ERROR; @@ -224,7 +224,7 @@ ReportType BKE_report_print_level(ReportList *reports) return reports->printlevel; } -void BKE_report_print_level_set(ReportList *reports, ReportType level) +void BKE_report_print_level_set(ReportList *reports, eReportType level) { if (!reports) { return; @@ -233,7 +233,7 @@ void BKE_report_print_level_set(ReportList *reports, ReportType level) reports->printlevel = level; } -ReportType BKE_report_store_level(ReportList *reports) +eReportType BKE_report_store_level(ReportList *reports) { if (!reports) { return RPT_ERROR; @@ -242,7 +242,7 @@ ReportType BKE_report_store_level(ReportList *reports) return reports->storelevel; } -void BKE_report_store_level_set(ReportList *reports, ReportType level) +void BKE_report_store_level_set(ReportList *reports, eReportType level) { if (!reports) { return; @@ -251,7 +251,7 @@ void BKE_report_store_level_set(ReportList *reports, ReportType level) reports->storelevel = level; } -char *BKE_reports_string(ReportList *reports, ReportType level) +char *BKE_reports_string(ReportList *reports, eReportType level) { Report *report; DynStr *ds; @@ -279,7 +279,7 @@ char *BKE_reports_string(ReportList *reports, ReportType level) return cstring; } -void BKE_reports_print(ReportList *reports, ReportType level) +void BKE_reports_print(ReportList *reports, eReportType level) { char *cstring = BKE_reports_string(reports, level); @@ -305,7 +305,7 @@ Report *BKE_reports_last_displayable(ReportList *reports) return NULL; } -bool BKE_reports_contain(ReportList *reports, ReportType level) +bool BKE_reports_contain(ReportList *reports, eReportType level) { Report *report; if (reports != NULL) { diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index db5184edfd2..26d37489e35 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -346,7 +346,7 @@ static bool undosys_stack_push_main(UndoStack *ustack, const char *name, struct CLOG_INFO(&LOG, 1, "'%s'", name); bContext *C_temp = CTX_create(); CTX_data_main_set(C_temp, bmain); - UndoPushReturn ret = BKE_undosys_step_push_with_type( + eUndoPushReturn ret = BKE_undosys_step_push_with_type( ustack, C_temp, name, BKE_UNDOSYS_TYPE_MEMFILE); CTX_free(C_temp); return (ret & UNDO_PUSH_RET_SUCCESS); @@ -500,17 +500,17 @@ UndoStep *BKE_undosys_step_push_init(UndoStack *ustack, bContext *C, const char /** * \param C: Can be NULL from some callers if their encoding function doesn't need it */ -UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, - bContext *C, - const char *name, - const UndoType *ut) +eUndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, + bContext *C, + const char *name, + const UndoType *ut) { BLI_assert((ut->flags & UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE) == 0 || C != NULL); UNDO_NESTED_ASSERT(false); undosys_stack_validate(ustack, false); bool is_not_empty = ustack->step_active != NULL; - UndoPushReturn retval = UNDO_PUSH_RET_FAILURE; + eUndoPushReturn retval = UNDO_PUSH_RET_FAILURE; /* Might not be final place for this to be called - probably only want to call it from some * undo handlers, not all of them? */ @@ -602,7 +602,7 @@ UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, return (retval | UNDO_PUSH_RET_SUCCESS); } -UndoPushReturn BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name) +eUndoPushReturn BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name) { UNDO_NESTED_ASSERT(false); const UndoType *ut = ustack->step_init ? ustack->step_init->type : diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 418db14e2f3..53ac5f7047b 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -62,10 +62,10 @@ int BLI_task_scheduler_num_threads(void); * be launched. */ -typedef enum TaskPriority { +typedef enum eTaskPriority { TASK_PRIORITY_LOW, TASK_PRIORITY_HIGH, -} TaskPriority; +} eTaskPriority; typedef struct TaskPool TaskPool; typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata); @@ -73,21 +73,21 @@ typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata); /* Regular task pool that immediately starts executing tasks as soon as they * are pushed, either on the current or another thread. */ -TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create(void *userdata, eTaskPriority priority); /* Background: always run tasks in a background thread, never immediately * execute them. For running background jobs. */ -TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_background(void *userdata, eTaskPriority priority); /* Background Serial: run tasks one after the other in the background, * without parallelization between the tasks. */ -TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_background_serial(void *userdata, eTaskPriority priority); /* Suspended: don't execute tasks until work_and_wait is called. This is slower * as threads can't immediately start working. But it can be used if the data * structures the threads operate on are not fully initialized until all tasks * are created. */ -TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_suspended(void *userdata, eTaskPriority priority); /* No threads: immediately executes tasks on the same thread. For debugging. */ TaskPool *BLI_task_pool_create_no_threads(void *userdata); diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc index cbb5bf34477..1651d4c2205 100644 --- a/source/blender/blenlib/intern/task_pool.cc +++ b/source/blender/blenlib/intern/task_pool.cc @@ -121,7 +121,7 @@ class Task { #ifdef WITH_TBB class TBBTaskGroup : public tbb::task_group { public: - TBBTaskGroup(TaskPriority priority) + TBBTaskGroup(eTaskPriority priority) { # if TBB_INTERFACE_VERSION_MAJOR >= 12 /* TODO: support priorities in TBB 2021, where they are only available as @@ -186,7 +186,7 @@ void Task::operator()() const * Tasks may be suspended until in all are created, to make it possible to * initialize data structures and create tasks in a single pass. */ -static void tbb_task_pool_create(TaskPool *pool, TaskPriority priority) +static void tbb_task_pool_create(TaskPool *pool, eTaskPriority priority) { if (pool->type == TASK_POOL_TBB_SUSPENDED) { pool->is_suspended = true; @@ -363,7 +363,7 @@ static void background_task_pool_free(TaskPool *pool) /* Task Pool */ -static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPriority priority) +static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, eTaskPriority priority) { const bool use_threads = BLI_task_scheduler_num_threads() > 1 && type != TASK_POOL_NO_THREADS; @@ -401,7 +401,7 @@ static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPrio /** * Create a normal task pool. Tasks will be executed as soon as they are added. */ -TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create(void *userdata, eTaskPriority priority) { return task_pool_create_ex(userdata, TASK_POOL_TBB, priority); } @@ -418,7 +418,7 @@ TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority) * they could end never being executed, since the 'fallback' background thread is already * busy with parent task in single-threaded context). */ -TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create_background(void *userdata, eTaskPriority priority) { return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority); } @@ -428,7 +428,7 @@ TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority) * for until BLI_task_pool_work_and_wait() is called. This helps reducing threading * overhead when pushing huge amount of small initial tasks from the main thread. */ -TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create_suspended(void *userdata, eTaskPriority priority) { return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority); } @@ -446,7 +446,7 @@ TaskPool *BLI_task_pool_create_no_threads(void *userdata) * Task pool that executes one task after the other, possibly on different threads * but never in parallel. */ -TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create_background_serial(void *userdata, eTaskPriority priority) { return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority); } diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index be5d28c7716..ca7ea6d8f09 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -43,7 +43,7 @@ /* for SDNA_TYPE_FROM_STRUCT() macro */ #include "dna_type_offsets.h" -#include "DNA_windowmanager_types.h" /* for ReportType */ +#include "DNA_windowmanager_types.h" /* for eReportType */ #ifdef __cplusplus extern "C" { @@ -252,7 +252,7 @@ void BLO_expand_id(BlendExpander *expander, struct ID *id); */ void BLO_reportf_wrap(struct BlendFileReadReport *reports, - ReportType type, + eReportType type, const char *format, ...) ATTR_PRINTF_FORMAT(3, 4); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index f82c97443c1..bc50a14ac40 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -222,7 +222,7 @@ typedef struct BHeadN { * bit kludge but better than doubling up on prints, * we could alternatively have a versions of a report function which forces printing - campbell */ -void BLO_reportf_wrap(BlendFileReadReport *reports, ReportType type, const char *format, ...) +void BLO_reportf_wrap(BlendFileReadReport *reports, eReportType type, const char *format, ...) { char fixed_buf[1024]; /* should be long enough */ diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index beeed8e45ae..75152a05063 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -31,7 +31,7 @@ #include "BLI_filereader.h" #include "DNA_sdna_types.h" #include "DNA_space_types.h" -#include "DNA_windowmanager_types.h" /* for ReportType */ +#include "DNA_windowmanager_types.h" /* for eReportType */ struct BLI_mmap_file; struct BLOCacheStorage; diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 97303117dd3..3ba8f601937 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -166,7 +166,7 @@ bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool eBMOpErrorLevel level; while (BMO_error_pop(em->bm, &errmsg, NULL, &level)) { - ReportType type = RPT_INFO; + eReportType type = RPT_INFO; switch (level) { case BMO_ERROR_CANCEL: { changed_was_set = true; diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 22064e04e86..fa722d0646a 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -146,7 +146,7 @@ void ED_undo_push(bContext *C, const char *str) } } - UndoPushReturn push_retval; + eUndoPushReturn push_retval; /* Only apply limit if this is the last undo step. */ if (wm->undo_stack->step_active && (wm->undo_stack->step_active->next == NULL)) { diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index a9016dd4edd..e24d39b61eb 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -59,7 +59,7 @@ struct wmTimer; #define KMAP_MAX_NAME 64 /* keep in sync with 'rna_enum_wm_report_items' in wm_rna.c */ -typedef enum ReportType { +typedef enum eReportType { RPT_DEBUG = (1 << 0), RPT_INFO = (1 << 1), RPT_OPERATOR = (1 << 2), @@ -69,7 +69,7 @@ typedef enum ReportType { RPT_ERROR_INVALID_INPUT = (1 << 6), RPT_ERROR_INVALID_CONTEXT = (1 << 7), RPT_ERROR_OUT_OF_MEMORY = (1 << 8), -} ReportType; +} eReportType; #define RPT_DEBUG_ALL (RPT_DEBUG) #define RPT_INFO_ALL (RPT_INFO) @@ -91,7 +91,7 @@ enum ReportListFlags { # typedef struct Report { struct Report *next, *prev; - /** ReportType. */ + /** eReportType. */ short type; short flag; /** `strlen(message)`, saves some time calculating the word wrap. */ @@ -103,9 +103,9 @@ typedef struct Report { /* saved in the wm, don't remove */ typedef struct ReportList { ListBase list; - /** ReportType. */ + /** eReportType. */ int printlevel; - /** ReportType. */ + /** eReportType. */ int storelevel; int flag; char _pad[4]; diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 4ee514edb3c..eaf32c06aba 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -178,12 +178,12 @@ void WM_opengl_context_dispose(void *context); void WM_opengl_context_activate(void *context); void WM_opengl_context_release(void *context); -/* WM_window_open alignment */ -typedef enum WindowAlignment { +/* #WM_window_open alignment */ +typedef enum eWindowAlignment { WIN_ALIGN_ABSOLUTE = 0, WIN_ALIGN_LOCATION_CENTER, WIN_ALIGN_PARENT_CENTER, -} WindowAlignment; +} eWindowAlignment; struct wmWindow *WM_window_open(struct bContext *C, const char *title, @@ -195,7 +195,7 @@ struct wmWindow *WM_window_open(struct bContext *C, bool toplevel, bool dialog, bool temp, - WindowAlignment alignment); + eWindowAlignment alignment); void WM_window_set_dpi(const wmWindow *win); @@ -372,8 +372,8 @@ void WM_main_remap_editor_id_reference(struct ID *old_id, struct ID *new_id); /* reports */ void WM_report_banner_show(void); void WM_report_banners_cancel(struct Main *bmain); -void WM_report(ReportType type, const char *message); -void WM_reportf(ReportType type, const char *format, ...) ATTR_PRINTF_FORMAT(2, 3); +void WM_report(eReportType type, const char *message); +void WM_reportf(eReportType type, const char *format, ...) ATTR_PRINTF_FORMAT(2, 3); struct wmEvent *wm_event_add_ex(struct wmWindow *win, const struct wmEvent *event_to_add, diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 3ea61812b8a..7e8910644b6 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -804,7 +804,7 @@ static void wm_add_reports(ReportList *reports) } } -void WM_report(ReportType type, const char *message) +void WM_report(eReportType type, const char *message) { ReportList reports; BKE_reports_init(&reports, RPT_STORE); @@ -815,7 +815,7 @@ void WM_report(ReportType type, const char *message) BKE_reports_clear(&reports); } -void WM_reportf(ReportType type, const char *format, ...) +void WM_reportf(eReportType type, const char *format, ...) { va_list args; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 8baf4a0e013..7113beef56e 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -772,7 +772,7 @@ wmWindow *WM_window_open(bContext *C, bool toplevel, bool dialog, bool temp, - WindowAlignment alignment) + eWindowAlignment alignment) { Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); From 6e859f7ff8906766ab19d46d06e2c131301d266d Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Oct 2021 11:09:11 +0200 Subject: [PATCH 0920/1500] Fix invalid OSL shader compilation state The lookup tables are to be initialized after device free. On Linux was only noticeable when rendering default cube scene with an extra assert. On Windows it was causing an assert in STL in debug builds. Differential Revision: https://developer.blender.org/D12918 --- intern/cycles/render/osl.cpp | 29 +++++++++++++++-------------- intern/cycles/render/osl.h | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index b6c743ac295..ef24c1876f3 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -91,7 +91,7 @@ void OSLShaderManager::reset(Scene * /*scene*/) shading_system_init(); } -void OSLShaderManager::host_update_specific(Device *device, Scene *scene, Progress &progress) +void OSLShaderManager::host_update_specific(Device * /*device*/, Scene *scene, Progress &progress) { if (!need_update()) { return; @@ -109,7 +109,6 @@ void OSLShaderManager::host_update_specific(Device *device, Scene *scene, Progre scene->image_manager->set_osl_texture_system((void *)ts); /* create shaders */ - OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); Shader *background_shader = scene->background->get_shader(scene); for (Shader *shader : scene->shaders) { @@ -126,7 +125,7 @@ void OSLShaderManager::host_update_specific(Device *device, Scene *scene, Progre OSLCompiler compiler(this, services, ss, scene); compiler.background = (shader == background_shader); - compiler.compile(og, shader); + compiler.compile(shader); if (shader->get_use_mis() && shader->has_surface_emission) { scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED); @@ -178,14 +177,22 @@ void OSLShaderManager::device_update_specific(Device *device, og->ts = ts; og->services = services; - const int background_id = scene->shader_manager->get_shader_id(background_shader); - og->background_state = og->surface_state[background_id & SHADER_MASK]; - og->use = true; + for (Shader *shader : scene->shaders) { + /* push state to array for lookup */ + og->surface_state.push_back(shader->osl_surface_ref); + og->volume_state.push_back(shader->osl_volume_ref); + og->displacement_state.push_back(shader->osl_displacement_ref); + og->bump_state.push_back(shader->osl_surface_bump_ref); - foreach (Shader *shader, scene->shaders) { shader->clear_modified(); } + const int background_id = scene->shader_manager->get_shader_id(background_shader); + const int background_state_index = (background_id & SHADER_MASK); + DCHECK_LT(background_state_index, og->surface_state.size()); + og->background_state = og->surface_state[background_state_index]; + og->use = true; + update_flags = UPDATE_NONE; device_update_common(device, dscene, scene, progress); @@ -1151,7 +1158,7 @@ OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph return group; } -void OSLCompiler::compile(OSLGlobals *og, Shader *shader) +void OSLCompiler::compile(Shader *shader) { if (shader->is_modified()) { ShaderGraph *graph = shader->graph; @@ -1213,12 +1220,6 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader) else shader->osl_displacement_ref = OSL::ShaderGroupRef(); } - - /* push state to array for lookup */ - og->surface_state.push_back(shader->osl_surface_ref); - og->volume_state.push_back(shader->osl_volume_ref); - og->displacement_state.push_back(shader->osl_displacement_ref); - og->bump_state.push_back(shader->osl_surface_bump_ref); } void OSLCompiler::parameter_texture(const char *name, ustring filename, ustring colorspace) diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h index 019dca16df7..7b7b8cc8dc5 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/render/osl.h @@ -140,7 +140,7 @@ class OSLCompiler { OSL::ShadingSystem *shadingsys, Scene *scene); #endif - void compile(OSLGlobals *og, Shader *shader); + void compile(Shader *shader); void add(ShaderNode *node, const char *name, bool isfilepath = false); From d6b54068d48da9975a997c4fcf2992c0418471eb Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Oct 2021 11:13:26 +0200 Subject: [PATCH 0921/1500] Cleanup: Unused device argument in host update functions Better not to tempt anyone from using unsafe access to device functionality during host update. --- intern/cycles/render/osl.cpp | 2 +- intern/cycles/render/osl.h | 2 +- intern/cycles/render/scene.cpp | 6 +++--- intern/cycles/render/scene.h | 4 +--- intern/cycles/render/shader.cpp | 4 ++-- intern/cycles/render/shader.h | 4 ++-- intern/cycles/render/svm.cpp | 2 +- intern/cycles/render/svm.h | 2 +- 8 files changed, 12 insertions(+), 14 deletions(-) diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index ef24c1876f3..77863fa0137 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -91,7 +91,7 @@ void OSLShaderManager::reset(Scene * /*scene*/) shading_system_init(); } -void OSLShaderManager::host_update_specific(Device * /*device*/, Scene *scene, Progress &progress) +void OSLShaderManager::host_update_specific(Scene *scene, Progress &progress) { if (!need_update()) { return; diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h index 7b7b8cc8dc5..1da8d8d0026 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/render/osl.h @@ -79,7 +79,7 @@ class OSLShaderManager : public ShaderManager { return true; } - void host_update_specific(Device *device, Scene *scene, Progress &progress) override; + void host_update_specific(Scene *scene, Progress &progress) override; void device_update_specific(Device *device, DeviceScene *dscene, diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index 17dc99dd589..6d79536061e 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -228,7 +228,7 @@ void Scene::free_memory(bool final) } } -void Scene::host_update(Device *device, Progress &progress) +void Scene::host_update(Progress &progress) { if (update_stats) { update_stats->clear(); @@ -241,7 +241,7 @@ void Scene::host_update(Device *device, Progress &progress) }); progress.set_status("Updating Shaders"); - shader_manager->host_update(device, this, progress); + shader_manager->host_update(this, progress); } void Scene::device_update(Device *device_, Progress &progress) @@ -552,7 +552,7 @@ bool Scene::update(Progress &progress) /* Update scene data on the host side. * Only updates which do not depend on the kernel (including kernel features). */ progress.set_status("Updating Scene"); - MEM_GUARDED_CALL(&progress, host_update, device, progress); + MEM_GUARDED_CALL(&progress, host_update, progress); /* Load render kernels. After host scene update so that the required kernel features are known. */ diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 83abb4c28db..87fbc872c0a 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -251,9 +251,7 @@ class Scene : public NodeOwner { Scene(const SceneParams ¶ms, Device *device); ~Scene(); - /* NOTE: Device can only use used to access invariant data. For example, OSL globals is valid - * but anything what is related on kernel and kernel features is not. */ - void host_update(Device *device, Progress &progress); + void host_update(Progress &progress); void device_update(Device *device, Progress &progress); diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index cf18b269d4c..171cbb89549 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -462,7 +462,7 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth) return id; } -void ShaderManager::host_update(Device *device, Scene *scene, Progress &progress) +void ShaderManager::host_update(Scene *scene, Progress &progress) { if (!need_update()) { return; @@ -480,7 +480,7 @@ void ShaderManager::host_update(Device *device, Scene *scene, Progress &progress assert(scene->default_background->reference_count() != 0); assert(scene->default_empty->reference_count() != 0); - host_update_specific(device, scene, progress); + host_update_specific(scene, progress); } void ShaderManager::device_update(Device *device, diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index db5e06a715e..44d88ac0e93 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -193,8 +193,8 @@ class ShaderManager { return false; } - void host_update(Device *device, Scene *scene, Progress &progress); - virtual void host_update_specific(Device *device, Scene *scene, Progress &progress) = 0; + void host_update(Scene *scene, Progress &progress); + virtual void host_update_specific(Scene *scene, Progress &progress) = 0; /* device update */ void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index efbc446fc49..a670ceb3ea3 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -69,7 +69,7 @@ static void host_compile_shader(Scene *scene, << summary.full_report(); } -void SVMShaderManager::host_update_specific(Device * /*device*/, Scene *scene, Progress &progress) +void SVMShaderManager::host_update_specific(Scene *scene, Progress &progress) { if (!need_update()) { return; diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h index e3c42b5a45a..a45c66907b1 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/render/svm.h @@ -46,7 +46,7 @@ class SVMShaderManager : public ShaderManager { void reset(Scene *scene) override; - void host_update_specific(Device *device, Scene *scene, Progress &progress) override; + void host_update_specific(Scene *scene, Progress &progress) override; void device_update_specific(Device *device, DeviceScene *dscene, From abc3128011b484c270701211b40831d11c8ac44b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Oct 2021 11:37:28 +0200 Subject: [PATCH 0922/1500] Fix T85779: Cycles not using all threads when using OpenImageDenoise The thread affinity setting in OIDN can break multithreading on some CPUs. While this leads to somewhat worse performance on CPUs that do work correctly, it's better than having some CPUs use only half the cores. --- intern/cycles/integrator/denoiser_oidn.cpp | 1 + source/blender/compositor/operations/COM_DenoiseOperation.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp index ee3b62668a7..cc9a3f51387 100644 --- a/intern/cycles/integrator/denoiser_oidn.cpp +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -169,6 +169,7 @@ class OIDNDenoiseContext { OIDNPass oidn_color_access_pass = read_input_pass(oidn_color_pass, oidn_output_pass); oidn::DeviceRef oidn_device = oidn::newDevice(); + oidn_device.set("setAffinity", false); oidn_device.commit(); /* Create a filter for denoising a beauty (color) image using prefiltered auxiliary images too. diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index 53417112974..9b9670c6f06 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -64,6 +64,7 @@ class DenoiseFilter { BLI_mutex_lock(&oidn_lock); device = oidn::newDevice(); + device.set("setAffinity", false); device.commit(); filter = device.newFilter("RT"); initialized_ = true; From 765eba5a6e0322db7ffdc0fb582f8c9645bbbb6b Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Oct 2021 11:46:11 +0200 Subject: [PATCH 0923/1500] Cleanup: More readable Cycles OSL BSDF definition A Clang-Format configuration to make the closure definition block to be properly recognized as such. Also small wrapper macro to avoid comma in the actual definition code which was causing unwanted indentation of parameters definition. Requires Clang-Format 7 or newer. The version we ship in the libs is 12, so for recommended development setup it should all be good. Differential Revision: https://developer.blender.org/D12920 --- .clang-format | 3 + intern/cycles/kernel/osl/osl_closures.cpp | 218 +++++++++++----------- intern/cycles/kernel/osl/osl_closures.h | 3 + 3 files changed, 115 insertions(+), 109 deletions(-) diff --git a/.clang-format b/.clang-format index 91df22f4d5b..41f828787b2 100644 --- a/.clang-format +++ b/.clang-format @@ -268,3 +268,6 @@ ForEachMacros: StatementMacros: - PyObject_HEAD - PyObject_VAR_HEAD + +MacroBlockBegin: "^BSDF_CLOSURE_CLASS_BEGIN$" +MacroBlockEnd: "^BSDF_CLOSURE_CLASS_END$" diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp index b59bf5a1322..a2062046ae8 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/osl_closures.cpp @@ -73,133 +73,133 @@ using namespace OSL; /* BSDF class definitions */ BSDF_CLOSURE_CLASS_BEGIN(Diffuse, diffuse, DiffuseBsdf, LABEL_DIFFUSE) -CLOSURE_FLOAT3_PARAM(DiffuseClosure, params.N), - BSDF_CLOSURE_CLASS_END(Diffuse, diffuse) + BSDF_CLOSURE_FLOAT3_PARAM(DiffuseClosure, params.N) +BSDF_CLOSURE_CLASS_END(Diffuse, diffuse) - BSDF_CLOSURE_CLASS_BEGIN(Translucent, translucent, DiffuseBsdf, LABEL_DIFFUSE) - CLOSURE_FLOAT3_PARAM(TranslucentClosure, params.N), - BSDF_CLOSURE_CLASS_END(Translucent, translucent) +BSDF_CLOSURE_CLASS_BEGIN(Translucent, translucent, DiffuseBsdf, LABEL_DIFFUSE) + BSDF_CLOSURE_FLOAT3_PARAM(TranslucentClosure, params.N) +BSDF_CLOSURE_CLASS_END(Translucent, translucent) - BSDF_CLOSURE_CLASS_BEGIN(OrenNayar, oren_nayar, OrenNayarBsdf, LABEL_DIFFUSE) - CLOSURE_FLOAT3_PARAM(OrenNayarClosure, params.N), - CLOSURE_FLOAT_PARAM(OrenNayarClosure, params.roughness), - BSDF_CLOSURE_CLASS_END(OrenNayar, oren_nayar) +BSDF_CLOSURE_CLASS_BEGIN(OrenNayar, oren_nayar, OrenNayarBsdf, LABEL_DIFFUSE) + BSDF_CLOSURE_FLOAT3_PARAM(OrenNayarClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(OrenNayarClosure, params.roughness) +BSDF_CLOSURE_CLASS_END(OrenNayar, oren_nayar) - BSDF_CLOSURE_CLASS_BEGIN(Reflection, reflection, MicrofacetBsdf, LABEL_SINGULAR) - CLOSURE_FLOAT3_PARAM(ReflectionClosure, params.N), - BSDF_CLOSURE_CLASS_END(Reflection, reflection) +BSDF_CLOSURE_CLASS_BEGIN(Reflection, reflection, MicrofacetBsdf, LABEL_SINGULAR) + BSDF_CLOSURE_FLOAT3_PARAM(ReflectionClosure, params.N) +BSDF_CLOSURE_CLASS_END(Reflection, reflection) - BSDF_CLOSURE_CLASS_BEGIN(Refraction, refraction, MicrofacetBsdf, LABEL_SINGULAR) - CLOSURE_FLOAT3_PARAM(RefractionClosure, params.N), - CLOSURE_FLOAT_PARAM(RefractionClosure, params.ior), - BSDF_CLOSURE_CLASS_END(Refraction, refraction) +BSDF_CLOSURE_CLASS_BEGIN(Refraction, refraction, MicrofacetBsdf, LABEL_SINGULAR) + BSDF_CLOSURE_FLOAT3_PARAM(RefractionClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(RefractionClosure, params.ior) +BSDF_CLOSURE_CLASS_END(Refraction, refraction) - BSDF_CLOSURE_CLASS_BEGIN(AshikhminVelvet, ashikhmin_velvet, VelvetBsdf, LABEL_DIFFUSE) - CLOSURE_FLOAT3_PARAM(AshikhminVelvetClosure, params.N), - CLOSURE_FLOAT_PARAM(AshikhminVelvetClosure, params.sigma), - BSDF_CLOSURE_CLASS_END(AshikhminVelvet, ashikhmin_velvet) +BSDF_CLOSURE_CLASS_BEGIN(AshikhminVelvet, ashikhmin_velvet, VelvetBsdf, LABEL_DIFFUSE) + BSDF_CLOSURE_FLOAT3_PARAM(AshikhminVelvetClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(AshikhminVelvetClosure, params.sigma) +BSDF_CLOSURE_CLASS_END(AshikhminVelvet, ashikhmin_velvet) - BSDF_CLOSURE_CLASS_BEGIN(AshikhminShirley, - ashikhmin_shirley, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.N), - CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.T), - CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_y), - BSDF_CLOSURE_CLASS_END(AshikhminShirley, ashikhmin_shirley) +BSDF_CLOSURE_CLASS_BEGIN(AshikhminShirley, + ashikhmin_shirley, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_REFLECT) + BSDF_CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.N) + BSDF_CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.T) + BSDF_CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_x) + BSDF_CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_y) +BSDF_CLOSURE_CLASS_END(AshikhminShirley, ashikhmin_shirley) - BSDF_CLOSURE_CLASS_BEGIN(DiffuseToon, diffuse_toon, ToonBsdf, LABEL_DIFFUSE) - CLOSURE_FLOAT3_PARAM(DiffuseToonClosure, params.N), - CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.size), - CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.smooth), - BSDF_CLOSURE_CLASS_END(DiffuseToon, diffuse_toon) +BSDF_CLOSURE_CLASS_BEGIN(DiffuseToon, diffuse_toon, ToonBsdf, LABEL_DIFFUSE) + BSDF_CLOSURE_FLOAT3_PARAM(DiffuseToonClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.size) + BSDF_CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.smooth) +BSDF_CLOSURE_CLASS_END(DiffuseToon, diffuse_toon) - BSDF_CLOSURE_CLASS_BEGIN(GlossyToon, glossy_toon, ToonBsdf, LABEL_GLOSSY) - CLOSURE_FLOAT3_PARAM(GlossyToonClosure, params.N), - CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.size), - CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.smooth), - BSDF_CLOSURE_CLASS_END(GlossyToon, glossy_toon) +BSDF_CLOSURE_CLASS_BEGIN(GlossyToon, glossy_toon, ToonBsdf, LABEL_GLOSSY) + BSDF_CLOSURE_FLOAT3_PARAM(GlossyToonClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.size) + BSDF_CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.smooth) +BSDF_CLOSURE_CLASS_END(GlossyToon, glossy_toon) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXIsotropic, - microfacet_ggx_isotropic, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - CLOSURE_FLOAT3_PARAM(MicrofacetGGXIsotropicClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetGGXIsotropicClosure, params.alpha_x), - BSDF_CLOSURE_CLASS_END(MicrofacetGGXIsotropic, microfacet_ggx_isotropic) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXIsotropic, + microfacet_ggx_isotropic, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_REFLECT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXIsotropicClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXIsotropicClosure, params.alpha_x) +BSDF_CLOSURE_CLASS_END(MicrofacetGGXIsotropic, microfacet_ggx_isotropic) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGX, - microfacet_ggx, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.N), - CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.T), - CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_y), - BSDF_CLOSURE_CLASS_END(MicrofacetGGX, microfacet_ggx) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGX, + microfacet_ggx, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_REFLECT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.N) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.T) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_x) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_y) +BSDF_CLOSURE_CLASS_END(MicrofacetGGX, microfacet_ggx) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannIsotropic, - microfacet_beckmann_isotropic, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannIsotropicClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetBeckmannIsotropicClosure, params.alpha_x), - BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannIsotropic, microfacet_beckmann_isotropic) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannIsotropic, + microfacet_beckmann_isotropic, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_REFLECT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannIsotropicClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannIsotropicClosure, params.alpha_x) +BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannIsotropic, microfacet_beckmann_isotropic) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmann, - microfacet_beckmann, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.N), - CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.T), - CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_y), - BSDF_CLOSURE_CLASS_END(MicrofacetBeckmann, microfacet_beckmann) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmann, + microfacet_beckmann, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_REFLECT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.N) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.T) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_x) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_y) +BSDF_CLOSURE_CLASS_END(MicrofacetBeckmann, microfacet_beckmann) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXRefraction, - microfacet_ggx_refraction, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_TRANSMIT) - CLOSURE_FLOAT3_PARAM(MicrofacetGGXRefractionClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.ior), - BSDF_CLOSURE_CLASS_END(MicrofacetGGXRefraction, microfacet_ggx_refraction) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXRefraction, + microfacet_ggx_refraction, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_TRANSMIT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXRefractionClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.alpha_x) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.ior) +BSDF_CLOSURE_CLASS_END(MicrofacetGGXRefraction, microfacet_ggx_refraction) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannRefraction, - microfacet_beckmann_refraction, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_TRANSMIT) - CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannRefractionClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.ior), - BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannRefraction, microfacet_beckmann_refraction) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannRefraction, + microfacet_beckmann_refraction, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_TRANSMIT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannRefractionClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.alpha_x) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.ior) +BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannRefraction, microfacet_beckmann_refraction) - BSDF_CLOSURE_CLASS_BEGIN(HairReflection, hair_reflection, HairBsdf, LABEL_GLOSSY) - CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.N), - CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness1), - CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness2), - CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T), - CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset), - BSDF_CLOSURE_CLASS_END(HairReflection, hair_reflection) +BSDF_CLOSURE_CLASS_BEGIN(HairReflection, hair_reflection, HairBsdf, LABEL_GLOSSY) + BSDF_CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness1) + BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness2) + BSDF_CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T) + BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset) +BSDF_CLOSURE_CLASS_END(HairReflection, hair_reflection) - BSDF_CLOSURE_CLASS_BEGIN(HairTransmission, hair_transmission, HairBsdf, LABEL_GLOSSY) - CLOSURE_FLOAT3_PARAM(HairTransmissionClosure, params.N), - CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness1), - CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness2), - CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T), - CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset), - BSDF_CLOSURE_CLASS_END(HairTransmission, hair_transmission) +BSDF_CLOSURE_CLASS_BEGIN(HairTransmission, hair_transmission, HairBsdf, LABEL_GLOSSY) + BSDF_CLOSURE_FLOAT3_PARAM(HairTransmissionClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness1) + BSDF_CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness2) + BSDF_CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T) + BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset) +BSDF_CLOSURE_CLASS_END(HairTransmission, hair_transmission) - BSDF_CLOSURE_CLASS_BEGIN(PrincipledDiffuse, - principled_diffuse, - PrincipledDiffuseBsdf, - LABEL_DIFFUSE) - CLOSURE_FLOAT3_PARAM(PrincipledDiffuseClosure, params.N), - CLOSURE_FLOAT_PARAM(PrincipledDiffuseClosure, params.roughness), - BSDF_CLOSURE_CLASS_END(PrincipledDiffuse, principled_diffuse) +BSDF_CLOSURE_CLASS_BEGIN(PrincipledDiffuse, + principled_diffuse, + PrincipledDiffuseBsdf, + LABEL_DIFFUSE) + BSDF_CLOSURE_FLOAT3_PARAM(PrincipledDiffuseClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(PrincipledDiffuseClosure, params.roughness) +BSDF_CLOSURE_CLASS_END(PrincipledDiffuse, principled_diffuse) - class PrincipledSheenClosure : public CBSDFClosure { +class PrincipledSheenClosure : public CBSDFClosure { public: PrincipledSheenBsdf params; diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/osl_closures.h index 90a965a3365..7869d793737 100644 --- a/intern/cycles/kernel/osl/osl_closures.h +++ b/intern/cycles/kernel/osl/osl_closures.h @@ -105,6 +105,9 @@ void closure_bsdf_principled_hair_prepare(OSL::RendererServices *, int id, void TypeDesc::TypeVector, (int)reckless_offsetof(st, fld), NULL, sizeof(OSL::Vec3) \ } +#define BSDF_CLOSURE_FLOAT_PARAM(st, fld) CLOSURE_FLOAT_PARAM(st, fld), +#define BSDF_CLOSURE_FLOAT3_PARAM(st, fld) CLOSURE_FLOAT3_PARAM(st, fld), + #define TO_VEC3(v) OSL::Vec3(v.x, v.y, v.z) #define TO_COLOR3(v) OSL::Color3(v.x, v.y, v.z) #define TO_FLOAT3(v) make_float3(v[0], v[1], v[2]) From c107a3c4d9a540a287e21c517d353b670a71a0b5 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Oct 2021 12:00:41 +0200 Subject: [PATCH 0924/1500] Fix invalid principled diffuse in Cycles OSL Need to initialize components for the full Diffuse BSDF. Steps to reproduce: - Default cube scene - Switch to Cycles renderer - Enable OSL backend - Start viewport render - Observe cube being much black Differential Revision: https://developer.blender.org/D12921 --- intern/cycles/kernel/closure/bsdf_principled_diffuse.h | 1 + 1 file changed, 1 insertion(+) diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index 6d25daa2356..74390f768a2 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -49,6 +49,7 @@ static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledDiffuseBsdf), ccl_device int bsdf_principled_diffuse_setup(ccl_private PrincipledDiffuseBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID; + bsdf->components = PRINCIPLED_DIFFUSE_FULL; return SD_BSDF | SD_BSDF_HAS_EVAL; } From a395a1b36b9f2dfb32408a1150844312ab62b0b3 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Oct 2021 12:17:58 +0200 Subject: [PATCH 0925/1500] Cleanup: fix compiler warnings --- intern/cycles/kernel/svm/svm_ao.h | 6 +++--- intern/cycles/kernel/svm/svm_aov.h | 6 ++---- intern/cycles/kernel/svm/svm_bevel.h | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/intern/cycles/kernel/svm/svm_ao.h b/intern/cycles/kernel/svm/svm_ao.h index 18d60c43b12..a1efd2f0a43 100644 --- a/intern/cycles/kernel/svm/svm_ao.h +++ b/intern/cycles/kernel/svm/svm_ao.h @@ -110,13 +110,13 @@ ccl_device_noinline uint color_offset, out_color_offset, samples; svm_unpack_node_uchar3(node.z, &color_offset, &out_color_offset, &samples); - float dist = stack_load_float_default(stack, dist_offset, node.w); - float3 normal = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N; - float ao = 1.0f; IF_KERNEL_NODES_FEATURE(RAYTRACE) { + float dist = stack_load_float_default(stack, dist_offset, node.w); + float3 normal = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N; + # ifdef __KERNEL_OPTIX__ ao = optixDirectCall(0, kg, state, sd, normal, dist, samples, flags); # else diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h index a18567fdc3c..0d6395d52c0 100644 --- a/intern/cycles/kernel/svm/svm_aov.h +++ b/intern/cycles/kernel/svm/svm_aov.h @@ -34,10 +34,9 @@ ccl_device void svm_node_aov_color(KernelGlobals kg, uint4 node, ccl_global float *render_buffer) { - float3 val = stack_load_float3(stack, node.y); - IF_KERNEL_NODES_FEATURE(AOV) { + const float3 val = stack_load_float3(stack, node.y); const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kernel_data.film.pass_stride; @@ -55,10 +54,9 @@ ccl_device void svm_node_aov_value(KernelGlobals kg, uint4 node, ccl_global float *render_buffer) { - float val = stack_load_float(stack, node.y); - IF_KERNEL_NODES_FEATURE(AOV) { + const float val = stack_load_float(stack, node.y); const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * kernel_data.film.pass_stride; diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/svm_bevel.h index 197562434f9..3ce3af20795 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/svm_bevel.h @@ -298,12 +298,12 @@ ccl_device_noinline uint num_samples, radius_offset, normal_offset, out_offset; svm_unpack_node_uchar4(node.y, &num_samples, &radius_offset, &normal_offset, &out_offset); - float radius = stack_load_float(stack, radius_offset); - float3 bevel_N = sd->N; IF_KERNEL_NODES_FEATURE(RAYTRACE) { + float radius = stack_load_float(stack, radius_offset); + # ifdef __KERNEL_OPTIX__ bevel_N = optixDirectCall(1, kg, state, sd, radius, num_samples); # else From 6ee181ec2446fce3f61ed3a187ea08f50db82ca2 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Oct 2021 12:39:19 +0200 Subject: [PATCH 0926/1500] Cycles: improve sampling pattern description regarding adaptive sampling --- intern/cycles/blender/addon/properties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index faa0aaec8ae..7ac59cf563e 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -332,7 +332,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): sampling_pattern: EnumProperty( name="Sampling Pattern", - description="Random sampling pattern used by the integrator", + description="Random sampling pattern used by the integrator. When adaptive sampling is enabled, Progressive Multi-Jitter is always used instead of Sobol", items=enum_sampling_pattern, default='PROGRESSIVE_MUTI_JITTER', ) From 8e8932c8ff28ec1b1ac7af9254244d94b326b9b8 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Oct 2021 12:13:27 +0200 Subject: [PATCH 0927/1500] Render: use "_" as delimiter in AOV names to avoid issues with OpenEXR OpenEXR uses "." to separate layers/passes/channels, so using AOV.001 is a problem. Other applications will not be able to parse it correctly. Default to AOV_001 instead, and don't allow using dots in AOV names. Fixes T89991 Ref T73266 Ref D12871 --- source/blender/blenkernel/intern/layer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 434a2296d95..502a4b8c22a 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -2380,8 +2380,12 @@ static void viewlayer_aov_make_name_unique(ViewLayer *view_layer) if (aov == NULL) { return; } + + /* Don't allow dots, it's incompatible with OpenEXR convention to store channels + * as "layer.pass.channel". */ + BLI_str_replace_char(aov->name, '.', '_'); BLI_uniquename( - &view_layer->aovs, aov, DATA_("AOV"), '.', offsetof(ViewLayerAOV, name), sizeof(aov->name)); + &view_layer->aovs, aov, DATA_("AOV"), '_', offsetof(ViewLayerAOV, name), sizeof(aov->name)); } static void viewlayer_aov_active_set(ViewLayer *view_layer, ViewLayerAOV *aov) From 9e9d003a823f6f9f83c04b6f2493604735d80fe9 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Oct 2021 14:22:22 +0200 Subject: [PATCH 0928/1500] Render: change view layer name convention to ViewLayer_001 to avoid OpenEXR issues Some compositing applications do not support spaces and dots in layer names, and change these to other symbols on import. This causes various compatibility issues, including with Cryptomatte metadata. While technically those could be considered bugs in the Cryptomatte implementation of other software, where they are not properly accounting for that layer renaming, it's not ideal. The OpenEXR channel naming convention is "layer.pass.channel". We get away with dots in the layer name since we parse this from right to left, but it's a weak assumption. Now we don't forbid using spaces or dots, and existing files are unchanged. But at least by default names will be compatible, and hopefully other software catches up in time to support more flexible layer names. Ref T68924 --- source/blender/blenkernel/intern/layer.c | 4 ++-- source/blender/blenkernel/intern/scene.c | 2 +- source/blender/blenloader/intern/versioning_defaults.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 502a4b8c22a..e51442b705d 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -170,7 +170,7 @@ ViewLayer *BKE_view_layer_context_active_PLACEHOLDER(const Scene *scene) static ViewLayer *view_layer_add(const char *name) { if (!name) { - name = DATA_("View Layer"); + name = DATA_("ViewLayer"); } ViewLayer *view_layer = MEM_callocN(sizeof(ViewLayer), "View Layer"); @@ -248,7 +248,7 @@ ViewLayer *BKE_view_layer_add(Scene *scene, BLI_uniquename(&scene->view_layers, view_layer_new, DATA_("ViewLayer"), - '.', + '_', offsetof(ViewLayer, name), sizeof(view_layer_new->name)); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 397bd430fd9..2cb0213a192 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -239,7 +239,7 @@ static void scene_init_data(ID *id) /* Master Collection */ scene->master_collection = BKE_collection_master_add(); - BKE_view_layer_add(scene, "View Layer", NULL, VIEWLAYER_ADD_NEW); + BKE_view_layer_add(scene, "ViewLayer", NULL, VIEWLAYER_ADD_NEW); } static void scene_copy_markers(Scene *scene_dst, const Scene *scene_src, const int flag) diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 2dcb2c35b22..c50b410e2b9 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -294,7 +294,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) } /* Rename render layers. */ - BKE_view_layer_rename(bmain, scene, scene->view_layers.first, "View Layer"); + BKE_view_layer_rename(bmain, scene, scene->view_layers.first, "ViewLayer"); /* Disable Z pass by default. */ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { From 5b6dacb9ebf337a745aff40b9179d9632c558340 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 6 Oct 2021 18:07:36 +0200 Subject: [PATCH 0929/1500] Fix splash screen showing language when building without that feature --- release/scripts/startup/bl_operators/wm.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 280df736c18..b011711d133 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -2840,12 +2840,13 @@ class WM_MT_splash_quick_setup(Menu): col = split.column() - sub = col.split(factor=0.35) - row = sub.row() - row.alignment = 'RIGHT' - row.label(text="Language") - prefs = context.preferences - sub.prop(prefs.view, "language", text="") + if bpy.app.build_options.international: + sub = col.split(factor=0.35) + row = sub.row() + row.alignment = 'RIGHT' + row.label(text="Language") + prefs = context.preferences + sub.prop(prefs.view, "language", text="") col.separator() From 7fa6794037577d467f3b01da2becdf0a060ff3ba Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Oct 2021 14:53:48 +0200 Subject: [PATCH 0930/1500] Fix failing view layer tests after recent changes to naming convention --- source/blender/blenkernel/intern/layer_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/layer_test.cc b/source/blender/blenkernel/intern/layer_test.cc index c918c0cab67..b5f800dd181 100644 --- a/source/blender/blenkernel/intern/layer_test.cc +++ b/source/blender/blenkernel/intern/layer_test.cc @@ -69,7 +69,7 @@ TEST(view_layer, aov_unique_names) EXPECT_FALSE((aov1->flag & AOV_CONFLICT) != 0); EXPECT_FALSE((aov2->flag & AOV_CONFLICT) != 0); EXPECT_TRUE(STREQ(aov1->name, "AOV")); - EXPECT_TRUE(STREQ(aov2->name, "AOV.001")); + EXPECT_TRUE(STREQ(aov2->name, "AOV_001")); /* Revert previous resolution */ BLI_strncpy(aov2->name, "AOV", MAX_NAME); @@ -78,7 +78,7 @@ TEST(view_layer, aov_unique_names) EXPECT_FALSE((aov1->flag & AOV_CONFLICT) != 0); EXPECT_FALSE((aov2->flag & AOV_CONFLICT) != 0); EXPECT_TRUE(STREQ(aov1->name, "AOV")); - EXPECT_TRUE(STREQ(aov2->name, "AOV.001")); + EXPECT_TRUE(STREQ(aov2->name, "AOV_001")); /* Resolve by removing AOV resolution */ BKE_view_layer_remove_aov(view_layer, aov2); From 6e473a897ce563ad04224bdd731387b0dbd22235 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Oct 2021 15:02:44 +0200 Subject: [PATCH 0931/1500] Tests: update Cycles GPU tests blacklist so all tests pass More tests are matching now, mainly due to unified volume sampling. --- tests/python/cycles_render_tests.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/tests/python/cycles_render_tests.py b/tests/python/cycles_render_tests.py index ca0bc9f18b9..98b2cd4a200 100644 --- a/tests/python/cycles_render_tests.py +++ b/tests/python/cycles_render_tests.py @@ -18,40 +18,23 @@ BLACKLIST_OSL = [ ] BLACKLIST_OPTIX = [ - # No branched path on Optix. - 'T53854.blend', + # Ray intersection precision issues 'T50164.blend', - 'portal.blend', - 'denoise_sss.blend', - 'denoise_passes.blend', - 'distant_light.blend', - 'aov_position.blend', - 'subsurface_branched_path.blend', 'T43865.blend', ] BLACKLIST_GPU = [ - # Missing equiangular sampling on GPU. - 'area_light.blend', - 'denoise_hair.blend', - 'point_density_.*.blend', - 'point_light.blend', - 'shadow_catcher_bpt_.*.blend', - 'sphere_light.blend', - 'spot_light.blend', - 'T48346.blend', - 'world_volume.blend', # Uninvestigated differences with GPU. 'image_log.blend', - 'subsurface_behind_glass_branched.blend', 'T40964.blend', 'T45609.blend', - 'T48860.blend', 'smoke_color.blend', 'bevel_mblur.blend', # Inconsistency between Embree and Hair primitive on GPU. + 'denoise_hair.blend', 'hair_basemesh_intercept.blend', 'hair_instancer_uv.blend', + 'hair_length_info.blend', 'hair_particle_random.blend', 'principled_hair_.*.blend', 'transparent_shadow_hair.*.blend', From 943e73b07e26d64c04ccb7d8f656e3818a57cca0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 17 Oct 2021 16:22:20 +0200 Subject: [PATCH 0932/1500] Cycles: decouple shadow paths from main path on GPU The motivation for this is twofold. It improves performance (5-10% on most benchmark scenes), and will help to bring back transparency support for the ambient occlusion pass. * Duplicate some members from the main path state in the shadow path state. * Add shadow paths incrementally to the array similar to what we do for the shadow catchers. * For the scheduling, allow running shade surface and shade volume kernels as long as there is enough space in the shadow paths array. If not, execute shadow kernels until it is empty. * Add IntegratorShadowState and ConstIntegratorShadowState typedefs that can be different between CPU and GPU. For GPU both main and shadow paths juse have an integer for SoA access. Bt with CPU it's a different pointer type so we get type safety checks in code shared between CPU and GPU. * For CPU, add a separate IntegratorShadowStateCPU struct embedded in IntegratorShadowState. * Update various functions to take the shadow state, and make SVM take either type of state using templates. Differential Revision: https://developer.blender.org/D12889 --- .../cycles/integrator/path_trace_work_gpu.cpp | 113 ++++++++++++---- .../cycles/integrator/path_trace_work_gpu.h | 7 + intern/cycles/kernel/CMakeLists.txt | 1 + .../kernel/device/cpu/kernel_arch_impl.h | 36 ++++-- intern/cycles/kernel/device/gpu/kernel.h | 10 +- .../integrator/integrator_intersect_closest.h | 7 - .../integrator/integrator_intersect_shadow.h | 10 +- .../kernel/integrator/integrator_megakernel.h | 11 +- .../integrator/integrator_shade_shadow.h | 10 +- .../integrator/integrator_shade_surface.h | 39 ++++-- .../integrator/integrator_shade_volume.h | 43 +++++-- .../integrator_shadow_state_template.h | 83 ++++++++++++ .../kernel/integrator/integrator_state.h | 34 ++++- .../kernel/integrator/integrator_state_flow.h | 11 +- .../integrator/integrator_state_template.h | 58 +-------- .../kernel/integrator/integrator_state_util.h | 121 +++++++++++++++--- .../integrator/integrator_volume_stack.h | 6 +- intern/cycles/kernel/kernel_accumulate.h | 9 +- intern/cycles/kernel/kernel_path_state.h | 15 ++- intern/cycles/kernel/kernel_shader.h | 11 +- intern/cycles/kernel/kernel_shadow_catcher.h | 2 +- intern/cycles/kernel/kernel_types.h | 1 + intern/cycles/kernel/osl/osl_services.cpp | 69 ++++++---- intern/cycles/kernel/osl/osl_shader.cpp | 19 +-- intern/cycles/kernel/osl/osl_shader.h | 10 +- intern/cycles/kernel/svm/svm.h | 4 +- intern/cycles/kernel/svm/svm_ao.h | 22 ++-- intern/cycles/kernel/svm/svm_aov.h | 8 +- intern/cycles/kernel/svm/svm_bevel.h | 18 +-- intern/cycles/kernel/svm/svm_light_path.h | 39 +++--- 30 files changed, 550 insertions(+), 277 deletions(-) create mode 100644 intern/cycles/kernel/integrator/integrator_shadow_state_template.h diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index bc380f269ad..18aa5dda70d 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -52,7 +52,11 @@ static size_t estimate_single_state_size() * For until then use common value. Currently this size is only used for logging, but is weak to * rely on this. */ #define KERNEL_STRUCT_VOLUME_STACK_SIZE 4 + #include "kernel/integrator/integrator_state_template.h" + +#include "kernel/integrator/integrator_shadow_state_template.h" + #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER #undef KERNEL_STRUCT_ARRAY_MEMBER @@ -74,6 +78,8 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device, integrator_shader_sort_counter_(device, "integrator_shader_sort_counter", MEM_READ_WRITE), integrator_shader_raytrace_sort_counter_( device, "integrator_shader_raytrace_sort_counter", MEM_READ_WRITE), + integrator_next_shadow_path_index_( + device, "integrator_next_shadow_path_index", MEM_READ_WRITE), integrator_next_shadow_catcher_path_index_( device, "integrator_next_shadow_catcher_path_index", MEM_READ_WRITE), queued_paths_(device, "queued_paths", MEM_READ_WRITE), @@ -138,7 +144,11 @@ void PathTraceWorkGPU::alloc_integrator_soa() } \ } #define KERNEL_STRUCT_VOLUME_STACK_SIZE (integrator_state_soa_volume_stack_size_) + #include "kernel/integrator/integrator_state_template.h" + +#include "kernel/integrator/integrator_shadow_state_template.h" + #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER #undef KERNEL_STRUCT_ARRAY_MEMBER @@ -199,16 +209,22 @@ void PathTraceWorkGPU::alloc_integrator_sorting() void PathTraceWorkGPU::alloc_integrator_path_split() { - if (integrator_next_shadow_catcher_path_index_.size() != 0) { - return; + if (integrator_next_shadow_path_index_.size() == 0) { + integrator_next_shadow_path_index_.alloc(1); + integrator_next_shadow_path_index_.zero_to_device(); + + integrator_state_gpu_.next_shadow_path_index = + (int *)integrator_next_shadow_path_index_.device_pointer; } - integrator_next_shadow_catcher_path_index_.alloc(1); - /* TODO(sergey): Use queue? */ - integrator_next_shadow_catcher_path_index_.zero_to_device(); + if (integrator_next_shadow_catcher_path_index_.size() == 0) { + integrator_next_shadow_catcher_path_index_.alloc(1); + integrator_next_shadow_path_index_.data()[0] = 0; + integrator_next_shadow_catcher_path_index_.zero_to_device(); - integrator_state_gpu_.next_shadow_catcher_path_index = - (int *)integrator_next_shadow_catcher_path_index_.device_pointer; + integrator_state_gpu_.next_shadow_catcher_path_index = + (int *)integrator_next_shadow_catcher_path_index_.device_pointer; + } } void PathTraceWorkGPU::alloc_work_memory() @@ -341,27 +357,45 @@ bool PathTraceWorkGPU::enqueue_path_iteration() return false; } - /* Finish shadows before potentially adding more shadow rays. We can only - * store one shadow ray in the integrator state. + /* If the number of shadow kernels dropped to zero, set the next shadow path + * index to zero as well. * - * When there is a shadow catcher in the scene finish shadow rays before invoking intersect - * closest kernel since so that the shadow paths are writing to the pre-split state. */ - if (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || - kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE || - kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME || - (has_shadow_catcher() && kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST)) { - if (queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW]) { - enqueue_path_iteration(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); - return true; + * TODO: use shadow path compaction to lower it more often instead of letting + * it fill up entirely? */ + const int num_queued_shadow = + queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW] + + queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW]; + if (num_queued_shadow == 0) { + if (integrator_next_shadow_path_index_.data()[0] != 0) { + integrator_next_shadow_path_index_.data()[0] = 0; + queue_->copy_to_device(integrator_next_shadow_path_index_); } - else if (queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW]) { - enqueue_path_iteration(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); - return true; + } + + /* For kernels that add shadow paths, check if there is enough space available. + * If not, schedule shadow kernels first to clear out the shadow paths. */ + if (kernel_creates_shadow_paths(kernel)) { + if (max_num_paths_ - integrator_next_shadow_path_index_.data()[0] < + queue_counter->num_queued[kernel]) { + if (queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW]) { + enqueue_path_iteration(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + return true; + } + else if (queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW]) { + enqueue_path_iteration(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + return true; + } } } /* Schedule kernel with maximum number of queued items. */ enqueue_path_iteration(kernel); + + /* Update next shadow path index for kernels that can add shadow paths. */ + if (kernel_creates_shadow_paths(kernel)) { + queue_->copy_from_device(integrator_next_shadow_path_index_); + } + return true; } @@ -370,13 +404,12 @@ void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel) void *d_path_index = (void *)NULL; /* Create array of path indices for which this kernel is queued to be executed. */ - int work_size = max_active_path_index_; + int work_size = kernel_max_active_path_index(kernel); IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); int num_queued = queue_counter->num_queued[kernel]; - if (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || - kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) { + if (kernel_uses_sorting(kernel)) { /* Compute array of active paths, sorted by shader. */ work_size = num_queued; d_path_index = (void *)queued_paths_.device_pointer; @@ -387,8 +420,7 @@ void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel) work_size = num_queued; d_path_index = (void *)queued_paths_.device_pointer; - if (kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || - kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW) { + if (kernel_is_shadow_path(kernel)) { /* Compute array of active shadow paths for specific kernel. */ compute_queued_paths(DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY, kernel); } @@ -452,7 +484,7 @@ void PathTraceWorkGPU::compute_sorted_queued_paths(DeviceKernel kernel, DeviceKe { /* TODO: this could be smaller for terminated paths based on amount of work we want * to schedule. */ - const int work_size = max_active_path_index_; + const int work_size = kernel_max_active_path_index(queued_kernel); void *d_queued_paths = (void *)queued_paths_.device_pointer; void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; @@ -481,7 +513,7 @@ void PathTraceWorkGPU::compute_queued_paths(DeviceKernel kernel, DeviceKernel qu int d_queued_kernel = queued_kernel; /* Launch kernel to fill the active paths arrays. */ - const int work_size = max_active_path_index_; + const int work_size = kernel_max_active_path_index(queued_kernel); void *d_queued_paths = (void *)queued_paths_.device_pointer; void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; void *args[] = { @@ -981,4 +1013,29 @@ int PathTraceWorkGPU::shadow_catcher_count_possible_splits() return num_queued_paths_.data()[0]; } +bool PathTraceWorkGPU::kernel_uses_sorting(DeviceKernel kernel) +{ + return (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE); +} + +bool PathTraceWorkGPU::kernel_creates_shadow_paths(DeviceKernel kernel) +{ + return (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); +} + +bool PathTraceWorkGPU::kernel_is_shadow_path(DeviceKernel kernel) +{ + return (kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); +} + +int PathTraceWorkGPU::kernel_max_active_path_index(DeviceKernel kernel) +{ + return (kernel_is_shadow_path(kernel)) ? integrator_next_shadow_path_index_.data()[0] : + max_active_path_index_; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h index e66851cc8d8..dd2c1c197ae 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.h +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -113,6 +113,12 @@ class PathTraceWorkGPU : public PathTraceWork { /* Count how many currently scheduled paths can still split. */ int shadow_catcher_count_possible_splits(); + /* Kernel properties. */ + bool kernel_uses_sorting(DeviceKernel kernel); + bool kernel_creates_shadow_paths(DeviceKernel kernel); + bool kernel_is_shadow_path(DeviceKernel kernel); + int kernel_max_active_path_index(DeviceKernel kernel); + /* Integrator queue. */ unique_ptr queue_; @@ -131,6 +137,7 @@ class PathTraceWorkGPU : public PathTraceWork { device_vector integrator_shader_sort_counter_; device_vector integrator_shader_raytrace_sort_counter_; /* Path split. */ + device_vector integrator_next_shadow_path_index_; device_vector integrator_next_shadow_catcher_path_index_; /* Temporary buffer to get an array of queued path for a particular kernel. */ diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index e0d48361650..7357c5804ed 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -236,6 +236,7 @@ set(SRC_INTEGRATOR_HEADERS integrator/integrator_shade_shadow.h integrator/integrator_shade_surface.h integrator/integrator_shade_volume.h + integrator/integrator_shadow_state_template.h integrator/integrator_state.h integrator/integrator_state_flow.h integrator/integrator_state_template.h diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h index bf8667ac045..2b0eea4fb61 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h @@ -69,6 +69,18 @@ CCL_NAMESPACE_BEGIN # define KERNEL_INVOKE(name, ...) integrator_##name(__VA_ARGS__) #endif +/* TODO: Either use something like get_work_pixel(), or simplify tile which is passed here, so + * that it does not contain unused fields. */ +#define DEFINE_INTEGRATOR_INIT_KERNEL(name) \ + bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *kg, \ + IntegratorStateCPU *state, \ + KernelWorkTile *tile, \ + ccl_global float *render_buffer) \ + { \ + return KERNEL_INVOKE( \ + name, kg, state, tile, render_buffer, tile->x, tile->y, tile->start_sample); \ + } + #define DEFINE_INTEGRATOR_KERNEL(name) \ void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *kg, \ IntegratorStateCPU *state) \ @@ -83,30 +95,32 @@ CCL_NAMESPACE_BEGIN KERNEL_INVOKE(name, kg, state, render_buffer); \ } -/* TODO: Either use something like get_work_pixel(), or simplify tile which is passed here, so - * that it does not contain unused fields. */ -#define DEFINE_INTEGRATOR_INIT_KERNEL(name) \ - bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *kg, \ - IntegratorStateCPU *state, \ - KernelWorkTile *tile, \ - ccl_global float *render_buffer) \ +#define DEFINE_INTEGRATOR_SHADOW_KERNEL(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *kg, \ + IntegratorStateCPU *state) \ { \ - return KERNEL_INVOKE( \ - name, kg, state, tile, render_buffer, tile->x, tile->y, tile->start_sample); \ + KERNEL_INVOKE(name, kg, &state->shadow); \ + } + +#define DEFINE_INTEGRATOR_SHADOW_SHADE_KERNEL(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)( \ + const KernelGlobalsCPU *kg, IntegratorStateCPU *state, ccl_global float *render_buffer) \ + { \ + KERNEL_INVOKE(name, kg, &state->shadow, render_buffer); \ } DEFINE_INTEGRATOR_INIT_KERNEL(init_from_camera) DEFINE_INTEGRATOR_INIT_KERNEL(init_from_bake) DEFINE_INTEGRATOR_KERNEL(intersect_closest) -DEFINE_INTEGRATOR_KERNEL(intersect_shadow) DEFINE_INTEGRATOR_KERNEL(intersect_subsurface) DEFINE_INTEGRATOR_KERNEL(intersect_volume_stack) DEFINE_INTEGRATOR_SHADE_KERNEL(shade_background) DEFINE_INTEGRATOR_SHADE_KERNEL(shade_light) -DEFINE_INTEGRATOR_SHADE_KERNEL(shade_shadow) DEFINE_INTEGRATOR_SHADE_KERNEL(shade_surface) DEFINE_INTEGRATOR_SHADE_KERNEL(shade_volume) DEFINE_INTEGRATOR_SHADE_KERNEL(megakernel) +DEFINE_INTEGRATOR_SHADOW_KERNEL(intersect_shadow) +DEFINE_INTEGRATOR_SHADOW_SHADE_KERNEL(shade_shadow) /* -------------------------------------------------------------------- * Shader evaluation. diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index b5ecab2a4db..6b4d79ed5b7 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -265,8 +265,7 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_B { gpu_parallel_active_index_array( num_states, indices, num_indices, [](const int state) { - return (INTEGRATOR_STATE(state, path, queued_kernel) != 0) || - (INTEGRATOR_STATE(state, shadow_path, queued_kernel) != 0); + return (INTEGRATOR_STATE(state, path, queued_kernel) != 0); }); } @@ -278,8 +277,7 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_B { gpu_parallel_active_index_array( num_states, indices + indices_offset, num_indices, [](const int state) { - return (INTEGRATOR_STATE(state, path, queued_kernel) == 0) && - (INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0); + return (INTEGRATOR_STATE(state, path, queued_kernel) == 0); }); } @@ -303,9 +301,7 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_B { gpu_parallel_active_index_array( num_states, indices, num_indices, [num_active_paths](const int state) { - return (state >= num_active_paths) && - ((INTEGRATOR_STATE(state, path, queued_kernel) != 0) || - (INTEGRATOR_STATE(state, shadow_path, queued_kernel) != 0)); + return (state >= num_active_paths) && (INTEGRATOR_STATE(state, path, queued_kernel) != 0); }); } diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/integrator_intersect_closest.h index 317ea76553a..ef8dcb50115 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_closest.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_closest.h @@ -136,13 +136,6 @@ ccl_device_forceinline void integrator_intersect_shader_next_kernel( else { INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } - - /* If the split happened after bounce through a transparent object it's possible to have shadow - * patch. Make sure it is properly re-scheduled on the split path. */ - const int shadow_kernel = INTEGRATOR_STATE(state, shadow_path, queued_kernel); - if (shadow_kernel != 0) { - INTEGRATOR_SHADOW_PATH_INIT(shadow_kernel); - } } #endif } diff --git a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h index 06f58f88bc8..9dc0eb02c9b 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h @@ -20,7 +20,7 @@ CCL_NAMESPACE_BEGIN /* Visibility for the shadow ray. */ ccl_device_forceinline uint integrate_intersect_shadow_visibility(KernelGlobals kg, - ConstIntegratorState state) + ConstIntegratorShadowState state) { uint visibility = PATH_RAY_SHADOW; @@ -33,7 +33,7 @@ ccl_device_forceinline uint integrate_intersect_shadow_visibility(KernelGlobals } ccl_device bool integrate_intersect_shadow_opaque(KernelGlobals kg, - IntegratorState state, + IntegratorShadowState state, ccl_private const Ray *ray, const uint visibility) { @@ -55,7 +55,7 @@ ccl_device bool integrate_intersect_shadow_opaque(KernelGlobals kg, } ccl_device_forceinline int integrate_shadow_max_transparent_hits(KernelGlobals kg, - ConstIntegratorState state) + ConstIntegratorShadowState state) { const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce; const int transparent_bounce = INTEGRATOR_STATE(state, shadow_path, transparent_bounce); @@ -65,7 +65,7 @@ ccl_device_forceinline int integrate_shadow_max_transparent_hits(KernelGlobals k #ifdef __TRANSPARENT_SHADOWS__ ccl_device bool integrate_intersect_shadow_transparent(KernelGlobals kg, - IntegratorState state, + IntegratorShadowState state, ccl_private const Ray *ray, const uint visibility) { @@ -106,7 +106,7 @@ ccl_device bool integrate_intersect_shadow_transparent(KernelGlobals kg, } #endif -ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorState state) +ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowState state) { PROFILING_INIT(kg, PROFILING_INTERSECT_SHADOW); diff --git a/intern/cycles/kernel/integrator/integrator_megakernel.h b/intern/cycles/kernel/integrator/integrator_megakernel.h index a3b2b1f9e90..6e3220aa3b7 100644 --- a/intern/cycles/kernel/integrator/integrator_megakernel.h +++ b/intern/cycles/kernel/integrator/integrator_megakernel.h @@ -39,14 +39,17 @@ ccl_device void integrator_megakernel(KernelGlobals kg, * TODO: investigate if we can use device side enqueue for GPUs to avoid * having to compile this big kernel. */ while (true) { - if (INTEGRATOR_STATE(state, shadow_path, queued_kernel)) { + const uint32_t shadow_queued_kernel = INTEGRATOR_STATE( + &state->shadow, shadow_path, queued_kernel); + + if (shadow_queued_kernel) { /* First handle any shadow paths before we potentially create more shadow paths. */ - switch (INTEGRATOR_STATE(state, shadow_path, queued_kernel)) { + switch (shadow_queued_kernel) { case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: - integrator_intersect_shadow(kg, state); + integrator_intersect_shadow(kg, &state->shadow); break; case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: - integrator_shade_shadow(kg, state, render_buffer); + integrator_shade_shadow(kg, &state->shadow, render_buffer); break; default: kernel_assert(0); diff --git a/intern/cycles/kernel/integrator/integrator_shade_shadow.h b/intern/cycles/kernel/integrator/integrator_shade_shadow.h index cdbe85f6b8c..94900754b76 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_shade_shadow.h @@ -30,7 +30,7 @@ ccl_device_inline bool shadow_intersections_has_remaining(const int num_hits) #ifdef __TRANSPARENT_SHADOWS__ ccl_device_inline float3 integrate_transparent_surface_shadow(KernelGlobals kg, - IntegratorState state, + IntegratorShadowState state, const int hit) { PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SURFACE); @@ -69,7 +69,7 @@ ccl_device_inline float3 integrate_transparent_surface_shadow(KernelGlobals kg, # ifdef __VOLUME__ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg, - IntegratorState state, + IntegratorShadowState state, const int hit, const int num_recorded_hits, ccl_private float3 *ccl_restrict @@ -97,14 +97,14 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg, shader_setup_from_volume(kg, shadow_sd, &ray); const float step_size = volume_stack_step_size( - kg, state, [=](const int i) { return integrator_state_read_shadow_volume_stack(state, i); }); + kg, [=](const int i) { return integrator_state_read_shadow_volume_stack(state, i); }); volume_shadow_heterogeneous(kg, state, &ray, shadow_sd, throughput, step_size); } # endif ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, - IntegratorState state, + IntegratorShadowState state, const int num_hits) { /* Accumulate shadow for transparent surfaces. */ @@ -158,7 +158,7 @@ ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, #endif /* __TRANSPARENT_SHADOWS__ */ ccl_device void integrator_shade_shadow(KernelGlobals kg, - IntegratorState state, + IntegratorShadowState state, ccl_global float *ccl_restrict render_buffer) { PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SETUP); diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index 08580645984..0108ba1373c 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -167,17 +167,20 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, light_sample_to_surface_shadow_ray(kg, sd, &ls, &ray); const bool is_light = light_sample_is_light(&ls); + /* Branch off shadow kernel. */ + INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + /* Copy volume stack and enter/exit volume. */ - integrator_state_copy_volume_stack_to_shadow(kg, state); + integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); if (is_transmission) { # ifdef __VOLUME__ - shadow_volume_stack_enter_exit(kg, state, sd); + shadow_volume_stack_enter_exit(kg, shadow_state, sd); # endif } /* Write shadow ray and associated state to global memory. */ - integrator_state_write_shadow_ray(kg, state, &ray); + integrator_state_write_shadow_ray(kg, shadow_state, &ray); /* Copy state from main path to shadow path. */ const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); @@ -191,20 +194,32 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, const float3 diffuse_glossy_ratio = (bounce == 0) ? bsdf_eval_diffuse_glossy_ratio(&bsdf_eval) : INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); - INTEGRATOR_STATE_WRITE(state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; } - INTEGRATOR_STATE_WRITE(state, shadow_path, flag) = shadow_flag; - INTEGRATOR_STATE_WRITE(state, shadow_path, bounce) = bounce; - INTEGRATOR_STATE_WRITE(state, shadow_path, transparent_bounce) = transparent_bounce; - INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) = throughput; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( + state, path, render_pixel_index); + INTEGRATOR_STATE_WRITE( + shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE(state, path, rng_offset) - + PRNG_BOUNCE_NUM * transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE( + state, path, rng_hash); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE( + state, path, sample); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_bounce) = INTEGRATOR_STATE( + state, path, diffuse_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, glossy_bounce) = INTEGRATOR_STATE( + state, path, glossy_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transmission_bounce) = INTEGRATOR_STATE( + state, path, transmission_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, throughput) = throughput; if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) { - INTEGRATOR_STATE_WRITE(state, shadow_path, unshadowed_throughput) = throughput; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unshadowed_throughput) = throughput; } - - /* Branch off shadow kernel. */ - INTEGRATOR_SHADOW_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); } #endif diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index d0dde815b5c..13a5e7bda05 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -71,7 +71,7 @@ typedef struct VolumeShaderCoefficients { /* Evaluate shader to get extinction coefficient at P. */ ccl_device_inline bool shadow_volume_shader_sample(KernelGlobals kg, - IntegratorState state, + IntegratorShadowState state, ccl_private ShaderData *ccl_restrict sd, ccl_private float3 *ccl_restrict extinction) { @@ -187,7 +187,7 @@ ccl_device void volume_shadow_homogeneous(KernelGlobals kg, IntegratorState stat /* heterogeneous volume: integrate stepping through the volume until we * reach the end, get absorbed entirely, or run out of iterations */ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, - IntegratorState state, + IntegratorShadowState state, ccl_private Ray *ccl_restrict ray, ccl_private ShaderData *ccl_restrict sd, ccl_private float3 *ccl_restrict throughput, @@ -775,8 +775,11 @@ ccl_device_forceinline void integrate_volume_direct_light( light_sample_to_volume_shadow_ray(kg, sd, ls, P, &ray); const bool is_light = light_sample_is_light(ls); + /* Branch off shadow kernel. */ + INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + /* Write shadow ray and associated state to global memory. */ - integrator_state_write_shadow_ray(kg, state, &ray); + integrator_state_write_shadow_ray(kg, shadow_state, &ray); /* Copy state from main path to shadow path. */ const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); @@ -790,22 +793,34 @@ ccl_device_forceinline void integrate_volume_direct_light( const float3 diffuse_glossy_ratio = (bounce == 0) ? one_float3() : INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); - INTEGRATOR_STATE_WRITE(state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; } - INTEGRATOR_STATE_WRITE(state, shadow_path, flag) = shadow_flag; - INTEGRATOR_STATE_WRITE(state, shadow_path, bounce) = bounce; - INTEGRATOR_STATE_WRITE(state, shadow_path, transparent_bounce) = transparent_bounce; - INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) = throughput_phase; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( + state, path, render_pixel_index); + INTEGRATOR_STATE_WRITE( + shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE(state, path, rng_offset) - + PRNG_BOUNCE_NUM * transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE( + state, path, rng_hash); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE( + state, path, sample); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_bounce) = INTEGRATOR_STATE( + state, path, diffuse_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, glossy_bounce) = INTEGRATOR_STATE( + state, path, glossy_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transmission_bounce) = INTEGRATOR_STATE( + state, path, transmission_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, throughput) = throughput_phase; if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) { - INTEGRATOR_STATE_WRITE(state, shadow_path, unshadowed_throughput) = throughput; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unshadowed_throughput) = throughput; } - integrator_state_copy_volume_stack_to_shadow(kg, state); - - /* Branch off shadow kernel. */ - INTEGRATOR_SHADOW_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); } # endif @@ -902,7 +917,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg, /* Step through volume. */ const float step_size = volume_stack_step_size( - kg, state, [=](const int i) { return integrator_state_read_volume_stack(state, i); }); + kg, [=](const int i) { return integrator_state_read_volume_stack(state, i); }); /* TODO: expensive to zero closures? */ VolumeIntegrateResult result = {}; diff --git a/intern/cycles/kernel/integrator/integrator_shadow_state_template.h b/intern/cycles/kernel/integrator/integrator_shadow_state_template.h new file mode 100644 index 00000000000..bc35b644ee1 --- /dev/null +++ b/intern/cycles/kernel/integrator/integrator_shadow_state_template.h @@ -0,0 +1,83 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/********************************* Shadow Path State **************************/ + +KERNEL_STRUCT_BEGIN(shadow_path) +/* Index of a pixel within the device render buffer. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, render_pixel_index, KERNEL_FEATURE_PATH_TRACING) +/* Current sample number. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, sample, KERNEL_FEATURE_PATH_TRACING) +/* Random number generator seed. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, rng_hash, KERNEL_FEATURE_PATH_TRACING) +/* Random number dimension offset. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, rng_offset, KERNEL_FEATURE_PATH_TRACING) +/* Current ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current transparent ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, transparent_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current diffuse ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, diffuse_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current glossy ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, glossy_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current transmission ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, transmission_bounce, KERNEL_FEATURE_PATH_TRACING) +/* DeviceKernel bit indicating queued kernels. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) +/* enum PathRayFlag */ +KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) +/* Throughput. */ +KERNEL_STRUCT_MEMBER(shadow_path, float3, throughput, KERNEL_FEATURE_PATH_TRACING) +/* Throughput for shadow pass. */ +KERNEL_STRUCT_MEMBER(shadow_path, float3, unshadowed_throughput, KERNEL_FEATURE_SHADOW_PASS) +/* Ratio of throughput to distinguish diffuse and glossy render passes. */ +KERNEL_STRUCT_MEMBER(shadow_path, float3, diffuse_glossy_ratio, KERNEL_FEATURE_LIGHT_PASSES) +/* Number of intersections found by ray-tracing. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, num_hits, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(shadow_path) + +/********************************** Shadow Ray *******************************/ + +KERNEL_STRUCT_BEGIN(shadow_ray) +KERNEL_STRUCT_MEMBER(shadow_ray, float3, P, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float3, D, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(shadow_ray) + +/*********************** Shadow Intersection result **************************/ + +/* Result from scene intersection. */ +KERNEL_STRUCT_BEGIN(shadow_isect) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, u, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, v, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, prim, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, object, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, type, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END_ARRAY(shadow_isect, + INTEGRATOR_SHADOW_ISECT_SIZE_CPU, + INTEGRATOR_SHADOW_ISECT_SIZE_GPU) + +/**************************** Shadow Volume Stack *****************************/ + +KERNEL_STRUCT_BEGIN(shadow_volume_stack) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, object, KERNEL_FEATURE_VOLUME) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, shader, KERNEL_FEATURE_VOLUME) +KERNEL_STRUCT_END_ARRAY(shadow_volume_stack, + KERNEL_STRUCT_VOLUME_STACK_SIZE, + KERNEL_STRUCT_VOLUME_STACK_SIZE) diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h index 3aab456a021..84f34c6b986 100644 --- a/intern/cycles/kernel/integrator/integrator_state.h +++ b/intern/cycles/kernel/integrator/integrator_state.h @@ -66,6 +66,25 @@ CCL_NAMESPACE_BEGIN /* Integrator State * * CPU rendering path state with AoS layout. */ +typedef struct IntegratorShadowStateCPU { +#define KERNEL_STRUCT_BEGIN(name) struct { +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) type name; +#define KERNEL_STRUCT_ARRAY_MEMBER KERNEL_STRUCT_MEMBER +#define KERNEL_STRUCT_END(name) \ + } \ + name; +#define KERNEL_STRUCT_END_ARRAY(name, cpu_size, gpu_size) \ + } \ + name[cpu_size]; +#define KERNEL_STRUCT_VOLUME_STACK_SIZE MAX_VOLUME_STACK_SIZE +#include "kernel/integrator/integrator_shadow_state_template.h" +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_ARRAY_MEMBER +#undef KERNEL_STRUCT_END +#undef KERNEL_STRUCT_END_ARRAY +} IntegratorShadowStateCPU; + typedef struct IntegratorStateCPU { #define KERNEL_STRUCT_BEGIN(name) struct { #define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) type name; @@ -84,6 +103,8 @@ typedef struct IntegratorStateCPU { #undef KERNEL_STRUCT_END #undef KERNEL_STRUCT_END_ARRAY #undef KERNEL_STRUCT_VOLUME_STACK_SIZE + + IntegratorShadowStateCPU shadow; } IntegratorStateCPU; /* Path Queue @@ -108,7 +129,11 @@ typedef struct IntegratorStateGPU { } \ name[gpu_size]; #define KERNEL_STRUCT_VOLUME_STACK_SIZE MAX_VOLUME_STACK_SIZE + #include "kernel/integrator/integrator_state_template.h" + +#include "kernel/integrator/integrator_shadow_state_template.h" + #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER #undef KERNEL_STRUCT_ARRAY_MEMBER @@ -122,7 +147,10 @@ typedef struct IntegratorStateGPU { /* Count number of kernels queued for specific shaders. */ ccl_global int *sort_key_counter[DEVICE_KERNEL_INTEGRATOR_NUM]; - /* Index of path which will be used by a next shadow catcher split. */ + /* Index of shadow path which will be used by a next shadow path. */ + ccl_global int *next_shadow_path_index; + + /* Index of main path which will be used by a next shadow catcher split. */ ccl_global int *next_shadow_catcher_path_index; } IntegratorStateGPU; @@ -140,6 +168,8 @@ typedef struct IntegratorStateGPU { typedef IntegratorStateCPU *ccl_restrict IntegratorState; typedef const IntegratorStateCPU *ccl_restrict ConstIntegratorState; +typedef IntegratorShadowStateCPU *ccl_restrict IntegratorShadowState; +typedef const IntegratorShadowStateCPU *ccl_restrict ConstIntegratorShadowState; # define INTEGRATOR_STATE_NULL nullptr @@ -157,6 +187,8 @@ typedef const IntegratorStateCPU *ccl_restrict ConstIntegratorState; typedef const int IntegratorState; typedef const int ConstIntegratorState; +typedef const int IntegratorShadowState; +typedef const int ConstIntegratorShadowState; # define INTEGRATOR_STATE_NULL -1 diff --git a/intern/cycles/kernel/integrator/integrator_state_flow.h b/intern/cycles/kernel/integrator/integrator_state_flow.h index 9829da875eb..df8fb5e0e46 100644 --- a/intern/cycles/kernel/integrator/integrator_state_flow.h +++ b/intern/cycles/kernel/integrator/integrator_state_flow.h @@ -63,10 +63,12 @@ CCL_NAMESPACE_BEGIN &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; -# define INTEGRATOR_SHADOW_PATH_INIT(next_kernel) \ +# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel) \ + IntegratorShadowState shadow_state = atomic_fetch_and_add_uint32( \ + &kernel_integrator_state.next_shadow_path_index[0], 1); \ atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ 1); \ - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; # define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ atomic_fetch_and_sub_uint32( \ &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ @@ -127,8 +129,9 @@ CCL_NAMESPACE_BEGIN (void)current_kernel; \ } -# define INTEGRATOR_SHADOW_PATH_INIT(next_kernel) \ - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; +# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel) \ + IntegratorShadowState shadow_state = &state->shadow; \ + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; # define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ { \ INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; \ diff --git a/intern/cycles/kernel/integrator/integrator_state_template.h b/intern/cycles/kernel/integrator/integrator_state_template.h index d9801574d4f..b1a6fd36fae 100644 --- a/intern/cycles/kernel/integrator/integrator_state_template.h +++ b/intern/cycles/kernel/integrator/integrator_state_template.h @@ -28,6 +28,8 @@ KERNEL_STRUCT_MEMBER(path, uint32_t, render_pixel_index, KERNEL_FEATURE_PATH_TRA KERNEL_STRUCT_MEMBER(path, uint16_t, sample, KERNEL_FEATURE_PATH_TRACING) /* Current ray bounce depth. */ KERNEL_STRUCT_MEMBER(path, uint16_t, bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current transparent ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, transparent_bounce, KERNEL_FEATURE_PATH_TRACING) /* Current diffuse ray bounce depth. */ KERNEL_STRUCT_MEMBER(path, uint16_t, diffuse_bounce, KERNEL_FEATURE_PATH_TRACING) /* Current glossy ray bounce depth. */ @@ -38,8 +40,6 @@ KERNEL_STRUCT_MEMBER(path, uint16_t, transmission_bounce, KERNEL_FEATURE_PATH_TR KERNEL_STRUCT_MEMBER(path, uint16_t, volume_bounce, KERNEL_FEATURE_PATH_TRACING) /* Current volume bounds ray bounce depth. */ KERNEL_STRUCT_MEMBER(path, uint16_t, volume_bounds_bounce, KERNEL_FEATURE_PATH_TRACING) -/* Current transparent ray bounce depth. */ -KERNEL_STRUCT_MEMBER(path, uint16_t, transparent_bounce, KERNEL_FEATURE_PATH_TRACING) /* DeviceKernel bit indicating queued kernels. */ KERNEL_STRUCT_MEMBER(path, uint16_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) /* Random number generator seed. */ @@ -107,57 +107,3 @@ KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, shader, KERNEL_FEATURE_VOLUME) KERNEL_STRUCT_END_ARRAY(volume_stack, KERNEL_STRUCT_VOLUME_STACK_SIZE, KERNEL_STRUCT_VOLUME_STACK_SIZE) - -/********************************* Shadow Path State **************************/ - -KERNEL_STRUCT_BEGIN(shadow_path) -/* Current ray bounce depth. */ -KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, bounce, KERNEL_FEATURE_PATH_TRACING) -/* Current transparent ray bounce depth. */ -KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, transparent_bounce, KERNEL_FEATURE_PATH_TRACING) -/* DeviceKernel bit indicating queued kernels. */ -KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) -/* enum PathRayFlag */ -KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) -/* Throughput. */ -KERNEL_STRUCT_MEMBER(shadow_path, float3, throughput, KERNEL_FEATURE_PATH_TRACING) -/* Throughput for shadow pass. */ -KERNEL_STRUCT_MEMBER(shadow_path, float3, unshadowed_throughput, KERNEL_FEATURE_SHADOW_PASS) -/* Ratio of throughput to distinguish diffuse and glossy render passes. */ -KERNEL_STRUCT_MEMBER(shadow_path, float3, diffuse_glossy_ratio, KERNEL_FEATURE_LIGHT_PASSES) -/* Number of intersections found by ray-tracing. */ -KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, num_hits, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_END(shadow_path) - -/********************************** Shadow Ray *******************************/ - -KERNEL_STRUCT_BEGIN(shadow_ray) -KERNEL_STRUCT_MEMBER(shadow_ray, float3, P, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER(shadow_ray, float3, D, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER(shadow_ray, float, t, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_END(shadow_ray) - -/*********************** Shadow Intersection result **************************/ - -/* Result from scene intersection. */ -KERNEL_STRUCT_BEGIN(shadow_isect) -KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, t, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, u, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, v, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, prim, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, object, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, type, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_END_ARRAY(shadow_isect, - INTEGRATOR_SHADOW_ISECT_SIZE_CPU, - INTEGRATOR_SHADOW_ISECT_SIZE_GPU) - -/**************************** Shadow Volume Stack *****************************/ - -KERNEL_STRUCT_BEGIN(shadow_volume_stack) -KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, object, KERNEL_FEATURE_VOLUME) -KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, shader, KERNEL_FEATURE_VOLUME) -KERNEL_STRUCT_END_ARRAY(shadow_volume_stack, - KERNEL_STRUCT_VOLUME_STACK_SIZE, - KERNEL_STRUCT_VOLUME_STACK_SIZE) diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index dacc21e6eeb..5bcb9cc2d67 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -50,7 +50,7 @@ ccl_device_forceinline void integrator_state_read_ray(KernelGlobals kg, /* Shadow Ray */ ccl_device_forceinline void integrator_state_write_shadow_ray( - KernelGlobals kg, IntegratorState state, ccl_private const Ray *ccl_restrict ray) + KernelGlobals kg, IntegratorShadowState state, ccl_private const Ray *ccl_restrict ray) { INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray->P; INTEGRATOR_STATE_WRITE(state, shadow_ray, D) = ray->D; @@ -60,7 +60,7 @@ ccl_device_forceinline void integrator_state_write_shadow_ray( } ccl_device_forceinline void integrator_state_read_shadow_ray(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorShadowState state, ccl_private Ray *ccl_restrict ray) { ray->P = INTEGRATOR_STATE(state, shadow_ray, P); @@ -122,7 +122,9 @@ ccl_device_forceinline bool integrator_state_volume_stack_is_empty(KernelGlobals /* Shadow Intersection */ ccl_device_forceinline void integrator_state_write_shadow_isect( - IntegratorState state, ccl_private const Intersection *ccl_restrict isect, const int index) + IntegratorShadowState state, + ccl_private const Intersection *ccl_restrict isect, + const int index) { INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, t) = isect->t; INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, u) = isect->u; @@ -133,7 +135,9 @@ ccl_device_forceinline void integrator_state_write_shadow_isect( } ccl_device_forceinline void integrator_state_read_shadow_isect( - ConstIntegratorState state, ccl_private Intersection *ccl_restrict isect, const int index) + ConstIntegratorShadowState state, + ccl_private Intersection *ccl_restrict isect, + const int index) { isect->prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, prim); isect->object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, object); @@ -143,8 +147,8 @@ ccl_device_forceinline void integrator_state_read_shadow_isect( isect->t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, t); } -ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(KernelGlobals kg, - IntegratorState state) +ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow( + KernelGlobals kg, IntegratorShadowState shadow_state, ConstIntegratorState state) { if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { int index = 0; @@ -152,9 +156,9 @@ ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(KernelG do { shader = INTEGRATOR_STATE_ARRAY(state, volume_stack, index, shader); - INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_volume_stack, index, object) = + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_volume_stack, index, object) = INTEGRATOR_STATE_ARRAY(state, volume_stack, index, object); - INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_volume_stack, index, shader) = shader; + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_volume_stack, index, shader) = shader; ++index; } while (shader != OBJECT_NONE); @@ -181,7 +185,7 @@ ccl_device_forceinline void integrator_state_copy_volume_stack(KernelGlobals kg, } ccl_device_forceinline VolumeStack -integrator_state_read_shadow_volume_stack(ConstIntegratorState state, int i) +integrator_state_read_shadow_volume_stack(ConstIntegratorShadowState state, int i) { VolumeStack entry = {INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, i, object), INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, i, shader)}; @@ -189,14 +193,14 @@ integrator_state_read_shadow_volume_stack(ConstIntegratorState state, int i) } ccl_device_forceinline bool integrator_state_shadow_volume_stack_is_empty( - KernelGlobals kg, ConstIntegratorState state) + KernelGlobals kg, ConstIntegratorShadowState state) { return (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) ? INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, 0, shader) == SHADER_NONE : true; } -ccl_device_forceinline void integrator_state_write_shadow_volume_stack(IntegratorState state, +ccl_device_forceinline void integrator_state_write_shadow_volume_stack(IntegratorShadowState state, int i, VolumeStack entry) { @@ -259,7 +263,6 @@ ccl_device_inline void integrator_state_move(KernelGlobals kg, integrator_state_copy_only(kg, to_state, state); INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; } #endif @@ -270,12 +273,11 @@ ccl_device_inline void integrator_state_shadow_catcher_split(KernelGlobals kg, IntegratorState state) { #if defined(__KERNEL_GPU__) - const IntegratorState to_state = atomic_fetch_and_add_uint32( + ConstIntegratorState to_state = atomic_fetch_and_add_uint32( &kernel_integrator_state.next_shadow_catcher_path_index[0], 1); integrator_state_copy_only(kg, to_state, state); #else - IntegratorStateCPU *ccl_restrict to_state = state + 1; /* Only copy the required subset, since shadow intersections are big and irrelevant here. */ @@ -283,10 +285,99 @@ ccl_device_inline void integrator_state_shadow_catcher_split(KernelGlobals kg, to_state->ray = state->ray; to_state->isect = state->isect; integrator_state_copy_volume_stack(kg, to_state, state); - to_state->shadow_path = state->shadow_path; #endif INTEGRATOR_STATE_WRITE(to_state, path, flag) |= PATH_RAY_SHADOW_CATCHER_PASS; } +#ifdef __KERNEL_CPU__ +ccl_device_inline int integrator_state_bounce(ConstIntegratorState state, const int) +{ + return INTEGRATOR_STATE(state, path, bounce); +} + +ccl_device_inline int integrator_state_bounce(ConstIntegratorShadowState state, const int) +{ + return INTEGRATOR_STATE(state, shadow_path, bounce); +} + +ccl_device_inline int integrator_state_diffuse_bounce(ConstIntegratorState state, const int) +{ + return INTEGRATOR_STATE(state, path, diffuse_bounce); +} + +ccl_device_inline int integrator_state_diffuse_bounce(ConstIntegratorShadowState state, const int) +{ + return INTEGRATOR_STATE(state, shadow_path, diffuse_bounce); +} + +ccl_device_inline int integrator_state_glossy_bounce(ConstIntegratorState state, const int) +{ + return INTEGRATOR_STATE(state, path, glossy_bounce); +} + +ccl_device_inline int integrator_state_glossy_bounce(ConstIntegratorShadowState state, const int) +{ + return INTEGRATOR_STATE(state, shadow_path, glossy_bounce); +} + +ccl_device_inline int integrator_state_transmission_bounce(ConstIntegratorState state, const int) +{ + return INTEGRATOR_STATE(state, path, transmission_bounce); +} + +ccl_device_inline int integrator_state_transmission_bounce(ConstIntegratorShadowState state, + const int) +{ + return INTEGRATOR_STATE(state, shadow_path, transmission_bounce); +} + +ccl_device_inline int integrator_state_transparent_bounce(ConstIntegratorState state, const int) +{ + return INTEGRATOR_STATE(state, path, transparent_bounce); +} + +ccl_device_inline int integrator_state_transparent_bounce(ConstIntegratorShadowState state, + const int) +{ + return INTEGRATOR_STATE(state, shadow_path, transparent_bounce); +} +#else +ccl_device_inline int integrator_state_bounce(ConstIntegratorShadowState state, + const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(state, shadow_path, bounce) : + INTEGRATOR_STATE(state, path, bounce); +} + +ccl_device_inline int integrator_state_diffuse_bounce(ConstIntegratorShadowState state, + const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(state, shadow_path, diffuse_bounce) : + INTEGRATOR_STATE(state, path, diffuse_bounce); +} + +ccl_device_inline int integrator_state_glossy_bounce(ConstIntegratorShadowState state, + const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(state, shadow_path, glossy_bounce) : + INTEGRATOR_STATE(state, path, glossy_bounce); +} + +ccl_device_inline int integrator_state_transmission_bounce(ConstIntegratorShadowState state, + const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW) ? + INTEGRATOR_STATE(state, shadow_path, transmission_bounce) : + INTEGRATOR_STATE(state, path, transmission_bounce); +} + +ccl_device_inline int integrator_state_transparent_bounce(ConstIntegratorShadowState state, + const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(state, shadow_path, transparent_bounce) : + INTEGRATOR_STATE(state, path, transparent_bounce); +} +#endif + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_volume_stack.h b/intern/cycles/kernel/integrator/integrator_volume_stack.h index e3a4546508f..cf69826ffff 100644 --- a/intern/cycles/kernel/integrator/integrator_volume_stack.h +++ b/intern/cycles/kernel/integrator/integrator_volume_stack.h @@ -98,7 +98,7 @@ ccl_device void volume_stack_enter_exit(KernelGlobals kg, } ccl_device void shadow_volume_stack_enter_exit(KernelGlobals kg, - IntegratorState state, + IntegratorShadowState state, ccl_private const ShaderData *sd) { volume_stack_enter_exit( @@ -136,9 +136,7 @@ ccl_device_inline void volume_stack_clean(KernelGlobals kg, IntegratorState stat } template -ccl_device float volume_stack_step_size(KernelGlobals kg, - IntegratorState state, - StackReadOp stack_read) +ccl_device float volume_stack_step_size(KernelGlobals kg, StackReadOp stack_read) { float step_size = FLT_MAX; diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index 5f32150d33c..848aaa18aae 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -393,17 +393,20 @@ ccl_device_inline void kernel_accum_emission_or_background_pass(KernelGlobals kg /* Write light contribution to render buffer. */ ccl_device_inline void kernel_accum_light(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorShadowState state, ccl_global float *ccl_restrict render_buffer) { /* The throughput for shadow paths already contains the light shader evaluation. */ float3 contribution = INTEGRATOR_STATE(state, shadow_path, throughput); kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, shadow_path, bounce)); - ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, shadow_path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + ccl_global float *buffer = render_buffer + render_buffer_offset; const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag); - const int sample = INTEGRATOR_STATE(state, path, sample); + const int sample = INTEGRATOR_STATE(state, shadow_path, sample); kernel_accum_combined_pass(kg, path_flag, sample, contribution, buffer); diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h index 66eb468fdca..fa8de14916e 100644 --- a/intern/cycles/kernel/kernel_path_state.h +++ b/intern/cycles/kernel/kernel_path_state.h @@ -26,7 +26,9 @@ CCL_NAMESPACE_BEGIN ccl_device_inline void path_state_init_queues(IntegratorState state) { INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; +#ifdef __KERNEL_CPU__ + INTEGRATOR_STATE_WRITE(&state->shadow, shadow_path, queued_kernel) = 0; +#endif } /* Minimalistic initialization of the path state, which is needed for early outputs in the @@ -293,16 +295,15 @@ ccl_device_inline void path_state_rng_load(ConstIntegratorState state, rng_state->sample = INTEGRATOR_STATE(state, path, sample); } -ccl_device_inline void shadow_path_state_rng_load(ConstIntegratorState state, +ccl_device_inline void shadow_path_state_rng_load(ConstIntegratorShadowState state, ccl_private RNGState *rng_state) { - const uint shadow_bounces = INTEGRATOR_STATE(state, shadow_path, transparent_bounce) - - INTEGRATOR_STATE(state, path, transparent_bounce); + const uint shadow_bounces = INTEGRATOR_STATE(state, shadow_path, transparent_bounce); - rng_state->rng_hash = INTEGRATOR_STATE(state, path, rng_hash); - rng_state->rng_offset = INTEGRATOR_STATE(state, path, rng_offset) + + rng_state->rng_hash = INTEGRATOR_STATE(state, shadow_path, rng_hash); + rng_state->rng_offset = INTEGRATOR_STATE(state, shadow_path, rng_offset) + PRNG_BOUNCE_NUM * shadow_bounces; - rng_state->sample = INTEGRATOR_STATE(state, path, sample); + rng_state->sample = INTEGRATOR_STATE(state, shadow_path, sample); } ccl_device_inline float path_state_rng_1D(KernelGlobals kg, diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 4a5a5309c61..d25191b72cf 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -575,9 +575,9 @@ ccl_device float3 shader_holdout_apply(KernelGlobals kg, ccl_private ShaderData /* Surface Evaluation */ -template +template ccl_device void shader_eval_surface(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorGenericState state, ccl_private ShaderData *ccl_restrict sd, ccl_global float *ccl_restrict buffer, uint32_t path_flag) @@ -753,9 +753,9 @@ ccl_device int shader_phase_sample_closure(KernelGlobals kg, /* Volume Evaluation */ -template +template ccl_device_inline void shader_eval_volume(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorGenericState state, ccl_private ShaderData *ccl_restrict sd, const uint32_t path_flag, StackReadOp stack_read) @@ -831,8 +831,9 @@ ccl_device_inline void shader_eval_volume(KernelGlobals kg, /* Displacement Evaluation */ +template ccl_device void shader_eval_displacement(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorGenericState state, ccl_private ShaderData *sd) { sd->num_closure = 0; diff --git a/intern/cycles/kernel/kernel_shadow_catcher.h b/intern/cycles/kernel/kernel_shadow_catcher.h index 00dddb5b198..9bed140b395 100644 --- a/intern/cycles/kernel/kernel_shadow_catcher.h +++ b/intern/cycles/kernel/kernel_shadow_catcher.h @@ -62,7 +62,7 @@ ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(KernelGlobals ccl_device_inline bool kernel_shadow_catcher_path_can_split(KernelGlobals kg, ConstIntegratorState state) { - if (INTEGRATOR_PATH_IS_TERMINATED && INTEGRATOR_SHADOW_PATH_IS_TERMINATED) { + if (INTEGRATOR_PATH_IS_TERMINATED) { return false; } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 3e276c24cdd..edae158f403 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -812,6 +812,7 @@ typedef struct ccl_align(16) ShaderData #ifdef __OSL__ const struct KernelGlobalsCPU *osl_globals; const struct IntegratorStateCPU *osl_path_state; + const struct IntegratorShadowStateCPU *osl_shadow_path_state; #endif /* LCG state for closures that require additional random numbers. */ diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index bb7655fbe9a..cbe1bf1bfc0 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -1015,31 +1015,44 @@ bool OSLRenderServices::get_background_attribute(const KernelGlobalsCPU *kg, else if (name == u_path_ray_depth) { /* Ray Depth */ const IntegratorStateCPU *state = sd->osl_path_state; - int f = state->path.bounce; + const IntegratorShadowStateCPU *shadow_state = sd->osl_shadow_path_state; + int f = (state) ? state->path.bounce : (shadow_state) ? shadow_state->shadow_path.bounce : 0; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_diffuse_depth) { /* Diffuse Ray Depth */ const IntegratorStateCPU *state = sd->osl_path_state; - int f = state->path.diffuse_bounce; + const IntegratorShadowStateCPU *shadow_state = sd->osl_shadow_path_state; + int f = (state) ? state->path.diffuse_bounce : + (shadow_state) ? shadow_state->shadow_path.diffuse_bounce : + 0; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_glossy_depth) { /* Glossy Ray Depth */ const IntegratorStateCPU *state = sd->osl_path_state; - int f = state->path.glossy_bounce; + const IntegratorShadowStateCPU *shadow_state = sd->osl_shadow_path_state; + int f = (state) ? state->path.glossy_bounce : + (shadow_state) ? shadow_state->shadow_path.glossy_bounce : + 0; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_transmission_depth) { /* Transmission Ray Depth */ const IntegratorStateCPU *state = sd->osl_path_state; - int f = state->path.transmission_bounce; + const IntegratorShadowStateCPU *shadow_state = sd->osl_shadow_path_state; + int f = (state) ? state->path.transmission_bounce : + (shadow_state) ? shadow_state->shadow_path.transmission_bounce : + 0; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_transparent_depth) { /* Transparent Ray Depth */ const IntegratorStateCPU *state = sd->osl_path_state; - int f = state->path.transparent_bounce; + const IntegratorShadowStateCPU *shadow_state = sd->osl_shadow_path_state; + int f = (state) ? state->path.transparent_bounce : + (shadow_state) ? shadow_state->shadow_path.transparent_bounce : + 0; return set_attribute_int(f, type, derivatives, val); } else if (name == u_ndc) { @@ -1228,34 +1241,38 @@ bool OSLRenderServices::texture(ustring filename, /* Bevel shader hack. */ if (nchannels >= 3) { const IntegratorStateCPU *state = sd->osl_path_state; - int num_samples = (int)s; - float radius = t; - float3 N = svm_bevel(kernel_globals, state, sd, radius, num_samples); - result[0] = N.x; - result[1] = N.y; - result[2] = N.z; - status = true; + if (state) { + int num_samples = (int)s; + float radius = t; + float3 N = svm_bevel(kernel_globals, state, sd, radius, num_samples); + result[0] = N.x; + result[1] = N.y; + result[2] = N.z; + status = true; + } } break; } case OSLTextureHandle::AO: { /* AO shader hack. */ const IntegratorStateCPU *state = sd->osl_path_state; - int num_samples = (int)s; - float radius = t; - float3 N = make_float3(dsdx, dtdx, dsdy); - int flags = 0; - if ((int)dtdy) { - flags |= NODE_AO_INSIDE; + if (state) { + int num_samples = (int)s; + float radius = t; + float3 N = make_float3(dsdx, dtdx, dsdy); + int flags = 0; + if ((int)dtdy) { + flags |= NODE_AO_INSIDE; + } + if ((int)options.sblur) { + flags |= NODE_AO_ONLY_LOCAL; + } + if ((int)options.tblur) { + flags |= NODE_AO_GLOBAL_RADIUS; + } + result[0] = svm_ao(kernel_globals, state, sd, N, radius, num_samples, flags); + status = true; } - if ((int)options.sblur) { - flags |= NODE_AO_ONLY_LOCAL; - } - if ((int)options.tblur) { - flags |= NODE_AO_GLOBAL_RADIUS; - } - result[0] = svm_ao(kernel_globals, state, sd, N, radius, num_samples, flags); - status = true; break; } case OSLTextureHandle::SVM: { diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp index 4c067e88ab6..fba207e7230 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/osl_shader.cpp @@ -89,7 +89,7 @@ void OSLShader::thread_free(KernelGlobalsCPU *kg) static void shaderdata_to_shaderglobals(const KernelGlobalsCPU *kg, ShaderData *sd, - const IntegratorStateCPU *state, + const void *state, uint32_t path_flag, OSLThreadData *tdata) { @@ -134,7 +134,12 @@ static void shaderdata_to_shaderglobals(const KernelGlobalsCPU *kg, /* Used by render-services. */ sd->osl_globals = kg; - sd->osl_path_state = state; + if (path_flag & PATH_RAY_SHADOW) { + sd->osl_shadow_path_state = (const IntegratorShadowStateCPU *)state; + } + else { + sd->osl_path_state = (const IntegratorStateCPU *)state; + } } /* Surface */ @@ -175,7 +180,7 @@ static void flatten_surface_closure_tree(ShaderData *sd, } void OSLShader::eval_surface(const KernelGlobalsCPU *kg, - const IntegratorStateCPU *state, + const void *state, ShaderData *sd, uint32_t path_flag) { @@ -283,7 +288,7 @@ static void flatten_background_closure_tree(ShaderData *sd, } void OSLShader::eval_background(const KernelGlobalsCPU *kg, - const IntegratorStateCPU *state, + const void *state, ShaderData *sd, uint32_t path_flag) { @@ -341,7 +346,7 @@ static void flatten_volume_closure_tree(ShaderData *sd, } void OSLShader::eval_volume(const KernelGlobalsCPU *kg, - const IntegratorStateCPU *state, + const void *state, ShaderData *sd, uint32_t path_flag) { @@ -366,9 +371,7 @@ void OSLShader::eval_volume(const KernelGlobalsCPU *kg, /* Displacement */ -void OSLShader::eval_displacement(const KernelGlobalsCPU *kg, - const IntegratorStateCPU *state, - ShaderData *sd) +void OSLShader::eval_displacement(const KernelGlobalsCPU *kg, const void *state, ShaderData *sd) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; diff --git a/intern/cycles/kernel/osl/osl_shader.h b/intern/cycles/kernel/osl/osl_shader.h index 2b3810b0a33..037a18a1f19 100644 --- a/intern/cycles/kernel/osl/osl_shader.h +++ b/intern/cycles/kernel/osl/osl_shader.h @@ -55,20 +55,18 @@ class OSLShader { /* eval */ static void eval_surface(const KernelGlobalsCPU *kg, - const IntegratorStateCPU *state, + const void *state, ShaderData *sd, uint32_t path_flag); static void eval_background(const KernelGlobalsCPU *kg, - const IntegratorStateCPU *state, + const void *state, ShaderData *sd, uint32_t path_flag); static void eval_volume(const KernelGlobalsCPU *kg, - const IntegratorStateCPU *state, + const void *state, ShaderData *sd, uint32_t path_flag); - static void eval_displacement(const KernelGlobalsCPU *kg, - const IntegratorStateCPU *state, - ShaderData *sd); + static void eval_displacement(const KernelGlobalsCPU *kg, const void *state, ShaderData *sd); /* attributes */ static int find_attribute(const KernelGlobalsCPU *kg, diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 57879dc238f..472f3517839 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -225,9 +225,9 @@ CCL_NAMESPACE_END CCL_NAMESPACE_BEGIN /* Main Interpreter Loop */ -template +template ccl_device void svm_eval_nodes(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorGenericState state, ShaderData *sd, ccl_global float *render_buffer, uint32_t path_flag) diff --git a/intern/cycles/kernel/svm/svm_ao.h b/intern/cycles/kernel/svm/svm_ao.h index a1efd2f0a43..4cfef7bc204 100644 --- a/intern/cycles/kernel/svm/svm_ao.h +++ b/intern/cycles/kernel/svm/svm_ao.h @@ -21,17 +21,17 @@ CCL_NAMESPACE_BEGIN #ifdef __SHADER_RAYTRACE__ # ifdef __KERNEL_OPTIX__ -extern "C" __device__ float __direct_callable__svm_node_ao(KernelGlobals kg, - ConstIntegratorState state, +extern "C" __device__ float __direct_callable__svm_node_ao( # else -ccl_device float svm_ao(KernelGlobals kg, - ConstIntegratorState state, +ccl_device float svm_ao( # endif - ccl_private ShaderData *sd, - float3 N, - float max_dist, - int num_samples, - int flags) + KernelGlobals kg, + ConstIntegratorState state, + ccl_private ShaderData *sd, + float3 N, + float max_dist, + int num_samples, + int flags) { if (flags & NODE_AO_GLOBAL_RADIUS) { max_dist = kernel_data.integrator.ao_bounces_distance; @@ -91,7 +91,7 @@ ccl_device float svm_ao(KernelGlobals kg, return ((float)unoccluded) / num_samples; } -template +template # if defined(__KERNEL_OPTIX__) ccl_device_inline # else @@ -99,7 +99,7 @@ ccl_device_noinline # endif void svm_node_ao(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorGenericState state, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h index 0d6395d52c0..833a6443b3c 100644 --- a/intern/cycles/kernel/svm/svm_aov.h +++ b/intern/cycles/kernel/svm/svm_aov.h @@ -26,9 +26,9 @@ ccl_device_inline bool svm_node_aov_check(const uint32_t path_flag, return ((render_buffer != NULL) && is_primary); } -template +template ccl_device void svm_node_aov_color(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorGenericState state, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, @@ -46,9 +46,9 @@ ccl_device void svm_node_aov_color(KernelGlobals kg, } } -template +template ccl_device void svm_node_aov_value(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorGenericState state, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/svm_bevel.h index 3ce3af20795..292887beedf 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/svm_bevel.h @@ -99,15 +99,15 @@ ccl_device void svm_bevel_cubic_sample(const float radius, */ # ifdef __KERNEL_OPTIX__ -extern "C" __device__ float3 __direct_callable__svm_node_bevel(KernelGlobals kg, - ConstIntegratorState state, +extern "C" __device__ float3 __direct_callable__svm_node_bevel( # else -ccl_device float3 svm_bevel(KernelGlobals kg, - ConstIntegratorState state, +ccl_device float3 svm_bevel( # endif - ccl_private ShaderData *sd, - float radius, - int num_samples) + KernelGlobals kg, + ConstIntegratorState state, + ccl_private ShaderData *sd, + float radius, + int num_samples) { /* Early out if no sampling needed. */ if (radius <= 0.0f || num_samples < 1 || sd->object == OBJECT_NONE) { @@ -282,7 +282,7 @@ ccl_device float3 svm_bevel(KernelGlobals kg, return is_zero(N) ? sd->N : (sd->flag & SD_BACKFACING) ? -N : N; } -template +template # if defined(__KERNEL_OPTIX__) ccl_device_inline # else @@ -290,7 +290,7 @@ ccl_device_noinline # endif void svm_node_bevel(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorGenericState state, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) diff --git a/intern/cycles/kernel/svm/svm_light_path.h b/intern/cycles/kernel/svm/svm_light_path.h index c61ace9757a..5e1fc4f671c 100644 --- a/intern/cycles/kernel/svm/svm_light_path.h +++ b/intern/cycles/kernel/svm/svm_light_path.h @@ -18,9 +18,9 @@ CCL_NAMESPACE_BEGIN /* Light Path Node */ -template +template ccl_device_noinline void svm_node_light_path(KernelGlobals kg, - ConstIntegratorState state, + ConstIntegratorGenericState state, ccl_private const ShaderData *sd, ccl_private float *stack, uint type, @@ -64,48 +64,43 @@ ccl_device_noinline void svm_node_light_path(KernelGlobals kg, /* Read bounce from difference location depending if this is a shadow * path. It's a bit dubious to have integrate state details leak into * this function but hard to avoid currently. */ - int bounce = 0; IF_KERNEL_NODES_FEATURE(LIGHT_PATH) { - bounce = (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(state, shadow_path, bounce) : - INTEGRATOR_STATE(state, path, bounce); + info = (float)integrator_state_bounce(state, path_flag); } /* For background, light emission and shadow evaluation we from a * surface or volume we are effective one bounce further. */ if (path_flag & (PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { - bounce++; + info += 1.0f; } - - info = (float)bounce; break; } - /* TODO */ case NODE_LP_ray_transparent: { - int bounce = 0; IF_KERNEL_NODES_FEATURE(LIGHT_PATH) { - bounce = (path_flag & PATH_RAY_SHADOW) ? - INTEGRATOR_STATE(state, shadow_path, transparent_bounce) : - INTEGRATOR_STATE(state, path, transparent_bounce); + info = (float)integrator_state_transparent_bounce(state, path_flag); } - - info = (float)bounce; break; } -#if 0 case NODE_LP_ray_diffuse: - info = (float)state->diffuse_bounce; + IF_KERNEL_NODES_FEATURE(LIGHT_PATH) + { + info = (float)integrator_state_diffuse_bounce(state, path_flag); + } break; case NODE_LP_ray_glossy: - info = (float)state->glossy_bounce; + IF_KERNEL_NODES_FEATURE(LIGHT_PATH) + { + info = (float)integrator_state_glossy_bounce(state, path_flag); + } break; -#endif -#if 0 case NODE_LP_ray_transmission: - info = (float)state->transmission_bounce; + IF_KERNEL_NODES_FEATURE(LIGHT_PATH) + { + info = (float)integrator_state_transmission_bounce(state, path_flag); + } break; -#endif } stack_store_float(stack, out_offset, info); From d06828f0b8ebb083de59fd2cb8c5f8fe6af1da22 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 18 Oct 2021 19:20:09 +0200 Subject: [PATCH 0933/1500] Cycles: avoid intermediate stack array for writing shadow intersections Helps save one OptiX payload and is a bit more efficient. Differential Revision: https://developer.blender.org/D12909 --- intern/cycles/kernel/bvh/bvh.h | 19 ++++--- intern/cycles/kernel/bvh/bvh_shadow_all.h | 15 ++--- intern/cycles/kernel/bvh/bvh_util.h | 29 +--------- intern/cycles/kernel/device/optix/kernel.cu | 22 ++++---- .../integrator/integrator_intersect_shadow.h | 56 +++++++++++++++---- 5 files changed, 77 insertions(+), 64 deletions(-) diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index bdbd574bf0f..0d9ba7e6369 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -34,6 +34,8 @@ #include "kernel/bvh/bvh_types.h" #include "kernel/bvh/bvh_util.h" +#include "kernel/integrator/integrator_state_util.h" + CCL_NAMESPACE_BEGIN #ifndef __KERNEL_OPTIX__ @@ -361,15 +363,15 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, #ifdef __SHADOW_RECORD_ALL__ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, + IntegratorShadowState state, ccl_private const Ray *ray, - ccl_private Intersection *isect, uint visibility, uint max_hits, ccl_private uint *num_hits) { # ifdef __KERNEL_OPTIX__ - uint p0 = pointer_pack_to_uint_0(isect); - uint p1 = pointer_pack_to_uint_1(isect); + uint p0 = state; + uint p1 = 0; /* Unused */ uint p2 = 0; /* Number of hits. */ uint p3 = max_hits; uint p4 = visibility; @@ -412,7 +414,8 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, # ifdef __EMBREE__ if (kernel_data.bvh.scene) { CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_SHADOW_ALL); - ctx.isect_s = isect; + Intersection *isect_array = (Intersection *)state->shadow_isect; + ctx.isect_s = isect_array; ctx.max_hits = max_hits; IntersectContext rtc_ctx(&ctx); RTCRay rtc_ray; @@ -428,21 +431,21 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, if (kernel_data.bvh.have_motion) { # ifdef __HAIR__ if (kernel_data.bvh.have_curves) { - return bvh_intersect_shadow_all_hair_motion(kg, ray, isect, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all_hair_motion(kg, ray, state, visibility, max_hits, num_hits); } # endif /* __HAIR__ */ - return bvh_intersect_shadow_all_motion(kg, ray, isect, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all_motion(kg, ray, state, visibility, max_hits, num_hits); } # endif /* __OBJECT_MOTION__ */ # ifdef __HAIR__ if (kernel_data.bvh.have_curves) { - return bvh_intersect_shadow_all_hair(kg, ray, isect, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all_hair(kg, ray, state, visibility, max_hits, num_hits); } # endif /* __HAIR__ */ - return bvh_intersect_shadow_all(kg, ray, isect, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all(kg, ray, state, visibility, max_hits, num_hits); # endif /* __KERNEL_OPTIX__ */ } #endif /* __SHADOW_RECORD_ALL__ */ diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index 42ab9eda37e..b997235b6e4 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -38,7 +38,7 @@ ccl_device_inline #endif bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, ccl_private const Ray *ray, - ccl_private Intersection *isect_array, + IntegratorShadowState state, const uint visibility, const uint max_hits, ccl_private uint *num_hits) @@ -227,12 +227,13 @@ ccl_device_inline * the largest distance to potentially replace when another hit * is found. */ const int num_recorded_hits = min(max_hits, record_index); - float max_recorded_t = isect_array[0].t; + float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t); int max_recorded_hit = 0; for (int i = 1; i < num_recorded_hits; i++) { - if (isect_array[i].t > max_recorded_t) { - max_recorded_t = isect_array[i].t; + const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t); + if (isect_t > max_recorded_t) { + max_recorded_t = isect_t; max_recorded_hit = i; } } @@ -246,7 +247,7 @@ ccl_device_inline t_max_current = t_max_world * t_world_to_instance; } - isect_array[record_index] = isect; + integrator_state_write_shadow_isect(state, &isect, record_index); } prim_addr++; @@ -300,12 +301,12 @@ ccl_device_inline ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg, ccl_private const Ray *ray, - ccl_private Intersection *isect_array, + IntegratorShadowState state, const uint visibility, const uint max_hits, ccl_private uint *num_hits) { - return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect_array, visibility, max_hits, num_hits); + return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, state, visibility, max_hits, num_hits); } #undef BVH_FUNCTION_NAME diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index d45eeec4815..869311b38e2 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -71,8 +71,7 @@ ccl_device_inline float3 ray_offset(float3 P, float3 Ng) #endif } -#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__)) -/* TODO: Move to another file? */ +#if defined(__KERNEL_CPU__) ccl_device int intersections_compare(const void *a, const void *b) { const Intersection *isect_a = (const Intersection *)a; @@ -87,32 +86,6 @@ ccl_device int intersections_compare(const void *a, const void *b) } #endif -#if defined(__SHADOW_RECORD_ALL__) -ccl_device_inline void sort_intersections(ccl_private Intersection *hits, uint num_hits) -{ - kernel_assert(num_hits > 0); - -# ifdef __KERNEL_GPU__ - /* Use bubble sort which has more friendly memory pattern on GPU. */ - bool swapped; - do { - swapped = false; - for (int j = 0; j < num_hits - 1; ++j) { - if (hits[j].t > hits[j + 1].t) { - struct Intersection tmp = hits[j]; - hits[j] = hits[j + 1]; - hits[j + 1] = tmp; - swapped = true; - } - } - --num_hits; - } while (swapped); -# else - qsort(hits, num_hits, sizeof(Intersection), intersections_compare); -# endif -} -#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */ - /* For subsurface scattering, only sorting a small amount of intersections * so bubble sort is fine for CPU and GPU. */ ccl_device_inline void sort_intersections_and_normals(ccl_private Intersection *hits, diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu index e97b25d31a2..574f66ab708 100644 --- a/intern/cycles/kernel/device/optix/kernel.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -225,16 +225,17 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() optixSetPayload_2(num_hits + 1); - Intersection *const isect_array = get_payload_ptr_0(); + const IntegratorShadowState state = optixGetPayload_0(); if (record_index >= max_hits) { /* If maximum number of hits reached, find a hit to replace. */ - float max_recorded_t = isect_array[0].t; + float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t); int max_recorded_hit = 0; for (int i = 1; i < max_hits; i++) { - if (isect_array[i].t > max_recorded_t) { - max_recorded_t = isect_array[i].t; + const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t); + if (isect_t > max_recorded_t) { + max_recorded_t = isect_t; max_recorded_hit = i; } } @@ -248,13 +249,12 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() record_index = max_recorded_hit; } - Intersection *const isect = isect_array + record_index; - isect->u = u; - isect->v = v; - isect->t = optixGetRayTmax(); - isect->prim = prim; - isect->object = object; - isect->type = type; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, u) = u; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, v) = v; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, t) = optixGetRayTmax(); + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, prim) = prim; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, object) = object; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, type) = type; optixIgnoreIntersection(); # endif /* __TRANSPARENT_SHADOWS__ */ diff --git a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h index 9dc0eb02c9b..d5c6ec145f0 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h @@ -64,19 +64,61 @@ ccl_device_forceinline int integrate_shadow_max_transparent_hits(KernelGlobals k } #ifdef __TRANSPARENT_SHADOWS__ +# if defined(__KERNEL_CPU__) +ccl_device int shadow_intersections_compare(const void *a, const void *b) +{ + const Intersection *isect_a = (const Intersection *)a; + const Intersection *isect_b = (const Intersection *)b; + + if (isect_a->t < isect_b->t) + return -1; + else if (isect_a->t > isect_b->t) + return 1; + else + return 0; +} +# endif + +ccl_device_inline void sort_shadow_intersections(IntegratorShadowState state, uint num_hits) +{ + kernel_assert(num_hits > 0); + +# ifdef __KERNEL_GPU__ + /* Use bubble sort which has more friendly memory pattern on GPU. */ + bool swapped; + do { + swapped = false; + for (int j = 0; j < num_hits - 1; ++j) { + if (INTEGRATOR_STATE_ARRAY(state, shadow_isect, j, t) > + INTEGRATOR_STATE_ARRAY(state, shadow_isect, j + 1, t)) { + struct Intersection tmp_j ccl_optional_struct_init; + struct Intersection tmp_j_1 ccl_optional_struct_init; + integrator_state_read_shadow_isect(state, &tmp_j, j); + integrator_state_read_shadow_isect(state, &tmp_j_1, j + 1); + integrator_state_write_shadow_isect(state, &tmp_j_1, j); + integrator_state_write_shadow_isect(state, &tmp_j, j + 1); + swapped = true; + } + } + --num_hits; + } while (swapped); +# else + Intersection *isect_array = (Intersection *)state->shadow_isect; + qsort(isect_array, num_hits, sizeof(Intersection), shadow_intersections_compare); +# endif +} + ccl_device bool integrate_intersect_shadow_transparent(KernelGlobals kg, IntegratorShadowState state, ccl_private const Ray *ray, const uint visibility) { - Intersection isect[INTEGRATOR_SHADOW_ISECT_SIZE]; - /* Limit the number hits to the max transparent bounces allowed and the size that we * have available in the integrator state. */ const uint max_transparent_hits = integrate_shadow_max_transparent_hits(kg, state); const uint max_hits = min(max_transparent_hits, (uint)INTEGRATOR_SHADOW_ISECT_SIZE); uint num_hits = 0; - bool opaque_hit = scene_intersect_shadow_all(kg, ray, isect, visibility, max_hits, &num_hits); + bool opaque_hit = scene_intersect_shadow_all(kg, state, ray, visibility, max_hits, &num_hits); /* If number of hits exceed the transparent bounces limit, make opaque. */ if (num_hits > max_transparent_hits) { @@ -87,13 +129,7 @@ ccl_device bool integrate_intersect_shadow_transparent(KernelGlobals kg, uint num_recorded_hits = min(num_hits, max_hits); if (num_recorded_hits > 0) { - sort_intersections(isect, num_recorded_hits); - - /* Write intersection result into global integrator state memory. - * More efficient may be to do this directly from the intersection kernel. */ - for (int hit = 0; hit < num_recorded_hits; hit++) { - integrator_state_write_shadow_isect(state, &isect[hit], hit); - } + sort_shadow_intersections(state, num_recorded_hits); } INTEGRATOR_STATE_WRITE(state, shadow_path, num_hits) = num_hits; From fd77a28031daff3122ded3a1cb37a7fb44feedf6 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 20 Sep 2021 16:16:11 +0200 Subject: [PATCH 0934/1500] Cycles: bake transparent shadows for hair These transparent shadows can be expansive to evaluate. Especially on the GPU they can lead to poor occupancy when only some pixels require many kernel launches to trace and evaluate many layers of transparency. Baked transparency allows tracing a single ray in many cases by accumulating the throughput directly in the intersection program without recording hits or evaluating shaders. Transparency is baked at curve vertices and interpolated, for most shaders this will look practically the same as actual shader evaluation. Fixes T91428, performance regression with spring demo file due to transparent hair, and makes it render significantly faster than Blender 2.93. Differential Revision: https://developer.blender.org/D12880 --- intern/cycles/bvh/bvh_embree.cpp | 45 ++++--- intern/cycles/device/cpu/kernel.cpp | 1 + intern/cycles/device/cpu/kernel.h | 1 + intern/cycles/device/device_kernel.cpp | 2 + intern/cycles/integrator/shader_eval.cpp | 6 + intern/cycles/integrator/shader_eval.h | 1 + intern/cycles/kernel/bvh/bvh.h | 29 +++-- intern/cycles/kernel/bvh/bvh_embree.h | 8 +- intern/cycles/kernel/bvh/bvh_shadow_all.h | 83 ++++++++----- intern/cycles/kernel/bvh/bvh_util.h | 28 +++++ intern/cycles/kernel/device/cpu/kernel_arch.h | 5 + .../kernel/device/cpu/kernel_arch_impl.h | 13 ++ intern/cycles/kernel/device/gpu/kernel.h | 16 ++- intern/cycles/kernel/device/optix/kernel.cu | 40 ++++-- intern/cycles/kernel/geom/geom_shader_data.h | 75 ++++++++++++ .../integrator/integrator_intersect_shadow.h | 17 ++- .../integrator/integrator_shade_shadow.h | 10 +- .../kernel/integrator/integrator_state.h | 13 -- intern/cycles/kernel/kernel_bake.h | 21 ++++ intern/cycles/kernel/kernel_types.h | 11 ++ intern/cycles/render/attribute.cpp | 5 + intern/cycles/render/geometry.cpp | 61 ++++++---- intern/cycles/render/hair.cpp | 115 ++++++++++++++++++ intern/cycles/render/hair.h | 4 + 24 files changed, 500 insertions(+), 110 deletions(-) diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index 343d62dedf4..cd19e009bf3 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -80,31 +80,49 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) Intersection current_isect; kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - /* If no transparent shadows, all light is blocked. */ + /* If no transparent shadows or max number of hits exceeded, all light is blocked. */ const int flags = intersection_get_shader_flags(kg, current_isect.prim, current_isect.type); - if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->max_hits == 0) { + if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->num_hits >= ctx->max_hits) { ctx->opaque_hit = true; return; } + ++ctx->num_hits; + + /* Always use baked shadow transparency for curves. */ + if (current_isect.type & PRIMITIVE_ALL_CURVE) { + ctx->throughput *= intersection_curve_shadow_transparency( + kg, current_isect.object, current_isect.prim, current_isect.u); + + if (ctx->throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) { + ctx->opaque_hit = true; + return; + } + else { + *args->valid = 0; + return; + } + } + /* Test if we need to record this transparent intersection. */ - if (ctx->num_hits < ctx->max_hits || ray->tfar < ctx->max_t) { + const uint max_record_hits = min(ctx->max_hits, INTEGRATOR_SHADOW_ISECT_SIZE); + if (ctx->num_recorded_hits < max_record_hits || ray->tfar < ctx->max_t) { /* If maximum number of hits was reached, replace the intersection with the * highest distance. We want to find the N closest intersections. */ - const int num_recorded_hits = min(ctx->num_hits, ctx->max_hits); - int isect_index = num_recorded_hits; - if (num_recorded_hits + 1 >= ctx->max_hits) { + const uint num_recorded_hits = min(ctx->num_recorded_hits, max_record_hits); + uint isect_index = num_recorded_hits; + if (num_recorded_hits + 1 >= max_record_hits) { float max_t = ctx->isect_s[0].t; - int max_recorded_hit = 0; + uint max_recorded_hit = 0; - for (int i = 1; i < num_recorded_hits; ++i) { + for (uint i = 1; i < num_recorded_hits; ++i) { if (ctx->isect_s[i].t > max_t) { max_recorded_hit = i; max_t = ctx->isect_s[i].t; } } - if (num_recorded_hits >= ctx->max_hits) { + if (num_recorded_hits >= max_record_hits) { isect_index = max_recorded_hit; } @@ -118,10 +136,9 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) ctx->isect_s[isect_index] = current_isect; } - /* Always increase the number of hits, even beyond ray.max_hits so that - * the caller can detect this as and consider it opaque, or trace another - * ray. */ - ++ctx->num_hits; + /* Always increase the number of recorded hits, even beyond the maximum, + * so that we can detect this and trace another ray if needed. */ + ++ctx->num_recorded_hits; /* This tells Embree to continue tracing. */ *args->valid = 0; @@ -160,7 +177,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) if (ctx->lcg_state) { /* See triangle_intersect_subsurface() for the native equivalent. */ - for (int i = min(ctx->max_hits, local_isect->num_hits) - 1; i >= 0; --i) { + for (int i = min((int)ctx->max_hits, local_isect->num_hits) - 1; i >= 0; --i) { if (local_isect->hits[i].t == ray->tfar) { /* This tells Embree to continue tracing. */ *args->valid = 0; diff --git a/intern/cycles/device/cpu/kernel.cpp b/intern/cycles/device/cpu/kernel.cpp index 91282390e27..bbad2f3147d 100644 --- a/intern/cycles/device/cpu/kernel.cpp +++ b/intern/cycles/device/cpu/kernel.cpp @@ -44,6 +44,7 @@ CPUKernels::CPUKernels() /* Shader evaluation. */ REGISTER_KERNEL(shader_eval_displace), REGISTER_KERNEL(shader_eval_background), + REGISTER_KERNEL(shader_eval_curve_shadow_transparency), /* Adaptive sampling. */ REGISTER_KERNEL(adaptive_sampling_convergence_check), REGISTER_KERNEL(adaptive_sampling_filter_x), diff --git a/intern/cycles/device/cpu/kernel.h b/intern/cycles/device/cpu/kernel.h index 2db09057e44..3787fe37a33 100644 --- a/intern/cycles/device/cpu/kernel.h +++ b/intern/cycles/device/cpu/kernel.h @@ -58,6 +58,7 @@ class CPUKernels { ShaderEvalFunction shader_eval_displace; ShaderEvalFunction shader_eval_background; + ShaderEvalFunction shader_eval_curve_shadow_transparency; /* Adaptive stopping. */ diff --git a/intern/cycles/device/device_kernel.cpp b/intern/cycles/device/device_kernel.cpp index ceaddee4756..e0833331b77 100644 --- a/intern/cycles/device/device_kernel.cpp +++ b/intern/cycles/device/device_kernel.cpp @@ -74,6 +74,8 @@ const char *device_kernel_as_string(DeviceKernel kernel) return "shader_eval_displace"; case DEVICE_KERNEL_SHADER_EVAL_BACKGROUND: return "shader_eval_background"; + case DEVICE_KERNEL_SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY: + return "shader_eval_curve_shadow_transparency"; /* Film. */ diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp index cfc30056f7d..3de7bb6fd16 100644 --- a/intern/cycles/integrator/shader_eval.cpp +++ b/intern/cycles/integrator/shader_eval.cpp @@ -122,6 +122,9 @@ bool ShaderEval::eval_cpu(Device *device, case SHADER_EVAL_BACKGROUND: kernels.shader_eval_background(kg, input_data, output_data, work_index); break; + case SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY: + kernels.shader_eval_curve_shadow_transparency(kg, input_data, output_data, work_index); + break; } }); }); @@ -144,6 +147,9 @@ bool ShaderEval::eval_gpu(Device *device, case SHADER_EVAL_BACKGROUND: kernel = DEVICE_KERNEL_SHADER_EVAL_BACKGROUND; break; + case SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY: + kernel = DEVICE_KERNEL_SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY; + break; }; /* Create device queue. */ diff --git a/intern/cycles/integrator/shader_eval.h b/intern/cycles/integrator/shader_eval.h index 013fad17d4f..43b6b1bdd47 100644 --- a/intern/cycles/integrator/shader_eval.h +++ b/intern/cycles/integrator/shader_eval.h @@ -30,6 +30,7 @@ class Progress; enum ShaderEvalType { SHADER_EVAL_DISPLACE, SHADER_EVAL_BACKGROUND, + SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY, }; /* ShaderEval class performs shader evaluation for background light and displacement. */ diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index 0d9ba7e6369..813ac15711e 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -367,12 +367,13 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, ccl_private const Ray *ray, uint visibility, uint max_hits, - ccl_private uint *num_hits) + ccl_private uint *num_recorded_hits, + ccl_private float *throughput) { # ifdef __KERNEL_OPTIX__ uint p0 = state; - uint p1 = 0; /* Unused */ - uint p2 = 0; /* Number of hits. */ + uint p1 = __float_as_uint(1.0f); /* Throughput. */ + uint p2 = 0; /* Number of hits. */ uint p3 = max_hits; uint p4 = visibility; uint p5 = false; @@ -382,7 +383,6 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, ray_mask = 0xFF; } - *num_hits = 0; /* Initialize hit count to zero. */ optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, ray->P, ray->D, @@ -402,12 +402,14 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, p4, p5); - *num_hits = p2; + *num_recorded_hits = uint16_unpack_from_uint_0(p2); + *throughput = __uint_as_float(p1); return p5; # else /* __KERNEL_OPTIX__ */ if (!scene_intersect_valid(ray)) { - *num_hits = 0; + *num_recorded_hits = 0; + *throughput = 1.0f; return false; } @@ -422,7 +424,8 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, kernel_embree_setup_ray(*ray, rtc_ray, visibility); rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); - *num_hits = ctx.num_hits; + *num_recorded_hits = ctx.num_recorded_hits; + *throughput = ctx.throughput; return ctx.opaque_hit; } # endif /* __EMBREE__ */ @@ -431,21 +434,25 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, if (kernel_data.bvh.have_motion) { # ifdef __HAIR__ if (kernel_data.bvh.have_curves) { - return bvh_intersect_shadow_all_hair_motion(kg, ray, state, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all_hair_motion( + kg, ray, state, visibility, max_hits, num_recorded_hits, throughput); } # endif /* __HAIR__ */ - return bvh_intersect_shadow_all_motion(kg, ray, state, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all_motion( + kg, ray, state, visibility, max_hits, num_recorded_hits, throughput); } # endif /* __OBJECT_MOTION__ */ # ifdef __HAIR__ if (kernel_data.bvh.have_curves) { - return bvh_intersect_shadow_all_hair(kg, ray, state, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all_hair( + kg, ray, state, visibility, max_hits, num_recorded_hits, throughput); } # endif /* __HAIR__ */ - return bvh_intersect_shadow_all(kg, ray, state, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all( + kg, ray, state, visibility, max_hits, num_recorded_hits, throughput); # endif /* __KERNEL_OPTIX__ */ } #endif /* __SHADOW_RECORD_ALL__ */ diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/bvh_embree.h index 4f85e8bee4b..321e0f28dae 100644 --- a/intern/cycles/kernel/bvh/bvh_embree.h +++ b/intern/cycles/kernel/bvh/bvh_embree.h @@ -40,8 +40,10 @@ struct CCLIntersectContext { /* for shadow rays */ Intersection *isect_s; - int max_hits; - int num_hits; + uint max_hits; + uint num_hits; + uint num_recorded_hits; + float throughput; float max_t; bool opaque_hit; @@ -56,6 +58,8 @@ struct CCLIntersectContext { type = type_; max_hits = 1; num_hits = 0; + num_recorded_hits = 0; + throughput = 1.0f; max_t = FLT_MAX; opaque_hit = false; isect_s = NULL; diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index b997235b6e4..049c6a03fe0 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -41,7 +41,8 @@ ccl_device_inline IntegratorShadowState state, const uint visibility, const uint max_hits, - ccl_private uint *num_hits) + ccl_private uint *num_recorded_hits, + ccl_private float *throughput) { /* todo: * - likely and unlikely for if() statements @@ -61,6 +62,7 @@ ccl_device_inline float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); int object = OBJECT_NONE; + uint num_hits = 0; #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; @@ -77,7 +79,8 @@ ccl_device_inline * otherwise. */ float t_world_to_instance = 1.0f; - *num_hits = 0; + *num_recorded_hits = 0; + *throughput = 1.0f; /* traversal loop */ do { @@ -212,42 +215,62 @@ ccl_device_inline * the primitive has a transparent shadow shader? */ const int flags = intersection_get_shader_flags(kg, isect.prim, isect.type); - if (!(flags & SD_HAS_TRANSPARENT_SHADOW) || max_hits == 0) { + if (!(flags & SD_HAS_TRANSPARENT_SHADOW) || num_hits >= max_hits) { /* If no transparent shadows, all light is blocked and we can * stop immediately. */ return true; } - /* Increase the number of hits, possibly beyond max_hits, we will - * simply not record those and only keep the max_hits closest. */ - uint record_index = (*num_hits)++; + num_hits++; - if (record_index >= max_hits - 1) { - /* If maximum number of hits reached, find the intersection with - * the largest distance to potentially replace when another hit - * is found. */ - const int num_recorded_hits = min(max_hits, record_index); - float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t); - int max_recorded_hit = 0; + bool record_intersection = true; - for (int i = 1; i < num_recorded_hits; i++) { - const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t); - if (isect_t > max_recorded_t) { - max_recorded_t = isect_t; - max_recorded_hit = i; - } + /* Always use baked shadow transparency for curves. */ + if (isect.type & PRIMITIVE_ALL_CURVE) { + *throughput *= intersection_curve_shadow_transparency( + kg, isect.object, isect.prim, isect.u); + + if (*throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) { + return true; } - - if (record_index >= max_hits) { - record_index = max_recorded_hit; + else { + record_intersection = false; } - - /* Limit the ray distance and stop counting hits beyond this. */ - t_max_world = max(max_recorded_t, isect.t); - t_max_current = t_max_world * t_world_to_instance; } - integrator_state_write_shadow_isect(state, &isect, record_index); + if (record_intersection) { + /* Increase the number of hits, possibly beyond max_hits, we will + * simply not record those and only keep the max_hits closest. */ + uint record_index = (*num_recorded_hits)++; + + const uint max_record_hits = min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE); + if (record_index >= max_record_hits - 1) { + /* If maximum number of hits reached, find the intersection with + * the largest distance to potentially replace when another hit + * is found. */ + const int num_recorded_hits = min(max_record_hits, record_index); + float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t); + int max_recorded_hit = 0; + + for (int i = 1; i < num_recorded_hits; i++) { + const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t); + if (isect_t > max_recorded_t) { + max_recorded_t = isect_t; + max_recorded_hit = i; + } + } + + if (record_index >= max_record_hits) { + record_index = max_recorded_hit; + } + + /* Limit the ray distance and stop counting hits beyond this. */ + t_max_world = max(max_recorded_t, isect.t); + t_max_current = t_max_world * t_world_to_instance; + } + + integrator_state_write_shadow_isect(state, &isect, record_index); + } } prim_addr++; @@ -304,9 +327,11 @@ ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg, IntegratorShadowState state, const uint visibility, const uint max_hits, - ccl_private uint *num_hits) + ccl_private uint *num_recorded_hits, + ccl_private float *throughput) { - return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, state, visibility, max_hits, num_hits); + return BVH_FUNCTION_FULL_NAME(BVH)( + kg, ray, state, visibility, max_hits, num_recorded_hits, throughput); } #undef BVH_FUNCTION_NAME diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index 869311b38e2..309f0eeb1e2 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -195,4 +195,32 @@ ccl_device_inline int intersection_find_attribute(KernelGlobals kg, return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; } +/* Transparent Shadows */ + +/* Cut-off value to stop transparent shadow tracing when practically opaque. */ +#define CURVE_SHADOW_TRANSPARENCY_CUTOFF 0.001f + +ccl_device_inline float intersection_curve_shadow_transparency(KernelGlobals kg, + const int object, + const int prim, + const float u) +{ + /* Find attribute. */ + const int offset = intersection_find_attribute(kg, object, ATTR_STD_SHADOW_TRANSPARENCY); + if (offset == ATTR_STD_NOT_FOUND) { + /* If no shadow transparency attribute, assume opaque. */ + return 0.0f; + } + + /* Interpolate transparency between curve keys. */ + const KernelCurve kcurve = kernel_tex_fetch(__curves, prim); + const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(kcurve.type); + const int k1 = k0 + 1; + + const float f0 = kernel_tex_fetch(__attributes_float, offset + k0); + const float f1 = kernel_tex_fetch(__attributes_float, offset + k1); + + return (1.0f - u) * f0 + u * f1; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/cpu/kernel_arch.h b/intern/cycles/kernel/device/cpu/kernel_arch.h index ae7fab65100..7a438b58e73 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch.h @@ -64,6 +64,11 @@ void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobalsCPU *kg, const KernelShaderEvalInput *input, float *output, const int offset); +void KERNEL_FUNCTION_FULL_NAME(shader_eval_curve_shadow_transparency)( + const KernelGlobalsCPU *kg, + const KernelShaderEvalInput *input, + float *output, + const int offset); /* -------------------------------------------------------------------- * Adaptive sampling. diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h index 2b0eea4fb61..ac606c768db 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h @@ -150,6 +150,19 @@ void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobalsCPU *k #endif } +void KERNEL_FUNCTION_FULL_NAME(shader_eval_curve_shadow_transparency)( + const KernelGlobalsCPU *kg, + const KernelShaderEvalInput *input, + float *output, + const int offset) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, shader_eval_curve_shadow_transparency); +#else + kernel_curve_shadow_transparency_evaluate(kg, input, output, offset); +#endif +} + /* -------------------------------------------------------------------- * Adaptive sampling. */ diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index 6b4d79ed5b7..b6df74e835a 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -621,7 +621,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) } } -/* Background Shader Evaluation */ +/* Background */ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) kernel_gpu_shader_eval_background(KernelShaderEvalInput *input, @@ -635,6 +635,20 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) } } +/* Curve Shadow Transparency */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_shader_eval_curve_shadow_transparency(KernelShaderEvalInput *input, + float *output, + const int offset, + const int work_size) +{ + int i = ccl_gpu_global_id_x(); + if (i < work_size) { + kernel_curve_shadow_transparency_evaluate(NULL, input, output, offset + i); + } +} + /* -------------------------------------------------------------------- * Denoising. */ diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu index 574f66ab708..a3bafb9846c 100644 --- a/intern/cycles/kernel/device/optix/kernel.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -210,29 +210,50 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() optixSetPayload_5(true); return optixTerminateRay(); # else - const int max_hits = optixGetPayload_3(); + const uint max_hits = optixGetPayload_3(); + const uint num_hits_packed = optixGetPayload_2(); + const uint num_recorded_hits = uint16_unpack_from_uint_0(num_hits_packed); + const uint num_hits = uint16_unpack_from_uint_1(num_hits_packed); /* If no transparent shadows, all light is blocked and we can stop immediately. */ - if (max_hits == 0 || + if (num_hits >= max_hits || !(intersection_get_shader_flags(NULL, prim, type) & SD_HAS_TRANSPARENT_SHADOW)) { optixSetPayload_5(true); return optixTerminateRay(); } - /* Record transparent intersection. */ - const int num_hits = optixGetPayload_2(); - int record_index = num_hits; + /* Always use baked shadow transparency for curves. */ + if (type & PRIMITIVE_ALL_CURVE) { + float throughput = __uint_as_float(optixGetPayload_1()); + throughput *= intersection_curve_shadow_transparency(nullptr, object, prim, u); + optixSetPayload_1(__float_as_uint(throughput)); + optixSetPayload_2(uint16_pack_to_uint(num_recorded_hits, num_hits + 1)); - optixSetPayload_2(num_hits + 1); + if (throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) { + optixSetPayload_4(true); + return optixTerminateRay(); + } + else { + /* Continue tracing. */ + optixIgnoreIntersection(); + return; + } + } + + /* Record transparent intersection. */ + optixSetPayload_2(uint16_pack_to_uint(num_recorded_hits + 1, num_hits + 1)); + + uint record_index = num_recorded_hits; const IntegratorShadowState state = optixGetPayload_0(); - if (record_index >= max_hits) { + const uint max_record_hits = min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE); + if (record_index >= max_record_hits) { /* If maximum number of hits reached, find a hit to replace. */ float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t); - int max_recorded_hit = 0; + uint max_recorded_hit = 0; - for (int i = 1; i < max_hits; i++) { + for (int i = 1; i < max_record_hits; i++) { const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t); if (isect_t > max_recorded_t) { max_recorded_t = isect_t; @@ -256,6 +277,7 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, object) = object; INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, type) = type; + /* Continue tracing. */ optixIgnoreIntersection(); # endif /* __TRANSPARENT_SHADOWS__ */ #endif /* __SHADOW_RECORD_ALL__ */ diff --git a/intern/cycles/kernel/geom/geom_shader_data.h b/intern/cycles/kernel/geom/geom_shader_data.h index e6a5b8f7923..46bda2b656c 100644 --- a/intern/cycles/kernel/geom/geom_shader_data.h +++ b/intern/cycles/kernel/geom/geom_shader_data.h @@ -279,6 +279,81 @@ ccl_device void shader_setup_from_displace(KernelGlobals kg, LAMP_NONE); } +/* ShaderData setup for point on curve. */ + +ccl_device void shader_setup_from_curve(KernelGlobals kg, + ccl_private ShaderData *ccl_restrict sd, + int object, + int prim, + int segment, + float u) +{ + /* Primitive */ + sd->type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_CURVE_THICK, segment); + sd->lamp = LAMP_NONE; + sd->prim = prim; + sd->u = u; + sd->v = 0.0f; + sd->time = 0.5f; + sd->ray_length = 0.0f; + + /* Shader */ + sd->shader = kernel_tex_fetch(__curves, prim).shader_id; + sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; + + /* Object */ + sd->object = object; + sd->object_flag = kernel_tex_fetch(__object_flag, sd->object); +#ifdef __OBJECT_MOTION__ + shader_setup_object_transforms(kg, sd, sd->time); +#endif + + /* Get control points. */ + KernelCurve kcurve = kernel_tex_fetch(__curves, prim); + + int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); + int k1 = k0 + 1; + int ka = max(k0 - 1, kcurve.first_key); + int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1); + + float4 P_curve[4]; + + P_curve[0] = kernel_tex_fetch(__curve_keys, ka); + P_curve[1] = kernel_tex_fetch(__curve_keys, k0); + P_curve[2] = kernel_tex_fetch(__curve_keys, k1); + P_curve[3] = kernel_tex_fetch(__curve_keys, kb); + + /* Interpolate position and tangent. */ + sd->P = float4_to_float3(catmull_rom_basis_derivative(P_curve, sd->u)); +#ifdef __DPDU__ + sd->dPdu = float4_to_float3(catmull_rom_basis_derivative(P_curve, sd->u)); +#endif + + /* Transform into world space */ + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + object_position_transform_auto(kg, sd, &sd->P); +#ifdef __DPDU__ + object_dir_transform_auto(kg, sd, &sd->dPdu); +#endif + } + + /* No view direction, normals or bitangent. */ + sd->I = zero_float3(); + sd->N = zero_float3(); + sd->Ng = zero_float3(); +#ifdef __DPDU__ + sd->dPdv = zero_float3(); +#endif + + /* No ray differentials currently. */ +#ifdef __RAY_DIFFERENTIALS__ + sd->dP = differential3_zero(); + sd->dI = differential3_zero(); + sd->du = differential_zero(); + sd->dv = differential_zero(); +#endif +} + /* ShaderData setup from ray into background */ ccl_device_inline void shader_setup_from_background(KernelGlobals kg, diff --git a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h index d5c6ec145f0..90422445fad 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h @@ -115,18 +115,25 @@ ccl_device bool integrate_intersect_shadow_transparent(KernelGlobals kg, { /* Limit the number hits to the max transparent bounces allowed and the size that we * have available in the integrator state. */ - const uint max_transparent_hits = integrate_shadow_max_transparent_hits(kg, state); - const uint max_hits = min(max_transparent_hits, (uint)INTEGRATOR_SHADOW_ISECT_SIZE); + const uint max_hits = integrate_shadow_max_transparent_hits(kg, state); uint num_hits = 0; - bool opaque_hit = scene_intersect_shadow_all(kg, state, ray, visibility, max_hits, &num_hits); + float throughput = 1.0f; + bool opaque_hit = scene_intersect_shadow_all( + kg, state, ray, visibility, max_hits, &num_hits, &throughput); + + /* Computed throughput from baked shadow transparency, where we can bypass recording + * intersections and shader evaluation. */ + if (throughput != 1.0f) { + INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) *= throughput; + } /* If number of hits exceed the transparent bounces limit, make opaque. */ - if (num_hits > max_transparent_hits) { + if (num_hits > max_hits) { opaque_hit = true; } if (!opaque_hit) { - uint num_recorded_hits = min(num_hits, max_hits); + const uint num_recorded_hits = min(num_hits, min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE)); if (num_recorded_hits > 0) { sort_shadow_intersections(state, num_recorded_hits); diff --git a/intern/cycles/kernel/integrator/integrator_shade_shadow.h b/intern/cycles/kernel/integrator/integrator_shade_shadow.h index 94900754b76..2d056a0b76f 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_shade_shadow.h @@ -23,7 +23,7 @@ CCL_NAMESPACE_BEGIN -ccl_device_inline bool shadow_intersections_has_remaining(const int num_hits) +ccl_device_inline bool shadow_intersections_has_remaining(const uint num_hits) { return num_hits >= INTEGRATOR_SHADOW_ISECT_SIZE; } @@ -105,12 +105,12 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg, ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, IntegratorShadowState state, - const int num_hits) + const uint num_hits) { /* Accumulate shadow for transparent surfaces. */ - const int num_recorded_hits = min(num_hits, INTEGRATOR_SHADOW_ISECT_SIZE); + const uint num_recorded_hits = min(num_hits, INTEGRATOR_SHADOW_ISECT_SIZE); - for (int hit = 0; hit < num_recorded_hits + 1; hit++) { + for (uint hit = 0; hit < num_recorded_hits + 1; hit++) { /* Volume shaders. */ if (hit < num_recorded_hits || !shadow_intersections_has_remaining(num_hits)) { # ifdef __VOLUME__ @@ -162,7 +162,7 @@ ccl_device void integrator_shade_shadow(KernelGlobals kg, ccl_global float *ccl_restrict render_buffer) { PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SETUP); - const int num_hits = INTEGRATOR_STATE(state, shadow_path, num_hits); + const uint num_hits = INTEGRATOR_STATE(state, shadow_path, num_hits); #ifdef __TRANSPARENT_SHADOWS__ /* Evaluate transparent shadows. */ diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h index 84f34c6b986..4f21ab35d1f 100644 --- a/intern/cycles/kernel/integrator/integrator_state.h +++ b/intern/cycles/kernel/integrator/integrator_state.h @@ -48,19 +48,6 @@ CCL_NAMESPACE_BEGIN -/* Constants - * - * TODO: these could be made dynamic depending on the features used in the scene. */ - -#define INTEGRATOR_SHADOW_ISECT_SIZE_CPU 1024 -#define INTEGRATOR_SHADOW_ISECT_SIZE_GPU 4 - -#ifdef __KERNEL_CPU__ -# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_CPU -#else -# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_GPU -#endif - /* Data structures */ /* Integrator State diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index 1473e9ba8bf..30a41b9d3e3 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -96,4 +96,25 @@ ccl_device void kernel_background_evaluate(KernelGlobals kg, output[offset * 3 + 2] += color.z; } +ccl_device void kernel_curve_shadow_transparency_evaluate( + KernelGlobals kg, + ccl_global const KernelShaderEvalInput *input, + ccl_global float *output, + const int offset) +{ + /* Setup shader data. */ + const KernelShaderEvalInput in = input[offset]; + + ShaderData sd; + shader_setup_from_curve(kg, &sd, in.object, in.prim, __float_as_int(in.v), in.u); + + /* Evaluate transparency. */ + shader_eval_surface( + kg, INTEGRATOR_STATE_NULL, &sd, NULL, PATH_RAY_SHADOW); + + /* Write output. */ + output[offset] = clamp(average(shader_bsdf_transparency(kg, &sd)), 0.0f, 1.0f); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index edae158f403..fa8453b99cb 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -61,6 +61,15 @@ CCL_NAMESPACE_BEGIN #define ID_NONE (0.0f) #define PASS_UNUSED (~0) +#define INTEGRATOR_SHADOW_ISECT_SIZE_CPU 1024U +#define INTEGRATOR_SHADOW_ISECT_SIZE_GPU 4U + +#ifdef __KERNEL_CPU__ +# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_CPU +#else +# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_GPU +#endif + /* Kernel features */ #define __SOBOL__ #define __DPDU__ @@ -582,6 +591,7 @@ typedef enum AttributeStandard { ATTR_STD_VOLUME_VELOCITY, ATTR_STD_POINTINESS, ATTR_STD_RANDOM_PER_ISLAND, + ATTR_STD_SHADOW_TRANSPARENCY, ATTR_STD_NUM, ATTR_STD_NOT_FOUND = ~0 @@ -1452,6 +1462,7 @@ typedef enum DeviceKernel { DEVICE_KERNEL_SHADER_EVAL_DISPLACE, DEVICE_KERNEL_SHADER_EVAL_BACKGROUND, + DEVICE_KERNEL_SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY, #define DECLARE_FILM_CONVERT_KERNEL(variant) \ DEVICE_KERNEL_FILM_CONVERT_##variant, DEVICE_KERNEL_FILM_CONVERT_##variant##_HALF_RGBA diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp index aaf21ad9fd2..d7e6939cd80 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/render/attribute.cpp @@ -366,6 +366,8 @@ const char *Attribute::standard_name(AttributeStandard std) return "pointiness"; case ATTR_STD_RANDOM_PER_ISLAND: return "random_per_island"; + case ATTR_STD_SHADOW_TRANSPARENCY: + return "shadow_transparency"; case ATTR_STD_NOT_FOUND: case ATTR_STD_NONE: case ATTR_STD_NUM: @@ -603,6 +605,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name) case ATTR_STD_RANDOM_PER_ISLAND: attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_FACE); break; + case ATTR_STD_SHADOW_TRANSPARENCY: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE_KEY); + break; default: assert(0); break; diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 5d89060c1a1..5cedab24ceb 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -734,6 +734,10 @@ void GeometryManager::device_update_attributes(Device *device, Shader *shader = static_cast(node); geom_attributes[i].add(shader->attributes); } + + if (geom->is_hair() && static_cast(geom)->need_shadow_transparency()) { + geom_attributes[i].add(ATTR_STD_SHADOW_TRANSPARENCY); + } } /* convert object attributes to use the same data structures as geometry ones */ @@ -1659,6 +1663,7 @@ void GeometryManager::device_update(Device *device, VLOG(1) << "Total " << scene->geometry.size() << " meshes."; bool true_displacement_used = false; + bool curve_shadow_transparency_used = false; size_t total_tess_needed = 0; { @@ -1669,26 +1674,33 @@ void GeometryManager::device_update(Device *device, }); foreach (Geometry *geom, scene->geometry) { - if (geom->is_modified() && - (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME)) { - Mesh *mesh = static_cast(geom); + if (geom->is_modified()) { + if ((geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME)) { + Mesh *mesh = static_cast(geom); - /* Update normals. */ - mesh->add_face_normals(); - mesh->add_vertex_normals(); + /* Update normals. */ + mesh->add_face_normals(); + mesh->add_vertex_normals(); - if (mesh->need_attribute(scene, ATTR_STD_POSITION_UNDISPLACED)) { - mesh->add_undisplaced(); + if (mesh->need_attribute(scene, ATTR_STD_POSITION_UNDISPLACED)) { + mesh->add_undisplaced(); + } + + /* Test if we need tessellation. */ + if (mesh->need_tesselation()) { + total_tess_needed++; + } + + /* Test if we need displacement. */ + if (mesh->has_true_displacement()) { + true_displacement_used = true; + } } - - /* Test if we need tessellation. */ - if (mesh->need_tesselation()) { - total_tess_needed++; - } - - /* Test if we need displacement. */ - if (mesh->has_true_displacement()) { - true_displacement_used = true; + else if (geom->geometry_type == Geometry::HAIR) { + Hair *hair = static_cast(geom); + if (hair->need_shadow_transparency()) { + curve_shadow_transparency_used = true; + } } if (progress.get_cancel()) { @@ -1752,7 +1764,7 @@ void GeometryManager::device_update(Device *device, /* Update images needed for true displacement. */ bool old_need_object_flags_update = false; - if (true_displacement_used) { + if (true_displacement_used || curve_shadow_transparency_used) { scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { scene->update_stats->geometry.times.add_entry( @@ -1770,7 +1782,7 @@ void GeometryManager::device_update(Device *device, const BVHLayout bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout, device->get_bvh_layout_mask()); mesh_calc_offset(scene, bvh_layout); - if (true_displacement_used) { + if (true_displacement_used || curve_shadow_transparency_used) { scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { scene->update_stats->geometry.times.add_entry( @@ -1795,8 +1807,9 @@ void GeometryManager::device_update(Device *device, } } - /* Update displacement. */ + /* Update displacement and hair shadow transparency. */ bool displacement_done = false; + bool curve_shadow_transparency_done = false; size_t num_bvh = 0; { @@ -1817,6 +1830,12 @@ void GeometryManager::device_update(Device *device, displacement_done = true; } } + else if (geom->geometry_type == Geometry::HAIR) { + Hair *hair = static_cast(geom); + if (hair->update_shadow_transparency(device, scene, progress)) { + curve_shadow_transparency_done = true; + } + } } if (geom->is_modified() || geom->need_update_bvh_for_offset) { @@ -1836,7 +1855,7 @@ void GeometryManager::device_update(Device *device, } /* Device re-update after displacement. */ - if (displacement_done) { + if (displacement_done || curve_shadow_transparency_done) { scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { scene->update_stats->geometry.times.add_entry( diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp index e757e3fd3e0..4656148119a 100644 --- a/intern/cycles/render/hair.cpp +++ b/intern/cycles/render/hair.cpp @@ -18,8 +18,13 @@ #include "render/curves.h" #include "render/hair.h" +#include "render/object.h" #include "render/scene.h" +#include "integrator/shader_eval.h" + +#include "util/util_progress.h" + CCL_NAMESPACE_BEGIN /* Hair Curve */ @@ -514,4 +519,114 @@ PrimitiveType Hair::primitive_type() const ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK); } +/* Fill in coordinates for curve transparency shader evaluation on device. */ +static int fill_shader_input(const Hair *hair, + const int object_index, + device_vector &d_input) +{ + int d_input_size = 0; + KernelShaderEvalInput *d_input_data = d_input.data(); + + const int num_curves = hair->num_curves(); + for (int i = 0; i < num_curves; i++) { + const Hair::Curve curve = hair->get_curve(i); + const int num_segments = curve.num_segments(); + + for (int j = 0; j < num_segments + 1; j++) { + KernelShaderEvalInput in; + in.object = object_index; + in.prim = hair->prim_offset + i; + in.u = (j < num_segments) ? 0.0f : 1.0f; + in.v = (j < num_segments) ? __int_as_float(j) : __int_as_float(j - 1); + d_input_data[d_input_size++] = in; + } + } + + return d_input_size; +} + +/* Read back curve transparency shader output. */ +static void read_shader_output(float *shadow_transparency, + bool &is_fully_opaque, + const device_vector &d_output) +{ + const int num_keys = d_output.size(); + const float *output_data = d_output.data(); + bool is_opaque = true; + + for (int i = 0; i < num_keys; i++) { + shadow_transparency[i] = output_data[i]; + if (shadow_transparency[i] > 0.0f) { + is_opaque = false; + } + } + + is_fully_opaque = is_opaque; +} + +bool Hair::need_shadow_transparency() +{ + for (const Node *node : used_shaders) { + const Shader *shader = static_cast(node); + if (shader->has_surface_transparent && shader->get_use_transparent_shadow()) { + return true; + } + } + + return false; +} + +bool Hair::update_shadow_transparency(Device *device, Scene *scene, Progress &progress) +{ + if (!need_shadow_transparency()) { + /* If no shaders with shadow transparency, remove attribute. */ + Attribute *attr = attributes.find(ATTR_STD_SHADOW_TRANSPARENCY); + if (attr) { + attributes.remove(attr); + return true; + } + else { + return false; + } + } + + string msg = string_printf("Computing Shadow Transparency %s", name.c_str()); + progress.set_status("Updating Hair", msg); + + /* Create shadow transparency attribute. */ + Attribute *attr = attributes.find(ATTR_STD_SHADOW_TRANSPARENCY); + const bool attribute_exists = (attr != nullptr); + if (!attribute_exists) { + attr = attributes.add(ATTR_STD_SHADOW_TRANSPARENCY); + } + + float *attr_data = attr->data_float(); + + /* Find object index. */ + size_t object_index = OBJECT_NONE; + + for (size_t i = 0; i < scene->objects.size(); i++) { + if (scene->objects[i]->get_geometry() == this) { + object_index = i; + break; + } + } + + /* Evaluate shader on device. */ + ShaderEval shader_eval(device, progress); + bool is_fully_opaque = false; + shader_eval.eval(SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY, + num_keys(), + 1, + function_bind(&fill_shader_input, this, object_index, _1), + function_bind(&read_shader_output, attr_data, is_fully_opaque, _1)); + + if (is_fully_opaque) { + attributes.remove(attr); + return attribute_exists; + } + + return true; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h index 920e9601b35..3e91fc3dcbb 100644 --- a/intern/cycles/render/hair.h +++ b/intern/cycles/render/hair.h @@ -153,6 +153,10 @@ class Hair : public Geometry { KernelCurveSegment *curve_segments); PrimitiveType primitive_type() const override; + + /* Attributes */ + bool need_shadow_transparency(); + bool update_shadow_transparency(Device *device, Scene *scene, Progress &progress); }; CCL_NAMESPACE_END From cd36f59027042ff0f2ad53e98088428a4c0c5e38 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 19 Oct 2021 15:30:25 +0200 Subject: [PATCH 0935/1500] Cleanup: trailing whitespace --- release/scripts/startup/nodeitems_builtins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 8087f64c5ab..66c1ab0358a 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -226,7 +226,7 @@ def point_node_items(context): yield NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_legacy_poll) yield NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_legacy_poll) yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) - + yield NodeItem("GeometryNodeDistributePointsOnFaces") yield NodeItem("GeometryNodePointsToVertices") yield NodeItem("GeometryNodePointsToVolume") From 219058c213d5c9efce3f44216f6513edc93b5536 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Oct 2021 08:39:10 -0500 Subject: [PATCH 0936/1500] Geometry Nodes: Remove implicit realizing and conversion This commit removes the implicit conversion from points to a mesh that used to happen before the next modifier. It also removes the implicit realizing of instances that happened before another modifier. Now we have specific nodes for both of these operations, the implicit conversions make less sense, and implicit instance realizing has already been removed in other nodes. This adds another geometry nodes modifier before modifiers that would have realized instances implicitly before. Currently adding another data-block during versioning after linking means that an assert needs to be changed. That should be made unnecessary by T92333. Differential Revision: https://developer.blender.org/D12722 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenkernel/BKE_geometry_set_instances.hh | 1 - .../blender/blenkernel/intern/DerivedMesh.cc | 44 +--------- .../intern/geometry_set_instances.cc | 66 +++------------ source/blender/blenkernel/intern/lib_id.c | 5 +- .../blenloader/intern/versioning_300.c | 81 +++++++++++++++++++ source/blender/modifiers/intern/MOD_nodes.cc | 6 -- 7 files changed, 95 insertions(+), 110 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index d8112de760d..5ee86f9446e 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 34 +#define BLENDER_FILE_SUBVERSION 35 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh index d7a60162e81..e5b28e4fbab 100644 --- a/source/blender/blenkernel/BKE_geometry_set_instances.hh +++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh @@ -44,7 +44,6 @@ struct GeometryInstanceGroup { void geometry_set_gather_instances(const GeometrySet &geometry_set, Vector &r_instance_groups); -GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set); GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set); /** diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 59e81938e79..3ec0ab9c512 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -885,33 +885,6 @@ void BKE_mesh_wrapper_deferred_finalize(Mesh *me_eval, BLI_assert(me_eval->runtime.wrapper_type_finalize == 0); } -/** - * Some modifiers don't work on geometry sets directly, but expect a single mesh as input. - * Therefore, we convert data from the geometry set into a single mesh, so that those - * modifiers can work on it as well. - */ -static Mesh *prepare_geometry_set_for_mesh_modifier(Mesh *mesh, GeometrySet &r_geometry_set) -{ - if (!r_geometry_set.has_instances() && !r_geometry_set.has_pointcloud()) { - return mesh; - } - - { - /* Add the mesh to the geometry set. */ - MeshComponent &mesh_component = r_geometry_set.get_component_for_write(); - mesh_component.replace(mesh, GeometryOwnershipType::Editable); - } - { - /* Combine mesh and all instances into a single mesh that can be passed to the modifier. */ - GeometrySet new_geometry_set = blender::bke::geometry_set_realize_mesh_for_modifier( - r_geometry_set); - MeshComponent &mesh_component = new_geometry_set.get_component_for_write(); - Mesh *new_mesh = mesh_component.release(); - r_geometry_set = new_geometry_set; - return new_mesh; - } -} - /** * Modifies the given mesh and geometry set. The mesh is not passed as part of the mesh component * in the \a geometry_set input, it is only passed in \a input_mesh and returned in the return @@ -928,14 +901,7 @@ static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md, Mesh *mesh_output = nullptr; const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (mti->modifyGeometrySet == nullptr) { - Mesh *new_input_mesh = prepare_geometry_set_for_mesh_modifier(input_mesh, geometry_set); - mesh_output = BKE_modifier_modify_mesh(md, &mectx, new_input_mesh); - - /* The caller is responsible for freeing `input_mesh` and `mesh_output`. The intermediate - * `new_input_mesh` has to be freed here. */ - if (!ELEM(new_input_mesh, input_mesh, mesh_output)) { - BKE_id_free(nullptr, new_input_mesh); - } + mesh_output = BKE_modifier_modify_mesh(md, &mectx, input_mesh); } else { /* For performance reasons, this should be called by the modifier and/or nodes themselves at @@ -1177,14 +1143,6 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* No existing verts to deform, need to build them. */ if (!deformed_verts) { if (mesh_final) { - Mesh *mesh_final_new = prepare_geometry_set_for_mesh_modifier(mesh_final, - geometry_set_final); - if (mesh_final_new != mesh_final) { - BLI_assert(mesh_final != mesh_input); - BKE_id_free(nullptr, mesh_final); - mesh_final = mesh_final_new; - } - /* Deforming a mesh, read the vertex locations * out of the mesh and deform them. Once done with this * run of deformers verts will be written back. */ diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index d92df386575..35c6a7df2c7 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -224,8 +224,7 @@ void geometry_set_gather_instances_attribute_info(Span se } } -static Mesh *join_mesh_topology_and_builtin_attributes(Span set_groups, - const bool convert_points_to_vertices) +static Mesh *join_mesh_topology_and_builtin_attributes(Span set_groups) { int totverts = 0; int totloops = 0; @@ -255,10 +254,6 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Spanmvert[vert_offset + i]; - const float3 old_position = pointcloud.co[i]; - const float3 new_position = transform * old_position; - copy_v3_v3(new_vert.co, new_position); - memcpy(&new_vert.no, point_normal_short, sizeof(point_normal_short)); - } - vert_offset += pointcloud.totpoint; - } - } } /* A possible optimization is to only tag the normals dirty when there are transforms that change @@ -495,12 +472,9 @@ static CurveEval *join_curve_splines_and_builtin_attributes(Span set_groups, - bool convert_points_to_vertices, - GeometrySet &result) +static void join_instance_groups_mesh(Span set_groups, GeometrySet &result) { - Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups, - convert_points_to_vertices); + Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups); if (new_mesh == nullptr) { return; } @@ -508,21 +482,17 @@ static void join_instance_groups_mesh(Span set_groups, MeshComponent &dst_component = result.get_component_for_write(); dst_component.replace(new_mesh); - Vector component_types; - component_types.append(GEO_COMPONENT_TYPE_MESH); - if (convert_points_to_vertices) { - component_types.append(GEO_COMPONENT_TYPE_POINT_CLOUD); - } - /* Don't copy attributes that are stored directly in the mesh data structs. */ Map attributes; geometry_set_gather_instances_attribute_info( set_groups, - component_types, + {GEO_COMPONENT_TYPE_MESH}, {"position", "material_index", "normal", "shade_smooth", "crease"}, attributes); - join_attributes( - set_groups, component_types, attributes, static_cast(dst_component)); + join_attributes(set_groups, + {GEO_COMPONENT_TYPE_MESH}, + attributes, + static_cast(dst_component)); } static void join_instance_groups_pointcloud(Span set_groups, @@ -582,24 +552,6 @@ static void join_instance_groups_curve(Span set_groups, G static_cast(dst_component)); } -GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) -{ - if (!geometry_set.has_instances() && !geometry_set.has_pointcloud()) { - return geometry_set; - } - - GeometrySet new_geometry_set = geometry_set; - Vector set_groups; - geometry_set_gather_instances(geometry_set, set_groups); - join_instance_groups_mesh(set_groups, true, new_geometry_set); - /* Remove all instances, even though some might contain other non-mesh data. We can't really - * keep only non-mesh instances in general. */ - new_geometry_set.remove(); - /* If there was a point cloud, it is now part of the mesh. */ - new_geometry_set.remove(); - return new_geometry_set; -} - GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) { if (!geometry_set.has_instances()) { @@ -610,7 +562,7 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) Vector set_groups; geometry_set_gather_instances(geometry_set, set_groups); - join_instance_groups_mesh(set_groups, false, new_geometry_set); + join_instance_groups_mesh(set_groups, new_geometry_set); join_instance_groups_pointcloud(set_groups, new_geometry_set); join_instance_groups_volume(set_groups, new_geometry_set); join_instance_groups_curve(set_groups, new_geometry_set); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 3b2d2c5d2c3..3c85ba62d07 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1121,8 +1121,9 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl id->us = 1; } if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { - /* Note that 2.8x versioning has tested not to cause conflicts. */ - BLI_assert(bmain->is_locked_for_linking == false || ELEM(type, ID_WS, ID_GR)); + /* Note that 2.8x versioning has tested not to cause conflicts. Node trees are + * skipped in this check to allow adding a geometry node tree for versioning. */ + BLI_assert(bmain->is_locked_for_linking == false || ELEM(type, ID_WS, ID_GR, ID_NT)); ListBase *lb = which_libbase(bmain, type); BKE_main_lock(bmain); diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 035642a561a..4c6b70982a4 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -55,6 +55,7 @@ #include "BKE_idprop.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_node.h" #include "RNA_access.h" @@ -539,6 +540,54 @@ static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree) } } +/** + * The geometry nodes modifier used to realize instances for the next modifier implicitly. Now it + * is done with the realize instances node. It also used to convert meshes to point clouds + * automatically, which is also now done with a specific node. + */ +static bNodeTree *add_realize_node_tree(Main *bmain) +{ + bNodeTree *node_tree = ntreeAddTree(bmain, "Realize Instances 2.93 Legacy", "GeometryNodeTree"); + + ntreeAddSocketInterface(node_tree, SOCK_IN, "NodeSocketGeometry", "Geometry"); + ntreeAddSocketInterface(node_tree, SOCK_OUT, "NodeSocketGeometry", "Geometry"); + + bNode *group_input = nodeAddStaticNode(NULL, node_tree, NODE_GROUP_INPUT); + group_input->locx = -400.0f; + bNode *group_output = nodeAddStaticNode(NULL, node_tree, NODE_GROUP_OUTPUT); + group_output->locx = 500.0f; + group_output->flag |= NODE_DO_OUTPUT; + + bNode *join = nodeAddStaticNode(NULL, node_tree, GEO_NODE_JOIN_GEOMETRY); + join->locx = group_output->locx - 175.0f; + join->locy = group_output->locy; + bNode *conv = nodeAddStaticNode(NULL, node_tree, GEO_NODE_POINTS_TO_VERTICES); + conv->locx = join->locx - 175.0f; + conv->locy = join->locy - 70.0; + bNode *separate = nodeAddStaticNode(NULL, node_tree, GEO_NODE_SEPARATE_COMPONENTS); + separate->locx = join->locx - 350.0f; + separate->locy = join->locy + 50.0f; + bNode *realize = nodeAddStaticNode(NULL, node_tree, GEO_NODE_REALIZE_INSTANCES); + realize->locx = separate->locx - 200.0f; + realize->locy = join->locy; + + nodeAddLink(node_tree, group_input, group_input->outputs.first, realize, realize->inputs.first); + nodeAddLink(node_tree, realize, realize->outputs.first, separate, separate->inputs.first); + nodeAddLink(node_tree, conv, conv->outputs.first, join, join->inputs.first); + nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 3), join, join->inputs.first); + nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 1), conv, conv->inputs.first); + nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 2), join, join->inputs.first); + nodeAddLink(node_tree, separate, separate->outputs.first, join, join->inputs.first); + nodeAddLink(node_tree, join, join->outputs.first, group_output, group_output->inputs.first); + + LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) { + nodeSetSelected(node, false); + } + + ntreeUpdateTree(bmain, node_tree); + return node_tree; +} + void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) { if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) { @@ -672,6 +721,38 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 35)) { + /* Add a new modifier to realize instances from previous modifiers. + * Previously that was done automatically by geometry nodes. */ + bNodeTree *realize_instances_node_tree = NULL; + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH_MUTABLE (ModifierData *, md, &ob->modifiers) { + if (md->type != eModifierType_Nodes) { + continue; + } + if (md->next == NULL) { + break; + } + if (md->next->type == eModifierType_Nodes) { + continue; + } + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group == NULL) { + continue; + } + + NodesModifierData *new_nmd = (NodesModifierData *)BKE_modifier_new(eModifierType_Nodes); + STRNCPY(new_nmd->modifier.name, "Realize Instances 2.93 Legacy"); + BKE_modifier_unique_name(&ob->modifiers, &new_nmd->modifier); + BLI_insertlinkafter(&ob->modifiers, md, new_nmd); + if (realize_instances_node_tree == NULL) { + realize_instances_node_tree = add_realize_node_tree(bmain); + } + new_nmd->node_group = realize_instances_node_tree; + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index b28fead299d..b5ca5f3545f 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1093,12 +1093,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * modifyGeometry(md, ctx, geometry_set); - if (ctx->flag & MOD_APPLY_TO_BASE_MESH) { - /* In this case it makes sense to realize instances, otherwise in some cases there might be no - * results when applying the modifier. */ - geometry_set = blender::bke::geometry_set_realize_mesh_for_modifier(geometry_set); - } - Mesh *new_mesh = geometry_set.get_component_for_write().release(); if (new_mesh == nullptr) { return BKE_mesh_new_nomain(0, 0, 0, 0, 0); From a7075a30e2611b41af672d5987af662ce5934492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 19 Oct 2021 15:48:12 +0200 Subject: [PATCH 0937/1500] BKE_callback_remove: prevent crash on Blender exit `BKE_callback_remove` now checks whether the callback actually is known, before trying to remove it. `BKE_blender_atexit()` runs after `BKE_callback_global_finalize()`. When an at-exit callback tried to unregister its BKE callbacks, these would already be unregistered, causing a crash of Blender when exiting, --- source/blender/blenkernel/intern/callbacks.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/callbacks.c b/source/blender/blenkernel/intern/callbacks.c index 7fd3048b740..dbc213907ac 100644 --- a/source/blender/blenkernel/intern/callbacks.c +++ b/source/blender/blenkernel/intern/callbacks.c @@ -82,7 +82,12 @@ void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt) void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt) { ListBase *lb = &callback_slots[evt]; - BLI_remlink(lb, funcstore); + + /* Be safe, as the callback may have already been removed by BKE_callback_global_finalize(), for + * example when removing callbacks in response to a BKE_blender_atexit_register callback + * function. `BKE_blender_atexit()` runs after `BKE_callback_global_finalize()`. */ + BLI_remlink_safe(lb, funcstore); + if (funcstore->alloc) { MEM_freeN(funcstore); } From 9a1d75e0b9580819dba188afdab72295cbf47302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 19 Oct 2021 15:22:40 +0200 Subject: [PATCH 0938/1500] Asset Library Service: make insensitive to trailing slashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make `AssetLibraryService::get_asset_library_on_disk(path)` insensitive to trailing slashes; i.e. `get_asset_library_on_disk("/path")` and `get_asset_library_on_disk("/path/¨)` will now return the same `AssetLibrary*`. --- .../intern/asset_library_service.cc | 24 +++++++++++++---- .../intern/asset_library_service_test.cc | 26 +++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_library_service.cc b/source/blender/blenkernel/intern/asset_library_service.cc index 14604def0d3..53ce2826022 100644 --- a/source/blender/blenkernel/intern/asset_library_service.cc +++ b/source/blender/blenkernel/intern/asset_library_service.cc @@ -24,6 +24,7 @@ #include "BKE_blender.h" #include "BKE_callbacks.h" +#include "BLI_path_util.h" #include "BLI_string_ref.hh" #include "MEM_guardedalloc.h" @@ -54,14 +55,27 @@ void AssetLibraryService::destroy() instance_.reset(); } +namespace { +std::string normalize_directory_path(StringRefNull directory) +{ + + char dir_normalized[PATH_MAX]; + STRNCPY(dir_normalized, directory.c_str()); + BLI_path_normalize_dir(NULL, dir_normalized); + return std::string(dir_normalized); +} +} // namespace + AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull top_level_directory) { BLI_assert_msg(!top_level_directory.is_empty(), "top level directory must be given for on-disk asset library"); - AssetLibraryPtr *lib_uptr_ptr = on_disk_libraries_.lookup_ptr(top_level_directory); + std::string top_dir_trailing_slash = normalize_directory_path(top_level_directory); + + AssetLibraryPtr *lib_uptr_ptr = on_disk_libraries_.lookup_ptr(top_dir_trailing_slash); if (lib_uptr_ptr != nullptr) { - CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", top_level_directory.c_str()); + CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", top_dir_trailing_slash.c_str()); return lib_uptr_ptr->get(); } @@ -69,10 +83,10 @@ AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull top_l AssetLibrary *lib = lib_uptr.get(); lib->on_save_handler_register(); - lib->load(top_level_directory); + lib->load(top_dir_trailing_slash); - on_disk_libraries_.add_new(top_level_directory, std::move(lib_uptr)); - CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", top_level_directory.c_str()); + on_disk_libraries_.add_new(top_dir_trailing_slash, std::move(lib_uptr)); + CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", top_dir_trailing_slash.c_str()); return lib; } diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index b4bb1dc0895..ed299028c41 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -19,6 +19,8 @@ #include "asset_library_service.hh" +#include "BLI_path_util.h" + #include "CLG_log.h" #include "testing/testing.h" @@ -84,6 +86,30 @@ TEST_F(AssetLibraryServiceTest, library_pointers) * cannot be reliably tested by just pointer comparison, though. */ } +TEST_F(AssetLibraryServiceTest, library_path_trailing_slashes) +{ + AssetLibraryService *service = AssetLibraryService::get(); + + char asset_lib_no_slash[PATH_MAX]; + char asset_lib_with_slash[PATH_MAX]; + STRNCPY(asset_lib_no_slash, asset_library_root_.c_str()); + STRNCPY(asset_lib_with_slash, asset_library_root_.c_str()); + + /* Ensure #asset_lib_no_slash has no trailing slash, regardless of what was passed on the CLI to + * the unit test. */ + while (strlen(asset_lib_no_slash) && + ELEM(asset_lib_no_slash[strlen(asset_lib_no_slash) - 1], SEP, ALTSEP)) { + asset_lib_no_slash[strlen(asset_lib_no_slash) - 1] = '\0'; + } + + BLI_path_slash_ensure(asset_lib_with_slash); + + AssetLibrary *const lib_no_slash = service->get_asset_library_on_disk(asset_lib_no_slash); + + EXPECT_EQ(lib_no_slash, service->get_asset_library_on_disk(asset_lib_with_slash)) + << "With or without trailing slash shouldn't matter."; +} + TEST_F(AssetLibraryServiceTest, catalogs_loaded) { AssetLibraryService *const service = AssetLibraryService::get(); From 0a6cf3ed0c64a0e4e58ecd40a491d0e6c93532f2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Oct 2021 09:01:39 -0500 Subject: [PATCH 0939/1500] Geometry Nodes: Fields version of the raycast node This patch includes an updated version of the raycast node that uses fields instead of attributes for inputs instead of outputs. This makes the node's UI much clearer. It should be faster too, since the evaluation system for fields provides multi-threading. The source position replaces the input geometry (since this node is evaluated in the context of a geometry like the other field nodes). Thanks to @guitargeek for an initial version of this patch. Differential Revision: https://developer.blender.org/D12638 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_mesh_sample.hh | 3 +- source/blender/blenkernel/BKE_node.h | 1 + .../blender/blenkernel/intern/mesh_sample.cc | 33 +- source/blender/blenkernel/intern/node.cc | 2 +- source/blender/makesdna/DNA_node_types.h | 6 +- source/blender/makesrna/intern/rna_nodetree.c | 35 +- source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 3 +- .../legacy/node_geo_attribute_transfer.cc | 3 +- .../geometry/nodes/legacy/node_geo_raycast.cc | 5 +- .../nodes/geometry/nodes/node_geo_raycast.cc | 456 ++++++++++++++++++ .../nodes/node_geo_transfer_attribute.cc | 4 +- 14 files changed, 523 insertions(+), 31 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_raycast.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 66c1ab0358a..05f2e900841 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -678,6 +678,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_legacy_poll), NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeRaycast"), NodeItem("GeometryNodeProximity"), NodeItem("GeometryNodeBoundBox"), NodeItem("GeometryNodeConvexHull"), diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh index 4845876751b..17f8e766724 100644 --- a/source/blender/blenkernel/BKE_mesh_sample.hh +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -75,6 +75,7 @@ enum class eAttributeMapMode { class MeshAttributeInterpolator { private: const Mesh *mesh_; + const IndexMask mask_; const Span positions_; const Span looptri_indices_; @@ -83,13 +84,13 @@ class MeshAttributeInterpolator { public: MeshAttributeInterpolator(const Mesh *mesh, + const IndexMask mask, const Span positions, const Span looptri_indices); void sample_data(const GVArray &src, const AttributeDomain domain, const eAttributeMapMode mode, - const IndexMask mask, const GMutableSpan dst); void sample_attribute(const ReadAttributeLookup &src_attribute, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 65e54be7db4..07ad317dd30 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1544,6 +1544,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_TRANSFER_ATTRIBUTE 1125 #define GEO_NODE_SUBDIVISION_SURFACE 1126 #define GEO_NODE_CURVE_ENDPOINT_SELECTION 1127 +#define GEO_NODE_RAYCAST 1128 /** \} */ diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc index 9842b3aff31..2274d34f0f1 100644 --- a/source/blender/blenkernel/intern/mesh_sample.cc +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -60,8 +60,6 @@ void sample_point_attribute(const Mesh &mesh, const IndexMask mask, const GMutableSpan data_out) { - BLI_assert(data_out.size() == looptri_indices.size()); - BLI_assert(data_out.size() == bary_coords.size()); BLI_assert(data_in.size() == mesh.totvert); BLI_assert(data_in.type() == data_out.type()); @@ -109,8 +107,6 @@ void sample_corner_attribute(const Mesh &mesh, const IndexMask mask, const GMutableSpan data_out) { - BLI_assert(data_out.size() == looptri_indices.size()); - BLI_assert(data_out.size() == bary_coords.size()); BLI_assert(data_in.size() == mesh.totloop); BLI_assert(data_in.type() == data_out.type()); @@ -146,7 +142,6 @@ void sample_face_attribute(const Mesh &mesh, const IndexMask mask, const GMutableSpan data_out) { - BLI_assert(data_out.size() == looptri_indices.size()); BLI_assert(data_in.size() == mesh.totpoly); BLI_assert(data_in.type() == data_out.type()); @@ -158,9 +153,10 @@ void sample_face_attribute(const Mesh &mesh, } MeshAttributeInterpolator::MeshAttributeInterpolator(const Mesh *mesh, + const IndexMask mask, const Span positions, const Span looptri_indices) - : mesh_(mesh), positions_(positions), looptri_indices_(looptri_indices) + : mesh_(mesh), mask_(mask), positions_(positions), looptri_indices_(looptri_indices) { BLI_assert(positions.size() == looptri_indices.size()); } @@ -168,15 +164,15 @@ MeshAttributeInterpolator::MeshAttributeInterpolator(const Mesh *mesh, Span MeshAttributeInterpolator::ensure_barycentric_coords() { if (!bary_coords_.is_empty()) { - BLI_assert(bary_coords_.size() == positions_.size()); + BLI_assert(bary_coords_.size() >= mask_.min_array_size()); return bary_coords_; } - bary_coords_.reinitialize(positions_.size()); + bary_coords_.reinitialize(mask_.min_array_size()); const Span looptris{BKE_mesh_runtime_looptri_ensure(mesh_), BKE_mesh_runtime_looptri_len(mesh_)}; - for (const int i : bary_coords_.index_range()) { + for (const int i : mask_) { const int looptri_index = looptri_indices_[i]; const MLoopTri &looptri = looptris[looptri_index]; @@ -196,15 +192,15 @@ Span MeshAttributeInterpolator::ensure_barycentric_coords() Span MeshAttributeInterpolator::ensure_nearest_weights() { if (!nearest_weights_.is_empty()) { - BLI_assert(nearest_weights_.size() == positions_.size()); + BLI_assert(nearest_weights_.size() >= mask_.min_array_size()); return nearest_weights_; } - nearest_weights_.reinitialize(positions_.size()); + nearest_weights_.reinitialize(mask_.min_array_size()); const Span looptris{BKE_mesh_runtime_looptri_ensure(mesh_), BKE_mesh_runtime_looptri_len(mesh_)}; - for (const int i : nearest_weights_.index_range()) { + for (const int i : mask_) { const int looptri_index = looptri_indices_[i]; const MLoopTri &looptri = looptris[looptri_index]; @@ -224,7 +220,6 @@ Span MeshAttributeInterpolator::ensure_nearest_weights() void MeshAttributeInterpolator::sample_data(const GVArray &src, const AttributeDomain domain, const eAttributeMapMode mode, - const IndexMask mask, const GMutableSpan dst) { if (src.is_empty() || dst.is_empty()) { @@ -247,15 +242,15 @@ void MeshAttributeInterpolator::sample_data(const GVArray &src, /* Interpolate the source attributes on the surface. */ switch (domain) { case ATTR_DOMAIN_POINT: { - sample_point_attribute(*mesh_, looptri_indices_, weights, src, mask, dst); + sample_point_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst); break; } case ATTR_DOMAIN_FACE: { - sample_face_attribute(*mesh_, looptri_indices_, src, mask, dst); + sample_face_attribute(*mesh_, looptri_indices_, src, mask_, dst); break; } case ATTR_DOMAIN_CORNER: { - sample_corner_attribute(*mesh_, looptri_indices_, weights, src, mask, dst); + sample_corner_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst); break; } case ATTR_DOMAIN_EDGE: { @@ -274,11 +269,7 @@ void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_ eAttributeMapMode mode) { if (src_attribute && dst_attribute) { - this->sample_data(*src_attribute.varray, - src_attribute.domain, - mode, - IndexMask(dst_attribute->size()), - dst_attribute.as_span()); + this->sample_data(*src_attribute.varray, src_attribute.domain, mode, dst_attribute.as_span()); } } diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index c5fb9030847..ce8f941b0b6 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5751,6 +5751,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_curve_subdivide(); register_node_type_geo_legacy_edge_split(); register_node_type_geo_legacy_subdivision_surface(); + register_node_type_geo_legacy_raycast(); register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); @@ -5814,7 +5815,6 @@ static void registerGeometryNodes() register_node_type_geo_instance_on_points(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); - register_node_type_geo_material_replace(); register_node_type_geo_material_selection(); register_node_type_geo_mesh_primitive_circle(); register_node_type_geo_mesh_primitive_cone(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index dd749a9dc60..e6d02923dfe 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1526,9 +1526,13 @@ typedef struct NodeGeometryRaycast { /* GeometryNodeRaycastMapMode. */ uint8_t mapping; + /* CustomDataType. */ + int8_t data_type; + + /* Deprecated input types in new Raycast node. Can be removed when legacy nodes are no longer + * supported. */ uint8_t input_type_ray_direction; uint8_t input_type_ray_length; - char _pad[1]; } NodeGeometryRaycast; typedef struct NodeGeometryCurveFill { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 9571d889f6d..dfefc774b3d 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10716,7 +10716,7 @@ static void def_geo_input_material(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_raycast(StructRNA *srna) +static void def_geo_legacy_raycast(StructRNA *srna) { static EnumPropertyItem mapping_items[] = { {GEO_NODE_RAYCAST_INTERPOLATED, @@ -10752,6 +10752,39 @@ static void def_geo_raycast(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_raycast(StructRNA *srna) +{ + static EnumPropertyItem mapping_items[] = { + {GEO_NODE_RAYCAST_INTERPOLATED, + "INTERPOLATED", + 0, + "Interpolated", + "Interpolate the attribute from the corners of the hit face"}, + {GEO_NODE_RAYCAST_NEAREST, + "NEAREST", + 0, + "Nearest", + "Use the attribute value of the closest mesh element"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryRaycast", "storage"); + + prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mapping_items); + RNA_def_property_ui_text(prop, "Mapping", "Mapping from the target geometry to hit points"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); +} + static void def_geo_curve_fill(StructRNA *srna) { static const EnumPropertyItem mode_items[] = { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 35cd1c8c5a9..4d006342e72 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -255,6 +255,7 @@ set(SRC geometry/nodes/node_geo_points_to_vertices.cc geometry/nodes/node_geo_points_to_volume.cc geometry/nodes/node_geo_proximity.cc + geometry/nodes/node_geo_raycast.cc geometry/nodes/node_geo_realize_instances.cc geometry/nodes/node_geo_rotate_instances.cc geometry/nodes/node_geo_scale_instances.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 3ca8c0d02d7..4d75303363c 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -45,6 +45,7 @@ void register_node_type_geo_legacy_select_by_handle_type(void); void register_node_type_geo_legacy_curve_subdivide(void); void register_node_type_geo_legacy_edge_split(void); void register_node_type_geo_legacy_subdivision_surface(void); +void register_node_type_geo_legacy_raycast(void); void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_capture(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 9a361c3a3f1..c20a9545968 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -315,7 +315,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SCALE, def_geo_point_scale, "LEGACY_ DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SEPARATE, 0, "LEGACY_POINT_SEPARATE", LegacyPointSeparate, "Point Separate", "") DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, "LEGACY_POINT_TRANSLATE", LegacyPointTranslate, "Point Translate", "") DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_legacy_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") @@ -383,6 +383,7 @@ DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "") +DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "") DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc index 6bd39bc9145..d7a66dac3ad 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc @@ -284,7 +284,8 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_ Array positions(tot_samples); get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions); - bke::mesh_surface_sample::MeshAttributeInterpolator interp(mesh, positions, looptri_indices); + bke::mesh_surface_sample::MeshAttributeInterpolator interp( + mesh, IndexMask(tot_samples), positions, looptri_indices); interp.sample_attribute( src_attribute, dst_attribute, bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc index 401a478f04c..6641e622362 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc @@ -250,7 +250,8 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, hit_distance_attribute.save(); /* Custom interpolated attributes */ - bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices); + bke::mesh_surface_sample::MeshAttributeInterpolator interp( + src_mesh, IndexMask(ray_origins.size()), hit_positions, hit_indices); for (const int i : hit_attribute_names.index_range()) { const std::optional meta_data = src_mesh_component->attribute_get_meta_data( hit_attribute_names[i]); @@ -304,7 +305,7 @@ static void geo_node_raycast_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_raycast() +void register_node_type_geo_legacy_raycast() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc new file mode 100644 index 00000000000..e5ed5c02090 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -0,0 +1,456 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_bvhutils.h" +#include "BKE_mesh_sample.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using namespace blender::bke::mesh_surface_sample; + +namespace blender::nodes { + +static void geo_node_raycast_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Target Geometry"); + + b.add_input("Attribute").hide_value().supports_field(); + b.add_input("Attribute", "Attribute_001").hide_value().supports_field(); + b.add_input("Attribute", "Attribute_002").hide_value().supports_field(); + b.add_input("Attribute", "Attribute_003").hide_value().supports_field(); + b.add_input("Attribute", "Attribute_004").hide_value().supports_field(); + + b.add_input("Source Position").implicit_field(); + b.add_input("Ray Direction").default_value({0.0f, 0.0f, 1.0f}).supports_field(); + b.add_input("Ray Length") + .default_value(100.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + + b.add_output("Is Hit").dependent_field(); + b.add_output("Hit Position").dependent_field(); + b.add_output("Hit Normal").dependent_field(); + b.add_output("Hit Distance").dependent_field(); + + b.add_output("Attribute").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output("Attribute", "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output("Attribute", "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output("Attribute", "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output("Attribute", "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6}); +} + +static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE); +} + +static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast), + __func__); + data->mapping = GEO_NODE_RAYCAST_INTERPOLATED; + data->data_type = CD_PROP_FLOAT; + node->storage = data; +} + +static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryRaycast &data = *(const NodeGeometryRaycast *)node->storage; + const CustomDataType data_type = static_cast(data.data_type); + + bNodeSocket *socket_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + bNodeSocket *socket_float = socket_vector->next; + bNodeSocket *socket_color4f = socket_float->next; + bNodeSocket *socket_boolean = socket_color4f->next; + bNodeSocket *socket_int32 = socket_boolean->next; + + nodeSetSocketAvailability(socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(socket_int32, data_type == CD_PROP_INT32); + + bNodeSocket *out_socket_vector = (bNodeSocket *)BLI_findlink(&node->outputs, 4); + bNodeSocket *out_socket_float = out_socket_vector->next; + bNodeSocket *out_socket_color4f = out_socket_float->next; + bNodeSocket *out_socket_boolean = out_socket_color4f->next; + bNodeSocket *out_socket_int32 = out_socket_boolean->next; + + nodeSetSocketAvailability(out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(out_socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(out_socket_int32, data_type == CD_PROP_INT32); +} + +static eAttributeMapMode get_map_mode(GeometryNodeRaycastMapMode map_mode) +{ + switch (map_mode) { + case GEO_NODE_RAYCAST_INTERPOLATED: + return eAttributeMapMode::INTERPOLATED; + default: + case GEO_NODE_RAYCAST_NEAREST: + return eAttributeMapMode::NEAREST; + } +} + +static void raycast_to_mesh(IndexMask mask, + const Mesh &mesh, + const VArray &ray_origins, + const VArray &ray_directions, + const VArray &ray_lengths, + const MutableSpan r_hit, + const MutableSpan r_hit_indices, + const MutableSpan r_hit_positions, + const MutableSpan r_hit_normals, + const MutableSpan r_hit_distances, + int &hit_count) +{ + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4); + if (tree_data.tree == nullptr) { + free_bvhtree_from_mesh(&tree_data); + return; + } + + for (const int i : mask) { + const float ray_length = ray_lengths[i]; + const float3 ray_origin = ray_origins[i]; + const float3 ray_direction = ray_directions[i].normalized(); + + BVHTreeRayHit hit; + hit.index = -1; + hit.dist = ray_length; + if (BLI_bvhtree_ray_cast(tree_data.tree, + ray_origin, + ray_direction, + 0.0f, + &hit, + tree_data.raycast_callback, + &tree_data) != -1) { + hit_count++; + if (!r_hit.is_empty()) { + r_hit[i] = hit.index >= 0; + } + if (!r_hit_indices.is_empty()) { + /* The caller must be able to handle invalid indices anyway, so don't clamp this value. */ + r_hit_indices[i] = hit.index; + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = hit.co; + } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = hit.no; + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = hit.dist; + } + } + else { + if (!r_hit.is_empty()) { + r_hit[i] = false; + } + if (!r_hit_indices.is_empty()) { + r_hit_indices[i] = -1; + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = ray_length; + } + } + } + + /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */ + BLI_assert(tree_data.cached); + free_bvhtree_from_mesh(&tree_data); +} + +class RaycastFunction : public fn::MultiFunction { + private: + GeometrySet target_; + GeometryNodeRaycastMapMode mapping_; + + /** The field for data evaluated on the target geometry. */ + std::optional target_context_; + std::unique_ptr target_evaluator_; + const GVArray *target_data_ = nullptr; + + /* Always evaluate the target domain data on the point domain. Eventually this could be + * exposed as an option or determined automatically from the field inputs in order to avoid + * losing information if the target field is on a different domain. */ + const AttributeDomain domain_ = ATTR_DOMAIN_POINT; + + fn::MFSignature signature_; + + public: + RaycastFunction(GeometrySet target, GField src_field, GeometryNodeRaycastMapMode mapping) + : target_(std::move(target)), mapping_((GeometryNodeRaycastMapMode)mapping) + { + target_.ensure_owns_direct_data(); + this->evaluate_target_field(std::move(src_field)); + signature_ = create_signature(); + this->set_signature(&signature_); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Geometry Proximity"}; + signature.single_input("Source Position"); + signature.single_input("Ray Direction"); + signature.single_input("Ray Length"); + signature.single_output("Is Hit"); + signature.single_output("Hit Position"); + signature.single_output("Hit Normal"); + signature.single_output("Distance"); + if (target_data_) { + signature.single_output("Attribute", target_data_->type()); + } + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + /* Hit positions are always necessary for retrieving the attribute from the target if that + * output is required, so always retrieve a span from the evaluator in that case (it's + * expected that the evaluator is more likely to have a spare buffer that could be used). */ + MutableSpan hit_positions = + (target_data_) ? params.uninitialized_single_output(4, "Hit Position") : + params.uninitialized_single_output_if_required(4, "Hit Position"); + + Array hit_indices; + if (target_data_) { + hit_indices.reinitialize(mask.min_array_size()); + } + + BLI_assert(target_.has_mesh()); + const Mesh &mesh = *target_.get_mesh_for_read(); + + int hit_count = 0; + raycast_to_mesh(mask, + mesh, + params.readonly_single_input(0, "Source Position"), + params.readonly_single_input(1, "Ray Direction"), + params.readonly_single_input(2, "Ray Length"), + params.uninitialized_single_output_if_required(3, "Is Hit"), + hit_indices, + hit_positions, + params.uninitialized_single_output_if_required(5, "Hit Normal"), + params.uninitialized_single_output_if_required(6, "Distance"), + hit_count); + + IndexMask hit_mask; + Vector hit_mask_indices; + if (hit_count < mask.size()) { + /* Not all rays hit the target. Create a corrected mask to avoid transferring attribute data + * to invalid indices. An alternative would be handling -1 indices in a separate case in + * #MeshAttributeInterpolator, but since it already has an IndexMask in its constructor, it's + * simpler to use that. */ + hit_mask_indices.reserve(hit_count); + for (const int64_t i : mask) { + if (hit_indices[i] != -1) { + hit_mask_indices.append(i); + } + hit_mask = IndexMask(hit_mask_indices); + } + } + else { + hit_mask = mask; + } + + if (target_data_) { + GMutableSpan result = params.uninitialized_single_output_if_required(7, "Attribute"); + if (!result.is_empty()) { + MeshAttributeInterpolator interp(&mesh, hit_mask, hit_positions, hit_indices); + result.type().fill_assign_indices(result.type().default_value(), result.data(), mask); + interp.sample_data(*target_data_, domain_, get_map_mode(mapping_), result); + } + } + } + + private: + void evaluate_target_field(GField src_field) + { + if (!src_field) { + return; + } + const MeshComponent &mesh_component = *target_.get_component_for_read(); + target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); + const int domain_size = mesh_component.attribute_domain_size(domain_); + target_evaluator_ = std::make_unique(*target_context_, domain_size); + target_evaluator_->add(std::move(src_field)); + target_evaluator_->evaluate(); + target_data_ = &target_evaluator_->get_evaluated(0); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const CustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + if (params.output_is_required("Attribute_001")) { + return params.extract_input>("Attribute_001"); + } + break; + case CD_PROP_FLOAT3: + if (params.output_is_required("Attribute")) { + return params.extract_input>("Attribute"); + } + break; + case CD_PROP_COLOR: + if (params.output_is_required("Attribute_002")) { + return params.extract_input>("Attribute_002"); + } + break; + case CD_PROP_BOOL: + if (params.output_is_required("Attribute_003")) { + return params.extract_input>("Attribute_003"); + } + break; + case CD_PROP_INT32: + if (params.output_is_required("Attribute_004")) { + return params.extract_input>("Attribute_004"); + } + break; + default: + BLI_assert_unreachable(); + } + return {}; +} + +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) +{ + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Attribute_001", Field(field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Attribute", Field(field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Attribute_002", Field(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Attribute_003", Field(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Attribute_004", Field(field)); + break; + } + default: + break; + } +} + +static void geo_node_raycast_exec(GeoNodeExecParams params) +{ + GeometrySet target = params.extract_input("Target Geometry"); + const NodeGeometryRaycast &data = *(const NodeGeometryRaycast *)params.node().storage; + const GeometryNodeRaycastMapMode mapping = static_cast(data.mapping); + const CustomDataType data_type = static_cast(data.data_type); + + auto return_default = [&]() { + params.set_output("Is Hit", fn::make_constant_field(false)); + params.set_output("Hit Position", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + params.set_output("Hit Normal", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + params.set_output("Hit Distance", fn::make_constant_field(0.0f)); + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + output_attribute_field(params, fn::make_constant_field(T())); + }); + }; + + if (target.has_instances()) { + if (target.has_realized_data()) { + params.error_message_add( + NodeWarningType::Info, + TIP_("Only realized geometry is supported, instances will not be used")); + } + else { + params.error_message_add(NodeWarningType::Error, + TIP_("Target target must contain realized data")); + return return_default(); + } + } + + if (target.is_empty()) { + return return_default(); + } + + if (!target.has_mesh()) { + params.error_message_add(NodeWarningType::Error, + TIP_("The target geometry must contain a mesh")); + return return_default(); + } + + if (target.get_mesh_for_read()->totpoly == 0) { + params.error_message_add(NodeWarningType::Error, TIP_("The target mesh must have faces")); + return return_default(); + } + + GField field = get_input_attribute_field(params, data_type); + const bool do_attribute_transfer = bool(field); + Field position_field = params.extract_input>("Source Position"); + Field direction_field = params.extract_input>("Ray Direction"); + Field length_field = params.extract_input>("Ray Length"); + + auto fn = std::make_unique(std::move(target), std::move(field), mapping); + auto op = std::make_shared(FieldOperation( + std::move(fn), + {std::move(position_field), std::move(direction_field), std::move(length_field)})); + + params.set_output("Is Hit", Field(op, 0)); + params.set_output("Hit Position", Field(op, 1)); + params.set_output("Hit Normal", Field(op, 2)); + params.set_output("Hit Distance", Field(op, 3)); + if (do_attribute_transfer) { + output_attribute_field(params, GField(op, 4)); + } +} + +} // namespace blender::nodes + +void register_node_type_geo_raycast() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, blender::nodes::geo_node_raycast_init); + node_type_update(&ntype, blender::nodes::geo_node_raycast_update); + node_type_storage( + &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_raycast_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec; + ntype.draw_buttons = blender::nodes::geo_node_raycast_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 63792304726..4bf856698d9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -409,8 +409,8 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction { Array sampled_positions(mask.min_array_size()); get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions); - MeshAttributeInterpolator interp(&mesh, sampled_positions, looptri_indices); - interp.sample_data(*target_data_, domain_, eAttributeMapMode::INTERPOLATED, mask, dst); + MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices); + interp.sample_data(*target_data_, domain_, eAttributeMapMode::INTERPOLATED, dst); } private: From 1c5722ba071ac08042f2e3150495b865a0ffa95a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 19 Oct 2021 16:17:04 +0200 Subject: [PATCH 0940/1500] Fix T91197: marking assets from Python may crash When using `asset_mark` function from a Python script and afterwards updating the preview image, a crash might happen. The preview image is generated by the `asset_mark` function. This may happen on a background thread, introducing potential synchronization issues. This patch fixes this by separating the preview generation `ID.asset_generate_preview` from the mark as asset `ID.asset_mark`. Note: this separation of "mark as asset" and "generate preview" also applies to the `ED_asset_mark_id()` C function; if it is desired to have previews rendered after marking as asset, a call to `ED_asset_generate_preview()` is now also required. Reviewed By: sybren Maniphest Tasks: T91197 Differential Revision: https://developer.blender.org/D12922 --- .../editors/asset/ED_asset_mark_clear.h | 9 ++++++++- .../editors/asset/intern/asset_mark_clear.cc | 9 ++++++--- .../blender/editors/asset/intern/asset_ops.cc | 4 +++- source/blender/makesrna/intern/rna_ID.c | 18 +++++++++++++++--- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h index d8b8f15a109..bab1d1bf8a5 100644 --- a/source/blender/editors/asset/ED_asset_mark_clear.h +++ b/source/blender/editors/asset/ED_asset_mark_clear.h @@ -34,7 +34,14 @@ struct bContext; * * \return whether the datablock was marked as asset; false when it is not capable of becoming an * asset, or when it already was an asset. */ -bool ED_asset_mark_id(const struct bContext *C, struct ID *id); +bool ED_asset_mark_id(struct ID *id); + +/** + * Generate preview image for the given datablock. + * + * The preview image might be generated using a background thread. + */ +void ED_asset_generate_preview(const struct bContext *C, struct ID *id); /** * Remove the asset metadata, turning the ID into a "normal" ID. diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc index 8290124c209..e7516bc94c4 100644 --- a/source/blender/editors/asset/intern/asset_mark_clear.cc +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -40,7 +40,7 @@ #include "ED_asset_list.h" #include "ED_asset_mark_clear.h" -bool ED_asset_mark_id(const bContext *C, ID *id) +bool ED_asset_mark_id(ID *id) { if (id->asset_data) { return false; @@ -53,14 +53,17 @@ bool ED_asset_mark_id(const bContext *C, ID *id) id->asset_data = BKE_asset_metadata_create(); - UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); - /* Important for asset storage to update properly! */ ED_assetlist_storage_tag_main_data_dirty(); return true; } +void ED_asset_generate_preview(const bContext *C, ID *id) +{ + UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); +} + bool ED_asset_clear_id(ID *id) { if (!id->asset_data) { diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index bf532903c7c..33a22775280 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -145,7 +145,9 @@ void AssetMarkHelper::operator()(const bContext &C, PointerRNAVec &ids) continue; } - if (ED_asset_mark_id(&C, id)) { + if (ED_asset_mark_id(id)) { + ED_asset_generate_preview(&C, id); + stats.last_id = id; stats.tot_created++; } diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 1776da7e62d..fe03d5245ba 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -669,14 +669,22 @@ static ID *rna_ID_copy(ID *id, Main *bmain) return newid; } -static void rna_ID_asset_mark(ID *id, bContext *C) +static void rna_ID_asset_mark(ID *id) { - if (ED_asset_mark_id(C, id)) { + if (ED_asset_mark_id(id)) { WM_main_add_notifier(NC_ID | NA_EDITED, NULL); WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL); } } +static void rna_ID_asset_generate_preview(ID *id, bContext *C) +{ + ED_asset_generate_preview(C, id); + + WM_main_add_notifier(NC_ID | NA_EDITED, NULL); + WM_main_add_notifier(NC_ASSET | NA_EDITED, NULL); +} + static void rna_ID_asset_clear(ID *id) { if (ED_asset_clear_id(id)) { @@ -1986,13 +1994,17 @@ static void rna_def_ID(BlenderRNA *brna) func, "Enable easier reuse of the data-block through the Asset Browser, with the help of " "customizable metadata (like previews, descriptions and tags)"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT); func = RNA_def_function(srna, "asset_clear", "rna_ID_asset_clear"); RNA_def_function_ui_description( func, "Delete all asset metadata and turn the asset data-block back into a normal data-block"); + func = RNA_def_function(srna, "asset_generate_preview", "rna_ID_asset_generate_preview"); + RNA_def_function_ui_description( + func, "Generate preview image (might be scheduled in a background thread)"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + func = RNA_def_function(srna, "override_create", "rna_ID_override_create"); RNA_def_function_ui_description(func, "Create an overridden local copy of this linked data-block (not " From b3b7319de7aaa17d96e304857bc57401bb11ad17 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 19 Oct 2021 16:08:03 +0200 Subject: [PATCH 0941/1500] Fix T92224: Refactor of append code unexpectedly changed behavior with 'localize all' off. In 2.93 and before, when appending wityh 'localize all' off, all linked IDs (including indirectly linked ones) from initial library would be made local. In 3.0, after refactor from rB3be5ce4aad5e, only directly linked IDs (i.e. user-selected IDs) would be made local. This change was not intentional (result of confusing code and naming in previous implementation), and old behavior is used in some workflows to control which data is kept linked and which data is made local. This commit revert to 2.93 behavior. NOTE: there is still an (extreme) corner case where behavior is different between 2.93 and 3.0: If you append (at the same time) object A from LibA.blend, and object B from LibB.blend, and object B uses somehow a material from LibA.blend: * In 2.93, that material would have been made local (because it belonged to one of the 'initial' libraries, even though not the initial lib of object B). * In 3.0, this material will remain linked, since from object B persective it comes from a different library. --- source/blender/blenloader/BLO_readfile.h | 4 +++- source/blender/windowmanager/intern/wm_files_link.c | 11 ++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 4656bc64a1f..541483d4eed 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -220,7 +220,9 @@ typedef enum eBLOLibLinkFlags { BLO_LIBLINK_NEEDS_ID_TAG_DOIT = 1 << 18, /** Set fake user on appended IDs. */ BLO_LIBLINK_APPEND_SET_FAKEUSER = 1 << 19, - /** Append (make local) also indirect dependencies of appended IDs. */ + /** Append (make local) also indirect dependencies of appended IDs comming from other libraries. + * NOTE: All IDs (including indirectly linked ones) coming from the same initial library are + * always made local. */ BLO_LIBLINK_APPEND_RECURSIVE = 1 << 20, /** Try to re-use previously appended matching ID on new append. */ BLO_LIBLINK_APPEND_LOCAL_ID_REUSE = 1 << 21, diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 260ea73cc3b..461ae20cf8a 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -619,6 +619,13 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) return IDWALK_RET_NOP; } + const bool do_recursive = (data->lapp_data->flag & BLO_LIBLINK_APPEND_RECURSIVE) != 0; + if (!do_recursive && cb_data->id_owner->lib != id->lib) { + /* When `do_recursive` is false, we only make local IDs from same library(-ies) as the + * initially directly linked ones. */ + return IDWALK_RET_NOP; + } + WMLinkAppendDataItem *item = BLI_ghash_lookup(data->lapp_data->new_id_to_item, id); if (item == NULL) { item = wm_link_append_data_item_add(data->lapp_data, id->name, GS(id->name), NULL); @@ -651,7 +658,6 @@ static void wm_append_do(WMLinkAppendData *lapp_data, { BLI_assert((lapp_data->flag & FILE_LINK) == 0); - const bool do_recursive = (lapp_data->flag & BLO_LIBLINK_APPEND_RECURSIVE) != 0; const bool set_fakeuser = (lapp_data->flag & BLO_LIBLINK_APPEND_SET_FAKEUSER) != 0; const bool do_reuse_local_id = (lapp_data->flag & BLO_LIBLINK_APPEND_LOCAL_ID_REUSE) != 0; @@ -723,8 +729,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, /* Only check dependencies if we are not keeping linked data, nor re-using existing local data. */ - if (do_recursive && - !ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) { + if (!ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) { WMLinkAppendDataCallBack cb_data = { .lapp_data = lapp_data, .item = item, .reports = reports}; BKE_library_foreach_ID_link( From 57f1379104e7e36dd60fc270253e4a4555f0e699 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 19 Oct 2021 10:18:54 +0200 Subject: [PATCH 0942/1500] Fix T92265: Outliner crash clicking override warning buttons `outliner_draw_overrides_buts` uses `uiDefIconBlockBut` but doing so without defining a function callback to actually build a block. This will make the button go down the route of spawning a popup, but without a menu. Crash then happens later accesing the (missing) menu in `ui_handler_region_menu`. So while we could dive into making this usage failsafe (carefully checking `BUTTON_STATE_MENU_OPEN` in combination with `uiHandleButtonData->menu` being NULL all over), but it seems much more straightforward to just use `uiDefIconBut` (instead of `uiDefIconBlockBut`) since this Override Warning buttons seem not to intend spawning a menu anyways? Maniphest Tasks: T92265 Differential Revision: https://developer.blender.org/D12917 --- .../editors/space_outliner/outliner_draw.c | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 7cdfb553da5..956c455c545 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1841,16 +1841,20 @@ static bool outliner_draw_overrides_buts(uiBlock *block, if (tip == NULL) { tip = TIP_("Some sub-items require attention"); } - uiBut *bt = uiDefIconBlockBut(block, - NULL, - NULL, - 1, - ICON_ERROR, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - tip); + uiBut *bt = uiDefIconBut(block, + UI_BTYPE_BUT, + 1, + ICON_ERROR, + (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + NULL, + 0.0, + 0.0, + 0.0, + 0.0, + tip); UI_but_flag_enable(bt, but_flag); } any_item_has_warnings = any_item_has_warnings || item_has_warnings || any_child_has_warnings; From 79a88b5e919d56b27e11cadb79ca30474c071af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 19 Oct 2021 16:46:50 +0200 Subject: [PATCH 0943/1500] Fix compilation error about undefined `PATH_MAX` --- source/blender/blenkernel/intern/asset_library_service.cc | 1 + source/blender/blenkernel/intern/asset_library_service_test.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/source/blender/blenkernel/intern/asset_library_service.cc b/source/blender/blenkernel/intern/asset_library_service.cc index 53ce2826022..dec71583441 100644 --- a/source/blender/blenkernel/intern/asset_library_service.cc +++ b/source/blender/blenkernel/intern/asset_library_service.cc @@ -24,6 +24,7 @@ #include "BKE_blender.h" #include "BKE_callbacks.h" +#include "BLI_fileops.h" #include "BLI_path_util.h" #include "BLI_string_ref.hh" diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index ed299028c41..36baa877454 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -19,6 +19,7 @@ #include "asset_library_service.hh" +#include "BLI_fileops.h" #include "BLI_path_util.h" #include "CLG_log.h" From a7ade57e11113a74b11bd0330b073a252ad1c026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 19 Oct 2021 16:55:46 +0200 Subject: [PATCH 0944/1500] Asset Catalogs: allow creating catalog in unsaved blend file Allow creating a new asset catalog in a yet-to-be-saved blend file. The problem was caused by `AssetLibrary` not having an `AssetCatalogService` right after creation; only after loading data from disk was this instance created. It's now always there. --- source/blender/blenkernel/BKE_asset_library.hh | 1 + source/blender/blenkernel/intern/asset_library.cc | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh index b8b4b0f8447..6d5c9836b26 100644 --- a/source/blender/blenkernel/BKE_asset_library.hh +++ b/source/blender/blenkernel/BKE_asset_library.hh @@ -40,6 +40,7 @@ namespace blender::bke { struct AssetLibrary { std::unique_ptr catalog_service; + AssetLibrary(); ~AssetLibrary(); void load(StringRefNull library_root_directory); diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index 48ace8efea1..32e7aab235d 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -102,6 +102,10 @@ void BKE_asset_library_refresh_catalog_simplename(struct AssetLibrary *asset_lib namespace blender::bke { +AssetLibrary::AssetLibrary() : catalog_service(std::make_unique()) +{ +} + AssetLibrary::~AssetLibrary() { if (on_save_callback_store_.func) { From 7a61916717e3daed8172e679d35fe61bf13f360f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Oct 2021 10:42:14 -0500 Subject: [PATCH 0945/1500] Fix T92345: Crash with only pointcloud in attribute transfer node The distances array is only allocated if there are mesh distances to compare to, so it is empty when there is only a point cloud. --- .../nodes/geometry/nodes/node_geo_transfer_attribute.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 4bf856698d9..1c287a0f1bf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -170,7 +170,9 @@ static void get_closest_pointcloud_points(const PointCloud &pointcloud, BLI_bvhtree_find_nearest( tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); r_indices[i] = nearest.index; - r_distances_sq[i] = nearest.dist_sq; + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } } free_bvhtree_from_pointcloud(&tree_data); From a3d4ed20f9511c90fb8eea0f7555c1380a296f76 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 19 Oct 2021 18:00:53 +0200 Subject: [PATCH 0946/1500] Fix liblink tests after recent commit. NOTE: This needs new tests, no time now, will do tomorrow. --- tests/python/bl_blendfile_liblink.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index 4545e0b846a..918c74d17d0 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -211,8 +211,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): instance_object_data=False, set_fake=False, use_recursive=False, do_reuse_local_id=False) assert(len(bpy.data.meshes) == 1) - # This one fails currently, for unclear reasons. - assert(bpy.data.meshes[0].library is not None) + assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].users == 1) assert(len(bpy.data.objects) == 1) assert(bpy.data.objects[0].library is None) From b6c3b41d413813d8059476f2c0357b7a4e51ad22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 19 Oct 2021 18:04:57 +0200 Subject: [PATCH 0947/1500] Cleanup: use nullptr in C++ --- source/blender/blenkernel/intern/asset_library_service.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/asset_library_service.cc b/source/blender/blenkernel/intern/asset_library_service.cc index dec71583441..c5447de645b 100644 --- a/source/blender/blenkernel/intern/asset_library_service.cc +++ b/source/blender/blenkernel/intern/asset_library_service.cc @@ -62,7 +62,7 @@ std::string normalize_directory_path(StringRefNull directory) char dir_normalized[PATH_MAX]; STRNCPY(dir_normalized, directory.c_str()); - BLI_path_normalize_dir(NULL, dir_normalized); + BLI_path_normalize_dir(nullptr, dir_normalized); return std::string(dir_normalized); } } // namespace From 823996b0342b7352fc5b2e24eceb6204612438cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 19 Oct 2021 18:07:22 +0200 Subject: [PATCH 0948/1500] Asset Browser: Improved workflow for asset catalog saving No longer save asset catalogs on blendfile save. Instead: - extend the confirmation prompt for unsaved changes to show unsaved catalogs. - In the confirmation prompt, make catalog saving explicit & optional, just like we do it for external images. {F10881736} - In the Asset Browser catalog tree, show an operator icon to save the catalogs to disk. It's grayed out if there are no changes to save, or if the .blend wasn't saved yet (required to know where to save the catalog definitions to). {F10881743} Much of the work was done by @Severin and reviewed by me, then we swapped roles. Reviewed By: Severin Differential Revision: https://developer.blender.org/D12796 --- .../blender/blenkernel/BKE_asset_catalog.hh | 17 ++++- source/blender/blenkernel/BKE_asset_library.h | 3 + .../blender/blenkernel/BKE_asset_library.hh | 10 ++- .../blenkernel/intern/asset_catalog.cc | 27 ++++++- .../blenkernel/intern/asset_catalog_test.cc | 18 ++--- .../blenkernel/intern/asset_library.cc | 30 +++++--- .../intern/asset_library_service.cc | 19 ++++- .../intern/asset_library_service.hh | 3 + .../intern/asset_library_service_test.cc | 74 +++++++++++++++++++ .../blenkernel/intern/asset_library_test.cc | 6 +- source/blender/editors/asset/CMakeLists.txt | 1 + .../blender/editors/asset/ED_asset_catalog.h | 39 ++++++++++ .../blender/editors/asset/ED_asset_catalog.hh | 4 + .../editors/asset/intern/asset_catalog.cc | 54 +++++++++++++- .../blender/editors/asset/intern/asset_ops.cc | 57 +++++++++++++- source/blender/editors/include/ED_asset.h | 2 + .../space_file/asset_catalog_tree_view.cc | 20 ++--- source/blender/windowmanager/WM_types.h | 4 + .../blender/windowmanager/intern/wm_files.c | 60 +++++++++++++-- .../blender/windowmanager/intern/wm_window.c | 3 +- source/blender/windowmanager/wm_files.h | 2 +- 21 files changed, 402 insertions(+), 51 deletions(-) create mode 100644 source/blender/editors/asset/ED_asset_catalog.h diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index d5c8acff960..f7896e0f51e 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -63,13 +63,21 @@ class AssetCatalogService { AssetCatalogService(); explicit AssetCatalogService(const CatalogFilePath &asset_library_root); + /** + * Set global tag indicating that some catalog modifications are unsaved that could get lost + * on exit. This tag is not set by internal catalog code, the catalog service user is responsible + * for it. It is cleared by #write_to_disk(). + */ + void tag_has_unsaved_changes(); + bool has_unsaved_changes() const; + /** Load asset catalog definitions from the files found in the asset library. */ void load_from_disk(); /** Load asset catalog definitions from the given file or directory. */ void load_from_disk(const CatalogFilePath &file_or_directory_path); /** - * Write the catalog definitions to disk in response to the blend file being saved. + * Write the catalog definitions to disk. * * The location where the catalogs are saved is variable, and depends on the location of the * blend file. The first matching rule wins: @@ -85,7 +93,7 @@ class AssetCatalogService { * * Return true on success, which either means there were no in-memory categories to save, * or the save was successful. */ - bool write_to_disk_on_blendfile_save(const CatalogFilePath &blend_file_path); + bool write_to_disk(const CatalogFilePath &blend_file_path); /** * Merge on-disk changes into the in-memory asset catalogs. @@ -166,10 +174,15 @@ class AssetCatalogService { Vector> undo_snapshots_; Vector> redo_snapshots_; + bool has_unsaved_changes_ = false; void load_directory_recursive(const CatalogFilePath &directory_path); void load_single_file(const CatalogFilePath &catalog_definition_file_path); + /** Implementation of #write_to_disk() that doesn't clear the "has unsaved changes" tag. */ + bool write_to_disk_ex(const CatalogFilePath &blend_file_path); + void untag_has_unsaved_changes(); + std::unique_ptr parse_catalog_file( const CatalogFilePath &catalog_definition_file_path); diff --git a/source/blender/blenkernel/BKE_asset_library.h b/source/blender/blenkernel/BKE_asset_library.h index b4674a8d932..ca12fd6f4fb 100644 --- a/source/blender/blenkernel/BKE_asset_library.h +++ b/source/blender/blenkernel/BKE_asset_library.h @@ -77,6 +77,9 @@ bool BKE_asset_library_find_suitable_root_path_from_main( void BKE_asset_library_refresh_catalog_simplename(struct AssetLibrary *asset_library, struct AssetMetaData *asset_data); +/** Return whether any loaded AssetLibrary has unsaved changes to its catalogs. */ +bool BKE_asset_library_has_any_unsaved_catalogs(void); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh index 6d5c9836b26..64c8afecaef 100644 --- a/source/blender/blenkernel/BKE_asset_library.hh +++ b/source/blender/blenkernel/BKE_asset_library.hh @@ -38,6 +38,10 @@ namespace blender::bke { * For now this is only for catalogs, later this can be expanded to indexes/caches/more. */ struct AssetLibrary { + /* Controlled by #ED_asset_catalogs_set_save_catalogs_when_file_is_saved, + * for managing the "Save Catalog Changes" in the quit-confirmation dialog box. */ + static bool save_catalogs_when_file_is_saved; + std::unique_ptr catalog_service; AssetLibrary(); @@ -53,10 +57,10 @@ struct AssetLibrary { * meant to help recover from. */ void refresh_catalog_simplename(struct AssetMetaData *asset_data); - void on_save_handler_register(); - void on_save_handler_unregister(); + void on_blend_save_handler_register(); + void on_blend_save_handler_unregister(); - void on_save_post(struct Main *, struct PointerRNA **pointers, const int num_pointers); + void on_blend_save_post(struct Main *, struct PointerRNA **pointers, const int num_pointers); private: bCallbackFuncStore on_save_callback_store_{}; diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index f3a4f88ef38..aa8f12d0e23 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -66,6 +66,21 @@ AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_ro { } +void AssetCatalogService::tag_has_unsaved_changes() +{ + has_unsaved_changes_ = true; +} + +void AssetCatalogService::untag_has_unsaved_changes() +{ + has_unsaved_changes_ = false; +} + +bool AssetCatalogService::has_unsaved_changes() const +{ + return has_unsaved_changes_; +} + bool AssetCatalogService::is_empty() const { return catalog_collection_->catalogs_.is_empty(); @@ -344,7 +359,17 @@ void AssetCatalogService::merge_from_disk_before_writing() cdf->parse_catalog_file(cdf->file_path, catalog_parsed_callback); } -bool AssetCatalogService::write_to_disk_on_blendfile_save(const CatalogFilePath &blend_file_path) +bool AssetCatalogService::write_to_disk(const CatalogFilePath &blend_file_path) +{ + if (!write_to_disk_ex(blend_file_path)) { + return false; + } + + untag_has_unsaved_changes(); + return true; +} + +bool AssetCatalogService::write_to_disk_ex(const CatalogFilePath &blend_file_path) { /* TODO(Sybren): expand to support multiple CDFs. */ diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index d1413d521a1..d743d250c1d 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -222,7 +222,7 @@ class AssetCatalogTest : public testing::Test { const AssetCatalog *cat = service.create_catalog("some/catalog/path"); /* Mock that the blend file is written to the directory already containing a CDF. */ - ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename)); + ASSERT_TRUE(service.write_to_disk(blendfilename)); /* Test that the CDF still exists in the expected location. */ EXPECT_TRUE(BLI_exists(cdf_toplevel.c_str())); @@ -489,7 +489,7 @@ TEST_F(AssetCatalogTest, no_writing_empty_files) { const CatalogFilePath temp_lib_root = create_temp_path(); AssetCatalogService service(temp_lib_root); - service.write_to_disk_on_blendfile_save(temp_lib_root + "phony.blend"); + service.write_to_disk(temp_lib_root + "phony.blend"); const CatalogFilePath default_cdf_path = temp_lib_root + AssetCatalogService::DEFAULT_CATALOG_FILENAME; @@ -515,7 +515,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf) const AssetCatalog *cat = service.create_catalog("some/catalog/path"); const CatalogFilePath blendfilename = top_level_dir + "subdir/some_file.blend"; - ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename)); + ASSERT_TRUE(service.write_to_disk(blendfilename)); EXPECT_EQ(cdf_filename, service.get_catalog_definition_file()->file_path); /* Test that the CDF was created in the expected location. */ @@ -542,7 +542,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_empty_directory) const AssetCatalog *cat = service.create_catalog("some/catalog/path"); const CatalogFilePath blendfilename = target_dir + "some_file.blend"; - ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename)); + ASSERT_TRUE(service.write_to_disk(blendfilename)); /* Test that the CDF was created in the expected location. */ const CatalogFilePath expected_cdf_path = target_dir + @@ -575,7 +575,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_me /* Mock that the blend file is written to a subdirectory of the asset library. */ const CatalogFilePath blendfilename = target_dir + "some_file.blend"; - ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename)); + ASSERT_TRUE(service.write_to_disk(blendfilename)); /* Test that the CDF still exists in the expected location. */ const CatalogFilePath backup_filename = writable_cdf_file + "~"; @@ -630,7 +630,7 @@ TEST_F(AssetCatalogTest, create_first_catalog_from_scratch) EXPECT_FALSE(BLI_exists(temp_lib_root.c_str())); /* Writing to disk should create the directory + the default file. */ - service.write_to_disk_on_blendfile_save(temp_lib_root + "phony.blend"); + service.write_to_disk(temp_lib_root + "phony.blend"); EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str())); const CatalogFilePath definition_file_path = temp_lib_root + "/" + @@ -681,7 +681,7 @@ TEST_F(AssetCatalogTest, create_catalog_after_loading_file) << "expecting newly added catalog to not yet be saved to " << temp_lib_root; /* Write and reload the catalog file. */ - service.write_to_disk_on_blendfile_save(temp_lib_root + "phony.blend"); + service.write_to_disk(temp_lib_root + "phony.blend"); AssetCatalogService reloaded_service(temp_lib_root); reloaded_service.load_from_disk(); EXPECT_NE(nullptr, reloaded_service.find_catalog(UUID_POSES_ELLIE)) @@ -867,7 +867,7 @@ TEST_F(AssetCatalogTest, merge_catalog_files) ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str())); /* Overwrite the modified file. This should merge the on-disk file with our catalogs. */ - service.write_to_disk_on_blendfile_save(cdf_dir + "phony.blend"); + service.write_to_disk(cdf_dir + "phony.blend"); AssetCatalogService loaded_service(cdf_dir); loaded_service.load_from_disk(); @@ -897,7 +897,7 @@ TEST_F(AssetCatalogTest, backups) AssetCatalogService service(cdf_dir); service.load_from_disk(); service.delete_catalog_by_id(UUID_POSES_ELLIE); - service.write_to_disk_on_blendfile_save(cdf_dir + "phony.blend"); + service.write_to_disk(cdf_dir + "phony.blend"); const CatalogFilePath backup_path = writable_cdf_file + "~"; ASSERT_TRUE(BLI_is_file(backup_path.c_str())); diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index 32e7aab235d..6c4660ae75d 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -35,6 +35,8 @@ #include +bool blender::bke::AssetLibrary::save_catalogs_when_file_is_saved = true; + /** * Loading an asset library at this point only means loading the catalogs. Later on this should * invoke reading of asset representations too. @@ -52,6 +54,12 @@ struct AssetLibrary *BKE_asset_library_load(const char *library_path) return reinterpret_cast(lib); } +bool BKE_asset_library_has_any_unsaved_catalogs() +{ + blender::bke::AssetLibraryService *service = blender::bke::AssetLibraryService::get(); + return service->has_any_unsaved_catalogs(); +} + bool BKE_asset_library_find_suitable_root_path_from_path(const char *input_path, char *r_library_path) { @@ -109,7 +117,7 @@ AssetLibrary::AssetLibrary() : catalog_service(std::make_unique(arg); - asset_lib->on_save_post(main, pointers, num_pointers); + asset_lib->on_blend_save_post(main, pointers, num_pointers); } + } // namespace -void AssetLibrary::on_save_handler_register() +void AssetLibrary::on_blend_save_handler_register() { /* The callback system doesn't own `on_save_callback_store_`. */ on_save_callback_store_.alloc = false; @@ -142,22 +151,24 @@ void AssetLibrary::on_save_handler_register() BKE_callback_add(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST); } -void AssetLibrary::on_save_handler_unregister() +void AssetLibrary::on_blend_save_handler_unregister() { BKE_callback_remove(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST); on_save_callback_store_.func = nullptr; on_save_callback_store_.arg = nullptr; } -void AssetLibrary::on_save_post(struct Main *main, - struct PointerRNA ** /*pointers*/, - const int /*num_pointers*/) +void AssetLibrary::on_blend_save_post(struct Main *main, + struct PointerRNA ** /*pointers*/, + const int /*num_pointers*/) { if (this->catalog_service == nullptr) { return; } - this->catalog_service->write_to_disk_on_blendfile_save(main->name); + if (save_catalogs_when_file_is_saved) { + this->catalog_service->write_to_disk(main->name); + } } void AssetLibrary::refresh_catalog_simplename(struct AssetMetaData *asset_data) @@ -166,15 +177,12 @@ void AssetLibrary::refresh_catalog_simplename(struct AssetMetaData *asset_data) asset_data->catalog_simple_name[0] = '\0'; return; } - const AssetCatalog *catalog = this->catalog_service->find_catalog(asset_data->catalog_id); if (catalog == nullptr) { /* No-op if the catalog cannot be found. This could be the kind of "the catalog definition file * is corrupt/lost" scenario that the simple name is meant to help recover from. */ return; } - STRNCPY(asset_data->catalog_simple_name, catalog->simple_name.c_str()); } - } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_library_service.cc b/source/blender/blenkernel/intern/asset_library_service.cc index c5447de645b..aeded0bc128 100644 --- a/source/blender/blenkernel/intern/asset_library_service.cc +++ b/source/blender/blenkernel/intern/asset_library_service.cc @@ -83,7 +83,7 @@ AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull top_l AssetLibraryPtr lib_uptr = std::make_unique(); AssetLibrary *lib = lib_uptr.get(); - lib->on_save_handler_register(); + lib->on_blend_save_handler_register(); lib->load(top_dir_trailing_slash); on_disk_libraries_.add_new(top_dir_trailing_slash, std::move(lib_uptr)); @@ -99,7 +99,7 @@ AssetLibrary *AssetLibraryService::get_asset_library_current_file() else { CLOG_INFO(&LOG, 2, "get current file lib (loaded)"); current_file_library_ = std::make_unique(); - current_file_library_->on_save_handler_register(); + current_file_library_->on_blend_save_handler_register(); } AssetLibrary *lib = current_file_library_.get(); @@ -148,4 +148,19 @@ void AssetLibraryService::app_handler_unregister() on_load_callback_store_.arg = nullptr; } +bool AssetLibraryService::has_any_unsaved_catalogs() const +{ + if (current_file_library_ && current_file_library_->catalog_service->has_unsaved_changes()) { + return true; + } + + for (const auto &asset_lib_uptr : on_disk_libraries_.values()) { + if (asset_lib_uptr->catalog_service->has_unsaved_changes()) { + return true; + } + } + + return false; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_library_service.hh b/source/blender/blenkernel/intern/asset_library_service.hh index 63ffe56ab74..03df706bc42 100644 --- a/source/blender/blenkernel/intern/asset_library_service.hh +++ b/source/blender/blenkernel/intern/asset_library_service.hh @@ -66,6 +66,9 @@ class AssetLibraryService { /** Get the "Current File" asset library. */ AssetLibrary *get_asset_library_current_file(); + /** Returns whether there are any known asset libraries with unsaved catalog edits. */ + bool has_any_unsaved_catalogs() const; + protected: static std::unique_ptr instance_; diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index 36baa877454..ed132d7a8d8 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -22,6 +22,8 @@ #include "BLI_fileops.h" #include "BLI_path_util.h" +#include "BKE_appdir.h" + #include "CLG_log.h" #include "testing/testing.h" @@ -31,6 +33,7 @@ namespace blender::bke::tests { class AssetLibraryServiceTest : public testing::Test { public: CatalogFilePath asset_library_root_; + CatalogFilePath temp_library_path_; static void SetUpTestSuite() { @@ -48,11 +51,34 @@ class AssetLibraryServiceTest : public testing::Test { FAIL(); } asset_library_root_ = test_files_dir + "/" + "asset_library"; + temp_library_path_ = ""; } void TearDown() override { AssetLibraryService::destroy(); + + if (!temp_library_path_.empty()) { + BLI_delete(temp_library_path_.c_str(), true, true); + temp_library_path_ = ""; + } + } + + /* Register a temporary path, which will be removed at the end of the test. + * The returned path ends in a slash. */ + CatalogFilePath use_temp_path() + { + BKE_tempdir_init(""); + const CatalogFilePath tempdir = BKE_tempdir_session(); + temp_library_path_ = tempdir + "test-temporary-path/"; + return temp_library_path_; + } + + CatalogFilePath create_temp_path() + { + CatalogFilePath path = use_temp_path(); + BLI_dir_create_recursive(path.c_str()); + return path; } }; @@ -122,4 +148,52 @@ TEST_F(AssetLibraryServiceTest, catalogs_loaded) << "Catalogs should be loaded after getting an asset library from disk."; } +TEST_F(AssetLibraryServiceTest, has_any_unsaved_catalogs) +{ + AssetLibraryService *const service = AssetLibraryService::get(); + EXPECT_FALSE(service->has_any_unsaved_catalogs()) + << "Empty AssetLibraryService should have no unsaved catalogs"; + + AssetLibrary *const lib = service->get_asset_library_on_disk(asset_library_root_); + AssetCatalogService *const cat_service = lib->catalog_service.get(); + EXPECT_FALSE(service->has_any_unsaved_catalogs()) + << "Unchanged AssetLibrary should have no unsaved catalogs"; + + const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); + cat_service->prune_catalogs_by_id(UUID_POSES_ELLIE); + EXPECT_FALSE(service->has_any_unsaved_catalogs()) + << "Deletion of catalogs via AssetCatalogService should not tag as 'unsaved changes'."; + + cat_service->tag_has_unsaved_changes(); + EXPECT_TRUE(service->has_any_unsaved_catalogs()) + << "Tagging as having unsaved changes of a single catalog service should result in unsaved " + "changes being reported."; +} + +TEST_F(AssetLibraryServiceTest, has_any_unsaved_catalogs_after_write) +{ + const CatalogFilePath writable_dir = create_temp_path(); /* Has trailing slash. */ + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + CatalogFilePath writable_cdf_file = writable_dir + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + BLI_path_slash_native(writable_cdf_file.data()); + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); + + AssetLibraryService *const service = AssetLibraryService::get(); + AssetLibrary *const lib = service->get_asset_library_on_disk(writable_dir); + + EXPECT_FALSE(service->has_any_unsaved_catalogs()) + << "Unchanged AssetLibrary should have no unsaved catalogs"; + + AssetCatalogService *const cat_service = lib->catalog_service.get(); + cat_service->tag_has_unsaved_changes(); + + EXPECT_TRUE(service->has_any_unsaved_catalogs()) + << "Tagging as having unsaved changes of a single catalog service should result in unsaved " + "changes being reported."; + + cat_service->write_to_disk(writable_dir + "dummy_path.blend"); + EXPECT_FALSE(service->has_any_unsaved_catalogs()) + << "Written AssetCatalogService should have no unsaved catalogs"; +} + } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc index 7c3587c6dfa..c6c949a7ec4 100644 --- a/source/blender/blenkernel/intern/asset_library_test.cc +++ b/source/blender/blenkernel/intern/asset_library_test.cc @@ -29,7 +29,7 @@ namespace blender::bke::tests { -class AssetLibraryServiceTest : public testing::Test { +class AssetLibraryTest : public testing::Test { public: static void SetUpTestSuite() { @@ -46,7 +46,7 @@ class AssetLibraryServiceTest : public testing::Test { } }; -TEST_F(AssetLibraryServiceTest, bke_asset_library_load) +TEST_F(AssetLibraryTest, bke_asset_library_load) { const std::string test_files_dir = blender::tests::flags_test_asset_dir(); if (test_files_dir.empty()) { @@ -73,7 +73,7 @@ TEST_F(AssetLibraryServiceTest, bke_asset_library_load) EXPECT_EQ("character/Ellie/poselib", poses_ellie->path.str()); } -TEST_F(AssetLibraryServiceTest, load_nonexistent_directory) +TEST_F(AssetLibraryTest, load_nonexistent_directory) { const std::string test_files_dir = blender::tests::flags_test_asset_dir(); if (test_files_dir.empty()) { diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index b6657bfca63..9017f136319 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC intern/asset_ops.cc intern/asset_temp_id_consumer.cc + ED_asset_catalog.h ED_asset_catalog.hh ED_asset_filter.h ED_asset_handle.h diff --git a/source/blender/editors/asset/ED_asset_catalog.h b/source/blender/editors/asset/ED_asset_catalog.h new file mode 100644 index 00000000000..451ec0d5984 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_catalog.h @@ -0,0 +1,39 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#pragma once + +#include "BLI_utildefines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Main; +struct AssetLibrary; + +void ED_asset_catalogs_save_from_main_path(struct AssetLibrary *library, const struct Main *bmain); + +void ED_asset_catalogs_set_save_catalogs_when_file_is_saved(bool should_save); +bool ED_asset_catalogs_get_save_catalogs_when_file_is_saved(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh index cffd7728a60..8b8fc4d3574 100644 --- a/source/blender/editors/asset/ED_asset_catalog.hh +++ b/source/blender/editors/asset/ED_asset_catalog.hh @@ -33,3 +33,7 @@ blender::bke::AssetCatalog *ED_asset_catalog_add(AssetLibrary *library, blender::StringRefNull name, blender::StringRef parent_path = nullptr); void ED_asset_catalog_remove(AssetLibrary *library, const blender::bke::CatalogID &catalog_id); + +void ED_asset_catalog_rename(AssetLibrary *library, + blender::bke::CatalogID catalog_id, + blender::StringRefNull new_name); diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index 056fda63bd7..f8b33e76447 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -21,11 +21,15 @@ #include "BKE_asset_catalog.hh" #include "BKE_asset_catalog_path.hh" #include "BKE_asset_library.hh" +#include "BKE_main.h" #include "BLI_string_utils.h" +#include "ED_asset_catalog.h" #include "ED_asset_catalog.hh" +#include "WM_api.h" + using namespace blender; using namespace blender::bke; @@ -67,7 +71,13 @@ AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library, AssetCatalogPath fullpath = AssetCatalogPath(parent_path) / unique_name; catalog_service->undo_push(); - return catalog_service->create_catalog(fullpath); + catalog_service->tag_has_unsaved_changes(); + bke::AssetCatalog *new_catalog = catalog_service->create_catalog(fullpath); + if (!new_catalog) { + return nullptr; + } + + return new_catalog; } void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_id) @@ -79,5 +89,47 @@ void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_i } catalog_service->undo_push(); + catalog_service->tag_has_unsaved_changes(); catalog_service->prune_catalogs_by_id(catalog_id); } + +void ED_asset_catalog_rename(::AssetLibrary *library, + const CatalogID catalog_id, + const StringRefNull new_name) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + BLI_assert_unreachable(); + return; + } + + const AssetCatalog *catalog = catalog_service->find_catalog(catalog_id); + + AssetCatalogPath new_path = catalog->path.parent(); + new_path = new_path / StringRef(new_name); + + catalog_service->undo_push(); + catalog_service->tag_has_unsaved_changes(); + catalog_service->update_catalog_path(catalog_id, new_path); +} + +void ED_asset_catalogs_save_from_main_path(::AssetLibrary *library, const Main *bmain) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + BLI_assert_unreachable(); + return; + } + + catalog_service->write_to_disk(bmain->name); +} + +void ED_asset_catalogs_set_save_catalogs_when_file_is_saved(const bool should_save) +{ + bke::AssetLibrary::save_catalogs_when_file_is_saved = should_save; +} + +bool ED_asset_catalogs_get_save_catalogs_when_file_is_saved() +{ + return bke::AssetLibrary::save_catalogs_when_file_is_saved; +} diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 33a22775280..834874abc19 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -22,6 +22,7 @@ #include "BKE_asset_library.hh" #include "BKE_context.h" #include "BKE_lib_id.h" +#include "BKE_main.h" #include "BKE_report.h" #include "BLI_string_ref.hh" @@ -398,7 +399,8 @@ static int asset_catalog_new_exec(bContext *C, wmOperator *op) MEM_freeN(parent_path); - WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + WM_event_add_notifier_ex( + CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_CATALOGS, nullptr); return OPERATOR_FINISHED; } @@ -436,7 +438,8 @@ static int asset_catalog_delete_exec(bContext *C, wmOperator *op) MEM_freeN(catalog_id_str); - WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + WM_event_add_notifier_ex( + CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_CATALOGS, nullptr); return OPERATOR_FINISHED; } @@ -561,6 +564,55 @@ static void ASSET_OT_catalog_undo_push(struct wmOperatorType *ot) /* -------------------------------------------------------------------- */ +static bool asset_catalogs_save_poll(bContext *C) +{ + if (!asset_catalog_operator_poll(C)) { + return false; + } + + const Main *bmain = CTX_data_main(C); + if (!bmain->name[0]) { + CTX_wm_operator_poll_msg_set(C, "Cannot save asset catalogs before the Blender file is saved"); + return false; + } + + if (!BKE_asset_library_has_any_unsaved_catalogs()) { + CTX_wm_operator_poll_msg_set(C, "No changes to be saved"); + return false; + } + + return true; +} + +static int asset_catalogs_save_exec(bContext *C, wmOperator * /*op*/) +{ + const SpaceFile *sfile = CTX_wm_space_file(C); + ::AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); + + ED_asset_catalogs_save_from_main_path(asset_library, CTX_data_main(C)); + + WM_event_add_notifier_ex( + CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_CATALOGS, nullptr); + + return OPERATOR_FINISHED; +} + +static void ASSET_OT_catalogs_save(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Save Asset Catalogs"; + ot->description = + "Make any edits to any catalogs permanent by writing the current set up to the asset " + "library"; + ot->idname = "ASSET_OT_catalogs_save"; + + /* api callbacks */ + ot->exec = asset_catalogs_save_exec; + ot->poll = asset_catalogs_save_poll; +} + +/* -------------------------------------------------------------------- */ + void ED_operatortypes_asset(void) { WM_operatortype_append(ASSET_OT_mark); @@ -568,6 +620,7 @@ void ED_operatortypes_asset(void) WM_operatortype_append(ASSET_OT_catalog_new); WM_operatortype_append(ASSET_OT_catalog_delete); + WM_operatortype_append(ASSET_OT_catalogs_save); WM_operatortype_append(ASSET_OT_catalog_undo); WM_operatortype_append(ASSET_OT_catalog_redo); WM_operatortype_append(ASSET_OT_catalog_undo_push); diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index 8f19c97e671..6b8d33fa713 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -36,6 +36,7 @@ void ED_operatortypes_asset(void); } #endif +#include "../asset/ED_asset_catalog.h" #include "../asset/ED_asset_filter.h" #include "../asset/ED_asset_handle.h" #include "../asset/ED_asset_library.h" @@ -45,5 +46,6 @@ void ED_operatortypes_asset(void); /* C++ only headers. */ #ifdef __cplusplus +# include "../asset/ED_asset_catalog.hh" # include "../asset/ED_asset_list.hh" #endif diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 8906cf34288..0eade57934e 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -21,8 +21,6 @@ * \ingroup spfile */ -#include "ED_fileselect.h" - #include "DNA_space_types.h" #include "BKE_asset.h" @@ -33,6 +31,9 @@ #include "BLT_translation.h" +#include "ED_asset.h" +#include "ED_fileselect.h" + #include "RNA_access.h" #include "UI_interface.h" @@ -52,7 +53,7 @@ using namespace blender::bke; namespace blender::ed::asset_browser { class AssetCatalogTreeView : public ui::AbstractTreeView { - bke::AssetCatalogService *catalog_service_; + ::AssetLibrary *asset_library_; /** The asset catalog tree this tree-view represents. */ bke::AssetCatalogTree *catalog_tree_; FileAssetSelectParams *params_; @@ -129,7 +130,7 @@ class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params, SpaceFile &space_file) - : catalog_service_(BKE_asset_library_get_catalog_service(library)), + : asset_library_(library), catalog_tree_(BKE_asset_library_get_catalog_tree(library)), params_(params), space_file_(space_file) @@ -353,12 +354,7 @@ bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) const AssetCatalogTreeView &tree_view = static_cast( get_tree_view()); - - AssetCatalogPath new_path = catalog_item_.catalog_path().parent(); - new_path = new_path / StringRef(new_name); - - tree_view.catalog_service_->undo_push(); - tree_view.catalog_service_->update_catalog_path(catalog_item_.get_catalog_id(), new_path); + ED_asset_catalog_rename(tree_view.asset_library_, catalog_item_.get_catalog_id(), new_name); return true; } @@ -369,6 +365,10 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) ui::BasicTreeViewItem::build_row(row); PointerRNA *props; + + UI_but_extra_operator_icon_add( + (uiBut *)tree_row_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK); + props = UI_but_extra_operator_icon_add( (uiBut *)tree_row_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); /* No parent path to use the root level. */ diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index b5f6caf4cb7..081e76e3d5b 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -462,6 +462,10 @@ typedef struct wmNotifier { #define ND_ASSET_LIST (1 << 16) #define ND_ASSET_LIST_PREVIEW (2 << 16) #define ND_ASSET_LIST_READING (3 << 16) +/* Catalog data changed, requiring a redraw of catalog UIs. Note that this doesn't denote a + * reloading of asset libraries & their catalogs should happen. That only happens on explicit user + * action. */ +#define ND_ASSET_CATALOGS (4 << 16) /* subtype, 256 entries too */ #define NOTE_SUBTYPE 0x0000FF00 diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 9d3fd9b2ec9..e203281297b 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -75,6 +75,7 @@ #include "BKE_addon.h" #include "BKE_appdir.h" +#include "BKE_asset_library.h" #include "BKE_autoexec.h" #include "BKE_blender.h" #include "BKE_blendfile.h" @@ -105,6 +106,7 @@ #include "IMB_imbuf_types.h" #include "IMB_thumbs.h" +#include "ED_asset.h" #include "ED_datafiles.h" #include "ED_fileselect.h" #include "ED_image.h" @@ -168,9 +170,13 @@ void WM_file_tag_modified(void) } } -bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm) +/** + * Check if there is data that would be lost when closing the current file without saving. + */ +bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWindowManager *wm) { - return !wm->file_saved || ED_image_should_save_modified(bmain); + return !wm->file_saved || ED_image_should_save_modified(bmain) || + BKE_asset_library_has_any_unsaved_catalogs(); } /** \} */ @@ -3598,6 +3604,14 @@ static void wm_block_file_close_save_button(uiBlock *block, wmGenericCallback *p static const char *close_file_dialog_name = "file_close_popup"; +static void save_catalogs_when_file_is_closed_set_fn(bContext *UNUSED(C), + void *arg1, + void *UNUSED(arg2)) +{ + char *save_catalogs_when_file_is_closed = arg1; + ED_asset_catalogs_set_save_catalogs_when_file_is_saved(*save_catalogs_when_file_is_closed != 0); +} + static uiBlock *block_create__close_file_dialog(struct bContext *C, struct ARegion *region, void *arg1) @@ -3654,11 +3668,17 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C, MEM_freeN(message); } + /* Used to determine if extra separators are needed. */ + bool has_extra_checkboxes = false; + /* Modified Images Checkbox. */ if (modified_images_count > 0) { char message[64]; BLI_snprintf(message, sizeof(message), "Save %u modified image(s)", modified_images_count); - uiItemS(layout); + /* Only the first checkbox should get extra separation. */ + if (!has_extra_checkboxes) { + uiItemS(layout); + } uiDefButBitC(block, UI_BTYPE_CHECKBOX, 1, @@ -3674,11 +3694,41 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C, 0, 0, ""); + has_extra_checkboxes = true; + } + + if (BKE_asset_library_has_any_unsaved_catalogs()) { + static char save_catalogs_when_file_is_closed; + + save_catalogs_when_file_is_closed = ED_asset_catalogs_get_save_catalogs_when_file_is_saved(); + + /* Only the first checkbox should get extra separation. */ + if (!has_extra_checkboxes) { + uiItemS(layout); + } + uiBut *but = uiDefButBitC(block, + UI_BTYPE_CHECKBOX, + 1, + 0, + "Save modified asset catalogs", + 0, + 0, + 0, + UI_UNIT_Y, + &save_catalogs_when_file_is_closed, + 0, + 0, + 0, + 0, + ""); + UI_but_func_set( + but, save_catalogs_when_file_is_closed_set_fn, &save_catalogs_when_file_is_closed, NULL); + has_extra_checkboxes = true; } BKE_reports_clear(&reports); - uiItemS_ex(layout, modified_images_count > 0 ? 2.0f : 4.0f); + uiItemS_ex(layout, has_extra_checkboxes ? 2.0f : 4.0f); /* Buttons. */ #ifdef _WIN32 @@ -3759,7 +3809,7 @@ bool wm_operator_close_file_dialog_if_needed(bContext *C, wmGenericCallbackFn post_action_fn) { if (U.uiflag & USER_SAVE_PROMPT && - wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) { + wm_file_or_session_data_has_unsaved_changes(CTX_data_main(C), CTX_wm_manager(C))) { wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__); callback->exec = post_action_fn; callback->user_data = IDP_CopyProperty(op->properties); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 7113beef56e..2c92d4ac6f8 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -368,7 +368,8 @@ void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win) CTX_wm_window_set(C, win); if (U.uiflag & USER_SAVE_PROMPT) { - if (wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C)) && !G.background) { + if (wm_file_or_session_data_has_unsaved_changes(CTX_data_main(C), CTX_wm_manager(C)) && + !G.background) { wm_window_raise(win); wm_confirm_quit(C); } diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 2fa5a68829e..135f31cf8ac 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -79,7 +79,7 @@ void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action); bool wm_operator_close_file_dialog_if_needed(bContext *C, wmOperator *op, wmGenericCallbackFn exec_fn); -bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm); +bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWindowManager *wm); void WM_OT_save_homefile(struct wmOperatorType *ot); void WM_OT_save_userpref(struct wmOperatorType *ot); From 56bf34aa174f99eb26a678a6d1bd53e96c94ed00 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Tue, 19 Oct 2021 15:03:35 +0100 Subject: [PATCH 0949/1500] Geometry Nodes: Add Magic texture node Port shader node magic texture Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12732 --- release/scripts/startup/nodeitems_builtins.py | 1 + .../shader/nodes/node_shader_tex_magic.cc | 127 +++++++++++++++++- 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 05f2e900841..cfe685ef403 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -723,6 +723,7 @@ geometry_node_categories = [ ]), GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ NodeItem("ShaderNodeTexGradient"), + NodeItem("ShaderNodeTexMagic"), NodeItem("ShaderNodeTexMusgrave"), NodeItem("ShaderNodeTexNoise"), NodeItem("ShaderNodeTexVoronoi"), diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc index 0bd15005816..b6cdcf86528 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc @@ -58,17 +58,140 @@ static int node_shader_gpu_tex_magic(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_tex_magic", in, out, GPU_constant(&depth)); } -/* node type definition */ +namespace blender::nodes { + +class MagicFunction : public fn::MultiFunction { + private: + int depth_; + + public: + MagicFunction(int depth) : depth_(depth) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"MagicFunction"}; + signature.single_input("Vector"); + signature.single_input("Scale"); + signature.single_input("Distortion"); + signature.single_output("Color"); + signature.single_output("Fac"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &scale = params.readonly_single_input(1, "Scale"); + const VArray &distortion = params.readonly_single_input(2, "Distortion"); + + MutableSpan r_color = params.uninitialized_single_output( + 3, "Color"); + MutableSpan r_fac = params.uninitialized_single_output_if_required(4, "Fac"); + + const bool compute_factor = !r_fac.is_empty(); + + for (int64_t i : mask) { + const float3 co = vector[i] * scale[i]; + const float distort = distortion[i]; + float x = sinf((co[0] + co[1] + co[2]) * 5.0f); + float y = cosf((-co[0] + co[1] - co[2]) * 5.0f); + float z = -cosf((-co[0] - co[1] + co[2]) * 5.0f); + + if (depth_ > 0) { + x *= distort; + y *= distort; + z *= distort; + y = -cosf(x - y + z); + y *= distort; + + if (depth_ > 1) { + x = cosf(x - y - z); + x *= distort; + + if (depth_ > 2) { + z = sinf(-x - y - z); + z *= distort; + + if (depth_ > 3) { + x = -cosf(-x + y - z); + x *= distort; + + if (depth_ > 4) { + y = -sinf(-x + y + z); + y *= distort; + + if (depth_ > 5) { + y = -cosf(-x + y + z); + y *= distort; + + if (depth_ > 6) { + x = cosf(x + y + z); + x *= distort; + + if (depth_ > 7) { + z = sinf(x + y - z); + z *= distort; + + if (depth_ > 8) { + x = -cosf(-x - y + z); + x *= distort; + + if (depth_ > 9) { + y = -sinf(x - y + z); + y *= distort; + } + } + } + } + } + } + } + } + } + } + + if (distort != 0.0f) { + const float d = distort * 2.0f; + x /= d; + y /= d; + z /= d; + } + + r_color[i] = ColorGeometry4f(0.5f - x, 0.5f - y, 0.5f - z, 1.0f); + } + if (compute_factor) { + for (int64_t i : mask) { + r_fac[i] = (r_color[i].r + r_color[i].g + r_color[i].b) * (1.0f / 3.0f); + } + } + } +}; + +static void sh_node_magic_tex_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexMagic *tex = (NodeTexMagic *)node.storage; + builder.construct_and_set_matching_fn(tex->depth); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_magic(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_MAGIC, "Magic Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_MAGIC, "Magic Texture", NODE_CLASS_TEXTURE, 0); ntype.declare = blender::nodes::sh_node_tex_magic_declare; node_type_init(&ntype, node_shader_init_tex_magic); node_type_storage( &ntype, "NodeTexMagic", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_magic); + ntype.build_multi_function = blender::nodes::sh_node_magic_tex_build_multi_function; nodeRegisterType(&ntype); } From 67dbb42236da7aa32a420707213b3fa69374a930 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Tue, 19 Oct 2021 17:28:55 +0100 Subject: [PATCH 0950/1500] Geometry Nodes: Add Wave texture node Port shader wave texture node Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12733 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenlib/BLI_float3.hh | 5 + .../shader/nodes/node_shader_tex_wave.cc | 143 +++++++++++++++++- 3 files changed, 147 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index cfe685ef403..215bd65cd1a 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -727,6 +727,7 @@ geometry_node_categories = [ NodeItem("ShaderNodeTexMusgrave"), NodeItem("ShaderNodeTexNoise"), NodeItem("ShaderNodeTexVoronoi"), + NodeItem("ShaderNodeTexWave"), NodeItem("ShaderNodeTexWhiteNoise"), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index 8263ef72584..6ee0c4b973b 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -62,6 +62,11 @@ struct float3 { return {a.x + b.x, a.y + b.y, a.z + b.z}; } + friend float3 operator+(const float3 &a, const float &b) + { + return {a.x + b, a.y + b, a.z + b}; + } + float3 &operator+=(const float3 &b) { this->x += b.x; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc index 79fec5f6e0e..25e65e3d3f0 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc @@ -19,6 +19,8 @@ #include "../node_shader_util.h" +#include "BLI_noise.hh" + namespace blender::nodes { static void sh_node_tex_wave_declare(NodeDeclarationBuilder &b) @@ -79,17 +81,154 @@ static int node_shader_gpu_tex_wave(GPUMaterial *mat, GPU_constant(&wave_profile)); } -/* node type definition */ +namespace blender::nodes { + +class WaveFunction : public fn::MultiFunction { + private: + int wave_type_; + int bands_direction_; + int rings_direction_; + int wave_profile_; + + public: + WaveFunction(int wave_type, int bands_direction, int rings_direction, int wave_profile) + : wave_type_(wave_type), + bands_direction_(bands_direction), + rings_direction_(rings_direction), + wave_profile_(wave_profile) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"MagicFunction"}; + signature.single_input("Vector"); + signature.single_input("Scale"); + signature.single_input("Distortion"); + signature.single_input("Detail"); + signature.single_input("Detail Scale"); + signature.single_input("Detail Roughness"); + signature.single_input("Phase Offset"); + signature.single_output("Color"); + signature.single_output("Fac"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &scale = params.readonly_single_input(1, "Scale"); + const VArray &distortion = params.readonly_single_input(2, "Distortion"); + const VArray &detail = params.readonly_single_input(3, "Detail"); + const VArray &dscale = params.readonly_single_input(4, "Detail Scale"); + const VArray &droughness = params.readonly_single_input(5, "Detail Roughness"); + const VArray &phase = params.readonly_single_input(6, "Phase Offset"); + + MutableSpan r_color = + params.uninitialized_single_output_if_required(7, "Color"); + MutableSpan r_fac = params.uninitialized_single_output(8, "Fac"); + + for (int64_t i : mask) { + + float3 p = vector[i] * scale[i]; + /* Prevent precision issues on unit coordinates. */ + p = (p + 0.000001f) * 0.999999f; + + float n = 0.0f; + float val = 0.0f; + + switch (wave_type_) { + case SHD_WAVE_BANDS: + switch (bands_direction_) { + case SHD_WAVE_BANDS_DIRECTION_X: + n = p.x * 20.0f; + break; + case SHD_WAVE_BANDS_DIRECTION_Y: + n = p.y * 20.0f; + break; + case SHD_WAVE_BANDS_DIRECTION_Z: + n = p.z * 20.0f; + break; + case SHD_WAVE_BANDS_DIRECTION_DIAGONAL: + n = (p.x + p.y + p.z) * 10.0f; + break; + } + break; + case SHD_WAVE_RINGS: + float3 rp = p; + switch (rings_direction_) { + case SHD_WAVE_RINGS_DIRECTION_X: + rp *= float3(0.0f, 1.0f, 1.0f); + break; + case SHD_WAVE_RINGS_DIRECTION_Y: + rp *= float3(1.0f, 0.0f, 1.0f); + break; + case SHD_WAVE_RINGS_DIRECTION_Z: + rp *= float3(1.0f, 1.0f, 0.0f); + break; + case SHD_WAVE_RINGS_DIRECTION_SPHERICAL: + /* Ignore. */ + break; + } + n = len_v3(rp) * 20.0f; + break; + } + + n += phase[i]; + + if (distortion[i] != 0.0f) { + n += distortion[i] * + (noise::perlin_fractal(p * dscale[i], detail[i], droughness[i]) * 2.0f - 1.0f); + } + + switch (wave_profile_) { + case SHD_WAVE_PROFILE_SIN: + val = 0.5f + 0.5f * sinf(n - M_PI_2); + break; + case SHD_WAVE_PROFILE_SAW: + n /= M_PI * 2.0f; + val = n - floorf(n); + break; + case SHD_WAVE_PROFILE_TRI: + n /= M_PI * 2.0f; + val = fabsf(n - floorf(n + 0.5f)) * 2.0f; + break; + } + + r_fac[i] = val; + } + if (!r_color.is_empty()) { + for (int64_t i : mask) { + r_color[i] = ColorGeometry4f(r_fac[i], r_fac[i], r_fac[i], 1.0f); + } + } + } +}; + +static void sh_node_wave_tex_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexWave *tex = (NodeTexWave *)node.storage; + builder.construct_and_set_matching_fn( + tex->wave_type, tex->bands_direction, tex->rings_direction, tex->wave_profile); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_wave(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_WAVE, "Wave Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_WAVE, "Wave Texture", NODE_CLASS_TEXTURE, 0); ntype.declare = blender::nodes::sh_node_tex_wave_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, node_shader_init_tex_wave); node_type_storage(&ntype, "NodeTexWave", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_wave); + ntype.build_multi_function = blender::nodes::sh_node_wave_tex_build_multi_function; nodeRegisterType(&ntype); } From fccc530003be62bcfafc15ed73aa01bbf443af52 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 19 Oct 2021 17:00:23 +0200 Subject: [PATCH 0951/1500] Fix asset catalog operators disabled without experimental feature flag If the Extended Asset Browser experimental feature was disabled, the asset catalog operators wouldn't work. This wasn't intentional, catalogs aren't considered experimental. --- source/blender/editors/asset/intern/asset_ops.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 834874abc19..da699768970 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -49,7 +49,7 @@ static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C); static PointerRNAVec asset_operation_get_nonexperimental_ids_from_context(const bContext *C); static bool asset_type_is_nonexperimental(const ID_Type id_type); -static bool asset_operation_poll(bContext * /*C*/) +static bool asset_operation_experimental_feature_poll(bContext * /*C*/) { /* At this moment only the pose library is non-experimental. Still, directly marking arbitrary * Actions as asset is not part of the stable functionality; instead, the pose library "Create @@ -61,7 +61,7 @@ static bool asset_operation_poll(bContext * /*C*/) static bool asset_clear_poll(bContext *C) { - if (asset_operation_poll(C)) { + if (asset_operation_experimental_feature_poll(C)) { return true; } @@ -212,7 +212,7 @@ static void ASSET_OT_mark(wmOperatorType *ot) ot->idname = "ASSET_OT_mark"; ot->exec = asset_mark_exec; - ot->poll = asset_operation_poll; + ot->poll = asset_operation_experimental_feature_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } @@ -386,7 +386,7 @@ static void ASSET_OT_list_refresh(struct wmOperatorType *ot) static bool asset_catalog_operator_poll(bContext *C) { const SpaceFile *sfile = CTX_wm_space_file(C); - return asset_operation_poll(C) && sfile && ED_fileselect_active_asset_library_get(sfile); + return sfile && ED_fileselect_active_asset_library_get(sfile); } static int asset_catalog_new_exec(bContext *C, wmOperator *op) @@ -463,7 +463,7 @@ static void ASSET_OT_catalog_delete(struct wmOperatorType *ot) static bke::AssetCatalogService *get_catalog_service(bContext *C) { const SpaceFile *sfile = CTX_wm_space_file(C); - if (!asset_operation_poll(C) || !sfile) { + if (!sfile) { return nullptr; } From 85c8dd6c968334fc8c58a1f4ba1b145996510120 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Oct 2021 14:04:03 -0500 Subject: [PATCH 0952/1500] Fix: Display color sockets without labels correctly Don't build a manual property split layout when the label for the socket is hidden. --- source/blender/editors/space_node/drawnode.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index f7231df85a0..afe36922b09 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3577,9 +3577,14 @@ static void std_node_socket_draw( } break; case SOCK_RGBA: { - uiLayout *row = uiLayoutSplit(layout, 0.4f, false); - uiItemL(row, text, 0); - uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); + if (text[0] == '\0') { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, "", 0); + } + else { + uiLayout *row = uiLayoutSplit(layout, 0.4f, false); + uiItemL(row, text, 0); + uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); + } break; } case SOCK_STRING: { From a83b405a4524dc5a0014737ec25e31b12cd7216c Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Tue, 19 Oct 2021 15:27:47 -0500 Subject: [PATCH 0953/1500] Geometry Nodes: Replace String node This commit adds a node that can be used to find and replace strings inside of the input string. One initial use case is to have an easier way to add line breaks to strings to the string to curves node. Differential Revision: https://developer.blender.org/D12721 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_function.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../function/nodes/node_fn_replace_string.cc | 67 +++++++++++++++++++ 7 files changed, 73 insertions(+) create mode 100644 source/blender/nodes/function/nodes/node_fn_replace_string.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 215bd65cd1a..a9112727daa 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -720,6 +720,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeStringJoin"), NodeItem("FunctionNodeInputSpecialCharacters"), NodeItem("GeometryNodeStringToCurves"), + NodeItem("FunctionNodeReplaceString"), ]), GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ NodeItem("ShaderNodeTexGradient"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 07ad317dd30..2d3b24630b1 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1566,6 +1566,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_ROTATE_EULER 1215 #define FN_NODE_ALIGN_EULER_TO_VECTOR 1216 #define FN_NODE_INPUT_COLOR 1217 +#define FN_NODE_REPLACE_STRING 1218 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index ce8f941b0b6..54a094e40ef 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5879,6 +5879,7 @@ static void registerFunctionNodes() register_node_type_fn_input_vector(); register_node_type_fn_input_color(); register_node_type_fn_random_value(); + register_node_type_fn_replace_string(); register_node_type_fn_rotate_euler(); register_node_type_fn_string_length(); register_node_type_fn_string_substring(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 4d006342e72..879d24fdf53 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -145,6 +145,7 @@ set(SRC function/nodes/node_fn_input_vector.cc function/nodes/node_fn_input_color.cc function/nodes/node_fn_random_value.cc + function/nodes/node_fn_replace_string.cc function/nodes/node_fn_rotate_euler.cc function/nodes/node_fn_string_length.cc function/nodes/node_fn_string_substring.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 999162b1803..e3e75b6fa7a 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -31,6 +31,7 @@ void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); void register_node_type_fn_input_color(void); void register_node_type_fn_random_value(void); +void register_node_type_fn_replace_string(void); void register_node_type_fn_rotate_euler(void); void register_node_type_fn_string_length(void); void register_node_type_fn_string_substring(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index c20a9545968..78a3735c5b8 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -274,6 +274,7 @@ DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARAC DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "") +DefNode(FunctionNode, FN_NODE_REPLACE_STRING, 0, "REPLACE_STRING", ReplaceString, "Replace String", "") DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "") DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") DefNode(FunctionNode, FN_NODE_STRING_SUBSTRING, 0, "STRING_SUBSTRING", StringSubstring, "String Substring", "") diff --git a/source/blender/nodes/function/nodes/node_fn_replace_string.cc b/source/blender/nodes/function/nodes/node_fn_replace_string.cc new file mode 100644 index 00000000000..d21044d54a5 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_replace_string.cc @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_string_utf8.h" + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_replace_string_declare(NodeDeclarationBuilder &b) +{ + b.add_input("String"); + b.add_input("Find").description("The string to find in the input string"); + b.add_input("Replace").description("The string to replace each match with"); + b.add_output("String"); +}; + +} // namespace blender::nodes + +static std::string replace_all(std::string str, const std::string &from, const std::string &to) +{ + if (from.length() <= 0) { + return str; + } + const size_t step = to.length() > 0 ? to.length() : 1; + + size_t offset = 0; + while ((offset = str.find(from, offset)) != std::string::npos) { + str.replace(offset, from.length(), to); + offset += step; + } + return str; +} + +static void fn_node_replace_string_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + static blender::fn::CustomMF_SI_SI_SI_SO + substring_fn{"Replace", + [](const std::string &str, + const std::string &find, + const std::string &replace) { return replace_all(str, find, replace); }}; + builder.set_matching_fn(&substring_fn); +} + +void register_node_type_fn_replace_string() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_REPLACE_STRING, "Replace String", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_replace_string_declare; + ntype.build_multi_function = fn_node_replace_string_build_multi_function; + nodeRegisterType(&ntype); +} From d7b4350749ce3906fcca9803b863fb029b3f541a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Oct 2021 16:27:37 -0500 Subject: [PATCH 0954/1500] Fix T92354: Missing raycast node in geometry nodes add menu Was removed by mistake in rB0a6cf3ed0c64 --- source/blender/blenkernel/intern/node.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 54a094e40ef..0dffa63d044 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5802,8 +5802,8 @@ static void registerGeometryNodes() register_node_type_geo_input_curve_handles(); register_node_type_geo_input_curve_tilt(); register_node_type_geo_input_index(); - register_node_type_geo_input_material(); register_node_type_geo_input_material_index(); + register_node_type_geo_input_material(); register_node_type_geo_input_normal(); register_node_type_geo_input_position(); register_node_type_geo_input_radius(); @@ -5815,6 +5815,7 @@ static void registerGeometryNodes() register_node_type_geo_instance_on_points(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); + register_node_type_geo_material_replace(); register_node_type_geo_material_selection(); register_node_type_geo_mesh_primitive_circle(); register_node_type_geo_mesh_primitive_cone(); @@ -5847,8 +5848,8 @@ static void registerGeometryNodes() register_node_type_geo_set_curve_handles(); register_node_type_geo_set_curve_radius(); register_node_type_geo_set_curve_tilt(); - register_node_type_geo_set_material(); register_node_type_geo_set_material_index(); + register_node_type_geo_set_material(); register_node_type_geo_set_point_radius(); register_node_type_geo_set_position(); register_node_type_geo_set_shade_smooth(); From d73f6647906d4562a191b78faa578a619090aea9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 20 Oct 2021 09:13:38 +1100 Subject: [PATCH 0955/1500] Cleanup: trailing space, use single quotes for enums --- release/scripts/modules/rna_prop_ui.py | 2 +- release/scripts/startup/bl_ui/properties_animviz.py | 6 +++--- release/scripts/startup/bl_ui/properties_data_bone.py | 4 ++-- release/scripts/startup/bl_ui/properties_freestyle.py | 2 +- release/scripts/startup/bl_ui/space_node.py | 2 +- release/scripts/startup/bl_ui/space_sequencer.py | 10 +++++----- release/scripts/startup/bl_ui/space_view3d.py | 2 +- source/blender/draw/intern/draw_manager_text.h | 2 +- source/blender/nodes/CMakeLists.txt | 4 ++-- tests/python/bl_pyapi_idprop_datablock.py | 4 ++-- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 7da7ccdeddd..2cc806be10d 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -224,7 +224,7 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): if use_edit: row = split.row(align=True) - # Do not allow editing of overridden properties (we cannot use a poll function + # Do not allow editing of overridden properties (we cannot use a poll function # of the operators here since they's have no access to the specific property). row.enabled = not(is_lib_override and key in rna_item.id_data.override_library.reference) if is_rna: diff --git a/release/scripts/startup/bl_ui/properties_animviz.py b/release/scripts/startup/bl_ui/properties_animviz.py index 92e78cd32b6..44965a60489 100644 --- a/release/scripts/startup/bl_ui/properties_animviz.py +++ b/release/scripts/startup/bl_ui/properties_animviz.py @@ -70,14 +70,14 @@ class MotionPathButtonsPanel: col = layout.column(align=True) - row = col.row(align=True) + row = col.row(align=True) if bones: row.operator("pose.paths_update", text="Update Paths", icon='BONE_DATA') row.operator("pose.paths_clear", text="", icon='X') else: row.operator("object.paths_update", text="Update Paths", icon='OBJECT_DATA') row.operator("object.paths_clear", text="", icon='X') - col.operator("object.paths_update_visible", text="Update All Paths", icon="WORLD") + col.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD') else: col = layout.column(align=True) col.label(text="Nothing to show yet...", icon='ERROR') @@ -86,7 +86,7 @@ class MotionPathButtonsPanel: col.operator("pose.paths_calculate", text="Calculate...", icon='BONE_DATA') else: col.operator("object.paths_calculate", text="Calculate...", icon='OBJECT_DATA') - col.operator("object.paths_update_visible", text="Update All Paths", icon="WORLD") + col.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD') class MotionPathButtonsPanel_display: diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py index daf64642f68..1a00150d6d2 100644 --- a/release/scripts/startup/bl_ui/properties_data_bone.py +++ b/release/scripts/startup/bl_ui/properties_data_bone.py @@ -196,7 +196,7 @@ class BONE_PT_curved(BoneButtonsPanel, Panel): row2.prop(bone, "bbone_handle_use_scale_start", index=1, text="Y", toggle=True) row2.prop(bone, "bbone_handle_use_scale_start", index=2, text="Z", toggle=True) split2.prop(bone, "bbone_handle_use_ease_start", text="Ease", toggle=True) - row.label(icon="BLANK1") + row.label(icon='BLANK1') col = topcol.column(align=True) col.prop(bone, "bbone_handle_type_end", text="End Handle") @@ -216,7 +216,7 @@ class BONE_PT_curved(BoneButtonsPanel, Panel): row2.prop(bone, "bbone_handle_use_scale_end", index=1, text="Y", toggle=True) row2.prop(bone, "bbone_handle_use_scale_end", index=2, text="Z", toggle=True) split2.prop(bone, "bbone_handle_use_ease_end", text="Ease", toggle=True) - row.label(icon="BLANK1") + row.label(icon='BLANK1') class BONE_PT_relations(BoneButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_freestyle.py b/release/scripts/startup/bl_ui/properties_freestyle.py index 3c765c10154..4519390f953 100644 --- a/release/scripts/startup/bl_ui/properties_freestyle.py +++ b/release/scripts/startup/bl_ui/properties_freestyle.py @@ -280,7 +280,7 @@ class VIEWLAYER_PT_freestyle_lineset(ViewLayerFreestyleEditorButtonsPanel, Panel col.separator() - col.menu("RENDER_MT_lineset_context_menu", icon="DOWNARROW_HLT", text="") + col.menu("RENDER_MT_lineset_context_menu", icon='DOWNARROW_HLT', text="") if is_sortable: col.separator() diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index f806fc345d1..8e2533edac6 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -757,7 +757,7 @@ class NodeTreeInterfacePanel: field_socket_prefixes = { "NodeSocketInt", "NodeSocketColor", "NodeSocketVector", "NodeSocketBool", "NodeSocketFloat"} is_field_type = any(active_socket.bl_socket_idname.startswith(prefix) for prefix in field_socket_prefixes) - if in_out == "OUT" and is_field_type: + if in_out == 'OUT' and is_field_type: layout.prop(active_socket, "attribute_domain") active_socket.draw(context, layout) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 01c33db2ddc..53e40257d9f 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1140,7 +1140,7 @@ class SEQUENCER_PT_color_tag_picker(SequencerColorTagPicker, Panel): layout = self.layout row = layout.row(align=True) - row.operator("sequencer.strip_color_tag_set", icon="X").color = 'NONE' + row.operator("sequencer.strip_color_tag_set", icon='X').color = 'NONE' for i in range(1, 10): icon = 'SEQUENCE_COLOR_%02d' % i row.operator("sequencer.strip_color_tag_set", icon=icon).color = 'COLOR_%02d' % i @@ -1313,15 +1313,15 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): elif strip_type == 'SPEED': col = layout.column(align=True) col.prop(strip, "speed_control", text="Speed Control") - if strip.speed_control == "MULTIPLY": + if strip.speed_control == 'MULTIPLY': col.prop(strip, "speed_factor", text=" ") - elif strip.speed_control == "LENGTH": + elif strip.speed_control == 'LENGTH': col.prop(strip, "speed_length", text=" ") - elif strip.speed_control == "FRAME_NUMBER": + elif strip.speed_control == 'FRAME_NUMBER': col.prop(strip, "speed_frame_number", text=" ") row = layout.row(align=True) - row.enabled = strip.speed_control != "STRETCH" + row.enabled = strip.speed_control != 'STRETCH' row = layout.row(align=True, heading="Interpolation") row.prop(strip, "use_frame_interpolate", text="") diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 1808287f7a9..1c2190bb7a0 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -3602,7 +3602,7 @@ class VIEW3D_MT_pose_context_menu(Menu): layout.operator("pose.paths_calculate", text="Calculate Motion Paths") layout.operator("pose.paths_clear", text="Clear Motion Paths") layout.operator("pose.paths_update", text="Update Armature Motion Paths") - layout.operator("object.paths_update_visible", text="Update All Motion Paths") + layout.operator("object.paths_update_visible", text="Update All Motion Paths") layout.separator() diff --git a/source/blender/draw/intern/draw_manager_text.h b/source/blender/draw/intern/draw_manager_text.h index 4f3a6153775..883b37c52af 100644 --- a/source/blender/draw/intern/draw_manager_text.h +++ b/source/blender/draw/intern/draw_manager_text.h @@ -64,4 +64,4 @@ struct DRWTextStore *DRW_text_cache_ensure(void); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 879d24fdf53..9cf04ac3aab 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -224,7 +224,7 @@ set(SRC geometry/nodes/node_geo_distribute_points_on_faces.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_curve_handles.cc - geometry/nodes/node_geo_input_curve_tilt.cc + geometry/nodes/node_geo_input_curve_tilt.cc geometry/nodes/node_geo_input_index.cc geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_material_index.cc @@ -264,7 +264,7 @@ set(SRC geometry/nodes/node_geo_separate_geometry.cc geometry/nodes/node_geo_set_curve_handles.cc geometry/nodes/node_geo_set_curve_radius.cc - geometry/nodes/node_geo_set_curve_tilt.cc + geometry/nodes/node_geo_set_curve_tilt.cc geometry/nodes/node_geo_set_material.cc geometry/nodes/node_geo_set_material_index.cc geometry/nodes/node_geo_set_point_radius.cc diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py index d4253dc4665..5a7e4bee4fc 100644 --- a/tests/python/bl_pyapi_idprop_datablock.py +++ b/tests/python/bl_pyapi_idprop_datablock.py @@ -257,8 +257,8 @@ def test_restrictions1(): # just panel for testing the poll callback with lots of objects class TEST_PT_DatablockProp(bpy.types.Panel): bl_label = "Datablock IDProp" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' bl_context = "render" def draw(self, context): From 967fec68837d3496b994094e5f24775ebcf6f6ce Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 20 Oct 2021 09:17:46 +1100 Subject: [PATCH 0956/1500] Cleanup: sort cmake file lists --- source/blender/blenkernel/CMakeLists.txt | 4 ++-- source/blender/draw/CMakeLists.txt | 10 +++++----- source/blender/functions/CMakeLists.txt | 4 ++-- source/blender/nodes/CMakeLists.txt | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index a86c6fbd3dd..ef95d747948 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -472,8 +472,8 @@ set(SRC intern/CCGSubSurf.h intern/CCGSubSurf_inline.h intern/CCGSubSurf_intern.h - intern/attribute_access_intern.hh intern/asset_library_service.hh + intern/attribute_access_intern.hh intern/data_transfer_intern.h intern/lib_intern.h intern/multires_inline.h @@ -801,8 +801,8 @@ if(WITH_GTESTS) set(TEST_SRC intern/action_test.cc intern/armature_test.cc - intern/asset_catalog_test.cc intern/asset_catalog_path_test.cc + intern/asset_catalog_test.cc intern/asset_library_service_test.cc intern/asset_library_test.cc intern/asset_test.cc diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 62dcf438471..baf83234354 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -94,7 +94,6 @@ set(SRC intern/draw_color_management.cc intern/draw_common.c intern/draw_debug.c - intern/draw_view_data.cc intern/draw_fluid.c intern/draw_hair.c intern/draw_instance_data.c @@ -106,10 +105,11 @@ set(SRC intern/draw_manager_text.c intern/draw_manager_texture.c intern/draw_select_buffer.c - intern/draw_texture_pool.cc intern/draw_shader.c - intern/smaa_textures.c + intern/draw_texture_pool.cc intern/draw_view.c + intern/draw_view_data.cc + intern/smaa_textures.c engines/basic/basic_engine.c engines/image/image_engine.c engines/image/image_shader.c @@ -200,16 +200,16 @@ set(SRC intern/draw_color_management.h intern/draw_common.h intern/draw_debug.h - intern/draw_view_data.h intern/draw_hair_private.h intern/draw_instance_data.h intern/draw_manager.h intern/draw_manager_profiling.h intern/draw_manager_testing.h intern/draw_manager_text.h - intern/draw_texture_pool.h intern/draw_shader.h + intern/draw_texture_pool.h intern/draw_view.h + intern/draw_view_data.h intern/mesh_extractors/extract_mesh.h intern/smaa_textures.h engines/basic/basic_engine.h diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 844e21f5e2c..74625fe2826 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -41,9 +41,9 @@ set(SRC FN_cpp_type.hh FN_cpp_type_make.hh - FN_generic_array.hh FN_field.hh FN_field_cpp_type.hh + FN_generic_array.hh FN_generic_pointer.hh FN_generic_span.hh FN_generic_value_map.hh @@ -88,8 +88,8 @@ blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC tests/FN_cpp_type_test.cc - tests/FN_generic_array_test.cc tests/FN_field_test.cc + tests/FN_generic_array_test.cc tests/FN_generic_span_test.cc tests/FN_generic_vector_array_test.cc tests/FN_multi_function_procedure_test.cc diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 9cf04ac3aab..39265c13a1f 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -140,10 +140,10 @@ set(SRC function/nodes/node_fn_boolean_math.cc function/nodes/node_fn_float_compare.cc function/nodes/node_fn_float_to_int.cc + function/nodes/node_fn_input_color.cc function/nodes/node_fn_input_special_characters.cc function/nodes/node_fn_input_string.cc function/nodes/node_fn_input_vector.cc - function/nodes/node_fn_input_color.cc function/nodes/node_fn_random_value.cc function/nodes/node_fn_replace_string.cc function/nodes/node_fn_rotate_euler.cc @@ -274,6 +274,7 @@ set(SRC geometry/nodes/node_geo_set_spline_resolution.cc geometry/nodes/node_geo_string_join.cc geometry/nodes/node_geo_string_to_curves.cc + geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc geometry/nodes/node_geo_transfer_attribute.cc geometry/nodes/node_geo_transform.cc @@ -281,7 +282,6 @@ set(SRC geometry/nodes/node_geo_triangulate.cc geometry/nodes/node_geo_viewer.cc geometry/nodes/node_geo_volume_to_mesh.cc - geometry/nodes/node_geo_subdivision_surface.cc geometry/node_geometry_exec.cc geometry/node_geometry_tree.cc From 93197c4660daaaa40527a701570f55ad76a99eb3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 20 Oct 2021 09:19:21 +1100 Subject: [PATCH 0957/1500] Cleanup: spelling in comments --- source/blender/blenlib/BLI_kdtree.h | 2 +- source/blender/blenlib/BLI_kdtree_impl.h | 2 +- source/blender/blenloader/BLO_readfile.h | 2 +- source/blender/bmesh/tools/bmesh_intersect.c | 2 +- source/blender/editors/transform/transform_convert.c | 2 +- source/blender/gpu/GPU_index_buffer.h | 2 +- source/blender/makesdna/DNA_node_types.h | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/blenlib/BLI_kdtree.h b/source/blender/blenlib/BLI_kdtree.h index 76f39dfbacb..bb61ba17d99 100644 --- a/source/blender/blenlib/BLI_kdtree.h +++ b/source/blender/blenlib/BLI_kdtree.h @@ -22,7 +22,7 @@ extern "C" { /** \file * \ingroup bli - * \brief A kd-tree for nearest neighbor search. + * \brief A KD-tree for nearest neighbor search. */ /* 1D version */ diff --git a/source/blender/blenlib/BLI_kdtree_impl.h b/source/blender/blenlib/BLI_kdtree_impl.h index 4b2a37830ae..26a22fc2ac4 100644 --- a/source/blender/blenlib/BLI_kdtree_impl.h +++ b/source/blender/blenlib/BLI_kdtree_impl.h @@ -16,7 +16,7 @@ /** \file * \ingroup bli - * \brief A kd-tree for nearest neighbor search. + * \brief A KD-tree for nearest neighbor search. */ #include "BLI_compiler_attrs.h" diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 541483d4eed..d2631840f74 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -220,7 +220,7 @@ typedef enum eBLOLibLinkFlags { BLO_LIBLINK_NEEDS_ID_TAG_DOIT = 1 << 18, /** Set fake user on appended IDs. */ BLO_LIBLINK_APPEND_SET_FAKEUSER = 1 << 19, - /** Append (make local) also indirect dependencies of appended IDs comming from other libraries. + /** Append (make local) also indirect dependencies of appended IDs coming from other libraries. * NOTE: All IDs (including indirectly linked ones) coming from the same initial library are * always made local. */ BLO_LIBLINK_APPEND_RECURSIVE = 1 << 20, diff --git a/source/blender/bmesh/tools/bmesh_intersect.c b/source/blender/bmesh/tools/bmesh_intersect.c index d51661a08bb..947442c7bd8 100644 --- a/source/blender/bmesh/tools/bmesh_intersect.c +++ b/source/blender/bmesh/tools/bmesh_intersect.c @@ -901,7 +901,7 @@ static int isect_bvhtree_point_v3(BVHTree *tree, const float **looptris, const f const float dir[3] = {1.0f, 0.0f, 0.0f}; /* Need to initialize hit even tho it's not used. - * This is to make it so kd-tree believes we didn't intersect anything and + * This is to make it so KD-tree believes we didn't intersect anything and * keeps calling the intersect callback. */ hit.index = -1; diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 00be756878b..781bf221dd2 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -228,7 +228,7 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) * Used to find #TransData from the index returned by #BLI_kdtree_find_nearest. */ TransData **td_table = MEM_mallocN(sizeof(*td_table) * td_table_len, __func__); - /* Create and fill kd-tree of selected's positions - in global or proj_vec space. */ + /* Create and fill KD-tree of selected's positions - in global or proj_vec space. */ KDTree_3d *td_tree = BLI_kdtree_3d_new(td_table_len); int td_table_index = 0; diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h index 197077cf76c..e4f1709173e 100644 --- a/source/blender/gpu/GPU_index_buffer.h +++ b/source/blender/gpu/GPU_index_buffer.h @@ -56,7 +56,7 @@ GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len); /* * Thread safe. * - * Function inspired by the reduction directives of multithread work APIs.. + * Function inspired by the reduction directives of multi-thread work API's. */ void GPU_indexbuf_join(GPUIndexBufBuilder *builder, const GPUIndexBufBuilder *builder_from); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index e6d02923dfe..12b81433ffd 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1529,7 +1529,7 @@ typedef struct NodeGeometryRaycast { /* CustomDataType. */ int8_t data_type; - /* Deprecated input types in new Raycast node. Can be removed when legacy nodes are no longer + /* Deprecated input types in new Ray-cast node. Can be removed when legacy nodes are no longer * supported. */ uint8_t input_type_ray_direction; uint8_t input_type_ray_length; From bca2701236ab60dce5e32a8731f3bba43783b5b2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 20 Oct 2021 09:59:44 +1100 Subject: [PATCH 0958/1500] GNUmakefile: clarify that order isn't important for multiple targets Also include example. --- GNUmakefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GNUmakefile b/GNUmakefile index d620e5c4363..57892959d2a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -27,7 +27,7 @@ define HELP_TEXT Blender Convenience Targets - Provided for building Blender, (multiple at once can be used). + Provided for building Blender (multiple targets can be used at once). * debug: Build a debug binary. * full: Enable all supported dependencies & options. @@ -40,6 +40,8 @@ Blender Convenience Targets * ninja: Use ninja build tool for faster builds. * ccache: Use ccache for faster rebuilds. + Note: when passing in multiple targets their order is not important. + So for a fast build you can for e.g. run 'make lite ccache ninja'. Note: passing the argument 'BUILD_DIR=path' when calling make will override the default build dir. Note: passing the argument 'BUILD_CMAKE_ARGS=args' lets you add cmake arguments. From ef9269bd62f31e39d39cc59cd34354b53eae6661 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 20 Oct 2021 10:16:36 +1100 Subject: [PATCH 0959/1500] Thumbnails: refactor extraction to use one code-path for all platforms Thumbnail extraction now shares code between Linux/Windows, allowing thumbnails from Zstd compressed blend files to be extracted. The main logic is placed in blendthumb_extract.cc and is built as static library. For windows there is DLL which is registered during blender install and which then reads and generates thumbnails. For other platforms there is blender-thumbnailer executable file which takes blend file as an input and generates PNG file. As a result Python script blender-thumbnailer.py is no longer needed. The thumbnail extractor shares the same code-path as Blenders file reading, so there is no need to duplicate any file reading logic. This means reading compressed blend files is supported (broken since the recent move Zstd compression - D5799). This resolves T63736. Contributors: - @alausic original patch. - @LazyDodo windows fixes/support. - @campbellbarton general fixes/update. - @lukasstockner97 Zstd support. Reviewed By: sybren, mont29, LazyDodo, campbellbarton Ref D6408 --- release/bin/blender-thumbnailer.py | 193 ----------- source/blender/CMakeLists.txt | 5 +- source/blender/blendthumb/CMakeLists.txt | 64 +++- .../blender/blendthumb/src/BlenderThumb.cpp | 321 ------------------ .../blendthumb/src/blender_thumbnailer.cc | 113 ++++++ source/blender/blendthumb/src/blendthumb.hh | 65 ++++ .../blendthumb/src/blendthumb_extract.cc | 257 ++++++++++++++ .../blender/blendthumb/src/blendthumb_png.cc | 158 +++++++++ .../blendthumb/src/blendthumb_win32.cc | 237 +++++++++++++ .../{BlendThumb.def => blendthumb_win32.def} | 0 .../{BlendThumb.rc => blendthumb_win32.rc} | 0 .../src/{Dll.cpp => blendthumb_win32_dll.cc} | 0 source/creator/CMakeLists.txt | 5 +- 13 files changed, 883 insertions(+), 535 deletions(-) delete mode 100755 release/bin/blender-thumbnailer.py delete mode 100644 source/blender/blendthumb/src/BlenderThumb.cpp create mode 100644 source/blender/blendthumb/src/blender_thumbnailer.cc create mode 100644 source/blender/blendthumb/src/blendthumb.hh create mode 100644 source/blender/blendthumb/src/blendthumb_extract.cc create mode 100644 source/blender/blendthumb/src/blendthumb_png.cc create mode 100644 source/blender/blendthumb/src/blendthumb_win32.cc rename source/blender/blendthumb/src/{BlendThumb.def => blendthumb_win32.def} (100%) rename source/blender/blendthumb/src/{BlendThumb.rc => blendthumb_win32.rc} (100%) rename source/blender/blendthumb/src/{Dll.cpp => blendthumb_win32_dll.cc} (100%) diff --git a/release/bin/blender-thumbnailer.py b/release/bin/blender-thumbnailer.py deleted file mode 100755 index e050a681ca0..00000000000 --- a/release/bin/blender-thumbnailer.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -""" -Thumbnailer runs with python 2.7 and 3.x. -To run automatically with a file manager such as Nautilus, save this file -in a directory that is listed in PATH environment variable, and create -blender.thumbnailer file in ${HOME}/.local/share/thumbnailers/ directory -with the following contents: - -[Thumbnailer Entry] -TryExec=blender-thumbnailer.py -Exec=blender-thumbnailer.py %u %o -MimeType=application/x-blender; -""" - -import struct - - -def open_wrapper_get(): - """ wrap OS specific read functionality here, fallback to 'open()' - """ - - class GFileWrapper: - __slots__ = ("mode", "g_file") - - def __init__(self, url, mode='r'): - self.mode = mode # used in gzip module - self.g_file = Gio.File.parse_name(url).read(None) - - def read(self, size): - return self.g_file.read_bytes(size, None).get_data() - - def seek(self, offset, whence=0): - self.g_file.seek(offset, [1, 0, 2][whence], None) - return self.g_file.tell() - - def tell(self): - return self.g_file.tell() - - def close(self): - self.g_file.close(None) - - def open_local_url(url, mode='r'): - o = urlparse(url) - if o.scheme == '': - path = o.path - elif o.scheme == 'file': - path = unquote(o.path) - else: - raise(IOError('URL scheme "%s" needs gi.repository.Gio module' % o.scheme)) - return open(path, mode) - - try: - from gi.repository import Gio - return GFileWrapper - except ImportError: - try: - # Python 3 - from urllib.parse import urlparse, unquote - except ImportError: - # Python 2 - from urlparse import urlparse - from urllib import unquote - return open_local_url - - -def blend_extract_thumb(path): - import os - open_wrapper = open_wrapper_get() - - REND = b'REND' - TEST = b'TEST' - - blendfile = open_wrapper(path, 'rb') - - head = blendfile.read(12) - - if head[0:2] == b'\x1f\x8b': # gzip magic - import gzip - blendfile.close() - blendfile = gzip.GzipFile('', 'rb', 0, open_wrapper(path, 'rb')) - head = blendfile.read(12) - - if not head.startswith(b'BLENDER'): - blendfile.close() - return None, 0, 0 - - is_64_bit = (head[7] == b'-'[0]) - - # true for PPC, false for X86 - is_big_endian = (head[8] == b'V'[0]) - - # blender pre 2.5 had no thumbs - if head[9:11] <= b'24': - return None, 0, 0 - - sizeof_bhead = 24 if is_64_bit else 20 - int_endian = '>i' if is_big_endian else ' ") - else: - file_in = sys.argv[-2] - - buf, width, height = blend_extract_thumb(file_in) - - if buf: - file_out = sys.argv[-1] - - f = open(file_out, "wb") - f.write(write_png(buf, width, height)) - f.close() - - -if __name__ == '__main__': - main() diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 84d31bccc53..0a494677d96 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -131,6 +131,7 @@ add_subdirectory(io) add_subdirectory(functions) add_subdirectory(makesdna) add_subdirectory(makesrna) +add_subdirectory(blendthumb) if(WITH_COMPOSITOR) add_subdirectory(compositor) @@ -159,7 +160,3 @@ endif() if(WITH_FREESTYLE) add_subdirectory(freestyle) endif() - -if(WIN32) - add_subdirectory(blendthumb) -endif() diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt index b42ca284ecb..4bcd27082c0 100644 --- a/source/blender/blendthumb/CMakeLists.txt +++ b/source/blender/blendthumb/CMakeLists.txt @@ -19,23 +19,59 @@ # ***** END GPL LICENSE BLOCK ***** #----------------------------------------------------------------------------- -include_directories(${ZLIB_INCLUDE_DIRS}) +# Shared Thumbnail Extraction Logic + +include_directories( + ../blenlib + ../makesdna + ../../../intern/guardedalloc +) + +include_directories( + SYSTEM + ${ZLIB_INCLUDE_DIRS} +) set(SRC - src/BlenderThumb.cpp - src/BlendThumb.def - src/BlendThumb.rc - src/Dll.cpp + src/blendthumb.hh + src/blendthumb_extract.cc + src/blendthumb_png.cc ) -string(APPEND CMAKE_SHARED_LINKER_FLAGS_DEBUG " /nodefaultlib:MSVCRT.lib") +if(WIN32) + # ----------------------------------------------------------------------------- + # Build `BlendThumb.dll` -add_library(BlendThumb SHARED ${SRC}) -setup_platform_linker_flags(BlendThumb) -target_link_libraries(BlendThumb ${ZLIB_LIBRARIES}) + set(SRC_WIN32 + src/blendthumb_win32.cc + src/blendthumb_win32.def + src/blendthumb_win32.rc + src/blendthumb_win32_dll.cc + ) -install( - FILES $ - COMPONENT Blender - DESTINATION "." -) + add_definitions(-DNOMINMAX) + + add_library(BlendThumb SHARED ${SRC} ${SRC_WIN32}) + + target_link_libraries(BlendThumb bf_blenlib dbghelp.lib Version.lib) + set_target_properties(BlendThumb PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:msvcrt") + + install( + FILES $ + COMPONENT Blender + DESTINATION "." + ) +else() + # ----------------------------------------------------------------------------- + # Build `blender-thumbnailer` executable + + add_executable(blender-thumbnailer ${SRC} src/blender_thumbnailer.cc) + target_link_libraries(blender-thumbnailer bf_blenlib) + target_link_libraries(blender-thumbnailer ${PTHREADS_LIBRARIES}) + + install( + FILES $ + COMPONENT Blender + DESTINATION "." + ) +endif() diff --git a/source/blender/blendthumb/src/BlenderThumb.cpp b/source/blender/blendthumb/src/BlenderThumb.cpp deleted file mode 100644 index 939e7bbf67c..00000000000 --- a/source/blender/blendthumb/src/BlenderThumb.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include // For IThumbnailProvider. - -#pragma comment(lib, "shlwapi.lib") - -// this thumbnail provider implements IInitializeWithStream to enable being hosted -// in an isolated process for robustness - -class CBlendThumb : public IInitializeWithStream, public IThumbnailProvider { - public: - CBlendThumb() : _cRef(1), _pStream(NULL) - { - } - - virtual ~CBlendThumb() - { - if (_pStream) { - _pStream->Release(); - } - } - - // IUnknown - IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv) - { - static const QITAB qit[] = { - QITABENT(CBlendThumb, IInitializeWithStream), - QITABENT(CBlendThumb, IThumbnailProvider), - {0}, - }; - return QISearch(this, qit, riid, ppv); - } - - IFACEMETHODIMP_(ULONG) AddRef() - { - return InterlockedIncrement(&_cRef); - } - - IFACEMETHODIMP_(ULONG) Release() - { - ULONG cRef = InterlockedDecrement(&_cRef); - if (!cRef) { - delete this; - } - return cRef; - } - - // IInitializeWithStream - IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode); - - // IThumbnailProvider - IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha); - - private: - long _cRef; - IStream *_pStream; // provided during initialization. -}; - -HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv) -{ - CBlendThumb *pNew = new (std::nothrow) CBlendThumb(); - HRESULT hr = pNew ? S_OK : E_OUTOFMEMORY; - if (SUCCEEDED(hr)) { - hr = pNew->QueryInterface(riid, ppv); - pNew->Release(); - } - return hr; -} - -// IInitializeWithStream -IFACEMETHODIMP CBlendThumb::Initialize(IStream *pStream, DWORD) -{ - HRESULT hr = E_UNEXPECTED; // can only be inited once - if (_pStream == NULL) { - // take a reference to the stream if we have not been inited yet - hr = pStream->QueryInterface(&_pStream); - } - return hr; -} - -#include "Wincodec.h" -#include -#include -const unsigned char gzip_magic[3] = {0x1f, 0x8b, 0x08}; - -// IThumbnailProvider -IFACEMETHODIMP CBlendThumb::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha) -{ - ULONG BytesRead; - HRESULT hr = S_FALSE; - LARGE_INTEGER SeekPos; - - // Compressed? - unsigned char in_magic[3]; - _pStream->Read(&in_magic, 3, &BytesRead); - bool gzipped = true; - for (int i = 0; i < 3; i++) - if (in_magic[i] != gzip_magic[i]) { - gzipped = false; - break; - } - - if (gzipped) { - // Zlib inflate - z_stream stream; - stream.zalloc = Z_NULL; - stream.zfree = Z_NULL; - stream.opaque = Z_NULL; - - // Get compressed file length - SeekPos.QuadPart = 0; - _pStream->Seek(SeekPos, STREAM_SEEK_END, NULL); - - // Get compressed and uncompressed size - uLong source_size; - uLongf dest_size; - // SeekPos.QuadPart = -4; // last 4 bytes define size of uncompressed file - // ULARGE_INTEGER Tell; - //_pStream->Seek(SeekPos,STREAM_SEEK_END,&Tell); - // source_size = (uLong)Tell.QuadPart + 4; // src - //_pStream->Read(&dest_size,4,&BytesRead); // dest - dest_size = 1024 * 70; // thumbnail is currently always inside the first 65KB...if it moves or - // enlargens this line will have to change or go! - source_size = (uLong)max(SeekPos.QuadPart, dest_size); // for safety, assume no compression - - // Input - Bytef *src = new Bytef[source_size]; - stream.next_in = (Bytef *)src; - stream.avail_in = (uInt)source_size; - - // Output - Bytef *dest = new Bytef[dest_size]; - stream.next_out = (Bytef *)dest; - stream.avail_out = dest_size; - - // IStream to src - SeekPos.QuadPart = 0; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - _pStream->Read(src, source_size, &BytesRead); - - // Do the inflation - int err; - err = inflateInit2(&stream, 16); // 16 means "gzip"...nice! - err = inflate(&stream, Z_FINISH); - err = inflateEnd(&stream); - - // Replace the IStream, which is read-only - _pStream->Release(); - _pStream = SHCreateMemStream(dest, dest_size); - - delete[] src; - delete[] dest; - } - - // Blender version, early out if sub 2.5 - SeekPos.QuadPart = 9; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - char version[4]; - version[3] = '\0'; - _pStream->Read(&version, 3, &BytesRead); - if (BytesRead != 3) { - return E_UNEXPECTED; - } - int iVersion = atoi(version); - if (iVersion < 250) { - return S_FALSE; - } - - // 32 or 64 bit blend? - SeekPos.QuadPart = 7; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - - char _PointerSize; - _pStream->Read(&_PointerSize, 1, &BytesRead); - - int PointerSize = _PointerSize == '_' ? 4 : 8; - int HeaderSize = 16 + PointerSize; - - // Find and read thumbnail ("TEST") block - SeekPos.QuadPart = 12; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - int BlockOffset = 12; - while (_pStream) { - // Scan current block - char BlockName[5]; - BlockName[4] = '\0'; - int BlockSize = 0; - - if (_pStream->Read(BlockName, 4, &BytesRead) == S_OK && - _pStream->Read((void *)&BlockSize, 4, &BytesRead) == S_OK) { - if (strcmp(BlockName, "TEST") != 0) { - SeekPos.QuadPart = BlockOffset += HeaderSize + BlockSize; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - continue; - } - } - else { - break; // eof - } - - // Found the block - SeekPos.QuadPart = BlockOffset + HeaderSize; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - - int width, height; - _pStream->Read((char *)&width, 4, &BytesRead); - _pStream->Read((char *)&height, 4, &BytesRead); - BlockSize -= 8; - - // Isolate RGBA data - char *pRGBA = new char[BlockSize]; - _pStream->Read(pRGBA, BlockSize, &BytesRead); - - if (BytesRead != (ULONG)BlockSize) { - return E_UNEXPECTED; - } - - // Convert to BGRA for Windows - for (int i = 0; i < BlockSize; i += 4) { -#define RED_BYTE pRGBA[i] -#define BLUE_BYTE pRGBA[i + 2] - - char red = RED_BYTE; - RED_BYTE = BLUE_BYTE; - BLUE_BYTE = red; - } - - // Flip vertically (Blender stores it upside-down) - unsigned int LineSize = width * 4; - char *FlippedImage = new char[BlockSize]; - for (int i = 0; i < height; i++) { - if (0 != memcpy_s(&FlippedImage[(height - i - 1) * LineSize], - LineSize, - &pRGBA[i * LineSize], - LineSize)) { - return E_UNEXPECTED; - } - } - delete[] pRGBA; - pRGBA = FlippedImage; - - // Create image - *phbmp = CreateBitmap(width, height, 1, 32, pRGBA); - if (!*phbmp) { - return E_FAIL; - } - *pdwAlpha = WTSAT_ARGB; // it's actually BGRA, not sure why this works - - // Scale down if required - if ((unsigned)width > cx || (unsigned)height > cx) { - float scale = 1.0f / (max(width, height) / (float)cx); - LONG NewWidth = (LONG)(width * scale); - LONG NewHeight = (LONG)(height * scale); - -#ifdef _DEBUG -# if 0 - MessageBox(0, "Attach now", "Debugging", MB_OK); -# endif -#endif - IWICImagingFactory *pImgFac; - hr = CoCreateInstance( - CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImgFac)); - - IWICBitmap *WICBmp; - hr = pImgFac->CreateBitmapFromHBITMAP(*phbmp, 0, WICBitmapUseAlpha, &WICBmp); - - BITMAPINFO bmi = {}; - bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - bmi.bmiHeader.biWidth = NewWidth; - bmi.bmiHeader.biHeight = -NewHeight; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - - BYTE *pBits; - HBITMAP ResizedHBmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void **)&pBits, NULL, 0); - hr = ResizedHBmp ? S_OK : E_OUTOFMEMORY; - if (SUCCEEDED(hr)) { - IWICBitmapScaler *pIScaler; - hr = pImgFac->CreateBitmapScaler(&pIScaler); - hr = pIScaler->Initialize(WICBmp, NewWidth, NewHeight, WICBitmapInterpolationModeFant); - - WICRect rect = {0, 0, NewWidth, NewHeight}; - hr = pIScaler->CopyPixels(&rect, NewWidth * 4, NewWidth * NewHeight * 4, pBits); - - if (SUCCEEDED(hr)) { - DeleteObject(*phbmp); - *phbmp = ResizedHBmp; - } - else { - DeleteObject(ResizedHBmp); - } - - pIScaler->Release(); - } - WICBmp->Release(); - pImgFac->Release(); - } - else { - hr = S_OK; - } - break; - } - return hr; -} diff --git a/source/blender/blendthumb/src/blender_thumbnailer.cc b/source/blender/blendthumb/src/blender_thumbnailer.cc new file mode 100644 index 00000000000..8dd9d5c0c0a --- /dev/null +++ b/source/blender/blendthumb/src/blender_thumbnailer.cc @@ -0,0 +1,113 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup blendthumb + * + * This file defines the thumbnail generation command (typically used on UNIX). + * + * To run automatically with a file manager such as Nautilus, save this file + * in a directory that is listed in PATH environment variable, and create + * `blender.thumbnailer` file in `${HOME}/.local/share/thumbnailers/` directory + * with the following contents: + * + * \code{.txt} + * [Thumbnailer Entry] + * TryExec=blender-thumbnailer + * Exec=blender-thumbnailer %u %o + * MimeType=application/x-blender; + * \endcode + */ + +#include +#include + +#include +#ifndef WIN32 +# include /* For read close. */ +#else +# include "BLI_winstuff.h" +# include "winsock2.h" +# include /* For open close read. */ +#endif + +#include "BLI_fileops.h" +#include "BLI_filereader.h" +#include "BLI_vector.hh" + +#include "blendthumb.hh" + +/** + * This function opens .blend file from src_blend, extracts thumbnail from file if there is one, + * and writes `.png` image into `dst_png`. + * Returns exit code (0 if successful). + */ +static eThumbStatus extract_png_from_blend_file(const char *src_blend, const char *dst_png) +{ + eThumbStatus err; + + /* Open source file `src_blend`. */ + const int src_file = BLI_open(src_blend, O_BINARY | O_RDONLY, 0); + if (src_file == -1) { + return BT_FILE_ERR; + } + + /* Thumbnail reading is responsible for freeing `file` and closing `src_file`. */ + FileReader *file = BLI_filereader_new_file(src_file); + if (file == nullptr) { + close(src_file); + return BT_FILE_ERR; + } + + /* Extract thumbnail from file. */ + Thumbnail thumb; + err = blendthumb_create_thumb_from_file(file, &thumb); + if (err != BT_OK) { + return err; + } + + /* Write thumbnail to `dst_png`. */ + const int dst_file = BLI_open(dst_png, O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (dst_file == -1) { + return BT_FILE_ERR; + } + + std::optional> png_buf_opt = blendthumb_create_png_data_from_thumb( + &thumb); + if (png_buf_opt == std::nullopt) { + err = BT_ERROR; + } + else { + blender::Vector png_buf = *png_buf_opt; + err = (write(dst_file, png_buf.data(), png_buf.size()) == png_buf.size()) ? BT_OK : + BT_FILE_ERR; + } + close(dst_file); + + return err; +} + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + std::cerr << "Usage: blender-thumbnailer " << std::endl; + return -1; + } + + eThumbStatus ret = extract_png_from_blend_file(argv[1], argv[2]); + + return (int)ret; +} diff --git a/source/blender/blendthumb/src/blendthumb.hh b/source/blender/blendthumb/src/blendthumb.hh new file mode 100644 index 00000000000..c029a1766d6 --- /dev/null +++ b/source/blender/blendthumb/src/blendthumb.hh @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008-2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup blendthumb + * + * Shared thumbnail extraction logic. + * + * Used for both MS-Windows DLL and Unix command line. + */ + +#pragma once + +#include + +#include "BLI_array.hh" +#include "BLI_vector.hh" + +struct FileReader; + +struct Thumbnail { + blender::Array data; + int width; + int height; +}; + +enum eThumbStatus { + BT_OK = 0, + BT_FILE_ERR = 1, + BT_COMPRES_ERR = 2, + BT_DECOMPRESS_ERR = 3, + BT_INVALID_FILE = 4, + BT_EARLY_VERSION = 5, + BT_INVALID_THUMB = 6, + BT_ERROR = 9 +}; + +std::optional> blendthumb_create_png_data_from_thumb( + const Thumbnail *thumb); +eThumbStatus blendthumb_create_thumb_from_file(struct FileReader *rawfile, Thumbnail *thumb); + +/* INTEGER CODES */ +#ifdef __BIG_ENDIAN__ +/* Big Endian */ +# define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d)) +#else +/* Little Endian */ +# define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a)) +#endif diff --git a/source/blender/blendthumb/src/blendthumb_extract.cc b/source/blender/blendthumb/src/blendthumb_extract.cc new file mode 100644 index 00000000000..99b13c89994 --- /dev/null +++ b/source/blender/blendthumb/src/blendthumb_extract.cc @@ -0,0 +1,257 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup blendthumb + * + * Expose #blendthumb_create_thumb_from_file that creates the PNG data + * but does not write it to a file. + */ + +#include + +#include "BLI_alloca.h" +#include "BLI_endian_defines.h" +#include "BLI_endian_switch.h" +#include "BLI_fileops.h" +#include "BLI_filereader.h" +#include "BLI_string.h" + +#include "blendthumb.hh" + +static bool blend_header_check_magic(const char header[12]) +{ + /* Check magic string at start of file. */ + if (!BLI_str_startswith(header, "BLENDER")) { + return false; + } + /* Check pointer size and endianness indicators. */ + if (((header[7] != '_') && (header[7] != '-')) || ((header[8] != 'v') && (header[8] != 'V'))) { + return false; + } + /* Check version number. */ + if (!isdigit(header[9]) || !isdigit(header[10]) || !isdigit(header[11])) { + return false; + } + return true; +} + +static bool blend_header_is_version_valid(const char header[12]) +{ + /* Thumbnails are only in files with version >= 2.50 */ + char num[4]; + memcpy(num, header + 9, 3); + num[3] = 0; + return atoi(num) >= 250; +} + +static int blend_header_pointer_size(const char header[12]) +{ + return header[7] == '_' ? 4 : 8; +} + +static bool blend_header_is_endian_switch_needed(const char header[12]) +{ + return (((header[8] == 'v') ? L_ENDIAN : B_ENDIAN) != ENDIAN_ORDER); +} + +static void thumb_data_vertical_flip(Thumbnail *thumb) +{ + uint32_t *rect = (uint32_t *)thumb->data.data(); + int x = thumb->width, y = thumb->height; + uint32_t *top = rect; + uint32_t *bottom = top + ((y - 1) * x); + uint32_t *line = (uint32_t *)malloc(x * sizeof(uint32_t)); + + y >>= 1; + for (; y > 0; y--) { + memcpy(line, top, x * sizeof(uint32_t)); + memcpy(top, bottom, x * sizeof(uint32_t)); + memcpy(bottom, line, x * sizeof(uint32_t)); + bottom -= x; + top += x; + } + free(line); +} + +static int32_t bytes_to_native_i32(const uint8_t bytes[4], bool endian_switch) +{ + int32_t data; + memcpy(&data, bytes, 4); + if (endian_switch) { + BLI_endian_switch_int32(&data); + } + return data; +} + +static bool file_read(FileReader *file, uint8_t *buf, size_t buf_len) +{ + return (file->read(file, buf, buf_len) == buf_len); +} + +static bool file_seek(FileReader *file, size_t len) +{ + if (file->seek != nullptr) { + if (file->seek(file, len, SEEK_CUR) == -1) { + return false; + } + return true; + } + + /* File doesn't support seeking (e.g. gzip), so read and discard in chunks. */ + constexpr size_t dummy_data_size = 4096; + blender::Array dummy_data(dummy_data_size); + while (len > 0) { + const size_t len_chunk = std::min(len, dummy_data_size); + if ((size_t)file->read(file, dummy_data.data(), len_chunk) != len_chunk) { + return false; + } + len -= len_chunk; + } + return true; +} + +static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file, + Thumbnail *thumb, + const size_t bhead_size, + const bool endian_switch) +{ + /* Iterate over file blocks until we find the thumbnail or run out of data. */ + uint8_t *bhead_data = (uint8_t *)BLI_array_alloca(bhead_data, bhead_size); + while (file_read(file, bhead_data, bhead_size)) { + /* Parse type and size from `BHead`. */ + const int32_t block_size = bytes_to_native_i32(&bhead_data[4], endian_switch); + + /* We're looking for the thumbnail, so skip any other block. */ + switch (*((int32_t *)bhead_data)) { + case MAKE_ID('T', 'E', 'S', 'T'): { + uint8_t shape[8]; + if (!file_read(file, shape, sizeof(shape))) { + return BT_INVALID_THUMB; + } + thumb->width = bytes_to_native_i32(&shape[0], endian_switch); + thumb->height = bytes_to_native_i32(&shape[4], endian_switch); + + /* Verify that image dimensions and data size make sense. */ + size_t data_size = block_size - 8; + const size_t expected_size = thumb->width * thumb->height * 4; + if (thumb->width < 0 || thumb->height < 0 || data_size != expected_size) { + return BT_INVALID_THUMB; + } + + thumb->data = blender::Array(data_size); + if (!file_read(file, thumb->data.data(), data_size)) { + return BT_INVALID_THUMB; + } + return BT_OK; + } + case MAKE_ID('R', 'E', 'N', 'D'): { + if (!file_seek(file, block_size)) { + return BT_INVALID_THUMB; + } + /* Check the next block. */ + break; + } + default: { + /* Early exit if there are no `TEST` or `REND` blocks. + * This saves scanning the entire blend file which could be slow. */ + return BT_INVALID_THUMB; + } + } + } + + return BT_INVALID_THUMB; +} + +/** + * This function extracts the thumbnail from the .blend file into thumb. + * Returns #BT_OK for success and the relevant error code otherwise. + */ +eThumbStatus blendthumb_create_thumb_from_file(FileReader *rawfile, Thumbnail *thumb) +{ + /* Read header in order to identify file type. */ + char header[12]; + if (rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) { + rawfile->close(rawfile); + return BT_ERROR; + } + + /* Rewind the file after reading the header. */ + rawfile->seek(rawfile, 0, SEEK_SET); + + /* Try to identify the file type from the header. */ + FileReader *file = nullptr; + if (BLI_str_startswith(header, "BLENDER")) { + file = rawfile; + rawfile = nullptr; + } + else if (BLI_file_magic_is_gzip(header)) { + file = BLI_filereader_new_gzip(rawfile); + if (file != nullptr) { + rawfile = nullptr; /* The Gzip #FileReader takes ownership of raw-file. */ + } + } + else if (BLI_file_magic_is_zstd(header)) { + file = BLI_filereader_new_zstd(rawfile); + if (file != nullptr) { + rawfile = nullptr; /* The Zstd #FileReader takes ownership of raw-file. */ + } + } + + /* Clean up rawfile if it wasn't taken over. */ + if (rawfile != nullptr) { + rawfile->close(rawfile); + } + + if (file == nullptr) { + return BT_ERROR; + } + + /* Re-read header in case we had compression. */ + if (file->read(file, header, sizeof(header)) != sizeof(header)) { + file->close(file); + return BT_ERROR; + } + + /* Check if the header format is valid for a .blend file. */ + if (!blend_header_check_magic(header)) { + file->close(file); + return BT_INVALID_FILE; + } + + /* Check if the file is new enough to contain a thumbnail. */ + if (!blend_header_is_version_valid(header)) { + file->close(file); + return BT_EARLY_VERSION; + } + + /* Depending on where it was saved, the file can use different pointer size or endianness. */ + int bhead_size = 16 + blend_header_pointer_size(header); + const bool endian_switch = blend_header_is_endian_switch_needed(header); + + /* Read the thumbnail. */ + eThumbStatus err = blendthumb_extract_from_file_impl(file, thumb, bhead_size, endian_switch); + file->close(file); + if (err != BT_OK) { + return err; + } + + thumb_data_vertical_flip(thumb); + return BT_OK; +} diff --git a/source/blender/blendthumb/src/blendthumb_png.cc b/source/blender/blendthumb/src/blendthumb_png.cc new file mode 100644 index 00000000000..d8156150078 --- /dev/null +++ b/source/blender/blendthumb/src/blendthumb_png.cc @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup blendthumb + * + * Expose #blendthumb_create_png_data_from_thumb that creates the PNG data + * but does not write it to a file. + */ + +#include +#include +#include + +#include "blendthumb.hh" + +#include "BLI_endian_defines.h" +#include "BLI_endian_switch.h" +#include "BLI_vector.hh" + +static void png_extend_native_int32(blender::Vector &output, int32_t data) +{ + if (ENDIAN_ORDER == L_ENDIAN) { + BLI_endian_switch_int32(&data); + } + output.extend_unchecked(blender::Span((uint8_t *)&data, 4)); +} + +/** The number of bytes each chunk uses on top of the data that's written. */ +#define PNG_CHUNK_EXTRA 12 + +static void png_chunk_create(blender::Vector &output, + const uint32_t tag, + const blender::Vector &data) +{ + uint32_t crc = crc32(0, nullptr, 0); + crc = crc32(crc, (uint8_t *)&tag, sizeof(tag)); + crc = crc32(crc, (uint8_t *)data.data(), data.size()); + + png_extend_native_int32(output, data.size()); + output.extend_unchecked(blender::Span((uint8_t *)&tag, sizeof(tag))); + output.extend_unchecked(data); + png_extend_native_int32(output, crc); +} + +static blender::Vector filtered_rows_from_thumb(const Thumbnail *thumb) +{ + /* In the image data sent to the compression step, each scan-line is preceded by a filter type + * byte containing the numeric code of the filter algorithm used for that scan-line. */ + const size_t line_size = thumb->width * 4; + blender::Vector filtered{}; + size_t final_size = thumb->height * (line_size + 1); + filtered.reserve(final_size); + for (int i = 0; i < thumb->height; i++) { + filtered.append_unchecked(0x00); + filtered.extend_unchecked(blender::Span(&thumb->data[i * line_size], line_size)); + } + BLI_assert(final_size == filtered.size()); + return filtered; +} + +static std::optional> zlib_compress(const blender::Vector &data) +{ + unsigned long uncompressed_size = data.size(); + uLongf compressed_size = compressBound(uncompressed_size); + + blender::Vector compressed(compressed_size, 0x00); + + int return_value = compress2((uchar *)compressed.data(), + &compressed_size, + (uchar *)data.data(), + uncompressed_size, + Z_NO_COMPRESSION); + if (return_value != Z_OK) { + /* Something went wrong with compression of data. */ + return std::nullopt; + } + compressed.resize(compressed_size); + return compressed; +} + +std::optional> blendthumb_create_png_data_from_thumb( + const Thumbnail *thumb) +{ + if (thumb->data.is_empty()) { + return std::nullopt; + } + + /* Create `IDAT` chunk data. */ + blender::Vector image_data{}; + { + auto image_data_opt = zlib_compress(filtered_rows_from_thumb(thumb)); + if (image_data_opt == std::nullopt) { + return std::nullopt; + } + image_data = *image_data_opt; + } + + /* Create the IHDR chunk data. */ + blender::Vector ihdr_data{}; + { + const size_t ihdr_data_final_size = 4 + 4 + 5; + ihdr_data.reserve(ihdr_data_final_size); + png_extend_native_int32(ihdr_data, thumb->width); + png_extend_native_int32(ihdr_data, thumb->height); + ihdr_data.extend_unchecked({ + 0x08, /* Bit Depth. */ + 0x06, /* Color Type. */ + 0x00, /* Compression method. */ + 0x00, /* Filter method. */ + 0x00, /* Interlace method. */ + }); + BLI_assert((size_t)ihdr_data.size() == ihdr_data_final_size); + } + + /* Join it all together to create a PNG image. */ + blender::Vector png_buf{}; + { + const size_t png_buf_final_size = ( + /* Header. */ + 8 + + /* `IHDR` chunk. */ + (ihdr_data.size() + PNG_CHUNK_EXTRA) + + /* `IDAT` chunk. */ + (image_data.size() + PNG_CHUNK_EXTRA) + + /* `IEND` chunk. */ + PNG_CHUNK_EXTRA); + + png_buf.reserve(png_buf_final_size); + + /* This is the standard PNG file header. Every PNG file starts with it. */ + png_buf.extend_unchecked({0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}); + + png_chunk_create(png_buf, MAKE_ID('I', 'H', 'D', 'R'), ihdr_data); + png_chunk_create(png_buf, MAKE_ID('I', 'D', 'A', 'T'), image_data); + png_chunk_create(png_buf, MAKE_ID('I', 'E', 'N', 'D'), {}); + + BLI_assert((size_t)png_buf.size() == png_buf_final_size); + } + + return png_buf; +} diff --git a/source/blender/blendthumb/src/blendthumb_win32.cc b/source/blender/blendthumb/src/blendthumb_win32.cc new file mode 100644 index 00000000000..d757bb1c97e --- /dev/null +++ b/source/blender/blendthumb/src/blendthumb_win32.cc @@ -0,0 +1,237 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup blendthumb + * + * Thumbnail from Blend file extraction for MS-Windows. + */ + +#include +#include +#include +#include +#include /* for #IThumbnailProvider */ + +#include "Wincodec.h" + +#include "blendthumb.hh" + +#include "BLI_filereader.h" + +#pragma comment(lib, "shlwapi.lib") + +/** + * This thumbnail provider implements #IInitializeWithStream to enable being hosted + * in an isolated process for robustness. + */ +class CBlendThumb : public IInitializeWithStream, public IThumbnailProvider { + public: + CBlendThumb() : _cRef(1), _pStream(NULL) + { + } + + virtual ~CBlendThumb() + { + if (_pStream) { + _pStream->Release(); + } + } + + IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv) + { + static const QITAB qit[] = { + QITABENT(CBlendThumb, IInitializeWithStream), + QITABENT(CBlendThumb, IThumbnailProvider), + {0}, + }; + return QISearch(this, qit, riid, ppv); + } + + IFACEMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&_cRef); + } + + IFACEMETHODIMP_(ULONG) Release() + { + ULONG cRef = InterlockedDecrement(&_cRef); + if (!cRef) { + delete this; + } + return cRef; + } + + /** IInitializeWithStream */ + IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode); + + /** IThumbnailProvider */ + IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha); + + private: + long _cRef; + IStream *_pStream; /* provided in Initialize(). */ +}; + +HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv) +{ + CBlendThumb *pNew = new (std::nothrow) CBlendThumb(); + HRESULT hr = pNew ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) { + hr = pNew->QueryInterface(riid, ppv); + pNew->Release(); + } + return hr; +} + +IFACEMETHODIMP CBlendThumb::Initialize(IStream *pStream, DWORD) +{ + if (_pStream != NULL) { + /* Can only be initialized once. */ + return E_UNEXPECTED; + } + /* Take a reference to the stream. */ + return pStream->QueryInterface(&_pStream); +} + +/** + * #FileReader compatible wrapper around the Windows stream that gives access to the .blend file. + */ +typedef struct { + FileReader reader; + + IStream *_pStream; +} StreamReader; + +static ssize_t stream_read(FileReader *reader, void *buffer, size_t size) +{ + StreamReader *stream = (StreamReader *)reader; + + ULONG readsize; + stream->_pStream->Read(buffer, size, &readsize); + stream->reader.offset += readsize; + + return (ssize_t)readsize; +} + +static off64_t stream_seek(FileReader *reader, off64_t offset, int whence) +{ + StreamReader *stream = (StreamReader *)reader; + + DWORD origin = STREAM_SEEK_SET; + switch (whence) { + case SEEK_CUR: + origin = STREAM_SEEK_CUR; + break; + case SEEK_END: + origin = STREAM_SEEK_END; + break; + } + LARGE_INTEGER offsetI; + offsetI.QuadPart = offset; + ULARGE_INTEGER newPos; + stream->_pStream->Seek(offsetI, origin, &newPos); + stream->reader.offset = newPos.QuadPart; + + return stream->reader.offset; +} + +static void stream_close(FileReader *reader) +{ + StreamReader *stream = (StreamReader *)reader; + delete stream; +} + +IFACEMETHODIMP CBlendThumb::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha) +{ + HRESULT hr = S_FALSE; + + StreamReader *file = new StreamReader; + file->reader.read = stream_read; + file->reader.seek = stream_seek; + file->reader.close = stream_close; + file->reader.offset = 0; + file->_pStream = _pStream; + + file->reader.seek(&file->reader, 0, SEEK_SET); + + /* Extract thumbnail from stream. */ + Thumbnail thumb; + if (blendthumb_create_thumb_from_file(&file->reader, &thumb) != BT_OK) { + return S_FALSE; + } + + /* Convert to BGRA for Windows. */ + for (int i = 0; i < thumb.width * thumb.height; i++) { + std::swap(thumb.data[4 * i], thumb.data[4 * i + 2]); + } + + *phbmp = CreateBitmap(thumb.width, thumb.height, 1, 32, thumb.data.data()); + if (!*phbmp) { + return E_FAIL; + } + *pdwAlpha = WTSAT_ARGB; + + /* Scale down the thumbnail if required. */ + if ((unsigned)thumb.width > cx || (unsigned)thumb.height > cx) { + float scale = 1.0f / (std::max(thumb.width, thumb.height) / (float)cx); + LONG NewWidth = (LONG)(thumb.width * scale); + LONG NewHeight = (LONG)(thumb.height * scale); + + IWICImagingFactory *pImgFac; + hr = CoCreateInstance( + CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImgFac)); + + IWICBitmap *WICBmp; + hr = pImgFac->CreateBitmapFromHBITMAP(*phbmp, 0, WICBitmapUseAlpha, &WICBmp); + + BITMAPINFO bmi = {}; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = NewWidth; + bmi.bmiHeader.biHeight = -NewHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + BYTE *pBits; + HBITMAP ResizedHBmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void **)&pBits, NULL, 0); + hr = ResizedHBmp ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) { + IWICBitmapScaler *pIScaler; + hr = pImgFac->CreateBitmapScaler(&pIScaler); + hr = pIScaler->Initialize(WICBmp, NewWidth, NewHeight, WICBitmapInterpolationModeFant); + + WICRect rect = {0, 0, NewWidth, NewHeight}; + hr = pIScaler->CopyPixels(&rect, NewWidth * 4, NewWidth * NewHeight * 4, pBits); + + if (SUCCEEDED(hr)) { + DeleteObject(*phbmp); + *phbmp = ResizedHBmp; + } + else { + DeleteObject(ResizedHBmp); + } + + pIScaler->Release(); + } + WICBmp->Release(); + pImgFac->Release(); + } + else { + hr = S_OK; + } + return hr; +} diff --git a/source/blender/blendthumb/src/BlendThumb.def b/source/blender/blendthumb/src/blendthumb_win32.def similarity index 100% rename from source/blender/blendthumb/src/BlendThumb.def rename to source/blender/blendthumb/src/blendthumb_win32.def diff --git a/source/blender/blendthumb/src/BlendThumb.rc b/source/blender/blendthumb/src/blendthumb_win32.rc similarity index 100% rename from source/blender/blendthumb/src/BlendThumb.rc rename to source/blender/blendthumb/src/blendthumb_win32.rc diff --git a/source/blender/blendthumb/src/Dll.cpp b/source/blender/blendthumb/src/blendthumb_win32_dll.cc similarity index 100% rename from source/blender/blendthumb/src/Dll.cpp rename to source/blender/blendthumb/src/blendthumb_win32_dll.cc diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 47fb2642da1..45bfc3d6bdb 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -516,8 +516,7 @@ if(UNIX AND NOT APPLE) ) install( - PROGRAMS - ${CMAKE_SOURCE_DIR}/release/bin/blender-thumbnailer.py + TARGETS blender-thumbnailer DESTINATION "." ) @@ -560,7 +559,7 @@ if(UNIX AND NOT APPLE) DESTINATION share/icons/hicolor/symbolic/apps ) install( - PROGRAMS ${CMAKE_SOURCE_DIR}/release/bin/blender-thumbnailer.py + TARGETS blender-thumbnailer DESTINATION bin ) set(BLENDER_TEXT_FILES_DESTINATION share/doc/blender) From b280699078eee23bd8eb572668d6f0d8972d879a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 20 Oct 2021 10:45:59 +1100 Subject: [PATCH 0960/1500] Cleanup: use elem macros --- .../blendthumb/src/blendthumb_extract.cc | 2 +- .../blenkernel/intern/attribute_access.cc | 2 +- source/blender/blenkernel/intern/curve.c | 2 +- .../blender/blenkernel/intern/dynamicpaint.c | 3 +-- source/blender/blenkernel/intern/fluid.c | 2 +- .../blender/blenkernel/intern/mesh_convert.cc | 4 +-- source/blender/blenkernel/intern/node.cc | 2 +- source/blender/blenkernel/intern/object.c | 2 +- source/blender/blenlib/intern/boxpack_2d.c | 8 +++--- source/blender/blenlib/intern/delaunay_2d.cc | 4 +-- .../blender/blenlib/intern/filereader_gzip.c | 2 +- source/blender/blenlib/intern/noise.cc | 2 +- .../blender/blenlib/intern/scanfill_utils.c | 6 ++--- source/blender/bmesh/tools/bmesh_bevel.c | 6 ++--- source/blender/draw/intern/draw_cache.c | 6 ++--- .../draw/intern/draw_cache_impl_displist.c | 4 +-- source/blender/editors/animation/keyframing.c | 2 +- source/blender/editors/armature/pose_slide.c | 2 +- source/blender/editors/curve/editcurve.c | 2 +- source/blender/editors/curve/editcurve_add.c | 2 +- .../editors/gpencil/gpencil_bake_animation.c | 2 +- .../editors/interface/interface_handlers.c | 2 +- .../interface/interface_region_popup.c | 4 +-- source/blender/editors/mesh/editmesh_bevel.c | 3 +-- source/blender/editors/mesh/editmesh_rip.c | 2 +- source/blender/editors/object/object_add.c | 2 +- source/blender/editors/object/object_bake.c | 2 +- .../editors/sculpt_paint/paint_cursor.c | 2 +- .../editors/sculpt_paint/paint_vertex.c | 4 +-- .../sculpt_paint/paint_vertex_color_ops.c | 2 +- source/blender/editors/sculpt_paint/sculpt.c | 5 ++-- .../editors/space_buttons/buttons_context.c | 26 +++++++++---------- .../blender/editors/space_image/image_edit.c | 2 +- .../editors/space_sequencer/sequencer_add.c | 2 +- .../editors/space_sequencer/sequencer_draw.c | 2 +- .../space_sequencer/sequencer_thumbnails.c | 2 +- .../transform/transform_convert_object.c | 5 ++-- .../intern/lineart/lineart_cpu.c | 5 ++-- source/blender/imbuf/intern/bmp.c | 3 +-- source/blender/imbuf/intern/tiff.c | 2 +- source/blender/io/avi/intern/avi.c | 2 +- .../io/usd/intern/usd_reader_material.cc | 2 +- .../blender/io/usd/intern/usd_reader_mesh.cc | 14 +++++----- source/blender/makesdna/intern/dna_genfile.c | 2 +- source/blender/modifiers/intern/MOD_mask.cc | 2 +- source/blender/modifiers/intern/MOD_skin.c | 2 +- .../nodes/node_geo_curve_primitive_circle.cc | 2 +- .../nodes/node_geo_delete_geometry.cc | 2 +- .../blender/nodes/shader/node_shader_util.c | 2 +- .../nodes/node_shader_bsdf_principled.c | 2 +- .../shader/nodes/node_shader_tex_voronoi.cc | 9 +++---- source/blender/python/intern/bpy_rna.c | 2 +- .../python/mathutils/mathutils_Matrix.c | 2 +- source/blender/render/intern/texture_image.c | 2 +- source/blender/sequencer/intern/image_cache.c | 2 +- .../windowmanager/intern/wm_event_system.c | 3 +-- .../windowmanager/xr/intern/wm_xr_action.c | 2 +- 57 files changed, 95 insertions(+), 103 deletions(-) diff --git a/source/blender/blendthumb/src/blendthumb_extract.cc b/source/blender/blendthumb/src/blendthumb_extract.cc index 99b13c89994..f1c5567bab5 100644 --- a/source/blender/blendthumb/src/blendthumb_extract.cc +++ b/source/blender/blendthumb/src/blendthumb_extract.cc @@ -42,7 +42,7 @@ static bool blend_header_check_magic(const char header[12]) return false; } /* Check pointer size and endianness indicators. */ - if (((header[7] != '_') && (header[7] != '-')) || ((header[8] != 'v') && (header[8] != 'V'))) { + if (!ELEM(header[7], '_', '-') || !ELEM(header[8], 'v', 'V')) { return false; } /* Check version number. */ diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 0658569752c..d0510e0008e 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -1060,7 +1060,7 @@ std::unique_ptr GeometryComponent::attribute_try_get_for_r } std::unique_ptr varray = std::move(attribute.varray); - if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) { + if (!ELEM(domain, ATTR_DOMAIN_AUTO, attribute.domain)) { varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain); if (!varray) { return {}; diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index b0fc1426093..620110f7881 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -3347,7 +3347,7 @@ static void calchandleNurb_intern(BezTriple *bezt, if (skip_align || /* when one handle is free, alignming makes no sense, see: T35952 */ - (ELEM(HD_FREE, bezt->h1, bezt->h2)) || + ELEM(HD_FREE, bezt->h1, bezt->h2) || /* also when no handles are aligned, skip this step */ (!ELEM(HD_ALIGN, bezt->h1, bezt->h2) && !ELEM(HD_ALIGN_DOUBLESIDE, bezt->h1, bezt->h2))) { /* handles need to be updated during animation and applying stuff like hooks, diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 6fc3e0fa8e2..2ef7ef91160 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -5883,8 +5883,7 @@ static void dynamic_paint_surface_pre_step_cb(void *__restrict userdata, } /* dissolve for float types */ else if (surface->flags & MOD_DPAINT_DISSOLVE && - (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WEIGHT)) { + ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WEIGHT)) { float *point = &((float *)sData->type_data)[index]; /* log or linear */ value_dissolve( diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index e272b71acb8..6b7594dcf36 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -2676,7 +2676,7 @@ static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int n } /* Activate color field if flows add smoke with varying colors. */ if (ffs->density != 0.0 && - (ffs->type == FLUID_FLOW_TYPE_SMOKE || ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE)) { + ELEM(ffs->type, FLUID_FLOW_TYPE_SMOKE, FLUID_FLOW_TYPE_SMOKEFIRE)) { if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) { copy_v3_v3(fds->active_color, ffs->color); active_fields |= FLUID_DOMAIN_ACTIVE_COLOR_SET; diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 59cdb6a2b27..adfbe4b8c94 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -454,10 +454,10 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, mloopuv->uv[1] = (v % dl->nr) / (float)orco_sizeu; /* cyclic correction */ - if ((i == 1 || i == 2) && mloopuv->uv[0] == 0.0f) { + if ((ELEM(i, 1, 2)) && mloopuv->uv[0] == 0.0f) { mloopuv->uv[0] = 1.0f; } - if ((i == 0 || i == 1) && mloopuv->uv[1] == 0.0f) { + if ((ELEM(i, 0, 1)) && mloopuv->uv[1] == 0.0f) { mloopuv->uv[1] = 1.0f; } } diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 0dffa63d044..b47fe45c868 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -1417,7 +1417,7 @@ void nodeUnregisterType(bNodeType *nt) bool nodeTypeUndefined(bNode *node) { return (node->typeinfo == &NodeTypeUndefined) || - ((node->type == NODE_GROUP || node->type == NODE_CUSTOM_GROUP) && node->id && + ((ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) && node->id && ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING)); } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index e3bb384ffcc..2f3b229a180 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -5628,7 +5628,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph, /* skip subframe if object is parented * to vertex of a dynamic paint canvas */ - if (no_update && (ob->partype == PARVERT1 || ob->partype == PARVERT3)) { + if (no_update && (ELEM(ob->partype, PARVERT1, PARVERT3))) { return false; } diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c index 4a07f1134d0..cf5831cada5 100644 --- a/source/blender/blenlib/intern/boxpack_2d.c +++ b/source/blender/blenlib/intern/boxpack_2d.c @@ -516,7 +516,7 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r * flag verts on one or both of the boxes * as being used by checking the width or * height of both boxes */ - if (vert->tlb && vert->trb && (box == vert->tlb || box == vert->trb)) { + if (vert->tlb && vert->trb && (ELEM(box, vert->tlb, vert->trb))) { if (UNLIKELY(fabsf(vert->tlb->h - vert->trb->h) < EPSILON_MERGE)) { #ifdef USE_MERGE # define A (vert->trb->v[TL]) @@ -547,7 +547,7 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r vert->tlb->v[TR]->free &= ~(TRF | BRF); } } - else if (vert->blb && vert->brb && (box == vert->blb || box == vert->brb)) { + else if (vert->blb && vert->brb && (ELEM(box, vert->blb, vert->brb))) { if (UNLIKELY(fabsf(vert->blb->h - vert->brb->h) < EPSILON_MERGE)) { #ifdef USE_MERGE # define A (vert->blb->v[BR]) @@ -579,7 +579,7 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r } } /* Horizontal */ - if (vert->tlb && vert->blb && (box == vert->tlb || box == vert->blb)) { + if (vert->tlb && vert->blb && (ELEM(box, vert->tlb, vert->blb))) { if (UNLIKELY(fabsf(vert->tlb->w - vert->blb->w) < EPSILON_MERGE)) { #ifdef USE_MERGE # define A (vert->blb->v[TL]) @@ -610,7 +610,7 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r vert->tlb->v[BL]->free &= ~(BLF | BRF); } } - else if (vert->trb && vert->brb && (box == vert->trb || box == vert->brb)) { + else if (vert->trb && vert->brb && (ELEM(box, vert->trb, vert->brb))) { if (UNLIKELY(fabsf(vert->trb->w - vert->brb->w) < EPSILON_MERGE)) { #ifdef USE_MERGE diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc index 4582ea69d9b..53e881a9fc7 100644 --- a/source/blender/blenlib/intern/delaunay_2d.cc +++ b/source/blender/blenlib/intern/delaunay_2d.cc @@ -2274,8 +2274,8 @@ void add_face_constraints(CDT_state *cdt_state, * making valid BMesh faces. */ int id = cdt_state->need_ids ? f : 0; add_face_ids(cdt_state, face_symedge0, id, fedge_start, fedge_end); - if (cdt_state->need_ids || (output_type == CDT_CONSTRAINTS_VALID_BMESH || - output_type == CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES)) { + if (cdt_state->need_ids || + ELEM(output_type, CDT_CONSTRAINTS_VALID_BMESH, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES)) { add_face_ids(cdt_state, face_symedge0, f, fedge_start, fedge_end); } } diff --git a/source/blender/blenlib/intern/filereader_gzip.c b/source/blender/blenlib/intern/filereader_gzip.c index 72eb153a8b9..fe766e5da41 100644 --- a/source/blender/blenlib/intern/filereader_gzip.c +++ b/source/blender/blenlib/intern/filereader_gzip.c @@ -64,7 +64,7 @@ static ssize_t gzip_read(FileReader *reader, void *buffer, size_t size) int ret = inflate(&gzip->strm, Z_NO_FLUSH); - if (ret != Z_OK && ret != Z_BUF_ERROR) { + if (!ELEM(ret, Z_OK, Z_BUF_ERROR)) { break; } } diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index 58b6bb638e9..5fa2746d07f 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -410,7 +410,7 @@ BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z) { uint32_t h = hash & 15u; float u = h < 8u ? x : y; - float vt = ((h == 12u) || (h == 14u)) ? x : z; + float vt = ELEM(h, 12u, 14u) ? x : z; float v = h < 4u ? y : vt; return negate_if(u, h & 1u) + negate_if(v, h & 2u); } diff --git a/source/blender/blenlib/intern/scanfill_utils.c b/source/blender/blenlib/intern/scanfill_utils.c index ec0f8659395..33c0f4afd01 100644 --- a/source/blender/blenlib/intern/scanfill_utils.c +++ b/source/blender/blenlib/intern/scanfill_utils.c @@ -156,15 +156,13 @@ static ScanFillEdge *edge_step(PolyInfo *poly_info, eed = (e_curr->next && e_curr != poly_info[poly_nr].edge_last) ? e_curr->next : poly_info[poly_nr].edge_first; - if ((v_curr == eed->v1 || v_curr == eed->v2) == true && - (ELEM(v_prev, eed->v1, eed->v2)) == false) { + if (ELEM(v_curr, eed->v1, eed->v2) == true && ELEM(v_prev, eed->v1, eed->v2) == false) { return eed; } eed = (e_curr->prev && e_curr != poly_info[poly_nr].edge_first) ? e_curr->prev : poly_info[poly_nr].edge_last; - if ((v_curr == eed->v1 || v_curr == eed->v2) == true && - (ELEM(v_prev, eed->v1, eed->v2)) == false) { + if (ELEM(v_curr, eed->v1, eed->v2) == true && ELEM(v_prev, eed->v1, eed->v2) == false) { return eed; } diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 8bd498a08bd..6cf3641fed2 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -4691,7 +4691,7 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe) * vertices to snap to the midline on the pipe, not just to one plane or the other. */ bool even = (ns % 2) == 0; bool midline = even && k == half_ns && - ((i == 0 && j == half_ns) || (i == ipipe1 || i == ipipe2)); + ((i == 0 && j == half_ns) || (ELEM(i, ipipe1, ipipe2))); snap_to_pipe_profile(vpipe, midline, mesh_vert(vm, i, j, k)->co); } } @@ -5217,7 +5217,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert for (int i = 0; i < n_bndv; i++) { for (int j = 0; j <= ns2; j++) { for (int k = 0; k <= ns; k++) { - if (j == 0 && (k == 0 || k == ns)) { + if (j == 0 && (ELEM(k, 0, ns))) { continue; /* Boundary corners already made. */ } if (!is_canon(vm, i, j, k)) { @@ -5794,7 +5794,7 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv) /* Make sure the pipe case ADJ mesh is used for both the "Grid Fill" (ADJ) and cutoff options. */ BoundVert *vpipe = NULL; - if ((vm->count == 3 || vm->count == 4) && bp->seg > 1) { + if (ELEM(vm->count, 3, 4) && bp->seg > 1) { /* Result is passed to bevel_build_rings to avoid overhead. */ vpipe = pipe_test(bv); if (vpipe) { diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index a3a5d6b065a..7b66026f7ad 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -2431,9 +2431,9 @@ GPUBatch *DRW_cache_bone_stick_get(void) /* Bone rectangle */ pos[0] = 0.0f; for (int i = 0; i < 6; i++) { - pos[1] = (ELEM(i, 0, 3)) ? 0.0f : ((i < 3) ? 1.0f : -1.0f); - flag = ((i < 2 || i > 4) ? POS_HEAD : POS_TAIL) | ((i == 0 || i == 3) ? 0 : COL_WIRE) | - COL_BONE | POS_BONE; + pos[1] = ELEM(i, 0, 3) ? 0.0f : ((i < 3) ? 1.0f : -1.0f); + flag = ((i < 2 || i > 4) ? POS_HEAD : POS_TAIL) | (ELEM(i, 0, 3) ? 0 : COL_WIRE) | COL_BONE | + POS_BONE; GPU_vertbuf_attr_set(vbo, attr_id.pos, v, pos); GPU_vertbuf_attr_set(vbo, attr_id.flag, v, &flag); GPU_indexbuf_add_generic_vert(&elb, v++); diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c index 1fed5d79697..fb8f998f036 100644 --- a/source/blender/draw/intern/draw_cache_impl_displist.c +++ b/source/blender/draw/intern/draw_cache_impl_displist.c @@ -372,10 +372,10 @@ static void surf_uv_quad(const DispList *dl, const uint quad[4], float r_uv[4][2 r_uv[i][1] = (quad[i] % dl->nr) / (float)orco_sizeu; /* cyclic correction */ - if ((i == 1 || i == 2) && r_uv[i][0] == 0.0f) { + if (ELEM(i, 1, 2) && r_uv[i][0] == 0.0f) { r_uv[i][0] = 1.0f; } - if ((i == 0 || i == 1) && r_uv[i][1] == 0.0f) { + if (ELEM(i, 0, 1) && r_uv[i][1] == 0.0f) { r_uv[i][1] = 1.0f; } } diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 2c6b5cc594b..0a9f1a36a28 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -425,7 +425,7 @@ int insert_bezt_fcurve(FCurve *fcu, const BezTriple *bezt, eInsertKeyFlags flag) if (flag & INSERTKEY_CYCLE_AWARE) { /* If replacing an end point of a cyclic curve without offset, * modify the other end too. */ - if ((i == 0 || i == fcu->totvert - 1) && + if (ELEM(i, 0, fcu->totvert - 1) && BKE_fcurve_get_cycle_type(fcu) == FCU_CYCLE_PERFECT) { replace_bezt_keyframe_ypos(&fcu->bezt[i == 0 ? fcu->totvert - 1 : 0], bezt); } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 1079985346c..ca024f1d4d6 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -634,7 +634,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) interp_qt_qtqt(quat_final, quat_prev, quat_next, ED_slider_factor_get(pso->slider)); } - else if (pso->mode == POSESLIDE_PUSH || pso->mode == POSESLIDE_RELAX) { + else if (ELEM(pso->mode, POSESLIDE_PUSH, POSESLIDE_RELAX)) { float quat_breakdown[4]; float quat_curr[4]; diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index d4fdf46d8f4..029c8b05923 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -5409,7 +5409,7 @@ static int ed_editcurve_addvert(Curve *cu, add_v3_v3(bezt->vec[1], ofs); add_v3_v3(bezt->vec[2], ofs); - if (((nu->flagu & CU_NURB_CYCLIC) == 0) && (i == 0 || i == nu->pntsu - 1)) { + if (((nu->flagu & CU_NURB_CYCLIC) == 0) && ELEM(i, 0, nu->pntsu - 1)) { BKE_nurb_handle_calc_simple_auto(nu, bezt); } } diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 75fb17e8cc1..614805a70f5 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -356,7 +356,7 @@ Nurb *ED_curve_add_nurbs_primitive( bp->vec[0] += fac * grid; fac = (float)b - 1.5f; bp->vec[1] += fac * grid; - if ((a == 1 || a == 2) && (b == 1 || b == 2)) { + if ((ELEM(a, 1, 2)) && (ELEM(b, 1, 2))) { bp->vec[2] += grid; } mul_m4_v3(mat, bp->vec); diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c index 960f228edd0..4467e6202b4 100644 --- a/source/blender/editors/gpencil/gpencil_bake_animation.c +++ b/source/blender/editors/gpencil/gpencil_bake_animation.c @@ -102,7 +102,7 @@ static bool gpencil_bake_grease_pencil_animation_poll(bContext *C) } /* Check if grease pencil or empty for dupli groups. */ - if ((obact == NULL) || ((obact->type != OB_GPENCIL) && (obact->type != OB_EMPTY))) { + if ((obact == NULL) || (!ELEM(obact->type, OB_GPENCIL, OB_EMPTY))) { return false; } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index e1f8d18ce35..81494264cdf 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -11129,7 +11129,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle case EVT_XKEY: case EVT_YKEY: case EVT_ZKEY: { - if ((event->val == KM_PRESS || event->val == KM_DBL_CLICK) && + if ((ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) && !IS_EVENT_MOD(event, shift, ctrl, oskey)) { LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->menu_key == event->type) { diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 55a162c883a..3234af3d344 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -184,10 +184,10 @@ static void ui_popup_block_position(wmWindow *window, dir1 &= (UI_DIR_UP | UI_DIR_DOWN); } - if ((dir2 == 0) && (dir1 == UI_DIR_LEFT || dir1 == UI_DIR_RIGHT)) { + if ((dir2 == 0) && (ELEM(dir1, UI_DIR_LEFT, UI_DIR_RIGHT))) { dir2 = UI_DIR_DOWN; } - if ((dir2 == 0) && (dir1 == UI_DIR_UP || dir1 == UI_DIR_DOWN)) { + if ((dir2 == 0) && (ELEM(dir1, UI_DIR_UP, UI_DIR_DOWN))) { dir2 = UI_DIR_LEFT; } diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 0d74187b50e..6f41b7f04a6 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -764,8 +764,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) } } /* Update offset accordingly to new offset_type. */ - if (!has_numinput && - (opdata->value_mode == OFFSET_VALUE || opdata->value_mode == OFFSET_VALUE_PERCENT)) { + if (!has_numinput && (ELEM(opdata->value_mode, OFFSET_VALUE, OFFSET_VALUE_PERCENT))) { edbm_bevel_mouse_set_value(op, event); } edbm_bevel_calc(op); diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index d1df063d9d0..07ec8e184ba 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -930,7 +930,7 @@ static int edbm_rip_invoke__edge(bContext *C, const wmEvent *event, Object *obed /* NOTE: if the case of 3 edges has one change in loop stepping, * if this becomes more involved we may be better off splitting * the 3 edge case into its own else-if branch */ - if ((totedge_manifold == 4 || totedge_manifold == 3) || (all_manifold == false)) { + if ((ELEM(totedge_manifold, 4, 3)) || (all_manifold == false)) { BMLoop *l_a = e_best->l; BMLoop *l_b = l_a->radial_next; diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index cf7c848ae24..9c6969110fd 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1487,7 +1487,7 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op) uiItemR(layout, op->ptr, "type", 0, NULL, ICON_NONE); int type = RNA_enum_get(op->ptr, "type"); - if (type == GP_LRT_COLLECTION || type == GP_LRT_OBJECT || type == GP_LRT_SCENE) { + if (ELEM(type, GP_LRT_COLLECTION, GP_LRT_OBJECT, GP_LRT_SCENE)) { uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE); uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE); bool in_front = RNA_boolean_get(op->ptr, "use_in_front"); diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index 3a10a423e91..1b6b0c78037 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -198,7 +198,7 @@ static bool multiresbake_check(bContext *C, wmOperator *op) ok = false; } - if (ibuf->rect_float && !(ibuf->channels == 0 || ibuf->channels == 4)) { + if (ibuf->rect_float && !(ELEM(ibuf->channels, 0, 4))) { ok = false; } diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index ab2b2f4b16b..dafc7f45e2e 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1103,7 +1103,7 @@ static void cursor_draw_point_with_symmetry(const uint gpuattr, float location[3], symm_rot_mat[4][4]; for (int i = 0; i <= symm; i++) { - if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5))))) { /* Axis Symmetry. */ flip_v3_v3(location, true_location, (char)i); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 9387b84f437..4e0dcfe8e3c 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -2332,7 +2332,7 @@ static void wpaint_do_symmetrical_brush_actions( /* symm is a bit combination of XYZ - 1 is mirror * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ for (i = 1; i <= symm; i++) { - if ((symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + if ((symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5))))) { cache->mirror_symmetry_pass = i; cache->radial_symmetry_pass = 0; SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0); @@ -3350,7 +3350,7 @@ static void vpaint_do_symmetrical_brush_actions( /* symm is a bit combination of XYZ - 1 is mirror * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ for (i = 1; i <= symm; i++) { - if (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) { + if (symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5)))) { cache->mirror_symmetry_pass = i; cache->radial_symmetry_pass = 0; SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0); diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c index 9f023dd6e63..f95e1d8d89c 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c @@ -48,7 +48,7 @@ static bool vertex_weight_paint_mode_poll(bContext *C) { Object *ob = CTX_data_active_object(C); Mesh *me = BKE_mesh_from_object(ob); - return (ob && (ob->mode == OB_MODE_VERTEX_PAINT || ob->mode == OB_MODE_WEIGHT_PAINT)) && + return (ob && (ELEM(ob->mode, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT))) && (me && me->totpoly && me->dvert); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 95be866adf1..1330beb917b 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1046,7 +1046,7 @@ int SCULPT_nearest_vertex_get( bool SCULPT_is_symmetry_iteration_valid(char i, char symm) { - return i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))); + return i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5)))); } /* Checks if a vertex is inside the brush radius from any of its mirrored axis. */ @@ -7695,8 +7695,7 @@ static void sculpt_restore_mesh(Sculpt *sd, Object *ob) /* Restore the mesh before continuing with anchored stroke. */ if ((brush->flag & BRUSH_ANCHORED) || - ((brush->sculpt_tool == SCULPT_TOOL_GRAB || - brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) && + ((ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ELASTIC_DEFORM)) && BKE_brush_use_size_pressure(brush)) || (brush->flag & BRUSH_DRAG_DOT)) { diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 91b0677ebaa..600b04f8563 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -242,55 +242,55 @@ static bool buttons_context_path_data(ButsContextPath *path, int type) PointerRNA *ptr = &path->ptr[path->len - 1]; /* if we already have a data, we're done */ - if (RNA_struct_is_a(ptr->type, &RNA_Mesh) && (type == -1 || type == OB_MESH)) { + if (RNA_struct_is_a(ptr->type, &RNA_Mesh) && (ELEM(type, -1, OB_MESH))) { return true; } if (RNA_struct_is_a(ptr->type, &RNA_Curve) && (type == -1 || ELEM(type, OB_CURVE, OB_SURF, OB_FONT))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (type == -1 || type == OB_ARMATURE)) { + if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (ELEM(type, -1, OB_ARMATURE))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (type == -1 || type == OB_MBALL)) { + if (RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (ELEM(type, -1, OB_MBALL))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_Lattice) && (type == -1 || type == OB_LATTICE)) { + if (RNA_struct_is_a(ptr->type, &RNA_Lattice) && (ELEM(type, -1, OB_LATTICE))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_Camera) && (type == -1 || type == OB_CAMERA)) { + if (RNA_struct_is_a(ptr->type, &RNA_Camera) && (ELEM(type, -1, OB_CAMERA))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_Light) && (type == -1 || type == OB_LAMP)) { + if (RNA_struct_is_a(ptr->type, &RNA_Light) && (ELEM(type, -1, OB_LAMP))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (type == -1 || type == OB_SPEAKER)) { + if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (ELEM(type, -1, OB_SPEAKER))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (type == -1 || type == OB_LIGHTPROBE)) { + if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (ELEM(type, -1, OB_LIGHTPROBE))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) { + if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (ELEM(type, -1, OB_GPENCIL))) { return true; } #ifdef WITH_HAIR_NODES - if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (type == -1 || type == OB_HAIR)) { + if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (ELEM(type, -1, OB_HAIR))) { return true; } #endif #ifdef WITH_POINT_CLOUD - if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) { + if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (ELEM(type, -1, OB_POINTCLOUD))) { return true; } #endif - if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) { + if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (ELEM(type, -1, OB_VOLUME))) { return true; } /* try to get an object in the path, no pinning supported here */ if (buttons_context_path_object(path)) { Object *ob = path->ptr[path->len - 1].data; - if (ob && (type == -1 || type == ob->type)) { + if (ob && (ELEM(type, -1, ob->type))) { RNA_id_pointer_create(ob->data, &path->ptr[path->len]); path->len++; diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index c1aa2da9e00..05124f49a20 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -434,7 +434,7 @@ void ED_space_image_scopes_update(const struct bContext *C, /* We also don't update scopes of render result during render. */ if (G.is_rendering) { const Image *image = sima->image; - if (image != NULL && (image->type == IMA_TYPE_R_RESULT || image->type == IMA_TYPE_COMPOSITE)) { + if (image != NULL && (ELEM(image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE))) { return; } } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 8d6d81d7115..e58e52b5e94 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -195,7 +195,7 @@ static int sequencer_generic_invoke_xy_guess_channel(bContext *C, int type) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if ((type == -1 || seq->type == type) && (seq->enddisp < timeline_frame) && + if ((ELEM(type, -1, seq->type)) && (seq->enddisp < timeline_frame) && (timeline_frame - seq->enddisp < proximity)) { tgt = seq; proximity = timeline_frame - seq->enddisp; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 7117323c214..644d2cbfedb 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1388,7 +1388,7 @@ static void draw_seq_strip(const bContext *C, if ((sseq->flag & SEQ_SHOW_OVERLAY) && (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_THUMBNAILS) && - (seq->type == SEQ_TYPE_MOVIE || seq->type == SEQ_TYPE_IMAGE)) { + (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE))) { draw_seq_strip_thumbnail(v2d, C, scene, seq, y1, y2, pixelx, pixely); } diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.c b/source/blender/editors/space_sequencer/sequencer_thumbnails.c index 06db96badc2..9efd78c47ff 100644 --- a/source/blender/editors/space_sequencer/sequencer_thumbnails.c +++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.c @@ -85,7 +85,7 @@ static void thumbnail_endjob(void *data) static bool check_seq_need_thumbnails(Sequence *seq, rctf *view_area) { - if (seq->type != SEQ_TYPE_MOVIE && seq->type != SEQ_TYPE_IMAGE) { + if (!ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) { return false; } if (min_ii(seq->startdisp, seq->start) > view_area->xmax) { diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 947fbb00fad..7e4e0892420 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -388,7 +388,7 @@ static void set_trans_object_base_flags(TransInfo *t) if (parsel != NULL) { /* Rotation around local centers are allowed to propagate. */ if ((t->around == V3D_AROUND_LOCAL_ORIGINS) && - (t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL)) { + (ELEM(t->mode, TFM_ROTATION, TFM_TRACKBALL))) { base->flag_legacy |= BA_TRANSFORM_CHILD; } else { @@ -432,8 +432,7 @@ static int count_proportional_objects(TransInfo *t) /* Clear all flags we need. It will be used to detect dependencies. */ trans_object_base_deps_flag_prepare(view_layer); /* Rotations around local centers are allowed to propagate, so we take all objects. */ - if (!((t->around == V3D_AROUND_LOCAL_ORIGINS) && - (t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL))) { + if (!((t->around == V3D_AROUND_LOCAL_ORIGINS) && (ELEM(t->mode, TFM_ROTATION, TFM_TRACKBALL)))) { /* Mark all parents. */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (BASE_SELECTED_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base)) { diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 7441c9a909c..6660da79b40 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1830,7 +1830,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu if (usage == OBJECT_LRT_INTERSECTION_ONLY) { tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; } - else if (usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_OCCLUSION_ONLY) { + else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) { tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; } @@ -1917,8 +1917,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu la_e->flags = use_type; la_e->object_ref = orig_ob; BLI_addtail(&la_e->segments, la_s); - if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || - usage == OBJECT_LRT_NO_INTERSECTION) { + if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) { lineart_add_edge_to_list_thread(obi, la_e); } diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c index 70bb70ec4fa..5b9d78f5614 100644 --- a/source/blender/imbuf/intern/bmp.c +++ b/source/blender/imbuf/intern/bmp.c @@ -151,8 +151,7 @@ ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[ } /* Validate and cross-check offsets and sizes. */ - if (x < 1 || - !(depth == 1 || depth == 4 || depth == 8 || depth == 16 || depth == 24 || depth == 32)) { + if (x < 1 || !(ELEM(depth, 1, 4, 8, 16, 24, 32))) { return NULL; } diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index 7b68e3f3bea..623c00fa670 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -457,7 +457,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image) } /* simple RGBA image */ - if (!(bitspersample == 32 || bitspersample == 16)) { + if (!(ELEM(bitspersample, 32, 16))) { success |= TIFFReadRGBAImage(image, ibuf->x, ibuf->y, tmpibuf->rect, 0); } /* contiguous channels: RGBRGBRGB */ diff --git a/source/blender/io/avi/intern/avi.c b/source/blender/io/avi/intern/avi.c index 88f2e1a259f..87058693378 100644 --- a/source/blender/io/avi/intern/avi.c +++ b/source/blender/io/avi/intern/avi.c @@ -145,7 +145,7 @@ static bool fcc_is_data(int fcc) fccs[2] = fcc >> 16; fccs[3] = fcc >> 24; - if (!isdigit(fccs[0]) || !isdigit(fccs[1]) || (fccs[2] != 'd' && fccs[2] != 'w')) { + if (!isdigit(fccs[0]) || !isdigit(fccs[1]) || (!ELEM(fccs[2], 'd', 'w'))) { return 0; } if (!ELEM(fccs[3], 'b', 'c')) { diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc index 93e433e231c..317dfd2a62b 100644 --- a/source/blender/io/usd/intern/usd_reader_material.cc +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -648,7 +648,7 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader, color_space = file_input.GetAttr().GetColorSpace(); } - if (color_space == usdtokens::RAW || color_space == usdtokens::raw) { + if (ELEM(color_space, usdtokens::RAW, usdtokens::raw)) { STRNCPY(image->colorspace_settings.name, "Raw"); } } diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 9c75bc8afae..5c8bd88e87a 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -390,8 +390,9 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo const UVSample &sample = uv_primvars[layer_idx]; - if (!(sample.interpolation == pxr::UsdGeomTokens->faceVarying || - sample.interpolation == pxr::UsdGeomTokens->vertex)) { + if (!(ELEM(sample.interpolation, + pxr::UsdGeomTokens->faceVarying, + pxr::UsdGeomTokens->vertex))) { std::cerr << "WARNING: unexpected interpolation type " << sample.interpolation << " for uv " << layer->name << std::endl; continue; @@ -781,9 +782,10 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, bool is_uv = false; /* Assume all UVs are stored in one of these primvar types */ - if (type == pxr::SdfValueTypeNames->TexCoord2hArray || - type == pxr::SdfValueTypeNames->TexCoord2fArray || - type == pxr::SdfValueTypeNames->TexCoord2dArray) { + if (ELEM(type, + pxr::SdfValueTypeNames->TexCoord2hArray, + pxr::SdfValueTypeNames->TexCoord2fArray, + pxr::SdfValueTypeNames->TexCoord2dArray)) { is_uv = true; } /* In some cases, the st primvar is stored as float2 values. */ @@ -795,7 +797,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, pxr::TfToken interp = p.GetInterpolation(); - if (!(interp == pxr::UsdGeomTokens->faceVarying || interp == pxr::UsdGeomTokens->vertex)) { + if (!(ELEM(interp, pxr::UsdGeomTokens->faceVarying, pxr::UsdGeomTokens->vertex))) { continue; } diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c index 83e55f3bf0d..8aa2a07d071 100644 --- a/source/blender/makesdna/intern/dna_genfile.c +++ b/source/blender/makesdna/intern/dna_genfile.c @@ -525,7 +525,7 @@ static bool init_structDNA(SDNA *sdna, bool do_endian_swap, const char **r_error sdna->pointer_size = sdna->types_size[struct_info->type] / 2; - if (struct_info->members_len != 2 || (sdna->pointer_size != 4 && sdna->pointer_size != 8)) { + if (struct_info->members_len != 2 || (!ELEM(sdna->pointer_size, 4, 8))) { *r_error_message = "ListBase struct error! Needs it to calculate pointerize."; /* well, at least sizeof(ListBase) is error proof! (ton) */ return false; diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 9a8af35109a..f452b0c7091 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -464,7 +464,7 @@ void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, BLI_assert(src_mesh.totedge == edge_map.size()); for (const int i_src : IndexRange(src_mesh.totedge)) { const int i_dst = edge_map[i_src]; - if (i_dst == -1 || i_dst == -2) { + if (ELEM(i_dst, -1, -2)) { continue; } diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 7d90935f678..07ce819e91c 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -1479,7 +1479,7 @@ static void quad_from_tris(BMEdge *e, BMFace *adj[2], BMVert *ndx[4]) ndx[j] = tri[0][i]; /* When the triangle edge cuts across our quad-to-be, * throw in the second triangle's vertex */ - if ((tri[0][i] == e->v1 || tri[0][i] == e->v2) && + if ((ELEM(tri[0][i], e->v1, e->v2)) && (tri[0][(i + 1) % 3] == e->v1 || tri[0][(i + 1) % 3] == e->v2)) { j++; ndx[j] = opp; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index efc2dff48c1..8d5f4855512 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -77,7 +77,7 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) { const float3 a = (p2 - p1).normalized(); const float3 b = (p3 - p1).normalized(); - return (a == b || a == b * -1.0f); + return (ELEM(a, b, b * -1.0f)); } static std::unique_ptr create_point_circle_curve( diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 25f0d355c9e..e293cd9b8fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -202,7 +202,7 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, BLI_assert(src_mesh.totedge == edge_map.size()); for (const int i_src : IndexRange(src_mesh.totedge)) { const int i_dst = edge_map[i_src]; - if (i_dst == -1 || i_dst == -2) { + if (ELEM(i_dst, -1, -2)) { continue; } diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.c index abc2c7008c7..97041b3fdfd 100644 --- a/source/blender/nodes/shader/node_shader_util.c +++ b/source/blender/nodes/shader/node_shader_util.c @@ -40,7 +40,7 @@ static bool sh_fn_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint) { - if (!STREQ(ntree->idname, "ShaderNodeTree") && !STREQ(ntree->idname, "GeometryNodeTree")) { + if (!STR_ELEM(ntree->idname, "ShaderNodeTree", "GeometryNodeTree")) { *r_disabled_hint = "Not a shader or geometry node tree"; return false; } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c index 2d3957d159e..cb4f0594310 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c @@ -183,7 +183,7 @@ static void node_shader_update_principled(bNodeTree *UNUSED(ntree), bNode *node) } } - if (STREQ(sock->name, "Subsurface IOR") || STREQ(sock->name, "Subsurface Anisotropy")) { + if (STR_ELEM(sock->name, "Subsurface IOR", "Subsurface Anisotropy")) { if (sss_method == SHD_SUBSURFACE_BURLEY) { sock->flag |= SOCK_UNAVAIL; } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index 07904123ace..574260f3c36 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -157,7 +157,7 @@ static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node nodeSetSocketAvailability(outWSock, tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && tex->feature != SHD_VORONOI_N_SPHERE_RADIUS && - (tex->dimensions == 1 || tex->dimensions == 4)); + (ELEM(tex->dimensions, 1, 4))); nodeSetSocketAvailability(outRadiusSock, tex->feature == SHD_VORONOI_N_SPHERE_RADIUS); } @@ -211,7 +211,7 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { if (dimensions != 1) { signature.single_output("Position"); } - if ((dimensions == 1 || dimensions == 4)) { + if (ELEM(dimensions, 1, 4)) { signature.single_output("W"); } @@ -547,7 +547,7 @@ class VoronoiMetricFunction : public fn::MultiFunction { if (dimensions != 1) { signature.single_output("Position"); } - if ((dimensions == 1 || dimensions == 4)) { + if (ELEM(dimensions, 1, 4)) { signature.single_output("W"); } @@ -1048,8 +1048,7 @@ static void sh_node_voronoi_build_multi_function(blender::nodes::NodeMultiFuncti NodeTexVoronoi *tex = (NodeTexVoronoi *)node.storage; bool minowski = (tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 && !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); - bool dist_radius = (tex->feature == SHD_VORONOI_DISTANCE_TO_EDGE || - tex->feature == SHD_VORONOI_N_SPHERE_RADIUS); + bool dist_radius = ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS); if (dist_radius) { builder.construct_and_set_matching_fn(tex->dimensions, tex->feature); } diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 8c83f611b5c..1296a6c174c 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -5495,7 +5495,7 @@ static PyObject *pyprop_array_foreach_getset(BPy_PropertyArrayRNA *self, } else { const char f = buf.format ? buf.format[0] : 0; - if ((prop_type == PROP_INT && (buf.itemsize != sizeof(int) || (f != 'l' && f != 'i'))) || + if ((prop_type == PROP_INT && (buf.itemsize != sizeof(int) || (!ELEM(f, 'l', 'i')))) || (prop_type == PROP_FLOAT && (buf.itemsize != sizeof(float) || f != 'f'))) { PyBuffer_Release(&buf); PyErr_Format(PyExc_TypeError, "incorrect sequence item type: %s", buf.format); diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index ce04a143aae..28b91e13d47 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -526,7 +526,7 @@ static PyObject *C_Matrix_Rotation(PyObject *cls, PyObject *args) "cannot create a 2x2 rotation matrix around arbitrary axis"); return NULL; } - if ((matSize == 3 || matSize == 4) && (axis == NULL) && (vec == NULL)) { + if ((ELEM(matSize, 3, 4)) && (axis == NULL) && (vec == NULL)) { PyErr_SetString(PyExc_ValueError, "Matrix.Rotation(): " "axis of rotation for 3d and 4d matrices is required"); diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c index edfa284242c..dfeaec2341e 100644 --- a/source/blender/render/intern/texture_image.c +++ b/source/blender/render/intern/texture_image.c @@ -959,7 +959,7 @@ static void alpha_clip_aniso( /* TXF alpha: we're doing the same alpha-clip here as box-sample, but I'm doubting * if this is actually correct for the all the filtering algorithms. */ - if (!(extflag == TXC_REPT || extflag == TXC_EXTD)) { + if (!(ELEM(extflag, TXC_REPT, TXC_EXTD))) { rf.xmin = minx * (ibuf->x); rf.xmax = maxx * (ibuf->x); rf.ymin = miny * (ibuf->y); diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index 7b3d342b2e4..c742fca0562 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -151,7 +151,7 @@ static float seq_cache_timeline_frame_to_frame_index(Sequence *seq, float timeli /* With raw images, map timeline_frame to strip input media frame range. This means that static * images or extended frame range of movies will only generate one cache entry. No special * treatment in converting frame index to timeline_frame is needed. */ - if (type == SEQ_CACHE_STORE_RAW || type == SEQ_CACHE_STORE_THUMBNAIL) { + if (ELEM(type, SEQ_CACHE_STORE_RAW, SEQ_CACHE_STORE_THUMBNAIL)) { return seq_give_frame_index(seq, timeline_frame); } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 7e8910644b6..dffbea64a30 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -505,8 +505,7 @@ void wm_event_do_notifiers(bContext *C) } } - if (note->window == win || - (note->window == NULL && (note->reference == NULL || note->reference == scene))) { + if (note->window == win || (note->window == NULL && (ELEM(note->reference, NULL, scene)))) { if (note->category == NC_SCENE) { if (note->data == ND_FRAME) { do_anim = true; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index ba347c537ec..b5a606b4298 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_action.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -118,7 +118,7 @@ static wmXrAction *action_create(const char *action_name, action->states = MEM_calloc_arrayN(count, size, "XrAction_States"); action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev"); - const bool is_float_action = (type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT); + const bool is_float_action = ELEM(type, XR_FLOAT_INPUT, XR_VECTOR2F_INPUT); const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT); if (is_float_action) { action->float_thresholds = MEM_calloc_arrayN( From fd7510984a6d472969436721031ae309ec4981a0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 20 Oct 2021 12:10:09 +1100 Subject: [PATCH 0961/1500] CMake: add WITH_BLENDER_THUMBNAILER option Make building the thumbnail extraction executable optional, disable on macOS as this was not linking, further, macOS doesn't use this for thumbnail extraction so it could be left disabled. --- CMakeLists.txt | 10 ++++++++++ build_files/cmake/config/blender_lite.cmake | 1 + source/blender/CMakeLists.txt | 5 ++++- source/creator/CMakeLists.txt | 20 ++++++++++++-------- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 16842f3134b..544e22f342b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,16 @@ get_blender_version() option(WITH_BLENDER "Build blender (disable to build only the blender player)" ON) mark_as_advanced(WITH_BLENDER) +if(APPLE) + # Currently this causes a build error linking, disable. + set(WITH_BLENDER_THUMBNAILER OFF) +elseif(WIN32) + # Building the thumbnail extraction DLL could be made optional. + set(WITH_BLENDER_THUMBNAILER ON) +else() + option(WITH_BLENDER_THUMBNAILER "Build \"blender-thumbnailer\" thumbnail extraction utility" ON) +endif() + option(WITH_INTERNATIONAL "Enable I18N (International fonts and text)" ON) option(WITH_PYTHON "Enable Embedded Python API (only disable for development)" ON) diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake index 0cd886e67d7..89bd46ecd7d 100644 --- a/build_files/cmake/config/blender_lite.cmake +++ b/build_files/cmake/config/blender_lite.cmake @@ -9,6 +9,7 @@ set(WITH_INSTALL_PORTABLE ON CACHE BOOL "" FORCE) set(WITH_ALEMBIC OFF CACHE BOOL "" FORCE) set(WITH_AUDASPACE OFF CACHE BOOL "" FORCE) +set(WITH_BLENDER_THUMBNAILER OFF CACHE BOOL "" FORCE) set(WITH_BOOST OFF CACHE BOOL "" FORCE) set(WITH_BUILDINFO OFF CACHE BOOL "" FORCE) set(WITH_BULLET OFF CACHE BOOL "" FORCE) diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 0a494677d96..c6112344208 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -131,7 +131,10 @@ add_subdirectory(io) add_subdirectory(functions) add_subdirectory(makesdna) add_subdirectory(makesrna) -add_subdirectory(blendthumb) + +if(WITH_BLENDER_THUMBNAILER) + add_subdirectory(blendthumb) +endif() if(WITH_COMPOSITOR) add_subdirectory(compositor) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 45bfc3d6bdb..1d5d1491c7a 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -515,10 +515,12 @@ if(UNIX AND NOT APPLE) DESTINATION "." ) - install( - TARGETS blender-thumbnailer - DESTINATION "." - ) + if(WITH_BLENDER_THUMBNAILER) + install( + TARGETS blender-thumbnailer + DESTINATION "." + ) + endif() if(EXISTS ${LIBDIR}/mesa) install(DIRECTORY ${LIBDIR}/mesa/lib DESTINATION ".") @@ -558,10 +560,12 @@ if(UNIX AND NOT APPLE) FILES ${CMAKE_SOURCE_DIR}/release/freedesktop/icons/symbolic/apps/blender-symbolic.svg DESTINATION share/icons/hicolor/symbolic/apps ) - install( - TARGETS blender-thumbnailer - DESTINATION bin - ) + if(WITH_BLENDER_THUMBNAILER) + install( + TARGETS blender-thumbnailer + DESTINATION bin + ) + endif() set(BLENDER_TEXT_FILES_DESTINATION share/doc/blender) endif() From 6ed93391c485ac3425e4d67981b4e71ba1d0eb5c Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Tue, 19 Oct 2021 23:23:14 -0500 Subject: [PATCH 0962/1500] Fix : Set Curve Handle Position Node Auto Convert When trying to auto convert Vector to Free or Auto to Align, the old handle positions needed to be baked in first. --- .../nodes/geometry/nodes/node_geo_set_curve_handles.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index cb1c57fa476..d7aaaffc7c6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -78,17 +78,21 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode, if (selection[current_mask] == current_point) { if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Vector) { + bezier.ensure_auto_handles(); bezier.handle_types_left()[i] = BezierSpline::HandleType::Free; } else if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Auto) { + bezier.ensure_auto_handles(); bezier.handle_types_left()[i] = BezierSpline::HandleType::Align; } } else { if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Vector) { + bezier.ensure_auto_handles(); bezier.handle_types_right()[i] = BezierSpline::HandleType::Free; } else if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Auto) { + bezier.ensure_auto_handles(); bezier.handle_types_right()[i] = BezierSpline::HandleType::Align; } } From 943debfab5c93719d42c143fcf4a5017d2061816 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 20 Oct 2021 18:01:49 +1100 Subject: [PATCH 0963/1500] View3D: snap with active pivot doesn't need to require a 3D view --- source/blender/editors/space_view3d/view3d_snap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 4482e5897ca..3701b23f9a1 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -326,7 +326,7 @@ static int snap_selected_to_location(bContext *C, int a; if (use_offset) { - if ((v3d && scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) && + if ((scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) && snap_calc_active_center(C, true, center_global)) { /* pass */ } From 2bcf93bbbeb9e32f680c37a1e0054ff16cb00ef0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 20 Oct 2021 18:15:33 +1100 Subject: [PATCH 0964/1500] View3D: expose snap selection as a utility funciton This makes it convenient to position appended objects, see: T92111. --- source/blender/editors/include/ED_view3d.h | 5 + .../editors/space_view3d/view3d_snap.c | 115 ++++++++++++------ 2 files changed, 80 insertions(+), 40 deletions(-) diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 4439ecfdd97..078ebb5e020 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -229,6 +229,11 @@ typedef enum { (V3D_PROJ_TEST_CLIP_CONTENT | V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_FAR | \ V3D_PROJ_TEST_CLIP_WIN) +/* view3d_snap.c */ +bool ED_view3d_snap_selected_to_location(struct bContext *C, + const float snap_target_global[3], + const int pivot_point); + /* view3d_cursor_snap.c */ #define USE_SNAP_DETECT_FROM_KEYMAP_HACK typedef enum { diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 3701b23f9a1..55ec6652495 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -58,7 +58,7 @@ #include "view3d_intern.h" -static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]); +static bool snap_curs_to_sel_ex(bContext *C, const int pivot_point, float r_cursor[3]); static bool snap_calc_active_center(bContext *C, const bool select_only, float r_center[3]); /* -------------------------------------------------------------------- */ @@ -310,9 +310,11 @@ void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot) * and be snapped by the selection pivot point (median, active), * or if every object origin should be snapped to the given location. */ -static int snap_selected_to_location(bContext *C, - const float snap_target_global[3], - const bool use_offset) +static bool snap_selected_to_location(bContext *C, + const float snap_target_global[3], + const bool use_offset, + const int pivot_point, + const bool use_toolsettings) { Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); @@ -326,12 +328,11 @@ static int snap_selected_to_location(bContext *C, int a; if (use_offset) { - if ((scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) && - snap_calc_active_center(C, true, center_global)) { + if ((pivot_point == V3D_AROUND_ACTIVE) && snap_calc_active_center(C, true, center_global)) { /* pass */ } else { - snap_curs_to_sel_ex(C, center_global); + snap_curs_to_sel_ex(C, pivot_point, center_global); } sub_v3_v3v3(offset_global, snap_target_global, center_global); } @@ -341,7 +342,7 @@ static int snap_selected_to_location(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - view_layer, CTX_wm_view3d(C), &objects_len); + view_layer, v3d, &objects_len); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { obedit = objects[ob_index]; @@ -435,18 +436,23 @@ static int snap_selected_to_location(bContext *C, } /* copy new position */ - if ((pchan->protectflag & OB_LOCK_LOCX) == 0) { - pchan->loc[0] = cursor_pose[0]; - } - if ((pchan->protectflag & OB_LOCK_LOCY) == 0) { - pchan->loc[1] = cursor_pose[1]; - } - if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) { - pchan->loc[2] = cursor_pose[2]; - } + if (use_toolsettings) { + if ((pchan->protectflag & OB_LOCK_LOCX) == 0) { + pchan->loc[0] = cursor_pose[0]; + } + if ((pchan->protectflag & OB_LOCK_LOCY) == 0) { + pchan->loc[1] = cursor_pose[1]; + } + if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) { + pchan->loc[2] = cursor_pose[2]; + } - /* auto-keyframing */ - ED_autokeyframe_pchan(C, scene, ob, pchan, ks); + /* auto-keyframing */ + ED_autokeyframe_pchan(C, scene, ob, pchan, ks); + } + else { + copy_v3_v3(pchan->loc, cursor_pose); + } } } @@ -484,9 +490,11 @@ static int snap_selected_to_location(bContext *C, objects_len = BLI_array_len(objects); } - const bool use_transform_skip_children = (scene->toolsettings->transform_flag & + const bool use_transform_skip_children = use_toolsettings && + (scene->toolsettings->transform_flag & SCE_XFORM_SKIP_CHILDREN); - const bool use_transform_data_origin = (scene->toolsettings->transform_flag & + const bool use_transform_data_origin = use_toolsettings && + (scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN); struct XFormObjectSkipChild_Container *xcs = NULL; struct XFormObjectData_Container *xds = NULL; @@ -538,18 +546,23 @@ static int snap_selected_to_location(bContext *C, invert_m3_m3(imat, originmat); mul_m3_v3(imat, cursor_parent); } - if ((ob->protectflag & OB_LOCK_LOCX) == 0) { - ob->loc[0] += cursor_parent[0]; - } - if ((ob->protectflag & OB_LOCK_LOCY) == 0) { - ob->loc[1] += cursor_parent[1]; - } - if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { - ob->loc[2] += cursor_parent[2]; - } + if (use_toolsettings) { + if ((ob->protectflag & OB_LOCK_LOCX) == 0) { + ob->loc[0] += cursor_parent[0]; + } + if ((ob->protectflag & OB_LOCK_LOCY) == 0) { + ob->loc[1] += cursor_parent[1]; + } + if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { + ob->loc[2] += cursor_parent[2]; + } - /* auto-keyframing */ - ED_autokeyframe_object(C, scene, ob, ks); + /* auto-keyframing */ + ED_autokeyframe_object(C, scene, ob, ks); + } + else { + add_v3_v3(ob->loc, cursor_parent); + } DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); } @@ -570,7 +583,21 @@ static int snap_selected_to_location(bContext *C, WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - return OPERATOR_FINISHED; + return true; +} + +bool ED_view3d_snap_selected_to_location(bContext *C, + const float snap_target_global[3], + const int pivot_point) +{ + /* These could be passed as arguments if needed. */ + /* Always use pivot point. */ + const bool use_offset = true; + /* Disable object protected flags & auto-keyframing, + * so this can be used as a low level function. */ + const bool use_toolsettings = false; + return snap_selected_to_location( + C, snap_target_global, use_offset, pivot_point, use_toolsettings); } /** \} */ @@ -586,8 +613,12 @@ static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); const float *snap_target_global = scene->cursor.location; + const int pivot_point = scene->toolsettings->transform_pivot_point; - return snap_selected_to_location(C, snap_target_global, use_offset); + if (snap_selected_to_location(C, snap_target_global, pivot_point, use_offset, true)) { + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; } void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot) @@ -628,7 +659,10 @@ static int snap_selected_to_active_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - return snap_selected_to_location(C, snap_target_global, false); + if (!snap_selected_to_location(C, snap_target_global, -1, false, true)) { + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; } void VIEW3D_OT_snap_selected_to_active(wmOperatorType *ot) @@ -752,7 +786,7 @@ static void bundle_midpoint(Scene *scene, Object *ob, float r_vec[3]) } /** Snaps the 3D cursor location to the median point of the selection. */ -static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]) +static bool snap_curs_to_sel_ex(bContext *C, const int pivot_point, float r_cursor[3]) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); @@ -849,12 +883,12 @@ static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]) return false; } - if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_BOUNDS) { - mid_v3_v3v3(cursor, min, max); + if (pivot_point == V3D_AROUND_CENTER_BOUNDS) { + mid_v3_v3v3(r_cursor, min, max); } else { mul_v3_fl(centroid, 1.0f / (float)count); - copy_v3_v3(cursor, centroid); + copy_v3_v3(r_cursor, centroid); } return true; } @@ -862,7 +896,8 @@ static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]) static int snap_curs_to_sel_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - if (snap_curs_to_sel_ex(C, scene->cursor.location)) { + const int pivot_point = scene->toolsettings->transform_pivot_point; + if (snap_curs_to_sel_ex(C, pivot_point, scene->cursor.location)) { WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); From 25c173ffd12c0b46de4cc9919b8dbcc125f7e5ec Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 19 Oct 2021 16:24:23 +0200 Subject: [PATCH 0965/1500] Tracking: support editing all selected tracks This patch adds a "selected_movieclip_tracks" context member and enables editing properties of multiple selected tracks via the usual Alt-click editing (as well as the "Copy To Selected" operator). Both use UI_context_copy_to_selected_list() to gather a list of other selected items [which are now taken via said new context member]. Strictly speaking, this could be done without the context member as well [just gathering other selected tracks in UI_context_copy_to_selected_list() without relying on a context member], but this might come in handy in other places (e.g. Addons). note: some could be desired for markers (e.g. editing pattern/search areas of all selected track markers, but since this is burried in a uiTemplate, this is a bit more work for another patch). Differential Revision: https://developer.blender.org/D12923 --- doc/python_api/sphinx_doc_gen.py | 1 + .../blender/editors/interface/interface_ops.c | 3 ++ .../blender/editors/screen/screen_context.c | 31 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index ec636036f95..afc84834dd1 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1110,6 +1110,7 @@ context_type_map = { "selected_editable_sequences": ("Sequence", True), "selected_files": ("FileSelectEntry", True), "selected_nla_strips": ("NlaStrip", True), + "selected_movieclip_tracks": ("MovieTrackingTrack", True), "selected_nodes": ("Node", True), "selected_objects": ("Object", True), "selected_pose_bones": ("PoseBone", True), diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 423950d4dbd..69a3cc959c9 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -849,6 +849,9 @@ bool UI_context_copy_to_selected_list(bContext *C, else if (RNA_struct_is_a(ptr->type, &RNA_NlaStrip)) { *r_lb = CTX_data_collection_get(C, "selected_nla_strips"); } + else if (RNA_struct_is_a(ptr->type, &RNA_MovieTrackingTrack)) { + *r_lb = CTX_data_collection_get(C, "selected_movieclip_tracks"); + } else if (RNA_struct_is_a(ptr->type, &RNA_Constraint) && (path_from_bone = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_PoseBone)) != NULL) { diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 3d447d90626..a8c63027254 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -49,11 +49,13 @@ #include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_object.h" +#include "BKE_tracking.h" #include "RNA_access.h" #include "ED_anim_api.h" #include "ED_armature.h" +#include "ED_clip.h" #include "ED_gpencil.h" #include "SEQ_select.h" @@ -99,6 +101,7 @@ const char *screen_context_dir[] = { "active_nla_track", "active_nla_strip", "selected_nla_strips", /* nla editor */ + "selected_movieclip_tracks", "gpencil_data", "gpencil_data_owner", /* grease pencil data */ "annotation_data", @@ -709,6 +712,33 @@ static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContext } return CTX_RESULT_NO_DATA; } +static eContextResult screen_ctx_selected_movieclip_tracks(const bContext *C, + bContextDataResult *result) +{ + SpaceClip *space_clip = CTX_wm_space_clip(C); + if (space_clip == NULL) { + return CTX_RESULT_NO_DATA; + } + MovieClip *clip = ED_space_clip_get_clip(space_clip); + if (clip == NULL) { + return CTX_RESULT_NO_DATA; + } + MovieTracking *tracking = &clip->tracking; + if (tracking == NULL) { + return CTX_RESULT_NO_DATA; + } + + ListBase *tracks_list = BKE_tracking_get_active_tracks(tracking); + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracks_list) { + if (!TRACK_SELECTED(track)) { + continue; + } + CTX_data_list_add(result, &clip->id, &RNA_MovieTrackingTrack, track); + } + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return CTX_RESULT_OK; +} static eContextResult screen_ctx_gpencil_data(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); @@ -1143,6 +1173,7 @@ static void ensure_ed_screen_context_functions(void) register_context_function("active_nla_track", screen_ctx_active_nla_track); register_context_function("active_nla_strip", screen_ctx_active_nla_strip); register_context_function("selected_nla_strips", screen_ctx_selected_nla_strips); + register_context_function("selected_movieclip_tracks", screen_ctx_selected_movieclip_tracks); register_context_function("gpencil_data", screen_ctx_gpencil_data); register_context_function("gpencil_data_owner", screen_ctx_gpencil_data_owner); register_context_function("annotation_data", screen_ctx_annotation_data); From 6cd191a660394042a9df701d7ee9164881fc316b Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 20 Oct 2021 11:56:04 +0200 Subject: [PATCH 0966/1500] Link/Append tests: properly support and test with/without 'recursive' behaviors. This requires adding an extra ('indirect') library to the test cases for append. Aftermath of T92224. --- tests/python/bl_blendfile_liblink.py | 61 +++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index 918c74d17d0..ab26059e944 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -34,7 +34,44 @@ class TestBlendLibLinkHelper(TestHelper): output_dir = self.args.output_dir self.ensure_path(output_dir) # Take care to keep the name unique so multiple test jobs can run at once. - output_lib_path = os.path.join(output_dir, self.unique_blendfile_name("blendlib")) + output_lib_path = os.path.join(output_dir, self.unique_blendfile_name("blendlib_basic")) + + bpy.ops.wm.save_as_mainfile(filepath=output_lib_path, check_existing=False, compress=False) + + return output_lib_path + + def init_lib_data_indirect_lib(self): + output_dir = self.args.output_dir + self.ensure_path(output_dir) + + # Create an indirect library containing a material. + self.reset_blender() + + ma = bpy.data.materials.new("LibMaterial") + ma.use_fake_user = True + # Take care to keep the name unique so multiple test jobs can run at once. + output_lib_path = os.path.join(output_dir, self.unique_blendfile_name("blendlib_indirect_material")) + + bpy.ops.wm.save_as_mainfile(filepath=output_lib_path, check_existing=False, compress=False) + + # Create a main library containing object etc., and linking material from indirect library. + self.reset_blender() + + link_dir = os.path.join(output_lib_path, "Material") + bpy.ops.wm.link(directory=link_dir, filename="LibMaterial") + ma = bpy.data.materials[0] + + me = bpy.data.meshes.new("LibMesh") + me.materials.append(ma) + ob = bpy.data.objects.new("LibMesh", me) + coll = bpy.data.collections.new("LibMesh") + coll.objects.link(ob) + bpy.context.scene.collection.children.link(coll) + + output_dir = self.args.output_dir + self.ensure_path(output_dir) + # Take care to keep the name unique so multiple test jobs can run at once. + output_lib_path = os.path.join(output_dir, self.unique_blendfile_name("blendlib_indirect_main")) bpy.ops.wm.save_as_mainfile(filepath=output_lib_path, check_existing=False, compress=False) @@ -158,7 +195,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): def test_append(self): output_dir = self.args.output_dir - output_lib_path = self.init_lib_data_basic() + output_lib_path = self.init_lib_data_indirect_lib() # Simple append of a single ObData. self.reset_blender() @@ -167,6 +204,11 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): bpy.ops.wm.append(directory=link_dir, filename="LibMesh", instance_object_data=False, set_fake=False, use_recursive=False, do_reuse_local_id=False) + print(bpy.data.materials[:], bpy.data.materials[0].library, bpy.data.materials[0].users, bpy.data.materials[0].use_fake_user) + + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is not None) + assert(bpy.data.materials[0].users == 2) # Fake user is not cleared when linking. assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].use_fake_user is False) @@ -181,6 +223,9 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): bpy.ops.wm.append(directory=link_dir, filename="LibMesh", instance_object_data=True, set_fake=False, use_recursive=False, do_reuse_local_id=False) + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is not None) + assert(bpy.data.materials[0].users == 2) # Fake user is not cleared when linking. assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].use_fake_user is False) @@ -196,6 +241,9 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): bpy.ops.wm.append(directory=link_dir, filename="LibMesh", instance_object_data=False, set_fake=True, use_recursive=False, do_reuse_local_id=False) + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is not None) + assert(bpy.data.materials[0].users == 2) # Fake user is not cleared when linking. assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].use_fake_user is True) @@ -210,6 +258,9 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): bpy.ops.wm.append(directory=link_dir, filename="LibMesh", instance_object_data=False, set_fake=False, use_recursive=False, do_reuse_local_id=False) + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is not None) + assert(bpy.data.materials[0].users == 2) # Fake user is not cleared when linking. assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].users == 1) @@ -225,6 +276,9 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): bpy.ops.wm.append(directory=link_dir, filename="LibMesh", instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False) + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is None) + assert(bpy.data.materials[0].users == 1) # Fake user is cleared when appending. assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].users == 1) @@ -240,6 +294,9 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): bpy.ops.wm.append(directory=link_dir, filename="LibMesh", instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False) + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is None) + assert(bpy.data.materials[0].users == 1) # Fake user is cleared when appending. assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].users == 1) assert(len(bpy.data.objects) == 1) From dfa1c7e554fcc3ddd40780fb8555cdd6e90eaba3 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 21 Nov 2020 16:06:02 +0300 Subject: [PATCH 0967/1500] Split and extend unit tests for vec_roll_to_mat3_normalized. Separate the huge test into huge logical parts and add more cases to check. Also add a utility to check that the matrix is orthogonal, with arbitrary epsilon values and calculations in double. A couple of tests deliberately fail, to be fixed in following commits. Ref D9551 --- .../blenkernel/intern/armature_test.cc | 252 ++++++++++++++---- source/blender/blenlib/BLI_math_matrix.h | 2 + source/blender/blenlib/intern/math_matrix.c | 22 ++ 3 files changed, 222 insertions(+), 54 deletions(-) diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 2994563175f..8ebb91ffc74 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -30,6 +30,36 @@ namespace blender::bke::tests { static const float FLOAT_EPSILON = 1.2e-7; +static const float SCALE_EPSILON = 3.71e-5; +static const float ORTHO_EPSILON = 5e-5; + +/** Test that the matrix is orthogonal, i.e. has no scale or shear within acceptable precision. */ +static double EXPECT_M3_ORTHOGONAL(const float mat[3][3], + double epsilon_scale, + double epsilon_ortho) +{ + /* Do the checks in double precision to avoid precision issues in the checks themselves. */ + double dmat[3][3]; + copy_m3d_m3(dmat, mat); + + /* Check individual axis scaling. */ + EXPECT_NEAR(len_v3_db(dmat[0]), 1.0, epsilon_scale); + EXPECT_NEAR(len_v3_db(dmat[1]), 1.0, epsilon_scale); + EXPECT_NEAR(len_v3_db(dmat[2]), 1.0, epsilon_scale); + + /* Check orthogonality. */ + EXPECT_NEAR(dot_v3v3_db(dmat[0], dmat[1]), 0.0, epsilon_ortho); + EXPECT_NEAR(dot_v3v3_db(dmat[0], dmat[2]), 0.0, epsilon_ortho); + EXPECT_NEAR(dot_v3v3_db(dmat[1], dmat[2]), 0.0, epsilon_ortho); + + /* Check determinant to detect flipping and as a secondary volume change check. */ + double determinant = determinant_m3_array_db(dmat); + + EXPECT_NEAR(determinant, 1.0, epsilon_ortho); + + return determinant; +} + TEST(mat3_vec_to_roll, UnitMatrix) { float unit_matrix[3][3]; @@ -93,71 +123,185 @@ TEST(mat3_vec_to_roll, Rotationmatrix) } } -TEST(vec_roll_to_mat3_normalized, Rotationmatrix) +/** Generic function to test vec_roll_to_mat3_normalized. */ +static double test_vec_roll_to_mat3_normalized(const float input[3], + float roll, + const float expected_roll_mat[3][3], + bool normalize = true) { - float negative_y_axis[3][3]; - unit_m3(negative_y_axis); - negative_y_axis[0][0] = negative_y_axis[1][1] = -1.0f; - - const float roll = 0.0f; + float input_normalized[3]; float roll_mat[3][3]; + if (normalize) { + /* The vector is renormalized to replicate the actual usage. */ + normalize_v3_v3(input_normalized, input); + } + else { + copy_v3_v3(input_normalized, input); + } + + vec_roll_to_mat3_normalized(input_normalized, roll, roll_mat); + + EXPECT_V3_NEAR(roll_mat[1], input_normalized, FLT_EPSILON); + + if (expected_roll_mat) { + EXPECT_M3_NEAR(roll_mat, expected_roll_mat, FLT_EPSILON); + } + + return EXPECT_M3_ORTHOGONAL(roll_mat, SCALE_EPSILON, ORTHO_EPSILON); +} + +/** Binary search to test where the code switches to the most degenerate special case. */ +static double find_flip_boundary(double x, double z) +{ + /* Irrational scale factor to ensure values aren't 'nice', have a lot of rounding errors, + * and can't accidentally produce the exact result returned by the special case. */ + const double scale = M_1_PI / 10; + double theta = x * x + z * z; + double minv = 0, maxv = 1e-2; + + while (maxv - minv > FLT_EPSILON * 1e-3) { + double mid = (minv + maxv) / 2; + + float roll_mat[3][3]; + float input[3] = {float(x * mid * scale), + -float(sqrt(1 - theta * mid * mid) * scale), + float(z * mid * scale)}; + + normalize_v3(input); + vec_roll_to_mat3_normalized(input, 0, roll_mat); + + /* The special case assigns exact constants rather than computing. */ + if (roll_mat[0][0] == -1 && roll_mat[0][1] == 0 && roll_mat[2][1] == 0) { + minv = mid; + } + else { + maxv = mid; + } + } + + return sqrt(theta) * (minv + maxv) * 0.5; +} + +TEST(vec_roll_to_mat3_normalized, FlippedBoundary1) +{ + EXPECT_NEAR(find_flip_boundary(0, 1), 2.40e-4, 0.01e-4); +} + +TEST(vec_roll_to_mat3_normalized, FlippedBoundary2) +{ + EXPECT_NEAR(find_flip_boundary(1, 1), 3.39e-4, 0.01e-4); +} + +/* Test cases close to the -Y axis. */ +TEST(vec_roll_to_mat3_normalized, Flipped1) +{ /* If normalized_vector is -Y, simple symmetry by Z axis. */ - { - const float normalized_vector[3] = {0.0f, -1.0f, 0.0f}; - vec_roll_to_mat3_normalized(normalized_vector, roll, roll_mat); - EXPECT_M3_NEAR(roll_mat, negative_y_axis, FLT_EPSILON); - } + const float input[3] = {0.0f, -1.0f, 0.0f}; + const float expected_roll_mat[3][3] = { + {-1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat, false); +} - /* If normalized_vector is far enough from -Y, apply the general case. */ - { - const float expected_roll_mat[3][3] = {{1.000000f, 0.000000f, 0.000000f}, - {0.000000f, -0.999989986f, -0.000000f}, - {0.000000f, 0.000000f, 1.000000f}}; - - const float normalized_vector[3] = {0.0f, -1.0f + 1e-5f, 0.0f}; - vec_roll_to_mat3_normalized(normalized_vector, roll, roll_mat); - EXPECT_M3_NEAR(roll_mat, expected_roll_mat, FLT_EPSILON); - } - -#if 0 - /* TODO: This test will pass after fixing T82455) */ +TEST(vec_roll_to_mat3_normalized, Flipped2) +{ /* If normalized_vector is close to -Y and - * it has X and Z values above a threshold, - * apply the special case. */ - { - const float expected_roll_mat[3][3] = {{0.000000f, -9.99999975e-06f, 1.000000f}, - {9.99999975e-06f, -0.999999881f, 9.99999975e-06f}, - {1.000000f, -9.99999975e-06, 0.000000f}}; - const float normalized_vector[3] = {1e-24, -0.999999881, 0}; - vec_roll_to_mat3_normalized(normalized_vector, roll, roll_mat); - EXPECT_M3_NEAR(roll_mat, expected_roll_mat, FLT_EPSILON); - } -#endif + * it has X and Z values below a threshold, + * simple symmetry by Z axis. */ + const float input[3] = {1e-24, -0.999999881, 0}; + const float expected_roll_mat[3][3] = { + {-1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat, false); +} +TEST(vec_roll_to_mat3_normalized, Flipped3) +{ /* If normalized_vector is in a critical range close to -Y, apply the special case. */ - { - const float expected_roll_mat[3][3] = {{0.000000f, -9.99999975e-06f, 1.000000f}, - {9.99999975e-06f, -0.999999881f, 9.99999975e-06f}, - {1.000000f, -9.99999975e-06f, 0.000000f}}; + const float input[3] = {2.5e-4f, -0.999999881f, 2.5e-4f}; /* Corner Case. */ + const float expected_roll_mat[3][3] = {{0.000000f, -2.5e-4f, 1.000000f}, + {2.5e-4f, -0.999999881f, 2.5e-4f}, + {1.000000f, -2.5e-4f, 0.000000f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat, false); +} - const float normalized_vector[3] = {1e-5f, -0.999999881f, 1e-5f}; /* Corner Case. */ - vec_roll_to_mat3_normalized(normalized_vector, roll, roll_mat); - EXPECT_M3_NEAR(roll_mat, expected_roll_mat, FLT_EPSILON); - } +/* Test 90 degree rotations. */ +TEST(vec_roll_to_mat3_normalized, Rotate90_Z_CW) +{ + /* Rotate 90 around Z. */ + const float input[3] = {1, 0, 0}; + const float expected_roll_mat[3][3] = {{0, -1, 0}, {1, 0, 0}, {0, 0, 1}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} - /* If normalized_vector is far enough from -Y, apply the general case. */ - { - const float expected_roll_mat[3][3] = {{0.788675129f, -0.577350259f, -0.211324856f}, - {0.577350259f, 0.577350259f, 0.577350259f}, - {-0.211324856f, -0.577350259f, 0.788675129f}}; +TEST(vec_roll_to_mat3_normalized, Rotate90_Z_CCW) +{ + /* Rotate 90 around Z. */ + const float input[3] = {-1, 0, 0}; + const float expected_roll_mat[3][3] = {{0, 1, 0}, {-1, 0, 0}, {0, 0, 1}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} - const float vector[3] = {1.0f, 1.0f, 1.0f}; /* Arbitrary Value. */ - float normalized_vector[3]; - normalize_v3_v3(normalized_vector, vector); - vec_roll_to_mat3_normalized(normalized_vector, roll, roll_mat); - EXPECT_M3_NEAR(roll_mat, expected_roll_mat, FLT_EPSILON); - } +TEST(vec_roll_to_mat3_normalized, Rotate90_X_CW) +{ + /* Rotate 90 around X. */ + const float input[3] = {0, 0, -1}; + const float expected_roll_mat[3][3] = {{1, 0, 0}, {0, 0, -1}, {0, 1, 0}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +TEST(vec_roll_to_mat3_normalized, Rotate90_X_CCW) +{ + /* Rotate 90 around X. */ + const float input[3] = {0, 0, 1}; + const float expected_roll_mat[3][3] = {{1, 0, 0}, {0, 0, 1}, {0, -1, 0}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +/* Test the general case when the vector is far enough from -Y. */ +TEST(vec_roll_to_mat3_normalized, Generic1) +{ + const float input[3] = {1.0f, 1.0f, 1.0f}; /* Arbitrary Value. */ + const float expected_roll_mat[3][3] = {{0.788675129f, -0.577350259f, -0.211324856f}, + {0.577350259f, 0.577350259f, 0.577350259f}, + {-0.211324856f, -0.577350259f, 0.788675129f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +TEST(vec_roll_to_mat3_normalized, Generic2) +{ + const float input[3] = {1.0f, -1.0f, 1.0f}; /* Arbitrary Value. */ + const float expected_roll_mat[3][3] = {{0.211324856f, -0.577350259f, -0.788675129f}, + {0.577350259f, -0.577350259f, 0.577350259f}, + {-0.788675129f, -0.577350259f, 0.211324856f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +TEST(vec_roll_to_mat3_normalized, Generic3) +{ + const float input[3] = {-1.0f, -1.0f, 1.0f}; /* Arbitrary Value. */ + const float expected_roll_mat[3][3] = {{0.211324856f, 0.577350259f, 0.788675129f}, + {-0.577350259f, -0.577350259f, 0.577350259f}, + {0.788675129f, -0.577350259f, 0.211324856f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +TEST(vec_roll_to_mat3_normalized, Generic4) +{ + const float input[3] = {-1.0f, -1.0f, -1.0f}; /* Arbitrary Value. */ + const float expected_roll_mat[3][3] = {{0.211324856f, 0.577350259f, -0.788675129f}, + {-0.577350259f, -0.577350259f, -0.577350259f}, + {-0.788675129f, 0.577350259f, 0.211324856f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +/* Test roll. */ +TEST(vec_roll_to_mat3_normalized, Roll1) +{ + const float input[3] = {1.0f, 1.0f, 1.0f}; /* Arbitrary Value. */ + const float expected_roll_mat[3][3] = {{0.211324856f, 0.577350259f, -0.788675129f}, + {0.577350259f, 0.577350259f, 0.577350259f}, + {0.788675129f, -0.577350259f, -0.211324856f}}; + test_vec_roll_to_mat3_normalized(input, float(M_PI * 0.5), expected_roll_mat); } class BKE_armature_find_selected_bones_test : public testing::Test { diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 2b0c3db21ee..241acebffa3 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -57,6 +57,7 @@ void copy_m4_m4_db(double m1[4][4], const double m2[4][4]); void copy_m3_m3d(float m1[3][3], const double m2[3][3]); /* float->double */ +void copy_m3d_m3(double m1[3][3], const float m2[3][3]); void copy_m4d_m4(double m1[4][4], const float m2[4][4]); void swap_m3m3(float m1[3][3], float m2[3][3]); @@ -291,6 +292,7 @@ float determinant_m3( float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3); float determinant_m3_array(const float m[3][3]); float determinant_m4_mat3_array(const float m[4][4]); +double determinant_m3_array_db(const double m[3][3]); float determinant_m4(const float m[4][4]); #define PSEUDOINVERSE_EPSILON 1e-8f diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index 554506e90e7..b6d80d76be1 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -180,6 +180,21 @@ void copy_m4_m2(float m1[4][4], const float m2[2][2]) m1[3][3] = 1.0f; } +void copy_m3d_m3(double m1[3][3], const float m2[3][3]) +{ + m1[0][0] = m2[0][0]; + m1[0][1] = m2[0][1]; + m1[0][2] = m2[0][2]; + + m1[1][0] = m2[1][0]; + m1[1][1] = m2[1][1]; + m1[1][2] = m2[1][2]; + + m1[2][0] = m2[2][0]; + m1[2][1] = m2[2][1]; + m1[2][2] = m2[2][2]; +} + void copy_m4d_m4(double m1[4][4], const float m2[4][4]) { m1[0][0] = m2[0][0]; @@ -1113,6 +1128,13 @@ float determinant_m4_mat3_array(const float m[4][4]) m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1])); } +double determinant_m3_array_db(const double m[3][3]) +{ + return (m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) - + m[1][0] * (m[0][1] * m[2][2] - m[0][2] * m[2][1]) + + m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1])); +} + bool invert_m3_ex(float m[3][3], const float epsilon) { float tmp[3][3]; From df445cc571bd1cf7fab4c5c8474f5e185a757fe2 Mon Sep 17 00:00:00 2001 From: Gaia Clary Date: Sat, 21 Nov 2020 13:28:44 +0300 Subject: [PATCH 0968/1500] Fix T82455: vec_roll_to_mat3_normalized returns NaN when nor close to -Y. In this case theta is completely unsafe to use, so a different threshold based on x and z has to be used to avoid division by zero. Ref D9551 --- source/blender/blenkernel/intern/armature.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index a60cba3c892..a266718dcfc 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2240,15 +2240,16 @@ void mat3_vec_to_roll(const float mat[3][3], const float vec[3], float *r_roll) */ void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_mat[3][3]) { - const float THETA_SAFE = 1.0e-5f; /* theta above this value are always safe to use. */ - const float THETA_CRITICAL = 1.0e-9f; /* above this is safe under certain conditions. */ + const float SAFE_THRESHOLD = 1.0e-5f; /* theta above this value has good enough precision. */ + const float CRITICAL_THRESHOLD = 1.0e-9f; /* above this is safe under certain conditions. */ + const float THRESHOLD_SQUARED = CRITICAL_THRESHOLD * CRITICAL_THRESHOLD; const float x = nor[0]; const float y = nor[1]; const float z = nor[2]; - const float theta = 1.0f + y; - const float theta_alt = x * x + z * z; + const float theta = 1.0f + y; /* remapping Y from [-1,+1] to [0,2]. */ + const float theta_alt = x * x + z * z; /* Helper value for matrix calculations.*/ float rMatrix[3][3], bMatrix[3][3]; BLI_ASSERT_UNIT_V3(nor); @@ -2258,10 +2259,8 @@ void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_m * Also, due to float precision errors, nor can be (0.0, -0.99999994, 0.0) which results * in theta being close to zero. This will cause problems when theta is used as divisor. */ - if (theta > THETA_SAFE || ((x || z) && theta > THETA_CRITICAL)) { - /* nor is *not* aligned to negative Y-axis (0,-1,0). - * We got these values for free... so be happy with it... ;) - */ + if (theta > SAFE_THRESHOLD || (theta > CRITICAL_THRESHOLD && theta_alt > THRESHOLD_SQUARED)) { + /* nor is *not* aligned to negative Y-axis (0,-1,0). */ bMatrix[0][1] = -x; bMatrix[1][0] = x; @@ -2269,7 +2268,7 @@ void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_m bMatrix[1][2] = z; bMatrix[2][1] = -z; - if (theta > THETA_SAFE) { + if (theta > SAFE_THRESHOLD) { /* nor differs significantly from negative Y axis (0,-1,0): apply the general case. */ bMatrix[0][0] = 1 - x * x / theta; bMatrix[2][2] = 1 - z * z / theta; From 16eafdadf6040fb84bacf657ac0bf16a78e1057e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 21 Nov 2020 21:45:14 +0300 Subject: [PATCH 0969/1500] Fix precision issues and a bug in vec_roll_to_mat3_normalized. When the input vector gets close to -Y, y and theta becomes totally unreliable. It is thus necessary to compute the result in a different way based on x and z. The code already had a special case, but: - The threshold for using the special case was way too low. - The special case was not precise enough to extend the threshold. - The special case math had a sign error, resulting in a jump. This adds tests for the computation precision and fixes the issues by adjusting the threshold, and replacing the special case with one based on a quadratic Taylor expansion of sqrt instead of linear. Replacing the special case fixes the bug and results in a compatibility break, requiring versioning for the roll of affected bones. Differential Revision: https://developer.blender.org/D9551 --- .../blender/blenkernel/BKE_blender_version.h | 4 +- source/blender/blenkernel/intern/armature.c | 45 +++--- .../blenkernel/intern/armature_test.cc | 67 ++++++++- .../blenloader/intern/versioning_300.c | 138 ++++++++++++++++-- 4 files changed, 216 insertions(+), 38 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 5ee86f9446e..899d21683e4 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,13 +39,13 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 35 +#define BLENDER_FILE_SUBVERSION 36 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file * was written with too new a version. */ #define BLENDER_FILE_MIN_VERSION 300 -#define BLENDER_FILE_MIN_SUBVERSION 26 +#define BLENDER_FILE_MIN_SUBVERSION 36 /** User readable version string. */ const char *BKE_blender_version_string(void); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index a266718dcfc..0fa4c6e47e8 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2227,39 +2227,47 @@ void mat3_vec_to_roll(const float mat[3][3], const float vec[3], float *r_roll) *
* * When y is close to -1, computing 1 / (1 + y) will cause severe numerical instability, - * so we ignore it and normalize M instead. + * so we use a different approach based on x and z as inputs. * We know `y^2 = 1 - (x^2 + z^2)`, and `y < 0`, hence `y = -sqrt(1 - (x^2 + z^2))`. * - * Since x and z are both close to 0, we apply the binomial expansion to the first order: - * `y = -sqrt(1 - (x^2 + z^2)) = -1 + (x^2 + z^2) / 2`. Which gives: + * Since x and z are both close to 0, we apply the binomial expansion to the second order: + * `y = -sqrt(1 - (x^2 + z^2)) = -1 + (x^2 + z^2) / 2 + (x^2 + z^2)^2 / 8`, which allows + * eliminating the problematic `1` constant. + * + * A first order expansion allows simplifying to this, but second order is more precise: *
  *                        ┌  z^2 - x^2,  -2 * x * z ┐
  * M* = 1 / (x^2 + z^2) * │                         │
  *                        └ -2 * x * z,   x^2 - z^2 ┘
  * 
+ * + * P.S. In the end, this basically is a heavily optimized version of Damped Track +Y. */ void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_mat[3][3]) { - const float SAFE_THRESHOLD = 1.0e-5f; /* theta above this value has good enough precision. */ - const float CRITICAL_THRESHOLD = 1.0e-9f; /* above this is safe under certain conditions. */ + const float SAFE_THRESHOLD = 6.1e-3f; /* theta above this value has good enough precision. */ + const float CRITICAL_THRESHOLD = 2.5e-4f; /* true singularity if xz distance is below this. */ const float THRESHOLD_SQUARED = CRITICAL_THRESHOLD * CRITICAL_THRESHOLD; const float x = nor[0]; const float y = nor[1]; const float z = nor[2]; - const float theta = 1.0f + y; /* remapping Y from [-1,+1] to [0,2]. */ - const float theta_alt = x * x + z * z; /* Helper value for matrix calculations.*/ + float theta = 1.0f + y; /* remapping Y from [-1,+1] to [0,2]. */ + const float theta_alt = x * x + z * z; /* squared distance from origin in x,z plane. */ float rMatrix[3][3], bMatrix[3][3]; BLI_ASSERT_UNIT_V3(nor); - /* When theta is close to zero (nor is aligned close to negative Y Axis), + /* Determine if the input is far enough from the true singularity of this type of + * transformation at (0,-1,0), where roll becomes 0/0 undefined without a limit. + * + * When theta is close to zero (nor is aligned close to negative Y Axis), * we have to check we do have non-null X/Z components as well. * Also, due to float precision errors, nor can be (0.0, -0.99999994, 0.0) which results * in theta being close to zero. This will cause problems when theta is used as divisor. */ - if (theta > SAFE_THRESHOLD || (theta > CRITICAL_THRESHOLD && theta_alt > THRESHOLD_SQUARED)) { + if (theta > SAFE_THRESHOLD || theta_alt > THRESHOLD_SQUARED) { /* nor is *not* aligned to negative Y-axis (0,-1,0). */ bMatrix[0][1] = -x; @@ -2268,18 +2276,15 @@ void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_m bMatrix[1][2] = z; bMatrix[2][1] = -z; - if (theta > SAFE_THRESHOLD) { - /* nor differs significantly from negative Y axis (0,-1,0): apply the general case. */ - bMatrix[0][0] = 1 - x * x / theta; - bMatrix[2][2] = 1 - z * z / theta; - bMatrix[2][0] = bMatrix[0][2] = -x * z / theta; - } - else { - /* nor is close to negative Y axis (0,-1,0): apply the special case. */ - bMatrix[0][0] = (x + z) * (x - z) / -theta_alt; - bMatrix[2][2] = -bMatrix[0][0]; - bMatrix[2][0] = bMatrix[0][2] = 2.0f * x * z / theta_alt; + if (theta <= SAFE_THRESHOLD) { + /* When nor is close to negative Y axis (0,-1,0) the theta precision is very bad, + * so recompute it from x and z instead, using the series expansion for sqrt. */ + theta = theta_alt * 0.5f + theta_alt * theta_alt * 0.125f; } + + bMatrix[0][0] = 1 - x * x / theta; + bMatrix[2][2] = 1 - z * z / theta; + bMatrix[2][0] = bMatrix[0][2] = -x * z / theta; } else { /* nor is very close to negative Y axis (0,-1,0): use simple symmetry by Z axis. */ diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 8ebb91ffc74..3d22351e9a6 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -185,12 +185,12 @@ static double find_flip_boundary(double x, double z) TEST(vec_roll_to_mat3_normalized, FlippedBoundary1) { - EXPECT_NEAR(find_flip_boundary(0, 1), 2.40e-4, 0.01e-4); + EXPECT_NEAR(find_flip_boundary(0, 1), 2.50e-4, 0.01e-4); } TEST(vec_roll_to_mat3_normalized, FlippedBoundary2) { - EXPECT_NEAR(find_flip_boundary(1, 1), 3.39e-4, 0.01e-4); + EXPECT_NEAR(find_flip_boundary(1, 1), 2.50e-4, 0.01e-4); } /* Test cases close to the -Y axis. */ @@ -218,9 +218,9 @@ TEST(vec_roll_to_mat3_normalized, Flipped3) { /* If normalized_vector is in a critical range close to -Y, apply the special case. */ const float input[3] = {2.5e-4f, -0.999999881f, 2.5e-4f}; /* Corner Case. */ - const float expected_roll_mat[3][3] = {{0.000000f, -2.5e-4f, 1.000000f}, + const float expected_roll_mat[3][3] = {{0.000000f, -2.5e-4f, -1.000000f}, {2.5e-4f, -0.999999881f, 2.5e-4f}, - {1.000000f, -2.5e-4f, 0.000000f}}; + {-1.000000f, -2.5e-4f, 0.000000f}}; test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat, false); } @@ -304,6 +304,65 @@ TEST(vec_roll_to_mat3_normalized, Roll1) test_vec_roll_to_mat3_normalized(input, float(M_PI * 0.5), expected_roll_mat); } +/** Test that the matrix is orthogonal for an input close to -Y. */ +static double test_vec_roll_to_mat3_orthogonal(double s, double x, double z) +{ + const float input[3] = {float(x), float(s * sqrt(1 - x * x - z * z)), float(z)}; + + return test_vec_roll_to_mat3_normalized(input, 0.0f, NULL); +} + +/** Test that the matrix is orthogonal for a range of inputs close to -Y. */ +static void test_vec_roll_to_mat3_orthogonal(double s, double x1, double x2, double y1, double y2) +{ + const int count = 5000; + double delta = 0; + double tmax = 0; + + for (int i = 0; i <= count; i++) { + double t = double(i) / count; + double det = test_vec_roll_to_mat3_orthogonal(s, interpd(x2, x1, t), interpd(y2, y1, t)); + + /* Find and report maximum error in the matrix determinant. */ + double curdelta = abs(det - 1); + if (curdelta > delta) { + delta = curdelta; + tmax = t; + } + } + + printf(" Max determinant error %.10f at %f.\n", delta, tmax); +} + +#define TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(name, s, x1, x2, y1, y2) \ + TEST(vec_roll_to_mat3_normalized, name) \ + { \ + test_vec_roll_to_mat3_orthogonal(s, x1, x2, y1, y2); \ + } + +/* Moving from -Y towards X. */ +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_005, -1, 0, 0, 3e-4, 0.005) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_010, -1, 0, 0, 0.005, 0.010) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_050, -1, 0, 0, 0.010, 0.050) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_100, -1, 0, 0, 0.050, 0.100) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_200, -1, 0, 0, 0.100, 0.200) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_300, -1, 0, 0, 0.200, 0.300) + +/* Moving from -Y towards X and Y. */ +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_005_005, -1, 3e-4, 0.005, 3e-4, 0.005) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_010_010, -1, 0.005, 0.010, 0.005, 0.010) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_050_050, -1, 0.010, 0.050, 0.010, 0.050) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_100_100, -1, 0.050, 0.100, 0.050, 0.100) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_200_200, -1, 0.100, 0.200, 0.100, 0.200) + +/* Moving from +Y towards X. */ +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoP_000_005, 1, 0, 0, 0, 0.005) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoP_000_100, 1, 0, 0, 0.005, 0.100) + +/* Moving from +Y towards X and Y. */ +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoP_005_005, 1, 0, 0.005, 0, 0.005) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoP_100_100, 1, 0.005, 0.100, 0.005, 0.100) + class BKE_armature_find_selected_bones_test : public testing::Test { protected: bArmature arm; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 4c6b70982a4..6b57bdf9a9c 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -47,6 +47,7 @@ #include "BKE_action.h" #include "BKE_animsys.h" +#include "BKE_armature.h" #include "BKE_asset.h" #include "BKE_collection.h" #include "BKE_deform.h" @@ -1097,6 +1098,112 @@ static void version_geometry_nodes_add_attribute_input_settings(NodesModifierDat } } +/* Copy of the function before the fixes. */ +static void legacy_vec_roll_to_mat3_normalized(const float nor[3], + const float roll, + float r_mat[3][3]) +{ + const float SAFE_THRESHOLD = 1.0e-5f; /* theta above this value has good enough precision. */ + const float CRITICAL_THRESHOLD = 1.0e-9f; /* above this is safe under certain conditions. */ + const float THRESHOLD_SQUARED = CRITICAL_THRESHOLD * CRITICAL_THRESHOLD; + + const float x = nor[0]; + const float y = nor[1]; + const float z = nor[2]; + + const float theta = 1.0f + y; /* remapping Y from [-1,+1] to [0,2]. */ + const float theta_alt = x * x + z * z; /* Helper value for matrix calculations.*/ + float rMatrix[3][3], bMatrix[3][3]; + + BLI_ASSERT_UNIT_V3(nor); + + /* When theta is close to zero (nor is aligned close to negative Y Axis), + * we have to check we do have non-null X/Z components as well. + * Also, due to float precision errors, nor can be (0.0, -0.99999994, 0.0) which results + * in theta being close to zero. This will cause problems when theta is used as divisor. + */ + if (theta > SAFE_THRESHOLD || (theta > CRITICAL_THRESHOLD && theta_alt > THRESHOLD_SQUARED)) { + /* nor is *not* aligned to negative Y-axis (0,-1,0). */ + + bMatrix[0][1] = -x; + bMatrix[1][0] = x; + bMatrix[1][1] = y; + bMatrix[1][2] = z; + bMatrix[2][1] = -z; + + if (theta > SAFE_THRESHOLD) { + /* nor differs significantly from negative Y axis (0,-1,0): apply the general case. */ + bMatrix[0][0] = 1 - x * x / theta; + bMatrix[2][2] = 1 - z * z / theta; + bMatrix[2][0] = bMatrix[0][2] = -x * z / theta; + } + else { + /* nor is close to negative Y axis (0,-1,0): apply the special case. */ + bMatrix[0][0] = (x + z) * (x - z) / -theta_alt; + bMatrix[2][2] = -bMatrix[0][0]; + bMatrix[2][0] = bMatrix[0][2] = 2.0f * x * z / theta_alt; + } + } + else { + /* nor is very close to negative Y axis (0,-1,0): use simple symmetry by Z axis. */ + unit_m3(bMatrix); + bMatrix[0][0] = bMatrix[1][1] = -1.0; + } + + /* Make Roll matrix */ + axis_angle_normalized_to_mat3(rMatrix, nor, roll); + + /* Combine and output result */ + mul_m3_m3m3(r_mat, rMatrix, bMatrix); +} + +static void correct_bone_roll_value(const float head[3], + const float tail[3], + const float check_x_axis[3], + const float check_y_axis[3], + float *r_roll) +{ + const float SAFE_THRESHOLD = 1.0e-5f; + float vec[3], bone_mat[3][3], vec2[3]; + + /* Compute the Y axis vector. */ + sub_v3_v3v3(vec, tail, head); + normalize_v3(vec); + + /* Only correct when in the danger zone. */ + if (1.0f + vec[1] < SAFE_THRESHOLD * 2 && (vec[0] || vec[2])) { + /* Use the armature matrix to double-check if adjustment is needed. + * This should minimize issues if the file is bounced back and forth between + * 2.92 and 2.91, provided Edit Mode isn't entered on the armature in 2.91. */ + vec_roll_to_mat3(vec, *r_roll, bone_mat); + + BLI_assert(dot_v3v3(bone_mat[1], check_y_axis) > 0.999f); + + if (dot_v3v3(bone_mat[0], check_x_axis) < 0.999f) { + /* Recompute roll using legacy code to interpret the old value. */ + legacy_vec_roll_to_mat3_normalized(vec, *r_roll, bone_mat); + mat3_to_vec_roll(bone_mat, vec2, r_roll); + BLI_assert(compare_v3v3(vec, vec2, FLT_EPSILON)); + } + } +} + +/* Update the armature Bone roll fields for bones very close to -Y direction. */ +static void do_version_bones_roll(ListBase *lb) +{ + LISTBASE_FOREACH (Bone *, bone, lb) { + /* Parent-relative orientation (used for posing). */ + correct_bone_roll_value( + bone->head, bone->tail, bone->bone_mat[0], bone->bone_mat[1], &bone->roll); + + /* Absolute orientation (used for Edit mode). */ + correct_bone_roll_value( + bone->arm_head, bone->arm_tail, bone->arm_mat[0], bone->arm_mat[1], &bone->arm_roll); + + do_version_bones_roll(&bone->childbase); + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -1839,18 +1946,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 300, 36)) { /* Update the `idnames` for renamed geometry and function nodes. */ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type != NTREE_GEOMETRY) { @@ -1871,5 +1967,23 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) version_node_id(ntree, GEO_NODE_SET_MATERIAL, "GeometryNodeSetMaterial"); version_node_id(ntree, GEO_NODE_SPLIT_EDGES, "GeometryNodeSplitEdges"); } + + /* Update bone roll after a fix to vec_roll_to_mat3_normalized. */ + LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) { + do_version_bones_roll(&arm->bonebase); + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } From 381965eb568932b311fa23b7d8b70a7d6c1070dc Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 20 Oct 2021 11:49:33 +0200 Subject: [PATCH 0970/1500] UI: Activate parent when active child is collapsed Previously, when an item was active and its parent (or grand parent, etc.) was collapsed, the active item would simply not be visible anymore. It seemed like there was no active item. So instead, change the just collapsed parent to be the active item then, so the active item stays visible. --- source/blender/editors/include/UI_tree_view.hh | 2 ++ source/blender/editors/interface/tree_view.cc | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index ae85375ed2f..b1ec22c57a6 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -337,6 +337,8 @@ class AbstractTreeViewItem : public TreeViewItemContainer { void add_indent(uiLayout &row) const; void add_collapse_chevron(uiBlock &block) const; void add_rename_button(uiLayout &row); + + bool has_active_child() const; }; /** \} */ diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 3f3a8c5bce5..cf3ddcc3fff 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -227,6 +227,11 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, BLI_assert(hovered_item != nullptr); hovered_item->toggle_collapsed(); + /* When collapsing an item with an active child, make this collapsed item active instead so the + * active item stays visible. */ + if (hovered_item->has_active_child()) { + hovered_item->activate(); + } } bool AbstractTreeViewItem::is_collapse_chevron_but(const uiBut *but) @@ -327,6 +332,18 @@ void AbstractTreeViewItem::add_rename_button(uiLayout &row) UI_block_layout_set_current(block, &row); } +bool AbstractTreeViewItem::has_active_child() const +{ + bool found = false; + foreach_item_recursive([&found](const AbstractTreeViewItem &item) { + if (item.is_active()) { + found = true; + } + }); + + return found; +} + void AbstractTreeViewItem::on_activate() { /* Do nothing by default. */ From dd728e15396eadc2d6f8dcb307343003d0050cfb Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 20 Oct 2021 11:54:09 +0200 Subject: [PATCH 0971/1500] Asset Browser: UI polish for the asset metadata sidebar * Show asset path in a (read only) text button. Makes it possible to see the full path in the tooltip, brings support for copying the path and integrates better with the sourrounding layout. Previous label needed lots of space to show the full path without clipping. * Remove "Details" panel, it only contained one item (description). That is moved next to the name and asset path button. * Use property split layout for name source and description buttons. Now that there are multiple buttons, it's better to have a label for them. * Always show operators for asset previews, just gray them out if not applicable instead of hiding. Keeps the layout consistent and graying out is less confusing than hiding UI elements. --- release/scripts/startup/bl_ui/__init__.py | 2 + .../startup/bl_ui/space_filebrowser.py | 71 +++++++++++-------- 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 25484e905c3..1fb40ad8bc8 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -117,6 +117,8 @@ def register(): for cls in mod.classes: register_class(cls) + space_filebrowser.register_props() + from bpy.props import ( EnumProperty, StringProperty, diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index f916a9988fd..a811ba93c17 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -661,6 +661,7 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): def draw(self, context): layout = self.layout + wm = context.window_manager asset_file_handle = context.asset_file_handle if asset_file_handle is None: @@ -672,20 +673,20 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): show_developer_ui = context.preferences.view.show_developer_ui + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + if asset_file_handle.local_id: # If the active file is an ID, use its name directly so renaming is possible from right here. - layout.prop(asset_file_handle.local_id, "name", text="") + layout.prop(asset_file_handle.local_id, "name") if show_developer_ui: col = layout.column(align=True) col.label(text="Asset Catalog:") col.prop(asset_file_handle.local_id.asset_data, "catalog_id", text="UUID") col.prop(asset_file_handle.local_id.asset_data, "catalog_simple_name", text="Simple Name") - - row = layout.row() - row.label(text="Source: Current File") else: - layout.prop(asset_file_handle, "name", text="") + layout.prop(asset_file_handle, "name") if show_developer_ui: col = layout.column(align=True) @@ -694,13 +695,12 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): col.prop(asset_file_handle.asset_data, "catalog_id", text="UUID") col.prop(asset_file_handle.asset_data, "catalog_simple_name", text="Simple Name") - col = layout.column(align=True) # Just to reduce margin. - col.label(text="Source:") - row = col.row() - row.label(text=asset_lib_path) - + row = layout.row(align=True) + row.prop(wm, "asset_path_dummy", text="Source") row.operator("asset.open_containing_blend_file", text="", icon='TOOL_SETTINGS') + layout.prop(asset_file_handle.asset_data, "description") + class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel): bl_label = "Preview" @@ -712,24 +712,11 @@ class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel): row = layout.row() box = row.box() box.template_icon(icon_value=active_file.preview_icon_id, scale=5.0) - if bpy.ops.ed.lib_id_load_custom_preview.poll(): - col = row.column(align=True) - col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="") - col.separator() - col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="") - -class ASSETBROWSER_PT_metadata_details(asset_utils.AssetMetaDataPanel, Panel): - bl_label = "Details" - - def draw(self, context): - layout = self.layout - active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context) - - layout.use_property_split = True - - if active_asset: - layout.prop(active_asset, "description") + col = row.column(align=True) + col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="") + col.separator() + col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="") class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel): @@ -810,12 +797,40 @@ classes = ( ASSETBROWSER_MT_edit, ASSETBROWSER_PT_metadata, ASSETBROWSER_PT_metadata_preview, - ASSETBROWSER_PT_metadata_details, ASSETBROWSER_PT_metadata_tags, ASSETBROWSER_UL_metadata_tags, ASSETBROWSER_MT_context_menu, ) +def asset_path_str_get(self): + asset_file_handle = bpy.context.asset_file_handle + if asset_file_handle is None: + return None + + if asset_file_handle.local_id: + return "Current File" + + asset_library_ref = bpy.context.asset_library_ref + return bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref) + + +def register_props(): + from bpy.props import ( + StringProperty, + ) + from bpy.types import ( + WindowManager, + ) + + # Just a dummy property to be able to show a string in a label button via + # UILayout.prop(). + WindowManager.asset_path_dummy = StringProperty( + name="Asset Blend Path", + description="Full path to the Blender file containing the active asset", + get=asset_path_str_get, + ) + + if __name__ == "__main__": # only for live edit. from bpy.utils import register_class From f605ce7e9afcdf473ef6cd9180c25f1981846256 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 20 Oct 2021 12:30:11 +0200 Subject: [PATCH 0972/1500] Cleanup: Remove unused file-list array info members --- source/blender/editors/space_file/filelist.c | 5 +---- source/blender/makesdna/DNA_space_types.h | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index fc502b065f3..ab274fcea62 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1470,8 +1470,6 @@ static void filelist_direntryarr_free(FileDirEntryArr *array) #endif array->nbr_entries = FILEDIR_NBR_ENTRIES_UNSET; array->nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET; - array->entry_idx_start = -1; - array->entry_idx_end = -1; } static void filelist_intern_entry_free(FileListInternEntry *entry) @@ -3582,8 +3580,7 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params, BLI_movelisttolist(&filelist->filelist.entries, &tmp_entries); filelist->filelist.nbr_entries += nbr_entries; - filelist->filelist.nbr_entries_filtered = filelist->filelist.entry_idx_start = - filelist->filelist.entry_idx_end = -1; + filelist->filelist.nbr_entries_filtered = -1; } } diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index a75f52a5036..d0b95d5d1d3 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1150,7 +1150,6 @@ typedef struct FileDirEntryArr { ListBase entries; int nbr_entries; int nbr_entries_filtered; - int entry_idx_start, entry_idx_end; /** FILE_MAX. */ char root[1024]; From 690e1baf722c6c79ec6a7eaa548cff4c0538f5d3 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 20 Oct 2021 12:26:54 +0200 Subject: [PATCH 0973/1500] Fix T91808: Batch Generate Previews fails Caused by the Cycles-X merge. The old style of tile rendering was removed, leaving the script to error out trying to set the tile size. Tile rendering came back in a new form (but only really relevant for large resolution rendering), so now leave setting auto_tile & tile_size alone (since previews are rendered at PREVIEW_RENDER_DEFAULT_HEIGHT 128 -- which should never make a difference here). Maniphest Tasks: T91808 Differential Revision: https://developer.blender.org/D12937 --- release/scripts/modules/bl_previews_utils/bl_previews_render.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/release/scripts/modules/bl_previews_utils/bl_previews_render.py b/release/scripts/modules/bl_previews_utils/bl_previews_render.py index 17577a9a9f2..fa3355bd6d5 100644 --- a/release/scripts/modules/bl_previews_utils/bl_previews_render.py +++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py @@ -140,8 +140,6 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern): scene.render.use_overwrite = True scene.render.use_stamp = False scene.render.threads_mode = 'AUTO' - scene.render.tile_x = RENDER_PREVIEW_SIZE // 4 - scene.render.tile_y = RENDER_PREVIEW_SIZE // 4 image = bpy.data.images.new("TEMP_render_image", RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE, alpha=True) image.source = 'FILE' From 9001dd7f29a2f22534cf5549bc500422b1243c97 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 19 Oct 2021 12:55:20 -0300 Subject: [PATCH 0974/1500] View3D: Cursor Snap Refactor Make the snap system consistent with the placement tool and leak-safe. **Changes:** - Store `SnapCursorDataIntern` in a `static` variable; - Initialize (lazily) `SnapCursorDataIntern` only once (for the keymap). - Move setup members of `V3DSnapCursorData` to a new struct `V3DSnapCursorState` - Merge `ED_view3d_cursor_snap_activate_point` and `ED_view3d_cursor_snap_activate_plane` into `state = ED_view3d_cursor_snap_active()` - Merge `ED_view3d_cursor_snap_deactivate_point` and `ED_view3d_cursor_snap_deactivate_plane` into `ED_view3d_cursor_snap_deactive(state)` - Be sure to free the snap context when closing via `ED_view3d_cursor_snap_exit` - Use RNA properties callbacks to update the properties of the `"Add Primitive Object"` operator --- .../gizmo_library/gizmo_types/snap3d_gizmo.c | 112 ++--- source/blender/editors/include/ED_view3d.h | 48 +- .../editors/space_view3d/space_view3d.c | 4 - .../editors/space_view3d/view3d_cursor_snap.c | 443 ++++++++++-------- .../editors/space_view3d/view3d_placement.c | 250 +++++----- .../windowmanager/intern/wm_init_exit.c | 2 + 6 files changed, 460 insertions(+), 399 deletions(-) diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index fccb65bad68..b95935f2e06 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -53,22 +53,18 @@ typedef struct SnapGizmo3D { wmGizmo gizmo; - V3DSnapCursorData *cursor_handle; + V3DSnapCursorState *snap_state; } SnapGizmo3D; static void snap_gizmo_snap_elements_update(SnapGizmo3D *snap_gizmo) { - V3DSnapCursorData *snap_data = snap_gizmo->cursor_handle; wmGizmoProperty *gz_prop_snap; gz_prop_snap = WM_gizmo_target_property_find(&snap_gizmo->gizmo, "snap_elements"); if (gz_prop_snap->prop) { - snap_data->snap_elem_force |= RNA_property_enum_get(&gz_prop_snap->ptr, gz_prop_snap->prop); + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->snap_elem_force |= RNA_property_enum_get(&gz_prop_snap->ptr, gz_prop_snap->prop); } - - UI_GetThemeColor3ubv(TH_TRANSFORM, snap_data->color_line); - snap_data->color_line[3] = 128; - rgba_float_to_uchar(snap_data->color_point, snap_gizmo->gizmo.color); } /* -------------------------------------------------------------------- */ @@ -77,49 +73,47 @@ static void snap_gizmo_snap_elements_update(SnapGizmo3D *snap_gizmo) SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(Scene *scene, wmGizmo *UNUSED(gz)) { - ED_view3d_cursor_snap_activate_point(); return ED_view3d_cursor_snap_context_ensure(scene); } -void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, int flag) +void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *UNUSED(gz), int flag) { - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - snap_gizmo->cursor_handle->flag |= flag; + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->flag |= flag; } -void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, int flag) +void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *UNUSED(gz), int flag) { - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - snap_gizmo->cursor_handle->flag &= ~flag; + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->flag &= ~flag; } -bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, int flag) +bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *UNUSED(gz), int flag) { - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - return (snap_gizmo->cursor_handle->flag & flag) != 0; + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return (snap_state->flag & flag) != 0; } -bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz) +bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *UNUSED(gz)) { - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - return snap_gizmo->cursor_handle->is_snap_invert; + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + return snap_data->is_snap_invert; } bool ED_gizmotypes_snap_3d_is_enabled(const wmGizmo *gz) { - const SnapGizmo3D *snap_gizmo = (const SnapGizmo3D *)gz; - return snap_gizmo->cursor_handle->is_enabled; + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + return snap_data->is_enabled; } void ED_gizmotypes_snap_3d_data_get(const struct bContext *C, - wmGizmo *gz, + wmGizmo *UNUSED(gz), float r_loc[3], float r_nor[3], int r_elem_index[3], int *r_snap_elem) { - V3DSnapCursorData *snap_data = ((SnapGizmo3D *)gz)->cursor_handle; - + V3DSnapCursorData *snap_data = NULL; if (C) { /* Snap values are updated too late at the cursor. Be sure to update ahead of time. */ wmWindowManager *wm = CTX_wm_manager(C); @@ -128,9 +122,12 @@ void ED_gizmotypes_snap_3d_data_get(const struct bContext *C, ARegion *region = CTX_wm_region(C); int x = event->x - region->winrct.xmin; int y = event->y - region->winrct.ymin; - ED_view3d_cursor_snap_update(C, x, y, snap_data); + snap_data = ED_view3d_cursor_snap_data_get(NULL, C, x, y); } } + if (!snap_data) { + snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + } if (r_loc) { copy_v3_v3(r_loc, snap_data->loc); @@ -155,30 +152,25 @@ void ED_gizmotypes_snap_3d_data_get(const struct bContext *C, static int gizmo_snap_rna_snap_elements_force_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop)) { - V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - if (snap_data) { - return snap_data->snap_elem_force; - } - return 0; + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return snap_state->snap_elem_force; } static void gizmo_snap_rna_snap_elements_force_set_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), int value) { - V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - if (snap_data) { - snap_data->snap_elem_force = (short)value; - } + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->snap_elem_force = (short)value; } static void gizmo_snap_rna_prevpoint_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), float *values) { - V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - if (snap_data && snap_data->prevpoint) { - copy_v3_v3(values, snap_data->prevpoint); + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + if (snap_state->prevpoint) { + copy_v3_v3(values, snap_state->prevpoint); } } @@ -186,47 +178,40 @@ static void gizmo_snap_rna_prevpoint_set_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), const float *values) { - ED_view3d_cursor_snap_prevpoint_set(values); + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + ED_view3d_cursor_snap_prevpoint_set(snap_state, values); } static void gizmo_snap_rna_location_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), float *values) { - V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - if (snap_data) { - copy_v3_v3(values, snap_data->loc); - } + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + copy_v3_v3(values, snap_data->loc); } static void gizmo_snap_rna_location_set_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), const float *values) { - V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - if (snap_data) { - copy_v3_v3(snap_data->loc, values); - } + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + copy_v3_v3(snap_data->loc, values); } static void gizmo_snap_rna_normal_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), float *values) { - V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - if (snap_data) { - copy_v3_v3(values, snap_data->nor); - } + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + copy_v3_v3(values, snap_data->nor); } static void gizmo_snap_rna_snap_elem_index_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), int *values) { - V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - if (snap_data) { - copy_v3_v3_int(values, snap_data->elem_index); - } + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + copy_v3_v3_int(values, snap_data->elem_index); } /** \} */ @@ -239,8 +224,11 @@ static void snap_gizmo_setup(wmGizmo *gz) { gz->flag |= WM_GIZMO_NO_TOOLTIP; SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - ED_view3d_cursor_snap_activate_point(); - snap_gizmo->cursor_handle = ED_view3d_cursor_snap_data_get(); + snap_gizmo->snap_state = ED_view3d_cursor_snap_active(); + snap_gizmo->snap_state->draw_point = true; + snap_gizmo->snap_state->draw_plane = false; + + rgba_float_to_uchar(snap_gizmo->snap_state->color_point, gz->color); } static void snap_gizmo_draw(const bContext *UNUSED(C), wmGizmo *UNUSED(gz)) @@ -251,7 +239,6 @@ static void snap_gizmo_draw(const bContext *UNUSED(C), wmGizmo *UNUSED(gz)) static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2]) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - V3DSnapCursorData *snap_data = snap_gizmo->cursor_handle; /* Snap Elements can change while the gizmo is active. Need to be updated somewhere. */ snap_gizmo_snap_elements_update(snap_gizmo); @@ -271,9 +258,9 @@ static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2]) y = mval[1]; } } - ED_view3d_cursor_snap_update(C, x, y, snap_data); + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(snap_gizmo->snap_state, C, x, y); - if (snap_data && snap_data->snap_elem) { + if (snap_data->snap_elem) { return 0; } return -1; @@ -297,10 +284,7 @@ static int snap_gizmo_invoke(bContext *UNUSED(C), static void snap_gizmo_free(wmGizmo *gz) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - V3DSnapCursorData *snap_data = snap_gizmo->cursor_handle; - if (snap_data) { - ED_view3d_cursor_snap_deactivate_point(); - } + ED_view3d_cursor_snap_deactive(snap_gizmo->snap_state); } static void GIZMO_GT_snap_3d(wmGizmoType *gzt) diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 078ebb5e020..67c470a005f 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -257,18 +257,6 @@ typedef enum { } eV3DPlaceOrient; typedef struct V3DSnapCursorData { - /* Setup. */ - eV3DSnapCursor flag; - eV3DPlaceDepth plane_depth; - eV3DPlaceOrient plane_orient; - uchar color_line[4]; - uchar color_point[4]; - float *prevpoint; - short snap_elem_force; /* If zero, use scene settings. */ - short plane_axis; - bool use_plane_axis_auto; - - /* Return values. */ short snap_elem; float loc[3]; float nor[3]; @@ -281,16 +269,31 @@ typedef struct V3DSnapCursorData { bool is_enabled; } V3DSnapCursorData; -V3DSnapCursorData *ED_view3d_cursor_snap_data_get(void); -void ED_view3d_cursor_snap_activate_point(void); -void ED_view3d_cursor_snap_activate_plane(void); -void ED_view3d_cursor_snap_deactivate_point(void); -void ED_view3d_cursor_snap_deactivate_plane(void); -void ED_view3d_cursor_snap_prevpoint_set(const float prev_point[3]); -void ED_view3d_cursor_snap_update(const struct bContext *C, - const int x, - const int y, - V3DSnapCursorData *snap_data); +typedef struct V3DSnapCursorState { + /* Setup. */ + eV3DSnapCursor flag; + eV3DPlaceDepth plane_depth; + eV3DPlaceOrient plane_orient; + uchar color_line[4]; + uchar color_point[4]; + float *prevpoint; + short snap_elem_force; /* If zero, use scene settings. */ + short plane_axis; + bool use_plane_axis_auto; + bool draw_point; + bool draw_plane; +} V3DSnapCursorState; + +void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state); +V3DSnapCursorState *ED_view3d_cursor_snap_state_get(void); +V3DSnapCursorState *ED_view3d_cursor_snap_active(void); +void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state); +void ED_view3d_cursor_snap_prevpoint_set(V3DSnapCursorState *state, const float prev_point[3]); +V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state, + const struct bContext *C, + const int x, + const int y); + struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *scene); void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d, const float loc_prev[3], @@ -299,6 +302,7 @@ void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d, const uchar color_line[4], const uchar color_point[4], const short snap_elem_type); +void ED_view3d_cursor_snap_exit(void); /* view3d_iterators.c */ diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 83aa2d93fbb..787cf529483 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -355,10 +355,6 @@ static void view3d_exit(wmWindowManager *UNUSED(wm), ScrArea *area) BLI_assert(area->spacetype == SPACE_VIEW3D); View3D *v3d = area->spacedata.first; MEM_SAFE_FREE(v3d->runtime.local_stats); - - /* Be sure to release the #V3DSnapCursorData from the cursor, or it will get lost. */ - ED_view3d_cursor_snap_deactivate_point(); - ED_view3d_cursor_snap_deactivate_plane(); } static SpaceLink *view3d_duplicate(SpaceLink *sl) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 590f0b4563a..06a37703648 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -53,14 +53,26 @@ #include "DEG_depsgraph_query.h" #include "WM_api.h" -#include "wm.h" + +#define STATE_LEN 3 + +typedef struct SnapStateIntern { + V3DSnapCursorState snap_state; + float prevpoint_stack[3]; + int state_active_prev; + bool is_active; +} SnapStateIntern; typedef struct SnapCursorDataIntern { - /* Keep as first member. */ - struct V3DSnapCursorData snap_data; + V3DSnapCursorState state_default; + SnapStateIntern state_intern[STATE_LEN]; + V3DSnapCursorData snap_data; + + int state_active_len; + int state_active; struct SnapObjectContext *snap_context_v3d; - float prevpoint_stack[3]; + const Scene *scene; short snap_elem_hidden; /* Copy of the parameters of the last event state in order to detect updates. */ @@ -79,10 +91,11 @@ typedef struct SnapCursorDataIntern { struct wmPaintCursor *handle; - bool draw_point; - bool draw_plane; + bool is_initiated; } SnapCursorDataIntern; +static SnapCursorDataIntern g_data_intern = {{0}}; + /** * Calculate a 3x3 orientation matrix from the surface under the cursor. */ @@ -437,23 +450,23 @@ void ED_view3d_cursor_snap_draw_util(RegionView3D *rv3d, * \{ */ /* Checks if the current event is different from the one captured in the last update. */ -static bool v3d_cursor_eventstate_has_changed(SnapCursorDataIntern *sdata_intern, +static bool v3d_cursor_eventstate_has_changed(SnapCursorDataIntern *data_intern, + V3DSnapCursorState *state, const wmWindowManager *wm, const int x, const int y) { - V3DSnapCursorData *snap_data = (V3DSnapCursorData *)sdata_intern; if (wm && wm->winactive) { const wmEvent *event = wm->winactive->eventstate; - if ((x != sdata_intern->last_eventstate.x) || (y != sdata_intern->last_eventstate.y)) { + if ((x != data_intern->last_eventstate.x) || (y != data_intern->last_eventstate.y)) { return true; } #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - if (!(snap_data && (snap_data->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE))) { - if ((event->ctrl != sdata_intern->last_eventstate.ctrl) || - (event->shift != sdata_intern->last_eventstate.shift) || - (event->alt != sdata_intern->last_eventstate.alt) || - (event->oskey != sdata_intern->last_eventstate.oskey)) { + if (!(state && (state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE))) { + if ((event->ctrl != data_intern->last_eventstate.ctrl) || + (event->shift != data_intern->last_eventstate.shift) || + (event->alt != data_intern->last_eventstate.alt) || + (event->oskey != data_intern->last_eventstate.oskey)) { return true; } } @@ -472,31 +485,30 @@ static void v3d_cursor_eventstate_save_xy(SnapCursorDataIntern *cursor_snap, } #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK -static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *sdata_intern, - const wmWindowManager *wm) +static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const wmWindowManager *wm) { if (!wm || !wm->winactive) { return false; } const wmEvent *event = wm->winactive->eventstate; - if ((event->ctrl == sdata_intern->last_eventstate.ctrl) && - (event->shift == sdata_intern->last_eventstate.shift) && - (event->alt == sdata_intern->last_eventstate.alt) && - (event->oskey == sdata_intern->last_eventstate.oskey)) { + if ((event->ctrl == data_intern->last_eventstate.ctrl) && + (event->shift == data_intern->last_eventstate.shift) && + (event->alt == data_intern->last_eventstate.alt) && + (event->oskey == data_intern->last_eventstate.oskey)) { /* Nothing has changed. */ - return sdata_intern->snap_data.is_snap_invert; + return data_intern->snap_data.is_snap_invert; } /* Save new eventstate. */ - sdata_intern->last_eventstate.ctrl = event->ctrl; - sdata_intern->last_eventstate.shift = event->shift; - sdata_intern->last_eventstate.alt = event->alt; - sdata_intern->last_eventstate.oskey = event->oskey; + data_intern->last_eventstate.ctrl = event->ctrl; + data_intern->last_eventstate.shift = event->shift; + data_intern->last_eventstate.alt = event->alt; + data_intern->last_eventstate.oskey = event->oskey; - const int snap_on = sdata_intern->snap_on; + const int snap_on = data_intern->snap_on; - wmKeyMap *keymap = WM_keymap_active(wm, sdata_intern->keymap); + wmKeyMap *keymap = WM_keymap_active(wm, data_intern->keymap); for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) { if (kmi->flag & KMI_INACTIVE) { continue; @@ -521,34 +533,41 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *sdata_intern, /** \name Update * \{ */ -static short v3d_cursor_snap_elements(V3DSnapCursorData *cursor_snap, Scene *scene) +static short v3d_cursor_snap_elements(V3DSnapCursorState *snap_state, Scene *scene) { - short snap_elements = cursor_snap->snap_elem_force; + short snap_elements = snap_state->snap_elem_force; if (!snap_elements) { return scene->toolsettings->snap_mode; } return snap_elements; } -static void v3d_cursor_snap_context_ensure(SnapCursorDataIntern *sdata_intern, Scene *scene) +static void v3d_cursor_snap_context_ensure(Scene *scene) { - if (sdata_intern->snap_context_v3d == NULL) { - sdata_intern->snap_context_v3d = ED_transform_snap_object_context_create(scene, 0); + SnapCursorDataIntern *data_intern = &g_data_intern; + if (data_intern->snap_context_v3d && (data_intern->scene != scene)) { + ED_transform_snap_object_context_destroy(data_intern->snap_context_v3d); + data_intern->snap_context_v3d = NULL; + } + if (data_intern->snap_context_v3d == NULL) { + data_intern->snap_context_v3d = ED_transform_snap_object_context_create(scene, 0); + data_intern->scene = scene; } } -static void v3d_cursor_snap_update(const bContext *C, +static void v3d_cursor_snap_update(V3DSnapCursorState *state, + const bContext *C, wmWindowManager *wm, Depsgraph *depsgraph, Scene *scene, ARegion *region, View3D *v3d, int x, - int y, - SnapCursorDataIntern *sdata_intern) + int y) { - v3d_cursor_snap_context_ensure(sdata_intern, scene); - V3DSnapCursorData *snap_data = (V3DSnapCursorData *)sdata_intern; + SnapCursorDataIntern *data_intern = &g_data_intern; + V3DSnapCursorData *snap_data = &data_intern->snap_data; + v3d_cursor_snap_context_ensure(scene); float co[3], no[3], face_nor[3], obmat[4][4], omat[3][3]; short snap_elem = 0; @@ -560,18 +579,18 @@ static void v3d_cursor_snap_update(const bContext *C, zero_v3(face_nor); unit_m3(omat); - ushort snap_elements = v3d_cursor_snap_elements(snap_data, scene); - sdata_intern->snap_elem_hidden = 0; - const bool draw_plane = sdata_intern->draw_plane; + ushort snap_elements = v3d_cursor_snap_elements(state, scene); + data_intern->snap_elem_hidden = 0; + const bool draw_plane = state->draw_plane; if (draw_plane && !(snap_elements & SCE_SNAP_MODE_FACE)) { - sdata_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; + data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; snap_elements |= SCE_SNAP_MODE_FACE; } snap_data->is_enabled = true; #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - if (!(snap_data->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) { - snap_data->is_snap_invert = v3d_cursor_is_snap_invert(sdata_intern, wm); + if (!(state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) { + snap_data->is_snap_invert = v3d_cursor_is_snap_invert(data_intern, wm); const ToolSettings *ts = scene->toolsettings; if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) { @@ -580,7 +599,7 @@ static void v3d_cursor_snap_update(const bContext *C, snap_data->snap_elem = 0; return; } - snap_elements = sdata_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; + snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; } } #endif @@ -588,30 +607,28 @@ static void v3d_cursor_snap_update(const bContext *C, if (snap_elements & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { float prev_co[3] = {0.0f}; - if (snap_data->prevpoint) { - copy_v3_v3(prev_co, snap_data->prevpoint); + if (state->prevpoint) { + copy_v3_v3(prev_co, state->prevpoint); } else { snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR; } - eSnapSelect snap_select = (snap_data->flag & V3D_SNAPCURSOR_SNAP_ONLY_ACTIVE) ? - SNAP_ONLY_ACTIVE : - SNAP_ALL; + eSnapSelect snap_select = (state->flag & V3D_SNAPCURSOR_SNAP_ONLY_ACTIVE) ? SNAP_ONLY_ACTIVE : + SNAP_ALL; - eSnapEditType edit_mode_type = (snap_data->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL) ? + eSnapEditType edit_mode_type = (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL) ? SNAP_GEOM_FINAL : - (snap_data->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE) ? + (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE) ? SNAP_GEOM_CAGE : SNAP_GEOM_EDIT; - bool use_occlusion_test = (snap_data->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false : - true; + bool use_occlusion_test = (state->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false : true; float dist_px = 12.0f * U.pixelsize; snap_elem = ED_transform_snap_object_project_view3d_ex( - sdata_intern->snap_context_v3d, + data_intern->snap_context_v3d, depsgraph, region, v3d, @@ -633,11 +650,11 @@ static void v3d_cursor_snap_update(const bContext *C, } if (is_zero_v3(face_nor)) { - face_nor[snap_data->plane_axis] = 1.0f; + face_nor[state->plane_axis] = 1.0f; } if (draw_plane) { - bool orient_surface = snap_elem && (snap_data->plane_orient == V3D_PLACE_ORIENT_SURFACE); + bool orient_surface = snap_elem && (state->plane_orient == V3D_PLACE_ORIENT_SURFACE); if (orient_surface) { copy_m3_m4(omat, obmat); } @@ -650,8 +667,8 @@ static void v3d_cursor_snap_update(const bContext *C, scene, view_layer, v3d, region->regiondata, ob, ob, orient_index, pivot_point, omat); RegionView3D *rv3d = region->regiondata; - if (snap_data->use_plane_axis_auto) { - mat3_align_axis_to_v3(omat, snap_data->plane_axis, rv3d->viewinv[2]); + if (state->use_plane_axis_auto) { + mat3_align_axis_to_v3(omat, state->plane_axis, rv3d->viewinv[2]); } } @@ -659,7 +676,7 @@ static void v3d_cursor_snap_update(const bContext *C, * * While making orthogonal doesn't always work well (especially with gimbal orientation for * e.g.) it's a corner case, without better alternatives as objects don't support shear. */ - orthogonalize_m3(omat, snap_data->plane_axis); + orthogonalize_m3(omat, state->plane_axis); if (orient_surface) { v3d_cursor_poject_surface_normal(face_nor, obmat, omat); @@ -667,21 +684,21 @@ static void v3d_cursor_snap_update(const bContext *C, } float *co_depth = snap_elem ? co : scene->cursor.location; - snap_elem &= ~sdata_intern->snap_elem_hidden; + snap_elem &= ~data_intern->snap_elem_hidden; if (snap_elem == 0) { float plane[4]; - if (snap_data->plane_depth != V3D_PLACE_DEPTH_CURSOR_VIEW) { - const float *plane_normal = omat[snap_data->plane_axis]; + if (state->plane_depth != V3D_PLACE_DEPTH_CURSOR_VIEW) { + const float *plane_normal = omat[state->plane_axis]; plane_from_point_normal_v3(plane, co_depth, plane_normal); } - if ((snap_data->plane_depth == V3D_PLACE_DEPTH_CURSOR_VIEW) || + if ((state->plane_depth == V3D_PLACE_DEPTH_CURSOR_VIEW) || !ED_view3d_win_to_3d_on_plane(region, plane, mval_fl, true, co)) { ED_view3d_win_to_3d(v3d, region, co_depth, mval_fl, co); } if (snap_data->is_enabled && (snap_elements & SCE_SNAP_MODE_INCREMENT)) { - v3d_cursor_snap_calc_incremental(scene, v3d, region, snap_data->prevpoint, co); + v3d_cursor_snap_calc_incremental(scene, v3d, region, state->prevpoint, co); } } else if (snap_elem == SCE_SNAP_MODE_VERTEX) { @@ -703,7 +720,7 @@ static void v3d_cursor_snap_update(const bContext *C, copy_m3_m3(snap_data->plane_omat, omat); - v3d_cursor_eventstate_save_xy(sdata_intern, x, y); + v3d_cursor_eventstate_save_xy(data_intern, x, y); } /** \} */ @@ -739,21 +756,22 @@ static bool v3d_cursor_snap_pool_fn(bContext *C) static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *customdata) { - V3DSnapCursorData *snap_data = customdata; - SnapCursorDataIntern *sdata_intern = customdata; + SnapCursorDataIntern *data_intern = &g_data_intern; + V3DSnapCursorState *state = ED_view3d_cursor_snap_state_get(); + V3DSnapCursorData *snap_data = &data_intern->snap_data; wmWindowManager *wm = CTX_wm_manager(C); ARegion *region = CTX_wm_region(C); x -= region->winrct.xmin; y -= region->winrct.ymin; - if (v3d_cursor_eventstate_has_changed(sdata_intern, wm, x, y)) { + if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = DEG_get_input_scene(depsgraph); View3D *v3d = CTX_wm_view3d(C); - v3d_cursor_snap_update(C, wm, depsgraph, scene, region, v3d, x, y, sdata_intern); + v3d_cursor_snap_update(state, C, wm, depsgraph, scene, region, v3d, x, y); } - const bool draw_plane = sdata_intern->draw_plane; + const bool draw_plane = state->draw_plane; if (!snap_data->snap_elem && !draw_plane) { return; } @@ -773,12 +791,12 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *customdata) copy_m4_m3(matrix, snap_data->plane_omat); copy_v3_v3(matrix[3], snap_data->loc); - v3d_cursor_plane_draw(rv3d, snap_data->plane_axis, matrix); + v3d_cursor_plane_draw(rv3d, state->plane_axis, matrix); } - if (snap_data->snap_elem && sdata_intern->draw_point) { + if (snap_data->snap_elem && state->draw_point) { const float *prev_point = (snap_data->snap_elem & SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? - snap_data->prevpoint : + state->prevpoint : NULL; GPU_line_smooth(false); @@ -788,8 +806,8 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *customdata) prev_point, snap_data->loc, NULL, - snap_data->color_line, - snap_data->color_point, + state->color_line, + state->color_point, snap_data->snap_elem); } @@ -802,158 +820,177 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *customdata) /** \} */ -V3DSnapCursorData *ED_view3d_cursor_snap_data_get(void) +V3DSnapCursorState *ED_view3d_cursor_snap_state_get(void) { - LISTBASE_FOREACH_MUTABLE (wmWindowManager *, wm, &G.main->wm) { - LISTBASE_FOREACH_MUTABLE (wmPaintCursor *, pc, &wm->paintcursors) { - if (pc->draw == v3d_cursor_snap_draw_fn) { - return (V3DSnapCursorData *)pc->customdata; - } + if (!g_data_intern.state_active_len) { + return &g_data_intern.state_default; + } + return (V3DSnapCursorState *)&g_data_intern.state_intern[g_data_intern.state_active]; +} + +static void v3d_cursor_snap_state_init(V3DSnapCursorState *state) +{ + state->prevpoint = NULL; + state->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | + SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT); + state->plane_axis = 2; + rgba_uchar_args_set(state->color_point, 255, 255, 255, 255); + UI_GetThemeColor3ubv(TH_TRANSFORM, state->color_line); + state->color_line[3] = 128; + state->draw_point = true; + state->draw_plane = false; +} + +static void v3d_cursor_snap_activate(void) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (!data_intern->handle) { + if (!data_intern->is_initiated) { + /* Only initiate intern data once. + * TODO: ED_view3d_cursor_snap_init */ + +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + struct wmKeyConfig *keyconf = ((wmWindowManager *)G.main->wm.first)->defaultconf; + + data_intern->keymap = WM_modalkeymap_find(keyconf, "Generic Gizmo Tweak Modal Map"); + RNA_enum_value_from_id(data_intern->keymap->modal_items, "SNAP_ON", &data_intern->snap_on); +#endif + V3DSnapCursorState *state_default = &data_intern->state_default; + state_default->prevpoint = NULL; + state_default->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | + SCE_SNAP_MODE_FACE | SCE_SNAP_MODE_EDGE_PERPENDICULAR | + SCE_SNAP_MODE_EDGE_MIDPOINT); + state_default->plane_axis = 2; + rgba_uchar_args_set(state_default->color_point, 255, 255, 255, 255); + UI_GetThemeColor3ubv(TH_TRANSFORM, state_default->color_line); + state_default->color_line[3] = 128; + state_default->draw_point = true; + state_default->draw_plane = false; + + data_intern->is_initiated = true; + } + + struct wmPaintCursor *pc = WM_paint_cursor_activate( + SPACE_VIEW3D, RGN_TYPE_WINDOW, v3d_cursor_snap_pool_fn, v3d_cursor_snap_draw_fn, NULL); + data_intern->handle = pc; + } +} + +static void v3d_cursor_snap_free(void) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (data_intern->handle) { + WM_paint_cursor_end(data_intern->handle); + data_intern->handle = NULL; + } + if (data_intern->snap_context_v3d) { + ED_transform_snap_object_context_destroy(data_intern->snap_context_v3d); + data_intern->snap_context_v3d = NULL; + } + + for (SnapStateIntern *state_intern = data_intern->state_intern; + state_intern < &data_intern->state_intern[STATE_LEN]; + state_intern++) { + state_intern->is_active = false; + } +} + +void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state) +{ + g_data_intern.state_default = *state; +} + +V3DSnapCursorState *ED_view3d_cursor_snap_active(void) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (!data_intern->state_active_len) { + v3d_cursor_snap_activate(); + } + + data_intern->state_active_len++; + for (int i = 0; i < STATE_LEN; i++) { + SnapStateIntern *state_intern = &g_data_intern.state_intern[i]; + if (!state_intern->is_active) { + state_intern->snap_state = g_data_intern.state_default; + state_intern->is_active = true; + state_intern->state_active_prev = data_intern->state_active; + data_intern->state_active = i; + return (V3DSnapCursorState *)state_intern; } } + + BLI_assert(false); + data_intern->state_active_len--; return NULL; } -static void v3d_cursor_snap_data_init(SnapCursorDataIntern *sdata_intern) +void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state) { -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - struct wmKeyConfig *keyconf = ((wmWindowManager *)G.main->wm.first)->defaultconf; - - sdata_intern->keymap = WM_modalkeymap_find(keyconf, "Generic Gizmo Tweak Modal Map"); - RNA_enum_value_from_id(sdata_intern->keymap->modal_items, "SNAP_ON", &sdata_intern->snap_on); -#endif - - V3DSnapCursorData *snap_data = &sdata_intern->snap_data; - snap_data->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT); - snap_data->plane_axis = 2; - rgba_uchar_args_set(snap_data->color_point, 255, 255, 255, 255); - UI_GetThemeColor3ubv(TH_TRANSFORM, snap_data->color_line); - snap_data->color_line[3] = 128; -} - -static SnapCursorDataIntern *v3d_cursor_snap_ensure(void) -{ - SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); - if (!sdata_intern) { - sdata_intern = MEM_callocN(sizeof(*sdata_intern), __func__); - v3d_cursor_snap_data_init(sdata_intern); - - struct wmPaintCursor *pc = WM_paint_cursor_activate(SPACE_VIEW3D, - RGN_TYPE_WINDOW, - v3d_cursor_snap_pool_fn, - v3d_cursor_snap_draw_fn, - sdata_intern); - sdata_intern->handle = pc; - } - return sdata_intern; -} - -void ED_view3d_cursor_snap_activate_point(void) -{ - SnapCursorDataIntern *sdata_intern = v3d_cursor_snap_ensure(); - sdata_intern->draw_point = true; -} - -void ED_view3d_cursor_snap_activate_plane(void) -{ - SnapCursorDataIntern *sdata_intern = v3d_cursor_snap_ensure(); - sdata_intern->draw_plane = true; -} - -static void v3d_cursor_snap_free(SnapCursorDataIntern *sdata_intern) -{ - WM_paint_cursor_end(sdata_intern->handle); - if (sdata_intern->snap_context_v3d) { - ED_transform_snap_object_context_destroy(sdata_intern->snap_context_v3d); - } - MEM_freeN(sdata_intern); -} - -void ED_view3d_cursor_snap_deactivate_point(void) -{ - SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); - if (!sdata_intern) { + SnapCursorDataIntern *data_intern = &g_data_intern; + if (!data_intern->state_active_len) { + BLI_assert(false); return; } - sdata_intern->draw_point = false; - sdata_intern->snap_data.prevpoint = NULL; - if (sdata_intern->draw_plane) { + SnapStateIntern *state_intern = (SnapStateIntern *)state; + if (!state_intern->is_active) { return; } - v3d_cursor_snap_free(sdata_intern); -} - -void ED_view3d_cursor_snap_deactivate_plane(void) -{ - SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); - if (!sdata_intern) { - return; - } - - sdata_intern->draw_plane = false; - sdata_intern->snap_data.prevpoint = NULL; - if (sdata_intern->draw_point) { - return; - } - - v3d_cursor_snap_free(sdata_intern); -} - -void ED_view3d_cursor_snap_prevpoint_set(const float prev_point[3]) -{ - SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); - if (!sdata_intern) { - return; - } - if (prev_point) { - copy_v3_v3(sdata_intern->prevpoint_stack, prev_point); - sdata_intern->snap_data.prevpoint = sdata_intern->prevpoint_stack; + state_intern->is_active = false; + data_intern->state_active_len--; + if (!data_intern->state_active_len) { + v3d_cursor_snap_free(); } else { - sdata_intern->snap_data.prevpoint = NULL; + data_intern->state_active = state_intern->state_active_prev; } } -void ED_view3d_cursor_snap_update(const bContext *C, - const int x, - const int y, - V3DSnapCursorData *snap_data) +void ED_view3d_cursor_snap_prevpoint_set(V3DSnapCursorState *state, const float prev_point[3]) { - SnapCursorDataIntern stack = {0}, *sdata_intern; - sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); - if (!sdata_intern) { - sdata_intern = &stack; - v3d_cursor_snap_data_init(sdata_intern); - sdata_intern->draw_plane = true; + SnapStateIntern *state_intern = (SnapStateIntern *)state; + if (prev_point) { + copy_v3_v3(state_intern->prevpoint_stack, prev_point); + state->prevpoint = state_intern->prevpoint_stack; } - - wmWindowManager *wm = CTX_wm_manager(C); - if (v3d_cursor_eventstate_has_changed(sdata_intern, wm, x, y)) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = DEG_get_input_scene(depsgraph); - ARegion *region = CTX_wm_region(C); - View3D *v3d = CTX_wm_view3d(C); - - v3d_cursor_snap_update(C, wm, depsgraph, scene, region, v3d, x, y, sdata_intern); + else { + state->prevpoint = NULL; } - if ((void *)snap_data != (void *)sdata_intern) { - if ((sdata_intern == &stack) && sdata_intern->snap_context_v3d) { - ED_transform_snap_object_context_destroy(sdata_intern->snap_context_v3d); - sdata_intern->snap_context_v3d = NULL; +} + +V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state, + const bContext *C, + const int x, + const int y) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (C && data_intern->state_active_len) { + wmWindowManager *wm = CTX_wm_manager(C); + if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = DEG_get_input_scene(depsgraph); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + + if (!state) { + state = ED_view3d_cursor_snap_state_get(); + } + v3d_cursor_snap_update(state, C, wm, depsgraph, scene, region, v3d, x, y); } - *snap_data = *(V3DSnapCursorData *)sdata_intern; } + + return &data_intern->snap_data; } -struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *scene) +struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(Scene *scene) { - SnapCursorDataIntern *sdata_intern = (SnapCursorDataIntern *)ED_view3d_cursor_snap_data_get(); - if (!sdata_intern) { - return NULL; - } - v3d_cursor_snap_context_ensure(sdata_intern, scene); - return sdata_intern->snap_context_v3d; + SnapCursorDataIntern *data_intern = &g_data_intern; + v3d_cursor_snap_context_ensure(scene); + return data_intern->snap_context_v3d; +} + +void ED_view3d_cursor_snap_exit(void) +{ + v3d_cursor_snap_free(); } diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index adff75b571d..bffcdcfc320 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -45,11 +45,11 @@ #include "view3d_intern.h" -static const char *view3d_gzgt_placement_id = "VIEW3D_GGT_placement"; +#define SNAP_MODE_GEOM \ + (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | \ + SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT) -static void preview_plane_cursor_visible_set(wmGizmoGroup *gzgroup, - bool do_draw_plane, - bool do_draw_point); +static const char *view3d_gzgt_placement_id = "VIEW3D_GGT_placement"; /** * Dot products below this will be considered view aligned. @@ -151,6 +151,7 @@ struct InteractivePlaceData { float matrix_orient[3][3]; int orient_axis; + V3DSnapCursorState *snap_state; bool use_snap, is_snap_found, is_snap_invert; float snap_co[3]; @@ -693,18 +694,25 @@ static void draw_primitive_view(const struct bContext *C, ARegion *UNUSED(region * Use by both the operator and placement cursor. * \{ */ -static bool view3d_interactive_add_calc_plane(bContext *C, - const wmEvent *event, - float r_co_src[3], - float r_matrix_orient[3][3]) +static bool view3d_interactive_add_calc_snap(bContext *C, + const wmEvent *event, + float r_co_src[3], + float r_matrix_orient[3][3], + bool *r_is_enabled, + bool *r_is_snap_invert) { - V3DSnapCursorData snap_data; - ED_view3d_cursor_snap_update(C, UNPACK2(event->mval), &snap_data); - copy_v3_v3(r_co_src, snap_data.loc); + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + copy_v3_v3(r_co_src, snap_data->loc); if (r_matrix_orient) { - copy_m3_m3(r_matrix_orient, snap_data.plane_omat); + copy_m3_m3(r_matrix_orient, snap_data->plane_omat); } - return snap_data.snap_elem != 0; + if (r_is_enabled) { + *r_is_enabled = snap_data->is_enabled; + } + if (r_is_snap_invert) { + *r_is_snap_invert = snap_data->is_snap_invert; + } + return snap_data->snap_elem != 0; } /** \} */ @@ -715,12 +723,11 @@ static bool view3d_interactive_add_calc_plane(bContext *C, static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEvent *event) { + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); - const int plane_axis = RNA_enum_get(op->ptr, "plane_axis"); - const bool plane_axis_auto = RNA_boolean_get(op->ptr, "plane_axis_auto"); + const int plane_axis = snap_state->plane_axis; const enum ePlace_SnapTo snap_to = RNA_enum_get(op->ptr, "snap_target"); - const eV3DPlaceDepth plane_depth = RNA_enum_get(op->ptr, "plane_depth"); - const eV3DPlaceOrient plane_orient = RNA_enum_get(op->ptr, "plane_orient"); + const enum ePlace_Origin plane_origin[2] = { RNA_enum_get(op->ptr, "plane_origin_base"), RNA_enum_get(op->ptr, "plane_origin_depth"), @@ -734,24 +741,16 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv ipd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); - ED_view3d_cursor_snap_activate_point(); - { - V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - snap_data->plane_depth = plane_depth; - snap_data->plane_orient = plane_orient; - snap_data->plane_axis = plane_axis; - snap_data->use_plane_axis_auto = plane_axis_auto; + ipd->snap_state = ED_view3d_cursor_snap_active(); + ipd->snap_state->draw_point = true; + ipd->snap_state->draw_plane = true; - ED_view3d_cursor_snap_activate_plane(); - view3d_interactive_add_calc_plane(C, event, ipd->co_src, ipd->matrix_orient); - ED_view3d_cursor_snap_deactivate_plane(); + ipd->is_snap_found = + view3d_interactive_add_calc_snap( + C, event, ipd->co_src, ipd->matrix_orient, &ipd->use_snap, &ipd->is_snap_invert) != 0; - ED_view3d_cursor_snap_prevpoint_set(ipd->co_src); - - ipd->use_snap = snap_data->is_enabled; - ipd->is_snap_invert = snap_data->is_snap_invert; - ipd->is_snap_found = snap_data->snap_elem != 0; - } + ipd->snap_state->draw_plane = false; + ED_view3d_cursor_snap_prevpoint_set(ipd->snap_state, ipd->co_src); ipd->orient_axis = plane_axis; for (int i = 0; i < 2; i++) { @@ -905,14 +904,7 @@ static void view3d_interactive_add_exit(bContext *C, wmOperator *op) UNUSED_VARS(C); struct InteractivePlaceData *ipd = op->customdata; - - wmGizmoGroup *gzgroup = idp_gizmogroup_from_region(ipd->region); - if (gzgroup != NULL) { - preview_plane_cursor_visible_set(gzgroup, true, true); - } - else { - ED_view3d_cursor_snap_deactivate_point(); - } + ED_view3d_cursor_snap_deactive(ipd->snap_state); ED_region_draw_cb_exit(ipd->region->type, ipd->draw_handle_view); @@ -1035,7 +1027,7 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve if (ipd->step_index == STEP_BASE) { if (ELEM(event->type, ipd->launch_event, LEFTMOUSE)) { if (event->val == KM_RELEASE) { - ED_view3d_cursor_snap_prevpoint_set(ipd->co_src); + ED_view3d_cursor_snap_prevpoint_set(ipd->snap_state, ipd->co_src); /* Set secondary plane. */ @@ -1184,7 +1176,8 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve /* Calculate the snap location on mouse-move or when toggling snap. */ ipd->is_snap_found = false; if (ipd->use_snap) { - ipd->is_snap_found = view3d_interactive_add_calc_plane(C, event, ipd->snap_co, NULL); + ipd->is_snap_found = view3d_interactive_add_calc_snap( + C, event, ipd->snap_co, NULL, NULL, NULL); } if (ipd->step_index == STEP_BASE) { @@ -1254,6 +1247,98 @@ static bool view3d_interactive_add_poll(bContext *C) return ELEM(mode, CTX_MODE_OBJECT, CTX_MODE_EDIT_MESH); } +static int idp_rna_plane_axis_get_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop)) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return snap_state->plane_axis; +} + +static void idp_rna_plane_axis_set_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop), + int value) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->plane_axis = value; + ED_view3d_cursor_snap_state_default_set(snap_state); +} + +static int idp_rna_plane_depth_get_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop)) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return snap_state->plane_depth; +} + +static void idp_rna_plane_depth_set_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop), + int value) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->plane_depth = value; + ED_view3d_cursor_snap_state_default_set(snap_state); +} + +static int idp_rna_plane_orient_get_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop)) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return snap_state->plane_orient; +} + +static void idp_rna_plane_orient_set_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop), + int value) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->plane_orient = value; + ED_view3d_cursor_snap_state_default_set(snap_state); +} + +static int idp_rna_snap_target_get_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop)) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + if (!snap_state->snap_elem_force) { + return PLACE_SNAP_TO_DEFAULT; + } + + /* Make sure you keep a consistent #snap_mode. */ + snap_state->snap_elem_force = SNAP_MODE_GEOM; + return PLACE_SNAP_TO_GEOMETRY; +} + +static void idp_rna_snap_target_set_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop), + int value) +{ + short snap_mode = 0; /* #toolsettings->snap_mode. */ + const enum ePlace_SnapTo snap_to = value; + if (snap_to == PLACE_SNAP_TO_GEOMETRY) { + snap_mode = SNAP_MODE_GEOM; + } + + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->snap_elem_force = snap_mode; + ED_view3d_cursor_snap_state_default_set(snap_state); +} + +static bool idp_rna_use_plane_axis_auto_get_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop)) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return snap_state->use_plane_axis_auto; +} + +static void idp_rna_use_plane_axis_auto_set_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop), + bool value) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->use_plane_axis_auto = value; + ED_view3d_cursor_snap_state_default_set(snap_state); +} + void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) { /* identifiers */ @@ -1294,6 +1379,8 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_enum_default(prop, 2); RNA_def_property_enum_items(prop, rna_enum_axis_xyz_items); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_enum_funcs_runtime( + prop, idp_rna_plane_axis_get_fn, idp_rna_plane_axis_set_fn, NULL); prop = RNA_def_boolean(ot->srna, "plane_axis_auto", @@ -1302,6 +1389,8 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) "Select the closest axis when placing objects " "(surface overrides)"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_boolean_funcs_runtime( + prop, idp_rna_use_plane_axis_auto_get_fn, idp_rna_use_plane_axis_auto_set_fn); static const EnumPropertyItem plane_depth_items[] = { {V3D_PLACE_DEPTH_SURFACE, @@ -1327,6 +1416,8 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_enum_default(prop, V3D_PLACE_DEPTH_SURFACE); RNA_def_property_enum_items(prop, plane_depth_items); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_enum_funcs_runtime( + prop, idp_rna_plane_depth_get_fn, idp_rna_plane_depth_set_fn, NULL); static const EnumPropertyItem plane_orientation_items[] = { {V3D_PLACE_ORIENT_SURFACE, @@ -1346,6 +1437,8 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_enum_default(prop, V3D_PLACE_ORIENT_SURFACE); RNA_def_property_enum_items(prop, plane_orientation_items); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_enum_funcs_runtime( + prop, idp_rna_plane_orient_get_fn, idp_rna_plane_orient_set_fn, NULL); static const EnumPropertyItem snap_to_items[] = { {PLACE_SNAP_TO_GEOMETRY, "GEOMETRY", 0, "Geometry", "Snap to all geometry"}, @@ -1357,6 +1450,8 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_enum_default(prop, PLACE_SNAP_TO_GEOMETRY); RNA_def_property_enum_items(prop, snap_to_items); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_enum_funcs_runtime( + prop, idp_rna_snap_target_get_fn, idp_rna_snap_target_set_fn, NULL); { /* Plane Origin. */ static const EnumPropertyItem items[] = { @@ -1404,77 +1499,21 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) * we could show a placement plane here. * \{ */ -static void preview_plane_cursor_visible_set(wmGizmoGroup *UNUSED(gzgroup), - bool do_draw_plane, - bool do_draw_point) -{ - V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - - if (do_draw_point) { - ED_view3d_cursor_snap_activate_point(); - } - - if (do_draw_plane) { - ED_view3d_cursor_snap_activate_plane(); - } - else { - ED_view3d_cursor_snap_deactivate_plane(); - } - - if (!do_draw_point) { - ED_view3d_cursor_snap_deactivate_point(); - } - - if (!snap_data && (do_draw_point || do_draw_plane)) { - snap_data = ED_view3d_cursor_snap_data_get(); - UI_GetThemeColor3ubv(TH_TRANSFORM, snap_data->color_line); - snap_data->color_line[3] = 128; - rgba_uchar_args_set(snap_data->color_point, 255, 255, 255, 255); - } -} - static void preview_plane_free_fn(void *customdata) { - preview_plane_cursor_visible_set(customdata, false, false); + V3DSnapCursorState *snap_state = customdata; + ED_view3d_cursor_snap_deactive(snap_state); } static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) { - preview_plane_cursor_visible_set(gzgroup, true, true); - gzgroup->customdata = gzgroup; + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_active(); + snap_state->draw_plane = true; + + gzgroup->customdata = snap_state; gzgroup->customdata_free = preview_plane_free_fn; } -static void WIDGETGROUP_placement_draw_prepare(const bContext *C, wmGizmoGroup *UNUSED(gzgroup)) -{ - V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - if (!snap_data) { - return; - } - - PointerRNA ptr; - { - wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_interactive_add", true); - BLI_assert(ot != NULL); - - ScrArea *area = CTX_wm_area(C); - bToolRef *tref = area->runtime.tool; - WM_toolsystem_ref_properties_ensure_from_operator(tref, ot, &ptr); - } - - short snap_mode = 0; /* #toolsettings->snap_mode. */ - const enum ePlace_SnapTo snap_to = RNA_enum_get(&ptr, "snap_target"); - if (snap_to == PLACE_SNAP_TO_GEOMETRY) { - snap_mode = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT); - } - snap_data->snap_elem_force = snap_mode; - snap_data->plane_depth = (eV3DPlaceDepth)RNA_enum_get(&ptr, "plane_depth"); - snap_data->plane_orient = (eV3DPlaceOrient)RNA_enum_get(&ptr, "plane_orientation"); - snap_data->plane_axis = RNA_enum_get(&ptr, "plane_axis"); - snap_data->use_plane_axis_auto = RNA_boolean_get(&ptr, "plane_axis_auto"); -} - void VIEW3D_GGT_placement(wmGizmoGroupType *gzgt) { gzgt->name = "Placement Widget"; @@ -1487,7 +1526,6 @@ void VIEW3D_GGT_placement(wmGizmoGroupType *gzgt) gzgt->poll = ED_gizmo_poll_or_unlink_delayed_from_tool; gzgt->setup = WIDGETGROUP_placement_setup; - gzgt->draw_prepare = WIDGETGROUP_placement_draw_prepare; } /** \} */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index d4ef14bbf5d..d0693d37ef4 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -119,6 +119,7 @@ #include "ED_space_api.h" #include "ED_undo.h" #include "ED_util.h" +#include "ED_view3d.h" #include "BLF_api.h" #include "BLT_lang.h" @@ -547,6 +548,7 @@ void WM_exit_ex(bContext *C, const bool do_python) ED_preview_free_dbase(); /* frees a Main dbase, before BKE_blender_free! */ ED_assetlist_storage_exit(); + ED_view3d_cursor_snap_exit(); if (wm) { /* Before BKE_blender_free! - since the ListBases get freed there. */ From 4f15c247052b6db49b5226b6c473bdb7b2be6293 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 20 Oct 2021 13:16:12 +0200 Subject: [PATCH 0975/1500] Fix T62325, T91990: changing Cycles presets does not update the Blender UI --- intern/cycles/blender/addon/ui.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 598f6f083ac..0ed2dd24f2e 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -26,27 +26,31 @@ from bpy.types import Panel from bl_ui.properties_grease_pencil_common import GreasePencilSimplifyPanel from bl_ui.properties_view_layer import ViewLayerCryptomattePanel, ViewLayerAOVPanel +class CyclesPresetPanel(PresetPanel, Panel): + COMPAT_ENGINES = {'CYCLES'} + preset_operator = "script.execute_preset" -class CYCLES_PT_sampling_presets(PresetPanel, Panel): + @staticmethod + def post_cb(context): + # Modify an arbitrary built-in scene property to force a depsgraph + # update, because add-on properties don't. (see T62325) + render = context.scene.render + render.filter_size = render.filter_size + +class CYCLES_PT_sampling_presets(CyclesPresetPanel): bl_label = "Sampling Presets" preset_subdir = "cycles/sampling" - preset_operator = "script.execute_preset" preset_add_operator = "render.cycles_sampling_preset_add" - COMPAT_ENGINES = {'CYCLES'} -class CYCLES_PT_viewport_sampling_presets(PresetPanel, Panel): +class CYCLES_PT_viewport_sampling_presets(CyclesPresetPanel): bl_label = "Viewport Sampling Presets" preset_subdir = "cycles/viewport_sampling" - preset_operator = "script.execute_preset" preset_add_operator = "render.cycles_viewport_sampling_preset_add" - COMPAT_ENGINES = {'CYCLES'} -class CYCLES_PT_integrator_presets(PresetPanel, Panel): +class CYCLES_PT_integrator_presets(CyclesPresetPanel): bl_label = "Integrator Presets" preset_subdir = "cycles/integrator" - preset_operator = "script.execute_preset" preset_add_operator = "render.cycles_integrator_preset_add" - COMPAT_ENGINES = {'CYCLES'} class CyclesButtonsPanel: From d28aaf6139c8cfa8555542f4f228f390485dd7ed Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 20 Oct 2021 13:09:40 +0200 Subject: [PATCH 0976/1500] Asset Browser: Show current file assets in other asset libraries if contained If the current file is saved within an asset library, showing that asset library in the Asset Browser will also display the assets from this current file now. In fact, it's the latest state of the open file, including all unsaved modifications. These assets will show a little Blender icon in the preview image, which is our usual icon for current file data. Note that this means an important design change: The "Current File" asset library isn't the only place to edit assets from anymore. From now on assets from the current file can also be edited in the context of the full asset library. See T90193 for more info. Technical info: Besides just including the assets from the current `Main`, this requires partial clearing and reading of file-lists, so that asset operations (e.g. removing an asset data-block) doesn't require a full reload of the asset library. Maniphest Task: https://developer.blender.org/T90193 --- .../editors/asset/intern/asset_list.cc | 6 +- source/blender/editors/interface/interface.c | 3 + source/blender/editors/space_file/file_draw.c | 11 + source/blender/editors/space_file/filelist.c | 349 ++++++++++++++---- source/blender/editors/space_file/filelist.h | 2 + .../blender/editors/space_file/space_file.c | 18 +- 6 files changed, 293 insertions(+), 96 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index 400b3572c9b..4bc15e842fc 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -187,7 +187,7 @@ void AssetList::fetch(const bContext &C) if (filelist_needs_force_reset(files)) { filelist_readjob_stop(files, CTX_wm_manager(&C)); - filelist_clear(files); + filelist_clear_from_reset_tag(files); } if (filelist_needs_reading(files)) { @@ -295,9 +295,7 @@ int AssetList::size() const void AssetList::tagMainDataDirty() const { if (filelist_needs_reset_on_main_changes(filelist_)) { - /* Full refresh of the file list if local asset data was changed. Refreshing this view - * is cheap and users expect this to be updated immediately. */ - filelist_tag_force_reset(filelist_); + filelist_tag_force_reset_mainfiles(filelist_); } } diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index b47b63aa14c..b7458793f15 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -742,6 +742,9 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) if (but->optype != oldbut->optype) { return false; } + if (but->dragtype != oldbut->dragtype) { + return false; + } if ((but->type == UI_BTYPE_TREEROW) && (oldbut->type == UI_BTYPE_TREEROW)) { uiButTreeRow *but_treerow = (uiButTreeRow *)but; diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 9a46579780e..24b24eb81dd 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -465,6 +465,17 @@ static void file_draw_preview(const SpaceFile *sfile, UI_icon_draw_ex(icon_x, icon_y, icon, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false); } + const bool is_current_main_data = filelist_file_get_id(file) != NULL; + if (is_current_main_data) { + /* Smaller, fainter icon at the top-right indicating that the file represents data from the + * current file (from current #Main in fact). */ + float icon_x, icon_y; + const uchar light[4] = {255, 255, 255, 255}; + icon_x = xco + ex - UI_UNIT_X; + icon_y = yco + ey - UI_UNIT_Y; + UI_icon_draw_ex(icon_x, icon_y, ICON_FILE_BLEND, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false); + } + /* Contrasting outline around some preview types. */ if (show_outline) { GPUVertFormat *format = immVertexFormat(); diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index ab274fcea62..d329a8809c7 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -435,11 +435,14 @@ typedef struct FileList { /* FileList.flags */ enum { FL_FORCE_RESET = 1 << 0, - FL_IS_READY = 1 << 1, - FL_IS_PENDING = 1 << 2, - FL_NEED_SORTING = 1 << 3, - FL_NEED_FILTERING = 1 << 4, - FL_SORT_INVERT = 1 << 5, + /* Don't do a full reset (unless #FL_FORCE_RESET is also set), only reset files representing main + * data (assets from the current file/#Main). */ + FL_FORCE_RESET_MAIN_FILES = 1 << 1, + FL_IS_READY = 1 << 2, + FL_IS_PENDING = 1 << 3, + FL_NEED_SORTING = 1 << 4, + FL_NEED_FILTERING = 1 << 5, + FL_SORT_INVERT = 1 << 6, }; /* FileList.tags */ @@ -1375,6 +1378,11 @@ int ED_file_icon(const FileDirEntry *file) filelist_geticon_ex(file, NULL, false, false); } +static bool filelist_intern_entry_is_main_file(const FileListInternEntry *intern_entry) +{ + return intern_entry->local_data.id != NULL; +} + /* ********** Main ********** */ static void parent_dir_until_exists_or_default_root(char *dir) @@ -1503,6 +1511,26 @@ static void filelist_intern_free(FileListIntern *filelist_intern) MEM_SAFE_FREE(filelist_intern->filtered); } +/** + * \return the number of main files removed. + */ +static int filelist_intern_free_main_files(FileListIntern *filelist_intern) +{ + int removed_counter = 0; + LISTBASE_FOREACH_MUTABLE (FileListInternEntry *, entry, &filelist_intern->entries) { + if (!filelist_intern_entry_is_main_file(entry)) { + continue; + } + + BLI_remlink(&filelist_intern->entries, entry); + filelist_intern_entry_free(entry); + removed_counter++; + } + + MEM_SAFE_FREE(filelist_intern->filtered); + return removed_counter; +} + static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdata) { FileListEntryCache *cache = BLI_task_pool_user_data(pool); @@ -1803,6 +1831,7 @@ void filelist_settype(FileList *filelist, short type) filelist->read_job_fn = filelist_readjob_asset_library; filelist->prepare_filter_fn = prepare_filter_asset_library; filelist->filter_fn = is_filtered_asset_library; + filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA; break; case FILE_MAIN_ASSET: filelist->check_dir_fn = filelist_checkdir_main_assets; @@ -1821,6 +1850,12 @@ void filelist_settype(FileList *filelist, short type) filelist->flags |= FL_FORCE_RESET; } +static void filelist_clear_asset_library(FileList *filelist) +{ + /* The AssetLibraryService owns the AssetLibrary pointer, so no need for us to free it. */ + filelist->asset_library = NULL; +} + void filelist_clear_ex(struct FileList *filelist, const bool do_asset_library, const bool do_cache, @@ -1844,17 +1879,64 @@ void filelist_clear_ex(struct FileList *filelist, BLI_ghash_clear(filelist->selection_state, NULL, NULL); } - if (do_asset_library && (filelist->asset_library != NULL)) { - /* The AssetLibraryService owns the AssetLibrary pointer, so no need for us to free it. */ - filelist->asset_library = NULL; + if (do_asset_library) { + filelist_clear_asset_library(filelist); } } -void filelist_clear(struct FileList *filelist) +static void filelist_clear_main_files(FileList *filelist, + const bool do_asset_library, + const bool do_cache, + const bool do_selection) +{ + if (!filelist || !(filelist->tags & FILELIST_TAGS_USES_MAIN_DATA)) { + return; + } + + filelist_tag_needs_filtering(filelist); + + if (do_cache) { + filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size); + } + + const int removed_files = filelist_intern_free_main_files(&filelist->filelist_intern); + + filelist->filelist.nbr_entries -= removed_files; + filelist->filelist.nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET; + BLI_assert(filelist->filelist.nbr_entries > FILEDIR_NBR_ENTRIES_UNSET); + + if (do_selection && filelist->selection_state) { + BLI_ghash_clear(filelist->selection_state, NULL, NULL); + } + + if (do_asset_library) { + filelist_clear_asset_library(filelist); + } +} + +void filelist_clear(FileList *filelist) { filelist_clear_ex(filelist, true, true, true); } +/** + * A "smarter" version of #filelist_clear() that calls partial clearing based on the filelist + * force-reset flags. + */ +void filelist_clear_from_reset_tag(FileList *filelist) +{ + /* Do a full clear if needed. */ + if (filelist->flags & FL_FORCE_RESET) { + filelist_clear(filelist); + return; + } + + if (filelist->flags & FL_FORCE_RESET_MAIN_FILES) { + filelist_clear_main_files(filelist, false, true, false); + return; + } +} + void filelist_free(struct FileList *filelist) { if (!filelist) { @@ -1977,7 +2059,7 @@ void filelist_setrecursion(struct FileList *filelist, const int recursion_level) bool filelist_needs_force_reset(FileList *filelist) { - return (filelist->flags & FL_FORCE_RESET) != 0; + return (filelist->flags & (FL_FORCE_RESET | FL_FORCE_RESET_MAIN_FILES)) != 0; } void filelist_tag_force_reset(FileList *filelist) @@ -1985,6 +2067,14 @@ void filelist_tag_force_reset(FileList *filelist) filelist->flags |= FL_FORCE_RESET; } +void filelist_tag_force_reset_mainfiles(FileList *filelist) +{ + if (!(filelist->tags & FILELIST_TAGS_USES_MAIN_DATA)) { + return; + } + filelist->flags |= FL_FORCE_RESET_MAIN_FILES; +} + bool filelist_is_ready(struct FileList *filelist) { return (filelist->flags & FL_IS_READY) != 0; @@ -2714,9 +2804,10 @@ int ED_file_extension_icon(const char *path) } } -int filelist_needs_reading(struct FileList *filelist) +int filelist_needs_reading(FileList *filelist) { - return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET); + return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET) || + filelist_needs_force_reset(filelist); } uint filelist_entry_select_set(const FileList *filelist, @@ -3279,6 +3370,9 @@ typedef struct FileListReadJob { char main_name[FILE_MAX]; Main *current_main; struct FileList *filelist; + /** Set to request a partial read that only adds files representing #Main data (IDs). Used when + * #Main may have received changes of interest (e.g. asset removed or renamed). */ + bool only_main_data; /** Shallow copy of #filelist for thread-safe access. * @@ -3293,6 +3387,26 @@ typedef struct FileListReadJob { struct FileList *tmp_filelist; } FileListReadJob; +static void filelist_readjob_append_entries(FileListReadJob *job_params, + ListBase *from_entries, + int nbr_from_entries, + short *do_update) +{ + BLI_assert(BLI_listbase_count(from_entries) == nbr_from_entries); + if (nbr_from_entries <= 0) { + *do_update = false; + return; + } + + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + BLI_mutex_lock(&job_params->lock); + BLI_movelisttolist(&filelist->filelist.entries, from_entries); + filelist->filelist.nbr_entries += nbr_from_entries; + BLI_mutex_unlock(&job_params->lock); + + *do_update = true; +} + static bool filelist_readjob_should_recurse_into_entry(const int max_recursion, const bool is_lib, const int current_recursion_level, @@ -3329,11 +3443,11 @@ static bool filelist_readjob_should_recurse_into_entry(const int max_recursion, return true; } -static void filelist_readjob_do(const bool do_lib, - FileListReadJob *job_params, - const short *stop, - short *do_update, - float *progress) +static void filelist_readjob_recursive_dir_add_items(const bool do_lib, + FileListReadJob *job_params, + const short *stop, + short *do_update, + float *progress) { FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ ListBase entries = {0}; @@ -3345,13 +3459,6 @@ static void filelist_readjob_do(const bool do_lib, const int max_recursion = filelist->max_recursion; int nbr_done_dirs = 0, nbr_todo_dirs = 1; - // BLI_assert(filelist->filtered == NULL); - BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && - (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); - - /* A valid, but empty directory from now. */ - filelist->filelist.nbr_entries = 0; - todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__); td_dir = BLI_stack_push_r(todo_dirs); td_dir->level = 1; @@ -3439,16 +3546,7 @@ static void filelist_readjob_do(const bool do_lib, } } - if (nbr_entries) { - BLI_mutex_lock(&job_params->lock); - - *do_update = true; - - BLI_movelisttolist(&filelist->filelist.entries, &entries); - filelist->filelist.nbr_entries += nbr_entries; - - BLI_mutex_unlock(&job_params->lock); - } + filelist_readjob_append_entries(job_params, &entries, nbr_entries, do_update); nbr_done_dirs++; *progress = (float)nbr_done_dirs / (float)nbr_todo_dirs; @@ -3465,6 +3563,24 @@ static void filelist_readjob_do(const bool do_lib, BLI_stack_free(todo_dirs); } +static void filelist_readjob_do(const bool do_lib, + FileListReadJob *job_params, + const short *stop, + short *do_update, + float *progress) +{ + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + + // BLI_assert(filelist->filtered == NULL); + BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && + (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + + /* A valid, but empty directory from now. */ + filelist->filelist.nbr_entries = 0; + + filelist_readjob_recursive_dir_add_items(do_lib, job_params, stop, do_update, progress); +} + static void filelist_readjob_dir(FileListReadJob *job_params, short *stop, short *do_update, @@ -3502,57 +3618,41 @@ static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params { FileList *tmp_filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ - if (job_params->filelist->asset_library_ref != NULL) { - char library_root_path[FILE_MAX]; - filelist_asset_library_path(job_params, library_root_path); + *do_update = false; - /* Load asset catalogs, into the temp filelist for thread-safety. - * #filelist_readjob_endjob() will move it into the real filelist. */ - tmp_filelist->asset_library = BKE_asset_library_load(library_root_path); - *do_update = true; + if (job_params->filelist->asset_library_ref == NULL) { + return; } + if (tmp_filelist->asset_library != NULL) { + /* Asset library already loaded. */ + return; + } + + char library_root_path[FILE_MAX]; + filelist_asset_library_path(job_params, library_root_path); + + /* Load asset catalogs, into the temp filelist for thread-safety. + * #filelist_readjob_endjob() will move it into the real filelist. */ + tmp_filelist->asset_library = BKE_asset_library_load(library_root_path); + *do_update = true; } -static void filelist_readjob_asset_library(FileListReadJob *job_params, - short *stop, - short *do_update, - float *progress) -{ - filelist_readjob_load_asset_library_data(job_params, do_update); - filelist_readjob_lib(job_params, stop, do_update, progress); -} - -static void filelist_readjob_main(FileListReadJob *job_params, - short *stop, - short *do_update, - float *progress) -{ - /* TODO! */ - filelist_readjob_dir(job_params, stop, do_update, progress); -} - -/** - * \warning Acts on main, so NOT thread-safe! - */ -static void filelist_readjob_main_assets(FileListReadJob *job_params, - short *UNUSED(stop), - short *do_update, - float *UNUSED(progress)) +static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params, + short *UNUSED(stop), + short *do_update, + float *UNUSED(progress)) { FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ - BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && - (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); - - filelist_readjob_load_asset_library_data(job_params, do_update); - - /* A valid, but empty directory from now. */ - filelist->filelist.nbr_entries = 0; FileListInternEntry *entry; ListBase tmp_entries = {0}; ID *id_iter; int nbr_entries = 0; + /* Make sure no IDs are added/removed/reallocated in the main thread while this is running in + * parallel. */ + BKE_main_lock(job_params->current_main); + FOREACH_MAIN_ID_BEGIN (job_params->current_main, id_iter) { if (!id_iter->asset_data) { continue; @@ -3575,6 +3675,8 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params, } FOREACH_MAIN_ID_END; + BKE_main_unlock(job_params->current_main); + if (nbr_entries) { *do_update = true; @@ -3584,6 +3686,82 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params, } } +/** + * Check if \a bmain is stored within the root path of \a filelist. This means either directly or + * in some nested directory. In other words, it checks if the \a filelist root path is contained in + * the path to \a bmain. + * This is irrespective of the recursion level displayed, it basically assumes unlimited recursion + * levels. + */ +static bool filelist_contains_main(const FileList *filelist, const Main *bmain) +{ + const char *main_path = BKE_main_blendfile_path(bmain); + return main_path[0] && BLI_path_contains(filelist->filelist.root, main_path); +} + +static void filelist_readjob_asset_library(FileListReadJob *job_params, + short *stop, + short *do_update, + float *progress) +{ + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + + BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && + (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + + /* A valid, but empty file-list from now. */ + filelist->filelist.nbr_entries = 0; + + /* NOP if already read. */ + filelist_readjob_load_asset_library_data(job_params, do_update); + + if (filelist_contains_main(filelist, job_params->current_main)) { + filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress); + } + if (!job_params->only_main_data) { + filelist_readjob_recursive_dir_add_items(true, job_params, stop, do_update, progress); + } +} + +static void filelist_readjob_main(FileListReadJob *job_params, + short *stop, + short *do_update, + float *progress) +{ + /* TODO! */ + filelist_readjob_dir(job_params, stop, do_update, progress); +} + +static void filelist_readjob_main_assets(FileListReadJob *job_params, + short *stop, + short *do_update, + float *progress) +{ + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && + (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + + filelist_readjob_load_asset_library_data(job_params, do_update); + + /* A valid, but empty file-list from now. */ + filelist->filelist.nbr_entries = 0; + + filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress); +} + +/** + * Check if the read-job is requesting a partial reread of the file list only. + */ +static bool filelist_readjob_is_partial_read(const FileListReadJob *read_job) +{ + return read_job->only_main_data; +} + +/** + * \note This may trigger partial filelist reading. If the #FL_FORCE_RESET_MAIN_FILES flag is set, + * some current entries are kept and we just call the readjob to update the main files (see + * #FileListReadJob.only_main_data). + */ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update, float *progress) { FileListReadJob *flrj = flrjv; @@ -3594,8 +3772,6 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update BLI_mutex_lock(&flrj->lock); BLI_assert((flrj->tmp_filelist == NULL) && flrj->filelist); - BLI_assert_msg(flrj->filelist->asset_library == NULL, - "Asset library should not yet be assigned at start of read job"); flrj->tmp_filelist = MEM_dupallocN(flrj->filelist); @@ -3604,7 +3780,12 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update flrj->tmp_filelist->filelist_intern.filtered = NULL; BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries); - filelist_uid_unset(&flrj->tmp_filelist->filelist_intern.curr_uid); + if (filelist_readjob_is_partial_read(flrj)) { + /* Don't unset the current UID on partial read, would give duplicates otherwise. */ + } + else { + filelist_uid_unset(&flrj->tmp_filelist->filelist_intern.curr_uid); + } flrj->tmp_filelist->libfiledata = NULL; memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache)); @@ -3617,6 +3798,11 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update flrj->tmp_filelist->read_job_fn(flrj, stop, do_update, progress); } +/** + * \note This may update for a partial filelist reading job. If the #FL_FORCE_RESET_MAIN_FILES flag + * is set, some current entries are kept and we just call the readjob to update the main + * files (see #FileListReadJob.only_main_data). + */ static void filelist_readjob_update(void *flrjv) { FileListReadJob *flrj = flrjv; @@ -3638,7 +3824,11 @@ static void filelist_readjob_update(void *flrjv) if (flrj->tmp_filelist->asset_library) { flrj->filelist->asset_library = flrj->tmp_filelist->asset_library; - flrj->tmp_filelist->asset_library = NULL; /* MUST be NULL to avoid double-free. */ + } + + /* Important for partial reads: Copy increased UID counter back to the real list. */ + if (flrj->tmp_filelist->filelist_intern.curr_uid > fl_intern->curr_uid) { + fl_intern->curr_uid = flrj->tmp_filelist->filelist_intern.curr_uid; } BLI_mutex_unlock(&flrj->lock); @@ -3703,8 +3893,11 @@ void filelist_readjob_start(FileList *filelist, const int space_notifier, const flrj->filelist = filelist; flrj->current_main = bmain; BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name)); + if ((filelist->flags & FL_FORCE_RESET_MAIN_FILES) && !(filelist->flags & FL_FORCE_RESET)) { + flrj->only_main_data = true; + } - filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY); + filelist->flags &= ~(FL_FORCE_RESET | FL_FORCE_RESET_MAIN_FILES | FL_IS_READY); filelist->flags |= FL_IS_PENDING; /* Init even for single threaded execution. Called functions use it. */ diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index c2c1211b81c..0048a349dca 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -96,6 +96,7 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_asset_library, const bool do_cache, const bool do_selection); +void filelist_clear_from_reset_tag(struct FileList *filelist); void filelist_free(struct FileList *filelist); const char *filelist_dir(struct FileList *filelist); @@ -117,6 +118,7 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index); bool filelist_needs_force_reset(struct FileList *filelist); void filelist_tag_force_reset(struct FileList *filelist); +void filelist_tag_force_reset_mainfiles(struct FileList *filelist); bool filelist_pending(struct FileList *filelist); bool filelist_needs_reset_on_main_changes(const struct FileList *filelist); bool filelist_is_ready(struct FileList *filelist); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index c8bca22c166..2587c8f7f1f 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -303,15 +303,6 @@ static void file_ensure_valid_region_state(bContext *C, } } -/** - * Tag the space to recreate the file-list. - */ -static void file_tag_reset_list(ScrArea *area, SpaceFile *sfile) -{ - filelist_tag_force_reset(sfile->files); - ED_area_tag_refresh(area); -} - static void file_refresh(const bContext *C, ScrArea *area) { wmWindowManager *wm = CTX_wm_manager(C); @@ -326,7 +317,7 @@ static void file_refresh(const bContext *C, ScrArea *area) if (sfile->files && (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES) && filelist_needs_reset_on_main_changes(sfile->files)) { - filelist_tag_force_reset(sfile->files); + filelist_tag_force_reset_mainfiles(sfile->files); } sfile->tags &= ~FILE_TAG_REBUILD_MAIN_FILES; @@ -369,7 +360,7 @@ static void file_refresh(const bContext *C, ScrArea *area) if (filelist_needs_force_reset(sfile->files)) { filelist_readjob_stop(sfile->files, wm); - filelist_clear(sfile->files); + filelist_clear_from_reset_tag(sfile->files); } if (filelist_needs_reading(sfile->files)) { @@ -431,9 +422,8 @@ static void file_on_reload_callback_call(SpaceFile *sfile) static void file_reset_filelist_showing_main_data(ScrArea *area, SpaceFile *sfile) { if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) { - /* Full refresh of the file list if local asset data was changed. Refreshing this view - * is cheap and users expect this to be updated immediately. */ - file_tag_reset_list(area, sfile); + filelist_tag_force_reset_mainfiles(sfile->files); + ED_area_tag_refresh(area); } } From ba4e227def13b4b953775ff4a2fd9c154bb07751 Mon Sep 17 00:00:00 2001 From: Sayak Biswas Date: Wed, 20 Oct 2021 13:37:39 +0200 Subject: [PATCH 0977/1500] HIP device code cleanup and fix for high VRAM usage This patch cleans up code for HIP device and makes it more consistent with the CUDA code. It also fixes the issue with high VRAM usage on AMD cards using HIP allowing better performance and usage on cards like 6600XT. Added a check in intern/cycles/kernel/bvh/bvh_util.h to prevent compiler error with hipcc Reviewed By: brecht, leesonw Maniphest Tasks: T92124 Differential Revision: https://developer.blender.org/D12834 --- CMakeLists.txt | 2 +- extern/hipew/include/hipew.h | 6 +- intern/cycles/device/hip/device_impl.cpp | 142 +---------------------- intern/cycles/device/hip/queue.cpp | 82 ++++++++----- intern/cycles/device/hip/queue.h | 3 +- intern/cycles/kernel/bvh/bvh_util.h | 2 +- 6 files changed, 69 insertions(+), 168 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 544e22f342b..ac11b495948 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -430,7 +430,7 @@ mark_as_advanced(WITH_CYCLES_NATIVE_ONLY) option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles CUDA compute support" ON) option(WITH_CYCLES_DEVICE_OPTIX "Enable Cycles OptiX support" ON) -option(WITH_CYCLES_DEVICE_HIP "Enable Cycles HIP support" OFF) +option(WITH_CYCLES_DEVICE_HIP "Enable Cycles HIP support" ON) mark_as_advanced(WITH_CYCLES_DEVICE_HIP) mark_as_advanced(WITH_CYCLES_DEVICE_CUDA) diff --git a/extern/hipew/include/hipew.h b/extern/hipew/include/hipew.h index 02fffc331bf..aa42fdf8ecd 100644 --- a/extern/hipew/include/hipew.h +++ b/extern/hipew/include/hipew.h @@ -24,9 +24,13 @@ extern "C" { #include #define HIP_IPC_HANDLE_SIZE 64 +#define hipHostMallocDefault 0x00 #define hipHostMallocPortable 0x01 #define hipHostMallocMapped 0x02 #define hipHostMallocWriteCombined 0x04 +#define hipHostMallocNumaUser 0x20000000 +#define hipHostMallocCoherent 0x40000000 +#define hipHostMallocNonCoherent 0x80000000 #define hipHostRegisterPortable 0x01 #define hipHostRegisterMapped 0x02 #define hipHostRegisterIoMemory 0x04 @@ -989,7 +993,7 @@ typedef hipError_t HIPAPI thipMalloc(hipDeviceptr_t* dptr, size_t bytesize); typedef hipError_t HIPAPI thipMemAllocPitch(hipDeviceptr_t* dptr, size_t* pPitch, size_t WidthInBytes, size_t Height, unsigned int ElementSizeBytes); typedef hipError_t HIPAPI thipFree(hipDeviceptr_t dptr); typedef hipError_t HIPAPI thipMemGetAddressRange(hipDeviceptr_t* pbase, size_t* psize, hipDeviceptr_t dptr); -typedef hipError_t HIPAPI thipHostMalloc(void** pp, size_t bytesize); +typedef hipError_t HIPAPI thipHostMalloc(void** pp, size_t bytesize, unsigned int flags); typedef hipError_t HIPAPI thipHostFree(void* p); typedef hipError_t HIPAPI thipMemHostAlloc(void** pp, size_t bytesize, unsigned int Flags); typedef hipError_t HIPAPI thipHostGetDevicePointer(hipDeviceptr_t* pdptr, void* p, unsigned int Flags); diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp index 0e5ac6ce401..964783a08bf 100644 --- a/intern/cycles/device/hip/device_impl.cpp +++ b/intern/cycles/device/hip/device_impl.cpp @@ -108,6 +108,9 @@ HIPDevice::HIPDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler) return; } + /* hipDeviceMapHost for mapping host memory when out of device memory. + * hipDeviceLmemResizeToMax for reserving local memory ahead of render, + * so we can predict which memory to map to host. */ hip_assert(hipDeviceGetAttribute(&can_map_host, hipDeviceAttributeCanMapHostMemory, hipDevice)); hip_assert( @@ -657,7 +660,8 @@ HIPDevice::HIPMem *HIPDevice::generic_alloc(device_memory &mem, size_t pitch_pad } else if (map_host_used + size < map_host_limit) { /* Allocate host memory ourselves. */ - mem_alloc_result = hipHostMalloc(&shared_pointer, size); + mem_alloc_result = hipHostMalloc( + &shared_pointer, size, hipHostMallocMapped | hipHostMallocWriteCombined); assert((mem_alloc_result == hipSuccess && shared_pointer != 0) || (mem_alloc_result != hipSuccess && shared_pointer == 0)); @@ -874,7 +878,6 @@ void HIPDevice::const_copy_to(const char *name, void *host, size_t size) size_t bytes; hip_assert(hipModuleGetGlobal(&mem, &bytes, hipModule, name)); - assert(bytes == size); hip_assert(hipMemcpyHtoD(mem, host, size)); } @@ -1142,141 +1145,6 @@ void HIPDevice::tex_free(device_texture &mem) } } -# if 0 -void HIPDevice::render(DeviceTask &task, - RenderTile &rtile, - device_vector &work_tiles) -{ - scoped_timer timer(&rtile.buffers->render_time); - - if (have_error()) - return; - - HIPContextScope scope(this); - hipFunction_t hipRender; - - /* Get kernel function. */ - if (rtile.task == RenderTile::BAKE) { - hip_assert(hipModuleGetFunction(&hipRender, hipModule, "kernel_hip_bake")); - } - else { - hip_assert(hipModuleGetFunction(&hipRender, hipModule, "kernel_hip_path_trace")); - } - - if (have_error()) { - return; - } - - hip_assert(hipFuncSetCacheConfig(hipRender, hipFuncCachePreferL1)); - - /* Allocate work tile. */ - work_tiles.alloc(1); - - KernelWorkTile *wtile = work_tiles.data(); - wtile->x = rtile.x; - wtile->y = rtile.y; - wtile->w = rtile.w; - wtile->h = rtile.h; - wtile->offset = rtile.offset; - wtile->stride = rtile.stride; - wtile->buffer = (float *)(hipDeviceptr_t)rtile.buffer; - - /* Prepare work size. More step samples render faster, but for now we - * remain conservative for GPUs connected to a display to avoid driver - * timeouts and display freezing. */ - int min_blocks, num_threads_per_block; - hip_assert( - hipModuleOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, hipRender, NULL, 0, 0)); - if (!info.display_device) { - min_blocks *= 8; - } - - uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h); - - /* Render all samples. */ - uint start_sample = rtile.start_sample; - uint end_sample = rtile.start_sample + rtile.num_samples; - - for (int sample = start_sample; sample < end_sample;) { - /* Setup and copy work tile to device. */ - wtile->start_sample = sample; - wtile->num_samples = step_samples; - if (task.adaptive_sampling.use) { - wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples); - } - wtile->num_samples = min(wtile->num_samples, end_sample - sample); - work_tiles.copy_to_device(); - - hipDeviceptr_t d_work_tiles = (hipDeviceptr_t)work_tiles.device_pointer; - uint total_work_size = wtile->w * wtile->h * wtile->num_samples; - uint num_blocks = divide_up(total_work_size, num_threads_per_block); - - /* Launch kernel. */ - void *args[] = {&d_work_tiles, &total_work_size}; - - hip_assert( - hipModuleLaunchKernel(hipRender, num_blocks, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0)); - - /* Run the adaptive sampling kernels at selected samples aligned to step samples. */ - uint filter_sample = sample + wtile->num_samples - 1; - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) { - adaptive_sampling_filter(filter_sample, wtile, d_work_tiles); - } - - hip_assert(hipDeviceSynchronize()); - - /* Update progress. */ - sample += wtile->num_samples; - rtile.sample = sample; - task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); - - if (task.get_cancel()) { - if (task.need_finish_queue == false) - break; - } - } - - /* Finalize adaptive sampling. */ - if (task.adaptive_sampling.use) { - hipDeviceptr_t d_work_tiles = (hipDeviceptr_t)work_tiles.device_pointer; - adaptive_sampling_post(rtile, wtile, d_work_tiles); - hip_assert(hipDeviceSynchronize()); - task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); - } -} - -void HIPDevice::thread_run(DeviceTask &task) -{ - HIPContextScope scope(this); - - if (task.type == DeviceTask::RENDER) { - device_vector work_tiles(this, "work_tiles", MEM_READ_ONLY); - - /* keep rendering tiles until done */ - RenderTile tile; - DenoisingTask denoising(this, task); - - while (task.acquire_tile(this, tile, task.tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) { - render(task, tile, work_tiles); - } - else if (tile.task == RenderTile::BAKE) { - render(task, tile, work_tiles); - } - - task.release_tile(tile); - - if (task.get_cancel()) { - if (task.need_finish_queue == false) - break; - } - } - - work_tiles.free(); - } -} -# endif - unique_ptr HIPDevice::gpu_queue_create() { return make_unique(this); diff --git a/intern/cycles/device/hip/queue.cpp b/intern/cycles/device/hip/queue.cpp index 78c77e5fdae..0d9f5916d30 100644 --- a/intern/cycles/device/hip/queue.cpp +++ b/intern/cycles/device/hip/queue.cpp @@ -39,11 +39,30 @@ HIPDeviceQueue::~HIPDeviceQueue() hipStreamDestroy(hip_stream_); } -int HIPDeviceQueue::num_concurrent_states(const size_t /*state_size*/) const +int HIPDeviceQueue::num_concurrent_states(const size_t state_size) const { - /* TODO: compute automatically. */ - /* TODO: must have at least num_threads_per_block. */ - return 14416128; + int num_states = 0; + const int max_num_threads = hip_device_->get_num_multiprocessors() * + hip_device_->get_max_num_threads_per_multiprocessor(); + if (max_num_threads == 0) { + num_states = 1048576; // 65536 * 16 + } + else { + num_states = max_num_threads * 16; + } + + const char *factor_str = getenv("CYCLES_CONCURRENT_STATES_FACTOR"); + if (factor_str) { + float factor = atof(factor_str); + if (!factor) + VLOG(3) << "CYCLES_CONCURRENT_STATES_FACTOR evaluated to 0"; + num_states = max((int)(num_states * factor), 1024); + } + + VLOG(3) << "GPU queue concurrent states: " << num_states << ", using up to " + << string_human_readable_size(num_states * state_size); + + return num_states; } int HIPDeviceQueue::num_concurrent_busy_states() const @@ -105,18 +124,19 @@ bool HIPDeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *arg } /* Launch kernel. */ - hip_device_assert(hip_device_, - hipModuleLaunchKernel(hip_kernel.function, - num_blocks, - 1, - 1, - num_threads_per_block, - 1, - 1, - shared_mem_bytes, - hip_stream_, - args, - 0)); + assert_success(hipModuleLaunchKernel(hip_kernel.function, + num_blocks, + 1, + 1, + num_threads_per_block, + 1, + 1, + shared_mem_bytes, + hip_stream_, + args, + 0), + "enqueue"); + return !(hip_device_->have_error()); } @@ -127,7 +147,7 @@ bool HIPDeviceQueue::synchronize() } const HIPContextScope scope(hip_device_); - hip_device_assert(hip_device_, hipStreamSynchronize(hip_stream_)); + assert_success(hipStreamSynchronize(hip_stream_), "synchronize"); debug_synchronize(); return !(hip_device_->have_error()); @@ -150,9 +170,9 @@ void HIPDeviceQueue::zero_to_device(device_memory &mem) assert(mem.device_pointer != 0); const HIPContextScope scope(hip_device_); - hip_device_assert( - hip_device_, - hipMemsetD8Async((hipDeviceptr_t)mem.device_pointer, 0, mem.memory_size(), hip_stream_)); + assert_success( + hipMemsetD8Async((hipDeviceptr_t)mem.device_pointer, 0, mem.memory_size(), hip_stream_), + "zero_to_device"); } void HIPDeviceQueue::copy_to_device(device_memory &mem) @@ -173,10 +193,10 @@ void HIPDeviceQueue::copy_to_device(device_memory &mem) /* Copy memory to device. */ const HIPContextScope scope(hip_device_); - hip_device_assert( - hip_device_, + assert_success( hipMemcpyHtoDAsync( - (hipDeviceptr_t)mem.device_pointer, mem.host_pointer, mem.memory_size(), hip_stream_)); + (hipDeviceptr_t)mem.device_pointer, mem.host_pointer, mem.memory_size(), hip_stream_), + "copy_to_device"); } void HIPDeviceQueue::copy_from_device(device_memory &mem) @@ -192,13 +212,21 @@ void HIPDeviceQueue::copy_from_device(device_memory &mem) /* Copy memory from device. */ const HIPContextScope scope(hip_device_); - hip_device_assert( - hip_device_, + assert_success( hipMemcpyDtoHAsync( - mem.host_pointer, (hipDeviceptr_t)mem.device_pointer, mem.memory_size(), hip_stream_)); + mem.host_pointer, (hipDeviceptr_t)mem.device_pointer, mem.memory_size(), hip_stream_), + "copy_from_device"); +} + +void HIPDeviceQueue::assert_success(hipError_t result, const char *operation) +{ + if (result != hipSuccess) { + const char *name = hipewErrorString(result); + hip_device_->set_error( + string_printf("%s in HIP queue %s (%s)", name, operation, debug_active_kernels().c_str())); + } } -// TODO : (Arya) Enable this after stabilizing dev branch unique_ptr HIPDeviceQueue::graphics_interop_create() { return make_unique(this); diff --git a/intern/cycles/device/hip/queue.h b/intern/cycles/device/hip/queue.h index 04c8a5982ce..b92f7de7e4b 100644 --- a/intern/cycles/device/hip/queue.h +++ b/intern/cycles/device/hip/queue.h @@ -55,12 +55,13 @@ class HIPDeviceQueue : public DeviceQueue { return hip_stream_; } - // TODO : (Arya) Enable this after stabilizing the dev branch virtual unique_ptr graphics_interop_create() override; protected: HIPDevice *hip_device_; hipStream_t hip_stream_; + + void assert_success(hipError_t result, const char *operation); }; CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index 309f0eeb1e2..8686f887021 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -98,7 +98,7 @@ ccl_device_inline void sort_intersections_and_normals(ccl_private Intersection * for (int j = 0; j < num_hits - 1; ++j) { if (hits[j].t > hits[j + 1].t) { struct Intersection tmp_hit = hits[j]; - struct float3 tmp_Ng = Ng[j]; + float3 tmp_Ng = Ng[j]; hits[j] = hits[j + 1]; Ng[j] = Ng[j + 1]; hits[j + 1] = tmp_hit; From 40180c3500a8226d1ef3a9a55af9fda2f079f185 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 20 Oct 2021 09:07:26 -0300 Subject: [PATCH 0978/1500] Fix crash when reloading with placement tool enabled Paint Cursors are already released at this stage. --- source/blender/editors/space_view3d/view3d_cursor_snap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 06a37703648..9bbd90d2db6 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -879,7 +879,7 @@ static void v3d_cursor_snap_activate(void) static void v3d_cursor_snap_free(void) { SnapCursorDataIntern *data_intern = &g_data_intern; - if (data_intern->handle) { + if (data_intern->handle && G_MAIN->wm.first) { WM_paint_cursor_end(data_intern->handle); data_intern->handle = NULL; } From f855e2e06e19ec19b49d9084825202bc4e654f62 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 20 Oct 2021 09:18:44 -0300 Subject: [PATCH 0979/1500] Cleanup: unused function --- .../blender/editors/space_view3d/view3d_cursor_snap.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 9bbd90d2db6..b3e010bb938 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -856,16 +856,7 @@ static void v3d_cursor_snap_activate(void) RNA_enum_value_from_id(data_intern->keymap->modal_items, "SNAP_ON", &data_intern->snap_on); #endif V3DSnapCursorState *state_default = &data_intern->state_default; - state_default->prevpoint = NULL; - state_default->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | - SCE_SNAP_MODE_FACE | SCE_SNAP_MODE_EDGE_PERPENDICULAR | - SCE_SNAP_MODE_EDGE_MIDPOINT); - state_default->plane_axis = 2; - rgba_uchar_args_set(state_default->color_point, 255, 255, 255, 255); - UI_GetThemeColor3ubv(TH_TRANSFORM, state_default->color_line); - state_default->color_line[3] = 128; - state_default->draw_point = true; - state_default->draw_plane = false; + v3d_cursor_snap_state_init(state_default); data_intern->is_initiated = true; } From dfb193f634bf2d4b6a28b458d7d23b79bcd45633 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 20 Oct 2021 12:50:39 +0200 Subject: [PATCH 0980/1500] Fix T91243: Object modes are not loaded correctly in inactive scenes. Do not try to preserve edit modes of objects in non-active scenes (at least for now), except for Pose mode. Code was also slitghly refactored (reducing indent levels), and comments about expected behaviors and known limitations were added. --- source/blender/editors/util/ed_util.c | 81 +++++++++++++++++---------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 73f328f85d7..348deec1166 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -33,6 +33,7 @@ #include "BLT_translation.h" +#include "BKE_collection.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_material.h" @@ -121,48 +122,66 @@ void ED_editors_init(bContext *C) continue; } + /* Reset object to Object mode, so that code below can properly re-switch it to its + * previous mode if possible, re-creating its mode data, etc. */ ID *ob_data = ob->data; ob->mode = OB_MODE_OBJECT; DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - if (obact && (ob->type == obact->type) && !ID_IS_LINKED(ob) && - !(ob_data && ID_IS_LINKED(ob_data))) { - if (mode == OB_MODE_EDIT) { - ED_object_editmode_enter_ex(bmain, scene, ob, 0); - } - else if (mode == OB_MODE_POSE) { - ED_object_posemode_enter_ex(bmain, ob); - } - else if (mode & OB_MODE_ALL_SCULPT) { - if (obact == ob) { - if (mode == OB_MODE_SCULPT) { - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, reports); - } - else if (mode == OB_MODE_VERTEX_PAINT) { - ED_object_vpaintmode_enter_ex(bmain, depsgraph, scene, ob); - } - else if (mode == OB_MODE_WEIGHT_PAINT) { - ED_object_wpaintmode_enter_ex(bmain, depsgraph, scene, ob); - } - else { - BLI_assert_unreachable(); - } + + /* Object mode is enforced if there is no active object, or if the active object's type is + * different. */ + if (obact == NULL || ob->type != obact->type) { + continue; + } + /* Object mode is enforced for linked data (or their obdata). */ + if (ID_IS_LINKED(ob) || (ob_data != NULL && ID_IS_LINKED(ob_data))) { + continue; + } + + /* Pose mode is very similar to Object one, we can apply it even on objects not in current + * scene. */ + if (mode == OB_MODE_POSE) { + ED_object_posemode_enter_ex(bmain, ob); + } + + /* Other edit/paint/etc. modes are only settable for objects in active scene currently. */ + if (!BKE_collection_has_object_recursive(scene->master_collection, ob)) { + continue; + } + + if (mode == OB_MODE_EDIT) { + ED_object_editmode_enter_ex(bmain, scene, ob, 0); + } + else if (mode & OB_MODE_ALL_SCULPT) { + if (obact == ob) { + if (mode == OB_MODE_SCULPT) { + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, reports); + } + else if (mode == OB_MODE_VERTEX_PAINT) { + ED_object_vpaintmode_enter_ex(bmain, depsgraph, scene, ob); + } + else if (mode == OB_MODE_WEIGHT_PAINT) { + ED_object_wpaintmode_enter_ex(bmain, depsgraph, scene, ob); } else { - /* Create data for non-active objects which need it for - * mode-switching but don't yet support multi-editing. */ - if (mode & OB_MODE_ALL_SCULPT) { - ob->mode = mode; - BKE_object_sculpt_data_create(ob); - } + BLI_assert_unreachable(); } } else { - /* TODO(campbell): avoid operator calls. */ - if (obact == ob) { - ED_object_mode_set(C, mode); + /* Create data for non-active objects which need it for + * mode-switching but don't yet support multi-editing. */ + if (mode & OB_MODE_ALL_SCULPT) { + ob->mode = mode; + BKE_object_sculpt_data_create(ob); } } } + else { + /* TODO(campbell): avoid operator calls. */ + if (obact == ob) { + ED_object_mode_set(C, mode); + } + } } /* image editor paint mode */ From 3435ea014d42d1e223513f448cbdaba63864115c Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 20 Oct 2021 14:56:18 +0200 Subject: [PATCH 0981/1500] Cleanup: unused parameters, `nullptr` instead of `NULL` in cpp code... --- source/blender/blenkernel/intern/armature_test.cc | 2 +- .../blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c | 2 +- source/blender/editors/space_view3d/view3d_cursor_snap.c | 2 +- source/blender/editors/space_view3d/view3d_placement.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 3d22351e9a6..bd2088c6aba 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -309,7 +309,7 @@ static double test_vec_roll_to_mat3_orthogonal(double s, double x, double z) { const float input[3] = {float(x), float(s * sqrt(1 - x * x - z * z)), float(z)}; - return test_vec_roll_to_mat3_normalized(input, 0.0f, NULL); + return test_vec_roll_to_mat3_normalized(input, 0.0f, nullptr); } /** Test that the matrix is orthogonal for a range of inputs close to -Y. */ diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index b95935f2e06..13d9f1ecda7 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -100,7 +100,7 @@ bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *UNUSED(gz)) return snap_data->is_snap_invert; } -bool ED_gizmotypes_snap_3d_is_enabled(const wmGizmo *gz) +bool ED_gizmotypes_snap_3d_is_enabled(const wmGizmo *UNUSED(gz)) { V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); return snap_data->is_enabled; diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index b3e010bb938..4457afcb67f 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -754,7 +754,7 @@ static bool v3d_cursor_snap_pool_fn(bContext *C) return true; } -static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *customdata) +static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(customdata)) { SnapCursorDataIntern *data_intern = &g_data_intern; V3DSnapCursorState *state = ED_view3d_cursor_snap_state_get(); diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index bffcdcfc320..09d3aa31811 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -694,8 +694,8 @@ static void draw_primitive_view(const struct bContext *C, ARegion *UNUSED(region * Use by both the operator and placement cursor. * \{ */ -static bool view3d_interactive_add_calc_snap(bContext *C, - const wmEvent *event, +static bool view3d_interactive_add_calc_snap(bContext *UNUSED(C), + const wmEvent *UNUSED(event), float r_co_src[3], float r_matrix_orient[3][3], bool *r_is_enabled, From 2743d746ea4f38c098512f6dd6fc33d5a62429d3 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 20 Oct 2021 23:45:30 +1100 Subject: [PATCH 0982/1500] Cleanup: use an array for wmEvent cursor position variables Use arrays for wmEvent coordinates, this quiets warnings with GCC11. - `x, y` -> `xy`. - `prevx, prevy` -> `prev_xy`. - `prevclickx, prevclicky` -> `prev_click_xy`. There is still some cleanup such as using `copy_v2_v2_int()`, this can be done separately. Reviewed By: campbellbarton, Severin Ref D12901 --- .../blender/editors/animation/anim_markers.c | 15 +- .../blender/editors/animation/time_scrub_ui.c | 2 +- source/blender/editors/armature/pose_lib_2.c | 8 +- .../gizmo_library/gizmo_types/dial3d_gizmo.c | 2 +- .../gizmo_library/gizmo_types/snap3d_gizmo.c | 8 +- .../blender/editors/gpencil/annotate_paint.c | 2 +- source/blender/editors/gpencil/gpencil_fill.c | 4 +- .../editors/gpencil/gpencil_interpolate.c | 2 +- .../blender/editors/gpencil/gpencil_paint.c | 2 +- source/blender/editors/interface/interface.c | 2 +- .../interface/interface_context_menu.c | 6 +- .../editors/interface/interface_dropboxes.cc | 4 +- .../editors/interface/interface_eyedropper.c | 8 +- .../interface/interface_eyedropper_color.c | 10 +- .../interface_eyedropper_colorband.c | 12 +- .../interface_eyedropper_datablock.c | 4 +- .../interface/interface_eyedropper_depth.c | 8 +- .../interface_eyedropper_gpencil_color.c | 4 +- .../editors/interface/interface_handlers.c | 241 +++++++++--------- .../blender/editors/interface/interface_ops.c | 7 +- .../editors/interface/interface_panel.c | 20 +- .../editors/interface/interface_query.c | 11 +- .../interface/interface_region_menu_pie.c | 6 +- .../interface/interface_region_menu_popup.c | 8 +- .../interface/interface_region_popup.c | 2 +- .../interface/interface_region_search.c | 9 +- .../interface/interface_region_tooltip.c | 6 +- .../interface_template_search_menu.c | 4 +- source/blender/editors/interface/tree_view.cc | 2 +- .../editors/interface/view2d_edge_pan.c | 2 +- source/blender/editors/interface/view2d_ops.c | 60 ++--- .../blender/editors/mesh/editmesh_add_gizmo.c | 4 +- source/blender/editors/mesh/editmesh_bevel.c | 2 +- .../mesh/editmesh_extrude_spin_gizmo.c | 2 +- .../blender/editors/mesh/editmesh_loopcut.c | 4 +- .../editors/mesh/editmesh_mask_extract.c | 5 +- source/blender/editors/object/object_add.c | 8 +- .../blender/editors/render/render_internal.c | 2 +- source/blender/editors/render/render_opengl.c | 2 +- source/blender/editors/render/render_view.c | 2 +- source/blender/editors/screen/area.c | 4 +- source/blender/editors/screen/screen_edit.c | 4 +- source/blender/editors/screen/screen_ops.c | 80 +++--- source/blender/editors/screen/screendump.c | 3 +- .../editors/sculpt_paint/paint_image.c | 6 +- .../sculpt_paint/paint_vertex_weight_ops.c | 4 +- source/blender/editors/sculpt_paint/sculpt.c | 2 +- .../editors/sculpt_paint/sculpt_cloth.c | 2 +- .../editors/sculpt_paint/sculpt_detail.c | 2 +- .../sculpt_paint/sculpt_filter_color.c | 2 +- .../editors/sculpt_paint/sculpt_filter_mesh.c | 2 +- source/blender/editors/space_clip/clip_ops.c | 24 +- .../blender/editors/space_clip/tracking_ops.c | 4 +- .../editors/space_console/space_console.c | 2 +- source/blender/editors/space_file/file_ops.c | 2 +- .../blender/editors/space_file/space_file.c | 2 +- .../blender/editors/space_graph/graph_edit.c | 4 +- .../blender/editors/space_image/image_ops.c | 22 +- .../blender/editors/space_image/space_image.c | 2 +- .../blender/editors/space_node/node_draw.cc | 4 +- .../blender/editors/space_node/space_node.c | 4 +- .../spreadsheet_dataset_draw.cc | 3 +- .../blender/editors/space_text/space_text.c | 2 +- source/blender/editors/space_text/text_ops.c | 10 +- .../editors/space_view3d/space_view3d.c | 6 +- .../editors/space_view3d/view3d_edit.c | 71 +++--- .../space_view3d/view3d_navigate_fly.c | 2 +- source/blender/editors/transform/transform.c | 2 +- source/blender/editors/util/ed_draw.c | 14 +- source/blender/editors/util/ed_util_imbuf.c | 2 +- source/blender/makesrna/intern/rna_wm.c | 8 +- source/blender/makesrna/intern/rna_wm_api.c | 4 +- source/blender/windowmanager/WM_types.h | 14 +- .../windowmanager/gizmo/WM_gizmo_types.h | 2 +- .../windowmanager/gizmo/intern/wm_gizmo_map.c | 2 +- .../windowmanager/intern/wm_dragdrop.c | 7 +- source/blender/windowmanager/intern/wm_draw.c | 2 +- .../windowmanager/intern/wm_event_query.c | 12 +- .../windowmanager/intern/wm_event_system.c | 135 +++++----- .../blender/windowmanager/intern/wm_gesture.c | 12 +- .../windowmanager/intern/wm_gesture_ops.c | 58 ++--- .../windowmanager/intern/wm_operators.c | 20 +- .../blender/windowmanager/intern/wm_tooltip.c | 2 +- .../blender/windowmanager/intern/wm_window.c | 20 +- 84 files changed, 563 insertions(+), 540 deletions(-) diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 0de3f429bc7..e12433f4d4c 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -872,7 +872,7 @@ static int ed_marker_move_invoke(bContext *C, wmOperator *op, const wmEvent *eve ARegion *region = CTX_wm_region(C); View2D *v2d = ®ion->v2d; ListBase *markers = ED_context_get_markers(C); - if (!region_position_is_over_marker(v2d, markers, event->x - region->winrct.xmin)) { + if (!region_position_is_over_marker(v2d, markers, event->xy[0] - region->winrct.xmin)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } } @@ -880,8 +880,8 @@ static int ed_marker_move_invoke(bContext *C, wmOperator *op, const wmEvent *eve if (ed_marker_move_init(C, op)) { MarkerMove *mm = op->customdata; - mm->evtx = event->x; - mm->firstx = event->x; + mm->evtx = event->xy[0]; + mm->firstx = event->xy[0]; mm->event_type = event->type; /* add temp handler */ @@ -993,11 +993,11 @@ static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *even dx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask); - if (event->x != mm->evtx) { /* XXX maybe init for first time */ + if (event->xy[0] != mm->evtx) { /* XXX maybe init for first time */ float fac; - mm->evtx = event->x; - fac = ((float)(event->x - mm->firstx) * dx); + mm->evtx = event->xy[0]; + fac = ((float)(event->xy[0] - mm->firstx) * dx); apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, FPS, 0.1 * FPS, 0); @@ -1354,7 +1354,8 @@ static int ed_marker_box_select_invoke(bContext *C, wmOperator *op, const wmEven View2D *v2d = ®ion->v2d; ListBase *markers = ED_context_get_markers(C); - bool over_marker = region_position_is_over_marker(v2d, markers, event->x - region->winrct.xmin); + bool over_marker = region_position_is_over_marker( + v2d, markers, event->xy[0] - region->winrct.xmin); bool tweak = RNA_boolean_get(op->ptr, "tweak"); if (tweak && over_marker) { diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 94366a5e852..f3cfbabd544 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -208,7 +208,7 @@ bool ED_time_scrub_event_in_region(const ARegion *region, const wmEvent *event) { rcti rect = region->winrct; rect.ymin = rect.ymax - UI_TIME_SCRUB_MARGIN_Y; - return BLI_rcti_isect_pt(&rect, event->x, event->y); + return BLI_rcti_isect_pt(&rect, event->xy[0], event->xy[1]); } void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *region, bDopeSheet *dopesheet) diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c index 91a5dc67a21..328ca0265c1 100644 --- a/source/blender/editors/armature/pose_lib_2.c +++ b/source/blender/editors/armature/pose_lib_2.c @@ -209,11 +209,11 @@ static void poselib_slide_mouse_update_blendfactor(PoseBlendData *pbd, const wmE if (pbd->release_confirm_info.use_release_confirm) { /* Release confirm calculates factor based on where the dragging was started from. */ const float range = 300 * U.pixelsize; - const float new_factor = (event->x - pbd->release_confirm_info.drag_start_xy[0]) / range; + const float new_factor = (event->xy[0] - pbd->release_confirm_info.drag_start_xy[0]) / range; poselib_blend_set_factor(pbd, new_factor); } else { - const float new_factor = (event->x - pbd->area->v1->vec.x) / ((float)pbd->area->winx); + const float new_factor = (event->xy[0] - pbd->area->v1->vec.x) / ((float)pbd->area->winx); poselib_blend_set_factor(pbd, new_factor); } } @@ -379,8 +379,8 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent * if (pbd->release_confirm_info.use_release_confirm) { BLI_assert(event != NULL); - pbd->release_confirm_info.drag_start_xy[0] = event->x; - pbd->release_confirm_info.drag_start_xy[1] = event->y; + pbd->release_confirm_info.drag_start_xy[0] = event->xy[0]; + pbd->release_confirm_info.drag_start_xy[1] = event->xy[1]; pbd->release_confirm_info.init_event_type = WM_userdef_event_type_from_keymap_type( event->type); } diff --git a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c index 6345cd3525a..d4d4c889209 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c @@ -307,7 +307,7 @@ static void dial_ghostarc_get_angles(const wmGizmo *gz, { DialInteraction *inter = gz->interaction_data; const RegionView3D *rv3d = region->regiondata; - const float mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; + const float mval[2] = {event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin}; /* We might need to invert the direction of the angles. */ float view_vec[3], axis_vec[3]; diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index 13d9f1ecda7..33532bd0549 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -120,8 +120,8 @@ void ED_gizmotypes_snap_3d_data_get(const struct bContext *C, const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; if (event) { ARegion *region = CTX_wm_region(C); - int x = event->x - region->winrct.xmin; - int y = event->y - region->winrct.ymin; + int x = event->xy[0] - region->winrct.xmin; + int y = event->xy[1] - region->winrct.ymin; snap_data = ED_view3d_cursor_snap_data_get(NULL, C, x, y); } } @@ -250,8 +250,8 @@ static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2]) const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; if (event) { ARegion *region = CTX_wm_region(C); - x = event->x - region->winrct.xmin; - y = event->y - region->winrct.ymin; + x = event->xy[0] - region->winrct.xmin; + y = event->xy[1] - region->winrct.ymin; } else { x = mval[0]; diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 8924865f5e1..ba603cdd6ec 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -2560,7 +2560,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve if ((p->region) && (p->region->regiontype == RGN_TYPE_TOOLS)) { /* Change to whatever region is now under the mouse */ ARegion *current_region = BKE_area_find_region_xy( - p->area, RGN_TYPE_ANY, event->x, event->y); + p->area, RGN_TYPE_ANY, event->xy[0], event->xy[1]); if (current_region) { /* Assume that since we found the cursor in here, it is in bounds diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index f5474a7cdc3..52487e07f53 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -2103,11 +2103,11 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) /* first time the event is not enabled to show help lines. */ if ((tgpf->oldkey != -1) || (!help_lines)) { ARegion *region = BKE_area_find_region_xy( - CTX_wm_area(C), RGN_TYPE_ANY, event->x, event->y); + CTX_wm_area(C), RGN_TYPE_ANY, event->xy[0], event->xy[1]); if (region) { bool in_bounds = false; /* Perform bounds check */ - in_bounds = BLI_rcti_isect_pt(®ion->winrct, event->x, event->y); + in_bounds = BLI_rcti_isect_pt(®ion->winrct, event->xy[0], event->xy[1]); if ((in_bounds) && (region->regiontype == RGN_TYPE_WINDOW)) { tgpf->mouse[0] = event->mval[0]; diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index d7cab85abad..22127d7ac3b 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -586,7 +586,7 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) static void gpencil_mouse_update_shift(tGPDinterpolate *tgpi, wmOperator *op, const wmEvent *event) { float mid = (float)(tgpi->region->winx - tgpi->region->winrct.xmin) / 2.0f; - float mpos = event->x - tgpi->region->winrct.xmin; + float mpos = event->xy[0] - tgpi->region->winrct.xmin; if (mpos >= mid) { tgpi->shift = ((mpos - mid) * tgpi->high_limit) / mid; diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 2aa4b3b3282..070470b3734 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -3666,7 +3666,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) if ((p->region) && (p->region->regiontype == RGN_TYPE_TOOLS)) { /* Change to whatever region is now under the mouse */ ARegion *current_region = BKE_area_find_region_xy( - p->area, RGN_TYPE_ANY, event->x, event->y); + p->area, RGN_TYPE_ANY, event->xy[0], event->xy[1]); if (current_region) { /* Assume that since we found the cursor in here, it is in bounds diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index b7458793f15..fe4caf03184 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1988,7 +1988,7 @@ void UI_block_end(const bContext *C, uiBlock *block) { wmWindow *window = CTX_wm_window(C); - UI_block_end_ex(C, block, &window->eventstate->x, NULL); + UI_block_end_ex(C, block, window->eventstate->xy, NULL); } /* ************** BLOCK DRAWING FUNCTION ************* */ diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index d327124484b..7d5295aa758 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -928,7 +928,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev { const ARegion *region = CTX_wm_region(C); uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over( - region, event->x, event->y); + region, event->xy[0], event->xy[1]); if (treerow_but) { BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW); UI_tree_view_item_context_menu_build( @@ -1216,8 +1216,8 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev ARegion *region = CTX_wm_region(C); const bool is_inside_listbox = ui_list_find_mouse_over(region, event) != NULL; const bool is_inside_listrow = is_inside_listbox ? - ui_list_row_find_mouse_over(region, event->x, event->y) != - NULL : + ui_list_row_find_mouse_over( + region, event->xy[0], event->xy[1]) != NULL : false; if (is_inside_listrow) { MenuType *mt = WM_menutype_find("UI_MT_list_item_context_menu", true); diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index cb33e7f736e..34bd5f17881 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -30,7 +30,7 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve { const ARegion *region = CTX_wm_region(C); const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, event->x, event->y); + region, event->xy[0], event->xy[1]); if (!hovered_tree_item) { return false; } @@ -45,7 +45,7 @@ static char *ui_tree_view_drop_tooltip(bContext *C, { const ARegion *region = CTX_wm_region(C); const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, event->x, event->y); + region, event->xy[0], event->xy[1]); if (!hovered_tree_item) { return nullptr; } diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index 58a9f362488..395ecc77ef4 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -126,8 +126,8 @@ void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const cha return; } - const int x = window->eventstate->x; - const int y = window->eventstate->y; + const int x = window->eventstate->xy[0]; + const int y = window->eventstate->xy[1]; eyedropper_draw_cursor_text_ex(x, y, name); } @@ -153,8 +153,8 @@ void eyedropper_draw_cursor_text_region(const int x, const int y, const char *na uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event) { bScreen *screen = CTX_wm_screen(C); - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); - const ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, event->x, event->y); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy[0], event->xy[1]); + const ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, event->xy[0], event->xy[1]); uiBut *but = ui_but_find_mouse_over(region, event); diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 9d06fb2b27a..c3633e11f83 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -481,7 +481,7 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = eye->is_undo; if (eye->accum_tot == 0) { - eyedropper_color_sample(C, eye, event->x, event->y); + eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); } eyedropper_exit(C, op); /* Could support finished & undo-skip. */ @@ -490,23 +490,23 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ eye->accum_start = true; - eyedropper_color_sample(C, eye, event->x, event->y); + eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); break; case EYE_MODAL_SAMPLE_RESET: eye->accum_tot = 0; zero_v3(eye->accum_col); - eyedropper_color_sample(C, eye, event->x, event->y); + eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); break; } } else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { if (eye->accum_start) { /* button is pressed so keep sampling */ - eyedropper_color_sample(C, eye, event->x, event->y); + eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); } if (eye->draw_handle_sample_text) { - eyedropper_color_sample_text_update(C, eye, event->x, event->y); + eyedropper_color_sample_text_update(C, eye, event->xy[0], event->xy[1]); ED_region_tag_redraw(CTX_wm_region(C)); } } diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/interface_eyedropper_colorband.c index d32eb415b19..22320282766 100644 --- a/source/blender/editors/interface/interface_eyedropper_colorband.c +++ b/source/blender/editors/interface/interface_eyedropper_colorband.c @@ -233,7 +233,7 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = eye->is_undo; - eyedropper_colorband_sample_segment(C, eye, event->x, event->y); + eyedropper_colorband_sample_segment(C, eye, event->xy[0], event->xy[1]); eyedropper_colorband_apply(C, op); eyedropper_colorband_exit(C, op); /* Could support finished & undo-skip. */ @@ -242,10 +242,10 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ eye->sample_start = true; - eyedropper_colorband_sample_point(C, eye, event->x, event->y); + eyedropper_colorband_sample_point(C, eye, event->xy[0], event->xy[1]); eyedropper_colorband_apply(C, op); - eye->last_x = event->x; - eye->last_y = event->y; + eye->last_x = event->xy[0]; + eye->last_y = event->xy[1]; break; case EYE_MODAL_SAMPLE_RESET: break; @@ -253,7 +253,7 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent } else if (event->type == MOUSEMOVE) { if (eye->sample_start) { - eyedropper_colorband_sample_segment(C, eye, event->x, event->y); + eyedropper_colorband_sample_segment(C, eye, event->xy[0], event->xy[1]); eyedropper_colorband_apply(C, op); } } @@ -280,7 +280,7 @@ static int eyedropper_colorband_point_modal(bContext *C, wmOperator *op, const w } break; case EYE_MODAL_POINT_SAMPLE: - eyedropper_colorband_sample_point(C, eye, event->x, event->y); + eyedropper_colorband_sample_point(C, eye, event->xy[0], event->xy[1]); eyedropper_colorband_apply(C, op); if (eye->color_buffer_len == MAXCOLORBAND) { eyedropper_colorband_exit(C, op); diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index 4996c803dfe..261aa997d06 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -292,7 +292,7 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = ddr->is_undo; - const bool success = datadropper_id_sample(C, ddr, event->x, event->y); + const bool success = datadropper_id_sample(C, ddr, event->xy[0], event->xy[1]); datadropper_exit(C, op); if (success) { /* Could support finished & undo-skip. */ @@ -309,7 +309,7 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) wmWindow *win; ScrArea *area; - int mval[] = {event->x, event->y}; + int mval[] = {event->xy[0], event->xy[1]}; datadropper_win_area_find(C, mval, mval, &win, &area); /* Set the region for eyedropper cursor text drawing */ diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c index 311f718d950..4172c474f4a 100644 --- a/source/blender/editors/interface/interface_eyedropper_depth.c +++ b/source/blender/editors/interface/interface_eyedropper_depth.c @@ -276,7 +276,7 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = ddr->is_undo; if (ddr->accum_tot == 0) { - depthdropper_depth_sample(C, ddr, event->x, event->y); + depthdropper_depth_sample(C, ddr, event->xy[0], event->xy[1]); } else { depthdropper_depth_set_accum(C, ddr); @@ -288,12 +288,12 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ ddr->accum_start = true; - depthdropper_depth_sample_accum(C, ddr, event->x, event->y); + depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]); break; case EYE_MODAL_SAMPLE_RESET: ddr->accum_tot = 0; ddr->accum_depth = 0.0f; - depthdropper_depth_sample_accum(C, ddr, event->x, event->y); + depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]); depthdropper_depth_set_accum(C, ddr); break; } @@ -301,7 +301,7 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) else if (event->type == MOUSEMOVE) { if (ddr->accum_start) { /* button is pressed so keep sampling */ - depthdropper_depth_sample_accum(C, ddr, event->x, event->y); + depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]); depthdropper_depth_set_accum(C, ddr); } } diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c index 417807afff1..d76ff84bcad 100644 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c @@ -292,7 +292,7 @@ static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent * return OPERATOR_CANCELLED; } case EYE_MODAL_SAMPLE_CONFIRM: { - eyedropper_gpencil_color_sample(C, eye, event->x, event->y); + eyedropper_gpencil_color_sample(C, eye, event->xy[0], event->xy[1]); /* Create material. */ eyedropper_gpencil_color_set(C, event, eye); @@ -309,7 +309,7 @@ static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent * } case MOUSEMOVE: case INBETWEEN_MOUSEMOVE: { - eyedropper_gpencil_color_sample(C, eye, event->x, event->y); + eyedropper_gpencil_color_sample(C, eye, event->xy[0], event->xy[1]); break; } default: { diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 81494264cdf..129f3143451 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -358,7 +358,7 @@ typedef struct uiHandleButtonMulti { * here so we can tell if this is a vertical motion or not. */ float drag_dir[2]; - /* values copied direct from event->x,y + /* values copied direct from event->xy * used to detect buttons between the current and initial mouse position */ int drag_start[2]; @@ -1426,8 +1426,8 @@ static bool ui_multibut_states_tag(uiBut *but_active, seg[0][0] = data->multi_data.drag_start[0]; seg[0][1] = data->multi_data.drag_start[1]; - seg[1][0] = event->x; - seg[1][1] = event->y; + seg[1][0] = event->xy[0]; + seg[1][1] = event->xy[1]; BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_SETUP); @@ -1746,7 +1746,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void break; } case MOUSEMOVE: { - ui_drag_toggle_set(C, drag_info, &event->x); + ui_drag_toggle_set(C, drag_info, event->xy); break; } } @@ -2064,7 +2064,8 @@ static bool ui_but_drag_init(bContext *C, WM_event_drag_threshold(event), (int)((UI_UNIT_Y / 2) * ui_block_to_window_scale(data->region, but->block))); - if (abs(data->dragstartx - event->x) + abs(data->dragstarty - event->y) > drag_threshold) { + if (abs(data->dragstartx - event->xy[0]) + abs(data->dragstarty - event->xy[1]) > + drag_threshold) { button_activate_state(C, but, BUTTON_STATE_EXIT); data->cancel = true; #ifdef USE_DRAG_TOGGLE @@ -2079,8 +2080,8 @@ static bool ui_but_drag_init(bContext *C, drag_info->pushed_state = ui_drag_toggle_but_pushed_state(but); drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect); drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect); - copy_v2_v2_int(drag_info->xy_init, &event->x); - copy_v2_v2_int(drag_info->xy_last, &event->x); + copy_v2_v2_int(drag_info->xy_init, &event->xy[0]); + copy_v2_v2_int(drag_info->xy_last, &event->xy[0]); /* needed for toggle drag on popups */ region_prev = CTX_wm_region(C); @@ -3421,9 +3422,9 @@ static void ui_textedit_ime_begin(wmWindow *win, uiBut *UNUSED(but)) BLI_assert(win->ime_data == NULL); /* enable IME and position to cursor, it's a trick */ - x = win->eventstate->x; + x = win->eventstate->xy[0]; /* flip y and move down a bit, prevent the IME panel cover the edit button */ - y = win->eventstate->y - 12; + y = win->eventstate->xy[1] - 12; wm_window_IME_begin(win, x, y, 0, 0, true); } @@ -3737,18 +3738,18 @@ static void ui_do_but_textedit( /* exit on LMB only on RELEASE for searchbox, to mimic other popups, * and allow multiple menu levels */ if (data->searchbox) { - inbox = ui_searchbox_inside(data->searchbox, event->x, event->y); + inbox = ui_searchbox_inside(data->searchbox, event->xy[0], event->xy[1]); } /* for double click: we do a press again for when you first click on button * (selects all text, no cursor pos) */ if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) { - float mx = event->x; - float my = event->y; + float mx = event->xy[0]; + float my = event->xy[1]; ui_window_to_block_fl(data->region, block, &mx, &my); if (ui_but_contains_pt(but, mx, my)) { - ui_textedit_set_cursor_pos(but, data, event->x); + ui_textedit_set_cursor_pos(but, data, event->xy[0]); but->selsta = but->selend = but->pos; data->sel_pos_init = but->pos; @@ -4019,11 +4020,11 @@ static void ui_do_but_textedit_select( switch (event->type) { case MOUSEMOVE: { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); - ui_textedit_set_cursor_select(but, data, event->x); + ui_textedit_set_cursor_select(but, data, event->xy[0]); retval = WM_UI_HANDLER_BREAK; break; } @@ -4359,7 +4360,8 @@ static uiBut *ui_but_list_row_text_activate(bContext *C, uiButtonActivateType activate_type) { ARegion *region = CTX_wm_region(C); - uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true, NULL, NULL); + uiBut *labelbut = ui_but_find_mouse_over_ex( + region, event->xy[0], event->xy[1], true, NULL, NULL); if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) { /* exit listrow */ @@ -4386,7 +4388,7 @@ static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but, { float xmax = but->rect.xmax; const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */ - int x = event->x, y = event->y; + int x = event->xy[0], y = event->xy[1]; ui_window_to_block(data->region, but->block, &x, &y); if (!BLI_rctf_isect_pt(&but->rect, x, y)) { @@ -4476,8 +4478,8 @@ static bool ui_do_but_ANY_drag_toggle( # endif ui_apply_but(C, but->block, but, data, true); button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; *r_retval = WM_UI_HANDLER_BREAK; return true; } @@ -4563,7 +4565,8 @@ static int ui_do_but_HOTKEYEVT(bContext *C, if (event->type == LEFTMOUSE && event->val == KM_PRESS) { /* only cancel if click outside the button */ - if (ui_but_contains_point_px(but, but->active->region, event->x, event->y) == false) { + if (ui_but_contains_point_px(but, but->active->region, event->xy[0], event->xy[1]) == + false) { /* data->cancel doesn't work, this button opens immediate */ if (but->flag & UI_BUT_IMMEDIATE) { ui_but_value_set(but, 0); @@ -4860,16 +4863,16 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con /* tell the button to wait and keep checking further events to * see if it should start dragging */ button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_CONTINUE; } } #ifdef USE_DRAG_TOGGLE if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && ui_but_is_drag_toggle(but)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_CONTINUE; } #endif @@ -5291,8 +5294,8 @@ static void ui_numedit_set_active(uiBut *but) BLI_rctf_size_y(&but->rect) * 0.7f); /* we can click on the side arrows to increment/decrement, * or click inside to edit the value directly */ - int mx = data->window->eventstate->x; - int my = data->window->eventstate->y; + int mx = data->window->eventstate->xy[0]; + int my = data->window->eventstate->xy[1]; ui_window_to_block(data->region, but->block, &mx, &my); if (mx < (but->rect.xmin + handle_width)) { @@ -5332,10 +5335,10 @@ static int ui_do_but_NUM( int retval = WM_UI_HANDLER_CONTINUE; /* mouse location scaled to fit the UI */ - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; /* mouse location kept at screen pixel coords */ - const int screen_mx = event->x; + const int screen_mx = event->xy[0]; BLI_assert(but->type == UI_BTYPE_NUM); @@ -5387,7 +5390,7 @@ static int ui_do_but_NUM( } #ifdef USE_DRAG_MULTINUM - copy_v2_v2_int(data->multi_data.drag_start, &event->x); + copy_v2_v2_int(data->multi_data.drag_start, event->xy); #endif } } @@ -5681,8 +5684,8 @@ static int ui_do_but_SLI( int click = 0; int retval = WM_UI_HANDLER_CONTINUE; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5741,7 +5744,7 @@ static int ui_do_but_SLI( } } #ifdef USE_DRAG_MULTINUM - copy_v2_v2_int(data->multi_data.drag_start, &event->x); + copy_v2_v2_int(data->multi_data.drag_start, event->xy); #endif } else if (data->state == BUTTON_STATE_NUM_EDITING) { @@ -5901,8 +5904,8 @@ static int ui_do_but_SCROLL( int retval = WM_UI_HANDLER_CONTINUE; const bool horizontal = (BLI_rctf_size_x(&but->rect) > BLI_rctf_size_y(&but->rect)); - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5958,15 +5961,15 @@ static int ui_do_but_GRIP( * See T37739. */ - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->val == KM_PRESS) { if (event->type == LEFTMOUSE) { - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); retval = WM_UI_HANDLER_BREAK; } @@ -6029,16 +6032,16 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) { if (ui_but_contains_point_px_icon(but, data->region, event)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_BREAK; } } #ifdef USE_DRAG_TOGGLE if (event->type == LEFTMOUSE && event->val == KM_PRESS && (ui_but_is_drag_toggle(but))) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_BREAK; } #endif @@ -6215,8 +6218,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co ui_palette_set_active(color_but); if (ui_but_contains_point_px_icon(but, data->region, event)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_BREAK; } } @@ -6224,8 +6227,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co if (event->type == LEFTMOUSE && event->val == KM_PRESS) { ui_palette_set_active(color_but); button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_BREAK; } #endif @@ -6251,7 +6254,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co hsv[2] = clamp_f(hsv[2] + 0.05f, 0.0f, 1.0f); } else { - const float fac = 0.005 * (event->y - event->prevy); + const float fac = 0.005 * (event->xy[1] - event->prev_xy[1]); hsv[2] = clamp_f(hsv[2] + fac, 0.0f, 1.0f); } @@ -6355,8 +6358,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co static int ui_do_but_UNITVEC( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -6649,8 +6652,8 @@ static int ui_do_but_HSVCUBE( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { uiButHSVCube *hsv_but = (uiButHSVCube *)but; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -6925,8 +6928,8 @@ static int ui_do_but_HSVCIRCLE( { ColorPicker *cpicker = but->custom_data; float *hsv = cpicker->hsv_perceptual; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7054,8 +7057,8 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m static int ui_do_but_COLORBAND( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7250,8 +7253,8 @@ static int ui_do_but_CURVE( Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7343,10 +7346,10 @@ static int ui_do_but_CURVE( data->dragsel = sel; - data->dragstartx = event->x; - data->dragstarty = event->y; - data->draglastx = event->x; - data->draglasty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; + data->draglastx = event->xy[0]; + data->draglasty = event->xy[1]; button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); return WM_UI_HANDLER_BREAK; @@ -7354,10 +7357,15 @@ static int ui_do_but_CURVE( } else if (data->state == BUTTON_STATE_NUM_EDITING) { if (event->type == MOUSEMOVE) { - if (event->x != data->draglastx || event->y != data->draglasty) { + if (event->xy[0] != data->draglastx || event->xy[1] != data->draglasty) { - if (ui_numedit_but_CURVE( - block, but, data, event->x, event->y, event->ctrl != 0, event->shift != 0)) { + if (ui_numedit_but_CURVE(block, + but, + data, + event->xy[0], + event->xy[1], + event->ctrl != 0, + event->shift != 0)) { ui_numedit_apply(C, block, but, data); } } @@ -7531,8 +7539,8 @@ static int ui_do_but_CURVEPROFILE( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { CurveProfile *profile = (CurveProfile *)but->poin; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); @@ -7714,8 +7722,8 @@ static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int m static int ui_do_but_HISTOGRAM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7787,8 +7795,8 @@ static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx static int ui_do_but_WAVEFORM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7878,8 +7886,8 @@ static bool ui_numedit_but_TRACKPREVIEW( static int ui_do_but_TRACKPREVIEW( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -8140,7 +8148,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * if (fabsf(dot_v2v2(dir_nor_drag, dir_nor_y)) > DRAG_MULTINUM_THRESHOLD_VERTICAL) { data->multi_data.init = BUTTON_MULTI_INIT_SETUP; - data->multi_data.drag_lock_x = event->x; + data->multi_data.drag_lock_x = event->xy[0]; } else { data->multi_data.init = BUTTON_MULTI_INIT_DISABLE; @@ -8153,9 +8161,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* Check if we're don't setting buttons. */ if ((data->str && ELEM(data->state, BUTTON_STATE_TEXT_EDITING, BUTTON_STATE_NUM_EDITING)) || - ((abs(data->multi_data.drag_lock_x - event->x) > margin_x) && + ((abs(data->multi_data.drag_lock_x - event->xy[0]) > margin_x) && /* Just to be sure, check we're dragging more horizontally then vertically. */ - abs(event->prevx - event->x) > abs(event->prevy - event->y))) { + abs(event->prev_xy[0] - event->xy[0]) > abs(event->prev_xy[1] - event->xy[1]))) { if (data->multi_data.has_mbuts) { ui_multibut_states_create(but, data); data->multi_data.init = BUTTON_MULTI_INIT_ENABLE; @@ -9206,7 +9214,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) /* always deactivate button for pie menus, * else moving to blank space will leave activated */ if ((!ui_block_is_menu(block) || ui_block_is_pie_menu(block)) && - !ui_but_contains_point_px(but, region, event->x, event->y)) { + !ui_but_contains_point_px(but, region, event->xy[0], event->xy[1])) { exit = true; } else if (but_other && ui_but_is_editable(but_other) && (but_other != but)) { @@ -9217,7 +9225,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) data->cancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); } - else if (event->x != event->prevx || event->y != event->prevy) { + else if (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1]) { /* Re-enable tool-tip on mouse move. */ ui_blocks_set_tooltips(region, true); button_tooltip_timer_reset(C, but); @@ -9234,7 +9242,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) WM_event_remove_timer(data->wm, data->window, data->autoopentimer); data->autoopentimer = NULL; - if (ui_but_contains_point_px(but, region, event->x, event->y) || but->active) { + if (ui_but_contains_point_px(but, region, event->xy[0], event->xy[1]) || but->active) { button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); } } @@ -9284,12 +9292,12 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) case MOUSEMOVE: { /* deselect the button when moving the mouse away */ /* also de-activate for buttons that only show highlights */ - if (ui_but_contains_point_px(but, region, event->x, event->y)) { + if (ui_but_contains_point_px(but, region, event->xy[0], event->xy[1])) { /* Drag on a hold button (used in the toolbar) now opens it immediately. */ if (data->hold_action_timer) { if (but->flag & UI_SELECT) { - if (len_manhattan_v2v2_int(&event->x, &event->prevx) <= + if (len_manhattan_v2v2_int(event->xy, event->prev_xy) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { /* pass */ } @@ -9342,7 +9350,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) uiBut *bt; if (data->menu && data->menu->region) { - if (ui_region_contains_point_px(data->menu->region, event->x, event->y)) { + if (ui_region_contains_point_px(data->menu->region, event->xy[0], event->xy[1])) { break; } } @@ -9457,7 +9465,7 @@ static int ui_list_activate_hovered_row(bContext *C, } } - const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x; + const int *mouse_xy = ISTWEAK(event->type) ? &event->prev_click_xy[0] : &event->xy[0]; uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy[0], mouse_xy[1]); if (listrow) { wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype; @@ -9484,7 +9492,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, const wmEvent *event) { /* On a tweak event, uses the coordinates from where tweaking was started. */ - const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x; + const int *mouse_xy = ISTWEAK(event->type) ? &event->prev_click_xy[0] : &event->xy[0]; const uiBut *hovered_but = ui_but_find_mouse_over_ex( region, mouse_xy[0], mouse_xy[1], false, NULL, NULL); @@ -9589,8 +9597,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi } uiListDyn *dyn_data = ui_list->dyn_data; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(region, listbox->block, &mx, &my); /* Convert pan to scroll-wheel. */ @@ -9728,7 +9736,7 @@ static int ui_handle_tree_hover(const wmEvent *event, const ARegion *region) /* Always highlight the hovered tree-row, even if the mouse hovers another button inside of it. */ - uiBut *hovered_row_but = ui_tree_row_find_mouse_over(region, event->x, event->y); + uiBut *hovered_row_but = ui_tree_row_find_mouse_over(region, event->xy[0], event->xy[1]); if (hovered_row_but) { hovered_row_but->flag |= UI_ACTIVE; } @@ -9772,7 +9780,7 @@ static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, u } else if (menu->menuretval & UI_RETURN_OUT) { if (event->type == MOUSEMOVE && - ui_but_contains_point_px(but, data->region, event->x, event->y)) { + ui_but_contains_point_px(but, data->region, event->xy[0], event->xy[1])) { button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT); } else { @@ -9912,7 +9920,7 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event) { keynav->is_keynav = true; - copy_v2_v2_int(keynav->event_xy, &event->x); + copy_v2_v2_int(keynav->event_xy, &event->xy[0]); } /** * Return true if key-input isn't blocking mouse-motion, @@ -9921,7 +9929,7 @@ static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEve static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event) { if (keynav->is_keynav && - (len_manhattan_v2v2_int(keynav->event_xy, &event->x) > BUTTON_KEYNAV_PX_LIMIT)) { + (len_manhattan_v2v2_int(keynav->event_xy, event->xy) > BUTTON_KEYNAV_PX_LIMIT)) { keynav->is_keynav = false; } @@ -10111,13 +10119,13 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock else if (!ui_block_is_menu(but->block) || ui_block_is_pie_menu(but->block)) { /* pass, skip for dialogs */ } - else if (!ui_region_contains_point_px(but->active->region, event->x, event->y)) { + else if (!ui_region_contains_point_px(but->active->region, event->xy[0], event->xy[1])) { /* Pass, needed to click-exit outside of non-floating menus. */ ui_region_auto_open_clear(but->active->region); } else if ((!ELEM(event->type, MOUSEMOVE, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN)) && ISMOUSE(event->type)) { - if (!ui_but_contains_point_px(but, but->active->region, event->x, event->y)) { + if (!ui_but_contains_point_px(but, but->active->region, event->xy[0], event->xy[1])) { but = NULL; } } @@ -10191,8 +10199,8 @@ static int ui_handle_menu_event(bContext *C, int retval = WM_UI_HANDLER_CONTINUE; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(region, block, &mx, &my); /* check if mouse is inside block */ @@ -10213,8 +10221,8 @@ static int ui_handle_menu_event(bContext *C, if (event->type == MOUSEMOVE) { int mdiff[2]; - sub_v2_v2v2_int(mdiff, &event->x, menu->grab_xy_prev); - copy_v2_v2_int(menu->grab_xy_prev, &event->x); + sub_v2_v2v2_int(mdiff, event->xy, menu->grab_xy_prev); + copy_v2_v2_int(menu->grab_xy_prev, event->xy); add_v2_v2v2_int(menu->popup_create_vars.event_xy, menu->popup_create_vars.event_xy, mdiff); @@ -10231,7 +10239,7 @@ static int ui_handle_menu_event(bContext *C, /* if a button is activated modal, always reset the start mouse * position of the towards mechanism to avoid losing focus, * and don't handle events */ - ui_mouse_motion_towards_reinit(menu, &event->x); + ui_mouse_motion_towards_reinit(menu, event->xy); } } else if (event->type == TIMER) { @@ -10243,7 +10251,7 @@ static int ui_handle_menu_event(bContext *C, /* for ui_mouse_motion_towards_block */ if (event->type == MOUSEMOVE) { if (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER)) { - ui_mouse_motion_towards_init(menu, &event->x); + ui_mouse_motion_towards_init(menu, event->xy); } /* add menu scroll timer, if needed */ @@ -10326,14 +10334,14 @@ static int ui_handle_menu_event(bContext *C, retval = WM_UI_HANDLER_BREAK; break; - /* Smooth scrolling for popovers. */ + /* Smooth scrolling for pocopy_v2_v2_int(&povers. */ case MOUSEPAN: { if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { /* pass */ } else if (!ui_block_is_menu(block)) { if (block->flag & (UI_BLOCK_CLIPTOP | UI_BLOCK_CLIPBOTTOM)) { - const float dy = event->y - event->prevy; + const float dy = event->xy[1] - event->prev_xy[1]; if (dy != 0.0f) { ui_menu_scroll_apply_offset_y(region, block, dy); @@ -10656,7 +10664,7 @@ static int ui_handle_menu_event(bContext *C, menu->menuretval = UI_RETURN_OUT; } } - else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->x, event->y)) { + else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->xy[0], event->xy[1])) { if (block->flag & UI_BLOCK_OUT_1) { menu->menuretval = UI_RETURN_OK; } @@ -10715,13 +10723,13 @@ static int ui_handle_menu_event(bContext *C, #ifdef USE_DRAG_POPUP else if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && (inside && is_floating && inside_title)) { - if (!but || !ui_but_contains_point_px(but, region, event->x, event->y)) { + if (!but || !ui_but_contains_point_px(but, region, event->xy[0], event->xy[1])) { if (but) { UI_but_tooltip_timer_remove(C, but); } menu->is_grab = true; - copy_v2_v2_int(menu->grab_xy_prev, &event->x); + copy_v2_v2_int(menu->grab_xy_prev, event->xy); retval = WM_UI_HANDLER_BREAK; } } @@ -10732,7 +10740,7 @@ static int ui_handle_menu_event(bContext *C, if (inside == false && (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER))) { uiSafetyRct *saferct; - ui_mouse_motion_towards_check(block, menu, &event->x, is_parent_inside == false); + ui_mouse_motion_towards_check(block, menu, event->xy, is_parent_inside == false); /* Check for all parent rects, enables arrow-keys to be used. */ for (saferct = block->saferct.first; saferct; saferct = saferct->next) { @@ -10740,10 +10748,10 @@ static int ui_handle_menu_event(bContext *C, * events we check all preceding block rects too to make * arrow keys navigation work */ if (event->type != MOUSEMOVE || saferct == block->saferct.first) { - if (BLI_rctf_isect_pt(&saferct->parent, (float)event->x, (float)event->y)) { + if (BLI_rctf_isect_pt(&saferct->parent, (float)event->xy[0], (float)event->xy[1])) { break; } - if (BLI_rctf_isect_pt(&saferct->safety, (float)event->x, (float)event->y)) { + if (BLI_rctf_isect_pt(&saferct->safety, (float)event->xy[0], (float)event->xy[1])) { break; } } @@ -10844,7 +10852,7 @@ static int ui_handle_menu_return_submenu(bContext *C, if (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER)) { /* for cases where close does not cascade, allow the user to * move the mouse back towards the menu without closing */ - ui_mouse_motion_towards_reinit(menu, &event->x); + ui_mouse_motion_towards_reinit(menu, event->xy); } if (menu->menuretval) { @@ -10952,7 +10960,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle const double duration = menu->scrolltimer->duration; - float event_xy[2] = {event->x, event->y}; + float event_xy[2] = {event->xy[0], event->xy[1]}; ui_window_to_block_fl(region, block, &event_xy[0], &event_xy[1]); @@ -11207,8 +11215,8 @@ static int ui_handle_menus_recursive(bContext *C, if (do_recursion) { if (is_parent_inside == false) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(menu->region, block, &mx, &my); inside = BLI_rctf_isect_pt(&block->rect, mx, my); } @@ -11274,7 +11282,7 @@ static int ui_handle_menus_recursive(bContext *C, } if (do_towards_reinit) { - ui_mouse_motion_towards_reinit(menu, &event->x); + ui_mouse_motion_towards_reinit(menu, event->xy); } return retval; @@ -11335,7 +11343,8 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use } /* Re-enable tool-tips. */ - if (event->type == MOUSEMOVE && (event->x != event->prevx || event->y != event->prevy)) { + if (event->type == MOUSEMOVE && + (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1])) { ui_blocks_set_tooltips(region, true); } @@ -11436,7 +11445,8 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE } /* Re-enable tool-tips. */ - if (event->type == MOUSEMOVE && (event->x != event->prevx || event->y != event->prevy)) { + if (event->type == MOUSEMOVE && + (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1])) { ui_blocks_set_tooltips(region, true); } @@ -11530,7 +11540,8 @@ static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata) } else { /* Re-enable tool-tips */ - if (event->type == MOUSEMOVE && (event->x != event->prevx || event->y != event->prevy)) { + if (event->type == MOUSEMOVE && + (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1])) { ui_blocks_set_tooltips(menu->region, true); } } diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 69a3cc959c9..e58b2168dda 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1686,7 +1686,8 @@ static int ui_button_press_invoke(bContext *C, wmOperator *op, const wmEvent *ev bScreen *screen = CTX_wm_screen(C); const bool skip_depressed = RNA_boolean_get(op->ptr, "skip_depressed"); ARegion *region_prev = CTX_wm_region(C); - ARegion *region = screen ? BKE_screen_find_region_xy(screen, RGN_TYPE_ANY, event->x, event->y) : + ARegion *region = screen ? BKE_screen_find_region_xy( + screen, RGN_TYPE_ANY, event->xy[0], event->xy[1]) : NULL; if (region == NULL) { @@ -1929,7 +1930,7 @@ static bool ui_tree_view_drop_poll(bContext *C) const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, win->eventstate->x, win->eventstate->y); + region, win->eventstate->xy[0], win->eventstate->xy[1]); return hovered_tree_item != NULL; } @@ -1942,7 +1943,7 @@ static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const w const ARegion *region = CTX_wm_region(C); uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, event->x, event->y); + region, event->xy[0], event->xy[1]); if (!UI_tree_view_item_drop_handle(hovered_tree_item, event->customdata)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index e5f84f63d35..11013e4cdf7 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1926,7 +1926,7 @@ static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel) ARegion *region = CTX_wm_region(C); /* Keep the drag position in the region with a small pad to keep the panel visible. */ - const int y = clamp_i(event->y, region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD); + const int y = clamp_i(event->xy[1], region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD); float dy = (float)(y - data->starty); @@ -2041,7 +2041,7 @@ static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, voi switch (event->type) { case MOUSEMOVE: - ui_panel_drag_collapse(C, dragcol_data, &event->x); + ui_panel_drag_collapse(C, dragcol_data, &event->xy[0]); retval = WM_UI_HANDLER_BREAK; break; @@ -2070,7 +2070,7 @@ static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was uiPanelDragCollapseHandle *dragcol_data = MEM_mallocN(sizeof(*dragcol_data), __func__); dragcol_data->was_first_open = was_open; - copy_v2_v2_int(dragcol_data->xy_init, &event->x); + copy_v2_v2_int(dragcol_data->xy_init, event->xy); WM_event_add_ui_handler(C, &win->modalhandlers, @@ -2363,7 +2363,7 @@ int ui_handler_panel_region(bContext *C, } /* Scroll-bars can overlap panels now, they have handling priority. */ - if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->x, event->y)) { + if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->xy[0], event->xy[1])) { return WM_UI_HANDLER_CONTINUE; } @@ -2406,8 +2406,8 @@ int ui_handler_panel_region(bContext *C, continue; } - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(region, block, &mx, &my); const uiPanelMouseState mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); @@ -2485,8 +2485,8 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm continue; } - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(region, block, &mx, &my); const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { @@ -2559,8 +2559,8 @@ static void panel_handle_data_ensure(const bContext *C, data->animtimer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, ANIMATION_INTERVAL); data->state = state; - data->startx = win->eventstate->x; - data->starty = win->eventstate->y; + data->startx = win->eventstate->xy[0]; + data->starty = win->eventstate->xy[1]; data->startofsx = panel->ofsx; data->startofsy = panel->ofsy; data->start_cur_xmin = region->v2d.cur.xmin; diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 8674f1e435a..b80e0bfc3ca 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -247,7 +247,7 @@ bool ui_but_contains_point_px(const uiBut *but, const ARegion *region, int x, in bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEvent *event) { rcti rect; - int x = event->x, y = event->y; + int x = event->xy[0], y = event->xy[1]; ui_window_to_block(region, but->block, &x, &y); @@ -333,7 +333,8 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event) { - return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0, NULL, NULL); + return ui_but_find_mouse_over_ex( + region, event->xy[0], event->xy[1], event->ctrl != 0, NULL, NULL); } uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) @@ -398,7 +399,7 @@ uiBut *ui_list_find_mouse_over(const ARegion *region, const wmEvent *event) /* If there is no info about the mouse, just act as if there is nothing underneath it. */ return NULL; } - return ui_list_find_mouse_over_ex(region, event->x, event->y); + return ui_list_find_mouse_over_ex(region, event->xy[0], event->xy[1]); } uiList *UI_list_find_mouse_over(const ARegion *region, const wmEvent *event) @@ -707,7 +708,7 @@ uiBlock *ui_block_find_mouse_over_ex(const ARegion *region, uiBlock *ui_block_find_mouse_over(const ARegion *region, const wmEvent *event, bool only_clip) { - return ui_block_find_mouse_over_ex(region, event->x, event->y, only_clip); + return ui_block_find_mouse_over_ex(region, event->xy[0], event->xy[1], only_clip); } /** \} */ @@ -819,7 +820,7 @@ ARegion *ui_screen_region_find_mouse_over_ex(bScreen *screen, int x, int y) ARegion *ui_screen_region_find_mouse_over(bScreen *screen, const wmEvent *event) { - return ui_screen_region_find_mouse_over_ex(screen, event->x, event->y); + return ui_screen_region_find_mouse_over_ex(screen, event->xy[0], event->xy[1]); } /** \} */ diff --git a/source/blender/editors/interface/interface_region_menu_pie.c b/source/blender/editors/interface/interface_region_menu_pie.c index 05aa139e055..01562b25da1 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.c +++ b/source/blender/editors/interface/interface_region_menu_pie.c @@ -147,9 +147,9 @@ uiPieMenu *UI_pie_menu_begin(struct bContext *C, const char *title, int icon, co pie->layout = UI_block_layout( pie->block_radial, UI_LAYOUT_VERTICAL, UI_LAYOUT_PIEMENU, 0, 0, 200, 0, 0, style); - /* Note event->x/y is where we started dragging in case of KM_CLICK_DRAG. */ - pie->mx = event->x; - pie->my = event->y; + /* NOTE: #wmEvent.xy is where we started dragging in case of #KM_CLICK_DRAG. */ + pie->mx = event->xy[0]; + pie->my = event->xy[1]; /* create title button */ if (title[0]) { diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index d3c1a97e957..4e20466326e 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -338,8 +338,8 @@ uiPopupBlockHandle *ui_popup_menu_create( if (!but) { /* no button to start from, means we are a popup */ - pup->mx = window->eventstate->x; - pup->my = window->eventstate->y; + pup->mx = window->eventstate->xy[0]; + pup->my = window->eventstate->xy[1]; pup->popup = true; pup->block->flag |= UI_BLOCK_NO_FLIP; } @@ -468,8 +468,8 @@ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) ARegion *butregion = NULL; pup->popup = true; - pup->mx = window->eventstate->x; - pup->my = window->eventstate->y; + pup->mx = window->eventstate->xy[0]; + pup->my = window->eventstate->xy[1]; if (pup->but) { but = pup->but; diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 3234af3d344..0e53100f91b 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -803,7 +803,7 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, handle->popup_create_vars.arg_free = arg_free; handle->popup_create_vars.but = but; handle->popup_create_vars.butregion = but ? butregion : NULL; - copy_v2_v2_int(handle->popup_create_vars.event_xy, &window->eventstate->x); + copy_v2_v2_int(handle->popup_create_vars.event_xy, window->eventstate->xy); /* don't allow by default, only if popup type explicitly supports it */ handle->can_refresh = false; diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index c863b1f8bdf..a78c208e4ca 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -400,8 +400,9 @@ bool ui_searchbox_event( * (a little confusing if this isn't the case, although it does work). */ rcti rect; ui_searchbox_butrect(&rect, data, data->active); - if (BLI_rcti_isect_pt( - &rect, event->x - region->winrct.xmin, event->y - region->winrct.ymin)) { + if (BLI_rcti_isect_pt(&rect, + event->xy[0] - region->winrct.xmin, + event->xy[1] - region->winrct.ymin)) { void *active = data->items.pointers[data->active]; if (search_but->item_context_menu_fn(C, search_but->arg, active, event)) { @@ -415,14 +416,14 @@ bool ui_searchbox_event( case MOUSEMOVE: { bool is_inside = false; - if (BLI_rcti_isect_pt(®ion->winrct, event->x, event->y)) { + if (BLI_rcti_isect_pt(®ion->winrct, event->xy[0], event->xy[1])) { rcti rect; int a; for (a = 0; a < data->items.totitem; a++) { ui_searchbox_butrect(&rect, data, a); if (BLI_rcti_isect_pt( - &rect, event->x - region->winrct.xmin, event->y - region->winrct.ymin)) { + &rect, event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin)) { is_inside = true; if (data->active != a) { data->active = a; diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 9aa65bd5b55..4a7203bb421 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -1453,7 +1453,7 @@ ARegion *UI_tooltip_create_from_button_or_extra_icon( init_position[1] = but->rect.ymin; if (butregion) { ui_block_to_window_fl(butregion, but->block, &init_position[0], &init_position[1]); - init_position[0] = win->eventstate->x; + init_position[0] = win->eventstate->xy[0]; } init_position[1] -= (UI_POPUP_MARGIN / 2); } @@ -1477,7 +1477,7 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz) { wmWindow *win = CTX_wm_window(C); const float aspect = 1.0f; - float init_position[2] = {win->eventstate->x, win->eventstate->y}; + float init_position[2] = {win->eventstate->xy[0], win->eventstate->xy[1]}; uiTooltipData *data = ui_tooltip_data_from_gizmo(C, gz); if (data == NULL) { @@ -1561,7 +1561,7 @@ ARegion *UI_tooltip_create_from_search_item_generic( const float aspect = 1.0f; const wmWindow *win = CTX_wm_window(C); float init_position[2]; - init_position[0] = win->eventstate->x; + init_position[0] = win->eventstate->xy[0]; init_position[1] = item_rect->ymin + searchbox_region->winrct.ymin - (UI_POPUP_MARGIN / 2); return ui_tooltip_create_with_data(C, data, init_position, NULL, aspect); diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index bbb04a9911c..f305199c6b0 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -1102,8 +1102,8 @@ static struct ARegion *ui_search_menu_create_tooltip(struct bContext *C, /* Place the fake button at the cursor so the tool-tip is places properly. */ float tip_init[2]; const wmEvent *event = CTX_wm_window(C)->eventstate; - tip_init[0] = event->x; - tip_init[1] = event->y - (UI_UNIT_Y / 2); + tip_init[0] = event->xy[0]; + tip_init[1] = event->xy[1] - (UI_UNIT_Y / 2); ui_window_to_block_fl(region, block, &tip_init[0], &tip_init[1]); but->rect.xmin = tip_init[0]; diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index cf3ddcc3fff..6e4440947be 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -221,7 +221,7 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); uiTreeViewItemHandle *hovered_item_handle = UI_block_tree_view_find_item_at( - region, win->eventstate->x, win->eventstate->y); + region, win->eventstate->xy[0], win->eventstate->xy[1]); AbstractTreeViewItem *hovered_item = reinterpret_cast( hovered_item_handle); BLI_assert(hovered_item != nullptr); diff --git a/source/blender/editors/interface/view2d_edge_pan.c b/source/blender/editors/interface/view2d_edge_pan.c index 54e0d8f40ea..0ad6883b078 100644 --- a/source/blender/editors/interface/view2d_edge_pan.c +++ b/source/blender/editors/interface/view2d_edge_pan.c @@ -272,7 +272,7 @@ void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const w return; } - UI_view2d_edge_pan_apply(C, vpd, event->x, event->y); + UI_view2d_edge_pan_apply(C, vpd, event->xy[0], event->xy[1]); } void UI_view2d_edge_pan_cancel(bContext *C, View2DEdgePanData *vpd) diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 4ef4c3dbc6d..a73449f659d 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -224,13 +224,13 @@ static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event) View2D *v2d = vpd->v2d; /* set initial settings */ - vpd->startx = vpd->lastx = event->x; - vpd->starty = vpd->lasty = event->y; + vpd->startx = vpd->lastx = event->xy[0]; + vpd->starty = vpd->lasty = event->xy[1]; vpd->invoke_event = event->type; if (event->type == MOUSEPAN) { - RNA_int_set(op->ptr, "deltax", event->prevx - event->x); - RNA_int_set(op->ptr, "deltay", event->prevy - event->y); + RNA_int_set(op->ptr, "deltax", event->prev_xy[0] - event->xy[0]); + RNA_int_set(op->ptr, "deltay", event->prev_xy[1] - event->xy[1]); view_pan_apply(C, op); view_pan_exit(op); @@ -266,11 +266,11 @@ static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: { /* calculate new delta transform, then store mouse-coordinates for next-time */ - RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->x)); - RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->y)); + RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->xy[0])); + RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->xy[1])); - vpd->lastx = event->x; - vpd->lasty = event->y; + vpd->lastx = event->xy[0]; + vpd->lasty = event->xy[1]; view_pan_apply(C, op); break; @@ -1097,8 +1097,8 @@ static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *even } if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) { - vzd->lastx = event->prevx; - vzd->lasty = event->prevy; + vzd->lastx = event->prev_xy[0]; + vzd->lasty = event->prev_xy[1]; float facx, facy; float zoomfac = 0.01f; @@ -1156,8 +1156,8 @@ static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *even } /* set initial settings */ - vzd->lastx = event->x; - vzd->lasty = event->y; + vzd->lastx = event->xy[0]; + vzd->lasty = event->xy[1]; RNA_float_set(op->ptr, "deltax", 0); RNA_float_set(op->ptr, "deltay", 0); @@ -1216,12 +1216,12 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event /* x-axis transform */ dist = BLI_rcti_size_x(&v2d->mask) / 2.0f; len_old[0] = zoomfac * fabsf(vzd->lastx - vzd->region->winrct.xmin - dist); - len_new[0] = zoomfac * fabsf(event->x - vzd->region->winrct.xmin - dist); + len_new[0] = zoomfac * fabsf(event->xy[0] - vzd->region->winrct.xmin - dist); /* y-axis transform */ dist = BLI_rcti_size_y(&v2d->mask) / 2.0f; len_old[1] = zoomfac * fabsf(vzd->lasty - vzd->region->winrct.ymin - dist); - len_new[1] = zoomfac * fabsf(event->y - vzd->region->winrct.ymin - dist); + len_new[1] = zoomfac * fabsf(event->xy[1] - vzd->region->winrct.ymin - dist); /* Calculate distance */ if (v2d->keepzoom & V2D_KEEPASPECT) { @@ -1237,8 +1237,8 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event dy *= BLI_rctf_size_y(&v2d->cur); } else { /* USER_ZOOM_CONTINUE or USER_ZOOM_DOLLY */ - float facx = zoomfac * (event->x - vzd->lastx); - float facy = zoomfac * (event->y - vzd->lasty); + float facx = zoomfac * (event->xy[0] - vzd->lastx); + float facy = zoomfac * (event->xy[1] - vzd->lasty); /* Only respect user setting zoom axis if the view does not have any zoom restrictions * any will be scaled uniformly */ @@ -1284,8 +1284,8 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event * to starting point to determine rate of change. */ if (U.viewzoom != USER_ZOOM_CONTINUE) { /* XXX store this setting as RNA prop? */ - vzd->lastx = event->x; - vzd->lasty = event->y; + vzd->lastx = event->xy[0]; + vzd->lasty = event->xy[1]; } /* apply zooming */ @@ -1846,7 +1846,7 @@ static bool scroller_activate_poll(bContext *C) wmEvent *event = win->eventstate; /* check if mouse in scrollbars, if they're enabled */ - return (UI_view2d_mouse_in_scrollers(region, v2d, event->x, event->y) != 0); + return (UI_view2d_mouse_in_scrollers(region, v2d, event->xy[0], event->xy[1]) != 0); } /* initialize customdata for scroller manipulation operator */ @@ -1868,8 +1868,8 @@ static void scroller_activate_init(bContext *C, vsm->scroller = in_scroller; /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */ - vsm->lastx = event->x; - vsm->lasty = event->y; + vsm->lastx = event->xy[0]; + vsm->lasty = event->xy[1]; /* 'zone' depends on where mouse is relative to bubble * - zooming must be allowed on this axis, otherwise, default to pan */ @@ -2024,11 +2024,11 @@ static int scroller_activate_modal(bContext *C, wmOperator *op, const wmEvent *e switch (vsm->scroller) { case 'h': /* horizontal scroller - so only horizontal movement * ('cur' moves opposite to mouse) */ - vsm->delta = (float)(event->x - vsm->lastx); + vsm->delta = (float)(event->xy[0] - vsm->lastx); break; case 'v': /* vertical scroller - so only vertical movement * ('cur' moves opposite to mouse) */ - vsm->delta = (float)(event->y - vsm->lasty); + vsm->delta = (float)(event->xy[1] - vsm->lasty); break; } } @@ -2037,18 +2037,18 @@ static int scroller_activate_modal(bContext *C, wmOperator *op, const wmEvent *e switch (vsm->scroller) { case 'h': /* horizontal scroller - so only horizontal movement * ('cur' moves with mouse) */ - vsm->delta = (float)(vsm->lastx - event->x); + vsm->delta = (float)(vsm->lastx - event->xy[0]); break; case 'v': /* vertical scroller - so only vertical movement * ('cur' moves with to mouse) */ - vsm->delta = (float)(vsm->lasty - event->y); + vsm->delta = (float)(vsm->lasty - event->xy[1]); break; } } /* store previous coordinates */ - vsm->lastx = event->x; - vsm->lasty = event->y; + vsm->lastx = event->xy[0]; + vsm->lasty = event->xy[1]; scroller_activate_apply(C, op); break; @@ -2090,7 +2090,7 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent * View2D *v2d = ®ion->v2d; /* check if mouse in scrollbars, if they're enabled */ - const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->x, event->y); + const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->xy[0], event->xy[1]); /* if in a scroller, init customdata then set modal handler which will * catch mouse-down to start doing useful stuff */ @@ -2104,11 +2104,11 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent * switch (vsm->scroller) { case 'h': /* horizontal scroller - so only horizontal movement * ('cur' moves opposite to mouse) */ - vsm->delta = (float)(event->x - vsm->scrollbar_orig); + vsm->delta = (float)(event->xy[0] - vsm->scrollbar_orig); break; case 'v': /* vertical scroller - so only vertical movement * ('cur' moves opposite to mouse) */ - vsm->delta = (float)(event->y - vsm->scrollbar_orig); + vsm->delta = (float)(event->xy[1] - vsm->scrollbar_orig); break; } scroller_activate_apply(C, op); diff --git a/source/blender/editors/mesh/editmesh_add_gizmo.c b/source/blender/editors/mesh/editmesh_add_gizmo.c index 9efcf0963b4..e2c75435af4 100644 --- a/source/blender/editors/mesh/editmesh_add_gizmo.c +++ b/source/blender/editors/mesh/editmesh_add_gizmo.c @@ -220,8 +220,8 @@ static void gizmo_mesh_placement_modal_from_setup(const bContext *C, wmGizmoGrou float location[3]; calc_initial_placement_point_from_view((bContext *)C, (float[2]){ - win->eventstate->x - region->winrct.xmin, - win->eventstate->y - region->winrct.ymin, + win->eventstate->xy[0] - region->winrct.xmin, + win->eventstate->xy[1] - region->winrct.ymin, }, location, mat3); diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 6f41b7f04a6..332f1f98d81 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -705,7 +705,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (etype == MOUSEPAN) { - float delta = 0.02f * (event->y - event->prevy); + float delta = 0.02f * (event->xy[1] - event->prev_xy[1]); if (opdata->segments >= 1 && opdata->segments + delta < 1) { opdata->segments = 1; } diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c index 5faafa77bba..7c289744eeb 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c @@ -941,7 +941,7 @@ static void gizmo_mesh_spin_redo_setup(const bContext *C, wmGizmoGroup *gzgroup) /* Use cursor as fallback if it's not set by the 'ortho_axis_active'. */ if (is_zero_v3(ggd->data.orient_axis_relative)) { float cursor_co[3]; - const int mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; + const int mval[2] = {event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin}; float plane[4]; plane_from_point_normal_v3(plane, plane_co, plane_no); if (UNLIKELY(!ED_view3d_win_to_3d_on_plane_int(region, plane, mval, false, cursor_co))) { diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 8b78b091fd2..24825ef1e3a 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -598,13 +598,13 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) break; case MOUSEPAN: if (event->alt == 0) { - cuts += 0.02f * (event->y - event->prevy); + cuts += 0.02f * (event->xy[1] - event->prev_xy[1]); if (cuts < 1 && lcd->cuts >= 1) { cuts = 1; } } else { - smoothness += 0.002f * (event->y - event->prevy); + smoothness += 0.002f * (event->xy[1] - event->prev_xy[1]); } handled = true; break; diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index cccfc7e934c..8cfcc96517c 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -389,13 +389,14 @@ static int face_set_extract_modal(bContext *C, wmOperator *op, const wmEvent *ev * the PBVH and update the active Face Set ID. */ bScreen *screen = CTX_wm_screen(C); ARegion *region = BKE_screen_find_main_region_at_xy( - screen, SPACE_VIEW3D, event->x, event->y); + screen, SPACE_VIEW3D, event->xy[0], event->xy[1]); if (!region) { return OPERATOR_CANCELLED; } - const float mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; + const float mval[2] = {event->xy[0] - region->winrct.xmin, + event->xy[1] - region->winrct.ymin}; Object *ob = CTX_data_active_object(C); const int face_set_id = ED_sculpt_face_sets_active_update_and_get(C, ob, mval); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 9c6969110fd..114f540b614 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -259,8 +259,8 @@ static bool object_add_drop_xy_get(bContext *C, wmOperator *op, int (*r_mval)[2] static int object_add_drop_xy_generic_invoke(bContext *C, wmOperator *op, const wmEvent *event) { if (!object_add_drop_xy_is_set(op)) { - RNA_int_set(op->ptr, "drop_x", event->x); - RNA_int_set(op->ptr, "drop_y", event->y); + RNA_int_set(op->ptr, "drop_x", event->xy[0]); + RNA_int_set(op->ptr, "drop_y", event->xy[1]); } return op->type->exec(C, op); } @@ -1691,8 +1691,8 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op) static int object_instance_add_invoke(bContext *C, wmOperator *op, const wmEvent *event) { if (!object_add_drop_xy_is_set(op)) { - RNA_int_set(op->ptr, "drop_x", event->x); - RNA_int_set(op->ptr, "drop_y", event->y); + RNA_int_set(op->ptr, "drop_x", event->xy[0]); + RNA_int_set(op->ptr, "drop_y", event->xy[1]); } if (!RNA_struct_property_is_set(op->ptr, "name")) { diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index d5ad5a5eb84..2ff47edabf4 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -959,7 +959,7 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even * store spare */ /* ensure at least 1 area shows result */ - area = render_view_open(C, event->x, event->y, op->reports); + area = render_view_open(C, event->xy[0], event->xy[1], op->reports); /* job custom data */ rj = MEM_callocN(sizeof(RenderJob), "render job"); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index e4bbbfb0f57..4c539bdea90 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -1263,7 +1263,7 @@ static int screen_opengl_render_invoke(bContext *C, wmOperator *op, const wmEven } oglrender = op->customdata; - render_view_open(C, event->x, event->y, op->reports); + render_view_open(C, event->xy[0], event->xy[1], op->reports); /* View may be changed above #USER_RENDER_DISPLAY_WINDOW. */ oglrender->win = CTX_wm_window(C); diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index 0e5acc0a108..7f27b6f585a 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -365,7 +365,7 @@ static int render_view_show_invoke(bContext *C, wmOperator *op, const wmEvent *e } } else { - render_view_open(C, event->x, event->y, op->reports); + render_view_open(C, event->xy[0], event->xy[1], op->reports); } } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 6b0b4d911cc..54727cc79f1 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1681,7 +1681,7 @@ static bool event_in_markers_region(const ARegion *region, const wmEvent *event) { rcti rect = region->winrct; rect.ymax = rect.ymin + UI_MARKER_MARGIN_Y; - return BLI_rcti_isect_pt(&rect, event->x, event->y); + return BLI_rcti_isect_pt(&rect, event->xy[0], event->xy[1]); } /** @@ -1912,7 +1912,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar /* Some AZones use View2D data which is only updated in region init, so call that first! */ region_azones_add(screen, area, region); } - ED_area_azones_update(area, &win->eventstate->x); + ED_area_azones_update(area, win->eventstate->xy); area->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE; } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 79ef1c99b1d..e1b5c355e3e 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -686,7 +686,7 @@ void ED_screens_init(Main *bmain, wmWindowManager *wm) ED_screen_refresh(wm, win); if (win->eventstate) { - ED_screen_set_active_region(NULL, win, &win->eventstate->x); + ED_screen_set_active_region(NULL, win, win->eventstate->xy); } } @@ -959,7 +959,7 @@ int ED_screen_area_active(const bContext *C) ScrArea *area = CTX_wm_area(C); if (win && screen && area) { - AZone *az = ED_area_actionzone_find_xy(area, &win->eventstate->x); + AZone *az = ED_area_actionzone_find_xy(area, win->eventstate->xy); if (az && az->type == AZONE_REGION) { return 1; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 3a0192aef54..ca2eeca2284 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -773,7 +773,7 @@ static bool actionzone_area_poll(bContext *C) bScreen *screen = WM_window_get_active_screen(win); if (screen && win && win->eventstate) { - const int *xy = &win->eventstate->x; + const int *xy = &win->eventstate->xy[0]; LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (AZone *, az, &area->actionzones) { @@ -1061,7 +1061,7 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type) static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) { bScreen *screen = CTX_wm_screen(C); - AZone *az = screen_actionzone_find_xy(screen, &event->x); + AZone *az = screen_actionzone_find_xy(screen, &event->xy[0]); /* Quick escape - Scroll azones only hide/unhide the scroll-bars, * they have their own handling. */ @@ -1073,8 +1073,8 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) sActionzoneData *sad = op->customdata = MEM_callocN(sizeof(sActionzoneData), "sActionzoneData"); sad->sa1 = screen_actionzone_area(screen, az); sad->az = az; - sad->x = event->x; - sad->y = event->y; + sad->x = event->xy[0]; + sad->y = event->xy[1]; /* region azone directly reacts on mouse clicks */ if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) { @@ -1098,8 +1098,8 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: { - const int delta_x = (event->x - sad->x); - const int delta_y = (event->y - sad->y); + const int delta_x = (event->xy[0] - sad->x); + const int delta_y = (event->xy[1] - sad->y); /* Movement in dominant direction. */ const int delta_max = max_ii(abs(delta_x), abs(delta_y)); @@ -1131,12 +1131,13 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) WM_window_screen_rect_calc(win, &screen_rect); /* Have we dragged off the zone and are not on an edge? */ - if ((ED_area_actionzone_find_xy(sad->sa1, &event->x) != sad->az) && + if ((ED_area_actionzone_find_xy(sad->sa1, event->xy) != sad->az) && (screen_geom_area_map_find_active_scredge( - AREAMAP_FROM_SCREEN(screen), &screen_rect, event->x, event->y) == NULL)) { + AREAMAP_FROM_SCREEN(screen), &screen_rect, event->xy[0], event->xy[1]) == NULL)) { /* What area are we now in? */ - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + ScrArea *area = BKE_screen_find_area_xy( + screen, SPACE_TYPE_ANY, event->xy[0], event->xy[1]); if (area == sad->sa1) { /* Same area, so possible split. */ @@ -1180,7 +1181,7 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* gesture is large enough? */ if (is_gesture) { /* second area, for join when (sa1 != sa2) */ - sad->sa2 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + sad->sa2 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy[0], event->xy[1]); /* apply sends event */ actionzone_apply(C, op, sad->az->type); actionzone_exit(op); @@ -1341,7 +1342,8 @@ static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: /* second area, for join */ - sad->sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y); + sad->sa2 = BKE_screen_find_area_xy( + CTX_wm_screen(C), SPACE_TYPE_ANY, event->xy[0], event->xy[1]); break; case LEFTMOUSE: /* release LMB */ if (event->val == KM_RELEASE) { @@ -1942,8 +1944,8 @@ static int area_move_exec(bContext *C, wmOperator *op) /* interaction callback */ static int area_move_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - RNA_int_set(op->ptr, "x", event->x); - RNA_int_set(op->ptr, "y", event->y); + RNA_int_set(op->ptr, "x", event->xy[0]); + RNA_int_set(op->ptr, "y", event->xy[1]); if (!area_move_init(C, op)) { return OPERATOR_PASS_THROUGH; @@ -1975,7 +1977,7 @@ static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event) int x = RNA_int_get(op->ptr, "x"); int y = RNA_int_get(op->ptr, "y"); - const int delta = (md->dir_axis == SCREEN_AXIS_V) ? event->x - x : event->y - y; + const int delta = (md->dir_axis == SCREEN_AXIS_V) ? event->xy[0] - x : event->xy[1] - y; RNA_int_set(op->ptr, "delta", delta); area_move_apply(C, op); @@ -2295,8 +2297,8 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) } /* The factor will be close to 1.0f when near the top-left and the bottom-right corners. */ - const float factor_v = ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy; - const float factor_h = ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx; + const float factor_v = ((float)(event->xy[1] - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy; + const float factor_h = ((float)(event->xy[0] - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx; const bool is_left = factor_v < 0.5f; const bool is_bottom = factor_h < 0.5f; const bool is_right = !is_left; @@ -2334,11 +2336,11 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) dir_axis = RNA_property_enum_get(op->ptr, prop_dir); if (dir_axis == SCREEN_AXIS_H) { RNA_property_float_set( - op->ptr, prop_factor, ((float)(event->x - area->v1->vec.x)) / (float)area->winx); + op->ptr, prop_factor, ((float)(event->xy[0] - area->v1->vec.x)) / (float)area->winx); } else { RNA_property_float_set( - op->ptr, prop_factor, ((float)(event->y - area->v1->vec.y)) / (float)area->winy); + op->ptr, prop_factor, ((float)(event->xy[1] - area->v1->vec.y)) / (float)area->winy); } if (!area_split_init(C, op)) { @@ -2353,7 +2355,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) RNA_property_int_get_array(op->ptr, prop_cursor, event_co); } else { - copy_v2_v2_int(event_co, &event->x); + copy_v2_v2_int(event_co, event->xy); } rcti window_rect; @@ -2493,7 +2495,8 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) if (update_factor) { const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir); - sd->delta = (dir_axis == SCREEN_AXIS_V) ? event->x - sd->origval : event->y - sd->origval; + sd->delta = (dir_axis == SCREEN_AXIS_V) ? event->xy[0] - sd->origval : + event->xy[1] - sd->origval; if (sd->previewmode == 0) { if (sd->do_snap) { @@ -2513,7 +2516,8 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_area_tag_redraw(sd->sarea); } /* area context not set */ - sd->sarea = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y); + sd->sarea = BKE_screen_find_area_xy( + CTX_wm_screen(C), SPACE_TYPE_ANY, event->xy[0], event->xy[1]); if (sd->sarea) { ScrArea *area = sd->sarea; @@ -2702,8 +2706,8 @@ static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event } rmd->area = sad->sa1; rmd->edge = az->edge; - rmd->origx = event->x; - rmd->origy = event->y; + rmd->origx = event->xy[0]; + rmd->origy = event->xy[1]; rmd->maxsize = area_max_regionsize(rmd->area, rmd->region, rmd->edge); /* if not set we do now, otherwise it uses type */ @@ -2790,7 +2794,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) (BLI_rcti_size_x(&rmd->region->v2d.mask) + 1); const int snap_size_threshold = (U.widget_unit * 2) / aspect; if (ELEM(rmd->edge, AE_LEFT_TO_TOPRIGHT, AE_RIGHT_TO_TOPLEFT)) { - delta = event->x - rmd->origx; + delta = event->xy[0] - rmd->origx; if (rmd->edge == AE_LEFT_TO_TOPRIGHT) { delta = -delta; } @@ -2823,7 +2827,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else { - delta = event->y - rmd->origy; + delta = event->xy[1] - rmd->origy; if (rmd->edge == AE_BOTTOM_TO_TOPLEFT) { delta = -delta; } @@ -2865,7 +2869,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) } case LEFTMOUSE: if (event->val == KM_RELEASE) { - if (len_manhattan_v2v2_int(&event->x, &rmd->origx) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { + if (len_manhattan_v2v2_int(event->xy, &rmd->origx) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { if (rmd->region->flag & RGN_FLAG_HIDDEN) { region_scale_toggle_hidden(C, rmd); } @@ -3521,7 +3525,7 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: { - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy[0], event->xy[1]); jd->dir = area_getorientation(jd->sa1, jd->sa2); if (area == jd->sa1) { @@ -3611,7 +3615,7 @@ static void SCREEN_OT_area_join(wmOperatorType *ot) static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ScrArea *sa1, *sa2; - if (screen_area_edge_from_cursor(C, &event->x, &sa1, &sa2) == NULL) { + if (screen_area_edge_from_cursor(C, event->xy, &sa1, &sa2) == NULL) { return OPERATOR_CANCELLED; } @@ -3629,7 +3633,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent 0, &ptr); /* store initial mouse cursor position. */ - RNA_int_set_array(&ptr, "cursor", &event->x); + RNA_int_set_array(&ptr, "cursor", &event->xy[0]); RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V); /* Horizontal Split */ @@ -3642,7 +3646,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent 0, &ptr); /* store initial mouse cursor position. */ - RNA_int_set_array(&ptr, "cursor", &event->x); + RNA_int_set_array(&ptr, "cursor", &event->xy[0]); RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H); if (sa1 && sa2) { @@ -3659,7 +3663,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent WM_OP_INVOKE_DEFAULT, 0, &ptr); - RNA_int_set_array(&ptr, "cursor", &event->x); + RNA_int_set_array(&ptr, "cursor", &event->xy[0]); } /* Swap just needs two areas. */ @@ -3672,7 +3676,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_int_set_array(&ptr, "cursor", &event->x); + RNA_int_set_array(&ptr, "cursor", &event->xy[0]); } UI_popup_menu_end(C, pup); @@ -5058,8 +5062,8 @@ static int userpref_show_exec(bContext *C, wmOperator *op) /* changes context! */ if (WM_window_open(C, IFACE_("Blender Preferences"), - event->x, - event->y, + event->xy[0], + event->xy[1], sizex, sizey, SPACE_USERPREF, @@ -5125,8 +5129,8 @@ static int drivers_editor_show_exec(bContext *C, wmOperator *op) /* changes context! */ if (WM_window_open(C, IFACE_("Blender Drivers Editor"), - event->x, - event->y, + event->xy[0], + event->xy[1], sizex, sizey, SPACE_GRAPH, @@ -5194,8 +5198,8 @@ static int info_log_show_exec(bContext *C, wmOperator *op) /* changes context! */ if (WM_window_open(C, IFACE_("Blender Info Log"), - event->x, - event->y + shift_y, + event->xy[0], + event->xy[1] + shift_y, sizex, sizey, SPACE_INFO, diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index bc1c15abb96..5c4ba67d72a 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -166,7 +166,8 @@ static int screenshot_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (use_crop) { area = CTX_wm_area(C); bScreen *screen = CTX_wm_screen(C); - ScrArea *area_test = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + ScrArea *area_test = BKE_screen_find_area_xy( + screen, SPACE_TYPE_ANY, event->xy[0], event->xy[1]); if (area_test != NULL) { area = area_test; } diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index b5113681955..6ba5c43f698 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -863,8 +863,8 @@ static int grab_clone_invoke(bContext *C, wmOperator *op, const wmEvent *event) cmv = MEM_callocN(sizeof(GrabClone), "GrabClone"); copy_v2_v2(cmv->startoffset, brush->clone.offset); - cmv->startx = event->x; - cmv->starty = event->y; + cmv->startx = event->xy[0]; + cmv->starty = event->xy[1]; op->customdata = cmv; WM_event_add_modal_handler(C, op); @@ -890,7 +890,7 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* mouse moved, so move the clone image */ UI_view2d_region_to_view( ®ion->v2d, cmv->startx - xmin, cmv->starty - ymin, &startfx, &startfy); - UI_view2d_region_to_view(®ion->v2d, event->x - xmin, event->y - ymin, &fx, &fy); + UI_view2d_region_to_view(®ion->v2d, event->xy[0] - xmin, event->xy[1] - ymin, &fx, &fy); delta[0] = fx - startfx; delta[1] = fy - startfy; diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c index cb8dc838422..1077f66f10c 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -339,8 +339,8 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, uint index; const int mval[2] = { - win->eventstate->x - vc.region->winrct.xmin, - win->eventstate->y - vc.region->winrct.ymin, + win->eventstate->xy[0] - vc.region->winrct.xmin, + win->eventstate->xy[1] - vc.region->winrct.ymin, }; view3d_operator_needs_opengl(C); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 1330beb917b..7b854e886a0 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -8057,7 +8057,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent /* For tablet rotation. */ ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); - if (ignore_background_click && !over_mesh(C, op, event->x, event->y)) { + if (ignore_background_click && !over_mesh(C, op, event->xy[0], event->xy[1])) { paint_stroke_free(C, op); return OPERATOR_PASS_THROUGH; } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index a53a2126af4..fa879214794 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -1528,7 +1528,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent return OPERATOR_RUNNING_MODAL; } - const float len = event->prevclickx - event->x; + const float len = event->prev_click_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC; SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index 188bb0a88eb..edbb98b481e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -306,7 +306,7 @@ static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wm switch (event->type) { case LEFTMOUSE: if (event->val == KM_PRESS) { - const int ss_co[2] = {event->x, event->y}; + const int ss_co[2] = {event->xy[0], event->xy[1]}; int mode = RNA_enum_get(op->ptr, "mode"); sample_detail(C, ss_co[0], ss_co[1], mode); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 4b49bf2cefb..a0062a98194 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -231,7 +231,7 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent return OPERATOR_RUNNING_MODAL; } - const float len = event->prevclickx - event->x; + const float len = event->prev_click_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f; float fill_color[3]; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 2a7c2a46818..760cf632ae9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -624,7 +624,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * return OPERATOR_RUNNING_MODAL; } - const float len = event->prevclickx - event->x; + const float len = event->prev_click_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC; SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index f5e4c4d55d9..b1f8949871b 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -393,8 +393,8 @@ static void view_pan_init(bContext *C, wmOperator *op, const wmEvent *event) WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } - vpd->x = event->x; - vpd->y = event->y; + vpd->x = event->xy[0]; + vpd->y = event->xy[1]; if (clip_view_has_locked_selection(C)) { vpd->vec = &sc->xlockof; @@ -454,8 +454,8 @@ static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event) SpaceClip *sc = CTX_wm_space_clip(C); float offset[2]; - offset[0] = (event->prevx - event->x) / sc->zoom; - offset[1] = (event->prevy - event->y) / sc->zoom; + offset[0] = (event->prev_xy[0] - event->xy[0]) / sc->zoom; + offset[1] = (event->prev_xy[1] - event->xy[1]) / sc->zoom; RNA_float_set_array(op->ptr, "offset", offset); @@ -478,8 +478,8 @@ static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: copy_v2_v2(vpd->vec, &vpd->xorig); - offset[0] = (vpd->x - event->x) / sc->zoom; - offset[1] = (vpd->y - event->y) / sc->zoom; + offset[0] = (vpd->x - event->xy[0]) / sc->zoom; + offset[1] = (vpd->y - event->xy[1]) / sc->zoom; RNA_float_set_array(op->ptr, "offset", offset); view_pan_exec(C, op); break; @@ -575,8 +575,8 @@ static void view_zoom_init(bContext *C, wmOperator *op, const wmEvent *event) vpd->timer_lastdraw = PIL_check_seconds_timer(); } - vpd->x = event->x; - vpd->y = event->y; + vpd->x = event->xy[0]; + vpd->y = event->xy[1]; vpd->zoom = sc->zoom; vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); @@ -619,7 +619,7 @@ static int view_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) { float delta, factor; - delta = event->prevx - event->x + event->prevy - event->y; + delta = event->prev_xy[0] - event->xy[0] + event->prev_xy[1] - event->xy[1]; if (U.uiflag & USER_ZOOM_INVERT) { delta *= -1; @@ -646,14 +646,14 @@ static void view_zoom_apply( if (U.viewzoom != USER_ZOOM_SCALE) { if (U.uiflag & USER_ZOOM_HORIZ) { - delta = (float)(event->x - vpd->x); + delta = (float)(event->xy[0] - vpd->x); } else { - delta = (float)(event->y - vpd->y); + delta = (float)(event->xy[1] - vpd->y); } } else { - delta = event->x - vpd->x + event->y - vpd->y; + delta = event->xy[0] - vpd->x + event->xy[1] - vpd->y; } delta /= U.pixelsize; diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index 4965099642b..ee0406cde30 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -181,8 +181,8 @@ static int add_marker_at_click_modal(bContext *C, wmOperator *UNUSED(op), const ED_clip_point_stable_pos(sc, region, - event->x - region->winrct.xmin, - event->y - region->winrct.ymin, + event->xy[0] - region->winrct.xmin, + event->xy[1] - region->winrct.ymin, &pos[0], &pos[1]); diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 47d15efb6ca..50e664913d2 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -149,7 +149,7 @@ static void console_cursor(wmWindow *win, ScrArea *UNUSED(area), ARegion *region { int wmcursor = WM_CURSOR_TEXT_EDIT; const wmEvent *event = win->eventstate; - if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->x, event->y)) { + if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->xy[0], event->xy[1])) { wmcursor = WM_CURSOR_DEFAULT; } diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index d0f2a4fdc4c..f647e1d4e4f 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1449,7 +1449,7 @@ static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), const wmEv ARegion *region = CTX_wm_region(C); SpaceFile *sfile = CTX_wm_space_file(C); - if (!file_highlight_set(sfile, region, event->x, event->y)) { + if (!file_highlight_set(sfile, region, event->xy[0], event->xy[1])) { return OPERATOR_PASS_THROUGH; } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 2587c8f7f1f..b93f9b34f3b 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -654,7 +654,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region) /* on first read, find active file */ if (params->highlight_file == -1) { wmEvent *event = CTX_wm_window(C)->eventstate; - file_highlight_set(sfile, region, event->x, event->y); + file_highlight_set(sfile, region, event->xy[0], event->xy[1]); } if (!file_draw_hint_if_invalid(sfile, region)) { diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 0b74c801399..1967dfabd21 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -403,8 +403,8 @@ static int graphkeys_click_insert_invoke(bContext *C, wmOperator *op, const wmEv region = ac.region; v2d = ®ion->v2d; - mval[0] = (event->x - region->winrct.xmin); - mval[1] = (event->y - region->winrct.ymin); + mval[0] = (event->xy[0] - region->winrct.xmin); + mval[1] = (event->xy[1] - region->winrct.ymin); UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 0dbcb1885c2..ae56238d73f 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -360,8 +360,8 @@ static void image_view_pan_init(bContext *C, wmOperator *op, const wmEvent *even WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } - vpd->x = event->x; - vpd->y = event->y; + vpd->x = event->xy[0]; + vpd->y = event->xy[1]; vpd->xof = sima->xof; vpd->yof = sima->yof; vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); @@ -406,8 +406,8 @@ static int image_view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *eve SpaceImage *sima = CTX_wm_space_image(C); float offset[2]; - offset[0] = (event->prevx - event->x) / sima->zoom; - offset[1] = (event->prevy - event->y) / sima->zoom; + offset[0] = (event->prev_xy[0] - event->xy[0]) / sima->zoom; + offset[1] = (event->prev_xy[1] - event->xy[1]) / sima->zoom; RNA_float_set_array(op->ptr, "offset", offset); image_view_pan_exec(C, op); @@ -428,8 +428,8 @@ static int image_view_pan_modal(bContext *C, wmOperator *op, const wmEvent *even case MOUSEMOVE: sima->xof = vpd->xof; sima->yof = vpd->yof; - offset[0] = (vpd->x - event->x) / sima->zoom; - offset[1] = (vpd->y - event->y) / sima->zoom; + offset[0] = (vpd->x - event->xy[0]) / sima->zoom; + offset[1] = (vpd->y - event->xy[1]) / sima->zoom; RNA_float_set_array(op->ptr, "offset", offset); image_view_pan_exec(C, op); break; @@ -516,8 +516,8 @@ static void image_view_zoom_init(bContext *C, wmOperator *op, const wmEvent *eve WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } - vpd->origx = event->x; - vpd->origy = event->y; + vpd->origx = event->xy[0]; + vpd->origy = event->xy[1]; vpd->zoom = sima->zoom; vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); @@ -584,7 +584,7 @@ static int image_view_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *ev UI_view2d_region_to_view( ®ion->v2d, event->mval[0], event->mval[1], &location[0], &location[1]); - delta = event->prevx - event->x + event->prevy - event->y; + delta = event->prev_xy[0] - event->xy[0] + event->prev_xy[1] - event->xy[1]; if (U.uiflag & USER_ZOOM_INVERT) { delta *= -1; @@ -675,8 +675,8 @@ static int image_view_zoom_modal(bContext *C, wmOperator *op, const wmEvent *eve const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); image_zoom_apply(vpd, op, - event->x, - event->y, + event->xy[0], + event->xy[1], U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0, (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index f14a8266cdd..516ffcd1e75 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -258,7 +258,7 @@ static void image_keymap(struct wmKeyConfig *keyconf) static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ScrArea *area = CTX_wm_area(C); - if (ED_region_overlap_isect_any_xy(area, &event->x)) { + if (ED_region_overlap_isect_any_xy(area, event->xy)) { return false; } if (drag->type == WM_DRAG_PATH) { diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 4e879d6c2f7..dca09a58c9a 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2220,8 +2220,8 @@ void node_draw_space(const bContext *C, ARegion *region) /* XXX `snode->runtime->cursor` set in coordinate-space for placing new nodes, * used for drawing noodles too. */ UI_view2d_region_to_view(®ion->v2d, - win->eventstate->x - region->winrct.xmin, - win->eventstate->y - region->winrct.ymin, + win->eventstate->xy[0] - region->winrct.xmin, + win->eventstate->xy[1] - region->winrct.ymin, &snode->runtime->cursor[0], &snode->runtime->cursor[1]); snode->runtime->cursor[0] /= UI_DPI_FAC; diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index 956fb3aa867..c932525ee1a 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -619,8 +619,8 @@ static void node_cursor(wmWindow *win, ScrArea *area, ARegion *region) /* convert mouse coordinates to v2d space */ UI_view2d_region_to_view(®ion->v2d, - win->eventstate->x - region->winrct.xmin, - win->eventstate->y - region->winrct.ymin, + win->eventstate->xy[0] - region->winrct.xmin, + win->eventstate->xy[1] - region->winrct.ymin, &snode->runtime->cursor[0], &snode->runtime->cursor[1]); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index 2b31ce7d03c..4cf6d14cbda 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -81,7 +81,8 @@ DatasetDrawContext::DatasetDrawContext(const bContext *C) { const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - mval_ = {win->eventstate->x - region->winrct.xmin, win->eventstate->y - region->winrct.ymin}; + mval_ = {win->eventstate->xy[0] - region->winrct.xmin, + win->eventstate->xy[1] - region->winrct.ymin}; } GeometrySet DatasetDrawContext::geometry_set_from_component(GeometryComponentType component) diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 89e92231657..f8dc61f18f2 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -302,7 +302,7 @@ static void text_cursor(wmWindow *win, ScrArea *area, ARegion *region) int wmcursor = WM_CURSOR_TEXT_EDIT; if (st->text && BLI_rcti_isect_pt(&st->runtime.scroll_region_handle, - win->eventstate->x - region->winrct.xmin, + win->eventstate->xy[0] - region->winrct.xmin, st->runtime.scroll_region_handle.ymin)) { wmcursor = WM_CURSOR_DEFAULT; } diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index c3bc474b98a..b7a79a320e8 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -2592,7 +2592,7 @@ static void text_scroll_apply(bContext *C, wmOperator *op, const wmEvent *event) { SpaceText *st = CTX_wm_space_text(C); TextScroll *tsc = op->customdata; - const int mval[2] = {event->x, event->y}; + const int mval[2] = {event->xy[0], event->xy[1]}; text_update_character_width(st); @@ -2757,11 +2757,11 @@ static int text_scroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEPAN) { text_update_character_width(st); - tsc->mval_prev[0] = event->x; - tsc->mval_prev[1] = event->y; + tsc->mval_prev[0] = event->xy[0]; + tsc->mval_prev[1] = event->xy[1]; /* Sensitivity of scroll set to 4pix per line/char */ - tsc->mval_delta[0] = (event->x - event->prevx) * st->runtime.cwidth_px / 4; - tsc->mval_delta[1] = (event->y - event->prevy) * st->runtime.lheight_px / 4; + tsc->mval_delta[0] = (event->xy[0] - event->prev_xy[0]) * st->runtime.cwidth_px / 4; + tsc->mval_delta[1] = (event->xy[1] - event->prev_xy[1]) * st->runtime.lheight_px / 4; tsc->is_first = false; tsc->is_scrollbar = false; text_scroll_apply(C, op, event); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 787cf529483..7999018a6b6 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -474,7 +474,7 @@ static void view3d_main_region_exit(wmWindowManager *wm, ARegion *region) static bool view3d_drop_in_main_region_poll(bContext *C, const wmEvent *event) { ScrArea *area = CTX_wm_area(C); - return ED_region_overlap_isect_any_xy(area, &event->x) == false; + return ED_region_overlap_isect_any_xy(area, event->xy) == false; } static ID_Type view3d_drop_id_in_main_region_poll_get_id_type(bContext *C, @@ -483,7 +483,7 @@ static ID_Type view3d_drop_id_in_main_region_poll_get_id_type(bContext *C, { const ScrArea *area = CTX_wm_area(C); - if (ED_region_overlap_isect_any_xy(area, &event->x)) { + if (ED_region_overlap_isect_any_xy(area, event->xy)) { return 0; } if (!view3d_drop_in_main_region_poll(C, event)) { @@ -564,7 +564,7 @@ static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C), static bool view3d_ima_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { - if (ED_region_overlap_isect_any_xy(CTX_wm_area(C), &event->x)) { + if (ED_region_overlap_isect_any_xy(CTX_wm_area(C), event->xy)) { return false; } if (drag->type == WM_DRAG_PATH) { diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index ba9b104a9cc..e7e41799dcb 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -191,7 +191,7 @@ typedef struct ViewOpsData { float dist; float camzoom; float quat[4]; - /** #wmEvent.x, y. */ + /** #wmEvent.xy. */ int event_xy[2]; /** Offset to use when #VIEWOPS_FLAG_USE_MOUSE_INIT is not set. * so we can simulate pressing in the middle of the screen. */ @@ -473,8 +473,8 @@ static void viewops_data_create(bContext *C, vod->init.dist = rv3d->dist; vod->init.camzoom = rv3d->camzoom; copy_qt_qt(vod->init.quat, rv3d->viewquat); - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->x; - vod->init.event_xy[1] = vod->prev.event_xy[1] = event->y; + vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; + vod->init.event_xy[1] = vod->prev.event_xy[1] = event->xy[1]; if (viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) { vod->init.event_xy_offset[0] = 0; @@ -482,8 +482,8 @@ static void viewops_data_create(bContext *C, } else { /* Simulate the event starting in the middle of the region. */ - vod->init.event_xy_offset[0] = BLI_rcti_cent_x(&vod->region->winrct) - event->x; - vod->init.event_xy_offset[1] = BLI_rcti_cent_y(&vod->region->winrct) - event->y; + vod->init.event_xy_offset[0] = BLI_rcti_cent_x(&vod->region->winrct) - event->xy[0]; + vod->init.event_xy_offset[1] = BLI_rcti_cent_y(&vod->region->winrct) - event->xy[1]; } vod->init.event_type = event->type; @@ -549,8 +549,8 @@ static void viewops_data_create(bContext *C, { const int event_xy_offset[2] = { - event->x + vod->init.event_xy_offset[0], - event->y + vod->init.event_xy_offset[1], + event->xy[0] + vod->init.event_xy_offset[0], + event->xy[1] + vod->init.event_xy_offset[1], }; /* For rotation with trackball rotation. */ calctrackballvec(&vod->region->winrct, event_xy_offset, vod->init.trackvec); @@ -955,7 +955,7 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (event_code == VIEW_APPLY) { - viewrotate_apply(vod, &event->x); + viewrotate_apply(vod, event->xy); if (ED_screen_animation_playing(CTX_wm_manager(C))) { use_autokey = true; } @@ -1006,18 +1006,18 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEPAN) { if (event->is_direction_inverted) { - event_xy[0] = 2 * event->x - event->prevx; - event_xy[1] = 2 * event->y - event->prevy; + event_xy[0] = 2 * event->xy[0] - event->prev_xy[0]; + event_xy[1] = 2 * event->xy[1] - event->prev_xy[1]; } else { - event_xy[0] = event->prevx; - event_xy[1] = event->prevy; + event_xy[0] = event->prev_xy[0]; + event_xy[1] = event->prev_xy[1]; } } else { /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */ - event_xy[0] = event->prevx; - event_xy[1] = event->y; + event_xy[0] = event->prev_xy[0]; + event_xy[1] = event->xy[1]; } viewrotate_apply(vod, event_xy); @@ -1799,7 +1799,7 @@ static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (event_code == VIEW_APPLY) { - viewmove_apply(vod, event->x, event->y); + viewmove_apply(vod, event->xy[0], event->xy[1]); if (ED_screen_animation_playing(CTX_wm_manager(C))) { use_autokey = true; } @@ -1844,7 +1844,8 @@ static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEPAN) { /* invert it, trackpad scroll follows same principle as 2d windows this way */ - viewmove_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy); + viewmove_apply( + vod, 2 * event->xy[0] - event->prev_xy[0], 2 * event->xy[1] - event->prev_xy[1]); viewops_data_free(C, op); @@ -1924,7 +1925,7 @@ void viewzoom_modal_keymap(wmKeyConfig *keyconf) /** * \param zoom_xy: Optionally zoom to window location - * (coords compatible w/ #wmEvent.x, y). Use when not NULL. + * (coords compatible w/ #wmEvent.xy). Use when not NULL. */ static void view_zoom_to_window_xy_camera(Scene *scene, Depsgraph *depsgraph, @@ -1977,7 +1978,7 @@ static void view_zoom_to_window_xy_camera(Scene *scene, /** * \param zoom_xy: Optionally zoom to window location - * (coords compatible w/ #wmEvent.x, y). Use when not NULL. + * (coords compatible w/ #wmEvent.xy). Use when not NULL. */ static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoom_xy[2]) { @@ -2249,7 +2250,7 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event_code == VIEW_APPLY) { const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); viewzoom_apply(vod, - &event->x, + event->xy, (eViewZoom_Style)U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0, (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); @@ -2374,8 +2375,8 @@ static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* if one or the other zoom position aren't set, set from event */ if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) { - RNA_int_set(op->ptr, "mx", event->x); - RNA_int_set(op->ptr, "my", event->y); + RNA_int_set(op->ptr, "mx", event->xy[0]); + RNA_int_set(op->ptr, "my", event->xy[1]); } if (RNA_struct_property_is_set(op->ptr, "delta")) { @@ -2385,15 +2386,15 @@ static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) { if (U.uiflag & USER_ZOOM_HORIZ) { - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->x; + vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; } else { /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */ - vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->x - - event->prevx; + vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] - + event->prev_xy[0]; } viewzoom_apply(vod, - &event->prevx, + event->prev_xy, USER_ZOOM_DOLLY, (U.uiflag & USER_ZOOM_INVERT) != 0, (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); @@ -2572,7 +2573,7 @@ static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (event_code == VIEW_APPLY) { - viewdolly_apply(vod, &event->x, (U.uiflag & USER_ZOOM_INVERT) != 0); + viewdolly_apply(vod, event->xy, (U.uiflag & USER_ZOOM_INVERT) != 0); if (ED_screen_animation_playing(CTX_wm_manager(C))) { use_autokey = true; } @@ -2688,8 +2689,8 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* if one or the other zoom position aren't set, set from event */ if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) { - RNA_int_set(op->ptr, "mx", event->x); - RNA_int_set(op->ptr, "my", event->y); + RNA_int_set(op->ptr, "mx", event->xy[0]); + RNA_int_set(op->ptr, "my", event->xy[1]); } if (RNA_struct_property_is_set(op->ptr, "delta")) { @@ -2706,14 +2707,14 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* Bypass Zoom invert flag for track pads (pass false always) */ if (U.uiflag & USER_ZOOM_HORIZ) { - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->x; + vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; } else { /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */ - vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->x - - event->prevx; + vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] - + event->prev_xy[0]; } - viewdolly_apply(vod, &event->prevx, (U.uiflag & USER_ZOOM_INVERT) == 0); + viewdolly_apply(vod, event->prev_xy, (U.uiflag & USER_ZOOM_INVERT) == 0); viewops_data_free(C, op); return OPERATOR_FINISHED; @@ -4421,7 +4422,7 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (event_code == VIEW_APPLY) { - viewroll_apply(vod, event->x, event->y); + viewroll_apply(vod, event->xy[0], event->xy[1]); if (ED_screen_animation_playing(CTX_wm_manager(C))) { use_autokey = true; } @@ -4535,8 +4536,8 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) negate_v3(vod->init.mousevec); if (event->type == MOUSEROTATE) { - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->x; - viewroll_apply(vod, event->prevx, event->prevy); + vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; + viewroll_apply(vod, event->prev_xy[0], event->prev_xy[1]); viewops_data_free(C, op); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_view3d/view3d_navigate_fly.c b/source/blender/editors/space_view3d/view3d_navigate_fly.c index 5752837c40f..f48e436e014 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_fly.c +++ b/source/blender/editors/space_view3d/view3d_navigate_fly.c @@ -539,7 +539,7 @@ static void flyEvent(FlyInfo *fly, const wmEvent *event) /* Speed adjusting with mouse-pan (track-pad). */ case FLY_MODAL_SPEED: { - float fac = 0.02f * (event->prevy - event->y); + float fac = 0.02f * (event->prev_xy[1] - event->xy[1]); /* allowing to brake immediate */ if (fac > 0.0f && fly->speed < 0.0f) { diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index ec3847acbce..86afe1172a1 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1025,7 +1025,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) case TFM_MODAL_PROPSIZE: /* MOUSEPAN usage... */ if (t->flag & T_PROP_EDIT) { - float fac = 1.0f + 0.005f * (event->y - event->prevy); + float fac = 1.0f + 0.005f * (event->xy[1] - event->prev_xy[1]); t->prop_size *= fac; if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) { t->prop_size = max_ff(min_ff(t->prop_size, ((View3D *)t->view)->clip_end), diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c index fc351ab728c..97540068c30 100644 --- a/source/blender/editors/util/ed_draw.c +++ b/source/blender/editors/util/ed_draw.c @@ -356,12 +356,12 @@ static void slider_draw(const struct bContext *UNUSED(C), ARegion *region, void static void slider_update_factor(tSlider *slider, const wmEvent *event) { - const float factor_delta = (event->x - slider->last_cursor[0]) / SLIDE_PIXEL_DISTANCE; + const float factor_delta = (event->xy[0] - slider->last_cursor[0]) / SLIDE_PIXEL_DISTANCE; /* Reduced factor delta in precision mode (shift held). */ slider->raw_factor += slider->precision ? (factor_delta / 8) : factor_delta; slider->factor = slider->raw_factor; - slider->last_cursor[0] = event->x; - slider->last_cursor[1] = event->y; + slider->last_cursor[0] = event->xy[0]; + slider->last_cursor[1] = event->xy[1]; if (!slider->overshoot) { slider->factor = clamp_f(slider->factor, 0, 1); @@ -403,8 +403,8 @@ tSlider *ED_slider_create(struct bContext *C) */ void ED_slider_init(struct tSlider *slider, const wmEvent *event) { - slider->last_cursor[0] = event->x; - slider->last_cursor[1] = event->y; + slider->last_cursor[0] = event->xy[0]; + slider->last_cursor[1] = event->xy[1]; } /** @@ -533,8 +533,8 @@ void ED_region_draw_mouse_line_cb(const bContext *C, ARegion *region, void *arg_ wmWindow *win = CTX_wm_window(C); const float *mval_src = (float *)arg_info; const float mval_dst[2] = { - win->eventstate->x - region->winrct.xmin, - win->eventstate->y - region->winrct.ymin, + win->eventstate->xy[0] - region->winrct.xmin, + win->eventstate->xy[1] - region->winrct.ymin, }; const uint shdr_pos = GPU_vertformat_attr_add( diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index 7f1a53cc1e8..38ae98c678f 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -451,7 +451,7 @@ void ED_imbuf_sample_draw(const bContext *C, ARegion *region, void *arg_info) rctf sample_rect_fl; BLI_rctf_init_pt_radius( &sample_rect_fl, - (float[2]){event->x - region->winrct.xmin, event->y - region->winrct.ymin}, + (float[2]){event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin}, (float)(info->sample_size / 2.0f) * sima->zoom); GPU_logic_op_xor_set(true); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index f46e4a0e7a6..1cb1397a0ed 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -2139,13 +2139,13 @@ static void rna_def_event(BlenderRNA *brna) /* mouse */ prop = RNA_def_property(srna, "mouse_x", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "x"); + RNA_def_property_int_sdna(prop, NULL, "xy[0]"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Mouse X Position", "The window relative horizontal location of the mouse"); prop = RNA_def_property(srna, "mouse_y", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "y"); + RNA_def_property_int_sdna(prop, NULL, "xy[1]"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Mouse Y Position", "The window relative vertical location of the mouse"); @@ -2163,13 +2163,13 @@ static void rna_def_event(BlenderRNA *brna) prop, "Mouse Y Position", "The region relative vertical location of the mouse"); prop = RNA_def_property(srna, "mouse_prev_x", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "prevx"); + RNA_def_property_int_sdna(prop, NULL, "prev_xy[0]"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Mouse Previous X Position", "The window relative horizontal location of the mouse"); prop = RNA_def_property(srna, "mouse_prev_y", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "prevy"); + RNA_def_property_int_sdna(prop, NULL, "prev_xy[1]"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Mouse Previous Y Position", "The window relative vertical location of the mouse"); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index 09ed525fd87..06edf6f443e 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -652,8 +652,8 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win, e.type = type; e.val = value; e.is_repeat = false; - e.x = x; - e.y = y; + e.xy[0] = x; + e.xy[1] = y; e.shift = shift; e.ctrl = ctrl; diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 081e76e3d5b..105e533ad22 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -597,7 +597,7 @@ typedef struct wmTabletData { * - The previous values are only set for mouse button and keyboard events. * See: #ISMOUSE_BUTTON & #ISKEYBOARD macros. * - * - Previous x/y are exceptions: #wmEvent.prevx & #wmEvent.prevy + * - Previous x/y are exceptions: #wmEvent.prev * these are set on mouse motion, see #MOUSEMOVE & track-pad events. * * - Modal key-map handling sets `prevval` & `prevtype` to `val` & `type`, @@ -611,7 +611,7 @@ typedef struct wmEvent { /** Press, release, scroll-value. */ short val; /** Mouse pointer position, screen coord. */ - int x, y; + int xy[2]; /** Region relative mouse position (name convention before Blender 2.5). */ int mval[2]; /** @@ -638,13 +638,13 @@ typedef struct wmEvent { /** The time when the key is pressed, see #PIL_check_seconds_timer. */ double prevclicktime; /** The location when the key is pressed (used to enforce drag thresholds). */ - int prevclickx, prevclicky; + int prev_click_xy[2]; /** - * The previous value of #wmEvent.x #wmEvent.y, + * The previous value of #wmEvent.xy, * Unlike other previous state variables, this is set on any mouse motion. - * Use `prevclickx` & `prevclicky` for the value at time of pressing. + * Use `prevclick` for the value at time of pressing. */ - int prevx, prevy; + int prev_xy[2]; /** Modifier states. */ /** 'oskey' is apple or windows-key, value denotes order of pressed. */ @@ -665,7 +665,7 @@ typedef struct wmEvent { /** * True if the operating system inverted the delta x/y values and resulting - * `prevx`, `prevy` values, for natural scroll direction. + * `prev_xy` values, for natural scroll direction. * For absolute scroll direction, the delta must be negated again. */ char is_direction_inverted; diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index b667872a914..ceaec94e70b 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -380,7 +380,7 @@ typedef struct wmGizmoType { /** * Returns screen-space bounding box in the window space - * (compatible with #wmEvent.x #wmEvent.y). + * (compatible with #wmEvent.xy). * * Used for tool-tip placement (otherwise the cursor location is used). */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 295196c701b..6e1da3e6716 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -1073,7 +1073,7 @@ void wm_gizmomap_modal_set( if ((gz->flag & WM_GIZMO_MOVE_CURSOR) && (event->tablet.is_motion_absolute == false)) { WM_cursor_grab_enable(win, WM_CURSOR_WRAP_XY, true, NULL); - copy_v2_v2_int(gzmap->gzmap_context.event_xy, &event->x); + copy_v2_v2_int(gzmap->gzmap_context.event_xy, &event->xy[0]); gzmap->gzmap_context.event_grabcursor = win->grabcursor; } else { diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index b76b1672543..0e51619b50a 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -311,7 +311,8 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e const int winsize_y = WM_window_pixels_y(win); /* for multiwin drags, we only do this if mouse inside */ - if (event->x < 0 || event->y < 0 || event->x > winsize_x || event->y > winsize_y) { + if (event->xy[0] < 0 || event->xy[1] < 0 || event->xy[0] > winsize_x || + event->xy[1] > winsize_y) { return; } @@ -650,8 +651,8 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) wmWindowManager *wm = CTX_wm_manager(C); const int winsize_y = WM_window_pixels_y(win); - int cursorx = win->eventstate->x; - int cursory = win->eventstate->y; + int cursorx = win->eventstate->xy[0]; + int cursory = win->eventstate->xy[1]; if (rect) { rect->xmin = rect->xmax = cursorx; rect->ymin = rect->ymax = cursory; diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 328950cf8f9..b5e81528e2b 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -121,7 +121,7 @@ static void wm_paintcursor_draw(bContext *C, ScrArea *area, ARegion *region) pc->draw(C, x, y, pc->customdata); } else { - pc->draw(C, win->eventstate->x, win->eventstate->y, pc->customdata); + pc->draw(C, win->eventstate->xy[0], win->eventstate->xy[1], pc->customdata); } GPU_scissor_test(false); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 8b77c460be3..85abd8469fe 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -103,8 +103,8 @@ void WM_event_print(const wmEvent *event) event->oskey, event->keymodifier, event->is_repeat, - event->x, - event->y, + event->xy[0], + event->xy[1], event->ascii, BLI_str_utf8_size(event->utf8_buf), event->utf8_buf, @@ -316,8 +316,8 @@ bool WM_event_drag_test_with_delta(const wmEvent *event, const int drag_delta[2] bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2]) { const int drag_delta[2] = { - prev_xy[0] - event->x, - prev_xy[1] - event->y, + prev_xy[0] - event->xy[0], + prev_xy[1] - event->xy[1], }; return WM_event_drag_test_with_delta(event, drag_delta); } @@ -476,7 +476,7 @@ bool WM_event_is_tablet(const struct wmEvent *event) int WM_event_absolute_delta_x(const struct wmEvent *event) { - int dx = event->x - event->prevx; + int dx = event->xy[0] - event->prev_xy[0]; if (!event->is_direction_inverted) { dx = -dx; @@ -487,7 +487,7 @@ int WM_event_absolute_delta_x(const struct wmEvent *event) int WM_event_absolute_delta_y(const struct wmEvent *event) { - int dy = event->y - event->prevy; + int dy = event->xy[1] - event->prev_xy[1]; if (!event->is_direction_inverted) { dy = -dy; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index dffbea64a30..54826649099 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -153,12 +153,12 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) /* Logic for setting previous value is documented on the #wmEvent struct, * see #wm_event_add_ghostevent for the implementation of logic this follows. */ - win->eventstate->x = event->x; - win->eventstate->y = event->y; + win->eventstate->xy[0] = event->xy[0]; + win->eventstate->xy[1] = event->xy[1]; if (event->type == MOUSEMOVE) { - win->eventstate->prevx = event->prevx = win->eventstate->x; - win->eventstate->prevy = event->prevy = win->eventstate->y; + win->eventstate->prev_xy[0] = event->prev_xy[0] = win->eventstate->xy[0]; + win->eventstate->prev_xy[1] = event->prev_xy[1] = win->eventstate->xy[1]; } else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) { win->eventstate->prevval = event->prevval = win->eventstate->val; @@ -169,8 +169,7 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) if (event->val == KM_PRESS) { if (event->is_repeat == false) { - win->eventstate->prevclickx = event->x; - win->eventstate->prevclicky = event->y; + copy_v2_v2_int(win->eventstate->prev_click_xy, event->xy); } } } @@ -1285,8 +1284,8 @@ static void wm_region_mouse_co(bContext *C, wmEvent *event) ARegion *region = CTX_wm_region(C); if (region) { /* Compatibility convention. */ - event->mval[0] = event->x - region->winrct.xmin; - event->mval[1] = event->y - region->winrct.ymin; + event->mval[0] = event->xy[0] - region->winrct.xmin; + event->mval[1] = event->xy[1] - region->winrct.ymin; } else { /* These values are invalid (avoid odd behavior by relying on old mval values). */ @@ -1422,10 +1421,10 @@ static int wm_operator_invoke(bContext *C, } if (region && region->regiontype == RGN_TYPE_WINDOW && - BLI_rcti_isect_pt_v(®ion->winrct, &event->x)) { + BLI_rcti_isect_pt_v(®ion->winrct, event->xy)) { winrect = ®ion->winrct; } - else if (area && BLI_rcti_isect_pt_v(&area->totrct, &event->x)) { + else if (area && BLI_rcti_isect_pt_v(&area->totrct, event->xy)) { winrect = &area->totrct; } @@ -1893,7 +1892,8 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const CTX_wm_area_set(C, area); if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) { - region = BKE_area_find_region_xy(area, handler->context.region_type, event->x, event->y); + region = BKE_area_find_region_xy( + area, handler->context.region_type, event->xy[0], event->xy[1]); if (region) { handler->context.region = region; } @@ -2491,7 +2491,8 @@ static int wm_handler_fileselect_do(bContext *C, wm_window_make_drawable(wm, ctx_win); /* Ensure correct cursor position, otherwise, popups may close immediately after * opening (UI_BLOCK_MOVEMOUSE_QUIT). */ - wm_cursor_position_get(ctx_win, &ctx_win->eventstate->x, &ctx_win->eventstate->y); + wm_cursor_position_get( + ctx_win, &ctx_win->eventstate->xy[0], &ctx_win->eventstate->xy[1]); wm->winactive = ctx_win; /* Reports use this... */ if (handler->context.win == win) { handler->context.win = NULL; @@ -2811,7 +2812,7 @@ static int wm_handlers_do_gizmo_handler(bContext *C, * noticeable for the node editor - where dragging on a node should move it, see: T73212. * note we still allow for starting the gizmo drag outside, then travel 'inside' the node. */ if (region->type->clip_gizmo_events_by_ui) { - if (UI_region_block_find_mouse_over(region, &event->x, true)) { + if (UI_region_block_find_mouse_over(region, &event->xy[0], true)) { if (gz != NULL && event->type != EVT_GIZMO_UPDATE) { if (restore_highlight_unless_activated == false) { WM_tooltip_clear(C, CTX_wm_window(C)); @@ -3172,16 +3173,16 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) /* Test for CLICK_DRAG events. */ if (wm_action_not_handled(action)) { if (win->event_queue_check_drag) { - if (WM_event_drag_test(event, &event->prevclickx)) { + if (WM_event_drag_test(event, event->prev_click_xy)) { win->event_queue_check_drag_handled = true; - int x = event->x; - int y = event->y; + int x = event->xy[0]; + int y = event->xy[1]; short val = event->val; short type = event->type; - event->x = event->prevclickx; - event->y = event->prevclicky; + event->xy[0] = event->prev_click_xy[0]; + event->xy[1] = event->prev_click_xy[1]; event->val = KM_CLICK_DRAG; event->type = event->prevtype; @@ -3191,8 +3192,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) event->val = val; event->type = type; - event->x = x; - event->y = y; + event->xy[0] = x; + event->xy[1] = y; win->event_queue_check_click = false; if (!wm_action_not_handled(action)) { @@ -3230,18 +3231,18 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) if (event->val == KM_RELEASE) { if (event->prevval == KM_PRESS) { if (win->event_queue_check_click == true) { - if (WM_event_drag_test(event, &event->prevclickx)) { + if (WM_event_drag_test(event, event->prev_click_xy)) { win->event_queue_check_click = false; win->event_queue_check_drag = false; } else { /* Position is where the actual click happens, for more * accurate selecting in case the mouse drifts a little. */ - int x = event->x; - int y = event->y; + int x = event->xy[0]; + int y = event->xy[1]; - event->x = event->prevclickx; - event->y = event->prevclicky; + event->xy[0] = event->prev_click_xy[0]; + event->xy[1] = event->prev_click_xy[1]; event->val = KM_CLICK; CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK"); @@ -3249,8 +3250,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) action |= wm_handlers_do_intern(C, win, event, handlers); event->val = KM_RELEASE; - event->x = x; - event->y = y; + event->xy[0] = x; + event->xy[1] = y; } } } @@ -3301,7 +3302,7 @@ static bool wm_event_inside_rect(const wmEvent *event, const rcti *rect) if (wm_event_always_pass(event)) { return true; } - if (BLI_rcti_isect_pt_v(rect, &event->x)) { + if (BLI_rcti_isect_pt_v(rect, event->xy)) { return true; } return false; @@ -3312,7 +3313,7 @@ static bool wm_event_inside_region(const wmEvent *event, const ARegion *region) if (wm_event_always_pass(event)) { return true; } - return ED_region_contains_xy(region, &event->x); + return ED_region_contains_xy(region, event->xy); } static ScrArea *area_event_inside(bContext *C, const int xy[2]) @@ -3371,11 +3372,11 @@ static void wm_paintcursor_test(bContext *C, const wmEvent *event) } /* If previous position was not in current region, we have to set a temp new context. */ - if (region == NULL || !BLI_rcti_isect_pt_v(®ion->winrct, &event->prevx)) { + if (region == NULL || !BLI_rcti_isect_pt_v(®ion->winrct, event->prev_xy)) { ScrArea *area = CTX_wm_area(C); - CTX_wm_area_set(C, area_event_inside(C, &event->prevx)); - CTX_wm_region_set(C, region_event_inside(C, &event->prevx)); + CTX_wm_area_set(C, area_event_inside(C, event->prev_xy)); + CTX_wm_region_set(C, region_event_inside(C, event->prev_xy)); wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C)); @@ -3637,15 +3638,15 @@ void wm_event_do_handlers(bContext *C) /* Clear tool-tip on mouse move. */ if (screen->tool_tip && screen->tool_tip->exit_on_event) { if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, &event->x) > U.move_threshold) { + if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) > U.move_threshold) { WM_tooltip_clear(C, win); } } } /* We let modal handlers get active area/region, also wm_paintcursor_test needs it. */ - CTX_wm_area_set(C, area_event_inside(C, &event->x)); - CTX_wm_region_set(C, region_event_inside(C, &event->x)); + CTX_wm_area_set(C, area_event_inside(C, event->xy)); + CTX_wm_region_set(C, region_event_inside(C, event->xy)); /* MVC demands to not draw in event handlers... * but we need to leave it for ogl selecting etc. */ @@ -3682,7 +3683,7 @@ void wm_event_do_handlers(bContext *C) if (event->type == MOUSEMOVE) { /* State variables in screen, cursors. * Also used in wm_draw.c, fails for modal handlers though. */ - ED_screen_set_active_region(C, win, &event->x); + ED_screen_set_active_region(C, win, event->xy); /* For regions having custom cursors. */ wm_paintcursor_test(C, event); } @@ -3704,7 +3705,7 @@ void wm_event_do_handlers(bContext *C) /* Update azones if needed - done here because it needs to be independent from redraws. */ if (area->flag & AREA_FLAG_ACTIONZONES_UPDATE) { - ED_area_azones_update(area, &event->x); + ED_area_azones_update(area, event->xy); } if (wm_event_inside_rect(event, &area->totrct)) { @@ -3757,8 +3758,8 @@ void wm_event_do_handlers(bContext *C) if ((action & WM_HANDLER_BREAK) == 0) { /* Also some non-modal handlers need active area/region. */ - CTX_wm_area_set(C, area_event_inside(C, &event->x)); - CTX_wm_region_set(C, region_event_inside(C, &event->x)); + CTX_wm_area_set(C, area_event_inside(C, event->xy)); + CTX_wm_region_set(C, region_event_inside(C, event->xy)); wm_region_mouse_co(C, event); @@ -3787,8 +3788,8 @@ void wm_event_do_handlers(bContext *C) } /* Update previous mouse position for following events to use. */ - win->eventstate->prevx = event->x; - win->eventstate->prevy = event->y; + win->eventstate->prev_xy[0] = event->xy[0]; + win->eventstate->prev_xy[1] = event->xy[1]; /* Unlink and free here, blender-quit then frees all. */ BLI_remlink(&win->event_queue, event); @@ -3798,10 +3799,10 @@ void wm_event_do_handlers(bContext *C) /* Only add mouse-move when the event queue was read entirely. */ if (win->addmousemove && win->eventstate) { wmEvent tevent = *(win->eventstate); - // printf("adding MOUSEMOVE %d %d\n", tevent.x, tevent.y); + // printf("adding MOUSEMOVE %d %d\n", tevent.xy[0], tevent.xy[1]); tevent.type = MOUSEMOVE; - tevent.prevx = tevent.x; - tevent.prevy = tevent.y; + tevent.prev_xy[0] = tevent.xy[0]; + tevent.prev_xy[1] = tevent.xy[1]; tevent.is_repeat = false; wm_event_add(win, &tevent); win->addmousemove = 0; @@ -4180,10 +4181,10 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler_priority(ListBase *handlers, static bool event_or_prev_in_rect(const wmEvent *event, const rcti *rect) { - if (BLI_rcti_isect_pt(rect, event->x, event->y)) { + if (BLI_rcti_isect_pt(rect, event->xy[0], event->xy[1])) { return true; } - if (event->type == MOUSEMOVE && BLI_rcti_isect_pt(rect, event->prevx, event->prevy)) { + if (event->type == MOUSEMOVE && BLI_rcti_isect_pt(rect, event->prev_xy[0], event->prev_xy[1])) { return true; } return false; @@ -4659,7 +4660,7 @@ static void attach_ndof_data(wmEvent *event, const GHOST_TEventNDOFMotionData *g /* Imperfect but probably usable... draw/enable drags to other windows. */ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *event) { - int mval[2] = {event->x, event->y}; + int mval[2] = {event->xy[0], event->xy[1]}; if (wm->windows.first == wm->windows.last) { return NULL; @@ -4681,8 +4682,8 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi wmWindow *win_other = WM_window_find_under_cursor(wm, win, win, mval, mval); if (win_other) { - event->x = mval[0]; - event->y = mval[1]; + event->xy[0] = mval[0]; + event->xy[1] = mval[1]; return win_other; } } @@ -4693,7 +4694,7 @@ static bool wm_event_is_double_click(const wmEvent *event) { if ((event->type == event->prevtype) && (event->prevval == KM_RELEASE) && (event->val == KM_PRESS)) { - if (ISMOUSE(event->type) && WM_event_drag_test(event, &event->prevclickx)) { + if (ISMOUSE(event->type) && WM_event_drag_test(event, event->prev_click_xy)) { /* Pass. */ } else { @@ -4718,8 +4719,8 @@ static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state) static void wm_event_prev_click_set(wmEvent *event, wmEvent *event_state) { event->prevclicktime = event_state->prevclicktime = PIL_check_seconds_timer(); - event->prevclickx = event_state->prevclickx = event_state->x; - event->prevclicky = event_state->prevclicky = event_state->y; + event->prev_click_xy[0] = event_state->prev_click_xy[0] = event_state->xy[1]; + event->prev_click_xy[1] = event_state->prev_click_xy[1] = event_state->xy[1]; } static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) @@ -4739,7 +4740,7 @@ static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) event_last = win->eventstate; } - copy_v2_v2_int(&event_new->prevx, &event_last->x); + copy_v2_v2_int(event_new->prev_xy, event_last->xy); return event_new; } @@ -4749,16 +4750,16 @@ static wmEvent *wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int d * for painting with mouse moves, for navigation using the accumulated value is ok. */ wmEvent *event_last = win->event_queue.last; if (event_last && event_last->type == event->type) { - deltax += event_last->x - event_last->prevx; - deltay += event_last->y - event_last->prevy; + deltax += event_last->xy[0] - event_last->prev_xy[0]; + deltay += event_last->xy[1] - event_last->prev_xy[1]; wm_event_free_last(win); } - /* Set prevx/prevy, the delta is computed from this in operators. */ + /* Set prev_xy, the delta is computed from this in operators. */ wmEvent *event_new = wm_event_add(win, event); - event_new->prevx = event_new->x - deltax; - event_new->prevy = event_new->y - deltay; + event_new->prev_xy[0] = event_new->xy[0] - deltax; + event_new->prev_xy[1] = event_new->xy[1] - deltay; return event_new; } @@ -4827,14 +4828,14 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case GHOST_kEventCursorMove: { GHOST_TEventCursorData *cd = customdata; - copy_v2_v2_int(&event.x, &cd->x); - wm_stereo3d_mouse_offset_apply(win, &event.x); + copy_v2_v2_int(event.xy, &cd->x); + wm_stereo3d_mouse_offset_apply(win, &event.xy[0]); wm_tablet_data_from_ghost(&cd->tablet, &event.tablet); event.type = MOUSEMOVE; { wmEvent *event_new = wm_event_add_mousemove(win, &event); - copy_v2_v2_int(&event_state->x, &event_new->x); + copy_v2_v2_int(event_state->xy, event_new->xy); event_state->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute; } @@ -4848,11 +4849,11 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event_other.prevtype = event_other.type; event_other.prevval = event_other.val; - copy_v2_v2_int(&event_other.x, &event.x); + copy_v2_v2_int(event_other.xy, event.xy); event_other.type = MOUSEMOVE; { wmEvent *event_new = wm_event_add_mousemove(win_other, &event_other); - copy_v2_v2_int(&win_other->eventstate->x, &event_new->x); + copy_v2_v2_int(win_other->eventstate->xy, event_new->xy); win_other->eventstate->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute; } } @@ -4879,8 +4880,8 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void break; } - event.x = event_state->x = pd->x; - event.y = event_state->y = pd->y; + event.xy[0] = event_state->xy[0] = pd->x; + event.xy[1] = event_state->xy[1] = pd->y; event.val = KM_NOTHING; /* The direction is inverted from the device due to system preferences. */ @@ -4947,7 +4948,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event_other.prevtype = event_other.type; event_other.prevval = event_other.val; - copy_v2_v2_int(&event_other.x, &event.x); + copy_v2_v2_int(event_other.xy, event.xy); event_other.type = event.type; event_other.val = event.val; @@ -5125,7 +5126,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void attach_ndof_data(&event, customdata); wm_event_add(win, &event); - CLOG_INFO(WM_LOG_HANDLERS, 1, "sending NDOF_MOTION, prev = %d %d", event.x, event.y); + CLOG_INFO(WM_LOG_HANDLERS, 1, "sending NDOF_MOTION, prev = %d %d", event.xy[0], event.xy[1]); break; } diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index ae2cdb5608c..ab971901a20 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -73,14 +73,14 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new"); gesture->customdata = rect; - rect->xmin = event->x - gesture->winrct.xmin; - rect->ymin = event->y - gesture->winrct.ymin; + rect->xmin = event->xy[0] - gesture->winrct.xmin; + rect->ymin = event->xy[1] - gesture->winrct.ymin; if (type == WM_GESTURE_CIRCLE) { /* caller is responsible for initializing 'xmax' to radius. */ } else { - rect->xmax = event->x - gesture->winrct.xmin; - rect->ymax = event->y - gesture->winrct.ymin; + rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymax = event->xy[1] - gesture->winrct.ymin; } } else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) { @@ -88,8 +88,8 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent gesture->points_alloc = 1024; gesture->customdata = lasso = MEM_mallocN(sizeof(short[2]) * gesture->points_alloc, "lasso points"); - lasso[0] = event->x - gesture->winrct.xmin; - lasso[1] = event->y - gesture->winrct.ymin; + lasso[0] = event->xy[0] - gesture->winrct.xmin; + lasso[1] = event->xy[1] - gesture->winrct.ymin; gesture->points = 1; } diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 578699dda60..19b678d79d8 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -245,17 +245,17 @@ int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: { if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) { - rect->xmin = rect->xmax = event->x - gesture->winrct.xmin; - rect->ymin = rect->ymax = event->y - gesture->winrct.ymin; + rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin; } else if (gesture->move) { BLI_rcti_translate(rect, - (event->x - gesture->winrct.xmin) - rect->xmax, - (event->y - gesture->winrct.ymin) - rect->ymax); + (event->xy[0] - gesture->winrct.xmin) - rect->xmax, + (event->xy[1] - gesture->winrct.ymin) - rect->ymax); } else { - rect->xmax = event->x - gesture->winrct.xmin; - rect->ymax = event->y - gesture->winrct.ymin; + rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymax = event->xy[1] - gesture->winrct.ymin; } gesture_box_apply_rect(op); @@ -365,8 +365,8 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEMOVE) { - rect->xmin = event->x - gesture->winrct.xmin; - rect->ymin = event->y - gesture->winrct.ymin; + rect->xmin = event->xy[0] - gesture->winrct.xmin; + rect->ymin = event->xy[1] - gesture->winrct.ymin; wm_gesture_tag_redraw(win); @@ -381,7 +381,7 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->val) { case GESTURE_MODAL_CIRCLE_SIZE: - fac = 0.3f * (event->y - event->prevy); + fac = 0.3f * (event->xy[1] - event->prev_xy[1]); if (fac > 0) { rect->xmax += ceil(fac); } @@ -500,8 +500,8 @@ static void gesture_tweak_modal(bContext *C, const wmEvent *event) case MOUSEMOVE: case INBETWEEN_MOUSEMOVE: { - rect->xmax = event->x - gesture->winrct.xmin; - rect->ymax = event->y - gesture->winrct.ymin; + rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymax = event->xy[1] - gesture->winrct.ymin; const int val = wm_gesture_evaluate(gesture, event); if (val != 0) { @@ -510,8 +510,8 @@ static void gesture_tweak_modal(bContext *C, const wmEvent *event) wm_event_init_from_window(window, &tevent); /* We want to get coord from start of drag, * not from point where it becomes a tweak event, see T40549. */ - tevent.x = rect->xmin + gesture->winrct.xmin; - tevent.y = rect->ymin + gesture->winrct.ymin; + tevent.xy[0] = rect->xmin + gesture->winrct.xmin; + tevent.xy[1] = rect->ymin + gesture->winrct.ymin; if (gesture->event_type == LEFTMOUSE) { tevent.type = EVT_TWEAK_L; } @@ -696,8 +696,8 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) { short(*lasso)[2] = gesture->customdata; - const int x = ((event->x - gesture->winrct.xmin) - lasso[gesture->points - 1][0]); - const int y = ((event->y - gesture->winrct.ymin) - lasso[gesture->points - 1][1]); + const int x = ((event->xy[0] - gesture->winrct.xmin) - lasso[gesture->points - 1][0]); + const int y = ((event->xy[1] - gesture->winrct.ymin) - lasso[gesture->points - 1][1]); /* move the lasso */ if (gesture->move) { @@ -709,8 +709,8 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Make a simple distance check to get a smoother lasso * add only when at least 2 pixels between this and previous location. */ else if ((x * x + y * y) > pow2f(2.0f * UI_DPI_FAC)) { - lasso[gesture->points][0] = event->x - gesture->winrct.xmin; - lasso[gesture->points][1] = event->y - gesture->winrct.ymin; + lasso[gesture->points][0] = event->xy[0] - gesture->winrct.xmin; + lasso[gesture->points][1] = event->xy[1] - gesture->winrct.ymin; gesture->points++; } } @@ -981,18 +981,18 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev switch (event->type) { case MOUSEMOVE: { if (gesture->is_active == false) { - rect->xmin = rect->xmax = event->x - gesture->winrct.xmin; - rect->ymin = rect->ymax = event->y - gesture->winrct.ymin; + rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin; } else if (gesture->move) { BLI_rcti_translate(rect, - (event->x - gesture->winrct.xmin) - rect->xmax, - (event->y - gesture->winrct.ymin) - rect->ymax); + (event->xy[0] - gesture->winrct.xmin) - rect->xmax, + (event->xy[1] - gesture->winrct.ymin) - rect->ymax); gesture_straightline_apply(C, op); } else { - rect->xmax = event->x - gesture->winrct.xmin; - rect->ymax = event->y - gesture->winrct.ymin; + rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymax = event->xy[1] - gesture->winrct.ymin; gesture_straightline_apply(C, op); } @@ -1072,17 +1072,17 @@ int WM_gesture_straightline_oneshot_modal(bContext *C, wmOperator *op, const wmE switch (event->type) { case MOUSEMOVE: { if (gesture->is_active == false) { - rect->xmin = rect->xmax = event->x - gesture->winrct.xmin; - rect->ymin = rect->ymax = event->y - gesture->winrct.ymin; + rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin; } else if (gesture->move) { BLI_rcti_translate(rect, - (event->x - gesture->winrct.xmin) - rect->xmax, - (event->y - gesture->winrct.ymin) - rect->ymax); + (event->xy[0] - gesture->winrct.xmin) - rect->xmax, + (event->xy[1] - gesture->winrct.ymin) - rect->ymax); } else { - rect->xmax = event->x - gesture->winrct.xmin; - rect->ymax = event->y - gesture->winrct.ymin; + rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymax = event->xy[1] - gesture->winrct.ymin; } if (gesture->use_snap) { diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 0e58e1c0466..b6a4a3a94bf 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2274,11 +2274,11 @@ static void radial_control_set_initial_mouse(RadialControl *rc, const wmEvent *e float d[2] = {0, 0}; float zoom[2] = {1, 1}; - rc->initial_mouse[0] = event->x; - rc->initial_mouse[1] = event->y; + rc->initial_mouse[0] = event->xy[0]; + rc->initial_mouse[1] = event->xy[1]; - rc->initial_co[0] = event->x; - rc->initial_co[1] = event->y; + rc->initial_co[0] = event->xy[0]; + rc->initial_co[1] = event->xy[1]; switch (rc->subtype) { case PROP_NONE: @@ -2954,7 +2954,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even if (!has_numInput) { if (rc->slow_mode) { if (rc->subtype == PROP_ANGLE) { - const float position[2] = {event->x, event->y}; + const float position[2] = {event->xy[0], event->xy[1]}; /* calculate the initial angle here first */ delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0]; @@ -2974,7 +2974,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even dist = len_v2(delta); - delta[0] = event->x - rc->slow_mouse[0]; + delta[0] = event->xy[0] - rc->slow_mouse[0]; if (rc->zoom_prop) { delta[0] /= zoom[0]; @@ -2984,8 +2984,8 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even } } else { - delta[0] = rc->initial_mouse[0] - event->x; - delta[1] = rc->initial_mouse[1] - event->y; + delta[0] = rc->initial_mouse[0] - event->xy[0]; + delta[1] = rc->initial_mouse[1] - event->xy[1]; if (rc->zoom_prop) { RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom); delta[0] /= zoom[0]; @@ -3052,8 +3052,8 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even case EVT_LEFTSHIFTKEY: case EVT_RIGHTSHIFTKEY: { if (event->val == KM_PRESS) { - rc->slow_mouse[0] = event->x; - rc->slow_mouse[1] = event->y; + rc->slow_mouse[0] = event->xy[0]; + rc->slow_mouse[1] = event->xy[1]; rc->slow_mode = true; if (rc->subtype == PROP_ANGLE) { const float initial_position[2] = {UNPACK2(rc->initial_mouse)}; diff --git a/source/blender/windowmanager/intern/wm_tooltip.c b/source/blender/windowmanager/intern/wm_tooltip.c index a9f0e01cfb5..3bcd122f08d 100644 --- a/source/blender/windowmanager/intern/wm_tooltip.c +++ b/source/blender/windowmanager/intern/wm_tooltip.c @@ -131,7 +131,7 @@ void WM_tooltip_init(bContext *C, wmWindow *win) CTX_wm_region_set(C, region_prev); } - copy_v2_v2_int(screen->tool_tip->event_xy, &win->eventstate->x); + copy_v2_v2_int(screen->tool_tip->event_xy, win->eventstate->xy); if (pass_prev != screen->tool_tip->pass) { /* The pass changed, add timer for next pass. */ wmWindowManager *wm = CTX_wm_manager(C); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 2c92d4ac6f8..e8d626512ca 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -521,7 +521,7 @@ void WM_window_set_dpi(const wmWindow *win) static void wm_window_update_eventstate(wmWindow *win) { /* Update mouse position when a window is activated. */ - wm_cursor_position_get(win, &win->eventstate->x, &win->eventstate->y); + wm_cursor_position_get(win, &win->eventstate->xy[0], &win->eventstate->xy[1]); } static void wm_window_ensure_eventstate(wmWindow *win) @@ -990,8 +990,8 @@ void wm_cursor_position_to_ghost(wmWindow *win, int *x, int *y) void wm_cursor_position_get(wmWindow *win, int *r_x, int *r_y) { if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) { - *r_x = win->eventstate->x; - *r_y = win->eventstate->y; + *r_x = win->eventstate->xy[0]; + *r_y = win->eventstate->xy[1]; return; } GHOST_GetCursorPosition(g_system, r_x, r_y); @@ -1266,8 +1266,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wmEvent event; wm_event_init_from_window(win, &event); event.type = MOUSEMOVE; - event.prevx = event.x; - event.prevy = event.y; + copy_v2_v2_int(event.prev_xy, event.xy); event.is_repeat = false; wm_event_add(win, &event); @@ -1398,8 +1397,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* activate region */ event.type = MOUSEMOVE; - event.prevx = event.x; - event.prevy = event.y; + copy_v2_v2_int(event.prev_xy, event.xy); event.is_repeat = false; /* No context change! C->wm->windrawable is drawable, or for area queues. */ @@ -2099,11 +2097,11 @@ void WM_cursor_warp(wmWindow *win, int x, int y) wm_cursor_position_to_ghost(win, &x, &y); GHOST_SetCursorPosition(g_system, x, y); - win->eventstate->prevx = oldx; - win->eventstate->prevy = oldy; + win->eventstate->prev_xy[0] = oldx; + win->eventstate->prev_xy[1] = oldy; - win->eventstate->x = oldx; - win->eventstate->y = oldy; + win->eventstate->xy[0] = oldx; + win->eventstate->xy[1] = oldy; } } From 4539c7cc5786c20aa52e046739e39ce67c8b2a17 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 20 Oct 2021 10:03:46 -0300 Subject: [PATCH 0983/1500] Cleanup: silence Warnings Warning C4100 unreferenced formal parameter Warning C4242 conversion from 'int' to 'short', possible loss of data --- source/blender/editors/space_view3d/view3d_cursor_snap.c | 4 ++-- source/blender/editors/space_view3d/view3d_placement.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 4457afcb67f..52b9f468ca0 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -235,7 +235,7 @@ static void v3d_cursor_plane_draw_grid(const int resolution, i += 1; } } - BLI_assert(i == coords_len); + BLI_assert(i == (int)coords_len); immBeginAtMost(GPU_PRIM_LINES, coords_len * 4); i = 0; for (int x = 0; x < resolution_min; x++) { @@ -661,7 +661,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, else { ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = OBACT(view_layer); - const short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); + const int orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); const int pivot_point = scene->toolsettings->transform_pivot_point; ED_transform_calc_orientation_from_type_ex( scene, view_layer, v3d, region->regiondata, ob, ob, orient_index, pivot_point, omat); diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 09d3aa31811..b6444ebb29b 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -1259,7 +1259,7 @@ static void idp_rna_plane_axis_set_fn(struct PointerRNA *UNUSED(ptr), int value) { V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); - snap_state->plane_axis = value; + snap_state->plane_axis = (short)value; ED_view3d_cursor_snap_state_default_set(snap_state); } From 2d46cef2e88730f40d8927a10102fa9e2a2d4ea3 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 20 Oct 2021 15:21:24 +0200 Subject: [PATCH 0984/1500] Fix T92347: Append function excludes Collections that do not contain Objects directly. Collections directly selected by the user should always be instantiated. Regression from recent append code refactor. --- source/blender/windowmanager/intern/wm_files_link.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 461ae20cf8a..e698702316e 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -462,7 +462,8 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, * children. */ Collection *collection = (Collection *)id; - bool do_add_collection = false; + /* We always add collections directly selected by the user. */ + bool do_add_collection = (item->append_tag & WM_APPEND_TAG_INDIRECT) == 0; LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { Object *ob = coll_ob->ob; if (!object_in_any_scene(bmain, ob)) { From 494c3fb1bdb425059b26abd21d7c9230eda68438 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 20 Oct 2021 16:33:20 +0200 Subject: [PATCH 0985/1500] Fix T92368: LMB mouse events (Selecting, doubleclicks etc) are broken --- source/blender/windowmanager/intern/wm_event_system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 54826649099..3c03d413214 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -4719,7 +4719,7 @@ static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state) static void wm_event_prev_click_set(wmEvent *event, wmEvent *event_state) { event->prevclicktime = event_state->prevclicktime = PIL_check_seconds_timer(); - event->prev_click_xy[0] = event_state->prev_click_xy[0] = event_state->xy[1]; + event->prev_click_xy[0] = event_state->prev_click_xy[0] = event_state->xy[0]; event->prev_click_xy[1] = event_state->prev_click_xy[1] = event_state->xy[1]; } From 990b912fd76cd9a2bf0278e7a5bc4a2029e024d2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 20 Oct 2021 09:57:54 -0500 Subject: [PATCH 0986/1500] Cleanup: Add check whether to remove an anonymous atttribute Add a higher level check that can be used instead of checking whether the attribute ID is anonymous and checking whether it has any strong references. --- source/blender/blenkernel/BKE_attribute_access.hh | 11 +++++++++++ source/blender/blenkernel/intern/geometry_set.cc | 9 ++++----- .../blender/geometry/intern/mesh_to_curve_convert.cc | 8 ++------ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index ff2aebc7d10..4a1c29badb4 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -55,6 +55,7 @@ class AttributeIDRef { bool is_anonymous() const; StringRef name() const; const AnonymousAttributeID &anonymous_id() const; + bool should_be_kept() const; friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b); friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id); @@ -438,6 +439,16 @@ inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const return *anonymous_id_; } +/** + * \return True if the attribute should not be removed automatically as an optimization during + * processing or copying. Anonymous attributes can be removed when they no longer have any + * references. + */ +inline bool AttributeIDRef::should_be_kept() const +{ + return this->is_named() || BKE_anonymous_attribute_id_has_strong_references(anonymous_id_); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index e439cc838e3..4753a9e0768 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -507,12 +507,11 @@ void GeometrySet::gather_attributes_for_propagation( return; } } - if (attribute_id.is_anonymous()) { - if (!BKE_anonymous_attribute_id_has_strong_references(&attribute_id.anonymous_id())) { - /* Don't propagate anonymous attributes that are not used anymore. */ - return; - } + + if (!attribute_id.should_be_kept()) { + return; } + auto add_info = [&](AttributeKind *attribute_kind) { attribute_kind->domain = meta_data.domain; attribute_kind->data_type = meta_data.data_type; diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index 64b9fa5c01d..24f0b6308ba 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -78,12 +78,8 @@ static void copy_attributes_to_points(CurveEval &curve, continue; } - /* Don't copy anonymous attributes with no references anymore. */ - if (attribute_id.is_anonymous()) { - const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); - if (!BKE_anonymous_attribute_id_has_strong_references(&anonymous_id)) { - continue; - } + if (!attribute_id.should_be_kept()) { + continue; } const fn::GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read( From e00bf04c0f59a9ed5bbe79e62f2f81861fbef2d7 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 20 Oct 2021 17:21:30 +0200 Subject: [PATCH 0987/1500] Fix T89771: Cloth disk cache is not read on library overrides when original linked data is already set to use 'Disk Cache' Yet another try at that hairy issue... See comment in commit for details, essentially this extend the workaround introduced in Objects' `lib_override_apply_post` callback to try to also properly 're-use' `OUTDATED` and `BAKED` flags from old source liboverride into new destination one. --- source/blender/blenkernel/intern/object.c | 61 ++++++++++++++++++++--- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 2f3b229a180..0d5fd6aadec 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1131,18 +1131,63 @@ static void object_blend_read_expand(BlendExpander *expander, ID *id) } } -static void object_lib_override_apply_post(ID *id_dst, ID *UNUSED(id_src)) +static void object_lib_override_apply_post(ID *id_dst, ID *id_src) { - Object *object = (Object *)id_dst; + /* id_dst is the new local override copy of the linked reference data. id_src is the old override + * data stored on disk, used as source data for override operations. */ + Object *object_dst = (Object *)id_dst; + Object *object_src = (Object *)id_src; - ListBase pidlist; - BKE_ptcache_ids_from_object(&pidlist, object, NULL, 0); - LISTBASE_FOREACH (PTCacheID *, pid, &pidlist) { - LISTBASE_FOREACH (PointCache *, point_cache, pid->ptcaches) { - point_cache->flag |= PTCACHE_FLAG_INFO_DIRTY; + ListBase pidlist_dst, pidlist_src; + BKE_ptcache_ids_from_object(&pidlist_dst, object_dst, NULL, 0); + BKE_ptcache_ids_from_object(&pidlist_src, object_src, NULL, 0); + + /* Problem with point caches is that several status flags (like OUTDATED or BAKED) are read-only + * at RNA level, and therefore not overridable per-se. + * + * This code is a workaround this to check all pointcaches from both source and destination + * objects in parallel, and transfer those flags when it makes sense. + * + * This allows to keep baked caches across liboverrides applies. + * + * NOTE: This is fairly hackish and weak, but so is the pointcache system as its whole. A more + * robust solution would be e.g. to have a specific RNA entry point to deal with such cases + * (maybe a new flag to allow override code to set values of some read-only properties?). + */ + PTCacheID *pid_src, *pid_dst; + for (pid_dst = pidlist_dst.first, pid_src = pidlist_src.first; pid_dst != NULL; + pid_dst = pid_dst->next, pid_src = (pid_src != NULL) ? pid_src->next : NULL) { + /* If pid's do not match, just tag info of caches in dst as dirty and continue. */ + if (pid_src == NULL || pid_dst->type != pid_src->type || + pid_dst->file_type != pid_src->file_type || + pid_dst->default_step != pid_src->default_step || pid_dst->max_step != pid_src->max_step || + pid_dst->data_types != pid_src->data_types || pid_dst->info_types != pid_src->info_types) { + LISTBASE_FOREACH (PointCache *, point_cache_src, pid_src->ptcaches) { + point_cache_src->flag |= PTCACHE_FLAG_INFO_DIRTY; + } + continue; + } + + PointCache *point_cache_dst, *point_cache_src; + for (point_cache_dst = pid_dst->ptcaches->first, point_cache_src = pid_src->ptcaches->first; + point_cache_dst != NULL; + point_cache_dst = point_cache_dst->next, + point_cache_src = (point_cache_src != NULL) ? point_cache_src->next : NULL) { + /* Always force updating info about caches of applied liboverrides. */ + point_cache_dst->flag |= PTCACHE_FLAG_INFO_DIRTY; + if (point_cache_src == NULL || !STREQ(point_cache_dst->name, point_cache_src->name)) { + continue; + } + if ((point_cache_src->flag & PTCACHE_BAKED) != 0) { + point_cache_dst->flag |= PTCACHE_BAKED; + } + if ((point_cache_src->flag & PTCACHE_OUTDATED) == 0) { + point_cache_dst->flag &= ~PTCACHE_OUTDATED; + } } } - BLI_freelistN(&pidlist); + BLI_freelistN(&pidlist_dst); + BLI_freelistN(&pidlist_src); } IDTypeInfo IDType_ID_OB = { From 334a8d9b3eebaf4abf7c8d18e4512b5f04ecffc0 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 20 Oct 2021 10:25:49 -0500 Subject: [PATCH 0988/1500] Geometry Nodes: Fields version of Curve to Points node This commit adds an updated version of the curve to points that supports fields. Only the position and radius are transferred by default now, which should improve performance. The other outputs like tangent and rotation are outputted with anonymous attributes. I took the opportunity to change a few other small things: - Name geometry sockets "Curve" and "Points" like other nodes. - Remove the radius multiple of 0.1, which was confusing. Thanks to @Johnny Matthews (guitargeek) for an initial patch. Differential Revision: https://developer.blender.org/D12887 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesrna/intern/rna_nodetree.c | 32 ++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 3 +- .../nodes/legacy/node_geo_curve_to_points.cc | 14 +- .../nodes/node_geo_curve_to_points.cc | 390 ++++++++++++++++++ 9 files changed, 431 insertions(+), 13 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index a9112727daa..c870ce13f7d 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -103,6 +103,7 @@ def curve_node_items(context): yield NodeItem("GeometryNodeCurveLength") yield NodeItem("GeometryNodeCurveToMesh") + yield NodeItem("GeometryNodeCurveToPoints") yield NodeItem("GeometryNodeFillCurve") yield NodeItem("GeometryNodeFilletCurve") yield NodeItem("GeometryNodeResampleCurve") diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 2d3b24630b1..a9bba3908ce 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1545,6 +1545,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_SUBDIVISION_SURFACE 1126 #define GEO_NODE_CURVE_ENDPOINT_SELECTION 1127 #define GEO_NODE_RAYCAST 1128 +#define GEO_NODE_CURVE_TO_POINTS 1130 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index b47fe45c868..6b626af7dd2 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5752,6 +5752,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_edge_split(); register_node_type_geo_legacy_subdivision_surface(); register_node_type_geo_legacy_raycast(); + register_node_type_geo_legacy_curve_to_points(); register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index dfefc774b3d..438344304cf 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10528,6 +10528,38 @@ static void def_geo_curve_fillet(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_legacy_curve_to_points(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_RESAMPLE_EVALUATED, + "EVALUATED", + 0, + "Evaluated", + "Create points from the curve's evaluated points, based on the resolution attribute for " + "NURBS and Bezier splines"}, + {GEO_NODE_CURVE_RESAMPLE_COUNT, + "COUNT", + 0, + "Count", + "Sample each spline by evenly distributing the specified number of points"}, + {GEO_NODE_CURVE_RESAMPLE_LENGTH, + "LENGTH", + 0, + "Length", + "Sample each spline by splitting it into segments with the specified length"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveToPoints", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "How to generate points from the input curve"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_to_points(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 39265c13a1f..8630bcd3b73 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -219,6 +219,7 @@ set(SRC geometry/nodes/node_geo_curve_spline_type.cc geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_curve_to_mesh.cc + geometry/nodes/node_geo_curve_to_points.cc geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_distribute_points_on_faces.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 4d75303363c..890825fdcc0 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -33,6 +33,7 @@ void register_node_type_geo_legacy_attribute_transfer(void); void register_node_type_geo_legacy_curve_set_handles(void); void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); +void register_node_type_geo_legacy_curve_to_points(void); void register_node_type_geo_legacy_delete_geometry(void); void register_node_type_geo_legacy_material_assign(void); void register_node_type_geo_legacy_mesh_to_curve(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 78a3735c5b8..b5254f2e898 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -304,7 +304,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_legacy_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_legacy_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_TO_POINTS, def_geo_curve_to_points, "LEGACY_CURVE_TO_POINTS", LegacyCurveToPoints, "Curve to Points", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_TO_POINTS, def_geo_legacy_curve_to_points, "LEGACY_CURVE_TO_POINTS", LegacyCurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_LEGACY_EDGE_SPLIT, 0, "LEGACY_EDGE_SPLIT", LegacyEdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_LEGACY_MATERIAL_ASSIGN, 0, "LEGACY_MATERIAL_ASSIGN", LegacyMaterialAssign, "Material Assign", "") @@ -333,6 +333,7 @@ DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") +DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc index b6409290f31..820d52e2259 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc @@ -286,17 +286,7 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, }); } -void curve_create_default_rotation_attribute(Span tangents, - Span normals, - MutableSpan rotations) -{ - threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) { - for (const int i : range) { - rotations[i] = - float4x4::from_normalized_axis_data({0, 0, 0}, normals[i], tangents[i]).to_euler(); - } - }); -} + static void geo_node_curve_to_points_exec(GeoNodeExecParams params) { @@ -354,7 +344,7 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_curve_to_points() +void register_node_type_geo_legacy_curve_to_points() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc new file mode 100644 index 00000000000..4f4fde6c7df --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -0,0 +1,390 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Curve"); + b.add_input("Count").default_value(10).min(2).max(100000); + b.add_input("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_output("Points"); + b.add_output("Tangent").field_source(); + b.add_output("Normal").field_source(); + b.add_output("Rotation").field_source(); +} + +static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN( + sizeof(NodeGeometryCurveToPoints), __func__); + + data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; + node->storage = data; +} + +static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; + + bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *length_socket = count_socket->next; + + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); +} + +static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, + const GeometryNodeCurveResampleMode mode, + const CurveEval &curve, + const Span splines) +{ + const int size = curve.splines().size(); + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + const int count = params.get_input("Count"); + if (count < 1) { + return {0}; + } + Array offsets(size + 1); + for (const int i : offsets.index_range()) { + offsets[i] = count * i; + } + return offsets; + } + case GEO_NODE_CURVE_RESAMPLE_LENGTH: { + /* Don't allow asymptotic count increase for low resolution values. */ + const float resolution = std::max(params.get_input("Length"), 0.0001f); + Array offsets(size + 1); + int offset = 0; + for (const int i : IndexRange(size)) { + offsets[i] = offset; + offset += splines[i]->length() / resolution + 1; + } + offsets.last() = offset; + return offsets; + } + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: { + return curve.evaluated_point_offsets(); + } + } + BLI_assert_unreachable(); + return {0}; +} + +/** + * \note: Relies on the fact that all attributes on point clouds are stored contiguously. + */ +static GMutableSpan ensure_point_attribute(PointCloudComponent &points, + const AttributeIDRef &attribute_id, + const CustomDataType data_type) +{ + points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); + WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id); + BLI_assert(attribute); + return attribute.varray->get_internal_span(); +} + +template +static MutableSpan ensure_point_attribute(PointCloudComponent &points, + const AttributeIDRef &attribute_id) +{ + GMutableSpan attribute = ensure_point_attribute( + points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get())); + return attribute.typed(); +} + +namespace { +struct AnonymousAttributeIDs { + StrongAnonymousAttributeID tangent_id; + StrongAnonymousAttributeID normal_id; + StrongAnonymousAttributeID rotation_id; +}; + +struct ResultAttributes { + MutableSpan positions; + MutableSpan radii; + + Map point_attributes; + + MutableSpan tangents; + MutableSpan normals; + MutableSpan rotations; +}; +} // namespace + +static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points, + const CurveEval &curve, + const AnonymousAttributeIDs &attributes) +{ + ResultAttributes outputs; + + outputs.positions = ensure_point_attribute(points, "position"); + outputs.radii = ensure_point_attribute(points, "radius"); + + if (attributes.tangent_id) { + outputs.tangents = ensure_point_attribute(points, attributes.tangent_id.get()); + } + if (attributes.normal_id) { + outputs.normals = ensure_point_attribute(points, attributes.normal_id.get()); + } + if (attributes.rotation_id) { + outputs.rotations = ensure_point_attribute(points, attributes.rotation_id.get()); + } + + /* Because of the invariants of the curve component, we use the attributes of the first spline + * as a representative for the attribute meta data all splines. Attributes from the spline domain + * are handled separately. */ + curve.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (id.should_be_kept()) { + outputs.point_attributes.add_new( + id, ensure_point_attribute(points, id, meta_data.data_type)); + } + return true; + }, + ATTR_DOMAIN_POINT); + + return outputs; +} + +/** + * TODO: For non-poly splines, this has double copies that could be avoided as part + * of a general look at optimizing uses of #Spline::interpolate_to_evaluated. + */ +static void copy_evaluated_point_attributes(const Span splines, + const Span offsets, + ResultAttributes &data) +{ + threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + + data.positions.slice(offset, size).copy_from(spline.evaluated_positions()); + spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size)); + + for (const Map::Item item : data.point_attributes.items()) { + const AttributeIDRef attribute_id = item.key; + const GMutableSpan dst = item.value; + + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); + + spline.interpolate_to_evaluated(spline_span)->materialize(dst.slice(offset, size).data()); + } + + if (!data.tangents.is_empty()) { + data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); + } + if (!data.normals.is_empty()) { + data.normals.slice(offset, size).copy_from(spline.evaluated_normals()); + } + } + }); +} + +static void copy_uniform_sample_point_attributes(const Span splines, + const Span offsets, + ResultAttributes &data) +{ + threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + if (size == 0) { + continue; + } + + const Array uniform_samples = spline.sample_uniform_index_factors(size); + + spline.sample_with_index_factors( + spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size)); + spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline.radii()), + uniform_samples, + data.radii.slice(offset, size)); + + for (const Map::Item item : data.point_attributes.items()) { + const AttributeIDRef attribute_id = item.key; + const GMutableSpan dst = item.value; + + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); + + spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span), + uniform_samples, + dst.slice(offset, size)); + } + + if (!data.tangents.is_empty()) { + spline.sample_with_index_factors( + spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size)); + for (float3 &tangent : data.tangents) { + tangent.normalize(); + } + } + + if (!data.normals.is_empty()) { + spline.sample_with_index_factors( + spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size)); + for (float3 &normals : data.normals) { + normals.normalize(); + } + } + } + }); +} + +static void copy_spline_domain_attributes(const CurveEval &curve, + const Span offsets, + PointCloudComponent &points) +{ + curve.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + const GSpan curve_attribute = *curve.attributes.get_for_read(attribute_id); + const CPPType &type = curve_attribute.type(); + const GMutableSpan dst = ensure_point_attribute(points, attribute_id, meta_data.data_type); + + for (const int i : curve.splines().index_range()) { + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + type.fill_assign_n(curve_attribute[i], dst[offset], size); + } + + return true; + }, + ATTR_DOMAIN_CURVE); +} + +void curve_create_default_rotation_attribute(Span tangents, + Span normals, + MutableSpan rotations) +{ + threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) { + for (const int i : range) { + rotations[i] = + float4x4::from_normalized_axis_data({0, 0, 0}, normals[i], tangents[i]).to_euler(); + } + }); +} + +static void geo_node_curve_to_points_exec(GeoNodeExecParams params) +{ + NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; + GeometrySet geometry_set = params.extract_input("Curve"); + + AnonymousAttributeIDs attribute_outputs; + attribute_outputs.tangent_id = StrongAnonymousAttributeID("Tangent"); + attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); + attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + const CurveEval &curve = *geometry_set.get_curve_for_read(); + const Span splines = curve.splines(); + curve.assert_valid_point_attributes(); + + const Array offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const int total_size = offsets.last(); + if (total_size == 0) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size)); + PointCloudComponent &points = geometry_set.get_component_for_write(); + ResultAttributes point_attributes = create_attributes_for_transfer( + points, curve, attribute_outputs); + + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_LENGTH: + copy_uniform_sample_point_attributes(splines, offsets, point_attributes); + break; + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: + copy_evaluated_point_attributes(splines, offsets, point_attributes); + break; + } + + copy_spline_domain_attributes(curve, offsets, points); + + if (!point_attributes.rotations.is_empty()) { + curve_create_default_rotation_attribute( + point_attributes.tangents, point_attributes.normals, point_attributes.rotations); + } + + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES, GEO_COMPONENT_TYPE_POINT_CLOUD}); + }); + + params.set_output("Points", std::move(geometry_set)); + if (attribute_outputs.tangent_id) { + params.set_output( + "Tangent", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.tangent_id))); + } + if (attribute_outputs.normal_id) { + params.set_output( + "Normal", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.normal_id))); + } + if (attribute_outputs.rotation_id) { + params.set_output( + "Rotation", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.rotation_id))); + } +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_to_points() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_to_points_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_points_exec; + ntype.draw_buttons = blender::nodes::geo_node_curve_to_points_layout; + node_type_storage( + &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, blender::nodes::geo_node_curve_to_points_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_to_points_update); + + nodeRegisterType(&ntype); +} From 2537b32392405fe586180d11f116bb39a184f6b4 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Wed, 20 Oct 2021 16:14:59 +0100 Subject: [PATCH 0989/1500] Geometry Nodes: Add Checker Texture Port checker shader to GN Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12762 --- release/scripts/startup/nodeitems_builtins.py | 1 + .../shader/nodes/node_shader_tex_checker.cc | 68 ++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index c870ce13f7d..e481b360300 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -724,6 +724,7 @@ geometry_node_categories = [ NodeItem("FunctionNodeReplaceString"), ]), GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ + NodeItem("ShaderNodeTexChecker"), NodeItem("ShaderNodeTexGradient"), NodeItem("ShaderNodeTexMagic"), NodeItem("ShaderNodeTexMusgrave"), diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc index 26abc66fc3d..7ba468a93e0 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc @@ -59,17 +59,81 @@ static int node_shader_gpu_tex_checker(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_tex_checker", in, out); } -/* node type definition */ +namespace blender::nodes { + +class NodeTexChecker : public fn::MultiFunction { + public: + NodeTexChecker() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Checker"}; + signature.single_input("Vector"); + signature.single_input("Color1"); + signature.single_input("Color2"); + signature.single_input("Scale"); + signature.single_output("Color"); + signature.single_output("Fac"); + return signature.build(); + } + + void call(blender::IndexMask mask, + fn::MFParams params, + fn::MFContext UNUSED(context)) const override + { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &color1 = params.readonly_single_input( + 1, "Color1"); + const VArray &color2 = params.readonly_single_input( + 2, "Color2"); + const VArray &scale = params.readonly_single_input(3, "Scale"); + MutableSpan r_color = + params.uninitialized_single_output_if_required(4, "Color"); + MutableSpan r_fac = params.uninitialized_single_output(5, "Fac"); + + for (int64_t i : mask) { + /* Avoid precision issues on unit coordinates. */ + const float3 p = (vector[i] * scale[i] + 0.000001f) * 0.999999f; + + const int xi = abs((int)(floorf(p.x))); + const int yi = abs((int)(floorf(p.y))); + const int zi = abs((int)(floorf(p.z))); + + r_fac[i] = ((xi % 2 == yi % 2) == (zi % 2)) ? 1.0f : 0.0f; + } + + if (!r_color.is_empty()) { + for (int64_t i : mask) { + r_color[i] = (r_fac[i] == 1.0f) ? color1[i] : color2[i]; + } + } + } +}; + +static void sh_node_tex_checker_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + static NodeTexChecker fn; + builder.set_matching_fn(fn); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_checker(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_CHECKER, "Checker Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_CHECKER, "Checker Texture", NODE_CLASS_TEXTURE, 0); ntype.declare = blender::nodes::sh_node_tex_checker_declare; node_type_init(&ntype, node_shader_init_tex_checker); node_type_storage( &ntype, "NodeTexChecker", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_checker); + ntype.build_multi_function = blender::nodes::sh_node_tex_checker_build_multi_function; nodeRegisterType(&ntype); } From 001f548227c413a4fdbee275744ea8bea886081a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 20 Oct 2021 14:21:01 +0200 Subject: [PATCH 0990/1500] Cycles: reduce kernel reserved local memory when not using shader raytracing Ref T87836 --- intern/cycles/device/cuda/device_impl.cpp | 6 ++++-- intern/cycles/device/hip/device_impl.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp index 5e1a63c04df..1c970096801 100644 --- a/intern/cycles/device/cuda/device_impl.cpp +++ b/intern/cycles/device/cuda/device_impl.cpp @@ -454,7 +454,7 @@ bool CUDADevice::load_kernels(const uint kernel_features) return (result == CUDA_SUCCESS); } -void CUDADevice::reserve_local_memory(const uint /* kernel_features */) +void CUDADevice::reserve_local_memory(const uint kernel_features) { /* Together with CU_CTX_LMEM_RESIZE_TO_MAX, this reserves local memory * needed for kernel launches, so that we can reliably figure out when @@ -468,7 +468,9 @@ void CUDADevice::reserve_local_memory(const uint /* kernel_features */) { /* Use the biggest kernel for estimation. */ - const DeviceKernel test_kernel = DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE; + const DeviceKernel test_kernel = (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) ? + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE : + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE; /* Launch kernel, using just 1 block appears sufficient to reserve memory for all * multiprocessors. It would be good to do this in parallel for the multi GPU case diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp index 964783a08bf..583ab8ae208 100644 --- a/intern/cycles/device/hip/device_impl.cpp +++ b/intern/cycles/device/hip/device_impl.cpp @@ -430,7 +430,7 @@ bool HIPDevice::load_kernels(const uint kernel_features) return (result == hipSuccess); } -void HIPDevice::reserve_local_memory(const uint) +void HIPDevice::reserve_local_memory(const uint kernel_features) { /* Together with hipDeviceLmemResizeToMax, this reserves local memory * needed for kernel launches, so that we can reliably figure out when @@ -444,7 +444,9 @@ void HIPDevice::reserve_local_memory(const uint) { /* Use the biggest kernel for estimation. */ - const DeviceKernel test_kernel = DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE; + const DeviceKernel test_kernel = (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) ? + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE : + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE; /* Launch kernel, using just 1 block appears sufficient to reserve memory for all * multiprocessors. It would be good to do this in parallel for the multi GPU case From cccfa597ba69944817e0913944cf3c3d0a6e1165 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 17 Oct 2021 18:08:00 +0200 Subject: [PATCH 0991/1500] Cycles: make ambient occlusion pass take into account transparency again Taking advantage of the new decoupled main and shadow paths. For CPU we just store two nested structs in the integrator state, one for direct light shadows and one for AO. For the GPU we restrict the number of shade surface states to be executed based on available space in the shadow paths queue. This also helps improve performance in benchmark scenes with an AO pass, since it is no longer needed to use the shader raytracing kernel there, which has worse performance. Differential Revision: https://developer.blender.org/D12900 --- .../cycles/integrator/path_trace_work_gpu.cpp | 58 +++++++++---- .../cycles/integrator/path_trace_work_gpu.h | 8 +- intern/cycles/kernel/device/gpu/kernel.h | 22 +++-- .../kernel/device/gpu/parallel_prefix_sum.h | 8 +- .../kernel/device/gpu/parallel_sorted_index.h | 12 ++- .../integrator/integrator_init_from_bake.h | 2 +- .../integrator/integrator_intersect_closest.h | 3 +- .../kernel/integrator/integrator_megakernel.h | 40 ++++++--- .../integrator/integrator_shade_background.h | 2 +- .../integrator/integrator_shade_surface.h | 87 +++++++++++-------- .../integrator/integrator_shade_volume.h | 3 +- .../kernel/integrator/integrator_state.h | 1 + .../kernel/integrator/integrator_state_flow.h | 6 +- .../kernel/integrator/integrator_subsurface.h | 2 +- intern/cycles/kernel/kernel_accumulate.h | 7 ++ intern/cycles/kernel/kernel_path_state.h | 1 + intern/cycles/kernel/kernel_types.h | 9 +- intern/cycles/render/film.cpp | 4 - 18 files changed, 178 insertions(+), 97 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 18aa5dda70d..a4788c437a1 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -78,6 +78,8 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device, integrator_shader_sort_counter_(device, "integrator_shader_sort_counter", MEM_READ_WRITE), integrator_shader_raytrace_sort_counter_( device, "integrator_shader_raytrace_sort_counter", MEM_READ_WRITE), + integrator_shader_sort_prefix_sum_( + device, "integrator_shader_sort_prefix_sum", MEM_READ_WRITE), integrator_next_shadow_path_index_( device, "integrator_next_shadow_path_index", MEM_READ_WRITE), integrator_next_shadow_catcher_path_index_( @@ -200,6 +202,9 @@ void PathTraceWorkGPU::alloc_integrator_sorting() integrator_shader_raytrace_sort_counter_.alloc(max_shaders); integrator_shader_raytrace_sort_counter_.zero_to_device(); + integrator_shader_sort_prefix_sum_.alloc(max_shaders); + integrator_shader_sort_prefix_sum_.zero_to_device(); + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE] = (int *)integrator_shader_sort_counter_.device_pointer; integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE] = @@ -374,9 +379,12 @@ bool PathTraceWorkGPU::enqueue_path_iteration() /* For kernels that add shadow paths, check if there is enough space available. * If not, schedule shadow kernels first to clear out the shadow paths. */ + int num_paths_limit = INT_MAX; + if (kernel_creates_shadow_paths(kernel)) { - if (max_num_paths_ - integrator_next_shadow_path_index_.data()[0] < - queue_counter->num_queued[kernel]) { + const int available_shadow_paths = max_num_paths_ - + integrator_next_shadow_path_index_.data()[0]; + if (available_shadow_paths < queue_counter->num_queued[kernel]) { if (queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW]) { enqueue_path_iteration(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); return true; @@ -386,10 +394,14 @@ bool PathTraceWorkGPU::enqueue_path_iteration() return true; } } + else if (kernel_creates_ao_paths(kernel)) { + /* AO kernel creates two shadow paths, so limit number of states to schedule. */ + num_paths_limit = available_shadow_paths / 2; + } } /* Schedule kernel with maximum number of queued items. */ - enqueue_path_iteration(kernel); + enqueue_path_iteration(kernel, num_paths_limit); /* Update next shadow path index for kernels that can add shadow paths. */ if (kernel_creates_shadow_paths(kernel)) { @@ -399,7 +411,7 @@ bool PathTraceWorkGPU::enqueue_path_iteration() return true; } -void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel) +void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel, const int num_paths_limit) { void *d_path_index = (void *)NULL; @@ -414,7 +426,8 @@ void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel) work_size = num_queued; d_path_index = (void *)queued_paths_.device_pointer; - compute_sorted_queued_paths(DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY, kernel); + compute_sorted_queued_paths( + DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY, kernel, num_paths_limit); } else if (num_queued < work_size) { work_size = num_queued; @@ -430,6 +443,8 @@ void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel) } } + work_size = min(work_size, num_paths_limit); + DCHECK_LE(work_size, max_num_paths_); switch (kernel) { @@ -464,17 +479,20 @@ void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel) } } -void PathTraceWorkGPU::compute_sorted_queued_paths(DeviceKernel kernel, DeviceKernel queued_kernel) +void PathTraceWorkGPU::compute_sorted_queued_paths(DeviceKernel kernel, + DeviceKernel queued_kernel, + const int num_paths_limit) { int d_queued_kernel = queued_kernel; void *d_counter = integrator_state_gpu_.sort_key_counter[d_queued_kernel]; - assert(d_counter != nullptr); + void *d_prefix_sum = (void *)integrator_shader_sort_prefix_sum_.device_pointer; + assert(d_counter != nullptr && d_prefix_sum != nullptr); /* Compute prefix sum of number of active paths with each shader. */ { const int work_size = 1; int max_shaders = device_scene_->data.max_shaders; - void *args[] = {&d_counter, &max_shaders}; + void *args[] = {&d_counter, &d_prefix_sum, &max_shaders}; queue_->enqueue(DEVICE_KERNEL_PREFIX_SUM, work_size, args); } @@ -483,29 +501,24 @@ void PathTraceWorkGPU::compute_sorted_queued_paths(DeviceKernel kernel, DeviceKe /* Launch kernel to fill the active paths arrays. */ { /* TODO: this could be smaller for terminated paths based on amount of work we want - * to schedule. */ + * to schedule, and also based on num_paths_limit. + * + * Also, when the number paths is limited it may be better to prefer paths from the + * end of the array since compaction would need to do less work. */ const int work_size = kernel_max_active_path_index(queued_kernel); void *d_queued_paths = (void *)queued_paths_.device_pointer; void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; void *args[] = {const_cast(&work_size), + const_cast(&num_paths_limit), &d_queued_paths, &d_num_queued_paths, &d_counter, + &d_prefix_sum, &d_queued_kernel}; queue_->enqueue(kernel, work_size, args); } - - if (queued_kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE) { - queue_->zero_to_device(integrator_shader_sort_counter_); - } - else if (queued_kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) { - queue_->zero_to_device(integrator_shader_raytrace_sort_counter_); - } - else { - assert(0); - } } void PathTraceWorkGPU::compute_queued_paths(DeviceKernel kernel, DeviceKernel queued_kernel) @@ -1026,6 +1039,13 @@ bool PathTraceWorkGPU::kernel_creates_shadow_paths(DeviceKernel kernel) kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); } +bool PathTraceWorkGPU::kernel_creates_ao_paths(DeviceKernel kernel) +{ + return (device_scene_->data.film.pass_ao != PASS_UNUSED) && + (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE); +} + bool PathTraceWorkGPU::kernel_is_shadow_path(DeviceKernel kernel) { return (kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h index dd2c1c197ae..e1f6c09d334 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.h +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -79,10 +79,12 @@ class PathTraceWorkGPU : public PathTraceWork { const int num_predicted_splits); bool enqueue_path_iteration(); - void enqueue_path_iteration(DeviceKernel kernel); + void enqueue_path_iteration(DeviceKernel kernel, const int num_paths_limit = INT_MAX); void compute_queued_paths(DeviceKernel kernel, DeviceKernel queued_kernel); - void compute_sorted_queued_paths(DeviceKernel kernel, DeviceKernel queued_kernel); + void compute_sorted_queued_paths(DeviceKernel kernel, + DeviceKernel queued_kernel, + const int num_paths_limit); void compact_states(const int num_active_paths); @@ -116,6 +118,7 @@ class PathTraceWorkGPU : public PathTraceWork { /* Kernel properties. */ bool kernel_uses_sorting(DeviceKernel kernel); bool kernel_creates_shadow_paths(DeviceKernel kernel); + bool kernel_creates_ao_paths(DeviceKernel kernel); bool kernel_is_shadow_path(DeviceKernel kernel); int kernel_max_active_path_index(DeviceKernel kernel); @@ -136,6 +139,7 @@ class PathTraceWorkGPU : public PathTraceWork { /* Shader sorting. */ device_vector integrator_shader_sort_counter_; device_vector integrator_shader_raytrace_sort_counter_; + device_vector integrator_shader_sort_prefix_sum_; /* Path split. */ device_vector integrator_next_shadow_path_index_; device_vector integrator_next_shadow_catcher_path_index_; diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index b6df74e835a..fcb398f7e6d 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -282,11 +282,22 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_B } extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE) - kernel_gpu_integrator_sorted_paths_array( - int num_states, int *indices, int *num_indices, int *key_prefix_sum, int kernel) + kernel_gpu_integrator_sorted_paths_array(int num_states, + int num_states_limit, + int *indices, + int *num_indices, + int *key_counter, + int *key_prefix_sum, + int kernel) { gpu_parallel_sorted_index_array( - num_states, indices, num_indices, key_prefix_sum, [kernel](const int state) { + num_states, + num_states_limit, + indices, + num_indices, + key_counter, + key_prefix_sum, + [kernel](const int state) { return (INTEGRATOR_STATE(state, path, queued_kernel) == kernel) ? INTEGRATOR_STATE(state, path, shader_sort_key) : GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY; @@ -322,9 +333,10 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_B } extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE) - kernel_gpu_prefix_sum(int *values, int num_values) + kernel_gpu_prefix_sum(int *counter, int *prefix_sum, int num_values) { - gpu_parallel_prefix_sum(values, num_values); + gpu_parallel_prefix_sum( + counter, prefix_sum, num_values); } /* -------------------------------------------------------------------- diff --git a/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h index a1349e82efb..aabe6e2e27a 100644 --- a/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h +++ b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h @@ -33,7 +33,8 @@ CCL_NAMESPACE_BEGIN # define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 512 #endif -template __device__ void gpu_parallel_prefix_sum(int *values, const int num_values) +template +__device__ void gpu_parallel_prefix_sum(int *counter, int *prefix_sum, const int num_values) { if (!(ccl_gpu_block_idx_x == 0 && ccl_gpu_thread_idx_x == 0)) { return; @@ -41,8 +42,9 @@ template __device__ void gpu_parallel_prefix_sum(int *values, co int offset = 0; for (int i = 0; i < num_values; i++) { - const int new_offset = offset + values[i]; - values[i] = offset; + const int new_offset = offset + counter[i]; + prefix_sum[i] = offset; + counter[i] = 0; offset = new_offset; } } diff --git a/intern/cycles/kernel/device/gpu/parallel_sorted_index.h b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h index 9bca1fad22f..7570c5a6bbd 100644 --- a/intern/cycles/kernel/device/gpu/parallel_sorted_index.h +++ b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h @@ -35,8 +35,10 @@ CCL_NAMESPACE_BEGIN template __device__ void gpu_parallel_sorted_index_array(const uint num_states, + const int num_states_limit, int *indices, int *num_indices, + int *key_counter, int *key_prefix_sum, GetKeyOp get_key_op) { @@ -46,7 +48,15 @@ __device__ void gpu_parallel_sorted_index_array(const uint num_states, if (key != GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY) { const uint index = atomic_fetch_and_add_uint32(&key_prefix_sum[key], 1); - indices[index] = state_index; + if (index < num_states_limit) { + /* Assign state index. */ + indices[index] = state_index; + } + else { + /* Can't process this state now, increase the counter again so that + * it will be handled in another iteration. */ + atomic_fetch_and_add_uint32(&key_counter[key], 1); + } } } diff --git a/intern/cycles/kernel/integrator/integrator_init_from_bake.h b/intern/cycles/kernel/integrator/integrator_init_from_bake.h index 9bc115150ff..de916be24e7 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_bake.h +++ b/intern/cycles/kernel/integrator/integrator_init_from_bake.h @@ -185,7 +185,7 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, /* Setup next kernel to execute. */ const int shader_index = shader & SHADER_MASK; const int shader_flags = kernel_tex_fetch(__shaders, shader_index).flags; - if ((shader_flags & SD_HAS_RAYTRACE) || (kernel_data.film.pass_ao != PASS_UNUSED)) { + if (shader_flags & SD_HAS_RAYTRACE) { INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader_index); } else { diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/integrator_intersect_closest.h index ef8dcb50115..c1315d48694 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_closest.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_closest.h @@ -111,8 +111,7 @@ ccl_device_forceinline void integrator_intersect_shader_next_kernel( * Note that the splitting leaves kernel and sorting counters as-is, so use INIT semantic for * the matte path. */ - const bool use_raytrace_kernel = ((shader_flags & SD_HAS_RAYTRACE) || - (kernel_data.film.pass_ao != PASS_UNUSED)); + const bool use_raytrace_kernel = (shader_flags & SD_HAS_RAYTRACE); if (use_raytrace_kernel) { INTEGRATOR_PATH_NEXT_SORTED( diff --git a/intern/cycles/kernel/integrator/integrator_megakernel.h b/intern/cycles/kernel/integrator/integrator_megakernel.h index 6e3220aa3b7..21a483a792b 100644 --- a/intern/cycles/kernel/integrator/integrator_megakernel.h +++ b/intern/cycles/kernel/integrator/integrator_megakernel.h @@ -34,16 +34,12 @@ ccl_device void integrator_megakernel(KernelGlobals kg, ccl_global float *ccl_restrict render_buffer) { /* Each kernel indicates the next kernel to execute, so here we simply - * have to check what that kernel is and execute it. - * - * TODO: investigate if we can use device side enqueue for GPUs to avoid - * having to compile this big kernel. */ + * have to check what that kernel is and execute it. */ while (true) { + /* Handle any shadow paths before we potentially create more shadow paths. */ const uint32_t shadow_queued_kernel = INTEGRATOR_STATE( &state->shadow, shadow_path, queued_kernel); - if (shadow_queued_kernel) { - /* First handle any shadow paths before we potentially create more shadow paths. */ switch (shadow_queued_kernel) { case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: integrator_intersect_shadow(kg, &state->shadow); @@ -55,10 +51,30 @@ ccl_device void integrator_megakernel(KernelGlobals kg, kernel_assert(0); break; } + continue; } - else if (INTEGRATOR_STATE(state, path, queued_kernel)) { - /* Then handle regular path kernels. */ - switch (INTEGRATOR_STATE(state, path, queued_kernel)) { + + /* Handle any AO paths before we potentially create more AO paths. */ + const uint32_t ao_queued_kernel = INTEGRATOR_STATE(&state->ao, shadow_path, queued_kernel); + if (ao_queued_kernel) { + switch (ao_queued_kernel) { + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: + integrator_intersect_shadow(kg, &state->ao); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: + integrator_shade_shadow(kg, &state->ao, render_buffer); + break; + default: + kernel_assert(0); + break; + } + continue; + } + + /* Then handle regular path kernels. */ + const uint32_t queued_kernel = INTEGRATOR_STATE(state, path, queued_kernel); + if (queued_kernel) { + switch (queued_kernel) { case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: integrator_intersect_closest(kg, state); break; @@ -87,10 +103,10 @@ ccl_device void integrator_megakernel(KernelGlobals kg, kernel_assert(0); break; } + continue; } - else { - break; - } + + break; } } diff --git a/intern/cycles/kernel/integrator/integrator_shade_background.h b/intern/cycles/kernel/integrator/integrator_shade_background.h index d98e53e6bbf..287c54d7243 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_background.h +++ b/intern/cycles/kernel/integrator/integrator_shade_background.h @@ -198,7 +198,7 @@ ccl_device void integrator_shade_background(KernelGlobals kg, const int shader = intersection_get_shader_from_isect_prim(kg, isect_prim, isect_type); const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; - if ((shader_flags & SD_HAS_RAYTRACE) || (kernel_data.film.pass_ao != PASS_UNUSED)) { + if (shader_flags & SD_HAS_RAYTRACE) { INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index 0108ba1373c..2fb0dcc2097 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -168,7 +168,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, const bool is_light = light_sample_is_light(&ls); /* Branch off shadow kernel. */ - INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + INTEGRATOR_SHADOW_PATH_INIT( + shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, shadow); /* Copy volume stack and enter/exit volume. */ integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); @@ -324,26 +325,14 @@ ccl_device_forceinline bool integrate_surface_volume_only_bounce(IntegratorState } #endif -#if defined(__AO__) && defined(__SHADER_RAYTRACE__) +#if defined(__AO__) ccl_device_forceinline void integrate_surface_ao_pass( KernelGlobals kg, - ConstIntegratorState state, + IntegratorState state, ccl_private const ShaderData *ccl_restrict sd, ccl_private const RNGState *ccl_restrict rng_state, ccl_global float *ccl_restrict render_buffer) { -# ifdef __KERNEL_OPTIX__ - optixDirectCall(2, kg, state, sd, rng_state, render_buffer); -} - -extern "C" __device__ void __direct_callable__ao_pass( - KernelGlobals kg, - ConstIntegratorState state, - ccl_private const ShaderData *ccl_restrict sd, - ccl_private const RNGState *ccl_restrict rng_state, - ccl_global float *ccl_restrict render_buffer) -{ -# endif /* __KERNEL_OPTIX__ */ float bsdf_u, bsdf_v; path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); @@ -352,24 +341,48 @@ extern "C" __device__ void __direct_callable__ao_pass( float ao_pdf; sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); - if (dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f) { - Ray ray ccl_optional_struct_init; - ray.P = ray_offset(sd->P, sd->Ng); - ray.D = ao_D; - ray.t = kernel_data.integrator.ao_bounces_distance; - ray.time = sd->time; - ray.dP = differential_zero_compact(); - ray.dD = differential_zero_compact(); - - Intersection isect ccl_optional_struct_init; - if (!scene_intersect(kg, &ray, PATH_RAY_SHADOW_OPAQUE, &isect)) { - ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_write_pass_float3(buffer + kernel_data.film.pass_ao, throughput); - } + if (!(dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f)) { + return; } + + Ray ray ccl_optional_struct_init; + ray.P = ray_offset(sd->P, sd->Ng); + ray.D = ao_D; + ray.t = kernel_data.integrator.ao_bounces_distance; + ray.time = sd->time; + ray.dP = differential_zero_compact(); + ray.dD = differential_zero_compact(); + + /* Branch off shadow kernel. */ + INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, ao); + + /* Copy volume stack and enter/exit volume. */ + integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); + + /* Write shadow ray and associated state to global memory. */ + integrator_state_write_shadow_ray(kg, shadow_state, &ray); + + /* Copy state from main path to shadow path. */ + const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); + const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); + uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag) | PATH_RAY_SHADOW_FOR_AO; + const float3 throughput = INTEGRATOR_STATE(state, path, throughput) * shader_bsdf_alpha(kg, sd); + + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( + state, path, render_pixel_index); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = + INTEGRATOR_STATE(state, path, rng_offset) - + PRNG_BOUNCE_NUM * INTEGRATOR_STATE(state, path, transparent_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE( + state, path, rng_hash); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE( + state, path, sample); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, throughput) = throughput; } -#endif /* defined(__AO__) && defined(__SHADER_RAYTRACE__) */ +#endif /* defined(__AO__) */ template ccl_device bool integrate_surface(KernelGlobals kg, @@ -474,14 +487,12 @@ ccl_device bool integrate_surface(KernelGlobals kg, PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT); integrate_surface_direct_light(kg, state, &sd, &rng_state); -#if defined(__AO__) && defined(__SHADER_RAYTRACE__) +#if defined(__AO__) /* Ambient occlusion pass. */ - if (node_feature_mask & KERNEL_FEATURE_NODE_RAYTRACE) { - if ((kernel_data.film.pass_ao != PASS_UNUSED) && - (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_CAMERA)) { - PROFILING_EVENT(PROFILING_SHADE_SURFACE_AO); - integrate_surface_ao_pass(kg, state, &sd, &rng_state, render_buffer); - } + if ((kernel_data.film.pass_ao != PASS_UNUSED) && + (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_CAMERA)) { + PROFILING_EVENT(PROFILING_SHADE_SURFACE_AO); + integrate_surface_ao_pass(kg, state, &sd, &rng_state, render_buffer); } #endif diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index 13a5e7bda05..1dd701237a8 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -776,7 +776,8 @@ ccl_device_forceinline void integrate_volume_direct_light( const bool is_light = light_sample_is_light(ls); /* Branch off shadow kernel. */ - INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + INTEGRATOR_SHADOW_PATH_INIT( + shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, shadow); /* Write shadow ray and associated state to global memory. */ integrator_state_write_shadow_ray(kg, shadow_state, &ray); diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h index 4f21ab35d1f..84efcb00349 100644 --- a/intern/cycles/kernel/integrator/integrator_state.h +++ b/intern/cycles/kernel/integrator/integrator_state.h @@ -92,6 +92,7 @@ typedef struct IntegratorStateCPU { #undef KERNEL_STRUCT_VOLUME_STACK_SIZE IntegratorShadowStateCPU shadow; + IntegratorShadowStateCPU ao; } IntegratorStateCPU; /* Path Queue diff --git a/intern/cycles/kernel/integrator/integrator_state_flow.h b/intern/cycles/kernel/integrator/integrator_state_flow.h index df8fb5e0e46..1569bf68e24 100644 --- a/intern/cycles/kernel/integrator/integrator_state_flow.h +++ b/intern/cycles/kernel/integrator/integrator_state_flow.h @@ -63,7 +63,7 @@ CCL_NAMESPACE_BEGIN &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; -# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel) \ +# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel, shadow_type) \ IntegratorShadowState shadow_state = atomic_fetch_and_add_uint32( \ &kernel_integrator_state.next_shadow_path_index[0], 1); \ atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ @@ -129,8 +129,8 @@ CCL_NAMESPACE_BEGIN (void)current_kernel; \ } -# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel) \ - IntegratorShadowState shadow_state = &state->shadow; \ +# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel, shadow_type) \ + IntegratorShadowState shadow_state = &state->shadow_type; \ INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; # define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ { \ diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/integrator_subsurface.h index e9517a82453..e3bf9db80f7 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface.h @@ -182,7 +182,7 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat const int shader = intersection_get_shader(kg, &ss_isect.hits[0]); const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; - if ((shader_flags & SD_HAS_RAYTRACE) || (kernel_data.film.pass_ao != PASS_UNUSED)) { + if (shader_flags & SD_HAS_RAYTRACE) { INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index 848aaa18aae..54492bef974 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -408,6 +408,13 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag); const int sample = INTEGRATOR_STATE(state, shadow_path, sample); + /* Ambient occlusion. */ + if (path_flag & PATH_RAY_SHADOW_FOR_AO) { + kernel_write_pass_float3(buffer + kernel_data.film.pass_ao, contribution); + return; + } + + /* Direct light shadow. */ kernel_accum_combined_pass(kg, path_flag, sample, contribution, buffer); #ifdef __PASSES__ diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h index fa8de14916e..428eb3498f7 100644 --- a/intern/cycles/kernel/kernel_path_state.h +++ b/intern/cycles/kernel/kernel_path_state.h @@ -28,6 +28,7 @@ ccl_device_inline void path_state_init_queues(IntegratorState state) INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; #ifdef __KERNEL_CPU__ INTEGRATOR_STATE_WRITE(&state->shadow, shadow_path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(&state->ao, shadow_path, queued_kernel) = 0; #endif } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index fa8453b99cb..4bdd8185ca6 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -285,21 +285,22 @@ enum PathRayFlag { PATH_RAY_VOLUME_PASS = (1U << 26U), PATH_RAY_ANY_PASS = (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS | PATH_RAY_VOLUME_PASS), - /* Shadow ray is for a light or surface. */ + /* Shadow ray is for a light or surface, or AO. */ PATH_RAY_SHADOW_FOR_LIGHT = (1U << 27U), + PATH_RAY_SHADOW_FOR_AO = (1U << 28U), /* A shadow catcher object was hit and the path was split into two. */ - PATH_RAY_SHADOW_CATCHER_HIT = (1U << 28U), + PATH_RAY_SHADOW_CATCHER_HIT = (1U << 29U), /* A shadow catcher object was hit and this path traces only shadow catchers, writing them into * their dedicated pass for later division. * * NOTE: Is not covered with `PATH_RAY_ANY_PASS` because shadow catcher does special handling * which is separate from the light passes. */ - PATH_RAY_SHADOW_CATCHER_PASS = (1U << 29U), + PATH_RAY_SHADOW_CATCHER_PASS = (1U << 30U), /* Path is evaluating background for an approximate shadow catcher with non-transparent film. */ - PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1U << 30U), + PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1U << 31U), }; /* Configure ray visibility bits for rays and objects respectively, diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 48f87ea3bf7..381f794545a 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -677,10 +677,6 @@ uint Film::get_kernel_features(const Scene *scene) const kernel_features |= KERNEL_FEATURE_SHADOW_PASS; } } - - if (pass_type == PASS_AO) { - kernel_features |= KERNEL_FEATURE_NODE_RAYTRACE; - } } return kernel_features; From 52c5300214e58776623b308de4e5ac33fc8f17d5 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 20 Oct 2021 14:45:17 +0200 Subject: [PATCH 0992/1500] Cleanup: some renaming to better distinguish main and shadow paths --- .../cycles/integrator/path_trace_work_gpu.cpp | 55 +++++++++---------- .../cycles/integrator/path_trace_work_gpu.h | 10 ++-- .../kernel/integrator/integrator_state.h | 2 +- .../kernel/integrator/integrator_state_util.h | 2 +- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index a4788c437a1..f898f3fce81 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -80,24 +80,23 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device, device, "integrator_shader_raytrace_sort_counter", MEM_READ_WRITE), integrator_shader_sort_prefix_sum_( device, "integrator_shader_sort_prefix_sum", MEM_READ_WRITE), + integrator_next_main_path_index_(device, "integrator_next_main_path_index", MEM_READ_WRITE), integrator_next_shadow_path_index_( device, "integrator_next_shadow_path_index", MEM_READ_WRITE), - integrator_next_shadow_catcher_path_index_( - device, "integrator_next_shadow_catcher_path_index", MEM_READ_WRITE), queued_paths_(device, "queued_paths", MEM_READ_WRITE), num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE), work_tiles_(device, "work_tiles", MEM_READ_WRITE), display_rgba_half_(device, "display buffer half", MEM_READ_WRITE), max_num_paths_(queue_->num_concurrent_states(estimate_single_state_size())), - min_num_active_paths_(queue_->num_concurrent_busy_states()), - max_active_path_index_(0) + min_num_active_main_paths_(queue_->num_concurrent_busy_states()), + max_active_main_path_index_(0) { memset(&integrator_state_gpu_, 0, sizeof(integrator_state_gpu_)); /* Limit number of active paths to the half of the overall state. This is due to the logic in the * path compaction which relies on the fact that regeneration does not happen sooner than half of * the states are available again. */ - min_num_active_paths_ = min(min_num_active_paths_, max_num_paths_ / 2); + min_num_active_main_paths_ = min(min_num_active_main_paths_, max_num_paths_ / 2); } void PathTraceWorkGPU::alloc_integrator_soa() @@ -222,13 +221,13 @@ void PathTraceWorkGPU::alloc_integrator_path_split() (int *)integrator_next_shadow_path_index_.device_pointer; } - if (integrator_next_shadow_catcher_path_index_.size() == 0) { - integrator_next_shadow_catcher_path_index_.alloc(1); + if (integrator_next_main_path_index_.size() == 0) { + integrator_next_main_path_index_.alloc(1); integrator_next_shadow_path_index_.data()[0] = 0; - integrator_next_shadow_catcher_path_index_.zero_to_device(); + integrator_next_main_path_index_.zero_to_device(); - integrator_state_gpu_.next_shadow_catcher_path_index = - (int *)integrator_next_shadow_catcher_path_index_.device_pointer; + integrator_state_gpu_.next_main_path_index = + (int *)integrator_next_main_path_index_.device_pointer; } } @@ -303,7 +302,7 @@ void PathTraceWorkGPU::render_samples(RenderStatistics &statistics, break; } - num_busy_accum += get_num_active_paths(); + num_busy_accum += num_active_main_paths_paths(); ++num_iterations; } @@ -416,7 +415,7 @@ void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel, const int num void *d_path_index = (void *)NULL; /* Create array of path indices for which this kernel is queued to be executed. */ - int work_size = kernel_max_active_path_index(kernel); + int work_size = kernel_max_active_main_path_index(kernel); IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); int num_queued = queue_counter->num_queued[kernel]; @@ -505,7 +504,7 @@ void PathTraceWorkGPU::compute_sorted_queued_paths(DeviceKernel kernel, * * Also, when the number paths is limited it may be better to prefer paths from the * end of the array since compaction would need to do less work. */ - const int work_size = kernel_max_active_path_index(queued_kernel); + const int work_size = kernel_max_active_main_path_index(queued_kernel); void *d_queued_paths = (void *)queued_paths_.device_pointer; void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; @@ -526,7 +525,7 @@ void PathTraceWorkGPU::compute_queued_paths(DeviceKernel kernel, DeviceKernel qu int d_queued_kernel = queued_kernel; /* Launch kernel to fill the active paths arrays. */ - const int work_size = kernel_max_active_path_index(queued_kernel); + const int work_size = kernel_max_active_main_path_index(queued_kernel); void *d_queued_paths = (void *)queued_paths_.device_pointer; void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; void *args[] = { @@ -539,12 +538,12 @@ void PathTraceWorkGPU::compute_queued_paths(DeviceKernel kernel, DeviceKernel qu void PathTraceWorkGPU::compact_states(const int num_active_paths) { if (num_active_paths == 0) { - max_active_path_index_ = 0; + max_active_main_path_index_ = 0; } /* Compact fragmented path states into the start of the array, moving any paths * with index higher than the number of active paths into the gaps. */ - if (max_active_path_index_ == num_active_paths) { + if (max_active_main_path_index_ == num_active_paths) { return; } @@ -564,7 +563,7 @@ void PathTraceWorkGPU::compact_states(const int num_active_paths) /* Create array of paths that we need to compact, where the path index is bigger * than the number of active paths. */ { - int work_size = max_active_path_index_; + int work_size = max_active_main_path_index_; void *args[] = { &work_size, &d_compact_paths, &d_num_queued_paths, const_cast(&num_active_paths)}; queue_->zero_to_device(num_queued_paths_); @@ -589,7 +588,7 @@ void PathTraceWorkGPU::compact_states(const int num_active_paths) queue_->synchronize(); /* Adjust max active path index now we know which part of the array is actually used. */ - max_active_path_index_ = num_active_paths; + max_active_main_path_index_ = num_active_paths; } bool PathTraceWorkGPU::enqueue_work_tiles(bool &finished) @@ -603,7 +602,7 @@ bool PathTraceWorkGPU::enqueue_work_tiles(bool &finished) return false; } - int num_active_paths = get_num_active_paths(); + int num_active_paths = num_active_main_paths_paths(); /* Don't schedule more work if canceling. */ if (is_cancel_requested()) { @@ -643,7 +642,7 @@ bool PathTraceWorkGPU::enqueue_work_tiles(bool &finished) /* Schedule when we're out of paths or there are too few paths to keep the * device occupied. */ int num_paths = num_active_paths; - if (num_paths == 0 || num_paths < min_num_active_paths_) { + if (num_paths == 0 || num_paths < min_num_active_main_paths_) { /* Get work tiles until the maximum number of path is reached. */ while (num_paths < max_num_camera_paths) { KernelWorkTile work_tile; @@ -673,8 +672,8 @@ bool PathTraceWorkGPU::enqueue_work_tiles(bool &finished) compact_states(num_active_paths); if (has_shadow_catcher()) { - integrator_next_shadow_catcher_path_index_.data()[0] = num_paths; - queue_->copy_to_device(integrator_next_shadow_catcher_path_index_); + integrator_next_main_path_index_.data()[0] = num_paths; + queue_->copy_to_device(integrator_next_main_path_index_); } enqueue_work_tiles((device_scene_->data.bake.use) ? DEVICE_KERNEL_INTEGRATOR_INIT_FROM_BAKE : @@ -727,10 +726,10 @@ void PathTraceWorkGPU::enqueue_work_tiles(DeviceKernel kernel, queue_->enqueue(kernel, max_tile_work_size * num_work_tiles, args); - max_active_path_index_ = path_index_offset + num_predicted_splits; + max_active_main_path_index_ = path_index_offset + num_predicted_splits; } -int PathTraceWorkGPU::get_num_active_paths() +int PathTraceWorkGPU::num_active_main_paths_paths() { /* TODO: this is wrong, does not account for duplicates with shadow! */ IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); @@ -1005,7 +1004,7 @@ bool PathTraceWorkGPU::has_shadow_catcher() const int PathTraceWorkGPU::shadow_catcher_count_possible_splits() { - if (max_active_path_index_ == 0) { + if (max_active_main_path_index_ == 0) { return 0; } @@ -1015,7 +1014,7 @@ int PathTraceWorkGPU::shadow_catcher_count_possible_splits() queue_->zero_to_device(num_queued_paths_); - const int work_size = max_active_path_index_; + const int work_size = max_active_main_path_index_; void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; void *args[] = {const_cast(&work_size), &d_num_queued_paths}; @@ -1052,10 +1051,10 @@ bool PathTraceWorkGPU::kernel_is_shadow_path(DeviceKernel kernel) kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); } -int PathTraceWorkGPU::kernel_max_active_path_index(DeviceKernel kernel) +int PathTraceWorkGPU::kernel_max_active_main_path_index(DeviceKernel kernel) { return (kernel_is_shadow_path(kernel)) ? integrator_next_shadow_path_index_.data()[0] : - max_active_path_index_; + max_active_main_path_index_; } CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h index e1f6c09d334..e16c491695b 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.h +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -88,7 +88,7 @@ class PathTraceWorkGPU : public PathTraceWork { void compact_states(const int num_active_paths); - int get_num_active_paths(); + int num_active_main_paths_paths(); /* Check whether graphics interop can be used for the PathTraceDisplay update. */ bool should_use_graphics_interop(); @@ -120,7 +120,7 @@ class PathTraceWorkGPU : public PathTraceWork { bool kernel_creates_shadow_paths(DeviceKernel kernel); bool kernel_creates_ao_paths(DeviceKernel kernel); bool kernel_is_shadow_path(DeviceKernel kernel); - int kernel_max_active_path_index(DeviceKernel kernel); + int kernel_max_active_main_path_index(DeviceKernel kernel); /* Integrator queue. */ unique_ptr queue_; @@ -141,8 +141,8 @@ class PathTraceWorkGPU : public PathTraceWork { device_vector integrator_shader_raytrace_sort_counter_; device_vector integrator_shader_sort_prefix_sum_; /* Path split. */ + device_vector integrator_next_main_path_index_; device_vector integrator_next_shadow_path_index_; - device_vector integrator_next_shadow_catcher_path_index_; /* Temporary buffer to get an array of queued path for a particular kernel. */ device_vector queued_paths_; @@ -166,12 +166,12 @@ class PathTraceWorkGPU : public PathTraceWork { /* Minimum number of paths which keeps the device bust. If the actual number of paths falls below * this value more work will be scheduled. */ - int min_num_active_paths_; + int min_num_active_main_paths_; /* Maximum path index, effective number of paths used may be smaller than * the size of the integrator_state_ buffer so can avoid iterating over the * full buffer. */ - int max_active_path_index_; + int max_active_main_path_index_; }; CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h index 84efcb00349..09b399ff1b8 100644 --- a/intern/cycles/kernel/integrator/integrator_state.h +++ b/intern/cycles/kernel/integrator/integrator_state.h @@ -139,7 +139,7 @@ typedef struct IntegratorStateGPU { ccl_global int *next_shadow_path_index; /* Index of main path which will be used by a next shadow catcher split. */ - ccl_global int *next_shadow_catcher_path_index; + ccl_global int *next_main_path_index; } IntegratorStateGPU; /* Abstraction diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index 5bcb9cc2d67..6da41cddcf8 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -274,7 +274,7 @@ ccl_device_inline void integrator_state_shadow_catcher_split(KernelGlobals kg, { #if defined(__KERNEL_GPU__) ConstIntegratorState to_state = atomic_fetch_and_add_uint32( - &kernel_integrator_state.next_shadow_catcher_path_index[0], 1); + &kernel_integrator_state.next_main_path_index[0], 1); integrator_state_copy_only(kg, to_state, state); #else From 0c52eed863522b4dbac2704e8c88b5c009e886e7 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 20 Oct 2021 14:47:04 +0200 Subject: [PATCH 0993/1500] Cycles: more accurately count main paths for adding work tiles Easy now thanks to the main and shadow path decoupling. Doesn't help in an benchmark scene except Spring, where it reduces render time by maybe 2-3%. Ref T87836 --- intern/cycles/integrator/path_trace_work_gpu.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index f898f3fce81..2c71b1cf876 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -731,7 +731,6 @@ void PathTraceWorkGPU::enqueue_work_tiles(DeviceKernel kernel, int PathTraceWorkGPU::num_active_main_paths_paths() { - /* TODO: this is wrong, does not account for duplicates with shadow! */ IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); int num_paths = 0; @@ -739,7 +738,10 @@ int PathTraceWorkGPU::num_active_main_paths_paths() DCHECK_GE(queue_counter->num_queued[i], 0) << "Invalid number of queued states for kernel " << device_kernel_as_string(static_cast(i)); - num_paths += queue_counter->num_queued[i]; + + if (!kernel_is_shadow_path((DeviceKernel)i)) { + num_paths += queue_counter->num_queued[i]; + } } return num_paths; From 1a96045eec96e0c6f52ded6fd40040c4b88524d9 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 20 Oct 2021 16:27:03 +0200 Subject: [PATCH 0994/1500] Fix T92367: missing Subsurface Anisotropy and IOR sockets with factory startup --- source/blender/blenloader/intern/versioning_defaults.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index c50b410e2b9..fa61b1ca6cd 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -592,9 +592,11 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) bNodeSocketValueFloat *roughness_data = roughness_socket->default_value; roughness_data->value = 0.4f; node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; + nodeUpdate(ma->nodetree, node); } else if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { node->custom1 = SHD_SUBSURFACE_RANDOM_WALK; + nodeUpdate(ma->nodetree, node); } } } From 40c3b8836b7a36303ea9c78b0932758cbf277f93 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 20 Oct 2021 10:54:54 -0500 Subject: [PATCH 0995/1500] Geometry Nodes: Make Random ID a builtin attribute, remove sockets In order to address feedback that the "Stable ID" was not easy enough to use, remove the "Stable ID" output from the distribution node and the input from the instance on points node. Instead, the nodes write or read a builtin named attribute called `id`. In the future we may add more attributes like `edge_id` and `face_id`. The downside is that more behavior is invisible, which is les expected now that most attributes are passed around with node links. This behavior will have to be explained in the manual. The random value node's "ID" input that had an implicit index input is converted to a special implicit input that uses the `id` attribute if possible, but otherwise defaults to the index. There is no way to tell in the UI which it uses, except by knowing that rule and checking in the spreadsheet for the id attribute. Because it isn't always possible to create stable randomness, this attribute does not always exist, and it will be possible to remove it when we have the attribute remove node back, to improve performance. Differential Revision: https://developer.blender.org/D12903 --- source/blender/blenkernel/BKE_geometry_set.hh | 18 ++ .../blenkernel/intern/attribute_access.cc | 114 ++++++++- .../intern/attribute_access_intern.hh | 7 +- .../intern/geometry_component_curve.cc | 228 +++++++++++------- .../intern/geometry_component_mesh.cc | 42 +++- .../intern/geometry_component_pointcloud.cc | 13 +- source/blender/functions/FN_field.hh | 2 + source/blender/functions/intern/field.cc | 13 +- .../modifiers/intern/MOD_nodes_evaluator.cc | 6 +- .../node_geo_distribute_points_on_faces.cc | 35 +-- .../nodes/node_geo_instance_on_points.cc | 8 +- 11 files changed, 339 insertions(+), 147 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 66466e3972e..f57765e373b 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -760,6 +760,24 @@ class AttributeFieldInput : public fn::FieldInput { bool is_equal_to(const fn::FieldNode &other) const override; }; +class IDAttributeFieldInput : public fn::FieldInput { + public: + IDAttributeFieldInput() : fn::FieldInput(CPPType::get()) + { + } + + static fn::Field Create(); + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const override; + + std::string socket_inspection_name() const override; + + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + class AnonymousAttributeFieldInput : public fn::FieldInput { private: /** diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d0510e0008e..930cabafb00 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -345,11 +345,18 @@ GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( return {}; } - const int domain_size = component.attribute_domain_size(domain_); - const void *data = CustomData_get_layer(custom_data, stored_type_); + const void *data; + if (stored_as_named_attribute_) { + data = CustomData_get_layer_named(custom_data, stored_type_, name_.c_str()); + } + else { + data = CustomData_get_layer(custom_data, stored_type_); + } if (data == nullptr) { return {}; } + + const int domain_size = component.attribute_domain_size(domain_); return as_read_attribute_(data, domain_size); } @@ -368,11 +375,21 @@ GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( if (data == nullptr) { return {}; } - void *new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size); + + void *new_data; + if (stored_as_named_attribute_) { + new_data = CustomData_duplicate_referenced_layer_named( + custom_data, stored_type_, name_.c_str(), domain_size); + } + else { + new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size); + } + if (data != new_data) { custom_data_access_.update_custom_data_pointers(component); data = new_data; } + if (update_on_write_ != nullptr) { update_on_write_(component); } @@ -390,7 +407,19 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co } const int domain_size = component.attribute_domain_size(domain_); - const int layer_index = CustomData_get_layer_index(custom_data, stored_type_); + int layer_index; + if (stored_as_named_attribute_) { + for (const int i : IndexRange(custom_data->totlayer)) { + if (custom_data_layer_matches_attribute_id(custom_data->layers[i], name_)) { + layer_index = i; + break; + } + } + } + else { + layer_index = CustomData_get_layer_index(custom_data, stored_type_); + } + const bool delete_success = CustomData_free_layer( custom_data, stored_type_, domain_size, layer_index); if (delete_success) { @@ -409,14 +438,25 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, if (custom_data == nullptr) { return false; } - if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { - /* Exists already. */ - return false; - } const int domain_size = component.attribute_domain_size(domain_); - const bool success = add_builtin_type_custom_data_layer_from_init( - *custom_data, stored_type_, domain_size, initializer); + bool success; + if (stored_as_named_attribute_) { + if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) { + /* Exists already. */ + return false; + } + success = add_custom_data_layer_from_attribute_init( + name_, *custom_data, stored_type_, domain_size, initializer); + } + else { + if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { + /* Exists already. */ + return false; + } + success = add_builtin_type_custom_data_layer_from_init( + *custom_data, stored_type_, domain_size, initializer); + } if (success) { custom_data_access_.update_custom_data_pointers(component); } @@ -429,8 +469,10 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) if (custom_data == nullptr) { return false; } - const void *data = CustomData_get_layer(custom_data, stored_type_); - return data != nullptr; + if (stored_as_named_attribute_) { + return CustomData_get_layer_named(custom_data, stored_type_, name_.c_str()) != nullptr; + } + return CustomData_get_layer(custom_data, stored_type_) != nullptr; } ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( @@ -1353,6 +1395,54 @@ bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const return false; } +static StringRef get_random_id_attribute_name(const AttributeDomain domain) +{ + switch (domain) { + case ATTR_DOMAIN_POINT: + return "id"; + default: + return ""; + } +} + +const GVArray *IDAttributeFieldInput::get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const +{ + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + const StringRef name = get_random_id_attribute_name(domain); + GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32); + if (attribute) { + BLI_assert(attribute->size() == component.attribute_domain_size(domain)); + return scope.add(std::move(attribute)); + } + + /* Use the index as the fallback if no random ID attribute exists. */ + return fn::IndexFieldInput::get_index_varray(mask, scope); + } + return nullptr; +} + +std::string IDAttributeFieldInput::socket_inspection_name() const +{ + return TIP_("ID / Index"); +} + +uint64_t IDAttributeFieldInput::hash() const +{ + /* All random ID attribute inputs are the same within the same evaluation context. */ + return 92386459827; +} + +bool IDAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + /* All random ID attribute inputs are the same within the same evaluation context. */ + return dynamic_cast(&other) != nullptr; +} + const GVArray *AnonymousAttributeFieldInput::get_varray_for_context( const fn::FieldContext &context, IndexMask UNUSED(mask), ResourceScope &scope) const { diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 6e5cdd6faba..5cedcf69953 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -227,6 +227,9 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { * This provider is used to provide access to builtin attributes. It supports making internal types * available as different types. For example, the vertex position attribute is stored as part of * the #MVert struct, but is exposed as float3 attribute. + * + * It also supports named builtin attributes, and will look up attributes in #CustomData by name + * if the stored type is the same as the attribute type. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size); @@ -238,6 +241,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { const AsReadAttribute as_read_attribute_; const AsWriteAttribute as_write_attribute_; const UpdateOnWrite update_on_write_; + bool stored_as_named_attribute_; public: BuiltinCustomDataLayerProvider(std::string attribute_name, @@ -257,7 +261,8 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { custom_data_access_(custom_data_access), as_read_attribute_(as_read_attribute), as_write_attribute_(as_write_attribute), - update_on_write_(update_on_write) + update_on_write_(update_on_write), + stored_as_named_attribute_(data_type_ == stored_type_) { } diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 50c0f06b12c..c3d7eff4e6f 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -623,6 +623,103 @@ static void point_attribute_materialize_to_uninitialized(Span> data, } } +static GVArrayPtr varray_from_initializer(const AttributeInit &initializer, + const CustomDataType data_type, + const Span splines) +{ + switch (initializer.type) { + case AttributeInit::Type::Default: + /* This function shouldn't be called in this case, since there + * is no need to copy anything to the new custom data array. */ + BLI_assert_unreachable(); + return {}; + case AttributeInit::Type::VArray: + return static_cast(initializer).varray->shallow_copy(); + case AttributeInit::Type::MoveArray: + int total_size = 0; + for (const SplinePtr &spline : splines) { + total_size += spline->size(); + } + return std::make_unique( + GSpan(*bke::custom_data_type_to_cpp_type(data_type), + static_cast(initializer).data, + total_size)); + } + BLI_assert_unreachable(); + return {}; +} + +static bool create_point_attribute(GeometryComponent &component, + const AttributeIDRef &attribute_id, + const AttributeInit &initializer, + const CustomDataType data_type) +{ + CurveEval *curve = get_curve_from_component_for_write(component); + if (curve == nullptr || curve->splines().size() == 0) { + return false; + } + + MutableSpan splines = curve->splines(); + + /* First check the one case that allows us to avoid copying the input data. */ + if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) { + void *source_data = static_cast(initializer).data; + if (!splines.first()->attributes.create_by_move(attribute_id, data_type, source_data)) { + MEM_freeN(source_data); + return false; + } + return true; + } + + /* Otherwise just create a custom data layer on each of the splines. */ + for (const int i : splines.index_range()) { + if (!splines[i]->attributes.create(attribute_id, data_type)) { + /* If attribute creation fails on one of the splines, we cannot leave the custom data + * layers in the previous splines around, so delete them before returning. However, + * this is not an expected case. */ + BLI_assert_unreachable(); + return false; + } + } + + /* With a default initializer type, we can keep the values at their initial values. */ + if (initializer.type == AttributeInit::Type::Default) { + return true; + } + + WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id); + /* We just created the attribute, it should exist. */ + BLI_assert(write_attribute); + + GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, splines); + /* TODO: When we can call a variant of #set_all with a virtual array argument, + * this theoretically unnecessary materialize step could be removed. */ + GVArray_GSpan source_varray_span{*source_varray}; + write_attribute.varray->set_all(source_varray_span.data()); + + if (initializer.type == AttributeInit::Type::MoveArray) { + MEM_freeN(static_cast(initializer).data); + } + + return true; +} + +static bool remove_point_attribute(GeometryComponent &component, + const AttributeIDRef &attribute_id) +{ + CurveEval *curve = get_curve_from_component_for_write(component); + if (curve == nullptr) { + return false; + } + + /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */ + bool layer_freed = false; + for (SplinePtr &spline : curve->splines()) { + layer_freed = spline->attributes.remove(attribute_id); + } + return layer_freed; +} + /** * Virtual array for any control point data accessed with spans and an offset array. */ @@ -980,15 +1077,17 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu public: BuiltinPointAttributeProvider(std::string attribute_name, + const CreatableEnum creatable, + const DeletableEnum deletable, const GetSpan get_span, const GetMutableSpan get_mutable_span, const UpdateOnWrite update_on_write) : BuiltinAttributeProvider(std::move(attribute_name), ATTR_DOMAIN_POINT, bke::cpp_type_to_custom_data_type(CPPType::get()), - CreatableEnum::NonCreatable, + creatable, WritableEnum::Writable, - DeletableEnum::NonDeletable), + deletable), get_span_(get_span), get_mutable_span_(get_mutable_span), update_on_write_(update_on_write) @@ -1041,15 +1140,20 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu return point_data_gvarray(spans, offsets); } - bool try_delete(GeometryComponent &UNUSED(component)) const final + bool try_delete(GeometryComponent &component) const final { - return false; + if (deletable_ == DeletableEnum::NonDeletable) { + return false; + } + return remove_point_attribute(component, name_); } - bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const final + bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final { - return false; + if (createable_ == CreatableEnum::NonCreatable) { + return false; + } + return create_point_attribute(component, name_, initializer, CD_PROP_INT32); } bool exists(const GeometryComponent &component) const final @@ -1064,6 +1168,10 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu return false; } + if (!curve->splines().first()->attributes.get_for_read(name_)) { + return false; + } + bool has_point = false; for (const SplinePtr &spline : curve->splines()) { if (spline->size() != 0) { @@ -1090,6 +1198,8 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvidersplines()) { - layer_freed = spline->attributes.remove(attribute_id); - } - return layer_freed; - } - - static GVArrayPtr varray_from_initializer(const AttributeInit &initializer, - const CustomDataType data_type, - const int total_size) - { - switch (initializer.type) { - case AttributeInit::Type::Default: - /* This function shouldn't be called in this case, since there - * is no need to copy anything to the new custom data array. */ - BLI_assert_unreachable(); - return {}; - case AttributeInit::Type::VArray: - return static_cast(initializer).varray->shallow_copy(); - case AttributeInit::Type::MoveArray: - return std::make_unique( - GSpan(*bke::custom_data_type_to_cpp_type(data_type), - static_cast(initializer).data, - total_size)); - } - BLI_assert_unreachable(); - return {}; + return remove_point_attribute(component, attribute_id); } bool try_create(GeometryComponent &component, @@ -1364,55 +1442,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { if (domain != ATTR_DOMAIN_POINT) { return false; } - CurveEval *curve = get_curve_from_component_for_write(component); - if (curve == nullptr || curve->splines().size() == 0) { - return false; - } - - MutableSpan splines = curve->splines(); - - /* First check the one case that allows us to avoid copying the input data. */ - if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) { - void *source_data = static_cast(initializer).data; - if (!splines[0]->attributes.create_by_move(attribute_id, data_type, source_data)) { - MEM_freeN(source_data); - return false; - } - return true; - } - - /* Otherwise just create a custom data layer on each of the splines. */ - for (const int i : splines.index_range()) { - if (!splines[i]->attributes.create(attribute_id, data_type)) { - /* If attribute creation fails on one of the splines, we cannot leave the custom data - * layers in the previous splines around, so delete them before returning. However, - * this is not an expected case. */ - BLI_assert_unreachable(); - return false; - } - } - - /* With a default initializer type, we can keep the values at their initial values. */ - if (initializer.type == AttributeInit::Type::Default) { - return true; - } - - WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_id); - /* We just created the attribute, it should exist. */ - BLI_assert(write_attribute); - - const int total_size = curve->control_point_offsets().last(); - GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size); - /* TODO: When we can call a variant of #set_all with a virtual array argument, - * this theoretically unnecessary materialize step could be removed. */ - GVArray_GSpan source_varray_span{*source_varray}; - write_attribute.varray->set_all(source_varray_span.data()); - - if (initializer.type == AttributeInit::Type::MoveArray) { - MEM_freeN(static_cast(initializer).data); - } - - return true; + return create_point_attribute(component, attribute_id, initializer, data_type); } bool foreach_attribute(const GeometryComponent &component, @@ -1487,14 +1517,32 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() static BezierHandleAttributeProvider handles_start(false); static BezierHandleAttributeProvider handles_end(true); + static BuiltinPointAttributeProvider id( + "id", + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Deletable, + [](const Spline &spline) { + std::optional span = spline.attributes.get_for_read("id"); + return span ? span->typed() : Span(); + }, + [](Spline &spline) { + std::optional span = spline.attributes.get_for_write("id"); + return span ? span->typed() : MutableSpan(); + }, + {}); + static BuiltinPointAttributeProvider radius( "radius", + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::NonDeletable, [](const Spline &spline) { return spline.radii(); }, [](Spline &spline) { return spline.radii(); }, nullptr); static BuiltinPointAttributeProvider tilt( "tilt", + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::NonDeletable, [](const Spline &spline) { return spline.tilts(); }, [](Spline &spline) { return spline.tilts(); }, [](Spline &spline) { spline.mark_cache_invalid(); }); @@ -1502,7 +1550,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() static DynamicPointAttributeProvider point_custom_data; return ComponentAttributeProviders( - {&position, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic}, + {&position, &id, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic}, {&spline_custom_data, &point_custom_data}); } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 0c98aa5551b..6091d3f3dab 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -912,6 +912,19 @@ static GVMutableArrayPtr make_derived_write_attribute(void *data, const int doma MutableSpan((StructT *)data, domain_size)); } +template +static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size) +{ + return std::make_unique>(Span((const T *)data, domain_size)); +} + +template +static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size) +{ + return std::make_unique>( + MutableSpan((T *)data, domain_size)); +} + static float3 get_vertex_position(const MVert &vert) { return float3(vert.co); @@ -1274,6 +1287,18 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() static NormalAttributeProvider normal; + static BuiltinCustomDataLayerProvider id("id", + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + CD_PROP_INT32, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute, + make_array_write_attribute, + nullptr); + static BuiltinCustomDataLayerProvider material_index( "material_index", ATTR_DOMAIN_FACE, @@ -1335,14 +1360,15 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access); static CustomDataAttributeProvider face_custom_data(ATTR_DOMAIN_FACE, face_access); - return ComponentAttributeProviders({&position, &material_index, &shade_smooth, &normal, &crease}, - {&uvs, - &vertex_colors, - &corner_custom_data, - &vertex_groups, - &point_custom_data, - &edge_custom_data, - &face_custom_data}); + return ComponentAttributeProviders( + {&position, &id, &material_index, &shade_smooth, &normal, &crease}, + {&uvs, + &vertex_colors, + &corner_custom_data, + &vertex_groups, + &point_custom_data, + &edge_custom_data, + &face_custom_data}); } } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 6c4af7a6d23..dfb65a9078d 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -202,8 +202,19 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() make_array_read_attribute, make_array_write_attribute, nullptr); + static BuiltinCustomDataLayerProvider id("id", + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + CD_PROP_INT32, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute, + make_array_write_attribute, + nullptr); static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); - return ComponentAttributeProviders({&position, &radius}, {&point_custom_data}); + return ComponentAttributeProviders({&position, &radius, &id}, {&point_custom_data}); } } // namespace blender::bke diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 78a49e342a5..ed5064fdf25 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -418,6 +418,8 @@ class IndexFieldInput final : public FieldInput { public: IndexFieldInput(); + static GVArray *get_index_varray(IndexMask mask, ResourceScope &scope); + const GVArray *get_varray_for_context(const FieldContext &context, IndexMask mask, ResourceScope &scope) const final; diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 1f7bad134a8..c6125b65c5e 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -519,15 +519,20 @@ IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get(), "Index") { } +GVArray *IndexFieldInput::get_index_varray(IndexMask mask, ResourceScope &scope) +{ + auto index_func = [](int i) { return i; }; + return &scope.construct< + fn::GVArray_For_EmbeddedVArray>>( + mask.min_array_size(), mask.min_array_size(), index_func); +} + const GVArray *IndexFieldInput::get_varray_for_context(const fn::FieldContext &UNUSED(context), IndexMask mask, ResourceScope &scope) const { /* TODO: Investigate a similar method to IndexRange::as_span() */ - auto index_func = [](int i) { return i; }; - return &scope.construct< - fn::GVArray_For_EmbeddedVArray>>( - mask.min_array_size(), mask.min_array_size(), index_func); + return get_index_varray(mask, scope); } uint64_t IndexFieldInput::hash() const diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 0e7f5fe155b..51dd210c77a 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -335,8 +335,8 @@ static bool get_implicit_socket_input(const SocketRef &socket, void *r_value) } const nodes::SocketDeclaration &socket_declaration = *node_declaration->inputs()[socket.index()]; if (socket_declaration.input_field_type() == nodes::InputSocketFieldType::Implicit) { + const bNode &bnode = *socket.bnode(); if (socket.typeinfo()->type == SOCK_VECTOR) { - const bNode &bnode = *socket.bnode(); if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) { StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode == GEO_NODE_CURVE_HANDLE_LEFT ? @@ -349,6 +349,10 @@ static bool get_implicit_socket_input(const SocketRef &socket, void *r_value) return true; } if (socket.typeinfo()->type == SOCK_INT) { + if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) { + new (r_value) Field(std::make_shared()); + return true; + } new (r_value) Field(std::make_shared()); return true; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 44beead86ad..0d481011f00 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -58,7 +58,6 @@ static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBui b.add_output("Points"); b.add_output("Normal").field_source(); b.add_output("Rotation").subtype(PROP_EULER).field_source(); - b.add_output("Stable ID").field_source(); } static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout, @@ -329,7 +328,6 @@ namespace { struct AttributeOutputs { StrongAnonymousAttributeID normal_id; StrongAnonymousAttributeID rotation_id; - StrongAnonymousAttributeID stable_id_id; }; } // namespace @@ -339,19 +337,16 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com const Span looptri_indices, const AttributeOutputs &attribute_outputs) { - OutputAttribute_Typed id_attribute; + OutputAttribute_Typed id_attribute = point_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT); + MutableSpan ids = id_attribute.as_span(); + OutputAttribute_Typed normal_attribute; OutputAttribute_Typed rotation_attribute; - MutableSpan ids; MutableSpan normals; MutableSpan rotations; - if (attribute_outputs.stable_id_id) { - id_attribute = point_component.attribute_try_get_for_output_only( - attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT); - ids = id_attribute.as_span(); - } if (attribute_outputs.normal_id) { normal_attribute = point_component.attribute_try_get_for_output_only( attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT); @@ -379,9 +374,8 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com const float3 v1_pos = float3(mesh.mvert[v1_index].co); const float3 v2_pos = float3(mesh.mvert[v2_index].co); - if (!ids.is_empty()) { - ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index); - } + ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index); + float3 normal; if (!normals.is_empty() || !rotations.is_empty()) { normal_tri_v3(normal, v0_pos, v1_pos, v2_pos); @@ -394,9 +388,8 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com } } - if (id_attribute) { - id_attribute.save(); - } + id_attribute.save(); + if (normal_attribute) { normal_attribute.save(); } @@ -512,6 +505,10 @@ static void point_distribution_calculate(GeometrySet &geometry_set, } } + if (positions.is_empty()) { + return; + } + PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size()); uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); @@ -551,9 +548,6 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par if (params.output_is_required("Rotation")) { attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation"); } - if (params.output_is_required("Stable ID")) { - attribute_outputs.stable_id_id = StrongAnonymousAttributeID("stable id"); - } geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { point_distribution_calculate( @@ -575,11 +569,6 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par "Rotation", AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.rotation_id))); } - if (attribute_outputs.stable_id_id) { - params.set_output( - "Stable ID", - AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.stable_id_id))); - } } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index c7235fb2c29..78399fad2c0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -48,11 +48,6 @@ static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b) .subtype(PROP_XYZ) .supports_field() .description("Scale of the instances"); - b.add_input("Stable ID") - .supports_field() - .description( - "ID for every instance that is used to identify it over time even when the number of " - "instances changes. Used for example for motion blur"); b.add_output("Instances"); } @@ -91,12 +86,11 @@ static void add_instances_from_component(InstancesComponent &dst_component, const VArray *scales = nullptr; /* The evaluator could use the component's stable IDs as a destination directly, but only the * selected indices should be copied. */ - const VArray *stable_ids = nullptr; + GVArray_Typed stable_ids = src_component.attribute_get_for_read("id", ATTR_DOMAIN_POINT, 0); field_evaluator.add(params.get_input>("Pick Instance"), &pick_instance); field_evaluator.add(params.get_input>("Instance Index"), &indices); field_evaluator.add(params.get_input>("Rotation"), &rotations); field_evaluator.add(params.get_input>("Scale"), &scales); - field_evaluator.add(params.get_input>("Stable ID"), &stable_ids); field_evaluator.evaluate(); GVArray_Typed positions = src_component.attribute_get_for_read( From 0baa876b8347a81a457dadaa1f11acc7530c2769 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Wed, 20 Oct 2021 12:07:28 -0400 Subject: [PATCH 0996/1500] New test for USD import. Added a basic test for importing a primitive hierarchy from a USDA file into Blender. This was reviewed by Sybren in patch D12479. --- tests/python/CMakeLists.txt | 9 ++++ tests/python/bl_usd_import_test.py | 78 ++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 tests/python/bl_usd_import_test.py diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 2b31b6362e9..b761f310b1a 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -838,6 +838,15 @@ if(WITH_ALEMBIC) ) endif() +if(WITH_USD) + add_blender_test( + usd_import_test + --python ${CMAKE_CURRENT_LIST_DIR}/bl_usd_import_test.py + -- + --testdir "${TEST_SRC_DIR}/usd" + ) +endif() + if(WITH_CODEC_FFMPEG) add_python_test( ffmpeg diff --git a/tests/python/bl_usd_import_test.py b/tests/python/bl_usd_import_test.py new file mode 100644 index 00000000000..6f157c3f928 --- /dev/null +++ b/tests/python/bl_usd_import_test.py @@ -0,0 +1,78 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +import pathlib +import sys +import unittest + +import bpy + +args = None + +class AbstractUSDTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.testdir = args.testdir + + def setUp(self): + self.assertTrue(self.testdir.exists(), + 'Test dir {0} should exist'.format(self.testdir)) + + # Make sure we always start with a known-empty file. + bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend")) + +class USDImportTest(AbstractUSDTest): + + def test_import_prim_hierarchy(self): + """Test importing a simple object hierarchy from a USDA file.""" + + infile = str(self.testdir / "prim-hierarchy.usda") + + res = bpy.ops.wm.usd_import(filepath=infile) + self.assertEqual({'FINISHED'}, res) + + objects = bpy.context.scene.collection.objects + self.assertEqual(5, len(objects)) + + # Test the hierarchy. + self.assertIsNone(objects['World'].parent) + self.assertEqual(objects['World'], objects['Plane'].parent) + self.assertEqual(objects['World'], objects['Plane_001'].parent) + self.assertEqual(objects['World'], objects['Empty'].parent) + self.assertEqual(objects['Empty'], objects['Plane_002'].parent) + +def main(): + global args + import argparse + + if '--' in sys.argv: + argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:] + else: + argv = sys.argv + + parser = argparse.ArgumentParser() + parser.add_argument('--testdir', required=True, type=pathlib.Path) + args, remaining = parser.parse_known_args(argv) + + unittest.main(argv=remaining) + + +if __name__ == "__main__": + main() From 7d111f4ac2b1321fc9387922c606381cadd3f3ad Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 20 Oct 2021 17:58:16 +0200 Subject: [PATCH 0997/1500] Cleanup: remove unused code --- intern/cycles/device/cpu/kernel.cpp | 4 +--- intern/cycles/device/cpu/kernel.h | 4 ---- intern/cycles/kernel/device/cpu/kernel_arch.h | 8 -------- .../kernel/device/cpu/kernel_arch_impl.h | 19 ------------------- 4 files changed, 1 insertion(+), 34 deletions(-) diff --git a/intern/cycles/device/cpu/kernel.cpp b/intern/cycles/device/cpu/kernel.cpp index bbad2f3147d..3b253c094fd 100644 --- a/intern/cycles/device/cpu/kernel.cpp +++ b/intern/cycles/device/cpu/kernel.cpp @@ -50,9 +50,7 @@ CPUKernels::CPUKernels() REGISTER_KERNEL(adaptive_sampling_filter_x), REGISTER_KERNEL(adaptive_sampling_filter_y), /* Cryptomatte. */ - REGISTER_KERNEL(cryptomatte_postprocess), - /* Bake. */ - REGISTER_KERNEL(bake) + REGISTER_KERNEL(cryptomatte_postprocess) { } diff --git a/intern/cycles/device/cpu/kernel.h b/intern/cycles/device/cpu/kernel.h index 3787fe37a33..5f9cb85389f 100644 --- a/intern/cycles/device/cpu/kernel.h +++ b/intern/cycles/device/cpu/kernel.h @@ -102,10 +102,6 @@ class CPUKernels { CryptomattePostprocessFunction cryptomatte_postprocess; - /* Bake. */ - - CPUKernelFunction bake; - CPUKernels(); }; diff --git a/intern/cycles/kernel/device/cpu/kernel_arch.h b/intern/cycles/kernel/device/cpu/kernel_arch.h index 7a438b58e73..432ac5e15a9 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch.h @@ -107,12 +107,4 @@ void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobalsCPU * ccl_global float *render_buffer, int pixel_index); -/* -------------------------------------------------------------------- - * Bake. - */ -/* TODO(sergey): Needs to be re-implemented. Or not? Brecht did it already :) */ - -void KERNEL_FUNCTION_FULL_NAME(bake)( - const KernelGlobalsCPU *kg, float *buffer, int sample, int x, int y, int offset, int stride); - #undef KERNEL_ARCH diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h index ac606c768db..ba777062113 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h @@ -231,25 +231,6 @@ void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobalsCPU * #endif } -/* -------------------------------------------------------------------- - * Bake. - */ -/* TODO(sergey): Needs to be re-implemented. Or not? Brecht did it already :) */ - -void KERNEL_FUNCTION_FULL_NAME(bake)( - const KernelGlobalsCPU *kg, float *buffer, int sample, int x, int y, int offset, int stride) -{ -#if 0 -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, bake); -# else -# ifdef __BAKING__ - kernel_bake_evaluate(kg, buffer, sample, x, y, offset, stride); -# endif -# endif /* KERNEL_STUB */ -#endif -} - #undef KERNEL_INVOKE #undef DEFINE_INTEGRATOR_KERNEL #undef DEFINE_INTEGRATOR_SHADE_KERNEL From 9959c5315f462371c55f4164427af64f9d588d15 Mon Sep 17 00:00:00 2001 From: Peter Sergay Date: Wed, 20 Oct 2021 11:48:53 -0500 Subject: [PATCH 0998/1500] Geometry Nodes: Add warnings for instances input in two nodes Certain geometry nodes don't work properly on inputs that contain instances, but don't display any warning that they aren't working. The nodes now will display a warning that explains the situtation iff the input contains any instances. Differential Revision: https://developer.blender.org/D12858 --- .../blender/nodes/geometry/nodes/node_geo_curve_sample.cc | 6 ++++++ source/blender/nodes/geometry/nodes/node_geo_proximity.cc | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 6abb7cc31ac..6d371c27d43 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -237,6 +237,12 @@ static void geo_node_curve_sample_exec(GeoNodeExecParams params) params.set_output("Normal", fn::make_constant_field({0.0f, 0.0f, 0.0f})); }; + if (geometry_set.has_instances()) { + params.error_message_add( + NodeWarningType::Info, + TIP_("The node only supports realized curve data, instances are ignored")); + } + const CurveComponent *component = geometry_set.get_component_for_read(); if (component == nullptr) { return return_default(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index 02694a4a496..ec4d6ceb728 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -214,6 +214,12 @@ static void geo_node_proximity_exec(GeoNodeExecParams params) params.set_output("Distance", fn::make_constant_field(0.0f)); }; + if (geometry_set_target.has_instances()) { + params.error_message_add( + NodeWarningType::Info, + TIP_("The node only supports realized mesh or point cloud data, instances are ignored")); + } + if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) { return return_default(); } From 1de837437498f25997fa69df152529fe92a51f5c Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 20 Oct 2021 17:53:03 +0200 Subject: [PATCH 0999/1500] UI: Remove extra padding on Annotate tool settings For some reason the Annotate tool has an extra padding when compared to other tools. This makes the UI to flicker a bit (specially with D12939 applied). Differential Revision: https://developer.blender.org/D12943 --- release/scripts/startup/bl_ui/space_toolsystem_toolbar.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 9b1b401bc5b..721c6bdb99c 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -185,23 +185,18 @@ class _defs_annotate: tool_settings = context.tool_settings if space_type == 'VIEW_3D': - layout.separator() - row = layout.row(align=True) row.prop(tool_settings, "annotation_stroke_placement_view3d", text="Placement") if tool_settings.gpencil_stroke_placement_view3d == 'CURSOR': row.prop(tool_settings.gpencil_sculpt, "lockaxis") elif tool_settings.gpencil_stroke_placement_view3d in {'SURFACE', 'STROKE'}: row.prop(tool_settings, "use_gpencil_stroke_endpoints") - elif space_type in {'IMAGE_EDITOR', 'NODE_EDITOR', 'SEQUENCE_EDITOR', 'CLIP_EDITOR'}: - layout.separator() + elif space_type in {'IMAGE_EDITOR', 'NODE_EDITOR', 'SEQUENCE_EDITOR', 'CLIP_EDITOR'}: row = layout.row(align=True) row.prop(tool_settings, "annotation_stroke_placement_view2d", text="Placement") if tool.idname == "builtin.annotate_line": - layout.separator() - props = tool.operator_properties("gpencil.annotate") if region_type == 'TOOL_HEADER': row = layout.row() @@ -223,7 +218,6 @@ class _defs_annotate: subrow.prop(props, "stabilizer_radius", text="Radius", slider=True) subrow.prop(props, "stabilizer_factor", text="Factor", slider=True) else: - layout.separator() layout.prop(props, "use_stabilizer", text="Stabilize Stroke") col = layout.column(align=False) col.active = props.use_stabilizer From 010e675b1b436a824a3149dd478a1c8f4191fe42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Wed, 20 Oct 2021 13:20:44 +0200 Subject: [PATCH 1000/1500] Fix missing null-terminator in BLI_string_join_arrayN Although the documentation says so, the null-terminator was missing. This could cause crashes when logging shader linking errors as shader sources are empty in this case. --- source/blender/blenlib/intern/string_utils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c index bd733aca4f1..798eb60f64d 100644 --- a/source/blender/blenlib/intern/string_utils.c +++ b/source/blender/blenlib/intern/string_utils.c @@ -469,6 +469,7 @@ char *BLI_string_join_arrayN(const char *strings[], uint strings_len) for (uint i = 0; i < strings_len; i++) { c += BLI_strcpy_rlen(c, strings[i]); } + *c = '\0'; return result; } From 704d077d8fa35178af97ec5d946c470dbe25dab6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 20 Oct 2021 13:15:37 -0500 Subject: [PATCH 1001/1500] Fix: Crash when retrieving output "id" attribute The attribute provider needs to handle the case where the data is stored with just a data type, and the case where it is stored with a name. --- source/blender/blenkernel/intern/attribute_access.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 930cabafb00..3386d346364 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -371,7 +371,14 @@ GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( return {}; } const int domain_size = component.attribute_domain_size(domain_); - void *data = CustomData_get_layer(custom_data, stored_type_); + + void *data; + if (stored_as_named_attribute_) { + data = CustomData_get_layer_named(custom_data, stored_type_, name_.c_str()); + } + else { + data = CustomData_get_layer(custom_data, stored_type_); + } if (data == nullptr) { return {}; } From f0df0e9e07f9e0c8534cc77262cca9bae97ea618 Mon Sep 17 00:00:00 2001 From: William Leeson Date: Wed, 20 Oct 2021 20:51:56 +0200 Subject: [PATCH 1002/1500] Fix: Add cast to atof for CYCLES_CONCURRENT_STATES_FACTOR env variable parsing. The conversion from double to float was causing a build failure. Differential Revision: https://developer.blender.org/D12946 --- intern/cycles/device/hip/queue.cpp | 2 +- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- source/tools | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/intern/cycles/device/hip/queue.cpp b/intern/cycles/device/hip/queue.cpp index 0d9f5916d30..6cb29670f94 100644 --- a/intern/cycles/device/hip/queue.cpp +++ b/intern/cycles/device/hip/queue.cpp @@ -53,7 +53,7 @@ int HIPDeviceQueue::num_concurrent_states(const size_t state_size) const const char *factor_str = getenv("CYCLES_CONCURRENT_STATES_FACTOR"); if (factor_str) { - float factor = atof(factor_str); + float factor = (float)atof(factor_str); if (!factor) VLOG(3) << "CYCLES_CONCURRENT_STATES_FACTOR evaluated to 0"; num_states = max((int)(num_states * factor), 1024); diff --git a/release/datafiles/locale b/release/datafiles/locale index 75e46177f36..80d9e7ee122 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 75e46177f36a49ad36b917e641ee1586ddef7092 +Subproject commit 80d9e7ee122c626cbbcd1da554683bce79f8d3df diff --git a/release/scripts/addons b/release/scripts/addons index f10ca8c1561..e68c0118c13 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit f10ca8c156169b24e70027a43f718f99571d280f +Subproject commit e68c0118c13c3575e6096ad2dc7fb4434eadf38e diff --git a/source/tools b/source/tools index 5061594ee62..7c5acb95df9 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 5061594ee62b8eabf705443a5483c7a1dfaa8789 +Subproject commit 7c5acb95df918503d11cfc43172ce13901019289 From 98c53f660a57af837a160a842eb2e7a9a4c37de8 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 20 Oct 2021 14:22:25 -0500 Subject: [PATCH 1003/1500] Fix T92369: Raycast node crash with no target attribute The corrected hit mask should only be generated when there is data to transfer. Also correct two of the warning messages. --- .../nodes/geometry/nodes/node_geo_raycast.cc | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index e5ed5c02090..1e687f163e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -264,26 +264,26 @@ class RaycastFunction : public fn::MultiFunction { params.uninitialized_single_output_if_required(6, "Distance"), hit_count); - IndexMask hit_mask; - Vector hit_mask_indices; - if (hit_count < mask.size()) { - /* Not all rays hit the target. Create a corrected mask to avoid transferring attribute data - * to invalid indices. An alternative would be handling -1 indices in a separate case in - * #MeshAttributeInterpolator, but since it already has an IndexMask in its constructor, it's - * simpler to use that. */ - hit_mask_indices.reserve(hit_count); - for (const int64_t i : mask) { - if (hit_indices[i] != -1) { - hit_mask_indices.append(i); - } - hit_mask = IndexMask(hit_mask_indices); - } - } - else { - hit_mask = mask; - } - if (target_data_) { + IndexMask hit_mask; + Vector hit_mask_indices; + if (hit_count < mask.size()) { + /* Not all rays hit the target. Create a corrected mask to avoid transferring attribute + * data to invalid indices. An alternative would be handling -1 indices in a separate case + * in #MeshAttributeInterpolator, but since it already has an IndexMask in its constructor, + * it's simpler to use that. */ + hit_mask_indices.reserve(hit_count); + for (const int64_t i : mask) { + if (hit_indices[i] != -1) { + hit_mask_indices.append(i); + } + hit_mask = IndexMask(hit_mask_indices); + } + } + else { + hit_mask = mask; + } + GMutableSpan result = params.uninitialized_single_output_if_required(7, "Attribute"); if (!result.is_empty()) { MeshAttributeInterpolator interp(&mesh, hit_mask, hit_positions, hit_indices); @@ -393,11 +393,11 @@ static void geo_node_raycast_exec(GeoNodeExecParams params) if (target.has_realized_data()) { params.error_message_add( NodeWarningType::Info, - TIP_("Only realized geometry is supported, instances will not be used")); + TIP_("The node only supports realized mesh data, instances are ignored")); } else { params.error_message_add(NodeWarningType::Error, - TIP_("Target target must contain realized data")); + TIP_("The target geometry must contain realized data")); return return_default(); } } From 3f8b45d8d1fff8a082e3d35221d39854484e8551 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 20 Oct 2021 16:54:04 -0500 Subject: [PATCH 1004/1500] Fix: Builtin curve attributes unavailable After the addition of the `id` attribute in rB40c3b8836b7a, the `exists` function assumed that all attributes were stored in the custom data. --- .../intern/geometry_component_curve.cc | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index c3d7eff4e6f..2f2851b4fd4 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -1074,6 +1074,7 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu const GetSpan get_span_; const GetMutableSpan get_mutable_span_; const UpdateOnWrite update_on_write_; + bool stored_in_custom_data_; public: BuiltinPointAttributeProvider(std::string attribute_name, @@ -1081,7 +1082,8 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu const DeletableEnum deletable, const GetSpan get_span, const GetMutableSpan get_mutable_span, - const UpdateOnWrite update_on_write) + const UpdateOnWrite update_on_write, + const bool stored_in_custom_data) : BuiltinAttributeProvider(std::move(attribute_name), ATTR_DOMAIN_POINT, bke::cpp_type_to_custom_data_type(CPPType::get()), @@ -1090,7 +1092,8 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu deletable), get_span_(get_span), get_mutable_span_(get_mutable_span), - update_on_write_(update_on_write) + update_on_write_(update_on_write), + stored_in_custom_data_(stored_in_custom_data) { } @@ -1168,8 +1171,10 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu return false; } - if (!curve->splines().first()->attributes.get_for_read(name_)) { - return false; + if (stored_in_custom_data_) { + if (!curve->splines().first()->attributes.get_for_read(name_)) { + return false; + } } bool has_point = false; @@ -1202,7 +1207,8 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider span = spline.attributes.get_for_write("id"); return span ? span->typed() : MutableSpan(); }, - {}); + {}, + true); static BuiltinPointAttributeProvider radius( "radius", @@ -1537,7 +1544,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() BuiltinAttributeProvider::NonDeletable, [](const Spline &spline) { return spline.radii(); }, [](Spline &spline) { return spline.radii(); }, - nullptr); + nullptr, + false); static BuiltinPointAttributeProvider tilt( "tilt", @@ -1545,7 +1553,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() BuiltinAttributeProvider::NonDeletable, [](const Spline &spline) { return spline.tilts(); }, [](Spline &spline) { return spline.tilts(); }, - [](Spline &spline) { spline.mark_cache_invalid(); }); + [](Spline &spline) { spline.mark_cache_invalid(); }, + false); static DynamicPointAttributeProvider point_custom_data; From 6b761c59d25855080de683d15837f31172745f43 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 20 Oct 2021 17:45:14 -0500 Subject: [PATCH 1005/1500] Fix: Empty id attribute on curve control points No virtual array should be returned instead of returning an empty span. --- .../blender/blenkernel/intern/geometry_component_curve.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 2f2851b4fd4..f30ff2a70a7 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -1104,6 +1104,10 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu return {}; } + if (!this->exists(component)) { + return {}; + } + Span splines = curve->splines(); if (splines.size() == 1) { return std::make_unique(get_span_(*splines.first())); @@ -1125,6 +1129,10 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu return {}; } + if (!this->exists(component)) { + return {}; + } + MutableSpan splines = curve->splines(); if (splines.size() == 1) { return std::make_unique( From 15e71f3d97ec495e623ddac92de83e317f71417e Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 20 Oct 2021 19:57:03 -0300 Subject: [PATCH 1006/1500] Cleanup: Set default snap cursor values during build time --- .../editors/space_view3d/view3d_cursor_snap.c | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 52b9f468ca0..1b513c0a9a7 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -94,7 +94,27 @@ typedef struct SnapCursorDataIntern { bool is_initiated; } SnapCursorDataIntern; -static SnapCursorDataIntern g_data_intern = {{0}}; +static void v3d_cursor_snap_state_init(V3DSnapCursorState *state) +{ + state->prevpoint = NULL; + state->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | + SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT); + state->plane_axis = 2; + rgba_uchar_args_set(state->color_point, 255, 255, 255, 255); + rgba_uchar_args_set(state->color_line, 255, 255, 255, 128); + state->draw_point = true; + state->draw_plane = false; +} +static SnapCursorDataIntern g_data_intern = { + .state_default = {.prevpoint = NULL, + .snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | + SCE_SNAP_MODE_FACE | SCE_SNAP_MODE_EDGE_PERPENDICULAR | + SCE_SNAP_MODE_EDGE_MIDPOINT), + .plane_axis = 2, + .color_point = {255, 255, 255, 255}, + .color_line = {255, 255, 255, 128}, + .draw_point = true, + .draw_plane = false}}; /** * Calculate a 3x3 orientation matrix from the surface under the cursor. @@ -828,22 +848,10 @@ V3DSnapCursorState *ED_view3d_cursor_snap_state_get(void) return (V3DSnapCursorState *)&g_data_intern.state_intern[g_data_intern.state_active]; } -static void v3d_cursor_snap_state_init(V3DSnapCursorState *state) -{ - state->prevpoint = NULL; - state->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT); - state->plane_axis = 2; - rgba_uchar_args_set(state->color_point, 255, 255, 255, 255); - UI_GetThemeColor3ubv(TH_TRANSFORM, state->color_line); - state->color_line[3] = 128; - state->draw_point = true; - state->draw_plane = false; -} - static void v3d_cursor_snap_activate(void) { SnapCursorDataIntern *data_intern = &g_data_intern; + if (!data_intern->handle) { if (!data_intern->is_initiated) { /* Only initiate intern data once. @@ -855,9 +863,6 @@ static void v3d_cursor_snap_activate(void) data_intern->keymap = WM_modalkeymap_find(keyconf, "Generic Gizmo Tweak Modal Map"); RNA_enum_value_from_id(data_intern->keymap->modal_items, "SNAP_ON", &data_intern->snap_on); #endif - V3DSnapCursorState *state_default = &data_intern->state_default; - v3d_cursor_snap_state_init(state_default); - data_intern->is_initiated = true; } From 2905b493fe5b569069d6035c9a17ed855c03fa17 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 20 Oct 2021 20:49:02 -0300 Subject: [PATCH 1007/1500] Fix failing 'script_validate_keymap' after recent changes Properties with `_funcs_runtime` are always saved when exporting keymaps. This is an error since changing one changes all others. For now, work around the problem by setting the `PROP_IDPROPERTY` flag. --- .../editors/space_view3d/view3d_placement.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index b6444ebb29b..f1fd5732708 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -1359,6 +1359,12 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) /* properties */ PropertyRNA *prop; + /* WORKAROUND: properties with `_funcs_runtime` should not be saved in keymaps. + * So reasign the #PROP_IDPROPERTY flag to trick the property as not being set. + * (See #RNA_property_is_set). */ + PropertyFlag unsalvageable = PROP_SKIP_SAVE | PROP_HIDDEN | PROP_PTR_NO_OWNERSHIP | + PROP_IDPROPERTY; + /* Normally not accessed directly, leave unset and check the active tool. */ static const EnumPropertyItem primitive_type[] = { {PLACE_PRIMITIVE_TYPE_CUBE, "CUBE", 0, "Cube", ""}, @@ -1378,9 +1384,9 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_ui_text(prop, "Plane Axis", "The axis used for placing the base region"); RNA_def_property_enum_default(prop, 2); RNA_def_property_enum_items(prop, rna_enum_axis_xyz_items); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_property_enum_funcs_runtime( prop, idp_rna_plane_axis_get_fn, idp_rna_plane_axis_set_fn, NULL); + RNA_def_property_flag(prop, unsalvageable); prop = RNA_def_boolean(ot->srna, "plane_axis_auto", @@ -1388,9 +1394,9 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) "Auto Axis", "Select the closest axis when placing objects " "(surface overrides)"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_property_boolean_funcs_runtime( prop, idp_rna_use_plane_axis_auto_get_fn, idp_rna_use_plane_axis_auto_set_fn); + RNA_def_property_flag(prop, unsalvageable); static const EnumPropertyItem plane_depth_items[] = { {V3D_PLACE_DEPTH_SURFACE, @@ -1415,9 +1421,9 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_ui_text(prop, "Position", "The initial depth used when placing the cursor"); RNA_def_property_enum_default(prop, V3D_PLACE_DEPTH_SURFACE); RNA_def_property_enum_items(prop, plane_depth_items); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_property_enum_funcs_runtime( prop, idp_rna_plane_depth_get_fn, idp_rna_plane_depth_set_fn, NULL); + RNA_def_property_flag(prop, unsalvageable); static const EnumPropertyItem plane_orientation_items[] = { {V3D_PLACE_ORIENT_SURFACE, @@ -1436,9 +1442,9 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_ui_text(prop, "Orientation", "The initial depth used when placing the cursor"); RNA_def_property_enum_default(prop, V3D_PLACE_ORIENT_SURFACE); RNA_def_property_enum_items(prop, plane_orientation_items); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_property_enum_funcs_runtime( prop, idp_rna_plane_orient_get_fn, idp_rna_plane_orient_set_fn, NULL); + RNA_def_property_flag(prop, unsalvageable); static const EnumPropertyItem snap_to_items[] = { {PLACE_SNAP_TO_GEOMETRY, "GEOMETRY", 0, "Geometry", "Snap to all geometry"}, @@ -1449,9 +1455,9 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_ui_text(prop, "Snap to", "The target to use while snapping"); RNA_def_property_enum_default(prop, PLACE_SNAP_TO_GEOMETRY); RNA_def_property_enum_items(prop, snap_to_items); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_property_enum_funcs_runtime( prop, idp_rna_snap_target_get_fn, idp_rna_snap_target_set_fn, NULL); + RNA_def_property_flag(prop, unsalvageable); { /* Plane Origin. */ static const EnumPropertyItem items[] = { From 69102786047dccdcbaee0df6307a8c3364d28fe0 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 20 Oct 2021 20:49:02 -0300 Subject: [PATCH 1008/1500] Cleanup: Use array as a parameter for event x/y functions This change simplifies the parameter list for these functions and reduces the chance of typos mixing up array indices. Reviewed By: campbellbarton Ref D12950 --- source/blender/editors/include/UI_interface.h | 3 +- source/blender/editors/include/UI_view2d.h | 16 +++--- .../interface/interface_context_menu.c | 6 +-- .../editors/interface/interface_dropboxes.cc | 8 +-- .../editors/interface/interface_handlers.c | 39 +++++++------- .../editors/interface/interface_intern.h | 36 ++++++------- .../blender/editors/interface/interface_ops.c | 5 +- .../editors/interface/interface_panel.c | 2 +- .../editors/interface/interface_query.c | 51 +++++++++---------- .../interface/interface_region_search.c | 4 +- .../editors/interface/interface_view.cc | 6 +-- source/blender/editors/interface/tree_view.cc | 4 +- source/blender/editors/interface/view2d.c | 14 ++--- .../editors/interface/view2d_edge_pan.c | 18 +++---- source/blender/editors/interface/view2d_ops.c | 4 +- source/blender/editors/screen/screen_ops.c | 3 +- .../editors/space_console/space_console.c | 2 +- .../transform/transform_convert_node.c | 8 +-- 18 files changed, 108 insertions(+), 121 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 047340ac304..ed132422d3b 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2788,7 +2788,8 @@ void UI_tree_view_item_context_menu_build(struct bContext *C, const uiTreeViewItemHandle *item, uiLayout *column); -uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, int x, int y); +uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, + const int xy[2]) ATTR_NONNULL(1, 2); uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region); #ifdef __cplusplus diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index fdfa07a7e02..13895879f01 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -249,19 +249,20 @@ void UI_view2d_center_set(struct View2D *v2d, float x, float y); void UI_view2d_offset(struct View2D *v2d, float xfac, float yfac); -char UI_view2d_mouse_in_scrollers_ex( - const struct ARegion *region, const struct View2D *v2d, int x, int y, int *r_scroll); +char UI_view2d_mouse_in_scrollers_ex(const struct ARegion *region, + const struct View2D *v2d, + const int xy[2], + int *r_scroll) ATTR_NONNULL(1, 2, 3, 4); char UI_view2d_mouse_in_scrollers(const struct ARegion *region, const struct View2D *v2d, - int x, - int y); + const int xy[2]) ATTR_NONNULL(1, 2, 3); char UI_view2d_rect_in_scrollers_ex(const struct ARegion *region, const struct View2D *v2d, const struct rcti *rect, - int *r_scroll); + int *r_scroll) ATTR_NONNULL(1, 2, 3); char UI_view2d_rect_in_scrollers(const struct ARegion *region, const struct View2D *v2d, - const struct rcti *rect); + const struct rcti *rect) ATTR_NONNULL(1, 2, 3); /* cached text drawing in v2d, to allow pixel-aligned draw as post process */ void UI_view2d_text_cache_add(struct View2D *v2d, @@ -354,7 +355,8 @@ void UI_view2d_edge_pan_init(struct bContext *C, void UI_view2d_edge_pan_reset(struct View2DEdgePanData *vpd); /* Apply transform to view (i.e. adjust 'cur' rect). */ -void UI_view2d_edge_pan_apply(struct bContext *C, struct View2DEdgePanData *vpd, int x, int y); +void UI_view2d_edge_pan_apply(struct bContext *C, struct View2DEdgePanData *vpd, const int xy[2]) + ATTR_NONNULL(1, 2, 3); /* Apply transform to view using mouse events. */ void UI_view2d_edge_pan_apply_event(struct bContext *C, diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 7d5295aa758..9e436601132 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -927,8 +927,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev { const ARegion *region = CTX_wm_region(C); - uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over( - region, event->xy[0], event->xy[1]); + uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, event->xy); if (treerow_but) { BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW); UI_tree_view_item_context_menu_build( @@ -1216,8 +1215,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev ARegion *region = CTX_wm_region(C); const bool is_inside_listbox = ui_list_find_mouse_over(region, event) != NULL; const bool is_inside_listrow = is_inside_listbox ? - ui_list_row_find_mouse_over( - region, event->xy[0], event->xy[1]) != NULL : + ui_list_row_find_mouse_over(region, event->xy) != NULL : false; if (is_inside_listrow) { MenuType *mt = WM_menutype_find("UI_MT_list_item_context_menu", true); diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index 34bd5f17881..62250a34cf4 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -29,8 +29,8 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, event->xy[0], event->xy[1]); + const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, + event->xy); if (!hovered_tree_item) { return false; } @@ -44,8 +44,8 @@ static char *ui_tree_view_drop_tooltip(bContext *C, wmDropBox *UNUSED(drop)) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, event->xy[0], event->xy[1]); + const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, + event->xy); if (!hovered_tree_item) { return nullptr; } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 129f3143451..243ebe4e75c 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1684,7 +1684,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const */ if (drag_info->is_xy_lock_init == false) { /* first store the buttons original coords */ - uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true, NULL, NULL); + uiBut *but = ui_but_find_mouse_over_ex(region, xy_input, true, NULL, NULL); if (but) { if (but->flag & UI_BUT_DRAG_LOCK) { @@ -1754,8 +1754,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void if (done) { wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - uiBut *but = ui_but_find_mouse_over_ex( - region, drag_info->xy_init[0], drag_info->xy_init[1], true, NULL, NULL); + uiBut *but = ui_but_find_mouse_over_ex(region, drag_info->xy_init, true, NULL, NULL); if (but) { ui_apply_but_undo(but); @@ -3738,7 +3737,7 @@ static void ui_do_but_textedit( /* exit on LMB only on RELEASE for searchbox, to mimic other popups, * and allow multiple menu levels */ if (data->searchbox) { - inbox = ui_searchbox_inside(data->searchbox, event->xy[0], event->xy[1]); + inbox = ui_searchbox_inside(data->searchbox, event->xy); } /* for double click: we do a press again for when you first click on button @@ -4360,8 +4359,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C, uiButtonActivateType activate_type) { ARegion *region = CTX_wm_region(C); - uiBut *labelbut = ui_but_find_mouse_over_ex( - region, event->xy[0], event->xy[1], true, NULL, NULL); + uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->xy, true, NULL, NULL); if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) { /* exit listrow */ @@ -4565,8 +4563,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C, if (event->type == LEFTMOUSE && event->val == KM_PRESS) { /* only cancel if click outside the button */ - if (ui_but_contains_point_px(but, but->active->region, event->xy[0], event->xy[1]) == - false) { + if (ui_but_contains_point_px(but, but->active->region, event->xy) == false) { /* data->cancel doesn't work, this button opens immediate */ if (but->flag & UI_BUT_IMMEDIATE) { ui_but_value_set(but, 0); @@ -8815,7 +8812,7 @@ uiBlock *UI_region_block_find_mouse_over(const struct ARegion *region, const int xy[2], bool only_clip) { - return ui_block_find_mouse_over_ex(region, xy[0], xy[1], only_clip); + return ui_block_find_mouse_over_ex(region, xy, only_clip); } /** @@ -9214,7 +9211,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) /* always deactivate button for pie menus, * else moving to blank space will leave activated */ if ((!ui_block_is_menu(block) || ui_block_is_pie_menu(block)) && - !ui_but_contains_point_px(but, region, event->xy[0], event->xy[1])) { + !ui_but_contains_point_px(but, region, event->xy)) { exit = true; } else if (but_other && ui_but_is_editable(but_other) && (but_other != but)) { @@ -9242,7 +9239,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) WM_event_remove_timer(data->wm, data->window, data->autoopentimer); data->autoopentimer = NULL; - if (ui_but_contains_point_px(but, region, event->xy[0], event->xy[1]) || but->active) { + if (ui_but_contains_point_px(but, region, event->xy) || but->active) { button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); } } @@ -9292,7 +9289,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) case MOUSEMOVE: { /* deselect the button when moving the mouse away */ /* also de-activate for buttons that only show highlights */ - if (ui_but_contains_point_px(but, region, event->xy[0], event->xy[1])) { + if (ui_but_contains_point_px(but, region, event->xy)) { /* Drag on a hold button (used in the toolbar) now opens it immediately. */ if (data->hold_action_timer) { @@ -9350,7 +9347,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) uiBut *bt; if (data->menu && data->menu->region) { - if (ui_region_contains_point_px(data->menu->region, event->xy[0], event->xy[1])) { + if (ui_region_contains_point_px(data->menu->region, event->xy)) { break; } } @@ -9466,7 +9463,7 @@ static int ui_list_activate_hovered_row(bContext *C, } const int *mouse_xy = ISTWEAK(event->type) ? &event->prev_click_xy[0] : &event->xy[0]; - uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy[0], mouse_xy[1]); + uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy); if (listrow) { wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype; @@ -9493,8 +9490,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, { /* On a tweak event, uses the coordinates from where tweaking was started. */ const int *mouse_xy = ISTWEAK(event->type) ? &event->prev_click_xy[0] : &event->xy[0]; - const uiBut *hovered_but = ui_but_find_mouse_over_ex( - region, mouse_xy[0], mouse_xy[1], false, NULL, NULL); + const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, NULL, NULL); if (list->dyn_data->custom_drag_optype) { if (ui_but_context_poll_operator(C, list->dyn_data->custom_drag_optype, hovered_but)) { @@ -9736,7 +9732,7 @@ static int ui_handle_tree_hover(const wmEvent *event, const ARegion *region) /* Always highlight the hovered tree-row, even if the mouse hovers another button inside of it. */ - uiBut *hovered_row_but = ui_tree_row_find_mouse_over(region, event->xy[0], event->xy[1]); + uiBut *hovered_row_but = ui_tree_row_find_mouse_over(region, event->xy); if (hovered_row_but) { hovered_row_but->flag |= UI_ACTIVE; } @@ -9779,8 +9775,7 @@ static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, u button_activate_exit(C, but, data, true, false); } else if (menu->menuretval & UI_RETURN_OUT) { - if (event->type == MOUSEMOVE && - ui_but_contains_point_px(but, data->region, event->xy[0], event->xy[1])) { + if (event->type == MOUSEMOVE && ui_but_contains_point_px(but, data->region, event->xy)) { button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT); } else { @@ -10119,13 +10114,13 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock else if (!ui_block_is_menu(but->block) || ui_block_is_pie_menu(but->block)) { /* pass, skip for dialogs */ } - else if (!ui_region_contains_point_px(but->active->region, event->xy[0], event->xy[1])) { + else if (!ui_region_contains_point_px(but->active->region, event->xy)) { /* Pass, needed to click-exit outside of non-floating menus. */ ui_region_auto_open_clear(but->active->region); } else if ((!ELEM(event->type, MOUSEMOVE, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN)) && ISMOUSE(event->type)) { - if (!ui_but_contains_point_px(but, but->active->region, event->xy[0], event->xy[1])) { + if (!ui_but_contains_point_px(but, but->active->region, event->xy)) { but = NULL; } } @@ -10723,7 +10718,7 @@ static int ui_handle_menu_event(bContext *C, #ifdef USE_DRAG_POPUP else if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && (inside && is_floating && inside_title)) { - if (!but || !ui_but_contains_point_px(but, region, event->xy[0], event->xy[1])) { + if (!but || !ui_but_contains_point_px(but, region, event->xy)) { if (but) { UI_but_tooltip_timer_remove(C, but); } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 0301ab9156e..b91dafeb02b 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -830,7 +830,7 @@ struct ARegion *ui_searchbox_create_menu(struct bContext *C, struct ARegion *butregion, uiButSearch *search_but); -bool ui_searchbox_inside(struct ARegion *region, int x, int y); +bool ui_searchbox_inside(struct ARegion *region, const int xy[2]) ATTR_NONNULL(1, 2); int ui_searchbox_find_index(struct ARegion *region, const char *name); void ui_searchbox_update(struct bContext *C, struct ARegion *region, uiBut *but, const bool reset); int ui_searchbox_autocomplete(struct bContext *C, struct ARegion *region, uiBut *but, char *str); @@ -1163,37 +1163,36 @@ bool ui_but_contains_rect(const uiBut *but, const rctf *rect); bool ui_but_contains_point_px_icon(const uiBut *but, struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT; -bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, int x, int y) - ATTR_WARN_UNUSED_RESULT; +bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2, 3) ATTR_WARN_UNUSED_RESULT; uiBut *ui_list_find_mouse_over(const struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT; uiBut *ui_list_find_from_row(const struct ARegion *region, const uiBut *row_but) ATTR_WARN_UNUSED_RESULT; -uiBut *ui_list_row_find_mouse_over(const struct ARegion *region, - int x, - int y) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_list_row_find_mouse_over(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; uiBut *ui_list_row_find_from_index(const struct ARegion *region, const int index, uiBut *listbox) ATTR_WARN_UNUSED_RESULT; -uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int x, const int y); +uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2); uiBut *ui_tree_row_find_active(const struct ARegion *region); typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region, - const int x, - const int y, + const int xy[2], const bool labeledit, const uiButFindPollFn find_poll, - const void *find_custom_data) ATTR_WARN_UNUSED_RESULT; + const void *find_custom_data) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_find_mouse_over(const struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) ATTR_WARN_UNUSED_RESULT; -uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region, - int x, - int y) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; bool ui_but_contains_password(const uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -1213,10 +1212,8 @@ bool ui_block_is_popover(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; bool ui_block_is_popup_any(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; -uiBlock *ui_block_find_mouse_over_ex(const struct ARegion *region, - const int x, - const int y, - bool only_clip); +uiBlock *ui_block_find_mouse_over_ex(const struct ARegion *region, const int xy[2], bool only_clip) + ATTR_NONNULL(1, 2); uiBlock *ui_block_find_mouse_over(const struct ARegion *region, const struct wmEvent *event, bool only_clip); @@ -1225,9 +1222,8 @@ uiBut *ui_region_find_first_but_test_flag(struct ARegion *region, int flag_include, int flag_exclude); uiBut *ui_region_find_active_but(struct ARegion *region) ATTR_WARN_UNUSED_RESULT; -bool ui_region_contains_point_px(const struct ARegion *region, - int x, - int y) ATTR_WARN_UNUSED_RESULT; +bool ui_region_contains_point_px(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; bool ui_region_contains_rect_px(const struct ARegion *region, const rcti *rect_px); struct ARegion *ui_screen_region_find_mouse_over_ex(struct bScreen *screen, int x, int y); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index e58b2168dda..1a1d52b0425 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1930,7 +1930,7 @@ static bool ui_tree_view_drop_poll(bContext *C) const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, win->eventstate->xy[0], win->eventstate->xy[1]); + region, win->eventstate->xy); return hovered_tree_item != NULL; } @@ -1942,8 +1942,7 @@ static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const w } const ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, event->xy[0], event->xy[1]); + uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, event->xy); if (!UI_tree_view_item_drop_handle(hovered_tree_item, event->customdata)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 11013e4cdf7..43536b23353 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -2363,7 +2363,7 @@ int ui_handler_panel_region(bContext *C, } /* Scroll-bars can overlap panels now, they have handling priority. */ - if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->xy[0], event->xy[1])) { + if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->xy)) { return WM_UI_HANDLER_CONTINUE; } diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index b80e0bfc3ca..85f829c7f48 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -222,14 +222,14 @@ bool ui_but_contains_rect(const uiBut *but, const rctf *rect) return BLI_rctf_isect(&but->rect, rect, NULL); } -bool ui_but_contains_point_px(const uiBut *but, const ARegion *region, int x, int y) +bool ui_but_contains_point_px(const uiBut *but, const ARegion *region, const int xy[2]) { uiBlock *block = but->block; - if (!ui_region_contains_point_px(region, x, y)) { + if (!ui_region_contains_point_px(region, xy)) { return false; } - float mx = x, my = y; + float mx = xy[0], my = xy[1]; ui_window_to_block_fl(region, block, &mx, &my); if (but->pie_dir != UI_RADIAL_NONE) { @@ -286,19 +286,18 @@ static uiBut *ui_but_find(const ARegion *region, /* x and y are only used in case event is NULL... */ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, - const int x, - const int y, + const int xy[2], const bool labeledit, const uiButFindPollFn find_poll, const void *find_custom_data) { uiBut *butover = NULL; - if (!ui_region_contains_point_px(region, x, y)) { + if (!ui_region_contains_point_px(region, xy)) { return NULL; } LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - float mx = x, my = y; + float mx = xy[0], my = xy[1]; ui_window_to_block_fl(region, block, &mx, &my); LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { @@ -333,8 +332,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event) { - return ui_but_find_mouse_over_ex( - region, event->xy[0], event->xy[1], event->ctrl != 0, NULL, NULL); + return ui_but_find_mouse_over_ex(region, event->xy, event->ctrl != 0, NULL, NULL); } uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) @@ -375,13 +373,13 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) return butover; } -uiBut *ui_list_find_mouse_over_ex(const ARegion *region, int x, int y) +uiBut *ui_list_find_mouse_over_ex(const ARegion *region, const int xy[2]) { - if (!ui_region_contains_point_px(region, x, y)) { + if (!ui_region_contains_point_px(region, xy)) { return NULL; } LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - float mx = x, my = y; + float mx = xy[0], my = xy[1]; ui_window_to_block_fl(region, block, &mx, &my); LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { if (but->type == UI_BTYPE_LISTBOX && ui_but_contains_pt(but, mx, my)) { @@ -399,7 +397,7 @@ uiBut *ui_list_find_mouse_over(const ARegion *region, const wmEvent *event) /* If there is no info about the mouse, just act as if there is nothing underneath it. */ return NULL; } - return ui_list_find_mouse_over_ex(region, event->xy[0], event->xy[1]); + return ui_list_find_mouse_over_ex(region, event->xy); } uiList *UI_list_find_mouse_over(const ARegion *region, const wmEvent *event) @@ -436,9 +434,9 @@ static bool ui_but_is_listrow(const uiBut *but, const void *UNUSED(customdata)) return but->type == UI_BTYPE_LISTROW; } -uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int x, const int y) +uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int xy[2]) { - return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_listrow, NULL); + return ui_but_find_mouse_over_ex(region, xy, false, ui_but_is_listrow, NULL); } struct ListRowFindIndexData { @@ -469,9 +467,9 @@ static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata)) return but->type == UI_BTYPE_TREEROW; } -uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int x, const int y) +uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int xy[2]) { - return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_treerow, NULL); + return ui_but_find_mouse_over_ex(region, xy, false, ui_but_is_treerow, NULL); } static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata) @@ -683,12 +681,9 @@ bool UI_block_can_add_separator(const uiBlock *block) /** \name Block (#uiBlock) Spatial * \{ */ -uiBlock *ui_block_find_mouse_over_ex(const ARegion *region, - const int x, - const int y, - bool only_clip) +uiBlock *ui_block_find_mouse_over_ex(const ARegion *region, const int xy[2], bool only_clip) { - if (!ui_region_contains_point_px(region, x, y)) { + if (!ui_region_contains_point_px(region, xy)) { return NULL; } LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { @@ -697,7 +692,7 @@ uiBlock *ui_block_find_mouse_over_ex(const ARegion *region, continue; } } - float mx = x, my = y; + float mx = xy[0], my = xy[1]; ui_window_to_block_fl(region, block, &mx, &my); if (BLI_rctf_isect_pt(&block->rect, mx, my)) { return block; @@ -708,7 +703,7 @@ uiBlock *ui_block_find_mouse_over_ex(const ARegion *region, uiBlock *ui_block_find_mouse_over(const ARegion *region, const wmEvent *event, bool only_clip) { - return ui_block_find_mouse_over_ex(region, event->xy[0], event->xy[1], only_clip); + return ui_block_find_mouse_over_ex(region, event->xy, only_clip); } /** \} */ @@ -748,11 +743,11 @@ uiBut *ui_region_find_first_but_test_flag(ARegion *region, int flag_include, int /** \name Region (#ARegion) Spatial * \{ */ -bool ui_region_contains_point_px(const ARegion *region, int x, int y) +bool ui_region_contains_point_px(const ARegion *region, const int xy[2]) { rcti winrct; ui_region_winrct_get_no_margin(region, &winrct); - if (!BLI_rcti_isect_pt(&winrct, x, y)) { + if (!BLI_rcti_isect_pt_v(&winrct, xy)) { return false; } @@ -763,11 +758,11 @@ bool ui_region_contains_point_px(const ARegion *region, int x, int y) */ if (region->v2d.mask.xmin != region->v2d.mask.xmax) { const View2D *v2d = ®ion->v2d; - int mx = x, my = y; + int mx = xy[0], my = xy[1]; ui_window_to_region(region, &mx, &my); if (!BLI_rcti_isect_pt(&v2d->mask, mx, my) || - UI_view2d_mouse_in_scrollers(region, ®ion->v2d, x, y)) { + UI_view2d_mouse_in_scrollers(region, ®ion->v2d, xy)) { return false; } } diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index a78c208e4ca..1cd3ef89ed3 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -290,11 +290,11 @@ int ui_searchbox_find_index(ARegion *region, const char *name) } /* x and y in screen-coords. */ -bool ui_searchbox_inside(ARegion *region, int x, int y) +bool ui_searchbox_inside(ARegion *region, const int xy[2]) { uiSearchboxData *data = region->regiondata; - return BLI_rcti_isect_pt(&data->bbox, x - region->winrct.xmin, y - region->winrct.ymin); + return BLI_rcti_isect_pt(&data->bbox, xy[0] - region->winrct.xmin, xy[1] - region->winrct.ymin); } /* string validated to be of correct length (but->hardmax) */ diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc index fdd8eb0cc71..4e38f245155 100644 --- a/source/blender/editors/interface/interface_view.cc +++ b/source/blender/editors/interface/interface_view.cc @@ -82,11 +82,9 @@ void ui_block_free_views(uiBlock *block) /** * \param x, y: Coordinate to find a tree-row item at, in window space. */ -uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, - const int x, - const int y) +uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, const int xy[2]) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, x, y); + uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, xy); if (!tree_row_but) { return nullptr; } diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 6e4440947be..88aa362deb5 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -220,8 +220,8 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *hovered_item_handle = UI_block_tree_view_find_item_at( - region, win->eventstate->xy[0], win->eventstate->xy[1]); + uiTreeViewItemHandle *hovered_item_handle = UI_block_tree_view_find_item_at(region, + win->eventstate->xy); AbstractTreeViewItem *hovered_item = reinterpret_cast( hovered_item_handle); BLI_assert(hovered_item != nullptr); diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 0036a812a87..ca96fde9810 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -1924,8 +1924,10 @@ void UI_view2d_offset(struct View2D *v2d, float xfac, float yfac) * - 'v' = in vertical scroller. * - 0 = not in scroller. */ -char UI_view2d_mouse_in_scrollers_ex( - const ARegion *region, const View2D *v2d, int x, int y, int *r_scroll) +char UI_view2d_mouse_in_scrollers_ex(const ARegion *region, + const View2D *v2d, + const int xy[2], + int *r_scroll) { const int scroll = view2d_scroll_mapped(v2d->scroll); *r_scroll = scroll; @@ -1933,8 +1935,8 @@ char UI_view2d_mouse_in_scrollers_ex( if (scroll) { /* Move to region-coordinates. */ const int co[2] = { - x - region->winrct.xmin, - y - region->winrct.ymin, + xy[0] - region->winrct.xmin, + xy[1] - region->winrct.ymin, }; if (scroll & V2D_SCROLL_HORIZONTAL) { if (IN_2D_HORIZ_SCROLL(v2d, co)) { @@ -1978,10 +1980,10 @@ char UI_view2d_rect_in_scrollers_ex(const ARegion *region, return 0; } -char UI_view2d_mouse_in_scrollers(const ARegion *region, const View2D *v2d, int x, int y) +char UI_view2d_mouse_in_scrollers(const ARegion *region, const View2D *v2d, const int xy[2]) { int scroll_dummy = 0; - return UI_view2d_mouse_in_scrollers_ex(region, v2d, x, y, &scroll_dummy); + return UI_view2d_mouse_in_scrollers_ex(region, v2d, xy, &scroll_dummy); } char UI_view2d_rect_in_scrollers(const ARegion *region, const View2D *v2d, const rcti *rect) diff --git a/source/blender/editors/interface/view2d_edge_pan.c b/source/blender/editors/interface/view2d_edge_pan.c index 0ad6883b078..a49666ebbd3 100644 --- a/source/blender/editors/interface/view2d_edge_pan.c +++ b/source/blender/editors/interface/view2d_edge_pan.c @@ -217,7 +217,7 @@ static void edge_pan_apply_delta(bContext *C, View2DEdgePanData *vpd, float dx, UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY); } -void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, int x, int y) +void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, const int xy[2]) { ARegion *region = vpd->region; @@ -229,18 +229,18 @@ void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, int x, int y) int pan_dir_x = 0; int pan_dir_y = 0; - if ((vpd->outside_pad == 0) || BLI_rcti_isect_pt(&outside_rect, x, y)) { + if ((vpd->outside_pad == 0) || BLI_rcti_isect_pt_v(&outside_rect, xy)) { /* Find whether the mouse is beyond X and Y edges. */ - if (x > inside_rect.xmax) { + if (xy[0] > inside_rect.xmax) { pan_dir_x = 1; } - else if (x < inside_rect.xmin) { + else if (xy[0] < inside_rect.xmin) { pan_dir_x = -1; } - if (y > inside_rect.ymax) { + if (xy[1] > inside_rect.ymax) { pan_dir_y = 1; } - else if (y < inside_rect.ymin) { + else if (xy[1] < inside_rect.ymin) { pan_dir_y = -1; } } @@ -252,11 +252,11 @@ void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, int x, int y) const float dtime = (float)(current_time - vpd->edge_pan_last_time); float dx = 0.0f, dy = 0.0f; if (pan_dir_x != 0) { - const float speed = edge_pan_speed(vpd, x, true, current_time); + const float speed = edge_pan_speed(vpd, xy[0], true, current_time); dx = dtime * speed * (float)pan_dir_x; } if (pan_dir_y != 0) { - const float speed = edge_pan_speed(vpd, y, false, current_time); + const float speed = edge_pan_speed(vpd, xy[1], false, current_time); dy = dtime * speed * (float)pan_dir_y; } vpd->edge_pan_last_time = current_time; @@ -272,7 +272,7 @@ void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const w return; } - UI_view2d_edge_pan_apply(C, vpd, event->xy[0], event->xy[1]); + UI_view2d_edge_pan_apply(C, vpd, event->xy); } void UI_view2d_edge_pan_cancel(bContext *C, View2DEdgePanData *vpd) diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index a73449f659d..0bca4e327cc 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -1846,7 +1846,7 @@ static bool scroller_activate_poll(bContext *C) wmEvent *event = win->eventstate; /* check if mouse in scrollbars, if they're enabled */ - return (UI_view2d_mouse_in_scrollers(region, v2d, event->xy[0], event->xy[1]) != 0); + return (UI_view2d_mouse_in_scrollers(region, v2d, event->xy) != 0); } /* initialize customdata for scroller manipulation operator */ @@ -2090,7 +2090,7 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent * View2D *v2d = ®ion->v2d; /* check if mouse in scrollbars, if they're enabled */ - const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->xy[0], event->xy[1]); + const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->xy); /* if in a scroller, init customdata then set modal handler which will * catch mouse-down to start doing useful stuff */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index ca2eeca2284..861c2573de8 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -900,8 +900,7 @@ static AZone *area_actionzone_refresh_xy(ScrArea *area, const int xy[2], const b ARegion *region = az->region; View2D *v2d = ®ion->v2d; int scroll_flag = 0; - const int isect_value = UI_view2d_mouse_in_scrollers_ex( - region, v2d, xy[0], xy[1], &scroll_flag); + const int isect_value = UI_view2d_mouse_in_scrollers_ex(region, v2d, xy, &scroll_flag); /* Check if we even have scroll bars. */ if (((az->direction == AZ_SCROLL_HOR) && !(scroll_flag & V2D_SCROLL_HORIZONTAL)) || diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 50e664913d2..84be24e96ac 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -149,7 +149,7 @@ static void console_cursor(wmWindow *win, ScrArea *UNUSED(area), ARegion *region { int wmcursor = WM_CURSOR_TEXT_EDIT; const wmEvent *event = win->eventstate; - if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->xy[0], event->xy[1])) { + if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->xy)) { wmcursor = WM_CURSOR_DEFAULT; } diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index ecc7f01be33..da11666d445 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -173,9 +173,11 @@ void flushTransNodes(TransInfo *t) } else { /* Edge panning functions expect window coordinates, mval is relative to region */ - const float x = t->region->winrct.xmin + t->mval[0]; - const float y = t->region->winrct.ymin + t->mval[1]; - UI_view2d_edge_pan_apply(t->context, customdata, x, y); + const int xy[2] = { + t->region->winrct.xmin + t->mval[0], + t->region->winrct.ymin + t->mval[1], + }; + UI_view2d_edge_pan_apply(t->context, customdata, xy); } } From 035dcdad90ec9d6881e2d99b90e30f5a481237e1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Oct 2021 13:24:17 +1100 Subject: [PATCH 1009/1500] Cleanup: use underscore separators for event struct members Improve readability using underscores for separators, e.g. prev_click_time instead of prevclicktime. --- source/blender/editors/interface/interface.c | 2 +- .../editors/interface/interface_handlers.c | 2 +- source/blender/editors/mesh/editmesh_knife.c | 4 +- source/blender/editors/screen/screen_ops.c | 2 +- .../editors/sculpt_paint/sculpt_mask_expand.c | 8 +- source/blender/editors/transform/transform.c | 8 +- source/blender/windowmanager/WM_types.h | 12 +-- .../gizmo/intern/wm_gizmo_group.c | 4 +- .../windowmanager/intern/wm_event_query.c | 10 +-- .../windowmanager/intern/wm_event_system.c | 78 +++++++++---------- .../blender/windowmanager/intern/wm_window.c | 2 +- 11 files changed, 66 insertions(+), 66 deletions(-) diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index fe4caf03184..5068969946a 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -7013,7 +7013,7 @@ void UI_but_focus_on_enter_event(wmWindow *win, uiBut *but) event.val = KM_PRESS; event.is_repeat = false; event.customdata = but; - event.customdatafree = false; + event.customdata_free = false; wm_event_add(win, &event); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 243ebe4e75c..556474f6df4 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -9034,7 +9034,7 @@ void ui_but_activate_event(bContext *C, ARegion *region, uiBut *but) event.val = KM_PRESS; event.is_repeat = false; event.customdata = but; - event.customdatafree = false; + event.customdata_free = false; ui_do_button(C, but->block, but, &event); } diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 036a2d9582f..d073f5f2ba4 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -4515,7 +4515,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) knife_recalc_ortho(kcd); /* Get the value of the event which triggered this one. */ - if (event->prevval != KM_RELEASE) { + if (event->prev_val != KM_RELEASE) { if (kcd->mode == MODE_DRAGGING) { knife_add_cut(kcd); } @@ -4754,7 +4754,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (wait_for_input == false) { /* Avoid copy-paste logic. */ wmEvent event_modal = { - .prevval = KM_NOTHING, + .prev_val = KM_NOTHING, .type = EVT_MODAL_MAP, .val = KNF_MODAL_ADD_CUT, }; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 861c2573de8..80ad35b8d8f 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1051,7 +1051,7 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type) event.val = KM_NOTHING; event.is_repeat = false; event.customdata = op->customdata; - event.customdatafree = true; + event.customdata_free = true; op->customdata = NULL; wm_event_add(win, &event); diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 9b06b2ee5d5..b59d461bab5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -170,10 +170,10 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; ARegion *region = CTX_wm_region(C); - float prevclick_f[2]; - copy_v2_v2(prevclick_f, op->customdata); - const int prevclick[2] = {(int)prevclick_f[0], (int)prevclick_f[1]}; - int len = (int)len_v2v2_int(prevclick, event->mval); + float prev_click_f[2]; + copy_v2_v2(prev_click_f, op->customdata); + const int prev_click[2] = {(int)prev_click_f[0], (int)prev_click_f[1]}; + int len = (int)len_v2v2_int(prev_click, event->mval); len = abs(len); int mask_speed = RNA_int_get(op->ptr, "mask_speed"); int mask_expand_update_it = len / mask_speed; diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 86afe1172a1..f212c7f5747 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1097,8 +1097,8 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case TFM_MODAL_AUTOCONSTRAINT: case TFM_MODAL_AUTOCONSTRAINTPLANE: - if ((t->flag & T_RELEASE_CONFIRM) && (event->prevval == KM_RELEASE) && - event->prevtype == t->launch_event) { + if ((t->flag & T_RELEASE_CONFIRM) && (event->prev_val == KM_RELEASE) && + event->prev_type == t->launch_event) { /* Confirm transform if launch key is released after mouse move. */ t->state = TRANS_CONFIRM; } @@ -1137,13 +1137,13 @@ int transformEvent(TransInfo *t, const wmEvent *event) } break; case TFM_MODAL_PRECISION: - if (event->prevval == KM_PRESS) { + if (event->prev_val == KM_PRESS) { t->modifiers |= MOD_PRECISION; /* Shift is modifier for higher precision transform. */ t->mouse.precision = 1; t->redraw |= TREDRAW_HARD; } - else if (event->prevval == KM_RELEASE) { + else if (event->prev_val == KM_RELEASE) { t->modifiers &= ~MOD_PRECISION; t->mouse.precision = 0; t->redraw |= TREDRAW_HARD; diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 105e533ad22..f4595869baf 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -600,7 +600,7 @@ typedef struct wmTabletData { * - Previous x/y are exceptions: #wmEvent.prev * these are set on mouse motion, see #MOUSEMOVE & track-pad events. * - * - Modal key-map handling sets `prevval` & `prevtype` to `val` & `type`, + * - Modal key-map handling sets `prev_val` & `prev_type` to `val` & `type`, * this allows modal keys-maps to check the original values (needed in some cases). */ typedef struct wmEvent { @@ -632,17 +632,17 @@ typedef struct wmEvent { char is_repeat; /** The previous value of `type`. */ - short prevtype; + short prev_type; /** The previous value of `val`. */ - short prevval; + short prev_val; /** The time when the key is pressed, see #PIL_check_seconds_timer. */ - double prevclicktime; + double prev_click_time; /** The location when the key is pressed (used to enforce drag thresholds). */ int prev_click_xy[2]; /** * The previous value of #wmEvent.xy, * Unlike other previous state variables, this is set on any mouse motion. - * Use `prevclick` for the value at time of pressing. + * Use `prev_click` for the value at time of pressing. */ int prev_xy[2]; @@ -658,7 +658,7 @@ typedef struct wmEvent { /* Custom data. */ /** Custom data type, stylus, 6dof, see wm_event_types.h */ short custom; - short customdatafree; + short customdata_free; int pad2; /** Ascii, unicode, mouse-coords, angles, vectors, NDOF data, drag-drop info. */ void *customdata; diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index 7772c87f71c..b6d759eb95f 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -549,8 +549,8 @@ static int gizmo_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == EVT_MODAL_MAP) { event_modal_val = evil_event->val; - evil_event->type = evil_event->prevtype; - evil_event->val = evil_event->prevval; + evil_event->type = evil_event->prev_type; + evil_event->val = evil_event->prev_val; } int modal_retval = modal_fn(C, gz, event, mtweak->flag); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 85abd8469fe..5687e024975 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -82,7 +82,7 @@ void WM_event_print(const wmEvent *event) const char *prev_val_id = unknown; event_ids_from_type_and_value(event->type, event->val, &type_id, &val_id); - event_ids_from_type_and_value(event->prevtype, event->prevval, &prev_type_id, &prev_val_id); + event_ids_from_type_and_value(event->prev_type, event->prev_val, &prev_type_id, &prev_val_id); printf( "wmEvent type:%d / %s, val:%d / %s,\n" @@ -93,9 +93,9 @@ void WM_event_print(const wmEvent *event) type_id, event->val, val_id, - event->prevtype, + event->prev_type, prev_type_id, - event->prevval, + event->prev_val, prev_val_id, event->shift, event->ctrl, @@ -288,8 +288,8 @@ bool WM_event_is_mouse_drag_or_press(const wmEvent *event) int WM_event_drag_threshold(const struct wmEvent *event) { int drag_threshold; - if (ISMOUSE(event->prevtype)) { - BLI_assert(event->prevtype != MOUSEMOVE); + if (ISMOUSE(event->prev_type)) { + BLI_assert(event->prev_type != MOUSEMOVE); /* Using the previous type is important is we want to check the last pressed/released button, * The `event->type` would include #MOUSEMOVE which is always the case when dragging * and does not help us know which threshold to use. */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 3c03d413214..8d55a684f10 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -161,8 +161,8 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) win->eventstate->prev_xy[1] = event->prev_xy[1] = win->eventstate->xy[1]; } else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) { - win->eventstate->prevval = event->prevval = win->eventstate->val; - win->eventstate->prevtype = event->prevtype = win->eventstate->type; + win->eventstate->prev_val = event->prev_val = win->eventstate->val; + win->eventstate->prev_type = event->prev_type = win->eventstate->type; win->eventstate->val = event->val; win->eventstate->type = event->type; @@ -188,7 +188,7 @@ void wm_event_free(wmEvent *event) #endif if (event->customdata) { - if (event->customdatafree) { + if (event->customdata_free) { /* NOTE: pointer to listbase struct elsewhere. */ if (event->custom == EVT_DATA_DRAGDROP) { ListBase *lb = event->customdata; @@ -2108,8 +2108,8 @@ static wmKeyMapItem *wm_eventmatch_modal_keymap_items(const wmKeyMap *keymap, } struct wmEvent_ModalMapStore { - short prevtype; - short prevval; + short prev_type; + short prev_val; bool dbl_click_disabled; }; @@ -2157,19 +2157,19 @@ static void wm_event_modalkeymap_begin(const bContext *C, } if (event_match != NULL) { - event_backup->prevtype = event->prevtype; - event_backup->prevval = event->prevval; + event_backup->prev_type = event->prev_type; + event_backup->prev_val = event->prev_val; - event->prevtype = event_match->type; - event->prevval = event_match->val; + event->prev_type = event_match->type; + event->prev_val = event_match->val; event->type = EVT_MODAL_MAP; event->val = kmi->propvalue; /* Avoid double-click events even in the case of 'EVT_MODAL_MAP', * since it's possible users configure double-click keymap items * which would break when modal functions expect press/release. */ - if (event->prevtype == KM_DBL_CLICK) { - event->prevtype = KM_PRESS; + if (event->prev_type == KM_DBL_CLICK) { + event->prev_type = KM_PRESS; event_backup->dbl_click_disabled = true; } } @@ -2195,11 +2195,11 @@ static void wm_event_modalkeymap_end(wmEvent *event, const struct wmEvent_ModalMapStore *event_backup) { if (event->type == EVT_MODAL_MAP) { - event->type = event->prevtype; - event->val = event->prevval; + event->type = event->prev_type; + event->val = event->prev_val; - event->prevtype = event_backup->prevtype; - event->prevval = event_backup->prevval; + event->prev_type = event_backup->prev_type; + event->prev_val = event_backup->prev_val; } if (event_backup->dbl_click_disabled) { @@ -3184,7 +3184,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) event->xy[0] = event->prev_click_xy[0]; event->xy[1] = event->prev_click_xy[1]; event->val = KM_CLICK_DRAG; - event->type = event->prevtype; + event->type = event->prev_type; CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG"); @@ -3208,7 +3208,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } } else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) { - /* All events that don't set wmEvent.prevtype must be ignored. */ + /* All events that don't set wmEvent.prev_type must be ignored. */ /* Test for CLICK events. */ if (wm_action_not_handled(action)) { @@ -3226,10 +3226,10 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) win->event_queue_check_drag = false; } - if (event->prevtype == event->type) { + if (event->prev_type == event->type) { if (event->val == KM_RELEASE) { - if (event->prevval == KM_PRESS) { + if (event->prev_val == KM_PRESS) { if (win->event_queue_check_click == true) { if (WM_event_drag_test(event, event->prev_click_xy)) { win->event_queue_check_click = false; @@ -3280,7 +3280,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) /* pass */ } else { - if (ISKEYMODIFIER(event->prevtype)) { + if (ISKEYMODIFIER(event->prev_type)) { win->event_queue_check_click = false; } } @@ -3407,14 +3407,14 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv /* Create customdata, first free existing. */ if (event->customdata) { - if (event->customdatafree) { + if (event->customdata_free) { MEM_freeN(event->customdata); } } event->custom = EVT_DATA_DRAGDROP; event->customdata = &wm->drags; - event->customdatafree = 1; + event->customdata_free = true; /* Clear drop icon. */ screen->do_draw_drag = true; @@ -4653,7 +4653,7 @@ static void attach_ndof_data(wmEvent *event, const GHOST_TEventNDOFMotionData *g event->custom = EVT_DATA_NDOF_MOTION; event->customdata = data; - event->customdatafree = 1; + event->customdata_free = true; } #endif /* WITH_INPUT_NDOF */ @@ -4692,13 +4692,13 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi static bool wm_event_is_double_click(const wmEvent *event) { - if ((event->type == event->prevtype) && (event->prevval == KM_RELEASE) && + if ((event->type == event->prev_type) && (event->prev_val == KM_RELEASE) && (event->val == KM_PRESS)) { if (ISMOUSE(event->type) && WM_event_drag_test(event, event->prev_click_xy)) { /* Pass. */ } else { - if ((PIL_check_seconds_timer() - event->prevclicktime) * 1000 < U.dbl_click_time) { + if ((PIL_check_seconds_timer() - event->prev_click_time) * 1000 < U.dbl_click_time) { return true; } } @@ -4712,13 +4712,13 @@ static bool wm_event_is_double_click(const wmEvent *event) */ static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state) { - event->prevval = event_state->prevval = event_state->val; - event->prevtype = event_state->prevtype = event_state->type; + event->prev_val = event_state->prev_val = event_state->val; + event->prev_type = event_state->prev_type = event_state->type; } static void wm_event_prev_click_set(wmEvent *event, wmEvent *event_state) { - event->prevclicktime = event_state->prevclicktime = PIL_check_seconds_timer(); + event->prev_click_time = event_state->prev_click_time = PIL_check_seconds_timer(); event->prev_click_xy[0] = event_state->prev_click_xy[0] = event_state->xy[0]; event->prev_click_xy[1] = event_state->prev_click_xy[1] = event_state->xy[1]; } @@ -4799,8 +4799,8 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void * If this was done it could leave `win->eventstate` previous and current value * set to the same key press/release state which doesn't make sense. */ - event.prevtype = event.type; - event.prevval = event.val; + event.prev_type = event.type; + event.prev_val = event.val; /* Ensure the event state is correct, any deviation from this may cause bugs. * @@ -4814,12 +4814,12 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void "Non-keyboard/mouse button found in 'win->eventstate->type = %d'", event_state->type); } - if ((event_state->prevtype || event_state->prevval) && /* Ignore cleared event state. */ - !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype) || + if ((event_state->prev_type || event_state->prev_val) && /* Ignore cleared event state. */ + !(ISMOUSE_BUTTON(event_state->prev_type) || ISKEYBOARD(event_state->prev_type) || (event_state->type == EVENT_NONE))) { CLOG_WARN(WM_LOG_HANDLERS, - "Non-keyboard/mouse button found in 'win->eventstate->prevtype = %d'", - event_state->prevtype); + "Non-keyboard/mouse button found in 'win->eventstate->prev_type = %d'", + event_state->prev_type); } #endif @@ -4846,8 +4846,8 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void wmEvent event_other = *win_other->eventstate; /* See comment for this operation on `event` for details. */ - event_other.prevtype = event_other.type; - event_other.prevval = event_other.val; + event_other.prev_type = event_other.type; + event_other.prev_val = event_other.val; copy_v2_v2_int(event_other.xy, event.xy); event_other.type = MOUSEMOVE; @@ -4945,8 +4945,8 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void wmEvent event_other = *win_other->eventstate; /* See comment for this operation on `event` for details. */ - event_other.prevtype = event_other.type; - event_other.prevval = event_other.val; + event_other.prev_type = event_other.type; + event_other.prev_val = event_other.val; copy_v2_v2_int(event_other.xy, event.xy); @@ -5207,7 +5207,7 @@ void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val) .is_repeat = false, .custom = EVT_DATA_XR, .customdata = actiondata, - .customdatafree = 1, + .customdata_free = true, }; wm_event_add(win, &event); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index e8d626512ca..ce0f710f4c2 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1412,7 +1412,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr event.val = KM_RELEASE; event.custom = EVT_DATA_DRAGDROP; event.customdata = &wm->drags; - event.customdatafree = 1; + event.customdata_free = true; wm_event_add(win, &event); From 5297bf318e6f18c9165c9968728e6c9d27f8ae3c Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Thu, 21 Oct 2021 00:56:31 -0400 Subject: [PATCH 1010/1500] Cleanup: Use array as a parameter for event x/y functions This change simplifies the parameter list for these functions and reduces the chance of typos mixing up array indices. Missed in rB69102786047dccdcbaee0df6307a8c3364d28fe0 --- source/blender/editors/interface/interface_handlers.c | 6 +++--- source/blender/editors/interface/interface_intern.h | 2 +- source/blender/editors/interface/interface_query.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 556474f6df4..8ec97c1df10 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2079,8 +2079,8 @@ static bool ui_but_drag_init(bContext *C, drag_info->pushed_state = ui_drag_toggle_but_pushed_state(but); drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect); drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect); - copy_v2_v2_int(drag_info->xy_init, &event->xy[0]); - copy_v2_v2_int(drag_info->xy_last, &event->xy[0]); + copy_v2_v2_int(drag_info->xy_init, event->xy); + copy_v2_v2_int(drag_info->xy_last, event->xy); /* needed for toggle drag on popups */ region_prev = CTX_wm_region(C); @@ -9915,7 +9915,7 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event) { keynav->is_keynav = true; - copy_v2_v2_int(keynav->event_xy, &event->xy[0]); + copy_v2_v2_int(keynav->event_xy, &event->xy); } /** * Return true if key-input isn't blocking mouse-motion, diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index b91dafeb02b..fc4de89944f 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1226,7 +1226,7 @@ bool ui_region_contains_point_px(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; bool ui_region_contains_rect_px(const struct ARegion *region, const rcti *rect_px); -struct ARegion *ui_screen_region_find_mouse_over_ex(struct bScreen *screen, int x, int y); +struct ARegion *ui_screen_region_find_mouse_over_ex(struct bScreen *screen, const int xy[2]) ATTR_NONNULL(1, 2); struct ARegion *ui_screen_region_find_mouse_over(struct bScreen *screen, const struct wmEvent *event); diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 85f829c7f48..bdf93d7c82e 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -799,14 +799,14 @@ bool ui_region_contains_rect_px(const ARegion *region, const rcti *rect_px) * \{ */ /** Check if the cursor is over any popups. */ -ARegion *ui_screen_region_find_mouse_over_ex(bScreen *screen, int x, int y) +ARegion *ui_screen_region_find_mouse_over_ex(bScreen *screen, const int xy[2]) { LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { rcti winrct; ui_region_winrct_get_no_margin(region, &winrct); - if (BLI_rcti_isect_pt(&winrct, x, y)) { + if (BLI_rcti_isect_pt_v(&winrct, xy)) { return region; } } @@ -815,7 +815,7 @@ ARegion *ui_screen_region_find_mouse_over_ex(bScreen *screen, int x, int y) ARegion *ui_screen_region_find_mouse_over(bScreen *screen, const wmEvent *event) { - return ui_screen_region_find_mouse_over_ex(screen, event->xy[0], event->xy[1]); + return ui_screen_region_find_mouse_over_ex(screen, event->xy); } /** \} */ From ec31f3174936a69f85c1115d32672189b8093a47 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Thu, 21 Oct 2021 01:01:31 -0400 Subject: [PATCH 1011/1500] Cleanup: Remove dead code --- source/blender/editors/interface/interface_handlers.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 8ec97c1df10..840bff0eead 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -395,9 +395,6 @@ typedef struct uiHandleButtonData { char *origstr; double value, origvalue, startvalue; float vec[3], origvec[3]; -#if 0 /* UNUSED */ - int togdual, togonly; -#endif ColorBand *coba; /* Tool-tip. */ @@ -4470,10 +4467,6 @@ static bool ui_do_but_ANY_drag_toggle( { if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE && event->val == KM_PRESS && ui_but_is_drag_toggle(but)) { -# if 0 /* UNUSED */ - data->togdual = event->ctrl; - data->togonly = !event->shift; -# endif ui_apply_but(C, but->block, but, data, true); button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); data->dragstartx = event->xy[0]; @@ -4767,10 +4760,6 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons } if (do_activate) { -#if 0 /* UNUSED */ - data->togdual = event->ctrl; - data->togonly = !event->shift; -#endif button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } From 33d6d7c6e33fe0380a6e8cc186dd4eeef5c5153a Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 21 Oct 2021 07:49:12 +0200 Subject: [PATCH 1012/1500] VSE: Add 2D cursor overlay option Since 2D cursor will be used rarely in VSE and it is adding visual noise, it will be hidden by default. Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D12933 --- release/scripts/startup/bl_ui/space_sequencer.py | 1 + source/blender/editors/space_sequencer/space_sequencer.c | 3 ++- source/blender/makesdna/DNA_space_types.h | 1 + source/blender/makesrna/intern/rna_space.c | 5 +++++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 53e40257d9f..8d1b43f91f8 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -291,6 +291,7 @@ class SEQUENCER_PT_preview_overlay(Panel): layout.active = st.show_overlays layout.prop(overlay_settings, "show_image_outline") + layout.prop(overlay_settings, "show_2d_cursor") layout.prop(ed, "show_overlay_frame", text="Frame Overlay") layout.prop(overlay_settings, "show_safe_areas", text="Safe Areas") layout.prop(overlay_settings, "show_metadata", text="Metadata") diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index c876bf7d483..7b763ac8540 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -809,7 +809,8 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) } /* No need to show the cursor for scopes. */ - if (draw_overlay && (is_playing == false) && (sseq->mainb == SEQ_DRAW_IMG_IMBUF)) { + if (draw_overlay && (is_playing == false) && (sseq->mainb == SEQ_DRAW_IMG_IMBUF) && + (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_2D_CURSOR) != 0) { GPU_color_mask(true, true, true, true); GPU_depth_mask(false); GPU_depth_test(GPU_DEPTH_NONE); diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index d0b95d5d1d3..c77a4b7857d 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -583,6 +583,7 @@ typedef struct SequencerPreviewOverlay { /* SequencerPreviewOverlay.flag */ typedef enum eSpaceSeq_SequencerPreviewOverlay_Flag { + SEQ_PREVIEW_SHOW_2D_CURSOR = (1 << 1), SEQ_PREVIEW_SHOW_OUTLINE_SELECTED = (1 << 2), SEQ_PREVIEW_SHOW_SAFE_MARGINS = (1 << 3), SEQ_PREVIEW_SHOW_GPENCIL = (1 << 4), diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 3255bc30d4c..31d4a91f534 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5427,6 +5427,11 @@ static void rna_def_space_sequencer_preview_overlay(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_OUTLINE_SELECTED); RNA_def_property_ui_text(prop, "Image Outline", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_2d_cursor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_2D_CURSOR); + RNA_def_property_ui_text(prop, "2D cursor", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna) From 2a047fadc021539d51f4cb37995f9bbc33d4b9f5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Oct 2021 16:44:03 +1100 Subject: [PATCH 1013/1500] Fix type error in 5297bf318e6f18c9165c9968728e6c9d27f8ae3c --- source/blender/editors/interface/interface_handlers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 840bff0eead..2dbdbc56342 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -9904,7 +9904,7 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event) { keynav->is_keynav = true; - copy_v2_v2_int(keynav->event_xy, &event->xy); + copy_v2_v2_int(keynav->event_xy, event->xy); } /** * Return true if key-input isn't blocking mouse-motion, From 063c4e467da3d926fc6447418e3f04fded9c051f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Oct 2021 16:44:05 +1100 Subject: [PATCH 1014/1500] Cleanup: de-duplicate function to instantiate objects De-duplicates wm_append_loose_data_instantiate_object_base_instance_init and object_base_instance_init. Add BLO_object_instantiate_object_base_instance_init which also adds to a collection since all callers did this. --- source/blender/blenloader/BLO_readfile.h | 10 +++++ source/blender/blenloader/intern/readfile.c | 37 ++++++++++------ .../windowmanager/intern/wm_files_link.c | 44 +++---------------- 3 files changed, 40 insertions(+), 51 deletions(-) diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index d2631840f74..4e1b2c68bd9 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -32,11 +32,13 @@ extern "C" { struct BHead; struct BlendThumbnail; +struct Collection; struct FileData; struct LinkNode; struct ListBase; struct Main; struct MemFile; +struct Object; struct ReportList; struct Scene; struct UserDef; @@ -333,6 +335,14 @@ void BLO_sanitize_experimental_features_userpref_blend(struct UserDef *userdef); struct BlendThumbnail *BLO_thumbnail_from_file(const char *filepath); +void BLO_object_instantiate_object_base_instance_init(struct Main *bmain, + struct Collection *collection, + struct Object *ob, + struct ViewLayer *view_layer, + const struct View3D *v3d, + const int flag, + bool set_active); + /* datafiles (generated theme) */ extern const struct bTheme U_theme_default; extern const struct UserDef U_default; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index bc50a14ac40..ba152bbf2c6 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4471,7 +4471,7 @@ static bool object_in_any_collection(Main *bmain, Object *ob) * Shared operations to perform on the object's base after adding it to the scene. */ static void object_base_instance_init( - Object *ob, bool set_selected, bool set_active, ViewLayer *view_layer, const View3D *v3d) + Object *ob, ViewLayer *view_layer, const View3D *v3d, const int flag, bool set_active) { Base *base = BKE_view_layer_base_find(view_layer, ob); @@ -4479,7 +4479,7 @@ static void object_base_instance_init( base->local_view_bits |= v3d->local_view_uuid; } - if (set_selected) { + if (flag & FILE_AUTOSELECT) { if (base->flag & BASE_SELECTABLE) { base->flag |= BASE_SELECTED; } @@ -4492,6 +4492,22 @@ static void object_base_instance_init( BKE_scene_object_base_flag_sync_from_base(base); } +/** + * Exported for link/append to create objects as well. + */ +void BLO_object_instantiate_object_base_instance_init(Main *bmain, + Collection *collection, + Object *ob, + ViewLayer *view_layer, + const View3D *v3d, + const int flag, + bool set_active) +{ + BKE_collection_object_add(bmain, collection, ob); + + object_base_instance_init(ob, view_layer, v3d, flag, set_active); +} + static void add_loose_objects_to_scene(Main *mainvar, Main *bmain, Scene *scene, @@ -4540,13 +4556,11 @@ static void add_loose_objects_to_scene(Main *mainvar, CLAMP_MIN(ob->id.us, 0); ob->mode = OB_MODE_OBJECT; - BKE_collection_object_add(bmain, active_collection, ob); - - const bool set_selected = (flag & FILE_AUTOSELECT) != 0; /* Do NOT make base active here! screws up GUI stuff, * if you want it do it at the editor level. */ const bool set_active = false; - object_base_instance_init(ob, set_selected, set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, flag, set_active); ob->id.tag &= ~LIB_TAG_INDIRECT; ob->id.flag &= ~LIB_INDIRECT_WEAK_LINK; @@ -4602,13 +4616,11 @@ static void add_loose_object_data_to_scene(Main *mainvar, id_us_plus(id); BKE_object_materials_test(bmain, ob, ob->data); - BKE_collection_object_add(bmain, active_collection, ob); - - const bool set_selected = (flag & FILE_AUTOSELECT) != 0; /* Do NOT make base active here! screws up GUI stuff, * if you want it do it at the editor level. */ bool set_active = false; - object_base_instance_init(ob, set_selected, set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, flag, set_active); copy_v3_v3(ob->loc, scene->cursor.location); } @@ -4641,13 +4653,12 @@ static void add_collections_to_scene(Main *mainvar, ob->type = OB_EMPTY; ob->empty_drawsize = U.collection_instance_empty_size; - BKE_collection_object_add(bmain, active_collection, ob); - const bool set_selected = (flag & FILE_AUTOSELECT) != 0; /* TODO: why is it OK to make this active here but not in other situations? * See other callers of #object_base_instance_init */ const bool set_active = set_selected; - object_base_instance_init(ob, set_selected, set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, flag, set_active); DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index e698702316e..d193c5663f0 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -312,31 +312,6 @@ static bool object_in_any_collection(Main *bmain, Object *ob) return false; } -/** - * Shared operations to perform on the object's base after adding it to the scene. - */ -static void wm_append_loose_data_instantiate_object_base_instance_init( - Object *ob, bool set_selected, bool set_active, ViewLayer *view_layer, const View3D *v3d) -{ - Base *base = BKE_view_layer_base_find(view_layer, ob); - - if (v3d != NULL) { - base->local_view_bits |= v3d->local_view_uuid; - } - - if (set_selected) { - if (base->flag & BASE_SELECTABLE) { - base->flag |= BASE_SELECTED; - } - } - - if (set_active) { - view_layer->basact = base; - } - - BKE_scene_object_base_flag_sync_from_base(base); -} - static ID *wm_append_loose_data_instantiate_process_check(WMLinkAppendDataItem *item) { /* We consider that if we either kept it linked, or re-used already local data, instantiation @@ -402,7 +377,6 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, Collection *active_collection = NULL; const bool do_obdata = (lapp_data->flag & BLO_LIBLINK_OBDATA_INSTANCE) != 0; - const bool object_set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0; /* Do NOT make base active here! screws up GUI stuff, * if you want it do it at the editor level. */ const bool object_set_active = false; @@ -484,14 +458,12 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, ob->type = OB_EMPTY; ob->empty_drawsize = U.collection_instance_empty_size; - BKE_collection_object_add(bmain, active_collection, ob); - const bool set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0; /* TODO: why is it OK to make this active here but not in other situations? * See other callers of #object_base_instance_init */ const bool set_active = set_selected; - wm_append_loose_data_instantiate_object_base_instance_init( - ob, set_selected, set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, lapp_data->flag, set_active); /* Assign the collection. */ ob->instance_collection = collection; @@ -538,10 +510,8 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, CLAMP_MIN(ob->id.us, 0); ob->mode = OB_MODE_OBJECT; - BKE_collection_object_add(bmain, active_collection, ob); - - wm_append_loose_data_instantiate_object_base_instance_init( - ob, object_set_selected, object_set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, lapp_data->flag, object_set_active); } if (!do_obdata) { @@ -572,10 +542,8 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, id_us_plus(id); BKE_object_materials_test(bmain, ob, ob->data); - BKE_collection_object_add(bmain, active_collection, ob); - - wm_append_loose_data_instantiate_object_base_instance_init( - ob, object_set_selected, object_set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, lapp_data->flag, object_set_active); copy_v3_v3(ob->loc, scene->cursor.location); From 3a2b7f35f4693dd40cd9457a0e6cd43b760ec558 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Oct 2021 16:44:06 +1100 Subject: [PATCH 1015/1500] LibLink: ensure objects are selectable when "Select" is enabled Appended objects could be hidden, making any further operations potentially skip the newly added objects. Now FILE_AUTOSELECT asserts when newly added options aren't selectable. --- source/blender/blenloader/intern/readfile.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index ba152bbf2c6..0b69395b4f8 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4480,6 +4480,8 @@ static void object_base_instance_init( } if (flag & FILE_AUTOSELECT) { + /* All objects that use #FILE_AUTOSELECT must be selectable (unless linking data). */ + BLI_assert((base->flag & BASE_SELECTABLE) || (flag & FILE_LINK)); if (base->flag & BASE_SELECTABLE) { base->flag |= BASE_SELECTED; } @@ -4503,6 +4505,13 @@ void BLO_object_instantiate_object_base_instance_init(Main *bmain, const int flag, bool set_active) { + /* Auto-select and appending. */ + if ((flag & FILE_AUTOSELECT) && ((flag & FILE_LINK) == 0)) { + /* While in general the object should not be manipulated, + * when the user requests the object to be selected, ensure it's visible and selectable. */ + ob->visibility_flag &= ~(OB_HIDE_VIEWPORT | OB_HIDE_SELECT); + } + BKE_collection_object_add(bmain, collection, ob); object_base_instance_init(ob, view_layer, v3d, flag, set_active); From d71c423c28d82b00b0c67349fe0b0e6e098bd3d9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Oct 2021 16:44:07 +1100 Subject: [PATCH 1016/1500] Cleanup: Use array as a parameter for event x/y functions This change simplifies the parameter list for these functions and reduces the chance of typos mixing up array indices. Missed in rB69102786047dccdcbaee0df6307a8c3364d28fe0. --- source/blender/editors/interface/interface_handlers.c | 4 ++-- source/blender/editors/interface/interface_panel.c | 2 +- source/blender/editors/screen/screen_ops.c | 10 +++++----- .../blender/windowmanager/gizmo/intern/wm_gizmo_map.c | 2 +- source/blender/windowmanager/intern/wm_event_system.c | 4 ++-- source/blender/windowmanager/intern/wm_stereo.c | 2 +- source/blender/windowmanager/wm.h | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 2dbdbc56342..205c25d562a 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -9451,7 +9451,7 @@ static int ui_list_activate_hovered_row(bContext *C, } } - const int *mouse_xy = ISTWEAK(event->type) ? &event->prev_click_xy[0] : &event->xy[0]; + const int *mouse_xy = ISTWEAK(event->type) ? event->prev_click_xy : event->xy; uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy); if (listrow) { wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype; @@ -9478,7 +9478,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, const wmEvent *event) { /* On a tweak event, uses the coordinates from where tweaking was started. */ - const int *mouse_xy = ISTWEAK(event->type) ? &event->prev_click_xy[0] : &event->xy[0]; + const int *mouse_xy = ISTWEAK(event->type) ? event->prev_click_xy : event->xy; const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, NULL, NULL); if (list->dyn_data->custom_drag_optype) { diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 43536b23353..3cc9a743a17 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -2041,7 +2041,7 @@ static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, voi switch (event->type) { case MOUSEMOVE: - ui_panel_drag_collapse(C, dragcol_data, &event->xy[0]); + ui_panel_drag_collapse(C, dragcol_data, event->xy); retval = WM_UI_HANDLER_BREAK; break; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 80ad35b8d8f..e5fbcbb0b6c 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1060,7 +1060,7 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type) static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) { bScreen *screen = CTX_wm_screen(C); - AZone *az = screen_actionzone_find_xy(screen, &event->xy[0]); + AZone *az = screen_actionzone_find_xy(screen, event->xy); /* Quick escape - Scroll azones only hide/unhide the scroll-bars, * they have their own handling. */ @@ -3632,7 +3632,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent 0, &ptr); /* store initial mouse cursor position. */ - RNA_int_set_array(&ptr, "cursor", &event->xy[0]); + RNA_int_set_array(&ptr, "cursor", event->xy); RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V); /* Horizontal Split */ @@ -3645,7 +3645,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent 0, &ptr); /* store initial mouse cursor position. */ - RNA_int_set_array(&ptr, "cursor", &event->xy[0]); + RNA_int_set_array(&ptr, "cursor", event->xy); RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H); if (sa1 && sa2) { @@ -3662,7 +3662,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent WM_OP_INVOKE_DEFAULT, 0, &ptr); - RNA_int_set_array(&ptr, "cursor", &event->xy[0]); + RNA_int_set_array(&ptr, "cursor", event->xy); } /* Swap just needs two areas. */ @@ -3675,7 +3675,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_int_set_array(&ptr, "cursor", &event->xy[0]); + RNA_int_set_array(&ptr, "cursor", event->xy); } UI_popup_menu_end(C, pup); diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 6e1da3e6716..18e1a8420a6 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -1073,7 +1073,7 @@ void wm_gizmomap_modal_set( if ((gz->flag & WM_GIZMO_MOVE_CURSOR) && (event->tablet.is_motion_absolute == false)) { WM_cursor_grab_enable(win, WM_CURSOR_WRAP_XY, true, NULL); - copy_v2_v2_int(gzmap->gzmap_context.event_xy, &event->xy[0]); + copy_v2_v2_int(gzmap->gzmap_context.event_xy, event->xy); gzmap->gzmap_context.event_grabcursor = win->grabcursor; } else { diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 8d55a684f10..3c0853cf926 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -2812,7 +2812,7 @@ static int wm_handlers_do_gizmo_handler(bContext *C, * noticeable for the node editor - where dragging on a node should move it, see: T73212. * note we still allow for starting the gizmo drag outside, then travel 'inside' the node. */ if (region->type->clip_gizmo_events_by_ui) { - if (UI_region_block_find_mouse_over(region, &event->xy[0], true)) { + if (UI_region_block_find_mouse_over(region, event->xy, true)) { if (gz != NULL && event->type != EVT_GIZMO_UPDATE) { if (restore_highlight_unless_activated == false) { WM_tooltip_clear(C, CTX_wm_window(C)); @@ -4829,7 +4829,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void GHOST_TEventCursorData *cd = customdata; copy_v2_v2_int(event.xy, &cd->x); - wm_stereo3d_mouse_offset_apply(win, &event.xy[0]); + wm_stereo3d_mouse_offset_apply(win, event.xy); wm_tablet_data_from_ghost(&cd->tablet, &event.tablet); event.type = MOUSEMOVE; diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c index a7cdc0602bc..16a17484f4f 100644 --- a/source/blender/windowmanager/intern/wm_stereo.c +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -181,7 +181,7 @@ bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) * If needed, adjust \a r_mouse_xy * so that drawn cursor and handled mouse position are matching visually. */ -void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy) +void wm_stereo3d_mouse_offset_apply(wmWindow *win, int r_mouse_xy[2]) { if (!WM_stereo3d_enabled(win, false)) { return; diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index af696ddd8f9..cfef70b7dcc 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -86,7 +86,7 @@ void WM_OT_splash_about(wmOperatorType *ot); void wm_stereo3d_draw_sidebyside(wmWindow *win, int view); void wm_stereo3d_draw_topbottom(wmWindow *win, int view); -void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy); +void wm_stereo3d_mouse_offset_apply(wmWindow *win, int r_mouse_xy[2]); int wm_stereo3d_set_exec(bContext *C, wmOperator *op); int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *event); void wm_stereo3d_set_draw(bContext *C, wmOperator *op); From 5faa333e788c2e752eded396991757d537b1df39 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Oct 2021 17:01:15 +1100 Subject: [PATCH 1017/1500] Cleanup: match cursor hide option with naming for the 3D viewport --- release/scripts/startup/bl_ui/space_sequencer.py | 2 +- source/blender/makesrna/intern/rna_space.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 8d1b43f91f8..9b96cef9de4 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -291,7 +291,7 @@ class SEQUENCER_PT_preview_overlay(Panel): layout.active = st.show_overlays layout.prop(overlay_settings, "show_image_outline") - layout.prop(overlay_settings, "show_2d_cursor") + layout.prop(overlay_settings, "show_cursor") layout.prop(ed, "show_overlay_frame", text="Frame Overlay") layout.prop(overlay_settings, "show_safe_areas", text="Safe Areas") layout.prop(overlay_settings, "show_metadata", text="Metadata") diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 31d4a91f534..552aa6ece6c 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5428,7 +5428,7 @@ static void rna_def_space_sequencer_preview_overlay(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Image Outline", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_2d_cursor", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "show_cursor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_2D_CURSOR); RNA_def_property_ui_text(prop, "2D cursor", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); From cfbac9032b0009f760e93f9e70813ac6693b26cb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Oct 2021 17:10:19 +1100 Subject: [PATCH 1018/1500] Docs: note why BLI_string_join_arrayN needs to nil terminate --- source/blender/blenlib/intern/string_utils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c index 798eb60f64d..b62b9c3bc7a 100644 --- a/source/blender/blenlib/intern/string_utils.c +++ b/source/blender/blenlib/intern/string_utils.c @@ -469,6 +469,7 @@ char *BLI_string_join_arrayN(const char *strings[], uint strings_len) for (uint i = 0; i < strings_len; i++) { c += BLI_strcpy_rlen(c, strings[i]); } + /* Only needed when `strings_len == 0`. */ *c = '\0'; return result; } From f0f119886701efd9d2038b3bf8caa401ab2c87c5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Oct 2021 17:14:33 +1100 Subject: [PATCH 1019/1500] Cleanup: unused functuion warnings --- source/blender/editors/space_view3d/view3d_cursor_snap.c | 2 +- source/blender/editors/space_view3d/view3d_placement.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 1b513c0a9a7..ff5c5c6a946 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -94,7 +94,7 @@ typedef struct SnapCursorDataIntern { bool is_initiated; } SnapCursorDataIntern; -static void v3d_cursor_snap_state_init(V3DSnapCursorState *state) +static void UNUSED_FUNCTION(v3d_cursor_snap_state_init)(V3DSnapCursorState *state) { state->prevpoint = NULL; state->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index f1fd5732708..79377e5599c 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -222,7 +222,8 @@ static int dot_v3_array_find_max_index(const float dirs[][3], return index_found; } -static wmGizmoGroup *idp_gizmogroup_from_region(ARegion *region) +static UNUSED_FUNCTION_WITH_RETURN_TYPE(wmGizmoGroup *, + idp_gizmogroup_from_region)(ARegion *region) { wmGizmoMap *gzmap = region->gizmo_map; return gzmap ? WM_gizmomap_group_find(gzmap, view3d_gzgt_placement_id) : NULL; From 98e1c6e9358a54001a2c76a86d7ca31283175547 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Oct 2021 18:27:46 +1100 Subject: [PATCH 1020/1500] Cleanup: clang-format --- source/blender/editors/interface/interface_intern.h | 3 ++- .../nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index fc4de89944f..3e610e62b5a 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1226,7 +1226,8 @@ bool ui_region_contains_point_px(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; bool ui_region_contains_rect_px(const struct ARegion *region, const rcti *rect_px); -struct ARegion *ui_screen_region_find_mouse_over_ex(struct bScreen *screen, const int xy[2]) ATTR_NONNULL(1, 2); +struct ARegion *ui_screen_region_find_mouse_over_ex(struct bScreen *screen, const int xy[2]) + ATTR_NONNULL(1, 2); struct ARegion *ui_screen_region_find_mouse_over(struct bScreen *screen, const struct wmEvent *event); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc index 820d52e2259..2936c150376 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc @@ -286,8 +286,6 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, }); } - - static void geo_node_curve_to_points_exec(GeoNodeExecParams params) { NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage; From 8d2cabcb97c5b49a19115f47a6f364d02af03f87 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 21 Oct 2021 11:28:12 +0200 Subject: [PATCH 1021/1500] Cleanup: VSE code docstring --- .../sequencer/intern/strip_transform.c | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index c70f8d646af..becf44a7a8e 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -468,14 +468,6 @@ void SEQ_image_transform_origin_offset_pixelspace_get(const Scene *scene, mul_v2_v2(r_origin, mirror); } -/** - * Get strip transform origin offset from image center - * - * \param scene: Scene in which strips are located - * \param seq: Sequence to calculate image transform origin - * \param apply_rotation: Apply sequence rotation transform to the quad - * \param r_origin: return value - */ static void seq_image_transform_quad_get_ex(const Scene *scene, const Sequence *seq, bool apply_rotation, @@ -527,6 +519,14 @@ static void seq_image_transform_quad_get_ex(const Scene *scene, } } +/** + * Get 4 corner points of strip image, optionally without rotation component applied + * + * \param scene: Scene in which strips are located + * \param seq: Sequence to calculate image transform origin + * \param apply_rotation: Apply sequence rotation transform to the quad + * \param r_quad: array of 4 2D vectors + */ void SEQ_image_transform_quad_get(const Scene *scene, const Sequence *seq, bool apply_rotation, @@ -535,6 +535,13 @@ void SEQ_image_transform_quad_get(const Scene *scene, seq_image_transform_quad_get_ex(scene, seq, apply_rotation, r_quad); } +/** + * Get 4 corner points of strip image. + * + * \param scene: Scene in which strips are located + * \param seq: Sequence to calculate image transform origin + * \param r_quad: array of 4 2D vectors + */ void SEQ_image_transform_final_quad_get(const Scene *scene, const Sequence *seq, float r_quad[4][2]) From 2eb2e861a3f03fa471c5382e5e168acfacf69683 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 21 Oct 2021 11:58:06 +0200 Subject: [PATCH 1022/1500] Install_deps: Rename `_VERSION_MAX` to `_VERSION_MEX` variables. We define the minimum exclusive number for our supported dependencies versions, and not the maximum inclusive number. Thanks to @sybren for raising this point and finding the 'mex' math term. --- build_files/build_environment/install_deps.sh | 140 +++++++++--------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index faabde2af6e..f910d8c12d9 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -371,18 +371,18 @@ NO_BUILD=false NO_CONFIRM=false USE_CXX11=true -# Note about versions: Min is inclusive, Max is exclusive (i.e. XXX_VERSION_MIN <= ACTUAL_VERSION < XXX_VERSION_MAX) +# Note about versions: Min is inclusive, Mex is 'minimum exclusive' (i.e. XXX_VERSION_MIN <= ACTUAL_VERSION < XXX_VERSION_MEX) # XXX_VERSION is officially supported/used version in official builds. # XXX_VERSION_SHORT is used for various things, like preferred version (when distribution provides several of them), # and to name shortcuts to built libraries' installation directories... CLANG_FORMAT_VERSION_MIN="6.0" -CLANG_FORMAT_VERSION_MAX="10.0" +CLANG_FORMAT_VERSION_MEX="10.0" PYTHON_VERSION="3.9.2" PYTHON_VERSION_SHORT="3.9" PYTHON_VERSION_MIN="3.7" -PYTHON_VERSION_MAX="3.11" +PYTHON_VERSION_MEX="3.11" PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT PYTHON_FORCE_BUILD=false PYTHON_FORCE_REBUILD=false @@ -391,42 +391,42 @@ PYTHON_SKIP=false # Additional Python modules. PYTHON_IDNA_VERSION="2.9" PYTHON_IDNA_VERSION_MIN="2.0" -PYTHON_IDNA_VERSION_MAX="3.0" +PYTHON_IDNA_VERSION_MEX="3.0" PYTHON_IDNA_NAME="idna" PYTHON_CHARDET_VERSION="3.0.4" PYTHON_CHARDET_VERSION_MIN="3.0" -PYTHON_CHARDET_VERSION_MAX="5.0" +PYTHON_CHARDET_VERSION_MEX="5.0" PYTHON_CHARDET_NAME="chardet" PYTHON_URLLIB3_VERSION="1.25.9" PYTHON_URLLIB3_VERSION_MIN="1.0" -PYTHON_URLLIB3_VERSION_MAX="2.0" +PYTHON_URLLIB3_VERSION_MEX="2.0" PYTHON_URLLIB3_NAME="urllib3" PYTHON_CERTIFI_VERSION="2020.4.5.2" PYTHON_CERTIFI_VERSION_MIN="2020.0" -PYTHON_CERTIFI_VERSION_MAX="2021.0" +PYTHON_CERTIFI_VERSION_MEX="2021.0" PYTHON_CERTIFI_NAME="certifi" PYTHON_REQUESTS_VERSION="2.23.0" PYTHON_REQUESTS_VERSION_MIN="2.0" -PYTHON_REQUESTS_VERSION_MAX="3.0" +PYTHON_REQUESTS_VERSION_MEX="3.0" PYTHON_REQUESTS_NAME="requests" PYTHON_NUMPY_VERSION="1.19.5" PYTHON_NUMPY_VERSION_MIN="1.14" -PYTHON_NUMPY_VERSION_MAX="2.0" +PYTHON_NUMPY_VERSION_MEX="2.0" PYTHON_NUMPY_NAME="numpy" # As package-ready parameters (only used with distro packages). PYTHON_MODULES_PACKAGES=( - "$PYTHON_IDNA_NAME $PYTHON_IDNA_VERSION_MIN $PYTHON_IDNA_VERSION_MAX" - "$PYTHON_CHARDET_NAME $PYTHON_CHARDET_VERSION_MIN $PYTHON_CHARDET_VERSION_MAX" - "$PYTHON_URLLIB3_NAME $PYTHON_URLLIB3_VERSION_MIN $PYTHON_URLLIB3_VERSION_MAX" - "$PYTHON_CERTIFI_NAME $PYTHON_CERTIFI_VERSION_MIN $PYTHON_CERTIFI_VERSION_MAX" - "$PYTHON_REQUESTS_NAME $PYTHON_REQUESTS_VERSION_MIN $PYTHON_REQUESTS_VERSION_MAX" - "$PYTHON_NUMPY_NAME $PYTHON_NUMPY_VERSION_MIN $PYTHON_NUMPY_VERSION_MAX" + "$PYTHON_IDNA_NAME $PYTHON_IDNA_VERSION_MIN $PYTHON_IDNA_VERSION_MEX" + "$PYTHON_CHARDET_NAME $PYTHON_CHARDET_VERSION_MIN $PYTHON_CHARDET_VERSION_MEX" + "$PYTHON_URLLIB3_NAME $PYTHON_URLLIB3_VERSION_MIN $PYTHON_URLLIB3_VERSION_MEX" + "$PYTHON_CERTIFI_NAME $PYTHON_CERTIFI_VERSION_MIN $PYTHON_CERTIFI_VERSION_MEX" + "$PYTHON_REQUESTS_NAME $PYTHON_REQUESTS_VERSION_MIN $PYTHON_REQUESTS_VERSION_MEX" + "$PYTHON_NUMPY_NAME $PYTHON_NUMPY_VERSION_MIN $PYTHON_NUMPY_VERSION_MEX" ) # As pip-ready parameters (only used when building python). @@ -443,7 +443,7 @@ PYTHON_MODULES_PIP=( BOOST_VERSION="1.73.0" BOOST_VERSION_SHORT="1.73" BOOST_VERSION_MIN="1.49" -BOOST_VERSION_MAX="2.0" +BOOST_VERSION_MEX="2.0" BOOST_FORCE_BUILD=false BOOST_FORCE_REBUILD=false BOOST_SKIP=false @@ -452,7 +452,7 @@ TBB_VERSION="2020" TBB_VERSION_SHORT="2020" TBB_VERSION_UPDATE="_U2" # Used for source packages... TBB_VERSION_MIN="2018" -TBB_VERSION_MAX="2022" +TBB_VERSION_MEX="2022" TBB_FORCE_BUILD=false TBB_FORCE_REBUILD=false TBB_SKIP=false @@ -460,7 +460,7 @@ TBB_SKIP=false OCIO_VERSION="2.0.0" OCIO_VERSION_SHORT="2.0" OCIO_VERSION_MIN="2.0" -OCIO_VERSION_MAX="3.0" +OCIO_VERSION_MEX="3.0" OCIO_FORCE_BUILD=false OCIO_FORCE_REBUILD=false OCIO_SKIP=false @@ -468,7 +468,7 @@ OCIO_SKIP=false OPENEXR_VERSION="2.5.5" OPENEXR_VERSION_SHORT="2.5" OPENEXR_VERSION_MIN="2.4" -OPENEXR_VERSION_MAX="3.0" +OPENEXR_VERSION_MEX="3.0" OPENEXR_FORCE_BUILD=false OPENEXR_FORCE_REBUILD=false OPENEXR_SKIP=false @@ -477,7 +477,7 @@ _with_built_openexr=false OIIO_VERSION="2.2.15.1" OIIO_VERSION_SHORT="2.2" OIIO_VERSION_MIN="2.1.12" -OIIO_VERSION_MAX="2.3.0" +OIIO_VERSION_MEX="2.3.0" OIIO_FORCE_BUILD=false OIIO_FORCE_REBUILD=false OIIO_SKIP=false @@ -485,7 +485,7 @@ OIIO_SKIP=false LLVM_VERSION="12.0.0" LLVM_VERSION_SHORT="12.0" LLVM_VERSION_MIN="11.0" -LLVM_VERSION_MAX="13.0" +LLVM_VERSION_MEX="13.0" LLVM_VERSION_FOUND="" LLVM_FORCE_BUILD=false LLVM_FORCE_REBUILD=false @@ -495,7 +495,7 @@ LLVM_SKIP=false OSL_VERSION="1.11.14.1" OSL_VERSION_SHORT="1.11" OSL_VERSION_MIN="1.11" -OSL_VERSION_MAX="2.0" +OSL_VERSION_MEX="2.0" OSL_FORCE_BUILD=false OSL_FORCE_REBUILD=false OSL_SKIP=false @@ -504,7 +504,7 @@ OSL_SKIP=false OSD_VERSION="3.4.3" OSD_VERSION_SHORT="3.4" OSD_VERSION_MIN="3.4" -OSD_VERSION_MAX="4.0" +OSD_VERSION_MEX="4.0" OSD_FORCE_BUILD=false OSD_FORCE_REBUILD=false OSD_SKIP=false @@ -515,7 +515,7 @@ OPENVDB_BLOSC_VERSION="1.5.0" OPENVDB_VERSION="8.0.1" OPENVDB_VERSION_SHORT="8.0" OPENVDB_VERSION_MIN="8.0" -OPENVDB_VERSION_MAX="8.1" +OPENVDB_VERSION_MEX="8.1" OPENVDB_FORCE_BUILD=false OPENVDB_FORCE_REBUILD=false OPENVDB_SKIP=false @@ -524,7 +524,7 @@ OPENVDB_SKIP=false ALEMBIC_VERSION="1.7.16" ALEMBIC_VERSION_SHORT="1.7" ALEMBIC_VERSION_MIN="1.7" -ALEMBIC_VERSION_MAX="2.0" +ALEMBIC_VERSION_MEX="2.0" ALEMBIC_FORCE_BUILD=false ALEMBIC_FORCE_REBUILD=false ALEMBIC_SKIP=false @@ -532,7 +532,7 @@ ALEMBIC_SKIP=false USD_VERSION="21.02" USD_VERSION_SHORT="21.02" USD_VERSION_MIN="20.05" -USD_VERSION_MAX="22.00" +USD_VERSION_MEX="22.00" USD_FORCE_BUILD=false USD_FORCE_REBUILD=false USD_SKIP=false @@ -540,7 +540,7 @@ USD_SKIP=false OPENCOLLADA_VERSION="1.6.68" OPENCOLLADA_VERSION_SHORT="1.6" OPENCOLLADA_VERSION_MIN="1.6.68" -OPENCOLLADA_VERSION_MAX="1.7" +OPENCOLLADA_VERSION_MEX="1.7" OPENCOLLADA_FORCE_BUILD=false OPENCOLLADA_FORCE_REBUILD=false OPENCOLLADA_SKIP=false @@ -548,7 +548,7 @@ OPENCOLLADA_SKIP=false EMBREE_VERSION="3.10.0" EMBREE_VERSION_SHORT="3.10" EMBREE_VERSION_MIN="3.10" -EMBREE_VERSION_MAX="4.0" +EMBREE_VERSION_MEX="4.0" EMBREE_FORCE_BUILD=false EMBREE_FORCE_REBUILD=false EMBREE_SKIP=false @@ -556,7 +556,7 @@ EMBREE_SKIP=false OIDN_VERSION="1.4.1" OIDN_VERSION_SHORT="1.4" OIDN_VERSION_MIN="1.4.0" -OIDN_VERSION_MAX="1.5" +OIDN_VERSION_MEX="1.5" OIDN_FORCE_BUILD=false OIDN_FORCE_REBUILD=false OIDN_SKIP=false @@ -566,7 +566,7 @@ ISPC_VERSION="1.16.0" FFMPEG_VERSION="4.4" FFMPEG_VERSION_SHORT="4.4" FFMPEG_VERSION_MIN="3.0" -FFMPEG_VERSION_MAX="5.0" +FFMPEG_VERSION_MEX="5.0" FFMPEG_FORCE_BUILD=false FFMPEG_FORCE_REBUILD=false FFMPEG_SKIP=false @@ -575,7 +575,7 @@ _ffmpeg_list_sep=";" XR_OPENXR_VERSION="1.0.17" XR_OPENXR_VERSION_SHORT="1.0" XR_OPENXR_VERSION_MIN="1.0.8" -XR_OPENXR_VERSION_MAX="2.0" +XR_OPENXR_VERSION_MEX="2.0" XR_OPENXR_FORCE_BUILD=false XR_OPENXR_FORCE_REBUILD=false XR_OPENXR_SKIP=false @@ -4028,7 +4028,7 @@ install_DEB() { INFO "Forced Python building, as requested..." _do_compile_python=true else - check_package_version_ge_lt_DEB python3-dev $PYTHON_VERSION_MIN $PYTHON_VERSION_MAX + check_package_version_ge_lt_DEB python3-dev $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX if [ $? -eq 0 ]; then PYTHON_VERSION_INSTALLED=$(echo `get_package_version_DEB python3-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') @@ -4041,8 +4041,8 @@ install_DEB() { module=($module) package="python3-${module[0]}" package_vmin=${module[1]} - package_vmax=${module[2]} - check_package_version_ge_lt_DEB "$package" $package_vmin $package_vmax + package_vmex=${module[2]} + check_package_version_ge_lt_DEB "$package" $package_vmin $package_vmex if [ $? -eq 0 ]; then install_packages_DEB "$package" else @@ -4068,7 +4068,7 @@ install_DEB() { INFO "Forced Boost building, as requested..." compile_Boost else - check_package_version_ge_lt_DEB libboost-dev $BOOST_VERSION_MIN $BOOST_VERSION_MAX + check_package_version_ge_lt_DEB libboost-dev $BOOST_VERSION_MIN $BOOST_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libboost-dev @@ -4089,7 +4089,7 @@ install_DEB() { INFO "Forced TBB building, as requested..." compile_TBB else - check_package_version_ge_lt_DEB libtbb-dev $TBB_VERSION_MIN $TBB_VERSION_MAX + check_package_version_ge_lt_DEB libtbb-dev $TBB_VERSION_MIN $TBB_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libtbb-dev clean_TBB @@ -4106,7 +4106,7 @@ install_DEB() { INFO "Forced OpenColorIO building, as requested..." compile_OCIO else - check_package_version_ge_lt_DEB libopencolorio-dev $OCIO_VERSION_MIN $OCIO_VERSION_MAX + check_package_version_ge_lt_DEB libopencolorio-dev $OCIO_VERSION_MIN $OCIO_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libopencolorio-dev clean_OCIO @@ -4123,7 +4123,7 @@ install_DEB() { INFO "Forced ILMBase/OpenEXR building, as requested..." compile_OPENEXR else - check_package_version_ge_lt_DEB libopenexr-dev $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MAX + check_package_version_ge_lt_DEB libopenexr-dev $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libopenexr-dev OPENEXR_VERSION=`get_package_version_DEB libopenexr-dev` @@ -4144,7 +4144,7 @@ install_DEB() { INFO "Forced OpenImageIO building, as requested..." compile_OIIO else - check_package_version_ge_lt_DEB libopenimageio-dev $OIIO_VERSION_MIN $OIIO_VERSION_MAX + check_package_version_ge_lt_DEB libopenimageio-dev $OIIO_VERSION_MIN $OIIO_VERSION_MEX if [ $? -eq 0 -a "$_with_built_openexr" = false ]; then install_packages_DEB libopenimageio-dev openimageio-tools clean_OIIO @@ -4164,7 +4164,7 @@ install_DEB() { INFO "Forced LLVM building, as requested..." _do_compile_llvm=true else - check_package_version_ge_lt_DEB llvm-dev $LLVM_VERSION_MIN $LLVM_VERSION_MAX + check_package_version_ge_lt_DEB llvm-dev $LLVM_VERSION_MIN $LLVM_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB llvm-dev clang libclang-dev have_llvm=true @@ -4195,7 +4195,7 @@ install_DEB() { INFO "Forced OpenShadingLanguage building, as requested..." _do_compile_osl=true else - check_package_version_ge_lt_DEB libopenshadinglanguage-dev $OSL_VERSION_MIN $OSL_VERSION_MAX + check_package_version_ge_lt_DEB libopenshadinglanguage-dev $OSL_VERSION_MIN $OSL_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libopenshadinglanguage-dev clean_OSL @@ -4233,7 +4233,7 @@ install_DEB() { INFO "Forced OpenVDB building, as requested..." compile_OPENVDB else - check_package_version_ge_lt_DEB libopenvdb-dev $OPENVDB_VERSION_MIN $OPENVDB_VERSION_MAX + check_package_version_ge_lt_DEB libopenvdb-dev $OPENVDB_VERSION_MIN $OPENVDB_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libopenvdb-dev libblosc-dev clean_OPENVDB @@ -4295,7 +4295,7 @@ install_DEB() { _do_compile_embree=true else # There is a package, but it does not provide everything that Blender needs... - #~ check_package_version_ge_lt_DEB libembree-dev $EMBREE_VERSION_MIN $EMBREE_VERSION_MAX + #~ check_package_version_ge_lt_DEB libembree-dev $EMBREE_VERSION_MIN $EMBREE_VERSION_MEX #~ if [ $? -eq 0 ]; then #~ install_packages_DEB libembree-dev #~ clean_Embree @@ -4337,7 +4337,7 @@ install_DEB() { # XXX Debian Testing / Ubuntu 16.04 finally includes FFmpeg, so check as usual check_package_DEB ffmpeg if [ $? -eq 0 ]; then - check_package_version_ge_lt_DEB ffmpeg $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MAX + check_package_version_ge_lt_DEB ffmpeg $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libavdevice-dev clean_FFmpeg @@ -4671,7 +4671,7 @@ install_RPM() { INFO "Forced Python building, as requested..." _do_compile_python=true else - check_package_version_ge_lt_RPM python3-devel $PYTHON_VERSION_MIN $PYTHON_VERSION_MAX + check_package_version_ge_lt_RPM python3-devel $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX if [ $? -eq 0 ]; then PYTHON_VERSION_INSTALLED=$(echo `get_package_version_RPM python3-devel` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') @@ -4683,8 +4683,8 @@ install_RPM() { module=($module) package="python3-${module[0]}" package_vmin=${module[1]} - package_vmax=${module[2]} - check_package_version_ge_lt_RPM "$package" $package_vmin $package_vmax + package_vmex=${module[2]} + check_package_version_ge_lt_RPM "$package" $package_vmin $package_vmex if [ $? -eq 0 ]; then install_packages_RPM "$package" else @@ -4711,7 +4711,7 @@ install_RPM() { INFO "Forced Boost building, as requested..." _do_compile_boost=true else - check_package_version_ge_lt_RPM boost-devel $BOOST_VERSION_MIN $BOOST_VERSION_MAX + check_package_version_ge_lt_RPM boost-devel $BOOST_VERSION_MIN $BOOST_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM boost-devel clean_Boost @@ -4738,7 +4738,7 @@ install_RPM() { INFO "Forced TBB building, as requested..." compile_TBB else - check_package_version_ge_lt_RPM tbb-devel $TBB_VERSION_MIN $TBB_VERSION_MAX + check_package_version_ge_lt_RPM tbb-devel $TBB_VERSION_MIN $TBB_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM tbb-devel clean_TBB @@ -4756,7 +4756,7 @@ install_RPM() { compile_OCIO else if [ "$RPM" = "SUSE" ]; then - check_package_version_ge_lt_RPM OpenColorIO-devel $OCIO_VERSION_MIN $OCIO_VERSION_MAX + check_package_version_ge_lt_RPM OpenColorIO-devel $OCIO_VERSION_MIN $OCIO_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM OpenColorIO-devel clean_OCIO @@ -4776,7 +4776,7 @@ install_RPM() { INFO "Forced ILMBase/OpenEXR building, as requested..." compile_OPENEXR else - check_package_version_ge_lt_RPM openexr-devel $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MAX + check_package_version_ge_lt_RPM openexr-devel $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM openexr-devel OPENEXR_VERSION=`get_package_version_RPM openexr-devel` @@ -4794,7 +4794,7 @@ install_RPM() { INFO "Forced OpenImageIO building, as requested..." compile_OIIO else - check_package_version_ge_lt_RPM OpenImageIO-devel $OIIO_VERSION_MIN $OIIO_VERSION_MAX + check_package_version_ge_lt_RPM OpenImageIO-devel $OIIO_VERSION_MIN $OIIO_VERSION_MEX if [ $? -eq 0 -a $_with_built_openexr == false ]; then install_packages_RPM OpenImageIO-devel OpenImageIO-utils clean_OIIO @@ -4819,7 +4819,7 @@ install_RPM() { else CLANG_DEV="clang-devel" fi - check_package_version_ge_lt_RPM llvm-devel $LLVM_VERSION_MIN $LLVM_VERSION_MAX + check_package_version_ge_lt_RPM llvm-devel $LLVM_VERSION_MIN $LLVM_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM llvm-devel $CLANG_DEV have_llvm=true @@ -4855,7 +4855,7 @@ install_RPM() { else OSL_DEV="openshadinglanguage-devel" fi - check_package_version_ge_lt_RPM $OSL_DEV $OSL_VERSION_MIN $OSL_VERSION_MAX + check_package_version_ge_lt_RPM $OSL_DEV $OSL_VERSION_MIN $OSL_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM $OSL_DEV clean_OSL @@ -4950,7 +4950,7 @@ install_RPM() { _do_compile_embree=true else # There is a package, but it does not provide everything that Blender needs... - #~ check_package_version_ge_lt_RPM embree-devel $EMBREE_VERSION_MIN $EMBREE_VERSION_MAX + #~ check_package_version_ge_lt_RPM embree-devel $EMBREE_VERSION_MIN $EMBREE_VERSION_MEX #~ if [ $? -eq 0 ]; then #~ install_packages_RPM embree-devel #~ clean_Embree @@ -4989,7 +4989,7 @@ install_RPM() { INFO "Forced FFMpeg building, as requested..." compile_FFmpeg else - check_package_version_ge_lt_RPM ffmpeg-devel $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MAX + check_package_version_ge_lt_RPM ffmpeg-devel $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM ffmpeg ffmpeg-devel clean_FFmpeg @@ -5214,7 +5214,7 @@ install_ARCH() { INFO "Forced Python building, as requested..." _do_compile_python=true else - check_package_version_ge_lt_ARCH python $PYTHON_VERSION_MIN $PYTHON_VERSION_MAX + check_package_version_ge_lt_ARCH python $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX if [ $? -eq 0 ]; then PYTHON_VERSION_INSTALLED=$(echo `get_package_version_ARCH python` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') @@ -5227,8 +5227,8 @@ install_ARCH() { module=($module) package="python-${module[0]}" package_vmin=${module[1]} - package_vmax=${module[2]} - check_package_version_ge_lt_ARCH "$package" $package_vmin $package_vmax + package_vmex=${module[2]} + check_package_version_ge_lt_ARCH "$package" $package_vmin $package_vmex if [ $? -eq 0 ]; then install_packages_ARCH "$package" else @@ -5254,7 +5254,7 @@ install_ARCH() { INFO "Forced Boost building, as requested..." compile_Boost else - check_package_version_ge_lt_ARCH boost $BOOST_VERSION_MIN $BOOST_VERSION_MAX + check_package_version_ge_lt_ARCH boost $BOOST_VERSION_MIN $BOOST_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH boost clean_Boost @@ -5271,7 +5271,7 @@ install_ARCH() { INFO "Forced TBB building, as requested..." compile_TBB else - check_package_version_ge_lt_ARCH intel-tbb $TBB_VERSION_MIN $TBB_VERSION_MAX + check_package_version_ge_lt_ARCH intel-tbb $TBB_VERSION_MIN $TBB_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH intel-tbb clean_TBB @@ -5288,7 +5288,7 @@ install_ARCH() { INFO "Forced OpenColorIO building, as requested..." compile_OCIO else - check_package_version_ge_lt_ARCH opencolorio $OCIO_VERSION_MIN $OCIO_VERSION_MAX + check_package_version_ge_lt_ARCH opencolorio $OCIO_VERSION_MIN $OCIO_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH opencolorio clean_OCIO @@ -5305,7 +5305,7 @@ install_ARCH() { INFO "Forced ILMBase/OpenEXR building, as requested..." compile_OPENEXR else - check_package_version_ge_lt_ARCH openexr $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MAX + check_package_version_ge_lt_ARCH openexr $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH openexr OPENEXR_VERSION=`get_package_version_ARCH openexr` @@ -5324,7 +5324,7 @@ install_ARCH() { INFO "Forced OpenImageIO building, as requested..." compile_OIIO else - check_package_version_ge_lt_ARCH openimageio $OIIO_VERSION_MIN $OIIO_VERSION_MAX + check_package_version_ge_lt_ARCH openimageio $OIIO_VERSION_MIN $OIIO_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH openimageio clean_OIIO @@ -5344,7 +5344,7 @@ install_ARCH() { INFO "Forced LLVM building, as requested..." _do_compile_llvm=true else - check_package_version_ge_lt_ARCH llvm $LLVM_VERSION_MIN $LLVM_VERSION_MAX + check_package_version_ge_lt_ARCH llvm $LLVM_VERSION_MIN $LLVM_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH llvm clang have_llvm=true @@ -5375,7 +5375,7 @@ install_ARCH() { INFO "Forced OpenShadingLanguage building, as requested..." _do_compile_osl=true else - check_package_version_ge_lt_ARCH openshadinglanguage $OSL_VERSION_MIN $OSL_VERSION_MAX + check_package_version_ge_lt_ARCH openshadinglanguage $OSL_VERSION_MIN $OSL_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH openshadinglanguage clean_OSL @@ -5401,7 +5401,7 @@ install_ARCH() { INFO "Forced OpenSubdiv building, as requested..." compile_OSD else - check_package_version_ge_lt_ARCH opensubdiv $OSD_VERSION_MIN $OSD_VERSION_MAX + check_package_version_ge_lt_ARCH opensubdiv $OSD_VERSION_MIN $OSD_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH opensubdiv clean_OSD @@ -5418,7 +5418,7 @@ install_ARCH() { INFO "Forced OpenVDB building, as requested..." compile_OPENVDB else - check_package_version_ge_lt_ARCH openvdb $OPENVDB_VERSION_MIN $OPENVDB_VERSION_MAX + check_package_version_ge_lt_ARCH openvdb $OPENVDB_VERSION_MIN $OPENVDB_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH openvdb clean_OPENVDB @@ -5484,7 +5484,7 @@ install_ARCH() { _do_compile_embree=true else # There is a package, but it does not provide everything that Blender needs... - #~ check_package_version_ge_lt_ARCH embree $EMBREE_VERSION_MIN $EMBREE_VERSION_MAX + #~ check_package_version_ge_lt_ARCH embree $EMBREE_VERSION_MIN $EMBREE_VERSION_MEX #~ if [ $? -eq 0 ]; then #~ install_packages_ARCH embree #~ clean_Embree @@ -5523,7 +5523,7 @@ install_ARCH() { INFO "Forced FFMpeg building, as requested..." compile_FFmpeg else - check_package_version_ge_lt_ARCH ffmpeg $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MAX + check_package_version_ge_lt_ARCH ffmpeg $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH ffmpeg clean_FFmpeg From e79e86018e3879b1587df58ee58fabbce844084f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 21 Oct 2021 12:06:38 +0200 Subject: [PATCH 1023/1500] Fix T92387: crash in legacy point distribute node This was caused by rB40c3b8836b7a36303ea9c78b0932758cbf277f93. The same fix exists in the Distribute Points on Faces node. --- .../nodes/geometry/nodes/legacy/node_geo_point_distribute.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc index 892bf12d9c0..f30feb48734 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc @@ -623,6 +623,11 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) final_points_len += positions.size(); } + if (final_points_len == 0) { + params.set_output("Geometry", GeometrySet()); + return; + } + PointCloud *pointcloud = BKE_pointcloud_new_nomain(final_points_len); for (const int instance_index : positions_all.index_range()) { const int offset = instance_start_offsets[instance_index]; From 84bb6d7c02d1fd56f454d4470fabad2f11b73808 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 20 Oct 2021 16:19:49 +0200 Subject: [PATCH 1024/1500] UI: Header / Tool Settings: Show tool icon when tools are collapsed Show the scaled down tool icon when the tool region is collapsed. Show a blank space when the tool region is visible. * Minimize the UI flickering when changing the active tool. * Show the active tool when the tool region is collapsed. * Smaler header footprint (the tool name is not visible). This is a follow up for T91536. Differential Revision: https://developer.blender.org/D12939 --- .../scripts/startup/bl_ui/space_toolsystem_common.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 4a598d0aa63..4305476cb0f 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -190,7 +190,7 @@ class ToolActivePanelHelper: ToolSelectPanelHelper.draw_active_tool_header( context, layout.column(), - show_tool_icon=True, + show_tool_icon_always=True, tool_key=ToolSelectPanelHelper._tool_key_from_context(context, space_type=self.bl_space_type), ) @@ -766,7 +766,7 @@ class ToolSelectPanelHelper: def draw_active_tool_header( context, layout, *, - show_tool_icon=False, + show_tool_icon_always=False, tool_key=None, ): if tool_key is None: @@ -783,11 +783,15 @@ class ToolSelectPanelHelper: return None # Note: we could show 'item.text' here but it makes the layout jitter when switching tools. # Add some spacing since the icon is currently assuming regular small icon size. - if show_tool_icon: + if show_tool_icon_always: layout.label(text=" " + item.label, icon_value=icon_value) layout.separator() else: - layout.label(text=item.label) + if context.space_data.show_region_toolbar: + layout.template_icon(icon_value=0, scale=0.5) + else: + layout.template_icon(icon_value=icon_value, scale=0.5) + layout.separator() draw_settings = item.draw_settings if draw_settings is not None: From 17a96051cf0f664509638bc31b714a4925b5052c Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 21 Oct 2021 12:39:00 +0200 Subject: [PATCH 1025/1500] Re-bundle Libmv to ensure it is in sync with the repo --- intern/libmv/CMakeLists.txt | 2 + intern/libmv/ChangeLog | 946 +++++++++--------- intern/libmv/bundle.sh | 24 +- intern/libmv/files.txt | 2 + intern/libmv/mkfiles.sh | 2 +- .../libmv/third_party/msinttypes/inttypes.h | 21 +- intern/libmv/third_party/msinttypes/stdint.h | 21 +- 7 files changed, 529 insertions(+), 489 deletions(-) diff --git a/intern/libmv/CMakeLists.txt b/intern/libmv/CMakeLists.txt index 91ef1f4d038..596e8efa082 100644 --- a/intern/libmv/CMakeLists.txt +++ b/intern/libmv/CMakeLists.txt @@ -156,6 +156,7 @@ if(WITH_LIBMV) libmv/base/scoped_ptr.h libmv/base/vector.h libmv/base/vector_utils.h + libmv/build/build_config.h libmv/image/array_nd.h libmv/image/convolve.h libmv/image/correlation.h @@ -199,6 +200,7 @@ if(WITH_LIBMV) libmv/simple_pipeline/reconstruction_scale.h libmv/simple_pipeline/resect.h libmv/simple_pipeline/tracks.h + libmv/threading/threading.h libmv/tracking/brute_region_tracker.h libmv/tracking/hybrid_region_tracker.h libmv/tracking/kalman_filter.h diff --git a/intern/libmv/ChangeLog b/intern/libmv/ChangeLog index 81096dd90c9..80e69878c99 100644 --- a/intern/libmv/ChangeLog +++ b/intern/libmv/ChangeLog @@ -1,520 +1,556 @@ -commit efd7a93317e0278b99e66785f667823e451daef1 -Author: Sergey Sharybin -Date: Tue May 9 10:16:42 2017 +0200 +commit f701b2b9fbc641b3252b3513239eeb14477ed5e1 +Author: Lazydodo +Date: Wed Aug 21 10:38:33 2019 -0600 - Fix strict compiler warnings, unused variables - -commit 8efd47e13dfdd3f7209bc96f26d0b13127dd6376 -Author: Sergey Sharybin -Date: Wed Dec 14 10:44:57 2016 +0100 - - Fix T50243: libmv_panography_test is broken + Cleanup: Fix build error with MSVC - There was fully wrong logic in comparison: was actually accessing memory - past the array boundary. Run test manually and the figure seems correct - to me now. + Previously eigens internal include order somehow implicitly provided + M_PI and friends. The recent eigen version bump broke this implicit + behaviour, better to be explicit that we need the math defines for MSVC. + +commit 5b2892f5a3cb6a7a2300f1c32e93d90e22302286 +Author: Sergey Sharybin +Date: Mon Mar 15 14:58:13 2021 +0100 + + Cleanup: Spelling in Libmv comments + +commit 54cdd2fe5cad29df83cb4d3c3ef1e02e06b022a7 +Author: Sergey Sharybin +Date: Thu Oct 21 12:09:30 2021 +0200 + + Ensure clang-format is disabled for 3rd party libraries + +commit 517d51654d6a4cf790a340b657cc0f7dc00f3158 +Author: Campbell Barton +Date: Tue Jul 13 21:58:45 2021 +1000 + + Fix x/y mismatch in retract region tracker - Spotted by @LazyDodo, thanks! - -commit 6dfb9cd1bd14669d84be789000ce234747fb00ff -Author: Sergey Sharybin -Date: Thu Jul 14 11:49:38 2016 +0200 - - Fix some strict compiler warnings + Correct X/Y mismatch in RetrackRegionTracker. - One of them was a real bug! - -commit f61adaecf7b29ebe6677be0e1c825f0a8d475e4b -Author: Sergey Sharybin -Date: Wed May 31 11:22:34 2017 +0200 - - Enable explicit schur complement for BA step - - This is something we do in Blender and only reason it was not - enabled for standalone Libmv is because we did not have fresh - enough version of Ceres bundled. - -commit fc5d3a1d4880c6658aff693c1c1e8c10c96ce1a7 -Author: Sergey Sharybin -Date: Wed Nov 2 15:32:11 2016 +0100 - - Update tests to make tests pass after recent Ceres update - - Just a precision issue, difference is around 1e-7. Should be fine to - simply update expected value. - -commit e1ac9f6124110c1a90d8e417bea47acfcbdcca42 -Author: Sergey Sharybin -Date: Wed May 31 10:54:48 2017 +0200 - - Update Ceres to latest release 1.12.0 - -commit ac1571352b4962f110929b963f8616d7310ceea5 -Author: Sergey Sharybin -Date: Fri Apr 7 17:10:44 2017 +0200 - - Fix crash of keyframe selection on 32bit linux - -commit 5f8df3da965686df39a6ae5c9f17482075017bf4 -Author: Sergey Sharybin -Date: Tue Jan 19 14:00:53 2016 +0500 - - Solve some strict warnings in tests - -commit 8ea3a5d752a9ce3337ab7643897472a4d33747f1 -Author: Brecht Van Lommel -Date: Sat Feb 18 23:52:31 2017 +0100 - - Fix a few compiler warnings with macOS / clang. - -commit ffbe81461770e70736e80b8cab8e6eb1f8b27160 -Author: Mike Erwin -Date: Wed May 31 10:43:08 2017 +0200 - - Fix comparison of identicals - - Some of these check that dimensions match before running code that - assumes they do match. - - Found with PVS-Studio T48917. - -commit 206c01999cde16c1c6c43a8e13ffa86020821d98 -Author: Sergey Sharybin -Date: Wed May 31 10:39:16 2017 +0200 - - Add basic track masking API in place - - This brings back ability to mask non-interesting parts of - specific track (the feature got lost with new auto-track API). - - Added it back by extending frame accessor class. This isn't really - a frame thing, but we don't have other type of accessor here. - - Surely, we can use old-style API here and pass mask via region - tracker options for this particular case, but then it becomes much - less obvious how real auto-tracker will access this mask with old - style API. - - So seems we do need an accessor for such data, just matter of - finding better place than frame accessor. - -commit faa069cb826892780356477cc10602390fecf06b -Author: Sergey Sharybin -Date: Wed May 31 10:36:26 2017 +0200 - - Tests: Tweak epsilon to avoid what looks a false-positive failure - -commit 7c84e45c1d330871477ba3516f57178e5b9d101f -Author: Sergey Sharybin -Date: Wed May 31 10:15:43 2017 +0200 - - CMake: Fix mistake in closing branch - -commit cb769a0d319a8c95948153d78a4c3378a0142ece -Author: Sergey Sharybin -Date: Thu Jul 21 12:52:33 2016 +0200 - - Set of fixes for MSVC215 - - - Move GLOG/GFLAGS defines to a more global scope, - this way ANY of our own libraries will use proper - declspec. - - - Compile png/zlib/openexif on Windows as well since - those are required for a correct linking. - -commit bb95c8654fd2cea72d66ed04cd825cc3712ea804 -Author: Sergey Sharybin -Date: Wed Jul 20 18:14:46 2016 +0200 - - Disable unexisting Ceres option - - Explicit Schur complement requires having - newer Ceres than we currently have bundled. - -commit a2e12c959ef32cc9382244d1581992c2f7aa9c09 -Author: Sergey Sharybin -Date: Wed Jul 20 18:04:57 2016 +0200 - - Various fixes for MSVC - - - Update Eigen to 3.2.7 since this brings crucial - fixes for MSVC 2015. - - - Switch to STATIC build by default. - - There are issues building current sources as dynamic - libraries with MSVC2015 and additionally building - dynamic Ceres is not recommended anyway, so let's - not do this for the time being. - - If anyone finds a way to make this all working -- - it'llsurely be a welcome addition. - -commit 7a676106720fb126a27ff010abdd8bb65d7e0d9a -Author: Sergey Sharybin -Date: Mon Jan 4 18:30:12 2016 +0500 - - Fix strict compiler warnings - -commit d3537e3709fe11f42312e82cb1c9837c9e742385 -Author: Sergey Sharybin -Date: Sun Jan 3 14:17:10 2016 +0500 - - GLog/GFlags: Reduce difference between upstream and bundled versions - - Several things here: - - - Re-bundled sources using own fork with pull-requests applied on the sources. - - - Got rid of changes around include "config.h", it was needed by Blender to - make it's include directories configuration to work. This could be addressed - differently from Blender side. - - - Moved some customization to defines set by CMakeLists. - -commit 1ec37bba2cfbbf0d6568429fa3035ee2164c23e6 -Author: Sergey Sharybin -Date: Sat Jan 2 12:42:55 2016 +0500 - - GFlags linking errors fix for MSVC - -commit df7642b270e8e43685e9ffb404b59d7b226a9f60 -Author: Sergey Sharybin -Date: Thu Dec 31 17:56:12 2015 +0500 - - Alternative fix for missing prototype for a couple of functions - -commit 08f685797b7d776cdaa579136c82b15ddc6ffb30 -Author: Sergey Sharybin -Date: Thu Dec 31 17:33:05 2015 +0500 - - Update GFlags to the latest upstream version - - Makes it easier to synchronize some compiler/warning fixes. - -commit e0ef5b09203e3906a555e6c2010f25cb667da9cd -Author: Sergey Sharybin -Date: Thu Dec 31 16:15:59 2015 +0500 - - GLog: Solve some compilation warnings - - Those are actually sent to a pull-request, see - - https://github.com/google/glog/pull/81 - -commit 2072b213d4d3a55d099a063ed1e7331cc773454e -Author: Sergey Sharybin -Date: Thu Dec 31 16:13:53 2015 +0500 - - Add Martijn Berger to the AUTHORS file - -commit 4dd0770d98d67896e4f936356e281f63d927410e -Author: Martijn Berger -Date: Thu Dec 31 16:13:08 2015 +0500 - - Fix compilation error of Glog and Gflags with MSVC2015 - -commit 2712f42be2ad79e7d3a6c9905f6d8d1e3b7133ac -Author: Brecht Van Lommel -Date: Thu Dec 31 14:00:58 2015 +0500 - - Fix OS X (with 10.11 SDK) glog build errors due to using deprecated code. - - Some values are now restored to the ones from before the upgrade today. - -commit d249280fdf7c937fd6ebbc465508843a70aafd4c -Author: Sergey Sharybin -Date: Wed Dec 30 16:59:28 2015 +0500 - - Tweaks to Glog to support building on all platforms - - This makes it possible to compile Libmv on all platforms, - amount of hacks is lower, which could bring some warnings - up, but those are better be addressed via upstream which - is now rather active. - -commit 86c57750ddb857643fb5dd2c83b4953da83dd57d -Author: Sergey Sharybin -Date: Wed Dec 30 16:15:47 2015 +0500 - - Enable explicit Schur complement matrix by default - - Gives up to 2x speed up of camera solving process in average scene. - In the really huge one it might be slower, but that we need to investigate. - -commit d6c52a70b5a0664b7c74bda68f59a895fe8aa235 -Author: Sergey Sharybin -Date: Wed Dec 30 16:13:03 2015 +0500 - - Fix one frame memory leak when tracking last frame - -commit 6e2ac41d25d5923b2a62c96d27d919a36eff9b48 -Author: Brecht Van Lommel -Date: Wed Dec 30 16:11:24 2015 +0500 - - Motion tracking not workig with Xcode 7 on OS X. - - Caused by use of the uninitialized shape_ variable in Resize(). - -commit fc72ae06fb4ae559ac37d14d1b34d6669505cc86 -Author: Sergey Sharybin -Date: Wed Dec 30 15:56:40 2015 +0500 - - Update GLog to latest upstream - - Should fix issues building with MSVC2015. - -commit d4b2d15bd3d195074b074331354de96a1b51042f -Author: Sergey Sharybin -Date: Wed Dec 30 16:01:10 2015 +0500 - - Fix wrong README file reference - -commit 2b4aa0b7720cae9a408284834559bea9960157ee -Author: Keir Mierle -Date: Mon May 11 02:16:53 2015 -0700 - - Make README more informative for GitHub viewers - - Reviewers: sergey + NOTE: This isn't used at the moment. Reviewed By: sergey - Differential Revision: https://developer.blender.org/D1295 + Ref D11895 -commit 514e4491aea655d20be047ed87f002fb7854d5c9 -Author: Keir Mierle -Date: Mon May 11 01:54:09 2015 -0700 +commit cfcfc803cf599c3bd7bbfa3bac7d5e9c8bca284b +Author: Jesse Yurkovich +Date: Mon Jul 12 21:01:18 2021 -0700 - Simplify the modal solver Ceres cost function + Cleanup: Use correct _WIN32/64 defines for MSVC - Fix test by flipping the quaternion. + Docs: https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros - Reviewers: sergey + Differential Revision: https://developer.blender.org/D11460 + +commit ad8dfd41958b0e4cb90c7c4e5cc20f93c0832e73 +Author: Campbell Barton +Date: Thu Jun 24 15:56:58 2021 +1000 + + Cleanup: comment blocks, trailing space in comments + +commit 9a6cc5daa6c38f33ce2385fe489c07d97586d452 +Author: Campbell Barton +Date: Sun Jun 13 15:11:40 2021 +1000 + + Fix new[]/delete[] mismatch + +commit 5591d12928df74ed3517b5e61eeda0b64e6ade4f +Author: Sergey Sharybin +Date: Mon Mar 22 15:16:07 2021 +0100 + + Fix T86591: Tracking backwards is slower - Reviewed By: sergey + The root of the issue was caused by the PredictMarkerPosition() + always returning false when tracking backwards. This was making + it so tracker always had to run brute initialization, which is + an expensive operation. - Projects: #libmv + From own timing here: - Differential Revision: https://developer.blender.org/D756 - -commit e55fafd31f7d53d42af7c6b7df2eebe3c2568da9 -Author: Sergey Sharybin -Date: Wed Dec 31 19:05:51 2014 +0500 - - Synchronize MSVC compilation fixes from Blender - -commit 7d6020d2ec42c6cb2749bc891186b4880d26d40b -Author: Sergey Sharybin -Date: Wed Dec 31 15:32:07 2014 +0500 - - Update GLog to latest upstream revision 143 + - Tracking forward takes 0.667637 seconds + - Tracking backward used to take 2.591856 seconds + - Tracking backward now takes 0.827724 seconds - Mainly to solve compilation error with demangle.cc. + This is a very nice speedup, although the tracking backwards is + still somewhat slower. Will be investigated further as part of + a regular development. -commit 5dc746700eaf85cb674f0fb73ff3c1b49a7f6315 -Author: Sergey Sharybin -Date: Fri Dec 12 14:59:55 2014 +0500 +commit ad9546d2319b0db3e1ccc41c4f84899729d1ad1e +Author: Sergey Sharybin +Date: Mon Mar 15 15:48:15 2021 +0100 - Update GFlags to latest release 2.1.1 + Fix T86262: Tracking backwards fails after gap in track - Main purpose of this (andsome of upcoming) update is to check if the - upstream sources are useable without any modifications for us. And if - not, then we'll need to consider moving some changes into upstream. - - This commit contains an one-to-one copy of the upstream GFlags library - and also changes namespace usage since it's changed in the upstream. + The issue was caused by a prediction algorithm detecting tracking the + wrong way. Solved by passing tracking direction explicitly, so that + prediction will always happen correctly regardless of the state of the + Tracks context. -commit 6fe6d75f7e90e161b44643b953f058a3829a5247 -Author: Sergey Sharybin -Date: Sat Nov 1 02:53:36 2014 +0500 +commit 0773a8d6dfe1bf997b8e42d61d136a65b67fde88 +Author: Sergey Sharybin +Date: Thu Oct 21 11:18:10 2021 +0200 - Libmv: Code cleanup, mixed class/struct in declaration/definition + Cleanup: clang-format + + Is based on Google style which was used in the Libmv project before, + but is now consistently applied for the sources of the library itself + and to C-API. With some time C-API will likely be removed, and it + makes it easier to make it follow Libmv style, hence the diversion + from Blender's style. + + There are quite some exceptions (clang-format off) in the code around + Eigen matrix initialization. It is rather annoying, and there could be + some neat way to make initialization readable without such exception. + + Could be some places where loss of readability in matrix initialization + got lost as the change is quite big. If this has happened it is easier + to address readability once actually working on the code. + + This change allowed to spot some missing header guards, so that's nice. + + Doing it in bundled version, as the upstream library needs to have some + of the recent development ported over from bundle to upstream. + + There should be no functional changes. -commit d2a5f7953812d2d09765431b59c6c4ac72faf35b -Author: Sergey Sharybin -Date: Thu Oct 30 23:13:53 2014 +0500 +commit c4de0ccd5aede3bb90ac6ad1039a83c2260fbefd +Author: Sergey Sharybin +Date: Tue Feb 23 16:43:01 2021 +0100 - Libmv: Support disabled color channels in tracking settings + Avoid use of LOG(INFO) in solver - This was never ported to a new tracking pipeline and now it's done using - FrameAccessor::Transform routines. Quite striaghtforward, but i've changed - order of grayscale conversion in blender side with call of transform callback. - - This way it's much easier to perform rescaling in libmv side. + Usage of LOG(INFO) actually went against own guidelines in the + logging.h: the INFO is for messages which are to be printed + regardless of debug/verbosity settings. -commit d976e034cdf74b34860e0632d7b29713f47c5756 -Author: Keir Mierle -Date: Sat Aug 23 00:38:01 2014 -0700 +commit 94d925131b08e23cea7fdf735923e24b78a8c7fd +Author: Campbell Barton +Date: Fri Feb 5 16:23:34 2021 +1100 - Minor keyframe selection cleanups - - Reviewers: sergey - - Reviewed By: sergey - - Differential Revision: https://developer.blender.org/D757 + Cleanup: correct spelling in comments -commit bc99ca55dadfca89fde0f93764397c2fe028943d -Author: Sergey Sharybin -Date: Sat Aug 23 01:55:32 2014 +0600 +commit db0c8dbea19d684899dffcb46a2e79c12dc91e6b +Author: Sergey Sharybin +Date: Tue Dec 1 14:52:08 2020 +0100 - implement backward prediction + Tweak default logging verbosity level - The title actually says it all, just extend current implementation - of PredictMarkerPosition() to cases when tracking happens in the reverse - order (from the end frame to start). + Log to verbosity level 1 rather than INFO severity. - it's still doesn't solve all the ambiguity happening in the function - in cases when one tracks the feature and then re-tracks it in order - to refine the sliding. This is considered a separate TODO for now and - will likely be solved by passing tracking direction to the prediction + Avoids a lot of overhead coming from construction of the INFO stream + and improves performance and threadability of code which uses logging. + + This makes tracking of 250 frames of a track of default settings to + drop down from 0.6sec to 0.4sec. + +commit 8f2c13edf0d3b0c8a2ad90d6ae13f11aed457709 +Author: Sergey Sharybin +Date: Tue Dec 1 14:46:35 2020 +0100 + + Cleanup, remove unused logging macros + + Unused and was not entirely happy with such short abbreviations. + +commit 1dc2ab9847ce6cc84ab6c8e3e8613687480d735d +Author: Sergey Sharybin +Date: Fri Nov 27 15:58:55 2020 +0100 + + Add threading primitives + + Allows to use mutex, scoped_lock, and conditional_variable from within + the libmv namespace. + + Implementation is coming from C++11. Other configurations are easy to + implement, but currently C++11 is the way to go. + +commit 6531b66fd3bf4b5ee112b904b390d164a03628c5 +Author: Sergey Sharybin +Date: Fri Nov 27 15:43:44 2020 +0100 + + Add build configuration header + + Allows to easily access build platform information, such as bitness, + compiler, supported C++ version and so on. + +commit 632c356375e4ac07da43b346384fdf67e7916426 +Author: Campbell Barton +Date: Fri Nov 6 14:35:38 2020 +1100 + + Cleanup: doxygen comments + +commit c9479472ce8d63b1d6e4c9c1b072b975479cd4c0 +Author: Campbell Barton +Date: Fri Nov 6 11:25:27 2020 +1100 + + Cleanup: follow our code style for float literals + +commit 6455055b265cddbf604523653b01c9d7adbc23d5 +Author: Ivan Perevala +Date: Thu Oct 29 10:19:06 2020 +0100 + + Fix clang inconsistent-missing-override warnings. + + Reviewed By: sergey, ankitm + + Differential Revision: https://developer.blender.org/D9377 + +commit 020fc13a5336c872c3251e45d28cc02584094f33 +Author: Sergey Sharybin +Date: Wed Oct 21 10:53:13 2020 +0200 + + Simplify configuration of intrinsics to refine + + Previously, only predefined and limited set of intrinsics combinations + could have been refined. This was caused by a bundle adjustment library + used in the early days of the solver. + + Now it is possible to fully customize which intrinsics are to be refined + during camera solving. Internally solver supports per-parameter settings + but in the interface they are grouped as following: + + * Focal length + * Optical center + * Radial distortion coefficients (which includes k1, k2, k3, k4) + * Tangential distortion coefficients (which includes p1, p2) + + Differential Revision: https://developer.blender.org/D9294 + +commit 5ef3c2c41854a2e634ba692e9b63194aa74b928d +Author: Sergey Sharybin +Date: Wed Oct 21 10:48:13 2020 +0200 + + Fix typo in packed intrinsics + + Was using doing an implicit cast of floating point value to boolean. + Was not noticed before because the boolean value was never never used. + +commit 648388878525aa585bdbbed2e457186b6483bd5e +Author: Sergey Sharybin +Date: Tue Oct 13 11:34:05 2020 +0200 + + Refactor camera intrinsics parameter block + + Use the newly introduced packed intrinsics, which allows to remove + code which was initializing parameters block based on distortion + model type. + + Now such initialization is done by a particular implementation of + a distortion model. + + Differential Revision: https://developer.blender.org/D9192 + +commit 60780e30a8aec37f53d4e952375ffc8d720341c6 +Author: Sergey Sharybin +Date: Tue Oct 13 11:32:35 2020 +0200 + + Add generic class for packed intrinsics + + This is a common class which can be used in all sort of minimization + problems which needs camera intrinsics as a parameter block. + + Currently unused, but will replace a lot of hard-coded logic in the + bundle adjustment code. + +commit 409924c76be27fec3b619d79379df76cb8de6a9a +Author: Sergey Sharybin +Date: Tue Oct 13 11:25:34 2020 +0200 + + Add array to libmv namespace + +commit 6411c7fed3c30dce05b9b2df239b7f4cf91cdecd +Author: Sebastian Parborg +Date: Tue Oct 20 14:45:54 2020 +0200 + + Fix test on windows + + There is no point in testing std::vector capacity as it can differ + between std implementations. + +commit 242d31ff4ed78a6f36ac29f139b03abc4f32f4f5 +Author: Harley Acheson +Date: Mon Oct 19 08:51:50 2020 -0700 + + Spelling: Miscellaneous + + Corrects 34 miscellaneous misspelled words. + + Differential Revision: https://developer.blender.org/D9248 + + Reviewed by Campbell Barton + +commit 91cd0310d6762fdcc7253303bec7f0453cf1f18f +Author: Harley Acheson +Date: Mon Oct 19 08:12:33 2020 -0700 + + Spelling: It's Versus Its + + Corrects incorrect usage of contraction for 'it is', when possessive 'its' was required. + + Differential Revision: https://developer.blender.org/D9250 + + Reviewed by Campbell Barton + +commit 788ed5fa8ace0df951fe8f03e6510187ed694044 +Author: Sebastian Parborg +Date: Mon Oct 19 13:03:06 2020 +0200 + + Fix alignment issues when compiling with AVX support + + There would be eigen alignment issues with the custom libmv vector + class when compiling with AVX optimizations. This would lead to + segfaults. + + Simply use the std::vector base class as suggested by the old TODO in + the class header. + + Reviewed By: Sergey + + Differential Revision: http://developer.blender.org/D8968 + +commit 21a114476eaafec4b25c0ea19eaccdd9e870160d +Author: Sergey Sharybin +Date: Mon Oct 12 14:59:18 2020 +0200 + + Remove array access from camera intrinsics + + That was a suboptimal decision from back in the days, which ended up + being problematic. It is no longer used, so remove it from API making + it so new code does not depend on this weak concept. + +commit b6a579fd88de5e13c812310a0416e698648578c4 +Author: Sergey Sharybin +Date: Mon Oct 12 12:17:55 2020 +0200 + + Fix wrong packing order of intrinsics for BA step + + The order got broken when Brown distortion model has been added. + Made it so the indexing of parameters is strictly defined in the + parameter block, matching how parameters are used in the cost function. - Reviewers: keir + There is some duplication going on accessing parameters. This can + be refactored in the future, by either moving common parts packing + and cost function to an utility function in bundle.cc. + Alternatively, can introduce a public PackedIntrinsics class which + will contain a continuous block of parameters, and each of the + camera models will have API to be initialized from packed form and + to create this packed form. - Reviewed By: keir + The benefit of this approach over alternative solutions previously + made in the master branch or suggested in D9116 is that the specific + implementation of BA does not dictate the way how public classes need + to be organized. It is API which needs to define how implementation + goes, not the other way around. - Differential Revision: https://developer.blender.org/D663 + Thanks Bastien and Ivan for the investigation! -commit 5b87682d98df65ade02638bc6482d824cf0dd0b3 -Author: Keir Mierle -Date: Thu Aug 21 22:45:22 2014 -0700 +commit 595d4a45db2f1d68bc6091b6fad3580411a10e40 +Author: Sergey Sharybin +Date: Mon Oct 12 10:46:31 2020 +0200 - Make libmv compile on Ubuntu 14.04 + Fix memory leak in modal solver - Reviewers: fsiddi + The leak was happening when problem did not have any parameters blocks + defined. This happens, for example, if there are no 3D points at all, + or when all markers are set to 0 weight. - Reviewed By: fsiddi - - Subscribers: sergey - - Differential Revision: https://developer.blender.org/D755 + Was noticeable in libmv_modal_solver_test when building with LSAN + enabled. -commit 0a81db623c458e0384b4f7060d1bcff8993fb469 +commit b6ecdc497c00e5f9a0a6dffe1c27051c8df8482a +Author: Sergey Sharybin +Date: Mon Oct 12 10:44:57 2020 +0200 + + Cleanup, spelling in function name + + Is a local function, not affecting API. + +commit 2a712777076c076fcf8aae9b9657089d81fd129f +Author: Ivan Perevala +Date: Wed Sep 30 15:12:14 2020 +0200 + + Implement Brown-Conrady distortion model + + Implemented Brown-Conrady lens distortion model with 4 radial and + 2 tangential coefficients to improve compatibility with other software, + such as Agisoft Photoscan/Metashapes, 3DF Zephir, RealityCapture, + Bentley ContextCapture, Alisevision Meshroom(opensource). + + Also older programs: Bundler, CPMVS. + In general terms, most photogrammetric software. + + The new model is available under the distortion model menu in Lens + settings. + + For tests and demos check the original patch. + + Reviewed By: sergey + + Differential Revision: https://developer.blender.org/D9037 + +commit e4e2b8c382f9bb2490318cd621a438ccb4dd7327 +Author: Ivan +Date: Mon Sep 28 09:57:03 2020 +0200 + + Fix NukeCameraIntrinsics copy constructor + + Copy the appropriate parameter + + Reviewed By: sergey + + Differential Revision: https://developer.blender.org/D9014 + +commit 1e5deb138ad1ef79204b118f4dc741f96bd0e650 +Author: Johan Walles +Date: Tue Jul 7 11:09:31 2020 +0200 + + Add units to motion tracking solve errors + + The unit being "pixels". + + Before this change the solve errors were unitless in the UI. + + With this change in place, the UI is now clear on that the unit of the + reprojection errors is pixels (px). + + Differential Revision: https://developer.blender.org/D8000 + +commit a8a5a701f2839a97af51825d98726e7eb7e6eb1d +Author: Campbell Barton +Date: Wed Jul 1 13:12:24 2020 +1000 + + Cleanup: spelling + +commit fd83866975d5d8163d9bb71d645239562cb329b5 Author: Sergey Sharybin -Date: Wed Jul 23 00:42:00 2014 +0600 +Date: Thu Jun 18 10:12:01 2020 +0200 - Fix wrong residual blocks counter + Update Ceres to the latest upstream version - This happened in cases when having zero-weighted tracks - and could lead to some assert failures on marking parameter - block constant. + Using latest master because of various compilation error fixes. + + Brings a lot of recent development. From most interesting parts: + + - New threading model. + - Tiny solver. + - Compatibility with C++17. -commit 2824dbac54cacf74828678be7a5c9fd960ce83e2 +commit 307e9a945a320a1b7d9af210c2da1cfbb6439ec5 Author: Sergey Sharybin -Date: Fri Jul 18 12:52:03 2014 +0600 +Date: Fri May 15 14:54:30 2020 +0200 - Fix search area sliding issue + Fix crash solving when having negative frames - The only way to do this is to store search region in floats - and round when we need to sample it. Otherwise you'll always - have sliding effect caused by rounding the issues, especially - when doing incremental offset (thing which happens in the - prediction code). + Don't use linear array with frame as an index since it has the + following disadvantages: - Pretty much straightforward change apart from stuff to be kept - in mind: offset calculation int should happen relative to the - rounded search region. This is because tracker works in the space - of the search window image which get's rounded on the frame access, + - Requires every application to take care of frame remapping, which + could be way more annoying than it sounds. - This makes API a bit creepy because frame accessor uses the same - Region struct as the search window in Marker and ideally we would - need to have either IntRegion or Region in order to make - Libmv fully track on what's getting rounded and when. + - Inefficient from memory point of view when solving part of a footage + which is closer to the end of frame range. - Reviewers: keir + Using map technically is slower from performance point of view, but + could not feel any difference as the actual computation is way more + complex than access of camera on individual frames. - Reviewed By: keir - - Differential Revision: https://developer.blender.org/D616 + Solves crash aspect of T72009 -commit 04862c479332308be47a0f27361402444ace8880 -Author: Keir Mierle -Date: Fri May 9 23:00:03 2014 +0200 +commit 730eec3e0a382b23648d9000e07218519e1223d1 +Author: Sergey Sharybin +Date: Fri May 15 11:08:18 2020 +0200 - Start the automatic 2D tracking code - - This starts the 2D automatic tracking code. It is totally unfinished. - - Reviewers: sergey - - Reviewed By: sergey - - Differential Revision: https://developer.blender.org/D523 + Add map utility -commit be679f67d807a2139c1f7d7e2ca45141940b30d5 -Author: Keir Mierle -Date: Fri May 9 14:36:04 2014 +0200 +commit 0d3f5d94474553c51a1e5d830521fca4ee82aa54 +Author: Sergey Sharybin +Date: Fri May 15 11:05:07 2020 +0200 - Also shift the search window - - Reviewers: sergey - - Reviewed By: sergey - - Differential Revision: https://developer.blender.org/D520 + Cleanup, spelling -commit 66b8f5eef2633ebcde32a388fc14c60171011821 -Author: Keir Mierle -Date: Fri May 9 13:06:28 2014 +0200 +commit d26503ab5a9d2faef0f388695d6bb1c46ffff4eb +Author: Aaron Carlisle +Date: Thu May 7 23:42:22 2020 -0400 - Change the search region to absolute frame coordinates - - Smarter Eigen usage - - Better error logging - - Reviewers: sergey - - Reviewed By: sergey - - Differential Revision: https://developer.blender.org/D519 + Cleanup: Doxygen: fix markup warnings for links -commit a08193319ae409fad8f08887eae1f79f02e91eaa -Author: Keir Mierle -Date: Fri May 9 12:02:47 2014 +0200 +commit 9e8e94e8281abb6edb5e81afe4365743994800d2 +Author: Sergey Sharybin +Date: Mon Apr 20 17:33:03 2020 +0200 - First cut at predictive tracing + Implement Nuke/Natron distortion model - This adds a Kalman filter-based approach to predict where a marker - will go in the next frame to track. Hopefully this will make the - tracker work faster by avoiding lengthy searches. This code - compiles, but is otherwise untested, and likely does not work. + Neither Nuke nor Natron support OpenCV's radial distortion model + which makes it impossible to have any kind of interoperability. - Fix else branch + The new model is available under the distortion model menu in Lens + settings. - Add some tests - - Update patch coordinates as well (and test) - - Reviewers: sergey - - Reviewed By: sergey - - Differential Revision: https://developer.blender.org/D518 + Differential Revision: https://developer.blender.org/D7484 -commit 607ffb2f62b56e34a841abbb952d83e19cd1e23c -Author: Keir Mierle -Date: Thu May 8 16:05:28 2014 +0200 +commit 1dc83e7545fc832180322f492245933fb343f158 +Author: Sergey Sharybin +Date: Tue Apr 21 16:41:23 2020 +0200 - Add constructor to AutoTrack - -commit c39e20a0c27da3733804c3848454b5d4c4f0e66b -Author: Keir Mierle -Date: Thu May 8 16:04:20 2014 +0200 - - Fix GetMarker compilation issue - -commit 8dd93e431b6e44439c803bfd26ec2669b656177e -Author: Keir Mierle -Date: Thu May 8 15:50:26 2014 +0200 - - Expose GetMarker() in AutoTrack + Cleanup, naming - Reviewers: sergey + Initial bundle adjustment only supported OpenCV's radial distortion + model, so the cost functor was called after it. - Reviewed By: sergey + Nowadays it supports more than this single model, so naming was a bit + wrong and misleading. + +commit b6df71dd5e5a49b3459c522c10ae98e8795e69b5 +Author: Sergey Sharybin +Date: Tue Apr 21 12:25:45 2020 +0200 + + Cleanup, spelling and naming in bundle adjustment - Differential Revision: https://developer.blender.org/D516 + Just more things which were discovered to be annoying on unclear when + adding more features to this code. + +commit a4bbe3a10a5b6b23acd261b1e6c772a6f6672c00 +Author: Sergey Sharybin +Date: Mon Apr 20 17:26:45 2020 +0200 + + Cleanup, spelling in comment + +commit 4219e9d22bad012e9e64b83a5e26d1d4eff3fcc6 +Author: Sergey Sharybin +Date: Mon Apr 20 12:44:07 2020 +0200 + + De-duplicate creation of residual block + + Allows to centralize logic which is needed to check which cost functor + to use for the specific intrinsics. + +commit 9637ebc18b2bac794c8f11b1c2c092a3a9c3e6d2 +Author: Sergey Sharybin +Date: Mon Apr 20 11:41:01 2020 +0200 + + Cleanup reprojection cost function + + Make it smaller and more clear how and what it operates on. + +commit 1efc975a5457cfee6baf41df67afb3e43834d57f +Author: Sergey Sharybin +Date: Mon Apr 20 11:19:47 2020 +0200 + + Pass entire camera intrinsics to reprojection error functor + + Currently no functional changes, but allows to have access to some + invariant settings of camera intrinsics such as image dimensions. + +commit 0f54f2b305f59fc99764b9c85fe28f35f10faa86 +Author: Sergey Sharybin +Date: Fri Apr 17 17:34:19 2020 +0200 + + Cleanup, rephrase comment + +commit 229912b0e1746145c4ab710f8609ce90f690a8e2 +Author: Sergey Sharybin +Date: Fri Apr 17 17:29:04 2020 +0200 + + Cleanup, fix indentation diff --git a/intern/libmv/bundle.sh b/intern/libmv/bundle.sh index db8f88845a8..b728d038c5b 100755 --- a/intern/libmv/bundle.sh +++ b/intern/libmv/bundle.sh @@ -9,7 +9,8 @@ fi BRANCH="master" -repo="git://git.blender.org/libmv.git" +# repo="git://git.blender.org/libmv.git" +repo="/home/sergey/Developer/libmv" tmp=`mktemp -d` git clone -b $BRANCH $repo $tmp/libmv @@ -26,16 +27,16 @@ done rm -rf $tmp -sources=`find ./libmv -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v _test.cc | grep -v test_data_sets | sed -r 's/^\.\//\t\t/' | sort -d` -headers=`find ./libmv -type f -iname '*.h' | grep -v test_data_sets | sed -r 's/^\.\//\t\t/' | sort -d` +sources=`find ./libmv -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v _test.cc | grep -v test_data_sets | sed -r 's/^\.\// /' | sort -d` +headers=`find ./libmv -type f -iname '*.h' | grep -v test_data_sets | sed -r 's/^\.\// /' | sort -d` -third_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\//\t\t/' | sort -d` -third_headers=`find ./third_party -type f -iname '*.h' | sed -r 's/^\.\//\t\t/' | sort -d` +third_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\// /' | sort -d` +third_headers=`find ./third_party -type f -iname '*.h' | sed -r 's/^\.\// /' | sort -d` -tests=`find ./libmv -type f -iname '*_test.cc' | sort -d | awk ' { name=gensub(".*/([A-Za-z_]+)_test.cc", "\\\\1", $1); printf("\t\tBLENDER_SRC_GTEST(\"libmv_%s\" \"%s\" \"libmv_test_dataset;bf_intern_libmv;extern_ceres\")\n", name, $1) } '` +tests=`find ./libmv -type f -iname '*_test.cc' | sort -d | awk ' { name=gensub(".*/([A-Za-z_]+)_test.cc", "\\\\1", "g", $1); printf(" blender_add_test_executable(\"libmv_%s\" \"%s\" \"\${INC}\" \"\${INC_SYS}\" \"libmv_test_dataset;bf_intern_libmv;extern_ceres\")\n", name, $1) } '` -src_dir=`find ./libmv -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\//\t\t/' | sort -d | uniq` -src_third_dir=`find ./third_party -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\//\t\t/' | sort -d | uniq` +src_dir=`find ./libmv -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\// /' | sort -d | uniq` +src_third_dir=`find ./third_party -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\// /' | sort -d | uniq` src="" win_src="" for x in $src_dir $src_third_dir; do @@ -119,6 +120,9 @@ set(LIB if(WITH_LIBMV) setup_libdirs() + if(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + endif() add_definitions(\${GFLAGS_DEFINES}) add_definitions(\${GLOG_DEFINES}) add_definitions(\${CERES_DEFINES}) @@ -186,7 +190,9 @@ ${third_headers} if(WITH_GTESTS) - blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "${INC}" "${INC_SYS}" "") + include(GTestTesting) + + blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "\${INC}" "\${INC_SYS}" "") ${tests} endif() diff --git a/intern/libmv/files.txt b/intern/libmv/files.txt index 985074bfe50..1a177a84c7a 100644 --- a/intern/libmv/files.txt +++ b/intern/libmv/files.txt @@ -119,6 +119,7 @@ libmv/simple_pipeline/resect.h libmv/simple_pipeline/resect_test.cc libmv/simple_pipeline/tracks.cc libmv/simple_pipeline/tracks.h +libmv/threading/threading.h libmv/tracking/brute_region_tracker.cc libmv/tracking/brute_region_tracker.h libmv/tracking/brute_region_tracker_test.cc @@ -138,6 +139,7 @@ libmv/tracking/track_region.cc libmv/tracking/track_region.h libmv/tracking/trklt_region_tracker.cc libmv/tracking/trklt_region_tracker.h +third_party/.clang-format third_party/msinttypes/inttypes.h third_party/msinttypes/README.libmv third_party/msinttypes/stdint.h diff --git a/intern/libmv/mkfiles.sh b/intern/libmv/mkfiles.sh index 618070f0a81..165b328f9aa 100755 --- a/intern/libmv/mkfiles.sh +++ b/intern/libmv/mkfiles.sh @@ -1,6 +1,6 @@ #!/bin/sh find ./libmv/ -type f | sed -r 's/^\.\///' | sort > files.txt -find ./third_party/ -mindepth 2 -type f | \ +find ./third_party/ -type f | \ grep -v third_party/ceres | \ sed -r 's/^\.\///' | sort >> files.txt diff --git a/intern/libmv/third_party/msinttypes/inttypes.h b/intern/libmv/third_party/msinttypes/inttypes.h index 71f5693def6..0e8af69cb07 100644 --- a/intern/libmv/third_party/msinttypes/inttypes.h +++ b/intern/libmv/third_party/msinttypes/inttypes.h @@ -1,35 +1,32 @@ -/* No need to format 3rd-party compatibility headers. */ -/* clang-format off */ - // ISO C9x compliant inttypes.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// // Copyright (c) 2006 Alexander Chemeris -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. -// +// // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +// /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ diff --git a/intern/libmv/third_party/msinttypes/stdint.h b/intern/libmv/third_party/msinttypes/stdint.h index 8dfd65f591d..189ee34571c 100644 --- a/intern/libmv/third_party/msinttypes/stdint.h +++ b/intern/libmv/third_party/msinttypes/stdint.h @@ -1,35 +1,32 @@ -/* No need to format 3rd-party compatibility headers. */ -/* clang-format off */ - // ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// // Copyright (c) 2006-2008 Alexander Chemeris -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. -// +// // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +// /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ From 641a5be50e03f4a7152dd37e97680bee26dc3e6f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 21 Oct 2021 12:55:15 +0200 Subject: [PATCH 1026/1500] IDManagement: Add option to clear asset data when making ID local. When appending an asset from the asset browser, its asset data needs to be cleared. However, linking an asset (or regular append from the file browser) should not clear such data. In linking case, it would be there again after a blend file reload anyway. So this commit introduces a new `BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR` option. NOTE: in case the appended ID needs to be copied from its linked data (instead of making the later directly local), asset data is lost anyway since it is never copied with the ID currently. Ref. {T91749} and D11768. --- source/blender/blenkernel/BKE_lib_id.h | 8 +++-- source/blender/blenkernel/intern/brush.c | 4 +-- source/blender/blenkernel/intern/lib_id.c | 32 ++++++++++++------- source/blender/blenkernel/intern/object.c | 4 +-- source/blender/blenloader/BLO_readfile.h | 2 ++ .../windowmanager/intern/wm_dragdrop.c | 4 ++- .../windowmanager/intern/wm_files_link.c | 14 +++++--- 7 files changed, 44 insertions(+), 24 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index d2a8ec2e332..d79df4b2216 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -245,6 +245,10 @@ enum { /** In case caller code already knows this ID should be made local using copying. */ LIB_ID_MAKELOCAL_FORCE_COPY = 1 << 2, + /** Clear asset data (in case the ID can actually be made local, in copy case asset data is never + * copied over). */ + LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR = 1 << 3, + /* Special type-specific options. */ /** For Objects, do not clear the proxy pointers while making the data-block local. */ LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING = 1 << 16, @@ -271,13 +275,13 @@ void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b); void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint); -void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id); +void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id, const int flags); bool BKE_id_new_name_validate(struct ListBase *lb, struct ID *id, const char *name, const bool do_linked_data) ATTR_NONNULL(1, 2); -void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id); +void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id, const int flags); /* Affect whole Main database. */ void BKE_main_id_tag_idcode(struct Main *mainvar, diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index d70b941695e..9facb146361 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -183,8 +183,8 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) } if (force_local) { - BKE_lib_id_clear_library_data(bmain, &brush->id); - BKE_lib_id_expand_local(bmain, &brush->id); + BKE_lib_id_clear_library_data(bmain, &brush->id, flags); + BKE_lib_id_expand_local(bmain, &brush->id, flags); /* enable fake user by default */ id_fake_user_set(&brush->id); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 3c85ba62d07..9e86ef0e3b7 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -152,8 +152,10 @@ static int lib_id_clear_library_data_users_update_cb(LibraryIDLinkCallbackData * /** * Pull an ID out of a library (make it local). Only call this for IDs that * don't have other library users. + * + * \param flags Same set of `LIB_ID_MAKELOCAL_` flags as passed to `BKE_lib_id_make_local`. */ -void BKE_lib_id_clear_library_data(Main *bmain, ID *id) +void BKE_lib_id_clear_library_data(Main *bmain, ID *id, const int flags) { const bool id_in_mainlist = (id->tag & LIB_TAG_NO_MAIN) == 0 && (id->flag & LIB_EMBEDDED_DATA) == 0; @@ -177,6 +179,10 @@ void BKE_lib_id_clear_library_data(Main *bmain, ID *id) BKE_lib_libblock_session_uuid_renew(id); } + if ((flags & LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR) != 0 && id->asset_data != NULL) { + BKE_asset_metadata_free(&id->asset_data); + } + /* We need to tag this IDs and all of its users, conceptually new local ID and original linked * ones are two completely different data-blocks that were virtually remapped, even though in * reality they remain the same data. For undo this info is critical now. */ @@ -193,7 +199,7 @@ void BKE_lib_id_clear_library_data(Main *bmain, ID *id) * IDs here, this is down automatically in `lib_id_expand_local_cb()`. */ Key *key = BKE_key_from_id(id); if (key != NULL) { - BKE_lib_id_clear_library_data(bmain, &key->id); + BKE_lib_id_clear_library_data(bmain, &key->id, flags); } DEG_relations_tag_update(bmain); @@ -372,6 +378,7 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) ID *id_self = cb_data->id_self; ID **id_pointer = cb_data->id_pointer; int const cb_flag = cb_data->cb_flag; + const int flags = POINTER_AS_INT(cb_data->user_data); if (cb_flag & IDWALK_CB_LOOPBACK) { /* We should never have anything to do with loop-back pointers here. */ @@ -386,7 +393,7 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) if (*id_pointer != NULL && ID_IS_LINKED(*id_pointer)) { BLI_assert(*id_pointer != id_self); - BKE_lib_id_clear_library_data(bmain, *id_pointer); + BKE_lib_id_clear_library_data(bmain, *id_pointer, flags); } return IDWALK_RET_NOP; } @@ -407,18 +414,19 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) * Expand ID usages of given id as 'extern' (and no more indirect) linked data. * Used by ID copy/make_local functions. */ -void BKE_lib_id_expand_local(Main *bmain, ID *id) +void BKE_lib_id_expand_local(Main *bmain, ID *id, const int flags) { - BKE_library_foreach_ID_link(bmain, id, lib_id_expand_local_cb, bmain, IDWALK_READONLY); + BKE_library_foreach_ID_link( + bmain, id, lib_id_expand_local_cb, POINTER_FROM_INT(flags), IDWALK_READONLY); } /** * Ensure new (copied) ID is fully made local. */ -static void lib_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id) +static void lib_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id, const int flags) { if (ID_IS_LINKED(old_id)) { - BKE_lib_id_expand_local(bmain, new_id); + BKE_lib_id_expand_local(bmain, new_id, flags); lib_id_library_local_paths(bmain, old_id->lib, new_id); } } @@ -459,8 +467,8 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) } if (force_local) { - BKE_lib_id_clear_library_data(bmain, id); - BKE_lib_id_expand_local(bmain, id); + BKE_lib_id_clear_library_data(bmain, id, flags); + BKE_lib_id_expand_local(bmain, id, flags); } else if (force_copy) { ID *id_new = BKE_id_copy(bmain, id); @@ -648,7 +656,7 @@ ID *BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag) * XXX TODO: is this behavior OK, or should we need own flag to control that? */ if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { BLI_assert((flag & LIB_ID_COPY_KEEP_LIB) == 0); - lib_id_copy_ensure_local(bmain, id, newid); + lib_id_copy_ensure_local(bmain, id, newid, 0); } else { newid->lib = id->lib; @@ -2046,8 +2054,8 @@ void BKE_library_make_local(Main *bmain, * currently there are some indirect usages. So instead of making a copy that we'll likely * get rid of later, directly make that data block local. * Saves a tremendous amount of time with complex scenes... */ - BKE_lib_id_clear_library_data(bmain, id); - BKE_lib_id_expand_local(bmain, id); + BKE_lib_id_clear_library_data(bmain, id, 0); + BKE_lib_id_expand_local(bmain, id, 0); id->tag &= ~LIB_TAG_DOIT; if (GS(id->name) == ID_OB) { diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 0d5fd6aadec..58041c84cdf 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -357,8 +357,8 @@ static void object_make_local(Main *bmain, ID *id, const int flags) } if (force_local) { - BKE_lib_id_clear_library_data(bmain, &ob->id); - BKE_lib_id_expand_local(bmain, &ob->id); + BKE_lib_id_clear_library_data(bmain, &ob->id, flags); + BKE_lib_id_expand_local(bmain, &ob->id, flags); if (clear_proxy) { if (ob->proxy_from != NULL) { ob->proxy_from->proxy = NULL; diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 4e1b2c68bd9..5a919ae3605 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -228,6 +228,8 @@ typedef enum eBLOLibLinkFlags { BLO_LIBLINK_APPEND_RECURSIVE = 1 << 20, /** Try to re-use previously appended matching ID on new append. */ BLO_LIBLINK_APPEND_LOCAL_ID_REUSE = 1 << 21, + /** Clear the asset data. */ + BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR = 1 << 22, /** Instantiate object data IDs (i.e. create objects for them if needed). */ BLO_LIBLINK_OBDATA_INSTANCE = 1 << 24, /** Instantiate collections as empties, instead of linking them into current view layer. */ diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 0e51619b50a..9af90355a79 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -466,7 +466,8 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) asset_drag->path, idtype, name, - BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION); + BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | + BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR); case FILE_ASSET_IMPORT_APPEND_REUSE: return WM_file_append_datablock(G_MAIN, scene, @@ -476,6 +477,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) idtype, name, BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | + BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR | BLO_LIBLINK_APPEND_LOCAL_ID_REUSE); } diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index d193c5663f0..c88e577df6a 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -630,6 +630,12 @@ static void wm_append_do(WMLinkAppendData *lapp_data, const bool set_fakeuser = (lapp_data->flag & BLO_LIBLINK_APPEND_SET_FAKEUSER) != 0; const bool do_reuse_local_id = (lapp_data->flag & BLO_LIBLINK_APPEND_LOCAL_ID_REUSE) != 0; + const int make_local_common_flags = LIB_ID_MAKELOCAL_FULL_LIBRARY | + ((lapp_data->flag & BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR) != + 0 ? + LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR : + 0); + LinkNode *itemlink; /* Generate a mapping between newly linked IDs and their items, and tag linked IDs used as @@ -731,16 +737,14 @@ static void wm_append_do(WMLinkAppendData *lapp_data, BLI_strncpy(lib_id_name, id->name, sizeof(lib_id_name)); switch (item->append_action) { - case WM_APPEND_ACT_COPY_LOCAL: { - BKE_lib_id_make_local( - bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_COPY); + case WM_APPEND_ACT_COPY_LOCAL: + BKE_lib_id_make_local(bmain, id, make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_COPY); local_appended_new_id = id->newid; break; - } case WM_APPEND_ACT_MAKE_LOCAL: BKE_lib_id_make_local(bmain, id, - LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_LOCAL | + make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); BLI_assert(id->newid == NULL); local_appended_new_id = id; From f6af3d9197b6685e9916f4d2d7e43a3434857d1f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Oct 2021 09:39:40 +0200 Subject: [PATCH 1027/1500] Build: remove cmake messages about disabled tests when feature is disabled No need to report this, it just adds noise to the cmake config. The messages that we need to keep are the ones about disabling tests when the test file or idiff are missing. --- tests/python/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index b761f310b1a..92d46eb8d80 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -753,20 +753,14 @@ set(geo_node_tests if(WITH_GMP) list(APPEND geo_node_tests mesh/boolean) -else() - MESSAGE(STATUS "Disabling mesh/boolean tests because WITH_GMP is off.") endif() if(WITH_OPENVDB) list(APPEND geo_node_tests volume) -else() - MESSAGE(STATUS "Disabling volume tests because WITH_OPENVDB is off.") endif() if(WITH_OPENSUBDIV) list(APPEND geo_node_tests mesh/subdivision_tests) -else() - MESSAGE(STATUS "Disabling mesh/subdivision_tests because WITH_OPENSUBDIV is off.") endif() foreach(geo_node_test ${geo_node_tests}) From 6ef8c9e6461456354c5e6b95a1d5cc7176723fe0 Mon Sep 17 00:00:00 2001 From: Yevgeny Makarov Date: Wed, 20 Oct 2021 03:47:13 +0200 Subject: [PATCH 1028/1500] Fix T65532: can't assign a key on Italian apple keyboards. Apple's international keyboards have an additional `kVK_ISO_Section` key. With some (Italian, Spanish) keyboard layouts, this is `\`, `[` keys which Blender keymap can use. Right now this key is explicitly set as `Unknown`. Note that `kVK_ANSI_Grave` is located in a different location. Differential Revision: https://developer.blender.org/D12905 --- intern/ghost/intern/GHOST_SystemCocoa.mm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 189e663f91a..204bbdaec50 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -116,8 +116,6 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) case kVK_ANSI_Z: return GHOST_kKeyZ; #endif /* Numbers keys: mapped to handle some int'l keyboard (e.g. French). */ - case kVK_ISO_Section: - return GHOST_kKeyUnknown; case kVK_ANSI_1: return GHOST_kKey1; case kVK_ANSI_2: @@ -257,6 +255,7 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) case kVK_ANSI_LeftBracket: return GHOST_kKeyLeftBracket; case kVK_ANSI_RightBracket: return GHOST_kKeyRightBracket; case kVK_ANSI_Grave: return GHOST_kKeyAccentGrave; + case kVK_ISO_Section: return GHOST_kKeyUnknown; #endif case kVK_VolumeUp: case kVK_VolumeDown: From 39810b3f5163cb934c87db4e623b2ae41901cef3 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 20 Oct 2021 20:40:32 +0200 Subject: [PATCH 1029/1500] Cleanup: make HIP and CUDA code more consistent Ref D12834 --- intern/cycles/device/cuda/queue.cpp | 14 ++++++++++---- intern/cycles/device/hip/queue.cpp | 17 +++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/intern/cycles/device/cuda/queue.cpp b/intern/cycles/device/cuda/queue.cpp index 1149a835b14..6b2c9a40082 100644 --- a/intern/cycles/device/cuda/queue.cpp +++ b/intern/cycles/device/cuda/queue.cpp @@ -41,13 +41,19 @@ CUDADeviceQueue::~CUDADeviceQueue() int CUDADeviceQueue::num_concurrent_states(const size_t state_size) const { - int num_states = max(cuda_device_->get_num_multiprocessors() * - cuda_device_->get_max_num_threads_per_multiprocessor() * 16, - 1048576); + const int max_num_threads = cuda_device_->get_num_multiprocessors() * + cuda_device_->get_max_num_threads_per_multiprocessor(); + int num_states = max(max_num_threads, 65536) * 16; const char *factor_str = getenv("CYCLES_CONCURRENT_STATES_FACTOR"); if (factor_str) { - num_states = max((int)(num_states * atof(factor_str)), 1024); + const float factor = (float)atof(factor_str); + if (factor != 0.0f) { + num_states = max((int)(num_states * factor), 1024); + } + else { + VLOG(3) << "CYCLES_CONCURRENT_STATES_FACTOR evaluated to 0"; + } } VLOG(3) << "GPU queue concurrent states: " << num_states << ", using up to " diff --git a/intern/cycles/device/hip/queue.cpp b/intern/cycles/device/hip/queue.cpp index 6cb29670f94..a612f59fb32 100644 --- a/intern/cycles/device/hip/queue.cpp +++ b/intern/cycles/device/hip/queue.cpp @@ -41,22 +41,19 @@ HIPDeviceQueue::~HIPDeviceQueue() int HIPDeviceQueue::num_concurrent_states(const size_t state_size) const { - int num_states = 0; const int max_num_threads = hip_device_->get_num_multiprocessors() * hip_device_->get_max_num_threads_per_multiprocessor(); - if (max_num_threads == 0) { - num_states = 1048576; // 65536 * 16 - } - else { - num_states = max_num_threads * 16; - } + int num_states = ((max_num_threads == 0) ? 65536 : max_num_threads) * 16; const char *factor_str = getenv("CYCLES_CONCURRENT_STATES_FACTOR"); if (factor_str) { - float factor = (float)atof(factor_str); - if (!factor) + const float factor = (float)atof(factor_str); + if (factor != 0.0f) { + num_states = max((int)(num_states * factor), 1024); + } + else { VLOG(3) << "CYCLES_CONCURRENT_STATES_FACTOR evaluated to 0"; - num_states = max((int)(num_states * factor), 1024); + } } VLOG(3) << "GPU queue concurrent states: " << num_states << ", using up to " From f81f56751aafa3ea5881a139ab178741833cc562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 21 Oct 2021 13:37:45 +0200 Subject: [PATCH 1030/1500] =?UTF-8?q?Deps:=20Bump=20Python=203.9.2=20?= =?UTF-8?q?=E2=86=92=203.9.7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump Python from 3.9.2 to 3.9.7, which is the latest 3.9 release at this moment. Updates to bundled Python packages will follow in a separate commit. Reviewed By: LazyDodo, mont29, brecht Differential Revision: https://developer.blender.org/D12777 --- build_files/build_environment/cmake/versions.cmake | 4 ++-- build_files/build_environment/install_deps.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index d1675bdddfd..d8faaf3c6f6 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -189,11 +189,11 @@ set(OSL_HASH 1abd7ce40481771a9fa937f19595d2f2) set(OSL_HASH_TYPE MD5) set(OSL_FILE OpenShadingLanguage-${OSL_VERSION}.tar.gz) -set(PYTHON_VERSION 3.9.2) +set(PYTHON_VERSION 3.9.7) set(PYTHON_SHORT_VERSION 3.9) set(PYTHON_SHORT_VERSION_NO_DOTS 39) set(PYTHON_URI https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz) -set(PYTHON_HASH f0dc9000312abeb16de4eccce9a870ab) +set(PYTHON_HASH fddb060b483bc01850a3f412eea1d954) set(PYTHON_HASH_TYPE MD5) set(PYTHON_FILE Python-${PYTHON_VERSION}.tar.xz) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index f910d8c12d9..e46e6d90c33 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -379,7 +379,7 @@ USE_CXX11=true CLANG_FORMAT_VERSION_MIN="6.0" CLANG_FORMAT_VERSION_MEX="10.0" -PYTHON_VERSION="3.9.2" +PYTHON_VERSION="3.9.7" PYTHON_VERSION_SHORT="3.9" PYTHON_VERSION_MIN="3.7" PYTHON_VERSION_MEX="3.11" From 417ce7ffc1bbb9da15e82b6b81537bc86831f21c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 21 Oct 2021 13:41:59 +0200 Subject: [PATCH 1031/1500] Deps: Python, bump bundled packages to their latest versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit certifi : 2020.12.5 → 2021.10.8 chardet : 4.0.0 → charset-normalizer 2.0.6 cython : 0.29.21 → 0.29.24 idna : 2.10 → 3.2 numpy : 1.19.5 → 1.21.2 (which makes it possible to remove our patch) requests: 2.25.1 → 2.26.0 urllib3 : 1.26.3 → 1.26.7 Nowadays `requests` no longer depends on `chardet` but on `charset-normalizer`. That project describes itself as: > A library that helps you read text from an unknown charset encoding. > Motivated by chardet, I'm trying to resolve the issue by taking a new > approach. All IANA character set names for which the Python core library > provides codecs are supported. Reviewed By: LazyDodo, mont29, brecht Differential Revision: https://developer.blender.org/D12777 --- .../build_environment/cmake/numpy.cmake | 1 - .../cmake/python_site_packages.cmake | 2 +- .../build_environment/cmake/versions.cmake | 18 +++++------ build_files/build_environment/install_deps.sh | 30 ++++++++++--------- .../build_environment/patches/numpy.diff | 27 ----------------- .../build_environment/windows/build_deps.cmd | 3 ++ 6 files changed, 29 insertions(+), 52 deletions(-) delete mode 100644 build_files/build_environment/patches/numpy.diff diff --git a/build_files/build_environment/cmake/numpy.cmake b/build_files/build_environment/cmake/numpy.cmake index 73fa6e75556..1b58fd6aee9 100644 --- a/build_files/build_environment/cmake/numpy.cmake +++ b/build_files/build_environment/cmake/numpy.cmake @@ -38,7 +38,6 @@ ExternalProject_Add(external_numpy PREFIX ${BUILD_DIR}/numpy PATCH_COMMAND ${NUMPY_PATCH} CONFIGURE_COMMAND "" - PATCH_COMMAND COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/numpy/src/external_numpy < ${PATCH_DIR}/numpy.diff LOG_BUILD 1 BUILD_COMMAND ${PYTHON_BINARY} ${BUILD_DIR}/numpy/src/external_numpy/setup.py build ${NUMPY_BUILD_OPTION} install --old-and-unmanageable INSTALL_COMMAND "" diff --git a/build_files/build_environment/cmake/python_site_packages.cmake b/build_files/build_environment/cmake/python_site_packages.cmake index 2b75a6a244f..6bc47fa5c45 100644 --- a/build_files/build_environment/cmake/python_site_packages.cmake +++ b/build_files/build_environment/cmake/python_site_packages.cmake @@ -25,7 +25,7 @@ ExternalProject_Add(external_python_site_packages CONFIGURE_COMMAND "" BUILD_COMMAND "" PREFIX ${BUILD_DIR}/site_packages - INSTALL_COMMAND ${PYTHON_BINARY} -m pip install ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} chardet==${CHARDET_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} --no-binary :all: + INSTALL_COMMAND ${PYTHON_BINARY} -m pip install ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} --no-binary :all: ) if(USE_PIP_NUMPY) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index d8faaf3c6f6..ec83de4866f 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -215,17 +215,17 @@ set(NANOVDB_HASH e7b9e863ec2f3b04ead171dec2322807) set(NANOVDB_HASH_TYPE MD5) set(NANOVDB_FILE nano-vdb-${NANOVDB_GIT_UID}.tar.gz) -set(IDNA_VERSION 2.10) -set(CHARDET_VERSION 4.0.0) -set(URLLIB3_VERSION 1.26.3) -set(CERTIFI_VERSION 2020.12.5) -set(REQUESTS_VERSION 2.25.1) -set(CYTHON_VERSION 0.29.21) +set(IDNA_VERSION 3.2) +set(CHARSET_NORMALIZER_VERSION 2.0.6) +set(URLLIB3_VERSION 1.26.7) +set(CERTIFI_VERSION 2021.10.8) +set(REQUESTS_VERSION 2.26.0) +set(CYTHON_VERSION 0.29.24) -set(NUMPY_VERSION 1.19.5) -set(NUMPY_SHORT_VERSION 1.19) +set(NUMPY_VERSION 1.21.2) +set(NUMPY_SHORT_VERSION 1.21) set(NUMPY_URI https://github.com/numpy/numpy/releases/download/v${NUMPY_VERSION}/numpy-${NUMPY_VERSION}.zip) -set(NUMPY_HASH f6a1b48717c552bbc18f1adc3cc1fe0e) +set(NUMPY_HASH 5638d5dae3ca387be562912312db842e) set(NUMPY_HASH_TYPE MD5) set(NUMPY_FILE numpy-${NUMPY_VERSION}.zip) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index e46e6d90c33..a278fabb6b8 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -389,24 +389,24 @@ PYTHON_FORCE_REBUILD=false PYTHON_SKIP=false # Additional Python modules. -PYTHON_IDNA_VERSION="2.9" +PYTHON_IDNA_VERSION="3.2" PYTHON_IDNA_VERSION_MIN="2.0" -PYTHON_IDNA_VERSION_MEX="3.0" +PYTHON_IDNA_VERSION_MEX="4.0" PYTHON_IDNA_NAME="idna" -PYTHON_CHARDET_VERSION="3.0.4" -PYTHON_CHARDET_VERSION_MIN="3.0" -PYTHON_CHARDET_VERSION_MEX="5.0" -PYTHON_CHARDET_NAME="chardet" +PYTHON_CHARSET_NORMALIZER_VERSION="2.0.6" +PYTHON_CHARSET_NORMALIZER_VERSION_MIN="2.0.6" +PYTHON_CHARSET_NORMALIZER_VERSION_MEX="2.1.0" # requests uses `charset_normalizer~=2.0.0` +PYTHON_CHARSET_NORMALIZER_NAME="charset-normalizer" -PYTHON_URLLIB3_VERSION="1.25.9" +PYTHON_URLLIB3_VERSION="1.26.7" PYTHON_URLLIB3_VERSION_MIN="1.0" PYTHON_URLLIB3_VERSION_MEX="2.0" PYTHON_URLLIB3_NAME="urllib3" -PYTHON_CERTIFI_VERSION="2020.4.5.2" -PYTHON_CERTIFI_VERSION_MIN="2020.0" -PYTHON_CERTIFI_VERSION_MEX="2021.0" +PYTHON_CERTIFI_VERSION="2021.10.8" +PYTHON_CERTIFI_VERSION_MIN="2021.0" +PYTHON_CERTIFI_VERSION_MEX="2023.0" PYTHON_CERTIFI_NAME="certifi" PYTHON_REQUESTS_VERSION="2.23.0" @@ -414,7 +414,7 @@ PYTHON_REQUESTS_VERSION_MIN="2.0" PYTHON_REQUESTS_VERSION_MEX="3.0" PYTHON_REQUESTS_NAME="requests" -PYTHON_NUMPY_VERSION="1.19.5" +PYTHON_NUMPY_VERSION="1.21.2" PYTHON_NUMPY_VERSION_MIN="1.14" PYTHON_NUMPY_VERSION_MEX="2.0" PYTHON_NUMPY_NAME="numpy" @@ -422,7 +422,7 @@ PYTHON_NUMPY_NAME="numpy" # As package-ready parameters (only used with distro packages). PYTHON_MODULES_PACKAGES=( "$PYTHON_IDNA_NAME $PYTHON_IDNA_VERSION_MIN $PYTHON_IDNA_VERSION_MEX" - "$PYTHON_CHARDET_NAME $PYTHON_CHARDET_VERSION_MIN $PYTHON_CHARDET_VERSION_MEX" + "$PYTHON_CHARSET_NORMALIZER_NAME $PYTHON_CHARSET_NORMALIZER_VERSION_MIN $PYTHON_CHARSET_NORMALIZER_VERSION_MEX" "$PYTHON_URLLIB3_NAME $PYTHON_URLLIB3_VERSION_MIN $PYTHON_URLLIB3_VERSION_MEX" "$PYTHON_CERTIFI_NAME $PYTHON_CERTIFI_VERSION_MIN $PYTHON_CERTIFI_VERSION_MEX" "$PYTHON_REQUESTS_NAME $PYTHON_REQUESTS_VERSION_MIN $PYTHON_REQUESTS_VERSION_MEX" @@ -432,10 +432,11 @@ PYTHON_MODULES_PACKAGES=( # As pip-ready parameters (only used when building python). PYTHON_MODULES_PIP=( "$PYTHON_IDNA_NAME==$PYTHON_IDNA_VERSION" - "$PYTHON_CHARDET_NAME==$PYTHON_CHARDET_VERSION" + "$PYTHON_CHARSET_NORMALIZER_NAME==$PYTHON_CHARSET_NORMALIZER_VERSION" "$PYTHON_URLLIB3_NAME==$PYTHON_URLLIB3_VERSION" "$PYTHON_CERTIFI_NAME==$PYTHON_CERTIFI_VERSION" "$PYTHON_REQUESTS_NAME==$PYTHON_REQUESTS_VERSION" + "$PYTHON_ZSTANDARD_NAME==$PYTHON_ZSTANDARD_VERSION" "$PYTHON_NUMPY_NAME==$PYTHON_NUMPY_VERSION" ) @@ -1141,10 +1142,11 @@ You may also want to build them yourself (optional ones are [between brackets]): * Python $PYTHON_VERSION (from $PYTHON_SOURCE). ** [IDNA $PYTHON_IDNA_VERSION] (use pip). - ** [Chardet $PYTHON_CHARDET_VERSION] (use pip). + ** [Charset Normalizer $PYTHON_CHARSET_NORMALIZER_VERSION] (use pip). ** [Urllib3 $PYTHON_URLLIB3_VERSION] (use pip). ** [Certifi $PYTHON_CERTIFI_VERSION] (use pip). ** [Requests $PYTHON_REQUESTS_VERSION] (use pip). + ** [ZStandard $PYTHON_ZSTANDARD_VERSION] (use pip). ** [NumPy $PYTHON_NUMPY_VERSION] (use pip). * Boost $BOOST_VERSION (from $BOOST_SOURCE, modules: $BOOST_BUILD_MODULES). * TBB $TBB_VERSION (from $TBB_SOURCE). diff --git a/build_files/build_environment/patches/numpy.diff b/build_files/build_environment/patches/numpy.diff deleted file mode 100644 index a9b783dd856..00000000000 --- a/build_files/build_environment/patches/numpy.diff +++ /dev/null @@ -1,27 +0,0 @@ -diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py -index ba2b1f4..b10f7df 100644 ---- a/numpy/distutils/system_info.py -+++ b/numpy/distutils/system_info.py -@@ -2164,8 +2164,8 @@ class accelerate_info(system_info): - 'accelerate' in libraries): - if intel: - args.extend(['-msse3']) -- else: -- args.extend(['-faltivec']) -+# else: -+# args.extend(['-faltivec']) - args.extend([ - '-I/System/Library/Frameworks/vecLib.framework/Headers']) - link_args.extend(['-Wl,-framework', '-Wl,Accelerate']) -@@ -2174,8 +2174,8 @@ class accelerate_info(system_info): - 'veclib' in libraries): - if intel: - args.extend(['-msse3']) -- else: -- args.extend(['-faltivec']) -+# else: -+# args.extend(['-faltivec']) - args.extend([ - '-I/System/Library/Frameworks/vecLib.framework/Headers']) - link_args.extend(['-Wl,-framework', '-Wl,vecLib']) - diff --git a/build_files/build_environment/windows/build_deps.cmd b/build_files/build_environment/windows/build_deps.cmd index 2159055f3bd..5174af8e20d 100644 --- a/build_files/build_environment/windows/build_deps.cmd +++ b/build_files/build_environment/windows/build_deps.cmd @@ -79,6 +79,9 @@ set STAGING=%BUILD_DIR%\S rem for python module build set MSSdk=1 set DISTUTILS_USE_SDK=1 +rem if you let pip pick its own build dirs, it'll stick it somewhere deep inside the user profile +rem and cython will refuse to link due to a path that gets too long. +set TMPDIR=c:\t\ rem for python externals source to be shared between the various archs and compilers mkdir %BUILD_DIR%\downloads\externals From a5917175d8c1a7cab83b401ae2f4affcd4ab8df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 21 Oct 2021 13:45:37 +0200 Subject: [PATCH 1032/1500] Deps: Python, bundle zstandard package This package allows Python scripts to handle compressed blend files (see rB2ea66af742bc). This is for example needed by Blender Asset Tracer to send files to a Flamenco render farm. This change includes a new `WITH_PYTHON_INSTALL_ZSTANDARD` build-time option, to control whether to actually install the package. For this the already-existing approach for Requests was copied. Reviewed By: LazyDodo, mont29, brecht Differential Revision: https://developer.blender.org/D12777 --- CMakeLists.txt | 11 +++++++++++ .../cmake/python_site_packages.cmake | 10 ++++++++-- .../build_environment/cmake/versions.cmake | 1 + build_files/build_environment/install_deps.sh | 6 ++++++ source/creator/CMakeLists.txt | 18 ++++++++++++++++++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ac11b495948..94a5ff27491 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -398,6 +398,10 @@ if(WITH_PYTHON_INSTALL) set(PYTHON_REQUESTS_PATH "" CACHE PATH "Path to python site-packages or dist-packages containing 'requests' module") mark_as_advanced(PYTHON_REQUESTS_PATH) endif() + + option(WITH_PYTHON_INSTALL_ZSTANDARD "Copy zstandard into the blender install folder" ON) + set(PYTHON_ZSTANDARD_PATH "" CACHE PATH "Path to python site-packages or dist-packages containing 'zstandard' module") + mark_as_advanced(PYTHON_ZSTANDARD_PATH) endif() option(WITH_CPU_SIMD "Enable SIMD instruction if they're detected on the host machine" ON) @@ -1734,6 +1738,12 @@ if(WITH_PYTHON) elseif(WITH_PYTHON_INSTALL_REQUESTS) find_python_package(requests "") endif() + + if(WIN32 OR APPLE) + # pass, we have this in lib/python/site-packages + elseif(WITH_PYTHON_INSTALL_ZSTANDARD) + find_python_package(zstandard "") + endif() endif() # Select C++17 as the standard for C++ projects. @@ -2005,6 +2015,7 @@ if(FIRST_RUN) endif() info_cfg_option(WITH_PYTHON_INSTALL) info_cfg_option(WITH_PYTHON_INSTALL_NUMPY) + info_cfg_option(WITH_PYTHON_INSTALL_ZSTANDARD) info_cfg_option(WITH_PYTHON_MODULE) info_cfg_option(WITH_PYTHON_SAFETY) diff --git a/build_files/build_environment/cmake/python_site_packages.cmake b/build_files/build_environment/cmake/python_site_packages.cmake index 6bc47fa5c45..a8918fdb784 100644 --- a/build_files/build_environment/cmake/python_site_packages.cmake +++ b/build_files/build_environment/cmake/python_site_packages.cmake @@ -18,14 +18,20 @@ if(WIN32 AND BUILD_MODE STREQUAL Debug) set(SITE_PACKAGES_EXTRA --global-option build --global-option --debug) + # zstandard is determined to build and link release mode libs in a debug + # configuration, the only way to make it happy is to bend to its will + # and give it a library to link with. + set(PIP_CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/python/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}_d.lib ${LIBDIR}/python/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}.lib) +else() + set(PIP_CONFIGURE_COMMAND echo ".") endif() ExternalProject_Add(external_python_site_packages DOWNLOAD_COMMAND "" - CONFIGURE_COMMAND "" + CONFIGURE_COMMAND ${PIP_CONFIGURE_COMMAND} BUILD_COMMAND "" PREFIX ${BUILD_DIR}/site_packages - INSTALL_COMMAND ${PYTHON_BINARY} -m pip install ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} --no-binary :all: + INSTALL_COMMAND ${PYTHON_BINARY} -m pip install ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} --no-binary :all: ) if(USE_PIP_NUMPY) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index ec83de4866f..f2c245dc380 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -221,6 +221,7 @@ set(URLLIB3_VERSION 1.26.7) set(CERTIFI_VERSION 2021.10.8) set(REQUESTS_VERSION 2.26.0) set(CYTHON_VERSION 0.29.24) +set(ZSTANDARD_VERSION 0.15.2 ) set(NUMPY_VERSION 1.21.2) set(NUMPY_SHORT_VERSION 1.21) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index a278fabb6b8..2bb7175da00 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -414,6 +414,11 @@ PYTHON_REQUESTS_VERSION_MIN="2.0" PYTHON_REQUESTS_VERSION_MEX="3.0" PYTHON_REQUESTS_NAME="requests" +PYTHON_ZSTANDARD_VERSION="0.15.2" +PYTHON_ZSTANDARD_VERSION_MIN="0.15.2" +PYTHON_ZSTANDARD_VERSION_MEX="0.16.0" +PYTHON_ZSTANDARD_NAME="zstandard" + PYTHON_NUMPY_VERSION="1.21.2" PYTHON_NUMPY_VERSION_MIN="1.14" PYTHON_NUMPY_VERSION_MEX="2.0" @@ -426,6 +431,7 @@ PYTHON_MODULES_PACKAGES=( "$PYTHON_URLLIB3_NAME $PYTHON_URLLIB3_VERSION_MIN $PYTHON_URLLIB3_VERSION_MEX" "$PYTHON_CERTIFI_NAME $PYTHON_CERTIFI_VERSION_MIN $PYTHON_CERTIFI_VERSION_MEX" "$PYTHON_REQUESTS_NAME $PYTHON_REQUESTS_VERSION_MIN $PYTHON_REQUESTS_VERSION_MEX" + "$PYTHON_ZSTANDARD_NAME $PYTHON_ZSTANDARD_VERSION_MIN $PYTHON_ZSTANDARD_VERSION_MEX" "$PYTHON_NUMPY_NAME $PYTHON_NUMPY_VERSION_MIN $PYTHON_NUMPY_VERSION_MEX" ) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 1d5d1491c7a..5ee9a3f4342 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -642,6 +642,24 @@ if(UNIX AND NOT APPLE) unset(_suffix) endif() + if(WITH_PYTHON_INSTALL_ZSTANDARD) + # Install to the same directory as the source, so debian-like + # distros are happy with their policy. + set(_suffix "site-packages") + if(${PYTHON_NUMPY_PATH} MATCHES "dist-packages") + set(_suffix "dist-packages") + endif() + install( + DIRECTORY ${PYTHON_NUMPY_PATH}/zstandard + DESTINATION ${TARGETDIR_VER}/python/${_target_LIB}/python${PYTHON_VERSION}/${_suffix} + PATTERN ".svn" EXCLUDE + PATTERN "__pycache__" EXCLUDE # * any cache * + PATTERN "*.pyc" EXCLUDE # * any cache * + PATTERN "*.pyo" EXCLUDE # * any cache * + ) + unset(_suffix) + endif() + # Copy requests, we need to generalize site-packages if(WITH_PYTHON_INSTALL_REQUESTS) set(_suffix "site-packages") From ab0195c78fa20b1c1f8abd1474f0fee07ffb6c8c Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 20 Oct 2021 17:27:40 +0200 Subject: [PATCH 1033/1500] Fix T92169: "View Selected" on Custom Boneshape with transform wrong Since the introduction in rBfc5bf09fd88c, `BKE_pose_minmax` was not taking these custom transforms into account (making "View Selected" ignoring these as well and focusing on the bone instead). Now consider these transforms in `BKE_pose_minmax`. Maniphest Tasks: T92169 Differential Revision: https://developer.blender.org/D12942 --- source/blender/blenkernel/intern/armature.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 0fa4c6e47e8..65fdc9278e1 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2970,10 +2970,16 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden BKE_object_boundbox_get(pchan->custom) : NULL; if (bb_custom) { - float mat[4][4], smat[4][4]; + float mat[4][4], smat[4][4], rmat[4][4], tmp[4][4]; scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan)); rescale_m4(smat, pchan->custom_scale_xyz); - mul_m4_series(mat, ob->obmat, pchan_tx->pose_mat, smat); + eulO_to_mat4(rmat, pchan->custom_rotation_euler, ROT_MODE_XYZ); + copy_m4_m4(tmp, pchan_tx->pose_mat); + translate_m4(tmp, + pchan->custom_translation[0], + pchan->custom_translation[1], + pchan->custom_translation[2]); + mul_m4_series(mat, ob->obmat, tmp, rmat, smat); BKE_boundbox_minmax(bb_custom, mat, r_min, r_max); } else { From 91b4c1841a9b961a7361eb48bd349a674e0b61ae Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 21 Oct 2021 11:42:16 +0200 Subject: [PATCH 1034/1500] Fix T92272: Rigid Body Copy to Selected "Margin" crash Caused by {rB43167a2c251b} Code from above commit called RNA updates with a NULL scene. This was already commented (and mostly handled) in rB5949d598bc33, but the reported case was missing in that commit. This fixes the crash in a similar manner as rB5949d598bc33. Maniphest Tasks: T92272 Differential Revision: https://developer.blender.org/D12953 --- source/blender/makesrna/intern/rna_rigidbody.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c index c51931d0d1a..c0fb904101d 100644 --- a/source/blender/makesrna/intern/rna_rigidbody.c +++ b/source/blender/makesrna/intern/rna_rigidbody.c @@ -233,10 +233,12 @@ static void rna_RigidBodyOb_shape_update(Main *bmain, Scene *scene, PointerRNA * static void rna_RigidBodyOb_shape_reset(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) { - RigidBodyWorld *rbw = scene->rigidbody_world; - RigidBodyOb *rbo = (RigidBodyOb *)ptr->data; + if (scene != NULL) { + RigidBodyWorld *rbw = scene->rigidbody_world; + BKE_rigidbody_cache_reset(rbw); + } - BKE_rigidbody_cache_reset(rbw); + RigidBodyOb *rbo = (RigidBodyOb *)ptr->data; if (rbo->shared->physics_shape) { rbo->flag |= RBO_FLAG_NEEDS_RESHAPE; } From 10fb5cc58d210592ef0ae410e305be906ad5ce51 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Thu, 21 Oct 2021 15:13:55 +0200 Subject: [PATCH 1035/1500] Fix T92355: Quadriflow crashes with zero length edges Add a check for zero length edges to the manifold check as quadriflow doesn't handle meshes with these. --- source/blender/editors/object/object_remesh.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index d56cb3c7548..3bdf7e0d34d 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -708,12 +708,19 @@ static bool mesh_is_manifold_consistent(Mesh *mesh) } if (is_manifold_consistent) { - /* check for wire edges */ for (uint i = 0; i < mesh->totedge; i++) { + /* Check for wire edges. */ if (edge_faces[i] == 0) { is_manifold_consistent = false; break; } + /* Check for zero length edges */ + MVert *v1 = &mesh->mvert[mesh->medge[i].v1]; + MVert *v2 = &mesh->mvert[mesh->medge[i].v2]; + if (compare_v3v3(v1->co, v2->co, 1e-4f)) { + is_manifold_consistent = false; + break; + } } } From 7681326acdd66ecad9f85f57d1d14858b6223e51 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 21 Oct 2021 15:04:39 +0200 Subject: [PATCH 1036/1500] IDManagement: Assign current Main's lib to newly created IDs. This is mainly for doversion code, when it needs to create new IDs those should be considered as part of the same library as the current Main's one. No practical changes are expected here, this is more of a general consistency fix, and a pre-requisite for {T92333}. --- source/blender/blenkernel/intern/lib_id.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 9e86ef0e3b7..bc0a8e2b9b8 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1141,6 +1141,11 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); + /* This is important in 'readfile doversion after liblink' context mainly, but is a good + * consistency change in general: ID created for a Main should get that main's current + * library pointer. */ + id->lib = bmain->curlib; + /* TODO: to be removed from here! */ if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0) { DEG_id_type_tag(bmain, type); From fd560ef2af6aef06e6dad00854bfdd3fd81a8d6f Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Thu, 21 Oct 2021 07:36:21 -0600 Subject: [PATCH 1037/1500] Windows: Fix finding python for build helpers It was still looking for the 3.7 folder rather than 3.9 --- build_files/windows/find_dependencies.cmd | 2 +- build_files/windows/format.cmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build_files/windows/find_dependencies.cmd b/build_files/windows/find_dependencies.cmd index 0b6ae2d3c32..9fa3b156a4f 100644 --- a/build_files/windows/find_dependencies.cmd +++ b/build_files/windows/find_dependencies.cmd @@ -3,7 +3,7 @@ for %%X in (svn.exe) do (set SVN=%%~$PATH:X) for %%X in (cmake.exe) do (set CMAKE=%%~$PATH:X) for %%X in (ctest.exe) do (set CTEST=%%~$PATH:X) for %%X in (git.exe) do (set GIT=%%~$PATH:X) -set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\37\bin\python.exe +set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe if NOT "%verbose%" == "" ( echo svn : "%SVN%" echo cmake : "%CMAKE%" diff --git a/build_files/windows/format.cmd b/build_files/windows/format.cmd index d19595bf042..d5003c9f8d8 100644 --- a/build_files/windows/format.cmd +++ b/build_files/windows/format.cmd @@ -10,7 +10,7 @@ exit /b 1 echo found clang-format in %CF_PATH% if EXIST %PYTHON% ( - set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\37\bin\python.exe + set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe goto detect_python_done ) From df004637643241136a3294a63c7d4ca865cdea98 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 21 Oct 2021 15:14:30 +0200 Subject: [PATCH 1038/1500] Cycles: add shadow path compaction for GPU rendering Similar to main path compaction that happens before adding work tiles, this compacts shadow paths before launching kernels that may add shadow paths. Only do it when more than 50% of space is wasted. It's not a clear win in all scenes, some are up to 1.5% slower. Likely caused by different order of scheduling kernels having an unpredictable performance impact. Still feels like compaction is just the right thing to avoid cases where a few shadow paths can hold up a lot of main paths. Differential Revision: https://developer.blender.org/D12944 --- intern/cycles/device/cuda/queue.cpp | 2 + intern/cycles/device/device_kernel.cpp | 6 ++ intern/cycles/device/hip/queue.cpp | 2 + .../cycles/integrator/path_trace_work_gpu.cpp | 102 ++++++++++++------ .../cycles/integrator/path_trace_work_gpu.h | 8 +- intern/cycles/kernel/device/gpu/kernel.h | 41 +++++++ .../kernel/integrator/integrator_state_util.h | 56 ++++++++++ intern/cycles/kernel/kernel_types.h | 3 + 8 files changed, 188 insertions(+), 32 deletions(-) diff --git a/intern/cycles/device/cuda/queue.cpp b/intern/cycles/device/cuda/queue.cpp index 6b2c9a40082..09352a84181 100644 --- a/intern/cycles/device/cuda/queue.cpp +++ b/intern/cycles/device/cuda/queue.cpp @@ -113,6 +113,8 @@ bool CUDADeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *ar case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY: case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY: case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY: /* See parall_active_index.h for why this amount of shared memory is needed. */ shared_mem_bytes = (num_threads_per_block + 1) * sizeof(int); break; diff --git a/intern/cycles/device/device_kernel.cpp b/intern/cycles/device/device_kernel.cpp index e0833331b77..1e282aac57e 100644 --- a/intern/cycles/device/device_kernel.cpp +++ b/intern/cycles/device/device_kernel.cpp @@ -64,6 +64,12 @@ const char *device_kernel_as_string(DeviceKernel kernel) return "integrator_compact_paths_array"; case DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES: return "integrator_compact_states"; + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY: + return "integrator_terminated_shadow_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY: + return "integrator_compact_shadow_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_STATES: + return "integrator_compact_shadow_states"; case DEVICE_KERNEL_INTEGRATOR_RESET: return "integrator_reset"; case DEVICE_KERNEL_INTEGRATOR_SHADOW_CATCHER_COUNT_POSSIBLE_SPLITS: diff --git a/intern/cycles/device/hip/queue.cpp b/intern/cycles/device/hip/queue.cpp index a612f59fb32..0f053ccbeb5 100644 --- a/intern/cycles/device/hip/queue.cpp +++ b/intern/cycles/device/hip/queue.cpp @@ -113,6 +113,8 @@ bool HIPDeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *arg case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY: case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY: case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY: /* See parall_active_index.h for why this amount of shared memory is needed. */ shared_mem_bytes = (num_threads_per_block + 1) * sizeof(int); break; diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 2c71b1cf876..36f275e1075 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -361,26 +361,13 @@ bool PathTraceWorkGPU::enqueue_path_iteration() return false; } - /* If the number of shadow kernels dropped to zero, set the next shadow path - * index to zero as well. - * - * TODO: use shadow path compaction to lower it more often instead of letting - * it fill up entirely? */ - const int num_queued_shadow = - queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW] + - queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW]; - if (num_queued_shadow == 0) { - if (integrator_next_shadow_path_index_.data()[0] != 0) { - integrator_next_shadow_path_index_.data()[0] = 0; - queue_->copy_to_device(integrator_next_shadow_path_index_); - } - } - /* For kernels that add shadow paths, check if there is enough space available. * If not, schedule shadow kernels first to clear out the shadow paths. */ int num_paths_limit = INT_MAX; if (kernel_creates_shadow_paths(kernel)) { + compact_shadow_paths(); + const int available_shadow_paths = max_num_paths_ - integrator_next_shadow_path_index_.data()[0]; if (available_shadow_paths < queue_counter->num_queued[kernel]) { @@ -535,18 +522,76 @@ void PathTraceWorkGPU::compute_queued_paths(DeviceKernel kernel, DeviceKernel qu queue_->enqueue(kernel, work_size, args); } -void PathTraceWorkGPU::compact_states(const int num_active_paths) +void PathTraceWorkGPU::compact_main_paths(const int num_active_paths) { + /* Early out if there is nothing that needs to be compacted. */ if (num_active_paths == 0) { max_active_main_path_index_ = 0; - } - - /* Compact fragmented path states into the start of the array, moving any paths - * with index higher than the number of active paths into the gaps. */ - if (max_active_main_path_index_ == num_active_paths) { return; } + const int min_compact_paths = 32; + if (max_active_main_path_index_ == num_active_paths || + max_active_main_path_index_ < min_compact_paths) { + return; + } + + /* Compact. */ + compact_paths(num_active_paths, + max_active_main_path_index_, + DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES); + + /* Adjust max active path index now we know which part of the array is actually used. */ + max_active_main_path_index_ = num_active_paths; +} + +void PathTraceWorkGPU::compact_shadow_paths() +{ + IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); + const int num_active_paths = + queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW] + + queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW]; + + /* Early out if there is nothing that needs to be compacted. */ + if (num_active_paths == 0) { + if (integrator_next_shadow_path_index_.data()[0] != 0) { + integrator_next_shadow_path_index_.data()[0] = 0; + queue_->copy_to_device(integrator_next_shadow_path_index_); + } + return; + } + + /* Compact if we can reduce the space used by half. Not always since + * compaction has a cost. */ + const float shadow_compact_ratio = 0.5f; + const int min_compact_paths = 32; + if (integrator_next_shadow_path_index_.data()[0] < num_active_paths * shadow_compact_ratio || + integrator_next_shadow_path_index_.data()[0] < min_compact_paths) { + return; + } + + /* Compact. */ + compact_paths(num_active_paths, + integrator_next_shadow_path_index_.data()[0], + DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_STATES); + + /* Adjust max active path index now we know which part of the array is actually used. */ + integrator_next_shadow_path_index_.data()[0] = num_active_paths; + queue_->copy_to_device(integrator_next_shadow_path_index_); +} + +void PathTraceWorkGPU::compact_paths(const int num_active_paths, + const int max_active_path_index, + DeviceKernel terminated_paths_kernel, + DeviceKernel compact_paths_kernel, + DeviceKernel compact_kernel) +{ + /* Compact fragmented path states into the start of the array, moving any paths + * with index higher than the number of active paths into the gaps. */ void *d_compact_paths = (void *)queued_paths_.device_pointer; void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; @@ -557,17 +602,17 @@ void PathTraceWorkGPU::compact_states(const int num_active_paths) int work_size = num_active_paths; void *args[] = {&work_size, &d_compact_paths, &d_num_queued_paths, &offset}; queue_->zero_to_device(num_queued_paths_); - queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY, work_size, args); + queue_->enqueue(terminated_paths_kernel, work_size, args); } /* Create array of paths that we need to compact, where the path index is bigger * than the number of active paths. */ { - int work_size = max_active_main_path_index_; + int work_size = max_active_path_index; void *args[] = { &work_size, &d_compact_paths, &d_num_queued_paths, const_cast(&num_active_paths)}; queue_->zero_to_device(num_queued_paths_); - queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY, work_size, args); + queue_->enqueue(compact_paths_kernel, work_size, args); } queue_->copy_from_device(num_queued_paths_); @@ -582,13 +627,8 @@ void PathTraceWorkGPU::compact_states(const int num_active_paths) int terminated_states_offset = num_active_paths; void *args[] = { &d_compact_paths, &active_states_offset, &terminated_states_offset, &work_size}; - queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES, work_size, args); + queue_->enqueue(compact_kernel, work_size, args); } - - queue_->synchronize(); - - /* Adjust max active path index now we know which part of the array is actually used. */ - max_active_main_path_index_ = num_active_paths; } bool PathTraceWorkGPU::enqueue_work_tiles(bool &finished) @@ -669,7 +709,7 @@ bool PathTraceWorkGPU::enqueue_work_tiles(bool &finished) /* Compact state array when number of paths becomes small relative to the * known maximum path index, which makes computing active index arrays slow. */ - compact_states(num_active_paths); + compact_main_paths(num_active_paths); if (has_shadow_catcher()) { integrator_next_main_path_index_.data()[0] = num_paths; diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h index e16c491695b..8734d2c2852 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.h +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -86,7 +86,13 @@ class PathTraceWorkGPU : public PathTraceWork { DeviceKernel queued_kernel, const int num_paths_limit); - void compact_states(const int num_active_paths); + void compact_main_paths(const int num_active_paths); + void compact_shadow_paths(); + void compact_paths(const int num_active_paths, + const int max_active_path_index, + DeviceKernel terminated_paths_kernel, + DeviceKernel compact_paths_kernel, + DeviceKernel compact_kernel); int num_active_main_paths_paths(); diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index fcb398f7e6d..eeac09d4b29 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -281,6 +281,18 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_B }); } +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_terminated_shadow_paths_array(int num_states, + int *indices, + int *num_indices, + int indices_offset) +{ + gpu_parallel_active_index_array( + num_states, indices + indices_offset, num_indices, [](const int state) { + return (INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0); + }); +} + extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE) kernel_gpu_integrator_sorted_paths_array(int num_states, int num_states_limit, @@ -332,6 +344,35 @@ extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_B } } +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_compact_shadow_paths_array(int num_states, + int *indices, + int *num_indices, + int num_active_paths) +{ + gpu_parallel_active_index_array( + num_states, indices, num_indices, [num_active_paths](const int state) { + return (state >= num_active_paths) && + (INTEGRATOR_STATE(state, shadow_path, queued_kernel) != 0); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_compact_shadow_states(const int *active_terminated_states, + const int active_states_offset, + const int terminated_states_offset, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int from_state = active_terminated_states[active_states_offset + global_index]; + const int to_state = active_terminated_states[terminated_states_offset + global_index]; + + integrator_shadow_state_move(NULL, to_state, from_state); + } +} + extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE) kernel_gpu_prefix_sum(int *counter, int *prefix_sum, int num_values) { diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index 6da41cddcf8..6e6b7f8a40f 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -265,6 +265,62 @@ ccl_device_inline void integrator_state_move(KernelGlobals kg, INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; } +ccl_device_inline void integrator_shadow_state_copy_only(KernelGlobals kg, + ConstIntegratorShadowState to_state, + ConstIntegratorShadowState state) +{ + int index; + + /* Rely on the compiler to optimize out unused assignments and `while(false)`'s. */ + +# define KERNEL_STRUCT_BEGIN(name) \ + index = 0; \ + do { + +# define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) \ + if (kernel_integrator_state.parent_struct.name != nullptr) { \ + kernel_integrator_state.parent_struct.name[to_state] = \ + kernel_integrator_state.parent_struct.name[state]; \ + } + +# define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) \ + if (kernel_integrator_state.parent_struct[index].name != nullptr) { \ + kernel_integrator_state.parent_struct[index].name[to_state] = \ + kernel_integrator_state.parent_struct[index].name[state]; \ + } + +# define KERNEL_STRUCT_END(name) \ + } \ + while (false) \ + ; + +# define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \ + ++index; \ + } \ + while (index < gpu_array_size) \ + ; + +# define KERNEL_STRUCT_VOLUME_STACK_SIZE kernel_data.volume_stack_size + +# include "kernel/integrator/integrator_shadow_state_template.h" + +# undef KERNEL_STRUCT_BEGIN +# undef KERNEL_STRUCT_MEMBER +# undef KERNEL_STRUCT_ARRAY_MEMBER +# undef KERNEL_STRUCT_END +# undef KERNEL_STRUCT_END_ARRAY +# undef KERNEL_STRUCT_VOLUME_STACK_SIZE +} + +ccl_device_inline void integrator_shadow_state_move(KernelGlobals kg, + ConstIntegratorState to_state, + ConstIntegratorState state) +{ + integrator_shadow_state_copy_only(kg, to_state, state); + + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; +} + #endif /* NOTE: Leaves kernel scheduling information untouched. Use INIT semantic for one of the paths diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 4bdd8185ca6..5cbe2939dfc 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1458,6 +1458,9 @@ typedef enum DeviceKernel { DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY, DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY, DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES, + DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_STATES, DEVICE_KERNEL_INTEGRATOR_RESET, DEVICE_KERNEL_INTEGRATOR_SHADOW_CATCHER_COUNT_POSSIBLE_SPLITS, From 6600ae3aa707d7e1c2728b9af50dc15e81fd7f2b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 21 Oct 2021 15:38:41 +0200 Subject: [PATCH 1039/1500] Nodes: add utility to find NodeRef for node In the future `NodeTreeRef` could have a lazy initialized map, but for now this is good enough. --- source/blender/nodes/NOD_node_tree_ref.hh | 2 ++ source/blender/nodes/intern/node_tree_ref.cc | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index 5337f79536b..b6e372470c8 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -275,6 +275,8 @@ class NodeTreeRef : NonCopyable, NonMovable { Span links() const; + const NodeRef *find_node(const bNode &bnode) const; + bool has_link_cycles() const; bool has_undefined_nodes_or_sockets() const; diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 43c7fbd2599..2ca797009da 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -576,6 +576,16 @@ Vector NodeTreeRef::toposort(const ToposortDirection direction) return toposort; } +const NodeRef *NodeTreeRef::find_node(const bNode &bnode) const +{ + for (const NodeRef *node : this->nodes_by_type(bnode.typeinfo)) { + if (node->bnode_ == &bnode) { + return node; + } + } + return nullptr; +} + std::string NodeTreeRef::to_dot() const { dot::DirectedGraph digraph; From 090be2775e3d7c0dc3f7956b05c7fb9a72c4bdfc Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 21 Oct 2021 15:49:29 +0200 Subject: [PATCH 1040/1500] Geometry Nodes: fix force-computing multiple non-output sockets There were some issues when multiple inputs of the same node were forced to be computed (e.g. for the spreadsheet), but none of the node outputs (if existant) were used. Essentially the node was marked as "finished" too early in this case. This fix is necessary for the improved viewer node (T92167). --- source/blender/modifiers/intern/MOD_nodes.cc | 37 +++++------ .../modifiers/intern/MOD_nodes_evaluator.cc | 61 +++++++++++++------ .../nodes/NOD_geometry_nodes_eval_log.hh | 14 +++-- .../nodes/intern/geometry_nodes_eval_log.cc | 2 +- 4 files changed, 69 insertions(+), 45 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index b5ca5f3545f..562589e2610 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -758,32 +758,33 @@ static Vector find_spreadsheet_editors(Main *bmain) return spreadsheets; } -static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadsheet, - NodesModifierData *nmd, - const ModifierEvalContext *ctx, - const DerivedNodeTree &tree) +static void find_sockets_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadsheet, + NodesModifierData *nmd, + const ModifierEvalContext *ctx, + const DerivedNodeTree &tree, + Set &r_sockets_to_preview) { Vector context_path = sspreadsheet->context_path; if (context_path.size() < 3) { - return {}; + return; } if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { - return {}; + return; } if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { - return {}; + return; } SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0]; if (object_context->object != DEG_get_original_object(ctx->object)) { - return {}; + return; } SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context_path[1]; if (StringRef(modifier_context->modifier_name) != nmd->modifier.name) { - return {}; + return; } for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) { if (context->type != SPREADSHEET_CONTEXT_NODE) { - return {}; + return; } } @@ -802,11 +803,11 @@ static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspre } } if (found_node == nullptr) { - return {}; + return; } context = context->child_context(*found_node); if (context == nullptr) { - return {}; + return; } } @@ -814,10 +815,13 @@ static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspre for (const NodeRef *node_ref : tree_ref.nodes_by_type("GeometryNodeViewer")) { if (node_ref->name() == last_context->node_name) { const DNode viewer_node{context, node_ref}; - return viewer_node.input(0); + for (const InputSocketRef *input_socket : node_ref->inputs()) { + if (input_socket->is_available() && input_socket->is_logically_linked()) { + r_sockets_to_preview.add(DSocket{context, input_socket}); + } + } } } - return {}; } static void find_sockets_to_preview(NodesModifierData *nmd, @@ -831,10 +835,7 @@ static void find_sockets_to_preview(NodesModifierData *nmd, * intermediate geometries cached for display. */ Vector spreadsheets = find_spreadsheet_editors(bmain); for (SpaceSpreadsheet *sspreadsheet : spreadsheets) { - const DSocket socket = try_get_socket_to_preview_for_spreadsheet(sspreadsheet, nmd, ctx, tree); - if (socket) { - r_sockets_to_preview.add(socket); - } + find_sockets_to_preview_for_spreadsheet(sspreadsheet, nmd, ctx, tree, r_sockets_to_preview); } } diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 51dd210c77a..69132f6c177 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -126,6 +126,12 @@ struct InputState { * changed by others anymore. */ bool was_ready_for_execution = false; + + /** + * True when this input has to be computed for logging/debugging purposes, regardless of whether + * it is needed for some output. + */ + bool force_compute = false; }; struct OutputState { @@ -497,6 +503,14 @@ class GeometryNodesEvaluator { this->initialize_node_state(item.node, *item.state, allocator); } }); + + /* Mark input sockets that have to be computed. */ + for (const DSocket &socket : params_.force_compute_sockets) { + NodeState &node_state = *node_states_.lookup_key_as(socket.node()).state; + if (socket->is_input()) { + node_state.inputs[socket->index()].force_compute = true; + } + } } void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator) @@ -743,7 +757,8 @@ class GeometryNodesEvaluator { return do_execute_node; } - /* A node is finished when it has computed all outputs that may be used. */ + /* A node is finished when it has computed all outputs that may be used have been computed and + * when no input is still forced to be computed. */ bool finish_node_if_possible(LockedNode &locked_node) { if (locked_node.node_state.node_has_finished) { @@ -752,35 +767,41 @@ class GeometryNodesEvaluator { } /* Check if there is any output that might be used but has not been computed yet. */ - bool has_remaining_output = false; for (OutputState &output_state : locked_node.node_state.outputs) { if (output_state.has_been_computed) { continue; } if (output_state.output_usage != ValueUsage::Unused) { - has_remaining_output = true; - break; + return false; } } - if (!has_remaining_output) { - /* If there are no remaining outputs, all the inputs can be destructed and/or can become - * unused. This can also trigger a chain reaction where nodes to the left become finished - * too. */ - for (const int i : locked_node.node->inputs().index_range()) { - const DInputSocket socket = locked_node.node.input(i); - InputState &input_state = locked_node.node_state.inputs[i]; - if (input_state.usage == ValueUsage::Maybe) { - this->set_input_unused(locked_node, socket); - } - else if (input_state.usage == ValueUsage::Required) { - /* The value was required, so it cannot become unused. However, we can destruct the - * value. */ - this->destruct_input_value_if_exists(locked_node, socket); + + /* Check if there is an input that still has to be computed. */ + for (InputState &input_state : locked_node.node_state.inputs) { + if (input_state.force_compute) { + if (!input_state.was_ready_for_execution) { + return false; } } - locked_node.node_state.node_has_finished = true; } - return locked_node.node_state.node_has_finished; + + /* If there are no remaining outputs, all the inputs can be destructed and/or can become + * unused. This can also trigger a chain reaction where nodes to the left become finished + * too. */ + for (const int i : locked_node.node->inputs().index_range()) { + const DInputSocket socket = locked_node.node.input(i); + InputState &input_state = locked_node.node_state.inputs[i]; + if (input_state.usage == ValueUsage::Maybe) { + this->set_input_unused(locked_node, socket); + } + else if (input_state.usage == ValueUsage::Required) { + /* The value was required, so it cannot become unused. However, we can destruct the + * value. */ + this->destruct_input_value_if_exists(locked_node, socket); + } + } + locked_node.node_state.node_has_finished = true; + return true; } bool prepare_node_outputs_for_execution(LockedNode &locked_node) diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index ff8e137e341..ffe2a0d63c3 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -181,17 +181,19 @@ class LocalGeoLogger { /** The root logger class. */ class GeoLogger { private: - /** The entire geometry of sockets in this set should be cached, because e.g. the spreadsheet - * displays the data. We don't log the entire geometry at all places, because that would require - * way too much memory. */ - Set log_full_geometry_sockets_; + /** + * Log the entire value for these sockets, because they may be inspected afterwards. + * We don't log everything, because that would take up too much memory and cause significant + * slowdowns. + */ + Set log_full_sockets_; threading::EnumerableThreadSpecific threadlocals_; friend LocalGeoLogger; public: - GeoLogger(Set log_full_geometry_sockets) - : log_full_geometry_sockets_(std::move(log_full_geometry_sockets)), + GeoLogger(Set log_full_sockets) + : log_full_sockets_(std::move(log_full_sockets)), threadlocals_([this]() { return LocalGeoLogger(*this); }) { } diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index fa9bf09d8d9..d39f8367686 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -354,7 +354,7 @@ void LocalGeoLogger::log_value_for_sockets(Span sockets, GPointer value if (type.is()) { bool log_full_geometry = false; for (const DSocket &socket : sockets) { - if (main_logger_->log_full_geometry_sockets_.contains(socket)) { + if (main_logger_->log_full_sockets_.contains(socket)) { log_full_geometry = true; break; } From 9a1fce698bc6dd51c66464f9dcccfb89d0432823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 21 Oct 2021 15:06:49 +0200 Subject: [PATCH 1041/1500] Cleanup: rename & restructure `AssetCatalogPathCmp` Rename `AssetCatalogPathCmp` to `AssetCatalogLessThan`: - it compares more than paths (so no more `Path` in the name), and - performs a less-than operation (so no more `Cmp` in the name). Also restructure its code to make an extra upcoming comparison easier to add. No functional changes. --- .../blender/blenkernel/BKE_asset_catalog.hh | 17 ++++--- .../blenkernel/intern/asset_catalog_test.cc | 44 +++++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index f7896e0f51e..ca4517707e3 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -395,6 +395,12 @@ class AssetCatalog { /* Treat this catalog as deleted. Keeping deleted catalogs around is necessary to support * merging of on-disk changes with in-memory changes. */ bool is_deleted = false; + + /* Sort this catalog first when there are multiple catalogs with the same catalog path. This + * ensures that in a situation where missing catalogs were auto-created, and then + * load-and-merged with a file that also has these catalogs, the first one in that file is + * always sorted first, regardless of the sort order of its UUID. */ + bool is_first_loaded = false; } flags; /** @@ -411,20 +417,21 @@ class AssetCatalog { }; /** Comparator for asset catalogs, ordering by (path, UUID). */ -struct AssetCatalogPathCmp { +struct AssetCatalogLessThan { bool operator()(const AssetCatalog *lhs, const AssetCatalog *rhs) const { - if (lhs->path == rhs->path) { - return lhs->catalog_id < rhs->catalog_id; + if (lhs->path != rhs->path) { + return lhs->path < rhs->path; } - return lhs->path < rhs->path; + + return lhs->catalog_id < rhs->catalog_id; } }; /** * Set that stores catalogs ordered by (path, UUID). * Being a set, duplicates are removed. The catalog's simple name is ignored in this. */ -using AssetCatalogOrderedSet = std::set; +using AssetCatalogOrderedSet = std::set; /** * Filter that can determine whether an asset should be visible or not, based on its catalog ID. diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index d743d250c1d..478d2d2b31e 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -954,6 +954,50 @@ TEST_F(AssetCatalogTest, order_by_path) } } +TEST_F(AssetCatalogTest, order_by_path_and_first_seen) +{ + AssetCatalogService service; + service.load_from_disk(asset_library_root_); + + const bUUID first_seen_uuid("3d451c87-27d1-40fd-87fc-f4c9e829c848"); + const bUUID first_sorted_uuid("00000000-0000-0000-0000-000000000001"); + const bUUID last_sorted_uuid("ffffffff-ffff-ffff-ffff-ffffffffffff"); + + AssetCatalog first_seen_cat(first_seen_uuid, "simple/path/child", ""); + const AssetCatalog first_sorted_cat(first_sorted_uuid, "simple/path/child", ""); + const AssetCatalog last_sorted_cat(last_sorted_uuid, "simple/path/child", ""); + + /* Mimick that this catalog was first-seen when loading from disk. */ + first_seen_cat.flags.is_first_loaded = true; + + /* Just an assertion of the defaults; this is more to avoid confusing errors later on than an + * actual test of these defaults. */ + ASSERT_FALSE(first_sorted_cat.flags.is_first_loaded); + ASSERT_FALSE(last_sorted_cat.flags.is_first_loaded); + + AssetCatalogOrderedSet by_path; + by_path.insert(&first_seen_cat); + by_path.insert(&first_sorted_cat); + by_path.insert(&last_sorted_cat); + + AssetCatalogOrderedSet::const_iterator set_iter = by_path.begin(); + + EXPECT_EQ(1, by_path.count(&first_seen_cat)); + EXPECT_EQ(1, by_path.count(&first_sorted_cat)); + EXPECT_EQ(1, by_path.count(&last_sorted_cat)); + ASSERT_EQ(3, by_path.size()); + + EXPECT_EQ(first_seen_uuid, (*(set_iter++))->catalog_id); + EXPECT_EQ(first_sorted_uuid, (*(set_iter++))->catalog_id); + EXPECT_EQ(last_sorted_uuid, (*(set_iter++))->catalog_id); + + if (set_iter != by_path.end()) { + const AssetCatalog *next_cat = *set_iter; + FAIL() << "Did not expect more items in the set, had at least " << next_cat->catalog_id << ":" + << next_cat->path; + } +} + TEST_F(AssetCatalogTest, create_missing_catalogs) { TestableAssetCatalogService new_service; From 5ccec8ec6bed3e0eda1cffaae565fdfaccd2a6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 21 Oct 2021 15:53:12 +0200 Subject: [PATCH 1042/1500] Asset Catalogs: treat first-loaded catalog as main catalog When there are multiple catalogs with the same path (so different UUIDs all mapped to the same catalog path), treat the first-loaded one as the main catalog for that path, and the rest as aliases. This ensures that the UUID of a catalog (as chosen in the tree UI and thus interacted with by users) is stable, regardless of whether by some coincidence later another catalog with the same UUID is created. --- .../blender/blenkernel/BKE_asset_catalog.hh | 15 +++++++-- .../blenkernel/intern/asset_catalog.cc | 25 ++++++++++++--- .../blenkernel/intern/asset_catalog_test.cc | 32 +++++++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index ca4517707e3..5de74efe2cf 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -108,8 +108,12 @@ class AssetCatalogService { /** Return catalog with the given ID. Return nullptr if not found. */ AssetCatalog *find_catalog(CatalogID catalog_id) const; - /** Return first catalog with the given path. Return nullptr if not found. This is not an - * efficient call as it's just a linear search over the catalogs. */ + /** + * Return first catalog with the given path. Return nullptr if not found. This is not an + * efficient call as it's just a linear search over the catalogs. + * + * If there are multiple catalogs with the same path, return the first-loaded one. If there is + * none marked as "first loaded", return the one with the lowest UUID. */ AssetCatalog *find_catalog_by_path(const AssetCatalogPath &path) const; /** @@ -416,7 +420,7 @@ class AssetCatalog { static std::string sensible_simple_name_for_path(const AssetCatalogPath &path); }; -/** Comparator for asset catalogs, ordering by (path, UUID). */ +/** Comparator for asset catalogs, ordering by (path, first_seen, UUID). */ struct AssetCatalogLessThan { bool operator()(const AssetCatalog *lhs, const AssetCatalog *rhs) const { @@ -424,6 +428,10 @@ struct AssetCatalogLessThan { return lhs->path < rhs->path; } + if (lhs->flags.is_first_loaded != rhs->flags.is_first_loaded) { + return lhs->flags.is_first_loaded; + } + return lhs->catalog_id < rhs->catalog_id; } }; @@ -432,6 +440,7 @@ struct AssetCatalogLessThan { * Set that stores catalogs ordered by (path, UUID). * Being a set, duplicates are removed. The catalog's simple name is ignored in this. */ using AssetCatalogOrderedSet = std::set; +using MutableAssetCatalogOrderedSet = std::set; /** * Filter that can determine whether an asset should be visible or not, based on its catalog ID. diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index aa8f12d0e23..51f8457ebd9 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -24,6 +24,7 @@ #include "BLI_fileops.h" #include "BLI_path_util.h" +#include "BLI_set.hh" #include "BLI_string_ref.hh" #include "DNA_userdef_types.h" @@ -108,13 +109,23 @@ AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) const AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath &path) const { + /* Use an AssetCatalogOrderedSet to find the 'best' catalog for this path. This will be the first + * one loaded from disk, or if that does not exist the one with the lowest UUID. This ensures + * stable, predictable results. */ + MutableAssetCatalogOrderedSet ordered_catalogs; + for (const auto &catalog : catalog_collection_->catalogs_.values()) { if (catalog->path == path) { - return catalog.get(); + ordered_catalogs.insert(catalog.get()); } } - return nullptr; + if (ordered_catalogs.empty()) { + return nullptr; + } + + MutableAssetCatalogOrderedSet::iterator best_choice_it = ordered_catalogs.begin(); + return *best_choice_it; } AssetCatalogFilter AssetCatalogService::create_catalog_filter( @@ -309,7 +320,10 @@ std::unique_ptr AssetCatalogService::parse_catalog_f auto cdf = std::make_unique(); cdf->file_path = catalog_definition_file_path; - auto catalog_parsed_callback = [this, catalog_definition_file_path]( + /* TODO(Sybren): this might have to move to a higher level when supporting multiple CDFs. */ + Set seen_paths; + + auto catalog_parsed_callback = [this, catalog_definition_file_path, &seen_paths]( std::unique_ptr catalog) { if (catalog_collection_->catalogs_.contains(catalog->catalog_id)) { /* TODO(@sybren): apparently another CDF was already loaded. This is not supported yet. */ @@ -319,6 +333,8 @@ std::unique_ptr AssetCatalogService::parse_catalog_f return false; } + catalog->flags.is_first_loaded = seen_paths.add(catalog->path); + /* The AssetCatalog pointer is now owned by the AssetCatalogService. */ catalog_collection_->catalogs_.add_new(catalog->catalog_id, std::move(catalog)); return true; @@ -648,7 +664,8 @@ void AssetCatalogTree::insert_item(const AssetCatalog &catalog) /* If full path of this catalog already exists as parent path of a previously read catalog, * we can ensure this tree item's UUID is set here. */ - if (is_last_component && BLI_uuid_is_nil(item.catalog_id_)) { + if (is_last_component && + (BLI_uuid_is_nil(item.catalog_id_) || catalog.flags.is_first_loaded)) { item.catalog_id_ = catalog.catalog_id; } diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 478d2d2b31e..7691658bc57 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -39,6 +39,7 @@ const bUUID UUID_POSES_RUZENA("79a4f887-ab60-4bd4-94da-d572e27d6aed"); const bUUID UUID_POSES_RUZENA_HAND("81811c31-1a88-4bd7-bb34-c6fc2607a12e"); const bUUID UUID_POSES_RUZENA_FACE("82162c1f-06cc-4d91-a9bf-4f72c104e348"); const bUUID UUID_WITHOUT_SIMPLENAME("d7916a31-6ca9-4909-955f-182ca2b81fa3"); +const bUUID UUID_ANOTHER_RUZENA("00000000-d9fa-4b91-b704-e6af1f1339ef"); /* UUIDs from lib/tests/asset_library/modified_assets.cats.txt */ const bUUID UUID_AGENT_47("c5744ba5-43f5-4f73-8e52-010ad4a61b34"); @@ -290,6 +291,37 @@ TEST_F(AssetCatalogTest, load_single_file) EXPECT_EQ(UUID_POSES_RUZENA, poses_ruzena->catalog_id); EXPECT_EQ("character/Ružena/poselib", poses_ruzena->path.str()); EXPECT_EQ("POSES_RUŽENA", poses_ruzena->simple_name); + + /* Test getting a catalog that aliases an earlier-defined catalog. */ + AssetCatalog *another_ruzena = service.find_catalog(UUID_ANOTHER_RUZENA); + ASSERT_NE(nullptr, another_ruzena); + EXPECT_EQ(UUID_ANOTHER_RUZENA, another_ruzena->catalog_id); + EXPECT_EQ("character/Ružena/poselib", another_ruzena->path.str()); + EXPECT_EQ("Another Ružena", another_ruzena->simple_name); +} + +TEST_F(AssetCatalogTest, is_first_loaded_flag) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + AssetCatalog *new_cat = service.create_catalog("never/before/seen/path"); + EXPECT_FALSE(new_cat->flags.is_first_loaded) + << "Adding a catalog at runtime should never mark it as 'first loaded'; " + "only loading from disk is allowed to do that."; + + AssetCatalog *alias_cat = service.create_catalog("character/Ružena/poselib"); + EXPECT_FALSE(alias_cat->flags.is_first_loaded) + << "Adding a new catalog with an already-loaded path should not mark it as 'first loaded'"; + + EXPECT_TRUE(service.find_catalog(UUID_POSES_ELLIE)->flags.is_first_loaded); + EXPECT_TRUE(service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)->flags.is_first_loaded); + EXPECT_TRUE(service.find_catalog(UUID_POSES_RUZENA)->flags.is_first_loaded); + EXPECT_FALSE(service.find_catalog(UUID_ANOTHER_RUZENA)->flags.is_first_loaded); + + AssetCatalog *ruzena = service.find_catalog_by_path("character/Ružena/poselib"); + EXPECT_EQ(UUID_POSES_RUZENA, ruzena->catalog_id) + << "The first-seen definition of a catalog should be returned"; } TEST_F(AssetCatalogTest, insert_item_into_tree) From 4b48b1079d9175a5b86b2299c902cac0fbe27f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 21 Oct 2021 16:06:14 +0200 Subject: [PATCH 1043/1500] Asset Catalogs: refresh simple name when renaming catalog When renaming an asset catalog, also update its simple name. Catalogs will most likely be created from within Blender, so via the catalog tree in the asset browser. Here catalogs are always named "Catalog" until the user renames them, which was reflected in all simple names being "Catalog". --- source/blender/blenkernel/BKE_asset_catalog.hh | 3 +++ .../blender/blenkernel/intern/asset_catalog.cc | 9 +++++++++ .../blenkernel/intern/asset_catalog_test.cc | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 5de74efe2cf..cbb15780a68 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -415,6 +415,9 @@ class AssetCatalog { */ static std::unique_ptr from_path(const AssetCatalogPath &path); + /** Make a new simple name for the catalog, based on its path. */ + void simple_name_refresh(); + protected: /** Generate a sensible catalog ID for the given path. */ static std::string sensible_simple_name_for_path(const AssetCatalogPath &path); diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 51f8457ebd9..9dd5ccdf3cf 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -219,6 +219,10 @@ void AssetCatalogService::update_catalog_path(const CatalogID catalog_id, continue; } cat->path = new_path; + cat->simple_name_refresh(); + + /* TODO(Sybren): go over all assets that are assigned to this catalog, defined in the current + * blend file, and update the catalog simple name stored there. */ } this->rebuild_tree(); @@ -953,6 +957,11 @@ std::unique_ptr AssetCatalog::from_path(const AssetCatalogPath &pa return catalog; } +void AssetCatalog::simple_name_refresh() +{ + this->simple_name = sensible_simple_name_for_path(this->path); +} + std::string AssetCatalog::sensible_simple_name_for_path(const AssetCatalogPath &path) { std::string name = path.str(); diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 7691658bc57..ebb282a7371 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -882,6 +882,22 @@ TEST_F(AssetCatalogTest, update_catalog_path) << "Changing the path should update children."; } +TEST_F(AssetCatalogTest, update_catalog_path_simple_name) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + service.update_catalog_path(UUID_POSES_RUZENA, "charlib/Ružena"); + + /* This may not be valid forever; maybe at some point we'll expose the simple name to users & let + * them change it from the UI. Until then, automatically updating it is better, because otherwise + * all simple names would be "Catalog". */ + EXPECT_EQ("charlib-Ružena", service.find_catalog(UUID_POSES_RUZENA)->simple_name) + << "Changing the path should update the simplename."; + EXPECT_EQ("charlib-Ružena-face", service.find_catalog(UUID_POSES_RUZENA_FACE)->simple_name) + << "Changing the path should update the simplename of children."; +} + TEST_F(AssetCatalogTest, merge_catalog_files) { const CatalogFilePath cdf_dir = create_temp_path(); From ff46afb4dd3e04f22640a5f2e49e75ed088e3a93 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 21 Oct 2021 09:16:58 -0500 Subject: [PATCH 1044/1500] Fix: Curve trim crash on splines with no edges --- source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 2963b2c4bbb..82e2d7ad283 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -361,6 +361,10 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, continue; } + if (spline.evaluated_edges_size() == 0) { + continue; + } + /* Return a spline with one point instead of implicitly * reversing the spline or switching the parameters. */ if (ends[i] < starts[i]) { From 16c79d3b8276493a5ce70e74d50cf066f64894c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 21 Oct 2021 16:23:50 +0200 Subject: [PATCH 1045/1500] Asset Catalogs: add test for backslashed catalog paths No functional changes necessary, test already succeeds. --- .../blenkernel/intern/asset_catalog_test.cc | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index ebb282a7371..b41fa0fe833 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -300,6 +300,26 @@ TEST_F(AssetCatalogTest, load_single_file) EXPECT_EQ("Another Ružena", another_ruzena->simple_name); } +TEST_F(AssetCatalogTest, load_catalog_path_backslashes) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + const bUUID ELLIE_BACKSLASHES_UUID("a51e17ae-34fc-47d5-ba0f-64c2c9b771f7"); + const AssetCatalog *found_by_id = service.find_catalog(ELLIE_BACKSLASHES_UUID); + ASSERT_NE(nullptr, found_by_id); + EXPECT_EQ(AssetCatalogPath("character/Ellie/backslashes"), found_by_id->path) + << "Backslashes should be normalised when loading from disk."; + EXPECT_EQ(StringRefNull("Windows For Life!"), found_by_id->simple_name); + + const AssetCatalog *found_by_path = service.find_catalog_by_path("character/Ellie/backslashes"); + EXPECT_EQ(found_by_id, found_by_path) + << "Catalog with backslashed path should be findable by the normalized path."; + + EXPECT_EQ(nullptr, service.find_catalog_by_path("character\\Ellie\\backslashes")) + << "Nothing should be found when searching for backslashes."; +} + TEST_F(AssetCatalogTest, is_first_loaded_flag) { AssetCatalogService service(asset_library_root_); @@ -413,6 +433,7 @@ TEST_F(AssetCatalogTest, load_single_file_into_tree) std::vector expected_paths{ "character", "character/Ellie", + "character/Ellie/backslashes", "character/Ellie/poselib", "character/Ellie/poselib/tailslash", "character/Ellie/poselib/white space", @@ -759,6 +780,7 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf) std::vector expected_paths{ "character", "character/Ellie", + "character/Ellie/backslashes", "character/Ellie/poselib", "character/Ellie/poselib/tailslash", "character/Ellie/poselib/white space", @@ -795,7 +817,8 @@ TEST_F(AssetCatalogTest, delete_catalog_parent_by_path) service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); /* Create an extra catalog with the to-be-deleted path, and one with a child of that. - * This creates some duplicates that are bound to occur in production asset libraries as well. */ + * This creates some duplicates that are bound to occur in production asset libraries as well. + */ const bUUID cat1_uuid = service.create_catalog("character/Ružena/poselib")->catalog_id; const bUUID cat2_uuid = service.create_catalog("character/Ružena/poselib/body")->catalog_id; @@ -814,6 +837,7 @@ TEST_F(AssetCatalogTest, delete_catalog_parent_by_path) std::vector expected_paths{ "character", "character/Ellie", + "character/Ellie/backslashes", "character/Ellie/poselib", "character/Ellie/poselib/tailslash", "character/Ellie/poselib/white space", @@ -889,9 +913,9 @@ TEST_F(AssetCatalogTest, update_catalog_path_simple_name) AssetCatalogService::DEFAULT_CATALOG_FILENAME); service.update_catalog_path(UUID_POSES_RUZENA, "charlib/Ružena"); - /* This may not be valid forever; maybe at some point we'll expose the simple name to users & let - * them change it from the UI. Until then, automatically updating it is better, because otherwise - * all simple names would be "Catalog". */ + /* This may not be valid forever; maybe at some point we'll expose the simple name to users & + * let them change it from the UI. Until then, automatically updating it is better, because + * otherwise all simple names would be "Catalog". */ EXPECT_EQ("charlib-Ružena", service.find_catalog(UUID_POSES_RUZENA)->simple_name) << "Changing the path should update the simplename."; EXPECT_EQ("charlib-Ružena-face", service.find_catalog(UUID_POSES_RUZENA_FACE)->simple_name) From 594c857f652b7f9283b299e1e82cd7dc324f8ffe Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 21 Oct 2021 16:36:35 +0200 Subject: [PATCH 1046/1500] Fix tooltip disabled hint not using correct context from the button To display the "disabled hint" (text explaining why a button is disabled) in a tooltip, it would run the operator poll callback, which could then set a poll message. But the context for the poll check wasn't the one from the button, so the poll may give a different result (and disabled hint) than the check of the button itself did. Make sure it uses the exact context from the button. --- source/blender/editors/interface/interface_region_tooltip.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 4a7203bb421..eb25d896d26 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -960,9 +960,10 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, /* if operator poll check failed, it can give pretty precise info why */ if (optype) { + const int opcontext = extra_icon ? extra_icon->optype_params->opcontext : but->opcontext; CTX_wm_operator_poll_msg_clear(C); - WM_operator_poll_context( - C, optype, extra_icon ? extra_icon->optype_params->opcontext : but->opcontext); + ui_but_context_poll_operator_ex( + C, but, &(wmOperatorCallParams){.optype = optype, .opcontext = opcontext}); disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free); } /* alternatively, buttons can store some reasoning too */ From deb7ec312a43d5a511b2862da8e31b2addb7f492 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 21 Oct 2021 19:27:30 +0300 Subject: [PATCH 1047/1500] Suppress the unused parameter warning from D9551. The parameter is only used in an assert. --- source/blender/blenloader/intern/versioning_300.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 6b57bdf9a9c..51a3e32f2bb 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1177,6 +1177,7 @@ static void correct_bone_roll_value(const float head[3], * 2.92 and 2.91, provided Edit Mode isn't entered on the armature in 2.91. */ vec_roll_to_mat3(vec, *r_roll, bone_mat); + UNUSED_VARS_NDEBUG(check_y_axis); BLI_assert(dot_v3v3(bone_mat[1], check_y_axis) > 0.999f); if (dot_v3v3(bone_mat[0], check_x_axis) < 0.999f) { From 7b9319adf90e1742873213abbcd9293c3c4c3749 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 21 Oct 2021 11:51:56 -0500 Subject: [PATCH 1048/1500] Geometry Nodes: Update bounding box to work on individual instances This commit makes the bounding box node work on each unique geometry (including instances) individually instead of making one large bounding box for everything. This makes the node much faster, and is often the desired result anyway. For the old behavior, a realize instances node can be used in front of this node (versioning adds it automatically). The min and max outputs now only output the values from the realized geometry. Differential Revision: https://developer.blender.org/D12951 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_300.c | 13 ++ .../geometry/nodes/node_geo_bounding_box.cc | 154 ++++-------------- 3 files changed, 50 insertions(+), 119 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 899d21683e4..32b607ecf9b 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 36 +#define BLENDER_FILE_SUBVERSION 37 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 51a3e32f2bb..bdee5194c9a 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -754,6 +754,19 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 37)) { + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_BOUNDING_BOX) { + bNodeSocket *geometry_socket = node->inputs.first; + add_realize_instances_before_socket(ntree, node, geometry_socket); + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index 8b90595a641..e34b65e6c94 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -14,9 +14,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BKE_spline.hh" -#include "BKE_volume.h" - #include "node_geometry_util.hh" namespace blender::nodes { @@ -29,135 +26,56 @@ static void geo_node_bounding_box_declare(NodeDeclarationBuilder &b) b.add_output("Max"); } -using bke::GeometryInstanceGroup; - -static void compute_min_max_from_position_and_transform(const GeometryComponent &component, - Span transforms, - float3 &r_min, - float3 &r_max) -{ - GVArray_Typed positions = component.attribute_get_for_read( - "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - - for (const float4x4 &transform : transforms) { - for (const int i : positions.index_range()) { - const float3 position = positions[i]; - const float3 transformed_position = transform * position; - minmax_v3v3_v3(r_min, r_max, transformed_position); - } - } -} - -static void compute_min_max_from_volume_and_transforms(const VolumeComponent &volume_component, - Span transforms, - float3 &r_min, - float3 &r_max) -{ -#ifdef WITH_OPENVDB - const Volume *volume = volume_component.get_for_read(); - if (volume == nullptr) { - return; - } - for (const int i : IndexRange(BKE_volume_num_grids(volume))) { - const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); - openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); - - for (const float4x4 &transform : transforms) { - openvdb::GridBase::ConstPtr instance_grid = BKE_volume_grid_shallow_transform(grid, - transform); - float3 grid_min = float3(FLT_MAX); - float3 grid_max = float3(-FLT_MAX); - if (BKE_volume_grid_bounds(instance_grid, grid_min, grid_max)) { - DO_MIN(grid_min, r_min); - DO_MAX(grid_max, r_max); - } - } - } -#else - UNUSED_VARS(volume_component, transforms, r_min, r_max); -#endif -} - -static void compute_min_max_from_curve_and_transforms(const CurveComponent &curve_component, - Span transforms, - float3 &r_min, - float3 &r_max) -{ - const CurveEval *curve = curve_component.get_for_read(); - if (curve == nullptr) { - return; - } - for (const SplinePtr &spline : curve->splines()) { - Span positions = spline->evaluated_positions(); - - for (const float4x4 &transform : transforms) { - for (const int i : positions.index_range()) { - const float3 position = positions[i]; - const float3 transformed_position = transform * position; - minmax_v3v3_v3(r_min, r_max, transformed_position); - } - } - } -} - -static void compute_geometry_set_instances_boundbox(const GeometrySet &geometry_set, - float3 &r_min, - float3 &r_max) -{ - Vector set_groups; - bke::geometry_set_gather_instances(geometry_set, set_groups); - - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - Span transforms = set_group.transforms; - - if (set.has()) { - compute_min_max_from_position_and_transform( - *set.get_component_for_read(), transforms, r_min, r_max); - } - if (set.has()) { - compute_min_max_from_position_and_transform( - *set.get_component_for_read(), transforms, r_min, r_max); - } - if (set.has()) { - compute_min_max_from_volume_and_transforms( - *set.get_component_for_read(), transforms, r_min, r_max); - } - if (set.has()) { - compute_min_max_from_curve_and_transforms( - *set.get_component_for_read(), transforms, r_min, r_max); - } - } -} - static void geo_node_bounding_box_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); + /* Compute the min and max of all realized geometry for the two + * vector outputs, which are only meant to consider real geometry. */ float3 min = float3(FLT_MAX); float3 max = float3(-FLT_MAX); - - if (geometry_set.has_instances()) { - compute_geometry_set_instances_boundbox(geometry_set, min, max); - } - else { - geometry_set.compute_boundbox_without_instances(&min, &max); - } - + geometry_set.compute_boundbox_without_instances(&min, &max); if (min == float3(FLT_MAX)) { - params.set_output("Bounding Box", GeometrySet()); params.set_output("Min", float3(0)); params.set_output("Max", float3(0)); } else { - const float3 scale = max - min; - const float3 center = min + scale / 2.0f; - Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); - transform_mesh(*mesh, center, float3(0), float3(1)); - params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh)); params.set_output("Min", min); params.set_output("Max", max); } + + /* Generate the bounding box meshes inside each unique geometry set (including individually for + * every instance). Because geometry components are reference counted anyway, we can just + * repurpose the original geometry sets for the output. */ + if (params.output_is_required("Bounding Box")) { + geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) { + float3 sub_min = float3(FLT_MAX); + float3 sub_max = float3(-FLT_MAX); + + /* Reuse the min and max calculation if this is the main "real" geometry set. */ + if (&sub_geometry == &geometry_set) { + sub_min = min; + sub_max = max; + } + else { + sub_geometry.compute_boundbox_without_instances(&sub_min, &sub_max); + } + + if (sub_min == float3(FLT_MAX)) { + sub_geometry.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + } + else { + const float3 scale = sub_max - sub_min; + const float3 center = sub_min + scale / 2.0f; + Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); + transform_mesh(*mesh, center, float3(0), float3(1)); + sub_geometry.replace_mesh(mesh); + sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + } + }); + + params.set_output("Bounding Box", std::move(geometry_set)); + } } } // namespace blender::nodes From 3858bf5c6fb4b40f6dc09312372d79e797da3750 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 21 Oct 2021 12:50:55 -0500 Subject: [PATCH 1049/1500] Cleanup: Use common define for menu separator arrow --- source/blender/editors/include/UI_interface.h | 3 +++ .../interface/interface_template_search_menu.c | 15 +++++---------- .../space_node/node_geometry_attribute_search.cc | 7 ++----- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index ed132422d3b..00e96955fb4 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -96,6 +96,9 @@ typedef struct uiTreeViewItemHandle uiTreeViewItemHandle; #define UI_SEP_CHAR '|' #define UI_SEP_CHAR_S "|" +/* Separator for text in search menus. */ +#define UI_MENU_ARROW_SEP "▶" + /* names */ #define UI_MAX_DRAW_STR 400 #define UI_MAX_NAME_STR 128 diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index f305199c6b0..5877b4fe6d7 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -69,9 +69,6 @@ /** \name Menu Search Template Implementation * \{ */ -/* Unicode arrow. */ -#define MENU_SEP "\xe2\x96\xb6" - /** * Use when #menu_items_from_ui_create is called with `include_all_areas`. * so we can run the menu item in the area it was extracted from. @@ -415,7 +412,7 @@ static void menu_items_from_all_operators(bContext *C, struct MenuSearch_Data *d char uiname[256]; WM_operator_py_idname(idname_as_py, ot->idname); - SNPRINTF(uiname, "%s " MENU_SEP "%s", idname_as_py, ot_ui_name); + SNPRINTF(uiname, "%s " UI_MENU_ARROW_SEP "%s", idname_as_py, ot_ui_name); item->drawwstr_full = strdup_memarena(memarena, uiname); item->drawstr = ot_ui_name; @@ -841,7 +838,7 @@ static struct MenuSearch_Data *menu_items_from_ui_create( } while (menu_parent) { BLI_dynstr_append(dyn_str, menu_parent->drawstr); - BLI_dynstr_append(dyn_str, " " MENU_SEP " "); + BLI_dynstr_append(dyn_str, " " UI_MENU_ARROW_SEP " "); menu_parent = menu_parent->temp_child; } } @@ -859,13 +856,13 @@ static struct MenuSearch_Data *menu_items_from_ui_create( BLI_dynstr_appendf(dyn_str, " (%s)", kmi_str); } - BLI_dynstr_append(dyn_str, " " MENU_SEP " "); + BLI_dynstr_append(dyn_str, " " UI_MENU_ARROW_SEP " "); } /* Optional nested menu. */ if (item->drawstr_submenu != NULL) { BLI_dynstr_append(dyn_str, item->drawstr_submenu); - BLI_dynstr_append(dyn_str, " " MENU_SEP " "); + BLI_dynstr_append(dyn_str, " " UI_MENU_ARROW_SEP " "); } BLI_dynstr_append(dyn_str, item->drawstr); @@ -1160,7 +1157,7 @@ void UI_but_func_menu_search(uiBut *but) UI_but_func_search_set_context_menu(but, ui_search_menu_create_context_menu); UI_but_func_search_set_tooltip(but, ui_search_menu_create_tooltip); - UI_but_func_search_set_sep_string(but, MENU_SEP); + UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP); } void uiTemplateMenuSearch(uiLayout *layout) @@ -1177,6 +1174,4 @@ void uiTemplateMenuSearch(uiLayout *layout) UI_but_func_menu_search(but); } -#undef MENU_SEP - /** \} */ diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 411719cf6c0..a69109db69c 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -74,14 +74,11 @@ static StringRef attribute_domain_string(const AttributeDomain domain) return StringRef(IFACE_(name)); } -/* Unicode arrow. */ -#define MENU_SEP "\xe2\x96\xb6" - static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item) { const StringRef data_type_name = attribute_data_type_string(item.data_type); const StringRef domain_name = attribute_domain_string(item.domain); - std::string search_item_text = domain_name + " " + MENU_SEP + item.name + UI_SEP_CHAR + + std::string search_item_text = domain_name + " " + UI_MENU_ARROW_SEP + item.name + UI_SEP_CHAR + data_type_name; return UI_search_item_add( @@ -198,7 +195,7 @@ void node_geometry_add_attribute_search_button(const bContext *UNUSED(C), AttributeSearchData, {node_tree, node, (bNodeSocket *)socket_ptr->data}); UI_but_func_search_set_results_are_suggestions(but, true); - UI_but_func_search_set_sep_string(but, MENU_SEP); + UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP); UI_but_func_search_set(but, nullptr, attribute_search_update_fn, From 65490e62708c6bb0c03d9c73fd7179fdf71355a5 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Thu, 21 Oct 2021 11:17:38 -0700 Subject: [PATCH 1050/1500] Fix T92371: Move AZONE_REGION When Overlapped Overlapped regions have transparent backgrounds, so when placing AZONE_REGION we need to move them in to the content edge. See D12956 for details and examples. Differential Revision: https://developer.blender.org/D12956 Reviewed by Hans Goudey --- source/blender/editors/screen/area.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 54727cc79f1..9e179dad2e8 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -972,29 +972,33 @@ static void fullscreen_azone_init(ScrArea *area, ARegion *region) #define AZONEPAD_ICON (0.45f * U.widget_unit) static void region_azone_edge(AZone *az, ARegion *region) { + /* If region is overlapped (transparent background), move AZone to content. + * Note this is an arbitrary amount that matches nicely with numbers elswhere. */ + int overlap_padding = (region->overlap) ? (int)(0.4f * U.widget_unit) : 0; + switch (az->edge) { case AE_TOP_TO_BOTTOMRIGHT: az->x1 = region->winrct.xmin; - az->y1 = region->winrct.ymax - AZONEPAD_EDGE; + az->y1 = region->winrct.ymax - AZONEPAD_EDGE - overlap_padding; az->x2 = region->winrct.xmax; - az->y2 = region->winrct.ymax + AZONEPAD_EDGE; + az->y2 = region->winrct.ymax + AZONEPAD_EDGE - overlap_padding; break; case AE_BOTTOM_TO_TOPLEFT: az->x1 = region->winrct.xmin; - az->y1 = region->winrct.ymin + AZONEPAD_EDGE; + az->y1 = region->winrct.ymin + AZONEPAD_EDGE + overlap_padding; az->x2 = region->winrct.xmax; - az->y2 = region->winrct.ymin - AZONEPAD_EDGE; + az->y2 = region->winrct.ymin - AZONEPAD_EDGE + overlap_padding; break; case AE_LEFT_TO_TOPRIGHT: - az->x1 = region->winrct.xmin - AZONEPAD_EDGE; + az->x1 = region->winrct.xmin - AZONEPAD_EDGE + overlap_padding; az->y1 = region->winrct.ymin; - az->x2 = region->winrct.xmin + AZONEPAD_EDGE; + az->x2 = region->winrct.xmin + AZONEPAD_EDGE + overlap_padding; az->y2 = region->winrct.ymax; break; case AE_RIGHT_TO_TOPLEFT: - az->x1 = region->winrct.xmax + AZONEPAD_EDGE; + az->x1 = region->winrct.xmax + AZONEPAD_EDGE - overlap_padding; az->y1 = region->winrct.ymin; - az->x2 = region->winrct.xmax - AZONEPAD_EDGE; + az->x2 = region->winrct.xmax - AZONEPAD_EDGE - overlap_padding; az->y2 = region->winrct.ymax; break; } From 1d96a482675dd2ccad2af31c274f74b9f6603d6b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 21 Oct 2021 13:54:48 -0500 Subject: [PATCH 1051/1500] Geometry Nodes: Attribute search in the modifier This adds attribute search to the geometry nodes modifier for the input and output attributes. The "New" search item is only shown for the output attributes. Some of the attribute search code is extracted to a new file in the interface code, to avoid some code duplication. The UI code required two fixes so that the search would work for dynamic length strings (IDProperties do not have a fixed size). Since this does changes to the UI layout of the modifier, I also addressed T91485 here. Differential Revisiion: https://developer.blender.org/D12788 --- .../blender/editors/include/UI_interface.hh | 14 +- .../blender/editors/interface/CMakeLists.txt | 3 + .../interface/interface_region_search.c | 11 +- .../interface_template_attribute_search.cc | 125 +++++++++++ .../node_geometry_attribute_search.cc | 78 +------ source/blender/modifiers/intern/MOD_nodes.cc | 211 +++++++++++++++--- .../nodes/NOD_geometry_nodes_eval_log.hh | 23 +- .../nodes/intern/geometry_nodes_eval_log.cc | 11 + 8 files changed, 362 insertions(+), 114 deletions(-) create mode 100644 source/blender/editors/interface/interface_template_attribute_search.cc diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index 4a583d0225e..5edccfa8c88 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -24,10 +24,22 @@ #include "BLI_string_ref.hh" +namespace blender::nodes::geometry_nodes_eval_log { +struct GeometryAttributeInfo; +} + struct uiBlock; namespace blender::ui { class AbstractTreeView; -} + +void attribute_search_add_items( + StringRefNull str, + const bool is_output, + Span infos, + uiSearchItems *items, + const bool is_first); + +} // namespace blender::ui blender::ui::AbstractTreeView *UI_block_add_view( uiBlock &block, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 8fcc704a301..b2659f5ed52 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -25,9 +25,11 @@ set(INC ../../depsgraph ../../draw ../../gpu + ../../functions ../../imbuf ../../makesdna ../../makesrna + ../../nodes ../../python ../../render ../../windowmanager @@ -69,6 +71,7 @@ set(SRC interface_style.c interface_template_asset_view.cc interface_template_list.cc + interface_template_attribute_search.cc interface_template_search_menu.c interface_template_search_operator.c interface_templates.c diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 1cd3ef89ed3..5bea03dee63 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -316,7 +316,11 @@ bool ui_searchbox_apply(uiBut *but, ARegion *region) const char *name_sep = data->use_shortcut_sep ? strrchr(name, UI_SEP_CHAR) : NULL; - BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen); + /* Search button with dynamic string properties may have their own method of applying + * the search results, so only copy the result if there is a proper space for it. */ + if (but->hardmax != 0) { + BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen); + } search_but->item_active = data->items.pointers[data->active]; @@ -878,7 +882,8 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, else { data->items.maxitem = SEARCH_ITEMS; } - data->items.maxstrlen = but->hardmax; + /* In case the button's string is dynamic, make sure there are buffers available. */ + data->items.maxstrlen = but->hardmax == 0 ? UI_MAX_NAME_STR : but->hardmax; data->items.totitem = 0; data->items.names = MEM_callocN(data->items.maxitem * sizeof(void *), "search names"); data->items.pointers = MEM_callocN(data->items.maxitem * sizeof(void *), "search pointers"); @@ -886,7 +891,7 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, data->items.states = MEM_callocN(data->items.maxitem * sizeof(int), "search flags"); data->items.name_prefix_offsets = NULL; /* Lazy initialized as needed. */ for (int i = 0; i < data->items.maxitem; i++) { - data->items.names[i] = MEM_callocN(but->hardmax + 1, "search pointers"); + data->items.names[i] = MEM_callocN(data->items.maxstrlen + 1, "search pointers"); } return region; diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc new file mode 100644 index 00000000000..0157d0b66a3 --- /dev/null +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -0,0 +1,125 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edinterface + */ + +#include "BLI_string_ref.hh" +#include "BLI_string_search.h" + +#include "DNA_customdata_types.h" + +#include "RNA_access.h" +#include "RNA_enum_types.h" + +#include "BLT_translation.h" + +#include "NOD_geometry_nodes_eval_log.hh" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" + +using blender::nodes::geometry_nodes_eval_log::GeometryAttributeInfo; + +namespace blender::ui { + +static StringRef attribute_data_type_string(const CustomDataType type) +{ + const char *name = nullptr; + RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name); + return StringRef(IFACE_(name)); +} + +static StringRef attribute_domain_string(const AttributeDomain domain) +{ + const char *name = nullptr; + RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name); + return StringRef(IFACE_(name)); +} + +static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item) +{ + const StringRef data_type_name = attribute_data_type_string(item.data_type); + const StringRef domain_name = attribute_domain_string(item.domain); + std::string search_item_text = domain_name + " " + UI_MENU_ARROW_SEP + item.name + UI_SEP_CHAR + + data_type_name; + + return UI_search_item_add( + items, search_item_text.c_str(), (void *)&item, ICON_NONE, UI_BUT_HAS_SEP_CHAR, 0); +} + +void attribute_search_add_items(StringRefNull str, + const bool is_output, + Span infos, + uiSearchItems *seach_items, + const bool is_first) +{ + static GeometryAttributeInfo dummy_info; + + /* Any string may be valid, so add the current search string along with the hints. */ + if (str[0] != '\0') { + bool contained = false; + for (const GeometryAttributeInfo *attribute_info : infos) { + if (attribute_info->name == str) { + contained = true; + break; + } + } + if (!contained && is_output) { + dummy_info.name = str; + UI_search_item_add(seach_items, str.c_str(), &dummy_info, ICON_ADD, 0, 0); + } + } + + if (str[0] == '\0' && !is_first) { + /* Allow clearing the text field when the string is empty, but not on the first pass, + * or opening an attribute field for the first time would show this search item. */ + dummy_info.name = str; + UI_search_item_add(seach_items, str.c_str(), &dummy_info, ICON_X, 0, 0); + } + + /* Don't filter when the menu is first opened, but still run the search + * so the items are in the same order they will appear in while searching. */ + const char *string = is_first ? "" : str.c_str(); + + StringSearch *search = BLI_string_search_new(); + for (const GeometryAttributeInfo *item : infos) { + + /* Don't show the legacy "normal" attribute. */ + if (item->name == "normal" && item->domain == ATTR_DOMAIN_FACE) { + continue; + } + + BLI_string_search_add(search, item->name.c_str(), (void *)item); + } + + GeometryAttributeInfo **filtered_items; + const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items); + + for (const int i : IndexRange(filtered_amount)) { + const GeometryAttributeInfo *item = filtered_items[i]; + if (!attribute_search_item_add(seach_items, *item)) { + break; + } + } + + MEM_freeN(filtered_items); + BLI_string_search_free(search); +} + +} // namespace blender::ui \ No newline at end of file diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index a69109db69c..d0ccbb03107 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -38,6 +38,7 @@ #include "BLT_translation.h" #include "UI_interface.h" +#include "UI_interface.hh" #include "UI_resources.h" #include "NOD_geometry_nodes_eval_log.hh" @@ -60,37 +61,6 @@ struct AttributeSearchData { /* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */ BLI_STATIC_ASSERT(std::is_trivially_destructible_v, ""); -static StringRef attribute_data_type_string(const CustomDataType type) -{ - const char *name = nullptr; - RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name); - return StringRef(IFACE_(name)); -} - -static StringRef attribute_domain_string(const AttributeDomain domain) -{ - const char *name = nullptr; - RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name); - return StringRef(IFACE_(name)); -} - -static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item) -{ - const StringRef data_type_name = attribute_data_type_string(item.data_type); - const StringRef domain_name = attribute_domain_string(item.domain); - std::string search_item_text = domain_name + " " + UI_MENU_ARROW_SEP + item.name + UI_SEP_CHAR + - data_type_name; - - return UI_search_item_add( - items, search_item_text.c_str(), (void *)&item, ICON_NONE, UI_BUT_HAS_SEP_CHAR, 0); -} - -static GeometryAttributeInfo &get_dummy_item_info() -{ - static GeometryAttributeInfo info; - return info; -} - static void attribute_search_update_fn( const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first) { @@ -104,51 +74,7 @@ static void attribute_search_update_fn( } blender::Vector infos = node_log->lookup_available_attributes(); - GeometryAttributeInfo &dummy_info = get_dummy_item_info(); - - /* Any string may be valid, so add the current search string along with the hints. */ - if (str[0] != '\0') { - bool contained = false; - for (const GeometryAttributeInfo *attribute_info : infos) { - if (attribute_info->name == str) { - contained = true; - break; - } - } - if (!contained) { - dummy_info.name = str; - UI_search_item_add(items, str, &dummy_info, ICON_ADD, 0, 0); - } - } - - if (str[0] == '\0' && !is_first) { - /* Allow clearing the text field when the string is empty, but not on the first pass, - * or opening an attribute field for the first time would show this search item. */ - dummy_info.name = str; - UI_search_item_add(items, str, &dummy_info, ICON_X, 0, 0); - } - - /* Don't filter when the menu is first opened, but still run the search - * so the items are in the same order they will appear in while searching. */ - const char *string = is_first ? "" : str; - - StringSearch *search = BLI_string_search_new(); - for (const GeometryAttributeInfo *item : infos) { - BLI_string_search_add(search, item->name.c_str(), (void *)item); - } - - GeometryAttributeInfo **filtered_items; - const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items); - - for (const int i : IndexRange(filtered_amount)) { - const GeometryAttributeInfo *item = filtered_items[i]; - if (!attribute_search_item_add(items, *item)) { - break; - } - } - - MEM_freeN(filtered_items); - BLI_string_search_free(search); + blender::ui::attribute_search_add_items(str, true, infos, items, is_first); } static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 562589e2610..e6cc7663c58 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -27,11 +27,13 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.hh" #include "BLI_float3.hh" #include "BLI_listbase.h" #include "BLI_multi_value_map.hh" #include "BLI_set.hh" #include "BLI_string.h" +#include "BLI_string_search.h" #include "BLI_utildefines.h" #include "DNA_collection_types.h" @@ -66,6 +68,7 @@ #include "BLO_read_write.h" #include "UI_interface.h" +#include "UI_interface.hh" #include "UI_resources.h" #include "BLT_translation.h" @@ -84,6 +87,7 @@ #include "MOD_ui_common.h" #include "ED_spreadsheet.h" +#include "ED_undo.h" #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" @@ -93,6 +97,7 @@ #include "FN_field.hh" #include "FN_multi_function.hh" +using blender::Array; using blender::ColorGeometry4f; using blender::destruct_ptr; using blender::float3; @@ -114,6 +119,7 @@ using blender::nodes::InputSocketFieldType; using blender::threading::EnumerableThreadSpecific; using namespace blender::fn::multi_function_types; using namespace blender::nodes::derived_node_tree_types; +using geo_log::GeometryAttributeInfo; static void initData(ModifierData *md) { @@ -957,9 +963,6 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, } } - /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */ - input_geometry_set.clear(); - Vector group_outputs; for (const InputSocketRef *socket_ref : output_node.inputs().drop_back(1)) { group_outputs.append({root_context, socket_ref}); @@ -974,8 +977,13 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, find_sockets_to_preview(nmd, ctx, tree, preview_sockets); eval_params.force_compute_sockets.extend(preview_sockets.begin(), preview_sockets.end()); geo_logger.emplace(std::move(preview_sockets)); + + geo_logger->log_input_geometry(input_geometry_set); } + /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */ + input_geometry_set.clear(); + eval_params.input_values = group_inputs; eval_params.output_sockets = group_outputs; eval_params.mf_by_node = &mf_by_node; @@ -985,14 +993,15 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, eval_params.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr; blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params); + GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out(); + if (geo_logger.has_value()) { + geo_logger->log_output_geometry(output_geometry_set); NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(&nmd->modifier); clear_runtime_data(nmd_orig); nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger); } - GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out(); - for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) { GMutablePointer socket_value = eval_params.r_output_values[socket->index()]; store_output_value_in_geometry(output_geometry_set, nmd, *socket, socket_value); @@ -1108,6 +1117,154 @@ static void modifyGeometrySet(ModifierData *md, modifyGeometry(md, ctx, *geometry_set); } +struct AttributeSearchData { + const geo_log::ModifierLog &modifier_log; + IDProperty &name_property; + bool is_output; +}; + +/* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */ +BLI_STATIC_ASSERT(std::is_trivially_destructible_v, ""); + +static void attribute_search_update_fn(const bContext *UNUSED(C), + void *arg, + const char *str, + uiSearchItems *items, + const bool is_first) +{ + AttributeSearchData *data = static_cast(arg); + + const geo_log::GeometryValueLog *geometry_log = data->is_output ? + data->modifier_log.output_geometry_log() : + data->modifier_log.input_geometry_log(); + if (geometry_log == nullptr) { + return; + } + + Span infos = geometry_log->attributes(); + + /* The shared attribute search code expects a span of pointers, so convert to that. */ + Array info_ptrs(infos.size()); + for (const int i : infos.index_range()) { + info_ptrs[i] = &infos[i]; + } + blender::ui::attribute_search_add_items( + str, data->is_output, info_ptrs.as_span(), items, is_first); +} + +static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) +{ + if (item_v == nullptr) { + return; + } + AttributeSearchData &data = *static_cast(data_v); + const GeometryAttributeInfo &item = *static_cast(item_v); + + IDProperty &name_property = data.name_property; + BLI_assert(name_property.type == IDP_STRING); + IDP_AssignString(&name_property, item.name.c_str(), 0); + + ED_undo_push(C, "Assign Attribute Name"); +} + +static void add_attribute_search_button(uiLayout *layout, + const NodesModifierData &nmd, + PointerRNA *md_ptr, + const StringRefNull rna_path_attribute_name, + const bNodeSocket &socket, + const bool is_output) +{ + const geo_log::ModifierLog *log = static_cast(nmd.runtime_eval_log); + if (log == nullptr) { + uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, "", ICON_NONE); + return; + } + + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but = uiDefIconTextButR(block, + UI_BTYPE_SEARCH_MENU, + 0, + ICON_NONE, + "", + 0, + 0, + 10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */ + UI_UNIT_Y, + md_ptr, + rna_path_attribute_name.c_str(), + 0, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + + const std::string use_attribute_prop_name = socket.identifier + attribute_name_suffix; + IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties, + use_attribute_prop_name.c_str()); + BLI_assert(property != nullptr); + if (property == nullptr) { + return; + } + + AttributeSearchData *data = OBJECT_GUARDED_NEW(AttributeSearchData, + {*log, *property, is_output}); + + UI_but_func_search_set_results_are_suggestions(but, true); + UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP); + UI_but_func_search_set(but, + nullptr, + attribute_search_update_fn, + static_cast(data), + true, + nullptr, + attribute_search_exec_fn, + nullptr); +} + +static void add_attribute_search_or_value_buttons(uiLayout *layout, + const NodesModifierData &nmd, + PointerRNA *md_ptr, + const bNodeSocket &socket) +{ + char socket_id_esc[sizeof(socket.identifier) * 2]; + BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + const std::string rna_path = "[\"" + std::string(socket_id_esc) + "\"]"; + const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + + use_attribute_suffix + "\"]"; + const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) + + attribute_name_suffix + "\"]"; + + uiLayout *split = uiLayoutSplit(layout, 0.4f, false); + uiLayout *name_row = uiLayoutRow(split, false); + uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); + uiItemL(name_row, socket.name, ICON_NONE); + + uiLayout *row = uiLayoutRow(split, true); + + PointerRNA props; + uiItemFullO(row, + "object.geometry_nodes_input_attribute_toggle", + "", + ICON_SPREADSHEET, + nullptr, + WM_OP_INVOKE_DEFAULT, + 0, + &props); + RNA_string_set(&props, "modifier_name", nmd.modifier.name); + RNA_string_set(&props, "prop_path", rna_path_use_attribute.c_str()); + + const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; + if (use_attribute) { + add_attribute_search_button(row, nmd, md_ptr, rna_path_attribute_name, socket, false); + uiItemL(row, "", ICON_BLANK1); + } + else { + uiItemR(row, md_ptr, rna_path.c_str(), 0, "", ICON_NONE); + uiItemDecoratorR(row, md_ptr, rna_path.c_str(), 0); + } +} + /* Drawing the properties manually with #uiItemR instead of #uiDefAutoButsRNA allows using * the node socket identifier for the property names, since they are unique, but also having * the correct label displayed in the UI. */ @@ -1166,39 +1323,19 @@ static void draw_property_for_socket(uiLayout *layout, } default: { if (input_has_attribute_toggle(*nmd->node_group, socket_index)) { - const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + - use_attribute_suffix + "\"]"; - const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) + - attribute_name_suffix + "\"]"; - - uiLayout *row = uiLayoutRow(layout, true); - const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; - if (use_attribute) { - uiItemR(row, md_ptr, rna_path_attribute_name.c_str(), 0, socket.name, ICON_NONE); - } - else { - uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE); - } - PointerRNA props; - uiItemFullO(row, - "object.geometry_nodes_input_attribute_toggle", - "", - ICON_SPREADSHEET, - nullptr, - WM_OP_INVOKE_DEFAULT, - 0, - &props); - RNA_string_set(&props, "modifier_name", nmd->modifier.name); - RNA_string_set(&props, "prop_path", rna_path_use_attribute.c_str()); + add_attribute_search_or_value_buttons(layout, *nmd, md_ptr, socket); } else { - uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); + uiLayout *row = uiLayoutRow(layout, false); + uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE); + uiItemDecoratorR(row, md_ptr, rna_path, 0); } } } } static void draw_property_for_output_socket(uiLayout *layout, + const NodesModifierData &nmd, PointerRNA *md_ptr, const bNodeSocket &socket) { @@ -1207,7 +1344,13 @@ static void draw_property_for_output_socket(uiLayout *layout, const std::string rna_path_attribute_name = "[\"" + StringRef(socket_id_esc) + attribute_name_suffix + "\"]"; - uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, socket.name, ICON_NONE); + uiLayout *split = uiLayoutSplit(layout, 0.4f, false); + uiLayout *name_row = uiLayoutRow(split, false); + uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); + uiItemL(name_row, socket.name, ICON_NONE); + + uiLayout *row = uiLayoutRow(split, true); + add_attribute_search_button(row, nmd, md_ptr, rna_path_attribute_name, socket, true); } static void panel_draw(const bContext *C, Panel *panel) @@ -1219,7 +1362,9 @@ static void panel_draw(const bContext *C, Panel *panel) NodesModifierData *nmd = static_cast(ptr->data); uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, true); + /* Decorators are added manually for supported properties because the + * attribute/value toggle requires a manually built layout anyway. */ + uiLayoutSetPropDecorate(layout, false); uiTemplateID(layout, C, @@ -1282,7 +1427,7 @@ static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel) if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { if (socket_type_has_attribute_toggle(*socket)) { - draw_property_for_output_socket(layout, ptr, *socket); + draw_property_for_output_socket(layout, *nmd, ptr, *socket); } } } diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index ffe2a0d63c3..830a7d4070c 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -109,7 +109,7 @@ class GeometryValueLog : public ValueLog { std::optional pointcloud_info; std::optional instances_info; - GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry); + GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false); Span attributes() const { @@ -189,7 +189,12 @@ class GeoLogger { Set log_full_sockets_; threading::EnumerableThreadSpecific threadlocals_; + /* These are only optional since they don't have a default constructor. */ + std::unique_ptr input_geometry_log_; + std::unique_ptr output_geometry_log_; + friend LocalGeoLogger; + friend ModifierLog; public: GeoLogger(Set log_full_sockets) @@ -198,6 +203,16 @@ class GeoLogger { { } + void log_input_geometry(const GeometrySet &geometry) + { + input_geometry_log_ = std::make_unique(geometry); + } + + void log_output_geometry(const GeometrySet &geometry) + { + output_geometry_log_ = std::make_unique(geometry); + } + LocalGeoLogger &local() { return threadlocals_.local(); @@ -283,6 +298,9 @@ class ModifierLog { destruct_ptr root_tree_logs_; Vector> logged_values_; + std::unique_ptr input_geometry_log_; + std::unique_ptr output_geometry_log_; + public: ModifierLog(GeoLogger &logger); @@ -303,6 +321,9 @@ class ModifierLog { const SpaceSpreadsheet &sspreadsheet); void foreach_node_log(FunctionRef fn) const; + const GeometryValueLog *input_geometry_log() const; + const GeometryValueLog *output_geometry_log() const; + private: using LogByTreeContext = Map; diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index d39f8367686..852f52f38cd 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -26,6 +26,8 @@ namespace blender::nodes::geometry_nodes_eval_log { using fn::CPPType; ModifierLog::ModifierLog(GeoLogger &logger) + : input_geometry_log_(std::move(logger.input_geometry_log_)), + output_geometry_log_(std::move(logger.output_geometry_log_)) { root_tree_logs_ = allocator_.construct(); @@ -106,6 +108,15 @@ void ModifierLog::foreach_node_log(FunctionRef fn) const } } +const GeometryValueLog *ModifierLog::input_geometry_log() const +{ + return input_geometry_log_.get(); +} +const GeometryValueLog *ModifierLog::output_geometry_log() const +{ + return output_geometry_log_.get(); +} + const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const { const destruct_ptr *node_log = node_logs_.lookup_ptr_as(node_name); From 9b1b4b9e32c8ac86e460204bb93e0ddc42ad9e49 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Thu, 21 Oct 2021 21:00:17 +0200 Subject: [PATCH 1052/1500] Node Editor: Introduce color overlay and dashed wires theme setting This patch includes code from D9891 and D12754, so credit goes to Juanfran and Dalai. I updated the patches to work with `master` and with the new overlay toggle. The reason to include both changes as part of one patch is that the dimmed dashed lines work much better together with colored wires. Theme setting for dash opacity: {F11370574, size=full} {F11286177, size=full, autoplay, loop} {F11149912, size=full} For adding the overlay I used `SpaceImageOverlay` as reference, although I'm not familiar with this code so there might be mistakes. Reviewed By: #user_interface, HooglyBoogly Differential Revision: https://developer.blender.org/D12886 --- .../datafiles/userdef/userdef_default_theme.c | 1 + .../presets/interface_theme/Blender_Light.xml | 1 + .../keyconfig/keymap_data/blender_default.py | 2 + release/scripts/startup/bl_ui/space_node.py | 32 +++++++++ .../blenloader/intern/versioning_300.c | 17 +++++ .../blenloader/intern/versioning_userdef.c | 4 ++ source/blender/editors/include/UI_resources.h | 1 + source/blender/editors/interface/resources.c | 3 + source/blender/editors/space_node/drawnode.cc | 70 +++++++++++++++---- .../blender/editors/space_node/node_draw.cc | 4 +- .../shaders/gpu_shader_2D_nodelink_frag.glsl | 6 +- .../shaders/gpu_shader_2D_nodelink_vert.glsl | 25 ++++++- source/blender/makesdna/DNA_space_types.h | 12 ++++ source/blender/makesdna/DNA_userdef_types.h | 4 +- source/blender/makesrna/intern/rna_space.c | 44 ++++++++++++ source/blender/makesrna/intern/rna_userdef.c | 6 ++ 16 files changed, 212 insertions(+), 20 deletions(-) diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index eebdcf84fd2..ad93f87e962 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -828,6 +828,7 @@ const bTheme U_theme_default = { .facedot_size = 4, .noodle_curving = 4, .grid_levels = 2, + .dash_alpha = 0.5f, .syntaxl = RGBA(0x565656ff), .syntaxs = RGBA(0x975b5bff), .syntaxb = RGBA(0xccb83dff), diff --git a/release/scripts/presets/interface_theme/Blender_Light.xml b/release/scripts/presets/interface_theme/Blender_Light.xml index 8b7995cef4c..8074371c450 100644 --- a/release/scripts/presets/interface_theme/Blender_Light.xml +++ b/release/scripts/presets/interface_theme/Blender_Light.xml @@ -956,6 +956,7 @@ distor_node="#749797" noodle_curving="4" grid_levels="2" + dash_alpha="0.5" input_node="#cb3d4a" output_node="#cb3d4a" filter_node="#6c696f" diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 3ba848e6caf..e70fe63677a 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2064,6 +2064,8 @@ def km_node_editor(params): {"properties": [("data_path", 'tool_settings.use_snap')]}), ("wm.context_menu_enum", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("data_path", 'tool_settings.snap_node_element')]}), + ("wm.context_toggle", {"type": 'Z', "value": 'PRESS', "alt": True, "shift": True}, + {"properties": [("data_path", "space_data.overlay.show_overlays")]}), *_template_items_context_menu("NODE_MT_context_menu", params.context_menu_event), ]) diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 8e2533edac6..da3db48d962 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -49,6 +49,7 @@ class NODE_HT_header(Header): scene = context.scene snode = context.space_data + overlay = snode.overlay snode_id = snode.id id_from = snode.id_from tool_settings = context.tool_settings @@ -205,6 +206,13 @@ class NODE_HT_header(Header): if tool_settings.snap_node_element != 'GRID': row.prop(tool_settings, "snap_target", text="") + # Overlay toggle & popover + row = layout.row(align=True) + row.prop(overlay, "show_overlays", icon='OVERLAY', text="") + sub = row.row(align=True) + sub.active = overlay.show_overlays + sub.popover(panel="NODE_PT_overlay", text="") + class NODE_MT_editor_menus(Menu): bl_idname = "NODE_MT_editor_menus" @@ -680,6 +688,29 @@ class NODE_PT_quality(bpy.types.Panel): col.prop(snode, "use_auto_render") +class NODE_PT_overlay(Panel): + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'HEADER' + bl_label = "Overlays" + bl_ui_units_x = 7 + + def draw(self, context): + layout = self.layout + layout.label(text="Node Editor Overlays") + + snode = context.space_data + overlay = snode.overlay + + layout.active = overlay.show_overlays + + col = layout.column() + col.prop(overlay, "show_wire_color", text="Wire Colors") + + col.separator() + + col.prop(snode, "show_annotation", text="Annotations") + + class NODE_UL_interface_sockets(bpy.types.UIList): def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index): socket = item @@ -848,6 +879,7 @@ classes = ( NODE_PT_backdrop, NODE_PT_quality, NODE_PT_annotation, + NODE_PT_overlay, NODE_UL_interface_sockets, NODE_PT_node_tree_interface_inputs, NODE_PT_node_tree_interface_outputs, diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index bdee5194c9a..d5064f411c2 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1988,6 +1988,23 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 37)) { + /* Node Editor: toggle overlays on. */ + if (!DNA_struct_find(fd->filesdna, "SpaceNodeOverlay")) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { + if (space->spacetype == SPACE_NODE) { + SpaceNode *snode = (SpaceNode *)space; + snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS; + snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS; + } + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 2c01fae3d6a..b95de52a0cd 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -310,6 +310,10 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) btheme->tui.panel_roundness = 0.4f; } + if (!USER_VERSION_ATLEAST(300, 37)) { + btheme->space_node.dash_alpha = 0.5f; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 61da496d344..7235d57d667 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -240,6 +240,7 @@ typedef enum ThemeColorID { TH_NODE_CURVING, TH_NODE_GRID_LEVELS, + TH_NODE_DASH_ALPHA, TH_MARKER_OUTLINE, TH_MARKER, diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index ad7c6332ee9..91832e1437c 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -657,6 +657,9 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_NODE_GRID_LEVELS: cp = &ts->grid_levels; break; + case TH_NODE_DASH_ALPHA: + cp = &ts->dash_alpha; + break; case TH_SEQ_MOVIE: cp = ts->movie; diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index afe36922b09..7bb35ab37d5 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3944,15 +3944,17 @@ static struct { GPUBatch *batch_single; /* for single line */ GPUVertBuf *inst_vbo; uint p0_id, p1_id, p2_id, p3_id; - uint colid_id, muted_id; + uint colid_id, muted_id, start_color_id, end_color_id; uint dim_factor_id; uint thickness_id; uint dash_factor_id; + uint dash_alpha_id; GPUVertBufRaw p0_step, p1_step, p2_step, p3_step; - GPUVertBufRaw colid_step, muted_step; + GPUVertBufRaw colid_step, muted_step, start_color_step, end_color_step; GPUVertBufRaw dim_factor_step; GPUVertBufRaw thickness_step; GPUVertBufRaw dash_factor_step; + GPUVertBufRaw dash_alpha_step; uint count; bool enabled; } g_batch_link; @@ -3973,6 +3975,12 @@ static void nodelink_batch_reset() g_batch_link.inst_vbo, g_batch_link.thickness_id, &g_batch_link.thickness_step); GPU_vertbuf_attr_get_raw_data( g_batch_link.inst_vbo, g_batch_link.dash_factor_id, &g_batch_link.dash_factor_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.dash_alpha_id, &g_batch_link.dash_alpha_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.start_color_id, &g_batch_link.start_color_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.end_color_id, &g_batch_link.end_color_step); g_batch_link.count = 0; } @@ -4088,6 +4096,10 @@ static void nodelink_batch_init() &format_inst, "P3", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); g_batch_link.colid_id = GPU_vertformat_attr_add( &format_inst, "colid_doarrow", GPU_COMP_U8, 4, GPU_FETCH_INT); + g_batch_link.start_color_id = GPU_vertformat_attr_add( + &format_inst, "start_color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + g_batch_link.end_color_id = GPU_vertformat_attr_add( + &format_inst, "end_color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); g_batch_link.muted_id = GPU_vertformat_attr_add( &format_inst, "domuted", GPU_COMP_U8, 2, GPU_FETCH_INT); g_batch_link.dim_factor_id = GPU_vertformat_attr_add( @@ -4096,6 +4108,8 @@ static void nodelink_batch_init() &format_inst, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_batch_link.dash_factor_id = GPU_vertformat_attr_add( &format_inst, "dash_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + g_batch_link.dash_alpha_id = GPU_vertformat_attr_add( + &format_inst, "dash_alpha", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM); /* Alloc max count but only draw the range we need. */ GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE); @@ -4170,11 +4184,14 @@ static void nodelink_batch_add_link(const SpaceNode *snode, int th_col1, int th_col2, int th_col3, + const float start_color[4], + const float end_color[4], bool drawarrow, bool drawmuted, float dim_factor, float thickness, - float dash_factor) + float dash_factor, + float dash_alpha) { /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); @@ -4191,11 +4208,14 @@ static void nodelink_batch_add_link(const SpaceNode *snode, colid[1] = nodelink_get_color_id(th_col2); colid[2] = nodelink_get_color_id(th_col3); colid[3] = drawarrow; + copy_v4_v4((float *)GPU_vertbuf_raw_step(&g_batch_link.start_color_step), start_color); + copy_v4_v4((float *)GPU_vertbuf_raw_step(&g_batch_link.end_color_step), end_color); char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step); muted[0] = drawmuted; *(float *)GPU_vertbuf_raw_step(&g_batch_link.dim_factor_step) = dim_factor; *(float *)GPU_vertbuf_raw_step(&g_batch_link.thickness_step) = thickness; *(float *)GPU_vertbuf_raw_step(&g_batch_link.dash_factor_step) = dash_factor; + *(float *)GPU_vertbuf_raw_step(&g_batch_link.dash_alpha_step) = dash_alpha; if (g_batch_link.count == NODELINK_GROUP_SIZE) { nodelink_batch_draw(snode); @@ -4213,6 +4233,10 @@ void node_draw_link_bezier(const View2D *v2d, const float dim_factor = node_link_dim_factor(v2d, link); float thickness = 1.5f; float dash_factor = 1.0f; + + bTheme *btheme = UI_GetTheme(); + const float dash_alpha = btheme->space_node.dash_alpha; + if (snode->edittree->type == NTREE_GEOMETRY) { if (link->fromsock && link->fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) { /* Make field links a bit thinner. */ @@ -4231,6 +4255,32 @@ void node_draw_link_bezier(const View2D *v2d, if (g_batch_link.batch == nullptr) { nodelink_batch_init(); } + /* Draw single link. */ + float colors[3][4] = {{0.0f}}; + if (th_col3 != -1) { + UI_GetThemeColor4fv(th_col3, colors[0]); + } + + if (snode->overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && + snode->overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS) { + if (link->fromsock) { + copy_v4_v4(colors[1], std_node_socket_colors[link->fromsock->typeinfo->type]); + } + else { + copy_v4_v4(colors[1], std_node_socket_colors[link->tosock->typeinfo->type]); + } + + if (link->tosock) { + copy_v4_v4(colors[2], std_node_socket_colors[link->tosock->typeinfo->type]); + } + else { + copy_v4_v4(colors[2], std_node_socket_colors[link->fromsock->typeinfo->type]); + } + } + else { + UI_GetThemeColor4fv(th_col1, colors[1]); + UI_GetThemeColor4fv(th_col2, colors[2]); + } if (g_batch_link.enabled && !highlighted) { /* Add link to batch. */ @@ -4242,21 +4292,16 @@ void node_draw_link_bezier(const View2D *v2d, th_col1, th_col2, th_col3, + colors[1], + colors[2], drawarrow, drawmuted, dim_factor, thickness, - dash_factor); + dash_factor, + dash_alpha); } else { - /* Draw single link. */ - float colors[3][4] = {{0.0f}}; - if (th_col3 != -1) { - UI_GetThemeColor4fv(th_col3, colors[0]); - } - UI_GetThemeColor4fv(th_col1, colors[1]); - UI_GetThemeColor4fv(th_col2, colors[2]); - if (highlighted) { float link_preselection_highlight_color[4]; UI_GetThemeColor4fv(TH_SELECT, link_preselection_highlight_color); @@ -4274,6 +4319,7 @@ void node_draw_link_bezier(const View2D *v2d, GPU_batch_uniform_1f(batch, "dim_factor", dim_factor); GPU_batch_uniform_1f(batch, "thickness", thickness); GPU_batch_uniform_1f(batch, "dash_factor", dash_factor); + GPU_batch_uniform_1f(batch, "dash_alpha", dash_alpha); GPU_batch_draw(batch); } } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index dca09a58c9a..97655080192 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2299,7 +2299,7 @@ void node_draw_space(const bContext *C, ARegion *region) GPU_line_smooth(false); GPU_blend(GPU_BLEND_NONE); - if (snode->flag & SNODE_SHOW_GPENCIL) { + if (snode->overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && snode->flag & SNODE_SHOW_GPENCIL) { /* Draw grease-pencil annotations. */ ED_annotation_draw_view2d(C, true); } @@ -2318,7 +2318,7 @@ void node_draw_space(const bContext *C, ARegion *region) UI_view2d_view_restore(C); if (snode->treepath.last) { - if (snode->flag & SNODE_SHOW_GPENCIL) { + if (snode->overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && snode->flag & SNODE_SHOW_GPENCIL) { /* Draw grease-pencil (screen strokes, and also paint-buffer). */ ED_annotation_draw_view2d(C, false); } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl index 402a07ad4e8..134a7d00127 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl @@ -4,12 +4,14 @@ in vec4 finalColor; in float lineU; flat in float lineLength; flat in float dashFactor; +flat in float dashAlpha; flat in int isMainLine; out vec4 fragColor; -#define DASH_WIDTH 20.0 +#define DASH_WIDTH 10.0 #define ANTIALIAS 1.0 +#define MINIMUM_ALPHA 0.5 void main() { @@ -29,7 +31,7 @@ void main() float slope = 1.0 / (2.0 * t); float unclamped_alpha = 1.0 - slope * (normalized_distance_triangle - dashFactor + t); - float alpha = max(0.0, min(unclamped_alpha, 1.0)); + float alpha = max(dashAlpha, min(unclamped_alpha, 1.0)); fragColor.a *= alpha; } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl index 8f46c8eda4b..acc7030415f 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl @@ -17,15 +17,18 @@ in vec2 P1; in vec2 P2; in vec2 P3; in ivec4 colid_doarrow; +in vec4 start_color; +in vec4 end_color; in ivec2 domuted; in float dim_factor; in float thickness; in float dash_factor; +in float dash_alpha; uniform vec4 colors[6]; -# define colStart colors[colid_doarrow[0]] -# define colEnd colors[colid_doarrow[1]] +# define colStart (colid_doarrow[0] < 3 ? start_color : colors[colid_doarrow[0]]) +# define colEnd (colid_doarrow[1] < 3 ? end_color : colors[colid_doarrow[1]]) # define colShadow colors[colid_doarrow[2]] # define doArrow (colid_doarrow[3] != 0) # define doMuted (domuted[0] != 0) @@ -61,13 +64,20 @@ out vec4 finalColor; out float lineU; flat out float lineLength; flat out float dashFactor; +flat out float dashAlpha; flat out int isMainLine; +/* Define where along the noodle the gradient will starts and ends. + * Use 0.25 instead of 0.35-0.65, because of a visual shift issue. */ +const float start_gradient_threshold = 0.25; +const float end_gradient_threshold = 0.55; + void main(void) { /* Parameters for the dashed line. */ isMainLine = expand.y != 1.0 ? 0 : 1; dashFactor = dash_factor; + dashAlpha = dash_alpha; /* Approximate line length, no need for real bezier length calculation. */ lineLength = distance(P0, P3); /* TODO: Incorrect U, this leads to non-uniform dash distribution. */ @@ -109,7 +119,16 @@ void main(void) } else { /* Second pass */ - finalColor = mix(colStart, colEnd, uv.x); + if (uv.x < start_gradient_threshold) { + finalColor = colStart; + } + else if (uv.x > end_gradient_threshold) { + finalColor = colEnd; + } + else { + /* Add 0.1 to avoid a visual shift issue. */ + finalColor = mix(colStart, colEnd, uv.x + 0.1); + } expand_dist *= 0.5; if (doMuted) { finalColor[3] = 0.65; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index c77a4b7857d..5ebc81fff4f 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1509,6 +1509,15 @@ typedef struct bNodeTreePath { char display_name[64]; } bNodeTreePath; +typedef struct SpaceNodeOverlay { + int flag; +} SpaceNodeOverlay; + +typedef enum eSpaceNodeOverlay_Flag { + SN_OVERLAY_SHOW_OVERLAYS = (1 << 1), + SN_OVERLAY_SHOW_WIRE_COLORS = (1 << 2), +} eSpaceNodeOverlay_Flag; + typedef struct SpaceNode { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -1562,6 +1571,9 @@ typedef struct SpaceNode { /** Grease-pencil data. */ struct bGPdata *gpd; + SpaceNodeOverlay overlay; + char _pad2[4]; + SpaceNode_Runtime *runtime; } SpaceNode; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 4a4d0ea2586..7e1df5dec36 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -206,7 +206,6 @@ typedef struct ThemeUI { /** Intensity of the border icons. >0 will render an border around themed * icons. */ float icon_border_intensity; - float panel_roundness; char _pad2[4]; @@ -323,6 +322,8 @@ typedef struct ThemeSpace { unsigned char vertex_size, outline_width, obcenter_dia, facedot_size; unsigned char noodle_curving; unsigned char grid_levels; + char _pad5[3]; + float dash_alpha; /* syntax for textwindow and nodes */ unsigned char syntaxl[4], syntaxs[4]; /* in nodespace used for backdrop matte */ @@ -345,6 +346,7 @@ typedef struct ThemeSpace { unsigned char active_strip[4], selected_strip[4]; /** For dopesheet - scale factor for size of keyframes (i.e. height of channels). */ + char _pad7[1]; float keyframe_scale_fac; unsigned char editmesh_active[4]; diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 552aa6ece6c..cd6a7b64061 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2348,6 +2348,15 @@ static char *rna_SpaceSequencerTimelineOverlay_path(PointerRNA *UNUSED(ptr)) } /* Space Node Editor */ +static PointerRNA rna_SpaceNode_overlay_get(PointerRNA *ptr) +{ + return rna_pointer_inherit_refine(ptr, &RNA_SpaceNodeOverlay, ptr->data); +} + +static char *rna_SpaceNodeOverlay_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("overlay"); +} static void rna_SpaceNodeEditor_node_tree_set(PointerRNA *ptr, const PointerRNA value, @@ -7004,6 +7013,32 @@ static void rna_def_space_node_path_api(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_flag(func, FUNC_USE_CONTEXT); } +static void rna_def_space_node_overlay(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SpaceNodeOverlay", NULL); + RNA_def_struct_sdna(srna, "SpaceNode"); + RNA_def_struct_nested(brna, srna, "SpaceNodeEditor"); + RNA_def_struct_path_func(srna, "rna_SpaceNodeOverlay_path"); + RNA_def_struct_ui_text( + srna, "Overlay Settings", "Settings for display of overlays in the Node Editor"); + + prop = RNA_def_property(srna, "show_overlays", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", SN_OVERLAY_SHOW_OVERLAYS); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Show Overlays", "Display overlays like colored or dashed wires"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); + + prop = RNA_def_property(srna, "show_wire_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", SN_OVERLAY_SHOW_WIRE_COLORS); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text( + prop, "Show Wire Colors", "Color node links based on their connected sockets"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); +} + static void rna_def_space_node(BlenderRNA *brna) { StructRNA *srna; @@ -7187,6 +7222,15 @@ static void rna_def_space_node(BlenderRNA *brna) prop, "Auto-offset Direction", "Direction to offset nodes on insertion"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); + /* Overlays */ + prop = RNA_def_property(srna, "overlay", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "SpaceNodeOverlay"); + RNA_def_property_pointer_funcs(prop, "rna_SpaceNode_overlay_get", NULL, NULL, NULL); + RNA_def_property_ui_text( + prop, "Overlay Settings", "Settings for display of overlays in the Node Editor"); + + rna_def_space_node_overlay(brna); RNA_api_space_node(srna); } diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 6d7bb9118b1..c80ba4ef6e6 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2917,6 +2917,12 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna) prop, "Grid Levels", "Amount of grid lines displayed in the background"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "dash_alpha", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_ui_text(prop, "Dashed Lines Opacity", "Opacity for the dashed lines in wires"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "input_node", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_float_sdna(prop, NULL, "syntaxn"); RNA_def_property_array(prop, 3); From 0a63297a8818e0dba89ce169bd39a2f800534721 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Thu, 21 Oct 2021 21:22:43 +0200 Subject: [PATCH 1053/1500] Nodes: Fix missing variable --- source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl index acc7030415f..8325568988c 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl @@ -48,6 +48,7 @@ uniform bool doMuted; uniform float dim_factor; uniform float thickness; uniform float dash_factor; +uniform float dash_alpha; # define colShadow colors[0] # define colStart colors[1] From aea2287af33c17c5f310ddb20f57d23d2cbe09ac Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 21 Oct 2021 18:31:17 +0200 Subject: [PATCH 1054/1500] Tests: updated Python bundled modules test Add the new zstandard module, as well as previously missing ones. Ref D12777, T88438 --- tests/python/bl_bundled_modules.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/python/bl_bundled_modules.py b/tests/python/bl_bundled_modules.py index d3fe2861d9e..012eaf8246d 100644 --- a/tests/python/bl_bundled_modules.py +++ b/tests/python/bl_bundled_modules.py @@ -21,9 +21,14 @@ # Test that modules we ship with our Python installation are available import bz2 +import certifi import ctypes +import cython import lzma import numpy +import requests import sqlite3 import ssl +import urllib3 import zlib +import zstandard From be558d2d9775b3d9d1f84d316d2675a205932d92 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 21 Oct 2021 17:00:37 +0200 Subject: [PATCH 1055/1500] Fix T92363: OptiX fails with ambient occlusion node, after recent changes This triggered a compiler bug where it does not handle the sub.s16 PTX instruction. Instead refactor the code so we don't need to do uint16_t subtraction at all. Also update OptiX device to remove the AO pass direct callable. Thanks Patrick Mours for figuring this out. --- intern/cycles/device/optix/device_impl.cpp | 3 --- intern/cycles/device/optix/device_impl.h | 1 - .../cycles/kernel/integrator/integrator_shade_shadow.h | 1 + .../kernel/integrator/integrator_shade_surface.h | 10 ++++------ .../cycles/kernel/integrator/integrator_shade_volume.h | 5 ++--- intern/cycles/kernel/kernel_path_state.h | 5 +---- 6 files changed, 8 insertions(+), 17 deletions(-) diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index 89f4b696b4c..29e46be7745 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -377,9 +377,6 @@ bool OptiXDevice::load_kernels(const uint kernel_features) group_descs[PG_CALL_SVM_BEVEL].callables.moduleDC = optix_module; group_descs[PG_CALL_SVM_BEVEL].callables.entryFunctionNameDC = "__direct_callable__svm_node_bevel"; - group_descs[PG_CALL_AO_PASS].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; - group_descs[PG_CALL_AO_PASS].callables.moduleDC = optix_module; - group_descs[PG_CALL_AO_PASS].callables.entryFunctionNameDC = "__direct_callable__ao_pass"; } optix_assert(optixProgramGroupCreate( diff --git a/intern/cycles/device/optix/device_impl.h b/intern/cycles/device/optix/device_impl.h index 3695ac6afc2..b20d42f8c61 100644 --- a/intern/cycles/device/optix/device_impl.h +++ b/intern/cycles/device/optix/device_impl.h @@ -45,7 +45,6 @@ enum { PG_HITS_MOTION, PG_CALL_SVM_AO, PG_CALL_SVM_BEVEL, - PG_CALL_AO_PASS, NUM_PROGRAM_GROUPS }; diff --git a/intern/cycles/kernel/integrator/integrator_shade_shadow.h b/intern/cycles/kernel/integrator/integrator_shade_shadow.h index 2d056a0b76f..a82254e1dea 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_shade_shadow.h @@ -136,6 +136,7 @@ ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) = throughput; INTEGRATOR_STATE_WRITE(state, shadow_path, transparent_bounce) += 1; + INTEGRATOR_STATE_WRITE(state, shadow_path, rng_offset) += PRNG_BOUNCE_NUM; } /* Note we do not need to check max_transparent_bounce here, the number diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index 2fb0dcc2097..3724b05c6b0 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -200,9 +200,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( state, path, render_pixel_index); - INTEGRATOR_STATE_WRITE( - shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE(state, path, rng_offset) - - PRNG_BOUNCE_NUM * transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE( + state, path, rng_offset); INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE( state, path, rng_hash); INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE( @@ -370,9 +369,8 @@ ccl_device_forceinline void integrate_surface_ao_pass( INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( state, path, render_pixel_index); - INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = - INTEGRATOR_STATE(state, path, rng_offset) - - PRNG_BOUNCE_NUM * INTEGRATOR_STATE(state, path, transparent_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE( + state, path, rng_offset); INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE( state, path, rng_hash); INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE( diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index 1dd701237a8..d0aabb550c0 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -799,9 +799,8 @@ ccl_device_forceinline void integrate_volume_direct_light( INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( state, path, render_pixel_index); - INTEGRATOR_STATE_WRITE( - shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE(state, path, rng_offset) - - PRNG_BOUNCE_NUM * transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE( + state, path, rng_offset); INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE( state, path, rng_hash); INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE( diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h index 428eb3498f7..a0584f0b219 100644 --- a/intern/cycles/kernel/kernel_path_state.h +++ b/intern/cycles/kernel/kernel_path_state.h @@ -299,11 +299,8 @@ ccl_device_inline void path_state_rng_load(ConstIntegratorState state, ccl_device_inline void shadow_path_state_rng_load(ConstIntegratorShadowState state, ccl_private RNGState *rng_state) { - const uint shadow_bounces = INTEGRATOR_STATE(state, shadow_path, transparent_bounce); - rng_state->rng_hash = INTEGRATOR_STATE(state, shadow_path, rng_hash); - rng_state->rng_offset = INTEGRATOR_STATE(state, shadow_path, rng_offset) + - PRNG_BOUNCE_NUM * shadow_bounces; + rng_state->rng_offset = INTEGRATOR_STATE(state, shadow_path, rng_offset); rng_state->sample = INTEGRATOR_STATE(state, shadow_path, sample); } From be171b295fc7defa451ef8eba8bc9dbf260c511a Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Thu, 21 Oct 2021 15:29:56 -0400 Subject: [PATCH 1056/1500] Cleanup: Use array utility for cursor events --- .../blender/editors/animation/time_scrub_ui.c | 2 +- source/blender/editors/armature/pose_lib_2.c | 4 +-- source/blender/editors/gpencil/gpencil_fill.c | 2 +- .../editors/interface/interface_handlers.c | 4 +-- source/blender/editors/space_text/text_ops.c | 10 +++--- .../editors/space_view3d/view3d_edit.c | 20 +++++------ source/blender/editors/util/ed_draw.c | 6 ++-- .../windowmanager/intern/wm_event_query.c | 6 ++-- .../windowmanager/intern/wm_event_system.c | 36 +++++++------------ .../windowmanager/intern/wm_operators.c | 14 +++----- 10 files changed, 39 insertions(+), 65 deletions(-) diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index f3cfbabd544..2ddaa2c415e 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -208,7 +208,7 @@ bool ED_time_scrub_event_in_region(const ARegion *region, const wmEvent *event) { rcti rect = region->winrct; rect.ymin = rect.ymax - UI_TIME_SCRUB_MARGIN_Y; - return BLI_rcti_isect_pt(&rect, event->xy[0], event->xy[1]); + return BLI_rcti_isect_pt_v(&rect, event->xy); } void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *region, bDopeSheet *dopesheet) diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c index 328ca0265c1..002a4f74037 100644 --- a/source/blender/editors/armature/pose_lib_2.c +++ b/source/blender/editors/armature/pose_lib_2.c @@ -25,6 +25,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_math.h" #include "BLI_string.h" #include "BLT_translation.h" @@ -379,8 +380,7 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent * if (pbd->release_confirm_info.use_release_confirm) { BLI_assert(event != NULL); - pbd->release_confirm_info.drag_start_xy[0] = event->xy[0]; - pbd->release_confirm_info.drag_start_xy[1] = event->xy[1]; + copy_v2_v2_int(pbd->release_confirm_info.drag_start_xy, event->xy); pbd->release_confirm_info.init_event_type = WM_userdef_event_type_from_keymap_type( event->type); } diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 52487e07f53..1b69947b294 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -2107,7 +2107,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) if (region) { bool in_bounds = false; /* Perform bounds check */ - in_bounds = BLI_rcti_isect_pt(®ion->winrct, event->xy[0], event->xy[1]); + in_bounds = BLI_rcti_isect_pt_v(®ion->winrct, event->xy); if ((in_bounds) && (region->regiontype == RGN_TYPE_WINDOW)) { tgpf->mouse[0] = event->mval[0]; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 205c25d562a..1b6fb696a64 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -10648,7 +10648,7 @@ static int ui_handle_menu_event(bContext *C, menu->menuretval = UI_RETURN_OUT; } } - else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->xy[0], event->xy[1])) { + else if (saferct && !BLI_rctf_isect_pt_v(&saferct->parent, event->xy)) { if (block->flag & UI_BLOCK_OUT_1) { menu->menuretval = UI_RETURN_OK; } @@ -10944,7 +10944,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle const double duration = menu->scrolltimer->duration; - float event_xy[2] = {event->xy[0], event->xy[1]}; + float event_xy[2] = {UNPACK2(event->xy)}; ui_window_to_block_fl(region, block, &event_xy[0], &event_xy[1]); diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index b7a79a320e8..458a1be0308 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -29,6 +29,7 @@ #include "DNA_text_types.h" #include "BLI_blenlib.h" +#include "BLI_math.h" #include "BLI_math_base.h" #include "BLT_translation.h" @@ -2598,14 +2599,12 @@ static void text_scroll_apply(bContext *C, wmOperator *op, const wmEvent *event) /* compute mouse move distance */ if (tsc->is_first) { - tsc->mval_prev[0] = mval[0]; - tsc->mval_prev[1] = mval[1]; + copy_v2_v2_int(tsc->mval_prev, mval); tsc->is_first = false; } if (event->type != MOUSEPAN) { - tsc->mval_delta[0] = mval[0] - tsc->mval_prev[0]; - tsc->mval_delta[1] = mval[1] - tsc->mval_prev[1]; + sub_v2_v2v2_int(tsc->mval_delta, mval, tsc->mval_prev); } /* accumulate scroll, in float values for events that give less than one @@ -2757,8 +2756,7 @@ static int text_scroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEPAN) { text_update_character_width(st); - tsc->mval_prev[0] = event->xy[0]; - tsc->mval_prev[1] = event->xy[1]; + copy_v2_v2_int(tsc->mval_prev, event->xy); /* Sensitivity of scroll set to 4pix per line/char */ tsc->mval_delta[0] = (event->xy[0] - event->prev_xy[0]) * st->runtime.cwidth_px / 4; tsc->mval_delta[1] = (event->xy[1] - event->prev_xy[1]) * st->runtime.lheight_px / 4; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index e7e41799dcb..44652fdaf5a 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -473,12 +473,11 @@ static void viewops_data_create(bContext *C, vod->init.dist = rv3d->dist; vod->init.camzoom = rv3d->camzoom; copy_qt_qt(vod->init.quat, rv3d->viewquat); - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; - vod->init.event_xy[1] = vod->prev.event_xy[1] = event->xy[1]; + copy_v2_v2_int(vod->init.event_xy, event->xy); + copy_v2_v2_int(vod->prev.event_xy, event->xy); if (viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) { - vod->init.event_xy_offset[0] = 0; - vod->init.event_xy_offset[1] = 0; + zero_v2_int(vod->init.event_xy_offset); } else { /* Simulate the event starting in the middle of the region. */ @@ -548,10 +547,9 @@ static void viewops_data_create(bContext *C, ED_view3d_win_to_vector(vod->region, (const float[2]){UNPACK2(event->mval)}, vod->init.mousevec); { - const int event_xy_offset[2] = { - event->xy[0] + vod->init.event_xy_offset[0], - event->xy[1] + vod->init.event_xy_offset[1], - }; + int event_xy_offset[2]; + add_v2_v2v2_int(event_xy_offset, event->xy, vod->init.event_xy_offset); + /* For rotation with trackball rotation. */ calctrackballvec(&vod->region->winrct, event_xy_offset, vod->init.trackvec); } @@ -1010,14 +1008,12 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) event_xy[1] = 2 * event->xy[1] - event->prev_xy[1]; } else { - event_xy[0] = event->prev_xy[0]; - event_xy[1] = event->prev_xy[1]; + copy_v2_v2_int(event_xy, event->prev_xy); } } else { /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */ - event_xy[0] = event->prev_xy[0]; - event_xy[1] = event->xy[1]; + copy_v2_v2_int(event_xy, event->prev_xy); } viewrotate_apply(vod, event_xy); diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c index 97540068c30..089d1635438 100644 --- a/source/blender/editors/util/ed_draw.c +++ b/source/blender/editors/util/ed_draw.c @@ -360,8 +360,7 @@ static void slider_update_factor(tSlider *slider, const wmEvent *event) /* Reduced factor delta in precision mode (shift held). */ slider->raw_factor += slider->precision ? (factor_delta / 8) : factor_delta; slider->factor = slider->raw_factor; - slider->last_cursor[0] = event->xy[0]; - slider->last_cursor[1] = event->xy[1]; + copy_v2_v2_int(slider->last_cursor, event->xy); if (!slider->overshoot) { slider->factor = clamp_f(slider->factor, 0, 1); @@ -403,8 +402,7 @@ tSlider *ED_slider_create(struct bContext *C) */ void ED_slider_init(struct tSlider *slider, const wmEvent *event) { - slider->last_cursor[0] = event->xy[0]; - slider->last_cursor[1] = event->xy[1]; + copy_v2_v2_int(slider->last_cursor, event->xy); } /** diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 5687e024975..9ee114674ed 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -315,10 +315,8 @@ bool WM_event_drag_test_with_delta(const wmEvent *event, const int drag_delta[2] bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2]) { - const int drag_delta[2] = { - prev_xy[0] - event->xy[0], - prev_xy[1] - event->xy[1], - }; + int drag_delta[2]; + sub_v2_v2v2_int(drag_delta, prev_xy, event->xy); return WM_event_drag_test_with_delta(event, drag_delta); } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 3c0853cf926..df976d9a4cd 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -152,13 +152,11 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) /* Logic for setting previous value is documented on the #wmEvent struct, * see #wm_event_add_ghostevent for the implementation of logic this follows. */ - - win->eventstate->xy[0] = event->xy[0]; - win->eventstate->xy[1] = event->xy[1]; + copy_v2_v2_int(win->eventstate->xy, event->xy); if (event->type == MOUSEMOVE) { - win->eventstate->prev_xy[0] = event->prev_xy[0] = win->eventstate->xy[0]; - win->eventstate->prev_xy[1] = event->prev_xy[1] = win->eventstate->xy[1]; + copy_v2_v2_int(win->eventstate->prev_xy, win->eventstate->xy); + copy_v2_v2_int(event->prev_xy, win->eventstate->xy); } else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) { win->eventstate->prev_val = event->prev_val = win->eventstate->val; @@ -3176,13 +3174,11 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) if (WM_event_drag_test(event, event->prev_click_xy)) { win->event_queue_check_drag_handled = true; - int x = event->xy[0]; - int y = event->xy[1]; + int xy[2] = {UNPACK2(event->xy)}; short val = event->val; short type = event->type; - event->xy[0] = event->prev_click_xy[0]; - event->xy[1] = event->prev_click_xy[1]; + copy_v2_v2_int(event->xy, event->prev_click_xy); event->val = KM_CLICK_DRAG; event->type = event->prev_type; @@ -3192,8 +3188,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) event->val = val; event->type = type; - event->xy[0] = x; - event->xy[1] = y; + copy_v2_v2_int(event->xy, xy); win->event_queue_check_click = false; if (!wm_action_not_handled(action)) { @@ -3238,11 +3233,9 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) else { /* Position is where the actual click happens, for more * accurate selecting in case the mouse drifts a little. */ - int x = event->xy[0]; - int y = event->xy[1]; + int xy[2] = {UNPACK2(event->xy)}; - event->xy[0] = event->prev_click_xy[0]; - event->xy[1] = event->prev_click_xy[1]; + copy_v2_v2_int(event->xy, event->prev_click_xy); event->val = KM_CLICK; CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK"); @@ -3250,8 +3243,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) action |= wm_handlers_do_intern(C, win, event, handlers); event->val = KM_RELEASE; - event->xy[0] = x; - event->xy[1] = y; + copy_v2_v2_int(event->xy, xy); } } } @@ -3788,8 +3780,7 @@ void wm_event_do_handlers(bContext *C) } /* Update previous mouse position for following events to use. */ - win->eventstate->prev_xy[0] = event->xy[0]; - win->eventstate->prev_xy[1] = event->xy[1]; + copy_v2_v2_int(win->eventstate->prev_xy, event->xy); /* Unlink and free here, blender-quit then frees all. */ BLI_remlink(&win->event_queue, event); @@ -4181,10 +4172,10 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler_priority(ListBase *handlers, static bool event_or_prev_in_rect(const wmEvent *event, const rcti *rect) { - if (BLI_rcti_isect_pt(rect, event->xy[0], event->xy[1])) { + if (BLI_rcti_isect_pt_v(rect, event->xy)) { return true; } - if (event->type == MOUSEMOVE && BLI_rcti_isect_pt(rect, event->prev_xy[0], event->prev_xy[1])) { + if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(rect, event->prev_xy)) { return true; } return false; @@ -4682,8 +4673,7 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi wmWindow *win_other = WM_window_find_under_cursor(wm, win, win, mval, mval); if (win_other) { - event->xy[0] = mval[0]; - event->xy[1] = mval[1]; + copy_v2_v2_int(event->xy, mval); return win_other; } } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index b6a4a3a94bf..98ae1eb62dc 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2274,11 +2274,8 @@ static void radial_control_set_initial_mouse(RadialControl *rc, const wmEvent *e float d[2] = {0, 0}; float zoom[2] = {1, 1}; - rc->initial_mouse[0] = event->xy[0]; - rc->initial_mouse[1] = event->xy[1]; - - rc->initial_co[0] = event->xy[0]; - rc->initial_co[1] = event->xy[1]; + copy_v2_v2_int(rc->initial_mouse, event->xy); + copy_v2_v2_int(rc->initial_co, event->xy); switch (rc->subtype) { case PROP_NONE: @@ -2954,14 +2951,12 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even if (!has_numInput) { if (rc->slow_mode) { if (rc->subtype == PROP_ANGLE) { - const float position[2] = {event->xy[0], event->xy[1]}; - /* calculate the initial angle here first */ delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0]; delta[1] = rc->initial_mouse[1] - rc->slow_mouse[1]; /* precision angle gets calculated from dial and gets added later */ - angle_precision = -0.1f * BLI_dial_angle(rc->dial, position); + angle_precision = -0.1f * BLI_dial_angle(rc->dial, event->xy); } else { delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0]; @@ -2984,8 +2979,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even } } else { - delta[0] = rc->initial_mouse[0] - event->xy[0]; - delta[1] = rc->initial_mouse[1] - event->xy[1]; + sub_v2_v2v2(delta, rc->initial_mouse, event->xy); if (rc->zoom_prop) { RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom); delta[0] /= zoom[0]; From 0c668852765ddac0ebe2d34f347095baac7eacc4 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 21 Oct 2021 16:37:41 -0300 Subject: [PATCH 1057/1500] View3D Snap Cursor: Fix drawing state incorrectly reset The viewport was being changed, so it needs to be reset. --- source/blender/editors/space_view3d/view3d_cursor_snap.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index ff5c5c6a946..1cb650910ce 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -799,8 +799,6 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust /* Setup viewport & matrix. */ RegionView3D *rv3d = region->regiondata; wmViewport(®ion->winrct); - GPU_matrix_push_projection(); - GPU_matrix_push(); GPU_matrix_projection_set(rv3d->winmat); GPU_matrix_set(rv3d->viewmat); @@ -834,8 +832,7 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust GPU_blend(GPU_BLEND_NONE); /* Restore matrix. */ - GPU_matrix_pop(); - GPU_matrix_pop_projection(); + wmWindowViewport(CTX_wm_window(C)); } /** \} */ From 9aab1a46263a87980efa18ba35d87814eb2f4cda Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Thu, 21 Oct 2021 22:02:45 +0200 Subject: [PATCH 1058/1500] Cleanup: unused code Also fixes incompatible types. --- source/blender/editors/include/UI_resources.h | 1 - source/blender/editors/interface/resources.c | 3 --- 2 files changed, 4 deletions(-) diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 7235d57d667..61da496d344 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -240,7 +240,6 @@ typedef enum ThemeColorID { TH_NODE_CURVING, TH_NODE_GRID_LEVELS, - TH_NODE_DASH_ALPHA, TH_MARKER_OUTLINE, TH_MARKER, diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 91832e1437c..ad7c6332ee9 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -657,9 +657,6 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_NODE_GRID_LEVELS: cp = &ts->grid_levels; break; - case TH_NODE_DASH_ALPHA: - cp = &ts->dash_alpha; - break; case TH_SEQ_MOVIE: cp = ts->movie; From a28614879986c26c7d354ebe659e2e327503ed28 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 21 Oct 2021 20:42:41 +0200 Subject: [PATCH 1059/1500] Assets: Allow specific data-block types to be enabled by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates UI code so that we can enable the asset UI for specific data-block types by default, i.e. irrespective of the "Extended Asset Browser" experimental feature. "Mark as Asset" and "Clear Asset" are always visible in the Outliner context menu now, but are grayed out if not applicable and show a disabled hint in the tooltip. A known side-effect of this: The "Mark as Asset" and "Clear Asset" operators are enabled for action data-blocks now, even though only pose actions created through the Pose Libraries add-on are supported. If this is something worth addressing is being discussed still. Differential Revision: https://developer.blender.org/D12955 Reviewed by: Sybren Stüvel --- .../startup/bl_ui/space_filebrowser.py | 3 +- .../scripts/startup/bl_ui/space_outliner.py | 4 - source/blender/editors/asset/CMakeLists.txt | 2 + source/blender/editors/asset/ED_asset_type.h | 55 ++++++++++ .../editors/asset/intern/asset_mark_clear.cc | 7 +- .../blender/editors/asset/intern/asset_ops.cc | 100 ++++++++++++------ .../editors/asset/intern/asset_type.cc | 62 +++++++++++ source/blender/editors/include/ED_asset.h | 1 + .../interface/interface_context_menu.c | 2 +- .../blender/editors/space_file/space_file.c | 5 +- 10 files changed, 197 insertions(+), 44 deletions(-) create mode 100644 source/blender/editors/asset/ED_asset_type.h create mode 100644 source/blender/editors/asset/intern/asset_type.cc diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index a811ba93c17..641812694c8 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -190,8 +190,7 @@ class FILEBROWSER_PT_filter(FileBrowserPanel, Panel): sub = row.column(align=True) - if context.preferences.experimental.use_extended_asset_browser: - sub.prop(params, "use_filter_asset_only") + sub.prop(params, "use_filter_asset_only") filter_id = params.filter_id for identifier in dir(filter_id): diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 3d18160d90f..07fa2220915 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -313,10 +313,6 @@ class OUTLINER_MT_object(Menu): class OUTLINER_MT_asset(Menu): bl_label = "Assets" - @classmethod - def poll(cls, context): - return context.preferences.experimental.use_extended_asset_browser - def draw(self, context): layout = self.layout diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 9017f136319..b9ef8e82bba 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC intern/asset_mark_clear.cc intern/asset_ops.cc intern/asset_temp_id_consumer.cc + intern/asset_type.cc ED_asset_catalog.h ED_asset_catalog.hh @@ -50,6 +51,7 @@ set(SRC ED_asset_list.hh ED_asset_mark_clear.h ED_asset_temp_id_consumer.h + ED_asset_type.h intern/asset_library_reference.hh ) diff --git a/source/blender/editors/asset/ED_asset_type.h b/source/blender/editors/asset/ED_asset_type.h new file mode 100644 index 00000000000..ba0a3d2812f --- /dev/null +++ b/source/blender/editors/asset/ED_asset_type.h @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID; + +bool ED_asset_type_id_is_non_experimental(const struct ID *id); + +/** + * Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset, + * respecting the "Extended Asset Browser" experimental feature flag. + */ +bool ED_asset_type_is_supported(const ID *id); + +/** + * Get the filter flags (subset of #FILTER_ID_ALL) representing the asset ID types that may be + * turned into assets, respecting the "Extended Asset Browser" experimental feature flag. + * \note Does not check for #BKE_id_can_be_asset(), so may return filter flags for IDs that can + * never be assets. + */ +int64_t ED_asset_types_supported_as_filter_flags(void); + +/** + * Utility: A string enumerating the non-experimental asset types. This is useful info to + * the user, it should be displayed in tooltips or messages. Macro to support concatenating static + * strings with this (not all UI code supports dynamic strings nicely). + * Should start with a consonant, so usages can prefix it with "a" (not "an"). + */ +#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Pose Action" + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc index e7516bc94c4..4be7376a1c3 100644 --- a/source/blender/editors/asset/intern/asset_mark_clear.cc +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -39,6 +39,7 @@ #include "ED_asset_list.h" #include "ED_asset_mark_clear.h" +#include "ED_asset_type.h" bool ED_asset_mark_id(ID *id) { @@ -81,5 +82,9 @@ bool ED_asset_clear_id(ID *id) bool ED_asset_can_mark_single_from_context(const bContext *C) { /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ - return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != nullptr; + const ID *id = static_cast(CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data); + if (!id) { + return false; + } + return ED_asset_type_is_supported(id); } diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index da699768970..e2ae3b3893b 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -18,6 +18,7 @@ * \ingroup edasset */ +#include "BKE_asset.h" #include "BKE_asset_catalog.hh" #include "BKE_asset_library.hh" #include "BKE_context.h" @@ -45,30 +46,6 @@ using namespace blender; using PointerRNAVec = blender::Vector; -static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C); -static PointerRNAVec asset_operation_get_nonexperimental_ids_from_context(const bContext *C); -static bool asset_type_is_nonexperimental(const ID_Type id_type); - -static bool asset_operation_experimental_feature_poll(bContext * /*C*/) -{ - /* At this moment only the pose library is non-experimental. Still, directly marking arbitrary - * Actions as asset is not part of the stable functionality; instead, the pose library "Create - * Pose Asset" operator should be used. Actions can still be marked as asset via - * `the_action.asset_mark()` (so a function call instead of this operator), which is what the - * pose library uses internally. */ - return U.experimental.use_extended_asset_browser; -} - -static bool asset_clear_poll(bContext *C) -{ - if (asset_operation_experimental_feature_poll(C)) { - return true; - } - - PointerRNAVec pointers = asset_operation_get_nonexperimental_ids_from_context(C); - return !pointers.is_empty(); -} - /** * Return the IDs to operate on as PointerRNA vector. Either a single one ("id" context member) or * multiple ones ("selected_ids" context member). @@ -94,26 +71,51 @@ static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C) return ids; } -static PointerRNAVec asset_operation_get_nonexperimental_ids_from_context(const bContext *C) +/** + * Information about what's contained in a #PointerRNAVec, returned by + * #asset_operation_get_id_vec_stats_from_context(). + */ +struct IDVecStats { + bool has_asset = false; + bool has_supported_type = false; + bool is_single = false; +}; + +/** + * Helper to report stats about the IDs in context. Operator polls use this, also to report a + * helpful disabled hint to the user. + */ +static IDVecStats asset_operation_get_id_vec_stats_from_context(const bContext *C) { - PointerRNAVec nonexperimental; PointerRNAVec pointers = asset_operation_get_ids_from_context(C); + IDVecStats stats; + + stats.is_single = pointers.size() == 1; + for (PointerRNA &ptr : pointers) { BLI_assert(RNA_struct_is_ID(ptr.type)); ID *id = static_cast(ptr.data); - if (asset_type_is_nonexperimental(GS(id->name))) { - nonexperimental.append(ptr); + if (ED_asset_type_is_supported(id)) { + stats.has_supported_type = true; + } + if (ID_IS_ASSET(id)) { + stats.has_asset = true; } } - return nonexperimental; + + return stats; } -static bool asset_type_is_nonexperimental(const ID_Type id_type) +static const char *asset_operation_unsupported_type_msg(const bool is_single) { - /* At this moment only the pose library is non-experimental. For simplicity, allow asset - * operations on all Action datablocks (even though pose assets are limited to single frames). */ - return ELEM(id_type, ID_AC); + const char *msg_single = + "Data-block does not support asset operations - must be " + "a " ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING; + const char *msg_multiple = + "No data-block selected that supports asset operations - select at least " + "one " ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING; + return is_single ? msg_single : msg_multiple; } /* -------------------------------------------------------------------- */ @@ -203,6 +205,18 @@ static int asset_mark_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool asset_mark_poll(bContext *C) +{ + IDVecStats ctx_stats = asset_operation_get_id_vec_stats_from_context(C); + + if (!ctx_stats.has_supported_type) { + CTX_wm_operator_poll_msg_set(C, asset_operation_unsupported_type_msg(ctx_stats.is_single)); + return false; + } + + return true; +} + static void ASSET_OT_mark(wmOperatorType *ot) { ot->name = "Mark as Asset"; @@ -212,7 +226,7 @@ static void ASSET_OT_mark(wmOperatorType *ot) ot->idname = "ASSET_OT_mark"; ot->exec = asset_mark_exec; - ot->poll = asset_operation_experimental_feature_poll; + ot->poll = asset_mark_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } @@ -315,6 +329,24 @@ static int asset_clear_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool asset_clear_poll(bContext *C) +{ + IDVecStats ctx_stats = asset_operation_get_id_vec_stats_from_context(C); + + if (!ctx_stats.has_asset) { + const char *msg_single = "Data-block is not marked as asset"; + const char *msg_multiple = "No data-block selected that is marked as asset"; + CTX_wm_operator_poll_msg_set(C, ctx_stats.is_single ? msg_single : msg_multiple); + return false; + } + if (!ctx_stats.has_supported_type) { + CTX_wm_operator_poll_msg_set(C, asset_operation_unsupported_type_msg(ctx_stats.is_single)); + return false; + } + + return true; +} + static char *asset_clear_get_description(struct bContext *UNUSED(C), struct wmOperatorType *UNUSED(op), struct PointerRNA *values) diff --git a/source/blender/editors/asset/intern/asset_type.cc b/source/blender/editors/asset/intern/asset_type.cc new file mode 100644 index 00000000000..1b8e56974bf --- /dev/null +++ b/source/blender/editors/asset/intern/asset_type.cc @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#include "BLI_utildefines.h" + +#include "DNA_userdef_types.h" + +#include "BKE_lib_id.h" + +#include "ED_asset_type.h" + +bool ED_asset_type_id_is_non_experimental(const ID *id) +{ + /* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and + * #asset_type_ids_non_experimental_as_filter_flags() with this! */ + return ELEM(GS(id->name), ID_AC); +} +static int64_t asset_type_ids_non_experimental_as_filter_flags() +{ + return FILTER_ID_AC; +} + +bool ED_asset_type_is_supported(const ID *id) +{ + if (!BKE_id_can_be_asset(id)) { + return false; + } + + if (U.experimental.use_extended_asset_browser) { + /* The "Extended Asset Browser" experimental feature flag enables all asset types that can + * technically be assets. */ + return true; + } + + return ED_asset_type_id_is_non_experimental(id); +} + +int64_t ED_asset_types_supported_as_filter_flags() +{ + if (U.experimental.use_extended_asset_browser) { + return FILTER_ID_ALL; + } + + return asset_type_ids_non_experimental_as_filter_flags(); +} diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index 6b8d33fa713..3b1d87e8ff7 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -43,6 +43,7 @@ void ED_operatortypes_asset(void); #include "../asset/ED_asset_list.h" #include "../asset/ED_asset_mark_clear.h" #include "../asset/ED_asset_temp_id_consumer.h" +#include "../asset/ED_asset_type.h" /* C++ only headers. */ #ifdef __cplusplus diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 9e436601132..1e4f1a7f5dc 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -937,7 +937,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev } /* If the button represents an id, it can set the "id" context pointer. */ - if (U.experimental.use_extended_asset_browser && ED_asset_can_mark_single_from_context(C)) { + if (ED_asset_can_mark_single_from_context(C)) { ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data; /* Gray out items depending on if data-block is an asset. Preferably this could be done via diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index b93f9b34f3b..b5c395c8bc2 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -43,6 +43,7 @@ #include "WM_message.h" #include "WM_types.h" +#include "ED_asset.h" #include "ED_fileselect.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -327,8 +328,8 @@ static void file_refresh(const bContext *C, ScrArea *area) } if (ED_fileselect_is_asset_browser(sfile)) { - /* Only poses supported as non-experimental right now. */ - params->filter_id = U.experimental.use_extended_asset_browser ? FILTER_ID_ALL : FILTER_ID_AC; + /* Ask the asset code for appropriate ID filter flags for the supported assets. */ + params->filter_id = ED_asset_types_supported_as_filter_flags(); } filelist_settype(sfile->files, params->type); From 94fb47e57251d662c6cdbcf460c2b52c75b34cb0 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 21 Oct 2021 21:50:37 +0200 Subject: [PATCH 1060/1500] Assets: Enable material and world assets by default Both material and world assets should be ready to use as non-experimental feature. They were not enabled by default yet because the work from the previous commit was needed first. Objects should follow soon. Maniphest Task: https://developer.blender.org/T91752 --- source/blender/editors/asset/ED_asset_type.h | 2 +- source/blender/editors/asset/intern/asset_type.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/asset/ED_asset_type.h b/source/blender/editors/asset/ED_asset_type.h index ba0a3d2812f..1030be1ea05 100644 --- a/source/blender/editors/asset/ED_asset_type.h +++ b/source/blender/editors/asset/ED_asset_type.h @@ -48,7 +48,7 @@ int64_t ED_asset_types_supported_as_filter_flags(void); * strings with this (not all UI code supports dynamic strings nicely). * Should start with a consonant, so usages can prefix it with "a" (not "an"). */ -#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Pose Action" +#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Material, Pose Action, or World" #ifdef __cplusplus } diff --git a/source/blender/editors/asset/intern/asset_type.cc b/source/blender/editors/asset/intern/asset_type.cc index 1b8e56974bf..5be627e3fbc 100644 --- a/source/blender/editors/asset/intern/asset_type.cc +++ b/source/blender/editors/asset/intern/asset_type.cc @@ -30,11 +30,11 @@ bool ED_asset_type_id_is_non_experimental(const ID *id) { /* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and * #asset_type_ids_non_experimental_as_filter_flags() with this! */ - return ELEM(GS(id->name), ID_AC); + return ELEM(GS(id->name), ID_MA, ID_AC, ID_WO); } static int64_t asset_type_ids_non_experimental_as_filter_flags() { - return FILTER_ID_AC; + return FILTER_ID_MA | FILTER_ID_AC | FILTER_ID_WO; } bool ED_asset_type_is_supported(const ID *id) From f45470472fb16b7153795735e7c16ad400a71a07 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Thu, 21 Oct 2021 16:28:42 -0400 Subject: [PATCH 1061/1500] Cleanup: Compile warnings --- source/blender/editors/interface/interface_handlers.c | 2 +- source/blender/windowmanager/intern/wm_operators.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 1b6fb696a64..52ab13e5cd0 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -10648,7 +10648,7 @@ static int ui_handle_menu_event(bContext *C, menu->menuretval = UI_RETURN_OUT; } } - else if (saferct && !BLI_rctf_isect_pt_v(&saferct->parent, event->xy)) { + else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, (float)event->xy[0], (float)event->xy[1])) { if (block->flag & UI_BLOCK_OUT_1) { menu->menuretval = UI_RETURN_OK; } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 98ae1eb62dc..8d8905ea737 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2979,7 +2979,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even } } else { - sub_v2_v2v2(delta, rc->initial_mouse, event->xy); + sub_v2_v2v2_int(delta, rc->initial_mouse, event->xy); if (rc->zoom_prop) { RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom); delta[0] /= zoom[0]; From d9ebe25a0cfe107c21a125bd780478afd33fb342 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 21 Oct 2021 18:11:16 -0300 Subject: [PATCH 1062/1500] Fix errors in be171b295fc7d MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ´´´ error C4133: 'function': incompatible types - from 'float [2]' to 'int *' error C4133: 'function': incompatible types - from 'const int [2]' to 'const float *' ´´´ --- source/blender/editors/util/ed_draw.c | 4 ++-- source/blender/windowmanager/intern/wm_operators.c | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c index 089d1635438..2412939e859 100644 --- a/source/blender/editors/util/ed_draw.c +++ b/source/blender/editors/util/ed_draw.c @@ -360,7 +360,7 @@ static void slider_update_factor(tSlider *slider, const wmEvent *event) /* Reduced factor delta in precision mode (shift held). */ slider->raw_factor += slider->precision ? (factor_delta / 8) : factor_delta; slider->factor = slider->raw_factor; - copy_v2_v2_int(slider->last_cursor, event->xy); + copy_v2fl_v2i(slider->last_cursor, event->xy); if (!slider->overshoot) { slider->factor = clamp_f(slider->factor, 0, 1); @@ -402,7 +402,7 @@ tSlider *ED_slider_create(struct bContext *C) */ void ED_slider_init(struct tSlider *slider, const wmEvent *event) { - copy_v2_v2_int(slider->last_cursor, event->xy); + copy_v2fl_v2i(slider->last_cursor, event->xy); } /** diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 8d8905ea737..89f0206d72e 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2956,7 +2956,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even delta[1] = rc->initial_mouse[1] - rc->slow_mouse[1]; /* precision angle gets calculated from dial and gets added later */ - angle_precision = -0.1f * BLI_dial_angle(rc->dial, event->xy); + angle_precision = -0.1f * BLI_dial_angle(rc->dial, (float[2]){UNPACK2(event->xy)}); } else { delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0]; @@ -2979,7 +2979,8 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even } } else { - sub_v2_v2v2_int(delta, rc->initial_mouse, event->xy); + delta[0] = (float)(rc->initial_mouse[0] - event->xy[0]); + delta[1] = (float)(rc->initial_mouse[1] - event->xy[1]); if (rc->zoom_prop) { RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom); delta[0] /= zoom[0]; From bdbaf0301df630cefd3f753c9419646b3f858588 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 22 Oct 2021 08:46:10 +1100 Subject: [PATCH 1063/1500] Fix crash when creating a new window fails Also add operator error report. --- source/blender/editors/screen/screen_edit.c | 8 ++++++-- .../blender/windowmanager/intern/wm_window.c | 18 +++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index e1b5c355e3e..4d387ad0191 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -647,8 +647,12 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) /* Exception for background mode, we only need the screen context. */ if (!G.background) { - /* header size depends on DPI, let's verify */ - WM_window_set_dpi(win); + + /* Called even when creating the ghost window fails in #WM_window_open. */ + if (win->ghostwin) { + /* Header size depends on DPI, let's verify. */ + WM_window_set_dpi(win); + } ED_screen_global_areas_refresh(win); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index ce0f710f4c2..89f85caa729 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -49,6 +49,7 @@ #include "BKE_icons.h" #include "BKE_layer.h" #include "BKE_main.h" +#include "BKE_report.h" #include "BKE_screen.h" #include "BKE_workspace.h" @@ -914,7 +915,7 @@ int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op)) +int wm_window_new_exec(bContext *C, wmOperator *op) { wmWindow *win_src = CTX_wm_window(C); ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TYPE_ANY, 0); @@ -931,16 +932,23 @@ int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op)) false, WIN_ALIGN_PARENT_CENTER) != NULL); - return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + if (!ok) { + BKE_report(op->reports, RPT_ERROR, "Failed to create window"); + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; } -int wm_window_new_main_exec(bContext *C, wmOperator *UNUSED(op)) +int wm_window_new_main_exec(bContext *C, wmOperator *op) { wmWindow *win_src = CTX_wm_window(C); bool ok = (wm_window_copy_test(C, win_src, true, false) != NULL); - - return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + if (!ok) { + BKE_report(op->reports, RPT_ERROR, "Failed to create window"); + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; } /* fullscreen operator callback */ From 3e1baa7d539757b8e5fa870d4909354e0b5645b9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 22 Oct 2021 10:12:01 +1100 Subject: [PATCH 1064/1500] Fix T92357: assert changing scenes while in edit mode ScrArea.runtime.tool needs to be updated after switching scenes. --- source/blender/editors/include/ED_screen.h | 5 ++- source/blender/editors/screen/screen_edit.c | 10 +++++- source/blender/windowmanager/WM_toolsystem.h | 1 + .../windowmanager/intern/wm_toolsystem.c | 32 +++++++++++-------- .../blender/windowmanager/intern/wm_window.c | 7 ++-- 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 92aeaf03329..b90c7f27c57 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -210,7 +210,10 @@ void ED_screen_ensure_updated(struct wmWindowManager *wm, struct bScreen *screen); void ED_screen_do_listen(struct bContext *C, struct wmNotifier *note); bool ED_screen_change(struct bContext *C, struct bScreen *screen); -void ED_screen_scene_change(struct bContext *C, struct wmWindow *win, struct Scene *scene); +void ED_screen_scene_change(struct bContext *C, + struct wmWindow *win, + struct Scene *scene, + const bool refresh_toolsystem); void ED_screen_set_active_region(struct bContext *C, struct wmWindow *win, const int xy[2]); void ED_screen_exit(struct bContext *C, struct wmWindow *window, struct bScreen *screen); void ED_screen_animation_timer(struct bContext *C, int redraws, int sync, int enable); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 4d387ad0191..841792d5f2d 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -57,6 +57,7 @@ #include "UI_interface.h" #include "WM_message.h" +#include "WM_toolsystem.h" #include "DEG_depsgraph_query.h" @@ -1239,7 +1240,10 @@ static void screen_set_3dview_camera(Scene *scene, } } -void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene) +void ED_screen_scene_change(bContext *C, + wmWindow *win, + Scene *scene, + const bool refresh_toolsystem) { #if 0 ViewLayer *view_layer_old = WM_window_get_active_view_layer(win); @@ -1277,6 +1281,10 @@ void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene) } } } + + if (refresh_toolsystem) { + WM_toolsystem_refresh_screen_window(win); + } } ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type) diff --git a/source/blender/windowmanager/WM_toolsystem.h b/source/blender/windowmanager/WM_toolsystem.h index 018165634f2..eb89fca7b56 100644 --- a/source/blender/windowmanager/WM_toolsystem.h +++ b/source/blender/windowmanager/WM_toolsystem.h @@ -134,6 +134,7 @@ void WM_toolsystem_refresh_active(struct bContext *C); void WM_toolsystem_refresh_screen_area(struct WorkSpace *workspace, struct ViewLayer *view_layer, struct ScrArea *area); +void WM_toolsystem_refresh_screen_window(struct wmWindow *win); void WM_toolsystem_refresh_screen_all(struct Main *bmain); #ifdef __cplusplus diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index 0c24520d565..3ac1a7c0be1 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -572,25 +572,29 @@ void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, ViewLayer *view_lay } } +void WM_toolsystem_refresh_screen_window(wmWindow *win) +{ + WorkSpace *workspace = WM_window_get_active_workspace(win); + bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0}; + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + space_type_has_tools[tref->space_type] = true; + } + bScreen *screen = WM_window_get_active_screen(win); + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + area->runtime.tool = NULL; + area->runtime.is_tool_set = true; + if (space_type_has_tools[area->spacetype]) { + WM_toolsystem_refresh_screen_area(workspace, view_layer, area); + } + } +} + void WM_toolsystem_refresh_screen_all(Main *bmain) { /* Update all ScrArea's tools */ for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - WorkSpace *workspace = WM_window_get_active_workspace(win); - bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0}; - LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { - space_type_has_tools[tref->space_type] = true; - } - bScreen *screen = WM_window_get_active_screen(win); - ViewLayer *view_layer = WM_window_get_active_view_layer(win); - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - area->runtime.tool = NULL; - area->runtime.is_tool_set = true; - if (space_type_has_tools[area->spacetype]) { - WM_toolsystem_refresh_screen_area(workspace, view_layer, area); - } - } } } } diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 89f85caa729..5f684a752d8 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -854,7 +854,8 @@ wmWindow *WM_window_open(bContext *C, /* Set scene and view layer to match original window. */ STRNCPY(win->view_layer_name, view_layer->name); if (WM_window_get_active_scene(win) != scene) { - ED_screen_scene_change(C, win, scene); + /* No need to refresh the tool-system as the window has not yet finished being setup. */ + ED_screen_scene_change(C, win, scene, false); } screen->temp = temp; @@ -2271,13 +2272,13 @@ void WM_window_set_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene * /* Set scene in parent and its child windows. */ if (win_parent->scene != scene) { - ED_screen_scene_change(C, win_parent, scene); + ED_screen_scene_change(C, win_parent, scene, true); changed = true; } LISTBASE_FOREACH (wmWindow *, win_child, &wm->windows) { if (win_child->parent == win_parent && win_child->scene != scene) { - ED_screen_scene_change(C, win_child, scene); + ED_screen_scene_change(C, win_child, scene, true); changed = true; } } From 05ab3356a719ca1991dc993e2a055962e24aa138 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 22 Oct 2021 10:28:56 +1100 Subject: [PATCH 1065/1500] Cleanup: spelling in comments, use C style comments --- source/blender/blenkernel/intern/armature.c | 4 ++-- source/blender/blenkernel/intern/asset_catalog_test.cc | 6 +++--- source/blender/blenkernel/intern/collision.c | 2 +- source/blender/blenkernel/intern/object.c | 8 ++++---- source/blender/blenlib/tests/BLI_uuid_test.cc | 6 +++--- source/blender/editors/screen/area.c | 4 ++-- source/blender/editors/space_view3d/view3d_placement.c | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 65fdc9278e1..b64b050f4e7 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2245,8 +2245,8 @@ void mat3_vec_to_roll(const float mat[3][3], const float vec[3], float *r_roll) */ void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_mat[3][3]) { - const float SAFE_THRESHOLD = 6.1e-3f; /* theta above this value has good enough precision. */ - const float CRITICAL_THRESHOLD = 2.5e-4f; /* true singularity if xz distance is below this. */ + const float SAFE_THRESHOLD = 6.1e-3f; /* Theta above this value has good enough precision. */ + const float CRITICAL_THRESHOLD = 2.5e-4f; /* True singularity if XZ distance is below this. */ const float THRESHOLD_SQUARED = CRITICAL_THRESHOLD * CRITICAL_THRESHOLD; const float x = nor[0]; diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index b41fa0fe833..a6e372e4ae6 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -1039,7 +1039,7 @@ TEST_F(AssetCatalogTest, order_by_path_and_first_seen) const AssetCatalog first_sorted_cat(first_sorted_uuid, "simple/path/child", ""); const AssetCatalog last_sorted_cat(last_sorted_uuid, "simple/path/child", ""); - /* Mimick that this catalog was first-seen when loading from disk. */ + /* Mimic that this catalog was first-seen when loading from disk. */ first_seen_cat.flags.is_first_loaded = true; /* Just an assertion of the defaults; this is more to avoid confusing errors later on than an @@ -1299,7 +1299,7 @@ TEST_F(AssetCatalogTest, undo_redo_one_step) EXPECT_TRUE(service.is_undo_possbile()) << "Undo should be possible after creating an undo snapshot."; - // Undo the creation of the catalog. + /* Undo the creation of the catalog. */ service.undo(); EXPECT_FALSE(service.is_undo_possbile()) << "Undoing the only stored step should make it impossible to undo further."; @@ -1311,7 +1311,7 @@ TEST_F(AssetCatalogTest, undo_redo_one_step) EXPECT_FALSE(service.get_catalog_definition_file()->contains(other_catalog_id)) << "The CDF should also not contain the undone catalog."; - // Redo the creation of the catalog. + /* Redo the creation of the catalog. */ service.redo(); EXPECT_TRUE(service.is_undo_possbile()) << "Undoing and then redoing a step should make it possible to undo again."; diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 1c24dae430c..03957d54629 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -577,7 +577,7 @@ static float compute_collision_point_edge_tri(const float a1[3], return dist; } -// w3 is not perfect +/* `w3` is not perfect. */ static void collision_compute_barycentric(const float pv[3], const float p1[3], const float p2[3], diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 58041c84cdf..3ec7370a47f 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1145,12 +1145,12 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src) /* Problem with point caches is that several status flags (like OUTDATED or BAKED) are read-only * at RNA level, and therefore not overridable per-se. * - * This code is a workaround this to check all pointcaches from both source and destination + * This code is a workaround this to check all point-caches from both source and destination * objects in parallel, and transfer those flags when it makes sense. * * This allows to keep baked caches across liboverrides applies. * - * NOTE: This is fairly hackish and weak, but so is the pointcache system as its whole. A more + * NOTE: This is fairly hackish and weak, but so is the point-cache system as its whole. A more * robust solution would be e.g. to have a specific RNA entry point to deal with such cases * (maybe a new flag to allow override code to set values of some read-only properties?). */ @@ -2427,8 +2427,8 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f psysn->pointcache = BKE_ptcache_copy_list(&psysn->ptcaches, &psys->ptcaches, flag); } - /* XXX(campbell): from reading existing code this seems correct but intended usage of - * pointcache should /w cloth should be added in 'ParticleSystem'. */ + /* XXX(@campbellbarton): from reading existing code this seems correct but intended usage of + * point-cache should /w cloth should be added in 'ParticleSystem'. */ if (psysn->clmd) { psysn->clmd->point_cache = psysn->pointcache; } diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc index b406a0521a1..111c21cb7d1 100644 --- a/source/blender/blenlib/tests/BLI_uuid_test.cc +++ b/source/blender/blenlib/tests/BLI_uuid_test.cc @@ -24,11 +24,11 @@ TEST(BLI_uuid, generate_random) { const bUUID uuid = BLI_uuid_generate_random(); - // The 4 MSbits represent the "version" of the UUID. + /* The 4 MSbits represent the "version" of the UUID. */ const uint16_t version = uuid.time_hi_and_version >> 12; EXPECT_EQ(version, 4); - // The 2 MSbits should be 0b10, indicating compliance with RFC4122. + /* The 2 MSbits should be 0b10, indicating compliance with RFC4122. */ const uint8_t reserved = uuid.clock_seq_hi_and_reserved >> 6; EXPECT_EQ(reserved, 0b10); } @@ -42,7 +42,7 @@ TEST(BLI_uuid, generate_many_random) const bUUID uuid = BLI_uuid_generate_random(); EXPECT_NE(first_uuid, uuid); - // Check that the non-random bits are set according to RFC4122. + /* Check that the non-random bits are set according to RFC4122. */ const uint16_t version = uuid.time_hi_and_version >> 12; EXPECT_EQ(version, 4); const uint8_t reserved = uuid.clock_seq_hi_and_reserved >> 6; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 9e179dad2e8..b69a563166a 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -972,8 +972,8 @@ static void fullscreen_azone_init(ScrArea *area, ARegion *region) #define AZONEPAD_ICON (0.45f * U.widget_unit) static void region_azone_edge(AZone *az, ARegion *region) { - /* If region is overlapped (transparent background), move AZone to content. - * Note this is an arbitrary amount that matches nicely with numbers elswhere. */ + /* If region is overlapped (transparent background), move #AZone to content. + * Note this is an arbitrary amount that matches nicely with numbers elsewhere. */ int overlap_padding = (region->overlap) ? (int)(0.4f * U.widget_unit) : 0; switch (az->edge) { diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 79377e5599c..7fe97705765 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -1361,7 +1361,7 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) PropertyRNA *prop; /* WORKAROUND: properties with `_funcs_runtime` should not be saved in keymaps. - * So reasign the #PROP_IDPROPERTY flag to trick the property as not being set. + * So reassign the #PROP_IDPROPERTY flag to trick the property as not being set. * (See #RNA_property_is_set). */ PropertyFlag unsalvageable = PROP_SKIP_SAVE | PROP_HIDDEN | PROP_PTR_NO_OWNERSHIP | PROP_IDPROPERTY; From b0d64841d23aecc2f84135857e1e2841e376f460 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 22 Oct 2021 07:33:56 +0200 Subject: [PATCH 1066/1500] Cycles: keep HIP device disabled until we have binaries And clear communication about supported hardware. Ref T92397 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94a5ff27491..52958d45abf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -434,7 +434,7 @@ mark_as_advanced(WITH_CYCLES_NATIVE_ONLY) option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles CUDA compute support" ON) option(WITH_CYCLES_DEVICE_OPTIX "Enable Cycles OptiX support" ON) -option(WITH_CYCLES_DEVICE_HIP "Enable Cycles HIP support" ON) +option(WITH_CYCLES_DEVICE_HIP "Enable Cycles HIP support" OFF) mark_as_advanced(WITH_CYCLES_DEVICE_HIP) mark_as_advanced(WITH_CYCLES_DEVICE_CUDA) From fa688fbf060069f4d77fc6200b3ac5d1423ff41d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 22 Oct 2021 08:31:47 +0200 Subject: [PATCH 1067/1500] Build: improve descriptions and organization of Cycles build options --- CMakeLists.txt | 54 ++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 52958d45abf..558e1545c4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -408,39 +408,45 @@ option(WITH_CPU_SIMD "Enable SIMD instruction if they're detected o mark_as_advanced(WITH_CPU_SIMD) # Cycles -option(WITH_CYCLES "Enable Cycles Render Engine" ON) -option(WITH_CYCLES_STANDALONE "Build Cycles standalone application" OFF) -option(WITH_CYCLES_STANDALONE_GUI "Build Cycles standalone with GUI" OFF) -option(WITH_CYCLES_OSL "Build Cycles with OSL support" ON) -option(WITH_CYCLES_EMBREE "Build Cycles with Embree support" ON) -option(WITH_CYCLES_CUDA_BINARIES "Build Cycles CUDA binaries" OFF) -option(WITH_CYCLES_CUBIN_COMPILER "Build cubins with nvrtc based compiler instead of nvcc" OFF) -option(WITH_CYCLES_CUDA_BUILD_SERIAL "Build cubins one after another (useful on machines with limited RAM)" OFF) -mark_as_advanced(WITH_CYCLES_CUDA_BUILD_SERIAL) -set(CYCLES_TEST_DEVICES CPU CACHE STRING "Run regression tests on the specified device types (CPU CUDA OPTIX)" ) -set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 compute_75 CACHE STRING "CUDA architectures to build binaries for") -mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH) -option(WITH_CYCLES_HIP_BINARIES "Build Cycles HIP binaries" OFF) -unset(PLATFORM_DEFAULT) -option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON) -option(WITH_CYCLES_DEBUG_NAN "Build Cycles with additional asserts for detecting NaNs and invalid values" OFF) -option(WITH_CYCLES_NATIVE_ONLY "Build Cycles with native kernel only (which fits current CPU, use for development only)" OFF) -option(WITH_CYCLES_KERNEL_ASAN "Build Cycles kernels with address sanitizer when WITH_COMPILER_ASAN is on, even if it's very slow" OFF) +option(WITH_CYCLES "Enable Cycles Render Engine" ON) +option(WITH_CYCLES_OSL "Build Cycles with OpenShadingLanguage support" ON) +option(WITH_CYCLES_EMBREE "Build Cycles with Embree support" ON) +option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON) + +option(WITH_CYCLES_STANDALONE "Build Cycles standalone application" OFF) +option(WITH_CYCLES_STANDALONE_GUI "Build Cycles standalone with GUI" OFF) + +option(WITH_CYCLES_DEBUG_NAN "Build Cycles with additional asserts for detecting NaNs and invalid values" OFF) +option(WITH_CYCLES_NATIVE_ONLY "Build Cycles with native kernel only (which fits current CPU, use for development only)" OFF) +option(WITH_CYCLES_KERNEL_ASAN "Build Cycles kernels with address sanitizer when WITH_COMPILER_ASAN is on, even if it's very slow" OFF) +set(CYCLES_TEST_DEVICES CPU CACHE STRING "Run regression tests on the specified device types (CPU CUDA OPTIX HIP)" ) mark_as_advanced(WITH_CYCLES_KERNEL_ASAN) -mark_as_advanced(WITH_CYCLES_CUBIN_COMPILER) mark_as_advanced(WITH_CYCLES_LOGGING) mark_as_advanced(WITH_CYCLES_DEBUG_NAN) mark_as_advanced(WITH_CYCLES_NATIVE_ONLY) -option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles CUDA compute support" ON) -option(WITH_CYCLES_DEVICE_OPTIX "Enable Cycles OptiX support" ON) -option(WITH_CYCLES_DEVICE_HIP "Enable Cycles HIP support" OFF) -mark_as_advanced(WITH_CYCLES_DEVICE_HIP) +# NVIDIA CUDA & OptiX +option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles NVIDIA CUDA compute support" ON) +option(WITH_CYCLES_DEVICE_OPTIX "Enable Cycles NVIDIA OptiX support" ON) mark_as_advanced(WITH_CYCLES_DEVICE_CUDA) -option(WITH_CUDA_DYNLOAD "Dynamically load CUDA libraries at runtime" ON) +option(WITH_CYCLES_CUDA_BINARIES "Build Cycles NVIDIA CUDA binaries" OFF) +set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 compute_75 CACHE STRING "CUDA architectures to build binaries for") +option(WITH_CYCLES_CUBIN_COMPILER "Build cubins with nvrtc based compiler instead of nvcc" OFF) +option(WITH_CYCLES_CUDA_BUILD_SERIAL "Build cubins one after another (useful on machines with limited RAM)" OFF) +option(WITH_CUDA_DYNLOAD "Dynamically load CUDA libraries at runtime (for developers, makes cuda-gdb work)" ON) +mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH) +mark_as_advanced(WITH_CYCLES_CUBIN_COMPILER) +mark_as_advanced(WITH_CYCLES_CUDA_BUILD_SERIAL) mark_as_advanced(WITH_CUDA_DYNLOAD) +# AMD HIP +option(WITH_CYCLES_DEVICE_HIP "Enable Cycles AMD HIP support" OFF) +option(WITH_CYCLES_HIP_BINARIES "Build Cycles AMD HIP binaries" OFF) +set(CYCLES_HIP_BINARIES_ARCH gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032 gfx1034 CACHE STRING "AMD HIP architectures to build binaries for") +mark_as_advanced(WITH_CYCLES_DEVICE_HIP) +mark_as_advanced(CYCLES_HIP_BINARIES_ARCH) + # Draw Manager option(WITH_DRAW_DEBUG "Add extra debug capabilities to Draw Manager" OFF) mark_as_advanced(WITH_DRAW_DEBUG) From 622d8b77a6ae4f66d652207c1d1de633baf0a7ee Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 22 Oct 2021 08:24:24 +0200 Subject: [PATCH 1068/1500] Cycles: improve communication of supported GPUs in preferences Mention required CUDA and OptiX compute capability and minimum driver version. For HIP there is a placeholder until we know the supported architectures. --- intern/cycles/blender/addon/properties.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 7ac59cf563e..1d8ebe94694 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1373,8 +1373,18 @@ class CyclesPreferences(bpy.types.AddonPreferences): if not found_device: col = box.column(align=True) - col.label(text="No compatible GPUs found for path tracing", icon='INFO') - col.label(text="Cycles will render on the CPU", icon='BLANK1') + col.label(text="No compatible GPUs found for Cycles", icon='INFO') + + if device_type == 'CUDA': + col.label(text="Requires NVIDIA GPU with compute capability 3.0", icon='BLANK1') + elif device_type == 'OPTIX': + col.label(text="Requires NVIDIA GPU with compute capability 5.0", icon='BLANK1') + col.label(text="and NVIDIA driver version 470 or newer", icon='BLANK1') + elif device_type == 'HIP': + import sys + col.label(text="Requires discrete AMD GPU with ??? architecture", icon='BLANK1') + if sys.platform[:3] == "win": + col.label(text="and AMD driver version ??? or newer", icon='BLANK1') return for device in devices: From c37121f16c14519a9056217a2f02fc9078b6e05e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 21 Oct 2021 17:34:05 +0200 Subject: [PATCH 1069/1500] Fix T91410: Make Single User operation ignores linked data-blocks. Now 'Make Single User' will also create local copy of linked data as needed. IMPORTANT: Unlike with local data, this always happen, even if linked data has only one user. This avoids e.g. cases like two local objects sharing a same linked mesh, then when calling 'Make Single User -> Object and ObData' on both objects, yu expect both of your objects to get localized meshes, not one of them keeping its linked, un-editable mesh. --- .../blender/editors/object/object_relations.c | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 5c7e1e1fa01..1cc293ba227 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1677,6 +1677,14 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) /** \name Make Single User Operator * \{ */ +static bool single_data_needs_duplication(ID *id) +{ + /* NOTE: When dealing with linked data, we always make alocal copy of it. + * While in theory we could rather make it local when it only has one user, this is difficult + * in practice with current code of this function. */ + return (id != NULL && (id->us > 1 || ID_IS_LINKED(id))); +} + static void libblock_relink_collection(Collection *collection, const bool do_collection) { if (do_collection) { @@ -1800,8 +1808,7 @@ static void single_obdata_users( FOREACH_OBJECT_FLAG_BEGIN (scene, view_layer, v3d, flag, ob) { if (!ID_IS_LINKED(ob)) { id = ob->data; - - if (id && id->us > 1 && !ID_IS_LINKED(id)) { + if (single_data_needs_duplication(id)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); switch (ob->type) { @@ -1895,8 +1902,16 @@ static void single_object_action_users( { FOREACH_OBJECT_FLAG_BEGIN (scene, view_layer, v3d, flag, ob) { if (!ID_IS_LINKED(ob)) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_animdata_copy_id_action(bmain, &ob->id); + AnimData *adt = BKE_animdata_from_id(&ob->id); + if (adt == NULL) { + continue; + } + + ID *id_act = (ID *)adt->action; + if (single_data_needs_duplication(id_act)) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + BKE_animdata_duplicate_id_action(bmain, &ob->id, USER_DUP_ACT | USER_DUP_LINKED_ID); + } } } FOREACH_OBJECT_FLAG_END; @@ -1909,10 +1924,14 @@ static void single_objectdata_action_users( if (!ID_IS_LINKED(ob) && ob->data != NULL) { ID *id_obdata = (ID *)ob->data; AnimData *adt = BKE_animdata_from_id(id_obdata); + if (adt == NULL) { + continue; + } + ID *id_act = (ID *)adt->action; - if (id_act && id_act->us > 1) { + if (single_data_needs_duplication(id_act)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_animdata_copy_id_action(bmain, id_obdata); + BKE_animdata_duplicate_id_action(bmain, &ob->id, USER_DUP_ACT | USER_DUP_LINKED_ID); } } } @@ -1928,18 +1947,13 @@ static void single_mat_users( FOREACH_OBJECT_FLAG_BEGIN (scene, view_layer, v3d, flag, ob) { if (!ID_IS_LINKED(ob)) { for (a = 1; a <= ob->totcol; a++) { - ma = BKE_object_material_get(ob, a); - if (ma) { - /* do not test for LIB_TAG_NEW or use newid: - * this functions guaranteed delivers single_users! */ + ma = BKE_object_material_get(ob, (short)a); + if (single_data_needs_duplication(&ma->id)) { + man = (Material *)BKE_id_copy(bmain, &ma->id); + BKE_animdata_copy_id_action(bmain, &man->id); - if (ma->id.us > 1) { - man = (Material *)BKE_id_copy(bmain, &ma->id); - BKE_animdata_copy_id_action(bmain, &man->id); - - man->id.us = 0; - BKE_object_material_assign(bmain, ob, man, a, BKE_MAT_ASSIGN_USERPREF); - } + man->id.us = 0; + BKE_object_material_assign(bmain, ob, man, (short)a, BKE_MAT_ASSIGN_USERPREF); } } } @@ -1982,9 +1996,7 @@ static void tag_localizable_objects(bContext *C, const int mode) CTX_DATA_BEGIN (C, Object *, object, selected_objects) { object->id.tag |= LIB_TAG_DOIT; - /* If data is also gonna to become local, mark data we're interested in - * as gonna-to-be-local. - */ + /* If obdata is also going to become local, mark it as such too. */ if (mode == MAKE_LOCAL_SELECT_OBDATA && object->data) { ID *data_id = (ID *)object->data; data_id->tag |= LIB_TAG_DOIT; From 1133b1478e237b252af033c04628de738d337a5f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 21 Oct 2021 17:38:23 +0200 Subject: [PATCH 1070/1500] Cleanup/Fix (unreported) 'make single user' handling of actions. Actions of duplicated objects would not be properly made single user, unlike obdata and materials. Further more, there is no reason to manually handle such animdata copying here, `BKE_id_copy_ex` can do that for us with the proper flags. --- .../blender/editors/object/object_relations.c | 79 +++++++++++-------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 1cc293ba227..fceccbb6df1 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1710,7 +1710,8 @@ static Collection *single_object_users_collection(Main *bmain, /* Generate new copies for objects in given collection and all its children, * and optionally also copy collections themselves. */ if (copy_collections && !is_master_collection) { - Collection *collection_new = (Collection *)BKE_id_copy(bmain, &collection->id); + Collection *collection_new = (Collection *)BKE_id_copy_ex( + bmain, &collection->id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS); id_us_min(&collection_new->id); collection = ID_NEW_SET(collection, collection_new); } @@ -1721,7 +1722,8 @@ static Collection *single_object_users_collection(Main *bmain, /* an object may be in more than one collection */ if ((ob->id.newid == NULL) && ((ob->flag & flag) == flag)) { if (!ID_IS_LINKED(ob) && BKE_object_scenes_users_get(bmain, ob) > 1) { - ID_NEW_SET(ob, BKE_id_copy(bmain, &ob->id)); + ID_NEW_SET( + ob, BKE_id_copy_ex(bmain, &ob->id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); id_us_min(ob->id.newid); } } @@ -1813,60 +1815,77 @@ static void single_obdata_users( switch (ob->type) { case OB_LAMP: - ob->data = la = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = la = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_CAMERA: - cam = ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + cam = ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); ID_NEW_REMAP(cam->dof.focus_object); break; case OB_MESH: /* Needed to remap texcomesh below. */ - me = ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); - if (me->key) { /* We do not need to set me->key->id.newid here... */ - BKE_animdata_copy_id_action(bmain, (ID *)me->key); - } + me = ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_MBALL: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_CURVE: case OB_SURF: case OB_FONT: - ob->data = cu = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = cu = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); ID_NEW_REMAP(cu->bevobj); ID_NEW_REMAP(cu->taperobj); - if (cu->key) { /* We do not need to set cu->key->id.newid here... */ - BKE_animdata_copy_id_action(bmain, (ID *)cu->key); - } break; case OB_LATTICE: - ob->data = lat = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); - if (lat->key) { /* We do not need to set lat->key->id.newid here... */ - BKE_animdata_copy_id_action(bmain, (ID *)lat->key); - } + ob->data = lat = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_ARMATURE: DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); BKE_pose_rebuild(bmain, ob, ob->data, true); break; case OB_SPEAKER: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_LIGHTPROBE: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_GPENCIL: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_HAIR: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_POINTCLOUD: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_VOLUME: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; default: printf("ERROR %s: can't copy %s\n", __func__, id->name); @@ -1877,13 +1896,6 @@ static void single_obdata_users( return; } - /* Copy animation data after object data became local, - * otherwise old and new object data will share the same - * AnimData structure, which is not what we want. - * (sergey) - */ - BKE_animdata_copy_id_action(bmain, (ID *)ob->data); - id_us_min(id); } } @@ -1949,9 +1961,8 @@ static void single_mat_users( for (a = 1; a <= ob->totcol; a++) { ma = BKE_object_material_get(ob, (short)a); if (single_data_needs_duplication(&ma->id)) { - man = (Material *)BKE_id_copy(bmain, &ma->id); - BKE_animdata_copy_id_action(bmain, &man->id); - + man = (Material *)BKE_id_copy_ex( + bmain, &ma->id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS); man->id.us = 0; BKE_object_material_assign(bmain, ob, man, (short)a, BKE_MAT_ASSIGN_USERPREF); } From 675a22b3415919740d7adc01d823b9507e30a918 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 22 Oct 2021 11:52:51 +0200 Subject: [PATCH 1071/1500] Nodes: fix link drawing for some socket types The type of sockets is `-1` in some cases, resulting in a crash when accessing the `std_node_socket_colors` array. --- source/blender/editors/space_node/drawnode.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 7bb35ab37d5..5117544aba8 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -4262,7 +4262,9 @@ void node_draw_link_bezier(const View2D *v2d, } if (snode->overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && - snode->overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS) { + snode->overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS && + ((link->fromsock == nullptr || link->fromsock->typeinfo->type >= 0) && + (link->tosock == nullptr || link->tosock->typeinfo->type >= 0))) { if (link->fromsock) { copy_v4_v4(colors[1], std_node_socket_colors[link->fromsock->typeinfo->type]); } From d1fcf93f039b0546dfd01c33daf50bd135e34344 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 22 Oct 2021 11:56:05 +0200 Subject: [PATCH 1072/1500] Geometry Nodes: disable attribute search for non-attribute string sockets This is a simplified version of D12730 by @erik85. I added attribute search only to one legacy node for testing purposes. --- source/blender/editors/space_node/drawnode.cc | 17 +++++++++++++++-- source/blender/nodes/NOD_node_declaration.hh | 14 ++++++++++++++ .../nodes/legacy/node_geo_attribute_fill.cc | 2 +- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 5117544aba8..8a63a1f3505 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -73,6 +73,7 @@ #include "NOD_composite.h" #include "NOD_geometry.h" +#include "NOD_node_declaration.hh" #include "NOD_shader.h" #include "NOD_texture.h" #include "node_intern.h" /* own include */ @@ -3535,6 +3536,18 @@ static void node_file_output_socket_draw(bContext *C, } } +static bool socket_needs_attribute_search(bNode &node, bNodeSocket &socket) +{ + if (node.declaration == nullptr) { + return false; + } + if (socket.in_out == SOCK_OUT) { + return false; + } + const int socket_index = BLI_findindex(&node.inputs, &socket); + return node.declaration->inputs()[socket_index]->is_attribute_name(); +} + static void std_node_socket_draw( bContext *C, uiLayout *layout, PointerRNA *ptr, PointerRNA *node_ptr, const char *text) { @@ -3591,8 +3604,8 @@ static void std_node_socket_draw( uiLayout *row = uiLayoutSplit(layout, 0.4f, false); uiItemL(row, text, 0); - const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; - if (node_tree->type == NTREE_GEOMETRY) { + if (socket_needs_attribute_search(*node, *sock)) { + const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; node_geometry_add_attribute_search_button(C, node_tree, node, ptr, row); } else { diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index da0ce6a3907..1481e69c00e 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -88,6 +88,7 @@ class SocketDeclaration { bool hide_value_ = false; bool is_multi_input_ = false; bool no_mute_links_ = false; + bool is_attribute_name_ = false; InputSocketFieldType input_field_type_ = InputSocketFieldType::None; OutputFieldDependency output_field_dependency_; @@ -105,6 +106,7 @@ class SocketDeclaration { StringRefNull name() const; StringRefNull description() const; StringRefNull identifier() const; + bool is_attribute_name() const; InputSocketFieldType input_field_type() const; const OutputFieldDependency &output_field_dependency() const; @@ -163,6 +165,12 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { return *(Self *)this; } + Self &is_attribute_name(bool value = true) + { + decl_->is_attribute_name_ = value; + return *(Self *)this; + } + /** The input socket allows passing in a field. */ Self &supports_field() { @@ -349,6 +357,12 @@ inline StringRefNull SocketDeclaration::description() const { return description_; } + +inline bool SocketDeclaration::is_attribute_name() const +{ + return is_attribute_name_; +} + inline InputSocketFieldType SocketDeclaration::input_field_type() const { return input_field_type_; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc index 3c50ae5c837..1458b6df9ba 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void geo_node_attribute_fill_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Attribute"); + b.add_input("Attribute").is_attribute_name(); b.add_input("Value", "Value"); b.add_input("Value", "Value_001"); b.add_input("Value", "Value_002"); From d092933abbadb3a6d5ab53a0b2b3b865cd5c9079 Mon Sep 17 00:00:00 2001 From: Sayak Biswas Date: Thu, 21 Oct 2021 20:57:17 +0200 Subject: [PATCH 1073/1500] Cycles: various fixes for HIP and compilation of HIP binaries * Additional structs added to the hipew loader for device props * Adds hipRTC functions to the loader for future usage * Enables CPU+GPU usage for HIP * Cleanup to the adaptive kernel compilation process * Fix for kernel compilation failures with HIP with latest master Ref T92393, D12958 --- extern/hipew/include/hipew.h | 141 ++++++++++++++++++++++ extern/hipew/src/hipew.c | 23 ++++ intern/cycles/blender/addon/properties.py | 2 +- intern/cycles/device/hip/device_impl.cpp | 24 ++-- intern/cycles/kernel/CMakeLists.txt | 57 ++++----- intern/cycles/kernel/device/hip/globals.h | 4 +- 6 files changed, 208 insertions(+), 43 deletions(-) diff --git a/extern/hipew/include/hipew.h b/extern/hipew/include/hipew.h index aa42fdf8ecd..d18cf67524d 100644 --- a/extern/hipew/include/hipew.h +++ b/extern/hipew/include/hipew.h @@ -425,6 +425,105 @@ typedef struct HIPdevprop_st { int textureAlign; } HIPdevprop; +typedef struct { + // 32-bit Atomics + unsigned hasGlobalInt32Atomics : 1; ///< 32-bit integer atomics for global memory. + unsigned hasGlobalFloatAtomicExch : 1; ///< 32-bit float atomic exch for global memory. + unsigned hasSharedInt32Atomics : 1; ///< 32-bit integer atomics for shared memory. + unsigned hasSharedFloatAtomicExch : 1; ///< 32-bit float atomic exch for shared memory. + unsigned hasFloatAtomicAdd : 1; ///< 32-bit float atomic add in global and shared memory. + + // 64-bit Atomics + unsigned hasGlobalInt64Atomics : 1; ///< 64-bit integer atomics for global memory. + unsigned hasSharedInt64Atomics : 1; ///< 64-bit integer atomics for shared memory. + + // Doubles + unsigned hasDoubles : 1; ///< Double-precision floating point. + + // Warp cross-lane operations + unsigned hasWarpVote : 1; ///< Warp vote instructions (__any, __all). + unsigned hasWarpBallot : 1; ///< Warp ballot instructions (__ballot). + unsigned hasWarpShuffle : 1; ///< Warp shuffle operations. (__shfl_*). + unsigned hasFunnelShift : 1; ///< Funnel two words into one with shift&mask caps. + + // Sync + unsigned hasThreadFenceSystem : 1; ///< __threadfence_system. + unsigned hasSyncThreadsExt : 1; ///< __syncthreads_count, syncthreads_and, syncthreads_or. + + // Misc + unsigned hasSurfaceFuncs : 1; ///< Surface functions. + unsigned has3dGrid : 1; ///< Grid and group dims are 3D (rather than 2D). + unsigned hasDynamicParallelism : 1; ///< Dynamic parallelism. +} hipDeviceArch_t; + +typedef struct hipDeviceProp_t { + char name[256]; ///< Device name. + size_t totalGlobalMem; ///< Size of global memory region (in bytes). + size_t sharedMemPerBlock; ///< Size of shared memory region (in bytes). + int regsPerBlock; ///< Registers per block. + int warpSize; ///< Warp size. + int maxThreadsPerBlock; ///< Max work items per work group or workgroup max size. + int maxThreadsDim[3]; ///< Max number of threads in each dimension (XYZ) of a block. + int maxGridSize[3]; ///< Max grid dimensions (XYZ). + int clockRate; ///< Max clock frequency of the multiProcessors in khz. + int memoryClockRate; ///< Max global memory clock frequency in khz. + int memoryBusWidth; ///< Global memory bus width in bits. + size_t totalConstMem; ///< Size of shared memory region (in bytes). + int major; ///< Major compute capability. On HCC, this is an approximation and features may + ///< differ from CUDA CC. See the arch feature flags for portable ways to query + ///< feature caps. + int minor; ///< Minor compute capability. On HCC, this is an approximation and features may + ///< differ from CUDA CC. See the arch feature flags for portable ways to query + ///< feature caps. + int multiProcessorCount; ///< Number of multi-processors (compute units). + int l2CacheSize; ///< L2 cache size. + int maxThreadsPerMultiProcessor; ///< Maximum resident threads per multi-processor. + int computeMode; ///< Compute mode. + int clockInstructionRate; ///< Frequency in khz of the timer used by the device-side "clock*" + ///< instructions. New for HIP. + hipDeviceArch_t arch; ///< Architectural feature flags. New for HIP. + int concurrentKernels; ///< Device can possibly execute multiple kernels concurrently. + int pciDomainID; ///< PCI Domain ID + int pciBusID; ///< PCI Bus ID. + int pciDeviceID; ///< PCI Device ID. + size_t maxSharedMemoryPerMultiProcessor; ///< Maximum Shared Memory Per Multiprocessor. + int isMultiGpuBoard; ///< 1 if device is on a multi-GPU board, 0 if not. + int canMapHostMemory; ///< Check whether HIP can map host memory + int gcnArch; ///< DEPRECATED: use gcnArchName instead + char gcnArchName[256]; ///< AMD GCN Arch Name. + int integrated; ///< APU vs dGPU + int cooperativeLaunch; ///< HIP device supports cooperative launch + int cooperativeMultiDeviceLaunch; ///< HIP device supports cooperative launch on multiple devices + int maxTexture1DLinear; ///< Maximum size for 1D textures bound to linear memory + int maxTexture1D; ///< Maximum number of elements in 1D images + int maxTexture2D[2]; ///< Maximum dimensions (width, height) of 2D images, in image elements + int maxTexture3D[3]; ///< Maximum dimensions (width, height, depth) of 3D images, in image elements + unsigned int* hdpMemFlushCntl; ///< Addres of HDP_MEM_COHERENCY_FLUSH_CNTL register + unsigned int* hdpRegFlushCntl; ///< Addres of HDP_REG_COHERENCY_FLUSH_CNTL register + size_t memPitch; /// Date: Fri, 22 Oct 2021 12:33:03 +0200 Subject: [PATCH 1074/1500] Fix T90638: Inconsistent object data behavior when link-duplicating collections. Camera, lattice and speaker object types were missing there own proper `USER_DUP_` flags, leading to not properly handling duplication of their object data. NOTE: We could probably simply opions here, by using categories (like 'GEOMETRY', 'SHADING', etc.) instead of exact object types. But this is beyond bugfix scope. --- release/datafiles/userdef/userdef_default.c | 6 +++--- release/scripts/startup/bl_ui/space_userpref.py | 9 +++++++-- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/object.c | 6 +++--- .../blenloader/intern/versioning_userdef.c | 7 +++++++ source/blender/makesdna/DNA_userdef_types.h | 3 +++ source/blender/makesrna/intern/rna_userdef.c | 15 +++++++++++++++ 7 files changed, 39 insertions(+), 9 deletions(-) diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index b82d78b927e..3cbc6b26b4a 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -35,9 +35,9 @@ const UserDef U_default = { .subversionfile = BLENDER_FILE_SUBVERSION, .flag = (USER_AUTOSAVE | USER_TOOLTIPS | USER_RELPATHS | USER_RELEASECONFIRM | USER_SCRIPT_AUTOEXEC_DISABLE | USER_NONEGFRAMES), - .dupflag = USER_DUP_MESH | USER_DUP_CURVE | USER_DUP_SURF | USER_DUP_FONT | USER_DUP_MBALL | - USER_DUP_LAMP | USER_DUP_ARM | USER_DUP_ACT | USER_DUP_LIGHTPROBE | - USER_DUP_GPENCIL, + .dupflag = USER_DUP_MESH | USER_DUP_CURVE | USER_DUP_SURF | USER_DUP_LATTICE | USER_DUP_FONT | + USER_DUP_MBALL | USER_DUP_LAMP | USER_DUP_ARM | USER_DUP_CAMERA | USER_DUP_SPEAKER | + USER_DUP_ACT | USER_DUP_LIGHTPROBE | USER_DUP_GPENCIL, .pref_flag = USER_PREF_FLAG_SAVE, .savetime = 2, .tempdir = "", diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index e5f6cff79ee..bb3b3fbc23b 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -398,21 +398,26 @@ class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Pa col = flow.column() col.prop(edit, "use_duplicate_action", text="Action") col.prop(edit, "use_duplicate_armature", text="Armature") + col.prop(edit, "use_duplicate_camera", text="Camera") col.prop(edit, "use_duplicate_curve", text="Curve") # col.prop(edit, "use_duplicate_fcurve", text="F-Curve") # Not implemented. col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil") if hasattr(edit, "use_duplicate_hair"): col.prop(edit, "use_duplicate_hair", text="Hair") - col.prop(edit, "use_duplicate_light", text="Light") + col = flow.column() + col.prop(edit, "use_duplicate_lattice", text="Lattice") + col.prop(edit, "use_duplicate_light", text="Light") col.prop(edit, "use_duplicate_lightprobe", text="Light Probe") col.prop(edit, "use_duplicate_material", text="Material") col.prop(edit, "use_duplicate_mesh", text="Mesh") col.prop(edit, "use_duplicate_metaball", text="Metaball") - col.prop(edit, "use_duplicate_particle", text="Particle") + col = flow.column() + col.prop(edit, "use_duplicate_particle", text="Particle") if hasattr(edit, "use_duplicate_pointcloud"): col.prop(edit, "use_duplicate_pointcloud", text="Point Cloud") + col.prop(edit, "use_duplicate_speaker", text="Speaker") col.prop(edit, "use_duplicate_surface", text="Surface") col.prop(edit, "use_duplicate_text", text="Text") # col.prop(edit, "use_duplicate_texture", text="Texture") # Not implemented. diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 32b607ecf9b..3f8b56b2736 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 37 +#define BLENDER_FILE_SUBVERSION 38 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 3ec7370a47f..e85c6b4c7c5 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2760,12 +2760,12 @@ Object *BKE_object_duplicate(Main *bmain, } break; case OB_LATTICE: - if (dupflag != 0) { + if (dupflag & USER_DUP_LATTICE) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_CAMERA: - if (dupflag != 0) { + if (dupflag & USER_DUP_CAMERA) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; @@ -2775,7 +2775,7 @@ Object *BKE_object_duplicate(Main *bmain, } break; case OB_SPEAKER: - if (dupflag != 0) { + if (dupflag & USER_DUP_SPEAKER) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index b95de52a0cd..170e6be715a 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -916,6 +916,13 @@ void blo_do_versions_userdef(UserDef *userdef) userdef->flag &= ~USER_FLAG_UNUSED_5; } + if (!USER_VERSION_ATLEAST(300, 38)) { + /* Patch to set Dupli Lattice/Camera/Speaker. */ + userdef->dupflag |= USER_DUP_LATTICE; + userdef->dupflag |= USER_DUP_CAMERA; + userdef->dupflag |= USER_DUP_SPEAKER; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 7e1df5dec36..4f91d6b2fbb 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1248,6 +1248,9 @@ typedef enum eDupli_ID_Flags { USER_DUP_HAIR = (1 << 14), USER_DUP_POINTCLOUD = (1 << 15), USER_DUP_VOLUME = (1 << 16), + USER_DUP_LATTICE = (1 << 17), + USER_DUP_CAMERA = (1 << 18), + USER_DUP_SPEAKER = (1 << 19), USER_DUP_OBDATA = (~0) & ((1 << 24) - 1), diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index c80ba4ef6e6..37d2b711b7d 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -5181,6 +5181,11 @@ static void rna_def_userdef_edit(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Duplicate Curve", "Causes curve data to be duplicated with the object"); + prop = RNA_def_property(srna, "use_duplicate_lattice", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_LATTICE); + RNA_def_property_ui_text( + prop, "Duplicate Lattice", "Causes lattice data to be duplicated with the object"); + prop = RNA_def_property(srna, "use_duplicate_text", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_FONT); RNA_def_property_ui_text( @@ -5196,6 +5201,16 @@ static void rna_def_userdef_edit(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Duplicate Armature", "Causes armature data to be duplicated with the object"); + prop = RNA_def_property(srna, "use_duplicate_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_CAMERA); + RNA_def_property_ui_text( + prop, "Duplicate Camera", "Causes camera data to be duplicated with the object"); + + prop = RNA_def_property(srna, "use_duplicate_speaker", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_SPEAKER); + RNA_def_property_ui_text( + prop, "Duplicate Speaker", "Causes speaker data to be duplicated with the object"); + prop = RNA_def_property(srna, "use_duplicate_light", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_LAMP); RNA_def_property_ui_text( From 282516e53eba9bb3aaddd67b2b099fea98bd4c1f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 21 Oct 2021 19:25:38 +0200 Subject: [PATCH 1075/1500] Cleanup: refactor float/half conversions for clarity --- intern/cycles/integrator/pass_accessor.cpp | 2 +- .../cycles/integrator/pass_accessor_cpu.cpp | 4 +- intern/cycles/kernel/device/cpu/image.h | 4 +- intern/cycles/kernel/device/cuda/compat.h | 7 + intern/cycles/kernel/device/gpu/kernel.h | 2 +- intern/cycles/kernel/device/optix/compat.h | 7 + intern/cycles/util/util_half.h | 172 +++++++++--------- intern/cycles/util/util_image.h | 4 +- 8 files changed, 103 insertions(+), 99 deletions(-) diff --git a/intern/cycles/integrator/pass_accessor.cpp b/intern/cycles/integrator/pass_accessor.cpp index 4ef9ce7ef42..1308b03b06c 100644 --- a/intern/cycles/integrator/pass_accessor.cpp +++ b/intern/cycles/integrator/pass_accessor.cpp @@ -115,7 +115,7 @@ static void pad_pixels(const BufferParams &buffer_params, } if (destination.pixels_half_rgba) { - const half one = float_to_half(1.0f); + const half one = float_to_half_display(1.0f); half4 *pixel = destination.pixels_half_rgba + destination.offset; for (size_t i = 0; i < size; i++, pixel++) { diff --git a/intern/cycles/integrator/pass_accessor_cpu.cpp b/intern/cycles/integrator/pass_accessor_cpu.cpp index 80908271ff6..e3cb81d31b7 100644 --- a/intern/cycles/integrator/pass_accessor_cpu.cpp +++ b/intern/cycles/integrator/pass_accessor_cpu.cpp @@ -148,8 +148,8 @@ inline void PassAccessorCPU::run_get_pass_kernel_processor_half_rgba( film_apply_pass_pixel_overlays_rgba(kfilm_convert, buffer, pixel_rgba); - float4_store_half(&pixel->x, - make_float4(pixel_rgba[0], pixel_rgba[1], pixel_rgba[2], pixel_rgba[3])); + *pixel = float4_to_half4_display( + make_float4(pixel_rgba[0], pixel_rgba[1], pixel_rgba[2], pixel_rgba[3])); } }); } diff --git a/intern/cycles/kernel/device/cpu/image.h b/intern/cycles/kernel/device/cpu/image.h index 44c5d7ef065..93f956e354d 100644 --- a/intern/cycles/kernel/device/cpu/image.h +++ b/intern/cycles/kernel/device/cpu/image.h @@ -72,12 +72,12 @@ template struct TextureInterpolator { static ccl_always_inline float4 read(half4 r) { - return half4_to_float4(r); + return half4_to_float4_image(r); } static ccl_always_inline float4 read(half r) { - float f = half_to_float(r); + float f = half_to_float_image(r); return make_float4(f, f, f, 1.0f); } diff --git a/intern/cycles/kernel/device/cuda/compat.h b/intern/cycles/kernel/device/cuda/compat.h index 685c7a5b753..8a50eb1a3d5 100644 --- a/intern/cycles/kernel/device/cuda/compat.h +++ b/intern/cycles/kernel/device/cuda/compat.h @@ -128,6 +128,13 @@ __device__ half __float2half(const float f) return val; } +__device__ float __half2float(const half h) +{ + float val; + asm("{ cvt.f32.f16 %0, %1;}\n" : "=f"(val) : "h"(h)); + return val; +} + /* Types */ #include "util/util_half.h" diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index eeac09d4b29..335cb1ec0c0 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -516,7 +516,7 @@ ccl_device_inline void kernel_gpu_film_convert_half_rgba_common_rgba( film_apply_pass_pixel_overlays_rgba(kfilm_convert, buffer, pixel); ccl_global half4 *out = ((ccl_global half4 *)rgba) + rgba_offset + y * rgba_stride + x; - float4_store_half((ccl_global half *)out, make_float4(pixel[0], pixel[1], pixel[2], pixel[3])); + *out = float4_to_half4_display(make_float4(pixel[0], pixel[1], pixel[2], pixel[3])); } /* Common implementation for half4 destination and 3-channel input pass. */ diff --git a/intern/cycles/kernel/device/optix/compat.h b/intern/cycles/kernel/device/optix/compat.h index c9ec9be05df..d27b7d55475 100644 --- a/intern/cycles/kernel/device/optix/compat.h +++ b/intern/cycles/kernel/device/optix/compat.h @@ -120,6 +120,13 @@ __device__ half __float2half(const float f) return val; } +__device__ float __half2float(const half h) +{ + float val; + asm("{ cvt.f32.f16 %0, %1;}\n" : "=f"(val) : "h"(h)); + return val; +} + /* Types */ #include "util/util_half.h" diff --git a/intern/cycles/util/util_half.h b/intern/cycles/util/util_half.h index 81723abe1e2..0db5acd319a 100644 --- a/intern/cycles/util/util_half.h +++ b/intern/cycles/util/util_half.h @@ -59,99 +59,16 @@ struct half4 { half x, y, z, w; }; +/* Conversion to/from half float for image textures + * + * Simplified float to half for fast sampling on processor without a native + * instruction, and eliminating any NaN and inf values. */ + +ccl_device_inline half float_to_half_image(float f) +{ #if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__) - -ccl_device_inline void float4_store_half(ccl_private half *h, float4 f) -{ - h[0] = __float2half(f.x); - h[1] = __float2half(f.y); - h[2] = __float2half(f.z); - h[3] = __float2half(f.w); -} - + return __float2half(f); #else - -ccl_device_inline void float4_store_half(ccl_private half *h, float4 f) -{ - -# ifndef __KERNEL_SSE2__ - for (int i = 0; i < 4; i++) { - /* optimized float to half for pixels: - * assumes no negative, no nan, no inf, and sets denormal to 0 */ - union { - uint i; - float f; - } in; - in.f = (f[i] > 0.0f) ? ((f[i] < 65504.0f) ? f[i] : 65504.0f) : 0.0f; - int x = in.i; - - int absolute = x & 0x7FFFFFFF; - int Z = absolute + 0xC8000000; - int result = (absolute < 0x38800000) ? 0 : Z; - int rshift = (result >> 13); - - h[i] = (rshift & 0x7FFF); - } -# else - /* same as above with SSE */ - ssef x = min(max(load4f(f), 0.0f), 65504.0f); - -# ifdef __KERNEL_AVX2__ - ssei rpack = _mm_cvtps_ph(x, 0); -# else - ssei absolute = cast(x) & 0x7FFFFFFF; - ssei Z = absolute + 0xC8000000; - ssei result = andnot(absolute < 0x38800000, Z); - ssei rshift = (result >> 13) & 0x7FFF; - ssei rpack = _mm_packs_epi32(rshift, rshift); -# endif - - _mm_storel_pi((__m64 *)h, _mm_castsi128_ps(rpack)); -# endif -} - -# ifndef __KERNEL_HIP__ - -ccl_device_inline float half_to_float(half h) -{ - float f; - - *((int *)&f) = ((h & 0x8000) << 16) | (((h & 0x7c00) + 0x1C000) << 13) | ((h & 0x03FF) << 13); - - return f; -} -# else - -ccl_device_inline float half_to_float(std::uint32_t a) noexcept -{ - - std::uint32_t u = ((a << 13) + 0x70000000U) & 0x8fffe000U; - - std::uint32_t v = __float_as_uint(__uint_as_float(u) * - __uint_as_float(0x77800000U) /*0x1.0p+112f*/) + - 0x38000000U; - - u = (a & 0x7fff) != 0 ? v : u; - - return __uint_as_float(u) * __uint_as_float(0x07800000U) /*0x1.0p-112f*/; -} - -# endif /* __KERNEL_HIP__ */ - -ccl_device_inline float4 half4_to_float4(half4 h) -{ - float4 f; - - f.x = half_to_float(h.x); - f.y = half_to_float(h.y); - f.z = half_to_float(h.z); - f.w = half_to_float(h.w); - - return f; -} - -ccl_device_inline half float_to_half(float f) -{ const uint u = __float_as_uint(f); /* Sign bit, shifted to its position. */ uint sign_bit = u & 0x80000000; @@ -170,9 +87,82 @@ ccl_device_inline half float_to_half(float f) value_bits = (exponent_bits == 0 ? 0 : value_bits); /* Re-insert sign bit and return. */ return (value_bits | sign_bit); +#endif } +ccl_device_inline float half_to_float_image(half h) +{ +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__) + return __half2float(h); +#else + const int x = ((h & 0x8000) << 16) | (((h & 0x7c00) + 0x1C000) << 13) | ((h & 0x03FF) << 13); + return __int_as_float(x); #endif +} + +ccl_device_inline float4 half4_to_float4_image(const half4 h) +{ + /* Unable to use because it gives different results half_to_float_image, can we + * modify float_to_half_image so the conversion results are identical? */ +#if 0 /* defined(__KERNEL_AVX2__) */ + /* CPU: AVX. */ + __m128i x = _mm_castpd_si128(_mm_load_sd((const double *)&h)); + return float4(_mm_cvtph_ps(x)); +#endif + + const float4 f = make_float4(half_to_float_image(h.x), + half_to_float_image(h.y), + half_to_float_image(h.z), + half_to_float_image(h.w)); + return f; +} + +/* Conversion to half float texture for display. + * + * Simplified float to half for fast display texture conversion on processors + * without a native instruction. Assumes no negative, no NaN, no inf, and sets + * denormal to 0. */ + +ccl_device_inline half float_to_half_display(const float f) +{ +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__) + return __float2half(f); +#else + const int x = __float_as_int((f > 0.0f) ? ((f < 65504.0f) ? f : 65504.0f) : 0.0f); + const int absolute = x & 0x7FFFFFFF; + const int Z = absolute + 0xC8000000; + const int result = (absolute < 0x38800000) ? 0 : Z; + const int rshift = (result >> 13); + return (rshift & 0x7FFF); +#endif +} + +ccl_device_inline half4 float4_to_half4_display(const float4 f) +{ +#ifdef __KERNEL_SSE2__ + /* CPU: SSE and AVX. */ + ssef x = min(max(load4f(f), 0.0f), 65504.0f); +# ifdef __KERNEL_AVX2__ + ssei rpack = _mm_cvtps_ph(x, 0); +# else + ssei absolute = cast(x) & 0x7FFFFFFF; + ssei Z = absolute + 0xC8000000; + ssei result = andnot(absolute < 0x38800000, Z); + ssei rshift = (result >> 13) & 0x7FFF; + ssei rpack = _mm_packs_epi32(rshift, rshift); +# endif + half4 h; + _mm_storel_pi((__m64 *)&h, _mm_castsi128_ps(rpack)); + return h; +#else + /* GPU and scalar fallback. */ + const half4 h = {float_to_half_display(f.x), + float_to_half_display(f.y), + float_to_half_display(f.z), + float_to_half_display(f.w)}; + return h; +#endif +} CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_image.h b/intern/cycles/util/util_image.h index 27ec7ffb423..b082b971613 100644 --- a/intern/cycles/util/util_image.h +++ b/intern/cycles/util/util_image.h @@ -56,7 +56,7 @@ template<> inline float util_image_cast_to_float(uint16_t value) } template<> inline float util_image_cast_to_float(half value) { - return half_to_float(value); + return half_to_float_image(value); } /* Cast float value to output pixel type. */ @@ -88,7 +88,7 @@ template<> inline uint16_t util_image_cast_from_float(float value) } template<> inline half util_image_cast_from_float(float value) { - return float_to_half(value); + return float_to_half_image(value); } CCL_NAMESPACE_END From ab1909fe06b0d8c1d5eee4824fb19413e0de1b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 22 Oct 2021 14:01:47 +0200 Subject: [PATCH 1076/1500] Deps: Python, install Cython package Cython was already bundled with Blender's libraries in SVN (as dependency of Numpy, see rB5bddfde217b1), but was never actually installed in the CMake install step. As a result, `import cython` would fail. This is now fixed. --- source/creator/CMakeLists.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 5ee9a3f4342..de560e39606 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -639,6 +639,18 @@ if(UNIX AND NOT APPLE) PATTERN "*.h" EXCLUDE # some includes are not in include dirs PATTERN "*.a" EXCLUDE # ./core/lib/libnpymath.a - for linking, we don't need. ) + install( + DIRECTORY ${PYTHON_NUMPY_PATH}/Cython + DESTINATION ${TARGETDIR_VER}/python/${_target_LIB}/python${PYTHON_VERSION}/${_suffix} + PATTERN ".svn" EXCLUDE + PATTERN "__pycache__" EXCLUDE # * any cache * + PATTERN "*.pyc" EXCLUDE # * any cache * + PATTERN "*.pyo" EXCLUDE # * any cache * + ) + install( + FILES ${PYTHON_NUMPY_PATH}/cython.py + DESTINATION ${TARGETDIR_VER}/python/${_target_LIB}/python${PYTHON_VERSION}/${_suffix} + ) unset(_suffix) endif() From 269f4a3024f05378a2305b15be41d35e867dae51 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 22 Oct 2021 14:20:26 +0200 Subject: [PATCH 1077/1500] Fix VSE left crop not working Caused by using 3D math on 2D vectors, violating memory boundaries. Use temporary float[3] variable. --- source/blender/imbuf/intern/imageprocess.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index f01d2efa3ed..0ec1e4c19d8 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -388,13 +388,15 @@ static void imb_transform_calc_add_x(const float transform_matrix[4][4], const int width, float r_add_x[2]) { + float r_add_x_temp[3]; float uv_max_x[3]; zero_v3(uv_max_x); uv_max_x[0] = width; uv_max_x[1] = 0.0f; - mul_v3_m4v3(r_add_x, transform_matrix, uv_max_x); - sub_v2_v2(r_add_x, start_uv); - mul_v2_fl(r_add_x, 1.0f / width); + mul_v3_m4v3(r_add_x_temp, transform_matrix, uv_max_x); + sub_v2_v2(r_add_x_temp, start_uv); + mul_v2_fl(r_add_x_temp, 1.0f / width); + copy_v2_v2(r_add_x, r_add_x_temp); } static void imb_transform_calc_add_y(const float transform_matrix[4][4], @@ -402,13 +404,15 @@ static void imb_transform_calc_add_y(const float transform_matrix[4][4], const int height, float r_add_y[2]) { + float r_add_y_temp[3]; float uv_max_y[3]; zero_v3(uv_max_y); uv_max_y[0] = 0.0f; uv_max_y[1] = height; - mul_v3_m4v3(r_add_y, transform_matrix, uv_max_y); - sub_v2_v2(r_add_y, start_uv); - mul_v2_fl(r_add_y, 1.0f / height); + mul_v3_m4v3(r_add_y_temp, transform_matrix, uv_max_y); + sub_v2_v2(r_add_y_temp, start_uv); + mul_v2_fl(r_add_y_temp, 1.0f / height); + copy_v2_v2(r_add_y, r_add_y_temp); } typedef void (*InterpolationColorFunction)( From c4b02bb6bc8560268c54682b3627cf7742bd9552 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 22 Oct 2021 14:14:03 +0200 Subject: [PATCH 1078/1500] Fix Cycles HIP binaries always recompiling --- intern/cycles/kernel/CMakeLists.txt | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 6c87c9c32f2..6d5d386ddea 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -500,20 +500,17 @@ if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP) ) set(hip_fatbins) - macro(CYCLES_HIP_KERNEL_ADD arch prev_arch name flags sources experimental) + macro(CYCLES_HIP_KERNEL_ADD arch name flags sources experimental) set(format "fatbin") set(hip_file ${name}_${arch}.${format}) set(kernel_sources ${sources}) - if(NOT ${prev_arch} STREQUAL "none") - set(kernel_sources ${kernel_sources} ${name}_${prev_arch}.fatbin) - endif() set(hip_kernel_src "/device/hip/${name}.cpp") if(WIN32) set(hip_command ${CMAKE_COMMAND}) set(hip_flags - -E env "HIP_PATH=${HIP_ROOT_DIR}" "PATH=${HIP_PERL_PATH}" + -E env "HIP_PATH=${HIP_ROOT_DIR}" "PATH=${HIP_PERL_DIR}" ${HIP_HIPCC_EXECUTABLE}.bat) else() set(hip_command ${HIP_HIPCC_EXECUTABLE}) @@ -547,18 +544,17 @@ if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP) set(hip_flags ${hip_flags} -D __KERNEL_DEBUG__) endif() - add_custom_target( - ${hip_file} + add_custom_command( + OUTPUT ${hip_file} COMMAND ${hip_command} ${hip_flags} DEPENDS ${kernel_sources}) delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${hip_file}" ${CYCLES_INSTALL_PATH}/lib) list(APPEND hip_fatbins ${hip_file}) endmacro() - set(prev_arch "none") foreach(arch ${CYCLES_HIP_BINARIES_ARCH}) # Compile regular kernel - CYCLES_HIP_KERNEL_ADD(${arch} ${prev_arch} kernel "" "${hip_sources}" FALSE) + CYCLES_HIP_KERNEL_ADD(${arch} kernel "" "${hip_sources}" FALSE) endforeach() add_custom_target(cycles_kernel_hip ALL DEPENDS ${hip_fatbins}) From 029cf23d71b3557e1a9cacc39bde59ec99263458 Mon Sep 17 00:00:00 2001 From: Gilberto Rodrigues Date: Fri, 22 Oct 2021 14:29:18 +0200 Subject: [PATCH 1079/1500] UI: Fix misaligned icons This patch corrects the misalignment of some icons. Some of them can't be centered because they would look blurry, but look better if shifted to the right instead of shifted to the left. {F10864196 size=full} {F10864202 size=full} {F10864216} {F10864228} {F10864231 size=full} {F10864234 size=full} {F10867008 size=full} {F10867015 size=full} Reviewed By: #user_interface, pablovazquez Differential Revision: https://developer.blender.org/D12789 --- release/datafiles/blender_icons.svg | 54 +++++++++--------- .../blender_icons16/icon16_editmode_hlt.dat | Bin 1048 -> 1048 bytes .../blender_icons16/icon16_fake_user_off.dat | Bin 1048 -> 1048 bytes .../blender_icons16/icon16_fake_user_on.dat | Bin 1048 -> 1048 bytes .../blender_icons16/icon16_force_boid.dat | Bin 1048 -> 1048 bytes .../icon16_object_datamode.dat | Bin 1048 -> 1048 bytes .../blender_icons16/icon16_particlemode.dat | Bin 1048 -> 1048 bytes .../blender_icons16/icon16_pose_hlt.dat | Bin 1048 -> 1048 bytes .../icon16_restrict_render_off.dat | Bin 1048 -> 1048 bytes .../icon16_restrict_render_on.dat | Bin 1048 -> 1048 bytes .../icon16_restrict_select_off.dat | Bin 1048 -> 1048 bytes .../icon16_restrict_select_on.dat | Bin 1048 -> 1048 bytes .../icon16_restrict_view_off.dat | Bin 1048 -> 1048 bytes .../icon16_restrict_view_on.dat | Bin 1048 -> 1048 bytes .../blender_icons16/icon16_sculptmode_hlt.dat | Bin 1048 -> 1048 bytes .../blender_icons16/icon16_tpaint_hlt.dat | Bin 1048 -> 1048 bytes .../blender_icons16/icon16_uv_data.dat | Bin 1048 -> 1048 bytes .../blender_icons16/icon16_vpaint_hlt.dat | Bin 1048 -> 1048 bytes .../blender_icons16/icon16_wpaint_hlt.dat | Bin 1048 -> 1048 bytes .../blender_icons32/icon32_editmode_hlt.dat | Bin 4120 -> 4120 bytes .../blender_icons32/icon32_fake_user_off.dat | Bin 4120 -> 4120 bytes .../blender_icons32/icon32_fake_user_on.dat | Bin 4120 -> 4120 bytes .../blender_icons32/icon32_force_boid.dat | Bin 4120 -> 4120 bytes .../icon32_object_datamode.dat | Bin 4120 -> 4120 bytes .../blender_icons32/icon32_particlemode.dat | Bin 4120 -> 4120 bytes .../blender_icons32/icon32_pose_hlt.dat | Bin 4120 -> 4120 bytes .../icon32_restrict_render_off.dat | Bin 4120 -> 4120 bytes .../icon32_restrict_render_on.dat | Bin 4120 -> 4120 bytes .../icon32_restrict_select_off.dat | Bin 4120 -> 4120 bytes .../icon32_restrict_select_on.dat | Bin 4120 -> 4120 bytes .../icon32_restrict_view_off.dat | Bin 4120 -> 4120 bytes .../icon32_restrict_view_on.dat | Bin 4120 -> 4120 bytes .../blender_icons32/icon32_sculptmode_hlt.dat | Bin 4120 -> 4120 bytes .../blender_icons32/icon32_tpaint_hlt.dat | Bin 4120 -> 4120 bytes .../blender_icons32/icon32_uv_data.dat | Bin 4120 -> 4120 bytes .../blender_icons32/icon32_vpaint_hlt.dat | Bin 4120 -> 4120 bytes .../blender_icons32/icon32_wpaint_hlt.dat | Bin 4120 -> 4120 bytes 37 files changed, 27 insertions(+), 27 deletions(-) diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 70bd7dc8085..c87ad40ac0f 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -7719,7 +7719,7 @@ + transform="translate(-20)"> @@ -7741,7 +7741,7 @@ @@ -8843,14 +8843,14 @@ @@ -10425,13 +10425,13 @@ sodipodi:nodetypes="sssss" /> @@ -11015,7 +11015,7 @@ inkscape:connector-curvature="0" /> + transform="translate(-626.99987,42.000005)"> + transform="translate(-87.000002,-170)"> @@ -17302,11 +17302,11 @@ inkscape:connector-curvature="0" /> - + diff --git a/release/datafiles/blender_icons16/icon16_editmode_hlt.dat b/release/datafiles/blender_icons16/icon16_editmode_hlt.dat index 69d4e3bc4a949b06cae215c3309fd73cef2eeae6..a29abf4e4c8b05f22cd5d36945963305d6af0ca2 100644 GIT binary patch delta 20 acmbQiF@s}5%tQm0$qtMPKx$*d6lMTLDh6`^ delta 13 UcmbQiF@s}5%*KQr%o7b*03*Kyz5oCK diff --git a/release/datafiles/blender_icons16/icon16_fake_user_off.dat b/release/datafiles/blender_icons16/icon16_fake_user_off.dat index 1807949b5795d0b94b8cad3c7a74d1991d44c335..a40ac7a148ed32c569171cca1f72c099e56c852c 100644 GIT binary patch delta 13 VcmbQiF@s}5&cp}o8}pYk0{|u^1$_Vj delta 13 UcmbQiF@s}5&c=cj%o7b*03=TY&Hw-a diff --git a/release/datafiles/blender_icons16/icon16_fake_user_on.dat b/release/datafiles/blender_icons16/icon16_fake_user_on.dat index d7f5a678d406fe5864284d5260cb2011a88eb503..ea1176dacc39ec61560765f84dc098086aff6465 100644 GIT binary patch delta 13 VcmbQiF@s}5&cp}o8}pYk0{|u^1$_Vj delta 13 UcmbQiF@s}5&c=cj%o7b*03=TY&Hw-a diff --git a/release/datafiles/blender_icons16/icon16_force_boid.dat b/release/datafiles/blender_icons16/icon16_force_boid.dat index f719054d84acac167b928f37977b0aa9eb1f556a..71f89bd7c0440f75ecd56ac975e9a5f6d398c668 100644 GIT binary patch delta 20 ccmbQiF@s~nW=2NN$y*rh86R!_!l=Ro07?-C=Kufz delta 20 ccmbQiF@s~nW=2M?$y*rh86R){!l=Ro07@eU=>Px# diff --git a/release/datafiles/blender_icons16/icon16_object_datamode.dat b/release/datafiles/blender_icons16/icon16_object_datamode.dat index 2d2545d372a888175289181b9fe850dba006e749..282934245104fed8a99d0bc3a52438a619a695b6 100644 GIT binary patch delta 13 UcmbQiF@s}5%)|!vjqzKV0V7of4FCWD delta 13 UcmbQiF@s}5%*KQr%o7b*03*Kyz5oCK diff --git a/release/datafiles/blender_icons16/icon16_particlemode.dat b/release/datafiles/blender_icons16/icon16_particlemode.dat index dccb70f4bbe2b8ac6e554d50eb64897629e1f075..1a32156a7b83d83c9ec319f333f4e4d167a72f9a 100644 GIT binary patch literal 1048 zcmb`GD-HrN6h#|?M2uhwNHk(FW6+>U*aB++6p|I72o5#^!NU#+!SI#kyh)opMW@X) zIn#9J-8;9x%ovlw4yG_}7&C*qo3(ZV$5>x9O<2MUiUj={-4ptvr60hdst>jD-;L5< z8JAvFJg7av2exp7s2tP}!MCIwi1*Gw=g8HZ{_YFfHe8&CbkRA`bU@}WEj35(w517; zlpF=RhcnPQ^uC1Fqlf_~)wGy3zXdGb^hj0UlQXGIAsJMn+fg=ba8lqg^zuA{e6uV=2 z$(M_P7h)?W6a z8TuVk^lsq=E4V^mIjvCYLC+AZe2+7my+8-ut@1sxEPJIM^fX`(R-Yc(e=2ez*o0F) z!1u_q?1y-C=+k##<$DCZ7lK{5=L4#Gw8`r}E8jzYNzt>=89gmH58pjoB-i0VBZ$82|tP delta 13 UcmbQiF@s}5#>SjA%o7b*03<5}$^ZZW diff --git a/release/datafiles/blender_icons16/icon16_restrict_render_off.dat b/release/datafiles/blender_icons16/icon16_restrict_render_off.dat index db720670903dbba0609d1e0c4b60539d1999748c..5eab5eaa53854d9bdec846a207a4a0b0761e7b17 100644 GIT binary patch delta 13 VcmbQiF@s~morwl48}C>!0{|uC1v&r# delta 13 VcmbQiF@s~mosADHm?s*r001aI1wH@( diff --git a/release/datafiles/blender_icons16/icon16_restrict_render_on.dat b/release/datafiles/blender_icons16/icon16_restrict_render_on.dat index 3db7aa2bb8637eccd079edc7d37455551bbb068e..29bebb2ee75419dd9ae11f60717e960cbbed60b7 100644 GIT binary patch delta 13 VcmbQiF@s~morwl48}C>!0{|uC1v&r# delta 13 VcmbQiF@s~mosADHm?s*r001aI1wH@( diff --git a/release/datafiles/blender_icons16/icon16_restrict_select_off.dat b/release/datafiles/blender_icons16/icon16_restrict_select_off.dat index cd3910bdce830073e9156707c4f0eb2b13d6bbbc..fa8bbcb3a045c159da019c59f8616d163fa08cbf 100644 GIT binary patch delta 13 VcmbQiF@s~mnu!J+8`q>T0{|pH1quKF delta 13 VcmbQiF@s~mnvGjhm?s)=001VJ1rh)N diff --git a/release/datafiles/blender_icons16/icon16_restrict_select_on.dat b/release/datafiles/blender_icons16/icon16_restrict_select_on.dat index 9f52ef4af6b7fe3c427556b8c663f1cc6048891b..8fd1bcc97ab48c42fafe460331f75b8fa6373d78 100644 GIT binary patch delta 13 VcmbQiF@s~mnu!J+8`q>T0{|pH1quKF delta 13 VcmbQiF@s~mnvGjhm?s)=001VJ1rh)N diff --git a/release/datafiles/blender_icons16/icon16_restrict_view_off.dat b/release/datafiles/blender_icons16/icon16_restrict_view_off.dat index 717d8d04f32bfeac104fb281ff71c1f014fdc27b..7b116a56356d1254b53b3db7be65ecee3fc6e9f3 100644 GIT binary patch delta 13 VcmbQiF@s~miirj+8&~8o0{|os1qJ{B delta 13 VcmbQiF@s~mij8Y>m?s*r001Uy1quKF diff --git a/release/datafiles/blender_icons16/icon16_restrict_view_on.dat b/release/datafiles/blender_icons16/icon16_restrict_view_on.dat index 54fda79d24ec235e825252e93d334a4674988b2e..60ed6d0ef42ea60ffea0133da45d521d059a039f 100644 GIT binary patch delta 13 VcmbQiF@s~miirj+8&~8o0{|os1qJ{B delta 13 VcmbQiF@s~mij8Y>m?s*r001Uy1quKF diff --git a/release/datafiles/blender_icons16/icon16_sculptmode_hlt.dat b/release/datafiles/blender_icons16/icon16_sculptmode_hlt.dat index 30bb15380323d12d37e40ca058e7bc3d4c37b164..ab8fbd88fe13fff0f546ad3374fc4aa15d928518 100644 GIT binary patch delta 86 zcmbQiF@s}5%;XPD>=WY;Fq%(RV031*nViCSoAJwJ2PS7;p8x;n9&!vSwUAiP;{cf!P^IwlGfs04+5vl>h($ delta 79 zcmbQiF@s}5%*2Glj24qs7@ZkyCucC;=KcEr|Njj@%mu`96BRWkS1|Fj@&Y;QC-*Q} cGj5oC0!*?n+cSQh?858}Bs-WVOf+Bt0Go3nS^xk5 diff --git a/release/datafiles/blender_icons16/icon16_tpaint_hlt.dat b/release/datafiles/blender_icons16/icon16_tpaint_hlt.dat index 3537aa620995e80294d628b2f9a6b45b0ad4d765..9a56ff6177f956c6b91c22e13b9be19d4ed1417e 100644 GIT binary patch delta 20 ccmbQiF@s}5!DIzy_KEieCu-_!+>pWy07~EoEdT%j delta 26 hcmbQiF@s}5!Ndn56E$@vD=-2niH#2|m?s*r004lx2-g4r diff --git a/release/datafiles/blender_icons16/icon16_uv_data.dat b/release/datafiles/blender_icons16/icon16_uv_data.dat index b843c28ee268ed7ed5aa416b878aba3170df8a0b..bea802a3bcb854d1605caaad44198fbeb36f2ee5 100644 GIT binary patch delta 13 UcmbQiF@s}5%tQm0jWIiz0U=`qx&QzG delta 13 UcmbQiF@s}5%*KQr%o7b*03*Kyz5oCK diff --git a/release/datafiles/blender_icons16/icon16_vpaint_hlt.dat b/release/datafiles/blender_icons16/icon16_vpaint_hlt.dat index 7b4b42af987faf0a7c1939aef42da4615e08d574..0ad483ac8620250a526aa3d19ba7a121c24c76df 100644 GIT binary patch delta 13 UcmbQiF@s}5!z5<*jm;C80U@#kwEzGB delta 13 UcmbQiF@s}5!^V~=%o7b*03`zi;s5{u diff --git a/release/datafiles/blender_icons16/icon16_wpaint_hlt.dat b/release/datafiles/blender_icons16/icon16_wpaint_hlt.dat index f5eaf8ff566b504545eb027f204d6c5dccf19aff..199a859bb5dd2b1069a88d094648dfefed14784d 100644 GIT binary patch delta 13 UcmbQiF@s}5%|rv1jWsiv0V0D0+5i9m delta 13 UcmbQiF@s}5&Blfq%o7b*03_c8-T(jq diff --git a/release/datafiles/blender_icons32/icon32_editmode_hlt.dat b/release/datafiles/blender_icons32/icon32_editmode_hlt.dat index 1a2f4ad096a8abe385b0114972e4eaeca33c9e9f..6bfcaafe87546988a2b5932eda7ab41d50b9952f 100644 GIT binary patch delta 27 jcmbQCFhgO30^?)_R_@8!jHVL}1STsmT5MjxSilDWaPtSq delta 19 bcmbQCFhgO30^{TYMvKh}j63)y8gKvrL9YfO diff --git a/release/datafiles/blender_icons32/icon32_fake_user_off.dat b/release/datafiles/blender_icons32/icon32_fake_user_off.dat index 53a555f8ac573aa7c02377d6ab5bf2fdf1c647d6..2e111fa7ba6fabff5f2d8b98fefcb3b0b8662bbd 100644 GIT binary patch delta 15 WcmbQCFhgO32jfHoj?Es7C-?v=palg0 delta 15 WcmbQCFhgO32jk`d#uI!K4LATSVFe8U diff --git a/release/datafiles/blender_icons32/icon32_fake_user_on.dat b/release/datafiles/blender_icons32/icon32_fake_user_on.dat index 81ad3792699120d3faa7bb365d0f2ded7300a0ec..ce276de53d63876b514a11a7b8f5b7fee8df8853 100644 GIT binary patch delta 15 WcmbQCFhgO32jfHoj?Es7C-?v=palg0 delta 15 WcmbQCFhgO32jk`d#uI!K4LATSVFe8U diff --git a/release/datafiles/blender_icons32/icon32_force_boid.dat b/release/datafiles/blender_icons32/icon32_force_boid.dat index 9043989024be2e3d6d1b218b91ffe3253dc060f9..7fc7cb5ee8ca45304a05a9e6e49dc86f44b069e1 100644 GIT binary patch delta 30 mcmbQCFhgO(X*O1Y|NsB1Prkrb&3JXP4|_f1>CI=@eK-KovkdS6 delta 30 mcmbQCFhgO(X*O2D|NsAMPrkrb&3JRN4|_f1+0AFzeK-KpO$_@0 diff --git a/release/datafiles/blender_icons32/icon32_object_datamode.dat b/release/datafiles/blender_icons32/icon32_object_datamode.dat index 32173788c7eaa01e211476219e8607bc8c70f85f..d4086196780fc30c3a871fb28aac17cd2ecf01b6 100644 GIT binary patch delta 15 WcmbQCFhgO30^>vjj?D^;ANT+$zy#d@ delta 15 WcmbQCFhgO30^?>4#t(cG4LATQfdu6M diff --git a/release/datafiles/blender_icons32/icon32_particlemode.dat b/release/datafiles/blender_icons32/icon32_particlemode.dat index 0863a19a5f491e16d18897dbc183e5189f825604..343c568ec73492016feea2c7fc3617a9afc09375 100644 GIT binary patch literal 4120 zcmeH|yGjE=6hJrl#3G7?Ha>#2_z5v!X(v{GfR&9{2p0Z?e_*93XklYvP()A*3-J*! zrV_ExMzIkzo(ns0+010~*x5oHIGNdb+;eyLvdINOkb{Lx5R~Z;I{E2>{}%fMs@3Ww zlwdb@bHW>N&cg>tD<&M?84gzlR^c0@UBRI9K!OW+vak=PrRnZRdT+$^moT7jkLwY* zfkQo?#F&^HV4B>w&0P*R93|n@F^KzQ*E|=!x79K6-;{sa#9_|6TU>a~ zmH(P?9%i8u&ez;G^O9R4^+24J&|~Yo*8@_i|5uxwxq^8g;j{AcPVg){{HfFf?hZJ! zzZ5D&vcodnXThf(~GjvSh)hg)Lo=$B}AP9N+!60p^SY+bU&YITly@{L-A rPl#)qt$O0lA)bPhItiSz)*Bb6-E|x@um#T`@A?kbpu64Jf9?D)|IfVLpK{3>)Yg zotT|GU4Yp{A4sC*2@^{TtAaMr50ZF9)3J^o8sLneJ>;THb`9vI19QGb6MC9bM=f-& z+lwfZa~rvkc_*=DOcI6rft>g4%$>uMo)b+nZ^w>37u`2+nfxyubo;pI@=){678aai z#ebqaj~0-;3yIjgX)DYR{WwbiXULh+IN&NhSK~1dj`EH zj6pYvAI9A`()F+YPX4t%kXwBJt$~rUcR#>ek1%z$EI;gMs4AD@2Y6LRD4A7IP(b!Utrs~>XhCt**b2V)Gnx!>=_m0mltlz~hky;sm& zS5EVT>}eZWn9;u6FMc>hR#<-c<%VW7U!FR7hS9DufFwUi*Udbvw0p?8f<7gApV;R* q|Ne>(^3Ec?e~J#}f`-xJ$-Nn2%prLvZ|Dojd)Pv?-kyV+djfy8y~s5H diff --git a/release/datafiles/blender_icons32/icon32_pose_hlt.dat b/release/datafiles/blender_icons32/icon32_pose_hlt.dat index d5f05a2e730ea536a87d7f0ea0ce9e10be22aa7d..af5b10ad63a0460670204461e49fde5415ea2f2f 100644 GIT binary patch delta 16 YcmbQCFhgO31>@ugEZm!|8L#pI05ZV^t^fc4 delta 15 WcmbQCFhgO31>z12_~W$8%XCi~(sr%5Ox8es4p-G YfZmbjd;)Yz2N26mRE*iI!SjF_0C1p#+5i9m delta 327 zcmbQCFhgO33lq=(|NohQ_#hA;n4G{=&g%hW!GP98#R35!XB|ucNasyd%$arT|?&`8TKIWCafO&DK13nSl}j$;Ygu diff --git a/release/datafiles/blender_icons32/icon32_restrict_select_on.dat b/release/datafiles/blender_icons32/icon32_restrict_select_on.dat index ff7ba6ceef042427f8ffa3945e43f4956a0fda7d..a4149dc650d25ddf6d704b366c98ee4851e0e8ce 100644 GIT binary patch delta 16 YcmbQCFhgO33)AEU9KxI3nGW&-05PQne*gdg delta 19 bcmbQCFhgO33)AEWoa~#UnfCHdG!Os)M;iu} diff --git a/release/datafiles/blender_icons32/icon32_restrict_view_off.dat b/release/datafiles/blender_icons32/icon32_restrict_view_off.dat index df6268b17c7670880d6150efa09866944f51f44e..df30b2e873fa617b24b65d93470d45c94d0ea164 100644 GIT binary patch delta 34 ocmbQCFhgO31=BpUH>u7>Kp`J(D6Y0PJH7$N&HU delta 29 lcmbQCFhgO31=HjeJm! diff --git a/release/datafiles/blender_icons32/icon32_restrict_view_on.dat b/release/datafiles/blender_icons32/icon32_restrict_view_on.dat index d85859c3aed72435dffd9f530b2239a27221a704..02c08a102d254aadda376b94e02261469aa978a0 100644 GIT binary patch delta 19 bcmbQCFhgO31=B!vn%s1yVE#w6NLb3*I delta 15 WcmbQCFhgO31=D5+rW?Ey4LATRtOWi5 diff --git a/release/datafiles/blender_icons32/icon32_sculptmode_hlt.dat b/release/datafiles/blender_icons32/icon32_sculptmode_hlt.dat index e5dfa527311ac3a267f0cc44d46a120de462a64b..8590aa141c5b9ada904c98743ee45b6bcb25cfb3 100644 GIT binary patch delta 16 XcmbQCFhgO33ghGe7VgdJj4$~BEj|P= diff --git a/release/datafiles/blender_icons32/icon32_tpaint_hlt.dat b/release/datafiles/blender_icons32/icon32_tpaint_hlt.dat index cd21f05419cce8dfeb969dfa889aefbdb7b03d55..b92dec6ef42f491541ce77db787127f182eb2be3 100644 GIT binary patch delta 19 bcmbQCFhgO32jfHoj>!tl8k-9k7w`c9KI{eK delta 15 WcmbQCFhgO32jk`d#uI!K4LATSVFe8U diff --git a/release/datafiles/blender_icons32/icon32_uv_data.dat b/release/datafiles/blender_icons32/icon32_uv_data.dat index 9ef062e760e50c3bf3d1b302263fe2bdf574e804..d7bf1314c0c404c9e1c17b313098a497568e7946 100644 GIT binary patch delta 16 XcmbQCFhgO30^{TYX70_(jBoh>E&>H5 delta 15 WcmbQCFhgO30^?>4#t(cG4LATQfdu6M diff --git a/release/datafiles/blender_icons32/icon32_vpaint_hlt.dat b/release/datafiles/blender_icons32/icon32_vpaint_hlt.dat index cee921a6ba82a100ee12e1f8f9fb4e9de0661a9b..71d263c5bc9f2cf16d7a2cb7224c8e1df0aec264 100644 GIT binary patch delta 15 WcmbQCFhgNO2IE8nj?Ed2EBF8`AO$4= delta 15 WcmbQCFhgNO2IJ-e#ua=M4LATT;RP)K diff --git a/release/datafiles/blender_icons32/icon32_wpaint_hlt.dat b/release/datafiles/blender_icons32/icon32_wpaint_hlt.dat index 0d12a956ef737fd4c3e40cbec69a51bd3480f3e7..9f3bb8421aff6a752a5b23515a8af766603bf3cd 100644 GIT binary patch delta 16 YcmbQCFhgNO0^{Tb?A)7^8Q1aw05aYMnE(I) delta 15 WcmbQCFhgNO0^{Zk#tnQE4LATTfdwT1 From 01e2a532f55ee2de3bfd1c0d97d063976546a036 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 22 Oct 2021 14:57:34 +0200 Subject: [PATCH 1080/1500] Fix T92290: Linked Color Palette datablocks can not be used. * Forbid editing linked palettes. * Make `color` RNA property of ColorPalette '`LIB_EXCEPTION`', so that the color buttons in the palette template remain active on linked data. NOTE: This incidently makes linked palettes' colors editable from RNA, not from UI though, so think this is OK for now. --- source/blender/editors/sculpt_paint/paint_ops.c | 3 ++- source/blender/makesrna/intern/rna_palette.c | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index f08771292a8..c6b519f711f 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -233,7 +233,8 @@ static bool palette_poll(bContext *C) { Paint *paint = BKE_paint_get_active_from_context(C); - if (paint && paint->palette != NULL) { + if (paint && paint->palette != NULL && !ID_IS_LINKED(paint->palette) && + !ID_IS_OVERRIDE_LIBRARY(paint->palette)) { return true; } diff --git a/source/blender/makesrna/intern/rna_palette.c b/source/blender/makesrna/intern/rna_palette.c index 67d6daf5a13..30604c7a3b8 100644 --- a/source/blender/makesrna/intern/rna_palette.c +++ b/source/blender/makesrna/intern/rna_palette.c @@ -37,12 +37,20 @@ # include "BKE_report.h" static PaletteColor *rna_Palette_color_new(Palette *palette) { + if (ID_IS_LINKED(palette) || ID_IS_OVERRIDE_LIBRARY(palette)) { + return NULL; + } + PaletteColor *color = BKE_palette_color_add(palette); return color; } static void rna_Palette_color_remove(Palette *palette, ReportList *reports, PointerRNA *color_ptr) { + if (ID_IS_LINKED(palette) || ID_IS_OVERRIDE_LIBRARY(palette)) { + return; + } + PaletteColor *color = color_ptr->data; if (BLI_findindex(&palette->colors, color) == -1) { @@ -58,6 +66,10 @@ static void rna_Palette_color_remove(Palette *palette, ReportList *reports, Poin static void rna_Palette_color_clear(Palette *palette) { + if (ID_IS_LINKED(palette) || ID_IS_OVERRIDE_LIBRARY(palette)) { + return; + } + BKE_palette_clear(palette); } @@ -141,6 +153,7 @@ static void rna_def_palettecolor(BlenderRNA *brna) prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "rgb"); + RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Color", ""); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); From 781289e31fbec004584b3789c801d1db012dfaa4 Mon Sep 17 00:00:00 2001 From: Dorian Date: Fri, 22 Oct 2021 14:59:15 +0200 Subject: [PATCH 1081/1500] Geometry Nodes: add Boolean and Integer Input nodes These nodes just output a single value of their respective types, making it possible to control multiple inputs with the same value. Differential Revision: https://developer.blender.org/D12932 --- release/scripts/startup/nodeitems_builtins.py | 2 + source/blender/blenkernel/BKE_node.h | 2 + source/blender/blenkernel/intern/node.cc | 2 + source/blender/makesdna/DNA_node_types.h | 8 +++ source/blender/makesrna/intern/rna_nodetree.c | 28 ++++++++ source/blender/nodes/CMakeLists.txt | 2 + source/blender/nodes/NOD_function.h | 2 + source/blender/nodes/NOD_static_types.h | 2 + .../function/nodes/node_fn_input_bool.cc | 64 +++++++++++++++++++ .../nodes/function/nodes/node_fn_input_int.cc | 64 +++++++++++++++++++ 10 files changed, 176 insertions(+) create mode 100644 source/blender/nodes/function/nodes/node_fn_input_bool.cc create mode 100644 source/blender/nodes/function/nodes/node_fn_input_int.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index e481b360300..418311cf27e 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -172,7 +172,9 @@ def geometry_input_node_items(context): yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeCollectionInfo") + yield NodeItem("FunctionNodeInputBool") yield NodeItem("FunctionNodeInputColor") + yield NodeItem("FunctionNodeInputInt") yield NodeItem("GeometryNodeIsViewport") yield NodeItem("GeometryNodeInputMaterial") yield NodeItem("GeometryNodeObjectInfo") diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a9bba3908ce..7a9c23d3f54 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1568,6 +1568,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_ALIGN_EULER_TO_VECTOR 1216 #define FN_NODE_INPUT_COLOR 1217 #define FN_NODE_REPLACE_STRING 1218 +#define FN_NODE_INPUT_BOOL 1219 +#define FN_NODE_INPUT_INT 1220 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 6b626af7dd2..d403d048173 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5876,6 +5876,8 @@ static void registerFunctionNodes() register_node_type_fn_boolean_math(); register_node_type_fn_float_compare(); register_node_type_fn_float_to_int(); + register_node_type_fn_input_bool(); + register_node_type_fn_input_int(); register_node_type_fn_input_special_characters(); register_node_type_fn_input_string(); register_node_type_fn_input_vector(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 12b81433ffd..b585cbd6306 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1291,6 +1291,14 @@ typedef struct NodeAttributeCurveMap { CurveMapping *curve_rgb; } NodeAttributeCurveMap; +typedef struct NodeInputBool { + uint8_t boolean; +} NodeInputBool; + +typedef struct NodeInputInt { + int integer; +} NodeInputInt; + typedef struct NodeInputVector { float vector[3]; } NodeInputVector; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 438344304cf..f0508ee5317 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -5014,6 +5014,34 @@ static void def_fn_input_color(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_fn_input_bool(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeInputBool", "storage"); + + prop = RNA_def_property(srna, "boolean", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "boolean", 1); + RNA_def_property_ui_text( + prop, "Boolean", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + +} + +static void def_fn_input_int(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeInputInt", "storage"); + + prop = RNA_def_property(srna, "integer", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "integer"); + RNA_def_property_int_default(prop, 1); + RNA_def_property_ui_text( + prop, "Integer", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_fn_input_vector(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 8630bcd3b73..3e62fcb47f5 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -140,7 +140,9 @@ set(SRC function/nodes/node_fn_boolean_math.cc function/nodes/node_fn_float_compare.cc function/nodes/node_fn_float_to_int.cc + function/nodes/node_fn_input_bool.cc function/nodes/node_fn_input_color.cc + function/nodes/node_fn_input_int.cc function/nodes/node_fn_input_special_characters.cc function/nodes/node_fn_input_string.cc function/nodes/node_fn_input_vector.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index e3e75b6fa7a..0652a0d1e94 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -26,6 +26,8 @@ void register_node_type_fn_align_euler_to_vector(void); void register_node_type_fn_boolean_math(void); void register_node_type_fn_float_compare(void); void register_node_type_fn_float_to_int(void); +void register_node_type_fn_input_bool(void); +void register_node_type_fn_input_int(void); void register_node_type_fn_input_special_characters(void); void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index b5254f2e898..843d414f426 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -269,7 +269,9 @@ DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vecto DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_COMPARE_FLOATS, def_float_compare, "COMPARE_FLOATS", CompareFloats, "Compare Floats", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") +DefNode(FunctionNode, FN_NODE_INPUT_BOOL, def_fn_input_bool, "INPUT_BOOL", InputBool, "Boolean", "") DefNode(FunctionNode, FN_NODE_INPUT_COLOR, def_fn_input_color, "INPUT_COLOR", InputColor, "Color", "") +DefNode(FunctionNode, FN_NODE_INPUT_INT, def_fn_input_int, "INPUT_INT", InputInt, "Integer", "") DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "") DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") diff --git a/source/blender/nodes/function/nodes/node_fn_input_bool.cc b/source/blender/nodes/function/nodes/node_fn_input_bool.cc new file mode 100644 index 00000000000..58f8969f1b6 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_bool.cc @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "BLI_hash.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void fn_node_input_bool_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Boolean"); +}; + +static void fn_node_input_bool_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "boolean", UI_ITEM_R_EXPAND, IFACE_("Value"), ICON_NONE); +} + +static void fn_node_input_bool_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + bNode &bnode = builder.node(); + NodeInputBool *node_storage = static_cast(bnode.storage); + builder.construct_and_set_matching_fn>(node_storage->boolean); +} + +static void fn_node_input_bool_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeInputBool *data = (NodeInputBool *)MEM_callocN(sizeof(NodeInputBool), __func__); + node->storage = data; +} + +} // namespace blender::nodes + +void register_node_type_fn_input_bool() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_INPUT_BOOL, "Boolean", 0, 0); + ntype.declare = blender::nodes::fn_node_input_bool_declare; + node_type_init(&ntype, blender::nodes::fn_node_input_bool_init); + node_type_storage( + &ntype, "NodeInputBool", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_input_bool_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_input_bool_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_input_int.cc b/source/blender/nodes/function/nodes/node_fn_input_int.cc new file mode 100644 index 00000000000..db52d569ac5 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_int.cc @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "BLI_hash.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void fn_node_input_int_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Integer"); +}; + +static void fn_node_input_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "integer", UI_ITEM_R_EXPAND, "", ICON_NONE); +} + +static void fn_node_input_int_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + bNode &bnode = builder.node(); + NodeInputInt *node_storage = static_cast(bnode.storage); + builder.construct_and_set_matching_fn>(node_storage->integer); +} + +static void fn_node_input_int_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeInputInt *data = (NodeInputInt *)MEM_callocN(sizeof(NodeInputInt), __func__); + node->storage = data; +} + +} // namespace blender::nodes + +void register_node_type_fn_input_int() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_INPUT_INT, "Integer", 0, 0); + ntype.declare = blender::nodes::fn_node_input_int_declare; + node_type_init(&ntype, blender::nodes::fn_node_input_int_init); + node_type_storage( + &ntype, "NodeInputInt", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_input_int_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_input_int_layout; + nodeRegisterType(&ntype); +} From 0c16ac9ddfd9c0a7d5915caf8b6000bf7c408447 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 22 Oct 2021 15:27:10 +0200 Subject: [PATCH 1082/1500] Cleanup: restore alphabetic ordering --- source/blender/blenkernel/intern/node.cc | 24 ++++++++++++------------ source/blender/nodes/NOD_function.h | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index d403d048173..e97b309f75b 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5735,24 +5735,24 @@ static void registerGeometryNodes() { register_node_type_geo_group(); - register_node_type_geo_legacy_attribute_transfer(); - register_node_type_geo_legacy_curve_set_handles(); register_node_type_geo_legacy_attribute_proximity(); register_node_type_geo_legacy_attribute_randomize(); + register_node_type_geo_legacy_attribute_transfer(); + register_node_type_geo_legacy_curve_endpoints(); + register_node_type_geo_legacy_curve_reverse(); + register_node_type_geo_legacy_curve_set_handles(); + register_node_type_geo_legacy_curve_spline_type(); + register_node_type_geo_legacy_curve_subdivide(); + register_node_type_geo_legacy_curve_to_points(); register_node_type_geo_legacy_delete_geometry(); + register_node_type_geo_legacy_edge_split(); register_node_type_geo_legacy_material_assign(); register_node_type_geo_legacy_mesh_to_curve(); register_node_type_geo_legacy_points_to_volume(); - register_node_type_geo_legacy_select_by_material(); - register_node_type_geo_legacy_curve_endpoints(); - register_node_type_geo_legacy_curve_spline_type(); - register_node_type_geo_legacy_curve_reverse(); - register_node_type_geo_legacy_select_by_handle_type(); - register_node_type_geo_legacy_curve_subdivide(); - register_node_type_geo_legacy_edge_split(); - register_node_type_geo_legacy_subdivision_surface(); register_node_type_geo_legacy_raycast(); - register_node_type_geo_legacy_curve_to_points(); + register_node_type_geo_legacy_select_by_handle_type(); + register_node_type_geo_legacy_select_by_material(); + register_node_type_geo_legacy_subdivision_surface(); register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); @@ -5877,11 +5877,11 @@ static void registerFunctionNodes() register_node_type_fn_float_compare(); register_node_type_fn_float_to_int(); register_node_type_fn_input_bool(); + register_node_type_fn_input_color(); register_node_type_fn_input_int(); register_node_type_fn_input_special_characters(); register_node_type_fn_input_string(); register_node_type_fn_input_vector(); - register_node_type_fn_input_color(); register_node_type_fn_random_value(); register_node_type_fn_replace_string(); register_node_type_fn_rotate_euler(); diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 0652a0d1e94..78069a2fade 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -27,11 +27,11 @@ void register_node_type_fn_boolean_math(void); void register_node_type_fn_float_compare(void); void register_node_type_fn_float_to_int(void); void register_node_type_fn_input_bool(void); +void register_node_type_fn_input_color(void); void register_node_type_fn_input_int(void); void register_node_type_fn_input_special_characters(void); void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); -void register_node_type_fn_input_color(void); void register_node_type_fn_random_value(void); void register_node_type_fn_replace_string(void); void register_node_type_fn_rotate_euler(void); From 39f88480bb1fa0db7fda8bacb3c7d5a0b1cf2171 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 22 Oct 2021 15:34:53 +0200 Subject: [PATCH 1083/1500] Cleanup: improve consistency between function node implementations --- .../function/nodes/node_fn_boolean_math.cc | 29 +++++++++-------- .../function/nodes/node_fn_float_compare.cc | 31 +++++++++---------- .../function/nodes/node_fn_float_to_int.cc | 30 ++++++++---------- .../function/nodes/node_fn_input_color.cc | 4 +-- .../function/nodes/node_fn_input_string.cc | 21 +++++++------ .../function/nodes/node_fn_input_vector.cc | 18 +++++------ .../function/nodes/node_fn_replace_string.cc | 18 +++++------ .../function/nodes/node_fn_rotate_euler.cc | 1 + .../function/nodes/node_fn_string_length.cc | 11 +++---- .../nodes/node_fn_string_substring.cc | 11 +++---- .../function/nodes/node_fn_value_to_string.cc | 11 +++---- 11 files changed, 88 insertions(+), 97 deletions(-) diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index d10490bb2ee..09caf12e425 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -34,8 +34,6 @@ static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b) b.add_output("Boolean"); }; -} // namespace blender::nodes - static void fn_node_boolean_math_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); @@ -59,13 +57,13 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) +static const fn::MultiFunction *get_multi_function(bNode &bnode) { - static blender::fn::CustomMF_SI_SI_SO and_fn{ - "And", [](bool a, bool b) { return a && b; }}; - static blender::fn::CustomMF_SI_SI_SO or_fn{ - "Or", [](bool a, bool b) { return a || b; }}; - static blender::fn::CustomMF_SI_SO not_fn{"Not", [](bool a) { return !a; }}; + static fn::CustomMF_SI_SI_SO and_fn{"And", + [](bool a, bool b) { return a && b; }}; + static fn::CustomMF_SI_SI_SO or_fn{"Or", + [](bool a, bool b) { return a || b; }}; + static fn::CustomMF_SI_SO not_fn{"Not", [](bool a) { return !a; }}; switch (bnode.custom1) { case NODE_BOOLEAN_MATH_AND: @@ -80,22 +78,23 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) return nullptr; } -static void fn_node_boolean_math_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_boolean_math_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); + const fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } +} // namespace blender::nodes + void register_node_type_fn_boolean_math() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_boolean_math_declare; - node_type_label(&ntype, node_boolean_math_label); - node_type_update(&ntype, node_boolean_math_update); - ntype.build_multi_function = fn_node_boolean_math_build_multi_function; - ntype.draw_buttons = fn_node_boolean_math_layout; + node_type_label(&ntype, blender::nodes::node_boolean_math_label); + node_type_update(&ntype, blender::nodes::node_boolean_math_update); + ntype.build_multi_function = blender::nodes::fn_node_boolean_math_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_boolean_math_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 7905419ddb1..bdc4a3c1e02 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -37,8 +37,6 @@ static void fn_node_float_compare_declare(NodeDeclarationBuilder &b) b.add_output("Result"); }; -} // namespace blender::nodes - static void geo_node_float_compare_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); @@ -65,19 +63,19 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree), BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction *get_multi_function(bNode &node) +static const fn::MultiFunction *get_multi_function(bNode &node) { - static blender::fn::CustomMF_SI_SI_SO less_than_fn{ + static fn::CustomMF_SI_SI_SO less_than_fn{ "Less Than", [](float a, float b) { return a < b; }}; - static blender::fn::CustomMF_SI_SI_SO less_equal_fn{ + static fn::CustomMF_SI_SI_SO less_equal_fn{ "Less Equal", [](float a, float b) { return a <= b; }}; - static blender::fn::CustomMF_SI_SI_SO greater_than_fn{ + static fn::CustomMF_SI_SI_SO greater_than_fn{ "Greater Than", [](float a, float b) { return a > b; }}; - static blender::fn::CustomMF_SI_SI_SO greater_equal_fn{ + static fn::CustomMF_SI_SI_SO greater_equal_fn{ "Greater Equal", [](float a, float b) { return a >= b; }}; - static blender::fn::CustomMF_SI_SI_SI_SO equal_fn{ + static fn::CustomMF_SI_SI_SI_SO equal_fn{ "Equal", [](float a, float b, float epsilon) { return std::abs(a - b) <= epsilon; }}; - static blender::fn::CustomMF_SI_SI_SI_SO not_equal_fn{ + static fn::CustomMF_SI_SI_SI_SO not_equal_fn{ "Not Equal", [](float a, float b, float epsilon) { return std::abs(a - b) > epsilon; }}; switch (node.custom1) { @@ -99,22 +97,23 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) return nullptr; } -static void fn_node_float_compare_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_float_compare_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); + const fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } +} // namespace blender::nodes + void register_node_type_fn_float_compare() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_COMPARE_FLOATS, "Compare Floats", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_float_compare_declare; - node_type_label(&ntype, node_float_compare_label); - node_type_update(&ntype, node_float_compare_update); - ntype.build_multi_function = fn_node_float_compare_build_multi_function; - ntype.draw_buttons = geo_node_float_compare_layout; + node_type_label(&ntype, blender::nodes::node_float_compare_label); + node_type_update(&ntype, blender::nodes::node_float_compare_update); + ntype.build_multi_function = blender::nodes::fn_node_float_compare_build_multi_function; + ntype.draw_buttons = blender::nodes::geo_node_float_compare_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index 8bb5dafff8a..5dccd26158b 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -34,8 +34,6 @@ static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b) b.add_output("Integer"); }; -} // namespace blender::nodes - static void fn_node_float_to_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "rounding_mode", 0, "", ICON_NONE); @@ -51,16 +49,13 @@ static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) +static const fn::MultiFunction *get_multi_function(bNode &bnode) { - static blender::fn::CustomMF_SI_SO round_fn{"Round", - [](float a) { return (int)round(a); }}; - static blender::fn::CustomMF_SI_SO floor_fn{"Floor", - [](float a) { return (int)floor(a); }}; - static blender::fn::CustomMF_SI_SO ceil_fn{"Ceiling", - [](float a) { return (int)ceil(a); }}; - static blender::fn::CustomMF_SI_SO trunc_fn{"Truncate", - [](float a) { return (int)trunc(a); }}; + static fn::CustomMF_SI_SO round_fn{"Round", [](float a) { return (int)round(a); }}; + static fn::CustomMF_SI_SO floor_fn{"Floor", [](float a) { return (int)floor(a); }}; + static fn::CustomMF_SI_SO ceil_fn{"Ceiling", [](float a) { return (int)ceil(a); }}; + static fn::CustomMF_SI_SO trunc_fn{"Truncate", + [](float a) { return (int)trunc(a); }}; switch (static_cast(bnode.custom1)) { case FN_NODE_FLOAT_TO_INT_ROUND: @@ -77,21 +72,22 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) return nullptr; } -static void fn_node_float_to_int_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_float_to_int_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); + const fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } +} // namespace blender::nodes + void register_node_type_fn_float_to_int() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_float_to_int_declare; - node_type_label(&ntype, node_float_to_int_label); - ntype.build_multi_function = fn_node_float_to_int_build_multi_function; - ntype.draw_buttons = fn_node_float_to_int_layout; + node_type_label(&ntype, blender::nodes::node_float_to_int_label); + ntype.build_multi_function = blender::nodes::fn_node_float_to_int_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_float_to_int_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc index 5dc211da1b2..b6079835eae 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_color.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc @@ -32,7 +32,7 @@ static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "color", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } -static void fn_node_color_input_build_multi_function( +static void fn_node_input_color_build_multi_function( blender::nodes::NodeMultiFunctionBuilder &builder) { bNode &bnode = builder.node(); @@ -59,7 +59,7 @@ void register_node_type_fn_input_color() node_type_init(&ntype, blender::nodes::fn_node_input_color_init); node_type_storage( &ntype, "NodeInputColor", node_free_standard_storage, node_copy_standard_storage); - ntype.build_multi_function = blender::nodes::fn_node_color_input_build_multi_function; + ntype.build_multi_function = blender::nodes::fn_node_input_color_build_multi_function; ntype.draw_buttons = blender::nodes::fn_node_input_color_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index 704ae9d900c..0982096eaea 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -27,21 +27,17 @@ static void fn_node_input_string_declare(NodeDeclarationBuilder &b) b.add_output("String"); }; -} // namespace blender::nodes - static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "string", 0, "", ICON_NONE); } -static void fn_node_input_string_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_input_string_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &bnode = builder.node(); NodeInputString *node_storage = static_cast(bnode.storage); std::string string = std::string((node_storage->string) ? node_storage->string : ""); - builder.construct_and_set_matching_fn>( - std::move(string)); + builder.construct_and_set_matching_fn>(std::move(string)); } static void fn_node_input_string_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -75,15 +71,20 @@ static void fn_node_string_copy(bNodeTree *UNUSED(dest_ntree), dest_node->storage = destination_storage; } +} // namespace blender::nodes + void register_node_type_fn_input_string() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_INPUT_STRING, "String", NODE_CLASS_INPUT, 0); ntype.declare = blender::nodes::fn_node_input_string_declare; - node_type_init(&ntype, fn_node_input_string_init); - node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy); - ntype.build_multi_function = fn_node_input_string_build_multi_function; - ntype.draw_buttons = fn_node_input_string_layout; + node_type_init(&ntype, blender::nodes::fn_node_input_string_init); + node_type_storage(&ntype, + "NodeInputString", + blender::nodes::fn_node_input_string_free, + blender::nodes::fn_node_string_copy); + ntype.build_multi_function = blender::nodes::fn_node_input_string_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_input_string_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 9548df7b423..f64fd182282 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -28,22 +28,20 @@ static void fn_node_input_vector_declare(NodeDeclarationBuilder &b) b.add_output("Vector"); }; -} // namespace blender::nodes - static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "vector", UI_ITEM_R_EXPAND, "", ICON_NONE); } -static void fn_node_vector_input_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_input_vector_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &bnode = builder.node(); NodeInputVector *node_storage = static_cast(bnode.storage); - blender::float3 vector(node_storage->vector); - builder.construct_and_set_matching_fn>(vector); + float3 vector(node_storage->vector); + builder.construct_and_set_matching_fn>(vector); } + static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeInputVector *data = (NodeInputVector *)MEM_callocN(sizeof(NodeInputVector), @@ -51,16 +49,18 @@ static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node) node->storage = data; } +} // namespace blender::nodes + void register_node_type_fn_input_vector() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_INPUT_VECTOR, "Vector", 0, 0); ntype.declare = blender::nodes::fn_node_input_vector_declare; - node_type_init(&ntype, fn_node_input_vector_init); + node_type_init(&ntype, blender::nodes::fn_node_input_vector_init); node_type_storage( &ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage); - ntype.build_multi_function = fn_node_vector_input_build_multi_function; - ntype.draw_buttons = fn_node_input_vector_layout; + ntype.build_multi_function = blender::nodes::fn_node_input_vector_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_input_vector_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_replace_string.cc b/source/blender/nodes/function/nodes/node_fn_replace_string.cc index d21044d54a5..1ec4979176e 100644 --- a/source/blender/nodes/function/nodes/node_fn_replace_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_replace_string.cc @@ -28,8 +28,6 @@ static void fn_node_replace_string_declare(NodeDeclarationBuilder &b) b.add_output("String"); }; -} // namespace blender::nodes - static std::string replace_all(std::string str, const std::string &from, const std::string &to) { if (from.length() <= 0) { @@ -45,23 +43,23 @@ static std::string replace_all(std::string str, const std::string &from, const s return str; } -static void fn_node_replace_string_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_replace_string_build_multi_function(NodeMultiFunctionBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SI_SO - substring_fn{"Replace", - [](const std::string &str, - const std::string &find, - const std::string &replace) { return replace_all(str, find, replace); }}; + static fn::CustomMF_SI_SI_SI_SO substring_fn{ + "Replace", [](const std::string &str, const std::string &find, const std::string &replace) { + return replace_all(str, find, replace); + }}; builder.set_matching_fn(&substring_fn); } +} // namespace blender::nodes + void register_node_type_fn_replace_string() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_REPLACE_STRING, "Replace String", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_replace_string_declare; - ntype.build_multi_function = fn_node_replace_string_build_multi_function; + ntype.build_multi_function = blender::nodes::fn_node_replace_string_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc index 7db566e96b5..a01cc6b58dd 100644 --- a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc +++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc @@ -25,6 +25,7 @@ #include "node_function_util.hh" namespace blender::nodes { + static void fn_node_rotate_euler_declare(NodeDeclarationBuilder &b) { b.is_function_node(); diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc index a0f85dfd2bf..d882280b566 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_length.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc @@ -28,22 +28,21 @@ static void fn_node_string_length_declare(NodeDeclarationBuilder &b) b.add_output("Length"); }; -} // namespace blender::nodes - -static void fn_node_string_length_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_string_length_build_multi_function(NodeMultiFunctionBuilder &builder) { - static blender::fn::CustomMF_SI_SO str_len_fn{ + static fn::CustomMF_SI_SO str_len_fn{ "String Length", [](const std::string &a) { return BLI_strlen_utf8(a.c_str()); }}; builder.set_matching_fn(&str_len_fn); } +} // namespace blender::nodes + void register_node_type_fn_string_length() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_STRING_LENGTH, "String Length", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_string_length_declare; - ntype.build_multi_function = fn_node_string_length_build_multi_function; + ntype.build_multi_function = blender::nodes::fn_node_string_length_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_string_substring.cc b/source/blender/nodes/function/nodes/node_fn_string_substring.cc index 55a01093ae9..0f91b1af813 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_substring.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_substring.cc @@ -28,12 +28,9 @@ static void fn_node_string_substring_declare(NodeDeclarationBuilder &b) b.add_output("String"); }; -} // namespace blender::nodes - -static void fn_node_string_substring_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_string_substring_build_multi_function(NodeMultiFunctionBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SI_SO substring_fn{ + static fn::CustomMF_SI_SI_SI_SO substring_fn{ "Substring", [](const std::string &str, int a, int b) { const int len = BLI_strlen_utf8(str.c_str()); const int start = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a, 0, len)); @@ -43,12 +40,14 @@ static void fn_node_string_substring_build_multi_function( builder.set_matching_fn(&substring_fn); } +} // namespace blender::nodes + void register_node_type_fn_string_substring() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_STRING_SUBSTRING, "String Substring", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_string_substring_declare; - ntype.build_multi_function = fn_node_string_substring_build_multi_function; + ntype.build_multi_function = blender::nodes::fn_node_string_substring_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc index c1e6373cb6d..112726f98dc 100644 --- a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc @@ -26,12 +26,9 @@ static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b) b.add_output("String"); }; -} // namespace blender::nodes - -static void fn_node_value_to_string_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_value_to_string_build_multi_function(NodeMultiFunctionBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SO to_str_fn{ + static fn::CustomMF_SI_SI_SO to_str_fn{ "Value To String", [](float a, int b) { std::stringstream stream; stream << std::fixed << std::setprecision(std::max(0, b)) << a; @@ -40,12 +37,14 @@ static void fn_node_value_to_string_build_multi_function( builder.set_matching_fn(&to_str_fn); } +} // namespace blender::nodes + void register_node_type_fn_value_to_string() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_VALUE_TO_STRING, "Value to String", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_value_to_string_declare; - ntype.build_multi_function = fn_node_value_to_string_build_multi_function; + ntype.build_multi_function = blender::nodes::fn_node_value_to_string_build_multi_function; nodeRegisterType(&ntype); } From 76ebc10794a4bb66a1b94339fd8873dc23f1247c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 22 Oct 2021 17:17:21 +0300 Subject: [PATCH 1084/1500] Increase assert epsilon in versioning for D9551. The epsilon was too optimistic. Snapping to hard-coded (0,-1,0) at singularity should produce max delta 0.0005, but double it to be safe. This only affects debug builds obviously. --- source/blender/blenloader/intern/versioning_300.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index d5064f411c2..7d93439a44d 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1197,7 +1197,7 @@ static void correct_bone_roll_value(const float head[3], /* Recompute roll using legacy code to interpret the old value. */ legacy_vec_roll_to_mat3_normalized(vec, *r_roll, bone_mat); mat3_to_vec_roll(bone_mat, vec2, r_roll); - BLI_assert(compare_v3v3(vec, vec2, FLT_EPSILON)); + BLI_assert(compare_v3v3(vec, vec2, 0.001f)); } } } From 16ffa7bb6e519edd039683fe83031542d7059d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 22 Oct 2021 16:31:25 +0200 Subject: [PATCH 1085/1500] Asset Catalogs: push undo step before saving to disk Since writing asset catalogs to disk also means loading any on-disk changes, it's a good idea to store an undo step. --- source/blender/editors/asset/intern/asset_catalog.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index f8b33e76447..98f017db20c 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -121,6 +121,9 @@ void ED_asset_catalogs_save_from_main_path(::AssetLibrary *library, const Main * return; } + /* Since writing to disk also means loading any on-disk changes, it may be a good idea to store + * an undo step. */ + catalog_service->undo_push(); catalog_service->write_to_disk(bmain->name); } From 70aad5f498fcd7ed52f3422edda3021e5d4f9538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 22 Oct 2021 16:29:31 +0200 Subject: [PATCH 1086/1500] Asset Catalogs: support reloading without losing local changes Keep track of unsaved asset catalog changes, in a more granular way than just one boolean per asset library. Individual catalogs can now be marked with a flag `has_unsaved_changes`. This is taken into account when reloading data from the catalog definition file (CDF): - New catalog in CDF: gets loaded - Already-known catalog in CDF: - local unsaved changes: on-disk catalog is ignored - otherwise: on-disk catalog replaces in-memory one - Already-known catalog that does not exist in CDF: - local unsaved changes: catalog is kept around - otherwise: catalog is deleted. Because this saving-is-also-loading behaviour, the "has unsaved changes" flags are all stored in the undo buffer; undoing after saving will not change the CDF, but at least it'll undo the loading from disk, and it'll re-mark any changes as "not saved". Reviewed By: Severin Differential Revision: https://developer.blender.org/D12967 --- .../blender/blenkernel/BKE_asset_catalog.hh | 68 ++++++-- .../blender/blenkernel/BKE_asset_library.hh | 3 + .../blenkernel/intern/asset_catalog.cc | 156 ++++++++++++++---- .../blenkernel/intern/asset_catalog_test.cc | 121 ++++++++++++-- .../blenkernel/intern/asset_library.cc | 5 + .../intern/asset_library_service.cc | 4 +- .../intern/asset_library_service_test.cc | 18 +- .../editors/asset/intern/asset_catalog.cc | 13 +- .../space_file/asset_catalog_tree_view.cc | 7 +- 9 files changed, 324 insertions(+), 71 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index cbb15780a68..902c0e414d6 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -64,11 +64,13 @@ class AssetCatalogService { explicit AssetCatalogService(const CatalogFilePath &asset_library_root); /** - * Set global tag indicating that some catalog modifications are unsaved that could get lost - * on exit. This tag is not set by internal catalog code, the catalog service user is responsible - * for it. It is cleared by #write_to_disk(). - */ - void tag_has_unsaved_changes(); + * Set tag indicating that some catalog modifications are unsaved, which could + * get lost on exit. This tag is not set by internal catalog code, the catalog + * service user is responsible for it. It is cleared by #write_to_disk(). + * + * This "dirty" state is tracked per catalog, so that it's possible to gracefully load changes + * from disk. Any catalog with unsaved changes will not be overwritten by on-disk changes. */ + void tag_has_unsaved_changes(AssetCatalog *edited_catalog); bool has_unsaved_changes() const; /** Load asset catalog definitions from the files found in the asset library. */ @@ -103,7 +105,7 @@ class AssetCatalogService { * - Already-known on-disk catalogs are ignored (so will be overwritten with our in-memory * data). This includes in-memory marked-as-deleted catalogs. */ - void merge_from_disk_before_writing(); + void reload_catalogs(); /** Return catalog with the given ID. Return nullptr if not found. */ AssetCatalog *find_catalog(CatalogID catalog_id) const; @@ -139,12 +141,6 @@ class AssetCatalogService { */ void prune_catalogs_by_id(CatalogID catalog_id); - /** - * Delete a catalog, without deleting any of its children and without rebuilding the catalog - * tree. This is a lower-level function than #prune_catalogs_by_path. - */ - void delete_catalog_by_id(CatalogID catalog_id); - /** * Update the catalog path, also updating the catalog path of all sub-catalogs. */ @@ -178,7 +174,6 @@ class AssetCatalogService { Vector> undo_snapshots_; Vector> redo_snapshots_; - bool has_unsaved_changes_ = false; void load_directory_recursive(const CatalogFilePath &directory_path); void load_single_file(const CatalogFilePath &catalog_definition_file_path); @@ -186,6 +181,31 @@ class AssetCatalogService { /** Implementation of #write_to_disk() that doesn't clear the "has unsaved changes" tag. */ bool write_to_disk_ex(const CatalogFilePath &blend_file_path); void untag_has_unsaved_changes(); + bool is_catalog_known_with_unsaved_changes(CatalogID catalog_id) const; + + /** + * Delete catalogs, only keeping them when they are either listed in + * \a catalogs_to_keep or have unsaved changes. + * + * \note Deleted catalogs are hard-deleted, i.e. they just vanish instead of + * remembering them as "deleted". + */ + void purge_catalogs_not_listed(const Set &catalogs_to_keep); + + /** + * Delete a catalog, without deleting any of its children and without rebuilding the catalog + * tree. The deletion in "Soft", in the sense that the catalog pointer is moved from `catalogs_` + * to `deleted_catalogs_`; the AssetCatalog instance itself is kept in memory. As a result, it + * will be removed from a CDF when saved to disk. + * + * This is a lower-level function than #prune_catalogs_by_path. + */ + void delete_catalog_by_id_soft(CatalogID catalog_id); + + /** + * Hard delete a catalog. This simply removes the catalog from existence. The deletion will not + * be remembered, and reloading the CDF will bring it back. */ + void delete_catalog_by_id_hard(CatalogID catalog_id); std::unique_ptr parse_catalog_file( const CatalogFilePath &catalog_definition_file_path); @@ -216,6 +236,7 @@ class AssetCatalogService { /* For access by subclasses, as those will not be marked as friend by #AssetCatalogCollection. */ AssetCatalogDefinitionFile *get_catalog_definition_file(); OwningAssetCatalogMap &get_catalogs(); + OwningAssetCatalogMap &get_deleted_catalogs(); }; /** @@ -246,6 +267,9 @@ class AssetCatalogCollection { * The aim is to support an arbitrary number of such files per asset library in the future. */ std::unique_ptr catalog_definition_file_; + /** Whether any of the catalogs have unsaved changes. */ + bool has_unsaved_changes_ = false; + static OwningAssetCatalogMap copy_catalog_map(const OwningAssetCatalogMap &orig); }; @@ -269,6 +293,7 @@ class AssetCatalogTreeItem { CatalogID get_catalog_id() const; StringRefNull get_simple_name() const; StringRefNull get_name() const; + bool has_unsaved_changes() const; /** Return the full catalog path, defined as the name of this catalog prefixed by the full * catalog path of its parent and a separator. */ AssetCatalogPath catalog_path() const; @@ -287,6 +312,8 @@ class AssetCatalogTreeItem { CatalogID catalog_id_; /** Copy of #AssetCatalog::simple_name. */ std::string simple_name_; + /** Copy of #AssetCatalog::flags.has_unsaved_changes. */ + bool has_unsaved_changes_ = false; /** Pointer back to the parent item. Used to reconstruct the hierarchy from an item (e.g. to * build a path). */ @@ -353,9 +380,14 @@ class AssetCatalogDefinitionFile { bool write_to_disk(const CatalogFilePath &dest_file_path) const; bool contains(CatalogID catalog_id) const; - /* Add a new catalog. Undefined behavior if a catalog with the same ID was already added. */ + /** Add a catalog, overwriting the one with the same catalog ID. */ + void add_overwrite(AssetCatalog *catalog); + /** Add a new catalog. Undefined behavior if a catalog with the same ID was already added. */ void add_new(AssetCatalog *catalog); + /** Remove the catalog from the collection of catalogs stored in this file. */ + void forget(CatalogID catalog_id); + using AssetCatalogParsedFn = FunctionRef)>; void parse_catalog_file(const CatalogFilePath &catalog_definition_file_path, AssetCatalogParsedFn callback); @@ -405,6 +437,14 @@ class AssetCatalog { * load-and-merged with a file that also has these catalogs, the first one in that file is * always sorted first, regardless of the sort order of its UUID. */ bool is_first_loaded = false; + + /* Merging on-disk changes into memory will not overwrite this catalog. + * For example, when a catalog was renamed (i.e. changed path) in this Blender session, + * reloading the catalog definition file should not overwrite that change. + * + * Note that this flag is ignored when is_deleted=true; deleted catalogs that are still in + * memory are considered "unsaved" by definition. */ + bool has_unsaved_changes = false; } flags; /** diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh index 64c8afecaef..15f7991e75e 100644 --- a/source/blender/blenkernel/BKE_asset_library.hh +++ b/source/blender/blenkernel/BKE_asset_library.hh @@ -49,6 +49,9 @@ struct AssetLibrary { void load(StringRefNull library_root_directory); + /** Load catalogs that have changed on disk. */ + void refresh(); + /** * Update `catalog_simple_name` by looking up the asset's catalog by its ID. * diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 9dd5ccdf3cf..a1647426c41 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -67,23 +67,45 @@ AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_ro { } -void AssetCatalogService::tag_has_unsaved_changes() +void AssetCatalogService::tag_has_unsaved_changes(AssetCatalog *edited_catalog) { - has_unsaved_changes_ = true; + if (edited_catalog) { + edited_catalog->flags.has_unsaved_changes = true; + } + BLI_assert(catalog_collection_); + catalog_collection_->has_unsaved_changes_ = true; } void AssetCatalogService::untag_has_unsaved_changes() { - has_unsaved_changes_ = false; + BLI_assert(catalog_collection_); + catalog_collection_->has_unsaved_changes_ = false; + + /* TODO(Sybren): refactor; this is more like "post-write cleanup" than "remove a tag" code. */ + + /* Forget about any deleted catalogs. */ + if (catalog_collection_->catalog_definition_file_) { + for (CatalogID catalog_id : catalog_collection_->deleted_catalogs_.keys()) { + catalog_collection_->catalog_definition_file_->forget(catalog_id); + } + } + catalog_collection_->deleted_catalogs_.clear(); + + /* Mark all remaining catalogs as "without unsaved changes". */ + for (auto &catalog_uptr : catalog_collection_->catalogs_.values()) { + catalog_uptr->flags.has_unsaved_changes = false; + } } bool AssetCatalogService::has_unsaved_changes() const { - return has_unsaved_changes_; + BLI_assert(catalog_collection_); + return catalog_collection_->has_unsaved_changes_; } bool AssetCatalogService::is_empty() const { + BLI_assert(catalog_collection_); return catalog_collection_->catalogs_.is_empty(); } @@ -91,6 +113,10 @@ OwningAssetCatalogMap &AssetCatalogService::get_catalogs() { return catalog_collection_->catalogs_; } +OwningAssetCatalogMap &AssetCatalogService::get_deleted_catalogs() +{ + return catalog_collection_->deleted_catalogs_; +} AssetCatalogDefinitionFile *AssetCatalogService::get_catalog_definition_file() { @@ -155,7 +181,7 @@ AssetCatalogFilter AssetCatalogService::create_catalog_filter( return AssetCatalogFilter(std::move(matching_catalog_ids)); } -void AssetCatalogService::delete_catalog_by_id(const CatalogID catalog_id) +void AssetCatalogService::delete_catalog_by_id_soft(const CatalogID catalog_id) { std::unique_ptr *catalog_uptr_ptr = catalog_collection_->catalogs_.lookup_ptr( catalog_id); @@ -176,6 +202,15 @@ void AssetCatalogService::delete_catalog_by_id(const CatalogID catalog_id) catalog_collection_->catalogs_.remove(catalog_id); } +void AssetCatalogService::delete_catalog_by_id_hard(CatalogID catalog_id) +{ + catalog_collection_->catalogs_.remove(catalog_id); + catalog_collection_->deleted_catalogs_.remove(catalog_id); + + /* TODO(Sybren): adjust this when supporting mulitple CDFs. */ + catalog_collection_->catalog_definition_file_->forget(catalog_id); +} + void AssetCatalogService::prune_catalogs_by_path(const AssetCatalogPath &path) { /* Build a collection of catalog IDs to delete. */ @@ -189,7 +224,7 @@ void AssetCatalogService::prune_catalogs_by_path(const AssetCatalogPath &path) /* Delete the catalogs. */ for (const CatalogID cat_id : catalogs_to_delete) { - this->delete_catalog_by_id(cat_id); + this->delete_catalog_by_id_soft(cat_id); } this->rebuild_tree(); @@ -231,6 +266,7 @@ void AssetCatalogService::update_catalog_path(const CatalogID catalog_id, AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalog_path) { std::unique_ptr catalog = AssetCatalog::from_path(catalog_path); + catalog->flags.has_unsaved_changes = true; /* So we can std::move(catalog) and still use the non-owning pointer: */ AssetCatalog *const catalog_ptr = catalog.get(); @@ -349,7 +385,7 @@ std::unique_ptr AssetCatalogService::parse_catalog_f return cdf; } -void AssetCatalogService::merge_from_disk_before_writing() +void AssetCatalogService::reload_catalogs() { /* TODO(Sybren): expand to support multiple CDFs. */ AssetCatalogDefinitionFile *const cdf = catalog_collection_->catalog_definition_file_.get(); @@ -357,26 +393,66 @@ void AssetCatalogService::merge_from_disk_before_writing() return; } - auto catalog_parsed_callback = [this](std::unique_ptr catalog) { - const bUUID catalog_id = catalog->catalog_id; + /* Keeps track of the catalog IDs that are seen in the CDF, so that we also know what was deleted + * from the file on disk. */ + Set cats_in_file; - /* The following two conditions could be or'ed together. Keeping them separated helps when - * adding debug prints, breakpoints, etc. */ - if (catalog_collection_->catalogs_.contains(catalog_id)) { - /* This catalog was already seen, so just ignore it. */ - return false; - } - if (catalog_collection_->deleted_catalogs_.contains(catalog_id)) { - /* This catalog was already seen and subsequently deleted, so just ignore it. */ + auto catalog_parsed_callback = [this, &cats_in_file](std::unique_ptr catalog) { + const CatalogID catalog_id = catalog->catalog_id; + cats_in_file.add(catalog_id); + + const bool should_skip = is_catalog_known_with_unsaved_changes(catalog_id); + if (should_skip) { + /* Do not overwrite unsaved local changes. */ return false; } - /* This is a new catalog, so let's keep it around. */ - catalog_collection_->catalogs_.add_new(catalog_id, std::move(catalog)); + /* This is either a new catalog, or we can just replace the in-memory one with the newly loaded + * one. */ + catalog_collection_->catalogs_.add_overwrite(catalog_id, std::move(catalog)); return true; }; cdf->parse_catalog_file(cdf->file_path, catalog_parsed_callback); + this->purge_catalogs_not_listed(cats_in_file); + this->rebuild_tree(); +} + +void AssetCatalogService::purge_catalogs_not_listed(const Set &catalogs_to_keep) +{ + Set cats_to_remove; + for (CatalogID cat_id : this->catalog_collection_->catalogs_.keys()) { + if (catalogs_to_keep.contains(cat_id)) { + continue; + } + if (is_catalog_known_with_unsaved_changes(cat_id)) { + continue; + } + /* This catalog is not on disk, but also not modified, so get rid of it. */ + cats_to_remove.add(cat_id); + } + + for (CatalogID cat_id : cats_to_remove) { + delete_catalog_by_id_hard(cat_id); + } +} + +bool AssetCatalogService::is_catalog_known_with_unsaved_changes(const CatalogID catalog_id) const +{ + if (catalog_collection_->deleted_catalogs_.contains(catalog_id)) { + /* Deleted catalogs are always considered modified, by definition. */ + return true; + } + + const std::unique_ptr *catalog_uptr_ptr = + catalog_collection_->catalogs_.lookup_ptr(catalog_id); + if (!catalog_uptr_ptr) { + /* Catalog is unknown. */ + return false; + } + + const bool has_unsaved_changes = (*catalog_uptr_ptr)->flags.has_unsaved_changes; + return has_unsaved_changes; } bool AssetCatalogService::write_to_disk(const CatalogFilePath &blend_file_path) @@ -386,6 +462,7 @@ bool AssetCatalogService::write_to_disk(const CatalogFilePath &blend_file_path) } untag_has_unsaved_changes(); + rebuild_tree(); return true; } @@ -395,7 +472,7 @@ bool AssetCatalogService::write_to_disk_ex(const CatalogFilePath &blend_file_pat /* - Already loaded a CDF from disk? -> Always write to that file. */ if (catalog_collection_->catalog_definition_file_) { - merge_from_disk_before_writing(); + reload_catalogs(); return catalog_collection_->catalog_definition_file_->write_to_disk(); } @@ -407,7 +484,7 @@ bool AssetCatalogService::write_to_disk_ex(const CatalogFilePath &blend_file_pat const CatalogFilePath cdf_path_to_write = find_suitable_cdf_path_for_writing(blend_file_path); catalog_collection_->catalog_definition_file_ = construct_cdf_in_memory(cdf_path_to_write); - merge_from_disk_before_writing(); + reload_catalogs(); return catalog_collection_->catalog_definition_file_->write_to_disk(); } @@ -507,7 +584,9 @@ void AssetCatalogService::create_missing_catalogs() } /* The parent doesn't exist, so create it and queue it up for checking its parent. */ - create_catalog(parent_path); + AssetCatalog *parent_catalog = create_catalog(parent_path); + parent_catalog->flags.has_unsaved_changes = true; + paths_to_check.insert(parent_path); } @@ -555,6 +634,7 @@ std::unique_ptr AssetCatalogCollection::deep_copy() cons { auto copy = std::make_unique(); + copy->has_unsaved_changes_ = this->has_unsaved_changes_; copy->catalogs_ = copy_catalog_map(this->catalogs_); copy->deleted_catalogs_ = copy_catalog_map(this->deleted_catalogs_); @@ -602,6 +682,10 @@ StringRefNull AssetCatalogTreeItem::get_simple_name() const { return simple_name_; } +bool AssetCatalogTreeItem::has_unsaved_changes() const +{ + return has_unsaved_changes_; +} AssetCatalogPath AssetCatalogTreeItem::catalog_path() const { @@ -668,9 +752,11 @@ void AssetCatalogTree::insert_item(const AssetCatalog &catalog) /* If full path of this catalog already exists as parent path of a previously read catalog, * we can ensure this tree item's UUID is set here. */ - if (is_last_component && - (BLI_uuid_is_nil(item.catalog_id_) || catalog.flags.is_first_loaded)) { - item.catalog_id_ = catalog.catalog_id; + if (is_last_component) { + if (BLI_uuid_is_nil(item.catalog_id_) || catalog.flags.is_first_loaded) { + item.catalog_id_ = catalog.catalog_id; + } + item.has_unsaved_changes_ = catalog.flags.has_unsaved_changes; } /* Walk further into the path (no matter if a new item was created or not). */ @@ -705,6 +791,16 @@ void AssetCatalogDefinitionFile::add_new(AssetCatalog *catalog) catalogs_.add_new(catalog->catalog_id, catalog); } +void AssetCatalogDefinitionFile::add_overwrite(AssetCatalog *catalog) +{ + catalogs_.add_overwrite(catalog->catalog_id, catalog); +} + +void AssetCatalogDefinitionFile::forget(CatalogID catalog_id) +{ + catalogs_.remove(catalog_id); +} + void AssetCatalogDefinitionFile::parse_catalog_file( const CatalogFilePath &catalog_definition_file_path, AssetCatalogParsedFn catalog_loaded_callback) @@ -742,16 +838,8 @@ void AssetCatalogDefinitionFile::parse_catalog_file( continue; } - if (this->contains(non_owning_ptr->catalog_id)) { - std::cerr << catalog_definition_file_path << ": multiple definitions of catalog " - << non_owning_ptr->catalog_id << " in the same file, using first occurrence." - << std::endl; - /* Don't store 'catalog'; unique_ptr will free its memory. */ - continue; - } - /* The AssetDefinitionFile should include this catalog when writing it back to disk. */ - this->add_new(non_owning_ptr); + this->add_overwrite(non_owning_ptr); } } diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index a6e372e4ae6..a2835d7b3b2 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -35,6 +35,7 @@ const bUUID UUID_ID_WITHOUT_PATH("e34dd2c5-5d2e-4668-9794-1db5de2a4f71"); const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); const bUUID UUID_POSES_ELLIE_WHITESPACE("b06132f6-5687-4751-a6dd-392740eb3c46"); const bUUID UUID_POSES_ELLIE_TRAILING_SLASH("3376b94b-a28d-4d05-86c1-bf30b937130d"); +const bUUID UUID_POSES_ELLIE_BACKSLASHES("a51e17ae-34fc-47d5-ba0f-64c2c9b771f7"); const bUUID UUID_POSES_RUZENA("79a4f887-ab60-4bd4-94da-d572e27d6aed"); const bUUID UUID_POSES_RUZENA_HAND("81811c31-1a88-4bd7-bb34-c6fc2607a12e"); const bUUID UUID_POSES_RUZENA_FACE("82162c1f-06cc-4d91-a9bf-4f72c104e348"); @@ -59,11 +60,21 @@ class TestableAssetCatalogService : public AssetCatalogService { return AssetCatalogService::get_catalog_definition_file(); } + OwningAssetCatalogMap &get_deleted_catalogs() + { + return AssetCatalogService::get_deleted_catalogs(); + } + void create_missing_catalogs() { AssetCatalogService::create_missing_catalogs(); } + void delete_catalog_by_id_soft(CatalogID catalog_id) + { + AssetCatalogService::delete_catalog_by_id_soft(catalog_id); + } + int64_t count_catalogs_with_path(const CatalogFilePath &path) { int64_t count = 0; @@ -305,8 +316,7 @@ TEST_F(AssetCatalogTest, load_catalog_path_backslashes) AssetCatalogService service(asset_library_root_); service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); - const bUUID ELLIE_BACKSLASHES_UUID("a51e17ae-34fc-47d5-ba0f-64c2c9b771f7"); - const AssetCatalog *found_by_id = service.find_catalog(ELLIE_BACKSLASHES_UUID); + const AssetCatalog *found_by_id = service.find_catalog(UUID_POSES_ELLIE_BACKSLASHES); ASSERT_NE(nullptr, found_by_id); EXPECT_EQ(AssetCatalogPath("character/Ellie/backslashes"), found_by_id->path) << "Backslashes should be normalised when loading from disk."; @@ -799,11 +809,11 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf) TEST_F(AssetCatalogTest, delete_catalog_parent_by_id) { - AssetCatalogService service(asset_library_root_); + TestableAssetCatalogService service(asset_library_root_); service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); /* Delete a parent catalog. */ - service.delete_catalog_by_id(UUID_POSES_RUZENA); + service.delete_catalog_by_id_soft(UUID_POSES_RUZENA); /* The catalog should have been deleted, but its children should still be there. */ EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA)); @@ -857,7 +867,7 @@ TEST_F(AssetCatalogTest, delete_catalog_write_to_disk) service.load_from_disk(asset_library_root_ + "/" + AssetCatalogService::DEFAULT_CATALOG_FILENAME); - service.delete_catalog_by_id(UUID_POSES_ELLIE); + service.delete_catalog_by_id_soft(UUID_POSES_ELLIE); const CatalogFilePath save_to_path = use_temp_path(); AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); @@ -938,7 +948,9 @@ TEST_F(AssetCatalogTest, merge_catalog_files) * CDF after we loaded it. */ ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str())); - /* Overwrite the modified file. This should merge the on-disk file with our catalogs. */ + /* Overwrite the modified file. This should merge the on-disk file with our catalogs. + * No catalog was marked as "has unsaved changes", so effectively this should not + * save anything, and reload what's on disk. */ service.write_to_disk(cdf_dir + "phony.blend"); AssetCatalogService loaded_service(cdf_dir); @@ -946,16 +958,90 @@ TEST_F(AssetCatalogTest, merge_catalog_files) /* Test that the expected catalogs are there. */ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); - EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); - EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); - EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); - EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_AGENT_47)); /* New in the modified file. */ - /* When there are overlaps, the in-memory (i.e. last-saved) paths should win. */ + /* Test that catalogs removed from modified CDF are gone. */ + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); + + /* On-disk changed catalogs should have overridden in-memory not-changed ones. */ const AssetCatalog *ruzena_face = loaded_service.find_catalog(UUID_POSES_RUZENA_FACE); - EXPECT_EQ("character/Ružena/poselib/face", ruzena_face->path.str()); + EXPECT_EQ("character/Ružena/poselib/gezicht", ruzena_face->path.str()); +} + +TEST_F(AssetCatalogTest, refresh_catalogs_with_modification) +{ + const CatalogFilePath cdf_dir = create_temp_path(); + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath modified_cdf_file = asset_library_root_ + "/catalog_reload_test.cats.txt"; + const CatalogFilePath temp_cdf_file = cdf_dir + "blender_assets.cats.txt"; + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str())); + + /* Load the unmodified, original CDF. */ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(cdf_dir); + + /* === Perfom changes that should be handled gracefully by the reloading code: */ + + /* 1. Delete a subtree of catalogs. */ + service.prune_catalogs_by_id(UUID_POSES_RUZENA); + /* 2. Rename a catalog. */ + service.tag_has_unsaved_changes(service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + service.update_catalog_path(UUID_POSES_ELLIE_TRAILING_SLASH, "character/Ellie/test-value"); + + /* Copy a modified file, to mimic a situation where someone changed the + * CDF after we loaded it. */ + ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str())); + + AssetCatalog *const ellie_whitespace_before_reload = service.find_catalog( + UUID_POSES_ELLIE_WHITESPACE); + + /* This should merge the on-disk file with our catalogs. */ + service.reload_catalogs(); + + /* === Test that the expected catalogs are there. */ + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_ELLIE)); + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + + /* === Test changes made to the CDF: */ + + /* Removed from the file. */ + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_ELLIE_BACKSLASHES)); + /* Added to the file. */ + EXPECT_NE(nullptr, service.find_catalog(UUID_AGENT_47)); + /* Path modified in file. */ + AssetCatalog *ellie_whitespace_after_reload = service.find_catalog(UUID_POSES_ELLIE_WHITESPACE); + EXPECT_EQ(AssetCatalogPath("whitespace from file"), ellie_whitespace_after_reload->path); + EXPECT_NE(ellie_whitespace_after_reload, ellie_whitespace_before_reload); + /* Simple name modified in file. */ + EXPECT_EQ(std::string("Hah simple name after all"), + service.find_catalog(UUID_WITHOUT_SIMPLENAME)->simple_name); + + /* === Test persistence of in-memory changes: */ + + /* This part of the tree we deleted, but still existed in the CDF. They should remain deleted + * after reloading: */ + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_FACE)); + + /* This catalog had its path changed in the test and in the CDF. The change from the test (i.e. + * the in-memory, yet-unsaved change) should persist. */ + EXPECT_EQ(AssetCatalogPath("character/Ellie/test-value"), + service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)->path); + + /* Overwrite the modified file. This should merge the on-disk file with our catalogs, and clear + * the "has_unsaved_changes" flags. */ + service.write_to_disk(cdf_dir + "phony.blend"); + + EXPECT_FALSE(service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)->flags.has_unsaved_changes) + << "The catalogs whose path we changed should now be saved"; + EXPECT_TRUE(service.get_deleted_catalogs().is_empty()) + << "Deleted catalogs should not be remembered after saving."; } TEST_F(AssetCatalogTest, backups) @@ -966,9 +1052,9 @@ TEST_F(AssetCatalogTest, backups) ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); /* Read a CDF, modify, and write it. */ - AssetCatalogService service(cdf_dir); + TestableAssetCatalogService service(cdf_dir); service.load_from_disk(); - service.delete_catalog_by_id(UUID_POSES_ELLIE); + service.delete_catalog_by_id_soft(UUID_POSES_ELLIE); service.write_to_disk(cdf_dir + "phony.blend"); const CatalogFilePath backup_path = writable_cdf_file + "~"; @@ -1100,6 +1186,13 @@ TEST_F(AssetCatalogTest, create_missing_catalogs_after_loading) ASSERT_NE(nullptr, cat_ellie) << "Missing parents should be created immediately after loading."; ASSERT_NE(nullptr, cat_ruzena) << "Missing parents should be created immediately after loading."; + EXPECT_TRUE(cat_char->flags.has_unsaved_changes) + << "Missing parents should be marked as having changes."; + EXPECT_TRUE(cat_ellie->flags.has_unsaved_changes) + << "Missing parents should be marked as having changes."; + EXPECT_TRUE(cat_ruzena->flags.has_unsaved_changes) + << "Missing parents should be marked as having changes."; + AssetCatalogDefinitionFile *cdf = loaded_service.get_catalog_definition_file(); ASSERT_NE(nullptr, cdf); EXPECT_TRUE(cdf->contains(cat_char->catalog_id)) << "Missing parents should be saved to a CDF."; diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index 6c4660ae75d..aae8a289d32 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -128,6 +128,11 @@ void AssetLibrary::load(StringRefNull library_root_directory) this->catalog_service = std::move(catalog_service); } +void AssetLibrary::refresh() +{ + this->catalog_service->reload_catalogs(); +} + namespace { void asset_library_on_save_post(struct Main *main, struct PointerRNA **pointers, diff --git a/source/blender/blenkernel/intern/asset_library_service.cc b/source/blender/blenkernel/intern/asset_library_service.cc index aeded0bc128..7cf95ee4cc1 100644 --- a/source/blender/blenkernel/intern/asset_library_service.cc +++ b/source/blender/blenkernel/intern/asset_library_service.cc @@ -77,7 +77,9 @@ AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull top_l AssetLibraryPtr *lib_uptr_ptr = on_disk_libraries_.lookup_ptr(top_dir_trailing_slash); if (lib_uptr_ptr != nullptr) { CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", top_dir_trailing_slash.c_str()); - return lib_uptr_ptr->get(); + AssetLibrary *lib = lib_uptr_ptr->get(); + lib->refresh(); + return lib; } AssetLibraryPtr lib_uptr = std::make_unique(); diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index ed132d7a8d8..80504bbdc05 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -30,6 +30,8 @@ namespace blender::bke::tests { +const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); + class AssetLibraryServiceTest : public testing::Test { public: CatalogFilePath asset_library_root_; @@ -162,12 +164,18 @@ TEST_F(AssetLibraryServiceTest, has_any_unsaved_catalogs) const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); cat_service->prune_catalogs_by_id(UUID_POSES_ELLIE); EXPECT_FALSE(service->has_any_unsaved_catalogs()) - << "Deletion of catalogs via AssetCatalogService should not tag as 'unsaved changes'."; + << "Deletion of catalogs via AssetCatalogService should not automatically tag as 'unsaved " + "changes'."; - cat_service->tag_has_unsaved_changes(); + const bUUID UUID_POSES_RUZENA("79a4f887-ab60-4bd4-94da-d572e27d6aed"); + AssetCatalog *cat = cat_service->find_catalog(UUID_POSES_RUZENA); + ASSERT_NE(nullptr, cat) << "Catalog " << UUID_POSES_RUZENA << " should be known"; + + cat_service->tag_has_unsaved_changes(cat); EXPECT_TRUE(service->has_any_unsaved_catalogs()) << "Tagging as having unsaved changes of a single catalog service should result in unsaved " "changes being reported."; + EXPECT_TRUE(cat->flags.has_unsaved_changes); } TEST_F(AssetLibraryServiceTest, has_any_unsaved_catalogs_after_write) @@ -185,15 +193,19 @@ TEST_F(AssetLibraryServiceTest, has_any_unsaved_catalogs_after_write) << "Unchanged AssetLibrary should have no unsaved catalogs"; AssetCatalogService *const cat_service = lib->catalog_service.get(); - cat_service->tag_has_unsaved_changes(); + AssetCatalog *cat = cat_service->find_catalog(UUID_POSES_ELLIE); + + cat_service->tag_has_unsaved_changes(cat); EXPECT_TRUE(service->has_any_unsaved_catalogs()) << "Tagging as having unsaved changes of a single catalog service should result in unsaved " "changes being reported."; + EXPECT_TRUE(cat->flags.has_unsaved_changes); cat_service->write_to_disk(writable_dir + "dummy_path.blend"); EXPECT_FALSE(service->has_any_unsaved_catalogs()) << "Written AssetCatalogService should have no unsaved catalogs"; + EXPECT_FALSE(cat->flags.has_unsaved_changes); } } // namespace blender::bke::tests diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index 98f017db20c..af8128ffb8a 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -71,11 +71,11 @@ AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library, AssetCatalogPath fullpath = AssetCatalogPath(parent_path) / unique_name; catalog_service->undo_push(); - catalog_service->tag_has_unsaved_changes(); bke::AssetCatalog *new_catalog = catalog_service->create_catalog(fullpath); if (!new_catalog) { return nullptr; } + catalog_service->tag_has_unsaved_changes(new_catalog); return new_catalog; } @@ -89,7 +89,7 @@ void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_i } catalog_service->undo_push(); - catalog_service->tag_has_unsaved_changes(); + catalog_service->tag_has_unsaved_changes(nullptr); catalog_service->prune_catalogs_by_id(catalog_id); } @@ -103,13 +103,18 @@ void ED_asset_catalog_rename(::AssetLibrary *library, return; } - const AssetCatalog *catalog = catalog_service->find_catalog(catalog_id); + AssetCatalog *catalog = catalog_service->find_catalog(catalog_id); AssetCatalogPath new_path = catalog->path.parent(); new_path = new_path / StringRef(new_name); + if (new_path == catalog->path) { + /* Nothing changed, so don't bother renaming for nothing. */ + return; + } + catalog_service->undo_push(); - catalog_service->tag_has_unsaved_changes(); + catalog_service->tag_has_unsaved_changes(catalog); catalog_service->update_catalog_path(catalog_id, new_path); } diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 0eade57934e..7cdf9be689c 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -219,7 +219,12 @@ void AssetCatalogTreeViewItem::on_activate() void AssetCatalogTreeViewItem::build_row(uiLayout &row) { - ui::BasicTreeViewItem::build_row(row); + if (catalog_item_.has_unsaved_changes()) { + uiItemL(&row, (label_ + "*").c_str(), icon); + } + else { + uiItemL(&row, label_.c_str(), icon); + } if (!is_hovered()) { return; From 85312f2236784716fc24cc5d3236cb016fb99828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 22 Oct 2021 16:32:05 +0200 Subject: [PATCH 1087/1500] Asset Browser: Show "orphaned" assets in "Unassigned" catalog Show assets that have an unknown catalog ID assigned in the "Unassigned" catalog. Another catalog named "Orphans" was considered as well, but that would clash with the usual handling of Blender (discarding orphan data on save) and thus that idea was discarded. Manifest Task: T91949 --- .../blender/blenkernel/BKE_asset_catalog.hh | 14 +++++++-- .../blenkernel/intern/asset_catalog.cc | 31 +++++++++++++------ .../space_file/asset_catalog_tree_view.cc | 2 +- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 902c0e414d6..766a3f34a66 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -118,6 +118,11 @@ class AssetCatalogService { * none marked as "first loaded", return the one with the lowest UUID. */ AssetCatalog *find_catalog_by_path(const AssetCatalogPath &path) const; + /** + * Return true only if this catalog is known. + * This treats deleted catalogs as "unknown". */ + bool is_catalog_known(CatalogID catalog_id) const; + /** * Create a filter object that can be used to determine whether an asset belongs to the given * catalog, or any of the catalogs in the sub-tree rooted at the given catalog. @@ -488,17 +493,22 @@ using MutableAssetCatalogOrderedSet = std::set matching_catalog_ids; + const Set known_catalog_ids; - explicit AssetCatalogFilter(Set &&matching_catalog_ids); + explicit AssetCatalogFilter(Set &&matching_catalog_ids, + Set &&known_catalog_ids); }; } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index a1647426c41..41510ff1d02 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -154,18 +154,20 @@ AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath & return *best_choice_it; } +bool AssetCatalogService::is_catalog_known(CatalogID catalog_id) const +{ + BLI_assert(catalog_collection_); + return catalog_collection_->catalogs_.contains(catalog_id); +} + AssetCatalogFilter AssetCatalogService::create_catalog_filter( const CatalogID active_catalog_id) const { Set matching_catalog_ids; + Set known_catalog_ids; matching_catalog_ids.add(active_catalog_id); const AssetCatalog *active_catalog = find_catalog(active_catalog_id); - if (!active_catalog) { - /* If the UUID is unknown (i.e. not mapped to an actual Catalog), it is impossible to determine - * its children. The filter can still work on the given UUID. */ - return AssetCatalogFilter(std::move(matching_catalog_ids)); - } /* This cannot just iterate over tree items to get all the required data, because tree items only * represent single UUIDs. It could be used to get the main UUIDs of the children, though, and @@ -173,12 +175,13 @@ AssetCatalogFilter AssetCatalogService::create_catalog_filter( * call). Without an extra indexed-by-path acceleration structure, this is still going to require * a linear search, though. */ for (const auto &catalog_uptr : catalog_collection_->catalogs_.values()) { - if (catalog_uptr->path.is_contained_in(active_catalog->path)) { + if (active_catalog && catalog_uptr->path.is_contained_in(active_catalog->path)) { matching_catalog_ids.add(catalog_uptr->catalog_id); } + known_catalog_ids.add(catalog_uptr->catalog_id); } - return AssetCatalogFilter(std::move(matching_catalog_ids)); + return AssetCatalogFilter(std::move(matching_catalog_ids), std::move(known_catalog_ids)); } void AssetCatalogService::delete_catalog_by_id_soft(const CatalogID catalog_id) @@ -1063,8 +1066,10 @@ std::string AssetCatalog::sensible_simple_name_for_path(const AssetCatalogPath & return "..." + name.substr(name.length() - 60); } -AssetCatalogFilter::AssetCatalogFilter(Set &&matching_catalog_ids) - : matching_catalog_ids(std::move(matching_catalog_ids)) +AssetCatalogFilter::AssetCatalogFilter(Set &&matching_catalog_ids, + Set &&known_catalog_ids) + : matching_catalog_ids(std::move(matching_catalog_ids)), + known_catalog_ids(std::move(known_catalog_ids)) { } @@ -1073,4 +1078,12 @@ bool AssetCatalogFilter::contains(const CatalogID asset_catalog_id) const return matching_catalog_ids.contains(asset_catalog_id); } +bool AssetCatalogFilter::is_known(const CatalogID asset_catalog_id) const +{ + if (BLI_uuid_is_nil(asset_catalog_id)) { + return false; + } + return known_catalog_ids.contains(asset_catalog_id); +} + } // namespace blender::bke diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 7cdf9be689c..3b26a66cd05 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -490,7 +490,7 @@ bool file_is_asset_visible_in_catalog_filter_settings( switch (filter_settings->asset_catalog_visibility) { case FILE_SHOW_ASSETS_WITHOUT_CATALOG: - return BLI_uuid_is_nil(asset_data->catalog_id); + return !filter_settings->catalog_filter->is_known(asset_data->catalog_id); case FILE_SHOW_ASSETS_FROM_CATALOG: return filter_settings->catalog_filter->contains(asset_data->catalog_id); case FILE_SHOW_ASSETS_ALL_CATALOGS: From cd6fc651cec1d45e206ba5dce38dd120e2e8a8c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 22 Oct 2021 16:35:18 +0200 Subject: [PATCH 1088/1500] Asset Browser: add notifiers on catalog edits Send out notifications on catalog edits. This way all asset browsers will refresh when catalogs are edited. --- source/blender/editors/asset/intern/asset_catalog.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index af8128ffb8a..dae960cbb0a 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -77,6 +77,7 @@ AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library, } catalog_service->tag_has_unsaved_changes(new_catalog); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); return new_catalog; } @@ -91,6 +92,7 @@ void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_i catalog_service->undo_push(); catalog_service->tag_has_unsaved_changes(nullptr); catalog_service->prune_catalogs_by_id(catalog_id); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); } void ED_asset_catalog_rename(::AssetLibrary *library, @@ -116,6 +118,7 @@ void ED_asset_catalog_rename(::AssetLibrary *library, catalog_service->undo_push(); catalog_service->tag_has_unsaved_changes(catalog); catalog_service->update_catalog_path(catalog_id, new_path); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); } void ED_asset_catalogs_save_from_main_path(::AssetLibrary *library, const Main *bmain) From 7ef3fe492eaa63f9b84021abcaba27745aff8e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 22 Oct 2021 16:40:24 +0200 Subject: [PATCH 1089/1500] Asset Browser: fix issue rebuilding the visible asset filter --- source/blender/editors/space_file/asset_catalog_tree_view.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 3b26a66cd05..c305a11daf4 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -475,7 +475,7 @@ void file_ensure_updated_catalog_filter_data( const AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service( asset_library); - if (filter_settings->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG) { + if (filter_settings->asset_catalog_visibility != FILE_SHOW_ASSETS_ALL_CATALOGS) { filter_settings->catalog_filter = std::make_unique( catalog_service->create_catalog_filter(filter_settings->asset_catalog_id)); } From cca811de98fa9eeddc128d1fa0e26e4ec1b700f9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 22 Oct 2021 18:26:00 +0300 Subject: [PATCH 1090/1500] Fix test print wording that confuses build bot highlighting. --- source/blender/blenkernel/intern/armature_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index bd2088c6aba..2375c606247 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -331,7 +331,7 @@ static void test_vec_roll_to_mat3_orthogonal(double s, double x1, double x2, dou } } - printf(" Max determinant error %.10f at %f.\n", delta, tmax); + printf(" Max determinant deviation %.10f at %f.\n", delta, tmax); } #define TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(name, s, x1, x2, y1, y2) \ From 7ac4e874db5a176429da2cf869b0552a24ef0b3e Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 22 Oct 2021 13:13:48 +0200 Subject: [PATCH 1091/1500] Fix panel pinning showing for instanced panels (e.g. FCurve modifiers) This was reported for FCurve modifiers, but was also true (in theory) for other instanced panels (regular modifiers, spreadsheet filters, ...), these would not show pinning for other reasons (no caterories). So in the case of the Graph Editor the follwing happens: `graph_buttons_register` only registers `GRAPH_PT_modifiers`, the panel itself has no header (PANEL_TYPE_NO_HEADER), further panels for individual modifiers are added dynamically in `graph_panel_modifiers`. So when pinning a particular modifier, we would pin e.g. `GRAPH_PT_noise` (not `GRAPH_PT_modifiers`). ED_region_panels_layout_ex would only collect panels known to `graph_buttons_register` (so is not aware of the specific panels of modifiers). So while I think it should be possible to pin `GRAPH_PT_modifiers` on top of an individual modifier's panel this would result in all modifiers being shown in other categories [which would also be weird]. Panel header layout was also not correct (drawing the pin icon over the modifier delete icon). So to resolve this, just dont use pinning for these type of panels. part of T92293. Maniphest Tasks: T92293 Differential Revision: https://developer.blender.org/D12965 --- source/blender/editors/include/UI_interface.h | 1 + source/blender/editors/interface/interface_context_menu.c | 3 +++ source/blender/editors/interface/interface_panel.c | 7 ++++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 00e96955fb4..0d6236a7728 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1777,6 +1777,7 @@ void UI_panel_label_offset(const struct uiBlock *block, int *r_x, int *r_y); int UI_panel_size_y(const struct Panel *panel); bool UI_panel_is_dragging(const struct Panel *panel); bool UI_panel_matches_search_filter(const struct Panel *panel); +bool UI_panel_can_be_pinned(const struct Panel *panel); bool UI_panel_category_is_visible(const struct ARegion *region); void UI_panel_category_add(struct ARegion *region, const char *name); diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 1e4f1a7f5dc..516fd4fc7fc 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -1257,6 +1257,9 @@ void ui_popup_context_menu_for_panel(bContext *C, ARegion *region, Panel *panel) if (panel->type->parent != NULL) { return; } + if (!UI_panel_can_be_pinned(panel)) { + return; + } PointerRNA ptr; RNA_pointer_create(&screen->id, &RNA_Panel, panel, &ptr); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 3cc9a743a17..a22351eea7e 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -2100,7 +2100,7 @@ static void ui_handle_panel_header(const bContext *C, BLI_assert(!(panel->type->flag & PANEL_TYPE_NO_HEADER)); const bool is_subpanel = (panel->type->parent != NULL); - const bool use_pin = UI_panel_category_is_visible(region) && !is_subpanel; + const bool use_pin = UI_panel_category_is_visible(region) && UI_panel_can_be_pinned(panel); const bool show_pin = use_pin && (panel->flag & PNL_PIN); const bool show_drag = !is_subpanel; @@ -2497,6 +2497,11 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm return NULL; } +bool UI_panel_can_be_pinned(const Panel *panel) +{ + return (panel->type->parent == NULL) && !(panel->type->flag & PANEL_TYPE_INSTANCED); +} + /** \} */ /* -------------------------------------------------------------------- */ From 742b7adbad0e50e5cb321c9f0c80be3300a3e368 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 22 Oct 2021 16:01:48 -0500 Subject: [PATCH 1092/1500] Cleanup: Remove unused function --- .../editors/interface/interface_intern.h | 1 - .../editors/interface/interface_widgets.c | 24 ------------------- 2 files changed, 25 deletions(-) diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 3e610e62b5a..0826157b5e6 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1033,7 +1033,6 @@ struct GPUBatch *ui_batch_roundbox_shadow_get(void); void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]); void ui_draw_menu_back(struct uiStyle *style, uiBlock *block, rcti *rect); -void ui_draw_box_opaque(rcti *rect, int roundboxalign); void ui_draw_popover_back(struct ARegion *region, struct uiStyle *style, uiBlock *block, diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index a26d409037d..e9acc65ed37 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -4986,30 +4986,6 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) ui_draw_clip_tri(block, rect, wt); } -/** - * Uses the widget base drawing and colors from the box widget, but ensures an opaque - * inner color. - */ -void ui_draw_box_opaque(rcti *rect, int roundboxalign) -{ - uiWidgetType *wt = widget_type(UI_WTYPE_BOX); - - /* Alpha blend with the region's background color to force an opaque background. */ - uiWidgetColors *wcol = &wt->wcol; - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); - float background[4]; - UI_GetThemeColor4fv(TH_BACK, background); - float new_inner[4]; - rgba_uchar_to_float(new_inner, wcol->inner); - new_inner[0] = (new_inner[0] * new_inner[3]) + (background[0] * (1.0f - new_inner[3])); - new_inner[1] = (new_inner[1] * new_inner[3]) + (background[1] * (1.0f - new_inner[3])); - new_inner[2] = (new_inner[2] * new_inner[3]) + (background[2] * (1.0f - new_inner[3])); - new_inner[3] = 1.0f; - rgba_float_to_uchar(wcol->inner, new_inner); - - wt->custom(NULL, wcol, rect, 0, roundboxalign); -} - /** * Similar to 'widget_menu_back', however we can't use the widget preset system * because we need to pass in the original location so we know where to show the arrow. From cf72b10075758be971f9806b97db01f98383aba2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 22 Oct 2021 16:21:16 -0500 Subject: [PATCH 1093/1500] UI: Turn on overlays by default in new node editors This was missed in the initial commit adding the node editor overlays. --- source/blender/editors/space_node/space_node.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index c932525ee1a..bd2559c4d4d 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -92,6 +92,9 @@ void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from) snode->id = id; snode->from = from; + snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS; + snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS; + ED_node_set_active_viewer_key(snode); WM_main_add_notifier(NC_SCENE | ND_NODES, NULL); From 8cd4cee0d551d9d960154ff0a9e59ccd21e63ce9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 22 Oct 2021 16:21:37 -0500 Subject: [PATCH 1094/1500] Fix: Double calculation of UVs in cone primitive --- .../nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 059e6e8680c..93c35a1111b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -695,8 +695,6 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, BKE_mesh_normals_tag_dirty(mesh); - calculate_cone_uvs(mesh, config); - return mesh; } From 3e1fd2682807c31b62168737c953a6b4ea33ce77 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 22 Oct 2021 16:39:39 -0500 Subject: [PATCH 1095/1500] Fix: Assert for unused field evaluator in resample curve node Declare it at a lower scope so it doesn't go unused in evaluated mode. --- .../blender/nodes/geometry/nodes/node_geo_curve_resample.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 3db18992c93..2617b2f6646 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -171,8 +171,6 @@ static std::unique_ptr resample_curve(const CurveComponent *component GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE}; const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; - Span input_splines = input_curve->splines(); std::unique_ptr output_curve = std::make_unique(); @@ -180,6 +178,7 @@ static std::unique_ptr resample_curve(const CurveComponent *component MutableSpan output_splines = output_curve->splines(); if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { + fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.add(*mode_param.count); evaluator.evaluate(); const VArray &cuts = evaluator.get_evaluated(0); @@ -192,6 +191,7 @@ static std::unique_ptr resample_curve(const CurveComponent *component }); } else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { + fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.add(*mode_param.length); evaluator.evaluate(); const VArray &lengths = evaluator.get_evaluated(0); From ca5d84b31d16c2e69847ffee16f8d5b092623490 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 22 Oct 2021 12:33:28 +0200 Subject: [PATCH 1096/1500] UI: Show ID filter settings in Link/Append File Browser as icon & checkbox The checkboxes integrate better with the surrounding layout and are not that attention grabbing. To my knowledge the only reason not to use checkboxes was so the icons could be displayed. But this does it just like the Outliner filter settings: Show the icon before the checkbox. Also widen the popover a bit to fit longer labels (didn't fit before this patch even). --- release/scripts/startup/bl_ui/space_filebrowser.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 641812694c8..420481eec13 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -127,7 +127,7 @@ class FILEBROWSER_PT_display(FileBrowserPanel, Panel): class FILEBROWSER_PT_filter(FileBrowserPanel, Panel): bl_region_type = 'HEADER' bl_label = "Filter Settings" # Shows as tooltip in popover - bl_ui_units_x = 8 + bl_ui_units_x = 10 def draw(self, context): layout = self.layout @@ -195,7 +195,9 @@ class FILEBROWSER_PT_filter(FileBrowserPanel, Panel): filter_id = params.filter_id for identifier in dir(filter_id): if identifier.startswith("category_"): - sub.prop(filter_id, identifier, toggle=True) + subrow = sub.row() + subrow.label(icon=filter_id.bl_rna.properties[identifier].icon) + subrow.prop(filter_id, identifier, toggle=False) col.separator() @@ -388,7 +390,9 @@ class FILEBROWSER_PT_advanced_filter(Panel): filter_id = params.filter_id for identifier in dir(filter_id): if identifier.startswith("filter_"): - col.prop(filter_id, identifier, toggle=True) + row = col.row() + row.label(icon=filter_id.bl_rna.properties[identifier].icon) + row.prop(filter_id, identifier, toggle=False) def is_option_region_visible(context, space): From c51eac24fea8e010a8fed84f5f8787521067fb42 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 22 Oct 2021 23:54:03 +0200 Subject: [PATCH 1097/1500] UI: Correct icons of File Browser Link/Append ID filter settings Wasn't using the same icons as usually used for these IDs, which can be confusing. Corrected them to be consistent with other usages of these IDs. --- source/blender/makesrna/intern/rna_ID.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index fe03d5245ba..be612a1602b 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -131,7 +131,7 @@ static const EnumPropertyItem rna_enum_override_library_property_operation_items */ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { /* Datablocks */ - {FILTER_ID_AC, "filter_action", ICON_ANIM_DATA, "Actions", "Show Action data-blocks"}, + {FILTER_ID_AC, "filter_action", ICON_ACTION, "Actions", "Show Action data-blocks"}, {FILTER_ID_AR, "filter_armature", ICON_ARMATURE_DATA, @@ -173,7 +173,7 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { {FILTER_ID_MB, "filter_metaball", ICON_META_DATA, "Metaballs", "Show Metaball data-blocks"}, {FILTER_ID_MC, "filter_movie_clip", - ICON_TRACKER_DATA, + ICON_TRACKER, "Movie Clips", "Show Movie Clip data-blocks"}, {FILTER_ID_ME, "filter_mesh", ICON_MESH_DATA, "Meshes", "Show Mesh data-blocks"}, From cfc64261c12e90bf3219cb377d4fe7c008407850 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 22 Oct 2021 23:56:05 +0200 Subject: [PATCH 1098/1500] Asset Browser: Filter options for specific ID types Adds a filter popup to the header that allows specifiying which data-block types to show. The menu automatically reflects all supported ID types, so it shows a checkbox for materials, worlds and actions currently by default, and all ID types with the "Extended Asset Browser" experimental feature enabled. --- .../startup/bl_ui/space_filebrowser.py | 29 +++++++++++ .../blenloader/intern/versioning_300.c | 16 ++++++ source/blender/editors/asset/ED_asset_type.h | 3 ++ .../editors/asset/intern/asset_type.cc | 8 +-- .../blender/editors/space_file/space_file.c | 5 +- source/blender/makesrna/intern/rna_space.c | 52 +++++++++++++++++++ 6 files changed, 105 insertions(+), 8 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 420481eec13..62a9343f644 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -54,6 +54,12 @@ class FILEBROWSER_HT_header(Header): layout.prop(params, "filter_search", text="", icon='VIEWZOOM') + layout.popover( + panel="ASSETBROWSER_PT_filter", + text="", + icon='FILTER' + ) + layout.operator( "screen.region_toggle", text="", @@ -592,6 +598,28 @@ class ASSETBROWSER_PT_display(asset_utils.AssetBrowserPanel, Panel): col.prop(params, "show_details_datetime", text="Date") +class ASSETBROWSER_PT_filter(asset_utils.AssetBrowserPanel, Panel): + bl_region_type = 'HEADER' + bl_category = "Filter" + bl_label = "Filter" + + def draw(self, context): + layout = self.layout + space = context.space_data + params = space.params + use_extended_browser = context.preferences.experimental.use_extended_asset_browser + + if params.use_filter_blendid: + col = layout.column(align=True) + + filter_id = params.filter_asset_id + for identifier in dir(filter_id): + if identifier.startswith("filter_") or (identifier.startswith("experimental_filter_") and use_extended_browser): + row = col.row() + row.label(icon=filter_id.bl_rna.properties[identifier].icon) + row.prop(filter_id, identifier, toggle=False) + + class AssetBrowserMenu: @classmethod def poll(cls, context): @@ -794,6 +822,7 @@ classes = ( FILEBROWSER_MT_select, FILEBROWSER_MT_context_menu, ASSETBROWSER_PT_display, + ASSETBROWSER_PT_filter, ASSETBROWSER_MT_editor_menus, ASSETBROWSER_MT_view, ASSETBROWSER_MT_select, diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 7d93439a44d..c68bae1d4af 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2005,6 +2005,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 38)) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { + if (space->spacetype == SPACE_FILE) { + SpaceFile *sfile = (SpaceFile *)space; + FileAssetSelectParams *asset_params = sfile->asset_params; + if (asset_params) { + asset_params->base_params.filter_id = FILTER_ID_ALL; + } + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/editors/asset/ED_asset_type.h b/source/blender/editors/asset/ED_asset_type.h index 1030be1ea05..5629ae189c0 100644 --- a/source/blender/editors/asset/ED_asset_type.h +++ b/source/blender/editors/asset/ED_asset_type.h @@ -20,6 +20,8 @@ #pragma once +#include "DNA_ID.h" + #ifdef __cplusplus extern "C" { #endif @@ -27,6 +29,7 @@ extern "C" { struct ID; bool ED_asset_type_id_is_non_experimental(const struct ID *id); +#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS (FILTER_ID_MA | FILTER_ID_AC | FILTER_ID_WO) /** * Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset, diff --git a/source/blender/editors/asset/intern/asset_type.cc b/source/blender/editors/asset/intern/asset_type.cc index 5be627e3fbc..cdff538a712 100644 --- a/source/blender/editors/asset/intern/asset_type.cc +++ b/source/blender/editors/asset/intern/asset_type.cc @@ -29,13 +29,9 @@ bool ED_asset_type_id_is_non_experimental(const ID *id) { /* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and - * #asset_type_ids_non_experimental_as_filter_flags() with this! */ + * #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */ return ELEM(GS(id->name), ID_MA, ID_AC, ID_WO); } -static int64_t asset_type_ids_non_experimental_as_filter_flags() -{ - return FILTER_ID_MA | FILTER_ID_AC | FILTER_ID_WO; -} bool ED_asset_type_is_supported(const ID *id) { @@ -58,5 +54,5 @@ int64_t ED_asset_types_supported_as_filter_flags() return FILTER_ID_ALL; } - return asset_type_ids_non_experimental_as_filter_flags(); + return ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS; } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index b5c395c8bc2..a875b7a2c12 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -328,8 +328,9 @@ static void file_refresh(const bContext *C, ScrArea *area) } if (ED_fileselect_is_asset_browser(sfile)) { - /* Ask the asset code for appropriate ID filter flags for the supported assets. */ - params->filter_id = ED_asset_types_supported_as_filter_flags(); + /* Ask the asset code for appropriate ID filter flags for the supported assets, and mask others + * out. */ + params->filter_id &= ED_asset_types_supported_as_filter_flags(); } filelist_settype(sfile->files, params->type); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index cd6a7b64061..c91ef25daa8 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -34,11 +34,14 @@ #include "BKE_node.h" #include "BKE_studiolight.h" +#include "ED_asset.h" #include "ED_spreadsheet.h" #include "ED_text.h" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_sys_types.h" #include "BLI_uuid.h" #include "DNA_action_types.h" @@ -2624,6 +2627,11 @@ static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int val params->asset_library_ref = ED_asset_library_reference_from_enum_value(value); } +static PointerRNA rna_FileAssetSelectParams_filter_id_get(PointerRNA *ptr) +{ + return rna_pointer_inherit_refine(ptr, &RNA_FileAssetSelectIDFilter, ptr->data); +} + static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr) { const FileDirEntry *entry = ptr->data; @@ -6351,6 +6359,40 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna) } } +/* Filter for datablock types in the Asset Browser. */ +static void rna_def_fileselect_asset_idfilter(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "FileAssetSelectIDFilter", NULL); + RNA_def_struct_sdna(srna, "FileSelectParams"); + RNA_def_struct_nested(brna, srna, "FileSelectParams"); + RNA_def_struct_ui_text(srna, + "File Select Asset Filter", + "Which asset types to show/hide, when browsing an asset library"); + + static char experimental_prop_names[INDEX_ID_MAX][MAX_NAME]; + + for (uint i = 0; rna_enum_id_type_filter_items[i].identifier; i++) { + const struct IDFilterEnumPropertyItem *item = &rna_enum_id_type_filter_items[i]; + const bool is_experimental = (ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS & item->flag) == 0; + + const char *identifier = rna_enum_id_type_filter_items[i].identifier; + if (is_experimental) { + /* Create name for experimental property and store in static buffer. */ + snprintf(experimental_prop_names[i], + ARRAY_SIZE(experimental_prop_names[i]), + "experimental_%s", + identifier); + identifier = experimental_prop_names[i]; + } + + PropertyRNA *prop = RNA_def_property(srna, identifier, PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "filter_id", item->flag); + RNA_def_property_ui_text(prop, item->name, item->description); + RNA_def_property_ui_icon(prop, item->icon, 0); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + } +} + static void rna_def_fileselect_entry(BlenderRNA *brna) { PropertyRNA *prop; @@ -6673,6 +6715,15 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Catalog UUID", "The UUID of the catalog shown in the browser"); + prop = RNA_def_property(srna, "filter_asset_id", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "FileAssetSelectIDFilter"); + RNA_def_property_pointer_funcs( + prop, "rna_FileAssetSelectParams_filter_id_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, + "Filter Asset Types", + "Which asset types to show/hide, when browsing an asset library"); + prop = RNA_def_property(srna, "import_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, asset_import_type_items); RNA_def_property_ui_text(prop, "Import Type", "Determine how the asset will be imported"); @@ -7880,6 +7931,7 @@ void RNA_def_space(BlenderRNA *brna) rna_def_fileselect_params(brna); rna_def_fileselect_asset_params(brna); rna_def_fileselect_idfilter(brna); + rna_def_fileselect_asset_idfilter(brna); rna_def_filemenu_entry(brna); rna_def_space_filebrowser(brna); rna_def_space_outliner(brna); From 1d9e2dc954ff837737c0bbc00ef8ae4b841f2427 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 22 Oct 2021 17:13:26 -0500 Subject: [PATCH 1099/1500] Fix: Cyclic single point bezier splines have multiple evaluated points Because `segment_is_vector` didn't handle the combined cyclic and single control point case, it returned false, that the "segment" should have the resolution evaluated point count. To avoid checking the size in every call, add an assert for the size and check it elsewhere. --- .../blenkernel/intern/spline_bezier.cc | 35 ++++++++++++++----- .../nodes/legacy/node_geo_curve_subdivide.cc | 8 +++-- .../nodes/node_geo_curve_subdivide.cc | 8 +++-- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index f719a1cfda2..36ccadf521b 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -345,8 +345,14 @@ bool BezierSpline::point_is_sharp(const int index) const ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free); } +/** + * \warning: This functiona assumes that the spline has more than one point. + */ bool BezierSpline::segment_is_vector(const int index) const { + /* Two control points are necessary to form a segment, that should be checked by the caller. */ + BLI_assert(this->size() > 1); + if (index == this->size() - 1) { if (is_cyclic_) { return handle_types_right_.last() == HandleType::Vector && @@ -507,13 +513,18 @@ Span BezierSpline::control_point_offsets() const offset_cache_.resize(size + 1); MutableSpan offsets = offset_cache_; - - int offset = 0; - for (const int i : IndexRange(size)) { - offsets[i] = offset; - offset += this->segment_is_vector(i) ? 1 : resolution_; + if (size == 1) { + offsets.first() = 0; + offsets.last() = 1; + } + else { + int offset = 0; + for (const int i : IndexRange(size)) { + offsets[i] = offset; + offset += this->segment_is_vector(i) ? 1 : resolution_; + } + offsets.last() = offset; } - offsets.last() = offset; offset_cache_dirty_ = false; return offsets; @@ -600,14 +611,22 @@ Span BezierSpline::evaluated_positions() const return evaluated_position_cache_; } - this->ensure_auto_handles(); - const int size = this->size(); const int eval_size = this->evaluated_points_size(); evaluated_position_cache_.resize(eval_size); MutableSpan positions = evaluated_position_cache_; + if (size == 1) { + /* Use a special case for single point splines to avoid checking in #evaluate_segment. */ + BLI_assert(eval_size == 1); + positions.first() = positions_.first(); + position_cache_dirty_ = false; + return positions; + } + + this->ensure_auto_handles(); + Span offsets = this->control_point_offsets(); const int grain_size = std::max(512 / resolution_, 1); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc index f32a68bc042..61165902028 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc @@ -308,8 +308,12 @@ static SplinePtr subdivide_spline(const Spline &spline, const VArray &cuts, const int spline_offset) { - /* Since we expect to access each value many times, it should be worth it to make sure the - * attribute is a real span (especially considering the note below). Using the offset at each + if (spline.size() <= 1) { + return spline.copy(); + } + + /* Since we expect to access each value many times, it should be worth it to make sure count + * of cuts is a real span (especially considering the note below). Using the offset at each * point facilitates subdividing in parallel later. */ Array offsets = get_subdivided_offsets(spline, cuts, spline_offset); const int result_size = offsets.last() + int(!spline.is_cyclic()); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index a856d593bc2..5a79fcffd4d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -283,8 +283,12 @@ static SplinePtr subdivide_spline(const Spline &spline, const VArray &cuts, const int spline_offset) { - /* Since we expect to access each value many times, it should be worth it to make sure the - * attribute is a real span (especially considering the note below). Using the offset at each + if (spline.size() <= 1) { + return spline.copy(); + } + + /* Since we expect to access each value many times, it should be worth it to make sure count + * of cuts is a real span (especially considering the note below). Using the offset at each * point facilitates subdividing in parallel later. */ Array offsets = get_subdivided_offsets(spline, cuts, spline_offset); const int result_size = offsets.last() + int(!spline.is_cyclic()); From 62d64bec2a9c14c5d1515350cfc6b524118058c2 Mon Sep 17 00:00:00 2001 From: Xavier Cho Date: Fri, 22 Oct 2021 18:27:03 -0400 Subject: [PATCH 1100/1500] Docs: Fixes and improvements in API documentation Fixes several notable mistakes and missing information regarding the API documentation (*.rst). This will allow API stub generators like bpystubgen or fake-bpy-module to produce more accurate result. Differential Revision: https://developer.blender.org/D12639 --- extern/audaspace/bindings/python/PySound.cpp | 2 +- release/scripts/modules/bpy/path.py | 52 ++++++++++++++++--- .../blender/python/generic/bl_math_py_api.c | 22 ++++---- source/blender/python/gpu/gpu_py_texture.c | 2 +- source/blender/python/intern/bpy_rna.c | 4 +- .../python/mathutils/mathutils_noise.c | 2 +- 6 files changed, 62 insertions(+), 22 deletions(-) diff --git a/extern/audaspace/bindings/python/PySound.cpp b/extern/audaspace/bindings/python/PySound.cpp index 2236057e7d2..1f149482914 100644 --- a/extern/audaspace/bindings/python/PySound.cpp +++ b/extern/audaspace/bindings/python/PySound.cpp @@ -290,7 +290,7 @@ PyDoc_STRVAR(M_aud_Sound_buffer_doc, ".. classmethod:: buffer(data, rate)\n\n" " Creates a sound from a data buffer.\n\n" " :arg data: The data as two dimensional numpy array.\n" - " :type data: numpy.ndarray\n" + " :type data: :class:`numpy.ndarray`\n" " :arg rate: The sample rate.\n" " :type rate: double\n" " :return: The created :class:`Sound` object.\n" diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py index 30f93d051e5..37bdb939c07 100644 --- a/release/scripts/modules/bpy/path.py +++ b/release/scripts/modules/bpy/path.py @@ -67,6 +67,8 @@ def abspath(path, *, start=None, library=None): :arg library: The library this path is from. This is only included for convenience, when the library is not None its path replaces *start*. :type library: :class:`bpy.types.Library` + :return: The absolute path. + :rtype: string """ if isinstance(path, bytes): if path.startswith(b"//"): @@ -101,6 +103,8 @@ def relpath(path, *, start=None): :arg start: Relative to this path, when not set the current filename is used. :type start: string or bytes + :return: The relative path. + :rtype: string """ if isinstance(path, bytes): if not path.startswith(b"//"): @@ -123,6 +127,8 @@ def is_subdir(path, directory): :arg path: An absolute path. :type path: string or bytes + :return: Whether or not the path is a subdirectory. + :rtype: boolean """ from os.path import normpath, normcase, sep path = normpath(normcase(path)) @@ -141,6 +147,12 @@ def clean_name(name, *, replace="_"): such as writing to a file. All characters besides A-Z/a-z, 0-9 are replaced with "_" or the *replace* argument if defined. + :arg name: The path name. + :type name: string or bytes + :arg replace: The replacement for non-valid characters. + :type replace: string + :return: The cleaned name. + :rtype: string """ if replace != "_": @@ -207,8 +219,14 @@ def display_name(name, *, has_ext=True, title_case=True): Creates a display string from name to be used menus and the user interface. Intended for use with filenames and module names. - :arg has_ext: Remove file extension from name - :arg title_case: Convert lowercase names to title case + :arg name: The name to be used for displaying the user interface. + :type name: string + :arg has_ext: Remove file extension from name. + :type has_ext: boolean + :arg title_case: Convert lowercase names to title case. + :type title_case: boolean + :return: The display string. + :rtype: string """ if has_ext: @@ -233,6 +251,10 @@ def display_name_to_filepath(name): """ Performs the reverse of display_name using literal versions of characters which aren't supported in a filepath. + :arg name: The display name to convert. + :type name: string + :return: The file path. + :rtype: string """ for disp_value, file_value in _display_name_literals.items(): name = name.replace(disp_value, file_value) @@ -243,6 +265,10 @@ def display_name_from_filepath(name): """ Returns the path stripped of directory and extension, ensured to be utf8 compatible. + :arg name: The file path to convert. + :type name: string + :return: The display name. + :rtype: string """ name = _os.path.splitext(basename(name))[0] @@ -254,6 +280,10 @@ def resolve_ncase(path): """ Resolve a case insensitive path on a case sensitive system, returning a string with the path if found else return the original path. + :arg path: The path name to resolve. + :type path: string + :return: The resolved path. + :rtype: string """ def _ncase_path_found(path): @@ -315,11 +345,15 @@ def ensure_ext(filepath, ext, *, case_sensitive=False): """ Return the path with the extension added if it is not already set. + :arg filepath: The file path. + :type filepath: string :arg ext: The extension to check for, can be a compound extension. Should start with a dot, such as '.blend' or '.tar.gz'. :type ext: string :arg case_sensitive: Check for matching case when comparing extensions. - :type case_sensitive: bool + :type case_sensitive: boolean + :return: The file path with the given extension. + :rtype: string """ if case_sensitive: @@ -341,7 +375,7 @@ def module_names(path, *, recursive=False): :arg recursive: Also return submodule names for packages. :type recursive: bool :return: a list of string pairs (module_name, module_file). - :rtype: list + :rtype: list of strings """ from os.path import join, isfile @@ -374,6 +408,8 @@ def basename(path): Equivalent to ``os.path.basename``, but skips a "//" prefix. Use for Windows compatibility. + :return: The base name of the given path. + :rtype: string """ return _os.path.basename(path[2:] if path[:2] in {"//", b"//"} else path) @@ -381,6 +417,10 @@ def basename(path): def native_pathsep(path): """ Replace the path separator with the systems native ``os.sep``. + :arg path: The path to replace. + :type path: string + :return: The path with system native separators. + :rtype: string """ if type(path) is str: if _os.sep == "/": @@ -407,9 +447,9 @@ def reduce_dirs(dirs): (Useful for recursive path searching). :arg dirs: Sequence of directory paths. - :type dirs: sequence + :type dirs: sequence of strings :return: A unique list of paths. - :rtype: list + :rtype: list of strings """ dirs = list({_os.path.normpath(_os.path.abspath(d)) for d in dirs}) dirs.sort(key=lambda d: len(d)) diff --git a/source/blender/python/generic/bl_math_py_api.c b/source/blender/python/generic/bl_math_py_api.c index f17aaa4ca5d..5e938db0c35 100644 --- a/source/blender/python/generic/bl_math_py_api.c +++ b/source/blender/python/generic/bl_math_py_api.c @@ -77,14 +77,14 @@ static PyObject *py_bl_math_clamp(PyObject *UNUSED(self), PyObject *args) } PyDoc_STRVAR(py_bl_math_lerp_doc, - ".. function:: lerp(from, to, factor)\n" + ".. function:: lerp(from_value, to_value, factor)\n" "\n" " Linearly interpolate between two float values based on factor.\n" "\n" - " :arg from: The value to return when factor is 0.\n" - " :type from: float\n" - " :arg to: The value to return when factor is 1.\n" - " :type to: float\n" + " :arg from_value: The value to return when factor is 0.\n" + " :type from_value: float\n" + " :arg to_value: The value to return when factor is 1.\n" + " :type to_value: float\n" " :arg factor: The interpolation value, normally in [0.0, 1.0].\n" " :type factor: float\n" " :return: The interpolated value.\n" @@ -101,15 +101,15 @@ static PyObject *py_bl_math_lerp(PyObject *UNUSED(self), PyObject *args) PyDoc_STRVAR( py_bl_math_smoothstep_doc, - ".. function:: smoothstep(from, to, value)\n" + ".. function:: smoothstep(from_value, to_value, value)\n" "\n" - " Performs smooth interpolation between 0 and 1 as value changes between from and to.\n" + " Performs smooth interpolation between 0 and 1 as value changes between from and to values.\n" " Outside the range the function returns the same value as the nearest edge.\n" "\n" - " :arg from: The edge value where the result is 0.\n" - " :type from: float\n" - " :arg to: The edge value where the result is 1.\n" - " :type to: float\n" + " :arg from_value: The edge value where the result is 0.\n" + " :type from_value: float\n" + " :arg to_value: The edge value where the result is 1.\n" + " :type to_value: float\n" " :arg factor: The interpolation value.\n" " :type factor: float\n" " :return: The interpolated value in [0.0, 1.0].\n" diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c index 5d136921300..c034c31d828 100644 --- a/source/blender/python/gpu/gpu_py_texture.c +++ b/source/blender/python/gpu/gpu_py_texture.c @@ -536,7 +536,7 @@ PyDoc_STRVAR(pygpu_texture_from_image_doc, "premultiplied or straight alpha matching the image alpha mode.\n" "\n" " :arg image: The Image datablock.\n" - " :type image: `bpy.types.Image`\n" + " :type image: :class:`bpy.types.Image`\n" " :return: The GPUTexture used by the image.\n" " :rtype: :class:`gpu.types.GPUTexture`\n"); static PyObject *pygpu_texture_from_image(PyObject *UNUSED(self), PyObject *arg) diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 1296a6c174c..7b1877f3191 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -8786,7 +8786,7 @@ void pyrna_free_types(void) * - Should still be fixed - Campbell */ PyDoc_STRVAR(pyrna_register_class_doc, - ".. method:: register_class(cls)\n" + ".. function:: register_class(cls)\n" "\n" " Register a subclass of a Blender type class.\n" "\n" @@ -8971,7 +8971,7 @@ static int pyrna_srna_contains_pointer_prop_srna(StructRNA *srna_props, } PyDoc_STRVAR(pyrna_unregister_class_doc, - ".. method:: unregister_class(cls)\n" + ".. function:: unregister_class(cls)\n" "\n" " Unload the Python class from blender.\n" "\n" diff --git a/source/blender/python/mathutils/mathutils_noise.c b/source/blender/python/mathutils/mathutils_noise.c index 69d37b345c6..d5e27496af8 100644 --- a/source/blender/python/mathutils/mathutils_noise.c +++ b/source/blender/python/mathutils/mathutils_noise.c @@ -562,7 +562,7 @@ PyDoc_STRVAR(M_Noise_turbulence_vector_doc, " :type octaves: int\n" " :arg hard: Specifies whether returned turbulence is hard (sharp transitions) or " "soft (smooth transitions).\n" - " :type hard: :boolean\n" BPY_NOISE_BASIS_ENUM_DOC + " :type hard: boolean\n" BPY_NOISE_BASIS_ENUM_DOC " :arg amplitude_scale: The amplitude scaling factor.\n" " :type amplitude_scale: float\n" " :arg frequency_scale: The frequency scaling factor\n" From 231d66d8ba355cb7fa5332e9ea7d8921bbde82fe Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 22 Oct 2021 17:32:21 -0500 Subject: [PATCH 1101/1500] Fix: Support swapped inputs properly in trim curve node Previously, when the start input was greater than the end input, the spline was resized to a single point. That is correct, but the single point wasn't placed properly along the spline. Now, it is placed according to the "Start" value, as if the trim started, but couldn't continue because the "End" value was smaller. This behavior is handled with a separate code path to keep each simpler and avoid special cases. Any cleanup to reduce duplication should focus on making each code path shorter separately rather than merging them. Also included are some changes to `lookup_control_point_position` to support cyclic splines, though this single-point method is still disabled on cyclic splines for consistency. --- .../geometry/nodes/node_geo_curve_trim.cc | 215 ++++++++++++++---- 1 file changed, 173 insertions(+), 42 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 82e2d7ad283..6b049b4d384 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -132,19 +132,19 @@ static void linear_trim_to_output_data(const TrimLocation &start, /* Look up the control points to the left and right of factor, and get the factor between them. */ static TrimLocation lookup_control_point_position(const Spline::LookupResult &lookup, - Span control_point_offsets) + const BezierSpline &spline) { - const int *left_offset = std::lower_bound( - control_point_offsets.begin(), control_point_offsets.end(), lookup.evaluated_index); - const int index = left_offset - control_point_offsets.begin(); - const int left = control_point_offsets[index] > lookup.evaluated_index ? index - 1 : index; - const int right = left + 1; + Span offsets = spline.control_point_offsets(); - const float factor = std::clamp( - (lookup.evaluated_index + lookup.factor - control_point_offsets[left]) / - (control_point_offsets[right] - control_point_offsets[left]), - 0.0f, - 1.0f); + const int *offset = std::lower_bound(offsets.begin(), offsets.end(), lookup.evaluated_index); + const int index = offset - offsets.begin(); + + const int left = offsets[index] > lookup.evaluated_index ? index - 1 : index; + const int right = left == (spline.size() - 1) ? 0 : left + 1; + + const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left]; + const int segment_eval_size = offsets[left + 1] - offsets[left]; + const float factor = std::clamp(offset_in_segment / segment_eval_size, 0.0f, 1.0f); return {left, right, factor}; } @@ -244,10 +244,11 @@ static void trim_bezier_spline(Spline &spline, const Spline::LookupResult &end_lookup) { BezierSpline &bezier_spline = static_cast(spline); - Span control_offsets = bezier_spline.control_point_offsets(); - const TrimLocation start = lookup_control_point_position(start_lookup, control_offsets); - TrimLocation end = lookup_control_point_position(end_lookup, control_offsets); + const TrimLocation start = lookup_control_point_position(start_lookup, bezier_spline); + TrimLocation end = lookup_control_point_position(end_lookup, bezier_spline); + + const Span control_offsets = bezier_spline.control_point_offsets(); /* The number of control points in the resulting spline. */ const int size = end.right_index - start.left_index + 1; @@ -329,6 +330,133 @@ static void trim_bezier_spline(Spline &spline, bezier_spline.resize(size); } +static void trim_spline(SplinePtr &spline, + const Spline::LookupResult start, + const Spline::LookupResult end) +{ + switch (spline->type()) { + case Spline::Type::Bezier: + trim_bezier_spline(*spline, start, end); + break; + case Spline::Type::Poly: + trim_poly_spline(*spline, start, end); + break; + case Spline::Type::NURBS: + spline = std::make_unique(trim_nurbs_spline(*spline, start, end)); + break; + } + spline->mark_cache_invalid(); +} + +template +static void to_single_point_data(const TrimLocation &trim, MutableSpan data) +{ + data.first() = mix2(trim.factor, data[trim.left_index], data[trim.right_index]); +} +template +static void to_single_point_data(const TrimLocation &trim, Span src, MutableSpan dst) +{ + dst.first() = mix2(trim.factor, src[trim.left_index], src[trim.right_index]); +} + +static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &lookup) +{ + BezierSpline &bezier = static_cast(spline); + + const TrimLocation trim = lookup_control_point_position(lookup, bezier); + + const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion( + trim.left_index, trim.right_index, trim.factor); + bezier.positions().first() = new_point.position; + bezier.handle_types_left().first() = BezierSpline::HandleType::Free; + bezier.handle_types_right().first() = BezierSpline::HandleType::Free; + bezier.handle_positions_left().first() = new_point.left_handle; + bezier.handle_positions_right().first() = new_point.right_handle; + + to_single_point_data(trim, bezier.radii()); + to_single_point_data(trim, bezier.tilts()); + spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + std::optional data = spline.attributes.get_for_write(attribute_id); + attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { + using T = decltype(dummy); + to_single_point_data(trim, data->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + spline.resize(1); +} + +static void to_single_point_poly(Spline &spline, const Spline::LookupResult &lookup) +{ + const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; + + to_single_point_data(trim, spline.positions()); + to_single_point_data(trim, spline.radii()); + to_single_point_data(trim, spline.tilts()); + spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + std::optional data = spline.attributes.get_for_write(attribute_id); + attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { + using T = decltype(dummy); + to_single_point_data(trim, data->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + spline.resize(1); +} + +static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::LookupResult &lookup) +{ + /* Since this outputs a poly spline, the evaluated indices are the control point indices. */ + const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; + + /* Create poly spline and copy trimmed data to it. */ + PolySpline new_spline; + new_spline.resize(1); + + spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + new_spline.attributes.create(attribute_id, meta_data.data_type); + std::optional src = spline.attributes.get_for_read(attribute_id); + std::optional dst = new_spline.attributes.get_for_write(attribute_id); + attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed eval_data = spline.interpolate_to_evaluated(src->typed()); + to_single_point_data(trim, eval_data->get_internal_span(), dst->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + to_single_point_data(trim, spline.evaluated_positions(), new_spline.positions()); + + GVArray_Typed evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); + to_single_point_data(trim, evaluated_radii->get_internal_span(), new_spline.radii()); + + GVArray_Typed evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); + to_single_point_data(trim, evaluated_tilts->get_internal_span(), new_spline.tilts()); + + return new_spline; +} + +static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup) +{ + switch (spline->type()) { + case Spline::Type::Bezier: + to_single_point_bezier(*spline, lookup); + break; + case Spline::Type::Poly: + to_single_point_poly(*spline, lookup); + break; + case Spline::Type::NURBS: + spline = std::make_unique(to_single_point_nurbs(*spline, lookup)); + break; + } +} + static void geometry_set_curve_trim(GeometrySet &geometry_set, const GeometryNodeCurveSampleMode mode, Field &start_field, @@ -354,46 +482,49 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - Spline &spline = *splines[i]; + SplinePtr &spline = splines[i]; - /* Currently this node doesn't support cyclic splines, it could in the future though. */ - if (spline.is_cyclic()) { + /* Currently trimming cyclic splines is not supported. It could be in the future though. */ + if (spline->is_cyclic()) { continue; } - if (spline.evaluated_edges_size() == 0) { + if (spline->evaluated_edges_size() == 0) { continue; } - /* Return a spline with one point instead of implicitly - * reversing the spline or switching the parameters. */ - if (ends[i] < starts[i]) { - spline.resize(1); + const float length = spline->length(); + if (length == 0.0f) { continue; } - const Spline::LookupResult start_lookup = - (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) ? - spline.lookup_evaluated_length(std::clamp(starts[i], 0.0f, spline.length())) : - spline.lookup_evaluated_factor(std::clamp(starts[i], 0.0f, 1.0f)); - const Spline::LookupResult end_lookup = - (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) ? - spline.lookup_evaluated_length(std::clamp(ends[i], 0.0f, spline.length())) : - spline.lookup_evaluated_factor(std::clamp(ends[i], 0.0f, 1.0f)); + const float start = starts[i]; + const float end = ends[i]; - switch (spline.type()) { - case Spline::Type::Bezier: - trim_bezier_spline(spline, start_lookup, end_lookup); - break; - case Spline::Type::Poly: - trim_poly_spline(spline, start_lookup, end_lookup); - break; - case Spline::Type::NURBS: - splines[i] = std::make_unique( - trim_nurbs_spline(spline, start_lookup, end_lookup)); - break; + /* When the start and end samples are reversed, instead of implicitly reversing the spline + * or switching the parameters, create a single point spline with the end sample point. */ + if (end <= start) { + if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + to_single_point_spline(spline, + spline->lookup_evaluated_length(std::clamp(start, 0.0f, length))); + } + else { + to_single_point_spline(spline, + spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f))); + } + continue; + } + + if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + trim_spline(spline, + spline->lookup_evaluated_length(std::clamp(start, 0.0f, length)), + spline->lookup_evaluated_length(std::clamp(end, 0.0f, length))); + } + else { + trim_spline(spline, + spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)), + spline->lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f))); } - splines[i]->mark_cache_invalid(); } }); } From 2a7a8a04e37b3892b3497529e9f7c4c9f8f36f4a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 22 Oct 2021 22:35:27 -0500 Subject: [PATCH 1102/1500] Fix: Empty geometry nodes input menu with legacy nodes enabled Caused by a misplaced comma at the end of a line in Python. --- release/scripts/startup/nodeitems_builtins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 418311cf27e..c568eef1d95 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -168,7 +168,7 @@ def geometry_input_node_items(context): return if geometry_nodes_legacy_poll(context): - yield NodeItem("FunctionNodeLegacyRandomFloat"), + yield NodeItem("FunctionNodeLegacyRandomFloat") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeCollectionInfo") From 3af597d16b1ed2887df23cc621f0bf7f2b6d4091 Mon Sep 17 00:00:00 2001 From: Jarrett Johnson Date: Sat, 23 Oct 2021 00:01:59 -0500 Subject: [PATCH 1103/1500] Geometry Nodes: Add Instances to Points Node This node takes a geometry set with instances as input and outputs points located on the origins of the top level of instances in the geometry set (not nested instances). It also has position and radius inputs to allow overriding the default, and a selection input to only generate points for some instances. The use case for this node is a method to use geometry proximity on instance origins, but in a more generic way that is flexible and useful in other situations. Differential Revision: https://developer.blender.org/D12893 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_geo_instances_to_points.cc | 117 ++++++++++++++++++ 7 files changed, 123 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index c568eef1d95..4d227be50ad 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -695,6 +695,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items), GeometryNodeCategory("GEO_INSTANCE", "Instances", items=[ NodeItem("GeometryNodeInstanceOnPoints"), + NodeItem("GeometryNodeInstancesToPoints"), NodeItem("GeometryNodeRealizeInstances"), NodeItem("GeometryNodeRotateInstances"), NodeItem("GeometryNodeScaleInstances"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 7a9c23d3f54..1280b83b765 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1546,6 +1546,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_ENDPOINT_SELECTION 1127 #define GEO_NODE_RAYCAST 1128 #define GEO_NODE_CURVE_TO_POINTS 1130 +#define GEO_NODE_INSTANCES_TO_POINTS 1131 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index e97b309f75b..bd0f0df6e02 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5814,6 +5814,7 @@ static void registerGeometryNodes() register_node_type_geo_input_spline_resolution(); register_node_type_geo_input_tangent(); register_node_type_geo_instance_on_points(); + register_node_type_geo_instances_to_points(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); register_node_type_geo_material_replace(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 3e62fcb47f5..6c6ce821d0e 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -240,6 +240,7 @@ set(SRC geometry/nodes/node_geo_input_spline_resolution.cc geometry/nodes/node_geo_input_tangent.cc geometry/nodes/node_geo_instance_on_points.cc + geometry/nodes/node_geo_instances_to_points.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc geometry/nodes/node_geo_material_replace.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 890825fdcc0..a37dcf28757 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -108,6 +108,7 @@ void register_node_type_geo_input_spline_length(void); void register_node_type_geo_input_spline_resolution(void); void register_node_type_geo_input_tangent(void); void register_node_type_geo_instance_on_points(void); +void register_node_type_geo_instances_to_points(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); void register_node_type_geo_material_replace(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 843d414f426..e966839ab27 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -368,6 +368,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLe DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_RESOLUTION, 0, "INPUT_SPLINE_RESOLUTION", InputSplineResolution, "Spline Resolution", "") DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "") DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") +DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", InstancesToPoints, "Instances to Points", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc new file mode 100644 index 00000000000..7f9308d43ad --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -0,0 +1,117 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_pointcloud.h" +#include "DNA_pointcloud_types.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_instances_to_points_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Instances"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("Position").implicit_field(); + b.add_input("Radius") + .default_value(0.05f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_output("Points"); +} + +template +static void copy_attribute_to_points(const VArray &src, + const IndexMask mask, + MutableSpan dst) +{ + for (const int i : mask.index_range()) { + dst[i] = src[mask[i]]; + } +} + +static void convert_instances_to_points(GeometrySet &geometry_set, + Field position_field, + Field radius_field, + const Field selection_field) +{ + const InstancesComponent &instances = *geometry_set.get_component_for_read(); + + const AttributeDomain attribute_domain = ATTR_DOMAIN_POINT; + GeometryComponentFieldContext field_context{instances, attribute_domain}; + const int domain_size = instances.attribute_domain_size(attribute_domain); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(std::move(selection_field)); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + if (selection.is_empty()) { + return; + } + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); + geometry_set.replace_pointcloud(pointcloud); + + PointCloudComponent &points = geometry_set.get_component_for_write(); + + fn::FieldEvaluator evaluator{field_context, &selection}; + evaluator.add(std::move(position_field)); + evaluator.add(std::move(radius_field)); + evaluator.evaluate(); + const VArray &positions = evaluator.get_evaluated(0); + copy_attribute_to_points(positions, selection, {(float3 *)pointcloud->co, pointcloud->totpoint}); + const VArray &radii = evaluator.get_evaluated(1); + copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint}); + + OutputAttribute_Typed id_attribute = points.attribute_try_get_for_output( + "id", ATTR_DOMAIN_POINT, 0); + MutableSpan ids = id_attribute.as_span(); + for (const int64_t i : selection) { + ids[i] = instances.instance_ids()[i]; + } + id_attribute.save(); +} + +static void geo_node_instances_to_points_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Instances"); + + if (geometry_set.has_instances()) { + convert_instances_to_points(geometry_set, + params.extract_input>("Position"), + params.extract_input>("Radius"), + params.extract_input>("Selection")); + geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD}); + params.set_output("Points", std::move(geometry_set)); + } + else { + params.set_output("Points", GeometrySet()); + } +} + +} // namespace blender::nodes + +void register_node_type_geo_instances_to_points() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INSTANCES_TO_POINTS, "Instances to Points", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_instances_to_points_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_instances_to_points_exec; + nodeRegisterType(&ntype); +} From 3bcd264141fda53529e9c11f98f9fc28b548b79c Mon Sep 17 00:00:00 2001 From: Ankit Meel Date: Sat, 23 Oct 2021 10:49:51 +0530 Subject: [PATCH 1104/1500] GitHub: add PR template Differential Revision: https://developer.blender.org/D12890 --- .github/pull_request_template.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..93a29565e54 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ +This repository is only used as a mirror of git.blender.org. Blender development happens on +https://developer.blender.org. + +To get started with contributing code, please see: +https://wiki.blender.org/wiki/Process/Contributing_Code From 50e7645211fb023280c6c55147fe15edcadddc17 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sat, 23 Oct 2021 00:42:07 -0500 Subject: [PATCH 1105/1500] Fix: Asset using selection in instances to points node Caused by my own incorrect cleanup when committing the patch. --- .../nodes/geometry/nodes/node_geo_instances_to_points.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index 7f9308d43ad..63d1f88a442 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -80,8 +80,8 @@ static void convert_instances_to_points(GeometrySet &geometry_set, OutputAttribute_Typed id_attribute = points.attribute_try_get_for_output( "id", ATTR_DOMAIN_POINT, 0); MutableSpan ids = id_attribute.as_span(); - for (const int64_t i : selection) { - ids[i] = instances.instance_ids()[i]; + for (const int i : selection.index_range()) { + ids[i] = instances.instance_ids()[selection[i]]; } id_attribute.save(); } From 9ad642c59ade21d8664ba968d317b062e9654c07 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sat, 23 Oct 2021 18:07:48 +0200 Subject: [PATCH 1106/1500] Assets/UI: Improve asset library Preferences UI * Open File Browser when pressing "Add Asset Library". This just makes sense, since users have to select a directory for the asset library anyway. * Move '+' icon back to the right side of the box. Then it is right under the 'x' icons for each indivdual library, which seems like the more natural place. * Correct tooltip for the "Add Asset Library" operator. * Mark empty asset library name or paths field in red, to make clear that these need to be set. --- .../scripts/startup/bl_ui/space_userpref.py | 12 ++++-- .../editors/space_userpref/userpref_ops.c | 37 +++++++++++++++++-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index bb3b3fbc23b..abd235f1c44 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1410,12 +1410,18 @@ class USERPREF_PT_file_paths_asset_libraries(FilePathsPanel, Panel): row.label(text="Path") for i, library in enumerate(paths.asset_libraries): - name_col.prop(library, "name", text="") + row = name_col.row() + row.alert = not library.name + row.prop(library, "name", text="") + row = path_col.row() - row.prop(library, "path", text="") + subrow = row.row() + subrow.alert = not library.path + subrow.prop(library, "path", text="") row.operator("preferences.asset_library_remove", text="", icon='X', emboss=False).index = i + row = box.row() - row.alignment = 'LEFT' + row.alignment = 'RIGHT' row.operator("preferences.asset_library_add", text="", icon='ADD', emboss=False) diff --git a/source/blender/editors/space_userpref/userpref_ops.c b/source/blender/editors/space_userpref/userpref_ops.c index 63506678b70..d40229332fd 100644 --- a/source/blender/editors/space_userpref/userpref_ops.c +++ b/source/blender/editors/space_userpref/userpref_ops.c @@ -24,6 +24,7 @@ #include #include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "BLI_listbase.h" #ifdef WIN32 @@ -139,23 +140,51 @@ static void PREFERENCES_OT_autoexec_path_remove(wmOperatorType *ot) /** \name Add Asset Library Operator * \{ */ -static int preferences_asset_library_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +static int preferences_asset_library_add_exec(bContext *UNUSED(C), wmOperator *op) { - BKE_preferences_asset_library_add(&U, NULL, NULL); + char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL); + + /* NULL is a valid directory path here. A library without path will be created then. */ + BKE_preferences_asset_library_add(&U, NULL, directory); U.runtime.is_dirty = true; + + /* There's no dedicated notifier for the Preferences. */ + WM_main_add_notifier(NC_WINDOW, NULL); + + MEM_freeN(directory); return OPERATOR_FINISHED; } +static int preferences_asset_library_add_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + if (!RNA_struct_property_is_set(op->ptr, "directory")) { + WM_event_add_fileselect(C, op); + return OPERATOR_RUNNING_MODAL; + } + + return preferences_asset_library_add_exec(C, op); +} + static void PREFERENCES_OT_asset_library_add(wmOperatorType *ot) { ot->name = "Add Asset Library"; ot->idname = "PREFERENCES_OT_asset_library_add"; - ot->description = - "Add a path to a .blend file to be used by the Asset Browser as source of assets"; + ot->description = "Add a directory to be used by the Asset Browser as source of assets"; ot->exec = preferences_asset_library_add_exec; + ot->invoke = preferences_asset_library_add_invoke; ot->flag = OPTYPE_INTERNAL; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER, + FILE_SPECIAL, + FILE_OPENFILE, + WM_FILESEL_DIRECTORY, + FILE_DEFAULTDISPLAY, + FILE_SORT_DEFAULT); } /** \} */ From 7a0cad0989d8162787896d6235f2e4d3064573ad Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sat, 23 Oct 2021 12:31:48 -0500 Subject: [PATCH 1107/1500] Fix: Add missing TBB define to geometry module --- source/blender/geometry/CMakeLists.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 4e7e0b1ea58..03f736d4dde 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -39,4 +39,16 @@ set(LIB bf_blenlib ) +if(WITH_TBB) + add_definitions(-DWITH_TBB) + + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${TBB_LIBRARIES} + ) +endif() + blender_add_lib(bf_geometry "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") From dd505a7ebba21a1823592be7e603c7d94188f4b7 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sat, 23 Oct 2021 20:35:15 +0200 Subject: [PATCH 1108/1500] Fix T92400: Denoise node Prefilter output is always "Fast" Caused by an accidental renaming in {rB1c42d4930a24d639b3aa561b9a8b4bbc} --- source/blender/compositor/operations/COM_DenoiseOperation.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index 9b9670c6f06..14822e9a49c 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -244,7 +244,7 @@ void DenoiseOperation::generate_denoise(MemoryBuffer *output, if (settings) { filter.set("hdr", settings->hdr); filter.set("srgb", false); - filter.set("clean_aux", are_guiding_passes_noise_free(settings)); + filter.set("cleanAux", are_guiding_passes_noise_free(settings)); } filter.execute(); From a3d0b50d75030dd3d75056dd6722403ad5258a5d Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sat, 23 Oct 2021 20:52:30 +0200 Subject: [PATCH 1109/1500] Cleanup: use underscore suffix for private data members --- .../operations/COM_DenoiseOperation.cc | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index 14822e9a49c..0174c50450c 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -44,8 +44,8 @@ bool COM_is_denoise_supported() class DenoiseFilter { private: #ifdef WITH_OPENIMAGEDENOISE - oidn::DeviceRef device; - oidn::FilterRef filter; + oidn::DeviceRef device_; + oidn::FilterRef filter_; #endif bool initialized_ = false; @@ -63,10 +63,10 @@ class DenoiseFilter { * nonetheless. */ BLI_mutex_lock(&oidn_lock); - device = oidn::newDevice(); - device.set("setAffinity", false); - device.commit(); - filter = device.newFilter("RT"); + device_ = oidn::newDevice(); + device_.set("setAffinity", false); + device_.commit(); + filter_ = device_.newFilter("RT"); initialized_ = true; set_image("output", output); } @@ -81,26 +81,26 @@ class DenoiseFilter { { BLI_assert(initialized_); BLI_assert(!buffer->is_a_single_elem()); - filter.setImage(name.data(), - buffer->get_buffer(), - oidn::Format::Float3, - buffer->get_width(), - buffer->get_height(), - 0, - buffer->get_elem_bytes_len()); + filter_.setImage(name.data(), + buffer->get_buffer(), + oidn::Format::Float3, + buffer->get_width(), + buffer->get_height(), + 0, + buffer->get_elem_bytes_len()); } template void set(const StringRef option_name, T value) { BLI_assert(initialized_); - filter.set(option_name.data(), value); + filter_.set(option_name.data(), value); } void execute() { BLI_assert(initialized_); - filter.commit(); - filter.execute(); + filter_.commit(); + filter_.execute(); } #else From 8c21667f3ca7c2cf58a9053dc31215a0d0688ad5 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sat, 23 Oct 2021 21:03:25 +0200 Subject: [PATCH 1110/1500] Cleanup: remove unused function It doesn't have definition either. --- source/blender/compositor/intern/COM_ExecutionGroup.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.h b/source/blender/compositor/intern/COM_ExecutionGroup.h index 6d1520f9c24..ce5e9dd4699 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.h +++ b/source/blender/compositor/intern/COM_ExecutionGroup.h @@ -320,14 +320,6 @@ class ExecutionGroup { */ void init_execution(); - /** - * \brief get all inputbuffers needed to calculate an chunk - * \note all inputbuffers must be executed - * \param chunk_number: the chunk to be calculated - * \return (MemoryBuffer **) the inputbuffers - */ - MemoryBuffer **getInputBuffersCPU(); - /** * \brief get all inputbuffers needed to calculate an chunk * \note all inputbuffers must be executed From dc2524eaaeb121aa48c5b81de67d3ede8a319123 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Sun, 24 Oct 2021 11:19:10 +0200 Subject: [PATCH 1111/1500] Geometry Nodes: Rename node "String Substring" This patch renames the node "String Substring" to "Slice String" to conform to the "verb first" naming convention. Default length is also changed to 10 to make it easier for users to understand what the node does. Reviewed By: HooglyBoogly Differential Revision: https://developer.blender.org/D12931 --- release/scripts/startup/nodeitems_builtins.py | 2 +- source/blender/blenkernel/BKE_node.h | 2 +- source/blender/blenkernel/intern/node.cc | 2 +- .../blenloader/intern/versioning_300.c | 8 ++++++++ source/blender/nodes/CMakeLists.txt | 2 +- source/blender/nodes/NOD_function.h | 2 +- source/blender/nodes/NOD_static_types.h | 2 +- ...g_substring.cc => node_fn_slice_string.cc} | 20 +++++++++---------- 8 files changed, 24 insertions(+), 16 deletions(-) rename source/blender/nodes/function/nodes/{node_fn_string_substring.cc => node_fn_slice_string.cc} (65%) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 4d227be50ad..0a34f541e5c 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -719,7 +719,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_POINT", "Point", items=point_node_items), GeometryNodeCategory("GEO_TEXT", "Text", items=[ NodeItem("FunctionNodeStringLength"), - NodeItem("FunctionNodeStringSubstring"), + NodeItem("FunctionNodeSliceString"), NodeItem("FunctionNodeValueToString"), NodeItem("GeometryNodeStringJoin"), NodeItem("FunctionNodeInputSpecialCharacters"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 1280b83b765..ef1e7249658 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1562,7 +1562,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_FLOAT_TO_INT 1209 #define FN_NODE_VALUE_TO_STRING 1210 #define FN_NODE_STRING_LENGTH 1211 -#define FN_NODE_STRING_SUBSTRING 1212 +#define FN_NODE_SLICE_STRING 1212 #define FN_NODE_INPUT_SPECIAL_CHARACTERS 1213 #define FN_NODE_RANDOM_VALUE 1214 #define FN_NODE_ROTATE_EULER 1215 diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index bd0f0df6e02..eda57d9e984 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5886,8 +5886,8 @@ static void registerFunctionNodes() register_node_type_fn_random_value(); register_node_type_fn_replace_string(); register_node_type_fn_rotate_euler(); + register_node_type_fn_slice_string(); register_node_type_fn_string_length(); - register_node_type_fn_string_substring(); register_node_type_fn_value_to_string(); } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index c68bae1d4af..8168b917b5e 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2030,7 +2030,15 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) * * \note Keep this message at the bottom of the function. */ + { + /* Update the `idnames` for renamed geometry and function nodes. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString"); + } /* Keep this block, even when empty. */ } } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 6c6ce821d0e..7a728b9041b 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -149,8 +149,8 @@ set(SRC function/nodes/node_fn_random_value.cc function/nodes/node_fn_replace_string.cc function/nodes/node_fn_rotate_euler.cc + function/nodes/node_fn_slice_string.cc function/nodes/node_fn_string_length.cc - function/nodes/node_fn_string_substring.cc function/nodes/node_fn_value_to_string.cc function/node_function_util.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 78069a2fade..81f0667fe1c 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -35,8 +35,8 @@ void register_node_type_fn_input_vector(void); void register_node_type_fn_random_value(void); void register_node_type_fn_replace_string(void); void register_node_type_fn_rotate_euler(void); +void register_node_type_fn_slice_string(void); void register_node_type_fn_string_length(void); -void register_node_type_fn_string_substring(void); void register_node_type_fn_value_to_string(void); #ifdef __cplusplus diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index e966839ab27..af14538e6cc 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -278,8 +278,8 @@ DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "") DefNode(FunctionNode, FN_NODE_REPLACE_STRING, 0, "REPLACE_STRING", ReplaceString, "Replace String", "") DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "") +DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Slice String", "") DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") -DefNode(FunctionNode, FN_NODE_STRING_SUBSTRING, 0, "STRING_SUBSTRING", StringSubstring, "String Substring", "") DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") diff --git a/source/blender/nodes/function/nodes/node_fn_string_substring.cc b/source/blender/nodes/function/nodes/node_fn_slice_string.cc similarity index 65% rename from source/blender/nodes/function/nodes/node_fn_string_substring.cc rename to source/blender/nodes/function/nodes/node_fn_slice_string.cc index 0f91b1af813..08e17da0d92 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_substring.cc +++ b/source/blender/nodes/function/nodes/node_fn_slice_string.cc @@ -20,34 +20,34 @@ namespace blender::nodes { -static void fn_node_string_substring_declare(NodeDeclarationBuilder &b) +static void fn_node_slice_string_declare(NodeDeclarationBuilder &b) { b.add_input("String"); b.add_input("Position"); - b.add_input("Length").min(0); + b.add_input("Length").min(0).default_value(10); b.add_output("String"); }; -static void fn_node_string_substring_build_multi_function(NodeMultiFunctionBuilder &builder) +static void fn_node_slice_string_build_multi_function(NodeMultiFunctionBuilder &builder) { - static fn::CustomMF_SI_SI_SI_SO substring_fn{ - "Substring", [](const std::string &str, int a, int b) { + static blender::fn::CustomMF_SI_SI_SI_SO slice_fn{ + "Slice", [](const std::string &str, int a, int b) { const int len = BLI_strlen_utf8(str.c_str()); const int start = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a, 0, len)); const int end = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a + b, 0, len)); return str.substr(start, std::max(end - start, 0)); }}; - builder.set_matching_fn(&substring_fn); + builder.set_matching_fn(&slice_fn); } } // namespace blender::nodes -void register_node_type_fn_string_substring() +void register_node_type_fn_slice_string() { static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_STRING_SUBSTRING, "String Substring", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::fn_node_string_substring_declare; - ntype.build_multi_function = blender::nodes::fn_node_string_substring_build_multi_function; + fn_node_type_base(&ntype, FN_NODE_SLICE_STRING, "Slice String", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_slice_string_declare; + ntype.build_multi_function = blender::nodes::fn_node_slice_string_build_multi_function; nodeRegisterType(&ntype); } From e288e392a8c3100cb60f6b7659323905e47c72b8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 24 Oct 2021 19:12:34 +1100 Subject: [PATCH 1112/1500] Cleanup: line length in Python scripts --- .../bl_i18n_utils/bl_extract_messages.py | 9 +++- .../bl_previews_utils/bl_previews_render.py | 51 ++++++++++++++----- release/scripts/startup/bl_operators/wm.py | 5 +- .../startup/bl_ui/properties_physics_fluid.py | 14 ++++- .../bl_ui/properties_physics_rigidbody.py | 18 +++++-- .../startup/bl_ui/space_filebrowser.py | 5 +- release/scripts/startup/bl_ui/space_node.py | 12 ++++- 7 files changed, 90 insertions(+), 24 deletions(-) diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 6c9c212387e..49dbac1d502 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -743,7 +743,9 @@ def dump_src_messages(msgs, reports, settings): def clean_str(s): # The encode/decode to/from 'raw_unicode_escape' allows to transform the C-type unicode hexadecimal escapes # (like '\u2715' for the '×' symbol) back into a proper unicode character. - return "".join(m.group("clean") for m in _clean_str(s)).encode('raw_unicode_escape').decode('raw_unicode_escape') + return "".join( + m.group("clean") for m in _clean_str(s) + ).encode('raw_unicode_escape').decode('raw_unicode_escape') def dump_src_file(path, rel_path, msgs, reports, settings): def process_entry(_msgctxt, _msgid): @@ -870,7 +872,10 @@ def dump_messages(do_messages, do_checks, settings): dump_src_messages(msgs, reports, settings) # Get strings from addons' categories. - for uid, label, tip in bpy.types.WindowManager.addon_filter.keywords['items'](bpy.context.window_manager, bpy.context): + for uid, label, tip in bpy.types.WindowManager.addon_filter.keywords['items']( + bpy.context.window_manager, + bpy.context, + ): process_msg(msgs, settings.DEFAULT_CONTEXT, label, "Add-ons' categories", reports, None, settings) if tip: process_msg(msgs, settings.DEFAULT_CONTEXT, tip, "Add-ons' categories", reports, None, settings) diff --git a/release/scripts/modules/bl_previews_utils/bl_previews_render.py b/release/scripts/modules/bl_previews_utils/bl_previews_render.py index fa3355bd6d5..0b3b3a4e406 100644 --- a/release/scripts/modules/bl_previews_utils/bl_previews_render.py +++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py @@ -483,19 +483,44 @@ def main(): argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] parser = argparse.ArgumentParser( - description="Use Blender to generate previews for currently open Blender file's items.") - parser.add_argument('--clear', default=False, action="store_true", - help="Clear previews instead of generating them.") - parser.add_argument('--no_backups', default=False, action="store_true", - help="Do not generate a backup .blend1 file when saving processed ones.") - parser.add_argument('--no_scenes', default=True, action="store_false", - help="Do not generate/clear previews for scene IDs.") - parser.add_argument('--no_collections', default=True, action="store_false", - help="Do not generate/clear previews for collection IDs.") - parser.add_argument('--no_objects', default=True, action="store_false", - help="Do not generate/clear previews for object IDs.") - parser.add_argument('--no_data_intern', default=True, action="store_false", - help="Do not generate/clear previews for mat/tex/image/etc. IDs (those handled by core Blender code).") + description="Use Blender to generate previews for currently open Blender file's items.", + ) + parser.add_argument( + '--clear', + default=False, + action="store_true", + help="Clear previews instead of generating them.", + ) + parser.add_argument( + '--no_backups', + default=False, + action="store_true", + help="Do not generate a backup .blend1 file when saving processed ones.", + ) + parser.add_argument( + '--no_scenes', + default=True, + action="store_false", + help="Do not generate/clear previews for scene IDs.", + ) + parser.add_argument( + '--no_collections', + default=True, + action="store_false", + help="Do not generate/clear previews for collection IDs.", + ) + parser.add_argument( + '--no_objects', + default=True, + action="store_false", + help="Do not generate/clear previews for object IDs.", + ) + parser.add_argument( + '--no_data_intern', + default=True, + action="store_false", + help="Do not generate/clear previews for mat/tex/image/etc. IDs (those handled by core Blender code).", + ) args = parser.parse_args(argv) orig_save_version = bpy.context.preferences.filepaths.save_version diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index b011711d133..170b9f3ae44 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1327,7 +1327,10 @@ class WM_OT_properties_edit(Operator): use_soft_limits: BoolProperty( name="Use Soft Limits", - description="Limits the Property Value slider to a range, values outside the range must be inputted numerically", + description=( + "Limits the Property Value slider to a range, " + "values outside the range must be inputted numerically" + ), ) array_length: IntProperty( name="Array Length", diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index 6408d4096fe..47f0b399b66 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -227,7 +227,12 @@ class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): split.enabled = note_flag and ob.mode == 'OBJECT' bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end) - if domain.cache_resumable and domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete: + if ( + domain.cache_resumable and + domain.has_cache_baked_data and + not domain.is_cache_baking_data and + bake_incomplete + ): col = split.column() col.operator("fluid.bake_data", text="Resume") col = split.column() @@ -1249,7 +1254,12 @@ class PHYSICS_PT_cache(PhysicButtonsPanel, Panel): split.enabled = ob.mode == 'OBJECT' bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end) - if domain.cache_resumable and domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete: + if ( + domain.cache_resumable and + domain.has_cache_baked_data and + not domain.is_cache_baking_data and + bake_incomplete + ): col = split.column() col.operator("fluid.bake_all", text="Resume") col = split.column() diff --git a/release/scripts/startup/bl_ui/properties_physics_rigidbody.py b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py index a55bd89ca18..26fe215b17d 100644 --- a/release/scripts/startup/bl_ui/properties_physics_rigidbody.py +++ b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py @@ -109,7 +109,11 @@ class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel): @classmethod def poll(cls, context): obj = context.object - if obj.parent is not None and obj.parent.rigid_body is not None and not obj.parent.rigid_body.collision_shape == 'COMPOUND': + if ( + (obj.parent is not None) and + (obj.parent.rigid_body is not None) and + (not obj.parent.rigid_body.collision_shape == 'COMPOUND') + ): return False return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES)) @@ -124,7 +128,11 @@ class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel): layout.prop(rbo, "collision_shape", text="Shape") if rbo.collision_shape == 'COMPOUND': - if parent is not None and parent.rigid_body is not None and parent.rigid_body.collision_shape == 'COMPOUND': + if ( + (parent is not None) and + (parent.rigid_body is not None) and + (parent.rigid_body.collision_shape == 'COMPOUND') + ): rigid_body_warning(layout, "Sub compound shapes are not allowed") else: found = False @@ -179,7 +187,11 @@ class PHYSICS_PT_rigid_body_collisions_sensitivity(PHYSICS_PT_rigidbody_panel, P @classmethod def poll(cls, context): obj = context.object - if obj.parent is not None and obj.parent.rigid_body is not None and not obj.parent.rigid_body.collision_shape == 'COMPOUND': + if ( + (obj.parent is not None) and + (obj.parent.rigid_body is not None) and + (not obj.parent.rigid_body.collision_shape == 'COMPOUND') + ): return False return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES)) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 62a9343f644..927a30f0ae0 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -614,7 +614,10 @@ class ASSETBROWSER_PT_filter(asset_utils.AssetBrowserPanel, Panel): filter_id = params.filter_asset_id for identifier in dir(filter_id): - if identifier.startswith("filter_") or (identifier.startswith("experimental_filter_") and use_extended_browser): + if ( + identifier.startswith("filter_") or + (identifier.startswith("experimental_filter_") and use_extended_browser) + ): row = col.row() row.label(icon=filter_id.bl_rna.properties[identifier].icon) row.prop(filter_id, identifier, toggle=False) diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index da3db48d962..2fda13184da 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -786,8 +786,16 @@ class NodeTreeInterfacePanel: if tree.type == 'GEOMETRY': layout.prop(active_socket, "description") field_socket_prefixes = { - "NodeSocketInt", "NodeSocketColor", "NodeSocketVector", "NodeSocketBool", "NodeSocketFloat"} - is_field_type = any(active_socket.bl_socket_idname.startswith(prefix) for prefix in field_socket_prefixes) + "NodeSocketInt", + "NodeSocketColor", + "NodeSocketVector", + "NodeSocketBool", + "NodeSocketFloat", + } + is_field_type = any( + active_socket.bl_socket_idname.startswith(prefix) + for prefix in field_socket_prefixes + ) if in_out == 'OUT' and is_field_type: layout.prop(active_socket, "attribute_domain") active_socket.draw(context, layout) From 1411118055368022cf466448d4da8494d05e02c1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 24 Oct 2021 19:31:08 +1100 Subject: [PATCH 1113/1500] Cleanup: spelling in comments --- intern/cycles/render/osl.cpp | 4 ++-- source/blender/blenkernel/intern/asset_catalog.cc | 2 +- source/blender/blenkernel/intern/asset_catalog_test.cc | 2 +- source/blender/blenkernel/intern/spline_bezier.cc | 2 +- source/blender/blenlib/BLI_string_ref.hh | 2 +- source/blender/blenlib/intern/expr_pylike_eval.c | 2 +- source/blender/blenlib/intern/noise.c | 3 +-- source/blender/blenlib/intern/string.c | 2 +- source/blender/bmesh/intern/bmesh_operators.c | 4 ++-- source/blender/compositor/intern/COM_Node.h | 10 ++++------ source/blender/draw/engines/eevee/eevee_motion_blur.c | 4 ++-- .../blender/draw/engines/workbench/workbench_render.c | 2 +- source/blender/editors/object/object_relations.c | 2 +- source/blender/editors/physics/particle_edit.c | 2 +- source/blender/editors/space_text/text_format.h | 4 ++-- source/blender/editors/space_text/text_format_lua.c | 2 +- source/blender/editors/space_text/text_format_osl.c | 4 ++-- source/blender/editors/space_text/text_format_pov.c | 2 +- .../blender/editors/space_text/text_format_pov_ini.c | 2 +- source/blender/editors/space_text/text_format_py.c | 2 +- source/blender/modifiers/intern/MOD_edgesplit.c | 4 ++-- 21 files changed, 30 insertions(+), 33 deletions(-) diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 77863fa0137..dd8f1b31177 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -640,7 +640,7 @@ string OSLCompiler::compatible_name(ShaderNode *node, ShaderInput *input) string sname(input->name().string()); size_t i; - /* strip whitespace */ + /* Strip white-space. */ while ((i = sname.find(" ")) != string::npos) sname.replace(i, 1, ""); @@ -660,7 +660,7 @@ string OSLCompiler::compatible_name(ShaderNode *node, ShaderOutput *output) string sname(output->name().string()); size_t i; - /* strip whitespace */ + /* Strip white-space. */ while ((i = sname.find(" ")) != string::npos) sname.replace(i, 1, ""); diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 41510ff1d02..13f66445c46 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -210,7 +210,7 @@ void AssetCatalogService::delete_catalog_by_id_hard(CatalogID catalog_id) catalog_collection_->catalogs_.remove(catalog_id); catalog_collection_->deleted_catalogs_.remove(catalog_id); - /* TODO(Sybren): adjust this when supporting mulitple CDFs. */ + /* TODO(@sybren): adjust this when supporting multiple CDFs. */ catalog_collection_->catalog_definition_file_->forget(catalog_id); } diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index a2835d7b3b2..abe6d384247 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -984,7 +984,7 @@ TEST_F(AssetCatalogTest, refresh_catalogs_with_modification) TestableAssetCatalogService service(asset_library_root_); service.load_from_disk(cdf_dir); - /* === Perfom changes that should be handled gracefully by the reloading code: */ + /* === Perform changes that should be handled gracefully by the reloading code: */ /* 1. Delete a subtree of catalogs. */ service.prune_catalogs_by_id(UUID_POSES_RUZENA); diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 36ccadf521b..e760bf3495e 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -346,7 +346,7 @@ bool BezierSpline::point_is_sharp(const int index) const } /** - * \warning: This functiona assumes that the spline has more than one point. + * \warning This functional assumes that the spline has more than one point. */ bool BezierSpline::segment_is_vector(const int index) const { diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index 4c2b26fe316..34baf94c448 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -391,7 +391,7 @@ constexpr StringRef StringRefBase::trim() const } /** - * Return a new StringRef that does not contain leading and trailing whitespace. + * Return a new StringRef that does not contain leading and trailing white-space. */ constexpr StringRef StringRefBase::trim(const char character_to_remove) const { diff --git a/source/blender/blenlib/intern/expr_pylike_eval.c b/source/blender/blenlib/intern/expr_pylike_eval.c index 4d1ba190c14..1acb8299aa2 100644 --- a/source/blender/blenlib/intern/expr_pylike_eval.c +++ b/source/blender/blenlib/intern/expr_pylike_eval.c @@ -653,7 +653,7 @@ static bool parse_add_func(ExprParseState *state, eOpCode code, int args, void * /* Extract the next token from raw characters. */ static bool parse_next_token(ExprParseState *state) { - /* Skip whitespace. */ + /* Skip white-space. */ while (isspace(*state->cur)) { state->cur++; } diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c index 9850de69b5a..a0938f4f713 100644 --- a/source/blender/blenlib/intern/noise.c +++ b/source/blender/blenlib/intern/noise.c @@ -1426,8 +1426,7 @@ float BLI_noise_mg_multi_fractal( } return value; - -} /* multifractal() */ +} /** * Heterogeneous procedural terrain function: stats by altitude method. diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 0ea784c95b0..e8be674b6c1 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -1026,7 +1026,7 @@ void BLI_str_toupper_ascii(char *str, const size_t len) } /** - * Strip whitespace from end of the string. + * Strip white-space from end of the string. */ void BLI_str_rstrip(char *str) { diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c index cf7697ad35f..221994aa313 100644 --- a/source/blender/bmesh/intern/bmesh_operators.c +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -1796,11 +1796,11 @@ bool BMO_op_vinitf(BMesh *bm, BMOperator *op, const int flag, const char *_fmt, while (*fmt) { if (state) { - /* jump past leading whitespace */ + /* Jump past leading white-space. */ i = strspn(fmt, " "); fmt += i; - /* ignore trailing whitespace */ + /* Ignore trailing white-space. */ if (!fmt[i]) { break; } diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h index bd8b37276a0..dd126770303 100644 --- a/source/blender/compositor/intern/COM_Node.h +++ b/source/blender/compositor/intern/COM_Node.h @@ -126,16 +126,14 @@ class Node { } /** - * get the reference to a certain outputsocket - * \param index: - * the index of the needed outputsocket + * Get the reference to a certain output-socket. + * \param index: The index of the needed output-socket. */ NodeOutput *get_output_socket(const unsigned int index = 0) const; /** - * get the reference to a certain inputsocket - * \param index: - * the index of the needed inputsocket + * get the reference to a certain input-socket. + * \param index: The index of the needed input-socket. */ NodeInput *get_input_socket(const unsigned int index) const; diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index e8c2514d908..1eff2a3af24 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -405,8 +405,8 @@ void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata) /* Push instances attributes to the GPU. */ DRW_render_instance_buffer_finish(); - /* Need to be called after DRW_render_instance_buffer_finish() */ - /* Also we weed to have a correct fbo bound for DRW_hair_update */ + /* Need to be called after #DRW_render_instance_buffer_finish() */ + /* Also we weed to have a correct FBO bound for #DRW_hair_update. */ GPU_framebuffer_bind(vedata->fbl->main_fb); DRW_hair_update(); diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index f2d178f6565..125bee87614 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -185,7 +185,7 @@ void workbench_render(void *ved, RenderEngine *engine, RenderLayer *render_layer DRW_render_instance_buffer_finish(); - /* Also we weed to have a correct fbo bound for DRW_hair_update */ + /* Also we weed to have a correct FBO bound for #DRW_hair_update */ GPU_framebuffer_bind(dfbl->default_fb); DRW_hair_update(); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index fceccbb6df1..d81143d6081 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1679,7 +1679,7 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) static bool single_data_needs_duplication(ID *id) { - /* NOTE: When dealing with linked data, we always make alocal copy of it. + /* NOTE: When dealing with linked data, we always make a local copy of it. * While in theory we could rather make it local when it only has one user, this is difficult * in practice with current code of this function. */ return (id != NULL && (id->us > 1 || ID_IS_LINKED(id))); diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 8afc5c583e0..97f3b96cd18 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -1000,7 +1000,7 @@ static void PE_update_mirror_cache(Object *ob, ParticleSystem *psys) tree = BLI_kdtree_3d_new(totpart); - /* insert particles into kd tree */ + /* Insert particles into KD-tree. */ LOOP_PARTICLES { key = pa->hair; diff --git a/source/blender/editors/space_text/text_format.h b/source/blender/editors/space_text/text_format.h index 833f40730ad..fe7b3328030 100644 --- a/source/blender/editors/space_text/text_format.h +++ b/source/blender/editors/space_text/text_format.h @@ -75,7 +75,7 @@ typedef struct TextFormatType { } TextFormatType; enum { - /** Whitespace */ + /** White-space */ FMT_TYPE_WHITESPACE = '_', /** Comment text */ FMT_TYPE_COMMENT = '#', @@ -85,7 +85,7 @@ enum { FMT_TYPE_NUMERAL = 'n', /** String letters */ FMT_TYPE_STRING = 'l', - /** Decorator / Preprocessor directive */ + /** Decorator / Pre-processor directive */ FMT_TYPE_DIRECTIVE = 'd', /** Special variables (class, def) */ FMT_TYPE_SPECIAL = 'v', diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c index 0cd2d9baa0b..cdc43246a2f 100644 --- a/source/blender/editors/space_text/text_format_lua.c +++ b/source/blender/editors/space_text/text_format_lua.c @@ -277,7 +277,7 @@ static void txtfmt_lua_format_line(SpaceText *st, TextLine *line, const bool do_ cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* Whitespace (all ws. has been converted to spaces) */ + /* White-space (all ws. has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } diff --git a/source/blender/editors/space_text/text_format_osl.c b/source/blender/editors/space_text/text_format_osl.c index 97d9ec546ca..6fc65a19d98 100644 --- a/source/blender/editors/space_text/text_format_osl.c +++ b/source/blender/editors/space_text/text_format_osl.c @@ -170,7 +170,7 @@ static int txtfmt_osl_find_preprocessor(const char *string) { if (string[0] == '#') { int i = 1; - /* Whitespace is ok '# foo' */ + /* White-space is ok '# foo'. */ while (text_check_whitespace(string[i])) { i++; } @@ -298,7 +298,7 @@ static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_ cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* Whitespace (all ws. has been converted to spaces) */ + /* White-space (all ws. has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c index 7af59c00499..0f103351eee 100644 --- a/source/blender/editors/space_text/text_format_pov.c +++ b/source/blender/editors/space_text/text_format_pov.c @@ -870,7 +870,7 @@ static void txtfmt_pov_format_line(SpaceText *st, TextLine *line, const bool do_ cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* Whitespace (all ws. has been converted to spaces) */ + /* White-space (all ws. has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c index 4e6945a279c..2ce8bc37868 100644 --- a/source/blender/editors/space_text/text_format_pov_ini.c +++ b/source/blender/editors/space_text/text_format_pov_ini.c @@ -448,7 +448,7 @@ static void txtfmt_pov_ini_format_line(SpaceText *st, TextLine *line, const bool cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* Whitespace (all ws. has been converted to spaces) */ + /* White-space (all ws. has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c index e2a01a8d85d..a8a3bfa3a68 100644 --- a/source/blender/editors/space_text/text_format_py.c +++ b/source/blender/editors/space_text/text_format_py.c @@ -423,7 +423,7 @@ static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const bool do_n } *fmt = FMT_TYPE_STRING; } - /* Whitespace (all ws. has been converted to spaces) */ + /* White-space (all ws. has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index b21a536ad8a..1039bcb2b3b 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -20,10 +20,10 @@ /** \file * \ingroup modifiers * - * EdgeSplit modifier + * Edge Split modifier * * Splits edges in the mesh according to sharpness flag - * or edge angle (can be used to achieve autosmoothing) + * or edge angle (can be used to achieve auto-smoothing) */ #include "BLI_utildefines.h" From 6ce383a9dfba5c49a48676c3a651804fde3dfe34 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 24 Oct 2021 22:16:51 +1100 Subject: [PATCH 1114/1500] Cleanup: cross-reference right pointing arrow literal This value is defined in the UI module, but happens to be used in string_search.cc too. Note that these references need to be kept in sync. Use escaped utf-8 sequence since the literal can be avoided. Also replace BLI_str_utf8_as_unicode calls with constant assignments as these values are known there is no need to decode a utf-8 sequence. --- source/blender/blenlib/intern/string_search.cc | 11 +++++++++-- .../blender/blenlib/tests/BLI_string_search_test.cc | 10 ++++++++-- source/blender/editors/include/UI_interface.h | 5 +++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/source/blender/blenlib/intern/string_search.cc b/source/blender/blenlib/intern/string_search.cc index a466c124073..08ba473f96b 100644 --- a/source/blender/blenlib/intern/string_search.cc +++ b/source/blender/blenlib/intern/string_search.cc @@ -24,6 +24,10 @@ #include "BLI_string_utf8.h" #include "BLI_timeit.hh" +/* Right arrow, keep in sync with #UI_MENU_ARROW_SEP in `UI_interface.h`. */ +#define UI_MENU_ARROW_SEP "\xe2\x96\xb6" +#define UI_MENU_ARROW_SEP_UNICODE 0x25b6 + namespace blender::string_search { static int64_t count_utf8_code_points(StringRef str) @@ -350,8 +354,11 @@ void extract_normalized_words(StringRef str, LinearAllocator<> &allocator, Vector &r_words) { - const uint32_t unicode_space = BLI_str_utf8_as_unicode(" "); - const uint32_t unicode_right_triangle = BLI_str_utf8_as_unicode("▶"); + const uint32_t unicode_space = (uint32_t)' '; + const uint32_t unicode_right_triangle = UI_MENU_ARROW_SEP_UNICODE; + + BLI_assert(unicode_space == BLI_str_utf8_as_unicode(" ")); + BLI_assert(unicode_right_triangle == BLI_str_utf8_as_unicode(UI_MENU_ARROW_SEP)); auto is_separator = [&](uint32_t unicode) { return ELEM(unicode, unicode_space, unicode_right_triangle); diff --git a/source/blender/blenlib/tests/BLI_string_search_test.cc b/source/blender/blenlib/tests/BLI_string_search_test.cc index 0d1fd2cab96..aa1c0b41c44 100644 --- a/source/blender/blenlib/tests/BLI_string_search_test.cc +++ b/source/blender/blenlib/tests/BLI_string_search_test.cc @@ -8,6 +8,9 @@ namespace blender::string_search::tests { +/* Right arrow, keep in sync with #UI_MENU_ARROW_SEP in `UI_interface.h`. */ +#define UI_MENU_ARROW_SEP "\xe2\x96\xb6" + TEST(string_search, damerau_levenshtein_distance) { EXPECT_EQ(damerau_levenshtein_distance("test", "test"), 0); @@ -30,14 +33,17 @@ TEST(string_search, get_fuzzy_match_errors) EXPECT_EQ(get_fuzzy_match_errors("", "abc"), 0); EXPECT_EQ(get_fuzzy_match_errors("hello", "hallo"), 1); EXPECT_EQ(get_fuzzy_match_errors("hap", "hello"), -1); - EXPECT_EQ(get_fuzzy_match_errors("armature", "▶restore"), -1); + EXPECT_EQ(get_fuzzy_match_errors("armature", UI_MENU_ARROW_SEP "restore"), -1); } TEST(string_search, extract_normalized_words) { LinearAllocator<> allocator; Vector words; - extract_normalized_words("hello world▶test another test▶ 3", allocator, words); + extract_normalized_words("hello world" UI_MENU_ARROW_SEP "test another test" UI_MENU_ARROW_SEP + " 3", + allocator, + words); EXPECT_EQ(words.size(), 6); EXPECT_EQ(words[0], "hello"); EXPECT_EQ(words[1], "world"); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 0d6236a7728..67d034f4ab6 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -96,8 +96,9 @@ typedef struct uiTreeViewItemHandle uiTreeViewItemHandle; #define UI_SEP_CHAR '|' #define UI_SEP_CHAR_S "|" -/* Separator for text in search menus. */ -#define UI_MENU_ARROW_SEP "▶" +/* Separator for text in search menus (right pointing arrow). + * keep in sync with `string_search.cc`. */ +#define UI_MENU_ARROW_SEP "\xe2\x96\xb6" /* names */ #define UI_MAX_DRAW_STR 400 From cc388651eb48e73470c53c185840f74d950cb38d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 24 Oct 2021 17:53:19 +0200 Subject: [PATCH 1115/1500] Fix T92327: use default value when field is passed into data socket Previously, the computed value passed into the data socket could depend on the actual field a bit. However, given that the link is marked as invalid in the ui, the user should not depend on this behavior. Using a default value is consistent with other cases when there are invalid links. --- source/blender/functions/intern/field.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index c6125b65c5e..5c13db9ec17 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -477,6 +477,12 @@ Vector evaluate_fields(ResourceScope &scope, void evaluate_constant_field(const GField &field, void *r_value) { + if (field.node().depends_on_input()) { + const CPPType &type = field.cpp_type(); + type.copy_construct(type.default_value(), r_value); + return; + } + ResourceScope scope; FieldContext context; Vector varrays = evaluate_fields(scope, {field}, IndexRange(1), context); From 665657812d5d0ab37e72ec732102dbc9f4d34b90 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 24 Oct 2021 18:20:06 +0200 Subject: [PATCH 1116/1500] Cleanup: quiet asan warning because of uninitialized variable --- source/blender/blenkernel/BKE_attribute_access.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 4a1c29badb4..38f5497ed92 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -208,7 +208,7 @@ class OutputAttribute { private: GVMutableArrayPtr varray_; - AttributeDomain domain_; + AttributeDomain domain_ = ATTR_DOMAIN_AUTO; SaveFn save_; std::unique_ptr optional_span_varray_; bool ignore_old_values_ = false; From 6f0dd4f0f045916c9a4b4fc786a69fd7b7d2d530 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 24 Oct 2021 19:26:07 +0200 Subject: [PATCH 1117/1500] Fix: memory leak after type conversion in geometry nodes group The leak happened when two things were true: * Inside of a node group a socket is linked to a Group Input that has a different type. * The corresponding input on the parent Group node is not linked. The conversion happened correctly, but the original value wasn't destructed. --- source/blender/modifiers/intern/MOD_nodes_evaluator.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 69132f6c177..a312872f5d9 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -1455,6 +1455,7 @@ class GeometryNodesEvaluator { } void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment()); this->convert_value(type, required_type, buffer, converted_buffer); + type.destruct(buffer); return {required_type, converted_buffer}; } From 3e75f70acd7d7bb57efd6d07ea05f615d4b12c94 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 24 Oct 2021 13:24:02 -0500 Subject: [PATCH 1118/1500] Geometry Nodes: Remove repeated instance attribute names in search This commit makes sure that each attribute name is only added once when logging geometry values for attribute search. The `attribute_foreach` function for a single geometry component deduplicated names, but a much more common situation is to have more than one component in the instances of a geometry set. Differential Revision: https://developer.blender.org/D12959 --- source/blender/nodes/intern/geometry_nodes_eval_log.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 852f52f38cd..92f20656609 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -175,13 +175,19 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_VOLUME}; + + /* Keep track handled attribute names to make sure that we do not return the same name twice. + * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge + * attributes with the same name but different domains or data types on separate components. */ + Set names; + geometry_set.attribute_foreach( all_component_types, true, [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data, const GeometryComponent &UNUSED(component)) { - if (attribute_id.is_named()) { + if (attribute_id.is_named() && names.add(attribute_id.name())) { this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); } }); From 28ad680ff06677778e85204e00bafb789402491b Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 25 Oct 2021 04:24:41 +0200 Subject: [PATCH 1119/1500] Fix T90836: Strip snaps to empty space This was caused by snap to hold offset feature, which calculates strip content boundary, but it can be outside of strip boundary. Clamp content start and end values so they are always inside of strip. --- source/blender/editors/transform/transform_snap_sequencer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index 2acdf5cfd9c..d752e26088e 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -227,6 +227,10 @@ static void seq_snap_target_points_build(const TransInfo *t, content_end = seq->enddisp; } } + + CLAMP(content_start, seq->startdisp, seq->enddisp); + CLAMP(content_end, seq->startdisp, seq->enddisp); + snap_data->target_snap_points[i] = content_start; snap_data->target_snap_points[i + 1] = content_end; i += 2; From 4266538ab980fc475a6fd295892b1ad48ee2b7dd Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 25 Oct 2021 04:33:43 +0200 Subject: [PATCH 1120/1500] Fix T90835: Strip snaps to first and second frame This was caused by strips with single frame input like single image strip or color strip. their length is always 1, and so content length was calculated to end after first frame. There was code handling this case, but it was also checking for `anim_endofs` and `endstill` values. Anim offset values have no effect on these strips and still frame value was used incorrectly. So these chacks can be removed completely. --- .../blender/editors/transform/transform_snap_sequencer.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index d752e26088e..7bcf6812ce9 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -220,12 +220,8 @@ static void seq_snap_target_points_build(const TransInfo *t, int content_end = max_ii(seq->startdisp, seq->start + seq->len); /* Effects and single image strips produce incorrect content length. Skip these strips. */ if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->len == 1) { - if (seq->anim_startofs == 0 && seq->startstill == 0) { - content_start = seq->startdisp; - } - if (seq->anim_endofs == 0 && seq->endstill == 0) { - content_end = seq->enddisp; - } + content_start = seq->startdisp; + content_end = seq->enddisp; } CLAMP(content_start, seq->startdisp, seq->enddisp); From 84f7bf56a8c3bc520583b6e49e954d84469659db Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 25 Oct 2021 05:35:54 +0200 Subject: [PATCH 1121/1500] Fix T90855: Transform effect gives inconsistent output When using downscaled preview size with proxies, transform effect doesn't compensate for fact, that pixels are effectively larger. There was compensation for scene render size already. Use same compensation method as text effect uses for font size. --- source/blender/sequencer/intern/effects.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 427a8835879..b9ee23a9186 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -2451,7 +2451,6 @@ static void do_transform_effect(const SeqRenderData *context, int total_lines, ImBuf *out) { - Scene *scene = context->scene; TransformVars *transform = (TransformVars *)seq->effectdata; float scale_x, scale_y, translate_x, translate_y, rotate_radians; @@ -2469,10 +2468,14 @@ static void do_transform_effect(const SeqRenderData *context, /* Translate */ if (!transform->percent) { - float rd_s = (scene->r.size / 100.0f); + /* Compensate text size for preview render size. */ + double proxy_size_comp = context->scene->r.size / 100.0; + if (context->preview_render_size != SEQ_RENDER_SIZE_SCENE) { + proxy_size_comp = SEQ_rendersize_to_scale_factor(context->preview_render_size); + } - translate_x = transform->xIni * rd_s + (x / 2.0f); - translate_y = transform->yIni * rd_s + (y / 2.0f); + translate_x = transform->xIni * proxy_size_comp + (x / 2.0f); + translate_y = transform->yIni * proxy_size_comp + (y / 2.0f); } else { translate_x = x * (transform->xIni / 100.0f) + (x / 2.0f); From 82ae7b990acfa2c522895322eb052b90f0c3b83a Mon Sep 17 00:00:00 2001 From: Andrea Beconcini Date: Mon, 25 Oct 2021 06:47:51 +0200 Subject: [PATCH 1122/1500] Fix T90633: Frame all doesn't use meta range This commit fixes T90633, it changes the behavior of the `Frame All` operation when the user is tabbed into a metastrip: instead of using the scene timeline's range, `Frame All` uses the current metastrip's range. Reviewed By: ISS Differential Revision: https://developer.blender.org/D12974 --- .../editors/space_sequencer/sequencer_view.c | 9 +++++- source/blender/sequencer/SEQ_time.h | 2 ++ source/blender/sequencer/intern/strip_time.c | 31 ++++++++++++++++--- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 79593b0bbb0..2d2e7de7135 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -92,7 +92,14 @@ static int sequencer_view_all_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); const Editing *ed = SEQ_editing_get(scene); - SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &box); + SEQ_timeline_init_boundbox(scene, &box); + MetaStack *ms = SEQ_meta_stack_active_get(ed); + /* Use meta strip range instead of scene. */ + if (ms != NULL) { + box.xmin = ms->disp_range[0] - 1; + box.xmax = ms->disp_range[1] + 1; + } + SEQ_timeline_expand_boundbox(SEQ_active_seqbase_get(ed), &box); UI_view2d_smooth_view(C, region, &box, smooth_viewtx); return OPERATOR_FINISHED; } diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h index c9024614dfd..df3c9a40409 100644 --- a/source/blender/sequencer/SEQ_time.h +++ b/source/blender/sequencer/SEQ_time.h @@ -32,6 +32,8 @@ struct Scene; struct Sequence; struct rctf; +void SEQ_timeline_init_boundbox(const struct Scene *scene, struct rctf *rect); +void SEQ_timeline_expand_boundbox(const struct ListBase *seqbase, struct rctf *rect); void SEQ_timeline_boundbox(const struct Scene *scene, const struct ListBase *seqbase, struct rctf *rect); diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 1c5f4c3ab76..92ac580f3b1 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -376,19 +376,27 @@ float SEQ_time_sequence_get_fps(Scene *scene, Sequence *seq) } /** - * Define boundary rectangle of sequencer timeline and fill in rect data + * Initialize given rectangle with the Scene's timeline boundaries. * - * \param scene: Scene in which strips are located - * \param seqbase: ListBase in which strips are located - * \param rect: data structure describing rectangle, that will be filled in by this function + * \param scene: the Scene instance whose timeline boundaries are extracted from + * \param rect: output parameter to be filled with timeline boundaries */ -void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect) +void SEQ_timeline_init_boundbox(const Scene *scene, rctf *rect) { rect->xmin = scene->r.sfra; rect->xmax = scene->r.efra + 1; rect->ymin = 0.0f; rect->ymax = 8.0f; +} +/** + * Stretch the given rectangle to include the given strips boundaries + * + * \param seqbase: ListBase in which strips are located + * \param rect: output parameter to be filled with strips' boundaries + */ +void SEQ_timeline_expand_boundbox(const ListBase *seqbase, rctf *rect) +{ if (seqbase == NULL) { return; } @@ -406,6 +414,19 @@ void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *re } } +/** + * Define boundary rectangle of sequencer timeline and fill in rect data + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param rect: data structure describing rectangle, that will be filled in by this function + */ +void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect) +{ + SEQ_timeline_init_boundbox(scene, rect); + SEQ_timeline_expand_boundbox(seqbase, rect); +} + static bool strip_exists_at_frame(SeqCollection *all_strips, const int timeline_frame) { Sequence *seq; From b714f9bf437de32dab768d4c28674ca1f3e5b325 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Sun, 24 Oct 2021 22:01:20 -0700 Subject: [PATCH 1123/1500] Fix T91931: Thumbnail Missing Region Fixes a crash when blend thumbnails set to Camera View when there is no camera, which resulted in use of a null region. See D12748 for more details. Differential Revision: https://developer.blender.org/D12748 Reviewed by Campbell Barton --- source/blender/windowmanager/intern/wm_files.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index e203281297b..2525c627785 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1644,6 +1644,7 @@ static ImBuf *blend_file_thumb_from_camera(const bContext *C, area = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0); if (area) { v3d = area->spacedata.first; + region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); } } From 8733d310e54766d7c07d101e8959bd9f3eaf0daf Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 21 Oct 2021 14:52:34 +0200 Subject: [PATCH 1124/1500] Cycles: Revert all host update specific changes The approach didn't turn out to be that useful after all as there are unavoidable dependencies of data from the device. For example, to know whether object intersects volume or not it is required to run displacement kernels. The way of splitting host and device updates caused state where some data is not yet available, causing confusion and leaving code to be error-prone. --- intern/cycles/render/osl.cpp | 94 +++++++++++++-------------------- intern/cycles/render/osl.h | 4 +- intern/cycles/render/scene.cpp | 32 +++-------- intern/cycles/render/scene.h | 2 - intern/cycles/render/shader.cpp | 17 ++---- intern/cycles/render/shader.h | 3 -- intern/cycles/render/svm.cpp | 59 ++++++++------------- intern/cycles/render/svm.h | 11 ++-- 8 files changed, 75 insertions(+), 147 deletions(-) diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index dd8f1b31177..1037ebcae7e 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -91,51 +91,70 @@ void OSLShaderManager::reset(Scene * /*scene*/) shading_system_init(); } -void OSLShaderManager::host_update_specific(Scene *scene, Progress &progress) +void OSLShaderManager::device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { - if (!need_update()) { + if (!need_update()) return; - } scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { - scene->update_stats->osl.times.add_entry({"host_update", time}); + scene->update_stats->osl.times.add_entry({"device_update", time}); } }); VLOG(1) << "Total " << scene->shaders.size() << " shaders."; + device_free(device, dscene, scene); + /* set texture system */ scene->image_manager->set_osl_texture_system((void *)ts); /* create shaders */ + OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); Shader *background_shader = scene->background->get_shader(scene); - for (Shader *shader : scene->shaders) { + foreach (Shader *shader, scene->shaders) { assert(shader->graph); - if (progress.get_cancel()) { + if (progress.get_cancel()) return; - } - /* we can only compile one shader at the time as the OSL ShadingSystem + /* we can only compile one shader at the time as the OSL ShadingSytem * has a single state, but we put the lock here so different renders can * compile shaders alternating */ thread_scoped_lock lock(ss_mutex); OSLCompiler compiler(this, services, ss, scene); compiler.background = (shader == background_shader); - compiler.compile(shader); + compiler.compile(og, shader); - if (shader->get_use_mis() && shader->has_surface_emission) { + if (shader->get_use_mis() && shader->has_surface_emission) scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED); - } } + /* setup shader engine */ + og->ss = ss; + og->ts = ts; + og->services = services; + + int background_id = scene->shader_manager->get_shader_id(background_shader); + og->background_state = og->surface_state[background_id & SHADER_MASK]; + og->use = true; + + foreach (Shader *shader, scene->shaders) + shader->clear_modified(); + + update_flags = UPDATE_NONE; + /* add special builtin texture types */ services->textures.insert(ustring("@ao"), new OSLTextureHandle(OSLTextureHandle::AO)); services->textures.insert(ustring("@bevel"), new OSLTextureHandle(OSLTextureHandle::BEVEL)); + device_update_common(device, dscene, scene, progress); + { /* Perform greedyjit optimization. * @@ -153,51 +172,6 @@ void OSLShaderManager::host_update_specific(Scene *scene, Progress &progress) } } -void OSLShaderManager::device_update_specific(Device *device, - DeviceScene *dscene, - Scene *scene, - Progress &progress) -{ - if (!need_update()) - return; - - scoped_callback_timer timer([scene](double time) { - if (scene->update_stats) { - scene->update_stats->osl.times.add_entry({"device_update", time}); - } - }); - - device_free(device, dscene, scene); - - OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); - Shader *background_shader = scene->background->get_shader(scene); - - /* Setup shader engine. */ - og->ss = ss; - og->ts = ts; - og->services = services; - - for (Shader *shader : scene->shaders) { - /* push state to array for lookup */ - og->surface_state.push_back(shader->osl_surface_ref); - og->volume_state.push_back(shader->osl_volume_ref); - og->displacement_state.push_back(shader->osl_displacement_ref); - og->bump_state.push_back(shader->osl_surface_bump_ref); - - shader->clear_modified(); - } - - const int background_id = scene->shader_manager->get_shader_id(background_shader); - const int background_state_index = (background_id & SHADER_MASK); - DCHECK_LT(background_state_index, og->surface_state.size()); - og->background_state = og->surface_state[background_state_index]; - og->use = true; - - update_flags = UPDATE_NONE; - - device_update_common(device, dscene, scene, progress); -} - void OSLShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *scene) { OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); @@ -1158,7 +1132,7 @@ OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph return group; } -void OSLCompiler::compile(Shader *shader) +void OSLCompiler::compile(OSLGlobals *og, Shader *shader) { if (shader->is_modified()) { ShaderGraph *graph = shader->graph; @@ -1220,6 +1194,12 @@ void OSLCompiler::compile(Shader *shader) else shader->osl_displacement_ref = OSL::ShaderGroupRef(); } + + /* push state to array for lookup */ + og->surface_state.push_back(shader->osl_surface_ref); + og->volume_state.push_back(shader->osl_volume_ref); + og->displacement_state.push_back(shader->osl_displacement_ref); + og->bump_state.push_back(shader->osl_surface_bump_ref); } void OSLCompiler::parameter_texture(const char *name, ustring filename, ustring colorspace) diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h index 1da8d8d0026..dfeec54d915 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/render/osl.h @@ -79,8 +79,6 @@ class OSLShaderManager : public ShaderManager { return true; } - void host_update_specific(Scene *scene, Progress &progress) override; - void device_update_specific(Device *device, DeviceScene *dscene, Scene *scene, @@ -140,7 +138,7 @@ class OSLCompiler { OSL::ShadingSystem *shadingsys, Scene *scene); #endif - void compile(Shader *shader); + void compile(OSLGlobals *og, Shader *shader); void add(ShaderNode *node, const char *name, bool isfilepath = false); diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index 6d79536061e..da6666babe6 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -228,22 +228,6 @@ void Scene::free_memory(bool final) } } -void Scene::host_update(Progress &progress) -{ - if (update_stats) { - update_stats->clear(); - } - - scoped_callback_timer timer([this](double time) { - if (update_stats) { - update_stats->scene.times.add_entry({"host_update", time}); - } - }); - - progress.set_status("Updating Shaders"); - shader_manager->host_update(this, progress); -} - void Scene::device_update(Device *device_, Progress &progress) { if (!device) @@ -251,6 +235,10 @@ void Scene::device_update(Device *device_, Progress &progress) bool print_stats = need_data_update(); + if (update_stats) { + update_stats->clear(); + } + scoped_callback_timer timer([this, print_stats](double time) { if (update_stats) { update_stats->scene.times.add_entry({"device_update", time}); @@ -549,17 +537,11 @@ bool Scene::update(Progress &progress) return false; } - /* Update scene data on the host side. - * Only updates which do not depend on the kernel (including kernel features). */ - progress.set_status("Updating Scene"); - MEM_GUARDED_CALL(&progress, host_update, progress); - - /* Load render kernels. After host scene update so that the required kernel features are known. - */ + /* Load render kernels, before device update where we upload data to the GPU. */ load_kernels(progress, false); - /* Upload scene data to the device. */ - progress.set_status("Updating Scene Device"); + /* Upload scene data to the GPU. */ + progress.set_status("Updating Scene"); MEM_GUARDED_CALL(&progress, device_update, device, progress); return true; diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 87fbc872c0a..001da31e893 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -251,8 +251,6 @@ class Scene : public NodeOwner { Scene(const SceneParams ¶ms, Device *device); ~Scene(); - void host_update(Progress &progress); - void device_update(Device *device, Progress &progress); bool need_global_attribute(AttributeStandard std); diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index 171cbb89549..23786a3390f 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -462,7 +462,10 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth) return id; } -void ShaderManager::host_update(Scene *scene, Progress &progress) +void ShaderManager::device_update(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { if (!need_update()) { return; @@ -480,18 +483,6 @@ void ShaderManager::host_update(Scene *scene, Progress &progress) assert(scene->default_background->reference_count() != 0); assert(scene->default_empty->reference_count() != 0); - host_update_specific(scene, progress); -} - -void ShaderManager::device_update(Device *device, - DeviceScene *dscene, - Scene *scene, - Progress &progress) -{ - if (!need_update()) { - return; - } - device_update_specific(device, dscene, scene, progress); } diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index 44d88ac0e93..5f9adea3949 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -193,9 +193,6 @@ class ShaderManager { return false; } - void host_update(Scene *scene, Progress &progress); - virtual void host_update_specific(Scene *scene, Progress &progress) = 0; - /* device update */ void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); virtual void device_update_specific(Device *device, diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index a670ceb3ea3..2379eb775a0 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -47,10 +47,10 @@ void SVMShaderManager::reset(Scene * /*scene*/) { } -static void host_compile_shader(Scene *scene, - Shader *shader, - Progress *progress, - array *svm_nodes) +void SVMShaderManager::device_update_shader(Scene *scene, + Shader *shader, + Progress *progress, + array *svm_nodes) { if (progress->get_cancel()) { return; @@ -69,32 +69,6 @@ static void host_compile_shader(Scene *scene, << summary.full_report(); } -void SVMShaderManager::host_update_specific(Scene *scene, Progress &progress) -{ - if (!need_update()) { - return; - } - - scoped_callback_timer timer([scene](double time) { - if (scene->update_stats) { - scene->update_stats->svm.times.add_entry({"host_update", time}); - } - }); - - const int num_shaders = scene->shaders.size(); - - VLOG(1) << "Total " << num_shaders << " shaders."; - - /* Build all shaders. */ - TaskPool task_pool; - shader_svm_nodes_.resize(num_shaders); - for (int i = 0; i < num_shaders; i++) { - task_pool.push(function_bind( - host_compile_shader, scene, scene->shaders[i], &progress, &shader_svm_nodes_[i])); - } - task_pool.wait_work(); -} - void SVMShaderManager::device_update_specific(Device *device, DeviceScene *dscene, Scene *scene, @@ -118,6 +92,19 @@ void SVMShaderManager::device_update_specific(Device *device, /* test if we need to update */ device_free(device, dscene, scene); + /* Build all shaders. */ + TaskPool task_pool; + vector> shader_svm_nodes(num_shaders); + for (int i = 0; i < num_shaders; i++) { + task_pool.push(function_bind(&SVMShaderManager::device_update_shader, + this, + scene, + scene->shaders[i], + &progress, + &shader_svm_nodes[i])); + } + task_pool.wait_work(); + if (progress.get_cancel()) { return; } @@ -127,7 +114,7 @@ void SVMShaderManager::device_update_specific(Device *device, int svm_nodes_size = num_shaders; for (int i = 0; i < num_shaders; i++) { /* Since we're not copying the local jump node, the size ends up being one node lower. */ - svm_nodes_size += shader_svm_nodes_[i].size() - 1; + svm_nodes_size += shader_svm_nodes[i].size() - 1; } int4 *svm_nodes = dscene->svm_nodes.alloc(svm_nodes_size); @@ -145,22 +132,22 @@ void SVMShaderManager::device_update_specific(Device *device, * Each compiled shader starts with a jump node that has offsets local * to the shader, so copy those and add the offset into the global node list. */ int4 &global_jump_node = svm_nodes[shader->id]; - int4 &local_jump_node = shader_svm_nodes_[i][0]; + int4 &local_jump_node = shader_svm_nodes[i][0]; global_jump_node.x = NODE_SHADER_JUMP; global_jump_node.y = local_jump_node.y - 1 + node_offset; global_jump_node.z = local_jump_node.z - 1 + node_offset; global_jump_node.w = local_jump_node.w - 1 + node_offset; - node_offset += shader_svm_nodes_[i].size() - 1; + node_offset += shader_svm_nodes[i].size() - 1; } /* Copy the nodes of each shader into the correct location. */ svm_nodes += num_shaders; for (int i = 0; i < num_shaders; i++) { - int shader_size = shader_svm_nodes_[i].size() - 1; + int shader_size = shader_svm_nodes[i].size() - 1; - memcpy(svm_nodes, &shader_svm_nodes_[i][1], sizeof(int4) * shader_size); + memcpy(svm_nodes, &shader_svm_nodes[i][1], sizeof(int4) * shader_size); svm_nodes += shader_size; } @@ -174,8 +161,6 @@ void SVMShaderManager::device_update_specific(Device *device, update_flags = UPDATE_NONE; - shader_svm_nodes_.clear(); - VLOG(1) << "Shader manager updated " << num_shaders << " shaders in " << time_dt() - start_time << " seconds."; } diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h index a45c66907b1..0353c393ae4 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/render/svm.h @@ -46,8 +46,6 @@ class SVMShaderManager : public ShaderManager { void reset(Scene *scene) override; - void host_update_specific(Scene *scene, Progress &progress) override; - void device_update_specific(Device *device, DeviceScene *dscene, Scene *scene, @@ -55,11 +53,10 @@ class SVMShaderManager : public ShaderManager { void device_free(Device *device, DeviceScene *dscene, Scene *scene) override; protected: - /* Compiled shader nodes. - * - * The compilation happens in the `host_update_specific()`, and the `device_update_specific()` - * moves these nodes to the device. */ - vector> shader_svm_nodes_; + void device_update_shader(Scene *scene, + Shader *shader, + Progress *progress, + array *svm_nodes); }; /* Graph Compiler */ From c4fa17c67a7c28d34abe0db2da8783f4c5ab2a8f Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 22 Oct 2021 14:20:22 +0200 Subject: [PATCH 1125/1500] Cycles: More accurate volume stack size calculation The idea is to allow having a lot of non-intersecting volumes without allocating volume stack to its full size. With the F11285472 file the memory usage goes from 1400 MiB to 1000 on the RTX6000 card. The fix makes it so the integrator work memory is allocated after scene update which has downside of possible less efficient update when some textures don't fit GPU memory, but has an advantage of making proper decision and having a clear and consistent internal API. Fixes memory part of T92014. Differential Revision: https://developer.blender.org/D12966 --- intern/cycles/render/object.cpp | 19 +++---------------- intern/cycles/render/object.h | 10 +++------- intern/cycles/render/scene.cpp | 25 ++++++++++++++++++++++--- intern/cycles/render/scene.h | 2 +- intern/cycles/render/session.cpp | 11 ++--------- 5 files changed, 31 insertions(+), 36 deletions(-) diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 6d5c537e33d..330ae5ec0fc 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -114,6 +114,7 @@ Object::Object() : Node(get_node_type()) particle_index = 0; attr_map_offset = 0; bounds = BoundBox::empty; + intersects_volume = false; } Object::~Object() @@ -367,22 +368,6 @@ float Object::compute_volume_step_size() const return step_size; } -bool Object::check_is_volume() const -{ - if (geometry->geometry_type == Geometry::VOLUME) { - return true; - } - - for (Node *node : get_geometry()->get_used_shaders()) { - const Shader *shader = static_cast(node); - if (shader->has_volume) { - return true; - } - } - - return false; -} - int Object::get_device_index() const { return index; @@ -775,12 +760,14 @@ void ObjectManager::device_update_flags( } if (bounds_valid) { + object->intersects_volume = false; foreach (Object *volume_object, volume_objects) { if (object == volume_object) { continue; } if (object->bounds.intersects(volume_object->bounds)) { object_flag[object->index] |= SD_OBJECT_INTERSECTS_VOLUME; + object->intersects_volume = true; break; } } diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 6920f2c1f1c..ad312835789 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -75,6 +75,9 @@ class Object : public Node { NODE_SOCKET_API(float, ao_distance) + /* Set during device update. */ + bool intersects_volume; + Object(); ~Object(); @@ -109,13 +112,6 @@ class Object : public Node { /* Compute step size from attributes, shaders, transforms. */ float compute_volume_step_size() const; - /* Check whether this object requires volume sampling (and hence might require space in the - * volume stack). - * - * Note that this is a naive iteration over shaders, which allows to access information prior - * to `scene_update()`. */ - bool check_is_volume() const; - protected: /* Specifies the position of the object in scene->objects and * in the device vectors. Gets set in device_update. */ diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index da6666babe6..fd19ab2efda 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -360,6 +360,8 @@ void Scene::device_update(Device *device_, Progress &progress) return; if (device->have_error() == false) { + dscene.data.volume_stack_size = get_volume_stack_size(); + progress.set_status("Updating Device", "Writing constant memory"); device->const_copy_to("__data", &dscene.data, sizeof(dscene.data)); } @@ -527,8 +529,6 @@ void Scene::update_kernel_features() const uint max_closures = (params.background) ? get_max_closure_count() : MAX_CLOSURE; dscene.data.max_closures = max_closures; dscene.data.max_shaders = shaders.size(); - - dscene.data.volume_stack_size = get_volume_stack_size(); } bool Scene::update(Progress &progress) @@ -586,6 +586,8 @@ bool Scene::load_kernels(Progress &progress, bool lock_scene) scene_lock = thread_scoped_lock(mutex); } + update_kernel_features(); + const uint kernel_features = dscene.data.kernel_features; if (!kernels_loaded || loaded_kernel_features != kernel_features) { @@ -656,10 +658,25 @@ int Scene::get_volume_stack_size() const /* Quick non-expensive check. Can over-estimate maximum possible nested level, but does not * require expensive calculation during pre-processing. */ + bool has_volume_object = false; for (const Object *object : objects) { - if (object->check_is_volume()) { + if (!object->get_geometry()->has_volume) { + continue; + } + + if (object->intersects_volume) { + /* Object intersects another volume, assume it's possible to go deeper in the stack. */ + /* TODO(sergey): This might count nesting twice (A intersects B and B intersects A), but + * can't think of a computantially cheap algorithm. Dividing my 2 doesn't work because of + * Venn diagram example with 3 circles. */ ++volume_stack_size; } + else if (!has_volume_object) { + /* Allocate space for at least one volume object. */ + ++volume_stack_size; + } + + has_volume_object = true; if (volume_stack_size == MAX_VOLUME_STACK_SIZE) { break; @@ -668,6 +685,8 @@ int Scene::get_volume_stack_size() const volume_stack_size = min(volume_stack_size, MAX_VOLUME_STACK_SIZE); + VLOG(3) << "Detected required volume stack size " << volume_stack_size; + return volume_stack_size; } diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 001da31e893..de7e3c8a99f 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -270,7 +270,6 @@ class Scene : public NodeOwner { void enable_update_stats(); - void update_kernel_features(); bool update(Progress &progress); bool has_shadow_catcher(); @@ -333,6 +332,7 @@ class Scene : public NodeOwner { bool kernels_loaded; uint loaded_kernel_features; + void update_kernel_features(); bool load_kernels(Progress &progress, bool lock_scene = true); bool has_shadow_catcher_ = false; diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index a18c61599c2..7fba7ce7552 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -539,19 +539,12 @@ bool Session::update_scene(int width, int height) Camera *cam = scene->camera; cam->set_screen_size(width, height); - /* First detect which kernel features are used and allocate working memory. - * This helps estimate how may device memory is available for the scene and - * how much we need to allocate on the host instead. */ - scene->update_kernel_features(); + const bool scene_update_result = scene->update(progress); path_trace_->load_kernels(); path_trace_->alloc_work_memory(); - if (scene->update(progress)) { - return true; - } - - return false; + return scene_update_result; } static string status_append(const string &status, const string &suffix) From d16e7326386d055fc5cdfa9f60bcd3d75bcbbed5 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sun, 24 Oct 2021 18:51:12 +0200 Subject: [PATCH 1126/1500] UI: Refactor dropping support for the tree-view API Introduces a dropping-controller API for the tree-view items, `AbstractTreeViewItemDropController`. This reduces responsibilities of the main tree-view item classes, which are already getting quite big. As I expect even more functionality to be needed for it (e.g. drag support), it's better to start introducing such controller types already. --- source/blender/editors/include/UI_interface.h | 5 +- .../blender/editors/include/UI_tree_view.hh | 66 +++++-- .../editors/interface/interface_dropboxes.cc | 2 +- source/blender/editors/interface/tree_view.cc | 52 +++--- .../space_file/asset_catalog_tree_view.cc | 167 +++++++++++------- 5 files changed, 189 insertions(+), 103 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 67d034f4ab6..de6b975a910 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2781,11 +2781,8 @@ void UI_interface_tag_script_reload(void); bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const struct wmDrag *drag); +char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct wmDrag *drag); bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const struct ListBase *drags); -char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, - const struct bContext *C, - const struct wmDrag *drag, - const struct wmEvent *event); bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle); void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle); diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index b1ec22c57a6..f565c80193f 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -48,6 +48,7 @@ namespace blender::ui { class AbstractTreeView; class AbstractTreeViewItem; +class AbstractTreeViewItemDropController; /* ---------------------------------------------------------------------- */ /** \name Tree-View Item Container @@ -242,17 +243,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer { * arguments for checking if the item is currently in an active state. */ virtual void is_active(IsActiveFn is_active_fn); - virtual bool on_drop(const wmDrag &drag); - virtual bool can_drop(const wmDrag &drag) const; - /** - * Custom text to display when dragging over a tree item. Should explain what happens when - * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() - * returns true, so the implementing override doesn't have to check that again. - * The returned value must be a translated string. - */ - virtual std::string drop_tooltip(const bContext &C, - const wmDrag &drag, - const wmEvent &event) const; + /** * Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if * another item is already being renamed. @@ -282,6 +273,15 @@ class AbstractTreeViewItem : public TreeViewItemContainer { */ virtual bool matches(const AbstractTreeViewItem &other) const; + /** + * If an item wants to support dropping data into it, it has to return a drop controller here. + * That is an object implementing #AbstractTreeViewItemDropController. + * + * \note This drop controller may be requested for each event. The tree-view doesn't keep a drop + * controller around currently. So it can not contain persistent state. + */ + virtual std::unique_ptr create_drop_controller() const; + void begin_renaming(); void end_renaming(); @@ -343,6 +343,45 @@ class AbstractTreeViewItem : public TreeViewItemContainer { /** \} */ +/* ---------------------------------------------------------------------- */ +/** \name Drag 'n Drop + * \{ */ + +/** + * Class to customize the drop behavior of a tree-item, plus the behavior when dragging over this + * item. An item can return a drop controller for itself via a custom implementation of + * #AbstractTreeViewItem::create_drop_controller(). + */ +class AbstractTreeViewItemDropController { + protected: + AbstractTreeView &tree_view_; + + public: + AbstractTreeViewItemDropController(AbstractTreeView &tree_view); + virtual ~AbstractTreeViewItemDropController() = default; + + /** + * Check if the data dragged with \a drag can be dropped on the item this controller is for. + */ + virtual bool can_drop(const wmDrag &drag) const = 0; + /** + * Custom text to display when dragging over a tree item. Should explain what happens when + * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() + * returns true, so the implementing override doesn't have to check that again. + * The returned value must be a translated string. + */ + virtual std::string drop_tooltip(const wmDrag &drag) const = 0; + /** + * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this + * controller is for. + */ + virtual bool on_drop(const wmDrag &drag) = 0; + + template inline TreeViewType &tree_view() const; +}; + +/** \} */ + /* ---------------------------------------------------------------------- */ /** \name Predefined Tree-View Item Types * @@ -390,4 +429,9 @@ inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args) add_tree_item(std::make_unique(std::forward(args)...))); } +template TreeViewType &AbstractTreeViewItemDropController::tree_view() const +{ + return static_cast(tree_view_); +} + } // namespace blender::ui diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index 62250a34cf4..ae626080a9a 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -50,7 +50,7 @@ static char *ui_tree_view_drop_tooltip(bContext *C, return nullptr; } - return UI_tree_view_item_drop_tooltip(hovered_tree_item, C, drag, event); + return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag); } void ED_dropboxes_ui() diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 88aa362deb5..946699d5115 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -354,22 +354,11 @@ void AbstractTreeViewItem::is_active(IsActiveFn is_active_fn) is_active_fn_ = is_active_fn; } -bool AbstractTreeViewItem::on_drop(const wmDrag & /*drag*/) +std::unique_ptr AbstractTreeViewItem::create_drop_controller() + const { - /* Do nothing by default. */ - return false; -} - -bool AbstractTreeViewItem::can_drop(const wmDrag & /*drag*/) const -{ - return false; -} - -std::string AbstractTreeViewItem::drop_tooltip(const bContext & /*C*/, - const wmDrag & /*drag*/, - const wmEvent & /*event*/) const -{ - return TIP_("Drop into/onto tree item"); + /* There's no drop controller (and hence no drop support) by default. */ + return nullptr; } bool AbstractTreeViewItem::can_rename() const @@ -553,6 +542,12 @@ void AbstractTreeViewItem::change_state_delayed() activate(); } } +/* ---------------------------------------------------------------------- */ + +AbstractTreeViewItemDropController::AbstractTreeViewItemDropController(AbstractTreeView &tree_view) + : tree_view_(tree_view) +{ +} /* ---------------------------------------------------------------------- */ @@ -683,16 +678,25 @@ bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const wmDrag *drag) { const AbstractTreeViewItem &item = reinterpret_cast(*item_); - return item.can_drop(*drag); + const std::unique_ptr drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return false; + } + + return drop_controller->can_drop(*drag); } -char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, - const bContext *C, - const wmDrag *drag, - const wmEvent *event) +char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wmDrag *drag) { const AbstractTreeViewItem &item = reinterpret_cast(*item_); - return BLI_strdup(item.drop_tooltip(*C, *drag, *event).c_str()); + const std::unique_ptr drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return NULL; + } + + return BLI_strdup(drop_controller->drop_tooltip(*drag).c_str()); } /** @@ -702,10 +706,12 @@ char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const ListBase *drags) { AbstractTreeViewItem &item = reinterpret_cast(*item_); + std::unique_ptr drop_controller = + item.create_drop_controller(); LISTBASE_FOREACH (const wmDrag *, drag, drags) { - if (item.can_drop(*drag)) { - return item.on_drop(*drag); + if (drop_controller->can_drop(*drag)) { + return drop_controller->on_drop(*drag); } } diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index c305a11daf4..68df4cc0544 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -60,6 +60,7 @@ class AssetCatalogTreeView : public ui::AbstractTreeView { SpaceFile &space_file_; friend class AssetCatalogTreeViewItem; + friend class AssetCatalogDropController; public: AssetCatalogTreeView(::AssetLibrary *library, @@ -86,25 +87,34 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { public: AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item); - static bool has_droppable_item(const wmDrag &drag); - static bool drop_into_catalog(const AssetCatalogTreeView &tree_view, - const wmDrag &drag, - CatalogID catalog_id, - StringRefNull simple_name = ""); - void on_activate() override; void build_row(uiLayout &row) override; void build_context_menu(bContext &C, uiLayout &column) const override; - bool can_drop(const wmDrag &drag) const override; - std::string drop_tooltip(const bContext &C, - const wmDrag &drag, - const wmEvent &event) const override; - bool on_drop(const wmDrag &drag) override; - bool can_rename() const override; bool rename(StringRefNull new_name) override; + + /** Add dropping support for catalog items. */ + std::unique_ptr create_drop_controller() const override; +}; + +class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController { + AssetCatalogTreeItem &catalog_item_; + + public: + explicit AssetCatalogDropController(AssetCatalogTreeView &tree_view, + AssetCatalogTreeItem &catalog_item); + + bool can_drop(const wmDrag &drag) const override; + std::string drop_tooltip(const wmDrag &drag) const override; + bool on_drop(const wmDrag &drag) override; + + static bool has_droppable_item(const wmDrag &drag); + static bool drop_into_catalog(const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name = ""); }; /** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level @@ -118,11 +128,15 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { using BasicTreeViewItem::BasicTreeViewItem; - bool can_drop(const wmDrag &drag) const override; - std::string drop_tooltip(const bContext &C, - const wmDrag &drag, - const wmEvent &event) const override; - bool on_drop(const wmDrag &drag) override; + struct DropController : public ui::AbstractTreeViewItemDropController { + DropController(AssetCatalogTreeView &tree_view); + + bool can_drop(const wmDrag &drag) const override; + std::string drop_tooltip(const wmDrag &drag) const override; + bool on_drop(const wmDrag &drag) override; + }; + + std::unique_ptr create_drop_controller() const override; }; /* ---------------------------------------------------------------------- */ @@ -275,20 +289,38 @@ void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column) UI_menutype_draw(&C, mt, &column); } -bool AssetCatalogTreeViewItem::has_droppable_item(const wmDrag &drag) +bool AssetCatalogTreeViewItem::can_rename() const { - const ListBase *asset_drags = WM_drag_asset_list_get(&drag); - - /* There needs to be at least one asset from the current file. */ - LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) { - if (!asset_item->is_external) { - return true; - } - } - return false; + return true; } -bool AssetCatalogTreeViewItem::can_drop(const wmDrag &drag) const +bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) +{ + /* Important to keep state. */ + BasicTreeViewItem::rename(new_name); + + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + ED_asset_catalog_rename(tree_view.asset_library_, catalog_item_.get_catalog_id(), new_name); + return true; +} + +std::unique_ptr AssetCatalogTreeViewItem:: + create_drop_controller() const +{ + return std::make_unique( + static_cast(get_tree_view()), catalog_item_); +} + +/* ---------------------------------------------------------------------- */ + +AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view, + AssetCatalogTreeItem &catalog_item) + : ui::AbstractTreeViewItemDropController(tree_view), catalog_item_(catalog_item) +{ +} + +bool AssetCatalogDropController::can_drop(const wmDrag &drag) const { if (drag.type != WM_DRAG_ASSET_LIST) { return false; @@ -296,9 +328,7 @@ bool AssetCatalogTreeViewItem::can_drop(const wmDrag &drag) const return has_droppable_item(drag); } -std::string AssetCatalogTreeViewItem::drop_tooltip(const bContext & /*C*/, - const wmDrag &drag, - const wmEvent & /*event*/) const +std::string AssetCatalogDropController::drop_tooltip(const wmDrag &drag) const { const ListBase *asset_drags = WM_drag_asset_list_get(&drag); const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); @@ -312,10 +342,18 @@ std::string AssetCatalogTreeViewItem::drop_tooltip(const bContext & /*C*/, ")"; } -bool AssetCatalogTreeViewItem::drop_into_catalog(const AssetCatalogTreeView &tree_view, - const wmDrag &drag, - CatalogID catalog_id, - StringRefNull simple_name) +bool AssetCatalogDropController::on_drop(const wmDrag &drag) +{ + return drop_into_catalog(tree_view(), + drag, + catalog_item_.get_catalog_id(), + catalog_item_.get_simple_name()); +} + +bool AssetCatalogDropController::drop_into_catalog(const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name) { const ListBase *asset_drags = WM_drag_asset_list_get(&drag); if (!asset_drags) { @@ -339,28 +377,17 @@ bool AssetCatalogTreeViewItem::drop_into_catalog(const AssetCatalogTreeView &tre return true; } -bool AssetCatalogTreeViewItem::on_drop(const wmDrag &drag) +bool AssetCatalogDropController::has_droppable_item(const wmDrag &drag) { - const AssetCatalogTreeView &tree_view = static_cast( - get_tree_view()); - return drop_into_catalog( - tree_view, drag, catalog_item_.get_catalog_id(), catalog_item_.get_simple_name()); -} + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); -bool AssetCatalogTreeViewItem::can_rename() const -{ - return true; -} - -bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) -{ - /* Important to keep state. */ - BasicTreeViewItem::rename(new_name); - - const AssetCatalogTreeView &tree_view = static_cast( - get_tree_view()); - ED_asset_catalog_rename(tree_view.asset_library_, catalog_item_.get_catalog_id(), new_name); - return true; + /* There needs to be at least one asset from the current file. */ + LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) { + if (!asset_item->is_external) { + return true; + } + } + return false; } /* ---------------------------------------------------------------------- */ @@ -382,17 +409,28 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) /* ---------------------------------------------------------------------- */ -bool AssetCatalogTreeViewUnassignedItem::can_drop(const wmDrag &drag) const +std::unique_ptr AssetCatalogTreeViewUnassignedItem:: + create_drop_controller() const +{ + return std::make_unique( + static_cast(get_tree_view())); +} + +AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view) + : ui::AbstractTreeViewItemDropController(tree_view) +{ +} + +bool AssetCatalogTreeViewUnassignedItem::DropController::can_drop(const wmDrag &drag) const { if (drag.type != WM_DRAG_ASSET_LIST) { return false; } - return AssetCatalogTreeViewItem::has_droppable_item(drag); + return AssetCatalogDropController::has_droppable_item(drag); } -std::string AssetCatalogTreeViewUnassignedItem::drop_tooltip(const bContext & /*C*/, - const wmDrag &drag, - const wmEvent & /*event*/) const +std::string AssetCatalogTreeViewUnassignedItem::DropController::drop_tooltip( + const wmDrag &drag) const { const ListBase *asset_drags = WM_drag_asset_list_get(&drag); const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); @@ -401,16 +439,17 @@ std::string AssetCatalogTreeViewUnassignedItem::drop_tooltip(const bContext & /* TIP_("Move asset out of any catalog"); } -bool AssetCatalogTreeViewUnassignedItem::on_drop(const wmDrag &drag) +bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(const wmDrag &drag) { - const AssetCatalogTreeView &tree_view = static_cast( - get_tree_view()); /* Assign to nil catalog ID. */ - return AssetCatalogTreeViewItem::drop_into_catalog(tree_view, drag, CatalogID{}); + return AssetCatalogDropController::drop_into_catalog( + tree_view(), drag, CatalogID{}); } } // namespace blender::ed::asset_browser +/* ---------------------------------------------------------------------- */ + namespace blender::ed::asset_browser { class AssetCatalogFilterSettings { From 15762e961127fb8fbde1855311e01f99dfe5677e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 25 Oct 2021 09:58:16 +0200 Subject: [PATCH 1127/1500] Fix T92430: Infinite recursion in some cases in new append code. Shapekeys, always shapekeys... Since we cannot deal with them as regular IDs, we need to handle potential recursion cases ourselves here. sigh. --- source/blender/windowmanager/intern/wm_files_link.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index c88e577df6a..97e610b797d 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -583,8 +583,13 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) /* While we do not want to add non-linkable ID (shape keys...) to the list of linked items, * unfortunately they can use fully linkable valid IDs too, like actions. Those need to be * processed, so we need to recursively deal with them here. */ - BKE_library_foreach_ID_link( - cb_data->bmain, id, foreach_libblock_append_callback, data, IDWALK_NOP); + /* NOTE: Since we are by-passing checks in `BKE_library_foreach_ID_link` by manually calling it + * recursively, we need to take care of potential recursion cases ourselves (e.g.animdata of + * shapekey referencing the shapekey itself). */ + if (id != cb_data->id_self) { + BKE_library_foreach_ID_link( + cb_data->bmain, id, foreach_libblock_append_callback, data, IDWALK_NOP); + } return IDWALK_RET_NOP; } From 1ecb4e6fd8fcde581c8d79d9750a147361fa6915 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 25 Oct 2021 11:30:41 +0200 Subject: [PATCH 1128/1500] Fix T92446: node editor overlays reset automatically Caused by rBcf72b10075758be971f9806b97db01f98383aba2. The fix is to only enable the flags when a new node editor is actually created. --- source/blender/editors/space_node/space_node.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index bd2559c4d4d..8c015a56fa1 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -92,9 +92,6 @@ void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from) snode->id = id; snode->from = from; - snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS; - snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS; - ED_node_set_active_viewer_key(snode); WM_main_add_notifier(NC_SCENE | ND_NODES, NULL); @@ -259,6 +256,8 @@ static SpaceLink *node_create(const ScrArea *UNUSED(area), const Scene *UNUSED(s snode->spacetype = SPACE_NODE; snode->flag = SNODE_SHOW_GPENCIL | SNODE_USE_ALPHA; + snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS; + snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS; /* backdrop */ snode->zoom = 1.0f; From b3ca926aa8e1a2ea26dc5145cd361c211d786c63 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 25 Oct 2021 11:37:45 +0200 Subject: [PATCH 1129/1500] Geometry Nodes: use vector socket for offset in Set Position node This replaces the boolean Offset input in the Set Position node with a vector input. This makes the node easier to use. Using a "Position" input as an "Offset" sounds wrong anyway. The Position and Offset inputs are evaluated at the same time. The versioning only works correctly when the Offset input was not connected to something else before. Differential Revision: https://developer.blender.org/D12983 --- .../blenloader/intern/versioning_300.c | 47 +++++++++++++++++++ .../geometry/nodes/node_geo_set_position.cc | 11 ++--- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 8168b917b5e..f0bc910889b 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1218,6 +1218,52 @@ static void do_version_bones_roll(ListBase *lb) } } +static void version_geometry_nodes_set_position_node_offset(bNodeTree *ntree) +{ + /* Add the new Offset socket. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != GEO_NODE_SET_POSITION) { + continue; + } + bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3); + if (old_offset_socket->type == SOCK_VECTOR) { + /* Versioning happened already. */ + return; + } + /* Change identifier of old socket, so that the there is no name collision. */ + STRNCPY(old_offset_socket->identifier, "Offset_old"); + nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_VECTOR, PROP_TRANSLATION, "Offset", "Offset"); + } + + /* Relink links that were connected to Position while Offset was enabled. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->tonode->type != GEO_NODE_SET_POSITION) { + continue; + } + if (!STREQ(link->tosock->identifier, "Position")) { + continue; + } + bNodeSocket *old_offset_socket = BLI_findlink(&link->tonode->inputs, 3); + /* This assumes that the offset is not linked to something else. That seems to be a reasonable + * assumption, because the node is probably only ever used in one or the other mode. */ + const bool offset_enabled = + ((bNodeSocketValueBoolean *)old_offset_socket->default_value)->value; + if (offset_enabled) { + /* Relink to new offset socket. */ + link->tosock = old_offset_socket->next; + } + } + + /* Remove old Offset socket. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != GEO_NODE_SET_POSITION) { + continue; + } + bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3); + nodeRemoveSocket(ntree, node, old_offset_socket); + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -2038,6 +2084,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) continue; } version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString"); + version_geometry_nodes_set_position_node_offset(ntree); } /* Keep this block, even when empty. */ } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 15930508e78..1b6b6a5bdd7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -25,14 +25,14 @@ static void geo_node_set_position_declare(NodeDeclarationBuilder &b) b.add_input("Geometry"); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Position").implicit_field(); - b.add_input("Offset").default_value(false).supports_field(); + b.add_input("Offset").supports_field().subtype(PROP_TRANSLATION); b.add_output("Geometry"); } static void set_position_in_component(GeometryComponent &component, const Field &selection_field, const Field &position_field, - const Field &offset_field) + const Field &offset_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); @@ -58,11 +58,10 @@ static void set_position_in_component(GeometryComponent &component, * value or not */ const VArray &positions_input = position_evaluator.get_evaluated(0); - const VArray &offsets_input = position_evaluator.get_evaluated(1); + const VArray &offsets_input = position_evaluator.get_evaluated(1); for (int i : selection) { - position_mutable[i] = offsets_input[i] ? position_mutable[i] + positions_input[i] : - positions_input[i]; + position_mutable[i] = positions_input[i] + offsets_input[i]; } positions.save(); } @@ -71,7 +70,7 @@ static void geo_node_set_position_exec(GeoNodeExecParams params) { GeometrySet geometry = params.extract_input("Geometry"); Field selection_field = params.extract_input>("Selection"); - Field offset_field = params.extract_input>("Offset"); + Field offset_field = params.extract_input>("Offset"); Field position_field = params.extract_input>("Position"); for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, From 8e56f3e8a36d76ad1765f939c018a5ec3c01faab Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 25 Oct 2021 11:40:49 +0200 Subject: [PATCH 1130/1500] Image: Fix Crash During Undo. Fixes T91294. --- source/blender/blenkernel/intern/image.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 5ae338aaaeb..3800cbec94b 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -211,8 +211,12 @@ static void image_foreach_cache(ID *id, for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + GPUTexture *texture = image->gputexture[a][eye][resolution]; + if (texture == NULL) { + continue; + } key.offset_in_ID = offsetof(Image, gputexture[a][eye][resolution]); - key.cache_v = image->gputexture[a][eye]; + key.cache_v = texture; function_callback(id, &key, (void **)&image->gputexture[a][eye][resolution], 0, user_data); } } From beea601e7253f1eafbf43ce3c0e59497788335bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 25 Oct 2021 12:07:37 +0200 Subject: [PATCH 1131/1500] BKE Callbacks: more explicit initialisation check Add static boolean to track whether the callbacks system has been initialised. This makes it possible to make the `BKE_callback_remove()` function more noisy in case of programming errors, and avoids accessing `funcstore->alloc` when `funcstore` was potentially already freed. Thanks @campbellbarton for pointing this out. --- source/blender/blenkernel/intern/callbacks.c | 29 ++++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/callbacks.c b/source/blender/blenkernel/intern/callbacks.c index dbc213907ac..72dd51a940d 100644 --- a/source/blender/blenkernel/intern/callbacks.c +++ b/source/blender/blenkernel/intern/callbacks.c @@ -30,11 +30,20 @@ static ListBase callback_slots[BKE_CB_EVT_TOT] = {{NULL}}; +static bool callbacks_initialized = false; + +#define ASSERT_CALLBACKS_INITIALIZED() \ + BLI_assert_msg(callbacks_initialized, \ + "Callbacks should be initialized with BKE_callback_global_init() before using " \ + "the callback system.") + void BKE_callback_exec(struct Main *bmain, struct PointerRNA **pointers, const int num_pointers, eCbEvent evt) { + ASSERT_CALLBACKS_INITIALIZED(); + /* Use mutable iteration so handlers are able to remove themselves. */ ListBase *lb = &callback_slots[evt]; LISTBASE_FOREACH_MUTABLE (bCallbackFuncStore *, funcstore, lb) { @@ -75,18 +84,26 @@ void BKE_callback_exec_id_depsgraph(struct Main *bmain, void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt) { + ASSERT_CALLBACKS_INITIALIZED(); ListBase *lb = &callback_slots[evt]; BLI_addtail(lb, funcstore); } void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt) { - ListBase *lb = &callback_slots[evt]; - - /* Be safe, as the callback may have already been removed by BKE_callback_global_finalize(), for + /* The callback may have already been removed by BKE_callback_global_finalize(), for * example when removing callbacks in response to a BKE_blender_atexit_register callback * function. `BKE_blender_atexit()` runs after `BKE_callback_global_finalize()`. */ - BLI_remlink_safe(lb, funcstore); + if (!callbacks_initialized) { + return; + } + + ListBase *lb = &callback_slots[evt]; + + /* Be noisy about potential programming errors. */ + BLI_assert_msg(BLI_findindex(lb, funcstore) != -1, "To-be-removed callback not found"); + + BLI_remlink(lb, funcstore); if (funcstore->alloc) { MEM_freeN(funcstore); @@ -95,7 +112,7 @@ void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt) void BKE_callback_global_init(void) { - /* do nothing */ + callbacks_initialized = true; } /* call on application exit */ @@ -111,4 +128,6 @@ void BKE_callback_global_finalize(void) BKE_callback_remove(funcstore, evt); } } + + callbacks_initialized = false; } From 31f6e7837024672a895bba0390f6e25df0482162 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 22 Oct 2021 17:24:18 +0200 Subject: [PATCH 1132/1500] Fix T92073: Cycles flicker when panning in camera view with border render Panning in camera view makes the border to be modified, which was causing the Cycles display to believe the rendered result is unusable. The solution is to draw the render result at the display parameters it was updated for. This allows to avoid flickering during panning, zooming, and camera FOV changes. The suboptimal aspect of this is that it has some jelly effect, although it is on the same level as jelly effect of object outline so it is not terrible. Differential Revision: https://developer.blender.org/D12970 --- .../cycles/blender/blender_display_driver.cpp | 35 ++++++++++++++----- .../cycles/blender/blender_display_driver.h | 3 ++ .../cycles/integrator/path_trace_display.cpp | 19 +--------- intern/cycles/integrator/path_trace_display.h | 9 ----- 4 files changed, 30 insertions(+), 36 deletions(-) diff --git a/intern/cycles/blender/blender_display_driver.cpp b/intern/cycles/blender/blender_display_driver.cpp index f55a8ce8c4e..3870f4956f2 100644 --- a/intern/cycles/blender/blender_display_driver.cpp +++ b/intern/cycles/blender/blender_display_driver.cpp @@ -357,6 +357,8 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms, * centralized place. */ texture_.need_update = true; + texture_.params = params; + return true; } @@ -717,8 +719,23 @@ void BlenderDisplayDriver::texture_update_if_needed() texture_.need_update = false; } -void BlenderDisplayDriver::vertex_buffer_update(const Params ¶ms) +void BlenderDisplayDriver::vertex_buffer_update(const Params & /*params*/) { + /* Draw at the parameters for which the texture has been updated for. This allows to always draw + * texture during bordered-rendered camera view without flickering. The validness of the display + * parameters for a texture is guaranteed by the initial "clear" state which makes drawing to + * have an early output. + * + * Such approach can cause some extra "jelly" effect during panning, but it is not more jelly + * than overlay of selected objects. Also, it's possible to redraw texture at an intersection of + * the texture draw parameters and the latest updated draw paaremters (altohoyugh, complexity of + * doing it might not worth it. */ + const int x = texture_.params.full_offset.x; + const int y = texture_.params.full_offset.y; + + const int width = texture_.params.size.x; + const int height = texture_.params.size.y; + /* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be * rendered. */ glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW); @@ -730,23 +747,23 @@ void BlenderDisplayDriver::vertex_buffer_update(const Params ¶ms) vpointer[0] = 0.0f; vpointer[1] = 0.0f; - vpointer[2] = params.full_offset.x; - vpointer[3] = params.full_offset.y; + vpointer[2] = x; + vpointer[3] = y; vpointer[4] = 1.0f; vpointer[5] = 0.0f; - vpointer[6] = (float)params.size.x + params.full_offset.x; - vpointer[7] = params.full_offset.y; + vpointer[6] = x + width; + vpointer[7] = y; vpointer[8] = 1.0f; vpointer[9] = 1.0f; - vpointer[10] = (float)params.size.x + params.full_offset.x; - vpointer[11] = (float)params.size.y + params.full_offset.y; + vpointer[10] = x + width; + vpointer[11] = y + height; vpointer[12] = 0.0f; vpointer[13] = 1.0f; - vpointer[14] = params.full_offset.x; - vpointer[15] = (float)params.size.y + params.full_offset.y; + vpointer[14] = x; + vpointer[15] = y + height; glUnmapBuffer(GL_ARRAY_BUFFER); } diff --git a/intern/cycles/blender/blender_display_driver.h b/intern/cycles/blender/blender_display_driver.h index 558997c6b4f..5e7e9b01065 100644 --- a/intern/cycles/blender/blender_display_driver.h +++ b/intern/cycles/blender/blender_display_driver.h @@ -188,6 +188,9 @@ class BlenderDisplayDriver : public DisplayDriver { /* Dimensions of the underlying PBO. */ int buffer_width = 0; int buffer_height = 0; + + /* Display parameters the texture has been updated for. */ + Params params; } texture_; unique_ptr display_shader_; diff --git a/intern/cycles/integrator/path_trace_display.cpp b/intern/cycles/integrator/path_trace_display.cpp index 28f0a7f7745..e22989bb301 100644 --- a/intern/cycles/integrator/path_trace_display.cpp +++ b/intern/cycles/integrator/path_trace_display.cpp @@ -30,29 +30,16 @@ void PathTraceDisplay::reset(const BufferParams &buffer_params) { thread_scoped_lock lock(mutex_); - const DisplayDriver::Params old_params = params_; - params_.full_offset = make_int2(buffer_params.full_x, buffer_params.full_y); params_.full_size = make_int2(buffer_params.full_width, buffer_params.full_height); params_.size = make_int2(buffer_params.width, buffer_params.height); - /* If the parameters did change tag texture as unusable. This avoids drawing old texture content - * in an updated configuration of the viewport. For example, avoids drawing old frame when render - * border did change. - * If the parameters did not change, allow drawing the current state of the texture, which will - * not count as an up-to-date redraw. This will avoid flickering when doping camera navigation by - * showing a previously rendered frame for until the new one is ready. */ - if (old_params.modified(params_)) { - texture_state_.is_usable = false; - } - texture_state_.is_outdated = true; } void PathTraceDisplay::mark_texture_updated() { texture_state_.is_outdated = false; - texture_state_.is_usable = true; } /* -------------------------------------------------------------------- @@ -248,19 +235,15 @@ bool PathTraceDisplay::draw() * The drawing itself is non-blocking however, for better performance and to avoid * potential deadlocks due to locks held by the subclass. */ DisplayDriver::Params params; - bool is_usable; bool is_outdated; { thread_scoped_lock lock(mutex_); params = params_; - is_usable = texture_state_.is_usable; is_outdated = texture_state_.is_outdated; } - if (is_usable) { - driver_->draw(params); - } + driver_->draw(params); return !is_outdated; } diff --git a/intern/cycles/integrator/path_trace_display.h b/intern/cycles/integrator/path_trace_display.h index 24aaa0df6b1..99d2e0e1203 100644 --- a/intern/cycles/integrator/path_trace_display.h +++ b/intern/cycles/integrator/path_trace_display.h @@ -174,15 +174,6 @@ class PathTraceDisplay { /* State of the texture, which is needed for an integration with render session and interactive * updates and navigation. */ struct { - /* Denotes whether possibly existing state of GPU side texture is still usable. - * It will not be usable in cases like render border did change (in this case we don't want - * previous texture to be rendered at all). - * - * However, if only navigation or object in scene did change, then the outdated state of the - * texture is still usable for draw, preventing display viewport flickering on navigation and - * object modifications. */ - bool is_usable = false; - /* Texture is considered outdated after `reset()` until the next call of * `copy_pixels_to_texture()`. */ bool is_outdated = true; From 550cbec5c4dcf785d382950e2651a423f55b2f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 25 Oct 2021 12:35:21 +0200 Subject: [PATCH 1133/1500] Cleanup: asset catalog path test, improve sub-test separation Put related lines in a block of their own, such that each block doesn't have access to the variables of the previous blocks. This makes it easier to correctly copy-paste some tests, as the compiler forces you to update the code afterwards. --- .../intern/asset_catalog_path_test.cc | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc index be50f2fc001..997f57a161c 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc @@ -193,19 +193,21 @@ TEST(AssetCatalogPathTest, is_contained_in) TEST(AssetCatalogPathTest, cleanup) { - AssetCatalogPath ugly_path("/ some / родитель / "); - AssetCatalogPath clean_path = ugly_path.cleanup(); - - EXPECT_EQ(AssetCatalogPath("/ some / родитель / "), ugly_path) - << "cleanup should not modify the path instance itself"; - - EXPECT_EQ(AssetCatalogPath("some/родитель"), clean_path); - - AssetCatalogPath double_slashed("some//родитель"); - EXPECT_EQ(AssetCatalogPath("some/родитель"), double_slashed.cleanup()); - - AssetCatalogPath with_colons("some/key:subkey=value/path"); - EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup()); + { + AssetCatalogPath ugly_path("/ some / родитель / "); + AssetCatalogPath clean_path = ugly_path.cleanup(); + EXPECT_EQ(AssetCatalogPath("/ some / родитель / "), ugly_path) + << "cleanup should not modify the path instance itself"; + EXPECT_EQ(AssetCatalogPath("some/родитель"), clean_path); + } + { + AssetCatalogPath double_slashed("some//родитель"); + EXPECT_EQ(AssetCatalogPath("some/родитель"), double_slashed.cleanup()); + } + { + AssetCatalogPath with_colons("some/key:subkey=value/path"); + EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup()); + } } TEST(AssetCatalogPathTest, iterate_components) From 892e5f4a9f8a3f4e67a6ff00e4195b628decc127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 25 Oct 2021 12:36:47 +0200 Subject: [PATCH 1134/1500] Asset Catalogs: be more accepting of backslashes as separators Asset Catalog Paths should only contain forward slashes as separators, but now the UI is more resilient to people using blackslashes instead. Manifest Task: T90553 --- .../blenkernel/intern/asset_catalog_path.cc | 2 + .../intern/asset_catalog_path_test.cc | 12 ++++++ .../blenkernel/intern/asset_catalog_test.cc | 43 ++++++++++++++++++- .../editors/asset/intern/asset_catalog.cc | 8 ++-- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc index fec2b76e7a1..20cac76b40b 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path.cc @@ -197,6 +197,8 @@ void AssetCatalogPath::iterate_components(ComponentIteratorFn callback) const for (const char *path_component = this->path_.data(); path_component && path_component[0]; /* Jump to one after the next slash if there is any. */ path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) { + /* Note that this also treats backslashes as component separators, which + * helps in cleaning up backslash-separated paths. */ next_slash_ptr = BLI_path_slash_find(path_component); const bool is_last_component = next_slash_ptr == nullptr; diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc index 997f57a161c..f248863ce77 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc @@ -208,6 +208,18 @@ TEST(AssetCatalogPathTest, cleanup) AssetCatalogPath with_colons("some/key:subkey=value/path"); EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup()); } + { + const AssetCatalogPath with_backslashes("windows\\for\\life"); + EXPECT_EQ(AssetCatalogPath("windows/for/life"), with_backslashes.cleanup()); + } + { + const AssetCatalogPath with_mixed("windows\\for/life"); + EXPECT_EQ(AssetCatalogPath("windows/for/life"), with_mixed.cleanup()); + } + { + const AssetCatalogPath with_punctuation("is!/this?/¿valid?"); + EXPECT_EQ(AssetCatalogPath("is!/this?/¿valid?"), with_punctuation.cleanup()); + } } TEST(AssetCatalogPathTest, iterate_components) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index abe6d384247..2cef34966f8 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -902,8 +902,6 @@ TEST_F(AssetCatalogTest, update_catalog_path) const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA); ASSERT_NE(nullptr, renamed_cat); ASSERT_EQ(orig_cat, renamed_cat) << "Changing the path should not reallocate the catalog."; - EXPECT_EQ(orig_cat->simple_name, renamed_cat->simple_name) - << "Changing the path should not change the simple name."; EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id) << "Changing the path should not change the catalog ID."; @@ -932,6 +930,47 @@ TEST_F(AssetCatalogTest, update_catalog_path_simple_name) << "Changing the path should update the simplename of children."; } +TEST_F(AssetCatalogTest, update_catalog_path_add_slashes) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA); + const AssetCatalogPath orig_path = orig_cat->path; + + /* Original path is `character/Ružena/poselib`. + * This rename will also create a new catalog for `character/Ružena/poses`. */ + service.update_catalog_path(UUID_POSES_RUZENA, "character/Ružena/poses/general"); + + EXPECT_EQ(nullptr, service.find_catalog_by_path(orig_path)) + << "The original (pre-rename) path should not be associated with a catalog any more."; + + const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA); + ASSERT_NE(nullptr, renamed_cat); + EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id) + << "Changing the path should not change the catalog ID."; + + EXPECT_EQ("character/Ružena/poses/general", renamed_cat->path.str()) + << "When creating a new catalog by renaming + adding a slash, the renamed catalog should be " + "assigned the path passed to update_catalog_path()"; + + /* Test the newly created catalog. */ + const AssetCatalog *new_cat = service.find_catalog_by_path("character/Ružena/poses"); + ASSERT_NE(nullptr, new_cat) << "Renaming to .../X/Y should cause .../X to exist as well."; + EXPECT_EQ("character/Ružena/poses", new_cat->path.str()); + EXPECT_EQ("character-Ružena-poses", new_cat->simple_name); + EXPECT_TRUE(new_cat->flags.has_unsaved_changes); + + /* Test the children. */ + EXPECT_EQ("character/Ružena/poses/general/hand", + service.find_catalog(UUID_POSES_RUZENA_HAND)->path.str()) + << "Changing the path should update children."; + EXPECT_EQ("character/Ružena/poses/general/face", + service.find_catalog(UUID_POSES_RUZENA_FACE)->path.str()) + << "Changing the path should update children."; +} + TEST_F(AssetCatalogTest, merge_catalog_files) { const CatalogFilePath cdf_dir = create_temp_path(); diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index dae960cbb0a..f3ba12a6324 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -107,17 +107,17 @@ void ED_asset_catalog_rename(::AssetLibrary *library, AssetCatalog *catalog = catalog_service->find_catalog(catalog_id); - AssetCatalogPath new_path = catalog->path.parent(); - new_path = new_path / StringRef(new_name); + const AssetCatalogPath new_path = catalog->path.parent() / StringRef(new_name); + const AssetCatalogPath clean_new_path = new_path.cleanup(); - if (new_path == catalog->path) { + if (new_path == catalog->path || clean_new_path == catalog->path) { /* Nothing changed, so don't bother renaming for nothing. */ return; } catalog_service->undo_push(); catalog_service->tag_has_unsaved_changes(catalog); - catalog_service->update_catalog_path(catalog_id, new_path); + catalog_service->update_catalog_path(catalog_id, clean_new_path); WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); } From 06ae3c98ebf549959454e25b0fb7fd8a0405af76 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 25 Oct 2021 12:46:28 +0200 Subject: [PATCH 1135/1500] Spreadsheet: fix updating active domain when component type changes The mesh and instances case wasn't handled before. --- source/blender/makesrna/intern/rna_space.c | 34 ++++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index c91ef25daa8..03976967e9f 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3074,12 +3074,34 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma PointerRNA *ptr) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data; - if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) { - sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; - } - if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_CURVE && - !ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { - sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + switch (sspreadsheet->geometry_component_type) { + case GEO_COMPONENT_TYPE_MESH: { + if (!ELEM(sspreadsheet->attribute_domain, + ATTR_DOMAIN_POINT, + ATTR_DOMAIN_EDGE, + ATTR_DOMAIN_FACE, + ATTR_DOMAIN_CORNER)) { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + } + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + if (!ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + } + break; + } } } From 3c2e4f4cfd57944244bb80b93539af8a9bb98130 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 15 Sep 2021 08:43:21 +0200 Subject: [PATCH 1136/1500] Fix T91411: Outliner crash using contextmenu operators from a shortcut Oversight in {rBb0741e1dcbc5}. This was guarded by an assert in `get_target_element`, but it can be valid to have these assigned to a shortcut (and then perform the action without an active outliner element). Now remove the assert and let the operator polls check if we really have a target element. note: this basically makes `get_target_element` obsolete, could call `outliner_find_element_with_flag` instead in all cases. Maniphest Tasks: T91411 Differential Revision: https://developer.blender.org/D12495 --- .../editors/space_outliner/outliner_tools.c | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 75bdc5dbac6..515fe8fe228 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -197,11 +197,24 @@ static void get_element_operation_type( static TreeElement *get_target_element(SpaceOutliner *space_outliner) { TreeElement *te = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE); - BLI_assert(te); return te; } +static bool outliner_operation_tree_element_poll(bContext *C) +{ + if (!ED_operator_outliner_active(C)) { + return false; + } + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + TreeElement *te = get_target_element(space_outliner); + if (te == NULL) { + return false; + } + + return true; +} + static void unlink_action_fn(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), @@ -1868,6 +1881,10 @@ static bool outliner_id_operation_item_poll(bContext *C, PropertyRNA *UNUSED(prop), const int enum_value) { + if (!outliner_operation_tree_element_poll(C)) { + return false; + } + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); TreeElement *te = get_target_element(space_outliner); TreeStoreElem *tselem = TREESTORE(te); @@ -2254,7 +2271,7 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = outliner_id_operation_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; ot->flag = 0; @@ -2361,7 +2378,7 @@ void OUTLINER_OT_lib_operation(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = outliner_lib_operation_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; ot->prop = RNA_def_enum( ot->srna, "type", outliner_lib_op_type_items, 0, "Library Operation", ""); @@ -2420,14 +2437,8 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - bAction *act; - /* check for invalid states */ - if (space_outliner == NULL) { - return OPERATOR_CANCELLED; - } - TreeElement *te = get_target_element(space_outliner); get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); @@ -2482,7 +2493,7 @@ void OUTLINER_OT_action_set(wmOperatorType *ot) /* api callbacks */ ot->invoke = WM_enum_search_invoke; ot->exec = outliner_action_set_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2531,12 +2542,6 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) wmWindowManager *wm = CTX_wm_manager(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - - /* check for invalid states */ - if (space_outliner == NULL) { - return OPERATOR_CANCELLED; - } - TreeElement *te = get_target_element(space_outliner); get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); @@ -2722,12 +2727,6 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - - /* check for invalid states */ - if (space_outliner == NULL) { - return OPERATOR_CANCELLED; - } - TreeElement *te = get_target_element(space_outliner); get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); @@ -2806,6 +2805,13 @@ static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C, return DummyRNA_DEFAULT_items; } + TreeElement *te = get_target_element(space_outliner); + if (te == NULL) { + return DummyRNA_NULL_items; + } + + TreeStoreElem *tselem = TREESTORE(te); + static const EnumPropertyItem optype_sel_and_hide[] = { {OL_DOP_SELECT, "SELECT", 0, "Select", ""}, {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""}, @@ -2816,9 +2822,6 @@ static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C, static const EnumPropertyItem optype_sel_linked[] = { {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""}, {0, NULL, 0, NULL, NULL}}; - TreeElement *te = get_target_element(space_outliner); - TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == TSE_RNA_STRUCT) { return optype_sel_linked; } @@ -2835,7 +2838,7 @@ void OUTLINER_OT_data_operation(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = outliner_data_operation_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; ot->flag = 0; From 2b91445ddd0d24e4ac362c33b79a9e9c8e63f5d5 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 25 Oct 2021 12:51:21 +0200 Subject: [PATCH 1137/1500] Cleanup: rename Outliner function Seems like typos in rB32dc085289ac outline_batch_delete_hierarchy --> outliner_batch_delete_hierarchy Differential Revision: https://developer.blender.org/D12989 --- source/blender/editors/space_outliner/outliner_tools.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 515fe8fe228..ae2b1870884 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -1439,7 +1439,7 @@ static void outliner_do_data_operation( } } -static Base *outline_batch_delete_hierarchy( +static Base *outliner_batch_delete_hierarchy( ReportList *reports, Main *bmain, ViewLayer *view_layer, Scene *scene, Base *base) { Base *child_base, *base_next; @@ -1457,7 +1457,7 @@ static Base *outline_batch_delete_hierarchy( /* pass */ } if (parent) { - base_next = outline_batch_delete_hierarchy(reports, bmain, view_layer, scene, child_base); + base_next = outliner_batch_delete_hierarchy(reports, bmain, view_layer, scene, child_base); } } @@ -1510,7 +1510,7 @@ static void object_batch_delete_hierarchy_fn(bContext *C, ED_object_editmode_exit(C, EM_FREEDATA); } - outline_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base); + outliner_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base); } } From 039094c1ec50636cdff89132a67598841889cda9 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 25 Oct 2021 13:02:43 +0200 Subject: [PATCH 1138/1500] Geometry Nodes: new Image Texture node This adds a new image texture node for geometry nodes. It does not reuse the same node that is used in shading, because we want to be able to expose the image and frame as sockets. There is a known update issue when a movie or image sequence is used. That will be fixed separately (also see D12957). Currently, the image socket is just a pointer to an Image ID data block. This can contain single images but also movies and image sequences. In the future, the definition of an image socket can be expanded to include images that are generated from scratch in the node tree. For more details read the discussion in D12827. Some of the code is a direct port from cycles and should be cleaned up a bit in the future. For example `image_cubic_texture_lookup`. For still images, the frame input is ignored. Otherwise, the frame has to be in a valid range for the node to work. In the future we may add e.g. automatic looping functionality. Differential Revision: https://developer.blender.org/D12827 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/editors/space_node/drawnode.cc | 34 +- source/blender/editors/space_node/node_add.cc | 14 +- source/blender/makesdna/DNA_node_types.h | 5 + source/blender/makesrna/intern/rna_nodetree.c | 51 ++- source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../geometry/nodes/node_geo_image_texture.cc | 428 ++++++++++++++++++ 11 files changed, 530 insertions(+), 8 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_image_texture.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 0a34f541e5c..13adde5c439 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -735,6 +735,7 @@ geometry_node_categories = [ NodeItem("ShaderNodeTexVoronoi"), NodeItem("ShaderNodeTexWave"), NodeItem("ShaderNodeTexWhiteNoise"), + NodeItem("GeometryNodeImageTexture"), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index ef1e7249658..c868eb414f7 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1547,6 +1547,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_RAYCAST 1128 #define GEO_NODE_CURVE_TO_POINTS 1130 #define GEO_NODE_INSTANCES_TO_POINTS 1131 +#define GEO_NODE_IMAGE_TEXTURE 1132 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index eda57d9e984..0452481849b 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5800,6 +5800,7 @@ static void registerGeometryNodes() register_node_type_geo_delete_geometry(); register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_edge_split(); + register_node_type_geo_image_texture(); register_node_type_geo_input_curve_handles(); register_node_type_geo_input_curve_tilt(); register_node_type_geo_input_index(); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 8a63a1f3505..dfbb2132f1b 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3619,7 +3619,39 @@ static void std_node_socket_draw( break; } case SOCK_IMAGE: { - uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; + if (node_tree->type == NTREE_GEOMETRY) { + if (text[0] == '\0') { + uiTemplateID(layout, + C, + ptr, + "default_value", + "image.new", + "image.open", + nullptr, + 0, + ICON_NONE, + nullptr); + } + else { + /* 0.3 split ratio is inconsistent, but use it here because the "New" button is large. */ + uiLayout *row = uiLayoutSplit(layout, 0.3f, false); + uiItemL(row, text, 0); + uiTemplateID(row, + C, + ptr, + "default_value", + "image.new", + "image.open", + nullptr, + 0, + ICON_NONE, + nullptr); + } + } + else { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + } break; } case SOCK_COLLECTION: { diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 7b6ca5e6e61..cb66d0dbd2b 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -758,7 +758,7 @@ static bool node_add_file_poll(bContext *C) { const SpaceNode *snode = CTX_wm_space_node(C); return ED_operator_node_editable(C) && - ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT); + ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT, NTREE_GEOMETRY); } static int node_add_file_exec(bContext *C, wmOperator *op) @@ -784,6 +784,9 @@ static int node_add_file_exec(bContext *C, wmOperator *op) case NTREE_COMPOSIT: type = CMP_NODE_IMAGE; break; + case NTREE_GEOMETRY: + type = GEO_NODE_IMAGE_TEXTURE; + break; default: return OPERATOR_CANCELLED; } @@ -797,7 +800,14 @@ static int node_add_file_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - node->id = (ID *)ima; + if (type == GEO_NODE_IMAGE_TEXTURE) { + bNodeSocket *image_socket = (bNodeSocket *)node->inputs.first; + bNodeSocketValueImage *socket_value = (bNodeSocketValueImage *)image_socket->default_value; + socket_value->value = ima; + } + else { + node->id = (ID *)ima; + } /* When adding new image file via drag-drop we need to load imbuf in order * to get proper image source. diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index b585cbd6306..c2eb67b7dd8 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1581,6 +1581,11 @@ typedef struct NodeGeometrySeparateGeometry { int8_t domain; } NodeGeometrySeparateGeometry; +typedef struct NodeGeometryImageTexture { + int interpolation; + int extension; +} NodeGeometryImageTexture; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index f0508ee5317..8daf311fbfe 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -5022,10 +5022,8 @@ static void def_fn_input_bool(StructRNA *srna) prop = RNA_def_property(srna, "boolean", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "boolean", 1); - RNA_def_property_ui_text( - prop, "Boolean", "Input value used for unconnected socket"); + RNA_def_property_ui_text(prop, "Boolean", "Input value used for unconnected socket"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - } static void def_fn_input_int(StructRNA *srna) @@ -5037,8 +5035,7 @@ static void def_fn_input_int(StructRNA *srna) prop = RNA_def_property(srna, "integer", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "integer"); RNA_def_property_int_default(prop, 1); - RNA_def_property_ui_text( - prop, "Integer", "Input value used for unconnected socket"); + RNA_def_property_ui_text(prop, "Integer", "Input value used for unconnected socket"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -5416,6 +5413,50 @@ static void def_sh_tex_image(StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Node_update"); } +static void def_geo_image_texture(StructRNA *srna) +{ + static const EnumPropertyItem fn_tex_prop_interpolation_items[] = { + {SHD_INTERP_LINEAR, "Linear", 0, "Linear", "Linear interpolation"}, + {SHD_INTERP_CLOSEST, "Closest", 0, "Closest", "No interpolation (sample closest texel)"}, + {SHD_INTERP_CUBIC, "Cubic", 0, "Cubic", "Cubic interpolation"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem prop_image_extension[] = { + {SHD_IMAGE_EXTENSION_REPEAT, + "REPEAT", + 0, + "Repeat", + "Cause the image to repeat horizontally and vertically"}, + {SHD_IMAGE_EXTENSION_EXTEND, + "EXTEND", + 0, + "Extend", + "Extend by repeating edge pixels of the image"}, + {SHD_IMAGE_EXTENSION_CLIP, + "CLIP", + 0, + "Clip", + "Clip to image size and set exterior pixels as transparent"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryImageTexture", "storage"); + + prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, fn_tex_prop_interpolation_items); + RNA_def_property_ui_text(prop, "Interpolation", "Method for smoothing values between pixels"); + RNA_def_property_update(prop, 0, "rna_Node_update"); + + prop = RNA_def_property(srna, "extension", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_image_extension); + RNA_def_property_ui_text( + prop, "Extension", "How the image is extrapolated past its original bounds"); + RNA_def_property_update(prop, 0, "rna_Node_update"); +} + static void def_sh_tex_gradient(StructRNA *srna) { static const EnumPropertyItem prop_gradient_type[] = { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 7a728b9041b..657d0d11441 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -226,6 +226,7 @@ set(SRC geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_distribute_points_on_faces.cc geometry/nodes/node_geo_edge_split.cc + geometry/nodes/node_geo_image_texture.cc geometry/nodes/node_geo_input_curve_handles.cc geometry/nodes/node_geo_input_curve_tilt.cc geometry/nodes/node_geo_input_index.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index a37dcf28757..6702443e20e 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -94,6 +94,7 @@ void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_edge_split(void); +void register_node_type_geo_image_texture(void); void register_node_type_geo_input_curve_handles(void); void register_node_type_geo_input_curve_tilt(void); void register_node_type_geo_input_index(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index af14538e6cc..29cbe0d13c8 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -355,6 +355,7 @@ DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "") +DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc new file mode 100644 index 00000000000..02f99b6cf94 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc @@ -0,0 +1,428 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_geometry_util.hh" + +#include "BKE_image.h" + +#include "BLI_float4.hh" +#include "BLI_threads.h" +#include "BLI_timeit.hh" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_image_texture_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Image").hide_label(); + b.add_input("Vector").implicit_field().description( + "Texture coordinates from 0 to 1"); + b.add_input("Frame").min(0).max(MAXFRAMEF); + b.add_output("Color").no_muted_links().dependent_field(); + b.add_output("Alpha").no_muted_links().dependent_field(); +} + +static void geo_node_image_texture_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "extension", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void geo_node_image_texture_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryImageTexture *tex = (NodeGeometryImageTexture *)MEM_callocN( + sizeof(NodeGeometryImageTexture), __func__); + node->storage = tex; +} + +class ImageFieldsFunction : public fn::MultiFunction { + private: + const int interpolation_; + const int extension_; + Image &image_; + ImageUser image_user_; + void *image_lock_; + ImBuf *image_buffer_; + + public: + ImageFieldsFunction(const int interpolation, + const int extension, + Image &image, + ImageUser image_user) + : interpolation_(interpolation), + extension_(extension), + image_(image), + image_user_(image_user) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + + image_buffer_ = BKE_image_acquire_ibuf(&image_, &image_user_, &image_lock_); + if (image_buffer_ == nullptr) { + throw std::runtime_error("cannot aquire image buffer"); + } + + if (image_buffer_->rect_float == nullptr) { + BLI_thread_lock(LOCK_IMAGE); + if (!image_buffer_->rect_float) { + IMB_float_from_rect(image_buffer_); + } + BLI_thread_unlock(LOCK_IMAGE); + } + + if (image_buffer_->rect_float == nullptr) { + BKE_image_release_ibuf(&image_, image_buffer_, image_lock_); + throw std::runtime_error("cannot get float buffer"); + } + } + + ~ImageFieldsFunction() override + { + BKE_image_release_ibuf(&image_, image_buffer_, image_lock_); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"ImageFunction"}; + signature.single_input("Vector"); + signature.single_output("Color"); + signature.single_output("Alpha"); + return signature.build(); + } + + static int wrap_periodic(int x, const int width) + { + x %= width; + if (x < 0) { + x += width; + } + return x; + } + + static int wrap_clamp(const int x, const int width) + { + return std::clamp(x, 0, width - 1); + } + + static float4 image_pixel_lookup(const ImBuf *ibuf, const int px, const int py) + { + if (px < 0 || py < 0 || px >= ibuf->x || py >= ibuf->y) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + return ((const float4 *)ibuf->rect_float)[px + py * ibuf->x]; + } + + static float frac(const float x, int *ix) + { + const int i = (int)x - ((x < 0.0f) ? 1 : 0); + *ix = i; + return x - (float)i; + } + + static float4 image_cubic_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int pix, piy, nix, niy; + const float tx = frac(px * (float)width - 0.5f, &pix); + const float ty = frac(py * (float)height - 0.5f, &piy); + int ppix, ppiy, nnix, nniy; + + switch (extension) { + case SHD_IMAGE_EXTENSION_REPEAT: { + pix = wrap_periodic(pix, width); + piy = wrap_periodic(piy, height); + ppix = wrap_periodic(pix - 1, width); + ppiy = wrap_periodic(piy - 1, height); + nix = wrap_periodic(pix + 1, width); + niy = wrap_periodic(piy + 1, height); + nnix = wrap_periodic(pix + 2, width); + nniy = wrap_periodic(piy + 2, height); + break; + } + case SHD_IMAGE_EXTENSION_CLIP: { + ppix = pix - 1; + ppiy = piy - 1; + nix = pix + 1; + niy = piy + 1; + nnix = pix + 2; + nniy = piy + 2; + break; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + ppix = wrap_clamp(pix - 1, width); + ppiy = wrap_clamp(piy - 1, height); + nix = wrap_clamp(pix + 1, width); + niy = wrap_clamp(piy + 1, height); + nnix = wrap_clamp(pix + 2, width); + nniy = wrap_clamp(piy + 2, height); + pix = wrap_clamp(pix, width); + piy = wrap_clamp(piy, height); + break; + } + default: + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + + const int xc[4] = {ppix, pix, nix, nnix}; + const int yc[4] = {ppiy, piy, niy, nniy}; + float u[4], v[4]; + + u[0] = (((-1.0f / 6.0f) * tx + 0.5f) * tx - 0.5f) * tx + (1.0f / 6.0f); + u[1] = ((0.5f * tx - 1.0f) * tx) * tx + (2.0f / 3.0f); + u[2] = ((-0.5f * tx + 0.5f) * tx + 0.5f) * tx + (1.0f / 6.0f); + u[3] = (1.0f / 6.0f) * tx * tx * tx; + + v[0] = (((-1.0f / 6.0f) * ty + 0.5f) * ty - 0.5f) * ty + (1.0f / 6.0f); + v[1] = ((0.5f * ty - 1.0f) * ty) * ty + (2.0f / 3.0f); + v[2] = ((-0.5f * ty + 0.5f) * ty + 0.5f) * ty + (1.0f / 6.0f); + v[3] = (1.0f / 6.0f) * ty * ty * ty; + + return (v[0] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[0])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[0])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[0])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[0])))) + + (v[1] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[1])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[1])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[1])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[1])))) + + (v[2] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[2])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[2])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[2])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[2])))) + + (v[3] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[3])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[3])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[3])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[3])))); + } + + static float4 image_linear_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int pix, piy, nix, niy; + const float nfx = frac(px * (float)width - 0.5f, &pix); + const float nfy = frac(py * (float)height - 0.5f, &piy); + + switch (extension) { + case SHD_IMAGE_EXTENSION_CLIP: { + nix = pix + 1; + niy = piy + 1; + break; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + nix = wrap_clamp(pix + 1, width); + niy = wrap_clamp(piy + 1, height); + pix = wrap_clamp(pix, width); + piy = wrap_clamp(piy, height); + break; + } + default: + case SHD_IMAGE_EXTENSION_REPEAT: + pix = wrap_periodic(pix, width); + piy = wrap_periodic(piy, height); + nix = wrap_periodic(pix + 1, width); + niy = wrap_periodic(piy + 1, height); + break; + } + + const float ptx = 1.0f - nfx; + const float pty = 1.0f - nfy; + + return image_pixel_lookup(ibuf, pix, piy) * ptx * pty + + image_pixel_lookup(ibuf, nix, piy) * nfx * pty + + image_pixel_lookup(ibuf, pix, niy) * ptx * nfy + + image_pixel_lookup(ibuf, nix, niy) * nfx * nfy; + } + + static float4 image_closest_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int ix, iy; + const float tx = frac(px * (float)width - 0.5f, &ix); + const float ty = frac(py * (float)height - 0.5f, &iy); + + switch (extension) { + case SHD_IMAGE_EXTENSION_REPEAT: { + ix = wrap_periodic(ix, width); + iy = wrap_periodic(iy, height); + return image_pixel_lookup(ibuf, ix, iy); + } + case SHD_IMAGE_EXTENSION_CLIP: { + if (tx < 0.0f || ty < 0.0f || tx > 1.0f || ty > 1.0f) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + if (ix < 0 || iy < 0 || ix > width || iy > height) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + ATTR_FALLTHROUGH; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + ix = wrap_clamp(ix, width); + iy = wrap_clamp(iy, height); + return image_pixel_lookup(ibuf, ix, iy); + } + default: + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &vectors = params.readonly_single_input(0, "Vector"); + MutableSpan r_color = params.uninitialized_single_output( + 1, "Color"); + MutableSpan r_alpha = params.uninitialized_single_output_if_required(2, "Alpha"); + + MutableSpan color_data{(float4 *)r_color.data(), r_color.size()}; + + /* Sample image texture. */ + switch (interpolation_) { + case SHD_INTERP_LINEAR: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_linear_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + case SHD_INTERP_CLOSEST: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_closest_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + case SHD_INTERP_CUBIC: + case SHD_INTERP_SMART: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_cubic_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + } + + int alpha_mode = image_.alpha_mode; + if (IMB_colormanagement_space_name_is_data(image_.colorspace_settings.name)) { + alpha_mode = IMA_ALPHA_CHANNEL_PACKED; + } + + switch (alpha_mode) { + case IMA_ALPHA_STRAIGHT: { + /* #ColorGeometry expects premultiplied alpha, so convert from straight to that. */ + for (int64_t i : mask) { + straight_to_premul_v4(color_data[i]); + } + break; + } + case IMA_ALPHA_PREMUL: { + /* Alpha is premultiplied already, nothing to do. */ + break; + } + case IMA_ALPHA_CHANNEL_PACKED: { + /* Color and alpha channels shouldn't interact with each other, nothing to do. */ + break; + } + case IMA_ALPHA_IGNORE: { + /* The image should be treated as being opaque. */ + for (int64_t i : mask) { + color_data[i].w = 1.0f; + } + break; + } + } + + if (!r_alpha.is_empty()) { + for (int64_t i : mask) { + r_alpha[i] = r_color[i].a; + } + } + } +}; + +static void geo_node_image_texture_exec(GeoNodeExecParams params) +{ + auto return_default = [&]() { + params.set_output("Color", ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + params.set_output("Alpha", 1.0f); + }; + + Image *image = params.get_input("Image"); + if (image == nullptr) { + return return_default(); + } + + const bNode &node = params.node(); + NodeGeometryImageTexture *data = (NodeGeometryImageTexture *)node.storage; + + ImageUser image_user; + BKE_imageuser_default(&image_user); + image_user.cycl = false; + image_user.frames = INT_MAX; + image_user.sfra = 1; + image_user.framenr = BKE_image_is_animated(image) ? params.get_input("Frame") : 0; + + std::unique_ptr image_fn; + try { + image_fn = std::make_unique( + data->interpolation, data->extension, *image, image_user); + } + catch (const std::runtime_error &) { + return return_default(); + } + + Field vector_field = params.extract_input>("Vector"); + + auto image_op = std::make_shared( + FieldOperation(std::move(image_fn), {std::move(vector_field)})); + + params.set_output("Color", Field(image_op, 0)); + params.set_output("Alpha", Field(image_op, 1)); +} + +} // namespace blender::nodes + +void register_node_type_geo_image_texture(void) +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_IMAGE_TEXTURE, "Image Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::geo_node_image_texture_declare; + ntype.draw_buttons = blender::nodes::geo_node_image_texture_layout; + node_type_init(&ntype, blender::nodes::geo_node_image_texture_init); + node_type_storage( + &ntype, "NodeGeometryImageTexture", node_free_standard_storage, node_copy_standard_storage); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + ntype.geometry_node_execute = blender::nodes::geo_node_image_texture_exec; + + nodeRegisterType(&ntype); +} From 61bb70e0c7a2099639ebe3b87d770cc4d4023001 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 25 Oct 2021 13:14:43 +0200 Subject: [PATCH 1139/1500] GPencil: Fix unreported double stroke color in materials panel There was an extra stroke color in the side panel, but this prop must be visible only in Topbar. --- .../scripts/startup/bl_ui/properties_grease_pencil_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index c31881fa194..6ca13674234 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -591,7 +591,7 @@ class GreasePencilMaterialsPanel: if len(ob.material_slots) > 0 and ob.active_material_index >= 0: ma = ob.material_slots[ob.active_material_index].material - if ma is not None and ma.grease_pencil is not None: + if is_view3d and ma is not None and ma.grease_pencil is not None: gpcolor = ma.grease_pencil if gpcolor.stroke_style == 'SOLID': row = layout.row() From 8cecf88dcaa82560d6849e2532a3e84b459ef19f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 25 Oct 2021 22:25:50 +1100 Subject: [PATCH 1140/1500] Cleanup: clang-tidy, spelling --- intern/cycles/blender/blender_display_driver.cpp | 2 +- intern/cycles/render/scene.cpp | 2 +- .../editors/interface/interface_template_attribute_search.cc | 2 +- source/blender/editors/interface/tree_view.cc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/intern/cycles/blender/blender_display_driver.cpp b/intern/cycles/blender/blender_display_driver.cpp index 3870f4956f2..cdf175f91d0 100644 --- a/intern/cycles/blender/blender_display_driver.cpp +++ b/intern/cycles/blender/blender_display_driver.cpp @@ -728,7 +728,7 @@ void BlenderDisplayDriver::vertex_buffer_update(const Params & /*params*/) * * Such approach can cause some extra "jelly" effect during panning, but it is not more jelly * than overlay of selected objects. Also, it's possible to redraw texture at an intersection of - * the texture draw parameters and the latest updated draw paaremters (altohoyugh, complexity of + * the texture draw parameters and the latest updated draw parameters (although, complexity of * doing it might not worth it. */ const int x = texture_.params.full_offset.x; const int y = texture_.params.full_offset.y; diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index fd19ab2efda..7c5e1a86f5e 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -667,7 +667,7 @@ int Scene::get_volume_stack_size() const if (object->intersects_volume) { /* Object intersects another volume, assume it's possible to go deeper in the stack. */ /* TODO(sergey): This might count nesting twice (A intersects B and B intersects A), but - * can't think of a computantially cheap algorithm. Dividing my 2 doesn't work because of + * can't think of a computationally cheap algorithm. Dividing my 2 doesn't work because of * Venn diagram example with 3 circles. */ ++volume_stack_size; } diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc index 0157d0b66a3..f462c0cb4cb 100644 --- a/source/blender/editors/interface/interface_template_attribute_search.cc +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -122,4 +122,4 @@ void attribute_search_add_items(StringRefNull str, BLI_string_search_free(search); } -} // namespace blender::ui \ No newline at end of file +} // namespace blender::ui diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 946699d5115..da95cad0fc7 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -693,7 +693,7 @@ char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wm const std::unique_ptr drop_controller = item.create_drop_controller(); if (!drop_controller) { - return NULL; + return nullptr; } return BLI_strdup(drop_controller->drop_tooltip(*drag).c_str()); From 5c2330203e11e0d916960218b07d88d2193bf526 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 25 Oct 2021 22:27:51 +1100 Subject: [PATCH 1141/1500] Fix T92293: Clipped labels for graph editor modifiers While c7d94a7827a5be9343eea22a9638bb059f185206 exposed this bug, this was caused by a discrepancy in padding where labels would have additional padding when drawing without emboss. The padding made widget drawing behave as if the text took up more room causing it to be clipped. Now labels are considered the same width with/without emboss. --- source/blender/editors/interface/interface_widgets.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index e9acc65ed37..61d66c44a39 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -4632,6 +4632,9 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu switch (but->type) { case UI_BTYPE_LABEL: wt = widget_type(UI_WTYPE_ICON_LABEL); + if (!(but->flag & UI_HAS_ICON)) { + but->drawflag |= UI_BUT_NO_TEXT_PADDING; + } break; default: wt = widget_type(UI_WTYPE_ICON); From 60e2103507b2d89a64771e969294272dd23b83e6 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 25 Oct 2021 13:29:42 +0200 Subject: [PATCH 1142/1500] Fix: crash in previously added versioning code --- source/blender/blenloader/intern/versioning_300.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index f0bc910889b..7341797bb65 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1225,6 +1225,10 @@ static void version_geometry_nodes_set_position_node_offset(bNodeTree *ntree) if (node->type != GEO_NODE_SET_POSITION) { continue; } + if (BLI_listbase_count(&node->inputs) < 4) { + /* The offset socket didn't exist in the file yet. */ + return; + } bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3); if (old_offset_socket->type == SOCK_VECTOR) { /* Versioning happened already. */ From e7bea3fb6ed00f5eb9e332d1d5162097e865a1c0 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 25 Oct 2021 13:30:44 +0200 Subject: [PATCH 1143/1500] Assets/IDs: Don't generate previews for object types with no real geometry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Object types like empties, cameras or lamps will just end up as empty preview images. We can think about ways to visualize them still, but meanwhile, don't create such an empty preview. Differential Revision: https://developer.blender.org/D10334 Reviewed by: Bastien Montagne, Sybren Stüvel --- source/blender/blenkernel/intern/icons.cc | 30 ++++++++++++++-------- source/blender/makesdna/DNA_object_types.h | 3 +++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 97c742b1ec1..c48f3934a19 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -359,22 +359,30 @@ void BKE_previewimg_id_copy(ID *new_id, const ID *old_id) PreviewImage **BKE_previewimg_id_get_p(const ID *id) { switch (GS(id->name)) { + case ID_OB: { + Object *ob = (Object *)id; + /* Currently, only object types with real geometry can be rendered as preview. */ + if (!OB_TYPE_IS_GEOMETRY(ob->type)) { + return NULL; + } + return &ob->preview; + } + #define ID_PRV_CASE(id_code, id_struct) \ case id_code: { \ return &((id_struct *)id)->preview; \ } \ ((void)0) - ID_PRV_CASE(ID_MA, Material); - ID_PRV_CASE(ID_TE, Tex); - ID_PRV_CASE(ID_WO, World); - ID_PRV_CASE(ID_LA, Light); - ID_PRV_CASE(ID_IM, Image); - ID_PRV_CASE(ID_BR, Brush); - ID_PRV_CASE(ID_OB, Object); - ID_PRV_CASE(ID_GR, Collection); - ID_PRV_CASE(ID_SCE, Scene); - ID_PRV_CASE(ID_SCR, bScreen); - ID_PRV_CASE(ID_AC, bAction); + ID_PRV_CASE(ID_MA, Material); + ID_PRV_CASE(ID_TE, Tex); + ID_PRV_CASE(ID_WO, World); + ID_PRV_CASE(ID_LA, Light); + ID_PRV_CASE(ID_IM, Image); + ID_PRV_CASE(ID_BR, Brush); + ID_PRV_CASE(ID_GR, Collection); + ID_PRV_CASE(ID_SCE, Scene); + ID_PRV_CASE(ID_SCR, bScreen); + ID_PRV_CASE(ID_AC, bAction); #undef ID_PRV_CASE default: break; diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 5a88ce7c9f5..e94541fdc7f 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -501,6 +501,9 @@ enum { /* check if the object type supports materials */ #define OB_TYPE_SUPPORT_MATERIAL(_type) \ (((_type) >= OB_MESH && (_type) <= OB_MBALL) || ((_type) >= OB_GPENCIL && (_type) <= OB_VOLUME)) +/** Does the object have some render-able geometry (unlike empties, cameras, etc.). */ +#define OB_TYPE_IS_GEOMETRY(_type) \ + (ELEM(_type, OB_MESH, OB_SURF, OB_FONT, OB_MBALL, OB_GPENCIL, OB_HAIR, OB_POINTCLOUD, OB_VOLUME)) #define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL)) #define OB_TYPE_SUPPORT_EDITMODE(_type) \ (ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE)) From 4100a79219e7f7c474046806db3e156be34a1011 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 25 Oct 2021 13:48:48 +0200 Subject: [PATCH 1144/1500] Assets: Add Author field to asset metadata This is information you'd typically want to be visible in the UI. It's optional of course, so if not relevant, it can just remain unset. --- .../startup/bl_ui/space_filebrowser.py | 1 + source/blender/blenkernel/intern/asset.cc | 5 +++ source/blender/makesdna/DNA_asset_types.h | 4 ++ source/blender/makesrna/intern/rna_asset.c | 42 +++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 927a30f0ae0..086df46d799 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -734,6 +734,7 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): row.operator("asset.open_containing_blend_file", text="", icon='TOOL_SETTINGS') layout.prop(asset_file_handle.asset_data, "description") + layout.prop(asset_file_handle.asset_data, "author") class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel): diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc index ae9ded3c754..dfe568729db 100644 --- a/source/blender/blenkernel/intern/asset.cc +++ b/source/blender/blenkernel/intern/asset.cc @@ -53,6 +53,7 @@ void BKE_asset_metadata_free(AssetMetaData **asset_data) if ((*asset_data)->properties) { IDP_FreeProperty((*asset_data)->properties); } + MEM_SAFE_FREE((*asset_data)->author); MEM_SAFE_FREE((*asset_data)->description); BLI_freelistN(&(*asset_data)->tags); @@ -158,6 +159,9 @@ void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data) IDP_BlendWrite(writer, asset_data->properties); } + if (asset_data->author) { + BLO_write_string(writer, asset_data->author); + } if (asset_data->description) { BLO_write_string(writer, asset_data->description); } @@ -175,6 +179,7 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data) IDP_BlendDataRead(reader, &asset_data->properties); } + BLO_read_data_address(reader, &asset_data->author); BLO_read_data_address(reader, &asset_data->description); BLO_read_list(reader, &asset_data->tags); BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags); diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index f5bdad3e79e..60de254fec7 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -72,8 +72,12 @@ typedef struct AssetMetaData { * #catalog_id is updated. */ char catalog_simple_name[64]; /* MAX_NAME */ + /** Optional name of the author for display in the UI. Dynamic length. */ + char *author; + /** Optional description of this asset for display in the UI. Dynamic length. */ char *description; + /** User defined tags for this asset. The asset manager uses these for filtering, but how they * function exactly (e.g. how they are registered to provide a list of searchable available tags) * is up to the asset-engine. */ diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 979d0882dd5..5d83da170b5 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -139,6 +139,40 @@ static IDProperty **rna_AssetMetaData_idprops(PointerRNA *ptr) return &asset_data->properties; } +static void rna_AssetMetaData_author_get(PointerRNA *ptr, char *value) +{ + AssetMetaData *asset_data = ptr->data; + + if (asset_data->author) { + strcpy(value, asset_data->author); + } + else { + value[0] = '\0'; + } +} + +static int rna_AssetMetaData_author_length(PointerRNA *ptr) +{ + AssetMetaData *asset_data = ptr->data; + return asset_data->author ? strlen(asset_data->author) : 0; +} + +static void rna_AssetMetaData_author_set(PointerRNA *ptr, const char *value) +{ + AssetMetaData *asset_data = ptr->data; + + if (asset_data->author) { + MEM_freeN(asset_data->author); + } + + if (value[0]) { + asset_data->author = BLI_strdup(value); + } + else { + asset_data->author = NULL; + } +} + static void rna_AssetMetaData_description_get(PointerRNA *ptr, char *value) { AssetMetaData *asset_data = ptr->data; @@ -347,6 +381,14 @@ static void rna_def_asset_data(BlenderRNA *brna) RNA_def_struct_idprops_func(srna, "rna_AssetMetaData_idprops"); RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */ + prop = RNA_def_property(srna, "author", PROP_STRING, PROP_NONE); + RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); + RNA_def_property_string_funcs(prop, + "rna_AssetMetaData_author_get", + "rna_AssetMetaData_author_length", + "rna_AssetMetaData_author_set"); + RNA_def_property_ui_text(prop, "Author", "Name of the creator of the asset"); + prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); RNA_def_property_string_funcs(prop, From cdcca917cfa4df1bd8c3a31831bb460cc3e0f604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 25 Oct 2021 14:21:35 +0200 Subject: [PATCH 1145/1500] Tests: assets, add BKE callback init/finalize to test setup/teardown Add calls to `BKE_callback_global_init()` and `BKE_callback_global_finalize()` to ensure unit tests mimick Blender (and don't trip the assertions added in rBbeea601e7253). No functional changes to Blender. --- source/blender/blenkernel/intern/asset_library_service_test.cc | 3 +++ source/blender/blenkernel/intern/asset_library_test.cc | 3 +++ 2 files changed, 6 insertions(+) diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index 80504bbdc05..e26ae05301e 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -23,6 +23,7 @@ #include "BLI_path_util.h" #include "BKE_appdir.h" +#include "BKE_callbacks.h" #include "CLG_log.h" @@ -40,10 +41,12 @@ class AssetLibraryServiceTest : public testing::Test { static void SetUpTestSuite() { CLG_init(); + BKE_callback_global_init(); } static void TearDownTestSuite() { CLG_exit(); + BKE_callback_global_finalize(); } void SetUp() override diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc index c6c949a7ec4..702008fed96 100644 --- a/source/blender/blenkernel/intern/asset_library_test.cc +++ b/source/blender/blenkernel/intern/asset_library_test.cc @@ -20,6 +20,7 @@ #include "BKE_appdir.h" #include "BKE_asset_catalog.hh" #include "BKE_asset_library.hh" +#include "BKE_callbacks.h" #include "asset_library_service.hh" @@ -34,10 +35,12 @@ class AssetLibraryTest : public testing::Test { static void SetUpTestSuite() { CLG_init(); + BKE_callback_global_init(); } static void TearDownTestSuite() { CLG_exit(); + BKE_callback_global_finalize(); } void TearDown() override From bd7f1c5cce09fefbeb11bfa729dbe86dd4021c97 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 25 Oct 2021 14:41:47 +0200 Subject: [PATCH 1146/1500] Fix non-capitalized UI message. --- source/blender/editors/asset/intern/asset_ops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index e2ae3b3893b..158e877ed7d 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -554,7 +554,7 @@ static bool asset_catalog_redo_poll(bContext *C) static void ASSET_OT_catalog_redo(struct wmOperatorType *ot) { /* identifiers */ - ot->name = "redo Catalog Edits"; + ot->name = "Redo Catalog Edits"; ot->description = "Redo the last undone edit to the asset catalogs"; ot->idname = "ASSET_OT_catalog_redo"; From b15e1861ac1d8e98af8f8d33cb4e59cf0e0d3419 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 25 Oct 2021 15:12:50 +0200 Subject: [PATCH 1147/1500] Fix T92460: crash when instancing on curves generated from string Issue is that the Instance on Points node currently expects that all instance references are used (see `remove_unused_references`). This should be fixed at some point, but for now make sure that the String to Curves node does not output unused references. --- .../nodes/geometry/nodes/node_geo_instance_on_points.cc | 1 + .../nodes/geometry/nodes/node_geo_string_to_curves.cc | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 78399fad2c0..44cad2d38d4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -196,6 +196,7 @@ static void geo_node_instance_on_points_exec(GeoNodeExecParams params) } /* Unused references may have been added above. Remove those now so that other nodes don't * process them needlessly. */ + /** \note: This currently expects that all originally existing instances were used. */ instances.remove_unused_references(); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 1cb6d43f685..ac946540221 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -271,8 +271,9 @@ static void geo_node_string_to_curves_exec(GeoNodeExecParams params) /* Convert UTF-8 encoded string to UTF-32. */ size_t len_bytes; size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes); - Array char_codes(len_chars + 1); - BLI_str_utf8_as_utf32(char_codes.data(), layout.text.c_str(), len_chars + 1); + Array char_codes_with_null(len_chars + 1); + BLI_str_utf8_as_utf32(char_codes_with_null.data(), layout.text.c_str(), len_chars + 1); + const Span char_codes = char_codes_with_null.as_span().drop_back(1); /* Create and add instances. */ GeometrySet geometry_set_out; From cfde1f9f3b75b75b1336b2134abb8b260f27ab8c Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 25 Oct 2021 15:14:01 +0200 Subject: [PATCH 1148/1500] Geometry Nodes: Keep Add menu sorted alphabetically --- release/scripts/startup/nodeitems_builtins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 13adde5c439..b1a3d8a762d 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -171,8 +171,8 @@ def geometry_input_node_items(context): yield NodeItem("FunctionNodeLegacyRandomFloat") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) - yield NodeItem("GeometryNodeCollectionInfo") yield NodeItem("FunctionNodeInputBool") + yield NodeItem("GeometryNodeCollectionInfo") yield NodeItem("FunctionNodeInputColor") yield NodeItem("FunctionNodeInputInt") yield NodeItem("GeometryNodeIsViewport") From 4b1ad2dc1748c7c004d8829400a8296efd31576a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 26 Oct 2021 00:10:18 +1100 Subject: [PATCH 1149/1500] Fix T92361: Zooming nodes clips text labels While c7d94a7827a5be9343eea22a9638bb059f185206 exposed this bug, this was caused by text widths being calculated without taking the zoom level into account since drawing at a smaller size is often wider than the width of the larger text scaled by the zoom. --- source/blender/editors/include/UI_interface.h | 7 ++++- .../editors/interface/interface_layout.c | 8 +++-- .../editors/interface/interface_style.c | 29 +++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index de6b975a910..4cd921bc61e 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2687,7 +2687,12 @@ void UI_fontstyle_draw_simple_backdrop(const struct uiFontStyle *fs, const float col_fg[4], const float col_bg[4]); -int UI_fontstyle_string_width(const struct uiFontStyle *fs, const char *str); +int UI_fontstyle_string_width(const struct uiFontStyle *fs, + const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2); +int UI_fontstyle_string_width_with_block_aspect(const struct uiFontStyle *fs, + const char *str, + const float aspect) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(1, 2); int UI_fontstyle_height_max(const struct uiFontStyle *fs); void UI_draw_icon_tri(float x, float y, char dir, const float[4]); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index e54b261facd..25ba0e13487 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -351,12 +351,16 @@ static int ui_text_icon_width_ex(uiLayout *layout, if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) { layout->item.flag |= UI_ITEM_FIXED_SIZE; } - const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + float margin = pad_factor->text; if (icon) { margin += pad_factor->icon; } - return UI_fontstyle_string_width(fstyle, name) + (unit_x * margin); + + const float aspect = layout->root->block->aspect; + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + return UI_fontstyle_string_width_with_block_aspect(fstyle, name, aspect) + + (int)ceilf(unit_x * margin); } return unit_x * 10; } diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 6b1ff92a855..2a1cdfd447c 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -376,6 +376,35 @@ int UI_fontstyle_string_width(const uiFontStyle *fs, const char *str) return (int)BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); } +/** + * Return the width of `str` with the spacing & kerning of `fs` with `aspect` + * (representing #uiBlock.aspect) applied. + * + * When calculating text width, the UI layout logic calculate widths without scale, + * only applying scale when drawing. This causes problems for fonts since kerning at + * smaller sizes often makes them wider than a scaled down version of the larger text. + * Resolve this by calculating the text at the on-screen size, + * returning the result scaled back to 1:1. See T92361. + */ +int UI_fontstyle_string_width_with_block_aspect(const uiFontStyle *fs, + const char *str, + const float aspect) +{ + uiFontStyle fs_buf; + if (aspect != 1.0f) { + fs_buf = *fs; + ui_fontscale(&fs_buf.points, aspect); + fs = &fs_buf; + } + + int width = UI_fontstyle_string_width(fs, str); + + if (aspect != 1.0f) { + width = (int)ceilf(width * aspect); + } + return width; +} + int UI_fontstyle_height_max(const uiFontStyle *fs) { UI_fontstyle_set(fs); From bc2f4dd8b408eee35353c18a44ce0dc5b51394a9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 25 Oct 2021 09:10:44 -0500 Subject: [PATCH 1150/1500] Geometry Nodes: Add "Fill Caps" option to curve to mesh node This adds an option to fill the ends of the generated mesh for each spline combination with an N-gon. The resulting mesh is manifold, so it can be used for operations like Boolean. Differential Revision: https://developer.blender.org/D12982 --- .../blender/blenkernel/BKE_curve_to_mesh.hh | 2 +- .../intern/curve_to_mesh_convert.cc | 62 ++++++++++++++++--- .../geometry/nodes/node_geo_curve_to_mesh.cc | 15 +++-- 3 files changed, 64 insertions(+), 15 deletions(-) diff --git a/source/blender/blenkernel/BKE_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh index 87bec6203a9..fb077425336 100644 --- a/source/blender/blenkernel/BKE_curve_to_mesh.hh +++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh @@ -25,7 +25,7 @@ struct CurveEval; namespace blender::bke { -Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile); +Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, bool fill_caps); Mesh *curve_to_wire_mesh(const CurveEval &curve); } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 5f2f945192c..b3957e57920 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -88,6 +88,7 @@ static void mark_edges_sharp(MutableSpan edges) } static void spline_extrude_to_mesh_data(const ResultInfo &info, + const bool fill_caps, MutableSpan r_verts, MutableSpan r_edges, MutableSpan r_loops, @@ -180,6 +181,36 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, } } + if (fill_caps && profile.is_cyclic()) { + const int poly_size = info.spline_edge_len * info.profile_edge_len; + const int cap_loop_offset = info.loop_offset + poly_size * 4; + const int cap_poly_offset = info.poly_offset + poly_size; + + MPoly &poly_start = r_polys[cap_poly_offset]; + poly_start.loopstart = cap_loop_offset; + poly_start.totloop = info.profile_edge_len; + MPoly &poly_end = r_polys[cap_poly_offset + 1]; + poly_end.loopstart = cap_loop_offset + info.profile_edge_len; + poly_end.totloop = info.profile_edge_len; + + const int last_ring_index = info.spline_vert_len - 1; + const int last_ring_vert_offset = info.vert_offset + info.profile_vert_len * last_ring_index; + const int last_ring_edge_offset = profile_edges_start + + info.profile_edge_len * last_ring_index; + + for (const int i : IndexRange(info.profile_edge_len)) { + MLoop &loop_start = r_loops[cap_loop_offset + i]; + loop_start.v = info.vert_offset + i; + loop_start.e = profile_edges_start + i; + MLoop &loop_end = r_loops[cap_loop_offset + info.profile_edge_len + i]; + loop_end.v = last_ring_vert_offset + i; + loop_end.e = last_ring_edge_offset + i; + } + + mark_edges_sharp(r_edges.slice(profile_edges_start, info.profile_edge_len)); + mark_edges_sharp(r_edges.slice(last_ring_edge_offset, info.profile_edge_len)); + } + /* Calculate the positions of each profile ring profile along the spline. */ Span positions = spline.evaluated_positions(); Span tangents = spline.evaluated_tangents(); @@ -226,14 +257,22 @@ static inline int spline_extrude_edge_size(const Spline &curve, const Spline &pr curve.evaluated_edges_size() * profile.evaluated_points_size(); } -static inline int spline_extrude_loop_size(const Spline &curve, const Spline &profile) +static inline int spline_extrude_loop_size(const Spline &curve, + const Spline &profile, + const bool fill_caps) { - return curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; + const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; + const int caps = (fill_caps && profile.is_cyclic()) ? profile.evaluated_edges_size() * 2 : 0; + return tube + caps; } -static inline int spline_extrude_poly_size(const Spline &curve, const Spline &profile) +static inline int spline_extrude_poly_size(const Spline &curve, + const Spline &profile, + const bool fill_caps) { - return curve.evaluated_edges_size() * profile.evaluated_edges_size(); + const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size(); + const int caps = (fill_caps && profile.is_cyclic()) ? 2 : 0; + return tube + caps; } struct ResultOffsets { @@ -242,7 +281,9 @@ struct ResultOffsets { Array loop; Array poly; }; -static ResultOffsets calculate_result_offsets(Span profiles, Span curves) +static ResultOffsets calculate_result_offsets(Span profiles, + Span curves, + const bool fill_caps) { const int total = profiles.size() * curves.size(); Array vert(total + 1); @@ -263,8 +304,8 @@ static ResultOffsets calculate_result_offsets(Span profiles, Span profiles = profile.splines(); Span curves = curve.splines(); - const ResultOffsets offsets = calculate_result_offsets(profiles, curves); + const ResultOffsets offsets = calculate_result_offsets(profiles, curves, fill_caps); if (offsets.vert.last() == 0) { return nullptr; } @@ -696,6 +737,7 @@ Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile) }; spline_extrude_to_mesh_data(info, + fill_caps, {mesh->mvert, mesh->totvert}, {mesh->medge, mesh->totedge}, {mesh->mloop, mesh->totloop}, @@ -733,7 +775,7 @@ static CurveEval get_curve_single_vert() Mesh *curve_to_wire_mesh(const CurveEval &curve) { static const CurveEval vert_curve = get_curve_single_vert(); - return curve_to_mesh_sweep(curve, vert_curve); + return curve_to_mesh_sweep(curve, vert_curve, false); } } // namespace blender::bke diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index 00451946af9..be755c7269e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -29,19 +29,25 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) { b.add_input("Curve"); b.add_input("Profile Curve"); + b.add_input("Fill Caps") + .description( + "If the profile spline is cyclic, fill the ends of the generated mesh with N-gons"); b.add_output("Mesh"); } -static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set) +static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, + const GeometrySet &profile_set, + const bool fill_caps) { + const CurveEval *curve = geometry_set.get_curve_for_read(); const CurveEval *profile_curve = profile_set.get_curve_for_read(); if (profile_curve == nullptr) { - Mesh *mesh = bke::curve_to_wire_mesh(*geometry_set.get_curve_for_read()); + Mesh *mesh = bke::curve_to_wire_mesh(*curve); geometry_set.replace_mesh(mesh); } else { - Mesh *mesh = bke::curve_to_mesh_sweep(*geometry_set.get_curve_for_read(), *profile_curve); + Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps); geometry_set.replace_mesh(mesh); } } @@ -50,6 +56,7 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input("Curve"); GeometrySet profile_set = params.extract_input("Profile Curve"); + const bool fill_caps = params.extract_input("Fill Caps"); if (profile_set.has_instances()) { params.error_message_add(NodeWarningType::Error, @@ -67,7 +74,7 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_curve()) { has_curve = true; - geometry_set_curve_to_mesh(geometry_set, profile_set); + geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); } geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); }); From 1bc28fc73b9cf6ffa873c6fb0d2f6c3e9e7aaf2b Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 25 Oct 2021 11:26:01 -0300 Subject: [PATCH 1151/1500] Fix T92466: Crash snapping to text objects with XRay shading Should have been addressed along with {rB6cff1d648030} --- source/blender/editors/transform/transform_snap_object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index dea8a7c6f03..f5f3fafe897 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -2308,7 +2308,7 @@ static short snapMesh(SnapObjectContext *sctx, float dist_px_sq = square_f(*dist_px); /* Test BoundBox */ - BoundBox *bb = BKE_mesh_boundbox_get(ob_eval); + BoundBox *bb = BKE_object_boundbox_get(ob_eval); if (bb && !snap_bound_box_check_dist( bb->vec[0], bb->vec[6], lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) { From 4758a7d35750ae0a2ed24014817e5b169a71e364 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Mon, 25 Oct 2021 16:28:56 +0200 Subject: [PATCH 1152/1500] UI: Use flat colors for NLA strip drawing The NLA editor is in need of a design overhaul, hopefully for 3.1 or 3.2. This should be a project on itself, however, the worst offender currently is the use of gradients on strips. Something that can be fixed easily. {F11390293, size=full, loop, autoplay} A simple replace of `UI_draw_roundbox_shade_x` for `UI_draw_roundbox_4fv` brings strips in line with how other areas are drawn. This patch also: * Remove embossed lines around active action channel. * Highlight the strip while being moved. This patch does not include any theme changes. This will be tackled separately. Reviewed By: HooglyBoogly Differential Revision: https://developer.blender.org/D12968 --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- source/blender/editors/space_nla/nla_draw.c | 54 ++++++--------------- source/tools | 2 +- 5 files changed, 20 insertions(+), 42 deletions(-) diff --git a/release/datafiles/locale b/release/datafiles/locale index 80d9e7ee122..8ee2942570f 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 80d9e7ee122c626cbbcd1da554683bce79f8d3df +Subproject commit 8ee2942570f08d10484bb2328d0d1b0aaaa0367c diff --git a/release/scripts/addons b/release/scripts/addons index e68c0118c13..c49c16c38d4 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit e68c0118c13c3575e6096ad2dc7fb4434eadf38e +Subproject commit c49c16c38d4bce29a1e0518d5fad5d90f42ab5e7 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 42da56aa737..16467648282 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 42da56aa73726710107031787af5eea186797984 +Subproject commit 16467648282500cc229c271f62201ef897f2c2c3 diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 4694d8652f6..0c81f461369 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -323,12 +323,19 @@ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uin { const float yheight = ymaxc - yminc; - immUniformColor3f(0.7f, 0.7f, 0.7f); - /* draw with AA'd line */ GPU_line_smooth(true); GPU_blend(GPU_BLEND_ALPHA); + /* Fully opaque line on selected strips. */ + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* TODO: Use theme setting. */ + immUniformColor3f(1.0f, 1.0f, 1.0f); + } + else { + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); + } + /* influence -------------------------- */ if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) { FCurve *fcu = BKE_fcurve_find(&strip->fcurves, "influence", 0); @@ -501,7 +508,7 @@ static void nla_draw_strip(SpaceNla *snla, /* strip is in normal track */ UI_draw_roundbox_corner_set(UI_CNR_ALL); /* all corners rounded */ - UI_draw_roundbox_shade_x( + UI_draw_roundbox_4fv( &(const rctf){ .xmin = strip->start, .xmax = strip->end, @@ -509,9 +516,7 @@ static void nla_draw_strip(SpaceNla *snla, .ymax = ymaxc, }, true, - 0.0, - 0.5, - 0.1, + 0.0f, color); /* restore current vertex format & program (roundbox trashes it) */ @@ -545,11 +550,9 @@ static void nla_draw_strip(SpaceNla *snla, /* draw strip outline * - color used here is to indicate active vs non-active */ - if (strip->flag & NLASTRIP_FLAG_ACTIVE) { + if (strip->flag & NLASTRIP_FLAG_ACTIVE | strip->flag & NLASTRIP_FLAG_SELECT) { /* strip should appear 'sunken', so draw a light border around it */ - color[0] = 0.9f; /* FIXME: hardcoded temp-hack colors */ - color[1] = 1.0f; - color[2] = 0.9f; + color[0] = color[1] = color[2] = 1.0f; /* FIXME: hardcoded temp-hack colors */ } else { /* strip should appear to stand out, so draw a dark border around it */ @@ -566,7 +569,7 @@ static void nla_draw_strip(SpaceNla *snla, } else { /* non-muted - draw solid, rounded outline */ - UI_draw_roundbox_shade_x( + UI_draw_roundbox_4fv( &(const rctf){ .xmin = strip->start, .xmax = strip->end, @@ -574,9 +577,7 @@ static void nla_draw_strip(SpaceNla *snla, .ymax = ymaxc, }, false, - 0.0, - 0.0, - 0.1, + 0.0f, color); /* restore current vertex format & program (roundbox trashes it) */ @@ -661,7 +662,7 @@ static void nla_draw_strip_text(AnimData *adt, } /* set text color - if colors (see above) are light, draw black text, otherwise draw white */ - if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_TWEAKUSER)) { + if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_TWEAKUSER)) { col[0] = col[1] = col[2] = 0; } else { @@ -805,29 +806,6 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region) immRectf( pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP, v2d->cur.xmax, ymax - NLACHANNEL_SKIP); - /* draw 'embossed' lines above and below the strip for effect */ - /* white base-lines */ - GPU_line_width(2.0f); - immUniformColor4f(1.0f, 1.0f, 1.0f, 0.3f); - immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmin, ymax - NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymax - NLACHANNEL_SKIP); - immEnd(); - - /* black top-lines */ - GPU_line_width(1.0f); - immUniformColor3f(0.0f, 0.0f, 0.0f); - immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmin, ymax - NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymax - NLACHANNEL_SKIP); - immEnd(); - - /* TODO: these lines but better --^ */ - immUnbindProgram(); /* draw keyframes in the action */ diff --git a/source/tools b/source/tools index 7c5acb95df9..2e8c8792488 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 7c5acb95df918503d11cfc43172ce13901019289 +Subproject commit 2e8c879248822c8e500ed49d79acc605e5aa75b9 From 1267dfee7c5453036a2affde90c4a094a4a824a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 25 Oct 2021 15:50:24 +0200 Subject: [PATCH 1153/1500] Cleanup: refactor filelist filter functions Perform a few cleanups: - Add documentation to explain what returned bools mean. - Early returns so that flow is clearer and some checks on `is_filtered` are no longer necessary. - Split up `is_filtered_file` and `is_filtered_id_file`, so that they can reuse common code, and such that the different filter checks they perform can be separated from each other. The latter is done not only to reduce code duplication, but also as preparation to fix the asset browser filtering. For that, it helps when the "filter by file name" and "filter by file type" parts are separate. No functional changes. Reviewed By: mont29 Differential Revision: https://developer.blender.org/D12991 --- source/blender/editors/space_file/filelist.c | 157 +++++++++---------- 1 file changed, 78 insertions(+), 79 deletions(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index d329a8809c7..c3fff43d192 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -817,88 +817,85 @@ static bool is_filtered_hidden(const char *filename, return false; } +/** + * Apply the filter string as file path matching pattern. + * \return true when the file should be in the result set, false if it should be filtered out. */ +static bool is_filtered_file_relpath(const FileListInternEntry *file, const FileListFilter *filter) +{ + if (filter->filter_search[0] == '\0') { + return true; + } + + /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ + return fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) == 0; +} + +/** \return true when the file should be in the result set, false if it should be filtered out. */ +static bool is_filtered_file_type(const FileListInternEntry *file, const FileListFilter *filter) +{ + if (is_filtered_hidden(file->relpath, filter, file)) { + return false; + } + + if (FILENAME_IS_CURRPAR(file->relpath)) { + return false; + } + + /* We only check for types if some type are enabled in filtering. */ + if (filter->filter && (filter->flags & FLF_DO_FILTER)) { + if (file->typeflag & FILE_TYPE_DIR) { + if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { + if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { + return false; + } + } + else { + if (!(filter->filter & FILE_TYPE_FOLDER)) { + return false; + } + } + } + else { + if (!(file->typeflag & filter->filter)) { + return false; + } + } + } + return true; +} + +/** \return true when the file should be in the result set, false if it should be filtered out. */ static bool is_filtered_file(FileListInternEntry *file, const char *UNUSED(root), FileListFilter *filter) { - bool is_filtered = !is_filtered_hidden(file->relpath, filter, file); - - if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { - /* We only check for types if some type are enabled in filtering. */ - if (filter->filter && (filter->flags & FLF_DO_FILTER)) { - if (file->typeflag & FILE_TYPE_DIR) { - if (file->typeflag & - (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { - if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { - is_filtered = false; - } - } - else { - if (!(filter->filter & FILE_TYPE_FOLDER)) { - is_filtered = false; - } - } - } - else { - if (!(file->typeflag & filter->filter)) { - is_filtered = false; - } - } - } - /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ - if (is_filtered && (filter->filter_search[0] != '\0')) { - if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { - is_filtered = false; - } - } - } - - return is_filtered; + return is_filtered_file_type(file, filter) && is_filtered_file_relpath(file, filter); } -static bool is_filtered_id_file(const FileListInternEntry *file, - const char *id_group, - const char *name, - const FileListFilter *filter) +static bool is_filtered_id_file_type(const FileListInternEntry *file, + const char *id_group, + const char *name, + const FileListFilter *filter) { - bool is_filtered = !is_filtered_hidden(file->relpath, filter, file); - if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { - /* We only check for types if some type are enabled in filtering. */ - if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) { - if (file->typeflag & FILE_TYPE_DIR) { - if (file->typeflag & - (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { - if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { - is_filtered = false; - } - } - else { - if (!(filter->filter & FILE_TYPE_FOLDER)) { - is_filtered = false; - } - } + if (!is_filtered_file_type(file, filter)) { + return false; + } + + /* We only check for types if some type are enabled in filtering. */ + if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) { + if (id_group) { + if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) { + return false; } - if (is_filtered && id_group) { - if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) { - is_filtered = false; - } - else { - uint64_t filter_id = groupname_to_filter_id(id_group); - if (!(filter_id & filter->filter_id)) { - is_filtered = false; - } - } - } - } - /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ - if (is_filtered && (filter->filter_search[0] != '\0')) { - if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { - is_filtered = false; + + uint64_t filter_id = groupname_to_filter_id(id_group); + if (!(filter_id & filter->filter_id)) { + return false; } } } - return is_filtered; + return true; } /** @@ -933,21 +930,23 @@ static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter) asset_data); } -static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) +static bool is_filtered_lib_type(FileListInternEntry *file, + const char *root, + FileListFilter *filter) { - bool is_filtered; char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name; BLI_join_dirfile(path, sizeof(path), root, file->relpath); if (BLO_library_path_explode(path, dir, &group, &name)) { - is_filtered = is_filtered_id_file(file, group, name, filter); - } - else { - is_filtered = is_filtered_file(file, root, filter); + return is_filtered_id_file_type(file, group, name, filter); } + return is_filtered_file_type(file, filter); +} - return is_filtered; +static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) +{ + return is_filtered_lib_type(file, root, filter) && is_filtered_file_relpath(file, filter); } static bool is_filtered_asset_library(FileListInternEntry *file, @@ -969,8 +968,8 @@ static bool is_filtered_main_assets(FileListInternEntry *file, FileListFilter *filter) { /* "Filtered" means *not* being filtered out... So return true if the file should be visible. */ - return is_filtered_id_file(file, file->relpath, file->name, filter) && - is_filtered_asset(file, filter); + return is_filtered_id_file_type(file, file->relpath, file->name, filter) && + is_filtered_file_relpath(file, filter) && is_filtered_asset(file, filter); } void filelist_tag_needs_filtering(FileList *filelist) From 6da63b19fff2f690aa155bfd877cb8574d8c2987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 25 Oct 2021 16:25:38 +0200 Subject: [PATCH 1154/1500] Fix T87627: Asset Browser Search Bar not working The asset browser search bar was filtering on the filename containing the asset, and not the asset name. This is now fixed. --- source/blender/editors/space_file/filelist.c | 30 ++++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index c3fff43d192..af26de1a3bd 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -920,14 +920,32 @@ static void prepare_filter_asset_library(const FileList *filelist, FileListFilte static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter) { + const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file); + /* Not used yet for the asset view template. */ - if (!filter->asset_catalog_filter) { + if (filter->asset_catalog_filter && !file_is_asset_visible_in_catalog_filter_settings( + filter->asset_catalog_filter, asset_data)) { + return false; + } + + if (filter->filter_search[0] == '\0') { + /* If there is no filter text, everything matches. */ return true; } - const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file); - return file_is_asset_visible_in_catalog_filter_settings(filter->asset_catalog_filter, - asset_data); + /* filter->filter_search contains "*the search text*"; this code strips the asterisks. + * For a simple name search it would work to call fnmatch() here, but that + * would be inefficient when expanding to searching for tags as well.*/ + char filter_search[64]; /* sizeof(filter->filter_search) - 1 */ + const size_t string_length = STRNCPY_RLEN(filter_search, filter->filter_search + 1); + filter_search[string_length - 1] = '\0'; + + if (BLI_strcasestr(file->name, filter_search) != NULL) { + return true; + } + + /* TODO: search for matching tag. */ + return false; } static bool is_filtered_lib_type(FileListInternEntry *file, @@ -953,7 +971,7 @@ static bool is_filtered_asset_library(FileListInternEntry *file, const char *root, FileListFilter *filter) { - return is_filtered_lib(file, root, filter) && is_filtered_asset(file, filter); + return is_filtered_lib_type(file, root, filter) && is_filtered_asset(file, filter); } static bool is_filtered_main(FileListInternEntry *file, @@ -969,7 +987,7 @@ static bool is_filtered_main_assets(FileListInternEntry *file, { /* "Filtered" means *not* being filtered out... So return true if the file should be visible. */ return is_filtered_id_file_type(file, file->relpath, file->name, filter) && - is_filtered_file_relpath(file, filter) && is_filtered_asset(file, filter); + is_filtered_asset(file, filter); } void filelist_tag_needs_filtering(FileList *filelist) From 2a7b9f2d45c345ee910a430e7035fd3d406de8f7 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Mon, 25 Oct 2021 16:52:17 +0200 Subject: [PATCH 1155/1500] Cleanup: silence warning in recent commit Thanks to Dr. Sybren for pointing it out! --- source/blender/editors/space_nla/nla_draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 0c81f461369..bf2d20cf4c9 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -550,7 +550,7 @@ static void nla_draw_strip(SpaceNla *snla, /* draw strip outline * - color used here is to indicate active vs non-active */ - if (strip->flag & NLASTRIP_FLAG_ACTIVE | strip->flag & NLASTRIP_FLAG_SELECT) { + if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT)) { /* strip should appear 'sunken', so draw a light border around it */ color[0] = color[1] = color[2] = 1.0f; /* FIXME: hardcoded temp-hack colors */ } From e8027ec2a0cac4b0e92d51a64ccc40fd3f190a20 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 25 Oct 2021 10:07:00 -0300 Subject: [PATCH 1156/1500] UI Drag Drop: allow customizable drawing No functional changes. This commit adds 3 callbacks for `wmDropBox` which allow custom drawing without affecting the internal dropbox API. Differential Revision: https://developer.blender.org/D12948 --- source/blender/editors/include/ED_object.h | 2 +- .../editors/interface/interface_dropboxes.cc | 5 +- .../blender/editors/object/object_relations.c | 4 +- .../space_outliner/outliner_dragdrop.c | 8 +- .../editors/space_view3d/space_view3d.c | 11 +- source/blender/windowmanager/WM_api.h | 4 + source/blender/windowmanager/WM_types.h | 21 +- .../windowmanager/intern/wm_dragdrop.c | 296 ++++++++++-------- source/blender/windowmanager/intern/wm_draw.c | 4 +- .../blender/windowmanager/wm_event_system.h | 2 +- 10 files changed, 205 insertions(+), 152 deletions(-) diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 083d167c573..458ce57ab86 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -202,7 +202,7 @@ void ED_object_parent(struct Object *ob, const char *substr); char *ED_object_ot_drop_named_material_tooltip(struct bContext *C, struct PointerRNA *properties, - const struct wmEvent *event); + const int mval[2]); /* bitflags for enter/exit editmode */ enum { diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index ae626080a9a..1cc06db3a8c 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -40,12 +40,11 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve static char *ui_tree_view_drop_tooltip(bContext *C, wmDrag *drag, - const wmEvent *event, + const int xy[2], wmDropBox *UNUSED(drop)) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, - event->xy); + const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, xy); if (!hovered_tree_item) { return nullptr; } diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index d81143d6081..a64510662d9 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2589,10 +2589,10 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot) char *ED_object_ot_drop_named_material_tooltip(bContext *C, PointerRNA *properties, - const wmEvent *event) + const int mval[2]) { int mat_slot = 0; - Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot); + Object *ob = ED_view3d_give_material_slot_under_cursor(C, mval, &mat_slot); if (ob == NULL) { return BLI_strdup(""); } diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index a82f516b125..a391d032d7e 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -868,7 +868,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) static char *datastack_drop_tooltip(bContext *UNUSED(C), wmDrag *drag, - const wmEvent *UNUSED(event), + const int UNUSED(xy[2]), struct wmDropBox *UNUSED(drop)) { StackDropData *drop_data = drag->poin; @@ -1201,11 +1201,13 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event static char *collection_drop_tooltip(bContext *C, wmDrag *drag, - const wmEvent *event, + const int UNUSED(xy[2]), wmDropBox *UNUSED(drop)) { + wmWindowManager *wm = CTX_wm_manager(C); + const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; CollectionDrop data; - if (!event->shift && collection_drop_init(C, drag, event, &data)) { + if (event && !event->shift && collection_drop_init(C, drag, event, &data)) { TreeElement *te = data.te; if (!data.from || event->ctrl) { return BLI_strdup(TIP_("Link inside Collection")); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 7999018a6b6..eb30d0987ab 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -532,12 +532,17 @@ static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event static char *view3d_mat_drop_tooltip(bContext *C, wmDrag *drag, - const wmEvent *event, + const int xy[2], struct wmDropBox *drop) { const char *name = WM_drag_get_item_name(drag); + ARegion *region = CTX_wm_region(C); RNA_string_set(drop->ptr, "name", name); - return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event); + int mval[2] = { + xy[0] - region->winrct.xmin, + xy[1] - region->winrct.ymin, + }; + return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, mval); } static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) @@ -556,7 +561,7 @@ static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEven static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C), wmDrag *UNUSED(drag), - const wmEvent *UNUSED(event), + const int UNUSED(xy[2]), wmDropBox *UNUSED(drop)) { return BLI_strdup(TIP_("Create object instance from object-data")); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index eaf32c06aba..b4fe2f85b72 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -740,6 +740,10 @@ struct wmDropBox *WM_dropbox_add( void (*copy)(struct wmDrag *, struct wmDropBox *), void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *), WMDropboxTooltipFunc tooltip); +void WM_drag_draw_default_fn(struct bContext *C, + struct wmWindow *win, + struct wmDrag *drag, + const int xy[2]); ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); /* ID drag and drop */ diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index f4595869baf..bbfc9d53e44 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -1020,7 +1020,7 @@ typedef struct wmDragAssetListItem { typedef char *(*WMDropboxTooltipFunc)(struct bContext *, struct wmDrag *, - const struct wmEvent *event, + const int xy[2], struct wmDropBox *drop); typedef struct wmDrag { @@ -1038,8 +1038,11 @@ typedef struct wmDrag { float scale; int sx, sy; - /** If filled, draws operator tooltip/operator name. */ - char tooltip[200]; + /** Informs which dropbox is activated with the drag item. + * When this value changes, the #draw_activate and #draw_deactivate dropbox callbacks are + * triggered. + */ + struct wmDropBox *active_dropbox; unsigned int flags; /** List of wmDragIDs, all are guaranteed to have the same ID type. */ @@ -1067,6 +1070,18 @@ typedef struct wmDropBox { */ void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *); + /** Override the default drawing function. */ + void (*draw)(struct bContext *, struct wmWindow *, struct wmDrag *, const int *); + + /** Called when pool returns true the first time. */ + void (*draw_activate)(struct wmDropBox *, struct wmDrag *drag); + + /** Called when pool returns false the first time or when the drag event ends. */ + void (*draw_deactivate)(struct wmDropBox *, struct wmDrag *drag); + + /** Custom data for drawing. */ + void *draw_data; + /** Custom tooltip shown during dragging. */ WMDropboxTooltipFunc tooltip; diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 9af90355a79..8495fa2a082 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -43,6 +43,9 @@ #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_screen.h" + +#include "GHOST_C-api.h" #include "BLO_readfile.h" @@ -63,6 +66,7 @@ #include "WM_api.h" #include "WM_types.h" #include "wm_event_system.h" +#include "wm_window.h" /* ****************************************************** */ @@ -229,6 +233,9 @@ void WM_drag_data_free(int dragtype, void *poin) void WM_drag_free(wmDrag *drag) { + if (drag->active_dropbox && drag->active_dropbox->draw_deactivate) { + drag->active_dropbox->draw_deactivate(drag->active_dropbox, drag); + } if (drag->flags & WM_DRAG_FREE_DATA) { WM_drag_data_free(drag->type, drag->poin); } @@ -250,11 +257,11 @@ void WM_drag_free_list(struct ListBase *lb) } } -static char *dropbox_tooltip(bContext *C, wmDrag *drag, const wmEvent *event, wmDropBox *drop) +static char *dropbox_tooltip(bContext *C, wmDrag *drag, const int xy[2], wmDropBox *drop) { char *tooltip = NULL; if (drop->tooltip) { - tooltip = drop->tooltip(C, drag, event, drop); + tooltip = drop->tooltip(C, drag, xy, drop); } if (!tooltip) { tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr)); @@ -286,7 +293,7 @@ static wmDropBox *dropbox_active(bContext *C, } /* return active operator tooltip/name when mouse is in box */ -static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) +static wmDropBox *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); wmDropBox *drop = dropbox_active(C, &win->handlers, drag, event); @@ -298,10 +305,7 @@ static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) ARegion *region = CTX_wm_region(C); drop = dropbox_active(C, ®ion->handlers, drag, event); } - if (drop) { - return dropbox_tooltip(C, drag, event, drop); - } - return NULL; + return drop; } static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *event) @@ -316,23 +320,17 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e return; } - drag->tooltip[0] = 0; - - /* check buttons (XXX todo rna and value) */ - if (UI_but_active_drop_name(C)) { - BLI_strncpy(drag->tooltip, IFACE_("Paste name"), sizeof(drag->tooltip)); - } - else { - char *tooltip = wm_dropbox_active(C, drag, event); - - if (tooltip) { - BLI_strncpy(drag->tooltip, tooltip, sizeof(drag->tooltip)); - MEM_freeN(tooltip); - // WM_cursor_modal_set(win, WM_CURSOR_COPY); + wmDropBox *drop_prev = drag->active_dropbox; + wmDropBox *drop = wm_dropbox_active(C, drag, event); + if (drop != drop_prev) { + if (drop_prev && drop_prev->draw_deactivate) { + drop_prev->draw_deactivate(drop_prev, drag); + BLI_assert(drop_prev->draw_data == NULL); } - // else - // WM_cursor_modal_restore(win); - /* unsure about cursor type, feels to be too much */ + if (drop && drop->draw_activate) { + drop->draw_activate(drop, drag); + } + drag->active_dropbox = drop; } } @@ -629,132 +627,162 @@ const char *WM_drag_get_item_name(wmDrag *drag) return ""; } -static void drag_rect_minmax(rcti *rect, int x1, int y1, int x2, int y2) +static void wm_drag_draw_icon(bContext *UNUSED(C), + wmWindow *UNUSED(win), + wmDrag *drag, + const int xy[2]) { - if (rect->xmin > x1) { - rect->xmin = x1; + int x, y; + if (drag->imb) { + x = xy[0] - drag->sx / 2; + y = xy[1] - drag->sy / 2; + + float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */ + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); + immDrawPixelsTexScaled(&state, + x, + y, + drag->imb->x, + drag->imb->y, + GPU_RGBA8, + false, + drag->imb->rect, + drag->scale, + drag->scale, + 1.0f, + 1.0f, + col); } - if (rect->xmax < x2) { - rect->xmax = x2; - } - if (rect->ymin > y1) { - rect->ymin = y1; - } - if (rect->ymax < y2) { - rect->ymax = y2; + else { + int padding = 4 * UI_DPI_FAC; + x = xy[0] - 2 * padding; + y = xy[1] - 2 * UI_DPI_FAC; + + const uchar text_col[] = {255, 255, 255, 255}; + UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false); } } -/* called in wm_draw.c */ -/* if rect set, do not draw */ -void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) +static void wm_drag_draw_item_name(wmDrag *drag, const int x, const int y) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - wmWindowManager *wm = CTX_wm_manager(C); - const int winsize_y = WM_window_pixels_y(win); + const uchar text_col[] = {255, 255, 255, 255}; + UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); +} - int cursorx = win->eventstate->xy[0]; - int cursory = win->eventstate->xy[1]; - if (rect) { - rect->xmin = rect->xmax = cursorx; - rect->ymin = rect->ymax = cursory; +static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + if (!CTX_wm_region(C)) { + /* Some callbacks require the region. */ + return; } + int iconsize = UI_DPI_ICON_SIZE; + int padding = 4 * UI_DPI_FAC; + + char *tooltip = NULL; + bool free_tooltip = false; + if (UI_but_active_drop_name(C)) { + tooltip = IFACE_("Paste name"); + } + else if (drag->active_dropbox) { + tooltip = dropbox_tooltip(C, drag, xy, drag->active_dropbox); + free_tooltip = true; + } + + if (tooltip) { + const int winsize_y = WM_window_pixels_y(win); + int x, y; + if (drag->imb) { + x = xy[0] - drag->sx / 2; + + if (xy[1] + drag->sy / 2 + padding + iconsize < winsize_y) { + y = xy[1] + drag->sy / 2 + padding; + } + else { + y = xy[1] - drag->sy / 2 - padding - iconsize - padding - iconsize; + } + } + else { + x = xy[0] - 2 * padding; + + if (xy[1] + iconsize + iconsize < winsize_y) { + y = (xy[1] + iconsize) + padding; + } + else { + y = (xy[1] - iconsize) - padding; + } + } + + wm_drop_operator_draw(tooltip, x, y); + if (free_tooltip) { + MEM_freeN(tooltip); + } + } +} + +static void wm_drag_draw_default(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + int xy_tmp[2] = {UNPACK2(xy)}; + + /* Image or icon. */ + wm_drag_draw_icon(C, win, drag, xy_tmp); + + /* Item name. */ + if (drag->imb) { + int iconsize = UI_DPI_ICON_SIZE; + xy_tmp[0] = xy[0] - (drag->sx / 2); + xy_tmp[1] = xy[1] - (drag->sy / 2) - iconsize; + } + else { + xy_tmp[0] = xy[0] + 10 * UI_DPI_FAC; + xy_tmp[1] = xy[1] + 1 * UI_DPI_FAC; + } + wm_drag_draw_item_name(drag, UNPACK2(xy_tmp)); + + /* Operator name with roundbox. */ + wm_drag_draw_tooltip(C, win, drag, xy); +} + +void WM_drag_draw_default_fn(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + wm_drag_draw_default(C, win, drag, xy); +} + +/* Called in #wm_draw_window_onscreen. */ +void wm_drags_draw(bContext *C, wmWindow *win) +{ + int xy[2]; + if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) { + wm_cursor_position_get(win, &xy[0], &xy[1]); + } + else { + xy[0] = win->eventstate->xy[0]; + xy[1] = win->eventstate->xy[1]; + } + + /* Set a region. It is used in the `UI_but_active_drop_name`. */ + bScreen *screen = CTX_wm_screen(C); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, UNPACK2(xy)); + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, UNPACK2(xy)); + if (region) { + BLI_assert(!CTX_wm_area(C) && !CTX_wm_region(C)); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + } + + wmWindowManager *wm = CTX_wm_manager(C); /* Should we support multi-line drag draws? Maybe not, more types mixed won't work well. */ GPU_blend(GPU_BLEND_ALPHA); LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) { - const uchar text_col[] = {255, 255, 255, 255}; - int iconsize = UI_DPI_ICON_SIZE; - int padding = 4 * UI_DPI_FAC; - - /* image or icon */ - int x, y; - if (drag->imb) { - x = cursorx - drag->sx / 2; - y = cursory - drag->sy / 2; - - if (rect) { - drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy); - } - else { - float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */ - IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - immDrawPixelsTexScaled(&state, - x, - y, - drag->imb->x, - drag->imb->y, - GPU_RGBA8, - false, - drag->imb->rect, - drag->scale, - drag->scale, - 1.0f, - 1.0f, - col); - } - } - else { - x = cursorx - 2 * padding; - y = cursory - 2 * UI_DPI_FAC; - - if (rect) { - drag_rect_minmax(rect, x, y, x + iconsize, y + iconsize); - } - else { - UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false); - } + if (drag->active_dropbox && drag->active_dropbox->draw) { + drag->active_dropbox->draw(C, win, drag, xy); + continue; } - /* item name */ - if (drag->imb) { - x = cursorx - drag->sx / 2; - y = cursory - drag->sy / 2 - iconsize; - } - else { - x = cursorx + 10 * UI_DPI_FAC; - y = cursory + 1 * UI_DPI_FAC; - } - - if (rect) { - int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag)); - drag_rect_minmax(rect, x, y, x + w, y + iconsize); - } - else { - UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); - } - - /* operator name with roundbox */ - if (drag->tooltip[0]) { - if (drag->imb) { - x = cursorx - drag->sx / 2; - - if (cursory + drag->sy / 2 + padding + iconsize < winsize_y) { - y = cursory + drag->sy / 2 + padding; - } - else { - y = cursory - drag->sy / 2 - padding - iconsize - padding - iconsize; - } - } - else { - x = cursorx - 2 * padding; - - if (cursory + iconsize + iconsize < winsize_y) { - y = (cursory + iconsize) + padding; - } - else { - y = (cursory - iconsize) - padding; - } - } - - if (rect) { - int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag)); - drag_rect_minmax(rect, x, y, x + w, y + iconsize); - } - else { - wm_drop_operator_draw(drag->tooltip, x, y); - } - } + wm_drag_draw_default(C, win, drag, xy); } GPU_blend(GPU_BLEND_NONE); + CTX_wm_area_set(C, NULL); + CTX_wm_region_set(C, NULL); } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index b5e81528e2b..8acce240046 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -856,9 +856,9 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) wm_gesture_draw(win); } - /* needs pixel coords in screen */ + /* Needs pixel coords in screen. */ if (wm->drags.first) { - wm_drags_draw(C, win, NULL); + wm_drags_draw(C, win); } GPU_debug_group_end(); diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index d1eb10787e2..dfc8c578420 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -173,7 +173,7 @@ void wm_tablet_data_from_ghost(const struct GHOST_TabletData *tablet_data, wmTab /* wm_dropbox.c */ void wm_dropbox_free(void); void wm_drags_check_ops(bContext *C, const wmEvent *event); -void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect); +void wm_drags_draw(bContext *C, wmWindow *win); #ifdef __cplusplus } From b17038db31e0dd312dd3987fb9491bf402b3a40a Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 25 Oct 2021 10:07:00 -0300 Subject: [PATCH 1157/1500] Drag Drop: allow customizable drawing --- source/blender/windowmanager/WM_api.h | 4 ++++ source/blender/windowmanager/intern/wm_dragdrop.c | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index b4fe2f85b72..c7087b28783 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -740,6 +740,10 @@ struct wmDropBox *WM_dropbox_add( void (*copy)(struct wmDrag *, struct wmDropBox *), void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *), WMDropboxTooltipFunc tooltip); +void WM_drag_draw_item_name_fn(struct bContext *C, + struct wmWindow *win, + struct wmDrag *drag, + const int xy[2]); void WM_drag_draw_default_fn(struct bContext *C, struct wmWindow *win, struct wmDrag *drag, diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 8495fa2a082..bad58c32cdb 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -670,6 +670,17 @@ static void wm_drag_draw_item_name(wmDrag *drag, const int x, const int y) UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); } +void WM_drag_draw_item_name_fn(bContext *UNUSED(C), + wmWindow *UNUSED(win), + wmDrag *drag, + const int xy[2]) +{ + int x = xy[0] + 10 * UI_DPI_FAC; + int y = xy[1] + 1 * UI_DPI_FAC; + + wm_drag_draw_item_name(drag, x, y); +} + static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) { if (!CTX_wm_region(C)) { From a84f1c02d251a9ce6267030a46e02ed2d3ce22e1 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 25 Oct 2021 08:02:08 -0300 Subject: [PATCH 1158/1500] Assets: Snapping with visual feedback while dragging The drag and drop feature of objects in 3D View has been modified to include: - Snap the object being dragged. - Visual feedback through a box and the placement tool grid. Maniphest Tasks: T90198 Differential Revision: https://developer.blender.org/D12912 --- source/blender/blenkernel/BKE_asset.h | 17 ++++ source/blender/blenkernel/BKE_idtype.h | 5 ++ source/blender/blenkernel/BKE_main.h | 4 +- source/blender/blenkernel/intern/asset.cc | 20 +++++ source/blender/blenkernel/intern/object.c | 37 ++++++++ source/blender/blenloader/intern/readfile.c | 3 + .../editors/asset/ED_asset_mark_clear.h | 3 + .../editors/asset/intern/asset_mark_clear.cc | 19 +++++ source/blender/editors/include/ED_view3d.h | 4 +- source/blender/editors/include/UI_interface.h | 2 + source/blender/editors/interface/interface.c | 3 +- .../interface_template_asset_view.cc | 1 + source/blender/editors/object/object_add.c | 28 +++++-- source/blender/editors/space_file/file_draw.c | 2 + .../editors/space_view3d/space_view3d.c | 84 +++++++++++++++++-- .../editors/space_view3d/view3d_cursor_snap.c | 61 ++++++++------ source/blender/gpu/GPU_immediate_util.h | 4 + .../blender/gpu/intern/gpu_immediate_util.c | 27 ++++++ source/blender/makesdna/DNA_asset_types.h | 3 + source/blender/windowmanager/WM_api.h | 2 + source/blender/windowmanager/WM_types.h | 1 + .../windowmanager/intern/wm_dragdrop.c | 25 +++++- .../blender/windowmanager/intern/wm_files.c | 2 + 23 files changed, 314 insertions(+), 43 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h index 42eea41b7a7..722d142b56c 100644 --- a/source/blender/blenkernel/BKE_asset.h +++ b/source/blender/blenkernel/BKE_asset.h @@ -20,6 +20,7 @@ #pragma once +#include "BLI_compiler_attrs.h" #include "BLI_utildefines.h" #include "DNA_asset_types.h" @@ -29,11 +30,23 @@ extern "C" { #endif struct AssetLibraryReference; +struct AssetMetaData; struct BlendDataReader; struct BlendWriter; struct ID; +struct IDProperty; struct PreviewImage; +typedef void (*PreSaveFn)(void *asset_ptr, struct AssetMetaData *asset_data); + +typedef struct AssetTypeInfo { + /** + * For local assets (assets in the current .blend file), a callback to execute before the file is + * saved. + */ + PreSaveFn pre_save_fn; +} AssetTypeInfo; + struct AssetMetaData *BKE_asset_metadata_create(void); void BKE_asset_metadata_free(struct AssetMetaData **asset_data); @@ -56,6 +69,10 @@ void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data, void BKE_asset_library_reference_init_default(struct AssetLibraryReference *library_ref); +void BKE_asset_metadata_idprop_ensure(struct AssetMetaData *asset_data, struct IDProperty *prop); +struct IDProperty *BKE_asset_metadata_idprop_find(const struct AssetMetaData *asset_data, + const char *name) ATTR_WARN_UNUSED_RESULT; + struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data, const struct ID *owner_id); diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index cd656d94fce..d33c24f2c75 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -228,6 +228,11 @@ typedef struct IDTypeInfo { * \note Currently needed for some update operation on point caches. */ IDTypeLibOverrideApplyPost lib_override_apply_post; + + /** + * Callbacks for assets, based on the type of asset. + */ + struct AssetTypeInfo *asset_type_info; } IDTypeInfo; /* ********** Declaration of each IDTypeInfo. ********** */ diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 68b1b55f47f..9ded97e0003 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -244,9 +244,9 @@ void BKE_main_library_weak_reference_remove_item(struct GHash *library_weak_refe #define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id) \ { \ - ID *_id_next = (_lb)->first; \ + ID *_id_next = (ID *)(_lb)->first; \ for ((_id) = _id_next; (_id) != NULL; (_id) = _id_next) { \ - _id_next = (_id)->next; + _id_next = (ID *)(_id)->next; #define FOREACH_MAIN_LISTBASE_ID_END \ } \ diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc index dfe568729db..7bea089b9bf 100644 --- a/source/blender/blenkernel/intern/asset.cc +++ b/source/blender/blenkernel/intern/asset.cc @@ -141,6 +141,25 @@ void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data, trimmed_id.copy(asset_data->catalog_simple_name, max_simple_name_length); } +void BKE_asset_metadata_idprop_ensure(AssetMetaData *asset_data, IDProperty *prop) +{ + if (!asset_data->properties) { + IDPropertyTemplate val = {0}; + asset_data->properties = IDP_New(IDP_GROUP, &val, "AssetMetaData.properties"); + } + /* Important: The property may already exist. For now just allow always allow a newly allocated + * property, and replace the existing one as a way of updating. */ + IDP_ReplaceInGroup(asset_data->properties, prop); +} + +IDProperty *BKE_asset_metadata_idprop_find(const AssetMetaData *asset_data, const char *name) +{ + if (!asset_data->properties) { + return nullptr; + } + return IDP_GetPropertyFromGroup(asset_data->properties, name); +} + /* Queries -------------------------------------------- */ PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data), @@ -173,6 +192,7 @@ void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data) void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data) { /* asset_data itself has been read already. */ + asset_data->local_type_info = nullptr; if (asset_data->properties) { BLO_read_data_address(reader, &asset_data->properties); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index e85c6b4c7c5..45dfb9af074 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -82,6 +82,7 @@ #include "BKE_anim_visualization.h" #include "BKE_animsys.h" #include "BKE_armature.h" +#include "BKE_asset.h" #include "BKE_camera.h" #include "BKE_collection.h" #include "BKE_constraint.h" @@ -1190,6 +1191,40 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src) BLI_freelistN(&pidlist_src); } +static IDProperty *object_asset_dimensions_property(Object *ob) +{ + float dimensions[3]; + BKE_object_dimensions_get(ob, dimensions); + if (is_zero_v3(dimensions)) { + return NULL; + } + + IDPropertyTemplate idprop = {0}; + idprop.array.len = ARRAY_SIZE(dimensions); + idprop.array.type = IDP_FLOAT; + + IDProperty *property = IDP_New(IDP_ARRAY, &idprop, "dimensions"); + memcpy(IDP_Array(property), dimensions, sizeof(dimensions)); + + return property; +} + +static void object_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data) +{ + Object *ob = asset_ptr; + BLI_assert(GS(ob->id.name) == ID_OB); + + /* Update dimensions hint for the asset. */ + IDProperty *dimensions_prop = object_asset_dimensions_property(ob); + if (dimensions_prop) { + BKE_asset_metadata_idprop_ensure(asset_data, dimensions_prop); + } +} + +AssetTypeInfo AssetType_OB = { + .pre_save_fn = object_asset_pre_save, +}; + IDTypeInfo IDType_ID_OB = { .id_code = ID_OB, .id_filter = FILTER_ID_OB, @@ -1216,6 +1251,8 @@ IDTypeInfo IDType_ID_OB = { .blend_read_undo_preserve = NULL, .lib_override_apply_post = object_lib_override_apply_post, + + .asset_type_info = &AssetType_OB, }; void BKE_object_workob_clear(Object *workob) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 0b69395b4f8..600abcca818 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2211,6 +2211,9 @@ static void direct_link_id_common( if (id->asset_data) { BLO_read_data_address(reader, &id->asset_data); BKE_asset_metadata_read(reader, id->asset_data); + /* Restore runtime asset type info. */ + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + id->asset_data->local_type_info = id_type->asset_type_info; } /* Link direct data of ID properties. */ diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h index bab1d1bf8a5..8e6a8e11d69 100644 --- a/source/blender/editors/asset/ED_asset_mark_clear.h +++ b/source/blender/editors/asset/ED_asset_mark_clear.h @@ -26,6 +26,7 @@ extern "C" { struct ID; struct bContext; +struct Main; /** * Mark the datablock as asset. @@ -52,6 +53,8 @@ void ED_asset_generate_preview(const struct bContext *C, struct ID *id); * \return whether the asset metadata was actually removed; false when the ID was not an asset. */ bool ED_asset_clear_id(struct ID *id); +void ED_assets_pre_save(struct Main *bmain); + bool ED_asset_can_mark_single_from_context(const struct bContext *C); #ifdef __cplusplus diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc index 4be7376a1c3..eb254dcd28b 100644 --- a/source/blender/editors/asset/intern/asset_mark_clear.cc +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -25,7 +25,9 @@ #include "BKE_asset.h" #include "BKE_context.h" +#include "BKE_idtype.h" #include "BKE_lib_id.h" +#include "BKE_main.h" #include "BLO_readfile.h" @@ -52,7 +54,9 @@ bool ED_asset_mark_id(ID *id) id_fake_user_set(id); + const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_id(id); id->asset_data = BKE_asset_metadata_create(); + id->asset_data->local_type_info = id_type_info->asset_type_info; /* Important for asset storage to update properly! */ ED_assetlist_storage_tag_main_data_dirty(); @@ -79,6 +83,21 @@ bool ED_asset_clear_id(ID *id) return true; } +void ED_assets_pre_save(struct Main *bmain) +{ + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (!id->asset_data || !id->asset_data->local_type_info) { + continue; + } + + if (id->asset_data->local_type_info->pre_save_fn) { + id->asset_data->local_type_info->pre_save_fn(id, id->asset_data); + } + } + FOREACH_MAIN_ID_END; +} + bool ED_asset_can_mark_single_from_context(const bContext *C) { /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 67c470a005f..a0c733b2ebf 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -276,12 +276,15 @@ typedef struct V3DSnapCursorState { eV3DPlaceOrient plane_orient; uchar color_line[4]; uchar color_point[4]; + uchar color_box[4]; float *prevpoint; + float box_dimensions[3]; short snap_elem_force; /* If zero, use scene settings. */ short plane_axis; bool use_plane_axis_auto; bool draw_point; bool draw_plane; + bool draw_box; } V3DSnapCursorState; void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state); @@ -293,7 +296,6 @@ V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state, const struct bContext *C, const int x, const int y); - struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *scene); void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d, const float loc_prev[3], diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 4cd921bc61e..1ac20a5d070 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -37,6 +37,7 @@ extern "C" { struct ARegion; struct AssetFilterSettings; struct AssetHandle; +struct AssetMetaData; struct AutoComplete; struct EnumPropertyItem; struct FileDirEntry; @@ -785,6 +786,7 @@ void UI_but_drag_set_id(uiBut *but, struct ID *id); void UI_but_drag_set_asset(uiBut *but, const struct AssetHandle *asset, const char *path, + struct AssetMetaData *metadata, int import_type, /* eFileAssetImportType */ int icon, struct ImBuf *imb, diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 5068969946a..62c84ed38ff 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -6231,12 +6231,13 @@ void UI_but_drag_set_id(uiBut *but, ID *id) void UI_but_drag_set_asset(uiBut *but, const AssetHandle *asset, const char *path, + struct AssetMetaData *metadata, int import_type, int icon, struct ImBuf *imb, float scale) { - wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, path, import_type); + wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, metadata, path, import_type); /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the * #wmDropBox. diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index f27b37a27de..d3ce7ebc3db 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -70,6 +70,7 @@ static void asset_view_item_but_drag_set(uiBut *but, UI_but_drag_set_asset(but, asset_handle, BLI_strdup(blend_path), + ED_asset_handle_get_metadata(asset_handle), FILE_ASSET_IMPORT_APPEND, ED_asset_handle_get_preview_icon_id(asset_handle), imbuf, diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 114f540b614..629f40e0ae9 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -3533,12 +3533,6 @@ static int object_add_named_exec(bContext *C, wmOperator *op) basen->object->visibility_flag &= ~OB_HIDE_VIEWPORT; - int mval[2]; - if (object_add_drop_xy_get(C, op, &mval)) { - ED_object_location_from_view(C, basen->object->loc); - ED_view3d_cursor3d_position(C, mval, false, basen->object->loc); - } - /* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or * BKE_view_layer_base_deselect_all(). */ ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT); @@ -3556,13 +3550,29 @@ static int object_add_named_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); ED_outliner_select_sync_from_object_tag(C); + PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix"); + if (RNA_property_is_set(op->ptr, prop_matrix)) { + Object *ob_add = basen->object; + RNA_property_float_get_array(op->ptr, prop_matrix, &ob_add->obmat[0][0]); + BKE_object_apply_mat4(ob_add, ob_add->obmat, true, true); + + DEG_id_tag_update(&ob_add->id, ID_RECALC_TRANSFORM); + } + else { + int mval[2]; + if (object_add_drop_xy_get(C, op, &mval)) { + ED_object_location_from_view(C, basen->object->loc); + ED_view3d_cursor3d_position(C, mval, false, basen->object->loc); + } + } + return OPERATOR_FINISHED; } void OBJECT_OT_add_named(wmOperatorType *ot) { /* identifiers */ - ot->name = "Add Named Object"; + ot->name = "Add Object"; ot->description = "Add named object"; ot->idname = "OBJECT_OT_add_named"; @@ -3594,6 +3604,10 @@ void OBJECT_OT_add_named(wmOperatorType *ot) RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add"); + prop = RNA_def_float_matrix( + ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + object_add_drop_xy_props(ot); } diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 24b24eb81dd..5c6b753d4a5 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -190,6 +190,7 @@ static void file_draw_icon(const SpaceFile *sfile, UI_but_drag_set_asset(but, &(AssetHandle){.file_data = file}, BLI_strdup(blend_path), + file->asset_data, asset_params->import_type, icon, preview_image, @@ -515,6 +516,7 @@ static void file_draw_preview(const SpaceFile *sfile, UI_but_drag_set_asset(but, &(AssetHandle){.file_data = file}, BLI_strdup(blend_path), + file->asset_data, asset_params->import_type, icon, imb, diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index eb30d0987ab..eaf90aabe8c 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -40,6 +40,7 @@ #include "BLT_translation.h" +#include "BKE_asset.h" #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_global.h" @@ -515,6 +516,45 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C, return WM_drag_is_ID_type(drag, id_type); } +static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag) +{ + V3DSnapCursorState *state = drop->draw_data; + if (state) { + return; + } + state = drop->draw_data = ED_view3d_cursor_snap_active(); + state->draw_point = true; + state->draw_plane = true; + + float dimensions[3] = {0.0f}; + if (drag->type == WM_DRAG_ID) { + Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB); + BKE_object_dimensions_get(ob, dimensions); + } + else { + struct AssetMetaData *meta_data = WM_drag_get_asset_meta_data(drag, ID_OB); + IDProperty *dimensions_prop = BKE_asset_metadata_idprop_find(meta_data, "dimensions"); + if (dimensions_prop) { + copy_v3_v3(dimensions, IDP_Array(dimensions_prop)); + } + } + + if (!is_zero_v3(dimensions)) { + mul_v3_v3fl(state->box_dimensions, dimensions, 0.5f); + UI_GetThemeColor4ubv(TH_GIZMO_PRIMARY, state->color_box); + state->draw_box = true; + } +} + +static void view3d_ob_drop_draw_deactivate(struct wmDropBox *drop, wmDrag *UNUSED(drag)) +{ + V3DSnapCursorState *state = drop->draw_data; + if (state) { + ED_view3d_cursor_snap_deactive(state); + drop->draw_data = NULL; + } +} + static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_OB); @@ -639,6 +679,31 @@ static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ const bool is_imported_id = drag->type == WM_DRAG_ASSET; RNA_boolean_set(drop->ptr, "duplicate", !is_imported_id); + + V3DSnapCursorState *snap_state = drop->draw_data; + if (snap_state) { + Object *ob = (Object *)id; + float obmat_final[4][4]; + + V3DSnapCursorData *snap_data; + snap_data = ED_view3d_cursor_snap_data_get(snap_state, NULL, 0, 0); + copy_m4_m3(obmat_final, snap_data->plane_omat); + copy_v3_v3(obmat_final[3], snap_data->loc); + + float scale[3]; + mat4_to_size(scale, ob->obmat); + rescale_m4(obmat_final, scale); + + BoundBox *bb = BKE_object_boundbox_get(ob); + if (bb) { + float offset[3]; + BKE_boundbox_calc_center_aabb(bb, offset); + offset[2] = bb->vec[0][2]; + mul_mat3_m4_v3(obmat_final, offset); + sub_v3_v3(obmat_final[3], offset); + } + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); + } } static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop) @@ -703,12 +768,19 @@ static void view3d_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, - "OBJECT_OT_add_named", - view3d_ob_drop_poll, - view3d_ob_drop_copy, - WM_drag_free_imported_drag_ID, - NULL); + struct wmDropBox *drop; + drop = WM_dropbox_add(lb, + "OBJECT_OT_add_named", + view3d_ob_drop_poll, + view3d_ob_drop_copy, + WM_drag_free_imported_drag_ID, + NULL); + + drop->draw = WM_drag_draw_item_name_fn; + drop->draw_activate = view3d_ob_drop_draw_activate; + drop->draw_deactivate = view3d_ob_drop_draw_deactivate; + drop->opcontext = WM_OP_EXEC_DEFAULT; /* Not really needed. */ + WM_dropbox_add(lb, "OBJECT_OT_drop_named_material", view3d_mat_drop_poll, diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 1cb650910ce..5eb9ec3625c 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -58,7 +58,6 @@ typedef struct SnapStateIntern { V3DSnapCursorState snap_state; - float prevpoint_stack[3]; int state_active_prev; bool is_active; } SnapStateIntern; @@ -75,6 +74,8 @@ typedef struct SnapCursorDataIntern { const Scene *scene; short snap_elem_hidden; + float prevpoint_stack[3]; + /* Copy of the parameters of the last event state in order to detect updates. */ struct { int x; @@ -94,17 +95,6 @@ typedef struct SnapCursorDataIntern { bool is_initiated; } SnapCursorDataIntern; -static void UNUSED_FUNCTION(v3d_cursor_snap_state_init)(V3DSnapCursorState *state) -{ - state->prevpoint = NULL; - state->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT); - state->plane_axis = 2; - rgba_uchar_args_set(state->color_point, 255, 255, 255, 255); - rgba_uchar_args_set(state->color_line, 255, 255, 255, 128); - state->draw_point = true; - state->draw_plane = false; -} static SnapCursorDataIntern g_data_intern = { .state_default = {.prevpoint = NULL, .snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | @@ -113,8 +103,9 @@ static SnapCursorDataIntern g_data_intern = { .plane_axis = 2, .color_point = {255, 255, 255, 255}, .color_line = {255, 255, 255, 128}, - .draw_point = true, - .draw_plane = false}}; + .color_box = {255, 255, 255, 128}, + .box_dimensions = {1.0f, 1.0f, 1.0f}, + .draw_point = true}}; /** * Calculate a 3x3 orientation matrix from the surface under the cursor. @@ -373,6 +364,24 @@ static void v3d_cursor_plane_draw(const RegionView3D *rv3d, } } +static void cursor_box_draw(const float dimensions[3], uchar color[4]) +{ + GPUVertFormat *format = immVertexFormat(); + const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_smooth(true); + GPU_line_width(1.0f); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor4ubv(color); + imm_draw_cube_corners_3d(pos_id, (float[3]){0.0f, 0.0f, dimensions[2]}, dimensions, 0.15f); + immUnbindProgram(); + + GPU_line_smooth(false); + GPU_blend(GPU_BLEND_NONE); +} + void ED_view3d_cursor_snap_draw_util(RegionView3D *rv3d, const float loc_prev[3], const float loc_curr[3], @@ -601,7 +610,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, ushort snap_elements = v3d_cursor_snap_elements(state, scene); data_intern->snap_elem_hidden = 0; - const bool draw_plane = state->draw_plane; + const bool draw_plane = state->draw_plane || state->draw_box; if (draw_plane && !(snap_elements & SCE_SNAP_MODE_FACE)) { data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; snap_elements |= SCE_SNAP_MODE_FACE; @@ -674,6 +683,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, } if (draw_plane) { + RegionView3D *rv3d = region->regiondata; bool orient_surface = snap_elem && (state->plane_orient == V3D_PLACE_ORIENT_SURFACE); if (orient_surface) { copy_m3_m4(omat, obmat); @@ -686,7 +696,6 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, ED_transform_calc_orientation_from_type_ex( scene, view_layer, v3d, region->regiondata, ob, ob, orient_index, pivot_point, omat); - RegionView3D *rv3d = region->regiondata; if (state->use_plane_axis_auto) { mat3_align_axis_to_v3(omat, state->plane_axis, rv3d->viewinv[2]); } @@ -699,6 +708,9 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, orthogonalize_m3(omat, state->plane_axis); if (orient_surface) { + if (dot_v3v3(rv3d->viewinv[2], face_nor) < 0.0f) { + negate_v3(face_nor); + } v3d_cursor_poject_surface_normal(face_nor, obmat, omat); } } @@ -791,7 +803,7 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust v3d_cursor_snap_update(state, C, wm, depsgraph, scene, region, v3d, x, y); } - const bool draw_plane = state->draw_plane; + const bool draw_plane = state->draw_plane || state->draw_box; if (!snap_data->snap_elem && !draw_plane) { return; } @@ -802,8 +814,6 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust GPU_matrix_projection_set(rv3d->winmat); GPU_matrix_set(rv3d->viewmat); - GPU_blend(GPU_BLEND_ALPHA); - float matrix[4][4]; if (draw_plane) { copy_m4_m3(matrix, snap_data->plane_omat); @@ -812,7 +822,7 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust v3d_cursor_plane_draw(rv3d, state->plane_axis, matrix); } - if (snap_data->snap_elem && state->draw_point) { + if (snap_data->snap_elem && (state->draw_point || state->draw_box)) { const float *prev_point = (snap_data->snap_elem & SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? state->prevpoint : NULL; @@ -829,7 +839,10 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust snap_data->snap_elem); } - GPU_blend(GPU_BLEND_NONE); + if (state->draw_box) { + GPU_matrix_mul(matrix); + cursor_box_draw(state->box_dimensions, state->color_box); + } /* Restore matrix. */ wmWindowViewport(CTX_wm_window(C)); @@ -942,10 +955,10 @@ void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state) void ED_view3d_cursor_snap_prevpoint_set(V3DSnapCursorState *state, const float prev_point[3]) { - SnapStateIntern *state_intern = (SnapStateIntern *)state; + SnapCursorDataIntern *data_intern = &g_data_intern; if (prev_point) { - copy_v3_v3(state_intern->prevpoint_stack, prev_point); - state->prevpoint = state_intern->prevpoint_stack; + copy_v3_v3(data_intern->prevpoint_stack, prev_point); + state->prevpoint = data_intern->prevpoint_stack; } else { state->prevpoint = NULL; diff --git a/source/blender/gpu/GPU_immediate_util.h b/source/blender/gpu/GPU_immediate_util.h index 3ea809d59a7..793348ad8d2 100644 --- a/source/blender/gpu/GPU_immediate_util.h +++ b/source/blender/gpu/GPU_immediate_util.h @@ -80,6 +80,10 @@ void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2); void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]); void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]); +void imm_draw_cube_corners_3d(uint pos, + const float co[3], + const float aspect[3], + const float factor); void imm_draw_cylinder_fill_normal_3d( uint pos, uint nor, float base, float top, float height, int slices, int stacks); diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c index d18dc862ce7..d848ea09a2a 100644 --- a/source/blender/gpu/intern/gpu_immediate_util.c +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -428,6 +428,33 @@ void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]) immEnd(); } +void imm_draw_cube_corners_3d(uint pos, + const float co[3], + const float aspect[3], + const float factor) +{ + float coords[ARRAY_SIZE(cube_coords)][3]; + + for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { + madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect); + } + + immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 4); + for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) { + float vec[3], co[3]; + sub_v3_v3v3(vec, coords[cube_line_index[i][1]], coords[cube_line_index[i][0]]); + mul_v3_fl(vec, factor); + + immVertex3fv(pos, coords[cube_line_index[i][0]]); + add_v3_v3v3(co, coords[cube_line_index[i][0]], vec); + immVertex3fv(pos, co); + sub_v3_v3v3(co, coords[cube_line_index[i][1]], vec); + immVertex3fv(pos, co); + immVertex3fv(pos, coords[cube_line_index[i][1]]); + } + immEnd(); +} + /** * Draw a cylinder. Replacement for gluCylinder. * _warning_ : Slow, better use it only if you no other choices. diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index 60de254fec7..bd604b90b5a 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -56,6 +56,9 @@ typedef struct AssetFilterSettings { * more than that from the file. So pointers to other IDs or ID data are strictly forbidden. */ typedef struct AssetMetaData { + /** Runtime type, to reference event callbacks. Only valid for local assets. */ + struct AssetTypeInfo *local_type_info; + /** Custom asset meta-data. Cannot store pointers to IDs (#STRUCT_NO_DATABLOCK_IDPROPERTIES)! */ struct IDProperty *properties; diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index c7087b28783..2988aacc4d3 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -757,9 +757,11 @@ struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short id bool WM_drag_is_ID_type(const struct wmDrag *drag, int idcode); wmDragAsset *WM_drag_create_asset_data(const struct AssetHandle *asset, + struct AssetMetaData *metadata, const char *path, int import_type); struct wmDragAsset *WM_drag_get_asset_data(const struct wmDrag *drag, int idcode); +struct AssetMetaData *WM_drag_get_asset_meta_data(const struct wmDrag *drag, int idcode); struct ID *WM_drag_get_local_ID_or_import_from_asset(const struct wmDrag *drag, int idcode); void WM_drag_free_imported_drag_ID(struct Main *bmain, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index bbfc9d53e44..2813047f0e4 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -989,6 +989,7 @@ typedef struct wmDragAsset { /* Always freed. */ const char *path; int id_type; + struct AssetMetaData *metadata; int import_type; /* eFileAssetImportType */ /* FIXME: This is temporary evil solution to get scene/view-layer/etc in the copy callback of the diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index bad58c32cdb..f78bd528c5e 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -40,6 +40,7 @@ #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -405,11 +406,15 @@ bool WM_drag_is_ID_type(const wmDrag *drag, int idcode) /** * \note: Does not store \a asset in any way, so it's fine to pass a temporary. */ -wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset, const char *path, int import_type) +wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset, + AssetMetaData *metadata, + const char *path, + int import_type) { wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset"); BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name)); + asset_drag->metadata = metadata; asset_drag->path = path; asset_drag->id_type = ED_asset_handle_get_id_type(asset); asset_drag->import_type = import_type; @@ -433,6 +438,21 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : NULL; } +struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode) +{ + wmDragAsset *drag_asset = WM_drag_get_asset_data(drag, idcode); + if (drag_asset) { + return drag_asset->metadata; + } + + ID *local_id = WM_drag_get_local_ID(drag, idcode); + if (local_id) { + return local_id->asset_data; + } + + return NULL; +} + static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) { const char *name = asset_drag->name; @@ -566,11 +586,12 @@ void WM_drag_add_asset_list_item( drag_asset->asset_data.local_id = local_id; } else { + AssetMetaData *metadata = ED_asset_handle_get_metadata(asset); char asset_blend_path[FILE_MAX_LIBEXTRA]; ED_asset_handle_get_full_library_path(C, asset_library_ref, asset, asset_blend_path); drag_asset->is_external = true; drag_asset->asset_data.external_info = WM_drag_create_asset_data( - asset, BLI_strdup(asset_blend_path), FILE_ASSET_IMPORT_APPEND); + asset, metadata, BLI_strdup(asset_blend_path), FILE_ASSET_IMPORT_APPEND); } BLI_addtail(&drag->asset_items, drag_asset); } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 2525c627785..8fcc30dfed7 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1788,6 +1788,7 @@ static bool wm_file_write(bContext *C, /* Call pre-save callbacks before writing preview, * that way you can generate custom file thumbnail. */ BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE); + ED_assets_pre_save(bmain); /* Enforce full override check/generation on file save. */ BKE_lib_override_library_main_operations_create(bmain, true); @@ -2105,6 +2106,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) } BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE); + ED_assets_pre_save(bmain); /* check current window and close it if temp */ if (win && WM_window_is_temp_screen(win)) { From 972677b25e1d84e4c02d0e55b12ce87661faff5e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 25 Oct 2021 10:01:07 -0500 Subject: [PATCH 1159/1500] UI: Improve layout of custom property edit panel This patch makes the layout of the custom property panel more coherent with the rest of the property editor interface, makes it less busy, allows more space for the buttons for the actual properties, and simplifies editing values of unsupported property types or long arrays. - Remove the box around each property. - Use an non-embossed X icon for deleting. - Use an "edit" icon instead of the text for the meta-data edit operator. The "gear" icon used for editing isn't ideal here. - Increase the max array length for drawing the values directly to 8. - Add an "Edit Property Value" operator for dictionaries or longer arrays. - Replace the "Library Override" text with an icon. - Use a proper split factor, the same as the rest of the UI. Differential Revision: https://developer.blender.org/D12805 --- release/scripts/modules/rna_prop_ui.py | 100 +++++++++------------ release/scripts/startup/bl_operators/wm.py | 74 +++++++++++++-- 2 files changed, 106 insertions(+), 68 deletions(-) diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 2cc806be10d..fce59a26c38 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -28,7 +28,7 @@ ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector, bpy_prop_array) # Maximum length of an array property for which a multi-line # edit field will be displayed in the Custom Properties panel. -MAX_DISPLAY_ROWS = 4 +MAX_DISPLAY_ROWS = 8 def rna_idprop_quote_path(prop): @@ -134,18 +134,7 @@ def rna_idprop_ui_create( def draw(layout, context, context_member, property_type, *, use_edit=True): - - def assign_props(prop, value, key): - prop.data_path = context_member - prop.property_name = key - - try: - prop.value = str(value) - except: - pass - rna_item, context_member = rna_idprop_context_value(context, context_member, property_type) - # poll should really get this... if not rna_item: return @@ -164,17 +153,15 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): # TODO: Allow/support adding new custom props to overrides. if use_edit and not is_lib_override: row = layout.row() - props = row.operator("wm.properties_add", text="Add") + props = row.operator("wm.properties_add", text="New", icon='ADD') props.data_path = context_member del row + layout.separator() show_developer_ui = context.preferences.view.show_developer_ui rna_properties = {prop.identifier for prop in rna_item.bl_rna.properties if prop.is_runtime} if items else None - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True) + layout.use_property_decorate = False for key, value in items: is_rna = (key in rna_properties) @@ -188,57 +175,50 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): if to_dict: value = to_dict() - val_draw = str(value) elif to_list: value = to_list() - val_draw = str(value) - else: - val_draw = value - row = layout.row(align=True) - box = row.box() + split = layout.split(factor=0.4, align=True) + label_row = split.row() + label_row.alignment = 'RIGHT' + label_row.label(text=key, translate=False) + + value_row = split.row(align=True) + value_column = value_row.column(align=True) + + is_long_array = to_list and len(value) >= MAX_DISPLAY_ROWS + + if is_rna: + value_column.prop(rna_item, key, text="") + elif to_dict or is_long_array: + props = value_column.operator("wm.properties_edit_value", text="Edit Value") + props.data_path = context_member + props.property_name = key + else: + value_column.prop(rna_item, '["%s"]' % escape_identifier(key), text="") + + operator_row = value_row.row() + + # Do not allow editing of overridden properties (we cannot use a poll function + # of the operators here since they's have no access to the specific property). + operator_row.enabled = not(is_lib_override and key in rna_item.id_data.override_library.reference) if use_edit: - split = box.split(factor=0.75) - row = split.row() - else: - split = box.split(factor=1.00) - row = split.row() - - row.alignment = 'RIGHT' - - row.label(text=key, translate=False) - - # Explicit exception for arrays. - show_array_ui = to_list and not is_rna and 0 < len(value) <= MAX_DISPLAY_ROWS - - if show_array_ui and isinstance(value[0], (int, float)): - row.prop(rna_item, '["%s"]' % escape_identifier(key), text="") - elif to_dict or to_list: - row.label(text=val_draw, translate=False) - else: if is_rna: - row.prop(rna_item, key, text="") - else: - row.prop(rna_item, '["%s"]' % escape_identifier(key), text="") - - if use_edit: - row = split.row(align=True) - # Do not allow editing of overridden properties (we cannot use a poll function - # of the operators here since they's have no access to the specific property). - row.enabled = not(is_lib_override and key in rna_item.id_data.override_library.reference) - if is_rna: - row.label(text="API Defined") + operator_row.label(text="API Defined") elif is_lib_override: - row.label(text="Library Override") + operator_row.active = False + operator_row.label(text="", icon='DECORATE_LIBRARY_OVERRIDE') else: - props = row.operator("wm.properties_edit", text="Edit") - assign_props(props, val_draw, key) - props = row.operator("wm.properties_remove", text="", icon='REMOVE') - assign_props(props, val_draw, key) - - del flow - + props = operator_row.operator("wm.properties_edit", text="", icon='PREFERENCES', emboss=False) + props.data_path = context_member + props.property_name = key + props = operator_row.operator("wm.properties_remove", text="", icon='X', emboss=False) + props.data_path = context_member + props.property_name = key + else: + # Add some spacing, so the right side of the buttons line up with layouts with decorators. + operator_row.label(text="", icon='BLANK1') class PropertyPanel: """ diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 170b9f3ae44..4dbdc6e633e 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1442,7 +1442,7 @@ class WM_OT_properties_edit(Operator): # Convert an old property for a string, avoiding unhelpful string representations for custom list types. @staticmethod - def _convert_old_property_to_string(item, name): + def convert_custom_property_to_string(item, name): # The IDProperty group view API currently doesn't have a "lookup" method. for key, value in item.items(): if key == name: @@ -1461,7 +1461,8 @@ class WM_OT_properties_edit(Operator): # Retrieve the current type of the custom property on the RNA struct. Some properties like group properties # can be created in the UI, but editing their meta-data isn't supported. In that case, return 'PYTHON'. - def _get_property_type(self, item, property_name): + @staticmethod + def get_property_type(item, property_name): from rna_prop_ui import ( rna_idprop_value_item_type, ) @@ -1549,17 +1550,17 @@ class WM_OT_properties_edit(Operator): return self._convert_new_value_single(item[name_old], float) if prop_type_new == 'INT_ARRAY': - prop_type_old = self._get_property_type(item, name_old) + prop_type_old = self.get_property_type(item, name_old) if prop_type_old in {'INT', 'FLOAT', 'INT_ARRAY', 'FLOAT_ARRAY'}: return self._convert_new_value_array(item[name_old], int, self.array_length) if prop_type_new == 'FLOAT_ARRAY': - prop_type_old = self._get_property_type(item, name_old) + prop_type_old = self.get_property_type(item, name_old) if prop_type_old in {'INT', 'FLOAT', 'FLOAT_ARRAY', 'INT_ARRAY'}: return self._convert_new_value_array(item[name_old], float, self.array_length) if prop_type_new == 'STRING': - return self._convert_old_property_to_string(item, name_old) + return self.convert_custom_property_to_string(item, name_old) # If all else fails, create an empty string property. That should avoid errors later on anyway. return "" @@ -1672,7 +1673,7 @@ class WM_OT_properties_edit(Operator): self.report({'ERROR'}, "Cannot edit properties from override data") return {'CANCELLED'} - prop_type_old = self._get_property_type(item, name_old) + prop_type_old = self.get_property_type(item, name_old) prop_type_new = self.property_type self._old_prop_name[:] = [name] @@ -1716,14 +1717,14 @@ class WM_OT_properties_edit(Operator): return {'CANCELLED'} # Set operator's property type with the type of the existing property, to display the right settings. - old_type = self._get_property_type(item, name) + old_type = self.get_property_type(item, name) self.property_type = old_type self.last_property_type = old_type # So that the operator can do something for unsupported properties, change the property into # a string, just for editing in the dialog. When the operator executes, it will be converted back # into a python value. Always do this conversion, in case the Python property edit type is selected. - self.eval_string = self._convert_old_property_to_string(item, name) + self.eval_string = self.convert_custom_property_to_string(item, name) if old_type != 'PYTHON': self._fill_old_ui_data(item, name) @@ -1845,6 +1846,62 @@ class WM_OT_properties_edit(Operator): layout.prop(self, "description") +# Edit the value of a custom property with the given name on the RNA struct at the given data path. +# For supported types, this simply acts as a convenient way to create a popup for a specific property +# and draws the custom property value directly in the popup. For types like groups which can't be edited +# directly with buttons, instead convert the value to a string, evaluate the changed string when executing. +class WM_OT_properties_edit_value(Operator): + """Edit the value of a custom property""" + bl_idname = "wm.properties_edit_value" + bl_label = "Edit Property Value" + # register only because invoke_props_popup requires. + bl_options = {'REGISTER', 'INTERNAL'} + + data_path: rna_path + property_name: rna_custom_property_name + + # Store the value converted to a string as a fallback for otherwise unsupported types. + eval_string: StringProperty( + name="Value", + description="Value for custom property types that can only be edited as a Python expression" + ) + + def execute(self, context): + if self.eval_string: + rna_item = eval("context.%s" % self.data_path) + try: + new_value = eval(self.eval_string) + except Exception as ex: + self.report({'WARNING'}, "Python evaluation failed: " + str(ex)) + return {'CANCELLED'} + rna_item[self.property_name] = new_value + return {'FINISHED'} + + def invoke(self, context, _event): + rna_item = eval("context.%s" % self.data_path) + + if WM_OT_properties_edit.get_property_type(rna_item, self.property_name) == 'PYTHON': + self.eval_string = WM_OT_properties_edit.convert_custom_property_to_string(rna_item, + self.property_name) + else: + self.eval_string = "" + + wm = context.window_manager + return wm.invoke_props_dialog(self) + + def draw(self, context): + from bpy.utils import escape_identifier + + rna_item = eval("context.%s" % self.data_path) + + layout = self.layout + if WM_OT_properties_edit.get_property_type(rna_item, self.property_name) == 'PYTHON': + layout.prop(self, "eval_string") + else: + col = layout.column(align=True) + col.prop(rna_item, '["%s"]' % escape_identifier(self.property_name), text="") + + class WM_OT_properties_add(Operator): """Add your own property to the data-block""" bl_idname = "wm.properties_add" @@ -3056,6 +3113,7 @@ classes = ( WM_OT_properties_add, WM_OT_properties_context_change, WM_OT_properties_edit, + WM_OT_properties_edit_value, WM_OT_properties_remove, WM_OT_sysinfo, WM_OT_owner_disable, From 08a1492ae53fb1575cffb0d11fa3c7e5bdc80b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 25 Oct 2021 17:12:56 +0200 Subject: [PATCH 1160/1500] Asset Browser: apply search filter to tags as well The search bar in the asset browser now also matches on asset tags. - Matching is done on entire tags, so searching for "redder" will not show assets tagged with "red". - All assets are shown that have at least one matching tag. So searching for "red green" will show all assets with either "red" or "green" tags (or both, of course). - Searching is case-insensitive. - Only assets from the active catalog are shown; if all assets should be searched through, users can select the "All" catalog. Manifest Task: T82679 --- source/blender/editors/space_file/filelist.c | 58 +++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index af26de1a3bd..1aabe5fde2b 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -918,6 +918,43 @@ static void prepare_filter_asset_library(const FileList *filelist, FileListFilte file_ensure_updated_catalog_filter_data(filter->asset_catalog_filter, filelist->asset_library); } +/** + * Copy a string from source to dest, but prefix and suffix it with a single space. + * Assumes `dest` has at least space enough for the two spaces. + */ +static void tag_copy_with_spaces(char *dest, const char *source, const size_t dest_size) +{ + BLI_assert(dest_size > 2); + const size_t source_length = BLI_strncpy_rlen(dest + 1, source, dest_size - 2); + dest[0] = ' '; + dest[source_length + 1] = ' '; + dest[source_length + 2] = '\0'; +} + +/** + * Return whether at least one tag matches the search filter. + * Tags are searched as "entire words", so instead of searching for "tag" in the + * filter string, this function searches for " tag ". Assumes the search filter + * starts and ends with a space. + * + * Here the tags on the asset are written in set notation: + * + * asset_tag_matches_filter(" some tags ", {"some", "blue"}) -> true + * asset_tag_matches_filter(" some tags ", {"som", "tag"}) -> false + * asset_tag_matches_filter(" some tags ", {}) -> false + */ +static bool asset_tag_matches_filter(const char *filter_search, const AssetMetaData *asset_data) +{ + LISTBASE_FOREACH (const AssetTag *, asset_tag, &asset_data->tags) { + char tag_name[MAX_NAME + 2]; /* sizeof(AssetTag::name) + 2 */ + tag_copy_with_spaces(tag_name, asset_tag->name, sizeof(tag_name)); + if (BLI_strcasestr(filter_search, tag_name) != NULL) { + return true; + } + } + return false; +} + static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter) { const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file); @@ -933,19 +970,22 @@ static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter) return true; } - /* filter->filter_search contains "*the search text*"; this code strips the asterisks. - * For a simple name search it would work to call fnmatch() here, but that - * would be inefficient when expanding to searching for tags as well.*/ - char filter_search[64]; /* sizeof(filter->filter_search) - 1 */ - const size_t string_length = STRNCPY_RLEN(filter_search, filter->filter_search + 1); - filter_search[string_length - 1] = '\0'; + /* filter->filter_search contains "*the search text*". */ + char filter_search[66]; /* sizeof(FileListFilter::filter_search) */ + const size_t string_length = STRNCPY_RLEN(filter_search, filter->filter_search); - if (BLI_strcasestr(file->name, filter_search) != NULL) { + /* When doing a name comparison, get rid of the leading/trailing asterisks. */ + filter_search[string_length - 1] = '\0'; + if (BLI_strcasestr(file->name, filter_search + 1) != NULL) { return true; } - /* TODO: search for matching tag. */ - return false; + /* Replace the asterisks with spaces, so that we can do matching on " sometag "; that way + * an artist searching for "redder" doesn't result in a match for the tag "red". */ + filter_search[string_length - 1] = ' '; + filter_search[0] = ' '; + + return asset_tag_matches_filter(filter_search, asset_data); } static bool is_filtered_lib_type(FileListInternEntry *file, From 046a99d5807000e9015119399a8483ba381917bb Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 25 Oct 2021 12:31:46 -0300 Subject: [PATCH 1161/1500] Cleanup: silence warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` warning: assignment discards ‘const’ qualifier from pointer target warning: declaration of ‘co’ shadows a parameter ``` --- source/blender/gpu/intern/gpu_immediate_util.c | 17 ++++++++++------- .../blender/windowmanager/intern/wm_dragdrop.c | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c index d848ea09a2a..aeed03080a3 100644 --- a/source/blender/gpu/intern/gpu_immediate_util.c +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -441,16 +441,19 @@ void imm_draw_cube_corners_3d(uint pos, immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 4); for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) { - float vec[3], co[3]; + float vec[3], _co[3]; sub_v3_v3v3(vec, coords[cube_line_index[i][1]], coords[cube_line_index[i][0]]); mul_v3_fl(vec, factor); - immVertex3fv(pos, coords[cube_line_index[i][0]]); - add_v3_v3v3(co, coords[cube_line_index[i][0]], vec); - immVertex3fv(pos, co); - sub_v3_v3v3(co, coords[cube_line_index[i][1]], vec); - immVertex3fv(pos, co); - immVertex3fv(pos, coords[cube_line_index[i][1]]); + copy_v3_v3(_co, coords[cube_line_index[i][0]]); + immVertex3fv(pos, _co); + add_v3_v3(_co, vec); + immVertex3fv(pos, _co); + + copy_v3_v3(_co, coords[cube_line_index[i][1]]); + immVertex3fv(pos, _co); + sub_v3_v3(_co, vec); + immVertex3fv(pos, _co); } immEnd(); } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index f78bd528c5e..c60e47a3b0f 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -711,7 +711,7 @@ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const int iconsize = UI_DPI_ICON_SIZE; int padding = 4 * UI_DPI_FAC; - char *tooltip = NULL; + const char *tooltip = NULL; bool free_tooltip = false; if (UI_but_active_drop_name(C)) { tooltip = IFACE_("Paste name"); @@ -747,7 +747,7 @@ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const wm_drop_operator_draw(tooltip, x, y); if (free_tooltip) { - MEM_freeN(tooltip); + MEM_freeN((void *)tooltip); } } } From 3be91d6da54342fd8baa5701d4f868d055cc421a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 25 Oct 2021 13:39:19 -0500 Subject: [PATCH 1162/1500] Cleanup: Remove unused functions, make functions static --- source/blender/editors/include/UI_interface.h | 10 ---- .../editors/interface/interface_draw.c | 29 ----------- .../editors/interface/interface_intern.h | 1 - .../editors/interface/interface_widgets.c | 51 +++---------------- 4 files changed, 8 insertions(+), 83 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 1ac20a5d070..b6e33d6ed0d 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -424,10 +424,6 @@ typedef enum eButGradientType { * Functions to draw various shapes, taking theme settings into account. * Used for code that draws its own UI style elements. */ -void UI_draw_anti_tria( - float x1, float y1, float x2, float y2, float x3, float y3, const float color[4]); -void UI_draw_anti_fan(float tri_array[][2], unsigned int length, const float color[4]); - void UI_draw_roundbox_corner_set(int type); void UI_draw_roundbox_aa(const struct rctf *rect, bool filled, float rad, const float color[4]); void UI_draw_roundbox_4fv(const struct rctf *rect, bool filled, float rad, const float col[4]); @@ -438,12 +434,6 @@ void UI_draw_roundbox_3ub_alpha(const struct rctf *rect, unsigned char alpha); void UI_draw_roundbox_3fv_alpha( const struct rctf *rect, bool filled, float rad, const float col[3], float alpha); -void UI_draw_roundbox_shade_x(const struct rctf *rect, - bool filled, - float rad, - float shadetop, - float shadedown, - const float col[4]); void UI_draw_roundbox_4fv_ex(const struct rctf *rect, const float inner1[4], const float inner2[4], diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 6cb0fcd499c..e45a5fc61c6 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -178,35 +178,6 @@ void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float UI_draw_roundbox_4fv_ex(rect, (filled) ? col : NULL, NULL, 1.0f, col, U.pixelsize, rad); } -/* linear horizontal shade within button or in outline */ -/* view2d scrollers use it */ -void UI_draw_roundbox_shade_x( - const rctf *rect, bool filled, float rad, float shadetop, float shadedown, const float col[4]) -{ - float inner1[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - float inner2[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - float outline[4]; - - if (filled) { - inner1[0] = min_ff(1.0f, col[0] + shadetop); - inner1[1] = min_ff(1.0f, col[1] + shadetop); - inner1[2] = min_ff(1.0f, col[2] + shadetop); - inner1[3] = 1.0f; - inner2[0] = max_ff(0.0f, col[0] + shadedown); - inner2[1] = max_ff(0.0f, col[1] + shadedown); - inner2[2] = max_ff(0.0f, col[2] + shadedown); - inner2[3] = 1.0f; - } - - /* TODO: non-filled box don't have gradients. Just use middle color. */ - outline[0] = clamp_f(col[0] + shadetop + shadedown, 0.0f, 1.0f); - outline[1] = clamp_f(col[1] + shadetop + shadedown, 0.0f, 1.0f); - outline[2] = clamp_f(col[2] + shadetop + shadedown, 0.0f, 1.0f); - outline[3] = clamp_f(col[3] + shadetop + shadedown, 0.0f, 1.0f); - - UI_draw_roundbox_4fv_ex(rect, inner1, inner2, 1.0f, outline, U.pixelsize, rad); -} - void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]) { const int ofs_y = 4 * U.pixelsize; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 0826157b5e6..28227c2331a 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1031,7 +1031,6 @@ enum { struct GPUBatch *ui_batch_roundbox_widget_get(void); struct GPUBatch *ui_batch_roundbox_shadow_get(void); -void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]); void ui_draw_menu_back(struct uiStyle *style, uiBlock *block, rcti *rect); void ui_draw_popover_back(struct ARegion *region, struct uiStyle *style, diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 61d66c44a39..acff14488a6 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -518,7 +518,7 @@ GPUBatch *ui_batch_roundbox_shadow_get(void) /** \name Draw Triangle Arrow * \{ */ -void UI_draw_anti_tria( +static void draw_anti_tria( float x1, float y1, float x2, float y2, float x3, float y3, const float color[4]) { const float tri_arr[3][2] = {{x1, y1}, {x2, y2}, {x3, y3}}; @@ -559,66 +559,31 @@ void UI_draw_icon_tri(float x, float y, char dir, const float color[4]) const float f7 = 0.25 * U.widget_unit; if (dir == 'h') { - UI_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color); + draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color); } else if (dir == 't') { - UI_draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3, color); + draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3, color); } else { /* 'v' = vertical, down. */ - UI_draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7, color); + draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7, color); } } /* triangle 'icon' inside rect */ -void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]) +static void draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]) { if (dir == 'h') { const float half = 0.5f * BLI_rctf_size_y(rect); - UI_draw_anti_tria( + draw_anti_tria( rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color); } else { const float half = 0.5f * BLI_rctf_size_x(rect); - UI_draw_anti_tria( + draw_anti_tria( rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color); } } -void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4]) -{ - float draw_color[4]; - - copy_v4_v4(draw_color, color); - draw_color[3] *= 2.0f / WIDGET_AA_JITTER; - - GPU_blend(GPU_BLEND_ALPHA); - - const uint pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor4fv(draw_color); - - /* for each AA step */ - for (int j = 0; j < WIDGET_AA_JITTER; j++) { - immBegin(GPU_PRIM_TRI_FAN, length); - immVertex2f(pos, tri_array[0][0], tri_array[0][1]); - immVertex2f(pos, tri_array[1][0], tri_array[1][1]); - - /* We jitter only the middle of the fan, the extremes are pinned. */ - for (int i = 2; i < length - 1; i++) { - immVertex2f(pos, tri_array[i][0] + jit[j][0], tri_array[i][1] + jit[j][1]); - } - - immVertex2f(pos, tri_array[length - 1][0], tri_array[length - 1][1]); - immEnd(); - } - - immUnbindProgram(); - - GPU_blend(GPU_BLEND_NONE); -} - static void widget_init(uiWidgetBase *wtb) { wtb->totvert = wtb->halfwayvert = 0; @@ -1494,7 +1459,7 @@ static void widget_draw_submenu_tria(const uiBut *but, GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); GPU_blend(GPU_BLEND_NONE); - ui_draw_anti_tria_rect(&tria_rect, 'h', col); + draw_anti_tria_rect(&tria_rect, 'h', col); } static void ui_text_clip_give_prev_off(uiBut *but, const char *str) From 8f1284ab788f219ffd52db5aedfae47b25bb5a35 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 25 Oct 2021 14:20:44 -0500 Subject: [PATCH 1163/1500] UI: Make the mesh to volume node slightly narrower Also use consistent UI names for properties that are similar between that node and the points to volume node (which happen to be shorter, allowing the node to be narrower). --- source/blender/makesrna/intern/rna_nodetree.c | 4 ++-- .../blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 8daf311fbfe..8ce9bc264ba 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10341,12 +10341,12 @@ static void def_geo_volume_to_mesh(StructRNA *srna) {VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT, "VOXEL_AMOUNT", 0, - "Voxel Amount", + "Amount", "Desired number of voxels along one axis"}, {VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE, "VOXEL_SIZE", 0, - "Voxel Size", + "Size", "Desired voxel side length"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 229a35e0007..d1fb22f4ba2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -163,7 +163,7 @@ void register_node_type_geo_volume_to_mesh() ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare; node_type_storage( &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); - node_type_size(&ntype, 200, 120, 700); + node_type_size(&ntype, 170, 120, 700); node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init); node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update); ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec; From 348b5f98f0b63bc8993bde7cf3b5bf5c24c609b2 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 24 Oct 2021 16:21:22 -0400 Subject: [PATCH 1164/1500] UI: Don't use colons at the end of property labels This was a 2.7x design, now with property split we omit the colon. --- release/scripts/presets/keyconfig/Blender.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index 1852e150589..35c920b3f40 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -56,7 +56,7 @@ class Prefs(bpy.types.KeyConfigPreferences): update=update_fn, ) tool_key_mode: EnumProperty( - name="Tool Keys:", + name="Tool Keys", description=( "The method of keys to activate tools such as move, rotate & scale (G, R, S)" ), @@ -242,13 +242,13 @@ class Prefs(bpy.types.KeyConfigPreferences): # General settings. col = layout.column() - col.row().prop(self, "select_mouse", text="Select with Mouse Button:", expand=True) - col.row().prop(self, "spacebar_action", text="Spacebar Action:", expand=True) + col.row().prop(self, "select_mouse", text="Select with Mouse Button", expand=True) + col.row().prop(self, "spacebar_action", text="Spacebar Action", expand=True) if is_select_left: - col.row().prop(self, "gizmo_action", text="Activate Gizmo Event:", expand=True) + col.row().prop(self, "gizmo_action", text="Activate Gizmo Event", expand=True) else: - col.row().prop(self, "rmb_action", text="Right Mouse Select Action:", expand=True) + col.row().prop(self, "rmb_action", text="Right Mouse Select Action", expand=True) col.row().prop(self, "tool_key_mode", expand=True) @@ -271,9 +271,9 @@ class Prefs(bpy.types.KeyConfigPreferences): # 3DView settings. col = layout.column() col.label(text="3D View") - col.row().prop(self, "v3d_tilde_action", text="Grave Accent / Tilde Action:", expand=True) - col.row().prop(self, "v3d_mmb_action", text="Middle Mouse Action:", expand=True) - col.row().prop(self, "v3d_alt_mmb_drag_action", text="Alt Middle Mouse Drag Action:", expand=True) + col.row().prop(self, "v3d_tilde_action", text="Grave Accent / Tilde Action", expand=True) + col.row().prop(self, "v3d_mmb_action", text="Middle Mouse Action", expand=True) + col.row().prop(self, "v3d_alt_mmb_drag_action", text="Alt Middle Mouse Drag Action", expand=True) # Checkboxes sub-layout. col = layout.column() From 4468c343787471df8a7a71ceb066301d2eaa6dc6 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Sun, 24 Oct 2021 22:15:30 +0100 Subject: [PATCH 1165/1500] Geometry Nodes: Optimise Voronoi texture node This patch improves performance by only assigning or calculating data for connected sockets. It is recommended that artists use the lowest dimensions setting for noise based textures. E.g. Use 2D instead of 3D where possible. Using a scoped timer and single thread on 256,000 points. Smooth F1 3D : Debug build Timer 'Optimised' took 9.39991 s Timer 'Normal' took 16.1531 s This optimisation is only for GN and not shaders. Differential Revision: https://developer.blender.org/D12985 --- source/blender/blenlib/intern/noise.cc | 204 ++++++-- .../shader/nodes/node_shader_tex_voronoi.cc | 483 +++++++++++++----- 2 files changed, 512 insertions(+), 175 deletions(-) diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index 5fa2746d07f..4259237af6e 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -1528,9 +1528,15 @@ void voronoi_f1( targetPosition = pointPosition; } } - *r_distance = minDistance; - *r_color = hash_float_to_float3(cellPosition + targetOffset); - *r_w = targetPosition + cellPosition; + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_w != nullptr) { + *r_w = targetPosition + cellPosition; + } } void voronoi_smooth_f1(const float w, @@ -1555,14 +1561,26 @@ void voronoi_smooth_f1(const float w, 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; - correctionFactor /= 1.0f + 3.0f * smoothness; - const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); - smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; - smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor; + if (r_color != nullptr || r_w != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_w != nullptr) { + smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor; + } + } + } + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_w != nullptr) { + *r_w = cellPosition + smoothPosition; } - *r_distance = smoothDistance; - *r_color = smoothColor; - *r_w = cellPosition + smoothPosition; } void voronoi_f2( @@ -1596,9 +1614,15 @@ void voronoi_f2( positionF2 = pointPosition; } } - *r_distance = distanceF2; - *r_color = hash_float_to_float3(cellPosition + offsetF2); - *r_w = positionF2 + cellPosition; + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_w != nullptr) { + *r_w = positionF2 + cellPosition; + } } void voronoi_distance_to_edge(const float w, const float randomness, float *r_distance) @@ -1706,9 +1730,15 @@ void voronoi_f1(const float2 coord, } } } - *r_distance = minDistance; - *r_color = hash_float_to_float3(cellPosition + targetOffset); - *r_position = targetPosition + cellPosition; + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_position != nullptr) { + *r_position = targetPosition + cellPosition; + } } void voronoi_smooth_f1(const float2 coord, @@ -1737,15 +1767,28 @@ void voronoi_smooth_f1(const float2 coord, 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; - correctionFactor /= 1.0f + 3.0f * smoothness; - const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); - smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; - smoothPosition = float2::interpolate(smoothPosition, pointPosition, h) - correctionFactor; + if (r_color != nullptr || r_position != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_position != nullptr) { + smoothPosition = float2::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } } } - *r_distance = smoothDistance; - *r_color = smoothColor; - *r_position = cellPosition + smoothPosition; + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_position != nullptr) { + *r_position = cellPosition + smoothPosition; + } } void voronoi_f2(const float2 coord, @@ -1787,9 +1830,15 @@ void voronoi_f2(const float2 coord, } } } - *r_distance = distanceF2; - *r_color = hash_float_to_float3(cellPosition + offsetF2); - *r_position = positionF2 + cellPosition; + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_position != nullptr) { + *r_position = positionF2 + cellPosition; + } } void voronoi_distance_to_edge(const float2 coord, const float randomness, float *r_distance) @@ -1928,9 +1977,15 @@ void voronoi_f1(const float3 coord, } } } - *r_distance = minDistance; - *r_color = hash_float_to_float3(cellPosition + targetOffset); - *r_position = targetPosition + cellPosition; + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_position != nullptr) { + *r_position = targetPosition + cellPosition; + } } void voronoi_smooth_f1(const float3 coord, @@ -1960,16 +2015,29 @@ void voronoi_smooth_f1(const float3 coord, 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; - correctionFactor /= 1.0f + 3.0f * smoothness; - const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); - smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; - smoothPosition = float3::interpolate(smoothPosition, pointPosition, h) - correctionFactor; + if (r_color != nullptr || r_position != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_position != nullptr) { + smoothPosition = float3::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } } } } - *r_distance = smoothDistance; - *r_color = smoothColor; - *r_position = cellPosition + smoothPosition; + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_position != nullptr) { + *r_position = cellPosition + smoothPosition; + } } void voronoi_f2(const float3 coord, @@ -2013,9 +2081,15 @@ void voronoi_f2(const float3 coord, } } } - *r_distance = distanceF2; - *r_color = hash_float_to_float3(cellPosition + offsetF2); - *r_position = positionF2 + cellPosition; + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_position != nullptr) { + *r_position = positionF2 + cellPosition; + } } void voronoi_distance_to_edge(const float3 coord, const float randomness, float *r_distance) @@ -2166,9 +2240,15 @@ void voronoi_f1(const float4 coord, } } } - *r_distance = minDistance; - *r_color = hash_float_to_float3(cellPosition + targetOffset); - *r_position = targetPosition + cellPosition; + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_position != nullptr) { + *r_position = targetPosition + cellPosition; + } } void voronoi_smooth_f1(const float4 coord, @@ -2200,18 +2280,30 @@ void voronoi_smooth_f1(const float4 coord, 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; - correctionFactor /= 1.0f + 3.0f * smoothness; - const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); - smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; - smoothPosition = float4::interpolate(smoothPosition, pointPosition, h) - - correctionFactor; + if (r_color != nullptr || r_position != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_position != nullptr) { + smoothPosition = float4::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } } } } } - *r_distance = smoothDistance; - *r_color = smoothColor; - *r_position = cellPosition + smoothPosition; + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_position != nullptr) { + *r_position = cellPosition + smoothPosition; + } } void voronoi_f2(const float4 coord, @@ -2258,9 +2350,15 @@ void voronoi_f2(const float4 coord, } } } - *r_distance = distanceF2; - *r_color = hash_float_to_float3(cellPosition + offsetF2); - *r_position = positionF2 + cellPosition; + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_position != nullptr) { + *r_position = positionF2 + cellPosition; + } } void voronoi_distance_to_edge(const float4 coord, const float randomness, float *r_distance) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index 574260f3c36..9fcc331e486 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -239,16 +239,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { return params.readonly_single_input(param_index, "Randomness"); }; auto get_r_distance = [&](int param_index) -> MutableSpan { - return params.uninitialized_single_output(param_index, "Distance"); + return params.uninitialized_single_output_if_required(param_index, "Distance"); }; auto get_r_color = [&](int param_index) -> MutableSpan { - return params.uninitialized_single_output(param_index, "Color"); + return params.uninitialized_single_output_if_required(param_index, "Color"); }; auto get_r_position = [&](int param_index) -> MutableSpan { - return params.uninitialized_single_output(param_index, "Position"); + return params.uninitialized_single_output_if_required(param_index, "Position"); }; auto get_r_w = [&](int param_index) -> MutableSpan { - return params.uninitialized_single_output(param_index, "W"); + return params.uninitialized_single_output_if_required(param_index, "W"); }; int param = 0; @@ -263,6 +263,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -271,12 +274,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -288,6 +295,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -296,12 +306,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -314,6 +328,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); @@ -324,12 +341,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -346,6 +367,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -353,11 +377,15 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } } break; } @@ -369,6 +397,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -376,11 +407,15 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } } break; } @@ -393,6 +428,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); @@ -402,11 +440,15 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } } break; } @@ -425,16 +467,34 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_f1(p, exponent[i], rand, SHD_VORONOI_F1, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_f1(p, + exponent[i], + rand, + SHD_VORONOI_F1, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } @@ -448,17 +508,34 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_f2( - p, exponent[i], rand, SHD_VORONOI_MINKOWSKI, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_f2(p, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } @@ -473,18 +550,36 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_smooth_f1( - p, smth, exponent[i], rand, SHD_VORONOI_MINKOWSKI, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_smooth_f1(p, + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } @@ -572,16 +667,16 @@ class VoronoiMetricFunction : public fn::MultiFunction { return params.readonly_single_input(param_index, "Randomness"); }; auto get_r_distance = [&](int param_index) -> MutableSpan { - return params.uninitialized_single_output(param_index, "Distance"); + return params.uninitialized_single_output_if_required(param_index, "Distance"); }; auto get_r_color = [&](int param_index) -> MutableSpan { - return params.uninitialized_single_output(param_index, "Color"); + return params.uninitialized_single_output_if_required(param_index, "Color"); }; auto get_r_position = [&](int param_index) -> MutableSpan { - return params.uninitialized_single_output(param_index, "Position"); + return params.uninitialized_single_output_if_required(param_index, "Position"); }; auto get_r_w = [&](int param_index) -> MutableSpan { - return params.uninitialized_single_output(param_index, "W"); + return params.uninitialized_single_output_if_required(param_index, "W"); }; int param = 0; @@ -595,13 +690,24 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float p = w[i] * scale[i]; const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; - noise::voronoi_f1(p, rand, &r_distance[i], &col, &r_w[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_w[i] = safe_divide(r_w[i], scale[i]); + noise::voronoi_f1(p, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } } break; } @@ -612,13 +718,24 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float p = w[i] * scale[i]; const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; - noise::voronoi_f2(p, rand, &r_distance[i], &col, &r_w[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_w[i] = safe_divide(r_w[i], scale[i]); + noise::voronoi_f2(p, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } } break; } @@ -630,14 +747,26 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float p = w[i] * scale[i]; const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; - noise::voronoi_smooth_f1(p, smth, rand, &r_distance[i], &col, &r_w[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_w[i] = safe_divide(r_w[i], scale[i]); + noise::voronoi_smooth_f1(p, + smth, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } } break; } @@ -653,6 +782,9 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -661,12 +793,16 @@ class VoronoiMetricFunction : public fn::MultiFunction { 0.0f, rand, metric_, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -677,6 +813,9 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -685,12 +824,16 @@ class VoronoiMetricFunction : public fn::MultiFunction { 0.0f, rand, metric_, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -702,6 +845,9 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); @@ -712,12 +858,16 @@ class VoronoiMetricFunction : public fn::MultiFunction { 0.0f, rand, metric_, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -733,13 +883,25 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; - noise::voronoi_f1( - vector[i] * scale[i], 0.0f, rand, metric_, &r_distance[i], &col, &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + noise::voronoi_f1(vector[i] * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } } break; } @@ -750,13 +912,25 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; - noise::voronoi_f2( - vector[i] * scale[i], 0.0f, rand, metric_, &r_distance[i], &col, &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + noise::voronoi_f2(vector[i] * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } } break; } @@ -768,21 +942,31 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_distance = get_r_distance(param++); MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); - for (int64_t i : mask) { - const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); - const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); - float3 col; - noise::voronoi_smooth_f1(vector[i] * scale[i], - smth, - 0.0f, - rand, - metric_, - &r_distance[i], - &col, - &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + { + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(vector[i] * scale[i], + smth, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + } } + break; } } @@ -799,16 +983,34 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_f1(p, 0.0f, rand, metric_, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_f1(p, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } @@ -821,16 +1023,34 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_f2(p, 0.0f, rand, metric_, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_f2(p, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } @@ -844,17 +1064,36 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan r_color = get_r_color(param++); MutableSpan r_position = get_r_position(param++); MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_smooth_f1(p, smth, 0.0f, rand, metric_, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_smooth_f1(p, + smth, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } From 40f59b5dad15eab01ddd326fbffd59846f398c34 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 25 Oct 2021 19:39:56 -0300 Subject: [PATCH 1166/1500] View3D Snap Cursor: sanitize and increase the maximum amount of states 3 is a small amount as each viewport creates a gizmo that creates its own state Now if the state is not created, the gizmos use the last state. --- .../gizmo_library/gizmo_types/snap3d_gizmo.c | 10 ++-- .../editors/space_view3d/space_view3d.c | 51 +++++++++---------- .../editors/space_view3d/view3d_cursor_snap.c | 10 +++- .../editors/space_view3d/view3d_placement.c | 21 +++++--- 4 files changed, 53 insertions(+), 39 deletions(-) diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index 33532bd0549..93ee6ec2d81 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -225,8 +225,10 @@ static void snap_gizmo_setup(wmGizmo *gz) gz->flag |= WM_GIZMO_NO_TOOLTIP; SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; snap_gizmo->snap_state = ED_view3d_cursor_snap_active(); - snap_gizmo->snap_state->draw_point = true; - snap_gizmo->snap_state->draw_plane = false; + if (snap_gizmo->snap_state) { + snap_gizmo->snap_state->draw_point = true; + snap_gizmo->snap_state->draw_plane = false; + } rgba_float_to_uchar(snap_gizmo->snap_state->color_point, gz->color); } @@ -284,7 +286,9 @@ static int snap_gizmo_invoke(bContext *UNUSED(C), static void snap_gizmo_free(wmGizmo *gz) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - ED_view3d_cursor_snap_deactive(snap_gizmo->snap_state); + if (snap_gizmo->snap_state) { + ED_view3d_cursor_snap_deactive(snap_gizmo->snap_state); + } } static void GIZMO_GT_snap_3d(wmGizmoType *gzt) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index eaf90aabe8c..d20a07d3517 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -522,9 +522,12 @@ static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag) if (state) { return; } + state = drop->draw_data = ED_view3d_cursor_snap_active(); - state->draw_point = true; - state->draw_plane = true; + if (!state) { + /* The maximum snap status stack value has been reached. */ + return; + } float dimensions[3] = {0.0f}; if (drag->type == WM_DRAG_ID) { @@ -549,10 +552,8 @@ static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag) static void view3d_ob_drop_draw_deactivate(struct wmDropBox *drop, wmDrag *UNUSED(drag)) { V3DSnapCursorState *state = drop->draw_data; - if (state) { - ED_view3d_cursor_snap_deactive(state); - drop->draw_data = NULL; - } + ED_view3d_cursor_snap_deactive(state); + drop->draw_data = NULL; } static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) @@ -680,30 +681,28 @@ static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) const bool is_imported_id = drag->type == WM_DRAG_ASSET; RNA_boolean_set(drop->ptr, "duplicate", !is_imported_id); - V3DSnapCursorState *snap_state = drop->draw_data; - if (snap_state) { - Object *ob = (Object *)id; - float obmat_final[4][4]; + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + Object *ob = (Object *)id; + float obmat_final[4][4]; - V3DSnapCursorData *snap_data; - snap_data = ED_view3d_cursor_snap_data_get(snap_state, NULL, 0, 0); - copy_m4_m3(obmat_final, snap_data->plane_omat); - copy_v3_v3(obmat_final[3], snap_data->loc); + V3DSnapCursorData *snap_data; + snap_data = ED_view3d_cursor_snap_data_get(snap_state, NULL, 0, 0); + copy_m4_m3(obmat_final, snap_data->plane_omat); + copy_v3_v3(obmat_final[3], snap_data->loc); - float scale[3]; - mat4_to_size(scale, ob->obmat); - rescale_m4(obmat_final, scale); + float scale[3]; + mat4_to_size(scale, ob->obmat); + rescale_m4(obmat_final, scale); - BoundBox *bb = BKE_object_boundbox_get(ob); - if (bb) { - float offset[3]; - BKE_boundbox_calc_center_aabb(bb, offset); - offset[2] = bb->vec[0][2]; - mul_mat3_m4_v3(obmat_final, offset); - sub_v3_v3(obmat_final[3], offset); - } - RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); + BoundBox *bb = BKE_object_boundbox_get(ob); + if (bb) { + float offset[3]; + BKE_boundbox_calc_center_aabb(bb, offset); + offset[2] = bb->vec[0][2]; + mul_mat3_m4_v3(obmat_final, offset); + sub_v3_v3(obmat_final[3], offset); } + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); } static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 5eb9ec3625c..9c45a89c3ff 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -54,7 +54,7 @@ #include "WM_api.h" -#define STATE_LEN 3 +#define STATE_LEN 8 typedef struct SnapStateIntern { V3DSnapCursorState snap_state; @@ -925,7 +925,6 @@ V3DSnapCursorState *ED_view3d_cursor_snap_active(void) } } - BLI_assert(false); data_intern->state_active_len--; return NULL; } @@ -938,6 +937,10 @@ void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state) return; } + if (!state) { + return; + } + SnapStateIntern *state_intern = (SnapStateIntern *)state; if (!state_intern->is_active) { return; @@ -956,6 +959,9 @@ void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state) void ED_view3d_cursor_snap_prevpoint_set(V3DSnapCursorState *state, const float prev_point[3]) { SnapCursorDataIntern *data_intern = &g_data_intern; + if (!state) { + state = ED_view3d_cursor_snap_state_get(); + } if (prev_point) { copy_v3_v3(data_intern->prevpoint_stack, prev_point); state->prevpoint = data_intern->prevpoint_stack; diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 7fe97705765..572fc8e3156 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -742,16 +742,19 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv ipd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); - ipd->snap_state = ED_view3d_cursor_snap_active(); - ipd->snap_state->draw_point = true; - ipd->snap_state->draw_plane = true; + V3DSnapCursorState *snap_state_new = ED_view3d_cursor_snap_active(); + if (snap_state_new) { + ipd->snap_state = snap_state = snap_state_new; + } + snap_state->draw_point = true; + snap_state->draw_plane = true; ipd->is_snap_found = view3d_interactive_add_calc_snap( C, event, ipd->co_src, ipd->matrix_orient, &ipd->use_snap, &ipd->is_snap_invert) != 0; - ipd->snap_state->draw_plane = false; - ED_view3d_cursor_snap_prevpoint_set(ipd->snap_state, ipd->co_src); + snap_state->draw_plane = false; + ED_view3d_cursor_snap_prevpoint_set(snap_state, ipd->co_src); ipd->orient_axis = plane_axis; for (int i = 0; i < 2; i++) { @@ -1515,10 +1518,12 @@ static void preview_plane_free_fn(void *customdata) static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) { V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_active(); - snap_state->draw_plane = true; + if (snap_state) { + snap_state->draw_plane = true; - gzgroup->customdata = snap_state; - gzgroup->customdata_free = preview_plane_free_fn; + gzgroup->customdata = snap_state; + gzgroup->customdata_free = preview_plane_free_fn; + } } void VIEW3D_GGT_placement(wmGizmoGroupType *gzgt) From 32cc9ff037465e5522b1f6ee7139f6665975a106 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 25 Oct 2021 20:16:28 -0300 Subject: [PATCH 1167/1500] View3D Snap Cursor: don't limit the number of states The benefit of a flat array in this case is small and limiting, so use a linklist. --- .../editors/space_view3d/space_view3d.c | 4 -- .../editors/space_view3d/view3d_cursor_snap.c | 64 ++++++------------- 2 files changed, 18 insertions(+), 50 deletions(-) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index d20a07d3517..ccad1b8b552 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -524,10 +524,6 @@ static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag) } state = drop->draw_data = ED_view3d_cursor_snap_active(); - if (!state) { - /* The maximum snap status stack value has been reached. */ - return; - } float dimensions[3] = {0.0f}; if (drag->type == WM_DRAG_ID) { diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 9c45a89c3ff..5db9dd5d0a7 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -54,22 +54,19 @@ #include "WM_api.h" -#define STATE_LEN 8 +#define STATE_INTERN_GET(state) \ + (SnapStateIntern *)((char *)state - offsetof(SnapStateIntern, snap_state)) typedef struct SnapStateIntern { + struct SnapStateIntern *next, *prev; V3DSnapCursorState snap_state; - int state_active_prev; - bool is_active; } SnapStateIntern; typedef struct SnapCursorDataIntern { V3DSnapCursorState state_default; - SnapStateIntern state_intern[STATE_LEN]; + ListBase state_intern; V3DSnapCursorData snap_data; - int state_active_len; - int state_active; - struct SnapObjectContext *snap_context_v3d; const Scene *scene; short snap_elem_hidden; @@ -852,10 +849,11 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust V3DSnapCursorState *ED_view3d_cursor_snap_state_get(void) { - if (!g_data_intern.state_active_len) { + SnapCursorDataIntern *data_intern = &g_data_intern; + if (BLI_listbase_is_empty(&data_intern->state_intern)) { return &g_data_intern.state_default; } - return (V3DSnapCursorState *)&g_data_intern.state_intern[g_data_intern.state_active]; + return &((SnapStateIntern *)data_intern->state_intern.last)->snap_state; } static void v3d_cursor_snap_activate(void) @@ -894,11 +892,7 @@ static void v3d_cursor_snap_free(void) data_intern->snap_context_v3d = NULL; } - for (SnapStateIntern *state_intern = data_intern->state_intern; - state_intern < &data_intern->state_intern[STATE_LEN]; - state_intern++) { - state_intern->is_active = false; - } + BLI_freelistN(&data_intern->state_intern); } void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state) @@ -909,51 +903,29 @@ void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state) V3DSnapCursorState *ED_view3d_cursor_snap_active(void) { SnapCursorDataIntern *data_intern = &g_data_intern; - if (!data_intern->state_active_len) { + if (!data_intern->handle) { v3d_cursor_snap_activate(); } - data_intern->state_active_len++; - for (int i = 0; i < STATE_LEN; i++) { - SnapStateIntern *state_intern = &g_data_intern.state_intern[i]; - if (!state_intern->is_active) { - state_intern->snap_state = g_data_intern.state_default; - state_intern->is_active = true; - state_intern->state_active_prev = data_intern->state_active; - data_intern->state_active = i; - return (V3DSnapCursorState *)state_intern; - } - } + SnapStateIntern *state_intern = MEM_mallocN(sizeof(*state_intern), __func__); + state_intern->snap_state = g_data_intern.state_default; + BLI_addtail(&g_data_intern.state_intern, state_intern); - data_intern->state_active_len--; - return NULL; + return (V3DSnapCursorState *)&state_intern->snap_state; } void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state) { SnapCursorDataIntern *data_intern = &g_data_intern; - if (!data_intern->state_active_len) { - BLI_assert(false); + if (BLI_listbase_is_empty(&data_intern->state_intern)) { return; } - if (!state) { - return; - } - - SnapStateIntern *state_intern = (SnapStateIntern *)state; - if (!state_intern->is_active) { - return; - } - - state_intern->is_active = false; - data_intern->state_active_len--; - if (!data_intern->state_active_len) { + SnapStateIntern *state_intern = STATE_INTERN_GET(state); + BLI_remlink(&data_intern->state_intern, state_intern); + if (BLI_listbase_is_empty(&data_intern->state_intern)) { v3d_cursor_snap_free(); } - else { - data_intern->state_active = state_intern->state_active_prev; - } } void ED_view3d_cursor_snap_prevpoint_set(V3DSnapCursorState *state, const float prev_point[3]) @@ -977,7 +949,7 @@ V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state, const int y) { SnapCursorDataIntern *data_intern = &g_data_intern; - if (C && data_intern->state_active_len) { + if (C) { wmWindowManager *wm = CTX_wm_manager(C); if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); From 4e22a9ab9ed2391788ec3521306c64ddf6c3a48b Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 25 Oct 2021 22:18:45 -0400 Subject: [PATCH 1168/1500] Cleanup: Clang format --- .../editors/interface/interface_handlers.c | 3 +- .../blender/python/generic/bl_math_py_api.c | 30 +++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 52ab13e5cd0..44420ee926e 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -10648,7 +10648,8 @@ static int ui_handle_menu_event(bContext *C, menu->menuretval = UI_RETURN_OUT; } } - else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, (float)event->xy[0], (float)event->xy[1])) { + else if (saferct && !BLI_rctf_isect_pt( + &saferct->parent, (float)event->xy[0], (float)event->xy[1])) { if (block->flag & UI_BLOCK_OUT_1) { menu->menuretval = UI_RETURN_OK; } diff --git a/source/blender/python/generic/bl_math_py_api.c b/source/blender/python/generic/bl_math_py_api.c index 5e938db0c35..2211cc931da 100644 --- a/source/blender/python/generic/bl_math_py_api.c +++ b/source/blender/python/generic/bl_math_py_api.c @@ -99,21 +99,21 @@ static PyObject *py_bl_math_lerp(PyObject *UNUSED(self), PyObject *args) return PyFloat_FromDouble(a * (1.0 - x) + b * x); } -PyDoc_STRVAR( - py_bl_math_smoothstep_doc, - ".. function:: smoothstep(from_value, to_value, value)\n" - "\n" - " Performs smooth interpolation between 0 and 1 as value changes between from and to values.\n" - " Outside the range the function returns the same value as the nearest edge.\n" - "\n" - " :arg from_value: The edge value where the result is 0.\n" - " :type from_value: float\n" - " :arg to_value: The edge value where the result is 1.\n" - " :type to_value: float\n" - " :arg factor: The interpolation value.\n" - " :type factor: float\n" - " :return: The interpolated value in [0.0, 1.0].\n" - " :rtype: float\n"); +PyDoc_STRVAR(py_bl_math_smoothstep_doc, + ".. function:: smoothstep(from_value, to_value, value)\n" + "\n" + " Performs smooth interpolation between 0 and 1 as value changes between from and " + "to values.\n" + " Outside the range the function returns the same value as the nearest edge.\n" + "\n" + " :arg from_value: The edge value where the result is 0.\n" + " :type from_value: float\n" + " :arg to_value: The edge value where the result is 1.\n" + " :type to_value: float\n" + " :arg factor: The interpolation value.\n" + " :type factor: float\n" + " :return: The interpolated value in [0.0, 1.0].\n" + " :rtype: float\n"); static PyObject *py_bl_math_smoothstep(PyObject *UNUSED(self), PyObject *args) { double a, b, x; From 6f5bf8aa3b65521fca3f9a442d5636037ea1730d Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 25 Oct 2021 22:19:04 -0400 Subject: [PATCH 1169/1500] Sequencer: Expose preview transform operators in menu The also moves all the image operators into one menu. The goal here is to expose the operators in the UI so they work with the operator search and to make the UI consistent. Reviewed By: ISS Differential Revision: https://developer.blender.org/D12808 --- .../scripts/startup/bl_ui/space_sequencer.py | 75 ++++++++++++++----- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 9b96cef9de4..cdfe4f4068f 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -238,6 +238,8 @@ class SEQUENCER_MT_editor_menus(Menu): layout.menu("SEQUENCER_MT_add") layout.menu("SEQUENCER_MT_strip") + layout.menu("SEQUENCER_MT_image") + class SEQUENCER_PT_gizmo_display(Panel): bl_space_type = 'SEQUENCE_EDITOR' @@ -787,23 +789,6 @@ class SEQUENCER_MT_add_effect(Menu): col.enabled = selected_sequences_len(context) != 0 -class SEQUENCER_MT_strip_image_transform(Menu): - bl_label = "Image Transform" - - def draw(self, _context): - layout = self.layout - - layout.operator("sequencer.strip_transform_fit", text="Scale To Fit").fit_method = 'FIT' - layout.operator("sequencer.strip_transform_fit", text="Scale to Fill").fit_method = 'FILL' - layout.operator("sequencer.strip_transform_fit", text="Stretch To Fill").fit_method = 'STRETCH' - layout.separator() - - layout.operator("sequencer.strip_transform_clear", text="Clear Position").property = 'POSITION' - layout.operator("sequencer.strip_transform_clear", text="Clear Scale").property = 'SCALE' - layout.operator("sequencer.strip_transform_clear", text="Clear Rotation").property = 'ROTATION' - layout.operator("sequencer.strip_transform_clear", text="Clear All").property = 'ALL' - - class SEQUENCER_MT_strip_transform(Menu): bl_label = "Transform" @@ -898,7 +883,6 @@ class SEQUENCER_MT_strip(Menu): layout.separator() layout.menu("SEQUENCER_MT_strip_transform") - layout.menu("SEQUENCER_MT_strip_image_transform") layout.separator() layout.operator("sequencer.split", text="Split").type = 'SOFT' @@ -958,6 +942,56 @@ class SEQUENCER_MT_strip(Menu): layout.menu("SEQUENCER_MT_strip_input") +class SEQUENCER_MT_image(Menu): + bl_label = "Image" + + def draw(self, context): + layout = self.layout + st = context.space_data + + if st.view_type == {'PREVIEW', 'SEQUENCER_PREVIEW'}: + layout.menu("SEQUENCER_MT_image_transform") + + layout.menu("SEQUENCER_MT_image_clear") + layout.menu("SEQUENCER_MT_image_apply") + + +class SEQUENCER_MT_image_transform(Menu): + bl_label = "Transfrom" + + def draw(self, _context): + layout = self.layout + + layout.operator_context = 'INVOKE_REGION_PREVIEW' + + layout.operator("transform.translate") + layout.operator("transform.rotate") + layout.operator("transform.resize", text="Scale") + + +class SEQUENCER_MT_image_clear(Menu): + bl_label = "Clear" + + def draw(self, _context): + layout = self.layout + + layout.operator("sequencer.strip_transform_clear", text="Position").property = 'POSITION' + layout.operator("sequencer.strip_transform_clear", text="Scale").property = 'SCALE' + layout.operator("sequencer.strip_transform_clear", text="Rotation").property = 'ROTATION' + layout.operator("sequencer.strip_transform_clear", text="All Transforms").property = 'ALL' + + +class SEQUENCER_MT_image_apply(Menu): + bl_label = "Apply" + + def draw(self, _context): + layout = self.layout + + layout.operator("sequencer.strip_transform_fit", text="Scale To Fit").fit_method = 'FIT' + layout.operator("sequencer.strip_transform_fit", text="Scale to Fill").fit_method = 'FILL' + layout.operator("sequencer.strip_transform_fit", text="Stretch To Fill").fit_method = 'STRETCH' + + class SEQUENCER_MT_context_menu(Menu): bl_label = "Sequencer Context Menu" @@ -2523,10 +2557,13 @@ classes = ( SEQUENCER_MT_strip_effect, SEQUENCER_MT_strip_movie, SEQUENCER_MT_strip, - SEQUENCER_MT_strip_image_transform, SEQUENCER_MT_strip_transform, SEQUENCER_MT_strip_input, SEQUENCER_MT_strip_lock_mute, + SEQUENCER_MT_image, + SEQUENCER_MT_image_transform, + SEQUENCER_MT_image_clear, + SEQUENCER_MT_image_apply, SEQUENCER_MT_color_tag_picker, SEQUENCER_MT_context_menu, SEQUENCER_MT_preview_context_menu, From e463d2c16f72e976ed1c886aca3f8efe396978fb Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 25 Oct 2021 21:46:39 -0500 Subject: [PATCH 1170/1500] UI: Change node editor grid into a dot grid This patch makes the background grid of the node editor a grid of dots instead of lines. This makes the background look a bit more subtle and reduces visual complexity. The dots are meant to provide a reference when panning and zooming. Based on the design of @pablovazquez, and a patch originally authored by @fabian_schempp. The "Grid Levels" controls how many levels of dots are drawn. As the editor zooms in, the higher levels of dots fade in, making them closer together visually. The zoom factor at which each grid starts and ends fading in is controllable in the code, and could be tweaked further in the future. The new default value is 7, out of a range from 0 to 9. Differential Revision: https://developer.blender.org/D10345 --- .../datafiles/userdef/userdef_default_theme.c | 4 +- .../presets/interface_theme/Blender_Light.xml | 4 +- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_userdef.c | 5 + source/blender/editors/include/ED_node.h | 2 +- source/blender/editors/include/UI_view2d.h | 4 + source/blender/editors/interface/view2d.c | 109 ++++++++++++++++++ .../blender/editors/space_node/node_draw.cc | 10 +- source/blender/makesrna/intern/rna_userdef.c | 6 +- 9 files changed, 130 insertions(+), 16 deletions(-) diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index ad93f87e962..867a4c8f56b 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -817,7 +817,7 @@ const bTheme U_theme_default = { .sub_back = RGBA(0x0000003e), }, .shade2 = RGBA(0x7f707064), - .grid = RGBA(0x23232300), + .grid = RGBA(0x3d3d3d00), .wire = RGBA(0x232323ff), .select = RGBA(0xed5700ff), .active = RGBA(0xffffffff), @@ -827,7 +827,7 @@ const bTheme U_theme_default = { .outline_width = 1, .facedot_size = 4, .noodle_curving = 4, - .grid_levels = 2, + .grid_levels = 7, .dash_alpha = 0.5f, .syntaxl = RGBA(0x565656ff), .syntaxs = RGBA(0x975b5bff), diff --git a/release/scripts/presets/interface_theme/Blender_Light.xml b/release/scripts/presets/interface_theme/Blender_Light.xml index 8074371c450..f4188e83e34 100644 --- a/release/scripts/presets/interface_theme/Blender_Light.xml +++ b/release/scripts/presets/interface_theme/Blender_Light.xml @@ -939,7 +939,7 @@ space_node.dash_alpha = 0.5f; } + if (!USER_VERSION_ATLEAST(300, 39)) { + FROM_DEFAULT_V4_UCHAR(space_node.grid); + btheme->space_node.grid_levels = 7; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 1d51a3e77cf..1ba52552902 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -49,7 +49,7 @@ typedef enum { NODE_RIGHT = 8, } NodeBorder; -#define NODE_GRID_STEPS 5 +#define NODE_GRID_STEP_SIZE 10 #define NODE_EDGE_PAN_INSIDE_PAD 2 #define NODE_EDGE_PAN_OUTSIDE_PAD 0 /* Disable clamping for node panning, use whole screen. */ #define NODE_EDGE_PAN_SPEED_RAMP 1 diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 13895879f01..e959f9ae7ce 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -147,6 +147,10 @@ void UI_view2d_view_restore(const struct bContext *C); /* grid drawing */ void UI_view2d_multi_grid_draw( const struct View2D *v2d, int colorid, float step, int level_size, int totlevels); +void UI_view2d_dot_grid_draw(const struct View2D *v2d, + int grid_color_id, + float step, + int grid_levels); void UI_view2d_draw_lines_y__values(const struct View2D *v2d); void UI_view2d_draw_lines_x__values(const struct View2D *v2d); diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index ca96fde9810..30483f11c45 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -32,6 +32,7 @@ #include "DNA_userdef_types.h" #include "BLI_array.h" +#include "BLI_easing.h" #include "BLI_link_utils.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -1291,6 +1292,114 @@ void UI_view2d_multi_grid_draw( immUnbindProgram(); } +static void grid_axis_start_and_count( + const float step, const float min, const float max, float *r_start, int *r_count) +{ + *r_start = min; + if (*r_start < 0.0f) { + *r_start += -(float)fmod(min, step); + } + else { + *r_start += step - (float)fabs(fmod(min, step)); + } + + if (*r_start > max) { + *r_count = 0; + } + else { + *r_count = (max - *r_start) / step + 1; + } +} + +typedef struct DotGridLevelInfo { + /* The factor applied to the #min_step argument. This could be easily computed in runtime, + * but seeing it together with the other values is helpful. */ + float step_factor; + /* The normalized zoom level at which the grid level starts to fade in. + * At lower zoom levels, the points will not be visible and the level will be skipped. */ + float fade_in_start_zoom; + /* The normalized zoom level at which the grid finishes fading in. + * At higher zoom levels, the points will be opaque. */ + float fade_in_end_zoom; +} DotGridLevelInfo; + +static const DotGridLevelInfo level_info[9] = { + {128.0f, -0.1f, 0.01f}, + {64.0f, 0.0f, 0.025f}, + {32.0f, 0.025f, 0.15f}, + {16.0f, 0.05f, 0.2f}, + {8.0f, 0.1f, 0.25f}, + {4.0f, 0.125f, 0.3f}, + {2.0f, 0.25f, 0.5f}, + {1.0f, 0.7f, 0.9f}, + {0.5f, 0.6f, 0.9f}, +}; + +/** + * Draw a multi-level grid of dots, with a dynamic number of levels based on the fading. + * + * \param grid_color_id: The theme color used for the points. Faded dynamically based on zoom. + * \param min_step: The base size of the grid. At different zoom levels, the visible grid may have + * a larger step size. + * \param grid_levels: The maximum grid depth. Larger grid levels will subdivide the grid more. + */ +void UI_view2d_dot_grid_draw(const View2D *v2d, + const int grid_color_id, + const float min_step, + const int grid_levels) +{ + BLI_assert(grid_levels > 0 && grid_levels < 10); + const float zoom_x = (float)(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur); + const float zoom_normalized = (zoom_x - v2d->minzoom) / (v2d->maxzoom - v2d->minzoom); + + GPUVertFormat *format = immVertexFormat(); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + GPU_point_size(3.0f * UI_DPI_FAC); + + float color[4]; + UI_GetThemeColor3fv(grid_color_id, color); + + for (int level = 0; level < grid_levels; level++) { + const DotGridLevelInfo *info = &level_info[level]; + const float step = min_step * info->step_factor; + + const float alpha_factor = (zoom_normalized - info->fade_in_start_zoom) / + (info->fade_in_end_zoom - info->fade_in_start_zoom); + color[3] = clamp_f(BLI_easing_cubic_ease_in_out(alpha_factor, 0.0f, 1.0f, 1.0f), 0.0f, 1.0f); + if (color[3] == 0.0f) { + break; + } + + int count_x; + float start_x; + grid_axis_start_and_count(step, v2d->cur.xmin, v2d->cur.xmax, &start_x, &count_x); + int count_y; + float start_y; + grid_axis_start_and_count(step, v2d->cur.ymin, v2d->cur.ymax, &start_y, &count_y); + if (count_x == 0 || count_y == 0) { + continue; + } + + immBegin(GPU_PRIM_POINTS, count_x * count_y); + + /* Theoretically drawing on top of lower grid levels could be avoided, but it would also + * increase the complexity of this loop, which isn't worth the time at the moment. */ + for (int i_y = 0; i_y < count_y; i_y++) { + const float y = start_y + step * i_y; + for (int i_x = 0; i_x < count_x; i_x++) { + const float x = start_x + step * i_x; + immAttr4fv(color_id, color); + immVertex2f(pos, x, y); + } + } + + immEnd(); + } + + immUnbindProgram(); +} /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 97655080192..8029eb5e0fa 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2227,8 +2227,6 @@ void node_draw_space(const bContext *C, ARegion *region) snode->runtime->cursor[0] /= UI_DPI_FAC; snode->runtime->cursor[1] /= UI_DPI_FAC; - int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE); - ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW); /* Only set once. */ @@ -2259,14 +2257,14 @@ void node_draw_space(const bContext *C, ARegion *region) copy_v2_v2(snode->edittree->view_center, center); } + const int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE); + UI_view2d_dot_grid_draw(v2d, TH_GRID, NODE_GRID_STEP_SIZE, grid_levels); + /* Top-level edit tree. */ bNodeTree *ntree = path->nodetree; if (ntree) { snode_setup_v2d(snode, region, center); - /* Grid. */ - UI_view2d_multi_grid_draw(v2d, TH_GRID, ED_node_grid_size(), NODE_GRID_STEPS, grid_levels); - /* Backdrop. */ draw_nodespace_back_pix(C, region, snode, path->parent_key); @@ -2305,8 +2303,6 @@ void node_draw_space(const bContext *C, ARegion *region) } } else { - /* Default grid. */ - UI_view2d_multi_grid_draw(v2d, TH_GRID, ED_node_grid_size(), NODE_GRID_STEPS, grid_levels); /* Backdrop. */ draw_nodespace_back_pix(C, region, snode, NODE_INSTANCE_KEY_NONE); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 37d2b711b7d..d6a46cbcc82 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2911,10 +2911,10 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna) prop = RNA_def_property(srna, "grid_levels", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "grid_levels"); - RNA_def_property_int_default(prop, 2); - RNA_def_property_range(prop, 0, 2); + RNA_def_property_int_default(prop, 7); + RNA_def_property_range(prop, 0, 9); RNA_def_property_ui_text( - prop, "Grid Levels", "Amount of grid lines displayed in the background"); + prop, "Grid Levels", "Number of subdivisions for the dot grid displayed in the background"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "dash_alpha", PROP_FLOAT, PROP_FACTOR); From 3434a991ec5befef19c5484bfc70a3a333e42c34 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 26 Oct 2021 13:33:02 +0900 Subject: [PATCH 1171/1500] XR Controller Support Step 5: Navigation Adds navigation transforms (pose, scale) to the XR session state that will be applied to the viewer/controller poses. By manipulating these values, a viewer can move through the VR viewport without the need to physically walk through it. Add-ons can access these transforms via Python (XrSessionState.navigation_location/rotation/scale) to use with custom operators. Also adds 3 new VR navigation operators that will be exposed to users as default actions in the VR Scene Inspection add-on. While all three of these operators have custom properties that can greatly influence their behaviors, for now these properties will not be accessible by users from the UI. However, other add-ons can still set these custom properties if they desire. 1). Raycast-based teleport Moves the user to a location pointed at on a mesh object. The result can optionally be constrained to specific axes, for example to achieve "elevation snapping" behavior by constraining to the Z-axis. In addition, one can specify an interpolation factor and offset. Credit to KISKA for the elevation snapping concept. 2). "Grab" navigation Moves the user through the viewport by pressing inputs on one or two held controllers and applying deltas to the navigation matrix based on the displacement of these controllers. When inputs on both controllers are pressed at the same time (bimanual interaction), the user can scale themselves relative to the scene based on the distance between the controllers. Also supports locks for location, rotation, and scale. 3). Fly navigation Navigates the viewport by pressing a button and moving/turning relative to navigation space or the VR viewer or controller. Via the operator's properties, one can select from a variety of these modes as well as specify the min/max speed and whether to lock elevation. Reviewed By: Severin Differential Revision: https://developer.blender.org/D11501 --- .../ED_transform_snap_object_context.h | 1 + .../editors/transform/transform_snap_object.c | 5 + source/blender/makesdna/DNA_xr_types.h | 4 +- source/blender/makesrna/intern/rna_xr.c | 98 ++ source/blender/windowmanager/CMakeLists.txt | 1 + source/blender/windowmanager/WM_api.h | 7 + .../windowmanager/intern/wm_operators.c | 88 +- .../windowmanager/xr/intern/wm_xr_draw.c | 47 +- .../windowmanager/xr/intern/wm_xr_intern.h | 22 +- .../windowmanager/xr/intern/wm_xr_operators.c | 1537 +++++++++++++++++ .../windowmanager/xr/intern/wm_xr_session.c | 176 +- source/blender/windowmanager/xr/wm_xr.h | 3 + 12 files changed, 1863 insertions(+), 126 deletions(-) create mode 100644 source/blender/windowmanager/xr/intern/wm_xr_operators.c diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index 7002db163b6..62d1dfbf0b1 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -44,6 +44,7 @@ typedef enum { SNAP_NOT_SELECTED = 1, SNAP_NOT_ACTIVE = 2, SNAP_ONLY_ACTIVE = 3, + SNAP_SELECTABLE = 4, } eSnapSelect; typedef enum { diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index f5f3fafe897..c779fbe4a33 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -493,6 +493,11 @@ static void iter_snap_objects(SnapObjectContext *sctx, continue; } } + else if (snap_select == SNAP_SELECTABLE) { + if (!(base->flag & BASE_SELECTABLE)) { + continue; + } + } Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base->object); if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) { diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index 6f4f7e3e8ae..f7da912f299 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -32,8 +32,8 @@ typedef struct XrSessionSettings { /** Shading settings, struct shared with 3D-View so settings are the same. */ struct View3DShading shading; - char _pad[7]; - + float base_scale; + char _pad[3]; char base_pose_type; /* #eXRSessionBasePoseType */ /** Object to take the location and rotation as base position from. */ Object *base_pose_object; diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 3705284ca66..0f6544e6d47 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -892,6 +892,71 @@ static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float * # endif } +static void rna_XrSessionState_nav_location_get(PointerRNA *ptr, float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_location_get(xr, r_values); +# else + UNUSED_VARS(ptr); + zero_v3(r_values); +# endif +} + +static void rna_XrSessionState_nav_location_set(PointerRNA *ptr, const float *values) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_location_set(xr, values); +# else + UNUSED_VARS(ptr, values); +# endif +} + +static void rna_XrSessionState_nav_rotation_get(PointerRNA *ptr, float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_rotation_get(xr, r_values); +# else + UNUSED_VARS(ptr); + unit_qt(r_values); +# endif +} + +static void rna_XrSessionState_nav_rotation_set(PointerRNA *ptr, const float *values) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_rotation_set(xr, values); +# else + UNUSED_VARS(ptr, values); +# endif +} + +static float rna_XrSessionState_nav_scale_get(PointerRNA *ptr) +{ + float value; +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_scale_get(xr, &value); +# else + UNUSED_VARS(ptr); + value = 1.0f; +# endif + return value; +} + +static void rna_XrSessionState_nav_scale_set(PointerRNA *ptr, float value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_scale_set(xr, value); +# else + UNUSED_VARS(ptr, value); +# endif +} + static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -1615,6 +1680,13 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) "Rotation angle around the Z-Axis to apply the rotation deltas from the VR headset to"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "base_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Base Scale", "Uniform scale to apply to VR view"); + RNA_def_property_range(prop, 1e-6f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "show_floor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_GRIDFLOOR); RNA_def_property_ui_text(prop, "Display Grid Floor", "Show the ground plane grid"); @@ -1981,6 +2053,32 @@ static void rna_def_xr_session_state(BlenderRNA *brna) "Viewer Pose Rotation", "Last known rotation of the viewer pose (center between the eyes) in world space"); + prop = RNA_def_property(srna, "navigation_location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 3); + RNA_def_property_float_funcs( + prop, "rna_XrSessionState_nav_location_get", "rna_XrSessionState_nav_location_set", NULL); + RNA_def_property_ui_text( + prop, + "Navigation Location", + "Location offset to apply to base pose when determining viewer location"); + + prop = RNA_def_property(srna, "navigation_rotation", PROP_FLOAT, PROP_QUATERNION); + RNA_def_property_array(prop, 4); + RNA_def_property_float_funcs( + prop, "rna_XrSessionState_nav_rotation_get", "rna_XrSessionState_nav_rotation_set", NULL); + RNA_def_property_ui_text( + prop, + "Navigation Rotation", + "Rotation offset to apply to base pose when determining viewer rotation"); + + prop = RNA_def_property(srna, "navigation_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_funcs( + prop, "rna_XrSessionState_nav_scale_get", "rna_XrSessionState_nav_scale_set", NULL); + RNA_def_property_ui_text( + prop, + "Navigation Scale", + "Additional scale multiplier to apply to base scale when determining viewer scale"); + prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_funcs(prop, "rna_XrSessionState_actionmaps_begin", diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 4d65726fe2b..03b2fb49085 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -202,6 +202,7 @@ if(WITH_XR_OPENXR) xr/intern/wm_xr_action.c xr/intern/wm_xr_actionmap.c xr/intern/wm_xr_draw.c + xr/intern/wm_xr_operators.c xr/intern/wm_xr_session.c xr/wm_xr.h diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 2988aacc4d3..2d5100eec7c 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1027,6 +1027,13 @@ bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr, bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, unsigned int subaction_idx, float r_rotation[4]); +bool WM_xr_session_state_nav_location_get(const wmXrData *xr, float r_location[3]); +void WM_xr_session_state_nav_location_set(wmXrData *xr, const float location[3]); +bool WM_xr_session_state_nav_rotation_get(const wmXrData *xr, float r_rotation[4]); +void WM_xr_session_state_nav_rotation_set(wmXrData *xr, const float rotation[4]); +bool WM_xr_session_state_nav_scale_get(const wmXrData *xr, float *r_scale); +void WM_xr_session_state_nav_scale_set(wmXrData *xr, float scale); +void WM_xr_session_state_navigation_reset(struct wmXrSessionState *state); struct ARegionType *WM_xr_surface_controller_region_type_get(void); diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 89f0206d72e..1130ad9a558 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -3759,87 +3759,6 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot) /** \} */ -#ifdef WITH_XR_OPENXR - -static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data) -{ - const bool session_exists = WM_xr_session_exists(xr_data); - - for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - LISTBASE_FOREACH (SpaceLink *, slink, &area->spacedata) { - if (slink->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *)slink; - - if (v3d->flag & V3D_XR_SESSION_MIRROR) { - ED_view3d_xr_mirror_update(area, v3d, session_exists); - } - - if (session_exists) { - wmWindowManager *wm = bmain->wm.first; - const Scene *scene = WM_windows_scene_get_from_screen(wm, screen); - - ED_view3d_xr_shading_update(wm, v3d, scene); - } - /* Ensure no 3D View is tagged as session root. */ - else { - v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT; - } - } - } - } - } - - WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL); -} - -static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data) -{ - /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */ - wm_xr_session_update_screen(G_MAIN, xr_data); -} - -static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - View3D *v3d = CTX_wm_view3d(C); - - /* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */ - if (wm_xr_init(wm) == false) { - return OPERATOR_CANCELLED; - } - - v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT; - wm_xr_session_toggle(wm, win, wm_xr_session_update_screen_on_exit_cb); - wm_xr_session_update_screen(bmain, &wm->xr); - - WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL); - - return OPERATOR_FINISHED; -} - -static void WM_OT_xr_session_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle VR Session"; - ot->idname = "WM_OT_xr_session_toggle"; - ot->description = - "Open a view for use with virtual reality headsets, or close it if already " - "opened"; - - /* callbacks */ - ot->exec = wm_xr_session_toggle_exec; - ot->poll = ED_operator_view3d_active; - - /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the - * UI instead. Not meant as a permanent solution. */ - ot->flag = OPTYPE_INTERNAL; -} - -#endif /* WITH_XR_OPENXR */ - /* -------------------------------------------------------------------- */ /** \name Operator Registration & Keymaps * \{ */ @@ -3881,9 +3800,6 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_call_panel); WM_operatortype_append(WM_OT_radial_control); WM_operatortype_append(WM_OT_stereo3d_set); -#ifdef WITH_XR_OPENXR - WM_operatortype_append(WM_OT_xr_session_toggle); -#endif #if defined(WIN32) WM_operatortype_append(WM_OT_console_toggle); #endif @@ -3891,6 +3807,10 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_previews_clear); WM_operatortype_append(WM_OT_doc_view_manual_ui_context); +#ifdef WITH_XR_OPENXR + wm_xr_operatortypes_register(); +#endif + /* gizmos */ WM_operatortype_append(GIZMOGROUP_OT_gizmo_select); WM_operatortype_append(GIZMOGROUP_OT_gizmo_tweak); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index 628d50f05bd..9829e13c677 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -49,6 +49,16 @@ void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]) copy_v3_v3(r_mat[3], pose->position); } +void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4]) +{ + wm_xr_pose_to_mat(pose, r_mat); + + BLI_assert(scale > 0.0f); + mul_v3_fl(r_mat[0], scale); + mul_v3_fl(r_mat[1], scale); + mul_v3_fl(r_mat[2], scale); +} + void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]) { float iquat[4]; @@ -57,15 +67,32 @@ void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]) translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]); } +void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4]) +{ + float iquat[4]; + invert_qt_qt_normalized(iquat, pose->orientation_quat); + quat_to_mat4(r_imat, iquat); + + BLI_assert(scale > 0.0f); + scale = 1.0f / scale; + mul_v3_fl(r_imat[0], scale); + mul_v3_fl(r_imat[1], scale); + mul_v3_fl(r_imat[2], scale); + + translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]); +} + static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, const GHOST_XrDrawViewInfo *draw_view, const XrSessionSettings *session_settings, - float r_view_mat[4][4], - float r_proj_mat[4][4]) + const wmXrSessionState *session_state, + float r_viewmat[4][4], + float r_projmat[4][4]) { GHOST_XrPose eye_pose; - float eye_inv[4][4], base_inv[4][4]; + float eye_inv[4][4], base_inv[4][4], nav_inv[4][4], m[4][4]; + /* Calculate inverse eye matrix. */ copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat); copy_v3_v3(eye_pose.position, draw_view->eye_pose.position); if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { @@ -76,12 +103,14 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, } wm_xr_pose_to_imat(&eye_pose, eye_inv); - /* Calculate the base pose matrix (in world space!). */ - wm_xr_pose_to_imat(&draw_data->base_pose, base_inv); - mul_m4_m4m4(r_view_mat, eye_inv, base_inv); + /* Apply base pose and navigation. */ + wm_xr_pose_scale_to_imat(&draw_data->base_pose, draw_data->base_scale, base_inv); + wm_xr_pose_scale_to_imat(&session_state->nav_pose_prev, session_state->nav_scale_prev, nav_inv); + mul_m4_m4m4(m, eye_inv, base_inv); + mul_m4_m4m4(r_viewmat, m, nav_inv); - perspective_m4_fov(r_proj_mat, + perspective_m4_fov(r_projmat, draw_view->fov.angle_left, draw_view->fov.angle_right, draw_view->fov.angle_up, @@ -131,8 +160,8 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) BLI_assert(WM_xr_session_is_ready(xr_data)); wm_xr_session_draw_data_update(session_state, settings, draw_view, draw_data); - wm_xr_draw_matrices_create(draw_data, draw_view, settings, viewmat, winmat); - wm_xr_session_state_update(settings, draw_data, draw_view, session_state); + wm_xr_draw_matrices_create(draw_data, draw_view, settings, session_state, viewmat, winmat); + wm_xr_session_state_update(settings, draw_data, draw_view, viewmat, session_state); if (!wm_xr_session_surface_offscreen_ensure(surface_data, draw_view)) { return; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 2cd0ba5c056..6df70dc37aa 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -33,6 +33,8 @@ typedef struct wmXrSessionState { GHOST_XrPose viewer_pose; /** The last known view matrix, calculated from above's viewer pose. */ float viewer_viewmat[4][4]; + /** The last known viewer matrix, without navigation applied. */ + float viewer_mat_base[4][4]; float focal_len; /** Copy of XrSessionSettings.base_pose_ data to detect changes that need @@ -43,6 +45,8 @@ typedef struct wmXrSessionState { int prev_settings_flag; /** Copy of wmXrDrawData.base_pose. */ GHOST_XrPose prev_base_pose; + /** Copy of wmXrDrawData.base_scale. */ + float prev_base_scale; /** Copy of GHOST_XrDrawViewInfo.local_pose. */ GHOST_XrPose prev_local_pose; /** Copy of wmXrDrawData.eye_position_ofs. */ @@ -51,6 +55,15 @@ typedef struct wmXrSessionState { bool force_reset_to_base_pose; bool is_view_data_set; + /** Current navigation transforms. */ + GHOST_XrPose nav_pose; + float nav_scale; + /** Navigation transforms from the last actions sync, used to calculate the viewer/controller + * poses. */ + GHOST_XrPose nav_pose_prev; + float nav_scale_prev; + bool is_navigation_dirty; + /** Last known controller data. */ ListBase controllers; /* #wmXrController */ @@ -106,6 +119,8 @@ typedef struct wmXrDrawData { * space). With positional tracking enabled, it should be the same as the base pose, when * disabled it also contains a location delta from the moment the option was toggled. */ GHOST_XrPose base_pose; + /** Base scale (uniform, world space). */ + float base_scale; /** Offset to _substract_ from the OpenXR eye and viewer pose to get the wanted effective pose * (e.g. a pose exactly at the landmark position). */ float eye_position_ofs[3]; /* Local/view space. */ @@ -123,9 +138,11 @@ typedef struct wmXrController { /** Pose (in world space) that represents the user's hand when holding the controller. */ GHOST_XrPose grip_pose; float grip_mat[4][4]; + float grip_mat_base[4][4]; /** Pose (in world space) that represents the controller's aiming source. */ GHOST_XrPose aim_pose; float aim_mat[4][4]; + float aim_mat_base[4][4]; /** Controller model. */ struct GPUBatch *model; @@ -192,13 +209,14 @@ void wm_xr_runtime_data_free(wmXrRuntimeData **runtime); void wm_xr_session_data_free(wmXrSessionState *state); wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm, const wmXrRuntimeData *runtime_data); -void wm_xr_session_draw_data_update(const wmXrSessionState *state, +void wm_xr_session_draw_data_update(wmXrSessionState *state, const XrSessionSettings *settings, const GHOST_XrDrawViewInfo *draw_view, wmXrDrawData *draw_data); void wm_xr_session_state_update(const XrSessionSettings *settings, const wmXrDrawData *draw_data, const GHOST_XrDrawViewInfo *draw_view, + const float viewmat[4][4], wmXrSessionState *state); bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, const GHOST_XrDrawViewInfo *draw_view); @@ -214,6 +232,8 @@ void wm_xr_session_controller_data_clear(wmXrSessionState *state); /* wm_xr_draw.c */ void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); +void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4]); void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]); +void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4]); void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata); void wm_xr_draw_controllers(const struct bContext *C, struct ARegion *region, void *customdata); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_operators.c b/source/blender/windowmanager/xr/intern/wm_xr_operators.c new file mode 100644 index 00000000000..652e357b6d5 --- /dev/null +++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c @@ -0,0 +1,1537 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup wm + * + * \name Window-Manager XR Operators + * + * Collection of XR-related operators. + */ + +#include "BLI_kdopbvh.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_main.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph.h" + +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_transform_snap_object_context.h" +#include "ED_view3d.h" + +#include "GHOST_Types.h" + +#include "GPU_immediate.h" + +#include "MEM_guardedalloc.h" + +#include "PIL_time.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_xr_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name Operator Conditions + * \{ */ + +/* op->poll */ +static bool wm_xr_operator_sessionactive(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_session_is_ready(&wm->xr); +} + +static bool wm_xr_operator_test_event(const wmOperator *op, const wmEvent *event) +{ + if (event->type != EVT_XR_ACTION) { + return false; + } + + BLI_assert(event->custom == EVT_DATA_XR); + BLI_assert(event->customdata); + + wmXrActionData *actiondata = event->customdata; + return (actiondata->ot == op->type && + IDP_EqualsProperties(actiondata->op_properties, op->properties)); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session Toggle + * + * Toggles an XR session, creating an XR context if necessary. + * \{ */ + +static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data) +{ + const bool session_exists = WM_xr_session_exists(xr_data); + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, slink, &area->spacedata) { + if (slink->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)slink; + + if (v3d->flag & V3D_XR_SESSION_MIRROR) { + ED_view3d_xr_mirror_update(area, v3d, session_exists); + } + + if (session_exists) { + wmWindowManager *wm = bmain->wm.first; + const Scene *scene = WM_windows_scene_get_from_screen(wm, screen); + + ED_view3d_xr_shading_update(wm, v3d, scene); + } + /* Ensure no 3D View is tagged as session root. */ + else { + v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT; + } + } + } + } + } + + WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL); +} + +static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data) +{ + /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */ + wm_xr_session_update_screen(G_MAIN, xr_data); +} + +static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); + + /* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */ + if (wm_xr_init(wm) == false) { + return OPERATOR_CANCELLED; + } + + v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT; + wm_xr_session_toggle(wm, win, wm_xr_session_update_screen_on_exit_cb); + wm_xr_session_update_screen(bmain, &wm->xr); + + WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL); + + return OPERATOR_FINISHED; +} + +static void WM_OT_xr_session_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle VR Session"; + ot->idname = "WM_OT_xr_session_toggle"; + ot->description = + "Open a view for use with virtual reality headsets, or close it if already " + "opened"; + + /* callbacks */ + ot->exec = wm_xr_session_toggle_exec; + ot->poll = ED_operator_view3d_active; + + /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the + * UI instead. Not meant as a permanent solution. */ + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Grab Utilities + * \{ */ + +typedef struct XrGrabData { + float mat_prev[4][4]; + float mat_other_prev[4][4]; + bool bimanual_prev; + bool loc_lock, locz_lock, rot_lock, rotz_lock, scale_lock; +} XrGrabData; + +static void wm_xr_grab_init(wmOperator *op) +{ + BLI_assert(op->customdata == NULL); + + op->customdata = MEM_callocN(sizeof(XrGrabData), __func__); +} + +static void wm_xr_grab_uninit(wmOperator *op) +{ + MEM_SAFE_FREE(op->customdata); +} + +static void wm_xr_grab_update(wmOperator *op, const wmXrActionData *actiondata) +{ + XrGrabData *data = op->customdata; + + quat_to_mat4(data->mat_prev, actiondata->controller_rot); + copy_v3_v3(data->mat_prev[3], actiondata->controller_loc); + + if (actiondata->bimanual) { + quat_to_mat4(data->mat_other_prev, actiondata->controller_rot_other); + copy_v3_v3(data->mat_other_prev[3], actiondata->controller_loc_other); + data->bimanual_prev = true; + } + else { + data->bimanual_prev = false; + } +} + +static void orient_mat_z_normalized(float R[4][4], const float z_axis[3]) +{ + const float scale = len_v3(R[0]); + float x_axis[3], y_axis[3]; + + cross_v3_v3v3(y_axis, z_axis, R[0]); + normalize_v3(y_axis); + mul_v3_v3fl(R[1], y_axis, scale); + + cross_v3_v3v3(x_axis, R[1], z_axis); + normalize_v3(x_axis); + mul_v3_v3fl(R[0], x_axis, scale); + + mul_v3_v3fl(R[2], z_axis, scale); +} + +static void wm_xr_navlocks_apply(const float nav_mat[4][4], + const float nav_inv[4][4], + bool loc_lock, + bool locz_lock, + bool rotz_lock, + float r_prev[4][4], + float r_curr[4][4]) +{ + /* Locked in base pose coordinates. */ + float prev_base[4][4], curr_base[4][4]; + + mul_m4_m4m4(prev_base, nav_inv, r_prev); + mul_m4_m4m4(curr_base, nav_inv, r_curr); + + if (rotz_lock) { + const float z_axis[3] = {0.0f, 0.0f, 1.0f}; + orient_mat_z_normalized(prev_base, z_axis); + orient_mat_z_normalized(curr_base, z_axis); + } + + if (loc_lock) { + copy_v3_v3(curr_base[3], prev_base[3]); + } + else if (locz_lock) { + curr_base[3][2] = prev_base[3][2]; + } + + mul_m4_m4m4(r_prev, nav_mat, prev_base); + mul_m4_m4m4(r_curr, nav_mat, curr_base); +} + +/** + * Compute transformation delta for a one-handed grab interaction. + * + * \param actiondata: Contains current controller pose in world space. + * \param data: Contains previous controller pose in world space. + * + * The delta is computed as the difference between the current and previous + * controller poses i.e. delta = curr * prev^-1. + */ +static void wm_xr_grab_compute(const wmXrActionData *actiondata, + const XrGrabData *data, + const float nav_mat[4][4], + const float nav_inv[4][4], + bool reverse, + float r_delta[4][4]) +{ + const bool nav_lock = (nav_mat && nav_inv); + float prev[4][4], curr[4][4]; + + if (!data->rot_lock) { + copy_m4_m4(prev, data->mat_prev); + zero_v3(prev[3]); + quat_to_mat4(curr, actiondata->controller_rot); + } + else { + unit_m4(prev); + unit_m4(curr); + } + + if (!data->loc_lock || nav_lock) { + copy_v3_v3(prev[3], data->mat_prev[3]); + copy_v3_v3(curr[3], actiondata->controller_loc); + } + + if (nav_lock) { + wm_xr_navlocks_apply( + nav_mat, nav_inv, data->loc_lock, data->locz_lock, data->rotz_lock, prev, curr); + } + + if (reverse) { + invert_m4(curr); + mul_m4_m4m4(r_delta, prev, curr); + } + else { + invert_m4(prev); + mul_m4_m4m4(r_delta, curr, prev); + } +} + +/** + * Compute transformation delta for a two-handed (bimanual) grab interaction. + * + * \param actiondata: Contains current controller poses in world space. + * \param data: Contains previous controller poses in world space. + * + * The delta is computed as the difference (delta = curr * prev^-1) between the current + * and previous transformations, where the transformations themselves are determined as follows: + * - Translation: Averaged controller positions. + * - Rotation: Rotation of axis line between controllers. + * - Scale: Distance between controllers. + */ +static void wm_xr_grab_compute_bimanual(const wmXrActionData *actiondata, + const XrGrabData *data, + const float nav_mat[4][4], + const float nav_inv[4][4], + bool reverse, + float r_delta[4][4]) +{ + const bool nav_lock = (nav_mat && nav_inv); + float prev[4][4], curr[4][4]; + unit_m4(prev); + unit_m4(curr); + + if (!data->rot_lock) { + /* Rotation. */ + float x_axis_prev[3], x_axis_curr[3], y_axis_prev[3], y_axis_curr[3], z_axis_prev[3], + z_axis_curr[3]; + float m0[3][3], m1[3][3]; + quat_to_mat3(m0, actiondata->controller_rot); + quat_to_mat3(m1, actiondata->controller_rot_other); + + /* x-axis is the base line between the two controllers. */ + sub_v3_v3v3(x_axis_prev, data->mat_prev[3], data->mat_other_prev[3]); + sub_v3_v3v3(x_axis_curr, actiondata->controller_loc, actiondata->controller_loc_other); + /* y-axis is the average of the controllers' y-axes. */ + add_v3_v3v3(y_axis_prev, data->mat_prev[1], data->mat_other_prev[1]); + mul_v3_fl(y_axis_prev, 0.5f); + add_v3_v3v3(y_axis_curr, m0[1], m1[1]); + mul_v3_fl(y_axis_curr, 0.5f); + /* z-axis is the cross product of the two. */ + cross_v3_v3v3(z_axis_prev, x_axis_prev, y_axis_prev); + cross_v3_v3v3(z_axis_curr, x_axis_curr, y_axis_curr); + /* Fix the y-axis to be orthogonal. */ + cross_v3_v3v3(y_axis_prev, z_axis_prev, x_axis_prev); + cross_v3_v3v3(y_axis_curr, z_axis_curr, x_axis_curr); + /* Normalize. */ + normalize_v3_v3(prev[0], x_axis_prev); + normalize_v3_v3(prev[1], y_axis_prev); + normalize_v3_v3(prev[2], z_axis_prev); + normalize_v3_v3(curr[0], x_axis_curr); + normalize_v3_v3(curr[1], y_axis_curr); + normalize_v3_v3(curr[2], z_axis_curr); + } + + if (!data->loc_lock || nav_lock) { + /* Translation: translation of the averaged controller locations. */ + add_v3_v3v3(prev[3], data->mat_prev[3], data->mat_other_prev[3]); + mul_v3_fl(prev[3], 0.5f); + add_v3_v3v3(curr[3], actiondata->controller_loc, actiondata->controller_loc_other); + mul_v3_fl(curr[3], 0.5f); + } + + if (!data->scale_lock) { + /* Scaling: distance between controllers. */ + float scale, v[3]; + + sub_v3_v3v3(v, data->mat_prev[3], data->mat_other_prev[3]); + scale = len_v3(v); + mul_v3_fl(prev[0], scale); + mul_v3_fl(prev[1], scale); + mul_v3_fl(prev[2], scale); + + sub_v3_v3v3(v, actiondata->controller_loc, actiondata->controller_loc_other); + scale = len_v3(v); + mul_v3_fl(curr[0], scale); + mul_v3_fl(curr[1], scale); + mul_v3_fl(curr[2], scale); + } + + if (nav_lock) { + wm_xr_navlocks_apply( + nav_mat, nav_inv, data->loc_lock, data->locz_lock, data->rotz_lock, prev, curr); + } + + if (reverse) { + invert_m4(curr); + mul_m4_m4m4(r_delta, prev, curr); + } + else { + invert_m4(prev); + mul_m4_m4m4(r_delta, curr, prev); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Grab + * + * Navigates the scene by grabbing with XR controllers. + * \{ */ + +static int wm_xr_navigation_grab_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + + wm_xr_grab_init(op); + wm_xr_grab_update(op, actiondata); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_xr_navigation_grab_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static bool wm_xr_navigation_grab_can_do_bimanual(const wmXrActionData *actiondata, + const XrGrabData *data) +{ + /* Returns true if: 1) Bimanual interaction is currently occurring (i.e. inputs on both + controllers are pressed) and 2) bimanual interaction occurred on the last update. This second + part is needed to avoid "jumpy" navigation changes when transitioning from one-handed to + two-handed interaction (see #wm_xr_grab_compute/compute_bimanual() for how navigation deltas + are calculated). */ + return (actiondata->bimanual && data->bimanual_prev); +} + +static bool wm_xr_navigation_grab_is_bimanual_ending(const wmXrActionData *actiondata, + const XrGrabData *data) +{ + return (!actiondata->bimanual && data->bimanual_prev); +} + +static bool wm_xr_navigation_grab_is_locked(const XrGrabData *data, const bool bimanual) +{ + if (bimanual) { + return data->loc_lock && data->rot_lock && data->scale_lock; + } + else { + /* Ignore scale lock, as one-handed interaction cannot change navigation scale. */ + return data->loc_lock && data->rot_lock; + } +} + +static void wm_xr_navigation_grab_apply(wmXrData *xr, + const wmXrActionData *actiondata, + const XrGrabData *data, + bool bimanual) +{ + GHOST_XrPose nav_pose; + float nav_scale; + float nav_mat[4][4], nav_inv[4][4], delta[4][4], out[4][4]; + + const bool need_navinv = (data->loc_lock || data->locz_lock || data->rotz_lock); + + WM_xr_session_state_nav_location_get(xr, nav_pose.position); + WM_xr_session_state_nav_rotation_get(xr, nav_pose.orientation_quat); + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + + wm_xr_pose_scale_to_mat(&nav_pose, nav_scale, nav_mat); + if (need_navinv) { + wm_xr_pose_scale_to_imat(&nav_pose, nav_scale, nav_inv); + } + + if (bimanual) { + wm_xr_grab_compute_bimanual( + actiondata, data, need_navinv ? nav_mat : NULL, need_navinv ? nav_inv : NULL, true, delta); + } + else { + wm_xr_grab_compute( + actiondata, data, need_navinv ? nav_mat : NULL, need_navinv ? nav_inv : NULL, true, delta); + } + + mul_m4_m4m4(out, delta, nav_mat); + + /* Limit scale to reasonable values. */ + nav_scale = len_v3(out[0]); + + if (!(nav_scale < xr->session_settings.clip_start || + nav_scale > xr->session_settings.clip_end)) { + WM_xr_session_state_nav_location_set(xr, out[3]); + if (!data->rot_lock) { + mat4_to_quat(nav_pose.orientation_quat, out); + normalize_qt(nav_pose.orientation_quat); + WM_xr_session_state_nav_rotation_set(xr, nav_pose.orientation_quat); + } + if (!data->scale_lock && bimanual) { + WM_xr_session_state_nav_scale_set(xr, nav_scale); + } + } +} + +static void wm_xr_navigation_grab_bimanual_state_update(const wmXrActionData *actiondata, + XrGrabData *data) +{ + if (actiondata->bimanual) { + if (!data->bimanual_prev) { + quat_to_mat4(data->mat_prev, actiondata->controller_rot); + copy_v3_v3(data->mat_prev[3], actiondata->controller_loc); + quat_to_mat4(data->mat_other_prev, actiondata->controller_rot_other); + copy_v3_v3(data->mat_other_prev[3], actiondata->controller_loc_other); + } + data->bimanual_prev = true; + } + else { + if (data->bimanual_prev) { + quat_to_mat4(data->mat_prev, actiondata->controller_rot); + copy_v3_v3(data->mat_prev[3], actiondata->controller_loc); + } + data->bimanual_prev = false; + } +} + +static int wm_xr_navigation_grab_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + XrGrabData *data = op->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + + const bool do_bimanual = wm_xr_navigation_grab_can_do_bimanual(actiondata, data); + + data->loc_lock = RNA_boolean_get(op->ptr, "lock_location"); + data->locz_lock = RNA_boolean_get(op->ptr, "lock_location_z"); + data->rot_lock = RNA_boolean_get(op->ptr, "lock_rotation"); + data->rotz_lock = RNA_boolean_get(op->ptr, "lock_rotation_z"); + data->scale_lock = RNA_boolean_get(op->ptr, "lock_scale"); + + /* Check if navigation is locked. */ + if (!wm_xr_navigation_grab_is_locked(data, do_bimanual)) { + /* Prevent unwanted snapping (i.e. "jumpy" navigation changes when transitioning from + two-handed to one-handed interaction) at the end of a bimanual interaction. */ + if (!wm_xr_navigation_grab_is_bimanual_ending(actiondata, data)) { + wm_xr_navigation_grab_apply(xr, actiondata, data, do_bimanual); + } + } + + wm_xr_navigation_grab_bimanual_state_update(actiondata, data); + + /* Note: KM_PRESS and KM_RELEASE are the only two values supported by XR events during event + dispatching (see #wm_xr_session_action_states_interpret()). For modal XR operators, modal + handling starts when an input is "pressed" (action state exceeds the action threshold) and + ends when the input is "released" (state falls below the threshold). */ + if (event->val == KM_PRESS) { + return OPERATOR_RUNNING_MODAL; + } + else if (event->val == KM_RELEASE) { + wm_xr_grab_uninit(op); + return OPERATOR_FINISHED; + } + + BLI_assert_unreachable(); + wm_xr_grab_uninit(op); + return OPERATOR_CANCELLED; +} + +static void WM_OT_xr_navigation_grab(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Grab"; + ot->idname = "WM_OT_xr_navigation_grab"; + ot->description = "Navigate the VR scene by grabbing with controllers"; + + /* callbacks */ + ot->invoke = wm_xr_navigation_grab_invoke; + ot->exec = wm_xr_navigation_grab_exec; + ot->modal = wm_xr_navigation_grab_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + RNA_def_boolean( + ot->srna, "lock_location", false, "Lock Location", "Prevent changes to viewer location"); + RNA_def_boolean( + ot->srna, "lock_location_z", false, "Lock Elevation", "Prevent changes to viewer elevation"); + RNA_def_boolean( + ot->srna, "lock_rotation", false, "Lock Rotation", "Prevent changes to viewer rotation"); + RNA_def_boolean(ot->srna, + "lock_rotation_z", + false, + "Lock Up Orientation", + "Prevent changes to viewer up orientation"); + RNA_def_boolean(ot->srna, "lock_scale", false, "Lock Scale", "Prevent changes to viewer scale"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Raycast Utilities + * \{ */ + +static const float g_xr_default_raycast_axis[3] = {0.0f, 0.0f, -1.0f}; +static const float g_xr_default_raycast_color[4] = {0.35f, 0.35f, 1.0f, 1.0f}; + +typedef struct XrRaycastData { + bool from_viewer; + float origin[3]; + float direction[3]; + float end[3]; + float color[4]; + void *draw_handle; +} XrRaycastData; + +static void wm_xr_raycast_draw(const bContext *UNUSED(C), + ARegion *UNUSED(region), + void *customdata) +{ + const XrRaycastData *data = customdata; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + if (data->from_viewer) { + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor4fv(data->color); + + GPU_depth_test(GPU_DEPTH_NONE); + GPU_point_size(7.0f); + + immBegin(GPU_PRIM_POINTS, 1); + immVertex3fv(pos, data->end); + immEnd(); + } + else { + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR); + + float viewport[4]; + GPU_viewport_size_get_f(viewport); + immUniform2fv("viewportSize", &viewport[2]); + + immUniform1f("lineWidth", 3.0f * U.pixelsize); + + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + + immBegin(GPU_PRIM_LINES, 2); + immAttrSkip(col); + immVertex3fv(pos, data->origin); + immAttr4fv(col, data->color); + immVertex3fv(pos, data->end); + immEnd(); + } + + immUnbindProgram(); +} + +static void wm_xr_raycast_init(wmOperator *op) +{ + BLI_assert(op->customdata == NULL); + + op->customdata = MEM_callocN(sizeof(XrRaycastData), __func__); + + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + if (!st) { + return; + } + + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR); + if (!art) { + return; + } + + XrRaycastData *data = op->customdata; + data->draw_handle = ED_region_draw_cb_activate( + art, wm_xr_raycast_draw, op->customdata, REGION_DRAW_POST_VIEW); +} + +static void wm_xr_raycast_uninit(wmOperator *op) +{ + if (!op->customdata) { + return; + } + + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + if (st) { + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR); + if (art) { + XrRaycastData *data = op->customdata; + ED_region_draw_cb_exit(art, data->draw_handle); + } + } + + MEM_freeN(op->customdata); +} + +static void wm_xr_raycast_update(wmOperator *op, + const wmXrData *xr, + const wmXrActionData *actiondata) +{ + XrRaycastData *data = op->customdata; + float ray_length, axis[3]; + + data->from_viewer = RNA_boolean_get(op->ptr, "from_viewer"); + RNA_float_get_array(op->ptr, "axis", axis); + RNA_float_get_array(op->ptr, "color", data->color); + + if (data->from_viewer) { + float viewer_rot[4]; + WM_xr_session_state_viewer_pose_location_get(xr, data->origin); + WM_xr_session_state_viewer_pose_rotation_get(xr, viewer_rot); + mul_qt_v3(viewer_rot, axis); + ray_length = (xr->session_settings.clip_start + xr->session_settings.clip_end) / 2.0f; + } + else { + copy_v3_v3(data->origin, actiondata->controller_loc); + mul_qt_v3(actiondata->controller_rot, axis); + ray_length = xr->session_settings.clip_end; + } + + copy_v3_v3(data->direction, axis); + madd_v3_v3v3fl(data->end, data->origin, data->direction, ray_length); +} + +static void wm_xr_raycast(Scene *scene, + Depsgraph *depsgraph, + const float origin[3], + const float direction[3], + float *ray_dist, + bool selectable_only, + float r_location[3], + float r_normal[3], + int *r_index, + Object **r_ob, + float r_obmat[4][4]) +{ + /* Uses same raycast method as Scene.ray_cast(). */ + SnapObjectContext *sctx = ED_transform_snap_object_context_create(scene, 0); + + ED_transform_snap_object_project_ray_ex( + sctx, + depsgraph, + NULL, + &(const struct SnapObjectParams){ + .snap_select = (selectable_only ? SNAP_SELECTABLE : SNAP_ALL)}, + origin, + direction, + ray_dist, + r_location, + r_normal, + r_index, + r_ob, + r_obmat); + + ED_transform_snap_object_context_destroy(sctx); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Fly + * + * Navigates the scene by moving/turning relative to navigation space or the XR viewer or + * controller. + * \{ */ + +#define XR_DEFAULT_FLY_SPEED_MOVE 0.054f +#define XR_DEFAULT_FLY_SPEED_TURN 0.03f + +typedef enum eXrFlyMode { + XR_FLY_FORWARD = 0, + XR_FLY_BACK = 1, + XR_FLY_LEFT = 2, + XR_FLY_RIGHT = 3, + XR_FLY_UP = 4, + XR_FLY_DOWN = 5, + XR_FLY_TURNLEFT = 6, + XR_FLY_TURNRIGHT = 7, + XR_FLY_VIEWER_FORWARD = 8, + XR_FLY_VIEWER_BACK = 9, + XR_FLY_VIEWER_LEFT = 10, + XR_FLY_VIEWER_RIGHT = 11, + XR_FLY_CONTROLLER_FORWARD = 12, +} eXrFlyMode; + +typedef struct XrFlyData { + float viewer_rot[4]; + double time_prev; +} XrFlyData; + +static void wm_xr_fly_init(wmOperator *op, const wmXrData *xr) +{ + BLI_assert(op->customdata == NULL); + + XrFlyData *data = op->customdata = MEM_callocN(sizeof(XrFlyData), __func__); + + WM_xr_session_state_viewer_pose_rotation_get(xr, data->viewer_rot); + data->time_prev = PIL_check_seconds_timer(); +} + +static void wm_xr_fly_uninit(wmOperator *op) +{ + MEM_SAFE_FREE(op->customdata); +} + +static void wm_xr_fly_compute_move(eXrFlyMode mode, + float speed, + const float ref_quat[4], + const float nav_mat[4][4], + bool locz_lock, + float r_delta[4][4]) +{ + float ref_axes[3][3]; + quat_to_mat3(ref_axes, ref_quat); + + unit_m4(r_delta); + + switch (mode) { + /* Navigation space reference. */ + case XR_FLY_FORWARD: + madd_v3_v3fl(r_delta[3], ref_axes[1], speed); + return; + case XR_FLY_BACK: + madd_v3_v3fl(r_delta[3], ref_axes[1], -speed); + return; + case XR_FLY_LEFT: + madd_v3_v3fl(r_delta[3], ref_axes[0], -speed); + return; + case XR_FLY_RIGHT: + madd_v3_v3fl(r_delta[3], ref_axes[0], speed); + return; + case XR_FLY_UP: + case XR_FLY_DOWN: + if (!locz_lock) { + madd_v3_v3fl(r_delta[3], ref_axes[2], (mode == XR_FLY_UP) ? speed : -speed); + } + return; + /* Viewer/controller space reference. */ + case XR_FLY_VIEWER_FORWARD: + case XR_FLY_CONTROLLER_FORWARD: + negate_v3_v3(r_delta[3], ref_axes[2]); + break; + case XR_FLY_VIEWER_BACK: + copy_v3_v3(r_delta[3], ref_axes[2]); + break; + case XR_FLY_VIEWER_LEFT: + negate_v3_v3(r_delta[3], ref_axes[0]); + break; + case XR_FLY_VIEWER_RIGHT: + copy_v3_v3(r_delta[3], ref_axes[0]); + break; + /* Unused. */ + case XR_FLY_TURNLEFT: + case XR_FLY_TURNRIGHT: + BLI_assert_unreachable(); + return; + } + + if (locz_lock) { + /* Lock elevation in navigation space. */ + float z_axis[3], projected[3]; + + normalize_v3_v3(z_axis, nav_mat[2]); + project_v3_v3v3_normalized(projected, r_delta[3], z_axis); + sub_v3_v3(r_delta[3], projected); + + normalize_v3(r_delta[3]); + } + + mul_v3_fl(r_delta[3], speed); +} + +static void wm_xr_fly_compute_turn(eXrFlyMode mode, + float speed, + const float viewer_mat[4][4], + const float nav_mat[4][4], + const float nav_inv[4][4], + float r_delta[4][4]) +{ + BLI_assert(mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT); + + float z_axis[3], m[3][3], prev[4][4], curr[4][4]; + + /* Turn around Z-axis in navigation space. */ + normalize_v3_v3(z_axis, nav_mat[2]); + axis_angle_normalized_to_mat3(m, z_axis, (mode == XR_FLY_TURNLEFT) ? speed : -speed); + copy_m4_m3(r_delta, m); + + copy_m4_m4(prev, viewer_mat); + mul_m4_m4m4(curr, r_delta, viewer_mat); + + /* Lock location in base pose space. */ + wm_xr_navlocks_apply(nav_mat, nav_inv, true, false, false, prev, curr); + + invert_m4(prev); + mul_m4_m4m4(r_delta, curr, prev); +} + +static void wm_xr_basenav_rotation_calc(const wmXrData *xr, + const float nav_rotation[4], + float r_rotation[4]) +{ + /* Apply nav rotation to base pose Z-rotation. */ + float base_eul[3], base_quatz[4]; + quat_to_eul(base_eul, xr->runtime->session_state.prev_base_pose.orientation_quat); + axis_angle_to_quat_single(base_quatz, 'Z', base_eul[2]); + mul_qt_qtqt(r_rotation, nav_rotation, base_quatz); +} + +static int wm_xr_navigation_fly_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + wmWindowManager *wm = CTX_wm_manager(C); + + wm_xr_fly_init(op, &wm->xr); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_xr_navigation_fly_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static int wm_xr_navigation_fly_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + if (event->val == KM_RELEASE) { + wm_xr_fly_uninit(op); + return OPERATOR_FINISHED; + } + + const wmXrActionData *actiondata = event->customdata; + XrFlyData *data = op->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + eXrFlyMode mode; + bool turn, locz_lock, dir_lock, speed_frame_based; + bool speed_interp_cubic = false; + float speed, speed_max, speed_p0[2], speed_p1[2]; + GHOST_XrPose nav_pose; + float nav_mat[4][4], delta[4][4], out[4][4]; + + const double time_now = PIL_check_seconds_timer(); + + mode = (eXrFlyMode)RNA_enum_get(op->ptr, "mode"); + turn = (mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT); + + locz_lock = RNA_boolean_get(op->ptr, "lock_location_z"); + dir_lock = RNA_boolean_get(op->ptr, "lock_direction"); + speed_frame_based = RNA_boolean_get(op->ptr, "speed_frame_based"); + speed = RNA_float_get(op->ptr, "speed_min"); + speed_max = RNA_float_get(op->ptr, "speed_max"); + + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "speed_interpolation0"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_get_array(op->ptr, prop, speed_p0); + speed_interp_cubic = true; + } + else { + speed_p0[0] = speed_p0[1] = 0.0f; + } + + prop = RNA_struct_find_property(op->ptr, "speed_interpolation1"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_get_array(op->ptr, prop, speed_p1); + speed_interp_cubic = true; + } + else { + speed_p1[0] = speed_p1[1] = 1.0f; + } + + /* Ensure valid interpolation. */ + if (speed_max < speed) { + speed_max = speed; + } + + /* Interpolate between min/max speeds based on button state. */ + switch (actiondata->type) { + case XR_BOOLEAN_INPUT: + speed = speed_max; + break; + case XR_FLOAT_INPUT: + case XR_VECTOR2F_INPUT: { + float state = (actiondata->type == XR_FLOAT_INPUT) ? fabsf(actiondata->state[0]) : + len_v2(actiondata->state); + float speed_t = (actiondata->float_threshold < 1.0f) ? + (state - actiondata->float_threshold) / + (1.0f - actiondata->float_threshold) : + 1.0f; + if (speed_interp_cubic) { + float start[2], end[2], p[2]; + + start[0] = 0.0f; + start[1] = speed; + speed_p0[1] = speed + speed_p0[1] * (speed_max - speed); + speed_p1[1] = speed + speed_p1[1] * (speed_max - speed); + end[0] = 1.0f; + end[1] = speed_max; + + interp_v2_v2v2v2v2_cubic(p, start, speed_p0, speed_p1, end, speed_t); + speed = p[1]; + } + else { + speed += speed_t * (speed_max - speed); + } + break; + } + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + + if (!speed_frame_based) { + /* Adjust speed based on last update time. */ + speed *= time_now - data->time_prev; + } + data->time_prev = time_now; + + WM_xr_session_state_nav_location_get(xr, nav_pose.position); + WM_xr_session_state_nav_rotation_get(xr, nav_pose.orientation_quat); + wm_xr_pose_to_mat(&nav_pose, nav_mat); + + if (turn) { + if (dir_lock) { + unit_m4(delta); + } + else { + GHOST_XrPose viewer_pose; + float viewer_mat[4][4], nav_inv[4][4]; + + WM_xr_session_state_viewer_pose_location_get(xr, viewer_pose.position); + WM_xr_session_state_viewer_pose_rotation_get(xr, viewer_pose.orientation_quat); + wm_xr_pose_to_mat(&viewer_pose, viewer_mat); + wm_xr_pose_to_imat(&nav_pose, nav_inv); + + wm_xr_fly_compute_turn(mode, speed, viewer_mat, nav_mat, nav_inv, delta); + } + } + else { + float nav_scale, ref_quat[4]; + + /* Adjust speed for base and navigation scale. */ + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + speed *= xr->session_settings.base_scale * nav_scale; + + switch (mode) { + /* Move relative to navigation space. */ + case XR_FLY_FORWARD: + case XR_FLY_BACK: + case XR_FLY_LEFT: + case XR_FLY_RIGHT: + case XR_FLY_UP: + case XR_FLY_DOWN: + wm_xr_basenav_rotation_calc(xr, nav_pose.orientation_quat, ref_quat); + break; + /* Move relative to viewer. */ + case XR_FLY_VIEWER_FORWARD: + case XR_FLY_VIEWER_BACK: + case XR_FLY_VIEWER_LEFT: + case XR_FLY_VIEWER_RIGHT: + if (dir_lock) { + copy_qt_qt(ref_quat, data->viewer_rot); + } + else { + WM_xr_session_state_viewer_pose_rotation_get(xr, ref_quat); + } + break; + /* Move relative to controller. */ + case XR_FLY_CONTROLLER_FORWARD: + copy_qt_qt(ref_quat, actiondata->controller_rot); + break; + /* Unused. */ + case XR_FLY_TURNLEFT: + case XR_FLY_TURNRIGHT: + BLI_assert_unreachable(); + break; + } + + wm_xr_fly_compute_move(mode, speed, ref_quat, nav_mat, locz_lock, delta); + } + + mul_m4_m4m4(out, delta, nav_mat); + + WM_xr_session_state_nav_location_set(xr, out[3]); + if (turn) { + mat4_to_quat(nav_pose.orientation_quat, out); + WM_xr_session_state_nav_rotation_set(xr, nav_pose.orientation_quat); + } + + if (event->val == KM_PRESS) { + return OPERATOR_RUNNING_MODAL; + } + + /* XR events currently only support press and release. */ + BLI_assert_unreachable(); + wm_xr_fly_uninit(op); + return OPERATOR_CANCELLED; +} + +static void WM_OT_xr_navigation_fly(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Fly"; + ot->idname = "WM_OT_xr_navigation_fly"; + ot->description = "Move/turn relative to the VR viewer or controller"; + + /* callbacks */ + ot->invoke = wm_xr_navigation_fly_invoke; + ot->exec = wm_xr_navigation_fly_exec; + ot->modal = wm_xr_navigation_fly_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + static const EnumPropertyItem fly_modes[] = { + {XR_FLY_FORWARD, "FORWARD", 0, "Forward", "Move along navigation forward axis"}, + {XR_FLY_BACK, "BACK", 0, "Back", "Move along navigation back axis"}, + {XR_FLY_LEFT, "LEFT", 0, "Left", "Move along navigation left axis"}, + {XR_FLY_RIGHT, "RIGHT", 0, "Right", "Move along navigation right axis"}, + {XR_FLY_UP, "UP", 0, "Up", "Move along navigation up axis"}, + {XR_FLY_DOWN, "DOWN", 0, "Down", "Move along navigation down axis"}, + {XR_FLY_TURNLEFT, + "TURNLEFT", + 0, + "Turn Left", + "Turn counter-clockwise around navigation up axis"}, + {XR_FLY_TURNRIGHT, "TURNRIGHT", 0, "Turn Right", "Turn clockwise around navigation up axis"}, + {XR_FLY_VIEWER_FORWARD, + "VIEWER_FORWARD", + 0, + "Viewer Forward", + "Move along viewer's forward axis"}, + {XR_FLY_VIEWER_BACK, "VIEWER_BACK", 0, "Viewer Back", "Move along viewer's back axis"}, + {XR_FLY_VIEWER_LEFT, "VIEWER_LEFT", 0, "Viewer Left", "Move along viewer's left axis"}, + {XR_FLY_VIEWER_RIGHT, "VIEWER_RIGHT", 0, "Viewer Right", "Move along viewer's right axis"}, + {XR_FLY_CONTROLLER_FORWARD, + "CONTROLLER_FORWARD", + 0, + "Controller Forward", + "Move along controller's forward axis"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const float default_speed_p0[2] = {0.0f, 0.0f}; + static const float default_speed_p1[2] = {1.0f, 1.0f}; + + RNA_def_enum(ot->srna, "mode", fly_modes, XR_FLY_VIEWER_FORWARD, "Mode", "Fly mode"); + RNA_def_boolean( + ot->srna, "lock_location_z", false, "Lock Elevation", "Prevent changes to viewer elevation"); + RNA_def_boolean(ot->srna, + "lock_direction", + false, + "Lock Direction", + "Limit movement to viewer's intial direction"); + RNA_def_boolean(ot->srna, + "speed_frame_based", + true, + "Frame Based Speed", + "Apply fixed movement deltas every update"); + RNA_def_float(ot->srna, + "speed_min", + XR_DEFAULT_FLY_SPEED_MOVE / 3.0f, + 0.0f, + 1000.0f, + "Minimum Speed", + "Minimum move (turn) speed in meters (radians) per second or frame", + 0.0f, + 1000.0f); + RNA_def_float(ot->srna, + "speed_max", + XR_DEFAULT_FLY_SPEED_MOVE, + 0.0f, + 1000.0f, + "Maximum Speed", + "Maximum move (turn) speed in meters (radians) per second or frame", + 0.0f, + 1000.0f); + RNA_def_float_vector(ot->srna, + "speed_interpolation0", + 2, + default_speed_p0, + 0.0f, + 1.0f, + "Speed Interpolation 0", + "First cubic spline control point between min/max speeds", + 0.0f, + 1.0f); + RNA_def_float_vector(ot->srna, + "speed_interpolation1", + 2, + default_speed_p1, + 0.0f, + 1.0f, + "Speed Interpolation 1", + "Second cubic spline control point between min/max speeds", + 0.0f, + 1.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Teleport + * + * Casts a ray from an XR controller's pose and teleports to any hit geometry. + * \{ */ + +static void wm_xr_navigation_teleport(bContext *C, + wmXrData *xr, + const float origin[3], + const float direction[3], + float *ray_dist, + bool selectable_only, + const bool teleport_axes[3], + float teleport_t, + float teleport_ofs) +{ + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + float location[3]; + float normal[3]; + int index; + Object *ob = NULL; + float obmat[4][4]; + + wm_xr_raycast(scene, + depsgraph, + origin, + direction, + ray_dist, + selectable_only, + location, + normal, + &index, + &ob, + obmat); + + /* Teleport. */ + if (ob) { + float nav_location[3], nav_rotation[4], viewer_location[3]; + float nav_axes[3][3], projected[3], v0[3], v1[3]; + float out[3] = {0.0f, 0.0f, 0.0f}; + + WM_xr_session_state_nav_location_get(xr, nav_location); + WM_xr_session_state_nav_rotation_get(xr, nav_rotation); + WM_xr_session_state_viewer_pose_location_get(xr, viewer_location); + + wm_xr_basenav_rotation_calc(xr, nav_rotation, nav_rotation); + quat_to_mat3(nav_axes, nav_rotation); + + /* Project locations onto navigation axes. */ + for (int a = 0; a < 3; ++a) { + project_v3_v3v3_normalized(projected, nav_location, nav_axes[a]); + if (teleport_axes[a]) { + /* Interpolate between projected locations. */ + project_v3_v3v3_normalized(v0, location, nav_axes[a]); + project_v3_v3v3_normalized(v1, viewer_location, nav_axes[a]); + sub_v3_v3(v0, v1); + madd_v3_v3fl(projected, v0, teleport_t); + /* Subtract offset. */ + project_v3_v3v3_normalized(v0, normal, nav_axes[a]); + madd_v3_v3fl(projected, v0, teleport_ofs); + } + /* Add to final location. */ + add_v3_v3(out, projected); + } + + WM_xr_session_state_nav_location_set(xr, out); + } +} + +static int wm_xr_navigation_teleport_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + wm_xr_raycast_init(op); + + int retval = op->type->modal(C, op, event); + + if ((retval & OPERATOR_RUNNING_MODAL) != 0) { + WM_event_add_modal_handler(C, op); + } + + return retval; +} + +static int wm_xr_navigation_teleport_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static int wm_xr_navigation_teleport_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + + wm_xr_raycast_update(op, xr, actiondata); + + if (event->val == KM_PRESS) { + return OPERATOR_RUNNING_MODAL; + } + else if (event->val == KM_RELEASE) { + XrRaycastData *data = op->customdata; + bool selectable_only, teleport_axes[3]; + float teleport_t, teleport_ofs, ray_dist; + + RNA_boolean_get_array(op->ptr, "teleport_axes", teleport_axes); + teleport_t = RNA_float_get(op->ptr, "interpolation"); + teleport_ofs = RNA_float_get(op->ptr, "offset"); + selectable_only = RNA_boolean_get(op->ptr, "selectable_only"); + ray_dist = RNA_float_get(op->ptr, "distance"); + + wm_xr_navigation_teleport(C, + xr, + data->origin, + data->direction, + &ray_dist, + selectable_only, + teleport_axes, + teleport_t, + teleport_ofs); + + wm_xr_raycast_uninit(op); + + return OPERATOR_FINISHED; + } + + /* XR events currently only support press and release. */ + BLI_assert_unreachable(); + wm_xr_raycast_uninit(op); + return OPERATOR_CANCELLED; +} + +static void WM_OT_xr_navigation_teleport(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Teleport"; + ot->idname = "WM_OT_xr_navigation_teleport"; + ot->description = "Set VR viewer location to controller raycast hit location"; + + /* callbacks */ + ot->invoke = wm_xr_navigation_teleport_invoke; + ot->exec = wm_xr_navigation_teleport_exec; + ot->modal = wm_xr_navigation_teleport_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + static bool default_teleport_axes[3] = {true, true, true}; + + RNA_def_boolean_vector(ot->srna, + "teleport_axes", + 3, + default_teleport_axes, + "Teleport Axes", + "Enabled teleport axes in navigation space"); + RNA_def_float(ot->srna, + "interpolation", + 1.0f, + 0.0f, + 1.0f, + "Interpolation", + "Interpolation factor between viewer and hit locations", + 0.0f, + 1.0f); + RNA_def_float(ot->srna, + "offset", + 0.0f, + 0.0f, + FLT_MAX, + "Offset", + "Offset along hit normal to subtract from final location", + 0.0f, + FLT_MAX); + RNA_def_boolean(ot->srna, + "selectable_only", + true, + "Selectable Only", + "Only allow selectable objects to influence raycast result"); + RNA_def_float(ot->srna, + "distance", + BVH_RAYCAST_DIST_MAX, + 0.0, + BVH_RAYCAST_DIST_MAX, + "", + "Maximum raycast distance", + 0.0, + BVH_RAYCAST_DIST_MAX); + RNA_def_boolean( + ot->srna, "from_viewer", false, "From Viewer", "Use viewer pose as raycast origin"); + RNA_def_float_vector(ot->srna, + "axis", + 3, + g_xr_default_raycast_axis, + -1.0f, + 1.0f, + "Axis", + "Raycast axis in controller/viewer space", + -1.0f, + 1.0f); + RNA_def_float_color(ot->srna, + "color", + 4, + g_xr_default_raycast_color, + 0.0f, + 1.0f, + "Color", + "Raycast color", + 0.0f, + 1.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Reset + * + * Resets XR navigation deltas relative to session base pose. + * \{ */ + +static int wm_xr_navigation_reset_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + bool reset_loc, reset_rot, reset_scale; + + reset_loc = RNA_boolean_get(op->ptr, "location"); + reset_rot = RNA_boolean_get(op->ptr, "rotation"); + reset_scale = RNA_boolean_get(op->ptr, "scale"); + + if (reset_loc) { + float loc[3]; + if (!reset_scale) { + float nav_rotation[4], nav_scale; + + WM_xr_session_state_nav_rotation_get(xr, nav_rotation); + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + + /* Adjust location based on scale. */ + mul_v3_v3fl(loc, xr->runtime->session_state.prev_base_pose.position, nav_scale); + sub_v3_v3(loc, xr->runtime->session_state.prev_base_pose.position); + mul_qt_v3(nav_rotation, loc); + negate_v3(loc); + } + else { + zero_v3(loc); + } + WM_xr_session_state_nav_location_set(xr, loc); + } + + if (reset_rot) { + float rot[4]; + unit_qt(rot); + WM_xr_session_state_nav_rotation_set(xr, rot); + } + + if (reset_scale) { + if (!reset_loc) { + float nav_location[3], nav_rotation[4], nav_scale; + float nav_axes[3][3], v[3]; + + WM_xr_session_state_nav_location_get(xr, nav_location); + WM_xr_session_state_nav_rotation_get(xr, nav_rotation); + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + + /* Offset any location changes when changing scale. */ + mul_v3_v3fl(v, xr->runtime->session_state.prev_base_pose.position, nav_scale); + sub_v3_v3(v, xr->runtime->session_state.prev_base_pose.position); + mul_qt_v3(nav_rotation, v); + add_v3_v3(nav_location, v); + + /* Reset elevation to base pose value. */ + quat_to_mat3(nav_axes, nav_rotation); + project_v3_v3v3_normalized(v, nav_location, nav_axes[2]); + sub_v3_v3(nav_location, v); + + WM_xr_session_state_nav_location_set(xr, nav_location); + } + WM_xr_session_state_nav_scale_set(xr, 1.0f); + } + + return OPERATOR_FINISHED; +} + +static void WM_OT_xr_navigation_reset(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Reset"; + ot->idname = "WM_OT_xr_navigation_reset"; + ot->description = "Reset VR navigation deltas relative to session base pose"; + + /* callbacks */ + ot->exec = wm_xr_navigation_reset_exec; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + RNA_def_boolean(ot->srna, "location", true, "Location", "Reset location deltas"); + RNA_def_boolean(ot->srna, "rotation", true, "Rotation", "Reset rotation deltas"); + RNA_def_boolean(ot->srna, "scale", true, "Scale", "Reset scale deltas"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operator Registration + * \{ */ + +void wm_xr_operatortypes_register(void) +{ + WM_operatortype_append(WM_OT_xr_session_toggle); + WM_operatortype_append(WM_OT_xr_navigation_grab); + WM_operatortype_append(WM_OT_xr_navigation_fly); + WM_operatortype_append(WM_OT_xr_navigation_teleport); + WM_operatortype_append(WM_OT_xr_navigation_reset); +} + +/** \} */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 9f53db1347c..62757c0bddd 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -66,11 +66,20 @@ static void wm_xr_session_create_cb(void) Main *bmain = G_MAIN; wmWindowManager *wm = bmain->wm.first; wmXrData *xr_data = &wm->xr; + wmXrSessionState *state = &xr_data->runtime->session_state; + XrSessionSettings *settings = &xr_data->session_settings; /* Get action set data from Python. */ BKE_callback_exec_null(bmain, BKE_CB_EVT_XR_SESSION_START_PRE); wm_xr_session_actions_init(xr_data); + + /* Initialize navigation. */ + WM_xr_session_state_navigation_reset(state); + if (settings->base_scale < FLT_EPSILON) { + settings->base_scale = 1.0f; + } + state->prev_base_scale = settings->base_scale; } static void wm_xr_session_controller_data_free(wmXrSessionState *state) @@ -167,7 +176,8 @@ bool WM_xr_session_is_ready(const wmXrData *xr) static void wm_xr_session_base_pose_calc(const Scene *scene, const XrSessionSettings *settings, - GHOST_XrPose *r_base_pose) + GHOST_XrPose *r_base_pose, + float *r_base_scale) { const Object *base_pose_object = ((settings->base_pose_type == XR_BASE_POSE_OBJECT) && settings->base_pose_object) ? @@ -198,6 +208,8 @@ static void wm_xr_session_base_pose_calc(const Scene *scene, copy_v3_fl(r_base_pose->position, 0.0f); axis_angle_to_quat_single(r_base_pose->orientation_quat, 'X', M_PI_2); } + + *r_base_scale = settings->base_scale; } static void wm_xr_session_draw_data_populate(wmXrData *xr_data, @@ -213,7 +225,8 @@ static void wm_xr_session_draw_data_populate(wmXrData *xr_data, r_draw_data->xr_data = xr_data; r_draw_data->surface_data = g_xr_surface->customdata; - wm_xr_session_base_pose_calc(r_draw_data->scene, settings, &r_draw_data->base_pose); + wm_xr_session_base_pose_calc( + r_draw_data->scene, settings, &r_draw_data->base_pose, &r_draw_data->base_scale); } wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm, @@ -291,7 +304,7 @@ static wmXrSessionStateEvent wm_xr_session_state_to_event(const wmXrSessionState return SESSION_STATE_EVENT_NONE; } -void wm_xr_session_draw_data_update(const wmXrSessionState *state, +void wm_xr_session_draw_data_update(wmXrSessionState *state, const XrSessionSettings *settings, const GHOST_XrDrawViewInfo *draw_view, wmXrDrawData *draw_data) @@ -319,6 +332,8 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state, else { copy_v3_fl(draw_data->eye_position_ofs, 0.0f); } + /* Reset navigation. */ + WM_xr_session_state_navigation_reset(state); break; case SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE: if (use_position_tracking) { @@ -345,32 +360,36 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state, void wm_xr_session_state_update(const XrSessionSettings *settings, const wmXrDrawData *draw_data, const GHOST_XrDrawViewInfo *draw_view, + const float viewmat[4][4], wmXrSessionState *state) { GHOST_XrPose viewer_pose; - const bool use_position_tracking = settings->flag & XR_SESSION_USE_POSITION_TRACKING; - const bool use_absolute_tracking = settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING; + float viewer_mat[4][4], base_mat[4][4], nav_mat[4][4]; - mul_qt_qtqt(viewer_pose.orientation_quat, - draw_data->base_pose.orientation_quat, - draw_view->local_pose.orientation_quat); - copy_v3_v3(viewer_pose.position, draw_data->base_pose.position); - /* The local pose and the eye pose (which is copied from an earlier local pose) both are view - * space, so Y-up. In this case we need them in regular Z-up. */ - if (use_position_tracking) { - viewer_pose.position[0] += draw_view->local_pose.position[0]; - viewer_pose.position[1] -= draw_view->local_pose.position[2]; - viewer_pose.position[2] += draw_view->local_pose.position[1]; + /* Calculate viewer matrix. */ + copy_qt_qt(viewer_pose.orientation_quat, draw_view->local_pose.orientation_quat); + if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { + zero_v3(viewer_pose.position); } - if (!use_absolute_tracking) { - viewer_pose.position[0] -= draw_data->eye_position_ofs[0]; - viewer_pose.position[1] += draw_data->eye_position_ofs[2]; - viewer_pose.position[2] -= draw_data->eye_position_ofs[1]; + else { + copy_v3_v3(viewer_pose.position, draw_view->local_pose.position); } + if ((settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) { + sub_v3_v3(viewer_pose.position, draw_data->eye_position_ofs); + } + wm_xr_pose_to_mat(&viewer_pose, viewer_mat); + + /* Apply base pose and navigation. */ + wm_xr_pose_scale_to_mat(&draw_data->base_pose, draw_data->base_scale, base_mat); + wm_xr_pose_scale_to_mat(&state->nav_pose_prev, state->nav_scale_prev, nav_mat); + mul_m4_m4m4(state->viewer_mat_base, base_mat, viewer_mat); + mul_m4_m4m4(viewer_mat, nav_mat, state->viewer_mat_base); + + /* Save final viewer pose and viewmat. */ + mat4_to_loc_quat(state->viewer_pose.position, state->viewer_pose.orientation_quat, viewer_mat); + wm_xr_pose_scale_to_imat( + &state->viewer_pose, draw_data->base_scale * state->nav_scale_prev, state->viewer_viewmat); - copy_v3_v3(state->viewer_pose.position, viewer_pose.position); - copy_qt_qt(state->viewer_pose.orientation_quat, viewer_pose.orientation_quat); - wm_xr_pose_to_imat(&viewer_pose, state->viewer_viewmat); /* No idea why, but multiplying by two seems to make it match the VR view more. */ state->focal_len = 2.0f * fov_to_focallength(draw_view->fov.angle_right - draw_view->fov.angle_left, @@ -378,7 +397,10 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs); memcpy(&state->prev_base_pose, &draw_data->base_pose, sizeof(state->prev_base_pose)); + state->prev_base_scale = draw_data->base_scale; memcpy(&state->prev_local_pose, &draw_view->local_pose, sizeof(state->prev_local_pose)); + copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs); + state->prev_settings_flag = settings->flag; state->prev_base_pose_type = settings->base_pose_type; state->prev_base_pose_object = settings->base_pose_object; @@ -503,6 +525,74 @@ bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, return true; } +bool WM_xr_session_state_nav_location_get(const wmXrData *xr, float r_location[3]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + zero_v3(r_location); + return false; + } + + copy_v3_v3(r_location, xr->runtime->session_state.nav_pose.position); + return true; +} + +void WM_xr_session_state_nav_location_set(wmXrData *xr, const float location[3]) +{ + if (WM_xr_session_exists(xr)) { + copy_v3_v3(xr->runtime->session_state.nav_pose.position, location); + xr->runtime->session_state.is_navigation_dirty = true; + } +} + +bool WM_xr_session_state_nav_rotation_get(const wmXrData *xr, float r_rotation[4]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + unit_qt(r_rotation); + return false; + } + + copy_qt_qt(r_rotation, xr->runtime->session_state.nav_pose.orientation_quat); + return true; +} + +void WM_xr_session_state_nav_rotation_set(wmXrData *xr, const float rotation[4]) +{ + if (WM_xr_session_exists(xr)) { + BLI_ASSERT_UNIT_QUAT(rotation); + copy_qt_qt(xr->runtime->session_state.nav_pose.orientation_quat, rotation); + xr->runtime->session_state.is_navigation_dirty = true; + } +} + +bool WM_xr_session_state_nav_scale_get(const wmXrData *xr, float *r_scale) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + *r_scale = 1.0f; + return false; + } + + *r_scale = xr->runtime->session_state.nav_scale; + return true; +} + +void WM_xr_session_state_nav_scale_set(wmXrData *xr, float scale) +{ + if (WM_xr_session_exists(xr)) { + /* Clamp to reasonable values. */ + CLAMP(scale, xr->session_settings.clip_start, xr->session_settings.clip_end); + xr->runtime->session_state.nav_scale = scale; + xr->runtime->session_state.is_navigation_dirty = true; + } +} + +void WM_xr_session_state_navigation_reset(wmXrSessionState *state) +{ + zero_v3(state->nav_pose.position); + unit_qt(state->nav_pose.orientation_quat); + state->nav_scale = 1.0f; + state->is_navigation_dirty = true; +} + /* -------------------------------------------------------------------- */ /** \name XR-Session Actions * @@ -522,16 +612,21 @@ void wm_xr_session_actions_init(wmXrData *xr) static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose, const float view_ofs[3], const float base_mat[4][4], + const float nav_mat[4][4], GHOST_XrPose *r_pose, - float r_mat[4][4]) + float r_mat[4][4], + float r_mat_base[4][4]) { float m[4][4]; /* Calculate controller matrix in world space. */ wm_xr_pose_to_mat(raw_pose, m); - /* Apply eye position and base pose offsets. */ + /* Apply eye position offset. */ sub_v3_v3(m[3], view_ofs); - mul_m4_m4m4(r_mat, base_mat, m); + + /* Apply base pose and navigation. */ + mul_m4_m4m4(r_mat_base, base_mat, m); + mul_m4_m4m4(r_mat, nav_mat, r_mat_base); /* Save final pose. */ mat4_to_loc_quat(r_pose->position, r_pose->orientation_quat, r_mat); @@ -547,7 +642,7 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin BLI_assert(grip_action->count_subaction_paths == BLI_listbase_count(&state->controllers)); unsigned int subaction_idx = 0; - float view_ofs[3], base_mat[4][4]; + float view_ofs[3], base_mat[4][4], nav_mat[4][4]; if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { copy_v3_v3(view_ofs, state->prev_local_pose.position); @@ -559,19 +654,24 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin add_v3_v3(view_ofs, state->prev_eye_position_ofs); } - wm_xr_pose_to_mat(&state->prev_base_pose, base_mat); + wm_xr_pose_scale_to_mat(&state->prev_base_pose, state->prev_base_scale, base_mat); + wm_xr_pose_scale_to_mat(&state->nav_pose, state->nav_scale, nav_mat); LISTBASE_FOREACH_INDEX (wmXrController *, controller, &state->controllers, subaction_idx) { wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)grip_action->states)[subaction_idx], view_ofs, base_mat, + nav_mat, &controller->grip_pose, - controller->grip_mat); + controller->grip_mat, + controller->grip_mat_base); wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)aim_action->states)[subaction_idx], view_ofs, base_mat, + nav_mat, &controller->aim_pose, - controller->aim_mat); + controller->aim_mat, + controller->aim_mat_base); if (!controller->model) { /* Notify GHOST to load/continue loading the controller model data. This can be called more @@ -1094,10 +1194,26 @@ void wm_xr_session_actions_update(wmWindowManager *wm) return; } + XrSessionSettings *settings = &xr->session_settings; GHOST_XrContextHandle xr_context = xr->runtime->context; wmXrSessionState *state = &xr->runtime->session_state; wmXrActionSet *active_action_set = state->active_action_set; + if (state->is_navigation_dirty) { + memcpy(&state->nav_pose_prev, &state->nav_pose, sizeof(state->nav_pose_prev)); + state->nav_scale_prev = state->nav_scale; + state->is_navigation_dirty = false; + + /* Update viewer pose with any navigation changes since the last actions sync so that data + * is correct for queries. */ + float m[4][4], viewer_mat[4][4]; + wm_xr_pose_scale_to_mat(&state->nav_pose, state->nav_scale, m); + mul_m4_m4m4(viewer_mat, m, state->viewer_mat_base); + mat4_to_loc_quat(state->viewer_pose.position, state->viewer_pose.orientation_quat, viewer_mat); + wm_xr_pose_scale_to_imat( + &state->viewer_pose, settings->base_scale * state->nav_scale, state->viewer_viewmat); + } + int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL); if (!ret) { return; @@ -1108,7 +1224,7 @@ void wm_xr_session_actions_update(wmWindowManager *wm) wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, xr->runtime); if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) { - wm_xr_session_controller_data_update(&xr->session_settings, + wm_xr_session_controller_data_update(settings, active_action_set->controller_grip_action, active_action_set->controller_aim_action, xr_context, diff --git a/source/blender/windowmanager/xr/wm_xr.h b/source/blender/windowmanager/xr/wm_xr.h index 0f0fbe8bc00..caba6038c56 100644 --- a/source/blender/windowmanager/xr/wm_xr.h +++ b/source/blender/windowmanager/xr/wm_xr.h @@ -30,3 +30,6 @@ bool wm_xr_init(wmWindowManager *wm); void wm_xr_exit(wmWindowManager *wm); void wm_xr_session_toggle(wmWindowManager *wm, wmWindow *win, wmXrSessionExitFn session_exit_fn); bool wm_xr_events_handle(wmWindowManager *wm); + +/* wm_xr_operators.c */ +void wm_xr_operatortypes_register(void); From 7ae2810848338033d9e71834a2cea294025cb214 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 26 Oct 2021 13:33:21 +0900 Subject: [PATCH 1172/1500] XR: View adjustments for variable viewer scale This adjusts some calculations and visibility flags for XR viewports in order to account for a possible scale factor in the XR view matrix. This scale factor can be introduced via the XR session settings base scale, which allows a viewer to begin their session at a specific reference scale, or the XR session state navigation scale, which allows a viewer to adjust their scale relative to the reference scale during the session. Reviewed by Severin as part of D11501, but requested to be committed separately. --- source/blender/draw/engines/overlay/overlay_grid.c | 9 +++++++++ source/blender/draw/intern/draw_manager_data.c | 8 +++++--- source/blender/editors/space_view3d/view3d_draw.c | 2 ++ source/blender/editors/space_view3d/view3d_view.c | 7 ++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c index 31c8ed9d664..4a551c4dec5 100644 --- a/source/blender/draw/engines/overlay/overlay_grid.c +++ b/source/blender/draw/engines/overlay/overlay_grid.c @@ -200,6 +200,15 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) shd->grid_distance = dist / 2.0f; ED_view3d_grid_steps(scene, v3d, rv3d, shd->grid_steps); + + if ((v3d->flag & (V3D_XR_SESSION_SURFACE | V3D_XR_SESSION_MIRROR)) != 0) { + /* The calculations for the grid parameters assume that the view matrix has no scale component, + * which may not be correct if the user is "shrunk" or "enlarged" by zooming in or out. + * Therefore, we need to compensate the values here. */ + float viewinvscale = len_v3( + viewinv[0]); /* Assumption is uniform scaling (all column vectors are of same length). */ + shd->grid_distance *= viewinvscale; + } } void OVERLAY_grid_cache_init(OVERLAY_Data *vedata) diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index f96bd474aec..5d3e3db866f 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1684,10 +1684,12 @@ static void draw_frustum_bound_sphere_calc(const BoundBox *bbox, bsphere->center[0] = farcenter[0] * z / e; bsphere->center[1] = farcenter[1] * z / e; bsphere->center[2] = z; - bsphere->radius = len_v3v3(bsphere->center, farpoint); - /* Transform to world space. */ - mul_m4_v3(viewinv, bsphere->center); + /* For XR, the view matrix may contain a scale factor. Then, transforming only the center + * into world space after calculating the radius will result in incorrect behavior. */ + mul_m4_v3(viewinv, bsphere->center); /* Transform to world space. */ + mul_m4_v3(viewinv, farpoint); + bsphere->radius = len_v3v3(bsphere->center, farpoint); } } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index fe347e89600..fceb6553cab 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -347,6 +347,8 @@ static void view3d_xr_mirror_setup(const wmWindowManager *wm, (wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) != 0, V3D_XR_SHOW_CUSTOM_OVERLAYS); + /* Hide navigation gizmo since it gets distorted if the view matrix has a scale factor. */ + v3d->gizmo_flag |= V3D_GIZMO_HIDE_NAVIGATE; /* Reset overridden View3D data. */ v3d->lens = lens_old; diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index f5da7c14a88..46a664f10fa 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -1730,7 +1730,12 @@ void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const S if (v3d->runtime.flag & V3D_RUNTIME_XR_SESSION_ROOT) { View3DShading *xr_shading = &wm->xr.session_settings.shading; /* Flags that shouldn't be overridden by the 3D View shading. */ - const int flag_copy = V3D_SHADING_WORLD_ORIENTATION; + int flag_copy = 0; + if (v3d->shading.type != + OB_SOLID) { /* Don't set V3D_SHADING_WORLD_ORIENTATION for solid shading since it results + in distorted lighting when the view matrix has a scale factor. */ + flag_copy |= V3D_SHADING_WORLD_ORIENTATION; + } BLI_assert(WM_xr_session_exists(&wm->xr)); From 4c0512bc3221f5319cfdcd25e77c45e08ad61249 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 26 Oct 2021 13:37:31 +0900 Subject: [PATCH 1173/1500] XR: Versioning for session draw flags, base scale --- source/blender/blenloader/intern/versioning_300.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 7341797bb65..044659e4fbe 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2071,6 +2071,15 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) { + LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) { + wm->xr.session_settings.base_scale = 1.0f; + wm->xr.session_settings.draw_flags |= (V3D_OFSDRAW_SHOW_SELECTION | + V3D_OFSDRAW_XR_SHOW_CONTROLLERS | + V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS); + } + } + /** * Versioning code until next subversion bump goes here. * From 9db13c8d793e4b11ba717e9e4124b1faae66fe8a Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 26 Oct 2021 13:38:59 +0900 Subject: [PATCH 1174/1500] XR: Fix potential crash when toggling session --- source/blender/windowmanager/xr/intern/wm_xr_session.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 62757c0bddd..98cc4e21953 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -137,8 +137,10 @@ void wm_xr_session_toggle(wmWindowManager *wm, wmXrData *xr_data = &wm->xr; if (WM_xr_session_exists(xr_data)) { - GHOST_XrSessionEnd(xr_data->runtime->context); + /* Must set first, since #GHOST_XrSessionEnd() may immediately free the runtime. */ xr_data->runtime->session_state.is_started = false; + + GHOST_XrSessionEnd(xr_data->runtime->context); } else { GHOST_XrSessionBeginInfo begin_info; From 9dbfa05c44fa691603ebb15f442094639d010827 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 26 Oct 2021 13:41:02 +0900 Subject: [PATCH 1175/1500] XR: Only show ref space warnings in debug-xr mode This avoids spamming the console for users who have not set up a tracking space/boundary for their headsets. --- intern/ghost/intern/GHOST_XrSession.cpp | 26 ++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index 808f3a26be7..64aa2f515c4 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -127,7 +127,9 @@ void GHOST_XrSession::initSystem() /** \name State Management * \{ */ -static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose &base_pose) +static void create_reference_spaces(OpenXRSessionData &oxr, + const GHOST_XrPose &base_pose, + bool isDebugMode) { XrReferenceSpaceCreateInfo create_info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; create_info.poseInReferenceSpace.orientation.w = 1.0f; @@ -163,10 +165,11 @@ static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose & * since runtimes are not required to support the stage reference space. If the runtime * doesn't support it then just fall back to the local space. */ if (result == XR_ERROR_REFERENCE_SPACE_UNSUPPORTED) { - printf( - "Warning: XR runtime does not support stage reference space, falling back to local " - "reference space.\n"); - + if (isDebugMode) { + printf( + "Warning: XR runtime does not support stage reference space, falling back to local " + "reference space.\n"); + } create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space), "Failed to create local reference space."); @@ -182,11 +185,12 @@ static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose & CHECK_XR(xrGetReferenceSpaceBoundsRect(oxr.session, XR_REFERENCE_SPACE_TYPE_STAGE, &extents), "Failed to get stage reference space bounds."); if (extents.width == 0.0f || extents.height == 0.0f) { - printf( - "Warning: Invalid stage reference space bounds, falling back to local reference space. " - "To use the stage reference space, please define a tracking space via the XR " - "runtime.\n"); - + if (isDebugMode) { + printf( + "Warning: Invalid stage reference space bounds, falling back to local reference " + "space. To use the stage reference space, please define a tracking space via the XR " + "runtime.\n"); + } /* Fallback to local space. */ if (oxr.reference_space != XR_NULL_HANDLE) { CHECK_XR(xrDestroySpace(oxr.reference_space), "Failed to destroy stage reference space."); @@ -255,7 +259,7 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info) "detailed error information to the command line."); prepareDrawing(); - create_reference_spaces(*m_oxr, begin_info->base_pose); + create_reference_spaces(*m_oxr, begin_info->base_pose, m_context->isDebugMode()); /* Create and bind actions here. */ m_context->getCustomFuncs().session_create_fn(); From 89637f40df337db587709818cf548554f75b4239 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 26 Oct 2021 13:42:09 +0900 Subject: [PATCH 1176/1500] Cleanup: Improve description for XR absolute tracking --- source/blender/makesrna/intern/rna_xr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 0f6544e6d47..dd4cbeac174 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -1747,7 +1747,9 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) "rna_XrSessionSettings_use_absolute_tracking_get", "rna_XrSessionSettings_use_absolute_tracking_set"); RNA_def_property_ui_text( - prop, "Absolute Tracking", "Use unadjusted location/rotation as defined by the XR runtime"); + prop, + "Absolute Tracking", + "Allow the VR tracking origin to be defined independently of the headset location"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); } From 5f9b00a07ebdec1a95837063e781b37d8534b11e Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 26 Oct 2021 15:05:25 +0900 Subject: [PATCH 1177/1500] Cleanup: Remove unused parameter --- source/blender/windowmanager/xr/intern/wm_xr_draw.c | 2 +- source/blender/windowmanager/xr/intern/wm_xr_intern.h | 1 - source/blender/windowmanager/xr/intern/wm_xr_session.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index 9829e13c677..72d88bb3043 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -161,7 +161,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) wm_xr_session_draw_data_update(session_state, settings, draw_view, draw_data); wm_xr_draw_matrices_create(draw_data, draw_view, settings, session_state, viewmat, winmat); - wm_xr_session_state_update(settings, draw_data, draw_view, viewmat, session_state); + wm_xr_session_state_update(settings, draw_data, draw_view, session_state); if (!wm_xr_session_surface_offscreen_ensure(surface_data, draw_view)) { return; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 6df70dc37aa..7de1f254224 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -216,7 +216,6 @@ void wm_xr_session_draw_data_update(wmXrSessionState *state, void wm_xr_session_state_update(const XrSessionSettings *settings, const wmXrDrawData *draw_data, const GHOST_XrDrawViewInfo *draw_view, - const float viewmat[4][4], wmXrSessionState *state); bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, const GHOST_XrDrawViewInfo *draw_view); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 98cc4e21953..3224869b04a 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -362,7 +362,6 @@ void wm_xr_session_draw_data_update(wmXrSessionState *state, void wm_xr_session_state_update(const XrSessionSettings *settings, const wmXrDrawData *draw_data, const GHOST_XrDrawViewInfo *draw_view, - const float viewmat[4][4], wmXrSessionState *state) { GHOST_XrPose viewer_pose; From ee743204b0b7fddd4a3cf315f2996467ca786500 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 26 Oct 2021 09:52:08 +0200 Subject: [PATCH 1178/1500] Cleanup: build warnings. `NULL` instead of `nullptr` in cpp code, and `else` statements after returns. --- source/blender/blenkernel/intern/icons.cc | 2 +- .../windowmanager/xr/intern/wm_xr_operators.c | 92 +++++++++---------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index c48f3934a19..f820b345c59 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -363,7 +363,7 @@ PreviewImage **BKE_previewimg_id_get_p(const ID *id) Object *ob = (Object *)id; /* Currently, only object types with real geometry can be rendered as preview. */ if (!OB_TYPE_IS_GEOMETRY(ob->type)) { - return NULL; + return nullptr; } return &ob->preview; } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_operators.c b/source/blender/windowmanager/xr/intern/wm_xr_operators.c index 652e357b6d5..308abc4fca4 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_operators.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c @@ -449,10 +449,8 @@ static bool wm_xr_navigation_grab_is_locked(const XrGrabData *data, const bool b if (bimanual) { return data->loc_lock && data->rot_lock && data->scale_lock; } - else { - /* Ignore scale lock, as one-handed interaction cannot change navigation scale. */ - return data->loc_lock && data->rot_lock; - } + /* Ignore scale lock, as one-handed interaction cannot change navigation scale. */ + return data->loc_lock && data->rot_lock; } static void wm_xr_navigation_grab_apply(wmXrData *xr, @@ -558,17 +556,17 @@ static int wm_xr_navigation_grab_modal(bContext *C, wmOperator *op, const wmEven dispatching (see #wm_xr_session_action_states_interpret()). For modal XR operators, modal handling starts when an input is "pressed" (action state exceeds the action threshold) and ends when the input is "released" (state falls below the threshold). */ - if (event->val == KM_PRESS) { - return OPERATOR_RUNNING_MODAL; + switch (event->val) { + case KM_PRESS: + return OPERATOR_RUNNING_MODAL; + case KM_RELEASE: + wm_xr_grab_uninit(op); + return OPERATOR_FINISHED; + default: + BLI_assert_unreachable(); + wm_xr_grab_uninit(op); + return OPERATOR_CANCELLED; } - else if (event->val == KM_RELEASE) { - wm_xr_grab_uninit(op); - return OPERATOR_FINISHED; - } - - BLI_assert_unreachable(); - wm_xr_grab_uninit(op); - return OPERATOR_CANCELLED; } static void WM_OT_xr_navigation_grab(wmOperatorType *ot) @@ -1317,39 +1315,41 @@ static int wm_xr_navigation_teleport_modal(bContext *C, wmOperator *op, const wm wm_xr_raycast_update(op, xr, actiondata); - if (event->val == KM_PRESS) { - return OPERATOR_RUNNING_MODAL; + switch (event->val) { + case KM_PRESS: + return OPERATOR_RUNNING_MODAL; + case KM_RELEASE: { + XrRaycastData *data = op->customdata; + bool selectable_only, teleport_axes[3]; + float teleport_t, teleport_ofs, ray_dist; + + RNA_boolean_get_array(op->ptr, "teleport_axes", teleport_axes); + teleport_t = RNA_float_get(op->ptr, "interpolation"); + teleport_ofs = RNA_float_get(op->ptr, "offset"); + selectable_only = RNA_boolean_get(op->ptr, "selectable_only"); + ray_dist = RNA_float_get(op->ptr, "distance"); + + wm_xr_navigation_teleport(C, + xr, + data->origin, + data->direction, + &ray_dist, + selectable_only, + teleport_axes, + teleport_t, + teleport_ofs); + + wm_xr_raycast_uninit(op); + + return OPERATOR_FINISHED; + } + default: + + /* XR events currently only support press and release. */ + BLI_assert_unreachable(); + wm_xr_raycast_uninit(op); + return OPERATOR_CANCELLED; } - else if (event->val == KM_RELEASE) { - XrRaycastData *data = op->customdata; - bool selectable_only, teleport_axes[3]; - float teleport_t, teleport_ofs, ray_dist; - - RNA_boolean_get_array(op->ptr, "teleport_axes", teleport_axes); - teleport_t = RNA_float_get(op->ptr, "interpolation"); - teleport_ofs = RNA_float_get(op->ptr, "offset"); - selectable_only = RNA_boolean_get(op->ptr, "selectable_only"); - ray_dist = RNA_float_get(op->ptr, "distance"); - - wm_xr_navigation_teleport(C, - xr, - data->origin, - data->direction, - &ray_dist, - selectable_only, - teleport_axes, - teleport_t, - teleport_ofs); - - wm_xr_raycast_uninit(op); - - return OPERATOR_FINISHED; - } - - /* XR events currently only support press and release. */ - BLI_assert_unreachable(); - wm_xr_raycast_uninit(op); - return OPERATOR_CANCELLED; } static void WM_OT_xr_navigation_teleport(wmOperatorType *ot) From 7bc7d1747c96e4f4159a91b8ceffd112168c6907 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 26 Oct 2021 09:58:03 +0200 Subject: [PATCH 1179/1500] Cleanup: forgot to remove empty line in previous commit. --- source/blender/windowmanager/xr/intern/wm_xr_operators.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/windowmanager/xr/intern/wm_xr_operators.c b/source/blender/windowmanager/xr/intern/wm_xr_operators.c index 308abc4fca4..105c3b29cc8 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_operators.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c @@ -1344,7 +1344,6 @@ static int wm_xr_navigation_teleport_modal(bContext *C, wmOperator *op, const wm return OPERATOR_FINISHED; } default: - /* XR events currently only support press and release. */ BLI_assert_unreachable(); wm_xr_raycast_uninit(op); From 9ba22bd1f77a4b168ce19e941fe84837fc69f701 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 26 Oct 2021 10:13:44 +0200 Subject: [PATCH 1180/1500] Fix crash in liboverride/pointcache handling code after recent changes. In some cases code would try to access NULL pointer. Reported by @dfelinto, thanks. --- source/blender/blenkernel/intern/object.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 45dfb9af074..7103f0a4db6 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1159,10 +1159,14 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src) for (pid_dst = pidlist_dst.first, pid_src = pidlist_src.first; pid_dst != NULL; pid_dst = pid_dst->next, pid_src = (pid_src != NULL) ? pid_src->next : NULL) { /* If pid's do not match, just tag info of caches in dst as dirty and continue. */ - if (pid_src == NULL || pid_dst->type != pid_src->type || - pid_dst->file_type != pid_src->file_type || - pid_dst->default_step != pid_src->default_step || pid_dst->max_step != pid_src->max_step || - pid_dst->data_types != pid_src->data_types || pid_dst->info_types != pid_src->info_types) { + if (pid_src == NULL) { + continue; + } + else if (pid_dst->type != pid_src->type || pid_dst->file_type != pid_src->file_type || + pid_dst->default_step != pid_src->default_step || + pid_dst->max_step != pid_src->max_step || + pid_dst->data_types != pid_src->data_types || + pid_dst->info_types != pid_src->info_types) { LISTBASE_FOREACH (PointCache *, point_cache_src, pid_src->ptcaches) { point_cache_src->flag |= PTCACHE_FLAG_INFO_DIRTY; } From fe68b54edbeb1cb567aa4c20783a9579ab191863 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 26 Oct 2021 10:28:52 +0200 Subject: [PATCH 1181/1500] Cleanup: `else` after `continue`. --- source/blender/blenkernel/intern/object.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 7103f0a4db6..80150e79eb3 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1162,11 +1162,9 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src) if (pid_src == NULL) { continue; } - else if (pid_dst->type != pid_src->type || pid_dst->file_type != pid_src->file_type || - pid_dst->default_step != pid_src->default_step || - pid_dst->max_step != pid_src->max_step || - pid_dst->data_types != pid_src->data_types || - pid_dst->info_types != pid_src->info_types) { + if (pid_dst->type != pid_src->type || pid_dst->file_type != pid_src->file_type || + pid_dst->default_step != pid_src->default_step || pid_dst->max_step != pid_src->max_step || + pid_dst->data_types != pid_src->data_types || pid_dst->info_types != pid_src->info_types) { LISTBASE_FOREACH (PointCache *, point_cache_src, pid_src->ptcaches) { point_cache_src->flag |= PTCACHE_FLAG_INFO_DIRTY; } From f11ed418e5faeb31aa6d581c320a2893a396878c Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 26 Oct 2021 10:40:36 +0200 Subject: [PATCH 1182/1500] Cleanup: LibQuery: Rename `BKE_LIB_FOREACHID_PROCESS` to `BKE_LIB_FOREACHID_PROCESS_IDSUPER`. More in-line name with the rest of that macro-based API, especially since this will be extended in the future. --- source/blender/blenkernel/BKE_lib_query.h | 2 +- source/blender/blenkernel/intern/action.c | 2 +- source/blender/blenkernel/intern/anim_data.c | 4 +- source/blender/blenkernel/intern/brush.c | 8 ++-- source/blender/blenkernel/intern/camera.c | 6 +-- source/blender/blenkernel/intern/collection.c | 6 +-- source/blender/blenkernel/intern/curve.c | 18 ++++----- source/blender/blenkernel/intern/fcurve.c | 2 +- source/blender/blenkernel/intern/gpencil.c | 4 +- source/blender/blenkernel/intern/hair.c | 2 +- source/blender/blenkernel/intern/lattice.c | 2 +- source/blender/blenkernel/intern/lib_query.c | 2 +- source/blender/blenkernel/intern/library.c | 2 +- source/blender/blenkernel/intern/lightprobe.c | 4 +- source/blender/blenkernel/intern/linestyle.c | 6 +-- source/blender/blenkernel/intern/material.c | 6 +-- source/blender/blenkernel/intern/mball.c | 2 +- source/blender/blenkernel/intern/mesh.c | 6 +-- source/blender/blenkernel/intern/movieclip.c | 8 ++-- source/blender/blenkernel/intern/nla.c | 2 +- source/blender/blenkernel/intern/node.cc | 12 +++--- source/blender/blenkernel/intern/object.c | 34 +++++++++-------- source/blender/blenkernel/intern/particle.c | 24 ++++++------ .../blender/blenkernel/intern/pointcloud.cc | 2 +- source/blender/blenkernel/intern/scene.c | 28 +++++++------- source/blender/blenkernel/intern/screen.c | 38 +++++++++---------- source/blender/blenkernel/intern/speaker.c | 2 +- source/blender/blenkernel/intern/texture.c | 6 +-- source/blender/blenkernel/intern/volume.cc | 2 +- source/blender/blenkernel/intern/workspace.c | 2 +- source/blender/windowmanager/intern/wm.c | 2 +- 31 files changed, 125 insertions(+), 121 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index 9c49514e7b8..957a623577e 100644 --- a/source/blender/blenkernel/BKE_lib_query.h +++ b/source/blender/blenkernel/BKE_lib_query.h @@ -160,7 +160,7 @@ int BKE_lib_query_foreachid_process_callback_flag_override(struct LibraryForeach } \ ((void)0) -#define BKE_LIB_FOREACHID_PROCESS(_data, _id_super, _cb_flag) \ +#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag) \ { \ CHECK_TYPE(&((_id_super)->id), ID *); \ if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag))) { \ diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 65900ec0f4b..7403b7f2109 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -179,7 +179,7 @@ static void action_foreach_id(ID *id, LibraryForeachIDData *data) } LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { - BKE_LIB_FOREACHID_PROCESS(data, marker->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, marker->camera, IDWALK_CB_NOP); } } diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 7e4ab754500..23d2c4fe55f 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -297,8 +297,8 @@ void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data) BKE_fcurve_foreach_id(fcu, data); } - BKE_LIB_FOREACHID_PROCESS(data, adt->action, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, adt->tmpact, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->action, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->tmpact, IDWALK_CB_USER); LISTBASE_FOREACH (NlaTrack *, nla_track, &adt->nla_tracks) { LISTBASE_FOREACH (NlaStrip *, nla_strip, &nla_track->strips) { diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 9facb146361..7d217d6907d 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -207,11 +207,11 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data) { Brush *brush = (Brush *)id; - BKE_LIB_FOREACHID_PROCESS(data, brush->toggle_brush, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, brush->clone.image, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, brush->paint_curve, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->toggle_brush, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->clone.image, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->paint_curve, IDWALK_CB_USER); if (brush->gpencil_settings) { - BKE_LIB_FOREACHID_PROCESS(data, brush->gpencil_settings->material, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material, IDWALK_CB_USER); } BKE_texture_mtex_foreach_id(data, &brush->mtex); BKE_texture_mtex_foreach_id(data, &brush->mask_mtex); diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index ed1f6fcb40a..9455eed7f3f 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -103,13 +103,13 @@ static void camera_foreach_id(ID *id, LibraryForeachIDData *data) { Camera *camera = (Camera *)id; - BKE_LIB_FOREACHID_PROCESS(data, camera->dof.focus_object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, camera->dof.focus_object, IDWALK_CB_NOP); LISTBASE_FOREACH (CameraBGImage *, bgpic, &camera->bg_images) { if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) { - BKE_LIB_FOREACHID_PROCESS(data, bgpic->ima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, bgpic->ima, IDWALK_CB_USER); } else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) { - BKE_LIB_FOREACHID_PROCESS(data, bgpic->clip, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, bgpic->clip, IDWALK_CB_USER); } } } diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 8e50b9e9534..17122200295 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -158,10 +158,10 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data) Collection *collection = (Collection *)id; LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - BKE_LIB_FOREACHID_PROCESS(data, cob->ob, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, cob->ob, IDWALK_CB_USER); } LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - BKE_LIB_FOREACHID_PROCESS(data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); } LISTBASE_FOREACH (CollectionParent *, parent, &collection->parents) { /* XXX This is very weak. The whole idea of keeping pointers to private IDs is very bad @@ -170,7 +170,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data) (parent->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ? IDWALK_CB_EMBEDDED : IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK | cb_flag); } } diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 620110f7881..aae9ac383a4 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -130,17 +130,17 @@ static void curve_free_data(ID *id) static void curve_foreach_id(ID *id, LibraryForeachIDData *data) { Curve *curve = (Curve *)id; - BKE_LIB_FOREACHID_PROCESS(data, curve->bevobj, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, curve->taperobj, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, curve->textoncurve, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, curve->key, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->bevobj, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->taperobj, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->textoncurve, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->key, IDWALK_CB_USER); for (int i = 0; i < curve->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, curve->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->mat[i], IDWALK_CB_USER); } - BKE_LIB_FOREACHID_PROCESS(data, curve->vfont, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, curve->vfontb, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, curve->vfonti, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, curve->vfontbi, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfont, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfontb, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfonti, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfontbi, IDWALK_CB_USER); } static void curve_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 8e9c504dcbf..1564eb3aa7b 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -203,7 +203,7 @@ void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data) switch (fcm->type) { case FMODIFIER_TYPE_PYTHON: { FMod_Python *fcm_py = (FMod_Python *)fcm->data; - BKE_LIB_FOREACHID_PROCESS(data, fcm_py->script, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fcm_py->script, IDWALK_CB_NOP); IDP_foreach_property(fcm_py->prop, IDP_TYPE_FILTER_ID, diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index fa0741d3a2e..bea65030c06 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -139,11 +139,11 @@ static void greasepencil_foreach_id(ID *id, LibraryForeachIDData *data) bGPdata *gpencil = (bGPdata *)id; /* materials */ for (int i = 0; i < gpencil->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, gpencil->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gpencil->mat[i], IDWALK_CB_USER); } LISTBASE_FOREACH (bGPDlayer *, gplayer, &gpencil->layers) { - BKE_LIB_FOREACHID_PROCESS(data, gplayer->parent, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gplayer->parent, IDWALK_CB_NOP); } } diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index cf346e9cac7..7433ee7ac29 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -107,7 +107,7 @@ static void hair_foreach_id(ID *id, LibraryForeachIDData *data) { Hair *hair = (Hair *)id; for (int i = 0; i < hair->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, hair->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, hair->mat[i], IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 9bca8172e64..a2da59bca58 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -131,7 +131,7 @@ static void lattice_free_data(ID *id) static void lattice_foreach_id(ID *id, LibraryForeachIDData *data) { Lattice *lattice = (Lattice *)id; - BKE_LIB_FOREACHID_PROCESS(data, lattice->key, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lattice->key, IDWALK_CB_USER); } static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 2ac92828cec..4165452801c 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -227,7 +227,7 @@ static void library_foreach_ID_link(Main *bmain, BKE_LIB_FOREACHID_PROCESS_ID(&data, check_id, cb_flag) #define CALLBACK_INVOKE(check_id_super, cb_flag) \ - BKE_LIB_FOREACHID_PROCESS(&data, check_id_super, cb_flag) + BKE_LIB_FOREACHID_PROCESS_IDSUPER(&data, check_id_super, cb_flag) for (; id != NULL; id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL) { data.self_id = id; diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 36958e36004..1dba353d8ce 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -57,7 +57,7 @@ static void library_free_data(ID *id) static void library_foreach_id(ID *id, LibraryForeachIDData *data) { Library *lib = (Library *)id; - BKE_LIB_FOREACHID_PROCESS(data, lib->parent, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lib->parent, IDWALK_CB_NEVER_SELF); } IDTypeInfo IDType_ID_LI = { diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 1f4abf36426..57ad6695db4 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -53,8 +53,8 @@ static void lightprobe_foreach_id(ID *id, LibraryForeachIDData *data) { LightProbe *probe = (LightProbe *)id; - BKE_LIB_FOREACHID_PROCESS(data, probe->image, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, probe->visibility_grp, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, probe->image, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, probe->visibility_grp, IDWALK_CB_NOP); } static void lightprobe_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index f4e4dd9f1ab..a1c93920731 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -168,7 +168,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) LineStyleColorModifier_DistanceFromObject *p = (LineStyleColorModifier_DistanceFromObject *) lsm; if (p->target) { - BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP); } } } @@ -177,7 +177,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) LineStyleAlphaModifier_DistanceFromObject *p = (LineStyleAlphaModifier_DistanceFromObject *) lsm; if (p->target) { - BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP); } } } @@ -186,7 +186,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) LineStyleThicknessModifier_DistanceFromObject *p = (LineStyleThicknessModifier_DistanceFromObject *)lsm; if (p->target) { - BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP); } } } diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 6c57d3139bb..d3b34639d8a 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -170,11 +170,11 @@ static void material_foreach_id(ID *id, LibraryForeachIDData *data) return; } if (material->texpaintslot != NULL) { - BKE_LIB_FOREACHID_PROCESS(data, material->texpaintslot->ima, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->texpaintslot->ima, IDWALK_CB_NOP); } if (material->gp_style != NULL) { - BKE_LIB_FOREACHID_PROCESS(data, material->gp_style->sima, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, material->gp_style->ima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->gp_style->sima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->gp_style->ima, IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 6c8664aefed..48d31361eac 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -112,7 +112,7 @@ static void metaball_foreach_id(ID *id, LibraryForeachIDData *data) { MetaBall *metaball = (MetaBall *)id; for (int i = 0; i < metaball->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, metaball->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, metaball->mat[i], IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index ed3766ad6a3..7277f7ad209 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -176,10 +176,10 @@ static void mesh_free_data(ID *id) static void mesh_foreach_id(ID *id, LibraryForeachIDData *data) { Mesh *mesh = (Mesh *)id; - BKE_LIB_FOREACHID_PROCESS(data, mesh->texcomesh, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, mesh->key, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->texcomesh, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->key, IDWALK_CB_USER); for (int i = 0; i < mesh->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, mesh->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->mat[i], IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 0c2ac841b87..002f370cdcb 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -132,19 +132,19 @@ static void movie_clip_foreach_id(ID *id, LibraryForeachIDData *data) MovieClip *movie_clip = (MovieClip *)id; MovieTracking *tracking = &movie_clip->tracking; - BKE_LIB_FOREACHID_PROCESS(data, movie_clip->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, movie_clip->gpd, IDWALK_CB_USER); LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking->tracks) { - BKE_LIB_FOREACHID_PROCESS(data, track->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, track->gpd, IDWALK_CB_USER); } LISTBASE_FOREACH (MovieTrackingObject *, object, &tracking->objects) { LISTBASE_FOREACH (MovieTrackingTrack *, track, &object->tracks) { - BKE_LIB_FOREACHID_PROCESS(data, track->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, track->gpd, IDWALK_CB_USER); } } LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking->plane_tracks) { - BKE_LIB_FOREACHID_PROCESS(data, plane_track->image, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, plane_track->image, IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 487e925df79..ef84afd8668 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -488,7 +488,7 @@ NlaStrip *BKE_nla_add_soundstrip(Main *bmain, Scene *scene, Speaker *speaker) */ void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data) { - BKE_LIB_FOREACHID_PROCESS(data, strip->act, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, strip->act, IDWALK_CB_USER); LISTBASE_FOREACH (FCurve *, fcu, &strip->fcurves) { BKE_fcurve_foreach_id(fcu, data); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 0452481849b..fb2110d7e53 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -313,28 +313,28 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { bNodeSocketValueObject *default_value = (bNodeSocketValueObject *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_IMAGE: { bNodeSocketValueImage *default_value = (bNodeSocketValueImage *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_COLLECTION: { bNodeSocketValueCollection *default_value = (bNodeSocketValueCollection *) sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_TEXTURE: { bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_MATERIAL: { bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_FLOAT: @@ -355,7 +355,7 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data) { bNodeTree *ntree = (bNodeTree *)id; - BKE_LIB_FOREACHID_PROCESS(data, ntree->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ntree->gpd, IDWALK_CB_USER); LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { BKE_LIB_FOREACHID_PROCESS_ID(data, node->id, IDWALK_CB_USER); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 80150e79eb3..a55bb32f927 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -453,11 +453,11 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) } } - BKE_LIB_FOREACHID_PROCESS(data, object->parent, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, object->track, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF); /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ - BKE_LIB_FOREACHID_PROCESS(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, object->proxy_group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy_group, IDWALK_CB_NOP); /* Special case! * Since this field is set/owned by 'user' of this ID (and not ID itself), @@ -469,22 +469,23 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) IDWALK_CB_INDIRECT_USAGE : 0, true); - BKE_LIB_FOREACHID_PROCESS(data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); } - BKE_LIB_FOREACHID_PROCESS(data, object->poselib, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->poselib, IDWALK_CB_USER); for (int i = 0; i < object->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER); } /* Note that ob->gpd is deprecated, so no need to handle it here. */ - BKE_LIB_FOREACHID_PROCESS(data, object->instance_collection, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->instance_collection, IDWALK_CB_USER); if (object->pd) { - BKE_LIB_FOREACHID_PROCESS(data, object->pd->tex, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, object->pd->f_source, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->f_source, IDWALK_CB_NOP); } /* Note that ob->effect is deprecated, so no need to handle it here. */ @@ -494,15 +495,17 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { IDP_foreach_property( pchan->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); - BKE_LIB_FOREACHID_PROCESS(data, pchan->custom, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pchan->custom, IDWALK_CB_USER); BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, data); } BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); } if (object->rigidbody_constraint) { - BKE_LIB_FOREACHID_PROCESS(data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); } BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data); @@ -516,10 +519,11 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) } if (object->soft) { - BKE_LIB_FOREACHID_PROCESS(data, object->soft->collision_group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->soft->collision_group, IDWALK_CB_NOP); if (object->soft->effector_weights) { - BKE_LIB_FOREACHID_PROCESS(data, object->soft->effector_weights->group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->soft->effector_weights->group, IDWALK_CB_NOP); } } } diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 7b2a1af7086..feb997a4c5d 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -173,10 +173,10 @@ static void particle_settings_free_data(ID *id) static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data) { ParticleSettings *psett = (ParticleSettings *)id; - BKE_LIB_FOREACHID_PROCESS(data, psett->instance_collection, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, psett->instance_object, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, psett->bb_ob, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, psett->collision_group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->instance_collection, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->instance_object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->bb_ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->collision_group, IDWALK_CB_NOP); for (int i = 0; i < MAX_MTEX; i++) { if (psett->mtex[i]) { @@ -185,16 +185,16 @@ static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data) } if (psett->effector_weights) { - BKE_LIB_FOREACHID_PROCESS(data, psett->effector_weights->group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->effector_weights->group, IDWALK_CB_NOP); } if (psett->pd) { - BKE_LIB_FOREACHID_PROCESS(data, psett->pd->tex, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, psett->pd->f_source, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd->f_source, IDWALK_CB_NOP); } if (psett->pd2) { - BKE_LIB_FOREACHID_PROCESS(data, psett->pd2->tex, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, psett->pd2->f_source, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd2->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd2->f_source, IDWALK_CB_NOP); } if (psett->boids) { @@ -202,18 +202,18 @@ static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data) LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { if (rule->type == eBoidRuleType_Avoid) { BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule; - BKE_LIB_FOREACHID_PROCESS(data, gabr->ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gabr->ob, IDWALK_CB_NOP); } else if (rule->type == eBoidRuleType_FollowLeader) { BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule; - BKE_LIB_FOREACHID_PROCESS(data, flbr->ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, flbr->ob, IDWALK_CB_NOP); } } } } LISTBASE_FOREACH (ParticleDupliWeight *, dw, &psett->instance_weights) { - BKE_LIB_FOREACHID_PROCESS(data, dw->ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, dw->ob, IDWALK_CB_NOP); } } diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 1db14dc3dc8..15c5a809118 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -105,7 +105,7 @@ static void pointcloud_foreach_id(ID *id, LibraryForeachIDData *data) { PointCloud *pointcloud = (PointCloud *)id; for (int i = 0; i < pointcloud->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, pointcloud->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pointcloud->mat[i], IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 2cb0213a192..8059cd45f05 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -530,7 +530,7 @@ static void scene_foreach_toolsettings_id_pointer_process( (ID **)&(__id), __action, __reader, (ID **)&(__id_old), __cb_flag); \ } \ else { \ - BKE_LIB_FOREACHID_PROCESS(__data, __id, __cb_flag); \ + BKE_LIB_FOREACHID_PROCESS_IDSUPER(__data, __id, __cb_flag); \ } \ } \ (void)0 @@ -695,7 +695,7 @@ static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase (lc->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ? IDWALK_CB_EMBEDDED : IDWALK_CB_NOP; - BKE_LIB_FOREACHID_PROCESS(data, lc->collection, cb_flag); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lc->collection, cb_flag); scene_foreach_layer_collection(data, &lc->layer_collections); } } @@ -738,12 +738,12 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) { Scene *scene = (Scene *)id; - BKE_LIB_FOREACHID_PROCESS(data, scene->camera, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, scene->world, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, scene->set, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, scene->clip, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, scene->gpd, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, scene->r.bake.cage_object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->world, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->set, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->clip, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->r.bake.cage_object, IDWALK_CB_NOP); if (scene->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ BKE_library_foreach_ID_embedded(data, (ID **)&scene->nodetree); @@ -758,10 +758,10 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) } LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { - BKE_LIB_FOREACHID_PROCESS(data, view_layer->mat_override, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, view_layer->mat_override, IDWALK_CB_USER); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - BKE_LIB_FOREACHID_PROCESS( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, base->object, IDWALK_CB_NOP | IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE); } @@ -769,23 +769,23 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) { if (fmc->script) { - BKE_LIB_FOREACHID_PROCESS(data, fmc->script, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fmc->script, IDWALK_CB_NOP); } } LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) { if (fls->group) { - BKE_LIB_FOREACHID_PROCESS(data, fls->group, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fls->group, IDWALK_CB_USER); } if (fls->linestyle) { - BKE_LIB_FOREACHID_PROCESS(data, fls->linestyle, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fls->linestyle, IDWALK_CB_USER); } } } LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) { - BKE_LIB_FOREACHID_PROCESS(data, marker->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, marker->camera, IDWALK_CB_NOP); IDP_foreach_property( marker->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 0474c2b81cb..df826dff602 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -93,13 +93,13 @@ static void screen_foreach_id_dopesheet(LibraryForeachIDData *data, bDopeSheet * { if (ads != NULL) { BKE_LIB_FOREACHID_PROCESS_ID(data, ads->source, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, ads->filter_grp, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ads->filter_grp, IDWALK_CB_NOP); } } void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area) { - BKE_LIB_FOREACHID_PROCESS(data, area->full, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, area->full, IDWALK_CB_NOP); /* TODO: this should be moved to a callback in `SpaceType`, defined in each editor's own code. * Will be for a later round of cleanup though... */ @@ -108,11 +108,11 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area case SPACE_VIEW3D: { View3D *v3d = (View3D *)sl; - BKE_LIB_FOREACHID_PROCESS(data, v3d->camera, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, v3d->ob_center, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->ob_center, IDWALK_CB_NOP); if (v3d->localvd) { - BKE_LIB_FOREACHID_PROCESS(data, v3d->localvd->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->localvd->camera, IDWALK_CB_NOP); } break; } @@ -134,21 +134,21 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area SpaceAction *saction = (SpaceAction *)sl; screen_foreach_id_dopesheet(data, &saction->ads); - BKE_LIB_FOREACHID_PROCESS(data, saction->action, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, saction->action, IDWALK_CB_NOP); break; } case SPACE_IMAGE: { SpaceImage *sima = (SpaceImage *)sl; - BKE_LIB_FOREACHID_PROCESS(data, sima->image, IDWALK_CB_USER_ONE); - BKE_LIB_FOREACHID_PROCESS(data, sima->mask_info.mask, IDWALK_CB_USER_ONE); - BKE_LIB_FOREACHID_PROCESS(data, sima->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->image, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->mask_info.mask, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->gpd, IDWALK_CB_USER); break; } case SPACE_SEQ: { SpaceSeq *sseq = (SpaceSeq *)sl; - BKE_LIB_FOREACHID_PROCESS(data, sseq->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sseq->gpd, IDWALK_CB_USER); break; } case SPACE_NLA: { @@ -160,13 +160,13 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area case SPACE_TEXT: { SpaceText *st = (SpaceText *)sl; - BKE_LIB_FOREACHID_PROCESS(data, st->text, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, st->text, IDWALK_CB_NOP); break; } case SPACE_SCRIPT: { SpaceScript *scpt = (SpaceScript *)sl; - BKE_LIB_FOREACHID_PROCESS(data, scpt->script, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scpt->script, IDWALK_CB_NOP); break; } case SPACE_OUTLINER: { @@ -194,19 +194,19 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area BKE_LIB_FOREACHID_PROCESS_ID(data, snode->id, IDWALK_CB_NOP); BKE_LIB_FOREACHID_PROCESS_ID(data, snode->from, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, snode->nodetree, is_private_nodetree ? IDWALK_CB_EMBEDDED : IDWALK_CB_USER_ONE); LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) { if (path == snode->treepath.first) { /* first nodetree in path is same as snode->nodetree */ - BKE_LIB_FOREACHID_PROCESS(data, + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, path->nodetree, is_private_nodetree ? IDWALK_CB_EMBEDDED : IDWALK_CB_USER_ONE); } else { - BKE_LIB_FOREACHID_PROCESS(data, path->nodetree, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, path->nodetree, IDWALK_CB_USER_ONE); } if (path->nodetree == NULL) { @@ -214,14 +214,14 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } } - BKE_LIB_FOREACHID_PROCESS(data, snode->edittree, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_NOP); break; } case SPACE_CLIP: { SpaceClip *sclip = (SpaceClip *)sl; - BKE_LIB_FOREACHID_PROCESS(data, sclip->clip, IDWALK_CB_USER_ONE); - BKE_LIB_FOREACHID_PROCESS(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->clip, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE); break; } case SPACE_SPREADSHEET: { @@ -229,7 +229,7 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { if (context->type == SPREADSHEET_CONTEXT_OBJECT) { - BKE_LIB_FOREACHID_PROCESS( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, ((SpreadsheetContextObject *)context)->object, IDWALK_CB_NOP); } } diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index b361f31cc30..230ff9d6da0 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -50,7 +50,7 @@ static void speaker_foreach_id(ID *id, LibraryForeachIDData *data) { Speaker *speaker = (Speaker *)id; - BKE_LIB_FOREACHID_PROCESS(data, speaker->sound, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, speaker->sound, IDWALK_CB_USER); } static void speaker_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index d5f7647f07a..74e8db039f2 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -144,7 +144,7 @@ static void texture_foreach_id(ID *id, LibraryForeachIDData *data) /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ BKE_library_foreach_ID_embedded(data, (ID **)&texture->nodetree); } - BKE_LIB_FOREACHID_PROCESS(data, texture->ima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, texture->ima, IDWALK_CB_USER); } static void texture_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -233,8 +233,8 @@ IDTypeInfo IDType_ID_TE = { /* Utils for all IDs using those texture slots. */ void BKE_texture_mtex_foreach_id(LibraryForeachIDData *data, MTex *mtex) { - BKE_LIB_FOREACHID_PROCESS(data, mtex->object, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, mtex->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mtex->object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mtex->tex, IDWALK_CB_USER); } /* ****************** Mapping ******************* */ diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 0b9ef5c537d..7e7a40d8e9b 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -556,7 +556,7 @@ static void volume_foreach_id(ID *id, LibraryForeachIDData *data) { Volume *volume = (Volume *)id; for (int i = 0; i < volume->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, volume->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, volume->mat[i], IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 3c168a6c7b2..6269cfc4349 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -82,7 +82,7 @@ static void workspace_foreach_id(ID *id, LibraryForeachIDData *data) WorkSpace *workspace = (WorkSpace *)id; LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { - BKE_LIB_FOREACHID_PROCESS(data, layout->screen, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, IDWALK_CB_USER); } } diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 0b7d5e5f1f4..4458b386ab6 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -86,7 +86,7 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data) wmWindowManager *wm = (wmWindowManager *)id; LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - BKE_LIB_FOREACHID_PROCESS(data, win->scene, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, win->scene, IDWALK_CB_USER_ONE); /* This pointer can be NULL during old files reading, better be safe than sorry. */ if (win->workspace_hook != NULL) { From fee2cedb33ccf33f72d0370892dd25faa884968b Mon Sep 17 00:00:00 2001 From: YimingWu Date: Tue, 26 Oct 2021 17:09:17 +0800 Subject: [PATCH 1183/1500] GPencil: Fix(unreported) Dash modifier wrong logic. When the modifier iterates to an empty layer with no frame it will return, while the correct logic is to continue. --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- source/blender/gpencil_modifiers/intern/MOD_gpencildash.c | 2 +- source/tools | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/release/datafiles/locale b/release/datafiles/locale index 8ee2942570f..80d9e7ee122 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 8ee2942570f08d10484bb2328d0d1b0aaaa0367c +Subproject commit 80d9e7ee122c626cbbcd1da554683bce79f8d3df diff --git a/release/scripts/addons b/release/scripts/addons index c49c16c38d4..27fe7f3a4f9 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit c49c16c38d4bce29a1e0518d5fad5d90f42ab5e7 +Subproject commit 27fe7f3a4f964b53af436c4da4ddea337eff0c7e diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 16467648282..42da56aa737 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 16467648282500cc229c271f62201ef897f2c2c3 +Subproject commit 42da56aa73726710107031787af5eea186797984 diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c index ba33edd6a94..33cc3094a36 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -255,7 +255,7 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec BKE_gpencil_frame_active_set(depsgraph, gpd); bGPDframe *gpf = gpl->actframe; if (gpf == NULL) { - return; + continue; } apply_dash_for_frame(ob, gpl, gpd, gpf, (DashGpencilModifierData *)md); } diff --git a/source/tools b/source/tools index 2e8c8792488..7c5acb95df9 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 2e8c879248822c8e500ed49d79acc605e5aa75b9 +Subproject commit 7c5acb95df918503d11cfc43172ce13901019289 From 5bfe09df2244cb9de0b6554a378eecef77b1e75d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 26 Oct 2021 11:25:32 +0200 Subject: [PATCH 1184/1500] Geometry Nodes: support viewing field values in spreadsheet The viewer node has been expanded to have a field input next to the geometry input. When both are connected (by ctrl+shift clicking on a node) the spreadsheet will show the evaluated field on the geometry. The operator to link to the viewer has become a bit smarter. It automatically detects if it should link to the geometry or field input. In the future some more smartness could be added, such as automatically relinking the "right" geometry when viewing a field. Internally, there are two major changes: * Refactor of what happens when ctrl+shift clicking on a node to link to a viewer. The behavior of the geometry nodes viewer is a bit more complex than that of the compositor viewers. The behavior in compositing nodes should not have changed. Any change should be reported as a bug (and then we can decide if it's worse than before or if it needs fixing). * Evaluation, display and caching of fields in the spreadsheet editor. Differential Revision: https://developer.blender.org/D12938 --- .../blenloader/intern/versioning_300.c | 17 + .../editors/space_node/node_relationships.cc | 392 ++++++++++++------ .../editors/space_spreadsheet/CMakeLists.txt | 2 + .../space_spreadsheet/space_spreadsheet.cc | 66 ++- .../space_spreadsheet/spreadsheet_cache.cc | 79 ++++ .../space_spreadsheet/spreadsheet_cache.hh | 78 ++++ .../spreadsheet_column_values.hh | 5 - .../spreadsheet_data_source.hh | 6 +- .../spreadsheet_data_source_geometry.cc | 299 ++++++++++--- .../spreadsheet_data_source_geometry.hh | 38 +- .../space_spreadsheet/spreadsheet_intern.hh | 22 +- .../space_spreadsheet/spreadsheet_layout.cc | 4 +- source/blender/makesdna/DNA_node_types.h | 5 + source/blender/makesrna/intern/rna_nodetree.c | 14 + source/blender/nodes/NOD_static_types.h | 2 +- .../nodes/geometry/nodes/node_geo_viewer.cc | 61 +++ 16 files changed, 860 insertions(+), 230 deletions(-) create mode 100644 source/blender/editors/space_spreadsheet/spreadsheet_cache.cc create mode 100644 source/blender/editors/space_spreadsheet/spreadsheet_cache.hh diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 044659e4fbe..57447db8723 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2100,5 +2100,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) version_geometry_nodes_set_position_node_offset(ntree); } /* Keep this block, even when empty. */ + + /* Add storage to viewer node. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_VIEWER) { + if (node->storage == NULL) { + NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN( + sizeof(NodeGeometryViewer), __func__); + data->data_type = CD_PROP_FLOAT; + node->storage = data; + } + } + } + } } } diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index b69e7e98bca..459608a67ea 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -57,8 +57,12 @@ #include "BLT_translation.h" +#include "NOD_node_tree_ref.hh" + #include "node_intern.h" /* own include */ +using namespace blender::nodes::node_tree_ref_types; + /* -------------------------------------------------------------------- */ /** \name Relations Helpers * \{ */ @@ -612,160 +616,274 @@ static void snode_autoconnect(Main *bmain, /** \name Link Viewer Operator * \{ */ -static int node_link_viewer(const bContext *C, bNode *tonode) +namespace blender::ed::nodes::viewer_linking { + +/* Depending on the node tree type, different socket types are supported by viewer nodes. */ +static bool socket_can_be_viewed(const OutputSocketRef &socket) +{ + if (nodeSocketIsHidden(socket.bsocket())) { + return false; + } + if (socket.idname() == "NodeSocketVirtual") { + return false; + } + if (socket.tree().btree()->type != NTREE_GEOMETRY) { + return true; + } + return ELEM(socket.typeinfo()->type, + SOCK_GEOMETRY, + SOCK_FLOAT, + SOCK_VECTOR, + SOCK_INT, + SOCK_BOOLEAN, + SOCK_RGBA); +} + +static CustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type) +{ + switch (socket_type) { + case SOCK_FLOAT: + return CD_PROP_FLOAT; + case SOCK_INT: + return CD_PROP_INT32; + case SOCK_VECTOR: + return CD_PROP_FLOAT3; + case SOCK_BOOLEAN: + return CD_PROP_BOOL; + case SOCK_RGBA: + return CD_PROP_COLOR; + default: + /* Fallback. */ + return CD_AUTO_FROM_NAME; + } +} + +/** + * Find the socket to link to in a viewer node. + */ +static bNodeSocket *node_link_viewer_get_socket(bNodeTree *ntree, + bNode *viewer_node, + bNodeSocket *src_socket) +{ + if (viewer_node->type != GEO_NODE_VIEWER) { + /* In viewer nodes in the compositor, only the first input should be linked to. */ + return (bNodeSocket *)viewer_node->inputs.first; + } + /* For the geometry nodes viewer, find the socket with the correct type. */ + LISTBASE_FOREACH (bNodeSocket *, viewer_socket, &viewer_node->inputs) { + if (viewer_socket->type == src_socket->type) { + if (viewer_socket->type == SOCK_GEOMETRY) { + return viewer_socket; + } + NodeGeometryViewer *storage = (NodeGeometryViewer *)viewer_node->storage; + const CustomDataType data_type = socket_type_to_custom_data_type( + (eNodeSocketDatatype)src_socket->type); + BLI_assert(data_type != CD_AUTO_FROM_NAME); + storage->data_type = data_type; + nodeUpdate(ntree, viewer_node); + return viewer_socket; + } + } + return nullptr; +} + +static bool is_viewer_node(const NodeRef &node) +{ + return ELEM(node.bnode()->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER); +} + +static Vector find_viewer_nodes(const NodeTreeRef &tree) +{ + Vector viewer_nodes; + for (const NodeRef *node : tree.nodes()) { + if (is_viewer_node(*node)) { + viewer_nodes.append(node); + } + } + return viewer_nodes; +} + +static bool is_viewer_socket_in_viewer(const InputSocketRef &socket) +{ + const NodeRef &node = socket.node(); + BLI_assert(is_viewer_node(node)); + if (node.typeinfo()->type == GEO_NODE_VIEWER) { + return true; + } + return socket.index() == 0; +} + +static bool is_linked_to_viewer(const OutputSocketRef &socket, const NodeRef &viewer_node) +{ + for (const InputSocketRef *target_socket : socket.directly_linked_sockets()) { + if (&target_socket->node() != &viewer_node) { + continue; + } + if (!target_socket->is_available()) { + continue; + } + if (is_viewer_socket_in_viewer(*target_socket)) { + return true; + } + } + return false; +} + +static int get_default_viewer_type(const bContext *C) { SpaceNode *snode = CTX_wm_space_node(C); + return ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER; +} - /* context check */ - if (tonode == nullptr || BLI_listbase_is_empty(&tonode->outputs)) { - return OPERATOR_CANCELLED; +static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &viewer_node) +{ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) { + if (link->tonode == &viewer_node) { + if (link->tosock->flag & SOCK_UNAVAIL) { + nodeRemLink(&btree, link); + } + } } - if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) { +} + +static const NodeRef *get_existing_viewer(const NodeTreeRef &tree) +{ + Vector viewer_nodes = find_viewer_nodes(tree); + + /* Check if there is already an active viewer node that should be used. */ + for (const NodeRef *viewer_node : viewer_nodes) { + if (viewer_node->bnode()->flag & NODE_DO_OUTPUT) { + return viewer_node; + } + } + + /* If no active but non-active viewers exist, make one active. */ + if (!viewer_nodes.is_empty()) { + viewer_nodes[0]->bnode()->flag |= NODE_DO_OUTPUT; + return viewer_nodes[0]; + } + return nullptr; +} + +static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *active_viewer_node, + const NodeRef &node_to_view) +{ + const OutputSocketRef *last_socket_linked_to_viewer = nullptr; + if (active_viewer_node != nullptr) { + for (const OutputSocketRef *output_socket : node_to_view.outputs()) { + if (!socket_can_be_viewed(*output_socket)) { + continue; + } + if (is_linked_to_viewer(*output_socket, *active_viewer_node)) { + last_socket_linked_to_viewer = output_socket; + } + } + } + if (last_socket_linked_to_viewer == nullptr) { + /* If no output is connected to a viewer, use the first output that can be viewed. */ + for (const OutputSocketRef *output_socket : node_to_view.outputs()) { + if (socket_can_be_viewed(*output_socket)) { + return output_socket; + } + } + } + else { + /* Pick the next socket to be linked to the viewer. */ + const int tot_outputs = node_to_view.outputs().size(); + for (const int offset : IndexRange(1, tot_outputs - 1)) { + const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs; + const OutputSocketRef &output_socket = node_to_view.output(index); + if (!socket_can_be_viewed(output_socket)) { + continue; + } + if (is_linked_to_viewer(output_socket, *active_viewer_node)) { + continue; + } + return &output_socket; + } + } + return nullptr; +} + +static int link_socket_to_viewer(const bContext *C, + bNode *viewer_bnode, + bNode *bnode_to_view, + bNodeSocket *bsocket_to_view) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *btree = snode->edittree; + + if (viewer_bnode == nullptr) { + /* Create a new viewer node if none exists. */ + const int viewer_type = get_default_viewer_type(C); + viewer_bnode = node_add_node( + C, nullptr, viewer_type, bsocket_to_view->locx + 100, bsocket_to_view->locy); + if (viewer_bnode == nullptr) { + return OPERATOR_CANCELLED; + } + } + + bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, viewer_bnode, bsocket_to_view); + if (viewer_bsocket == nullptr) { return OPERATOR_CANCELLED; } - /* get viewer */ - bNode *viewer_node = nullptr; - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) { - if (node->flag & NODE_DO_OUTPUT) { - viewer_node = node; - break; - } - } - } - /* no viewer, we make one active */ - if (viewer_node == nullptr) { - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) { - node->flag |= NODE_DO_OUTPUT; - viewer_node = node; - break; - } + bNodeLink *link_to_change = nullptr; + LISTBASE_FOREACH (bNodeLink *, link, &btree->links) { + if (link->tosock == viewer_bsocket) { + link_to_change = link; + break; } } - bNodeSocket *sock = nullptr; - bNodeLink *link = nullptr; - - /* try to find an already connected socket to cycle to the next */ - if (viewer_node) { - link = nullptr; - - for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { - if (link->tonode == viewer_node && link->fromnode == tonode) { - if (link->tosock == viewer_node->inputs.first) { - break; - } - } - } - if (link) { - /* unlink existing connection */ - sock = link->fromsock; - nodeRemLink(snode->edittree, link); - - /* find a socket after the previously connected socket */ - if (ED_node_is_geometry(snode)) { - /* Geometry nodes viewer only supports geometry sockets for now. */ - for (sock = sock->next; sock; sock = sock->next) { - if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) { - break; - } - } - } - else { - for (sock = sock->next; sock; sock = sock->next) { - if (!nodeSocketIsHidden(sock)) { - break; - } - } - } - } + if (link_to_change == nullptr) { + nodeAddLink(btree, bnode_to_view, bsocket_to_view, viewer_bnode, viewer_bsocket); + } + else { + link_to_change->fromnode = bnode_to_view; + link_to_change->fromsock = bsocket_to_view; + btree->update |= NTREE_UPDATE_LINKS; } - if (tonode) { - /* Find a selected socket that overrides the socket to connect to */ - if (ED_node_is_geometry(snode)) { - /* Geometry nodes viewer only supports geometry sockets for now. */ - LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { - if (sock2->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { - sock = sock2; - break; - } - } - } - else { - LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { - if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { - sock = sock2; - break; - } - } - } + remove_links_to_unavailable_viewer_sockets(*btree, *viewer_bnode); + + if (btree->type == NTREE_GEOMETRY) { + ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_bnode); } - /* find a socket starting from the first socket */ - if (!sock) { - if (ED_node_is_geometry(snode)) { - /* Geometry nodes viewer only supports geometry sockets for now. */ - for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { - if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) { - break; - } - } - } - else { - for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { - if (!nodeSocketIsHidden(sock)) { - break; - } - } - } - } - - if (sock) { - /* add a new viewer if none exists yet */ - if (!viewer_node) { - /* XXX location is a quick hack, just place it next to the linked socket */ - const int viewer_type = ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER; - viewer_node = node_add_node(C, nullptr, viewer_type, sock->locx + 100, sock->locy); - if (!viewer_node) { - return OPERATOR_CANCELLED; - } - - link = nullptr; - } - else { - /* get link to viewer */ - for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { - if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) { - break; - } - } - } - - if (link == nullptr) { - nodeAddLink( - snode->edittree, tonode, sock, viewer_node, (bNodeSocket *)viewer_node->inputs.first); - } - else { - link->fromnode = tonode; - link->fromsock = sock; - /* make sure the dependency sorting is updated */ - snode->edittree->update |= NTREE_UPDATE_LINKS; - } - if (ED_node_is_geometry(snode)) { - ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_node); - } - - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - snode_update(snode, viewer_node); - DEG_id_tag_update(&snode->edittree->id, 0); - } + ntreeUpdateTree(CTX_data_main(C), btree); + snode_update(snode, viewer_bnode); + DEG_id_tag_update(&btree->id, 0); return OPERATOR_FINISHED; } +static int node_link_viewer(const bContext *C, bNode *bnode_to_view) +{ + if (bnode_to_view == nullptr) { + return OPERATOR_CANCELLED; + } + + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *btree = snode->edittree; + + const NodeTreeRef tree{btree}; + const NodeRef &node_to_view = *tree.find_node(*bnode_to_view); + const NodeRef *active_viewer_node = get_existing_viewer(tree); + + const OutputSocketRef *socket_to_view = find_output_socket_to_be_viewed(active_viewer_node, + node_to_view); + if (socket_to_view == nullptr) { + return OPERATOR_FINISHED; + } + + bNodeSocket *bsocket_to_view = socket_to_view->bsocket(); + bNode *viewer_bnode = active_viewer_node ? active_viewer_node->bnode() : nullptr; + return link_socket_to_viewer(C, viewer_bnode, bnode_to_view, bsocket_to_view); +} + +} // namespace blender::ed::nodes::viewer_linking + static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceNode *snode = CTX_wm_space_node(C); @@ -777,7 +895,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - if (node_link_viewer(C, node) == OPERATOR_CANCELLED) { + if (blender::ed::nodes::viewer_linking::node_link_viewer(C, node) == OPERATOR_CANCELLED) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt index e903feeec1b..91fe1bc01b7 100644 --- a/source/blender/editors/space_spreadsheet/CMakeLists.txt +++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt @@ -35,6 +35,7 @@ set(INC set(SRC space_spreadsheet.cc + spreadsheet_cache.cc spreadsheet_column.cc spreadsheet_context.cc spreadsheet_data_source.cc @@ -47,6 +48,7 @@ set(SRC spreadsheet_row_filter.cc spreadsheet_row_filter_ui.cc + spreadsheet_cache.hh spreadsheet_cell_value.hh spreadsheet_column.hh spreadsheet_column_values.hh diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index a82648aeee0..73e0be76466 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -112,7 +112,7 @@ static void spreadsheet_free(SpaceLink *sl) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - MEM_SAFE_FREE(sspreadsheet->runtime); + delete sspreadsheet->runtime; LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) { spreadsheet_row_filter_free(row_filter); @@ -129,8 +129,7 @@ static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)area->spacedata.first; if (sspreadsheet->runtime == nullptr) { - sspreadsheet->runtime = (SpaceSpreadsheet_Runtime *)MEM_callocN( - sizeof(SpaceSpreadsheet_Runtime), __func__); + sspreadsheet->runtime = new SpaceSpreadsheet_Runtime(); } } @@ -138,7 +137,7 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) { const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl; SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old); - sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime); + sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime); BLI_listbase_clear(&sspreadsheet_new->row_filters); LISTBASE_FOREACH (const SpreadsheetRowFilter *, src_filter, &sspreadsheet_old->row_filters) { @@ -294,16 +293,39 @@ static std::unique_ptr get_data_source(const bContext *C) return {}; } -static float get_column_width(const ColumnValues &values) +static float get_default_column_width(const ColumnValues &values) { - if (values.default_width > 0) { + if (values.default_width > 0.0f) { return values.default_width; } + static const float float_width = 3; + switch (values.type()) { + case SPREADSHEET_VALUE_TYPE_BOOL: + return 2.0f; + case SPREADSHEET_VALUE_TYPE_INT32: + return float_width; + case SPREADSHEET_VALUE_TYPE_FLOAT: + return float_width; + case SPREADSHEET_VALUE_TYPE_FLOAT2: + return 2.0f * float_width; + case SPREADSHEET_VALUE_TYPE_FLOAT3: + return 3.0f * float_width; + case SPREADSHEET_VALUE_TYPE_COLOR: + return 4.0f * float_width; + case SPREADSHEET_VALUE_TYPE_INSTANCES: + return 8.0f; + } + return float_width; +} + +static float get_column_width(const ColumnValues &values) +{ + float data_width = get_default_column_width(values); const int fontid = UI_style_get()->widget.uifont_id; BLF_size(fontid, UI_DEFAULT_TEXT_POINTS, U.dpi); const StringRefNull name = values.name(); const float name_width = BLF_width(fontid, name.data(), name.size()); - return std::max(name_width / UI_UNIT_X + 1.0f, 3.0f); + return std::max(name_width / UI_UNIT_X + 1.0f, data_width); } static float get_column_width_in_pixels(const ColumnValues &values) @@ -339,21 +361,28 @@ static void update_visible_columns(ListBase &columns, DataSource &data_source) } } - data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) { - std::unique_ptr values = data_source.get_column_values(column_id); - if (values) { - if (used_ids.add(column_id)) { - SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id); - SpreadsheetColumn *new_column = spreadsheet_column_new(new_id); - BLI_addtail(&columns, new_column); - } - } - }); + data_source.foreach_default_column_ids( + [&](const SpreadsheetColumnID &column_id, const bool is_extra) { + std::unique_ptr values = data_source.get_column_values(column_id); + if (values) { + if (used_ids.add(column_id)) { + SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id); + SpreadsheetColumn *new_column = spreadsheet_column_new(new_id); + if (is_extra) { + BLI_addhead(&columns, new_column); + } + else { + BLI_addtail(&columns, new_column); + } + } + } + }); } static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + sspreadsheet->runtime->cache.set_all_unused(); spreadsheet_update_context_path(C); std::unique_ptr data_source = get_data_source(C); @@ -394,6 +423,9 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) ED_region_tag_redraw(footer); ARegion *sidebar = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_UI); ED_region_tag_redraw(sidebar); + + /* Free all cache items that have not been used. */ + sspreadsheet->runtime->cache.remove_all_unused(); } static void spreadsheet_main_region_listener(const wmRegionListenerParams *params) diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc new file mode 100644 index 00000000000..2a399e018b6 --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "spreadsheet_cache.hh" + +namespace blender::ed::spreadsheet { + +void SpreadsheetCache::add(std::unique_ptr key, std::unique_ptr value) +{ + key->is_used = true; + cache_map_.add_overwrite(*key, std::move(value)); + keys_.append(std::move(key)); +} + +SpreadsheetCache::Value *SpreadsheetCache::lookup(const Key &key) +{ + std::unique_ptr *value = cache_map_.lookup_ptr(key); + if (value == nullptr) { + return nullptr; + } + const Key &stored_cache_key = cache_map_.lookup_key(key); + stored_cache_key.is_used = true; + return value->get(); +} + +SpreadsheetCache::Value &SpreadsheetCache::lookup_or_add( + std::unique_ptr key, FunctionRef()> create_value) +{ + Value *value = this->lookup(*key); + if (value != nullptr) { + return *value; + } + std::unique_ptr new_value = create_value(); + value = new_value.get(); + this->add(std::move(key), std::move(new_value)); + return *value; +} + +void SpreadsheetCache::set_all_unused() +{ + for (std::unique_ptr &key : keys_) { + key->is_used = false; + } +} + +void SpreadsheetCache::remove_all_unused() +{ + /* First remove the keys from the map and free the values. */ + for (auto it = cache_map_.keys().begin(); it != cache_map_.keys().end(); ++it) { + const Key &key = *it; + if (!key.is_used) { + cache_map_.remove(it); + } + } + /* Then free the keys. */ + for (int i = 0; i < keys_.size();) { + if (keys_[i]->is_used) { + i++; + } + else { + keys_.remove_and_reorder(i); + } + } +} + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh new file mode 100644 index 00000000000..d370bdab5c1 --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include + +#include "BLI_function_ref.hh" +#include "BLI_map.hh" +#include "BLI_vector.hh" + +namespace blender::ed::spreadsheet { + +/** + * A generic cache for the spreadsheet. Different data sources can cache custom data using custom + * keys. + * + * Elements are removed from the cache when they are not used during a redraw. + */ +class SpreadsheetCache { + public: + class Key { + public: + virtual ~Key() = default; + + mutable bool is_used = false; + + virtual uint64_t hash() const = 0; + + friend bool operator==(const Key &a, const Key &b) + { + return a.is_equal_to(b); + } + + private: + virtual bool is_equal_to(const Key &other) const = 0; + }; + + class Value { + public: + virtual ~Value() = default; + }; + + private: + Vector> keys_; + Map, std::unique_ptr> cache_map_; + + public: + /* Adding or looking up a key tags it as being used, so that it won't be removed. */ + void add(std::unique_ptr key, std::unique_ptr value); + Value *lookup(const Key &key); + Value &lookup_or_add(std::unique_ptr key, + FunctionRef()> create_value); + + void set_all_unused(); + void remove_all_unused(); + + template T &lookup_or_add(std::unique_ptr key) + { + return dynamic_cast( + this->lookup_or_add(std::move(key), []() { return std::make_unique(); })); + } +}; + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh index 68370cf6a44..877651d6530 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh @@ -97,9 +97,4 @@ std::unique_ptr column_values_from_function(const eSpreadsheetColu return column_values; } -static constexpr float default_float_column_width = 3; -static constexpr float default_float2_column_width = 2 * default_float_column_width; -static constexpr float default_float3_column_width = 3 * default_float_column_width; -static constexpr float default_color_column_width = 4 * default_float_column_width; - } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh index 2ea7fb5809f..873735c81e5 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh @@ -36,8 +36,12 @@ class DataSource { * Calls the callback with all the column ids that should be displayed as long as the user does * not manually add or remove columns. The column id can be stack allocated. Therefore, the * callback should not keep a reference to it (and copy it instead). + * + * The `is_extra` argument indicates that this column is special and should be drawn as the first + * column. (This can be made a bit more generic in the future when necessary.) */ - virtual void foreach_default_column_ids(FunctionRef fn) const + virtual void foreach_default_column_ids( + FunctionRef fn) const { UNUSED_VARS(fn); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 78d9f61d8d5..136dc244d16 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -33,18 +33,99 @@ #include "NOD_geometry_nodes_eval_log.hh" +#include "FN_field_cpp_type.hh" + #include "bmesh.h" #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" namespace geo_log = blender::nodes::geometry_nodes_eval_log; +using blender::fn::GField; namespace blender::ed::spreadsheet { -void GeometryDataSource::foreach_default_column_ids( - FunctionRef fn) const +static std::optional cpp_type_to_column_value_type( + const fn::CPPType &type) { + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_BOOL; + } + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_INT32; + } + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_FLOAT; + } + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_FLOAT2; + } + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_FLOAT3; + } + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_COLOR; + } + return std::nullopt; +} + +void ExtraColumns::foreach_default_column_ids( + FunctionRef fn) const +{ + for (const auto &item : columns_.items()) { + SpreadsheetColumnID column_id; + column_id.name = (char *)item.key.c_str(); + fn(column_id, true); + } +} + +std::unique_ptr ExtraColumns::get_column_values( + const SpreadsheetColumnID &column_id) const +{ + const fn::GSpan *values = columns_.lookup_ptr(column_id.name); + if (values == nullptr) { + return {}; + } + eSpreadsheetColumnValueType column_type = *cpp_type_to_column_value_type(values->type()); + return column_values_from_function(column_type, + column_id.name, + values->size(), + [column_type, values](int index, CellValue &r_cell_value) { + const void *value = (*values)[index]; + switch (column_type) { + case SPREADSHEET_VALUE_TYPE_BOOL: + r_cell_value.value_bool = *(const bool *)value; + break; + case SPREADSHEET_VALUE_TYPE_INT32: + r_cell_value.value_int = *(const int *)value; + break; + case SPREADSHEET_VALUE_TYPE_FLOAT: + r_cell_value.value_float = *(const float *)value; + break; + case SPREADSHEET_VALUE_TYPE_FLOAT2: + r_cell_value.value_float2 = *(const float2 *)value; + break; + case SPREADSHEET_VALUE_TYPE_FLOAT3: + r_cell_value.value_float3 = *(const float3 *)value; + break; + case SPREADSHEET_VALUE_TYPE_COLOR: + r_cell_value.value_color = *( + const ColorGeometry4f *)value; + break; + case SPREADSHEET_VALUE_TYPE_INSTANCES: + break; + } + }); +} + +void GeometryDataSource::foreach_default_column_ids( + FunctionRef fn) const +{ + if (component_->attribute_domain_size(domain_) == 0) { + return; + } + + extra_columns_.foreach_default_column_ids(fn); component_->attribute_foreach( [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { if (meta_data.domain != domain_) { @@ -55,7 +136,7 @@ void GeometryDataSource::foreach_default_column_ids( } SpreadsheetColumnID column_id; column_id.name = (char *)attribute_id.name().data(); - fn(column_id); + fn(column_id, false); return true; }); } @@ -63,8 +144,17 @@ void GeometryDataSource::foreach_default_column_ids( std::unique_ptr GeometryDataSource::get_column_values( const SpreadsheetColumnID &column_id) const { + if (component_->attribute_domain_size(domain_) == 0) { + return {}; + } + std::lock_guard lock{mutex_}; + std::unique_ptr extra_column_values = extra_columns_.get_column_values(column_id); + if (extra_column_values) { + return extra_column_values; + } + bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name); if (!attribute) { return {}; @@ -104,40 +194,34 @@ std::unique_ptr GeometryDataSource::get_column_values( r_cell_value.value_bool = value; }); case CD_PROP_FLOAT2: { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT2, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - float2 value; - varray->get(index, &value); - r_cell_value.value_float2 = value; - }, - default_float2_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT2, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + float2 value; + varray->get(index, &value); + r_cell_value.value_float2 = value; + }); } case CD_PROP_FLOAT3: { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT3, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - float3 value; - varray->get(index, &value); - r_cell_value.value_float3 = value; - }, - default_float3_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + float3 value; + varray->get(index, &value); + r_cell_value.value_float3 = value; + }); } case CD_PROP_COLOR: { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_COLOR, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - ColorGeometry4f value; - varray->get(index, &value); - r_cell_value.value_color = value; - }, - default_color_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_COLOR, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + ColorGeometry4f value; + varray->get(index, &value); + r_cell_value.value_color = value; + }); } default: break; @@ -293,18 +377,20 @@ void GeometryDataSource::apply_selection_filter(MutableSpan rows_included) } void InstancesDataSource::foreach_default_column_ids( - FunctionRef fn) const + FunctionRef fn) const { if (component_->instances_amount() == 0) { return; } + extra_columns_.foreach_default_column_ids(fn); + SpreadsheetColumnID column_id; column_id.name = (char *)"Name"; - fn(column_id); + fn(column_id, false); for (const char *name : {"Position", "Rotation", "Scale", "ID"}) { column_id.name = (char *)name; - fn(column_id); + fn(column_id, false); } } @@ -315,6 +401,11 @@ std::unique_ptr InstancesDataSource::get_column_values( return {}; } + std::unique_ptr extra_column_values = extra_columns_.get_column_values(column_id); + if (extra_column_values) { + return extra_column_values; + } + const int size = this->tot_rows(); if (STREQ(column_id.name, "Name")) { Span reference_handles = component_->instance_reference_handles(); @@ -346,7 +437,6 @@ std::unique_ptr InstancesDataSource::get_column_values( } } }); - values->default_width = 8.0f; return values; } Span transforms = component_->instance_transforms(); @@ -357,28 +447,23 @@ std::unique_ptr InstancesDataSource::get_column_values( size, [transforms](int index, CellValue &r_cell_value) { r_cell_value.value_float3 = transforms[index].translation(); - }, - default_float3_column_width); + }); } if (STREQ(column_id.name, "Rotation")) { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT3, - column_id.name, - size, - [transforms](int index, CellValue &r_cell_value) { - r_cell_value.value_float3 = transforms[index].to_euler(); - }, - default_float3_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, + column_id.name, + size, + [transforms](int index, CellValue &r_cell_value) { + r_cell_value.value_float3 = transforms[index].to_euler(); + }); } if (STREQ(column_id.name, "Scale")) { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT3, - column_id.name, - size, - [transforms](int index, CellValue &r_cell_value) { - r_cell_value.value_float3 = transforms[index].scale(); - }, - default_float3_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, + column_id.name, + size, + [transforms](int index, CellValue &r_cell_value) { + r_cell_value.value_float3 = transforms[index].scale(); + }); } Span ids = component_->instance_ids(); if (STREQ(column_id.name, "ID")) { @@ -469,6 +554,38 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread return geometry_set; } +static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet, + Map &r_fields) +{ + if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { + return; + } + if (BLI_listbase_count(&sspreadsheet->context_path) <= 1) { + /* No viewer is currently referenced by the context path. */ + return; + } + const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_spreadsheet_editor_context( + *sspreadsheet); + if (node_log == nullptr) { + return; + } + for (const geo_log::SocketLog &socket_log : node_log->input_logs()) { + const geo_log::ValueLog *value_log = socket_log.value(); + if (value_log == nullptr) { + continue; + } + if (const geo_log::GenericValueLog *generic_value_log = + dynamic_cast(value_log)) { + const fn::GPointer value = generic_value_log->value(); + if (!dynamic_cast(value.type())) { + continue; + } + GField field = *(const GField *)value.get(); + r_fields.add("Viewer", std::move(field)); + } + } +} + static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); @@ -481,6 +598,69 @@ static GeometryComponentType get_display_component_type(const bContext *C, Objec return GEO_COMPONENT_TYPE_MESH; } +class GeometryComponentCacheKey : public SpreadsheetCache::Key { + public: + /* Use the pointer to the geometry component as a key to detect when the geometry changed. */ + const GeometryComponent *component; + + GeometryComponentCacheKey(const GeometryComponent &component) : component(&component) + { + } + + uint64_t hash() const override + { + return get_default_hash(this->component); + } + + bool is_equal_to(const Key &other) const override + { + if (const GeometryComponentCacheKey *other_geo = + dynamic_cast(&other)) { + return this->component == other_geo->component; + } + return false; + } +}; + +class GeometryComponentCacheValue : public SpreadsheetCache::Value { + public: + /* Stores the result of fields evaluated on a geometry component. Without this, fields would have + * to be reevaluated on every redraw. */ + Map, fn::GArray<>> arrays; +}; + +static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, + const GeometryComponent &component, + ExtraColumns &r_extra_columns) +{ + Map fields_to_show; + find_fields_to_evaluate(sspreadsheet, fields_to_show); + + GeometryComponentCacheValue &cache = + sspreadsheet->runtime->cache.lookup_or_add( + std::make_unique(component)); + + const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; + const int domain_size = component.attribute_domain_size(domain); + for (const auto &item : fields_to_show.items()) { + StringRef name = item.key; + const GField &field = item.value; + + /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ + fn::GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { + fn::GArray<> evaluated_array(field.cpp_type(), domain_size); + + bke::GeometryComponentFieldContext field_context{component, domain}; + fn::FieldEvaluator field_evaluator{field_context, domain_size}; + field_evaluator.add_with_destination(field, evaluated_array); + field_evaluator.evaluate(); + return evaluated_array; + }); + + r_extra_columns.add(std::move(name), evaluated_array.as_span()); + } +} + std::unique_ptr data_source_from_geometry(const bContext *C, Object *object_eval) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); @@ -493,10 +673,15 @@ std::unique_ptr data_source_from_geometry(const bContext *C, Object return {}; } + const GeometryComponent &component = *geometry_set.get_component_for_read(component_type); + ExtraColumns extra_columns; + add_fields_as_extra_columns(sspreadsheet, component, extra_columns); + if (component_type == GEO_COMPONENT_TYPE_INSTANCES) { - return std::make_unique(geometry_set); + return std::make_unique(geometry_set, std::move(extra_columns)); } - return std::make_unique(object_eval, geometry_set, component_type, domain); + return std::make_unique( + object_eval, geometry_set, component_type, domain, std::move(extra_columns)); } } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh index d1b5dc6845e..6c88a94f585 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -28,12 +28,34 @@ struct bContext; namespace blender::ed::spreadsheet { +/** + * Contains additional named columns that should be displayed that are not stored on the geometry + * directly. This is used for displaying the evaluated fields connected to a viewer node. + */ +class ExtraColumns { + private: + /** Maps column names to their data. The data is actually stored in the spreadsheet cache. */ + Map columns_; + + public: + void add(std::string name, fn::GSpan data) + { + columns_.add(std::move(name), data); + } + + void foreach_default_column_ids( + FunctionRef fn) const; + + std::unique_ptr get_column_values(const SpreadsheetColumnID &column_id) const; +}; + class GeometryDataSource : public DataSource { private: Object *object_eval_; const GeometrySet geometry_set_; const GeometryComponent *component_; AttributeDomain domain_; + ExtraColumns extra_columns_; /* Some data is computed on the fly only when it is requested. Computing it does not change the * logical state of this data source. Therefore, the corresponding methods are const and need to @@ -45,11 +67,13 @@ class GeometryDataSource : public DataSource { GeometryDataSource(Object *object_eval, GeometrySet geometry_set, const GeometryComponentType component_type, - const AttributeDomain domain) + const AttributeDomain domain, + ExtraColumns extra_columns) : object_eval_(object_eval), geometry_set_(std::move(geometry_set)), component_(geometry_set_.get_component_for_read(component_type)), - domain_(domain) + domain_(domain), + extra_columns_(std::move(extra_columns)) { } @@ -62,7 +86,7 @@ class GeometryDataSource : public DataSource { void apply_selection_filter(MutableSpan rows_included) const; void foreach_default_column_ids( - FunctionRef fn) const override; + FunctionRef fn) const override; std::unique_ptr get_column_values( const SpreadsheetColumnID &column_id) const override; @@ -73,16 +97,18 @@ class GeometryDataSource : public DataSource { class InstancesDataSource : public DataSource { const GeometrySet geometry_set_; const InstancesComponent *component_; + ExtraColumns extra_columns_; public: - InstancesDataSource(GeometrySet geometry_set) + InstancesDataSource(GeometrySet geometry_set, ExtraColumns extra_columns) : geometry_set_(std::move(geometry_set)), - component_(geometry_set_.get_component_for_read()) + component_(geometry_set_.get_component_for_read()), + extra_columns_(std::move(extra_columns)) { } void foreach_default_column_ids( - FunctionRef fn) const override; + FunctionRef fn) const override; std::unique_ptr get_column_values( const SpreadsheetColumnID &column_id) const override; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh index 8be5283fd63..8b050c2e69b 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh @@ -17,12 +17,24 @@ #pragma once #include "BKE_geometry_set.hh" +#include "spreadsheet_cache.hh" -typedef struct SpaceSpreadsheet_Runtime { - int visible_rows; - int tot_rows; - int tot_columns; -} SpaceSpreadsheet_Runtime; +struct SpaceSpreadsheet_Runtime { + public: + int visible_rows = 0; + int tot_rows = 0; + int tot_columns = 0; + + blender::ed::spreadsheet::SpreadsheetCache cache; + + SpaceSpreadsheet_Runtime() = default; + + /* The cache is not copied currently. */ + SpaceSpreadsheet_Runtime(const SpaceSpreadsheet_Runtime &other) + : visible_rows(other.visible_rows), tot_rows(other.tot_rows), tot_columns(other.tot_columns) + { + } +}; struct bContext; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index 1a5eac53306..355899be279 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -93,7 +93,9 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { const int real_index = spreadsheet_layout_.row_indices[row_index]; const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values; CellValue cell_value; - column.get_value(real_index, cell_value); + if (real_index < column.size()) { + column.get_value(real_index, cell_value); + } if (cell_value.value_int.has_value()) { const int value = *cell_value.value_int; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index c2eb67b7dd8..d5d2520ddf6 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1586,6 +1586,11 @@ typedef struct NodeGeometryImageTexture { int extension; } NodeGeometryImageTexture; +typedef struct NodeGeometryViewer { + /* CustomDataType. */ + int8_t data_type; +} NodeGeometryViewer; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 8ce9bc264ba..8535d334f39 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -11076,6 +11076,20 @@ static void def_geo_separate_geometry(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_viewer(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryViewer", "storage"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 29cbe0d13c8..7fd4840489e 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -413,7 +413,7 @@ DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, " DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") -DefNode(GeometryNode, GEO_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "") +DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") /* undefine macros */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index 3331962341f..920a6e1ed5d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -14,13 +14,69 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_geometry_util.hh" namespace blender::nodes { static void geo_node_viewer_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); + b.add_input("Value").supports_field().hide_value(); + b.add_input("Value", "Value_001").supports_field().hide_value(); + b.add_input("Value", "Value_002").supports_field().hide_value(); + b.add_input("Value", "Value_003").supports_field().hide_value(); + b.add_input("Value", "Value_004").supports_field().hide_value(); } + +static void geo_node_viewer_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN(sizeof(NodeGeometryViewer), + __func__); + data->data_type = CD_PROP_FLOAT; + + node->storage = data; +} + +static void geo_node_viewer_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static eNodeSocketDatatype custom_data_type_to_socket_type(const CustomDataType type) +{ + switch (type) { + case CD_PROP_FLOAT: + return SOCK_FLOAT; + case CD_PROP_INT32: + return SOCK_INT; + case CD_PROP_FLOAT3: + return SOCK_VECTOR; + case CD_PROP_BOOL: + return SOCK_BOOLEAN; + case CD_PROP_COLOR: + return SOCK_RGBA; + default: + BLI_assert_unreachable(); + return SOCK_FLOAT; + } +} + +static void geo_node_viewer_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryViewer &storage = *(const NodeGeometryViewer *)node->storage; + const CustomDataType data_type = static_cast(storage.data_type); + const eNodeSocketDatatype socket_type = custom_data_type_to_socket_type(data_type); + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->type == SOCK_GEOMETRY) { + continue; + } + nodeSetSocketAvailability(socket, socket->type == socket_type); + } +} + } // namespace blender::nodes void register_node_type_geo_viewer() @@ -28,6 +84,11 @@ void register_node_type_geo_viewer() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, 0); + node_type_storage( + &ntype, "NodeGeometryViewer", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, blender::nodes::geo_node_viewer_update); + node_type_init(&ntype, blender::nodes::geo_node_viewer_init); ntype.declare = blender::nodes::geo_node_viewer_declare; + ntype.draw_buttons_ex = blender::nodes::geo_node_viewer_layout; nodeRegisterType(&ntype); } From 816d3f830bf9f6ab06d81dad2087f9b1c2546d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 26 Oct 2021 10:45:55 +0200 Subject: [PATCH 1185/1500] CMake: expand instructions for accidental 'cmake' run in source dir Our CMake setup refuses to run from the source directory (i.e. Blender does not support in-source builds). Instead, it shows instructions on how to clean up after an accidental `cmake` invocation. These instructions missed one directory that should also be removed (`CMakeFiles`), so that's been added to the message now. No functional changes to Blender or the build. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 558e1545c4d..8e975c18aaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) "CMake generation for blender is not allowed within the source directory!" "\n Remove \"${CMAKE_SOURCE_DIR}/CMakeCache.txt\" and try again from another folder, e.g.:" "\n " - "\n rm CMakeCache.txt" + "\n rm -rf CMakeCache.txt CMakeFiles" "\n cd .." "\n mkdir cmake-make" "\n cd cmake-make" From 41a4c62c315508f1da8f5b020dd4e2304828453e Mon Sep 17 00:00:00 2001 From: bird_d Date: Tue, 26 Oct 2021 11:14:06 +0200 Subject: [PATCH 1186/1500] Animation UI: Make Ctrl+F use textbox instead of pop-up Avoid blocking the UI when searching for animation channels with Ctrl+F. Instead of showing a single text input in a blocking popup, Ctrl+F now just focuses the search box above the channel list. It feels nicer to use and has the niceties that come from using that textbox, like searching per keystroke, compared to the old pop-up method. As the behaviour of the operator has changed considerably, this also changes the operator name from `anim.channels_find` to `anim.channels_select_filter` and updates the keymaps. Reviewed By: ChrisLend, sybren Differential Revision: https://developer.blender.org/D12146 --- .../keyconfig/keymap_data/blender_default.py | 8 +- .../keymap_data/industry_compatible_data.py | 8 +- .../editors/animation/anim_channels_edit.c | 79 +++++++++---------- 3 files changed, 47 insertions(+), 48 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index e70fe63677a..3601da03cd4 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1637,7 +1637,7 @@ def km_graph_editor_generic(_params): sidebar_key={"type": 'N', "value": 'PRESS'}, ), ("graph.extrapolation_type", {"type": 'E', "value": 'PRESS', "shift": True}, None), - ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), + ("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), *_template_items_hide_reveal_actions("graph.hide", "graph.reveal"), ("wm.context_set_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, {"properties": [("data_path", 'area.type'), ("value", 'DOPESHEET_EDITOR')]}), @@ -2359,7 +2359,7 @@ def km_dopesheet(params): ("action.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None), ("action.view_frame", {"type": 'NUMPAD_0', "value": 'PRESS'}, None), ("anim.channels_editable_toggle", {"type": 'TAB', "value": 'PRESS'}, None), - ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), + ("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), ("transform.transform", {"type": 'G', "value": 'PRESS'}, {"properties": [("mode", 'TIME_TRANSLATE')]}), ("transform.transform", {"type": params.select_tweak, "value": 'ANY'}, @@ -2400,7 +2400,7 @@ def km_nla_generic(_params): {"properties": [("isolate_action", True)]}), ("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS', "shift": True}, {"properties": [("isolate_action", True)]}), - ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), + ("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), ]) return keymap @@ -3328,7 +3328,7 @@ def km_animation_channels(params): ("anim.channel_select_keys", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True}, {"properties": [("extend", True)]}), # Find (setting the name filter). - ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), + ("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), # Selection. *_template_items_select_actions(params, "anim.channels_select_all"), ("anim.channels_select_box", {"type": 'B', "value": 'PRESS'}, None), diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 6baf0d569d6..8200aad1091 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -875,7 +875,7 @@ def km_graph_editor_generic(params): items.extend([ op_panel("TOPBAR_PT_name", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]), - ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), + ("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), ("graph.hide", {"type": 'H', "value": 'PRESS', "ctrl": True}, {"properties": [("unselected", False)]}), ("graph.hide", {"type": 'H', "value": 'PRESS', "shift": True}, @@ -1439,7 +1439,7 @@ def km_dopesheet(params): ("action.view_selected", {"type": 'F', "value": 'PRESS'}, None), ("action.view_frame", {"type": 'NUMPAD_0', "value": 'PRESS'}, None), ("anim.channels_editable_toggle", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), - ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), + ("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), ("transform.transform", {"type": 'W', "value": 'PRESS'}, {"properties": [("mode", 'TIME_TRANSLATE')]}), ("transform.transform", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, @@ -1477,7 +1477,7 @@ def km_nla_generic(params): *_template_items_animation(), ("nla.tweakmode_enter", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), ("nla.tweakmode_exit", {"type": 'ESC', "value": 'PRESS'}, None), - ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), + ("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), ]) return keymap @@ -2234,7 +2234,7 @@ def km_animation_channels(params): ("anim.channel_select_keys", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True}, {"properties": [("extend", True)]}), # Find (setting the name filter). - ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), + ("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), # Selection. ("anim.channels_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}), ("anim.channels_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}), diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index afbd9b2c92d..69fabd004cc 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -51,6 +51,7 @@ #include "BKE_mask.h" #include "BKE_nla.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -2522,10 +2523,10 @@ static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Find / Set Filter Operator ******************** */ +/* ****************** Select Filter Textbox Operator ******************** */ /* XXX: make this generic? */ -static bool animchannels_find_poll(bContext *C) +static bool animchannels_select_filter_poll(bContext *C) { ScrArea *area = CTX_wm_area(C); @@ -2537,64 +2538,62 @@ static bool animchannels_find_poll(bContext *C) return ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA); } -/* find_invoke() - Get initial channels */ -static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int animchannels_select_filter_invoke(struct bContext *C, + struct wmOperator *op, + const struct wmEvent *UNUSED(event)) { - bAnimContext ac; + ScrArea *area = CTX_wm_area(C); + ARegion *region_ctx = CTX_wm_region(C); + ARegion *region_channels = BKE_area_find_region_type(area, RGN_TYPE_CHANNELS); - /* get editor data */ - if (ANIM_animdata_get_context(C, &ac) == 0) { - return OPERATOR_CANCELLED; + CTX_wm_region_set(C, region_channels); + + /* Show the channel region if it's hidden. This means that direct activation of the input field + * is impossible, as it may not exist yet. For that reason, the actual activation is deferred to + * the modal callback function; by the time it runs, the screen has been redrawn and the UI + * element is there to activate. */ + if (region_channels->flag & RGN_FLAG_HIDDEN) { + ED_region_toggle_hidden(C, region_channels); + ED_region_tag_redraw(region_channels); } - /* set initial filter text, and enable filter */ - RNA_string_set(op->ptr, "query", ac.ads->searchstr); + WM_event_add_modal_handler(C, op); - /* defer to popup */ - return WM_operator_props_popup(C, op, event); + CTX_wm_region_set(C, region_ctx); + return OPERATOR_RUNNING_MODAL; } -/* find_exec() - Called to set the value */ -static int animchannels_find_exec(bContext *C, wmOperator *op) +static int animchannels_select_filter_modal(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *UNUSED(event)) { bAnimContext ac; - - /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) { return OPERATOR_CANCELLED; } - /* update filter text */ - RNA_string_get(op->ptr, "query", ac.ads->searchstr); - - /* redraw */ - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + ARegion *region = CTX_wm_region(C); + if (UI_textbutton_activate_rna(C, region, ac.ads, "filter_text")) { + /* Redraw to make sure it shows the cursor after activating */ + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + } return OPERATOR_FINISHED; } -static void ANIM_OT_channels_find(wmOperatorType *ot) +static void ANIM_OT_channels_select_filter(wmOperatorType *ot) { /* identifiers */ - ot->name = "Find Channels"; - ot->idname = "ANIM_OT_channels_find"; - ot->description = "Filter the set of channels shown to only include those with matching names"; + ot->name = "Filter Channels"; + ot->idname = "ANIM_OT_channels_select_filter"; + ot->description = + "Start entering text which filters the set of channels shown to only include those with " + "matching names"; /* callbacks */ - ot->invoke = animchannels_find_invoke; - ot->exec = animchannels_find_exec; - ot->poll = animchannels_find_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_string(ot->srna, - "query", - "Query", - sizeof(((bDopeSheet *)NULL)->searchstr), - "", - "Text to search for in channel names"); + ot->invoke = animchannels_select_filter_invoke; + ot->modal = animchannels_select_filter_modal; + ot->poll = animchannels_select_filter_poll; } /* ********************** Select All Operator *********************** */ @@ -3563,7 +3562,7 @@ void ED_operatortypes_animchannels(void) WM_operatortype_append(ANIM_OT_channel_select_keys); WM_operatortype_append(ANIM_OT_channels_rename); - WM_operatortype_append(ANIM_OT_channels_find); + WM_operatortype_append(ANIM_OT_channels_select_filter); WM_operatortype_append(ANIM_OT_channels_setting_enable); WM_operatortype_append(ANIM_OT_channels_setting_disable); From d20fa6c4d48580bf5fc140cf83fd7d8829bd8e09 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 26 Oct 2021 12:48:29 +0200 Subject: [PATCH 1187/1500] Geometry Nodes: don't log full fields when not necessary Previously, the field on every socket was logged for later use. This had two main negative consequences: * Increased memory usage, because the fields may contain a lot of data under some circumstances (e.g. a Ray Cast field contains the target geometry). * Decreased performance, because anonymous attributes could not be removed from geometry automatically, because there were still fields that referenced them. Now most fields are not logged anymore. Only those that are viewed by a spreadsheet and constant fields. The required inputs of a field are still logged in string form to keep socket inspection working. --- .../blender/editors/space_node/node_draw.cc | 124 ++++++++++-------- .../spreadsheet_data_source_geometry.cc | 12 +- .../nodes/NOD_geometry_nodes_eval_log.hh | 25 ++++ .../nodes/intern/geometry_nodes_eval_log.cc | 41 ++++++ 4 files changed, 141 insertions(+), 61 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 8029eb5e0fa..3a307777d0a 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -854,60 +854,7 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal const GPointer value = value_log.value(); const CPPType &type = *value.type(); - if (const FieldCPPType *field_type = dynamic_cast(&type)) { - const CPPType &base_type = field_type->field_type(); - BUFFER_FOR_CPP_TYPE_VALUE(base_type, buffer); - const GField &field = field_type->get_gfield(value.get()); - if (field.node().depends_on_input()) { - if (base_type.is()) { - ss << TIP_("Integer Field"); - } - else if (base_type.is()) { - ss << TIP_("Float Field"); - } - else if (base_type.is()) { - ss << TIP_("Vector Field"); - } - else if (base_type.is()) { - ss << TIP_("Boolean Field"); - } - else if (base_type.is()) { - ss << TIP_("String Field"); - } - ss << TIP_(" based on:\n"); - - /* Use vector set to deduplicate inputs. */ - VectorSet> field_inputs; - field.node().foreach_field_input( - [&](const FieldInput &field_input) { field_inputs.add(field_input); }); - for (const FieldInput &field_input : field_inputs) { - ss << "\u2022 " << field_input.socket_inspection_name(); - if (field_input != field_inputs.as_span().last().get()) { - ss << ".\n"; - } - } - } - else { - blender::fn::evaluate_constant_field(field, buffer); - if (base_type.is()) { - ss << *(int *)buffer << TIP_(" (Integer)"); - } - else if (base_type.is()) { - ss << *(float *)buffer << TIP_(" (Float)"); - } - else if (base_type.is()) { - ss << *(blender::float3 *)buffer << TIP_(" (Vector)"); - } - else if (base_type.is()) { - ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); - } - else if (base_type.is()) { - ss << *(std::string *)buffer << TIP_(" (String)"); - } - base_type.destruct(buffer); - } - } - else if (type.is()) { + if (type.is()) { id_to_inspection_string((ID *)*value.get(), ID_OB); } else if (type.is()) { @@ -924,6 +871,71 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal } } +static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &value_log, + std::stringstream &ss) +{ + const CPPType &type = value_log.type(); + const GField &field = value_log.field(); + const Span input_tooltips = value_log.input_tooltips(); + + if (input_tooltips.is_empty()) { + if (field) { + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + blender::fn::evaluate_constant_field(field, buffer); + if (type.is()) { + ss << *(int *)buffer << TIP_(" (Integer)"); + } + else if (type.is()) { + ss << *(float *)buffer << TIP_(" (Float)"); + } + else if (type.is()) { + ss << *(blender::float3 *)buffer << TIP_(" (Vector)"); + } + else if (type.is()) { + ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); + } + else if (type.is()) { + ss << *(std::string *)buffer << TIP_(" (String)"); + } + type.destruct(buffer); + } + else { + /* Constant values should always be logged. */ + BLI_assert_unreachable(); + ss << "Value has not been logged"; + } + } + else { + if (type.is()) { + ss << TIP_("Integer Field"); + } + else if (type.is()) { + ss << TIP_("Float Field"); + } + else if (type.is()) { + ss << TIP_("Vector Field"); + } + else if (type.is()) { + ss << TIP_("Boolean Field"); + } + else if (type.is()) { + ss << TIP_("String Field"); + } + else if (type.is()) { + ss << TIP_("Color Field"); + } + ss << TIP_(" based on:\n"); + + for (const int i : input_tooltips.index_range()) { + const blender::StringRef tooltip = input_tooltips[i]; + ss << "\u2022 " << tooltip; + if (i < input_tooltips.size() - 1) { + ss << ".\n"; + } + } + } +} + static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log, std::stringstream &ss) { @@ -1015,6 +1027,10 @@ static std::optional create_socket_inspection_string(bContext *C, dynamic_cast(value_log)) { create_inspection_string_for_generic_value(*generic_value_log, ss); } + if (const geo_log::GFieldValueLog *gfield_value_log = + dynamic_cast(value_log)) { + create_inspection_string_for_gfield(*gfield_value_log, ss); + } else if (const geo_log::GeometryValueLog *geo_value_log = dynamic_cast(value_log)) { create_inspection_string_for_geometry(*geo_value_log, ss); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 136dc244d16..76e7291d905 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -574,14 +574,12 @@ static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet, if (value_log == nullptr) { continue; } - if (const geo_log::GenericValueLog *generic_value_log = - dynamic_cast(value_log)) { - const fn::GPointer value = generic_value_log->value(); - if (!dynamic_cast(value.type())) { - continue; + if (const geo_log::GFieldValueLog *field_value_log = + dynamic_cast(value_log)) { + const GField &field = field_value_log->field(); + if (field) { + r_fields.add("Viewer", std::move(field)); } - GField field = *(const GField *)value.get(); - r_fields.add("Viewer", std::move(field)); } } } diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 830a7d4070c..2a118057a03 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -76,6 +76,31 @@ class GenericValueLog : public ValueLog { } }; +class GFieldValueLog : public ValueLog { + private: + fn::GField field_; + const fn::CPPType &type_; + Vector input_tooltips_; + + public: + GFieldValueLog(fn::GField field, bool log_full_field); + + const fn::GField &field() const + { + return field_; + } + + Span input_tooltips() const + { + return input_tooltips_; + } + + const fn::CPPType &type() const + { + return type_; + } +}; + struct GeometryAttributeInfo { std::string name; AttributeDomain domain; diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 92f20656609..13dc73c7206 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -21,9 +21,16 @@ #include "DNA_modifier_types.h" #include "DNA_space_types.h" +#include "FN_field_cpp_type.hh" + +#include "BLT_translation.h" + namespace blender::nodes::geometry_nodes_eval_log { using fn::CPPType; +using fn::FieldCPPType; +using fn::FieldInput; +using fn::GField; ModifierLog::ModifierLog(GeoLogger &logger) : input_geometry_log_(std::move(logger.input_geometry_log_)), @@ -168,6 +175,20 @@ const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index); } +GFieldValueLog::GFieldValueLog(fn::GField field, bool log_full_field) : type_(field.cpp_type()) +{ + VectorSet> field_inputs; + field.node().foreach_field_input( + [&](const FieldInput &field_input) { field_inputs.add(field_input); }); + for (const FieldInput &field_input : field_inputs) { + input_tooltips_.append(field_input.socket_inspection_name()); + } + + if (log_full_field) { + field_ = std::move(field); + } +} + GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry) { static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE, @@ -382,6 +403,26 @@ void LocalGeoLogger::log_value_for_sockets(Span sockets, GPointer value geometry_set, log_full_geometry); values_.append({copied_sockets, std::move(value_log)}); } + else if (const FieldCPPType *field_type = dynamic_cast(&type)) { + GField field = field_type->get_gfield(value.get()); + bool log_full_field = false; + if (!field.node().depends_on_input()) { + /* Always log constant fields so that their value can be shown in socket inspection. + * In the future we can also evaluate the field here and only store the value. */ + log_full_field = true; + } + if (!log_full_field) { + for (const DSocket &socket : sockets) { + if (main_logger_->log_full_sockets_.contains(socket)) { + log_full_field = true; + break; + } + } + } + destruct_ptr value_log = allocator_->construct( + std::move(field), log_full_field); + values_.append({copied_sockets, std::move(value_log)}); + } else { void *buffer = allocator_->allocate(type.size(), type.alignment()); type.copy_construct(value.get(), buffer); From ddf97d6270d0a0da36257bb676d9d05513064de5 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 26 Oct 2021 13:05:59 +0200 Subject: [PATCH 1188/1500] BlenLib: Add JSON Serialization/Deserialization Abstraction Layer. Adds an abstraction layer to switch between serialization formats. Currently only supports JSON. The abstraction layer supports `String`, `Int`, `Array`, `Null`, `Boolean`, `Float` and `Object`. This feature is only CPP complaint. To write from a stream, the structure can be built by creating a value (any subclass of `blender::io::serialize::Value` can do, and pass it to the `serialize` method of a `blender::io::serialize::Formatter`. The formatter is abstract and there is one implementation for JSON (`JsonFormatter`). To read from a stream use the `deserialize` method of the formatter. {D12693} uses this abstraction layer to read/write asset indexes. Reviewed By: Severin, sybren Maniphest Tasks: T91430 Differential Revision: https://developer.blender.org/D12544 --- source/blender/blenlib/BLI_serialize.hh | 330 ++++++++++++++++++ source/blender/blenlib/CMakeLists.txt | 4 + source/blender/blenlib/intern/serialize.cc | 217 ++++++++++++ .../blenlib/tests/BLI_serialize_test.cc | 208 +++++++++++ 4 files changed, 759 insertions(+) create mode 100644 source/blender/blenlib/BLI_serialize.hh create mode 100644 source/blender/blenlib/intern/serialize.cc create mode 100644 source/blender/blenlib/tests/BLI_serialize_test.cc diff --git a/source/blender/blenlib/BLI_serialize.hh b/source/blender/blenlib/BLI_serialize.hh new file mode 100644 index 00000000000..0d59f33d552 --- /dev/null +++ b/source/blender/blenlib/BLI_serialize.hh @@ -0,0 +1,330 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + * + * An abstraction layer for serialization formats. + * + * Allowing to read/write data to a serialization format like JSON. + * + * + * + * # Supported data types + * + * The abstraction layer has a limited set of data types it supports. + * There are specific classes that builds up the data structure that + * can be (de)serialized. + * + * - StringValue: for strings + * - IntValue: for integer values + * - DoubleValue: for double precision floating point numbers + * - BooleanValue: for boolean values + * - ArrayValue: An array of any supported value. + * - ObjectValue: A key value pair where keys are std::string. + * - NullValue: for null values. + * + * # Basic usage + * + * ## Serializing + * + * - Construct a structure that needs to be serialized using the `*Value` classes. + * - Construct the formatter you want to use + * - Invoke the formatter.serialize method passing an output stream and the value. + * + * The next example would format an integer value (42) as JSON the result will + * be stored inside `out`. + * + * ``` + * JsonFormatter json; + * std::stringstream out; + * IntValue test_value(42); + * json.serialize(out, test_value); + * ``` + * + * ## Deserializing + * + * ``` + * std::stringstream is("42") + * JsonFormatter json; + * std::unique_ptr value = json.deserialize(is); + * ``` + * + * # Adding a new formatter + * + * To add a new formatter a new sub-class of `Formatter` must be created and the + * `serialize`/`deserialize` methods should be implemented. + * + */ + +#include + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +namespace blender::io::serialize { + +/** + * Enumeration containing all sub-classes of Value. It is used as for type checking. + * + * \see #Value::type() + */ +enum class eValueType { + String, + Int, + Array, + Null, + Boolean, + Double, + Object, +}; + +class Value; +class StringValue; +class ObjectValue; +template class PrimitiveValue; +using IntValue = PrimitiveValue; +using DoubleValue = PrimitiveValue; +using BooleanValue = PrimitiveValue; + +template class ContainerValue; +/* ArrayValue stores its items as shared pointer as it shares data with a lookup table that can + * be created by calling `create_lookup`. */ +using ArrayValue = + ContainerValue>, std::shared_ptr, eValueType::Array>; + +/** + * Class containing a (de)serializable value. + * + * To serialize from or to a specific format the Value will be used as an intermediate container + * holding the values. Value class is abstract. There are concreate classes to for different data + * types. + * + * - `StringValue`: contains a string. + * - `IntValue`: contains an integer. + * - `ArrayValue`: contains an array of elements. Elements don't need to be the same type. + * - `NullValue`: represents nothing (null pointer or optional). + * - `BooleanValue`: contains a boolean (true/false). + * - `DoubleValue`: contains a double precision floating point number. + * - `ObjectValue`: represents an object (key value pairs where keys are strings and values can be + * of different types. + * + */ +class Value { + private: + eValueType type_; + + protected: + Value() = delete; + explicit Value(eValueType type) : type_(type) + { + } + + public: + virtual ~Value() = default; + const eValueType type() const + { + return type_; + } + + /** + * Casts to a StringValue. + * Will return nullptr when it is a different type. + */ + const StringValue *as_string_value() const; + + /** + * Casts to an IntValue. + * Will return nullptr when it is a different type. + */ + const IntValue *as_int_value() const; + + /** + * Casts to a DoubleValue. + * Will return nullptr when it is a different type. + */ + const DoubleValue *as_double_value() const; + + /** + * Casts to a BooleanValue. + * Will return nullptr when it is a different type. + */ + const BooleanValue *as_boolean_value() const; + + /** + * Casts to an ArrayValue. + * Will return nullptr when it is a different type. + */ + const ArrayValue *as_array_value() const; + + /** + * Casts to an ObjectValue. + * Will return nullptr when it is a different type. + */ + const ObjectValue *as_object_value() const; +}; + +/** + * For generating value types that represent types that are typically known processor data types. + */ +template< + /** Wrapped c/cpp data type that is used to store the value. */ + typename T, + /** Value type of the class. */ + eValueType V> +class PrimitiveValue : public Value { + private: + T inner_value_{}; + + public: + explicit PrimitiveValue(const T value) : Value(V), inner_value_(value) + { + } + + const T value() const + { + return inner_value_; + } +}; + +class NullValue : public Value { + public: + NullValue() : Value(eValueType::Null) + { + } +}; + +class StringValue : public Value { + private: + std::string string_; + + public: + StringValue(const StringRef string) : Value(eValueType::String), string_(string) + { + } + + const std::string &value() const + { + return string_; + } +}; + +/** + * Template for arrays and objects. + * + * Both ArrayValue and ObjectValue store their values in an array. + */ +template< + /** The container type where the elements are stored in. */ + typename Container, + + /** Type of the data inside the container. */ + typename ContainerItem, + + /** ValueType representing the value (object/array). */ + eValueType V> +class ContainerValue : public Value { + public: + using Items = Container; + using Item = ContainerItem; + + private: + Container inner_value_; + + public: + ContainerValue() : Value(V) + { + } + + const Container &elements() const + { + return inner_value_; + } + + Container &elements() + { + return inner_value_; + } +}; + +/** + * Internal storage type for ObjectValue. + * + * The elements are stored as an key value pair. The value is a shared pointer so it can be shared + * when using `ObjectValue::create_lookup`. + */ +using ObjectElementType = std::pair>; + +/** + * Object is a key-value container where the key must be a std::string. + * Internally it is stored in a blender::Vector to ensure the order of keys. + */ +class ObjectValue + : public ContainerValue, ObjectElementType, eValueType::Object> { + public: + using LookupValue = std::shared_ptr; + using Lookup = Map; + + /** + * Return a lookup map to quickly lookup by key. + * + * The lookup is owned by the caller. + */ + const Lookup create_lookup() const + { + Lookup result; + for (const Item &item : elements()) { + result.add_as(item.first, item.second); + } + return result; + } +}; + +/** + * Interface for any provided Formatter. + */ +class Formatter { + public: + virtual ~Formatter() = default; + + /** Serialize the value to the given stream. */ + virtual void serialize(std::ostream &os, const Value &value) = 0; + + /** Deserialize the stream. */ + virtual std::unique_ptr deserialize(std::istream &is) = 0; +}; + +/** + * Formatter to (de)serialize a json formatted stream. + */ +class JsonFormatter : public Formatter { + public: + /** + * The identation level to use. + * Typically number of chars. Set to 0 to not use identation. + */ + int8_t indentation_len = 0; + + public: + void serialize(std::ostream &os, const Value &value) override; + std::unique_ptr deserialize(std::istream &is) override; +}; + +} // namespace blender::io::serialize + diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index c01052f0111..72087a12767 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../../../intern/guardedalloc ../../../intern/numaapi/include ../../../extern/wcwidth + ../../../extern/json/include ) set(INC_SYS @@ -126,6 +127,7 @@ set(SRC intern/scanfill.c intern/scanfill_utils.c intern/session_uuid.c + intern/serialize.cc intern/smallhash.c intern/sort.c intern/sort_utils.c @@ -282,6 +284,7 @@ set(SRC BLI_session_uuid.h BLI_set.hh BLI_set_slots.hh + BLI_serialize.hh BLI_simd.h BLI_smallhash.h BLI_sort.h @@ -448,6 +451,7 @@ if(WITH_GTESTS) tests/BLI_span_test.cc tests/BLI_stack_cxx_test.cc tests/BLI_stack_test.cc + tests/BLI_serialize_test.cc tests/BLI_string_ref_test.cc tests/BLI_string_search_test.cc tests/BLI_string_test.cc diff --git a/source/blender/blenlib/intern/serialize.cc b/source/blender/blenlib/intern/serialize.cc new file mode 100644 index 00000000000..1e023a9f782 --- /dev/null +++ b/source/blender/blenlib/intern/serialize.cc @@ -0,0 +1,217 @@ +#include "BLI_serialize.hh" + +#include "json.hpp" + +namespace blender::io::serialize { + +const StringValue *Value::as_string_value() const +{ + if (type_ != eValueType::String) { + return nullptr; + } + return static_cast(this); +} + +const IntValue *Value::as_int_value() const +{ + if (type_ != eValueType::Int) { + return nullptr; + } + return static_cast(this); +} + +const DoubleValue *Value::as_double_value() const +{ + if (type_ != eValueType::Double) { + return nullptr; + } + return static_cast(this); +} + +const BooleanValue *Value::as_boolean_value() const +{ + if (type_ != eValueType::Boolean) { + return nullptr; + } + return static_cast(this); +} + +const ArrayValue *Value::as_array_value() const +{ + if (type_ != eValueType::Array) { + return nullptr; + } + return static_cast(this); +} + +const ObjectValue *Value::as_object_value() const +{ + if (type_ != eValueType::Object) { + return nullptr; + } + return static_cast(this); +} + +static void convert_to_json(nlohmann::ordered_json &j, const Value &value); +static void convert_to_json(nlohmann::ordered_json &j, const ArrayValue &value) +{ + const ArrayValue::Items &items = value.elements(); + /* Create a json array to store the elements. If this isn't done and items is empty it would + * return use a null value, in stead of an empty array. */ + j = "[]"_json; + for (const ArrayValue::Item &item_value : items) { + nlohmann::ordered_json json_item; + convert_to_json(json_item, *item_value); + j.push_back(json_item); + } +} + +static void convert_to_json(nlohmann::ordered_json &j, const ObjectValue &value) +{ + const ObjectValue::Items &attributes = value.elements(); + /* Create a json object to store the attributes. If this isn't done and attributes is empty it + * would return use a null value, in stead of an empty object. */ + j = "{}"_json; + for (const ObjectValue::Item &attribute : attributes) { + nlohmann::ordered_json json_item; + convert_to_json(json_item, *attribute.second); + j[attribute.first] = json_item; + } +} + +static void convert_to_json(nlohmann::ordered_json &j, const Value &value) +{ + switch (value.type()) { + case eValueType::String: { + j = value.as_string_value()->value(); + break; + } + + case eValueType::Int: { + j = value.as_int_value()->value(); + break; + } + + case eValueType::Array: { + const ArrayValue &array = *value.as_array_value(); + convert_to_json(j, array); + break; + } + + case eValueType::Object: { + const ObjectValue &object = *value.as_object_value(); + convert_to_json(j, object); + break; + } + + case eValueType::Null: { + j = nullptr; + break; + } + + case eValueType::Boolean: { + j = value.as_boolean_value()->value(); + break; + } + + case eValueType::Double: { + j = value.as_double_value()->value(); + } + } +} + +static std::unique_ptr convert_from_json(const nlohmann::ordered_json &j); +static std::unique_ptr convert_from_json_to_array(const nlohmann::ordered_json &j) +{ + std::unique_ptr array = std::make_unique(); + ArrayValue::Items &elements = array->elements(); + for (auto element : j.items()) { + nlohmann::ordered_json element_json = element.value(); + std::unique_ptr value = convert_from_json(element_json); + elements.append_as(value.release()); + } + return array; +} + +static std::unique_ptr convert_from_json_to_object(const nlohmann::ordered_json &j) +{ + std::unique_ptr object = std::make_unique(); + ObjectValue::Items &elements = object->elements(); + for (auto element : j.items()) { + std::string key = element.key(); + nlohmann::ordered_json element_json = element.value(); + std::unique_ptr value = convert_from_json(element_json); + elements.append_as(std::pair(key, value.release())); + } + return object; +} + +static std::unique_ptr convert_from_json(const nlohmann::ordered_json &j) +{ + switch (j.type()) { + case nlohmann::json::value_t::array: { + return convert_from_json_to_array(j); + } + + case nlohmann::json::value_t::object: { + return convert_from_json_to_object(j); + } + + case nlohmann::json::value_t::string: { + std::string value = j; + return std::make_unique(value); + } + + case nlohmann::json::value_t::null: { + return std::make_unique(); + } + + case nlohmann::json::value_t::boolean: { + return std::make_unique(j); + } + case nlohmann::json::value_t::number_integer: + case nlohmann::json::value_t::number_unsigned: { + return std::make_unique(j); + } + + case nlohmann::json::value_t::number_float: { + return std::make_unique(j); + } + + case nlohmann::json::value_t::binary: + case nlohmann::json::value_t::discarded: + /* + * Binary data isn't supported. + * Discarded is an internal type of nlohmann. + * + * Assert in case we need to parse them. + */ + BLI_assert_unreachable(); + return std::make_unique(); + } + + BLI_assert_unreachable(); + return std::make_unique(); +} + +void JsonFormatter::serialize(std::ostream &os, const Value &value) +{ + nlohmann::ordered_json j; + convert_to_json(j, value); + if (indentation_len) { + os << j.dump(indentation_len); + } + else { + os << j.dump(); + } +} + +std::unique_ptr JsonFormatter::deserialize(std::istream &is) +{ + nlohmann::ordered_json j; + is >> j; + return convert_from_json(j); +} + +} // namespace blender::io::serialize + diff --git a/source/blender/blenlib/tests/BLI_serialize_test.cc b/source/blender/blenlib/tests/BLI_serialize_test.cc new file mode 100644 index 00000000000..8e4ce66fa7c --- /dev/null +++ b/source/blender/blenlib/tests/BLI_serialize_test.cc @@ -0,0 +1,208 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_serialize.hh" + +/* -------------------------------------------------------------------- */ +/* tests */ + +namespace blender::io::serialize::json::testing { + +TEST(serialize, string_to_json) +{ + JsonFormatter json; + std::stringstream out; + StringValue test_value("Hello JSON"); + json.serialize(out, test_value); + EXPECT_EQ(out.str(), "\"Hello JSON\""); +} + +static void test_int_to_json(int64_t value, StringRef expected) +{ + JsonFormatter json; + std::stringstream out; + IntValue test_value(value); + json.serialize(out, test_value); + EXPECT_EQ(out.str(), expected); +} + +TEST(serialize, int_to_json) +{ + test_int_to_json(42, "42"); + test_int_to_json(-42, "-42"); + test_int_to_json(std::numeric_limits::max(), "2147483647"); + test_int_to_json(std::numeric_limits::min(), "-2147483648"); + test_int_to_json(std::numeric_limits::max(), "9223372036854775807"); + test_int_to_json(std::numeric_limits::min(), "-9223372036854775808"); +} + +TEST(serialize, double_to_json) +{ + JsonFormatter json; + std::stringstream out; + DoubleValue test_value(42.31); + json.serialize(out, test_value); + EXPECT_EQ(out.str(), "42.31"); +} + +TEST(serialize, null_to_json) +{ + JsonFormatter json; + std::stringstream out; + NullValue test_value; + json.serialize(out, test_value); + EXPECT_EQ(out.str(), "null"); +} + +TEST(serialize, false_to_json) +{ + JsonFormatter json; + std::stringstream out; + BooleanValue value(false); + json.serialize(out, value); + EXPECT_EQ(out.str(), "false"); +} + +TEST(serialize, true_to_json) +{ + JsonFormatter json; + std::stringstream out; + BooleanValue value(true); + json.serialize(out, value); + EXPECT_EQ(out.str(), "true"); +} + +TEST(serialize, array_to_json) +{ + JsonFormatter json; + std::stringstream out; + ArrayValue value_array; + ArrayValue::Items &array = value_array.elements(); + array.append_as(new IntValue(42)); + array.append_as(new StringValue("Hello JSON")); + array.append_as(new NullValue); + array.append_as(new BooleanValue(false)); + array.append_as(new BooleanValue(true)); + + json.serialize(out, value_array); + EXPECT_EQ(out.str(), "[42,\"Hello JSON\",null,false,true]"); +} + +TEST(serialize, object_to_json) +{ + JsonFormatter json; + std::stringstream out; + ObjectValue value_object; + ObjectValue::Items &attributes = value_object.elements(); + attributes.append_as(std::pair(std::string("best_number"), new IntValue(42))); + + json.serialize(out, value_object); + EXPECT_EQ(out.str(), "{\"best_number\":42}"); +} + +TEST(serialize, json_roundtrip_ordering) +{ + const std::string input = + "[{\"_id\":\"614ada7c476c472ecbd0ecbb\",\"index\":0,\"guid\":\"d5b81381-cef8-4327-923d-" + "41e57ff79326\",\"isActive\":false,\"balance\":\"$2,062.25\",\"picture\":\"http://" + "placehold.it/32x32\",\"age\":26,\"eyeColor\":\"brown\",\"name\":\"Geneva " + "Vega\",\"gender\":\"female\",\"company\":\"SLOGANAUT\",\"email\":\"genevavega@sloganaut." + "com\",\"phone\":\"+1 (993) 432-2805\",\"address\":\"943 Christopher Avenue, Northchase, " + "Alabama, 5769\",\"about\":\"Eu cillum qui eu fugiat sit nulla eu duis. Aliqua nulla aliqua " + "ea tempor dolor fugiat sint consectetur exercitation ipsum magna ex. Aute laborum esse " + "magna nostrud in cillum et mollit proident. Deserunt ex minim adipisicing incididunt " + "incididunt dolore velit aliqua.\\r\\n\",\"registered\":\"2014-06-02T06:29:33 " + "-02:00\",\"latitude\":-66.003108,\"longitude\":44.038986,\"tags\":[\"exercitation\"," + "\"laborum\",\"velit\",\"magna\",\"officia\",\"aliqua\",\"laboris\"],\"friends\":[{\"id\":0," + "\"name\":\"Daniel Stuart\"},{\"id\":1,\"name\":\"Jackson " + "Velez\"},{\"id\":2,\"name\":\"Browning Boyd\"}],\"greeting\":\"Hello, Geneva Vega! You " + "have 8 unread " + "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7cf28685063c6722af\"," + "\"index\":1,\"guid\":\"e157edf3-a86d-4984-b18d-e2fe568a9915\",\"isActive\":false," + "\"balance\":\"$3,550.44\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":40,\"eyeColor\":\"blue\",\"name\":\"Lamb " + "Lowe\",\"gender\":\"male\",\"company\":\"PROXSOFT\",\"email\":\"lamblowe@proxsoft.com\"," + "\"phone\":\"+1 (999) 573-2855\",\"address\":\"632 Rockwell Place, Diaperville, " + "Pennsylvania, 5050\",\"about\":\"Anim dolor deserunt esse quis velit adipisicing aute " + "nostrud velit minim culpa aute et tempor. Dolor aliqua reprehenderit anim voluptate. " + "Consequat proident ut culpa reprehenderit qui. Nisi proident velit cillum voluptate. " + "Ullamco id sunt quis aute adipisicing cupidatat consequat " + "aliquip.\\r\\n\",\"registered\":\"2014-09-06T06:13:36 " + "-02:00\",\"latitude\":-44.550228,\"longitude\":-80.893356,\"tags\":[\"anim\",\"id\"," + "\"irure\",\"do\",\"officia\",\"irure\",\"Lorem\"],\"friends\":[{\"id\":0,\"name\":" + "\"Faulkner Watkins\"},{\"id\":1,\"name\":\"Cecile Schneider\"},{\"id\":2,\"name\":\"Burt " + "Lester\"}],\"greeting\":\"Hello, Lamb Lowe! You have 1 unread " + "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7c235335fc56bc2f78\"," + "\"index\":2,\"guid\":\"8206bad1-8274-49fd-9223-d727589f22ca\",\"isActive\":false," + "\"balance\":\"$2,548.34\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":37,\"eyeColor\":\"blue\",\"name\":\"Sallie " + "Chase\",\"gender\":\"female\",\"company\":\"FLEETMIX\",\"email\":\"salliechase@fleetmix." + "com\",\"phone\":\"+1 (953) 453-3388\",\"address\":\"865 Irving Place, Chelsea, Utah, " + "9777\",\"about\":\"In magna exercitation incididunt exercitation dolor anim. Consectetur " + "dolore commodo elit cillum dolor reprehenderit magna minim et ex labore pariatur. Nulla " + "ullamco officia velit in aute proident nostrud. Duis deserunt et labore Lorem aliqua " + "eiusmod commodo sunt.\\r\\n\",\"registered\":\"2017-03-16T08:54:53 " + "-01:00\",\"latitude\":-78.481939,\"longitude\":-149.820215,\"tags\":[\"Lorem\",\"ipsum\"," + "\"in\",\"tempor\",\"consectetur\",\"voluptate\",\"elit\"],\"friends\":[{\"id\":0,\"name\":" + "\"Gibson Garner\"},{\"id\":1,\"name\":\"Anna Frank\"},{\"id\":2,\"name\":\"Roberson " + "Daugherty\"}],\"greeting\":\"Hello, Sallie Chase! You have 7 unread " + "messages.\",\"favoriteFruit\":\"apple\"},{\"_id\":\"614ada7c93b63ecad5f9ba5e\",\"index\":3," + "\"guid\":\"924b02fc-7c27-481a-9941-db3b9403dfe1\",\"isActive\":true,\"balance\":\"$1,633." + "60\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":29,\"eyeColor\":\"brown\",\"name\":\"Grace " + "Mccall\",\"gender\":\"female\",\"company\":\"PIVITOL\",\"email\":\"gracemccall@pivitol." + "com\",\"phone\":\"+1 (964) 541-2514\",\"address\":\"734 Schaefer Street, Topaz, Virginia, " + "9137\",\"about\":\"Amet officia magna fugiat ut pariatur fugiat elit culpa voluptate elit " + "do proident culpa minim. Commodo do minim reprehenderit ut voluptate ut velit id esse " + "consequat. Labore ullamco deserunt irure eiusmod cillum tempor incididunt qui adipisicing " + "nostrud pariatur enim aliquip. Excepteur nostrud commodo consectetur esse duis irure " + "qui.\\r\\n\",\"registered\":\"2015-04-24T03:55:17 " + "-02:00\",\"latitude\":58.801446,\"longitude\":-157.413865,\"tags\":[\"do\",\"ea\",\"eu\"," + "\"eu\",\"qui\",\"duis\",\"sint\"],\"friends\":[{\"id\":0,\"name\":\"Carrie " + "Short\"},{\"id\":1,\"name\":\"Dickerson Barnes\"},{\"id\":2,\"name\":\"Rae " + "Rios\"}],\"greeting\":\"Hello, Grace Mccall! You have 5 unread " + "messages.\",\"favoriteFruit\":\"apple\"},{\"_id\":\"614ada7c9caf1353b0e22bbf\",\"index\":4," + "\"guid\":\"e5981ae1-90e4-41c4-9905-161522db700b\",\"isActive\":false,\"balance\":\"$3,660." + "34\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":31,\"eyeColor\":\"blue\",\"name\":\"Herring " + "Powers\",\"gender\":\"male\",\"company\":\"PYRAMIA\",\"email\":\"herringpowers@pyramia." + "com\",\"phone\":\"+1 (981) 541-2829\",\"address\":\"409 Furman Avenue, Waterloo, South " + "Carolina, 380\",\"about\":\"In officia culpa aliqua culpa pariatur aliqua mollit ex. Velit " + "est Lorem enim magna cillum sunt elit consectetur deserunt ea est consectetur fugiat " + "mollit. Aute Lorem excepteur minim esse qui. Id Lorem in tempor et. Nisi aliquip laborum " + "magna eu aute.\\r\\n\",\"registered\":\"2018-07-05T07:28:54 " + "-02:00\",\"latitude\":51.497405,\"longitude\":-129.422711,\"tags\":[\"eiusmod\",\"et\"," + "\"nostrud\",\"reprehenderit\",\"Lorem\",\"cillum\",\"nulla\"],\"friends\":[{\"id\":0," + "\"name\":\"Tonia Keith\"},{\"id\":1,\"name\":\"Leanne Rice\"},{\"id\":2,\"name\":\"Craig " + "Gregory\"}],\"greeting\":\"Hello, Herring Powers! You have 6 unread " + "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7c53a3d6da77468f25\"," + "\"index\":5,\"guid\":\"abb2eec9-c4f0-4a0d-b20a-5c8e50fe88a1\",\"isActive\":true," + "\"balance\":\"$1,481.08\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":31,\"eyeColor\":\"green\",\"name\":\"Lela " + "Dillard\",\"gender\":\"female\",\"company\":\"CEMENTION\",\"email\":\"leladillard@" + "cemention.com\",\"phone\":\"+1 (856) 456-3657\",\"address\":\"391 Diamond Street, Madaket, " + "Ohio, 9337\",\"about\":\"Tempor dolor ullamco esse cillum excepteur. Excepteur aliqua non " + "enim anim esse amet cupidatat non. Cillum excepteur occaecat cupidatat elit labore. " + "Pariatur ut esse sint elit. Velit sint magna et commodo sit velit labore consectetur irure " + "officia proident aliquip. Aliqua dolore ipsum voluptate veniam deserunt amet irure. Cillum " + "consequat veniam proident Lorem in anim enim veniam ea " + "nulla.\\r\\n\",\"registered\":\"2017-01-11T11:07:22 " + "-01:00\",\"latitude\":86.349081,\"longitude\":-179.983754,\"tags\":[\"consequat\"," + "\"labore\",\"consectetur\",\"dolor\",\"laborum\",\"eiusmod\",\"in\"],\"friends\":[{\"id\":" + "0,\"name\":\"Hancock Rivera\"},{\"id\":1,\"name\":\"Chasity " + "Oneil\"},{\"id\":2,\"name\":\"Whitaker Barr\"}],\"greeting\":\"Hello, Lela Dillard! You " + "have 3 unread messages.\",\"favoriteFruit\":\"strawberry\"}]"; + std::stringstream is(input); + + JsonFormatter json; + std::unique_ptr value = json.deserialize(is); + EXPECT_EQ(value->type(), eValueType::Array); + + std::stringstream out; + json.serialize(out, *value); + EXPECT_EQ(out.str(), input); +} + +} // namespace blender::io::serialize::json::testing + From 6d3d2988faad182eedc34210c651f8edb1cda6d8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 26 Oct 2021 22:24:56 +1100 Subject: [PATCH 1189/1500] Cleanup: spelling in comments --- source/blender/blenlib/BLI_serialize.hh | 5 ++--- .../editors/interface/interface_handlers.c | 2 +- source/blender/editors/interface/view2d.c | 2 +- source/blender/editors/object/object_relations.c | 6 ++++-- source/blender/editors/screen/area.c | 4 ++-- source/blender/editors/screen/screen_ops.c | 16 ++++++++-------- .../blender/editors/sculpt_paint/paint_vertex.c | 2 +- source/blender/editors/space_file/filelist.c | 8 ++++---- .../windowmanager/xr/intern/wm_xr_operators.c | 3 ++- 9 files changed, 25 insertions(+), 23 deletions(-) diff --git a/source/blender/blenlib/BLI_serialize.hh b/source/blender/blenlib/BLI_serialize.hh index 0d59f33d552..8bec2785f83 100644 --- a/source/blender/blenlib/BLI_serialize.hh +++ b/source/blender/blenlib/BLI_serialize.hh @@ -316,8 +316,8 @@ class Formatter { class JsonFormatter : public Formatter { public: /** - * The identation level to use. - * Typically number of chars. Set to 0 to not use identation. + * The indentation level to use. + * Typically number of chars. Set to 0 to not use indentation. */ int8_t indentation_len = 0; @@ -327,4 +327,3 @@ class JsonFormatter : public Formatter { }; } // namespace blender::io::serialize - diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 44420ee926e..b21d2127030 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -10780,7 +10780,7 @@ static int ui_handle_menu_event(bContext *C, } #endif - /* Don't handle double click events, rehandle as regular press/release. */ + /* Don't handle double click events, re-handle as regular press/release. */ if (retval == WM_UI_HANDLER_CONTINUE && event->val == KM_DBL_CLICK) { return retval; } diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 30483f11c45..3b6a7e1e88e 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -167,7 +167,7 @@ static void view2d_masks(View2D *v2d, const rcti *mask_scroll) scroll = view2d_scroll_mapped(v2d->scroll); - /* scrollers are based off regionsize + /* Scrollers are based off region-size: * - they can only be on one to two edges of the region they define * - if they overlap, they must not occupy the corners (which are reserved for other widgets) */ diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index a64510662d9..aa15ce36582 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2642,8 +2642,10 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_FINISHED; } -/* used for dropbox */ -/* assigns to object under cursor, only first material slot */ +/** + * Used for drop-box. + * Assigns to object under cursor, only first material slot. + */ void OBJECT_OT_drop_named_material(wmOperatorType *ot) { /* identifiers */ diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index b69a563166a..4309359bd91 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1940,7 +1940,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) rcti window_rect; WM_window_rect_calc(win, &window_rect); - /* set typedefinitions */ + /* Set type-definitions. */ area->type = BKE_spacetype_from_id(area->spacetype); if (area->type == NULL) { @@ -3027,7 +3027,7 @@ void ED_region_panels_layout_ex(const bContext *C, search_filter); } - /* Draw "polyinstantaited" panels that don't have a 1 to 1 correspondence with their types. */ + /* Draw "poly-instantiated" panels that don't have a 1 to 1 correspondence with their types. */ if (has_instanced_panel) { LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->type == NULL) { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index e5fbcbb0b6c..5acfc5ac681 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1283,7 +1283,7 @@ static ScrEdge *screen_area_edge_from_cursor(const bContext *C, * * callbacks: * - * invoke() gets called on shift+lmb drag in action-zone + * invoke() gets called on Shift-LMB drag in action-zone * exec() execute without any user interaction, based on properties * call init(), add handler * @@ -2078,7 +2078,7 @@ typedef struct sAreaSplitData { int bigger, smaller; /* constraints for moving new edge */ int delta; /* delta move edge */ int origmin, origsize; /* to calculate fac, for property storage */ - int previewmode; /* draw previewline, then split */ + int previewmode; /* draw preview-line, then split. */ void *draw_callback; /* call `screen_draw_split_preview` */ bool do_snap; @@ -2628,8 +2628,8 @@ static int area_max_regionsize(ScrArea *area, ARegion *scalear, AZEdge edge) dist = BLI_rcti_size_y(&area->totrct); } - /* subtractwidth of regions on opposite side - * prevents dragging regions into other opposite regions */ + /* Subtract the width of regions on opposite side + * prevents dragging regions into other opposite regions. */ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region == scalear) { continue; @@ -3082,12 +3082,12 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) float cfra = (float)(CFRA); - /* init binarytree-list for getting keyframes */ + /* Initialize binary-tree-list for getting keyframes. */ struct AnimKeylist *keylist = ED_keylist_create(); - /* seed up dummy dopesheet context with flags to perform necessary filtering */ + /* Speed up dummy dope-sheet context with flags to perform necessary filtering. */ if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) { - /* only selected channels are included */ + /* Only selected channels are included. */ ads.filterflag |= ADS_FILTER_ONLYSEL; } @@ -4206,7 +4206,7 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Region Context Menu Operator (Header/Footer/Navbar) +/** \name Region Context Menu Operator (Header/Footer/Navigation-Bar) * \{ */ static void screen_area_menu_items(ScrArea *area, uiLayout *layout) diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 4e0dcfe8e3c..fede01a614b 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -295,7 +295,7 @@ static uint vpaint_blend(const VPaint *vp, uint color_blend = ED_vpaint_blend_tool(blend, color_curr, color_paint, alpha_i); - /* if no accumulate, clip color adding with colorig & orig alpha */ + /* If no accumulate, clip color adding with `color_orig` & `color_test`. */ if (!brush_use_accumulate(vp)) { uint color_test, a; char *cp, *ct, *co; diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 1aabe5fde2b..a1b1c8cc363 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -919,7 +919,7 @@ static void prepare_filter_asset_library(const FileList *filelist, FileListFilte } /** - * Copy a string from source to dest, but prefix and suffix it with a single space. + * Copy a string from source to `dest`, but prefix and suffix it with a single space. * Assumes `dest` has at least space enough for the two spaces. */ static void tag_copy_with_spaces(char *dest, const char *source, const size_t dest_size) @@ -939,9 +939,9 @@ static void tag_copy_with_spaces(char *dest, const char *source, const size_t de * * Here the tags on the asset are written in set notation: * - * asset_tag_matches_filter(" some tags ", {"some", "blue"}) -> true - * asset_tag_matches_filter(" some tags ", {"som", "tag"}) -> false - * asset_tag_matches_filter(" some tags ", {}) -> false + * `asset_tag_matches_filter(" some tags ", {"some", "blue"})` -> true + * `asset_tag_matches_filter(" some tags ", {"som", "tag"})` -> false + * `asset_tag_matches_filter(" some tags ", {})` -> false */ static bool asset_tag_matches_filter(const char *filter_search, const AssetMetaData *asset_data) { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_operators.c b/source/blender/windowmanager/xr/intern/wm_xr_operators.c index 105c3b29cc8..36af0147cb8 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_operators.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c @@ -133,7 +133,8 @@ static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op)) wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); - /* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */ + /* Lazily-create XR context - tries to dynamic-link to the runtime, + * reading `active_runtime.json`. */ if (wm_xr_init(wm) == false) { return OPERATOR_CANCELLED; } From 118664e4631a29c9a4f1f8e06be124c8ea3a006a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 26 Oct 2021 22:24:58 +1100 Subject: [PATCH 1190/1500] Fix custom property editing with Python 3.10 --- release/scripts/startup/bl_operators/wm.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 4dbdc6e633e..28bb0a58c02 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1298,6 +1298,13 @@ rna_vector_subtype_items = ( ('QUATERNION', "Quaternion Rotation", "Quaternion rotation (affects NLA blending)"), ) + +# NOTE: needed for Python 3.10 since there are name-space issues with annotations. +# This can be moved into the class as a static-method once Python 3.9x is dropped. +def _wm_properties_edit_subtype_items(_self, _context): + return WM_OT_properties_edit.subtype_items + + class WM_OT_properties_edit(Operator): """Change a custom property's type, or adjust how it is displayed in the interface""" bl_idname = "wm.properties_edit" @@ -1312,7 +1319,7 @@ class WM_OT_properties_edit(Operator): property_name: rna_custom_property_name property_type: EnumProperty( name="Type", - items=lambda self, _context: WM_OT_properties_edit.type_items, + items=rna_custom_property_type_items, ) is_overridable_library: BoolProperty( name="Is Library Overridable", @@ -1404,7 +1411,7 @@ class WM_OT_properties_edit(Operator): ) subtype: EnumProperty( name="Subtype", - items=lambda self, _context: WM_OT_properties_edit.subtype_items, + items=_wm_properties_edit_subtype_items, ) # String properties. From 16a8d0fab07513bcafb3f3bd09ac9c880638816b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 25 Oct 2021 18:42:27 +0200 Subject: [PATCH 1191/1500] Cycles: change Position render pass to be not antialiased Similar to the Depth, for compositing the interpolated values between a far and near object can be non-sensical. --- intern/cycles/kernel/kernel_passes.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h index 4e4ffa68d25..ab8d7a26f44 100644 --- a/intern/cycles/kernel/kernel_passes.h +++ b/intern/cycles/kernel/kernel_passes.h @@ -231,12 +231,12 @@ ccl_device_inline void kernel_write_data_passes(KernelGlobals kg, const float id = shader_pass_id(kg, sd); kernel_write_pass_float(buffer + kernel_data.film.pass_material_id, id); } + if (flag & PASSMASK(POSITION)) { + const float3 position = sd->P; + kernel_write_pass_float3(buffer + kernel_data.film.pass_position, position); + } } - if (flag & PASSMASK(POSITION)) { - const float3 position = sd->P; - kernel_write_pass_float3(buffer + kernel_data.film.pass_position, position); - } if (flag & PASSMASK(NORMAL)) { const float3 normal = shader_bsdf_average_normal(kg, sd); kernel_write_pass_float3(buffer + kernel_data.film.pass_normal, normal); From eb1fed9d60a03cc5f9e648a1efaf89019bc2d8bd Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 25 Oct 2021 19:30:19 +0200 Subject: [PATCH 1192/1500] Cycles: restore Denoising Depth pass, when enabling Denoising Data passes This is still useful in some cases even if not used by OpenImageDenoise. In the future this may be replaced with a more generic system to control render passes and filtering, but for now this just does what it did before. --- intern/cycles/blender/blender_sync.cpp | 4 ++++ intern/cycles/kernel/kernel_passes.h | 8 ++++++++ intern/cycles/kernel/kernel_types.h | 4 +++- intern/cycles/render/film.cpp | 5 ++++- intern/cycles/render/pass.cpp | 4 ++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 9f5bbddbe77..e0a809b9307 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -541,6 +541,7 @@ static PassType get_blender_pass_type(BL::RenderPass &b_pass) MAP_PASS("Denoising Normal", PASS_DENOISING_NORMAL); MAP_PASS("Denoising Albedo", PASS_DENOISING_ALBEDO); + MAP_PASS("Denoising Depth", PASS_DENOISING_DEPTH); MAP_PASS("Shadow Catcher", PASS_SHADOW_CATCHER); MAP_PASS("Noisy Shadow Catcher", PASS_SHADOW_CATCHER); @@ -670,6 +671,9 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v b_engine.add_pass("Denoising Albedo", 3, "RGB", b_view_layer.name().c_str()); pass_add(scene, PASS_DENOISING_ALBEDO, "Denoising Albedo", PassMode::NOISY); + + b_engine.add_pass("Denoising Depth", 1, "Z", b_view_layer.name().c_str()); + pass_add(scene, PASS_DENOISING_DEPTH, "Denoising Depth", PassMode::NOISY); } /* Custom AOV passes. */ diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h index ab8d7a26f44..8ff7750c7d5 100644 --- a/intern/cycles/kernel/kernel_passes.h +++ b/intern/cycles/kernel/kernel_passes.h @@ -52,6 +52,14 @@ ccl_device_forceinline void kernel_write_denoising_features_surface( ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); + if (kernel_data.film.pass_denoising_depth != PASS_UNUSED) { + const float3 denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); + const float denoising_depth = ensure_finite(average(denoising_feature_throughput) * + sd->ray_length); + kernel_write_pass_float(buffer + kernel_data.film.pass_denoising_depth, denoising_depth); + } + float3 normal = zero_float3(); float3 diffuse_albedo = zero_float3(); float3 specular_albedo = zero_float3(); diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 5cbe2939dfc..df6b6b5be20 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -384,6 +384,7 @@ typedef enum PassType { PASS_MIST, PASS_DENOISING_NORMAL, PASS_DENOISING_ALBEDO, + PASS_DENOISING_DEPTH, /* PASS_SHADOW_CATCHER accumulates contribution of shadow catcher object which is not affected by * any other object. The pass accessor will divide the combined pass by the shadow catcher. The @@ -1031,6 +1032,7 @@ typedef struct KernelFilm { int pass_denoising_normal; int pass_denoising_albedo; + int pass_denoising_depth; int pass_aov_color; int pass_aov_value; @@ -1047,7 +1049,7 @@ typedef struct KernelFilm { int use_approximate_shadow_catcher; - int pad1, pad2, pad3; + int pad1, pad2; } KernelFilm; static_assert_align(KernelFilm, 16); diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 381f794545a..1f7882ea91e 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -339,6 +339,9 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) case PASS_DENOISING_ALBEDO: kfilm->pass_denoising_albedo = kfilm->pass_stride; break; + case PASS_DENOISING_DEPTH: + kfilm->pass_denoising_depth = kfilm->pass_stride; + break; case PASS_SHADOW_CATCHER: kfilm->pass_shadow_catcher = kfilm->pass_stride; @@ -665,7 +668,7 @@ uint Film::get_kernel_features(const Scene *scene) const const PassMode pass_mode = pass->get_mode(); if (pass_mode == PassMode::DENOISED || pass_type == PASS_DENOISING_NORMAL || - pass_type == PASS_DENOISING_ALBEDO) { + pass_type == PASS_DENOISING_ALBEDO || pass_type == PASS_DENOISING_DEPTH) { kernel_features |= KERNEL_FEATURE_DENOISING; } diff --git a/intern/cycles/render/pass.cpp b/intern/cycles/render/pass.cpp index 472c9fc0a82..cd044a16353 100644 --- a/intern/cycles/render/pass.cpp +++ b/intern/cycles/render/pass.cpp @@ -100,6 +100,7 @@ const NodeEnum *Pass::get_type_enum() pass_type_enum.insert("mist", PASS_MIST); pass_type_enum.insert("denoising_normal", PASS_DENOISING_NORMAL); pass_type_enum.insert("denoising_albedo", PASS_DENOISING_ALBEDO); + pass_type_enum.insert("denoising_depth", PASS_DENOISING_DEPTH); pass_type_enum.insert("shadow_catcher", PASS_SHADOW_CATCHER); pass_type_enum.insert("shadow_catcher_sample_count", PASS_SHADOW_CATCHER_SAMPLE_COUNT); @@ -294,6 +295,9 @@ PassInfo Pass::get_info(const PassType type, const bool include_albedo) case PASS_DENOISING_ALBEDO: pass_info.num_components = 3; break; + case PASS_DENOISING_DEPTH: + pass_info.num_components = 1; + break; case PASS_SHADOW_CATCHER: pass_info.num_components = 3; From 75704091fccb92774790f6451efe4e1d00174dad Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 21 Oct 2021 17:42:15 +0200 Subject: [PATCH 1193/1500] Cycles: add additive AO support through Fast GI settings Add a Fast GI Method, either Replace for the existing behavior, or Add to add ambient occlusion like the old world settings. This replaces the old Ambient Occlusion settings in the world properties. --- intern/cycles/blender/addon/properties.py | 13 ++++++ intern/cycles/blender/addon/ui.py | 8 +++- intern/cycles/blender/blender_shader.cpp | 41 +++++++++++++------ .../cycles/integrator/path_trace_work_gpu.cpp | 2 +- .../integrator/integrator_shade_surface.h | 31 +++++++++----- .../integrator_shadow_state_template.h | 5 ++- intern/cycles/kernel/kernel_accumulate.h | 8 +++- intern/cycles/kernel/kernel_shader.h | 11 ++++- intern/cycles/kernel/kernel_types.h | 8 +++- intern/cycles/render/film.cpp | 4 ++ intern/cycles/render/integrator.cpp | 13 ++++++ intern/cycles/render/integrator.h | 3 ++ intern/cycles/render/scene.cpp | 1 + source/blender/makesrna/intern/rna_world.c | 1 + 14 files changed, 119 insertions(+), 30 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 2a51e0be2a4..0f92238015d 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -125,6 +125,11 @@ enum_texture_limit = ( ('8192', "8192", "Limit texture size to 8192 pixels", 7), ) +enum_fast_gi_method = ( + ('REPLACE', "Replace", "Replace global illumination with ambient occlusion after a specified number of bounces"), + ('ADD', "Add", "Add ambient occlusion to diffuse surfaces"), +) + # NOTE: Identifiers are expected to be an upper case version of identifiers from `Pass::get_type_enum()` enum_view3d_shading_render_pass = ( ('', "General", ""), @@ -724,6 +729,14 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Approximate diffuse indirect light with background tinted ambient occlusion. This provides fast alternative to full global illumination, for interactive viewport rendering or final renders with reduced quality", default=False, ) + + fast_gi_method: EnumProperty( + name="Fast GI Method", + default='REPLACE', + description="Fast GI approximation method", + items=enum_fast_gi_method + ) + ao_bounces: IntProperty( name="AO Bounces", default=1, diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 0ed2dd24f2e..facf1b08676 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -465,8 +465,7 @@ class CYCLES_RENDER_PT_light_paths_fast_gi(CyclesButtonsPanel, Panel): layout.active = cscene.use_fast_gi col = layout.column(align=True) - col.prop(cscene, "ao_bounces", text="Viewport Bounces") - col.prop(cscene, "ao_bounces_render", text="Render Bounces") + col.prop(cscene, "fast_gi_method", text="Method") if world: light = world.light_settings @@ -474,6 +473,11 @@ class CYCLES_RENDER_PT_light_paths_fast_gi(CyclesButtonsPanel, Panel): col.prop(light, "ao_factor", text="AO Factor") col.prop(light, "distance", text="AO Distance") + if cscene.fast_gi_method == 'REPLACE': + col = layout.column(align=True) + col.prop(cscene, "ao_bounces", text="Viewport Bounces") + col.prop(cscene, "ao_bounces_render", text="Render Bounces") + class CYCLES_RENDER_PT_motion_blur(CyclesButtonsPanel, Panel): bl_label = "Motion Blur" diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index db5eadeed56..25e7ec71577 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -1375,6 +1375,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, { Background *background = scene->background; Integrator *integrator = scene->integrator; + PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); BL::World b_world = b_scene.world(); @@ -1466,14 +1467,8 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, graph->connect(background->output("Background"), out->input("Surface")); } + /* Visibility */ if (b_world) { - /* AO */ - BL::WorldLighting b_light = b_world.light_settings(); - - integrator->set_ao_factor(b_light.ao_factor()); - integrator->set_ao_distance(b_light.distance()); - - /* visibility */ PointerRNA cvisibility = RNA_pointer_get(&b_world.ptr, "cycles_visibility"); uint visibility = 0; @@ -1485,16 +1480,38 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, background->set_visibility(visibility); } - else { - integrator->set_ao_factor(1.0f); - integrator->set_ao_distance(10.0f); - } shader->set_graph(graph); shader->tag_update(scene); } - PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + /* Fast GI */ + if (b_world) { + BL::WorldLighting b_light = b_world.light_settings(); + enum { FAST_GI_METHOD_REPLACE = 0, FAST_GI_METHOD_ADD = 1, FAST_GI_METHOD_NUM }; + + const bool use_fast_gi = get_boolean(cscene, "use_fast_gi"); + if (use_fast_gi) { + const int fast_gi_method = get_enum( + cscene, "fast_gi_method", FAST_GI_METHOD_NUM, FAST_GI_METHOD_REPLACE); + integrator->set_ao_factor((fast_gi_method == FAST_GI_METHOD_REPLACE) ? b_light.ao_factor() : + 0.0f); + integrator->set_ao_additive_factor( + (fast_gi_method == FAST_GI_METHOD_ADD) ? b_light.ao_factor() : 0.0f); + } + else { + integrator->set_ao_factor(0.0f); + integrator->set_ao_additive_factor(0.0f); + } + + integrator->set_ao_distance(b_light.distance()); + } + else { + integrator->set_ao_factor(0.0f); + integrator->set_ao_additive_factor(0.0f); + integrator->set_ao_distance(10.0f); + } + background->set_transparent(b_scene.render().film_transparent()); if (background->get_transparent()) { diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 36f275e1075..605c1efca0f 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -1082,7 +1082,7 @@ bool PathTraceWorkGPU::kernel_creates_shadow_paths(DeviceKernel kernel) bool PathTraceWorkGPU::kernel_creates_ao_paths(DeviceKernel kernel) { - return (device_scene_->data.film.pass_ao != PASS_UNUSED) && + return (device_scene_->data.kernel_features & KERNEL_FEATURE_AO) && (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE); } diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index 3724b05c6b0..2a0bf4a3046 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -325,17 +325,25 @@ ccl_device_forceinline bool integrate_surface_volume_only_bounce(IntegratorState #endif #if defined(__AO__) -ccl_device_forceinline void integrate_surface_ao_pass( - KernelGlobals kg, - IntegratorState state, - ccl_private const ShaderData *ccl_restrict sd, - ccl_private const RNGState *ccl_restrict rng_state, - ccl_global float *ccl_restrict render_buffer) +ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const RNGState *ccl_restrict + rng_state, + ccl_global float *ccl_restrict render_buffer) { + if (!(kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) && + !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_CAMERA)) { + return; + } + float bsdf_u, bsdf_v; path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - const float3 ao_N = shader_bsdf_ao_normal(kg, sd); + float3 ao_N; + const float3 ao_weight = shader_bsdf_ao( + kg, sd, kernel_data.integrator.ao_additive_factor, &ao_N); + float3 ao_D; float ao_pdf; sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); @@ -379,6 +387,10 @@ ccl_device_forceinline void integrate_surface_ao_pass( INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, bounce) = bounce; INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transparent_bounce) = transparent_bounce; INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, throughput) = throughput; + + if (kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) { + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unshadowed_throughput) = ao_weight; + } } #endif /* defined(__AO__) */ @@ -487,10 +499,9 @@ ccl_device bool integrate_surface(KernelGlobals kg, #if defined(__AO__) /* Ambient occlusion pass. */ - if ((kernel_data.film.pass_ao != PASS_UNUSED) && - (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_CAMERA)) { + if (kernel_data.kernel_features & KERNEL_FEATURE_AO) { PROFILING_EVENT(PROFILING_SHADE_SURFACE_AO); - integrate_surface_ao_pass(kg, state, &sd, &rng_state, render_buffer); + integrate_surface_ao(kg, state, &sd, &rng_state, render_buffer); } #endif diff --git a/intern/cycles/kernel/integrator/integrator_shadow_state_template.h b/intern/cycles/kernel/integrator/integrator_shadow_state_template.h index bc35b644ee1..1fbadde2642 100644 --- a/intern/cycles/kernel/integrator/integrator_shadow_state_template.h +++ b/intern/cycles/kernel/integrator/integrator_shadow_state_template.h @@ -42,7 +42,10 @@ KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) /* Throughput. */ KERNEL_STRUCT_MEMBER(shadow_path, float3, throughput, KERNEL_FEATURE_PATH_TRACING) /* Throughput for shadow pass. */ -KERNEL_STRUCT_MEMBER(shadow_path, float3, unshadowed_throughput, KERNEL_FEATURE_SHADOW_PASS) +KERNEL_STRUCT_MEMBER(shadow_path, + float3, + unshadowed_throughput, + KERNEL_FEATURE_SHADOW_PASS | KERNEL_FEATURE_AO_ADDITIVE) /* Ratio of throughput to distinguish diffuse and glossy render passes. */ KERNEL_STRUCT_MEMBER(shadow_path, float3, diffuse_glossy_ratio, KERNEL_FEATURE_LIGHT_PASSES) /* Number of intersections found by ray-tracing. */ diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index 54492bef974..c494cb4e189 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -410,7 +410,13 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, /* Ambient occlusion. */ if (path_flag & PATH_RAY_SHADOW_FOR_AO) { - kernel_write_pass_float3(buffer + kernel_data.film.pass_ao, contribution); + if ((kernel_data.kernel_features & KERNEL_FEATURE_AO_PASS) && (path_flag & PATH_RAY_CAMERA)) { + kernel_write_pass_float3(buffer + kernel_data.film.pass_ao, contribution); + } + if (kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) { + const float3 ao_weight = INTEGRATOR_STATE(state, shadow_path, unshadowed_throughput); + kernel_accum_combined_pass(kg, path_flag, sample, contribution * ao_weight, buffer); + } return; } diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index d25191b72cf..22db6d0124e 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -457,19 +457,26 @@ ccl_device float3 shader_bsdf_average_normal(KernelGlobals kg, ccl_private const return (is_zero(N)) ? sd->N : normalize(N); } -ccl_device float3 shader_bsdf_ao_normal(KernelGlobals kg, ccl_private const ShaderData *sd) +ccl_device float3 shader_bsdf_ao(KernelGlobals kg, + ccl_private const ShaderData *sd, + const float ao_factor, + ccl_private float3 *N_) { + float3 eval = zero_float3(); float3 N = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { ccl_private const ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; + eval += sc->weight * ao_factor; N += bsdf->N * fabsf(average(sc->weight)); } } - return (is_zero(N)) ? sd->N : normalize(N); + *N_ = (is_zero(N)) ? sd->N : normalize(N); + return eval; } #ifdef __SUBSURFACE__ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index df6b6b5be20..94c27a1fca5 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1147,6 +1147,7 @@ typedef struct KernelIntegrator { int ao_bounces; float ao_bounces_distance; float ao_bounces_factor; + float ao_additive_factor; /* transparent */ int transparent_min_bounce; @@ -1179,7 +1180,7 @@ typedef struct KernelIntegrator { int has_shadow_catcher; /* padding */ - int pad1, pad2; + int pad1; } KernelIntegrator; static_assert_align(KernelIntegrator, 16); @@ -1561,6 +1562,11 @@ enum KernelFeatureFlag : unsigned int { /* Shadow render pass. */ KERNEL_FEATURE_SHADOW_PASS = (1U << 24U), + + /* AO. */ + KERNEL_FEATURE_AO_PASS = (1U << 25U), + KERNEL_FEATURE_AO_ADDITIVE = (1U << 26U), + KERNEL_FEATURE_AO = (KERNEL_FEATURE_AO_PASS | KERNEL_FEATURE_AO_ADDITIVE), }; /* Shader node feature mask, to specialize shader evaluation for kernels. */ diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 1f7882ea91e..bd18c777eb5 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -680,6 +680,10 @@ uint Film::get_kernel_features(const Scene *scene) const kernel_features |= KERNEL_FEATURE_SHADOW_PASS; } } + + if (pass_type == PASS_AO) { + kernel_features |= KERNEL_FEATURE_AO_PASS; + } } return kernel_features; diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp index d74d14242bb..16d9fc60fd3 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/render/integrator.cpp @@ -55,6 +55,7 @@ NODE_DEFINE(Integrator) SOCKET_INT(ao_bounces, "AO Bounces", 0); SOCKET_FLOAT(ao_factor, "AO Factor", 0.0f); SOCKET_FLOAT(ao_distance, "AO Distance", FLT_MAX); + SOCKET_FLOAT(ao_additive_factor, "AO Additive Factor", 0.0f); SOCKET_INT(volume_max_steps, "Volume Max Steps", 1024); SOCKET_FLOAT(volume_step_rate, "Volume Step Rate", 1.0f); @@ -159,6 +160,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene kintegrator->ao_bounces = ao_bounces; kintegrator->ao_bounces_distance = ao_distance; kintegrator->ao_bounces_factor = ao_factor; + kintegrator->ao_additive_factor = ao_additive_factor; /* Transparent Shadows * We only need to enable transparent shadows, if we actually have @@ -268,6 +270,17 @@ void Integrator::tag_update(Scene *scene, uint32_t flag) } } +uint Integrator::get_kernel_features(const Scene *scene) const +{ + uint kernel_features = 0; + + if (ao_additive_factor != 0.0f) { + kernel_features |= KERNEL_FEATURE_AO_ADDITIVE; + } + + return kernel_features; +} + AdaptiveSampling Integrator::get_adaptive_sampling() const { AdaptiveSampling adaptive_sampling; diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h index 5ad419e02ca..91efc25e51e 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -47,6 +47,7 @@ class Integrator : public Node { NODE_SOCKET_API(int, ao_bounces) NODE_SOCKET_API(float, ao_factor) NODE_SOCKET_API(float, ao_distance) + NODE_SOCKET_API(float, ao_additive_factor) NODE_SOCKET_API(int, volume_max_steps) NODE_SOCKET_API(float, volume_step_rate) @@ -101,6 +102,8 @@ class Integrator : public Node { void tag_update(Scene *scene, uint32_t flag); + uint get_kernel_features(const Scene *scene) const; + AdaptiveSampling get_adaptive_sampling() const; DenoiseParams get_denoise_params() const; }; diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index 7c5e1a86f5e..669f5abf7d6 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -522,6 +522,7 @@ void Scene::update_kernel_features() } kernel_features |= film->get_kernel_features(this); + kernel_features |= integrator->get_kernel_features(this); dscene.data.kernel_features = kernel_features; diff --git a/source/blender/makesrna/intern/rna_world.c b/source/blender/makesrna/intern/rna_world.c index 826e6d21c36..68f11d71de2 100644 --- a/source/blender/makesrna/intern/rna_world.c +++ b/source/blender/makesrna/intern/rna_world.c @@ -128,6 +128,7 @@ static void rna_def_lighting(BlenderRNA *brna) prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "aodist"); + RNA_def_property_range(prop, 0, FLT_MAX); RNA_def_property_ui_text( prop, "Distance", "Length of rays, defines how far away other faces give occlusion effect"); RNA_def_property_update(prop, 0, "rna_World_update"); From af26720b2131e5a0db96965b231f4dfa2d5c5a2d Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Tue, 26 Oct 2021 14:59:50 +0200 Subject: [PATCH 1194/1500] UI: Use text highlight theme color for active tab Currently, both inactive and active tabs are using the `Region Text` theme property. This patch makes it so active tabs use `Region Text Highlight`. Since this check is done in other places already but was simply missing in this case, I believe this was just an oversight and not a design decision. Top is master, bottom is this patch: {F11520838, size=full} This allows this kind of tab highlight, not possible before since all tabs would have white text. {F11520873, size=full} Reviewed By: #user_interface, Severin Differential Revision: https://developer.blender.org/D13003 --- .../presets/interface_theme/Blender_Light.xml | 38 +++++++++---------- .../editors/interface/interface_panel.c | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/release/scripts/presets/interface_theme/Blender_Light.xml b/release/scripts/presets/interface_theme/Blender_Light.xml index f4188e83e34..e3ac77b008d 100644 --- a/release/scripts/presets/interface_theme/Blender_Light.xml +++ b/release/scripts/presets/interface_theme/Blender_Light.xml @@ -434,7 +434,7 @@ button="#99999900" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#b3b3b3" @@ -509,7 +509,7 @@ button="#999999e6" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -555,7 +555,7 @@ button="#999999e6" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#999999e6" tab_active="#6697e6" @@ -613,7 +613,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -688,7 +688,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -773,7 +773,7 @@ button="#99999900" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#b3b3b3" @@ -833,7 +833,7 @@ button="#99999900" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#b3b3b3" @@ -870,7 +870,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#656565ff" execution_buts="#00000000" tab_active="#6697e6" @@ -917,7 +917,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -981,7 +981,7 @@ button="#99999900" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#b3b3b3" @@ -1032,7 +1032,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -1081,7 +1081,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -1115,7 +1115,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#b3b3b3ff" execution_buts="#b3b3b3ff" tab_active="#6697e6" @@ -1156,7 +1156,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -1221,7 +1221,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -1263,8 +1263,8 @@ header_text_hi="#ffffff" button="#2f303599" button_title="#ffffff" - button_text="#ffffff" - button_text_hi="#ffffff" + button_text="#000000" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#446499" @@ -1298,7 +1298,7 @@ button="#2f303500" button_title="#ffffff" button_text="#ffffff" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#446499" @@ -1334,7 +1334,7 @@ button="#999999e6" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#999999e6" tab_active="#6697e6" diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index a22351eea7e..072362492d8 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1550,7 +1550,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) } BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f); - BLF_color3ubv(fontid, theme_col_text); + BLF_color3ubv(fontid, is_active ? theme_col_text_hi : theme_col_text); BLF_draw(fontid, category_id_draw, category_draw_len); GPU_blend(GPU_BLEND_NONE); From b8b9023d8cd7e0be93f83533c37f50931a2a7c56 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 26 Oct 2021 10:07:49 -0300 Subject: [PATCH 1195/1500] Fix drop of dimensionless objects in 3DView The matrix and location were not being calculated in this case. --- source/blender/editors/space_view3d/space_view3d.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index ccad1b8b552..8e61ff1765a 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -524,6 +524,7 @@ static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag) } state = drop->draw_data = ED_view3d_cursor_snap_active(); + state->draw_plane = true; float dimensions[3] = {0.0f}; if (drag->type == WM_DRAG_ID) { @@ -678,6 +679,7 @@ static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) RNA_boolean_set(drop->ptr, "duplicate", !is_imported_id); V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + BLI_assert(snap_state->draw_box || snap_state->draw_plane); Object *ob = (Object *)id; float obmat_final[4][4]; From fd477e738dfd65f7bb0f06c1d91c3259ed26295d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 26 Oct 2021 15:32:01 +0200 Subject: [PATCH 1196/1500] Geometry Nodes: remove reference to anonymous attributes in tooltips This changes socket inspection for fields according to T91881. Differential Revision: https://developer.blender.org/D13006 --- source/blender/blenkernel/BKE_geometry_set.hh | 19 ++++++++++++++----- .../blenkernel/intern/attribute_access.cc | 4 ++-- .../blender/editors/space_node/node_draw.cc | 12 ++++++------ source/blender/functions/FN_field.hh | 16 ++++++++++++++++ source/blender/functions/intern/field.cc | 1 + source/blender/nodes/NOD_geometry_exec.hh | 2 ++ source/blender/nodes/NOD_node_tree_ref.hh | 16 ++++++++++++++++ .../nodes/node_geo_attribute_capture.cc | 6 +++--- .../node_geo_curve_endpoint_selection.cc | 5 ++++- .../node_geo_curve_handle_type_selection.cc | 3 ++- .../nodes/node_geo_curve_parameter.cc | 3 ++- .../nodes/node_geo_curve_to_points.cc | 9 ++++++--- .../node_geo_distribute_points_on_faces.cc | 10 ++++++---- .../geometry/nodes/node_geo_input_normal.cc | 3 ++- .../nodes/node_geo_input_spline_length.cc | 3 ++- .../geometry/nodes/node_geo_input_tangent.cc | 3 ++- .../nodes/node_geo_material_selection.cc | 3 ++- .../nodes/node_geo_transfer_attribute.cc | 3 ++- .../nodes/intern/geometry_nodes_eval_log.cc | 18 ++++++++++++++++-- .../nodes/intern/node_geometry_exec.cc | 5 +++++ 20 files changed, 111 insertions(+), 33 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index f57765e373b..78ccefaed5c 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -736,6 +736,7 @@ class AttributeFieldInput : public fn::FieldInput { AttributeFieldInput(std::string name, const CPPType &type) : fn::FieldInput(type, name), name_(std::move(name)) { + category_ = Category::NamedAttribute; } template static fn::Field Create(std::string name) @@ -764,6 +765,7 @@ class IDAttributeFieldInput : public fn::FieldInput { public: IDAttributeFieldInput() : fn::FieldInput(CPPType::get()) { + category_ = Category::Generated; } static fn::Field Create(); @@ -785,18 +787,25 @@ class AnonymousAttributeFieldInput : public fn::FieldInput { * automatically. */ StrongAnonymousAttributeID anonymous_id_; + std::string producer_name_; public: - AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, const CPPType &type) - : fn::FieldInput(type, anonymous_id.debug_name()), anonymous_id_(std::move(anonymous_id)) + AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, + const CPPType &type, + std::string producer_name) + : fn::FieldInput(type, anonymous_id.debug_name()), + anonymous_id_(std::move(anonymous_id)), + producer_name_(producer_name) { + category_ = Category::AnonymousAttribute; } - template static fn::Field Create(StrongAnonymousAttributeID anonymous_id) + template + static fn::Field Create(StrongAnonymousAttributeID anonymous_id, std::string producer_name) { const CPPType &type = CPPType::get(); - auto field_input = std::make_shared(std::move(anonymous_id), - type); + auto field_input = std::make_shared( + std::move(anonymous_id), type, std::move(producer_name)); return fn::Field{field_input}; } diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 3386d346364..1ea7f522bef 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -1385,7 +1385,7 @@ const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContex std::string AttributeFieldInput::socket_inspection_name() const { std::stringstream ss; - ss << TIP_("Attribute: ") << name_; + ss << '"' << name_ << '"' << TIP_(" attribute from geometry"); return ss.str(); } @@ -1468,7 +1468,7 @@ const GVArray *AnonymousAttributeFieldInput::get_varray_for_context( std::string AnonymousAttributeFieldInput::socket_inspection_name() const { std::stringstream ss; - ss << TIP_("Anonymous Attribute: ") << debug_name_; + ss << '"' << debug_name_ << '"' << TIP_(" from ") << producer_name_; return ss.str(); } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 3a307777d0a..9e91d394fef 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -907,22 +907,22 @@ static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &v } else { if (type.is()) { - ss << TIP_("Integer Field"); + ss << TIP_("Integer field"); } else if (type.is()) { - ss << TIP_("Float Field"); + ss << TIP_("Float field"); } else if (type.is()) { - ss << TIP_("Vector Field"); + ss << TIP_("Vector field"); } else if (type.is()) { - ss << TIP_("Boolean Field"); + ss << TIP_("Boolean field"); } else if (type.is()) { - ss << TIP_("String Field"); + ss << TIP_("String field"); } else if (type.is()) { - ss << TIP_("Color Field"); + ss << TIP_("Color field"); } ss << TIP_(" based on:\n"); diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index ed5064fdf25..5e0302130af 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -227,9 +227,19 @@ class FieldContext; * A #FieldNode that represents an input to the entire field-tree. */ class FieldInput : public FieldNode { + public: + /* The order is also used for sorting in socket inspection. */ + enum class Category { + NamedAttribute = 0, + Generated = 1, + AnonymousAttribute = 2, + Unknown, + }; + protected: const CPPType *type_; std::string debug_name_; + Category category_ = Category::Unknown; public: FieldInput(const CPPType &type, std::string debug_name = ""); @@ -245,6 +255,7 @@ class FieldInput : public FieldNode { virtual std::string socket_inspection_name() const; blender::StringRef debug_name() const; const CPPType &cpp_type() const; + Category category() const; const CPPType &output_cpp_type(int output_index) const override; void foreach_field_input(FunctionRef foreach_fn) const override; @@ -527,6 +538,11 @@ inline const CPPType &FieldInput::cpp_type() const return *type_; } +inline FieldInput::Category FieldInput::category() const +{ + return category_; +} + inline const CPPType &FieldInput::output_cpp_type(int output_index) const { BLI_assert(output_index == 0); diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 5c13db9ec17..4de5e71c910 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -523,6 +523,7 @@ const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input, IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get(), "Index") { + category_ = Category::Generated; } GVArray *IndexFieldInput::get_index_varray(IndexMask mask, ResourceScope &scope) diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 962e1c3c48f..f5775b8a6e0 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -335,6 +335,8 @@ class GeoNodeExecParams { const GeometryComponent &component, const AttributeDomain default_domain) const; + std::string attribute_producer_name() const; + private: /* Utilities for detecting common errors at when using this class. */ void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const; diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index b6e372470c8..9f9dcc69376 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -200,6 +200,8 @@ class NodeRef : NonCopyable, NonMovable { PointerRNA *rna() const; StringRefNull idname() const; StringRefNull name() const; + StringRefNull label() const; + StringRefNull label_or_name() const; bNodeType *typeinfo() const; const NodeDeclaration *declaration() const; @@ -575,6 +577,20 @@ inline StringRefNull NodeRef::name() const return bnode_->name; } +inline StringRefNull NodeRef::label() const +{ + return bnode_->label; +} + +inline StringRefNull NodeRef::label_or_name() const +{ + const StringRefNull label = this->label(); + if (!label.is_empty()) { + return label; + } + return this->name(); +} + inline bNodeType *NodeRef::typeinfo() const { return bnode_->typeinfo; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index b7352160f89..8ad70cd6d3f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -144,7 +144,7 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params) break; } - WeakAnonymousAttributeID anonymous_id{"Attribute Capture"}; + WeakAnonymousAttributeID anonymous_id{"Attribute"}; const CPPType &type = field.cpp_type(); static const Array types = { @@ -158,8 +158,8 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params) } }); - GField output_field{ - std::make_shared(std::move(anonymous_id), type)}; + GField output_field{std::make_shared( + std::move(anonymous_id), type, params.attribute_producer_name())}; switch (data_type) { case CD_PROP_FLOAT: { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc index ee6cf055ecb..fbe5af3bb18 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -56,8 +56,11 @@ class EndpointFieldInput final : public fn::FieldInput { public: EndpointFieldInput(Field start_size, Field end_size) - : FieldInput(CPPType::get(), "Selection"), start_size_(start_size), end_size_(end_size) + : FieldInput(CPPType::get(), "Endpoint Selection node"), + start_size_(start_size), + end_size_(end_size) { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index b565b1e4602..3ae330fd5cd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -91,8 +91,9 @@ class HandleTypeFieldInput final : public fn::FieldInput { public: HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode) - : FieldInput(CPPType::get(), "Selection"), type_(type), mode_(mode) + : FieldInput(CPPType::get(), "Handle Type Selection node"), type_(type), mode_(mode) { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc index 90853387ec7..938f5f22a03 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc @@ -150,8 +150,9 @@ static const GVArray *construct_curve_parameter_gvarray(const CurveEval &curve, class CurveParameterFieldInput final : public fn::FieldInput { public: - CurveParameterFieldInput() : fn::FieldInput(CPPType::get(), "Curve Parameter") + CurveParameterFieldInput() : fn::FieldInput(CPPType::get(), "Curve Parameter node") { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 4f4fde6c7df..e43e1fa6c75 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -357,17 +357,20 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params) if (attribute_outputs.tangent_id) { params.set_output( "Tangent", - AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.tangent_id))); + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.tangent_id), + params.attribute_producer_name())); } if (attribute_outputs.normal_id) { params.set_output( "Normal", - AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.normal_id))); + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.normal_id), + params.attribute_producer_name())); } if (attribute_outputs.rotation_id) { params.set_output( "Rotation", - AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.rotation_id))); + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.rotation_id), + params.attribute_producer_name())); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 0d481011f00..c267815226a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -543,10 +543,10 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par AttributeOutputs attribute_outputs; if (params.output_is_required("Normal")) { - attribute_outputs.normal_id = StrongAnonymousAttributeID("normal"); + attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); } if (params.output_is_required("Rotation")) { - attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation"); + attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); } geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { @@ -562,12 +562,14 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par if (attribute_outputs.normal_id) { params.set_output( "Normal", - AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.normal_id))); + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.normal_id), + params.attribute_producer_name())); } if (attribute_outputs.rotation_id) { params.set_output( "Rotation", - AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.rotation_id))); + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.rotation_id), + params.attribute_producer_name())); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc index 5a2495afb9e..3387f8d6c6e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -241,8 +241,9 @@ static const GVArray *construct_curve_normal_gvarray(const CurveComponent &compo class NormalFieldInput final : public fn::FieldInput { public: - NormalFieldInput() : fn::FieldInput(CPPType::get(), "Normal") + NormalFieldInput() : fn::FieldInput(CPPType::get(), "Normal node") { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index b5f3e1b0c28..ec502f7f1bc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -57,8 +57,9 @@ static const GVArray *construct_spline_length_gvarray(const CurveComponent &comp class SplineLengthFieldInput final : public fn::FieldInput { public: - SplineLengthFieldInput() : fn::FieldInput(CPPType::get(), "Spline Length") + SplineLengthFieldInput() : fn::FieldInput(CPPType::get(), "Spline Length node") { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index d690642373a..f746edbbca2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -121,8 +121,9 @@ static const GVArray *construct_curve_tangent_gvarray(const CurveComponent &comp class TangentFieldInput final : public fn::FieldInput { public: - TangentFieldInput() : fn::FieldInput(CPPType::get(), "Tangent") + TangentFieldInput() : fn::FieldInput(CPPType::get(), "Tangent node") { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index 9d4533b9bda..7dffc7793e5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -59,8 +59,9 @@ class MaterialSelectionFieldInput final : public fn::FieldInput { public: MaterialSelectionFieldInput(Material *material) - : fn::FieldInput(CPPType::get(), "Material Selection"), material_(material) + : fn::FieldInput(CPPType::get(), "Material Selection node"), material_(material) { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 1c287a0f1bf..5350b14b53b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -628,13 +628,14 @@ class IndexTransferFieldInput : public FieldInput { GField src_field, Field index_field, const AttributeDomain domain) - : FieldInput(src_field.cpp_type(), "Attribute Transfer Index"), + : FieldInput(src_field.cpp_type(), "Attribute Transfer node"), src_geometry_(std::move(geometry)), src_field_(std::move(src_field)), index_field_(std::move(index_field)), domain_(domain) { src_geometry_.ensure_owns_direct_data(); + category_ = Category::Generated; } const GVArray *get_varray_for_context(const FieldContext &context, diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 13dc73c7206..ddd3c991518 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -177,9 +177,23 @@ const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket GFieldValueLog::GFieldValueLog(fn::GField field, bool log_full_field) : type_(field.cpp_type()) { - VectorSet> field_inputs; + Set> field_inputs_set; field.node().foreach_field_input( - [&](const FieldInput &field_input) { field_inputs.add(field_input); }); + [&](const FieldInput &field_input) { field_inputs_set.add(field_input); }); + + Vector> field_inputs; + field_inputs.extend(field_inputs_set.begin(), field_inputs_set.end()); + + std::sort( + field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) { + const int index_a = (int)a.category(); + const int index_b = (int)b.category(); + if (index_a == index_b) { + return a.socket_inspection_name().size() < b.socket_inspection_name().size(); + } + return index_a < index_b; + }); + for (const FieldInput &field_input : field_inputs) { input_tooltips_.append(field_input.socket_inspection_name()); } diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index a3bbca90731..8ea085d42f9 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -183,6 +183,11 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( return default_domain; } +std::string GeoNodeExecParams::attribute_producer_name() const +{ + return provider_->dnode->label_or_name() + TIP_(" node"); +} + void GeoNodeExecParams::check_input_access(StringRef identifier, const CPPType *requested_type) const { From b698fe1e047e56e8ed67ba47464c0017d9c50eea Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 26 Oct 2021 15:30:12 +0200 Subject: [PATCH 1197/1500] Cleanup: compiler warnings --- intern/cycles/integrator/path_trace_work_gpu.cpp | 4 ++-- intern/cycles/render/integrator.cpp | 2 +- intern/cycles/render/integrator.h | 2 +- intern/cycles/render/scene.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 605c1efca0f..775cee57a90 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -120,7 +120,7 @@ void PathTraceWorkGPU::alloc_integrator_soa() * TODO: store float3 in separate XYZ arrays. */ #define KERNEL_STRUCT_BEGIN(name) for (int array_index = 0;; array_index++) { #define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) \ - if ((kernel_features & feature) && (integrator_state_gpu_.parent_struct.name == nullptr)) { \ + if ((kernel_features & (feature)) && (integrator_state_gpu_.parent_struct.name == nullptr)) { \ device_only_memory *array = new device_only_memory(device_, \ "integrator_state_" #name); \ array->alloc_to_device(max_num_paths_); \ @@ -128,7 +128,7 @@ void PathTraceWorkGPU::alloc_integrator_soa() integrator_state_gpu_.parent_struct.name = (type *)array->device_pointer; \ } #define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) \ - if ((kernel_features & feature) && \ + if ((kernel_features & (feature)) && \ (integrator_state_gpu_.parent_struct[array_index].name == nullptr)) { \ device_only_memory *array = new device_only_memory(device_, \ "integrator_state_" #name); \ diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp index 16d9fc60fd3..06fa7339b51 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/render/integrator.cpp @@ -270,7 +270,7 @@ void Integrator::tag_update(Scene *scene, uint32_t flag) } } -uint Integrator::get_kernel_features(const Scene *scene) const +uint Integrator::get_kernel_features() const { uint kernel_features = 0; diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h index 91efc25e51e..468971986c5 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -102,7 +102,7 @@ class Integrator : public Node { void tag_update(Scene *scene, uint32_t flag); - uint get_kernel_features(const Scene *scene) const; + uint get_kernel_features() const; AdaptiveSampling get_adaptive_sampling() const; DenoiseParams get_denoise_params() const; diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index 669f5abf7d6..20081677251 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -522,7 +522,7 @@ void Scene::update_kernel_features() } kernel_features |= film->get_kernel_features(this); - kernel_features |= integrator->get_kernel_features(this); + kernel_features |= integrator->get_kernel_features(); dscene.data.kernel_features = kernel_features; From d7d40745fa09061a3117bd3669c5a46bbf611eae Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 24 Oct 2021 14:19:19 +0200 Subject: [PATCH 1198/1500] Cycles: changes to source code folders structure * Split render/ into scene/ and session/. The scene/ folder now contains the scene and its nodes. The session/ folder contains the render session and associated data structures like drivers and render buffers. * Move top level kernel headers into new folders kernel/camera/, kernel/film/, kernel/light/, kernel/sample/, kernel/util/ * Move integrator related kernel headers into kernel/integrator/ * Move OSL shaders from kernel/shaders/ to kernel/osl/shaders/ For patches and branches, git merge and rebase should be able to detect the renames and move over code to the right file. --- intern/cycles/CMakeLists.txt | 3 +- intern/cycles/app/CMakeLists.txt | 3 +- intern/cycles/app/cycles_standalone.cpp | 10 +- intern/cycles/app/cycles_xml.cpp | 24 ++-- intern/cycles/app/oiio_output_driver.h | 2 +- intern/cycles/blender/CMakeLists.txt | 3 +- intern/cycles/blender/blender_camera.cpp | 4 +- intern/cycles/blender/blender_curves.cpp | 13 +- .../cycles/blender/blender_display_driver.h | 2 +- intern/cycles/blender/blender_geometry.cpp | 10 +- intern/cycles/blender/blender_id_map.h | 4 +- intern/cycles/blender/blender_image.h | 2 +- intern/cycles/blender/blender_light.cpp | 2 +- intern/cycles/blender/blender_mesh.cpp | 11 +- intern/cycles/blender/blender_object.cpp | 22 +-- intern/cycles/blender/blender_object_cull.cpp | 2 +- intern/cycles/blender/blender_output_driver.h | 2 +- intern/cycles/blender/blender_particles.cpp | 6 +- intern/cycles/blender/blender_python.cpp | 6 +- intern/cycles/blender/blender_session.cpp | 26 ++-- intern/cycles/blender/blender_session.h | 6 +- intern/cycles/blender/blender_shader.cpp | 18 +-- intern/cycles/blender/blender_sync.cpp | 26 ++-- intern/cycles/blender/blender_sync.h | 4 +- intern/cycles/blender/blender_util.h | 2 +- intern/cycles/blender/blender_viewport.cpp | 2 +- intern/cycles/blender/blender_viewport.h | 2 +- intern/cycles/blender/blender_volume.cpp | 10 +- intern/cycles/bvh/CMakeLists.txt | 2 +- intern/cycles/bvh/bvh2.cpp | 6 +- intern/cycles/bvh/bvh_build.cpp | 10 +- intern/cycles/bvh/bvh_embree.cpp | 8 +- intern/cycles/bvh/bvh_split.cpp | 6 +- intern/cycles/bvh/bvh_unaligned.cpp | 4 +- intern/cycles/device/cpu/device_impl.cpp | 2 +- intern/cycles/device/cuda/device_impl.cpp | 2 - intern/cycles/device/device_denoise.h | 2 +- .../cycles/device/device_graphics_interop.h | 2 +- intern/cycles/device/hip/device_impl.cpp | 2 - intern/cycles/device/multi/device.cpp | 3 +- intern/cycles/device/optix/device_impl.cpp | 13 +- intern/cycles/integrator/CMakeLists.txt | 4 +- intern/cycles/integrator/denoiser.cpp | 2 +- intern/cycles/integrator/denoiser_device.cpp | 2 +- intern/cycles/integrator/denoiser_oidn.cpp | 2 +- intern/cycles/integrator/pass_accessor.cpp | 2 +- intern/cycles/integrator/pass_accessor.h | 2 +- .../cycles/integrator/pass_accessor_cpu.cpp | 4 +- .../cycles/integrator/pass_accessor_gpu.cpp | 2 +- intern/cycles/integrator/path_trace.cpp | 6 +- intern/cycles/integrator/path_trace.h | 2 +- .../cycles/integrator/path_trace_display.cpp | 2 +- intern/cycles/integrator/path_trace_display.h | 2 +- intern/cycles/integrator/path_trace_tile.cpp | 8 +- intern/cycles/integrator/path_trace_tile.h | 2 +- intern/cycles/integrator/path_trace_work.cpp | 6 +- intern/cycles/integrator/path_trace_work.h | 4 +- .../cycles/integrator/path_trace_work_cpu.cpp | 6 +- .../cycles/integrator/path_trace_work_gpu.cpp | 4 +- intern/cycles/integrator/render_scheduler.cpp | 4 +- intern/cycles/integrator/render_scheduler.h | 2 +- .../cycles/integrator/work_tile_scheduler.cpp | 2 +- intern/cycles/kernel/CMakeLists.txt | 58 ++++---- .../kernel/{kernel_bake.h => bake/bake.h} | 5 +- .../{kernel_camera.h => camera/camera.h} | 8 +- .../camera_projection.h} | 0 .../kernel/closure/bsdf_ashikhmin_velvet.h | 2 +- intern/cycles/kernel/closure/bsdf_diffuse.h | 2 + .../cycles/kernel/closure/bsdf_diffuse_ramp.h | 2 + .../kernel/closure/bsdf_hair_principled.h | 2 +- .../cycles/kernel/closure/bsdf_microfacet.h | 4 +- .../kernel/closure/bsdf_microfacet_multi.h | 3 + .../kernel/closure/bsdf_principled_diffuse.h | 2 + intern/cycles/kernel/closure/bsdf_util.h | 106 ++++++++++++++ intern/cycles/kernel/device/cpu/globals.h | 2 +- .../kernel/device/cpu/kernel_arch_impl.h | 9 +- intern/cycles/kernel/device/cuda/globals.h | 3 +- intern/cycles/kernel/device/gpu/kernel.h | 9 +- .../gpu/work_stealing.h} | 0 intern/cycles/kernel/device/hip/globals.h | 3 +- intern/cycles/kernel/device/optix/globals.h | 3 +- .../film_accumulate.h} | 8 +- .../film_adaptive_sampling.h} | 2 +- .../film_id_passes.h} | 0 .../{kernel_passes.h => film/film_passes.h} | 4 +- .../{kernel_film.h => film/film_read.h} | 0 .../film_write_passes.h} | 0 intern/cycles/kernel/geom/geom_patch.h | 2 + intern/cycles/kernel/geom/geom_primitive.h | 2 +- .../kernel/geom/geom_triangle_intersect.h | 2 +- .../integrator/integrator_init_from_bake.h | 13 +- .../integrator/integrator_init_from_camera.h | 15 +- .../integrator/integrator_intersect_closest.h | 13 +- .../integrator_intersect_volume_stack.h | 2 +- .../integrator_path_state.h} | 2 +- .../integrator/integrator_shade_background.h | 9 +- .../integrator/integrator_shade_light.h | 8 +- .../integrator/integrator_shade_shadow.h | 3 +- .../integrator/integrator_shade_surface.h | 15 +- .../integrator/integrator_shade_volume.h | 15 +- .../integrator_shader_eval.h} | 5 +- .../integrator_shadow_catcher.h} | 2 +- .../kernel/integrator/integrator_state_util.h | 3 +- .../kernel/integrator/integrator_subsurface.h | 6 +- .../integrator_subsurface_random_walk.h | 2 +- intern/cycles/kernel/kernel_math.h | 25 ---- intern/cycles/kernel/kernel_types.h | 10 +- .../kernel/{kernel_light.h => light/light.h} | 9 +- .../light_background.h} | 2 +- .../light_common.h} | 2 +- .../light_sample.h} | 10 +- intern/cycles/kernel/osl/CMakeLists.txt | 2 +- .../cycles/kernel/osl/bsdf_diffuse_ramp.cpp | 1 - intern/cycles/kernel/osl/osl_bssrdf.cpp | 1 - intern/cycles/kernel/osl/osl_closures.cpp | 2 - intern/cycles/kernel/osl/osl_services.cpp | 23 +-- intern/cycles/kernel/osl/osl_shader.cpp | 5 +- .../kernel/{ => osl}/shaders/CMakeLists.txt | 0 .../shaders/node_absorption_volume.osl | 0 .../{ => osl}/shaders/node_add_closure.osl | 0 .../shaders/node_ambient_occlusion.osl | 0 .../shaders/node_anisotropic_bsdf.osl | 0 .../{ => osl}/shaders/node_attribute.osl | 0 .../{ => osl}/shaders/node_background.osl | 0 .../kernel/{ => osl}/shaders/node_bevel.osl | 0 .../{ => osl}/shaders/node_blackbody.osl | 0 .../{ => osl}/shaders/node_brick_texture.osl | 0 .../{ => osl}/shaders/node_brightness.osl | 0 .../kernel/{ => osl}/shaders/node_bump.osl | 0 .../kernel/{ => osl}/shaders/node_camera.osl | 0 .../shaders/node_checker_texture.osl | 0 .../kernel/{ => osl}/shaders/node_clamp.osl | 0 .../kernel/{ => osl}/shaders/node_color.h | 0 .../{ => osl}/shaders/node_combine_hsv.osl | 0 .../{ => osl}/shaders/node_combine_rgb.osl | 0 .../{ => osl}/shaders/node_combine_xyz.osl | 0 .../shaders/node_convert_from_color.osl | 0 .../shaders/node_convert_from_float.osl | 0 .../shaders/node_convert_from_int.osl | 0 .../shaders/node_convert_from_normal.osl | 0 .../shaders/node_convert_from_point.osl | 0 .../shaders/node_convert_from_string.osl | 0 .../shaders/node_convert_from_vector.osl | 0 .../{ => osl}/shaders/node_diffuse_bsdf.osl | 0 .../{ => osl}/shaders/node_displacement.osl | 0 .../{ => osl}/shaders/node_emission.osl | 0 .../shaders/node_environment_texture.osl | 0 .../{ => osl}/shaders/node_float_curve.osl | 0 .../kernel/{ => osl}/shaders/node_fresnel.h | 0 .../kernel/{ => osl}/shaders/node_fresnel.osl | 0 .../kernel/{ => osl}/shaders/node_gamma.osl | 0 .../{ => osl}/shaders/node_geometry.osl | 0 .../{ => osl}/shaders/node_glass_bsdf.osl | 0 .../{ => osl}/shaders/node_glossy_bsdf.osl | 0 .../shaders/node_gradient_texture.osl | 0 .../{ => osl}/shaders/node_hair_bsdf.osl | 0 .../{ => osl}/shaders/node_hair_info.osl | 0 .../kernel/{ => osl}/shaders/node_hash.h | 0 .../kernel/{ => osl}/shaders/node_holdout.osl | 0 .../kernel/{ => osl}/shaders/node_hsv.osl | 0 .../{ => osl}/shaders/node_ies_light.osl | 0 .../{ => osl}/shaders/node_image_texture.osl | 0 .../kernel/{ => osl}/shaders/node_invert.osl | 0 .../{ => osl}/shaders/node_layer_weight.osl | 0 .../{ => osl}/shaders/node_light_falloff.osl | 0 .../{ => osl}/shaders/node_light_path.osl | 0 .../{ => osl}/shaders/node_magic_texture.osl | 0 .../{ => osl}/shaders/node_map_range.osl | 0 .../kernel/{ => osl}/shaders/node_mapping.osl | 0 .../kernel/{ => osl}/shaders/node_math.h | 0 .../kernel/{ => osl}/shaders/node_math.osl | 0 .../kernel/{ => osl}/shaders/node_mix.osl | 0 .../{ => osl}/shaders/node_mix_closure.osl | 0 .../shaders/node_musgrave_texture.osl | 0 .../kernel/{ => osl}/shaders/node_noise.h | 0 .../{ => osl}/shaders/node_noise_texture.osl | 0 .../kernel/{ => osl}/shaders/node_normal.osl | 0 .../{ => osl}/shaders/node_normal_map.osl | 0 .../{ => osl}/shaders/node_object_info.osl | 0 .../shaders/node_output_displacement.osl | 0 .../{ => osl}/shaders/node_output_surface.osl | 0 .../{ => osl}/shaders/node_output_volume.osl | 0 .../{ => osl}/shaders/node_particle_info.osl | 0 .../shaders/node_principled_bsdf.osl | 0 .../shaders/node_principled_hair_bsdf.osl | 0 .../shaders/node_principled_volume.osl | 0 .../kernel/{ => osl}/shaders/node_ramp_util.h | 0 .../shaders/node_refraction_bsdf.osl | 0 .../{ => osl}/shaders/node_rgb_curves.osl | 0 .../{ => osl}/shaders/node_rgb_ramp.osl | 0 .../{ => osl}/shaders/node_rgb_to_bw.osl | 0 .../{ => osl}/shaders/node_scatter_volume.osl | 0 .../{ => osl}/shaders/node_separate_hsv.osl | 0 .../{ => osl}/shaders/node_separate_rgb.osl | 0 .../{ => osl}/shaders/node_separate_xyz.osl | 0 .../{ => osl}/shaders/node_set_normal.osl | 0 .../{ => osl}/shaders/node_sky_texture.osl | 0 .../shaders/node_subsurface_scattering.osl | 0 .../kernel/{ => osl}/shaders/node_tangent.osl | 0 .../shaders/node_texture_coordinate.osl | 0 .../{ => osl}/shaders/node_toon_bsdf.osl | 0 .../shaders/node_translucent_bsdf.osl | 0 .../shaders/node_transparent_bsdf.osl | 0 .../kernel/{ => osl}/shaders/node_uv_map.osl | 0 .../kernel/{ => osl}/shaders/node_value.osl | 0 .../{ => osl}/shaders/node_vector_curves.osl | 0 .../shaders/node_vector_displacement.osl | 0 .../{ => osl}/shaders/node_vector_math.osl | 0 .../{ => osl}/shaders/node_vector_rotate.osl | 0 .../shaders/node_vector_transform.osl | 0 .../{ => osl}/shaders/node_velvet_bsdf.osl | 0 .../{ => osl}/shaders/node_vertex_color.osl | 0 .../shaders/node_voronoi_texture.osl | 0 .../{ => osl}/shaders/node_voxel_texture.osl | 0 .../{ => osl}/shaders/node_wave_texture.osl | 0 .../{ => osl}/shaders/node_wavelength.osl | 0 .../shaders/node_white_noise_texture.osl | 0 .../{ => osl}/shaders/node_wireframe.osl | 0 .../kernel/{ => osl}/shaders/stdcycles.h | 0 .../sample_jitter.h} | 0 intern/cycles/kernel/sample/sample_lcg.h | 51 +++++++ .../sample_mapping.h} | 131 ------------------ intern/cycles/kernel/sample/sample_mis.h | 64 +++++++++ .../sample_pattern.h} | 33 +---- intern/cycles/kernel/svm/svm_aov.h | 2 +- intern/cycles/kernel/svm/svm_bevel.h | 4 +- intern/cycles/kernel/svm/svm_displace.h | 2 +- intern/cycles/kernel/svm/svm_tex_coord.h | 4 +- .../{kernel_color.h => util/util_color.h} | 0 .../util_differential.h} | 0 .../util_lookup_table.h} | 0 .../util_profiling.h} | 0 .../cycles/{render => scene}/CMakeLists.txt | 29 +--- intern/cycles/{render => scene}/alembic.cpp | 16 +-- intern/cycles/{render => scene}/alembic.h | 4 +- .../cycles/{render => scene}/alembic_read.cpp | 8 +- .../cycles/{render => scene}/alembic_read.h | 0 intern/cycles/{render => scene}/attribute.cpp | 8 +- intern/cycles/{render => scene}/attribute.h | 2 +- .../cycles/{render => scene}/background.cpp | 14 +- intern/cycles/{render => scene}/background.h | 0 intern/cycles/{render => scene}/bake.cpp | 14 +- intern/cycles/{render => scene}/bake.h | 2 +- intern/cycles/{render => scene}/camera.cpp | 19 +-- intern/cycles/{render => scene}/camera.h | 0 .../cycles/{render => scene}/colorspace.cpp | 2 +- intern/cycles/{render => scene}/colorspace.h | 0 .../{render => scene}/constant_fold.cpp | 4 +- .../cycles/{render => scene}/constant_fold.h | 0 intern/cycles/{render => scene}/curves.cpp | 8 +- intern/cycles/{render => scene}/curves.h | 2 +- intern/cycles/{render => scene}/film.cpp | 20 +-- intern/cycles/{render => scene}/film.h | 2 +- intern/cycles/{render => scene}/geometry.cpp | 24 ++-- intern/cycles/{render => scene}/geometry.h | 2 +- intern/cycles/{render => scene}/hair.cpp | 8 +- intern/cycles/{render => scene}/hair.h | 2 +- intern/cycles/{render => scene}/image.cpp | 12 +- intern/cycles/{render => scene}/image.h | 2 +- .../cycles/{render => scene}/image_oiio.cpp | 2 +- intern/cycles/{render => scene}/image_oiio.h | 2 +- intern/cycles/{render => scene}/image_sky.cpp | 2 +- intern/cycles/{render => scene}/image_sky.h | 2 +- intern/cycles/{render => scene}/image_vdb.cpp | 2 +- intern/cycles/{render => scene}/image_vdb.h | 2 +- .../cycles/{render => scene}/integrator.cpp | 22 +-- intern/cycles/{render => scene}/integrator.h | 0 intern/cycles/{render => scene}/jitter.cpp | 2 +- intern/cycles/{render => scene}/jitter.h | 0 intern/cycles/{render => scene}/light.cpp | 22 +-- intern/cycles/{render => scene}/light.h | 2 +- intern/cycles/{render => scene}/mesh.cpp | 10 +- intern/cycles/{render => scene}/mesh.h | 6 +- .../{render => scene}/mesh_displace.cpp | 8 +- .../{render => scene}/mesh_subdivision.cpp | 6 +- intern/cycles/{render => scene}/object.cpp | 22 +-- intern/cycles/{render => scene}/object.h | 4 +- intern/cycles/{render => scene}/osl.cpp | 18 +-- intern/cycles/{render => scene}/osl.h | 6 +- intern/cycles/{render => scene}/particles.cpp | 6 +- intern/cycles/{render => scene}/particles.h | 0 intern/cycles/{render => scene}/pass.cpp | 2 +- intern/cycles/{render => scene}/pass.h | 0 .../cycles/{render => scene}/procedural.cpp | 4 +- intern/cycles/{render => scene}/procedural.h | 0 intern/cycles/{render => scene}/scene.cpp | 38 ++--- intern/cycles/{render => scene}/scene.h | 6 +- intern/cycles/{render => scene}/shader.cpp | 30 ++-- intern/cycles/{render => scene}/shader.h | 2 +- .../graph.cpp => scene/shader_graph.cpp} | 12 +- .../{render/graph.h => scene/shader_graph.h} | 0 .../nodes.cpp => scene/shader_nodes.cpp} | 25 ++-- .../{render/nodes.h => scene/shader_nodes.h} | 4 +- intern/cycles/scene/sobol.cpp | 94 +++++++++++++ intern/cycles/{render => scene}/sobol.h | 0 .../{render/sobol.cpp => scene/sobol.tables} | 51 +------ intern/cycles/{render => scene}/stats.cpp | 4 +- intern/cycles/{render => scene}/stats.h | 2 +- intern/cycles/{render => scene}/svm.cpp | 18 +-- intern/cycles/{render => scene}/svm.h | 6 +- intern/cycles/{render => scene}/tables.cpp | 6 +- intern/cycles/{render => scene}/tables.h | 0 intern/cycles/{render => scene}/volume.cpp | 8 +- intern/cycles/{render => scene}/volume.h | 2 +- intern/cycles/session/CMakeLists.txt | 48 +++++++ intern/cycles/{render => session}/buffers.cpp | 2 +- intern/cycles/{render => session}/buffers.h | 2 +- .../cycles/{render => session}/denoising.cpp | 2 +- intern/cycles/{render => session}/denoising.h | 0 .../{render => session}/display_driver.h | 0 intern/cycles/{render => session}/merge.cpp | 2 +- intern/cycles/{render => session}/merge.h | 0 .../{render => session}/output_driver.h | 0 intern/cycles/{render => session}/session.cpp | 26 ++-- intern/cycles/{render => session}/session.h | 8 +- intern/cycles/{render => session}/tile.cpp | 10 +- intern/cycles/{render => session}/tile.h | 2 +- intern/cycles/subd/subd_dice.cpp | 4 +- intern/cycles/subd/subd_patch.cpp | 2 +- intern/cycles/subd/subd_split.cpp | 4 +- intern/cycles/test/CMakeLists.txt | 7 +- .../test/render_graph_finalize_test.cpp | 6 +- 322 files changed, 1013 insertions(+), 874 deletions(-) rename intern/cycles/kernel/{kernel_bake.h => bake/bake.h} (97%) rename intern/cycles/kernel/{kernel_camera.h => camera/camera.h} (99%) rename intern/cycles/kernel/{kernel_projection.h => camera/camera_projection.h} (100%) rename intern/cycles/kernel/{kernel_work_stealing.h => device/gpu/work_stealing.h} (100%) rename intern/cycles/kernel/{kernel_accumulate.h => film/film_accumulate.h} (99%) rename intern/cycles/kernel/{kernel_adaptive_sampling.h => film/film_adaptive_sampling.h} (99%) rename intern/cycles/kernel/{kernel_id_passes.h => film/film_id_passes.h} (100%) rename intern/cycles/kernel/{kernel_passes.h => film/film_passes.h} (99%) rename intern/cycles/kernel/{kernel_film.h => film/film_read.h} (100%) rename intern/cycles/kernel/{kernel_write_passes.h => film/film_write_passes.h} (100%) rename intern/cycles/kernel/{kernel_path_state.h => integrator/integrator_path_state.h} (99%) rename intern/cycles/kernel/{kernel_shader.h => integrator/integrator_shader_eval.h} (99%) rename intern/cycles/kernel/{kernel_shadow_catcher.h => integrator/integrator_shadow_catcher.h} (98%) delete mode 100644 intern/cycles/kernel/kernel_math.h rename intern/cycles/kernel/{kernel_light.h => light/light.h} (99%) rename intern/cycles/kernel/{kernel_light_background.h => light/light_background.h} (99%) rename intern/cycles/kernel/{kernel_light_common.h => light/light_common.h} (99%) rename intern/cycles/kernel/{kernel_emission.h => light/light_sample.h} (97%) rename intern/cycles/kernel/{ => osl}/shaders/CMakeLists.txt (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_absorption_volume.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_add_closure.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_ambient_occlusion.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_anisotropic_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_attribute.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_background.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_bevel.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_blackbody.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_brick_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_brightness.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_bump.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_camera.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_checker_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_clamp.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_color.h (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_combine_hsv.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_combine_rgb.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_combine_xyz.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_convert_from_color.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_convert_from_float.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_convert_from_int.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_convert_from_normal.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_convert_from_point.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_convert_from_string.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_convert_from_vector.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_diffuse_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_displacement.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_emission.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_environment_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_float_curve.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_fresnel.h (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_fresnel.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_gamma.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_geometry.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_glass_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_glossy_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_gradient_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_hair_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_hair_info.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_hash.h (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_holdout.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_hsv.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_ies_light.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_image_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_invert.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_layer_weight.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_light_falloff.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_light_path.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_magic_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_map_range.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_mapping.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_math.h (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_math.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_mix.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_mix_closure.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_musgrave_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_noise.h (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_noise_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_normal.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_normal_map.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_object_info.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_output_displacement.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_output_surface.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_output_volume.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_particle_info.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_principled_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_principled_hair_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_principled_volume.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_ramp_util.h (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_refraction_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_rgb_curves.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_rgb_ramp.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_rgb_to_bw.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_scatter_volume.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_separate_hsv.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_separate_rgb.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_separate_xyz.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_set_normal.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_sky_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_subsurface_scattering.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_tangent.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_texture_coordinate.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_toon_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_translucent_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_transparent_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_uv_map.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_value.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_vector_curves.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_vector_displacement.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_vector_math.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_vector_rotate.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_vector_transform.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_velvet_bsdf.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_vertex_color.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_voronoi_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_voxel_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_wave_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_wavelength.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_white_noise_texture.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/node_wireframe.osl (100%) rename intern/cycles/kernel/{ => osl}/shaders/stdcycles.h (100%) rename intern/cycles/kernel/{kernel_jitter.h => sample/sample_jitter.h} (100%) create mode 100644 intern/cycles/kernel/sample/sample_lcg.h rename intern/cycles/kernel/{kernel_montecarlo.h => sample/sample_mapping.h} (57%) create mode 100644 intern/cycles/kernel/sample/sample_mis.h rename intern/cycles/kernel/{kernel_random.h => sample/sample_pattern.h} (87%) rename intern/cycles/kernel/{kernel_color.h => util/util_color.h} (100%) rename intern/cycles/kernel/{kernel_differential.h => util/util_differential.h} (100%) rename intern/cycles/kernel/{kernel_lookup_table.h => util/util_lookup_table.h} (100%) rename intern/cycles/kernel/{kernel_profiling.h => util/util_profiling.h} (100%) rename intern/cycles/{render => scene}/CMakeLists.txt (87%) rename intern/cycles/{render => scene}/alembic.cpp (99%) rename intern/cycles/{render => scene}/alembic.h (99%) rename intern/cycles/{render => scene}/alembic_read.cpp (99%) rename intern/cycles/{render => scene}/alembic_read.h (100%) rename intern/cycles/{render => scene}/attribute.cpp (99%) rename intern/cycles/{render => scene}/attribute.h (99%) rename intern/cycles/{render => scene}/background.cpp (95%) rename intern/cycles/{render => scene}/background.h (100%) rename intern/cycles/{render => scene}/bake.cpp (92%) rename intern/cycles/{render => scene}/bake.h (97%) rename intern/cycles/{render => scene}/camera.cpp (98%) rename intern/cycles/{render => scene}/camera.h (100%) rename intern/cycles/{render => scene}/colorspace.cpp (99%) rename intern/cycles/{render => scene}/colorspace.h (100%) rename intern/cycles/{render => scene}/constant_fold.cpp (99%) rename intern/cycles/{render => scene}/constant_fold.h (100%) rename intern/cycles/{render => scene}/curves.cpp (95%) rename intern/cycles/{render => scene}/curves.h (98%) rename intern/cycles/{render => scene}/film.cpp (98%) rename intern/cycles/{render => scene}/film.h (99%) rename intern/cycles/{render => scene}/geometry.cpp (99%) rename intern/cycles/{render => scene}/geometry.h (99%) rename intern/cycles/{render => scene}/hair.cpp (99%) rename intern/cycles/{render => scene}/hair.h (99%) rename intern/cycles/{render => scene}/image.cpp (99%) rename intern/cycles/{render => scene}/image.h (99%) rename intern/cycles/{render => scene}/image_oiio.cpp (99%) rename intern/cycles/{render => scene}/image_oiio.h (98%) rename intern/cycles/{render => scene}/image_sky.cpp (98%) rename intern/cycles/{render => scene}/image_sky.h (98%) rename intern/cycles/{render => scene}/image_vdb.cpp (99%) rename intern/cycles/{render => scene}/image_vdb.h (98%) rename intern/cycles/{render => scene}/integrator.cpp (97%) rename intern/cycles/{render => scene}/integrator.h (100%) rename intern/cycles/{render => scene}/jitter.cpp (99%) rename intern/cycles/{render => scene}/jitter.h (100%) rename intern/cycles/{render => scene}/light.cpp (99%) rename intern/cycles/{render => scene}/light.h (99%) rename intern/cycles/{render => scene}/mesh.cpp (99%) rename intern/cycles/{render => scene}/mesh.h (98%) rename intern/cycles/{render => scene}/mesh_displace.cpp (99%) rename intern/cycles/{render => scene}/mesh_subdivision.cpp (99%) rename intern/cycles/{render => scene}/object.cpp (99%) rename intern/cycles/{render => scene}/object.h (98%) rename intern/cycles/{render => scene}/osl.cpp (99%) rename intern/cycles/{render => scene}/osl.h (98%) rename intern/cycles/{render => scene}/particles.cpp (97%) rename intern/cycles/{render => scene}/particles.h (100%) rename intern/cycles/{render => scene}/pass.cpp (99%) rename intern/cycles/{render => scene}/pass.h (100%) rename intern/cycles/{render => scene}/procedural.cpp (97%) rename intern/cycles/{render => scene}/procedural.h (100%) rename intern/cycles/{render => scene}/scene.cpp (98%) rename intern/cycles/{render => scene}/scene.h (99%) rename intern/cycles/{render => scene}/shader.cpp (98%) rename intern/cycles/{render => scene}/shader.h (99%) rename intern/cycles/{render/graph.cpp => scene/shader_graph.cpp} (99%) rename intern/cycles/{render/graph.h => scene/shader_graph.h} (100%) rename intern/cycles/{render/nodes.cpp => scene/shader_nodes.cpp} (99%) rename intern/cycles/{render/nodes.h => scene/shader_nodes.h} (99%) create mode 100644 intern/cycles/scene/sobol.cpp rename intern/cycles/{render => scene}/sobol.h (100%) rename intern/cycles/{render/sobol.cpp => scene/sobol.tables} (99%) rename intern/cycles/{render => scene}/stats.cpp (99%) rename intern/cycles/{render => scene}/stats.h (99%) rename intern/cycles/{render => scene}/svm.cpp (99%) rename intern/cycles/{render => scene}/svm.h (98%) rename intern/cycles/{render => scene}/tables.cpp (97%) rename intern/cycles/{render => scene}/tables.h (100%) rename intern/cycles/{render => scene}/volume.cpp (99%) rename intern/cycles/{render => scene}/volume.h (97%) create mode 100644 intern/cycles/session/CMakeLists.txt rename intern/cycles/{render => session}/buffers.cpp (99%) rename intern/cycles/{render => session}/buffers.h (99%) rename intern/cycles/{render => session}/denoising.cpp (99%) rename intern/cycles/{render => session}/denoising.h (100%) rename intern/cycles/{render => session}/display_driver.h (100%) rename intern/cycles/{render => session}/merge.cpp (99%) rename intern/cycles/{render => session}/merge.h (100%) rename intern/cycles/{render => session}/output_driver.h (100%) rename intern/cycles/{render => session}/session.cpp (97%) rename intern/cycles/{render => session}/session.h (98%) rename intern/cycles/{render => session}/tile.cpp (99%) rename intern/cycles/{render => session}/tile.h (99%) diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index 2018c1d9648..b6f3926f329 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -400,7 +400,8 @@ add_subdirectory(doc) add_subdirectory(graph) add_subdirectory(integrator) add_subdirectory(kernel) -add_subdirectory(render) +add_subdirectory(scene) +add_subdirectory(session) add_subdirectory(subd) add_subdirectory(util) diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index 3ed3f54ef9f..50600793854 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -25,7 +25,8 @@ set(INC_SYS set(LIBRARIES cycles_device cycles_kernel - cycles_render + cycles_scene + cycles_session cycles_bvh cycles_subd cycles_graph diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp index 00dc140648a..800227ccf48 100644 --- a/intern/cycles/app/cycles_standalone.cpp +++ b/intern/cycles/app/cycles_standalone.cpp @@ -17,11 +17,11 @@ #include #include "device/device.h" -#include "render/buffers.h" -#include "render/camera.h" -#include "render/integrator.h" -#include "render/scene.h" -#include "render/session.h" +#include "scene/camera.h" +#include "scene/integrator.h" +#include "scene/scene.h" +#include "session/buffers.h" +#include "session/session.h" #include "util/util_args.h" #include "util/util_foreach.h" diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 0b83c60f32d..1ced74b6136 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -22,18 +22,18 @@ #include "graph/node_xml.h" -#include "render/background.h" -#include "render/camera.h" -#include "render/film.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/osl.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/background.h" +#include "scene/camera.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/osl.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" #include "subd/subd_patch.h" #include "subd/subd_split.h" diff --git a/intern/cycles/app/oiio_output_driver.h b/intern/cycles/app/oiio_output_driver.h index cdc4085d962..a6984938fe7 100644 --- a/intern/cycles/app/oiio_output_driver.h +++ b/intern/cycles/app/oiio_output_driver.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "render/output_driver.h" +#include "session/output_driver.h" #include "util/util_function.h" #include "util/util_image.h" diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index a0442b3394b..d948f2b3118 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -69,7 +69,8 @@ set(LIB cycles_device cycles_graph cycles_kernel - cycles_render + cycles_scene + cycles_session cycles_subd cycles_util diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index 52acc2573f5..670e25841f5 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "render/camera.h" -#include "render/scene.h" +#include "scene/camera.h" +#include "scene/scene.h" #include "blender/blender_sync.h" #include "blender/blender_util.h" diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp index b6b4f206620..84333faaa23 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/blender_curves.cpp @@ -14,16 +14,17 @@ * limitations under the License. */ -#include "render/attribute.h" -#include "render/camera.h" -#include "render/curves.h" -#include "render/hair.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/attribute.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/hair.h" +#include "scene/object.h" +#include "scene/scene.h" #include "blender/blender_sync.h" #include "blender/blender_util.h" +#include "util/util_color.h" #include "util/util_foreach.h" #include "util/util_hash.h" #include "util/util_logging.h" diff --git a/intern/cycles/blender/blender_display_driver.h b/intern/cycles/blender/blender_display_driver.h index 5e7e9b01065..800d0791041 100644 --- a/intern/cycles/blender/blender_display_driver.h +++ b/intern/cycles/blender/blender_display_driver.h @@ -22,7 +22,7 @@ #include "RNA_blender_cpp.h" -#include "render/display_driver.h" +#include "session/display_driver.h" #include "util/util_thread.h" #include "util/util_unique_ptr.h" diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp index 7b49bb7fbb7..b4b0d04d104 100644 --- a/intern/cycles/blender/blender_geometry.cpp +++ b/intern/cycles/blender/blender_geometry.cpp @@ -15,11 +15,11 @@ * limitations under the License. */ -#include "render/curves.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/volume.h" +#include "scene/curves.h" +#include "scene/hair.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/volume.h" #include "blender/blender_sync.h" #include "blender/blender_util.h" diff --git a/intern/cycles/blender/blender_id_map.h b/intern/cycles/blender/blender_id_map.h index 198cfb4b29a..27a53a90f12 100644 --- a/intern/cycles/blender/blender_id_map.h +++ b/intern/cycles/blender/blender_id_map.h @@ -19,8 +19,8 @@ #include -#include "render/geometry.h" -#include "render/scene.h" +#include "scene/geometry.h" +#include "scene/scene.h" #include "util/util_map.h" #include "util/util_set.h" diff --git a/intern/cycles/blender/blender_image.h b/intern/cycles/blender/blender_image.h index fddbbfd9c37..6f1e72c21af 100644 --- a/intern/cycles/blender/blender_image.h +++ b/intern/cycles/blender/blender_image.h @@ -19,7 +19,7 @@ #include "RNA_blender_cpp.h" -#include "render/image.h" +#include "scene/image.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/blender_light.cpp index 4df1e720dde..aa0c6a964e4 100644 --- a/intern/cycles/blender/blender_light.cpp +++ b/intern/cycles/blender/blender_light.cpp @@ -16,7 +16,7 @@ * limitations under the License. */ -#include "render/light.h" +#include "scene/light.h" #include "blender/blender_sync.h" #include "blender/blender_util.h" diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 7ec430eb7fe..992e17d6f79 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "render/camera.h" -#include "render/colorspace.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/camera.h" +#include "scene/colorspace.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" #include "blender/blender_session.h" #include "blender/blender_sync.h" @@ -28,6 +28,7 @@ #include "subd/subd_split.h" #include "util/util_algorithm.h" +#include "util/util_color.h" #include "util/util_disjoint_set.h" #include "util/util_foreach.h" #include "util/util_hash.h" diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 4b1c4edef7e..75311805fd8 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -14,17 +14,17 @@ * limitations under the License. */ -#include "render/alembic.h" -#include "render/camera.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/particles.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/alembic.h" +#include "scene/camera.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/particles.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" #include "blender/blender_object_cull.h" #include "blender/blender_sync.h" diff --git a/intern/cycles/blender/blender_object_cull.cpp b/intern/cycles/blender/blender_object_cull.cpp index cb7827b3c4a..34cceb5a6e4 100644 --- a/intern/cycles/blender/blender_object_cull.cpp +++ b/intern/cycles/blender/blender_object_cull.cpp @@ -16,7 +16,7 @@ #include -#include "render/camera.h" +#include "scene/camera.h" #include "blender/blender_object_cull.h" #include "blender/blender_util.h" diff --git a/intern/cycles/blender/blender_output_driver.h b/intern/cycles/blender/blender_output_driver.h index 0852cba1b34..1d016f8bcb9 100644 --- a/intern/cycles/blender/blender_output_driver.h +++ b/intern/cycles/blender/blender_output_driver.h @@ -20,7 +20,7 @@ #include "RNA_blender_cpp.h" -#include "render/output_driver.h" +#include "session/output_driver.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/blender_particles.cpp index 206ee24a093..f654998af62 100644 --- a/intern/cycles/blender/blender_particles.cpp +++ b/intern/cycles/blender/blender_particles.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "render/mesh.h" -#include "render/object.h" -#include "render/particles.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/particles.h" #include "blender/blender_sync.h" #include "blender/blender_util.h" diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index d681517c9e1..45e5394cf34 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -23,8 +23,8 @@ #include "blender/blender_sync.h" #include "blender/blender_util.h" -#include "render/denoising.h" -#include "render/merge.h" +#include "session/denoising.h" +#include "session/merge.h" #include "util/util_debug.h" #include "util/util_foreach.h" @@ -39,7 +39,7 @@ #include "util/util_types.h" #ifdef WITH_OSL -# include "render/osl.h" +# include "scene/osl.h" # include # include diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 0b4b5c60def..988a8159864 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -17,19 +17,19 @@ #include #include "device/device.h" -#include "render/background.h" -#include "render/buffers.h" -#include "render/camera.h" -#include "render/colorspace.h" -#include "render/film.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/session.h" -#include "render/shader.h" -#include "render/stats.h" +#include "scene/background.h" +#include "scene/camera.h" +#include "scene/colorspace.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/stats.h" +#include "session/buffers.h" +#include "session/session.h" #include "util/util_algorithm.h" #include "util/util_color.h" diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index 7d3be5f8054..9bc685ec306 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -21,9 +21,9 @@ #include "device/device.h" -#include "render/bake.h" -#include "render/scene.h" -#include "render/session.h" +#include "scene/bake.h" +#include "scene/scene.h" +#include "session/session.h" #include "util/util_vector.h" diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 25e7ec71577..8d3bbb520c8 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -14,15 +14,15 @@ * limitations under the License. */ -#include "render/background.h" -#include "render/colorspace.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/nodes.h" -#include "render/osl.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/background.h" +#include "scene/colorspace.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/osl.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" #include "blender/blender_image.h" #include "blender/blender_sync.h" diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index e0a809b9307..1725ea5ec93 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -14,19 +14,19 @@ * limitations under the License. */ -#include "render/background.h" -#include "render/camera.h" -#include "render/curves.h" -#include "render/film.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/procedural.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/background.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/procedural.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" #include "device/device.h" diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 786479ac0f8..99e3f0bf02b 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -26,8 +26,8 @@ #include "blender/blender_util.h" #include "blender/blender_viewport.h" -#include "render/scene.h" -#include "render/session.h" +#include "scene/scene.h" +#include "session/session.h" #include "util/util_map.h" #include "util/util_set.h" diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 77b2bd5ac4f..a3dd2349525 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -17,7 +17,7 @@ #ifndef __BLENDER_UTIL_H__ #define __BLENDER_UTIL_H__ -#include "render/mesh.h" +#include "scene/mesh.h" #include "util/util_algorithm.h" #include "util/util_array.h" diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp index 62e32240bba..b8deb77b621 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/blender_viewport.cpp @@ -17,7 +17,7 @@ #include "blender_viewport.h" #include "blender_util.h" -#include "render/pass.h" +#include "scene/pass.h" #include "util/util_logging.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h index b5adafc30c9..a445973f4d2 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/blender_viewport.h @@ -23,7 +23,7 @@ #include "RNA_blender_cpp.h" #include "RNA_types.h" -#include "render/film.h" +#include "scene/film.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp index 0a5b19d7d4c..46083cb29dd 100644 --- a/intern/cycles/blender/blender_volume.cpp +++ b/intern/cycles/blender/blender_volume.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "render/colorspace.h" -#include "render/image.h" -#include "render/image_vdb.h" -#include "render/object.h" -#include "render/volume.h" +#include "scene/colorspace.h" +#include "scene/image.h" +#include "scene/image_vdb.h" +#include "scene/object.h" +#include "scene/volume.h" #include "blender/blender_sync.h" #include "blender/blender_util.h" diff --git a/intern/cycles/bvh/CMakeLists.txt b/intern/cycles/bvh/CMakeLists.txt index 8cc72359757..f7e47a8764d 100644 --- a/intern/cycles/bvh/CMakeLists.txt +++ b/intern/cycles/bvh/CMakeLists.txt @@ -49,7 +49,7 @@ set(SRC_HEADERS ) set(LIB - cycles_render + cycles_scene cycles_util ) diff --git a/intern/cycles/bvh/bvh2.cpp b/intern/cycles/bvh/bvh2.cpp index 4a90a1e8796..3b864859f31 100644 --- a/intern/cycles/bvh/bvh2.cpp +++ b/intern/cycles/bvh/bvh2.cpp @@ -17,9 +17,9 @@ #include "bvh/bvh2.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" +#include "scene/hair.h" +#include "scene/mesh.h" +#include "scene/object.h" #include "bvh/bvh_build.h" #include "bvh/bvh_node.h" diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp index 025a103d6f8..9ae40b57bc3 100644 --- a/intern/cycles/bvh/bvh_build.cpp +++ b/intern/cycles/bvh/bvh_build.cpp @@ -22,11 +22,11 @@ #include "bvh/bvh_params.h" #include "bvh_split.h" -#include "render/curves.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/curves.h" +#include "scene/hair.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" #include "util/util_algorithm.h" #include "util/util_foreach.h" diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index cd19e009bf3..59a72f27294 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -40,11 +40,11 @@ # include "kernel/bvh/bvh_util.h" # include "kernel/device/cpu/compat.h" # include "kernel/device/cpu/globals.h" -# include "kernel/kernel_random.h" +# include "kernel/sample/sample_lcg.h" -# include "render/hair.h" -# include "render/mesh.h" -# include "render/object.h" +# include "scene/hair.h" +# include "scene/mesh.h" +# include "scene/object.h" # include "util/util_foreach.h" # include "util/util_logging.h" diff --git a/intern/cycles/bvh/bvh_split.cpp b/intern/cycles/bvh/bvh_split.cpp index d4c79253834..0e7d36983e7 100644 --- a/intern/cycles/bvh/bvh_split.cpp +++ b/intern/cycles/bvh/bvh_split.cpp @@ -20,9 +20,9 @@ #include "bvh/bvh_build.h" #include "bvh/bvh_sort.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" +#include "scene/hair.h" +#include "scene/mesh.h" +#include "scene/object.h" #include "util/util_algorithm.h" diff --git a/intern/cycles/bvh/bvh_unaligned.cpp b/intern/cycles/bvh/bvh_unaligned.cpp index 38e55307848..ce95aa7aa74 100644 --- a/intern/cycles/bvh/bvh_unaligned.cpp +++ b/intern/cycles/bvh/bvh_unaligned.cpp @@ -16,8 +16,8 @@ #include "bvh/bvh_unaligned.h" -#include "render/hair.h" -#include "render/object.h" +#include "scene/hair.h" +#include "scene/object.h" #include "bvh/bvh_binning.h" #include "bvh_params.h" diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp index d02c18daee9..98d637b5f8a 100644 --- a/intern/cycles/device/cpu/device_impl.cpp +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -47,7 +47,7 @@ #include "bvh/bvh_embree.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_debug.h" #include "util/util_foreach.h" diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp index 1c970096801..40f407b4fb3 100644 --- a/intern/cycles/device/cuda/device_impl.cpp +++ b/intern/cycles/device/cuda/device_impl.cpp @@ -24,8 +24,6 @@ # include "device/cuda/device_impl.h" -# include "render/buffers.h" - # include "util/util_debug.h" # include "util/util_foreach.h" # include "util/util_logging.h" diff --git a/intern/cycles/device/device_denoise.h b/intern/cycles/device/device_denoise.h index dfdc7cc87b3..4e09f1a1ba3 100644 --- a/intern/cycles/device/device_denoise.h +++ b/intern/cycles/device/device_denoise.h @@ -18,7 +18,7 @@ #include "device/device_memory.h" #include "graph/node.h" -#include "render/buffers.h" +#include "session/buffers.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/device_graphics_interop.h b/intern/cycles/device/device_graphics_interop.h index eaf76077141..e5c97fe5a1e 100644 --- a/intern/cycles/device/device_graphics_interop.h +++ b/intern/cycles/device/device_graphics_interop.h @@ -16,7 +16,7 @@ #pragma once -#include "render/display_driver.h" +#include "session/display_driver.h" #include "util/util_types.h" diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp index 4ae714913ab..9ffeb9724a1 100644 --- a/intern/cycles/device/hip/device_impl.cpp +++ b/intern/cycles/device/hip/device_impl.cpp @@ -24,8 +24,6 @@ # include "device/hip/device_impl.h" -# include "render/buffers.h" - # include "util/util_debug.h" # include "util/util_foreach.h" # include "util/util_logging.h" diff --git a/intern/cycles/device/multi/device.cpp b/intern/cycles/device/multi/device.cpp index 4f995abf2c4..330a1ed06ef 100644 --- a/intern/cycles/device/multi/device.cpp +++ b/intern/cycles/device/multi/device.cpp @@ -24,8 +24,7 @@ #include "device/device.h" #include "device/device_queue.h" -#include "render/buffers.h" -#include "render/geometry.h" +#include "scene/geometry.h" #include "util/util_foreach.h" #include "util/util_list.h" diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index 29e46be7745..55c3fbd88f2 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -21,13 +21,14 @@ # include "bvh/bvh.h" # include "bvh/bvh_optix.h" + # include "integrator/pass_accessor_gpu.h" -# include "render/buffers.h" -# include "render/hair.h" -# include "render/mesh.h" -# include "render/object.h" -# include "render/pass.h" -# include "render/scene.h" + +# include "scene/hair.h" +# include "scene/mesh.h" +# include "scene/object.h" +# include "scene/pass.h" +# include "scene/scene.h" # include "util/util_debug.h" # include "util/util_logging.h" diff --git a/intern/cycles/integrator/CMakeLists.txt b/intern/cycles/integrator/CMakeLists.txt index 949254606b8..dedde513409 100644 --- a/intern/cycles/integrator/CMakeLists.txt +++ b/intern/cycles/integrator/CMakeLists.txt @@ -61,9 +61,11 @@ set(SRC_HEADERS ) set(LIB + cycles_device + # NOTE: Is required for RenderBuffers access. Might consider moving files around a bit to # avoid such cyclic dependency. - cycles_render + cycles_session cycles_util ) diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp index 598bbd497a5..45f967b38eb 100644 --- a/intern/cycles/integrator/denoiser.cpp +++ b/intern/cycles/integrator/denoiser.cpp @@ -19,7 +19,7 @@ #include "device/device.h" #include "integrator/denoiser_oidn.h" #include "integrator/denoiser_optix.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_logging.h" #include "util/util_progress.h" diff --git a/intern/cycles/integrator/denoiser_device.cpp b/intern/cycles/integrator/denoiser_device.cpp index e8361c50f2f..1afd8d46866 100644 --- a/intern/cycles/integrator/denoiser_device.cpp +++ b/intern/cycles/integrator/denoiser_device.cpp @@ -20,7 +20,7 @@ #include "device/device_denoise.h" #include "device/device_memory.h" #include "device/device_queue.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_logging.h" #include "util/util_progress.h" diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp index cc9a3f51387..c3555008699 100644 --- a/intern/cycles/integrator/denoiser_oidn.cpp +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -21,7 +21,7 @@ #include "device/device.h" #include "device/device_queue.h" #include "integrator/pass_accessor_cpu.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_array.h" #include "util/util_logging.h" #include "util/util_openimagedenoise.h" diff --git a/intern/cycles/integrator/pass_accessor.cpp b/intern/cycles/integrator/pass_accessor.cpp index 1308b03b06c..0a8c445eca7 100644 --- a/intern/cycles/integrator/pass_accessor.cpp +++ b/intern/cycles/integrator/pass_accessor.cpp @@ -16,7 +16,7 @@ #include "integrator/pass_accessor.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_logging.h" // clang-format off diff --git a/intern/cycles/integrator/pass_accessor.h b/intern/cycles/integrator/pass_accessor.h index 624bf7d0b2c..c9cf3ba8956 100644 --- a/intern/cycles/integrator/pass_accessor.h +++ b/intern/cycles/integrator/pass_accessor.h @@ -16,7 +16,7 @@ #pragma once -#include "render/pass.h" +#include "scene/pass.h" #include "util/util_half.h" #include "util/util_string.h" #include "util/util_types.h" diff --git a/intern/cycles/integrator/pass_accessor_cpu.cpp b/intern/cycles/integrator/pass_accessor_cpu.cpp index e3cb81d31b7..f3ca38c667d 100644 --- a/intern/cycles/integrator/pass_accessor_cpu.cpp +++ b/intern/cycles/integrator/pass_accessor_cpu.cpp @@ -16,7 +16,7 @@ #include "integrator/pass_accessor_cpu.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_logging.h" #include "util/util_tbb.h" @@ -24,7 +24,7 @@ #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" #include "kernel/kernel_types.h" -#include "kernel/kernel_film.h" +#include "kernel/film/film_read.h" // clang-format on CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/pass_accessor_gpu.cpp b/intern/cycles/integrator/pass_accessor_gpu.cpp index 7b01d061708..3b4290a89e6 100644 --- a/intern/cycles/integrator/pass_accessor_gpu.cpp +++ b/intern/cycles/integrator/pass_accessor_gpu.cpp @@ -17,7 +17,7 @@ #include "integrator/pass_accessor_gpu.h" #include "device/device_queue.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_logging.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 9f3049c6484..94687f7bc95 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -22,9 +22,9 @@ #include "integrator/path_trace_display.h" #include "integrator/path_trace_tile.h" #include "integrator/render_scheduler.h" -#include "render/pass.h" -#include "render/scene.h" -#include "render/tile.h" +#include "scene/pass.h" +#include "scene/scene.h" +#include "session/tile.h" #include "util/util_algorithm.h" #include "util/util_logging.h" #include "util/util_progress.h" diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h index dbb22c204d9..89fa5fa8eaf 100644 --- a/intern/cycles/integrator/path_trace.h +++ b/intern/cycles/integrator/path_trace.h @@ -20,7 +20,7 @@ #include "integrator/pass_accessor.h" #include "integrator/path_trace_work.h" #include "integrator/work_balancer.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_function.h" #include "util/util_thread.h" #include "util/util_unique_ptr.h" diff --git a/intern/cycles/integrator/path_trace_display.cpp b/intern/cycles/integrator/path_trace_display.cpp index e22989bb301..7455a107ae6 100644 --- a/intern/cycles/integrator/path_trace_display.cpp +++ b/intern/cycles/integrator/path_trace_display.cpp @@ -16,7 +16,7 @@ #include "integrator/path_trace_display.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_logging.h" diff --git a/intern/cycles/integrator/path_trace_display.h b/intern/cycles/integrator/path_trace_display.h index 99d2e0e1203..d40e45e7ead 100644 --- a/intern/cycles/integrator/path_trace_display.h +++ b/intern/cycles/integrator/path_trace_display.h @@ -16,7 +16,7 @@ #pragma once -#include "render/display_driver.h" +#include "session/display_driver.h" #include "util/util_half.h" #include "util/util_thread.h" diff --git a/intern/cycles/integrator/path_trace_tile.cpp b/intern/cycles/integrator/path_trace_tile.cpp index 540f4aa5f68..4834769f476 100644 --- a/intern/cycles/integrator/path_trace_tile.cpp +++ b/intern/cycles/integrator/path_trace_tile.cpp @@ -18,10 +18,10 @@ #include "integrator/pass_accessor_cpu.h" #include "integrator/path_trace.h" -#include "render/buffers.h" -#include "render/film.h" -#include "render/pass.h" -#include "render/scene.h" +#include "scene/film.h" +#include "scene/pass.h" +#include "scene/scene.h" +#include "session/buffers.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace_tile.h b/intern/cycles/integrator/path_trace_tile.h index fd3e2969f6c..6c7bddf2ca5 100644 --- a/intern/cycles/integrator/path_trace_tile.h +++ b/intern/cycles/integrator/path_trace_tile.h @@ -16,7 +16,7 @@ #pragma once -#include "render/output_driver.h" +#include "session/output_driver.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp index d46f095d0d7..e08c410a579 100644 --- a/intern/cycles/integrator/path_trace_work.cpp +++ b/intern/cycles/integrator/path_trace_work.cpp @@ -20,9 +20,9 @@ #include "integrator/path_trace_work.h" #include "integrator/path_trace_work_cpu.h" #include "integrator/path_trace_work_gpu.h" -#include "render/buffers.h" -#include "render/film.h" -#include "render/scene.h" +#include "scene/film.h" +#include "scene/scene.h" +#include "session/buffers.h" #include "kernel/kernel_types.h" diff --git a/intern/cycles/integrator/path_trace_work.h b/intern/cycles/integrator/path_trace_work.h index 404165b7c55..6b2a6c71e81 100644 --- a/intern/cycles/integrator/path_trace_work.h +++ b/intern/cycles/integrator/path_trace_work.h @@ -17,8 +17,8 @@ #pragma once #include "integrator/pass_accessor.h" -#include "render/buffers.h" -#include "render/pass.h" +#include "scene/pass.h" +#include "session/buffers.h" #include "util/util_types.h" #include "util/util_unique_ptr.h" diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp index 1cadcd9ec5c..1e59682a64c 100644 --- a/intern/cycles/integrator/path_trace_work_cpu.cpp +++ b/intern/cycles/integrator/path_trace_work_cpu.cpp @@ -19,13 +19,13 @@ #include "device/cpu/kernel.h" #include "device/device.h" -#include "kernel/kernel_path_state.h" +#include "kernel/integrator/integrator_path_state.h" #include "integrator/pass_accessor_cpu.h" #include "integrator/path_trace_display.h" -#include "render/buffers.h" -#include "render/scene.h" +#include "scene/scene.h" +#include "session/buffers.h" #include "util/util_atomic.h" #include "util/util_logging.h" diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 775cee57a90..67e5ae70316 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -20,8 +20,8 @@ #include "device/device.h" #include "integrator/pass_accessor_gpu.h" -#include "render/buffers.h" -#include "render/scene.h" +#include "scene/scene.h" +#include "session/buffers.h" #include "util/util_logging.h" #include "util/util_string.h" #include "util/util_tbb.h" diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp index 322d3d5f94c..3c64cba5feb 100644 --- a/intern/cycles/integrator/render_scheduler.cpp +++ b/intern/cycles/integrator/render_scheduler.cpp @@ -16,8 +16,8 @@ #include "integrator/render_scheduler.h" -#include "render/session.h" -#include "render/tile.h" +#include "session/session.h" +#include "session/tile.h" #include "util/util_logging.h" #include "util/util_math.h" #include "util/util_time.h" diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h index c4ab15e54ba..a2c1e75d3b6 100644 --- a/intern/cycles/integrator/render_scheduler.h +++ b/intern/cycles/integrator/render_scheduler.h @@ -18,7 +18,7 @@ #include "integrator/adaptive_sampling.h" #include "integrator/denoiser.h" /* For DenoiseParams. */ -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_string.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/work_tile_scheduler.cpp b/intern/cycles/integrator/work_tile_scheduler.cpp index 234b1fae915..a6775f27b65 100644 --- a/intern/cycles/integrator/work_tile_scheduler.cpp +++ b/intern/cycles/integrator/work_tile_scheduler.cpp @@ -18,7 +18,7 @@ #include "device/device_queue.h" #include "integrator/tile.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_atomic.h" #include "util/util_logging.h" diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 6d5d386ddea..f27bcb41d3d 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -44,7 +44,8 @@ set(SRC_DEVICE_OPTIX device/optix/kernel_shader_raytrace.cu ) -set(SRC_BVH_HEADERS +set(SRC_HEADERS + bake/bake.h bvh/bvh.h bvh/bvh_nodes.h bvh/bvh_shadow_all.h @@ -55,36 +56,32 @@ set(SRC_BVH_HEADERS bvh/bvh_volume.h bvh/bvh_volume_all.h bvh/bvh_embree.h -) - -set(SRC_HEADERS - kernel_accumulate.h - kernel_adaptive_sampling.h - kernel_bake.h - kernel_camera.h - kernel_color.h - kernel_differential.h - kernel_emission.h - kernel_film.h - kernel_id_passes.h - kernel_jitter.h - kernel_light.h - kernel_light_background.h - kernel_light_common.h - kernel_lookup_table.h - kernel_math.h - kernel_montecarlo.h - kernel_passes.h - kernel_path_state.h - kernel_profiling.h - kernel_projection.h - kernel_random.h - kernel_shader.h - kernel_shadow_catcher.h + camera/camera.h + camera/camera_projection.h + film/film_accumulate.h + film/film_adaptive_sampling.h + film/film_id_passes.h + film/film_passes.h + film/film_read.h + film/film_write_passes.h + integrator/integrator_path_state.h + integrator/integrator_shader_eval.h + integrator/integrator_shadow_catcher.h kernel_textures.h kernel_types.h - kernel_work_stealing.h - kernel_write_passes.h + light/light.h + light/light_background.h + light/light_common.h + light/light_sample.h + sample/sample_jitter.h + sample/sample_lcg.h + sample/sample_mapping.h + sample/sample_mis.h + sample/sample_pattern.h + util/util_color.h + util/util_differential.h + util/util_lookup_table.h + util/util_profiling.h ) set(SRC_DEVICE_CPU_HEADERS @@ -102,6 +99,7 @@ set(SRC_DEVICE_GPU_HEADERS device/gpu/parallel_prefix_sum.h device/gpu/parallel_reduce.h device/gpu/parallel_sorted_index.h + device/gpu/work_stealing.h ) set(SRC_DEVICE_CUDA_HEADERS @@ -666,7 +664,7 @@ if(WITH_CYCLES_OSL) cycles_kernel_osl ) add_subdirectory(osl) - add_subdirectory(shaders) + add_subdirectory(osl/shaders) endif() # CPU module diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/bake/bake.h similarity index 97% rename from intern/cycles/kernel/kernel_bake.h rename to intern/cycles/kernel/bake/bake.h index 30a41b9d3e3..e234d56bd3c 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/bake/bake.h @@ -16,9 +16,8 @@ #pragma once -#include "kernel/kernel_differential.h" -#include "kernel/kernel_projection.h" -#include "kernel/kernel_shader.h" +#include "kernel/camera/camera_projection.h" +#include "kernel/integrator/integrator_shader_eval.h" #include "kernel/geom/geom.h" diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/camera/camera.h similarity index 99% rename from intern/cycles/kernel/kernel_camera.h rename to intern/cycles/kernel/camera/camera.h index 58a34668f45..66bc25bb879 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/camera/camera.h @@ -16,10 +16,10 @@ #pragma once -#include "kernel_differential.h" -#include "kernel_lookup_table.h" -#include "kernel_montecarlo.h" -#include "kernel_projection.h" +#include "kernel/camera/camera_projection.h" +#include "kernel/sample/sample_mapping.h" +#include "kernel/util/util_differential.h" +#include "kernel/util/util_lookup_table.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/kernel_projection.h b/intern/cycles/kernel/camera/camera_projection.h similarity index 100% rename from intern/cycles/kernel/kernel_projection.h rename to intern/cycles/kernel/camera/camera_projection.h diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h index c00890be54c..fa88c66f536 100644 --- a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h +++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h @@ -32,7 +32,7 @@ #pragma once -#include "kernel/kernel_montecarlo.h" +#include "kernel/sample/sample_mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/closure/bsdf_diffuse.h b/intern/cycles/kernel/closure/bsdf_diffuse.h index 16c9b428004..dd3b4500b1f 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse.h @@ -32,6 +32,8 @@ #pragma once +#include "kernel/sample/sample_mapping.h" + CCL_NAMESPACE_BEGIN typedef struct DiffuseBsdf { diff --git a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h index 8bff7709a32..1e70d3e534e 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h @@ -32,6 +32,8 @@ #pragma once +#include "kernel/sample/sample_mapping.h" + CCL_NAMESPACE_BEGIN #ifdef __OSL__ diff --git a/intern/cycles/kernel/closure/bsdf_hair_principled.h b/intern/cycles/kernel/closure/bsdf_hair_principled.h index a474c5661b3..ff554d4a60e 100644 --- a/intern/cycles/kernel/closure/bsdf_hair_principled.h +++ b/intern/cycles/kernel/closure/bsdf_hair_principled.h @@ -20,7 +20,7 @@ # include #endif -#include "kernel/kernel_color.h" +#include "kernel/util/util_color.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index a4e1b7a491c..28aac368f2b 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -32,8 +32,8 @@ #pragma once -#include "kernel/kernel_lookup_table.h" -#include "kernel/kernel_random.h" +#include "kernel/sample/sample_pattern.h" +#include "kernel/util/util_lookup_table.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index b7bd7faaa54..b1ab8d7ffd0 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -16,6 +16,9 @@ #pragma once +#include "kernel/sample/sample_lcg.h" +#include "kernel/sample/sample_mapping.h" + CCL_NAMESPACE_BEGIN /* Most of the code is based on the supplemental implementations from diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index 74390f768a2..0e3b21117b5 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -27,6 +27,8 @@ #include "kernel/closure/bsdf_util.h" +#include "kernel/sample/sample_mapping.h" + CCL_NAMESPACE_BEGIN enum PrincipledDiffuseBsdfComponents { diff --git a/intern/cycles/kernel/closure/bsdf_util.h b/intern/cycles/kernel/closure/bsdf_util.h index 873494c1e03..b1c16a037df 100644 --- a/intern/cycles/kernel/closure/bsdf_util.h +++ b/intern/cycles/kernel/closure/bsdf_util.h @@ -148,4 +148,110 @@ interpolate_fresnel_color(float3 L, float3 H, float ior, float F0, float3 cspec0 return cspec0 * (1.0f - FH) + make_float3(1.0f, 1.0f, 1.0f) * FH; } +ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N) +{ + float3 R = 2 * dot(N, I) * N - I; + + /* Reflection rays may always be at least as shallow as the incoming ray. */ + float threshold = min(0.9f * dot(Ng, I), 0.01f); + if (dot(Ng, R) >= threshold) { + return N; + } + + /* Form coordinate system with Ng as the Z axis and N inside the X-Z-plane. + * The X axis is found by normalizing the component of N that's orthogonal to Ng. + * The Y axis isn't actually needed. + */ + float NdotNg = dot(N, Ng); + float3 X = normalize(N - NdotNg * Ng); + + /* Keep math expressions. */ + /* clang-format off */ + /* Calculate N.z and N.x in the local coordinate system. + * + * The goal of this computation is to find a N' that is rotated towards Ng just enough + * to lift R' above the threshold (here called t), therefore dot(R', Ng) = t. + * + * According to the standard reflection equation, + * this means that we want dot(2*dot(N', I)*N' - I, Ng) = t. + * + * Since the Z axis of our local coordinate system is Ng, dot(x, Ng) is just x.z, so we get + * 2*dot(N', I)*N'.z - I.z = t. + * + * The rotation is simple to express in the coordinate system we formed - + * since N lies in the X-Z-plane, we know that N' will also lie in the X-Z-plane, + * so N'.y = 0 and therefore dot(N', I) = N'.x*I.x + N'.z*I.z . + * + * Furthermore, we want N' to be normalized, so N'.x = sqrt(1 - N'.z^2). + * + * With these simplifications, + * we get the final equation 2*(sqrt(1 - N'.z^2)*I.x + N'.z*I.z)*N'.z - I.z = t. + * + * The only unknown here is N'.z, so we can solve for that. + * + * The equation has four solutions in general: + * + * N'.z = +-sqrt(0.5*(+-sqrt(I.x^2*(I.x^2 + I.z^2 - t^2)) + t*I.z + I.x^2 + I.z^2)/(I.x^2 + I.z^2)) + * We can simplify this expression a bit by grouping terms: + * + * a = I.x^2 + I.z^2 + * b = sqrt(I.x^2 * (a - t^2)) + * c = I.z*t + a + * N'.z = +-sqrt(0.5*(+-b + c)/a) + * + * Two solutions can immediately be discarded because they're negative so N' would lie in the + * lower hemisphere. + */ + /* clang-format on */ + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = safe_sqrtf(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + /* Evaluate both solutions. + * In many cases one can be immediately discarded (if N'.z would be imaginary or larger than + * one), so check for that first. If no option is viable (might happen in extreme cases like N + * being in the wrong hemisphere), give up and return Ng. */ + float fac = 0.5f / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + bool valid1 = (N1_z2 > 1e-5f) && (N1_z2 <= (1.0f + 1e-5f)); + bool valid2 = (N2_z2 > 1e-5f) && (N2_z2 <= (1.0f + 1e-5f)); + + float2 N_new; + if (valid1 && valid2) { + /* If both are possible, do the expensive reflection-based check. */ + float2 N1 = make_float2(safe_sqrtf(1.0f - N1_z2), safe_sqrtf(N1_z2)); + float2 N2 = make_float2(safe_sqrtf(1.0f - N2_z2), safe_sqrtf(N2_z2)); + + float R1 = 2 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; + float R2 = 2 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; + + valid1 = (R1 >= 1e-5f); + valid2 = (R2 >= 1e-5f); + if (valid1 && valid2) { + /* If both solutions are valid, return the one with the shallower reflection since it will be + * closer to the input (if the original reflection wasn't shallow, we would not be in this + * part of the function). */ + N_new = (R1 < R2) ? N1 : N2; + } + else { + /* If only one reflection is valid (= positive), pick that one. */ + N_new = (R1 > R2) ? N1 : N2; + } + } + else if (valid1 || valid2) { + /* Only one solution passes the N'.z criterium, so pick that one. */ + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new = make_float2(safe_sqrtf(1.0f - Nz2), safe_sqrtf(Nz2)); + } + else { + return Ng; + } + + return N_new.x * X + N_new.y * Ng; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/cpu/globals.h b/intern/cycles/kernel/device/cpu/globals.h index fb9aae38cfc..f3e530a9edc 100644 --- a/intern/cycles/kernel/device/cpu/globals.h +++ b/intern/cycles/kernel/device/cpu/globals.h @@ -18,8 +18,8 @@ #pragma once -#include "kernel/kernel_profiling.h" #include "kernel/kernel_types.h" +#include "kernel/util/util_profiling.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h index ba777062113..148b6a33cb5 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h @@ -46,10 +46,11 @@ # include "kernel/integrator/integrator_shade_volume.h" # include "kernel/integrator/integrator_megakernel.h" -# include "kernel/kernel_film.h" -# include "kernel/kernel_adaptive_sampling.h" -# include "kernel/kernel_bake.h" -# include "kernel/kernel_id_passes.h" +# include "kernel/film/film_adaptive_sampling.h" +# include "kernel/film/film_read.h" +# include "kernel/film/film_id_passes.h" + +# include "kernel/bake/bake.h" #else # define STUB_ASSERT(arch, name) \ diff --git a/intern/cycles/kernel/device/cuda/globals.h b/intern/cycles/kernel/device/cuda/globals.h index 2c187cf8a23..cde935198b3 100644 --- a/intern/cycles/kernel/device/cuda/globals.h +++ b/intern/cycles/kernel/device/cuda/globals.h @@ -18,11 +18,12 @@ #pragma once -#include "kernel/kernel_profiling.h" #include "kernel/kernel_types.h" #include "kernel/integrator/integrator_state.h" +#include "kernel/util/util_profiling.h" + CCL_NAMESPACE_BEGIN /* Not actually used, just a NULL pointer that gets passed everywhere, which we diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index 335cb1ec0c0..aa360b3016a 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -19,6 +19,7 @@ #include "kernel/device/gpu/parallel_active_index.h" #include "kernel/device/gpu/parallel_prefix_sum.h" #include "kernel/device/gpu/parallel_sorted_index.h" +#include "kernel/device/gpu/work_stealing.h" #include "kernel/integrator/integrator_state.h" #include "kernel/integrator/integrator_state_flow.h" @@ -36,10 +37,10 @@ #include "kernel/integrator/integrator_shade_surface.h" #include "kernel/integrator/integrator_shade_volume.h" -#include "kernel/kernel_adaptive_sampling.h" -#include "kernel/kernel_bake.h" -#include "kernel/kernel_film.h" -#include "kernel/kernel_work_stealing.h" +#include "kernel/bake/bake.h" + +#include "kernel/film/film_adaptive_sampling.h" +#include "kernel/film/film_read.h" /* -------------------------------------------------------------------- * Integrator. diff --git a/intern/cycles/kernel/kernel_work_stealing.h b/intern/cycles/kernel/device/gpu/work_stealing.h similarity index 100% rename from intern/cycles/kernel/kernel_work_stealing.h rename to intern/cycles/kernel/device/gpu/work_stealing.h diff --git a/intern/cycles/kernel/device/hip/globals.h b/intern/cycles/kernel/device/hip/globals.h index 28e1cc4282f..079944bd8f2 100644 --- a/intern/cycles/kernel/device/hip/globals.h +++ b/intern/cycles/kernel/device/hip/globals.h @@ -18,11 +18,12 @@ #pragma once -#include "kernel/kernel_profiling.h" #include "kernel/kernel_types.h" #include "kernel/integrator/integrator_state.h" +#include "kernel/util/util_profiling.h" + CCL_NAMESPACE_BEGIN /* Not actually used, just a NULL pointer that gets passed everywhere, which we diff --git a/intern/cycles/kernel/device/optix/globals.h b/intern/cycles/kernel/device/optix/globals.h index 7b8ebfe50e6..e038bc1797a 100644 --- a/intern/cycles/kernel/device/optix/globals.h +++ b/intern/cycles/kernel/device/optix/globals.h @@ -18,11 +18,12 @@ #pragma once -#include "kernel/kernel_profiling.h" #include "kernel/kernel_types.h" #include "kernel/integrator/integrator_state.h" +#include "kernel/util/util_profiling.h" + CCL_NAMESPACE_BEGIN /* Not actually used, just a NULL pointer that gets passed everywhere, which we diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/film/film_accumulate.h similarity index 99% rename from intern/cycles/kernel/kernel_accumulate.h rename to intern/cycles/kernel/film/film_accumulate.h index c494cb4e189..91424fdbe21 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/film/film_accumulate.h @@ -16,10 +16,10 @@ #pragma once -#include "kernel_adaptive_sampling.h" -#include "kernel_random.h" -#include "kernel_shadow_catcher.h" -#include "kernel_write_passes.h" +#include "kernel/film/film_adaptive_sampling.h" +#include "kernel/film/film_write_passes.h" + +#include "kernel/integrator/integrator_shadow_catcher.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/kernel_adaptive_sampling.h b/intern/cycles/kernel/film/film_adaptive_sampling.h similarity index 99% rename from intern/cycles/kernel/kernel_adaptive_sampling.h rename to intern/cycles/kernel/film/film_adaptive_sampling.h index b80853fcc51..c78b5f6b707 100644 --- a/intern/cycles/kernel/kernel_adaptive_sampling.h +++ b/intern/cycles/kernel/film/film_adaptive_sampling.h @@ -16,7 +16,7 @@ #pragma once -#include "kernel/kernel_write_passes.h" +#include "kernel/film/film_write_passes.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/kernel_id_passes.h b/intern/cycles/kernel/film/film_id_passes.h similarity index 100% rename from intern/cycles/kernel/kernel_id_passes.h rename to intern/cycles/kernel/film/film_id_passes.h diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/film/film_passes.h similarity index 99% rename from intern/cycles/kernel/kernel_passes.h rename to intern/cycles/kernel/film/film_passes.h index 8ff7750c7d5..6c124247f89 100644 --- a/intern/cycles/kernel/kernel_passes.h +++ b/intern/cycles/kernel/film/film_passes.h @@ -18,8 +18,8 @@ #include "kernel/geom/geom.h" -#include "kernel/kernel_id_passes.h" -#include "kernel/kernel_write_passes.h" +#include "kernel/film/film_id_passes.h" +#include "kernel/film/film_write_passes.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/kernel_film.h b/intern/cycles/kernel/film/film_read.h similarity index 100% rename from intern/cycles/kernel/kernel_film.h rename to intern/cycles/kernel/film/film_read.h diff --git a/intern/cycles/kernel/kernel_write_passes.h b/intern/cycles/kernel/film/film_write_passes.h similarity index 100% rename from intern/cycles/kernel/kernel_write_passes.h rename to intern/cycles/kernel/film/film_write_passes.h diff --git a/intern/cycles/kernel/geom/geom_patch.h b/intern/cycles/kernel/geom/geom_patch.h index bd797ef52ab..bf1a06220aa 100644 --- a/intern/cycles/kernel/geom/geom_patch.h +++ b/intern/cycles/kernel/geom/geom_patch.h @@ -26,6 +26,8 @@ #pragma once +#include "util/util_color.h" + CCL_NAMESPACE_BEGIN typedef struct PatchHandle { diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h index 91b29c7f990..bc559e3c812 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/geom_primitive.h @@ -21,7 +21,7 @@ #pragma once -#include "kernel/kernel_projection.h" +#include "kernel/camera/camera_projection.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h index fee629cc75a..440dc23d124 100644 --- a/intern/cycles/kernel/geom/geom_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h @@ -22,7 +22,7 @@ #pragma once -#include "kernel/kernel_random.h" +#include "kernel/sample/sample_lcg.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_init_from_bake.h b/intern/cycles/kernel/integrator/integrator_init_from_bake.h index de916be24e7..5790cfd3f22 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_bake.h +++ b/intern/cycles/kernel/integrator/integrator_init_from_bake.h @@ -16,11 +16,14 @@ #pragma once -#include "kernel/kernel_accumulate.h" -#include "kernel/kernel_adaptive_sampling.h" -#include "kernel/kernel_camera.h" -#include "kernel/kernel_path_state.h" -#include "kernel/kernel_random.h" +#include "kernel/camera/camera.h" + +#include "kernel/film/film_accumulate.h" +#include "kernel/film/film_adaptive_sampling.h" + +#include "kernel/integrator/integrator_path_state.h" + +#include "kernel/sample/sample_pattern.h" #include "kernel/geom/geom.h" diff --git a/intern/cycles/kernel/integrator/integrator_init_from_camera.h b/intern/cycles/kernel/integrator/integrator_init_from_camera.h index 5bab6b2e2fd..499a72ffbc4 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_camera.h +++ b/intern/cycles/kernel/integrator/integrator_init_from_camera.h @@ -16,12 +16,15 @@ #pragma once -#include "kernel/kernel_accumulate.h" -#include "kernel/kernel_adaptive_sampling.h" -#include "kernel/kernel_camera.h" -#include "kernel/kernel_path_state.h" -#include "kernel/kernel_random.h" -#include "kernel/kernel_shadow_catcher.h" +#include "kernel/camera/camera.h" + +#include "kernel/film/film_accumulate.h" +#include "kernel/film/film_adaptive_sampling.h" + +#include "kernel/integrator/integrator_path_state.h" +#include "kernel/integrator/integrator_shadow_catcher.h" + +#include "kernel/sample/sample_pattern.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/integrator_intersect_closest.h index c1315d48694..41d3dfde41a 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_closest.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_closest.h @@ -16,11 +16,14 @@ #pragma once -#include "kernel/kernel_differential.h" -#include "kernel/kernel_light.h" -#include "kernel/kernel_path_state.h" -#include "kernel/kernel_projection.h" -#include "kernel/kernel_shadow_catcher.h" +#include "kernel/camera/camera_projection.h" + +#include "kernel/integrator/integrator_path_state.h" +#include "kernel/integrator/integrator_shadow_catcher.h" + +#include "kernel/light/light.h" + +#include "kernel/util/util_differential.h" #include "kernel/geom/geom.h" diff --git a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h index 7def3e2f3f3..505d9687948 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h @@ -18,8 +18,8 @@ #include "kernel/bvh/bvh.h" #include "kernel/geom/geom.h" +#include "kernel/integrator/integrator_shader_eval.h" #include "kernel/integrator/integrator_volume_stack.h" -#include "kernel/kernel_shader.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/integrator/integrator_path_state.h similarity index 99% rename from intern/cycles/kernel/kernel_path_state.h rename to intern/cycles/kernel/integrator/integrator_path_state.h index a0584f0b219..73062b26682 100644 --- a/intern/cycles/kernel/kernel_path_state.h +++ b/intern/cycles/kernel/integrator/integrator_path_state.h @@ -16,7 +16,7 @@ #pragma once -#include "kernel_random.h" +#include "kernel/sample/sample_pattern.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shade_background.h b/intern/cycles/kernel/integrator/integrator_shade_background.h index 287c54d7243..b3bef9a234e 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_background.h +++ b/intern/cycles/kernel/integrator/integrator_shade_background.h @@ -16,10 +16,11 @@ #pragma once -#include "kernel/kernel_accumulate.h" -#include "kernel/kernel_emission.h" -#include "kernel/kernel_light.h" -#include "kernel/kernel_shader.h" +#include "kernel/film/film_accumulate.h" +#include "kernel/integrator/integrator_shader_eval.h" +#include "kernel/light/light.h" +#include "kernel/light/light_sample.h" +#include "kernel/sample/sample_mis.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shade_light.h b/intern/cycles/kernel/integrator/integrator_shade_light.h index 4f0f5a39756..7d220006322 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_light.h +++ b/intern/cycles/kernel/integrator/integrator_shade_light.h @@ -16,10 +16,10 @@ #pragma once -#include "kernel/kernel_accumulate.h" -#include "kernel/kernel_emission.h" -#include "kernel/kernel_light.h" -#include "kernel/kernel_shader.h" +#include "kernel/film/film_accumulate.h" +#include "kernel/integrator/integrator_shader_eval.h" +#include "kernel/light/light.h" +#include "kernel/light/light_sample.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shade_shadow.h b/intern/cycles/kernel/integrator/integrator_shade_shadow.h index a82254e1dea..0c4eeb8d10d 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_shadow.h +++ b/intern/cycles/kernel/integrator/integrator_shade_shadow.h @@ -18,8 +18,7 @@ #include "kernel/integrator/integrator_shade_volume.h" #include "kernel/integrator/integrator_volume_stack.h" - -#include "kernel/kernel_shader.h" +#include "kernel/integrator/integrator_shader_eval.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h index 2a0bf4a3046..70dce1c4913 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h @@ -16,16 +16,19 @@ #pragma once -#include "kernel/kernel_accumulate.h" -#include "kernel/kernel_emission.h" -#include "kernel/kernel_light.h" -#include "kernel/kernel_passes.h" -#include "kernel/kernel_path_state.h" -#include "kernel/kernel_shader.h" +#include "kernel/film/film_accumulate.h" +#include "kernel/film/film_passes.h" +#include "kernel/integrator/integrator_path_state.h" +#include "kernel/integrator/integrator_shader_eval.h" #include "kernel/integrator/integrator_subsurface.h" #include "kernel/integrator/integrator_volume_stack.h" +#include "kernel/light/light.h" +#include "kernel/light/light_sample.h" + +#include "kernel/sample/sample_mis.h" + CCL_NAMESPACE_BEGIN ccl_device_forceinline void integrate_surface_shader_setup(KernelGlobals kg, diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index d0aabb550c0..44ef4803575 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -16,16 +16,19 @@ #pragma once -#include "kernel/kernel_accumulate.h" -#include "kernel/kernel_emission.h" -#include "kernel/kernel_light.h" -#include "kernel/kernel_passes.h" -#include "kernel/kernel_path_state.h" -#include "kernel/kernel_shader.h" +#include "kernel/film/film_accumulate.h" +#include "kernel/film/film_passes.h" +#include "kernel/integrator/integrator_path_state.h" +#include "kernel/integrator/integrator_shader_eval.h" #include "kernel/integrator/integrator_intersect_closest.h" #include "kernel/integrator/integrator_volume_stack.h" +#include "kernel/light/light.h" +#include "kernel/light/light_sample.h" + +#include "kernel/sample/sample_mis.h" + CCL_NAMESPACE_BEGIN #ifdef __VOLUME__ diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/integrator/integrator_shader_eval.h similarity index 99% rename from intern/cycles/kernel/kernel_shader.h rename to intern/cycles/kernel/integrator/integrator_shader_eval.h index 22db6d0124e..04a3a965fd3 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/integrator/integrator_shader_eval.h @@ -18,14 +18,13 @@ #pragma once -// clang-format off #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_util.h" #include "kernel/closure/bsdf.h" #include "kernel/closure/emissive.h" -// clang-format on -#include "kernel/kernel_accumulate.h" +#include "kernel/film/film_accumulate.h" + #include "kernel/svm/svm.h" #ifdef __OSL__ diff --git a/intern/cycles/kernel/kernel_shadow_catcher.h b/intern/cycles/kernel/integrator/integrator_shadow_catcher.h similarity index 98% rename from intern/cycles/kernel/kernel_shadow_catcher.h rename to intern/cycles/kernel/integrator/integrator_shadow_catcher.h index 9bed140b395..24d03466393 100644 --- a/intern/cycles/kernel/kernel_shadow_catcher.h +++ b/intern/cycles/kernel/integrator/integrator_shadow_catcher.h @@ -16,8 +16,8 @@ #pragma once +#include "kernel/integrator/integrator_path_state.h" #include "kernel/integrator/integrator_state_util.h" -#include "kernel/kernel_path_state.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h index 6e6b7f8a40f..0b1f67daa92 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/integrator_state_util.h @@ -17,7 +17,8 @@ #pragma once #include "kernel/integrator/integrator_state.h" -#include "kernel/kernel_differential.h" + +#include "kernel/util/util_differential.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/integrator_subsurface.h index e3bf9db80f7..9560641c460 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface.h @@ -16,9 +16,7 @@ #pragma once -#include "kernel/kernel_path_state.h" -#include "kernel/kernel_projection.h" -#include "kernel/kernel_shader.h" +#include "kernel/camera/camera_projection.h" #include "kernel/bvh/bvh.h" @@ -29,6 +27,8 @@ #include "kernel/closure/volume.h" #include "kernel/integrator/integrator_intersect_volume_stack.h" +#include "kernel/integrator/integrator_path_state.h" +#include "kernel/integrator/integrator_shader_eval.h" #include "kernel/integrator/integrator_subsurface_disk.h" #include "kernel/integrator/integrator_subsurface_random_walk.h" diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h index 2ab6d0961e3..b98acda1f4d 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "kernel/kernel_projection.h" +#include "kernel/camera/camera_projection.h" #include "kernel/bvh/bvh.h" diff --git a/intern/cycles/kernel/kernel_math.h b/intern/cycles/kernel/kernel_math.h deleted file mode 100644 index 3c5ab95bbc8..00000000000 --- a/intern/cycles/kernel/kernel_math.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/util_color.h" -#include "util/util_math.h" -#include "util/util_math_fast.h" -#include "util/util_math_intersect.h" -#include "util/util_projection.h" -#include "util/util_texture.h" -#include "util/util_transform.h" diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 94c27a1fca5..4312c1b67d2 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -22,10 +22,16 @@ # define __EMBREE__ #endif -#include "kernel/kernel_math.h" -#include "kernel/svm/svm_types.h" +#include "util/util_math.h" +#include "util/util_math_fast.h" +#include "util/util_math_intersect.h" +#include "util/util_projection.h" +#include "util/util_texture.h" +#include "util/util_transform.h" #include "util/util_static_assert.h" +#include "kernel/svm/svm_types.h" + #ifndef __KERNEL_GPU__ # define __KERNEL_CPU__ #endif diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/light/light.h similarity index 99% rename from intern/cycles/kernel/kernel_light.h rename to intern/cycles/kernel/light/light.h index b3eaed4fcb0..facbbe23d0f 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/light/light.h @@ -16,12 +16,9 @@ #pragma once -#include "geom/geom.h" - -#include "kernel_light_background.h" -#include "kernel_montecarlo.h" -#include "kernel_projection.h" -#include "kernel_types.h" +#include "kernel/geom/geom.h" +#include "kernel/light/light_background.h" +#include "kernel/sample/sample_mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/kernel_light_background.h b/intern/cycles/kernel/light/light_background.h similarity index 99% rename from intern/cycles/kernel/kernel_light_background.h rename to intern/cycles/kernel/light/light_background.h index 2e828b8b765..78f8c94f7a3 100644 --- a/intern/cycles/kernel/kernel_light_background.h +++ b/intern/cycles/kernel/light/light_background.h @@ -16,7 +16,7 @@ #pragma once -#include "kernel_light_common.h" +#include "kernel/light/light_common.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/kernel_light_common.h b/intern/cycles/kernel/light/light_common.h similarity index 99% rename from intern/cycles/kernel/kernel_light_common.h rename to intern/cycles/kernel/light/light_common.h index 9e2b738f376..207e89090cc 100644 --- a/intern/cycles/kernel/kernel_light_common.h +++ b/intern/cycles/kernel/light/light_common.h @@ -16,7 +16,7 @@ #pragma once -#include "kernel_montecarlo.h" +#include "kernel/sample/sample_mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/light/light_sample.h similarity index 97% rename from intern/cycles/kernel/kernel_emission.h rename to intern/cycles/kernel/light/light_sample.h index 8d329b8dac3..4ae5d9e1944 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/light/light_sample.h @@ -16,10 +16,12 @@ #pragma once -#include "kernel/kernel_light.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_path_state.h" -#include "kernel/kernel_shader.h" +#include "kernel/integrator/integrator_path_state.h" +#include "kernel/integrator/integrator_shader_eval.h" + +#include "kernel/light/light.h" + +#include "kernel/sample/sample_mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/osl/CMakeLists.txt b/intern/cycles/kernel/osl/CMakeLists.txt index 6cdc7367fbb..7be1b7129e0 100644 --- a/intern/cycles/kernel/osl/CMakeLists.txt +++ b/intern/cycles/kernel/osl/CMakeLists.txt @@ -39,7 +39,7 @@ set(HEADER_SRC ) set(LIB - cycles_render + cycles_scene ${OSL_LIBRARIES} ${OPENIMAGEIO_LIBRARIES} diff --git a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp index 45216f4c74d..2ec7f14c0fa 100644 --- a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp @@ -39,7 +39,6 @@ // clang-format off #include "kernel/kernel_types.h" -#include "kernel/kernel_montecarlo.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_diffuse_ramp.h" // clang-format on diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/osl_bssrdf.cpp index 5bf7b604498..3b8661ce45d 100644 --- a/intern/cycles/kernel/osl/osl_bssrdf.cpp +++ b/intern/cycles/kernel/osl/osl_bssrdf.cpp @@ -37,7 +37,6 @@ // clang-format off #include "kernel/kernel_types.h" -#include "kernel/kernel_montecarlo.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_util.h" diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp index a2062046ae8..89bab35b60b 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/osl_closures.cpp @@ -44,8 +44,6 @@ #include "kernel/device/cpu/globals.h" #include "kernel/kernel_types.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_random.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_util.h" diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index cbe1bf1bfc0..56b04fd280e 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -25,10 +25,10 @@ #include -#include "render/colorspace.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/colorspace.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" #include "kernel/osl/osl_closures.h" #include "kernel/osl/osl_globals.h" @@ -44,19 +44,22 @@ #include "kernel/device/cpu/globals.h" #include "kernel/device/cpu/image.h" -#include "kernel/kernel_differential.h" +#include "kernel/util/util_differential.h" #include "kernel/integrator/integrator_state.h" #include "kernel/integrator/integrator_state_flow.h" #include "kernel/geom/geom.h" + #include "kernel/bvh/bvh.h" -#include "kernel/kernel_color.h" -#include "kernel/kernel_camera.h" -#include "kernel/kernel_path_state.h" -#include "kernel/kernel_projection.h" -#include "kernel/kernel_shader.h" +#include "kernel/camera/camera.h" +#include "kernel/camera/camera_projection.h" + +#include "kernel/integrator/integrator_path_state.h" +#include "kernel/integrator/integrator_shader_eval.h" + +#include "kernel/util/util_color.h" // clang-format on CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp index fba207e7230..6426a09b33d 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/osl_shader.cpp @@ -20,7 +20,6 @@ #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" -#include "kernel/kernel_montecarlo.h" #include "kernel/kernel_types.h" #include "kernel/geom/geom_object.h" @@ -33,9 +32,7 @@ #include "kernel/osl/osl_shader.h" // clang-format on -#include "util/util_foreach.h" - -#include "render/attribute.h" +#include "scene/attribute.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/osl/shaders/CMakeLists.txt similarity index 100% rename from intern/cycles/kernel/shaders/CMakeLists.txt rename to intern/cycles/kernel/osl/shaders/CMakeLists.txt diff --git a/intern/cycles/kernel/shaders/node_absorption_volume.osl b/intern/cycles/kernel/osl/shaders/node_absorption_volume.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_absorption_volume.osl rename to intern/cycles/kernel/osl/shaders/node_absorption_volume.osl diff --git a/intern/cycles/kernel/shaders/node_add_closure.osl b/intern/cycles/kernel/osl/shaders/node_add_closure.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_add_closure.osl rename to intern/cycles/kernel/osl/shaders/node_add_closure.osl diff --git a/intern/cycles/kernel/shaders/node_ambient_occlusion.osl b/intern/cycles/kernel/osl/shaders/node_ambient_occlusion.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_ambient_occlusion.osl rename to intern/cycles/kernel/osl/shaders/node_ambient_occlusion.osl diff --git a/intern/cycles/kernel/shaders/node_anisotropic_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_anisotropic_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_anisotropic_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_anisotropic_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_attribute.osl b/intern/cycles/kernel/osl/shaders/node_attribute.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_attribute.osl rename to intern/cycles/kernel/osl/shaders/node_attribute.osl diff --git a/intern/cycles/kernel/shaders/node_background.osl b/intern/cycles/kernel/osl/shaders/node_background.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_background.osl rename to intern/cycles/kernel/osl/shaders/node_background.osl diff --git a/intern/cycles/kernel/shaders/node_bevel.osl b/intern/cycles/kernel/osl/shaders/node_bevel.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_bevel.osl rename to intern/cycles/kernel/osl/shaders/node_bevel.osl diff --git a/intern/cycles/kernel/shaders/node_blackbody.osl b/intern/cycles/kernel/osl/shaders/node_blackbody.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_blackbody.osl rename to intern/cycles/kernel/osl/shaders/node_blackbody.osl diff --git a/intern/cycles/kernel/shaders/node_brick_texture.osl b/intern/cycles/kernel/osl/shaders/node_brick_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_brick_texture.osl rename to intern/cycles/kernel/osl/shaders/node_brick_texture.osl diff --git a/intern/cycles/kernel/shaders/node_brightness.osl b/intern/cycles/kernel/osl/shaders/node_brightness.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_brightness.osl rename to intern/cycles/kernel/osl/shaders/node_brightness.osl diff --git a/intern/cycles/kernel/shaders/node_bump.osl b/intern/cycles/kernel/osl/shaders/node_bump.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_bump.osl rename to intern/cycles/kernel/osl/shaders/node_bump.osl diff --git a/intern/cycles/kernel/shaders/node_camera.osl b/intern/cycles/kernel/osl/shaders/node_camera.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_camera.osl rename to intern/cycles/kernel/osl/shaders/node_camera.osl diff --git a/intern/cycles/kernel/shaders/node_checker_texture.osl b/intern/cycles/kernel/osl/shaders/node_checker_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_checker_texture.osl rename to intern/cycles/kernel/osl/shaders/node_checker_texture.osl diff --git a/intern/cycles/kernel/shaders/node_clamp.osl b/intern/cycles/kernel/osl/shaders/node_clamp.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_clamp.osl rename to intern/cycles/kernel/osl/shaders/node_clamp.osl diff --git a/intern/cycles/kernel/shaders/node_color.h b/intern/cycles/kernel/osl/shaders/node_color.h similarity index 100% rename from intern/cycles/kernel/shaders/node_color.h rename to intern/cycles/kernel/osl/shaders/node_color.h diff --git a/intern/cycles/kernel/shaders/node_combine_hsv.osl b/intern/cycles/kernel/osl/shaders/node_combine_hsv.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_combine_hsv.osl rename to intern/cycles/kernel/osl/shaders/node_combine_hsv.osl diff --git a/intern/cycles/kernel/shaders/node_combine_rgb.osl b/intern/cycles/kernel/osl/shaders/node_combine_rgb.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_combine_rgb.osl rename to intern/cycles/kernel/osl/shaders/node_combine_rgb.osl diff --git a/intern/cycles/kernel/shaders/node_combine_xyz.osl b/intern/cycles/kernel/osl/shaders/node_combine_xyz.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_combine_xyz.osl rename to intern/cycles/kernel/osl/shaders/node_combine_xyz.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_color.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_color.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_color.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_color.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_float.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_float.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_float.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_float.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_int.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_int.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_int.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_int.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_normal.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_normal.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_normal.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_normal.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_point.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_point.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_point.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_point.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_string.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_string.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_string.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_string.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_vector.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_vector.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_vector.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_vector.osl diff --git a/intern/cycles/kernel/shaders/node_diffuse_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_diffuse_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_diffuse_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_diffuse_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_displacement.osl b/intern/cycles/kernel/osl/shaders/node_displacement.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_displacement.osl rename to intern/cycles/kernel/osl/shaders/node_displacement.osl diff --git a/intern/cycles/kernel/shaders/node_emission.osl b/intern/cycles/kernel/osl/shaders/node_emission.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_emission.osl rename to intern/cycles/kernel/osl/shaders/node_emission.osl diff --git a/intern/cycles/kernel/shaders/node_environment_texture.osl b/intern/cycles/kernel/osl/shaders/node_environment_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_environment_texture.osl rename to intern/cycles/kernel/osl/shaders/node_environment_texture.osl diff --git a/intern/cycles/kernel/shaders/node_float_curve.osl b/intern/cycles/kernel/osl/shaders/node_float_curve.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_float_curve.osl rename to intern/cycles/kernel/osl/shaders/node_float_curve.osl diff --git a/intern/cycles/kernel/shaders/node_fresnel.h b/intern/cycles/kernel/osl/shaders/node_fresnel.h similarity index 100% rename from intern/cycles/kernel/shaders/node_fresnel.h rename to intern/cycles/kernel/osl/shaders/node_fresnel.h diff --git a/intern/cycles/kernel/shaders/node_fresnel.osl b/intern/cycles/kernel/osl/shaders/node_fresnel.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_fresnel.osl rename to intern/cycles/kernel/osl/shaders/node_fresnel.osl diff --git a/intern/cycles/kernel/shaders/node_gamma.osl b/intern/cycles/kernel/osl/shaders/node_gamma.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_gamma.osl rename to intern/cycles/kernel/osl/shaders/node_gamma.osl diff --git a/intern/cycles/kernel/shaders/node_geometry.osl b/intern/cycles/kernel/osl/shaders/node_geometry.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_geometry.osl rename to intern/cycles/kernel/osl/shaders/node_geometry.osl diff --git a/intern/cycles/kernel/shaders/node_glass_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_glass_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_glass_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_glass_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_glossy_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_glossy_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_glossy_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_glossy_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_gradient_texture.osl b/intern/cycles/kernel/osl/shaders/node_gradient_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_gradient_texture.osl rename to intern/cycles/kernel/osl/shaders/node_gradient_texture.osl diff --git a/intern/cycles/kernel/shaders/node_hair_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_hair_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_hair_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_hair_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_hair_info.osl b/intern/cycles/kernel/osl/shaders/node_hair_info.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_hair_info.osl rename to intern/cycles/kernel/osl/shaders/node_hair_info.osl diff --git a/intern/cycles/kernel/shaders/node_hash.h b/intern/cycles/kernel/osl/shaders/node_hash.h similarity index 100% rename from intern/cycles/kernel/shaders/node_hash.h rename to intern/cycles/kernel/osl/shaders/node_hash.h diff --git a/intern/cycles/kernel/shaders/node_holdout.osl b/intern/cycles/kernel/osl/shaders/node_holdout.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_holdout.osl rename to intern/cycles/kernel/osl/shaders/node_holdout.osl diff --git a/intern/cycles/kernel/shaders/node_hsv.osl b/intern/cycles/kernel/osl/shaders/node_hsv.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_hsv.osl rename to intern/cycles/kernel/osl/shaders/node_hsv.osl diff --git a/intern/cycles/kernel/shaders/node_ies_light.osl b/intern/cycles/kernel/osl/shaders/node_ies_light.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_ies_light.osl rename to intern/cycles/kernel/osl/shaders/node_ies_light.osl diff --git a/intern/cycles/kernel/shaders/node_image_texture.osl b/intern/cycles/kernel/osl/shaders/node_image_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_image_texture.osl rename to intern/cycles/kernel/osl/shaders/node_image_texture.osl diff --git a/intern/cycles/kernel/shaders/node_invert.osl b/intern/cycles/kernel/osl/shaders/node_invert.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_invert.osl rename to intern/cycles/kernel/osl/shaders/node_invert.osl diff --git a/intern/cycles/kernel/shaders/node_layer_weight.osl b/intern/cycles/kernel/osl/shaders/node_layer_weight.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_layer_weight.osl rename to intern/cycles/kernel/osl/shaders/node_layer_weight.osl diff --git a/intern/cycles/kernel/shaders/node_light_falloff.osl b/intern/cycles/kernel/osl/shaders/node_light_falloff.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_light_falloff.osl rename to intern/cycles/kernel/osl/shaders/node_light_falloff.osl diff --git a/intern/cycles/kernel/shaders/node_light_path.osl b/intern/cycles/kernel/osl/shaders/node_light_path.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_light_path.osl rename to intern/cycles/kernel/osl/shaders/node_light_path.osl diff --git a/intern/cycles/kernel/shaders/node_magic_texture.osl b/intern/cycles/kernel/osl/shaders/node_magic_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_magic_texture.osl rename to intern/cycles/kernel/osl/shaders/node_magic_texture.osl diff --git a/intern/cycles/kernel/shaders/node_map_range.osl b/intern/cycles/kernel/osl/shaders/node_map_range.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_map_range.osl rename to intern/cycles/kernel/osl/shaders/node_map_range.osl diff --git a/intern/cycles/kernel/shaders/node_mapping.osl b/intern/cycles/kernel/osl/shaders/node_mapping.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_mapping.osl rename to intern/cycles/kernel/osl/shaders/node_mapping.osl diff --git a/intern/cycles/kernel/shaders/node_math.h b/intern/cycles/kernel/osl/shaders/node_math.h similarity index 100% rename from intern/cycles/kernel/shaders/node_math.h rename to intern/cycles/kernel/osl/shaders/node_math.h diff --git a/intern/cycles/kernel/shaders/node_math.osl b/intern/cycles/kernel/osl/shaders/node_math.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_math.osl rename to intern/cycles/kernel/osl/shaders/node_math.osl diff --git a/intern/cycles/kernel/shaders/node_mix.osl b/intern/cycles/kernel/osl/shaders/node_mix.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_mix.osl rename to intern/cycles/kernel/osl/shaders/node_mix.osl diff --git a/intern/cycles/kernel/shaders/node_mix_closure.osl b/intern/cycles/kernel/osl/shaders/node_mix_closure.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_mix_closure.osl rename to intern/cycles/kernel/osl/shaders/node_mix_closure.osl diff --git a/intern/cycles/kernel/shaders/node_musgrave_texture.osl b/intern/cycles/kernel/osl/shaders/node_musgrave_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_musgrave_texture.osl rename to intern/cycles/kernel/osl/shaders/node_musgrave_texture.osl diff --git a/intern/cycles/kernel/shaders/node_noise.h b/intern/cycles/kernel/osl/shaders/node_noise.h similarity index 100% rename from intern/cycles/kernel/shaders/node_noise.h rename to intern/cycles/kernel/osl/shaders/node_noise.h diff --git a/intern/cycles/kernel/shaders/node_noise_texture.osl b/intern/cycles/kernel/osl/shaders/node_noise_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_noise_texture.osl rename to intern/cycles/kernel/osl/shaders/node_noise_texture.osl diff --git a/intern/cycles/kernel/shaders/node_normal.osl b/intern/cycles/kernel/osl/shaders/node_normal.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_normal.osl rename to intern/cycles/kernel/osl/shaders/node_normal.osl diff --git a/intern/cycles/kernel/shaders/node_normal_map.osl b/intern/cycles/kernel/osl/shaders/node_normal_map.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_normal_map.osl rename to intern/cycles/kernel/osl/shaders/node_normal_map.osl diff --git a/intern/cycles/kernel/shaders/node_object_info.osl b/intern/cycles/kernel/osl/shaders/node_object_info.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_object_info.osl rename to intern/cycles/kernel/osl/shaders/node_object_info.osl diff --git a/intern/cycles/kernel/shaders/node_output_displacement.osl b/intern/cycles/kernel/osl/shaders/node_output_displacement.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_output_displacement.osl rename to intern/cycles/kernel/osl/shaders/node_output_displacement.osl diff --git a/intern/cycles/kernel/shaders/node_output_surface.osl b/intern/cycles/kernel/osl/shaders/node_output_surface.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_output_surface.osl rename to intern/cycles/kernel/osl/shaders/node_output_surface.osl diff --git a/intern/cycles/kernel/shaders/node_output_volume.osl b/intern/cycles/kernel/osl/shaders/node_output_volume.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_output_volume.osl rename to intern/cycles/kernel/osl/shaders/node_output_volume.osl diff --git a/intern/cycles/kernel/shaders/node_particle_info.osl b/intern/cycles/kernel/osl/shaders/node_particle_info.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_particle_info.osl rename to intern/cycles/kernel/osl/shaders/node_particle_info.osl diff --git a/intern/cycles/kernel/shaders/node_principled_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_principled_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_principled_hair_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_principled_hair_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_principled_hair_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_principled_hair_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_principled_volume.osl b/intern/cycles/kernel/osl/shaders/node_principled_volume.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_principled_volume.osl rename to intern/cycles/kernel/osl/shaders/node_principled_volume.osl diff --git a/intern/cycles/kernel/shaders/node_ramp_util.h b/intern/cycles/kernel/osl/shaders/node_ramp_util.h similarity index 100% rename from intern/cycles/kernel/shaders/node_ramp_util.h rename to intern/cycles/kernel/osl/shaders/node_ramp_util.h diff --git a/intern/cycles/kernel/shaders/node_refraction_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_refraction_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_refraction_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_refraction_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_rgb_curves.osl b/intern/cycles/kernel/osl/shaders/node_rgb_curves.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_rgb_curves.osl rename to intern/cycles/kernel/osl/shaders/node_rgb_curves.osl diff --git a/intern/cycles/kernel/shaders/node_rgb_ramp.osl b/intern/cycles/kernel/osl/shaders/node_rgb_ramp.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_rgb_ramp.osl rename to intern/cycles/kernel/osl/shaders/node_rgb_ramp.osl diff --git a/intern/cycles/kernel/shaders/node_rgb_to_bw.osl b/intern/cycles/kernel/osl/shaders/node_rgb_to_bw.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_rgb_to_bw.osl rename to intern/cycles/kernel/osl/shaders/node_rgb_to_bw.osl diff --git a/intern/cycles/kernel/shaders/node_scatter_volume.osl b/intern/cycles/kernel/osl/shaders/node_scatter_volume.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_scatter_volume.osl rename to intern/cycles/kernel/osl/shaders/node_scatter_volume.osl diff --git a/intern/cycles/kernel/shaders/node_separate_hsv.osl b/intern/cycles/kernel/osl/shaders/node_separate_hsv.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_separate_hsv.osl rename to intern/cycles/kernel/osl/shaders/node_separate_hsv.osl diff --git a/intern/cycles/kernel/shaders/node_separate_rgb.osl b/intern/cycles/kernel/osl/shaders/node_separate_rgb.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_separate_rgb.osl rename to intern/cycles/kernel/osl/shaders/node_separate_rgb.osl diff --git a/intern/cycles/kernel/shaders/node_separate_xyz.osl b/intern/cycles/kernel/osl/shaders/node_separate_xyz.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_separate_xyz.osl rename to intern/cycles/kernel/osl/shaders/node_separate_xyz.osl diff --git a/intern/cycles/kernel/shaders/node_set_normal.osl b/intern/cycles/kernel/osl/shaders/node_set_normal.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_set_normal.osl rename to intern/cycles/kernel/osl/shaders/node_set_normal.osl diff --git a/intern/cycles/kernel/shaders/node_sky_texture.osl b/intern/cycles/kernel/osl/shaders/node_sky_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_sky_texture.osl rename to intern/cycles/kernel/osl/shaders/node_sky_texture.osl diff --git a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl b/intern/cycles/kernel/osl/shaders/node_subsurface_scattering.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_subsurface_scattering.osl rename to intern/cycles/kernel/osl/shaders/node_subsurface_scattering.osl diff --git a/intern/cycles/kernel/shaders/node_tangent.osl b/intern/cycles/kernel/osl/shaders/node_tangent.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_tangent.osl rename to intern/cycles/kernel/osl/shaders/node_tangent.osl diff --git a/intern/cycles/kernel/shaders/node_texture_coordinate.osl b/intern/cycles/kernel/osl/shaders/node_texture_coordinate.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_texture_coordinate.osl rename to intern/cycles/kernel/osl/shaders/node_texture_coordinate.osl diff --git a/intern/cycles/kernel/shaders/node_toon_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_toon_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_toon_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_toon_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_translucent_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_translucent_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_translucent_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_translucent_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_transparent_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_transparent_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_transparent_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_transparent_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_uv_map.osl b/intern/cycles/kernel/osl/shaders/node_uv_map.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_uv_map.osl rename to intern/cycles/kernel/osl/shaders/node_uv_map.osl diff --git a/intern/cycles/kernel/shaders/node_value.osl b/intern/cycles/kernel/osl/shaders/node_value.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_value.osl rename to intern/cycles/kernel/osl/shaders/node_value.osl diff --git a/intern/cycles/kernel/shaders/node_vector_curves.osl b/intern/cycles/kernel/osl/shaders/node_vector_curves.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vector_curves.osl rename to intern/cycles/kernel/osl/shaders/node_vector_curves.osl diff --git a/intern/cycles/kernel/shaders/node_vector_displacement.osl b/intern/cycles/kernel/osl/shaders/node_vector_displacement.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vector_displacement.osl rename to intern/cycles/kernel/osl/shaders/node_vector_displacement.osl diff --git a/intern/cycles/kernel/shaders/node_vector_math.osl b/intern/cycles/kernel/osl/shaders/node_vector_math.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vector_math.osl rename to intern/cycles/kernel/osl/shaders/node_vector_math.osl diff --git a/intern/cycles/kernel/shaders/node_vector_rotate.osl b/intern/cycles/kernel/osl/shaders/node_vector_rotate.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vector_rotate.osl rename to intern/cycles/kernel/osl/shaders/node_vector_rotate.osl diff --git a/intern/cycles/kernel/shaders/node_vector_transform.osl b/intern/cycles/kernel/osl/shaders/node_vector_transform.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vector_transform.osl rename to intern/cycles/kernel/osl/shaders/node_vector_transform.osl diff --git a/intern/cycles/kernel/shaders/node_velvet_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_velvet_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_velvet_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_velvet_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_vertex_color.osl b/intern/cycles/kernel/osl/shaders/node_vertex_color.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vertex_color.osl rename to intern/cycles/kernel/osl/shaders/node_vertex_color.osl diff --git a/intern/cycles/kernel/shaders/node_voronoi_texture.osl b/intern/cycles/kernel/osl/shaders/node_voronoi_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_voronoi_texture.osl rename to intern/cycles/kernel/osl/shaders/node_voronoi_texture.osl diff --git a/intern/cycles/kernel/shaders/node_voxel_texture.osl b/intern/cycles/kernel/osl/shaders/node_voxel_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_voxel_texture.osl rename to intern/cycles/kernel/osl/shaders/node_voxel_texture.osl diff --git a/intern/cycles/kernel/shaders/node_wave_texture.osl b/intern/cycles/kernel/osl/shaders/node_wave_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_wave_texture.osl rename to intern/cycles/kernel/osl/shaders/node_wave_texture.osl diff --git a/intern/cycles/kernel/shaders/node_wavelength.osl b/intern/cycles/kernel/osl/shaders/node_wavelength.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_wavelength.osl rename to intern/cycles/kernel/osl/shaders/node_wavelength.osl diff --git a/intern/cycles/kernel/shaders/node_white_noise_texture.osl b/intern/cycles/kernel/osl/shaders/node_white_noise_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_white_noise_texture.osl rename to intern/cycles/kernel/osl/shaders/node_white_noise_texture.osl diff --git a/intern/cycles/kernel/shaders/node_wireframe.osl b/intern/cycles/kernel/osl/shaders/node_wireframe.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_wireframe.osl rename to intern/cycles/kernel/osl/shaders/node_wireframe.osl diff --git a/intern/cycles/kernel/shaders/stdcycles.h b/intern/cycles/kernel/osl/shaders/stdcycles.h similarity index 100% rename from intern/cycles/kernel/shaders/stdcycles.h rename to intern/cycles/kernel/osl/shaders/stdcycles.h diff --git a/intern/cycles/kernel/kernel_jitter.h b/intern/cycles/kernel/sample/sample_jitter.h similarity index 100% rename from intern/cycles/kernel/kernel_jitter.h rename to intern/cycles/kernel/sample/sample_jitter.h diff --git a/intern/cycles/kernel/sample/sample_lcg.h b/intern/cycles/kernel/sample/sample_lcg.h new file mode 100644 index 00000000000..92cfff639b4 --- /dev/null +++ b/intern/cycles/kernel/sample/sample_lcg.h @@ -0,0 +1,51 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Linear Congruential Generator */ + +ccl_device uint lcg_step_uint(uint *rng) +{ + /* implicit mod 2^32 */ + *rng = (1103515245 * (*rng) + 12345); + return *rng; +} + +ccl_device float lcg_step_float(uint *rng) +{ + /* implicit mod 2^32 */ + *rng = (1103515245 * (*rng) + 12345); + return (float)*rng * (1.0f / (float)0xFFFFFFFF); +} + +ccl_device uint lcg_init(uint seed) +{ + uint rng = seed; + lcg_step_uint(&rng); + return rng; +} + +ccl_device_inline uint lcg_state_init(const uint rng_hash, + const uint rng_offset, + const uint sample, + const uint scramble) +{ + return lcg_init(rng_hash + rng_offset + sample * scramble); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/sample/sample_mapping.h similarity index 57% rename from intern/cycles/kernel/kernel_montecarlo.h rename to intern/cycles/kernel/sample/sample_mapping.h index c931aa45276..3297aa2a29a 100644 --- a/intern/cycles/kernel/kernel_montecarlo.h +++ b/intern/cycles/kernel/sample/sample_mapping.h @@ -126,31 +126,6 @@ ccl_device float3 sample_uniform_sphere(float u1, float u2) return make_float3(x, y, z); } -ccl_device float balance_heuristic(float a, float b) -{ - return (a) / (a + b); -} - -ccl_device float balance_heuristic_3(float a, float b, float c) -{ - return (a) / (a + b + c); -} - -ccl_device float power_heuristic(float a, float b) -{ - return (a * a) / (a * a + b * b); -} - -ccl_device float power_heuristic_3(float a, float b, float c) -{ - return (a * a) / (a * a + b * b + c * c); -} - -ccl_device float max_heuristic(float a, float b) -{ - return (a > b) ? 1.0f : 0.0f; -} - /* distribute uniform xy on [0,1] over unit disk [-1,1], with concentric mapping * to better preserve stratification for some RNG sequences */ ccl_device float2 concentric_sample_disk(float u1, float u2) @@ -199,110 +174,4 @@ ccl_device float2 regular_polygon_sample(float corners, float rotation, float u, return make_float2(cr * p.x - sr * p.y, sr * p.x + cr * p.y); } -ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N) -{ - float3 R = 2 * dot(N, I) * N - I; - - /* Reflection rays may always be at least as shallow as the incoming ray. */ - float threshold = min(0.9f * dot(Ng, I), 0.01f); - if (dot(Ng, R) >= threshold) { - return N; - } - - /* Form coordinate system with Ng as the Z axis and N inside the X-Z-plane. - * The X axis is found by normalizing the component of N that's orthogonal to Ng. - * The Y axis isn't actually needed. - */ - float NdotNg = dot(N, Ng); - float3 X = normalize(N - NdotNg * Ng); - - /* Keep math expressions. */ - /* clang-format off */ - /* Calculate N.z and N.x in the local coordinate system. - * - * The goal of this computation is to find a N' that is rotated towards Ng just enough - * to lift R' above the threshold (here called t), therefore dot(R', Ng) = t. - * - * According to the standard reflection equation, - * this means that we want dot(2*dot(N', I)*N' - I, Ng) = t. - * - * Since the Z axis of our local coordinate system is Ng, dot(x, Ng) is just x.z, so we get - * 2*dot(N', I)*N'.z - I.z = t. - * - * The rotation is simple to express in the coordinate system we formed - - * since N lies in the X-Z-plane, we know that N' will also lie in the X-Z-plane, - * so N'.y = 0 and therefore dot(N', I) = N'.x*I.x + N'.z*I.z . - * - * Furthermore, we want N' to be normalized, so N'.x = sqrt(1 - N'.z^2). - * - * With these simplifications, - * we get the final equation 2*(sqrt(1 - N'.z^2)*I.x + N'.z*I.z)*N'.z - I.z = t. - * - * The only unknown here is N'.z, so we can solve for that. - * - * The equation has four solutions in general: - * - * N'.z = +-sqrt(0.5*(+-sqrt(I.x^2*(I.x^2 + I.z^2 - t^2)) + t*I.z + I.x^2 + I.z^2)/(I.x^2 + I.z^2)) - * We can simplify this expression a bit by grouping terms: - * - * a = I.x^2 + I.z^2 - * b = sqrt(I.x^2 * (a - t^2)) - * c = I.z*t + a - * N'.z = +-sqrt(0.5*(+-b + c)/a) - * - * Two solutions can immediately be discarded because they're negative so N' would lie in the - * lower hemisphere. - */ - /* clang-format on */ - - float Ix = dot(I, X), Iz = dot(I, Ng); - float Ix2 = sqr(Ix), Iz2 = sqr(Iz); - float a = Ix2 + Iz2; - - float b = safe_sqrtf(Ix2 * (a - sqr(threshold))); - float c = Iz * threshold + a; - - /* Evaluate both solutions. - * In many cases one can be immediately discarded (if N'.z would be imaginary or larger than - * one), so check for that first. If no option is viable (might happen in extreme cases like N - * being in the wrong hemisphere), give up and return Ng. */ - float fac = 0.5f / a; - float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); - bool valid1 = (N1_z2 > 1e-5f) && (N1_z2 <= (1.0f + 1e-5f)); - bool valid2 = (N2_z2 > 1e-5f) && (N2_z2 <= (1.0f + 1e-5f)); - - float2 N_new; - if (valid1 && valid2) { - /* If both are possible, do the expensive reflection-based check. */ - float2 N1 = make_float2(safe_sqrtf(1.0f - N1_z2), safe_sqrtf(N1_z2)); - float2 N2 = make_float2(safe_sqrtf(1.0f - N2_z2), safe_sqrtf(N2_z2)); - - float R1 = 2 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; - float R2 = 2 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; - - valid1 = (R1 >= 1e-5f); - valid2 = (R2 >= 1e-5f); - if (valid1 && valid2) { - /* If both solutions are valid, return the one with the shallower reflection since it will be - * closer to the input (if the original reflection wasn't shallow, we would not be in this - * part of the function). */ - N_new = (R1 < R2) ? N1 : N2; - } - else { - /* If only one reflection is valid (= positive), pick that one. */ - N_new = (R1 > R2) ? N1 : N2; - } - } - else if (valid1 || valid2) { - /* Only one solution passes the N'.z criterium, so pick that one. */ - float Nz2 = valid1 ? N1_z2 : N2_z2; - N_new = make_float2(safe_sqrtf(1.0f - Nz2), safe_sqrtf(Nz2)); - } - else { - return Ng; - } - - return N_new.x * X + N_new.y * Ng; -} - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/sample/sample_mis.h b/intern/cycles/kernel/sample/sample_mis.h new file mode 100644 index 00000000000..0878b3aac36 --- /dev/null +++ b/intern/cycles/kernel/sample/sample_mis.h @@ -0,0 +1,64 @@ +/* + * Parts adapted from Open Shading Language with this license: + * + * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. + * All Rights Reserved. + * + * Modifications Copyright 2011, Blender Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sony Pictures Imageworks nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Multiple importance sampling utilities. */ + +ccl_device float balance_heuristic(float a, float b) +{ + return (a) / (a + b); +} + +ccl_device float balance_heuristic_3(float a, float b, float c) +{ + return (a) / (a + b + c); +} + +ccl_device float power_heuristic(float a, float b) +{ + return (a * a) / (a * a + b * b); +} + +ccl_device float power_heuristic_3(float a, float b, float c) +{ + return (a * a) / (a * a + b * b + c * c); +} + +ccl_device float max_heuristic(float a, float b) +{ + return (a > b) ? 1.0f : 0.0f; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/sample/sample_pattern.h similarity index 87% rename from intern/cycles/kernel/kernel_random.h rename to intern/cycles/kernel/sample/sample_pattern.h index e5e87453611..95635c2c855 100644 --- a/intern/cycles/kernel/kernel_random.h +++ b/intern/cycles/kernel/sample/sample_pattern.h @@ -15,7 +15,7 @@ */ #pragma once -#include "kernel/kernel_jitter.h" +#include "kernel/sample/sample_jitter.h" #include "util/util_hash.h" CCL_NAMESPACE_BEGIN @@ -157,37 +157,6 @@ ccl_device_inline uint path_rng_hash_init(KernelGlobals kg, return rng_hash; } -/* Linear Congruential Generator */ - -ccl_device uint lcg_step_uint(uint *rng) -{ - /* implicit mod 2^32 */ - *rng = (1103515245 * (*rng) + 12345); - return *rng; -} - -ccl_device float lcg_step_float(uint *rng) -{ - /* implicit mod 2^32 */ - *rng = (1103515245 * (*rng) + 12345); - return (float)*rng * (1.0f / (float)0xFFFFFFFF); -} - -ccl_device uint lcg_init(uint seed) -{ - uint rng = seed; - lcg_step_uint(&rng); - return rng; -} - -ccl_device_inline uint lcg_state_init(const uint rng_hash, - const uint rng_offset, - const uint sample, - const uint scramble) -{ - return lcg_init(rng_hash + rng_offset + sample * scramble); -} - ccl_device_inline bool sample_is_even(int pattern, int sample) { if (pattern == SAMPLING_PATTERN_PMJ) { diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h index 833a6443b3c..92be7fb6906 100644 --- a/intern/cycles/kernel/svm/svm_aov.h +++ b/intern/cycles/kernel/svm/svm_aov.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "kernel/kernel_write_passes.h" +#include "kernel/film/film_write_passes.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/svm_bevel.h index 292887beedf..0a30822aa68 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/svm_bevel.h @@ -15,8 +15,8 @@ */ #include "kernel/bvh/bvh.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_random.h" +#include "kernel/sample/sample_mapping.h" +#include "kernel/sample/sample_pattern.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/svm/svm_displace.h b/intern/cycles/kernel/svm/svm_displace.h index f2446c3b3ef..8429fe1d3e0 100644 --- a/intern/cycles/kernel/svm/svm_displace.h +++ b/intern/cycles/kernel/svm/svm_displace.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "kernel/kernel_montecarlo.h" +#include "kernel/sample/sample_mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index fe777eb34c8..9af0a818cad 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -14,9 +14,9 @@ * limitations under the License. */ +#include "kernel/camera/camera.h" #include "kernel/geom/geom.h" -#include "kernel/kernel_camera.h" -#include "kernel/kernel_montecarlo.h" +#include "kernel/sample/sample_mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/kernel_color.h b/intern/cycles/kernel/util/util_color.h similarity index 100% rename from intern/cycles/kernel/kernel_color.h rename to intern/cycles/kernel/util/util_color.h diff --git a/intern/cycles/kernel/kernel_differential.h b/intern/cycles/kernel/util/util_differential.h similarity index 100% rename from intern/cycles/kernel/kernel_differential.h rename to intern/cycles/kernel/util/util_differential.h diff --git a/intern/cycles/kernel/kernel_lookup_table.h b/intern/cycles/kernel/util/util_lookup_table.h similarity index 100% rename from intern/cycles/kernel/kernel_lookup_table.h rename to intern/cycles/kernel/util/util_lookup_table.h diff --git a/intern/cycles/kernel/kernel_profiling.h b/intern/cycles/kernel/util/util_profiling.h similarity index 100% rename from intern/cycles/kernel/kernel_profiling.h rename to intern/cycles/kernel/util/util_profiling.h diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/scene/CMakeLists.txt similarity index 87% rename from intern/cycles/render/CMakeLists.txt rename to intern/cycles/scene/CMakeLists.txt index 323222b8c85..a3fde99306b 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/scene/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2011-2020 Blender Foundation +# Copyright 2011-2021 Blender Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,28 +14,20 @@ set(INC .. - ../../glew-mx ../../sky/include ) -set(INC_SYS - ${GLEW_INCLUDE_DIR} -) - set(SRC alembic.cpp alembic_read.cpp attribute.cpp background.cpp bake.cpp - buffers.cpp camera.cpp colorspace.cpp constant_fold.cpp - denoising.cpp film.cpp geometry.cpp - graph.cpp hair.cpp image.cpp image_oiio.cpp @@ -44,11 +36,9 @@ set(SRC integrator.cpp jitter.cpp light.cpp - merge.cpp mesh.cpp mesh_displace.cpp mesh_subdivision.cpp - nodes.cpp procedural.cpp object.cpp osl.cpp @@ -56,13 +46,13 @@ set(SRC pass.cpp curves.cpp scene.cpp - session.cpp shader.cpp + shader_graph.cpp + shader_nodes.cpp sobol.cpp stats.cpp svm.cpp tables.cpp - tile.cpp volume.cpp ) @@ -72,16 +62,11 @@ set(SRC_HEADERS attribute.h bake.h background.h - buffers.h camera.h colorspace.h constant_fold.h - denoising.h - display_driver.h - output_driver.h film.h geometry.h - graph.h hair.h image.h image_oiio.h @@ -90,9 +75,7 @@ set(SRC_HEADERS integrator.h light.h jitter.h - merge.h mesh.h - nodes.h object.h osl.h particles.h @@ -100,13 +83,13 @@ set(SRC_HEADERS procedural.h curves.h scene.h - session.h shader.h + shader_graph.h + shader_nodes.h sobol.h stats.h svm.h tables.h - tile.h volume.h ) @@ -177,4 +160,4 @@ include_directories(SYSTEM ${INC_SYS}) add_definitions(${GL_DEFINITIONS}) -cycles_add_library(cycles_render "${LIB}" ${SRC} ${SRC_HEADERS}) +cycles_add_library(cycles_scene "${LIB}" ${SRC} ${SRC_HEADERS}) diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/scene/alembic.cpp similarity index 99% rename from intern/cycles/render/alembic.cpp rename to intern/cycles/scene/alembic.cpp index 69bc0712674..07a969e88d3 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/scene/alembic.cpp @@ -14,15 +14,15 @@ * limitations under the License. */ -#include "render/alembic.h" +#include "scene/alembic.h" -#include "render/alembic_read.h" -#include "render/camera.h" -#include "render/curves.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/alembic_read.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" #include "util/util_foreach.h" #include "util/util_logging.h" diff --git a/intern/cycles/render/alembic.h b/intern/cycles/scene/alembic.h similarity index 99% rename from intern/cycles/render/alembic.h rename to intern/cycles/scene/alembic.h index 8e166a5ab04..9aeef273910 100644 --- a/intern/cycles/render/alembic.h +++ b/intern/cycles/scene/alembic.h @@ -17,8 +17,8 @@ #pragma once #include "graph/node.h" -#include "render/attribute.h" -#include "render/procedural.h" +#include "scene/attribute.h" +#include "scene/procedural.h" #include "util/util_set.h" #include "util/util_transform.h" #include "util/util_vector.h" diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/scene/alembic_read.cpp similarity index 99% rename from intern/cycles/render/alembic_read.cpp rename to intern/cycles/scene/alembic_read.cpp index b105af63b44..1ce64d9ee41 100644 --- a/intern/cycles/render/alembic_read.cpp +++ b/intern/cycles/scene/alembic_read.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "render/alembic_read.h" - -#include "render/alembic.h" -#include "render/mesh.h" +#include "scene/alembic.h" +#include "scene/alembic_read.h" +#include "scene/mesh.h" +#include "util/util_color.h" #include "util/util_progress.h" #ifdef WITH_ALEMBIC diff --git a/intern/cycles/render/alembic_read.h b/intern/cycles/scene/alembic_read.h similarity index 100% rename from intern/cycles/render/alembic_read.h rename to intern/cycles/scene/alembic_read.h diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/scene/attribute.cpp similarity index 99% rename from intern/cycles/render/attribute.cpp rename to intern/cycles/scene/attribute.cpp index d7e6939cd80..0c440fb9fd1 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/scene/attribute.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "render/attribute.h" -#include "render/hair.h" -#include "render/image.h" -#include "render/mesh.h" +#include "scene/attribute.h" +#include "scene/hair.h" +#include "scene/image.h" +#include "scene/mesh.h" #include "util/util_foreach.h" #include "util/util_logging.h" diff --git a/intern/cycles/render/attribute.h b/intern/cycles/scene/attribute.h similarity index 99% rename from intern/cycles/render/attribute.h rename to intern/cycles/scene/attribute.h index 004c267cabc..9af3dcaee26 100644 --- a/intern/cycles/render/attribute.h +++ b/intern/cycles/scene/attribute.h @@ -17,7 +17,7 @@ #ifndef __ATTRIBUTE_H__ #define __ATTRIBUTE_H__ -#include "render/image.h" +#include "scene/image.h" #include "kernel/kernel_types.h" diff --git a/intern/cycles/render/background.cpp b/intern/cycles/scene/background.cpp similarity index 95% rename from intern/cycles/render/background.cpp rename to intern/cycles/scene/background.cpp index ae6290ac27b..db5be885538 100644 --- a/intern/cycles/render/background.cpp +++ b/intern/cycles/scene/background.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ -#include "render/background.h" +#include "scene/background.h" #include "device/device.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/nodes.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/stats.h" +#include "scene/integrator.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/stats.h" #include "util/util_foreach.h" #include "util/util_math.h" diff --git a/intern/cycles/render/background.h b/intern/cycles/scene/background.h similarity index 100% rename from intern/cycles/render/background.h rename to intern/cycles/scene/background.h diff --git a/intern/cycles/render/bake.cpp b/intern/cycles/scene/bake.cpp similarity index 92% rename from intern/cycles/render/bake.cpp rename to intern/cycles/scene/bake.cpp index 54e496caed6..86c5c4c02af 100644 --- a/intern/cycles/render/bake.cpp +++ b/intern/cycles/scene/bake.cpp @@ -14,13 +14,13 @@ * limitations under the License. */ -#include "render/bake.h" -#include "render/buffers.h" -#include "render/integrator.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/shader.h" -#include "render/stats.h" +#include "scene/bake.h" +#include "scene/integrator.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/shader.h" +#include "scene/stats.h" +#include "session/buffers.h" #include "util/util_foreach.h" diff --git a/intern/cycles/render/bake.h b/intern/cycles/scene/bake.h similarity index 97% rename from intern/cycles/render/bake.h rename to intern/cycles/scene/bake.h index 39e504490c2..044383d2d43 100644 --- a/intern/cycles/render/bake.h +++ b/intern/cycles/scene/bake.h @@ -18,7 +18,7 @@ #define __BAKE_H__ #include "device/device.h" -#include "render/scene.h" +#include "scene/scene.h" #include "util/util_progress.h" #include "util/util_vector.h" diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/scene/camera.cpp similarity index 98% rename from intern/cycles/render/camera.cpp rename to intern/cycles/scene/camera.cpp index 8b69c971991..1e78f8dd36f 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/scene/camera.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include "render/camera.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/stats.h" -#include "render/tables.h" +#include "scene/camera.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/stats.h" +#include "scene/tables.h" #include "device/device.h" @@ -32,15 +32,10 @@ #include "util/util_vector.h" /* needed for calculating differentials */ -// clang-format off #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" -#include "kernel/kernel_projection.h" -#include "kernel/kernel_differential.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_camera.h" -// clang-format on +#include "kernel/camera/camera.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/camera.h b/intern/cycles/scene/camera.h similarity index 100% rename from intern/cycles/render/camera.h rename to intern/cycles/scene/camera.h diff --git a/intern/cycles/render/colorspace.cpp b/intern/cycles/scene/colorspace.cpp similarity index 99% rename from intern/cycles/render/colorspace.cpp rename to intern/cycles/scene/colorspace.cpp index 3842f8e4726..8584a6f5dd7 100644 --- a/intern/cycles/render/colorspace.cpp +++ b/intern/cycles/scene/colorspace.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "render/colorspace.h" +#include "scene/colorspace.h" #include "util/util_color.h" #include "util/util_half.h" diff --git a/intern/cycles/render/colorspace.h b/intern/cycles/scene/colorspace.h similarity index 100% rename from intern/cycles/render/colorspace.h rename to intern/cycles/scene/colorspace.h diff --git a/intern/cycles/render/constant_fold.cpp b/intern/cycles/scene/constant_fold.cpp similarity index 99% rename from intern/cycles/render/constant_fold.cpp rename to intern/cycles/scene/constant_fold.cpp index a4d40ae8183..b2a17198c93 100644 --- a/intern/cycles/render/constant_fold.cpp +++ b/intern/cycles/scene/constant_fold.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "render/constant_fold.h" -#include "render/graph.h" +#include "scene/constant_fold.h" +#include "scene/shader_graph.h" #include "util/util_foreach.h" #include "util/util_logging.h" diff --git a/intern/cycles/render/constant_fold.h b/intern/cycles/scene/constant_fold.h similarity index 100% rename from intern/cycles/render/constant_fold.h rename to intern/cycles/scene/constant_fold.h diff --git a/intern/cycles/render/curves.cpp b/intern/cycles/scene/curves.cpp similarity index 95% rename from intern/cycles/render/curves.cpp rename to intern/cycles/scene/curves.cpp index db48d8b6430..6e45905d367 100644 --- a/intern/cycles/render/curves.cpp +++ b/intern/cycles/scene/curves.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "render/curves.h" +#include "scene/curves.h" #include "device/device.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" #include "util/util_foreach.h" #include "util/util_map.h" diff --git a/intern/cycles/render/curves.h b/intern/cycles/scene/curves.h similarity index 98% rename from intern/cycles/render/curves.h rename to intern/cycles/scene/curves.h index c52fcb9c882..9b0e2a29977 100644 --- a/intern/cycles/render/curves.h +++ b/intern/cycles/scene/curves.h @@ -20,7 +20,7 @@ #include "util/util_array.h" #include "util/util_types.h" -#include "render/hair.h" +#include "scene/hair.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/film.cpp b/intern/cycles/scene/film.cpp similarity index 98% rename from intern/cycles/render/film.cpp rename to intern/cycles/scene/film.cpp index bd18c777eb5..3f91e0321b2 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/scene/film.cpp @@ -14,17 +14,17 @@ * limitations under the License. */ -#include "render/film.h" +#include "scene/film.h" #include "device/device.h" -#include "render/background.h" -#include "render/bake.h" -#include "render/camera.h" -#include "render/integrator.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/stats.h" -#include "render/tables.h" +#include "scene/background.h" +#include "scene/bake.h" +#include "scene/camera.h" +#include "scene/integrator.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/stats.h" +#include "scene/tables.h" #include "util/util_algorithm.h" #include "util/util_foreach.h" diff --git a/intern/cycles/render/film.h b/intern/cycles/scene/film.h similarity index 99% rename from intern/cycles/render/film.h rename to intern/cycles/scene/film.h index 5d327353361..ede0d6298a6 100644 --- a/intern/cycles/render/film.h +++ b/intern/cycles/scene/film.h @@ -17,7 +17,7 @@ #ifndef __FILM_H__ #define __FILM_H__ -#include "render/pass.h" +#include "scene/pass.h" #include "util/util_string.h" #include "util/util_vector.h" diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/scene/geometry.cpp similarity index 99% rename from intern/cycles/render/geometry.cpp rename to intern/cycles/scene/geometry.cpp index 5cedab24ceb..9fe34ac6e99 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -19,18 +19,18 @@ #include "device/device.h" -#include "render/attribute.h" -#include "render/camera.h" -#include "render/geometry.h" -#include "render/hair.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/stats.h" -#include "render/volume.h" +#include "scene/attribute.h" +#include "scene/camera.h" +#include "scene/geometry.h" +#include "scene/hair.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_nodes.h" +#include "scene/stats.h" +#include "scene/volume.h" #include "subd/subd_patch_table.h" #include "subd/subd_split.h" diff --git a/intern/cycles/render/geometry.h b/intern/cycles/scene/geometry.h similarity index 99% rename from intern/cycles/render/geometry.h rename to intern/cycles/scene/geometry.h index 5c45754ad90..8133132229e 100644 --- a/intern/cycles/render/geometry.h +++ b/intern/cycles/scene/geometry.h @@ -21,7 +21,7 @@ #include "bvh/bvh_params.h" -#include "render/attribute.h" +#include "scene/attribute.h" #include "util/util_boundbox.h" #include "util/util_set.h" diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/scene/hair.cpp similarity index 99% rename from intern/cycles/render/hair.cpp rename to intern/cycles/scene/hair.cpp index 4656148119a..2390da5bf88 100644 --- a/intern/cycles/render/hair.cpp +++ b/intern/cycles/scene/hair.cpp @@ -16,10 +16,10 @@ #include "bvh/bvh.h" -#include "render/curves.h" -#include "render/hair.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/curves.h" +#include "scene/hair.h" +#include "scene/object.h" +#include "scene/scene.h" #include "integrator/shader_eval.h" diff --git a/intern/cycles/render/hair.h b/intern/cycles/scene/hair.h similarity index 99% rename from intern/cycles/render/hair.h rename to intern/cycles/scene/hair.h index 3e91fc3dcbb..832015058fe 100644 --- a/intern/cycles/render/hair.h +++ b/intern/cycles/scene/hair.h @@ -17,7 +17,7 @@ #ifndef __HAIR_H__ #define __HAIR_H__ -#include "render/geometry.h" +#include "scene/geometry.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/image.cpp b/intern/cycles/scene/image.cpp similarity index 99% rename from intern/cycles/render/image.cpp rename to intern/cycles/scene/image.cpp index 27f9b7df1dd..ac85cf5185e 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/scene/image.cpp @@ -14,13 +14,13 @@ * limitations under the License. */ -#include "render/image.h" +#include "scene/image.h" #include "device/device.h" -#include "render/colorspace.h" -#include "render/image_oiio.h" -#include "render/image_vdb.h" -#include "render/scene.h" -#include "render/stats.h" +#include "scene/colorspace.h" +#include "scene/image_oiio.h" +#include "scene/image_vdb.h" +#include "scene/scene.h" +#include "scene/stats.h" #include "util/util_foreach.h" #include "util/util_image.h" diff --git a/intern/cycles/render/image.h b/intern/cycles/scene/image.h similarity index 99% rename from intern/cycles/render/image.h rename to intern/cycles/scene/image.h index dede9513d5f..0c0bbff170a 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/scene/image.h @@ -19,7 +19,7 @@ #include "device/device_memory.h" -#include "render/colorspace.h" +#include "scene/colorspace.h" #include "util/util_string.h" #include "util/util_thread.h" diff --git a/intern/cycles/render/image_oiio.cpp b/intern/cycles/scene/image_oiio.cpp similarity index 99% rename from intern/cycles/render/image_oiio.cpp rename to intern/cycles/scene/image_oiio.cpp index 4867efe6ac0..256a7aeb7d4 100644 --- a/intern/cycles/render/image_oiio.cpp +++ b/intern/cycles/scene/image_oiio.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "render/image_oiio.h" +#include "scene/image_oiio.h" #include "util/util_image.h" #include "util/util_logging.h" diff --git a/intern/cycles/render/image_oiio.h b/intern/cycles/scene/image_oiio.h similarity index 98% rename from intern/cycles/render/image_oiio.h rename to intern/cycles/scene/image_oiio.h index a6dbb168b65..87f8b20f254 100644 --- a/intern/cycles/render/image_oiio.h +++ b/intern/cycles/scene/image_oiio.h @@ -17,7 +17,7 @@ #ifndef __IMAGE_OIIO__ #define __IMAGE_OIIO__ -#include "render/image.h" +#include "scene/image.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/image_sky.cpp b/intern/cycles/scene/image_sky.cpp similarity index 98% rename from intern/cycles/render/image_sky.cpp rename to intern/cycles/scene/image_sky.cpp index 7f9b85836f8..cd8b8d0d991 100644 --- a/intern/cycles/render/image_sky.cpp +++ b/intern/cycles/scene/image_sky.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "render/image_sky.h" +#include "scene/image_sky.h" #include "sky_model.h" diff --git a/intern/cycles/render/image_sky.h b/intern/cycles/scene/image_sky.h similarity index 98% rename from intern/cycles/render/image_sky.h rename to intern/cycles/scene/image_sky.h index 89ff586e7fd..d57e7bbee07 100644 --- a/intern/cycles/render/image_sky.h +++ b/intern/cycles/scene/image_sky.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "render/image.h" +#include "scene/image.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/image_vdb.cpp b/intern/cycles/scene/image_vdb.cpp similarity index 99% rename from intern/cycles/render/image_vdb.cpp rename to intern/cycles/scene/image_vdb.cpp index 4da11c8a140..466df82dd73 100644 --- a/intern/cycles/render/image_vdb.cpp +++ b/intern/cycles/scene/image_vdb.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "render/image_vdb.h" +#include "scene/image_vdb.h" #include "util/util_logging.h" #include "util/util_openvdb.h" diff --git a/intern/cycles/render/image_vdb.h b/intern/cycles/scene/image_vdb.h similarity index 98% rename from intern/cycles/render/image_vdb.h rename to intern/cycles/scene/image_vdb.h index 763196f2a15..0a43d83a24f 100644 --- a/intern/cycles/render/image_vdb.h +++ b/intern/cycles/scene/image_vdb.h @@ -24,7 +24,7 @@ # include #endif -#include "render/image.h" +#include "scene/image.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/scene/integrator.cpp similarity index 97% rename from intern/cycles/render/integrator.cpp rename to intern/cycles/scene/integrator.cpp index 06fa7339b51..5bf82898958 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/scene/integrator.cpp @@ -14,18 +14,18 @@ * limitations under the License. */ -#include "render/integrator.h" +#include "scene/integrator.h" #include "device/device.h" -#include "render/background.h" -#include "render/camera.h" -#include "render/film.h" -#include "render/jitter.h" -#include "render/light.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/sobol.h" -#include "render/stats.h" +#include "scene/background.h" +#include "scene/camera.h" +#include "scene/film.h" +#include "scene/jitter.h" +#include "scene/light.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/sobol.h" +#include "scene/stats.h" #include "kernel/kernel_types.h" diff --git a/intern/cycles/render/integrator.h b/intern/cycles/scene/integrator.h similarity index 100% rename from intern/cycles/render/integrator.h rename to intern/cycles/scene/integrator.h diff --git a/intern/cycles/render/jitter.cpp b/intern/cycles/scene/jitter.cpp similarity index 99% rename from intern/cycles/render/jitter.cpp rename to intern/cycles/scene/jitter.cpp index e31f8abd446..e2162195ad8 100644 --- a/intern/cycles/render/jitter.cpp +++ b/intern/cycles/scene/jitter.cpp @@ -23,7 +23,7 @@ * "Efficient Generation of Points that Satisfy Two-Dimensional Elementary Intervals" */ -#include "render/jitter.h" +#include "scene/jitter.h" #include #include diff --git a/intern/cycles/render/jitter.h b/intern/cycles/scene/jitter.h similarity index 100% rename from intern/cycles/render/jitter.h rename to intern/cycles/scene/jitter.h diff --git a/intern/cycles/render/light.cpp b/intern/cycles/scene/light.cpp similarity index 99% rename from intern/cycles/render/light.cpp rename to intern/cycles/scene/light.cpp index 400ed0802a6..26f208d58e5 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -16,17 +16,17 @@ #include "device/device.h" -#include "render/background.h" -#include "render/film.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/stats.h" +#include "scene/background.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/stats.h" #include "integrator/shader_eval.h" diff --git a/intern/cycles/render/light.h b/intern/cycles/scene/light.h similarity index 99% rename from intern/cycles/render/light.h rename to intern/cycles/scene/light.h index 7f86237c8b3..9820508d3a5 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/scene/light.h @@ -23,7 +23,7 @@ /* included as Light::set_shader defined through NODE_SOCKET_API does not select * the right Node::set overload as it does not know that Shader is a Node */ -#include "render/shader.h" +#include "scene/shader.h" #include "util/util_ies.h" #include "util/util_thread.h" diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/scene/mesh.cpp similarity index 99% rename from intern/cycles/render/mesh.cpp rename to intern/cycles/scene/mesh.cpp index 9c93f6f881c..1ed0eb4c30a 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/scene/mesh.cpp @@ -19,11 +19,11 @@ #include "device/device.h" -#include "render/graph.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/hair.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader_graph.h" #include "subd/subd_patch_table.h" #include "subd/subd_split.h" diff --git a/intern/cycles/render/mesh.h b/intern/cycles/scene/mesh.h similarity index 98% rename from intern/cycles/render/mesh.h rename to intern/cycles/scene/mesh.h index 8258c18ddd1..bc549fa55fa 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/scene/mesh.h @@ -20,9 +20,9 @@ #include "graph/node.h" #include "bvh/bvh_params.h" -#include "render/attribute.h" -#include "render/geometry.h" -#include "render/shader.h" +#include "scene/attribute.h" +#include "scene/geometry.h" +#include "scene/shader.h" #include "util/util_array.h" #include "util/util_boundbox.h" diff --git a/intern/cycles/render/mesh_displace.cpp b/intern/cycles/scene/mesh_displace.cpp similarity index 99% rename from intern/cycles/render/mesh_displace.cpp rename to intern/cycles/scene/mesh_displace.cpp index 3a11f39d977..c673d79f8fe 100644 --- a/intern/cycles/render/mesh_displace.cpp +++ b/intern/cycles/scene/mesh_displace.cpp @@ -18,10 +18,10 @@ #include "integrator/shader_eval.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" #include "util/util_foreach.h" #include "util/util_map.h" diff --git a/intern/cycles/render/mesh_subdivision.cpp b/intern/cycles/scene/mesh_subdivision.cpp similarity index 99% rename from intern/cycles/render/mesh_subdivision.cpp rename to intern/cycles/scene/mesh_subdivision.cpp index 575dbef8ec2..2b27d4b3b2a 100644 --- a/intern/cycles/render/mesh_subdivision.cpp +++ b/intern/cycles/scene/mesh_subdivision.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "render/attribute.h" -#include "render/camera.h" -#include "render/mesh.h" +#include "scene/attribute.h" +#include "scene/camera.h" +#include "scene/mesh.h" #include "subd/subd_patch.h" #include "subd/subd_patch_table.h" diff --git a/intern/cycles/render/object.cpp b/intern/cycles/scene/object.cpp similarity index 99% rename from intern/cycles/render/object.cpp rename to intern/cycles/scene/object.cpp index 330ae5ec0fc..8b0cc752049 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/scene/object.cpp @@ -14,18 +14,18 @@ * limitations under the License. */ -#include "render/object.h" +#include "scene/object.h" #include "device/device.h" -#include "render/camera.h" -#include "render/curves.h" -#include "render/hair.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/particles.h" -#include "render/scene.h" -#include "render/stats.h" -#include "render/volume.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/hair.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/particles.h" +#include "scene/scene.h" +#include "scene/stats.h" +#include "scene/volume.h" #include "util/util_foreach.h" #include "util/util_logging.h" diff --git a/intern/cycles/render/object.h b/intern/cycles/scene/object.h similarity index 98% rename from intern/cycles/render/object.h rename to intern/cycles/scene/object.h index ad312835789..d3909bb2b03 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/scene/object.h @@ -22,8 +22,8 @@ /* included as Object::set_particle_system defined through NODE_SOCKET_API does * not select the right Node::set overload as it does not know that ParticleSystem * is a Node */ -#include "render/particles.h" -#include "render/scene.h" +#include "scene/particles.h" +#include "scene/scene.h" #include "util/util_array.h" #include "util/util_boundbox.h" diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/scene/osl.cpp similarity index 99% rename from intern/cycles/render/osl.cpp rename to intern/cycles/scene/osl.cpp index 1037ebcae7e..c8ab83ab781 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/scene/osl.cpp @@ -16,15 +16,15 @@ #include "device/device.h" -#include "render/background.h" -#include "render/colorspace.h" -#include "render/graph.h" -#include "render/light.h" -#include "render/nodes.h" -#include "render/osl.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/stats.h" +#include "scene/background.h" +#include "scene/colorspace.h" +#include "scene/light.h" +#include "scene/osl.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/stats.h" #ifdef WITH_OSL diff --git a/intern/cycles/render/osl.h b/intern/cycles/scene/osl.h similarity index 98% rename from intern/cycles/render/osl.h rename to intern/cycles/scene/osl.h index dfeec54d915..4161fe6ed67 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/scene/osl.h @@ -22,9 +22,9 @@ #include "util/util_string.h" #include "util/util_thread.h" -#include "render/graph.h" -#include "render/nodes.h" -#include "render/shader.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" #ifdef WITH_OSL # include diff --git a/intern/cycles/render/particles.cpp b/intern/cycles/scene/particles.cpp similarity index 97% rename from intern/cycles/render/particles.cpp rename to intern/cycles/scene/particles.cpp index 02dd1359b18..8041c57ba02 100644 --- a/intern/cycles/render/particles.cpp +++ b/intern/cycles/scene/particles.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "render/particles.h" +#include "scene/particles.h" #include "device/device.h" -#include "render/scene.h" -#include "render/stats.h" +#include "scene/scene.h" +#include "scene/stats.h" #include "util/util_foreach.h" #include "util/util_hash.h" diff --git a/intern/cycles/render/particles.h b/intern/cycles/scene/particles.h similarity index 100% rename from intern/cycles/render/particles.h rename to intern/cycles/scene/particles.h diff --git a/intern/cycles/render/pass.cpp b/intern/cycles/scene/pass.cpp similarity index 99% rename from intern/cycles/render/pass.cpp rename to intern/cycles/scene/pass.cpp index cd044a16353..ee770ac8e58 100644 --- a/intern/cycles/render/pass.cpp +++ b/intern/cycles/scene/pass.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "render/pass.h" +#include "scene/pass.h" #include "util/util_algorithm.h" #include "util/util_logging.h" diff --git a/intern/cycles/render/pass.h b/intern/cycles/scene/pass.h similarity index 100% rename from intern/cycles/render/pass.h rename to intern/cycles/scene/pass.h diff --git a/intern/cycles/render/procedural.cpp b/intern/cycles/scene/procedural.cpp similarity index 97% rename from intern/cycles/render/procedural.cpp rename to intern/cycles/scene/procedural.cpp index 1307a35dcf2..abfc6c62ad4 100644 --- a/intern/cycles/render/procedural.cpp +++ b/intern/cycles/scene/procedural.cpp @@ -16,8 +16,8 @@ #include "procedural.h" -#include "render/scene.h" -#include "render/stats.h" +#include "scene/scene.h" +#include "scene/stats.h" #include "util/util_foreach.h" #include "util/util_progress.h" diff --git a/intern/cycles/render/procedural.h b/intern/cycles/scene/procedural.h similarity index 100% rename from intern/cycles/render/procedural.h rename to intern/cycles/scene/procedural.h diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/scene/scene.cpp similarity index 98% rename from intern/cycles/render/scene.cpp rename to intern/cycles/scene/scene.cpp index 20081677251..bc5737ec126 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/scene/scene.cpp @@ -18,25 +18,25 @@ #include "bvh/bvh.h" #include "device/device.h" -#include "render/alembic.h" -#include "render/background.h" -#include "render/bake.h" -#include "render/camera.h" -#include "render/curves.h" -#include "render/film.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/osl.h" -#include "render/particles.h" -#include "render/procedural.h" -#include "render/scene.h" -#include "render/session.h" -#include "render/shader.h" -#include "render/svm.h" -#include "render/tables.h" -#include "render/volume.h" +#include "scene/alembic.h" +#include "scene/background.h" +#include "scene/bake.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/osl.h" +#include "scene/particles.h" +#include "scene/procedural.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/svm.h" +#include "scene/tables.h" +#include "scene/volume.h" +#include "session/session.h" #include "util/util_foreach.h" #include "util/util_guarded_allocator.h" diff --git a/intern/cycles/render/scene.h b/intern/cycles/scene/scene.h similarity index 99% rename from intern/cycles/render/scene.h rename to intern/cycles/scene/scene.h index de7e3c8a99f..9b2502c9361 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/scene/scene.h @@ -19,9 +19,9 @@ #include "bvh/bvh_params.h" -#include "render/film.h" -#include "render/image.h" -#include "render/shader.h" +#include "scene/film.h" +#include "scene/image.h" +#include "scene/shader.h" #include "device/device.h" #include "device/device_memory.h" diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/scene/shader.cpp similarity index 98% rename from intern/cycles/render/shader.cpp rename to intern/cycles/scene/shader.cpp index 23786a3390f..6b464a77401 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/scene/shader.cpp @@ -16,21 +16,21 @@ #include "device/device.h" -#include "render/background.h" -#include "render/camera.h" -#include "render/colorspace.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/osl.h" -#include "render/procedural.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/svm.h" -#include "render/tables.h" +#include "scene/background.h" +#include "scene/camera.h" +#include "scene/colorspace.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/osl.h" +#include "scene/procedural.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/svm.h" +#include "scene/tables.h" #include "util/util_foreach.h" #include "util/util_murmurhash.h" diff --git a/intern/cycles/render/shader.h b/intern/cycles/scene/shader.h similarity index 99% rename from intern/cycles/render/shader.h rename to intern/cycles/scene/shader.h index 5f9adea3949..7ef3bda15d7 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/scene/shader.h @@ -24,7 +24,7 @@ #endif #include "kernel/kernel_types.h" -#include "render/attribute.h" +#include "scene/attribute.h" #include "graph/node.h" diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/scene/shader_graph.cpp similarity index 99% rename from intern/cycles/render/graph.cpp rename to intern/cycles/scene/shader_graph.cpp index ee1a6e68bcf..116d8335ef8 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/scene/shader_graph.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include "render/graph.h" -#include "render/attribute.h" -#include "render/constant_fold.h" -#include "render/nodes.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/shader_graph.h" +#include "scene/attribute.h" +#include "scene/constant_fold.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_nodes.h" #include "util/util_algorithm.h" #include "util/util_foreach.h" diff --git a/intern/cycles/render/graph.h b/intern/cycles/scene/shader_graph.h similarity index 100% rename from intern/cycles/render/graph.h rename to intern/cycles/scene/shader_graph.h diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/scene/shader_nodes.cpp similarity index 99% rename from intern/cycles/render/nodes.cpp rename to intern/cycles/scene/shader_nodes.cpp index d775859d24d..d7fc7ae1c27 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -14,21 +14,22 @@ * limitations under the License. */ -#include "render/nodes.h" -#include "render/colorspace.h" -#include "render/constant_fold.h" -#include "render/film.h" -#include "render/image.h" -#include "render/image_sky.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/osl.h" -#include "render/scene.h" -#include "render/svm.h" +#include "scene/shader_nodes.h" +#include "scene/colorspace.h" +#include "scene/constant_fold.h" +#include "scene/film.h" +#include "scene/image.h" +#include "scene/image_sky.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/osl.h" +#include "scene/scene.h" +#include "scene/svm.h" #include "sky_model.h" +#include "util/util_color.h" #include "util/util_foreach.h" #include "util/util_logging.h" #include "util/util_transform.h" diff --git a/intern/cycles/render/nodes.h b/intern/cycles/scene/shader_nodes.h similarity index 99% rename from intern/cycles/render/nodes.h rename to intern/cycles/scene/shader_nodes.h index 5ac72835ac5..b8a439fa9b9 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -18,8 +18,8 @@ #define __NODES_H__ #include "graph/node.h" -#include "render/graph.h" -#include "render/image.h" +#include "scene/image.h" +#include "scene/shader_graph.h" #include "util/util_array.h" #include "util/util_string.h" diff --git a/intern/cycles/scene/sobol.cpp b/intern/cycles/scene/sobol.cpp new file mode 100644 index 00000000000..397c28814ca --- /dev/null +++ b/intern/cycles/scene/sobol.cpp @@ -0,0 +1,94 @@ +/* + * Sobol sequence direction vectors. + * + * This file contains code to create direction vectors for generating sobol + * sequences in high dimensions. It is adapted from code on this webpage: + * + * http://web.maths.unsw.edu.au/~fkuo/sobol/ + * + * From these papers: + * + * S. Joe and F. Y. Kuo, Remark on Algorithm 659: Implementing Sobol's quasirandom + * sequence generator, ACM Trans. Math. Softw. 29, 49-57 (2003) + * + * S. Joe and F. Y. Kuo, Constructing Sobol sequences with better two-dimensional + * projections, SIAM J. Sci. Comput. 30, 2635-2654 (2008) + */ + +/* Copyright (c) 2008, Frances Y. Kuo and Stephen Joe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the names of the copyright holders nor the names of the + * University of New South Wales and the University of Waikato + * and its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "util/util_types.h" + +#include "scene/sobol.h" + +CCL_NAMESPACE_BEGIN + +#include "sobol.tables" + +void sobol_generate_direction_vectors(uint vectors[][SOBOL_BITS], int dimensions) +{ + assert(dimensions <= SOBOL_MAX_DIMENSIONS); + + const uint L = SOBOL_BITS; + + /* first dimension is exception */ + uint *v = vectors[0]; + + for (uint i = 0; i < L; i++) + v[i] = 1 << (31 - i); // all m's = 1 + + for (int dim = 1; dim < dimensions; dim++) { + const SobolDirectionNumbers *numbers = &SOBOL_NUMBERS[dim - 1]; + const uint s = numbers->s; + const uint a = numbers->a; + const uint *m = numbers->m; + + v = vectors[dim]; + + if (L <= s) { + for (uint i = 0; i < L; i++) + v[i] = m[i] << (31 - i); + } + else { + for (uint i = 0; i < s; i++) + v[i] = m[i] << (31 - i); + + for (uint i = s; i < L; i++) { + v[i] = v[i - s] ^ (v[i - s] >> s); + + for (uint k = 1; k < s; k++) + v[i] ^= (((a >> (s - 1 - k)) & 1) * v[i - k]); + } + } + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/sobol.h b/intern/cycles/scene/sobol.h similarity index 100% rename from intern/cycles/render/sobol.h rename to intern/cycles/scene/sobol.h diff --git a/intern/cycles/render/sobol.cpp b/intern/cycles/scene/sobol.tables similarity index 99% rename from intern/cycles/render/sobol.cpp rename to intern/cycles/scene/sobol.tables index c821249b239..24f174dfae2 100644 --- a/intern/cycles/render/sobol.cpp +++ b/intern/cycles/scene/sobol.tables @@ -45,11 +45,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "util/util_types.h" - -#include "render/sobol.h" - -CCL_NAMESPACE_BEGIN +/* Note: this file has a non-standard extension so it is skipped by clang-format. */ #define SOBOL_MAX_NUMBER 32 @@ -58,10 +54,6 @@ typedef struct SobolDirectionNumbers { uint m[SOBOL_MAX_NUMBER]; } SobolDirectionNumbers; -/* Note: this file is skipped by clang-format. */ - -/* Keep simple alignment. */ -/* clang-format off */ static const SobolDirectionNumbers SOBOL_NUMBERS[SOBOL_MAX_DIMENSIONS - 1] = { {2, 1, 0, {1}}, {3, 2, 1, {1, 3}}, @@ -21264,44 +21256,3 @@ static const SobolDirectionNumbers SOBOL_NUMBERS[SOBOL_MAX_DIMENSIONS - 1] = { {21200, 18, 131020, {1, 1, 5, 1, 19, 1, 83, 3, 425, 873, 1943, 3935, 4257, 14587, 11829, 55217, 21963, 39683}}, {21201, 18, 131059, {1, 1, 7, 11, 15, 7, 37, 239, 337, 245, 1557, 3681, 7357, 9639, 27367, 26869, 114603, 86317}} }; -/* clang-format on */ - -void sobol_generate_direction_vectors(uint vectors[][SOBOL_BITS], int dimensions) -{ - assert(dimensions <= SOBOL_MAX_DIMENSIONS); - - const uint L = SOBOL_BITS; - - /* first dimension is exception */ - uint *v = vectors[0]; - - for (uint i = 0; i < L; i++) - v[i] = 1 << (31 - i); // all m's = 1 - - for (int dim = 1; dim < dimensions; dim++) { - const SobolDirectionNumbers *numbers = &SOBOL_NUMBERS[dim - 1]; - const uint s = numbers->s; - const uint a = numbers->a; - const uint *m = numbers->m; - - v = vectors[dim]; - - if (L <= s) { - for (uint i = 0; i < L; i++) - v[i] = m[i] << (31 - i); - } - else { - for (uint i = 0; i < s; i++) - v[i] = m[i] << (31 - i); - - for (uint i = s; i < L; i++) { - v[i] = v[i - s] ^ (v[i - s] >> s); - - for (uint k = 1; k < s; k++) - v[i] ^= (((a >> (s - 1 - k)) & 1) * v[i - k]); - } - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/render/stats.cpp b/intern/cycles/scene/stats.cpp similarity index 99% rename from intern/cycles/render/stats.cpp rename to intern/cycles/scene/stats.cpp index 73eb7e21ff9..5c3cff232f4 100644 --- a/intern/cycles/render/stats.cpp +++ b/intern/cycles/scene/stats.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "render/stats.h" -#include "render/object.h" +#include "scene/stats.h" +#include "scene/object.h" #include "util/util_algorithm.h" #include "util/util_foreach.h" #include "util/util_string.h" diff --git a/intern/cycles/render/stats.h b/intern/cycles/scene/stats.h similarity index 99% rename from intern/cycles/render/stats.h rename to intern/cycles/scene/stats.h index 93d029bba61..ffcc4d55235 100644 --- a/intern/cycles/render/stats.h +++ b/intern/cycles/scene/stats.h @@ -17,7 +17,7 @@ #ifndef __RENDER_STATS_H__ #define __RENDER_STATS_H__ -#include "render/scene.h" +#include "scene/scene.h" #include "util/util_stats.h" #include "util/util_string.h" diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/scene/svm.cpp similarity index 99% rename from intern/cycles/render/svm.cpp rename to intern/cycles/scene/svm.cpp index 2379eb775a0..b0b7fb605d1 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/scene/svm.cpp @@ -16,15 +16,15 @@ #include "device/device.h" -#include "render/background.h" -#include "render/graph.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/stats.h" -#include "render/svm.h" +#include "scene/background.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/stats.h" +#include "scene/svm.h" #include "util/util_foreach.h" #include "util/util_logging.h" diff --git a/intern/cycles/render/svm.h b/intern/cycles/scene/svm.h similarity index 98% rename from intern/cycles/render/svm.h rename to intern/cycles/scene/svm.h index 0353c393ae4..a3d215218fa 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/scene/svm.h @@ -17,9 +17,9 @@ #ifndef __SVM_H__ #define __SVM_H__ -#include "render/attribute.h" -#include "render/graph.h" -#include "render/shader.h" +#include "scene/attribute.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" #include "util/util_array.h" #include "util/util_set.h" diff --git a/intern/cycles/render/tables.cpp b/intern/cycles/scene/tables.cpp similarity index 97% rename from intern/cycles/render/tables.cpp rename to intern/cycles/scene/tables.cpp index a0813400b1c..39edc5d89cd 100644 --- a/intern/cycles/render/tables.cpp +++ b/intern/cycles/scene/tables.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "render/tables.h" +#include "scene/tables.h" #include "device/device.h" -#include "render/scene.h" -#include "render/stats.h" +#include "scene/scene.h" +#include "scene/stats.h" #include "util/util_logging.h" #include "util/util_time.h" diff --git a/intern/cycles/render/tables.h b/intern/cycles/scene/tables.h similarity index 100% rename from intern/cycles/render/tables.h rename to intern/cycles/scene/tables.h diff --git a/intern/cycles/render/volume.cpp b/intern/cycles/scene/volume.cpp similarity index 99% rename from intern/cycles/render/volume.cpp rename to intern/cycles/scene/volume.cpp index 358ef71d501..757388a4491 100644 --- a/intern/cycles/render/volume.cpp +++ b/intern/cycles/scene/volume.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "render/volume.h" -#include "render/attribute.h" -#include "render/image_vdb.h" -#include "render/scene.h" +#include "scene/volume.h" +#include "scene/attribute.h" +#include "scene/image_vdb.h" +#include "scene/scene.h" #ifdef WITH_OPENVDB # include diff --git a/intern/cycles/render/volume.h b/intern/cycles/scene/volume.h similarity index 97% rename from intern/cycles/render/volume.h rename to intern/cycles/scene/volume.h index 2e9703bf7ed..eae48f78b8c 100644 --- a/intern/cycles/render/volume.h +++ b/intern/cycles/scene/volume.h @@ -18,7 +18,7 @@ #include "graph/node.h" -#include "render/mesh.h" +#include "scene/mesh.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/CMakeLists.txt b/intern/cycles/session/CMakeLists.txt new file mode 100644 index 00000000000..f441def128e --- /dev/null +++ b/intern/cycles/session/CMakeLists.txt @@ -0,0 +1,48 @@ +# Copyright 2011-2021 Blender Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(INC + .. +) + +set(SRC + buffers.cpp + denoising.cpp + merge.cpp + session.cpp + tile.cpp +) + +set(SRC_HEADERS + buffers.h + display_driver.h + denoising.h + merge.h + output_driver.h + session.h + tile.h +) + +set(LIB + cycles_device + cycles_integrator + cycles_util +) + +include_directories(${INC}) +include_directories(SYSTEM ${INC_SYS}) + +add_definitions(${GL_DEFINITIONS}) + +cycles_add_library(cycles_session "${LIB}" ${SRC} ${SRC_HEADERS}) diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/session/buffers.cpp similarity index 99% rename from intern/cycles/render/buffers.cpp rename to intern/cycles/session/buffers.cpp index 00b4284c22b..439c0f826ea 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/session/buffers.cpp @@ -17,7 +17,7 @@ #include #include "device/device.h" -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_foreach.h" #include "util/util_hash.h" diff --git a/intern/cycles/render/buffers.h b/intern/cycles/session/buffers.h similarity index 99% rename from intern/cycles/render/buffers.h rename to intern/cycles/session/buffers.h index 3cf826f14d6..4c261430bb6 100644 --- a/intern/cycles/render/buffers.h +++ b/intern/cycles/session/buffers.h @@ -19,7 +19,7 @@ #include "device/device_memory.h" #include "graph/node.h" -#include "render/pass.h" +#include "scene/pass.h" #include "kernel/kernel_types.h" diff --git a/intern/cycles/render/denoising.cpp b/intern/cycles/session/denoising.cpp similarity index 99% rename from intern/cycles/render/denoising.cpp rename to intern/cycles/session/denoising.cpp index bcf8d3fa204..21df068092a 100644 --- a/intern/cycles/render/denoising.cpp +++ b/intern/cycles/session/denoising.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "render/denoising.h" +#include "session/denoising.h" #if 0 diff --git a/intern/cycles/render/denoising.h b/intern/cycles/session/denoising.h similarity index 100% rename from intern/cycles/render/denoising.h rename to intern/cycles/session/denoising.h diff --git a/intern/cycles/render/display_driver.h b/intern/cycles/session/display_driver.h similarity index 100% rename from intern/cycles/render/display_driver.h rename to intern/cycles/session/display_driver.h diff --git a/intern/cycles/render/merge.cpp b/intern/cycles/session/merge.cpp similarity index 99% rename from intern/cycles/render/merge.cpp rename to intern/cycles/session/merge.cpp index 8a58c827e82..97e9c75d5f7 100644 --- a/intern/cycles/render/merge.cpp +++ b/intern/cycles/session/merge.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "render/merge.h" +#include "session/merge.h" #include "util/util_array.h" #include "util/util_map.h" diff --git a/intern/cycles/render/merge.h b/intern/cycles/session/merge.h similarity index 100% rename from intern/cycles/render/merge.h rename to intern/cycles/session/merge.h diff --git a/intern/cycles/render/output_driver.h b/intern/cycles/session/output_driver.h similarity index 100% rename from intern/cycles/render/output_driver.h rename to intern/cycles/session/output_driver.h diff --git a/intern/cycles/render/session.cpp b/intern/cycles/session/session.cpp similarity index 97% rename from intern/cycles/render/session.cpp rename to intern/cycles/session/session.cpp index 7fba7ce7552..f8fc892f127 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/session/session.cpp @@ -21,19 +21,19 @@ #include "device/device.h" #include "integrator/pass_accessor_cpu.h" #include "integrator/path_trace.h" -#include "render/background.h" -#include "render/bake.h" -#include "render/buffers.h" -#include "render/camera.h" -#include "render/display_driver.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/output_driver.h" -#include "render/scene.h" -#include "render/session.h" +#include "scene/background.h" +#include "scene/bake.h" +#include "scene/camera.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader_graph.h" +#include "session/buffers.h" +#include "session/display_driver.h" +#include "session/output_driver.h" +#include "session/session.h" #include "util/util_foreach.h" #include "util/util_function.h" diff --git a/intern/cycles/render/session.h b/intern/cycles/session/session.h similarity index 98% rename from intern/cycles/render/session.h rename to intern/cycles/session/session.h index 46c964bc98c..5aa6df79ef1 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/session/session.h @@ -19,10 +19,10 @@ #include "device/device.h" #include "integrator/render_scheduler.h" -#include "render/buffers.h" -#include "render/shader.h" -#include "render/stats.h" -#include "render/tile.h" +#include "scene/shader.h" +#include "scene/stats.h" +#include "session/buffers.h" +#include "session/tile.h" #include "util/util_progress.h" #include "util/util_stats.h" diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/session/tile.cpp similarity index 99% rename from intern/cycles/render/tile.cpp rename to intern/cycles/session/tile.cpp index 3070e93eff3..59332530596 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/session/tile.cpp @@ -14,15 +14,15 @@ * limitations under the License. */ -#include "render/tile.h" +#include "session/tile.h" #include #include "graph/node.h" -#include "render/background.h" -#include "render/film.h" -#include "render/integrator.h" -#include "render/scene.h" +#include "scene/background.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/scene.h" #include "util/util_algorithm.h" #include "util/util_foreach.h" #include "util/util_logging.h" diff --git a/intern/cycles/render/tile.h b/intern/cycles/session/tile.h similarity index 99% rename from intern/cycles/render/tile.h rename to intern/cycles/session/tile.h index 5872a9a8609..37a02081a53 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/session/tile.h @@ -16,7 +16,7 @@ #pragma once -#include "render/buffers.h" +#include "session/buffers.h" #include "util/util_image.h" #include "util/util_string.h" #include "util/util_unique_ptr.h" diff --git a/intern/cycles/subd/subd_dice.cpp b/intern/cycles/subd/subd_dice.cpp index 4efdb98aa0f..a4019a5d639 100644 --- a/intern/cycles/subd/subd_dice.cpp +++ b/intern/cycles/subd/subd_dice.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "render/camera.h" -#include "render/mesh.h" +#include "scene/camera.h" +#include "scene/mesh.h" #include "subd/subd_dice.h" #include "subd/subd_patch.h" diff --git a/intern/cycles/subd/subd_patch.cpp b/intern/cycles/subd/subd_patch.cpp index a975b7b6342..23b3e6d5136 100644 --- a/intern/cycles/subd/subd_patch.cpp +++ b/intern/cycles/subd/subd_patch.cpp @@ -16,7 +16,7 @@ /* Parts adapted from code in the public domain in NVidia Mesh Tools. */ -#include "render/mesh.h" +#include "scene/mesh.h" #include "subd/subd_patch.h" diff --git a/intern/cycles/subd/subd_split.cpp b/intern/cycles/subd/subd_split.cpp index 4d648eb1f77..6b352ab02c3 100644 --- a/intern/cycles/subd/subd_split.cpp +++ b/intern/cycles/subd/subd_split.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "render/camera.h" -#include "render/mesh.h" +#include "scene/camera.h" +#include "scene/mesh.h" #include "subd/subd_dice.h" #include "subd/subd_patch.h" diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt index 0f6b435813f..efd9cecb877 100644 --- a/intern/cycles/test/CMakeLists.txt +++ b/intern/cycles/test/CMakeLists.txt @@ -25,18 +25,19 @@ set(INC ../device ../graph ../kernel - ../render + ../scene ../util ) set(ALL_CYCLES_LIBRARIES - cycles_device cycles_kernel cycles_integrator - cycles_render + cycles_scene + cycles_session cycles_bvh cycles_graph cycles_subd + cycles_device cycles_util extern_clew ${CYCLES_GL_LIBRARIES} diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index 19c211fe5f7..4a87a382130 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -19,9 +19,9 @@ #include "device/device.h" -#include "render/graph.h" -#include "render/nodes.h" -#include "render/scene.h" +#include "scene/scene.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" #include "util/util_array.h" #include "util/util_logging.h" From fd25e883e2807a151f673b87c152a59701a0df80 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 24 Oct 2021 14:19:19 +0200 Subject: [PATCH 1199/1500] Cycles: remove prefix from source code file names Remove prefix of filenames that is the same as the folder name. This used to help when #includes were using individual files, but now they are always relative to the cycles root directory and so the prefixes are redundant. For patches and branches, git merge and rebase should be able to detect the renames and move over code to the right file. --- intern/cycles/app/cycles_server.cpp | 14 +- intern/cycles/app/cycles_standalone.cpp | 26 +- intern/cycles/app/cycles_xml.cpp | 14 +- intern/cycles/app/oiio_output_driver.h | 10 +- intern/cycles/blender/CMakeLists.txt | 62 ++-- .../{blender_camera.cpp => camera.cpp} | 6 +- .../{blender_curves.cpp => curves.cpp} | 14 +- .../{blender_device.cpp => device.cpp} | 8 +- .../blender/{blender_device.h => device.h} | 0 ..._display_driver.cpp => display_driver.cpp} | 6 +- ...nder_display_driver.h => display_driver.h} | 4 +- .../{blender_geometry.cpp => geometry.cpp} | 8 +- .../blender/{blender_id_map.h => id_map.h} | 6 +- .../blender/{blender_image.cpp => image.cpp} | 6 +- .../blender/{blender_image.h => image.h} | 0 .../blender/{blender_light.cpp => light.cpp} | 6 +- .../{blender_logging.cpp => logging.cpp} | 2 +- .../blender/{blender_mesh.cpp => mesh.cpp} | 26 +- .../{blender_object.cpp => object.cpp} | 16 +- ...lender_object_cull.cpp => object_cull.cpp} | 4 +- .../{blender_object_cull.h => object_cull.h} | 4 +- ...er_output_driver.cpp => output_driver.cpp} | 2 +- ...lender_output_driver.h => output_driver.h} | 0 .../{blender_particles.cpp => particles.cpp} | 8 +- .../{blender_python.cpp => python.cpp} | 30 +- .../{blender_session.cpp => session.cpp} | 30 +- .../blender/{blender_session.h => session.h} | 4 +- .../{blender_shader.cpp => shader.cpp} | 20 +- .../blender/{blender_sync.cpp => sync.cpp} | 20 +- .../cycles/blender/{blender_sync.h => sync.h} | 14 +- .../{blender_texture.cpp => texture.cpp} | 2 +- .../blender/{blender_texture.h => texture.h} | 2 +- .../cycles/blender/{blender_util.h => util.h} | 16 +- .../{blender_viewport.cpp => viewport.cpp} | 7 +- .../{blender_viewport.h => viewport.h} | 0 .../{blender_volume.cpp => volume.cpp} | 6 +- intern/cycles/bvh/CMakeLists.txt | 38 +- .../bvh/{bvh_binning.cpp => binning.cpp} | 8 +- .../cycles/bvh/{bvh_binning.h => binning.h} | 6 +- .../cycles/bvh/{bvh_build.cpp => build.cpp} | 26 +- intern/cycles/bvh/{bvh_build.h => build.h} | 10 +- intern/cycles/bvh/bvh.cpp | 10 +- intern/cycles/bvh/bvh.h | 8 +- intern/cycles/bvh/bvh2.cpp | 10 +- intern/cycles/bvh/bvh2.h | 6 +- .../cycles/bvh/{bvh_embree.cpp => embree.cpp} | 16 +- intern/cycles/bvh/{bvh_embree.h => embree.h} | 8 +- .../cycles/bvh/{bvh_multi.cpp => multi.cpp} | 4 +- intern/cycles/bvh/{bvh_multi.h => multi.h} | 2 +- intern/cycles/bvh/{bvh_node.cpp => node.cpp} | 6 +- intern/cycles/bvh/{bvh_node.h => node.h} | 4 +- .../cycles/bvh/{bvh_optix.cpp => optix.cpp} | 2 +- intern/cycles/bvh/{bvh_optix.h => optix.h} | 5 +- intern/cycles/bvh/{bvh_params.h => params.h} | 4 +- intern/cycles/bvh/{bvh_sort.cpp => sort.cpp} | 8 +- intern/cycles/bvh/{bvh_sort.h => sort.h} | 0 .../cycles/bvh/{bvh_split.cpp => split.cpp} | 8 +- intern/cycles/bvh/{bvh_split.h => split.h} | 4 +- .../bvh/{bvh_unaligned.cpp => unaligned.cpp} | 10 +- .../bvh/{bvh_unaligned.h => unaligned.h} | 2 +- intern/cycles/device/CMakeLists.txt | 20 +- intern/cycles/device/cpu/device.cpp | 2 +- intern/cycles/device/cpu/device.h | 4 +- intern/cycles/device/cpu/device_impl.cpp | 32 +- intern/cycles/device/cpu/device_impl.h | 8 +- intern/cycles/device/cpu/kernel.h | 2 +- intern/cycles/device/cpu/kernel_function.h | 4 +- .../device/cpu/kernel_thread_globals.cpp | 6 +- intern/cycles/device/cuda/device.cpp | 6 +- intern/cycles/device/cuda/device.h | 4 +- intern/cycles/device/cuda/device_impl.cpp | 22 +- intern/cycles/device/cuda/device_impl.h | 2 +- intern/cycles/device/cuda/graphics_interop.h | 2 +- intern/cycles/device/cuda/kernel.h | 2 +- intern/cycles/device/cuda/queue.h | 6 +- .../{device_denoise.cpp => denoise.cpp} | 2 +- .../device/{device_denoise.h => denoise.h} | 2 +- intern/cycles/device/device.cpp | 20 +- intern/cycles/device/device.h | 26 +- intern/cycles/device/dummy/device.cpp | 2 +- intern/cycles/device/dummy/device.h | 4 +- ...phics_interop.cpp => graphics_interop.cpp} | 2 +- ..._graphics_interop.h => graphics_interop.h} | 2 +- intern/cycles/device/hip/device.cpp | 6 +- intern/cycles/device/hip/device.h | 4 +- intern/cycles/device/hip/device_impl.cpp | 24 +- intern/cycles/device/hip/device_impl.h | 4 +- intern/cycles/device/hip/graphics_interop.h | 2 +- intern/cycles/device/hip/kernel.h | 2 +- intern/cycles/device/hip/queue.h | 6 +- .../device/{device_kernel.cpp => kernel.cpp} | 4 +- .../device/{device_kernel.h => kernel.h} | 4 +- .../device/{device_memory.cpp => memory.cpp} | 2 +- .../device/{device_memory.h => memory.h} | 12 +- intern/cycles/device/multi/device.cpp | 14 +- intern/cycles/device/multi/device.h | 4 +- intern/cycles/device/optix/device.cpp | 3 +- intern/cycles/device/optix/device.h | 4 +- intern/cycles/device/optix/device_impl.cpp | 16 +- intern/cycles/device/optix/device_impl.h | 2 +- intern/cycles/device/optix/queue.cpp | 2 +- .../device/{device_queue.cpp => queue.cpp} | 8 +- .../cycles/device/{device_queue.h => queue.h} | 12 +- intern/cycles/graph/node.cpp | 8 +- intern/cycles/graph/node.h | 6 +- intern/cycles/graph/node_enum.h | 4 +- intern/cycles/graph/node_type.cpp | 4 +- intern/cycles/graph/node_type.h | 10 +- intern/cycles/graph/node_xml.cpp | 6 +- intern/cycles/graph/node_xml.h | 6 +- .../cycles/integrator/adaptive_sampling.cpp | 2 +- intern/cycles/integrator/denoiser.cpp | 4 +- intern/cycles/integrator/denoiser.h | 6 +- intern/cycles/integrator/denoiser_device.cpp | 10 +- intern/cycles/integrator/denoiser_device.h | 2 +- intern/cycles/integrator/denoiser_oidn.cpp | 8 +- intern/cycles/integrator/denoiser_oidn.h | 4 +- intern/cycles/integrator/denoiser_optix.cpp | 2 +- intern/cycles/integrator/pass_accessor.cpp | 4 +- intern/cycles/integrator/pass_accessor.h | 6 +- .../cycles/integrator/pass_accessor_cpu.cpp | 8 +- .../cycles/integrator/pass_accessor_gpu.cpp | 4 +- intern/cycles/integrator/pass_accessor_gpu.h | 2 +- intern/cycles/integrator/path_trace.cpp | 10 +- intern/cycles/integrator/path_trace.h | 8 +- .../cycles/integrator/path_trace_display.cpp | 2 +- intern/cycles/integrator/path_trace_display.h | 8 +- intern/cycles/integrator/path_trace_work.cpp | 2 +- intern/cycles/integrator/path_trace_work.h | 4 +- .../cycles/integrator/path_trace_work_cpu.cpp | 8 +- .../cycles/integrator/path_trace_work_cpu.h | 6 +- .../cycles/integrator/path_trace_work_gpu.cpp | 18 +- .../cycles/integrator/path_trace_work_gpu.h | 10 +- intern/cycles/integrator/render_scheduler.cpp | 6 +- intern/cycles/integrator/render_scheduler.h | 2 +- intern/cycles/integrator/shader_eval.cpp | 8 +- intern/cycles/integrator/shader_eval.h | 6 +- intern/cycles/integrator/tile.cpp | 4 +- intern/cycles/integrator/tile.h | 2 +- intern/cycles/integrator/work_balancer.cpp | 4 +- intern/cycles/integrator/work_balancer.h | 2 +- .../cycles/integrator/work_tile_scheduler.cpp | 6 +- .../cycles/integrator/work_tile_scheduler.h | 2 +- intern/cycles/kernel/CMakeLists.txt | 344 +++++++++--------- intern/cycles/kernel/bake/bake.h | 4 +- intern/cycles/kernel/bvh/bvh.h | 38 +- .../kernel/bvh/{bvh_embree.h => embree.h} | 2 +- .../kernel/bvh/{bvh_local.h => local.h} | 0 .../kernel/bvh/{bvh_nodes.h => nodes.h} | 0 .../bvh/{bvh_shadow_all.h => shadow_all.h} | 0 .../bvh/{bvh_traversal.h => traversal.h} | 0 .../kernel/bvh/{bvh_types.h => types.h} | 0 .../cycles/kernel/bvh/{bvh_util.h => util.h} | 0 .../kernel/bvh/{bvh_volume.h => volume.h} | 0 .../bvh/{bvh_volume_all.h => volume_all.h} | 0 intern/cycles/kernel/camera/camera.h | 8 +- .../{camera_projection.h => projection.h} | 0 .../kernel/closure/bsdf_ashikhmin_velvet.h | 2 +- intern/cycles/kernel/closure/bsdf_diffuse.h | 2 +- .../cycles/kernel/closure/bsdf_diffuse_ramp.h | 2 +- .../kernel/closure/bsdf_hair_principled.h | 2 +- .../cycles/kernel/closure/bsdf_microfacet.h | 7 +- .../kernel/closure/bsdf_microfacet_multi.h | 4 +- .../kernel/closure/bsdf_principled_diffuse.h | 2 +- intern/cycles/kernel/device/cpu/compat.h | 10 +- intern/cycles/kernel/device/cpu/globals.h | 6 +- intern/cycles/kernel/device/cpu/kernel.cpp | 2 +- intern/cycles/kernel/device/cpu/kernel.h | 4 +- .../kernel/device/cpu/kernel_arch_impl.h | 36 +- .../cycles/kernel/device/cpu/kernel_avx.cpp | 2 +- .../cycles/kernel/device/cpu/kernel_avx2.cpp | 2 +- .../cycles/kernel/device/cpu/kernel_sse2.cpp | 2 +- .../cycles/kernel/device/cpu/kernel_sse3.cpp | 2 +- .../cycles/kernel/device/cpu/kernel_sse41.cpp | 2 +- intern/cycles/kernel/device/cuda/compat.h | 4 +- intern/cycles/kernel/device/cuda/globals.h | 8 +- intern/cycles/kernel/device/gpu/kernel.h | 32 +- .../kernel/device/gpu/parallel_active_index.h | 2 +- .../kernel/device/gpu/parallel_prefix_sum.h | 2 +- .../kernel/device/gpu/parallel_sorted_index.h | 2 +- intern/cycles/kernel/device/hip/compat.h | 4 +- intern/cycles/kernel/device/hip/globals.h | 8 +- intern/cycles/kernel/device/optix/compat.h | 4 +- intern/cycles/kernel/device/optix/globals.h | 8 +- intern/cycles/kernel/device/optix/kernel.cu | 14 +- .../device/optix/kernel_shader_raytrace.cu | 3 +- .../film/{film_accumulate.h => accumulate.h} | 6 +- ...daptive_sampling.h => adaptive_sampling.h} | 2 +- .../film/{film_id_passes.h => id_passes.h} | 0 .../kernel/film/{film_passes.h => passes.h} | 4 +- .../kernel/film/{film_read.h => read.h} | 0 .../{film_write_passes.h => write_passes.h} | 0 .../geom/{geom_attribute.h => attribute.h} | 0 .../kernel/geom/{geom_curve.h => curve.h} | 0 ...om_curve_intersect.h => curve_intersect.h} | 0 intern/cycles/kernel/geom/geom.h | 30 +- .../{geom_motion_curve.h => motion_curve.h} | 0 ...om_motion_triangle.h => motion_triangle.h} | 2 +- ...ntersect.h => motion_triangle_intersect.h} | 0 ...ngle_shader.h => motion_triangle_shader.h} | 0 .../kernel/geom/{geom_object.h => object.h} | 0 .../kernel/geom/{geom_patch.h => patch.h} | 2 +- .../geom/{geom_primitive.h => primitive.h} | 2 +- .../{geom_shader_data.h => shader_data.h} | 0 .../{geom_subd_triangle.h => subd_triangle.h} | 0 .../geom/{geom_triangle.h => triangle.h} | 0 ...angle_intersect.h => triangle_intersect.h} | 2 +- .../kernel/geom/{geom_volume.h => volume.h} | 0 ...ator_init_from_bake.h => init_from_bake.h} | 8 +- ..._init_from_camera.h => init_from_camera.h} | 10 +- ...ntersect_closest.h => intersect_closest.h} | 8 +- ..._intersect_shadow.h => intersect_shadow.h} | 0 ...ct_subsurface.h => intersect_subsurface.h} | 2 +- ...olume_stack.h => intersect_volume_stack.h} | 4 +- .../{integrator_megakernel.h => megakernel.h} | 20 +- .../{integrator_path_state.h => path_state.h} | 2 +- ..._shade_background.h => shade_background.h} | 8 +- ...integrator_shade_light.h => shade_light.h} | 6 +- ...tegrator_shade_shadow.h => shade_shadow.h} | 6 +- ...grator_shade_surface.h => shade_surface.h} | 16 +- ...tegrator_shade_volume.h => shade_volume.h} | 16 +- ...integrator_shader_eval.h => shader_eval.h} | 6 +- ...ator_shadow_catcher.h => shadow_catcher.h} | 4 +- ...ate_template.h => shadow_state_template.h} | 0 .../{integrator_state.h => state.h} | 12 +- .../{integrator_state_flow.h => state_flow.h} | 4 +- ...ator_state_template.h => state_template.h} | 0 .../{integrator_state_util.h => state_util.h} | 8 +- .../{integrator_subsurface.h => subsurface.h} | 12 +- ...or_subsurface_disk.h => subsurface_disk.h} | 0 ...random_walk.h => subsurface_random_walk.h} | 2 +- ...tegrator_volume_stack.h => volume_stack.h} | 0 .../{light_background.h => background.h} | 2 +- .../kernel/light/{light_common.h => common.h} | 2 +- intern/cycles/kernel/light/light.h | 4 +- .../kernel/light/{light_sample.h => sample.h} | 6 +- intern/cycles/kernel/osl/CMakeLists.txt | 16 +- intern/cycles/kernel/osl/background.cpp | 2 +- .../cycles/kernel/osl/bsdf_diffuse_ramp.cpp | 4 +- intern/cycles/kernel/osl/bsdf_phong_ramp.cpp | 4 +- .../kernel/osl/{osl_bssrdf.cpp => bssrdf.cpp} | 4 +- .../osl/{osl_closures.cpp => closures.cpp} | 10 +- .../kernel/osl/{osl_closures.h => closures.h} | 4 +- intern/cycles/kernel/osl/emissive.cpp | 4 +- .../kernel/osl/{osl_globals.h => globals.h} | 10 +- .../osl/{osl_services.cpp => services.cpp} | 28 +- .../kernel/osl/{osl_services.h => services.h} | 0 .../kernel/osl/{osl_shader.cpp => shader.cpp} | 14 +- .../kernel/osl/{osl_shader.h => shader.h} | 2 +- .../sample/{sample_jitter.h => jitter.h} | 0 intern/cycles/kernel/sample/lcg.h | 51 +++ .../sample/{sample_mapping.h => mapping.h} | 0 intern/cycles/kernel/sample/mis.h | 64 ++++ .../sample/{sample_pattern.h => pattern.h} | 4 +- intern/cycles/kernel/svm/{svm_ao.h => ao.h} | 2 + intern/cycles/kernel/svm/{svm_aov.h => aov.h} | 4 +- .../svm/{svm_attribute.h => attribute.h} | 2 + .../kernel/svm/{svm_bevel.h => bevel.h} | 6 +- .../svm/{svm_blackbody.h => blackbody.h} | 4 + .../kernel/svm/{svm_brick.h => brick.h} | 2 + .../svm/{svm_brightness.h => brightness.h} | 4 + .../cycles/kernel/svm/{svm_bump.h => bump.h} | 2 + .../kernel/svm/{svm_camera.h => camera.h} | 2 + .../kernel/svm/{svm_checker.h => checker.h} | 2 + .../kernel/svm/{svm_clamp.h => clamp.h} | 2 + .../kernel/svm/{svm_closure.h => closure.h} | 2 + .../svm/{svm_color_util.h => color_util.h} | 2 + .../kernel/svm/{svm_convert.h => convert.h} | 2 + .../kernel/svm/{svm_displace.h => displace.h} | 4 +- .../{svm_fractal_noise.h => fractal_noise.h} | 4 + .../kernel/svm/{svm_fresnel.h => fresnel.h} | 2 + .../kernel/svm/{svm_gamma.h => gamma.h} | 2 + .../kernel/svm/{svm_geometry.h => geometry.h} | 2 + .../kernel/svm/{svm_gradient.h => gradient.h} | 2 + intern/cycles/kernel/svm/{svm_hsv.h => hsv.h} | 5 +- intern/cycles/kernel/svm/{svm_ies.h => ies.h} | 2 + .../kernel/svm/{svm_image.h => image.h} | 2 + .../kernel/svm/{svm_invert.h => invert.h} | 2 + .../svm/{svm_light_path.h => light_path.h} | 2 + .../kernel/svm/{svm_magic.h => magic.h} | 2 + .../svm/{svm_map_range.h => map_range.h} | 2 + .../kernel/svm/{svm_mapping.h => mapping.h} | 4 + .../{svm_mapping_util.h => mapping_util.h} | 2 + .../cycles/kernel/svm/{svm_math.h => math.h} | 2 + .../svm/{svm_math_util.h => math_util.h} | 2 + intern/cycles/kernel/svm/{svm_mix.h => mix.h} | 2 + .../kernel/svm/{svm_musgrave.h => musgrave.h} | 4 + .../kernel/svm/{svm_noise.h => noise.h} | 2 + .../kernel/svm/{svm_noisetex.h => noisetex.h} | 4 + .../kernel/svm/{svm_normal.h => normal.h} | 2 + .../cycles/kernel/svm/{svm_ramp.h => ramp.h} | 5 +- .../svm/{svm_ramp_util.h => ramp_util.h} | 5 +- .../svm/{svm_sepcomb_hsv.h => sepcomb_hsv.h} | 2 + ...{svm_sepcomb_vector.h => sepcomb_vector.h} | 2 + intern/cycles/kernel/svm/{svm_sky.h => sky.h} | 2 + intern/cycles/kernel/svm/svm.h | 106 +++--- .../svm/{svm_tex_coord.h => tex_coord.h} | 4 +- .../kernel/svm/{svm_types.h => types.h} | 5 +- .../kernel/svm/{svm_value.h => value.h} | 2 + .../{svm_vector_rotate.h => vector_rotate.h} | 2 + ..._vector_transform.h => vector_transform.h} | 2 + .../{svm_vertex_color.h => vertex_color.h} | 2 + .../kernel/svm/{svm_voronoi.h => voronoi.h} | 2 + .../kernel/svm/{svm_voxel.h => voxel.h} | 2 + .../cycles/kernel/svm/{svm_wave.h => wave.h} | 2 + .../svm/{svm_wavelength.h => wavelength.h} | 2 + .../svm/{svm_white_noise.h => white_noise.h} | 2 + .../svm/{svm_wireframe.h => wireframe.h} | 2 + .../kernel/{kernel_textures.h => textures.h} | 0 .../cycles/kernel/{kernel_types.h => types.h} | 16 +- .../kernel/util/{util_color.h => color.h} | 2 +- .../{util_differential.h => differential.h} | 0 .../{util_lookup_table.h => lookup_table.h} | 0 .../util/{util_profiling.h => profiling.h} | 2 +- intern/cycles/scene/alembic.cpp | 10 +- intern/cycles/scene/alembic.h | 6 +- intern/cycles/scene/alembic_read.cpp | 6 +- intern/cycles/scene/alembic_read.h | 2 +- intern/cycles/scene/attribute.cpp | 6 +- intern/cycles/scene/attribute.h | 12 +- intern/cycles/scene/background.cpp | 8 +- intern/cycles/scene/background.h | 2 +- intern/cycles/scene/bake.cpp | 2 +- intern/cycles/scene/bake.h | 4 +- intern/cycles/scene/camera.cpp | 14 +- intern/cycles/scene/camera.h | 12 +- intern/cycles/scene/colorspace.cpp | 14 +- intern/cycles/scene/colorspace.h | 4 +- intern/cycles/scene/constant_fold.cpp | 4 +- intern/cycles/scene/constant_fold.h | 4 +- intern/cycles/scene/curves.cpp | 8 +- intern/cycles/scene/curves.h | 4 +- intern/cycles/scene/film.cpp | 10 +- intern/cycles/scene/film.h | 6 +- intern/cycles/scene/geometry.cpp | 14 +- intern/cycles/scene/geometry.h | 12 +- intern/cycles/scene/hair.cpp | 2 +- intern/cycles/scene/image.cpp | 18 +- intern/cycles/scene/image.h | 12 +- intern/cycles/scene/image_oiio.cpp | 6 +- intern/cycles/scene/image_sky.cpp | 8 +- intern/cycles/scene/image_vdb.cpp | 4 +- intern/cycles/scene/integrator.cpp | 12 +- intern/cycles/scene/integrator.h | 4 +- intern/cycles/scene/jitter.h | 2 +- intern/cycles/scene/light.cpp | 12 +- intern/cycles/scene/light.h | 10 +- intern/cycles/scene/mesh.cpp | 14 +- intern/cycles/scene/mesh.h | 18 +- intern/cycles/scene/mesh_displace.cpp | 8 +- intern/cycles/scene/mesh_subdivision.cpp | 12 +- intern/cycles/scene/object.cpp | 18 +- intern/cycles/scene/object.h | 14 +- intern/cycles/scene/osl.cpp | 20 +- intern/cycles/scene/osl.h | 8 +- intern/cycles/scene/particles.cpp | 12 +- intern/cycles/scene/particles.h | 4 +- intern/cycles/scene/pass.cpp | 4 +- intern/cycles/scene/pass.h | 6 +- intern/cycles/scene/procedural.cpp | 7 +- intern/cycles/scene/scene.cpp | 8 +- intern/cycles/scene/scene.h | 18 +- intern/cycles/scene/shader.cpp | 8 +- intern/cycles/scene/shader.h | 14 +- intern/cycles/scene/shader_graph.cpp | 10 +- intern/cycles/scene/shader_graph.h | 14 +- intern/cycles/scene/shader_nodes.cpp | 16 +- intern/cycles/scene/shader_nodes.h | 4 +- intern/cycles/scene/sobol.cpp | 4 +- intern/cycles/scene/sobol.h | 2 +- intern/cycles/scene/stats.cpp | 6 +- intern/cycles/scene/stats.h | 6 +- intern/cycles/scene/svm.cpp | 8 +- intern/cycles/scene/svm.h | 8 +- intern/cycles/scene/tables.cpp | 4 +- intern/cycles/scene/tables.h | 4 +- intern/cycles/scene/volume.cpp | 12 +- intern/cycles/session/buffers.cpp | 10 +- intern/cycles/session/buffers.h | 12 +- intern/cycles/session/display_driver.h | 4 +- intern/cycles/session/merge.cpp | 10 +- intern/cycles/session/merge.h | 4 +- intern/cycles/session/output_driver.h | 6 +- intern/cycles/session/session.cpp | 12 +- intern/cycles/session/session.h | 10 +- intern/cycles/session/tile.cpp | 14 +- intern/cycles/session/tile.h | 6 +- intern/cycles/subd/CMakeLists.txt | 18 +- .../cycles/subd/{subd_dice.cpp => dice.cpp} | 4 +- intern/cycles/subd/{subd_dice.h => dice.h} | 6 +- .../cycles/subd/{subd_patch.cpp => patch.cpp} | 6 +- intern/cycles/subd/{subd_patch.h => patch.h} | 4 +- .../{subd_patch_table.cpp => patch_table.cpp} | 6 +- .../{subd_patch_table.h => patch_table.h} | 4 +- .../cycles/subd/{subd_split.cpp => split.cpp} | 16 +- intern/cycles/subd/{subd_split.h => split.h} | 10 +- .../subd/{subd_subpatch.h => subpatch.h} | 4 +- intern/cycles/test/CMakeLists.txt | 6 - .../integrator_adaptive_sampling_test.cpp | 2 +- intern/cycles/test/integrator_tile_test.cpp | 2 +- .../test/render_graph_finalize_test.cpp | 10 +- .../cycles/test/util_aligned_malloc_test.cpp | 2 +- intern/cycles/test/util_avxf_test.h | 4 +- intern/cycles/test/util_math_test.cpp | 2 +- intern/cycles/test/util_path_test.cpp | 2 +- intern/cycles/test/util_string_test.cpp | 2 +- intern/cycles/test/util_task_test.cpp | 2 +- intern/cycles/test/util_time_test.cpp | 2 +- intern/cycles/test/util_transform_test.cpp | 4 +- intern/cycles/util/CMakeLists.txt | 240 ++++++------ .../util/{util_algorithm.h => algorithm.h} | 0 ..._aligned_malloc.cpp => aligned_malloc.cpp} | 4 +- ...util_aligned_malloc.h => aligned_malloc.h} | 2 +- intern/cycles/util/{util_args.h => args.h} | 0 intern/cycles/util/{util_array.h => array.h} | 8 +- .../cycles/util/{util_atomic.h => atomic.h} | 0 intern/cycles/util/{util_avxb.h => avxb.h} | 0 intern/cycles/util/{util_avxf.h => avxf.h} | 0 intern/cycles/util/{util_avxi.h => avxi.h} | 0 .../util/{util_boundbox.h => boundbox.h} | 8 +- intern/cycles/util/{util_color.h => color.h} | 6 +- .../cycles/util/{util_debug.cpp => debug.cpp} | 8 +- intern/cycles/util/{util_debug.h => debug.h} | 2 +- .../cycles/util/{util_defines.h => defines.h} | 0 intern/cycles/util/{util_deque.h => deque.h} | 0 .../{util_disjoint_set.h => disjoint_set.h} | 2 +- .../cycles/util/{util_foreach.h => foreach.h} | 0 .../util/{util_function.h => function.h} | 0 ...ed_allocator.cpp => guarded_allocator.cpp} | 4 +- ...uarded_allocator.h => guarded_allocator.h} | 0 intern/cycles/util/{util_half.h => half.h} | 6 +- intern/cycles/util/{util_hash.h => hash.h} | 2 +- intern/cycles/util/{util_ies.cpp => ies.cpp} | 8 +- intern/cycles/util/{util_ies.h => ies.h} | 4 +- intern/cycles/util/{util_image.h => image.h} | 6 +- .../util/{util_image_impl.h => image_impl.h} | 6 +- intern/cycles/util/{util_list.h => list.h} | 0 .../cycles/util/{util_logging.cpp => log.cpp} | 6 +- intern/cycles/util/{util_logging.h => log.h} | 0 intern/cycles/util/{util_map.h => map.h} | 0 intern/cycles/util/{util_math.h => math.h} | 16 +- .../util/{util_math_cdf.cpp => math_cdf.cpp} | 6 +- .../util/{util_math_cdf.h => math_cdf.h} | 6 +- .../util/{util_math_fast.h => math_fast.h} | 0 .../{util_math_float2.h => math_float2.h} | 2 +- .../{util_math_float3.h => math_float3.h} | 2 +- .../{util_math_float4.h => math_float4.h} | 2 +- .../util/{util_math_int2.h => math_int2.h} | 2 +- .../util/{util_math_int3.h => math_int3.h} | 2 +- .../util/{util_math_int4.h => math_int4.h} | 2 +- ...util_math_intersect.h => math_intersect.h} | 0 .../{util_math_matrix.h => math_matrix.h} | 0 intern/cycles/util/{util_md5.cpp => md5.cpp} | 4 +- intern/cycles/util/{util_md5.h => md5.h} | 4 +- .../{util_murmurhash.cpp => murmurhash.cpp} | 4 +- .../util/{util_murmurhash.h => murmurhash.h} | 2 +- .../cycles/util/{util_opengl.h => opengl.h} | 0 ..._openimagedenoise.h => openimagedenoise.h} | 2 +- .../cycles/util/{util_openvdb.h => openvdb.h} | 0 .../{util_optimization.h => optimization.h} | 0 intern/cycles/util/{util_param.h => param.h} | 0 .../cycles/util/{util_path.cpp => path.cpp} | 10 +- intern/cycles/util/{util_path.h => path.h} | 8 +- .../{util_profiling.cpp => profiling.cpp} | 8 +- .../util/{util_profiling.h => profiling.h} | 6 +- .../util/{util_progress.h => progress.h} | 8 +- .../util/{util_projection.h => projection.h} | 2 +- intern/cycles/util/{util_queue.h => queue.h} | 0 intern/cycles/util/{util_rect.h => rect.h} | 2 +- .../util/{util_semaphore.h => semaphore.h} | 2 +- intern/cycles/util/{util_set.h => set.h} | 0 .../cycles/util/{util_simd.cpp => simd.cpp} | 2 +- intern/cycles/util/{util_simd.h => simd.h} | 4 +- intern/cycles/util/{util_sseb.h => sseb.h} | 0 intern/cycles/util/{util_ssef.h => ssef.h} | 2 +- intern/cycles/util/{util_ssei.h => ssei.h} | 0 ...il_stack_allocator.h => stack_allocator.h} | 0 .../{util_static_assert.h => static_assert.h} | 0 intern/cycles/util/{util_stats.h => stats.h} | 4 +- .../util/{util_string.cpp => string.cpp} | 6 +- .../cycles/util/{util_string.h => string.h} | 7 +- .../util/{util_system.cpp => system.cpp} | 10 +- .../cycles/util/{util_system.h => system.h} | 4 +- .../cycles/util/{util_task.cpp => task.cpp} | 10 +- intern/cycles/util/{util_task.h => task.h} | 10 +- intern/cycles/util/{util_tbb.h => tbb.h} | 2 +- .../cycles/util/{util_texture.h => texture.h} | 2 +- .../util/{util_thread.cpp => thread.cpp} | 6 +- .../cycles/util/{util_thread.h => thread.h} | 4 +- .../cycles/util/{util_time.cpp => time.cpp} | 8 +- intern/cycles/util/{util_time.h => time.h} | 4 +- .../{util_transform.cpp => transform.cpp} | 8 +- .../util/{util_transform.h => transform.h} | 4 +- intern/cycles/util/{util_types.h => types.h} | 76 ++-- .../{util_types_float2.h => types_float2.h} | 2 +- ...ypes_float2_impl.h => types_float2_impl.h} | 2 +- .../{util_types_float3.h => types_float3.h} | 2 +- ...ypes_float3_impl.h => types_float3_impl.h} | 2 +- .../{util_types_float4.h => types_float4.h} | 2 +- ...ypes_float4_impl.h => types_float4_impl.h} | 2 +- .../{util_types_float8.h => types_float8.h} | 2 +- ...ypes_float8_impl.h => types_float8_impl.h} | 2 +- .../util/{util_types_int2.h => types_int2.h} | 2 +- ...il_types_int2_impl.h => types_int2_impl.h} | 2 +- .../util/{util_types_int3.h => types_int3.h} | 2 +- ...il_types_int3_impl.h => types_int3_impl.h} | 2 +- .../util/{util_types_int4.h => types_int4.h} | 2 +- ...il_types_int4_impl.h => types_int4_impl.h} | 2 +- .../{util_types_uchar2.h => types_uchar2.h} | 2 +- ...ypes_uchar2_impl.h => types_uchar2_impl.h} | 2 +- .../{util_types_uchar3.h => types_uchar3.h} | 2 +- ...ypes_uchar3_impl.h => types_uchar3_impl.h} | 2 +- .../{util_types_uchar4.h => types_uchar4.h} | 2 +- ...ypes_uchar4_impl.h => types_uchar4_impl.h} | 2 +- .../{util_types_uint2.h => types_uint2.h} | 2 +- ..._types_uint2_impl.h => types_uint2_impl.h} | 2 +- .../{util_types_uint3.h => types_uint3.h} | 2 +- ..._types_uint3_impl.h => types_uint3_impl.h} | 2 +- .../{util_types_uint4.h => types_uint4.h} | 2 +- ..._types_uint4_impl.h => types_uint4_impl.h} | 2 +- .../{util_types_ushort4.h => types_ushort4.h} | 2 +- .../{util_types_vector3.h => types_vector3.h} | 2 +- ...es_vector3_impl.h => types_vector3_impl.h} | 2 +- .../util/{util_unique_ptr.h => unique_ptr.h} | 0 .../cycles/util/{util_vector.h => vector.h} | 6 +- .../cycles/util/{util_version.h => version.h} | 0 .../cycles/util/{util_view.cpp => view.cpp} | 10 +- intern/cycles/util/{util_view.h => view.h} | 0 .../util/{util_windows.cpp => windows.cpp} | 2 +- .../cycles/util/{util_windows.h => windows.h} | 0 intern/cycles/util/{util_xml.h => xml.h} | 0 531 files changed, 2028 insertions(+), 1826 deletions(-) rename intern/cycles/blender/{blender_camera.cpp => camera.cpp} (99%) rename intern/cycles/blender/{blender_curves.cpp => curves.cpp} (99%) rename intern/cycles/blender/{blender_device.cpp => device.cpp} (96%) rename intern/cycles/blender/{blender_device.h => device.h} (100%) rename intern/cycles/blender/{blender_display_driver.cpp => display_driver.cpp} (99%) rename intern/cycles/blender/{blender_display_driver.h => display_driver.h} (99%) rename intern/cycles/blender/{blender_geometry.cpp => geometry.cpp} (98%) rename intern/cycles/blender/{blender_id_map.h => id_map.h} (98%) rename intern/cycles/blender/{blender_image.cpp => image.cpp} (98%) rename intern/cycles/blender/{blender_image.h => image.h} (100%) rename intern/cycles/blender/{blender_light.cpp => light.cpp} (98%) rename intern/cycles/blender/{blender_logging.cpp => logging.cpp} (96%) rename intern/cycles/blender/{blender_mesh.cpp => mesh.cpp} (99%) rename intern/cycles/blender/{blender_object.cpp => object.cpp} (99%) rename intern/cycles/blender/{blender_object_cull.cpp => object_cull.cpp} (98%) rename intern/cycles/blender/{blender_object_cull.h => object_cull.h} (95%) rename intern/cycles/blender/{blender_output_driver.cpp => output_driver.cpp} (98%) rename intern/cycles/blender/{blender_output_driver.h => output_driver.h} (100%) rename intern/cycles/blender/{blender_particles.cpp => particles.cpp} (96%) rename intern/cycles/blender/{blender_python.cpp => python.cpp} (98%) rename intern/cycles/blender/{blender_session.cpp => session.cpp} (98%) rename intern/cycles/blender/{blender_session.h => session.h} (98%) rename intern/cycles/blender/{blender_shader.cpp => shader.cpp} (99%) rename intern/cycles/blender/{blender_sync.cpp => sync.cpp} (98%) rename intern/cycles/blender/{blender_sync.h => sync.h} (97%) rename intern/cycles/blender/{blender_texture.cpp => texture.cpp} (97%) rename intern/cycles/blender/{blender_texture.h => texture.h} (96%) rename intern/cycles/blender/{blender_util.h => util.h} (98%) rename intern/cycles/blender/{blender_viewport.cpp => viewport.cpp} (97%) rename intern/cycles/blender/{blender_viewport.h => viewport.h} (100%) rename intern/cycles/blender/{blender_volume.cpp => volume.cpp} (99%) rename intern/cycles/bvh/{bvh_binning.cpp => binning.cpp} (98%) rename intern/cycles/bvh/{bvh_binning.h => binning.h} (97%) rename intern/cycles/bvh/{bvh_build.cpp => build.cpp} (99%) rename intern/cycles/bvh/{bvh_build.h => build.h} (96%) rename intern/cycles/bvh/{bvh_embree.cpp => embree.cpp} (98%) rename intern/cycles/bvh/{bvh_embree.h => embree.h} (93%) rename intern/cycles/bvh/{bvh_multi.cpp => multi.cpp} (94%) rename intern/cycles/bvh/{bvh_multi.h => multi.h} (97%) rename intern/cycles/bvh/{bvh_node.cpp => node.cpp} (98%) rename intern/cycles/bvh/{bvh_node.h => node.h} (98%) rename intern/cycles/bvh/{bvh_optix.cpp => optix.cpp} (97%) rename intern/cycles/bvh/{bvh_optix.h => optix.h} (94%) rename intern/cycles/bvh/{bvh_params.h => params.h} (99%) rename intern/cycles/bvh/{bvh_sort.cpp => sort.cpp} (98%) rename intern/cycles/bvh/{bvh_sort.h => sort.h} (100%) rename intern/cycles/bvh/{bvh_split.cpp => split.cpp} (99%) rename intern/cycles/bvh/{bvh_split.h => split.h} (99%) rename intern/cycles/bvh/{bvh_unaligned.cpp => unaligned.cpp} (97%) rename intern/cycles/bvh/{bvh_unaligned.h => unaligned.h} (98%) rename intern/cycles/device/{device_denoise.cpp => denoise.cpp} (98%) rename intern/cycles/device/{device_denoise.h => denoise.h} (99%) rename intern/cycles/device/{device_graphics_interop.cpp => graphics_interop.cpp} (93%) rename intern/cycles/device/{device_graphics_interop.h => graphics_interop.h} (97%) rename intern/cycles/device/{device_kernel.cpp => kernel.cpp} (98%) rename intern/cycles/device/{device_kernel.h => kernel.h} (93%) rename intern/cycles/device/{device_memory.cpp => memory.cpp} (99%) rename intern/cycles/device/{device_memory.h => memory.h} (98%) rename intern/cycles/device/{device_queue.cpp => queue.cpp} (95%) rename intern/cycles/device/{device_queue.h => queue.h} (95%) rename intern/cycles/kernel/bvh/{bvh_embree.h => embree.h} (99%) rename intern/cycles/kernel/bvh/{bvh_local.h => local.h} (100%) rename intern/cycles/kernel/bvh/{bvh_nodes.h => nodes.h} (100%) rename intern/cycles/kernel/bvh/{bvh_shadow_all.h => shadow_all.h} (100%) rename intern/cycles/kernel/bvh/{bvh_traversal.h => traversal.h} (100%) rename intern/cycles/kernel/bvh/{bvh_types.h => types.h} (100%) rename intern/cycles/kernel/bvh/{bvh_util.h => util.h} (100%) rename intern/cycles/kernel/bvh/{bvh_volume.h => volume.h} (100%) rename intern/cycles/kernel/bvh/{bvh_volume_all.h => volume_all.h} (100%) rename intern/cycles/kernel/camera/{camera_projection.h => projection.h} (100%) rename intern/cycles/kernel/film/{film_accumulate.h => accumulate.h} (99%) rename intern/cycles/kernel/film/{film_adaptive_sampling.h => adaptive_sampling.h} (99%) rename intern/cycles/kernel/film/{film_id_passes.h => id_passes.h} (100%) rename intern/cycles/kernel/film/{film_passes.h => passes.h} (99%) rename intern/cycles/kernel/film/{film_read.h => read.h} (100%) rename intern/cycles/kernel/film/{film_write_passes.h => write_passes.h} (100%) rename intern/cycles/kernel/geom/{geom_attribute.h => attribute.h} (100%) rename intern/cycles/kernel/geom/{geom_curve.h => curve.h} (100%) rename intern/cycles/kernel/geom/{geom_curve_intersect.h => curve_intersect.h} (100%) rename intern/cycles/kernel/geom/{geom_motion_curve.h => motion_curve.h} (100%) rename intern/cycles/kernel/geom/{geom_motion_triangle.h => motion_triangle.h} (99%) rename intern/cycles/kernel/geom/{geom_motion_triangle_intersect.h => motion_triangle_intersect.h} (100%) rename intern/cycles/kernel/geom/{geom_motion_triangle_shader.h => motion_triangle_shader.h} (100%) rename intern/cycles/kernel/geom/{geom_object.h => object.h} (100%) rename intern/cycles/kernel/geom/{geom_patch.h => patch.h} (99%) rename intern/cycles/kernel/geom/{geom_primitive.h => primitive.h} (99%) rename intern/cycles/kernel/geom/{geom_shader_data.h => shader_data.h} (100%) rename intern/cycles/kernel/geom/{geom_subd_triangle.h => subd_triangle.h} (100%) rename intern/cycles/kernel/geom/{geom_triangle.h => triangle.h} (100%) rename intern/cycles/kernel/geom/{geom_triangle_intersect.h => triangle_intersect.h} (99%) rename intern/cycles/kernel/geom/{geom_volume.h => volume.h} (100%) rename intern/cycles/kernel/integrator/{integrator_init_from_bake.h => init_from_bake.h} (97%) rename intern/cycles/kernel/integrator/{integrator_init_from_camera.h => init_from_camera.h} (94%) rename intern/cycles/kernel/integrator/{integrator_intersect_closest.h => intersect_closest.h} (97%) rename intern/cycles/kernel/integrator/{integrator_intersect_shadow.h => intersect_shadow.h} (100%) rename intern/cycles/kernel/integrator/{integrator_intersect_subsurface.h => intersect_subsurface.h} (94%) rename intern/cycles/kernel/integrator/{integrator_intersect_volume_stack.h => intersect_volume_stack.h} (98%) rename intern/cycles/kernel/integrator/{integrator_megakernel.h => megakernel.h} (85%) rename intern/cycles/kernel/integrator/{integrator_path_state.h => path_state.h} (99%) rename intern/cycles/kernel/integrator/{integrator_shade_background.h => shade_background.h} (98%) rename intern/cycles/kernel/integrator/{integrator_shade_light.h => shade_light.h} (97%) rename intern/cycles/kernel/integrator/{integrator_shade_shadow.h => shade_shadow.h} (97%) rename intern/cycles/kernel/integrator/{integrator_shade_surface.h => shade_surface.h} (98%) rename intern/cycles/kernel/integrator/{integrator_shade_volume.h => shade_volume.h} (99%) rename intern/cycles/kernel/integrator/{integrator_shader_eval.h => shader_eval.h} (99%) rename intern/cycles/kernel/integrator/{integrator_shadow_catcher.h => shadow_catcher.h} (97%) rename intern/cycles/kernel/integrator/{integrator_shadow_state_template.h => shadow_state_template.h} (100%) rename intern/cycles/kernel/integrator/{integrator_state.h => state.h} (95%) rename intern/cycles/kernel/integrator/{integrator_state_flow.h => state_flow.h} (99%) rename intern/cycles/kernel/integrator/{integrator_state_template.h => state_template.h} (100%) rename intern/cycles/kernel/integrator/{integrator_state_util.h => state_util.h} (98%) rename intern/cycles/kernel/integrator/{integrator_subsurface.h => subsurface.h} (95%) rename intern/cycles/kernel/integrator/{integrator_subsurface_disk.h => subsurface_disk.h} (100%) rename intern/cycles/kernel/integrator/{integrator_subsurface_random_walk.h => subsurface_random_walk.h} (99%) rename intern/cycles/kernel/integrator/{integrator_volume_stack.h => volume_stack.h} (100%) rename intern/cycles/kernel/light/{light_background.h => background.h} (99%) rename intern/cycles/kernel/light/{light_common.h => common.h} (99%) rename intern/cycles/kernel/light/{light_sample.h => sample.h} (98%) rename intern/cycles/kernel/osl/{osl_bssrdf.cpp => bssrdf.cpp} (98%) rename intern/cycles/kernel/osl/{osl_closures.cpp => closures.cpp} (99%) rename intern/cycles/kernel/osl/{osl_closures.h => closures.h} (99%) rename intern/cycles/kernel/osl/{osl_globals.h => globals.h} (93%) rename intern/cycles/kernel/osl/{osl_services.cpp => services.cpp} (98%) rename intern/cycles/kernel/osl/{osl_services.h => services.h} (100%) rename intern/cycles/kernel/osl/{osl_shader.cpp => shader.cpp} (98%) rename intern/cycles/kernel/osl/{osl_shader.h => shader.h} (98%) rename intern/cycles/kernel/sample/{sample_jitter.h => jitter.h} (100%) create mode 100644 intern/cycles/kernel/sample/lcg.h rename intern/cycles/kernel/sample/{sample_mapping.h => mapping.h} (100%) create mode 100644 intern/cycles/kernel/sample/mis.h rename intern/cycles/kernel/sample/{sample_pattern.h => pattern.h} (98%) rename intern/cycles/kernel/svm/{svm_ao.h => ao.h} (99%) rename intern/cycles/kernel/svm/{svm_aov.h => aov.h} (98%) rename intern/cycles/kernel/svm/{svm_attribute.h => attribute.h} (99%) rename intern/cycles/kernel/svm/{svm_bevel.h => bevel.h} (99%) rename intern/cycles/kernel/svm/{svm_blackbody.h => blackbody.h} (97%) rename intern/cycles/kernel/svm/{svm_brick.h => brick.h} (99%) rename intern/cycles/kernel/svm/{svm_brightness.h => brightness.h} (96%) rename intern/cycles/kernel/svm/{svm_bump.h => bump.h} (99%) rename intern/cycles/kernel/svm/{svm_camera.h => camera.h} (99%) rename intern/cycles/kernel/svm/{svm_checker.h => checker.h} (99%) rename intern/cycles/kernel/svm/{svm_clamp.h => clamp.h} (99%) rename intern/cycles/kernel/svm/{svm_closure.h => closure.h} (99%) rename intern/cycles/kernel/svm/{svm_color_util.h => color_util.h} (99%) rename intern/cycles/kernel/svm/{svm_convert.h => convert.h} (99%) rename intern/cycles/kernel/svm/{svm_displace.h => displace.h} (99%) rename intern/cycles/kernel/svm/{svm_fractal_noise.h => fractal_noise.h} (98%) rename intern/cycles/kernel/svm/{svm_fresnel.h => fresnel.h} (99%) rename intern/cycles/kernel/svm/{svm_gamma.h => gamma.h} (98%) rename intern/cycles/kernel/svm/{svm_geometry.h => geometry.h} (99%) rename intern/cycles/kernel/svm/{svm_gradient.h => gradient.h} (99%) rename intern/cycles/kernel/svm/{svm_hsv.h => hsv.h} (96%) rename intern/cycles/kernel/svm/{svm_ies.h => ies.h} (99%) rename intern/cycles/kernel/svm/{svm_image.h => image.h} (99%) rename intern/cycles/kernel/svm/{svm_invert.h => invert.h} (98%) rename intern/cycles/kernel/svm/{svm_light_path.h => light_path.h} (99%) rename intern/cycles/kernel/svm/{svm_magic.h => magic.h} (99%) rename intern/cycles/kernel/svm/{svm_map_range.h => map_range.h} (99%) rename intern/cycles/kernel/svm/{svm_mapping.h => mapping.h} (98%) rename intern/cycles/kernel/svm/{svm_mapping_util.h => mapping_util.h} (99%) rename intern/cycles/kernel/svm/{svm_math.h => math.h} (99%) rename intern/cycles/kernel/svm/{svm_math_util.h => math_util.h} (99%) rename intern/cycles/kernel/svm/{svm_mix.h => mix.h} (99%) rename intern/cycles/kernel/svm/{svm_musgrave.h => musgrave.h} (99%) rename intern/cycles/kernel/svm/{svm_noise.h => noise.h} (99%) rename intern/cycles/kernel/svm/{svm_noisetex.h => noisetex.h} (99%) rename intern/cycles/kernel/svm/{svm_normal.h => normal.h} (99%) rename intern/cycles/kernel/svm/{svm_ramp.h => ramp.h} (98%) rename intern/cycles/kernel/svm/{svm_ramp_util.h => ramp_util.h} (96%) rename intern/cycles/kernel/svm/{svm_sepcomb_hsv.h => sepcomb_hsv.h} (99%) rename intern/cycles/kernel/svm/{svm_sepcomb_vector.h => sepcomb_vector.h} (99%) rename intern/cycles/kernel/svm/{svm_sky.h => sky.h} (99%) rename intern/cycles/kernel/svm/{svm_tex_coord.h => tex_coord.h} (99%) rename intern/cycles/kernel/svm/{svm_types.h => types.h} (99%) rename intern/cycles/kernel/svm/{svm_value.h => value.h} (99%) rename intern/cycles/kernel/svm/{svm_vector_rotate.h => vector_rotate.h} (99%) rename intern/cycles/kernel/svm/{svm_vector_transform.h => vector_transform.h} (99%) rename intern/cycles/kernel/svm/{svm_vertex_color.h => vertex_color.h} (99%) rename intern/cycles/kernel/svm/{svm_voronoi.h => voronoi.h} (99%) rename intern/cycles/kernel/svm/{svm_voxel.h => voxel.h} (99%) rename intern/cycles/kernel/svm/{svm_wave.h => wave.h} (99%) rename intern/cycles/kernel/svm/{svm_wavelength.h => wavelength.h} (99%) rename intern/cycles/kernel/svm/{svm_white_noise.h => white_noise.h} (99%) rename intern/cycles/kernel/svm/{svm_wireframe.h => wireframe.h} (99%) rename intern/cycles/kernel/{kernel_textures.h => textures.h} (100%) rename intern/cycles/kernel/{kernel_types.h => types.h} (99%) rename intern/cycles/kernel/util/{util_color.h => color.h} (97%) rename intern/cycles/kernel/util/{util_differential.h => differential.h} (100%) rename intern/cycles/kernel/util/{util_lookup_table.h => lookup_table.h} (100%) rename intern/cycles/kernel/util/{util_profiling.h => profiling.h} (97%) rename intern/cycles/subd/{subd_dice.cpp => dice.cpp} (99%) rename intern/cycles/subd/{subd_dice.h => dice.h} (96%) rename intern/cycles/subd/{subd_patch.cpp => patch.cpp} (96%) rename intern/cycles/subd/{subd_patch.h => patch.h} (95%) rename intern/cycles/subd/{subd_patch_table.cpp => patch_table.cpp} (98%) rename intern/cycles/subd/{subd_patch_table.h => patch_table.h} (96%) rename intern/cycles/subd/{subd_split.cpp => split.cpp} (98%) rename intern/cycles/subd/{subd_split.h => split.h} (93%) rename intern/cycles/subd/{subd_subpatch.h => subpatch.h} (98%) rename intern/cycles/util/{util_algorithm.h => algorithm.h} (100%) rename intern/cycles/util/{util_aligned_malloc.cpp => aligned_malloc.cpp} (96%) rename intern/cycles/util/{util_aligned_malloc.h => aligned_malloc.h} (97%) rename intern/cycles/util/{util_args.h => args.h} (100%) rename intern/cycles/util/{util_array.h => array.h} (97%) rename intern/cycles/util/{util_atomic.h => atomic.h} (100%) rename intern/cycles/util/{util_avxb.h => avxb.h} (100%) rename intern/cycles/util/{util_avxf.h => avxf.h} (100%) rename intern/cycles/util/{util_avxi.h => avxi.h} (100%) rename intern/cycles/util/{util_boundbox.h => boundbox.h} (98%) rename intern/cycles/util/{util_color.h => color.h} (98%) rename intern/cycles/util/{util_debug.cpp => debug.cpp} (96%) rename intern/cycles/util/{util_debug.h => debug.h} (99%) rename intern/cycles/util/{util_defines.h => defines.h} (100%) rename intern/cycles/util/{util_deque.h => deque.h} (100%) rename intern/cycles/util/{util_disjoint_set.h => disjoint_set.h} (98%) rename intern/cycles/util/{util_foreach.h => foreach.h} (100%) rename intern/cycles/util/{util_function.h => function.h} (100%) rename intern/cycles/util/{util_guarded_allocator.cpp => guarded_allocator.cpp} (93%) rename intern/cycles/util/{util_guarded_allocator.h => guarded_allocator.h} (100%) rename intern/cycles/util/{util_half.h => half.h} (98%) rename intern/cycles/util/{util_hash.h => hash.h} (99%) rename intern/cycles/util/{util_ies.cpp => ies.cpp} (99%) rename intern/cycles/util/{util_ies.h => ies.h} (96%) rename intern/cycles/util/{util_image.h => image.h} (96%) rename intern/cycles/util/{util_image_impl.h => image_impl.h} (98%) rename intern/cycles/util/{util_list.h => list.h} (100%) rename intern/cycles/util/{util_logging.cpp => log.cpp} (96%) rename intern/cycles/util/{util_logging.h => log.h} (100%) rename intern/cycles/util/{util_map.h => map.h} (100%) rename intern/cycles/util/{util_math.h => math.h} (98%) rename intern/cycles/util/{util_math_cdf.cpp => math_cdf.cpp} (95%) rename intern/cycles/util/{util_math_cdf.h => math_cdf.h} (96%) rename intern/cycles/util/{util_math_fast.h => math_fast.h} (100%) rename intern/cycles/util/{util_math_float2.h => math_float2.h} (99%) rename intern/cycles/util/{util_math_float3.h => math_float3.h} (99%) rename intern/cycles/util/{util_math_float4.h => math_float4.h} (99%) rename intern/cycles/util/{util_math_int2.h => math_int2.h} (96%) rename intern/cycles/util/{util_math_int3.h => math_int3.h} (97%) rename intern/cycles/util/{util_math_int4.h => math_int4.h} (98%) rename intern/cycles/util/{util_math_intersect.h => math_intersect.h} (100%) rename intern/cycles/util/{util_math_matrix.h => math_matrix.h} (100%) rename intern/cycles/util/{util_md5.cpp => md5.cpp} (99%) rename intern/cycles/util/{util_md5.h => md5.h} (96%) rename intern/cycles/util/{util_murmurhash.cpp => murmurhash.cpp} (97%) rename intern/cycles/util/{util_murmurhash.h => murmurhash.h} (96%) rename intern/cycles/util/{util_opengl.h => opengl.h} (100%) rename intern/cycles/util/{util_openimagedenoise.h => openimagedenoise.h} (97%) rename intern/cycles/util/{util_openvdb.h => openvdb.h} (100%) rename intern/cycles/util/{util_optimization.h => optimization.h} (100%) rename intern/cycles/util/{util_param.h => param.h} (100%) rename intern/cycles/util/{util_path.cpp => path.cpp} (99%) rename intern/cycles/util/{util_path.h => path.h} (95%) rename intern/cycles/util/{util_profiling.cpp => profiling.cpp} (97%) rename intern/cycles/util/{util_profiling.h => profiling.h} (98%) rename intern/cycles/util/{util_progress.h => progress.h} (98%) rename intern/cycles/util/{util_projection.h => projection.h} (99%) rename intern/cycles/util/{util_queue.h => queue.h} (100%) rename intern/cycles/util/{util_rect.h => rect.h} (98%) rename intern/cycles/util/{util_semaphore.h => semaphore.h} (97%) rename intern/cycles/util/{util_set.h => set.h} (100%) rename intern/cycles/util/{util_simd.cpp => simd.cpp} (98%) rename intern/cycles/util/{util_simd.h => simd.h} (99%) rename intern/cycles/util/{util_sseb.h => sseb.h} (100%) rename intern/cycles/util/{util_ssef.h => ssef.h} (99%) rename intern/cycles/util/{util_ssei.h => ssei.h} (100%) rename intern/cycles/util/{util_stack_allocator.h => stack_allocator.h} (100%) rename intern/cycles/util/{util_static_assert.h => static_assert.h} (100%) rename intern/cycles/util/{util_stats.h => stats.h} (94%) rename intern/cycles/util/{util_string.cpp => string.cpp} (98%) rename intern/cycles/util/{util_string.h => string.h} (95%) rename intern/cycles/util/{util_system.cpp => system.cpp} (98%) rename intern/cycles/util/{util_system.h => system.h} (97%) rename intern/cycles/util/{util_task.cpp => task.cpp} (96%) rename intern/cycles/util/{util_task.h => task.h} (96%) rename intern/cycles/util/{util_tbb.h => tbb.h} (98%) rename intern/cycles/util/{util_texture.h => texture.h} (98%) rename intern/cycles/util/{util_thread.cpp => thread.cpp} (94%) rename intern/cycles/util/{util_thread.h => thread.h} (97%) rename intern/cycles/util/{util_time.cpp => time.cpp} (96%) rename intern/cycles/util/{util_time.h => time.h} (96%) rename intern/cycles/util/{util_transform.cpp => transform.cpp} (98%) rename intern/cycles/util/{util_transform.h => transform.h} (99%) rename intern/cycles/util/{util_types.h => types.h} (59%) rename intern/cycles/util/{util_types_float2.h => types_float2.h} (94%) rename intern/cycles/util/{util_types_float2_impl.h => types_float2_impl.h} (95%) rename intern/cycles/util/{util_types_float3.h => types_float3.h} (96%) rename intern/cycles/util/{util_types_float3_impl.h => types_float3_impl.h} (97%) rename intern/cycles/util/{util_types_float4.h => types_float4.h} (96%) rename intern/cycles/util/{util_types_float4_impl.h => types_float4_impl.h} (97%) rename intern/cycles/util/{util_types_float8.h => types_float8.h} (97%) rename intern/cycles/util/{util_types_float8_impl.h => types_float8_impl.h} (97%) rename intern/cycles/util/{util_types_int2.h => types_int2.h} (93%) rename intern/cycles/util/{util_types_int2_impl.h => types_int2_impl.h} (94%) rename intern/cycles/util/{util_types_int3.h => types_int3.h} (96%) rename intern/cycles/util/{util_types_int3_impl.h => types_int3_impl.h} (96%) rename intern/cycles/util/{util_types_int4.h => types_int4.h} (96%) rename intern/cycles/util/{util_types_int4_impl.h => types_int4_impl.h} (97%) rename intern/cycles/util/{util_types_uchar2.h => types_uchar2.h} (94%) rename intern/cycles/util/{util_types_uchar2_impl.h => types_uchar2_impl.h} (94%) rename intern/cycles/util/{util_types_uchar3.h => types_uchar3.h} (94%) rename intern/cycles/util/{util_types_uchar3_impl.h => types_uchar3_impl.h} (94%) rename intern/cycles/util/{util_types_uchar4.h => types_uchar4.h} (94%) rename intern/cycles/util/{util_types_uchar4_impl.h => types_uchar4_impl.h} (94%) rename intern/cycles/util/{util_types_uint2.h => types_uint2.h} (94%) rename intern/cycles/util/{util_types_uint2_impl.h => types_uint2_impl.h} (94%) rename intern/cycles/util/{util_types_uint3.h => types_uint3.h} (94%) rename intern/cycles/util/{util_types_uint3_impl.h => types_uint3_impl.h} (94%) rename intern/cycles/util/{util_types_uint4.h => types_uint4.h} (94%) rename intern/cycles/util/{util_types_uint4_impl.h => types_uint4_impl.h} (94%) rename intern/cycles/util/{util_types_ushort4.h => types_ushort4.h} (93%) rename intern/cycles/util/{util_types_vector3.h => types_vector3.h} (94%) rename intern/cycles/util/{util_types_vector3_impl.h => types_vector3_impl.h} (94%) rename intern/cycles/util/{util_unique_ptr.h => unique_ptr.h} (100%) rename intern/cycles/util/{util_vector.h => vector.h} (93%) rename intern/cycles/util/{util_version.h => version.h} (100%) rename intern/cycles/util/{util_view.cpp => view.cpp} (97%) rename intern/cycles/util/{util_view.h => view.h} (100%) rename intern/cycles/util/{util_windows.cpp => windows.cpp} (98%) rename intern/cycles/util/{util_windows.h => windows.h} (100%) rename intern/cycles/util/{util_xml.h => xml.h} (100%) diff --git a/intern/cycles/app/cycles_server.cpp b/intern/cycles/app/cycles_server.cpp index 1ad70a376ed..38771b8aed8 100644 --- a/intern/cycles/app/cycles_server.cpp +++ b/intern/cycles/app/cycles_server.cpp @@ -18,13 +18,13 @@ #include "device/device.h" -#include "util/util_args.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_stats.h" -#include "util/util_string.h" -#include "util/util_task.h" +#include "util/args.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/path.h" +#include "util/stats.h" +#include "util/string.h" +#include "util/task.h" using namespace ccl; diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp index 800227ccf48..0032938b116 100644 --- a/intern/cycles/app/cycles_standalone.cpp +++ b/intern/cycles/app/cycles_standalone.cpp @@ -23,24 +23,24 @@ #include "session/buffers.h" #include "session/session.h" -#include "util/util_args.h" -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_image.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_progress.h" -#include "util/util_string.h" -#include "util/util_time.h" -#include "util/util_transform.h" -#include "util/util_unique_ptr.h" -#include "util/util_version.h" +#include "util/args.h" +#include "util/foreach.h" +#include "util/function.h" +#include "util/image.h" +#include "util/log.h" +#include "util/path.h" +#include "util/progress.h" +#include "util/string.h" +#include "util/time.h" +#include "util/transform.h" +#include "util/unique_ptr.h" +#include "util/version.h" #include "app/cycles_xml.h" #include "app/oiio_output_driver.h" #ifdef WITH_CYCLES_STANDALONE_GUI -# include "util/util_view.h" +# include "util/view.h" #endif #include "app/cycles_xml.h" diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 1ced74b6136..6144d2c60a9 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -35,14 +35,14 @@ #include "scene/shader_graph.h" #include "scene/shader_nodes.h" -#include "subd/subd_patch.h" -#include "subd/subd_split.h" +#include "subd/patch.h" +#include "subd/split.h" -#include "util/util_foreach.h" -#include "util/util_path.h" -#include "util/util_projection.h" -#include "util/util_transform.h" -#include "util/util_xml.h" +#include "util/foreach.h" +#include "util/path.h" +#include "util/projection.h" +#include "util/transform.h" +#include "util/xml.h" #include "app/cycles_xml.h" diff --git a/intern/cycles/app/oiio_output_driver.h b/intern/cycles/app/oiio_output_driver.h index a6984938fe7..a5c88e0e890 100644 --- a/intern/cycles/app/oiio_output_driver.h +++ b/intern/cycles/app/oiio_output_driver.h @@ -16,11 +16,11 @@ #include "session/output_driver.h" -#include "util/util_function.h" -#include "util/util_image.h" -#include "util/util_string.h" -#include "util/util_unique_ptr.h" -#include "util/util_vector.h" +#include "util/function.h" +#include "util/image.h" +#include "util/string.h" +#include "util/unique_ptr.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index d948f2b3118..149967ad331 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -29,39 +29,39 @@ set(INC_SYS ) set(SRC - blender_camera.cpp - blender_device.cpp - blender_display_driver.cpp - blender_image.cpp - blender_geometry.cpp - blender_light.cpp - blender_mesh.cpp - blender_object.cpp - blender_object_cull.cpp - blender_output_driver.cpp - blender_particles.cpp - blender_curves.cpp - blender_logging.cpp - blender_python.cpp - blender_session.cpp - blender_shader.cpp - blender_sync.cpp - blender_texture.cpp - blender_viewport.cpp - blender_volume.cpp + camera.cpp + device.cpp + display_driver.cpp + image.cpp + geometry.cpp + light.cpp + mesh.cpp + object.cpp + object_cull.cpp + output_driver.cpp + particles.cpp + curves.cpp + logging.cpp + python.cpp + session.cpp + shader.cpp + sync.cpp + texture.cpp + viewport.cpp + volume.cpp CCL_api.h - blender_device.h - blender_display_driver.h - blender_id_map.h - blender_image.h - blender_object_cull.h - blender_output_driver.h - blender_sync.h - blender_session.h - blender_texture.h - blender_util.h - blender_viewport.h + device.h + display_driver.h + id_map.h + image.h + object_cull.h + output_driver.h + sync.h + session.h + texture.h + util.h + viewport.h ) set(LIB diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/camera.cpp similarity index 99% rename from intern/cycles/blender/blender_camera.cpp rename to intern/cycles/blender/camera.cpp index 670e25841f5..f87ebe39d21 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/camera.cpp @@ -17,10 +17,10 @@ #include "scene/camera.h" #include "scene/scene.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "util/util_logging.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/curves.cpp similarity index 99% rename from intern/cycles/blender/blender_curves.cpp rename to intern/cycles/blender/curves.cpp index 84333faaa23..fb2b329e61d 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/curves.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#include "blender/sync.h" +#include "blender/util.h" + #include "scene/attribute.h" #include "scene/camera.h" #include "scene/curves.h" @@ -21,13 +24,10 @@ #include "scene/object.h" #include "scene/scene.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" - -#include "util/util_color.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" +#include "util/color.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/device.cpp similarity index 96% rename from intern/cycles/blender/blender_device.cpp rename to intern/cycles/blender/device.cpp index 7bed33855c2..9fabc33a96b 100644 --- a/intern/cycles/blender/blender_device.cpp +++ b/intern/cycles/blender/device.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "blender/blender_device.h" -#include "blender/blender_session.h" -#include "blender/blender_util.h" +#include "blender/device.h" +#include "blender/session.h" +#include "blender/util.h" -#include "util/util_foreach.h" +#include "util/foreach.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_device.h b/intern/cycles/blender/device.h similarity index 100% rename from intern/cycles/blender/blender_device.h rename to intern/cycles/blender/device.h diff --git a/intern/cycles/blender/blender_display_driver.cpp b/intern/cycles/blender/display_driver.cpp similarity index 99% rename from intern/cycles/blender/blender_display_driver.cpp rename to intern/cycles/blender/display_driver.cpp index cdf175f91d0..d5f6d85251e 100644 --- a/intern/cycles/blender/blender_display_driver.cpp +++ b/intern/cycles/blender/display_driver.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "blender/blender_display_driver.h" +#include "blender/display_driver.h" #include "device/device.h" -#include "util/util_logging.h" -#include "util/util_opengl.h" +#include "util/log.h" +#include "util/opengl.h" extern "C" { struct RenderEngine; diff --git a/intern/cycles/blender/blender_display_driver.h b/intern/cycles/blender/display_driver.h similarity index 99% rename from intern/cycles/blender/blender_display_driver.h rename to intern/cycles/blender/display_driver.h index 800d0791041..66cfc8cffcc 100644 --- a/intern/cycles/blender/blender_display_driver.h +++ b/intern/cycles/blender/display_driver.h @@ -24,8 +24,8 @@ #include "session/display_driver.h" -#include "util/util_thread.h" -#include "util/util_unique_ptr.h" +#include "util/thread.h" +#include "util/unique_ptr.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/geometry.cpp similarity index 98% rename from intern/cycles/blender/blender_geometry.cpp rename to intern/cycles/blender/geometry.cpp index b4b0d04d104..479e76f68bc 100644 --- a/intern/cycles/blender/blender_geometry.cpp +++ b/intern/cycles/blender/geometry.cpp @@ -21,11 +21,11 @@ #include "scene/object.h" #include "scene/volume.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "util/util_foreach.h" -#include "util/util_task.h" +#include "util/foreach.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_id_map.h b/intern/cycles/blender/id_map.h similarity index 98% rename from intern/cycles/blender/blender_id_map.h rename to intern/cycles/blender/id_map.h index 27a53a90f12..c1b800026c3 100644 --- a/intern/cycles/blender/blender_id_map.h +++ b/intern/cycles/blender/id_map.h @@ -22,9 +22,9 @@ #include "scene/geometry.h" #include "scene/scene.h" -#include "util/util_map.h" -#include "util/util_set.h" -#include "util/util_vector.h" +#include "util/map.h" +#include "util/set.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_image.cpp b/intern/cycles/blender/image.cpp similarity index 98% rename from intern/cycles/blender/blender_image.cpp rename to intern/cycles/blender/image.cpp index f27275bd457..3ea3a47c1f4 100644 --- a/intern/cycles/blender/blender_image.cpp +++ b/intern/cycles/blender/image.cpp @@ -16,9 +16,9 @@ #include "MEM_guardedalloc.h" -#include "blender/blender_image.h" -#include "blender/blender_session.h" -#include "blender/blender_util.h" +#include "blender/image.h" +#include "blender/session.h" +#include "blender/util.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_image.h b/intern/cycles/blender/image.h similarity index 100% rename from intern/cycles/blender/blender_image.h rename to intern/cycles/blender/image.h diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/light.cpp similarity index 98% rename from intern/cycles/blender/blender_light.cpp rename to intern/cycles/blender/light.cpp index aa0c6a964e4..1e4cc0f1d14 100644 --- a/intern/cycles/blender/blender_light.cpp +++ b/intern/cycles/blender/light.cpp @@ -18,10 +18,10 @@ #include "scene/light.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "util/util_hash.h" +#include "util/hash.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_logging.cpp b/intern/cycles/blender/logging.cpp similarity index 96% rename from intern/cycles/blender/blender_logging.cpp rename to intern/cycles/blender/logging.cpp index b42a1f47821..613b4084aa8 100644 --- a/intern/cycles/blender/blender_logging.cpp +++ b/intern/cycles/blender/logging.cpp @@ -15,7 +15,7 @@ */ #include "blender/CCL_api.h" -#include "util/util_logging.h" +#include "util/log.h" void CCL_init_logging(const char *argv0) { diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/mesh.cpp similarity index 99% rename from intern/cycles/blender/blender_mesh.cpp rename to intern/cycles/blender/mesh.cpp index 992e17d6f79..b69bf88c213 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/mesh.cpp @@ -14,26 +14,26 @@ * limitations under the License. */ +#include "blender/session.h" +#include "blender/sync.h" +#include "blender/util.h" + #include "scene/camera.h" #include "scene/colorspace.h" #include "scene/mesh.h" #include "scene/object.h" #include "scene/scene.h" -#include "blender/blender_session.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "subd/patch.h" +#include "subd/split.h" -#include "subd/subd_patch.h" -#include "subd/subd_split.h" - -#include "util/util_algorithm.h" -#include "util/util_color.h" -#include "util/util_disjoint_set.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_math.h" +#include "util/algorithm.h" +#include "util/color.h" +#include "util/disjoint_set.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/math.h" #include "mikktspace.h" diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/object.cpp similarity index 99% rename from intern/cycles/blender/blender_object.cpp rename to intern/cycles/blender/object.cpp index 75311805fd8..9919b9d1836 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/object.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#include "blender/object_cull.h" +#include "blender/sync.h" +#include "blender/util.h" + #include "scene/alembic.h" #include "scene/camera.h" #include "scene/integrator.h" @@ -26,14 +30,10 @@ #include "scene/shader_graph.h" #include "scene/shader_nodes.h" -#include "blender/blender_object_cull.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" - -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_task.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_object_cull.cpp b/intern/cycles/blender/object_cull.cpp similarity index 98% rename from intern/cycles/blender/blender_object_cull.cpp rename to intern/cycles/blender/object_cull.cpp index 34cceb5a6e4..c2493be26dd 100644 --- a/intern/cycles/blender/blender_object_cull.cpp +++ b/intern/cycles/blender/object_cull.cpp @@ -18,8 +18,8 @@ #include "scene/camera.h" -#include "blender/blender_object_cull.h" -#include "blender/blender_util.h" +#include "blender/object_cull.h" +#include "blender/util.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_object_cull.h b/intern/cycles/blender/object_cull.h similarity index 95% rename from intern/cycles/blender/blender_object_cull.h rename to intern/cycles/blender/object_cull.h index 0879db4f802..be3068ef4e7 100644 --- a/intern/cycles/blender/blender_object_cull.h +++ b/intern/cycles/blender/object_cull.h @@ -17,8 +17,8 @@ #ifndef __BLENDER_OBJECT_CULL_H__ #define __BLENDER_OBJECT_CULL_H__ -#include "blender/blender_sync.h" -#include "util/util_types.h" +#include "blender/sync.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_output_driver.cpp b/intern/cycles/blender/output_driver.cpp similarity index 98% rename from intern/cycles/blender/blender_output_driver.cpp rename to intern/cycles/blender/output_driver.cpp index 2f2844d4820..2b3586af668 100644 --- a/intern/cycles/blender/blender_output_driver.cpp +++ b/intern/cycles/blender/output_driver.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "blender/blender_output_driver.h" +#include "blender/output_driver.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_output_driver.h b/intern/cycles/blender/output_driver.h similarity index 100% rename from intern/cycles/blender/blender_output_driver.h rename to intern/cycles/blender/output_driver.h diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/particles.cpp similarity index 96% rename from intern/cycles/blender/blender_particles.cpp rename to intern/cycles/blender/particles.cpp index f654998af62..3a2c1b0ecf9 100644 --- a/intern/cycles/blender/blender_particles.cpp +++ b/intern/cycles/blender/particles.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ +#include "scene/particles.h" #include "scene/mesh.h" #include "scene/object.h" -#include "scene/particles.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "util/util_foreach.h" +#include "util/foreach.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/python.cpp similarity index 98% rename from intern/cycles/blender/blender_python.cpp rename to intern/cycles/blender/python.cpp index 45e5394cf34..20bf6385999 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/python.cpp @@ -18,25 +18,25 @@ #include "blender/CCL_api.h" -#include "blender/blender_device.h" -#include "blender/blender_session.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/device.h" +#include "blender/session.h" +#include "blender/sync.h" +#include "blender/util.h" #include "session/denoising.h" #include "session/merge.h" -#include "util/util_debug.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_md5.h" -#include "util/util_opengl.h" -#include "util/util_openimagedenoise.h" -#include "util/util_path.h" -#include "util/util_string.h" -#include "util/util_task.h" -#include "util/util_tbb.h" -#include "util/util_types.h" +#include "util/debug.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/md5.h" +#include "util/opengl.h" +#include "util/openimagedenoise.h" +#include "util/path.h" +#include "util/string.h" +#include "util/task.h" +#include "util/tbb.h" +#include "util/types.h" #ifdef WITH_OSL # include "scene/osl.h" diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/session.cpp similarity index 98% rename from intern/cycles/blender/blender_session.cpp rename to intern/cycles/blender/session.cpp index 988a8159864..d9a2d3d3029 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/session.cpp @@ -31,22 +31,22 @@ #include "session/buffers.h" #include "session/session.h" -#include "util/util_algorithm.h" -#include "util/util_color.h" -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_murmurhash.h" -#include "util/util_path.h" -#include "util/util_progress.h" -#include "util/util_time.h" +#include "util/algorithm.h" +#include "util/color.h" +#include "util/foreach.h" +#include "util/function.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/murmurhash.h" +#include "util/path.h" +#include "util/progress.h" +#include "util/time.h" -#include "blender/blender_display_driver.h" -#include "blender/blender_output_driver.h" -#include "blender/blender_session.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/display_driver.h" +#include "blender/output_driver.h" +#include "blender/session.h" +#include "blender/sync.h" +#include "blender/util.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/session.h similarity index 98% rename from intern/cycles/blender/blender_session.h rename to intern/cycles/blender/session.h index 9bc685ec306..fa24b5f7467 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/session.h @@ -17,6 +17,8 @@ #ifndef __BLENDER_SESSION_H__ #define __BLENDER_SESSION_H__ +#include "MEM_guardedalloc.h" + #include "RNA_blender_cpp.h" #include "device/device.h" @@ -25,7 +27,7 @@ #include "scene/scene.h" #include "session/session.h" -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/shader.cpp similarity index 99% rename from intern/cycles/blender/blender_shader.cpp rename to intern/cycles/blender/shader.cpp index 8d3bbb520c8..0cd9052b47a 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -14,26 +14,26 @@ * limitations under the License. */ +#include "scene/shader.h" #include "scene/background.h" #include "scene/colorspace.h" #include "scene/integrator.h" #include "scene/light.h" #include "scene/osl.h" #include "scene/scene.h" -#include "scene/shader.h" #include "scene/shader_graph.h" #include "scene/shader_nodes.h" -#include "blender/blender_image.h" -#include "blender/blender_sync.h" -#include "blender/blender_texture.h" -#include "blender/blender_util.h" +#include "blender/image.h" +#include "blender/sync.h" +#include "blender/texture.h" +#include "blender/util.h" -#include "util/util_debug.h" -#include "util/util_foreach.h" -#include "util/util_set.h" -#include "util/util_string.h" -#include "util/util_task.h" +#include "util/debug.h" +#include "util/foreach.h" +#include "util/set.h" +#include "util/string.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/sync.cpp similarity index 98% rename from intern/cycles/blender/blender_sync.cpp rename to intern/cycles/blender/sync.cpp index 1725ea5ec93..73d3a4436b5 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -30,17 +30,17 @@ #include "device/device.h" -#include "blender/blender_device.h" -#include "blender/blender_session.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/device.h" +#include "blender/session.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "util/util_debug.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_opengl.h" -#include "util/util_openimagedenoise.h" +#include "util/debug.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/opengl.h" +#include "util/openimagedenoise.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/sync.h similarity index 97% rename from intern/cycles/blender/blender_sync.h rename to intern/cycles/blender/sync.h index 99e3f0bf02b..c2377406876 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/sync.h @@ -22,17 +22,17 @@ #include "RNA_blender_cpp.h" #include "RNA_types.h" -#include "blender/blender_id_map.h" -#include "blender/blender_util.h" -#include "blender/blender_viewport.h" +#include "blender/id_map.h" +#include "blender/util.h" +#include "blender/viewport.h" #include "scene/scene.h" #include "session/session.h" -#include "util/util_map.h" -#include "util/util_set.h" -#include "util/util_transform.h" -#include "util/util_vector.h" +#include "util/map.h" +#include "util/set.h" +#include "util/transform.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_texture.cpp b/intern/cycles/blender/texture.cpp similarity index 97% rename from intern/cycles/blender/blender_texture.cpp rename to intern/cycles/blender/texture.cpp index 0d593f2b385..43745bb8376 100644 --- a/intern/cycles/blender/blender_texture.cpp +++ b/intern/cycles/blender/texture.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "blender/blender_texture.h" +#include "blender/texture.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_texture.h b/intern/cycles/blender/texture.h similarity index 96% rename from intern/cycles/blender/blender_texture.h rename to intern/cycles/blender/texture.h index 8ab061aaed9..ead0c4e631b 100644 --- a/intern/cycles/blender/blender_texture.h +++ b/intern/cycles/blender/texture.h @@ -17,7 +17,7 @@ #ifndef __BLENDER_TEXTURE_H__ #define __BLENDER_TEXTURE_H__ -#include "blender/blender_sync.h" +#include "blender/sync.h" #include CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/util.h similarity index 98% rename from intern/cycles/blender/blender_util.h rename to intern/cycles/blender/util.h index a3dd2349525..33fd2c416c8 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/util.h @@ -19,14 +19,14 @@ #include "scene/mesh.h" -#include "util/util_algorithm.h" -#include "util/util_array.h" -#include "util/util_map.h" -#include "util/util_path.h" -#include "util/util_set.h" -#include "util/util_transform.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/algorithm.h" +#include "util/array.h" +#include "util/map.h" +#include "util/path.h" +#include "util/set.h" +#include "util/transform.h" +#include "util/types.h" +#include "util/vector.h" /* Hacks to hook into Blender API * todo: clean this up ... */ diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/viewport.cpp similarity index 97% rename from intern/cycles/blender/blender_viewport.cpp rename to intern/cycles/blender/viewport.cpp index b8deb77b621..2a6f7e3ecee 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/viewport.cpp @@ -14,11 +14,12 @@ * limitations under the License. */ -#include "blender_viewport.h" +#include "blender/viewport.h" +#include "blender/util.h" -#include "blender_util.h" #include "scene/pass.h" -#include "util/util_logging.h" + +#include "util/log.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/viewport.h similarity index 100% rename from intern/cycles/blender/blender_viewport.h rename to intern/cycles/blender/viewport.h diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/volume.cpp similarity index 99% rename from intern/cycles/blender/blender_volume.cpp rename to intern/cycles/blender/volume.cpp index 46083cb29dd..a41e15621a7 100644 --- a/intern/cycles/blender/blender_volume.cpp +++ b/intern/cycles/blender/volume.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ +#include "scene/volume.h" #include "scene/colorspace.h" #include "scene/image.h" #include "scene/image_vdb.h" #include "scene/object.h" -#include "scene/volume.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/sync.h" +#include "blender/util.h" #ifdef WITH_OPENVDB # include diff --git a/intern/cycles/bvh/CMakeLists.txt b/intern/cycles/bvh/CMakeLists.txt index f7e47a8764d..9edc30cf9c4 100644 --- a/intern/cycles/bvh/CMakeLists.txt +++ b/intern/cycles/bvh/CMakeLists.txt @@ -22,30 +22,30 @@ set(INC_SYS set(SRC bvh.cpp bvh2.cpp - bvh_binning.cpp - bvh_build.cpp - bvh_embree.cpp - bvh_multi.cpp - bvh_node.cpp - bvh_optix.cpp - bvh_sort.cpp - bvh_split.cpp - bvh_unaligned.cpp + binning.cpp + build.cpp + embree.cpp + multi.cpp + node.cpp + optix.cpp + sort.cpp + split.cpp + unaligned.cpp ) set(SRC_HEADERS bvh.h bvh2.h - bvh_binning.h - bvh_build.h - bvh_embree.h - bvh_multi.h - bvh_node.h - bvh_optix.h - bvh_params.h - bvh_sort.h - bvh_split.h - bvh_unaligned.h + binning.h + build.h + embree.h + multi.h + node.h + optix.h + params.h + sort.h + split.h + unaligned.h ) set(LIB diff --git a/intern/cycles/bvh/bvh_binning.cpp b/intern/cycles/bvh/binning.cpp similarity index 98% rename from intern/cycles/bvh/bvh_binning.cpp rename to intern/cycles/bvh/binning.cpp index 1cc38275d11..da591ef5cea 100644 --- a/intern/cycles/bvh/bvh_binning.cpp +++ b/intern/cycles/bvh/binning.cpp @@ -17,13 +17,13 @@ //#define __KERNEL_SSE__ -#include "bvh/bvh_binning.h" +#include "bvh/binning.h" #include -#include "util/util_algorithm.h" -#include "util/util_boundbox.h" -#include "util/util_types.h" +#include "util/algorithm.h" +#include "util/boundbox.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_binning.h b/intern/cycles/bvh/binning.h similarity index 97% rename from intern/cycles/bvh/bvh_binning.h rename to intern/cycles/bvh/binning.h index ae6dba2805d..876500ec540 100644 --- a/intern/cycles/bvh/bvh_binning.h +++ b/intern/cycles/bvh/binning.h @@ -18,10 +18,10 @@ #ifndef __BVH_BINNING_H__ #define __BVH_BINNING_H__ -#include "bvh/bvh_params.h" -#include "bvh/bvh_unaligned.h" +#include "bvh/params.h" +#include "bvh/unaligned.h" -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/build.cpp similarity index 99% rename from intern/cycles/bvh/bvh_build.cpp rename to intern/cycles/bvh/build.cpp index 9ae40b57bc3..3ce268dfb25 100644 --- a/intern/cycles/bvh/bvh_build.cpp +++ b/intern/cycles/bvh/build.cpp @@ -15,12 +15,12 @@ * limitations under the License. */ -#include "bvh/bvh_build.h" +#include "bvh/build.h" -#include "bvh/bvh_binning.h" -#include "bvh/bvh_node.h" -#include "bvh/bvh_params.h" -#include "bvh_split.h" +#include "bvh/binning.h" +#include "bvh/node.h" +#include "bvh/params.h" +#include "bvh/split.h" #include "scene/curves.h" #include "scene/hair.h" @@ -28,14 +28,14 @@ #include "scene/object.h" #include "scene/scene.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_queue.h" -#include "util/util_simd.h" -#include "util/util_stack_allocator.h" -#include "util/util_time.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/queue.h" +#include "util/simd.h" +#include "util/stack_allocator.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_build.h b/intern/cycles/bvh/build.h similarity index 96% rename from intern/cycles/bvh/bvh_build.h rename to intern/cycles/bvh/build.h index c35af083fbd..06b318f1ee0 100644 --- a/intern/cycles/bvh/bvh_build.h +++ b/intern/cycles/bvh/build.h @@ -20,12 +20,12 @@ #include -#include "bvh/bvh_params.h" -#include "bvh/bvh_unaligned.h" +#include "bvh/params.h" +#include "bvh/unaligned.h" -#include "util/util_array.h" -#include "util/util_task.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/task.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp index 050e090bddf..ae6655eb27b 100644 --- a/intern/cycles/bvh/bvh.cpp +++ b/intern/cycles/bvh/bvh.cpp @@ -18,12 +18,12 @@ #include "bvh/bvh.h" #include "bvh/bvh2.h" -#include "bvh/bvh_embree.h" -#include "bvh/bvh_multi.h" -#include "bvh/bvh_optix.h" +#include "bvh/embree.h" +#include "bvh/multi.h" +#include "bvh/optix.h" -#include "util/util_logging.h" -#include "util/util_progress.h" +#include "util/log.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h index b222dfb14ed..c1f55ee917e 100644 --- a/intern/cycles/bvh/bvh.h +++ b/intern/cycles/bvh/bvh.h @@ -18,10 +18,10 @@ #ifndef __BVH_H__ #define __BVH_H__ -#include "bvh/bvh_params.h" -#include "util/util_array.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "bvh/params.h" +#include "util/array.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh2.cpp b/intern/cycles/bvh/bvh2.cpp index 3b864859f31..04290602145 100644 --- a/intern/cycles/bvh/bvh2.cpp +++ b/intern/cycles/bvh/bvh2.cpp @@ -21,12 +21,12 @@ #include "scene/mesh.h" #include "scene/object.h" -#include "bvh/bvh_build.h" -#include "bvh/bvh_node.h" -#include "bvh/bvh_unaligned.h" +#include "bvh/build.h" +#include "bvh/node.h" +#include "bvh/unaligned.h" -#include "util/util_foreach.h" -#include "util/util_progress.h" +#include "util/foreach.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh2.h b/intern/cycles/bvh/bvh2.h index 1030a0f76c7..7937288f271 100644 --- a/intern/cycles/bvh/bvh2.h +++ b/intern/cycles/bvh/bvh2.h @@ -19,10 +19,10 @@ #define __BVH2_H__ #include "bvh/bvh.h" -#include "bvh/bvh_params.h" +#include "bvh/params.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/embree.cpp similarity index 98% rename from intern/cycles/bvh/bvh_embree.cpp rename to intern/cycles/bvh/embree.cpp index 59a72f27294..944a84ce0da 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/embree.cpp @@ -32,24 +32,24 @@ # include -# include "bvh/bvh_embree.h" +# include "bvh/embree.h" /* Kernel includes are necessary so that the filter function for Embree can access the packed BVH. */ -# include "kernel/bvh/bvh_embree.h" -# include "kernel/bvh/bvh_util.h" +# include "kernel/bvh/embree.h" +# include "kernel/bvh/util.h" # include "kernel/device/cpu/compat.h" # include "kernel/device/cpu/globals.h" -# include "kernel/sample/sample_lcg.h" +# include "kernel/sample/lcg.h" # include "scene/hair.h" # include "scene/mesh.h" # include "scene/object.h" -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_progress.h" -# include "util/util_stats.h" +# include "util/foreach.h" +# include "util/log.h" +# include "util/progress.h" +# include "util/stats.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_embree.h b/intern/cycles/bvh/embree.h similarity index 93% rename from intern/cycles/bvh/bvh_embree.h rename to intern/cycles/bvh/embree.h index 01636fbd1dc..746ca97b504 100644 --- a/intern/cycles/bvh/bvh_embree.h +++ b/intern/cycles/bvh/embree.h @@ -23,11 +23,11 @@ # include # include "bvh/bvh.h" -# include "bvh/bvh_params.h" +# include "bvh/params.h" -# include "util/util_thread.h" -# include "util/util_types.h" -# include "util/util_vector.h" +# include "util/thread.h" +# include "util/types.h" +# include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_multi.cpp b/intern/cycles/bvh/multi.cpp similarity index 94% rename from intern/cycles/bvh/bvh_multi.cpp rename to intern/cycles/bvh/multi.cpp index a9e771f20f1..db0ff5c7847 100644 --- a/intern/cycles/bvh/bvh_multi.cpp +++ b/intern/cycles/bvh/multi.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "bvh/bvh_multi.h" +#include "bvh/multi.h" -#include "util/util_foreach.h" +#include "util/foreach.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_multi.h b/intern/cycles/bvh/multi.h similarity index 97% rename from intern/cycles/bvh/bvh_multi.h rename to intern/cycles/bvh/multi.h index 840438c5d0c..88a459605c3 100644 --- a/intern/cycles/bvh/bvh_multi.h +++ b/intern/cycles/bvh/multi.h @@ -18,7 +18,7 @@ #define __BVH_MULTI_H__ #include "bvh/bvh.h" -#include "bvh/bvh_params.h" +#include "bvh/params.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_node.cpp b/intern/cycles/bvh/node.cpp similarity index 98% rename from intern/cycles/bvh/bvh_node.cpp rename to intern/cycles/bvh/node.cpp index 38b554acfbf..d3a665adfe7 100644 --- a/intern/cycles/bvh/bvh_node.cpp +++ b/intern/cycles/bvh/node.cpp @@ -15,12 +15,12 @@ * limitations under the License. */ -#include "bvh/bvh_node.h" +#include "bvh/node.h" +#include "bvh/build.h" #include "bvh/bvh.h" -#include "bvh/bvh_build.h" -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_node.h b/intern/cycles/bvh/node.h similarity index 98% rename from intern/cycles/bvh/bvh_node.h rename to intern/cycles/bvh/node.h index b3b5c43a394..d5de9e062fc 100644 --- a/intern/cycles/bvh/bvh_node.h +++ b/intern/cycles/bvh/node.h @@ -18,8 +18,8 @@ #ifndef __BVH_NODE_H__ #define __BVH_NODE_H__ -#include "util/util_boundbox.h" -#include "util/util_types.h" +#include "util/boundbox.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_optix.cpp b/intern/cycles/bvh/optix.cpp similarity index 97% rename from intern/cycles/bvh/bvh_optix.cpp rename to intern/cycles/bvh/optix.cpp index cd266f72f89..ebc3fa68b97 100644 --- a/intern/cycles/bvh/bvh_optix.cpp +++ b/intern/cycles/bvh/optix.cpp @@ -19,7 +19,7 @@ # include "device/device.h" -# include "bvh/bvh_optix.h" +# include "bvh/optix.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_optix.h b/intern/cycles/bvh/optix.h similarity index 94% rename from intern/cycles/bvh/bvh_optix.h rename to intern/cycles/bvh/optix.h index ba5d90471d1..037e54980bd 100644 --- a/intern/cycles/bvh/bvh_optix.h +++ b/intern/cycles/bvh/optix.h @@ -21,8 +21,9 @@ #ifdef WITH_OPTIX # include "bvh/bvh.h" -# include "bvh/bvh_params.h" -# include "device/device_memory.h" +# include "bvh/params.h" + +# include "device/memory.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_params.h b/intern/cycles/bvh/params.h similarity index 99% rename from intern/cycles/bvh/bvh_params.h rename to intern/cycles/bvh/params.h index 31b3971c110..8f185a2640f 100644 --- a/intern/cycles/bvh/bvh_params.h +++ b/intern/cycles/bvh/params.h @@ -18,9 +18,9 @@ #ifndef __BVH_PARAMS_H__ #define __BVH_PARAMS_H__ -#include "util/util_boundbox.h" +#include "util/boundbox.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_sort.cpp b/intern/cycles/bvh/sort.cpp similarity index 98% rename from intern/cycles/bvh/bvh_sort.cpp rename to intern/cycles/bvh/sort.cpp index b01785b547a..a9975ce6bb2 100644 --- a/intern/cycles/bvh/bvh_sort.cpp +++ b/intern/cycles/bvh/sort.cpp @@ -15,12 +15,12 @@ * limitations under the License. */ -#include "bvh/bvh_sort.h" +#include "bvh/sort.h" -#include "bvh/bvh_build.h" +#include "bvh/build.h" -#include "util/util_algorithm.h" -#include "util/util_task.h" +#include "util/algorithm.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_sort.h b/intern/cycles/bvh/sort.h similarity index 100% rename from intern/cycles/bvh/bvh_sort.h rename to intern/cycles/bvh/sort.h diff --git a/intern/cycles/bvh/bvh_split.cpp b/intern/cycles/bvh/split.cpp similarity index 99% rename from intern/cycles/bvh/bvh_split.cpp rename to intern/cycles/bvh/split.cpp index 0e7d36983e7..102c50e2979 100644 --- a/intern/cycles/bvh/bvh_split.cpp +++ b/intern/cycles/bvh/split.cpp @@ -15,16 +15,16 @@ * limitations under the License. */ -#include "bvh/bvh_split.h" +#include "bvh/split.h" -#include "bvh/bvh_build.h" -#include "bvh/bvh_sort.h" +#include "bvh/build.h" +#include "bvh/sort.h" #include "scene/hair.h" #include "scene/mesh.h" #include "scene/object.h" -#include "util/util_algorithm.h" +#include "util/algorithm.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_split.h b/intern/cycles/bvh/split.h similarity index 99% rename from intern/cycles/bvh/bvh_split.h rename to intern/cycles/bvh/split.h index 5582d90bf83..2650a500ea9 100644 --- a/intern/cycles/bvh/bvh_split.h +++ b/intern/cycles/bvh/split.h @@ -18,8 +18,8 @@ #ifndef __BVH_SPLIT_H__ #define __BVH_SPLIT_H__ -#include "bvh/bvh_build.h" -#include "bvh/bvh_params.h" +#include "bvh/build.h" +#include "bvh/params.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_unaligned.cpp b/intern/cycles/bvh/unaligned.cpp similarity index 97% rename from intern/cycles/bvh/bvh_unaligned.cpp rename to intern/cycles/bvh/unaligned.cpp index ce95aa7aa74..3c4a600fe58 100644 --- a/intern/cycles/bvh/bvh_unaligned.cpp +++ b/intern/cycles/bvh/unaligned.cpp @@ -14,16 +14,16 @@ * limitations under the License. */ -#include "bvh/bvh_unaligned.h" +#include "bvh/unaligned.h" #include "scene/hair.h" #include "scene/object.h" -#include "bvh/bvh_binning.h" -#include "bvh_params.h" +#include "bvh/binning.h" +#include "bvh/params.h" -#include "util/util_boundbox.h" -#include "util/util_transform.h" +#include "util/boundbox.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_unaligned.h b/intern/cycles/bvh/unaligned.h similarity index 98% rename from intern/cycles/bvh/bvh_unaligned.h rename to intern/cycles/bvh/unaligned.h index e8a9a25daa8..33e584ea8ed 100644 --- a/intern/cycles/bvh/bvh_unaligned.h +++ b/intern/cycles/bvh/unaligned.h @@ -17,7 +17,7 @@ #ifndef __BVH_UNALIGNED_H__ #define __BVH_UNALIGNED_H__ -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt index 6d33a6f107f..39de4bec799 100644 --- a/intern/cycles/device/CMakeLists.txt +++ b/intern/cycles/device/CMakeLists.txt @@ -45,11 +45,11 @@ endif() set(SRC device.cpp - device_denoise.cpp - device_graphics_interop.cpp - device_kernel.cpp - device_memory.cpp - device_queue.cpp + denoise.cpp + graphics_interop.cpp + kernel.cpp + memory.cpp + queue.cpp ) set(SRC_CPU @@ -116,11 +116,11 @@ set(SRC_OPTIX set(SRC_HEADERS device.h - device_denoise.h - device_graphics_interop.h - device_memory.h - device_kernel.h - device_queue.h + denoise.h + graphics_interop.h + memory.h + kernel.h + queue.h ) set(LIB diff --git a/intern/cycles/device/cpu/device.cpp b/intern/cycles/device/cpu/device.cpp index 68ca8e8bb22..f11b49ef65f 100644 --- a/intern/cycles/device/cpu/device.cpp +++ b/intern/cycles/device/cpu/device.cpp @@ -20,7 +20,7 @@ /* Used for `info.denoisers`. */ /* TODO(sergey): The denoisers are probably to be moved completely out of the device into their * own class. But until then keep API consistent with how it used to work before. */ -#include "util/util_openimagedenoise.h" +#include "util/openimagedenoise.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/cpu/device.h b/intern/cycles/device/cpu/device.h index 9cb2e80068d..c53bc338127 100644 --- a/intern/cycles/device/cpu/device.h +++ b/intern/cycles/device/cpu/device.h @@ -16,8 +16,8 @@ #pragma once -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp index 98d637b5f8a..dbad332f896 100644 --- a/intern/cycles/device/cpu/device_impl.cpp +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -22,7 +22,7 @@ /* So ImathMath is included before our kernel_cpu_compat. */ #ifdef WITH_OSL /* So no context pollution happens from indirectly included windows.h */ -# include "util/util_windows.h" +# include "util/windows.h" # include #endif @@ -39,27 +39,27 @@ #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" #include "kernel/device/cpu/kernel.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "kernel/osl/osl_shader.h" -#include "kernel/osl/osl_globals.h" +#include "kernel/osl/shader.h" +#include "kernel/osl/globals.h" // clang-format on -#include "bvh/bvh_embree.h" +#include "bvh/embree.h" #include "session/buffers.h" -#include "util/util_debug.h" -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_openimagedenoise.h" -#include "util/util_optimization.h" -#include "util/util_progress.h" -#include "util/util_system.h" -#include "util/util_task.h" -#include "util/util_thread.h" +#include "util/debug.h" +#include "util/foreach.h" +#include "util/function.h" +#include "util/log.h" +#include "util/map.h" +#include "util/openimagedenoise.h" +#include "util/optimization.h" +#include "util/progress.h" +#include "util/system.h" +#include "util/task.h" +#include "util/thread.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/cpu/device_impl.h b/intern/cycles/device/cpu/device_impl.h index 944c61e29f7..553728ccc3b 100644 --- a/intern/cycles/device/cpu/device_impl.h +++ b/intern/cycles/device/cpu/device_impl.h @@ -19,7 +19,7 @@ /* So ImathMath is included before our kernel_cpu_compat. */ #ifdef WITH_OSL /* So no context pollution happens from indirectly included windows.h */ -# include "util/util_windows.h" +# include "util/windows.h" # include #endif @@ -29,15 +29,15 @@ #include "device/cpu/kernel.h" #include "device/device.h" -#include "device/device_memory.h" +#include "device/memory.h" // clang-format off #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/kernel.h" #include "kernel/device/cpu/globals.h" -#include "kernel/osl/osl_shader.h" -#include "kernel/osl/osl_globals.h" +#include "kernel/osl/shader.h" +#include "kernel/osl/globals.h" // clang-format on CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/cpu/kernel.h b/intern/cycles/device/cpu/kernel.h index 5f9cb85389f..5beeaf148a1 100644 --- a/intern/cycles/device/cpu/kernel.h +++ b/intern/cycles/device/cpu/kernel.h @@ -17,7 +17,7 @@ #pragma once #include "device/cpu/kernel_function.h" -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/cpu/kernel_function.h b/intern/cycles/device/cpu/kernel_function.h index aa18720cc24..5ff55499d48 100644 --- a/intern/cycles/device/cpu/kernel_function.h +++ b/intern/cycles/device/cpu/kernel_function.h @@ -16,8 +16,8 @@ #pragma once -#include "util/util_debug.h" -#include "util/util_system.h" +#include "util/debug.h" +#include "util/system.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/cpu/kernel_thread_globals.cpp b/intern/cycles/device/cpu/kernel_thread_globals.cpp index 44735beb88d..739b6460318 100644 --- a/intern/cycles/device/cpu/kernel_thread_globals.cpp +++ b/intern/cycles/device/cpu/kernel_thread_globals.cpp @@ -17,11 +17,11 @@ #include "device/cpu/kernel_thread_globals.h" // clang-format off -#include "kernel/osl/osl_shader.h" -#include "kernel/osl/osl_globals.h" +#include "kernel/osl/shader.h" +#include "kernel/osl/globals.h" // clang-format on -#include "util/util_profiling.h" +#include "util/profiling.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/cuda/device.cpp b/intern/cycles/device/cuda/device.cpp index 84becd6d081..af2bdc6e29c 100644 --- a/intern/cycles/device/cuda/device.cpp +++ b/intern/cycles/device/cuda/device.cpp @@ -16,14 +16,14 @@ #include "device/cuda/device.h" -#include "util/util_logging.h" +#include "util/log.h" #ifdef WITH_CUDA # include "device/cuda/device_impl.h" # include "device/device.h" -# include "util/util_string.h" -# include "util/util_windows.h" +# include "util/string.h" +# include "util/windows.h" #endif /* WITH_CUDA */ CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/cuda/device.h b/intern/cycles/device/cuda/device.h index b0484904d1a..7142ad19857 100644 --- a/intern/cycles/device/cuda/device.h +++ b/intern/cycles/device/cuda/device.h @@ -16,8 +16,8 @@ #pragma once -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp index 40f407b4fb3..2f9a1394ad8 100644 --- a/intern/cycles/device/cuda/device_impl.cpp +++ b/intern/cycles/device/cuda/device_impl.cpp @@ -24,17 +24,17 @@ # include "device/cuda/device_impl.h" -# include "util/util_debug.h" -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_map.h" -# include "util/util_md5.h" -# include "util/util_path.h" -# include "util/util_string.h" -# include "util/util_system.h" -# include "util/util_time.h" -# include "util/util_types.h" -# include "util/util_windows.h" +# include "util/debug.h" +# include "util/foreach.h" +# include "util/log.h" +# include "util/map.h" +# include "util/md5.h" +# include "util/path.h" +# include "util/string.h" +# include "util/system.h" +# include "util/time.h" +# include "util/types.h" +# include "util/windows.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/cuda/device_impl.h b/intern/cycles/device/cuda/device_impl.h index c0316d18ba0..72d4108d1bf 100644 --- a/intern/cycles/device/cuda/device_impl.h +++ b/intern/cycles/device/cuda/device_impl.h @@ -21,7 +21,7 @@ # include "device/cuda/util.h" # include "device/device.h" -# include "util/util_map.h" +# include "util/map.h" # ifdef WITH_CUDA_DYNLOAD # include "cuew.h" diff --git a/intern/cycles/device/cuda/graphics_interop.h b/intern/cycles/device/cuda/graphics_interop.h index ec480f20c86..a00a837efea 100644 --- a/intern/cycles/device/cuda/graphics_interop.h +++ b/intern/cycles/device/cuda/graphics_interop.h @@ -16,7 +16,7 @@ #ifdef WITH_CUDA -# include "device/device_graphics_interop.h" +# include "device/graphics_interop.h" # ifdef WITH_CUDA_DYNLOAD # include "cuew.h" diff --git a/intern/cycles/device/cuda/kernel.h b/intern/cycles/device/cuda/kernel.h index b489547a350..e1650ea5b5b 100644 --- a/intern/cycles/device/cuda/kernel.h +++ b/intern/cycles/device/cuda/kernel.h @@ -18,7 +18,7 @@ #ifdef WITH_CUDA -# include "device/device_kernel.h" +# include "device/kernel.h" # ifdef WITH_CUDA_DYNLOAD # include "cuew.h" diff --git a/intern/cycles/device/cuda/queue.h b/intern/cycles/device/cuda/queue.h index 4d1995ed69e..28613cda071 100644 --- a/intern/cycles/device/cuda/queue.h +++ b/intern/cycles/device/cuda/queue.h @@ -18,9 +18,9 @@ #ifdef WITH_CUDA -# include "device/device_kernel.h" -# include "device/device_memory.h" -# include "device/device_queue.h" +# include "device/kernel.h" +# include "device/memory.h" +# include "device/queue.h" # include "device/cuda/util.h" diff --git a/intern/cycles/device/device_denoise.cpp b/intern/cycles/device/denoise.cpp similarity index 98% rename from intern/cycles/device/device_denoise.cpp rename to intern/cycles/device/denoise.cpp index aea7868f65d..c291a7a0adb 100644 --- a/intern/cycles/device/device_denoise.cpp +++ b/intern/cycles/device/denoise.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "device/device_denoise.h" +#include "device/denoise.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/device_denoise.h b/intern/cycles/device/denoise.h similarity index 99% rename from intern/cycles/device/device_denoise.h rename to intern/cycles/device/denoise.h index 4e09f1a1ba3..3f30506ae06 100644 --- a/intern/cycles/device/device_denoise.h +++ b/intern/cycles/device/denoise.h @@ -16,7 +16,7 @@ #pragma once -#include "device/device_memory.h" +#include "device/memory.h" #include "graph/node.h" #include "session/buffers.h" diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index 81574e8b184..5179f3bacdb 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -20,7 +20,7 @@ #include "bvh/bvh2.h" #include "device/device.h" -#include "device/device_queue.h" +#include "device/queue.h" #include "device/cpu/device.h" #include "device/cuda/device.h" @@ -29,15 +29,15 @@ #include "device/multi/device.h" #include "device/optix/device.h" -#include "util/util_foreach.h" -#include "util/util_half.h" -#include "util/util_logging.h" -#include "util/util_math.h" -#include "util/util_string.h" -#include "util/util_system.h" -#include "util/util_time.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/half.h" +#include "util/log.h" +#include "util/math.h" +#include "util/string.h" +#include "util/system.h" +#include "util/time.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index c73d74cdccc..3cb177adde7 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -19,21 +19,21 @@ #include -#include "bvh/bvh_params.h" +#include "bvh/params.h" -#include "device/device_denoise.h" -#include "device/device_memory.h" +#include "device/denoise.h" +#include "device/memory.h" -#include "util/util_function.h" -#include "util/util_list.h" -#include "util/util_logging.h" -#include "util/util_stats.h" -#include "util/util_string.h" -#include "util/util_texture.h" -#include "util/util_thread.h" -#include "util/util_types.h" -#include "util/util_unique_ptr.h" -#include "util/util_vector.h" +#include "util/function.h" +#include "util/list.h" +#include "util/log.h" +#include "util/stats.h" +#include "util/string.h" +#include "util/texture.h" +#include "util/thread.h" +#include "util/types.h" +#include "util/unique_ptr.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/dummy/device.cpp b/intern/cycles/device/dummy/device.cpp index e3cea272300..64f6b0eb58c 100644 --- a/intern/cycles/device/dummy/device.cpp +++ b/intern/cycles/device/dummy/device.cpp @@ -17,7 +17,7 @@ #include "device/dummy/device.h" #include "device/device.h" -#include "device/device_queue.h" +#include "device/queue.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/dummy/device.h b/intern/cycles/device/dummy/device.h index 832a9568129..c45eb036ca5 100644 --- a/intern/cycles/device/dummy/device.h +++ b/intern/cycles/device/dummy/device.h @@ -16,8 +16,8 @@ #pragma once -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/device_graphics_interop.cpp b/intern/cycles/device/graphics_interop.cpp similarity index 93% rename from intern/cycles/device/device_graphics_interop.cpp rename to intern/cycles/device/graphics_interop.cpp index a80a236759f..0b092711b61 100644 --- a/intern/cycles/device/device_graphics_interop.cpp +++ b/intern/cycles/device/graphics_interop.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "device/device_graphics_interop.h" +#include "device/graphics_interop.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/device_graphics_interop.h b/intern/cycles/device/graphics_interop.h similarity index 97% rename from intern/cycles/device/device_graphics_interop.h rename to intern/cycles/device/graphics_interop.h index e5c97fe5a1e..f1661146ddd 100644 --- a/intern/cycles/device/device_graphics_interop.h +++ b/intern/cycles/device/graphics_interop.h @@ -18,7 +18,7 @@ #include "session/display_driver.h" -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/hip/device.cpp b/intern/cycles/device/hip/device.cpp index 90028ac7f10..f71732d14bb 100644 --- a/intern/cycles/device/hip/device.cpp +++ b/intern/cycles/device/hip/device.cpp @@ -16,14 +16,14 @@ #include "device/hip/device.h" -#include "util/util_logging.h" +#include "util/log.h" #ifdef WITH_HIP # include "device/device.h" # include "device/hip/device_impl.h" -# include "util/util_string.h" -# include "util/util_windows.h" +# include "util/string.h" +# include "util/windows.h" #endif /* WITH_HIP */ CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/hip/device.h b/intern/cycles/device/hip/device.h index 965fd9e484b..cdbe364b2b3 100644 --- a/intern/cycles/device/hip/device.h +++ b/intern/cycles/device/hip/device.h @@ -16,8 +16,8 @@ #pragma once -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp index 9ffeb9724a1..31b7b07383b 100644 --- a/intern/cycles/device/hip/device_impl.cpp +++ b/intern/cycles/device/hip/device_impl.cpp @@ -24,18 +24,18 @@ # include "device/hip/device_impl.h" -# include "util/util_debug.h" -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_map.h" -# include "util/util_md5.h" -# include "util/util_opengl.h" -# include "util/util_path.h" -# include "util/util_string.h" -# include "util/util_system.h" -# include "util/util_time.h" -# include "util/util_types.h" -# include "util/util_windows.h" +# include "util/debug.h" +# include "util/foreach.h" +# include "util/log.h" +# include "util/map.h" +# include "util/md5.h" +# include "util/opengl.h" +# include "util/path.h" +# include "util/string.h" +# include "util/system.h" +# include "util/time.h" +# include "util/types.h" +# include "util/windows.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/hip/device_impl.h b/intern/cycles/device/hip/device_impl.h index 1d138ee9856..8d81291d15e 100644 --- a/intern/cycles/device/hip/device_impl.h +++ b/intern/cycles/device/hip/device_impl.h @@ -21,12 +21,12 @@ # include "device/hip/queue.h" # include "device/hip/util.h" -# include "util/util_map.h" +# include "util/map.h" # ifdef WITH_HIP_DYNLOAD # include "hipew.h" # else -# include "util/util_opengl.h" +# include "util/opengl.h" # endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/hip/graphics_interop.h b/intern/cycles/device/hip/graphics_interop.h index 2b2d287ff6c..8314405e670 100644 --- a/intern/cycles/device/hip/graphics_interop.h +++ b/intern/cycles/device/hip/graphics_interop.h @@ -16,7 +16,7 @@ #ifdef WITH_HIP -# include "device/device_graphics_interop.h" +# include "device/graphics_interop.h" # ifdef WITH_HIP_DYNLOAD # include "hipew.h" diff --git a/intern/cycles/device/hip/kernel.h b/intern/cycles/device/hip/kernel.h index 3301731f56e..f1378f8eebf 100644 --- a/intern/cycles/device/hip/kernel.h +++ b/intern/cycles/device/hip/kernel.h @@ -18,7 +18,7 @@ #ifdef WITH_HIP -# include "device/device_kernel.h" +# include "device/kernel.h" # ifdef WITH_HIP_DYNLOAD # include "hipew.h" diff --git a/intern/cycles/device/hip/queue.h b/intern/cycles/device/hip/queue.h index b92f7de7e4b..95d1afaff0f 100644 --- a/intern/cycles/device/hip/queue.h +++ b/intern/cycles/device/hip/queue.h @@ -18,9 +18,9 @@ #ifdef WITH_HIP -# include "device/device_kernel.h" -# include "device/device_memory.h" -# include "device/device_queue.h" +# include "device/kernel.h" +# include "device/memory.h" +# include "device/queue.h" # include "device/hip/util.h" diff --git a/intern/cycles/device/device_kernel.cpp b/intern/cycles/device/kernel.cpp similarity index 98% rename from intern/cycles/device/device_kernel.cpp rename to intern/cycles/device/kernel.cpp index 1e282aac57e..1e4f0c48f18 100644 --- a/intern/cycles/device/device_kernel.cpp +++ b/intern/cycles/device/kernel.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "device/device_kernel.h" +#include "device/kernel.h" -#include "util/util_logging.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/device_kernel.h b/intern/cycles/device/kernel.h similarity index 93% rename from intern/cycles/device/device_kernel.h rename to intern/cycles/device/kernel.h index 83d959ca87b..780ead2d28a 100644 --- a/intern/cycles/device/device_kernel.h +++ b/intern/cycles/device/kernel.h @@ -16,9 +16,9 @@ #pragma once -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "util/util_string.h" +#include "util/string.h" #include // NOLINT diff --git a/intern/cycles/device/device_memory.cpp b/intern/cycles/device/memory.cpp similarity index 99% rename from intern/cycles/device/device_memory.cpp rename to intern/cycles/device/memory.cpp index c0ab2e17cae..f162b00d9f7 100644 --- a/intern/cycles/device/device_memory.cpp +++ b/intern/cycles/device/memory.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "device/device_memory.h" +#include "device/memory.h" #include "device/device.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/memory.h similarity index 98% rename from intern/cycles/device/device_memory.h rename to intern/cycles/device/memory.h index be6123e09b2..281c54cc6a5 100644 --- a/intern/cycles/device/device_memory.h +++ b/intern/cycles/device/memory.h @@ -21,12 +21,12 @@ * * Data types for allocating, copying and freeing device memory. */ -#include "util/util_array.h" -#include "util/util_half.h" -#include "util/util_string.h" -#include "util/util_texture.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/half.h" +#include "util/string.h" +#include "util/texture.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/multi/device.cpp b/intern/cycles/device/multi/device.cpp index 330a1ed06ef..56efec3e131 100644 --- a/intern/cycles/device/multi/device.cpp +++ b/intern/cycles/device/multi/device.cpp @@ -19,18 +19,18 @@ #include #include -#include "bvh/bvh_multi.h" +#include "bvh/multi.h" #include "device/device.h" -#include "device/device_queue.h" +#include "device/queue.h" #include "scene/geometry.h" -#include "util/util_foreach.h" -#include "util/util_list.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_time.h" +#include "util/foreach.h" +#include "util/list.h" +#include "util/log.h" +#include "util/map.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/multi/device.h b/intern/cycles/device/multi/device.h index 6e121014a1f..ac77f6574ef 100644 --- a/intern/cycles/device/multi/device.h +++ b/intern/cycles/device/multi/device.h @@ -16,8 +16,8 @@ #pragma once -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/optix/device.cpp b/intern/cycles/device/optix/device.cpp index 13f23bd229a..a00169e719f 100644 --- a/intern/cycles/device/optix/device.cpp +++ b/intern/cycles/device/optix/device.cpp @@ -19,7 +19,8 @@ #include "device/cuda/device.h" #include "device/optix/device_impl.h" -#include "util/util_logging.h" + +#include "util/log.h" #ifdef WITH_OPTIX # include diff --git a/intern/cycles/device/optix/device.h b/intern/cycles/device/optix/device.h index 29fa729c2e4..dd60a7aa6e2 100644 --- a/intern/cycles/device/optix/device.h +++ b/intern/cycles/device/optix/device.h @@ -16,8 +16,8 @@ #pragma once -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index 55c3fbd88f2..e9164cc0a76 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -20,7 +20,7 @@ # include "device/optix/device_impl.h" # include "bvh/bvh.h" -# include "bvh/bvh_optix.h" +# include "bvh/optix.h" # include "integrator/pass_accessor_gpu.h" @@ -30,12 +30,12 @@ # include "scene/pass.h" # include "scene/scene.h" -# include "util/util_debug.h" -# include "util/util_logging.h" -# include "util/util_md5.h" -# include "util/util_path.h" -# include "util/util_progress.h" -# include "util/util_time.h" +# include "util/debug.h" +# include "util/log.h" +# include "util/md5.h" +# include "util/path.h" +# include "util/progress.h" +# include "util/time.h" # undef __KERNEL_CPU__ # define __KERNEL_OPTIX__ @@ -1574,7 +1574,7 @@ void OptiXDevice::const_copy_to(const char *name, void *host, size_t size) return; \ } KERNEL_TEX(IntegratorStateGPU, __integrator_state) -# include "kernel/kernel_textures.h" +# include "kernel/textures.h" # undef KERNEL_TEX } diff --git a/intern/cycles/device/optix/device_impl.h b/intern/cycles/device/optix/device_impl.h index b20d42f8c61..3ec98098eb7 100644 --- a/intern/cycles/device/optix/device_impl.h +++ b/intern/cycles/device/optix/device_impl.h @@ -22,7 +22,7 @@ # include "device/cuda/device_impl.h" # include "device/optix/queue.h" # include "device/optix/util.h" -# include "kernel/kernel_types.h" +# include "kernel/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/optix/queue.cpp b/intern/cycles/device/optix/queue.cpp index 458ed70baa8..f5bfd916ccf 100644 --- a/intern/cycles/device/optix/queue.cpp +++ b/intern/cycles/device/optix/queue.cpp @@ -19,7 +19,7 @@ # include "device/optix/queue.h" # include "device/optix/device_impl.h" -# include "util/util_time.h" +# include "util/time.h" # undef __KERNEL_CPU__ # define __KERNEL_OPTIX__ diff --git a/intern/cycles/device/device_queue.cpp b/intern/cycles/device/queue.cpp similarity index 95% rename from intern/cycles/device/device_queue.cpp rename to intern/cycles/device/queue.cpp index f2b2f3496e0..556dc97f23b 100644 --- a/intern/cycles/device/device_queue.cpp +++ b/intern/cycles/device/queue.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "device/device_queue.h" +#include "device/queue.h" -#include "util/util_algorithm.h" -#include "util/util_logging.h" -#include "util/util_time.h" +#include "util/algorithm.h" +#include "util/log.h" +#include "util/time.h" #include diff --git a/intern/cycles/device/device_queue.h b/intern/cycles/device/queue.h similarity index 95% rename from intern/cycles/device/device_queue.h rename to intern/cycles/device/queue.h index e6835b787cf..188162f4b74 100644 --- a/intern/cycles/device/device_queue.h +++ b/intern/cycles/device/queue.h @@ -16,13 +16,13 @@ #pragma once -#include "device/device_kernel.h" +#include "device/kernel.h" -#include "device/device_graphics_interop.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_string.h" -#include "util/util_unique_ptr.h" +#include "device/graphics_interop.h" +#include "util/log.h" +#include "util/map.h" +#include "util/string.h" +#include "util/unique_ptr.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node.cpp b/intern/cycles/graph/node.cpp index 8294e716ebe..a3d75e53afd 100644 --- a/intern/cycles/graph/node.cpp +++ b/intern/cycles/graph/node.cpp @@ -17,10 +17,10 @@ #include "graph/node.h" #include "graph/node_type.h" -#include "util/util_foreach.h" -#include "util/util_md5.h" -#include "util/util_param.h" -#include "util/util_transform.h" +#include "util/foreach.h" +#include "util/md5.h" +#include "util/param.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node.h b/intern/cycles/graph/node.h index 8f27a82d37b..a00162a3b9a 100644 --- a/intern/cycles/graph/node.h +++ b/intern/cycles/graph/node.h @@ -20,9 +20,9 @@ #include "graph/node_type.h" -#include "util/util_array.h" -#include "util/util_map.h" -#include "util/util_param.h" +#include "util/array.h" +#include "util/map.h" +#include "util/param.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node_enum.h b/intern/cycles/graph/node_enum.h index d3ed0928a4f..831c6e4a9c4 100644 --- a/intern/cycles/graph/node_enum.h +++ b/intern/cycles/graph/node_enum.h @@ -16,8 +16,8 @@ #pragma once -#include "util/util_map.h" -#include "util/util_param.h" +#include "util/map.h" +#include "util/param.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node_type.cpp b/intern/cycles/graph/node_type.cpp index 4efbd6725ee..bce98c694e1 100644 --- a/intern/cycles/graph/node_type.cpp +++ b/intern/cycles/graph/node_type.cpp @@ -15,8 +15,8 @@ */ #include "graph/node_type.h" -#include "util/util_foreach.h" -#include "util/util_transform.h" +#include "util/foreach.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node_type.h b/intern/cycles/graph/node_type.h index 8b37398fa17..71639341617 100644 --- a/intern/cycles/graph/node_type.h +++ b/intern/cycles/graph/node_type.h @@ -17,11 +17,11 @@ #pragma once #include "graph/node_enum.h" -#include "util/util_array.h" -#include "util/util_map.h" -#include "util/util_param.h" -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/map.h" +#include "util/param.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node_xml.cpp b/intern/cycles/graph/node_xml.cpp index 43462662b6a..b0c863ad4b5 100644 --- a/intern/cycles/graph/node_xml.cpp +++ b/intern/cycles/graph/node_xml.cpp @@ -16,9 +16,9 @@ #include "graph/node_xml.h" -#include "util/util_foreach.h" -#include "util/util_string.h" -#include "util/util_transform.h" +#include "util/foreach.h" +#include "util/string.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node_xml.h b/intern/cycles/graph/node_xml.h index 15bbf5d5621..ddbc5213ab1 100644 --- a/intern/cycles/graph/node_xml.h +++ b/intern/cycles/graph/node_xml.h @@ -18,9 +18,9 @@ #include "graph/node.h" -#include "util/util_map.h" -#include "util/util_string.h" -#include "util/util_xml.h" +#include "util/map.h" +#include "util/string.h" +#include "util/xml.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/adaptive_sampling.cpp b/intern/cycles/integrator/adaptive_sampling.cpp index 23fbcfea5c2..253879d67e3 100644 --- a/intern/cycles/integrator/adaptive_sampling.cpp +++ b/intern/cycles/integrator/adaptive_sampling.cpp @@ -16,7 +16,7 @@ #include "integrator/adaptive_sampling.h" -#include "util/util_math.h" +#include "util/math.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp index 45f967b38eb..b6ca96faebf 100644 --- a/intern/cycles/integrator/denoiser.cpp +++ b/intern/cycles/integrator/denoiser.cpp @@ -20,8 +20,8 @@ #include "integrator/denoiser_oidn.h" #include "integrator/denoiser_optix.h" #include "session/buffers.h" -#include "util/util_logging.h" -#include "util/util_progress.h" +#include "util/log.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/denoiser.h b/intern/cycles/integrator/denoiser.h index b02bcbeb046..8d7a644f8d9 100644 --- a/intern/cycles/integrator/denoiser.h +++ b/intern/cycles/integrator/denoiser.h @@ -19,10 +19,10 @@ /* TODO(sergey): The integrator folder might not be the best. Is easy to move files around if the * better place is figured out. */ +#include "device/denoise.h" #include "device/device.h" -#include "device/device_denoise.h" -#include "util/util_function.h" -#include "util/util_unique_ptr.h" +#include "util/function.h" +#include "util/unique_ptr.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/denoiser_device.cpp b/intern/cycles/integrator/denoiser_device.cpp index 1afd8d46866..2121dee500a 100644 --- a/intern/cycles/integrator/denoiser_device.cpp +++ b/intern/cycles/integrator/denoiser_device.cpp @@ -16,13 +16,13 @@ #include "integrator/denoiser_device.h" +#include "device/denoise.h" #include "device/device.h" -#include "device/device_denoise.h" -#include "device/device_memory.h" -#include "device/device_queue.h" +#include "device/memory.h" +#include "device/queue.h" #include "session/buffers.h" -#include "util/util_logging.h" -#include "util/util_progress.h" +#include "util/log.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/denoiser_device.h b/intern/cycles/integrator/denoiser_device.h index 0fd934dba79..2bacecaa2a2 100644 --- a/intern/cycles/integrator/denoiser_device.h +++ b/intern/cycles/integrator/denoiser_device.h @@ -17,7 +17,7 @@ #pragma once #include "integrator/denoiser.h" -#include "util/util_unique_ptr.h" +#include "util/unique_ptr.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp index c3555008699..b09b95a11b0 100644 --- a/intern/cycles/integrator/denoiser_oidn.cpp +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -19,12 +19,12 @@ #include #include "device/device.h" -#include "device/device_queue.h" +#include "device/queue.h" #include "integrator/pass_accessor_cpu.h" #include "session/buffers.h" -#include "util/util_array.h" -#include "util/util_logging.h" -#include "util/util_openimagedenoise.h" +#include "util/array.h" +#include "util/log.h" +#include "util/openimagedenoise.h" #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/kernel.h" diff --git a/intern/cycles/integrator/denoiser_oidn.h b/intern/cycles/integrator/denoiser_oidn.h index 566e761ae79..a0ec3e26b9c 100644 --- a/intern/cycles/integrator/denoiser_oidn.h +++ b/intern/cycles/integrator/denoiser_oidn.h @@ -17,8 +17,8 @@ #pragma once #include "integrator/denoiser.h" -#include "util/util_thread.h" -#include "util/util_unique_ptr.h" +#include "util/thread.h" +#include "util/unique_ptr.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/denoiser_optix.cpp b/intern/cycles/integrator/denoiser_optix.cpp index 5f9de23bfe6..ebd95d62ae4 100644 --- a/intern/cycles/integrator/denoiser_optix.cpp +++ b/intern/cycles/integrator/denoiser_optix.cpp @@ -16,8 +16,8 @@ #include "integrator/denoiser_optix.h" +#include "device/denoise.h" #include "device/device.h" -#include "device/device_denoise.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/pass_accessor.cpp b/intern/cycles/integrator/pass_accessor.cpp index 0a8c445eca7..7e19de51daa 100644 --- a/intern/cycles/integrator/pass_accessor.cpp +++ b/intern/cycles/integrator/pass_accessor.cpp @@ -17,11 +17,11 @@ #include "integrator/pass_accessor.h" #include "session/buffers.h" -#include "util/util_logging.h" +#include "util/log.h" // clang-format off #include "kernel/device/cpu/compat.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" // clang-format on CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/pass_accessor.h b/intern/cycles/integrator/pass_accessor.h index c9cf3ba8956..09eae0156c9 100644 --- a/intern/cycles/integrator/pass_accessor.h +++ b/intern/cycles/integrator/pass_accessor.h @@ -17,9 +17,9 @@ #pragma once #include "scene/pass.h" -#include "util/util_half.h" -#include "util/util_string.h" -#include "util/util_types.h" +#include "util/half.h" +#include "util/string.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/pass_accessor_cpu.cpp b/intern/cycles/integrator/pass_accessor_cpu.cpp index f3ca38c667d..820da757be0 100644 --- a/intern/cycles/integrator/pass_accessor_cpu.cpp +++ b/intern/cycles/integrator/pass_accessor_cpu.cpp @@ -17,14 +17,14 @@ #include "integrator/pass_accessor_cpu.h" #include "session/buffers.h" -#include "util/util_logging.h" -#include "util/util_tbb.h" +#include "util/log.h" +#include "util/tbb.h" // clang-format off #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" -#include "kernel/kernel_types.h" -#include "kernel/film/film_read.h" +#include "kernel/types.h" +#include "kernel/film/read.h" // clang-format on CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/pass_accessor_gpu.cpp b/intern/cycles/integrator/pass_accessor_gpu.cpp index 3b4290a89e6..c03ef64a2b2 100644 --- a/intern/cycles/integrator/pass_accessor_gpu.cpp +++ b/intern/cycles/integrator/pass_accessor_gpu.cpp @@ -16,9 +16,9 @@ #include "integrator/pass_accessor_gpu.h" -#include "device/device_queue.h" +#include "device/queue.h" #include "session/buffers.h" -#include "util/util_logging.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/pass_accessor_gpu.h b/intern/cycles/integrator/pass_accessor_gpu.h index bc37e4387f3..f3442d90013 100644 --- a/intern/cycles/integrator/pass_accessor_gpu.h +++ b/intern/cycles/integrator/pass_accessor_gpu.h @@ -17,7 +17,7 @@ #pragma once #include "integrator/pass_accessor.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 94687f7bc95..daf270d6686 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -25,11 +25,11 @@ #include "scene/pass.h" #include "scene/scene.h" #include "session/tile.h" -#include "util/util_algorithm.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_tbb.h" -#include "util/util_time.h" +#include "util/algorithm.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/tbb.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h index 89fa5fa8eaf..9b079352a63 100644 --- a/intern/cycles/integrator/path_trace.h +++ b/intern/cycles/integrator/path_trace.h @@ -21,10 +21,10 @@ #include "integrator/path_trace_work.h" #include "integrator/work_balancer.h" #include "session/buffers.h" -#include "util/util_function.h" -#include "util/util_thread.h" -#include "util/util_unique_ptr.h" -#include "util/util_vector.h" +#include "util/function.h" +#include "util/thread.h" +#include "util/unique_ptr.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace_display.cpp b/intern/cycles/integrator/path_trace_display.cpp index 7455a107ae6..c1cade923b1 100644 --- a/intern/cycles/integrator/path_trace_display.cpp +++ b/intern/cycles/integrator/path_trace_display.cpp @@ -18,7 +18,7 @@ #include "session/buffers.h" -#include "util/util_logging.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace_display.h b/intern/cycles/integrator/path_trace_display.h index d40e45e7ead..b69ee85fbbc 100644 --- a/intern/cycles/integrator/path_trace_display.h +++ b/intern/cycles/integrator/path_trace_display.h @@ -18,10 +18,10 @@ #include "session/display_driver.h" -#include "util/util_half.h" -#include "util/util_thread.h" -#include "util/util_types.h" -#include "util/util_unique_ptr.h" +#include "util/half.h" +#include "util/thread.h" +#include "util/types.h" +#include "util/unique_ptr.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp index e08c410a579..b0c40cfe15c 100644 --- a/intern/cycles/integrator/path_trace_work.cpp +++ b/intern/cycles/integrator/path_trace_work.cpp @@ -24,7 +24,7 @@ #include "scene/scene.h" #include "session/buffers.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace_work.h b/intern/cycles/integrator/path_trace_work.h index 6b2a6c71e81..0dc7cd2f896 100644 --- a/intern/cycles/integrator/path_trace_work.h +++ b/intern/cycles/integrator/path_trace_work.h @@ -19,8 +19,8 @@ #include "integrator/pass_accessor.h" #include "scene/pass.h" #include "session/buffers.h" -#include "util/util_types.h" -#include "util/util_unique_ptr.h" +#include "util/types.h" +#include "util/unique_ptr.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp index 1e59682a64c..541a7eca02f 100644 --- a/intern/cycles/integrator/path_trace_work_cpu.cpp +++ b/intern/cycles/integrator/path_trace_work_cpu.cpp @@ -19,7 +19,7 @@ #include "device/cpu/kernel.h" #include "device/device.h" -#include "kernel/integrator/integrator_path_state.h" +#include "kernel/integrator/path_state.h" #include "integrator/pass_accessor_cpu.h" #include "integrator/path_trace_display.h" @@ -27,9 +27,9 @@ #include "scene/scene.h" #include "session/buffers.h" -#include "util/util_atomic.h" -#include "util/util_logging.h" -#include "util/util_tbb.h" +#include "util/atomic.h" +#include "util/log.h" +#include "util/tbb.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace_work_cpu.h b/intern/cycles/integrator/path_trace_work_cpu.h index 91c024f4e4a..6e734690811 100644 --- a/intern/cycles/integrator/path_trace_work_cpu.h +++ b/intern/cycles/integrator/path_trace_work_cpu.h @@ -16,14 +16,14 @@ #pragma once -#include "kernel/integrator/integrator_state.h" +#include "kernel/integrator/state.h" #include "device/cpu/kernel_thread_globals.h" -#include "device/device_queue.h" +#include "device/queue.h" #include "integrator/path_trace_work.h" -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 67e5ae70316..b7dc4e5d181 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -22,12 +22,12 @@ #include "integrator/pass_accessor_gpu.h" #include "scene/scene.h" #include "session/buffers.h" -#include "util/util_logging.h" -#include "util/util_string.h" -#include "util/util_tbb.h" -#include "util/util_time.h" +#include "util/log.h" +#include "util/string.h" +#include "util/tbb.h" +#include "util/time.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" CCL_NAMESPACE_BEGIN @@ -53,9 +53,9 @@ static size_t estimate_single_state_size() * rely on this. */ #define KERNEL_STRUCT_VOLUME_STACK_SIZE 4 -#include "kernel/integrator/integrator_state_template.h" +#include "kernel/integrator/state_template.h" -#include "kernel/integrator/integrator_shadow_state_template.h" +#include "kernel/integrator/shadow_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER @@ -146,9 +146,9 @@ void PathTraceWorkGPU::alloc_integrator_soa() } #define KERNEL_STRUCT_VOLUME_STACK_SIZE (integrator_state_soa_volume_stack_size_) -#include "kernel/integrator/integrator_state_template.h" +#include "kernel/integrator/state_template.h" -#include "kernel/integrator/integrator_shadow_state_template.h" +#include "kernel/integrator/shadow_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h index 8734d2c2852..c5e291e72db 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.h +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -16,16 +16,16 @@ #pragma once -#include "kernel/integrator/integrator_state.h" +#include "kernel/integrator/state.h" -#include "device/device_graphics_interop.h" -#include "device/device_memory.h" -#include "device/device_queue.h" +#include "device/graphics_interop.h" +#include "device/memory.h" +#include "device/queue.h" #include "integrator/path_trace_work.h" #include "integrator/work_tile_scheduler.h" -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp index 3c64cba5feb..f776d01ef67 100644 --- a/intern/cycles/integrator/render_scheduler.cpp +++ b/intern/cycles/integrator/render_scheduler.cpp @@ -18,9 +18,9 @@ #include "session/session.h" #include "session/tile.h" -#include "util/util_logging.h" -#include "util/util_math.h" -#include "util/util_time.h" +#include "util/log.h" +#include "util/math.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h index a2c1e75d3b6..d7b7413ae31 100644 --- a/intern/cycles/integrator/render_scheduler.h +++ b/intern/cycles/integrator/render_scheduler.h @@ -19,7 +19,7 @@ #include "integrator/adaptive_sampling.h" #include "integrator/denoiser.h" /* For DenoiseParams. */ #include "session/buffers.h" -#include "util/util_string.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp index 3de7bb6fd16..42cbf87f254 100644 --- a/intern/cycles/integrator/shader_eval.cpp +++ b/intern/cycles/integrator/shader_eval.cpp @@ -17,14 +17,14 @@ #include "integrator/shader_eval.h" #include "device/device.h" -#include "device/device_queue.h" +#include "device/queue.h" #include "device/cpu/kernel.h" #include "device/cpu/kernel_thread_globals.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_tbb.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/tbb.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/shader_eval.h b/intern/cycles/integrator/shader_eval.h index 43b6b1bdd47..3ae63b84d04 100644 --- a/intern/cycles/integrator/shader_eval.h +++ b/intern/cycles/integrator/shader_eval.h @@ -16,11 +16,11 @@ #pragma once -#include "device/device_memory.h" +#include "device/memory.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "util/util_function.h" +#include "util/function.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/tile.cpp b/intern/cycles/integrator/tile.cpp index 3387b7bedf1..7ea73451d80 100644 --- a/intern/cycles/integrator/tile.cpp +++ b/intern/cycles/integrator/tile.cpp @@ -16,8 +16,8 @@ #include "integrator/tile.h" -#include "util/util_logging.h" -#include "util/util_math.h" +#include "util/log.h" +#include "util/math.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/tile.h b/intern/cycles/integrator/tile.h index d0824843ddb..879c68b875c 100644 --- a/intern/cycles/integrator/tile.h +++ b/intern/cycles/integrator/tile.h @@ -18,7 +18,7 @@ #include -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/work_balancer.cpp b/intern/cycles/integrator/work_balancer.cpp index 9f96fe3632b..4c6fa341f35 100644 --- a/intern/cycles/integrator/work_balancer.cpp +++ b/intern/cycles/integrator/work_balancer.cpp @@ -16,9 +16,9 @@ #include "integrator/work_balancer.h" -#include "util/util_math.h" +#include "util/math.h" -#include "util/util_logging.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/work_balancer.h b/intern/cycles/integrator/work_balancer.h index fc5e561845e..86ff9335f91 100644 --- a/intern/cycles/integrator/work_balancer.h +++ b/intern/cycles/integrator/work_balancer.h @@ -16,7 +16,7 @@ #pragma once -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/work_tile_scheduler.cpp b/intern/cycles/integrator/work_tile_scheduler.cpp index a6775f27b65..c874dffde91 100644 --- a/intern/cycles/integrator/work_tile_scheduler.cpp +++ b/intern/cycles/integrator/work_tile_scheduler.cpp @@ -16,11 +16,11 @@ #include "integrator/work_tile_scheduler.h" -#include "device/device_queue.h" +#include "device/queue.h" #include "integrator/tile.h" #include "session/buffers.h" -#include "util/util_atomic.h" -#include "util/util_logging.h" +#include "util/atomic.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/work_tile_scheduler.h b/intern/cycles/integrator/work_tile_scheduler.h index 85f11b601c7..155bba5cb68 100644 --- a/intern/cycles/integrator/work_tile_scheduler.h +++ b/intern/cycles/integrator/work_tile_scheduler.h @@ -17,7 +17,7 @@ #pragma once #include "integrator/tile.h" -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index f27bcb41d3d..6d57eff3d22 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -47,41 +47,41 @@ set(SRC_DEVICE_OPTIX set(SRC_HEADERS bake/bake.h bvh/bvh.h - bvh/bvh_nodes.h - bvh/bvh_shadow_all.h - bvh/bvh_local.h - bvh/bvh_traversal.h - bvh/bvh_types.h - bvh/bvh_util.h - bvh/bvh_volume.h - bvh/bvh_volume_all.h - bvh/bvh_embree.h + bvh/nodes.h + bvh/shadow_all.h + bvh/local.h + bvh/traversal.h + bvh/types.h + bvh/util.h + bvh/volume.h + bvh/volume_all.h + bvh/embree.h camera/camera.h - camera/camera_projection.h - film/film_accumulate.h - film/film_adaptive_sampling.h - film/film_id_passes.h - film/film_passes.h - film/film_read.h - film/film_write_passes.h - integrator/integrator_path_state.h - integrator/integrator_shader_eval.h - integrator/integrator_shadow_catcher.h - kernel_textures.h - kernel_types.h + camera/projection.h + film/accumulate.h + film/adaptive_sampling.h + film/id_passes.h + film/passes.h + film/read.h + film/write_passes.h + integrator/path_state.h + integrator/shader_eval.h + integrator/shadow_catcher.h + textures.h + types.h light/light.h - light/light_background.h - light/light_common.h - light/light_sample.h - sample/sample_jitter.h - sample/sample_lcg.h - sample/sample_mapping.h - sample/sample_mis.h - sample/sample_pattern.h - util/util_color.h - util/util_differential.h - util/util_lookup_table.h - util/util_profiling.h + light/background.h + light/common.h + light/sample.h + sample/jitter.h + sample/lcg.h + sample/mapping.h + sample/mis.h + sample/pattern.h + util/color.h + util/differential.h + util/lookup_table.h + util/profiling.h ) set(SRC_DEVICE_CPU_HEADERS @@ -147,155 +147,155 @@ set(SRC_CLOSURE_HEADERS set(SRC_SVM_HEADERS svm/svm.h - svm/svm_ao.h - svm/svm_aov.h - svm/svm_attribute.h - svm/svm_bevel.h - svm/svm_blackbody.h - svm/svm_bump.h - svm/svm_camera.h - svm/svm_clamp.h - svm/svm_closure.h - svm/svm_convert.h - svm/svm_checker.h - svm/svm_color_util.h - svm/svm_brick.h - svm/svm_displace.h - svm/svm_fresnel.h - svm/svm_wireframe.h - svm/svm_wavelength.h - svm/svm_gamma.h - svm/svm_brightness.h - svm/svm_geometry.h - svm/svm_gradient.h - svm/svm_hsv.h - svm/svm_ies.h - svm/svm_image.h - svm/svm_invert.h - svm/svm_light_path.h - svm/svm_magic.h - svm/svm_map_range.h - svm/svm_mapping.h - svm/svm_mapping_util.h - svm/svm_math.h - svm/svm_math_util.h - svm/svm_mix.h - svm/svm_musgrave.h - svm/svm_noise.h - svm/svm_noisetex.h - svm/svm_normal.h - svm/svm_ramp.h - svm/svm_ramp_util.h - svm/svm_sepcomb_hsv.h - svm/svm_sepcomb_vector.h - svm/svm_sky.h - svm/svm_tex_coord.h - svm/svm_fractal_noise.h - svm/svm_types.h - svm/svm_value.h - svm/svm_vector_rotate.h - svm/svm_vector_transform.h - svm/svm_voronoi.h - svm/svm_voxel.h - svm/svm_wave.h - svm/svm_white_noise.h - svm/svm_vertex_color.h + svm/ao.h + svm/aov.h + svm/attribute.h + svm/bevel.h + svm/blackbody.h + svm/bump.h + svm/camera.h + svm/clamp.h + svm/closure.h + svm/convert.h + svm/checker.h + svm/color_util.h + svm/brick.h + svm/displace.h + svm/fresnel.h + svm/wireframe.h + svm/wavelength.h + svm/gamma.h + svm/brightness.h + svm/geometry.h + svm/gradient.h + svm/hsv.h + svm/ies.h + svm/image.h + svm/invert.h + svm/light_path.h + svm/magic.h + svm/map_range.h + svm/mapping.h + svm/mapping_util.h + svm/math.h + svm/math_util.h + svm/mix.h + svm/musgrave.h + svm/noise.h + svm/noisetex.h + svm/normal.h + svm/ramp.h + svm/ramp_util.h + svm/sepcomb_hsv.h + svm/sepcomb_vector.h + svm/sky.h + svm/tex_coord.h + svm/fractal_noise.h + svm/types.h + svm/value.h + svm/vector_rotate.h + svm/vector_transform.h + svm/voronoi.h + svm/voxel.h + svm/wave.h + svm/white_noise.h + svm/vertex_color.h ) set(SRC_GEOM_HEADERS geom/geom.h - geom/geom_attribute.h - geom/geom_curve.h - geom/geom_curve_intersect.h - geom/geom_motion_curve.h - geom/geom_motion_triangle.h - geom/geom_motion_triangle_intersect.h - geom/geom_motion_triangle_shader.h - geom/geom_object.h - geom/geom_patch.h - geom/geom_primitive.h - geom/geom_shader_data.h - geom/geom_subd_triangle.h - geom/geom_triangle.h - geom/geom_triangle_intersect.h - geom/geom_volume.h + geom/attribute.h + geom/curve.h + geom/curve_intersect.h + geom/motion_curve.h + geom/motion_triangle.h + geom/motion_triangle_intersect.h + geom/motion_triangle_shader.h + geom/object.h + geom/patch.h + geom/primitive.h + geom/shader_data.h + geom/subd_triangle.h + geom/triangle.h + geom/triangle_intersect.h + geom/volume.h ) set(SRC_INTEGRATOR_HEADERS - integrator/integrator_init_from_bake.h - integrator/integrator_init_from_camera.h - integrator/integrator_intersect_closest.h - integrator/integrator_intersect_shadow.h - integrator/integrator_intersect_subsurface.h - integrator/integrator_intersect_volume_stack.h - integrator/integrator_megakernel.h - integrator/integrator_shade_background.h - integrator/integrator_shade_light.h - integrator/integrator_shade_shadow.h - integrator/integrator_shade_surface.h - integrator/integrator_shade_volume.h - integrator/integrator_shadow_state_template.h - integrator/integrator_state.h - integrator/integrator_state_flow.h - integrator/integrator_state_template.h - integrator/integrator_state_util.h - integrator/integrator_subsurface.h - integrator/integrator_subsurface_disk.h - integrator/integrator_subsurface_random_walk.h - integrator/integrator_volume_stack.h + integrator/init_from_bake.h + integrator/init_from_camera.h + integrator/intersect_closest.h + integrator/intersect_shadow.h + integrator/intersect_subsurface.h + integrator/intersect_volume_stack.h + integrator/megakernel.h + integrator/shade_background.h + integrator/shade_light.h + integrator/shade_shadow.h + integrator/shade_surface.h + integrator/shade_volume.h + integrator/shadow_state_template.h + integrator/state.h + integrator/state_flow.h + integrator/state_template.h + integrator/state_util.h + integrator/subsurface.h + integrator/subsurface_disk.h + integrator/subsurface_random_walk.h + integrator/volume_stack.h ) set(SRC_UTIL_HEADERS - ../util/util_atomic.h - ../util/util_color.h - ../util/util_defines.h - ../util/util_half.h - ../util/util_hash.h - ../util/util_math.h - ../util/util_math_fast.h - ../util/util_math_intersect.h - ../util/util_math_float2.h - ../util/util_math_float3.h - ../util/util_math_float4.h - ../util/util_math_int2.h - ../util/util_math_int3.h - ../util/util_math_int4.h - ../util/util_math_matrix.h - ../util/util_projection.h - ../util/util_rect.h - ../util/util_static_assert.h - ../util/util_transform.h - ../util/util_texture.h - ../util/util_types.h - ../util/util_types_float2.h - ../util/util_types_float2_impl.h - ../util/util_types_float3.h - ../util/util_types_float3_impl.h - ../util/util_types_float4.h - ../util/util_types_float4_impl.h - ../util/util_types_float8.h - ../util/util_types_float8_impl.h - ../util/util_types_int2.h - ../util/util_types_int2_impl.h - ../util/util_types_int3.h - ../util/util_types_int3_impl.h - ../util/util_types_int4.h - ../util/util_types_int4_impl.h - ../util/util_types_uchar2.h - ../util/util_types_uchar2_impl.h - ../util/util_types_uchar3.h - ../util/util_types_uchar3_impl.h - ../util/util_types_uchar4.h - ../util/util_types_uchar4_impl.h - ../util/util_types_uint2.h - ../util/util_types_uint2_impl.h - ../util/util_types_uint3.h - ../util/util_types_uint3_impl.h - ../util/util_types_uint4.h - ../util/util_types_uint4_impl.h - ../util/util_types_ushort4.h - ../util/util_types_vector3.h - ../util/util_types_vector3_impl.h + ../util/atomic.h + ../util/color.h + ../util/defines.h + ../util/half.h + ../util/hash.h + ../util/math.h + ../util/math_fast.h + ../util/math_intersect.h + ../util/math_float2.h + ../util/math_float3.h + ../util/math_float4.h + ../util/math_int2.h + ../util/math_int3.h + ../util/math_int4.h + ../util/math_matrix.h + ../util/projection.h + ../util/rect.h + ../util/static_assert.h + ../util/transform.h + ../util/texture.h + ../util/types.h + ../util/types_float2.h + ../util/types_float2_impl.h + ../util/types_float3.h + ../util/types_float3_impl.h + ../util/types_float4.h + ../util/types_float4_impl.h + ../util/types_float8.h + ../util/types_float8_impl.h + ../util/types_int2.h + ../util/types_int2_impl.h + ../util/types_int3.h + ../util/types_int3_impl.h + ../util/types_int4.h + ../util/types_int4_impl.h + ../util/types_uchar2.h + ../util/types_uchar2_impl.h + ../util/types_uchar3.h + ../util/types_uchar3_impl.h + ../util/types_uchar4.h + ../util/types_uchar4_impl.h + ../util/types_uint2.h + ../util/types_uint2_impl.h + ../util/types_uint3.h + ../util/types_uint3_impl.h + ../util/types_uint4.h + ../util/types_uint4_impl.h + ../util/types_ushort4.h + ../util/types_vector3.h + ../util/types_vector3_impl.h ) set(LIB diff --git a/intern/cycles/kernel/bake/bake.h b/intern/cycles/kernel/bake/bake.h index e234d56bd3c..0a78a635d75 100644 --- a/intern/cycles/kernel/bake/bake.h +++ b/intern/cycles/kernel/bake/bake.h @@ -16,8 +16,8 @@ #pragma once -#include "kernel/camera/camera_projection.h" -#include "kernel/integrator/integrator_shader_eval.h" +#include "kernel/camera/projection.h" +#include "kernel/integrator/shader_eval.h" #include "kernel/geom/geom.h" diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index 813ac15711e..0e083812355 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -28,13 +28,13 @@ #pragma once #ifdef __EMBREE__ -# include "kernel/bvh/bvh_embree.h" +# include "kernel/bvh/embree.h" #endif -#include "kernel/bvh/bvh_types.h" -#include "kernel/bvh/bvh_util.h" +#include "kernel/bvh/types.h" +#include "kernel/bvh/util.h" -#include "kernel/integrator/integrator_state_util.h" +#include "kernel/integrator/state_util.h" CCL_NAMESPACE_BEGIN @@ -42,28 +42,28 @@ CCL_NAMESPACE_BEGIN /* Regular BVH traversal */ -# include "kernel/bvh/bvh_nodes.h" +# include "kernel/bvh/nodes.h" # define BVH_FUNCTION_NAME bvh_intersect # define BVH_FUNCTION_FEATURES 0 -# include "kernel/bvh/bvh_traversal.h" +# include "kernel/bvh/traversal.h" # if defined(__HAIR__) # define BVH_FUNCTION_NAME bvh_intersect_hair # define BVH_FUNCTION_FEATURES BVH_HAIR -# include "kernel/bvh/bvh_traversal.h" +# include "kernel/bvh/traversal.h" # endif # if defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_motion # define BVH_FUNCTION_FEATURES BVH_MOTION -# include "kernel/bvh/bvh_traversal.h" +# include "kernel/bvh/traversal.h" # endif # if defined(__HAIR__) && defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_hair_motion # define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION -# include "kernel/bvh/bvh_traversal.h" +# include "kernel/bvh/traversal.h" # endif /* Subsurface scattering BVH traversal */ @@ -71,12 +71,12 @@ CCL_NAMESPACE_BEGIN # if defined(__BVH_LOCAL__) # define BVH_FUNCTION_NAME bvh_intersect_local # define BVH_FUNCTION_FEATURES BVH_HAIR -# include "kernel/bvh/bvh_local.h" +# include "kernel/bvh/local.h" # if defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_local_motion # define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR -# include "kernel/bvh/bvh_local.h" +# include "kernel/bvh/local.h" # endif # endif /* __BVH_LOCAL__ */ @@ -85,12 +85,12 @@ CCL_NAMESPACE_BEGIN # if defined(__VOLUME__) # define BVH_FUNCTION_NAME bvh_intersect_volume # define BVH_FUNCTION_FEATURES BVH_HAIR -# include "kernel/bvh/bvh_volume.h" +# include "kernel/bvh/volume.h" # if defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_volume_motion # define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR -# include "kernel/bvh/bvh_volume.h" +# include "kernel/bvh/volume.h" # endif # endif /* __VOLUME__ */ @@ -99,24 +99,24 @@ CCL_NAMESPACE_BEGIN # if defined(__SHADOW_RECORD_ALL__) # define BVH_FUNCTION_NAME bvh_intersect_shadow_all # define BVH_FUNCTION_FEATURES 0 -# include "kernel/bvh/bvh_shadow_all.h" +# include "kernel/bvh/shadow_all.h" # if defined(__HAIR__) # define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair # define BVH_FUNCTION_FEATURES BVH_HAIR -# include "kernel/bvh/bvh_shadow_all.h" +# include "kernel/bvh/shadow_all.h" # endif # if defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_shadow_all_motion # define BVH_FUNCTION_FEATURES BVH_MOTION -# include "kernel/bvh/bvh_shadow_all.h" +# include "kernel/bvh/shadow_all.h" # endif # if defined(__HAIR__) && defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair_motion # define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION -# include "kernel/bvh/bvh_shadow_all.h" +# include "kernel/bvh/shadow_all.h" # endif # endif /* __SHADOW_RECORD_ALL__ */ @@ -125,12 +125,12 @@ CCL_NAMESPACE_BEGIN # if defined(__VOLUME_RECORD_ALL__) # define BVH_FUNCTION_NAME bvh_intersect_volume_all # define BVH_FUNCTION_FEATURES BVH_HAIR -# include "kernel/bvh/bvh_volume_all.h" +# include "kernel/bvh/volume_all.h" # if defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_volume_all_motion # define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR -# include "kernel/bvh/bvh_volume_all.h" +# include "kernel/bvh/volume_all.h" # endif # endif /* __VOLUME_RECORD_ALL__ */ diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/embree.h similarity index 99% rename from intern/cycles/kernel/bvh/bvh_embree.h rename to intern/cycles/kernel/bvh/embree.h index 321e0f28dae..9edd4f90a7e 100644 --- a/intern/cycles/kernel/bvh/bvh_embree.h +++ b/intern/cycles/kernel/bvh/embree.h @@ -22,7 +22,7 @@ #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/bvh/bvh_local.h b/intern/cycles/kernel/bvh/local.h similarity index 100% rename from intern/cycles/kernel/bvh/bvh_local.h rename to intern/cycles/kernel/bvh/local.h diff --git a/intern/cycles/kernel/bvh/bvh_nodes.h b/intern/cycles/kernel/bvh/nodes.h similarity index 100% rename from intern/cycles/kernel/bvh/bvh_nodes.h rename to intern/cycles/kernel/bvh/nodes.h diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/shadow_all.h similarity index 100% rename from intern/cycles/kernel/bvh/bvh_shadow_all.h rename to intern/cycles/kernel/bvh/shadow_all.h diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/traversal.h similarity index 100% rename from intern/cycles/kernel/bvh/bvh_traversal.h rename to intern/cycles/kernel/bvh/traversal.h diff --git a/intern/cycles/kernel/bvh/bvh_types.h b/intern/cycles/kernel/bvh/types.h similarity index 100% rename from intern/cycles/kernel/bvh/bvh_types.h rename to intern/cycles/kernel/bvh/types.h diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/util.h similarity index 100% rename from intern/cycles/kernel/bvh/bvh_util.h rename to intern/cycles/kernel/bvh/util.h diff --git a/intern/cycles/kernel/bvh/bvh_volume.h b/intern/cycles/kernel/bvh/volume.h similarity index 100% rename from intern/cycles/kernel/bvh/bvh_volume.h rename to intern/cycles/kernel/bvh/volume.h diff --git a/intern/cycles/kernel/bvh/bvh_volume_all.h b/intern/cycles/kernel/bvh/volume_all.h similarity index 100% rename from intern/cycles/kernel/bvh/bvh_volume_all.h rename to intern/cycles/kernel/bvh/volume_all.h diff --git a/intern/cycles/kernel/camera/camera.h b/intern/cycles/kernel/camera/camera.h index 66bc25bb879..e966e9e1596 100644 --- a/intern/cycles/kernel/camera/camera.h +++ b/intern/cycles/kernel/camera/camera.h @@ -16,10 +16,10 @@ #pragma once -#include "kernel/camera/camera_projection.h" -#include "kernel/sample/sample_mapping.h" -#include "kernel/util/util_differential.h" -#include "kernel/util/util_lookup_table.h" +#include "kernel/camera/projection.h" +#include "kernel/sample/mapping.h" +#include "kernel/util/differential.h" +#include "kernel/util/lookup_table.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/camera/camera_projection.h b/intern/cycles/kernel/camera/projection.h similarity index 100% rename from intern/cycles/kernel/camera/camera_projection.h rename to intern/cycles/kernel/camera/projection.h diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h index fa88c66f536..b2a9c9555c3 100644 --- a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h +++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h @@ -32,7 +32,7 @@ #pragma once -#include "kernel/sample/sample_mapping.h" +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/closure/bsdf_diffuse.h b/intern/cycles/kernel/closure/bsdf_diffuse.h index dd3b4500b1f..3139cb612fa 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse.h @@ -32,7 +32,7 @@ #pragma once -#include "kernel/sample/sample_mapping.h" +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h index 1e70d3e534e..fbb82617dad 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h @@ -32,7 +32,7 @@ #pragma once -#include "kernel/sample/sample_mapping.h" +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/closure/bsdf_hair_principled.h b/intern/cycles/kernel/closure/bsdf_hair_principled.h index ff554d4a60e..f55ea0f6a2e 100644 --- a/intern/cycles/kernel/closure/bsdf_hair_principled.h +++ b/intern/cycles/kernel/closure/bsdf_hair_principled.h @@ -20,7 +20,7 @@ # include #endif -#include "kernel/util/util_color.h" +#include "kernel/util/color.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index 28aac368f2b..83242a73685 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -32,8 +32,11 @@ #pragma once -#include "kernel/sample/sample_pattern.h" -#include "kernel/util/util_lookup_table.h" +#include "kernel/closure/bsdf_util.h" + +#include "kernel/sample/pattern.h" + +#include "kernel/util/lookup_table.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index b1ab8d7ffd0..77370fbec4e 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -16,8 +16,8 @@ #pragma once -#include "kernel/sample/sample_lcg.h" -#include "kernel/sample/sample_mapping.h" +#include "kernel/sample/lcg.h" +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index 0e3b21117b5..69376c1294d 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -27,7 +27,7 @@ #include "kernel/closure/bsdf_util.h" -#include "kernel/sample/sample_mapping.h" +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/device/cpu/compat.h b/intern/cycles/kernel/device/cpu/compat.h index 888c0d5d872..5ccca52255f 100644 --- a/intern/cycles/kernel/device/cpu/compat.h +++ b/intern/cycles/kernel/device/cpu/compat.h @@ -26,11 +26,11 @@ # pragma GCC diagnostic ignored "-Wuninitialized" #endif -#include "util/util_half.h" -#include "util/util_math.h" -#include "util/util_simd.h" -#include "util/util_texture.h" -#include "util/util_types.h" +#include "util/half.h" +#include "util/math.h" +#include "util/simd.h" +#include "util/texture.h" +#include "util/types.h" /* On x86_64, versions of glibc < 2.16 have an issue where expf is * much slower than the double version. This was fixed in glibc 2.16. diff --git a/intern/cycles/kernel/device/cpu/globals.h b/intern/cycles/kernel/device/cpu/globals.h index f3e530a9edc..dd0327b3f94 100644 --- a/intern/cycles/kernel/device/cpu/globals.h +++ b/intern/cycles/kernel/device/cpu/globals.h @@ -18,8 +18,8 @@ #pragma once -#include "kernel/kernel_types.h" -#include "kernel/util/util_profiling.h" +#include "kernel/types.h" +#include "kernel/util/profiling.h" CCL_NAMESPACE_BEGIN @@ -36,7 +36,7 @@ struct OSLShadingSystem; typedef struct KernelGlobalsCPU { #define KERNEL_TEX(type, name) texture name; -#include "kernel/kernel_textures.h" +#include "kernel/textures.h" KernelData __data; diff --git a/intern/cycles/kernel/device/cpu/kernel.cpp b/intern/cycles/kernel/device/cpu/kernel.cpp index 8519b77aa08..a16c637d5ac 100644 --- a/intern/cycles/kernel/device/cpu/kernel.cpp +++ b/intern/cycles/kernel/device/cpu/kernel.cpp @@ -85,7 +85,7 @@ void kernel_global_memory_copy(KernelGlobalsCPU *kg, const char *name, void *mem kg->tname.data = (type *)mem; \ kg->tname.width = size; \ } -#include "kernel/kernel_textures.h" +#include "kernel/textures.h" else { assert(0); } diff --git a/intern/cycles/kernel/device/cpu/kernel.h b/intern/cycles/kernel/device/cpu/kernel.h index 28337a58898..c49d7ca445a 100644 --- a/intern/cycles/kernel/device/cpu/kernel.h +++ b/intern/cycles/kernel/device/cpu/kernel.h @@ -18,9 +18,9 @@ /* CPU Kernel Interface */ -#include "util/util_types.h" +#include "util/types.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h index 148b6a33cb5..6df5d7787fc 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h @@ -29,26 +29,26 @@ # include "kernel/device/cpu/globals.h" # include "kernel/device/cpu/image.h" -# include "kernel/integrator/integrator_state.h" -# include "kernel/integrator/integrator_state_flow.h" -# include "kernel/integrator/integrator_state_util.h" +# include "kernel/integrator/state.h" +# include "kernel/integrator/state_flow.h" +# include "kernel/integrator/state_util.h" -# include "kernel/integrator/integrator_init_from_camera.h" -# include "kernel/integrator/integrator_init_from_bake.h" -# include "kernel/integrator/integrator_intersect_closest.h" -# include "kernel/integrator/integrator_intersect_shadow.h" -# include "kernel/integrator/integrator_intersect_subsurface.h" -# include "kernel/integrator/integrator_intersect_volume_stack.h" -# include "kernel/integrator/integrator_shade_background.h" -# include "kernel/integrator/integrator_shade_light.h" -# include "kernel/integrator/integrator_shade_shadow.h" -# include "kernel/integrator/integrator_shade_surface.h" -# include "kernel/integrator/integrator_shade_volume.h" -# include "kernel/integrator/integrator_megakernel.h" +# include "kernel/integrator/init_from_camera.h" +# include "kernel/integrator/init_from_bake.h" +# include "kernel/integrator/intersect_closest.h" +# include "kernel/integrator/intersect_shadow.h" +# include "kernel/integrator/intersect_subsurface.h" +# include "kernel/integrator/intersect_volume_stack.h" +# include "kernel/integrator/shade_background.h" +# include "kernel/integrator/shade_light.h" +# include "kernel/integrator/shade_shadow.h" +# include "kernel/integrator/shade_surface.h" +# include "kernel/integrator/shade_volume.h" +# include "kernel/integrator/megakernel.h" -# include "kernel/film/film_adaptive_sampling.h" -# include "kernel/film/film_read.h" -# include "kernel/film/film_id_passes.h" +# include "kernel/film/adaptive_sampling.h" +# include "kernel/film/read.h" +# include "kernel/film/id_passes.h" # include "kernel/bake/bake.h" diff --git a/intern/cycles/kernel/device/cpu/kernel_avx.cpp b/intern/cycles/kernel/device/cpu/kernel_avx.cpp index 220768036ab..cece750a255 100644 --- a/intern/cycles/kernel/device/cpu/kernel_avx.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_avx.cpp @@ -18,7 +18,7 @@ * optimization flags and nearly all functions inlined, while kernel.cpp * is compiled without for other CPU's. */ -#include "util/util_optimization.h" +#include "util/optimization.h" #ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX # define KERNEL_STUB diff --git a/intern/cycles/kernel/device/cpu/kernel_avx2.cpp b/intern/cycles/kernel/device/cpu/kernel_avx2.cpp index 90c05113cbe..fad4581236e 100644 --- a/intern/cycles/kernel/device/cpu/kernel_avx2.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_avx2.cpp @@ -18,7 +18,7 @@ * optimization flags and nearly all functions inlined, while kernel.cpp * is compiled without for other CPU's. */ -#include "util/util_optimization.h" +#include "util/optimization.h" #ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 # define KERNEL_STUB diff --git a/intern/cycles/kernel/device/cpu/kernel_sse2.cpp b/intern/cycles/kernel/device/cpu/kernel_sse2.cpp index fb85ef5b0d0..5fb4849ac08 100644 --- a/intern/cycles/kernel/device/cpu/kernel_sse2.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_sse2.cpp @@ -18,7 +18,7 @@ * optimization flags and nearly all functions inlined, while kernel.cpp * is compiled without for other CPU's. */ -#include "util/util_optimization.h" +#include "util/optimization.h" #ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 # define KERNEL_STUB diff --git a/intern/cycles/kernel/device/cpu/kernel_sse3.cpp b/intern/cycles/kernel/device/cpu/kernel_sse3.cpp index 87baf04258a..c9424682fd4 100644 --- a/intern/cycles/kernel/device/cpu/kernel_sse3.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_sse3.cpp @@ -18,7 +18,7 @@ * optimization flags and nearly all functions inlined, while kernel.cpp * is compiled without for other CPU's. */ -#include "util/util_optimization.h" +#include "util/optimization.h" #ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 # define KERNEL_STUB diff --git a/intern/cycles/kernel/device/cpu/kernel_sse41.cpp b/intern/cycles/kernel/device/cpu/kernel_sse41.cpp index bb421d58815..849ebf51989 100644 --- a/intern/cycles/kernel/device/cpu/kernel_sse41.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_sse41.cpp @@ -18,7 +18,7 @@ * optimization flags and nearly all functions inlined, while kernel.cpp * is compiled without for other CPU's. */ -#include "util/util_optimization.h" +#include "util/optimization.h" #ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 # define KERNEL_STUB diff --git a/intern/cycles/kernel/device/cuda/compat.h b/intern/cycles/kernel/device/cuda/compat.h index 8a50eb1a3d5..1ee82e6eb7c 100644 --- a/intern/cycles/kernel/device/cuda/compat.h +++ b/intern/cycles/kernel/device/cuda/compat.h @@ -137,5 +137,5 @@ __device__ float __half2float(const half h) /* Types */ -#include "util/util_half.h" -#include "util/util_types.h" +#include "util/half.h" +#include "util/types.h" diff --git a/intern/cycles/kernel/device/cuda/globals.h b/intern/cycles/kernel/device/cuda/globals.h index cde935198b3..e5023fad40c 100644 --- a/intern/cycles/kernel/device/cuda/globals.h +++ b/intern/cycles/kernel/device/cuda/globals.h @@ -18,11 +18,11 @@ #pragma once -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "kernel/integrator/integrator_state.h" +#include "kernel/integrator/state.h" -#include "kernel/util/util_profiling.h" +#include "kernel/util/profiling.h" CCL_NAMESPACE_BEGIN @@ -36,7 +36,7 @@ typedef ccl_global const KernelGlobalsGPU *ccl_restrict KernelGlobals; /* Global scene data and textures */ __constant__ KernelData __data; #define KERNEL_TEX(type, name) const __constant__ __device__ type *name; -#include "kernel/kernel_textures.h" +#include "kernel/textures.h" /* Integrator state */ __constant__ IntegratorStateGPU __integrator_state; diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index aa360b3016a..f86a8c692aa 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -21,26 +21,26 @@ #include "kernel/device/gpu/parallel_sorted_index.h" #include "kernel/device/gpu/work_stealing.h" -#include "kernel/integrator/integrator_state.h" -#include "kernel/integrator/integrator_state_flow.h" -#include "kernel/integrator/integrator_state_util.h" +#include "kernel/integrator/state.h" +#include "kernel/integrator/state_flow.h" +#include "kernel/integrator/state_util.h" -#include "kernel/integrator/integrator_init_from_bake.h" -#include "kernel/integrator/integrator_init_from_camera.h" -#include "kernel/integrator/integrator_intersect_closest.h" -#include "kernel/integrator/integrator_intersect_shadow.h" -#include "kernel/integrator/integrator_intersect_subsurface.h" -#include "kernel/integrator/integrator_intersect_volume_stack.h" -#include "kernel/integrator/integrator_shade_background.h" -#include "kernel/integrator/integrator_shade_light.h" -#include "kernel/integrator/integrator_shade_shadow.h" -#include "kernel/integrator/integrator_shade_surface.h" -#include "kernel/integrator/integrator_shade_volume.h" +#include "kernel/integrator/init_from_bake.h" +#include "kernel/integrator/init_from_camera.h" +#include "kernel/integrator/intersect_closest.h" +#include "kernel/integrator/intersect_shadow.h" +#include "kernel/integrator/intersect_subsurface.h" +#include "kernel/integrator/intersect_volume_stack.h" +#include "kernel/integrator/shade_background.h" +#include "kernel/integrator/shade_light.h" +#include "kernel/integrator/shade_shadow.h" +#include "kernel/integrator/shade_surface.h" +#include "kernel/integrator/shade_volume.h" #include "kernel/bake/bake.h" -#include "kernel/film/film_adaptive_sampling.h" -#include "kernel/film/film_read.h" +#include "kernel/film/adaptive_sampling.h" +#include "kernel/film/read.h" /* -------------------------------------------------------------------- * Integrator. diff --git a/intern/cycles/kernel/device/gpu/parallel_active_index.h b/intern/cycles/kernel/device/gpu/parallel_active_index.h index db4a4bf71e0..d7416beb783 100644 --- a/intern/cycles/kernel/device/gpu/parallel_active_index.h +++ b/intern/cycles/kernel/device/gpu/parallel_active_index.h @@ -23,7 +23,7 @@ CCL_NAMESPACE_BEGIN * * Shared memory requirement is `sizeof(int) * (number_of_warps + 1)`. */ -#include "util/util_atomic.h" +#include "util/atomic.h" #ifdef __HIP__ # define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 1024 diff --git a/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h index aabe6e2e27a..6de3a022569 100644 --- a/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h +++ b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h @@ -25,7 +25,7 @@ CCL_NAMESPACE_BEGIN * This is used for an array the size of the number of shaders in the scene * which is not usually huge, so might not be a significant bottleneck. */ -#include "util/util_atomic.h" +#include "util/atomic.h" #ifdef __HIP__ # define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 1024 diff --git a/intern/cycles/kernel/device/gpu/parallel_sorted_index.h b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h index 7570c5a6bbd..c06d7be444f 100644 --- a/intern/cycles/kernel/device/gpu/parallel_sorted_index.h +++ b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h @@ -24,7 +24,7 @@ CCL_NAMESPACE_BEGIN * * TODO: there may be ways to optimize this to avoid this many atomic ops? */ -#include "util/util_atomic.h" +#include "util/atomic.h" #ifdef __HIP__ # define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 1024 diff --git a/intern/cycles/kernel/device/hip/compat.h b/intern/cycles/kernel/device/hip/compat.h index 089976d84e4..282c3eca641 100644 --- a/intern/cycles/kernel/device/hip/compat.h +++ b/intern/cycles/kernel/device/hip/compat.h @@ -116,5 +116,5 @@ ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object tex /* Types */ -#include "util/util_half.h" -#include "util/util_types.h" +#include "util/half.h" +#include "util/types.h" diff --git a/intern/cycles/kernel/device/hip/globals.h b/intern/cycles/kernel/device/hip/globals.h index 079944bd8f2..d9a560d668b 100644 --- a/intern/cycles/kernel/device/hip/globals.h +++ b/intern/cycles/kernel/device/hip/globals.h @@ -18,11 +18,11 @@ #pragma once -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "kernel/integrator/integrator_state.h" +#include "kernel/integrator/state.h" -#include "kernel/util/util_profiling.h" +#include "kernel/util/profiling.h" CCL_NAMESPACE_BEGIN @@ -36,7 +36,7 @@ typedef ccl_global const KernelGlobalsGPU *ccl_restrict KernelGlobals; /* Global scene data and textures */ __constant__ KernelData __data; #define KERNEL_TEX(type, name) __attribute__((used)) const __constant__ __device__ type *name; -#include "kernel/kernel_textures.h" +#include "kernel/textures.h" /* Integrator state */ __constant__ IntegratorStateGPU __integrator_state; diff --git a/intern/cycles/kernel/device/optix/compat.h b/intern/cycles/kernel/device/optix/compat.h index d27b7d55475..835e4621d47 100644 --- a/intern/cycles/kernel/device/optix/compat.h +++ b/intern/cycles/kernel/device/optix/compat.h @@ -129,5 +129,5 @@ __device__ float __half2float(const half h) /* Types */ -#include "util/util_half.h" -#include "util/util_types.h" +#include "util/half.h" +#include "util/types.h" diff --git a/intern/cycles/kernel/device/optix/globals.h b/intern/cycles/kernel/device/optix/globals.h index e038bc1797a..e9b72369cd5 100644 --- a/intern/cycles/kernel/device/optix/globals.h +++ b/intern/cycles/kernel/device/optix/globals.h @@ -18,11 +18,11 @@ #pragma once -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "kernel/integrator/integrator_state.h" +#include "kernel/integrator/state.h" -#include "kernel/util/util_profiling.h" +#include "kernel/util/profiling.h" CCL_NAMESPACE_BEGIN @@ -42,7 +42,7 @@ struct KernelParamsOptiX { /* Global scene data and textures */ KernelData data; #define KERNEL_TEX(type, name) const type *name; -#include "kernel/kernel_textures.h" +#include "kernel/textures.h" /* Integrator state */ IntegratorStateGPU __integrator_state; diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu index a3bafb9846c..6989219cd9f 100644 --- a/intern/cycles/kernel/device/optix/kernel.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -21,14 +21,14 @@ #include "kernel/device/gpu/image.h" /* Texture lookup uses normal CUDA intrinsics. */ -#include "kernel/integrator/integrator_state.h" -#include "kernel/integrator/integrator_state_flow.h" -#include "kernel/integrator/integrator_state_util.h" +#include "kernel/integrator/state.h" +#include "kernel/integrator/state_flow.h" +#include "kernel/integrator/state_util.h" -#include "kernel/integrator/integrator_intersect_closest.h" -#include "kernel/integrator/integrator_intersect_shadow.h" -#include "kernel/integrator/integrator_intersect_subsurface.h" -#include "kernel/integrator/integrator_intersect_volume_stack.h" +#include "kernel/integrator/intersect_closest.h" +#include "kernel/integrator/intersect_shadow.h" +#include "kernel/integrator/intersect_subsurface.h" +#include "kernel/integrator/intersect_volume_stack.h" // clang-format on diff --git a/intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu b/intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu index bf787e29eaa..071e9deae0b 100644 --- a/intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu +++ b/intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu @@ -18,7 +18,8 @@ * much longer to compiler. This is only loaded when needed by the scene. */ #include "kernel/device/optix/kernel.cu" -#include "kernel/integrator/integrator_shade_surface.h" + +#include "kernel/integrator/shade_surface.h" extern "C" __global__ void __raygen__kernel_optix_integrator_shade_surface_raytrace() { diff --git a/intern/cycles/kernel/film/film_accumulate.h b/intern/cycles/kernel/film/accumulate.h similarity index 99% rename from intern/cycles/kernel/film/film_accumulate.h rename to intern/cycles/kernel/film/accumulate.h index 91424fdbe21..30e1afea751 100644 --- a/intern/cycles/kernel/film/film_accumulate.h +++ b/intern/cycles/kernel/film/accumulate.h @@ -16,10 +16,10 @@ #pragma once -#include "kernel/film/film_adaptive_sampling.h" -#include "kernel/film/film_write_passes.h" +#include "kernel/film/adaptive_sampling.h" +#include "kernel/film/write_passes.h" -#include "kernel/integrator/integrator_shadow_catcher.h" +#include "kernel/integrator/shadow_catcher.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/film/film_adaptive_sampling.h b/intern/cycles/kernel/film/adaptive_sampling.h similarity index 99% rename from intern/cycles/kernel/film/film_adaptive_sampling.h rename to intern/cycles/kernel/film/adaptive_sampling.h index c78b5f6b707..468c5d4486e 100644 --- a/intern/cycles/kernel/film/film_adaptive_sampling.h +++ b/intern/cycles/kernel/film/adaptive_sampling.h @@ -16,7 +16,7 @@ #pragma once -#include "kernel/film/film_write_passes.h" +#include "kernel/film/write_passes.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/film/film_id_passes.h b/intern/cycles/kernel/film/id_passes.h similarity index 100% rename from intern/cycles/kernel/film/film_id_passes.h rename to intern/cycles/kernel/film/id_passes.h diff --git a/intern/cycles/kernel/film/film_passes.h b/intern/cycles/kernel/film/passes.h similarity index 99% rename from intern/cycles/kernel/film/film_passes.h rename to intern/cycles/kernel/film/passes.h index 6c124247f89..3a91d1653fe 100644 --- a/intern/cycles/kernel/film/film_passes.h +++ b/intern/cycles/kernel/film/passes.h @@ -18,8 +18,8 @@ #include "kernel/geom/geom.h" -#include "kernel/film/film_id_passes.h" -#include "kernel/film/film_write_passes.h" +#include "kernel/film/id_passes.h" +#include "kernel/film/write_passes.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/film/film_read.h b/intern/cycles/kernel/film/read.h similarity index 100% rename from intern/cycles/kernel/film/film_read.h rename to intern/cycles/kernel/film/read.h diff --git a/intern/cycles/kernel/film/film_write_passes.h b/intern/cycles/kernel/film/write_passes.h similarity index 100% rename from intern/cycles/kernel/film/film_write_passes.h rename to intern/cycles/kernel/film/write_passes.h diff --git a/intern/cycles/kernel/geom/geom_attribute.h b/intern/cycles/kernel/geom/attribute.h similarity index 100% rename from intern/cycles/kernel/geom/geom_attribute.h rename to intern/cycles/kernel/geom/attribute.h diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/curve.h similarity index 100% rename from intern/cycles/kernel/geom/geom_curve.h rename to intern/cycles/kernel/geom/curve.h diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/curve_intersect.h similarity index 100% rename from intern/cycles/kernel/geom/geom_curve_intersect.h rename to intern/cycles/kernel/geom/curve_intersect.h diff --git a/intern/cycles/kernel/geom/geom.h b/intern/cycles/kernel/geom/geom.h index 4de824cc277..9d023375a35 100644 --- a/intern/cycles/kernel/geom/geom.h +++ b/intern/cycles/kernel/geom/geom.h @@ -17,21 +17,21 @@ #pragma once // clang-format off -#include "kernel/geom/geom_attribute.h" -#include "kernel/geom/geom_object.h" +#include "kernel/geom/attribute.h" +#include "kernel/geom/object.h" #ifdef __PATCH_EVAL__ -# include "kernel/geom/geom_patch.h" +# include "kernel/geom/patch.h" #endif -#include "kernel/geom/geom_triangle.h" -#include "kernel/geom/geom_subd_triangle.h" -#include "kernel/geom/geom_triangle_intersect.h" -#include "kernel/geom/geom_motion_triangle.h" -#include "kernel/geom/geom_motion_triangle_intersect.h" -#include "kernel/geom/geom_motion_triangle_shader.h" -#include "kernel/geom/geom_motion_curve.h" -#include "kernel/geom/geom_curve.h" -#include "kernel/geom/geom_curve_intersect.h" -#include "kernel/geom/geom_volume.h" -#include "kernel/geom/geom_primitive.h" -#include "kernel/geom/geom_shader_data.h" +#include "kernel/geom/triangle.h" +#include "kernel/geom/subd_triangle.h" +#include "kernel/geom/triangle_intersect.h" +#include "kernel/geom/motion_triangle.h" +#include "kernel/geom/motion_triangle_intersect.h" +#include "kernel/geom/motion_triangle_shader.h" +#include "kernel/geom/motion_curve.h" +#include "kernel/geom/curve.h" +#include "kernel/geom/curve_intersect.h" +#include "kernel/geom/volume.h" +#include "kernel/geom/primitive.h" +#include "kernel/geom/shader_data.h" // clang-format on diff --git a/intern/cycles/kernel/geom/geom_motion_curve.h b/intern/cycles/kernel/geom/motion_curve.h similarity index 100% rename from intern/cycles/kernel/geom/geom_motion_curve.h rename to intern/cycles/kernel/geom/motion_curve.h diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/motion_triangle.h similarity index 99% rename from intern/cycles/kernel/geom/geom_motion_triangle.h rename to intern/cycles/kernel/geom/motion_triangle.h index 69d15f950ec..43f894938e0 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle.h +++ b/intern/cycles/kernel/geom/motion_triangle.h @@ -27,7 +27,7 @@ #pragma once -#include "kernel/bvh/bvh_util.h" +#include "kernel/bvh/util.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h b/intern/cycles/kernel/geom/motion_triangle_intersect.h similarity index 100% rename from intern/cycles/kernel/geom/geom_motion_triangle_intersect.h rename to intern/cycles/kernel/geom/motion_triangle_intersect.h diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h b/intern/cycles/kernel/geom/motion_triangle_shader.h similarity index 100% rename from intern/cycles/kernel/geom/geom_motion_triangle_shader.h rename to intern/cycles/kernel/geom/motion_triangle_shader.h diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/object.h similarity index 100% rename from intern/cycles/kernel/geom/geom_object.h rename to intern/cycles/kernel/geom/object.h diff --git a/intern/cycles/kernel/geom/geom_patch.h b/intern/cycles/kernel/geom/patch.h similarity index 99% rename from intern/cycles/kernel/geom/geom_patch.h rename to intern/cycles/kernel/geom/patch.h index bf1a06220aa..7d24937a41e 100644 --- a/intern/cycles/kernel/geom/geom_patch.h +++ b/intern/cycles/kernel/geom/patch.h @@ -26,7 +26,7 @@ #pragma once -#include "util/util_color.h" +#include "util/color.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/primitive.h similarity index 99% rename from intern/cycles/kernel/geom/geom_primitive.h rename to intern/cycles/kernel/geom/primitive.h index bc559e3c812..7a8921b6d6e 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/primitive.h @@ -21,7 +21,7 @@ #pragma once -#include "kernel/camera/camera_projection.h" +#include "kernel/camera/projection.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/geom/geom_shader_data.h b/intern/cycles/kernel/geom/shader_data.h similarity index 100% rename from intern/cycles/kernel/geom/geom_shader_data.h rename to intern/cycles/kernel/geom/shader_data.h diff --git a/intern/cycles/kernel/geom/geom_subd_triangle.h b/intern/cycles/kernel/geom/subd_triangle.h similarity index 100% rename from intern/cycles/kernel/geom/geom_subd_triangle.h rename to intern/cycles/kernel/geom/subd_triangle.h diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/triangle.h similarity index 100% rename from intern/cycles/kernel/geom/geom_triangle.h rename to intern/cycles/kernel/geom/triangle.h diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/triangle_intersect.h similarity index 99% rename from intern/cycles/kernel/geom/geom_triangle_intersect.h rename to intern/cycles/kernel/geom/triangle_intersect.h index 440dc23d124..faff8a85a93 100644 --- a/intern/cycles/kernel/geom/geom_triangle_intersect.h +++ b/intern/cycles/kernel/geom/triangle_intersect.h @@ -22,7 +22,7 @@ #pragma once -#include "kernel/sample/sample_lcg.h" +#include "kernel/sample/lcg.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/volume.h similarity index 100% rename from intern/cycles/kernel/geom/geom_volume.h rename to intern/cycles/kernel/geom/volume.h diff --git a/intern/cycles/kernel/integrator/integrator_init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h similarity index 97% rename from intern/cycles/kernel/integrator/integrator_init_from_bake.h rename to intern/cycles/kernel/integrator/init_from_bake.h index 5790cfd3f22..4e30563e21b 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_bake.h +++ b/intern/cycles/kernel/integrator/init_from_bake.h @@ -18,12 +18,12 @@ #include "kernel/camera/camera.h" -#include "kernel/film/film_accumulate.h" -#include "kernel/film/film_adaptive_sampling.h" +#include "kernel/film/accumulate.h" +#include "kernel/film/adaptive_sampling.h" -#include "kernel/integrator/integrator_path_state.h" +#include "kernel/integrator/path_state.h" -#include "kernel/sample/sample_pattern.h" +#include "kernel/sample/pattern.h" #include "kernel/geom/geom.h" diff --git a/intern/cycles/kernel/integrator/integrator_init_from_camera.h b/intern/cycles/kernel/integrator/init_from_camera.h similarity index 94% rename from intern/cycles/kernel/integrator/integrator_init_from_camera.h rename to intern/cycles/kernel/integrator/init_from_camera.h index 499a72ffbc4..f0ba77bd9a6 100644 --- a/intern/cycles/kernel/integrator/integrator_init_from_camera.h +++ b/intern/cycles/kernel/integrator/init_from_camera.h @@ -18,13 +18,13 @@ #include "kernel/camera/camera.h" -#include "kernel/film/film_accumulate.h" -#include "kernel/film/film_adaptive_sampling.h" +#include "kernel/film/accumulate.h" +#include "kernel/film/adaptive_sampling.h" -#include "kernel/integrator/integrator_path_state.h" -#include "kernel/integrator/integrator_shadow_catcher.h" +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shadow_catcher.h" -#include "kernel/sample/sample_pattern.h" +#include "kernel/sample/pattern.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h similarity index 97% rename from intern/cycles/kernel/integrator/integrator_intersect_closest.h rename to intern/cycles/kernel/integrator/intersect_closest.h index 41d3dfde41a..d5a9df9669b 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_closest.h +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -16,14 +16,14 @@ #pragma once -#include "kernel/camera/camera_projection.h" +#include "kernel/camera/projection.h" -#include "kernel/integrator/integrator_path_state.h" -#include "kernel/integrator/integrator_shadow_catcher.h" +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shadow_catcher.h" #include "kernel/light/light.h" -#include "kernel/util/util_differential.h" +#include "kernel/util/differential.h" #include "kernel/geom/geom.h" diff --git a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h b/intern/cycles/kernel/integrator/intersect_shadow.h similarity index 100% rename from intern/cycles/kernel/integrator/integrator_intersect_shadow.h rename to intern/cycles/kernel/integrator/intersect_shadow.h diff --git a/intern/cycles/kernel/integrator/integrator_intersect_subsurface.h b/intern/cycles/kernel/integrator/intersect_subsurface.h similarity index 94% rename from intern/cycles/kernel/integrator/integrator_intersect_subsurface.h rename to intern/cycles/kernel/integrator/intersect_subsurface.h index b575e7fd1e6..27b8e1e5f5a 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_subsurface.h +++ b/intern/cycles/kernel/integrator/intersect_subsurface.h @@ -16,7 +16,7 @@ #pragma once -#include "kernel/integrator/integrator_subsurface.h" +#include "kernel/integrator/subsurface.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h similarity index 98% rename from intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h rename to intern/cycles/kernel/integrator/intersect_volume_stack.h index 505d9687948..1c91318ff9c 100644 --- a/intern/cycles/kernel/integrator/integrator_intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h @@ -18,8 +18,8 @@ #include "kernel/bvh/bvh.h" #include "kernel/geom/geom.h" -#include "kernel/integrator/integrator_shader_eval.h" -#include "kernel/integrator/integrator_volume_stack.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/volume_stack.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_megakernel.h b/intern/cycles/kernel/integrator/megakernel.h similarity index 85% rename from intern/cycles/kernel/integrator/integrator_megakernel.h rename to intern/cycles/kernel/integrator/megakernel.h index 21a483a792b..d8cc794dc7a 100644 --- a/intern/cycles/kernel/integrator/integrator_megakernel.h +++ b/intern/cycles/kernel/integrator/megakernel.h @@ -16,16 +16,16 @@ #pragma once -#include "kernel/integrator/integrator_init_from_camera.h" -#include "kernel/integrator/integrator_intersect_closest.h" -#include "kernel/integrator/integrator_intersect_shadow.h" -#include "kernel/integrator/integrator_intersect_subsurface.h" -#include "kernel/integrator/integrator_intersect_volume_stack.h" -#include "kernel/integrator/integrator_shade_background.h" -#include "kernel/integrator/integrator_shade_light.h" -#include "kernel/integrator/integrator_shade_shadow.h" -#include "kernel/integrator/integrator_shade_surface.h" -#include "kernel/integrator/integrator_shade_volume.h" +#include "kernel/integrator/init_from_camera.h" +#include "kernel/integrator/intersect_closest.h" +#include "kernel/integrator/intersect_shadow.h" +#include "kernel/integrator/intersect_subsurface.h" +#include "kernel/integrator/intersect_volume_stack.h" +#include "kernel/integrator/shade_background.h" +#include "kernel/integrator/shade_light.h" +#include "kernel/integrator/shade_shadow.h" +#include "kernel/integrator/shade_surface.h" +#include "kernel/integrator/shade_volume.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_path_state.h b/intern/cycles/kernel/integrator/path_state.h similarity index 99% rename from intern/cycles/kernel/integrator/integrator_path_state.h rename to intern/cycles/kernel/integrator/path_state.h index 73062b26682..8311b97dedb 100644 --- a/intern/cycles/kernel/integrator/integrator_path_state.h +++ b/intern/cycles/kernel/integrator/path_state.h @@ -16,7 +16,7 @@ #pragma once -#include "kernel/sample/sample_pattern.h" +#include "kernel/sample/pattern.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shade_background.h b/intern/cycles/kernel/integrator/shade_background.h similarity index 98% rename from intern/cycles/kernel/integrator/integrator_shade_background.h rename to intern/cycles/kernel/integrator/shade_background.h index b3bef9a234e..71a590749bd 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_background.h +++ b/intern/cycles/kernel/integrator/shade_background.h @@ -16,11 +16,11 @@ #pragma once -#include "kernel/film/film_accumulate.h" -#include "kernel/integrator/integrator_shader_eval.h" +#include "kernel/film/accumulate.h" +#include "kernel/integrator/shader_eval.h" #include "kernel/light/light.h" -#include "kernel/light/light_sample.h" -#include "kernel/sample/sample_mis.h" +#include "kernel/light/sample.h" +#include "kernel/sample/mis.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shade_light.h b/intern/cycles/kernel/integrator/shade_light.h similarity index 97% rename from intern/cycles/kernel/integrator/integrator_shade_light.h rename to intern/cycles/kernel/integrator/shade_light.h index 7d220006322..7dad3b4e49d 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -16,10 +16,10 @@ #pragma once -#include "kernel/film/film_accumulate.h" -#include "kernel/integrator/integrator_shader_eval.h" +#include "kernel/film/accumulate.h" +#include "kernel/integrator/shader_eval.h" #include "kernel/light/light.h" -#include "kernel/light/light_sample.h" +#include "kernel/light/sample.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h similarity index 97% rename from intern/cycles/kernel/integrator/integrator_shade_shadow.h rename to intern/cycles/kernel/integrator/shade_shadow.h index 0c4eeb8d10d..1de890aae29 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_shadow.h +++ b/intern/cycles/kernel/integrator/shade_shadow.h @@ -16,9 +16,9 @@ #pragma once -#include "kernel/integrator/integrator_shade_volume.h" -#include "kernel/integrator/integrator_volume_stack.h" -#include "kernel/integrator/integrator_shader_eval.h" +#include "kernel/integrator/shade_volume.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/volume_stack.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h similarity index 98% rename from intern/cycles/kernel/integrator/integrator_shade_surface.h rename to intern/cycles/kernel/integrator/shade_surface.h index 70dce1c4913..cce591eb219 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -16,18 +16,18 @@ #pragma once -#include "kernel/film/film_accumulate.h" -#include "kernel/film/film_passes.h" +#include "kernel/film/accumulate.h" +#include "kernel/film/passes.h" -#include "kernel/integrator/integrator_path_state.h" -#include "kernel/integrator/integrator_shader_eval.h" -#include "kernel/integrator/integrator_subsurface.h" -#include "kernel/integrator/integrator_volume_stack.h" +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/subsurface.h" +#include "kernel/integrator/volume_stack.h" #include "kernel/light/light.h" -#include "kernel/light/light_sample.h" +#include "kernel/light/sample.h" -#include "kernel/sample/sample_mis.h" +#include "kernel/sample/mis.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h similarity index 99% rename from intern/cycles/kernel/integrator/integrator_shade_volume.h rename to intern/cycles/kernel/integrator/shade_volume.h index 44ef4803575..f455152dcf9 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -16,18 +16,18 @@ #pragma once -#include "kernel/film/film_accumulate.h" -#include "kernel/film/film_passes.h" +#include "kernel/film/accumulate.h" +#include "kernel/film/passes.h" -#include "kernel/integrator/integrator_path_state.h" -#include "kernel/integrator/integrator_shader_eval.h" -#include "kernel/integrator/integrator_intersect_closest.h" -#include "kernel/integrator/integrator_volume_stack.h" +#include "kernel/integrator/intersect_closest.h" +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/volume_stack.h" #include "kernel/light/light.h" -#include "kernel/light/light_sample.h" +#include "kernel/light/sample.h" -#include "kernel/sample/sample_mis.h" +#include "kernel/sample/mis.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shader_eval.h b/intern/cycles/kernel/integrator/shader_eval.h similarity index 99% rename from intern/cycles/kernel/integrator/integrator_shader_eval.h rename to intern/cycles/kernel/integrator/shader_eval.h index 04a3a965fd3..68f1ef8c118 100644 --- a/intern/cycles/kernel/integrator/integrator_shader_eval.h +++ b/intern/cycles/kernel/integrator/shader_eval.h @@ -19,16 +19,16 @@ #pragma once #include "kernel/closure/alloc.h" -#include "kernel/closure/bsdf_util.h" #include "kernel/closure/bsdf.h" +#include "kernel/closure/bsdf_util.h" #include "kernel/closure/emissive.h" -#include "kernel/film/film_accumulate.h" +#include "kernel/film/accumulate.h" #include "kernel/svm/svm.h" #ifdef __OSL__ -# include "kernel/osl/osl_shader.h" +# include "kernel/osl/shader.h" #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shadow_catcher.h b/intern/cycles/kernel/integrator/shadow_catcher.h similarity index 97% rename from intern/cycles/kernel/integrator/integrator_shadow_catcher.h rename to intern/cycles/kernel/integrator/shadow_catcher.h index 24d03466393..7beae235dbc 100644 --- a/intern/cycles/kernel/integrator/integrator_shadow_catcher.h +++ b/intern/cycles/kernel/integrator/shadow_catcher.h @@ -16,8 +16,8 @@ #pragma once -#include "kernel/integrator/integrator_path_state.h" -#include "kernel/integrator/integrator_state_util.h" +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/state_util.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_shadow_state_template.h b/intern/cycles/kernel/integrator/shadow_state_template.h similarity index 100% rename from intern/cycles/kernel/integrator/integrator_shadow_state_template.h rename to intern/cycles/kernel/integrator/shadow_state_template.h diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/state.h similarity index 95% rename from intern/cycles/kernel/integrator/integrator_state.h rename to intern/cycles/kernel/integrator/state.h index 09b399ff1b8..86dac0a65cf 100644 --- a/intern/cycles/kernel/integrator/integrator_state.h +++ b/intern/cycles/kernel/integrator/state.h @@ -40,9 +40,9 @@ * INTEGRATOR_STATE_NULL: use to pass empty state to other functions. */ -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "util/util_types.h" +#include "util/types.h" #pragma once @@ -64,7 +64,7 @@ typedef struct IntegratorShadowStateCPU { } \ name[cpu_size]; #define KERNEL_STRUCT_VOLUME_STACK_SIZE MAX_VOLUME_STACK_SIZE -#include "kernel/integrator/integrator_shadow_state_template.h" +#include "kernel/integrator/shadow_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER #undef KERNEL_STRUCT_ARRAY_MEMBER @@ -83,7 +83,7 @@ typedef struct IntegratorStateCPU { } \ name[cpu_size]; #define KERNEL_STRUCT_VOLUME_STACK_SIZE MAX_VOLUME_STACK_SIZE -#include "kernel/integrator/integrator_state_template.h" +#include "kernel/integrator/state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER #undef KERNEL_STRUCT_ARRAY_MEMBER @@ -118,9 +118,9 @@ typedef struct IntegratorStateGPU { name[gpu_size]; #define KERNEL_STRUCT_VOLUME_STACK_SIZE MAX_VOLUME_STACK_SIZE -#include "kernel/integrator/integrator_state_template.h" +#include "kernel/integrator/state_template.h" -#include "kernel/integrator/integrator_shadow_state_template.h" +#include "kernel/integrator/shadow_state_template.h" #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER diff --git a/intern/cycles/kernel/integrator/integrator_state_flow.h b/intern/cycles/kernel/integrator/state_flow.h similarity index 99% rename from intern/cycles/kernel/integrator/integrator_state_flow.h rename to intern/cycles/kernel/integrator/state_flow.h index 1569bf68e24..38a2b396847 100644 --- a/intern/cycles/kernel/integrator/integrator_state_flow.h +++ b/intern/cycles/kernel/integrator/state_flow.h @@ -16,8 +16,8 @@ #pragma once -#include "kernel/kernel_types.h" -#include "util/util_atomic.h" +#include "kernel/types.h" +#include "util/atomic.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_state_template.h b/intern/cycles/kernel/integrator/state_template.h similarity index 100% rename from intern/cycles/kernel/integrator/integrator_state_template.h rename to intern/cycles/kernel/integrator/state_template.h diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/state_util.h similarity index 98% rename from intern/cycles/kernel/integrator/integrator_state_util.h rename to intern/cycles/kernel/integrator/state_util.h index 0b1f67daa92..dafe06e7009 100644 --- a/intern/cycles/kernel/integrator/integrator_state_util.h +++ b/intern/cycles/kernel/integrator/state_util.h @@ -16,9 +16,9 @@ #pragma once -#include "kernel/integrator/integrator_state.h" +#include "kernel/integrator/state.h" -#include "kernel/util/util_differential.h" +#include "kernel/util/differential.h" CCL_NAMESPACE_BEGIN @@ -247,7 +247,7 @@ ccl_device_inline void integrator_state_copy_only(KernelGlobals kg, # define KERNEL_STRUCT_VOLUME_STACK_SIZE kernel_data.volume_stack_size -# include "kernel/integrator/integrator_state_template.h" +# include "kernel/integrator/state_template.h" # undef KERNEL_STRUCT_BEGIN # undef KERNEL_STRUCT_MEMBER @@ -303,7 +303,7 @@ ccl_device_inline void integrator_shadow_state_copy_only(KernelGlobals kg, # define KERNEL_STRUCT_VOLUME_STACK_SIZE kernel_data.volume_stack_size -# include "kernel/integrator/integrator_shadow_state_template.h" +# include "kernel/integrator/shadow_state_template.h" # undef KERNEL_STRUCT_BEGIN # undef KERNEL_STRUCT_MEMBER diff --git a/intern/cycles/kernel/integrator/integrator_subsurface.h b/intern/cycles/kernel/integrator/subsurface.h similarity index 95% rename from intern/cycles/kernel/integrator/integrator_subsurface.h rename to intern/cycles/kernel/integrator/subsurface.h index 9560641c460..49466112387 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface.h +++ b/intern/cycles/kernel/integrator/subsurface.h @@ -16,7 +16,7 @@ #pragma once -#include "kernel/camera/camera_projection.h" +#include "kernel/camera/projection.h" #include "kernel/bvh/bvh.h" @@ -26,11 +26,11 @@ #include "kernel/closure/bssrdf.h" #include "kernel/closure/volume.h" -#include "kernel/integrator/integrator_intersect_volume_stack.h" -#include "kernel/integrator/integrator_path_state.h" -#include "kernel/integrator/integrator_shader_eval.h" -#include "kernel/integrator/integrator_subsurface_disk.h" -#include "kernel/integrator/integrator_subsurface_random_walk.h" +#include "kernel/integrator/intersect_volume_stack.h" +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/subsurface_disk.h" +#include "kernel/integrator/subsurface_random_walk.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_disk.h b/intern/cycles/kernel/integrator/subsurface_disk.h similarity index 100% rename from intern/cycles/kernel/integrator/integrator_subsurface_disk.h rename to intern/cycles/kernel/integrator/subsurface_disk.h diff --git a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h b/intern/cycles/kernel/integrator/subsurface_random_walk.h similarity index 99% rename from intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h rename to intern/cycles/kernel/integrator/subsurface_random_walk.h index b98acda1f4d..f0712758174 100644 --- a/intern/cycles/kernel/integrator/integrator_subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/subsurface_random_walk.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "kernel/camera/camera_projection.h" +#include "kernel/camera/projection.h" #include "kernel/bvh/bvh.h" diff --git a/intern/cycles/kernel/integrator/integrator_volume_stack.h b/intern/cycles/kernel/integrator/volume_stack.h similarity index 100% rename from intern/cycles/kernel/integrator/integrator_volume_stack.h rename to intern/cycles/kernel/integrator/volume_stack.h diff --git a/intern/cycles/kernel/light/light_background.h b/intern/cycles/kernel/light/background.h similarity index 99% rename from intern/cycles/kernel/light/light_background.h rename to intern/cycles/kernel/light/background.h index 78f8c94f7a3..d801cc94393 100644 --- a/intern/cycles/kernel/light/light_background.h +++ b/intern/cycles/kernel/light/background.h @@ -16,7 +16,7 @@ #pragma once -#include "kernel/light/light_common.h" +#include "kernel/light/common.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/light/light_common.h b/intern/cycles/kernel/light/common.h similarity index 99% rename from intern/cycles/kernel/light/light_common.h rename to intern/cycles/kernel/light/common.h index 207e89090cc..75331d32d44 100644 --- a/intern/cycles/kernel/light/light_common.h +++ b/intern/cycles/kernel/light/common.h @@ -16,7 +16,7 @@ #pragma once -#include "kernel/sample/sample_mapping.h" +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h index facbbe23d0f..746c7747569 100644 --- a/intern/cycles/kernel/light/light.h +++ b/intern/cycles/kernel/light/light.h @@ -17,8 +17,8 @@ #pragma once #include "kernel/geom/geom.h" -#include "kernel/light/light_background.h" -#include "kernel/sample/sample_mapping.h" +#include "kernel/light/background.h" +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/light/light_sample.h b/intern/cycles/kernel/light/sample.h similarity index 98% rename from intern/cycles/kernel/light/light_sample.h rename to intern/cycles/kernel/light/sample.h index 4ae5d9e1944..6b643a95250 100644 --- a/intern/cycles/kernel/light/light_sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -16,12 +16,12 @@ #pragma once -#include "kernel/integrator/integrator_path_state.h" -#include "kernel/integrator/integrator_shader_eval.h" +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shader_eval.h" #include "kernel/light/light.h" -#include "kernel/sample/sample_mapping.h" +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/osl/CMakeLists.txt b/intern/cycles/kernel/osl/CMakeLists.txt index 7be1b7129e0..f226c95766f 100644 --- a/intern/cycles/kernel/osl/CMakeLists.txt +++ b/intern/cycles/kernel/osl/CMakeLists.txt @@ -25,17 +25,17 @@ set(SRC bsdf_diffuse_ramp.cpp bsdf_phong_ramp.cpp emissive.cpp - osl_bssrdf.cpp - osl_closures.cpp - osl_services.cpp - osl_shader.cpp + bssrdf.cpp + closures.cpp + services.cpp + shader.cpp ) set(HEADER_SRC - osl_closures.h - osl_globals.h - osl_services.h - osl_shader.h + closures.h + globals.h + services.h + shader.h ) set(LIB diff --git a/intern/cycles/kernel/osl/background.cpp b/intern/cycles/kernel/osl/background.cpp index bb290a5ced2..540180f99e8 100644 --- a/intern/cycles/kernel/osl/background.cpp +++ b/intern/cycles/kernel/osl/background.cpp @@ -34,7 +34,7 @@ #include -#include "kernel/osl/osl_closures.h" +#include "kernel/osl/closures.h" // clang-format off #include "kernel/device/cpu/compat.h" diff --git a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp index 2ec7f14c0fa..768531a0bf9 100644 --- a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp @@ -35,10 +35,10 @@ #include #include "kernel/device/cpu/compat.h" -#include "kernel/osl/osl_closures.h" +#include "kernel/osl/closures.h" // clang-format off -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_diffuse_ramp.h" // clang-format on diff --git a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp index 90160fba962..d34a33216a0 100644 --- a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp @@ -35,10 +35,10 @@ #include #include "kernel/device/cpu/compat.h" -#include "kernel/osl/osl_closures.h" +#include "kernel/osl/closures.h" // clang-format off -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_phong_ramp.h" // clang-format on diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/bssrdf.cpp similarity index 98% rename from intern/cycles/kernel/osl/osl_bssrdf.cpp rename to intern/cycles/kernel/osl/bssrdf.cpp index 3b8661ce45d..7c7f1ce157f 100644 --- a/intern/cycles/kernel/osl/osl_bssrdf.cpp +++ b/intern/cycles/kernel/osl/bssrdf.cpp @@ -33,10 +33,10 @@ #include #include "kernel/device/cpu/compat.h" -#include "kernel/osl/osl_closures.h" +#include "kernel/osl/closures.h" // clang-format off -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_util.h" diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/closures.cpp similarity index 99% rename from intern/cycles/kernel/osl/osl_closures.cpp rename to intern/cycles/kernel/osl/closures.cpp index 89bab35b60b..adc0f50aefb 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/closures.cpp @@ -33,17 +33,17 @@ #include #include -#include "kernel/osl/osl_closures.h" -#include "kernel/osl/osl_shader.h" +#include "kernel/osl/closures.h" +#include "kernel/osl/shader.h" -#include "util/util_math.h" -#include "util/util_param.h" +#include "util/math.h" +#include "util/param.h" // clang-format off #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_util.h" diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/closures.h similarity index 99% rename from intern/cycles/kernel/osl/osl_closures.h rename to intern/cycles/kernel/osl/closures.h index 7869d793737..8f573e89734 100644 --- a/intern/cycles/kernel/osl/osl_closures.h +++ b/intern/cycles/kernel/osl/closures.h @@ -33,8 +33,8 @@ #ifndef __OSL_CLOSURES_H__ #define __OSL_CLOSURES_H__ -#include "kernel/kernel_types.h" -#include "util/util_types.h" +#include "kernel/types.h" +#include "util/types.h" #include #include diff --git a/intern/cycles/kernel/osl/emissive.cpp b/intern/cycles/kernel/osl/emissive.cpp index 5a7fe14b22e..2615e300a92 100644 --- a/intern/cycles/kernel/osl/emissive.cpp +++ b/intern/cycles/kernel/osl/emissive.cpp @@ -34,11 +34,11 @@ #include -#include "kernel/osl/osl_closures.h" +#include "kernel/osl/closures.h" // clang-format off #include "kernel/device/cpu/compat.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/emissive.h" // clang-format on diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/globals.h similarity index 93% rename from intern/cycles/kernel/osl/osl_globals.h rename to intern/cycles/kernel/osl/globals.h index f1789f0d7eb..126ace0086e 100644 --- a/intern/cycles/kernel/osl/osl_globals.h +++ b/intern/cycles/kernel/osl/globals.h @@ -24,11 +24,11 @@ # include # include -# include "util/util_map.h" -# include "util/util_param.h" -# include "util/util_thread.h" -# include "util/util_unique_ptr.h" -# include "util/util_vector.h" +# include "util/map.h" +# include "util/param.h" +# include "util/thread.h" +# include "util/unique_ptr.h" +# include "util/vector.h" # ifndef WIN32 using std::isfinite; diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/services.cpp similarity index 98% rename from intern/cycles/kernel/osl/osl_services.cpp rename to intern/cycles/kernel/osl/services.cpp index 56b04fd280e..ca0a5a068b3 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -30,36 +30,36 @@ #include "scene/object.h" #include "scene/scene.h" -#include "kernel/osl/osl_closures.h" -#include "kernel/osl/osl_globals.h" -#include "kernel/osl/osl_services.h" -#include "kernel/osl/osl_shader.h" +#include "kernel/osl/closures.h" +#include "kernel/osl/globals.h" +#include "kernel/osl/services.h" +#include "kernel/osl/shader.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_string.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/string.h" // clang-format off #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" #include "kernel/device/cpu/image.h" -#include "kernel/util/util_differential.h" +#include "kernel/util/differential.h" -#include "kernel/integrator/integrator_state.h" -#include "kernel/integrator/integrator_state_flow.h" +#include "kernel/integrator/state.h" +#include "kernel/integrator/state_flow.h" #include "kernel/geom/geom.h" #include "kernel/bvh/bvh.h" #include "kernel/camera/camera.h" -#include "kernel/camera/camera_projection.h" +#include "kernel/camera/projection.h" -#include "kernel/integrator/integrator_path_state.h" -#include "kernel/integrator/integrator_shader_eval.h" +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shader_eval.h" -#include "kernel/util/util_color.h" +#include "kernel/util/color.h" // clang-format on CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/services.h similarity index 100% rename from intern/cycles/kernel/osl/osl_services.h rename to intern/cycles/kernel/osl/services.h diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/shader.cpp similarity index 98% rename from intern/cycles/kernel/osl/osl_shader.cpp rename to intern/cycles/kernel/osl/shader.cpp index 6426a09b33d..33633c69e29 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/shader.cpp @@ -20,16 +20,16 @@ #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "kernel/geom/geom_object.h" +#include "kernel/geom/object.h" -#include "kernel/integrator/integrator_state.h" +#include "kernel/integrator/state.h" -#include "kernel/osl/osl_closures.h" -#include "kernel/osl/osl_globals.h" -#include "kernel/osl/osl_services.h" -#include "kernel/osl/osl_shader.h" +#include "kernel/osl/closures.h" +#include "kernel/osl/globals.h" +#include "kernel/osl/services.h" +#include "kernel/osl/shader.h" // clang-format on #include "scene/attribute.h" diff --git a/intern/cycles/kernel/osl/osl_shader.h b/intern/cycles/kernel/osl/shader.h similarity index 98% rename from intern/cycles/kernel/osl/osl_shader.h rename to intern/cycles/kernel/osl/shader.h index 037a18a1f19..7d68d4eae7f 100644 --- a/intern/cycles/kernel/osl/osl_shader.h +++ b/intern/cycles/kernel/osl/shader.h @@ -29,7 +29,7 @@ * This means no thread state must be passed along in the kernel itself. */ -# include "kernel/kernel_types.h" +# include "kernel/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/sample/sample_jitter.h b/intern/cycles/kernel/sample/jitter.h similarity index 100% rename from intern/cycles/kernel/sample/sample_jitter.h rename to intern/cycles/kernel/sample/jitter.h diff --git a/intern/cycles/kernel/sample/lcg.h b/intern/cycles/kernel/sample/lcg.h new file mode 100644 index 00000000000..92cfff639b4 --- /dev/null +++ b/intern/cycles/kernel/sample/lcg.h @@ -0,0 +1,51 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Linear Congruential Generator */ + +ccl_device uint lcg_step_uint(uint *rng) +{ + /* implicit mod 2^32 */ + *rng = (1103515245 * (*rng) + 12345); + return *rng; +} + +ccl_device float lcg_step_float(uint *rng) +{ + /* implicit mod 2^32 */ + *rng = (1103515245 * (*rng) + 12345); + return (float)*rng * (1.0f / (float)0xFFFFFFFF); +} + +ccl_device uint lcg_init(uint seed) +{ + uint rng = seed; + lcg_step_uint(&rng); + return rng; +} + +ccl_device_inline uint lcg_state_init(const uint rng_hash, + const uint rng_offset, + const uint sample, + const uint scramble) +{ + return lcg_init(rng_hash + rng_offset + sample * scramble); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/sample/sample_mapping.h b/intern/cycles/kernel/sample/mapping.h similarity index 100% rename from intern/cycles/kernel/sample/sample_mapping.h rename to intern/cycles/kernel/sample/mapping.h diff --git a/intern/cycles/kernel/sample/mis.h b/intern/cycles/kernel/sample/mis.h new file mode 100644 index 00000000000..0878b3aac36 --- /dev/null +++ b/intern/cycles/kernel/sample/mis.h @@ -0,0 +1,64 @@ +/* + * Parts adapted from Open Shading Language with this license: + * + * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. + * All Rights Reserved. + * + * Modifications Copyright 2011, Blender Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sony Pictures Imageworks nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Multiple importance sampling utilities. */ + +ccl_device float balance_heuristic(float a, float b) +{ + return (a) / (a + b); +} + +ccl_device float balance_heuristic_3(float a, float b, float c) +{ + return (a) / (a + b + c); +} + +ccl_device float power_heuristic(float a, float b) +{ + return (a * a) / (a * a + b * b); +} + +ccl_device float power_heuristic_3(float a, float b, float c) +{ + return (a * a) / (a * a + b * b + c * c); +} + +ccl_device float max_heuristic(float a, float b) +{ + return (a > b) ? 1.0f : 0.0f; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/sample/sample_pattern.h b/intern/cycles/kernel/sample/pattern.h similarity index 98% rename from intern/cycles/kernel/sample/sample_pattern.h rename to intern/cycles/kernel/sample/pattern.h index 95635c2c855..191b24a5f2a 100644 --- a/intern/cycles/kernel/sample/sample_pattern.h +++ b/intern/cycles/kernel/sample/pattern.h @@ -15,8 +15,8 @@ */ #pragma once -#include "kernel/sample/sample_jitter.h" -#include "util/util_hash.h" +#include "kernel/sample/jitter.h" +#include "util/hash.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/svm/svm_ao.h b/intern/cycles/kernel/svm/ao.h similarity index 99% rename from intern/cycles/kernel/svm/svm_ao.h rename to intern/cycles/kernel/svm/ao.h index 4cfef7bc204..678f49c8ccd 100644 --- a/intern/cycles/kernel/svm/svm_ao.h +++ b/intern/cycles/kernel/svm/ao.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + #include "kernel/bvh/bvh.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/aov.h similarity index 98% rename from intern/cycles/kernel/svm/svm_aov.h rename to intern/cycles/kernel/svm/aov.h index 92be7fb6906..21ee7af7639 100644 --- a/intern/cycles/kernel/svm/svm_aov.h +++ b/intern/cycles/kernel/svm/aov.h @@ -14,7 +14,9 @@ * limitations under the License. */ -#include "kernel/film/film_write_passes.h" +#pragma once + +#include "kernel/film/write_passes.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/svm/svm_attribute.h b/intern/cycles/kernel/svm/attribute.h similarity index 99% rename from intern/cycles/kernel/svm/svm_attribute.h rename to intern/cycles/kernel/svm/attribute.h index b3c66d29f5c..e9de0164c7a 100644 --- a/intern/cycles/kernel/svm/svm_attribute.h +++ b/intern/cycles/kernel/svm/attribute.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Attribute Node */ diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/bevel.h similarity index 99% rename from intern/cycles/kernel/svm/svm_bevel.h rename to intern/cycles/kernel/svm/bevel.h index 0a30822aa68..37c7caf1372 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/bevel.h @@ -14,9 +14,11 @@ * limitations under the License. */ +#pragma once + #include "kernel/bvh/bvh.h" -#include "kernel/sample/sample_mapping.h" -#include "kernel/sample/sample_pattern.h" +#include "kernel/sample/mapping.h" +#include "kernel/sample/pattern.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/svm/svm_blackbody.h b/intern/cycles/kernel/svm/blackbody.h similarity index 97% rename from intern/cycles/kernel/svm/svm_blackbody.h rename to intern/cycles/kernel/svm/blackbody.h index f1adb0e76af..da15550f918 100644 --- a/intern/cycles/kernel/svm/svm_blackbody.h +++ b/intern/cycles/kernel/svm/blackbody.h @@ -30,6 +30,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + +#include "kernel/svm/math_util.h" + CCL_NAMESPACE_BEGIN /* Blackbody Node */ diff --git a/intern/cycles/kernel/svm/svm_brick.h b/intern/cycles/kernel/svm/brick.h similarity index 99% rename from intern/cycles/kernel/svm/svm_brick.h rename to intern/cycles/kernel/svm/brick.h index 9dc31ef37ec..3c8729fa027 100644 --- a/intern/cycles/kernel/svm/svm_brick.h +++ b/intern/cycles/kernel/svm/brick.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Brick */ diff --git a/intern/cycles/kernel/svm/svm_brightness.h b/intern/cycles/kernel/svm/brightness.h similarity index 96% rename from intern/cycles/kernel/svm/svm_brightness.h rename to intern/cycles/kernel/svm/brightness.h index 0a44ffe6359..5c82a4347cd 100644 --- a/intern/cycles/kernel/svm/svm_brightness.h +++ b/intern/cycles/kernel/svm/brightness.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +#include "kernel/svm/color_util.h" + CCL_NAMESPACE_BEGIN ccl_device_noinline void svm_node_brightness( diff --git a/intern/cycles/kernel/svm/svm_bump.h b/intern/cycles/kernel/svm/bump.h similarity index 99% rename from intern/cycles/kernel/svm/svm_bump.h rename to intern/cycles/kernel/svm/bump.h index 66e5b665532..2fae06fa54b 100644 --- a/intern/cycles/kernel/svm/svm_bump.h +++ b/intern/cycles/kernel/svm/bump.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Bump Eval Nodes */ diff --git a/intern/cycles/kernel/svm/svm_camera.h b/intern/cycles/kernel/svm/camera.h similarity index 99% rename from intern/cycles/kernel/svm/svm_camera.h rename to intern/cycles/kernel/svm/camera.h index 787f11f38b5..c71c02e6b19 100644 --- a/intern/cycles/kernel/svm/svm_camera.h +++ b/intern/cycles/kernel/svm/camera.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device_noinline void svm_node_camera(KernelGlobals kg, diff --git a/intern/cycles/kernel/svm/svm_checker.h b/intern/cycles/kernel/svm/checker.h similarity index 99% rename from intern/cycles/kernel/svm/svm_checker.h rename to intern/cycles/kernel/svm/checker.h index 9251d90c0e1..a79b1651f44 100644 --- a/intern/cycles/kernel/svm/svm_checker.h +++ b/intern/cycles/kernel/svm/checker.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Checker */ diff --git a/intern/cycles/kernel/svm/svm_clamp.h b/intern/cycles/kernel/svm/clamp.h similarity index 99% rename from intern/cycles/kernel/svm/svm_clamp.h rename to intern/cycles/kernel/svm/clamp.h index 5b5ea784f4a..c07c0206d29 100644 --- a/intern/cycles/kernel/svm/svm_clamp.h +++ b/intern/cycles/kernel/svm/clamp.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Clamp Node */ diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/closure.h similarity index 99% rename from intern/cycles/kernel/svm/svm_closure.h rename to intern/cycles/kernel/svm/closure.h index 3378832c233..1dcfe003f74 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Closure Nodes */ diff --git a/intern/cycles/kernel/svm/svm_color_util.h b/intern/cycles/kernel/svm/color_util.h similarity index 99% rename from intern/cycles/kernel/svm/svm_color_util.h rename to intern/cycles/kernel/svm/color_util.h index 1a0fa03305e..82024b61ba4 100644 --- a/intern/cycles/kernel/svm/svm_color_util.h +++ b/intern/cycles/kernel/svm/color_util.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device float3 svm_mix_blend(float t, float3 col1, float3 col2) diff --git a/intern/cycles/kernel/svm/svm_convert.h b/intern/cycles/kernel/svm/convert.h similarity index 99% rename from intern/cycles/kernel/svm/svm_convert.h rename to intern/cycles/kernel/svm/convert.h index ec5745dc78a..427ffd97f59 100644 --- a/intern/cycles/kernel/svm/svm_convert.h +++ b/intern/cycles/kernel/svm/convert.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Conversion Nodes */ diff --git a/intern/cycles/kernel/svm/svm_displace.h b/intern/cycles/kernel/svm/displace.h similarity index 99% rename from intern/cycles/kernel/svm/svm_displace.h rename to intern/cycles/kernel/svm/displace.h index 8429fe1d3e0..cea1436f36d 100644 --- a/intern/cycles/kernel/svm/svm_displace.h +++ b/intern/cycles/kernel/svm/displace.h @@ -14,7 +14,9 @@ * limitations under the License. */ -#include "kernel/sample/sample_mapping.h" +#pragma once + +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/svm/svm_fractal_noise.h b/intern/cycles/kernel/svm/fractal_noise.h similarity index 98% rename from intern/cycles/kernel/svm/svm_fractal_noise.h rename to intern/cycles/kernel/svm/fractal_noise.h index 57fa8c690ac..b955d626dde 100644 --- a/intern/cycles/kernel/svm/svm_fractal_noise.h +++ b/intern/cycles/kernel/svm/fractal_noise.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +#include "kernel/svm/noise.h" + CCL_NAMESPACE_BEGIN /* The fractal_noise_[1-4] functions are all exactly the same except for the input type. */ diff --git a/intern/cycles/kernel/svm/svm_fresnel.h b/intern/cycles/kernel/svm/fresnel.h similarity index 99% rename from intern/cycles/kernel/svm/svm_fresnel.h rename to intern/cycles/kernel/svm/fresnel.h index 449ec84370f..9dd68c3e38f 100644 --- a/intern/cycles/kernel/svm/svm_fresnel.h +++ b/intern/cycles/kernel/svm/fresnel.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Fresnel Node */ diff --git a/intern/cycles/kernel/svm/svm_gamma.h b/intern/cycles/kernel/svm/gamma.h similarity index 98% rename from intern/cycles/kernel/svm/svm_gamma.h rename to intern/cycles/kernel/svm/gamma.h index 7ec6c31065d..9f89e780be9 100644 --- a/intern/cycles/kernel/svm/svm_gamma.h +++ b/intern/cycles/kernel/svm/gamma.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device_noinline void svm_node_gamma(ccl_private ShaderData *sd, diff --git a/intern/cycles/kernel/svm/svm_geometry.h b/intern/cycles/kernel/svm/geometry.h similarity index 99% rename from intern/cycles/kernel/svm/svm_geometry.h rename to intern/cycles/kernel/svm/geometry.h index b29bfdbed07..772942e0c08 100644 --- a/intern/cycles/kernel/svm/svm_geometry.h +++ b/intern/cycles/kernel/svm/geometry.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Geometry Node */ diff --git a/intern/cycles/kernel/svm/svm_gradient.h b/intern/cycles/kernel/svm/gradient.h similarity index 99% rename from intern/cycles/kernel/svm/svm_gradient.h rename to intern/cycles/kernel/svm/gradient.h index 8cc37be606f..852196b73dc 100644 --- a/intern/cycles/kernel/svm/svm_gradient.h +++ b/intern/cycles/kernel/svm/gradient.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Gradient */ diff --git a/intern/cycles/kernel/svm/svm_hsv.h b/intern/cycles/kernel/svm/hsv.h similarity index 96% rename from intern/cycles/kernel/svm/svm_hsv.h rename to intern/cycles/kernel/svm/hsv.h index 978c4c2d781..f6881fd4512 100644 --- a/intern/cycles/kernel/svm/svm_hsv.h +++ b/intern/cycles/kernel/svm/hsv.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __SVM_HSV_H__ -#define __SVM_HSV_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -60,5 +59,3 @@ ccl_device_noinline void svm_node_hsv(KernelGlobals kg, } CCL_NAMESPACE_END - -#endif /* __SVM_HSV_H__ */ diff --git a/intern/cycles/kernel/svm/svm_ies.h b/intern/cycles/kernel/svm/ies.h similarity index 99% rename from intern/cycles/kernel/svm/svm_ies.h rename to intern/cycles/kernel/svm/ies.h index 0215670d062..f0923720878 100644 --- a/intern/cycles/kernel/svm/svm_ies.h +++ b/intern/cycles/kernel/svm/ies.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* IES Light */ diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/image.h similarity index 99% rename from intern/cycles/kernel/svm/svm_image.h rename to intern/cycles/kernel/svm/image.h index 68374fcfb0d..6ddf98a6ef1 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/image.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device float4 svm_image_texture(KernelGlobals kg, int id, float x, float y, uint flags) diff --git a/intern/cycles/kernel/svm/svm_invert.h b/intern/cycles/kernel/svm/invert.h similarity index 98% rename from intern/cycles/kernel/svm/svm_invert.h rename to intern/cycles/kernel/svm/invert.h index 60668ec00f1..5a88e9df2c9 100644 --- a/intern/cycles/kernel/svm/svm_invert.h +++ b/intern/cycles/kernel/svm/invert.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor) diff --git a/intern/cycles/kernel/svm/svm_light_path.h b/intern/cycles/kernel/svm/light_path.h similarity index 99% rename from intern/cycles/kernel/svm/svm_light_path.h rename to intern/cycles/kernel/svm/light_path.h index 5e1fc4f671c..44a35b568fa 100644 --- a/intern/cycles/kernel/svm/svm_light_path.h +++ b/intern/cycles/kernel/svm/light_path.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Light Path Node */ diff --git a/intern/cycles/kernel/svm/svm_magic.h b/intern/cycles/kernel/svm/magic.h similarity index 99% rename from intern/cycles/kernel/svm/svm_magic.h rename to intern/cycles/kernel/svm/magic.h index d3a429fec56..f103a8eebcc 100644 --- a/intern/cycles/kernel/svm/svm_magic.h +++ b/intern/cycles/kernel/svm/magic.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Magic */ diff --git a/intern/cycles/kernel/svm/svm_map_range.h b/intern/cycles/kernel/svm/map_range.h similarity index 99% rename from intern/cycles/kernel/svm/svm_map_range.h rename to intern/cycles/kernel/svm/map_range.h index 5e89947c6c7..fdbfc6531c4 100644 --- a/intern/cycles/kernel/svm/svm_map_range.h +++ b/intern/cycles/kernel/svm/map_range.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Map Range Node */ diff --git a/intern/cycles/kernel/svm/svm_mapping.h b/intern/cycles/kernel/svm/mapping.h similarity index 98% rename from intern/cycles/kernel/svm/svm_mapping.h rename to intern/cycles/kernel/svm/mapping.h index ed420e5bc3d..19f79471ad2 100644 --- a/intern/cycles/kernel/svm/svm_mapping.h +++ b/intern/cycles/kernel/svm/mapping.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +#include "kernel/svm/mapping_util.h" + CCL_NAMESPACE_BEGIN /* Mapping Node */ diff --git a/intern/cycles/kernel/svm/svm_mapping_util.h b/intern/cycles/kernel/svm/mapping_util.h similarity index 99% rename from intern/cycles/kernel/svm/svm_mapping_util.h rename to intern/cycles/kernel/svm/mapping_util.h index ec2c84e0791..51b13c0c264 100644 --- a/intern/cycles/kernel/svm/svm_mapping_util.h +++ b/intern/cycles/kernel/svm/mapping_util.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device float3 diff --git a/intern/cycles/kernel/svm/svm_math.h b/intern/cycles/kernel/svm/math.h similarity index 99% rename from intern/cycles/kernel/svm/svm_math.h rename to intern/cycles/kernel/svm/math.h index 97f7d486c09..ff0f3683ea3 100644 --- a/intern/cycles/kernel/svm/svm_math.h +++ b/intern/cycles/kernel/svm/math.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device_noinline void svm_node_math(KernelGlobals kg, diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/math_util.h similarity index 99% rename from intern/cycles/kernel/svm/svm_math_util.h rename to intern/cycles/kernel/svm/math_util.h index d3225b55ef0..b2e539cdd1f 100644 --- a/intern/cycles/kernel/svm/svm_math_util.h +++ b/intern/cycles/kernel/svm/math_util.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device void svm_vector_math(ccl_private float *value, diff --git a/intern/cycles/kernel/svm/svm_mix.h b/intern/cycles/kernel/svm/mix.h similarity index 99% rename from intern/cycles/kernel/svm/svm_mix.h rename to intern/cycles/kernel/svm/mix.h index 568dda3dddc..96e5b3f5b5e 100644 --- a/intern/cycles/kernel/svm/svm_mix.h +++ b/intern/cycles/kernel/svm/mix.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Node */ diff --git a/intern/cycles/kernel/svm/svm_musgrave.h b/intern/cycles/kernel/svm/musgrave.h similarity index 99% rename from intern/cycles/kernel/svm/svm_musgrave.h rename to intern/cycles/kernel/svm/musgrave.h index decd29bbe13..4225c3d2d71 100644 --- a/intern/cycles/kernel/svm/svm_musgrave.h +++ b/intern/cycles/kernel/svm/musgrave.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +#include "kernel/svm/noise.h" + CCL_NAMESPACE_BEGIN /* 1D Musgrave fBm diff --git a/intern/cycles/kernel/svm/svm_noise.h b/intern/cycles/kernel/svm/noise.h similarity index 99% rename from intern/cycles/kernel/svm/svm_noise.h rename to intern/cycles/kernel/svm/noise.h index ecb4df6afdf..0a1616226db 100644 --- a/intern/cycles/kernel/svm/svm_noise.h +++ b/intern/cycles/kernel/svm/noise.h @@ -30,6 +30,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + CCL_NAMESPACE_BEGIN /* **** Perlin Noise **** */ diff --git a/intern/cycles/kernel/svm/svm_noisetex.h b/intern/cycles/kernel/svm/noisetex.h similarity index 99% rename from intern/cycles/kernel/svm/svm_noisetex.h rename to intern/cycles/kernel/svm/noisetex.h index 3fe33f72b59..c43c3b9f9d2 100644 --- a/intern/cycles/kernel/svm/svm_noisetex.h +++ b/intern/cycles/kernel/svm/noisetex.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +#include "kernel/svm/fractal_noise.h" + CCL_NAMESPACE_BEGIN /* The following offset functions generate random offsets to be added to texture diff --git a/intern/cycles/kernel/svm/svm_normal.h b/intern/cycles/kernel/svm/normal.h similarity index 99% rename from intern/cycles/kernel/svm/svm_normal.h rename to intern/cycles/kernel/svm/normal.h index 9bf64ed8823..6a2d88b68a6 100644 --- a/intern/cycles/kernel/svm/svm_normal.h +++ b/intern/cycles/kernel/svm/normal.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device_noinline int svm_node_normal(KernelGlobals kg, diff --git a/intern/cycles/kernel/svm/svm_ramp.h b/intern/cycles/kernel/svm/ramp.h similarity index 98% rename from intern/cycles/kernel/svm/svm_ramp.h rename to intern/cycles/kernel/svm/ramp.h index d2dddf4c6eb..1dc3383956d 100644 --- a/intern/cycles/kernel/svm/svm_ramp.h +++ b/intern/cycles/kernel/svm/ramp.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __SVM_RAMP_H__ -#define __SVM_RAMP_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -164,5 +163,3 @@ ccl_device_noinline int svm_node_curve( } CCL_NAMESPACE_END - -#endif /* __SVM_RAMP_H__ */ diff --git a/intern/cycles/kernel/svm/svm_ramp_util.h b/intern/cycles/kernel/svm/ramp_util.h similarity index 96% rename from intern/cycles/kernel/svm/svm_ramp_util.h rename to intern/cycles/kernel/svm/ramp_util.h index 202596c1fe3..f5951f7e283 100644 --- a/intern/cycles/kernel/svm/svm_ramp_util.h +++ b/intern/cycles/kernel/svm/ramp_util.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __SVM_RAMP_UTIL_H__ -#define __SVM_RAMP_UTIL_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -86,5 +85,3 @@ ccl_device float float_ramp_lookup( } CCL_NAMESPACE_END - -#endif /* __SVM_RAMP_UTIL_H__ */ diff --git a/intern/cycles/kernel/svm/svm_sepcomb_hsv.h b/intern/cycles/kernel/svm/sepcomb_hsv.h similarity index 99% rename from intern/cycles/kernel/svm/svm_sepcomb_hsv.h rename to intern/cycles/kernel/svm/sepcomb_hsv.h index bafa0456342..941a83e85b3 100644 --- a/intern/cycles/kernel/svm/svm_sepcomb_hsv.h +++ b/intern/cycles/kernel/svm/sepcomb_hsv.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device_noinline int svm_node_combine_hsv(KernelGlobals kg, diff --git a/intern/cycles/kernel/svm/svm_sepcomb_vector.h b/intern/cycles/kernel/svm/sepcomb_vector.h similarity index 99% rename from intern/cycles/kernel/svm/svm_sepcomb_vector.h rename to intern/cycles/kernel/svm/sepcomb_vector.h index 11e440f2cbf..acdea741aed 100644 --- a/intern/cycles/kernel/svm/svm_sepcomb_vector.h +++ b/intern/cycles/kernel/svm/sepcomb_vector.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Vector combine / separate, used for the RGB and XYZ nodes */ diff --git a/intern/cycles/kernel/svm/svm_sky.h b/intern/cycles/kernel/svm/sky.h similarity index 99% rename from intern/cycles/kernel/svm/svm_sky.h rename to intern/cycles/kernel/svm/sky.h index 3ab7bc89c66..867fdfc2a3f 100644 --- a/intern/cycles/kernel/svm/svm_sky.h +++ b/intern/cycles/kernel/svm/sky.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Sky texture */ diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 472f3517839..62ba5bf04e3 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __SVM_H__ -#define __SVM_H__ +#pragma once /* Shader Virtual Machine * @@ -38,7 +37,7 @@ * mostly taken care of in the SVM compiler. */ -#include "kernel/svm/svm_types.h" +#include "kernel/svm/types.h" CCL_NAMESPACE_BEGIN @@ -165,61 +164,54 @@ CCL_NAMESPACE_END /* Nodes */ -#include "kernel/svm/svm_noise.h" -#include "svm_fractal_noise.h" - -#include "kernel/svm/svm_color_util.h" -#include "kernel/svm/svm_mapping_util.h" -#include "kernel/svm/svm_math_util.h" - -#include "kernel/svm/svm_aov.h" -#include "kernel/svm/svm_attribute.h" -#include "kernel/svm/svm_blackbody.h" -#include "kernel/svm/svm_brick.h" -#include "kernel/svm/svm_brightness.h" -#include "kernel/svm/svm_bump.h" -#include "kernel/svm/svm_camera.h" -#include "kernel/svm/svm_checker.h" -#include "kernel/svm/svm_clamp.h" -#include "kernel/svm/svm_closure.h" -#include "kernel/svm/svm_convert.h" -#include "kernel/svm/svm_displace.h" -#include "kernel/svm/svm_fresnel.h" -#include "kernel/svm/svm_gamma.h" -#include "kernel/svm/svm_geometry.h" -#include "kernel/svm/svm_gradient.h" -#include "kernel/svm/svm_hsv.h" -#include "kernel/svm/svm_ies.h" -#include "kernel/svm/svm_image.h" -#include "kernel/svm/svm_invert.h" -#include "kernel/svm/svm_light_path.h" -#include "kernel/svm/svm_magic.h" -#include "kernel/svm/svm_map_range.h" -#include "kernel/svm/svm_mapping.h" -#include "kernel/svm/svm_math.h" -#include "kernel/svm/svm_mix.h" -#include "kernel/svm/svm_musgrave.h" -#include "kernel/svm/svm_noisetex.h" -#include "kernel/svm/svm_normal.h" -#include "kernel/svm/svm_ramp.h" -#include "kernel/svm/svm_sepcomb_hsv.h" -#include "kernel/svm/svm_sepcomb_vector.h" -#include "kernel/svm/svm_sky.h" -#include "kernel/svm/svm_tex_coord.h" -#include "kernel/svm/svm_value.h" -#include "kernel/svm/svm_vector_rotate.h" -#include "kernel/svm/svm_vector_transform.h" -#include "kernel/svm/svm_vertex_color.h" -#include "kernel/svm/svm_voronoi.h" -#include "kernel/svm/svm_voxel.h" -#include "kernel/svm/svm_wave.h" -#include "kernel/svm/svm_wavelength.h" -#include "kernel/svm/svm_white_noise.h" -#include "kernel/svm/svm_wireframe.h" +#include "kernel/svm/aov.h" +#include "kernel/svm/attribute.h" +#include "kernel/svm/blackbody.h" +#include "kernel/svm/brick.h" +#include "kernel/svm/brightness.h" +#include "kernel/svm/bump.h" +#include "kernel/svm/camera.h" +#include "kernel/svm/checker.h" +#include "kernel/svm/clamp.h" +#include "kernel/svm/closure.h" +#include "kernel/svm/convert.h" +#include "kernel/svm/displace.h" +#include "kernel/svm/fresnel.h" +#include "kernel/svm/gamma.h" +#include "kernel/svm/geometry.h" +#include "kernel/svm/gradient.h" +#include "kernel/svm/hsv.h" +#include "kernel/svm/ies.h" +#include "kernel/svm/image.h" +#include "kernel/svm/invert.h" +#include "kernel/svm/light_path.h" +#include "kernel/svm/magic.h" +#include "kernel/svm/map_range.h" +#include "kernel/svm/mapping.h" +#include "kernel/svm/math.h" +#include "kernel/svm/mix.h" +#include "kernel/svm/musgrave.h" +#include "kernel/svm/noisetex.h" +#include "kernel/svm/normal.h" +#include "kernel/svm/ramp.h" +#include "kernel/svm/sepcomb_hsv.h" +#include "kernel/svm/sepcomb_vector.h" +#include "kernel/svm/sky.h" +#include "kernel/svm/tex_coord.h" +#include "kernel/svm/value.h" +#include "kernel/svm/vector_rotate.h" +#include "kernel/svm/vector_transform.h" +#include "kernel/svm/vertex_color.h" +#include "kernel/svm/voronoi.h" +#include "kernel/svm/voxel.h" +#include "kernel/svm/wave.h" +#include "kernel/svm/wavelength.h" +#include "kernel/svm/white_noise.h" +#include "kernel/svm/wireframe.h" #ifdef __SHADER_RAYTRACE__ -# include "kernel/svm/svm_ao.h" -# include "kernel/svm/svm_bevel.h" +# include "kernel/svm/ao.h" +# include "kernel/svm/bevel.h" #endif CCL_NAMESPACE_BEGIN @@ -607,5 +599,3 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, } CCL_NAMESPACE_END - -#endif /* __SVM_H__ */ diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/tex_coord.h similarity index 99% rename from intern/cycles/kernel/svm/svm_tex_coord.h rename to intern/cycles/kernel/svm/tex_coord.h index 9af0a818cad..5e0debc968a 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/tex_coord.h @@ -14,9 +14,11 @@ * limitations under the License. */ +#pragma once + #include "kernel/camera/camera.h" #include "kernel/geom/geom.h" -#include "kernel/sample/sample_mapping.h" +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/types.h similarity index 99% rename from intern/cycles/kernel/svm/svm_types.h rename to intern/cycles/kernel/svm/types.h index 6f6c101fb69..8c95c571815 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/types.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __SVM_TYPES_H__ -#define __SVM_TYPES_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -600,5 +599,3 @@ typedef enum ClosureType { #define CLOSURE_WEIGHT_CUTOFF 1e-5f CCL_NAMESPACE_END - -#endif /* __SVM_TYPES_H__ */ diff --git a/intern/cycles/kernel/svm/svm_value.h b/intern/cycles/kernel/svm/value.h similarity index 99% rename from intern/cycles/kernel/svm/svm_value.h rename to intern/cycles/kernel/svm/value.h index cc72961d0f6..cc62f1e2a82 100644 --- a/intern/cycles/kernel/svm/svm_value.h +++ b/intern/cycles/kernel/svm/value.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Value Nodes */ diff --git a/intern/cycles/kernel/svm/svm_vector_rotate.h b/intern/cycles/kernel/svm/vector_rotate.h similarity index 99% rename from intern/cycles/kernel/svm/svm_vector_rotate.h rename to intern/cycles/kernel/svm/vector_rotate.h index c20f9b2556f..2a0d331734c 100644 --- a/intern/cycles/kernel/svm/svm_vector_rotate.h +++ b/intern/cycles/kernel/svm/vector_rotate.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Vector Rotate */ diff --git a/intern/cycles/kernel/svm/svm_vector_transform.h b/intern/cycles/kernel/svm/vector_transform.h similarity index 99% rename from intern/cycles/kernel/svm/svm_vector_transform.h rename to intern/cycles/kernel/svm/vector_transform.h index 4e0d36647da..d7a51078cea 100644 --- a/intern/cycles/kernel/svm/svm_vector_transform.h +++ b/intern/cycles/kernel/svm/vector_transform.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Vector Transform */ diff --git a/intern/cycles/kernel/svm/svm_vertex_color.h b/intern/cycles/kernel/svm/vertex_color.h similarity index 99% rename from intern/cycles/kernel/svm/svm_vertex_color.h rename to intern/cycles/kernel/svm/vertex_color.h index a5fa15ee085..b676a28c0e3 100644 --- a/intern/cycles/kernel/svm/svm_vertex_color.h +++ b/intern/cycles/kernel/svm/vertex_color.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device_noinline void svm_node_vertex_color(KernelGlobals kg, diff --git a/intern/cycles/kernel/svm/svm_voronoi.h b/intern/cycles/kernel/svm/voronoi.h similarity index 99% rename from intern/cycles/kernel/svm/svm_voronoi.h rename to intern/cycles/kernel/svm/voronoi.h index b8067520770..730965b6aed 100644 --- a/intern/cycles/kernel/svm/svm_voronoi.h +++ b/intern/cycles/kernel/svm/voronoi.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* diff --git a/intern/cycles/kernel/svm/svm_voxel.h b/intern/cycles/kernel/svm/voxel.h similarity index 99% rename from intern/cycles/kernel/svm/svm_voxel.h rename to intern/cycles/kernel/svm/voxel.h index be4bb315145..43947fbc54f 100644 --- a/intern/cycles/kernel/svm/svm_voxel.h +++ b/intern/cycles/kernel/svm/voxel.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* TODO(sergey): Think of making it more generic volume-type attribute diff --git a/intern/cycles/kernel/svm/svm_wave.h b/intern/cycles/kernel/svm/wave.h similarity index 99% rename from intern/cycles/kernel/svm/svm_wave.h rename to intern/cycles/kernel/svm/wave.h index d04b7aa3476..40e71b9d5df 100644 --- a/intern/cycles/kernel/svm/svm_wave.h +++ b/intern/cycles/kernel/svm/wave.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Wave */ diff --git a/intern/cycles/kernel/svm/svm_wavelength.h b/intern/cycles/kernel/svm/wavelength.h similarity index 99% rename from intern/cycles/kernel/svm/svm_wavelength.h rename to intern/cycles/kernel/svm/wavelength.h index 4ef041f68d5..28fd172abc7 100644 --- a/intern/cycles/kernel/svm/svm_wavelength.h +++ b/intern/cycles/kernel/svm/wavelength.h @@ -30,6 +30,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Wavelength to RGB */ diff --git a/intern/cycles/kernel/svm/svm_white_noise.h b/intern/cycles/kernel/svm/white_noise.h similarity index 99% rename from intern/cycles/kernel/svm/svm_white_noise.h rename to intern/cycles/kernel/svm/white_noise.h index 6c2c3d6a683..d275a3f7068 100644 --- a/intern/cycles/kernel/svm/svm_white_noise.h +++ b/intern/cycles/kernel/svm/white_noise.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device_noinline void svm_node_tex_white_noise(KernelGlobals kg, diff --git a/intern/cycles/kernel/svm/svm_wireframe.h b/intern/cycles/kernel/svm/wireframe.h similarity index 99% rename from intern/cycles/kernel/svm/svm_wireframe.h rename to intern/cycles/kernel/svm/wireframe.h index d75976d23e1..530a9601bce 100644 --- a/intern/cycles/kernel/svm/svm_wireframe.h +++ b/intern/cycles/kernel/svm/wireframe.h @@ -30,6 +30,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Wireframe Node */ diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/textures.h similarity index 100% rename from intern/cycles/kernel/kernel_textures.h rename to intern/cycles/kernel/textures.h diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/types.h similarity index 99% rename from intern/cycles/kernel/kernel_types.h rename to intern/cycles/kernel/types.h index 4312c1b67d2..4109dd6a486 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/types.h @@ -22,15 +22,15 @@ # define __EMBREE__ #endif -#include "util/util_math.h" -#include "util/util_math_fast.h" -#include "util/util_math_intersect.h" -#include "util/util_projection.h" -#include "util/util_texture.h" -#include "util/util_transform.h" -#include "util/util_static_assert.h" +#include "util/math.h" +#include "util/math_fast.h" +#include "util/math_intersect.h" +#include "util/projection.h" +#include "util/static_assert.h" +#include "util/texture.h" +#include "util/transform.h" -#include "kernel/svm/svm_types.h" +#include "kernel/svm/types.h" #ifndef __KERNEL_GPU__ # define __KERNEL_CPU__ diff --git a/intern/cycles/kernel/util/util_color.h b/intern/cycles/kernel/util/color.h similarity index 97% rename from intern/cycles/kernel/util/util_color.h rename to intern/cycles/kernel/util/color.h index 0d7bfecd5f3..6d17647c9f8 100644 --- a/intern/cycles/kernel/util/util_color.h +++ b/intern/cycles/kernel/util/color.h @@ -16,7 +16,7 @@ #pragma once -#include "util/util_color.h" +#include "util/color.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/util/util_differential.h b/intern/cycles/kernel/util/differential.h similarity index 100% rename from intern/cycles/kernel/util/util_differential.h rename to intern/cycles/kernel/util/differential.h diff --git a/intern/cycles/kernel/util/util_lookup_table.h b/intern/cycles/kernel/util/lookup_table.h similarity index 100% rename from intern/cycles/kernel/util/util_lookup_table.h rename to intern/cycles/kernel/util/lookup_table.h diff --git a/intern/cycles/kernel/util/util_profiling.h b/intern/cycles/kernel/util/profiling.h similarity index 97% rename from intern/cycles/kernel/util/util_profiling.h rename to intern/cycles/kernel/util/profiling.h index db8644005ea..12ce441ccbf 100644 --- a/intern/cycles/kernel/util/util_profiling.h +++ b/intern/cycles/kernel/util/profiling.h @@ -17,7 +17,7 @@ #pragma once #ifdef __KERNEL_CPU__ -# include "util/util_profiling.h" +# include "util/profiling.h" #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/alembic.cpp b/intern/cycles/scene/alembic.cpp index 07a969e88d3..39b5f467736 100644 --- a/intern/cycles/scene/alembic.cpp +++ b/intern/cycles/scene/alembic.cpp @@ -24,11 +24,11 @@ #include "scene/scene.h" #include "scene/shader.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_transform.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/transform.h" +#include "util/vector.h" #ifdef WITH_ALEMBIC diff --git a/intern/cycles/scene/alembic.h b/intern/cycles/scene/alembic.h index 9aeef273910..77aafd0ab32 100644 --- a/intern/cycles/scene/alembic.h +++ b/intern/cycles/scene/alembic.h @@ -19,9 +19,9 @@ #include "graph/node.h" #include "scene/attribute.h" #include "scene/procedural.h" -#include "util/util_set.h" -#include "util/util_transform.h" -#include "util/util_vector.h" +#include "util/set.h" +#include "util/transform.h" +#include "util/vector.h" #ifdef WITH_ALEMBIC diff --git a/intern/cycles/scene/alembic_read.cpp b/intern/cycles/scene/alembic_read.cpp index 1ce64d9ee41..35f4854127a 100644 --- a/intern/cycles/scene/alembic_read.cpp +++ b/intern/cycles/scene/alembic_read.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include "scene/alembic.h" #include "scene/alembic_read.h" +#include "scene/alembic.h" #include "scene/mesh.h" -#include "util/util_color.h" -#include "util/util_progress.h" +#include "util/color.h" +#include "util/progress.h" #ifdef WITH_ALEMBIC diff --git a/intern/cycles/scene/alembic_read.h b/intern/cycles/scene/alembic_read.h index 9cc8622a1ba..6b656b59481 100644 --- a/intern/cycles/scene/alembic_read.h +++ b/intern/cycles/scene/alembic_read.h @@ -21,7 +21,7 @@ # include # include -# include "util/util_vector.h" +# include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/attribute.cpp b/intern/cycles/scene/attribute.cpp index 0c440fb9fd1..3401eea307f 100644 --- a/intern/cycles/scene/attribute.cpp +++ b/intern/cycles/scene/attribute.cpp @@ -19,9 +19,9 @@ #include "scene/image.h" #include "scene/mesh.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_transform.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/attribute.h b/intern/cycles/scene/attribute.h index 9af3dcaee26..4a25a900c14 100644 --- a/intern/cycles/scene/attribute.h +++ b/intern/cycles/scene/attribute.h @@ -19,13 +19,13 @@ #include "scene/image.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "util/util_list.h" -#include "util/util_param.h" -#include "util/util_set.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/list.h" +#include "util/param.h" +#include "util/set.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/background.cpp b/intern/cycles/scene/background.cpp index db5be885538..72dccc6f9a8 100644 --- a/intern/cycles/scene/background.cpp +++ b/intern/cycles/scene/background.cpp @@ -23,10 +23,10 @@ #include "scene/shader_nodes.h" #include "scene/stats.h" -#include "util/util_foreach.h" -#include "util/util_math.h" -#include "util/util_time.h" -#include "util/util_types.h" +#include "util/foreach.h" +#include "util/math.h" +#include "util/time.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/background.h b/intern/cycles/scene/background.h index 2f7ef0f7737..31f15d09749 100644 --- a/intern/cycles/scene/background.h +++ b/intern/cycles/scene/background.h @@ -19,7 +19,7 @@ #include "graph/node.h" -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/bake.cpp b/intern/cycles/scene/bake.cpp index 86c5c4c02af..90c9e0e4ae8 100644 --- a/intern/cycles/scene/bake.cpp +++ b/intern/cycles/scene/bake.cpp @@ -22,7 +22,7 @@ #include "scene/stats.h" #include "session/buffers.h" -#include "util/util_foreach.h" +#include "util/foreach.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/bake.h b/intern/cycles/scene/bake.h index 044383d2d43..370cc20ae4f 100644 --- a/intern/cycles/scene/bake.h +++ b/intern/cycles/scene/bake.h @@ -20,8 +20,8 @@ #include "device/device.h" #include "scene/scene.h" -#include "util/util_progress.h" -#include "util/util_vector.h" +#include "util/progress.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/camera.cpp b/intern/cycles/scene/camera.cpp index 1e78f8dd36f..5877b82ead5 100644 --- a/intern/cycles/scene/camera.cpp +++ b/intern/cycles/scene/camera.cpp @@ -23,13 +23,13 @@ #include "device/device.h" -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_logging.h" -#include "util/util_math_cdf.h" -#include "util/util_task.h" -#include "util/util_time.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/function.h" +#include "util/log.h" +#include "util/math_cdf.h" +#include "util/task.h" +#include "util/time.h" +#include "util/vector.h" /* needed for calculating differentials */ #include "kernel/device/cpu/compat.h" diff --git a/intern/cycles/scene/camera.h b/intern/cycles/scene/camera.h index cb8ecac1a7e..58e39599267 100644 --- a/intern/cycles/scene/camera.h +++ b/intern/cycles/scene/camera.h @@ -17,15 +17,15 @@ #ifndef __CAMERA_H__ #define __CAMERA_H__ -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "graph/node.h" -#include "util/util_array.h" -#include "util/util_boundbox.h" -#include "util/util_projection.h" -#include "util/util_transform.h" -#include "util/util_types.h" +#include "util/array.h" +#include "util/boundbox.h" +#include "util/projection.h" +#include "util/transform.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/colorspace.cpp b/intern/cycles/scene/colorspace.cpp index 8584a6f5dd7..c1a308fcbaa 100644 --- a/intern/cycles/scene/colorspace.cpp +++ b/intern/cycles/scene/colorspace.cpp @@ -16,13 +16,13 @@ #include "scene/colorspace.h" -#include "util/util_color.h" -#include "util/util_half.h" -#include "util/util_image.h" -#include "util/util_logging.h" -#include "util/util_math.h" -#include "util/util_thread.h" -#include "util/util_vector.h" +#include "util/color.h" +#include "util/half.h" +#include "util/image.h" +#include "util/log.h" +#include "util/math.h" +#include "util/thread.h" +#include "util/vector.h" #ifdef WITH_OCIO # include diff --git a/intern/cycles/scene/colorspace.h b/intern/cycles/scene/colorspace.h index 51d0b121cc0..7f7bc604f07 100644 --- a/intern/cycles/scene/colorspace.h +++ b/intern/cycles/scene/colorspace.h @@ -17,8 +17,8 @@ #ifndef __COLORSPACE_H__ #define __COLORSPACE_H__ -#include "util/util_map.h" -#include "util/util_param.h" +#include "util/map.h" +#include "util/param.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/constant_fold.cpp b/intern/cycles/scene/constant_fold.cpp index b2a17198c93..ca065e3f678 100644 --- a/intern/cycles/scene/constant_fold.cpp +++ b/intern/cycles/scene/constant_fold.cpp @@ -17,8 +17,8 @@ #include "scene/constant_fold.h" #include "scene/shader_graph.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" +#include "util/foreach.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/constant_fold.h b/intern/cycles/scene/constant_fold.h index fec4123c361..36b249920d0 100644 --- a/intern/cycles/scene/constant_fold.h +++ b/intern/cycles/scene/constant_fold.h @@ -17,8 +17,8 @@ #ifndef __CONSTANT_FOLD_H__ #define __CONSTANT_FOLD_H__ -#include "kernel/svm/svm_types.h" -#include "util/util_types.h" +#include "kernel/svm/types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/curves.cpp b/intern/cycles/scene/curves.cpp index 6e45905d367..7863ce6c666 100644 --- a/intern/cycles/scene/curves.cpp +++ b/intern/cycles/scene/curves.cpp @@ -20,10 +20,10 @@ #include "scene/object.h" #include "scene/scene.h" -#include "util/util_foreach.h" -#include "util/util_map.h" -#include "util/util_progress.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/map.h" +#include "util/progress.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/curves.h b/intern/cycles/scene/curves.h index 9b0e2a29977..3076f4291f5 100644 --- a/intern/cycles/scene/curves.h +++ b/intern/cycles/scene/curves.h @@ -17,8 +17,8 @@ #ifndef __CURVES_H__ #define __CURVES_H__ -#include "util/util_array.h" -#include "util/util_types.h" +#include "util/array.h" +#include "util/types.h" #include "scene/hair.h" diff --git a/intern/cycles/scene/film.cpp b/intern/cycles/scene/film.cpp index 3f91e0321b2..b6480fa64f1 100644 --- a/intern/cycles/scene/film.cpp +++ b/intern/cycles/scene/film.cpp @@ -26,11 +26,11 @@ #include "scene/stats.h" #include "scene/tables.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_math.h" -#include "util/util_math_cdf.h" -#include "util/util_time.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/math.h" +#include "util/math_cdf.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/film.h b/intern/cycles/scene/film.h index ede0d6298a6..5207c5e62b5 100644 --- a/intern/cycles/scene/film.h +++ b/intern/cycles/scene/film.h @@ -18,10 +18,10 @@ #define __FILM_H__ #include "scene/pass.h" -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "graph/node.h" diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index 9fe34ac6e99..5141e1f8358 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -32,15 +32,15 @@ #include "scene/stats.h" #include "scene/volume.h" -#include "subd/subd_patch_table.h" -#include "subd/subd_split.h" +#include "subd/patch_table.h" +#include "subd/split.h" -#include "kernel/osl/osl_globals.h" +#include "kernel/osl/globals.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_task.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/geometry.h b/intern/cycles/scene/geometry.h index 8133132229e..335bcdcd0b7 100644 --- a/intern/cycles/scene/geometry.h +++ b/intern/cycles/scene/geometry.h @@ -19,15 +19,15 @@ #include "graph/node.h" -#include "bvh/bvh_params.h" +#include "bvh/params.h" #include "scene/attribute.h" -#include "util/util_boundbox.h" -#include "util/util_set.h" -#include "util/util_transform.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/boundbox.h" +#include "util/set.h" +#include "util/transform.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/hair.cpp b/intern/cycles/scene/hair.cpp index 2390da5bf88..2951a609ae9 100644 --- a/intern/cycles/scene/hair.cpp +++ b/intern/cycles/scene/hair.cpp @@ -23,7 +23,7 @@ #include "integrator/shader_eval.h" -#include "util/util_progress.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/image.cpp b/intern/cycles/scene/image.cpp index ac85cf5185e..80091e01b8c 100644 --- a/intern/cycles/scene/image.cpp +++ b/intern/cycles/scene/image.cpp @@ -22,15 +22,15 @@ #include "scene/scene.h" #include "scene/stats.h" -#include "util/util_foreach.h" -#include "util/util_image.h" -#include "util/util_image_impl.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_progress.h" -#include "util/util_task.h" -#include "util/util_texture.h" -#include "util/util_unique_ptr.h" +#include "util/foreach.h" +#include "util/image.h" +#include "util/image_impl.h" +#include "util/log.h" +#include "util/path.h" +#include "util/progress.h" +#include "util/task.h" +#include "util/texture.h" +#include "util/unique_ptr.h" #ifdef WITH_OSL # include diff --git a/intern/cycles/scene/image.h b/intern/cycles/scene/image.h index 0c0bbff170a..6447b028ebf 100644 --- a/intern/cycles/scene/image.h +++ b/intern/cycles/scene/image.h @@ -17,15 +17,15 @@ #ifndef __IMAGE_H__ #define __IMAGE_H__ -#include "device/device_memory.h" +#include "device/memory.h" #include "scene/colorspace.h" -#include "util/util_string.h" -#include "util/util_thread.h" -#include "util/util_transform.h" -#include "util/util_unique_ptr.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/thread.h" +#include "util/transform.h" +#include "util/unique_ptr.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/image_oiio.cpp b/intern/cycles/scene/image_oiio.cpp index 256a7aeb7d4..feafae035a1 100644 --- a/intern/cycles/scene/image_oiio.cpp +++ b/intern/cycles/scene/image_oiio.cpp @@ -16,9 +16,9 @@ #include "scene/image_oiio.h" -#include "util/util_image.h" -#include "util/util_logging.h" -#include "util/util_path.h" +#include "util/image.h" +#include "util/log.h" +#include "util/path.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/image_sky.cpp b/intern/cycles/scene/image_sky.cpp index cd8b8d0d991..4f0877aeb99 100644 --- a/intern/cycles/scene/image_sky.cpp +++ b/intern/cycles/scene/image_sky.cpp @@ -18,10 +18,10 @@ #include "sky_model.h" -#include "util/util_image.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_task.h" +#include "util/image.h" +#include "util/log.h" +#include "util/path.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/image_vdb.cpp b/intern/cycles/scene/image_vdb.cpp index 466df82dd73..d3315670390 100644 --- a/intern/cycles/scene/image_vdb.cpp +++ b/intern/cycles/scene/image_vdb.cpp @@ -16,8 +16,8 @@ #include "scene/image_vdb.h" -#include "util/util_logging.h" -#include "util/util_openvdb.h" +#include "util/log.h" +#include "util/openvdb.h" #ifdef WITH_OPENVDB # include diff --git a/intern/cycles/scene/integrator.cpp b/intern/cycles/scene/integrator.cpp index 5bf82898958..3e795b30e7f 100644 --- a/intern/cycles/scene/integrator.cpp +++ b/intern/cycles/scene/integrator.cpp @@ -27,13 +27,13 @@ #include "scene/sobol.h" #include "scene/stats.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_task.h" -#include "util/util_time.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/task.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/integrator.h b/intern/cycles/scene/integrator.h index 468971986c5..c380203f4f3 100644 --- a/intern/cycles/scene/integrator.h +++ b/intern/cycles/scene/integrator.h @@ -17,9 +17,9 @@ #ifndef __INTEGRATOR_H__ #define __INTEGRATOR_H__ -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "device/device_denoise.h" /* For the parameters and type enum. */ +#include "device/denoise.h" /* For the parameters and type enum. */ #include "graph/node.h" #include "integrator/adaptive_sampling.h" diff --git a/intern/cycles/scene/jitter.h b/intern/cycles/scene/jitter.h index ed34c7a4f4d..756e4a1de78 100644 --- a/intern/cycles/scene/jitter.h +++ b/intern/cycles/scene/jitter.h @@ -17,7 +17,7 @@ #ifndef __JITTER_H__ #define __JITTER_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index 26f208d58e5..83e531f42ef 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -30,12 +30,12 @@ #include "integrator/shader_eval.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_progress.h" -#include "util/util_task.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/path.h" +#include "util/progress.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/light.h b/intern/cycles/scene/light.h index 9820508d3a5..97ec9792860 100644 --- a/intern/cycles/scene/light.h +++ b/intern/cycles/scene/light.h @@ -17,7 +17,7 @@ #ifndef __LIGHT_H__ #define __LIGHT_H__ -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "graph/node.h" @@ -25,10 +25,10 @@ * the right Node::set overload as it does not know that Shader is a Node */ #include "scene/shader.h" -#include "util/util_ies.h" -#include "util/util_thread.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/ies.h" +#include "util/thread.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/mesh.cpp b/intern/cycles/scene/mesh.cpp index 1ed0eb4c30a..f47dab30869 100644 --- a/intern/cycles/scene/mesh.cpp +++ b/intern/cycles/scene/mesh.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ +#include "bvh/build.h" #include "bvh/bvh.h" -#include "bvh/bvh_build.h" #include "device/device.h" @@ -25,13 +25,13 @@ #include "scene/scene.h" #include "scene/shader_graph.h" -#include "subd/subd_patch_table.h" -#include "subd/subd_split.h" +#include "subd/patch_table.h" +#include "subd/split.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_set.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/set.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/mesh.h b/intern/cycles/scene/mesh.h index bc549fa55fa..d13b3003164 100644 --- a/intern/cycles/scene/mesh.h +++ b/intern/cycles/scene/mesh.h @@ -19,19 +19,19 @@ #include "graph/node.h" -#include "bvh/bvh_params.h" +#include "bvh/params.h" #include "scene/attribute.h" #include "scene/geometry.h" #include "scene/shader.h" -#include "util/util_array.h" -#include "util/util_boundbox.h" -#include "util/util_list.h" -#include "util/util_map.h" -#include "util/util_param.h" -#include "util/util_set.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/boundbox.h" +#include "util/list.h" +#include "util/map.h" +#include "util/param.h" +#include "util/set.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/mesh_displace.cpp b/intern/cycles/scene/mesh_displace.cpp index c673d79f8fe..e69c2d1c3be 100644 --- a/intern/cycles/scene/mesh_displace.cpp +++ b/intern/cycles/scene/mesh_displace.cpp @@ -23,10 +23,10 @@ #include "scene/scene.h" #include "scene/shader.h" -#include "util/util_foreach.h" -#include "util/util_map.h" -#include "util/util_progress.h" -#include "util/util_set.h" +#include "util/foreach.h" +#include "util/map.h" +#include "util/progress.h" +#include "util/set.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/mesh_subdivision.cpp b/intern/cycles/scene/mesh_subdivision.cpp index 2b27d4b3b2a..a0c0bc68f8b 100644 --- a/intern/cycles/scene/mesh_subdivision.cpp +++ b/intern/cycles/scene/mesh_subdivision.cpp @@ -18,13 +18,13 @@ #include "scene/camera.h" #include "scene/mesh.h" -#include "subd/subd_patch.h" -#include "subd/subd_patch_table.h" -#include "subd/subd_split.h" +#include "subd/patch.h" +#include "subd/patch_table.h" +#include "subd/split.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/hash.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/object.cpp b/intern/cycles/scene/object.cpp index 8b0cc752049..69a2365f17c 100644 --- a/intern/cycles/scene/object.cpp +++ b/intern/cycles/scene/object.cpp @@ -27,16 +27,16 @@ #include "scene/stats.h" #include "scene/volume.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_murmurhash.h" -#include "util/util_progress.h" -#include "util/util_set.h" -#include "util/util_task.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/map.h" +#include "util/murmurhash.h" +#include "util/progress.h" +#include "util/set.h" +#include "util/task.h" +#include "util/vector.h" -#include "subd/subd_patch_table.h" +#include "subd/patch_table.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/object.h b/intern/cycles/scene/object.h index d3909bb2b03..f6dc57ee8b9 100644 --- a/intern/cycles/scene/object.h +++ b/intern/cycles/scene/object.h @@ -25,13 +25,13 @@ #include "scene/particles.h" #include "scene/scene.h" -#include "util/util_array.h" -#include "util/util_boundbox.h" -#include "util/util_param.h" -#include "util/util_thread.h" -#include "util/util_transform.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/boundbox.h" +#include "util/param.h" +#include "util/thread.h" +#include "util/transform.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/osl.cpp b/intern/cycles/scene/osl.cpp index c8ab83ab781..09626cb48bb 100644 --- a/intern/cycles/scene/osl.cpp +++ b/intern/cycles/scene/osl.cpp @@ -28,17 +28,17 @@ #ifdef WITH_OSL -# include "kernel/osl/osl_globals.h" -# include "kernel/osl/osl_services.h" -# include "kernel/osl/osl_shader.h" +# include "kernel/osl/globals.h" +# include "kernel/osl/services.h" +# include "kernel/osl/shader.h" -# include "util/util_aligned_malloc.h" -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_md5.h" -# include "util/util_path.h" -# include "util/util_progress.h" -# include "util/util_projection.h" +# include "util/aligned_malloc.h" +# include "util/foreach.h" +# include "util/log.h" +# include "util/md5.h" +# include "util/path.h" +# include "util/progress.h" +# include "util/projection.h" #endif diff --git a/intern/cycles/scene/osl.h b/intern/cycles/scene/osl.h index 4161fe6ed67..d54040e1047 100644 --- a/intern/cycles/scene/osl.h +++ b/intern/cycles/scene/osl.h @@ -17,10 +17,10 @@ #ifndef __OSL_H__ #define __OSL_H__ -#include "util/util_array.h" -#include "util/util_set.h" -#include "util/util_string.h" -#include "util/util_thread.h" +#include "util/array.h" +#include "util/set.h" +#include "util/string.h" +#include "util/thread.h" #include "scene/shader.h" #include "scene/shader_graph.h" diff --git a/intern/cycles/scene/particles.cpp b/intern/cycles/scene/particles.cpp index 8041c57ba02..92381171082 100644 --- a/intern/cycles/scene/particles.cpp +++ b/intern/cycles/scene/particles.cpp @@ -19,12 +19,12 @@ #include "scene/scene.h" #include "scene/stats.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_progress.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/map.h" +#include "util/progress.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/particles.h b/intern/cycles/scene/particles.h index 8b59756f148..b958d12e4e3 100644 --- a/intern/cycles/scene/particles.h +++ b/intern/cycles/scene/particles.h @@ -17,8 +17,8 @@ #ifndef __PARTICLES_H__ #define __PARTICLES_H__ -#include "util/util_array.h" -#include "util/util_types.h" +#include "util/array.h" +#include "util/types.h" #include "graph/node.h" diff --git a/intern/cycles/scene/pass.cpp b/intern/cycles/scene/pass.cpp index ee770ac8e58..791101e0940 100644 --- a/intern/cycles/scene/pass.cpp +++ b/intern/cycles/scene/pass.cpp @@ -16,8 +16,8 @@ #include "scene/pass.h" -#include "util/util_algorithm.h" -#include "util/util_logging.h" +#include "util/algorithm.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/pass.h b/intern/cycles/scene/pass.h index 82230c62cb0..7da07cfa562 100644 --- a/intern/cycles/scene/pass.h +++ b/intern/cycles/scene/pass.h @@ -18,10 +18,10 @@ #include // NOLINT -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "graph/node.h" diff --git a/intern/cycles/scene/procedural.cpp b/intern/cycles/scene/procedural.cpp index abfc6c62ad4..f038c8b1023 100644 --- a/intern/cycles/scene/procedural.cpp +++ b/intern/cycles/scene/procedural.cpp @@ -14,13 +14,12 @@ * limitations under the License. */ -#include "procedural.h" - +#include "scene/procedural.h" #include "scene/scene.h" #include "scene/stats.h" -#include "util/util_foreach.h" -#include "util/util_progress.h" +#include "util/foreach.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp index bc5737ec126..8cde4873eab 100644 --- a/intern/cycles/scene/scene.cpp +++ b/intern/cycles/scene/scene.cpp @@ -38,10 +38,10 @@ #include "scene/volume.h" #include "session/session.h" -#include "util/util_foreach.h" -#include "util/util_guarded_allocator.h" -#include "util/util_logging.h" -#include "util/util_progress.h" +#include "util/foreach.h" +#include "util/guarded_allocator.h" +#include "util/log.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/scene.h b/intern/cycles/scene/scene.h index 9b2502c9361..fa7fc54602a 100644 --- a/intern/cycles/scene/scene.h +++ b/intern/cycles/scene/scene.h @@ -17,22 +17,22 @@ #ifndef __SCENE_H__ #define __SCENE_H__ -#include "bvh/bvh_params.h" +#include "bvh/params.h" #include "scene/film.h" #include "scene/image.h" #include "scene/shader.h" #include "device/device.h" -#include "device/device_memory.h" +#include "device/memory.h" -#include "util/util_param.h" -#include "util/util_string.h" -#include "util/util_system.h" -#include "util/util_texture.h" -#include "util/util_thread.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/param.h" +#include "util/string.h" +#include "util/system.h" +#include "util/texture.h" +#include "util/thread.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp index 6b464a77401..0b286aba9cf 100644 --- a/intern/cycles/scene/shader.cpp +++ b/intern/cycles/scene/shader.cpp @@ -32,10 +32,10 @@ #include "scene/svm.h" #include "scene/tables.h" -#include "util/util_foreach.h" -#include "util/util_murmurhash.h" -#include "util/util_task.h" -#include "util/util_transform.h" +#include "util/foreach.h" +#include "util/murmurhash.h" +#include "util/task.h" +#include "util/transform.h" #ifdef WITH_OCIO # include diff --git a/intern/cycles/scene/shader.h b/intern/cycles/scene/shader.h index 7ef3bda15d7..e9d26412ae8 100644 --- a/intern/cycles/scene/shader.h +++ b/intern/cycles/scene/shader.h @@ -19,20 +19,20 @@ #ifdef WITH_OSL /* So no context pollution happens from indirectly included windows.h */ -# include "util/util_windows.h" +# include "util/windows.h" # include #endif -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "scene/attribute.h" #include "graph/node.h" -#include "util/util_map.h" -#include "util/util_param.h" -#include "util/util_string.h" -#include "util/util_thread.h" -#include "util/util_types.h" +#include "util/map.h" +#include "util/param.h" +#include "util/string.h" +#include "util/thread.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/shader_graph.cpp b/intern/cycles/scene/shader_graph.cpp index 116d8335ef8..f99dfa141f6 100644 --- a/intern/cycles/scene/shader_graph.cpp +++ b/intern/cycles/scene/shader_graph.cpp @@ -21,11 +21,11 @@ #include "scene/shader.h" #include "scene/shader_nodes.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_md5.h" -#include "util/util_queue.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/md5.h" +#include "util/queue.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/shader_graph.h b/intern/cycles/scene/shader_graph.h index 3584754fad1..8b525a7ec0b 100644 --- a/intern/cycles/scene/shader_graph.h +++ b/intern/cycles/scene/shader_graph.h @@ -20,14 +20,14 @@ #include "graph/node.h" #include "graph/node_type.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "util/util_list.h" -#include "util/util_map.h" -#include "util/util_param.h" -#include "util/util_set.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/list.h" +#include "util/map.h" +#include "util/param.h" +#include "util/set.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index d7fc7ae1c27..14d051350fb 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -29,15 +29,15 @@ #include "sky_model.h" -#include "util/util_color.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_transform.h" +#include "util/color.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/transform.h" -#include "kernel/svm/svm_color_util.h" -#include "kernel/svm/svm_mapping_util.h" -#include "kernel/svm/svm_math_util.h" -#include "kernel/svm/svm_ramp_util.h" +#include "kernel/svm/color_util.h" +#include "kernel/svm/mapping_util.h" +#include "kernel/svm/math_util.h" +#include "kernel/svm/ramp_util.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index b8a439fa9b9..64a2b1c7843 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -21,8 +21,8 @@ #include "scene/image.h" #include "scene/shader_graph.h" -#include "util/util_array.h" -#include "util/util_string.h" +#include "util/array.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/sobol.cpp b/intern/cycles/scene/sobol.cpp index 397c28814ca..09d10c3660e 100644 --- a/intern/cycles/scene/sobol.cpp +++ b/intern/cycles/scene/sobol.cpp @@ -45,13 +45,13 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "util/util_types.h" +#include "util/types.h" #include "scene/sobol.h" CCL_NAMESPACE_BEGIN -#include "sobol.tables" +#include "scene/sobol.tables" void sobol_generate_direction_vectors(uint vectors[][SOBOL_BITS], int dimensions) { diff --git a/intern/cycles/scene/sobol.h b/intern/cycles/scene/sobol.h index d38857d2b35..86b2a1616b8 100644 --- a/intern/cycles/scene/sobol.h +++ b/intern/cycles/scene/sobol.h @@ -17,7 +17,7 @@ #ifndef __SOBOL_H__ #define __SOBOL_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/stats.cpp b/intern/cycles/scene/stats.cpp index 5c3cff232f4..e2b00d16593 100644 --- a/intern/cycles/scene/stats.cpp +++ b/intern/cycles/scene/stats.cpp @@ -16,9 +16,9 @@ #include "scene/stats.h" #include "scene/object.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_string.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/stats.h b/intern/cycles/scene/stats.h index ffcc4d55235..d9095acc4c9 100644 --- a/intern/cycles/scene/stats.h +++ b/intern/cycles/scene/stats.h @@ -19,9 +19,9 @@ #include "scene/scene.h" -#include "util/util_stats.h" -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/stats.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/svm.cpp b/intern/cycles/scene/svm.cpp index b0b7fb605d1..6da0df302ad 100644 --- a/intern/cycles/scene/svm.cpp +++ b/intern/cycles/scene/svm.cpp @@ -26,10 +26,10 @@ #include "scene/stats.h" #include "scene/svm.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_task.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/svm.h b/intern/cycles/scene/svm.h index a3d215218fa..edfd71040e4 100644 --- a/intern/cycles/scene/svm.h +++ b/intern/cycles/scene/svm.h @@ -21,10 +21,10 @@ #include "scene/shader.h" #include "scene/shader_graph.h" -#include "util/util_array.h" -#include "util/util_set.h" -#include "util/util_string.h" -#include "util/util_thread.h" +#include "util/array.h" +#include "util/set.h" +#include "util/string.h" +#include "util/thread.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/tables.cpp b/intern/cycles/scene/tables.cpp index 39edc5d89cd..3544fea67d6 100644 --- a/intern/cycles/scene/tables.cpp +++ b/intern/cycles/scene/tables.cpp @@ -19,8 +19,8 @@ #include "scene/scene.h" #include "scene/stats.h" -#include "util/util_logging.h" -#include "util/util_time.h" +#include "util/log.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/tables.h b/intern/cycles/scene/tables.h index de538e2af78..3e52544d1fb 100644 --- a/intern/cycles/scene/tables.h +++ b/intern/cycles/scene/tables.h @@ -17,8 +17,8 @@ #ifndef __TABLES_H__ #define __TABLES_H__ -#include "util/util_list.h" -#include "util/util_vector.h" +#include "util/list.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/volume.cpp b/intern/cycles/scene/volume.cpp index 757388a4491..509d0ecedf7 100644 --- a/intern/cycles/scene/volume.cpp +++ b/intern/cycles/scene/volume.cpp @@ -25,12 +25,12 @@ # include #endif -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_openvdb.h" -#include "util/util_progress.h" -#include "util/util_types.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/openvdb.h" +#include "util/progress.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/buffers.cpp b/intern/cycles/session/buffers.cpp index 439c0f826ea..51d9c1e5d8f 100644 --- a/intern/cycles/session/buffers.cpp +++ b/intern/cycles/session/buffers.cpp @@ -19,11 +19,11 @@ #include "device/device.h" #include "session/buffers.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_math.h" -#include "util/util_time.h" -#include "util/util_types.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/math.h" +#include "util/time.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/buffers.h b/intern/cycles/session/buffers.h index 4c261430bb6..67022bb5b6b 100644 --- a/intern/cycles/session/buffers.h +++ b/intern/cycles/session/buffers.h @@ -17,16 +17,16 @@ #ifndef __BUFFERS_H__ #define __BUFFERS_H__ -#include "device/device_memory.h" +#include "device/memory.h" #include "graph/node.h" #include "scene/pass.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "util/util_half.h" -#include "util/util_string.h" -#include "util/util_thread.h" -#include "util/util_types.h" +#include "util/half.h" +#include "util/string.h" +#include "util/thread.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/display_driver.h b/intern/cycles/session/display_driver.h index 85f305034d7..77f89326fd0 100644 --- a/intern/cycles/session/display_driver.h +++ b/intern/cycles/session/display_driver.h @@ -16,8 +16,8 @@ #pragma once -#include "util/util_half.h" -#include "util/util_types.h" +#include "util/half.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/merge.cpp b/intern/cycles/session/merge.cpp index 97e9c75d5f7..5890c15f48c 100644 --- a/intern/cycles/session/merge.cpp +++ b/intern/cycles/session/merge.cpp @@ -16,11 +16,11 @@ #include "session/merge.h" -#include "util/util_array.h" -#include "util/util_map.h" -#include "util/util_system.h" -#include "util/util_time.h" -#include "util/util_unique_ptr.h" +#include "util/array.h" +#include "util/map.h" +#include "util/system.h" +#include "util/time.h" +#include "util/unique_ptr.h" #include #include diff --git a/intern/cycles/session/merge.h b/intern/cycles/session/merge.h index 87e5d2d4723..be03a69b27a 100644 --- a/intern/cycles/session/merge.h +++ b/intern/cycles/session/merge.h @@ -17,8 +17,8 @@ #ifndef __MERGE_H__ #define __MERGE_H__ -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/output_driver.h b/intern/cycles/session/output_driver.h index b7e980d71d4..95e15ed875b 100644 --- a/intern/cycles/session/output_driver.h +++ b/intern/cycles/session/output_driver.h @@ -16,9 +16,9 @@ #pragma once -#include "util/util_math.h" -#include "util/util_string.h" -#include "util/util_types.h" +#include "util/math.h" +#include "util/string.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/session.cpp b/intern/cycles/session/session.cpp index f8fc892f127..b228939689c 100644 --- a/intern/cycles/session/session.cpp +++ b/intern/cycles/session/session.cpp @@ -35,12 +35,12 @@ #include "session/output_driver.h" #include "session/session.h" -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_logging.h" -#include "util/util_math.h" -#include "util/util_task.h" -#include "util/util_time.h" +#include "util/foreach.h" +#include "util/function.h" +#include "util/log.h" +#include "util/math.h" +#include "util/task.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/session.h b/intern/cycles/session/session.h index 5aa6df79ef1..1ec0c6e9bb1 100644 --- a/intern/cycles/session/session.h +++ b/intern/cycles/session/session.h @@ -24,11 +24,11 @@ #include "session/buffers.h" #include "session/tile.h" -#include "util/util_progress.h" -#include "util/util_stats.h" -#include "util/util_thread.h" -#include "util/util_unique_ptr.h" -#include "util/util_vector.h" +#include "util/progress.h" +#include "util/stats.h" +#include "util/thread.h" +#include "util/unique_ptr.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/tile.cpp b/intern/cycles/session/tile.cpp index 59332530596..816bf4d5fa0 100644 --- a/intern/cycles/session/tile.cpp +++ b/intern/cycles/session/tile.cpp @@ -23,13 +23,13 @@ #include "scene/film.h" #include "scene/integrator.h" #include "scene/scene.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_string.h" -#include "util/util_system.h" -#include "util/util_types.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/path.h" +#include "util/string.h" +#include "util/system.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/tile.h b/intern/cycles/session/tile.h index 37a02081a53..eace148eb0a 100644 --- a/intern/cycles/session/tile.h +++ b/intern/cycles/session/tile.h @@ -17,9 +17,9 @@ #pragma once #include "session/buffers.h" -#include "util/util_image.h" -#include "util/util_string.h" -#include "util/util_unique_ptr.h" +#include "util/image.h" +#include "util/string.h" +#include "util/unique_ptr.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/CMakeLists.txt b/intern/cycles/subd/CMakeLists.txt index c697ddb9891..4bf5503dc4b 100644 --- a/intern/cycles/subd/CMakeLists.txt +++ b/intern/cycles/subd/CMakeLists.txt @@ -21,18 +21,18 @@ set(INC_SYS ) set(SRC - subd_dice.cpp - subd_patch.cpp - subd_split.cpp - subd_patch_table.cpp + dice.cpp + patch.cpp + split.cpp + patch_table.cpp ) set(SRC_HEADERS - subd_dice.h - subd_patch.h - subd_patch_table.h - subd_split.h - subd_subpatch.h + dice.h + patch.h + patch_table.h + split.h + subpatch.h ) set(LIB diff --git a/intern/cycles/subd/subd_dice.cpp b/intern/cycles/subd/dice.cpp similarity index 99% rename from intern/cycles/subd/subd_dice.cpp rename to intern/cycles/subd/dice.cpp index a4019a5d639..461fa0bcd9c 100644 --- a/intern/cycles/subd/subd_dice.cpp +++ b/intern/cycles/subd/dice.cpp @@ -17,8 +17,8 @@ #include "scene/camera.h" #include "scene/mesh.h" -#include "subd/subd_dice.h" -#include "subd/subd_patch.h" +#include "subd/dice.h" +#include "subd/patch.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/subd_dice.h b/intern/cycles/subd/dice.h similarity index 96% rename from intern/cycles/subd/subd_dice.h rename to intern/cycles/subd/dice.h index ee63403d40c..7510aae775c 100644 --- a/intern/cycles/subd/subd_dice.h +++ b/intern/cycles/subd/dice.h @@ -22,10 +22,10 @@ * DiagSplit. For more algorithm details, see the DiagSplit paper or the * ARB_tessellation_shader OpenGL extension, Section 2.X.2. */ -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/types.h" +#include "util/vector.h" -#include "subd/subd_subpatch.h" +#include "subd/subpatch.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/subd_patch.cpp b/intern/cycles/subd/patch.cpp similarity index 96% rename from intern/cycles/subd/subd_patch.cpp rename to intern/cycles/subd/patch.cpp index 23b3e6d5136..4d73f334c1b 100644 --- a/intern/cycles/subd/subd_patch.cpp +++ b/intern/cycles/subd/patch.cpp @@ -18,10 +18,10 @@ #include "scene/mesh.h" -#include "subd/subd_patch.h" +#include "subd/patch.h" -#include "util/util_math.h" -#include "util/util_types.h" +#include "util/math.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/subd_patch.h b/intern/cycles/subd/patch.h similarity index 95% rename from intern/cycles/subd/subd_patch.h rename to intern/cycles/subd/patch.h index 8fe423bc94d..ad4dc1bd8e9 100644 --- a/intern/cycles/subd/subd_patch.h +++ b/intern/cycles/subd/patch.h @@ -17,8 +17,8 @@ #ifndef __SUBD_PATCH_H__ #define __SUBD_PATCH_H__ -#include "util/util_boundbox.h" -#include "util/util_types.h" +#include "util/boundbox.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/subd_patch_table.cpp b/intern/cycles/subd/patch_table.cpp similarity index 98% rename from intern/cycles/subd/subd_patch_table.cpp rename to intern/cycles/subd/patch_table.cpp index 4e873375725..d215dfaa1dd 100644 --- a/intern/cycles/subd/subd_patch_table.cpp +++ b/intern/cycles/subd/patch_table.cpp @@ -24,10 +24,10 @@ * language governing permissions and limitations under the Apache License. */ -#include "subd/subd_patch_table.h" -#include "kernel/kernel_types.h" +#include "subd/patch_table.h" +#include "kernel/types.h" -#include "util/util_math.h" +#include "util/math.h" #ifdef WITH_OPENSUBDIV # include diff --git a/intern/cycles/subd/subd_patch_table.h b/intern/cycles/subd/patch_table.h similarity index 96% rename from intern/cycles/subd/subd_patch_table.h rename to intern/cycles/subd/patch_table.h index 118d410f8f0..b5fd5923f31 100644 --- a/intern/cycles/subd/subd_patch_table.h +++ b/intern/cycles/subd/patch_table.h @@ -17,8 +17,8 @@ #ifndef __SUBD_PATCH_TABLE_H__ #define __SUBD_PATCH_TABLE_H__ -#include "util/util_array.h" -#include "util/util_types.h" +#include "util/array.h" +#include "util/types.h" #ifdef WITH_OPENSUBDIV # ifdef _MSC_VER diff --git a/intern/cycles/subd/subd_split.cpp b/intern/cycles/subd/split.cpp similarity index 98% rename from intern/cycles/subd/subd_split.cpp rename to intern/cycles/subd/split.cpp index 6b352ab02c3..2b29f3a5a78 100644 --- a/intern/cycles/subd/subd_split.cpp +++ b/intern/cycles/subd/split.cpp @@ -17,15 +17,15 @@ #include "scene/camera.h" #include "scene/mesh.h" -#include "subd/subd_dice.h" -#include "subd/subd_patch.h" -#include "subd/subd_split.h" +#include "subd/dice.h" +#include "subd/patch.h" +#include "subd/split.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_math.h" -#include "util/util_types.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/math.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/subd_split.h b/intern/cycles/subd/split.h similarity index 93% rename from intern/cycles/subd/subd_split.h rename to intern/cycles/subd/split.h index 7416b2fbbf8..e876f34c419 100644 --- a/intern/cycles/subd/subd_split.h +++ b/intern/cycles/subd/split.h @@ -22,12 +22,12 @@ * evaluation at arbitrary points is required for this to work. See the paper * for more details. */ -#include "subd/subd_dice.h" -#include "subd/subd_subpatch.h" +#include "subd/dice.h" +#include "subd/subpatch.h" -#include "util/util_deque.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/deque.h" +#include "util/types.h" +#include "util/vector.h" #include diff --git a/intern/cycles/subd/subd_subpatch.h b/intern/cycles/subd/subpatch.h similarity index 98% rename from intern/cycles/subd/subd_subpatch.h rename to intern/cycles/subd/subpatch.h index cdaa310916a..0ba8ed88aa8 100644 --- a/intern/cycles/subd/subd_subpatch.h +++ b/intern/cycles/subd/subpatch.h @@ -17,8 +17,8 @@ #ifndef __SUBD_SUBPATCH_H__ #define __SUBD_SUBPATCH_H__ -#include "util/util_map.h" -#include "util/util_types.h" +#include "util/map.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt index efd9cecb877..86a830b2b65 100644 --- a/intern/cycles/test/CMakeLists.txt +++ b/intern/cycles/test/CMakeLists.txt @@ -20,13 +20,7 @@ if(WITH_GTESTS) endif() set(INC - . .. - ../device - ../graph - ../kernel - ../scene - ../util ) set(ALL_CYCLES_LIBRARIES diff --git a/intern/cycles/test/integrator_adaptive_sampling_test.cpp b/intern/cycles/test/integrator_adaptive_sampling_test.cpp index 3ed6a23125d..30688605e44 100644 --- a/intern/cycles/test/integrator_adaptive_sampling_test.cpp +++ b/intern/cycles/test/integrator_adaptive_sampling_test.cpp @@ -17,7 +17,7 @@ #include "testing/testing.h" #include "integrator/adaptive_sampling.h" -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/integrator_tile_test.cpp b/intern/cycles/test/integrator_tile_test.cpp index 5bb57b48c3c..e5ffa7c153d 100644 --- a/intern/cycles/test/integrator_tile_test.cpp +++ b/intern/cycles/test/integrator_tile_test.cpp @@ -17,7 +17,7 @@ #include "testing/testing.h" #include "integrator/tile.h" -#include "util/util_math.h" +#include "util/math.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index 4a87a382130..4207b437a41 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -23,11 +23,11 @@ #include "scene/shader_graph.h" #include "scene/shader_nodes.h" -#include "util/util_array.h" -#include "util/util_logging.h" -#include "util/util_stats.h" -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/log.h" +#include "util/stats.h" +#include "util/string.h" +#include "util/vector.h" using testing::_; using testing::AnyNumber; diff --git a/intern/cycles/test/util_aligned_malloc_test.cpp b/intern/cycles/test/util_aligned_malloc_test.cpp index 8829c422a0f..2748db520eb 100644 --- a/intern/cycles/test/util_aligned_malloc_test.cpp +++ b/intern/cycles/test/util_aligned_malloc_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_aligned_malloc.h" +#include "util/aligned_malloc.h" #define CHECK_ALIGNMENT(ptr, align) EXPECT_EQ((size_t)ptr % align, 0) diff --git a/intern/cycles/test/util_avxf_test.h b/intern/cycles/test/util_avxf_test.h index 64825200c9e..b178a0450d0 100644 --- a/intern/cycles/test/util_avxf_test.h +++ b/intern/cycles/test/util_avxf_test.h @@ -15,8 +15,8 @@ */ #include "testing/testing.h" -#include "util/util_system.h" -#include "util/util_types.h" +#include "util/system.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/util_math_test.cpp b/intern/cycles/test/util_math_test.cpp index b6ce3ef0cf3..adbedf7adbe 100644 --- a/intern/cycles/test/util_math_test.cpp +++ b/intern/cycles/test/util_math_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_math.h" +#include "util/math.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/util_path_test.cpp b/intern/cycles/test/util_path_test.cpp index 76d48dc241d..7afdd1150a4 100644 --- a/intern/cycles/test/util_path_test.cpp +++ b/intern/cycles/test/util_path_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_path.h" +#include "util/path.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/util_string_test.cpp b/intern/cycles/test/util_string_test.cpp index c9022d1b132..f558dda9e47 100644 --- a/intern/cycles/test/util_string_test.cpp +++ b/intern/cycles/test/util_string_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_string.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/util_task_test.cpp b/intern/cycles/test/util_task_test.cpp index a8b4dfc3a37..17cfe4ff9b2 100644 --- a/intern/cycles/test/util_task_test.cpp +++ b/intern/cycles/test/util_task_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_task.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/util_time_test.cpp b/intern/cycles/test/util_time_test.cpp index ab5ead2c7b1..97a0134df67 100644 --- a/intern/cycles/test/util_time_test.cpp +++ b/intern/cycles/test/util_time_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_time.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/util_transform_test.cpp b/intern/cycles/test/util_transform_test.cpp index a5267df9fb7..11dd71ea0c2 100644 --- a/intern/cycles/test/util_transform_test.cpp +++ b/intern/cycles/test/util_transform_test.cpp @@ -16,8 +16,8 @@ #include "testing/testing.h" -#include "util/util_transform.h" -#include "util/util_vector.h" +#include "util/transform.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index 18e14913884..b68646a44d5 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -22,23 +22,23 @@ set(INC_SYS ) set(SRC - util_aligned_malloc.cpp - util_debug.cpp - util_ies.cpp - util_logging.cpp - util_math_cdf.cpp - util_md5.cpp - util_murmurhash.cpp - util_path.cpp - util_profiling.cpp - util_string.cpp - util_simd.cpp - util_system.cpp - util_task.cpp - util_thread.cpp - util_time.cpp - util_transform.cpp - util_windows.cpp + aligned_malloc.cpp + debug.cpp + ies.cpp + log.cpp + math_cdf.cpp + md5.cpp + murmurhash.cpp + path.cpp + profiling.cpp + string.cpp + simd.cpp + system.cpp + task.cpp + thread.cpp + time.cpp + transform.cpp + windows.cpp ) set(LIB @@ -48,7 +48,7 @@ set(LIB if(WITH_CYCLES_STANDALONE) if(WITH_CYCLES_STANDALONE_GUI) list(APPEND SRC - util_view.cpp + view.cpp ) endif() endif() @@ -64,108 +64,108 @@ else() endif() set(SRC_HEADERS - util_algorithm.h - util_aligned_malloc.h - util_args.h - util_array.h - util_atomic.h - util_boundbox.h - util_debug.h - util_defines.h - util_deque.h - util_disjoint_set.h - util_guarded_allocator.cpp - util_foreach.h - util_function.h - util_guarded_allocator.h - util_half.h - util_hash.h - util_ies.h - util_image.h - util_image_impl.h - util_list.h - util_logging.h - util_map.h - util_math.h - util_math_cdf.h - util_math_fast.h - util_math_intersect.h - util_math_float2.h - util_math_float3.h - util_math_float4.h - util_math_int2.h - util_math_int3.h - util_math_int4.h - util_math_matrix.h - util_md5.h - util_murmurhash.h - util_openimagedenoise.h - util_opengl.h - util_openvdb.h - util_optimization.h - util_param.h - util_path.h - util_profiling.h - util_progress.h - util_projection.h - util_queue.h - util_rect.h - util_set.h - util_simd.h - util_avxf.h - util_avxb.h - util_avxi.h - util_semaphore.h - util_sseb.h - util_ssef.h - util_ssei.h - util_stack_allocator.h - util_static_assert.h - util_stats.h - util_string.h - util_system.h - util_task.h - util_tbb.h - util_texture.h - util_thread.h - util_time.h - util_transform.h - util_types.h - util_types_float2.h - util_types_float2_impl.h - util_types_float3.h - util_types_float3_impl.h - util_types_float4.h - util_types_float4_impl.h - util_types_float8.h - util_types_float8_impl.h - util_types_int2.h - util_types_int2_impl.h - util_types_int3.h - util_types_int3_impl.h - util_types_int4.h - util_types_int4_impl.h - util_types_uchar2.h - util_types_uchar2_impl.h - util_types_uchar3.h - util_types_uchar3_impl.h - util_types_uchar4.h - util_types_uchar4_impl.h - util_types_uint2.h - util_types_uint2_impl.h - util_types_uint3.h - util_types_uint3_impl.h - util_types_uint4.h - util_types_uint4_impl.h - util_types_ushort4.h - util_types_vector3.h - util_types_vector3_impl.h - util_unique_ptr.h - util_vector.h - util_version.h - util_view.h - util_windows.h - util_xml.h + algorithm.h + aligned_malloc.h + args.h + array.h + atomic.h + boundbox.h + debug.h + defines.h + deque.h + disjoint_set.h + guarded_allocator.cpp + foreach.h + function.h + guarded_allocator.h + half.h + hash.h + ies.h + image.h + image_impl.h + list.h + log.h + map.h + math.h + math_cdf.h + math_fast.h + math_intersect.h + math_float2.h + math_float3.h + math_float4.h + math_int2.h + math_int3.h + math_int4.h + math_matrix.h + md5.h + murmurhash.h + openimagedenoise.h + opengl.h + openvdb.h + optimization.h + param.h + path.h + profiling.h + progress.h + projection.h + queue.h + rect.h + set.h + simd.h + avxf.h + avxb.h + avxi.h + semaphore.h + sseb.h + ssef.h + ssei.h + stack_allocator.h + static_assert.h + stats.h + string.h + system.h + task.h + tbb.h + texture.h + thread.h + time.h + transform.h + types.h + types_float2.h + types_float2_impl.h + types_float3.h + types_float3_impl.h + types_float4.h + types_float4_impl.h + types_float8.h + types_float8_impl.h + types_int2.h + types_int2_impl.h + types_int3.h + types_int3_impl.h + types_int4.h + types_int4_impl.h + types_uchar2.h + types_uchar2_impl.h + types_uchar3.h + types_uchar3_impl.h + types_uchar4.h + types_uchar4_impl.h + types_uint2.h + types_uint2_impl.h + types_uint3.h + types_uint3_impl.h + types_uint4.h + types_uint4_impl.h + types_ushort4.h + types_vector3.h + types_vector3_impl.h + unique_ptr.h + vector.h + version.h + view.h + windows.h + xml.h ) include_directories(${INC}) diff --git a/intern/cycles/util/util_algorithm.h b/intern/cycles/util/algorithm.h similarity index 100% rename from intern/cycles/util/util_algorithm.h rename to intern/cycles/util/algorithm.h diff --git a/intern/cycles/util/util_aligned_malloc.cpp b/intern/cycles/util/aligned_malloc.cpp similarity index 96% rename from intern/cycles/util/util_aligned_malloc.cpp rename to intern/cycles/util/aligned_malloc.cpp index 9b729cd4fc4..2b05559b55f 100644 --- a/intern/cycles/util/util_aligned_malloc.cpp +++ b/intern/cycles/util/aligned_malloc.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "util/util_aligned_malloc.h" -#include "util/util_guarded_allocator.h" +#include "util/aligned_malloc.h" +#include "util/guarded_allocator.h" #include diff --git a/intern/cycles/util/util_aligned_malloc.h b/intern/cycles/util/aligned_malloc.h similarity index 97% rename from intern/cycles/util/util_aligned_malloc.h rename to intern/cycles/util/aligned_malloc.h index df7d93c056d..66c2ac1c593 100644 --- a/intern/cycles/util/util_aligned_malloc.h +++ b/intern/cycles/util/aligned_malloc.h @@ -17,7 +17,7 @@ #ifndef __UTIL_ALIGNED_MALLOC_H__ #define __UTIL_ALIGNED_MALLOC_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_args.h b/intern/cycles/util/args.h similarity index 100% rename from intern/cycles/util/util_args.h rename to intern/cycles/util/args.h diff --git a/intern/cycles/util/util_array.h b/intern/cycles/util/array.h similarity index 97% rename from intern/cycles/util/util_array.h rename to intern/cycles/util/array.h index 73f7d6cf7f8..4c905b09138 100644 --- a/intern/cycles/util/util_array.h +++ b/intern/cycles/util/array.h @@ -20,10 +20,10 @@ #include #include -#include "util/util_aligned_malloc.h" -#include "util/util_guarded_allocator.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/aligned_malloc.h" +#include "util/guarded_allocator.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_atomic.h b/intern/cycles/util/atomic.h similarity index 100% rename from intern/cycles/util/util_atomic.h rename to intern/cycles/util/atomic.h diff --git a/intern/cycles/util/util_avxb.h b/intern/cycles/util/avxb.h similarity index 100% rename from intern/cycles/util/util_avxb.h rename to intern/cycles/util/avxb.h diff --git a/intern/cycles/util/util_avxf.h b/intern/cycles/util/avxf.h similarity index 100% rename from intern/cycles/util/util_avxf.h rename to intern/cycles/util/avxf.h diff --git a/intern/cycles/util/util_avxi.h b/intern/cycles/util/avxi.h similarity index 100% rename from intern/cycles/util/util_avxi.h rename to intern/cycles/util/avxi.h diff --git a/intern/cycles/util/util_boundbox.h b/intern/cycles/util/boundbox.h similarity index 98% rename from intern/cycles/util/util_boundbox.h rename to intern/cycles/util/boundbox.h index 7fab7bd5a15..ed81e4cf8c3 100644 --- a/intern/cycles/util/util_boundbox.h +++ b/intern/cycles/util/boundbox.h @@ -20,10 +20,10 @@ #include #include -#include "util/util_math.h" -#include "util/util_string.h" -#include "util/util_transform.h" -#include "util/util_types.h" +#include "util/math.h" +#include "util/string.h" +#include "util/transform.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_color.h b/intern/cycles/util/color.h similarity index 98% rename from intern/cycles/util/util_color.h rename to intern/cycles/util/color.h index 361c36d9061..e2a5c5b9c4a 100644 --- a/intern/cycles/util/util_color.h +++ b/intern/cycles/util/color.h @@ -17,11 +17,11 @@ #ifndef __UTIL_COLOR_H__ #define __UTIL_COLOR_H__ -#include "util/util_math.h" -#include "util/util_types.h" +#include "util/math.h" +#include "util/types.h" #if !defined(__KERNEL_GPU__) && defined(__KERNEL_SSE2__) -# include "util/util_simd.h" +# include "util/simd.h" #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_debug.cpp b/intern/cycles/util/debug.cpp similarity index 96% rename from intern/cycles/util/util_debug.cpp rename to intern/cycles/util/debug.cpp index 2245668d02f..b49df3d42bc 100644 --- a/intern/cycles/util/util_debug.cpp +++ b/intern/cycles/util/debug.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ -#include "util/util_debug.h" +#include "util/debug.h" #include -#include "bvh/bvh_params.h" +#include "bvh/params.h" -#include "util/util_logging.h" -#include "util/util_string.h" +#include "util/log.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/debug.h similarity index 99% rename from intern/cycles/util/util_debug.h rename to intern/cycles/util/debug.h index 81677201790..58b2b047261 100644 --- a/intern/cycles/util/util_debug.h +++ b/intern/cycles/util/debug.h @@ -20,7 +20,7 @@ #include #include -#include "bvh/bvh_params.h" +#include "bvh/params.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_defines.h b/intern/cycles/util/defines.h similarity index 100% rename from intern/cycles/util/util_defines.h rename to intern/cycles/util/defines.h diff --git a/intern/cycles/util/util_deque.h b/intern/cycles/util/deque.h similarity index 100% rename from intern/cycles/util/util_deque.h rename to intern/cycles/util/deque.h diff --git a/intern/cycles/util/util_disjoint_set.h b/intern/cycles/util/disjoint_set.h similarity index 98% rename from intern/cycles/util/util_disjoint_set.h rename to intern/cycles/util/disjoint_set.h index 946632371d2..5226423d7cd 100644 --- a/intern/cycles/util/util_disjoint_set.h +++ b/intern/cycles/util/disjoint_set.h @@ -17,7 +17,7 @@ #ifndef __UTIL_DISJOINT_SET_H__ #define __UTIL_DISJOINT_SET_H__ -#include "util_array.h" +#include "util/array.h" #include CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_foreach.h b/intern/cycles/util/foreach.h similarity index 100% rename from intern/cycles/util/util_foreach.h rename to intern/cycles/util/foreach.h diff --git a/intern/cycles/util/util_function.h b/intern/cycles/util/function.h similarity index 100% rename from intern/cycles/util/util_function.h rename to intern/cycles/util/function.h diff --git a/intern/cycles/util/util_guarded_allocator.cpp b/intern/cycles/util/guarded_allocator.cpp similarity index 93% rename from intern/cycles/util/util_guarded_allocator.cpp rename to intern/cycles/util/guarded_allocator.cpp index 1cb466a1ffa..4063b301331 100644 --- a/intern/cycles/util/util_guarded_allocator.cpp +++ b/intern/cycles/util/guarded_allocator.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "util/util_guarded_allocator.h" -#include "util/util_stats.h" +#include "util/guarded_allocator.h" +#include "util/stats.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_guarded_allocator.h b/intern/cycles/util/guarded_allocator.h similarity index 100% rename from intern/cycles/util/util_guarded_allocator.h rename to intern/cycles/util/guarded_allocator.h diff --git a/intern/cycles/util/util_half.h b/intern/cycles/util/half.h similarity index 98% rename from intern/cycles/util/util_half.h rename to intern/cycles/util/half.h index 0db5acd319a..016975e3c25 100644 --- a/intern/cycles/util/util_half.h +++ b/intern/cycles/util/half.h @@ -17,11 +17,11 @@ #ifndef __UTIL_HALF_H__ #define __UTIL_HALF_H__ -#include "util/util_math.h" -#include "util/util_types.h" +#include "util/math.h" +#include "util/types.h" #if !defined(__KERNEL_GPU__) && defined(__KERNEL_SSE2__) -# include "util/util_simd.h" +# include "util/simd.h" #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_hash.h b/intern/cycles/util/hash.h similarity index 99% rename from intern/cycles/util/util_hash.h rename to intern/cycles/util/hash.h index 0021eec169b..013a0f90a27 100644 --- a/intern/cycles/util/util_hash.h +++ b/intern/cycles/util/hash.h @@ -17,7 +17,7 @@ #ifndef __UTIL_HASH_H__ #define __UTIL_HASH_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_ies.cpp b/intern/cycles/util/ies.cpp similarity index 99% rename from intern/cycles/util/util_ies.cpp rename to intern/cycles/util/ies.cpp index 62d3d42186d..5e879478df5 100644 --- a/intern/cycles/util/util_ies.cpp +++ b/intern/cycles/util/ies.cpp @@ -16,10 +16,10 @@ #include -#include "util/util_foreach.h" -#include "util/util_ies.h" -#include "util/util_math.h" -#include "util/util_string.h" +#include "util/foreach.h" +#include "util/ies.h" +#include "util/math.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_ies.h b/intern/cycles/util/ies.h similarity index 96% rename from intern/cycles/util/util_ies.h rename to intern/cycles/util/ies.h index 95473103614..7be072dd5f5 100644 --- a/intern/cycles/util/util_ies.h +++ b/intern/cycles/util/ies.h @@ -17,8 +17,8 @@ #ifndef __UTIL_IES_H__ #define __UTIL_IES_H__ -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_image.h b/intern/cycles/util/image.h similarity index 96% rename from intern/cycles/util/util_image.h rename to intern/cycles/util/image.h index b082b971613..69fc3a50c1d 100644 --- a/intern/cycles/util/util_image.h +++ b/intern/cycles/util/image.h @@ -21,8 +21,8 @@ # include -# include "util/util_half.h" -# include "util/util_vector.h" +# include "util/half.h" +# include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -95,4 +95,4 @@ CCL_NAMESPACE_END #endif /* __UTIL_IMAGE_H__ */ -#include "util/util_image_impl.h" +#include "util/image_impl.h" diff --git a/intern/cycles/util/util_image_impl.h b/intern/cycles/util/image_impl.h similarity index 98% rename from intern/cycles/util/util_image_impl.h rename to intern/cycles/util/image_impl.h index 3eb30d070ea..3d8eed80775 100644 --- a/intern/cycles/util/util_image_impl.h +++ b/intern/cycles/util/image_impl.h @@ -17,9 +17,9 @@ #ifndef __UTIL_IMAGE_IMPL_H__ #define __UTIL_IMAGE_IMPL_H__ -#include "util/util_algorithm.h" -#include "util/util_half.h" -#include "util/util_image.h" +#include "util/algorithm.h" +#include "util/half.h" +#include "util/image.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_list.h b/intern/cycles/util/list.h similarity index 100% rename from intern/cycles/util/util_list.h rename to intern/cycles/util/list.h diff --git a/intern/cycles/util/util_logging.cpp b/intern/cycles/util/log.cpp similarity index 96% rename from intern/cycles/util/util_logging.cpp rename to intern/cycles/util/log.cpp index 8272728a7a0..68a5a3f576f 100644 --- a/intern/cycles/util/util_logging.cpp +++ b/intern/cycles/util/log.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "util/util_logging.h" +#include "util/log.h" -#include "util/util_math.h" -#include "util/util_string.h" +#include "util/math.h" +#include "util/string.h" #include #ifdef _MSC_VER diff --git a/intern/cycles/util/util_logging.h b/intern/cycles/util/log.h similarity index 100% rename from intern/cycles/util/util_logging.h rename to intern/cycles/util/log.h diff --git a/intern/cycles/util/util_map.h b/intern/cycles/util/map.h similarity index 100% rename from intern/cycles/util/util_map.h rename to intern/cycles/util/map.h diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/math.h similarity index 98% rename from intern/cycles/util/util_math.h rename to intern/cycles/util/math.h index 535b6881d3f..e7fc492733f 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/math.h @@ -34,7 +34,7 @@ #include #include -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN @@ -474,15 +474,15 @@ ccl_device_inline float cubic_interp(float a, float b, float c, float d, float x CCL_NAMESPACE_END -#include "util/util_math_int2.h" -#include "util/util_math_int3.h" -#include "util/util_math_int4.h" +#include "util/math_int2.h" +#include "util/math_int3.h" +#include "util/math_int4.h" -#include "util/util_math_float2.h" -#include "util/util_math_float3.h" -#include "util/util_math_float4.h" +#include "util/math_float2.h" +#include "util/math_float3.h" +#include "util/math_float4.h" -#include "util/util_rect.h" +#include "util/rect.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_cdf.cpp b/intern/cycles/util/math_cdf.cpp similarity index 95% rename from intern/cycles/util/util_math_cdf.cpp rename to intern/cycles/util/math_cdf.cpp index a58bab188ef..02c6646f824 100644 --- a/intern/cycles/util/util_math_cdf.cpp +++ b/intern/cycles/util/math_cdf.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "util/util_math_cdf.h" +#include "util/math_cdf.h" -#include "util/util_algorithm.h" -#include "util/util_math.h" +#include "util/algorithm.h" +#include "util/math.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_cdf.h b/intern/cycles/util/math_cdf.h similarity index 96% rename from intern/cycles/util/util_math_cdf.h rename to intern/cycles/util/math_cdf.h index 43995204263..4c57dac4bbe 100644 --- a/intern/cycles/util/util_math_cdf.h +++ b/intern/cycles/util/math_cdf.h @@ -17,9 +17,9 @@ #ifndef __UTIL_MATH_CDF_H__ #define __UTIL_MATH_CDF_H__ -#include "util/util_algorithm.h" -#include "util/util_math.h" -#include "util/util_vector.h" +#include "util/algorithm.h" +#include "util/math.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/math_fast.h similarity index 100% rename from intern/cycles/util/util_math_fast.h rename to intern/cycles/util/math_fast.h diff --git a/intern/cycles/util/util_math_float2.h b/intern/cycles/util/math_float2.h similarity index 99% rename from intern/cycles/util/util_math_float2.h rename to intern/cycles/util/math_float2.h index 25eda840214..87141d5bc37 100644 --- a/intern/cycles/util/util_math_float2.h +++ b/intern/cycles/util/math_float2.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_FLOAT2_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_float3.h b/intern/cycles/util/math_float3.h similarity index 99% rename from intern/cycles/util/util_math_float3.h rename to intern/cycles/util/math_float3.h index c3230a8068c..e780d7e0a7c 100644 --- a/intern/cycles/util/util_math_float3.h +++ b/intern/cycles/util/math_float3.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_FLOAT3_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_float4.h b/intern/cycles/util/math_float4.h similarity index 99% rename from intern/cycles/util/util_math_float4.h rename to intern/cycles/util/math_float4.h index f30a78cfc69..c76959ee7ff 100644 --- a/intern/cycles/util/util_math_float4.h +++ b/intern/cycles/util/math_float4.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_FLOAT4_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_int2.h b/intern/cycles/util/math_int2.h similarity index 96% rename from intern/cycles/util/util_math_int2.h rename to intern/cycles/util/math_int2.h index 5782b878801..5b04be92152 100644 --- a/intern/cycles/util/util_math_int2.h +++ b/intern/cycles/util/math_int2.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_INT2_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_int3.h b/intern/cycles/util/math_int3.h similarity index 97% rename from intern/cycles/util/util_math_int3.h rename to intern/cycles/util/math_int3.h index e0dfae7c015..128f2cb53b8 100644 --- a/intern/cycles/util/util_math_int3.h +++ b/intern/cycles/util/math_int3.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_INT3_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_int4.h b/intern/cycles/util/math_int4.h similarity index 98% rename from intern/cycles/util/util_math_int4.h rename to intern/cycles/util/math_int4.h index 186cc58489b..9e3f001efc2 100644 --- a/intern/cycles/util/util_math_int4.h +++ b/intern/cycles/util/math_int4.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_INT4_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_intersect.h b/intern/cycles/util/math_intersect.h similarity index 100% rename from intern/cycles/util/util_math_intersect.h rename to intern/cycles/util/math_intersect.h diff --git a/intern/cycles/util/util_math_matrix.h b/intern/cycles/util/math_matrix.h similarity index 100% rename from intern/cycles/util/util_math_matrix.h rename to intern/cycles/util/math_matrix.h diff --git a/intern/cycles/util/util_md5.cpp b/intern/cycles/util/md5.cpp similarity index 99% rename from intern/cycles/util/util_md5.cpp rename to intern/cycles/util/md5.cpp index 0df521c2b58..47e489b1aed 100644 --- a/intern/cycles/util/util_md5.cpp +++ b/intern/cycles/util/md5.cpp @@ -23,8 +23,8 @@ /* Minor modifications done to remove some code and change style. */ -#include "util_md5.h" -#include "util_path.h" +#include "util/md5.h" +#include "util/path.h" #include #include diff --git a/intern/cycles/util/util_md5.h b/intern/cycles/util/md5.h similarity index 96% rename from intern/cycles/util/util_md5.h rename to intern/cycles/util/md5.h index 3102a0f4bad..cc7cbef6a49 100644 --- a/intern/cycles/util/util_md5.h +++ b/intern/cycles/util/md5.h @@ -30,8 +30,8 @@ #ifndef __UTIL_MD5_H__ #define __UTIL_MD5_H__ -#include "util/util_string.h" -#include "util/util_types.h" +#include "util/string.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_murmurhash.cpp b/intern/cycles/util/murmurhash.cpp similarity index 97% rename from intern/cycles/util/util_murmurhash.cpp rename to intern/cycles/util/murmurhash.cpp index 5d728769fe9..9ba0a282cc2 100644 --- a/intern/cycles/util/util_murmurhash.cpp +++ b/intern/cycles/util/murmurhash.cpp @@ -23,8 +23,8 @@ #include #include -#include "util/util_algorithm.h" -#include "util/util_murmurhash.h" +#include "util/algorithm.h" +#include "util/murmurhash.h" #if defined(_MSC_VER) # define ROTL32(x, y) _rotl(x, y) diff --git a/intern/cycles/util/util_murmurhash.h b/intern/cycles/util/murmurhash.h similarity index 96% rename from intern/cycles/util/util_murmurhash.h rename to intern/cycles/util/murmurhash.h index 2ec87efd87a..7c303db6ffa 100644 --- a/intern/cycles/util/util_murmurhash.h +++ b/intern/cycles/util/murmurhash.h @@ -17,7 +17,7 @@ #ifndef __UTIL_MURMURHASH_H__ #define __UTIL_MURMURHASH_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_opengl.h b/intern/cycles/util/opengl.h similarity index 100% rename from intern/cycles/util/util_opengl.h rename to intern/cycles/util/opengl.h diff --git a/intern/cycles/util/util_openimagedenoise.h b/intern/cycles/util/openimagedenoise.h similarity index 97% rename from intern/cycles/util/util_openimagedenoise.h rename to intern/cycles/util/openimagedenoise.h index 898c634141e..cc7b14ae18f 100644 --- a/intern/cycles/util/util_openimagedenoise.h +++ b/intern/cycles/util/openimagedenoise.h @@ -21,7 +21,7 @@ # include #endif -#include "util_system.h" +#include "util/system.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_openvdb.h b/intern/cycles/util/openvdb.h similarity index 100% rename from intern/cycles/util/util_openvdb.h rename to intern/cycles/util/openvdb.h diff --git a/intern/cycles/util/util_optimization.h b/intern/cycles/util/optimization.h similarity index 100% rename from intern/cycles/util/util_optimization.h rename to intern/cycles/util/optimization.h diff --git a/intern/cycles/util/util_param.h b/intern/cycles/util/param.h similarity index 100% rename from intern/cycles/util/util_param.h rename to intern/cycles/util/param.h diff --git a/intern/cycles/util/util_path.cpp b/intern/cycles/util/path.cpp similarity index 99% rename from intern/cycles/util/util_path.cpp rename to intern/cycles/util/path.cpp index c78f4615013..5704c4ef8ef 100644 --- a/intern/cycles/util/util_path.cpp +++ b/intern/cycles/util/path.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "util/util_path.h" -#include "util/util_md5.h" -#include "util/util_string.h" +#include "util/path.h" +#include "util/md5.h" +#include "util/string.h" #include #include @@ -44,8 +44,8 @@ OIIO_NAMESPACE_USING # include #endif -#include "util/util_map.h" -#include "util/util_windows.h" +#include "util/map.h" +#include "util/windows.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_path.h b/intern/cycles/util/path.h similarity index 95% rename from intern/cycles/util/util_path.h rename to intern/cycles/util/path.h index f899bc2e01c..a1394555302 100644 --- a/intern/cycles/util/util_path.h +++ b/intern/cycles/util/path.h @@ -24,10 +24,10 @@ #include -#include "util/util_set.h" -#include "util/util_string.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/set.h" +#include "util/string.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_profiling.cpp b/intern/cycles/util/profiling.cpp similarity index 97% rename from intern/cycles/util/util_profiling.cpp rename to intern/cycles/util/profiling.cpp index 5343f076e22..55b35b7320f 100644 --- a/intern/cycles/util/util_profiling.cpp +++ b/intern/cycles/util/profiling.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "util/util_profiling.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_set.h" +#include "util/profiling.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/set.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_profiling.h b/intern/cycles/util/profiling.h similarity index 98% rename from intern/cycles/util/util_profiling.h rename to intern/cycles/util/profiling.h index 96bb682c50e..b30aac90879 100644 --- a/intern/cycles/util/util_profiling.h +++ b/intern/cycles/util/profiling.h @@ -19,9 +19,9 @@ #include -#include "util/util_map.h" -#include "util/util_thread.h" -#include "util/util_vector.h" +#include "util/map.h" +#include "util/thread.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_progress.h b/intern/cycles/util/progress.h similarity index 98% rename from intern/cycles/util/util_progress.h rename to intern/cycles/util/progress.h index 176ee11e1e9..4b0ff08aa7e 100644 --- a/intern/cycles/util/util_progress.h +++ b/intern/cycles/util/progress.h @@ -23,10 +23,10 @@ * update notifications from a job running in another thread. All methods * except for the constructor/destructor are thread safe. */ -#include "util/util_function.h" -#include "util/util_string.h" -#include "util/util_thread.h" -#include "util/util_time.h" +#include "util/function.h" +#include "util/string.h" +#include "util/thread.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_projection.h b/intern/cycles/util/projection.h similarity index 99% rename from intern/cycles/util/util_projection.h rename to intern/cycles/util/projection.h index 04b4574d75b..8d822a3777d 100644 --- a/intern/cycles/util/util_projection.h +++ b/intern/cycles/util/projection.h @@ -17,7 +17,7 @@ #ifndef __UTIL_PROJECTION_H__ #define __UTIL_PROJECTION_H__ -#include "util/util_transform.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_queue.h b/intern/cycles/util/queue.h similarity index 100% rename from intern/cycles/util/util_queue.h rename to intern/cycles/util/queue.h diff --git a/intern/cycles/util/util_rect.h b/intern/cycles/util/rect.h similarity index 98% rename from intern/cycles/util/util_rect.h rename to intern/cycles/util/rect.h index 32df9327cbd..79d64b917b7 100644 --- a/intern/cycles/util/util_rect.h +++ b/intern/cycles/util/rect.h @@ -17,7 +17,7 @@ #ifndef __UTIL_RECT_H__ #define __UTIL_RECT_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_semaphore.h b/intern/cycles/util/semaphore.h similarity index 97% rename from intern/cycles/util/util_semaphore.h rename to intern/cycles/util/semaphore.h index d995b0732b8..8da8a232ba2 100644 --- a/intern/cycles/util/util_semaphore.h +++ b/intern/cycles/util/semaphore.h @@ -17,7 +17,7 @@ #ifndef __UTIL_SEMAPHORE_H__ #define __UTIL_SEMAPHORE_H__ -#include "util/util_thread.h" +#include "util/thread.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_set.h b/intern/cycles/util/set.h similarity index 100% rename from intern/cycles/util/util_set.h rename to intern/cycles/util/set.h diff --git a/intern/cycles/util/util_simd.cpp b/intern/cycles/util/simd.cpp similarity index 98% rename from intern/cycles/util/util_simd.cpp rename to intern/cycles/util/simd.cpp index 861dcf1fe36..089444bb6cc 100644 --- a/intern/cycles/util/util_simd.cpp +++ b/intern/cycles/util/simd.cpp @@ -18,7 +18,7 @@ #if (defined(WITH_KERNEL_SSE2)) || (defined(WITH_KERNEL_NATIVE) && defined(__SSE2__)) # define __KERNEL_SSE2__ -# include "util/util_simd.h" +# include "util/simd.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_simd.h b/intern/cycles/util/simd.h similarity index 99% rename from intern/cycles/util/util_simd.h rename to intern/cycles/util/simd.h index b4a153c329f..cc4950891d0 100644 --- a/intern/cycles/util/util_simd.h +++ b/intern/cycles/util/simd.h @@ -21,7 +21,7 @@ #include #include -#include "util/util_defines.h" +#include "util/defines.h" /* SSE Intrinsics includes * @@ -30,7 +30,7 @@ * MinGW64 has conflicting declarations for these SSE headers in . * Since we can't avoid including , better only include that */ #if defined(FREE_WINDOWS64) -# include "util/util_windows.h" +# include "util/windows.h" #elif defined(_MSC_VER) # include #elif (defined(__x86_64__) || defined(__i386__)) diff --git a/intern/cycles/util/util_sseb.h b/intern/cycles/util/sseb.h similarity index 100% rename from intern/cycles/util/util_sseb.h rename to intern/cycles/util/sseb.h diff --git a/intern/cycles/util/util_ssef.h b/intern/cycles/util/ssef.h similarity index 99% rename from intern/cycles/util/util_ssef.h rename to intern/cycles/util/ssef.h index 0c81ed87553..ea5e78b54d2 100644 --- a/intern/cycles/util/util_ssef.h +++ b/intern/cycles/util/ssef.h @@ -18,7 +18,7 @@ #ifndef __UTIL_SSEF_H__ #define __UTIL_SSEF_H__ -#include "util_ssei.h" +#include "util/ssei.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_ssei.h b/intern/cycles/util/ssei.h similarity index 100% rename from intern/cycles/util/util_ssei.h rename to intern/cycles/util/ssei.h diff --git a/intern/cycles/util/util_stack_allocator.h b/intern/cycles/util/stack_allocator.h similarity index 100% rename from intern/cycles/util/util_stack_allocator.h rename to intern/cycles/util/stack_allocator.h diff --git a/intern/cycles/util/util_static_assert.h b/intern/cycles/util/static_assert.h similarity index 100% rename from intern/cycles/util/util_static_assert.h rename to intern/cycles/util/static_assert.h diff --git a/intern/cycles/util/util_stats.h b/intern/cycles/util/stats.h similarity index 94% rename from intern/cycles/util/util_stats.h rename to intern/cycles/util/stats.h index 15cf836de3c..590973f1cbc 100644 --- a/intern/cycles/util/util_stats.h +++ b/intern/cycles/util/stats.h @@ -17,8 +17,8 @@ #ifndef __UTIL_STATS_H__ #define __UTIL_STATS_H__ -#include "util/util_atomic.h" -#include "util/util_profiling.h" +#include "util/atomic.h" +#include "util/profiling.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_string.cpp b/intern/cycles/util/string.cpp similarity index 98% rename from intern/cycles/util/util_string.cpp rename to intern/cycles/util/string.cpp index 0fc9cb4ae77..b98272f7759 100644 --- a/intern/cycles/util/util_string.cpp +++ b/intern/cycles/util/string.cpp @@ -20,9 +20,9 @@ #include #include -#include "util/util_foreach.h" -#include "util/util_string.h" -#include "util/util_windows.h" +#include "util/foreach.h" +#include "util/string.h" +#include "util/windows.h" #ifdef _WIN32 # ifndef vsnprintf diff --git a/intern/cycles/util/util_string.h b/intern/cycles/util/string.h similarity index 95% rename from intern/cycles/util/util_string.h rename to intern/cycles/util/string.h index 55462cfd8b8..cc20a6df120 100644 --- a/intern/cycles/util/util_string.h +++ b/intern/cycles/util/string.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __UTIL_STRING_H__ -#define __UTIL_STRING_H__ +#pragma once #include #include @@ -26,7 +25,7 @@ * namespace OIIO as it causes symbol collision. */ #include -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -80,5 +79,3 @@ string string_human_readable_size(size_t size); string string_human_readable_number(size_t num); CCL_NAMESPACE_END - -#endif /* __UTIL_STRING_H__ */ diff --git a/intern/cycles/util/util_system.cpp b/intern/cycles/util/system.cpp similarity index 98% rename from intern/cycles/util/util_system.cpp rename to intern/cycles/util/system.cpp index be8c2fb505a..f12e15e756f 100644 --- a/intern/cycles/util/util_system.cpp +++ b/intern/cycles/util/system.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "util/util_system.h" +#include "util/system.h" -#include "util/util_logging.h" -#include "util/util_string.h" -#include "util/util_types.h" +#include "util/log.h" +#include "util/string.h" +#include "util/types.h" #include @@ -29,7 +29,7 @@ OIIO_NAMESPACE_USING # if (!defined(FREE_WINDOWS)) # include # endif -# include "util_windows.h" +# include "util/windows.h" #elif defined(__APPLE__) # include # include diff --git a/intern/cycles/util/util_system.h b/intern/cycles/util/system.h similarity index 97% rename from intern/cycles/util/util_system.h rename to intern/cycles/util/system.h index a1797e6ca44..425c7255cbe 100644 --- a/intern/cycles/util/util_system.h +++ b/intern/cycles/util/system.h @@ -17,8 +17,8 @@ #ifndef __UTIL_SYSTEM_H__ #define __UTIL_SYSTEM_H__ -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_task.cpp b/intern/cycles/util/task.cpp similarity index 96% rename from intern/cycles/util/util_task.cpp rename to intern/cycles/util/task.cpp index 949ba0a7b4d..ce61bf8d6c4 100644 --- a/intern/cycles/util/util_task.cpp +++ b/intern/cycles/util/task.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "util/util_task.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_system.h" -#include "util/util_time.h" +#include "util/task.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/system.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_task.h b/intern/cycles/util/task.h similarity index 96% rename from intern/cycles/util/util_task.h rename to intern/cycles/util/task.h index ec45dfa8040..1a8f512b83a 100644 --- a/intern/cycles/util/util_task.h +++ b/intern/cycles/util/task.h @@ -17,11 +17,11 @@ #ifndef __UTIL_TASK_H__ #define __UTIL_TASK_H__ -#include "util/util_list.h" -#include "util/util_string.h" -#include "util/util_tbb.h" -#include "util/util_thread.h" -#include "util/util_vector.h" +#include "util/list.h" +#include "util/string.h" +#include "util/tbb.h" +#include "util/thread.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_tbb.h b/intern/cycles/util/tbb.h similarity index 98% rename from intern/cycles/util/util_tbb.h rename to intern/cycles/util/tbb.h index 8f84377ac8c..6fc3b8daad3 100644 --- a/intern/cycles/util/util_tbb.h +++ b/intern/cycles/util/tbb.h @@ -19,7 +19,7 @@ /* TBB includes , do it ourselves first so we are sure * WIN32_LEAN_AND_MEAN and similar are defined beforehand. */ -#include "util_windows.h" +#include "util/windows.h" #include #include diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/texture.h similarity index 98% rename from intern/cycles/util/util_texture.h rename to intern/cycles/util/texture.h index 4de66bf5f46..5e37b79e340 100644 --- a/intern/cycles/util/util_texture.h +++ b/intern/cycles/util/texture.h @@ -17,7 +17,7 @@ #ifndef __UTIL_TEXTURE_H__ #define __UTIL_TEXTURE_H__ -#include "util_transform.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_thread.cpp b/intern/cycles/util/thread.cpp similarity index 94% rename from intern/cycles/util/util_thread.cpp rename to intern/cycles/util/thread.cpp index cccde5ae7d5..24a0600425d 100644 --- a/intern/cycles/util/util_thread.cpp +++ b/intern/cycles/util/thread.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "util/util_thread.h" +#include "util/thread.h" -#include "util/util_system.h" -#include "util/util_windows.h" +#include "util/system.h" +#include "util/windows.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_thread.h b/intern/cycles/util/thread.h similarity index 97% rename from intern/cycles/util/util_thread.h rename to intern/cycles/util/thread.h index 29f9becbefe..09686e4b23f 100644 --- a/intern/cycles/util/util_thread.h +++ b/intern/cycles/util/thread.h @@ -24,7 +24,7 @@ #include #ifdef _WIN32 -# include "util_windows.h" +# include "util/windows.h" #else # include #endif @@ -33,7 +33,7 @@ * functionality requires RTTI, which is disabled for OSL kernel. */ #include -#include "util/util_function.h" +#include "util/function.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_time.cpp b/intern/cycles/util/time.cpp similarity index 96% rename from intern/cycles/util/util_time.cpp rename to intern/cycles/util/time.cpp index 1641395d07e..62d14b063be 100644 --- a/intern/cycles/util/util_time.cpp +++ b/intern/cycles/util/time.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "util/util_time.h" +#include "util/time.h" #include @@ -23,9 +23,9 @@ # include #endif -#include "util/util_math.h" -#include "util/util_string.h" -#include "util/util_windows.h" +#include "util/math.h" +#include "util/string.h" +#include "util/windows.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_time.h b/intern/cycles/util/time.h similarity index 96% rename from intern/cycles/util/util_time.h rename to intern/cycles/util/time.h index a82d400a0d7..380921664e8 100644 --- a/intern/cycles/util/util_time.h +++ b/intern/cycles/util/time.h @@ -17,8 +17,8 @@ #ifndef __UTIL_TIME_H__ #define __UTIL_TIME_H__ -#include "util/util_function.h" -#include "util/util_string.h" +#include "util/function.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_transform.cpp b/intern/cycles/util/transform.cpp similarity index 98% rename from intern/cycles/util/util_transform.cpp rename to intern/cycles/util/transform.cpp index e8233b7fe6d..bd990cb0f79 100644 --- a/intern/cycles/util/util_transform.cpp +++ b/intern/cycles/util/transform.cpp @@ -46,11 +46,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "util/util_transform.h" -#include "util/util_projection.h" +#include "util/transform.h" +#include "util/projection.h" -#include "util/util_boundbox.h" -#include "util/util_math.h" +#include "util/boundbox.h" +#include "util/math.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/transform.h similarity index 99% rename from intern/cycles/util/util_transform.h rename to intern/cycles/util/transform.h index fc04f9aab46..7bfe747fcfb 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/transform.h @@ -21,8 +21,8 @@ # include #endif -#include "util/util_math.h" -#include "util/util_types.h" +#include "util/math.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types.h b/intern/cycles/util/types.h similarity index 59% rename from intern/cycles/util/util_types.h rename to intern/cycles/util/types.h index 442c32b3a3d..697dc2b44ea 100644 --- a/intern/cycles/util/util_types.h +++ b/intern/cycles/util/types.h @@ -25,11 +25,11 @@ # include #endif -#include "util/util_defines.h" +#include "util/defines.h" #ifndef __KERNEL_GPU__ -# include "util/util_optimization.h" -# include "util/util_simd.h" +# include "util/optimization.h" +# include "util/simd.h" #endif CCL_NAMESPACE_BEGIN @@ -82,56 +82,56 @@ ccl_device_inline bool is_power_of_two(size_t x) CCL_NAMESPACE_END /* Vectorized types declaration. */ -#include "util/util_types_uchar2.h" -#include "util/util_types_uchar3.h" -#include "util/util_types_uchar4.h" +#include "util/types_uchar2.h" +#include "util/types_uchar3.h" +#include "util/types_uchar4.h" -#include "util/util_types_int2.h" -#include "util/util_types_int3.h" -#include "util/util_types_int4.h" +#include "util/types_int2.h" +#include "util/types_int3.h" +#include "util/types_int4.h" -#include "util/util_types_uint2.h" -#include "util/util_types_uint3.h" -#include "util/util_types_uint4.h" +#include "util/types_uint2.h" +#include "util/types_uint3.h" +#include "util/types_uint4.h" -#include "util/util_types_ushort4.h" +#include "util/types_ushort4.h" -#include "util/util_types_float2.h" -#include "util/util_types_float3.h" -#include "util/util_types_float4.h" -#include "util/util_types_float8.h" +#include "util/types_float2.h" +#include "util/types_float3.h" +#include "util/types_float4.h" +#include "util/types_float8.h" -#include "util/util_types_vector3.h" +#include "util/types_vector3.h" /* Vectorized types implementation. */ -#include "util/util_types_uchar2_impl.h" -#include "util/util_types_uchar3_impl.h" -#include "util/util_types_uchar4_impl.h" +#include "util/types_uchar2_impl.h" +#include "util/types_uchar3_impl.h" +#include "util/types_uchar4_impl.h" -#include "util/util_types_int2_impl.h" -#include "util/util_types_int3_impl.h" -#include "util/util_types_int4_impl.h" +#include "util/types_int2_impl.h" +#include "util/types_int3_impl.h" +#include "util/types_int4_impl.h" -#include "util/util_types_uint2_impl.h" -#include "util/util_types_uint3_impl.h" -#include "util/util_types_uint4_impl.h" +#include "util/types_uint2_impl.h" +#include "util/types_uint3_impl.h" +#include "util/types_uint4_impl.h" -#include "util/util_types_float2_impl.h" -#include "util/util_types_float3_impl.h" -#include "util/util_types_float4_impl.h" -#include "util/util_types_float8_impl.h" +#include "util/types_float2_impl.h" +#include "util/types_float3_impl.h" +#include "util/types_float4_impl.h" +#include "util/types_float8_impl.h" -#include "util/util_types_vector3_impl.h" +#include "util/types_vector3_impl.h" /* SSE types. */ #ifndef __KERNEL_GPU__ -# include "util/util_sseb.h" -# include "util/util_ssef.h" -# include "util/util_ssei.h" +# include "util/sseb.h" +# include "util/ssef.h" +# include "util/ssei.h" # if defined(__KERNEL_AVX__) || defined(__KERNEL_AVX2__) -# include "util/util_avxb.h" -# include "util/util_avxf.h" -# include "util/util_avxi.h" +# include "util/avxb.h" +# include "util/avxf.h" +# include "util/avxi.h" # endif #endif diff --git a/intern/cycles/util/util_types_float2.h b/intern/cycles/util/types_float2.h similarity index 94% rename from intern/cycles/util/util_types_float2.h rename to intern/cycles/util/types_float2.h index 3760bf579b6..e71204bef5b 100644 --- a/intern/cycles/util/util_types_float2.h +++ b/intern/cycles/util/types_float2.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT2_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_float2_impl.h b/intern/cycles/util/types_float2_impl.h similarity index 95% rename from intern/cycles/util/util_types_float2_impl.h rename to intern/cycles/util/types_float2_impl.h index 7810d2a8781..c02c13f8c47 100644 --- a/intern/cycles/util/util_types_float2_impl.h +++ b/intern/cycles/util/types_float2_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT2_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_float3.h b/intern/cycles/util/types_float3.h similarity index 96% rename from intern/cycles/util/util_types_float3.h rename to intern/cycles/util/types_float3.h index 694a600bf5c..f990367e7b8 100644 --- a/intern/cycles/util/util_types_float3.h +++ b/intern/cycles/util/types_float3.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT3_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_float3_impl.h b/intern/cycles/util/types_float3_impl.h similarity index 97% rename from intern/cycles/util/util_types_float3_impl.h rename to intern/cycles/util/types_float3_impl.h index ab25fb4c975..76a9067acc7 100644 --- a/intern/cycles/util/util_types_float3_impl.h +++ b/intern/cycles/util/types_float3_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT3_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_float4.h b/intern/cycles/util/types_float4.h similarity index 96% rename from intern/cycles/util/util_types_float4.h rename to intern/cycles/util/types_float4.h index c29e6e15bc3..8d4e07e7e4d 100644 --- a/intern/cycles/util/util_types_float4.h +++ b/intern/cycles/util/types_float4.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT4_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_float4_impl.h b/intern/cycles/util/types_float4_impl.h similarity index 97% rename from intern/cycles/util/util_types_float4_impl.h rename to intern/cycles/util/types_float4_impl.h index 05a1feee5b2..d75715332e5 100644 --- a/intern/cycles/util/util_types_float4_impl.h +++ b/intern/cycles/util/types_float4_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT4_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_float8.h b/intern/cycles/util/types_float8.h similarity index 97% rename from intern/cycles/util/util_types_float8.h rename to intern/cycles/util/types_float8.h index 27da120a4ba..cf1f66b7622 100644 --- a/intern/cycles/util/util_types_float8.h +++ b/intern/cycles/util/types_float8.h @@ -30,7 +30,7 @@ #define __UTIL_TYPES_FLOAT8_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_float8_impl.h b/intern/cycles/util/types_float8_impl.h similarity index 97% rename from intern/cycles/util/util_types_float8_impl.h rename to intern/cycles/util/types_float8_impl.h index 4e4ea28c6a4..a795666adc7 100644 --- a/intern/cycles/util/util_types_float8_impl.h +++ b/intern/cycles/util/types_float8_impl.h @@ -30,7 +30,7 @@ #define __UTIL_TYPES_FLOAT8_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_int2.h b/intern/cycles/util/types_int2.h similarity index 93% rename from intern/cycles/util/util_types_int2.h rename to intern/cycles/util/types_int2.h index 8811e5ec7c2..75970577d77 100644 --- a/intern/cycles/util/util_types_int2.h +++ b/intern/cycles/util/types_int2.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT2_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_int2_impl.h b/intern/cycles/util/types_int2_impl.h similarity index 94% rename from intern/cycles/util/util_types_int2_impl.h rename to intern/cycles/util/types_int2_impl.h index ce95d4f14e5..efa63cdfd2a 100644 --- a/intern/cycles/util/util_types_int2_impl.h +++ b/intern/cycles/util/types_int2_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT2_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_int3.h b/intern/cycles/util/types_int3.h similarity index 96% rename from intern/cycles/util/util_types_int3.h rename to intern/cycles/util/types_int3.h index 09edc09dff3..071a886136e 100644 --- a/intern/cycles/util/util_types_int3.h +++ b/intern/cycles/util/types_int3.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT3_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_int3_impl.h b/intern/cycles/util/types_int3_impl.h similarity index 96% rename from intern/cycles/util/util_types_int3_impl.h rename to intern/cycles/util/types_int3_impl.h index 080c892640b..c91c64b804e 100644 --- a/intern/cycles/util/util_types_int3_impl.h +++ b/intern/cycles/util/types_int3_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT3_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_int4.h b/intern/cycles/util/types_int4.h similarity index 96% rename from intern/cycles/util/util_types_int4.h rename to intern/cycles/util/types_int4.h index 5c7917cf5d6..cb497d70035 100644 --- a/intern/cycles/util/util_types_int4.h +++ b/intern/cycles/util/types_int4.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT4_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_int4_impl.h b/intern/cycles/util/types_int4_impl.h similarity index 97% rename from intern/cycles/util/util_types_int4_impl.h rename to intern/cycles/util/types_int4_impl.h index c6f6ff23a17..258b42c029e 100644 --- a/intern/cycles/util/util_types_int4_impl.h +++ b/intern/cycles/util/types_int4_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT4_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_uchar2.h b/intern/cycles/util/types_uchar2.h similarity index 94% rename from intern/cycles/util/util_types_uchar2.h rename to intern/cycles/util/types_uchar2.h index 8cc486e3e48..0dc1d46bf29 100644 --- a/intern/cycles/util/util_types_uchar2.h +++ b/intern/cycles/util/types_uchar2.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR2_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uchar2_impl.h b/intern/cycles/util/types_uchar2_impl.h similarity index 94% rename from intern/cycles/util/util_types_uchar2_impl.h rename to intern/cycles/util/types_uchar2_impl.h index 16968c32dd9..234a71a2247 100644 --- a/intern/cycles/util/util_types_uchar2_impl.h +++ b/intern/cycles/util/types_uchar2_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR2_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uchar3.h b/intern/cycles/util/types_uchar3.h similarity index 94% rename from intern/cycles/util/util_types_uchar3.h rename to intern/cycles/util/types_uchar3.h index 5838c437c70..d3913afb3a2 100644 --- a/intern/cycles/util/util_types_uchar3.h +++ b/intern/cycles/util/types_uchar3.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR3_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uchar3_impl.h b/intern/cycles/util/types_uchar3_impl.h similarity index 94% rename from intern/cycles/util/util_types_uchar3_impl.h rename to intern/cycles/util/types_uchar3_impl.h index aa31b725731..90f510e3b28 100644 --- a/intern/cycles/util/util_types_uchar3_impl.h +++ b/intern/cycles/util/types_uchar3_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR3_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uchar4.h b/intern/cycles/util/types_uchar4.h similarity index 94% rename from intern/cycles/util/util_types_uchar4.h rename to intern/cycles/util/types_uchar4.h index 22b6a1ac705..bfe1c06acd8 100644 --- a/intern/cycles/util/util_types_uchar4.h +++ b/intern/cycles/util/types_uchar4.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR4_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uchar4_impl.h b/intern/cycles/util/types_uchar4_impl.h similarity index 94% rename from intern/cycles/util/util_types_uchar4_impl.h rename to intern/cycles/util/types_uchar4_impl.h index 79879f176a6..d15c74bed03 100644 --- a/intern/cycles/util/util_types_uchar4_impl.h +++ b/intern/cycles/util/types_uchar4_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR4_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint2.h b/intern/cycles/util/types_uint2.h similarity index 94% rename from intern/cycles/util/util_types_uint2.h rename to intern/cycles/util/types_uint2.h index abcb8ee5346..7419977040b 100644 --- a/intern/cycles/util/util_types_uint2.h +++ b/intern/cycles/util/types_uint2.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT2_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint2_impl.h b/intern/cycles/util/types_uint2_impl.h similarity index 94% rename from intern/cycles/util/util_types_uint2_impl.h rename to intern/cycles/util/types_uint2_impl.h index db62bd99b89..8427f9694b5 100644 --- a/intern/cycles/util/util_types_uint2_impl.h +++ b/intern/cycles/util/types_uint2_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT2_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint3.h b/intern/cycles/util/types_uint3.h similarity index 94% rename from intern/cycles/util/util_types_uint3.h rename to intern/cycles/util/types_uint3.h index 436d870b621..1e97e7f2d36 100644 --- a/intern/cycles/util/util_types_uint3.h +++ b/intern/cycles/util/types_uint3.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT3_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint3_impl.h b/intern/cycles/util/types_uint3_impl.h similarity index 94% rename from intern/cycles/util/util_types_uint3_impl.h rename to intern/cycles/util/types_uint3_impl.h index d188fa06e2a..ba83cffe9a8 100644 --- a/intern/cycles/util/util_types_uint3_impl.h +++ b/intern/cycles/util/types_uint3_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT3_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint4.h b/intern/cycles/util/types_uint4.h similarity index 94% rename from intern/cycles/util/util_types_uint4.h rename to intern/cycles/util/types_uint4.h index 57f2859fedf..b135877b890 100644 --- a/intern/cycles/util/util_types_uint4.h +++ b/intern/cycles/util/types_uint4.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT4_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint4_impl.h b/intern/cycles/util/types_uint4_impl.h similarity index 94% rename from intern/cycles/util/util_types_uint4_impl.h rename to intern/cycles/util/types_uint4_impl.h index bac8d23030d..b860fbfc49a 100644 --- a/intern/cycles/util/util_types_uint4_impl.h +++ b/intern/cycles/util/types_uint4_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT4_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_ushort4.h b/intern/cycles/util/types_ushort4.h similarity index 93% rename from intern/cycles/util/util_types_ushort4.h rename to intern/cycles/util/types_ushort4.h index 476ceec622c..8d080bcc1b9 100644 --- a/intern/cycles/util/util_types_ushort4.h +++ b/intern/cycles/util/types_ushort4.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_USHORT4_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_vector3.h b/intern/cycles/util/types_vector3.h similarity index 94% rename from intern/cycles/util/util_types_vector3.h rename to intern/cycles/util/types_vector3.h index 728c7ca62a1..d46a0266855 100644 --- a/intern/cycles/util/util_types_vector3.h +++ b/intern/cycles/util/types_vector3.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_VECTOR3_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_vector3_impl.h b/intern/cycles/util/types_vector3_impl.h similarity index 94% rename from intern/cycles/util/util_types_vector3_impl.h rename to intern/cycles/util/types_vector3_impl.h index 33ba53e20b2..ff6dcd85b12 100644 --- a/intern/cycles/util/util_types_vector3_impl.h +++ b/intern/cycles/util/types_vector3_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_VECTOR3_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_unique_ptr.h b/intern/cycles/util/unique_ptr.h similarity index 100% rename from intern/cycles/util/util_unique_ptr.h rename to intern/cycles/util/unique_ptr.h diff --git a/intern/cycles/util/util_vector.h b/intern/cycles/util/vector.h similarity index 93% rename from intern/cycles/util/util_vector.h rename to intern/cycles/util/vector.h index 87cd4de8438..db35f198dc1 100644 --- a/intern/cycles/util/util_vector.h +++ b/intern/cycles/util/vector.h @@ -21,9 +21,9 @@ #include #include -#include "util/util_aligned_malloc.h" -#include "util/util_guarded_allocator.h" -#include "util/util_types.h" +#include "util/aligned_malloc.h" +#include "util/guarded_allocator.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_version.h b/intern/cycles/util/version.h similarity index 100% rename from intern/cycles/util/util_version.h rename to intern/cycles/util/version.h diff --git a/intern/cycles/util/util_view.cpp b/intern/cycles/util/view.cpp similarity index 97% rename from intern/cycles/util/util_view.cpp rename to intern/cycles/util/view.cpp index 9d9ff451b3b..1c70cea1a8b 100644 --- a/intern/cycles/util/util_view.cpp +++ b/intern/cycles/util/view.cpp @@ -17,11 +17,11 @@ #include #include -#include "util/util_opengl.h" -#include "util/util_string.h" -#include "util/util_time.h" -#include "util/util_version.h" -#include "util/util_view.h" +#include "util/opengl.h" +#include "util/string.h" +#include "util/time.h" +#include "util/version.h" +#include "util/view.h" #ifdef __APPLE__ # include diff --git a/intern/cycles/util/util_view.h b/intern/cycles/util/view.h similarity index 100% rename from intern/cycles/util/util_view.h rename to intern/cycles/util/view.h diff --git a/intern/cycles/util/util_windows.cpp b/intern/cycles/util/windows.cpp similarity index 98% rename from intern/cycles/util/util_windows.cpp rename to intern/cycles/util/windows.cpp index 807a5adc84a..96944d07390 100644 --- a/intern/cycles/util/util_windows.cpp +++ b/intern/cycles/util/windows.cpp @@ -18,7 +18,7 @@ # include #endif -#include "util_windows.h" +#include "util/windows.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_windows.h b/intern/cycles/util/windows.h similarity index 100% rename from intern/cycles/util/util_windows.h rename to intern/cycles/util/windows.h diff --git a/intern/cycles/util/util_xml.h b/intern/cycles/util/xml.h similarity index 100% rename from intern/cycles/util/util_xml.h rename to intern/cycles/util/xml.h From 949dbb08d2693a899da19d3540d939ad5fb4fe24 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 26 Oct 2021 15:37:59 +0200 Subject: [PATCH 1200/1500] Cleanup: remove useless WITH_CYCLES_DEVICE_MULTI --- intern/cycles/CMakeLists.txt | 7 ------- intern/cycles/device/CMakeLists.txt | 3 --- intern/cycles/device/device.cpp | 2 -- 3 files changed, 12 deletions(-) diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index b6f3926f329..1500743763b 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -295,13 +295,6 @@ if(WITH_OPENIMAGEDENOISE) ) endif() -if(WITH_CYCLES_STANDALONE) - set(WITH_CYCLES_DEVICE_CUDA TRUE) - set(WITH_CYCLES_DEVICE_HIP TRUE) -endif() -# TODO(sergey): Consider removing it, only causes confusion in interface. -set(WITH_CYCLES_DEVICE_MULTI TRUE) - # Logging capabilities using GLog library. if(WITH_CYCLES_LOGGING) add_definitions(-DWITH_CYCLES_LOGGING) diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt index 39de4bec799..99b1fc8135d 100644 --- a/intern/cycles/device/CMakeLists.txt +++ b/intern/cycles/device/CMakeLists.txt @@ -158,9 +158,6 @@ endif() if(WITH_CYCLES_DEVICE_OPTIX) add_definitions(-DWITH_OPTIX) endif() -if(WITH_CYCLES_DEVICE_MULTI) - add_definitions(-DWITH_MULTI) -endif() if(WITH_OPENIMAGEDENOISE) list(APPEND LIB diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index 5179f3bacdb..69e959b6f7b 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -71,14 +71,12 @@ void Device::build_bvh(BVH *bvh, Progress &progress, bool refit) Device *Device::create(const DeviceInfo &info, Stats &stats, Profiler &profiler) { -#ifdef WITH_MULTI if (!info.multi_devices.empty()) { /* Always create a multi device when info contains multiple devices. * This is done so that the type can still be e.g. DEVICE_CPU to indicate * that it is a homogeneous collection of devices, which simplifies checks. */ return device_multi_create(info, stats, profiler); } -#endif Device *device = NULL; From 8de942b11d41485ab3dbf3ac0834d8eef12b5b4f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 26 Oct 2021 15:38:37 +0200 Subject: [PATCH 1201/1500] Cleanup: compiler warning when building without OpenSubdiv --- intern/cycles/scene/scene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp index 8cde4873eab..ef0ee0c6625 100644 --- a/intern/cycles/scene/scene.cpp +++ b/intern/cycles/scene/scene.cpp @@ -505,8 +505,8 @@ void Scene::update_kernel_features() kernel_features |= KERNEL_FEATURE_SHADOW_CATCHER; } if (geom->is_mesh()) { - Mesh *mesh = static_cast(geom); #ifdef WITH_OPENSUBDIV + Mesh *mesh = static_cast(geom); if (mesh->get_subdivision_type() != Mesh::SUBDIVISION_NONE) { kernel_features |= KERNEL_FEATURE_PATCH_EVALUATION; } From b937b6d06956327578e82a10f4136377758425aa Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 26 Oct 2021 15:39:25 +0200 Subject: [PATCH 1202/1500] Cleanup: silence address sanitizer warning about NULL + 0 --- source/blender/makesrna/intern/rna_attribute.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index f1831bca0fe..dbf20896463 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -323,8 +323,10 @@ static void rna_AttributeGroup_next_domain(ID *id, int(skip)(CollectionPropertyIterator *iter, void *data)) { do { - CustomDataLayer *prev_layers = (CustomDataLayer *)iter->internal.array.endptr - - iter->internal.array.length; + CustomDataLayer *prev_layers = (iter->internal.array.endptr == NULL) ? + NULL : + (CustomDataLayer *)iter->internal.array.endptr - + iter->internal.array.length; CustomData *customdata = BKE_id_attributes_iterator_next_domain(id, prev_layers); if (customdata == NULL) { return; From 567bcb93871cf7b1097db212dafb782caf502f66 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 26 Oct 2021 15:39:58 +0200 Subject: [PATCH 1203/1500] Cleanup: skip unnecessary OIIO image setup when not using tiled render --- intern/cycles/session/tile.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/intern/cycles/session/tile.cpp b/intern/cycles/session/tile.cpp index 816bf4d5fa0..56bc519378f 100644 --- a/intern/cycles/session/tile.cpp +++ b/intern/cycles/session/tile.cpp @@ -367,20 +367,26 @@ void TileManager::update(const BufferParams ¶ms, const Scene *scene) buffer_params_ = params; - /* TODO(sergey): Proper Error handling, so that if configuration has failed we don't attempt to - * write to a partially configured file. */ - configure_image_spec_from_buffer(&write_state_.image_spec, buffer_params_, tile_size_); + if (has_multiple_tiles()) { + /* TODO(sergey): Proper Error handling, so that if configuration has failed we don't attempt to + * write to a partially configured file. */ + configure_image_spec_from_buffer(&write_state_.image_spec, buffer_params_, tile_size_); - const DenoiseParams denoise_params = scene->integrator->get_denoise_params(); - const AdaptiveSampling adaptive_sampling = scene->integrator->get_adaptive_sampling(); + const DenoiseParams denoise_params = scene->integrator->get_denoise_params(); + const AdaptiveSampling adaptive_sampling = scene->integrator->get_adaptive_sampling(); - node_to_image_spec_atttributes( - &write_state_.image_spec, &denoise_params, ATTR_DENOISE_SOCKET_PREFIX); + node_to_image_spec_atttributes( + &write_state_.image_spec, &denoise_params, ATTR_DENOISE_SOCKET_PREFIX); - if (adaptive_sampling.use) { - overscan_ = 4; + if (adaptive_sampling.use) { + overscan_ = 4; + } + else { + overscan_ = 0; + } } else { + write_state_.image_spec = ImageSpec(); overscan_ = 0; } } From f41d4735e99aae2a5247092b91106b1afcf52085 Mon Sep 17 00:00:00 2001 From: Dorian Date: Tue, 26 Oct 2021 15:54:52 +0200 Subject: [PATCH 1204/1500] Nodes: support transparency for link highlight color Node links that are connected to selected nodes are highlighted using the Wire Select theme color. Now it is possible to change the transparency of this color to allow the actual link color to be visible through the highlight (or to turn of the highlight entirely). Differential Revision: https://developer.blender.org/D12973 --- source/blender/editors/space_node/drawnode.cc | 28 +++++++++++++------ source/blender/makesrna/intern/rna_userdef.c | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index dfbb2132f1b..24f5decacdf 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -4329,6 +4329,25 @@ void node_draw_link_bezier(const View2D *v2d, UI_GetThemeColor4fv(th_col2, colors[2]); } + /* Highlight links connected to selected nodes. */ + const bool is_fromnode_selected = link->fromnode && link->fromnode->flag & SELECT; + const bool is_tonode_selected = link->tonode && link->tonode->flag & SELECT; + if (is_fromnode_selected || is_tonode_selected) { + float color_selected[4]; + UI_GetThemeColor4fv(TH_EDGE_SELECT, color_selected); + const float alpha = color_selected[3]; + + /* Interpolate color if highlight color is not fully transparent. */ + if (alpha != 0.0) { + if (is_fromnode_selected) { + interp_v3_v3v3(colors[1], colors[1], color_selected, alpha); + } + if (is_tonode_selected) { + interp_v3_v3v3(colors[2], colors[2], color_selected, alpha); + } + } + } + if (g_batch_link.enabled && !highlighted) { /* Add link to batch. */ nodelink_batch_add_link(snode, @@ -4402,15 +4421,6 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) else if (link->flag & NODE_LINK_MUTED) { th_col1 = th_col2 = TH_REDALERT; } - else { - /* Regular link, highlight if connected to selected node. */ - if (link->fromnode && link->fromnode->flag & SELECT) { - th_col1 = TH_EDGE_SELECT; - } - if (link->tonode && link->tonode->flag & SELECT) { - th_col2 = TH_EDGE_SELECT; - } - } } else { /* Invalid link. */ diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index d6a46cbcc82..2514a604087 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2844,7 +2844,7 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna) prop = RNA_def_property(srna, "wire_select", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_float_sdna(prop, NULL, "edge_select"); - RNA_def_property_array(prop, 3); + RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Wire Select", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); From 4094868f7301ed06e6fd0710d1e9193796eb857d Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 26 Oct 2021 16:05:33 +0200 Subject: [PATCH 1205/1500] Fix T92502: Position Pass broken The kernel was writing it for the first sample only, while the film accessor was thinking it needs to be filtered. --- intern/cycles/scene/pass.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/intern/cycles/scene/pass.cpp b/intern/cycles/scene/pass.cpp index 791101e0940..a885ede50a4 100644 --- a/intern/cycles/scene/pass.cpp +++ b/intern/cycles/scene/pass.cpp @@ -182,6 +182,7 @@ PassInfo Pass::get_info(const PassType type, const bool include_albedo) break; case PASS_POSITION: pass_info.num_components = 3; + pass_info.use_filter = false; break; case PASS_NORMAL: pass_info.num_components = 3; From 366262bef5426fa98e75c96a1562dd16001fba26 Mon Sep 17 00:00:00 2001 From: William Leeson Date: Tue, 26 Oct 2021 08:30:15 +0200 Subject: [PATCH 1206/1500] Distance Scrambling for for Cycles X - Sobol version Cycles:Distance Scrambling for Cycles Sobol Sampler This option implements micro jittering an is based on the INRIA research paper [[ https://hal.inria.fr/hal-01325702/document | on micro jittering ]] and work by Lukas Stockner for implementing the scrambling distance. It works by controlling the correlation between pixels by either using a user supplied value or an adaptive algorithm to limit the maximum deviation of the sample values between pixels. This is a follow up of https://developer.blender.org/D12316 The PMJ version can be found here: https://developer.blender.org/D12511 Reviewed By: leesonw Differential Revision: https://developer.blender.org/D12318 --- intern/cycles/blender/addon/properties.py | 18 ++++++ intern/cycles/blender/addon/ui.py | 7 +++ intern/cycles/blender/sync.cpp | 15 +++++ .../cycles/integrator/path_trace_work_gpu.cpp | 5 +- intern/cycles/integrator/tile.cpp | 58 +++++++++++++++---- intern/cycles/integrator/tile.h | 3 +- .../cycles/integrator/work_tile_scheduler.cpp | 9 ++- .../cycles/integrator/work_tile_scheduler.h | 8 ++- intern/cycles/kernel/sample/pattern.h | 2 +- intern/cycles/kernel/types.h | 2 +- intern/cycles/scene/integrator.cpp | 2 + intern/cycles/scene/integrator.h | 1 + intern/cycles/test/integrator_tile_test.cpp | 19 +++--- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- source/tools | 2 +- 17 files changed, 128 insertions(+), 29 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 0f92238015d..e5853529d1c 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -342,6 +342,24 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): default='PROGRESSIVE_MUTI_JITTER', ) + scrambling_distance: FloatProperty( + name="Scrambling Distance", + default=1.0, + min=0.0, max=1.0, + description="Lower values give faster rendering with GPU rendering and less noise with all devices at the cost of possible artifacts if set too low", + ) + preview_scrambling_distance: BoolProperty( + name="Scrambling Distance viewport", + default=False, + description="Uses the Scrambling Distance value for the viewport. Faster but may flicker", + ) + + adaptive_scrambling_distance: BoolProperty( + name="Adaptive Scrambling Distance", + default=False, + description="Uses a formula to adapt the scrambling distance strength based on the sample count", + ) + use_layer_samples: EnumProperty( name="Layer Samples", description="How to use per view layer sample settings", diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index facf1b08676..47907481b03 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -289,6 +289,13 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): col = layout.column(align=True) col.active = not(cscene.use_adaptive_sampling) col.prop(cscene, "sampling_pattern", text="Pattern") + col = layout.column(align=True) + col.active = cscene.sampling_pattern == 'SOBOL' and not cscene.use_adaptive_sampling + col.prop(cscene, "scrambling_distance", text="Scrambling Distance Strength") + col.prop(cscene, "adaptive_scrambling_distance", text="Adaptive Scrambling Distance") + col = layout.column(align=True) + col.active = ((cscene.scrambling_distance < 1.0) or cscene.adaptive_scrambling_distance) and cscene.sampling_pattern == 'SOBOL' and not cscene.use_adaptive_sampling + col.prop(cscene, "preview_scrambling_distance", text="Viewport Scrambling Distance") layout.separator() diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index 73d3a4436b5..f6f490077a7 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -352,6 +352,21 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background) integrator->set_adaptive_min_samples(get_int(cscene, "adaptive_min_samples")); } + int samples = get_int(cscene, "samples"); + float scrambling_distance = get_float(cscene, "scrambling_distance"); + bool adaptive_scrambling_distance = get_boolean(cscene, "adaptive_scrambling_distance"); + if (adaptive_scrambling_distance) { + scrambling_distance *= 4.0f / sqrtf(samples); + } + + /* only use scrambling distance in the viewport if user wants to and disable with AS */ + bool preview_scrambling_distance = get_boolean(cscene, "preview_scrambling_distance"); + if ((preview && !preview_scrambling_distance) || sampling_pattern != SAMPLING_PATTERN_SOBOL) + scrambling_distance = 1.0f; + + VLOG(1) << "Used Scrambling Distance: " << scrambling_distance; + integrator->set_scrambling_distance(scrambling_distance); + if (get_boolean(cscene, "use_fast_gi")) { if (preview) { integrator->set_ao_bounces(get_int(cscene, "ao_bounces")); diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index b7dc4e5d181..251bec0dc8f 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -258,7 +258,10 @@ void PathTraceWorkGPU::render_samples(RenderStatistics &statistics, * schedules work in halves of available number of paths. */ work_tile_scheduler_.set_max_num_path_states(max_num_paths_ / 8); - work_tile_scheduler_.reset(effective_buffer_params_, start_sample, samples_num); + work_tile_scheduler_.reset(effective_buffer_params_, + start_sample, + samples_num, + device_scene_->data.integrator.scrambling_distance); enqueue_reset(); diff --git a/intern/cycles/integrator/tile.cpp b/intern/cycles/integrator/tile.cpp index 7ea73451d80..b49e1b27b83 100644 --- a/intern/cycles/integrator/tile.cpp +++ b/intern/cycles/integrator/tile.cpp @@ -48,7 +48,8 @@ ccl_device_inline uint round_up_to_power_of_two(uint x) TileSize tile_calculate_best_size(const int2 &image_size, const int num_samples, - const int max_num_path_states) + const int max_num_path_states, + const float scrambling_distance) { if (max_num_path_states == 1) { /* Simple case: avoid any calculation, which could cause rounding issues. */ @@ -71,17 +72,54 @@ TileSize tile_calculate_best_size(const int2 &image_size, * - Keep values a power of two, for more integer fit into the maximum number of paths. */ TileSize tile_size; - - /* Calculate tile size as if it is the most possible one to fit an entire range of samples. - * The idea here is to keep tiles as small as possible, and keep device occupied by scheduling - * multiple tiles with the same coordinates rendering different samples. */ const int num_path_states_per_sample = max_num_path_states / num_samples; - if (num_path_states_per_sample != 0) { - tile_size.width = round_down_to_power_of_two(lround(sqrt(num_path_states_per_sample))); - tile_size.height = tile_size.width; + if (scrambling_distance < 0.9f) { + /* Prefer large tiles for scrambling distance. */ + if (image_size.x * image_size.y <= num_path_states_per_sample) { + tile_size.width = image_size.x; + tile_size.height = image_size.y; + } + else { + /* Pick the option with the biggest tile size */ + int heightOption = num_path_states_per_sample / image_size.x; + int widthOption = num_path_states_per_sample / image_size.y; + // Check if these options are possible + if ((heightOption > 0) || (widthOption > 0)) { + int area1 = image_size.x * heightOption; + int area2 = widthOption * image_size.y; + /* The option with the biggest pixel area */ + if (area1 >= area2) { + tile_size.width = image_size.x; + tile_size.height = heightOption; + } + else { + tile_size.width = widthOption; + tile_size.height = image_size.y; + } + } + else { // Large tiles are not an option so use square tiles + if (num_path_states_per_sample != 0) { + tile_size.width = round_down_to_power_of_two(lround(sqrt(num_path_states_per_sample))); + tile_size.height = tile_size.width; + } + else { + tile_size.width = tile_size.height = 1; + } + } + } } else { - tile_size.width = tile_size.height = 1; + /* Calculate tile size as if it is the most possible one to fit an entire range of samples. + * The idea here is to keep tiles as small as possible, and keep device occupied by scheduling + * multiple tiles with the same coordinates rendering different samples. */ + + if (num_path_states_per_sample != 0) { + tile_size.width = round_down_to_power_of_two(lround(sqrt(num_path_states_per_sample))); + tile_size.height = tile_size.width; + } + else { + tile_size.width = tile_size.height = 1; + } } if (num_samples == 1) { @@ -93,7 +131,7 @@ TileSize tile_calculate_best_size(const int2 &image_size, tile_size.num_samples = min(round_up_to_power_of_two(lround(sqrt(num_samples / 2))), static_cast(num_samples)); - const int tile_area = tile_size.width / tile_size.height; + const int tile_area = tile_size.width * tile_size.height; tile_size.num_samples = min(tile_size.num_samples, max_num_path_states / tile_area); } diff --git a/intern/cycles/integrator/tile.h b/intern/cycles/integrator/tile.h index 879c68b875c..61f7d736115 100644 --- a/intern/cycles/integrator/tile.h +++ b/intern/cycles/integrator/tile.h @@ -51,6 +51,7 @@ std::ostream &operator<<(std::ostream &os, const TileSize &tile_size); * possible, and have as many threads active for every tile as possible. */ TileSize tile_calculate_best_size(const int2 &image_size, const int num_samples, - const int max_num_path_states); + const int max_num_path_states, + const float scrambling_distance); CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/work_tile_scheduler.cpp b/intern/cycles/integrator/work_tile_scheduler.cpp index c874dffde91..2d1ac07db7f 100644 --- a/intern/cycles/integrator/work_tile_scheduler.cpp +++ b/intern/cycles/integrator/work_tile_scheduler.cpp @@ -33,13 +33,17 @@ void WorkTileScheduler::set_max_num_path_states(int max_num_path_states) max_num_path_states_ = max_num_path_states; } -void WorkTileScheduler::reset(const BufferParams &buffer_params, int sample_start, int samples_num) +void WorkTileScheduler::reset(const BufferParams &buffer_params, + int sample_start, + int samples_num, + float scrambling_distance) { /* Image buffer parameters. */ image_full_offset_px_.x = buffer_params.full_x; image_full_offset_px_.y = buffer_params.full_y; image_size_px_ = make_int2(buffer_params.width, buffer_params.height); + scrambling_distance_ = scrambling_distance; offset_ = buffer_params.offset; stride_ = buffer_params.stride; @@ -54,7 +58,8 @@ void WorkTileScheduler::reset(const BufferParams &buffer_params, int sample_star void WorkTileScheduler::reset_scheduler_state() { - tile_size_ = tile_calculate_best_size(image_size_px_, samples_num_, max_num_path_states_); + tile_size_ = tile_calculate_best_size( + image_size_px_, samples_num_, max_num_path_states_, scrambling_distance_); VLOG(3) << "Will schedule tiles of size " << tile_size_; diff --git a/intern/cycles/integrator/work_tile_scheduler.h b/intern/cycles/integrator/work_tile_scheduler.h index 155bba5cb68..d9fa7e84431 100644 --- a/intern/cycles/integrator/work_tile_scheduler.h +++ b/intern/cycles/integrator/work_tile_scheduler.h @@ -38,7 +38,10 @@ class WorkTileScheduler { void set_max_num_path_states(int max_num_path_states); /* Scheduling will happen for pixels within a big tile denotes by its parameters. */ - void reset(const BufferParams &buffer_params, int sample_start, int samples_num); + void reset(const BufferParams &buffer_params, + int sample_start, + int samples_num, + float scrambling_distance); /* Get work for a device. * Returns true if there is still work to be done and initialize the work tile to all @@ -68,6 +71,9 @@ class WorkTileScheduler { * Will be passed over to the KernelWorkTile. */ int offset_, stride_; + /* Scrambling Distance requires adapted tile size */ + float scrambling_distance_; + /* Start sample of index and number of samples which are to be rendered. * The scheduler will cover samples range of [start, start + num] over the entire image * (splitting into a smaller work tiles). */ diff --git a/intern/cycles/kernel/sample/pattern.h b/intern/cycles/kernel/sample/pattern.h index 191b24a5f2a..0c27992c7f6 100644 --- a/intern/cycles/kernel/sample/pattern.h +++ b/intern/cycles/kernel/sample/pattern.h @@ -79,7 +79,7 @@ ccl_device_forceinline float path_rng_1D(KernelGlobals kg, * See T38710, T50116. */ uint tmp_rng = cmj_hash_simple(dimension, rng_hash); - shift = tmp_rng * (1.0f / (float)0xFFFFFFFF); + shift = tmp_rng * (kernel_data.integrator.scrambling_distance / (float)0xFFFFFFFF); return r + shift - floorf(r + shift); #endif diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index 4109dd6a486..2827139d511 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -1184,9 +1184,9 @@ typedef struct KernelIntegrator { float volume_step_rate; int has_shadow_catcher; + float scrambling_distance; /* padding */ - int pad1; } KernelIntegrator; static_assert_align(KernelIntegrator, 16); diff --git a/intern/cycles/scene/integrator.cpp b/intern/cycles/scene/integrator.cpp index 3e795b30e7f..e9ff868c3fc 100644 --- a/intern/cycles/scene/integrator.cpp +++ b/intern/cycles/scene/integrator.cpp @@ -81,6 +81,7 @@ NODE_DEFINE(Integrator) sampling_pattern_enum.insert("sobol", SAMPLING_PATTERN_SOBOL); sampling_pattern_enum.insert("pmj", SAMPLING_PATTERN_PMJ); SOCKET_ENUM(sampling_pattern, "Sampling Pattern", sampling_pattern_enum, SAMPLING_PATTERN_SOBOL); + SOCKET_FLOAT(scrambling_distance, "Scrambling Distance", 1.0f); static NodeEnum denoiser_type_enum; denoiser_type_enum.insert("optix", DENOISER_OPTIX); @@ -192,6 +193,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene sample_clamp_indirect * 3.0f; kintegrator->sampling_pattern = new_sampling_pattern; + kintegrator->scrambling_distance = scrambling_distance; if (light_sampling_threshold > 0.0f) { kintegrator->light_inv_rr_threshold = 1.0f / light_sampling_threshold; diff --git a/intern/cycles/scene/integrator.h b/intern/cycles/scene/integrator.h index c380203f4f3..75764bcdedc 100644 --- a/intern/cycles/scene/integrator.h +++ b/intern/cycles/scene/integrator.h @@ -76,6 +76,7 @@ class Integrator : public Node { NODE_SOCKET_API(float, adaptive_threshold) NODE_SOCKET_API(SamplingPattern, sampling_pattern) + NODE_SOCKET_API(float, scrambling_distance) NODE_SOCKET_API(bool, use_denoise); NODE_SOCKET_API(DenoiserType, denoiser_type); diff --git a/intern/cycles/test/integrator_tile_test.cpp b/intern/cycles/test/integrator_tile_test.cpp index e5ffa7c153d..8bb0856d6a9 100644 --- a/intern/cycles/test/integrator_tile_test.cpp +++ b/intern/cycles/test/integrator_tile_test.cpp @@ -24,23 +24,26 @@ CCL_NAMESPACE_BEGIN TEST(tile_calculate_best_size, Basic) { /* Make sure CPU-like case is handled properly. */ - EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 1, 1), TileSize(1, 1, 1)); - EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 100, 1), TileSize(1, 1, 1)); + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 1, 1, 1.0f), TileSize(1, 1, 1)); + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 100, 1, 1.0f), TileSize(1, 1, 1)); /* Enough path states to fit an entire image with all samples. */ - EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 1, 1920 * 1080), + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 1, 1920 * 1080, 1.0f), TileSize(1920, 1080, 1)); - EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 100, 1920 * 1080 * 100), + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 100, 1920 * 1080 * 100, 1.0f), TileSize(1920, 1080, 100)); } TEST(tile_calculate_best_size, Extreme) { - EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 262144, 131072), TileSize(1, 1, 512)); - EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 1048576, 131072), TileSize(1, 1, 1024)); - EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 10485760, 131072), TileSize(1, 1, 4096)); + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 262144, 131072, 1.0f), + TileSize(1, 1, 512)); + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 1048576, 131072, 1.0f), + TileSize(1, 1, 1024)); + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 10485760, 131072, 1.0f), + TileSize(1, 1, 4096)); - EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 8192 * 8192 * 2, 1024), + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 8192 * 8192 * 2, 1024, 1.0f), TileSize(1, 1, 1024)); } diff --git a/release/datafiles/locale b/release/datafiles/locale index 80d9e7ee122..8ee2942570f 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 80d9e7ee122c626cbbcd1da554683bce79f8d3df +Subproject commit 8ee2942570f08d10484bb2328d0d1b0aaaa0367c diff --git a/release/scripts/addons b/release/scripts/addons index 27fe7f3a4f9..f2a08d80ccd 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 27fe7f3a4f964b53af436c4da4ddea337eff0c7e +Subproject commit f2a08d80ccd3c13af304525778df3905f95bd44d diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 42da56aa737..16467648282 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 42da56aa73726710107031787af5eea186797984 +Subproject commit 16467648282500cc229c271f62201ef897f2c2c3 diff --git a/source/tools b/source/tools index 7c5acb95df9..2e8c8792488 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 7c5acb95df918503d11cfc43172ce13901019289 +Subproject commit 2e8c879248822c8e500ed49d79acc605e5aa75b9 From dde11219c630c50c41bd84cfd173293e9e3ddf85 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 26 Oct 2021 15:51:39 +0200 Subject: [PATCH 1207/1500] Cleanup: remove files that should not have been added in file renames --- intern/cycles/kernel/sample/sample_lcg.h | 51 ------------------- intern/cycles/kernel/sample/sample_mis.h | 64 ------------------------ 2 files changed, 115 deletions(-) delete mode 100644 intern/cycles/kernel/sample/sample_lcg.h delete mode 100644 intern/cycles/kernel/sample/sample_mis.h diff --git a/intern/cycles/kernel/sample/sample_lcg.h b/intern/cycles/kernel/sample/sample_lcg.h deleted file mode 100644 index 92cfff639b4..00000000000 --- a/intern/cycles/kernel/sample/sample_lcg.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -CCL_NAMESPACE_BEGIN - -/* Linear Congruential Generator */ - -ccl_device uint lcg_step_uint(uint *rng) -{ - /* implicit mod 2^32 */ - *rng = (1103515245 * (*rng) + 12345); - return *rng; -} - -ccl_device float lcg_step_float(uint *rng) -{ - /* implicit mod 2^32 */ - *rng = (1103515245 * (*rng) + 12345); - return (float)*rng * (1.0f / (float)0xFFFFFFFF); -} - -ccl_device uint lcg_init(uint seed) -{ - uint rng = seed; - lcg_step_uint(&rng); - return rng; -} - -ccl_device_inline uint lcg_state_init(const uint rng_hash, - const uint rng_offset, - const uint sample, - const uint scramble) -{ - return lcg_init(rng_hash + rng_offset + sample * scramble); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/sample/sample_mis.h b/intern/cycles/kernel/sample/sample_mis.h deleted file mode 100644 index 0878b3aac36..00000000000 --- a/intern/cycles/kernel/sample/sample_mis.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Parts adapted from Open Shading Language with this license: - * - * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. - * All Rights Reserved. - * - * Modifications Copyright 2011, Blender Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Sony Pictures Imageworks nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -CCL_NAMESPACE_BEGIN - -/* Multiple importance sampling utilities. */ - -ccl_device float balance_heuristic(float a, float b) -{ - return (a) / (a + b); -} - -ccl_device float balance_heuristic_3(float a, float b, float c) -{ - return (a) / (a + b + c); -} - -ccl_device float power_heuristic(float a, float b) -{ - return (a * a) / (a * a + b * b); -} - -ccl_device float power_heuristic_3(float a, float b, float c) -{ - return (a * a) / (a * a + b * b + c * c); -} - -ccl_device float max_heuristic(float a, float b) -{ - return (a > b) ? 1.0f : 0.0f; -} - -CCL_NAMESPACE_END From d89c4999a7cbd427dc0929207fb9e86ea345dede Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 26 Oct 2021 16:13:39 +0200 Subject: [PATCH 1208/1500] Fix Cycles runtime GPU kernel compilation after recent refactor --- intern/cycles/kernel/CMakeLists.txt | 278 +++++++++++++++------------- 1 file changed, 151 insertions(+), 127 deletions(-) diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 6d57eff3d22..29ff69df864 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -22,7 +22,7 @@ set(INC_SYS ) -set(SRC_DEVICE_CPU +set(SRC_KERNEL_DEVICE_CPU device/cpu/kernel.cpp device/cpu/kernel_sse2.cpp device/cpu/kernel_sse3.cpp @@ -31,60 +31,20 @@ set(SRC_DEVICE_CPU device/cpu/kernel_avx2.cpp ) -set(SRC_DEVICE_CUDA +set(SRC_KERNEL_DEVICE_CUDA device/cuda/kernel.cu ) -set(SRC_DEVICE_HIP +set(SRC_KERNEL_DEVICE_HIP device/hip/kernel.cpp ) -set(SRC_DEVICE_OPTIX +set(SRC_KERNEL_DEVICE_OPTIX device/optix/kernel.cu device/optix/kernel_shader_raytrace.cu ) -set(SRC_HEADERS - bake/bake.h - bvh/bvh.h - bvh/nodes.h - bvh/shadow_all.h - bvh/local.h - bvh/traversal.h - bvh/types.h - bvh/util.h - bvh/volume.h - bvh/volume_all.h - bvh/embree.h - camera/camera.h - camera/projection.h - film/accumulate.h - film/adaptive_sampling.h - film/id_passes.h - film/passes.h - film/read.h - film/write_passes.h - integrator/path_state.h - integrator/shader_eval.h - integrator/shadow_catcher.h - textures.h - types.h - light/light.h - light/background.h - light/common.h - light/sample.h - sample/jitter.h - sample/lcg.h - sample/mapping.h - sample/mis.h - sample/pattern.h - util/color.h - util/differential.h - util/lookup_table.h - util/profiling.h -) - -set(SRC_DEVICE_CPU_HEADERS +set(SRC_KERNEL_DEVICE_CPU_HEADERS device/cpu/compat.h device/cpu/image.h device/cpu/globals.h @@ -92,7 +52,7 @@ set(SRC_DEVICE_CPU_HEADERS device/cpu/kernel_arch.h device/cpu/kernel_arch_impl.h ) -set(SRC_DEVICE_GPU_HEADERS +set(SRC_KERNEL_DEVICE_GPU_HEADERS device/gpu/image.h device/gpu/kernel.h device/gpu/parallel_active_index.h @@ -102,24 +62,24 @@ set(SRC_DEVICE_GPU_HEADERS device/gpu/work_stealing.h ) -set(SRC_DEVICE_CUDA_HEADERS +set(SRC_KERNEL_DEVICE_CUDA_HEADERS device/cuda/compat.h device/cuda/config.h device/cuda/globals.h ) -set(SRC_DEVICE_HIP_HEADERS +set(SRC_KERNEL_DEVICE_HIP_HEADERS device/hip/compat.h device/hip/config.h device/hip/globals.h ) -set(SRC_DEVICE_OPTIX_HEADERS +set(SRC_KERNEL_DEVICE_OPTIX_HEADERS device/optix/compat.h device/optix/globals.h ) -set(SRC_CLOSURE_HEADERS +set(SRC_KERNEL_CLOSURE_HEADERS closure/alloc.h closure/bsdf.h closure/bsdf_ashikhmin_velvet.h @@ -145,7 +105,7 @@ set(SRC_CLOSURE_HEADERS closure/bsdf_hair_principled.h ) -set(SRC_SVM_HEADERS +set(SRC_KERNEL_SVM_HEADERS svm/svm.h svm/ao.h svm/aov.h @@ -202,7 +162,7 @@ set(SRC_SVM_HEADERS svm/vertex_color.h ) -set(SRC_GEOM_HEADERS +set(SRC_KERNEL_GEOM_HEADERS geom/geom.h geom/attribute.h geom/curve.h @@ -221,7 +181,38 @@ set(SRC_GEOM_HEADERS geom/volume.h ) -set(SRC_INTEGRATOR_HEADERS +set(SRC_KERNEL_BAKE_HEADERS + bake/bake.h +) + +set(SRC_KERNEL_BVH_HEADERS + bvh/bvh.h + bvh/nodes.h + bvh/shadow_all.h + bvh/local.h + bvh/traversal.h + bvh/types.h + bvh/util.h + bvh/volume.h + bvh/volume_all.h + bvh/embree.h +) + +set(SRC_KERNEL_CAMERA_HEADERS + camera/camera.h + camera/projection.h +) + +set(SRC_KERNEL_FILM_HEADERS + film/accumulate.h + film/adaptive_sampling.h + film/id_passes.h + film/passes.h + film/read.h + film/write_passes.h +) + +set(SRC_KERNEL_INTEGRATOR_HEADERS integrator/init_from_bake.h integrator/init_from_camera.h integrator/intersect_closest.h @@ -229,22 +220,67 @@ set(SRC_INTEGRATOR_HEADERS integrator/intersect_subsurface.h integrator/intersect_volume_stack.h integrator/megakernel.h + integrator/path_state.h integrator/shade_background.h integrator/shade_light.h + integrator/shader_eval.h integrator/shade_shadow.h integrator/shade_surface.h integrator/shade_volume.h + integrator/shadow_catcher.h integrator/shadow_state_template.h - integrator/state.h integrator/state_flow.h + integrator/state.h integrator/state_template.h integrator/state_util.h - integrator/subsurface.h integrator/subsurface_disk.h + integrator/subsurface.h integrator/subsurface_random_walk.h integrator/volume_stack.h ) +set(SRC_KERNEL_LIGHT_HEADERS + light/light.h + light/background.h + light/common.h + light/sample.h +) + +set(SRC_KERNEL_SAMPLE_HEADERS + sample/jitter.h + sample/lcg.h + sample/mapping.h + sample/mis.h + sample/pattern.h +) + +set(SRC_KERNEL_UTIL_HEADERS + util/color.h + util/differential.h + util/lookup_table.h + util/profiling.h +) + +set(SRC_KERNEL_TYPES_HEADERS + textures.h + types.h +) + +set(SRC_KERNEL_HEADERS + ${SRC_KERNEL_BAKE_HEADERS} + ${SRC_KERNEL_BVH_HEADERS} + ${SRC_KERNEL_CAMERA_HEADERS} + ${SRC_KERNEL_CLOSURE_HEADERS} + ${SRC_KERNEL_FILM_HEADERS} + ${SRC_KERNEL_GEOM_HEADERS} + ${SRC_KERNEL_INTEGRATOR_HEADERS} + ${SRC_KERNEL_LIGHT_HEADERS} + ${SRC_KERNEL_SAMPLE_HEADERS} + ${SRC_KERNEL_SVM_HEADERS} + ${SRC_KERNEL_TYPES_HEADERS} + ${SRC_KERNEL_UTIL_HEADERS} +) + set(SRC_UTIL_HEADERS ../util/atomic.h ../util/color.h @@ -329,14 +365,9 @@ if(WITH_CYCLES_CUDA_BINARIES) # build for each arch set(cuda_sources device/cuda/kernel.cu - ${SRC_HEADERS} - ${SRC_DEVICE_GPU_HEADERS} - ${SRC_DEVICE_CUDA_HEADERS} - ${SRC_BVH_HEADERS} - ${SRC_SVM_HEADERS} - ${SRC_GEOM_HEADERS} - ${SRC_INTEGRATOR_HEADERS} - ${SRC_CLOSURE_HEADERS} + ${SRC_KERNEL_HEADERS} + ${SRC_KERNEL_DEVICE_GPU_HEADERS} + ${SRC_KERNEL_DEVICE_CUDA_HEADERS} ${SRC_UTIL_HEADERS} ) set(cuda_cubins) @@ -487,13 +518,9 @@ endif() if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP) # build for each arch set(hip_sources device/hip/kernel.cpp - ${SRC_HEADERS} - ${SRC_DEVICE_HIP_HEADERS} - ${SRC_BVH_HEADERS} - ${SRC_SVM_HEADERS} - ${SRC_GEOM_HEADERS} - ${SRC_INTEGRATOR_HEADERS} - ${SRC_CLOSURE_HEADERS} + ${SRC_KERNEL_HEADERS} + ${SRC_KERNEL_DEVICE_GPU_HEADERS} + ${SRC_KERNEL_DEVICE_HIP_HEADERS} ${SRC_UTIL_HEADERS} ) set(hip_fatbins) @@ -595,15 +622,10 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) OUTPUT ${output} DEPENDS ${input} - ${SRC_HEADERS} - ${SRC_DEVICE_GPU_HEADERS} - ${SRC_DEVICE_CUDA_HEADERS} - ${SRC_DEVICE_OPTIX_HEADERS} - ${SRC_BVH_HEADERS} - ${SRC_SVM_HEADERS} - ${SRC_GEOM_HEADERS} - ${SRC_INTEGRATOR_HEADERS} - ${SRC_CLOSURE_HEADERS} + ${SRC_KERNEL_HEADERS} + ${SRC_KERNEL_DEVICE_GPU_HEADERS} + ${SRC_KERNEL_DEVICE_CUDA_HEADERS} + ${SRC_KERNEL_DEVICE_OPTIX_HEADERS} ${SRC_UTIL_HEADERS} COMMAND ${CUBIN_CC_ENV} "$" @@ -620,15 +642,10 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) ${output} DEPENDS ${input} - ${SRC_HEADERS} - ${SRC_DEVICE_GPU_HEADERS} - ${SRC_DEVICE_CUDA_HEADERS} - ${SRC_DEVICE_OPTIX_HEADERS} - ${SRC_BVH_HEADERS} - ${SRC_SVM_HEADERS} - ${SRC_GEOM_HEADERS} - ${SRC_INTEGRATOR_HEADERS} - ${SRC_CLOSURE_HEADERS} + ${SRC_KERNEL_HEADERS} + ${SRC_KERNEL_DEVICE_GPU_HEADERS} + ${SRC_KERNEL_DEVICE_CUDA_HEADERS} + ${SRC_KERNEL_DEVICE_OPTIX_HEADERS} ${SRC_UTIL_HEADERS} COMMAND ${CUDA_NVCC_EXECUTABLE} @@ -702,34 +719,35 @@ if(CXX_HAS_AVX2) endif() cycles_add_library(cycles_kernel "${LIB}" - ${SRC_DEVICE_CPU} - ${SRC_DEVICE_CUDA} - ${SRC_DEVICE_HIP} - ${SRC_DEVICE_OPTIX} - ${SRC_HEADERS} - ${SRC_DEVICE_CPU_HEADERS} - ${SRC_DEVICE_GPU_HEADERS} - ${SRC_DEVICE_CUDA_HEADERS} - ${SRC_DEVICE_HIP_HEADERS} - ${SRC_DEVICE_OPTIX_HEADERS} - ${SRC_BVH_HEADERS} - ${SRC_CLOSURE_HEADERS} - ${SRC_SVM_HEADERS} - ${SRC_GEOM_HEADERS} - ${SRC_INTEGRATOR_HEADERS} + ${SRC_KERNEL_DEVICE_CPU} + ${SRC_KERNEL_DEVICE_CUDA} + ${SRC_KERNEL_DEVICE_HIP} + ${SRC_KERNEL_DEVICE_OPTIX} + ${SRC_KERNEL_HEADERS} + ${SRC_KERNEL_DEVICE_CPU_HEADERS} + ${SRC_KERNEL_DEVICE_GPU_HEADERS} + ${SRC_KERNEL_DEVICE_CUDA_HEADERS} + ${SRC_KERNEL_DEVICE_HIP_HEADERS} + ${SRC_KERNEL_DEVICE_OPTIX_HEADERS} ) -source_group("bvh" FILES ${SRC_BVH_HEADERS}) -source_group("closure" FILES ${SRC_CLOSURE_HEADERS}) -source_group("geom" FILES ${SRC_GEOM_HEADERS}) -source_group("integrator" FILES ${SRC_INTEGRATOR_HEADERS}) -source_group("kernel" FILES ${SRC_HEADERS}) -source_group("device\\cpu" FILES ${SRC_DEVICE_CPU} ${SRC_DEVICE_CPU_HEADERS}) -source_group("device\\hip" FILES ${SRC_DEVICE_HIP} ${SRC_DEVICE_HIP_HEADERS}) -source_group("device\\gpu" FILES ${SRC_DEVICE_GPU_HEADERS}) -source_group("device\\cuda" FILES ${SRC_DEVICE_CUDA} ${SRC_DEVICE_CUDA_HEADERS}) -source_group("device\\optix" FILES ${SRC_DEVICE_OPTIX} ${SRC_DEVICE_OPTIX_HEADERS}) -source_group("svm" FILES ${SRC_SVM_HEADERS}) +source_group("bake" FILES ${SRC_KERNEL_BAKE_HEADERS}) +source_group("bvh" FILES ${SRC_KERNEL_BVH_HEADERS}) +source_group("camera" FILES ${SRC_KERNEL_CAMERA_HEADERS}) +source_group("closure" FILES ${SRC_KERNEL_CLOSURE_HEADERS}) +source_group("device\\cpu" FILES ${SRC_KERNEL_DEVICE_CPU} ${SRC_KERNEL_DEVICE_CPU_HEADERS}) +source_group("device\\cuda" FILES ${SRC_KERNEL_DEVICE_CUDA} ${SRC_KERNEL_DEVICE_CUDA_HEADERS}) +source_group("device\\gpu" FILES ${SRC_KERNEL_DEVICE_GPU_HEADERS}) +source_group("device\\hip" FILES ${SRC_KERNEL_DEVICE_HIP} ${SRC_KERNEL_DEVICE_HIP_HEADERS}) +source_group("device\\optix" FILES ${SRC_KERNEL_DEVICE_OPTIX} ${SRC_KERNEL_DEVICE_OPTIX_HEADERS}) +source_group("film" FILES ${SRC_KERNEL_FILM_HEADERS}) +source_group("geom" FILES ${SRC_KERNEL_GEOM_HEADERS}) +source_group("integrator" FILES ${SRC_KERNEL_INTEGRATOR_HEADERS}) +source_group("kernel" FILES ${SRC_KERNEL_TYPES_HEADERS}) +source_group("light" FILES ${SRC_KERNEL_LIGHT_HEADERS}) +source_group("sample" FILES ${SRC_KERNEL_SAMPLE_HEADERS}) +source_group("svm" FILES ${SRC_KERNEL_SVM_HEADERS}) +source_group("util" FILES ${SRC_KERNEL_UTIL_HEADERS}) if(WITH_CYCLES_CUDA) add_dependencies(cycles_kernel cycles_kernel_cuda) @@ -743,19 +761,25 @@ endif() # Install kernel source for runtime compilation -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_CUDA}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_HIP}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_OPTIX}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_GPU_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/gpu) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_CUDA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_HIP_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_OPTIX_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_BVH_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bvh) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_CLOSURE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/closure) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_SVM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/svm) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_GEOM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/geom) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_INTEGRATOR_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/integrator) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_BAKE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bake) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_BVH_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bvh) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_CAMERA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/camera) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_CLOSURE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/closure) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_CUDA}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_CUDA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_GPU_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/gpu) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_HIP}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_HIP_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_OPTIX}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_OPTIX_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_FILM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/film) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_GEOM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/geom) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_INTEGRATOR_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/integrator) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_LIGHT_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/light) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_SAMPLE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/sample) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_SVM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/svm) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_TYPES_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_UTIL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/util) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_UTIL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/util) if(WITH_NANOVDB) From a90cb41cb985183d884c1e688d27da8e746cba99 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 26 Oct 2021 16:35:26 +0200 Subject: [PATCH 1209/1500] Cleanup: clang-format --- source/blender/blenkernel/intern/collection.c | 3 ++- source/blender/blenkernel/intern/screen.c | 6 +++--- source/blender/blenlib/intern/serialize.cc | 1 - source/blender/blenlib/tests/BLI_serialize_test.cc | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 17122200295..2dca5dcb75d 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -161,7 +161,8 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, cob->ob, IDWALK_CB_USER); } LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); } LISTBASE_FOREACH (CollectionParent *, parent, &collection->parents) { /* XXX This is very weak. The whole idea of keeping pointers to private IDs is very bad diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index df826dff602..3e1a2a9bbe9 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -201,9 +201,9 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area if (path == snode->treepath.first) { /* first nodetree in path is same as snode->nodetree */ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, - path->nodetree, - is_private_nodetree ? IDWALK_CB_EMBEDDED : - IDWALK_CB_USER_ONE); + path->nodetree, + is_private_nodetree ? IDWALK_CB_EMBEDDED : + IDWALK_CB_USER_ONE); } else { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, path->nodetree, IDWALK_CB_USER_ONE); diff --git a/source/blender/blenlib/intern/serialize.cc b/source/blender/blenlib/intern/serialize.cc index 1e023a9f782..52aff140e3e 100644 --- a/source/blender/blenlib/intern/serialize.cc +++ b/source/blender/blenlib/intern/serialize.cc @@ -214,4 +214,3 @@ std::unique_ptr JsonFormatter::deserialize(std::istream &is) } } // namespace blender::io::serialize - diff --git a/source/blender/blenlib/tests/BLI_serialize_test.cc b/source/blender/blenlib/tests/BLI_serialize_test.cc index 8e4ce66fa7c..6c55a85ca1e 100644 --- a/source/blender/blenlib/tests/BLI_serialize_test.cc +++ b/source/blender/blenlib/tests/BLI_serialize_test.cc @@ -205,4 +205,3 @@ TEST(serialize, json_roundtrip_ordering) } } // namespace blender::io::serialize::json::testing - From 52ccb445011dddd73dde5ee9d7ba29f678e0d076 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Tue, 26 Oct 2021 15:10:47 +0100 Subject: [PATCH 1210/1500] Geometry Nodes: Add Brick Texture node Port brick shader node to GN Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12783 --- release/scripts/startup/nodeitems_builtins.py | 1 + .../shader/nodes/node_shader_tex_brick.cc | 174 +++++++++++++++++- 2 files changed, 173 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index b1a3d8a762d..546f8c9e2ab 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -727,6 +727,7 @@ geometry_node_categories = [ NodeItem("FunctionNodeReplaceString"), ]), GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ + NodeItem("ShaderNodeTexBrick"), NodeItem("ShaderNodeTexChecker"), NodeItem("ShaderNodeTexGradient"), NodeItem("ShaderNodeTexMagic"), diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc index e90dae60189..ab5199c5fac 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc @@ -19,6 +19,9 @@ #include "../node_shader_util.h" +#include "BLI_float2.hh" +#include "BLI_float4.hh" + namespace blender::nodes { static void sh_node_tex_brick_declare(NodeDeclarationBuilder &b) @@ -98,18 +101,185 @@ static int node_shader_gpu_tex_brick(GPUMaterial *mat, GPU_constant(&squash_freq)); } -/* node type definition */ +namespace blender::nodes { + +class BrickFunction : public fn::MultiFunction { + private: + const float offset_; + const int offset_freq_; + const float squash_; + const int squash_freq_; + + public: + BrickFunction(const float offset, + const int offset_freq, + const float squash, + const int squash_freq) + : offset_(offset), offset_freq_(offset_freq), squash_(squash), squash_freq_(squash_freq) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"BrickTexture"}; + signature.single_input("Vector"); + signature.single_input("Color1"); + signature.single_input("Color2"); + signature.single_input("Mortar"); + signature.single_input("Scale"); + signature.single_input("Mortar Size"); + signature.single_input("Mortar Smooth"); + signature.single_input("Bias"); + signature.single_input("Brick Width"); + signature.single_input("Row Height"); + signature.single_output("Color"); + signature.single_output("Fac"); + return signature.build(); + } + + /* Fast integer noise. */ + static float brick_noise(uint n) + { + n = (n + 1013) & 0x7fffffff; + n = (n >> 13) ^ n; + const uint nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; + return 0.5f * ((float)nn / 1073741824.0f); + } + + static float smoothstepf(const float f) + { + const float ff = f * f; + return (3.0f * ff - 2.0f * ff * f); + } + + static float2 brick(float3 p, + float mortar_size, + float mortar_smooth, + float bias, + float brick_width, + float row_height, + float offset_amount, + int offset_frequency, + float squash_amount, + int squash_frequency) + { + float offset = 0.0f; + + const int rownum = (int)floorf(p.y / row_height); + + if (offset_frequency && squash_frequency) { + brick_width *= (rownum % squash_frequency) ? 1.0f : squash_amount; + offset = (rownum % offset_frequency) ? 0.0f : (brick_width * offset_amount); + } + + const int bricknum = (int)floorf((p.x + offset) / brick_width); + + const float x = (p.x + offset) - brick_width * bricknum; + const float y = p.y - row_height * rownum; + + const float tint = clamp_f( + brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias, 0.0f, 1.0f); + float min_dist = std::min(std::min(x, y), std::min(brick_width - x, row_height - y)); + + float mortar; + if (min_dist >= mortar_size) { + mortar = 0.0f; + } + else if (mortar_smooth == 0.0f) { + mortar = 1.0f; + } + else { + min_dist = 1.0f - min_dist / mortar_size; + mortar = (min_dist < mortar_smooth) ? smoothstepf(min_dist / mortar_smooth) : 1.0f; + } + + return float2(tint, mortar); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &color1_values = params.readonly_single_input( + 1, "Color1"); + const VArray &color2_values = params.readonly_single_input( + 2, "Color2"); + const VArray &mortar_values = params.readonly_single_input( + 3, "Mortar"); + const VArray &scale = params.readonly_single_input(4, "Scale"); + const VArray &mortar_size = params.readonly_single_input(5, "Mortar Size"); + const VArray &mortar_smooth = params.readonly_single_input(6, "Mortar Smooth"); + const VArray &bias = params.readonly_single_input(7, "Bias"); + const VArray &brick_width = params.readonly_single_input(8, "Brick Width"); + const VArray &row_height = params.readonly_single_input(9, "Row Height"); + + MutableSpan r_color = + params.uninitialized_single_output_if_required(10, "Color"); + MutableSpan r_fac = params.uninitialized_single_output_if_required(11, "Fac"); + + const bool store_fac = !r_fac.is_empty(); + const bool store_color = !r_color.is_empty(); + + for (int64_t i : mask) { + const float2 f2 = brick(vector[i] * scale[i], + mortar_size[i], + mortar_smooth[i], + bias[i], + brick_width[i], + row_height[i], + offset_, + offset_freq_, + squash_, + squash_freq_); + + float4 color_data, color1, color2, mortar; + copy_v4_v4(color_data, color1_values[i]); + copy_v4_v4(color1, color1_values[i]); + copy_v4_v4(color2, color2_values[i]); + copy_v4_v4(mortar, mortar_values[i]); + const float tint = f2.x; + const float f = f2.y; + + if (f != 1.0f) { + const float facm = 1.0f - tint; + color_data = color1 * facm + color2 * tint; + } + + if (store_color) { + color_data = color_data * (1.0f - f) + mortar * f; + copy_v4_v4(r_color[i], color_data); + } + if (store_fac) { + r_fac[i] = f; + } + } + } +}; + +static void sh_node_brick_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexBrick *tex = (NodeTexBrick *)node.storage; + + builder.construct_and_set_matching_fn( + tex->offset, tex->offset_freq, tex->squash, tex->squash_freq); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_brick(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0); ntype.declare = blender::nodes::sh_node_tex_brick_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, node_shader_init_tex_brick); node_type_storage( &ntype, "NodeTexBrick", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_brick); + ntype.build_multi_function = blender::nodes::sh_node_brick_build_multi_function; nodeRegisterType(&ntype); } From c3ef1c15f5653ac323f4d077c7faf7b46f3eee0d Mon Sep 17 00:00:00 2001 From: YimingWu Date: Tue, 26 Oct 2021 22:55:27 +0800 Subject: [PATCH 1211/1500] LineArt: Stroke offset towards camera. Allows the user to turn off in_front option for grease pencil object and offset strokes towards camera to allow depth interaction of the rest of the scene. Reviewed By: Antonio Vazquez (antoniov) Differential Revision: https://developer.blender.org/D12046 --- source/blender/editors/object/object_add.c | 24 ++++++--- .../intern/MOD_gpencillineart.c | 52 ++++++++++++++++--- .../intern/lineart/MOD_lineart.h | 4 +- .../intern/lineart/lineart_chain.c | 38 ++++++++++++-- .../intern/lineart/lineart_cpu.c | 7 ++- .../intern/lineart/lineart_ops.c | 4 +- .../makesdna/DNA_gpencil_modifier_defaults.h | 1 + .../makesdna/DNA_gpencil_modifier_types.h | 4 +- .../makesrna/intern/rna_gpencil_modifier.c | 8 +++ 9 files changed, 121 insertions(+), 21 deletions(-) diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 629f40e0ae9..8fc1c119d0c 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1323,6 +1323,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front"); const bool use_lights = RNA_boolean_get(op->ptr, "use_lights"); const int stroke_depth_order = RNA_enum_get(op->ptr, "stroke_depth_order"); + const float stroke_depth_offset = RNA_float_get(op->ptr, "stroke_depth_offset"); ushort local_view_bits; float loc[3], rot[3]; @@ -1454,6 +1455,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) if (stroke_depth_order == GP_DRAWMODE_3D) { gpd->draw_mode = GP_DRAWMODE_3D; } + md->stroke_depth_offset = stroke_depth_offset; } break; @@ -1491,9 +1493,10 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op) uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE); uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE); bool in_front = RNA_boolean_get(op->ptr, "use_in_front"); - uiLayout *row = uiLayoutRow(layout, false); - uiLayoutSetActive(row, !in_front); - uiItemR(row, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE); + uiLayout *col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, !in_front); + uiItemR(col, op->ptr, "stroke_depth_offset", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE); } } @@ -1532,9 +1535,18 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", ""); RNA_def_boolean(ot->srna, "use_in_front", - false, - "In Front", + true, + "Show In Front", "Show line art grease pencil in front of everything"); + RNA_def_float(ot->srna, + "stroke_depth_offset", + 0.05f, + 0.0f, + 0.0f, + "Stroke Offset", + "Stroke offset for the line art modifier", + 0.0f, + 0.5f); RNA_def_boolean( ot->srna, "use_lights", false, "Use Lights", "Use lights for this grease pencil object"); RNA_def_enum( @@ -1543,7 +1555,7 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot) rna_enum_gpencil_add_stroke_depth_order_items, GP_DRAWMODE_3D, "Stroke Depth Order", - "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front'"); + "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front')"); } /** \} */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index c5ccf1d8229..a7164e5bf2c 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -160,12 +160,14 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec LineartCache *local_lc = gpd->runtime.lineart_cache; if (!gpd->runtime.lineart_cache) { - MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } else { if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { - MOD_lineart_compute_feature_lines(depsgraph, lmd, &local_lc); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } MOD_lineart_chain_clear_picked_flag(local_lc); @@ -210,7 +212,8 @@ static void bakeModifier(Main *UNUSED(bmain), lmd->edge_types_override = lmd->edge_types; lmd->level_end_override = lmd->level_end; - MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } @@ -412,14 +415,23 @@ static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels"); + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, !is_baked); - const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels"); + if (!show_in_front) { + uiItemL(layout, IFACE_("Object is not in front"), ICON_INFO); + } + + layout = uiLayoutColumn(layout, false); + uiLayoutSetActive(layout, show_in_front); uiItemR(layout, ptr, "use_multiple_levels", 0, IFACE_("Range"), ICON_NONE); @@ -447,11 +459,14 @@ static bool anything_showing_through(PointerRNA *ptr) static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + uiLayoutSetEnabled(layout, !is_baked); - uiLayoutSetActive(layout, anything_showing_through(ptr)); + uiLayoutSetActive(layout, (!show_in_front) && anything_showing_through(ptr)); uiItemR(layout, ptr, "use_material_mask", 0, IFACE_("Material Mask"), ICON_NONE); } @@ -654,6 +669,27 @@ static void bake_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_clear_all"); } +static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayout *layout = panel->layout; + + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + + uiLayoutSetPropSep(layout, true); + + if (show_in_front) { + uiItemL(layout, IFACE_("Object is shown in front"), ICON_ERROR); + } + + uiLayout *row = uiLayoutRow(layout, false); + uiLayoutSetActive(row, !show_in_front); + + uiItemR(row, ptr, "stroke_depth_offset", UI_ITEM_R_SLIDER, IFACE_("Depth Offset"), ICON_NONE); +} + static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( @@ -681,6 +717,8 @@ static void panelRegister(ARegionType *region_type) region_type, "chaining", "Chaining", NULL, chaining_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type); + gpencil_modifier_subpanel_register( + region_type, "composition", "Composition", NULL, composition_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "bake", "Bake", NULL, bake_panel_draw, panel_type); } diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index c00f34185dd..d170a6033fa 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -595,13 +595,15 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb); void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold); void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance); +void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, float dist); int MOD_lineart_chain_count(const LineartEdgeChain *ec); void MOD_lineart_chain_clear_picked_flag(LineartCache *lc); bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph, struct LineartGpencilModifierData *lmd, - LineartCache **cached_result); + struct LineartCache **cached_result, + bool enable_stroke_offset); struct Scene; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 8935bdd1870..57eeeb96541 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -926,9 +926,9 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) { - LISTBASE_FOREACH (LineartEdgeChain *, rlc, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { LineartEdgeChainItem *next_eci; - for (LineartEdgeChainItem *eci = rlc->chain.first; eci; eci = next_eci) { + for (LineartEdgeChainItem *eci = ec->chain.first; eci; eci = next_eci) { next_eci = eci->next; LineartEdgeChainItem *eci2, *eci3, *eci4; @@ -944,7 +944,7 @@ void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { /* And if p4 is on the extension of p1-p2 , we remove p3. */ if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { - BLI_remlink(&rlc->chain, eci3); + BLI_remlink(&ec->chain, eci3); next_eci = eci; } } @@ -1008,3 +1008,35 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol } } } + +void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, float dist) +{ + float dir[3]; + float cam[3]; + float view[3]; + float view_clamp[3]; + copy_v3fl_v3db(cam, rb->camera_pos); + copy_v3fl_v3db(view, rb->view_vector); + if (rb->cam_is_persp) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + sub_v3_v3v3(dir, cam, eci->gpos); + float orig_len = len_v3(dir); + normalize_v3(dir); + mul_v3_fl(dir, MIN2(dist, orig_len - rb->near_clip)); + add_v3_v3(eci->gpos, dir); + } + } + } + else { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + sub_v3_v3v3(dir, cam, eci->gpos); + float len_lim = dot_v3v3(view, dir) - rb->near_clip; + normalize_v3_v3(view_clamp, view); + mul_v3_fl(view_clamp, MIN2(dist, len_lim)); + add_v3_v3(eci->gpos, view_clamp); + } + } + } +} diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 6660da79b40..f8dda2f9174 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -4076,7 +4076,8 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this */ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModifierData *lmd, - LineartCache **cached_result) + LineartCache **cached_result, + bool enable_stroke_depth_offset) { LineartRenderBuffer *rb; Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -4189,6 +4190,10 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold); } + if (enable_stroke_depth_offset && lmd->stroke_depth_offset > FLT_EPSILON) { + MOD_lineart_chain_offset_towards_camera(rb, lmd->stroke_depth_offset); + } + /* Finally transfer the result list into cache. */ memcpy(&lc->chains, &rb->chains, sizeof(ListBase)); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c index 988c90483a6..b74499daf6b 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c @@ -118,12 +118,12 @@ static bool bake_strokes(Object *ob, } LineartCache *local_lc = *lc; if (!(*lc)) { - MOD_lineart_compute_feature_lines(dg, lmd, lc); + MOD_lineart_compute_feature_lines(dg, lmd, lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } else { if (is_first || (!(lmd->flags & LRT_GPENCIL_USE_CACHE))) { - MOD_lineart_compute_feature_lines(dg, lmd, &local_lc); + MOD_lineart_compute_feature_lines(dg, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } MOD_lineart_chain_clear_picked_flag(local_lc); diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 11299ae9717..1ad884bee8f 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -319,6 +319,7 @@ .angle_splitting_threshold = DEG2RAD(60.0f), \ .chaining_image_threshold = 0.001f, \ .chain_smooth_tolerance = 0.2f,\ + .stroke_depth_offset = 0.05,\ } #define _DNA_DEFAULT_LengthGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index c7a93080f7c..6ea07163ca8 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -1042,7 +1042,6 @@ typedef struct LineartGpencilModifierData { /** Strength for smoothing jagged chains. */ float chain_smooth_tolerance; - int _pad1; /* CPU mode */ float chaining_image_threshold; @@ -1053,6 +1052,9 @@ typedef struct LineartGpencilModifierData { /* #eLineArtGPencilModifierFlags, modifier internal state. */ int flags; + /* Move strokes towards camera to avoid clipping while preserve depth for the viewport. */ + float stroke_depth_offset; + /* Runtime data. */ /* Because we can potentially only compute features lines once per modifier stack (Use Cache), we diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 631d5822c5e..29689a2b281 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -3200,6 +3200,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) "separate stroke for each overlapping type"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "stroke_depth_offset", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_ui_text(prop, + "Stroke Depth Offset", + "Move strokes slightly towards the camera to avoid clipping while " + "preserve depth for the viewport"); + RNA_def_property_ui_range(prop, 0.0f, 0.5f, 0.001f, 4); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "source_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, modifier_lineart_source_type); RNA_def_property_ui_text(prop, "Source Type", "Line art stroke source type"); From 773f5065f38fe3bb3b91424930e1c37ab85b4506 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Tue, 26 Oct 2021 23:44:08 +0800 Subject: [PATCH 1212/1500] LineArt: Fix prop range for stroke_depth_offset. --- source/blender/makesrna/intern/rna_gpencil_modifier.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 29689a2b281..f5865e6ea26 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -3205,6 +3205,7 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) "Stroke Depth Offset", "Move strokes slightly towards the camera to avoid clipping while " "preserve depth for the viewport"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0f, 0.5f, 0.001f, 4); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); From efbd36429a0c381a972f7da97bc9fbc9096e5f20 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Tue, 26 Oct 2021 23:37:25 +0800 Subject: [PATCH 1213/1500] LineArt: Custom Camera Allows line art camera to be different from scene active camera, useful when baking multiple shots in different angle as well as for motion graphics effect. Reviewed By: Antonio Vazquez (antoniov) Differential Revision: https://developer.blender.org/D12047 --- .../intern/MOD_gpencillineart.c | 21 +++++++++-- .../intern/lineart/MOD_lineart.h | 5 ++- .../intern/lineart/lineart_chain.c | 12 +++++- .../intern/lineart/lineart_cpu.c | 37 ++++++++++++++----- .../makesdna/DNA_gpencil_modifier_types.h | 3 ++ source/blender/makesdna/DNA_lineart_types.h | 1 + .../makesrna/intern/rna_gpencil_modifier.c | 21 +++++++++++ 7 files changed, 84 insertions(+), 16 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index a7164e5bf2c..4ce1a903269 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -264,6 +264,12 @@ static void updateDepsgraph(GpencilModifierData *md, else { add_this_collection(ctx->scene->master_collection, ctx, mode); } + if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA) { + DEG_add_object_relation( + ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); + DEG_add_object_relation( + ctx->node, lmd->source_camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier"); + } if (ctx->scene->camera) { DEG_add_object_relation( ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); @@ -280,6 +286,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, walk(userData, ob, (ID **)&lmd->source_collection, IDWALK_CB_NOP); walk(userData, ob, (ID **)&lmd->source_object, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&lmd->source_camera, IDWALK_CB_NOP); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -385,7 +392,12 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) return; } - uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE); + uiLayout *row = uiLayoutRowWithHeading(layout, false, IFACE_("Custom Camera")); + uiItemR(row, ptr, "use_custom_camera", 0, "", 0); + uiLayout *subrow = uiLayoutRow(row, true); + uiLayoutSetActive(subrow, RNA_boolean_get(ptr, "use_custom_camera")); + uiLayoutSetPropSep(subrow, true); + uiItemR(subrow, ptr, "source_camera", 0, "", ICON_OBJECT_DATA); uiLayout *col = uiLayoutColumn(layout, true); @@ -684,10 +696,11 @@ static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemL(layout, IFACE_("Object is shown in front"), ICON_ERROR); } - uiLayout *row = uiLayoutRow(layout, false); - uiLayoutSetActive(row, !show_in_front); + uiLayout *col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, !show_in_front); - uiItemR(row, ptr, "stroke_depth_offset", UI_ITEM_R_SLIDER, IFACE_("Depth Offset"), ICON_NONE); + uiItemR(col, ptr, "stroke_depth_offset", UI_ITEM_R_SLIDER, IFACE_("Depth Offset"), ICON_NONE); + uiItemR(col, ptr, "offset_towards_custom_camera", 0, IFACE_("Towards Custom Camera"), ICON_NONE); } static void panelRegister(ARegionType *region_type) diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index d170a6033fa..727de381fa4 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -311,6 +311,7 @@ typedef struct LineartRenderBuffer { bool cam_is_persp; float cam_obmat[4][4]; double camera_pos[3]; + double active_camera_pos[3]; /* Stroke offset calculation may use active or selected camera. */ double near_clip, far_clip; float shift_x, shift_y; float crease_threshold; @@ -595,7 +596,9 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb); void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold); void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance); -void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, float dist); +void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, + float dist, + bool use_custom_camera); int MOD_lineart_chain_count(const LineartEdgeChain *ec); void MOD_lineart_chain_clear_picked_flag(LineartCache *lc); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 57eeeb96541..8b7f53b8a36 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -1009,7 +1009,9 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol } } -void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, float dist) +void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, + float dist, + bool use_custom_camera) { float dir[3]; float cam[3]; @@ -1017,6 +1019,14 @@ void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, float dist float view_clamp[3]; copy_v3fl_v3db(cam, rb->camera_pos); copy_v3fl_v3db(view, rb->view_vector); + + if (use_custom_camera) { + copy_v3fl_v3db(cam, rb->camera_pos); + } + else { + copy_v3fl_v3db(cam, rb->active_camera_pos); + } + if (rb->cam_is_persp) { LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index f8dda2f9174..ea2619a9328 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -2998,7 +2998,7 @@ void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) } } -static LineartCache *lineart_init_cache() +static LineartCache *lineart_init_cache(void) { LineartCache *lc = MEM_callocN(sizeof(LineartCache), "Lineart Cache"); return lc; @@ -3016,6 +3016,8 @@ void MOD_lineart_clear_cache(struct LineartCache **lc) static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, LineartGpencilModifierData *lmd, + Object *camera, + Object *active_camera, LineartCache *lc) { LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer"); @@ -3024,10 +3026,10 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, lmd->render_buffer_ptr = rb; lc->rb_edge_types = lmd->edge_types_override; - if (!scene || !scene->camera || !lc) { + if (!scene || !camera || !lc) { return NULL; } - Camera *c = scene->camera->data; + Camera *c = camera->data; double clipping_offset = 0; if (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) { @@ -3035,8 +3037,11 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, clipping_offset = 0.0001; } - copy_v3db_v3fl(rb->camera_pos, scene->camera->obmat[3]); - copy_m4_m4(rb->cam_obmat, scene->camera->obmat); + copy_v3db_v3fl(rb->camera_pos, camera->obmat[3]); + if (active_camera) { + copy_v3db_v3fl(rb->active_camera_pos, active_camera->obmat[3]); + } + copy_m4_m4(rb->cam_obmat, camera->obmat); rb->cam_is_persp = (c->type == CAM_PERSP); rb->near_clip = c->clip_start + clipping_offset; rb->far_clip = c->clip_end - clipping_offset; @@ -4082,6 +4087,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartRenderBuffer *rb; Scene *scene = DEG_get_evaluated_scene(depsgraph); int intersections_only = 0; /* Not used right now, but preserve for future. */ + Object *use_camera; double t_start; @@ -4091,14 +4097,24 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, BKE_scene_camera_switch_update(scene); - if (!scene->camera) { - return false; + if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA) { + if (!lmd->source_camera || + (use_camera = DEG_get_evaluated_object(depsgraph, lmd->source_camera))->type != + OB_CAMERA) { + return false; + } + } + else { + if (!scene->camera) { + return false; + } + use_camera = scene->camera; } LineartCache *lc = lineart_init_cache(); *cached_result = lc; - rb = lineart_create_render_buffer(scene, lmd, lc); + rb = lineart_create_render_buffer(scene, lmd, use_camera, scene->camera, lc); /* Triangle thread testing data size varies depending on the thread count. * See definition of LineartTriangleThread for details. */ @@ -4118,7 +4134,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, /* Get view vector before loading geometries, because we detect feature lines there. */ lineart_main_get_view_vector(rb); lineart_main_load_geometries( - depsgraph, scene, scene->camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); + depsgraph, scene, use_camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); if (!rb->vertex_buffer_pointers.first) { /* No geometry loaded, return early. */ @@ -4191,7 +4207,8 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, } if (enable_stroke_depth_offset && lmd->stroke_depth_offset > FLT_EPSILON) { - MOD_lineart_chain_offset_towards_camera(rb, lmd->stroke_depth_offset); + MOD_lineart_chain_offset_towards_camera( + rb, lmd->stroke_depth_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); } /* Finally transfer the result list into cache. */ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 6ea07163ca8..339714da255 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -978,6 +978,7 @@ typedef enum eLineArtGPencilModifierFlags { LRT_GPENCIL_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */, LRT_GPENCIL_IS_BAKED = (1 << 3), LRT_GPENCIL_USE_CACHE = (1 << 4), + LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA = (1 << 5), } eLineArtGPencilModifierFlags; typedef enum eLineartGpencilMaskSwitches { @@ -1004,6 +1005,8 @@ typedef struct LineartGpencilModifierData { short level_start; short level_end; + struct Object *source_camera; + struct Object *source_object; struct Collection *source_collection; diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index bdc9bcb6980..a52c434595c 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -49,6 +49,7 @@ typedef enum eLineartMainFlags { LRT_ALLOW_OVERLAP_EDGE_TYPES = (1 << 14), LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15), LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16), + LRT_USE_CUSTOM_CAMERA = (1 << 17), } eLineartMainFlags; typedef enum eLineartEdgeFlag { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index f5865e6ea26..fdce35ba2de 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -3073,6 +3073,12 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_define_lib_overridable(true); + prop = RNA_def_property(srna, "use_custom_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_CUSTOM_CAMERA); + RNA_def_property_ui_text( + prop, "Use Custom Camera", "Use custom camera instead of the active camera"); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_INTERSECTION_AS_CONTOUR); RNA_def_property_ui_text(prop, @@ -3209,6 +3215,21 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_ui_range(prop, 0.0f, 0.5f, 0.001f, 4); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "offset_towards_custom_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + RNA_def_property_ui_text(prop, + "Offset Towards Custom Camera", + "Offset strokes towards selected camera instead of the active camera"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "source_camera", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text( + prop, "Camera Object", "Use specified camera object for generating line art"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + prop = RNA_def_property(srna, "source_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, modifier_lineart_source_type); RNA_def_property_ui_text(prop, "Source Type", "Line art stroke source type"); From 01d7211380f513cf8f1f9c7cae923b1d19b26ebc Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 26 Oct 2021 17:53:29 +0200 Subject: [PATCH 1214/1500] Fix T92505: previewing specific node outputs did not work anymore This was missing from my refactor in rB5bfe09df2244cb9de0b6554a378eecef77b1e75d. --- source/blender/editors/space_node/node_relationships.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 459608a67ea..76aad684b4c 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -768,6 +768,14 @@ static const NodeRef *get_existing_viewer(const NodeTreeRef &tree) static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *active_viewer_node, const NodeRef &node_to_view) { + /* Check if any of the output sockets is selected, which is the case when the user just clicked + * on the socket. */ + for (const OutputSocketRef *output_socket : node_to_view.outputs()) { + if (output_socket->bsocket()->flag & SELECT) { + return output_socket; + } + } + const OutputSocketRef *last_socket_linked_to_viewer = nullptr; if (active_viewer_node != nullptr) { for (const OutputSocketRef *output_socket : node_to_view.outputs()) { From f1a662c15771d7ced920986db6b94c50d3e6c815 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 26 Oct 2021 11:03:45 -0500 Subject: [PATCH 1215/1500] Fix: Assert on startup from incorrect float property min --- source/blender/editors/object/object_add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 8fc1c119d0c..58767d5f50f 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1542,7 +1542,7 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot) "stroke_depth_offset", 0.05f, 0.0f, - 0.0f, + FLT_MAX, "Stroke Offset", "Stroke offset for the line art modifier", 0.0f, From 3371a4c472eef06d1c9e15451b245cb4575d667f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 26 Oct 2021 11:05:01 -0500 Subject: [PATCH 1216/1500] UI: Improve node editor breadcrumbs display This patch upgrades node editor breadcrumbs to have slightly more visual weight, to including the base path of object/modifier/world, etc, have more visually pleasing spacing, and contain icons. In the code, a generic "context path" is added to interface code. The idea is that this could be used to draw other breadcrumbs in areas like the property editor or the spreadsheet, and features could be added to all of those areas at the same time. Ideally we would be able to control the color of the breadcrumbs with a specific theme color, but since they are drawn with the regular layout system, that is not easily possible. Thanks to @fabian_schempp for the original patch. Differential Revision: https://developer.blender.org/D10413 --- source/blender/editors/include/ED_node.h | 1 - .../blender/editors/include/UI_interface.hh | 25 +++ .../blender/editors/interface/CMakeLists.txt | 1 + .../interface/interface_context_path.cc | 85 ++++++++ .../blender/editors/space_node/CMakeLists.txt | 1 + .../editors/space_node/node_context_path.cc | 184 ++++++++++++++++++ .../blender/editors/space_node/node_draw.cc | 39 +++- .../blender/editors/space_node/node_intern.h | 8 + .../blender/editors/space_node/space_node.c | 21 -- .../makesrna/intern/rna_gpencil_modifier.c | 4 +- 10 files changed, 337 insertions(+), 32 deletions(-) create mode 100644 source/blender/editors/interface/interface_context_path.cc create mode 100644 source/blender/editors/space_node/node_context_path.cc diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 1ba52552902..e68617f7867 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -64,7 +64,6 @@ void ED_node_cursor_location_set(struct SpaceNode *snode, const float value[2]); int ED_node_tree_path_length(struct SpaceNode *snode); void ED_node_tree_path_get(struct SpaceNode *snode, char *value); -void ED_node_tree_path_get_fixedbuf(struct SpaceNode *snode, char *value, int max_length); void ED_node_tree_start(struct SpaceNode *snode, struct bNodeTree *ntree, diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index 5edccfa8c88..b14ee6c4a59 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -23,15 +23,40 @@ #include #include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "UI_resources.h" namespace blender::nodes::geometry_nodes_eval_log { struct GeometryAttributeInfo; } struct uiBlock; +struct StructRNA; +struct uiSearchItems; + namespace blender::ui { + class AbstractTreeView; +/** + * An item in a breadcrumb-like context. Currently this struct is very simple, but more + * could be added to it in the future, to support interactivity or tooltips, for example. + */ +struct ContextPathItem { + /* Text to display in the UI. */ + std::string name; + /* #BIFIconID */ + int icon; +}; + +void context_path_add_generic(Vector &path, + StructRNA &rna_type, + void *ptr, + const BIFIconID icon_override = ICON_NONE); + +void template_breadcrumbs(uiLayout &layout, Span context_path); + void attribute_search_add_items( StringRefNull str, const bool is_output, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index b2659f5ed52..84172c7efce 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -43,6 +43,7 @@ set(SRC interface_anim.c interface_button_group.c interface_context_menu.c + interface_context_path.cc interface_draw.c interface_dropboxes.cc interface_eyedropper.c diff --git a/source/blender/editors/interface/interface_context_path.cc b/source/blender/editors/interface/interface_context_path.cc new file mode 100644 index 00000000000..b0f8d186afa --- /dev/null +++ b/source/blender/editors/interface/interface_context_path.cc @@ -0,0 +1,85 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edinterface + */ + +#include "BLI_vector.hh" + +#include "BKE_screen.h" + +#include "RNA_access.h" + +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" + +#include "WM_api.h" + +namespace blender::ui { + +void context_path_add_generic(Vector &path, + StructRNA &rna_type, + void *ptr, + const BIFIconID icon_override) +{ + /* Add the null check here to make calling functions less verbose. */ + if (!ptr) { + return; + } + + PointerRNA rna_ptr; + RNA_pointer_create(nullptr, &rna_type, ptr, &rna_ptr); + char name[128]; + RNA_struct_name_get_alloc(&rna_ptr, name, sizeof(name), nullptr); + + /* Use a blank icon by default to check whether to retrieve it automatically from the type. */ + const BIFIconID icon = icon_override == ICON_NONE ? + static_cast(RNA_struct_ui_icon(rna_ptr.type)) : + icon_override; + + path.append({name, static_cast(icon)}); +} + +/* -------------------------------------------------------------------- */ +/** \name Breadcrumb Template + * \{ */ + +void template_breadcrumbs(uiLayout &layout, Span context_path) +{ + uiLayout *row = uiLayoutRow(&layout, true); + uiLayoutSetAlignment(&layout, UI_LAYOUT_ALIGN_LEFT); + + for (const int i : context_path.index_range()) { + uiLayout *sub_row = uiLayoutRow(row, true); + uiLayoutSetAlignment(sub_row, UI_LAYOUT_ALIGN_LEFT); + + if (i > 0) { + uiItemL(sub_row, "", ICON_RIGHTARROW_THIN); + } + uiItemL(sub_row, context_path[i].name.c_str(), context_path[i].icon); + } +} + +} // namespace blender::ui + +/** \} */ \ No newline at end of file diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 80d3b43bf6b..600309c2c86 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -40,6 +40,7 @@ set(INC set(SRC drawnode.cc node_add.cc + node_context_path.cc node_draw.cc node_edit.cc node_geometry_attribute_search.cc diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc new file mode 100644 index 00000000000..a0ff7f3ce25 --- /dev/null +++ b/source/blender/editors/space_node/node_context_path.cc @@ -0,0 +1,184 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spnode + * \brief Node breadcrumbs drawing + */ + +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "BKE_context.h" +#include "BKE_material.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BKE_screen.h" + +#include "RNA_access.h" + +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" + +#include "UI_interface.hh" + +#include "node_intern.h" + +struct Mesh; +struct Curve; +struct Light; +struct World; +struct Material; + +namespace blender::ed::space_node { + +static void context_path_add_object_data(Vector &path, Object &object) +{ + if (object.type == OB_MESH && object.data) { + Mesh *mesh = (Mesh *)object.data; + ui::context_path_add_generic(path, RNA_Mesh, mesh); + } + if (object.type == OB_LAMP && object.data) { + Light *light = (Light *)object.data; + ui::context_path_add_generic(path, RNA_Light, light); + } + if (ELEM(object.type, OB_CURVE, OB_FONT, OB_SURF) && object.data) { + Curve *curve = (Curve *)object.data; + ui::context_path_add_generic(path, RNA_Curve, curve); + } +} + +static void context_path_add_node_tree_and_node_groups(const SpaceNode &snode, + Vector &path, + const bool skip_base = false) +{ + Vector tree_path = snode.treepath; + for (const bNodeTreePath *path_item : tree_path.as_span().drop_front(int(skip_base))) { + ui::context_path_add_generic(path, RNA_NodeTree, path_item->nodetree, ICON_NODETREE); + } +} + +static void get_context_path_node_shader(const bContext &C, + SpaceNode &snode, + Vector &path) +{ + if (snode.flag & SNODE_PIN) { + if (snode.shaderfrom == SNODE_SHADER_WORLD) { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + if (scene != nullptr) { + World *world = scene->world; + ui::context_path_add_generic(path, RNA_World, world); + } + /* Skip the base node tree here, because the world contains a node tree already. */ + context_path_add_node_tree_and_node_groups(snode, path, true); + } + else { + context_path_add_node_tree_and_node_groups(snode, path); + } + } + else { + Object *object = CTX_data_active_object(&C); + if (snode.shaderfrom == SNODE_SHADER_OBJECT && object != nullptr) { + ui::context_path_add_generic(path, RNA_Object, object); + if (!(object->matbits && object->matbits[object->actcol - 1])) { + context_path_add_object_data(path, *object); + } + Material *material = BKE_object_material_get(object, object->actcol); + ui::context_path_add_generic(path, RNA_Material, material); + } + else if (snode.shaderfrom == SNODE_SHADER_WORLD) { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + if (scene != nullptr) { + World *world = scene->world; + ui::context_path_add_generic(path, RNA_World, world); + } + } +#ifdef WITH_FREESTYLE + else if (snode.shaderfrom == SNODE_SHADER_LINESTYLE) { + ViewLayer *viewlayer = CTX_data_view_layer(&C); + FreestyleLineStyle *linestyle = BKE_linestyle_active_from_view_layer(viewlayer); + ui::context_path_add_generic(path, RNA_ViewLayer, viewlayer); + Material *mat = BKE_object_material_get(object, object->actcol); + ui::context_path_add_generic(path, RNA_Material, mat); + } +#endif + context_path_add_node_tree_and_node_groups(snode, path, true); + } +} + +static void get_context_path_node_compositor(const bContext &C, + SpaceNode &snode, + Vector &path) +{ + if (snode.flag & SNODE_PIN) { + context_path_add_node_tree_and_node_groups(snode, path); + } + else { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + context_path_add_node_tree_and_node_groups(snode, path); + } +} + +static void get_context_path_node_geometry(const bContext &C, + SpaceNode &snode, + Vector &path) +{ + if (snode.flag & SNODE_PIN) { + context_path_add_node_tree_and_node_groups(snode, path); + } + else { + Object *object = CTX_data_active_object(&C); + ui::context_path_add_generic(path, RNA_Object, object); + ModifierData *modifier = BKE_object_active_modifier(object); + ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_MODIFIER); + context_path_add_node_tree_and_node_groups(snode, path); + } +} + +Vector context_path_for_space_node(const bContext &C) +{ + SpaceNode *snode = CTX_wm_space_node(&C); + if (snode == nullptr) { + return {}; + } + + Vector context_path; + + if (snode->edittree->type == NTREE_GEOMETRY) { + get_context_path_node_geometry(C, *snode, context_path); + } + else if (snode->edittree->type == NTREE_SHADER) { + get_context_path_node_shader(C, *snode, context_path); + } + else if (snode->edittree->type == NTREE_COMPOSIT) { + get_context_path_node_compositor(C, *snode, context_path); + } + + return context_path; +} + +} // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 9e91d394fef..ea65eaa0a14 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -71,8 +71,10 @@ #include "ED_gpencil.h" #include "ED_node.h" +#include "ED_screen.h" #include "ED_space_api.h" +#include "UI_interface.hh" #include "UI_resources.h" #include "UI_view2d.h" @@ -2158,15 +2160,34 @@ void node_draw_nodetree(const bContext *C, } } -/* Draw tree path info in lower left corner. */ -static void draw_tree_path(SpaceNode *snode) +/* Draw the breadcrumb on the bottom of the editor. */ +static void draw_tree_path(const bContext &C, ARegion ®ion) { - char info[256]; + using namespace blender; - ED_node_tree_path_get_fixedbuf(snode, info, sizeof(info)); + GPU_matrix_push_projection(); + wmOrtho2_region_pixelspace(®ion); - UI_FontThemeColor(BLF_default(), TH_TEXT_HI); - BLF_draw_default(1.5f * UI_UNIT_X, 1.5f * UI_UNIT_Y, 0.0f, info, sizeof(info)); + const rcti *rect = ED_region_visible_rect(®ion); + + const uiStyle *style = UI_style_get_dpi(); + const float padding_x = 16 * UI_DPI_FAC; + const int x = rect->xmin + padding_x; + const int y = region.winy - UI_UNIT_Y * 0.6f; + const int width = BLI_rcti_size_x(rect) - 2 * padding_x; + + uiBlock *block = UI_block_begin(&C, ®ion, __func__, UI_EMBOSS_NONE); + uiLayout *layout = UI_block_layout( + block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, x, y, width, 1, 0, style); + + Vector context_path = ed::space_node::context_path_for_space_node(C); + ui::template_breadcrumbs(*layout, context_path); + + UI_block_layout_resolve(block, nullptr, nullptr); + UI_block_end(&C, block); + UI_block_draw(&C, block); + + GPU_matrix_pop_projection(); } static void snode_setup_v2d(SpaceNode *snode, ARegion *region, const float center[2]) @@ -2336,8 +2357,10 @@ void node_draw_space(const bContext *C, ARegion *region) } } - /* Tree path info. */ - draw_tree_path(snode); + /* Draw context path. */ + if (snode->edittree) { + draw_tree_path(*C, *region); + } /* Scrollers. */ UI_view2d_scrollers_draw(v2d, nullptr); diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index f069038cc09..e7d9a12a52c 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -341,3 +341,11 @@ extern const char *node_context_dir[]; #ifdef __cplusplus } #endif + +#ifdef __cplusplus +# include "BLI_vector.hh" +# include "UI_interface.hh" +namespace blender::ed::space_node { +Vector context_path_for_space_node(const bContext &C); +} +#endif \ No newline at end of file diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index 8c015a56fa1..0b5d7cdda82 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -201,27 +201,6 @@ void ED_node_tree_path_get(SpaceNode *snode, char *value) } } -void ED_node_tree_path_get_fixedbuf(SpaceNode *snode, char *value, int max_length) -{ - int size; - - value[0] = '\0'; - int i = 0; - LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) { - if (i == 0) { - size = BLI_strncpy_rlen(value, path->display_name, max_length); - } - else { - size = BLI_snprintf_rlen(value, max_length, "/%s", path->display_name); - } - max_length -= size; - if (max_length <= 0) { - break; - } - value += size; - } -} - void ED_node_set_active_viewer_key(SpaceNode *snode) { bNodeTreePath *path = snode->treepath.last; diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index fdce35ba2de..79125e5d40e 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -3211,8 +3211,8 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) "Stroke Depth Offset", "Move strokes slightly towards the camera to avoid clipping while " "preserve depth for the viewport"); - RNA_def_property_range(prop, 0.0f, FLT_MAX); - RNA_def_property_ui_range(prop, 0.0f, 0.5f, 0.001f, 4); + RNA_def_property_ui_range(prop, 0.0, 0.5, 0.001, 4); + RNA_def_property_range(prop, -0.1, FLT_MAX); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "offset_towards_custom_camera", PROP_BOOLEAN, PROP_NONE); From ec831ce5df86cbdbed8030d8a056f8a9b4eb0273 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Wed, 27 Oct 2021 00:10:25 +0800 Subject: [PATCH 1217/1500] LineArt: Trimming edges right at the image border This option allows the edge to end right at the border instead of extending beyond. Useful when having multiple camera setup where you want the border to be clean. Also moved overscan option down inside "Composition" sub panel so it makes more sense. Reviewed By: Antonio Vazquez (antoniov) Differential Revision: https://developer.blender.org/D12126 --- .../intern/MOD_gpencillineart.c | 5 +- .../intern/lineart/MOD_lineart.h | 8 +- .../intern/lineart/lineart_chain.c | 127 +++++++++++++++++- .../intern/lineart/lineart_cpu.c | 6 + source/blender/makesdna/DNA_lineart_types.h | 1 + .../makesrna/intern/rna_gpencil_modifier.c | 8 ++ 6 files changed, 146 insertions(+), 9 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 4ce1a903269..c6bc5a9e53e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -406,7 +406,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE); - uiItemR(layout, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); + uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); } static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -692,6 +692,9 @@ static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); + uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_image_boundary_trimming", 0, NULL, ICON_NONE); + if (show_in_front) { uiItemL(layout, IFACE_("Object is shown in front"), ICON_ERROR); } diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 727de381fa4..d8926a63307 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -182,9 +182,9 @@ typedef struct LineartEdgeChain { typedef struct LineartEdgeChainItem { struct LineartEdgeChainItem *next, *prev; - /** Need z value for fading */ - float pos[3]; - /** For restoring position to 3d space */ + /** Need z value for fading, w value for image frame clipping. */ + float pos[4]; + /** For restoring position to 3d space. */ float gpos[3]; float normal[3]; unsigned char line_type; @@ -299,6 +299,7 @@ typedef struct LineartRenderBuffer { bool use_loose_as_contour; bool use_loose_edge_chain; bool use_geometry_space_chain; + bool use_image_boundary_trimming; bool filter_face_mark; bool filter_face_mark_invert; @@ -594,6 +595,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb); void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb); void MOD_lineart_chain_connect(LineartRenderBuffer *rb); void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold); +void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb); void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance); void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 8b7f53b8a36..85b801eece2 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -126,7 +126,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); - copy_v2_v2(eci->pos, fbcoord); + copy_v4_v4(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); eci->index = index; copy_v3_v3(eci->normal, normal); @@ -156,7 +156,7 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); - copy_v2_v2(eci->pos, fbcoord); + copy_v4_v4(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); eci->index = index; copy_v3_v3(eci->normal, normal); @@ -177,15 +177,15 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) int last_occlusion; unsigned char last_transparency; /* Used when converting from double. */ - float use_fbcoord[2]; + float use_fbcoord[4]; float use_gpos[3]; #define VERT_COORD_TO_FLOAT(a) \ - copy_v2fl_v2db(use_fbcoord, (a)->fbcoord); \ + copy_v4fl_v4db(use_fbcoord, (a)->fbcoord); \ copy_v3fl_v3db(use_gpos, (a)->gloc); #define POS_TO_FLOAT(lpos, gpos) \ - copy_v2fl_v2db(use_fbcoord, lpos); \ + copy_v3fl_v3db(use_fbcoord, lpos); \ copy_v3fl_v3db(use_gpos, gpos); LRT_ITER_ALL_LINES_BEGIN @@ -262,6 +262,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_prepend_point(rb, ec, @@ -287,6 +288,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_prepend_point(rb, ec, @@ -340,6 +342,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at); + use_fbcoord[3] = interpf(e->v2->fbcoord[3], e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, ec, @@ -403,6 +406,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); last_occlusion = es->prev ? es->prev->occlusion : last_occlusion; last_transparency = es->prev ? es->prev->material_mask_bits : last_transparency; POS_TO_FLOAT(lpos, gpos) @@ -430,6 +434,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, ec, @@ -952,6 +957,118 @@ void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) } } +static LineartEdgeChainItem *lineart_chain_create_crossing_point(LineartRenderBuffer *rb, + LineartEdgeChainItem *eci_inside, + LineartEdgeChainItem *eci_outside) +{ + float isec[2]; + float LU[2] = {-1.0f, 1.0f}, LB[2] = {-1.0f, -1.0f}, RU[2] = {1.0f, 1.0f}, RB[2] = {1.0f, -1.0f}; + bool found = false; + LineartEdgeChainItem *eci2 = eci_outside, *eci1 = eci_inside; + if (eci2->pos[0] < -1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, LB, isec) > 0); + } + if (!found && eci2->pos[0] > 1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, RU, RB, isec) > 0); + } + if (!found && eci2->pos[1] < -1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LB, RB, isec) > 0); + } + if (!found && eci2->pos[1] > 1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, RU, isec) > 0); + } + + if (UNLIKELY(!found)) { + return NULL; + } + + float ratio = (fabs(eci2->pos[0] - eci1->pos[0]) > fabs(eci2->pos[1] - eci1->pos[1])) ? + ratiof(eci1->pos[0], eci2->pos[0], isec[0]) : + ratiof(eci1->pos[1], eci2->pos[1], isec[1]); + float gratio = eci1->pos[3] * ratio / (ratio * eci1->pos[3] + (1 - ratio) * eci2->pos[3]); + + LineartEdgeChainItem *eci = lineart_mem_acquire(rb->chain_data_pool, + sizeof(LineartEdgeChainItem)); + memcpy(eci, eci1, sizeof(LineartEdgeChainItem)); + interp_v3_v3v3(eci->gpos, eci1->gpos, eci2->gpos, gratio); + interp_v3_v3v3(eci->pos, eci1->pos, eci2->pos, ratio); + eci->pos[3] = interpf(eci2->pos[3], eci1->pos[3], gratio); + eci->next = eci->prev = NULL; + return eci; +} + +#define LRT_ECI_INSIDE(eci) \ + ((eci)->pos[0] >= -1.0f && (eci)->pos[0] <= 1.0f && (eci)->pos[1] >= -1.0f && \ + (eci)->pos[1] <= 1.0f) + +void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb) +{ + LineartEdgeChain *ec; + LineartEdgeChainItem *eci, *next_eci, *prev_eci, *new_eci; + bool is_inside, new_inside; + ListBase swap = {0}; + swap.first = rb->chains.first; + swap.last = rb->chains.last; + + rb->chains.last = rb->chains.first = NULL; + while ((ec = BLI_pophead(&swap)) != NULL) { + bool ec_added = false; + LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first; + is_inside = LRT_ECI_INSIDE(first_eci) ? true : false; + if (!is_inside) { + ec->picked = true; + } + for (eci = first_eci->next; eci; eci = next_eci) { + next_eci = eci->next; + prev_eci = eci->prev; + + /* We only need to do something if the edge crossed from outside to the inside or from inside + * to the outside. */ + if ((new_inside = LRT_ECI_INSIDE(eci)) != is_inside) { + if (new_inside == false) { + /* Stroke goes out. */ + new_eci = lineart_chain_create_crossing_point(rb, prev_eci, eci); + + LineartEdgeChain *new_ec = lineart_mem_acquire(rb->chain_data_pool, + sizeof(LineartEdgeChain)); + memcpy(new_ec, ec, sizeof(LineartEdgeChain)); + new_ec->chain.first = next_eci; + eci->prev = NULL; + prev_eci->next = NULL; + ec->chain.last = prev_eci; + BLI_addtail(&ec->chain, new_eci); + BLI_addtail(&rb->chains, ec); + ec_added = true; + ec = new_ec; + + next_eci = eci->next; + is_inside = new_inside; + continue; + } + else { + /* Stroke comes in. */ + new_eci = lineart_chain_create_crossing_point(rb, eci, prev_eci); + + ec->chain.first = eci; + eci->prev = NULL; + + BLI_addhead(&ec->chain, new_eci); + + ec_added = false; + + next_eci = eci->next; + is_inside = new_inside; + continue; + } + } + } + + if ((!ec_added) && is_inside) { + BLI_addtail(&rb->chains, ec); + } + } +} + /** * This should always be the last stage!, see the end of * #MOD_lineart_chain_split_for_fixed_occlusion(). diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index ea2619a9328..93e9062e910 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -3077,6 +3077,8 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0; rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0; rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0; + rb->use_image_boundary_trimming = (lmd->calculation_flags & LRT_USE_IMAGE_BOUNDARY_TRIMMING) != + 0; /* See lineart_edge_from_triangle() for how this option may impact performance. */ rb->allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0; @@ -4202,6 +4204,10 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50); } + if (rb->use_image_boundary_trimming) { + MOD_lineart_chain_clip_at_border(rb); + } + if (rb->angle_splitting_threshold > FLT_EPSILON) { MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold); } diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index a52c434595c..d4440592a00 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -50,6 +50,7 @@ typedef enum eLineartMainFlags { LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15), LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16), LRT_USE_CUSTOM_CAMERA = (1 << 17), + LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20), } eLineartMainFlags; typedef enum eLineartEdgeFlag { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 79125e5d40e..30ec6532485 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -3405,6 +3405,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Crease On Sharp Edges", "Allow crease to show on sharp edges"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_image_boundary_trimming", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_IMAGE_BOUNDARY_TRIMMING); + RNA_def_property_ui_text( + prop, + "Image Boundary Trimming", + "Trim all edges right at the boundary of image(including overscan region)"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_define_lib_overridable(false); } From df2e05393533bff6b14751deb890ef0fc4064c8f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 18:04:45 +0200 Subject: [PATCH 1218/1500] UI: Improved feedback when dropping is not possible on drag 'n drop * Allow operators to show a "disabled hint" in red text explaining why dropping at the current location and in current context doesn't work. Should greatly help users to understand what's the problem. * Show a "stop" cursor when dropping isn't possible, like it's common on OSes. Differential Revision: https://developer.blender.org/D10358 --- source/blender/windowmanager/WM_types.h | 5 + .../windowmanager/intern/wm_dragdrop.c | 137 +++++++++++++++--- .../windowmanager/intern/wm_event_system.c | 10 +- .../blender/windowmanager/wm_event_system.h | 2 + 4 files changed, 125 insertions(+), 29 deletions(-) diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 2813047f0e4..1f1b1a70685 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -1044,6 +1044,11 @@ typedef struct wmDrag { * triggered. */ struct wmDropBox *active_dropbox; + /* Text to show when the operator poll fails. Typically the message the + * operator set with CTX_wm_operator_poll_msg_set(). */ + const char *disabled_info; + bool free_disabled_info; + unsigned int flags; /** List of wmDragIDs, all are guaranteed to have the same ID type. */ diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index c60e47a3b0f..3206d0c044e 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -207,6 +207,30 @@ wmDrag *WM_event_start_drag( return drag; } +/** + * Additional work to cleanly end dragging. Additional because this doesn't actually remove the + * drag items. Should be called whenever dragging is stopped (successful or not, also when + * canceled). + */ +void wm_drags_exit(wmWindowManager *wm, wmWindow *win) +{ + bool any_active = false; + LISTBASE_FOREACH (const wmDrag *, drag, &wm->drags) { + if (drag->active_dropbox) { + any_active = true; + } + } + + /* If there is no active drop-box #wm_drags_check_ops() set a stop-cursor, which needs to be + * restored. */ + if (!any_active) { + WM_cursor_modal_restore(win); + /* Ensure the correct area cursor is restored. */ + win->tag_cursor_refresh = true; + WM_event_add_mousemove(win); + } +} + void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy) { drag->imb = imb; @@ -240,6 +264,9 @@ void WM_drag_free(wmDrag *drag) if (drag->flags & WM_DRAG_FREE_DATA) { WM_drag_data_free(drag->type, drag->poin); } + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } BLI_freelistN(&drag->ids); LISTBASE_FOREACH_MUTABLE (wmDragAssetListItem *, asset_item, &drag->asset_items) { if (asset_item->is_external) { @@ -277,15 +304,35 @@ static wmDropBox *dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) { + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } + drag->disabled_info = NULL; + LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) { wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base; if (handler->dropboxes) { LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) { - if (drop->poll(C, drag, event) && - WM_operator_poll_context(C, drop->ot, drop->opcontext)) { + if (!drop->poll(C, drag, event)) { + /* If the drop's poll fails, don't set the disabled-info. This would be too aggressive. + * Instead show it only if the drop box could be used in principle, but the operator + * can't be executed. */ + continue; + } + + if (WM_operator_poll_context(C, drop->ot, drop->opcontext)) { return drop; } + + /* Attempt to set the disabled hint when the poll fails. Will always be the last hint set + * when there are multiple failing polls (could allow multiple disabled-hints too). */ + bool free_disabled_info = false; + const char *disabled_hint = CTX_wm_operator_poll_msg_get(C, &free_disabled_info); + if (disabled_hint) { + drag->disabled_info = disabled_hint; + drag->free_disabled_info = free_disabled_info; + } } } } @@ -309,7 +356,10 @@ static wmDropBox *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *ev return drop; } -static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *event) +/** + * Update dropping information for the current mouse position in \a event. + */ +static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); const int winsize_x = WM_window_pixels_x(win); @@ -335,13 +385,36 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e } } +void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop) +{ + /* Optionally copy drag information to operator properties. Don't call it if the + * operator fails anyway, it might do more than just set properties (e.g. + * typically import an asset). */ + if (drop->copy && WM_operator_poll_context(C, drop->ot, drop->opcontext)) { + drop->copy(drag, drop); + } + + wm_drags_exit(CTX_wm_manager(C), CTX_wm_window(C)); +} + /* called in inner handler loop, region context */ void wm_drags_check_ops(bContext *C, const wmEvent *event) { wmWindowManager *wm = CTX_wm_manager(C); + bool any_active = false; LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) { - wm_drop_operator_options(C, drag, event); + wm_drop_update_active(C, drag, event); + + if (drag->active_dropbox) { + any_active = true; + } + } + + /* Change the cursor to display that dropping isn't possible here. But only if there is something + * being dragged actually. Cursor will be restored in #wm_drags_exit(). */ + if (!BLI_listbase_is_empty(&wm->drags)) { + WM_cursor_modal_set(CTX_wm_window(C), any_active ? WM_CURSOR_DEFAULT : WM_CURSOR_STOP); } } @@ -622,6 +695,17 @@ static void wm_drop_operator_draw(const char *name, int x, int y) UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg); } +static void wm_drop_redalert_draw(const char *redalert_str, int x, int y) +{ + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + float col_fg[4]; + + UI_GetThemeColor4fv(TH_REDALERT, col_fg); + + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, redalert_str, col_fg, col_bg); +} + const char *WM_drag_get_item_name(wmDrag *drag) { switch (drag->type) { @@ -721,35 +805,42 @@ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const free_tooltip = true; } - if (tooltip) { - const int winsize_y = WM_window_pixels_y(win); - int x, y; - if (drag->imb) { - x = xy[0] - drag->sx / 2; + if (!tooltip && !drag->disabled_info) { + return; + } - if (xy[1] + drag->sy / 2 + padding + iconsize < winsize_y) { - y = xy[1] + drag->sy / 2 + padding; - } - else { - y = xy[1] - drag->sy / 2 - padding - iconsize - padding - iconsize; - } + const int winsize_y = WM_window_pixels_y(win); + int x, y; + if (drag->imb) { + x = xy[0] - drag->sx / 2; + + if (xy[1] + drag->sy / 2 + padding + iconsize < winsize_y) { + y = xy[1] + drag->sy / 2 + padding; } else { - x = xy[0] - 2 * padding; - - if (xy[1] + iconsize + iconsize < winsize_y) { - y = (xy[1] + iconsize) + padding; - } - else { - y = (xy[1] - iconsize) - padding; - } + y = xy[1] - drag->sy / 2 - padding - iconsize - padding - iconsize; } + } + else { + x = xy[0] - 2 * padding; + if (xy[1] + iconsize + iconsize < winsize_y) { + y = (xy[1] + iconsize) + padding; + } + else { + y = (xy[1] - iconsize) - padding; + } + } + + if (tooltip) { wm_drop_operator_draw(tooltip, x, y); if (free_tooltip) { MEM_freeN((void *)tooltip); } } + else if (drag->disabled_info) { + wm_drop_redalert_draw(drag->disabled_info, x, y); + } } static void wm_drag_draw_default(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index df976d9a4cd..2aa9a4d8676 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3055,12 +3055,7 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis ListBase *lb = (ListBase *)event->customdata; LISTBASE_FOREACH_MUTABLE (wmDrag *, drag, lb) { if (drop->poll(C, drag, event)) { - /* Optionally copy drag information to operator properties. Don't call it if the - * operator fails anyway, it might do more than just set properties (e.g. - * typically import an asset). */ - if (drop->copy && WM_operator_poll_context(C, drop->ot, drop->opcontext)) { - drop->copy(drag, drop); - } + wm_drop_prepare(C, drag, drop); /* Pass single matched wmDrag onto the operator. */ BLI_remlink(lb, drag); @@ -3094,6 +3089,8 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis break; } } + /* Always exit all drags on a drop event, even if poll didn't succeed. */ + wm_drags_exit(wm, win); } } } @@ -3390,6 +3387,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv screen->do_draw_drag = true; } else if (event->type == EVT_ESCKEY) { + wm_drags_exit(wm, win); WM_drag_free_list(&wm->drags); screen->do_draw_drag = true; diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index dfc8c578420..40e4d905fcd 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -172,6 +172,8 @@ void wm_tablet_data_from_ghost(const struct GHOST_TabletData *tablet_data, wmTab /* wm_dropbox.c */ void wm_dropbox_free(void); +void wm_drags_exit(wmWindowManager *wm, wmWindow *win); +void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop); void wm_drags_check_ops(bContext *C, const wmEvent *event); void wm_drags_draw(bContext *C, wmWindow *win); From 7979dff9dc7985cebb530c7490dc730d9c1acf1d Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 18:06:10 +0200 Subject: [PATCH 1219/1500] UI: Let object drop operator display hint why it's disabled When dragging an object in non-object mode into a 3D View, there will now be red text explaining that this is only possible in object mode. The previous commit enabled this. --- source/blender/editors/include/ED_screen.h | 1 + source/blender/editors/object/object_add.c | 2 +- source/blender/editors/screen/screen_ops.c | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index b90c7f27c57..08b6c02a8d0 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -317,6 +317,7 @@ bool ED_operator_regionactive(struct bContext *C); bool ED_operator_scene(struct bContext *C); bool ED_operator_scene_editable(struct bContext *C); bool ED_operator_objectmode(struct bContext *C); +bool ED_operator_objectmode_poll_msg(struct bContext *C); bool ED_operator_view3d_active(struct bContext *C); bool ED_operator_region_view3d_active(struct bContext *C); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 58767d5f50f..917df0da868 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -3591,7 +3591,7 @@ void OBJECT_OT_add_named(wmOperatorType *ot) /* api callbacks */ ot->invoke = object_add_drop_xy_generic_invoke; ot->exec = object_add_named_exec; - ot->poll = ED_operator_objectmode; + ot->poll = ED_operator_objectmode_poll_msg; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 5acfc5ac681..fc1b0ed173e 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -219,6 +219,20 @@ bool ED_operator_objectmode(bContext *C) return true; } +/** + * Same as #ED_operator_objectmode() but additionally sets a "disabled hint". That is, a message + * to be displayed to the user explaining why the operator can't be used in current context. + */ +bool ED_operator_objectmode_poll_msg(bContext *C) +{ + if (!ED_operator_objectmode(C)) { + CTX_wm_operator_poll_msg_set(C, "Only supported in object mode"); + return false; + } + + return true; +} + static bool ed_spacetype_test(bContext *C, int type) { if (ED_operator_areaactive(C)) { From 8ddfdfd2b2a52f746312246aa3099ab0df8544a0 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 26 Oct 2021 11:25:44 -0500 Subject: [PATCH 1220/1500] Geometry Nodes: Handle multiple grids in the volume to mesh node In future use cases, a volume can contain many grids that represent the density information. In this case, it's better if the volume to mesh node creates a mesh based on all of the grids in the volume. This is also a benefit to share-ability, since one doesn't have to specify the grid name in the node. Instead, in the future we can have a way to split particular grids into separate volumes, if only one grid should be considered. The code changes are relatively simple: - Move the old volume to mesh node to the legacy folder. - Run the volume to mesh node on all instance geometry, like elsewhere. - Make the blenkernel's volume to mesh API a bit more specific. Differential Revision: https://developer.blender.org/D12997 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 3 +- source/blender/blenkernel/BKE_volume.h | 5 + .../blender/blenkernel/BKE_volume_to_mesh.hh | 32 ++++ source/blender/blenkernel/intern/node.cc | 1 + source/blender/blenkernel/intern/volume.cc | 17 ++ .../blenkernel/intern/volume_to_mesh.cc | 93 +++++++--- .../blenloader/intern/versioning_290.c | 2 +- .../blenloader/intern/versioning_300.c | 3 +- source/blender/nodes/CMakeLists.txt | 2 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/legacy/node_geo_volume_to_mesh.cc | 173 ++++++++++++++++++ .../geometry/nodes/node_geo_volume_to_mesh.cc | 127 ++++++++----- 14 files changed, 384 insertions(+), 77 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 546f8c9e2ab..22fae8111fd 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -760,6 +760,7 @@ geometry_node_categories = [ ]), GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyVolumeToMesh", poll=geometry_nodes_legacy_poll), NodeItem("GeometryNodeVolumeToMesh"), ]), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index c868eb414f7..8daa96164ef 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1442,7 +1442,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_COLLECTION_INFO 1023 #define GEO_NODE_IS_VIEWPORT 1024 #define GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY 1025 -#define GEO_NODE_VOLUME_TO_MESH 1026 +#define GEO_NODE_LEGACY_VOLUME_TO_MESH 1026 #define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027 #define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028 #define GEO_NODE_SUBDIVIDE_MESH 1029 @@ -1548,6 +1548,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_TO_POINTS 1130 #define GEO_NODE_INSTANCES_TO_POINTS 1131 #define GEO_NODE_IMAGE_TEXTURE 1132 +#define GEO_NODE_VOLUME_TO_MESH 1133 /** \} */ diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h index 5fe0d54c2cf..601e0cf26a9 100644 --- a/source/blender/blenkernel/BKE_volume.h +++ b/source/blender/blenkernel/BKE_volume.h @@ -160,6 +160,7 @@ bool BKE_volume_save(const struct Volume *volume, #ifdef __cplusplus # include "BLI_float3.hh" # include "BLI_float4x4.hh" +# include "BLI_string_ref.hh" bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::float3 &r_max); @@ -167,6 +168,10 @@ bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::f # include # include +VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume, + blender::StringRef name, + openvdb::GridBase::Ptr vdb_grid); + bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid, blender::float3 &r_min, blender::float3 &r_max); diff --git a/source/blender/blenkernel/BKE_volume_to_mesh.hh b/source/blender/blenkernel/BKE_volume_to_mesh.hh index 1f6e89636c4..9532da8c23c 100644 --- a/source/blender/blenkernel/BKE_volume_to_mesh.hh +++ b/source/blender/blenkernel/BKE_volume_to_mesh.hh @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_span.hh" + #include "DNA_modifier_types.h" #ifdef WITH_OPENVDB @@ -33,10 +35,40 @@ struct VolumeToMeshResolution { }; #ifdef WITH_OPENVDB + +/** + * The result of converting a volume grid to mesh data, in the format used by the OpenVDB API. + */ +struct OpenVDBMeshData { + std::vector verts; + std::vector tris; + std::vector quads; + bool is_empty() const + { + return verts.empty(); + } +}; + struct Mesh *volume_to_mesh(const openvdb::GridBase &grid, const VolumeToMeshResolution &resolution, const float threshold, const float adaptivity); + +struct OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid, + const VolumeToMeshResolution &resolution, + const float threshold, + const float adaptivity); + +void fill_mesh_from_openvdb_data(const Span vdb_verts, + const Span vdb_tris, + const Span vdb_quads, + const int vert_offset, + const int poly_offset, + const int loop_offset, + MutableSpan verts, + MutableSpan polys, + MutableSpan loops); + #endif } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index fb2110d7e53..8494c51a66e 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5753,6 +5753,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_select_by_handle_type(); register_node_type_geo_legacy_select_by_material(); register_node_type_geo_legacy_subdivision_surface(); + register_node_type_geo_legacy_volume_to_mesh(); register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 7e7a40d8e9b..a72b5268e1d 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -36,6 +36,7 @@ #include "BLI_math.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_ref.hh" #include "BLI_task.hh" #include "BLI_utildefines.h" @@ -71,6 +72,7 @@ static CLG_LogRef LOG = {"bke.volume"}; using blender::float3; using blender::float4x4; using blender::IndexRange; +using blender::StringRef; #ifdef WITH_OPENVDB # include @@ -1451,6 +1453,21 @@ VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType #endif } +#ifdef WITH_OPENVDB +VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume, + const StringRef name, + openvdb::GridBase::Ptr vdb_grid) +{ + VolumeGridVector &grids = *volume.runtime.grids; + BLI_assert(BKE_volume_grid_find_for_read(&volume, name.data()) == nullptr); + BLI_assert(BKE_volume_grid_type_openvdb(*vdb_grid) != VOLUME_GRID_UNKNOWN); + + vdb_grid->setName(name); + grids.emplace_back(vdb_grid); + return &grids.back(); +} +#endif + void BKE_volume_grid_remove(Volume *volume, VolumeGrid *grid) { #ifdef WITH_OPENVDB diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc index e9d6eea4614..6e465b2fdf0 100644 --- a/source/blender/blenkernel/intern/volume_to_mesh.cc +++ b/source/blender/blenkernel/intern/volume_to_mesh.cc @@ -121,46 +121,66 @@ struct VolumeToMeshOp { } }; -static Mesh *new_mesh_from_openvdb_data(Span verts, - Span tris, - Span quads) +/** + * Convert mesh data from the format provided by OpenVDB into Blender's #Mesh data structure. + * This can be used to add mesh data from a grid into an existing mesh rather than merging multiple + * meshes later on. + */ +void fill_mesh_from_openvdb_data(const Span vdb_verts, + const Span vdb_tris, + const Span vdb_quads, + const int vert_offset, + const int poly_offset, + const int loop_offset, + MutableSpan verts, + MutableSpan polys, + MutableSpan loops) { - const int tot_loops = 3 * tris.size() + 4 * quads.size(); - const int tot_polys = tris.size() + quads.size(); - - Mesh *mesh = BKE_mesh_new_nomain(verts.size(), 0, 0, tot_loops, tot_polys); - /* Write vertices. */ - for (const int i : verts.index_range()) { - const blender::float3 co = blender::float3(verts[i].asV()); - copy_v3_v3(mesh->mvert[i].co, co); + for (const int i : vdb_verts.index_range()) { + const blender::float3 co = blender::float3(vdb_verts[i].asV()); + copy_v3_v3(verts[vert_offset + i].co, co); } /* Write triangles. */ - for (const int i : tris.index_range()) { - mesh->mpoly[i].loopstart = 3 * i; - mesh->mpoly[i].totloop = 3; + for (const int i : vdb_tris.index_range()) { + polys[poly_offset + i].loopstart = loop_offset + 3 * i; + polys[poly_offset + i].totloop = 3; for (int j = 0; j < 3; j++) { /* Reverse vertex order to get correct normals. */ - mesh->mloop[3 * i + j].v = tris[i][2 - j]; + loops[loop_offset + 3 * i + j].v = vert_offset + vdb_tris[i][2 - j]; } } /* Write quads. */ - const int poly_offset = tris.size(); - const int loop_offset = tris.size() * 3; - for (const int i : quads.index_range()) { - mesh->mpoly[poly_offset + i].loopstart = loop_offset + 4 * i; - mesh->mpoly[poly_offset + i].totloop = 4; + const int quad_offset = poly_offset + vdb_tris.size(); + const int quad_loop_offset = loop_offset + vdb_tris.size() * 3; + for (const int i : vdb_quads.index_range()) { + polys[quad_offset + i].loopstart = quad_loop_offset + 4 * i; + polys[quad_offset + i].totloop = 4; for (int j = 0; j < 4; j++) { /* Reverse vertex order to get correct normals. */ - mesh->mloop[loop_offset + 4 * i + j].v = quads[i][3 - j]; + loops[quad_loop_offset + 4 * i + j].v = vert_offset + vdb_quads[i][3 - j]; } } +} - BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_normals_tag_dirty(mesh); - return mesh; +/** + * Convert an OpenVDB volume grid to corresponding mesh data: vertex positions and quad and + * triangle indices. + */ +bke::OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid, + const VolumeToMeshResolution &resolution, + const float threshold, + const float adaptivity) +{ + const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid); + + VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity}; + if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) { + return {}; + } + return {std::move(to_mesh_op.verts), std::move(to_mesh_op.tris), std::move(to_mesh_op.quads)}; } Mesh *volume_to_mesh(const openvdb::GridBase &grid, @@ -168,14 +188,27 @@ Mesh *volume_to_mesh(const openvdb::GridBase &grid, const float threshold, const float adaptivity) { - const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid); + const bke::OpenVDBMeshData mesh_data = volume_to_mesh_data( + grid, resolution, threshold, adaptivity); - VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity}; - if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) { - return nullptr; - } + const int tot_loops = 3 * mesh_data.tris.size() + 4 * mesh_data.quads.size(); + const int tot_polys = mesh_data.tris.size() + mesh_data.quads.size(); + Mesh *mesh = BKE_mesh_new_nomain(mesh_data.verts.size(), 0, 0, tot_loops, tot_polys); - return new_mesh_from_openvdb_data(to_mesh_op.verts, to_mesh_op.tris, to_mesh_op.quads); + fill_mesh_from_openvdb_data(mesh_data.verts, + mesh_data.tris, + mesh_data.quads, + 0, + 0, + 0, + {mesh->mvert, mesh->totvert}, + {mesh->mpoly, mesh->totpoly}, + {mesh->mloop, mesh->totloop}); + + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_normals_tag_dirty(mesh); + + return mesh; } #endif /* WITH_OPENVDB */ diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index d2c722f8be7..def14768ec6 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1985,7 +1985,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_VOLUME_TO_MESH, "Grid", "Density"); + version_node_socket_name(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Grid", "Density"); } } FOREACH_NODETREE_END; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 57447db8723..d0d6c1471b7 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -967,7 +967,7 @@ static bool geometry_node_is_293_legacy(const short node_type) /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to * generate a mesh for all grids in the volume. */ - case GEO_NODE_VOLUME_TO_MESH: + case GEO_NODE_LEGACY_VOLUME_TO_MESH: return false; /* Legacy: Transferred *all* attributes before, will not transfer all built-ins now. */ @@ -2098,6 +2098,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString"); version_geometry_nodes_set_position_node_offset(ntree); + version_node_id(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "GeometryNodeLegacyVolumeToMesh"); } /* Keep this block, even when empty. */ diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 657d0d11441..acf0ab9b224 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -193,6 +193,8 @@ set(SRC geometry/nodes/legacy/node_geo_raycast.cc geometry/nodes/legacy/node_geo_select_by_material.cc geometry/nodes/legacy/node_geo_subdivision_surface.cc + geometry/nodes/legacy/node_geo_volume_to_mesh.cc + geometry/nodes/node_geo_attribute_capture.cc geometry/nodes/node_geo_attribute_remove.cc geometry/nodes/node_geo_attribute_statistic.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 6702443e20e..d6f0b511861 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -47,6 +47,7 @@ void register_node_type_geo_legacy_curve_subdivide(void); void register_node_type_geo_legacy_edge_split(void); void register_node_type_geo_legacy_subdivision_surface(void); void register_node_type_geo_legacy_raycast(void); +void register_node_type_geo_legacy_volume_to_mesh(void); void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_capture(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 7fd4840489e..af9685fcf0d 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -321,6 +321,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_legacy_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "") DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc new file mode 100644 index 00000000000..45f55dcf92e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc @@ -0,0 +1,173 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DEG_depsgraph_query.h" +#ifdef WITH_OPENVDB +# include +# include +#endif + +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_volume.h" +#include "BKE_volume_to_mesh.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Density"); + b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); + b.add_input("Threshold").default_value(0.1f).min(0.0f); + b.add_input("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output("Geometry"); +} + +static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); +} + +static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( + sizeof(NodeGeometryVolumeToMesh), __func__); + data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; + + bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); + bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; + STRNCPY(grid_socket_value->value, "density"); + + node->storage = data; +} + +static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage; + + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(voxel_amount_socket, + data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT); + nodeSetSocketAvailability(voxel_size_socket, + data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE); +} + +#ifdef WITH_OPENVDB + +static void create_mesh_from_volume(GeometrySet &geometry_set_in, + GeometrySet &geometry_set_out, + GeoNodeExecParams ¶ms) +{ + if (!geometry_set_in.has()) { + return; + } + + const NodeGeometryVolumeToMesh &storage = + *(const NodeGeometryVolumeToMesh *)params.node().storage; + + bke::VolumeToMeshResolution resolution; + resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; + if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { + resolution.settings.voxel_amount = params.get_input("Voxel Amount"); + if (resolution.settings.voxel_amount <= 0.0f) { + return; + } + } + else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { + resolution.settings.voxel_size = params.get_input("Voxel Size"); + if (resolution.settings.voxel_size <= 0.0f) { + return; + } + } + + const VolumeComponent *component = geometry_set_in.get_component_for_read(); + const Volume *volume = component->get_for_read(); + if (volume == nullptr) { + return; + } + + const Main *bmain = DEG_get_bmain(params.depsgraph()); + BKE_volume_load(volume, bmain); + + const std::string grid_name = params.get_input("Density"); + const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); + if (volume_grid == nullptr) { + return; + } + + float threshold = params.get_input("Threshold"); + float adaptivity = params.get_input("Adaptivity"); + + const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); + if (mesh == nullptr) { + return; + } + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MeshComponent &dst_component = geometry_set_out.get_component_for_write(); + dst_component.replace(mesh); +} + +#endif /* WITH_OPENVDB */ + +static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set_in = params.extract_input("Geometry"); + GeometrySet geometry_set_out; + +#ifdef WITH_OPENVDB + create_mesh_from_volume(geometry_set_in, geometry_set_out, params); +#else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); +#endif + + params.set_output("Geometry", geometry_set_out); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_volume_to_mesh() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare; + node_type_storage( + &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); + node_type_size(&ntype, 170, 120, 700); + node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init); + node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update); + ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec; + ntype.draw_buttons = blender::nodes::geo_node_volume_to_mesh_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index d1fb22f4ba2..c0084de367f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -39,13 +39,12 @@ namespace blender::nodes { static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Density"); + b.add_input("Volume"); b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); b.add_input("Threshold").default_value(0.1f).min(0.0f); b.add_input("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output("Geometry"); + b.add_output("Mesh"); } static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -60,11 +59,6 @@ static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( sizeof(NodeGeometryVolumeToMesh), __func__); data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; - - bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); - bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; - STRNCPY(grid_socket_value->value, "density"); - node->storage = data; } @@ -82,75 +76,120 @@ static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node #ifdef WITH_OPENVDB -static void create_mesh_from_volume(GeometrySet &geometry_set_in, - GeometrySet &geometry_set_out, - GeoNodeExecParams ¶ms) +static bke::VolumeToMeshResolution get_resolution_param(const GeoNodeExecParams ¶ms) { - if (!geometry_set_in.has()) { - return; - } - const NodeGeometryVolumeToMesh &storage = *(const NodeGeometryVolumeToMesh *)params.node().storage; bke::VolumeToMeshResolution resolution; resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { - resolution.settings.voxel_amount = params.get_input("Voxel Amount"); - if (resolution.settings.voxel_amount <= 0.0f) { - return; - } + resolution.settings.voxel_amount = std::max(params.get_input("Voxel Amount"), 0.0f); } else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { - resolution.settings.voxel_size = params.get_input("Voxel Size"); - if (resolution.settings.voxel_size <= 0.0f) { - return; - } + resolution.settings.voxel_size = std::max(params.get_input("Voxel Size"), 0.0f); } - const VolumeComponent *component = geometry_set_in.get_component_for_read(); - const Volume *volume = component->get_for_read(); + return resolution; +} + +static Mesh *create_mesh_from_volume_grids(Span grids, + const float threshold, + const float adaptivity, + const bke::VolumeToMeshResolution &resolution) +{ + Array mesh_data(grids.size()); + for (const int i : grids.index_range()) { + mesh_data[i] = bke::volume_to_mesh_data(*grids[i], resolution, threshold, adaptivity); + } + + int vert_offset = 0; + int poly_offset = 0; + int loop_offset = 0; + Array vert_offsets(mesh_data.size()); + Array poly_offsets(mesh_data.size()); + Array loop_offsets(mesh_data.size()); + for (const int i : grids.index_range()) { + const bke::OpenVDBMeshData &data = mesh_data[i]; + vert_offsets[i] = vert_offset; + poly_offsets[i] = poly_offset; + loop_offsets[i] = loop_offset; + vert_offset += data.verts.size(); + poly_offset += (data.tris.size() + data.quads.size()); + loop_offset += (3 * data.tris.size() + 4 * data.quads.size()); + } + + Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MutableSpan verts{mesh->mvert, mesh->totvert}; + MutableSpan loops{mesh->mloop, mesh->totloop}; + MutableSpan polys{mesh->mpoly, mesh->totpoly}; + + for (const int i : grids.index_range()) { + const bke::OpenVDBMeshData &data = mesh_data[i]; + bke::fill_mesh_from_openvdb_data(data.verts, + data.tris, + data.quads, + vert_offsets[i], + poly_offsets[i], + loop_offsets[i], + verts, + polys, + loops); + } + + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_normals_tag_dirty(mesh); + + return mesh; +} + +static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParams ¶ms) +{ + const Volume *volume = geometry_set.get_volume_for_read(); if (volume == nullptr) { - return; + return nullptr; } + const bke::VolumeToMeshResolution resolution = get_resolution_param(params); const Main *bmain = DEG_get_bmain(params.depsgraph()); BKE_volume_load(volume, bmain); - const std::string grid_name = params.get_input("Density"); - const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); - if (volume_grid == nullptr) { - return; + Vector grids; + for (const int i : IndexRange(BKE_volume_num_grids(volume))) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + grids.append(std::move(grid)); } - float threshold = params.get_input("Threshold"); - float adaptivity = params.get_input("Adaptivity"); - - const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); - Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); - if (mesh == nullptr) { - return; + if (grids.is_empty()) { + return nullptr; } - BKE_id_material_eval_ensure_default_slot(&mesh->id); - MeshComponent &dst_component = geometry_set_out.get_component_for_write(); - dst_component.replace(mesh); + + return create_mesh_from_volume_grids(grids, + params.get_input("Threshold"), + params.get_input("Adaptivity"), + resolution); } #endif /* WITH_OPENVDB */ static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) { - GeometrySet geometry_set_in = params.extract_input("Geometry"); - GeometrySet geometry_set_out; + GeometrySet geometry_set = params.extract_input("Volume"); #ifdef WITH_OPENVDB - create_mesh_from_volume(geometry_set_in, geometry_set_out, params); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + Mesh *mesh = create_mesh_from_volume(geometry_set, params); + geometry_set.replace_mesh(mesh); + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + }); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); #endif - params.set_output("Geometry", geometry_set_out); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes From 03013d19d16704672f9db93bc62547651b6a5cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Tue, 26 Oct 2021 18:16:22 +0200 Subject: [PATCH 1221/1500] Eevee: support accessing custom mesh attributes This adds generic attribute rendering support for meshes for Eevee and Workbench. Each attribute is stored inside of the `MeshBufferList` as a separate VBO, with a maximum of `GPU_MAX_ATTR` VBOs for consistency with the GPU shader compilation code. Since `DRW_MeshCDMask` is not general enough, attribute requests are stored in new `DRW_AttributeRequest` structures inside of a convenient `DRW_MeshAttributes` structure. The latter is used in a similar manner as `DRW_MeshCDMask`, with the `MeshBatchCache` keeping track of needed, used, and used-over-time attributes. Again, `GPU_MAX_ATTR` is used in `DRW_MeshAttributes` to prevent too many attributes being used. To ensure thread-safety when updating the used attributes list, a mutex is added to the Mesh runtime. This mutex will also be used in the future for other things when other part of the rendre pre-processing are multi-threaded. `GPU_BATCH_VBO_MAX_LEN` was increased to 16 in order to accommodate for this design. Since `CD_PROP_COLOR` are a valid attribute type, sculpt vertex colors are now handled using this system to avoid to complicate things. In the future regular vertex colors will also use this. From this change, bit operations for DRW_MeshCDMask are now using uint32_t (to match the representation now used by the compiler). Due to the difference in behavior for implicit type conversion for scalar types between OpenGL and what users expect (a scalar `s` is converted to `vec4(s, 0, 0, 1)` by OpenGL, vs. `vec4(s, s, s, 1)` in Blender's various node graphs) , all scalar types are using a float3 internally for now, which increases memory usage. This will be resolved during or after the EEVEE rewrite as properly handling this involves much deeper changes. Ref T85075 Reviewed By: fclem Maniphest Tasks: T85075 Differential Revision: https://developer.blender.org/D12969 --- .../blender/blenkernel/intern/mesh_runtime.c | 10 + source/blender/draw/CMakeLists.txt | 1 + .../blender/draw/intern/draw_cache_extract.h | 23 +- .../draw/intern/draw_cache_extract_mesh.cc | 3 + .../draw/intern/draw_cache_impl_mesh.c | 352 +++++++++++++--- .../intern/mesh_extractors/extract_mesh.h | 1 + .../extract_mesh_vbo_attributes.cc | 398 ++++++++++++++++++ .../mesh_extractors/extract_mesh_vbo_vcol.cc | 64 +-- source/blender/gpu/GPU_batch.h | 8 +- source/blender/makesdna/DNA_mesh_types.h | 4 + 10 files changed, 741 insertions(+), 123 deletions(-) create mode 100644 source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c index 7ac4c29f0ee..1c8646a4bdd 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.c +++ b/source/blender/blenkernel/intern/mesh_runtime.c @@ -52,6 +52,8 @@ void BKE_mesh_runtime_reset(Mesh *mesh) memset(&mesh->runtime, 0, sizeof(mesh->runtime)); mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); BLI_mutex_init(mesh->runtime.eval_mutex); + mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); + BLI_mutex_init(mesh->runtime.render_mutex); } /* Clear all pointers which we don't want to be shared on copying the datablock. @@ -71,6 +73,9 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag)) mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); BLI_mutex_init(mesh->runtime.eval_mutex); + + mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); + BLI_mutex_init(mesh->runtime.render_mutex); } void BKE_mesh_runtime_clear_cache(Mesh *mesh) @@ -80,6 +85,11 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh) MEM_freeN(mesh->runtime.eval_mutex); mesh->runtime.eval_mutex = NULL; } + if (mesh->runtime.render_mutex != NULL) { + BLI_mutex_end(mesh->runtime.render_mutex); + MEM_freeN(mesh->runtime.render_mutex); + mesh->runtime.render_mutex = NULL; + } if (mesh->runtime.mesh_eval != NULL) { mesh->runtime.mesh_eval->edit_mesh = NULL; BKE_id_free(NULL, mesh->runtime.mesh_eval); diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index baf83234354..0baf994d978 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -61,6 +61,7 @@ set(SRC intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc intern/mesh_extractors/extract_mesh_ibo_points.cc intern/mesh_extractors/extract_mesh_ibo_tris.cc + intern/mesh_extractors/extract_mesh_vbo_attributes.cc intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc intern/mesh_extractors/extract_mesh_vbo_edit_data.cc intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index a680cc0d6b7..ba42cdf66e7 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -24,6 +24,10 @@ struct TaskGraph; +#include "DNA_customdata_types.h" + +#include "BKE_attribute.h" + #include "GPU_batch.h" #include "GPU_index_buffer.h" #include "GPU_vertex_buffer.h" @@ -56,7 +60,6 @@ typedef struct DRW_MeshCDMask { uint32_t uv : 8; uint32_t tan : 8; uint32_t vcol : 8; - uint32_t sculpt_vcol : 8; uint32_t orco : 1; uint32_t tan_orco : 1; uint32_t sculpt_overlays : 1; @@ -64,10 +67,10 @@ typedef struct DRW_MeshCDMask { * modifiers could remove it. (see T68857) */ uint32_t edit_uv : 1; } DRW_MeshCDMask; -/* Keep `DRW_MeshCDMask` struct within an `uint64_t`. +/* Keep `DRW_MeshCDMask` struct within an `uint32_t`. * bit-wise and atomic operations are used to compare and update the struct. * See `mesh_cd_layers_type_*` functions. */ -BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits") +BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint32_t), "DRW_MeshCDMask exceeds 32 bits") typedef enum eMRIterType { MR_ITER_LOOPTRI = 1 << 0, MR_ITER_POLY = 1 << 1, @@ -76,6 +79,17 @@ typedef enum eMRIterType { } eMRIterType; ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) +typedef struct DRW_AttributeRequest { + CustomDataType cd_type; + int layer_index; + AttributeDomain domain; +} DRW_AttributeRequest; + +typedef struct DRW_MeshAttributes { + DRW_AttributeRequest requests[GPU_MAX_ATTR]; + int num_requests; +} DRW_MeshAttributes; + typedef enum eMRDataType { MR_DATA_NONE = 0, MR_DATA_POLY_NOR = 1 << 1, @@ -133,6 +147,7 @@ typedef struct MeshBufferList { GPUVertBuf *edge_idx; /* extend */ GPUVertBuf *poly_idx; GPUVertBuf *fdot_idx; + GPUVertBuf *attr[GPU_MAX_ATTR]; } vbo; /* Index Buffers: * Only need to be updated when topology changes. */ @@ -285,6 +300,8 @@ typedef struct MeshBatchCache { DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time; + DRW_MeshAttributes attr_used, attr_needed, attr_used_over_time; + int lastmatch; /* Valid only if edge_detection is up to date. */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 06c449fe590..f3b72503907 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -650,6 +650,9 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, EXTRACT_ADD_REQUESTED(vbo, vert_idx); EXTRACT_ADD_REQUESTED(vbo, fdot_idx); EXTRACT_ADD_REQUESTED(vbo, skin_roots); + for (int i = 0; i < GPU_MAX_ATTR; i++) { + EXTRACT_ADD_REQUESTED(vbo, attr[i]); + } EXTRACT_ADD_REQUESTED(ibo, tris); if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 18664498d00..847913927f7 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -41,6 +41,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" @@ -121,6 +122,8 @@ # define _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5) _MDEPS_ASSERT5(b, n1, n2, n3, n4); _MDEPS_ASSERT2(b, n5) # define _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6) _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5); _MDEPS_ASSERT2(b, n6) # define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7) +# define _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20) _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7); _MDEPS_ASSERT8(b, n8, n9, n10, n11, n12, n13, n14); _MDEPS_ASSERT7(b, n15, n16, n17, n18, n19, n20) +# define _MDEPS_ASSERT22(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20, n21) _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20); _MDEPS_ASSERT2(b, n21); # define MDEPS_ASSERT_FLAG(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__) # define MDEPS_ASSERT(batch_name, ...) MDEPS_ASSERT_FLAG(BATCH_FLAG(batch_name), __VA_ARGS__) @@ -192,6 +195,21 @@ static const DRWBatchFlag g_buffer_deps[] = { [BUFFER_INDEX(vbo.edge_idx)] = BATCH_FLAG(edit_selection_edges), [BUFFER_INDEX(vbo.poly_idx)] = BATCH_FLAG(edit_selection_faces), [BUFFER_INDEX(vbo.fdot_idx)] = BATCH_FLAG(edit_selection_fdots), + [BUFFER_INDEX(vbo.attr[0])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[1])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[2])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[3])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[4])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[5])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[6])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[7])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[8])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[9])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[10])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[11])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[12])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[13])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr[14])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, [BUFFER_INDEX(ibo.tris)] = BATCH_FLAG(surface, surface_weights, @@ -240,12 +258,12 @@ static void mesh_batch_cache_discard_batch(MeshBatchCache *cache, const DRWBatch /* Return true is all layers in _b_ are inside _a_. */ BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return (*((uint64_t *)&a) & *((uint64_t *)&b)) == *((uint64_t *)&b); + return (*((uint32_t *)&a) & *((uint32_t *)&b)) == *((uint32_t *)&b); } BLI_INLINE bool mesh_cd_layers_type_equal(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return *((uint64_t *)&a) == *((uint64_t *)&b); + return *((uint32_t *)&a) == *((uint32_t *)&b); } BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) @@ -253,12 +271,11 @@ BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) uint32_t *a_p = (uint32_t *)a; uint32_t *b_p = (uint32_t *)&b; atomic_fetch_and_or_uint32(a_p, *b_p); - atomic_fetch_and_or_uint32(a_p + 1, *(b_p + 1)); } BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a) { - *((uint64_t *)a) = 0; + *((uint32_t *)a) = 0; } BLI_INLINE const Mesh *editmesh_final_or_this(const Mesh *me) @@ -271,6 +288,95 @@ static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *c cd_used->edit_uv = 1; } +/** \name DRW_MeshAttributes + * + * Utilities for handling requested attributes. + * \{ */ + +/* Return true if the given DRW_AttributeRequest is already in the requests. */ +static bool has_request(const DRW_MeshAttributes *requests, DRW_AttributeRequest req) +{ + for (int i = 0; i < requests->num_requests; i++) { + const DRW_AttributeRequest src_req = requests->requests[i]; + if (src_req.domain != req.domain) { + continue; + } + if (src_req.layer_index != req.layer_index) { + continue; + } + if (src_req.cd_type != req.cd_type) { + continue; + } + return true; + } + return false; +} + +static void mesh_attrs_merge_requests(const DRW_MeshAttributes *src_requests, + DRW_MeshAttributes *dst_requests) +{ + for (int i = 0; i < src_requests->num_requests; i++) { + if (dst_requests->num_requests == GPU_MAX_ATTR) { + return; + } + + if (has_request(dst_requests, src_requests->requests[i])) { + continue; + } + + dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; + dst_requests->num_requests += 1; + } +} + +static void drw_mesh_attributes_clear(DRW_MeshAttributes *attributes) +{ + memset(attributes, 0, sizeof(DRW_MeshAttributes)); +} + +static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst, + const DRW_MeshAttributes *src, + ThreadMutex *mesh_render_mutex) +{ + BLI_mutex_lock(mesh_render_mutex); + mesh_attrs_merge_requests(src, dst); + BLI_mutex_unlock(mesh_render_mutex); +} + +/* Return true if all requests in b are in a. */ +static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b) +{ + if (a->num_requests != b->num_requests) { + return false; + } + + for (int i = 0; i < a->num_requests; i++) { + if (!has_request(a, b->requests[i])) { + return false; + } + } + + return true; +} + +static void drw_mesh_attributes_add_request(DRW_MeshAttributes *attrs, + CustomDataType type, + int layer, + AttributeDomain domain) +{ + if (attrs->num_requests >= GPU_MAX_ATTR) { + return; + } + + DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; + req->cd_type = type; + req->layer_index = layer; + req->domain = domain; + attrs->num_requests += 1; +} + +/** \} */ + BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -286,6 +392,36 @@ BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) return &me->ldata; } +BLI_INLINE const CustomData *mesh_cd_pdata_get_from_mesh(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_MDATA: + return &me->pdata; + break; + case ME_WRAPPER_TYPE_BMESH: + return &me->edit_mesh->bm->pdata; + break; + } + + BLI_assert(0); + return &me->pdata; +} + +BLI_INLINE const CustomData *mesh_cd_edata_get_from_mesh(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_MDATA: + return &me->edata; + break; + case ME_WRAPPER_TYPE_BMESH: + return &me->edit_mesh->bm->edata; + break; + } + + BLI_assert(0); + return &me->edata; +} + BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -321,14 +457,14 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd } } -static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used) +static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshAttributes *attrs_used) { const Mesh *me_final = editmesh_final_or_this(me); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR); if (layer != -1) { - cd_used->sculpt_vcol |= (1 << layer); + drw_mesh_attributes_add_request(attrs_used, CD_PROP_COLOR, layer, ATTR_DOMAIN_POINT); } } @@ -343,13 +479,45 @@ static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *c } } +static bool custom_data_match_attribute(const CustomData *custom_data, + const char *name, + int *r_layer_index, + int *r_type) +{ + const int possible_attribute_types[6] = { + CD_PROP_BOOL, + CD_PROP_INT32, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_COLOR, + }; + + for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { + const int attr_type = possible_attribute_types[i]; + int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); + if (layer_index == -1) { + continue; + } + + *r_layer_index = layer_index; + *r_type = attr_type; + return true; + } + + return false; +} + static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, struct GPUMaterial **gpumat_array, - int gpumat_array_len) + int gpumat_array_len, + DRW_MeshAttributes *attributes) { const Mesh *me_final = editmesh_final_or_this(me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); + const CustomData *cd_pdata = mesh_cd_pdata_get_from_mesh(me_final); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); + const CustomData *cd_edata = mesh_cd_edata_get_from_mesh(me_final); /* See: DM_vertex_attributes_from_gpu for similar logic */ DRW_MeshCDMask cd_used; @@ -363,6 +531,8 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, const char *name = gpu_attr->name; int type = gpu_attr->type; int layer = -1; + /* ATTR_DOMAIN_NUM is standard for "invalid value". */ + AttributeDomain domain = ATTR_DOMAIN_NUM; if (type == CD_AUTO_FROM_NAME) { /* We need to deduct what exact layer is used. @@ -373,13 +543,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name); type = CD_MTFACE; - if (layer == -1) { - if (U.experimental.use_sculpt_vertex_colors) { - layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name); - type = CD_PROP_COLOR; - } - } - if (layer == -1) { layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name); type = CD_MCOL; @@ -391,6 +554,27 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, type = CD_TANGENT; } #endif + if (layer == -1) { + /* Try to match a generic attribute, we use the first attribute domain with a + * matching name. */ + if (custom_data_match_attribute(cd_vdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_POINT; + } + else if (custom_data_match_attribute(cd_ldata, name, &layer, &type)) { + domain = ATTR_DOMAIN_CORNER; + } + else if (custom_data_match_attribute(cd_pdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_FACE; + } + else if (custom_data_match_attribute(cd_edata, name, &layer, &type)) { + domain = ATTR_DOMAIN_EDGE; + } + else { + layer = -1; + domain = ATTR_DOMAIN_NUM; + } + } + if (layer == -1) { continue; } @@ -432,31 +616,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, } break; } - case CD_PROP_COLOR: { - /* Sculpt Vertex Colors */ - bool use_mloop_cols = false; - if (layer == -1) { - layer = (name[0] != '\0') ? - CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name) : - CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR); - /* Fallback to Vertex Color data */ - if (layer == -1) { - layer = (name[0] != '\0') ? - CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name) : - CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL); - use_mloop_cols = true; - } - } - if (layer != -1) { - if (use_mloop_cols) { - cd_used.vcol |= (1 << layer); - } - else { - cd_used.sculpt_vcol |= (1 << layer); - } - } - break; - } case CD_MCOL: { /* Vertex Color Data */ if (layer == -1) { @@ -473,6 +632,17 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, cd_used.orco = 1; break; } + case CD_PROP_BOOL: + case CD_PROP_INT32: + case CD_PROP_FLOAT: + case CD_PROP_FLOAT2: + case CD_PROP_FLOAT3: + case CD_PROP_COLOR: { + if (layer != -1 && domain != ATTR_DOMAIN_NUM) { + drw_mesh_attributes_add_request(attributes, type, layer, domain); + } + break; + } } } } @@ -935,14 +1105,14 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me) static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me) { - DRW_MeshCDMask cd_needed; - mesh_cd_layers_type_clear(&cd_needed); - mesh_cd_calc_active_vcol_layer(me, &cd_needed); + DRW_MeshAttributes attrs_needed; + drw_mesh_attributes_clear(&attrs_needed); + mesh_cd_calc_active_vcol_layer(me, &attrs_needed); - BLI_assert(cd_needed.sculpt_vcol != 0 && + BLI_assert(attrs_needed.num_requests != 0 && "No MPropCol layer available in Sculpt, but batches requested anyway!"); - mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); + drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime.render_mutex); } GPUBatch *DRW_mesh_batch_cache_get_all_verts(Mesh *me) @@ -1015,11 +1185,16 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, uint gpumat_array_len) { MeshBatchCache *cache = mesh_batch_cache_get(me); - DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(me, gpumat_array, gpumat_array_len); + DRW_MeshAttributes attrs_needed; + drw_mesh_attributes_clear(&attrs_needed); + DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers( + me, gpumat_array, gpumat_array_len, &attrs_needed); BLI_assert(gpumat_array_len == cache->mat_len); mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); + ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; + drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->surface_per_mat; } @@ -1296,11 +1471,25 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) cache->lastmatch = ctime; } + if (drw_mesh_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { + cache->lastmatch = ctime; + } + if (ctime - cache->lastmatch > U.vbotimeout) { mesh_batch_cache_discard_shaded_tri(cache); } mesh_cd_layers_type_clear(&cache->cd_used_over_time); + drw_mesh_attributes_clear(&cache->attr_used_over_time); +} + +static void drw_add_attributes_vbo(GPUBatch *batch, + MeshBufferList *mbuflist, + DRW_MeshAttributes *attr_used) +{ + for (int i = 0; i < attr_used->num_requests; i++) { + DRW_vbo_request(batch, &mbuflist->vbo.attr[i]); + } } #ifdef DEBUG @@ -1409,12 +1598,15 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } } + ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; + /* Verify that all surface batches have needed attribute layers. */ /* TODO(fclem): We could be a bit smarter here and only do it per * material. */ bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed); - if (cd_overlap == false) { + bool attr_overlap = drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed); + if (cd_overlap == false || attr_overlap == false) { FOREACH_MESH_BUFFER_CACHE (cache, mbc) { if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv); @@ -1430,11 +1622,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_used.sculpt_overlays != cache->cd_needed.sculpt_overlays) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.sculpt_data); } - if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) || - ((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) != - cache->cd_needed.sculpt_vcol)) { + if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol); } + if (!drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { + for (int i = 0; i < GPU_MAX_ATTR; i++) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]); + } + } } /* We can't discard batches at this point as they have been * referenced for drawing. Just clear them in place. */ @@ -1445,9 +1640,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, cache->batch_ready &= ~(MBC_SURFACE); mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed); + drw_mesh_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); } mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed); mesh_cd_layers_type_clear(&cache->cd_needed); + + drw_mesh_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); + drw_mesh_attributes_clear(&cache->attr_needed); } if (batch_requested & MBC_EDITUV) { @@ -1506,7 +1705,27 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MeshBufferList *mbuflist = &cache->final.buff; /* Initialize batches and request VBO's & IBO's. */ - MDEPS_ASSERT(surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol); + MDEPS_ASSERT(surface, + ibo.tris, + vbo.lnor, + vbo.pos_nor, + vbo.uv, + vbo.vcol, + vbo.attr[0], + vbo.attr[1], + vbo.attr[2], + vbo.attr[3], + vbo.attr[4], + vbo.attr[5], + vbo.attr[6], + vbo.attr[7], + vbo.attr[8], + vbo.attr[9], + vbo.attr[10], + vbo.attr[11], + vbo.attr[12], + vbo.attr[13], + vbo.attr[14]); if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.surface, &mbuflist->ibo.tris); /* Order matters. First ones override latest VBO's attributes. */ @@ -1515,9 +1734,10 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_used.uv != 0) { DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.uv); } - if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { + if (cache->cd_used.vcol != 0) { DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.vcol); } + drw_add_attributes_vbo(cache->batch.surface, mbuflist, &cache->attr_used); } MDEPS_ASSERT(all_verts, vbo.pos_nor); if (DRW_batch_requested(cache->batch.all_verts, GPU_PRIM_POINTS)) { @@ -1580,8 +1800,28 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } /* Per Material */ - MDEPS_ASSERT_FLAG( - SURFACE_PER_MAT_FLAG, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.tan, vbo.vcol, vbo.orco); + MDEPS_ASSERT_FLAG(SURFACE_PER_MAT_FLAG, + vbo.lnor, + vbo.pos_nor, + vbo.uv, + vbo.tan, + vbo.vcol, + vbo.orco, + vbo.attr[0], + vbo.attr[1], + vbo.attr[2], + vbo.attr[3], + vbo.attr[4], + vbo.attr[5], + vbo.attr[6], + vbo.attr[7], + vbo.attr[8], + vbo.attr[9], + vbo.attr[10], + vbo.attr[11], + vbo.attr[12], + vbo.attr[13], + vbo.attr[14]); MDEPS_ASSERT_INDEX(TRIS_PER_MAT_INDEX, SURFACE_PER_MAT_FLAG); for (int i = 0; i < cache->mat_len; i++) { if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) { @@ -1595,12 +1835,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.tan); } - if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { + if (cache->cd_used.vcol != 0) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.vcol); } if (cache->cd_used.orco != 0) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.orco); } + drw_add_attributes_vbo(cache->surface_per_mat[i], mbuflist, &cache->attr_used); } } @@ -1751,6 +1992,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MDEPS_ASSERT_MAP(vbo.edituv_stretch_angle); MDEPS_ASSERT_MAP(vbo.fdots_uv); MDEPS_ASSERT_MAP(vbo.fdots_edituv_data); + for (int i = 0; i < GPU_MAX_ATTR; i++) { + MDEPS_ASSERT_MAP(vbo.attr[i]); + } MDEPS_ASSERT_MAP(ibo.tris); MDEPS_ASSERT_MAP(ibo.lines); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index d9f397fd8b8..d1ffef4fe92 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -328,6 +328,7 @@ extern const MeshExtract extract_poly_idx; extern const MeshExtract extract_edge_idx; extern const MeshExtract extract_vert_idx; extern const MeshExtract extract_fdot_idx; +extern const MeshExtract extract_attr[GPU_MAX_ATTR]; #ifdef __cplusplus } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc new file mode 100644 index 00000000000..f8cc92de1eb --- /dev/null +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -0,0 +1,398 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + */ + +#include "MEM_guardedalloc.h" + +#include + +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_float4.hh" +#include "BLI_string.h" + +#include "BKE_attribute.h" + +#include "extract_mesh.h" + +namespace blender::draw { + +/* ---------------------------------------------------------------------- */ +/** \name Extract Attributes + * \{ */ + +static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, AttributeDomain domain) +{ + switch (domain) { + default: { + return nullptr; + } + case ATTR_DOMAIN_POINT: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + } + case ATTR_DOMAIN_CORNER: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + } + case ATTR_DOMAIN_FACE: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; + } + case ATTR_DOMAIN_EDGE: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->edata : &mr->me->edata; + } + } +} + +/* Utility to convert from the type used in the attributes to the types for the VBO. + * This is mostly used to promote integers and booleans to floats, as other types (float, float2, + * etc.) directly map to avalaible GPU types. Booleans are still converted as attributes are vec4 + * in the shader. + */ +template struct attribute_type_converter { + static VBOType convert_value(AttributeType value) + { + if constexpr (std::is_same_v) { + return value; + } + + /* This should only concern bools which are converted to floats. */ + return static_cast(value); + } +}; + +/* Similar to the one in #extract_mesh_vcol_vbo.cc */ +struct gpuMeshCol { + ushort r, g, b, a; +}; + +template<> struct attribute_type_converter { + static gpuMeshCol convert_value(MPropCol value) + { + gpuMeshCol result; + result.r = unit_float_to_ushort_clamp(value.color[0]); + result.g = unit_float_to_ushort_clamp(value.color[1]); + result.b = unit_float_to_ushort_clamp(value.color[2]); + result.a = unit_float_to_ushort_clamp(value.color[3]); + return result; + } +}; + +/* Return the number of component for the attribute's value type, or 0 if is it unsupported. */ +static uint gpu_component_size_for_attribute_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_BOOL: + case CD_PROP_INT32: + case CD_PROP_FLOAT: { + /* TODO(kevindietrich) : should be 1 when scalar attributes conversion is handled by us. See + * comment #extract_attr_init. */ + return 3; + } + case CD_PROP_FLOAT2: { + return 2; + } + case CD_PROP_FLOAT3: { + return 3; + } + case CD_PROP_COLOR: { + return 4; + } + default: { + return 0; + } + } +} + +static GPUVertFetchMode get_fetch_mode_for_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_INT32: { + return GPU_FETCH_INT_TO_FLOAT; + } + case CD_PROP_COLOR: { + return GPU_FETCH_INT_TO_FLOAT_UNIT; + } + default: { + return GPU_FETCH_FLOAT; + } + } +} + +static GPUVertCompType get_comp_type_for_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_INT32: { + return GPU_COMP_I32; + } + case CD_PROP_COLOR: { + return GPU_COMP_U16; + } + default: { + return GPU_COMP_F32; + } + } +} + +static void init_vbo_for_attribute(const MeshRenderData *mr, + GPUVertBuf *vbo, + const DRW_AttributeRequest &request) +{ + GPUVertCompType comp_type = get_comp_type_for_type(request.cd_type); + GPUVertFetchMode fetch_mode = get_fetch_mode_for_type(request.cd_type); + const uint comp_size = gpu_component_size_for_attribute_type(request.cd_type); + /* We should not be here if the attribute type is not supported. */ + BLI_assert(comp_size != 0); + + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name( + custom_data, request.cd_type, request.layer_index); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* Attributes use auto-name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + GPU_vertformat_attr_add(&format, attr_name, comp_type, comp_size, fetch_mode); + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, static_cast(mr->loop_len)); +} + +template +static void fill_vertbuf_with_attribute(const MeshRenderData *mr, + VBOType *vbo_data, + const DRW_AttributeRequest &request) +{ + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + BLI_assert(custom_data); + const int layer_index = request.layer_index; + + const MPoly *mpoly = mr->mpoly; + const MLoop *mloop = mr->mloop; + + const AttributeType *attr_data = static_cast( + CustomData_get_layer_n(custom_data, request.cd_type, layer_index)); + + using converter = attribute_type_converter; + + switch (request.domain) { + default: { + BLI_assert(false); + break; + } + case ATTR_DOMAIN_POINT: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { + *vbo_data = converter::convert_value(attr_data[mloop->v]); + } + break; + } + case ATTR_DOMAIN_CORNER: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++) { + *vbo_data = converter::convert_value(attr_data[ml_index]); + } + break; + } + case ATTR_DOMAIN_EDGE: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { + *vbo_data = converter::convert_value(attr_data[mloop->e]); + } + break; + } + case ATTR_DOMAIN_FACE: { + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { + const MPoly &poly = mpoly[mp_index]; + const VBOType value = converter::convert_value(attr_data[mp_index]); + for (int l = 0; l < poly.totloop; l++) { + *vbo_data++ = value; + } + } + break; + } + } +} + +template +static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr, + VBOType *&vbo_data, + const DRW_AttributeRequest &request) +{ + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + BLI_assert(custom_data); + const int layer_index = request.layer_index; + + int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index); + + using converter = attribute_type_converter; + + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const AttributeType *attr_data = nullptr; + if (request.domain == ATTR_DOMAIN_POINT) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_CORNER) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_FACE) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(efa, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_EDGE) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter->e, cd_ofs)); + } + else { + BLI_assert(false); + continue; + } + *vbo_data = converter::convert_value(*attr_data); + vbo_data++; + } while ((l_iter = l_iter->next) != l_first); + } +} + +template +static void extract_attr_generic(const MeshRenderData *mr, + GPUVertBuf *vbo, + const DRW_AttributeRequest &request) +{ + VBOType *vbo_data = static_cast(GPU_vertbuf_get_data(vbo)); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + fill_vertbuf_with_attribute_bm(mr, vbo_data, request); + } + else { + fill_vertbuf_with_attribute(mr, vbo_data, request); + } +} + +static void extract_attr_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(tls_data), + int index) +{ + const DRW_MeshAttributes *attrs_used = &cache->attr_used; + const DRW_AttributeRequest &request = attrs_used->requests[index]; + + GPUVertBuf *vbo = static_cast(buf); + + init_vbo_for_attribute(mr, vbo, request); + + /* TODO(kevindietrich) : float3 is used for scalar attributes as the implicit conversion done by + * OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following the + * Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar + * texture as for volume attribute, so we can control the conversion ourselves. */ + switch (request.cd_type) { + case CD_PROP_BOOL: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_INT32: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_FLOAT: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_FLOAT2: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_FLOAT3: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_COLOR: { + extract_attr_generic(mr, vbo, request); + break; + } + default: { + BLI_assert(false); + } + } +} + +/* Wrappers around extract_attr_init so we can pass the index of the attribute that we want to + * extract. The overall API does not allow us to pass this in a convenient way. */ +#define EXTRACT_INIT_WRAPPER(index) \ + static void extract_attr_init##index( \ + const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf, void *tls_data) \ + { \ + extract_attr_init(mr, cache, buf, tls_data, index); \ + } + +EXTRACT_INIT_WRAPPER(0) +EXTRACT_INIT_WRAPPER(1) +EXTRACT_INIT_WRAPPER(2) +EXTRACT_INIT_WRAPPER(3) +EXTRACT_INIT_WRAPPER(4) +EXTRACT_INIT_WRAPPER(5) +EXTRACT_INIT_WRAPPER(6) +EXTRACT_INIT_WRAPPER(7) +EXTRACT_INIT_WRAPPER(8) +EXTRACT_INIT_WRAPPER(9) +EXTRACT_INIT_WRAPPER(10) +EXTRACT_INIT_WRAPPER(11) +EXTRACT_INIT_WRAPPER(12) +EXTRACT_INIT_WRAPPER(13) +EXTRACT_INIT_WRAPPER(14) + +template constexpr MeshExtract create_extractor_attr(ExtractInitFn fn) +{ + MeshExtract extractor = {nullptr}; + extractor.init = fn; + extractor.data_type = MR_DATA_NONE; + extractor.data_size = 0; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[index]); + return extractor; +} + +/** \} */ + +} // namespace blender::draw + +extern "C" { +#define CREATE_EXTRACTOR_ATTR(index) \ + blender::draw::create_extractor_attr(blender::draw::extract_attr_init##index) + +const MeshExtract extract_attr[GPU_MAX_ATTR] = { + CREATE_EXTRACTOR_ATTR(0), + CREATE_EXTRACTOR_ATTR(1), + CREATE_EXTRACTOR_ATTR(2), + CREATE_EXTRACTOR_ATTR(3), + CREATE_EXTRACTOR_ATTR(4), + CREATE_EXTRACTOR_ATTR(5), + CREATE_EXTRACTOR_ATTR(6), + CREATE_EXTRACTOR_ATTR(7), + CREATE_EXTRACTOR_ATTR(8), + CREATE_EXTRACTOR_ATTR(9), + CREATE_EXTRACTOR_ATTR(10), + CREATE_EXTRACTOR_ATTR(11), + CREATE_EXTRACTOR_ATTR(12), + CREATE_EXTRACTOR_ATTR(13), + CREATE_EXTRACTOR_ATTR(14), +}; +} diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index 2c7770c8e72..f8878eb2617 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -43,9 +43,7 @@ static void extract_vcol_init(const MeshRenderData *mr, GPU_vertformat_deinterleave(&format); CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; uint32_t vcol_layers = cache->cd_used.vcol; - uint32_t svcol_layers = cache->cd_used.sculpt_vcol; for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { @@ -64,42 +62,14 @@ static void extract_vcol_init(const MeshRenderData *mr, } /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 && - CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) { + /* We only do `vcols` that are not overridden by `uvs`. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); GPU_vertformat_alias_add(&format, attr_name); } } } - /* Sculpt Vertex Colors */ - if (U.experimental.use_sculpt_vertex_colors) { - for (int i = 0; i < 8; i++) { - if (svcol_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - - BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "c"); - } - if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "ac"); - } - /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs`. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); - } - } - } - } - GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -108,7 +78,6 @@ static void extract_vcol_init(const MeshRenderData *mr, }; gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo); - MLoop *loops = (MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { @@ -139,35 +108,6 @@ static void extract_vcol_init(const MeshRenderData *mr, } } } - - if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MPropCol *prop_col = (const MPropCol *)BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs); - vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]); - vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]); - vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]); - vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]); - vcol_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - MPropCol *vcol = (MPropCol *)CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) { - vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]); - vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]); - vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]); - vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]); - } - } - } } } diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 018e192bf37..911c8cc2e42 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -32,7 +32,7 @@ #include "GPU_shader.h" #include "GPU_vertex_buffer.h" -#define GPU_BATCH_VBO_MAX_LEN 6 +#define GPU_BATCH_VBO_MAX_LEN 16 #define GPU_BATCH_INST_VBO_MAX_LEN 2 #define GPU_BATCH_VAO_STATIC_LEN 3 #define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16 @@ -54,11 +54,11 @@ typedef enum eGPUBatchFlag { GPU_BATCH_OWNS_INDEX = (GPU_BATCH_OWNS_INST_VBO_MAX << 1), /** Has been initialized. At least one VBO is set. */ - GPU_BATCH_INIT = (1 << 16), + GPU_BATCH_INIT = (1 << 26), /** Batch is initialized but its VBOs are still being populated. (optional) */ - GPU_BATCH_BUILDING = (1 << 16), + GPU_BATCH_BUILDING = (1 << 26), /** Cached data need to be rebuild. (VAO, PSO, ...) */ - GPU_BATCH_DIRTY = (1 << 17), + GPU_BATCH_DIRTY = (1 << 27), } eGPUBatchFlag; #define GPU_BATCH_OWNS_NONE GPU_BATCH_INVALID diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 97f14b2195d..3943c063c39 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -127,6 +127,10 @@ typedef struct Mesh_Runtime { /** Needed in case we need to lazily initialize the mesh. */ CustomData_MeshMasks cd_mask_extra; + /** Needed to ensure some thread-safety during render data pre-processing. */ + void *render_mutex; + void *_pad3; + } Mesh_Runtime; typedef struct Mesh { From 26e3045eb929c26d9b93327f126fc8f0a75181f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 26 Oct 2021 17:15:43 +0200 Subject: [PATCH 1222/1500] 3D View context: return "ok" when fetching view3d context dir `view3d_context()` would return `-1` ("found but not available") when fetching the context dir. This is incorrect; it should return 1 ("ok"). This is a semantic change in preparation of further cleanup of the code. --- source/blender/editors/space_view3d/space_view3d.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 8e61ff1765a..816000619ba 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1731,7 +1731,7 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes return 0; /* not found */ } - return -1; /* found but not available */ + return 1; } static void view3d_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id) From 5acbc01d0dd6e2453bafc637e6ac995848bf205e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 26 Oct 2021 17:17:50 +0200 Subject: [PATCH 1223/1500] Cleanup: 3D View context, use enum values Use explicit enum values instead of returning 0/1 from `view3d_context()`. No functional changes. --- source/blender/editors/space_view3d/space_view3d.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 816000619ba..c2618a340d9 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1725,13 +1725,13 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes } } - return 1; + return CTX_RESULT_OK; } else { - return 0; /* not found */ + return CTX_RESULT_MEMBER_NOT_FOUND; } - return 1; + return CTX_RESULT_OK; } static void view3d_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id) From 2d5c9e0bafcca0624ceec324b6c3ca384a748d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 26 Oct 2021 17:22:11 +0200 Subject: [PATCH 1224/1500] Cleanup: 3D View context, early returns for clearer flow Refactor `view3d_context()` to use early `return`s instead of a bundle of `if`/`else if`/`else`, some of which had `return`s and some not. No functional changes. --- source/blender/editors/space_view3d/space_view3d.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index c2618a340d9..2b0e302a0d1 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1700,8 +1700,9 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes if (CTX_data_dir(member)) { CTX_data_dir_set(result, view3d_context_dir); + return CTX_RESULT_OK; } - else if (CTX_data_equals(member, "active_object")) { + if (CTX_data_equals(member, "active_object")) { /* In most cases the active object is the `view_layer->basact->object`. * For the 3D view however it can be NULL when hidden. * @@ -1727,11 +1728,8 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes return CTX_RESULT_OK; } - else { - return CTX_RESULT_MEMBER_NOT_FOUND; - } - return CTX_RESULT_OK; + return CTX_RESULT_MEMBER_NOT_FOUND; } static void view3d_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id) From 03c0581c6ed6eb98c4c4c7bf42c92d623880dfac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 26 Oct 2021 18:15:55 +0200 Subject: [PATCH 1225/1500] Assets: allow Mark/Clear Asset operators from 3D Viewport Make it possible to run `ASSET_OT_mark` and `ASSET_OT_clear` operators from the 3D Viewport. There is no menu entry, just compatibility with pressing F3 and executing the operators from the operator search. --- source/blender/editors/space_view3d/space_view3d.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 2b0e302a0d1..10fd7670996 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1690,6 +1690,7 @@ static void space_view3d_refresh(const bContext *C, ScrArea *area) const char *view3d_context_dir[] = { "active_object", + "selected_ids", NULL, }; @@ -1728,6 +1729,17 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes return CTX_RESULT_OK; } + if (CTX_data_equals(member, "selected_ids")) { + ListBase selected_objects; + CTX_data_selected_objects(C, &selected_objects); + LISTBASE_FOREACH (PointerRNA *, object_ptr, &selected_objects) { + ID *selected_id = object_ptr->data; + CTX_data_id_list_add(result, selected_id); + } + BLI_freelistN(&selected_objects); + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return CTX_RESULT_OK; + } return CTX_RESULT_MEMBER_NOT_FOUND; } From 11e8a2ec5fc95524a59a7422d2715ef1116dbef4 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 18:48:24 +0200 Subject: [PATCH 1226/1500] UI: Support disabled-hint for dropping in the tree-view API A tree-view item's drop controller can now return a message for the user explaining why dropping isn't possible with the dropped data. This is then displayed in red text next to the cursor. This isn't actually used yet, the follow up commit will do that. --- source/blender/editors/include/UI_interface.h | 4 +++- .../blender/editors/include/UI_tree_view.hh | 7 ++++++- .../editors/interface/interface_dropboxes.cc | 9 ++++++++- source/blender/editors/interface/tree_view.cc | 9 ++++++--- .../space_file/asset_catalog_tree_view.cc | 19 +++++++++++-------- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index b6e33d6ed0d..12fdda2092c 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2777,7 +2777,9 @@ void UI_interface_tag_script_reload(void); bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); -bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const struct wmDrag *drag); +bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, + const struct wmDrag *drag, + const char **r_disabled_hint); char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct wmDrag *drag); bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const struct ListBase *drags); bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle); diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index f565c80193f..fb0304060c9 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -362,8 +362,13 @@ class AbstractTreeViewItemDropController { /** * Check if the data dragged with \a drag can be dropped on the item this controller is for. + * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping + * isn't possible on this item. Shouldn't be done too aggressively, e.g. + * don't set this if the drag-type can't be dropped here; only if it can + * but there's another reason it can't be dropped. + * Can assume this is a non-null pointer. */ - virtual bool can_drop(const wmDrag &drag) const = 0; + virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; /** * Custom text to display when dragging over a tree item. Should explain what happens when * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index 1cc06db3a8c..ab0c7e088e2 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -22,6 +22,8 @@ #include "DNA_space_types.h" +#include "MEM_guardedalloc.h" + #include "WM_api.h" #include "UI_interface.h" @@ -35,7 +37,12 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve return false; } - return UI_tree_view_item_can_drop(hovered_tree_item, drag); + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } + + drag->free_disabled_info = false; + return UI_tree_view_item_can_drop(hovered_tree_item, drag, &drag->disabled_info); } static char *ui_tree_view_drop_tooltip(bContext *C, diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index da95cad0fc7..0eeb32bcc69 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -675,7 +675,9 @@ bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, return a.matches_including_parents(b); } -bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const wmDrag *drag) +bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, + const wmDrag *drag, + const char **r_disabled_hint) { const AbstractTreeViewItem &item = reinterpret_cast(*item_); const std::unique_ptr drop_controller = @@ -684,7 +686,7 @@ bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const wmDrag return false; } - return drop_controller->can_drop(*drag); + return drop_controller->can_drop(*drag, r_disabled_hint); } char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wmDrag *drag) @@ -709,8 +711,9 @@ bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const ListBase * std::unique_ptr drop_controller = item.create_drop_controller(); + const char *disabled_hint_dummy = nullptr; LISTBASE_FOREACH (const wmDrag *, drag, drags) { - if (drop_controller->can_drop(*drag)) { + if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { return drop_controller->on_drop(*drag); } } diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 68df4cc0544..a7762008c47 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -106,11 +106,11 @@ class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController explicit AssetCatalogDropController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item); - bool can_drop(const wmDrag &drag) const override; + bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; std::string drop_tooltip(const wmDrag &drag) const override; bool on_drop(const wmDrag &drag) override; - static bool has_droppable_item(const wmDrag &drag); + static bool has_droppable_item(const wmDrag &drag, const char **r_disabled_hint); static bool drop_into_catalog(const AssetCatalogTreeView &tree_view, const wmDrag &drag, CatalogID catalog_id, @@ -131,7 +131,7 @@ class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { struct DropController : public ui::AbstractTreeViewItemDropController { DropController(AssetCatalogTreeView &tree_view); - bool can_drop(const wmDrag &drag) const override; + bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; std::string drop_tooltip(const wmDrag &drag) const override; bool on_drop(const wmDrag &drag) override; }; @@ -320,12 +320,12 @@ AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tre { } -bool AssetCatalogDropController::can_drop(const wmDrag &drag) const +bool AssetCatalogDropController::can_drop(const wmDrag &drag, const char **r_disabled_hint) const { if (drag.type != WM_DRAG_ASSET_LIST) { return false; } - return has_droppable_item(drag); + return has_droppable_item(drag, r_disabled_hint); } std::string AssetCatalogDropController::drop_tooltip(const wmDrag &drag) const @@ -377,10 +377,12 @@ bool AssetCatalogDropController::drop_into_catalog(const AssetCatalogTreeView &t return true; } -bool AssetCatalogDropController::has_droppable_item(const wmDrag &drag) +bool AssetCatalogDropController::has_droppable_item(const wmDrag &drag, + const char **r_disabled_hint) { const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + *r_disabled_hint = nullptr; /* There needs to be at least one asset from the current file. */ LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) { if (!asset_item->is_external) { @@ -421,12 +423,13 @@ AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogT { } -bool AssetCatalogTreeViewUnassignedItem::DropController::can_drop(const wmDrag &drag) const +bool AssetCatalogTreeViewUnassignedItem::DropController::can_drop( + const wmDrag &drag, const char **r_disabled_hint) const { if (drag.type != WM_DRAG_ASSET_LIST) { return false; } - return AssetCatalogDropController::has_droppable_item(drag); + return AssetCatalogDropController::has_droppable_item(drag, r_disabled_hint); } std::string AssetCatalogTreeViewUnassignedItem::DropController::drop_tooltip( From 730de2e7fd33cf6d529aab6846d0a0d509806627 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 18:50:19 +0200 Subject: [PATCH 1227/1500] Asset Browser: Show disabled-hint when dragging external assets over catalog There's now a message displayed in red next to the cursor explaining that only assets from the current file can be moved between catalogs. The previous commit prepared this. --- source/blender/editors/space_file/asset_catalog_tree_view.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index a7762008c47..354ee742598 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -389,6 +389,8 @@ bool AssetCatalogDropController::has_droppable_item(const wmDrag &drag, return true; } } + + *r_disabled_hint = "Only assets from this current file can be moved between catalogs"; return false; } From eaed38cbd3372024408bab644c99559be56c8958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 26 Oct 2021 18:53:40 +0200 Subject: [PATCH 1228/1500] Add Assets menu to 3D View's Object menu In the 3D Viewport, add an "Assets" submenu to the Objects menu, for the same operators as available in the outliner: Mark as Asset, Clear Asset, Clear Asset (Set Fake User). Since object assets are still considered experimental, the menu is only shown when the Extended Asset Browser experimental feature is enabled. --- release/scripts/startup/bl_ui/space_view3d.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 1c2190bb7a0..07ca3a27392 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2265,6 +2265,7 @@ class VIEW3D_MT_object(Menu): layout.separator() + layout.menu("VIEW3D_MT_object_asset") layout.menu("VIEW3D_MT_object_parent") layout.menu("VIEW3D_MT_object_collection") layout.menu("VIEW3D_MT_object_relations") @@ -2758,6 +2759,21 @@ class VIEW3D_MT_object_cleanup(Menu): layout.operator("object.material_slot_remove_unused", text="Remove Unused Material Slots") +class VIEW3D_MT_object_asset(Menu): + bl_label = "Asset" + + @classmethod + def poll(cls, context): + # TODO(Sybren): once object assets are no longer considered experimental, remove this poll function. + return context.preferences.experimental.use_extended_asset_browser + + def draw(self, _context): + layout = self.layout + + layout.operator("asset.mark") + layout.operator("asset.clear", text="Clear Asset").set_fake_user = False + layout.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True + class VIEW3D_MT_make_single_user(Menu): bl_label = "Make Single User" @@ -7541,6 +7557,7 @@ classes = ( VIEW3D_MT_image_add, VIEW3D_MT_object, VIEW3D_MT_object_animation, + VIEW3D_MT_object_asset, VIEW3D_MT_object_rigid_body, VIEW3D_MT_object_clear, VIEW3D_MT_object_context_menu, From a062d86230a07caf022b105f395a6b47dc604040 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 04:19:45 +1100 Subject: [PATCH 1229/1500] Drop object assets and associated objects at the cursor location When dropping asset objects, place them under the mouse-cursor along with any other objects they link in. Ref D12935 Reviewed By: Severin --- source/blender/editors/include/ED_object.h | 2 + source/blender/editors/object/object_add.c | 168 +++++++++++++----- source/blender/editors/object/object_intern.h | 1 + source/blender/editors/object/object_ops.c | 1 + source/blender/editors/object/object_utils.c | 68 +++++++ .../editors/space_view3d/space_view3d.c | 104 +++++++++-- source/blender/windowmanager/WM_api.h | 1 + .../windowmanager/intern/wm_dragdrop.c | 27 +-- 8 files changed, 296 insertions(+), 76 deletions(-) diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 458ce57ab86..2a557f1abd3 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -122,6 +122,8 @@ void ED_object_xform_skip_child_container_item_ensure(struct XFormObjectSkipChil struct Object *ob_parent_recurse, int mode); +void ED_object_xform_array_m4(struct Object **objects, uint objects_len, const float matrix[4][4]); + /* object_ops.c */ void ED_operatortypes_object(void); void ED_operatormacros_object(void); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 917df0da868..f5a304a718b 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -3481,19 +3481,6 @@ void OBJECT_OT_duplicate(wmOperatorType *ot) * Use for drag & drop. * \{ */ -static Base *object_add_ensure_in_view_layer(Main *bmain, ViewLayer *view_layer, Object *ob) -{ - Base *base = BKE_view_layer_base_find(view_layer, ob); - - if (!base) { - LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_object_add(bmain, layer_collection->collection, ob); - base = BKE_view_layer_base_find(view_layer, ob); - } - - return base; -} - static int object_add_named_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -3501,8 +3488,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); Base *basen; Object *ob; - const bool duplicate = RNA_boolean_get(op->ptr, "duplicate"); - const bool linked = duplicate && RNA_boolean_get(op->ptr, "linked"); + const bool linked = RNA_boolean_get(op->ptr, "linked"); const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag; char name[MAX_ID_NAME - 2]; @@ -3516,30 +3502,21 @@ static int object_add_named_exec(bContext *C, wmOperator *op) } /* prepare dupli */ - if (duplicate) { - basen = object_add_duplicate_internal( - bmain, - scene, - view_layer, - ob, - dupflag, - /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this - * function will only work if the object is already linked in the view layer, which is not - * the case here. So we have to do the new-ID relinking ourselves - * (#copy_object_set_idnew()). - */ - LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); - } - else { - /* basen is actually not a new base in this case. */ - basen = object_add_ensure_in_view_layer(bmain, view_layer, ob); - } + basen = object_add_duplicate_internal( + bmain, + scene, + view_layer, + ob, + dupflag, + /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this + * function will only work if the object is already linked in the view layer, which is not + * the case here. So we have to do the new-ID relinking ourselves + * (#copy_object_set_idnew()). + */ + LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); if (basen == NULL) { - BKE_report(op->reports, - RPT_ERROR, - duplicate ? "Object could not be duplicated" : - "Object could not be linked to the view layer"); + BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated"); return OPERATOR_CANCELLED; } @@ -3597,22 +3574,11 @@ void OBJECT_OT_add_named(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; PropertyRNA *prop; - - prop = RNA_def_boolean( - ot->srna, - "duplicate", - true, - "Duplicate", - "Create a duplicate of the object. If not set, only ensures the object is linked into the " - "active view layer, positions and selects/activates it (deselecting others)"); - RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_boolean(ot->srna, "linked", false, "Linked", - "Duplicate object but not object data, linking to the original data (ignored if " - "'duplicate' is false)"); + "Duplicate object but not object data, linking to the original data"); RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add"); @@ -3625,6 +3591,110 @@ void OBJECT_OT_add_named(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Transform Object to Mouse Operator + * \{ */ + +/** + * Alternate behavior for dropping an asset that positions the appended object(s). + */ +static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob; + + if (RNA_struct_property_is_set(op->ptr, "name")) { + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); + ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); + } + else { + ob = OBACT(view_layer); + } + + if (ob == NULL) { + BKE_report(op->reports, RPT_ERROR, "Object not found"); + return OPERATOR_CANCELLED; + } + /* Ensure the locations are updated so snap reads the evaluated active location. */ + CTX_data_ensure_evaluated_depsgraph(C); + + PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix"); + if (RNA_property_is_set(op->ptr, prop_matrix)) { + uint objects_len; + Object **objects = BKE_view_layer_array_selected_objects(view_layer, NULL, &objects_len, {}); + + float matrix[4][4]; + RNA_property_float_get_array(op->ptr, prop_matrix, &matrix[0][0]); + + float mat_src_unit[4][4]; + float mat_dst_unit[4][4]; + float final_delta[4][4]; + + normalize_m4_m4(mat_src_unit, ob->obmat); + normalize_m4_m4(mat_dst_unit, matrix); + invert_m4(mat_src_unit); + mul_m4_m4m4(final_delta, mat_dst_unit, mat_src_unit); + + ED_object_xform_array_m4(objects, objects_len, final_delta); + + MEM_freeN(objects); + } + else { + int mval[2]; + if (object_add_drop_xy_get(C, op, &mval)) { + float cursor[3]; + ED_object_location_from_view(C, cursor); + ED_view3d_cursor3d_position(C, mval, false, cursor); + + /* Use the active objects location since this is the ID which the user selected to drop. + * + * This transforms all selected objects, so that dropping a single object which links in + * other objects will have their relative transformation preserved. + * For example a child/parent relationship or other objects used with a boolean modifier. + * + * The caller is responsible for ensuring the selection state gives useful results. + * Link/append does this using #FILE_AUTOSELECT. */ + ED_view3d_snap_selected_to_location(C, cursor, V3D_AROUND_ACTIVE); + } + } + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_transform_to_mouse(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Place Object Under Mouse"; + ot->description = "Snap selected item(s) to the mouse location"; + ot->idname = "OBJECT_OT_transform_to_mouse"; + + /* api callbacks */ + ot->invoke = object_add_drop_xy_generic_invoke; + ot->exec = object_transform_to_mouse_exec; + ot->poll = ED_operator_objectmode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + PropertyRNA *prop; + RNA_def_string(ot->srna, + "name", + NULL, + MAX_ID_NAME - 2, + "Name", + "Object name to place (when unset use the active object)"); + + prop = RNA_def_float_matrix( + ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + object_add_drop_xy_props(ot); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Join Object Operator * \{ */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index ea9a2de090b..fe07ecef438 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -106,6 +106,7 @@ void OBJECT_OT_select_same_collection(struct wmOperatorType *ot); /* object_add.c */ void OBJECT_OT_add(struct wmOperatorType *ot); void OBJECT_OT_add_named(struct wmOperatorType *ot); +void OBJECT_OT_transform_to_mouse(struct wmOperatorType *ot); void OBJECT_OT_metaball_add(struct wmOperatorType *ot); void OBJECT_OT_text_add(struct wmOperatorType *ot); void OBJECT_OT_armature_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index b3bf2c64a91..b171da42522 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -112,6 +112,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_volume_import); WM_operatortype_append(OBJECT_OT_add); WM_operatortype_append(OBJECT_OT_add_named); + WM_operatortype_append(OBJECT_OT_transform_to_mouse); WM_operatortype_append(OBJECT_OT_effector_add); WM_operatortype_append(OBJECT_OT_collection_instance_add); WM_operatortype_append(OBJECT_OT_data_instance_add); diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c index 66390f6f165..c7dfe911ce7 100644 --- a/source/blender/editors/object/object_utils.c +++ b/source/blender/editors/object/object_utils.c @@ -36,6 +36,7 @@ #include "BKE_armature.h" #include "BKE_editmesh.h" #include "BKE_lattice.h" +#include "BKE_object.h" #include "BKE_scene.h" #include "DEG_depsgraph_query.h" @@ -430,3 +431,70 @@ void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xd } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform Object Array + * + * Low level object transform function, transforming objects by `matrix`. + * Simple alternative to full transform logic. + * \{ */ + +static bool object_parent_in_set(GSet *objects_set, Object *ob) +{ + for (Object *parent = ob->parent; parent; parent = parent->parent) { + if (BLI_gset_lookup(objects_set, parent)) { + return true; + } + } + return false; +} + +void ED_object_xform_array_m4(Object **objects, uint objects_len, const float matrix[4][4]) +{ + /* Filter out objects that have parents in `objects_set`. */ + { + GSet *objects_set = BLI_gset_ptr_new_ex(__func__, objects_len); + for (uint i = 0; i < objects_len; i++) { + BLI_gset_add(objects_set, objects[i]); + } + for (uint i = 0; i < objects_len;) { + if (object_parent_in_set(objects_set, objects[i])) { + objects[i] = objects[--objects_len]; + } + else { + i++; + } + } + BLI_gset_free(objects_set, NULL); + } + + /* Detect translation only matrix, prevent rotation/scale channels from being touched at all. */ + bool is_translation_only; + { + float test_m4_a[4][4], test_m4_b[4][4]; + unit_m4(test_m4_a); + copy_m4_m4(test_m4_b, matrix); + zero_v3(test_m4_b[3]); + is_translation_only = equals_m4m4(test_m4_a, test_m4_b); + } + + if (is_translation_only) { + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + add_v3_v3(ob->loc, matrix[3]); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + } + else { + for (uint i = 0; i < objects_len; i++) { + float m4[4][4]; + Object *ob = objects[i]; + BKE_object_to_mat4(ob, m4); + mul_m4_m4m4(m4, matrix, m4); + BKE_object_apply_mat4(ob, m4, true, true); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + } +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 10fd7670996..0ab39d59aff 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -47,6 +47,7 @@ #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_lattice.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_mball.h" #include "BKE_mesh.h" @@ -56,6 +57,7 @@ #include "BKE_workspace.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_render.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -557,6 +559,25 @@ static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_OB); } +static bool view3d_ob_drop_poll_external_asset(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ASSET)) { + return false; + } + return true; +} + +/** + * \note the term local here refers to not being an external asset, + * poll will succeed for linked library objects. + */ +static bool view3d_ob_drop_poll_local_id(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ID)) { + return false; + } + return true; +} static bool view3d_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { @@ -669,22 +690,13 @@ static bool view3d_volume_drop_poll(bContext *UNUSED(C), return (drag->type == WM_DRAG_PATH) && (drag->icon == ICON_FILE_VOLUME); } -static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) +static void view3d_ob_drop_matrix_from_snap(V3DSnapCursorState *snap_state, + Object *ob, + float obmat_final[4][4]) { - ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB); - - RNA_string_set(drop->ptr, "name", id->name + 2); - /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ - const bool is_imported_id = drag->type == WM_DRAG_ASSET; - RNA_boolean_set(drop->ptr, "duplicate", !is_imported_id); - - V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); - BLI_assert(snap_state->draw_box || snap_state->draw_plane); - Object *ob = (Object *)id; - float obmat_final[4][4]; - V3DSnapCursorData *snap_data; snap_data = ED_view3d_cursor_snap_data_get(snap_state, NULL, 0, 0); + BLI_assert(snap_state->draw_box || snap_state->draw_plane); copy_m4_m3(obmat_final, snap_data->plane_omat); copy_v3_v3(obmat_final[3], snap_data->loc); @@ -700,6 +712,56 @@ static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) mul_mat3_m4_v3(obmat_final, offset); sub_v3_v3(obmat_final[3], offset); } +} + +static void view3d_ob_drop_copy_local_id(wmDrag *drag, wmDropBox *drop) +{ + ID *id = WM_drag_get_local_ID(drag, ID_OB); + + RNA_string_set(drop->ptr, "name", id->name + 2); + /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ + BLI_assert(drag->type != WM_DRAG_ASSET); + + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + float obmat_final[4][4]; + + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); +} + +static void view3d_ob_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop) +{ + /* NOTE(@campbellbarton): Selection is handled here, de-selecting objects before append, + * using auto-select to ensure the new objects are selected. + * This is done so #OBJECT_OT_transform_to_mouse (which runs after this drop handler) + * can use the context setup here to place the objects. */ + BLI_assert(drag->type == WM_DRAG_ASSET); + + wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + bContext *C = asset_drag->evil_C; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + BKE_view_layer_base_deselect_all(view_layer); + + ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT); + + RNA_string_set(drop->ptr, "name", id->name + 2); + + Base *base = BKE_view_layer_base_find(view_layer, (Object *)id); + if (base != NULL) { + BKE_view_layer_base_select_and_set_active(view_layer, base); + WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, scene); + } + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_object_tag(C); + + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + float obmat_final[4][4]; + + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); } @@ -768,8 +830,8 @@ static void view3d_dropboxes(void) struct wmDropBox *drop; drop = WM_dropbox_add(lb, "OBJECT_OT_add_named", - view3d_ob_drop_poll, - view3d_ob_drop_copy, + view3d_ob_drop_poll_local_id, + view3d_ob_drop_copy_local_id, WM_drag_free_imported_drag_ID, NULL); @@ -778,6 +840,18 @@ static void view3d_dropboxes(void) drop->draw_deactivate = view3d_ob_drop_draw_deactivate; drop->opcontext = WM_OP_EXEC_DEFAULT; /* Not really needed. */ + drop = WM_dropbox_add(lb, + "OBJECT_OT_transform_to_mouse", + view3d_ob_drop_poll_external_asset, + view3d_ob_drop_copy_external_asset, + WM_drag_free_imported_drag_ID, + NULL); + + drop->draw = WM_drag_draw_item_name_fn; + drop->draw_activate = view3d_ob_drop_draw_activate; + drop->draw_deactivate = view3d_ob_drop_draw_deactivate; + drop->opcontext = WM_OP_INVOKE_DEFAULT; + WM_dropbox_add(lb, "OBJECT_OT_drop_named_material", view3d_mat_drop_poll, diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 2d5100eec7c..f9bc309af9f 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -751,6 +751,7 @@ void WM_drag_draw_default_fn(struct bContext *C, ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); /* ID drag and drop */ +ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, int flag_extra); void WM_drag_add_local_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent); struct ID *WM_drag_get_local_ID(const struct wmDrag *drag, short idcode); struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short idcode); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 3206d0c044e..21f5925b037 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -526,8 +526,15 @@ struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode return NULL; } -static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) +/** + * \param flag_extra: Additional linking flags (from #eFileSel_Params_Flag). + */ +ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra) { + /* Only support passing in limited flags. */ + BLI_assert(flag_extra == (flag_extra & FILE_AUTOSELECT)); + eFileSel_Params_Flag flag = flag_extra | FILE_ACTIVE_COLLECTION; + const char *name = asset_drag->name; ID_Type idtype = asset_drag->id_type; @@ -541,14 +548,8 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) switch ((eFileAssetImportType)asset_drag->import_type) { case FILE_ASSET_IMPORT_LINK: - return WM_file_link_datablock(bmain, - scene, - view_layer, - view3d, - asset_drag->path, - idtype, - name, - FILE_ACTIVE_COLLECTION); + return WM_file_link_datablock( + bmain, scene, view_layer, view3d, asset_drag->path, idtype, name, flag); case FILE_ASSET_IMPORT_APPEND: return WM_file_append_datablock(bmain, scene, @@ -557,7 +558,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) asset_drag->path, idtype, name, - BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | + flag | BLO_LIBLINK_APPEND_RECURSIVE | BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR); case FILE_ASSET_IMPORT_APPEND_REUSE: return WM_file_append_datablock(G_MAIN, @@ -567,7 +568,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) asset_drag->path, idtype, name, - BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | + flag | BLO_LIBLINK_APPEND_RECURSIVE | BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR | BLO_LIBLINK_APPEND_LOCAL_ID_REUSE); } @@ -582,6 +583,8 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) * * Use #WM_drag_free_imported_drag_ID() as cancel callback of the drop-box, so that the asset * import is rolled back if the drop operator fails. + * + * \param flag: #eFileSel_Params_Flag passed to linking code. */ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) { @@ -599,7 +602,7 @@ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) } /* Link/append the asset. */ - return wm_drag_asset_id_import(asset_drag); + return WM_drag_asset_id_import(asset_drag, 0); } /** From 7bffddfa6e877bd6e66cf5641515ebd07267f145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 26 Oct 2021 19:21:22 +0200 Subject: [PATCH 1230/1500] Don't deselect assets in `ED_fileselect_activate_by_id` `ED_fileselect_activate_by_id()` activates an asset (i.e. marks it as the active asset in the asset browser). To avoid an "active but not selected" state, it also selects it. Before this commit, the function would also deselect all other assets, but that's considered doing too much. If deselection is required, the `ED_fileselect_deselect_all()` function can be called. Manifest Task: T92152 --- source/blender/editors/include/ED_fileselect.h | 2 +- source/blender/editors/space_file/filesel.c | 4 +--- source/blender/makesrna/intern/rna_space_api.c | 3 ++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 3beabaf2d1d..48ff742ef3a 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -145,7 +145,7 @@ bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile); struct AssetLibrary *ED_fileselect_active_asset_library_get(const struct SpaceFile *sfile); struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile); -/* Activate the file that corresponds to the given ID. +/* Activate and select the file that corresponds to the given ID. * Pass deferred=true to wait for the next refresh before activating. */ void ED_fileselect_activate_by_id(struct SpaceFile *sfile, struct ID *asset_id, diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 2b4f14451e7..4cd05237138 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -514,14 +514,12 @@ void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool def const FileDirEntry *file = filelist_file_ex(files, file_index, false); if (filelist_file_get_id(file) != asset_id) { - filelist_entry_select_set(files, file, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); continue; } params->active_file = file_index; filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); - - /* Keep looping to deselect the other files. */ + break; } WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, NULL); diff --git a/source/blender/makesrna/intern/rna_space_api.c b/source/blender/makesrna/intern/rna_space_api.c index 295ecf590bb..c5569683c9c 100644 --- a/source/blender/makesrna/intern/rna_space_api.c +++ b/source/blender/makesrna/intern/rna_space_api.c @@ -122,7 +122,8 @@ void RNA_api_space_filebrowser(StructRNA *srna) PropertyRNA *parm; func = RNA_def_function(srna, "activate_asset_by_id", "ED_fileselect_activate_by_id"); - RNA_def_function_ui_description(func, "Activate the asset entry that represents the given ID"); + RNA_def_function_ui_description( + func, "Activate and select the asset entry that represents the given ID"); parm = RNA_def_property(func, "id_to_activate", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(parm, "ID"); From 3286150ac9ab2c51cf4a12e75125c52de13fb9a0 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 14:33:23 +0200 Subject: [PATCH 1231/1500] Assets: Enable object asset support by default The two blocking issues for object assets are addressed, see T92111 and T90198. So now, objects can be marked as assets and be used in the Asset Browser. --- source/blender/editors/asset/ED_asset_type.h | 5 +++-- source/blender/editors/asset/intern/asset_type.cc | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/asset/ED_asset_type.h b/source/blender/editors/asset/ED_asset_type.h index 5629ae189c0..36cbb4591e9 100644 --- a/source/blender/editors/asset/ED_asset_type.h +++ b/source/blender/editors/asset/ED_asset_type.h @@ -29,7 +29,8 @@ extern "C" { struct ID; bool ED_asset_type_id_is_non_experimental(const struct ID *id); -#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS (FILTER_ID_MA | FILTER_ID_AC | FILTER_ID_WO) +#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS \ + (FILTER_ID_MA | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO) /** * Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset, @@ -51,7 +52,7 @@ int64_t ED_asset_types_supported_as_filter_flags(void); * strings with this (not all UI code supports dynamic strings nicely). * Should start with a consonant, so usages can prefix it with "a" (not "an"). */ -#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Material, Pose Action, or World" +#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Material, Object, Pose Action, or World" #ifdef __cplusplus } diff --git a/source/blender/editors/asset/intern/asset_type.cc b/source/blender/editors/asset/intern/asset_type.cc index cdff538a712..028c0cb9ffc 100644 --- a/source/blender/editors/asset/intern/asset_type.cc +++ b/source/blender/editors/asset/intern/asset_type.cc @@ -30,7 +30,7 @@ bool ED_asset_type_id_is_non_experimental(const ID *id) { /* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and * #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */ - return ELEM(GS(id->name), ID_MA, ID_AC, ID_WO); + return ELEM(GS(id->name), ID_MA, ID_OB, ID_AC, ID_WO); } bool ED_asset_type_is_supported(const ID *id) From 6871f8482b03f13b94171dcbb1211b00802bb9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 26 Oct 2021 19:26:57 +0200 Subject: [PATCH 1232/1500] 3D View, Objects menu: Always show Assets sub-menu Now that object assets are no longer considered experimental, the Assets submenu can always be shown (regardless of the Extended Asset Browser experimental feature). --- release/scripts/startup/bl_ui/space_view3d.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 07ca3a27392..0172fa0655f 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2762,11 +2762,6 @@ class VIEW3D_MT_object_cleanup(Menu): class VIEW3D_MT_object_asset(Menu): bl_label = "Asset" - @classmethod - def poll(cls, context): - # TODO(Sybren): once object assets are no longer considered experimental, remove this poll function. - return context.preferences.experimental.use_extended_asset_browser - def draw(self, _context): layout = self.layout From b6d2bee28f3a232f47de19b26051054e7c863269 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 26 Oct 2021 12:45:47 -0500 Subject: [PATCH 1233/1500] Geometry Nodes: Support instances in the remove attribute node This node is still hidden, but allowing removing the `id` attribute is useful for testing, and possibly optimization in the future. --- .../blender/nodes/geometry/nodes/node_geo_attribute_remove.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index f93ef6f1db3..65a137a028d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -59,6 +59,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params) remove_attribute( geometry_set.get_component_for_write(), params, attribute_names); } + if (geometry_set.has()) { + remove_attribute( + geometry_set.get_component_for_write(), params, attribute_names); + } params.set_output("Geometry", geometry_set); } From 9fa304bf13e402405351a2c9bc14903c08b557e5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 26 Oct 2021 12:50:39 -0500 Subject: [PATCH 1234/1500] Geometry Nodes: Only create instance IDs when they exist Instance IDs serve no purpose for rendering when they aren't stable from one frame to the next, and if the index is used in the end anyway, there is no point in storing a vector of IDs and copying it around. This commit exposes the `id` attribute on the instances component, makes it optional-- only generated by default with the distribute points on faces node. Since the string to curves node only added the index as each instance's ID, I removed it. This means that it would be necessary to add the ID data manually if the initial index actually helps (when deleting only certain characters, for example). Differential Revision: https://developer.blender.org/D12980 --- source/blender/blenkernel/BKE_geometry_set.hh | 9 +- .../intern/geometry_component_instances.cc | 117 ++++++++++++++++-- .../spreadsheet_data_source_geometry.cc | 18 +-- source/blender/modifiers/intern/MOD_nodes.cc | 4 + .../nodes/legacy/node_geo_point_instance.cc | 2 +- .../nodes/node_geo_instance_on_points.cc | 14 ++- .../nodes/node_geo_instances_to_points.cc | 14 ++- .../geometry/nodes/node_geo_join_geometry.cc | 5 +- .../nodes/node_geo_string_to_curves.cc | 2 - 9 files changed, 151 insertions(+), 34 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 78ccefaed5c..429c37e9c9b 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -621,7 +621,9 @@ class InstancesComponent : public GeometryComponent { blender::Vector instance_transforms_; /** * IDs of the instances. They are used for consistency over multiple frames for things like - * motion blur. + * motion blur. Proper stable ID data that actually helps when rendering can only be generated + * in some situations, so this vector is allowed to be empty, in which case the index of each + * instance will be used for the final ID. */ blender::Vector instance_ids_; @@ -643,7 +645,7 @@ class InstancesComponent : public GeometryComponent { void resize(int capacity); int add_reference(const InstanceReference &reference); - void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1); + void add_instance(int instance_handle, const blender::float4x4 &transform); blender::Span references() const; void remove_unused_references(); @@ -658,6 +660,9 @@ class InstancesComponent : public GeometryComponent { blender::MutableSpan instance_ids(); blender::Span instance_ids() const; + blender::MutableSpan instance_ids_ensure(); + void instance_ids_clear(); + int instances_amount() const; int references_amount() const; diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 4204d62e1a7..d02121b44a6 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -60,7 +60,9 @@ void InstancesComponent::reserve(int min_capacity) { instance_reference_handles_.reserve(min_capacity); instance_transforms_.reserve(min_capacity); - instance_ids_.reserve(min_capacity); + if (!instance_ids_.is_empty()) { + this->instance_ids_ensure(); + } } /** @@ -73,7 +75,9 @@ void InstancesComponent::resize(int capacity) { instance_reference_handles_.resize(capacity); instance_transforms_.resize(capacity); - instance_ids_.resize(capacity); + if (!instance_ids_.is_empty()) { + this->instance_ids_ensure(); + } } void InstancesComponent::clear() @@ -85,15 +89,15 @@ void InstancesComponent::clear() references_.clear(); } -void InstancesComponent::add_instance(const int instance_handle, - const float4x4 &transform, - const int id) +void InstancesComponent::add_instance(const int instance_handle, const float4x4 &transform) { BLI_assert(instance_handle >= 0); BLI_assert(instance_handle < references_.size()); instance_reference_handles_.append(instance_handle); instance_transforms_.append(transform); - instance_ids_.append(id); + if (!instance_ids_.is_empty()) { + this->instance_ids_ensure(); + } } blender::Span InstancesComponent::instance_reference_handles() const @@ -124,6 +128,22 @@ blender::Span InstancesComponent::instance_ids() const return instance_ids_; } +/** + * Make sure the ID storage size matches the number of instances. By directly resizing the + * component's vectors internally, it is possible to be in a situation where the IDs are not + * empty but they do not have the correct size; this function resolves that. + */ +blender::MutableSpan InstancesComponent::instance_ids_ensure() +{ + instance_ids_.append_n_times(0, this->instances_amount() - instance_ids_.size()); + return instance_ids_; +} + +void InstancesComponent::instance_ids_clear() +{ + instance_ids_.clear_and_make_inline(); +} + /** * With write access to the instances component, the data in the instanced geometry sets can be * changed. This is a function on the component rather than each reference to ensure `const` @@ -327,8 +347,16 @@ static blender::Array generate_unique_instance_ids(Span original_ids) blender::Span InstancesComponent::almost_unique_ids() const { std::lock_guard lock(almost_unique_ids_mutex_); - if (almost_unique_ids_.size() != instance_ids_.size()) { - almost_unique_ids_ = generate_unique_instance_ids(instance_ids_); + if (instance_ids().is_empty()) { + almost_unique_ids_.reinitialize(this->instances_amount()); + for (const int i : almost_unique_ids_.index_range()) { + almost_unique_ids_[i] = i; + } + } + else { + if (almost_unique_ids_.size() != instance_ids_.size()) { + almost_unique_ids_ = generate_unique_instance_ids(instance_ids_); + } } return almost_unique_ids_; } @@ -398,11 +426,82 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider } }; +class InstanceIDAttributeProvider final : public BuiltinAttributeProvider { + public: + InstanceIDAttributeProvider() + : BuiltinAttributeProvider( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32, Creatable, Writable, Deletable) + { + } + + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final + { + const InstancesComponent &instances = static_cast(component); + if (instances.instance_ids().is_empty()) { + return {}; + } + return std::make_unique>(instances.instance_ids()); + } + + GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + { + InstancesComponent &instances = static_cast(component); + if (instances.instance_ids().is_empty()) { + return {}; + } + return std::make_unique>(instances.instance_ids()); + } + + bool try_delete(GeometryComponent &component) const final + { + InstancesComponent &instances = static_cast(component); + if (instances.instance_ids().is_empty()) { + return false; + } + instances.instance_ids_clear(); + return true; + } + + bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final + { + InstancesComponent &instances = static_cast(component); + if (instances.instances_amount() == 0) { + return false; + } + MutableSpan ids = instances.instance_ids_ensure(); + switch (initializer.type) { + case AttributeInit::Type::Default: { + ids.fill(0); + break; + } + case AttributeInit::Type::VArray: { + const GVArray *varray = static_cast(initializer).varray; + varray->materialize_to_uninitialized(IndexRange(varray->size()), ids.data()); + break; + } + case AttributeInit::Type::MoveArray: { + void *source_data = static_cast(initializer).data; + ids.copy_from({static_cast(source_data), instances.instances_amount()}); + MEM_freeN(source_data); + break; + } + } + return true; + } + + bool exists(const GeometryComponent &component) const final + { + const InstancesComponent &instances = static_cast(component); + return !instances.instance_ids().is_empty(); + } +}; + static ComponentAttributeProviders create_attribute_providers_for_instances() { static InstancePositionAttributeProvider position; + static InstanceIDAttributeProvider id; - return ComponentAttributeProviders({&position}, {}); + return ComponentAttributeProviders({&position, &id}, {}); } } // namespace blender::bke diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 76e7291d905..f352a5fd0eb 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -466,14 +466,16 @@ std::unique_ptr InstancesDataSource::get_column_values( }); } Span ids = component_->instance_ids(); - if (STREQ(column_id.name, "ID")) { - /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_INT32, - column_id.name, - size, - [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; }, - 5.5f); + if (!ids.is_empty()) { + if (STREQ(column_id.name, "ID")) { + /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_INT32, + column_id.name, + size, + [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; }, + 5.5f); + } } return {}; } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index e6cc7663c58..292ba04490c 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -917,6 +917,10 @@ static void store_output_value_in_geometry(GeometrySet &geometry_set, CurveComponent &component = geometry_set.get_component_for_write(); store_field_on_geometry_component(component, attribute_name, domain, field); } + if (geometry_set.has_instances()) { + InstancesComponent &component = geometry_set.get_component_for_write(); + store_field_on_geometry_component(component, attribute_name, domain, field); + } } /** diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc index fb45c22ced4..b68dfe44984 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc @@ -184,7 +184,7 @@ static void add_instances_from_component(InstancesComponent &instances, instances.resize(start_len + domain_size); MutableSpan handles = instances.instance_reference_handles().slice(start_len, domain_size); MutableSpan transforms = instances.instance_transforms().slice(start_len, domain_size); - MutableSpan instance_ids = instances.instance_ids().slice(start_len, domain_size); + MutableSpan instance_ids = instances.instance_ids_ensure().slice(start_len, domain_size); /* Skip all of the randomness handling if there is only a single possible instance * (anything except for collection mode with "Whole Collection" turned off). */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 44cad2d38d4..f4a127faf43 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -77,7 +77,6 @@ static void add_instances_from_component(InstancesComponent &dst_component, select_len); MutableSpan dst_transforms = dst_component.instance_transforms().slice(start_len, select_len); - MutableSpan dst_stable_ids = dst_component.instance_ids().slice(start_len, select_len); FieldEvaluator field_evaluator{field_context, domain_size}; const VArray *pick_instance = nullptr; @@ -86,7 +85,6 @@ static void add_instances_from_component(InstancesComponent &dst_component, const VArray *scales = nullptr; /* The evaluator could use the component's stable IDs as a destination directly, but only the * selected indices should be copied. */ - GVArray_Typed stable_ids = src_component.attribute_get_for_read("id", ATTR_DOMAIN_POINT, 0); field_evaluator.add(params.get_input>("Pick Instance"), &pick_instance); field_evaluator.add(params.get_input>("Instance Index"), &indices); field_evaluator.add(params.get_input>("Rotation"), &rotations); @@ -119,7 +117,6 @@ static void add_instances_from_component(InstancesComponent &dst_component, threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { for (const int range_i : selection_range) { const int64_t i = selection[range_i]; - dst_stable_ids[range_i] = (*stable_ids)[i]; /* Compute base transform for every instances. */ float4x4 &dst_transform = dst_transforms[range_i]; @@ -157,6 +154,17 @@ static void add_instances_from_component(InstancesComponent &dst_component, } }); + GVArrayPtr id_attribute = src_component.attribute_try_get_for_read( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (id_attribute) { + GVArray_Typed ids{*id_attribute}; + VArray_Span ids_span{ids}; + MutableSpan dst_ids = dst_component.instance_ids_ensure(); + for (const int64_t i : selection.index_range()) { + dst_ids[i] = ids_span[selection[i]]; + } + } + if (pick_instance->is_single()) { if (pick_instance->get_internal_single()) { if (instance.has_realized_data()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index 63d1f88a442..434fe4a19d7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -77,13 +77,15 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const VArray &radii = evaluator.get_evaluated(1); copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint}); - OutputAttribute_Typed id_attribute = points.attribute_try_get_for_output( - "id", ATTR_DOMAIN_POINT, 0); - MutableSpan ids = id_attribute.as_span(); - for (const int i : selection.index_range()) { - ids[i] = instances.instance_ids()[selection[i]]; + if (!instances.instance_ids().is_empty()) { + OutputAttribute_Typed id_attribute = points.attribute_try_get_for_output( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + MutableSpan ids = id_attribute.as_span(); + for (const int i : selection.index_range()) { + ids[i] = instances.instance_ids()[selection[i]]; + } + id_attribute.save(); } - id_attribute.save(); } static void geo_node_instances_to_points_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index b628c5cbab8..bbba8635b88 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -270,17 +270,16 @@ static void join_components(Span src_components, Geo } Span src_transforms = src_component->instance_transforms(); - Span src_ids = src_component->instance_ids(); Span src_reference_handles = src_component->instance_reference_handles(); for (const int i : src_transforms.index_range()) { const int src_handle = src_reference_handles[i]; const int dst_handle = handle_map[src_handle]; const float4x4 &transform = src_transforms[i]; - const int id = src_ids[i]; - dst_component.add_instance(dst_handle, transform, id); + dst_component.add_instance(dst_handle, transform); } } + join_attributes(to_base_components(src_components), dst_component, {"position"}); } static void join_components(Span src_components, GeometrySet &result) diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index ac946540221..83526df3ac2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -242,13 +242,11 @@ static void add_instances_from_handles(InstancesComponent &instances, instances.resize(positions.size()); MutableSpan handles = instances.instance_reference_handles(); MutableSpan transforms = instances.instance_transforms(); - MutableSpan instance_ids = instances.instance_ids(); threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) { for (const int i : range) { handles[i] = char_handles.lookup(charcodes[i]); transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0}); - instance_ids[i] = i; } }); } From be3e09ecec5372f87b3e9779adb821867b062be1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 26 Oct 2021 12:59:46 -0500 Subject: [PATCH 1235/1500] Fix: Inverted normal for one curve to mesh cap --- source/blender/blenkernel/intern/curve_to_mesh_convert.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index b3957e57920..cd40d5e8a41 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -199,9 +199,10 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, info.profile_edge_len * last_ring_index; for (const int i : IndexRange(info.profile_edge_len)) { + const int i_inv = info.profile_edge_len - i - 1; MLoop &loop_start = r_loops[cap_loop_offset + i]; - loop_start.v = info.vert_offset + i; - loop_start.e = profile_edges_start + i; + loop_start.v = info.vert_offset + i_inv; + loop_start.e = profile_edges_start + i_inv; MLoop &loop_end = r_loops[cap_loop_offset + info.profile_edge_len + i]; loop_end.v = last_ring_vert_offset + i; loop_end.e = last_ring_edge_offset + i; From 0bfae1b12078ef278a56c6e932c13be5bc9781aa Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 26 Oct 2021 20:00:03 +0200 Subject: [PATCH 1236/1500] Geometry Nodes: geometry component type warning system Previously, every node had to create warnings for unsupported input geometry manually. Now this is automated. Nodes just have to specify the geometry types they support in the node declaration. Differential Revision: https://developer.blender.org/D12899 --- source/blender/blenkernel/BKE_geometry_set.hh | 2 + .../blender/blenkernel/intern/geometry_set.cc | 34 ++++++++++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry_exec.hh | 14 +++- .../blender/nodes/NOD_socket_declarations.hh | 10 --- .../nodes/NOD_socket_declarations_geometry.hh | 58 +++++++++++++++++ .../nodes/geometry/node_geometry_util.hh | 1 + .../nodes/geometry/nodes/node_geo_boolean.cc | 11 ++-- .../geometry/nodes/node_geo_curve_fill.cc | 2 +- .../geometry/nodes/node_geo_curve_fillet.cc | 2 +- .../geometry/nodes/node_geo_curve_length.cc | 2 +- .../geometry/nodes/node_geo_curve_resample.cc | 2 +- .../geometry/nodes/node_geo_curve_reverse.cc | 2 +- .../geometry/nodes/node_geo_curve_sample.cc | 9 +-- .../nodes/node_geo_curve_set_handles.cc | 2 +- .../nodes/node_geo_curve_spline_type.cc | 2 +- .../nodes/node_geo_curve_subdivide.cc | 2 +- .../geometry/nodes/node_geo_curve_to_mesh.cc | 23 ++----- .../nodes/node_geo_curve_to_points.cc | 2 +- .../geometry/nodes/node_geo_curve_trim.cc | 2 +- .../node_geo_distribute_points_on_faces.cc | 2 +- .../geometry/nodes/node_geo_edge_split.cc | 2 +- .../nodes/node_geo_instances_to_points.cc | 2 +- .../nodes/node_geo_material_replace.cc | 2 +- .../geometry/nodes/node_geo_mesh_subdivide.cc | 2 +- .../geometry/nodes/node_geo_mesh_to_curve.cc | 2 +- .../geometry/nodes/node_geo_mesh_to_points.cc | 2 +- .../nodes/node_geo_points_to_vertices.cc | 2 +- .../nodes/node_geo_points_to_volume.cc | 2 +- .../geometry/nodes/node_geo_proximity.cc | 9 +-- .../nodes/geometry/nodes/node_geo_raycast.cc | 19 +----- .../nodes/node_geo_rotate_instances.cc | 2 +- .../nodes/node_geo_scale_instances.cc | 2 +- .../nodes/node_geo_set_curve_handles.cc | 2 +- .../nodes/node_geo_set_curve_radius.cc | 2 +- .../geometry/nodes/node_geo_set_curve_tilt.cc | 2 +- .../geometry/nodes/node_geo_set_material.cc | 2 +- .../nodes/node_geo_set_material_index.cc | 2 +- .../nodes/node_geo_set_point_radius.cc | 2 +- .../nodes/node_geo_set_shade_smooth.cc | 2 +- .../nodes/node_geo_set_spline_cyclic.cc | 2 +- .../nodes/node_geo_set_spline_resolution.cc | 2 +- .../nodes/node_geo_subdivision_surface.cc | 2 +- .../nodes/node_geo_transfer_attribute.cc | 23 ++----- .../nodes/node_geo_translate_instances.cc | 2 +- .../geometry/nodes/node_geo_triangulate.cc | 2 +- .../geometry/nodes/node_geo_volume_to_mesh.cc | 2 +- .../nodes/intern/extern_implementations.cc | 1 + .../nodes/intern/node_geometry_exec.cc | 65 +++++++++++++++++++ .../nodes/intern/node_socket_declarations.cc | 41 ++++++++++++ 50 files changed, 269 insertions(+), 120 deletions(-) create mode 100644 source/blender/nodes/NOD_socket_declarations_geometry.hh diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 429c37e9c9b..d273fe74310 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -318,6 +318,8 @@ struct GeometrySet { bool include_instances, blender::Map &r_attributes) const; + blender::Vector gather_component_types(bool include_instances, bool ignore_empty) const; + using ForeachSubGeometryCallback = blender::FunctionRef; void modify_geometry_sets(ForeachSubGeometryCallback callback); diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 4753a9e0768..cd1bafe445a 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -527,6 +527,40 @@ void GeometrySet::gather_attributes_for_propagation( delete dummy_component; } +static void gather_component_types_recursive(const GeometrySet &geometry_set, + const bool include_instances, + const bool ignore_empty, + Vector &r_types) +{ + for (const GeometryComponent *component : geometry_set.get_components_for_read()) { + if (ignore_empty) { + if (component->is_empty()) { + continue; + } + } + r_types.append_non_duplicates(component->type()); + } + if (!include_instances) { + return; + } + const InstancesComponent *instances = geometry_set.get_component_for_read(); + if (instances == nullptr) { + return; + } + instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { + gather_component_types_recursive( + instance_geometry_set, include_instances, ignore_empty, r_types); + }); +} + +blender::Vector GeometrySet::gather_component_types( + const bool include_instances, bool ignore_empty) const +{ + Vector types; + gather_component_types_recursive(*this, include_instances, ignore_empty, types); + return types; +} + static void gather_mutable_geometry_sets(GeometrySet &geometry_set, Vector &r_geometry_sets) { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index acf0ab9b224..9d35b7b9bff 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -448,6 +448,7 @@ set(SRC NOD_shader.h NOD_socket.h NOD_socket_declarations.hh + NOD_socket_declarations_geometry.hh NOD_static_types.h NOD_texture.h NOD_type_conversions.hh diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index f5775b8a6e0..6e1f21dbae0 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -171,10 +171,16 @@ class GeoNodeExecParams { this->check_input_access(identifier, &CPPType::get()); #endif GMutablePointer gvalue = this->extract_input(identifier); - return gvalue.relocate_out(); + T value = gvalue.relocate_out(); + if constexpr (std::is_same_v) { + this->check_input_geometry_set(identifier, value); + } + return value; } } + void check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const; + /** * Get input as vector for multi input socket with the given identifier. * @@ -211,7 +217,11 @@ class GeoNodeExecParams { #endif GPointer gvalue = provider_->get_input(identifier); BLI_assert(gvalue.is_type()); - return *(const T *)gvalue.get(); + const T &value = *(const T *)gvalue.get(); + if constexpr (std::is_same_v) { + this->check_input_geometry_set(identifier, value); + } + return value; } } diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index d4958f433d6..f7aea212f73 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -212,14 +212,6 @@ class Image : public IDSocketDeclaration { Image(); }; -class Geometry : public SocketDeclaration { - public: - using Builder = SocketDeclarationBuilder; - - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; - bool matches(const bNodeSocket &socket) const override; -}; - /* -------------------------------------------------------------------- */ /** \name #FloatBuilder Inline Methods * \{ */ @@ -396,9 +388,7 @@ MAKE_EXTERN_SOCKET_DECLARATION(decl::Vector) MAKE_EXTERN_SOCKET_DECLARATION(decl::Bool) MAKE_EXTERN_SOCKET_DECLARATION(decl::Color) MAKE_EXTERN_SOCKET_DECLARATION(decl::String) -MAKE_EXTERN_SOCKET_DECLARATION(decl::Geometry) -#undef MAKE_EXTERN_SOCKET_DECLARATION } // namespace blender::nodes /** \} */ diff --git a/source/blender/nodes/NOD_socket_declarations_geometry.hh b/source/blender/nodes/NOD_socket_declarations_geometry.hh new file mode 100644 index 00000000000..1531f82d67d --- /dev/null +++ b/source/blender/nodes/NOD_socket_declarations_geometry.hh @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "BKE_geometry_set.hh" + +#include "NOD_socket_declarations.hh" + +namespace blender::nodes::decl { + +class GeometryBuilder; + +class Geometry : public SocketDeclaration { + private: + blender::Vector supported_types_; + bool only_realized_data_ = false; + bool only_instances_ = false; + + friend GeometryBuilder; + + public: + using Builder = GeometryBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + + Span supported_types() const; + bool only_realized_data() const; + bool only_instances() const; +}; + +class GeometryBuilder : public SocketDeclarationBuilder { + public: + GeometryBuilder &supported_type(GeometryComponentType supported_type); + GeometryBuilder &supported_type(blender::Vector supported_types); + GeometryBuilder &only_realized_data(bool value = true); + GeometryBuilder &only_instances(bool value = true); +}; + +} // namespace blender::nodes::decl + +namespace blender::nodes { +MAKE_EXTERN_SOCKET_DECLARATION(decl::Geometry) +} diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index f382ff6c132..167765fa131 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -32,6 +32,7 @@ #include "NOD_geometry.h" #include "NOD_geometry_exec.hh" #include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" #include "node_util.h" diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 27e18f16bad..dae7eb4bed1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -27,8 +27,10 @@ namespace blender::nodes { static void geo_node_boolean_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry 1"); - b.add_input("Geometry 2").multi_input(); + b.add_input("Geometry 1") + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input("Geometry 2").multi_input().supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Self Intersection"); b.add_input("Hole Tolerant"); b.add_output("Geometry"); @@ -83,11 +85,6 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) GeometrySet set_a; if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) { set_a = params.extract_input("Geometry 1"); - if (set_a.has_instances()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("Instances are not supported for the first geometry input, and will not be used")); - } /* Note that it technically wouldn't be necessary to realize the instances for the first * geometry input, but the boolean code expects the first shape for the difference operation * to be a single mesh. */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 953922531c1..75459a97816 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -35,7 +35,7 @@ namespace blender::nodes { static void geo_node_curve_fill_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_output("Mesh"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 67ce5b00d6b..5b6dfb01f38 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void geo_node_curve_fillet_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Count").default_value(1).min(1).max(1000).supports_field(); b.add_input("Radius") .min(0.0f) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index ac7df35bb72..ef617ed3742 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -21,7 +21,7 @@ namespace blender::nodes { static void geo_node_curve_length_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_output("Length"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 2617b2f6646..d3e7521631f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -34,7 +34,7 @@ namespace blender::nodes { static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Count").default_value(10).min(1).max(100000).supports_field(); b.add_input("Length").default_value(0.1f).min(0.001f).supports_field().subtype( PROP_DISTANCE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index d4ccb768713..f23d409741f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Curve"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 6d371c27d43..02d1e35cd07 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -27,7 +27,8 @@ namespace blender::nodes { static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); + b.add_input("Curve").only_realized_data().supported_type( + GEO_COMPONENT_TYPE_CURVE); b.add_input("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); b.add_input("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field(); @@ -237,12 +238,6 @@ static void geo_node_curve_sample_exec(GeoNodeExecParams params) params.set_output("Normal", fn::make_constant_field({0.0f, 0.0f, 0.0f})); }; - if (geometry_set.has_instances()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("The node only supports realized curve data, instances are ignored")); - } - const CurveComponent *component = geometry_set.get_component_for_read(); if (component == nullptr) { return return_default(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc index 9e7ac60c29d..8ee2ee76780 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void geo_node_curve_set_handles_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Curve"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index ec72154db13..ddbd2d74dd6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -27,7 +27,7 @@ namespace blender::nodes { static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Curve"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 5a79fcffd4d..a67a3a6736d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -33,7 +33,7 @@ namespace blender::nodes { static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Cuts").default_value(1).min(0).max(1000).supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index be755c7269e..b6aa550cf2e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -27,8 +27,10 @@ namespace blender::nodes { static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_input("Profile Curve"); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input("Profile Curve") + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Fill Caps") .description( "If the profile spline is cyclic, fill the ends of the generated mesh with N-gons"); @@ -58,18 +60,6 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) GeometrySet profile_set = params.extract_input("Profile Curve"); const bool fill_caps = params.extract_input("Fill Caps"); - if (profile_set.has_instances()) { - params.error_message_add(NodeWarningType::Error, - TIP_("Instances are not supported in the profile input")); - params.set_output("Mesh", GeometrySet()); - return; - } - - if (!profile_set.has_curve() && !profile_set.is_empty()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("No curve data available in the profile input")); - } - bool has_curve = false; curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_curve()) { @@ -79,11 +69,6 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); }); - if (!has_curve && !curve_set.is_empty()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("No curve data available in curve input")); - } - params.set_output("Mesh", std::move(curve_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index e43e1fa6c75..0f74a9edcf9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -30,7 +30,7 @@ namespace blender::nodes { static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Count").default_value(10).min(2).max(100000); b.add_input("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); b.add_output("Points"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 6b049b4d384..44a403b80a5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -28,7 +28,7 @@ namespace blender::nodes { static void geo_node_curve_trim_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); b.add_input("End") .min(0.0f) diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index c267815226a..58393aea158 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -42,7 +42,7 @@ namespace blender::nodes { static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Distance Min").min(0.0f).subtype(PROP_DISTANCE); b.add_input("Density Max").default_value(10.0f).min(0.0f); diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index e97fc5c2c83..2f9d3a60ba3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -26,7 +26,7 @@ namespace blender::nodes { static void geo_node_edge_split_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh"); + b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Mesh"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index 434fe4a19d7..a120560c848 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -23,7 +23,7 @@ namespace blender::nodes { static void geo_node_instances_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input("Instances"); + b.add_input("Instances").supported_type(GEO_COMPONENT_TYPE_INSTANCES); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Position").implicit_field(); b.add_input("Radius") diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc index f3562bed6e9..7181b438282 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc @@ -28,7 +28,7 @@ namespace blender::nodes { static void geo_node_material_replace_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Old"); b.add_input("New"); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index d1dd5b1bf8b..471ebab71aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -27,7 +27,7 @@ namespace blender::nodes { static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Level").default_value(1).min(0).max(6); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 7bca9ec141b..761581ca5d6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -22,7 +22,7 @@ namespace blender::nodes { static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh"); + b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Curve"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index df0fdd8eccd..0a4c0638e27 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -30,7 +30,7 @@ namespace blender::nodes { static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh"); + b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Selection").default_value(true).supports_field().hide_value(); b.add_input("Position").implicit_field(); b.add_input("Radius") diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index afd0ced6360..d16af8481f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -27,7 +27,7 @@ namespace blender::nodes { static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b) { - b.add_input("Points"); + b.add_input("Points").supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); b.add_input("Selection").default_value(true).supports_field().hide_value(); b.add_output("Mesh"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 719523f64f0..26001551fdf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -32,7 +32,7 @@ namespace blender::nodes { static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); b.add_input("Density").default_value(1.0f).min(0.0f); b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index ec4d6ceb728..f3840df52aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -31,7 +31,8 @@ namespace blender::nodes { static void geo_node_proximity_declare(NodeDeclarationBuilder &b) { - b.add_input("Target"); + b.add_input("Target").only_realized_data().supported_type( + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); b.add_input("Source Position").implicit_field(); b.add_output("Position").dependent_field(); b.add_output("Distance").dependent_field(); @@ -214,12 +215,6 @@ static void geo_node_proximity_exec(GeoNodeExecParams params) params.set_output("Distance", fn::make_constant_field(0.0f)); }; - if (geometry_set_target.has_instances()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("The node only supports realized mesh or point cloud data, instances are ignored")); - } - if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) { return return_default(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 1e687f163e6..c042256114f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -31,7 +31,9 @@ namespace blender::nodes { static void geo_node_raycast_declare(NodeDeclarationBuilder &b) { - b.add_input("Target Geometry"); + b.add_input("Target Geometry") + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Attribute").hide_value().supports_field(); b.add_input("Attribute", "Attribute_001").hide_value().supports_field(); @@ -389,26 +391,11 @@ static void geo_node_raycast_exec(GeoNodeExecParams params) }); }; - if (target.has_instances()) { - if (target.has_realized_data()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("The node only supports realized mesh data, instances are ignored")); - } - else { - params.error_message_add(NodeWarningType::Error, - TIP_("The target geometry must contain realized data")); - return return_default(); - } - } - if (target.is_empty()) { return return_default(); } if (!target.has_mesh()) { - params.error_message_add(NodeWarningType::Error, - TIP_("The target geometry must contain a mesh")); return return_default(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 09d92d7fa1e..3688e88b51b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -22,7 +22,7 @@ namespace blender::nodes { static void geo_node_rotate_instances_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").only_instances(); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Rotation").subtype(PROP_EULER).supports_field(); b.add_input("Pivot Point").subtype(PROP_TRANSLATION).supports_field(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index 33897ef354d..a5fa8fe1c73 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -22,7 +22,7 @@ namespace blender::nodes { static void geo_node_scale_instances_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").only_instances(); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Scale").subtype(PROP_XYZ).default_value({1, 1, 1}).supports_field(); b.add_input("Center").subtype(PROP_TRANSLATION).supports_field(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index d7aaaffc7c6..92a01d42241 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void geo_node_set_curve_handles_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Position").implicit_field(); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index 8fa4ff1a808..1e8c0acf0e4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_set_curve_radius_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Radius").min(0.0f).default_value(1.0f).supports_field().subtype( PROP_DISTANCE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index 113149613ef..2018b1668eb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_set_curve_tilt_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Tilt").subtype(PROP_ANGLE).supports_field(); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc index 0d85af60944..dab872a0440 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -28,7 +28,7 @@ namespace blender::nodes { static void geo_node_set_material_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Material").hide_label(); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index a25fe332916..d77f4f4fbaa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_set_material_index_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Material Index").supports_field().min(0); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index b59c9a9e8f5..449e9b55a39 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_set_point_radius_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Radius").default_value(0.05f).min(0.0f).supports_field().subtype( PROP_DISTANCE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index ca77041ba7c..9404f424c86 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_set_shade_smooth_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Shade Smooth").supports_field(); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index 50e00ff3758..03eb67ac021 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_set_spline_cyclic_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Cyclic").supports_field(); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index dccb0b1a969..c0608c36c2e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -22,7 +22,7 @@ namespace blender::nodes { static void geo_node_set_spline_resolution_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Resolution").default_value(12).supports_field(); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 410c9a8bb35..c82b09e6b38 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -31,7 +31,7 @@ namespace blender::nodes { static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Level").default_value(1).min(0).max(6); b.add_input("Crease") .default_value(0.0f) diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 5350b14b53b..d52f028c38c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -40,7 +40,8 @@ namespace blender::nodes { static void geo_node_transfer_attribute_declare(NodeDeclarationBuilder &b) { - b.add_input("Target"); + b.add_input("Target").only_realized_data().supported_type( + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); b.add_input("Attribute").hide_value().supports_field(); b.add_input("Attribute", "Attribute_001").hide_value().supports_field(); @@ -747,21 +748,9 @@ static void geo_node_transfer_attribute_exec(GeoNodeExecParams params) }); }; - if (geometry.has_instances()) { - if (geometry.has_realized_data()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("Only realized geometry is supported, instances will not be used")); - } - else { - params.error_message_add(NodeWarningType::Error, - TIP_("Target geometry must contain realized data")); - return return_default(); - } - /* Since the instances are not used, there is no point in keeping - * a reference to them while the field is passed around. */ - geometry.remove(GEO_COMPONENT_TYPE_INSTANCES); - } + /* Since the instances are not used, there is no point in keeping + * a reference to them while the field is passed around. */ + geometry.remove(GEO_COMPONENT_TYPE_INSTANCES); GField output_field; switch (mapping) { @@ -791,8 +780,6 @@ static void geo_node_transfer_attribute_exec(GeoNodeExecParams params) } case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("Curve targets are not currently supported")); return return_default(); } auto fn = std::make_unique( diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index 8fc2843fd8a..8bd9aece229 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -22,7 +22,7 @@ namespace blender::nodes { static void geo_node_translate_instances_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").only_instances(); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Translation").subtype(PROP_TRANSLATION).supports_field(); b.add_input("Local Space").default_value(true).supports_field(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 7ef0913622c..e0d99567d20 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -31,7 +31,7 @@ namespace blender::nodes { static void geo_node_triangulate_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Minimum Vertices").default_value(4).min(4).max(10000); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index c0084de367f..a8c92eb0f07 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -39,7 +39,7 @@ namespace blender::nodes { static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input("Volume"); + b.add_input("Volume").supported_type(GEO_COMPONENT_TYPE_VOLUME); b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); b.add_input("Threshold").default_value(0.1f).min(0.0f); diff --git a/source/blender/nodes/intern/extern_implementations.cc b/source/blender/nodes/intern/extern_implementations.cc index 42d4b2878bc..35de319f20b 100644 --- a/source/blender/nodes/intern/extern_implementations.cc +++ b/source/blender/nodes/intern/extern_implementations.cc @@ -15,6 +15,7 @@ */ #include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" namespace blender::nodes { #define MAKE_EXTERN_SOCKET_IMPLEMENTATION(TYPE) \ diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 8ea085d42f9..bcf6ff2fcf6 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -36,6 +36,71 @@ void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::strin local_logger.log_node_warning(provider_->dnode, type, std::move(message)); } +void GeoNodeExecParams::check_input_geometry_set(StringRef identifier, + const GeometrySet &geometry_set) const +{ + const int input_index = provider_->dnode->input_by_identifier(identifier).index(); + const SocketDeclaration &decl = *provider_->dnode->declaration()->inputs()[input_index]; + const decl::Geometry *geo_decl = dynamic_cast(&decl); + if (geo_decl == nullptr) { + return; + } + + const bool only_realized_data = geo_decl->only_realized_data(); + const bool only_instances = geo_decl->only_instances(); + const Span supported_types = geo_decl->supported_types(); + + if (only_realized_data) { + if (geometry_set.has_instances()) { + this->error_message_add(NodeWarningType::Info, "Instances in input geometry are ignored"); + } + } + if (only_instances) { + if (geometry_set.has_realized_data()) { + this->error_message_add(NodeWarningType::Info, + "Realized data in input geometry are ignored"); + } + } + if (supported_types.is_empty()) { + /* Assume all types are supported. */ + return; + } + const Vector types_in_geometry = geometry_set.gather_component_types( + true, true); + for (const GeometryComponentType type : types_in_geometry) { + if (type == GEO_COMPONENT_TYPE_INSTANCES) { + continue; + } + if (supported_types.contains(type)) { + continue; + } + std::string message = TIP_("Input geometry has unsupported type: "); + switch (type) { + case GEO_COMPONENT_TYPE_MESH: { + message += TIP_("Mesh"); + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + message += TIP_("Point Cloud"); + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + BLI_assert_unreachable(); + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + message += TIP_("Volume"); + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + message += TIP_("Curve"); + break; + } + } + this->error_message_add(NodeWarningType::Info, std::move(message)); + } +} + const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { for (const InputSocketRef *socket : provider_->dnode->inputs()) { diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index e823476f9e4..ed5691ebf7f 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -15,6 +15,7 @@ */ #include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" #include "BKE_node.h" @@ -333,6 +334,46 @@ bool Geometry::matches(const bNodeSocket &socket) const return true; } +Span Geometry::supported_types() const +{ + return supported_types_; +} + +bool Geometry::only_realized_data() const +{ + return only_realized_data_; +} + +bool Geometry::only_instances() const +{ + return only_instances_; +} + +GeometryBuilder &GeometryBuilder::supported_type(GeometryComponentType supported_type) +{ + decl_->supported_types_ = {supported_type}; + return *this; +} + +GeometryBuilder &GeometryBuilder::supported_type( + blender::Vector supported_types) +{ + decl_->supported_types_ = std::move(supported_types); + return *this; +} + +GeometryBuilder &GeometryBuilder::only_realized_data(bool value) +{ + decl_->only_realized_data_ = value; + return *this; +} + +GeometryBuilder &GeometryBuilder::only_instances(bool value) +{ + decl_->only_instances_ = value; + return *this; +} + /** \} */ } // namespace blender::nodes::decl From c7b27f45ae7856560cd35dead8f1897b917c8d67 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 26 Oct 2021 13:03:09 -0500 Subject: [PATCH 1237/1500] Fix: Show node editor dot grid when there is no node tree --- source/blender/editors/space_node/node_draw.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index ea65eaa0a14..b7f1209905e 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2272,6 +2272,9 @@ void node_draw_space(const bContext *C, ARegion *region) /* Nodes. */ snode_set_context(C); + const int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE); + UI_view2d_dot_grid_draw(v2d, TH_GRID, NODE_GRID_STEP_SIZE, grid_levels); + /* Draw parent node trees. */ if (snode->treepath.last) { bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last; @@ -2294,9 +2297,6 @@ void node_draw_space(const bContext *C, ARegion *region) copy_v2_v2(snode->edittree->view_center, center); } - const int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE); - UI_view2d_dot_grid_draw(v2d, TH_GRID, NODE_GRID_STEP_SIZE, grid_levels); - /* Top-level edit tree. */ bNodeTree *ntree = path->nodetree; if (ntree) { From 4db4a97355672ee27542e45ac9959523567e0053 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Tue, 26 Oct 2021 20:07:26 +0200 Subject: [PATCH 1238/1500] Node Editor: Style update to nodes This patch changes how nodes look visually, in an attempt to fix a number of issues: * The header background is currently drawn using a theme color fully opaque, this limits the colors we can use because the node name/label is drawn on top. * Hard-coded transparency makes nodes hard to read. The node backdrop already has alpha so if the user wants it they can set it. This patch uses alpha from the theme. * Better muted status indicator, instead of simply making everything transparent and the wires inside red, draw a red outline around the node, darken the header and backdrop. * On muted nodes, display wires behind the backdrop to not interfere with text/widgets inside the node. Nodes: * Darken header to improve readability of node label. * Draw a line under the header * Thicker outline. * Do not hard-code transparency on nodes, use the theme's node backdrop alpha component. * Use angle icon instead of triangle (to be consistent with the [[ https://developer.blender.org/D12814 | changes ]] to panels) Style adjustment to sockets drawing: * Do not hard-code the socket outline color to black, use `TH_WIRE` instead * Do not use `TH_TEXT_HI` for selected sockets, use `TH_ACTIVE` (active node outline) * Do not draw sockets background transparent on muted nodes. * Thicker outline to help contrast and readability {F11496707, size=full} Reviewed By: #user_interface, HooglyBoogly Differential Revision: https://developer.blender.org/D12884 --- .../datafiles/userdef/userdef_default_theme.c | 4 +- .../blender/editors/space_node/node_draw.cc | 334 +++++++++++------- .../blender/editors/space_node/node_intern.h | 2 +- 3 files changed, 200 insertions(+), 140 deletions(-) diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 867a4c8f56b..d0171867e50 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -792,7 +792,7 @@ const bTheme U_theme_default = { .row_alternate = RGBA(0xffffff07), }, .space_node = { - .back = RGBA(0x23232300), + .back = RGBA(0x1a1a1a00), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), @@ -829,7 +829,7 @@ const bTheme U_theme_default = { .noodle_curving = 4, .grid_levels = 7, .dash_alpha = 0.5f, - .syntaxl = RGBA(0x565656ff), + .syntaxl = RGBA(0x3e3e3eff), .syntaxs = RGBA(0x975b5bff), .syntaxb = RGBA(0xccb83dff), .syntaxn = RGBA(0xe64555ff), diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index b7f1209905e..429c53d6740 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -730,7 +730,7 @@ static void node_draw_mute_line(const View2D *v2d, const SpaceNode *snode, const GPU_blend(GPU_BLEND_ALPHA); LISTBASE_FOREACH (const bNodeLink *, link, &node->internal_links) { - node_draw_link_bezier(v2d, snode, link, TH_REDALERT, TH_REDALERT, -1); + node_draw_link_bezier(v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE); } GPU_blend(GPU_BLEND_NONE); @@ -809,12 +809,10 @@ static void node_socket_outline_color_get(const bool selected, float r_outline_color[4]) { if (selected) { - UI_GetThemeColor4fv(TH_TEXT_HI, r_outline_color); - r_outline_color[3] = 0.9f; + UI_GetThemeColor4fv(TH_ACTIVE, r_outline_color); } else { - copy_v4_fl(r_outline_color, 0.0f); - r_outline_color[3] = 0.6f; + UI_GetThemeColor4fv(TH_WIRE, r_outline_color); } /* Until there is a better place for per socket color, @@ -834,11 +832,6 @@ void node_socket_color_get( RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); sock->typeinfo->draw_color(C, &ptr, node_ptr, r_color); - - bNode *node = (bNode *)node_ptr->data; - if (node->flag & NODE_MUTED) { - r_color[3] *= 0.25f; - } } struct SocketTooltipData { @@ -1174,7 +1167,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); - immUniform1f("outline_scale", 0.7f); + immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", -1.0f, -1.0f); /* Single point. */ @@ -1319,7 +1312,7 @@ void node_draw_sockets(const View2D *v2d, GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); - immUniform1f("outline_scale", 0.7f); + immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", -1.0f, -1.0f); /* Set handle size. */ @@ -1613,24 +1606,13 @@ static void node_draw_basis(const bContext *C, /* Shadow. */ node_draw_shadow(snode, node, BASIS_RAD, 1.0f); + rctf *rct = &node->totr; float color[4]; int color_id = node_get_colorid(node); - if (node->flag & NODE_MUTED) { - /* Muted nodes are semi-transparent and colorless. */ - UI_GetThemeColor3fv(TH_NODE, color); - color[3] = 0.25f; - } - else { - /* Opaque headers for regular nodes. */ - UI_GetThemeColor3fv(color_id, color); - color[3] = 1.0f; - } GPU_line_width(1.0f); - rctf *rct = &node->totr; - UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT); - + /* Header. */ { const rctf rect = { rct->xmin, @@ -1638,7 +1620,19 @@ static void node_draw_basis(const bContext *C, rct->ymax - NODE_DY, rct->ymax, }; - UI_draw_roundbox_aa(&rect, true, BASIS_RAD, color); + + float color_header[4]; + + /* Muted nodes get a mix of the background with the node color. */ + if (node->flag & NODE_MUTED) { + UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, 0.1f, 0, color_header); + } + else { + UI_GetThemeColorBlendShade4fv(TH_NODE, color_id, 0.6f, -40, color_header); + } + + UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT); + UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color_header); } /* Show/hide icons. */ @@ -1721,31 +1715,28 @@ static void node_draw_basis(const bContext *C, UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color); } - /* Open/close entirely. */ + /* Collapse/expand icon. */ { - int but_size = U.widget_unit * 0.8f; - /* XXX button uses a custom triangle draw below, so make it invisible without icon. */ + const int but_size = U.widget_unit * 0.8f; UI_block_emboss_set(node->block, UI_EMBOSS_NONE); - uiBut *but = uiDefBut(node->block, - UI_BTYPE_BUT_TOGGLE, - 0, - "", - rct->xmin + 0.35f * U.widget_unit, - rct->ymax - NODE_DY / 2.2f - but_size / 2, - but_size, - but_size, - nullptr, - 0, - 0, - 0, - 0, - ""); + + uiBut *but = uiDefIconBut(node->block, + UI_BTYPE_BUT_TOGGLE, + 0, + ICON_DOWNARROW_HLT, + rct->xmin + (NODE_MARGIN_X / 3), + rct->ymax - NODE_DY / 2.2f - but_size / 2, + but_size, + but_size, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle"); UI_block_emboss_set(node->block, UI_EMBOSS); - - UI_GetThemeColor4fv(TH_TEXT, color); - /* Custom draw function for this button. */ - UI_draw_icon_tri(rct->xmin + 0.65f * U.widget_unit, rct->ymax - NODE_DY / 2.2f, 'v', color); } char showname[128]; @@ -1755,7 +1746,7 @@ static void node_draw_basis(const bContext *C, UI_BTYPE_LABEL, 0, showname, - (int)(rct->xmin + NODE_MARGIN_X), + (int)(rct->xmin + NODE_MARGIN_X + 0.4f), (int)(rct->ymax - NODE_DY), (short)(iconofs - rct->xmin - (18.0f * U.dpi_fac)), (short)NODE_DY, @@ -1769,49 +1760,97 @@ static void node_draw_basis(const bContext *C, UI_but_flag_enable(but, UI_BUT_INACTIVE); } - /* Body. */ - if (nodeTypeUndefined(node)) { - /* Use warning color to indicate undefined types. */ - UI_GetThemeColor4fv(TH_REDALERT, color); - } - else if (node->flag & NODE_MUTED) { - /* Muted nodes are semi-transparent and colorless. */ - UI_GetThemeColor4fv(TH_NODE, color); - } - else if (node->flag & NODE_CUSTOM_COLOR) { - rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); - } - else { - UI_GetThemeColor4fv(TH_NODE, color); - } - + /* Wire across the node when muted/disabled. */ if (node->flag & NODE_MUTED) { - color[3] = 0.5f; + node_draw_mute_line(v2d, snode, node); } + /* Body. */ + const float outline_width = 1.0f; { - UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT); + /* Use warning color to indicate undefined types. */ + if (nodeTypeUndefined(node)) { + UI_GetThemeColorBlendShade4fv(TH_REDALERT, color_id, 0.05f, -80, color); + } + /* Muted nodes get a mix of the background with the node color. */ + else if (node->flag & NODE_MUTED) { + UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.33f, 0, color); + } + else if (node->flag & NODE_CUSTOM_COLOR) { + rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); + } + else { + UI_GetThemeColor4fv(TH_NODE, color); + } + + /* Draw selected nodes fully opaque. */ + if (node->flag & SELECT) { + color[3] = 1.0f; + } + + /* Draw muted nodes slightly transparent so the wires inside are visible. */ + if (node->flag & NODE_MUTED) { + color[3] -= 0.2f; + } + const rctf rect = { rct->xmin, rct->xmax, rct->ymin, + rct->ymax - (NODE_DY + outline_width), + }; + + UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT); + UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color); + } + + /* Header underline. */ + { + float color_underline[4]; + + if (node->flag & NODE_MUTED) { + UI_GetThemeColor4fv(TH_WIRE, color_underline); + } + else { + UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, 0.4f, -30, color_underline); + color_underline[3] = 1.0f; + } + + const rctf rect = { + rct->xmin, + rct->xmax, + rct->ymax - (NODE_DY + outline_width), rct->ymax - NODE_DY, }; - UI_draw_roundbox_aa(&rect, true, BASIS_RAD, color); + + UI_draw_roundbox_corner_set(UI_CNR_NONE); + UI_draw_roundbox_4fv(&rect, true, 0.0f, color_underline); } - /* Outline active and selected emphasis. */ - if (node->flag & SELECT) { - UI_GetThemeColorShadeAlpha4fv( - (node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, 0, -40, color); + /* Outline. */ + { + const rctf rect = { + rct->xmin - outline_width, + rct->xmax + outline_width, + rct->ymin - outline_width, + rct->ymax + outline_width, + }; + + /* Color the outline according to active, selected, or undefined status. */ + float color_outline[4]; + + if (node->flag & SELECT) { + UI_GetThemeColor4fv((node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline); + } + else if (nodeTypeUndefined(node)) { + UI_GetThemeColor4fv(TH_REDALERT, color_outline); + } + else { + UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline); + } UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(rct, false, BASIS_RAD, color); - } - - /* Disable lines. */ - if (node->flag & NODE_MUTED) { - node_draw_mute_line(v2d, snode, node); + UI_draw_roundbox_4fv(&rect, false, BASIS_RAD, color_outline); } node_draw_sockets(v2d, C, ntree, node, true, false); @@ -1846,46 +1885,45 @@ static void node_draw_hidden(const bContext *C, float scale; UI_view2d_scale_get(v2d, &scale, nullptr); + const int color_id = node_get_colorid(node); + /* Shadow. */ node_draw_shadow(snode, node, hiddenrad, 1.0f); + /* Wire across the node when muted/disabled. */ + if (node->flag & NODE_MUTED) { + node_draw_mute_line(v2d, snode, node); + } + /* Body. */ float color[4]; - int color_id = node_get_colorid(node); - if (node->flag & NODE_MUTED) { - /* Muted nodes are semi-transparent and colorless. */ - UI_GetThemeColor4fv(TH_NODE, color); - color[3] = 0.25f; - } - else { - UI_GetThemeColor4fv(color_id, color); - } + { + if (nodeTypeUndefined(node)) { + /* Use warning color to indicate undefined types. */ + UI_GetThemeColorBlendShade4fv(TH_REDALERT, color_id, 0.05f, -80, color); + } + else if (node->flag & NODE_MUTED) { + /* Muted nodes get a mix of the background with the node color. */ + UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, 0.1f, 0, color); + } + else if (node->flag & NODE_CUSTOM_COLOR) { + rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); + } + else { + UI_GetThemeColorBlendShade4fv(TH_NODE, color_id, 0.6f, -40, color); + } - UI_draw_roundbox_aa(rct, true, hiddenrad, color); + /* Draw selected nodes fully opaque. */ + if (node->flag & SELECT) { + color[3] = 1.0f; + } - /* Outline active and selected emphasis. */ - if (node->flag & SELECT) { - UI_GetThemeColorShadeAlpha4fv( - (node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, 0, -40, color); + /* Draw muted nodes slightly transparent so the wires inside are visible. */ + if (node->flag & NODE_MUTED) { + color[3] -= 0.2f; + } - UI_draw_roundbox_aa(rct, false, hiddenrad, color); - } - - /* Custom color inline. */ - if (node->flag & NODE_CUSTOM_COLOR) { - GPU_blend(GPU_BLEND_ALPHA); - GPU_line_smooth(true); - - const rctf rect = { - rct->xmin + 1, - rct->xmax - 1, - rct->ymin + 1, - rct->ymax - 1, - }; - UI_draw_roundbox_3fv_alpha(&rect, false, hiddenrad, node->color, 1.0f); - - GPU_line_smooth(false); - GPU_blend(GPU_BLEND_NONE); + UI_draw_roundbox_4fv(rct, true, hiddenrad, color); } /* Title. */ @@ -1896,36 +1934,28 @@ static void node_draw_hidden(const bContext *C, UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color); } - /* Open / collapse icon. */ + /* Collapse/expand icon. */ { - int but_size = U.widget_unit * 0.8f; - /* XXX button uses a custom triangle draw below, so make it invisible without icon */ + const int but_size = U.widget_unit * 1.0f; UI_block_emboss_set(node->block, UI_EMBOSS_NONE); - uiBut *but = uiDefBut(node->block, - UI_BTYPE_BUT_TOGGLE, - 0, - "", - rct->xmin + 0.35f * U.widget_unit, - centy - but_size / 2, - but_size, - but_size, - nullptr, - 0, - 0, - 0, - 0, - ""); + + uiBut *but = uiDefIconBut(node->block, + UI_BTYPE_BUT_TOGGLE, + 0, + ICON_RIGHTARROW, + rct->xmin + (NODE_MARGIN_X / 3), + centy - but_size / 2, + but_size, + but_size, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle"); UI_block_emboss_set(node->block, UI_EMBOSS); - - UI_GetThemeColor4fv(TH_TEXT, color); - /* Custom draw function for this button. */ - UI_draw_icon_tri(rct->xmin + 0.65f * U.widget_unit, centy, 'h', color); - } - - /* Disable lines. */ - if (node->flag & NODE_MUTED) { - node_draw_mute_line(v2d, snode, node); } char showname[128]; @@ -1945,15 +1975,44 @@ static void node_draw_hidden(const bContext *C, 0, 0, ""); + + /* Outline. */ + { + const float outline_width = 1.0f; + const rctf rect = { + rct->xmin - outline_width, + rct->xmax + outline_width, + rct->ymin - outline_width, + rct->ymax + outline_width, + }; + + /* Color the outline according to active, selected, or undefined status. */ + float color_outline[4]; + + if (node->flag & SELECT) { + UI_GetThemeColor4fv((node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline); + } + else if (nodeTypeUndefined(node)) { + UI_GetThemeColor4fv(TH_REDALERT, color_outline); + } + else { + UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline); + } + + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_4fv(&rect, false, hiddenrad, color_outline); + } + if (node->flag & NODE_MUTED) { UI_but_flag_enable(but, UI_BUT_INACTIVE); } /* Scale widget thing. */ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + GPU_blend(GPU_BLEND_ALPHA); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColorShade(color_id, -10); + immUniformThemeColorShadeAlpha(TH_TEXT, -40, -180); float dx = 10.0f; immBegin(GPU_PRIM_LINES, 4); @@ -1964,7 +2023,7 @@ static void node_draw_hidden(const bContext *C, immVertex2f(pos, rct->xmax - dx - 3.0f * snode->runtime->aspect, centy + 4.0f); immEnd(); - immUniformThemeColorShade(color_id, 30); + immUniformThemeColorShadeAlpha(TH_TEXT, 0, -180); dx -= snode->runtime->aspect; immBegin(GPU_PRIM_LINES, 4); @@ -1976,6 +2035,7 @@ static void node_draw_hidden(const bContext *C, immEnd(); immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); node_draw_sockets(v2d, C, ntree, node, true, false); diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index e7d9a12a52c..c0d50e753ff 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -332,7 +332,7 @@ extern const char *node_context_dir[]; #define NODE_SOCKDY (0.1f * U.widget_unit) #define NODE_WIDTH(node) (node->width * UI_DPI_FAC) #define NODE_HEIGHT(node) (node->height * UI_DPI_FAC) -#define NODE_MARGIN_X (1.10f * U.widget_unit) +#define NODE_MARGIN_X (1.2f * U.widget_unit) #define NODE_SOCKSIZE (0.25f * U.widget_unit) #define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit) #define NODE_RESIZE_MARGIN (0.20f * U.widget_unit) From 63de6078daa6a757840f1b46b43670a5af4a91b0 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 19:55:51 +0200 Subject: [PATCH 1239/1500] Fix objects not appearing in Outliner after dragging in from Asset Browser When dragging in an object from an external asset library from the Asset Browser, the Outliner wouldn't update. --- source/blender/editors/space_view3d/space_view3d.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 0ab39d59aff..92a5efa2268 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -747,6 +747,8 @@ static void view3d_ob_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop) ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + RNA_string_set(drop->ptr, "name", id->name + 2); Base *base = BKE_view_layer_base_find(view_layer, (Object *)id); From 18ace3b541eec120ad75fa7bbbaa15d9fb9b6c12 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 19:56:53 +0200 Subject: [PATCH 1240/1500] Fix linked objects not appearing after dragging in from Asset Browser When the Asset Browser import type was set to "Link", after dragging in an object asset the object wouldn't actually appear in the viewport. Do the same depsgraph tagging (and TODO comment) as the `OBJECT_OT_add_named` operator, which does similar things. --- source/blender/editors/space_view3d/space_view3d.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 92a5efa2268..559e11e96a1 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -85,6 +85,7 @@ #endif #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "view3d_intern.h" /* own include */ @@ -747,6 +748,8 @@ static void view3d_ob_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop) ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT); + /* TODO(sergey): Only update relations for the current scene. */ + DEG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); RNA_string_set(drop->ptr, "name", id->name + 2); From f81c514bd22a25b2c6951e506f64252e0f5e84bf Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 20:25:19 +0200 Subject: [PATCH 1241/1500] Assets: Disable snap-dragging for linking object assets The location of a linked object isn't editable, or at least it will be reset when reloading the file. So the drag & drop shouldn't even pretend like this would work, so disable the snapping of the object and the bounding-box to show the snapped object location while dragging. --- source/blender/editors/object/object_add.c | 7 ++++++ .../editors/space_view3d/space_view3d.c | 22 ++++++++++++++----- source/blender/windowmanager/WM_api.h | 1 + .../windowmanager/intern/wm_dragdrop.c | 10 +++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index f5a304a718b..be8fe3e6db7 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -3617,6 +3617,13 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "Object not found"); return OPERATOR_CANCELLED; } + + /* Don't transform a linked object. There's just nothing to do here in this case, so return + * #OPERATOR_FINISHED. */ + if (ID_IS_LINKED(ob)) { + return OPERATOR_FINISHED; + } + /* Ensure the locations are updated so snap reads the evaluated active location. */ CTX_data_ensure_evaluated_depsgraph(C); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 559e11e96a1..37d013e7bd9 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -526,6 +526,12 @@ static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag) return; } + /* Don't use the snap cursor when linking the object. Object transform isn't editable then and + * would be reset on reload. */ + if (WM_drag_asset_will_import_linked(drag)) { + return; + } + state = drop->draw_data = ED_view3d_cursor_snap_active(); state->draw_plane = true; @@ -552,8 +558,10 @@ static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag) static void view3d_ob_drop_draw_deactivate(struct wmDropBox *drop, wmDrag *UNUSED(drag)) { V3DSnapCursorState *state = drop->draw_data; - ED_view3d_cursor_snap_deactive(state); - drop->draw_data = NULL; + if (state) { + ED_view3d_cursor_snap_deactive(state); + drop->draw_data = NULL; + } } static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) @@ -762,12 +770,14 @@ static void view3d_ob_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); ED_outliner_select_sync_from_object_tag(C); - V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); - float obmat_final[4][4]; + V3DSnapCursorState *snap_state = drop->draw_data; + if (snap_state) { + float obmat_final[4][4]; - view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); - RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); + } } static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index f9bc309af9f..26db02be289 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -752,6 +752,7 @@ ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); /* ID drag and drop */ ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, int flag_extra); +bool WM_drag_asset_will_import_linked(const wmDrag *drag); void WM_drag_add_local_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent); struct ID *WM_drag_get_local_ID(const struct wmDrag *drag, short idcode); struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short idcode); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 21f5925b037..638aca6c98e 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -577,6 +577,16 @@ ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra) return NULL; } +bool WM_drag_asset_will_import_linked(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_ASSET) { + return false; + } + + const wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + return asset_drag->import_type == FILE_ASSET_IMPORT_LINK; +} + /** * When dragging a local ID, return that. Otherwise, if dragging an asset-handle, link or append * that depending on what was chosen by the drag-box (currently append only in fact). From 35aa3bf22d4e1780cd4560d9ebafebb84384b085 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 26 Oct 2021 13:39:09 -0500 Subject: [PATCH 1242/1500] Cleanup: Restore alphabetical order --- source/blender/nodes/CMakeLists.txt | 4 ++-- source/blender/nodes/NOD_geometry.h | 24 +++++++++---------- source/blender/nodes/NOD_static_types.h | 32 ++++++++++++------------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 9d35b7b9bff..94becccb452 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -232,8 +232,8 @@ set(SRC geometry/nodes/node_geo_input_curve_handles.cc geometry/nodes/node_geo_input_curve_tilt.cc geometry/nodes/node_geo_input_index.cc - geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_material_index.cc + geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_normal.cc geometry/nodes/node_geo_input_position.cc geometry/nodes/node_geo_input_radius.cc @@ -272,8 +272,8 @@ set(SRC geometry/nodes/node_geo_set_curve_handles.cc geometry/nodes/node_geo_set_curve_radius.cc geometry/nodes/node_geo_set_curve_tilt.cc - geometry/nodes/node_geo_set_material.cc geometry/nodes/node_geo_set_material_index.cc + geometry/nodes/node_geo_set_material.cc geometry/nodes/node_geo_set_point_radius.cc geometry/nodes/node_geo_set_position.cc geometry/nodes/node_geo_set_shade_smooth.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d6f0b511861..f5c7116949a 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -29,24 +29,24 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); void register_node_type_geo_custom_group(bNodeType *ntype); -void register_node_type_geo_legacy_attribute_transfer(void); -void register_node_type_geo_legacy_curve_set_handles(void); void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); +void register_node_type_geo_legacy_attribute_transfer(void); +void register_node_type_geo_legacy_curve_endpoints(void); +void register_node_type_geo_legacy_curve_reverse(void); +void register_node_type_geo_legacy_curve_set_handles(void); +void register_node_type_geo_legacy_curve_spline_type(void); +void register_node_type_geo_legacy_curve_subdivide(void); void register_node_type_geo_legacy_curve_to_points(void); void register_node_type_geo_legacy_delete_geometry(void); +void register_node_type_geo_legacy_edge_split(void); void register_node_type_geo_legacy_material_assign(void); void register_node_type_geo_legacy_mesh_to_curve(void); void register_node_type_geo_legacy_points_to_volume(void); -void register_node_type_geo_legacy_select_by_material(void); -void register_node_type_geo_legacy_curve_endpoints(void); -void register_node_type_geo_legacy_curve_spline_type(void); -void register_node_type_geo_legacy_curve_reverse(void); -void register_node_type_geo_legacy_select_by_handle_type(void); -void register_node_type_geo_legacy_curve_subdivide(void); -void register_node_type_geo_legacy_edge_split(void); -void register_node_type_geo_legacy_subdivision_surface(void); void register_node_type_geo_legacy_raycast(void); +void register_node_type_geo_legacy_select_by_handle_type(void); +void register_node_type_geo_legacy_select_by_material(void); +void register_node_type_geo_legacy_subdivision_surface(void); void register_node_type_geo_legacy_volume_to_mesh(void); void register_node_type_geo_align_rotation_to_vector(void); @@ -99,8 +99,8 @@ void register_node_type_geo_image_texture(void); void register_node_type_geo_input_curve_handles(void); void register_node_type_geo_input_curve_tilt(void); void register_node_type_geo_input_index(void); -void register_node_type_geo_input_material(void); void register_node_type_geo_input_material_index(void); +void register_node_type_geo_input_material(void); void register_node_type_geo_input_normal(void); void register_node_type_geo_input_position(void); void register_node_type_geo_input_radius(void); @@ -147,8 +147,8 @@ void register_node_type_geo_separate_geometry(void); void register_node_type_geo_set_curve_handles(void); void register_node_type_geo_set_curve_radius(void); void register_node_type_geo_set_curve_tilt(void); -void register_node_type_geo_set_material(void); void register_node_type_geo_set_material_index(void); +void register_node_type_geo_set_material(void); void register_node_type_geo_set_point_radius(void); void register_node_type_geo_set_position(void); void register_node_type_geo_set_shade_smooth(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index af9685fcf0d..230eb517f3f 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -323,20 +323,16 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_M DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "") -DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "") -DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") +DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "") -DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "") -DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") -DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "") @@ -344,23 +340,20 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMI DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") -DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "") -DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "") -DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") -DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") -DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "") -DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "") +DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "") +DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "") DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "") +DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") -DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "") +DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "") @@ -373,8 +366,8 @@ DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", Inst DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", InstancesToPoints, "Instances to Points", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") -DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "") +DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") @@ -383,7 +376,6 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") -DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") @@ -392,28 +384,36 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "") DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") +DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "") +DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "") DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "") +DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "") DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "") -DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "") DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "") +DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "") DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "") DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "") DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "") +DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "") DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "") DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") +DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "") DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") From 319de793d79865603b3cdccec5933d2152e43bb3 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 26 Oct 2021 20:50:57 +0200 Subject: [PATCH 1243/1500] Fix T92508: cache invalidation bug in Set Position node The call to `attribute_try_get_for_output` does some cache invalidation internally. Under some circumstances the call to `position_evaluator.evaluate()` recomputed the caches (e.g. when the Normal node was used, the evaluated handle positions cache on curves were updated). After the positions have been updated in the Set Position node, the cache was not invalidated again., leading to incorrect rendering. The proper solution will be to do the cache invalidation in `OutputAttribute.save()` again. That is a bit more involved though. For now just reorder the code a bit to do the cache invalidation after the field has been computed. There is a follow up task: T92509. --- .../blender/nodes/geometry/nodes/node_geo_set_position.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 1b6b6a5bdd7..a20f14e1ee3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -45,10 +45,6 @@ static void set_position_in_component(GeometryComponent &component, selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - OutputAttribute_Typed positions = component.attribute_try_get_for_output( - "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - MutableSpan position_mutable = positions.as_span(); - fn::FieldEvaluator position_evaluator{field_context, &selection}; position_evaluator.add(position_field); position_evaluator.add(offset_field); @@ -60,6 +56,10 @@ static void set_position_in_component(GeometryComponent &component, const VArray &positions_input = position_evaluator.get_evaluated(0); const VArray &offsets_input = position_evaluator.get_evaluated(1); + OutputAttribute_Typed positions = component.attribute_try_get_for_output( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + MutableSpan position_mutable = positions.as_span(); + for (int i : selection) { position_mutable[i] = positions_input[i] + offsets_input[i]; } From 0cf9794c7ef0c8c7dfdad4a2e2006b3708e42a93 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 20:58:12 +0200 Subject: [PATCH 1244/1500] Assets: Rename "Default" asset library to "User Library" Feedback was that "Default" is a bit of a weird name, so switching it to "User Library". Added versioning code which won't be entirely bullet proof (e.g. will also rename libraries named "Default" by the user), but it doesn't have to be. Addresses T90298. --- source/blender/blenkernel/BKE_preferences.h | 3 +++ source/blender/blenkernel/intern/preferences.c | 3 ++- .../blender/blenloader/intern/versioning_userdef.c | 14 +++++++++++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index bd887c1ea0d..fce2d3178aa 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -29,6 +29,9 @@ extern "C" { struct UserDef; struct bUserAssetLibrary; +/** Name of the asset library added by default. */ +#define BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME DATA_("User Library") + struct bUserAssetLibrary *BKE_preferences_asset_library_add(struct UserDef *userdef, const char *name, const char *path) ATTR_NONNULL(1); diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c index 0b8e8d7c311..79a8b591f72 100644 --- a/source/blender/blenkernel/intern/preferences.c +++ b/source/blender/blenkernel/intern/preferences.c @@ -120,7 +120,8 @@ void BKE_preferences_asset_library_default_add(UserDef *userdef) return; } - bUserAssetLibrary *library = BKE_preferences_asset_library_add(userdef, DATA_("Default"), NULL); + bUserAssetLibrary *library = BKE_preferences_asset_library_add( + userdef, BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME, NULL); /* Add new "Default" library under '[doc_path]/Blender/Assets'. */ BLI_path_join( diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 9b81d8b19aa..df59cd3afa9 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -27,9 +27,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" -#ifdef WITH_INTERNATIONAL -# include "BLT_translation.h" -#endif +#include "BLT_translation.h" #include "DNA_anim_types.h" #include "DNA_collection_types.h" @@ -928,6 +926,16 @@ void blo_do_versions_userdef(UserDef *userdef) userdef->dupflag |= USER_DUP_SPEAKER; } + if (!USER_VERSION_ATLEAST(300, 40)) { + /* Rename the default asset library from "Default" to "User Library" */ + LISTBASE_FOREACH (bUserAssetLibrary *, asset_library, &userdef->asset_libraries) { + if (STREQ(asset_library->name, DATA_("Default"))) { + BKE_preferences_asset_library_name_set( + userdef, asset_library, BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME); + } + } + } + /** * Versioning code until next subversion bump goes here. * From 8422b24e1cc5a31719d2b4b96a823d4330f9da3a Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Tue, 26 Oct 2021 13:44:26 -0600 Subject: [PATCH 1245/1500] Fix: Build issue on windows Empty initializer is not appreciated by MSVC. --- source/blender/editors/object/object_add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index be8fe3e6db7..90caeecd91f 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -3630,7 +3630,7 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix"); if (RNA_property_is_set(op->ptr, prop_matrix)) { uint objects_len; - Object **objects = BKE_view_layer_array_selected_objects(view_layer, NULL, &objects_len, {}); + Object **objects = BKE_view_layer_array_selected_objects(view_layer, NULL, &objects_len, {0}); float matrix[4][4]; RNA_property_float_get_array(op->ptr, prop_matrix, &matrix[0][0]); From d040493cd48d3afce2d4b380c7ed5e518c3c5e6b Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 21:33:48 +0200 Subject: [PATCH 1246/1500] UI: Rename operator to open Preferences window Renames the operator from "Show Preferences" to "Open Preferences...". "Open" is more clear than "Show" (since they could be shown in-place). "..." is usually used in Blender to indicate that a new Window or popup will be opened. Note that vanilla Blender doesn't actually show this name anywhere, so this change shouldn't be visible. That may change, see D12894. --- source/blender/editors/screen/screen_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index fc1b0ed173e..e516c3ba2c3 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -5106,7 +5106,7 @@ static int userpref_show_exec(bContext *C, wmOperator *op) static void SCREEN_OT_userpref_show(struct wmOperatorType *ot) { /* identifiers */ - ot->name = "Show Preferences"; + ot->name = "Open Preferences..."; ot->description = "Edit user preferences and system settings"; ot->idname = "SCREEN_OT_userpref_show"; From ca2ae350ecb5eb154609fd673e3049b77d426774 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 26 Oct 2021 21:55:46 +0200 Subject: [PATCH 1247/1500] Asset Browser: Improve hint for asset library that isn't found We already show a message when showing an asset library whose path can't be found on disk. The red text was making it look like some fatal error happened. And the message could be a bit more useful generally. So this removes the red color of the text, (arguably) improves the text and adds a button as shortcut to open the Preferences with the asset library settings. Differential Revision: https://developer.blender.org/D12894 --- source/blender/editors/screen/screen_ops.c | 22 ++++++++ source/blender/editors/space_file/file_draw.c | 52 +++++++++++++------ .../blender/editors/space_file/file_intern.h | 2 +- .../blender/editors/space_file/space_file.c | 2 +- 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index e516c3ba2c3..66140cba9c6 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -5072,6 +5072,18 @@ static int userpref_show_exec(bContext *C, wmOperator *op) int sizex = (500 + UI_NAVIGATION_REGION_WIDTH) * UI_DPI_FAC; int sizey = 520 * UI_DPI_FAC; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "section"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + /* Set active section via RNA, so it can fail properly. */ + + PointerRNA pref_ptr; + RNA_pointer_create(NULL, &RNA_Preferences, &U, &pref_ptr); + PropertyRNA *active_section_prop = RNA_struct_find_property(&pref_ptr, "active_section"); + + RNA_property_enum_set(&pref_ptr, active_section_prop, RNA_property_enum_get(op->ptr, prop)); + RNA_property_update(C, &pref_ptr, active_section_prop); + } + /* changes context! */ if (WM_window_open(C, IFACE_("Blender Preferences"), @@ -5105,6 +5117,8 @@ static int userpref_show_exec(bContext *C, wmOperator *op) static void SCREEN_OT_userpref_show(struct wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Open Preferences..."; ot->description = "Edit user preferences and system settings"; @@ -5113,6 +5127,14 @@ static void SCREEN_OT_userpref_show(struct wmOperatorType *ot) /* api callbacks */ ot->exec = userpref_show_exec; ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */ + + prop = RNA_def_enum(ot->srna, + "section", + rna_enum_preference_section_items, + 0, + "", + "Section to activate in the Preferences"); + RNA_def_property_flag(prop, PROP_HIDDEN); } /** \} */ diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 5c6b753d4a5..2e2f0c146d6 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -243,8 +243,9 @@ static void file_draw_string(int sx, } /** - * \param r_sx, r_sy: The lower right corner of the last line drawn. AKA the cursor position on - * completion. + * \param r_sx, r_sy: The lower right corner of the last line drawn, plus the height of the last + * line. This is the cursor position on completion to allow drawing more text + * behind that. */ static void file_draw_string_multiline(int sx, int sy, @@ -1066,7 +1067,9 @@ void file_draw_list(const bContext *C, ARegion *region) layout->curr_size = params->thumbnail_size; } -static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion *region) +static void file_draw_invalid_library_hint(const bContext *C, + const SpaceFile *sfile, + ARegion *region) { const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); @@ -1074,9 +1077,7 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path)); uchar text_col[4]; - uchar text_alert_col[4]; UI_GetThemeColor4ubv(TH_TEXT, text_col); - UI_GetThemeColor4ubv(TH_REDALERT, text_alert_col); const View2D *v2d = ®ion->v2d; const int pad = sfile->layout->tile_border_x; @@ -1087,23 +1088,42 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion int sy = v2d->tot.ymax; { - const char *message = TIP_("Library not found"); - const int draw_string_str_len = strlen(message) + 2 + sizeof(library_ui_path); - char *draw_string = alloca(draw_string_str_len); - BLI_snprintf(draw_string, draw_string_str_len, "%s: %s", message, library_ui_path); - file_draw_string_multiline(sx, sy, draw_string, width, line_height, text_alert_col, NULL, &sy); + const char *message = TIP_("Path to asset library does not exist:"); + file_draw_string_multiline(sx, sy, message, width, line_height, text_col, NULL, &sy); + + sy -= line_height; + file_draw_string(sx, sy, library_ui_path, width, line_height, UI_STYLE_TEXT_LEFT, text_col); } - /* Next line, but separate it a bit further. */ - sy -= line_height; + /* Separate a bit further. */ + sy -= line_height * 2.2f; { UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO); const char *suggestion = TIP_( - "Set up the library or edit libraries in the Preferences, File Paths section"); + "Asset Libraries are local directories that can contain .blend files with assets inside.\n" + "Manage Asset Libraries from the File Paths section in Preferences."); file_draw_string_multiline( - sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, NULL); + sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, &sy); + + uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS); + uiBut *but = uiDefIconTextButO(block, + UI_BTYPE_BUT, + "SCREEN_OT_userpref_show", + WM_OP_INVOKE_DEFAULT, + ICON_PREFERENCES, + NULL, + sx + UI_UNIT_X, + sy - line_height - UI_UNIT_Y * 1.2f, + UI_UNIT_X * 8, + UI_UNIT_Y, + NULL); + PointerRNA *but_opptr = UI_but_operator_ptr_get(but); + RNA_enum_set(but_opptr, "section", USER_SECTION_FILE_PATHS); + + UI_block_end(C, block); + UI_block_draw(C, block); } } @@ -1111,7 +1131,7 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion * Draw a string hint if the file list is invalid. * \return true if the list is invalid and a hint was drawn. */ -bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) +bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region) { FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); /* Only for asset browser. */ @@ -1124,7 +1144,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) return false; } - file_draw_invalid_library_hint(sfile, region); + file_draw_invalid_library_hint(C, sfile, region); return true; } diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index ba08777e4e2..f6b5f0f47cd 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -47,7 +47,7 @@ struct View2D; void file_calc_previews(const bContext *C, ARegion *region); void file_draw_list(const bContext *C, ARegion *region); -bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region); +bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region); void file_draw_check_ex(bContext *C, struct ScrArea *area); void file_draw_check(bContext *C); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index a875b7a2c12..ef503708335 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -659,7 +659,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region) file_highlight_set(sfile, region, event->xy[0], event->xy[1]); } - if (!file_draw_hint_if_invalid(sfile, region)) { + if (!file_draw_hint_if_invalid(C, sfile, region)) { file_draw_list(C, region); } From 71fd0f7b7b0a136511949e25a185d90f5738749e Mon Sep 17 00:00:00 2001 From: Cian Jinks Date: Tue, 26 Oct 2021 20:29:01 +0100 Subject: [PATCH 1248/1500] Fix: Knife measurements broken when a cut point is in space Knife angle measurements were mis-aligned if a cut point was in space. Specifically, the arc drawing would not match with the cut line. Fixed by removing a correction for kcd->prev.cage. This correction was originally added for panning with measurements to work. In hindsight it is not needed and only introduces issues like this. --- source/blender/editors/mesh/editmesh_knife.c | 42 ++++++-------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index d073f5f2ba4..826a9ce2b6b 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -184,8 +184,6 @@ typedef struct KnifeMeasureData { float cage[3]; float mval[2]; bool is_stored; - float corr_prev_cage[3]; /* "knife_start_cut" updates prev.cage breaking angle calculations, - * store correct version. */ } KnifeMeasureData; typedef struct KnifeUndoFrame { @@ -496,7 +494,7 @@ static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd) const int distance_precision = 4; /* Calculate distance and convert to string. */ - const float cut_len = len_v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage); + const float cut_len = len_v3v3(kcd->prev.cage, kcd->curr.cage); UnitSettings *unit = &kcd->scene->unit; if (unit->system == USER_UNIT_NONE) { @@ -703,7 +701,7 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) else { tempkfv = tempkfe->v2; } - angle = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, tempkfv->cageco); + angle = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, tempkfv->cageco); if (angle < min_angle) { min_angle = angle; kfe = tempkfe; @@ -717,7 +715,7 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); knifetool_draw_angle(kcd, - kcd->mdata.corr_prev_cage, + kcd->prev.cage, kcd->curr.cage, end, kcd->prev.mval, @@ -730,11 +728,11 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) kfe = kcd->curr.edge; /* Check for most recent cut (if cage is part of previous cut). */ - if (!compare_v3v3(kfe->v1->cageco, kcd->mdata.corr_prev_cage, KNIFE_FLT_EPSBIG) && - !compare_v3v3(kfe->v2->cageco, kcd->mdata.corr_prev_cage, KNIFE_FLT_EPSBIG)) { + if (!compare_v3v3(kfe->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) && + !compare_v3v3(kfe->v2->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG)) { /* Determine acute angle. */ - float angle1 = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, kfe->v1->cageco); - float angle2 = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, kfe->v2->cageco); + float angle1 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v1->cageco); + float angle2 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v2->cageco); float angle; float *end; @@ -751,14 +749,8 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) float end_ss[2]; ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); - knifetool_draw_angle(kcd, - kcd->mdata.corr_prev_cage, - kcd->curr.cage, - end, - kcd->prev.mval, - kcd->curr.mval, - end_ss, - angle); + knifetool_draw_angle( + kcd, kcd->prev.cage, kcd->curr.cage, end, kcd->prev.mval, kcd->curr.mval, end_ss, angle); } } @@ -852,10 +844,10 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) kcd, kcd->curr.cage, kcd->prev.cage, end, kcd->curr.mval, kcd->prev.mval, end_ss, angle); } else if (kcd->mdata.is_stored && !kcd->prev.is_space) { - float angle = angle_v3v3v3(kcd->curr.cage, kcd->mdata.corr_prev_cage, kcd->mdata.cage); + float angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kcd->mdata.cage); knifetool_draw_angle(kcd, kcd->curr.cage, - kcd->mdata.corr_prev_cage, + kcd->prev.cage, kcd->mdata.cage, kcd->curr.mval, kcd->prev.mval, @@ -2396,7 +2388,7 @@ static void knife_add_cut(KnifeTool_OpData *kcd) } /* Save values for angle drawing calculations. */ - copy_v3_v3(kcd->mdata.cage, kcd->mdata.corr_prev_cage); + copy_v3_v3(kcd->mdata.cage, kcd->prev.cage); copy_v2_v2(kcd->mdata.mval, kcd->prev.mval); kcd->mdata.is_stored = true; @@ -4525,16 +4517,6 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->init = kcd->curr; } - /* Preserve correct prev.cage for angle drawing calculations. */ - if (kcd->prev.edge == NULL && kcd->prev.vert == NULL) { - /* "knife_start_cut" moves prev.cage so needs to be recalculated. */ - /* Only occurs if prev was started on a face. */ - knifetool_recast_cageco(kcd, kcd->prev.mval, kcd->mdata.corr_prev_cage); - } - else { - copy_v3_v3(kcd->mdata.corr_prev_cage, kcd->prev.cage); - } - /* Freehand drawing is incompatible with cut-through. */ if (kcd->cut_through == false) { kcd->is_drag_hold = true; From b37caa3f06440c3dd36c749950e50becfbddf2e8 Mon Sep 17 00:00:00 2001 From: Cian Jinks Date: Tue, 26 Oct 2021 21:21:22 +0100 Subject: [PATCH 1249/1500] Fix: Knife unused function warning --- source/blender/editors/mesh/editmesh_knife.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 826a9ce2b6b..49afe90af91 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1460,6 +1460,8 @@ static void knife_input_ray_segment(KnifeTool_OpData *kcd, ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); } +/* No longer used, but may be useful in the future. */ +#if 0 static void knifetool_recast_cageco(KnifeTool_OpData *kcd, float mval[3], float r_cage[3]) { float origin[3]; @@ -1474,6 +1476,7 @@ static void knifetool_recast_cageco(KnifeTool_OpData *kcd, float mval[3], float knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, NULL, co, r_cage, NULL); } +#endif static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) { From 7d3d09b69c0af677bd363cc009f37fdd1e4a7185 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 26 Oct 2021 15:40:57 -0500 Subject: [PATCH 1250/1500] Geometry Nodes: Get and set nodes for ID attribute These nodes allow accessing and changing the stable/random ID used for motion blur with instances and stable randomness. Since rB40c3b8836b7a, the stable ID is a built-in attribute, so to be consistent and allow changing it in the node tree like other built-in attributes, it has get and set nodes. --- release/scripts/startup/nodeitems_builtins.py | 45 ++++++--- source/blender/blenkernel/BKE_node.h | 2 + source/blender/blenkernel/intern/node.cc | 2 + source/blender/nodes/CMakeLists.txt | 2 + source/blender/nodes/NOD_geometry.h | 2 + source/blender/nodes/NOD_static_types.h | 2 + .../nodes/geometry/nodes/node_geo_input_id.cc | 42 +++++++++ .../nodes/geometry/nodes/node_geo_set_id.cc | 94 +++++++++++++++++++ 8 files changed, 176 insertions(+), 15 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_id.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_set_id.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 22fae8111fd..d822ed9599f 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -157,6 +157,34 @@ def mesh_node_items(context): yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeSetShadeSmooth") +# Custom Menu for Geometry Nodes "Geometry" category +def geometry_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + if not space.edit_tree: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_legacy_poll) + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeBoundBox") + yield NodeItem("GeometryNodeConvexHull") + yield NodeItem("GeometryNodeDeleteGeometry") + yield NodeItem("GeometryNodeProximity") + yield NodeItem("GeometryNodeJoinGeometry") + yield NodeItem("GeometryNodeRaycast") + yield NodeItem("GeometryNodeSeparateComponents") + yield NodeItem("GeometryNodeSeparateGeometry") + yield NodeItem("GeometryNodeTransform") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeSetID") + yield NodeItem("GeometryNodeSetPosition") + # Custom Menu for Geometry Node Input Nodes def geometry_input_node_items(context): if context is None: @@ -182,6 +210,7 @@ def geometry_input_node_items(context): yield NodeItem("ShaderNodeValue") yield NodeItem("FunctionNodeInputVector") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeInputID") yield NodeItem("GeometryNodeInputIndex") yield NodeItem("GeometryNodeInputNormal") yield NodeItem("GeometryNodeInputPosition") @@ -677,21 +706,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurvePrimitiveQuadrilateral"), NodeItem("GeometryNodeCurvePrimitiveBezierSegment"), ]), - GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ - NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_legacy_poll), - - NodeItem("GeometryNodeRaycast"), - NodeItem("GeometryNodeProximity"), - NodeItem("GeometryNodeBoundBox"), - NodeItem("GeometryNodeConvexHull"), - NodeItem("GeometryNodeDeleteGeometry"), - NodeItem("GeometryNodeTransform"), - NodeItem("GeometryNodeJoinGeometry"), - NodeItem("GeometryNodeSeparateComponents"), - NodeItem("GeometryNodeSeparateGeometry"), - NodeItem("GeometryNodeSetPosition"), - ]), + GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=geometry_node_items), GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items), GeometryNodeCategory("GEO_INSTANCE", "Instances", items=[ NodeItem("GeometryNodeInstanceOnPoints"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 8daa96164ef..58fea6d462c 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1549,6 +1549,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_INSTANCES_TO_POINTS 1131 #define GEO_NODE_IMAGE_TEXTURE 1132 #define GEO_NODE_VOLUME_TO_MESH 1133 +#define GEO_NODE_INPUT_ID 1134 +#define GEO_NODE_SET_ID 1135 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 8494c51a66e..d6c4e1f21e4 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5804,6 +5804,7 @@ static void registerGeometryNodes() register_node_type_geo_image_texture(); register_node_type_geo_input_curve_handles(); register_node_type_geo_input_curve_tilt(); + register_node_type_geo_input_id(); register_node_type_geo_input_index(); register_node_type_geo_input_material_index(); register_node_type_geo_input_material(); @@ -5852,6 +5853,7 @@ static void registerGeometryNodes() register_node_type_geo_set_curve_handles(); register_node_type_geo_set_curve_radius(); register_node_type_geo_set_curve_tilt(); + register_node_type_geo_set_id(); register_node_type_geo_set_material_index(); register_node_type_geo_set_material(); register_node_type_geo_set_point_radius(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 94becccb452..f4ca9f51b1b 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -231,6 +231,7 @@ set(SRC geometry/nodes/node_geo_image_texture.cc geometry/nodes/node_geo_input_curve_handles.cc geometry/nodes/node_geo_input_curve_tilt.cc + geometry/nodes/node_geo_input_id.cc geometry/nodes/node_geo_input_index.cc geometry/nodes/node_geo_input_material_index.cc geometry/nodes/node_geo_input_material.cc @@ -272,6 +273,7 @@ set(SRC geometry/nodes/node_geo_set_curve_handles.cc geometry/nodes/node_geo_set_curve_radius.cc geometry/nodes/node_geo_set_curve_tilt.cc + geometry/nodes/node_geo_set_id.cc geometry/nodes/node_geo_set_material_index.cc geometry/nodes/node_geo_set_material.cc geometry/nodes/node_geo_set_point_radius.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index f5c7116949a..ea3458af065 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -98,6 +98,7 @@ void register_node_type_geo_edge_split(void); void register_node_type_geo_image_texture(void); void register_node_type_geo_input_curve_handles(void); void register_node_type_geo_input_curve_tilt(void); +void register_node_type_geo_input_id(void); void register_node_type_geo_input_index(void); void register_node_type_geo_input_material_index(void); void register_node_type_geo_input_material(void); @@ -147,6 +148,7 @@ void register_node_type_geo_separate_geometry(void); void register_node_type_geo_set_curve_handles(void); void register_node_type_geo_set_curve_radius(void); void register_node_type_geo_set_curve_tilt(void); +void register_node_type_geo_set_id(void); void register_node_type_geo_set_material_index(void); void register_node_type_geo_set_material(void); void register_node_type_geo_set_point_radius(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 230eb517f3f..20ad4d359f1 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -351,6 +351,7 @@ DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "") +DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") @@ -395,6 +396,7 @@ DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SE DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "") +DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "") DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "") DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "") DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc new file mode 100644 index 00000000000..049dbf06d80 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_id_declare(NodeDeclarationBuilder &b) +{ + b.add_output("ID").field_source(); +} + +static void geo_node_input_id_exec(GeoNodeExecParams params) +{ + Field position_field{AttributeFieldInput::Create("id")}; + params.set_output("ID", std::move(position_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_id() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_ID, "ID", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_id_exec; + ntype.declare = blender::nodes::geo_node_input_id_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc new file mode 100644 index 00000000000..f8edfa145d9 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -0,0 +1,94 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_id_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Selection").default_value(true).hide_value().supports_field(); + b.add_input("ID").supports_field(); + b.add_output("Geometry"); +} + +static void set_id_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &id_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + /* Since adding the ID attribute can change the result of the field evaluation (the random value + * node uses the index if the ID is unavailable), make sure that it isn't added before evaluating + * the field. However, as an optimization, use a faster code path when it already exists. */ + fn::FieldEvaluator id_evaluator{field_context, &selection}; + if (component.attribute_exists("id")) { + OutputAttribute_Typed id_attribute = component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT); + id_evaluator.add_with_destination(id_field, id_attribute.varray()); + id_evaluator.evaluate(); + id_attribute.save(); + } + else { + id_evaluator.add(id_field); + id_evaluator.evaluate(); + const VArray &result_ids = id_evaluator.get_evaluated(0); + OutputAttribute_Typed id_attribute = component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT); + result_ids.materialize(selection, id_attribute.as_span()); + id_attribute.save(); + } +} + +static void geo_node_set_id_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field id_field = params.extract_input>("ID"); + + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_INSTANCES, + GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE}) { + if (geometry_set.has(type)) { + set_id_in_component(geometry_set.get_component_for_write(type), selection_field, id_field); + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_id() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_ID, "Set ID", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_id_exec; + ntype.declare = blender::nodes::geo_node_set_id_declare; + nodeRegisterType(&ntype); +} From e7fedf6dba5fe2ec39260943361915a6b2b8270a Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Tue, 26 Oct 2021 14:49:22 -0600 Subject: [PATCH 1251/1500] Revert "Eevee: support accessing custom mesh attributes" This reverts commit 03013d19d16704672f9db93bc62547651b6a5cb8. This commit broke the windows build pretty badly and I don't feel confident landing the fix for this without review. Will post a possible fix in D12969 and we'll take it from there. --- .../blender/blenkernel/intern/mesh_runtime.c | 10 - source/blender/draw/CMakeLists.txt | 1 - .../blender/draw/intern/draw_cache_extract.h | 23 +- .../draw/intern/draw_cache_extract_mesh.cc | 3 - .../draw/intern/draw_cache_impl_mesh.c | 352 +++------------- .../intern/mesh_extractors/extract_mesh.h | 1 - .../extract_mesh_vbo_attributes.cc | 398 ------------------ .../mesh_extractors/extract_mesh_vbo_vcol.cc | 64 ++- source/blender/gpu/GPU_batch.h | 8 +- source/blender/makesdna/DNA_mesh_types.h | 4 - 10 files changed, 123 insertions(+), 741 deletions(-) delete mode 100644 source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c index 1c8646a4bdd..7ac4c29f0ee 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.c +++ b/source/blender/blenkernel/intern/mesh_runtime.c @@ -52,8 +52,6 @@ void BKE_mesh_runtime_reset(Mesh *mesh) memset(&mesh->runtime, 0, sizeof(mesh->runtime)); mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); BLI_mutex_init(mesh->runtime.eval_mutex); - mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); - BLI_mutex_init(mesh->runtime.render_mutex); } /* Clear all pointers which we don't want to be shared on copying the datablock. @@ -73,9 +71,6 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag)) mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); BLI_mutex_init(mesh->runtime.eval_mutex); - - mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); - BLI_mutex_init(mesh->runtime.render_mutex); } void BKE_mesh_runtime_clear_cache(Mesh *mesh) @@ -85,11 +80,6 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh) MEM_freeN(mesh->runtime.eval_mutex); mesh->runtime.eval_mutex = NULL; } - if (mesh->runtime.render_mutex != NULL) { - BLI_mutex_end(mesh->runtime.render_mutex); - MEM_freeN(mesh->runtime.render_mutex); - mesh->runtime.render_mutex = NULL; - } if (mesh->runtime.mesh_eval != NULL) { mesh->runtime.mesh_eval->edit_mesh = NULL; BKE_id_free(NULL, mesh->runtime.mesh_eval); diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 0baf994d978..baf83234354 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -61,7 +61,6 @@ set(SRC intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc intern/mesh_extractors/extract_mesh_ibo_points.cc intern/mesh_extractors/extract_mesh_ibo_tris.cc - intern/mesh_extractors/extract_mesh_vbo_attributes.cc intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc intern/mesh_extractors/extract_mesh_vbo_edit_data.cc intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index ba42cdf66e7..a680cc0d6b7 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -24,10 +24,6 @@ struct TaskGraph; -#include "DNA_customdata_types.h" - -#include "BKE_attribute.h" - #include "GPU_batch.h" #include "GPU_index_buffer.h" #include "GPU_vertex_buffer.h" @@ -60,6 +56,7 @@ typedef struct DRW_MeshCDMask { uint32_t uv : 8; uint32_t tan : 8; uint32_t vcol : 8; + uint32_t sculpt_vcol : 8; uint32_t orco : 1; uint32_t tan_orco : 1; uint32_t sculpt_overlays : 1; @@ -67,10 +64,10 @@ typedef struct DRW_MeshCDMask { * modifiers could remove it. (see T68857) */ uint32_t edit_uv : 1; } DRW_MeshCDMask; -/* Keep `DRW_MeshCDMask` struct within an `uint32_t`. +/* Keep `DRW_MeshCDMask` struct within an `uint64_t`. * bit-wise and atomic operations are used to compare and update the struct. * See `mesh_cd_layers_type_*` functions. */ -BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint32_t), "DRW_MeshCDMask exceeds 32 bits") +BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits") typedef enum eMRIterType { MR_ITER_LOOPTRI = 1 << 0, MR_ITER_POLY = 1 << 1, @@ -79,17 +76,6 @@ typedef enum eMRIterType { } eMRIterType; ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) -typedef struct DRW_AttributeRequest { - CustomDataType cd_type; - int layer_index; - AttributeDomain domain; -} DRW_AttributeRequest; - -typedef struct DRW_MeshAttributes { - DRW_AttributeRequest requests[GPU_MAX_ATTR]; - int num_requests; -} DRW_MeshAttributes; - typedef enum eMRDataType { MR_DATA_NONE = 0, MR_DATA_POLY_NOR = 1 << 1, @@ -147,7 +133,6 @@ typedef struct MeshBufferList { GPUVertBuf *edge_idx; /* extend */ GPUVertBuf *poly_idx; GPUVertBuf *fdot_idx; - GPUVertBuf *attr[GPU_MAX_ATTR]; } vbo; /* Index Buffers: * Only need to be updated when topology changes. */ @@ -300,8 +285,6 @@ typedef struct MeshBatchCache { DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time; - DRW_MeshAttributes attr_used, attr_needed, attr_used_over_time; - int lastmatch; /* Valid only if edge_detection is up to date. */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index f3b72503907..06c449fe590 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -650,9 +650,6 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, EXTRACT_ADD_REQUESTED(vbo, vert_idx); EXTRACT_ADD_REQUESTED(vbo, fdot_idx); EXTRACT_ADD_REQUESTED(vbo, skin_roots); - for (int i = 0; i < GPU_MAX_ATTR; i++) { - EXTRACT_ADD_REQUESTED(vbo, attr[i]); - } EXTRACT_ADD_REQUESTED(ibo, tris); if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 847913927f7..18664498d00 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -41,7 +41,6 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" @@ -122,8 +121,6 @@ # define _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5) _MDEPS_ASSERT5(b, n1, n2, n3, n4); _MDEPS_ASSERT2(b, n5) # define _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6) _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5); _MDEPS_ASSERT2(b, n6) # define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7) -# define _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20) _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7); _MDEPS_ASSERT8(b, n8, n9, n10, n11, n12, n13, n14); _MDEPS_ASSERT7(b, n15, n16, n17, n18, n19, n20) -# define _MDEPS_ASSERT22(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20, n21) _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20); _MDEPS_ASSERT2(b, n21); # define MDEPS_ASSERT_FLAG(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__) # define MDEPS_ASSERT(batch_name, ...) MDEPS_ASSERT_FLAG(BATCH_FLAG(batch_name), __VA_ARGS__) @@ -195,21 +192,6 @@ static const DRWBatchFlag g_buffer_deps[] = { [BUFFER_INDEX(vbo.edge_idx)] = BATCH_FLAG(edit_selection_edges), [BUFFER_INDEX(vbo.poly_idx)] = BATCH_FLAG(edit_selection_faces), [BUFFER_INDEX(vbo.fdot_idx)] = BATCH_FLAG(edit_selection_fdots), - [BUFFER_INDEX(vbo.attr[0])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[1])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[2])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[3])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[4])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[5])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[6])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[7])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[8])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[9])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[10])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[11])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[12])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[13])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.attr[14])] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, [BUFFER_INDEX(ibo.tris)] = BATCH_FLAG(surface, surface_weights, @@ -258,12 +240,12 @@ static void mesh_batch_cache_discard_batch(MeshBatchCache *cache, const DRWBatch /* Return true is all layers in _b_ are inside _a_. */ BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return (*((uint32_t *)&a) & *((uint32_t *)&b)) == *((uint32_t *)&b); + return (*((uint64_t *)&a) & *((uint64_t *)&b)) == *((uint64_t *)&b); } BLI_INLINE bool mesh_cd_layers_type_equal(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return *((uint32_t *)&a) == *((uint32_t *)&b); + return *((uint64_t *)&a) == *((uint64_t *)&b); } BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) @@ -271,11 +253,12 @@ BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) uint32_t *a_p = (uint32_t *)a; uint32_t *b_p = (uint32_t *)&b; atomic_fetch_and_or_uint32(a_p, *b_p); + atomic_fetch_and_or_uint32(a_p + 1, *(b_p + 1)); } BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a) { - *((uint32_t *)a) = 0; + *((uint64_t *)a) = 0; } BLI_INLINE const Mesh *editmesh_final_or_this(const Mesh *me) @@ -288,95 +271,6 @@ static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *c cd_used->edit_uv = 1; } -/** \name DRW_MeshAttributes - * - * Utilities for handling requested attributes. - * \{ */ - -/* Return true if the given DRW_AttributeRequest is already in the requests. */ -static bool has_request(const DRW_MeshAttributes *requests, DRW_AttributeRequest req) -{ - for (int i = 0; i < requests->num_requests; i++) { - const DRW_AttributeRequest src_req = requests->requests[i]; - if (src_req.domain != req.domain) { - continue; - } - if (src_req.layer_index != req.layer_index) { - continue; - } - if (src_req.cd_type != req.cd_type) { - continue; - } - return true; - } - return false; -} - -static void mesh_attrs_merge_requests(const DRW_MeshAttributes *src_requests, - DRW_MeshAttributes *dst_requests) -{ - for (int i = 0; i < src_requests->num_requests; i++) { - if (dst_requests->num_requests == GPU_MAX_ATTR) { - return; - } - - if (has_request(dst_requests, src_requests->requests[i])) { - continue; - } - - dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; - dst_requests->num_requests += 1; - } -} - -static void drw_mesh_attributes_clear(DRW_MeshAttributes *attributes) -{ - memset(attributes, 0, sizeof(DRW_MeshAttributes)); -} - -static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst, - const DRW_MeshAttributes *src, - ThreadMutex *mesh_render_mutex) -{ - BLI_mutex_lock(mesh_render_mutex); - mesh_attrs_merge_requests(src, dst); - BLI_mutex_unlock(mesh_render_mutex); -} - -/* Return true if all requests in b are in a. */ -static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b) -{ - if (a->num_requests != b->num_requests) { - return false; - } - - for (int i = 0; i < a->num_requests; i++) { - if (!has_request(a, b->requests[i])) { - return false; - } - } - - return true; -} - -static void drw_mesh_attributes_add_request(DRW_MeshAttributes *attrs, - CustomDataType type, - int layer, - AttributeDomain domain) -{ - if (attrs->num_requests >= GPU_MAX_ATTR) { - return; - } - - DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; - req->cd_type = type; - req->layer_index = layer; - req->domain = domain; - attrs->num_requests += 1; -} - -/** \} */ - BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -392,36 +286,6 @@ BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) return &me->ldata; } -BLI_INLINE const CustomData *mesh_cd_pdata_get_from_mesh(const Mesh *me) -{ - switch ((eMeshWrapperType)me->runtime.wrapper_type) { - case ME_WRAPPER_TYPE_MDATA: - return &me->pdata; - break; - case ME_WRAPPER_TYPE_BMESH: - return &me->edit_mesh->bm->pdata; - break; - } - - BLI_assert(0); - return &me->pdata; -} - -BLI_INLINE const CustomData *mesh_cd_edata_get_from_mesh(const Mesh *me) -{ - switch ((eMeshWrapperType)me->runtime.wrapper_type) { - case ME_WRAPPER_TYPE_MDATA: - return &me->edata; - break; - case ME_WRAPPER_TYPE_BMESH: - return &me->edit_mesh->bm->edata; - break; - } - - BLI_assert(0); - return &me->edata; -} - BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -457,14 +321,14 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd } } -static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshAttributes *attrs_used) +static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used) { const Mesh *me_final = editmesh_final_or_this(me); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR); if (layer != -1) { - drw_mesh_attributes_add_request(attrs_used, CD_PROP_COLOR, layer, ATTR_DOMAIN_POINT); + cd_used->sculpt_vcol |= (1 << layer); } } @@ -479,45 +343,13 @@ static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *c } } -static bool custom_data_match_attribute(const CustomData *custom_data, - const char *name, - int *r_layer_index, - int *r_type) -{ - const int possible_attribute_types[6] = { - CD_PROP_BOOL, - CD_PROP_INT32, - CD_PROP_FLOAT, - CD_PROP_FLOAT2, - CD_PROP_FLOAT3, - CD_PROP_COLOR, - }; - - for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { - const int attr_type = possible_attribute_types[i]; - int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); - if (layer_index == -1) { - continue; - } - - *r_layer_index = layer_index; - *r_type = attr_type; - return true; - } - - return false; -} - static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, struct GPUMaterial **gpumat_array, - int gpumat_array_len, - DRW_MeshAttributes *attributes) + int gpumat_array_len) { const Mesh *me_final = editmesh_final_or_this(me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); - const CustomData *cd_pdata = mesh_cd_pdata_get_from_mesh(me_final); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); - const CustomData *cd_edata = mesh_cd_edata_get_from_mesh(me_final); /* See: DM_vertex_attributes_from_gpu for similar logic */ DRW_MeshCDMask cd_used; @@ -531,8 +363,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, const char *name = gpu_attr->name; int type = gpu_attr->type; int layer = -1; - /* ATTR_DOMAIN_NUM is standard for "invalid value". */ - AttributeDomain domain = ATTR_DOMAIN_NUM; if (type == CD_AUTO_FROM_NAME) { /* We need to deduct what exact layer is used. @@ -543,6 +373,13 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name); type = CD_MTFACE; + if (layer == -1) { + if (U.experimental.use_sculpt_vertex_colors) { + layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name); + type = CD_PROP_COLOR; + } + } + if (layer == -1) { layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name); type = CD_MCOL; @@ -554,27 +391,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, type = CD_TANGENT; } #endif - if (layer == -1) { - /* Try to match a generic attribute, we use the first attribute domain with a - * matching name. */ - if (custom_data_match_attribute(cd_vdata, name, &layer, &type)) { - domain = ATTR_DOMAIN_POINT; - } - else if (custom_data_match_attribute(cd_ldata, name, &layer, &type)) { - domain = ATTR_DOMAIN_CORNER; - } - else if (custom_data_match_attribute(cd_pdata, name, &layer, &type)) { - domain = ATTR_DOMAIN_FACE; - } - else if (custom_data_match_attribute(cd_edata, name, &layer, &type)) { - domain = ATTR_DOMAIN_EDGE; - } - else { - layer = -1; - domain = ATTR_DOMAIN_NUM; - } - } - if (layer == -1) { continue; } @@ -616,6 +432,31 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, } break; } + case CD_PROP_COLOR: { + /* Sculpt Vertex Colors */ + bool use_mloop_cols = false; + if (layer == -1) { + layer = (name[0] != '\0') ? + CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name) : + CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR); + /* Fallback to Vertex Color data */ + if (layer == -1) { + layer = (name[0] != '\0') ? + CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name) : + CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL); + use_mloop_cols = true; + } + } + if (layer != -1) { + if (use_mloop_cols) { + cd_used.vcol |= (1 << layer); + } + else { + cd_used.sculpt_vcol |= (1 << layer); + } + } + break; + } case CD_MCOL: { /* Vertex Color Data */ if (layer == -1) { @@ -632,17 +473,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, cd_used.orco = 1; break; } - case CD_PROP_BOOL: - case CD_PROP_INT32: - case CD_PROP_FLOAT: - case CD_PROP_FLOAT2: - case CD_PROP_FLOAT3: - case CD_PROP_COLOR: { - if (layer != -1 && domain != ATTR_DOMAIN_NUM) { - drw_mesh_attributes_add_request(attributes, type, layer, domain); - } - break; - } } } } @@ -1105,14 +935,14 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me) static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me) { - DRW_MeshAttributes attrs_needed; - drw_mesh_attributes_clear(&attrs_needed); - mesh_cd_calc_active_vcol_layer(me, &attrs_needed); + DRW_MeshCDMask cd_needed; + mesh_cd_layers_type_clear(&cd_needed); + mesh_cd_calc_active_vcol_layer(me, &cd_needed); - BLI_assert(attrs_needed.num_requests != 0 && + BLI_assert(cd_needed.sculpt_vcol != 0 && "No MPropCol layer available in Sculpt, but batches requested anyway!"); - drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime.render_mutex); + mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); } GPUBatch *DRW_mesh_batch_cache_get_all_verts(Mesh *me) @@ -1185,16 +1015,11 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, uint gpumat_array_len) { MeshBatchCache *cache = mesh_batch_cache_get(me); - DRW_MeshAttributes attrs_needed; - drw_mesh_attributes_clear(&attrs_needed); - DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers( - me, gpumat_array, gpumat_array_len, &attrs_needed); + DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(me, gpumat_array, gpumat_array_len); BLI_assert(gpumat_array_len == cache->mat_len); mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); - ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; - drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->surface_per_mat; } @@ -1471,25 +1296,11 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) cache->lastmatch = ctime; } - if (drw_mesh_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { - cache->lastmatch = ctime; - } - if (ctime - cache->lastmatch > U.vbotimeout) { mesh_batch_cache_discard_shaded_tri(cache); } mesh_cd_layers_type_clear(&cache->cd_used_over_time); - drw_mesh_attributes_clear(&cache->attr_used_over_time); -} - -static void drw_add_attributes_vbo(GPUBatch *batch, - MeshBufferList *mbuflist, - DRW_MeshAttributes *attr_used) -{ - for (int i = 0; i < attr_used->num_requests; i++) { - DRW_vbo_request(batch, &mbuflist->vbo.attr[i]); - } } #ifdef DEBUG @@ -1598,15 +1409,12 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } } - ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; - /* Verify that all surface batches have needed attribute layers. */ /* TODO(fclem): We could be a bit smarter here and only do it per * material. */ bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed); - bool attr_overlap = drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed); - if (cd_overlap == false || attr_overlap == false) { + if (cd_overlap == false) { FOREACH_MESH_BUFFER_CACHE (cache, mbc) { if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv); @@ -1622,14 +1430,11 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_used.sculpt_overlays != cache->cd_needed.sculpt_overlays) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.sculpt_data); } - if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) { + if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) || + ((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) != + cache->cd_needed.sculpt_vcol)) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol); } - if (!drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { - for (int i = 0; i < GPU_MAX_ATTR; i++) { - GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]); - } - } } /* We can't discard batches at this point as they have been * referenced for drawing. Just clear them in place. */ @@ -1640,13 +1445,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, cache->batch_ready &= ~(MBC_SURFACE); mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed); - drw_mesh_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); } mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed); mesh_cd_layers_type_clear(&cache->cd_needed); - - drw_mesh_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); - drw_mesh_attributes_clear(&cache->attr_needed); } if (batch_requested & MBC_EDITUV) { @@ -1705,27 +1506,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MeshBufferList *mbuflist = &cache->final.buff; /* Initialize batches and request VBO's & IBO's. */ - MDEPS_ASSERT(surface, - ibo.tris, - vbo.lnor, - vbo.pos_nor, - vbo.uv, - vbo.vcol, - vbo.attr[0], - vbo.attr[1], - vbo.attr[2], - vbo.attr[3], - vbo.attr[4], - vbo.attr[5], - vbo.attr[6], - vbo.attr[7], - vbo.attr[8], - vbo.attr[9], - vbo.attr[10], - vbo.attr[11], - vbo.attr[12], - vbo.attr[13], - vbo.attr[14]); + MDEPS_ASSERT(surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol); if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.surface, &mbuflist->ibo.tris); /* Order matters. First ones override latest VBO's attributes. */ @@ -1734,10 +1515,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_used.uv != 0) { DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.uv); } - if (cache->cd_used.vcol != 0) { + if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.vcol); } - drw_add_attributes_vbo(cache->batch.surface, mbuflist, &cache->attr_used); } MDEPS_ASSERT(all_verts, vbo.pos_nor); if (DRW_batch_requested(cache->batch.all_verts, GPU_PRIM_POINTS)) { @@ -1800,28 +1580,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } /* Per Material */ - MDEPS_ASSERT_FLAG(SURFACE_PER_MAT_FLAG, - vbo.lnor, - vbo.pos_nor, - vbo.uv, - vbo.tan, - vbo.vcol, - vbo.orco, - vbo.attr[0], - vbo.attr[1], - vbo.attr[2], - vbo.attr[3], - vbo.attr[4], - vbo.attr[5], - vbo.attr[6], - vbo.attr[7], - vbo.attr[8], - vbo.attr[9], - vbo.attr[10], - vbo.attr[11], - vbo.attr[12], - vbo.attr[13], - vbo.attr[14]); + MDEPS_ASSERT_FLAG( + SURFACE_PER_MAT_FLAG, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.tan, vbo.vcol, vbo.orco); MDEPS_ASSERT_INDEX(TRIS_PER_MAT_INDEX, SURFACE_PER_MAT_FLAG); for (int i = 0; i < cache->mat_len; i++) { if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) { @@ -1835,13 +1595,12 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.tan); } - if (cache->cd_used.vcol != 0) { + if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.vcol); } if (cache->cd_used.orco != 0) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.orco); } - drw_add_attributes_vbo(cache->surface_per_mat[i], mbuflist, &cache->attr_used); } } @@ -1992,9 +1751,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MDEPS_ASSERT_MAP(vbo.edituv_stretch_angle); MDEPS_ASSERT_MAP(vbo.fdots_uv); MDEPS_ASSERT_MAP(vbo.fdots_edituv_data); - for (int i = 0; i < GPU_MAX_ATTR; i++) { - MDEPS_ASSERT_MAP(vbo.attr[i]); - } MDEPS_ASSERT_MAP(ibo.tris); MDEPS_ASSERT_MAP(ibo.lines); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index d1ffef4fe92..d9f397fd8b8 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -328,7 +328,6 @@ extern const MeshExtract extract_poly_idx; extern const MeshExtract extract_edge_idx; extern const MeshExtract extract_vert_idx; extern const MeshExtract extract_fdot_idx; -extern const MeshExtract extract_attr[GPU_MAX_ATTR]; #ifdef __cplusplus } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc deleted file mode 100644 index f8cc92de1eb..00000000000 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ /dev/null @@ -1,398 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2021 by Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup draw - */ - -#include "MEM_guardedalloc.h" - -#include - -#include "BLI_float2.hh" -#include "BLI_float3.hh" -#include "BLI_float4.hh" -#include "BLI_string.h" - -#include "BKE_attribute.h" - -#include "extract_mesh.h" - -namespace blender::draw { - -/* ---------------------------------------------------------------------- */ -/** \name Extract Attributes - * \{ */ - -static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, AttributeDomain domain) -{ - switch (domain) { - default: { - return nullptr; - } - case ATTR_DOMAIN_POINT: { - return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - } - case ATTR_DOMAIN_CORNER: { - return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - } - case ATTR_DOMAIN_FACE: { - return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; - } - case ATTR_DOMAIN_EDGE: { - return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->edata : &mr->me->edata; - } - } -} - -/* Utility to convert from the type used in the attributes to the types for the VBO. - * This is mostly used to promote integers and booleans to floats, as other types (float, float2, - * etc.) directly map to avalaible GPU types. Booleans are still converted as attributes are vec4 - * in the shader. - */ -template struct attribute_type_converter { - static VBOType convert_value(AttributeType value) - { - if constexpr (std::is_same_v) { - return value; - } - - /* This should only concern bools which are converted to floats. */ - return static_cast(value); - } -}; - -/* Similar to the one in #extract_mesh_vcol_vbo.cc */ -struct gpuMeshCol { - ushort r, g, b, a; -}; - -template<> struct attribute_type_converter { - static gpuMeshCol convert_value(MPropCol value) - { - gpuMeshCol result; - result.r = unit_float_to_ushort_clamp(value.color[0]); - result.g = unit_float_to_ushort_clamp(value.color[1]); - result.b = unit_float_to_ushort_clamp(value.color[2]); - result.a = unit_float_to_ushort_clamp(value.color[3]); - return result; - } -}; - -/* Return the number of component for the attribute's value type, or 0 if is it unsupported. */ -static uint gpu_component_size_for_attribute_type(CustomDataType type) -{ - switch (type) { - case CD_PROP_BOOL: - case CD_PROP_INT32: - case CD_PROP_FLOAT: { - /* TODO(kevindietrich) : should be 1 when scalar attributes conversion is handled by us. See - * comment #extract_attr_init. */ - return 3; - } - case CD_PROP_FLOAT2: { - return 2; - } - case CD_PROP_FLOAT3: { - return 3; - } - case CD_PROP_COLOR: { - return 4; - } - default: { - return 0; - } - } -} - -static GPUVertFetchMode get_fetch_mode_for_type(CustomDataType type) -{ - switch (type) { - case CD_PROP_INT32: { - return GPU_FETCH_INT_TO_FLOAT; - } - case CD_PROP_COLOR: { - return GPU_FETCH_INT_TO_FLOAT_UNIT; - } - default: { - return GPU_FETCH_FLOAT; - } - } -} - -static GPUVertCompType get_comp_type_for_type(CustomDataType type) -{ - switch (type) { - case CD_PROP_INT32: { - return GPU_COMP_I32; - } - case CD_PROP_COLOR: { - return GPU_COMP_U16; - } - default: { - return GPU_COMP_F32; - } - } -} - -static void init_vbo_for_attribute(const MeshRenderData *mr, - GPUVertBuf *vbo, - const DRW_AttributeRequest &request) -{ - GPUVertCompType comp_type = get_comp_type_for_type(request.cd_type); - GPUVertFetchMode fetch_mode = get_fetch_mode_for_type(request.cd_type); - const uint comp_size = gpu_component_size_for_attribute_type(request.cd_type); - /* We should not be here if the attribute type is not supported. */ - BLI_assert(comp_size != 0); - - const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name( - custom_data, request.cd_type, request.layer_index); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - /* Attributes use auto-name. */ - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - - GPUVertFormat format = {0}; - GPU_vertformat_deinterleave(&format); - GPU_vertformat_attr_add(&format, attr_name, comp_type, comp_size, fetch_mode); - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, static_cast(mr->loop_len)); -} - -template -static void fill_vertbuf_with_attribute(const MeshRenderData *mr, - VBOType *vbo_data, - const DRW_AttributeRequest &request) -{ - const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); - BLI_assert(custom_data); - const int layer_index = request.layer_index; - - const MPoly *mpoly = mr->mpoly; - const MLoop *mloop = mr->mloop; - - const AttributeType *attr_data = static_cast( - CustomData_get_layer_n(custom_data, request.cd_type, layer_index)); - - using converter = attribute_type_converter; - - switch (request.domain) { - default: { - BLI_assert(false); - break; - } - case ATTR_DOMAIN_POINT: { - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { - *vbo_data = converter::convert_value(attr_data[mloop->v]); - } - break; - } - case ATTR_DOMAIN_CORNER: { - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++) { - *vbo_data = converter::convert_value(attr_data[ml_index]); - } - break; - } - case ATTR_DOMAIN_EDGE: { - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { - *vbo_data = converter::convert_value(attr_data[mloop->e]); - } - break; - } - case ATTR_DOMAIN_FACE: { - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { - const MPoly &poly = mpoly[mp_index]; - const VBOType value = converter::convert_value(attr_data[mp_index]); - for (int l = 0; l < poly.totloop; l++) { - *vbo_data++ = value; - } - } - break; - } - } -} - -template -static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr, - VBOType *&vbo_data, - const DRW_AttributeRequest &request) -{ - const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); - BLI_assert(custom_data); - const int layer_index = request.layer_index; - - int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index); - - using converter = attribute_type_converter; - - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const AttributeType *attr_data = nullptr; - if (request.domain == ATTR_DOMAIN_POINT) { - attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs)); - } - else if (request.domain == ATTR_DOMAIN_CORNER) { - attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs)); - } - else if (request.domain == ATTR_DOMAIN_FACE) { - attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(efa, cd_ofs)); - } - else if (request.domain == ATTR_DOMAIN_EDGE) { - attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter->e, cd_ofs)); - } - else { - BLI_assert(false); - continue; - } - *vbo_data = converter::convert_value(*attr_data); - vbo_data++; - } while ((l_iter = l_iter->next) != l_first); - } -} - -template -static void extract_attr_generic(const MeshRenderData *mr, - GPUVertBuf *vbo, - const DRW_AttributeRequest &request) -{ - VBOType *vbo_data = static_cast(GPU_vertbuf_get_data(vbo)); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - fill_vertbuf_with_attribute_bm(mr, vbo_data, request); - } - else { - fill_vertbuf_with_attribute(mr, vbo_data, request); - } -} - -static void extract_attr_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *UNUSED(tls_data), - int index) -{ - const DRW_MeshAttributes *attrs_used = &cache->attr_used; - const DRW_AttributeRequest &request = attrs_used->requests[index]; - - GPUVertBuf *vbo = static_cast(buf); - - init_vbo_for_attribute(mr, vbo, request); - - /* TODO(kevindietrich) : float3 is used for scalar attributes as the implicit conversion done by - * OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following the - * Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar - * texture as for volume attribute, so we can control the conversion ourselves. */ - switch (request.cd_type) { - case CD_PROP_BOOL: { - extract_attr_generic(mr, vbo, request); - break; - } - case CD_PROP_INT32: { - extract_attr_generic(mr, vbo, request); - break; - } - case CD_PROP_FLOAT: { - extract_attr_generic(mr, vbo, request); - break; - } - case CD_PROP_FLOAT2: { - extract_attr_generic(mr, vbo, request); - break; - } - case CD_PROP_FLOAT3: { - extract_attr_generic(mr, vbo, request); - break; - } - case CD_PROP_COLOR: { - extract_attr_generic(mr, vbo, request); - break; - } - default: { - BLI_assert(false); - } - } -} - -/* Wrappers around extract_attr_init so we can pass the index of the attribute that we want to - * extract. The overall API does not allow us to pass this in a convenient way. */ -#define EXTRACT_INIT_WRAPPER(index) \ - static void extract_attr_init##index( \ - const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf, void *tls_data) \ - { \ - extract_attr_init(mr, cache, buf, tls_data, index); \ - } - -EXTRACT_INIT_WRAPPER(0) -EXTRACT_INIT_WRAPPER(1) -EXTRACT_INIT_WRAPPER(2) -EXTRACT_INIT_WRAPPER(3) -EXTRACT_INIT_WRAPPER(4) -EXTRACT_INIT_WRAPPER(5) -EXTRACT_INIT_WRAPPER(6) -EXTRACT_INIT_WRAPPER(7) -EXTRACT_INIT_WRAPPER(8) -EXTRACT_INIT_WRAPPER(9) -EXTRACT_INIT_WRAPPER(10) -EXTRACT_INIT_WRAPPER(11) -EXTRACT_INIT_WRAPPER(12) -EXTRACT_INIT_WRAPPER(13) -EXTRACT_INIT_WRAPPER(14) - -template constexpr MeshExtract create_extractor_attr(ExtractInitFn fn) -{ - MeshExtract extractor = {nullptr}; - extractor.init = fn; - extractor.data_type = MR_DATA_NONE; - extractor.data_size = 0; - extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[index]); - return extractor; -} - -/** \} */ - -} // namespace blender::draw - -extern "C" { -#define CREATE_EXTRACTOR_ATTR(index) \ - blender::draw::create_extractor_attr(blender::draw::extract_attr_init##index) - -const MeshExtract extract_attr[GPU_MAX_ATTR] = { - CREATE_EXTRACTOR_ATTR(0), - CREATE_EXTRACTOR_ATTR(1), - CREATE_EXTRACTOR_ATTR(2), - CREATE_EXTRACTOR_ATTR(3), - CREATE_EXTRACTOR_ATTR(4), - CREATE_EXTRACTOR_ATTR(5), - CREATE_EXTRACTOR_ATTR(6), - CREATE_EXTRACTOR_ATTR(7), - CREATE_EXTRACTOR_ATTR(8), - CREATE_EXTRACTOR_ATTR(9), - CREATE_EXTRACTOR_ATTR(10), - CREATE_EXTRACTOR_ATTR(11), - CREATE_EXTRACTOR_ATTR(12), - CREATE_EXTRACTOR_ATTR(13), - CREATE_EXTRACTOR_ATTR(14), -}; -} diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index f8878eb2617..2c7770c8e72 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -43,7 +43,9 @@ static void extract_vcol_init(const MeshRenderData *mr, GPU_vertformat_deinterleave(&format); CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; uint32_t vcol_layers = cache->cd_used.vcol; + uint32_t svcol_layers = cache->cd_used.sculpt_vcol; for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { @@ -62,14 +64,42 @@ static void extract_vcol_init(const MeshRenderData *mr, } /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs`. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { + /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 && + CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) { BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); GPU_vertformat_alias_add(&format, attr_name); } } } + /* Sculpt Vertex Colors */ + if (U.experimental.use_sculpt_vertex_colors) { + for (int i = 0; i < 8; i++) { + if (svcol_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + + BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { + GPU_vertformat_alias_add(&format, "c"); + } + if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { + GPU_vertformat_alias_add(&format, "ac"); + } + /* Gather number of auto layers. */ + /* We only do `vcols` that are not overridden by `uvs`. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + } + } + } + } + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -78,6 +108,7 @@ static void extract_vcol_init(const MeshRenderData *mr, }; gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo); + MLoop *loops = (MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { @@ -108,6 +139,35 @@ static void extract_vcol_init(const MeshRenderData *mr, } } } + + if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) { + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i); + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const MPropCol *prop_col = (const MPropCol *)BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs); + vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]); + vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]); + vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]); + vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]); + vcol_data++; + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + MPropCol *vcol = (MPropCol *)CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) { + vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]); + vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]); + vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]); + vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]); + } + } + } } } diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 911c8cc2e42..018e192bf37 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -32,7 +32,7 @@ #include "GPU_shader.h" #include "GPU_vertex_buffer.h" -#define GPU_BATCH_VBO_MAX_LEN 16 +#define GPU_BATCH_VBO_MAX_LEN 6 #define GPU_BATCH_INST_VBO_MAX_LEN 2 #define GPU_BATCH_VAO_STATIC_LEN 3 #define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16 @@ -54,11 +54,11 @@ typedef enum eGPUBatchFlag { GPU_BATCH_OWNS_INDEX = (GPU_BATCH_OWNS_INST_VBO_MAX << 1), /** Has been initialized. At least one VBO is set. */ - GPU_BATCH_INIT = (1 << 26), + GPU_BATCH_INIT = (1 << 16), /** Batch is initialized but its VBOs are still being populated. (optional) */ - GPU_BATCH_BUILDING = (1 << 26), + GPU_BATCH_BUILDING = (1 << 16), /** Cached data need to be rebuild. (VAO, PSO, ...) */ - GPU_BATCH_DIRTY = (1 << 27), + GPU_BATCH_DIRTY = (1 << 17), } eGPUBatchFlag; #define GPU_BATCH_OWNS_NONE GPU_BATCH_INVALID diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 3943c063c39..97f14b2195d 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -127,10 +127,6 @@ typedef struct Mesh_Runtime { /** Needed in case we need to lazily initialize the mesh. */ CustomData_MeshMasks cd_mask_extra; - /** Needed to ensure some thread-safety during render data pre-processing. */ - void *render_mutex; - void *_pad3; - } Mesh_Runtime; typedef struct Mesh { From 485c634c4cdadebfc293a4d9f6ffd1631f3ac959 Mon Sep 17 00:00:00 2001 From: Cian Jinks Date: Tue, 26 Oct 2021 21:52:40 +0100 Subject: [PATCH 1252/1500] Cleanup: Confusion with knife xray functionality --- source/blender/editors/mesh/editmesh_knife.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 49afe90af91..8e0a7dbd69b 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -4796,7 +4796,7 @@ void MESH_OT_knife_tool(wmOperatorType *ot) "Occlude Geometry", "Only cut the front most geometry"); RNA_def_boolean(ot->srna, "only_selected", false, "Only Selected", "Only cut selected geometry"); - RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts through geometry"); + RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts hidden by geometry"); RNA_def_enum(ot->srna, "visible_measurements", From 3e3ff1a464b93c39cf31f30ef11b87e5b5786735 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 26 Oct 2021 18:16:33 -0300 Subject: [PATCH 1253/1500] Revert "Revert "Eevee: support accessing custom mesh attributes"" This reverts commit e7fedf6dba5fe2ec39260943361915a6b2b8270a. And also fix a compilation issue on windows. Differential Revision: https://developer.blender.org/D12969 --- .../blender/blenkernel/intern/mesh_runtime.c | 10 + source/blender/draw/CMakeLists.txt | 1 + .../blender/draw/intern/draw_cache_extract.h | 23 +- .../draw/intern/draw_cache_extract_mesh.cc | 3 + .../draw/intern/draw_cache_impl_mesh.c | 352 +++++++++++++--- .../intern/mesh_extractors/extract_mesh.h | 1 + .../extract_mesh_vbo_attributes.cc | 398 ++++++++++++++++++ .../mesh_extractors/extract_mesh_vbo_vcol.cc | 64 +-- source/blender/gpu/GPU_batch.h | 8 +- source/blender/makesdna/DNA_mesh_types.h | 4 + 10 files changed, 741 insertions(+), 123 deletions(-) create mode 100644 source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c index 7ac4c29f0ee..1c8646a4bdd 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.c +++ b/source/blender/blenkernel/intern/mesh_runtime.c @@ -52,6 +52,8 @@ void BKE_mesh_runtime_reset(Mesh *mesh) memset(&mesh->runtime, 0, sizeof(mesh->runtime)); mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); BLI_mutex_init(mesh->runtime.eval_mutex); + mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); + BLI_mutex_init(mesh->runtime.render_mutex); } /* Clear all pointers which we don't want to be shared on copying the datablock. @@ -71,6 +73,9 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag)) mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); BLI_mutex_init(mesh->runtime.eval_mutex); + + mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); + BLI_mutex_init(mesh->runtime.render_mutex); } void BKE_mesh_runtime_clear_cache(Mesh *mesh) @@ -80,6 +85,11 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh) MEM_freeN(mesh->runtime.eval_mutex); mesh->runtime.eval_mutex = NULL; } + if (mesh->runtime.render_mutex != NULL) { + BLI_mutex_end(mesh->runtime.render_mutex); + MEM_freeN(mesh->runtime.render_mutex); + mesh->runtime.render_mutex = NULL; + } if (mesh->runtime.mesh_eval != NULL) { mesh->runtime.mesh_eval->edit_mesh = NULL; BKE_id_free(NULL, mesh->runtime.mesh_eval); diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index baf83234354..0baf994d978 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -61,6 +61,7 @@ set(SRC intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc intern/mesh_extractors/extract_mesh_ibo_points.cc intern/mesh_extractors/extract_mesh_ibo_tris.cc + intern/mesh_extractors/extract_mesh_vbo_attributes.cc intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc intern/mesh_extractors/extract_mesh_vbo_edit_data.cc intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index a680cc0d6b7..ba42cdf66e7 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -24,6 +24,10 @@ struct TaskGraph; +#include "DNA_customdata_types.h" + +#include "BKE_attribute.h" + #include "GPU_batch.h" #include "GPU_index_buffer.h" #include "GPU_vertex_buffer.h" @@ -56,7 +60,6 @@ typedef struct DRW_MeshCDMask { uint32_t uv : 8; uint32_t tan : 8; uint32_t vcol : 8; - uint32_t sculpt_vcol : 8; uint32_t orco : 1; uint32_t tan_orco : 1; uint32_t sculpt_overlays : 1; @@ -64,10 +67,10 @@ typedef struct DRW_MeshCDMask { * modifiers could remove it. (see T68857) */ uint32_t edit_uv : 1; } DRW_MeshCDMask; -/* Keep `DRW_MeshCDMask` struct within an `uint64_t`. +/* Keep `DRW_MeshCDMask` struct within an `uint32_t`. * bit-wise and atomic operations are used to compare and update the struct. * See `mesh_cd_layers_type_*` functions. */ -BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits") +BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint32_t), "DRW_MeshCDMask exceeds 32 bits") typedef enum eMRIterType { MR_ITER_LOOPTRI = 1 << 0, MR_ITER_POLY = 1 << 1, @@ -76,6 +79,17 @@ typedef enum eMRIterType { } eMRIterType; ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) +typedef struct DRW_AttributeRequest { + CustomDataType cd_type; + int layer_index; + AttributeDomain domain; +} DRW_AttributeRequest; + +typedef struct DRW_MeshAttributes { + DRW_AttributeRequest requests[GPU_MAX_ATTR]; + int num_requests; +} DRW_MeshAttributes; + typedef enum eMRDataType { MR_DATA_NONE = 0, MR_DATA_POLY_NOR = 1 << 1, @@ -133,6 +147,7 @@ typedef struct MeshBufferList { GPUVertBuf *edge_idx; /* extend */ GPUVertBuf *poly_idx; GPUVertBuf *fdot_idx; + GPUVertBuf *attr[GPU_MAX_ATTR]; } vbo; /* Index Buffers: * Only need to be updated when topology changes. */ @@ -285,6 +300,8 @@ typedef struct MeshBatchCache { DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time; + DRW_MeshAttributes attr_used, attr_needed, attr_used_over_time; + int lastmatch; /* Valid only if edge_detection is up to date. */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 06c449fe590..f3b72503907 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -650,6 +650,9 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, EXTRACT_ADD_REQUESTED(vbo, vert_idx); EXTRACT_ADD_REQUESTED(vbo, fdot_idx); EXTRACT_ADD_REQUESTED(vbo, skin_roots); + for (int i = 0; i < GPU_MAX_ATTR; i++) { + EXTRACT_ADD_REQUESTED(vbo, attr[i]); + } EXTRACT_ADD_REQUESTED(ibo, tris); if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 18664498d00..12c19c671ab 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -41,6 +41,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" @@ -121,6 +122,8 @@ # define _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5) _MDEPS_ASSERT5(b, n1, n2, n3, n4); _MDEPS_ASSERT2(b, n5) # define _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6) _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5); _MDEPS_ASSERT2(b, n6) # define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7) +# define _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20) _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7); _MDEPS_ASSERT8(b, n8, n9, n10, n11, n12, n13, n14); _MDEPS_ASSERT7(b, n15, n16, n17, n18, n19, n20) +# define _MDEPS_ASSERT22(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20, n21) _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20); _MDEPS_ASSERT2(b, n21); # define MDEPS_ASSERT_FLAG(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__) # define MDEPS_ASSERT(batch_name, ...) MDEPS_ASSERT_FLAG(BATCH_FLAG(batch_name), __VA_ARGS__) @@ -192,6 +195,21 @@ static const DRWBatchFlag g_buffer_deps[] = { [BUFFER_INDEX(vbo.edge_idx)] = BATCH_FLAG(edit_selection_edges), [BUFFER_INDEX(vbo.poly_idx)] = BATCH_FLAG(edit_selection_faces), [BUFFER_INDEX(vbo.fdot_idx)] = BATCH_FLAG(edit_selection_fdots), + [BUFFER_INDEX(vbo.attr) + 0] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 1] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 2] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 3] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 4] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 5] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 6] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 7] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 8] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 9] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 10] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 11] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 12] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 13] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 14] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, [BUFFER_INDEX(ibo.tris)] = BATCH_FLAG(surface, surface_weights, @@ -240,12 +258,12 @@ static void mesh_batch_cache_discard_batch(MeshBatchCache *cache, const DRWBatch /* Return true is all layers in _b_ are inside _a_. */ BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return (*((uint64_t *)&a) & *((uint64_t *)&b)) == *((uint64_t *)&b); + return (*((uint32_t *)&a) & *((uint32_t *)&b)) == *((uint32_t *)&b); } BLI_INLINE bool mesh_cd_layers_type_equal(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return *((uint64_t *)&a) == *((uint64_t *)&b); + return *((uint32_t *)&a) == *((uint32_t *)&b); } BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) @@ -253,12 +271,11 @@ BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) uint32_t *a_p = (uint32_t *)a; uint32_t *b_p = (uint32_t *)&b; atomic_fetch_and_or_uint32(a_p, *b_p); - atomic_fetch_and_or_uint32(a_p + 1, *(b_p + 1)); } BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a) { - *((uint64_t *)a) = 0; + *((uint32_t *)a) = 0; } BLI_INLINE const Mesh *editmesh_final_or_this(const Mesh *me) @@ -271,6 +288,95 @@ static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *c cd_used->edit_uv = 1; } +/** \name DRW_MeshAttributes + * + * Utilities for handling requested attributes. + * \{ */ + +/* Return true if the given DRW_AttributeRequest is already in the requests. */ +static bool has_request(const DRW_MeshAttributes *requests, DRW_AttributeRequest req) +{ + for (int i = 0; i < requests->num_requests; i++) { + const DRW_AttributeRequest src_req = requests->requests[i]; + if (src_req.domain != req.domain) { + continue; + } + if (src_req.layer_index != req.layer_index) { + continue; + } + if (src_req.cd_type != req.cd_type) { + continue; + } + return true; + } + return false; +} + +static void mesh_attrs_merge_requests(const DRW_MeshAttributes *src_requests, + DRW_MeshAttributes *dst_requests) +{ + for (int i = 0; i < src_requests->num_requests; i++) { + if (dst_requests->num_requests == GPU_MAX_ATTR) { + return; + } + + if (has_request(dst_requests, src_requests->requests[i])) { + continue; + } + + dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; + dst_requests->num_requests += 1; + } +} + +static void drw_mesh_attributes_clear(DRW_MeshAttributes *attributes) +{ + memset(attributes, 0, sizeof(DRW_MeshAttributes)); +} + +static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst, + const DRW_MeshAttributes *src, + ThreadMutex *mesh_render_mutex) +{ + BLI_mutex_lock(mesh_render_mutex); + mesh_attrs_merge_requests(src, dst); + BLI_mutex_unlock(mesh_render_mutex); +} + +/* Return true if all requests in b are in a. */ +static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b) +{ + if (a->num_requests != b->num_requests) { + return false; + } + + for (int i = 0; i < a->num_requests; i++) { + if (!has_request(a, b->requests[i])) { + return false; + } + } + + return true; +} + +static void drw_mesh_attributes_add_request(DRW_MeshAttributes *attrs, + CustomDataType type, + int layer, + AttributeDomain domain) +{ + if (attrs->num_requests >= GPU_MAX_ATTR) { + return; + } + + DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; + req->cd_type = type; + req->layer_index = layer; + req->domain = domain; + attrs->num_requests += 1; +} + +/** \} */ + BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -286,6 +392,36 @@ BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) return &me->ldata; } +BLI_INLINE const CustomData *mesh_cd_pdata_get_from_mesh(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_MDATA: + return &me->pdata; + break; + case ME_WRAPPER_TYPE_BMESH: + return &me->edit_mesh->bm->pdata; + break; + } + + BLI_assert(0); + return &me->pdata; +} + +BLI_INLINE const CustomData *mesh_cd_edata_get_from_mesh(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_MDATA: + return &me->edata; + break; + case ME_WRAPPER_TYPE_BMESH: + return &me->edit_mesh->bm->edata; + break; + } + + BLI_assert(0); + return &me->edata; +} + BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -321,14 +457,14 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd } } -static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used) +static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshAttributes *attrs_used) { const Mesh *me_final = editmesh_final_or_this(me); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR); if (layer != -1) { - cd_used->sculpt_vcol |= (1 << layer); + drw_mesh_attributes_add_request(attrs_used, CD_PROP_COLOR, layer, ATTR_DOMAIN_POINT); } } @@ -343,13 +479,45 @@ static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *c } } +static bool custom_data_match_attribute(const CustomData *custom_data, + const char *name, + int *r_layer_index, + int *r_type) +{ + const int possible_attribute_types[6] = { + CD_PROP_BOOL, + CD_PROP_INT32, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_COLOR, + }; + + for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { + const int attr_type = possible_attribute_types[i]; + int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); + if (layer_index == -1) { + continue; + } + + *r_layer_index = layer_index; + *r_type = attr_type; + return true; + } + + return false; +} + static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, struct GPUMaterial **gpumat_array, - int gpumat_array_len) + int gpumat_array_len, + DRW_MeshAttributes *attributes) { const Mesh *me_final = editmesh_final_or_this(me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); + const CustomData *cd_pdata = mesh_cd_pdata_get_from_mesh(me_final); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); + const CustomData *cd_edata = mesh_cd_edata_get_from_mesh(me_final); /* See: DM_vertex_attributes_from_gpu for similar logic */ DRW_MeshCDMask cd_used; @@ -363,6 +531,8 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, const char *name = gpu_attr->name; int type = gpu_attr->type; int layer = -1; + /* ATTR_DOMAIN_NUM is standard for "invalid value". */ + AttributeDomain domain = ATTR_DOMAIN_NUM; if (type == CD_AUTO_FROM_NAME) { /* We need to deduct what exact layer is used. @@ -373,13 +543,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name); type = CD_MTFACE; - if (layer == -1) { - if (U.experimental.use_sculpt_vertex_colors) { - layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name); - type = CD_PROP_COLOR; - } - } - if (layer == -1) { layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name); type = CD_MCOL; @@ -391,6 +554,27 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, type = CD_TANGENT; } #endif + if (layer == -1) { + /* Try to match a generic attribute, we use the first attribute domain with a + * matching name. */ + if (custom_data_match_attribute(cd_vdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_POINT; + } + else if (custom_data_match_attribute(cd_ldata, name, &layer, &type)) { + domain = ATTR_DOMAIN_CORNER; + } + else if (custom_data_match_attribute(cd_pdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_FACE; + } + else if (custom_data_match_attribute(cd_edata, name, &layer, &type)) { + domain = ATTR_DOMAIN_EDGE; + } + else { + layer = -1; + domain = ATTR_DOMAIN_NUM; + } + } + if (layer == -1) { continue; } @@ -432,31 +616,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, } break; } - case CD_PROP_COLOR: { - /* Sculpt Vertex Colors */ - bool use_mloop_cols = false; - if (layer == -1) { - layer = (name[0] != '\0') ? - CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name) : - CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR); - /* Fallback to Vertex Color data */ - if (layer == -1) { - layer = (name[0] != '\0') ? - CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name) : - CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL); - use_mloop_cols = true; - } - } - if (layer != -1) { - if (use_mloop_cols) { - cd_used.vcol |= (1 << layer); - } - else { - cd_used.sculpt_vcol |= (1 << layer); - } - } - break; - } case CD_MCOL: { /* Vertex Color Data */ if (layer == -1) { @@ -473,6 +632,17 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, cd_used.orco = 1; break; } + case CD_PROP_BOOL: + case CD_PROP_INT32: + case CD_PROP_FLOAT: + case CD_PROP_FLOAT2: + case CD_PROP_FLOAT3: + case CD_PROP_COLOR: { + if (layer != -1 && domain != ATTR_DOMAIN_NUM) { + drw_mesh_attributes_add_request(attributes, type, layer, domain); + } + break; + } } } } @@ -935,14 +1105,14 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me) static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me) { - DRW_MeshCDMask cd_needed; - mesh_cd_layers_type_clear(&cd_needed); - mesh_cd_calc_active_vcol_layer(me, &cd_needed); + DRW_MeshAttributes attrs_needed; + drw_mesh_attributes_clear(&attrs_needed); + mesh_cd_calc_active_vcol_layer(me, &attrs_needed); - BLI_assert(cd_needed.sculpt_vcol != 0 && + BLI_assert(attrs_needed.num_requests != 0 && "No MPropCol layer available in Sculpt, but batches requested anyway!"); - mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); + drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime.render_mutex); } GPUBatch *DRW_mesh_batch_cache_get_all_verts(Mesh *me) @@ -1015,11 +1185,16 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, uint gpumat_array_len) { MeshBatchCache *cache = mesh_batch_cache_get(me); - DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(me, gpumat_array, gpumat_array_len); + DRW_MeshAttributes attrs_needed; + drw_mesh_attributes_clear(&attrs_needed); + DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers( + me, gpumat_array, gpumat_array_len, &attrs_needed); BLI_assert(gpumat_array_len == cache->mat_len); mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); + ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; + drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->surface_per_mat; } @@ -1296,11 +1471,25 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) cache->lastmatch = ctime; } + if (drw_mesh_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { + cache->lastmatch = ctime; + } + if (ctime - cache->lastmatch > U.vbotimeout) { mesh_batch_cache_discard_shaded_tri(cache); } mesh_cd_layers_type_clear(&cache->cd_used_over_time); + drw_mesh_attributes_clear(&cache->attr_used_over_time); +} + +static void drw_add_attributes_vbo(GPUBatch *batch, + MeshBufferList *mbuflist, + DRW_MeshAttributes *attr_used) +{ + for (int i = 0; i < attr_used->num_requests; i++) { + DRW_vbo_request(batch, &mbuflist->vbo.attr[i]); + } } #ifdef DEBUG @@ -1409,12 +1598,15 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } } + ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; + /* Verify that all surface batches have needed attribute layers. */ /* TODO(fclem): We could be a bit smarter here and only do it per * material. */ bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed); - if (cd_overlap == false) { + bool attr_overlap = drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed); + if (cd_overlap == false || attr_overlap == false) { FOREACH_MESH_BUFFER_CACHE (cache, mbc) { if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv); @@ -1430,11 +1622,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_used.sculpt_overlays != cache->cd_needed.sculpt_overlays) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.sculpt_data); } - if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) || - ((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) != - cache->cd_needed.sculpt_vcol)) { + if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol); } + if (!drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { + for (int i = 0; i < GPU_MAX_ATTR; i++) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]); + } + } } /* We can't discard batches at this point as they have been * referenced for drawing. Just clear them in place. */ @@ -1445,9 +1640,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, cache->batch_ready &= ~(MBC_SURFACE); mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed); + drw_mesh_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); } mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed); mesh_cd_layers_type_clear(&cache->cd_needed); + + drw_mesh_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); + drw_mesh_attributes_clear(&cache->attr_needed); } if (batch_requested & MBC_EDITUV) { @@ -1506,7 +1705,27 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MeshBufferList *mbuflist = &cache->final.buff; /* Initialize batches and request VBO's & IBO's. */ - MDEPS_ASSERT(surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol); + MDEPS_ASSERT(surface, + ibo.tris, + vbo.lnor, + vbo.pos_nor, + vbo.uv, + vbo.vcol, + vbo.attr[0], + vbo.attr[1], + vbo.attr[2], + vbo.attr[3], + vbo.attr[4], + vbo.attr[5], + vbo.attr[6], + vbo.attr[7], + vbo.attr[8], + vbo.attr[9], + vbo.attr[10], + vbo.attr[11], + vbo.attr[12], + vbo.attr[13], + vbo.attr[14]); if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.surface, &mbuflist->ibo.tris); /* Order matters. First ones override latest VBO's attributes. */ @@ -1515,9 +1734,10 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_used.uv != 0) { DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.uv); } - if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { + if (cache->cd_used.vcol != 0) { DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.vcol); } + drw_add_attributes_vbo(cache->batch.surface, mbuflist, &cache->attr_used); } MDEPS_ASSERT(all_verts, vbo.pos_nor); if (DRW_batch_requested(cache->batch.all_verts, GPU_PRIM_POINTS)) { @@ -1580,8 +1800,28 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } /* Per Material */ - MDEPS_ASSERT_FLAG( - SURFACE_PER_MAT_FLAG, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.tan, vbo.vcol, vbo.orco); + MDEPS_ASSERT_FLAG(SURFACE_PER_MAT_FLAG, + vbo.lnor, + vbo.pos_nor, + vbo.uv, + vbo.tan, + vbo.vcol, + vbo.orco, + vbo.attr[0], + vbo.attr[1], + vbo.attr[2], + vbo.attr[3], + vbo.attr[4], + vbo.attr[5], + vbo.attr[6], + vbo.attr[7], + vbo.attr[8], + vbo.attr[9], + vbo.attr[10], + vbo.attr[11], + vbo.attr[12], + vbo.attr[13], + vbo.attr[14]); MDEPS_ASSERT_INDEX(TRIS_PER_MAT_INDEX, SURFACE_PER_MAT_FLAG); for (int i = 0; i < cache->mat_len; i++) { if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) { @@ -1595,12 +1835,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.tan); } - if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { + if (cache->cd_used.vcol != 0) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.vcol); } if (cache->cd_used.orco != 0) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.orco); } + drw_add_attributes_vbo(cache->surface_per_mat[i], mbuflist, &cache->attr_used); } } @@ -1751,6 +1992,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MDEPS_ASSERT_MAP(vbo.edituv_stretch_angle); MDEPS_ASSERT_MAP(vbo.fdots_uv); MDEPS_ASSERT_MAP(vbo.fdots_edituv_data); + for (int i = 0; i < GPU_MAX_ATTR; i++) { + MDEPS_ASSERT_MAP(vbo.attr[i]); + } MDEPS_ASSERT_MAP(ibo.tris); MDEPS_ASSERT_MAP(ibo.lines); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index d9f397fd8b8..d1ffef4fe92 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -328,6 +328,7 @@ extern const MeshExtract extract_poly_idx; extern const MeshExtract extract_edge_idx; extern const MeshExtract extract_vert_idx; extern const MeshExtract extract_fdot_idx; +extern const MeshExtract extract_attr[GPU_MAX_ATTR]; #ifdef __cplusplus } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc new file mode 100644 index 00000000000..f8cc92de1eb --- /dev/null +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -0,0 +1,398 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + */ + +#include "MEM_guardedalloc.h" + +#include + +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_float4.hh" +#include "BLI_string.h" + +#include "BKE_attribute.h" + +#include "extract_mesh.h" + +namespace blender::draw { + +/* ---------------------------------------------------------------------- */ +/** \name Extract Attributes + * \{ */ + +static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, AttributeDomain domain) +{ + switch (domain) { + default: { + return nullptr; + } + case ATTR_DOMAIN_POINT: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + } + case ATTR_DOMAIN_CORNER: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + } + case ATTR_DOMAIN_FACE: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; + } + case ATTR_DOMAIN_EDGE: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->edata : &mr->me->edata; + } + } +} + +/* Utility to convert from the type used in the attributes to the types for the VBO. + * This is mostly used to promote integers and booleans to floats, as other types (float, float2, + * etc.) directly map to avalaible GPU types. Booleans are still converted as attributes are vec4 + * in the shader. + */ +template struct attribute_type_converter { + static VBOType convert_value(AttributeType value) + { + if constexpr (std::is_same_v) { + return value; + } + + /* This should only concern bools which are converted to floats. */ + return static_cast(value); + } +}; + +/* Similar to the one in #extract_mesh_vcol_vbo.cc */ +struct gpuMeshCol { + ushort r, g, b, a; +}; + +template<> struct attribute_type_converter { + static gpuMeshCol convert_value(MPropCol value) + { + gpuMeshCol result; + result.r = unit_float_to_ushort_clamp(value.color[0]); + result.g = unit_float_to_ushort_clamp(value.color[1]); + result.b = unit_float_to_ushort_clamp(value.color[2]); + result.a = unit_float_to_ushort_clamp(value.color[3]); + return result; + } +}; + +/* Return the number of component for the attribute's value type, or 0 if is it unsupported. */ +static uint gpu_component_size_for_attribute_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_BOOL: + case CD_PROP_INT32: + case CD_PROP_FLOAT: { + /* TODO(kevindietrich) : should be 1 when scalar attributes conversion is handled by us. See + * comment #extract_attr_init. */ + return 3; + } + case CD_PROP_FLOAT2: { + return 2; + } + case CD_PROP_FLOAT3: { + return 3; + } + case CD_PROP_COLOR: { + return 4; + } + default: { + return 0; + } + } +} + +static GPUVertFetchMode get_fetch_mode_for_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_INT32: { + return GPU_FETCH_INT_TO_FLOAT; + } + case CD_PROP_COLOR: { + return GPU_FETCH_INT_TO_FLOAT_UNIT; + } + default: { + return GPU_FETCH_FLOAT; + } + } +} + +static GPUVertCompType get_comp_type_for_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_INT32: { + return GPU_COMP_I32; + } + case CD_PROP_COLOR: { + return GPU_COMP_U16; + } + default: { + return GPU_COMP_F32; + } + } +} + +static void init_vbo_for_attribute(const MeshRenderData *mr, + GPUVertBuf *vbo, + const DRW_AttributeRequest &request) +{ + GPUVertCompType comp_type = get_comp_type_for_type(request.cd_type); + GPUVertFetchMode fetch_mode = get_fetch_mode_for_type(request.cd_type); + const uint comp_size = gpu_component_size_for_attribute_type(request.cd_type); + /* We should not be here if the attribute type is not supported. */ + BLI_assert(comp_size != 0); + + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name( + custom_data, request.cd_type, request.layer_index); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* Attributes use auto-name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + GPU_vertformat_attr_add(&format, attr_name, comp_type, comp_size, fetch_mode); + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, static_cast(mr->loop_len)); +} + +template +static void fill_vertbuf_with_attribute(const MeshRenderData *mr, + VBOType *vbo_data, + const DRW_AttributeRequest &request) +{ + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + BLI_assert(custom_data); + const int layer_index = request.layer_index; + + const MPoly *mpoly = mr->mpoly; + const MLoop *mloop = mr->mloop; + + const AttributeType *attr_data = static_cast( + CustomData_get_layer_n(custom_data, request.cd_type, layer_index)); + + using converter = attribute_type_converter; + + switch (request.domain) { + default: { + BLI_assert(false); + break; + } + case ATTR_DOMAIN_POINT: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { + *vbo_data = converter::convert_value(attr_data[mloop->v]); + } + break; + } + case ATTR_DOMAIN_CORNER: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++) { + *vbo_data = converter::convert_value(attr_data[ml_index]); + } + break; + } + case ATTR_DOMAIN_EDGE: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { + *vbo_data = converter::convert_value(attr_data[mloop->e]); + } + break; + } + case ATTR_DOMAIN_FACE: { + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { + const MPoly &poly = mpoly[mp_index]; + const VBOType value = converter::convert_value(attr_data[mp_index]); + for (int l = 0; l < poly.totloop; l++) { + *vbo_data++ = value; + } + } + break; + } + } +} + +template +static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr, + VBOType *&vbo_data, + const DRW_AttributeRequest &request) +{ + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + BLI_assert(custom_data); + const int layer_index = request.layer_index; + + int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index); + + using converter = attribute_type_converter; + + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const AttributeType *attr_data = nullptr; + if (request.domain == ATTR_DOMAIN_POINT) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_CORNER) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_FACE) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(efa, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_EDGE) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter->e, cd_ofs)); + } + else { + BLI_assert(false); + continue; + } + *vbo_data = converter::convert_value(*attr_data); + vbo_data++; + } while ((l_iter = l_iter->next) != l_first); + } +} + +template +static void extract_attr_generic(const MeshRenderData *mr, + GPUVertBuf *vbo, + const DRW_AttributeRequest &request) +{ + VBOType *vbo_data = static_cast(GPU_vertbuf_get_data(vbo)); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + fill_vertbuf_with_attribute_bm(mr, vbo_data, request); + } + else { + fill_vertbuf_with_attribute(mr, vbo_data, request); + } +} + +static void extract_attr_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(tls_data), + int index) +{ + const DRW_MeshAttributes *attrs_used = &cache->attr_used; + const DRW_AttributeRequest &request = attrs_used->requests[index]; + + GPUVertBuf *vbo = static_cast(buf); + + init_vbo_for_attribute(mr, vbo, request); + + /* TODO(kevindietrich) : float3 is used for scalar attributes as the implicit conversion done by + * OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following the + * Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar + * texture as for volume attribute, so we can control the conversion ourselves. */ + switch (request.cd_type) { + case CD_PROP_BOOL: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_INT32: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_FLOAT: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_FLOAT2: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_FLOAT3: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_COLOR: { + extract_attr_generic(mr, vbo, request); + break; + } + default: { + BLI_assert(false); + } + } +} + +/* Wrappers around extract_attr_init so we can pass the index of the attribute that we want to + * extract. The overall API does not allow us to pass this in a convenient way. */ +#define EXTRACT_INIT_WRAPPER(index) \ + static void extract_attr_init##index( \ + const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf, void *tls_data) \ + { \ + extract_attr_init(mr, cache, buf, tls_data, index); \ + } + +EXTRACT_INIT_WRAPPER(0) +EXTRACT_INIT_WRAPPER(1) +EXTRACT_INIT_WRAPPER(2) +EXTRACT_INIT_WRAPPER(3) +EXTRACT_INIT_WRAPPER(4) +EXTRACT_INIT_WRAPPER(5) +EXTRACT_INIT_WRAPPER(6) +EXTRACT_INIT_WRAPPER(7) +EXTRACT_INIT_WRAPPER(8) +EXTRACT_INIT_WRAPPER(9) +EXTRACT_INIT_WRAPPER(10) +EXTRACT_INIT_WRAPPER(11) +EXTRACT_INIT_WRAPPER(12) +EXTRACT_INIT_WRAPPER(13) +EXTRACT_INIT_WRAPPER(14) + +template constexpr MeshExtract create_extractor_attr(ExtractInitFn fn) +{ + MeshExtract extractor = {nullptr}; + extractor.init = fn; + extractor.data_type = MR_DATA_NONE; + extractor.data_size = 0; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[index]); + return extractor; +} + +/** \} */ + +} // namespace blender::draw + +extern "C" { +#define CREATE_EXTRACTOR_ATTR(index) \ + blender::draw::create_extractor_attr(blender::draw::extract_attr_init##index) + +const MeshExtract extract_attr[GPU_MAX_ATTR] = { + CREATE_EXTRACTOR_ATTR(0), + CREATE_EXTRACTOR_ATTR(1), + CREATE_EXTRACTOR_ATTR(2), + CREATE_EXTRACTOR_ATTR(3), + CREATE_EXTRACTOR_ATTR(4), + CREATE_EXTRACTOR_ATTR(5), + CREATE_EXTRACTOR_ATTR(6), + CREATE_EXTRACTOR_ATTR(7), + CREATE_EXTRACTOR_ATTR(8), + CREATE_EXTRACTOR_ATTR(9), + CREATE_EXTRACTOR_ATTR(10), + CREATE_EXTRACTOR_ATTR(11), + CREATE_EXTRACTOR_ATTR(12), + CREATE_EXTRACTOR_ATTR(13), + CREATE_EXTRACTOR_ATTR(14), +}; +} diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index 2c7770c8e72..f8878eb2617 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -43,9 +43,7 @@ static void extract_vcol_init(const MeshRenderData *mr, GPU_vertformat_deinterleave(&format); CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; uint32_t vcol_layers = cache->cd_used.vcol; - uint32_t svcol_layers = cache->cd_used.sculpt_vcol; for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { @@ -64,42 +62,14 @@ static void extract_vcol_init(const MeshRenderData *mr, } /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 && - CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) { + /* We only do `vcols` that are not overridden by `uvs`. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); GPU_vertformat_alias_add(&format, attr_name); } } } - /* Sculpt Vertex Colors */ - if (U.experimental.use_sculpt_vertex_colors) { - for (int i = 0; i < 8; i++) { - if (svcol_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - - BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "c"); - } - if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "ac"); - } - /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs`. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); - } - } - } - } - GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -108,7 +78,6 @@ static void extract_vcol_init(const MeshRenderData *mr, }; gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo); - MLoop *loops = (MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { @@ -139,35 +108,6 @@ static void extract_vcol_init(const MeshRenderData *mr, } } } - - if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MPropCol *prop_col = (const MPropCol *)BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs); - vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]); - vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]); - vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]); - vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]); - vcol_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - MPropCol *vcol = (MPropCol *)CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) { - vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]); - vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]); - vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]); - vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]); - } - } - } } } diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 018e192bf37..911c8cc2e42 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -32,7 +32,7 @@ #include "GPU_shader.h" #include "GPU_vertex_buffer.h" -#define GPU_BATCH_VBO_MAX_LEN 6 +#define GPU_BATCH_VBO_MAX_LEN 16 #define GPU_BATCH_INST_VBO_MAX_LEN 2 #define GPU_BATCH_VAO_STATIC_LEN 3 #define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16 @@ -54,11 +54,11 @@ typedef enum eGPUBatchFlag { GPU_BATCH_OWNS_INDEX = (GPU_BATCH_OWNS_INST_VBO_MAX << 1), /** Has been initialized. At least one VBO is set. */ - GPU_BATCH_INIT = (1 << 16), + GPU_BATCH_INIT = (1 << 26), /** Batch is initialized but its VBOs are still being populated. (optional) */ - GPU_BATCH_BUILDING = (1 << 16), + GPU_BATCH_BUILDING = (1 << 26), /** Cached data need to be rebuild. (VAO, PSO, ...) */ - GPU_BATCH_DIRTY = (1 << 17), + GPU_BATCH_DIRTY = (1 << 27), } eGPUBatchFlag; #define GPU_BATCH_OWNS_NONE GPU_BATCH_INVALID diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 97f14b2195d..3943c063c39 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -127,6 +127,10 @@ typedef struct Mesh_Runtime { /** Needed in case we need to lazily initialize the mesh. */ CustomData_MeshMasks cd_mask_extra; + /** Needed to ensure some thread-safety during render data pre-processing. */ + void *render_mutex; + void *_pad3; + } Mesh_Runtime; typedef struct Mesh { From 2c2d4bc3a33128d4c32a53a6b9e484309abba7bf Mon Sep 17 00:00:00 2001 From: Cian Jinks Date: Tue, 26 Oct 2021 22:20:50 +0100 Subject: [PATCH 1254/1500] Knife: Preserve right click cancel functionality Currently, the knife does not use right click cancel. It causes users to accidentally delete entire cuts easily. This patch allows right click cancel when no cuts have been made. This makes it consistent with other tools when switching between them. More info: https://devtalk.blender.org/t/gsoc-2021-knife-tool-improvements-feedback/19047/175?u=hobbesos --- source/blender/editors/mesh/editmesh_knife.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 8e0a7dbd69b..09ae779138c 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -240,6 +240,7 @@ typedef struct KnifeTool_OpData { BLI_mempool *kverts; BLI_mempool *kedges; + bool no_cuts; /* A cut has not been made yet. */ BLI_Stack *undostack; BLI_Stack *splitstack; /* Store edge splits by #knife_split_edge. */ @@ -4089,6 +4090,8 @@ static void knifetool_init(bContext *C, knife_init_colors(&kcd->colors); } + kcd->no_cuts = true; + kcd->axis_string[0] = ' '; kcd->axis_string[1] = '\0'; @@ -4501,12 +4504,23 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) handled = true; break; case KNF_MODAL_NEW_CUT: + /* If no cuts have been made, exit. + * Preserves right click cancel workflow which most tools use, + * but stops accidentally deleting entire cuts with right click. + */ + if (kcd->no_cuts) { + ED_region_tag_redraw(kcd->region); + knifetool_exit(op); + ED_workspace_status_text(C, NULL); + return OPERATOR_CANCELLED; + } ED_region_tag_redraw(kcd->region); knife_finish_cut(kcd); kcd->mode = MODE_IDLE; handled = true; break; case KNF_MODAL_ADD_CUT: + kcd->no_cuts = false; knife_recalc_ortho(kcd); /* Get the value of the event which triggered this one. */ From f195a3a30dfe27a5927b0dfa9843c571ae1f1cce Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 00:02:29 +0200 Subject: [PATCH 1255/1500] Asset Browser: Reduce paddings & margins between previews The paddings and margins were more than needed, this reduces them a bit. That way space is used more efficiently, the small differences add up so that more items fit into a row. The File Browser should not be affected. Before/after comparisons: {F11529986} {F11529988} {F11529987} {F11529989} --- source/blender/editors/space_file/filesel.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 4cd05237138..5299b8ba580 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -979,6 +979,8 @@ static void file_attribute_columns_init(const FileSelectParams *params, FileLayo void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) { FileSelectParams *params = ED_fileselect_get_active_params(sfile); + /* Request a slightly more compact layout for asset browsing. */ + const bool compact = ED_fileselect_is_asset_browser(sfile); FileLayout *layout = NULL; View2D *v2d = ®ion->v2d; int numfiles; @@ -998,12 +1000,13 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) layout->textheight = textheight; if (params->display == FILE_IMGDISPLAY) { + const float pad_fac = compact ? 0.15f : 0.3f; layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; - layout->tile_border_x = 0.3f * UI_UNIT_X; - layout->tile_border_y = 0.3f * UI_UNIT_X; - layout->prv_border_x = 0.3f * UI_UNIT_X; - layout->prv_border_y = 0.3f * UI_UNIT_Y; + layout->tile_border_x = pad_fac * UI_UNIT_X; + layout->tile_border_y = pad_fac * UI_UNIT_X; + layout->prv_border_x = pad_fac * UI_UNIT_X; + layout->prv_border_y = pad_fac * UI_UNIT_Y; layout->tile_w = layout->prv_w + 2 * layout->prv_border_x; layout->tile_h = layout->prv_h + 2 * layout->prv_border_y + textheight; layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x); From 8d8ce6443530ac3e39276c7b7219e2a8ca61040f Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Wed, 27 Oct 2021 01:24:19 +0200 Subject: [PATCH 1256/1500] Nodes: Cleanup setting node header alpha Always draw header opaque, set transparency only once after getting the color. Helps to unify the color and alpha values. --- source/blender/editors/space_node/node_draw.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 429c53d6740..79c2bc8185b 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -1625,10 +1625,10 @@ static void node_draw_basis(const bContext *C, /* Muted nodes get a mix of the background with the node color. */ if (node->flag & NODE_MUTED) { - UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, 0.1f, 0, color_header); + UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.1f, color_header); } else { - UI_GetThemeColorBlendShade4fv(TH_NODE, color_id, 0.6f, -40, color_header); + UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color_header); } UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT); @@ -1770,11 +1770,11 @@ static void node_draw_basis(const bContext *C, { /* Use warning color to indicate undefined types. */ if (nodeTypeUndefined(node)) { - UI_GetThemeColorBlendShade4fv(TH_REDALERT, color_id, 0.05f, -80, color); + UI_GetThemeColorBlend4f(TH_REDALERT, TH_NODE, 0.4f, color); } /* Muted nodes get a mix of the background with the node color. */ else if (node->flag & NODE_MUTED) { - UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.33f, 0, color); + UI_GetThemeColorBlend4f(TH_BACK, TH_NODE, 0.2f, color); } else if (node->flag & NODE_CUSTOM_COLOR) { rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); @@ -1900,7 +1900,7 @@ static void node_draw_hidden(const bContext *C, { if (nodeTypeUndefined(node)) { /* Use warning color to indicate undefined types. */ - UI_GetThemeColorBlendShade4fv(TH_REDALERT, color_id, 0.05f, -80, color); + UI_GetThemeColorBlend4f(TH_REDALERT, TH_NODE, 0.4f, color); } else if (node->flag & NODE_MUTED) { /* Muted nodes get a mix of the background with the node color. */ @@ -1910,7 +1910,7 @@ static void node_draw_hidden(const bContext *C, rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); } else { - UI_GetThemeColorBlendShade4fv(TH_NODE, color_id, 0.6f, -40, color); + UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color); } /* Draw selected nodes fully opaque. */ From 99a2a737061c7a02c03d7dac4d464c842f0eae63 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Tue, 26 Oct 2021 17:48:16 -0600 Subject: [PATCH 1257/1500] win/make.bat: Add svnfix convenience target SVN seems to die randomly *a lot* during large updates for some users, and I'm no closer to finding out why that keeps happening. "The internet" seems to imply some AV vendors may be at fault here but nothing conclusive. The solution however is repeatedly running `svn cleanup`and `svn update` in the library folder to repair the corruption and finish the update. This change adds a small convenience helper to automate the repair. This is done inside the make.bat code rather than the shared python based update code, since python lives in the library folder and may or may not exist when this corruption occurs. --- build_files/windows/parse_arguments.cmd | 3 +++ build_files/windows/svn_fix.cmd | 26 +++++++++++++++++++++++++ make.bat | 5 +++++ 3 files changed, 34 insertions(+) create mode 100644 build_files/windows/svn_fix.cmd diff --git a/build_files/windows/parse_arguments.cmd b/build_files/windows/parse_arguments.cmd index c63f062dfef..dcef46c2c9a 100644 --- a/build_files/windows/parse_arguments.cmd +++ b/build_files/windows/parse_arguments.cmd @@ -116,6 +116,9 @@ if NOT "%1" == "" ( ) else if "%1" == "doc_py" ( set DOC_PY=1 goto EOF + ) else if "%1" == "svnfix" ( + set SVN_FIX=1 + goto EOF ) else ( echo Command "%1" unknown, aborting! goto ERR diff --git a/build_files/windows/svn_fix.cmd b/build_files/windows/svn_fix.cmd new file mode 100644 index 00000000000..a9dcdf36847 --- /dev/null +++ b/build_files/windows/svn_fix.cmd @@ -0,0 +1,26 @@ +if "%BUILD_VS_YEAR%"=="2017" set BUILD_VS_LIBDIRPOST=vc15 +if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15 +if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15 + +set BUILD_VS_SVNDIR=win64_%BUILD_VS_LIBDIRPOST% +set BUILD_VS_LIBDIR="%BLENDER_DIR%..\lib\%BUILD_VS_SVNDIR%" + +echo Starting cleanup in %BUILD_VS_LIBDIR%. +cd %BUILD_VS_LIBDIR% +:RETRY +"%SVN%" cleanup +"%SVN%" update +if errorlevel 1 ( + set /p LibRetry= "Error during update, retry? y/n" + if /I "!LibRetry!"=="Y" ( + goto RETRY + ) + echo. + echo Error: Download of external libraries failed. + echo This is needed for building, please manually run 'svn cleanup' and 'svn update' in + echo %BUILD_VS_LIBDIR% , until this is resolved you CANNOT make a successful blender build + echo. + exit /b 1 +) +echo Cleanup complete + diff --git a/make.bat b/make.bat index e94f7637512..d55b2cfd1b3 100644 --- a/make.bat +++ b/make.bat @@ -56,6 +56,11 @@ if "%BUILD_VS_YEAR%" == "" ( ) ) +if "%SVN_FIX%" == "1" ( + call "%BLENDER_DIR%\build_files\windows\svn_fix.cmd" + goto EOF +) + if "%BUILD_UPDATE%" == "1" ( call "%BLENDER_DIR%\build_files\windows\check_libraries.cmd" if errorlevel 1 goto EOF From 44ac5830c561a690646108fd528ec9f3941f66ee Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Wed, 27 Oct 2021 04:33:12 +0200 Subject: [PATCH 1258/1500] Nodes: Cleanup code for header underline alpha Use `UI_GetThemeColorBlend4f` instead of manually setting alpha opaque. --- source/blender/editors/space_node/node_draw.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 79c2bc8185b..a6496294f96 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -1812,8 +1812,7 @@ static void node_draw_basis(const bContext *C, UI_GetThemeColor4fv(TH_WIRE, color_underline); } else { - UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, 0.4f, -30, color_underline); - color_underline[3] = 1.0f; + UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.2f, color_underline); } const rctf rect = { From a3b785bc083f3d83198c402e00da9d610389ca98 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 13:59:41 +1100 Subject: [PATCH 1259/1500] Cleanup: clang-format, clang-tidy, spelling --- source/blender/blenkernel/BKE_geometry_set.hh | 3 ++- .../blenkernel/intern/armature_test.cc | 2 +- source/blender/blenlib/BLI_serialize.hh | 14 ++++++------- .../extract_mesh_vbo_attributes.cc | 2 +- .../intern/lineart/lineart_chain.c | 20 +++++++++---------- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index d273fe74310..a9a0b87a778 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -318,7 +318,8 @@ struct GeometrySet { bool include_instances, blender::Map &r_attributes) const; - blender::Vector gather_component_types(bool include_instances, bool ignore_empty) const; + blender::Vector gather_component_types(bool include_instances, + bool ignore_empty) const; using ForeachSubGeometryCallback = blender::FunctionRef; diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 2375c606247..a6d9a1f41e9 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -133,7 +133,7 @@ static double test_vec_roll_to_mat3_normalized(const float input[3], float roll_mat[3][3]; if (normalize) { - /* The vector is renormalized to replicate the actual usage. */ + /* The vector is re-normalized to replicate the actual usage. */ normalize_v3_v3(input_normalized, input); } else { diff --git a/source/blender/blenlib/BLI_serialize.hh b/source/blender/blenlib/BLI_serialize.hh index 8bec2785f83..7b8aa03b807 100644 --- a/source/blender/blenlib/BLI_serialize.hh +++ b/source/blender/blenlib/BLI_serialize.hh @@ -50,20 +50,20 @@ * The next example would format an integer value (42) as JSON the result will * be stored inside `out`. * - * ``` + * \code{.cc} * JsonFormatter json; * std::stringstream out; * IntValue test_value(42); * json.serialize(out, test_value); - * ``` + * \endcode * * ## Deserializing * - * ``` - * std::stringstream is("42") + * \code{.cc} + * std::stringstream is("42"); * JsonFormatter json; * std::unique_ptr value = json.deserialize(is); - * ``` + * \endcode * * # Adding a new formatter * @@ -113,7 +113,7 @@ using ArrayValue = * Class containing a (de)serializable value. * * To serialize from or to a specific format the Value will be used as an intermediate container - * holding the values. Value class is abstract. There are concreate classes to for different data + * holding the values. Value class is abstract. There are concrete classes to for different data * types. * * - `StringValue`: contains a string. @@ -311,7 +311,7 @@ class Formatter { }; /** - * Formatter to (de)serialize a json formatted stream. + * Formatter to (de)serialize a JSON formatted stream. */ class JsonFormatter : public Formatter { public: diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index f8cc92de1eb..9edefe32fbc 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -63,7 +63,7 @@ static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, Attribut /* Utility to convert from the type used in the attributes to the types for the VBO. * This is mostly used to promote integers and booleans to floats, as other types (float, float2, - * etc.) directly map to avalaible GPU types. Booleans are still converted as attributes are vec4 + * etc.) directly map to available GPU types. Booleans are still converted as attributes are vec4 * in the shader. */ template struct attribute_type_converter { diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 85b801eece2..f3110cf87b6 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -1045,21 +1045,19 @@ void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb) is_inside = new_inside; continue; } - else { - /* Stroke comes in. */ - new_eci = lineart_chain_create_crossing_point(rb, eci, prev_eci); + /* Stroke comes in. */ + new_eci = lineart_chain_create_crossing_point(rb, eci, prev_eci); - ec->chain.first = eci; - eci->prev = NULL; + ec->chain.first = eci; + eci->prev = NULL; - BLI_addhead(&ec->chain, new_eci); + BLI_addhead(&ec->chain, new_eci); - ec_added = false; + ec_added = false; - next_eci = eci->next; - is_inside = new_inside; - continue; - } + next_eci = eci->next; + is_inside = new_inside; + continue; } } From 0d155f274ff2ca2dcf52745e32f17c05a9d1c890 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 13:59:43 +1100 Subject: [PATCH 1260/1500] Docs: add docstring for wmWindowManger.winactive Also justify rounding up font width. --- source/blender/editors/interface/interface_style.c | 2 ++ source/blender/makesdna/DNA_windowmanager_types.h | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 2a1cdfd447c..92a9f14c77d 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -400,6 +400,8 @@ int UI_fontstyle_string_width_with_block_aspect(const uiFontStyle *fs, int width = UI_fontstyle_string_width(fs, str); if (aspect != 1.0f) { + /* While in most cases rounding up isn't important, it can make a difference + * with small fonts (3px or less), zooming out in the node-editor for e.g. */ width = (int)ceilf(width * aspect); } return width; diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index e24d39b61eb..841edaf8724 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -138,7 +138,14 @@ typedef struct wmWindowManager { ID id; /** Separate active from drawable. */ - struct wmWindow *windrawable, *winactive; + struct wmWindow *windrawable; + /** + * \note `CTX_wm_window(C)` is usually preferred. + * Avoid relying on this where possible as this may become NULL during when handling + * events that close or replace windows (opening a file for e.g.). + * While this happens rarely in practice, it can cause difficult to reproduce bugs. + */ + struct wmWindow *winactive; ListBase windows; /** Set on file read. */ From 9cb4624296028295873181c497cb6d1408e4a83d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 16:52:50 +1100 Subject: [PATCH 1261/1500] Cleanup: add missing break --- source/blender/windowmanager/intern/wm_dragdrop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 638aca6c98e..50ac046ed54 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -218,6 +218,7 @@ void wm_drags_exit(wmWindowManager *wm, wmWindow *win) LISTBASE_FOREACH (const wmDrag *, drag, &wm->drags) { if (drag->active_dropbox) { any_active = true; + break; } } From 526e60d4f0b440e2e28e73620caa10920ab0732b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 17:05:02 +1100 Subject: [PATCH 1262/1500] Cleanup: remove underscore prefix from variable Avoid using underscore prefix since these typically mean the variable shouldn't be accessed directly (it may be accessed from a macro, or memory on the stack which is assigned to a pointer). In this case a more meaningful name can be used for the argument that was shadowed. --- .../gizmo_library/gizmo_types/cage3d_gizmo.c | 2 +- .../editors/space_view3d/view3d_cursor_snap.c | 2 +- source/blender/gpu/GPU_immediate_util.h | 6 ++-- .../blender/gpu/intern/gpu_immediate_util.c | 30 +++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c index 07117c0153b..aed58e31798 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c @@ -220,7 +220,7 @@ static void cage3d_draw_circle_wire(const float r[3], immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", line_width * U.pixelsize); - imm_draw_cube_wire_3d(pos, (float[3]){0}, r); + imm_draw_cube_wire_3d(pos, (const float[3]){0}, r); #if 0 if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) { diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 5db9dd5d0a7..937ac98acb8 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -372,7 +372,7 @@ static void cursor_box_draw(const float dimensions[3], uchar color[4]) immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4ubv(color); - imm_draw_cube_corners_3d(pos_id, (float[3]){0.0f, 0.0f, dimensions[2]}, dimensions, 0.15f); + imm_draw_cube_corners_3d(pos_id, (const float[3]){0.0f, 0.0f, dimensions[2]}, dimensions, 0.15f); immUnbindProgram(); GPU_line_smooth(false); diff --git a/source/blender/gpu/GPU_immediate_util.h b/source/blender/gpu/GPU_immediate_util.h index 793348ad8d2..0d3d39839b2 100644 --- a/source/blender/gpu/GPU_immediate_util.h +++ b/source/blender/gpu/GPU_immediate_util.h @@ -78,10 +78,10 @@ void imm_draw_box_checker_2d_ex(float x1, int checker_size); void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2); -void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]); -void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]); +void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3]); +void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3]); void imm_draw_cube_corners_3d(uint pos, - const float co[3], + const float center[3], const float aspect[3], const float factor); diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c index aeed03080a3..032974db8d1 100644 --- a/source/blender/gpu/intern/gpu_immediate_util.c +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -391,12 +391,12 @@ void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2) imm_draw_box_checker_2d_ex(x1, y1, x2, y2, checker_primary, checker_secondary, checker_size); } -void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]) +void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3]) { float coords[ARRAY_SIZE(cube_coords)][3]; for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { - madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect); + madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect); } immBegin(GPU_PRIM_TRIS, ARRAY_SIZE(cube_quad_index) * 3 * 2); @@ -412,12 +412,12 @@ void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]) immEnd(); } -void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]) +void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3]) { float coords[ARRAY_SIZE(cube_coords)][3]; for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { - madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect); + madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect); } immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 2); @@ -429,31 +429,31 @@ void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]) } void imm_draw_cube_corners_3d(uint pos, - const float co[3], + const float center[3], const float aspect[3], const float factor) { float coords[ARRAY_SIZE(cube_coords)][3]; for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { - madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect); + madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect); } immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 4); for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) { - float vec[3], _co[3]; + float vec[3], co[3]; sub_v3_v3v3(vec, coords[cube_line_index[i][1]], coords[cube_line_index[i][0]]); mul_v3_fl(vec, factor); - copy_v3_v3(_co, coords[cube_line_index[i][0]]); - immVertex3fv(pos, _co); - add_v3_v3(_co, vec); - immVertex3fv(pos, _co); + copy_v3_v3(co, coords[cube_line_index[i][0]]); + immVertex3fv(pos, co); + add_v3_v3(co, vec); + immVertex3fv(pos, co); - copy_v3_v3(_co, coords[cube_line_index[i][1]]); - immVertex3fv(pos, _co); - sub_v3_v3(_co, vec); - immVertex3fv(pos, _co); + copy_v3_v3(co, coords[cube_line_index[i][1]]); + immVertex3fv(pos, co); + sub_v3_v3(co, vec); + immVertex3fv(pos, co); } immEnd(); } From 55dda9fdb3f17281304d323ee18f23aa2e7a5ece Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 17:19:00 +1100 Subject: [PATCH 1263/1500] Cleanup: use 'use_' prefix for RNA naming in grease pencil modifiers --- .../blender/gpencil_modifiers/intern/MOD_gpencillineart.c | 3 ++- .../blender/gpencil_modifiers/intern/MOD_gpencilopacity.c | 4 ++-- source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c | 4 ++-- source/blender/makesrna/intern/rna_gpencil_modifier.c | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index c6bc5a9e53e..06dea6cd4d2 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -703,7 +703,8 @@ static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetActive(col, !show_in_front); uiItemR(col, ptr, "stroke_depth_offset", UI_ITEM_R_SLIDER, IFACE_("Depth Offset"), ICON_NONE); - uiItemR(col, ptr, "offset_towards_custom_camera", 0, IFACE_("Towards Custom Camera"), ICON_NONE); + uiItemR( + col, ptr, "use_offset_towards_custom_camera", 0, IFACE_("Towards Custom Camera"), ICON_NONE); } static void panelRegister(ARegionType *region_type) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index fb75b1e99ac..2e55369ea97 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -240,10 +240,10 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "hardness", 0, NULL, ICON_NONE); } else { - const bool is_normalized = RNA_boolean_get(ptr, "normalize_opacity"); + const bool is_normalized = RNA_boolean_get(ptr, "use_normalized_opacity"); const bool is_weighted = RNA_boolean_get(ptr, "use_weight_factor"); - uiItemR(layout, ptr, "normalize_opacity", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_normalized_opacity", 0, NULL, ICON_NONE); const char *text = (is_normalized) ? IFACE_("Strength") : IFACE_("Opacity Factor"); uiLayout *row = uiLayoutRow(layout, true); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index cac700e15f4..233992bbd31 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -191,8 +191,8 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); - uiItemR(layout, ptr, "normalize_thickness", 0, NULL, ICON_NONE); - if (RNA_boolean_get(ptr, "normalize_thickness")) { + uiItemR(layout, ptr, "use_normalized_thickness", 0, NULL, ICON_NONE); + if (RNA_boolean_get(ptr, "use_normalized_thickness")) { uiItemR(layout, ptr, "thickness", 0, NULL, ICON_NONE); } else { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 30ec6532485..c4efe5a0ea1 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -1309,7 +1309,7 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna) prop, "Custom Curve", "Use a custom curve to define thickness change along the strokes"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "normalize_thickness", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_normalized_thickness", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_NORMALIZE); RNA_def_property_ui_text(prop, "Uniform Thickness", "Replace the stroke thickness"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -1865,7 +1865,7 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "normalize_opacity", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_normalized_opacity", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_NORMALIZE); RNA_def_property_ui_text(prop, "Uniform Opacity", "Replace the stroke opacity"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_opacity_update"); @@ -3215,7 +3215,7 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_range(prop, -0.1, FLT_MAX); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "offset_towards_custom_camera", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_offset_towards_custom_camera", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); RNA_def_property_ui_text(prop, "Offset Towards Custom Camera", From d9799e770630c3971106bcadd45010bc82824f09 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 17:20:38 +1100 Subject: [PATCH 1264/1500] Cleanup: use UNUSED_FUNCTION(..) attribute Otherwise this function may fail to compile when other changes are made. --- source/blender/editors/mesh/editmesh_knife.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 09ae779138c..b712cfc24ed 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1462,8 +1462,9 @@ static void knife_input_ray_segment(KnifeTool_OpData *kcd, } /* No longer used, but may be useful in the future. */ -#if 0 -static void knifetool_recast_cageco(KnifeTool_OpData *kcd, float mval[3], float r_cage[3]) +static void UNUSED_FUNCTION(knifetool_recast_cageco)(KnifeTool_OpData *kcd, + float mval[3], + float r_cage[3]) { float origin[3]; float origin_ofs[3]; @@ -1477,7 +1478,6 @@ static void knifetool_recast_cageco(KnifeTool_OpData *kcd, float mval[3], float knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, NULL, co, r_cage, NULL); } -#endif static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) { From b3b2cd1fa9f28f698e385a96fbc137db8c9415af Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 21 May 2021 15:48:26 +0200 Subject: [PATCH 1265/1500] Fix T88443: Lattice still shows edges with "Bounds" display type Lattice wires are drawn as part of "Extras". Unlike the other types details (Cameras, Lights, Lightprobes and Speakers), Lattices actually have boundingboxes defined, so hide the lattice wires if only the boundingbox is requested. Maniphest Tasks: T88443 Differential Revision: https://developer.blender.org/D11343 --- source/blender/draw/engines/overlay/overlay_engine.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index b07e86000fd..7f9e37f58d5 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -463,9 +463,14 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) case OB_LIGHTPROBE: OVERLAY_lightprobe_cache_populate(vedata, ob); break; - case OB_LATTICE: - OVERLAY_lattice_cache_populate(vedata, ob); + case OB_LATTICE: { + /* Unlike the other types above, lattices actually have a bounding box defined, so hide the + * lattice wires if only the boundingbox is requested. */ + if (ob->dt > OB_BOUNDBOX) { + OVERLAY_lattice_cache_populate(vedata, ob); + } break; + } } } From ec77228f0f6a6205fea9437c85e618f7d779064c Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 26 Oct 2021 09:55:50 +0200 Subject: [PATCH 1266/1500] Fix T92402: copy_particle_systems use_active fails outside the Properties Editor Similar to rBf9308a585ecd, use `psys_get_current` if we cant get the active psys from context (which is only defined for the Properties Editor). Other solution would be to define a "particle_system" context member in other editors, but for now, stick with the simplest solution. thx @mano-wii for additional input Maniphest Tasks: T92402 Differential Revision: https://developer.blender.org/D13000 --- source/blender/editors/physics/particle_object.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 3ac6dca3044..367d72b0ad7 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -1235,9 +1235,15 @@ static int copy_particle_systems_exec(bContext *C, wmOperator *op) const bool use_active = RNA_boolean_get(op->ptr, "use_active"); Scene *scene = CTX_data_scene(C); Object *ob_from = ED_object_active_context(C); - ParticleSystem *psys_from = - use_active ? CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data : - NULL; + + ParticleSystem *psys_from = NULL; + if (use_active) { + psys_from = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data; + if (psys_from == NULL) { + /* Particle System context pointer is only valid in the Properties Editor. */ + psys_from = psys_get_current(ob_from); + } + } int changed_tot = 0; int fail = 0; From 16e1b18dd8899dbc47e971b5ca3deb7652ae954a Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 26 Oct 2021 17:12:23 +0200 Subject: [PATCH 1267/1500] Cleanup: Rename `scene.c`'s `FOREACHID_PROCESS` macro to `FOREACHID_PROCESS_IDSUPER`. Follow-up of rBf11ed418e5fa. --- source/blender/blenkernel/intern/scene.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 8059cd45f05..d7fc2005ab2 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -704,7 +704,7 @@ static bool seq_foreach_member_id_cb(Sequence *seq, void *user_data) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; -#define FOREACHID_PROCESS(_data, _id_super, _cb_flag) \ +#define FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag) \ { \ CHECK_TYPE(&((_id_super)->id), ID *); \ if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag))) { \ @@ -713,23 +713,23 @@ static bool seq_foreach_member_id_cb(Sequence *seq, void *user_data) } \ ((void)0) - FOREACHID_PROCESS(data, seq->scene, IDWALK_CB_NEVER_SELF); - FOREACHID_PROCESS(data, seq->scene_camera, IDWALK_CB_NOP); - FOREACHID_PROCESS(data, seq->clip, IDWALK_CB_USER); - FOREACHID_PROCESS(data, seq->mask, IDWALK_CB_USER); - FOREACHID_PROCESS(data, seq->sound, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, seq->scene, IDWALK_CB_NEVER_SELF); + FOREACHID_PROCESS_IDSUPER(data, seq->scene_camera, IDWALK_CB_NOP); + FOREACHID_PROCESS_IDSUPER(data, seq->clip, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, seq->mask, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, seq->sound, IDWALK_CB_USER); IDP_foreach_property( seq->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); LISTBASE_FOREACH (SequenceModifierData *, smd, &seq->modifiers) { - FOREACHID_PROCESS(data, smd->mask_id, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, smd->mask_id, IDWALK_CB_USER); } if (seq->type == SEQ_TYPE_TEXT && seq->effectdata) { TextVars *text_data = seq->effectdata; - FOREACHID_PROCESS(data, text_data->text_font, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, text_data->text_font, IDWALK_CB_USER); } -#undef FOREACHID_PROCESS +#undef FOREACHID_PROCESS_IDSUPER return true; } From b94447a298146f273c848541a3122afea001ba39 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 26 Oct 2021 12:33:36 +0200 Subject: [PATCH 1268/1500] Fix T92494: Node Editor dot grid not respecting display resolution scale This seems wrong and was especially noticeable since transform snapping does account for it (which was reported in T92494). Now divide the `DotGridLevelInfo` `step_factor` by the default of 20 for `U.widget_unit` and scale it later by the actual interface scale. note: when zooming, this will still always snap to the smallest dot level (not sure, with a bit more work it could be possible to only snap to the lowest visible level after fading?) Maniphest Tasks: T92494 Differential Revision: https://developer.blender.org/D13002 --- source/blender/editors/interface/view2d.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 3b6a7e1e88e..eea6512f0f8 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -1324,15 +1324,15 @@ typedef struct DotGridLevelInfo { } DotGridLevelInfo; static const DotGridLevelInfo level_info[9] = { - {128.0f, -0.1f, 0.01f}, - {64.0f, 0.0f, 0.025f}, - {32.0f, 0.025f, 0.15f}, - {16.0f, 0.05f, 0.2f}, - {8.0f, 0.1f, 0.25f}, - {4.0f, 0.125f, 0.3f}, - {2.0f, 0.25f, 0.5f}, - {1.0f, 0.7f, 0.9f}, - {0.5f, 0.6f, 0.9f}, + {6.4f, -0.1f, 0.01f}, + {3.2f, 0.0f, 0.025f}, + {1.6f, 0.025f, 0.15f}, + {0.8f, 0.05f, 0.2f}, + {0.4f, 0.1f, 0.25f}, + {0.2f, 0.125f, 0.3f}, + {0.1f, 0.25f, 0.5f}, + {0.05f, 0.7f, 0.9f}, + {0.025f, 0.6f, 0.9f}, }; /** @@ -1363,7 +1363,7 @@ void UI_view2d_dot_grid_draw(const View2D *v2d, for (int level = 0; level < grid_levels; level++) { const DotGridLevelInfo *info = &level_info[level]; - const float step = min_step * info->step_factor; + const float step = min_step * info->step_factor * U.widget_unit; const float alpha_factor = (zoom_normalized - info->fade_in_start_zoom) / (info->fade_in_end_zoom - info->fade_in_start_zoom); From 4d605ef2f413e8ab484743218f0d0517138513d5 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 26 Oct 2021 13:42:19 +0200 Subject: [PATCH 1269/1500] Fix T92427: Adding new nodes does no edge-panning Unlike translating existing nodes [which disables cursor wrapping and enables edge-panning instead since rBSa1cc7042a74], adding new nodes would still show the old behavior of cursor wrapping. This has been disabled for the case when the node whould be added outside (due to menus overlapping other editors). Now enable edge-panning for adding new nodes as well and make sure this only starts once the mouse has returned into the inside rect once. Maniphest Tasks: T92427 Differential Revision: https://developer.blender.org/D13005 --- source/blender/editors/include/UI_view2d.h | 3 +++ source/blender/editors/interface/view2d_edge_pan.c | 11 ++++++++++- source/blender/editors/space_node/node_ops.c | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index e959f9ae7ce..122e5a7d839 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -315,6 +315,9 @@ typedef struct View2DEdgePanData { /** View2d we're operating in. */ struct View2D *v2d; + /** Panning should only start once being in the inside rect once (e.g. adding nodes can happen + * outside). */ + bool enabled; /** Inside distance in UI units from the edge of the region within which to start panning. */ float inside_pad; /** Outside distance in UI units from the edge of the region at which to stop panning. */ diff --git a/source/blender/editors/interface/view2d_edge_pan.c b/source/blender/editors/interface/view2d_edge_pan.c index a49666ebbd3..8d8b9a4fe48 100644 --- a/source/blender/editors/interface/view2d_edge_pan.c +++ b/source/blender/editors/interface/view2d_edge_pan.c @@ -92,6 +92,8 @@ void UI_view2d_edge_pan_init(bContext *C, vpd->delay = delay; vpd->zoom_influence = zoom_influence; + vpd->enabled = false; + /* Calculate translation factor, based on size of view. */ const float winx = (float)(BLI_rcti_size_x(&vpd->region->winrct) + 1); const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1); @@ -227,9 +229,16 @@ void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, const int xy[ BLI_rcti_pad(&inside_rect, -vpd->inside_pad * U.widget_unit, -vpd->inside_pad * U.widget_unit); BLI_rcti_pad(&outside_rect, vpd->outside_pad * U.widget_unit, vpd->outside_pad * U.widget_unit); + /* Check if we can actually start the edge pan (e.g. adding nodes outside the view will start + * disabled). */ + if (BLI_rcti_isect_pt_v(&inside_rect, xy)) { + /* We are inside once, can start. */ + vpd->enabled = true; + } + int pan_dir_x = 0; int pan_dir_y = 0; - if ((vpd->outside_pad == 0) || BLI_rcti_isect_pt_v(&outside_rect, xy)) { + if (vpd->enabled && ((vpd->outside_pad == 0) || BLI_rcti_isect_pt_v(&outside_rect, xy))) { /* Find whether the mouse is beyond X and Y edges. */ if (xy[0] > inside_rect.xmax) { pan_dir_x = 1; diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index df4f63af20b..0c54da65e9c 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -156,6 +156,7 @@ void ED_operatormacros_node(void) OPTYPE_UNDO | OPTYPE_REGISTER); mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); RNA_boolean_set(mot->ptr, "remove_on_cancel", true); + RNA_boolean_set(mot->ptr, "view2d_edge_pan", true); WM_operatortype_macro_define(ot, "NODE_OT_attach"); WM_operatortype_macro_define(ot, "NODE_OT_insert_offset"); From 8b15b06b201169b9f1e2eb6604711ff2fe1abdab Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 11:02:49 +0200 Subject: [PATCH 1270/1500] Fix: missing cache invalidation when there is only one spline This fixes T92511, but there is still the more general problem described in T92508. --- source/blender/blenkernel/intern/geometry_component_curve.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index f30ff2a70a7..d2a404b860a 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -1135,6 +1135,9 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu MutableSpan splines = curve->splines(); if (splines.size() == 1) { + if (update_on_write_) { + update_on_write_(*splines[0]); + } return std::make_unique( get_mutable_span_(*splines.first())); } From 933215d6daa4d07ccad186cf9b488cf4a02f5935 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 27 Oct 2021 11:44:57 +0200 Subject: [PATCH 1271/1500] Cleanup: Move 2.80 doversion code under a MAIN_VERSION_ATLEAST check --- .../blenloader/intern/versioning_280.c | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index e6247750759..e809d580fd2 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -1717,18 +1717,8 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - #blo_do_versions_280 in this file. - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ + /* Old forgotten versioning code. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) { /* Paint Brush. This ensure that the brush paints by default. Used during the development and * patch review of the initial Sculpt Vertex Colors implementation (D5975) */ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { @@ -1748,6 +1738,20 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #blo_do_versions_280 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } } /* NOTE: This version patch is intended for versions < 2.52.2, @@ -5066,17 +5070,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - #do_versions_after_linking_280 in this file. - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { + /* Old forgotten versioning code. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) { /* Set the cloth wind factor to 1 for old forces. */ if (!DNA_struct_elem_find(fd->filesdna, "PartDeflect", "float", "f_wind_factor")) { LISTBASE_FOREACH (Object *, ob, &bmain->objects) { @@ -5096,10 +5091,22 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { /* Don't rotate light with the viewer by default, make it fixed. Shading settings can't be - * edited and this flag should always be set. So we can always execute this. */ + * edited and this flag should always be set. */ wm->xr.session_settings.shading.flag |= V3D_SHADING_WORLD_ORIENTATION; } + } + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #do_versions_after_linking_280 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { /* Keep this block, even when empty. */ } } From e16bc136f92bd166219ef6e21bcd1a303b335aa0 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 12:05:23 +0200 Subject: [PATCH 1272/1500] Cleanup: Make UI tree-view item constructor explicit Constructors that can be called with a single argument should be explicit, unless there's a reason not to, to avoid unintended constructions. --- source/blender/editors/include/UI_tree_view.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index fb0304060c9..5a7f16c4db6 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -401,7 +401,7 @@ class BasicTreeViewItem : public AbstractTreeViewItem { using ActivateFn = std::function; BIFIconID icon; - BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE); + explicit BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE); void build_row(uiLayout &row) override; void on_activate(ActivateFn fn); From d161b5d204a585b910a47ca432544570ea10911e Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 12:06:31 +0200 Subject: [PATCH 1273/1500] UI: Add padding to the left of tree-rows labels without icon The label was placed right at the left border of the row highlight, which looked weird. So add some padding to tree-row labels without icon or collapse chevron, which makes it look more polished. As additional benefit, it alignes the labels better with icons of other rows on the same tree level. And the padding makes it more clear that a child is indeed a child, not just a sibling without icon. --- source/blender/editors/include/UI_tree_view.hh | 1 + source/blender/editors/interface/tree_view.cc | 13 ++++++++++++- .../editors/space_file/asset_catalog_tree_view.cc | 8 ++------ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 5a7f16c4db6..905181a7251 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -404,6 +404,7 @@ class BasicTreeViewItem : public AbstractTreeViewItem { explicit BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE); void build_row(uiLayout &row) override; + void add_label(uiLayout &layout, StringRefNull label_override = ""); void on_activate(ActivateFn fn); protected: diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 0eeb32bcc69..04d7a066b36 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -641,7 +641,18 @@ BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(ic void BasicTreeViewItem::build_row(uiLayout &row) { - uiItemL(&row, label_.c_str(), icon); + add_label(row); +} + +void BasicTreeViewItem::add_label(uiLayout &layout, StringRefNull label_override) +{ + const StringRefNull label = label_override.is_empty() ? StringRefNull(label_) : label_override; + + /* Some padding for labels without collapse chevron and no icon. Looks weird without. */ + if (icon == ICON_NONE && !is_collapsible()) { + uiItemS_ex(&layout, 0.8f); + } + uiItemL(&layout, label.c_str(), icon); } void BasicTreeViewItem::on_activate() diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 354ee742598..53981ca244d 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -233,12 +233,8 @@ void AssetCatalogTreeViewItem::on_activate() void AssetCatalogTreeViewItem::build_row(uiLayout &row) { - if (catalog_item_.has_unsaved_changes()) { - uiItemL(&row, (label_ + "*").c_str(), icon); - } - else { - uiItemL(&row, label_.c_str(), icon); - } + const std::string label_override = catalog_item_.has_unsaved_changes() ? (label_ + "*") : label_; + add_label(row, label_override); if (!is_hovered()) { return; From c06a86f99f5bd59e2f1c37b18f5e668da245a206 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 12:29:46 +0200 Subject: [PATCH 1274/1500] Fix T92328: crash during field inferencing when there is a link cycle The toposort did not handle link cycles which it should. --- source/blender/blenkernel/intern/node.cc | 8 +- source/blender/nodes/NOD_node_tree_ref.hh | 12 +- source/blender/nodes/intern/node_tree_ref.cc | 135 ++++++++++++------- 3 files changed, 101 insertions(+), 54 deletions(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index d6c4e1f21e4..95192003f9f 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4717,10 +4717,10 @@ static OutputFieldDependency find_group_output_dependencies( static void propagate_data_requirements_from_right_to_left( const NodeTreeRef &tree, const MutableSpan field_state_by_socket_id) { - const Vector sorted_nodes = tree.toposort( + const NodeTreeRef::ToposortResult toposort_result = tree.toposort( NodeTreeRef::ToposortDirection::RightToLeft); - for (const NodeRef *node : sorted_nodes) { + for (const NodeRef *node : toposort_result.sorted_nodes) { const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( *node); @@ -4829,10 +4829,10 @@ static void determine_group_input_states( static void propagate_field_status_from_left_to_right( const NodeTreeRef &tree, const MutableSpan field_state_by_socket_id) { - Vector sorted_nodes = tree.toposort( + const NodeTreeRef::ToposortResult toposort_result = tree.toposort( NodeTreeRef::ToposortDirection::LeftToRight); - for (const NodeRef *node : sorted_nodes) { + for (const NodeRef *node : toposort_result.sorted_nodes) { if (node->is_group_input_node()) { continue; } diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index 9f9dcc69376..e04dd7f41bd 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -287,7 +287,17 @@ class NodeTreeRef : NonCopyable, NonMovable { RightToLeft, }; - Vector toposort(ToposortDirection direction) const; + struct ToposortResult { + Vector sorted_nodes; + /** + * There can't be a correct topologycal sort of the nodes when there is a cycle. The nodes will + * still be sorted to some degree. The caller has to decide whether it can handle non-perfect + * sorts or not. + */ + bool has_cycle = false; + }; + + ToposortResult toposort(ToposortDirection direction) const; bNodeTree *btree() const; StringRefNull name() const; diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 2ca797009da..5481465aef6 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -502,11 +502,15 @@ bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const return this->any_output_is_directly_linked(); } -/** - * Sort nodes topologically from left to right or right to left. - * In the future the result if this could be cached on #NodeTreeRef. - */ -Vector NodeTreeRef::toposort(const ToposortDirection direction) const +struct ToposortNodeState { + bool is_done = false; + bool is_in_stack = false; +}; + +static void toposort_from_start_node(const NodeTreeRef::ToposortDirection direction, + const NodeRef &start_node, + MutableSpan node_states, + NodeTreeRef::ToposortResult &result) { struct Item { const NodeRef *node; @@ -516,64 +520,97 @@ Vector NodeTreeRef::toposort(const ToposortDirection direction) int link_index = 0; }; - Vector toposort; - toposort.reserve(nodes_by_id_.size()); - Array node_is_done_by_id(nodes_by_id_.size(), false); - Stack nodes_to_check; + /* Do a depth-first search to sort nodes topologically. */ + Stack nodes_to_check; + nodes_to_check.push({&start_node}); + while (!nodes_to_check.is_empty()) { + Item &item = nodes_to_check.peek(); + const NodeRef &node = *item.node; + const Span sockets = node.sockets( + direction == NodeTreeRef::ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT); - for (const NodeRef *start_node : nodes_by_id_) { - if (node_is_done_by_id[start_node->id()]) { + while (true) { + if (item.socket_index == sockets.size()) { + /* All sockets have already been visited. */ + break; + } + const SocketRef &socket = *sockets[item.socket_index]; + const Span linked_sockets = socket.directly_linked_sockets(); + if (item.link_index == linked_sockets.size()) { + /* All links connected to this socket have already been visited. */ + item.socket_index++; + item.link_index = 0; + continue; + } + const SocketRef &linked_socket = *linked_sockets[item.link_index]; + const NodeRef &linked_node = linked_socket.node(); + ToposortNodeState &linked_node_state = node_states[linked_node.id()]; + if (linked_node_state.is_done) { + /* The linked node has already been visited. */ + item.link_index++; + continue; + } + if (linked_node_state.is_in_stack) { + result.has_cycle = true; + } + else { + nodes_to_check.push({&linked_node}); + linked_node_state.is_in_stack = true; + } + break; + } + + /* If no other element has been pushed, the current node can be pushed to the sorted list. */ + if (&item == &nodes_to_check.peek()) { + ToposortNodeState &node_state = node_states[node.id()]; + node_state.is_done = true; + node_state.is_in_stack = false; + result.sorted_nodes.append(&node); + nodes_to_check.pop(); + } + } +} + +/** + * Sort nodes topologically from left to right or right to left. + * In the future the result if this could be cached on #NodeTreeRef. + */ +NodeTreeRef::ToposortResult NodeTreeRef::toposort(const ToposortDirection direction) const +{ + ToposortResult result; + result.sorted_nodes.reserve(nodes_by_id_.size()); + + Array node_states(nodes_by_id_.size()); + + for (const NodeRef *node : nodes_by_id_) { + if (node_states[node->id()].is_done) { /* Ignore nodes that are done already. */ continue; } - if (start_node->any_socket_is_directly_linked( + if (node->any_socket_is_directly_linked( direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) { /* Ignore non-start nodes. */ continue; } - /* Do a depth-first search to sort nodes topologically. */ - nodes_to_check.push({start_node}); - while (!nodes_to_check.is_empty()) { - Item &item = nodes_to_check.peek(); - const NodeRef &node = *item.node; - const Span sockets = node.sockets( - direction == ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT); + toposort_from_start_node(direction, *node, node_states, result); + } - while (true) { - if (item.socket_index == sockets.size()) { - /* All sockets have already been visited. */ - break; - } - const SocketRef &socket = *sockets[item.socket_index]; - const Span linked_sockets = socket.directly_linked_sockets(); - if (item.link_index == linked_sockets.size()) { - /* All links connected to this socket have already been visited. */ - item.socket_index++; - item.link_index = 0; - continue; - } - const SocketRef &linked_socket = *linked_sockets[item.link_index]; - const NodeRef &linked_node = linked_socket.node(); - if (node_is_done_by_id[linked_node.id()]) { - /* The linked node has already been visited. */ - item.link_index++; - continue; - } - nodes_to_check.push({&linked_node}); - break; - } - - /* If no other element has been pushed, the current node can be pushed to the sorted list. */ - if (&item == &nodes_to_check.peek()) { - node_is_done_by_id[node.id()] = true; - toposort.append(&node); - nodes_to_check.pop(); + /* Check if the loop above forgot some nodes because there is a cycle. */ + if (result.sorted_nodes.size() < nodes_by_id_.size()) { + result.has_cycle = true; + for (const NodeRef *node : nodes_by_id_) { + if (node_states[node->id()].is_done) { + /* Ignore nodes that are done already. */ + continue; } + /* Start toposort at this node which is somewhere in the middle of a loop. */ + toposort_from_start_node(direction, *node, node_states, result); } } - return toposort; + BLI_assert(result.sorted_nodes.size() == nodes_by_id_.size()); + return result; } const NodeRef *NodeTreeRef::find_node(const bNode &bnode) const From 051bb46c55010042f1f16fde71c9de2f5af6b6de Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 12:36:42 +0200 Subject: [PATCH 1275/1500] Fix T92241: curve radius and tilt swapped after resampling This only happened when the spline contained a single point. --- source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index d3e7521631f..cc0fc6500c4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -79,7 +79,7 @@ static SplinePtr resample_spline(const Spline &src, const int count) Spline::copy_base_settings(src, *dst); if (src.evaluated_edges_size() < 1 || count == 1) { - dst->add_point(src.positions().first(), src.tilts().first(), src.radii().first()); + dst->add_point(src.positions().first(), src.radii().first(), src.tilts().first()); dst->attributes.reallocate(1); src.attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { From 18b6f0d0f18732a071b75ad56a2475c383d19111 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 12:43:04 +0200 Subject: [PATCH 1276/1500] Fix T92264: incorrect field inferencing when muting link between reroutes Previously, muted links were just ignored considered by field inferencing. Now muted links behave like normal links in the inferencing process. --- source/blender/blenkernel/intern/node.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 95192003f9f..3297bf29ee4 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4679,7 +4679,7 @@ static OutputFieldDependency find_group_output_dependencies( while (!sockets_to_check.is_empty()) { const InputSocketRef *input_socket = sockets_to_check.pop(); - for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) { + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { const NodeRef &origin_node = origin_socket->node(); const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; @@ -4848,14 +4848,14 @@ static void propagate_field_status_from_left_to_right( continue; } state.is_single = true; - if (input_socket->logically_linked_sockets().is_empty()) { + if (input_socket->directly_linked_sockets().is_empty()) { if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::Implicit) { state.is_single = false; } } else { - for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) { + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { if (!field_state_by_socket_id[origin_socket->id()].is_single) { state.is_single = false; break; From 7b1c5712f888ea37bbccafd9ffd7a3a6a61e665f Mon Sep 17 00:00:00 2001 From: William Leeson Date: Wed, 27 Oct 2021 13:28:13 +0200 Subject: [PATCH 1277/1500] Cycles: Replace saturate with saturatef saturate is depricated in favour of __saturatef this replaces saturate with __saturatef on CUDA by createing a saturatef function which replaces all instances of saturate and are hooked up to the correct function on all platforms. Reviewed By: brecht Differential Revision: https://developer.blender.org/D13010 --- intern/cycles/kernel/closure/bsdf_microfacet.h | 18 +++++++++--------- .../kernel/closure/bsdf_microfacet_multi.h | 10 +++++----- intern/cycles/kernel/closure/bsdf_oren_nayar.h | 2 +- intern/cycles/kernel/closure/bsdf_toon.h | 8 ++++---- intern/cycles/kernel/film/passes.h | 2 +- intern/cycles/kernel/film/read.h | 6 +++--- intern/cycles/kernel/svm/bevel.h | 2 +- intern/cycles/kernel/svm/brick.h | 2 +- intern/cycles/kernel/svm/closure.h | 8 ++++---- intern/cycles/kernel/svm/color_util.h | 2 +- intern/cycles/kernel/svm/gradient.h | 2 +- intern/cycles/kernel/svm/hsv.h | 2 +- intern/cycles/kernel/svm/image.h | 6 +++--- intern/cycles/kernel/svm/musgrave.h | 8 ++++---- intern/cycles/kernel/svm/ramp.h | 4 ++-- intern/cycles/kernel/util/lookup_table.h | 4 ++-- intern/cycles/scene/constant_fold.cpp | 10 +++++----- intern/cycles/util/math.h | 7 ++++++- intern/cycles/util/math_float3.h | 2 +- 19 files changed, 55 insertions(+), 50 deletions(-) diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index 83242a73685..466ba3e229e 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -315,8 +315,8 @@ ccl_device int bsdf_microfacet_ggx_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->extra = NULL; - bsdf->alpha_x = saturate(bsdf->alpha_x); - bsdf->alpha_y = saturate(bsdf->alpha_y); + bsdf->alpha_x = saturatef(bsdf->alpha_x); + bsdf->alpha_y = saturatef(bsdf->alpha_y); bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_ID; @@ -336,8 +336,8 @@ ccl_device int bsdf_microfacet_ggx_fresnel_setup(ccl_private MicrofacetBsdf *bsd { bsdf->extra->cspec0 = saturate3(bsdf->extra->cspec0); - bsdf->alpha_x = saturate(bsdf->alpha_x); - bsdf->alpha_y = saturate(bsdf->alpha_y); + bsdf->alpha_x = saturatef(bsdf->alpha_x); + bsdf->alpha_y = saturatef(bsdf->alpha_y); bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID; @@ -351,7 +351,7 @@ ccl_device int bsdf_microfacet_ggx_clearcoat_setup(ccl_private MicrofacetBsdf *b { bsdf->extra->cspec0 = saturate3(bsdf->extra->cspec0); - bsdf->alpha_x = saturate(bsdf->alpha_x); + bsdf->alpha_x = saturatef(bsdf->alpha_x); bsdf->alpha_y = bsdf->alpha_x; bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID; @@ -365,7 +365,7 @@ ccl_device int bsdf_microfacet_ggx_refraction_setup(ccl_private MicrofacetBsdf * { bsdf->extra = NULL; - bsdf->alpha_x = saturate(bsdf->alpha_x); + bsdf->alpha_x = saturatef(bsdf->alpha_x); bsdf->alpha_y = bsdf->alpha_x; bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; @@ -783,8 +783,8 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg, ccl_device int bsdf_microfacet_beckmann_setup(ccl_private MicrofacetBsdf *bsdf) { - bsdf->alpha_x = saturate(bsdf->alpha_x); - bsdf->alpha_y = saturate(bsdf->alpha_y); + bsdf->alpha_x = saturatef(bsdf->alpha_x); + bsdf->alpha_y = saturatef(bsdf->alpha_y); bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_ID; return SD_BSDF | SD_BSDF_HAS_EVAL; @@ -800,7 +800,7 @@ ccl_device int bsdf_microfacet_beckmann_isotropic_setup(ccl_private MicrofacetBs ccl_device int bsdf_microfacet_beckmann_refraction_setup(ccl_private MicrofacetBsdf *bsdf) { - bsdf->alpha_x = saturate(bsdf->alpha_x); + bsdf->alpha_x = saturatef(bsdf->alpha_x); bsdf->alpha_y = bsdf->alpha_x; bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID; diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index 77370fbec4e..5badbe9aa80 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -220,12 +220,12 @@ ccl_device_forceinline float mf_lambda(const float3 w, const float2 alpha) /* Height distribution CDF (based on page 4 of the supplemental implementation). */ ccl_device_forceinline float mf_invC1(const float h) { - return 2.0f * saturate(h) - 1.0f; + return 2.0f * saturatef(h) - 1.0f; } ccl_device_forceinline float mf_C1(const float h) { - return saturate(0.5f * (h + 1.0f)); + return saturatef(0.5f * (h + 1.0f)); } /* Masking function (based on page 16 of the supplemental implementation). */ @@ -284,7 +284,7 @@ ccl_device_forceinline float mf_ggx_albedo(float r) 0.027803f) * r + 0.00568739f; - return saturate(albedo); + return saturatef(albedo); } ccl_device_inline float mf_ggx_transmission_albedo(float a, float ior) @@ -292,7 +292,7 @@ ccl_device_inline float mf_ggx_transmission_albedo(float a, float ior) if (ior < 1.0f) { ior = 1.0f / ior; } - a = saturate(a); + a = saturatef(a); ior = clamp(ior, 1.0f, 3.0f); float I_1 = 0.0476898f * expf(-0.978352f * (ior - 0.65657f) * (ior - 0.65657f)) - 0.033756f * ior + 0.993261f; @@ -302,7 +302,7 @@ ccl_device_inline float mf_ggx_transmission_albedo(float a, float ior) float R_2 = ((((5.3725f * a - 24.9307f) * a + 22.7437f) * a - 3.40751f) * a + 0.0986325f) * a + 0.00493504f; - return saturate(1.0f + I_2 * R_2 * 0.0019127f - (1.0f - I_1) * (1.0f - R_1) * 9.3205f); + return saturatef(1.0f + I_2 * R_2 * 0.0019127f - (1.0f - I_1) * (1.0f - R_1) * 9.3205f); } ccl_device_forceinline float mf_ggx_pdf(const float3 wi, const float3 wo, const float alpha) diff --git a/intern/cycles/kernel/closure/bsdf_oren_nayar.h b/intern/cycles/kernel/closure/bsdf_oren_nayar.h index 00c2678f0a0..8827309a811 100644 --- a/intern/cycles/kernel/closure/bsdf_oren_nayar.h +++ b/intern/cycles/kernel/closure/bsdf_oren_nayar.h @@ -50,7 +50,7 @@ ccl_device int bsdf_oren_nayar_setup(ccl_private OrenNayarBsdf *bsdf) bsdf->type = CLOSURE_BSDF_OREN_NAYAR_ID; - sigma = saturate(sigma); + sigma = saturatef(sigma); float div = 1.0f / (M_PI_F + ((3.0f * M_PI_F - 4.0f) / 6.0f) * sigma); diff --git a/intern/cycles/kernel/closure/bsdf_toon.h b/intern/cycles/kernel/closure/bsdf_toon.h index 7f20a328b5e..20f3b8f0074 100644 --- a/intern/cycles/kernel/closure/bsdf_toon.h +++ b/intern/cycles/kernel/closure/bsdf_toon.h @@ -48,8 +48,8 @@ static_assert(sizeof(ShaderClosure) >= sizeof(ToonBsdf), "ToonBsdf is too large! ccl_device int bsdf_diffuse_toon_setup(ccl_private ToonBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_DIFFUSE_TOON_ID; - bsdf->size = saturate(bsdf->size); - bsdf->smooth = saturate(bsdf->smooth); + bsdf->size = saturatef(bsdf->size); + bsdf->smooth = saturatef(bsdf->smooth); return SD_BSDF | SD_BSDF_HAS_EVAL; } @@ -146,8 +146,8 @@ ccl_device int bsdf_diffuse_toon_sample(ccl_private const ShaderClosure *sc, ccl_device int bsdf_glossy_toon_setup(ccl_private ToonBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_GLOSSY_TOON_ID; - bsdf->size = saturate(bsdf->size); - bsdf->smooth = saturate(bsdf->smooth); + bsdf->size = saturatef(bsdf->size); + bsdf->smooth = saturatef(bsdf->smooth); return SD_BSDF | SD_BSDF_HAS_EVAL; } diff --git a/intern/cycles/kernel/film/passes.h b/intern/cycles/kernel/film/passes.h index 3a91d1653fe..22b4b779a17 100644 --- a/intern/cycles/kernel/film/passes.h +++ b/intern/cycles/kernel/film/passes.h @@ -312,7 +312,7 @@ ccl_device_inline void kernel_write_data_passes(KernelGlobals kg, const float mist_inv_depth = kernel_data.film.mist_inv_depth; const float depth = camera_distance(kg, sd->P); - float mist = saturate((depth - mist_start) * mist_inv_depth); + float mist = saturatef((depth - mist_start) * mist_inv_depth); /* Falloff */ const float mist_falloff = kernel_data.film.mist_falloff; diff --git a/intern/cycles/kernel/film/read.h b/intern/cycles/kernel/film/read.h index a87eff3832e..d308a9818e2 100644 --- a/intern/cycles/kernel/film/read.h +++ b/intern/cycles/kernel/film/read.h @@ -27,7 +27,7 @@ CCL_NAMESPACE_BEGIN * roulette. */ ccl_device_forceinline float film_transparency_to_alpha(float transparency) { - return saturate(1.0f - transparency); + return saturatef(1.0f - transparency); } ccl_device_inline float film_get_scale(ccl_global const KernelFilmConvert *ccl_restrict @@ -136,7 +136,7 @@ ccl_device_inline void film_get_pass_pixel_mist(ccl_global const KernelFilmConve /* Note that we accumulate 1 - mist in the kernel to avoid having to * track the mist values in the integrator state. */ - pixel[0] = saturate(1.0f - f * scale_exposure); + pixel[0] = saturatef(1.0f - f * scale_exposure); } ccl_device_inline void film_get_pass_pixel_sample_count( @@ -458,7 +458,7 @@ ccl_device_inline float4 film_calculate_shadow_catcher_matte_with_shadow( const float3 color_matte = make_float3(in_matte[0], in_matte[1], in_matte[2]) * scale_exposure; const float transparency = in_matte[3] * scale; - const float alpha = saturate(1.0f - transparency); + const float alpha = saturatef(1.0f - transparency); const float alpha_matte = (1.0f - alpha) * (1.0f - average(shadow_catcher)) + alpha; diff --git a/intern/cycles/kernel/svm/bevel.h b/intern/cycles/kernel/svm/bevel.h index 37c7caf1372..6799489514f 100644 --- a/intern/cycles/kernel/svm/bevel.h +++ b/intern/cycles/kernel/svm/bevel.h @@ -73,7 +73,7 @@ ccl_device_forceinline float svm_bevel_cubic_quintic_root_find(float xi) if (fabsf(f) < tolerance || f_ == 0.0f) break; - x = saturate(x - f / f_); + x = saturatef(x - f / f_); } return x; diff --git a/intern/cycles/kernel/svm/brick.h b/intern/cycles/kernel/svm/brick.h index 3c8729fa027..d8d01766106 100644 --- a/intern/cycles/kernel/svm/brick.h +++ b/intern/cycles/kernel/svm/brick.h @@ -56,7 +56,7 @@ ccl_device_noinline_cpu float2 svm_brick(float3 p, x = (p.x + offset) - brick_width * bricknum; y = p.y - row_height * rownum; - float tint = saturate((brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias)); + float tint = saturatef((brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias)); float min_dist = min(min(x, y), min(brick_width - x, row_height - y)); float mortar; diff --git a/intern/cycles/kernel/svm/closure.h b/intern/cycles/kernel/svm/closure.h index 1dcfe003f74..71952e9e0f8 100644 --- a/intern/cycles/kernel/svm/closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -173,9 +173,9 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, float fresnel = fresnel_dielectric_cos(cosNO, ior); // calculate weights of the diffuse and specular part - float diffuse_weight = (1.0f - saturate(metallic)) * (1.0f - saturate(transmission)); + float diffuse_weight = (1.0f - saturatef(metallic)) * (1.0f - saturatef(transmission)); - float final_transmission = saturate(transmission) * (1.0f - saturate(metallic)); + float final_transmission = saturatef(transmission) * (1.0f - saturatef(metallic)); float specular_weight = (1.0f - final_transmission); // get the base color @@ -746,7 +746,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, if (bsdf) { bsdf->N = N; - bsdf->sigma = saturate(param1); + bsdf->sigma = saturatef(param1); sd->flag |= bsdf_ashikhmin_velvet_setup(bsdf); } break; @@ -1233,7 +1233,7 @@ ccl_device_noinline void svm_node_mix_closure(ccl_private ShaderData *sd, node.y, &weight_offset, &in_weight_offset, &weight1_offset, &weight2_offset); float weight = stack_load_float(stack, weight_offset); - weight = saturate(weight); + weight = saturatef(weight); float in_weight = (stack_valid(in_weight_offset)) ? stack_load_float(stack, in_weight_offset) : 1.0f; diff --git a/intern/cycles/kernel/svm/color_util.h b/intern/cycles/kernel/svm/color_util.h index 82024b61ba4..0c1a510e655 100644 --- a/intern/cycles/kernel/svm/color_util.h +++ b/intern/cycles/kernel/svm/color_util.h @@ -262,7 +262,7 @@ ccl_device float3 svm_mix_clamp(float3 col) ccl_device_noinline_cpu float3 svm_mix(NodeMix type, float fac, float3 c1, float3 c2) { - float t = saturate(fac); + float t = saturatef(fac); switch (type) { case NODE_MIX_BLEND: diff --git a/intern/cycles/kernel/svm/gradient.h b/intern/cycles/kernel/svm/gradient.h index 852196b73dc..42d8dbef792 100644 --- a/intern/cycles/kernel/svm/gradient.h +++ b/intern/cycles/kernel/svm/gradient.h @@ -73,7 +73,7 @@ ccl_device_noinline void svm_node_tex_gradient(ccl_private ShaderData *sd, float3 co = stack_load_float3(stack, co_offset); float f = svm_gradient(co, (NodeGradientType)type); - f = saturate(f); + f = saturatef(f); if (stack_valid(fac_offset)) stack_store_float(stack, fac_offset, f); diff --git a/intern/cycles/kernel/svm/hsv.h b/intern/cycles/kernel/svm/hsv.h index f6881fd4512..fdb266883fa 100644 --- a/intern/cycles/kernel/svm/hsv.h +++ b/intern/cycles/kernel/svm/hsv.h @@ -40,7 +40,7 @@ ccl_device_noinline void svm_node_hsv(KernelGlobals kg, /* Remember: `fmodf` doesn't work for negative numbers here. */ color.x = fmodf(color.x + hue + 0.5f, 1.0f); - color.y = saturate(color.y * sat); + color.y = saturatef(color.y * sat); color.z *= val; color = hsv_to_rgb(color); diff --git a/intern/cycles/kernel/svm/image.h b/intern/cycles/kernel/svm/image.h index 6ddf98a6ef1..2ebd3d4eb87 100644 --- a/intern/cycles/kernel/svm/image.h +++ b/intern/cycles/kernel/svm/image.h @@ -167,17 +167,17 @@ ccl_device_noinline void svm_node_tex_image_box(KernelGlobals kg, /* in case of blending, test for mixes between two textures */ if (N.z < (1.0f - limit) * (N.y + N.x)) { weight.x = N.x / (N.x + N.y); - weight.x = saturate((weight.x - 0.5f * (1.0f - blend)) / blend); + weight.x = saturatef((weight.x - 0.5f * (1.0f - blend)) / blend); weight.y = 1.0f - weight.x; } else if (N.x < (1.0f - limit) * (N.y + N.z)) { weight.y = N.y / (N.y + N.z); - weight.y = saturate((weight.y - 0.5f * (1.0f - blend)) / blend); + weight.y = saturatef((weight.y - 0.5f * (1.0f - blend)) / blend); weight.z = 1.0f - weight.y; } else if (N.y < (1.0f - limit) * (N.x + N.z)) { weight.x = N.x / (N.x + N.z); - weight.x = saturate((weight.x - 0.5f * (1.0f - blend)) / blend); + weight.x = saturatef((weight.x - 0.5f * (1.0f - blend)) / blend); weight.z = 1.0f - weight.x; } else { diff --git a/intern/cycles/kernel/svm/musgrave.h b/intern/cycles/kernel/svm/musgrave.h index 4225c3d2d71..85e32eee638 100644 --- a/intern/cycles/kernel/svm/musgrave.h +++ b/intern/cycles/kernel/svm/musgrave.h @@ -180,7 +180,7 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_1d( for (int i = 1; i < float_to_int(octaves); i++) { p *= lacunarity; - weight = saturate(signal * gain); + weight = saturatef(signal * gain); signal = offset - fabsf(snoise_1d(p)); signal *= signal; signal *= weight; @@ -351,7 +351,7 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_2d( for (int i = 1; i < float_to_int(octaves); i++) { p *= lacunarity; - weight = saturate(signal * gain); + weight = saturatef(signal * gain); signal = offset - fabsf(snoise_2d(p)); signal *= signal; signal *= weight; @@ -522,7 +522,7 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_3d( for (int i = 1; i < float_to_int(octaves); i++) { p *= lacunarity; - weight = saturate(signal * gain); + weight = saturatef(signal * gain); signal = offset - fabsf(snoise_3d(p)); signal *= signal; signal *= weight; @@ -693,7 +693,7 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_4d( for (int i = 1; i < float_to_int(octaves); i++) { p *= lacunarity; - weight = saturate(signal * gain); + weight = saturatef(signal * gain); signal = offset - fabsf(snoise_4d(p)); signal *= signal; signal *= weight; diff --git a/intern/cycles/kernel/svm/ramp.h b/intern/cycles/kernel/svm/ramp.h index 1dc3383956d..61093e0bd82 100644 --- a/intern/cycles/kernel/svm/ramp.h +++ b/intern/cycles/kernel/svm/ramp.h @@ -44,7 +44,7 @@ ccl_device_inline float float_ramp_lookup( return t0 + dy * f * (table_size - 1); } - f = saturate(f) * (table_size - 1); + f = saturatef(f) * (table_size - 1); /* clamp int as well in case of NaN */ int i = clamp(float_to_int(f), 0, table_size - 1); @@ -76,7 +76,7 @@ ccl_device_inline float4 rgb_ramp_lookup( return t0 + dy * f * (table_size - 1); } - f = saturate(f) * (table_size - 1); + f = saturatef(f) * (table_size - 1); /* clamp int as well in case of NaN */ int i = clamp(float_to_int(f), 0, table_size - 1); diff --git a/intern/cycles/kernel/util/lookup_table.h b/intern/cycles/kernel/util/lookup_table.h index 2c26e668d7b..3ffbb4856da 100644 --- a/intern/cycles/kernel/util/lookup_table.h +++ b/intern/cycles/kernel/util/lookup_table.h @@ -22,7 +22,7 @@ CCL_NAMESPACE_BEGIN ccl_device float lookup_table_read(KernelGlobals kg, float x, int offset, int size) { - x = saturate(x) * (size - 1); + x = saturatef(x) * (size - 1); int index = min(float_to_int(x), size - 1); int nindex = min(index + 1, size - 1); @@ -39,7 +39,7 @@ ccl_device float lookup_table_read(KernelGlobals kg, float x, int offset, int si ccl_device float lookup_table_read_2D( KernelGlobals kg, float x, float y, int offset, int xsize, int ysize) { - y = saturate(y) * (ysize - 1); + y = saturatef(y) * (ysize - 1); int index = min(float_to_int(y), ysize - 1); int nindex = min(index + 1, ysize - 1); diff --git a/intern/cycles/scene/constant_fold.cpp b/intern/cycles/scene/constant_fold.cpp index ca065e3f678..a1fa34e7628 100644 --- a/intern/cycles/scene/constant_fold.cpp +++ b/intern/cycles/scene/constant_fold.cpp @@ -68,15 +68,15 @@ void ConstantFolder::make_constant(float3 value) const void ConstantFolder::make_constant_clamp(float value, bool clamp) const { - make_constant(clamp ? saturate(value) : value); + make_constant(clamp ? saturatef(value) : value); } void ConstantFolder::make_constant_clamp(float3 value, bool clamp) const { if (clamp) { - value.x = saturate(value.x); - value.y = saturate(value.y); - value.z = saturate(value.z); + value.x = saturatef(value.x); + value.y = saturatef(value.y); + value.z = saturatef(value.z); } make_constant(value); @@ -215,7 +215,7 @@ void ConstantFolder::fold_mix(NodeMix type, bool clamp) const ShaderInput *color1_in = node->input("Color1"); ShaderInput *color2_in = node->input("Color2"); - float fac = saturate(node->get_float(fac_in->socket_type)); + float fac = saturatef(node->get_float(fac_in->socket_type)); bool fac_is_zero = !fac_in->link && fac == 0.0f; bool fac_is_one = !fac_in->link && fac == 1.0f; diff --git a/intern/cycles/util/math.h b/intern/cycles/util/math.h index e7fc492733f..e4c7df6e44a 100644 --- a/intern/cycles/util/math.h +++ b/intern/cycles/util/math.h @@ -347,10 +347,15 @@ ccl_device_inline float smoothstep(float edge0, float edge1, float x) } #ifndef __KERNEL_CUDA__ -ccl_device_inline float saturate(float a) +ccl_device_inline float saturatef(float a) { return clamp(a, 0.0f, 1.0f); } +#else +ccl_device_inline float saturatef(float a) +{ + return __saturatef(a); +} #endif /* __KERNEL_CUDA__ */ ccl_device_inline int float_to_int(float f) diff --git a/intern/cycles/util/math_float3.h b/intern/cycles/util/math_float3.h index e780d7e0a7c..81550c5d03c 100644 --- a/intern/cycles/util/math_float3.h +++ b/intern/cycles/util/math_float3.h @@ -408,7 +408,7 @@ ccl_device_inline float3 project(const float3 v, const float3 v_proj) ccl_device_inline float3 saturate3(float3 a) { - return make_float3(saturate(a.x), saturate(a.y), saturate(a.z)); + return make_float3(saturatef(a.x), saturatef(a.y), saturatef(a.z)); } ccl_device_inline float3 normalize_len(const float3 a, ccl_private float *t) From 82cf25dfbfad39a64b620c20bbd0d65915827a44 Mon Sep 17 00:00:00 2001 From: William Leeson Date: Wed, 27 Oct 2021 14:14:43 +0200 Subject: [PATCH 1278/1500] Cycles: Scrambling distance for the PMJ sampler Adds scrambling distance to the PMJ sampler. This is based on the work by Mathieu Menuet in D12318 who created the original implementation for the Sobol sampler. Reviewed By: brecht Maniphest Tasks: T92181 Differential Revision: https://developer.blender.org/D12854 --- intern/cycles/blender/addon/ui.py | 4 +-- intern/cycles/blender/sync.cpp | 10 +++--- intern/cycles/integrator/tile.cpp | 36 ++----------------- intern/cycles/kernel/sample/jitter.h | 54 ++++++++++++++++++++-------- 4 files changed, 51 insertions(+), 53 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 47907481b03..dfaae666785 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -290,11 +290,11 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): col.active = not(cscene.use_adaptive_sampling) col.prop(cscene, "sampling_pattern", text="Pattern") col = layout.column(align=True) - col.active = cscene.sampling_pattern == 'SOBOL' and not cscene.use_adaptive_sampling + col.active = not cscene.use_adaptive_sampling col.prop(cscene, "scrambling_distance", text="Scrambling Distance Strength") col.prop(cscene, "adaptive_scrambling_distance", text="Adaptive Scrambling Distance") col = layout.column(align=True) - col.active = ((cscene.scrambling_distance < 1.0) or cscene.adaptive_scrambling_distance) and cscene.sampling_pattern == 'SOBOL' and not cscene.use_adaptive_sampling + col.active = ((cscene.scrambling_distance < 1.0) or cscene.adaptive_scrambling_distance) and not cscene.use_adaptive_sampling col.prop(cscene, "preview_scrambling_distance", text="Viewport Scrambling Distance") layout.separator() diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index f6f490077a7..e0a13625962 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -340,14 +340,16 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background) cscene, "sampling_pattern", SAMPLING_NUM_PATTERNS, SAMPLING_PATTERN_SOBOL); integrator->set_sampling_pattern(sampling_pattern); + bool use_adaptive_sampling = false; if (preview) { - integrator->set_use_adaptive_sampling( - RNA_boolean_get(&cscene, "use_preview_adaptive_sampling")); + use_adaptive_sampling = RNA_boolean_get(&cscene, "use_preview_adaptive_sampling"); + integrator->set_use_adaptive_sampling(use_adaptive_sampling); integrator->set_adaptive_threshold(get_float(cscene, "preview_adaptive_threshold")); integrator->set_adaptive_min_samples(get_int(cscene, "preview_adaptive_min_samples")); } else { - integrator->set_use_adaptive_sampling(RNA_boolean_get(&cscene, "use_adaptive_sampling")); + use_adaptive_sampling = RNA_boolean_get(&cscene, "use_adaptive_sampling"); + integrator->set_use_adaptive_sampling(use_adaptive_sampling); integrator->set_adaptive_threshold(get_float(cscene, "adaptive_threshold")); integrator->set_adaptive_min_samples(get_int(cscene, "adaptive_min_samples")); } @@ -361,7 +363,7 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background) /* only use scrambling distance in the viewport if user wants to and disable with AS */ bool preview_scrambling_distance = get_boolean(cscene, "preview_scrambling_distance"); - if ((preview && !preview_scrambling_distance) || sampling_pattern != SAMPLING_PATTERN_SOBOL) + if ((preview && !preview_scrambling_distance) || use_adaptive_sampling) scrambling_distance = 1.0f; VLOG(1) << "Used Scrambling Distance: " << scrambling_distance; diff --git a/intern/cycles/integrator/tile.cpp b/intern/cycles/integrator/tile.cpp index b49e1b27b83..4a1558cce09 100644 --- a/intern/cycles/integrator/tile.cpp +++ b/intern/cycles/integrator/tile.cpp @@ -74,39 +74,9 @@ TileSize tile_calculate_best_size(const int2 &image_size, TileSize tile_size; const int num_path_states_per_sample = max_num_path_states / num_samples; if (scrambling_distance < 0.9f) { - /* Prefer large tiles for scrambling distance. */ - if (image_size.x * image_size.y <= num_path_states_per_sample) { - tile_size.width = image_size.x; - tile_size.height = image_size.y; - } - else { - /* Pick the option with the biggest tile size */ - int heightOption = num_path_states_per_sample / image_size.x; - int widthOption = num_path_states_per_sample / image_size.y; - // Check if these options are possible - if ((heightOption > 0) || (widthOption > 0)) { - int area1 = image_size.x * heightOption; - int area2 = widthOption * image_size.y; - /* The option with the biggest pixel area */ - if (area1 >= area2) { - tile_size.width = image_size.x; - tile_size.height = heightOption; - } - else { - tile_size.width = widthOption; - tile_size.height = image_size.y; - } - } - else { // Large tiles are not an option so use square tiles - if (num_path_states_per_sample != 0) { - tile_size.width = round_down_to_power_of_two(lround(sqrt(num_path_states_per_sample))); - tile_size.height = tile_size.width; - } - else { - tile_size.width = tile_size.height = 1; - } - } - } + /* Prefer large tiles for scrambling distance, bounded by max num path states. */ + tile_size.width = min(image_size.x, max_num_path_states); + tile_size.height = min(image_size.y, max(max_num_path_states / tile_size.width, 1)); } else { /* Calculate tile size as if it is the most possible one to fit an entire range of samples. diff --git a/intern/cycles/kernel/sample/jitter.h b/intern/cycles/kernel/sample/jitter.h index b62ec7fda42..b76b9b8f23e 100644 --- a/intern/cycles/kernel/sample/jitter.h +++ b/intern/cycles/kernel/sample/jitter.h @@ -72,13 +72,27 @@ ccl_device_inline float cmj_randfloat_simple(uint i, uint p) return cmj_hash_simple(i, p) * (1.0f / (float)0xFFFFFFFF); } +ccl_device_inline float cmj_randfloat_simple_dist(uint i, uint p, float d) +{ + return cmj_hash_simple(i, p) * (d / (float)0xFFFFFFFF); +} + ccl_device float pmj_sample_1D(KernelGlobals kg, uint sample, uint rng_hash, uint dimension) { + uint hash = rng_hash; + float jitter_x = 0.0f; + if (kernel_data.integrator.scrambling_distance < 1.0f) { + hash = kernel_data.integrator.seed; + + jitter_x = cmj_randfloat_simple_dist( + dimension, rng_hash, kernel_data.integrator.scrambling_distance); + } + /* Perform Owen shuffle of the sample number to reorder the samples. */ #ifdef _SIMPLE_HASH_ - const uint rv = cmj_hash_simple(dimension, rng_hash); + const uint rv = cmj_hash_simple(dimension, hash); #else /* Use a _REGULAR_HASH_. */ - const uint rv = cmj_hash(dimension, rng_hash); + const uint rv = cmj_hash(dimension, hash); #endif #ifdef _XOR_SHUFFLE_ # warning "Using XOR shuffle." @@ -101,12 +115,12 @@ ccl_device float pmj_sample_1D(KernelGlobals kg, uint sample, uint rng_hash, uin #ifndef _NO_CRANLEY_PATTERSON_ROTATION_ /* Use Cranley-Patterson rotation to displace the sample pattern. */ # ifdef _SIMPLE_HASH_ - float dx = cmj_randfloat_simple(d, rng_hash); + float dx = cmj_randfloat_simple(d, hash); # else - float dx = cmj_randfloat(d, rng_hash); + float dx = cmj_randfloat(d, hash); # endif /* Jitter sample locations and map back into [0 1]. */ - fx = fx + dx; + fx = fx + dx + jitter_x; fx = fx - floorf(fx); #else # warning "Not using Cranley-Patterson Rotation." @@ -122,11 +136,23 @@ ccl_device void pmj_sample_2D(KernelGlobals kg, ccl_private float *x, ccl_private float *y) { + uint hash = rng_hash; + float jitter_x = 0.0f; + float jitter_y = 0.0f; + if (kernel_data.integrator.scrambling_distance < 1.0f) { + hash = kernel_data.integrator.seed; + + jitter_x = cmj_randfloat_simple_dist( + dimension, rng_hash, kernel_data.integrator.scrambling_distance); + jitter_y = cmj_randfloat_simple_dist( + dimension + 1, rng_hash, kernel_data.integrator.scrambling_distance); + } + /* Perform a shuffle on the sample number to reorder the samples. */ #ifdef _SIMPLE_HASH_ - const uint rv = cmj_hash_simple(dimension, rng_hash); + const uint rv = cmj_hash_simple(dimension, hash); #else /* Use a _REGULAR_HASH_. */ - const uint rv = cmj_hash(dimension, rng_hash); + const uint rv = cmj_hash(dimension, hash); #endif #ifdef _XOR_SHUFFLE_ # warning "Using XOR shuffle." @@ -137,7 +163,7 @@ ccl_device void pmj_sample_2D(KernelGlobals kg, /* Based on the sample number a sample pattern is selected and offset by the dimension. */ const uint sample_set = s / NUM_PMJ_SAMPLES; - const uint d = (dimension + sample_set); + const uint d = dimension + sample_set; uint dim = d % NUM_PMJ_PATTERNS; int index = 2 * (dim * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)); @@ -147,15 +173,15 @@ ccl_device void pmj_sample_2D(KernelGlobals kg, #ifndef _NO_CRANLEY_PATTERSON_ROTATION_ /* Use Cranley-Patterson rotation to displace the sample pattern. */ # ifdef _SIMPLE_HASH_ - float dx = cmj_randfloat_simple(d, rng_hash); - float dy = cmj_randfloat_simple(d + 1, rng_hash); + float dx = cmj_randfloat_simple(d, hash); + float dy = cmj_randfloat_simple(d + 1, hash); # else - float dx = cmj_randfloat(d, rng_hash); - float dy = cmj_randfloat(d + 1, rng_hash); + float dx = cmj_randfloat(d, hash); + float dy = cmj_randfloat(d + 1, hash); # endif /* Jitter sample locations and map back to the unit square [0 1]x[0 1]. */ - float sx = fx + dx; - float sy = fy + dy; + float sx = fx + dx + jitter_x; + float sy = fy + dy + jitter_y; sx = sx - floorf(sx); sy = sy - floorf(sy); #else From 8507336e76902604a2128b23a4d8e52094031ab0 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 27 Oct 2021 14:40:18 +0200 Subject: [PATCH 1279/1500] Fix T92423: Blender freeze rendering animation with Mantaflow Mantaflow could steal tasks from dependency graph, which under certain conditions causes a recursive lock involving GIL. Isolate threading done in mantaflow when it is interfaced form the dependency graph. Isolation done from the modifier, since the deeper calls are branching out quite quickly. Differential Revision: https://developer.blender.org/D13011 --- source/blender/modifiers/intern/MOD_fluid.c | 39 +++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index a14d582063a..acc92fde766 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -25,6 +25,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_task.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -112,6 +113,29 @@ static void requiredDataMask(Object *UNUSED(ob), } } +typedef struct FluidIsolationData { + Depsgraph *depsgraph; + Object *object; + Mesh *mesh; + FluidModifierData *fmd; + + Mesh *result; +} FluidIsolationData; + +static void fluid_modifier_do_isolated(void *userdata) +{ + FluidIsolationData *isolation_data = (FluidIsolationData *)userdata; + + Scene *scene = DEG_get_evaluated_scene(isolation_data->depsgraph); + + Mesh *result = BKE_fluid_modifier_do(isolation_data->fmd, + isolation_data->depsgraph, + scene, + isolation_data->object, + isolation_data->mesh); + isolation_data->result = result ? result : isolation_data->mesh; +} + static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *me) { #ifndef WITH_FLUID @@ -125,10 +149,19 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return me; } - Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); + /* Isolate execution of Mantaflow when running from dependency graph. The reason for this is + * because Mantaflow uses TBB to parallel its own computation which without isolation will start + * stealing tasks from dependency graph. Stealing tasks from the dependency graph might cause + * a recursive lock when Python drivers are used (because Mantaflow is interfaced via Python as + * well. */ + FluidIsolationData isolation_data; + isolation_data.depsgraph = ctx->depsgraph; + isolation_data.object = ctx->object; + isolation_data.mesh = me; + isolation_data.fmd = fmd; + BLI_task_isolate(fluid_modifier_do_isolated, &isolation_data); - result = BKE_fluid_modifier_do(fmd, ctx->depsgraph, scene, ctx->object, me); - return result ? result : me; + return isolation_data.result; #endif /* WITH_FLUID */ } From 1832e11f39a36681533c148de9300b290a8c309c Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 14:48:00 +0200 Subject: [PATCH 1280/1500] UI: Support dragging tree-view items Adds the needed bits to the UI tree-view API to support dragging tree-view items. This isn't used yet, but will be in the following commit for asset catalogs. There will probably be some further tweaks to the design at some point, for now this should work well enough for our use-cases. --- source/blender/editors/include/UI_interface.h | 1 + .../blender/editors/include/UI_tree_view.hh | 18 +++++++++ .../editors/interface/interface_handlers.c | 38 ++++++++++++++----- source/blender/editors/interface/tree_view.cc | 31 +++++++++++++++ 4 files changed, 79 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 12fdda2092c..0f290d4255b 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2777,6 +2777,7 @@ void UI_interface_tag_script_reload(void); bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); +bool UI_tree_view_item_drag_start(struct bContext *C, uiTreeViewItemHandle *item_); bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const struct wmDrag *drag, const char **r_disabled_hint); diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 905181a7251..0990d844d48 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -49,6 +49,7 @@ namespace blender::ui { class AbstractTreeView; class AbstractTreeViewItem; class AbstractTreeViewItemDropController; +class AbstractTreeViewItemDragController; /* ---------------------------------------------------------------------- */ /** \name Tree-View Item Container @@ -273,6 +274,11 @@ class AbstractTreeViewItem : public TreeViewItemContainer { */ virtual bool matches(const AbstractTreeViewItem &other) const; + /** + * If an item wants to support being dragged, it has to return a drag controller here. + * That is an object implementing #AbstractTreeViewItemDragController. + */ + virtual std::unique_ptr create_drag_controller() const; /** * If an item wants to support dropping data into it, it has to return a drop controller here. * That is an object implementing #AbstractTreeViewItemDropController. @@ -347,6 +353,18 @@ class AbstractTreeViewItem : public TreeViewItemContainer { /** \name Drag 'n Drop * \{ */ +/** + * Class to enable dragging a tree-item. An item can return a drop controller for itself via a + * custom implementation of #AbstractTreeViewItem::create_drag_controller(). + */ +class AbstractTreeViewItemDragController { + public: + virtual ~AbstractTreeViewItemDragController() = default; + + virtual int get_drag_type() const = 0; + virtual void *create_drag_data() const = 0; +}; + /** * Class to customize the drop behavior of a tree-item, plus the behavior when dragging over this * item. An item can return a drop controller for itself via a custom implementation of diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index b21d2127030..6dd6f9eb359 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2145,6 +2145,12 @@ static bool ui_but_drag_init(bContext *C, return false; } } + else if (but->type == UI_BTYPE_TREEROW) { + uiButTreeRow *tree_row_but = (uiButTreeRow *)but; + if (tree_row_but->tree_item) { + UI_tree_view_item_drag_start(C, tree_row_but->tree_item); + } + } else { wmDrag *drag = WM_event_start_drag( C, @@ -4821,19 +4827,33 @@ static int ui_do_but_TREEROW(bContext *C, if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE) { - if (event->val == KM_CLICK) { - button_activate_state(C, but, BUTTON_STATE_EXIT); - return WM_UI_HANDLER_BREAK; - } - if (event->val == KM_DBL_CLICK) { - data->cancel = true; + switch (event->val) { + case KM_PRESS: + /* Extra icons have priority, don't mess with them. */ + if (ui_but_extra_operator_icon_mouse_over_get(but, data, event)) { + return WM_UI_HANDLER_BREAK; + } + button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; + return WM_UI_HANDLER_CONTINUE; - UI_tree_view_item_begin_rename(tree_row_but->tree_item); - ED_region_tag_redraw(CTX_wm_region(C)); - return WM_UI_HANDLER_BREAK; + case KM_CLICK: + button_activate_state(C, but, BUTTON_STATE_EXIT); + return WM_UI_HANDLER_BREAK; + + case KM_DBL_CLICK: + data->cancel = true; + UI_tree_view_item_begin_rename(tree_row_but->tree_item); + ED_region_tag_redraw(CTX_wm_region(C)); + return WM_UI_HANDLER_BREAK; } } } + else if (data->state == BUTTON_STATE_WAIT_DRAG) { + /* Let "default" button handling take care of the drag logic. */ + return ui_do_but_EXIT(C, but, data, event); + } return WM_UI_HANDLER_CONTINUE; } diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 04d7a066b36..c08fa51d5a5 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -29,6 +29,7 @@ #include "UI_interface.h" +#include "WM_api.h" #include "WM_types.h" #include "UI_tree_view.hh" @@ -354,6 +355,13 @@ void AbstractTreeViewItem::is_active(IsActiveFn is_active_fn) is_active_fn_ = is_active_fn; } +std::unique_ptr AbstractTreeViewItem::create_drag_controller() + const +{ + /* There's no drag controller (and hence no drag support) by default. */ + return nullptr; +} + std::unique_ptr AbstractTreeViewItem::create_drop_controller() const { @@ -686,6 +694,29 @@ bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, return a.matches_including_parents(b); } +/** + * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't + * support dragging, i.e. it won't create a drag-controller upon request. + * \return True if dragging started successfully, otherwise false. + */ +bool UI_tree_view_item_drag_start(bContext *C, uiTreeViewItemHandle *item_) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_); + const std::unique_ptr drag_controller = + item.create_drag_controller(); + if (!drag_controller) { + return false; + } + + WM_event_start_drag(C, + ICON_NONE, + drag_controller->get_drag_type(), + drag_controller->create_drag_data(), + 0, + WM_DRAG_FREE_DATA); + return true; +} + bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const wmDrag *drag, const char **r_disabled_hint) From aae5f15238f73fcac762a1690e052b86fad23be1 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 14:50:48 +0200 Subject: [PATCH 1281/1500] Asset Browser: Support dragging catalogs to move them in the hierarchy Uses the additions to the UI tree-view API from the previous commit to enable drag & drop of asset catalogs. The catalogs will be moved in the tree including children. A remaining issue is that a catalog with children will always be collapsed when dropping. I need to find a way to fix that in the tree-view API. There are a few improvements I can think of for the tree-item drag & drop support, but time for these is too short. These can be done as normal cleanups at some point. --- .../blender/editors/asset/ED_asset_catalog.hh | 3 + .../editors/asset/intern/asset_catalog.cc | 27 ++++ .../space_file/asset_catalog_tree_view.cc | 135 +++++++++++++++--- source/blender/windowmanager/WM_api.h | 2 + source/blender/windowmanager/WM_types.h | 6 + .../windowmanager/intern/wm_dragdrop.c | 10 ++ 6 files changed, 161 insertions(+), 22 deletions(-) diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh index 8b8fc4d3574..8da8fc0d6c9 100644 --- a/source/blender/editors/asset/ED_asset_catalog.hh +++ b/source/blender/editors/asset/ED_asset_catalog.hh @@ -37,3 +37,6 @@ void ED_asset_catalog_remove(AssetLibrary *library, const blender::bke::CatalogI void ED_asset_catalog_rename(AssetLibrary *library, blender::bke::CatalogID catalog_id, blender::StringRefNull new_name); +void ED_asset_catalog_move(AssetLibrary *library, + blender::bke::CatalogID src_catalog_id, + blender::bke::CatalogID dst_parent_catalog_id); diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index f3ba12a6324..8e1e5be2e47 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -121,6 +121,33 @@ void ED_asset_catalog_rename(::AssetLibrary *library, WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); } +void ED_asset_catalog_move(::AssetLibrary *library, + const CatalogID src_catalog_id, + const CatalogID dst_parent_catalog_id) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + BLI_assert_unreachable(); + return; + } + + AssetCatalog *src_catalog = catalog_service->find_catalog(src_catalog_id); + AssetCatalog *dst_catalog = catalog_service->find_catalog(dst_parent_catalog_id); + + const AssetCatalogPath new_path = dst_catalog->path / StringRef(src_catalog->path.name()); + const AssetCatalogPath clean_new_path = new_path.cleanup(); + + if (new_path == src_catalog->path || clean_new_path == src_catalog->path) { + /* Nothing changed, so don't bother renaming for nothing. */ + return; + } + + catalog_service->undo_push(); + catalog_service->tag_has_unsaved_changes(src_catalog); + catalog_service->update_catalog_path(src_catalog_id, clean_new_path); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); +} + void ED_asset_catalogs_save_from_main_path(::AssetLibrary *library, const Main *bmain) { bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 53981ca244d..e6b76e05e16 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -95,26 +95,44 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { bool can_rename() const override; bool rename(StringRefNull new_name) override; + /** Add drag support for catalog items. */ + std::unique_ptr create_drag_controller() const override; /** Add dropping support for catalog items. */ std::unique_ptr create_drop_controller() const override; }; +class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController { + AssetCatalogTreeItem &catalog_item_; + + public: + explicit AssetCatalogDragController(AssetCatalogTreeItem &catalog_item); + + int get_drag_type() const override; + void *create_drag_data() const override; +}; + class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController { AssetCatalogTreeItem &catalog_item_; public: - explicit AssetCatalogDropController(AssetCatalogTreeView &tree_view, - AssetCatalogTreeItem &catalog_item); + AssetCatalogDropController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item); bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; std::string drop_tooltip(const wmDrag &drag) const override; bool on_drop(const wmDrag &drag) override; - static bool has_droppable_item(const wmDrag &drag, const char **r_disabled_hint); - static bool drop_into_catalog(const AssetCatalogTreeView &tree_view, - const wmDrag &drag, - CatalogID catalog_id, - StringRefNull simple_name = ""); + ::AssetLibrary &get_asset_library() const; + + static bool has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint); + static bool drop_assets_into_catalog(const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name = ""); + + private: + bool drop_asset_catalog_into_catalog(const wmDrag &drag); + std::string drop_tooltip_asset_list(const wmDrag &drag) const; + std::string drop_tooltip_asset_catalog(const wmDrag &drag) const; }; /** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level @@ -308,6 +326,12 @@ std::unique_ptr AssetCatalogTreeViewItem static_cast(get_tree_view()), catalog_item_); } +std::unique_ptr AssetCatalogTreeViewItem:: + create_drag_controller() const +{ + return std::make_unique(catalog_item_); +} + /* ---------------------------------------------------------------------- */ AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view, @@ -318,14 +342,41 @@ AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tre bool AssetCatalogDropController::can_drop(const wmDrag &drag, const char **r_disabled_hint) const { - if (drag.type != WM_DRAG_ASSET_LIST) { - return false; + if (drag.type == WM_DRAG_ASSET_CATALOG) { + /* Always supported. */ + return true; } - return has_droppable_item(drag, r_disabled_hint); + if (drag.type == WM_DRAG_ASSET_LIST) { + return has_droppable_asset(drag, r_disabled_hint); + } + return false; } std::string AssetCatalogDropController::drop_tooltip(const wmDrag &drag) const { + if (drag.type == WM_DRAG_ASSET_CATALOG) { + return drop_tooltip_asset_catalog(drag); + } + return drop_tooltip_asset_list(drag); +} + +std::string AssetCatalogDropController::drop_tooltip_asset_catalog(const wmDrag &drag) const +{ + BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); + + const ::AssetLibrary *asset_library = tree_view().asset_library_; + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(asset_library); + wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag); + AssetCatalog *src_catalog = catalog_service->find_catalog(catalog_drag->drag_catalog_id); + + return std::string(TIP_("Move Catalog")) + " '" + src_catalog->path.name() + "' " + + IFACE_("into") + " '" + catalog_item_.get_name() + "'"; +} + +std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &drag) const +{ + BLI_assert(drag.type == WM_DRAG_ASSET_LIST); + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); @@ -340,17 +391,32 @@ std::string AssetCatalogDropController::drop_tooltip(const wmDrag &drag) const bool AssetCatalogDropController::on_drop(const wmDrag &drag) { - return drop_into_catalog(tree_view(), - drag, - catalog_item_.get_catalog_id(), - catalog_item_.get_simple_name()); + if (drag.type == WM_DRAG_ASSET_CATALOG) { + return drop_asset_catalog_into_catalog(drag); + } + return drop_assets_into_catalog(tree_view(), + drag, + catalog_item_.get_catalog_id(), + catalog_item_.get_simple_name()); } -bool AssetCatalogDropController::drop_into_catalog(const AssetCatalogTreeView &tree_view, - const wmDrag &drag, - CatalogID catalog_id, - StringRefNull simple_name) +bool AssetCatalogDropController::drop_asset_catalog_into_catalog(const wmDrag &drag) { + BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); + wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag); + ED_asset_catalog_move( + &get_asset_library(), catalog_drag->drag_catalog_id, catalog_item_.get_catalog_id()); + + WM_main_add_notifier(NC_ASSET | ND_ASSET_CATALOGS, nullptr); + return true; +} + +bool AssetCatalogDropController::drop_assets_into_catalog(const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name) +{ + BLI_assert(drag.type == WM_DRAG_ASSET_LIST); const ListBase *asset_drags = WM_drag_asset_list_get(&drag); if (!asset_drags) { return false; @@ -373,8 +439,8 @@ bool AssetCatalogDropController::drop_into_catalog(const AssetCatalogTreeView &t return true; } -bool AssetCatalogDropController::has_droppable_item(const wmDrag &drag, - const char **r_disabled_hint) +bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag, + const char **r_disabled_hint) { const ListBase *asset_drags = WM_drag_asset_list_get(&drag); @@ -390,6 +456,31 @@ bool AssetCatalogDropController::has_droppable_item(const wmDrag &drag, return false; } +::AssetLibrary &AssetCatalogDropController::get_asset_library() const +{ + return *tree_view().asset_library_; +} + +/* ---------------------------------------------------------------------- */ + +AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeItem &catalog_item) + : catalog_item_(catalog_item) +{ +} + +int AssetCatalogDragController::get_drag_type() const +{ + return WM_DRAG_ASSET_CATALOG; +} + +void *AssetCatalogDragController::create_drag_data() const +{ + wmDragAssetCatalog *drag_catalog = (wmDragAssetCatalog *)MEM_callocN(sizeof(*drag_catalog), + __func__); + drag_catalog->drag_catalog_id = catalog_item_.get_catalog_id(); + return drag_catalog; +} + /* ---------------------------------------------------------------------- */ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) @@ -427,7 +518,7 @@ bool AssetCatalogTreeViewUnassignedItem::DropController::can_drop( if (drag.type != WM_DRAG_ASSET_LIST) { return false; } - return AssetCatalogDropController::has_droppable_item(drag, r_disabled_hint); + return AssetCatalogDropController::has_droppable_asset(drag, r_disabled_hint); } std::string AssetCatalogTreeViewUnassignedItem::DropController::drop_tooltip( @@ -443,7 +534,7 @@ std::string AssetCatalogTreeViewUnassignedItem::DropController::drop_tooltip( bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(const wmDrag &drag) { /* Assign to nil catalog ID. */ - return AssetCatalogDropController::drop_into_catalog( + return AssetCatalogDropController::drop_assets_into_catalog( tree_view(), drag, CatalogID{}); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 26db02be289..b5acecc4762 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -770,6 +770,8 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, struct wmDrag *drag, struct wmDropBox *drop); +struct wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const struct wmDrag *drag); + void WM_drag_add_asset_list_item(wmDrag *drag, const struct bContext *C, const struct AssetLibraryReference *asset_library_ref, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 1f1b1a70685..9f427a90353 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -119,6 +119,7 @@ struct wmWindowManager; #include "BLI_compiler_attrs.h" #include "DNA_listBase.h" +#include "DNA_uuid_types.h" #include "DNA_vec_types.h" #include "DNA_xr_types.h" #include "RNA_types.h" @@ -967,6 +968,7 @@ typedef void (*wmPaintCursorDraw)(struct bContext *C, int, int, void *customdata #define WM_DRAG_VALUE 6 #define WM_DRAG_COLOR 7 #define WM_DRAG_DATASTACK 8 +#define WM_DRAG_ASSET_CATALOG 9 typedef enum wmDragFlags { WM_DRAG_NOP = 0, @@ -1000,6 +1002,10 @@ typedef struct wmDragAsset { struct bContext *evil_C; } wmDragAsset; +typedef struct wmDragAssetCatalog { + bUUID drag_catalog_id; +} wmDragAssetCatalog; + /** * For some specific cases we support dragging multiple assets (#WM_DRAG_ASSET_LIST). There is no * proper support for dragging multiple items in the `wmDrag`/`wmDrop` API yet, so this is really diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 50ac046ed54..df6d3d5e9e7 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -181,6 +181,7 @@ wmDrag *WM_event_start_drag( } break; case WM_DRAG_ASSET: + case WM_DRAG_ASSET_CATALOG: /* Move ownership of poin to wmDrag. */ drag->poin = poin; drag->flags |= WM_DRAG_FREE_DATA; @@ -649,6 +650,15 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * } } +wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_ASSET_CATALOG) { + return NULL; + } + + return drag->poin; +} + /** * \note: Does not store \a asset in any way, so it's fine to pass a temporary. */ From 332de3a2da029ad8960fa87b812c8f6d73f8dc9e Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 14:55:39 +0200 Subject: [PATCH 1282/1500] Cleanup: Add static assert for tree-view getter template These kind of static asserts for the base type of a template parameter are useful, and can avoid wrong API usage. --- source/blender/editors/include/UI_tree_view.hh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 0990d844d48..0d18eedeac9 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -455,6 +455,8 @@ inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args) template TreeViewType &AbstractTreeViewItemDropController::tree_view() const { + static_assert(std::is_base_of::value, + "Type must derive from and implement the AbstractTreeView interface"); return static_cast(tree_view_); } From 19a559d1700770c6d5d3bcf2b9f500d287e24682 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 27 Oct 2021 15:12:19 +0200 Subject: [PATCH 1283/1500] Fix warning after recent fluid modifier changes --- source/blender/modifiers/intern/MOD_fluid.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index acc92fde766..c019a49cdb9 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -143,7 +143,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return me; #else FluidModifierData *fmd = (FluidModifierData *)md; - Mesh *result = NULL; if (ctx->flag & MOD_APPLY_ORCO) { return me; From bbd6dc55d1bc0218f982dbd865c3a209ebf731af Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 15:23:27 +0200 Subject: [PATCH 1284/1500] Nodes: fix menu when there is no node tree Previously, some submenus were empty. --- release/scripts/startup/nodeitems_builtins.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index d822ed9599f..34f447a7108 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -87,8 +87,6 @@ def curve_node_items(context): space = context.space_data if not space: return - if not space.edit_tree: - return if geometry_nodes_legacy_poll(context): yield NodeItem("GeometryNodeLegacyCurveEndpoints") @@ -137,8 +135,6 @@ def mesh_node_items(context): space = context.space_data if not space: return - if not space.edit_tree: - return if geometry_nodes_legacy_poll(context): yield NodeItem("GeometryNodeLegacyEdgeSplit", poll=geometry_nodes_legacy_poll) @@ -164,8 +160,6 @@ def geometry_node_items(context): space = context.space_data if not space: return - if not space.edit_tree: - return if geometry_nodes_legacy_poll(context): yield NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_legacy_poll) @@ -192,8 +186,6 @@ def geometry_input_node_items(context): space = context.space_data if not space: return - if not space.edit_tree: - return if geometry_nodes_legacy_poll(context): yield NodeItem("FunctionNodeLegacyRandomFloat") @@ -223,8 +215,6 @@ def geometry_material_node_items(context): space = context.space_data if not space: return - if not space.edit_tree: - return if geometry_nodes_legacy_poll(context): yield NodeItem("GeometryNodeLegacyMaterialAssign") @@ -246,8 +236,6 @@ def point_node_items(context): space = context.space_data if not space: return - if not space.edit_tree: - return if geometry_nodes_legacy_poll(context): yield NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_legacy_poll) @@ -273,15 +261,16 @@ def node_group_items(context): space = context.space_data if not space: return - ntree = space.edit_tree - if not ntree: - return yield NodeItemCustom(draw=group_tools_draw) yield NodeItem("NodeGroupInput", poll=group_input_output_item_poll) yield NodeItem("NodeGroupOutput", poll=group_input_output_item_poll) + ntree = space.edit_tree + if not ntree: + return + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) def contains_group(nodetree, group): From ff2e8d6510c271375b618b60cf98038658288662 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 28 Oct 2021 00:24:22 +1100 Subject: [PATCH 1285/1500] Fix building WITH_FLUID=OFF --- source/blender/modifiers/intern/MOD_fluid.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index c019a49cdb9..e087b8411f8 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -122,6 +122,7 @@ typedef struct FluidIsolationData { Mesh *result; } FluidIsolationData; +#ifdef WITH_FLUID static void fluid_modifier_do_isolated(void *userdata) { FluidIsolationData *isolation_data = (FluidIsolationData *)userdata; @@ -135,6 +136,7 @@ static void fluid_modifier_do_isolated(void *userdata) isolation_data->mesh); isolation_data->result = result ? result : isolation_data->mesh; } +#endif /* WITH_FLUID */ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *me) { From 2a709c82c369a8b87592156bf1afc0222d44a778 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 19:01:44 +1100 Subject: [PATCH 1286/1500] Fix sequencer selection toggle Sequence strips weren't being deselected while holding shift. --- source/blender/editors/space_sequencer/sequencer_select.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index b4271ebd812..cfc7273cb0a 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -759,7 +759,7 @@ static void sequencer_select_strip_impl(const Editing *ed, action = 0; } else { - if ((seq->flag & SELECT) == 0 || is_active) { + if (!((seq->flag & SELECT) && is_active)) { action = 1; } else if (toggle) { @@ -821,7 +821,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one keymap, * therefore both properties can be true at the same time. */ if (seq && RNA_boolean_get(op->ptr, "linked_time")) { - if (!extend) { + if (!extend && !toggle) { ED_sequencer_deselect_all(scene); } sequencer_select_strip_impl(ed, seq, handle_clicked, extend, deselect, toggle); @@ -833,7 +833,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* Select left, right or overlapping the current frame. */ if (RNA_boolean_get(op->ptr, "side_of_frame")) { - if (!extend) { + if (!extend && !toggle) { ED_sequencer_deselect_all(scene); } sequencer_select_side_of_frame(C, v2d, mval, scene); @@ -843,7 +843,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* On Alt selection, select the strip and bordering handles. */ if (seq && RNA_boolean_get(op->ptr, "linked_handle")) { - if (!extend) { + if (!extend && !toggle) { ED_sequencer_deselect_all(scene); } sequencer_select_linked_handle(C, seq, handle_clicked); From aea7e555221e071e65fe76eb9571198622bbc547 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 19:01:46 +1100 Subject: [PATCH 1287/1500] WM: de-duplicate cursor motion checks for selection picking Replace local static mouse coordinate storage with a single function. also resolve inconsistencies. - Edit-mesh selection used equality check (ignoring `U.move_threshold`). - Motion to clear tooltips checked the value without scaling by the DPI. Also prevent the unlikely case of the previous motion check matching a different area by resetting the value when the active region changes. --- .../editors/armature/armature_select.c | 6 +---- source/blender/editors/mesh/editmesh_select.c | 8 ++----- source/blender/editors/screen/screen_edit.c | 4 ++++ .../editors/space_view3d/view3d_select.c | 9 +++----- source/blender/windowmanager/WM_api.h | 1 + .../windowmanager/intern/wm_event_query.c | 22 +++++++++++++++++++ .../windowmanager/intern/wm_event_system.c | 3 ++- 7 files changed, 35 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index bd799c00373..937385f9ffa 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -673,11 +673,7 @@ static EditBone *get_nearest_editbonepoint( } if (use_cycle) { - static int last_mval[2] = {-100, -100}; - if ((len_manhattan_v2v2_int(vc->mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) == 0) { - use_cycle = false; - } - copy_v2_v2_int(last_mval, vc->mval); + use_cycle = !WM_cursor_test_motion_and_update(vc->mval); } const bool do_nearest = !(XRAY_ACTIVE(vc->v3d) || use_cycle); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 2fcf8fa6f8f..e0768bcff24 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -884,9 +884,8 @@ static bool unified_findnearest(ViewContext *vc, BMFace **r_efa) { BMEditMesh *em = vc->em; - static short mval_prev[2] = {-1, -1}; - /* only cycle while the mouse remains still */ - const bool use_cycle = ((mval_prev[0] == vc->mval[0]) && (mval_prev[1] == vc->mval[1])); + + const bool use_cycle = !WM_cursor_test_motion_and_update(vc->mval); const float dist_init = ED_view3d_select_dist_px(); /* since edges select lines, we give dots advantage of ~20 pix */ const float dist_margin = (dist_init / 2); @@ -988,9 +987,6 @@ static bool unified_findnearest(ViewContext *vc, } } - mval_prev[0] = vc->mval[0]; - mval_prev[1] = vc->mval[1]; - /* Only one element type will be non-null. */ BLI_assert(((hit.v.ele != NULL) + (hit.e.ele != NULL) + (hit.f.ele != NULL)) <= 1); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 841792d5f2d..fa0cfd16817 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -935,6 +935,10 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) } } } + + /* Ensure test-motion values are never shared between regions. */ + const bool use_cycle = !WM_cursor_test_motion_and_update((const int[2]){-1, -1}); + UNUSED_VARS(use_cycle); } /* Cursors, for time being set always on edges, diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 07f1f8a753c..18820039c7f 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2047,19 +2047,16 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, bool enumerate, bool *r_do_nearest) { - static int last_mval[2] = {-100, -100}; bool do_nearest = false; View3D *v3d = vc->v3d; /* define if we use solid nearest select or not */ if (use_cycle) { + /* Update the coordinates (even if the return value isn't used). */ + const bool has_motion = WM_cursor_test_motion_and_update(mval); if (!XRAY_ACTIVE(v3d)) { - do_nearest = true; - if (len_manhattan_v2v2_int(mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { - do_nearest = false; - } + do_nearest = has_motion; } - copy_v2_v2_int(last_mval, mval); } else { if (!XRAY_ACTIVE(v3d)) { diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index b5acecc4762..59e03f472f0 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -937,6 +937,7 @@ bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); bool WM_event_is_last_mousemove(const struct wmEvent *event); bool WM_event_is_mouse_drag(const struct wmEvent *event); bool WM_event_is_mouse_drag_or_press(const wmEvent *event); +bool WM_cursor_test_motion_and_update(const int mval[2]) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; int WM_event_drag_threshold(const struct wmEvent *event); bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 9ee114674ed..ef733837bf7 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -276,6 +276,28 @@ bool WM_event_is_mouse_drag_or_press(const wmEvent *event) (ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS)); } +/** + * Detect motion between selection (callers should only use this for selection picking), + * typically mouse press/click events. + * + * \param mval: Region relative coordinates, call with (-1, -1) resets the last cursor location. + * \returns True when there was motion since last called. + * + * NOTE(@campbellbarton): The logic used here isn't foolproof. + * It's possible that users move the cursor past #WM_EVENT_CURSOR_MOTION_THRESHOLD then back to + * a position within the threshold (between mouse clicks). + * In practice users never reported this since the threshold is very small (a few pixels). + * To prevent the unlikely case of values matching from another region, + * changing regions resets this value to (-1, -1). + */ +bool WM_cursor_test_motion_and_update(const int mval[2]) +{ + static int mval_prev[2] = {-1, -1}; + bool use_cycle = (len_manhattan_v2v2_int(mval, mval_prev) <= WM_EVENT_CURSOR_MOTION_THRESHOLD); + copy_v2_v2_int(mval_prev, mval); + return !use_cycle; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 2aa9a4d8676..a426841b49c 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3628,7 +3628,8 @@ void wm_event_do_handlers(bContext *C) /* Clear tool-tip on mouse move. */ if (screen->tool_tip && screen->tool_tip->exit_on_event) { if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) > U.move_threshold) { + if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) > + WM_EVENT_CURSOR_MOTION_THRESHOLD) { WM_tooltip_clear(C, win); } } From f5d8339fa5afc77d5389c47920190de250709d62 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 19:01:48 +1100 Subject: [PATCH 1288/1500] Sequencer: various preview selection improvements - Only cycle items when the cursor hasn't moved. This matches object-mode behavior, making it possible to tweak-drag the current selection without first cycling to the next sequence strip. Successive clicks will still cycle sequence strips. - For center selection, use a penalty for the active strip. - Use a temporary selection list to avoid moving the sequence strips out of the scene during selection (changing their order when added back). --- .../space_sequencer/sequencer_select.c | 130 +++++++++++++----- 1 file changed, 92 insertions(+), 38 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index cfc7273cb0a..1cc7507e5f6 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -25,6 +25,8 @@ #include #include +#include "MEM_guardedalloc.h" + #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -635,11 +637,51 @@ static void sequencer_select_linked_handle(const bContext *C, } } -/* Check if click happened on image which belongs to strip. If multiple strips are found, loop - * through them in order. */ -static Sequence *seq_select_seq_from_preview(const bContext *C, - const int mval[2], - const bool center) +/** Collect sequencer that are candidates for being selected. */ +struct SeqSelect_Link { + struct SeqSelect_Link *next, *prev; + Sequence *seq; + /** Only use for center selection. */ + float center_dist_sq; +}; + +static int seq_sort_for_depth_select(const void *a, const void *b) +{ + const struct SeqSelect_Link *slink_a = a; + const struct SeqSelect_Link *slink_b = b; + + /* Exactly overlapping strips, sort by machine (so the top-most is first). */ + if (slink_a->seq->machine < slink_b->seq->machine) { + return 1; + } + if (slink_a->seq->machine > slink_b->seq->machine) { + return -1; + } + return 0; +} + +static int seq_sort_for_center_select(const void *a, const void *b) +{ + const struct SeqSelect_Link *slink_a = a; + const struct SeqSelect_Link *slink_b = b; + if (slink_a->center_dist_sq > slink_b->center_dist_sq) { + return 1; + } + if (slink_a->center_dist_sq < slink_b->center_dist_sq) { + return -1; + } + + /* Exactly overlapping strips, use depth. */ + return seq_sort_for_depth_select(a, b); +} + +/** + * Check if click happened on image which belongs to strip. + * If multiple strips are found, loop through them in order + * (depth (top-most first) or closest to mouse when `center` is true). + */ +static Sequence *seq_select_seq_from_preview( + const bContext *C, const int mval[2], const bool toggle, const bool extend, const bool center) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); @@ -650,70 +692,82 @@ static Sequence *seq_select_seq_from_preview(const bContext *C, float mouseco_view[2]; UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseco_view[0], &mouseco_view[1]); + /* Always update the coordinates (check extended after). */ + const bool use_cycle = (!WM_cursor_test_motion_and_update(mval) || extend || toggle); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown); /* Allow strips this far from the closest center to be included. * This allows cycling over center points which are near enough * to overlapping from the users perspective. */ - const float center_threshold_cycle_px = 5.0f; - const float center_dist_sq_eps = square_f(center_threshold_cycle_px * U.pixelsize); + const float center_dist_sq_max = square_f(75.0f * U.pixelsize); const float center_scale_px[2] = { UI_view2d_scale_get_x(v2d), UI_view2d_scale_get_y(v2d), }; - float center_co_best[2] = {0.0f}; - - if (center) { - Sequence *seq_best = NULL; - float center_dist_sq_best = 0.0f; - - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, strips) { - float co[2]; - SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co); - const float center_dist_sq_test = len_squared_v2v2(co, mouseco_view); - if ((seq_best == NULL) || (center_dist_sq_test < center_dist_sq_best)) { - seq_best = seq; - center_dist_sq_best = center_dist_sq_test; - copy_v2_v2(center_co_best, co); - } - } - } + struct SeqSelect_Link *slink_active = NULL; + Sequence *seq_active = SEQ_select_active_get(scene); ListBase strips_ordered = {NULL}; Sequence *seq; SEQ_ITERATOR_FOREACH (seq, strips) { bool isect = false; + float center_dist_sq_test = 0.0f; if (center) { /* Detect overlapping center points (scaled by the zoom level). */ float co[2]; SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co); - sub_v2_v2(co, center_co_best); + sub_v2_v2(co, mouseco_view); mul_v2_v2(co, center_scale_px); - isect = len_squared_v2(co) <= center_dist_sq_eps; + center_dist_sq_test = len_squared_v2(co); + isect = center_dist_sq_test <= center_dist_sq_max; + if (isect) { + /* Use an active strip penalty for "center" selection when cycle is enabled. */ + if (use_cycle && (seq == seq_active) && (seq_active->flag & SELECT)) { + center_dist_sq_test = square_f(sqrtf(center_dist_sq_test) + (3.0f * U.pixelsize)); + } + } } else { isect = seq_point_image_isect(scene, seq, mouseco_view); } if (isect) { - BLI_remlink(seqbase, seq); - BLI_addtail(&strips_ordered, seq); + struct SeqSelect_Link *slink = MEM_callocN(sizeof(*slink), __func__); + slink->seq = seq; + slink->center_dist_sq = center_dist_sq_test; + BLI_addtail(&strips_ordered, slink); + + if (seq == seq_active) { + slink_active = slink; + } } } SEQ_collection_free(strips); - SEQ_sort(&strips_ordered); - Sequence *seq_active = SEQ_select_active_get(scene); - Sequence *seq_select = strips_ordered.first; - LISTBASE_FOREACH (Sequence *, seq_iter, &strips_ordered) { - if (seq_iter == seq_active && seq_iter->next != NULL) { - seq_select = seq_iter->next; - break; + BLI_listbase_sort(&strips_ordered, + center ? seq_sort_for_center_select : seq_sort_for_depth_select); + + struct SeqSelect_Link *slink_select = strips_ordered.first; + Sequence *seq_select = NULL; + if (slink_select != NULL) { + /* Only use special behavior for the active strip when it's selected. */ + if ((center == false) && slink_active && (seq_active->flag & SELECT)) { + if (use_cycle) { + if (slink_active->next) { + slink_select = slink_active->next; + } + } + else { + /* Match object selection behavior: keep the current active item unless cycle is enabled. + * Clicking again in the same location will cycle away from the active object. */ + slink_select = slink_active; + } } + seq_select = slink_select->seq; } - BLI_movelisttolist(seqbase, &strips_ordered); + BLI_freelistN(&strips_ordered); return seq_select; } @@ -812,7 +866,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) int handle_clicked = SEQ_SIDE_NONE; Sequence *seq = NULL; if (region->regiontype == RGN_TYPE_PREVIEW) { - seq = seq_select_seq_from_preview(C, mval, center); + seq = seq_select_seq_from_preview(C, mval, toggle, extend, center); } else { seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); From 9217f5c7a332ca2398a45924addc24ccbc3064e6 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 15:25:47 +0200 Subject: [PATCH 1289/1500] Geometry Nodes: change default raycast direction Raycasting downwards (e.g. onto some terrain) is more common than raycasting in the positive z direction. --- source/blender/nodes/geometry/nodes/node_geo_raycast.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index c042256114f..7040b47db1c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -42,7 +42,7 @@ static void geo_node_raycast_declare(NodeDeclarationBuilder &b) b.add_input("Attribute", "Attribute_004").hide_value().supports_field(); b.add_input("Source Position").implicit_field(); - b.add_input("Ray Direction").default_value({0.0f, 0.0f, 1.0f}).supports_field(); + b.add_input("Ray Direction").default_value({0.0f, 0.0f, -1.0f}).supports_field(); b.add_input("Ray Length") .default_value(100.0f) .min(0.0f) From 7cbb01f07e84c463af77a57930b3d027b09549a5 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 15:29:03 +0200 Subject: [PATCH 1290/1500] Fix: incorrect warning in Instances to Points node --- .../nodes/geometry/nodes/node_geo_instances_to_points.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index a120560c848..618d85d9c2f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -23,7 +23,7 @@ namespace blender::nodes { static void geo_node_instances_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input("Instances").supported_type(GEO_COMPONENT_TYPE_INSTANCES); + b.add_input("Instances").only_instances(); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Position").implicit_field(); b.add_input("Radius") From cc6d5bc241fd453c361c42d50b3a8ca9058bbaee Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 15:31:00 +0200 Subject: [PATCH 1291/1500] Fix: typo in info message --- source/blender/nodes/intern/node_geometry_exec.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index bcf6ff2fcf6..c7a3e795c33 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -52,13 +52,14 @@ void GeoNodeExecParams::check_input_geometry_set(StringRef identifier, if (only_realized_data) { if (geometry_set.has_instances()) { - this->error_message_add(NodeWarningType::Info, "Instances in input geometry are ignored"); + this->error_message_add(NodeWarningType::Info, + TIP_("Instances in input geometry are ignored")); } } if (only_instances) { if (geometry_set.has_realized_data()) { this->error_message_add(NodeWarningType::Info, - "Realized data in input geometry are ignored"); + TIP_("Realized data in input geometry is ignored")); } } if (supported_types.is_empty()) { From c1936be0c5572d6c2b84ac934d6f907b7335375c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 15:34:28 +0200 Subject: [PATCH 1292/1500] Spreadsheet: use "id" instead of "ID" as column name The lower case name is the internal name and will be exposed more to the user once we have instance attributes. --- .../space_spreadsheet/spreadsheet_data_source_geometry.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index f352a5fd0eb..bc6a8a48c3c 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -388,7 +388,7 @@ void InstancesDataSource::foreach_default_column_ids( SpreadsheetColumnID column_id; column_id.name = (char *)"Name"; fn(column_id, false); - for (const char *name : {"Position", "Rotation", "Scale", "ID"}) { + for (const char *name : {"Position", "Rotation", "Scale", "id"}) { column_id.name = (char *)name; fn(column_id, false); } @@ -467,7 +467,7 @@ std::unique_ptr InstancesDataSource::get_column_values( } Span ids = component_->instance_ids(); if (!ids.is_empty()) { - if (STREQ(column_id.name, "ID")) { + if (STREQ(column_id.name, "id")) { /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ return column_values_from_function( SPREADSHEET_VALUE_TYPE_INT32, From 827d7e7d252d488a4e267d3b84d49f4deeb654ec Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 27 Oct 2021 08:36:59 -0500 Subject: [PATCH 1293/1500] Geometry Nodes: Rename some sockets Subdivision surface: Both geometry sockets renamed to "Mesh" Points to Volume: Use "Points" and "Volume" names Distribute Points on Faces: Use "Mesh" input name These are meant to provide a hint to users which type each node is meant to use. --- .../blenloader/intern/versioning_300.c | 10 ++++ .../blenloader/intern/versioning_common.cc | 54 +++++++++++++------ .../blenloader/intern/versioning_common.h | 8 +++ .../node_geo_distribute_points_on_faces.cc | 4 +- .../nodes/node_geo_points_to_volume.cc | 10 ++-- .../nodes/node_geo_subdivision_surface.cc | 10 ++-- 6 files changed, 68 insertions(+), 28 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index d0d6c1471b7..6ab3e24cb2b 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2118,5 +2118,15 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_node_input_socket_name( + ntree, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, "Geometry", "Mesh"); + version_node_input_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Points"); + version_node_output_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Volume"); + version_node_socket_name(ntree, GEO_NODE_SUBDIVISION_SURFACE, "Geometry", "Mesh"); + } + } } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 6c4996ba9b2..ecc944defba 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -87,6 +87,18 @@ ID *do_versions_rename_id(Main *bmain, return id; } +static void change_node_socket_name(ListBase *sockets, const char *old_name, const char *new_name) +{ + LISTBASE_FOREACH (bNodeSocket *, socket, sockets) { + if (STREQ(socket->name, old_name)) { + BLI_strncpy(socket->name, new_name, sizeof(socket->name)); + } + if (STREQ(socket->identifier, old_name)) { + BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); + } + } +} + void version_node_socket_name(bNodeTree *ntree, const int node_type, const char *old_name, @@ -94,22 +106,32 @@ void version_node_socket_name(bNodeTree *ntree, { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == node_type) { - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - if (STREQ(socket->name, old_name)) { - BLI_strncpy(socket->name, new_name, sizeof(socket->name)); - } - if (STREQ(socket->identifier, old_name)) { - BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); - } - } - LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { - if (STREQ(socket->name, old_name)) { - BLI_strncpy(socket->name, new_name, sizeof(socket->name)); - } - if (STREQ(socket->identifier, old_name)) { - BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); - } - } + change_node_socket_name(&node->inputs, old_name, new_name); + change_node_socket_name(&node->outputs, old_name, new_name); + } + } +} + +void version_node_input_socket_name(bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + change_node_socket_name(&node->inputs, old_name, new_name); + } + } +} + +void version_node_output_socket_name(bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + change_node_socket_name(&node->outputs, old_name, new_name); } } } diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index 1826182be21..8697e8e2639 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -43,6 +43,14 @@ void version_node_socket_name(struct bNodeTree *ntree, const int node_type, const char *old_name, const char *new_name); +void version_node_input_socket_name(struct bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name); +void version_node_output_socket_name(struct bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name); void version_node_id(struct bNodeTree *ntree, const int node_type, const char *new_name); diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 58393aea158..21e6004347e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -42,7 +42,7 @@ namespace blender::nodes { static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Distance Min").min(0.0f).subtype(PROP_DISTANCE); b.add_input("Density Max").default_value(10.0f).min(0.0f); @@ -533,7 +533,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set, static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Mesh"); const GeometryNodeDistributePointsOnFacesMode method = static_cast(params.node().custom1); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 26001551fdf..6f68be439b3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -32,7 +32,7 @@ namespace blender::nodes { static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input("Points").supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); b.add_input("Density").default_value(1.0f).min(0.0f); b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); @@ -41,7 +41,7 @@ static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output("Geometry"); + b.add_output("Volume"); } static void geo_node_points_to_volume_layout(uiLayout *layout, @@ -239,17 +239,17 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, static void geo_node_points_to_volume_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Points"); #ifdef WITH_OPENVDB geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { initialize_volume_component_from_points(params, geometry_set); }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Volume", std::move(geometry_set)); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Volume", GeometrySet()); #endif } diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index c82b09e6b38..22bfe2a2bc7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -31,7 +31,7 @@ namespace blender::nodes { static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Level").default_value(1).min(0).max(6); b.add_input("Crease") .default_value(0.0f) @@ -39,7 +39,7 @@ static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) .max(1.0f) .supports_field() .subtype(PROP_FACTOR); - b.add_output("Geometry"); + b.add_output("Mesh"); } static void geo_node_subdivision_surface_layout(uiLayout *layout, @@ -67,7 +67,7 @@ static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *n static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Mesh"); #ifndef WITH_OPENSUBDIV params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); @@ -82,7 +82,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) /* Only process subdivision if level is greater than 0. */ if (subdiv_level == 0) { - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); return; } @@ -148,7 +148,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) BKE_subdiv_free(subdiv); }); #endif - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes From 62928b618adab0b5e8b766602ededfe8a26e719c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 15:39:43 +0200 Subject: [PATCH 1294/1500] Spreadsheet: make id column a bit wider Ids can often be relatively large numbers when they are generated automatically. --- .../spreadsheet_data_source_geometry.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index bc6a8a48c3c..c1d345d1861 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -176,14 +176,16 @@ std::unique_ptr GeometryDataSource::get_column_values( r_cell_value.value_float = value; }); case CD_PROP_INT32: - return column_values_from_function(SPREADSHEET_VALUE_TYPE_INT32, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - int value; - varray->get(index, &value); - r_cell_value.value_int = value; - }); + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_INT32, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + int value; + varray->get(index, &value); + r_cell_value.value_int = value; + }, + STREQ(column_id.name, "id") ? 5.5f : 0.0f); case CD_PROP_BOOL: return column_values_from_function(SPREADSHEET_VALUE_TYPE_BOOL, column_id.name, From 059e5a8a4c49975545308fc9204edcfc671d4426 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 15:42:02 +0200 Subject: [PATCH 1295/1500] Geometry Nodes: use true as default in Set Shade Smooth node The node is typically only added to enable smooth shading. So this default is much more useful. --- .../blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index 9404f424c86..c88264ebe94 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -22,7 +22,7 @@ static void geo_node_set_shade_smooth_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Shade Smooth").supports_field(); + b.add_input("Shade Smooth").supports_field().default_value(true); b.add_output("Geometry"); } From ef6e03c8e094ec14870f0a7c228609deadbfa392 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 27 Oct 2021 08:43:31 -0500 Subject: [PATCH 1296/1500] Fix: Show "new" attribute name typed in nodes modifier input search Previously it wouldn't be displayed at all for inputs, now just display it without an "add" icon. --- .../editors/interface/interface_template_attribute_search.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc index f462c0cb4cb..85a6147432b 100644 --- a/source/blender/editors/interface/interface_template_attribute_search.cc +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -80,9 +80,10 @@ void attribute_search_add_items(StringRefNull str, break; } } - if (!contained && is_output) { + if (!contained) { dummy_info.name = str; - UI_search_item_add(seach_items, str.c_str(), &dummy_info, ICON_ADD, 0, 0); + UI_search_item_add( + seach_items, str.c_str(), &dummy_info, is_output ? ICON_ADD : ICON_NONE, 0, 0); } } From 383985a91bf3c1057425dd4b0972142d41d7f0ef Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 15:48:53 +0200 Subject: [PATCH 1297/1500] Geometry Nodes: use index field implicitly in Set ID node That makes the node more useful by default. One use case is to delete some points after the Set ID node, then instance with some randomness. Now when deleting different points, the randomness will remain stable. --- source/blender/nodes/geometry/nodes/node_geo_set_id.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc index f8edfa145d9..7a3b91a17b0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -22,7 +22,7 @@ static void geo_node_set_id_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("ID").supports_field(); + b.add_input("ID").implicit_field(); b.add_output("Geometry"); } From b6bed63b5bae9f64959e3c03b1f6f6cf7c091163 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 27 Oct 2021 08:52:46 -0500 Subject: [PATCH 1298/1500] UI: Hide labels for subdivision surface node enums The text is just too long, it doesn't fit in the node width, and the tooltips display the property names well enough, since they aren't used as often as other settings. Also display the text in lite builds too, there is no reason not to. --- .../geometry/nodes/node_geo_subdivision_surface.cc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 22bfe2a2bc7..57c0dfc5b09 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -46,14 +46,8 @@ static void geo_node_subdivision_surface_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { -#ifdef WITH_OPENSUBDIV - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE); - uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); -#else - UNUSED_VARS(layout, ptr); -#endif + uiItemR(layout, ptr, "uv_smooth", 0, "", ICON_NONE); + uiItemR(layout, ptr, "boundary_smooth", 0, "", ICON_NONE); } static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node) From 212b02b5488982e83083a033150d11384181aa49 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 27 Oct 2021 08:54:24 -0500 Subject: [PATCH 1299/1500] Geometry Nodes: Show hint in empty output attributes panel This is meant to add something to the sub-panel when it is empty so it looks more purposeful, but also add a hint that might be helpful when figuring out how to output a named attribute. Differential Revision: https://developer.blender.org/D12715 --- source/blender/modifiers/intern/MOD_nodes.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 292ba04490c..c88940c00c2 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1428,13 +1428,18 @@ static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, true); + bool has_output_attribute = false; if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { if (socket_type_has_attribute_toggle(*socket)) { + has_output_attribute = true; draw_property_for_output_socket(layout, *nmd, ptr, *socket); } } } + if (!has_output_attribute) { + uiItemL(layout, IFACE_("No group output attributes connected."), ICON_INFO); + } } static void panelRegister(ARegionType *region_type) From 9beb5e38a9ca69b36022f75b61df09bc2522b490 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 15:56:26 +0200 Subject: [PATCH 1300/1500] Geometry Nodes: output Index from ID node if the geometry has no id This is consistent with all the other places where we use the id attribute: If it does not exist, use the index instead. --- source/blender/nodes/geometry/nodes/node_geo_input_id.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc index 049dbf06d80..d267325c26b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc @@ -25,7 +25,7 @@ static void geo_node_input_id_declare(NodeDeclarationBuilder &b) static void geo_node_input_id_exec(GeoNodeExecParams params) { - Field position_field{AttributeFieldInput::Create("id")}; + Field position_field{std::make_shared()}; params.set_output("ID", std::move(position_field)); } From 0d6f2cb303991d74404aa30d2f33ef52cb9993fd Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 27 Oct 2021 15:56:45 +0200 Subject: [PATCH 1301/1500] Cleanup: remove unused function declaration --- source/blender/blenkernel/BKE_geometry_set.hh | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index a9a0b87a778..58a89d0207a 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -776,8 +776,6 @@ class IDAttributeFieldInput : public fn::FieldInput { category_ = Category::Generated; } - static fn::Field Create(); - const GVArray *get_varray_for_context(const fn::FieldContext &context, IndexMask mask, ResourceScope &scope) const override; From 974002743e1fda0823a8101f92e8155a9baf1f21 Mon Sep 17 00:00:00 2001 From: Alaska Date: Wed, 27 Oct 2021 14:34:04 +0200 Subject: [PATCH 1302/1500] Cycles: Update UI for Scrambling Distance Patch The current UI for the Scramble Distance patch is grayed out depending on different settings that are enabled. However it didn't make much sense to me so I have updated when the UI is grayed out to hopefully make more sense to the end user. Differential Revision: https://developer.blender.org/D12963 --- intern/cycles/blender/addon/ui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index dfaae666785..a0bf6c1758f 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -287,14 +287,14 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): row.prop(cscene, "use_animated_seed", text="", icon='TIME') col = layout.column(align=True) - col.active = not(cscene.use_adaptive_sampling) + col.active = not (cscene.use_adaptive_sampling and cscene.use_preview_adaptive_sampling) col.prop(cscene, "sampling_pattern", text="Pattern") col = layout.column(align=True) - col.active = not cscene.use_adaptive_sampling + col.active = not (cscene.use_adaptive_sampling and cscene.use_preview_adaptive_sampling) col.prop(cscene, "scrambling_distance", text="Scrambling Distance Strength") col.prop(cscene, "adaptive_scrambling_distance", text="Adaptive Scrambling Distance") col = layout.column(align=True) - col.active = ((cscene.scrambling_distance < 1.0) or cscene.adaptive_scrambling_distance) and not cscene.use_adaptive_sampling + col.active = not cscene.use_preview_adaptive_sampling col.prop(cscene, "preview_scrambling_distance", text="Viewport Scrambling Distance") layout.separator() From dc37990e24eb801e3ff9f9f0f42d364a0af0389e Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Wed, 27 Oct 2021 09:03:29 -0500 Subject: [PATCH 1303/1500] Geometry Nodes: Add Image Socket to Switch Node Add the image type to the switch node without field support. Differential Revision: https://developer.blender.org/D13012 --- source/blender/makesrna/intern/rna_nodetree.c | 3 ++- source/blender/nodes/geometry/nodes/node_geo_switch.cc | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 8535d334f39..6a36ef07dee 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2056,7 +2056,8 @@ static bool switch_type_supported(const EnumPropertyItem *item) SOCK_OBJECT, SOCK_COLLECTION, SOCK_TEXTURE, - SOCK_MATERIAL); + SOCK_MATERIAL, + SOCK_IMAGE); } static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C), diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index c01fcf5bb5f..28faf217f64 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -60,6 +60,8 @@ static void geo_node_switch_declare(NodeDeclarationBuilder &b) b.add_input("True", "True_009"); b.add_input("False", "False_010"); b.add_input("True", "True_010"); + b.add_input("False", "False_011"); + b.add_input("True", "True_011"); b.add_output("Output").dependent_field(); b.add_output("Output", "Output_001").dependent_field(); @@ -72,6 +74,7 @@ static void geo_node_switch_declare(NodeDeclarationBuilder &b) b.add_output("Output", "Output_008"); b.add_output("Output", "Output_009"); b.add_output("Output", "Output_010"); + b.add_output("Output", "Output_011"); } static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -274,6 +277,10 @@ static void geo_node_switch_exec(GeoNodeExecParams params) switch_no_fields(params, "_010"); break; } + case SOCK_IMAGE: { + switch_no_fields(params, "_011"); + break; + } default: BLI_assert_unreachable(); break; From 87470169e07bfa9a06ea49232b78ca1f5da32fc3 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 27 Oct 2021 09:33:50 -0500 Subject: [PATCH 1304/1500] Geometry Nodes: Rename more geometry sockets This is a followup to rB827d7e7d252d48. After this we should be consistent everywhere with the hints in the names of geometry sockets. --- .../blenloader/intern/versioning_300.c | 25 +++++++++++++++++++ .../nodes/geometry/nodes/node_geo_boolean.cc | 16 ++++++------ .../geometry/nodes/node_geo_curve_resample.cc | 10 ++++---- .../nodes/node_geo_curve_subdivide.cc | 8 +++--- .../nodes/node_geo_mesh_primitive_circle.cc | 6 ++--- .../nodes/node_geo_mesh_primitive_cone.cc | 10 ++++---- .../nodes/node_geo_mesh_primitive_cube.cc | 6 ++--- .../nodes/node_geo_mesh_primitive_cylinder.cc | 10 ++++---- .../nodes/node_geo_mesh_primitive_grid.cc | 6 ++--- .../node_geo_mesh_primitive_ico_sphere.cc | 4 +-- .../nodes/node_geo_mesh_primitive_line.cc | 4 +-- .../node_geo_mesh_primitive_uv_sphere.cc | 6 ++--- .../geometry/nodes/node_geo_mesh_subdivide.cc | 12 ++++----- .../nodes/node_geo_rotate_instances.cc | 8 +++--- .../nodes/node_geo_scale_instances.cc | 8 +++--- .../nodes/node_geo_set_curve_handles.cc | 8 +++--- .../nodes/node_geo_set_curve_radius.cc | 8 +++--- .../geometry/nodes/node_geo_set_curve_tilt.cc | 8 +++--- .../nodes/node_geo_set_point_radius.cc | 8 +++--- .../nodes/node_geo_translate_instances.cc | 8 +++--- .../geometry/nodes/node_geo_triangulate.cc | 8 +++--- 21 files changed, 106 insertions(+), 81 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 6ab3e24cb2b..6840dc2b777 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2126,6 +2126,31 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) version_node_input_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Points"); version_node_output_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Volume"); version_node_socket_name(ntree, GEO_NODE_SUBDIVISION_SURFACE, "Geometry", "Mesh"); + version_node_socket_name(ntree, GEO_NODE_RESAMPLE_CURVE, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SUBDIVIDE_CURVE, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SET_CURVE_RADIUS, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SET_CURVE_TILT, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SET_CURVE_HANDLES, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_TRANSLATE_INSTANCES, "Geometry", "Instances"); + version_node_socket_name(ntree, GEO_NODE_ROTATE_INSTANCES, "Geometry", "Instances"); + version_node_socket_name(ntree, GEO_NODE_SCALE_INSTANCES, "Geometry", "Instances"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry", "Mesh"); + version_node_input_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry 1", "Mesh 1"); + version_node_input_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry 2", "Mesh 2"); + version_node_socket_name(ntree, GEO_NODE_SUBDIVIDE_MESH, "Geometry", "Mesh"); + version_node_socket_name(ntree, GEO_NODE_TRIANGULATE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CONE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CUBE, "Geometry", "Mesh"); + version_node_output_socket_name( + ntree, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_GRID, "Geometry", "Mesh"); + version_node_output_socket_name( + ntree, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_LINE, "Geometry", "Mesh"); + version_node_output_socket_name( + ntree, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "Geometry", "Mesh"); + version_node_socket_name(ntree, GEO_NODE_SET_POINT_RADIUS, "Geometry", "Points"); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index dae7eb4bed1..63b88c57694 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -27,13 +27,13 @@ namespace blender::nodes { static void geo_node_boolean_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry 1") + b.add_input("Mesh 1") .only_realized_data() .supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Geometry 2").multi_input().supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input("Mesh 2").multi_input().supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Self Intersection"); b.add_input("Hole Tolerant"); - b.add_output("Geometry"); + b.add_output("Mesh"); } static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -53,12 +53,12 @@ static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node) case GEO_NODE_BOOLEAN_UNION: nodeSetSocketAvailability(geometry_1_socket, false); nodeSetSocketAvailability(geometry_2_socket, true); - node_sock_label(geometry_2_socket, N_("Geometry")); + node_sock_label(geometry_2_socket, N_("Mesh")); break; case GEO_NODE_BOOLEAN_DIFFERENCE: nodeSetSocketAvailability(geometry_1_socket, true); nodeSetSocketAvailability(geometry_2_socket, true); - node_sock_label(geometry_2_socket, N_("Geometry 2")); + node_sock_label(geometry_2_socket, N_("Mesh 2")); break; } } @@ -84,7 +84,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) GeometrySet set_a; if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) { - set_a = params.extract_input("Geometry 1"); + set_a = params.extract_input("Mesh 1"); /* Note that it technically wouldn't be necessary to realize the instances for the first * geometry input, but the boolean code expects the first shape for the difference operation * to be a single mesh. */ @@ -98,7 +98,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) /* The instance transform matrices are owned by the instance group, so we have to * keep all of them around for use during the boolean operation. */ Vector set_groups; - Vector geometry_sets = params.extract_multi_input("Geometry 2"); + Vector geometry_sets = params.extract_multi_input("Mesh 2"); for (const GeometrySet &geometry_set : geometry_sets) { bke::geometry_set_gather_instances(geometry_set, set_groups); } @@ -116,7 +116,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) Mesh *result = blender::meshintersect::direct_mesh_boolean( meshes, transforms, float4x4::identity(), {}, use_self, hole_tolerant, operation); - params.set_output("Geometry", GeometrySet::create_with_mesh(result)); + params.set_output("Mesh", GeometrySet::create_with_mesh(result)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index cc0fc6500c4..c0134e802cf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -34,11 +34,11 @@ namespace blender::nodes { static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Count").default_value(10).min(1).max(100000).supports_field(); b.add_input("Length").default_value(0.1f).min(0.001f).supports_field().subtype( PROP_DISTANCE); - b.add_output("Geometry"); + b.add_output("Curve"); } static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -234,7 +234,7 @@ static void geometry_set_curve_resample(GeometrySet &geometry_set, static void geo_node_resample_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Curve"); NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage; const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; @@ -244,7 +244,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { Field count = params.extract_input>("Count"); if (count < 1) { - params.set_output("Geometry", GeometrySet()); + params.set_output("Curve", GeometrySet()); return; } mode_param.count.emplace(count); @@ -257,7 +257,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets( [&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index a67a3a6736d..a0a62270b2d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -33,9 +33,9 @@ namespace blender::nodes { static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Cuts").default_value(1).min(0).max(1000).supports_field(); - b.add_output("Geometry"); + b.add_output("Curve"); } static Array get_subdivided_offsets(const Spline &spline, @@ -328,7 +328,7 @@ static std::unique_ptr subdivide_curve(const CurveEval &input_curve, static void geo_node_subdivide_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Curve"); Field cuts_field = params.extract_input>("Cuts"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { @@ -352,7 +352,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params) std::unique_ptr output_curve = subdivide_curve(*component.get_for_read(), cuts); geometry_set.replace_curve(output_curve.release()); }); - params.set_output("Geometry", geometry_set); + params.set_output("Curve", geometry_set); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 9c477c639a2..2374d28523c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -31,7 +31,7 @@ static void geo_node_mesh_primitive_circle_declare(NodeDeclarationBuilder &b) { b.add_input("Vertices").default_value(32).min(3); b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Geometry"); + b.add_output("Mesh"); } static void geo_node_mesh_primitive_circle_layout(uiLayout *layout, @@ -204,7 +204,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) const int verts_num = params.extract_input("Vertices"); if (verts_num < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -212,7 +212,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) BLI_assert(BKE_mesh_is_valid(mesh)); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 93c35a1111b..bac859c6461 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -35,7 +35,7 @@ static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b) b.add_input("Radius Top").min(0.0f).subtype(PROP_DISTANCE); b.add_input("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); b.add_input("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Geometry"); + b.add_output("Mesh"); } static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -708,14 +708,14 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) const int circle_segments = params.extract_input("Vertices"); if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } const int side_segments = params.extract_input("Side Segments"); if (side_segments < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -723,7 +723,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) const int fill_segments = no_fill ? 1 : params.extract_input("Fill Segments"); if (fill_segments < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -737,7 +737,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) /* Transform the mesh so that the base of the cone is at the origin. */ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 5a520d36296..f450e310384 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -30,7 +30,7 @@ static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b) b.add_input("Vertices X").default_value(2).min(2).max(1000); b.add_input("Vertices Y").default_value(2).min(2).max(1000); b.add_input("Vertices Z").default_value(2).min(2).max(1000); - b.add_output("Geometry"); + b.add_output("Mesh"); } struct CuboidConfig { @@ -476,13 +476,13 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) const int verts_z = params.extract_input("Vertices Z"); if (verts_x < 1 || verts_y < 1 || verts_z < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 1")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index 287ea896ade..a9c8592d8b6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -54,7 +54,7 @@ static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) .min(0.0f) .subtype(PROP_DISTANCE) .description("The height of the cylinder on the Z axis"); - b.add_output("Geometry"); + b.add_output("Mesh"); } static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout, @@ -102,14 +102,14 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) const int circle_segments = params.extract_input("Vertices"); if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } const int side_segments = params.extract_input("Side Segments"); if (side_segments < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -117,7 +117,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) const int fill_segments = no_fill ? 1 : params.extract_input("Fill Segments"); if (fill_segments < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -125,7 +125,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) Mesh *mesh = create_cylinder_or_cone_mesh( radius, radius, depth, circle_segments, side_segments, fill_segments, fill_type); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 858ef8648f8..056b88c0cc8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -33,7 +33,7 @@ static void geo_node_mesh_primitive_grid_declare(NodeDeclarationBuilder &b) b.add_input("Size Y").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); b.add_input("Vertices X").default_value(3).min(2).max(1000); b.add_input("Vertices Y").default_value(3).min(2).max(1000); - b.add_output("Geometry"); + b.add_output("Mesh"); } static void calculate_uvs( @@ -160,7 +160,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) const int verts_x = params.extract_input("Vertices X"); const int verts_y = params.extract_input("Vertices Y"); if (verts_x < 1 || verts_y < 1) { - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -168,7 +168,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) BLI_assert(BKE_mesh_is_valid(mesh)); BKE_id_material_eval_ensure_default_slot(&mesh->id); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index 5ea7165ac31..08da2f458d4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -30,7 +30,7 @@ static void geo_node_mesh_primitive_ico_sphere_declare(NodeDeclarationBuilder &b { b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); b.add_input("Subdivisions").default_value(1).min(1).max(7); - b.add_output("Geometry"); + b.add_output("Mesh"); } static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) @@ -66,7 +66,7 @@ static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input("Radius"); Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 031223b5ca6..9bf2df4aa4d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -33,7 +33,7 @@ static void geo_node_mesh_primitive_line_declare(NodeDeclarationBuilder &b) b.add_input("Resolution").default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE); b.add_input("Start Location").subtype(PROP_TRANSLATION); b.add_input("Offset").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_output("Geometry"); + b.add_output("Mesh"); } static void geo_node_mesh_primitive_line_layout(uiLayout *layout, @@ -154,7 +154,7 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) mesh = create_line_mesh(start, delta, count); } - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 6fd6cdf5747..400930d0394 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -32,7 +32,7 @@ static void geo_node_mesh_primitive_uv_shpere_declare(NodeDeclarationBuilder &b) b.add_input("Segments").default_value(32).min(3).max(1024); b.add_input("Rings").default_value(16).min(2).max(1024); b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Geometry"); + b.add_output("Mesh"); } static int sphere_vert_total(const int segments, const int rings) @@ -291,14 +291,14 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) if (rings_num < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Rings must be at least 3")); } - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } const float radius = params.extract_input("Radius"); Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index 471ebab71aa..2a243d6b8e3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Level").default_value(1).min(0).max(6); - b.add_output("Geometry"); + b.add_output("Mesh"); } static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level) @@ -74,12 +74,12 @@ static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int lev static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Mesh"); #ifndef WITH_OPENSUBDIV params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); return; #endif @@ -87,14 +87,14 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) const int subdiv_level = clamp_i(params.extract_input("Level"), 0, 11); if (subdiv_level == 0) { - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); return; } geometry_set.modify_geometry_sets( [&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 3688e88b51b..69d96e0e84e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -22,12 +22,12 @@ namespace blender::nodes { static void geo_node_rotate_instances_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").only_instances(); + b.add_input("Instances").only_instances(); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Rotation").subtype(PROP_EULER).supports_field(); b.add_input("Pivot Point").subtype(PROP_TRANSLATION).supports_field(); b.add_input("Local Space").default_value(true).supports_field(); - b.add_output("Geometry"); + b.add_output("Instances"); }; static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) @@ -77,12 +77,12 @@ static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &inst static void geo_node_rotate_instances_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Instances"); if (geometry_set.has_instances()) { InstancesComponent &instances = geometry_set.get_component_for_write(); rotate_instances(params, instances); } - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Instances", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index a5fa8fe1c73..eeaefcdc26e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -22,12 +22,12 @@ namespace blender::nodes { static void geo_node_scale_instances_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").only_instances(); + b.add_input("Instances").only_instances(); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Scale").subtype(PROP_XYZ).default_value({1, 1, 1}).supports_field(); b.add_input("Center").subtype(PROP_TRANSLATION).supports_field(); b.add_input("Local Space").default_value(true).supports_field(); - b.add_output("Geometry"); + b.add_output("Instances"); }; static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) @@ -74,12 +74,12 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta static void geo_node_scale_instances_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Instances"); if (geometry_set.has_instances()) { InstancesComponent &instances = geometry_set.get_component_for_write(); scale_instances(params, instances); } - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Instances", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index 92a01d42241..a744754382a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -25,10 +25,10 @@ namespace blender::nodes { static void geo_node_set_curve_handles_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Position").implicit_field(); - b.add_output("Geometry"); + b.add_output("Curve"); } static void geo_node_set_curve_handles_layout(uiLayout *layout, @@ -125,7 +125,7 @@ static void geo_node_set_curve_handles_exec(GeoNodeExecParams params) (NodeGeometrySetCurveHandlePositions *)params.node().storage; const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode; - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Curve"); Field selection_field = params.extract_input>("Selection"); Field position_field = params.extract_input>("Position"); @@ -144,7 +144,7 @@ static void geo_node_set_curve_handles_exec(GeoNodeExecParams params) params.error_message_add(NodeWarningType::Info, TIP_("The input geometry does not contain a Bezier spline")); } - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index 1e8c0acf0e4..bad81c54553 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -20,11 +20,11 @@ namespace blender::nodes { static void geo_node_set_curve_radius_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Radius").min(0.0f).default_value(1.0f).supports_field().subtype( PROP_DISTANCE); - b.add_output("Geometry"); + b.add_output("Curve"); } static void set_radius_in_component(GeometryComponent &component, @@ -52,7 +52,7 @@ static void set_radius_in_component(GeometryComponent &component, static void geo_node_set_curve_radius_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Curve"); Field selection_field = params.extract_input>("Selection"); Field radii_field = params.extract_input>("Radius"); @@ -63,7 +63,7 @@ static void geo_node_set_curve_radius_exec(GeoNodeExecParams params) } }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index 2018b1668eb..670d396dbc6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_set_curve_tilt_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Tilt").subtype(PROP_ANGLE).supports_field(); - b.add_output("Geometry"); + b.add_output("Curve"); } static void set_tilt_in_component(GeometryComponent &component, @@ -51,7 +51,7 @@ static void set_tilt_in_component(GeometryComponent &component, static void geo_node_set_curve_tilt_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Curve"); Field selection_field = params.extract_input>("Selection"); Field tilt_field = params.extract_input>("Tilt"); @@ -62,7 +62,7 @@ static void geo_node_set_curve_tilt_exec(GeoNodeExecParams params) } }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index 449e9b55a39..791bc8cde6c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -20,11 +20,11 @@ namespace blender::nodes { static void geo_node_set_point_radius_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input("Points").supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Radius").default_value(0.05f).min(0.0f).supports_field().subtype( PROP_DISTANCE); - b.add_output("Geometry"); + b.add_output("Points"); } static void set_radius_in_component(GeometryComponent &component, @@ -52,7 +52,7 @@ static void set_radius_in_component(GeometryComponent &component, static void geo_node_set_point_radius_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Points"); Field selection_field = params.extract_input>("Selection"); Field radii_field = params.extract_input>("Radius"); @@ -64,7 +64,7 @@ static void geo_node_set_point_radius_exec(GeoNodeExecParams params) } }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Points", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index 8bd9aece229..d4aa7a327da 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -22,11 +22,11 @@ namespace blender::nodes { static void geo_node_translate_instances_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").only_instances(); + b.add_input("Instances").only_instances(); b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_input("Translation").subtype(PROP_TRANSLATION).supports_field(); b.add_input("Local Space").default_value(true).supports_field(); - b.add_output("Geometry"); + b.add_output("Instances"); }; static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) @@ -62,12 +62,12 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i static void geo_node_translate_instances_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Instances"); if (geometry_set.has_instances()) { InstancesComponent &instances = geometry_set.get_component_for_write(); translate_instances(params, instances); } - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Instances", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index e0d99567d20..1b2201c791a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -31,9 +31,9 @@ namespace blender::nodes { static void geo_node_triangulate_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input("Minimum Vertices").default_value(4).min(4).max(10000); - b.add_output("Geometry"); + b.add_output("Mesh"); } static void geo_node_triangulate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -50,7 +50,7 @@ static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node) static void geo_node_triangulate_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Mesh"); const int min_vertices = std::max(params.extract_input("Minimum Vertices"), 4); GeometryNodeTriangulateQuads quad_method = static_cast( @@ -67,7 +67,7 @@ static void geo_node_triangulate_exec(GeoNodeExecParams params) } }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes From defc1b8e1815155840cff1736523760083a05807 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 27 Oct 2021 10:24:29 -0500 Subject: [PATCH 1305/1500] Fix: Realize instances versioning fails for curve to mesh node It assumed that the last input socket was a geometry socket, but now it is the fill caps boolean option. --- source/blender/blenloader/intern/versioning_300.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 6840dc2b777..9bd6280dbf4 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -535,7 +535,7 @@ static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree) } /* Also realize instances for the profile input of the curve to mesh node. */ if (node->type == GEO_NODE_CURVE_TO_MESH) { - bNodeSocket *profile_socket = node->inputs.last; + bNodeSocket *profile_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1); add_realize_instances_before_socket(ntree, node, profile_socket); } } From 487faed6d0a1170a2f098552f794280fabe82186 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 17:38:59 +0200 Subject: [PATCH 1306/1500] Asset Browser: Ensure parent catalogs are expanded when adding child When pressing the '+' icon to add a new child catalog, or when adding it through the context menu, the new catalog should be visible. So the this change makes sure the parent is uncollapsed if needed. --- source/blender/editors/include/UI_tree_view.hh | 11 +++++++++++ source/blender/editors/interface/tree_view.cc | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 0d18eedeac9..77472979beb 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -196,6 +196,15 @@ class AbstractTreeView : public TreeViewItemContainer { * the actual state changes are done in a delayed manner through this function. */ void change_state_delayed(); + /** + * Typically when adding a new child from the UI, the parent should be expanded to make the new + * item visible. While this isn't totally bullet proof (items may be expanded where the child was + * added dynamically, not through user action), it should give the wanted behavior as needed in + * practice. + * "Focused" here means active or hovered, as a way to tell if the new item came from user + * interaction (on the row directly or through the context menu). + */ + void unveil_new_items_in_focused_parent() const; void build_layout_from_tree(const TreeViewLayoutBuilder &builder); }; @@ -223,6 +232,8 @@ class AbstractTreeViewItem : public TreeViewItemContainer { bool is_open_ = false; bool is_active_ = false; bool is_renaming_ = false; + /* Could the item be identified from a previous redraw? */ + bool is_new_ = true; IsActiveFn is_active_fn_; diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index c08fa51d5a5..d64f9d9c975 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -169,6 +169,21 @@ void AbstractTreeView::change_state_delayed() foreach_item([](AbstractTreeViewItem &item) { item.change_state_delayed(); }); } +void AbstractTreeView::unveil_new_items_in_focused_parent() const +{ + foreach_item([](AbstractTreeViewItem &item) { + if (!item.is_new_ || !item.parent_) { + return; + } + if (item.parent_->is_active() || + /* Tree-row button is not created if a parent is collapsed. It's required for the + * hover-check. */ + (item.parent_->tree_row_but_ && item.parent_->is_hovered())) { + item.parent_->set_collapsed(false); + } + }); +} + /* ---------------------------------------------------------------------- */ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, @@ -393,6 +408,7 @@ void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) is_open_ = old.is_open_; is_active_ = old.is_active_; is_renaming_ = old.is_renaming_; + is_new_ = false; } bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const @@ -569,6 +585,7 @@ void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view) tree_view.update_from_old(block_); tree_view.change_state_delayed(); tree_view.build_layout_from_tree(TreeViewLayoutBuilder(block_)); + tree_view.unveil_new_items_in_focused_parent(); } /* ---------------------------------------------------------------------- */ From bca9ec767cd7a017a8cbe64ef0bd4b713b99a2b4 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 18:24:25 +0200 Subject: [PATCH 1307/1500] Revert "Asset Browser: Ensure parent catalogs are expanded when adding child" This reverts commit 487faed6d0a1170a2f098552f794280fabe82186. I changed my mind on how to implement this feature. Adding a catalog should also activate it, like we do it for adding other data in Blender. The activation will automatically make it visible then. See the following commit. --- source/blender/editors/include/UI_tree_view.hh | 11 ----------- source/blender/editors/interface/tree_view.cc | 17 ----------------- 2 files changed, 28 deletions(-) diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 77472979beb..0d18eedeac9 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -196,15 +196,6 @@ class AbstractTreeView : public TreeViewItemContainer { * the actual state changes are done in a delayed manner through this function. */ void change_state_delayed(); - /** - * Typically when adding a new child from the UI, the parent should be expanded to make the new - * item visible. While this isn't totally bullet proof (items may be expanded where the child was - * added dynamically, not through user action), it should give the wanted behavior as needed in - * practice. - * "Focused" here means active or hovered, as a way to tell if the new item came from user - * interaction (on the row directly or through the context menu). - */ - void unveil_new_items_in_focused_parent() const; void build_layout_from_tree(const TreeViewLayoutBuilder &builder); }; @@ -232,8 +223,6 @@ class AbstractTreeViewItem : public TreeViewItemContainer { bool is_open_ = false; bool is_active_ = false; bool is_renaming_ = false; - /* Could the item be identified from a previous redraw? */ - bool is_new_ = true; IsActiveFn is_active_fn_; diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index d64f9d9c975..c08fa51d5a5 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -169,21 +169,6 @@ void AbstractTreeView::change_state_delayed() foreach_item([](AbstractTreeViewItem &item) { item.change_state_delayed(); }); } -void AbstractTreeView::unveil_new_items_in_focused_parent() const -{ - foreach_item([](AbstractTreeViewItem &item) { - if (!item.is_new_ || !item.parent_) { - return; - } - if (item.parent_->is_active() || - /* Tree-row button is not created if a parent is collapsed. It's required for the - * hover-check. */ - (item.parent_->tree_row_but_ && item.parent_->is_hovered())) { - item.parent_->set_collapsed(false); - } - }); -} - /* ---------------------------------------------------------------------- */ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, @@ -408,7 +393,6 @@ void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) is_open_ = old.is_open_; is_active_ = old.is_active_; is_renaming_ = old.is_renaming_; - is_new_ = false; } bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const @@ -585,7 +569,6 @@ void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view) tree_view.update_from_old(block_); tree_view.change_state_delayed(); tree_view.build_layout_from_tree(TreeViewLayoutBuilder(block_)); - tree_view.unveil_new_items_in_focused_parent(); } /* ---------------------------------------------------------------------- */ From 71adad288b02ae98608693d728843b5917f336e6 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 18:25:29 +0200 Subject: [PATCH 1308/1500] Asset Browser: Activate catalog after adding Adding a catalog should also activate it, like we do it for adding other data in Blender. The tree-view code will make sure the newly added item will not have collapsed parents. --- source/blender/editors/asset/intern/asset_ops.cc | 7 ++++++- source/blender/editors/include/ED_fileselect.h | 4 ++++ source/blender/editors/space_file/filesel.c | 12 ++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 158e877ed7d..d2fd8ab88a4 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -427,7 +427,12 @@ static int asset_catalog_new_exec(bContext *C, wmOperator *op) struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); char *parent_path = RNA_string_get_alloc(op->ptr, "parent_path", nullptr, 0, nullptr); - ED_asset_catalog_add(asset_library, "Catalog", parent_path); + blender::bke::AssetCatalog *new_catalog = ED_asset_catalog_add( + asset_library, "Catalog", parent_path); + + if (sfile) { + ED_fileselect_activate_asset_catalog(sfile, new_catalog->catalog_id); + } MEM_freeN(parent_path); diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 48ff742ef3a..68b6e44371c 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -23,6 +23,8 @@ #pragma once +#include "DNA_uuid_types.h" + #ifdef __cplusplus extern "C" { #endif @@ -145,6 +147,8 @@ bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile); struct AssetLibrary *ED_fileselect_active_asset_library_get(const struct SpaceFile *sfile); struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile); +void ED_fileselect_activate_asset_catalog(const struct SpaceFile *sfile, bUUID catalog_id); + /* Activate and select the file that corresponds to the given ID. * Pass deferred=true to wait for the next refresh before activating. */ void ED_fileselect_activate_by_id(struct SpaceFile *sfile, diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 5299b8ba580..11757975a62 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -483,6 +483,18 @@ struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile) return filelist_file_get_id(file); } +void ED_fileselect_activate_asset_catalog(const SpaceFile *sfile, const bUUID catalog_id) +{ + if (!ED_fileselect_is_asset_browser(sfile)) { + return; + } + + FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile); + params->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG; + params->catalog_id = catalog_id; + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL); +} + static void on_reload_activate_by_id(SpaceFile *sfile, onReloadFnData custom_data) { ID *asset_id = (ID *)custom_data; From bfec984cf82a64a74ee86a902348a46e3c231b8f Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Wed, 27 Oct 2021 18:20:40 +0200 Subject: [PATCH 1309/1500] UI: Theme refresh for Blender v3.0 {F11548100, size=full} To celebrate the beginning of a new series, it feels like the right time to give the theme a fresh look while improving on what already works. The aim of this refresh is to keep a familiar look but with polishing touches here and there. Like new paint on the walls of your well known house. The theme for Blender 2.8 was well received but presented a few flaws. * Transparency on menus and tooltips reduce readability * Mismatch on certain colors, especially outlines of connected widgets * Active/open menus highlight was not prominent enough * Header background mismatch in some editors At the same time we can make use of new features in 3.0: * Make panels look like panels again (like in v2.3!) * Make use of roundness in more widgets * Since nodes are no longer hard-coded to be transparent, tweak opacity and saturation * Tweak colors for the new dot grid This update does not include: * Meshes in edit mode to match greenish object-data color. This needs tweaks in the code to improve contrast. * A copy of the Blender 2.8x legacy theme. This could be added to the community themes (shouldn't cost much maintenance, I hope) There will be certainly small tweaks to do here and there, I've been working using this theme for months but there can be areas that are missing update. The overall style is presented here. This commit bumps the file subversion. Reviewed By: #user_interface, Severin Differential Revision: https://developer.blender.org/D13008 --- .../datafiles/userdef/userdef_default_theme.c | 852 +++++++++--------- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_300.c | 26 +- .../blenloader/intern/versioning_userdef.c | 16 +- source/blender/editors/include/UI_interface.h | 2 +- source/blender/editors/interface/resources.c | 7 +- 6 files changed, 467 insertions(+), 438 deletions(-) diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index d0171867e50..f33ecea0eed 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -22,181 +22,163 @@ const bTheme U_theme_default = { .name = "Default", .tui = { .wcol_regular = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x585858ff), - .inner_sel = RGBA(0x5680c2e6), - .item = RGBA(0x3e3e3eff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x545454ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0xffffff80), .text = RGBA(0xd9d9d9ff), .text_sel = RGBA(0xffffffff), - .shadedown = -5, .roundness = 0.2f, }, .wcol_tool = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x585858ff), - .inner_sel = RGBA(0x5680c2ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x545454ff), + .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xffffffff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shadedown = -5, .roundness = 0.2f, }, .wcol_toolbar_item = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x313131ff), - .inner_sel = RGBA(0x5680c2ff), - .item = RGBA(0xe6e6e6cc), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x282828ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0xffffff80), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_text = { - .outline = RGBA(0x444444ff), - .inner = RGBA(0x1f1f1fff), - .inner_sel = RGBA(0x505050ff), - .item = RGBA(0x191919ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x1d1d1dff), + .inner_sel = RGBA(0x181818ff), + .item = RGBA(0x4772b3ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shaded = 1, - .shadetop = -3, .roundness = 0.2f, }, .wcol_radio = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x595959ff), - .inner_sel = RGBA(0x5680c2e6), - .item = RGBA(0xffffffff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x282828ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0x252525ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shadetop = 5, - .shadedown = -5, .roundness = 0.2f, }, .wcol_option = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x666666ff), - .inner_sel = RGBA(0x5680c2e6), - .item = RGBA(0xffffffff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x282828ff), + .inner_sel = RGBA(0x71aaffff), + .item = RGBA(0x111111ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shadedown = -15, .roundness = 0.2f, }, .wcol_toggle = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x595959ff), - .inner_sel = RGBA(0x5680c2e6), - .item = RGBA(0x191919ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x282828ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0x252525ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_num = { - .outline = RGBA(0x444444ff), - .inner = RGBA(0x595959ff), - .inner_sel = RGBA(0x505050ff), - .item = RGBA(0x191919ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x545454ff), + .inner_sel = RGBA(0x222222ff), + .item = RGBA(0x4772b3ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_numslider = { - .outline = RGBA(0x444444ff), - .inner = RGBA(0x595959ff), - .inner_sel = RGBA(0x505050ff), - .item = RGBA(0x5680c2e6), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x545454ff), + .inner_sel = RGBA(0x222222ff), + .item = RGBA(0x4772b3ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shaded = 1, - .shadetop = -4, .roundness = 0.2f, }, .wcol_tab = { - .outline = RGBA(0x202020ff), - .inner = RGBA(0x2b2b2bff), - .inner_sel = RGBA(0x424242ff), - .item = RGBA(0x2d2d2dff), + .outline = RGBA(0x1d1d1dff), + .inner = RGBA(0x1d1d1dff), + .inner_sel = RGBA(0x303030ff), + .item = RGBA(0x1d1d1dff), .text = RGBA(0x989898ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_menu = { - .outline = RGBA(0x444444ff), - .inner = RGBA(0x2c2c2cff), - .inner_sel = RGBA(0x696e76ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x282828ff), + .inner_sel = RGBA(0x4772b3b3), .item = RGBA(0xd9d9d9ff), - .text = RGBA(0xd9d9d9ff), + .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shadetop = 10, - .shadedown = -10, .roundness = 0.2f, }, .wcol_pulldown = { - .outline = RGBA(0x4d4d4dff), - .inner = RGBA(0x2e2e2ecc), - .inner_sel = RGBA(0x5680c2e6), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x22222266), + .inner_sel = RGBA(0x4772b3b3), .item = RGBA(0x727272ff), .text = RGBA(0xd9d9d9ff), .text_sel = RGBA(0xffffffff), - .shadetop = 25, - .shadedown = -20, .roundness = 0.2f, }, .wcol_menu_back = { - .outline = RGBA(0x19191aff), - .inner = RGBA(0x1f1f1fef), - .inner_sel = RGBA(0x585858ff), - .item = RGBA(0x727272ff), - .text = RGBA(0xa5a5a5ff), + .outline = RGBA(0x242424ff), + .inner = RGBA(0x181818ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0xd9d9d9ff), + .text = RGBA(0x999999ff), .text_sel = RGBA(0xffffffff), - .shadetop = 25, - .shadedown = -20, .roundness = 0.2f, }, .wcol_menu_item = { - .inner_sel = RGBA(0x5680c2e6), + .outline = RGBA(0x3d3d3d00), + .inner = RGBA(0x18181800), + .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xffffff8f), - .text = RGBA(0xe6e6e6ff), + .text = RGBA(0xddddddff), .text_sel = RGBA(0xffffffff), - .shadetop = 38, .roundness = 0.2f, }, .wcol_tooltip = { .outline = RGBA(0x19191aff), - .inner = RGBA(0x19191aef), - .inner_sel = RGBA(0x19191aef), - .item = RGBA(0x19191aef), - .text = RGBA(0xe6e6e6ff), + .inner = RGBA(0x181818ff), + .inner_sel = RGBA(0x181818ff), + .item = RGBA(0x181818ff), + .text = RGBA(0xccccccff), .text_sel = RGBA(0xffffffff), - .shadetop = 25, - .shadedown = -20, .roundness = 0.2f, }, .wcol_box = { - .outline = RGBA(0x444444ff), - .inner = RGBA(0x00000033), - .inner_sel = RGBA(0x696e76ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x1d1d1d80), + .inner_sel = RGBA(0x545454ff), .item = RGBA(0x191919ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_scroll = { - .outline = RGBA(0x424242ff), - .inner = RGBA(0x67676700), - .inner_sel = RGBA(0xb3b3b3ff), - .item = RGBA(0x676767ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x22222200), + .inner_sel = RGBA(0xffffffff), + .item = RGBA(0x545454ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shadetop = 5, - .shadedown = -5, .roundness = 0.5f, }, .wcol_progress = { - .outline = RGBA(0x585858ff), - .inner = RGBA(0x2c2c2cff), - .inner_sel = RGBA(0x5680c2ff), - .item = RGBA(0x5680c2ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x222222ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0x4772b3ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, @@ -204,21 +186,19 @@ const bTheme U_theme_default = { .wcol_list_item = { .outline = RGBA(0x2d2d2dff), .inner = RGBA(0x2d2d2d00), - .inner_sel = RGBA(0x696e76ff), + .inner_sel = RGBA(0x484a4fff), .item = RGBA(0xb3b3b3ff), - .text = RGBA(0xe6e6e6ff), + .text = RGBA(0xccccccff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_pie_menu = { - .outline = RGBA(0x333333ff), - .inner = RGBA(0x212121ef), - .inner_sel = RGBA(0x5680c2e6), - .item = RGBA(0x585858ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x181818ff), + .inner_sel = RGBA(0x5680c2ff), + .item = RGBA(0x4772b3ff), .text = RGBA(0xd9d9d9ff), .text_sel = RGBA(0xffffffff), - .shadetop = 10, - .shadedown = -10, .roundness = 0.2f, }, .wcol_state = { @@ -235,15 +215,15 @@ const bTheme U_theme_default = { .blend = 0.5f, }, .widget_emboss = RGBA(0x00000026), - .menu_shadow_fac = 0.3f, - .menu_shadow_width = 4, - .editor_outline = RGBA(0x1f1f1fff), + .menu_shadow_fac = 0.4f, + .menu_shadow_width = 2, + .editor_outline = RGBA(0x161616ff), .transparent_checker_primary = RGBA(0x333333ff), .transparent_checker_secondary = RGBA(0x262626ff), .transparent_checker_size = 8, .icon_alpha = 1.0f, .icon_saturation = 0.5f, - .widget_text_cursor = RGBA(0x3399e6ff), + .widget_text_cursor = RGBA(0x71a8ffff), .xaxis = RGBA(0xff3352ff), .yaxis = RGBA(0x8bdc00ff), .zaxis = RGBA(0x2890ffff), @@ -253,65 +233,70 @@ const bTheme U_theme_default = { .gizmo_view_align = RGBA(0xffffffff), .gizmo_a = RGBA(0x4da84dff), .gizmo_b = RGBA(0xa33535ff), - .icon_scene = RGBA(0xe6e6e6ff), - .icon_collection = RGBA(0xf4f4f4ff), - .icon_object = RGBA(0xee9e5dff), + .icon_scene = RGBA(0xccccccff), + .icon_collection = RGBA(0xffffffff), + .icon_object = RGBA(0xe19658ff), .icon_object_data = RGBA(0x00d4a3ff), - .icon_modifier = RGBA(0x84b8ffff), - .icon_shading = RGBA(0xea7581ff), - .icon_folder = RGBA(0xe3c16eff), + .icon_modifier = RGBA(0x74a2ffff), + .icon_shading = RGBA(0xcc6670ff), + .icon_folder = RGBA(0xccad63ff), .panel_roundness = 0.4f, }, .space_properties = { - .back = RGBA(0x42424200), - .title = RGBA(0xd4d4d4ff), + .back = RGBA(0x30303000), + .title = RGBA(0xe6e6e6ff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), - .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), + .button_title = RGBA(0xccccccff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .navigation_bar = RGBA(0x232323ff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x1d1d1dff), .panelcolors = { - .header = RGBA(0x424242ff), - .back = RGBA(0x383838ff), - .sub_back = RGBA(0x00000024), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, + .active = RGBA(0x4772b3ff), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, - .match = RGBA(0x5680c2ff), - .active = RGBA(0x5680c2ff), + .match = RGBA(0x4772b3ff), }, .space_view3d = { - .back = RGBA(0x393939ff), + .back = RGBA(0x3d3d3dff), + .back_grad = RGBA(0x30303000), + .background_type = 2, .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x42424200), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x35353500), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242e6), - .back = RGBA(0x333333f0), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .grid = RGBA(0x6666664d), + .grid = RGBA(0x54545480), .wire = RGBA(0x000000ff), .wire_edit = RGBA(0x000000ff), .select = RGBA(0xed5700ff), @@ -347,7 +332,7 @@ const bTheme U_theme_default = { .bone_pose = RGBA(0x50c8ff50), .bone_pose_active = RGBA(0x8cffff50), .bone_locked_weight = RGBA(0xff000080), - .cframe = RGBA(0x60c040ff), + .cframe = RGBA(0x4772b3ff), .time_keyframe = RGBA(0xddd700ff), .time_gp_keyframe = RGBA(0xb5e61dff), .freestyle_edge_mark = RGBA(0x7fff7fff), @@ -371,7 +356,7 @@ const bTheme U_theme_default = { .obcenter_dia = 6, .facedot_size = 3, .editmesh_active = RGBA(0xffffff33), - .clipping_border_3d = RGBA(0x313131ff), + .clipping_border_3d = RGBA(0x3f3f3fff), .bundle_solid = RGBA(0xc8c8c8ff), .camera_path = RGBA(0x000000ff), .gp_vertex_size = 3, @@ -386,66 +371,69 @@ const bTheme U_theme_default = { .title = RGBA(0xffffffff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x2e2e2eff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x4b4b4bff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .execution_buts = RGBA(0x444444ff), + .navigation_bar = RGBA(0x303030ff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x4b4b4bff), - .back = RGBA(0x404040ff), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .hilite = RGBA(0x4f76b3ff), + .hilite = RGBA(0x4772b3ff), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, - .row_alternate = RGBA(0xffffff07), + .row_alternate = RGBA(0xffffff04), }, .space_graph = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xffffffff), .text = RGBA(0xa6a6a6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x2e2e2eff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .list = RGBA(0x282828ff), + .list = RGBA(0x1d1d1dff), .list_title = RGBA(0xffffffff), .list_text = RGBA(0xb8b8b8ff), - .list_text_hi = RGBA(0xffaf29ff), + .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .shade1 = RGBA(0x96969600), - .shade2 = RGBA(0x33333364), - .grid = RGBA(0x2a2a2aff), - .group = RGBA(0x16330fff), - .group_active = RGBA(0x368024ff), + .shade2 = RGBA(0x12121264), + .grid = RGBA(0x1a1a1aff), + .group = RGBA(0x1a332dff), + .group_active = RGBA(0x216d5bff), .vertex = RGBA(0x000000ff), .vertex_select = RGBA(0xff8500ff), .vertex_active = RGBA(0xffffffff), - .cframe = RGBA(0x5680c2ff), - .time_scrub_background = RGBA(0x292929e6), - .time_marker_line = RGBA(0x00000060), - .time_marker_line_selected = RGBA(0xffffff60), + .cframe = RGBA(0x4772b3ff), + .time_scrub_background = RGBA(0x161616ff), + .time_marker_line = RGBA(0xffffff4d), + .time_marker_line_selected = RGBA(0xffffffb3), .lastsel_point = RGBA(0xffffffff), .handle_auto = RGBA(0x909000ff), .handle_vect = RGBA(0x409030ff), @@ -455,8 +443,8 @@ const bTheme U_theme_default = { .handle_sel_vect = RGBA(0x40c030ff), .handle_sel_align = RGBA(0xf090a0ff), .handle_sel_auto_clamped = RGBA(0xf0af90ff), - .ds_channel = RGBA(0x0f2c4dff), - .ds_subchannel = RGBA(0x143e66ff), + .ds_channel = RGBA(0x194e80ff), + .ds_subchannel = RGBA(0x0f2c4dff), .vertex_size = 6, .outline_width = 1, .facedot_size = 4, @@ -465,25 +453,27 @@ const bTheme U_theme_default = { .anim_preview_range = RGBA(0xa14d0066), }, .space_info = { - .back = RGBA(0x28282800), + .back = RGBA(0x1d1d1d00), .title = RGBA(0xffffffff), .text = RGBA(0xc3c3c3ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x454545ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .vertex_size = 3, .outline_width = 1, @@ -498,56 +488,58 @@ const bTheme U_theme_default = { .info_info_text = RGBA(0xffffffff), .info_debug = RGBA(0x6b3293ff), .info_debug_text = RGBA(0xffffffff), - .info_property = RGBA(0x329364ff), + .info_property = RGBA(0x236666ff), .info_property_text = RGBA(0xffffffff), - .info_operator = RGBA(0x329364ff), + .info_operator = RGBA(0x235266ff), .info_operator_text = RGBA(0xffffffff), }, .space_action = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xeeeeeeff), .text = RGBA(0xa6a6a6ff), - .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .text_hi = RGBA(0x143e66ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x282828ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x22222200), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .list = RGBA(0x282828ff), + .list = RGBA(0x1d1d1dff), .list_title = RGBA(0xffffffff), .list_text = RGBA(0xb8b8b8ff), - .list_text_hi = RGBA(0xffaf29ff), + .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .shade1 = RGBA(0xc0c0c000), - .shade2 = RGBA(0x333333ff), + .shade2 = RGBA(0x1d1d1d99), .hilite = RGBA(0x60c04044), - .grid = RGBA(0x2a2a2aff), - .group = RGBA(0x16330f37), - .group_active = RGBA(0x36802455), - .strip = RGBA(0x1a151580), - .strip_select = RGBA(0xff8c00cc), - .cframe = RGBA(0x5680c2ff), - .time_scrub_background = RGBA(0x292929e6), - .time_marker_line = RGBA(0x00000060), - .time_marker_line_selected = RGBA(0xffffff60), - .ds_channel = RGBA(0x0f2c4d24), - .ds_subchannel = RGBA(0x143e6624), + .grid = RGBA(0x161616ff), + .group = RGBA(0x1a332d37), + .group_active = RGBA(0x216d5b67), + .strip = RGBA(0xffffff1f), + .strip_select = RGBA(0xff8c0099), + .cframe = RGBA(0x4772b3ff), + .time_scrub_background = RGBA(0x1d1d1dff), + .time_marker_line = RGBA(0xffffff4d), + .time_marker_line_selected = RGBA(0xffffffb3), + .ds_channel = RGBA(0x194e8080), + .ds_subchannel = RGBA(0x0f2c4d80), .ds_ipoline = RGBA(0x94e575cc), - .keytype_keyframe = RGBA(0xe8e8e8ff), + .keytype_keyframe = RGBA(0xbfbfbfff), .keytype_extreme = RGBA(0xe8b3ccff), .keytype_breakdown = RGBA(0xb3dbe8ff), .keytype_jitter = RGBA(0x94e575ff), - .keytype_movehold = RGBA(0x5c5656ff), + .keytype_movehold = RGBA(0x808080ff), .keytype_keyframe_select = RGBA(0xffbe33ff), .keytype_extreme_select = RGBA(0xf28080ff), .keytype_breakdown_select = RGBA(0x54bfedff), @@ -560,43 +552,45 @@ const bTheme U_theme_default = { .facedot_size = 4, .keyframe_scale_fac = 1.0f, .handle_vertex_size = 4, - .anim_active = RGBA(0x4d250066), + .anim_active = RGBA(0x4d272766), .anim_preview_range = RGBA(0xa14d0066), }, .space_nla = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xffffffff), .text = RGBA(0xa6a6a6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .list = RGBA(0x282828ff), + .list = RGBA(0x1d1d1dff), .list_title = RGBA(0xffffffff), - .list_text = RGBA(0xb8b8b8ff), - .list_text_hi = RGBA(0xffaf29ff), + .list_text = RGBA(0xe5e5e5ff), + .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .shade1 = RGBA(0x96969600), .grid = RGBA(0x2a2a2aff), - .strip = RGBA(0x0c0a0a80), + .strip = RGBA(0x0d0d0d80), .strip_select = RGBA(0xff8c00ff), - .cframe = RGBA(0x5680c2ff), - .time_scrub_background = RGBA(0x292929e6), - .time_marker_line = RGBA(0x00000060), - .time_marker_line_selected = RGBA(0xffffff60), - .ds_channel = RGBA(0x5a85b2ff), + .cframe = RGBA(0x4772b3ff), + .time_scrub_background = RGBA(0x161616ff), + .time_marker_line = RGBA(0xffffff4d), + .time_marker_line_selected = RGBA(0xffffffb3), + .ds_channel = RGBA(0x0f2c4dff), .ds_subchannel = RGBA(0x7d98b3ff), .keyborder = RGBA(0x000000ff), .keyborder_select = RGBA(0x000000ff), @@ -604,12 +598,12 @@ const bTheme U_theme_default = { .outline_width = 1, .facedot_size = 4, .handle_vertex_size = 4, - .anim_active = RGBA(0xcc701a66), - .anim_non_active = RGBA(0x9987614d), + .anim_active = RGBA(0x99541366), + .anim_non_active = RGBA(0x4d3b174d), .anim_preview_range = RGBA(0xa14d0066), .nla_tweaking = RGBA(0x4df31a4d), .nla_tweakdupli = RGBA(0xd90000ff), - .nla_track = RGBA(0x424242ff), + .nla_track = RGBA(0x303030ff), .nla_transition = RGBA(0x1c2630ff), .nla_transition_sel = RGBA(0x2e75dbff), .nla_meta = RGBA(0x332642ff), @@ -618,34 +612,36 @@ const bTheme U_theme_default = { .nla_sound_sel = RGBA(0x1f7a7aff), }, .space_sequencer = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xeeeeeeff), .text = RGBA(0xa6a6a6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .shade1 = RGBA(0xa0a0a000), - .grid = RGBA(0x212121ff), + .grid = RGBA(0x181818ff), .vertex_select = RGBA(0xff8500ff), .bone_pose = RGBA(0x50c8ff50), - .cframe = RGBA(0x5680c2ff), - .time_scrub_background = RGBA(0x292929e6), - .time_marker_line = RGBA(0x00000060), - .time_marker_line_selected = RGBA(0xffffff60), + .cframe = RGBA(0x4772b3ff), + .time_scrub_background = RGBA(0x121212ff), + .time_marker_line = RGBA(0xffffff4d), + .time_marker_line_selected = RGBA(0xffffffb3), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, @@ -653,8 +649,8 @@ const bTheme U_theme_default = { .movieclip = RGBA(0x8f4c4cff), .mask = RGBA(0x666666ff), .image = RGBA(0x8f744bff), - .scene = RGBA(0x828f50ff), - .audio = RGBA(0x4c8f8fff), + .scene = RGBA(0x808033ff), + .audio = RGBA(0x448080ff), .effect = RGBA(0x514a73ff), .transition = RGBA(0x8f4571ff), .meta = RGBA(0x5b4d91ff), @@ -664,39 +660,41 @@ const bTheme U_theme_default = { .selected_strip = RGBA(0xff8f0dff), .gp_vertex_size = 3, .gp_vertex_select = RGBA(0xff8500ff), + .row_alternate = RGBA(0xffffff05), .anim_preview_range = RGBA(0xa14d0066), .metadatatext = RGBA(0xffffffff), - .row_alternate = RGBA(0xffffff0d), }, .space_image = { - .back = RGBA(0x44444400), + .back = RGBA(0x30303000), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x35353500), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .grid = RGBA(0x505050ff), + .grid = RGBA(0x303030ff), .wire_edit = RGBA(0xc0c0c0ff), .vertex_select = RGBA(0xff8500ff), .edge_select = RGBA(0xff8500ff), .face = RGBA(0xffffff0a), .face_select = RGBA(0xff85003c), .face_dot = RGBA(0xff8500ff), - .cframe = RGBA(0x60c040ff), + .cframe = RGBA(0x4772b3ff), .freestyle_face_mark = RGBA(0x7fff7f33), .handle_auto = RGBA(0x909000ff), .handle_align = RGBA(0x803060ff), @@ -723,182 +721,190 @@ const bTheme U_theme_default = { .metadatatext = RGBA(0xffffffff), }, .space_text = { - .back = RGBA(0x30303000), + .back = RGBA(0x23232300), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x42424200), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x42424200), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .shade2 = RGBA(0x5680c2e6), + .shade2 = RGBA(0x2d4366e6), .hilite = RGBA(0xff0000ff), - .grid = RGBA(0x202020ff), + .grid = RGBA(0x1d1d1dff), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, - .syntaxl = RGBA(0xf6e162ff), + .syntaxl = RGBA(0xe6d573ff), .syntaxs = RGBA(0xff734dff), - .syntaxb = RGBA(0xff1961ff), - .syntaxn = RGBA(0x50dbffff), - .syntaxv = RGBA(0x95d600ff), + .syntaxb = RGBA(0xe62e67ff), + .syntaxn = RGBA(0x48c5e6ff), + .syntaxv = RGBA(0x689d06ff), .syntaxc = RGBA(0x939393ff), - .syntaxd = RGBA(0xad80ffff), + .syntaxd = RGBA(0x9c73e6ff), .syntaxr = RGBA(0xc4753bff), - .line_numbers = RGBA(0xd0d0d0ff), + .line_numbers = RGBA(0x777777ff), }, .space_outliner = { .back = RGBA(0x28282800), .title = RGBA(0xffffffff), .text = RGBA(0xc3c3c3ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x454545ff), + .header = RGBA(0x282828b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .active = RGBA(0x3b5689ff), + .active = RGBA(0x334d80ff), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, .match = RGBA(0x337f334c), - .selected_highlight = RGBA(0x223a5bff), + .selected_highlight = RGBA(0x1d314dff), .selected_object = RGBA(0xe96a00ff), .active_object = RGBA(0xffaf29ff), .edited_object = RGBA(0x00806266), - .row_alternate = RGBA(0xffffff07), + .row_alternate = RGBA(0xffffff04), }, .space_node = { - .back = RGBA(0x1a1a1a00), + .back = RGBA(0x1d1d1d00), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x1d1d1db3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x42424200), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .list = RGBA(0x282828ff), + .list = RGBA(0x303030ff), .list_title = RGBA(0xffffffff), .list_text = RGBA(0xccccccff), .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .shade2 = RGBA(0x7f707064), - .grid = RGBA(0x3d3d3d00), - .wire = RGBA(0x232323ff), + .shade2 = RGBA(0x7f7f7f64), + .grid = RGBA(0x28282800), + .wire = RGBA(0x1a1a1aff), .select = RGBA(0xed5700ff), .active = RGBA(0xffffffff), - .edge_select = RGBA(0xffffffff), - .console_output = RGBA(0x1a1a1aff), + .edge_select = RGBA(0xffffffb3), + .console_output = RGBA(0x000000ff), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, .noodle_curving = 4, .grid_levels = 7, .dash_alpha = 0.5f, - .syntaxl = RGBA(0x3e3e3eff), - .syntaxs = RGBA(0x975b5bff), - .syntaxb = RGBA(0xccb83dff), - .syntaxn = RGBA(0xe64555ff), - .syntaxv = RGBA(0x66c4ffff), - .syntaxc = RGBA(0x426628b9), - .syntaxd = RGBA(0x749797ff), - .syntaxr = RGBA(0x808080ff), - .nodeclass_output = RGBA(0xb33641ff), - .nodeclass_filter = RGBA(0x584d80ff), - .nodeclass_vector = RGBA(0x9b80ffff), - .nodeclass_texture = RGBA(0xe68745ff), - .nodeclass_shader = RGBA(0x63c763ff), + .syntaxl = RGBA(0x303030ff), + .syntaxs = RGBA(0x973c3cff), + .syntaxb = RGBA(0xcccc00ff), + .syntaxn = RGBA(0xff3371ff), + .syntaxv = RGBA(0x12adffff), + .syntaxc = RGBA(0x3b660aff), + .syntaxd = RGBA(0x4c9797ff), + .syntaxr = RGBA(0x8d8d8dff), + .nodeclass_output = RGBA(0x4d0017ff), + .nodeclass_filter = RGBA(0x551a80ff), + .nodeclass_vector = RGBA(0x4d4dffff), + .nodeclass_texture = RGBA(0xe66800ff), + .nodeclass_shader = RGBA(0x24b524ff), .nodeclass_script = RGBA(0x084d4dff), .nodeclass_pattern = RGBA(0x6c696fff), .nodeclass_layout = RGBA(0x6c696fff), - .nodeclass_geometry = RGBA(0x00d7a4ff), - .nodeclass_attribute = RGBA(0x3f5980ff), - .movie = RGBA(0x1a1a1a7d), + .nodeclass_geometry = RGBA(0x00d6a3ff), + .nodeclass_attribute = RGBA(0x001566ff), + .movie = RGBA(0x0f0f0fcc), .gp_vertex_size = 3, .gp_vertex = RGBA(0x97979700), .gp_vertex_select = RGBA(0xff8500ff), }, .space_preferences = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x1d1d1dff), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .navigation_bar = RGBA(0x4b4b4bff), - .execution_buts = RGBA(0x4b4b4bff), + .navigation_bar = RGBA(0x303030ff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x42424200), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .vertex_size = 3, .outline_width = 1, .facedot_size = 4, }, .space_console = { - .back = RGBA(0x30303000), + .back = RGBA(0x1d1d1d00), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .console_output = RGBA(0x71a8ffff), .console_input = RGBA(0xf2f2f2ff), @@ -911,46 +917,51 @@ const bTheme U_theme_default = { .facedot_size = 4, }, .space_clip = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xeeeeeeff), .text = RGBA(0xa6a6a6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .list = RGBA(0x282828ff), - .list_text = RGBA(0x000000ff), + .list = RGBA(0x303030ff), + .list_title = RGBA(0xffffff00), + .list_text = RGBA(0xb8b8b8ff), .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .grid = RGBA(0x424242ff), - .strip = RGBA(0x0c0a0a80), + .grid = RGBA(0x181818ff), + .strip = RGBA(0xffffff80), .strip_select = RGBA(0xff8c00ff), - .cframe = RGBA(0x5680c2ff), - .time_scrub_background = RGBA(0x292929e6), - .time_marker_line = RGBA(0x00000060), - .time_marker_line_selected = RGBA(0xffffff60), + .cframe = RGBA(0x4772b3ff), + .time_scrub_background = RGBA(0x181818ff), + .time_marker_line = RGBA(0xffffff4d), + .time_marker_line_selected = RGBA(0xffffffb3), .handle_auto = RGBA(0x909000ff), .handle_align = RGBA(0x803060ff), + .handle_auto_clamped = RGBA(0x99403000), .handle_sel_auto = RGBA(0xf0ff40ff), .handle_sel_align = RGBA(0xf090a0ff), + .handle_sel_auto_clamped = RGBA(0xf0af9000), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, - .handle_vertex_select = RGBA(0xffff00ff), + .handle_vertex_select = RGBA(0xff8500ff), .handle_vertex_size = 5, - .marker = RGBA(0x7f7f00ff), + .marker = RGBA(0x808000ff), .act_marker = RGBA(0xffffffff), .sel_marker = RGBA(0xffff00ff), .dis_marker = RGBA(0x7f0000ff), @@ -963,25 +974,27 @@ const bTheme U_theme_default = { .metadatatext = RGBA(0xffffffff), }, .space_topbar = { - .back = RGBA(0x42424200), + .back = RGBA(0x18181800), .title = RGBA(0xffffffff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x232323ff), + .header = RGBA(0x181818b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .vertex_size = 3, .outline_width = 1, @@ -989,23 +1002,26 @@ const bTheme U_theme_default = { .gp_vertex_size = 3, }, .space_statusbar = { - .back = RGBA(0x2e2e2e00), + .back = RGBA(0x30303000), .title = RGBA(0xffffffff), .text = RGBA(0x838383ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x303030ff), - .header_text = RGBA(0xaaaaaaff), + .header = RGBA(0x181818b3), + .header_text = RGBA(0x888888ff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x353535ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), + .button_text = RGBA(0xcccccc00), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .vertex_size = 3, .outline_width = 1, @@ -1017,22 +1033,29 @@ const bTheme U_theme_default = { .title = RGBA(0xffffffff), .text = RGBA(0xc3c3c3ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x454545ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .list = RGBA(0x303030ff), + .list_title = RGBA(0xc3c3c3ff), + .list_text = RGBA(0xc3c3c3ff), + .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, + .hilite = RGBA(0x80808080), .active = RGBA(0x3b5689ff), .vertex_size = 3, .outline_width = 1, @@ -1042,12 +1065,7 @@ const bTheme U_theme_default = { .selected_object = RGBA(0xe96a00ff), .active_object = RGBA(0xffaf29ff), .edited_object = RGBA(0x00806266), - .row_alternate = RGBA(0xffffff07), - .list = RGBA(0x424242ff), - .list_title = RGBA(0xc3c3c3ff), - .list_text = RGBA(0xc3c3c3ff), - .list_text_hi = RGBA(0xffffff), - .hilite = RGBA(0x80808080), + .row_alternate = RGBA(0xffffff04), }, .tarm = { { diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index d39dc8bc406..e5b63e7286b 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 39 +#define BLENDER_FILE_SUBVERSION 40 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 9bd6280dbf4..68faa4c0672 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2080,17 +2080,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - - { + if (!MAIN_VERSION_ATLEAST(bmain, 300, 40)) { /* Update the `idnames` for renamed geometry and function nodes. */ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type != NTREE_GEOMETRY) { @@ -2100,7 +2090,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) version_geometry_nodes_set_position_node_offset(ntree); version_node_id(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "GeometryNodeLegacyVolumeToMesh"); } - /* Keep this block, even when empty. */ /* Add storage to viewer node. */ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { @@ -2154,4 +2143,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index df59cd3afa9..4234570af6c 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -58,10 +58,6 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) { #define USER_VERSION_ATLEAST(ver, subver) MAIN_VERSION_ATLEAST(userdef, ver, subver) - if (!USER_VERSION_ATLEAST(280, 20)) { - memcpy(btheme, &U_theme_default, sizeof(*btheme)); - } - #define FROM_DEFAULT_V4_UCHAR(member) copy_v4_v4_uchar(btheme->member, U_theme_default.member) if (!USER_VERSION_ATLEAST(280, 25)) { @@ -317,6 +313,10 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) btheme->space_node.grid_levels = 7; } + if (!USER_VERSION_ATLEAST(300, 40)) { + memcpy(btheme, &U_theme_default, sizeof(*btheme)); + } + /** * Versioning code until next subversion bump goes here. * @@ -936,6 +936,14 @@ void blo_do_versions_userdef(UserDef *userdef) } } + if (!USER_VERSION_ATLEAST(300, 40)) { + LISTBASE_FOREACH (uiStyle *, style, &userdef->uistyles) { + const int default_title_points = 11; /* UI_DEFAULT_TITLE_POINTS */ + style->paneltitle.points = default_title_points; + style->grouplabel.points = default_title_points; + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 0f290d4255b..39d70f15a3a 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -247,7 +247,7 @@ enum { #define UI_DEFAULT_TEXT_POINTS 11 /* Larger size used for title text. */ -#define UI_DEFAULT_TITLE_POINTS 12 +#define UI_DEFAULT_TITLE_POINTS 11 #define UI_PANEL_WIDTH 340 #define UI_COMPACT_PANEL_WIDTH 160 diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index ad7c6332ee9..aece2e58f1e 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -252,10 +252,11 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_HEADER_ACTIVE: cp = ts->header; + const int factor = 5; /* Lighten the header color when editor is active. */ - header_active[0] = cp[0] > 245 ? cp[0] - 10 : cp[0] + 10; - header_active[1] = cp[1] > 245 ? cp[1] - 10 : cp[1] + 10; - header_active[2] = cp[2] > 245 ? cp[2] - 10 : cp[2] + 10; + header_active[0] = cp[0] > 245 ? cp[0] - factor : cp[0] + factor; + header_active[1] = cp[1] > 245 ? cp[1] - factor : cp[1] + factor; + header_active[2] = cp[2] > 245 ? cp[2] - factor : cp[2] + factor; header_active[3] = cp[3]; cp = header_active; break; From 7b9e3534cfd1dd4bf3aefb98407f2e42a6efac4a Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 27 Oct 2021 18:38:22 +0200 Subject: [PATCH 1310/1500] Blender 3.1 bcon1 - alpha Bump the version number for the new release cycle. --- doc/doxygen/Doxyfile | 2 +- source/blender/blenkernel/BKE_blender_version.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 96eb30a852e..89954d8a155 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = V3.0 +PROJECT_NUMBER = V3.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index e5b63e7286b..6fc2fa37d9f 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,7 +31,7 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 300 +#define BLENDER_VERSION 301 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 40 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file From f0a37dc31caa1920441b98dc56b9e56dfdb8c45d Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 27 Oct 2021 18:39:39 +0200 Subject: [PATCH 1311/1500] Revert "Blender 3.1 bcon1 - alpha" This reverts commit 7b9e3534cfd1dd4bf3aefb98407f2e42a6efac4a. This was supposed to go to master, not the 3.0 branch. --- doc/doxygen/Doxyfile | 2 +- source/blender/blenkernel/BKE_blender_version.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 89954d8a155..96eb30a852e 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = V3.1 +PROJECT_NUMBER = V3.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 6fc2fa37d9f..e5b63e7286b 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,7 +31,7 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 301 +#define BLENDER_VERSION 300 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 0 +#define BLENDER_FILE_SUBVERSION 40 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file From dab3591588e9ad1c971f1055556fbb23635ba4bb Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 27 Oct 2021 18:38:22 +0200 Subject: [PATCH 1312/1500] Blender 3.1 bcon1 - alpha Bump the version number for the new release cycle. --- doc/doxygen/Doxyfile | 2 +- source/blender/blenkernel/BKE_blender_version.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 96eb30a852e..89954d8a155 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = V3.0 +PROJECT_NUMBER = V3.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index e5b63e7286b..6fc2fa37d9f 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,7 +31,7 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 300 +#define BLENDER_VERSION 301 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 40 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file From f7a3450e63d129b680aa5fd8330d54c17efdf495 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 27 Oct 2021 18:41:31 +0200 Subject: [PATCH 1313/1500] Blender 3.0 bcon3 (beta) --- source/blender/blenkernel/BKE_blender_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index e5b63e7286b..115de3334b2 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -35,7 +35,7 @@ extern "C" { /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE alpha +#define BLENDER_VERSION_CYCLE beta /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION From 78c1c719880cc97fc2719e6a7f67509459cc9e6c Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 27 Oct 2021 18:42:24 +0200 Subject: [PATCH 1314/1500] 3.0 splashscreen Credit: Blender Animation Studio --- release/datafiles/splash.png | Bin 737984 -> 609962 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png index babb3e30c6dfab87cf58ec0aedf9f083b2d03966..74e239b0f98b148fd0ce6c5bb4e9e9e0b304aa9d 100644 GIT binary patch literal 609962 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

jE>xI?aw4V^r=NBf-+ri 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Π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�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%0Aeq<^o9z0ctzd9y}RRC9kAmu$N}8K4)wQ~ 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 literal 737984 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

hr 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Öz|El4;`>h< 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æ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

3jb?hq(q7bYC%CwuNBO_w1c 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

RS54&kc0_{+QJ$~ouKG`8sxz_Z6N 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

*iR|h0Xc%e2~!#n zh%v?yXQ{v@>wKS=8AFUXm!%y}iZ3!25lnHybvoeZk00UVkZ?MGg)i4Lq6+3Lh@$xH z_8x!rPy8SFwV(T`A2tkzJ$wNC-j6=VfAPQmNxXUY5mHnf65)P%z(9;FmJ3w~vN9G? zYa&p^SqX{lU^M4~6ii89J+DRlWs`Grt_0Fw#z%((-mD8A z@`^}`bI}F`d{`gx)w{27nooXMFTN#Z48svuhgX|*+@mLT!Bqi{+?*=Z>S#o}3~g60 zSfq5;U%RoDcDSPkWD0el^(?u#l26rY&(y$>MYn^QIFt;X#r{l+pMmi=%BT1@^jo;m zV0BJ@qyI5x{0;gs{wDtnpOQryT0ggc@4&H*4S{XNNFg*EJToIu+H&O;45_dN9&jNW@@n9A=9(OaT7*cMtdr_cJC0Ce03ZNKL_t)7ck7B%o>A1O+NU*RR%+^wbbz0=lr|8N!xC3r zy^czKacaHqF%GCWMRG8xcFrkw0AFOX@9WQ(igyBg4>;;D3v&}`7!WyNAX_#+7R5s@ zI7>$03BkJ?EYPD4GL2yG)oz4kzjm}DDlsmK_E7=|0!0ilL4+=hs}ELbIDEd@(iPzaz9k$7m$0Ss8%#X5V>xQPx|evO_b zLrV7CwmAV}OgP49a6IQJ7$Zk9q!Ck$7-DQSfB`_r1?n)k%HAlNk)>cQ1z&yn8eif| z{KNfojTd~4@i?G>FvQ>$e;)ty&;K9a{V{+O0N}&p&qH~;4s2xR&6?aS%>9t8@3*=3 zr|6y1-7yDja4ntU_Po5P&wI~((YQzLKHM+J`UL=BN}pqz(oX4b=5`9`f=b?oK+W>s z7@b{z)i(HRGw;;pg?9f(mkHZiH`vkhFX3?K(~9%@lYhW#8vqO-T;u9$^azZ@hPrpTe_>r;LPSEE&iDf*qp77m}nO3I{ayE<>(w-Qel+{joRMY0f@X+gqzbzTkI=z z+3an>ercc9hB&Z4S(k#x`49ZRevB8_$NfOtS@OxNR&^JA!>(+Qnm_1$0K%TAuNMUQ zr$E5^v-u)iBj3OtyHwTn83zUFb00N*qKb3pFxU;Yx-G=SpKsCL(vL2pvJK|ax8v#M zkbVhp+z;E_{rxQ)UL675^Tyi(X!qFrs<;atqC5wxtStuaAlvnzf1|(sIcEf(x+dyt z@fUyTw}dF*qpOz)PFG|O*2hhRkrNUz22s3VMo9}J8 z4;K%@_-ZMLumNNWcH#bNRh$vA6g$Yq5E>cx9Wo9uHS#79m=q6L@fsNgir2d0?eZ31 z+3z|S@9q%=6uHM^Bvxt-k?D7RnGV;*9-o&&j=qzhQ@^a=Jzpv|GU6? z#{9!CKvy3lu;3>P@Hf8@{8x7opKC!1ro>mUQ$4KK%W84^W#ZUi8Y;E~w8MOCOSZ^n z<1_%D-Ne}y*uW8i$ClTzH+E^coY;nYYE)L~iIskMP$hi-m%fKz`L$m*YB~r(8Q=e< zU%H+1lBeoZLS|Zm|Fb+5UyC@-2mghtpe(X+R97V2?2(7xiHa)=&@*xS6hSfBz*! zP8RbhjE{yH|Lkx4pZH6^_+$G(`*okc&Cs8SEqV;XkN@Y#pWuJ^|Nb0a-yg7+1%n^v zv$f0{y>00aXlXuwj!KF$_p*ebB*&%p(DR^!BHCGv9-VFh9&#zrM#u_&NMOeu$gT zJ^=?tq=1i(FYxW-hd2%iS3|_@K$uF#-8tjDuDD-TJj@I3%7PhYfoCSXIo%_RXX#=z z_lHL}Zm*8`&hd!zJYy*tZyrwoOznP9wPg;B6)=Sfi6a67Ll}TCAjXJKhKMvq%wq4Q zhh;$lVU>)N2p|QIS@5_#;Pu1TSn~`8;qAlEcGu0r^r~h3t`09T4p-hkZ@+&JBB~o; zX6pjy4lt6dRrdlqTvR$`$9vL=k#z;r*rFutv6o!dW{%fxc$BIn*Zj9`BgG8C7hwFG z`Vsye`3_zo0qY8UB@lcUz#|wx13r!bF@8vA{9E+<_%Fjdd@dH7s0u-#;Ma*QW>f2P zI0XNl*%U_J`d`f+Ui@_{`oSwX%?QMbf02ZRva*+iHN z;oWj-HrT{hAT4gQuZtD{A$f1|HgrbblA6}i!6@Znv%bO>p+ElOjDPX3{YCuNU;nH0 zPygm$z=t2b+A01&`q7W^fB#>5G^s%m0~<)Q^r3W)5p&K{j8P~@Wc zY>fEv>o4)PY%s`nknR9%Y7y)6X2(A08fXe|L|+ z_d7qt-~ONfj{fKW^?!()@HwXAfH4LK%h@~cXLmovfA$~$NA#P2`Jcky{eOQO|H41_ zuV}hj+S2lA^N_;&!pEM8L|3}J|Qi0Ik9+nqn!!l1g=&Xxd7i^tnQ|C14Jb&4kt~~Ef<4qTa zf*lEYuC_7|H4~0F>QFxP;rEEap}}oVV5jO0CdMZj8yn-Pr~Ym3yYE4s`1}1-f`!z2 zN(JDX$s5Gdsm+Wwc>+oivf7sCOJl#+0y0gv=egkDZbO&NG?6$3yM@V>O#iu%)oyP# zhAv`zu^615+>j)SJ@;6_b>F*M3=H=3jjw;teD!PpLtk$-&tPu@!t}IyOB87Koin%T z5?q^(p{-E3>uynY+cLD3cH9&`WAk5Yz~bE5OpEr~mS(A`Cs3cZMYP=(c0Y&j)LXE zbiFZ!7d^fE@SlJu=09{$3hRP6ZnrmS5A4CdE$J$iX=R#z2cQaNKwzMRVcgCY$UKi% zm-|IEkW6SnzsMN!1)=9Nn%b`SRH~@Q8AeJ5d`ZQZY)J)11Wal}S!7ILe{an|OBj43 zh=3jmK86h{V^U`*SLly6Mwm()XzAn2fJ~IA1ZCp11wuJ#oTlQ3XfhVenajL+b9wBE zzf<;nvMgn$EaBlXBz-DU5=cX&GJ1WVdK^*leS}wH{nP|nLW)OB%1GKEQ7J1Mqbv)X zynQHRQO!lmC{4KY0tVjsA$s;X1T-`ry`Pb5KTh=Y^H7C*7%2B_OW~&Ekg5nNf`TT=dj7z-P`R8MuN=MIFCbW~&sk~h z0DpF|oxlBDmakZ9i#=@*M*AJO4~HFh1heMNAWIETKlUV#{O%!I6AgUd1EJZpVGHY? zU&}H_-|(sJ#;UmoetYyU)r9#{<%AGDyVqUSvd@;^G#T{46~qjn5rj;S}{{;OfyQEOjGKaqONQfd}3?_U%k~N!4L=!?fmYZ4Dfu91--pg zF|?8vk%EaNB1sbxWk^sKB&Ra#mlqzPl&*_!8%pJ%#9*aL;0Y&*SJab;sklW`D=MB( zoW_hc$7m%nV3=&$>Ci_%auuKW^hJc_fdBpR?VR)WHv&+t3^|H@K(#tR=vSQ;py(%* zUGgXC^1VAUuy&WebD-BIa~)kTr`mQ>C)&lYJG2zG@H|u5bf)X3?c{X3PzJ0;bGqs0 zi)wpz6je|PHmB$%yOLh_2J{g-K?eSUbj3K9p-z!1r<7VNIO=n-M!YiJ)jdsr*&o}^2zeuyVSSIjB5&dMB|Y2TX^AJ5t+ za?Xg0B0_`8T(i`6WQ~Ig`cjaIl1o~#U-z8q0pVnz5{!|Z#%f`8mu~uHlQ@JCG9Xo! z#I98fOh%R_)GB4ZaqVZteh2L34$<8)TA~nV<4M8u&#mHv@B6Y@yXIwrvTY(LEu5d! z5#^mS10ksZdq9N0l>;h)&whLF$H?YQytwiu(j+3y5?7=PrzR1UgAjwJ;(2JD<-X#Q z42V?bV0&P6YAt9;5}oETf(pUxo?fO#HZYPU#ExL!o}jCOC%NtC7p}?K&ZL)4Nnw52 zsv~}MjD-%HpioK(fkv|P%$bClCQf1$nk37JRcd3Zyb#|D2?cnb&54e(ggA{I??s@6 zps!q};`tURnk7zIW7ng;N)K6-Futjdl$z3va_%DsNf7vyWjl%y2Fev=rioR8Qr6mG z(4;D3V)GOqy8Of9tnveESL)6MR?C3+e z>Brv@S(f2>9^d-Lcg<(Ma3uph{RL2&>GJ#{I(vAx?3cW~gyZ?-Qs!LcB0BL(1;Mc? z+`~COEgwQm*96lgVJ;%B+Ux%K)GAi4S}iK2G60)4kFa9JGBeP(pui-Mc}6qs(k|zX z6k%+p>#W~3J1oR7YNx2nBhuWz+v{jsmC%0t_8~EU1fv+AT*cb8>qY4M)3AnJ+7TD| z<>=c|vTD_8zIDwF=7yVYWX8Zu+AU()=Asl>Np0>D)25;_C;N*WW?Ce~x2<1om~mE! z=`?NSh*N=?w%W;9D{FUN`1As)&9?CJG>-f^Y&D$~Ox9O&=dXS%_CH`BwAKVc!0_-w z(_0&GD$XK3VS0V$TCSoKBUp?cG{x-m!VruC=x?j?umgd}ZwoW6l>JYs6M|`|graby z0I%*IBNuuDqSI{ZmcP6APqdp~9Zo_~{Zb^ccS+mZQ}yZ7{G!L*uvPwf=dw11re5cj zx1ud>WiL}L_f31*9i3EH??ZGyN3#v^9))JaHvY%fZ?GxK1Ev}N9gI_ME3c;TPjTFe zH)>-HHNS#~CB%+p5mJUIV+g~L(AW!9Ac;*%Ehw{~)X(CQFIqaIWE3wpViwgrO44U@ z5;G$#S$|WOF_vjU;n5R#BwElk0ToZMA;^TeL25-+S{`nKVbV=gHm6OruoJP3 z(I`pM1X;%OOK2(3DkI8Ld@1NF^-u}R4ln^qNJ1gmVc~YPCa2gmHbylwZ0C6x1U=Fa z>x_~xv@%WAx6{~AdTdD|w2`C+Mv{ni=_Gw#!dY_+i%XgHm8A*6f&qrk`zV!t51!6( zKnQ65^l|w^2c4`v%6)OuY_aaP2#V=6{ zs`k*GC1gfX6N0`4QR|RhE;v!4NtU zwPiWjxSd99vx%h;lzk6hdW^N|%#ugCLIykuFF*YMKf395&V6U6P!~cH_$6x9K|;R@+WC;%dDGH!)(fvN;nqke z1-C_Jdy;AoFP>*7OtMX=mA>N-hkVup;tk#NG(Ze_Nj_gN| zT?;lQXm%pK_pRuocWP%Kb+{;i|G$63JL_k5qCUF<0)E99=g5`Rg=U5$aC^cB>8vlp z%8iZDcz%#K`8t{3?ZQPBph4P69~BbMDu>+U#dE3`defPLsYJ2A#>T|*Z0zQ;;t0Zob0B%xyjmDUtkSakT4%#lu!ch=_zwdZv}zI zb9Mra1~m)mB=9O2vb@>SZm!w|Q09|!nfo3+ zXFD<&lLFR`M9!bEM=j5uo9Qm&K(~ch0)?03LUfL+5I;;d2|7Cy1W|UaPM`X7T zolz+da`%1Li{9Q^2kMl^X?9N%v?KjXr(eJ1;w#Oc9(V-Lmt@K|)CJP|=!J9#Z$V#K zB7+jIIreDQtzE~%Pd$NdHHb62H+ja|D=L1O3`N!R@!juY8a`b;fi?@2zMvpT6vJ_T6tU z@!$i0HDA8^^Hj?-yRLv@6r65b1PV{T_}*<*ly?<(#Ri1vs9W@O@?MvmVjHprk;fN` zLXQMM9J%}ytyD@SsM^97rI}EgeqWHfDUnwSNfJo4^=f)9GAERRrh?5zQ;TE9eL2 zrtZH3DLiV2y%qvL|12l&x|z{$ok=pe9kNtI8~DYD=ISkyva;~5_JZ!ajN?qjPG)P| zgaAix_wZ$C$;N7#*`8<}xEpFE5X$yk(q^}*>+39}S!md&+vwZhmm^o_>QbL-f zEZlw}uYdFF*>UIXb2VS95%J{T{=wXNbJ>2$!n}A@amFKmc$h~Ye3+>y!Ve@_s(9|% z7kTN$mk5HAouq`$Tkf)7OMXb2#-v%APlKfJ2z)z56&QT!BYh9)Jj2bXg;I)YrH438 zEEot$7zUL65+$!hC9I&LCcQ1q61!D!Y`jk0|9X5PHdBh2qf$uyV@!X;2S<`KLZ z`{g&09(#iHUsn+fSG)b*8Tg@dVCV$+k$p1lHB&h-aNKyHlZ&;__h&| zD_A@xqZU13KvP@4r7py6c^DFH2!%lj&r;x%2rUeyAVe#hg5dc+tyYtAsbmlIN@1iR zj$+ECkXl$ptAsc;4E6Ld+MHx$ay`DV*Lri!D6cv4crL#AYKG?w2*5E%h-+Le3a(jBw;ZXK?#HH&UruaA6#4uD$Mm*|=enOD=gkgM+nv zxA^oYzrvN5e;7fTTCE3R1m$uMwbGzV%dy^czYs;(mVKu>vvf~m<>1Q6S559Wlm)4y zlkPc)W?P2Yp|dNuC)-0psuEWe!Gth`l=zmKjHy|_kFw3>ms`7LB`-K%rpOCWht z@$6iW-R2bv^~~UZ^$!FezYQua=nGItzU8gwBDs!PXv!`gNH`9L6duBpE^fuLIy~X! zl5C7bc(!qxZ|u@~!L-qZgH!=LrENr}(KZ%MxyXS@1>x?QpZBLO1X&a=&J%hRZ zz-U9FGt$gL-L%R`-TtbIVpvf;>b310Ca16W)q0s(sZte!IEjfJuvD20>6cN)g7V6q zB+D{Vm6B!|sj)fcUJ%$l&kOO~JrO4{zVA`;OGqI|Cg86x-zSW)jg(*C_G>;Fc*C1sN2yfG3zjrZ*>1rSGu$(b5Q0WM=CspZ%U6m8`VVgALzjHi z4D<}=Dm`R7zNouC5nE0DI;U5OEcjYYzNStWEQ@WWwslE(+mBF``gZ2-I&UA+C6At{ zuV!Rqi%?m2fj&5}F!$l+_jfL>migWx{!4gN`gvck zX!1mTijmDP<^{TxlFxna3KO`qsxgv*fj$NX2E>Y8m$72^ELnX2c?8e_UkM$FSa(E&ZU=1TfQ@sk!u96e#c8@;)20O&Pc-6tp=&wWt{CBB^yZi2hqp zK+(P}iqdqtVViCJiQdX#-}&ZN)a3Nj-o&@R^A(Y0w)T6=>F1c=-TMbBm9UFD-~wrE z#NsyIv*}8NtrYjgTWc$uj>u*EW?CWNP2V#GxSZMgmrC%Psg(zlwycNAWGIu-h@q9m z#A!lSt`R9kZ@EI?=+B-`d8RSJuGJwnWQrlr%KBbT8kCJ;T{FTcK{c@6-@1?t_#SvR zi+aKcLSZviePO4_k+O54YL+r46fI?lN!i?*MBtICn91fiwNgLLw22mqGy%^qA<+oW zqwJSxKADC(001BWNkl7Wx`79_oIl0nI{`%*P-SlaaO$kUrID8R< z=UhSW32()C(0Ae)^d0pk#%{Tm(Q7Y4`n&L^nIXpvC;V`;$8{sH(Dx}xd$>xq^`ez! zNM;m<#4bw45c<_zUSuVXcIj?=J7i&VknI_cBl8N^t?`79G8Q_MWr~vTvuyX}EZA{7 zj4`BH#K6!nZ$0&7cHei8T*Vi+68?1GA9?7$zaoU8EKAJaem(~scPKk7T?nDiLB}7= z?g#C~AMd`ONB;6CQV4clzL@z7=km%6YuK=U18J5ZJ)5N+N*}FLeBT~EGbizs(j&`K z0zaVSCa`U4d8!gJl~M}JZW?Es6`qsyRsAZ$XjB~G2@Apry^ylsLp^zt3l91_yU!c7 zKKK;;`A|9b=lBaw&DTmj{sgVx?@jzKdoMNb`zhg4s2+A7%svu&ccyak1my#MNAuoe zNMD5hissydALZ~Ref;lZyRl(vW|yokRw>dG1fEA|tlZK#GH|LAgdb4#46(A4!!&I& zRO@qj)e7VJPMQi{P{J2>YSvbh^woNZ(-dJ0)u2o(o#2(tFVRY4EB#qF!9g>2<%9qF z6$_W`=q6pZFu(q?Z}P7{J;8Bj9>-Tz{b`T9s&KOevFj4&s7^iLs>-z7-B6b>C{0rB(YZ1(~OemVRXtswMKtAL;{U0 zqmiU&m5~UK)M$*d@pSY1`x)}B?I8i0sf|-gjb{DC7DgK*?tm#6uWtf|dG6(P?6AXJ znK zxK13`sZ<82mHOR5uHD&|xsTctc7iBnd)twDbW}$S-I)a7tPWlq(kSd zo=VAjt+-Op<3i?9HUb(cOjSs)I-Tso!@+l*Tm}9EDZy`38p`lLudXrtES$0w_M2-7 z_}NE~BzyNE1fTj9{lr5y~ZJ;80q5@rdW@8yR|A)NK5y)Nfz z2IK^H?Ha6jWiT;D1V}7L#FvIf+VM|HS+dPYV+j0`BmOG_zlSuNvg;%Uja3=g^ScJ) zBRyM$Dw8>hEwm!V1@AkJIdf*BG7Bz!=9w2b^wo!(zVdcd`>e-Y3ZGSLR&&qq9^u0u z{gS!t()V$}2hJAKmu%Rukw94ak(3_BNPMhsR$}8WtG>Y)k2Fn@(lQ5>vJI)g^Uy}( z%Yd>M;`xH6nxdpj`0iR!TIUKBuPuYsRtsQ^<$1&o(y6u092X+AMS|y34nwMurh&<) za|;J&;=9BTj3E=CmFw2Fvzs04uRYGmy_3Q!@!H=1_9htw0ilp2N>eMB5NS&2f$>8- z*EeoWFd~0V+GHfI=<lX98x}5`Yu5&4L6>v4o5K`! zdgvwagZ#YL8K!(gLumV3{$~CB|tQD`zB`W9fvE;iMetfRhkwm+L|?V=Lf zFlHfh+n6vN3|kSZX}Vwuy1>`pX871N(}LJ$*x%O7JdIH8Mn|{(VO#!XIbDrWkYo!p zJzULnB93!N%QOjy?&z}3wAaBjdA6Ar99gs&n08?*x)mv1)9-@BU3S?Y3R7rNV6q#h zgejzw7||^!-%2WPifJHX>t=%~aPW<>am>0)W;3nPmazQ}+uK2vF({>2v}hrk1Y?5k zs6cm{Sh~C^umL*FqjtLny0hU;F?vu>N9K1ri^dc`=xyA4#U@8V_TITv1yZS$R40tJ zCT-VQWf);-rIW2_pFC|JjqbXU{p%o`KMZ1F`Bk$wi-;f#+YhNpiIhG za}!ZIL7FzK)G*5ky};&!wm}OjBgqmrO^uSKEz)?3B#TJ1l&Y|x86`a?GtFqGs3#dy zX+}Lu7*7qOnPI5kaN>-09NjyLUmr(Jy+r@9$FSgMe`es+cS0#ZXlOk6Th_kqD7Jj- z8<=D!vNnLM^pQ3C*>u%sS$X<08u#C6nwUAnaZpW zEz>O6VLpc+cNhzIT!2;@DFR+|%E?@M`6t(#(GE+Qw_XB+C6IXEm-S=Bgj#okl zlC|qLar+Io^0WW>8S7VVKnSSyRypC#C-Uw~-ot{$JCMdPm0FcO_gT&X2knbruGncs zrpUC7CySHVNhfV=hcODR1yP!yR7NNOlaeGYR|H5>qe(M`7uFc44l-+SKK+#;0wM5K z3dYK2t9~CT8GCK_3qF7B@7ZnssD-^%p!&wk=zE93pMR=d0GlsU|D|B`t9uhYJc260HbjK$Nzql&dJ!B6N_h5Ra-9gi_{3uz)d*1(kHg0;EMwBv@HCb6-M?Gzjy3N*Lr`1UtuDbkU zYPH$as&nY;ok4%!4*c@x-(}W7g-bs2P14j*t%m&K*VpsX>Wwtx?U-t8MjJyT8e?>F z9ZB4@i>O;msN5p2+!Qk=qjPXHZ3(N3u+rKB^Hv$dk-Cp{ zdM`ezwxgU1*pQK(dOWSsuaSND2-=x^{=+Gy%bEoLmq20=fglht5yAH!hs*AVr#Cw& zOm9ef%^QhdxSDMDy>^?QcIqNsFYpY2{f^XJw0B3O8c*6ESJs&*CHrDy0 zIE`=bF`vhI4#9SKq8H)KBbB|3U33N$Vqf&_Phx<|vCCLyHnPW6_fR#f(2TJ|mht(~ zDK6bIiI>GhZT5%TM;mEG>e3bxmlqvtO)E>uvX-?zv`PKa1AJ1QmfKV-i> zPBEXp^eXf2cYVV2%`c&J?BYzzcwPyg5N!-4&pvOXGh2)%shxUZVWqzDsL7C$=i_-A zA3<3L_$XRgM3SZ$tsP9%HcMiykp|)@!(QR#@YS4;f;zT>Jo5&#TK9-}p0EXvund=SU}34o zC>PCXi*sG@0cQn~cgLb9sgx>&C7-Yq5|jc;l@hhyDzkcL5i-b+e(+Nx3MmqccG-!h zY2}Zp#I{gOL8r)&-OP1?K`TR&Bs7}|X{K^}PciJu81m0WyCN`4QWsBW3c2EjEXjyl z30Z8{Fkkvq!)jM@_}0X2H$m5iILe5k6qWslVCkZ7)=gO3*N7>`cNvXR5H~ZjB(uH0 zcIJ5-dTYA#*R@fkX_im0i(+VPG*Oh~=L!pE6w_(}lQ#<7G-;a?*@6BV1ARRV4D>QQ z*w4(tVP+2Q!0^CKKK6-^m^0scp7A`HTO0P9f9bT}Vz$l9Vjdg~o+FS7n(Zieg%EN}6e#{!lne z7~>jV?Z(VBEWL0OYEMcBU+!F!LUs^~?Kiw#u*+$r`01g8xmHBF-yx=Dv9{KJ- z`IT#EW}1o-%nW>n!w_EzdICwT6dNLil!Dj@hK7b&y6bX!!w{uXv{FoI&E_;_Z5lI? zrEJI|#*(^?!$FfO`}{D5R3%pOEPN6b=k!X6Z7}#k5_mpk>9e3ZL|KMt;}K~^Rd@{h z0W(Tfj5e%KTa0BbHpdZT#xRl@JYRA2oJmgUTS-s6o@8u2rR58mf789p{K%Eao?ZZ= zfBcQL7rc(mSAGuFDj};igdf-gn(#n+h-wWrS!VsE?`HM8jwOETaXa}O9A@?>uVIHD zJwRpGg{XRi?L5ubw}-2Cu*}LytI+J@(uU-}A__jQw7w zODK(#%S$Wo4Sd$DTFVcw`2n|o|Nj`qOBM7(pwpzzjv6PuorE7l2!{}D?}D! zOiE}V2&?S2@EI;U=BFIL^DFs1QF--&^j*-PwCuBX8%xJ%J-9bpu3AL&s7vS&pfg)^ z>%?s=5~8PJ;+j2Z-Lo@9>#U4(r}s1P-h|o-r{^HrLl&;%s@L7i;Y(hjr43n@QSyBn zO5^z4~BQHgC*=*&)?l(I!S#$68qu{)3mt*wg0<%Lc}ldK+H$z;+% zWr}#iI9Gh;Gu);B&0fbIgaIbjk8s_^pXb8mZ)DYrFS7lLdDJ|gdA+^X?xHj;tw@yS zg-8Cuec!#AmmhuFCJT9jGcW%Lk8B*{phf%g{KPtp@Yxbiv3hDFQ5un^2|?g97~0I` z6pb$=C28BU^)#lHDdHr;lRhQSlHa4$=An;85%oAi8xJJ}nKmr!?I+1Hrm~C-!-lCb zo>=z+Pp*G~NJUIF#u;mjaQ7X5L>a?@2k(sp9=!iiNf2sCX z864P&Z(jXHdP$aWH9o|>RchA;d)C%Qc`kWLXJ@mMD`GfagC^{)I_?aW;5!={*QhZV%n ze2Mgi)1c3Kp#5hQr7M~QHz&xJfne(nc_?8^3%>pceC;7vIS!r%y~6P}T;_b9OPY7z1>lhh zm>s3OeZvSxOf(6MMw@l4U@2!jyOG_@HxYdSre%0?&(HGhxxM^}KVX=Lo7v{EY(hwc zH$)=qjJ$DAroc z+#&4ws=XK*7-nMin6+y8RmX6UG==q<2cC}}joE+Sy;-r-VwxijyQfAOqpLTt{le`y z=*WZE|KNS7mikyTwmL8BiXf-Xo#f?3+?KH>TgE2YWtSx!e#9Xhe$+v1zvDu-tZI;r z#RLvS=X<_QPZ3t>(^7?IOV+Bt_;(fgmGTj@GQ@7{5OqF2}9*FBF>y6-({>jwNCD-x5hBGt-97Z zKf@T&sr8JY;+0wX@~SSMdYZ=F;ZfVHC{#X~GET9efvI}LL}M+UuCf0C`*F%CC$Za# z6-+h9nW(RE3c`X-+9^O(oHErA9B|M9Y-vo|W{iQ&^)+m6)H&c)`}3OD9nYbMAHwEF z#QMoKHkLF`DQ`DgI}O*U5woSfin%l9a>UVxa{LKLvv9}l8J}836s7+kcY6U!<(eQ{ z#y2rKKE=+vEat@59>u{2AIL!OAe*8|Wd3AHwij*qa<{(I5aKZ8cyzPA&T z5o6;oGkeBdjyU`fOj5So9`x1<;4ZJge|gq!EipO|hjX?Z$#DOC?)vSWTzBpFb2z9{ zic3E70nv!p<<^~c^r&>#_0(jG(WzA|*+ABVd?!p2Ch8N6*Viz2 z?mP}Z`Vfvk?g+NuaYq{UwM^DW3$I`O=dMfs71hN7kgkDJua7ZRU&+jwb9nWuU&Se> z9LI_k%V;z-Q}tJfT1l5++!SISTdf#rdU@4>`_O1^w)cWE)SK&QM2f=>KZMtwd^}4Q zFQMLAPop)yRSTj0F~@OA9AzAI@By@FUd7)S?w_V5glHbMnb2a_sR(BTPSy)&`6#v_;p{zhhm{TGME4pcQRm`O>Al?sX?{ z=wXKt&`&eknBOnO7LzcR_tcCwvd@0|Ff=rqMpU>STY45qoBil5nWcM z?s#)qNTd;4$>800O3_*5C``+zhex!VzD9J>;QdskK!(y1EfPFW5G4r)Lns3<*56$A zt^ZMDh;>5E3kf|*fI+B~wHr24!9v-3^4x8oNEzx%F&G3?q{MiF-S^lTJvbBd^s|IQ zFlrP{BXVy;6B}vPOzK2wOq^vX1X2Vjkz(-hMH>uYiO3i$|LO?^73sO@M zco(Bzy@<3~=HR{xdzGj7#e~oGBQ}#g^h6E>lESJG%DyGs1}@r5X-n?LWuCUZKfZ8M zcVoNRO4-=1J@#J4y!ms{#t=s>c3iv@Z+PpQSg`%v+-Ls6voCSyt-oab>UD%+KoHug zO{Ozgoe3!>;bn1LqhAc~v zUN3WJ)_L9XJ6JrUg-Y$Yv9vpskNFo~-?ID$YyIOwqThX!tlq6v$^kYGnrbrFIrFRt zto6|Iv>uyJ^|04dIp9`|3<&rC3E^&6)BM9;M2~K!;=vhv{fk#GdYMhyWvWD`YM(_9i{y8+dt&COTJCEsEogC$a)_ZwcJKkrU9y1w83Ww-p+S_C&)&Vk|6BSn7V&3z#zQS8t+#Lhis>QPKJ^muyn8eSS*YbUy- zOf)8V?^z$?>Knd@=i3eNm6zAE%T6Z)kJ3|{SAe2)JWtY1Hl&+I4Gs2!>Erx!-_H5x z4)FU2|Bpis-JSjR-<{YPzW?2yasK(IBdO8bV;G%WN2yd}ur|~=8kmj@OS^3FcIRC? zI9Ny6*6}Lk*}xbw1F4X#H;Nn0BF@$`?9<jiB-VrWKcwq}%`B&I~KAf^M43S#V(CeVQdTV>-E_oeu(!g(Mtt z`{N9rvR5wCd+}pG;ohs>$_Nz#<~iJ%*vfV2aCm59kc(;Qzth@lE;T+xb;}pgLm@MM z`+kNtp2PY%?_s0)A)cAf*^MU0wqma9uW?TxSs;sSXbT`MOF`&c>33oT^)$`j3!~lZ z6m4jg*XjCeWhvgGT}ArZ1JCz+hAcO<~ZaO_bBa@}`-#&&c2>~*U$gcl-Y2^(Rdok~I@1K)No zrDuyd0X@|!cl_>pQ7VO`sp4&?eawt)T#wcXMj9kOo)k1THu?6?FBiM-y9^-&Ke_G} z^OI|D0pEf_TkBJ<`uP{czOUK~Aq3Zb{RVUGXTQ%G7rcpgeBewmG(4DlBZZLs>+k>K znk%j~&;0XwDy0fhrU*ix%$KxQx47cx*N8)oIus!UKl=9f%?+QrkrU26o_BokTroUr zMi*85?@#=b&t3W@bN|B+Gtg5jXdUeaAka)TB5J`f*IoN5anfs!qFgR_+X;}1X1C*SdLllFuttq|HZ zbqbzclRF|M_pqLAhh}{BK2}b^>wY05W#^>_ z7-;arn|>sYJK-3c{^I_-QNW$QyobA2^$ebYPn(8t~{cGzKI z*EDkVnst2aL!UEu-*?NlhpnbtYTRt5^!3in6xy@m^*i0 z)899JMoZo#?mR1Rn}w)Vf^=*+ogtrPL=E-M=F(4o!klx?SwgsYvn4w(X0WyxV-l;V zYESXCVPx_ZK6c4xc-z@;5wmB{=z5##^#(t=`BpxD>1WK${$ZpPG@B`JeDmx1>NhSI zLcsmM|1)nm^>j0Dc#ch)&?*O`8Dm$+@x}ztKlhXv8X9Ef%2n*W&#TN}@32#yX{MU% z`Q*nx&lzufvzResxa(DqkB{?%@BhSn;qp&XtIg}!uJ}(LcG{(xzN>+m z+ZyRG7yRTTmUH9A~d|$F@^-olK001BW zNklVp-RH`uwvma`K61n~BCIl5B)i-*h&YeflF}?%Y`& zpqmgp{P3fkbka$tTpH{=_kypz6-5XJx$e3zi<3`2!Cl{-pMU0G&vNnQ^akE>_Ivs0M=uh?Glsez>*beMa_&1XG*3SD zB$aZRW>n|gbI##YpSna;YgOBojHCJ5uIdSEeKJNf>F;`rEwfWAq*HS6>PUj(Z zfgy|ckTIfL?mj)VO-wUJ2+;+yG+jk|XCTo@V-p1^Kv5QN5K9*vq>ZELdcrtwmv22F zMp5!Ac+#i0RHDCXJ*I)M{-2$KnwV)IlOc)>508o`NYDtQn9zd6XpA;g{E(fhp`Bv) z50WU!OJid+vWyav$UsXgDh4*H7WFJS^?! zc0L65kfrK%#&5ok`t3Jk!d^sBb?rhq&pOeT#O`y=zPRVBhtw1vT2k%Z@YtM{_zZc;M)RC zbYztvC@hB%qTHyUjurJg?C zyvN@-aPhxL8=w*hXG7)0pW-ikGi}RVy7I>~?mi2(-tAqE;=YxpWqNF?=(d-L4CFA> zPW(E-;tMvm@8`f-Mhym_+>K+!aSd`N+|NM)4`02;KO4EzU zq(C`xyf&Jk?V`*ydmec(3zjandH2Fntfhd=?encTX2$&O*z?GP7??F=*BAk}UVkg! zJns_B3?CEv_(CvL9YD!48yj`XPCe-Y{_v+8=&Q{^NZL}tIc!; zX+UpvkV@EtLZP(HoNrTjZ7#edb@}ELJn1Gc&n($l8(59zJTt%*W=Doi0L>UCeA3I` zK=$EdtmOibQyTBr8UCNs&a}L!^mEtlma^buuc~6aPWnez{E!1c9lw+P<4b3?oJa^h z@(cX$-DhLje8EdH<|4V0Kl&|ZJ85s=1y*7$g!6YfPpR`(3n55V;wHCP3bCVTE3MgM zVv@Z^$FVNmgdR3<%HE~Nnkav{=a{uX+CxxdDg?-3e zjf|j~B#t_7_fxG?LcbTSQ%h)<5-CErzXnd}ByygxX_NYeExOuKUe*Z~S}>k+)S=S0^GhcAPR}YiHo8S5xK78rMzDyzSVxn2j4Y+BA?%IktmM&ajhodzf7_I?DHM_@+4V-$R`D z>+^MOO%qy~;=FHQ6}SHVE^+ALbE1g12)xKp)_GrFAN%cjlwPr78E(QsE9lK;x%h%# za_;$O3Kf{2`&YlXQ2*wVUyG*ZrXk{XMa6Gf7N36d8N2N#ctkDvYx!77Zk>p>A|UxgH$!nEm4&xaZ!RL@e%x zQIm35uCl2M|LEwjS8r%Z#mCD z_k!4Z+bu)7x#axwF3}G^^dyN?%pAc$(>0Lg)mNSro6VXLHf^bh6Al~V(&C%17O~SV z`{}0IG@X5YJo@mxV)kx3p~7Q))SMIb#~*)U5}UMN0rAsx_6~5#Z-2!(zdT(8L4a-B z?DNw@^jBYf4MH%G^O^V7vtr8B$raJXqUzX7Df#4+&)H*-**cwS4`H5?fx(r$@%lV5 zb=s8ZeNLXTz4lAlNQ&=y0hx3&FTV1SXm1~BVlws6^QZhADJL8}@x*hScG?L#Q!_TI z#RdlZxbC_u#0e)Hg$nPvQ%^oy|MhQIiBwo{M$A85KGzlt=D*K=`|qt&iPp$Qk;`}U zhd*4w8E5>TD8JT47hR^WxZ-!hGGL(UEN>wwl}d~rJxX7C$(5XQ?is@O{VEGd849>+ zRR>#aF;gd#BO(c@5Rl6k*m=k8d2sF>A{LMR!1Zme&l~>s4}JL+7n4r38k2%n*Z4`2h=nl&oC2 znk~1SrIU%44eSDp5Ay{LY^FAu zp*EHzZn-qtDH@!F0kmO2KLsHu`9@yiX@#$i_sTQq02*zJpVkjDJ+1MTPcYa+a{KM5 z|JhmCElq?&YkHr5kp8>=jP~mgjt$Z=(6yit!UBAR?V1jo^3kdkMkDBq<5XV0#xcq%(KxZi+R)&p)ULP+odAsEz zLx%b3Wh$_z725Y{YN}`Ytm)KbYS2off`DE2+?74|--oyvY5tZea>!-pFGA4TO=&|eihF? z@)SL5JCT-=xpypG$%^lnVM{Y~xy9TT{yG~rg zzMH>9$^pL!fq>*sP9(m?UFeXStopwo_n(;*zBbeFFlKY;K^726RU_X@8Qfx3C|_hn zt!|k@-I)lpK)y(#GtX6X?_iH3_rVVfrDdNj;_v7Gg%#f} zB2%A2`6cqcZy;PkVgo*gf@}wpnLpW{DKn;Ep~_e=d=-S;62V}g@B^QGsl?PRX0p|; zI}lHrQ~|%>^Y_y(;gv&j<>L9ps5kD|@yJzFmQ3mjkeiuC`SkWL;h5tu=kK@vmRQU}N?5dTDKkPZ ze{H(eJUQ(+HK|5iX`;cTkOZhuX`-VNE6NQO<)(o7y%w0LliZ=kvAr_QJJy!)_t_cm z&ck3tY6t-LmWT6v3H*w|h}gLPtt%<`&Gt#O93rRcVL7!zfEPe*0uG-BTegLZlLhjN zbvSb#M1Hpr+FTHVr(`$hI;(M{AZ@z_Ru(Safeym*Sw!!%3_B`Ec=-rM=JIT_ZXNZ0 zDO?nnvlWN4{hGV5N2HBZ?fY(Co47k4@-Z?j%!l{BWktC@R=E6FCPOVkFa#v3I4EX{ z*pEXenSZ>2bNVjPG3Y=2e%9P}JORzd+J)fZRDyeIQpB}~11N3i?v7=V^F5PKEm9yZzLJDp zM_r>E#eE9lhk!)WCNAWCPCEH;&cE+Pu6_lhMP*F{>AwQD+9_~k-ksU(|kxf#;2 zBgwyGyX?LD(fZ4U3r#SK5O|(XYjYd#y!TA>sE|_9*W1U3AAX7o0=C#<7OkyK(Pq9V^-wElZfO#er%qz$U3a8RXD^jX?7iC@y=28QY}*E< z$z_Wia>Rk$c+<7Q4}2^sx$mD3ao+jo>$;jIv=;OYuHlcDUn5RG^JkPd>YeL5dE$v@ zS+!!No;+oeIQWnQNG6kjX5G3@X3X4F*Cx%>vp1XN_ZMHtS?8Q0%A53(S0WyZBZOf2 z@)h*;^|8sMiNxGkRnc(fspshDo_T>p!mwPjg9Q#d@*u9i0 zz^qv_Xl!gSME!CxTv?#kk47nh3Ls(E@!7&xBJGma@a!}H;mfbS(RH=8%$~iIm@#u2 zK@i|LE+?IMwtnrEw{c@exvISc-!6N7s5$lB_e`2JF-ln=Y_7lg#@ifv*c{Us(W06> ztt-lOt?0}4Z?r(KuN@t#D$Io6DqAc=PKwf+?!G?$^vB+NB)%{LQ!@B35pJuJ!Vug&Ao!{+FW z-NteMcQ}8z>UYumo&WZ`95`o=9@#J^jI%V2F{Kn;{mb}%@lugWB@sf<)z!_kO{eI( zbUVF+t9a{86O*Qt`9ANz_aSf3o3Hx^2HA3}&BcEE?nR&iEX(4VXa3E}Cm*kC((Qm| zU~u(@1^UEowWo51@uVP|UCq~DFA@z6^-=1O=lLvH@IC_rgG`#V2~(y_!W6w4+jhA7 z?gzQxf?w)%+AOStgI!#I{q^FQV-AU$2*qN7L?TJx2Ym41#|#c+*>uw>w6%|<@|->P z*i)Q&<|#Uv9D&p@nD6AuE3Ou&gy+UI-8HstQ7U;XT=*@`O^u9f8&S~&6MXaaLZ)px z)ub+DvwXW~F{8(fqM@NadL7HMxZ?6_^o=*&LMo97RU3+um!_j*LDlvB%rnpP`KO=j zy1F`c+kICtbF&%Y_3d!%(WmGaUU-FM!dNo$CCU2rUqshpsaPVOj3W>X^bhducZ+Fn zZ=F(v)Yp&OK-gPg@ zM5>~xTh_1Xs?2y<8Alg!Dym8RmGNzrpO71TPE=F*t3&1)K9rB_h*L&wuax$%MS?&p zVwQtt83(_R63fzL+=Q8C+Hn$=OAz?Pq=ix?(O@UZXooC{jN>8^bg2^SOWomAUeYL% zq%4Oz*Fpv!2op{;=xchlBnaj7T_Lr+grFhvgfBLD6itpDo;lzMz@$01kpxOc7Q;T}P&Z4>B#K&j~B zu|f%>)+Pq1ELF6GWYd{b89lBYt-&i57(Zzeho5jHBStox? z$7Qml5Yj~V2#F=lWW%-NNK6EU(8}1gwZaZb%sTRrn`%}mD3o$c+;kK6n)6d?>eHbV z2|j%LeO`O+1$@7R<2d-fX4%(EShI2ssaPz8D>+eTH|OQZSPniGNg?U;^C+2MY?IB= zKl(2dTRI4ehR*Ix9Z6#M`H0lm5dK*t_vS2eZ!R3VC#)z+KAJq1kBhFLO-9plSy7mW za(7Vy!4(>_KyN#V#I6g#4W&AzZgQ`VCAVM@X_=__#ocl4d4F4$_aw0`0{{w?nW?&# z#160LgtVxWAw*1Qvc)3H``1xWK7m$ruUyQrC;plXZoUjRY5auQ?g5^=>PFss`ekx8 z3F5ZNwl4XK&O#QUA!$1(4JD}l8+wZX}lV?mJMt~m-$snaAu#Cf$d=SuIDw4Jx zgyXXHF55AFv&oSHbjepsICA=?bd#j6wTYx_k#N)0Cu{MPV8HW{i;98MK=rJ!0=Cu3OBHJe%)0!Jbu^DsG+)(rOND5+K4_uxw$b@c88VWa8c`=90b z;}66YX;SGF8bLB%Ln@Yu070d&IT&f1B2|s)RtG||S}Xpn$8xT2!$QFNA}VOXd*x@S zpG=6zuJY>u=dmJky&jUXi;X|z%OAU9g5T_D3iQf?TEI?c`AmB1*4R_gHj23QK_^a@lWIX5PV_-mCD(Z%9w{{)l2M5DNZY|@< zv)8}Bpl!%Qg;>TZaSAvZbOPu#00FiVqBJSc+a*NVcR&^SW+`s@=z7#fiivC zma(uv_pzjl64LmOu}r~iS8hAOw9 z+-VGLKc+}`5JGAl+R6eH5}QIHV4Lk{aL1jOi?SatI<_+Oz~rn7{iG!0%P+b{-*orGG!w%|;$rj2OSg%cTTC<0 zPXV8Q@+C*@ak6e|&mg7E$`!pV?phEP03w{C{rS?X^;LhrmIi9+p&y0j`Dgwuw%K8u zun@6W_|3QMwDWelE;Am4pttXPK6w8VF=lLg)L?nwfk!#_+|xCTV60P%^h*5V_h)j_ zIVVSPT!nm|ACH}_Goxwe_3KLQ5G{gu?JI{J_Tx z0+V7AC^~w&x$UkS#6Ab?Z7B93Ebxb@RFxCLH{O`X&wqA-9?18QNW`M($lPF&LytI+ z8*aWSE5{?Kl1S7ob`)e>!$h!273#fa>nsocExXm?*~}Y z;<33;aK_oE>V#;;aU}hv4))n|4)@Q!Gm67H?4aZHC!c&q_rMCi{%WCUY-%vpH``{H zt#;R)J>6m2hk0LpeR)ni>2R*P_A(I!rpUhhva9rsx7-P6&j0nfTzKI*!Vdy$%i4iX9RW-n&haAhTx7{Fo-^X!mcHU_}y=3WPY|CL_aMea8=-X4~SA%KK}Sq z_S$O?U6UMv1_pB-;W3&V!cisLZn1}6y6O`kWwKrIxc|Pn;-`DYQhJciBEV`yVInkv`><^F8Vu!MS6K z98fce6$I#FmfA!1W8_WulGxn%9CcqWoqxK7)n}hd(3wT1)5uuLP@^q3iW}0}5Sx*9 zxL}nhMt&H8U4U~ z>JC1bU|=P#fHT@Ow@y?fq^1y;&#u5hb;YbS? zu*1bsNgGSXajZC4F1B><`O=at?wX^*&{Bk2`rwOkTfP$P^~lvo`H&0588_Pmz~O&tUUkQt~s z=07A4T8&7KMQA8}_h-8P5-0oW!ZPP3q-74vsi^SCQQ7QbXxd&?%m)=&s}*wL;(OoH zb5kS5&(Aksa~nwRJwW4`FJp~>z=QD(1^%$-8yqlW5kdt-w0u5?=XwA(z~96;9GcD|qAHC;4sL?!5l*m+7yGlUJIal1JV* z8<^HqhPHGKN!uZ2+hkmq+E{{-nKUg4m!uUTg7CCLNz;{3Veyf)Y(}TkG{j=mSdxWr zzQbD&JVVc#^`K$w)bYHh^IUWLKXBHrCtoblHQ39d^~>qb_oIctQ;J$Qfd~R}zDI8{ zOA$d{c@%x0q6+w6<=4Evd?8-ov3{_FLeXc{nl7%p?GMBhzM#cs7~k*Vo1(}6cJD=!UUB)yQm1XQ&XFwv3?TAA9Em%*o8%lmIH9)u?LVX zDh@dCczOobf)2{ao&XADpTfPPK!+(RRNF0iRXMEZSg6IY^i_^C07*naR2exi z$U#e%GG=fPpMW?^IrQY)I7(}3_cl$Ib@$HW9x3?O$5&Hg6c%I)v06)wKWaY zW>VB=MgmE0zx8fi&f}hQ$o>KbLLZG?0d6u)ukM~6_TFbtU7H?3eWsm;OgpJ~GZ&us zdtFuqYOR?%ZOTxNa<;&s2OdW%m8$U5f6eJXKUp_q+GwmBMRV;~8Zx7};*Xc`!V52l zoQdI^Ym+QAW7@7v-*jr!47l&UhxumFr!+Q{6~Wr8%syqWYgID5zdr zVP82OQV0u2#wZRWrpFTrqu@kR(Caw<#G{1@jB>2ErnEzgQZz+yC<`B_6&`H`F!ELwP9i1bk6uYAF7WHKYkWZKDO+IZ=u=lRp0{u;g? zV`2KmStp5Hp(m8n`x~&>dwcsh;GlhVO{#@VdIUA;5oA&$_~-q%bL*{l>Pkr8DW@JM zie6V$L0EaMtX{Q-v(G+VXVN3cq+7|PM{wDGd!d^M?KIfbZ_3LlGLQSfLbgGqfvW36@<9e1XSz5JLY`@)Z1VKPD zktCUD1!6dkQ}ueWSR6>2*GjgS;(XYEMpr-ubp--XY%n>ml(y>)>S;X;B3CJU7aL4a z^bf`!R(?uaGLBUi2WlggwuK~S#fS-w(4L9>vcOjby0e}5ULHRP@QeNQm-@l7=)U^o(Dm|dvCl$y_Moeu_kJ4hg#`SKu}TvMWyId0fPh-3FtN8Fx`|f*Kwao}CiL?@7l#Ydz zuF1od7Q(j8=?kMmbg6*QS)?{ZZ4F?%$XE@8QD-{z^n}EqkYi*a(=GJB^fIeX+L^)s zK4B)ZwxsEdiy3v-i^N9N5cry@371x`eH_+Y+sy2QR(Lo!8#N_uqVn6p%_6i*L;yhn zV%wcS^28#teJ|5A==+@hxq|+Go{!2B`FE)}1v-3Q>IyJiSf?27DgIO0di0MnSpx+YW?eBEZc6>ovcXixlb4i2#0REnDcgh8%JrR@<5d+1s*Be zA}PSuCO7=cuH~%D_7G@A|Eh)j?vIyoU+^_;Q;gL5`%k{%;@ytp_A73pdt#crWdLkx zTadKOL!lJ54;={^*P_;Th*`D)Uv&(aT=7zgQlKd+P0Y4QN`YyaX&fPNg+_*6c^8E( zC57Hz-h1L-eEIS`{DOy4aPTRI^K#EJelmJ13Tyia5UlB6M_;ZB&-2K*WtbQ!+a=}3 zXpGk|u{Oi<&LynN^`o_B^}re)y5~uJJxEh)16sjfueg(z#x%?N=TVz(3(ImH7H|ec zHlcW^!V!7NJo^bK~BEe;X_+@lQ6GTbw-75l4t7N}`VBnBM?lrw?i zw|~O_-ELuq8`!UU#QvDa`YM3U!AfkwYLIjT6$1Rb>*;u=>6-N7eX>;jLwG z$?NcbM;Nn`v?<+t1jX0SN3CvxLcn$^#apGxj2CHqtwObch~jqyMvgmmeK$L-U4yMO ziuG*C1WsGl%_esohXV57cJg1bGynX*oms~yZ0f2|MnCA^!YFrq){)jrA@~*@6sVpC z%P6l}R6wE1H?Wpt>Hq#Zx>6SYjjx$U^N!7H7AtFWjL6JZe(ZUp^fgVBx%1~hz1Rj21B5evmj#6M^Ap@-tQqwcgOW+qm3Of-j zNtAW4mfEfKb$SRCNM1AnP!%{o}$bkjZh zi!Z*3rYu7n=YYWXX&=?f9k*XDZoBm&v3yMzmIf&_wgr}~#`Xy`exV3LQwluLCXGW6 zgEMI~j&RXvY%5MK=U1begy4?r@1tpCx+2Hga7Y>)mw!F|0+k#5edp{!wxIEa0NiTd zp%6TL$0IsDqTYa1F+^!gSj4I0iASH&B8t0G9C5@x^z`Kz%;!1y(0zr{L*(s;?tem$ zX`afcOqv?YLHQnmACPRVEQetTfv}u+Fn2oXV%bEfv&_h97RFIm55G?v;5%0XU zfOp=W&pU6IKhEc)4?kw@+O^Sf9ye|**ZuV>@y~l66x{=>hEfz{2-FCpv4(Eh@}$?} zfCKjqr*x84t5(rBxR7ja9eo39=;>cYPycEl$ICCi9z_|B8#flPL)K4diw)1W){2*2 zdd)l8TSy zA2;8nH`ZK2z+g7V(Z?P*WFZlP#~yn|L!PegPCC1L>FVyIyQdHMjx}r6M!w~3x7~_t z-b4_(8@V=)JML^SQFLn9hrL2V{e z_1}BCdjV7UY&K;(CQcj|HLd2o`L=1?<>NdsxR$}(T5|cd0IXcOCOXbL?zjz}m#c#1MKH)9;OVF5Qj@N$$e1@D zzxe#?ssg>WwF%!>SdkS>ZDjDEs@Hd|!^yd*XdsJ?deJL$?#o%}>Id=_#YTMTT5K%W zVOXJF0hHUYVFtEs#ZX~ht0g3H%f=O&R4f+8UqPvqMMw?W^xI285s3oJB`6l?po@eX zrwWAHjE z2xvIrL~0K{GCGj{vgLH%cqOH!9U+xcnW_wdupA?Yvz(Ar9$q?S>ZLLJ(ei&M(s=m) zp|UyF-SHQC{`Dj>-2hnOVqzL0(S}hg6SJcYg;&@PenGPS+RN#G?mn6?{2T7r@wl68 z%IMo)r|X`p>7M%_F-tPLM(}N}90j1FOh{jOXf0_U)fS#)o8wM7j+tA`2r1d{)u&(a z$o&tKAI#!7P8jbL#ylYjltM~E`A!)3B+Vk9aAF9pIpo9xm^5u1-JLzW^6-nSUa=nO ziZG1ACZET@9(bDX zzxsx_?I0Rk$@h5FSaG!S5z?mC5fpLIj>pv2@9`Cg6eJFsht;}c^xVqL-;dl&&!a=` zQW=^R^&e!#N6P?;hb9sr;xO{(FJd?EOlj%8bU%6q!Qh5bqnhw-hWA~RrVxJ5(n=`a zGMm({(@FgJBeZad@9;eClm+z7-JYNurjHmTll17!xztFo%jpIoWUCP5D0qf^Ub{BW zo^FxE~8g)241!6F49W;=`? zH=5I4e1K$qEi1nImUmx%yCO~^7>bn;LqQZoKok=~B86n>my1~W-S^Cxy%X(ICy>b0 zaP0?=u=k_?;a5kTPO>dY(JRtepC%zBzVgZXKAwi!Sd4ndWo7R=15^_*Ww4end%q`D zn?wWOez}wxGsgpI8fwNEIj<`FoYh9MMk^k};czmWYsV|Cz;L!_Z zw2J7B$|q=jAN>&nOyStKo}lDd2=jiB-&b(Ra0R!Qs`4xq0~Ll z3aPEI5Dv3r%itlQv4q*!t8lXhlD3hx6bgOZa?4yUzx-wmiv9QBm0fq;LCl`LBV)&n zLIt7_v^Dwg=Bb19SC$AtI0h-_Dh+7yewT~TG)7&WF%1bJVJq+8tx2#LR6ef=3N z?V|{MA0<(YOR<& zaWgtPI>PO9^~37>#Y?mxSta!rA*7=cEa=AE==GpLYio0bIzX`ayYGp`OhSy*CTRsN zaBQ2m=70V_>bNYUN3~U?C@Fq*?ir#`{J&u->n7_u9*;-Y|A-My!t*^Xg&SH_h6=9r z>pOw~b%EZmW0pnWYsO6+ z0-$NFnY!sz9{A^6QEmi9K0&1zHMTucW3;q1N9WF=nLGdSPyMTl&JsbOke1}?D=*-p zi!Y}!Q%8SkHD~|w^k{mX%NMx%nm8mT5?bNkZiT=}-f>zkh&KDuvdXrsl?| z{M(QU4vQBr)q>0rs8X3{zIit1$YpcH;<0EVU6Go-!KqxjWErx2A5_G+WCycV|8&XX z<+zSL#9Iop5eEhaDSPb8#j&siP-4`mQKIr$`m6KK62-zPk;`V>vXvC}*it5yw}ygPU5HDz~{D zW{?vV_mkM@s9-}B&_+Vghbc6M`OyW~QcAP}+p=&la^ad-8r!i#60M>pA*m69tk;jH z0%8^jZQUK( z-m)p`08a%tjzzJQCmFB95)Ms?8fu*cO;{uXkGLc7ka*fRaavlFjK%2B<*~vdq-0st z3(0yvk1Zrg$3Y02TE|C9m)MwAY7aZoNRySO_n~_jc;Yd%bWkEdx(3b`LBg~`SfO`N z`%ySf+1qFSokwEwEQIA?Woj9D-sRM6KbwxfUP0hVgk__=TnLP-RHkWAC3FxBRdw+= zh40p|;;5adKkY&qj{OB%2pUhlh=JFhz+YFS(TSl%fED^K&FyK0MU)~DkE69_>m9Z+ z#d!evLV-vB`6wTK@ILWa63e!u4ryg~s=O(&q-E&WNR$eQxk)U`!BPS3V@H4xG&DDG z*e{OcT9UY(priu?3NQ3cmPM(#d=CSqUX<@)Sq{Rr`RbF;SoHZ<9C-ZU zO#jhlI5C&~j+w*XF8U+B?I0W*Pvyw@d9<)71PV{v*p5r1JAh?Q3(ij0N5#47`k4MF zw!~j!Cg#Eg?Nv<1hZQrGMR~NyD6o@Yx6B4?-0`Q=`s<_UeWH%RPoNC77ox($u7}pf zXv$t*WE~iM<4X!(#z-IhICk41$j0qR?|mBs_nwBfAmbJ&`Zd(rl9&U5(#FD}0&D@U zE$Lgil!rh4l=ks$XyD`5-sQ4=evLI+lNw!%0z*khAOjC2AdkjDkZ^3`G9b``Vlkko z0tP*gLN3QHtTm~}^ku`_5q_S+n#>IAwl;ZsJk73T7 zU70j#N0M=mWGaQwLoruFl1()X*QrP(WDL z2zVtJE~?(TRnEi> znDYHVT8yXFQG!Rv?-6((?*vTjFT2a3~fE(L=JFW>CT=(BRrJ3Sq~zOjTdIc0CY}!s%=o zLn#jgIJRKbs*bR!Y;KIk=0z+CAywu~lH)%r?v9|VD-Bz%Mx#BIksyMwZtCG?KViV{Ir zUHuiCV3$;^nOMw?BDh3FJHhvS_W0>;dhOb^0)&y>o_3PB_x{^7w4p5#0Wdf8XKF?? z&Zv6MG&VMJ;6eKiD~&1?K1!K}gC)u5OCcLUZXiW2X^gCMHio^1HXn@Q+DP@ zKiVP!3f_P3gQ2(Fkkwm8v_$ul)|#0!rnA}1=^I}nbva^H;s^dleonoS;Lai=Ml^|X zr(X!ks@1D;U5m(1H!MnY^{O>Yp0Y_a#g18H!(NLrD3(e?caZ+!jQ7D@K4O%VpR@nJ>!h`{wc`3#?78P||KtDYNbTSU zD%?qJ82MMo7l!uXI`nx*>vZ**5n69lQ@0T(ceoMJrkN#%53!@giE?_kuDExE76d|9 zh7q#y7LNWQrFu0AV)#!ihEMq_0%Bzt<>Cm7xDz8`CrQNPB&Ch70;COfb}fF8r--5@ z*-F0HhZZgb9x*q8)S9>*!}W_O3yRvt(*en33*+KVtnB-qmUt7Eo1(AOgQYY{VUZ9L zjZMsoQ_z}>?b283WOBNh0jrHhEg9j&sJA3>8JKBWEN(znzR#c!nOK~Hmq!Hw8P~-Q zyH?FevRadpE+yrYR|+4Wq$5y)LN+!<#aQ?C*V6yW>j+nxf;;fhqG){LB92xbQrbxA zMBX+jEi5ZRpz{cAqiuo6)UoF3-!ST`zZ0J{&2)!%n9bNp(^>PU-;n+KYlQ89QU;u+ zjVeJ1Gc6IegYpX18Oo`VbpGQnc%7?h{pHn0mfYNq-?5m4C=%chw_Ni$SEe)UST3Gl z!gg$eK$EG`S8xQZZfl?8UW{t6qCeiw8-;Lvc)079e@RxS01Yursvqwl%fhX8WJRAO2fNS(AQ zWbxYkUG+fUt%p)~)=O9|yCZ8iMTKIdbj+utzl5@5_<@lJD{UTV1EnIRKoH>3&}6bj zKYn!qXYY0_^^@96_H+nWD+vhv5&@9)eS8&=bYf%#_?}0usZNh=D<$RXc)?p10> zw{ysarvs2kC)wqQ{rU3E_xbj{k8vDx-I||A5tNCsB8cR>mHYxNVD<7fto`Ss9C-Ti zC@E;)WE@^Tz)C>9V-ffPgMm*G!MM6+YHbToLs6LtT?-IPng#2`z0YRue{ZL?c8u|Y zmRqc0Q6hvS8BdTegmEYp3#$ytenwPk5sxilld(tg-Li)P*kqGY1i>;6JL2~|`S^`A zHH;XNLkkf}wud&GDv}Ng4Ndjq>0bX94VkSds?P~DG_wUcy&Ox}A<$6_4LY<&2nCiT z<6EFD)WpW92zT!(V0|47UBCJV8n9wR_DJ~}Y#-44`qKi}I&m)95k{uNuWjwH$5_Rb z$BNw0;?R>a2@GN*9Fq;!6Jds84}SsK66`n0T&xxmg zM*N-GbnZBjl9&+kLe#LuR8=jpno<~fXRYKpy8r+n07*naRH4NLdas>Bz56Wg<@;k# zZ;J#A30-!CD-3w7Qk^5i_ixHhAtXxs1X#$x91|gB(89Rh;piSCj zsDlbO;-Pv#`ypu=%%0vT%%ZlER%QZJmNJCi93d2*AB56(2z(DA4Nz67fHi9e!y;ee z+KQ%zT97q-@x@ncxz#>;)r$9p<2qs(aU6NLjJ$ma|M*bMzRqZ)TJ{+n;j|1&o~=YwA)J*S{c_%ZCzSNq_%9bU#Z04GkH(yE;Qat1z*6!a|^F z7|~EMsWEY3iXwpu7$m>Je$7cp3MJb#cmkiIFiH_mYaAy`ixEUr_R12XNQsi~Q!M&q z?E(omO+zeh7!c(rmI$BE7!PpBa^IlPE!%~3dHNWt0-_Qzq?muZXbF=io1$TpNfW3* z8KDEQfsLj-G3x3Xtja}RxpEcv-2H%dT&E)caY*H6`oe;R3m36|{aTXAxcLWi!#%mD zhxRa)6SIx|a>MsA(BEJ6{2VjBjfD#rg>hr$V_beZ#gI~(iTp-hx>zVwd24UEJ7<_*yit)cMKF7$G(VzoV$*-FDFPQ&6Z@e~73(E|6jEW%$ z3$q})r{}#spVqo@Si(kIniOq3@Xtr|sb`!h0;Nc$Q=EO~`P}&T>$v&1*G1EFKk&Ku z(%{J*+?yLtPozLf^Y&V@wJZ-HgP9| z(gBuJ8^*X~sj*@NVZ>ITiU=U-CUC6;t?_!YUV*mcC|qe#^zz2hDZxQwYe|jm(rCM+ zYztdLjore8SQ3wbdfUda1S-%tmPH|yd6fdips(oCC4A2ZUsDhs3PDU*#DqnF@yEJC zfUt>42VaAuvk1#ZrfVW4P~p49ST&7kN@%YbPQ)FAC9&+7iLr?u%_{Vfg*UC`hIz&k zt$mcAWX1XC(0=KaB)8n!&=#9p82y(!S%3RAtbgD^MB0E}%-C0=d;^IQz8cz)3RB?; zG$d;&eED8PgUK}xo2i)XA_&q839R(1W5}>mLw7c35tq7V&oA8 zvq&(32m+EbNKi5=If=w&S;7XE&9f6b-FvIvKdSn6_sp(7=e+hgbN0+^&z-J&EBwMY zpp--k52!P|W`vtBx|vU(c?6~2G5~!u`Z)HiBY61c$9aGLB8+EwrwP!|4U)Jk%~p{FJ`Nqwgn~evKbnqH6m$cpRCt%18Iz-Fes#|rWSPsmPZq>d>UL+ zC@nC{R>7`awOx$uL`rvdv>%-FVdWo+Ogv>d!t1koklS5OIP){CzG)AVm7V8a3LBPf z$+^3}POQxuK)mc;)TF%;nJErl16?^o+0S6K6*!bmU`t559(>0m5AsF2^qwIrt05rQ_LR zw;h-`V=~YG`622_SyRHYqvSSMtSv?lZ2nRL#ng{DI*VXeEr{5R!V_r4ugBov>iab7*c*FT<$ zltZ6n#ur0=az~vjmUuirRi*626rM&W(j+Y|2nZy+v!-nc&&)x;Cf|g5Bdi#h0TSE! z@*R}bP$jpabv77${vCz^-&OBBpVr!r+SY+6+SFB>AG6MM{&M`T9J}bR1mB%wE72cY zpGlRN!Q5vVOddqY-nI~?^<)h#+p+c|Rx*|NM~@Qx@F2)%QUp|(XcDgpO>Pr@X?onJ zT|Ad0E8kL?gtbUZnifb+s%~31O&Ea|Nm}g_!nBoyWLw8eJ8LCdPHGCZDJSfdRttr~ z5TmuQ$-9m%w_I4^ttCsVK!XXrK2qqkqtNoPmucb~1a_t@UAml^v!g@&u?Z(u-QH+Tk{D)eJb4@dutu{Zt`O8ay07JO2}!<*l}U>fN(e@ynDwSl zX*q5Ht5^1$d@dlAikjO~niQ5G4B3#WHt{q9O^e!6uSLvUe|p*_2iv_D_!tx`malMj zr!KIbI*B*uy^Ws=hglAG6ep~=;fxN;p5;rI13pn41GN;++UgWr)pctUXcGt_L1;s@ zI!dNcvixM(xk_x}qEj&^qYc$Yjfy7;r4hEB=&F4o;`!tUwaEL+u0w}iDBdQxA`|$%(8UZ$`s`w zgy5+spW(rW{)Q()_uSlCaO+Qcwn$be1ZiQ$T5hZ>g=r=jdabbDu}~|KcGhd_K({w3 zHe=PS?HGnf-{+EFUni2J*HuOWIuW;j)hBFn#(o zpo%d$*XcGbVIrWfuZ5;)^1AJRd2Mlljak;4F%3-v$gVZhwAZp9vgr&VoLJ&o1!yFl zou{?uvZ;dZx_ZQ8lgy&-0`h#1rDx3QVkQ(>+A^zr=>Gak#cHkpCcH~PIQg_ z`f}>Y2%c`RqA^Tac}xuy8|Remj>i=W61L0uD3Oqnij^@et<>?c-ga3f7>E)^qJ(+T zD65hx%d2asCN+#Ul!Tww2V)mCW{kC`Qh=`wS#rKs5GsQV@~N$pEp0b9oYn-_gUizgCv#mGp~(0WUj`_lP{Oxeeg{dCLgA?ZDFtE1 z22)WK5hZONflg4!(i~b7Mr#YniW6gls5l``5*m#dA$+20g(x<7_*4hhaKlfpX6gGM zwyMz?p94?YmygZa2^|>{lh8^>dMp;f>lvYCl+Uuc_D z*WZU_7fvD>c%>BtOxT^iGlt1+1*S2EPU7s>97rYo9I~xQpsULq^nF58Ll9ru1lSYO zcB4!)h)QT^8-xW+Ak5hfd}O3zZ4ZqNx~dJUv>)pTtrO}|gL0T7CqUI})JAJWwFXf& zqB7c`He91R)L>+Foq^>eRHb3z%Wv}h-4Cb16F}dr>FoXa&rt5`qaNF$OD##LCz@KE zP>T~9N#fL|6Jle#b8(`HbWFWw1MO@vPlUBMG&-iP4V5IJsC+_U3kr1;QA_I9z9>Cd zp~}!W)mh_w#hXAQomd6!Mx)N~=qN)YgA9)hGc>%Cq2U1rhgUE(yoAA_52#10fxjT- zrc^2|Zr;(y?8AaZ%jh4NPorK-4+7H`!neaVEv+tT%h&BEnlz=CB#E8ct8QZ+YIi`} zKrKW-ErwFtpkS}cu zC%#iBYJ*~R2%dB#kIn9ywPH)hJRt*#A)^&_-9)BHhsd#a&od(ZaQ+lqc&RT~*%5+^ zw(x&!MO_m)3D8jzMu{0BW{`+wB&;G~ISGqNP^>s-e=483wl&nOqRZUG#G@}p==rV6 zSuJobP$_ z8YVrw4T8kPXkv_so$#JXgHgd4`i4WC;9LT<3*If-MY?{KhY-!++bX+fa|H$l1Xo}6 zZE@ReKNGXppTWwNYpkce!6o?yZKNyMHk0*9OrJ45y?$cAjGgPagl!uabbtR^?lXWm* z0LDN$zs1@lmULU`U%T^&)R_QaQgTi+S>4mV`knjfR8GvX(@EHU!|p zB_rF^?GHKpQ=%HLXroBkui@$%jy&ezb#@y=zsxgFKi{E(z3+Z|324yOm8GkkrCiF; zUCvP|XDF02G#W9rMg?P9-D{obb#UA}iymTau->+mqG{#n9N_;wpUK@<;hx``!8-ke z?{dTScZjXF-kkQeb?*0mY5ID*2t1F__qgZ5M^lb}q7!!5aeF&2ivhV(mV7a!PzouP z^K^IR$p#s!i7opmA!!OJ3YR?JaNTV-J~f6(lPB`rv(KkP)kgEpvwvoKO1){b$@4w_ z`rz#yJo8<5vq-9PsX(b%pj6CLF67dAWd$kMpkvD)?<^=e!8|DmLc8x5a~b}8*F&_s z0qniko}$I=*J*NItv1+m_k+{-N~w6_iD%YYbZ$cO=giqhK&4%+(OTwK8qAq<6ix1S z)B0@f8Q|J=1=2*&tRrm|EeV5vm)}a!#Zyl`*D=5Mo3pQggi^6YsZgMpFQs3FT+wMC z45iLB{F@GX(_u%niw22xC+W^qj%sDhewfX4>@~8T-r7=8a$%I+HB%~?==_N0KP2j7 z5kh9Iuz)rN_YQj@vlgE=fhUEJkah#EC3S=}jKrh(o^Kb7F-UEwm?~PwXwMqAcsjeaVH)<4QKq;Fi#xR<|aIMCYN`?L;Vs$iRE2L5}6xZnq%dCnMx>d+f9Fb8rVL=Nr zNle96wEe^`HbUFNG?9TsItrg_YdTD_J%D0_A0Qf|WIwVEqN|7qB+?5IN?3@O?}PFX zo&=>3L4fcBRIxm6b1i&qFE+gPB^H0{L>m1zl9pZ$qZ3S$U`zw46hek1re>3L>7#4I zHu&~HW^+i-$Ez#XC9$5BsYn*MgV=8Fq4V^t)f*|iPdT+(3yrrQGSM2NAy>$=;l}He z%V+WZfFKO;l#l26_dOpfmcuFGFcs1GWKCu^xWW20h zZb_YNSVSi^$Ah#%d<@hYEgp)e0-|UYji^Q-13WK}HiFR4p@l*UpD578&gvJ>79(`D zvXz``7Xzs@AF^>q1YR%rllqW_83+l|D`8~hJYYSLMes8Sl?RnYdK1v`Xh--iyaHXH zHh43SVd$P)EZ^M+nZYW?(`MJ!OtzYtP<@%3a#^(dh@mhJ`C&mhi9-wEgf2X(D5LOv zn;=#P)*ysNX^j@pFq&9f%cm$Y^yZ7G+9;FOpG{%bWO9W9`C{HC(@GJ98ARYCJ&%08 zKq;4H=7uxL_mqh14T4-NY0Tua?0v!!yzuZ7y#B(gNYAb_o#u-s?M{g_0kU2CC~AO# zLa9WKJY7;yX(X(|9>BeU?TFNL!cg5Rz{f_TQjE6+aHhOuN1sTOA2(^%3@-sb&LIbE zOTLsPlg*MZ1myF1vSC2Jm?zAH-)%(=ip$N~KDvoTq1Ex3$Ac-4<-pYDL3j8b}1j!Y548jkV#DYj54B!P0_c zd6#15Bq(G+XCP4moPsu{~@-W#wKsotwV)&@bnsCU)lP@uNdrrkhT787iQ7)@gW=YX|iG} z-Ud0;gdAQuM3q8BA%|ZK@JcyUF+;A9r`%OQZ8mA#P;)n*NO1i2toYPc#AcR-ymuVc zq+ONP?UoqUq&gDHA&oo?KAsiBOa01mNpk!1@?3!6p-tl8?#2b5qJ_MaPVb~Cc4&+h zKs6F=A{&gD*j>lL^ipv#TqoT^Nll_LN;&~j?NDzfMSBShM#qefB+Qw!4Tm1G7qO1n zbI+Z`U3Wde&wqZkdFP#19luk$Ri^-ta&a;bJ@~&usU`(P^5FfC5QY(<%94bd`Sa$t zgz>atx4k~baJ7mV5EKd_!%O>_wdF>fc-oO7>A*V*r=TG)Ms`Fk#vx9=dG43RBR4%| zDw#UDlAw_!EL%OocfWQ#TWqxnNn#=Jm5~ZJ-}N9{&hEuCP^aE$A*D5EefR6)oZ@c4`5Cl(=*RQqeJx6yQtJ^2_nMoW*o?59^!=+3#$?b_mHMQjp_a7wwB_W3EP zKzJlN;{Dearp0w*U?XRRG%`@d3j@5sD)tZ8>J+khvYt<(8$^jU#%ghO#abigs1Z?Z ztfbnI>F0t@d#KxQ`?K(UKlMv~{`nW}=aC?lSa%>U1%e4&f8DKS+wHa%No=iQZ@BKa zV!t_i8z`V;#uc<0zz{p{upeJI^F%T4t#{2&e)dbsg`(So&APF;Edhje!c3)<UopHRF*w>p{V|kv(B^O<029~d4 zVsDWUK`aD41O$pJ}iZDW}@qTa%vQ z4?pxc`|P_1T5G0E>f^lc|CsNecMedn6wo9Ah5*CypE;GojyOnM{`+gplYf62Kk&vC zEwt9us#Qz*P>Q81-r)7u-ej}Qtxx(!8*j*2XMKxb{p#mHk3|%l3Q*yf7oDHZ6DcJ( z+;EEt{9eb^?}U^aW7vH2P5JUyzQJ#PeSy7}6eM~CsPprmpWAV*J8r+*NZI8&=;L|X zt!W}UJGX1WT(=i}|I;g%%W&gOSMrmeo=e8*1bp_mqxs_%*D-hA3qaY4>c!yjao_*m zd2F`%#^RLEe$AMm@qhb6Dg0ipz2Wb8x;9n`dGGzj9V+*G?6I4;=%SySkxGrg^H9p^ z6N%1MBgTkWHe*cLp`Bsc)ZI-Q3$2ZDxYmR|t$>(LSfXj$p9CGMy`D)Tv=CPO$%!=7 zqbiN4f$#g2GX)|COB5nO#1Zv4VlZwHJ8rV3L0$XgO+*yeiKI`WM`*-#8U}<22&B)f zY!Qo*`7j=`0dG`g zFsh^cjJ5vqAQ75TA`)$3Pzu4YPKbo0AQTmCkTS7JcNp5+6E$pCLnu2FnuQwK6c(`l zvXd|RC0ReVQmyr`v>yN88Gh4uK#O5|f~e<&ejTCXx~IZ44txL?tog{R|nWEm4gls&O6ZAeja)g?+Z_)52+f=#*b)lk*R_^`^gY z_~#2(aKQz;@y&0ZKv(yMNXHLOp?6g zvObj&POT>vSN~slc?WLEl+_hhP1u@>ZXl%J2CUa@5WUtBtQj1)Q+TGy+%+xQlPgFd z6hZ{H&}I_neI7XouuZ;N$v95Lt;w^sJWVN_DzJ16Iw=W)4E07Wweb`v2kDk+1+WSI zwU(I)LaG-3oY)m_t-xuVG+4N3sqLt0D-Lz&q5E<8;rofji1PX5H)d&PZsK41n1 zD{Q*O#$xX|yP=e_6rQ%Cj6ynkgh0x%e(HkW-U&Rl@E&o;E%)*Eo9~+LQjw4EwTsy0 z6CZUH7f_z!_rJN?OztX@RRKn~&$4F63c7l_cxurz;+E@f<@ML*nnFIu?)&U1KDp2C zE;$FK6u-InawF&^6K0qgWRMcBx#*AP)Ng-5#Bqc+hRbjLgZS$`f90j8Uowe-EkClQ zIOM2<(hjjwikq&#l@+XDl38~=q0;~|N&P!@O2#%Aosc+Poqs<6GTZL(5scCJfzL}X zyezJ{;##wK(Gs3`-~~*sf%H6<4G%D-P(n!`UqO<@#Kx?XpQ^RyOJ6xn9C`GisX$e8 zElilu&7?^anLT?ZIEc2j#q&A;`#(3?Y+vdT&A5JKYbz@jvs`=K?>YVS&#}?Q8(_3% zyX`*0tFOK;?zsJKGk^a32qD>E!}U4np#8+`*|X3(;h=*L5I_0Z_l(wg=m?|NPWCkX zCtVFepgdGw^59K>Wxu26SQJ_;jcTaq9qU(1_51N3Gmg&hNhCK7RP)i&=l_Bs}4OS~?*&G35K-xX|pe&+a0d$vA` zxR|bTnTEh-)Z#2{Mj)zWy(IE+Ixb4=vIr`Yc?Htoh?E}G& zzWYnlTbOLc#M)eWO4+G=?eufa%kv)>%4r0g`K8mvjyvthqYpo77A*LH2@@u;`R1F5 zgAY59LLrZ^+-~vU!*`mlV7>8$9%D#^PRkpaU?N{Q?VINBPu(kwvDSs(``$OjuDgDW z2OoUI3=WR4>82ZU!U@NUQmL2<0KWeE8{Bu_ZIsK?+~*y`rF4nq_s{*d*mJjC`O5>3 zn1MAzY_-*99Dl+w!YaYrLfDceA9CeYmr^cGarea=FCZhuTCw6{tZ}+%Pu|xiL^Hm_ z4JCf@i(i@Z&;PE7;}`@y{{hur$8zIKIF?M$0$VcNLxYy$xyCF)(BB znmn|sZs1{vrBzn-y$s4|YPx~%0**!;Aw@zXv4>GLYT&B?fg%)wNN92@Pt9oRN)U+v zoe>m_qO2q%^*X{tWTnKDA)fFlC_z>#l1NjOf{`R4$YiM)K|Qho%SaqC)EHqXs*;$9 zIEkqzHKZTl356$phIIu`1a@;lQqeJ?u{JbytY3KIzPF0G?g43S=cQ6Mw~)qJ<*1aO z=ue;aUrWx@hZp!v{q`^DpYcal-FP!JlWk|VZ8aNwgic(7>AQf+NBWj~9EgCN?-6Q` zSX)sa?ciF0=hW9ltGlfe?O^Q!Pe>~oH)e;E3MW^-h)5cuKPiUoFs0ouS&oKTA+B!Vc3 zF_ehy+O)jZ)NDFg98;u_EQJG zN_)U5*su*kXn$yvTFY;6-l{R3rsVBha}6){DK_fhBtoRhPSSzZVvV&~bYY1MthR-&*}`OdXsO2>(S6MR zuO;W{JnG3ZQO{?UJd9EOEJ7C2h1%*owV#^NxDde?lGx2XDJ+`ADr75gl&e%9M1XWD zViPeWT**E*8s+}B?V7ew2SQp~Lkpo#sMki@^qZaisZ>_rDOJpNGqkxLo9gb(xoR{< zBu=c;F#qj&eDmu+F+cd>SB33}Bxns&r%qzpw8_o6or)r9ouuCAQ6u7Or=4%K6P**L zK{3IA%0oj&P!f+HiNyz%NA z?DC0SIPtU-tUi!~Ud6HP!b>H2@7BA?}PRs ziX*hv?0@k7?0@k7B2}$%opYrW|9tTk&OGZ3GpV#*O8}~Urn&WQsSVDd%$?D~2v-c1 zDIK6ZrNl3OaFMy`{%b|kxnjo5^*Hy(=LjJzM0&T6?r&BMtVC#7Iy^)<^pR4awB_wb zP1_-?-cNfsZ~N`HZKL9}1<$THq?;xILCC=m2 zcGhQ#0uA>dNq|-~5ctHs63Fk2;jtp*jvb{6G#n{6Ha61eJDk#H!#w_w0-0^Y$5; zs56l70PG8#!%mx(sE}y0aJ7-Q0oxo}OKC~9!t_kLln z`NOqrGHtRGzf`U`>0x9{y)nvZ$A86KbMs|FDVrc~zQv|&zU5{@w4EhrOTX|vj~AYM zi5xx7iN&sAPfDitcJZULE;2_Qdste7kxKH=1COw%|7~VX-hg_eK^)utsOd;GG`gD4 zpKzwR`i9GdaQAil?YH5hAN`137j5d}T5Eje@xt>j0_C+#6!&*^D@F4?pSkm2<$url zq4~*A&lO2xi#faPwhO!O{&68v&uyJ{{8y}4!5(`YXo|(DsV%Lg9JM1k;Qsp`;GlyJ zVAtI~#;&`Kzm^boPdVv?FPdz2qCEvf+XdQYiM2^?YvHEQe=&YBv{OJk?tyfBb*EW( zAy?qiOD|-z%{J!PV?S*hr@-My9L(WI94y*hCR+1b^W3v9knuCfbz$w>%H-=RxLJxr zXLs6uKA(#(y3Bn0J6{n|WY_NAd+*6U`+QOeA(%h^J$Bh;2b0V8VOmvChsYccU^lKB zky0ks!me+Tf6O{N>0&I6M2sz*w8ZP$xj-^XA{sg();963$CmrqNNU6;p%zs!2x>Z_ z7FQXHW6Q_0yA%@3G1kH(kXZ^|hO!rsmjPWWPfx7LnV3im5+q86gnkw&CCVhMF@l=5 z5YSj_ioU`_P>CZJS8J?nL@cV-SslkTbcKR3c*cs%_$uolPy%TLk%?&}F+)1S*A~7f z9AwrLHpx-VR*={7#B6$3OXVZ|04Y6$P)MPyfYyS=^#A%&hVHzJkp~{6^61~FJoOZ{ z7ha_H>T8ylzP48qQ=R_?BhUPuk>~$GZQ;A)-qQ1#Q<(IHFF_PJ;D5;2(#1$Jr#|cteh;B1^x6O2y87jq7 z|9GBTZ}>A0KkyI!{@7Ez@a!wR_VQfbox6Y~3zx1l*lpA!mMmDtyKgRF(flP0tr_kF z>?y^eCmqUGJ8g#)Is4g_kFP>9K^~<-GAbmmJTgMHv@Db&(Gi7AfmjHn0Z)1)QH`iF zLR1^YGZptYaK80mk^vjU#1_il?7pV_sQ?BZ`Y}U~%whQH&oc7jSE;=6BdTv*MD6WM ziB>+j?(W9OTx#$9j_Mmfp!WXd=*oi5jl8rqlh6DRZ>lRA2n&nth{KwzHmbKMV?nb4 zrXA8%6X}GSPG}^CjOQU7cd*iSxm5#$^pA`X8ADb{dNLvF6++g}hD=nTda_({?3cOv z+zYtlk}J9YiW_+3rn~w3pC9I_ho5Bi(v|BR>u~=7AHKJcMQ^{y$|cLv#4~NWt~Z&Z zK6j+E^wXWzfuIWHSea}TA2n*HVIWODUIqbL3cj8!X;e7Sz&b^ zkJ61WhD_5spzQ=|7TVkD5y!t>XPYM*d~kO}_2G!>QxU^2CRAQcSpBZ%omF7g)qY8! zu~_r|Ld{!CVcE!94#>j;&X@(CS{PAnJCNEa#R%h4R?&ujO{`8yRU5bWkixGmU$^xn zWyX$L*WxytK03nrrtR3Ptl%n<<2Esad&O=%B97n*@kL(R?p)@5@#=NX-}vFT7&+=n zhBy5QBfI{J+69k5U3Z+Wz2+`@_#8&BNkxh}xty|%mog@3Ft+&H44i~eu5&JtLRjjF zkX9Q+cy6AwLKEru-Kr_Z<~kWh*fNHtpP3YjBhU`{q=}PO$5|&45^HNIgh7d>#bDF( zT4QvK)>f228^>*yg240WDQEfXeUI_+Z4NirUwb>%Y7I}RR6s{SnoLM1n52~AACErE z(FdGqmM&gOSFxAw(iD7^rQWFHg#lMxbTijqbt}H7kkVO$xo)eFioZYp3`c$D^Jdx7 zWe6paN-}@$JM?(gF-6FhkzV>_3>-S=D`xI%?^sByY0WVyB~mK>eCJ=;XU<7x_LN>U zc8sP?K&}1tslz^Ho_PETM>lAtEmBH6PjS!Pe`T+|_b`)tH)3+BOb$gxDr$`gIW@~U zCw|vl`27n_y;?^pH74wI)Z@<2 z*HcRI`s;5QPvz6=Do>ElO19i`b93K)e{t_^6$zTpL`hqKxc>TE_}IsGGR49ajL`&M zo;P2AE4_b8DPDc;O)^<;El_ZHd!D`5N-5^edk64P0wU$nMII%5{%4ER&6AHmf#-Q` zt~Pf6Q=a11Tkqt{Upd3foA-8lt+{jGP80NI!ER#rEYA4C8Rk3RI?oIbj->O$j;-a# z?s*=szB-o!4>-=e^wKMMo=vU?t8X$|sn~IOiurH90~GC8g>)i`Z6ydJ+wPfYOR!uK z(T(Mu`S00lD4DLyv0qBrd+B)|A1q$NrN6n-Or5&3x&4+uGh@Apcui*k4I@#^ND|RV z5{z$n`o)*oW0ynBE3dqP^1PPkbi2*75{wLw@{6BbVopEvOQw6G^$}0B)i{XVp1Rzu z;`W>Fvd==<@tyNO=b7$8AGLZt<>^afMI$AU&xbtz$UoR)=L5{#x%2S6mKa;oCh)41 zV$JFS&N=5i^PO|fHr=Iy`@fL+>6%oGj#gMX@NR0kCj^uVUHsvS%Q)zuW6kpAD^SX7 zTYHJSzP-2i-~S+6Zut>YDCE;MwKnNOAryDrb-($_SH5X1>Y|k#Hs|(=6)XAp#}6=X z%$KoGQ9QHymb9qj{0i^4lUejeP@|FcV4<)>h(JF=DmpYn#JALT6y=~_tSO0 zaN(kHG6%)t6u$iB)6LmmKgSFX4v(4Nt%vP-9*S5%|G8?V3BF}8oc_zHfgT5_=la@ig(_~m*0_(vBSo#@tn?BcGzzTOsrO?Gnp z$Cm@d7-menfSwqm;{I4<`t#gme%j;KrM3PU7Eh;V_v@N*@{tOEDTXY<+&Fz&m_)w*YRv?Rl1J*G?}fp8pl}~ zzVkNL+<80ofkCvcp=+avFo)k8GVzrEp|ocUf?GcD5JVtZ`pXM!7s_$qZNnk}*+4`p z<=P76Th&?w)o7Hgvb^N>QzUT#F#PQ0mn>kKXenue~(S+16Pk=K2V4TR8>kfw@dB4aSvxcI~AiNNnz-UyX5my7>WtJY_`)9KZ!KQPE5DydqY@KoLpI~HW=)kHKDHA-`pc!`#=2tR z68`zRhOXDjX{f+&$CZs{O&0f#&z+Ui8>Af++CXD{%;|Rm%q#V zOA;#4auf%0(Isb(8|&vz{u$R?{T*O3N_q&B5SxUv&t3r>%*qwl(mT-#n(egX=Dam` z0h?|%i|(!s+QKo~tZAU(IuD^2w(&I;h-Nb8nAI*JRLx}E-BV(RD>rLVB@xh5HSDu6 zg7-V;cz+=H*+(<%yEAOqJ024jJrHs868L+MPsMZ9U=8aP;WJypN7^ad`}!QCB6u_6 z-t~PlW?Y_cizUqvFzY(#*2Br_Fu1({DQU!U3vuUAL}^5k;!EEVBrV+_1=fg6Muh!*;A)-`ib8#C!Kzb z7#tWjzq#m7lqcl?Wpyl6XTii6!zgwB=LrJ1+=vn{0|P@WTKoZzKK2+;A)lK>saV2Uci8RB_jqgG zJDl>lFPS;}?Ew>|rOH&z*X# zShaGsx#)+#WlB%4>mW8O7s?74Hyt55RK6S@DNPC`rNS(B-sK2$^2vwLRqk?yME7nf zP)1UZVg~vL={-|qh>11zSXx|!OWAs+_z?+I&xrHyv+iuIT_i6#s^Rt+p??|t?# z+itxp`|S5gHr#NQ=KQmL|F!3XB)C!goRNA4gHeN66|Y~zbI&)TM_;*{K%lHme3#6J|VXv)XiPF`c^gxkAW?A1-0{Jw9Q!+j>{_J76zn&zdf} zy1N+}8D+tH3(VjC@ihN<;sGRGbeE>K3jO(seBre3nGZi)#)O`3aoO*#G^PCHlup+~ zFO&+s{PV?^*?Q|O&91xd&t7}&#*`_2qEssJ;fEjc?z;=kWtUwJj8ZC2X+tM8V}S9^ zy)f{(@x~jt@y1)tnP;BP)?05b3WXwz7A@xKr=BtQ-gg`M{3Np3u+`}`fMY~ywNU@ZAriz|P?4!U*LcwhvHs+3 zwN1`kmq!W$De;6PI}pUY68~8 zu@$j13UrNOolx)#_%c9Z@Ztuckk$hoMMFTh3XuX%pE-<f-MXdbc52(H|kHo>RL~kCc6!nFR zNMZxE5xS2&ib-GiM#rzNzUeAfUVDSZC!54o*bOF5AYRSPyI#aA7U;kJQu?pDf?+S@ zpm~a^o+LI2zOd@RQV6$P7izT{U;6f!m_BUf414Fvxss1-1o@%8MKLs;{+uk^a8qi3Y_YExbMF5W|qFUoHeV~u;i^( z^knjkL{%E2s~L{#)DpuU8!qJ79bUo+D1Wsd5q48~bayIG{G$b7X>+ebIVM5UQoU++ z+A|x&grgTDvzX#AC3-?U7~rw z%m?7B`<=|-hYRV+_+&jnt)^*2bw(;ReBXLouBp{A#!yKbNEwhDg+oq1h7*4L^>Nk| z_x<{6=Kkw-rfp`0+=O&9}Jl{i{}U^B-<>d1cd( z(Bk{GHtc@DC)sS9%~L2qfM;}|uAl%~*zXiKO_*Yld;MVx!i?>O&AU+hFe#0KSWO{j(_ma?o` zvjmt1tPeZ{lqeTV3=S{k?fHw?Xv690?>_R#^X#+Fu1x5j)zKF4q+)shJf41jE+5@t zQ@;I;pK#GH&!w2}p|?EMDRb)%mDwda!RTFJb%W>+XA*yYXXG+XczzXvwGk#6?|-i0 zjR}g&w+}dT7s)0)V=HNz_}6v5J1t~MQQ`}joP(T($|}vWyrhx9VVl5_n~nM1I~)9N zmEgr`ei{U&fslk>?`P+=f5z>o+aKq>Y{CZWbvE}DzVZ-Al$1nT(_O3ciA9U3vXHOb z`A;Stwk>ZS`+M%W`P)cmr#GI3CfaBMjzU0#sjT3$FTGEGyIGyZrJJAQ(PMXJSGg%# z*HHA6VLLRV)OiUbW@F51N*qbI`9tR(EDmRcml4hf(KtS-!4M=KW&I`+SS||~(Z`)x z#o2kEiS~`o=et=kmx!hqr*NE5qwUY!{B1gDDB0rmrw>+2jMh%I*Fqwl19}>8X~38E zY*Rv}_OqfX`X!V@r^QbA%6c|uJl`ni@7g@`EMz%xitG_4NvKsDL^VyLo=_uVV!lL> z2`P1VQOf7>RETm#hpZ})4XlMu9Mwn=4Aw?TgvaXP)hu4}Hub7ulqy|;Pq`c-gTPiz zjif#jBRxU3m_rz0dFVz^U#fU<+3mvm%nMenT+L>iA7D0_K9M+%86AwMSE~4)hs-Ln zc@H5q(pkfKN--sqC#sFIe9;Fyx9U|9JOA*}kr8HZFxyP;o=qGl3=c<)P$MLVk0I9` z(v{6o%;fNp6#S3?$!M)cNviA{`XPO}0#O{37>#nC=8dRErCz5pRIyq- zG$@B9yo{d;Qd*m<7R)mAB}G4=TCEakO{@)-dPF^{kHNFF2du5VE(kn4U$t>LMQ3pB z=5wu9CrbJelqsby2rc&*({=@85~`yO672nOX$!tzAe#wV)`e~y#>K5e!&P8}z$=r> z`Ds^87~IxsQ}cubw_?A)Rz1w}F?6S8!)LZmHPVQ^#=&4^2;3X}>3+w^dk>mRT* zwFzCtrht)6dfQ9Y#ul6#^#+5Z4WNouwGL4-OR1Ex1v*Djlv1IjE#i4bFtlorSZE4e zS&)|Vubu0IHqGK+`?(-HgZ~uW+$wfOwFbs0LgitMZ4jtLlL$*nP@aeM1isgD`U!nS z-ctypi8Ry`LtR^!lBQC;5bZ*u32_uNJY1)djJk1q2(py(1^mEiB00KC)ACWY`O^wv z*VfQLg^C%aCs#rS(mu|nV}`WwcG8k($AOJ~3K~%yCC?!&d0)!lIQ)oIn>S7E?-C+JReQr$Wyi+)COiX85 zmWk=Rtbh|k|zQk<9i`U5AdjI zK}kSHNj%TTPijQMkn<(JRA>P;SG5I$G7)v{p&AiV$8;%46i4L341umwl^}$V^duy8 zJYmQPpHW?-AUsOGj}aPUA~HtNa8T2V6eyig#<#E*<1DVUXNA|AH_Z2K_bZND9lz#d z5mx*{N#F80;sk$E4?W-c7HZOXUV8tpE~fGJLJ$G@ZMUN5)X(Gf+V>@cgY@5WH3JVk z%+OyR#_yR(&(SBf;O@tKjx~2*Lp0=Ij!DgV*BV69=&%K{%@of;M+49i^(3;ATApWp zBE=ZQrmz{QU8f$2iEcPiIfdutDd)RLY85uzVm5~!e<1!CUV0qG+3=CCJ zQnK^z+q3gt+Y@F2m#nSfu{$4Q(fkG6cgtVt>M1jGqZ#S%AAIzun0xN8(Z&$R^`=@0 zjYdQg2W^m_kSK}qRY+F)L^{D}O~bk48YL~9C;=6!PbL|Jm47&a$!BkkENzo^ z(o6e%pVc>q0ooBv>=Nh`&AhJiC8# zhjC-wY~$m}X8OtJO04QP%$xfL8*kd0qgPycH>aI?7(L|;*?6NF9gwZP_ui4&vyWia z%4hMEMYniTrMY_Kr~qN$rLuMsyY~s_wh$$aCXd6FfRoNDYI6gAe6n@Dz>2FGmy<>vc_p#PDGKMhE3eOoah2cMkTjsXrh@y8Wj znq=k6%@`TAR>ZCx?I}u6D1qfxYi+wMIX@tWvI6y4yUui_(V?c=vWPNm(6W|oQiJ8} z$W|OUvKX1~B;nWJTgkP@?o1D}ka`tev%XI8t#4R`dBA)yK>zg5Gs24uaToq4x1sRZ znXNgz;3`IEbpso<9m5KAgr?r!=fIGl2y9U2Ncp18mskju22WC0uV&$Vz?%h9;r3u7 zoD;j&2x*_UwL#NpZS_ggj@kcX?7ib{$;vwK@3Ypb+IydL^X;7IhVCW{0^LYU))7Ww z0CjW-qX;4*j41EmM_yly<2bJ~qXYwEm=V-LRCtk`p~<<0Zs-KvbfCjcC+xkeYOVFW zf2>uttM)m!o#u19`J8>uxfRx`dYUl6I3pRUPfp@STqn6`HK`(LaBedF*;$9n znu+8dzmON$Y;7r3uqp&XiYJwbF^Z2-n)^jt=!#)lbV#;7(l-Xo?YBCH);p|GxtD-=zMF$+dj-*Y0x(m7)|cxaVo|Ck`{ z+bs~=oJ$Rqc9im#t;O`7?R`IM=Nzt>FrEm9G`ZC$LqB0{yov+H#<5ZqY)vb?BxKg) zXSL)=I_n%Ohu0YEYRWM0D7%_{MDiMsNmt$#`@n(qHAG^U$tfyK>lxE})=5i3R7$6v zVPjB_VDZ5dHBnL5_OG#ura_=cPMn4i028tdBb|3$2TsL#GBz}VQDro;j7F1%Mq7F? zkkIi;+Hy0*7P*y<;UH$1Z91NmjP=YE@z)MhW87sMC%HCRU3ZAA$1qX}1{Z zj-lOS2M=uY+|i}GzwI?{0T5Iu%aX&TLuDlmBy_P?HNU?knNF}#j?>dng#%~oC&ZBa z>Q(dWBHiC)@GMPEM_eN2SaZIem ziBd;l9%0(d7C2*!xnIqi>CA`|EF0@9h#BSKjU?}Y?EGp)h!mLCuLkXW8(OxfC@GFk zMxE}y)8$KYaA#2d$-;6&k}mzg`UbGESWD9N@{6}mZlc>IbYOjzlwz@v4YcTY+ek`o zu*$+wmg^uJeIB^Ju)e;5uy0{-s_njx3l@yP`r7^uQBqwrl-f>XdKYn2mW;=TI?t8fWT@8& zC+3f2Q*LW5Yb$3A(sR4!R)@It+GGMux^#;UpF#5=eR0)|F-%4!szqia_dWmGNz#&w zde_|d*2kbp?-`cra1Hf=lfH32Juj>!hrXc>y}u~)@fan!3yOgdXG!OiDRhm(GHQi)CadOl%428L3i}T)|b9CN#`KMMY#D6+)nBA4;02vxzph zs4Sx@_|){N$;F{|A)j4=g@QDJ@s*F{$nXE|p3V8}D_>>n*dGUYX;Ip3v#&Yb@MqtCSRrD0V^ zf6p56qc3<3`+jhfy)r_N|KpRvKy2X1&%A-vNBv!H&eMh}F8C?V_=Vdr8)=4e;++p4 z{QTtd*J4+CFOQy{O?5Uj-2^5k8KKnHaAd9Eq66y~QGA}yc=d!+)9yiWz$p6>ikPaf zle3zWbwj1ea4l)M6QcA0ImrLG_U(H%=dtnp-jDn|=Ip}+5uWx_|CV#^cm9Aack$(y z@Nb^`EQ~RnxaS1deEzG0-|^r_JUBHTk}{TC1Ty8M=)%nsh{mv1loUo%;VA;fGIAv` zCi-uTk&5vy_s(`2ERq@_S<0lTs)iGrw{q`2C--d5zjnq`NFe*##J$@@q^R+>9 z4)CI%c_ttDz-Kvr>So{&um07c6yS}2^b2flf198=cUYv0O{!XxxKYtPp<}C+%F!T& zb)Au3_dsedcKf9^xU+p`^^M~qR3iNT!^YhG)RNzL+-aKgFEwzT=Zy~;^OOtVrH|+} z=KvdqgU>4X#kIf_?hX9qKf}Dw1ukLjdmN9ttLffl(mTWjAW@`Yr*++QU6q^y5)p|w zSED78mj3Pe4$r>g*ZE=n!mj2V;G-8mpDm68n;f|6wXA>Z)m?KA zAm{GqkiLyI9*(lX$&b7Y-CQ)@a>0I9_<QLdP3T?W%9NK~ zuHsz5{?$Vi#!=Xk!la@uIZY?87K`ArM?$NU7d4DW6V}((abi*f?lv%t7}-?{J7Qip zlxEEOWDSYRPBTTt(uiXkBb!x4P^AtHW9KlYq!=B-6>Au?hAY;X6k{gkh>Om-fGZyL z7)EY_lcG!PsM{5z>@JdUIX7As27DMyYX~bP6K69sOfVFSJtlOS!ckj;K8q2D$xiZz zR`)Y8B@-!`6cc>iB=bs*C?RFVhlr}dS%}C* zRS0;^6PmV3)EslyB4+vb#+dhZgczjzT=Thg1u#p_ta;rgUpXDbRqhV|%7|E7ie-JK zwi-<}UBi@s1xcbmv5S1+w)ySn9cG=GN+t3E)h+wXrT?TZ8jX^DBucC#zoo(yjNAm1o&;Da7`p;1E}1Li7+Fgppla|6F(%fd zbc6DwTNl$tYuY?yOwSwDblwR;w(a4dZ3tW*a7XR$aJt5^O{X$3C-P`wA_+@uGF|v# z%0SB^`B`CcVzKJ5IGkqBknDpZE!?b&PD{JBmsfGFX~f84DYQ@b)-ptUYlpm7dQE#0 zQPn-RLw^TIe#XdxC7>q%K6*MyaulTu{umKr%q+v$YhI=2TCNMz+Cg)!`S(hn;=M3x zM9jd1vD*XfP)a4e&%(k(qDAMmC%pV}J)^qu@$>bmAV z7*ji!f4?l~{HjZXUmXH?y@tPkAle(G=#tkSu=GrzXI8`>X?VK(LgaHYK|LYXS+nW7 z=&dF;xfxKln+A`ebQ2ncD#r9D1k{cwTtcYpSR)u54NaJ1q@j*;PQ<`US)fL!#V|&Z z*q}ZVG3+l#M2&o>4x9{;sSnf$o3+Pf#ND8bBtUE!VV2w`pvq{}*f`crnWn~hBIz|z z(Y9hFgo>!1z8F${&e4nuS5J)ruk~UwTzmFE|HnA^@|S|$+vR-oeeZ%&AOsG+_-9W` ze>n6L{~k9om~tOmpZLh&v(?MLhZO1^v(bcrHiTvqG?|Ab1G(fKTxLVNd8lc;r!dMwSB8R`tc7x z?e{(98BfIGICk$TZvXZjgU>Fw=xlscEHNEoXP^~(4LUTM*Z?&VPayh+c}&{+SyK^3 zdj^OtJ!z4@x@_9erV(iac0Zi?A3o0bLBF}TA>7v2ljc0a>O*04;Y;_Xw3O$t@5wJl z4a~m|I=_AJd2tSH0@+{#_t`bpwbGQx+-o)*Gt_lOtOIq^P&a`lg759{&FyVY&bB!< z-(h=RQIMhp>zMM>gBa{I@c8e2H2>|ow{!00_uumzKKH(lF<%`~PdyKN!WE2H#(S>- zc|eB0-!l$#*~2bJMEJ&6t{r@~zHftKJnD2zZA6Rq>@%dusFesCg`x2^$3sI|lpJ1J z<=p)TI4~J^TXasjG;fs7SyM8-RwSb~q8vMQE8lbJQ&Ck89p1O+IsDdd{TbupY))?8 z&70oz7M$CAPe0`;j|LvizKu2h^Y8rL;Ik_q`|yPxSqwoVA>UA6rv}}$zgMI)>uSjb zL31pi_V}RAo?ggghA)6SSA^F*cFZH6YWbOm+(#b4)q&5P=eY5R<=;K{K6qI_;`p~W z&Zz@@_Ku~`F0fp3N@)fKRDaT)kFBaPA+f3?BXE*6j-|B}8X0FC(~QgXzMAX&m7q*YaKr1trH>`Pk2&_EZmi@pT9{6F5TkpN1Ot(Z!GN z5bmDoLDrqO$74HlQY#i`3)WWGGU7#Nu$2mg*r7DqO$JSGXs95qW<497wVnpqSPl*8 zXJdLV7?IqJr)bLLAL3J7S@shtZ9>xvJ7y%3nnpaf7loLd(gkPI0Ki#;6T#X7SB`;_ zstP!piuX~m!rJN)%5oiR#;mVwFjvElZ-}C)KJX1y3~bGJm^D*&<|nX3Lfpyk9>als z8yq=$1ZzfFJ8xK7-C%XHLBxS6sAFJW&%0EQc4;JMX+gzeZDPL}DKjNL{plX7F%Yr% z7-&MG>1))E1{|6Qlt?y%QWbni=;LIx&Vh236>*Hq5t6|GV-oGs$B=2z4YjYBS95Bg z7>GV3<^z(n;u?FhwH9UG!*HRCkunR{ZS5FHF}v$~nh*%l)5JjSQ?#t=0*!CqqEY%v zO4}h>+UkWhtHahr zI(n;iInjb*JI{{ZclPp(+n-CbgZo)4GyNJKR)3L%!Y!N+4w8=nz|zQM(u(k(dJ-mj*jJG$q&G@E{#a zL^Y-Xn523$=6bPRD{)zM9G5q$>6wI?t{zE%L^A3V(IrB2-WY1Sv2$9p1-@>URI(wl zs4(<(3mpQji}ky}mlxCfmUXdcHYFqI+P%qkcbYP=J(DF40E5OivT#`2Ew|I86=M)U-VO8?^07rE>VgF`D7N;}qxS{~ET_@w zzr=OtwAZT7m>9$~Xp5k0(v#0ZO^7v3^jKpktfLMU!KXatnV(Us$6|>HwMGhO@tVxN z8ue_&z&v`UK?v$OKCiOjQzX~|L8wB&NMOg$F*Yrn?P@za(kdY~RIy5^Xl#f$CIb7N zQfSR8kupX~F_hVmj2I{o3X^DlqDrZP#E8+rSR)l4*P8YKlHI;rw`}zxJKTa=gO7FA zOGIp0aP&8Sh1IA1;C<>2!mT%>)?!u)%*MXcf1|OK_j>@bsj0p_bO5(U&P3;%XlxMN zf||v_-p%x=uMvdxauq8j&ZGxUqC|R}q}*&{940QJgJO2bOq9DG9;Aw@<(xCm=I3Aa zJT7{`d8cw7+fWTseg z)_tZ(Q=IcG2uY*focP@EzOJv6XI^A*0&$7L-+fQ=(qde~zL8K8shWnWiOiY`?>!m< z5&}DHcc-^q*GzrGPSddI8@6I($5+f_po)fL-@cb0d*zGx<-dPDlT9wIzysLjO!Q%*roGafGNGJ9P_x^18p~6JPzG_j&vueDBvOM;4fJ>17w*=l4G8 zsgI-*MM7O1Zd9fw1HmK3cSr6DKS_XepNahODL&qc@L7xdmn%P)ZlUpQj< z-N&!+vL7@&;rxF=5Or_h)>X@)5lrlTeBan`XdU+X$Ss>opKZW_Dh^Gf7_w7nLLM+A zC-b0CN1x3plPh+S>C^_Yi)((K4QAED6An~d$eq0Grn@=wg^xe&;WsC?*~~v#a^OA_ z6y-r@WgY#5=AG9KKfChmVXkt!VHwPINkr&0lo7EwSAsZ9NBxsU0hAe8#I~?Eool%C zhE83~n`kaFlcT$2#3{31^8mo2_&iKV|Fy=jS|;jh$R>7>B1WAgly^YK%?5Uo%|t~E z#+F!HbjVR8A=%cAGQz7&Cj0Rk8MzfiggU}WKV@saP2-a1%-q*BVNUQhoX5x~xk#V@9Jj>^|XBpZF54C~(g5p$}fok=1>vF*g=x zR}hnY4%+LIM*o(5D-({4Mreoxlohwir$72B3Rh4#%f~+QaSm_<)kw%Jm;Gz&oVWiF z7p!kE5@B2vU7gT2R~pl^Da1(Bls3_LpNFV5jZgG_A6i7D-51g|;R_*$EP6NX=tW&N zsN{k&sNzF%z>hIf#gLk6U*ki_h+AG_*~Cj#68%*dQAZuTm?_EqcW5ehrK3g?C-%Z}&8mansRLt;;YeL*(fpXT!n%|>EW3VgVE&Yc5q*ayl_fQ) zEEqwJgkdz?K=zo?)DDg8NbTK_AZl-;_PfPS8s7hVO-F4njAd;!V#U}bcgb*Kh#@~4 zEMiNnEpe`-um#pwoGDX6Mm97~rus5O7JNyA~lpLPBqeMeOoqN_TsPxv4R-I7|qE< zxoL0rTZ}2fOEshNNs3^4X$2ih+rEb*gGSRxlD%3nT~jTwk3KI7L{aUL)bw=LU+fM4 zEEpsGbeW!Lc45?vGA{S=_P4)BQN>xy*47s9c*h4BkILR4$}VJ(E&AIZ@Di7r>|PHj zyZ0J{eYaZ^J@h16$Owl*l=fP&JC<$FEc05|F_WGC1rV8jr#W-RF;H;bMi zjzTQ67?IH!@%3WkiOJKt)IDia5oa}06lha1zUVy8diAehHinI8oXs(lA;K6}NleKC3W9bhbWGUTM1a)O;~5G83>H8jc1yXa4kEE;RVe2ZJQwmGjHGbuN4 zVyOHaV_lle#mKChC8tXku9z|UykQJ6daM}{qwsx?eI!5p<4^9)O1dVsYBE(fFPg#< zE%?xI+nu-HXK%2*xkIfUXNpBZ-Zr#Ffe@*^Pg&5CULqYGE0ah=N`IwR&(3_xG}ctq z6v1I!L9@Ks0ZcWd$FwXnn`rL`e(%|tXdyE@N2?8NDT_fAB$I_aPNj9;LRadZ6&y&M&&L2+mIf`z#3LP45;|V;2C&#z%YPe5c{Y~~i?$X6+u8b*w24#m6 zP-|i|?X_1we`{(y@yO*%cK5MuKx~)EixCL@<4l_DAePctV(^?ic{~jpB?adhAew~O zq%^Sn01`UpQYz@v8{Voxtf5USjM>q>b*VL3vu;EwjLnFCfwht(FuuXr2~%HDn39nN zXOs{<#u`dvsDd)Dvtb+oXH2KVOD07HjSraYEFY<}&ORNkvjx_c_`1eLL&zRUshBp@ zF;H(GL&Y$!xAA_P)loqR$#l-TNzzM;=Z-sX$J&I7TDL+7o)Sal6F;c1sSrJI^Z+mU z_rIeLzv6yWvmHM7>1#N6bPdHN6w+ifygZ+cC?h-W*bGe`0I5dS&R*p|z2dj@qK970 zw5s^>XTHLD=N|?sSRJjhHeRDdi9Vn)HNBNj4MN-OMAd{Ih6W!zA)Bj3a+BX?|Dp-e z?>EdjX`)41wKp{uO!Cs{PriEI9Y%#FDrE*enx?^PqGzW;LCh&AsUvPJEV`cA3~7{Y zW3HWOvR_PUkLeM5ADa%X)1L8~^mVba(0_N|^MeMg8hL)x- zgE%`G;Oe@&U>-CEvKXu`v3Epr$6yu?PbnEeI^P({H2LJFuRW77I;Z?@Dj{u~lUbT# zvi8mpHJz^!p|BPwX4rCc$ZX4Ekj$3wc?e=q%>z!26vkrQq@(&rBIE=XYb*VdACo%at#gGO8 zMzUFPZumQS7YjM6v}5kLSZmV7t@b5Ci#c~EC+e~8R5};3%pB-TsrG8XJ7x2rqm(5{ zmJUL=bT@51|7^SFMy2qD&wq*Y&%8n(_oRpL!4JKc^|b>X)1^&p=xd<+jx5@<1MjWC zfqzTuF|cs%(IrKI41fk?!AKF6fo7jp!`}XO>D^F`i{amZGT!rmF#Sd~%N}J3NtV4D z@ZEtn%VWF&`zF)#jVANdj)fy>I43%oC1ziYrDN%TLpMO(<6!nc%2j0dTt{@TQ1so@ z_H25XBv&X_6lV%XG#;l8D!;aaf$tkx99Bm3CcVGU>-2l|(55&*62wJoG?suHNY5l~Za!T&wDn{{E4UTYR*6Lp3m_1|Mi|-=Wxb(N0~R9+95!i?(k!F-B*)iS^H6M zV#b3(Tx#k(&3wkZu8H-Wrrw6oBoTamC~mXU6-34gK1QktK@FIa+A9Z7ZScQu{_w8m z{H||(hZmJs^3PWfQT|7K|6@4b9OM2MT+Y`&cg<<CEA@r(=vUPykL~7HY&8HVTJEPoK6~}>d|vkQ=deA$n*$sBc+LOw%1rIt{m(!A!$*N@sOFxlp1e0@y3L2~ zk5_HhSBKDag+f?V6&dME#V^tCM$Y$`(9VqEH@;^KPbv7nuiT?Kzq;Y$f79$fzy}mO z=FS?KMm}-deN0_G(eS>bC5^&^&sgqgdx0==n);<~5=z`FGOfURSrB66mjmT~M#Is(`L(AHKRh*OmsP&z_=ly@dz55Um8hMzGkLNCD*ZLp>SWtIbLp+)1$|Rn z6ERsJfGs^048{~#87DcKv6O{n>}=LoC(XIFB}#)6aHdEoR+!;qod~g+F0FY8k4bT) z#<)(7qN=D7O1F|5mh|-y6B-{8M&l79>lle6kOl-X)Xb_)rn3`-W)r8LfTM{8h)e7) zn}^Ra()gI`jWoQNHS^?!lR;c#COqXyPh@rfh-uSLoE_1ZEhf_6FZ3sW?Uib+eX*I~3ng|-_JNrOnAWoqQXppR)V z;M>1WhH@^GVeD3Jg6i%vec;g4Zz#f0cA`syU+Mi<7W&(+&=*~-eHjK94a@jN_K@`2 zaUD2OufOL)nQI^{O!8XY?!uNj zOH>xdj3y((4wD;nlM!i`4gDO5!?}{ujTn^^oGJ70C!KG|(WEgu~UE-dl_)p`)2Aod{F69O|}dQ()<6q*VknDjMe4mC%b%BOrl|y zjEns^H)j9ZSsXol35O2ek0bjoU}dt-MwzHEr7^iENm+TuNEeNuJvxkP!Aw2()HRzv zP{-65R;p~p$aYX>0gidkDHV1yC9I0dEHs&N-*mK?M2%NWzJSiU^im{J8q0*_9=wWD zm_@-;0aczIH6};evf@!st^Rh!i^;$}uLJUo(vw_qgzv7iI;?dtX6sOLo z6>s^IckrWZD&Nh)9A)l+ARf6Vkv0BL$1j>g5iHxE7C zI-L26PcXXVWkWRiuJ>`*9~@oUevx^^eDNlat zvrt!1o!I8X|L}h5YM4Pj-JbHUx4xTMH79z{qrQK)0o(S;tu$nk#RN5hk_$@OO4eXU z424MzO_iymUIQVvx=RJNmlPr+tQ#|OD|oLw^4zDQdIM|gL&nMvKJvx@gdn!<)8aRC5ObT}~*IqlMu)pE;f5)?*{Y3WdJCAvN8*lxqUmtva`)zmk zG@a=lwyyl6BE8s~x=H)A*Mhd@F)fP);}%A7OK!C7o8X?Y;s3gF%vDdeJokaSn`ho! zbIpeW|NU?=GSq`}(H;sZ1o_SMI`I^uxC46QVn6+YnCMt^Kc3%935AndiAMVwYANU%t zKeWa*oXMR4^I?wc$RM^X$DMiMo?LJwOQ|K^7slOq{wD=meP)s*47jYlOj4m#JkNbY)ui`(h*>|~Hr@SAncc6oSAUYFCVlF5w=M^+O?_%C z7;LA7UoMOL{%A>gB!j-cRrP9514>U1D?Rj42Q>ATzB@Eg(xviT7&c+jvtX71?YP$& zNUzj4r|S-6iPCBY=dbyx-kryioU0!<7CQtt_M&9S8geMRdu+SWO{>$Ul{kdN)bKGi znkFBQm}Kf^O}X$Y2#I;BnQ9)jVdQMLI}#|Dk1?je4~o%Tyw=Ml}OwIS2i7sD#ejj{}eM_E=N+2pII6V}Wsi}{=- zH|=RZq@E+>8Zfk#p{DC*Br*S_*YXWJf?cb7>FcKVUo#kKE$@)TbQ)4ry0wxQZ(=JK zUDKZABer8#w1uOz;M2g6rcZ7+y&;$B(c!h%it2!b-MxRbH#9_dH@!6Fv}hsoW-^8kMr`sWl>is{dwFH!C!hq$aosf_+CpymQG8T z){4=gzFkj?@hwF>DNa#JH^CT(sM5p+<6%;+B}Yb0I`N6Kh%ua+?eUtbE`1EoNtVNA>+MuR}SNzfoxa{(ahK@h+NKMOhZy zbNo2R?mo$(UJm*L9(Xa|_rxo?`lFww99iD*U*E+2FT0qt&OXX~Hsj{&Z()1$B>UFR zV154@Km45C`u%TS_sxXT*dkGWB_>moo>5ke4YOv-vFY7xH`|#BU>Uhlk|zY$U6(}5 zO0;N|uxsFc#?OBVvoT~W%)j#U-22uyf=hkU{5Fyuyje8Z>rePN^P46#cY;3#-~EHD zm|O;C?Q-JQH<{fO(RsR+tB-*7E8o5>Xkh*=ur~eC7Kkg-xpaqF0S)+hU{=pj4a_yB zFem4*#R{&T63v`74t7gtWpx5#u&$s9frG0D_~pO`jud+FscBqP>)s2IASf`-CuvVnu+D?-+t5KCk)3DDv2aRPP{ZQ|5 z)|xt^Dn~cZ<3p$Zd~gikx%Io8f5DX;-msi|_)NS8{^tLE8^8HqUOxD~Z(MgXmtB58 zjvjabXB-&wpa)$F><113-vK5ZKJWnUJ#jt%`>h}3%(I5)`sTlVFR-#ey?WF3GD`=V zCIUs4bjAfsq>WM`=e^pz*JFR(rW-sL_|->``SC{?p17GrMEK(`!Fz9nOAbI;!}L3mYa8LN`kV|bM(77&IRVw+=g;7s)h;o zuz%}Mcb)2te*js@^!5b;BD=H$39;&^qHUpOAO^+dT+c$Gq9WS(8j%W9)js4!M#< zdrve*mz?J{QC}k|VZ+!0V@ry11b9kYBG%E&P7uPJIwW`V5EHU1QshP=F{Q*5_?X8u zs)!|}kcbjvx_*fXX2913t+?@)ZxVx0*cCMFfRs3EkQfQplKV8x;BaM*(#(Do2P~3Y%zzpCUFRPa zG=WqaBULla$LAJ&-BvZDxq;3+4kHF<)8H+>oUJux`<0-XeTyUI0Fo(v4S;;8fO-x1&kPcjuY}x(U`Kz4~@r3KU}N<0f$L> zjZ$>9AS;F{Mv7d}Y=wqdXt2eIs14gbG7SymV!|0}sWeUDO$jkA8^Pbkkl zf0--G=11O(DMqL$Mk~+>8Y0+)s%u8jRg$6JYJM<8jaXA;gf*rSL>f5~Tjx=6I2lnE z6JiWZ$`vkq;Qa=N@P$u(jrYC%qgY!IiPRyW*723ke~l+UeQ2ikqd)m9%F%=mzxQJl zrRAm@ZsMjJZbhIiH={ z;t!wyO0N0RHMolpur=Eu2z>0LZ{@Dr|2t=#vp^7!o^uB8y79}r>u>%EACwS0*MI$+ zh_Q)+XH)#s6;FOFmp$@a6%~n*I#0c;;0ZwK zMi@<|E@tqATmkjOs7!Y%UhuN#4gUW3yyK%h?U_&H>_hj*Bo{s`!cBaEJ8!$2v(7!* zeeb20UBo|s<$50S@P`2VIkNx!jFKqGd7L=D!@Vc2=bi8TIRE-z?=}*A^{ZaR+WO(4 zj;b~Ha7nKm8=nCh&5W?c3$#TlS_-V7q^zUd>*wEj^8v^46~m*?UcT4?{#ON4g%b8x z%EONbKK>m$Y|sIoP;k(|Gr!XC_ZJp?XB%$c&hLZ5;2oT`0z(YJWR;&*--u7c|Tk1toC)%l}nxz7=?X z`82NP{W#9;Ua5vN`O+(1HE7TQ9;<)E*^j;iKL5+Wg#>dUA>~wn-XGQPvdUvPeD7D0 zwP8YlfA_W2+>OZ5-VL>Ba@I2|Y|lAJ@)nRhsW%ulHa3_|cc_~=1R-kZ6{8ERrf-jx ze*s96hDpw`PR)NrG||XQD~w!0VGX5C&wKA<%KCOCE8{WKd5za5Nz9sA$1*ae$VPGn zO`Ij?_ukR?7&_G6BDv9p*~~M&dy7pik|nH|g0UN;!6Px^eNCetOhPg>L}s^7na05Cxs#-m%|>h? z`h=kO5%ZRz*44GjiR~#3$8a2CV{Ns&#oBW(Z&`aSy}gS=vaNQsE#OUN%_VcIU?MT5 zjBSZM+PEYs#&C`8(Qb5l>|s-4as!3J&h`w?y}$|^YX_G~Va=<4FH}4B{MDCCn7|g{YnuzDnkQt?6y6W7efk!pIV;w`6?PakKkIj_n3j+0A4YyQ}FCOz(*0K0A%J zH#f9S_|l zG{(e|G})Y4dV`ss-nms1#LPTN9srqjUA#1KF|}p!UPzRcorJxd_%h==qEN z7`x}gu)$~8Y%9CF&;HkRd2jV16(c={${zgmzLbBkANQDp=-!R-UgMMD71z#Zq5U!2 zYS3FIMc*`1rI%a2#GKhv*VsSS{xxBEP0SLdMwSvDmaml#Pt-U&0+g{^O^=({B;-mA z9>LiatZ~#iQe@^U42D@XV{9E!lWS!a8fr;swHSvZVl}c8YA_CuP-)6=wnVmdMx~yh zo;#<eak8_4kf9xylY)^M>mS;TYhxm`LeFYDH?WVln;d^(dX}#{^Q~mc6SBrCDpftBYHC!J zrk+yy6cv-=ma}`i9A0Fh8mMoD`mTg*$IUNu_v^=OUfm-p=z_Fx@;$F)mo4MyD2IMh zIsCler@Uyf=Zu5N1L4ekIP=s<U0b<4^0e+ zH54oBtWH)a%aZl+D&=Uzq%6Bvl9FQRs;Z)D8fqe+{rh(_-AuIb8^3T3ufFtYeB&E8 z5?4x^m?&4#S~kvj1b_5vf4J-3oOkhgyyP`M$CW?uSdJV$lA29b%1OcfA94xLeBpEU zY|f`!J6!vfuVJj|p)A>01VOU-U#M#wE>GqcW+y%uyhaQ~IqqKMCkBN6GA9m1R#r>i z`1&_--8DC`d1{+qc;0Jy+B5$Z=N!JYEAwLX#27ieekMP3$xC(}^MfCDe^lrE<)8g2 z_nf?hdrlnV?&J4z_pw8~@|Rx4bZ53_bN*L<^={zqPG-@kt^pJ+#ogBl(UI)ZpNp0l z1EEDx#AX{NT~M~&Vn~0<$-w!Wkq_R|dx-y24R4skTo>hO6&{TQ~Q3seFII zGoNeuJ=<{AR$!%$VBn$y@KXom-gvdy4vPwxyqdiW;^CuSUf|3CBRrQ?%)nSyhB_Hl~C@n!y* zV}JMAU59+(=YEOvzIBX)ynr&!DB?-VSh1#$=IlRy4@dL^$gW8QoOt?+5OX-U9F}2# zX@76KOmvr&4V)>KXvEh|S1_t5CR5Nm5C~z}D@#nzjE9(iE}<+UN&Yi(j@82AXp(hi zm_zU#6S5Uaz;VsFqcJrr0ymSLB9UQT2r2nKw0cNNFj? zrW6E3D6PX~ZbTD3^ZY$A(wVq*EL@Y=eTB0)Yp}+qwJJSKvlp=?xl!$>2rZ#P4vEzO|<#BSSCQN%E&lQgo?3S;gqkjIztJFn_x4_bSl(oQQCr0HV5>^;M6e7 z$oEv+7gtj8%rRJ1<{DTPL!%WcNt0uMX?7n}Q-Z6(r{!*q&5c9OnibEfcfFs@k9`QK zg%}Mo*+*w&N7DCW+FwI$0$W@P5X4G~pe8^8w!fj!!U~&hWvrIv(RgWag`EPGqvK$Me?z z^-g~3r9Zyw*v~%qES~qmpGXC3<8g(%&*9UxU%i$ue&&me%7P|SdSj0Jv62i#+O^#T zpPX}}M`J}$*EyO_w|DRnn&=jTtTxN6SQ*M_0^Ipu{|#;p!cIo7%cYFV%zUfvgv}3K zz{Znz8^Vq5|4NSBKN|!#r=Zw(pXa*up+^$$O#@nqd1!BXk~o`@m8h7^R%?P`s*xBC z-<|HH7!rZ?jR}WzgvR+Gc&uC*qfV$3$Di+RZ}PH>pNd;qXHG+TxWtEwst!zjO#@g{ zV#fvl^ymM?Q-1X6JnmmTZr8D2`p`?c^r6WmJBFAW@6-SD(RY267?QrzYgk0c7>kBL zQRE#^H5e=&gUeA2TgctnukNh7|s&2aJ4xaz~r?YS0{fNQm^_z9Q z`K4BjQrqh5hPLb~u{6CRw=)XLj30-ECjUOOFd!)fe*ZHwPJBU#BQP^f66kXGv7NUW z_|b1SykTF#?>ue~$M%DUvz~5vVJo9{|H8@M9=Z0jo)3TT1lu!Y&1~HC0%_k-O@yd4 zSR{I?s6->w2-~%%w1NqNkV}o2HRy~|Qi$zdt&UIdX+CmrCAY$7VF!^l+N0Q>!{7ec z6};&7layx<_o7^QkPUq;8@%@a=)P`EewQiVMPwz{5TP6OCv>MjXv{9mV(^-VlC3s* zI@{`MnpW!=W9*91R0w2uYU#ykNuNAvI)n&>^>RdEgwlYI1|J%X9jAs>+6?a$QP|}S z=e8}z#43^qrCT9nhv(5~KTW8x8mXgC171U-O^3+*PUW4S{xiAc(u)A0npfO(!!2BY z-M8?u#>NS?uW2F#A5$ZpovB+a&4|eZ_Y;Yho!|4sil9Cni&-INr}U(uO*(YM5Q4`T zL-0N$rb*r+V(>AdF<_+0QFj@|){u$V4qsP1@bZfZA#m{n?nm9g0R!`3q#1~7F$$1W`$Qq%Ee*Cw_*}B4?)`NmF}P4 z)y1?!4?~ZJE=i`aAgD<%8ocLNAdCC0y)bQE(spPswoki|!^OWuq#q8p6#Qw1sQL21 zXLpX^A~KU!-X_ELv0EP5*M=I@t*#9 z7i7Gb&iZuL>U*K*dnGB#{~?`SPp?I1@U-adOI>QDdym_0pJtDh_G|R^wZ@@}VW58K z4>4qksy;Z^9YfzMX zFD_Ty=h_R$k8$T~Url5+J+L*TC>E2@bO{m4QF5#$(d1Pl-dN5UuR)mQg5M_Xo(dzk z!v57WG2%J|rz}fisIk^C8Lu#JDx5V;x3^GZDT*Q?@-fHoMC}Uv5F2jz#?AcW-@cpw zpR@B0x234k_ghuny~D}p<^WtWiUCl95+n);q8KraDCRiMU>ucEF`$TuVnRnm#hft= z1{`%1bwtHL5F}mTa`Op0bf{XtKdQQS?|m+YAJ4-B$~|X??yjn}*7v>NnP)xbem~3K zE6#7f<#s;z@hgd9U&J}_8&-$sLVQB38lgsmW0^bqs476dEQ~ zLT-g7WAxP`MmI^cLaP{uVs@_;y!_OcbLDlPqLo=!xFA}^87`WcH@T4L!1IE}Rhjpiga@6%8LauW_Bjf&Vzt(hZLXL!x*KIl*& ziFg>KFNkV+z6w^)Ea#=?K8rQ$S99i5PvrmSuT7h_v(G+{V0Pv}tnCF5ld9HN70BJq zMuGrYmIbL@>GKSUU3i+WQU(kZqP><7W{j~`DVxMm#4h<;Fh;L)%KBci)rYf4g~1sfRGj`I}I$;{)o`cGjdyy6%HwU zvV;R%>YHTx zHl7r19Z{TMl))A$N;iBVTy*_)b%lpzRRBT<&JLV1C?b;#GUJuP+T6#p8RMH3%3li+ zwdBNTT~Qt0warQMl)NYq>&Su(zOWV@N3;S#S=Zv5y7Xy~V}obcS{s&csrb)Qd2NTi z=!E9$z#m=0FLzA~suV|RFgq?mgc4-Cet6=1nc-|`=lC3Z1LOgUWr%1t;*j*>0lMNe zj=~@JL*YkQOOd)1T`&0@C|Bh$mlM6Jj;%*A)pQJ1(G)(D8!9^7$kd#nC%go)4IJq; zrBZGBC$+U;SJd~CgsoJ4T0UZ`trL_3d47mvrF2z%0=OoSxeffEZE*9R94Q6~u8PG;PE~Aqh>J(r2zU4da_SQc2u{ z$^u8CV;aqbSSeqK2f$(;&dNqZW^;e`usP!V{u>yEe*IIXY9>^-OSqPP@~uK@Nf7!6 zD-SM&=!$Q1IHjsMNhQ9(S7n4(dD+7IQlThzp-3v%-YTfD>_pXK00#*cGGsF-Zx=U2 zJ*ev)Tz~?OupLILOsz)ZuS+$LuF1dkP{HeY10#z-XnIIZaiwY#c&*fsoZ2Xc)rysp zV`-gQej{!~M^IPy41K;jIKEy`YqhjU-&L_K_w!Hn_Y3o-Qg#`6pA0?6x{7sFwKRf( zouJ}-3LDN4L&MXY$ai*;VU9^@TXNAv+>3ku4GL@|S-C(sj!l%kEJ z?Q*OuSl(VvWE#Xe!r6?}=4j_7zI|&kTG6)_jUh1+%_!!9d+*QkW&=k~U**h8!6HIS zYZB#{(AsaFIwDrkk%FES9XkBHOs<<5%5MNM<2rgShHJV|P5+d0G z9aFR>{p&{Ve*GILHgEZ-4~FT@H*(jjUO~R#&~c-tx2BR<3J%?9qa&Z8>+%8>HE^my zugJY`Pb(i^VIn5m%b02{^92Bc4SB6nT^^@8k4foA$~GbPSOkKuUS+A zwZddQ%?FE>lU|t0t*Q#}y62}#O>zt%&AX7;s2H|?=@ZOH!xju9My@tgf zd;eG4B}q^W@e_nkF(qL zlS%Q)5QT3NDy7lJ1UYNuC+o(9A{QSVUf=XaTj6?Fa;rh{{=yqF_WOCx+kOT+v;U8Z z^+wCDKArQ{2ek-i_#wuVpPoQuf?XD1AqS)AqsY^YZl1BDm(t5~KWV{dlvj<(1#ex5 zSI*Xwl}EjT!>&|UJ+y;3HihO6*72cbV{E_bcmLe8np)=6xorNzxhS)|);&{|TEo9a zfd>edy|j)m?C{iY6{a}4oO%Y?u%DXga2WRRrC2BaSqVhpQURd!gKV;~lKnzsdNF_6?a3?^(D4Yf}oF`RBoyz;W z?r%h+6wP>&vE~Zm*jwn7F@wDC&@gu{4|`8O5S}U@U1-Iku-?v9HUz?T@XZmgv?`no zc{}^_9Tw9rd7gz>C?PE@sr7$51fzvKPlG=nI&_1aJSQvsI_scnDi3JwBLniGhtduF zqG}|Yr$$OvLw9x<8HB`~$ix-vKtf?6T2~F|yvWfyq8TSlHOE;oF+n?O3`&k*n2Yu2LxaORho5 zO(^B%UL7CPiW@YvCNW-_;+%Njxh=@cKLC(GZ@*tQ(w#rq7h#HCSW8+IZQt-;L5r2 z0Ek&<$Ea{U)W{t_P;aQA06+hKYCp{>0dc|-VA|k*bhR7W4H{pHCGf#=LO)c$NsZg! z>hWJP^0%ro;R)YK`OJ?V7wWusRZc!cma6iH?C&*jqLcd*u5lI2WM5e)PlwgY-gM)AonoTCyf3FR+T5TqX zXsDRX^_UV%;R>`;^h-HWkb-r?hT3BIOAyem=aQAuVBQ{1yCO}|n z+0o_iR}p%0ufapoFVJy|OcaeMqM;*Z##f+qOfw!MF@_@Rh6$a}>-EBP+XQe<=rp<_ zEP@V=B`^AZ+7MW^iP01`1*M20L+&h%Mw6TWd@JvKW8zvF1^0K**B9OGAYEqgPX|)lQi)`U+)dfXCDr#`%Nw@m@ys>PT?Kl}x zqx_9PMRW{v*WAnIj~IG4Uq~$p`hL27IlY_S!M2aI=>2+A)l3bjgb?pXRfvlPl2D-M z3z()CbR;?^D->C7+1^jtk@nb`rp)zv^m9iq^XQ&D_3x)WF@e^aR@5X?306TIIh38BJFS#9ZDR>677 zEzIaBNQ@itTv}whUcp3z4+8;FGDHCM=t#W{eJp4KkF~Ay^(b*p3BK6{|jqY zH+>ehX=6;g*6kc@s%0|_VDr7(iK0QIi-d0aP*e$yTnjWbYN0o{AERA#S^rif$;iTg z=3g_O``=wY{dvn(w?OwF=`-EuxbrJH7yTSAeL!Q7GN}fA0aJTc8ntL}J=gaIy7e50 zBKLmcQVuC4aXI{Wf1{6%Y^<3Y#1Re8Bx{n^`g}|+IS(il+gZo^AA2xYsYft(%?*G1 zS>4g6`?ODUZ*v7Z`6kM&3=PqGh;uohFkx!e_HVp5So3maXlO&EOaMLkI51Pv%h7?N z9AS(hQc=}ZP%854jP@`?r>L@X%aELgQH&WA*7DL@Y%t1VgLJ)eb1H=m5n|QkU%)v_ z?u6VqvcloS=N6|$#?HkM1dDSy&RMiF6jD@RAR9=&Mk%s9B`tE&BBLlW3Y(MLoYZB$_zxAF zw@WR5OY%1AQ_;$o=^2Uiv5yNY3|Nn5D1%9ft^lvdSbb1?mqYCW|!$2uRIDj{#( z%isO)PK>A0x8erNTMfpe$ivbk#H~uXUIr^!*+{C23^lN2Qw*LhH7H(pZ6j4Rd1?L7 zJPLsu)xy@}+^SV%_~}+Ma*8gSEdvj|j7;y!0uG_(kS~ea3Q z98rdIAzsm-l`qtrk)ahQwBnYZjw?mOM6{EH$)wGA(q=kt($JbndGELuM`|5eAf-pz zGd9YCSmUSmRpYU0k9Y=zb(XyFF@;(wA{`M&nmCR^(eC#`qqUd6t3fkgG2CFU?w}C; zCZVWyva}X-&Q{>ys+1M{S{ZWYM;k#tK}Uykh7MMts8;gT{pLE<2&&W)4~o7a4nloW zT$^BrhCWsF*#HKIjK*wAExb=^jjPZ|(59wuA9$lw_0J&!P}mECEl|0k_Er6rlpe4v z>VK-0v{cd=HkI{ zY0cN#RLEBEF(*XJD5pc}QGvL=hj>t+OcP}^D)Rn6tsDLVX@iOqc7FQ@EL{C#k_YZX z>wp8$laqeqwHE1iu?ro#fBYlGZTEoDM6F4bQWX6KOwz^`eQcJZqZn5>L`9h93}$k= zx|lXi(T!Qsv`?ZmD`)m3FM7TSqM#`HKK{bybn~2kzlQ*E5|g{U0&T=9Mr;mJAg+`V zdY_#?%;X`y1tKC^Q3LA=Vxvg2jIUhz4Zi!0A9BEh_Gb4D>q(jowD!~Zey_)l?K`>o zPk&+SrcGY1*homrcp3yzo~~I&bm*`+wOWlz8kqGmw2rWa!$dJ6IzaHs2glIq51ua9 z-{P0gvHd*dmYUUU7rL3!6Fvmx)Pwa^Cs5oA+dlPb%*?B4AFwyks$(FT@;_6|A-#>{ zTd${g^L?nkcRIwSIyS&LRSiL)jVHF-98`OQzt7UmJ_g&uLKixj2Pj$*nnq)lrf&;6 zc^15$(C_q!S`)-GliWL>lC+kyNRD;|(*cmAq``gZJVw0Kvj`n(=Q7ZS%;rA63B~k= zS^o6L>v;OXPvV)cIh#kFb}Z8~GhqBdrN|5V-5%TS+s2)@-cFYKXGR+ph{l6FSX*|* zgS4xuW11~L7T&sb8?B^?Ei9>ZG-5Ayz6s8GeyD9YVF#c4dmQa<&BrG zX+g6=;Lh~?e@WE>-ct3yJZQ#W(JBJs)>@46kPCs>j#>RYX)z+Ph+;H7??%nY_ zKv=P?&8ig*a9yAQE+2sJ#0E)WAgR{m`1jql*~|YZ{jueiA4s;rr_=|3)65OlB{#0n z9oe-UO6@{Yrbfn22G)0km;a{E);}nIx<2BTBNGmoh4C1gVKeC#(C<37-YHx^r?_(} zCRz#2;Lol9YR54AX{_$*xvK|S=te;@Qj;9p@E-H(Eo=IBZ3kfdAscGj)re1Tu zdYylzPFB6so>CZuNRbalq45AE>ud$Eb>d@n2IaUnipC};)`rAbl5iL+ z@0mh;Y-V8{1&%}+bjauig)vR~c@Kxdy4+8rOFFl8pe;%#I4LMZV{MAE-m@H=ac=p` z9i;P)+1X`)X6u$MY+)zO`^Hf=qA?NCo@~?4JM7%j)qQ}K0eOWvw6e zY8C8gh0DNZbmt51nH&6EOwO9wCTZ}ymWN?ShI1{iMv$@z?xo+2*yq1X4d7O)b_(a% zx?_PwHV+-JX5-bw%{V;AswNn9BL}g<7n80Q^QAO4w=DE`0t+J(oV83(w}K*LaM%

gUa@rQh2en)*Yq3&zJ=Lspcs^qybb2J~n(*AqvYPAA36;Cmfw?-oF@ z^B28V>RLR3j&%On6}G_A%&0Kn5#>NK9D)r=NKqK(X*FmllrZUB6n*hz`P^k> z&Vh4eMNx~58w6sO1ObB!U_*jL-Fx;uXm|k1b5hFy2v!0Wn(>wYSK+g0OU_47i@312 zYF#!?bU5G1VDFGx)^*68*TJ4Jn8*(Z{KiDITVu4FV{D(_MxJ>Zj8?w!rUNIWN>k99 zVHBlCDOXuNN{D+sT2HCr38{?uk7a1rsg?R=BEwKzVA!XmZi*h5rOOnKp=htFz?2b- zhYWeQ87k@-X=kYFJyo%WGEnB+@03~BdfxxAtZihUugS$l4XexR5sR{mBaj3;q%4Vo zQ#I?(NNJ#JON|Vt#_ck!p;E)E!Dp`X1f(8ysqSCiU+d*5V+$q#Ukr z6v|Jfds&ad8OEA3h|TD$l(A$SU06(%VDl~}PUsgo7E7cejLV4i1hMuKr&JqEmLg5> zX&={$rWkNtayv(E1?M6XgjDs&tfnJ5%Cs0W5e*bQV4=UjcIPRkt4%@^M~Wdf4Z1<{ zGG$_NZRmx#nog0?b$#a2F1xkHv7N<=^)PYf6WMmr`@poTj+V3( z{BcUhs5k$`!jHgsPXDg4;uuVe~8I9yQV`CE>e8geZA*tKxk|Zq<>!bckRYPchiOX5cm4~n? zaJ@S~z^s1?jeVa_c8fo=)z`S8uACk|dq}B3l2BBaESVVGJS_a;R)kxJ-O~001BWNklx z0NXZgA!?@}#b0~_XFla5*3Rx5ezp!oDl@f9AL}3q zwT_0BS~q&d)C#m-59$_v_LDycqNn!Wy12!v0vIO?%8e3im{rtdlm|#Ib~f^y z7rZmjx8a66f5G(l3_5u6Ma0k2RG1L>9fTgvH|Fo_R84Pta*X~`=2z)mdCnbwmu_0> zWS8f{q4!w6b`Si1(}2yO)gZ#fDzMYgkR^NgFmzOW!5u|#;)a?MWo{9cD>}+nyPS*= z?yV~|Gh=kVcRL8Q4_M8^SjTU;o0v7VeQ>F>Qw`yJ)W}|0W{pQEwSiQ%=*!Or*{<4@ zZe38Th7Kc2)uA_TSfl20GKVR?wr!z0+GNU4X-l7=IG--!T!_XLKP^%|R|u^l zKgCeKV6hdkD=1Au6yZe6U~@qQRY(!wdQNnRu@sCbtSdsOl7H_$`Yb@JLUFEw99?Tq zg|=0L)6f3267r&8dbYt^-~D2hG!j;=UWRj)nVCtxd)38ChePU}x8KXf@3=yG{XTDf z_e<2tryb4NPkXK0e9K>nqB?-d;;du+y7m0@S6@-r{NNY7^f~9r^jZ&OU7SyO_3K~E z+0T87dg1e4Bj3I1H$3~CGx(QhKThqp-=4&Yp|jZMe|~%|=e_wulJ*uz5==JBTBab?Q)vH(HM7Z(Bzwr6beO*3y=_Slgukrq3V{q28b^8Jzy7XP@ zv1dG*GamDF`RyP6%(*Xr5obN~@oJ9^yA#KTxgGQT;J^1ANYiP^V?T0J!{tvwF+2VTx9*K4ZPvP z^VPA(9?7cJs}K=xzUeP~;d5V=_kZvL#>dvyz)LW{xPuEWc$a$G)1Jr)$3IE_a?7M>zKx@N+`y4MS=?_U)sWpH_EOUz+dB$1K=ULBwsyg5S`!O*-&fNSw zH~je~KJkgq%2&U31ruZI1Gp?)m)x=Yo*Ve?_pVh}e)dbe?)9&h70dSLLl>N)+<3FMF-bPES?^pi-l%3guR=V_Wo@vgT`cHA{1UZ=o156 zl?btg@&cB8Y8akJ_H~f&I2KFz7{X#`)?MlK9Pv!M5uay0`#{%W3nR%3 zZWqzyWiEZ7*Z-~J+x=tEw_VMat8Xj}R)##~^*Op4_K*w196wh-!zhOQ`E}KPb(9LR ze6CicC{b0Ny~Gz^M*hiY)LMD|s{c0`K3I(c?dp9P4N)nfves4|r)p!W<>|N}BkN{! z7~LdR4R+>zX4*5PHX}lCE+@B!-1U&CO-4k=_G#&eaSc0_Au@(04jna^h$6%mq&jA2 zw#Z^(nK2Ps{2?QvppC+!iIt&mQ>?P|i;T2LiA0EuKfR_-P-5s{X_<(w()7hKm-j$4 zxy_i%GZu2eW()LgybhN+RHT`B)G;*n+>5OjUqt^exB4$s8C<_p^G9oR;{EBS*+NGR zKQ)WMNYfvNtUok_3Kf(_8;ugwQK!#Tr@b#$bu;B#qydY{O+;i7Gm1p$iGmm4h{WkF3 zFJH~V++qb}^YU>^A?3rZLgDGT{3Z>ovlL7Qq2H;x)7iBPQuc@9W6;1W-VXa-A96w3x z=q-p!K!e@*SwB&aVN2JMTkDwv#^;)L`&}X%Yw9 z@BA=VfAEV$Yufb0XA1Ag7EvhXi;O54r*H**SJ2dnk35Xq6t+iX8Xh`hBAPm;ktD<> zCNDBnXwYb-FeWCmdF7jxnn1A%$Eko<1`|abciIW;^MJhwH48t#>VKi#0HsNkA=ZY5 zQuJ&dyi%S@9mmb;>ipq1H}jx}?Z@d)K8_o2ypux?cr=bVre@kfK28Ot^O@tey0_Gz ztvT#`tQuv$;nNIB41U`;=%6T3wJ0Rt&9}7)o6qL!EiRC|`}p!Vf65t;JqqVwaj_5N zm1V`hR@o3Oqlh)Qf*q=WsEPjXyZs<5g5sDMw?PwsHbrleVW`{aE-~tT8oiGL%*}+j zH&c)4961EYNJqJ9K!x9zIXG|yoc$1(Y5HlgzIzePW^jtFx;53eSSk!Dn5P?iGY`L7HFqutW&9z&_Wk)a+MvsQsLJd0O#)X~ z)~sF5QO7>4YK~fKiIbQk9(hQ$K0fFn`|-w?UW%pQ@JAj*qtW1y!ydq&Z@38^nFp=o9;P0B{IjLgT@1+wUY(KUIeYK9Cs$ngZZ$DEj#AL;_UU$e?6dC% z-ua$4sH2WKlt2FVk1`NiTi>uznpPvh5mPuzGx)PJ5ehX75Zar)`QeTKt)`}@SX}6^ zZOb;MXQz4S!ydx>Kk{Dnn8%&U)1UPeSw69Q&4cbn&Y(jlHg+!VZ+ne8~WR^Q2Qb;!%gIQy=^Ukr}~?kNy);q`jQuk3E`?U4D^Dk_4ro zv(TZ}?{mn*AIcS<`LMe50|%4m#V(c-ztGGr&heaQKZ^_A{h!K25!<)#;M!|{MZ4AF zzylw^o8R(Ub;g;G;e-<&CFA33YoEWv2`3ywyWQr{Lm$d{=lz>{%xRDIYd5fC$4(Zq zolGUI;B{BwkY37`9rK+zzWwd1)!~Of+yfqaJ#M@GPFAm8$>B#F$|H_AR6XMvPv(hF zdaO)M>`|RLy1krZjy{61c8jBrc?5?Yay;Ms&ZkupM^zJSetw=PidbAsIq`%e`NStL zQH_RA1L|~obh};l-+v$8d-2=U2`3!QH9!8DG@1#II`$}F8^UB|Uvfhpb%oK2#Ke_? zq1k9KmLymwp#T>O@#}@rs`6-;Ylnx1si4x)I6M!bUkFB7l1SHpnuNpwZ)urqwR}{Y z*h)c}I!ECO5);!5Rx2kCE1vn2yMn@&;MJg-sKuqZY8ay{Xj{2%$cV_jCJT2%J4ZQPR{nhs*%o~BGS5?PzvGTz*^esF zb_o_!)gD<|v|K3SU9Auwk+s$H&84QHW?*F+<>XXS?79tRltNzflUEID*+s=$uTW*Q zDt03)lUq_aFXg|;@UI!AxU=iC^FRAno#jy8YkEjZuEMlhh6mF!GMtvBc)7vgUv?=( zT&g~o0r#&2{OP5D_JOKow0WSGO4((UkT1hI^0kAQR@y`2ig1CP_^fFOhYjZ(w#Yp! zB23THe37i^(zhL~>oecqL7L4W${(EiS%J*C)Tb6NDcf?#oPv!M z?2w$hifoZfcjb@<04?Hg}>x0cHvK}t$Q}heiE~0ElNRt%Z=Rik|*H$4Wd??t*HZ7P!uVP z{cZH}E_r}uC2@nSpA$zhx7~afU;Ox2>2-RDz}^qolNY@191eNpLs{r7lIIy#9GNWw zP(-R?Uj&N6Va0cI%WO;^DO7rd9C4YWiKei^VwTe{ggijrWXRAKCOpbZ|EH%WIQtbZ z;z{S6MWY#`HT>ziKXBQ5KSZn5LKO6ReZ+Vlyeoai;fdRthJ0@W3s)B`d`ltHK?GVy z{hnp#{)%`%kZfreQmI||(`!X{`J4w~#*ziB`aK3rG{ql7@=j6j5HUA*3r9Tq z>Fn%eoPL@IYhCxN-?MD8#YB4ep*tZA%<&987a>7F32aff7kUWFIBU!slkty_r4pP8+^l0JWt`AL*QQzhv_Cb z1N!^75U+nDgy4ib=`o242TD80=?>ptM;jsxe`GnIjTG7K+i<`EUwZ{F{mT{}gr?x0 z+5vZPM9@oUd3CVd2$-pq$EaOkQ-d$Jd`Jfq^s?>VL(7fKT>LdA<-->CQS0Ba#5F2awgXN-~auN zhpawLjymKia@mJIEyftOY~IR&vnR??4?9DSJ>p67fZa}&nGM2ry43;QIv*|EuT#g*??;}dOe`|BM%`Ef6nb?Xn8efK+BR;)Z)E_~;u za_Z^F^OUDQPE`;$@JbQ9#62%E1R;sy%A;NHb2;bu!nI#e_iftD$wxk3cH3jU?Dv3u zW#y_3a`6Qp5GRh4Pd$%x}%rL)&NwY_f2V^2GccfRvYDz^nMdEvRT?|ysB zX{S9}jy?8BS+QbY`PDCf!^01K2$x;Tfa8ukDy*SW18FJxy^NENJAn%?I$w!6Uh(qR$iDmT zC8wQsvK)8(F|u;i3i;ok{(^%JdN5af>Poe^cn?GEG4c1qOJ01oddy=^3#pZ(02)ZD@y716o$po<9O<70f`%O6v>-g+C)d)_N$uiYLgD^@I% zkACbDpWB zVxA0xopqHt?cvCgi7T4DF`BVB_Gg(;oO^YVBQ^$WJ!^+D16?eIKp|cynY)6_S)Ys{ zmei%>)=`M_#f0|7tuls264sgU-iokCrY;XqL|ys&3R|$4_E|{#^s)l$%4S{2L3faa zGnj<9LFc@T-LD5FN5OE!wpahCswl5dcdKH#M*1(CuwMN( z@O%5;@28&*1v*Al3_1$z4?|vLcle#iLBCOnQ_}rybEisK@`27;O{}Our z$OL4R>|UxAtwWc2p00wiI4@y#9^hq_e;_3gTb;8MHm%62S>E+uptI!GM@Cuk6XL}@ zg-FxH&{B#fhNd{Cq6XtCB2l0VpUbHcvOK3H1)aP{YWqIB*(CH(Y_mC8ZfU9rCkAms zKNmKp8Jlg+4r|$(cIf8?g@Y_FzzNn0h^CoLF*C7@A9N@AbK1Z}V7K?U=VdQo$7eo; z6u!`ycadAM<#BQ)C%CKJs4fdDTmB zi;8G$n&0)>e1D-GX!4dgp1_y{6DCGl!=L_eGrevft>LztZs)`2f0W<-@(&&;6Gxo< zq!W3`>t4XBb!+MLdfwttlwRJ!fkr7uVGFFw$z2|%m8JGO^7)%8q7Y5t@G``(u~yv7 zkY_nK_y}Sh$DMW(uX@Yt+53RK5P`*=3w-9%%enF+my;J64#yq0Ze-JaTW~fn+3hH8 z!sb@c6w819bT7Jv#|U(P0^2Sar?~xM4+ok)l-cJA)91VfJ?5V{HBy{c58X%bo;?;3$nX6+bC*W+esHgEz~u13pRH%R zg~Gvn+F?FT>E;E3p=TYag!R*FiHv5S)%)`KPkxj~srB6Q`x^n+_hAp@eZTt@Pkrs# zY`$p=+9}e2rO(A7Mc)&}!^9{~S{S7mYczIbv)|@k7I9j7@Ie3M=5yu8=uX6Teoq*?9tr2-P=jLM~_<0X>BW4 zW_Uvi{;=41*>FjiHYUcE5Alj{2-!0b(t;S>}FI1sG@Xv!|jf!}KzJuSg;APk=H_|xp4MhL(U9TlqP&_&& z{m&Ne0OO6^GP1+DztOp_!%Y}2#OuCCSKB{~8ZcpOD9(@G1Kx8SJYr3F*}0VX%umxe zC!1S&PO;o7MwI$9GEaWgBqXrJZW_|!|$0*$?wbB5f@ zIhXu^72Hk1d=1B?mV7A3aQ^8C)}}ff)nl0|IF71OmnERTBuBZM7*j7~MJ6P#INGBo zetawUD8<&_oDX_W!13X!ac+%+MT5fA(6u4f5k@O-4bp-6P+faNq~j{~$LI*HO_0Yc zrrT}6Qj`&r&O=yTh|=+r-5T*csNSse1*cax`5-$h7Ng6b3CAs4gy+Gbuo$JQMvXCw z=E|Ifxh>4?ocH^IwcNvf^wK%z7Pqo$x26Ynk`Inh)={{=S2~pvTNRwRA-=*F>H!w# z4N_H>`10i*R5pz>V%8l{Yl>X=8I*|z4!1IGlVQ0u&oHW zwM3BzOcu`3&og>iMnB7|#1KK4n40A9M;<13ZoHlK%l2lE75lJm*(%O|_j@`2ymyI+ z@SJm=$;zn>{_L|v;y(N^_U$I#f5|(PbB@=%>do@`FMo-d>D`%`+MSu{J(!uA35UUU@lFlWUotT+8(2Iwr^0a>nCNlP#OK za@y&qvitgn1Y2CKT`Ix>58R)B|Mxe^zx~@wB;rANNK6F9?S1WN52MsNB33c63FaFm`oV1w#A9$ZsGCwgoyh~$3|c2B6$c=q0Tm%C3O^q~_4A-~RIyje24f}T zsGkLXVD=}auDrUo@EGUsmh-w zZV7NthFaOXvO(9&@!CH{Y7v$!jbi&}Cgy5a4T0K!b~Vr=!@xa8-nU}c5%e-@Xdito z8CKv8dEaa2UZumyp#~YOOX};xyj?g5hUx5#XzoWzb-SRv>++$o;UOVpkhYiZ*b4cpHKhy*P|7f>1o#d=Q~;Vo{MR$S&cSLbkYtF2tk4a zuIPJNyNWO-A!C`$xo5afuyB+lMX_lS$AI!e|mFV#a zFWI~9*WCN!r!oJ%9}%@@NX78+jg~jv(r3Q6fGtw*T~dn3G>M~_BuY@)(C?)D&kuja zU3c7rQ_w3izV_K~@u^Ec$Cgdo09d_lCFlJ6GkN^ePe+tc6h+l!vEs1SD=4xe^OKS= z$BLpTY(6e{_aq#0Z8b6Rf>ak8iN0%$2w9r4&jI`L?{9q_C!Ky0CIAtB{gZ3CHJbFgeSY=x>$u^MH{q1vg7?W_UHS5S&1MVtG?CdYl&vUw2 zpJv=(kEu0&$!#^*x;*B{gCD?~pYd93nuCHTz5FabxAA)%f8wL)+|@-#F-a5=MKO&y zW_Emv@g$+G745h|D@rg%(~M(m>4yt4HYervEVF+J>xHTa{1P-qk2vl~o_fyHS+j06 z3b_3*cW{_GgqOYeh3vO#Ka|ppCu8h3y&GeZhaeRqi1PkYAIIf0QX{R|clEyPvuYpE zn%VKS>}cP~`nAh=%ej{z4yI?v`0-yq$&Y?`EnDaAVav|#l@zkfo%Ib!S1a0U^wLV2 ztcY|HimV8&d=9nNc;c)K4g{S}haEd_=KUZ3D5?CG0}tGQ0>1L4Z!_77_|cEP%GlU4 zK+#Gj8E?;exqP6xmd!5}7H1_wM!_NGPrO5I^jQI`V&eCFl}6({^#9!Er|DCg^o33G zbDFpbP1Q3{yRf6V`}cc|%DT$P9V^4iH33o!drZR{kA!DE2pW-}nn$1e9gStLC;H(} zV0n|BfWpyqt)Q*eXFG9Kb5*Ql0BQ5?3?iPmF4qrg~IMdEl~$ z+eAzDVa4w@(sr*yB9Qrl9qJ9-b;3TRYA+%^$(;sXxNMRu#}nG&8hS~&_3~~L>`l6q znX3?Q=`({>;CtvmW{!g&y%G6hIdej89XX!xZp95^5#7KlBsK7$1RIvm2)AMVDyh}p z%kIRJl?~$M?!kuS3+cks(VfL=8{+JS^?`oUR!WKTp+k_L))z!739scs!?V7Crv9L> z1~;fuikol#D_{8XXIQ>!EbJYA4Vzfj;)g%{S+H4Y4moIFALr&Ai+#r_ryK*ohd*?M z%uWrURHY?t`IY2N>#3)#BW1H_*9;>ZzS!%gJmpExVQO-M9Xsdv%;!JG z%+yqMXp?GUILo)bbu}P7^%;+0zMBr@v|;V|@ijl?SJ(ZPsi_u88M7=fzHE|fe)v;S z3T9_#h*{-7n>e7s(T{p0pm^6i-Y4VD-7t}=GNnZnGm{&5(|NC!r!pzkU^n@RYM2$6|N%cPVbZ@h^Px@=KYX+zk`! z(D)=|w0kMA1dfFuy;s`J^_Xaw>OC_(%V$3O zQEt8U_JRF}(u-Ppw!^ek39*VWIwFY^T5*Jy0gSL5UZAumorgRL#QIppP@qXjO30lH z@qvOd#^;zv4O&rHvH7!U;8&IZJXoP}BN-ysqOWZf|sbHEmTx*91z7dpa5;E4Snxkw0sItkfs_A$M zrFFGQ^k6=%t24NVK}O-f>LFO1Z=#w(lR?7tG)&}&H_kzmeU$z$N`~Z^A`;H~wev_N z!?ZQrxT_7L2Ou<5NPj7(Kb1UF2J5hl98#UCP4i8Se7tnBJY1|xtq`yM|FV=rGb~CR zp~?T_HuEI{KVi5|6{&sEe~bDq>V6dJYKXoryLvr!|K9gfRjzh1ji=T)lwBRF>eKkq z7aPLI*QM=C4{BP1B)W1J1xbwurTA&O3lCfQX8oi}$n!o_!PEi!8 z2SHdxZd0ri7R8aGXz2)N9eo#}b&R!uMC?op(!9X99BfV=idk{6XmdJw$NOr+NYem) z>k#cdM0wF?vA=-E5;;ej_2}m*ne8**-Hx+4lSx8LYks&r;&r!=^T$QyA$L3a-1p}5 z*?i&qaPy0ZK=Yu3So@*Nm_6rN-VbHH&cUlz49X-37Tsuq_KS-)J}xb3OcKT8n5c~+ z!u5Md=u(#MByAGJfr%8eFL)ly&wm^7zP=-CcWh_N+up>scU^$(2=Vw7*LUI7w_1KQ zA2Y6mMjV6IBvF$n96$@}p?0lCD|7-K*Z=lU{Pcf*&HO?a(Qwm^H}U?re~|BfdWz@0;zd0F6)#|R`7BDoJ$KyAWfy#aZ-4PBM0tqHZMWXWPk#6-wr!iksvxO% zMa}0i2xKb65^Jz|!pnYe0B`=$1|P{BLI2mV_3eAn`{hagS&5f1eu}W{)juQI&&$r) zRV=M6wKWxQXp76l<& zsqJ$9c^9%}=dI*f>1PkpP8XU;Lmnln&CRcOw%VXd`oB?z*q90!vt!2}Iq~$Tvtu#i zMK3%P1>AAxy~IlKjI&Os-I}G<8Yfl_CdXIMYK#+`s3yX8zF8FtBvlbW9lHY;ntOR@ zbUnXh0hYz+RaHBH!@XkMsW7Q z@cKvkBHe1xw{D}c?tR41{4jcDADR(gR@<0~uIC$OJ8RHYIF?k04dW$`kqJ{@U%dZv zc9!ogTh6?RNLbBZS@j%!f7vMC zi)Vk(|9_Odceou@z5oAOYt77VXP=&42_^Ji6ui^`f`AQFK$=)DDx&vV&@1XyuZo>} zv0y{&s0ai^L=ZxU&`St`kc9N)^s@KNthK&>tTnS|p9Fu;kLSq)2`A_5GJDOZyx*_4 z-a(6I%?N>AN$+=Vpv&F@`@7-N*yT%kxjSSF*J9e?n2VTlaGLu_gn5HDtTFNq4049 zca_H;dOVLHGJfkNede^at_xI35d0=23Y|_Cg{BB0c=(aW?X2#aV-{HJnkYY%*|dIB{t_?f>30^DSo4;Xk3j{_wshmt z>#5d4{~5?g*=_(2@bJUS^Ts`U6ULHpPF!-duofP9csT$=BO4eV*}%x?21ZBMGd#Kh zXz`+-V+UQ zw+Jge#A`Zt+;NWuIO+|r_haRj`9KIkk|sR#@Et^v%9~AuB1sHOPB_fd{4GnD{)S35 z%wLoCiRcXO{Kv9_1o|6)*=$4P*gLZZw3=(Z4vBc|x(t&dr=M(5;iDWr$e zWF$j!y1R6_Mfo&Lh{RPaH) zBrXJk#DY$ogl9a49}RS|`|t$k&w}o9K;z-%tbgQDy3akEj#E!XC`tX4Q>YzvG{e_j z$He^)AW9X|bkfO=d`z76!~kn;o~0<10%P3otM#@$kF;)5*KxuUYG=I#QLXqy7sJ@C zOBw#nFA+hNu-3&uqsd>kba4ORI8BpKRRV$vjo5XUN=CV4Qpz%M8WSfCwD<2PCT3#m zCf48fD!c4Dk3IHUga{;e-hMX^J@^=JKl=>!J#cTzrHHq@>oi_}>=E4X>zi1;YBkna zlq}_uBLe3~b{iCbfaFcNFVl?n6it7F&9NLBq)P zcQE;&p!4n55zc!zvgateFSayR{tsg}UXIzi6IG&}lDOp0Nq@g;O}VlKiO)Ev6KZd~ zl<0u(;@LNtZFez#L(VF2nL0czZO-PY8AV~~$ zsTpa4wvv(zFbHOL^ivDVY#ATKgchQR^Upblh36c=^((Gr;o|umc;o^6{_)>&)3tx$ zoA3Ds<=q0L3Q#~(YX`Ct3M&Oo(~_@CDbe}V+sXB^#=vNot2=GxG>$p>IA+eCo)0em z_?w$K=iD=xS)Io&(|04)38hjOeU%=%OEs)?v}*$-+G2%vDF|Ue>W4XvbP^R*NYfal zT$Ieh>ATR~F~+Og*3ez+$6z_(#KVBYS-0UfX3Xm0lV5lb@4xh2EIIIfJhk>W^mQ)q z(Ic(BxoDdp`|Wr}!wp)^N$y28hOJvhN!WvDR{WJc7SF+2Xf`z$oqrjBy8bF^<$ed3 zL{Kev5rrKHVUR+hjU`e7E8OepPIBY(!KL52xSv=DhHaDAh0pPR-OtzTe0pYvsC)0D zGJQG8cg`gJ@X??kT@qprQv^4psK*iqy_JP%6S1T2OB9kzrdpw7!KZcXLO5+Pl%yY$ zkEev6`zyisZ-IURGeTaX!3S8yKY~$a>3qT9YZj4%`A?a@e!?hYkSr|0P zJo_~#>)KY9HYP=|iq84ppt9;ebI^8t?@Met`c%ePjgq?%Si$lr;L5HVqhUyge|eQ3 z${K_pbO}NkcngmvwXL{6@(s2kKzi1T0jWrI;&QvSkNxtHrPQWiC7}#ROiG|atf#bR z>I<8FAJOtpD-SA7F$A6=fz}@M6y&flvQ1Ut40lN)JFWs;G^6#=fM&-V`DZ9$o;waR0gFY(x%M4M}d&oL}P-0S+l$HV)40`RxxL0kB7gx z&y?^*c3)SWp-tPY5IJSQH3rfRHB4$y$_?S%Kv@tAXQ9gedHR(XnAbm}rC<;NLK>_F zL4{Y>ZLoltK4W@eqZ7V{@iJxBtm#;5Ir6B(dE&7rMJsE!7;6MoUlY{3I{Zf7w0)c> z$0vw_z!8W2hBHZ1Qk~Llv;c3Sq6m`+1x%mq9^>f9nE#%w;@o7Z7s911yt-~fF}%z_ zuLd(`Ia>Q6haSWek3A`@*m)^lE|&n9Kxe-J=&aXswtexRO&d2`2wU4bV|P@vwlx`@ z1flzUtzNx`Qps;UZRS&VuU!~Q*1Ym6Hvie~`c;KjZ|l~99Z0d(O47tKXZ8#L#>XaT z7q1IPl=gq7gkr;nO>K@=DP?Pa>miS!3Q$5Jv6KRp12eTBN*ic-r)>*jCYAfWM4*(y z*udLa906T$xC7|1sS?+}oeBy@msvMi&&)RpX&Xn)cd|Js6Z7?I7HT-UX z)JC7Tc86Ni7Kp>b79!pXQf-kfzNXl|T797P@0%)&-EG?$(;8Tr*1f+t#m~OKY$e~c zEduOLyw>9X1IhjxTk5H*`Bun=&Ec(kT4pKsWK456`wX;@0N& zfeh-HeOByLCZL`26zxu4C&UyDeDOsuC?lr4IDzw%YG)@YPDipWk`QRyx)6*fd|Nm)NK=%IusT6&OM*q4Ce~^a<0H;3gg)vfu@K6D zxY^)ILjLm=$(el#Cv_Wy4B7V6pEG>(4fK8J!&DAF2wAVu`{57LdFrVQT>DGR^DiQ* zVJlD1Z!3&R@~EDyQO{sR?wnBAW&14cO1gS~T@hC!An%bg-q?t!%xas4It5&mi z{bml>Zx8xs^)Wd(c!J&k&2hkK2HhwpxfJN|qVNs>}7mx<$;7oK^EwX0tt zHBF?-8&qt z_m?cCBJ)=pgOMhuxf|mrEnem~wsoAXrp#Y%`~|0-@%qdX&Tp@~i3=`xGm;wh+6=6* zlu9+qrLO$&mO|xHIgtVOY}Q1!Jo3tW_KF4L0wcJQZTwgb@Cz1lLAf7hYQoEYg}Uq> zl7F0oIernQQxabkA&*gn*C)vJE?HuycOkK(Ez2M0BpN0)95@Tk+#9A)j}!|D#5}f1!4JDSSXQY} z#}aw>wmW|WVIT-a;F?`fArOU|1e^yP~2dC+`N3B*wqI~p^19}03zIhbN zHL|ZI>g=f1987V|I-Y#;X~&+)-j^wKW+CB*KQFcQj_!6qsr7aV-&i|4n#m(CwbA+4 zhCzvHX9t4=L#^RWn&z7_n@Q_)W`m1`OLST|E0R1!uA{>}{#RdJ&*RIVY~QjM6Bqp1 za??$>+FE(WP9(CHETqsFuimi5Jm{$|)mr|$Bu?Dbu;nGRXN3QUu-?ZS(8UExB4& z6l0;}8$-u*a`i=;#Zr1Oq%jEwL7!q@nVh2cPEwk3M zDje`k2ummh6WX*{lQIdnBg@-X6H`ds$|A31Ch2Z#TYTJ|u?XqMHC7k4w!U#S1!`$_ zdRWuOf5|8++4YsFV_K4ES!lS8-+Z@tNLl@ntFE%dmm;`L$kxeJ@VvKi*R$w9(b9Q{ zY=drFdY5b<PT6N;vi`%z;-79paNvqw!sCan(pn+*?s~7WENwQ#dH}b_Y|U_W+-GO`zM?u2W%_f zS}T_qZmlIT2@#e?I?Ag?6Q&KKuu3EYq$)9<#)LLyGL6}$M~FngKy!j7F5W>3!K6;n zCI(R=wr(3`P3$fvVKF)(Rwbl$TV&(6Q(9Q0H5ePCt#tHEprI#0Dy(e~gcY>4j5eE; zR6w(-`At&emdy>`H)DdmtI*uIjrE`U1l5E0X4<8nBb+@GwQxQ&zW6yN9($7EYkz|t z7{&%I51-Xe7UN#iCf1}r>)ds8;p{#-&pns&f%~ayaGA zfifE#i7VEGq!iW|(kT+bw2mHDY*^uaoq;EQX10V30^|#09E?K(Dl8K>Ct1GyIl6o5 z?6d!3Y8_QpzqFdKe*Q|1IqpbKdCMtODiM3_x0pTl-ktmJdVt$*{u^2+NR`P{m6ygf z9fZS6Wegy!CXO5IvDa?A`JHcO&b(Q9X5^|DR&(?3uV>rFtwd2s7?oJRc0DVed4V)F z2xT3y)&^ix@B6o{SgHc8J3p|!v`Pz~!s`$I4rSRm(!rU3*^_tgIlzYwd6jy}(s&%2 z&+ba?qywnE;bE*OQ$6xtDhI7+{EoSd-|IJlYRdUz3i9_XDbHIlqWz(A;>_zRzTS>xg> zKE!DkzK!cXe>FERT}EwokN2BHoQ@NyDy@uAn@fDOHV9!k{OH$n@DT^0l$)ZDjW;;| z&F^FBUB6;heKtmQBZZ)+(oaXZMk8(j9yBPk*a{c1*0h?2PTkciyiC_Rl5Lu5lyuqI zih|8X2&Q#Tqr0PrO#|y0ni!6AmtD~PN~kwP&(HpbS8?OgS(AM(CS&&mhzFRfh9-g_PhY@?$# z-Gc!Gs`Xw%RmIvg_aO%PCf*f0skZ((y0cUXSHvY*RH478x~V;srC>gS_u7}ZMsDFM zvyg*k){$Z(r4z11A9o1pFVDiv>B1~jG(V*X9?__qV&sU)Vq|wdu5P8C5gX|1gtPaA z-TS?-S%G|fBjE|ZMGmfnnGq0iix}d3Rx>FR=2^c9HhCbdXKS>YE$Q!gfsg%?D6G)A z-%kkXR60U1Ap`zCcNS}7!=dXp(vkMF2Kensi#eE!Ib`$yqNbPG^N$~7x1ozz-g7z2 z_%<;!P`P}dJrJsGI$;gQY@*D*)bF~D;MiSq%K7l6e`3v5XOc3TNG`$vzwRjWlX{gd zpevI$`tKD;MHFNcb&D`Qj?zb>NxvP^R_4P!Cwg>X%h1PBrf95yBuxla=pj-E9YaGb~%Ab=MrI-1pfQLnnl< z$81hJZ#T=e1d}$)Up5d0(T-o+1|BOwLwyW^Q(XuTbxZTdkebv-nF=4+rNj;hGEN&z9&sN`7yp0#5QX-cjntf+uW5aq?%++YFLXs4hRBCnGW zZn)Cd-z^ZZZR@te_P5mjAw%ac!_e>uLI@sz{An)x_`h4I`)zhOC8og@*B!ME!cevq zg_#UZYiEbbNZ{_C>?fvK(X9lvT8(BSX4_Wh$M5XwYy+;ip<&h-05s_D?QMIHvDrb;5#f=5)6lsAB2vrs@=7K5R@XhCq`*^O@y5yTcg$`)y zQ<$uk7y;TO#M(HP1PRi*Ga*|7N#9iIJciOe@+6Ik5!9kGl7LcJ^3iT0FT5P2&9{x# zjT~cjUKh1X)`uGXR~nxmv(llY7m{ZB(X>z23r-W;xk@0ASniKQ>P~l6j0=8!${L zf8ATJa1yQ8D~Fa@uP7H=4*&ol07*naRIIXXDx22g)fO6Z$Axkk_90qiwj%%c1xOKk zwNUG<$)1_Pw8ghrJH-|##cQC-Uuz}4^DmuhyS8E{N`k5Tc3}%mb!)^@96^X3*4gbg zZ}TslfLUGGdhYF$0aKLDd;5(M?N?@7u~mp<#@c`J0_~(mldUt&XEb^mq4rRyY_eL| zq6zOWG=+xU80~$?F>w-OjGI0-eFkHqCBw-m4PzN^j<9KT1H;WxOgxH7VzwruSZmoD zPcUX0j3-UTb;@u$LDMwRLOPne_nx~Z!b!LjB*GdJ<4S^3AP9mAQUqulV?^Yn?L;KT zMI0qYlL|$WPBPXQL8lESlL-eviO1QRC@xzq`0^`;t&MR63FFJ3V(ocvW$@=eg+|lG zcO7^DGr#?Hy5Ic{WVPz7Acd(?=9vc2ljF#erR&0Tnf{HhQ958h_u5W282Z)k*z(EG z(tK_eLMcX!Dp!O zhZ@>X&a#LdB}1f?3=a*l?9OF8_w+MJ8&fTX+;{hbT>05=^6{N~yZ1H5e57Pk)`PLk1t zXO{%Gtm@_DKkUm5D|#`WtUiA8lWhBXiRQ{Ly4!Pk4jrdk`aks`rMt#q8Q4Ao93 zMSfwFf{sDWgZc+=rSdil9eX1Mq-*|{p>GSuf9INWA>p1E>RkNmUHRP;J;b(2(4F2=lFY^6c|BwFOZU)zG z1+9r>g+^n-ryux(D|Nol#ry8T1(#gJ;cqyUAaFycZ-4p!=F=J43L@fEJ6^PLl5Qz1Qb1WrCele7I;ELvFa4H2($UaJQw)}d54aanxxJMya;zT` z&F-Dc{QgBmVTsOaKb_TSoPNeJ1VXUlnU@g2o8EFPv9)~Zif=J6x{itQiTpNa{LMPG z8X;X1{mRx=oO$MZ7#`ou2R?olQo-oZD5o5DK6~#um+pE%cjs(MDxy}MPEY3oN@0hO zZ&QRaazjB8pp=6HX#}B=DB;}D!jH6^v|i`&KUpClmCV&4a|u|(I0pyM@lo{>+eHd< z0@PE>Db4#d!6%o(xO<(_Llw;{DkNt_I50!I!Mu1wN)1#(IA=fj{86xLpNpWfTgHjb z`ZcA4zlaQ9f|(^&iUz036`aPiR7J{k+@wVafmDHqg4x10$$j?n)P^kRNVGiufmDQn zqFOFfDV2x5Z@-VpS;XVq&aY-vSb6MKFlh<8OT5m0i%V8+l!U zCNY|Z)vZmlaPYVwkO-k&{E+uxS`TX!8I`HUA+HLyAQa}qA}Wg)w8D9 z6!^&PRaaJ$N)LH+bZqGE=_HO*3~9b)6+$pMp;@?So@g~*st^?v?7ULTx%$RO>gm=F z&V|UMl;FWfpZ7F*IAO^l#Em4cccMU14F$70YV=GBcHeie+*6sAqQ+X1Byp7KG$l?F z60K>bO&U!fx#Yj!8%{qmmm&+{78u&ABVaR!9C{E~c<#9u-6+R%7uxHwEJ31+r=Nb# z0vvX@%a`xz>Y}G>8a-V-^mO&o)79;N_0ZGZMHnj2QIKtAoi>(+HZISaLiDAS{3_#y7` zcg~=@a|WIDK051tbk%3jS?{N-K8>#WG`ESiw8?Q>g|*CwlWo4)e77ju#JMlOyc&Q* z4n2@YGb!w^Y;FKZ%mjxVdY}-%^Utqpdk=P0g{LV++#!YQAk1ep!(H8{yR|#<+&jr2hPLDI%r7N#=!zxCsG91_a)U zCWWLL2K1Cm^mbIKM-hRLD4}Q?>tV7IQt~EJ2BmX9ReqidPI|4W;#)dyyrC zWS;dlNqwPC740elEBy8%dkmtOkKRV0*l|Vc&s8g2trP|krBG2;p^uLZD1{*<6%Z;x zD1}cOk|-$(EqVr~6M3wn^wHb-3VEu$G>d!5Kf+>Xc=*@s-HS4DA$CrYX@U4!XGL2M zxwq!*EVax`+0tj1p`0S-5j*+4C@3g)VC_xyu#4iOwe1NzI~!sY0eE5x@XqAQ6q|MF z2&?@CCp{F&<`GKLPt62=W0D6$XNnXDzDaaS>NC9)W0}+wBsyWZF~np%2G)|qlZ?jW z3^gZ66U&57F;+976H;MG{1&~b$I-gs4}*Zz3Q{DowKM@K%Fe@zA=V9=X-c9KqzDLP z2U68AB68GAn~<6qYnqfuNzzGD3mQQ)Y0@;lIEu#zlMx;mN_hJ!%WsD&=rBTdOk?zx zJJ@vD$C>!&vV4M7JL!0)U-{p3oU#OAG{O|dHEDv?O}gIsc4l7n18OHO$ zFZlo?OP6AVrI|LlceCJ6>pB=}l;{?Uz)L3UL6yOzL0u>+0_rko$-;0SHV_1=L==>L zjFa1>I2yJf5TGQ4(hXyja081_S=PU@j{ELg!)MD7;ADsM}etWAVFHR zb-hYeNUnTn0q5PgCr@vw5JZmlKJ>FMF!W=A-u72lM)n**_xYCI4_`}ZA=rik&Y?Y- zeF9V;bX@pdYVWbg8K=0PHN2dWp9_Ys`UrY6$Ou+%i}>`7bNSkxGZ{^s2Ql@T^G%&# zjAl4?QqEG?O|9HRqZv~PB^A>oiJQdQB_foSL`s*B*i0LYCoxT}X_%B$M#!j4t=fe! z7ApdFo4J@pJ#z@8pw{2X>iL^_>yf8(?jaYmW&KtRu;1Z(@$)->%9pSG3Z=Bn(568W zo#slKBuSaS+XBwM_*_mn?RY93Wt4#1m)=Pw6nyscpJ8VAJi>BB7?kL$PNy8!^7l?> z(`uziZR$Z%&`1-KLi9&!oqy71+Z+#y%5GlT+i;}t1%e0GrAAYVO3a_Oh#B3pP*TxZ zn?^^uj|2AKjRWVL#=!Pru<+?C-p9a5lat?g9s{Fm866*O<+!FPYqvhb?(_bQm)EZ5 zd)I!Nay3Aw@Wo5NL4R*24?pu)`nu*3NJVG4lj%KkDMg(GQew36zCh)aDo791aWZ~K zqHb*&v+G=A2!+avA>}s@Mw|Sx8DWToWD%03ayxseC-{lngeggwEeWr_nNruM34Zo) z-V3D1hfIF9gg!cKgT`8Xp1Fe4_Jqrify3tb#ym9yU%Z{l?9ZV7aX-wA7!&XrUSY9% zp2x)~b4A$Ny!a1Unl}C9-hrN4dAJhgBhG|%QgHGtZy!O1@SlALm~5tQU{xuVc&e|L z-!5Fpno0+X5F_^B?z_Lrjnx@!{O`ZIE!Xb-oNVvp12;a&9!#KF^Emjqw@NKF!1! z&N=@bV&l;2ya_a3Iu$n)_Fa55`|P_XLI`gB%dNKF(OFoXu!EesUhUwIzq<(`1bgkd z2m9{*1{#xTUVOGRo`R8)27^Ou7#~mDa)67kS5v!@UTQ6=cCu?nqi=n2qp3-4Oe1Ns zcFSX|c>YBIF8S!iVt902K2*uth>5Y7U`ytFr_DFwUiGN0ES zasuOxrr+MTDo}}FbhN?n=&MYOrnw~fwdAf-`Ok5ZGBUP~k+Cg>KksJg&9_;AbIv(a zz@Wc|Tl-4)ct?2K+t0uPH{P(+3i{ecPCK*;qFn|IecbTpTLAdfr#>c98!|K;d;hs0 zO{j5mpqGQ^HICX>GebGe;b@l5lJ@^R5)0uG={+Q0s;Ynl!QtVN=Yasfy&^2 z(qFI2|8oB=eRQ3W%Kyqhe1(qI{)^a{*J{=UJ3fBtQYC~Ec@NvB9k4r|8Eve!Dc!Ve z(>k;(2zRDD7gHMhY#>$KmG3ZzuoNEa6mz^Rtn$qiRGuhW5~jf9tXI4UKfy|k^P1|y zM5~4VvbonI^Hmlj0tLm4Kuc-`+NPv7b+w^Qo!ri9v~IW?h?jSc>x72Q;!T=Bg0U&7 zv81L+fHYhFocXW$JshQ#DorLXhW(s z#tJ4HgT%=Msg5xwMF~Tn2)SXbhYzjmXL;O%2qK6rLqGc!TR!z)#7{r#CNbqQUGIDw zGr#{GYA2n5uqje#I!-^8SwHzH^)t_ca>>b_AAN+4A9*j^ulXVA_!!1Cxp(V0SFFzN7(P+ebHLe(NX8kXTOAwy>wsFB4uWZj@(z86F7@rb)v1wcp1khX!+rD`!!+#XmvFETB!g=S@^#M!&N3SE81*QSA2dd|P ziOx$b;i603`8~Oj$y)`(Uq6)iIoHgOG$dEwGlP%)em*O=l!%m%qw_$U#Cy3?88BfI zQong?rjsNlCRBnrY0`)j;>0!e%`_oN5)um{=)fpPn=?lGo4zCnTq;2+aIe?2dO!P3 z+l5(OvoTm|^Llt{({kp{+mp{+^hL(Unn(r5opB6*T5&y}z4|kVF+pQ;g87T)bLM-_ z;!STqg`U1{q=4t2T1g=4oc`t$n9((#Y2Doj71CXsMy=HCp;WEuxAqXao~X>UQq665 zgkW5!Xd#JHgH!=g5V-g!0Xp>=%O>@=i`KTKk?MRqYmLrJM~kJS(#e7uyVF_iK`KSP zHjS6I+{>)#-F)D}&yXaRj*f`ymwuZ~+eTS(+}R9_u4B{C3-tCJ!HSiya>tWbGiPo; z0{H#UZlDqhuKfO&>95bG9F?e*DopQMKv%7wPzH2_%0)^_8%fvylabCU4A*HBVlMBcH= z-RwIg$(tjZS5z=>a4@DYfK&H?D~^NX7eb(1a~@o`oKpGogx|giW&$O_FT@sRhtKl! zU^@#iL^hkMgGqH>P=sMQzc)B}oy=*{QiTLEpcDisB?weNqyhq^kizCu??6QkqGus7 zmNZGJg%M##CwJ|#h(FAk%f?VrqRt@Q-2VA zAOfFdEZvY(IgzLot=qp+k&CIc#av!;vby_zy2a5|val6$i3myxqM+pNtyx5w(KO=* zI*EOQqrLRh75S-2NYj|qxcjTgh6cuXz@~?X=9?{(7rLh4vCPy+*66~PdrDPhTXf-C zui~Kxp0EHXopda_&6~rT^@9wLOt5wH7^^o7am!6V5E*Gb-=K@EJPxK<5JzL*TF@4&ig3{37eOtzyIWVFm{iHV=$4 zJQ?!q-(M}PF+B3<Rd%dGd*8#QEo+#prmNdxDEqZ;~edy!D(?>&$Idw%)(gB)L7KUY^fa zzw#X`rQoe^J&g~3;4%h=USVKpjG>`1hKAP>n>s)E@wddpWTPz-$XX}OUbpcnZoc_% zSPMV@)zw0pE=EQ-FfuyM$mj&aqgxmneU%F?dU7fvVgo^% zr0lo%UM#!ke(|Fp{z#0BZN>uiS_l9A-FIK=GPv-rtRt`oy!>lq%~&dAu1`!&9f z-~a9g(bLn-mMvTP-c?_xr?cXP@~<%-u!Y(cOLtcXmwxQycFUG+)axC*{PO+cvX8x& z*>mPn3OYIB^#}9E>u(hAec!pQ zsD_eCU@3>rZc$c}P)Q=C2!!-`>F(!N!V-Z}Za5qSgn>dy)#3#Syp_d&zKnLU$To3M z!Bpp&oyX(v8{~qHt1p2wC9S@qET7oObCIHg~omGG>LKaXp<}5b~17DKO+<}XE4fK3Xw?y0#J(1s1IvK2!l?9 zja`A~Gli8ZqrCUsihvkHLnlb-pcU4pB*qn}p)6BUb*vdf>q(?65eVTj#{EGMpwL2l z|C`M{3K>m3HOA9&1wxfc;&Dn*4Q&NUGK`WDQJ}B{2vsJOj+k35cTld>=`GjUvr}?n z#~6z`ykvKDjPiba(f#hT33pxCVp%jCX_s=CTgu`WUS!~>|4ZY^6;LT5F+4ZY;76MT zuS~+c?rF@c&!AT-tPsR$N`yctN#Msbk%gvC*qp{}P9~X*$1o^lP{QaY5O_w0!kS6q z=BSq>xyE6%ZvtbJoc^n1klPBf!z%MM7Yk*VPYO*7gQ?{M`)}pE{nk?p@mnyc z90RqZw;;>Y@*$TQS&8V{gUr^09;NZXflU7WWf!ZaAWq=cr+fLs@;(yn0A$))8Wtw3 z``id2A#mPOD-=OcAqr{)N}`dJLa0eYn8r-TO(v2i21!{7nh1=Qh(HpCRip}NqzN5S znOeDnW}MPntutMv7#q;EmOvP`#3>C68wb`i+89Dgh{q>cGx`eOxcs|(_zNFJg(6>c zjf{`eS+DueDy&_*iL;Npn1^4!m6_f1GGr~asFQN3=Be&(1MCRMEs0d91aoSgl!RqM zTc;i{mI-UA1R<%lgnsjwrZG|~j7}+qB@&}i))EAP8)$eBZE7{vSQ4xKrZROhI-|i# z5)-q1WGj=+QLKQmi2=ayop1e|OFn*K4mX@^XeK8n>Fw*l8h3xW|L#XQ=GY^FgueQ0 z1W<`O=&JQmi#kZLNa=jllSxd|S{E;Aoq9()VwzL12nU8wQth{`&L8M9s0(v->*ZS+ zTQ>7=duni~6jB7vJ(yATmE={cInmVl3JclOcp%nhjXCT9l55_EJz%D*V}y?s4BX=} zUJWSzwvzCTe?YPpW|zUhGBw6!avjguG3Ln7c~32lK_Qh_0%ZByuHaB!qV1lyqsL0J zCfpYeL6FmBh0l`~LK3Pl|2zjWbe?^w+}9hO7kVQ}%pQZI9J_rh-D&DyyO&wasqFXE zHI(+7ncu16)dNKHd$G#jBO8_tr(Mk}cYc_Z`6yXNW4WhV<~Q9PASBA#T+@;H16{fa z1VJknuN;I(s}WK;*p>$q1yXs1kB7lwX{IqoD6G-$eDWYSA)K_(7}sThb z=`?og3@P1zTL&?^Vf{w-+kc5&G;bzY)yg5ZlE#>2+4A3t&aOI1lCbQb5Af=`jkd3^R~&o7QPgTx ze)xlH`Ov?g&%nSiyYI1&oil3@Si$;D!`$)rUx|Yb+K-?7BS{WVX3C*S06 z*%q^H+j0SL=G!i`&pz`kK^Ovx;q4QA{FCqH6aVq=!Whe|>(=w=eUCCZHg4z5n=6hy z;YdbD##s7?o9+1@I$x|`x1P5g_$J%6pqDhU?7Qz?{NL}d7HMi&`P>Vfc+AOm#9 z8!<;5{d#_M?f(jZPkrL^_S@h8AMwJ97rFbN_c1XxVHfSTP%Js=IKnVs+t%$Iyw7Pi zsyB11;I_d*e*B~Fi?^M5D!1Kw2k$=b-L|KD7Fda~n&FX++;{)|V$VHxLmSOM?^?z) zPd#rZ8!>a|%@S`s3`+QRp~`)%>=^Uea``R88Xo@MtlG10Jl z?zx9J`Q+nxY1L|$-TNRHU3d-;JoqrDpMHw%>6-6jmj%OPYqJ^5rY-_rLce z>h)?HbZlgN1GnCCyEyc)gHcNH&bOa$@4NpIqEbNI(CmNE{`~odUx;$4#O5tq`1@^l zv29?xoj!eFe)fe0-c!-*~otW%Vke$i?T44#&LzgYV``|9!dGuyGUn@4t_G zPHmN~%prvXLxP7M{GHfs_eE{e;_TY|@)y5r8~C}6V==DV0Khs?bNR> z3i|TaK&VKMch{HLiCNq3Y=+$S=XnAVmZ{);F~x_@PV&ypl%FPw!$ecGA^a;y-oNmg z+bK+fDSsu<*5ntfHZ~_(wuid6KeS9HC-X^LYvPk#QxfF>Wj1u6H0R_%8cP$6(k>oC zn3z-~XeSaRG49RR*7=bwid0y%RVbHYiZ(G;gec)8`NE{6UUF%rLdnqOrP>C8iZCW2 z(b%M7M*sjI07*naRBo~?g+S{DQCOiAlu=Sq@9IIwh(?kSh7q9(5mu1el`s0(^&dcNNhBNX`G8Y+PStilhluV#?Or&BGEs4Dh5)ry-m`x}JHLOjDN;Rsb4z_IC&YCrA z*k!l5%wIH@a=F5z4?e*Yk37Tcjy{y*PC1TRM~O24rc??U z+_s$+Pd&@{=(xLHq{t;m?y&U4Y(I8%^iks}pV`FG!!NRiz#8wRGXBr}RqDT2J`opL zyof)L@@c6h_59er0Dkk#EdIQ5CYKz%meUq*MF5j`L38c_u@$|1kS1Z70W9**^6 z6B^6kO5?U$&?6w+Fq!*Sb@KZMXRy7gkV@whx7bhBsvg`E`_GTD7F2+e5lBg5wLjDa zMyE7VO)LbR^|v%y$_#!wdi^Jx4A8Hs}i2`?X zvDT7ENvab^ghgOI$v+f=Nv$!y7{Mn=xD*59n{?-2ozXpu@n$DOV_WH{IN;G`pSXz2 zK5-G(U3(+%I{yqxWkspffdHOZ{v3xNz7GJs9kX3lv07Fd`|fYGVT?v)wT$QJ4glNSvVSFokR5JLJ+0 zz2DNph6}G_^)D}Cg6V|piWKm8Dd6|rb+!bGs&IorD-=?>h_t}J{>D$|r4WQl<@xne zx?w{YN>3(MKDy9t8B;&7(m;}vm>p0s@RCNSBD4AZR7r#K&>-zuFi!e!klJm%tTioV ziQjx@N(;Aj=*?_QNcZHSeNDZa4lqGu99Wis6wllu`^2k2;yZ=X&IzGHD2^ zhI8KjVSDvYzakbcoX3)rjsfY$G3(Z@=iTqP#P(0`M1ABvVt9BMD7RvyP>hX@qm*K3 zaFj3#a;;2mSCSGKMW>pm6tQ*dP}}yUr@w>^9iOoO_O9cRN-;VzN~l7NFJ@nUehqOmC5Qr6t$c|R6*ufqlI>dtQA#l~Jjze6 z{Xc{f_P?(9l(_hP=XpD?Q-s}h=Q7TE$ECKXE3*Z9FDYe)M}`TMVrY06sI*cBgrc)! zHb)(Kxc$nPuM`(tcrHtp9Ltg=$B9-tis9aSAL75R_@;f~xfRrEPN4xXGBV2Cd9&HJ zbvrijN*md-1_>e9uyznB720Sv4{V{gyP6m0z1=go^kbLUl`pR1vdcas_S$x>)*>AU(f9Agl0wsorhJhe&8mxfM`dp4V<|zAxFMN%QFFsEkf5I^wf5Ne? z*kWsW@PUW9;)<`^#~*u=dcB&TU!$XA{(T=r1**UT6MidPVdctIyzX^}kt7LEKemb> z3K0mRvS9h6PjK)7C)%I>{F~y)Bah&`3*MPOc4G{8-+eC^oq3r}O5>EGActMaKr%2e z?B3Ur(e^yz9D?YtFRAFCCOG1CC)u;!b~;N=K29uHFqet(30{2h75lRv|Be@5UdP8i ze15KE$n&GK6i4f9pg|aVaHIRIMhdJ={3g?Vaq35!GFQ0hJagO%=hRPqgh)pG)+Wv2 zib@EyPMrlVHU6F=^3(-w8eT^2nqD_N4xL&w2vJg@fo7bL+E(*rvgRD)hPt-c1e>NSY0Ms*_RE=8 z4Cy&65g>J@8H< z(RP}d*M^iAtt?xLzkiJ^Is{@y_gv$o70$P$^Kh$Ha18|nhxPtSH*1taO6Q5mXpa(C zghWBbQ;jDm2OVHzR3={*fwS{kN2|_c3MrPvW`|GgAUoDl3aThs2AiO4>WWq2KIoxx zn;Yf5ktU-$N{g^DLY19=j}nP##qI>EL~pf&iVUdNx=D?ptRx6Uq(TDapbAZ`NwoVQ zh5{NcYYc7S_4Nizx|-B{QD>Vm)uWG~^UY^q(v;CZ|BobEpIn9V#RelAa(WLa;q)GHFxBlL<;PVDsbvHmcK1n;4@B!!k++ zPDCgJ#^VW3<}@T}0|dlz18p3HEY(f4)(Bx-B$CyL|Hs(7#mcf>=Xw7abIz*jUTf{k z;pLErhd0~0n38No3Up@}OCe$tFf15BoCHRY_(cH%0}1TdF#^~S66qliLXf~fkOYAO z%Z32KO6G4e3RoHe`mK0*i(TjE}O ztzO+#v&QBBzn=%};touN5JnUwEbGxHtc0)o`uF3N_q^4%n%Te>-~GMcf$#p%cj8N* z{}O)opZqiY?(clo$n0sv<0p^ssZabS-uTMvNLAM8ab5Pf5d@dC_K5(+ue8zztL(!d zk0#Mgwj^4cd~G?WN?W`{EjWob=Wp||*XD-}v{IC?V#K>2WcK=iDJ*#e z;&**3hClhgAb;|&;qcS{81l%T4`1g8S2mfmW=iBX-j@u7(|sWya&>gh+%T?T|u3iiDTP9f|@c z4S1XjL<(LW2kc4ldM>yt1@k;xu~u@+bkwrqIN##&>8aU5isGw}-@t-f{MY~0-^NG& z{73M&{_1~;zyFW_XFQzVfjA|wGIqlbyYa^Tcw0zE@Xsu}(m*!K4M~5|!CfV5)$R)aJ zdEVxYBdjIkG_P)#vH?$6XMEr5kMOOxw>BWqjD*kOYw!bj?@#?Fc=X@@96tNM{T1Bd z8j&7g0O9vi#815C9zGcZ_HD~#1Hs__>e9%yMmY~5`jEs_I!s%fB!>vd431-qJgbB8 zd9CNIZ6}71{PW5N){`2&X&|Y@?1LstYsli94Ecmsdxd%hW8 z_x10Gs^YhP>$CXT|MUOBD{p@Y6vcOZ&$r>XKJ^(qdi+N7=tCLfu*Y|N$2Z|)KmGII zVfAfeg@C$hstDfw>bvolw?Duy|Ki6nPJ_$!>yjW~nP+_U(Np||k9;5A{hoIspH_VO zAepD0lxKv-;PiJ#;5V*=_3>YqzKlWaqs3nzV@5G20!z^{tU*qP4LKg z9PVa(%Xj`O_`>JEj4yrlOPCKU4*3u8;qU!(c-O1%!lOrz@$*0XalH2IlX%O+KhmxPR~6I2;ro`N+S9kNuOMN7|LP zz0{_118(o00CFphW1Aqa93GaB@eVm@H_?tiYpW@&DOaBi2<|jUd4}R$T^^d*l6_A?bSEL|@ zh+Rrh5kyx3s+s{tt5mgK&m0lhK+Uy4N>=j>Lm?YW$KJtJu24qp(o(>=kT`GqdSHzQ=DhN)>yved1xK@`7RXx|5+U|@) z^YnHa=e8u=hQM2#-6mDuGQl^JI-N1d=|u>#j;`ML?=22;`&m^A?hwu9_vcn?R!&Sk zFVE@z=qw!CAf9JfN4nU}UN+o)v)AI{E;q0Gg4}U+IITgmL$w;z>)s;<^@1c`Q#@MZ z*$rmel%d74QF|`d7{LBDS35L1EZ&=Kk)K~S(;vDS*Rw7Bg@sl7NVsmf&*yO5=NRjk zF>8tj=&fJS#u;^vg6+LH^DSI5`v4!1_~%&j0IS4Z^?ZB&qP(|y8NI_BbdlWrbN$AA z+jnl0g`nP`6H$}4dxj+wu!|E?*aH}#STO}_uDQ|>DME>m6`0E%2DS-ft__-0K#WZZ z$9QeDT*+JJHCx8;3PD70&A(9!ZQxUUGHFuyxnwX;U?@V|;X3Vbod(<=MhtO8jH6N1 zw0aGLZ#hk#T7g$1vu#Awpd4VqpM5Fgd+ucnq$oK<*SUQdJdK#w1wZxGjKA~c1tk}t z9Kj6C`Cu?DC~n3FxVd@@UfSQoRZJL=@XD~mm+}dBU8k66i8W+Wi>ga91yVLm@{_i3MmXoagVjEAda{@J+)ly zz|g#6m({m zUh(~ZY{g&t=HI}3?%kp+Mr}vgVq%$r-+N=gPyX^d@N1ucfGJwsNrqxA3s%qcex}i;YMsSF@T^Bq(-62pw2oWg` zcy&Kv$AqWCc;j>gI3dM^mtsOh!7PNwrvp?4d0nuJ6Hc=D!`4AkOt?KB@N~JwvdmC6 zd1l5f9*l2Cno=Xe-t6CkffKS8Fe%m|h+c3Pk4Eg}%pk&=7rgJ`TOn(PFfiv8vrwDV z!kZu#@!=37udBtJP&X_kMu9jDo3KjpB9~klRl#xIdH4ZZ&ho_tck@%+9-lOj$J66i z0RT6kiAyoX=9*<(^dY?=3)&#+Q=3xM4)ddwg- z)z26cvI>%uYg4c>^?qOk1XHPa>VP%kwuM--UE51up_-A>&A;ZBy^a=*NL6K0<(q7qLXm8Oe%-b+>YhyYqAceX4J=$ekN)o| z1Zyz7*e-cS&c+4dXvB4LwM?EZ8d9rQNVn*sya>{OHf*Y4LaN-B0wMwkfuq?07~HmB z1jY~(rWEloMwG)5r*#IUU_)!R!O^^AqoI?c6dQo7MIgCgSr>%CH+w!fD^RS9#eRaV z;D#!eWrY+Q%%+sQw+h?uq??bC2soZrjDw{zh%+Z_OLt!2<;A?fN`w>xVsdY%D^BY6 zh&(_w&l%&Cnm4z4h^fXP5o2Alu@bl=q$`~x`ACtMjCr;5dlQ!BY#Yevr245T3;rqF7~)&zOMT14#i?^34!8Y1lW?JRI=_dj#6^r0uu@Ze@bN>mlh#!w)}7zTXq z%dg{O|Mc(CH-F2Yz~BFge}MnuFaNMUeCI=>w>#`@KO}3+i(p_y&eq6;5WT*&Ejtl| zVzS|rTCG;3`j`!vIy-2ssN$51H_66}BJyXQXsf4cb8fCjf)u;I1Do1|IRdaHO(4P| z1z!H!eT7YL$-K-7^qeVbpJxU18ef-qp5rHXI;C_66w~ja;^&9O|HkJkkxFWPi(0 z=;iI)`P&s2rg2w@iE~R-w?FbIg`G0|5ehQ#ulyX4U#k}B5C}Q4k z5kd5*w7Hxx=M^VX%ueFv5OKY`!Xd9%@`_=)#@+GOZ#!;au3T2L6sTfdXOF_mAW}pY z-?W>Ql#wB20aHMTlOJ;A5R-(ZtQZ3$=ab*Wgs*(@HGJ`le}Hj}xW2hUhztr0$r-=% z>CfU5|MD}K4=a>bMCL}`Udzd{im7Hnx6tPe@=+s^dQu_)rHv+8n`~x65$y(!J_&Td zg*4~~wQVESxCm@F)DVZ}2_@f>ef|9#(6Yxb{N~H}^p{4w{pJ=gT^CTa1M7=VB7XE2 zUcuk_=kLJlrwL=Qn=~}03AbTW189W7W#0HFgj+3B@&QHU35 zBE_1sz*|BDywR52wluZ5Slz;~cv3_wh&|$^5@omS_D!;=4wxF^h@0s?m>5f*F%4Ik zhAS`#SK|#H-n;|X<9)Aqk8vw%m?L6L{s3hZ70l}a#dBUumx3XxE!xF4 z{%G0i*7(xK<)S*dx3f5s73(cE<01m9bad+&7$XR*89yCv@nifNUP8bJ_zHAy0PZK8 zxZrhOw)y8*v$zR{kv%rd#XGlIRTt(&i^?23_nBS+iDjL4_Y+d3zBQ$9FMk!nVA zrw*+IZ1%6GY8k7ahx^~FVl41RoZkGi8#TC=%*>29L~m}xw)KR4c6#QQBAZ|4eEeBzYRe_TRE9{`5z_7eDkvf1RE@eS%;9_%Gw`VAV+}io7Vk^5_YE;6MBe z_>2F+zfDAhAO67~(I-zI*}X`>7z0v>Ahmuk1mpYI62hWKGX=BqRXCd3pt-=F9g6~M zF)LKfWnPP&F{3|YBa;I+8PSK>!QZ#L+7ntG`?~I({9n*mFdiBC=0AAljPpN-NijIwRiX`7U>_()}u5XL>;vKqu{P zuh-9HyT08n*((m18wi^N;gxs1A0PPRAHe6o@VSe)E$X0hv;oYzXt@S&yKF|L&!N3fq;9xn6M_FOTapD1iOm-PK zLM3AtCQwXJ1T08+vaSdL7f)fFJ!r z#E(*&ia7)o#-5?9qzqUP^^vg_ISl5zJ%zT2(?W zv&UZ$A~WW7hQK2&q<}y;+&#lD{=%;y4TM)-eFe{+-Ql%Ac!XitnPeOQ@qmFXH#p}b z1QzwyBY%uY$Z zipd@VOb8UsFC7!!8VHjL9?6VPeBlPa^xJ}kd>*n99q zR-D#ncpX9zN>`qmZ^YtkK(wI7D|S=S;>B{!lhV5E1z8XJOob81d)=78+^ z;EI4!mMt==lUxziZNz!jck_r5eEM$>pZmK9c(8wH@2?iK4RG>@{RV_#k4}U33BA;c zg429Lih~0x3j_s;faH=vg<>sEH}}_;TiQiShIWrOnyeD81TarFcaJ^WY)IXxDQp{j z@vIo)fTW5g2>vpDAAdc39{-ly!!a=auD*f$P`r&!VdRKH0#a%mT?POpC}WoyOO*47Yi5jhp2C1-Xkt%Cpn zAOJ~3K~xRu*2s4~N)M@$KHJzcsEN;_egqj%i(nWcgIW$Kx*U6}mr5~Zkdm~htjcwP zZq56p?9{eI4j@!+gU+)Czg~Y@4HaDvA{Cfew(F)P{@}dfQ99#e)7h$Q1AbYvR=FhT z6gsxN*|fyJS094OtSq|_JaNPuQ|ajD&S8il z;kYg>=R6XufiHH~AJz;DFBGH{yzU`TDOfz-7A(eR(G`IcreW}58euI3saU*Y&IMrh z`+yLlHTJb(0~jR0ZX6*3yN~>H2~2)pbJQ%_TG~H{U2$0k(T zQgwO}j{G7At`}{e#TQ;~+%G_9E%UomHLJMd%i)U7{MphlUiis$%Wv&^H*N2?KIfxu zmZ&rA`nH+hmcr-~0H7W3Ubi5}b1nKMJ+Irl>(BvQqtEE9*`9XKGR$sXbwaGiFTLd0EJfkKEeVH$Us(jKZS=3GFkIOP*4 zPI&FvV=$SQa2&3|1e}&Ts4Uh*gaNx0F~$k=azq;TC{mEvy!1=Xn3rcb<)e8SLjcnN zP{DCMAvg?a2njD;zYS4=t8v5_1{}%}q`=j<0|x`8JUTqaG+yKN!&5vxypA#MkcMld z7;!i}M#WQ~TB4fh89&M6@;S zR{Z(KM{G+RLqgz$7zdl^>H@-woL8KdXU+|;av?K)#F*|N!VMs!m9T8f1Q5o2|)+5LMDaBtkIzjoVh#fm&G#9!xu2rNJlk z0u{k2FD_MgPpf}>^}L}Ae7>fX;4B4zGYQ%>9GNja*Y71Y8oH5#bADP z%euGgVJ(}N&a%_KH_3P=Sh47i#G+?`h2$c2b8 z0=p0ZRy=T7?~wzFC=wAyFrG-plT*f&0;bdU822bsjj8;$D6A`mlDx9_OYT=*9*I{@HJGcI{1bMFe~nO$jH>Y@^c( zdD_Nh2G=83MQsWJLDt0!BiabXu8sloR@b&-Zj(onR!X!r*Cw+Joouip{DN?AobX5Q z-N*6SEsnWBNN_VwIC>mu&f*bt_WQ0S`%tsk5KT8}r!>Fj7DK1hMfNmE*5;EX@OIDo za9O20d)moPt#Pisw6AZ^Fk(z8Vu}MwDVRMOXDx=`bXLC&v-)TuR~hxf z(oMC{7vyqIR_-X;4cbN2HnPvdm%8ncYS&!pg?E@PbeflzG{U*zvY%vj07P68k@ZcA z|MP!Cy+XrPQIVq$Iuu{|>H&Z8-}|%ptN+=5MDKpjyUyk*^)f6a(w)ZwewQ@s>cTMVk^XR;ffGI>Qt`;@RmHn)>z<_i$O~-BG>a`C z#JAZ#v_j=U)yu-ID`EqKzU}Ov+UMQ7beq(z>DwY3pG(Bv2I`$}xwq`%If**XW5w8N z+PA&s9eDKU35uFkv`$*op=CDI?t2&QRD0d^sy%_7THK}jnOdy}W@p;`|Js3#oA(vd*|$UPoS^dFE}x6mLBGD`Pj@ z;I;RL1xUG5;75#od;uQ;tI-;Nh>3kGu_~Dy2Hy4--Wy` zn9JHou%*~db}A9oA!zetgRSA&1&bd#ot~KYDwr%$L@}4#;y}p3 zOavHl-t85YOj-}~=A|M+k>b)eFQT+kI?aPyU>o_ssmY!~3&&K?5e*n)#G*j-uTxcA zO(R|nD^8~skB>)C5tLFqm)%*b%ox*TcZ4inq6Mse$cam-IRq#N3E+05cds^L9rN#Hjqnb`KQGQ$O@=<1eE9|l~s#4G-#up2U8poLPQpEIF4;( zRHWsH24d_2qafIOtqYjanY=T*^f$oZIi{R&zq2=R)tk$NMr3WM%esZacCbo|-LNZh zk&I9`bS>-CGi)6?sR|p+30 zK2WguQjdmH0ck;q10t)-DdF#9!BVts*HUl^nxD(cp{0h{eMJuLvte9{7Qa>jb2(@C zuiIxmG8_9NSYx!TxfN<$Gj+v%{7zwI4$!hTIkI^$jc^@6C={{Z?eO+p!f71wbX~EQ zf*}sr5h1~bSStstrC?EjLPYREf?~yS%>dNmjw%6J#K5K;k&Ds&vuwpy;?S z)OZ!S2#QEY(syD%?0juD09PVO-!d>daFVt{Y{MWq6p+w1lhu|(T_jiAGPl;4S}YzM z)U?GIRjRT(yz9O0Uw<_uT%{2MGg2B{ITo;}*l<+s8eH;fS=S=%H4>-|**4O2M~Bv) z46U1DqP>o8lidamdbR~rytwdf_N69%)w9C<90|33&E+zx+9v3^RTQ_P_I#arL0k5; zpRKc?mheKn$?0Ey+ZF!zfA~@S*x&mf^_#!(yYcPc`6uz7_r4ox7-%`p`a8eXn=HLH|5_n@ zl_FnNP{jUJtLKgnR-F7VH7d`emNyj-y0e_NhO_r}6Wcjk6A1i4p#ZdOU{UGfzL^0b z-9Vq{EQZv^230%#0)+v!p0nB)+8M*WC1RTj)H=T=n{3`#ZR5QNYPs~vRp7C~{`4%D zzHgZ6Y&ya9Sz5259v*ZiZ`T(q_oauzUH+V|%lGdWOO*onzX|lbB$zJp_`1NK&AZ+v zCMM0D^8ta@4JHKJP$g!}*gTm5ihZ0ggb7(Q*0Q4L;wThu4~WH^fj~y^O(cjAJj-`h zur<(uVBo>z4ldOen7lZ`He|C)wSyAYGGj=4AVl9v5>A?tmGJoV)Cln91W}82vj!mJ zl<&-oO99VLuVDx~loFBR4!Kyt`^Mpk4JTv)GaXL3EL0%7l zxaZv`PtpKVR)`UK_M%UB)tf6Lo=2B(YHRqK$4P60&wgOIgpwIt2?^Ata;i-y??ub` zjKm(f7n?^Ealp${0Idbfydda$CX=xul`(_?Ay}bKPHo;A0dk2yBax{%Rv#8+1qM#I zzkh%bBkoN%xX-|z3KB=0%Hkw!cnnFwv*VpLwglYf5d#}cYY_q0%_GcfroJVaSM!;x zH#NCrlhty>nokzt#z4v0K~xq|#y)8@$#dTJ7PQ5GWaM^0ljoQg?LAH##H62x7H{%u zWRw*ID=3Z#UPMp`UKsEeKY@T07`^y_KqiOw1t7rDHgRPCu9O*TR=?-PsOz4Ay-m*5L2Pn9^FJ$U zb(4}iY>heCaKbB}e#2g&Zl0X#MG!%XAcf#(p=HFEQh*Sfls^U=oQdXcfDvjOsdEC{ zEYCKKQdv#jR|Gf1gf%bz;3`ehsuWucSdB-#8h4-=A?xZJTSj)Kfk1uo?ag504Af1$ z%kW)_%T9T>B9>bg@Ah5*kzF>b*`G_)GJ*+;g$OLbkRl!oj3~er5n@XC%6f;n47iR! zg5pF0u?_)Mp~2V~1{t!!5xe}oNI_sU&vy2WZQCYNN7MGUvdS=7JK3%jh%11OtEHfz z+cs$}n6xJq(-xn07A51+JJRLvR+(zm-{TzkT0xE0G-&fJZsJJUpl?*`h;2h$&ujZx z#bw9-J5Xp_9`ryv+eh2=Sf%3KfK{6ZmfHqzmxi`@5N7OSzz_{?Nx@)u^OBn-rV^Ru z%qBxE16a<$Ox;$AE>AXW+q^T`D9+EIhPCSJwJmn3*$i3}L6t1i&IQqet8v0E4H%gb zx)?7&Fx0}?2TkVGjgPrIF++}@EqDt6)5AC3NdXXc1 zA!dJmQ~rF$RQFuhCKcZrH9fO@_Hkg<+d%#5t*Pxc+W8p=T!CR^ynT8PB=yNA3EQ0;4lsB1pa&ZMo0s)HM8^z`1TthkKtI?p{nSD5tX-=4)%I%Nx;T{iX1 z?FRu6IorPX5pAS^7JH8&Ksnrbb1oM#+LtQ->pL4&@js+*|R6yl5=?d&nz zNk9$otj`hn>CDte=h1SsSye3p(C_B%IG}S~`gVSI!wvScVT&fU?_XNH3BBNqq`p({ z8f|@nhnV7Z&F;HUqr~&*o;>voNovy1 zAxt0=>|(^kdyH|wv%?)i7%>hBA_2#Iz}~&diTApIb&mgjT!C)TZNXh(iEd!CT ztyoe8hfh_JFbV&&NHQD7=4k6+yF!pJ}NeNGv6H*+Vkzf+v zw@(A!nG~nP5zp2WSo3E4aGG+AW|=5rO~4caG8Cf|oCQEulkH}h|8W;07G1HA4{<0n z?oAW!jRPKrj9m&?jtk~8o17H{NU=CTM2y4KqQz7)X3YqNT()W3-0ZO$4By^@F|Uig zm&Ib4R$ai}m^(ujAq>dK#)c@_ysyn)u1yYJn^xlJ@@K)2jA9NZuy|44q7TKPP7w;r zgDy^upYsBU#YrWOn3#})DPm9vqN~d~w1uROh~Jf*sCC!Q;oaVx9cVVVFd;KrL;687T~SX&4aQle{NI zP{o}JZU}fS4G0h%g>V3nwIH&fuq9e61R1vdFKQ*H^`_#RB#Sky&0yq&qN{J#OM`^= ze!$v@)h4}RGqv+SHGXczHFYscsvWGZlCqm$wkr@RILY2p4kAaqbGO5_+qQ<3 zkO>GWS%K}=h{zFFqn(RuF$+Ys$TbE}3z(HN&az&p2m5SeeSvlHya9f0*WR5_a#p1F zxyH|wS4k+UR}hT_4iK?xUAndazb>%xDfeb-=hWDco_zo^w;Rm z`^$4gyH{JIi;1?UWwri8+-g$?fc3m-@^3=VORn2+z3_npdd3UdhP&G7AG!S>M? z+w~mTKG&jX$tZQBPTHTdIMYGY2(zE8i`Qp!NM_a6j znhKz~ygHS^=6~-RaJ_86{mthN9;cU4dKa1K{qx!V<#FQ?_#2E3?DL$zU3>T(wU`cd zPX&kM6Nnj#j0j+$2+^fQu2>FhK=1;y%5E*Tte985<OZIWJ6A-u>~$L4 zx0V28M7j@#Ut>1b;qBTFU~WfVIq^qAhz;%8C#YKoRTm48)9I`P8r51hZs_SoZMUvSJDYlnO>B z%xeJ$#T9nQP`nib-Y7@Nx*|-3{WxM?fgvX3TyQ;Vo$#c+2TwlQV@=K^7Qwy8%CfkHWzenuRx(i zeM1&ZwB!EuCV6TTEE{YTDFBO4{i&l}_F2jSY24%Lit+6BDOk-9%s!0@KE>UKhzJ9+ ztYl>I{kVgQMfgDlF-&-0450OZ*X9G3ydY2oZzwWsvz9`H)TTliD1yOrh)zYk2%r6+ zAPhthU66uF;BL|mjEqDB3Ka9QV$K;70v1u6B;)bA+V@I>St7jgWe3PCs!gI3N1-P@_lCNZGl zq>sntv04<|oKMI;oD&~x932MtFb#-2K=O)Jd`RE}lX^}a*3}aS3??U{F14=b7&$H9 z9z>;^YE}tT-R4v)gL%WHz1Bv9RkPTn5D-G83~M{E3o(|(wjLpx+&{bWB{HFu3?i}N zM3sk1c5i!EniAQ-L|sN({e7SY>q<`MRx(9~ZMd<0eob;kXHjO=Zyb2o^-eZb+~VK%-; zAja@fHVL;F!pPA*k#@br7*N!P2(02Ty5fVq;0l{sR2S!1L=3^Q z&Wpr(MJJ>Pg#Nx9M8d+K%n%yqeOmu}!M5 z)2dgW%dxj8N@|cZ(56ZPN{jfi*Imx4*jgvB8dOjk0!SMTeWM6lOqxm3r8P}0Zj71+ z%A1P{G^O)`tpP#pg+ykIfl=~`1&T<735*EKlF!*94l_LEp*CkkEpp7>|I4P4T2}Zb zt37YtapBj$WE6AT$oDjiesp-*=u2C6TuqbR|758J0`_~*#w8@hDfC_oHPvJBK_zv> zX0`NKH3g1s=ndniXMRb`rf({NUh{fZ!1{Z^b?~{Vf*`b=W4gT$E`+^cH}i#q9nZ@> zwF4oY+4Nlr5_$l#zn2cW+zrW|EsAELk!r_uEv|}KUY;+Eg7bCY*E&&bED=U@3A`x^ zrx-AhF>%-?QCK|@hlvnU1YB)HHZ?U)OH(}wRw1`um{Lpq|1JQA5CAC<)@C!RmY3og z=~4uibhtLDZFXCgLFXbUMx-Iw+eHixz)TheYw1CXQfB9t1c)et!%|6d7pG-|dZr3! zm6pChRJvw2_&L%+54-0u(EG-_VQQzR>D%TFF<&2x{kc4;WwomowKxv3^RU8gSc z`ca`YTcummpe+)W^m33uE}K8+Vpvb%&oh60>=NsGeFBQo<#daf`zv@{z>>E;`Pb09BuXHTC<(0{aA7cp|)h^tz z!j;PYx0{V)d!tt|NxMjHp4d7aRTnLjKe$zjdrqahskcI-Krql~d9$1lcm(MJQefJ> z-JnDH8Hk5=)~}5cY*9i_4_}1Rf?=2}1JU!bA&WH;Wp!UDoym}>!g?BXYHeO>s6u4H zY0gkxL1Jaq;$9fLhzk-$fBys`=8cVrcpZ1xg^1Vl5#umGXTm_hRf?GN(tIpM1WOhq zj^?ipgaERLIgUsnVjq&Zio`Y^LrhrHfRH1i0Q>14j>|Lbh6!sqVJTLurL;$421CFh z;=yRNh#Rx zt~|$l1`{E9BNcs%bQKduW-w2>DDRoNrMjRkm&K5!ZMT1;870NV~v9QHCI!%ee!*2&~aDspu9J zWSN_`C0~5QA3{Va-Ps`p5gGS}gjFIQpYB@p*bq!Hv*sdiXJG&UAOJ~3K~#)Fiq!!% z(j-P1*Sjm+9*_3@#50m909>|-wxUV77vLT~w54cThOP7lxB5eX*O(I^`xzAsW;~-9_cA$^tGG{QW zOBT`PjZ*_au}2$OzIYc+!u-iS+HjSZU>e~GjDqo=O@yHb&-2Cxq9&70`xwGeI=!M)H z4TJr+H$b>Y#Y|%IhklH&NyIT!h>PLNWmjqJ=QER*1Y1Bb7NN&6@xX3 z-3N84FK(W!3=nOb&TiBCoB&%o#2KA!dvtT&cdfZx2;%8E87$QlW>xIR3A<>KoC625 zpmY#D>U@*MuQ8Qv?^w5h6`myioKG=9V4i$!4a)YM-U>ju$Pv=#F~oZDXDtqC<1bXN zcP(V4oe1Xz@!6(KFJXCaLOAwZbLyh0xOvG5*W-X8Btx!AF?Ck!`FV?gqky7jO$ije zeO4eO|Mj)es6(`LncxlhE(QcCK76w8ZHyLeR*o84OGFU2i)=tr#Ucium10}U5D0@0 zcS8sW-NG#d!ahV~K~qm@i;N0b-Hs^!UgvB!q`_;3RSIT*pRyYBC-}9m9@AIjw`?fw z$w-rIhD@Z7ua0Ri07Ro{LGBPt|6w2VWaj6 z9&Mo7{`jMApiXYT3$>>tw~&c$7OD#~H`i_b8H=sk{L`lLsa7TQbs;u2jlO^qM>XwA zE{OVyX7QlsY>97@uTDJ_jy7q0zhx)wJnZM^ZLCsOoUyL;jG9<6F!ezuAdqqORfxQ( z0t|;C5S=}{+`MO5vr~+>DIh%7XDu1za#{6)LfvZ<9Igb~BqvoeSU(ST&{Xw}*$2x$ zZBGb1V9Ad$#2XY!YXir_W~&nPVoXf_!Fg&)5Xc8 zRz#JgZQGk_OQ7a7_TRy;(c;^;+b3@zP{J;acC(ZPQ(&+P#j zK(gpBR>eMz`1<`0<#dO`Izx(0vLS*I1R+AIeE1kKm&G7dF0`FhkP)zYq)*0bgw$z- zu191FcsNdY5Eq1^&}dC9)eYGgF@bPcOM{tJNi(au_ZCiKw`fA!Ec$=drs2fNr9*^O z3j)CpJ>3o?$^H;2Nd9NbJoZA0iNJ9%Ylcf;!J*||6Wb?}IAR(G+(*LXpUfM1#j1oi z?rt$>!H{fAkjsjDalj%g5)t0H+hKv?kj21^Dp-^dLqt|!j0wkO*_>)54sByU>x_7w zv$Wc>ZSBzI>S{Mm^i7BQ)R>$1%YLVtS4&=)GFfvjzP?we^4T|E-iy=ne zK)U>PnGreSCIoPbI7}l>Ihzte?Yf$vc-OSYso5$_ff9&te}Cme2!-SobBYEZ%L`UD zD}s2MfNiJUGg_nc*sLCnQVw_lpiTZ7-Seq%>qt$UaAgyDLsj|>f=W9x?3rJ-m^JZ% zRvk>af~2VUlSt1B+}tp46)?yz^f=aSi`45_^emQ5IE=0~k|u;CT^kU5tHyEsCm*!AVSlOp1fR69crK&g%9D+Jsqu^KwG)AH6(d4S z2&y>v`>+xI^~KRZwGnG|E}d=Fsn@-q(c7(dJC9|etvT1{eWuUsTN*>P%S4vp@x>nm-ZJ2zy#4Xy-pE;~%>EL8F1(7q5A{2sz;#L*=WMhnW zU%4+lYnO@be-qwij>HC)Vtk3@@w*fh zbFdDcJhi4WgbE6zP)jMGS|2zROdEu)#R6}w zzW51CjWTtXQ(l9n^m4hZ-C(%A^V`6)`4#AV?#R@on(8(*E)HbuEfOzqYg}ZUUw}BD zlcitAzHQ$iayJXEe=ohi02he|S{GOaffBUFX-WkikZ)_$skSS`r`SdTw+VQukwflr zwI!ez3A*_SEb6g1nty}5RKGjazRn6IMdY1(A#9M4%K?!FM4ljJ#Uemh7o<31$+JaW zNWk6v6i?^JSc`3g6-OZ4GdTz|Qk)>=1fu9|V?p52q@ctWYXks!u|m!zh9T@BvO={W zuwpJN0+LCE$UP^T5jY|EW{Y64R^-0oeKP8Da#{H-D;`W&xSLO4W?WAXKm;sW5TlX7 z2OcnSz??H~#t8#Qye;kRYY2E-7y$@ga~aEi*dubl6n1v)#DIC3@nH85htp&1Uiu?= z5Cb0U?_((^C=>$|f+$81Mh>_u3$6j&ot`48;O_JoQdXA&1yD!`al-xK3Wwus*zI4! zvd-49<|EX7SavrCZ_HCGe#zzD!MBcA<9(#GH;FMR;&6=w0`d_c!W|NpG9zoj#Ee;i zd*cpS3knAm0Iv>=p&aofpD?clcTy0EFncr7%p4T3locbhy*xEop=do|W0O%A5zHi* zivn0NVS#2sURNY8=F4VAVZtJeLX1p`yPR!n>M?=?MXWd(nOD_|jtmAPl7hrbR*D`q zW*MWg5kZUnZwSs%uS9E1s?=Ae%=qP9s(k%tdgUSC6<#xDEj+B^>7LY62S`q&Q&Biyx4= z`P@p$O+qBP&Ee$?QU$Pz7hl@Q?N!ERw6`i}QlwC2%$8MN>sKLQE6MZFD$jj#jij^KGqh)1IrB*71|;%Hzd!pz;EqMJZ38O|k(@7(om{QrK)Dd1rEIDZ zS1O%*p2KC*)fl_N8tomV(Keg=a=-;<=kz5|o>5@lv01&m}Mm$e^Ya~O6*k9ep z?bD|o(bUNUX^Z(HA9mM)i#T{xn%7zY9~jvdNN^n^A~AMxFnE;+RxMbTj6yXoO_8-& z%7UwRq$}`6s&%TD(>@PPs-Qw^bVBp0bLX9wGY&Le$YFbztGKSDr@Y%&rx?K;kQ@Tn zNZx)A6e-(s!e!glwOjq@;+jzo0XYj|fB{#fQnWiFdRs4E%FCrgyk7v9&}Bcr02>GC za_ie)aRW7-H)eXVc;ap&DP42h`8*p#q?7RjNGV`U6ZXT1kd)@g@}mNpsD_TS2KyIAglN|CScWq2uAV^b&cy=#Tv`( z>KRKY*%};A;k0X5Y)CTjO`;fZqtH$7Po_BqApE>kbb8DBS^9G+sSZj+| zJ=>V~G;9AF!sdDQDb~f%QQjm1+wOE`1OLJM3e_t6c18gc1|QD z_KiQ&=WMpi2`&sK-5l?$sp8xV7HL*Rmp-W~5 z`ECX^bg%}TCfTz)5wTqR=DVRsx$Ur*J=O$M^vS!eSLafij>cSLf<#etQydIg&Mrrd z{NjaIERv(?h&Eo{%@}X@SA4zwjHY)qb0C~q0rLqQ-Y>x7>-(i4LDZJvUFr}k z@`ij&tRg3s*t05ofgTehA%XCM%q~F(39_uC^65>7fbq1RkYX>&Bsd%4#11Gs2cRyC z`#PV~g3TI!SfB9IHt3_toee|@`Tgr2{6Hzmp=h0 z4bNH&1wfae&2c{|-gAy=@(<6y$7%fxZ52=J1y9QpGFkvRQZz9YO)IoDETrL=ci}us z0-52b^95EmT<;scZm)4z`-S7uH!qk<_|+sn3&j*g4JEd^UR;QKY*=tPo$wbweZile zF8GoYpe6E7Ikx%kcq;Kb{^fqfR<5`=!(BZx=dEs&UonX4<`cx!58~z`(=OssPHrz5 z(|j72eg}|rpZ@F#6PDqZO~MDc)JX*Q=G6BumkZ9~{yUf=$EjFjQ)MCDU(E0V;3Wxu zT+TS>1$O|?;tGJbTKsE*k<#K@MssjmjRw9`u!SXj%nA|N8D-BZN_?W&;XQHh6LZUOM9$O$dKpBt?eY*kzvdsD<+97bsx0!3qn|2 zVom^SK$O2dw#iit9@Dk&4``aMa(w2PF-ZXlYhJJ{OKd=D04oVw?f$j~qKu*&mb`=< zbBe&^ka7;y1hCcSHin2}%OZzd5hKQH(c!O7u}QtGzRg;MvF7By-~b!70nV}ka-{H_ z`vAm?R<~)`E|Wvg5oZXe080A#xu3= zAf`w1MFL8T@6pC)P^UX}g0<@8Tc=$TKW`m6X`2jVD?O(%%$1De@OnW*XuY7DeSV{2 z(RaR6hi&d4uSa6F?7XLAOYxTZ9VfX+2?;jB`pM8IR$N9l?AC+X>`-d*1C1G0a_rBaW4^ z#AZE_FNIo-CRw|6&wa-fv5Fd>H;ausv+U5ls9B3qY@q$DGgKrNH2CLr5UI%_lsUN< zzO)LMzdmLj?~_Kvs{=P(IaOSO=z9NE$|y(j(rq3Ppf54Zz5;Ar=~{yauv zP}J0ZE+L?cJxVt-paj2z2q?32Rgl0;KTRA0UC*esDE!RhYe|5A$%C3 z(E*r{$Kp6wo6Cj~=A!h7TeeKR6rd0}&8E{K5PT z%mV;BBy1l3{WM?OZAz-NUclO%8FzfVly-S+e`C9+t;=@0%o5W>z>#sRHci*|G*p+4 zKlnRlDx+!NKDaMNLLP2GXI^B%Ih`T!DQ8Y++`}&;L}*C3l`B{lXe+pGKclu+6t$4J z)@TkYRB5=~{R1ZuG>_XwJJWru+@f@1fP=N@7@~j zdc!IUo&cPe6*sO>ZOHk8t!_B6fQ<1upWH8{)gc??RO$lYg)&rtoPLC<;hdlG>-9IR z>kAqTX}jXv^%o>rz-h&4{S27m^YibaT9IhM)4HNm#gYiO@*M^V(3xMTKD;0&k88u1 z=igyHeZqaa;pP0d&YD$d-HXo1FtxhD{;%gH_5t)l2QqL7GO zxsZ)Kx1IyiX6h1tc8^jEDUz{+s*?DC#8~hrrxQ}Wf|^Upidke?`m}W;Kd2TGyq0@3 z{qb4p+scqX4-Y5=%}PXOg1SG7nt)tR3m}GZ@@e1*x*~ZQ-uki5+h^9O&eZY!KR8x{6eW4Dhnvl z@Q5Z^05oh`9cmc)+`dKS7}_A>7rjVfnhJm0qcjelp#T^@l$AkCM}TwO zOL_pZFvs)gK13lp$b@^*#6oDz8_h(FO&gZTk>*gxm??w>wJGNI!E?hqLt7_eu1gI) zMp}@U6-fj+yL~4`hWAb~^}!+`8e-eQQXJ=g3P6=dWBNsUS{Br@MFYlIBw=Ck&z%BG z&M0d5b}R0ml?B&(!J3i}A37v3-V=$}!!Zo#-e&Hs>7d;v@xq9>51hO~-PF}49Gk2p z38n=(<>B8Z5}cXwA z_bqa&_X5f();dbgrZt!#ZBn{U9=R?9)}5}~Kd6L=sp&VW*+FV7!!tdxVeGxUt`~uQ zu%px0C+ZSJjbTi{l4y@>BLH?lD|Z*xsD?O>OSPw zH@WdUW2+&fo@`Y5up%G#%+wmp#pr;w&0b&nIHs+uMTibjN;a*`cJ0CRuF(fu`BAR# zlGhdk8w;RX;*3*H!-mjs+{Du!P2dny;bToX#RVtgVIiVODe;Ya3`oL*Cq%ATgm32+ zUh_KNpn*h2>(wG|mHQw)>H!Nrg)u>!a~S;ImrxpcN`fhmSjEybw%2V$SOF1ZI_ypY zVTBnFP_ebJqo@rMJ$YfXCk-e;sg3hT#+~ECTL;Ot0n+N5^PE>7iqtwS4Cw%0q)9B^ zhlhHQf$xik$L8wLQ4O(2Pd%C36up}Z$vxSm6!~C3PWw1%@2lqMK4GjmKcF0ATh2b* zk?6CO6>PGTX2N4#bx*bMK|39OQiFmu5*uI#zqd{P=Q-BV4vW(F4shMErH7B+f0ni5I+%vQ{6WJIL$7%`|F8tu*ykTC$o_-DBEgBfFYM|DIgPkFz2lFXw6S`0&da zwSXbb4vg{UvrSUFsoM_x7u?z% z3p+XVN*RC64L`z3=ZP49U4Ma8!;55`mlQrK;J57!r?lW@IiW(aE@!Z0{N3vxVYJ|! z8ES;Qobl!SgBJ^Au;!4ZzQz6Bm;V@BRs8(h|Bc#iFlc0}8x~pcv^>EeI4@85^!yX9 zifg?i5rw>)k@E>gj1|D91>e+F5gCdcw9RWNktrHmEAs|H zd!BVry<7MieMGp#?@*D_ic3oPS_)2!AfaKa3f&4+6&no3g5nYnfENh4zf22*JXm=Y zKE#4#iX;ne^$s(jpax_~$HA1qFg8HzaXQ-bo&zc;iQVrVpM@OsYesWgEdyi?#-8mg zk>_sav}7WN>_1jP43;h_Bg6?E$bd<{;%Z zm&*>|8iRC~)VVBBk=E;@z|1g@MTYDxS9_R z$Q}5S&iL{9f^+yv?+W~~ZK&FCZyW0Uj!&O1ND8cEcvHo%Atya0Lu!VK4GRGk zguD8m5fN5lWEdJ5{&*{Rz22f2>fkf?kM?L#f;Uv)1VSB~WA3qN!|&$E+P&8a+OGmi zwl~CcBNYV^)2aU!pHXgYMnBOyGa{k~%CKqto3td1P#y^w1m>$WJ!2o_um z9y#Y24g3^QWafnrOW`Kbn;*pM+C7b-vvo%nlQ&V9NLigTW9>1=C?cN#;?P*eVMoVirf;fT##$iJILui+%644w;W*&LfPT^Y?a+ZpLTvzQtHBDf zP`@Hn0$wAOrZFsfY%uAeDdh1U(}xKMHeusvGFEU5771XThC^M3z3;&_Y&%_^O^+Y# zckzgP7}AJ13~)`!)mXvaO7R*}9{_|^!oN}sw&Uy>`+MFM3Pk=7NB^S=uy&q6S2^xV zf}th09zBg=PogpO?E};vD_lOj6%YNF{K~P%yv8g03ZNKL_t(?s2+7;a0mhim;otr zTX}#GgcAw@rQYMAt!P~)&kIy5O2~+xSF zBB*W-+!%$p1bw&x$RR0muU+OGMLLfyQ>zulJSuKU=MgiLWkD%lUBVaeA%&ARnf$-S z3@0*V2<~ljcefGNBq#t*DdB6o;)^w)DNbp{XBK?RJ~Mp*gGsP7FQ8UkJo1AbkaVv% zFflI6GoCr4Rd04TGOQ`%WqEPZK!A{#3&}H z?S`ri7kL6##kEzJg=;VoBD$i)b5NT?Vp3kU`BprBpAHW5?a{R~{KHQdTmr~us(39o z++!mvRv_efIb|<~OMAt)ph3f;C?*+&nraK6oFSQ)c|x)m<-LeDI_Ao@GD<|@3~f%W zw|Jgv3N>X5KEb7);@u}`C&p4z366a}?S6w?$J zYLe`#0d@V4gkyLiJT-vmc(eSI$1wTidGs@K0$=h+UE3h$nbutg@hjl zqU83NIA;*h%su91ic~ZAxO;=ksI~gWI}{cof~^)WNc(VUT$O^HG9(c$NdlbJL0o<@ zo+Tm0_pTw)e!X7DG~4Y4{@;P^3z%UYw?P)c`IPbLbcVrjFBSKu*gCi@n#S7dP>v)P z+*Fa(W#u_QNl6kU2`+N>A&u#tbqfWxf-@0LAT(wy=M!Gd2a5*7LX1rn8$1%R5^&WD zYH&g-B`^#x!gyvUx&QL*6~B9V!kv0y+6RdmMRkIyVhbx%mvw6lywE{Mb3@$$sNn_f zL!+j?rE$M;zkk&~zwyhrBjfnNqe!FZ?OXdjoQOj9j6<=FX%o|pYB1lH8|_SF3x+|P zypDZZz`?G(DUw-`+x7%yvt?vW>C2@z>;2vk65WzT)!p->vHl(&s zIbk72yG3dU3^lgJ&HVG!6D(R!O_=4K6kgIe??M|3d;n`BvI&ng5v1wC2Mdh4W6mw! z+2!;=ZnqKTlVn8>%5aN8Y70NCOV;ZsmfQ5XC*Fs2!VF{D0fk0WlestYZIrzYewxNm z<(>3mZrTGdG|O8LR-Iw*p5J`gY?}F#qs`@(gzKHFVT5IbXhPce3*Gk2@5Bj zm>uL80~&?yTfl+i4OE=Qdod|WvV?bd)Z0!C6%ZsDT8XnVm62OF~e2F8)V~IPj$F#*;?q`Hd z-q#k-vjC{Q1K~Yg2VuqRi8)8cwYg3;RCWzD8bbzip6=HEtKAh?e$KUF8L_co!-yz; zj68b;6DQZY1!l@^MwB}bY|FsGPo)b$+a zpF=rAc{ABLB+{pbswJWxXWz1`Gj=7w5uUim7tU<884@{y6UNYO-yG8{0mmG9ISnWd zo@pDNb0vTx(hsvT#}rg)sLOU1-_eY{_orQvt<$rO!J@#Vvgm9EqS*rBeH?*ny?w*viz6Cg?K7}u%(W(uI}NI^OW}t5A&U17Y;$>8f$#}c9TFztZ_JeOU;p!e zf&cAa{T~qSU>Q27sJdLNzcz1dW7}+q7$PsMy}TX=B^6Gv07&HYSzg-~`0EJ*;h%(JfpSV!6-nn^&MhNy0ifXM8d zb#YqB-W2w@DKk&_h^C;bA-4)z79=oUtA9Tt#xM1|zg`&DfNXG6EG$@w@GOE`%lKAY z?o9!BIV0io=?uW|Rx4Jj_)Pm#JFokfkT>DOWge~9}{7)75w?9e}LC|#fcMM@(IET&uMW; zn>7@xDA@2M3qBL$x3|CcjA1iuL2<53knM(gU7-tMGxyN4WZc_54j#cT_gDNmuPCiL zh23;`6*WL!1^&B9BseV@j0NBCS8Q8DvkfE}nLW;BT^3x}KwH693|lL>HA6}QNh?5# z(h8uR$j*4J3Q)qmd`Hn86hbT>gQeP^RsZ}GLz_1NJV(*ZK|9s}?t`jM^Tn`$a1`^1 z2C+eLPTr_w5F{jh6msyMcTVQCXR(Ub6xY^*EWOJW88m=31At_8Fe{n?NgZ(KaV&(Q zJM@K#P{|v#YKE=(??i$g&jhrx#reRfs>mWi#O9EXX+R=_*s8tg@}X0gab*$D7?wUc zF0qv~m`OyLVPZQ={Mime@w{~RdHdh>LC&&dv`DY$oAiANr^9b1)Z)E%Ke+|`t~Wbs z7|H>T=X;0Bz)9ocVSt~WpCL?my=}N}clWQFgnfoF&;ns=5D_FWp4W`l_FQEa_xt}xNgi3@2K@LXGy%~P9f5;9P3uaQK+~gW3Qrmli}*L z$N-<-<6%RI`+F9Pc4_N~d5S2$9vekCMw!|075Y}E|2(a?put|KHg8CF3jJ)2>ly8@ z-@c;;;BnsgE|PFbd3ft7DDo|s3snuP1SrjHC;(bhGy?8sAa!+vZw?hjWB48gV4I}i zHo$N=4C!=$gm@xI+hA236N!&8X~RY|o`6S>x*gz9HhHdxCtN*kLv>fn^ewt-4Zgt+ zivvx*O~YtF5$}TKH&~B>jS_;1<5q6Wc9RKZ(WdP3$QLb6N#6;Uoz;V$Yg+F1QW3t;@x2x}gv$IYRz^_ZY_%+fvpx zA{!AY3h}o4&^G?Fq!d*4Af20`#AjH;$J-ZM5@(rC1apg_vp}(FWSpD(%enchbrpr% z*;-i707I>9FhZaJi>r`&+*M9IB8ERLhSvJfIbs@ZS1=H?Bg}o9+9m4!-+^Jx z>#%cnPSTRH56Juh4Pa;=0ChHngu=!bRO87oItbW#M$(0XaWJWZ8b7!$IQoX?V6j8= z-)P8a#Gm%FYA|W2@6Q=Y63)x&{G})y`r@JXE$Y-8?fzwh8V^5xSKd&72ot>4@b{_& zjlK7-G^`0aG3!Unq=_7VA=juR9E(trdV1pxyn(MgemMjz5UH8@FnPQfD^ zQUY(NZz1)Lb6Xp#H2lkd^}j={ptd$-y?rZ%Q7BMM_1s(QO2<4r7b!N?Pw9-i-jQRX zS+qHYR@-6oU~x<~+d#I!sKKnEw6DmL$Kjdy$y17hiz z%k~DF4KPxO7Bs7%h&JjwM&aa_b2|Glf%A}LbUJCXhT67h+;@2$i+?bb1p@zG+#@`w zqA2|0@;}Aj)(smJ&+%bOL`Y<~MbUa|4WCj%sTJ4u6%`4emnSd;zmkGd#Gy6CSu)H!tyLG6^DXo0c-%Gmj6l4nx#&(e&rbH_$XBbAUqPnQIYe)d>KJ)5w@) z3eUN@ubIOaox#vpv=nPdDT7hZst)fY00P&zeTkB z-J13yh+Pg$hSGLvphQ5d!61wY-ngSwJaxeftaVvq%WT+UW7AY2Nzg33_yPTKX|Ms5 zqruHSy$-;e#^IeMyX;ZT)i$J1<9!xFoP*5_8@ujeVM68=W{R(M1CwEmyYWnnHS&PJnTCv7@oE*{@5LqZH|HN% ztyr=%MTRumrtM^8wkjRlIE8u44v~9Jr$M<+6<{|FRdt zUD{5HbIxHuamAM(OKuGff3FL!z4@M=#|M}0_Ac5-|l zKG~4Lw%!cP+RVP%m}H?g0nX@Qg|Z9d=|I=tx5X}9UsHl^HEcW@1msW!OvUD2ob!a= zqCsu%Y=HWZ9J~XR{Jl5hX_3RD!9P;8=snP26qtOB3L48BJ8Z$NuVSYr>m!AH-^z3* zf=#gE217Py4DtAk(_n_I-9DlQWv@F+3~by+gHcPh9wHdSRD&Y%5=$e8ei;mj{C6N> z^)Udgl|ikhfEd=MgNJcdS8sIJw#F8H9!7B3zcynKT5ORSip^o10MrW9eDTB9CG`f? z;+8ddtzKJm0cQBgZ5nc@QbbhNs0(%EigT#;7(0`OKxou__}o=Kwl~SW@uN}y^S%L& zyGcNvhy)E_zl|lJ84qf@H#QREWgEPz86Ih-5gSJjW1Y@bm?GTzV2x(j*=)n*k?=?d zRmSw;<(}W}3eVGeigc(w5k^CC($%>%WNkDuzV<<)jq|n-PVMX#p=AIkxg9r-+RIi8mK+Bj$HZx0*V!-w;_x*#$eBu-g zOTt(_%q}3?m({@EvpSzFK=c?f_kquXKKT1Yu|cOD=0KCjY)IK-3N*iHqA7z=CpBpt z0M(kyS0PZ_T%1G}2U$bu9@L)m84tT}ZYDs$_>k3LKM)`bh!_iJ)OH_JuG$KcENEI? z(xc)sS{9V10jEhr!o1bruuzK4q8I8!;Uy%G5i%>0KU~9a)By4##V?i|hgFvc-90~C z6G+ZD$t5Id6%qqx7_j6g)R5P=!RZbNNt`MvtpirasyvP|B&UldoS~I?FZ@vLG-?tV6E=2hMwj0i^Vu|hIUtfPkk~6Nvs4U@qZ1_$6 z{S_maUQtTH#u=v7y_-t7am9Drz>+-b<@Oylz#_HWQOeZ^D5ywz1sU*peL>TPGZtK; z8TpgtGt?S>pBY*Uek~imo8sJj3w^u)0UI&0TyRz3=WRo+?jv6$;otnPe}#X&{Y$i$ zjC)guqlF@a>X%GnBe#jWQSs3RoyBCZJZe z=9{rD>!D+$4~K-016=2y^RyTJecKti@~RESjLhDSujw?H4>_&4l{eqwsUeH|^Z?+t zZ61de4HKA=3@AiMvH~r8p--D{s>k>DChtOUz=t~B-&BF7$YxkMHV)e>ND}TO*y`q) zo&D?yk2nP*z=2({ISV3tk8A3YClb6=9Yw1*k{+|uv&y@KG{r3$1$^XD_v8p>Mv{!$ z-2a&ThIJ(XN90=h2F?r27;Ab)gSsytSO5RSzs6Q?(B{#Ho-eygj74O~=lm?>V=596 zgq)5pOCDLei!f9cR}2W_lm$W#-y(Kz{MIU#lyJ@xzPgYbhfG~t!@X>{?2=MwLm|Q+ zwky7E1@{`ykD8m1xJzGKNbNYzk!bnUhq3*RRkH_4UmL(gfJ2eMyJD^TCYez!083$T zwUC^5nX|wZCrTQ_w7t_AkTW9~?dd2J{C?ccV~ZTOeG7XRLJdVTnLV1|0fU14TpP+2zJP-<@Pv5%r>%roz#Tj=-b2iJ8NJd(lk~ zP5gmuY)A5OY=&m=Af};-#ksCJ*#!FQGmlSdHZ@*xzhx88w2!YUHm~P;L7IjiTY6ek zw+ZnwhAkf5*FQHh0Bfz4u;8~{czpPMSR}I4kqnU88*z~wkWm|PZawGP1*e9p1tkUv zP3yQjIMi7*R8u}EZ>>2%Ii-;b)SGz&ys7EYadVtvBsEm4qfHFf_86KgMY={%7ut@>4o1wkd z@b_(GXPdn<0u%Y!J=hYV(&+p9oIRz<0z7JQPgul86B<~Sp*XRLEn=ODZ?k&YcDQ43 zx-M2 zjAP$Y({2OesJ(2q3xu)-<^$iJ2zJoy%u@4*Ouhl(W!b@w-M;hSDd!jg*az^x4j+ZO z@7`!n@ObA9f0tNcN7;YB*B_iWI?hoHo5s88BTt)h#sdW5Ay(kfpYIFVl!QaiPeUF` zeUnQzO{gfKBXpP=*d(_^ys?K(0s1d3yi}^Y524FWtb3+P$RsG5^=*nF*zbi-O|0r< z(q$c$yz>OGRn^U3h<%(N=c0W5++^qQ4lRWN?v_YN|K+mB24aI=O zK3riEY`Wpg>4MM9_*QSxu=plQIN@}9!prFyKU{u*FyYoJZlz!m!L?LuwYVIe2p7vZ zYX*x;wv7oto-Y3VmIW{Rf(GFG?S`U?%?!6%qENNS_;zs15`e%KxzjC*NaDz7?euk3 z7`qg-`>Cy{_u5Fq}{r$2=~FYD~b}kR8!E0cnOPStT~}H zhiayjcX%=wmu10=B%D}Kt>Ig%cr9hfVpk^r9h+ag8wf>K{0c%A##=3TVnRd1t+|I$ zDB(F}BoaKskd_5k0N+hP5;@Oi*tDTVlPO?)KDnpz4em*IrPT&W+0VM5$bXjXo9^U= z+r1}TG>`9di5u`FD~N#CIIRhc)eOlDf(B?uG!YVjO%-qA#F7&HkgrAYdb|3?Zd2G8 z1q&s--8?R44M}o%;?xelZqPpL8MZQ}eVZHhg?WpvBb(ko$!rrcDI}O}NA3)d;H*YQ zsTi7PO#onj;Dr_#>R{%5Bu=+Y1<vhQ13m z5eM0q9@K3Y-8P5;@g1wWM>C}b{OPf>9r9LsP;yRSyjjv|M;HE{STu)2JtjKuE2fwq z2OMgzZ%ag?Qco!8RPw$F90?Wu8I(}{_;Vr@RQ+hQ223=(tnXLsKZI)>%~H-s|%V{%=k5bJ$`35Z4fYWP9Rfo zW^7@F^Ry3yMQc*f+N=iY%#ptRrIADJR#Msp5!lmKYITl5;ssxR_aEWp)HIEL-J6^KG5()mp^WnA~DJ)Yvo3~n*)c3Gi4Nkls= zSar9#jAR_$RXa9EtA?4?6l6Sx5;jLIT02750;;`lXmt|%>j5m;^Tr=jCe$bfGKZ({ z5Vt*^CY@9@v=7L3Mn{ZQbXOb^(=-IwrS3X0KBg&V{5-q5fY|2c{_wBkF?obBZSFO! z&NPO^UEbcbtGaNww;m$P-<`{JNZ@#PU{7ZiP5J2baHnDaopy@?9%=3!V)y}A^HHMy z7}I8U91;LX_u_epp8p)%@G0Apj02dmPic690qB@S_YBtrbT4PfA80zLjr1rDFho<) zrcC!;UeYFU7#|V?y;*EA#n*Jgn^wSzH{#)EB}*fg$sZC%ZN=%qMq{I%LfTxkg$EuX zliw%m9NTsp=0S}~dQ5To1kjVq*(YF>dol}F3MoA$XuU#$0&d!)DJl%f$gARhxLNj+jp$zFSxf2m*o;LEdc@sWoY4BQZ&6| z3s6-h#*HGAxfLW2=>loRUEMpq8G+gr%qx&&K=d*UciX^CK~+(63SVHu{rW2?tueuL z&`T!5C7oPV5QCFn${UD*C%%9oxJ$wx%N@TL!5@N5{x{_YBF1lxP$Ae_MWT#V&R}fF zR>1)7#W&+`>eI*q3F;&HMi%7K`9|2<&EF4BZW*A2`*wpd!%Xm!R{WUH5F%XbD_(1b z)*2v81*L*o1tJLvgl}4LVMa=Vb;%J!l2Hx#`R&aCanvXFp`f6i#cCb{7j@&^e3N&N z&0V$TO?BT;*$(39TQ=*Nl$?V2?w1iW?}sN83!DUd;{Eu|5&LDTSeS6?jeo>tY^_9G zR-2-h!cQ#(=IrutGDu5a&?s?7{(cJ8$`lNk~&<*2ObH&#fD|%w1nqShctYoc|?SsZ(J0DH1e7~3XX%E>v{JnXPnZC zdo6e?4uxBVa4QX`WreWE5anp;=7ZR`* zyqeR{zrk>84KFFAzNTozSS7=Vu$V`@nIycaOWwBl?3Gpl2F{XWJM@H?l(0pl-wS7a z29QyqMp#X;P=W@@*@0s|*byjh5M*<|w32^~5<_4ypqk;|Ys2?az$Ey0so}?zkgVZ# z+wi>vVLUwG-AheDoPWs4OrG6$e4L)B8>L#*AQ7qSYjEj5HK_ z2&3v-)3KSH@_P+H4O02oWUFsnC1T+QU8WBQ%--#&$ac7$g@T5e4*BI4T1%%}yLDlI zVKzl9A>fDzXx=P!bhwsA9Hg*6M=8#glgKE7nr*nZf;9$$O?Nd6q?4O%_jYzk`$Kl{ zu!;2FA5Wg~hb;z8o3~lI-_vT)n}Mm2rb7^|?ZBgMdx-C+JsvOyFWPDLHW%c3CVMp> z>ZRaKv^ec+FgmQuY-#%62SV{2LDWBcJZ-&q0AX+5cWET>K&F~v2<$--Kl#=tI+snJ z+VQ=nCW>sr^f8amSgj37z&E7;03ZNKL_t)-DPs5peKBj8W0G2^fUv_AyQWNUu?fz* zU(^%@hHNsFAi85Gu}Toh(WTlvAZ#)~kJ$b0U4=2yk!-M-07jCoWKjqKOC(X;n}ZVj zHkGGll(FA^YJTnsB`DY69x1@$vn4Z>;=B&gP7rrXks2oT1;T1+5#<;W%F08OJWH(= zG@41~ICrB_*6{qJk*nd@@Y9#y;b$X!ef@R#zB|Nrw}(ZmG@c)i4b{g4fj9$+_T|@Z zEjQ@I%j)1aAHcQob-S&NCSNhffS*G_;@p}rz-osJ?h2wY!XzHq@ERPa{mke!5)Zk3 zkB^&&550@xIW8S(NaQ0ku z2jtQ5Uw+(L&!+r%n288Vw%Y>w&&{;I5S|FE^AFOk$&7ty$aBQnLvv5>8~CJAgzV?5 zVdth$#K}<5)sNn$5jSE%=QTh>4&>09E*IQi1Os4BzHu6!9h=~DJ6Uo+CU{U)6R#ns z(Q%+uQe4X$1dL(@S+dI#AP)BtiTz1TsY8}XmH^zqcmYU!yRW{8&0wybH;1gyoiaRMV2$FRWxMWb;H;4il_Vq zcTyy0g%}}kI51+_~I|;yYeu5F< z$IAs)8?LQj6~S-UH*BqdbB0;QVhW>#EeOL~z<)?`5XZep$az7#do)%O_hTvymykR? z3*nj1c&jz!NHR##WIoYOMi8nVy%wA08*voq5<{doOao%fpphACs!(W9ynTDBT9G0K zXn4G%sLYf{O~&Hjn!GIdM^7*KTJHFFrYN}lVKc=-F|Zid%I>%JO^TQC#k^R?;9wYq0i7*OIKLs;KolTD^G;pq!9;w2A>jhN3;|mZ0I0BVtHe zqZ!Hp_z@@c#~^7T!)ZO?(`iAgCG0o}LW}~#Vm97~3j~V+nVgbb**wO~nh!;_;s#e) ztOVSwK%h7ia2CO5PAFkI$mBWHS0>zBC~AUwE}`l;5#tmfBTflwC%mFz1@MUo*?>wb z?u6KEx8475-mIItu&oeKz&Hh*si{j$t2NxL;j1YY5X79fQDOM?x5Y`J%t zJ;pG7&k>)RL8#wZ0eVYOiQJF(o{u6^#JVitTp8n{CdX5CK{g zj8iOk2Zb4RDL9SH=AM>dYI{OMM8#2$#G+1+rfK^gIR&)0`!u;e{YjFCUCq&b4g*pW zWJ;qRYr{9cZ>RjF@)(+C*r+`W#RqB@!&uurs%leHJtX{e1of%={`JvgKJx78eL*{; zu!E;DI*mDY$tK_%?QvKXRl4@SM)n|+ml6LrUUTG%tL{SuW{KCnONg`Nv4lx%FbaN) zDnO@b(0SBl9lWW*8qlZ-x&$4asy>F>5#`AoX(G`;pvMOaIQ|h%5v%5~Tn6uIlXze6 zrJV>M{?GsG-^9?lVgC*JGc~VEDKf&3 zNVFIl7#$&8VG{!NmUa4>M)SsF@C531(H-2#)8@DLNIvTpP$*Vf|D{ff0$^q{*hKjE zjB)G!q4C#rKpG|3Ka6SkI*SK*AhuDgD+T?oFbm-QL&p+xIhABdQsP zjp!KlhI&`$KQW6QB)%473`wXzm$aL_ZQ@p#*~c+ryE6)EA4iax?e@Tt|nmbi!Qx~%!> zFEQtK(i|;B5C81mhN1KCpa!NOPe&ebY;`;2ZG4XbAE0+2J(tw@mZ<3zxe@ZRlvar3 z*qeC#OCy(uxin>$wh=s67SusY0Uj}*tonA%3BE=W6EAOe8|uh1L~L4n=3vM9@6&Y* zn4R$AP^~E7RqG%Ut9fJ9;dU{O8?jKUoFLJ(Et0*V>Tn;Z`|{XnnD=tUwcN1Pt3$Ai zktrcFA*Th!#j_m*$F-ER0NyZ?z(ee+JGuHl$tjMCOVI zReZf)V=AbT2VPt`0>*W_!bk#sVdHw6X_1R$w01-5Q*PnuQu4zsaq?|BoP6t$sxZtE}3`W2m@%fqM-#{)a6wmFQQND8J|A=4*$h}`_J+6``_dG z>l;eBhgwBP5!W}teVWWePUfe=xz~4j!KNA;#|P!)Ayv}`<+Vqu^*nK61{))FUwL@* zeF)KF>*zzs7R8Z+87II;fEp>}|0y=5Lq2IPdu|aohj?FdN@EgjAl$aEDCIl0c6Zu4 zF-X<{YqE%d9HeDpP!yjmIf(om(RsfYOvLy-7$?&K zZ*#da;ZAOIvK}22{DNkT4T@VtAl{&OHN~qc-n8LcYq(a$y(zwzimMqmt9UC7cWrxq zZ}^(8wMN0PZ&zu`n(k3$z47iFovM3oLBB7@z^6xo+T^9|&E8C{9=p~3R_@KY*}QS- zFfeDcbUw;xh^O3R^sMaR<3*@AWbiHa;%Zjqx%aSzZ?IWM{6v8{(in7X^C{>Ud$+pc?Qh?(2G#zGv zG2>}{LQW}`6aMUHS$vqLai74ZJm#4S{0L{8H{AAKQXHAcAImWLeF1LtVPScYjm`z^ z+>_I+Ua)hV81ZS&vRk|LYVdP5M!Z}-iN=$M$^_9M1o2eJqy{G9nG%c|G70YWinmg*E=x=b1#1>8X$jbo#71$!Yq{dIyr8v) ztz2=+CzN&v8$+3K(+brMSw5p`gJj0mHZVJ>x`4gu2f@sn3P|#NQCX0MvG576?TVE% zG7(Z*f`HnvBtarVA>W>gF|IItFB=-1hVD^SPS{_@XUp)t-aux!w-)aqkV#PDqSz%- zg!6Jjmh9W#$eF!Xm+r2@IOhdxT=YL)KEc?~O7%^T0Dt`c6-^s9C|CvG%Z6{;hT0NV zCM+T_Nj_{KK`{+!uOFUUEn(2;5-@;JM`o&ZSjy~$g(n!4dIu=SR^Q8(M68`lHcW9eI=%i3Tl(Ay4^?fBF}H zf&cEG{f}r=QC}PW>Re*Jz#W)kN>5TS(%gkzILBaSV+Pf;YL7$i|NJ*ql3B+N0D zW*QNV8vgTIH~0OwkW3o^k;G6*hQGD(4iz#Q{Oa*bWlo;k4v#}iDMyT3G{~GBTxks! z0K6o@pD;xu@_`#75-|$)8V&keY^4R$7DE-^0XIZYoY@9X=mUyk1WcFl!^%oLPJ`$b4*8 zjmF@m4~iRV6pJ1c#)EJkNzI*Re~>Ebh?jYnX{-IU>3Amm(U*|{-K-8@t6`4_w5fP# zakuFNKpK?qK`EY|E;`!g_UNSe-V-#guR4hQwcIsfowgTALPgiA@2}OF+vToD335}^ z>aV+njF^~_lmVWikQ1lpoznFd$@pqPP~a0njvJz1gzfJGsiNa4->4pa$=Ze)KfbwmP6R3>5st4 z$+qsS0d^=K3s8+f>imG?yTk+Y+Y_SzKDeoRAC2MAr*UuXwvzYYNBbbedq%P;?aJnU zH`rs-JHVWF_`3ASNSn@md*l$6Ra1Y;=B#9=f z;?@dUZFtUSZ03F>5aYM)hWqvgPN(q03X}lm6RK{b)Ty1ONPbYPs9-I)Zf`iJgjXV` zTyh>Dk*(gbq_Y?1q&TMwG7-+pf@X$$xnWsWFc|ly_>>pFXbAXPT^0^jkI<_Htrq{D zrQ-YT71w$n4JeV}`~5nyPhBQTpyaz@X2ChH_<<8%NKs0`y*1ox0ck^R?h&i4qMD+% zJ8rdr2zXjuy&-AEm#5EIvTwMQw$JOCtdo5yGZuz za!xT&(6Mb66128Oq1cI=?v?Hq6^T(6r(yd84}!gH4h9);*pz zi%ip2>+p59)=*Kg5rKq&bjC_6mQNRarf2+<|MVZ=Kl`u#d;GKi=AYn)-~EK&?ypGy z{;%=twSbostR+x}$Dfg5iQR7^g0L_M9*oWyzeC_jX z=^J%8onNErtr~>v@Lzi|kjAx4!V$+4^LWW4hjmG76cQeLqgta0aw0yFKqw>4=Z|K z1);IaGg}A{3-#zK9}eB3S-)w+)qtOi;s(OcYWSvx#tAvCSknTN3}(WGJo9{ujn)-} z+7$QJVCw3VMv5oSxPY+`V33*UVGAXQ$!v&UP4uVSGQ2mb0CZ z-Q}9$14Rt{I#s)KtQdPbfOJ3{*#QdV&5aIe`6%|r=ER1N-M3?y)L(m6xmu4*?SMKN z!=>FrI0cds`w(#21=9iOHOGkYLH@@9G#mq-c+EX?xyEOBiE3bx1cFDGf&}+*r)xCl zuH2|S?>QJS6^2p^N-L;UM?uy?(J@1?@UYoFX4CStIj0YI;KT8E2$rS8nPYS0QJ46$ zvp2m5UURVR@&S9QZC->X17g zN}b*{=kaDEP+>(eWG5j>MlfQZ&ByUfBAucjP%s!u<9UdQD1^48d3=`8_#2 z-Yb@z!W!uVk7f`20qM4=t{9tsgWW1hge7^Zh>V3##5<84wv8=f8r_cL=@^MZ63&Ek zUOIjSSyEV7TrF448*EQ2;5{~zWo-O2C-+0gMtNmdyzSsXo`eit9_|wBPLv-$;jv8G z?Na@Jg9MTmWD(>fScq_I|371Iw`AFJUFVHCGjr`-Row>%06|%xMTrVYch-#_$5-}? z;DKzvfqm~=ON_8&IvgRIVTTnYad5i3YVWl&=k&$=$y~dtK>`stfz#d9`)93O^Jk22 z%m!}wwF*@neMS|Mt6(7mg5%-5af<+vvvPd<|IjzYy(J4$G4CP5NoBhNs8>ZswkpKw z-4BKo=PR0B7SYsPi+a3kQp#yYm{DwxDYxjYG;F*mfz#$R&t^RhtelInAj)*{;0M1z zG7STHW^R71bE-@vVQI2-tg7=fH=r9Y$cP2%xx}X7Bx`@axqi1xr>Wc%w|=Sk5bSiV z>AS9$@J<->_!WMvj?|A~Zs#+7sEa@jup4g@s!nKYMi(kWwak)WR_g3I)oikJ7AnV3U78`2{| z=!0x__00maZ@XD@ik({o?Thbgf*f)(pWFTLzE6i;{GEl{QmhON+;bae`IpTaLs+)C z%5lQooRdRr0v>~pf`-fW2`|q-;JzEFyi||5(vCiQZZ%4&i?7};u*%gARq3|PEcQ8G z3ggg@m-+<4z}s=)-Vf~i4Yim|{;j`SvtFAO%<+CpOBv5`y!nIT3AHtByy0_uG7>JV zA-(tAT=hDcnL~_%f4DdK=-+ZCRsj5b|H-qYpYe6S;pInfL~UaH>V@~0W5=z(C6MDL1N#`b?>pYcz|DQ%d*8A5JH~OKHNrpn z^cjEl=^2056;Ex$w=u9|*v6h1g$ACe;5r7r?KjBiXfzq2abF(8^g|+Y`R- zH~f6RV~icGn2cDM@p5^>)~d_o2m`>|7?2@&6Z^ytQIpg<^yL_mkth*|Qn)&)a-|8ymI#sWd178yL-!FcpE(<(fT;hXW}WI+6KNp;kA$D;g6Jr7Uq-_Cp>b=GR~o)`<^eQVJLY))UEI! z&AfL-$>x|H5_c-{XclaxTrCHlT%{uxPBzF`9F+>TcEPP1Q{a#eN`kBT?<*IKakyu3 zHdX`TQhH9ZFW4H{);84Iu(b;+7nEY4yO0nwF>13R;azszNAQY);xty~k`{p2?sNiv z>H~K#mAVMJP;x=w*FqLS%;+%t$w9#3`St1prcyYg#ll)~h+?bNGKY!qMLW8m>0edw zMFrj20dLZA9|J${17D7AiYHOr{O`Gmrs5%US(EB(ImcA0tEu|3S+ikPC@QwvaE!xc zv-bQfM5wKpuND35fm?H(=`1M^U}K~Tq8#8M`{E*3>~2?+CzaKO$&Ea*RzvN%~n zUb6?{Q||7RfO1CY5LVgfV`DWF!gZxu`*jJLzZybrebD_yo-NTyAbB1rs4Ld|ta~c< zDb-T2cY}J090~YRA_x_Kq>DOoHar>mifZK*YGT4J{QLGdq|TZ*Dwyhd+$=ev2q33?&nwz!*t{pE#jLjxWxuNc{V$rB_fT@8dXrMjTTBN zR?}z1-8?6P{B3Hl3KGG#RVY<7wnak3sEyIlhblQO2luy~v zQK>HZ+(`H$=2_+_&|l;?JuWvKH~4_G*DaTLX=6SZL zAGvrQa@mO<=r*zMiLl>$#~*Y$xe^*~app^6BZv-)%0njEPNd4U%9!KL&k} z3?x@Q3dP6;bdqF{Y9puhF_(Tuql}y^S+gn$bvsY-*NL^9HvIYOyeMBKJ$8eH$4=&s zEr|0<80Z#ziPz5er z@m#OieY$xY9Yh;i+0f;VN^GFKPzyjXTa= z?VF&dV%x6a#ofk)uWzq7-u@1M{a61H|M_qJZ`}4BZ~N{MF7CyI$=S_Y?iux}IK*Y) z3UIYJzOVc4=gQpl8HrXB-lGeAMk#z)mT_9CU@I2~s#Oo;nn0PL%T@_E47u@?D$LMxXxySudY8Ea6i)?${Uva4yhw_>Cl&PW= z1HrZSWDR&VHO!@M&M~2+&anH@3wvw~ac%%QOoda}Hns-Qv@Osd4oy$xZ3y1>9rvMl z>j%0ofdjO(HH(kr7%X^WY<^7Jx_31dQDsJDU@L@AwSq`-p#tK9oq#)x#qdS!+BS%y z;#=={>w;bE8aY()CKjKUQbaW?Mo5DrFdLt^No&PADYXn&@8Y!{`y%7eF<@z4J2 zKgYlRSN|HnVFqjY001BWNkl~gfJ<=~crS?x*1^`Z-`2iw++9(JlSA0z8i9=7B>`^nwvW804wE&xYdSM8cHd?2~ynln<*g_ z4`ca7v8RiCkj_Qxgru#Vu1+tkXHlO#uqSKb161mP+Y_~v3_bnv1VcFwq*YVhF*7#j zm|UyrjE-@*SKXq+y2QC0c8iDYI6bU*Rs-ycl0E?uigS3_uiFn+@ARN?Uj{w;XuVuL znR$KaGQ?SwC|t89qm`*rDlXN|oKPMXKO5PZE0=afP0*g6W9l?4LvyfH*`xQu^5Kam zFCI~*o>NV$ES4tFFY1FK4vF(xvy{!&HJrABGe70S*H;#~LqH?QO9Gl<3se3OX9v&5H{&i;a#Cl76;I%VVoCi(%7z0b zp-zHS5ueJ|%p8Xo(2j+&hq`tQnqK+H_*NRrC$sbHx6(q_||Xu+%CB9Z`j&1F14Wdj=jH` z2b~yw++7A-@Y)Z61Py}QamSBOKVYaGW{o%OIy}E{pq3}xZ#N7bcxHq9QGG()t{DA* zC~)uA@ZAsl=g+qNGSU}CK`#byr2%xs-|ugLDjF4h zV#0s3U2q}7UJBmczM+fwfWtC02NYv;r!14xVhQ^g_!QYNjFKf**h#EtV_TU*;Ow-ep2xV zzde*oDK4Q`aH$|sJh{}rI>75%HjHt{2G19FfBDRKHE&x-#9kmFY68Za2yQUv2^Axu z-rwHv*MIqcVh8Ycyy1QvE&=xfYS>VzJ0*9Fbz<&zjbXq4>;8r=1C>hVfRIyNna=L4 zM;~dU;!qd{aYI3Mo4P4`h}m9CDW-;TAYM?AxtRAk2=~>M3t{1jmy_@JVdf&(3e zE_k_q!rSd zh8jq#aPj6mWc?mj$3QEDdsic6cc5@^85rOCz^)4QqzW+Oa;Yg@8$Mty%xI8GDiO3Uq}EF04(yfg-_UaU=N zaX|Js{gmOEnA0TA1PCjhsM+&UOF|60 zc1U+X6WMot<$}xej~D=6`wjPg;BD{kkUH#48))Q!Pjaq(4-F1ymndm4ZKP=OpfK5rW;HUprs*=w#`eW+ub_AxrfSaRvHdZm|5kK!SF z=)uJKQM}IytZw&^E(t%=i>;5pdD()}X?Zleh1sBmN{M0;#L<+OR6SMJ zQfxSQ-JURx!=<-&rjH>Rv70FhT#P3{1fn+C-lRY=09*lKV^2;I_datVzu0Nb9$%*3 zczg8d5MxOc32PDK+8Vla6b337L&>gPe-9Tn_1FjtL@a(HYL(#CO!&tOUTQNHlPGo_ zrubpcs%Hj!26#xU;-LNd*c;=r|2J%iAye=yaTEC)Y3_PS`2&dX4z9L>0wzLc!b%Af_ zLqcN)oh--4J08ZWH;?!65JRf#kaG>2_(&N~Gggk4f1XoI^xXn~#`v9&P9=OC)utZ- z>+pVazMh}-FwD^9xdfnme){ouZH;M1gN`Dk>J!IMkUl!k4fPaNu*JsST` zLPATxr{~YO9lL{|=H!G<_9x11K9)mZl@82}znAPxbJI>s5yZpu5)GadU=<|fLQYAh zyr5nQRxlG+zep>{DcOaK`;6xFJ@~~$Z6VoPGB@+ZrCC2poMFz+bP8CWFL4y#PFZ90SvOVJ%ip%90$JhZ7K3_khD{R}Y z1Q}MG9cuEg*RkW<{SBoy6sj09tclp)Fl6_wvq1HL%CHB7bZcPUXZF+>{n&B8?-;$~ zt?%gWQ@xJ^+IO&Y?8BjD#q0=iv-Dw~JJk&Uu=fMs`W^c?aPLNQFT@_jG^ePl_IcdL z;Tgj0B>Z9a0nh7>&A@QD)EI(Nnn|WZMaw3`&ZEyI-Qbdx;`A(&1qiA)XgU3jd(J&q zm+gvzk`wgme1wRu*@rcb2AU#|*%R!M3z~6JHT|%nGwtD8>>iSF;H?sFW5<2p&C4&E zzBqEJFuTNXJ|dU0i+JI=`drU;Hf?v-CFcy{}H*PI8pb~Hq>H~ zgF}W{lpWZ3X&V@f;!$v|ZMI3T&70(cMqa48g(V2$Lj;G24TZcID6X#P>ZOS1oZbbv zb+LP|_hqnIK((ROic4!=yi{+B$j?9b?8as=_AETLOdui z5gqvQ^=JJ4r{ClC?JN4|^I052uxwoQl=e;Q@Hw6%NgMW}K-cYuo+-1mTE-%8dCDo_ z-*s&Fbkz&SU?j4D7evlk;w&eLGFLEuwcR?v^S2 zdAr~WwxO|3TT1vj?_yk#Lq?t(-Rb`Wut6GNsU50*?FgP~#iw?`56{nNrMQ1q66Uq~ zqjj#w{Q0l5Y0S^)Ve3KRNt_7|2}8>|i{EX67NPx2=2$-ODBg?5$J$4$3slND8N=oM zV{Hzin3E6aMUU&4kG$y1YACmS>jGv?^Evq~`CUaln;2Q>_`GbwRv)mRHy$^|#0%mJ z;9_T8p>p!M8dPw@ro=1 z>uM0>Lq>wgy1PTTHuIDdqf{FZ-VduWhu2HpzdN{%O*%4(L1bD(i>EcH+wy`!E-JWp zs}slmRQn#;w#Cd9Tr8@u){3oFe})*-BR1ZZ2>0W_(GNd|M_%ix%yD=i zyXq}0MQfX;6FI*4j~_0&U~3T0yn<6A)+EL2+H26NVzWU?DHU70;xGRE-{5`-bXbIG z;khj?sid;{yZ8X44Inh`NX7nU2(80X(Gh3Y&j>WVqQ4S-J zn|wHf>7<$0Splu@{k*#M>~kzlyPUP=^3G@OX-BdEH5WAu(R{+KX^fG`G9Xrm2VC?) z(Mh!ArftQgt#EG}xrA3!y05uyi1*sZk#hX`DJatYK{Nir zh?mtP?`|r|S#AgjOmFRa`I6Al|U6V)z4j zSDWbGMRANf?tM>bYD5F|zT<7bc|kfLV!xKrU0w(e9r$^_V=EO;ePBO)>puqW!>IhC zMvUM40o86zS83qVuw7p8>FEc&Y*+mH^JhG@ino2ot?$_T4)I2N_!c)G9t>Pd^JzWd zaBni)%Nx{8CbKgP5$vNUaZ{ENpX!8x%=>EkQHS&d3T`wT)hK^SC8K8Iu4tv8l`Zdh zvln<1@3Qlu1JA|N4~VhLz%FJ}P>GEudDF)^y&Vz3;@(A5HULE{j&9rKU91@kc^4Bv zvRX(ql`-N)mJ(ThyH4;8<)5ddR!6om$06(w4|bU*9D+kUCGQAu4Mkkwk@s*3I{+EH zXRFnD4kn8m09x66%RXPbdBX~7xn>+9lUa1cACT1x&X6t}xUyzZYJW%ZAtwwR$HlMD z>fa@2j(tw%iTr$~B-)3BQM{osWr7tcU0`u#0^Ek=kcC`6SV{#G3@`}GTxTlI#+V+s zuo5s?!^95Q32jqQrfs7|$uby9I7Y|YG4S>NEx%J*;>w0gdEaK91$yZ`>b#c%(s-{OD! z_x}M;t9j1K5u`SDa$^ zPOcc9t{PprB&VK}-s#uBrzro9qW)yN&gU-ayJPD^|NDn{4SGk9e;^UR|HYL;zn;lN z@6TdATNmhIu3A1_KL0KWt{<4**NMw?R-NX*C&0`q!#JbmF2Mtw2!ZnPz%+r5W$YQPpCQ~Kf;)-2PfSyqKf?t3BH5wJ{;|)LWuhuLx zyU?hj0}w9g3J6M0K#41a2*+`t)hik^p6do?Ya$P|&0AN+ZR{9BJWi?LEq8 z8*by9138SkYg4gmhlBgbe*KGI8!#}uz}_S(5xRtef-+`8{NVid`UiWk5M%TMulpU) zZd(KC2DK7XBn($Om?w4MQVTv^F8EC$9QQZuzVYv#DOvr18SWW3DOAqEu?DozLBMIP& z9xb0Lx)-z@@JN6EgBW-1Rs^WxaE~!TQCup|5izpRTE&acp~Gn7C>|p;&JXkiam7}I zO;Q#QJTkvE`A`H@vao{}>s&S{7c^?PY){zA)&1Yiy!xf0lnukF?W&5cwh0ZBVNyD7 zE=wh}R?+*w^>Ph97Zi%EG!%4KE)=x3W%QOs-&)~x^p4AR$%bE-v%GMe8=e3l;z|^Y z79;i$7H|HUY$yN(jAlOhMNPeQ$U>XOZ0w&eo2f_^7*9T&*T?GWZ=|_Ng+qqI|EaJT zv_1qX9eoV^;cf#+rkVv^$im47CB?IZ0dd+nO_?28g9io1I3VNhBJ6?D?`A83ZBRGf zFvK@Qa-cHd=!eJVAZfbMpap-y7R;0(T60Otx$Or%1f*u9Vt~{9`K1c-G=!-mi-6>?x@4Z~^s;89D+IJ4Co zIvwlDNQjLX*|lDY@$8D5_-yxaIGNlM5r*4fhRcde4KH#7bH%QLqwl6xa%Kio0l4Py zV~9B2e-0H+G~ZC$(Ny2j3|{u6hI!5;e2o^$JQy=3crtL{viX#KFE&4jEdF-41$g5S z11Ex|y@U!tAJs&!ZCYbWLY=lfH)s(Yjn`t8-2Sb~=IfCajF+ zIEc(oE=ePwzo)WA9+Hx=8Cbz+?m@R$#Pu@}#aSY)6Y9HyL&LM%O2by{nK;H+-Dk+PSq|Hau@FQ#Lfee}e(mQmk?J%=b#} zc^8X{GY>e~pf(IEOg8vk)GLbPW9CI7w-&lyKW<%`Ggdt}t}3XF@E`v2f5-pvSN{p4zZp2s z6@kOkDkSYj?DHo*E0(>6BcN$6?+|UC(Dj(aF{D;c*Pis`IndrPWI3`zkj3 zA7xzdVxL+h28-66&Ku}Fis+AKpwnr%@Gt}n``W{~r}W3VeLe9I9v95t2V_3N;OLxh zj(2F1)4!utrG?1|DKD4Lzsnnw7O#?TB@mgt5A$JN9CPmoqO_*YAtMb=vm3vV3$aH4 zxdeXkmqoPFI>8H{I+KBpm}!}a+my00`DD_rbfd|8ljco#G{e^DhwdZ}pEa(D(^Eki zy->5cVUJ*-Am4g1UC5e&$_-pL-!KB{k>A>ywinqRd6Ep1hBml=L!mZ-d zlnBnCW$Aug6rN7+q7e#l!BLb#h?D-SNQSehlFX!u)|+`2#}Oiw~2^LNJZyx zO8mWk+h1|`1{-SEW>+I!TkO`n2V$aL$RgL+8$t@1n(tmTr>v9;5(9Nv3>W$KU@V*f z+cr)xUDs}LS;HlRF3TJ4(G-`>4=0YpquSyDH`F}OU2S`H8|q$mK|>JgC7rktJ*2Q< z$`HZbhX|eA;ks#%R?_=P8SlUI{7nqB0@1uCGuBZ~f z2drR-f>M>RJ2*?#tSYPnh&*pb%I1&b?PRGN4Z6%Gww8+0nv5K8{S)IqJ$qWh8p3ST(;nASt|=tM^_>(t-F0m z)j>oxMUfww@KMx;s)>BqNIv+Ecp=J77kfd^=|ZQZVGHg6KD7oNGO6oH5jSX@$Z(Y- z8xTzyTeYT*iSS$-E|(|YEZVtqj2*|g`*WO7siIZmCUlQFML^=RrjXpSOdu4}2C!~O z^j)>%3Iv@{rzu~RRO@M)?sRD|7)r%$WF-|QHvD&~w8tT-*g#qq$-e4D#9LxldHk0mlzQj1V6={Kxx#CLm ztE1#cr88JV{!ciLj^2-l-0$%Hm&|q7hurV9_+Va#QDaZ~KA(G;P#uY!tSsv=_mnlQ zA8VLt8UH2rc}-UuLR%A%m;tKYY>hB_?KcrnNaN9^YG^Xo_EaMtt2na*G_#djLo4}j_`$BiD2x=>hg zsG3ew+J6AYu8(n&-`Tw5!PtK?t}j1Bfe*gI^O?+)qVXCBF?kj!7SNO~{t8`O`1;V0 z>vHhg1H~j}@_4?l@^+?kgd3(OV=XjRPeE+@Y4vO@4<{=NB7)6ciwF!DR7!5x2yEo< zC6`l|T!C41aY|C-FI1(3L_oWl2ArN9;2nZBk<88f!z4i2+|b`q%QYVct!%cHb^l?m{rmj=-eriQ(D^Tpn8 z_=-DT?{_PH>NAuIx*q7m3XCBK3T-JFw!y~>1brTWk*f*9#pP3%)@B31HK)chM#@+B zDwMnih?y{qRA!`2eF?u#x*6?+QvHY7;!uXbWqO{WeJLgVGL_Bl5T6e(a`iy|*Hf0PZ;NQL;=05mg9#+wSo^lRxSo%4V=M0n+8l&iQuNrk`sWx>8FjVJPyplZ zO_ccdENbzWwK6KfLJGpfM zuo1(!25Lj7fX=ALr^O~h$Km&~MM?Exg8XK4OP(&6qW0 zT-?Sq3K7CCJ*BR-Ft)-NS|K91j|0^qt;7Yt*fxN!xQpU;yqfhW;b(kSS!!e% z^8+e)rZ%T$l++s-IjIw@Ck&w0q&yp0e^#RjnnFYwcF4gPRq(uAC39_)ImN>sPp3tv{ zR4#D!40v_9vTc4X^3XpWwc3Yh~S zLUCRoF*72;{bJ)%9JX!wJBG>VBj`27+p3b1AqrNVQax@2KFA!z98o zupa|kDJFL4h1>Sx2N^?@acx)hZh7hlF^+CU z)|VaF#vOoxYPh-&cJ$1bB{ff~<+U1hT3m+G>Sj{<9M}|(iL;3;mx>C;{dlvC1v!x)5VFPy4NzHK6F*KZ+{qjwRsQqf$MU=;S@ z*QubE3lzY&w^w|=KD&2ULB7GQw3(M24Y&Pn652jcnIS__%H|PX1zkGYI^gtshu!ZS zo=LdZTSQQ~;Lu^Qa-N*QPMvN73@(124G3GoX!834Fq<01y>8c9atr&`yZPZmaRk9J z2=_iPGVX=)K4P~GSb7J!8o{^M-uU)Dw~#S}9j4KDd9^p@9-A1kJ0Ql}{f4g@<7LAP za_=;g5$Zux_9tV~&mq&lIL3~l#Q<<3{@l=Iz-vEa_bvHQMI(2UF3~+I;U9FUlozSG z$iAcHWtzN_kT>QILW2817+4`x=O^g<2WB+aA=!7E3(qD!h7jxKTP1*}c20<{NE^-V zT5w?|@p}TrXdy)Brbd^TwWT)`9m^m+l3A*6A?8LZpWCVB8D11eqlL7?L_D8uiN%Is zo|y32Pw0zUB%h@5uQ#i5mR{%@0}Y~bEMlq z&Qu1iHs}9X;s_8!qfiSuyTI$WPzX~os}y--9x-l(D`dp5M$8uDUU-VLkL>hMdbSh8 zN!OVidm00!4QcnK!c(g@BoyH9|I7c2zxq!V1A<$(XGI~n3J=)*xyXJdA3nH>XO(4T z3&0iK8{{4vRE9x;lXIweJuI>_qUY%3b@t6JP3Yr^nxSGu^lYIgI61Foo#)u}ozKm9 zJSW0xqq@SM_1&Vgg2gvTx<2hk{W>GrH33RFtxHW7jwb zMa7ZR6s@Zb{qKvRo;vVRm(8%bp-e$G0=TVnk=YgpKD>64QzFAmr?P8Ktx2X8 zdM234e@zg$uBw^zI!pFQ>rnC(>9&&GLm9Mi$P@~ar8$pu23Y(cFEo>!>*+)O`f|YP z2SF1b{eO1#NTdp!=8W`U_kIAj`8@j_bp3pYeh(+F4}i>fY`*2+KN{lC2>ACy`0p?9 zRe|zy{l)Kcs>^zoBdLPlVNf1C@T7RJQCUg-Q-WR!p*oPEm4e1gOahtQIwc&Q9XTT# z<_#7wqSJ&VCX4Q$koYpm=Ge@X#Kb}bcvDV6a$YCkDG#z6coKxYs6d5M2T}E+YO{Pq zk6~hJ9(856U#(9*fj6IaVMeVRp4y88D@w*~9PYyl56t03{4bwhAUbdt!ENv0c6BOq zOW$PVwJNzEPc5oxt5@5c5<9%hnp)xwm+gYLW5|ePc?Nlr763F; zRvdsHC}gtStzOYe11aNrd2-;2;M@Jx<<*1^!5Ee!+`SP9Pwm(H-EV{fz;e~QXVMQn zFywGClB7gVWZ>1C-5<9NzbJ&Suix;t-wkx;SL64?rM!i`N-?j#54eE`hpq7wP(ixNkV2o~sj+$K}#Hb<%#<0zT zdhD3Zx7ca@_8JbAl;JerQcrJt!QpI!Y#Lyh*3Fx3lct8=d z^@tjXI`oZB0$+=cskvc`=J@&gViGui-C$!WY+G~NMOk8D?8!93&s8jmp^qbda|$v7 zJdD(CQux{IPOxNI@`;^Q;r`}Uo73Fcts<~Ol&8c@0$9b>z;|Ss@8UAYkmcQm05`I($u6%B%tD?7e70hK30GIP7Qm#=Y6+u7ez)JB5YoVyZ%J^s+v51PdE%0`F~^{x6)SCn{M62#8wEGYb*a`}At z5<^NHHi5ZfjmQ;Uq-=Qi*eA6CY1mz|l+x8ZxI9nO7ACoQjwT`^M?*dghs7mG(rpIe zwdJU=XlV}pbu-ba2~%C6Z<8E8w`+Ltq0V9qLCbdCS~Q`Qo8l$yo96z)B<75l)(l?g zgPnaGHn^!~$KlfK-sOVoQIsY>)#W?0CZKY~%jJSg-LR{;pFOM)0ihEI2aX^~I)oCb zN|;PrSA@~Bha)^hiXqclRtog2O1+8I2aTr>TbLI3(7HBq?>w_}rgZ|gWKk>1T5>G| zq<3{@6ts_!AHw$xo$ojBAH@cJK!Kl;MGxG6UCGJ}D$P?e905i)d}(DfH4+G<7DH(? z_YKeOifi3axF%e90{xK68S@?AAu^@RwUjJHZTJXNga8gBl2DqI=sO1Zx3`WZ(mO{X4Hni9xg9xmX#l*-E_EU<( zoM*|IpMiN+tyU3zFpLC<79Ux6{kag&BOjekyUGI`Y;E$>qO+t8BxIA~{PJ_tBM!IP z(3sE)VPjJ{lm&8TiwDVPtS)vES!WM2KL?AaCDSH4Fg4PoR^VIkx`-y2H*A%U((c67 z#;{^(hV=xJoE(BRUM#F#x-z&HmrEf=S!r){UdF0Eo(nPDpAbRPhpcpcZz!L~D?PyB z=92XExxQ=2Csl5p6|T{b|2W#*N4E42!qWMJ?e`x+e4N;O%j59Gmjx}3Bzs4dmIn~! z{%y)cBxlPf_sDlQ{&_<%!LPl{H;o}|HrS%Msm>cK>jznGGD(8&^cpE1&W3~`$U@3x zm#3P)tD?~pcKKo(2<0V`W`UxFH`xEjJVTU?g1YOIPh?9k!clyuJ9C&tIto{?H`7Fn zjhjKHTs|*i1lvLdrCfabBOp;+>W2H#AvEx|TYjdB4G4$@;O^5zBM}3NW844;ieBF?fd-T8&&9W;5hCcSJb@mxclbLzQ>oRA3%Hoq2u{- zwL!!2hC>xw+ssnn05-U1y>ZCE%}ZK*15IS{IRs$uciY4SvEAjgF&X^RAAZ0;y*}ap zdwa#_!r1o%x8shxM&oLOn0?rE^y%dn_>-3xTol;Hj{7mC@SUbK(>6Ygfq~iuPg_H| zJmX0PpRZ5WWFLoT%L^)3^H0NyzMzF4Am3n0N~6V#cOBvlnhh9YfTR!pmC~N(&r>JuXo? zN;NNGcfVivJEIYS$%>A`j6n(-3f8_%MdtxN)@_R)%EgBhbY}Mjm7?ZkLY}bKW8ph~QJy3W;#bSv@ z&hv;Su{s=R_yEtj+o-jIleaO45rvFuUSiOa+t0bRRhQW@BNZueUW#up!h;$ABnmZ# zXJ>nK6zKqz^Un>B`(e^N`-~&O=~)&hWs5gI5j6=NIL0EVBa5LdrC=-DI={{BaEc;z zp25u321;!98BtC-e;<<`Evqlrw_@Z?>5$LG`?3R{JI~->;9Vn8@!_@q*q!Uy2~6OGfsXUtLahizx85cl@f_CUAEqMpwbn zJ4Skco7p2+eTWKlQd~;`M+_Ac_P(Q|0HaUfr2iK({gD<^Y3HCMhet9cCL40!I88va zd=QB?JK=N*2ar#_V(tQqrY;dKerfgW30!|+`T2?PbBxpm4=h-+G8aqu6Nk-|} zCS0)_^_&Sq8ydqIL>@im4UA9$Xh2c?t=RdJ+hvu!dc)oo9&j;mAr27gpNhQbdKr6>_iqn`A4&&OhE#j=;?9a zNIn~vgouDyF)tfpdH`%XBZ|)do;?A>&gq_9oEQeMzvrPn-*148hs@+0CeJb_@gWBu z(mG}=BS=AFX$_og5&@|%5yX@Wxz!H)z@n6zsZjyj=CnA;I+)cpi+~<(_2s$Y3Xezc zv`Q#c7mxg7L;lbKs#vfzppdboAkYWVWGg8>>s)=`^y>LOmu%lmK-49{xD_ z{o?~r&ocLidE`lLaN4lb65**I3?_8Wt{+k~9=UGi)6@V9D7)~QWNX2hts;gpZw6N$zRS?9GTTqHsrNo zF8rQA8p=#zFUU&sVS>TW)V<4viswMmblj0O;w2hzkE$TLco806PsX)u20-HGu?W-W zRG2`hxQ}j4TX;l$gZ6s+W?SAqa2!VpdG`IyGQ!8*r^vox)#01C4a}Q8e7qs8rJ>dh zqQI@+Tr#Fvq?){};&yz23Q&0St<(Wfb*n`~YtJZDacLJ26s?EIa=AZ8@$ZHF@W`Y-2DV!8Coc^Rg1`LsilQAVcc-1k4I}uAAAiIr0e*XF_>AG zFZUrGRe?i?eRiTaLN+NKL!IEw1|e%sy>+v!({Uy5#TsO6&vZI`;w}Kxfr}%WJ}MT4 zC6YvSW=k{>25#bHW-q#m%lH_CqQkt=5w}L+hn@1yL&ap>#HV}R*UPK zj&sJY(TW0~)NStc)HmhCsI_3L700lDCnCN@9pLty?>RPRs=^TyZJq$sm0IH2S{PC@_Ni~;PmnDl=BWiH zoV8>Jgs4Fw33f7x0ejp}2l7sboRCoFZw&+QGdoT5zRkEd0 zEC91r)}IMGnXA6SzIduH{LD6GQ$_PX(`w1)IQ-6`uv8eV6|M}La&VxO1`uPbwgqLE zMibd~ez@d0p}-vec+T{M%*1BO^B7bD-Drx?+VE*>IMh-@h6rA})rrTjn(fx@*{D7= z>am6OnoMF#9Wjm?$};lQ8heeq2r3mE$9-B@-0I(bGe6QgAgAp$(E`i$#+TWCZ_(jH zxb+gCnIf@lH`f)J9XaxF+3?xon${5oPN(FIS!0^ln9{s6ptS0cP6-TamKU!*jIKm; zoDwF&!dg5=HI#`Kg?5EJfajo} zi;7tk=t8htV^(O<*U!&Z=b@syWQg*KR=B=cCc>Zn)8Ar9$G+c|;f#4DW%2ohq&UDl zA-yAL%2z|eM~gv5i4B6~8~GtSh6a3S-nis)SoOFtrVWiZFf}jZs=cqAv&HpPe4AeV zfaoZ^fy)!9)ZDJL;y;IHL>6=L1G<}sMHF2RFaw7up4$bYmfL;wH@x;Yh%#Dh=D+j@ zl#DlY*)vLloszEDQRvAFPK(h}2#AetD@yow|2bS_Deq%8f4FK#q0K>X_IK?vJo2gF z`TEI+Bh{SJ#Ne{w^|<4{-*IUho}WKicJ2O#p^8tJhEIj@bqKx<_Bgq>+zM`!vA1zW@4GjlvtSKRRgkAmNdX6w5vW#-CDR}xuzJv}Jr*xzXFhyb zgQA(#V{vQ<>@WGbeo8@Zzb~A=sl?b=KXG0!?#E63{L>O|UL{0oqE5#vl91uYE z#-#c{&^F`}*}#~x|H2i3Ss@HeWuJwq6}s6ELbASCjYNiR6&;)uXWekvB9Z`(^pIIr zyPeh5Qx-BJC1jz}p<=RRUqDa7IeTHgd@iEHVNDi?S4wkukrh4Zw{^7ylabaF-s!U6 zWAtp!b!nPH1gK6!j(gAiox0c!ieEdDe(P+^P8%7W8b316^V!8U<%BB<<bIdZwOYdJlBdB5bk})eR!0nr(A5z*h<6a_6e7|;aUrxxL^aJlAVQ@ zQrsh~5ZR$%Y;ITBWz6Oz$OJ*5Crv7P0^AZ@uJ7Y=vI&>799OVegIF+-kc@Cd8 zwjLk(PH^i97TIFC)uk0XkDEtD7!s~B z)5R7-lz`j3tH^eg68>SgCKe`at>UQ}S^lY*O6OZ2OYpFt)fu~G%NX%%@b&(hL-A*Dq|U6j4Zs5(u*xh_z-bp*<^Zg0)9OKt}_ zp|xqk6!C$ldR0KvyA+DaEBRxTa&j)xYWboMI$!k*Q~i2Xee8SZ-x-`-ho=$Lh=)*YRp^2{{ zXsy{(72uOSmv{HkaUa7xN+O<1K2lP3A9slC?)5ae;*cHvHhe42AnHyM_F{yq)7lFZ z7c1zwT1-hPd69_-j(!6vVdw#rXQDIs5 z3c3{ht6x2%6u~P9MH#QhH{6aRJnhkfKj4zsDi zhE~>4JM2sj9|DZXyYRDt@U*>PAN#ZmiI`N><$^ln7%n0FQ$=j7>@Zh{~l@IC-8>q;s$@h(wrxeUEHkqZ= z3KY_5g9|w@C(}h;X{OFI58$8;ChmOz+XbZ~qpbvjU29q;u1)b=TBZVY-;l>(he0;H zEE`5qy!L@X18v(dhTu4MPgl{D@KZE@WDp#a8sBppHJ8&f{%)edaRS1ffIn)R#Mw=d zCw~<8LEH7#_3(RgFBi(ACa7H|4r|oM)lUOLBnOPfC8g>c5trXuI1%uml)(UI1kRh$gr+)j9#J zbb7QXp%y}`Y-%?Dd_@H;15|663;el4X^GKHgb-IOaQe8TNsl`N3hZJ4x-kZR?uVt( zsKvL14UB_)Ul=SF8P`=(F{NH>6)t8Q*-((Iz=$$1()5b&D2u|f8i0%2rv&F~h zHDj@f=v4nqT$%q}C5Mp)7=cUXbL{8MsmKCVjHMy1XZ)&g!lHg!foz@(qBv>DiRhH86`Sb* zV~>Xcz&iMt>TX~1&?6vqGFMdT{gt~6E?Fm9QN5zr2Bt?e z4lRzRT=9$u#{rO_k@>;z{ciNpc$i7|n>!A=YGAqm@X>sT zMYNJ?0o+F%Me_MJH6T?VhkuR=svEX?!SEP6(t%p9*tRQV2&xR1!4kSCKpjN|Py7Vk zcL%HiKg#d%iz@j2KG2o$f=}4^iZ5?}z=n!%_g9S3(aEkC6+4uliura2HPpiRdjE=2 zHgt!asQ|QXz-^$5pn~vnxj;)nX;&1zfe1MIn>Eos&PavadGdd9o>cO9&5GWQYagmRx04KH;Wl^AC*jd2cYg$wbw2V zxngF6d4a4j%xEUK!UB`t?RR#MO)4VmoYUV-4p3 zkap%dh;R8cbDUW@iN2IEO2{mB%~i+ZXBWl2-|*I|ZLe@gs~aj;+ygd7iWX|5G14Qv zOJ5|vZkuBQmx|Brf?8Wb*lvBp&$l-lScyqBBXNLS zG*bSrtF+kxYjDfarEoU7!3q(FvXMsG1~2!2ypu8`9#;ivY|=eDd}BLj2LxR-RWV&= zk07Lwu>)xKT<~E;p;(&mZ2CbyT(x_iz23AZ_E_B9@*(MuPWzO#{(WgE`~uFrlU&4n@|C@(;Sx9)6a7ba13HKp$vs zi!w>^E+uR_z&uUkw5LCfl~0N`eT+&W{YOIK9$)%La(54xC(L-s?>z(74f}^OBsxRX zS0JA*B}Oixfhw6B?sIO^6TEZ!w`R0i+Zyi2eUflL@~fnJMbB{^%f_A}`X$SYRrs>_ zEKiTd$+$PwaT%|=eY`MdHVk=JGb3Pmg9=pzSJDhK*Fuz6gQX!qE+VL9e7 z5<5IAScA+}3tGRT@CApwStL!weUSJ@wBYO4UjbJTa8Au8K+HD79&r?rcBNqS15}C~ zydLMJ%AO|*l)9m(lA~bTDoUwnt*2M4wT>@eU-9wY@#V6CSh0y>j2~$IaBsFL2`pBJ zaQyfkjewzyg9z8-1Mlqv1j6&S`*xo&u%nD_Ll9>C^70e<06v;PE93X~zoTkFZw)#G z)ibpPMm!bg9Q^e1Gma#vNi7yw3N`mOvr>x{ubZFQ*klV>D)3hWgE(P-%Dx(3Ff==Bt)?xYMJe4P4 zLJ=#btriddz(Cn{Y=v>OD=IOr{ji803`jdrd5?WdHj7-Gc&|Wf1J9Qy0G@O2kf=h% zfK@6ktq-+^Ta0A*=b(5hcJKC-U<-;{TDnZCwGC#}RV_AH92(7^KiK`u8qxqTUQ1b` zCT*)Sc$L%&Fw}udaDTtwGhUdN0$h*7r`fWUhz5(#@UCP^xPS{V(b8WQjBD9#P!1Snd2%M8Ir`|7&uRVA1{y5$h}5ZQ@Z4k z3Be$OOD)2lb`YB(f-1G?c}1Kqn1bklK;I?}TdA7^v$~7D_gadfIG7gb|~O;;B}Y#^^fG zhe5KKH&12p24F~L`HI;0Ctip(KLaiD&a6h8`&ciRXB_Rohv&M-a8KQ{o~a;S3osQ| zLOJoBCS^b4qx9^_Jr!*0iYK!JfREl_W+9y{iBc^lPQ`~yHq5!*fD+B3yo)|itJ6)N zO(zp6J!rf&z2fUY0QVc7bt|d#d0{KCTxoJEf!^2zTuL0aBm3LFxg>&(gT<^oOGC>- z*1P~2F_;QIhsO@*ZLAoFgk+Lgnx2@@#Fi#dJ5!@^9Wg{uX{x3!guN6`*if`CxKu(1;kj!+GJnmF#*K|a3Yflpr_TQsod<-q!rlqRzo0@pAp}O`A#CQ=HGY6`rC#AnZ_$@8^vqZ6O59xj|I&D_4j%E*L6Yw>yRa{ZUH7}W-qQAbUSR` zuOFaCRKS~u17&(4mR5fS=FnO4boy{)>GSDEiJ%}1KlJUvD!z#vqNpy>CEhcZ#%_9c zz*);k3DRL8sq&f+tS&uoj*ti1XVYy<9l6Z`#7W2=GA>8FkY)Ez^>NII2X8vyF-(EAJg@csJTuj3B&wf)uOF|3$3saRnG!*W)KG*lvt-q55$KzMd? zIuoM}h4=$MUbA`e=fP3QHd`7pB^mB1qugjH83+dvU*->f_7sD(B9KCeQQQ|?nDDYc z&mZJraqp~E~^J|e9VI?d*4kT>gmIwn7+PP?@Yibv46~W_jEW`y;aGAMzq3n&d77jAloXt3X7$ zHAMif?Xbo_HoQKdNa!V*D#nWP@N*mF6K)x*{tt(Q6w=Z+OfTnY1b%*or%pUViD)*U zGrH}(r6Qf1I+6er%|S-Uu*2iI%E9M0gHFWb7#c@$qavqgYDjLOb8Bze;#GBl*(isIbufvEC(vux6ezG_?k}^JQg+Hk$oy=iCr2W2+ zjwia{WqU>=7{wieCN}sAi-Lr7()j@z=ZF8M;?HBKaU!4$Vuf~~jSdmBpAc2|^mEEn zX`!gAF1CaOqsciNhwuI3vxz-#981~FEAL+25YLJ@Bvs=|C^j&?fuolp^_y#fTi=`5=L#~L>#U;(m zxRip6v(~x_e)Nt%ejKPQwrG(l7$0(&jSY%E8cM0TF;%tKSV{qg*}s?w;$-rp_h}mw zy+ODEHwKmEe@7!x=e>)W%0m8`O#8}AQekvYGMHz8_#jeTffvsvgQ$uRfF~bB({iD% zM7U|59DoZ&ZkG8*zJBsguNNEL0uImgk24QK&YzacAebpxD5`{O>&b`NP-gtA50ZsE zQZ0t~w8SYgodhrl#UTO`H`84}c1^MPXCD>~SaOaF#XMebReYVdv`diLqQb!moOgS_ z+K_sY|NBrP;&j*ZER#$YfScX{1aD_`G3Z@^ZMvMRlrA34xeeWIOFppS@dp#2)g2Nwvj!z^a>{oKZ}N)E0=pL==DBsq)HobO6+XDlz+zM! zq@)mS9_`?Ou@FM)%%h*skCM|%%T<2z*V}QaUs21RWcEva%FWFXYbdNSHsDoHi$FoC z8$_-i^#hmt`%TggP^rH8ZYUV|`t&m{TSYBKFMZlJT&l^R35-G=&$!}QfgkU0_}}Xr zzPAI{e#MV=SZ=Ks;tE9@9Zdu(&HlS@qhIzfczu4x^Y(;G+3@}PhU@5PVpMILhFcML z?dCHVHPSDW%ZkUa4CTl>4yto7PouFRMky7Sx??Zfl+%&tg$Fs5*=1*nOGv%d;(rvj zxu1?T!RB&xb;)vEzlCNzTNj^Hs%@NmZq)4896q>F2tL~36a6UWZR^uvV2@f#W~s{p zZXr^LH&P=>u;P0IfWMT8R z$MANDZQxe`E7RtYZ6hQY-MPh& zi;ekIaE8=2o11u!>9kX=@pj=~)Km0|X5<kU_y5V`-a497z)WaL};b*i&X0!;zVNT5liiKDOaMQw7YsFS8YT5AZa>3^3ttk7Dv@B}Q0D;k>%CTms189Dh#eQG> ze5t(QsTN#Z-PK0NwKYp}SvGO0^l~t#Iw-I*8z0Hf>{4n1cQ1RLFw4bfd=|&bgN}gD zR#1>ZZ>hLyp)3)e=kIWiM`Z3tb+LMo-pI>uCCD>=Eu_~uNV-FV-w>;Fpvg-*0^L^o zH~j&frS5cl9j^bDPoEyU8a0&`KCq?J_!NtJ#)r>~sY1ktC3#OTToPTL0c(s{Dlx=I zz(fhxrUiaRYed)`*KA726OC~fqs9;VP`%4+pe5Dg1O}uv9pe1AiO%#(0-eu+w4LGZ zbUDrXQe`o_@C<0mc)VN2oZcwQ=`L~PUxr&g?8$Gt?Hjo~eFA#d2Zt(Sl~a5^9MJpV zjP)Zfm&=#G+)hc?8)Lly#J{kJrYT;8XylgbO5{Tl7SBb+%Eny(TDcvwOa;Oz^#a_Rn@P2c@c0*xtWkj3BHjqyN zT|Oi(Ar`ewv0xaajv1wFsJ!`s$R1JBtPxO?x=RxE%cr&TO-%{fl>;#vf<}1ua@tug4b=w z)8*M31vO~TUVv9^=+`&=_5BaDZcv_U?>I(78!b0}L*caMk;T|p;#1l}E`8bzM7M8G z_~;+_aLLl}zulZNt>V6GHEHc$HXqoyuug|JL%w11p$fTg^NATxb;Dk2;#9f_4)xfQ zEPmk;UlU{{3l*24In3H?#a4Edx?$fo97C{kP1!I*v2#tCWq_^XP;R54kKqL4>8%~Z zHlR;+Lvi{hTt+-$GalyxO8GM~&d_7Pxa4)vF=p1Ul5ctym2D^!N|v(3F{z3-n~FW9 z=aOeZA6Br8Xxh10yjt1P=N{hWux8Mb1~IeF?O|SgBWkasBzf8zZ8ciDzvjq59okXr zWHY0oGv<5g6+B33&Ql22BS(MgdmXOK?ms;(U;gYrxrHFx4 z&o9?m82Lt=3D{t)j-%W8z!BS`Y8!5!?6-a-4R|Z(RxL8hO=g5kNWrJtYMoysDZBAJdPn>HF1WlOW}LdV=tV zx=K&=ZK!EN>PN0bnePdC9vMr4m(7&sR(49pCqOCPpHb}kc@)DsUK;nLWAl{5&sumX z=dyxg5KT}UWy7^v4d>4@6-%Jtf=b1j>Wn6}g8CYYGnh)Fz@O2gpdNgff>JIHT(gG# zXbl>PF3i~K?!(8C>W!TQT`hvphHge|?)~s>m>U2|7jktuUH=JTr3Vo_-iVg6qm~Uj zZ+O~YaM_;mRBJ9V`ta*kSF>J3)tomsw?VaQN={l3m+vM>*0k7vu)q%K21b_~Szfc1 z>0Dt(9{eYDFRgNAu0W~avLy`D8qg&*VB!7*rIN^OEYU<2nvLnD7Hl+W<$7u$CzRD7 ziUrS?3%*{SaoKj%!nkY&U#l$_qQ|$hZJ0$OrKX&cV{$7HRB-iy;_&aQlQUp^1G*K5 zrj21q1m2vB4j<||JIK>~a(%@G`?g`CIT(s=k;BaR{(YcU-|+eX_Kr?{*In>gX-a0v zjK3Gn`x^Ba@m`d!a$OW@!8@Q#d?CW}44#VH)2rc?TgcPSv(m0lXYvB}qc`lBc_oCnBvDs#GN%JEO&Z~wG0*BP)*w`co9#KU8yJU?jOfd@_cgAG011K|On z?Ayy<5;#@%ltStW)sNgR=5#YJkm&_H`yt%9N}5p-b91&lY=V@2dWTs2r1Q8Sno$j) z`2Rv;uIa5w7hJ+@#2Je-ZelCdrO6D^2GtJ2aA~^1cLXCJ48#3TmetAm{y60_mxPSD ztHnVfBPWPVyBz>QmY0^KWpzMPEQvz4${`U;36O1{x?Fu&VE)nOpf**-mzQ4v9saqJ zk&4;lk-YKly`l9t^PkVXywkWH{DhFGc>%(N!WBe>%l-vVTfr}1e@SJ*RxA2Y9PNq@ z!hgJM_|Ip;YkSA_c*7rWe?#k6yd57nM#sA}%WPInKl*@4zm?1*I6_LB&>lj&HZ%yD zbR$eF@XO0L{OR?Z|31N6Z@Bhm#TAX*SP&_$E@=`l#EI*SvRF0zdUBvz6f@$v=N1d& z&8g1Ga^dinYi{F)Zvg|MW%;W+3K2@REo&vllgG4(1KU&u@6t>r?;G0iRfEljP#g+& zl@9foFMv?V8bkByTf<%1Wxz#Cc7PEZ74#{UP|!+ z8s{df5M$c{YF9BvGkQM=TiMWM;Q8``)~?oUk`2pVFBgLgK`eeP2;9Ubr({cv@Gc*v zSP@pr4lWf>#UlA41Kskw30;9Ly^SL!cQ;p}#fHCv zPHa&%nF(`&-C?Na`)N|b@??c@-Y|}Hl(WA ztP=@pDY%q^y}0Dq3GidQ`^J2Zh-YrKvms863M~toxg8;38Pp2Yp;KU9{F!tol3YQq z5ogIh+*%t}p^+o~)iWM#GOa>QFS^42H}ksZMvO2w&r=;mX94S~eO{o7Soc5CT+sq} zI>DIMI6s`ch7lVS)ZoyLo6Ktvb8k`rurS@68=c>VGXTirbK+$?TP1($8J>zoKp3E#V^ zv4TO+kCtb%4WLS%aQr>*CEcON2dNC}PVZliejR zxq0->u_aoBy2*+ED5xAuC&o{g3!Yu!6Y3~gQURhdvg~lXPWM{zv|sR2tU_X6`4m6< zbBMmMk3=FKj8F=pve&U<1J0hDD@#}bT=mF|Z`+nUoUV#PI^I9t^FAAXFEJa0uuBK~ zka(-)18}>3x(Eswb*uR8Pru>MfBrB0{`+_Q&tL!BW$0EH;XFEg(1@;4b|n>&I!y(W zzOFs%V!}*hn)+8cF=Y%c|G3#zz4_0o{GrF2 zVWJ0fgN02*cYwIh5g7lE75Tk4e6{k>Y5~NVGeVy@2?+m4mh}0{;rUDTQP1x$;r{^G WXl6t{Q#Ww{0000 Date: Wed, 27 Oct 2021 18:43:27 +0200 Subject: [PATCH 1315/1500] Revert "3.0 splashscreen" This reverts commit 78c1c719880cc97fc2719e6a7f67509459cc9e6c. --- release/datafiles/splash.png | Bin 609962 -> 737984 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png index 74e239b0f98b148fd0ce6c5bb4e9e9e0b304aa9d..babb3e30c6dfab87cf58ec0aedf9f083b2d03966 100644 GIT binary patch literal 737984 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

hr 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Öz|El4;`>h< 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æ=0INxo{50Nql?^`gsgK$lIAKw1x=*-w%(F&Ju`tQQ-+hikwc-1oOBCNhlij;69N5 zS_1+|?8(T9Bk#mqm8K+s)lvOq_Qn#i{Wog>%MPkVn>VvZs*%D%imw6VG%FBFQ0sDh z9*w#MS-lT@nnvg-?>yL1O*$Mk8Wa+cX=tjWLn}dNuPfF*Fc@A4yoY#EtULL>D_C|VJlQTH z@MRR!=T+o{*YnHq3!>6K*QkFA==5>{zi)cBx;tQayaW8j5Q2HXU*wlVKWtHh8{@+#( ze9Ool8)9ZWr0n;eZfppRuhoVWeQWO3Xbd8(!_%U+r&x+GZ|sBtDeDJS*m^|h4(%8X zH2Dx>&x5lbkGa2p`(TZ?>!a?51o3DXgK?!_;IlYv#_5o#_F1Ai_V%ky;naVoutZ~* zenR8k#-{3sT6W(fVdpltSWX_nt4DYQ9g!NbKtA5@ejgq&iGFTScTs&BF!?Dl&ZopS zc4vK|I}iGP0{Q|HcZ_PYyJwmF&Tszi4@DR^Q=C$cNrcPAObbj4gcDR7vKX`hGaHh5 zJXQmvGUL_|zUS39yQQF!H^u^kB+uxZ5@F6150NxBX=Z{_THL%F{{LEBHeXfoYz`k; zL4IMXMJfC#WOg)t+Gpa#XUtp-zePc;DLpdr`l+44kaP_$+! z&EJtvg1qVvjI{zxUic+82nT}!3N8hi3nY1wPZi+v z8BED@c#0xcT%K97ysfOtuQu>XDwEO-mZBAbJaUmjf|1Lm*Py>%!(ZOauvm*tZjVa}uXAr^4ftX{_S?pb2zQUXGCEkZC2uo1m5t8`==WzJ<*L~+0jhIH< z97Pk_Li*pOb*cL#$-VjB6zj-l)_vSq_h;|6o4PAt`i5TbHVd6tPlMS0nisuq3y#rM zTN56)#I8^x9Am7=y6kw%fU`KGJ20>hJsyIp&GGA*7;lAfVZxai-#uUO-8XM>xxB@6 zdcYH96lUC(+aPP-)NoE|v(?2$yT`|Rs)5_KyrHKUCc0nLQ!ZTYeK|kiAqj3FF@M9} z+*fV*(bI?Up3BG!?i+XFgx~o7Z{qFcf^V)L!mr#V@8tegCf}AvEE!P>k~+M(2S@I? z>mfassd$z$`|wM(?aIla;P&6IZG3*+f&}h9cM3{%G~@$R6_K0KG|2Fw+fX>D^*dNc z0Xm|rhyZI|j0cZKLp|5pg4Qb5$Tqi~XH)mh`JTEQ>YgqW9k3(1qp=?Va9g#$FC)h= zi{0Zy~@k97LuIAV*Q>+xzrJS>J#jo6u1$+J*2h7!FQuh##a4aJB5%;RgP z5uo9+uC9vdpG%jdtN8$NFEO8z_`rlWRSE63;T~NZo^GZ=xd%Gk-mL=tn(6R9idl;) zjULeGQ`E7Sy|&ToRCF9N%sYo;XT|CbIUmFW$KKRvSj1j~H1Ix+WH-1OiUhU0XVdZf z>HG|%jgVnGDZcHD;{7F>(HLMvR^>~{y4^u@=yjIN3s;+d3TA=3gZ} zTp%PNFN(4VnmT~0nZas>!oh1=8zi>h(kexJ-m3A-w^?71#!p=DxOM5Wc}I!Ja>W(*><1R0V4CO}Y@{VK&^F z%b%BOQIwiP92RhG1g;IIvjD~43y=>Fu1$T=U=2$FzzwQ?W7OigzGYpZ z03OnWZ$L095KZ17Q^jJAiAWCPwj+?p2`wYbEcnebey)9u;c+u?<3hVztBwc)l_ zSOb&=!v0Xbt{xecnZ4FvLR$;C7~15`w(5#}dBAeJf|#L7s83&mE(D}GVlsT^GmGL% z0;>x+%_!{ygn>8H$$?l9SSy&|5X18E5qg@D>^5?fGci^yNY+rx6POt5^%>KAK{LY~ z+v|jcm7XxoZ;<3IvP?)>U`BXz`5sDHz|#piH8gsHEI`XA%&FqNEx}HB@R+eQ18V`N z1jFiEq*lBHO9}e%8-OQR7U-&I$<+%KwaZcoMiOyr6~qSN45kXdBO0tZz_1Bml3~k= zgo38*Qg|eYs#{HDhAj&u5sZ0EX_I(_qKFsutE%UU7IhiDxHnTS2@@OMeCrJ!zj{W^ z(}0bzHju<~U=~nP&@_V|1bABA&Y0dngg5_`P@E#`nMXYqN(xW>l4MX{WAz#0yjw*r{q;}ch*xJ7(!xVqEXPEbsr1XJAO!B+`y%&}R9@}<~-AlE;G54Qu zv3>D~xbVuy=Dh8i=3?%dt`P|~HseGGq2U^mwllqqN0az-5+wFMWzW3`iEpb$Vcng` z+=mNs`f_M|T2w>5+1h8LA2Mu>4#L7t&L6{bqC-n%w991O4-3p!Z@mx^+G6&)=N|wr zFq|Ud>n(eJd`7}0i&MX0c$iQ4{ukdy${B?xEIj$3NE=RRLe&N)U}i#50~13Nqm~*r zq|j1^U%6=;;3y#(x#SixX<)`XnXu{#Bf=%+0J|x^DGQ#Kb;wqS&)k|bYlQL5`>#-1 z*#UbZSC)P2&7ofN#ypyJi=PAHX7FdAT7ks^B6H^^bWdaV*KQI{i)O|Z8H~MQL$xBM zypvDva+L#Kgcwe1zfnTP)ssm~8;o=eKE~bJH;(Gti}TyY^|OKBbk~6QOzwUm8Ql$VKC?-* zop-jyK&4Y1I%rXdV@s`L*oH`JIb}j7K?N{@FsCULR~Gr}A#>L)`c$LAZUdIK4G0w8 z%ug{etQv!SvtdDj?S~P&slu?RbUV`awC>F(g(|l{LpsmMsqln3cmKT)14JDDictUb zO@052Wj9zL-X!Fv5e23j#L@zSV)U70fX`StK4ll_Y9Z$POP+T3-=3R(d?BnUU}OLQ zAOJ~3K~(Olh=We%*vHy#b-j7bw`_LX8{Tcljpzv*JmSpn5*TbZOzL^*-M7@`AGS&1 z_K$%60MfgG-)nhpdm6ia9#`?=4OYCI0306LaYgKgEy=8Tvu#d#w$`B9ASuDjflF&E z5GG_WZd#FRb)bVbu)(1Z^{9I8WWZ#(8QRsL6-`d1kmPDZH+*Qpshkx;=$n_=r?o1g$D6%he%N3ZN7(1eV%B5U9rY42GIVfK8e4&Bx+|cLL5c z;i&@a+MrQViYb=XhUkNsP?{J0)eNj2hgGX#6+=SB)f!TsFqJCdyz3^2F&Bd99+|Y=9^50 zfNO)CPH4*tk`%N`L#qWWH&|;Rnfzf6;hJz!lwVw%oSG6aT%!MZ#HtwP>?7xeDiuz&my zArEh0R|Se8$qAA&^oxY$oA*u|mx8($5GU{?02bKfnY&3cR0~8Bimm=|l{2(d_b@Vp zt2<{47^zigQwOg#_o>&L0o)+s#Qvr_;9owWX=v5Z@(B=u{773EE z7MdqPt?ClhR**$vV@}bS_-0CokS@TR?|ct@DJYMRxGfEA&1HH*9*xHe&@q?zc)@5pZ&=tYSOVnM%m-#5P%*5x>V>a%8a>A0*bnkzJ~*2Q$S~y5 zw1w=txoRS@vk<<1`hds(>+j;**6?LB*z*mK_!+)~@8Qq?)xUzvmtWw+vf?Md_xJDz zfB3iY5#L6B^92eyAkNI6h0o1>?^8V68~JvD3M0;M zJy}D*9wi3D$0#W(2vg611))+;h>h1V9ovw8RQLE4!6v`&PfqvByIef7C)`sW763$Y z#a@D^Q3w>H@jAP9W_(U9{P-semIob&0=+0+$%{XM4H7+uZ&F&3N<(vfK8*kqH+eKm=c;8NxtLQ{+!q zuuS;!@_>0hp%UW>6Fx?vF%hANH&wM3-*7hz5_yJ58dB=g3`B|=(V^Hy93}WLeyIyG zUyzggyl*Uc!-ARF?~S_P58i)0wkd5~V-8Iu&ws@G$FF=#t&uZ4B$gIFaoM1b;%9T@ z&iHjN$)R;YXBHxylAtvm(4k)7^FR>CCa$d@PI328c2CvUDBb#DTZCF4G;tL43b*$hn^xP2P;3l3h&P{pkF@OGX?vmFr#q{7 zr#I7~?td);>-BBe%YNQ=eFP6W^xl(Bue>6^;7)9t_D(vd@p`mN(RROoD3MA*nj5TX zu$1JG5+tG2>s!~RZFsc%R@(q)K$yP{4NaFW*06ef&cK<4vU= zqCL0foPLSqfcrapyDs<7)h%Jd_F)F*P}l8pJyO;z{9qJ{BF3hqX!ykQ&$ctx_CM$o zzLA%ygm)L>CU+p)4bClwOs{Rd?UxkL5MKJ*fX_#!{i5HC*W~W~+dazqlaS{XGu(@k z1F}}bo7VuyG&?nUU6H2~s`?a|)E^qx+Hi3tDmY01s%VQmAVCH|YFId-l_(gkVovVA zx+dSwy04D$@mkPWLJ~=MZcdf<;@z*zYED@9xRiuS0%Z%Ix8QOjkSb1-ATI@PXZP%$ zk{9*5DDt^Sel+)JB@P;@LPZvphTc|8rxVt?dPX=5)d*CcA#{Rj0ofTM$&-xLr-Q8#Ofu%w zpw+=YOxfk_^97IzjRk@gb-jVq3#i%{j3=zkcX|(C)b)yobi$Ps50@{Y&mY0*0djha zObJu~$rs@I4eafAu&j@mIU#-f3m+~#d>8!TXW)m6Z|T8+E}-O!focS81z8BV8EgW| zb@8~gR>o6KTScP`UMtGPU@bnK6#|utVg_m}I8UfcK@)&;iV3-*a`ITR=5Rs_KYxaz zv@NHULnch;gmN=va`i)>5DFO}86ZOv0<|07%`&F{#R!E2KmGBKP*s@)PWOgJ?#2nD8)Fx3CCYi9m+6DpF3cR>Il zikmgGpoLQa^C*C5jR+|e0<2!Fw%FDzszd4~THOy^8?Nh*@NfLPe*=H*Z~S}oYrpkp zUOc<6?;r8s{TKfg{^o!DAJ{ehNwm|11%A&J7(|`Qo?%!b6Ta?TRT&o2VGL**%%Nb0 zEgwCRA7V_v!n+|v$m6MJd=l_J4%(;g_1|sRI(&zArDJbV>K<%vVU6#q z4`K8GYK|e!$RpQ7R#mmNhQXT9x$jOs0Inlo+QZ`L(CntYRgzP51ggRK;wUH9sr6C;IE!lSmt2TttUMAI#@&ACN1u4i{Q^~fw6+=hN% ztMR5_*tD`^z9F>H!(E_2BQmMS#@Qz<m`MAb!lK=2X(hPDP`Lc96P3CFn}&mrN2nSpC~&8xPMwr@o8 zJ(g?#{TmSw`P`)47i~wfx9*_$d;0b6S&q04l(vSwZ-&9HOi3ax*_Sk!Qk~I3FeJE* zw1pIYM5}fF!tRHTpCjppliQ(muNttSKi}fF{E6#aiT#PTt;{*T_tY0nw1tIv!bG>a z^g)de5Bk8s)wI~XS)$W>P>@;OBM@ug|M}-}e8g`%Ep&z#0 zILTgG8|W~c;^0r%0TTD3huwMo(KFol8RoAx^*gl>9Y*vA1&zz!X?MGBgTCVf?=$EM z`-~{zwJh`3r0<{HuG=wQP3h9H#6N!_tX5%Y;4~q>{Q~9jgHKS)3atfoU4ho13e*<+JpoQ{ zzJm($&Hk@^AM5oIFvB!mTtNXsyFDVw*=4Wm6+BcGOPK&x&`FauaU9xsDXT0=F0-EJ_gNbTytq9hJZBSu{fO??_I z227KHX9kIb{!UgzgHgE$m7vLjlC?AXlx zSha5flu>F$WgjXi2zkoEesBp~TMH-&C}jvKGA9(u;I?#Mj~D)HMQP4?0D$+wVyLk# zBU3z^I>;+9F}&;PgIqks9|`8Vm^ul~xb!_|D6 z@mv4GKaYR)Km3pA|M*+~BmTjUejlfN!qTOTaX8kyD3%OE4tnVu^1SlYH|jm}nnH3) zE{E=ceio8!B}Ro8fsMMC&c^l{yRCU6&pmJPd;5=m7BxDOZ*@nThXIW-w&#^L$dK$d ze|#s0Ydga1<_q`24Lj&u3Xg2=3y<*N?e^{fOYc5&kRj#0ePYvnU;kNLyL)Q;=Yqsu`=_TZ zq_FK|CR|d&Nn&eA8=SBY^E&mOh$2th4mGHOj*z$=v3$R6;~o7;c0O$Cxn$81@-CL_ z-up?ky*`Im*g`JO!%lDnOnuTj`iUO?aNq7Wj|Vw=_wWusv4nlX7djjVGNFUwHmWs3 z<~!houtSuTg>{38ArWDF$xdgFa3wzn&Fdc>b+tcL2jK9<6Uw%v3Zesf+76WRm+v9- z-c!FZ29Bu|90@fJo_xp|r{pOSsjG0fGuK=ZW`oSG;Wc+@f8G7Nx&zT^Fd>pSpt4&| z?6Aq!jUIkzczHJ{-oC#q1d)uy3>lD5?6?>>04qB^0QYoRu?bMweGSLeL7dnWtD{)svHm_hH)BA$O6KR z5^k&CXa>Ux9!uuYY`*aZb4;HNtr|F|(cA)pSqN4dWMVKF%zn`e{9u`w@b;WAGvQ%Q zk=-dk5@-W1lmDKctL*hUAy15Qttc9#$eF-7PTFSBlTz<)n6;7D%Lx$Ec z{H-u=!YC990MMESS=cvk6w{uJ_?1rS`DrE_GG?H z9;G7Q@Fi(TiP6jl0D={(Rm^EdQ9#69M5gd9GH*Eut(gmkwV^eS%rGtB+Y^)+CJK@4 zyB5BH<%AP^&bw*B_2~mdGRkrV>IT)*wSblzj3@U)3gP+bE1<0IvxedQH$TGV;k%&b zg(l7qkZ*klwBG!ovfe;Yr1QmxB9d^1xIaCHEwwmY=_Cn@HDpN+^iu;XpvustKubfb z3mO=$HCQP~SzNj);uGQ94fga1K0ko#6(kOfs})#^!fp+$9_7ZfZ!&VoOpR0It*eI$ z>K0F<&4+AV?v(_X@{COC(lcXd&Q7`Jgas2$s92F<5dR#;!DU(Q?Whlv{Pl z-+BY7d*4~S+{>gm8KW_Q6CyaDjt>re1s|MW-n-GA~={ZhiS>2k)u^6&p!#D(xb{}2Bya()XY!i6j{ zy}j`-ac~J4E^$Jiqqvws9p5z$%Qoo!zFnyC|0@VffbCku^eJA(h_&6;Tz5bA5lTAX zaX127utk;i&3TvFcVD(rmz^F#QToapU870u6a5C4^{3$*$H-F79Zo$!Z*<58??qwR z#Ts>QChdH%y>ZcKu=$S4evhK+a$0?*qG!;a3v*mM?Ipee-a5j3d|Q)YxJbf7p7E<+ z{3WFGn#^)BdwvdX--;)8ML)nl(b_}I`WxA4zDe;lNIZFLyh|C z1>9*oC+U^{wv#OH*Il1cUfwt70*Xv?kEEJXLS`A;?mbAcdrd{sDC%uz^Bm1FwiJ#Y zSto}EcISV!V|~g2AEXy;#n!YQQk3?7?nnmmrd`IGkZ8vYc&!S5&8EIzvh!|8aL9bV zN8KHt3~bI;a9aj%e|8r)#l)K=H=@QsKxR2?NI8hvnUfRUSun*jRYMA2wO^ZIBSJ{L zZEMK;*D(NI%$qEk@FFL!4-SZYnX@U7x6iJV&4oRpSwh_aL$huCes`}5tfyx!4I1mX5vw(Wnr}1pyIJg8W1%# zfJGv{h@9X~-uTw)6W~UZ%R$vQvZNG^duvG32}>zhT0^M{vx4V8e!|cG=*M{f ze8aLlZ4e9vO3-GB3EHk$tKyW{C4FWt11G^^z`=LL(ge{Nu`)wf+q{9s90KoO%Q9I|mVoY>h0=RAYB z0W^V0@MIY?3knD%3{BY!o{&h7SdCD7m}{h6s>n{{7ibJ;+z*PB;rWFg0S9u0%Fq@^ zrl~Q3*}{9~(=%=Huww!z@_c3yoK7dy=DFz#_{KajDreXR+LM_AmX5^xyxN{}~$Ipozzhouac#9RLKEF%!A>zL9V8_fWVN zwD)SjYJ|mkjw3_7XQ^ut(v`ODI_)Iud;go>)t~#OzWd2X)ZATD-*eGHw#yXXHnbaz5capU|R+H}(wk2rcjkwb+U_ ztv<*l2XtisOKToI7|ptYK-z}n*ayS5t1%Qz-jp?MU=fsBFi8T;c?_#69-lwr+8UO+ z;^!Z}#&vm$CY8cZ-=cx=t4nttOcBEW`TKt#?;k&))OE-ez1h&wq$QrCS!2}jg&Sfs z-d1$+=!sJ_*={c@bwz8{f7TkYQ&B{#4gg+lHDtwokYHPiPA3FIL#pYGcxlc3`*DLb zt>g8&`?iZfeM{eEMEgg9)_ z(+>6n1v?BJ2G!aulzTLY_{?61_RYHx$57d=NQSL}cD0#C8j?qkk#*LF7i+Fi>F>KY zqonQh+7ZG zHE%Dh4Hl^&Uf$Z^h0xAXqS)lzb%4-zDuw+pG#j)7r=f! z3;X9JoJOqT5Sma#sM>bl(O%$%EvKGiyRCaLxoz=!)FU44c;4ClbcD8zKGE*!w>RbQ zhJE8aA;Tdrj$T00e@SonFMzxqYM(nB%po{}fY)Y#hS%2iyR8Jh7}C&x@-O|{TAJZG zXuM_?QzMtBcQ{UphXiSZM7#;i>F>+x5Hy$YXgoj|Xug%_n4q!2CEX}!m@qNn!roXB z3&1Yz76zIzIC&wtDx)F!_KWi z8+irFGjzSdnt_%Dk_Dwy(0X$&01+r>NY0pK##4C&$rW#sb% zaQg^;`z0`4{Io9bVJU-5U3O0F8J)R#wAoGm(T-~o6(`{;mdaJ-%Q7U=X z=(3`ip)|%hD>4jJZ%#jN3T@8fCiNU|nFaaHJ6x;ci~o8s2VMv-RuGDi zBFKXkK|mN3ArUF!pFs);$%<{9w)_4$=j^rCoK-aj4`Wo#nrrXVorrr{SNq;R=j^@K zTys{{7~}hX-?HF~FTO-{;`$PR51SxtPuRqsM-fG-2II=reT^~LGYj$DP%Dlif@PG) z1*P2ZIxg7i6;T-yZG&eeNL8P%TA@p7v2PFs)nd(rYEphhiyeuUpa2EeYFW}8Y`PoB zZ7piWc$*?sG02cuZsuwPFnc*DBaZ z5tZ;-1aDa|J^cp$E7iYee9QRjcYo`*@#lW}Pw4;s^y`=(UO99pet}T{03ZNKL_t)A zcXBicGj`Z!?ebg)x@jw9$(pH7MsJhm7TcoFxW#msnCqRxO$YzDO@@w=Zax&c=0T4> zAu%sM4YES&wBdL+2^<;7$0-DLB7Hj&2Ph^5X0mJ4=;m!wK9l|V)qmC~rEi|w+Yr+$ zxSMc5caWX9M86mK$Fo~Kd>yW@-WmBkl1A`yLxP-V09KE#F>mE5EtuyAEYlhDG$X}` zl;&Q{tYWlsW>dNqZ;l2!K#D;O%p9@RVh3O^s&{t1!Ii)fs?GuNX4ObuuTpLAmfDN4 zD#qSu-t=5Epq`gL=wt=E%rkN5UeOc-LYg5CtQC(^vON<>oXpeg4Vvthp$4QSSN7SY zzQK!jighllDb}~v#) ze?4kQaqk}QwnTk_SJWov?LHN0Kax7Y>BXLqqJuA$7!SdU&&bjKuEX4PAaRdV{dRt4 z8gcR3V;I}qyAGdu^x}#-AkI~oezsJ`0bJI!D90%<56>suHsJTtkY3S#pPDl31=8Do zZFh;S>Ugpr%b>9>^1=z82G2e2CVwu`r;$+YlXHQoCVLY~T7$#=z1ZdOD%I;0#>4?j z2#Bm$I3S96)75GEVsPiG7Bib&xzg@y=^ZyKdLB~hv4FXhUE<9F?BPnqUq3qwpnBn7 zy)ha{QK}VgQ;3)XW9kK@H3^GoZ@5&y=X<_;m#h24Ty!sVRs6pP+mY^jM2KdoNyMgj z6f?y%>x&K<=PmcV?Q=R%_=A5|yH9#oPK;h`>QeRHo-;mfN;~MY-_HY_4`ck{55YD* zw=?TOPEL0r3_mmqos4lqOo$=%CTo>~J?%^nX`x*PFN50AJ9lGKqRH4tt%82!9rdV4 zy3?}U%gtY?s6~(Q6AD*j&~v5v0EX$`QTm5T&v?ScL%K)$cmX3iBKp&v?40^4qhs@$rlo2mrjT%!W1A?AydGn)=3Ez5nvBfXaz4T zK*0S-q zkzK+igJ){>EQeZ6Kcen~y_90~NVpdB^Lu=`buFu6o)}+V3#P~@ zfsnHzgbbnx(F{UF)B@yy-@RTTO4y2d>b9+7ifyt|Kr`WK+b~DQRx-Bhh8PtqOll;X zpe85iD#H@&6ge;+n31>a@is;hy@_Iu$=CqyQ8fRaHE?RlVDh*1Jjb&0eBx?$6bH++J~hBGaAs)A5V9&z0YmUPAn zLCP!Svn%uzLAUpyoPozj;NcahDj*wBHXuY4xuH@;(gh(t;_)Hi-Q^u#pC;V$hMFss z0}xG0ep3KcL0LglLd-Tb$yuTG237-H0okJGBm*2kF@mIGeRsj*lJLd#f)XZ7RB@Gn zx@902e9$uj)TH%LL>D=SV8_zn+fe8oPxqojk?4d?6%TR7O)^q4^+gg!yUfK8KM@rq zmP#NX%&f>1!D0baM%p50;Lsiu6UbmroJ^wK*2*dnA!``4BaquBsM?{wXf#g|_0SB= zvo!$%0|A2TWkZMo53d%K>V;FOctpUpUUz@B+TkHWQH$XAVZ-8tS(_vX3ScP`toU3q zZsKxQHUD)cgF0^R)n%8P23SKSL-4i|zWMfN@c;MM*T4BG{?m7Vlm7jG>Cfw5{!jh` zJf437g)CM~JTj!VX)dJ8?JX1h*rl$GO78w|y#t?(Tc1EWk+*?tq{{>4a3~sQc5OD; z>9oY%A5KG7Y0Kzsk!It@TnG8(9;8Nx#(}C1Q+qYV3KW?PYPX0L1&Il(?y#qng zHxMw$?%SC>zrb8BgJi!=w8BS@fs+@>YHJgzLrqT}| znhe$NFR93q`=MJEC}Oc~%}ZXLqMnJoH^hj!2LbI^K^U84IeYTaeUj3Hg1&#(vT;d4c2V`Tuq6eH#k-E(Tipjl8% zC8^W^Ydhtc?@}9zR+qMGSDZ8*te4o{5H#*emqok%fMDye`h(HdohHLp?Dr}4NWwO8 zrp}a*k7(8DU`RP0{P+CfL9*MExA(u-r056RC~?=|v__4|w0SSk34NvOaf}WMcCbS~ z%=ZbX?yL%hKTKfrL36+qLXQCqO8)HndvU0vbXXe=S>+5SY4dX^6H*9BOgJ&2h$%n- zK|?ZewFR@%Zp|n|< zcxy^Av#o_*$B1f=@Z!Ut*WS#jW2xJ?1ueuL?6SbUQKWTj3ysEQ4HpnB2EWKfQ|+JS370 zFj>`?4e?`>mJWFb!!tWPw|xrV?gRe`Jp4?i=Lr|=lWuZ(pMH#{e$RRvYGodh*}x8> zYtw#*(Xu{|Q9oJ`?(7I3V_m#Jd5^909@QRTC$h-3Q<{(+ zkOmNLB1qm{OB!&B(WegzVy8Dd;*HqjeVXJ5gee-4RfUk%CTQ8?m~0U=@9l}y2;bL= zw-e#*+W_4JF%dK{E`=~LkgFQ}sy!@hpsV0yS0*4#YzK8BNETa^VuSi9h@!#is6uJ7 z*aa{W5fUeS@3wj{k#`7v(O=Gy028kh|@-9Ll(Pr=VbnS zAqGjsMHJ;`H2Mq$dHhYS3?QSB#pIk+@O~{gFP3Mk0M@+XloFoSj432k$yj_kvaK0h z4dkb4a;EDgBP~`C#uSZG%>)(35(!UYMEjTv&Qh?!ygM~mqozr-*gZw1W^jlu^+}*| z0VIP6xH`3Z3Mb_26F2~p2y|2Mat3%YkGY6Zr#1nqUFQ%^DBA`hfz*ohct(|iYq=m0 zgC!uvfPDQ7H6&oY;mg|<=j9c2J_8zorwc#<-y%Xa>@3UhVQ8jBL<4o`8o3 z)Lg-ji*n-={6QG>>I}*ozy*Yix(X^y2x?Mr)QGqc_-5Wwaso$C!AfME zXclBOA8L#Vw{-=Bv8gf8BLVLVBL@O+YAh~G6oiL{%YO|-C~Gw~MyW^)#$-1dvn#{&S zu&h*6TaOA9Pr2Y$tNF`aYG<4Y!IMi_1&jnnC73ey z!9V(k@$dYpe^Ot~zXwya4I`7qgPGOH!Dec{={jr@W$Zc+Q&gu=IG_8NA10$@+t2!} zR)W{zh@zcf?pv~dtJx3bp-;HIC?@iVw;{Te+Vp`AGlX#peP||-c0alcj}2f4ASmI9 zfJ!z+Pd>ezRPkB`*$a&75p&Wq+8fP%ptm;({BXQ+KlZ+_^4)Pt>8QvXi|y8dKc1g< zqV>U2ONXYvS&(4z(IqWTB@fPcv9e%u&}Afs)B;kr$yr^y=dl8KoKMIl15k@T>u^#9 zq?-LfwWm51Deif-LIPq}%7AeJ;!%&xj9N|3T|vnCYSEj+ud0?mP^vdlwR&-0yN3`J zV2aoz_w03(k6V*LD?!PuLTx0X7XQ(w$G4mzx|t%0f+d5r>Az@y4q8FPO*!G7Ujw@x z3FpCKULxB4&1l|KpJ0bJ!E{1(FTzC}lBzxDT<<0&Bh!KIrsw0xy7_>+0|EP#@tBYF z{Ny*D6_p5Y<^|^z4OXfnWx9dEXpq=ib3L@CkrU&C8uv#UkgIv5$<$ZTt$FQL6euR; ze!y-8IPQ`jv`@nnH2jZ8&nk{1sy%Ump2Me}r#|1cG-xlzF;2r<);;&>=MjJweRbHw z6&q%UI5-@Qy8Qy$h~F0V2oJNkOq zC#R~tobMA~SA^^TIvJBLikZfG%8K(i2*rKJ_ z{Vl;J?*-A2DI&rOeX_h5#7LN@xckYnEuc&$BcA5~o)m?WwG=2Ii&0BUu?a&KmxL0qIkf0f z#60grC;~jr$)=mZreEhN;<}kP9b|<*T*5vxuvT=rUxbgEY~T=4%MDUjfM=*rMS_auLgw2b;kV9xQ2}BC%?nVkpy~&) zdIM;J<^n2qAKHUO5S>xCFF|Vt)~iiy$fjR)TM?&(6+ny&wtT~^6>F|IhcoK+hQ&Wm zo*uxxwhVMOUw#llEE)N_0;Qmq%_8r% zjFL03-LO`G^NL&qS0PLQN+KvXKwP!vqx-Mh(p*LGqy<@R@^~eqwy&yqQs4sdDXhhZ z8G$|^sG5%xf_K|z_ya%t^Yjxx{Zn5_q5gaS!0*Q&`)B`o`fq>fmvPA(;v6km1G}d@ z>+S;+-w;bXKoRz=;JfL$I($ZzhfLh(@j%jx)*(Z@HBg8U-GBEO%x5~ew0v@f0X0Zl zWB2%LYF>38)bvAoB@AwQJ;2m-_;cd&b+~6-X+JXBbetiixB$3Hxj{dnqc@L4okm$akxqSk_I-VXKyYsSXzNAJXP2VL1z+&tGN z@h)DBW{6nfUbUGT1l6Mq&B7v{YdrwhL^}ha9n?fhmw<~_1Q3dr-b7JUNXf`jk;{rw zR@9p9@J!wqhG3J@VMVohLq&VWyFMc!4b!Lr1jfYf&)yTknHVv|?lEQ_{MS-@I*Yy_ zS^0qyNZe(cWAG#cP&P@s(htK9?`6ak2+7~Wn`uG}4gzIUFNrt&EbG}1(F3FqgU`A3 zDcBn+eG=_(PbP-A#X^T@r{IKjvlZBxF8cQ)vfOoTtO#!E9ooN+tvni#(!TcLAOlt$ zk#y8illwf92XXrSmE2))JVcrH$zuC^M774Wa4eT_9ou9ka8M zswV}&N%`0Hg7bXB#Fo8n*N6M0n3FrFaSn2Wy5mqIwmi~iT#)wPp5KUK^4Hl_(ZJMg2DMt2Dn z7_3k_d_8vMU!Vah9h$H-u;#))@{@n?yUc8pIx%l*^0*yVK_vF&%O;Q&z&s@@5Cxc8 z7o<(snmnD2HXg_peG1WKN(v#{6pZ|CWZ|nhc(H=8!2ngp{jd#U3MM6EI0z1c^AeF} z^QvD-iRUkx@k+oaWay0jiWEF#}347F=8oVhs zkefK<1RirIia9ZIwaz0)!nzeZ{8lk}xd=+N2`JR$#RxWq1laQ~70dy}8x%!6$JO&% zi<&H-yl|%w5L3eWoNP)q&q(ux5EEDpP(o@&YfxaLh=__@1qmht4?gu+{6SEk0ueJb znDpH;PJQ!N1-1)x+njFCVA<@@SR6k0{$gOm=o`MvGPA;pyAY7qD~dz}Ua*AMn9i?I z$_?@S8Z4S3sPAFoCxBs8lSLO|mH(0KB`sv#1y$Tx&(~;Z7C8nG&w~ zW zIKh|!N(NDk$vXZqL}aOmq^Kbx$_5FkgCb9Xp=@>ra%dZTiUne9!kEZB>4B_-VQ`=Br zqFy^)e)n@%-Q(13>LGi!=_uj7qekB!cGRQX41VXT0fLe1$%^yrv4nY;qF0yYsc){; z(@Dn3oZw}7-@RzA5q@z8_BxaTq&swKC@Z2jU}m=p1Y*QA;WRCnrxT`WamgWJj;VVf zwN^w2LDo{dC^kxXQSru~7}(4G;Yx&c+xmKgP8auRFwr3f)XDg@9OTHQu6E#dBDx*i zs{;YG$7`Yim73&I)f#%b6SAS?MZbUTKtZv0|&c}DxSU$SWjlOf-$Gy4b zuT8|sv)cE#IiIc%O$2R-%{JLSWFhaeeP+UvCQLD4Vs=%6+0{4(tm}qvfBRebo!|O@ z@sLas%C1_dy2D$W+N61Ot4IBsV$_s#bFfCg*~}`cn59LWmaej=1ymYT3Az_CO-Ye}4cA8raaE!8_>Ocm~ixZca|yCmP^$cQJhTK50Ex)(0!~9vMf+Z~%Xw^b$9% zt=aNii8i|1?_=OT23a4AalTs}8F!I$(4qPEF}uJ=8YsPyjzde3kEVyawdM$%V>&ik z!gqf1+uvOx;T#PV5=!xy7n|$~Y=U0JI;(Sxh#+LLtk?*JFnLGW7P11%(5w&?Vy89} zG6UdfL_=m=2?&&I3I#w@>Twtfo5}=cFbg6Pv;gOY5mm4x#(5^hWOTUk|b_^eI}=@x_*X z(A+#agee4Evw>tROOX)}?B{#YZC-Sns*_lDdYnymIQ#Tk*@)<0J_#Pq(c&}2$mc9ipcH_MRfvs_Xe?v-&zK?ln&)E$)kw} zp$e_p4vyOj%@^}*OE$Ut`}fdDP|67B1soHq=k*c@S_PAuU181klb7YuK2ttJi!V+> zAVf$4h$3(m;LQU-jLYZ$52ooAXgLG7CqOgumT_BG%hywKO1gs94VefJ%D5GU<_r{< zJd#Uh#jKvH6`3N|3KTZQ#>VzqO~GjgX*EN&QJbsB4dem}=D`=S>2vYVC{7Vs3BUx7 z6Q57<;OMLRUFw~D%LkgcK?MRr+TRY6uUa=cc2=^Tv$#TCF!6>BTx z8{wR&$It~Ly!9hRCE%g~|M|c7AL5VybN>{*`Q&R~Ns%66z#sinzeo>X{|Wl5|K(pq z3UB(Pd25;FG=kW6nb!D^96aQ3DY*9h`A8PW6=9#p}% zkNcNVsHXSGfYAwP6LP(aGuW~PKYsJ3bR?k zRIt`;2m0Ek)_&-A;2F>|p~+?DiV&jjSw=H2B0abF;PLFF>)`hjd9-Rf2$>|J1z)cDw2Vc`|FKS+K1rS1Gl%vz316sB?AEcz( z^XYwf;1ph_&_7!yCr7(wgIwbNeEJl699x@M>*(Po9vXFYnDEInnA@>&M_v2Hhne0k z!#x)Ehqf762i>&xrQ%>u2Cn z;2mPY{NR$;Qa}_?pjeAd(z8aB!gKIgoQkU|9w~Hhml7lSBF_|dkC)SxR}_y^BgD!m zLApP3_79|b7N-cKih5j=fZ6n1ff*?o?VVt-x5@#cjJIC{av@x*g4c>4J9$@31Qf04 zuVmXysN$1b?~FOYnI~YL#K1?v#z4$KCcnsyt`mYgvclJWtI#f;LB@9a=K+L z6)7ri!U(H?z81k3fldUj8PoX@Qi>tTC}3g5Ouz?8h*UAFKs5qk?njJ?30qk)ad0_g zMPUNhYRCPCvyTKV*%r@y16G18GlFbDRcs$N%%41fY6hnnB}{lVKY;QI)oe6loN&t- zy58`ZPBujsLCG26bbp03ZNKL_t)t>f^&|h1psmuOdP& z;L}^kyYC@y3M~alC&=~=TnefNr0D_c^$G8FvP^M@jh%>bsl_9V1Y1!=ikO0Z?nQx& z01_jIPaLtpqC@PnEDkRW_P|dkD1yn`m?2dGM1%-DtqN9xCc+sJ-~4^Q55M)l{jYeh zz$*ZiDp)qucKA;Z`26x7r|FCZ#Z)B@j+rz&Sc@&*^mDJO zP1!4E04LZKA(8oWmnGsUGXBWl|9kZ>{FPs!U--xW1iq?Y|IEMoXYe!M`9t)JKl3f! z=ryL(>_x8m(scxb_Gnb~gZFW|)Od?+f?J6Y)YM7|R*(m^MjQKv-_S@x`()tRgnp2@ z6?f|u&`^|*g~mP|)nT&S>yl@4#R1myGAi}%&+Z;Dy`Q7HYCzcDOLaKX)@kpi7>K%0 z;-g3Fy|jJvot2OLAU~$CDBUB0_CmZ7olP$7#AFY1;*fA&&UpLy7Pq`%D-|hNB+^z4 zv>6DHQrwqk?tk`sk~n~2-snhbs;Xv*Qd3#6D|Re9+CA4qo;+eOujD)h&hW?U{h8(oS9W{R7$a=tVn$doDPUk;H4sJ19);T9jHqrA-dSZ4*YYHo&Ui z4l?**b@cPDjaA2NR>BY3@QEZKFCGopM z>1DDp@GM#3kO$D|&$LH1>GLvQhLa={>(KBCbAhyCYz?sYBZ zMGl^pA!N!(h9z*on>-xUaj-4H$L{5-I5d8uby`eX@1Jo4g)uqo(Um0885@m~?jAA{+5I6fba%Xbq=2c7pP*lrm1_1{2i+^s)tEeXUZ{qxsDo;bNmZa>tk zNB#0aGRxi!sh&)bOU2vA1tbPlBi+5?4W!mA9PRH%S7)}#J~_CtiXeNcKm~!k*+xwr z*1YK4rpw)#-ob5m_FlUeuu}`ADc=GticQ4G?_QLP)4rugC-Oekh7O=ca@f4r6AqXp zW=y_5iPY|A?t6wawW?}gUz=^PKZAFE<65Af2Y?ACH*&QH2hYqFp+FRR8U{d3uOt#S8cBXqkJ~A${4n_JY)jUZA`_ z=kC*B{~=G%;e#sBZK6a5O!xCp_+vl)t?w?8Fa;n5LW+dN7mEb3xDW_VkpZb4fDyW}o9kT-fT*@7rw9fIYHaRvgL=?faFhcSNnoVxA zAy}q$^$g*v$Wo9?#8#^T@M1vrg{P>!-bmIRm1@(3AaF^qeSqsGm=~j*hnV_wy82-z z23SV6*xel2y!e*_%oBGCCXsl?wb*Mbgp_Qtl?u$u1cu_n`zt6hq$pm$e!%6)yvvWL z2?)tEC7o(5ic<=RF&axifoYmBEi*p(+JeLiii#M4X|{N`;LXLvCikr*)<{uxkGxBm zB!dGXARw_~%jT6dpK-K8&WXG&+g91`LCpYkK#RWuK`?so`wKKC@Oc5HU@sso9??}G z6hLtXbAsS(u(O&v*|YnthpcnCVgK{pr;EsPB@)^1ngiWz&ws= zK0|KrP;#+C8w9NxYXK%@thFL#!iUR~$x2H>RmKT~O#~;G`vhWaWHEoLgwt%&{3#G_ z)kv+Cg2#z9&tQchVhs^bl90vj)AB7#reyvAh}y!)Ns2J42R3awV$l27B` zZ5hH=pqU24Vv3}0uvVRUP9wR{TLi2dfC#ZLEkrKhU?gZZCk58<;PUi54d)UJX>)(!5U$OzAEDvtRy?FzJkc z@Q-|lzN#Yq>!1D@{)K1Wp%0T$YMoap_B)*vi>Cjjx*gb*;M$fOlfvQKxITG@PPWe8$;AbMm!Lv#jrk&%N ziF@h-`6T^uS#UbNLQ0c8H%t&IC{-Y8fKpQJ66NL(RR_AYsg=SS-c4p$YsKb;FvO;M z)xw<)Q>HH1)#85f>hWntjL%Kkq)nwDee&8Q-c9b^z(mBs&!=pMY=_pt;aTn4!1*ZJ za>F}ttxd$5JhVmp;4pC=0en1sR?!djy%EFYF|P*UgHOqItb>QlVQ4jFlh|v*5tDS# zZGH^(q(8tev40!&Ta@v)Wllyat8>kGf0C51H7jEskw_>qLYoq)1J6LXLNHojKo3!BSF@B&S6=Hx% z(Qs{^kJZMjwztfV=(*wFi9A=^ql1}26kKT$x}{-Ci@DkLINQ`-&ccGiDn#9L z*<&(`U<$#K0EYLs6aHKLZ_AJ0C#TH87zhI|K|z>1h>Re$7{t^k=GoOA;?Zwi`aR}S zxi?ruJ>JfSJl%pg)F3iMMUgnVjqEv^L+B8%>eK5cCEq_o?GpF?UYnZI@6X`xGlhWD zWD(5`2-liW7}gu9)Hrdao*$=vwfWKHuk_cc^~)85l_+W-s)*NwDW}MSwnk=Qg*p~lckTt?;-l`-XeEY z3caisqJdvrUmC-x0UO;#^rQqT4<-+ zzA#t!3X1y)1@r71uX}*jTfu3WAVE=X8)BGDf;17fRREgIA5V;Rt4J~W#?1&JAu1sT z#VI9BQ^4z20jd=#2&PyNLO`^R3=#>z0_MpIwN99{_Zpc2o)GE{6dw?#1)4v&M9@eE zs1^~_Rus|-k_xE>B82kU??AGmzW+Yx^&_w(&@_QLLN#MyhJ=KeP9UC)(vBIWDyAvn zx^76P*A@#`8AY-s3D0FVgc6ifoDinSIipv0c13Pw=ieKrbeatp^C?DM@AgUclrB+eE)LH4tn zY>`5An1&+K<{^*_p&1nc&;k;1<%*T_RTZoms*E57nNpu7sDov=TC+(?vADaE5o&=* z#rrQZg80;j$USrljuFbHz*$R$=;Z|N`n)xU(t^xB|it(f$agS6?a?@Zzc=Rn>J?Y>T634}*tJhFq{phya=eFDA% z(>4V*SV3M1uKX|sNWuP3lb1`+6(0!HbU!8h7&zx%uQp~8(Z~2}cXP=XigOt|@B@HK zcetJq4H?UOgTH~SxPudI($~oKV>uwbSQ+>7{r@{a;`}8=_W?h~fXCAtoTdffWQWaa z6EI?6-7E!Ts~eaDYAxMQtX{Me$mQnhg_zhRtrm~Kw1zBb@E`XmDVw~tV!cZCLcKz? zxEH*3&v~OLH|S!MuZk@gHZk<%gYIzD{U}326G>HVGA-5FCc1+SfP>d{B;f0CSZ^1U zhr=BoyqP;&C5Gtxv3BUH!M~(i%StJbnhgx<5k=foAN|IoL$ZOsFrd*pJ;<>R(1ZF@ zag@V;yfWWq*F>-gwGgqS3FkQDR&p<J1Ig=q8(L zW)XwddUFG0kikZ;=00h)+oV}Z*r8t*(;J$Rs^1G&k-_^-eeGy}ieHejKNplXI2?5f zxXC?c@XWO*}@iv{Zp!57Lm>vB$z4<<)-0dQP}@ zCW(xYEOPLdNA?8=(v4uyJsE&@ZbqZjx8{g90l2AG$i3zfBHQ>s%zZh z*DNMo<-x;!|7?w*!Rvi6e&2!3?&s#-;H0~TA>T>Fo2sDE)yD#scB=}JK6~5uC957* zabI=r8xU@-x~pM=1g_+r>)<#nz0`vyO^hd0_`FHQvEh?OFwGCtH6rD6a!W&&l!mVvbx z#OA~Sw`xAPIaG*RUabo7FcEIGVu4}^~JSUq-n>hjd!#jH&~PqR@_BPrIRU=~oY2)yfiBccE9=U?I*-*}5!1lMZ@34?e+ zh=eIcL~le`PTYnxMO?EH;6Hf)QcBj`KtVd6z;(|^CJ3YwM6cdGFP)|+FaTa(#LAZd zCd80%lMJq#D=CerPd>F3@`D-$CIbEOdz#MYhgy@GYYF6X4451C9JS$ zxJ_5Q{gF@c;d{S{%k5_VcZzrfyLUfW>`!_F;tH%!xK_afX52o!!*Y6ro?qee^nEOE zzX9>XDyIn)lPNz+0H+C4WxSU!ku+ng4AqQ@1$E2#!W&{HsTR!9AaOzo*j7{~EP-(= z=EJ;*$x7=^C>s@O_}RKl4Gat)x{=Du%+k`>ZmvZ=p{$l>)ODItCr1?rp0s*^PtLX(Ix?`aSq> ze&tv3o$2@MU;d5%o<9BizVVew-v9i+_2=;IpZ^y9{2%@~eVe|CWr}@LA%h(5L_mba z&XG4}2#U;hbUpdOYsT)i7x!joe>WK>ajt6DyG_%3{;7_W*PSM;gCYKYu60Z`#WM=? zvs01B#_W+O`hr)G?&g;CvY(7)->?s!_9lrxP8F&X*~Ncugryz8Binkxr`qG@)Yt0P zoCq%EtM^5P=Tk>Nq+}yUfH~o`yu!)=Nd<+7t*i$B(u5cdC>7ZhP|Ac{HnWoWzR)D7 zqJZQLHP}SZ$n3U;^oFia)2b+Vfb`X}8I8SU6wNMo*RJlO?z2UY5sL0z2hH0ah`E4x%;JCVGPthLDkkM1u#>@#EeY@TP>E@5Q0T__WW~#YQYp!mp4-g zX0VWo)TQ#a{0dn-WhCd%crjO97CtkO#bn~u*Cg@l!K7}nXhrOhmE4+Pjx|;qW`TM< z`0rV`H27-S6{0$D3tUOifW^|32@D9KqS$k!23Xbsm<+cTkQ#>wP*BSo`~K(%*IuHigHW66WAcEW1#y z2oR5K#-5$-HYK#?#9g92Gc2*hC->~$Z_8$H_9O+db{hT*gWW?s-}5oK9wxo|d~Uo? z`&*pXK#l9g_+SZPy_<#)8DZT{#nzd3chW~B-7giH^32<=cak0+o-+>xIlSK^E5qT; zqh04-B0oH1?Y?O;dOl>RyDf^W9@te5ba;I6QqQu3Mn=sNA86@=L#FTf$DqcKP0f$@ z)yolTE!OWi>(}xAQ43cW8WUR@C>4kxMadOf?7@%0rqU+>5-jrKrskCXAOI)DL#%jT ztmxf5?z)v1+!cEV54eRtR%YEOtuAo0RZn-4L~cTQ`atAYb6SvC(^P7cgcp2dHkKzyK}Cbpl-iKoQ%zLP!uW@@&S<|PW*M~(+%`EW81#K5*`reH>m3ck4drR3s8Yq z^Bb(OK%buQZhHc$b%u)sTX=;`fg_>g45z6Aql84;FIZuT2>@5-b|0U$i;l^WyO^;per8OnnhAFQaV9$ zaT|k22vGoOL5PeZ1eFNE|Q|m(j zSaIDJr=OC%&nF{lEL` z^mG64&wXV@`pYHH*j90M3oh517fo5_~ zUUc}I3?RJhgxw5aRg?QGV2Xu-xGp0dcHAj-l(W$xiyem&T%N(4#sd%!6H>t_Z`T7L z#~Oh$Y^*d;ed!s2_8>>o``uoTgdN&{K0~2GC_Z7Iyf_OZIA@TnsTc6*aZRTFCEIp}7O+dNn*svvXK#ls(XvNBsUDzTy=WPVZ5c9tcPpem zBestbOnR7-YcH%v*7&o6e$Nj&NeFMxS-1G>rfLx7o-U(o_gZ^Rwa?gX>rZXAs==kn z(kqnEt~l*E#HwQkv_*}+6ot5>wFh;cD+FwEEeCqn0b(b`zdNI<73N2}{1CzB^~l6_ z)}1(jNqW3!^a(k`8XZPJ2dco~sRYDmdu=W|9iIpSlf$!ePfk*?J1YRTmx-xYs#?6E zhz?CUPhas5r+UhQ`Za4GnCiH4>Ycp0MVLu1&Vv^}{fdd5-ddx~qUMI{@9~N3lg29h zwGSa+i3x!jlQ)n_0F~lvnB1+=?q`>~eS}&Pj6)th_F1H*(hRhD)^5|$aSypuws+Y# zj&QI;K&c)R7Sx-{>K3$KWRFAjF@~*u3~ndxb63IvjwaBqYP3C`@OR;$e%S_c*GUjP zpb`cs1AR z2oMk^vVtYTI1e6WT3nGlH z3n@17(MFc`E^G52YXKHRybpZzYXRAU0Di#%LXuAcCg>MOUNA60Lh4a&m+giW1GY_2 zQ^s1VDKt=VQgaWpRuBtVZcr73Tzlxbf^b?GS{)=JJ~@X=-2E6R9-9(czuT=CJ5kH- z|5&qcYJ^g4N^8dP(GxS0N3{h2b2|zK z!l&sK{@wrhm+GLxF=Qe4vj>GPE9BwYHo1FfMLX^NfQbkZ4ZmXV zHvl+?fSCiPUAlPRPb?X9}K( zvbw4vP_THq7Q5EH>%kjM=<;tnlzPL1Okp^=@7fwHR&2yh?P72YqE4C* zAP!hk!Z}9FG5B?Jn@sf#c0WJpAhjpw6*P81`|R3%)IHRL3|dEZ*3fj*7w`^k4`SMD z&1chF-IXH8Uhvp<9wP?t3V-YqbsBx=)Nj|0E7TY4&P;cO_j6j?i^h4qk3}2l`FFOV z=Zmp^G{pqtkC%IdI^8wxkHs<%&3D|X5zKNzcbRW=*K}7r&u$-N_b;3S=q363i}CMz z$7X-gU4NLE>u9^t;hNsPJ{_zj6OmC4001BWNklT+sY&8JnQ>>G?a z_(lXl#l0j7j=|{LF848ron`lu5|S!PU}RB9wPNcO4M1mk>cKmsFlo0M#Ww{)sTQvm zqP@SwjOBF3(^W9f37>y)!Ph={1yMqpV&CznlrRUyCW z3-tXH0w-LbK0viv1HxxYDhf&yDhHfshTsfphoDlRIb&PRyDU|Jyn?0$pa5An$XZah zjC#wqNLO|&RY5$x#*hE#kE6? zf-k@L9A9h~n+^p6LI5!#YH$di7}>hovntv)IO!566`&TIpqE^6%^Utd_TDbmvMsys z`j0W^`q<~A>fYOZyKg&fyJM#CU2Rki3AJeQGe!jk0761+Wlv%RAaDM zfpaZg`h0e{Kb!46yt*sS^$O^>HW4lalcc)>dHwI^`cvJ<@#)#ceSUxp#JGtAx){L0 zh~d$x?4Ypbjf<19s}!Hf&MG0qwF$u-&DP;1HiIoblLA5rKJBqdXwrfMM^i^trJ{2g zszsrG%BHd)bZquEZh(E2{a@qQux2tlxC`o$uRehzBj+;5RhqvOsWPwcF;i?NRvWFJ zK&#>d&JmrHU_+?Q*)EOIPBE#Hz`P^n2f)OMFqMV ztOA(tKm8K_EYF#V*kD zY|+~qny$fGw_IxC-kR63cDwg$hOut}l9VW}&zDGv2B`xSXP&+tGy@ z1f{DTDt7@&>v4nqwTN!;Mf#IJ@-InI;3;Kf1^OyK4*_Ebyf}3D+LK9_r(zPC`prJd z1sxUhM-UmoTrx6%o+3sL=sBS;2@i#kz(@kyG|@s6 zPrw7i(#XV$O>&Am2v2jso0Fob6(ZCHD=GnKQM?!l_gv6LnBQ9kUFY8;1oR{bk+5*Y zS5HRP6tV8{k`?0^k(DuJ!IW*Xo1iE~pxi`6sG0`|>fO$O(sUz}6bKlHAjn0~1;Q}_ zV^E|5gh)t>eL{V-J0KT95?bY(;*;@`?X~1oaC=}pofNmX5%ZM6fswPUk|scqvpwH_ zh$xxRb&NO9*%;g*AjF6mOk*~5=0PM8mn}vVDBe4acyY^kacgG!|7Q7fc=j7 z8Ou^|e~36qf@HyqK$vmBXKwE>zWfx3BINF1)c1~1POmUOPdJ^vih&fA66OhbeLO;z z1#?oQWQ$M?5ebBZ4${ZfQ#5!Jrb;r4%i!l+H*mXdYDTPzA}uDjGq1MY;Mw6tff#~_ z7#S2IUi3X)^Z_5v&**$VlahSu3mhmT1Yn4E^t=l*lsa^rLCX>1^1L(`+oZ&!v&2AS zg?cw(SlpfnAr}U_B#^}Nk(Er5kr9!Tu_54Dn=FE)VVzh}P6%nnn>Q2WAt9B7vSets zE@Mgxy0NH-zKaOLCV}^8AfwN_?_4YsI|N2a1zAi@LsP;uB|J?to>Rh93Qkfm6~SXk z_!=7-U&Qj#vjQg~%&I742cSwa_tpj8f#Mm!M1)Zl0~E7^)`AL>0*}WBeCpGm$6xs0 z{}R3Pb;?VxKmP6)e*u5+TYgASc#ZpaZ!xPNz?v{8_2w$DdTEoIWV`r*p%&;_Jtx

{-9O1<+uBC}}`(gKqG+I*laXgiFochGo%rT%$47oCfF&!6-~ZKXsZzk2aXdb`_cMA{c~#9ZI2gUWctIaHtPL1a zAQkcWJ9Ds#zs@41ITz6irm|?V)(2W6SF=e`b#vy)88P6ZAl4>>cX#C4nn4UUA2V&l z@USLcH_U^YyR6*I+b#z%Z4#0yT}KUd3t~$-XYcFyg~EAK_CkUqZc+e-k4D7)c9)0)>H`)d7vw>s{m1Y@IG% zeAeO_D>dKNfy&I+T0E$(90ICx_vZQ&f4+nky4K#Ow1P)j4Srj{BzQsAg}5>*`cSQU zc0Mh&kn%k{i>&c%sPMN{p+p`dsk>;)>SbT;Mj~#p*%}6r?XGoxrLW8C&5S(+j_Hz; zQe)?|DP9FwBS^`MYr3fKwwt=r`Z>_W*N|#6QKwb4##cEjS2nY1jkstZTq?_muBhR( zRm7nQaKBs!YsFq05W7Z5!*o$x6RfFoW$NCn?m5S$B0y>9$FHmiJ%_f{ZU%d5h?;Gd zcDZ-g7y4RZxU6WjoB7#{|%93zg+}X2gC#WW@~&xU=bc%{>lG zK!}hKaLNQx#_=RR^&#}39(ok1SaJTa2skLx8hxrYB@Rs17_j2VQnlgFJ^MmYRI5@L zV<3m5086q(HZVc67^s5CiWqKS4InGvi7*ZU+2b`uwnjl(GWy;-)q$}rg7LO*h82vI z?Vxfv9Q^P_=(~Vr$qvY|uIRD|`p&>FH+PIN3OY8BUw~MqJK14p=pqW6^eIrlU3b9D z5j{nO8Z@j4InB`GunuNuo+0zo>MOMStB1^!T{DGUMruV6{4o;uNT(CDC%iZu@H89D z?rs>Y+eR6KmQCL5{}v^oK#)rUTmiAnGY&&U65!DFka7YEt(`mP_WN3_C0QI4tVjYPojVK4#&sE4&6C#aSv>ki%5!|1p_h=fZ(%L zyQfV`SCkQDyOe~wrdM3HZmI(8S`7|P-u_RR~HygM(x!R5lxoROF8 zo1*74bG3sgh9QDOw?b9`LJ+mjrFJBJ(-6`1&?iYCsbEQqO(Jr^>yj*1(JAg7F&5wK zf)b_>%$M!&KvB!AtsdmwVM-k{2Da$RE=2QdhJc<;_W17M2Gi*g|M8#rH}C^L`s4Ke z2cQ1B73eSDeFy*Czx7k}`WOE${?^a_C4}K!1Yhe_1&dlFTv1?IC*@6XUJsVKO}(Xp z&lOGgeqY$IUr!2G09A2UEj-q^rgQ#k+rQuRe@g6!6xy|^8=eYwtXAC@GIS1Ir4|-* z29-I(!Ee(OY9{dKbXYo@x6m5vQ^>3Ud6f&U&S;NEvgjryAR{0HpzeSeI?Nm(6daz_ zVJ?$3?$jo3P;s)kXK|}wP8q4>wGb{PBc%nBW-MujN_NS!tKxhQVvkI%Xh0Qh8Yoq| zX@z^K7%qQIs)eE#CUq*kS_3_1jnyT+YvkIM*@S<@p-~o8)kpC6EBH@te>Q4_%2J5V z3RtaeO~$fbg!7zT$3P>SzmzKKTI!u2k!ha@a2Cd^dS$v!`)L=o(o4eb%DXr;)~T~k zk~{7^j;crWxIGTheXQ*AZ$irRZf>Ly%<8r>?l5uylQn1}2MlCh=TzGrZj=<=SHMu3F4g*?gDXlJj-4mgkwJWk;i)_EYivb6MUE_Y<5KsS51v(BZ<-AJz#Ul`9 zjg_m=QM=l1596vL=t4m6kVW#R1SrK;2PkVY6JJ%_8;?= z)7rE_t8h_lpC)bJqvb{Hk`#0+RL^D3tB3wt>9$iwZ~wt{)BY4tgOcO9}z+jvYEuJS@eD*G%0cDpo<~_F5SIB}m0+_lmTa_hRmGglNA5~;+}{O= z6buZ6jzEzyc&H>>8xiX9ai_$Xv%25INUoCtJtAh!5CC(|pg=~QWm+c;RzO*2wB_U-FQH_yGK4wCIAj2L>9b${mYO2!v4!7AOwm0aKnmHl=_9BTd;i55Y2Ja3DMH`r=qhwX_MC14I@B$qgei0zUnT zPvAR$%WuO6AAAd5ef2{e=Od0M^O+lCVuQ)_1O&?F@h34$LJ`YfS9`2>aVpfIXoObD zq)Y#xcBBiMEgGtrbUq`PC_XPpg^-k?6#brVx~=M7(wq@BAw6X*Z1XQIl(~=lpL1yuTvmJy&0n(B%Wg``TIcLmLC<^!EFgRq+hP zqJpC4vq>uM442Y8g{ektrRwrHLnqV>sHsq;k2`MoDsCQS%aPd>$ri*w0k4( zio{(^)6NgKODku%MXQ%7ISea`K!f z0Bl?pNtn_E$qPghve>;01#+3OERz-KIa!geby8_za=s@OPsh-jzF-?& z)wwoPmF*B$--$q5&ap3K4J7W|f46-Owdh%WzQC>(Lh9vNl@9tLFL;zvlM&*D^p+|h zJ9r@Wg-A1W5uE+H76MIC?n9pTKDc&bR-A*R{FOSOiY|Huzq;RL{ZjFQ3qYW_MtpTK zVvId_jc}zE&|B}&q+$SIUy}xh_%34a-l4ag?`IJk7^vXD5m_??0a;B=#N4nVJX-d4 zI6xGbq^!L!_Bc!?LI@o?j$kih3B`bB3iNJ)WaiZ}!jP4qP9Dd$W~+NHcWofHS>u){ zQYqzS8}Jajgk87B;yU%@OQ@W`>t-di*Kjvv2 zFKa`rzK#wQ%+DP|K-cvcg6Dw4Z5w`}79tcaO+jFj*sRrlV#=o?P0X4XyQ;d%+O0uQ z8&^*6uc-!1s~>rdYr{r2N7J-Lv%V8d)ml?!>Z;Ji2xi712K2$Q;f?)g4MB0W4Xdtl z_S{I&SBd%h`H`O$e30a?p|z##GP8-BS>{R~;&O{Vqcd-KeU8x%2+~MH4}{YYQ#Z?$>t8 zS|=Da!9jPXF8Ua3AFb=SQayESI>PW1|G@A1LIt=4w#cD(J1f)%22x4Bfhl?-d@Tz` zFKkaG`FmkEZE@OXp@4Tfg_Ml<`hb!Pq$qBp(U{2rG9-4W9V#4$Vbk{_=t4m37>CG+ z9j!;5mAvX9;suPXo=mBN$D>UIOExd){eh9QVu)6N7mCPEo+O`~&%#g+c)ci=1y-Po ztX)QkVD<~?R0^_enX*gPHG5jc7NzJD@c=ccOVhPke9@L7I0Rm!j8qj}4Cu#b9Y3zV zIC4Laz2=^5akWGR_N-At5HXMy2c#^B9fKo7iZxEiH#=2AAIKHOK1nHxl1<`DC`K|T z7;ROpS@3*P+#Luv45UnuF5=~AH0;RRU<4fNyvo?;q}P0HeEo4~IS=m5iPlp+Ddu z74&M2fXs|Qj2C0Tab7UJp24w0oS$(zEto_>*@)z&WYCgON=E2{Z*Gi^BkoEgf zq$o5CaxRFZNQDu&MobZ8EF$W&OhdXqI@f+WJiI?B9`6|af$S{0KjJ|f`6Ce_T$5GE{A z(8V4h7;(1i0&e>bF)(`1ij6hS$U#66g1*WO$+!@ozWoG#?x+8AJT8jg`h(y9b#$cz zzv;Wb13&oV|1|yiKl$(DVNony#H_&NMPbb@FD}8a>r$!mW7>mcxRu;i2_DzEmglbf z_u*A$$KIITymZ`b=PRG1$rsnAVy3fk23>4?XK`!idC|0YH(%R->CCgXKAtvH^X3pm z_&5m9_21Ww`kE=f_>?PoPH~1}DH*KBgILmxlosT?xH9U5x(wDqZ5H2IqtwKt#ad;wo4=KsSibe3tXl+|YL&55 z^Oav~|Jkz*2B%c$OoAcG$z)*VIlx2Gyhw^TX zr(N!k?uhMlK8`!Lh`#ODw;c&<{1Mk*Z6FajVh9m^*I~*TF?I+X5IFc6Y!JA}z#&Gr zSU6J$=HVWK?URr51bEK2s$DllH(O}Y?1i$E;bUiTH16@7lBvto(mtHVhTFpnbi)BD z&kiWF$1f^KViXf1Cz>#?(SNeqOmvOKVrG|UGsKg9r2QP$g2~QY6xa(>HY-V!A`pnd zh$~UQDl;qs&V9iCT!;zxLkDJSCXo8`_VtaJ5jpz6!(hps7nAk65vXN)vo%w?5OLFW zxQQLc7%(y;`Z>|0@b+tVHR>#8NlhLhtggybSJM^QruQ{QFi>!{2{(#@?M4tn1IOcr zV$gX1(L)KD2p;)7)LTyWvtB&wUGQy`B!|ut43(%9XZEcikz0 zbPs@&2G?@swNnd%ZTT6kY<;zvZ}tSz%aQyg|BlYP?YnIHwn4C3c^XQeuFVSu$GDdl zT*0(&`zEcbkv;rwOXt89A`W&+KUWTD1-tqgBPha;{e!>r3j+ec2DShQPm;a6w#h~S z!d!~WY-sgAnNQY;=FyACcLv5cy$JY*y9g9JfJY`w^KAOwVlb<_Xp`6I7XY_%EdV*$ z#5&gal;$85o7D?zbFwF(Qvn(yQ zvw+ird|VJB*_)nE&|E;d7yv1ZfDqEj)D#%(+5rMoI?&kT?!^t_>nAWDaQof|HnENa zP5@3KSdt)Vwt^}$`q^$&6Hh z*fFwf5>v2E(iLXGNZEY#Nd&Bdgo1}O4#~I08}$K2fQ@OJ@kEhjOWTR3$4|vQ0IVE!Vyt`V;_1 zlh0@PujWS=b*gXQAq*X8Spd(9RU%N=V|;mse(a%L2jzemsw~W4mkLD*=7E>2m`@4Q zDdX|!86TyBIV;|j86OtKD+P{FJgef72#-?m=w9*}_Kgb0@g@F`zw+1cvw!)2zz_VhKSr_ZzHUYOCqDf?{=gsmSLkp4 z?9bz8{?DJmp?~RxIWY$Os@WR(>RrH$ttew~u)+2^>q<>X`f`n9mF^3vkbYU-+!>KT`d z=dF7QIPDAG1fbW4001BWNklBHAFRMU z47WIR9l8*0l3KFu^&HWA)o^kspe4yvLie`UDS$BskF)a0H;g1sFgR{k{q0&+Fp7~J zOsA%hEMn?2X0vLPRGh9)E4ZshX7LJQWJCvT7WKloLgf7QGDU!*NBkA{@|Ml#SsQ&0 z@KoBa{dIEgcK{RZ+B@vi_4;+B8+1&exa)h|F|Ud1b)p%XbnHN3EslFHC=+2PFM#d6?Qnl0;V2o60C%q#1HIjm7#YXR8UZ&)ks z-K;FdH7DeXT5e(?wG!3|d+ijP&2@+oacxdJq2ALTil1LZWV73-HtUCO;O{06TpMY+ zU|XN$3@O>tO{#&M*u&PSCJJrb6RC+gZN7HRv|q2wvz?{oM>GpYZAzn=h}XtqE80NQ zWT0x@IonzCS|sZkBOEKV&z>Id+Yq!fXyaO3yEb+;)qOnbwvSx|E%pp3DP7TLmtApk1HJ$T~XJp)Qa@74xloMu5T3vT;}7dH$> z4Rv>~g{ns=sZ&91+T3|z5!H^!*$cxM4RR$mqy+~nikD;%94Wv|j2B%*DHe;tL^v)9 zvv`NkeS<7wncD?W@8He}crh{}1sqr*AtGx+$Al@@&_|m_W>LK80#bz;xtH3yum%EZWdLK#Ms5E1TgfH(rv z1RTb71Gwo&Jf{VBxA%B{{fJa7_2AG21kMPm$ORx)q!K_Rhyh3`p(FD$&&95X7$R~h zSdyaa8LI4(K)cSG;W!|IFoE&8Z~a!hdGm-5zw%`~;Spc_fBrY*T=4kt zWVxdb3k$F&l|;{1RVN)+<%4KVB(kko6SDTuq$Y|cAZjuXs%B796bcAjP`u-Al28J2 z*&nGraz z8Y@kTr#B}Y-%Oaapv)P+oD05|fj%cZsNjLg?(?M}W5EQ)>=VwDJIqg5&;d9Ac!qf? z$3CEsjEIbp6+w!tQWTjKd09ntC{QE-6AB272tfd2@nt+C;OBqlui>A#z0v>l|N7hX z&7c3)uUnBm97g<^zxwCscmAm##2@;X|FC{=`0VOk1kR(<&JAg`_@*6{Wup#kq}nP2 zt%sOifrV%vJ>$h#u7!8?ORnGDPBW}N9a;0Dn`yYZH~M^&T@M7c3*JDmu#abo9n%&u7mLbOJ#bbvccb~N4I{hs(J)m?U0pi z`q?%Kb(4~B=?l8DG_0W26#{2*bkxB2Ty4MsLl-dcR+P6Yo3&tN8=)Jg$@Y{wiB#D< z)T>Xtk=WNFinJUi z7+s0;qQ)EFMsw|9#4_S*Z?T{P)F6JFoCublhGH$}mDaem$okY-1wT{E9uwZhsI z;8zNA#amQEXyOld8FJFFV$R|-&Wm}$8q21x8c@2_*`2dQ&|K4K83*1t-sJB4-J6>R z_xiaht*QlS>w3Lp+gxmtv;qBADFXv>2#kdYx76V&0doO>ViQYNym!+h^aGYTVG)CC zX<d??AaE};xMP;IjNBX z=2UQe7KD)zN`Zs`8~mFOv`&#(6;UkHTx+JZ18vwr49b8=F?~vYDraco^8q`z>n(y|047iB_4~yXa7bBi>!VM2t)Z8>3n}0V3Lf~jw z*d8xu^k?&oRrnq1v?%yINfeI@a2pj<0f-_-67O>|`rbezF(|Y!PD_FaA#^s$UuFS! z0nuaQSh7uIi-M3%`W1j67SGmo);!=4Aui48LI%nSMWRmz8B7Hldc>5h$T$MMVd%lg z$TERh(f5Q!tw|DI4A9dFX?lR<1)2dAvq30DYi^tt9BvD=WI%7$yF$7Fu>(#LaDRIT z%^i|P9B%G$Iz0fPAWwo%3;}Nz#qoKv_vIi6<2_!_Uq#S@IA>4?bT4nAX+}}O*egP0 z;4vc06LOV9NydZ#yHjY zJY@z&i(?rgaLl#=_auI86fnbV9>!N&@9~s@*a1%`!BPq$1>}+t0^=#$f&Q2N&M)A3 zn!yZA(*&IoL>MUurX^wQ*(PnCqEM1xX2LRbh?-0tqCix#Omwx0xMTw72P|m-9|&Ab zQp=JcQ$b#2=VQ_kpj{7TMW)dd5}H614VreE@!8+;Tk*I5yPw7Kc*L^U;W!+63{=3& zj3pH)7%@gjN5Hb+5PIBv^XKr+=e_~){?-<_V)1a23d*uTmSP?IT=4Yjgr{Vxi5Y^I zSujH}dxKHjKiC}x+}w^P`K^T#nI9f*#bL5WVD#pVs=9F5)}cxhu4@yyS8iEjT&{6u zHUg-s;=lPX{%ib^fAe2O?Bdr`@s5A-kNzQi=WqTl`o16dK7B8IfH-y+ZnQe7ZY5n| zZTz+qaaj)`(O<>9X3kn+{oMRJa)4J=$HhtxHf_uXfg(H5l3OaUD-`7_gsZ;K+lEVx zDD8~8P8-N@JCO-Ud3R2rGmre+p*m;47d_9}rMDny=&jR=t>9FY9kvP$a*GVWMz;1d z?HRGMEBdJ6>L|7(Btm~_jn4+$w1kjdly^G@# z-8W5kz&4R4Zf^(UUbDyLTv6CYwb3@^uWypBF=xAZ()O=*+RC$oEaL)DxMi2)YF;q3 zdgy7D-&50IAl(`TtE`;}v5ESM6fH^&mR(-kXNwoLbYJgvU+So z2v*#?yj3(CeAb&y7V#GmIf4)oP@LwTE1|}ld{yckwo-z0Q^KLH)#6h=afq5(khBuQ zNWowT*`Vs(E@B`?Mg$?bUs_gvLEwPs4VK^~X#hYzqAXsFkG?K;*qT?Yn+LmibD~<{ zw7CbEji|3pq%T~3V5+W7nYH?NJ>Cut1P#qSQR==8s9$0~CVj6JP-_i@}BkTjXK2Kh??bCM}n>Rj6&j(=Z}H zx=zMdC4guH6Vx?wZ=IOeLX>os@2@qAueh=nc9+#OwZ3fK>@O8yN~fAWyrv^X+-IllEQIV3qIh0CCrf^GkyJIenbk|Gzq9t}l>wj0ZaFQ&Zc!W?4HC zN@wf`J=3kLo3`5qrw?Y=#tS#$?X|C<>+V>9IXzjX%u-= zZ7sg_O0M0mgR(_{M1>$10!LsL>w>))1{})?eXtEW7DntM4w0cr@TKX9AuyK4f3dia zWR?JBqh5zVn6hUobHuV_+>ACU?U9h``aOI0UfT0^G5Uh`VTq-y{qb;3*e$F=9z#@o>xzT(il}El0dAHu0^CEY#)c ztF+7(wfl~dQig_rjs;&^60#@`9kzphvhH;e%XfcH88>~vDHSZm;&v+hD+jxlipOr* zl$y~;K_V+6Q&Jo=Fb2V#EgA<}kTZcfpzCZa7^7ke5t$TSAUw^$*xCD#OIgXHaDNc9 zBIk^ZfUdKQb}fnp0$CJ@5kP^8B1?pZ4xK7wS)dXTJ3%Q6gpTMAHz@4!Qj~!L6iGl9 zlzE1v$qz^Z45Javi4j=9cLd=8($N<2VqFooATl@Z!6_4b$KFIXpejAZE-ZB0NqQZ`fjG3XqqKNCJe>pem{;UBvr+ z#NqanNGce+e)XIobhc(p3f3N^z~eIGGw*!@$Kx9$5-ge#b3zLD=w!0VeC!a(j^0UK zGUkmKCN|l)xZEifPzVT4>kok-;xRY?RI~ZERS|tca$07SF%s}RC(sxmRE&ncWF+oP zy3VrF(M-yVaH^&6|ul_1NnjbJrfplh9DbE?{8CxbT36{qrUVQEw@wq?n z4zM@BPPb|Nh^O55Mve{m}RPQ~E#sou9}1x9>SUb~|KHlT^Y0dS`85 zEgEH=ddH?a9sHnL{Lc$D6B4viV>*j8u|skFiY>iHF1z}*x@@(|bP26FL;CD|yxYXH zLEK#s+CRouPG`@hZP8ymkB026^s!`ExyFu zQX>hk@1>v(h*`CZvY!}jK*A;i*5xqzNv&W?=6>fxXnR z`bR}^A>r4Z!efKRt+IDIpQ_U~{J3HnJUfP+!31?B@j{y^THCO|ZcM+E*ssM8)hV$l z2jQ%q>M5?W*`XU(Z*>T42^d6pZwL@2yjcXt#m-{^ih;N0n!3C=X9CB7=&;ySGKvyj z#tu3tPD=(OBNC&KI!!-BQcg0 zq6ZN#&egapYWAWkV^_@;eql{2ZKqqN25W5xRybQ8d_nPHMibXY21)Jwg1+q{Uc?T; zqXnVKfvx~+Kwc9HyzrOoU+YZ;FQf~$CS>P~m`c2_M;{?tlSUkE2ZEa1pFJ{87(&Gh zbi3B_+HKl+4<^)XT+0nePd5+9S`I3jX}PY*fW1umY<;q2%%0^T8r&{m^R1V5TF{TJvKvqrR&KBG-MX|@`2g+aS%NF{+cO}SkR+Swpk)@R6P4VbOzdLElr z)gCS6Z3jEYWxB@sT)y68v)1DFzShR;eoB9B9mj6%w};ZTT_WBn;$3rS+uNxrvY8q%sqXdn)E4c#3w!&F z;-tXKn-O1mn!yCTj6Gg0Cq#+}5KPmI!`+Q}(ZzfQ_hSz&1rMx{Y~G)&gh-4@1UUp~ zIRPOcmEvT)jA7^?D!7f&$m|}?BK5-Pf&toiJCK|F8(!ph_BbFE?~s_nQBV*p+DjB& zfJulUR?vokRC5jsAgo9kzA)IoA%DXKTbpT4f+JbCZO&reaV4Zu8o!&^<_KG?UycWp zaRHc8#t;LhCAsxpT@EI=?Rv~&FubQZud*^Fwc;Oy=#eoqZF$dqN0q z7;uO-ofb7ISl>s;Y!ldr;|UxBrrET)UB@^rf)E3;)X21eI7B=>9MKICAsF5LG-q^u zz~jvL%zeRt1xr>?2q;OxEC^cMZCfL90PcFEr)O}OLCByknq(YgUbJO~o=(sbK`8@a z1nUXX-6MtwO*3#aLbE{U87KmIo*|=6()s>fU}hAYkjIGEuil`G1=BQPnkICiASc1` zLNi@#g6X^E@LcFe89f%nvIBWkxTS4V#oOQ$Xwn7iBRM1-8M zO6+>}Q~)BB0)$X-%r<#Xnbw6h_;RyAF%WQ)j3pDgE+WkZIa@>^_W?NN8rQn$@9buiC zB|(`B9s(mT8PlAAjxqMZVyf6-YF$Krdd7GEYd=Mw|AYU`uUFV#haaDO{}cEhzxdbb zkNwd#rxOVOdC}(D$tYVq6Yv+wJ5b(q;u1|54vR3PY%YN*$abxV-tMuN7Rqx(jxS7|WxmhzY`PcQVb19A6j zobb_7#RjX~di7v7(|@>4siqv0^Y7LojGa)d?1_jt+vb4Vx6Uwyxz?EyS~~QFOH+kd@B9QQYgrR7B7wT}Y9&oTU}mh@F3X zmq@i18O>CZ_7P|5(`FDE@Q!NJq#`NV6|$zT&{EdO#-kLEFuh!q3fh>nCK|D!i|d8q zW}~n(E^R#aDtU|QY?B+bg*>*6y#4(y=c{jJbkqWzH*3n?rm~Ma1YAyccBJ#|gU%N^Bz9EBMqh+0x`#OG&PeLsVq+T9=FJ4Z7_f5z)8E-#6Z`B?+ z+ZQ)j2wnNS(Wn5FH>tliO>=FmYM0^ew2|u19BAU7Q`_L6=0hdy(*kyK{nk&ewA-V0 z$~Zoo+biK*`WF7tZ~xve+{7NF0nd5CF-_zVi!k- ztTOE`DsB%w`armGAY0D?eGKRWuU|)z(UD>tJKWy&=!Sq02xGu@;c-A<>-zRX07dg! zmtbH!CdH(T$IMs~aMFNT2org>IY$&?M21b)IpCBOa}!r)6i-5O!Z`!TV$Mp2`J|-r zGZmLVsTO2!;O1QL$-}LKSr#M}yzDJXL;)Q03F4=L7)?$Zgz##b@gfE!5%c6TZ_^3~ zeL-Q7X<{FCM}(y2nJ)_T9i!v|br?wKI=r~ON8rO6;g(_9@-Z;xWQUj-Y(WdbI`5)_ zK*VH=?GOIPYL|=A!s_H zptsIoO3;T7p-TpmLMMS{1w{eJh;Vp;T!69=ke*HcUkDloECrw;A`kC^pC9nTjQMH8!#rV@0!;-^Q$jjrOiRKt7wG@`fZy}q{%QKg zf9&`EdS(4}{_*>M`1jCn`z`+n{bxV@AL15wxMfDK>XDEexMU>4Er6MU69}1YLf-ko zs4zK%(POD-pC-Jyn>QJ6^=59dS6g&S-E8ZcxE8eM{<7vbk<5C-%^bWQdcd?rs+~n( zZ{aq$0+x9jUYjmJHSJ{mB6bGbd!y&MIA;*KUn9`_WB*>yUV##w;cd_De7b^K$zy`5 zbhxG1Gi@Nh(8xmR>?h;$kchYV%=OAEQCwz#)5T9}m~6CneUk0hyJ8$|T-x@SF+|)P zZUJ-G^+JSV@)n#n1r1j93_?N?0_$Duoy`;%b|GM z3*3`_hzt&lhuN4y9VrI3;lP0j9Rs%^A_fDuq3iJC#V7Gce)3-cA1D0sM_)!MjF-1J z7>04BgRAn&ieS&AH=23`b3lx7LvwkxH)X+Z{Nx9CczSftxxL_`SUEeY1(AbK53QIE zK5?ze2(GM>)exarbTj4Zsyf(+_7=%YrYfw3h0CmiznAQk=)eZq8$yT3j6*OA`5Ng3 z#!yM#Dmw>vOFB1LBer~T(FV9>MSBt;N%0soK$wx7@lqtaj9rnDkv&;qO-xYpoaY)# z<{tAEo<@x%oN6=lT)VqZMQEX2TR~Z2fwJC&+Q>=B{j%(qvqKE%eV`<26V>3?+Cc2h zGJEXg2@qwqFKme+Wt9%=rDu3;EY#~!R#2#(m($d&4y`KXLQ1}?T;Z%bdv6_>EDe`} znnZ}2GP(0xpD`$E{9~)!XQkxya0XToAn(4n z?$XN6glK04y{^#I_4_w_D6wvTc+GcC+xmc=MX5EF(eATU+Jou#Z+eCDVY6neM-ARZ z#cd6By<#KVnzq&cMC<3W&HOi&(>5umrfITEd2>q!?Tu1bg5uAt>Hf#z4}Z@Oec@Lg zpK*Way|bx^n$UUHc&`9|CAI{VMy0w>=f@FI%sD_ zU<{twS&MmQ^tS=Od=xxOKq0omg8}qCrbQ3~p zr4l$;E@%u4gaM!Z4WGdW-||^}`Afe7@?!PXn*~}5Zf`mqPX)sm@$kGLsNygNOp~nO z5miNh7$Mmrze3;Pog2k(xJigoaO43R2!$B~W-t+Y&ETY4bcT@26d346h$PVT2C~eM zX@Mq&Ox7J_2Rv$zTk6p%BMdzrmy9`0 zNU7i^0&^CfyLuJBtlOU`WR6_$SGqC5pxmT4;AFP=qQM4?$x9uiKkGSVzaS&%cZOqQK2rPy0E3m%U% z=6S)Jl<`r{`0A4JE6ai}%?o}hEqKToZ;GIlf~QjOC-lR9U?|byse-FYyK<7o?2*OCfpa#5K3r^o$iV0He z-aR8+uRUmzjj*iRWVFamYOWXgr@Wg8P>pHvYyJLkkN0oi!JFy0J}Nc9pFN;uUrx5g zxi)3huUZaYi)>T69IrQ7*K3w7Umd0XYOeQRbFqGH@+*GW&=vHwFXY<-JNWD=HU(tx zBA(lv+VkhDm$v+MQnMa<8#oVLK2OGV!uIpdTUWDv4?QD42RYB}}4_={gM z1>LKUFb2ifCc!)JREl*(6dzRh<3yKm)&^Y1!49=jDn1G^M4XOK;NX#l|h?<8C`dQ}DzLo1Rx2Kvh}P0gc7>2HeID z?0k#^6FOo`IbDHmxJ0b!CG493z z9rKzL1t|IuT&v*FK`(}rKle5PqRNJAwAS7;PdlL59+$|g`iQRNZP$B?clU4|xasbF zCB+3@yq!erPM&>MY~r$Lq&1q&Vy~{IRasX3^=$+hzbzP~3HRB(Zn@UPRD7HO zY%Nx=g6?pUSF$#16<1a`J+u3qJ+5HaoFt+%ylRcm)0L!;)n0(JZgS&Mqqg!tvz#%V zNh&TICFd5V3$M7E{RFiAdlrKp@4WPF+xRStea|kTOGU($4KMt}Z~k3hSd!wl>+x!t zz#gqMgl_d!h?@?cb3r%sNV&sY2=_5~w<$xV_~J&ZS9>XfF%Yy5cuYV7fg;d%0m_U+ z5714-iAG4V>2?5nq3xt*4tVi$gfb%wd+xa{tf!M@FpEn}k_e<^WPq5dn`SV_6C)xa2q&mp_=~UyDUqy}Y}{vLuWlc(KH|iHs~3 zcQ(u7w8(;#Gm>WWe#z!vsqssF*u0m->U2z5rHEN*on9Q+^K-MJk8F`)qzc1cZz`uCK@qu zL?p54c`=g}6*XdB=(g-&*Sr%I@4ZK8X2>Z6(_)j^Zp6?>EMWvK8QgVX$|g|}1(HHe zf*d>K5>fgQM-0d*AZo_@_ah#b8Hqa_A3j0{AZs=sxl03?$UD(yODJMwPX&QuK#dC& zu(1&QhEgTm-`wCiXN(jOsNltT!2SIJ7)EFkpcIhH)&vB|jGhCYr#A*x6M#`-NfIMlqeTp|sIbEvN=BIqC<`PNV0}O`r)&Xu_n}W zV7Cc+Gc{i~=q87@$y&7oY@_eH*!)&5%qkrxY_V&pWIUXnY>lqPaGkWRU3gyC({mBu zrD~ntUIxEq%uPy{wb}XiXfk1Tk?ph(-Fn-M{qMC<*DJ#DRRY^Eb@bW+#({BL=kNOW zzZQE&JK8**HEGiNkjAAa)=hC=_2$%75!G>SG4msr;@?3H#z)T zteK*KR@txdVQP`g?20ks)q@|{&IWx9YfKuu@0+VFL=22t!GWtR)`v8!%T?v932A$9 z@kVwQi&TXVr;5YWHXn42n&Y)ti^OZ87SswpZ6<|uR&d}-qpb2C#$fL&Ims!Ri^Md|+k?7eNUZCzH^_Zwr*wbtJIyxjY|c0&X0 zOWKG60tx|1B*qscR8&gA7zGmx6Y?dNG5J7B(ZnjLd@z;Lij`F4R^zMOOR%UWyBIr3r5wf5TkoO`<=0$=((b?er1?!D*iv-VnZ z&N0US|N9q@Y;EctoXvj?_1;KR^yu<4*Z8H}UhJNd{FLavR4Uw$S9_T2cAENf(tVlV*JTL1o#wzT?!P%Bz8o)M z_;OXTZ|HO)89ayg7AZN`2~^ARMx>33x7X@n%90IRP4&9lg6<5QgF& z`&^W+fj)cmhtvXZH}r@ZV=5dDK6y8DDSfiYrgH#XXkhFUltMog)|(Y684`u<-N?;$ zlLAC z*&Ut}OGYek=nJ^b6KjpXosXs1{KleSKt)PDzK2(*j|Be5K{QQ66i{y{~*+0QWxblW$ zwG@>K&}?fXvxu=X*iYb;8mBk=e`VprK4}%(iB8X!H1M;X@zk>`_nPF(UD3==JK`s1 z?K6q@1101=fA)it_6H2$@_;0MxmoM!1YnjC+DpFX10s9-|2a{Ap&VX({VQ|4lFYwP z)4M#8lo!YMvMB23d~rEF*xBMezxLUlF`ftj@e>#D#tlp=^9`bHJ zd~v3U>@b!&%4nHme17th2a2L_(nPY8Qr!?b@!^9c<(Gcxm-69{eU#N|P1HaLkq{zH zXb=swvGap-bGQR;`JDTL`g%nL>EK@*%Ga4l~Tur?>76uqfQUKAhtQu%8M?ka@OEk z?nE!jRBPcb)pzeF)oJ2hscAiW<>rM6_amGy*EI7Lf zBUQMA7x+r5gmJBVbEBaa)~6AF^K->96Z@<#BI35qRvpqeX_50XVhxYdo2I*JM|Fze zY@|w>Tg*`1Msd}(v^q_6ec0pldYgfU)O{cot02guEZ;;=q}p@S&6fXfXARUPLw?zi zSme5k9IZLay`etSy7)V?EY_z&+m6j#c(6x&G8ml;@@euZOFqR}uJ>$nndaL^n)`Vg zmz@>e%Yt_qb}705QBJ$oNe0}p8lGW{lY&VWUGrs;EHCyJiP#Hv7k$X|epDnVb5g@m z9w7H9@sr)mY}tM~>l#c68M^F9oHzB4ECoBmn=JRL7k0tROZJSVJY`wXpT>RMyJwCh z`h|{O>@@79em?fO@b30NtVOJrkxhGvG)^v0^)PlA4o=U^4HvDG!ix!Gg0^0esLfmq z1cYraeEeDA`60O1JSaCN1U!-=WoV;&hJrvsJ#Hf#7aC|AVKQmxkzOWCau)_0@E3?lx zmRtz2p>5sITe8wcVb!*zG0~UAqo$!ZqsJQpB^!6ec%_Z(Qq7gknROEws(jwE5(W0f zz3>@W2RI~~Co7Y;Ln|uzP&2HH2u*a`cN1&eTrv*(J?l1bx6h=MSvM}VSMeB;^{V07 z&5p;9R#<^g-W+)E>(?Bf-!KjXX2R|eXg0yU^ceeoq=c4SoC)yZPZk_$}GQs9wPM#@S~AYDW12V@+P%azAerJfuc@332eRiq?Avd4ib$)~ZMawr{7MlY~$ z`yE5dY}>@^>&HCHg?DeCGN7!QK&i06RZ#{8iUgvXdnd#@*IgGlq|D`IOUQzT>i;LR z4uM0TX3lCc;dX?J>p&~U?QYLCO3DMx&2+neFgOntf1o5j?$-;c zUwP3+Uk@AQf!Mfv)#Xp@!(KY^cu4-fAdt_b^{ReYf`H`I)+(o+v z2tPb+sVGL!K%;@7%F5f7@^?P?k9d6j#$52NnhnKLl`01g;}$TYwmw~+Czl@Wp{lg? zRG&pjW~{ZN=txTikERhdAp{5#*q1~R_2#oeF*huTB?3|hGeT4t)1FX9M57nxb$VYc z(@5}WFKP^HR05E)H%A(Ls;cUSbE$-Lmm^nk_3T*jh_h1Z)~O^GZgLhnx)~8FW&+)Y_8R-u&NEWi~nZGfS$QQOQd7xoECr>O|6FSwY<^!E$yxij2 zE!1pwGF@Jn(d|^x_V8Y~yq1gN+2;JX`+E6FAvYK6zOc(8j?_5sf=oj*7W=>=ZbNLo zf8mtHJ~0xG)AQ4gLl%3uxqhC6rl&umCv4dZc3L_0C$O|%*vWfv0tc9W_9Dmkq23&E z71y+SQ};(@)2-P=;VuhTUCRMuNS=XSlHVAe6p|T-l37P(pOmgr^m1`2Zcu1Tq0xqr ztM?>X^{IH_-v;&U%_4IogO$Rd5DWCZFKm5362+>#JrkNh#)wVi$<2#9J~XIM@LB=A7#8zGSmk+sxV~tJ57F; z>0#F3UDcc&2zJQ6K}H#Aqp>{0WlMa%Xs zv)VL_hmq~g$UX|6yJ<-&V3vqcumWSw94xSog?Dcbys{4LigH=I@?%kkU5|AgmNV(O zPxPCRkk}vsd+i+Rk_$OzB>6qt)`?CYdbDjw1opWj=giQLgf7suYc89XXZsx=AN%?& z7KR~%1%yD~i2KV`XlhUX&?okTF{aE=3L4;I-LmeKVd%ZUREGpMAvy?9ghms)eb0~X zp7Q>c=cWf$@_xrY?};JOl#B!==!#&^=(Dj7%B_KB^=423Q;$s33Qe(4bEKs*hb!2y z04;g8a^xc9Tq(DqHq~llyHn^>sr_x^(Pqsj?{=QWDg{&TdhZUI*hW$+gdjX#6*|p? z;>COj5ee=cvqR5aFG$xi(6FMV4+TZw#Ty5U0 zKtpO%)i=U47wQu&oil&)v@`43#PWDBI?e^|$55Sag=f{(LZc@q-|KX8Zx0rxr#0Gr zIry1J&d#-frP z7dZwN`&Y06ejar*GnU&iw7Ynvh}?B=nb}aJNmIFh@l!7vo}h$!oD;@sT1~8vQL*KIESYe ztyrlv@LY{z@g_H^sg(f&)FJl$*W+1M}@)Vp(K%YqVvfw_5QHnRkQ^`ja zLv2n^WOM|sH?NSa%vM90yx^5(;g-v=@QP2v7RAU!jWY;QC`4^Iq)e>->$a|46KOp( zfhdZW$uL$gL==a<;b#WL!Fwi(l|r&i(F#PX{)=4S&s-m?}YlReKMt$IFQ zTsv7ditLD4ELFCz2w9B2a5j-HOME6~liHzT6Q^VgIg%7+?`@qtH$k~sEh%_s9ULh&m;I$}^y9uWHO2%Z{N^wJHNUSBq6jAt9(CAokJ=ZuEt80;&jh-4E1Imlqf8 zc3YwtL-y#A5a7@cPMoY%=0>4UhSkmZVx`0YIYB>`8T8_$`^-8hu@N4v{rwgbRuP)E zAqL~FFEowvT9etR)BJZiGWLa?u8Ep_0ci!hY|tjU4Io69MVW)!EJGGW)v(>*i)-7E z)g!{5j}3=Gcx}@#7*4;J$dj?BPa|lkG*!R{A|8mApyyIls!KL!aV6Dal15q-UE5XKrM9<~b8-!$7RBXIOHIh++1h%^! zmJgV$c=!1&T`_u_I^E`tqp4UiCtnAF7NMwOC084R=f93woob&m!%z=BwNvlidSNU= zjOzY+aS6Xwxt|Fh$2R(mr|ANBcRjB@YI*Zg(URxRdv>KRI-t>u^SUNL(jIrfefQR(~5h|$H@z5Fen@VwM+?tHrovL zdH&>w_=eyAouBoe{|tRJO~Y^gZQm>x7mwx7|F7T8I$l$%9I^<$exc;)oTv+uws5@9 z^<@&XOFESlJIFT47jt^Oi~+rv!tmz2M9^pwfc065p@OZ2WB z(X+L0VV}0V{^$1yJY1a1y`%1_@BjF_9_Jg!QD5R1xiF9E)X;->9KG`%I5s6i{E#xxHlR=+E3#-_8l899)^gQJ*HAc4(HO#)^ zMidRf1N8DyxlpXkYJh3No=@Ipze9>SBSypz#o0EO)KaUiXAEAJR#IDktl!-SeK?$xmd9UHd%lO@s$L*~_(6X0-+tka=t! zohnXFiHE1Vwk$Ko&kgT(zoBd|@_)%g4g7`oz&=g4TTW>ZDx5?`GI`87&ES*b&1jL+ z#MSW4|KeBwzMDfL*0`L4an-f8FmHKvvqn|unwDfnKzVncXeD3_{_vW212@4Jpim`D z$()EObs>r}RIaZ-D=8XCef1HilIgd1>~3yIeP1W*-Y4De62s`-<2D9}aC7JWvNl4{$dEjXeY0L; zBn0K=kjNzwVxX8$NUyGx>sC+-tz<64o=pRrjq;v$OfZ zcEY|01Hq}_6jD%1i1eaJa4LT^Ux0fNa*X5f!;FxXsnt@*C^|?aQ)u*(cc}|_6JtcP)h_0thjS4fhB?q z(^&U=%D83o=KI;c{SizeQ5BVuVd$~6!_vU*&=cc|w|67!s8ylio%~#*)%Fsnqb@^e zC>h#VyPw5n;WvZ2<*?J(q7MN#G`N8jjHt>>hV*`a^gU>x02k`0vEHofAyMfR9%CC?&KJarsTP^<#etgx}d?jE1 ztG_}1@PG4%Xyl3rLfpa~%Nq4~OqjB6`bJ%|!T zi5dx&C>~~#xz;E%rEMD4s|`)`!aT&5rd@liswhDkg8ElQTSARn^+AD8uqVl8$~`I$ zmlDy%WYzb7srP!e8i!?uNFgZv*hk++MfljqK1#BT5jc!@SV;^u54+T?>L^pAxGWCt zT4l{fQ-9{5TD{3S-w~lU3cLZJb3-i#o%662A=ILrc`B@nBfV4$VVR5UX+*Czh&auF z$?B2TK&i#F-*V*`X*5mBt4g8FVhNq)>>7e652?%hV{I@@Ye%V*{Wf@<-DGbVa&oD* zb8Fd}vzM|cW`);Ydk??zSN=U0L_4}&KEY*SIRCh1%D-GT}-9A&uCiQ$*=IQK2 zelPX%*`n~$so<&uF%_-;_8J*ni+UEa?Mca6mBYUo9Kz_KtG(K%Lf0>YgZn{rm5=?hkIyD#0Z4Kh2=qda+9 zmJ{it9KS5sAjfrcBBJY26{B(jKR>Bf<>6N<_qIJb`J9}4tT_thMDD8qrpAiMsbxW? zIJ1Qn?jal2{rzl;Pdy{F$$dduk(X5B4;AMp8Sf7W=gy;A7i@z$4{VVe@132~a|yEG zv&d;gsQi_0{jRc0BfHVqgvd6JT(5j$-dg6}Vb2>^?d-Am*zJLoGtc@FsS=i0{YX*^ zQ<3Uns-05_foR1&(PMGKx(G%lmw>8I*fYv#g+_%TIU-7Hu3|1OMGv9+)pI5UnSlo( z_zgIwLfb^z=&+YADD5Uv5~#WtE5#GA6Hnnr_W-=zTM7 zgqFa2AH7O2Vax|qmC-UGC{K4In=W#9*i%$U$vu-HK&VrbT^hakXqxKf@+L=Hv&okT zkCnpST%s-FncQQ}v>Lb_-7mdOcvM#g%NeWq)c%lZDP;AyuwpV#IGY&JApU+dD?a!0 z-^YhP{1H}-^7fPGSWfIx=DO>6?K1M(rDs}N3~U;B)bw?+@HR_M4$bQih1;7%`^FVf zM~FTJzr7n+H$qEh-9|4KgZF9MpgcdoRqLR%5`uftOQOV%o9Bfgbv)hg`MgILlrlOY zwlVGwiA_+3CJ?0%gC8715Mp$jz}ga7l|~F{It-6ZyXcHoibGf)|19pFyo*NTb3f-* zZa(=I``rzV6vkmhNc6>g!?et+>yB4m``KuWHWbJ~nsN;0;oVQg3Il1K7Hg^&|%$!N)}$1S_3 zJG6}yRdhd+w+D8+k*7n?p=3&Ic_k=z7#PNZRs%ieg}h}VN)Y29N)v=vy4ITkcxptl zOiqQ>zT?~8`kNA0-DfN1pW%?IrPQRckRgje72 z-F62&0RqZvtT)YBLW!Iy^yGB*{y?0`{Z0OeN_~;+{p_p0phTBbL*jH$E1W{%?4_D= zzY^zoV6&J0sV4c&w8$o)~0K0wV~cqnyw z7_-Fzd-5{Rhw{^FtEf)~Q9_*-#?Z`}+|!&W*VK;bP^}sXO-l@|XM;})1Zll_nMYLH zDq#(^5iXQGAOc<7xYxTRN+}Haj$Cq$SM$hC9SUzfzh&S#Kj(9Qc9pX>l=`()g%}$| zqD!fZr|V$ut1l&0qPok9-RnJZZ+t>eEh?r$1n_)tbx-+FL8HZrs|B38dX(p?5Xp7Q z?E{*SM^g4?{FsfAqpzCkJD)t!q7?>bN7-w(LaGIxPqF>rzMRPUkO8kpD5gT-3DU@>L0-WlR?(d3r z;^{u_e`faJ&=fU!Ea+{ypG?i2b9w)%5y2LX@O$6jd=IcQ=xD8g^FId>nsS7X202rHoC`mu zXU&c_tSL3+{@#9!Wt;mP`!O>Nh3jsOrOb8La=Xur3AR~y)UGKxa2OL3BCoAm-gDis zXSmGahFK~+B#3b+o&`SEL&2C5n<$KyY1Eq`jb-{V`9n8m)@|UhJ%GVoUl{s? z#I`O1Len)|UA733Zq;zzJmRar@mKQx&-nsU8EH_Si?WFgeJFK;8s6Dv-g(;di6?a!06N8-1DcVdl*fJjyhT>lIVxVTkAXR22w5!17Wy|`F3!2~)1e!?7?vqKW5J(*2 z1>5F=Tbil@0U3KP8>6XHsUpHqyrD1-xpvE86k)gRAsNrcgf)@F#TDuG*SUPp8+5O| z%4@G&^WLi~-ssl6w(59wz2W^AYd-Jliboe0TtB{~y;$-1a?PX71y>g<%2nde|I*)J zcW=G@*$Vk*_~Xs@yupWee@DLhD}M=3wzo$qwCPze&v_Q~LvS`urpR~OXMEQm{Js46 zk9}PJ_#gX|^6P%>uVK6U7zVsu)&kGx&f~#(IvqNEYR#ryv1u=8 zL&K_Bv2Hh9tS-1%U2(m>WYeu#uP#`17j)ePA#^_R(}1=c!c1OoT|$l$RyyA)taz4xxF5XlNENQa7Z50xs?3Q)`gM@>cwoH z{IFA#M8hnxRauwTgu zt;Bl`&FpY3bMgsjeLr+68jDNBqq=mxm@J~x@*EqNXA-%v9}c|p{F9{NFh`zFjW!3= ztH-xh8G4u}oHaFGJTgy~660E=Pq4LYR?~l~AH7QH=Rd2ZXHW+Z2X!UFH07Qm)|Q~l zIco8Y3UX3siLh$gD*rFln(4lLF@=(S(3CUd^jge4_=&2pGl%V3y%($&hm+5L^4FhJ z)+ZXgo%JlI3H7uxg=#N3QRLJZ(5VQW?y4}w?=sy_r+)EJ+h|81;Boy^IsTf6<45<5 z%}Silq*Xc>zki}P357BYA@evrS^lT zgqmVUTS(z37}PTbzAOf<_99umoi>(Fsq#W>txy7J-n$&O7+p}bDeMo4T@tj%m} zcJvsmVwqyffJanVF_IkR#$!@KDE@O-X zVpxdT_~<7-!s?axvC@WpKXTZQ=$P4&xxT#Q$#}=JKJ)telDF=*v_WdNK=3#kj6kNV z6kAk?F%Y6}5<}Imn>hfm3m!eOF{3x3QRCUJ4(oz3_K87-CI(_73<)#}+b0LQ%N6T3 zu-RQO#LV#gz+tU~^%_YBx}X#jp6ex6M!HAJxa$d8xV+xLIM7Ad?hA3fCg0wW#!P8C zB>Ai-4TTVto}Md&h74WHU06}tz@xU%jC;!NKzGxkBrjxAVMv9N2Z!jG`v619ZueNV zy!-Aa`R_jGtL%OM;kWQ5zy4pBFZi-AroG&JwqpJn_*ku1{2%}KUy^V6=6}!rhwu4r zUb%eCF=*&?dpj-B?)AwRaVY*jDJ9 z{+9jEf8kHpuXs({T+p@Q!AbXNnP@L;61;e-{NmG+yzCQo5>f;-+hPn;tH$iVmkG92 zCrU4_XXT{@uD!7Em9ql)Gr`N*10}wly`M`M+eCdo0*02ekq6%GPt$HV5~NS_V$Y`P z6$pACdc@HJAvM~y9yWD~@v6RRAG&Gh--S*ofgrIeu#NF63;sc`#r&xdc`;k{h*^}pqp^UHtL zzbwD{*Zixz=RL3AyIwgP$lwK-c;S@Fz>tTdc({7FsS{9|2&4PQV@38% zSxBvYjS?g%O62E#;OFzhAN&yQwIbudxFDL^G6S=my064uB6nKAG>=UNJ0|koTeIt@ z)hgWaK0NLo5!4p)iK#iI3kFRcwjLQI_hE4M(7yAcq`3Mst1MGWm}8?)pr>-|1K)4w zpMCG;>$0~~4{jd9u(u$BMw4#hfX;zV|1|&xQ$&)>gqHvcpT1K*sJRiqO zI4sp8Rc~f966#SDqFgmjv<&Kx;oVqx)Cofg{{2EEN+pPrF;&C@O;mcwkU>%j-ZK@j4o>JZU}=FqABaHq3Jqu9xDXIL0TaOUhNuE@x|7FRr=LNG~HL!=A#B61#Ea?l7X!H)boj7FY=lp80vb zjvPiWW+tDK`@MaU)havlriIl7$o7XrHKvO!;Wl_hJlnuK)K7w7bz1igYbd(c632~5w3A{pL%-3qes^? zl6mW$Ek9a1zU&Lu3>o?a%1Bh<@nzs6Zy$L3odcl>><%OAwskpQ8My3L6e8=c#+-GL zCKXl}4LKQ-GjBXv^X_(}?IMp`2j`%LCOfcg-Np)u^F{Jv?Kk}P;1lIXD_^{_#96W# zYbrprf~ZHyOd$;I!5=!dsb zggiQ>z7=JC)lv>CzWSGaDc}FUe~`DwXZ+mD!pe47@`KPp1J4i2cu2%XSw-01?6_=% zi$;hgQ^v&80ajzrc(e0$(wv;zit_aBPtk|SNbvjPl~);y(XBgzLT7_-@Tnk&Jq$fE zCJsYoOpJwHPLy2OrbMT%n2G8zjosnEhd=Rgw!BRPtoeev@Qoe`$w)!S^}m4wxv~Rx zc%0kRC7+~s?*ebtUoZOj?mzwA@|(WpH~q8~!9V;D|1m%CfBjwl-uM52edr&5h=26A z{sI56eE8nqTeIdXf6-U+fnV?ezUa%oSia(4`X&70ul&VyKTURkf9spS&Hl{y{7*c- zeEbTdgtKk%P@C%@{+YG}9=xJwe$Hj4?J^Jg!4soR6E2FQYkewUP%5BSBk=!Qcmt=nz*7-ttfyA3U?rx z5+#5btu}r=C(k^}N}Zyf%oT%;ILNlqNGQd*JZT`8Lc-yuxp=d?h}6cnAR34vAjM7?wb>JLssO)t>_}6$WlM1S3C_pQl}j)`Csu8==ab>%I;WLpZY1fCR+c4}HLV0YsHmU%V|^d*r?rqNI()>;csHP=o6cqX~`2hcuCX9EeTJ-QiHF(nd;|V#b(_u4%|t zXtc(nP4cNKb)RC>E><0@b;pnsyJ4ub4PVS&yI9jH6;S0l%uR^&$)#Yq6ozr6NO1K) z8W@u2uwSoR?~<=C*#y{4elG%jfzcAT{hpMMH+dG{GhM>u)50cB9rt7Yp*MuIF3J_0 z6b;EvpHL%DpgS-3A(oZkI6VVq`mvtV19MWxZ;!JdjMdTt`-I z%UF^_OM=$SSzk8Wl`@WSec?Ix!&uNDT(m+G=nC|vTzI3Nw(;mML%mCx3sfH&&5AxH z?)Ez_Br~A2QW#3$&1S=GfNjdWf=j$!xwv91nO#3}*(!v{v)x@~55R=Vi4>yrn6~T( zVbyxf85p5ec01#yv}}jk2^OKGOaykRxI7(mrHdJ=!m}rLtT!vqJ{xHsyl`aZl=(%d39eXSn5`o)KJtfyhgy7Sr zrUc%8=Tp#hBy*{xtS^za^L+YoB&SRhg+4>u2$F?ee_)M49?-+cZX7vOt>oB`thz{+ z!u4uH>q%_fN#kN=tfcHReG;etk6G)7P-{%hTx zOPyF)F7#>Oj@$X`ece}m9pCtyzfr#Fw|*0^zWVA<>1+Sy-|*Y*d;a|Y!sE-!Pg_Ku z_qUG)df{&SoImiN{(gSXcm8hq2^4d7efjxy%MUZ@85>YtxfHf!3i$|>I-5TW@?Z)7 zFf8r`#bF(S1#8JV2sU8@E0FOyNWJ{Es>b~s2(Gc3&2=~IQweb>c@ z8Yz#Md5f^*9ayHn9r+Kwjk_5YLG{t3B1oH#U8BW-Z8Z&PLI*@_P(#OSlj(c5~TyIi|Y=~O7@gn5qy z^(eC`6{pik9(z(rq?}R9q?{P4N5050-c2+tJg7byPUZ!yy44pY)jzpK^J$n<-+!zp1ZO~5iYLRtkz*Z;>&^(tuvKAR~5oyWvi3xB4w6F z`>?c5{3qyJZAeTJqs$RmQ(5n={IZ~47c0jje9c+6*>|i}9`2t0dE#F|!ln56bMj!H zc#idGWbvu7pUK*$srFK(sd@ELLS09FZPJoCWVb&|K6;z~R8G9sZY?;?(k@dmD2quu z$Ku8o@nKXlwrU0FiXBm6=I7CBBzZK33tErsAbry1?%Tms=3 zv1Ui7@eJTwKFGx$@|>S(nkFCqG6P19(g#ap&qWT?F9vTUF1)j0mhjg z>%}eLa%N&HFWKXla`?_{!)P=N9p@ALe5ok^1w@7urkWgM=;r5;;K%O zZi+FMj7fo#Y1>QsG;%wPq>{Kv1Dg;lG$kSw#wt+=2x}Gk+Kp~Sxg7@{U0w6O_q>OX z?zS#}1-iP(^+jnFC<7u58UziI?U1?XF4+ywtF-;taWssJCb%5DcBrHZP@0LQe=6^x7Z_gN@{(#2`piie4ax z10#l5;b4(Y;7LiuoLP-M(l)3GyW8jVZATCznKJCQTxuX_=BB`7>AAJQfxsgPB!sKh z=!-hwO#(O?$@O9vc(8=XcoUBTSURlnW^k z8Xm>S?e31z{D9PiNQw#Qwk^{(}Hqam|~n zH;MCs#3h+d>6Muvee}x-(iknqr9GYxCV%Jqe}KR8eSgKi{daylzv1h?iQn}dzf->E zYrp2F62#^I|EpEPs;lS_K}})7fJ7^YBk_X8ik!zjiUF zg;yw9sWf;8)w{x~EYit&&=vnu0sadPSoV_P!^=$b=f|TLonYhvf#Ip>UQhh~iwUAE zW{D=Wv7>DwL*G|_v0)m#*^d`yO{lc=fICC}(AMa~8q zTn*u%YoA-@oa=iPF$rk)$#SF3F-69}@*XSR;J12MPUS!;Px1msgMZAOF;!;Cl0z5GU2B5-edgM$DMv#GEJ}1cF3T>D^;) z4x-CBqaw5+(C18pPG{;`+%(lvptJv5azIO}jr2f}K$A12PWz=2@&%vLYnACXF`^}L z82eiA7URTgzVasD`6s?zKJbMfV0(AR zU;f^|YJcdD{8#*}*Wd66)iQeXVD>}%KH+uAkw7oCfus_qAiVkR9!f1!YcZ7SD+u_& z#7rpSUpHvu4G*JUqlMb>9izZrC7D)ALPs$S?9!>Fe zp)%8@{v4S$1fXP-hULUGut8&C8{pwxHdBtM1@j3-fl_3Kfb04hMZkJ^?Sp;oQruf{y<~(T;6R< zg|~1*bw7_TmovY%*>X*u*178IFPVt&LqGh3m@0V~Di5GCEKYpbOSyGTN?8`jdTch? zWW`u|&8Jwg*-zm%;Pb|U*&>DIylA(hlzqAJ+8JDq6BWW~pWs}2&dW4(b_!*emr3mG zY~z|``Nwa3ROlaHh%E_!wjj(`IG!xiUF0Z-kK3GBtI*aC;gEd;*`^)av7nZDmPU$Y zZn8_K45z3!A%Y3L8AYAyc_@i4IGy_W%`?*ehOsEBg(ylYzJWx|?vYL%SL@oDHvfM8 zum=mQqVT*QX^mPq3t0-038WDSR@jf(N$`{ZU3_sI4n7UzaEFG#s%d!o{EirucW?K+ zwsxPA^SK8?j1+vjTk49J`X22zymArQ4v9qKIyQ6_I1-|<&VeWWo{O#{C7U;r;gGq$ zUb7oUnh*(5kX+C%&<}~rm3!FK3av)=weSw=9ysr$9{n>?GOoH6CMd1gl>Qg_IJnJ=!p)%-9#Az+0nniBIXCWg|-=T z{m^p}Bkg)chj7zp)@{RUS8JX=-%>(D8hc*9T#-!J_rmqMFcN80iP1R*riG2VByLDX zIe2k@)xlj4my0;2P)46fVi2!5tS-51Be~uKijjvs&+iIlykQ-@tDOq0IIz(bZG=N? z>8?5`4qANvspn|+4J7rq1QRUnXhX}RDC~2`g(UClYh)E#hOtmmL1REj#58c5FXpAZ zPnnfEoS+fUNbjr=4F+*|+@=eR6&@oL?r!(qoOpUeJ|u=>Ty{zcRwa90fb25#1^Pat zA=0h`R;YkESgiy5=f>_+H?ZE2FCQb9myEju-J?g`?hd@Mx?p{I%_|4t`TjX!?AfMF z*ETg>AR*Q7FN|ntC?V1eJ7i4s+n%=#QfSH6^Z5Ux?aiYl%dYay-#+Kudn4ivnUz_U zHAq#G&IlKSZ=iZ2TnI)^r(pxK6NmVc3yAk)sIcM+v zeZOySL7xM)cT^hDIMA++5P?W$h^hD*1Bdgo{MXe(beb5F<*g4n(E_b1M>&%vaX3H2 z*+xiN>0;zKDJL<~m)3c&#vSOS7^Lq8_3c-6~4VTJjnhcEHn-}c@5gKzvnY8o!g zE=pW-c|i*T7kzP=f<;#5}Y4LYvnCbYqY7uX+|`c ziCTUXiT9~OT6VOWH`Lu$oa(No^HDtdAKe(BHz0<${gJvO9MqkXT(;z*?63lusJ%{v zx(XcZ9de}C$l02zSt0mf1zQJ9DR3+WT$m=0f=QjXLHQO>xeXr7_)_R*X9EHR zVzgLU=ZZ22*sBgmz%UZ1bE5W+*e{7W7Wr{SA6ruDP%^<)R;a65A&s6oz<+FC-fhIP znN*~)B}aRbOsi#o7s$bd^S9IXYcwas8J+&{;5L@)6{5Zrl?7Z2I9E+F?L{?1^MGo! z7*m8UX6mY9)AkU=1%rX9*q~$l%=j`<&lybv*;fPtXtE)UbJk2VX%McA?`}W)1TI~E znB_jZxH{3T=O+>f3aBD^GrskWa2?YvfNd>_WINJO!Pk@G_d1H-*JW$1fA> z;7q1HE*&u?<4P5^Y9)}<0Gb&l;(lRojICES`|DT&kJRz8p+?DPA7%TKIzZXYE_X7i z`Ml^P>EI?20rmq8$>wj?$QF%v&vdY*2QZXzG60R9y2 zRZgR8Z#Heek)|?17EfQwR^05gr^!|{>DKxnBa2|`X9z+F-f=W^G`=Q?C%Vj#q3ewd zx{Q$kBsK4BR^g=Ysk{)Qbrih_U5t3=jbxotnLG$xv=?QQXS-vSGg^Ao%%Z8OePGZ5 zjh5RSGc?}O%MD^=4-rHgPN4?_toXGc`&?%^~}7Z z?acEX6L?9i2I0(NP6&=ZW!hw3Z!)BE@NcRBIkAqgs61^F8gUH6z$|n;`L4Tp?*sR7 zl6q~Hgi$}$0`l!TC2{Yk(4|~C6d%@X)*RB&Hp1vCjQC!J6voFph^jUE( z)2mGY*4d`U5%3w-IrIKUdKxdR5)1_d7n4oE_PponI&pouB8lPtToyEEn&79!co1Rk z1HC%VF7~KPfwUBuSGFl#zP4s>ucloMESic{4|S!S-LDz4V{cwrbX-=d#<7Z#nnYcB zo2DrYF>#!Qy&CEi8GJ<&VIk1@8LQwpQ_VR4)TiQazY{q+;lX#k)2tC{qDU7!tqAiX zlU=)-!zzQf__eT#WbGI_;q2j@2aa22&XLs-VoP>V33Ms4$3W+UHDR&=Yr33E6ko!V zk-jH#2q`fPN-Y%MmHGPnL}3J^ayT7*>2_%75oY@pZD(?I3FY_ajL(k4St2-CCQlOk zd8du2WJu7) zM9P_Ah^+g>5F?kh<=XYD9B1>bCzL^rD&HxzD)cxS2ZxoxUAmO%G_y|jzDLa*=gi4+ z;Dh-ge(!gGU!M1Z=YGP9^Vk2tkMQbme5F<_xOIN}rhsv-WE0CyZtUq`QQgvGZBH<_ z<#vomYw^_k`&?k3_kG~Qyy&xEq<`@<{~|yB?|xh^+;QRK_L}DNIrrW7dvf>PPte1? zdy0?v(YC`IW(^bR`Ek^^I0@yCFmuK~s3qX&a8eV(0GbNF*X%fzcMNKhyjXTehc9Gk zh`G0czUlOAKP{0qdIQo{9@lk`lc2_XlaoqSB)isNi=|08MPE!HrB-k)RZ0b5SddLWY zzF(K()%N_D5+}=(NjjWV@jjFsZUfh56`Y%Mx$8NoOjSr$i*?PEtZFNcz4N53RH2#x zX4Bct`7)fbU^tf|Srq32>NbU09$~%ZXCAl9!V2;Q5U0d!_av8Tzsb(+wc`XQ0kQtt zmQbV=ve~BpSuJ6GFYohD{^jfBlMkM!e|+*zdG)s)bM?wKUj3D?)USHgSIhtPh98t~ z{TqKr_isBhoxyhDW-=9U6Wv~Qvh7Kk_>bB18-jMWiSneT$YoltQ)5cUY;6|v% z28!iLAzWW1Cg!fvEJza z-<7oQu6s9_<3<15^7$L~GQFVR82Cb2bfwwM!Cz@nYa&(-vC#gL-;bf7@t9P6Q{Zt8L!B@o2P1Z&B_+&gMlp{`8Oif6Wx zjqlEhy(2?5Us1xO{(VX&MK1Z~q46Yf7EPCgp}>TP2yF-bkeJQDIi+27bZt)+ym^|+ zba7ExUYA9DWR8Hj2+KTBJB!AFQ03+B2;vl5tSS%LYa`pfpEb68QQYlWf zEX=p;3;Ru`N9ZLmXhrW2a0myXV&2r$&-rBhr#uU{KPQDi9|K5jkvwGqcMLQxQ|Ck@ zpgD2ZETh^Y@fhikIXbz{OnXvN`ciN;zDDYr!`X~f*GP_#2U7NQ>ZqM(pN`-hF-IQJ zLmGOzwqqd)AH+^NV~mhk7j))5DW(!hpBVsPF&B4V^xmSae~nMfI81Di+%Q-pI|mE(2HXzDw=D@kMNvN z|Faw)9e=`#^ACUMUT%{o>#M)QyvHv9A_O^F$`<=Jz&;QJS__)5lCqD5me*WivL5`O9-gJ|^ zaleyCnFK$!uUepXHQrUM72`2$0xCq?k|1J})5=>kr--L6jp-WOzZRik=4Q-YP2)T> zx0$qzzCI}ofK9xPOdwl%9M?XtYMOe+UOlI&8fNvJS=}&i&d^kILNGv6?H!E^AfCp1 znlf=0=kU7Q`}e00qOz@4;3iY{(@3I^1;z6yUVPow6XZv|t9h$j9}n-&dz^E2Uwlnf zHF#ekE)Zkf>L19o8e8#U&0pDuQcarPQE}&r?0q~J=dIuGrUHI{abQ#368)v+6q{2v z{{-16=JYr!nyLmh0If}^B3DRnezvRomga>$V^Xw=T%!$c_%b&3!q`LXMi3q7E?grJJ9Cs;`bYGfG zi2Z+S{)BRGOYvM@lXHUeZhBt5lz+NV)Ex#493|;yfpXL2ct`Of>=LOp+xsI-llCDe z2J$3TW>goI>Hb8jS35hWk%^)k|8=6k?sj=tH`J$YB#w(cPnGvn-WLzNF!P?ud1@Iy zJrww(f$PTCDaE^ODvr^YUF;T9KzM=5jgxedDdKJ9FUaWQX0z6hij2`VF#dCd_)RRS zG6ueJn0`Y@=T-z)QGBTEN~hh85ohBPC#Q7TV~=~P^Rx)Pbps0=c`uv1^+#_OY|FW~ zRq#7g>b$9}Cy@GP{q}g&7Aa6GXHw}~BZBsS?fntrBnkpmb<7zjMRJo<8sjHeRZHq`| zP-Q652{UmV_dR{z;j2KK5+QhU8c43P?{`A2LRI8mvoIrOW#(qo&LcXojy6>nXC1ht znf;kTd@L#rX5v_PiM?6P(Rz)Sh{KUI(x`ckhZK3_>Jh*EUw)N1^yZycA*V=}t$XM( zUsmvrsF5L>yuNX;?u4eIc&flRw!t5*JLYvomz2iY_4pv9AzJ6xtNHP>upflVIX?K2 z2aCa9$PSdMOgbVG?J-@iTh1(IoUE2KO^uRi*GkdFs~Lx6F10OZs|Lj;?2<^`|c&T zYm!7BX%mZ(nfsnuT#~h>L#Z8HOEo^O(BXtLjptIdoYguH#mm$)O9M;i31o)l6+&H8 zhZ?Os$4hWwV5TN9@9RAzu8^2G%)QBn=7E!#jLMFirUXOG3_06|;!IWHoPqKX7>0}p zRJErG&?cpxRkZ8K;au1YisnF{Dths>Z6bK#{J8~jh|Cuavomv6*H54W4)z%aWrni1 z7Z_AXT_nwbRnPq=f5i2pN4R+JhghzAqA%r9xD2dHV#1)xiCCWP$q4KbA(C*eBC2rDqTzhi5J;Sm zY>mhk*WyKa&$SQo_rB}3@{Ql|EuXNd`Om-kKk~(2@@ou^*6+*X#VE zzx)M!`Io(%pZcl)z~N0|@Z{7{#oZu-e(YZZLQ|%04lFWLppr;dj~9rAiGX-&pIKB5 zv6QlHe?rV!QW@sNJb(@i4Aeye9h{m~W5>qrHj}x>`3{BnrbFm(IM>+u zMofhvc|fG5@^b^ZjT08ta=%w43Fk2?FA8qdxqz02f{5)o##6{8va2*G%HfZ~YX~k( z_rw*rTvcM?l2i;DS-8Sc$q5miKxd7&qB5(Jq$U#(_00Aby7jj!%yHmaq9{1-SP^_wxL^pU@)d8poWs1Eah-^<%V;UPqMwcH|GO0{FH)C&|h!mR~`aFH6 z#98FtC#0N#o9x<(P|E8_qrC=)ikil_UTD&GB^NVya0a78Ew!ogQua6s0J{9NLVfRz%u;;ndMcuAG}N3f!-}e+ zOsY4pm#Kk~$6~x-(^K9^y~iT{bfi{Rwmj(@x$-7jYg;TXJLtSAB5nM>Qta#L%sAPl zrEe@cML9bbu*JQQ$4vMUdZ$WNH$?48DHVzNcG0Le63ykmxe07F2X>?HrjE?c_+57^ zW4XC_OSeqxDl)|f?r<1n%W|eSQ2@y4>)*x1P02|xsU@85+RSCk%YM`J(ek>sL|7XO zKzXgY1%8t)$%u&T+*q9&{hQNTXf@F##Ygkw4jK9!2?$*pSX4Dxl|5gRs(~yH6UDYTZIc2;g%aQQs51NpprEcr|Z!gA`eyO-!;lX3jIDL>097M}%vu7B^JPn~GUe7fQTi z7*dH*u{Uc`L&(ZOT~WEfh53vaAqcFaZES5ypcMzfv!tg@9aRvTW?_=}dd|sm&0H!-gEC+4v`ii3_VK`&esiX zi38EBG+t=4EpSFow=U!y$1+CFcux-;4T-b9W*vKiz;ejkad1GtZnk}?tjOexN44p|T+XPK8J_3>v2Kww+(@;yMPHS)wW9CZA;5}99@I&TWy>Ff= zHA!Qi6(=(g*smPxRyhnkS%lgAka`$6>05%BRK1c&6k$e3ZuaR1+W=#-4{>nTIp5Gw zGnKQgAZCmFiAf2*d{%?dCySkHYMU-Cv-!Z+ZK9c({URr`f;7RA)}3w04%R0v2YWNb zS=@{c84ZqQm#L0VV2FgaBgGcy9BtcyTaXi$v8xfBFE$997=#q@-ef|vSxuNPSoKP7 zXVm?gqi#vtC49_udEh8#PEzJ-j$BWfUX_7NR}M4-m~upfUcqbR{A{0FDo?;-5-rJ~ zHF|rO`SA4*@Uw6IP5G?P{j5(|asGq<_=oxWZ+exUon0uNNWr^ml7ZKlB>}WpT-cHx zjv1^*uh%Y;5!yZytlqSOM&zc@QMP37x+O3cf!p?<%n$$bf5qE>>s|UAZ~rCvctx|l z@r^$uZ~Cc!t~uW`O{{dMZ>=|RCzmPzpAa0WKW=wMHBpDdX1;kg( z!wggjzQU1+*`r!nk>@-yMQU;MrAYUpJJti)b=EzuLaVN3gsNdSo8etU$|lTWV6 zy3wUC)FD`NJ_MXple~R=UPgbSlmZ?S>V#Z4Ig~w8#G|Cb4oH*Y#Hi+OC$`R&1Cv>J zOCw!M+rR~ut0f=2cWD!+uA2&u5fayg8S`pp0|+&_@^W=V%FyM0Dym{mW1EC3G*b*) z&MQ=7QOIRtY^R_9m?iEcn}p?9{Kw^16zJEsadmt0G!{llBbo=q)ufW&RJ(?hB2El0 z>%1_l8&>U#d6=2BIVnTTh&PB?6BeBUM9VNoB93J$mZ=;yMo zQ`Q-otPF~yam7Yc)RNXmw#k3Yx<&dfo^LJk_u@4lab+6?tP$Xo>LjO?k{>^T>PkGD zVJ(V}djv3U5z&ixr%0AJ#YXUfCe$>cV$^(Wk`uN7Z>33MFw4^Xl9Gbs4ASSssOYZj zXF5fzN%89&sIlle@D5xqDv&W&d6cYAMZZjZfmEIYEsZb5?I1O`*Cd&gA~LlNM_>Ec zTJ+@{kpb`hP9d&3!s>3A>%8$wpQgBu-y^9u^{MvbU zDQ2RwGbv?6Giexh<@Q;3N#tYkczU=kTg)@P39&>BS}9xau`wvRl^sVVyDgOy{8J#jj}ot;R~9X7!D zJdW{ex}pD%&9~A=CEUL}X0M(Tk`hL+k}3-kdJ@;$V-Bi@q=2i@)R9$L4n6z6qRWXI zrS^f7*mFR@IVES9`loSdfi||x!rT@z9gKphf#rJ55C;zT7igZ*<;Y&B zIUZVS?-)`t*|sXHRd4ZZf<=<4ErLOmHfJl$i;N>D+h~JRn&1H;MP;^_F%0HsiYelq z<763G%z_mLl_CkQFFWqoTTG{%sMeflTurDl$0?O4mPkq!`?dM;PkMvKT<-_S1M`_D z*=lB!#h9prFYvs~Fqk}lyp^>Jv~g7)ev>z)A>%5~*@FhKA~*y=lNha7Eq<<_Zn`6N z330;V!aVrbuJxQfsG(Nc!MeOB>wypg>Kyx3P4Lk65zPUoih5Ha)MD+)Bv5B%nG%%~ zV)2^|Sq%*1jU-#UVll{SUU;lCTVb&?A_f|~ZB}v8^|;Cra2ziOD&H|%G_02$w>2~N z8evuk@UZWNCp`C0!82|JfunmbB2_~eBFpIMx9G)lEudi8kuQN zpJwEBg&R6z>hL+T=W0?+93$)}hsy&qA6O1b?;JhCULF{7MQ{U>3{x~zGo#~TY6#~Y ztE}uf^N?!so<kdNY5H&zx7fmJNie17mI0c~O zu*OVE*2EG!a0LgA&qVdK*|Qpy{kgFpG(#ViSzV%Yh6Hs@Gp|@(>kBl_y0LxCxH88O zg+2??4h%W5Ustpp3~?w>M+a3!t^-aJIt-`}mKu=;I}BpcDb2jbJ4e^AS@#18r6qHc zGxzm9$7M3ML|Lm9=~|vQ9etxHa}hK+o>Y4lt|1ahYSDftS-JesF}FVbDg69<-zMiT z-1-SC&i~%Gey9F#-}_zMGQVSjHic7D^M1SKTe`+hgy1T!Ut4m_bzshCKmX70bOJ;TrcLMc(XeM zEU0D1Y!=Kuc2ftsespNgWPa3}In!?(wK%B7EF2+J6BXM#SBj0TBdlyB+$kqCDnplS zkF1=jUxFuindIz7zcxR%$*-+Q&|Id=x(PkDg3V3Gvo=L4r&J=F2(0l)=Nz>Nbv2`@ z4+y~%yN&&Aj5qyXd8a;Ed>`BpXBz4Ha4 zxq0lIRivzrdsf#w)?@|}RS8}^bI07|JT0`Rej5q)5H|y)(_&WKH*M5fr{K>oi zh&TPKH}Tqk@qP08*S%i<#BpR;JTQn*QZe(bS!lXrY9Ufotf)t5p$l?pEv z5ZE}$b!D0WjX-k0J9((l;-entdmARgIL~!r4TG^);GM%4D}@8_Qkoq$XtLiiPWt@x z+>a5d-U&G=Sxg<+gbG*e5K;>1)b*PvFx^bzC;7JmMbRm4Gi@Fx=fRm}H9MVT*Oe~~ z>+;!8<$J3UB3>lu6LcQ?T!qrsnw}4GS^LKfd3Gr6BZMuZMMAtKO)okw5o-o|XOr_- znk3dtaXSeUqa|hp=NVs4OWjtcvxUE~Dc*EvmDHOAhfQ1&HU-=bj-XhcAGc|HGNJdm6YqxE(At|NMzT(8>_>C_HX!J)O% zQ~@uJMXjvY3EwzUGO?maP4K2@m^|TqBzc3fppcSnXl?~mog_3*e|}Y%0VO$@|rd) zJ{+5Zq!sq6jK+alT3iKt*>iCF1+KmSB3iAOS2caIes7xtUL$0o&T9sln~T^7Tj0ep z!ww}LYaR^i6J+S9x;1@sh8UHqQew7gPF7DqEQUuCSsQAX*_(T6QLgn?c+6_Q1qiYj zliN||GY-WjnzqNaQh&!8DeaWrSrInVhj@YGq&%r^`=;3RO8I#xJ7@X#Ogl9zuY zuX)4k1o?#3=DhxOKg?L1+r}O6uHHIa%N99wLptr(16Sj{yzrAhk8k+fU&QDB`4`Kd zdExUu>hF5^p-a5=Ex*Pu|H7~6zkJh=0Oz@N|8UbE7Vx@Vo^K8ad;2pkA7A8!&-;9R z`yaklym$ZIul@5s|MPg^3;svm`OZIP)>Ko$<)#C--K$UzB)${{{&AHnsA>GI%;wb%Mc}TSefxMRH$Zv>Q(hD8r#^!VcbsRj*kiqJS+^YzedHm2{~hn+!wHD6mm#%Q} zp^M!6!3XsB-|+|h?ytR*qlb^V^C=CpvkSa$jFI6yk`r+(_+q3N#Y=^1XKW1%1t#ZI zVy{5NY;?oWF%JJXHJMDL{ow5UGJ3y3iCyB##jAY!i$9G&_xYc}GoJGdIeY#V1h{zd zGFLBO<&8i3ul0fZK2jv8g&HnGRn@%x*M5&*{iR>y?Z5pFd9Hk_zKeJB8!!D${hn`s z4d3^J-zT4S;j{Jjb9d6yQP*XfT)ft7<&wK;J(np%d-`{3#c?i^X2E3W<2pEcmd8wL zirh9A&-2(2m@3MN7;e&Pi$0!oJXVCJOjMIHXDP`p8x3v&C{jR-rE2k+N<8ysMqSmM zJv_&=pYaSHdf)*bdGG_9oNZOSw`M@p4fi3F%f!<@^Ql-1g7}>rc_~G>FCf3*+{9?8 z#hN;zR;2hXh@3Cpct18uHBYkj>@2y+<&x!IeC5U@*it%;1-aA7Tb@dTqw;}yRr4vI z{5<~f-S47JJuydOmML0nj7=OFlc7Kk`F6FaTVrr79`YT@ z`L@zqxyd@cDbAhS@}ZBvI^RS=H6{&|0(mMjRd2{#-v9}>2<5VsSw2DJWLF8ky+w?Z z|80h#8{w;R1NBaH8cknC1^ z!S?rzDuD4dP4DN9LSiFV2t&zyJ&LV$yK>J-PSnA15)-w9PBZ<`k}}M^N0T}C#0Olm zm<4f`hpt(vy|7lO94w@vk3A73ISTjQ+x+7l`tSjL`tSa2{G zE*@Qh%D@*%O=cV%q%5qvgb-PTU}VzXyt;#?(#f+$-+M@dfz$d7aj+?EjVA}C@g}!M zh3l(CXdHbX8KP&usZduk_o{$uCd}Ow!Df;qTX@#3$=q_X!hO9OsI0Zg`FX>&qZVH| z^BFZY&%gBoANcTxj7fm9@D(Y;Il-px2(3a@Rp>C#d&^_gn253F_Jf-F+_PxxAZ1Zw zP}m0 z0t~8L&sw0C3_~J?#D1fQMpXLJA+J!Mxpx2iKoU6y90Q)PB{wwH$PtGm@$Bc;Abw7$ zbLJ>{yd-?>sU*?Ih{Q~G4KB7+ZXmNL-m01=uj$<$s&>%zSSmnO@;O5Rxp?OoVwzwr zp&SfiR*P~eg}X@s&4#d7!>V`m$qr!NIhuK(?*>u^mjKCDI7rEd&WgmyA}H6FOCp|m zZM6R?Cx#qZ=d#&0b6nTrVvkG0YCTZT_Xw3m4*6^zLv`LV*vqwxmVm1~pzWIVx?>n3 z*Yd!Xn7B6dw9e5+n~L=!w5qJjRK$thkrkOn9QVu`i-XAnNi)Z(=V~8$=+YnaPk;6o zuIKu-BOd<9!`%Pj zhk4(-@7H(y_Mh<1U;lm1+@jPAN0LAr8dA!Lq@r9&B~8H&KD7%}u42EL5v1ZgpY+(| z`QGEzo{ty@>QEyhEZm+Q%qu~})6~B3SRkiFceUq}o^ua>`OE);yzs>@;0aH<^YPpw z0l56g6@Kv--pWt@Ow3NeS>=ynW!l9dXGg|P-(^T>Ok-9BX{0?f%CVXoH5*$3mzyXvyaFwc?~Qz;8}gtOFM>9urydwJ&=q|MN?}Q2vL{{Y+-_ zkFlBbtWUaOzVw0jevse#^|$l0Kk-X?<>3?VeBx~!9GvCD58PW?f|W7rQ>Gtmm9y7c z0C}X6jXg{gSUdmPwBJjG;s{PQ8TM)^98#JPSMIycOJDsJ{LOFs8oBd{cRcFlzvVx^ zmHR*VVQ89ZA}u%~k%PMq`IfKyR{ff>*9l@GHOkD}4He7ihPiI8*H* zIS|Abf45DP#{tl2K6Pw`CzOIN>E<~bDez3TiBk0Gc51U(FtTpuoQ{p}JiAS6HIz6( zvowrsh2n)Dc@Gn`OE=T=$!4WU-V{y21-z8u0LsB)!HYiaPxFqqMXp@@2ssbMcA})x zbl7;e$4RFZrW6R?sNxi7?@wQvQeH3sT)AE^fkv#*a^v*7d=@j=;Ad*T!xdGGinIN! zL{()KvfDIDMyrP(rRNh|aT5UYVYVzGN1F}&*JTa#|WI-c%{2 zkP6O~nBx*BxJ?EqFwrcd-#t%-X4cI#eUy*8ZMpjvXzzxk%q`~Uc82+O3-vT<9Mv62 z+|606x)b9nTk+o8boH^Azv1_sn%awQtTn2SVtZ0frG3cGD0?RcR8G;|@}_;~ZU}?l z6zgg0#cog1Kl+Y%GpL`tQA(e6tFkVQ?DFC#LBgr#1=}ew+t)faWya=|f_%egzan=z zQN>9~P90XzRXWX$kORtetzhW)0 zj4dt%E}XxMlj|pF%*=x0^74qi**=vETwWgmi9RQ;TyJqA?9Us-LrkVXsJtUOhm*|E zM;7yjw(D`OBBh~-Kux}6WD4`!E>C)zML?Wmb<#1L&x^E%BJDS-xzmKIE$WcyT4isq zV#q?@C%Sdd;bFt^NoPdQV1L(nVcl9oqi+YIGv2_EVQ;?R?1fu-_<;xLhJhg9%9FOv z&?r>FTeo*s)3*b&Amkx4J7`$12kyLO#;i8g13*831YzAK&Mb^kFt6_E^MMb!5dPD~g&8wt<5WA4X0sp!XEgUkfRn*-e9}=hRauDb z0P9gwN;E+TQ#W47DbYBS;`Rxa>xk3DEk47^ie8~tsOlMMwW4ZQXpRioGf0K2UFr6x zvY1lnbqsk*mN;p;x6>yH1%OFvJcN>QE8eHNKv4 z_hQb40DIzCro_q6arw%Ux;w`kKKQfp#3$eL2`$b)^u7=Bl&61^-Zi`3j!Z64xnP;{ z*n7JK;+CKJ^2srGa3}xy-TzLW{VC7YKk#{_JPI;Bj5=<%?H%>(BoxKlxAo zt-kXQ-oxU2Lr)^5WY8;3qx%;$vDciXae=e@hs<2X1Mj`WAL?&^%-8=wlkON$vu?HJCoX` z9JzXVO`AMF{)X4cOJDY-#drMpzJB6g{yV<@<^Q|Ne8pm~#93-8#mUwIu*htswEvr` zrYXg|cV^eC{enGzz%6;f=YQq@#Ap7+7s;Rcv!DF9uIuRf3IG00zsNuO?th^VKlm`W zK4DJk;ML#s)%>k*d--E}=5IUmB${S#0xOEML>Dn%gsG@<=JKTzS~5TUL*FG|^|HUp zd_McQKQn*$&i~5KyzwnsrJDcecYhmFOO6A5?Cn`et=SBmD@D0M5VbUrlUlBPOc6Ad zscwmQ+k%eevY&R#j@xg&z(4(|*UB@W^Nj!I_5JlPewqI6Z@!IsJ}U`3x!iwyu9GJE zqc!hay>I$E_wWSH-f@m0_e71%tHoqp7z=XIT#8=DkcJ|m-)MGY;fW-*qE*X89Bki9 zYLo3jjV+OioH~z%&_qMe>?T#4;>bBEXSvdx$#%}sq?1}WjhWZt%+gUlf4)SEjlSV& z4p^dgy%P>*3-$ya2b0vJbw@V1D#MM5nd00e+aFZ|q9co7({Paj|Lc<_AB@k=2=Uui42@}fH^9yHM*sQt z|6sR>zhT6lw~G78g08!DZe~YKp{~f{ zPmxwtbT<>eywT(RcM99|fKC1zJJ56;Z4Hl-oqqFw_fh`y_D$P~ecM(s>=y1j?~mKH zkLfnAPc}9I7l4I0x)cpIqDoFC`3f@nYKjjdCtS|Vopttb)C;KQ#GF|TcF3AH3nD^{k**s|KAx2!N4nl5H|svpCnfd+ zC#}(<+Z;KZ%{goq1eCd}NC_@oewZuEt5|ZgXTM(HRhc=sb?=b<*;(#7yg)rmoY|Y% z_o?MNSH7VeqUBu=))DIZ9ud1fDe|T&cw!RjrXj~n*Cl2LHC3q5tQKFA&9hupFvPNe z#8|q8_Vc_qnA&>X)2=#F+jF?zuw3@cDn~sJbZum|Xe_!bMY?uCLRA(-VJ=GQTCP9v zAXSQZEF!HbjhC8e@iXQqAf&|Ey&0iMscR>kS(wMV@%BCQkl0%Q$)2TkpYaY-vN$=H zB1toS9+2D;Q%Ba0xsOz-C&<9eNBU&``rwTCIW{aTZY5Dm!f9XNJ;`!OU7}mB$lVHU zJM&=2CHdqLhyy7Hx>$OhDKii(K>+hYhT0aVr5Q$?Ln7kQpbJxPUD9g+t6 zA(6WduZcls^i&K9x@^qRjyY|Fn1!k~yFkoFAaBGD%fXHD4;u+{@b(#wLz))(lR;=x zqH5|Xk94kyt}`C)ekHh^7}~)W<`nS`+QEwR=#0X?H?N7SHL01=Mr<+?dt4m0=*niN zKowsaDFz|QR78jg7ww;+?~s!v@#KW7-I^2MdXYJ2f_!@DZs;as8;1PfNuY4i@Y51K_cySK!RZoAh-W6^$pR)@VpIDk5 z4a!x_ma_nAg^MR2;%~j`Z*pIH*T=0mw~3YGJ74p6SL+Af|Dlh|HT;9ue7k&L^nIO%i`qJ3r724e4eN9@nJoCv<;Z48! zqw;Ov|BauJ;vC?%+s^ZUeBa-fM;^H%FZtrn=jzb|)GpvcO%vvbo1>wk4vxKM&femH z#eARO0uNqZ^5CT<7q497;+12rU0-tX@`?v9UE`rkOCG*_jf+=~cQP;=zl5 z#JBz3SCP_tU=(95b03ZNKL_t*h%`caqe)mtvE5Gvr|z+vIvkV#ca&6nN))3D#MtwZOYi6Je)rcC;|JyIUiGq1 zNO2DEDbIa6fB$u_mcR6}&z3wm)~husM|`#+#z{Y*s&xI5zF)B()+Duo!1V`DIC*Hz z(L-z253f0Sc+Kjv5|2IWiv#VYf%U@!-PHjnj^SF*=luB>^9%2M^T$-2+g6=e$9^I% z=OPaui()}ooSpNh?)+4qefzU{+PSAvpEsL{l9-2iIco|*9CPQ{_nti$Xv9$gb7$un z4>)xt4sK+L*g3Tl!RJiSJjK3&T45@iX`+-|cr6opnP#>nWK(=8&fQeR#UiDSF)?T= z`)nbPI7=N`)D82dVdgz`s0cm~ifY7_n76SQapDQ847q1Lv<$;wph_T#80%(&%}Ifj zZqlViM_;@R28L2hH{LT36}6YCK@gmn;zo?2G?_k1jPn@XHco(ZX#i}p$~UC+tTwpP z0*mxpoC`}UbrVTmN(7^n+;`NhbN64mpLJZ~CLo+ey*cM5wu_tM4Jm(2RxicwAcZYb zC=nYFSyrLT#yw{t*_R}N!(~!U$I|`dAg+982 z?j}?AT}y%P)vzD-g~X} z^dHaK=bT$9G&c!DZ$3p;-P}57pMCeRe$Q`eV{h_Jjm%-G0!=arVdPC3vtQE5vv|A@?01oXMOFdoA)K2NOV^md4n+aXtSvX#i6n5D$+OUab z8zU#@3o7r~swV8ENRBOwt|y>$DH2>{)_0sg-LMCK?zv!NgZG`dne(RSvD?jyyf-uU zV^VGzjmC7bw@ylL@-@3LdVCNZp1u7@sNv*^mgz>#Vn5-WW4B!}9tT3@h|xmp77Lry z?(Ozu@2UD!rc#a+d%9kjjI9E#Q%EsV1;GaccCHrYdvju+Okq(u+MLTkMseN|e72Az zaWoF=n)miFsjD%Fb-o*;;)t;a?<~olMBNC^CAvOCkkU1n@Lt(!9Bp5wqd-o=iKI+L zsXfG0YHOUb=n|yjNvefWPW0JvV&B53)WNj26W`-;Ia=C(pUhI>1dUoA*cIxmp%MC;A1U z8j<=Q>8;U0``#wxG1Et-+i!_o&s>$1GB>1%s<6n)ZcL@b(5iF1Dii0(zGkDY*{M9| zhQP7P7TUonDMjA-zW4L>Kl3vAif{fJJ{8-4`|+RU20p~7I!+Wv<7&>;$j8JjnTopj z1V!OP`yb%?gr_~>leSL}dhi3--+zZZ?3yR)`)_y;<7vH$C0U#PYMGW#>q~js z6Tepf_FaE`m!-RtJZa;3H^)DX4tN-HH`ggru?sMfmKV|34{U3BC|MRW?NnY~ZKdry}%GWSDHU@f>p8Z&Qrp4@5{d~@| zp8Hi?^T4ZRNV=9{svdg0*FCx)yYb_^@YyfXFaOpr;s<`}dk%MBD&5*zo}(*==wMZm zWhTbB8ZoV>mcgQ_B%c?*wi+1q1qP*M4O9VQnv6>-U@H%yrG-&lv7WZBwNClskxgp> zVtK75d1)w-rRu}2k)7f~!3)-C;+(gN;gUA&vco%1UDbFouea4~6;(+AFS&H91E`0Q zv-MzKR01hQ+bL(E!2@%zLnBVsW#z90yLl=Zg7i!^p=!elDQxCo$@9h2y!3?YdVrcP zrP^62fto_hnW_v-43q~t)+(*U0s?bRYxQVS3YRzI z^BRa1))M>mAl)oWPzbtd45Ir?G`?h^hmsAIm6Twp zC>s>gXMeFXMD?F2>vT;vFaHFazOG~#j@$YmMAqyHt8Y|OI}eie0|#?;7m~Gqqid=1 z!4pVCR6Mcxo%d3tc7EwwMKj4cVz$$45hD{}83uF<8C_z?9!Dpoz*>o`_1#hYE zwXYe68gW7!J5o^|1ROy#jle(NatoIn*&@3@iXBI%TUM8qBm0FtJ)Pz>cv6~~RbUj1 z(e#cS!G(lsP3$ASve2&(D*C>pb&D`A`?uI%Fqzb}J;bgpGAE%HQ#~xY-l{Vij|9Pc zPus>KV^LC{@lMHorXd)6k1Zm{O@-T!M@@qE&L4P|T!G$(VMikt}p7sQ2YY&>&8U>ZrXv+&qcwJvrKgKB_0E zvCh7*<72T2c37L_k5Wr?HIh4APRo!Vkp!_P`%$~KurpP>ql^F|euYRpD8qRu_jAS~L*>6BP>nZ=C@e(7z0A@{lFzMql> z>h6h?eBXCHSC5X5(~B?`^I`6aV@rioV#jn#VJ7hY{SWZ^KX|=-{^LLYlfG}0$(X;n z{*UB-mp@W(yye5xO*mt&ym~M;b;aLZ|3-fM)xXbEpZ>(V{NCUEg6GQDJoX8CeCujs zgq>2kfJ+_0!_H_zO3LmyF|H5C^1bR8UMXMnoUiz#9=pdr=?nPB({GWlf9mt~t#A4p zwvH(=3ER^nsAd-J3A{MGH%r&yu!CBbgC_d8Fzbybf~%Q^hWV+;OMl}B<$<5`06rbt zzj@l@c*on`B%go(uhPwn%kxGZeU3|=qbAtiIl>d3^yoYMx-WUrPjlh%OL_16UM)v< zwm)h6|FvKD73}Zt$@9MBg?jGJk=24LujHs%t$8Gu=i`SzzR&Nx=4a#yPx<6tmlt1h zoHzabtK*Ovj({^Tmh2_y72J$c2|4 zK8;e?%OP2W#_Vd(%#w-XjsVQaw8p%}7Dm!JF8?Xv<%WGd#O=YlSgwm>d zZpe5Udg5~#*5pznw$`=14y<*(j$9Ae4wkrb2nf{m=eFJ+c&;(j2fx?eQ_H|(u#cSi zM3MuHy{xgi>#}uj4?5R_>tr=R5LxSnmt{i0vJu6?;0|`AueJDbBk{sq0zs z+>ST@1X6-KoX@$gVqXt*(*r&N`>f*aJk*qyLl=>=?Ei8&aQ5&ymr**DMNUPkYtzd% zXPPR|VcunNj{U{F)R#aM$JG0BVc2BII(t1)lrC$j(Mr_Ld?d=yTR8NT+ElYm(QZa1 z9HS~Dlgc?ZYN2vUEn*C0O!Tq%Y_rlh^UY^3^obEbO&a^QRQzWNELWo^vvFMrg9WnK&#-;xR9F-UY;v})* zJ$r4-v5hSraQrx-8gqO!rte!)H$zp~7;SLgbO-0GThx2UqE%ANv~4CQn9VvylQATd z>L@~riI~d5BAJY}>mxY}K10)Z`d*ogASD~%EHsk(Xf;+Ln71L>&uGz_?9Ydl6r=Al zyNjNzk*X0!lfYzTwRNM7icte~EmW0|qXmlDQOLwL;(}dS-kB0(JTXuB*0y77tEQ=i z>C`hG!}bJ5p-4bdxtV1l1ZO?*lF=-vI2r-(6!nToW|3gO&79nemcSm%#9AQ5Ov>3l zw_EbaNjb_HqWL>!o;M7@=)4WYMw>pls&v+@- zMtzSY#Z?uq3TPkkIoWY2^*-u7S!~#%DS-r}?~uMDx1HVSvXc8q>Jz!`In_mWQ=-qA z(>XKO#J&?!@K6P)ytOjaBdU7D@$m+iO~>pws}0(lx7^%zeB_3kxcDIt;FbG-{V4LKcQ2`s0cxPNoY3dY3qbh8G1w!PV9Y{SN_a@{gi<7VGC8@f4}Q>a=P0qbt|9X zAtcK7v?Kf3m^( zux)Hi`PD!ES$W1+KY{kvifOY!j6E^8`ImKD=MTBi$;szA(7%kPY^q{jmG3%;B}&laAQg9bAE=xsc=KLU{LWg|wzUY7Gm9ehaa>;3m zDU*_YtuIXZ@clz1Tpl2WuBGyqKdT-L%BJ*yFC!XlX~C-L+rWZ%HYf}}Flt6@j3=cL zro6xEvI1>*FA+y@fpI-%vuWMew?+#-jMsDYbCEU-5{h%ZAH zEo}#)MoI#sP%*6<>e7njhOnb(9#XRI_~Q=Vu`cn zt1+ZvXwiG<3bij2<_1-UDrc`j(>34u8BDUel_h2f4m=z0ybjJ;?vtFcPB^3f`;0*Y zYxQV@72A~^)1?HQRY$rW2t^;F9+oNa%ir~EsCjK7v8Do0&d~C+_M-o!o?UJySUMYO zAG*%hhYQ|S>};Py2$fx^2ke9gfxevO^@d4^!v*$ICW@u9_i8|K>;qlanv<dY*b{QXrIzifV>tr( z-X$uZsVWPCaTa8c2*dX1TmKY6hX3e z8+#v^Ej+zes0)SPRXXwJ7*2^rpXm{@t7wAVpnEODl5(N-cuM#=9korHW1>n6LhhL` zL&!w6`nE+MmQ%x=L2`$x;8JAdI|32W41FednOPn)kHtqXLgSQL?Ocr6?)F`u@y;>t zGK(^hlG3jxrFJ7Bgla8dB`c{^;|#u|E=Ky85HFnG&va_n{%oF+*fZ+pjACahi>$=` z(|ApU9Ff-KVIfpV&^H@x=u|M+fs-)mp+<;*^Q8(Wp*%45fw0#kuHEod~cSqmG}nx?M!Ox}Lv zzkI*^n=gMfI}^S;SYY46Sti@3&VEDx92|p>*Y~Tc{uZhsU-a{R8S%Le5lvj*xayM z**nYZyMv70V2O27)_A3@1kJ@;%>gmImbCA@L4V~}wjj930M1uQ_v>=~0pz;^-t(FqJg)C9+lhN0)VqQ}ZEXO}XyfKz))flp(%71rf;PCZ*UH(zm`r_A+v>woOoI$1kv-dUq1mwkm*~9ASta;E?GpBY> zTIgk7om&SO{j(72YZ3}OM`CJ;MTRp-$8*l?X9I2Kby379^rk-p|ZOyDe8UF_gG zXE(nNXmchCL|nOvq~Z<9bed^nPgK~?k;)5uX@O`m32e?JwWR6<1gY5TO=>&w0hg1V z_{B33+eC047YYE=%tr8d?}#y$Aw~&r$eG}5+U5}^!79fFIe*T|$u1K6o|qFcM*5t{ zs`N22pS4zH1}tJvt%^joG;nns4W3D}!EN(>Dj%rDvsG`fT{*U@K+K?VM#ykJTzaCfY??qCyx-wCr83pG1Jwepc-Vt)@>D!h?){^v{ z=~Bk+4`!*U@Ws~?L@iZZTQns!3$EOHYyxcw`f+{Y7$b_%MP-q)4T`d< z1jOqyTqI_ti$YF9PUdHd<%XmsBnSoXjP=b5eS*E-v!JGBMD~HbT!FLH_%7SA(`@_X zG!t)a;KcseF5QaK)K z()LBPDX5c7>@r=MV9%o+V^xUJYShd#s+`>Itv|VZet>c1m@j%%#m>uY*HQ2`W!{>4 zf6=3tQ~ENRH6XBW+AL zfzvrL)5M;_uJi0Uk2+819Mjry|DB^;HXd=+WXu&+#g=BI?TB5=ZHt!O`HWjXdZ&|@|T#ULhT-UvwoD9z2Yb3)3vyN=))eu*FNi8 zIDLACQ^jLy3|E*3hy83f{3ZPS&%E+(VbLFX?c-%nKSYilak0mC71;2BN!8GZ60%a4 z#*EMO?WOsLpFK6Dh#2o2zqRTe8KE~$Z@OfD*He&bYh+UIk@SLacd5`_vJGFnW zf9;?09nX1ze)c5~*ULu_)QgWlM30_(rG~Jj554!p_4(iSLjC){efyzb_u-HH9IpG` zXR*6KFV%)Co#R~ra-WLrqKhu%OTPG7?C#B$4CeZ3@3vF#;dlS%AILS=T*?0n+rWYi zozow@ADo6Z*LiezJzJsplTMhhB2oXX`mfAEeIRTc7>~&(vS| z>0e^^B{J?RAkeGb1I>5!DLh}C99Ug1YtKrsSxiM$fyvZmZ2_-q*J&(% z;*5}r-(18m<=Q59EvzqYS^sH9JxiwdIIHd|taNa$NXS=AvMV0yb$Rj-GBwE9heU8w zuu943DXPJjh6mM%ux6lqhUOOZY$3uP`fWx24-0#st_fWb!}La?kGrI2eS{A}oO zFMypw01eQvC09KH%l2Jlw-aOd^ zBB}2Ud<@sM$XO5T?cZcoW9Lq$NGRb8F?A%M)ksyqMG*69^(i5_ObQFLUOBf+5}aox z_K_qeK~XHV9nAz;rKEa^%jp^-Cey*8>PldSG4#$k+MGz(lzx`2K25~J%vv?Du7IY5 z3pOaoNtgR5r3PajGhNrSH=8r-7f80`?U)Sg4dA9r(HKQdjKwia)>oZ15ev{0QTje| z>)xC`_T-e=iydw5aXGVyEm1wSD4WfMlzZA7slAd@PnQxSQI3taIW}$BpPgdtD=OEJ zu`n$`h<#7nM!LSEssb@b_D;=A-mM+6kK`OVy}M7JdKP`AcCbI6Qw5Xq?2IeAn2QWr zaMBa8$d>JKL*-yxSzTVJY$88+l$yp_6@(fyYd2j*W26$8P8{N4W5c5xyYJrxFN-^kf6^+u|S}UC9tC?H?+vC=6$l@4o&8V2C`ZjeacFVLQX3$ zl=D`-kyObs5&O)dH{edP;gd}QQ%MT7RCi5@*e45a^Paurh#8v7!1xey)QTgjV=o5M z;62JVNfJ1*n>g9o z`}NtT?( z`8{~u#n18p03ZNKL_t*5EpL;DKH_seEeqyf`RQNOBjH>;FcM#?xMV41OF#?sv(tR{ zcfE*X7o7KLUEu%dfA~RJ@G&Zs37IWbMhX*@u_{wlj*J7p^SWPQcX#(Lzu#34c_3pd zb{G5Hy1xfGa-^!*2qrO=tc*1+gM&WPxBvWG|3TUh_jp%vdfwxu!G{SIM-RtX=1f4S zd|2AMwkDf=-SAoIcYMVQ^eun-SI{JmUADp1mmlY<%P!`sORr>i zvBz)z`fu~V2R&B5{Ath86Sp0X@xShe{+--(qD9JLbm_e%->t`2vvTvtZ{=P8@NVAu z&g;49V;?_Ib#!O8XME|?B*q)joIz6wb5c&6+~axQ{z9Jg0Q1&pV%cU38TDU3QFrc=!8w z>G%DVUb*uieZ|lH>Y?6ZG;Vn5FTYT3e*di&Qijsu*z38=KhHbNo(l{$E(2&@c9oX_ zS<0#Sj@QDc2G4n6Qlpk|wQQuxz<3(9P};SfTOtNmfJcdvU4jNm@Nz|AyronQmXvT` z>M)&mWlpg2x+=PqbPumrIY$t`^w1A22q|L{`;_UE)gP(|Aq1*mO4HyxRp|kD<$g2( z$lzRIVbcK~6*cQxEJU5UA)Vap1GV>5-cywUgb2pYI~!DZR-oGBOEZS8qonwCR|7v? zGL$Ca4`rxf$9D*yOA4{%F*rv_+%Jv9OGevj@4T$M>BimZ0njBwcEIv~g69qi!N)dZ`RfK=^D<|<HVo*KC#8;J(D=crPN@{zy zw>Rj!7D3qDY=|*4pC_uuGn*x*lQFY-OYA$M6SlXe%;$63sC0Qj6FgnYIyuyA@k=lzf#W6B&f+;joViYW-gq%r|$jek#PY{#8 zSCtJWs!EDABA1SN>(|`x9ecg9=!7FvF*ZC}{aIj`XetrwN6%_3;^_rU$KJS5aWpP7 zPePW2^LE_4v;JA17Ie}Ov5+@cWLb5JRDQuiN2VlTPVytVJ`vydk2qghP+hi^{VGMG zDA7%s_YU7w#4eHrZtIlBXDWBl1660}`$Sy_suEN&?<{RKt1~HWNmf-C(9|zJoYBfr#fWC;vMnpD)gV}+YTHt~#A0spIf-+~316nWE@phnmQ0J{ zQjwHKTkyucZ0O@H$tKFYhOfMZ<6y&icL4}I(-`PWZ;j9hX5JC{+g zbMy#5^vlnepS$iCb$dExe}0Opp0G1IoV?gOPjEtm5K~W_wLJTWzh3J4aB*{Q{*yoB zOCR$Mddap5_;S{+Id7KmXtQ>esvlc-QT|=doY-biU_% zzfB(b$bWr!1;E4q)lwgv)T?Shv)@2B*=*T0_^{p1}#UXOj!7jnge zF5|?lCzw<`2brzB%~`W!`e`#DgZkbLP}Nf&e1QsLLF!Q0DZtXc(-k{nEwrASnlY=XrD>I_S5v$htfiprp5(t$x*Wy&eiDtkfFi_%1Kaa*&75;0C$6v(GI9 z_!Z@#5dF-~^V{DwbO3^?OP~3Aiu=lEt_N`P@<`$PnT`#zEKN7GFw$+6HFX!j7;i=nc7J%Vp8Qktr`!-6_6b& zytA5x+L?Sl7pe9r1a{j6eNM!#MRSjm*cu5F@0cx=i4SX?^L9hbfG;VYU4~8_ZA|o8 zX=7ry@7Y_->HEa#*cbnLPbb*lHB*Z+b%D0jr$jnZv_8pRLrT(%h;%(hSX!r~%hUtEVf>chZDpg&Z06 z`%Lqk4R5`@<6!EMPCc{i%?F;8d9eg|$w&}lRO;ZYW7I=kdunSmq+Rsn49+XrJF4Jm ziZ{Q?9YO7ZT1!u(y{$z=Xf-g;o`u$y%$^b>*Aq{6NC}RSt}ml|g=C9?td{t$|=q4ky)fnkO0opkT`ENW}G^zc0o*;w(Gd1n{#6yx%sABxb8Ktlusu( z2l(?h{8_oqwcIn6UM(@zl@xv`Zu_>N&Lg1)e$a~-XS9c55^^iw?uI$G-_!{rZU@ym#L@dF+Uv8N*zfur}Bd60)9N@QP)1|a1!MYe~hnt%s1RX5_A|)%t965Ds%S&JUZPZP3 zsDJ+h*S%DK?+@S1Mdxi;Hjl4p>J5VP)*Y>xQ8mIzrcJh{axSoQ*}44guf2&^z4A4O zdcW_w?wjSr2T#%!Kp2lm()l^3 z<0;;K{ku4I>b3%iY&}_`iVZ}HEIYJUpnb(m$`E!YQW|9fQmVb2SPM+>p-8d^X?sA# zTPU&*i1QZQ<~+W5@hj&M9DM-~QUdAh_*cemdr#vl8XpMW*)MU_&e-Wnur>^5`!K33 zzoPP{<@3QC`BEyxDsTIrbOlnKchufNPD|^mb*%d$`OZrqlQkcV3X@e$XXUKt9jr<( z6u{PHwJ^ABZ57K3vZ0<$SOz=~fy+9C(5V*5e+fcViZasjd`vp;3tMi(j`9Hxq~oW8 zBL?fjIx{}N%H^0tDFmRbg?>v>3k{WeazI|a^nn+az02m|*Sunob0Igytpl-xwD-V> zuj5n(f_BZLJh1UYe!=jKOJdlaS(`Qti`HIr=E84%5IgG_ z9Kt&U1G1XIi4xULjK&8#h1!`k#*5In;bOKbEAMThT89xKc#@iDCu*juVUa92s*S1i zhbLw+ky9)aJ*&fT2-6VgvamIp5WKS_>8wnV()$iuwGARd=@(3>rBvhWGiahJfG}wy zX{O88qrM*#AKyLAVm_zwp0R5nWfn2vTp+uOKKD3R)5V@D)a>?=d2DG@Z@y_NW_?c} zQ{kz^y1Hu*TbmPN@#HVs#Aq@pQ$@$#ERx%fzDu}l=YAbLRpscq$Y|u5Ef#nuOvVje z-UMm&m zt#A9~N$8v-#?m$JU{X1vDox{;%u6zLI6egG#uI7>&RH*ZAL*k_j~AWEwEVK2VC56C zPEYgMlx71uu}5$&fqSkmhofDeu=H^m@3@!;li?6*^+IgUFJ#mIf^;JpP)5pMqnm(CU z%ZahtwJTLjqRg_R?UhBJ@xFAWX8RltDVYp55nm*;e>AlayfAJ&eV=if89CUf9JOS& zm{XBxh)C>LK6~%zRnXc}??n`sqb0X@9a07(+9qHb{;fRXDUajRy}k9#f1yX2 z5LL)x$7m3cIW;fXR5K}_;xSKu{AY}Y`(Hlt^SA;yw-WBLy~UL~JKS$;n|n+qTrsY> zsHwU9=!AE_{;hY*`(5?m2XQVofZLiJ!TVqfqGF-a^$wFevAz6-FVmMi`MJ7#dXJs^ z9%H;QWqZ28z0bRtE012v!!Epve{s?Mc-STP;Gqw=KM#KBgUL7V^Rg#@m%jd2fA>zk z=U4r^XG*(Ru(^Gd$$0C~Y;y1piIGYIyR!vf_{_&I{l#~5`_b?Jah+bVMN+}Lz-Y8l z?ya`a%{VGwmEnhD(f5`slQZfZ?|tX{dDXA|&K-U~=RDWF;9EIy+pZ;7r#otFJPXjw znTLPQ6ZHFD_yc5GxSId;Uw(<7{>h&^^!q*R zbFZO~eMxA}z=W^)nlC@}>zusp6xV+4m+61J?q~Jr@i7;jyGcE%s6xZ28dFyjM)j2O zXq&CgZ7x6mLM}b;v-qu7{Q*ag+)w}RZ{Bv7uBDU`s*aockMosZ{l$kmVQ%`^&AjNv z-@^sRHqqQ0z!a;fwe`K0#BQt6R8{u6NXkOxVEeoYKlLrYsIiZC_&uNW)W_2kmvU+D zV${T|e)%`K=CkgnZ-4tcxa#zE}2>a^vnU@44kR zPNyCf$MMZ=DnB9s(=Z0jT(Ef#r`wiuCtFDHobFq0o$ZlQ$J94$Ruxt1I8sfyV7yHT zjv&2R526x+4MZm64H_?Gmzhk*OeSNTw`B4~8`<4m;OmO{yk|0M7&V^V-93(M)`S2# zb&Q&ZQNZM7QYyN7>u^ljDnvFn$3^mP!55Q_0M3!MXKPZ^_XkgIEfX*&4qw@2?7XG1 z*WMB8Ko?=rDtil?(xwDCTF6{dC3(m0UQZnyIU1_B+}xlG@S&5Y6h+_X+JY+Go1X`h{amxR|V_CMx?1yTE=3 z%pF8uGo5TQ2{regPPlZkfltu)J^Qg^E;YB@{2m_h#ZTeaKJZ34|B?$oBY^G=fB#N4 zscFRFi~P(La9)>(L@&Z=PV|r`!HpPjnRm@a;W!oS(l2PP}r?I1t68&oHypn`^Ew_3+rR@ z7wA$?7b9Iti1W)NW6?x?PR+}I;Z=vCG_QT?6J@u(!2q3itM%C-w=a3dv-Qv3`j_l% zpJNIpUs2Vg((x@OVJ%9kp(0AP;*#T+^KH+2kuDbPq2BM}OODg`Jt}Z&_cYIc{0#ePFFN1&DhY}_h@xi3a&I!1hkrU!( z;!l3>O^15lXMW>TIkmTE40Mt^wMYi|rJs8_&-lt`>XqkTL#P7c15Fq)9c?qJM^s@< zaHA!Y9mEl&A{6#}U}d!^@2o;haRqEzpK%FxD`91|3wXenA8HMZoTCO@PK-ntc^Lb^ z$ay9{F!6!ys$p7>aK6HahNf-^RiN>KY4B8DXt3;;K|$xSb*R@|Y#4ck#IQv8m=m2E zIQJ#ZKc+;VqgiEA2RV{cho+7`bu3CnaEd)ScU0bvLtX~wTGzR-*6oe~;vtj{=Wqou z8&L=2@;biUYqF+NA(shdo#mE*d&M@(0e~k`0`6B-x zZiz!$xvuwh7v+Hz1gB+RmeqTMmz}jXbU8!EUgY0PLVQp~e1e;srSo9A8E z)djZ$>1Vh8oVEWB$nF!{h1Qw;vr6LE!TOo~@%c=}f(=kYEGOhR)Syam0W{I)o*I*}B@b+Wg#D&zs+rviFKz&B`$EP9BVm`>_)f@bDzqG^OayX6g`Zxg;+)%@jB)0Q*1 zRHW@d>NQGZ62d-8fYJL>Rz1^T(M>A4-7f9JN z>plB4wB8l~lCgeyg?_fj$Gt->vuF=BC(9-P9|WQ`5X5Ndu|tPz zn-j6i#GE*Ra!Re0;Ak`Es1tHbP*q&mcqH|>tenWo>Fl}X&nS?4 z{Tts#Qw0Lnhq|oJ5$FoXpa>^{EAD;8XVku5boG7t$UEPKHSKk@IpMrj+oX~s5>ZZl z^d`11JpZn<=wI+KKJvjE@L>un)*K>Q7E%-y$RTCzuYEm#_VVB6=!F;3#g3h3gYD3e zh^Ry+lT9Ry$zIVM80jW$oKaN`P4LWo#m+^?dDD;lwqE(r`^ov2UT}xsyBXE|t7kr( zcmJR3+1faQzhLKZV6UjRYG30vJQv^dl0*Ibzx(UA@lj4Pf6qSWUvLp8`qRW&xi6)J zdSSlk(9#K+y;;U`;fnT%)Og9p3rJJn=E|OF#D;dUR{MJkt;Vx&Ptq@8a3t z^woOR1rGp$N-CzKt!0pH%Gp4*g3AFfkks;n95!T#8Ywy71+aUZ$Fi0%bzg9<1jk`1 z;zd>9L|3(3dDTaqQ&+mx2WHTfgmv&KGxZ+L)|!(_MiUrAuTF|h)SO|1El=9t#zis5trEr#@uvl_9V$bIzG%J)RkK{gPdFZortnJjVx+=ZbeE0P^*Z>sDXXbq~6n z@vUblA@r{_6;HR*p;Fn~{fb|%UMk8sayMK>+?>T$#werW>=iJ*pqtX1t z2upWrs=I%Wd(VBGefC=G`~7^gD(eu~wC-CLe8L?fwR$vDu)t{-xyqT9C|&62TL&k*t-;d@64Z1Xt9^rpu->7nc@4x7qRNNe>oK6L!-?Op#g) z4W0qIiUW71b5>nXowlUVV^%ooI#$samRm#5HiKC^?ihLM-Z`}^9ZP{#AE|AU9o0L4rj;UI zELT;`s&`F8ASPiPg%mv|s~Zv$Jql~AhQ)~KM2OybT&KiT-SSX1kdn){rI9~%27#1J zh>>xe7}h-{d*NMkB}Jiy0{8_b34I@%56_oHtx9MG`EII9v!~WdmB5f7183_*uKt;h zq4~0?qzJWnppZb(jD!yJpg=zdoA)egX#hU=qo<(mId8e1vC%{o5OWz|tcJxvu-b|^ zhsXyJVwtGb$YH+dPY97zcUbC)TDem1QU{dF3RMKNCOi_i9n~nTRH>6c<`9K7#=RKG z=CU}=wRNtIE<&i48XTHe%}9aPF%RT)LaBx(VT_4lu*of|Z_JFBBeD6`%1qEt0jn-4 ztJ#wug8Dg{Rk^E`!Gt>@a3@bp{lKk0vW}6lDq&a?Y(gewr^++a#IqM$hVx_o=6ioZ zUi(FF;Ir*^!3U7=NQ=?&_dSV`47P3IZQ*p>xOMZh>ez3*;x{rl7^#C%bD=a-}LQv4U@ewovw6+iMLKW6{d@BPZ{|6k z4O{po3=cVF-JpM1-=+ZR0iCKTngJE8AR+SJXbv+iRx5)0;X%ETJ=!cownb78 z0f~FKsHs~!C4?orX)fTT4al@jlA{PkJ(#+*L77TuQ%>hkB-uwdxdK(_QUu)>fYPSA zMGZkc?sB&2&9dbdP3&d+ehOM`Pvl;EsoN&xMr}~KIX_$^uz#6a{Otk*Yv;W<3&_M9 z7_)T!!D2=i343dH&cI5M2AJDNqwS;0S@-gZQ&>SUuVcNVyM6$U}pCIQWx;xANd+nx)_KL!0d7{)ZK;>{}sAw)^r6hZARc z0r~$7Qq=u>eK@{eJo|@OcH;$G4Dw>%f5BeF-sW)lGcRgsKlHNO-rhGKds6n9j}>Ex ziEN6Pdjn^GacqQH35lV-Fv*l5cI?WIlhjkpi=q@9!Zrohub&QyO)-u`&(QL9OYwpi zF}8U`Vh_OxCan9OkUF#!Qai3OD;mUer`t)l?|k_}7}^3nmdrYJTueIEe2`#^lL$yzBWgCurZ2{E>a1|vzNilMb|(hZzvBVBDdS{)Gx9IpmM zo$fd!xT?MY#NgA&i;_7?iQ{fy*Sd&NJeU2V>{z89A#jgNF3XOGPEN>FYF36WQ0v5v zvoop{N_JI3waU@@h|Rd8PKA@>HB~C9cWL*K25QNSlaQyv&9gH$+pWL9Q^8{AkdMwA z8{;?;LT$PC5K?U55MkUE2*Tw>;pjL~vrF0Yyb#ukq{uu7D=nU`R$t7g8kcD<;?vk* zg_t7aPFWrGU>eQkBw*qfxh}d&JDL}Y6>0Gs z4nYh=CV^OsljUpSxQi66^o?X1XIeUy+N=;+I&!E4(?(Uzge56Kz(VUZ`o&zVv`MpP zmr5{)Fp6(@q6viPyoc5ims;5oS{`|`lY{`xn}1b(vb0rWP$(jlS{XX06yH+_Rte&A zV)v`f=e-vbRw1Hf^UQ7~v6Do$h^2rwfXrAFF^?LwE0-$Pn*CCc0+MyfMb+#ww^E%I zk*hLV%CGx|D!*})ici$Zl3bc5V?JN#%3x6DqUQmlIXjhY$JEKuZOjrs_WPm zc=V0ew`@H6ibr|$6~8u-eP8(--^$;A`;YS2BiBDKfBxs+!}-;Fxpnp`Voc7lY1azV z+N?oJBye(alV_iMW|`Q0_#+=Ch|>3gpZllpz1HKV=+gOdm-%hK?e|{$xWD7Ce5ZZh z=}pX(AsjnESNGEs8g#Vk)HZpsHKz+R0Be?5-+tv8|Mh?N*X`43*dyhkcw${>D^`Gi*#-aTXauB+o^7lg3w+s>LUevRB}lDiUX& zQU|HyE8hB5XfFJdpL?f+j&p7UC@&XN2n}rJg<`EPx3-q3W7R8-xe!z4eY4QV7Tjj8 zB-I!Q4aRDXp+sZP9MZW%lc~=|YpunTN2E7%*N1ugiKr~Da5;#kvje$4 zi&dcdRBk_*opYzdOmvTpYqifMGa_nriHM#f)NGz}t9!{+?=P9H)h07TzBaS{;lRh{ zsXe|&wFe~Sa-T%np=a8fF?KMf-oIDUxMBx#^`_pLC*SwkROiBY?j*FhG{x?dWSbla z-)%oVUh3zjpnYC(`@pE%W;y#TLD%+b$-z20Kkr%Px>Vf2v;C*W6Od3uQuE{ZB9$c2Nw-7?g!bco zj4n|R!dNSp)ss(;)8J86A`J);k=T*C#K~%aqZ8IgC;s=&eOO0*M-b@6GaIK`ARAR- zRAuPAi(gfJ^VT||b38<~J>5MvgNp>%)X632<46b{80<=69RpcMo}HABj}At=QZF9V zaNmvyweL`Ysdc?dd+w!ChAl*PT3cp2+^J4YpER&7a9#>ID|g0?!_X}7=+JYVBKNX# z6cfET<*}eV@#ZgK=sRK;7((FjlQX80S@k_pBS)cU$`cQz9t+CRkia4_cw=FzJ4z`W ztyT!o#l-39ijX3g7h5hbwv4+GwaohXh*AoxqcywHrN>>2^sC6wr)9C2rVOZ4ZaiL~IQe?MNBn75j zCdNp2)MFu#OU0^EvbOA9VK)`VVq9dmCRFv@b8Q!-sVY-Zrs6W7GCLU>>D;POB(Mst zlRB?UDNz)NiBXLdng^^Z*~E+AkX%WkN=n8=M+qIOKG6=;CS~$YtWJeGwWmJ`T4srt zKaP?p;Rs7>G=ohJA}l1v0;MQX%d-5&c2PgC!bXL4f<6>ZQYA%Y9hKwcK)SWSNhcf) zfzwVoPF{%j!8i(;Bz|GBM(Ic*Oy)ARtin|`uCj7f0(W@ueHf? z1Is4yx#vDg4D%#f5iQhGkq~&tPyMrNJ#LIqpfZ*XU;L))N9_IIf9Hp=!Bc2bH?*Ng zrL|g@xS=3%FK-n{u|p7|#5R4cfbzbl-p5nF`t&uAVs^atJoYlETK-kuzVJ7FK9{+2 zl|QKq^poCx@cZ9xuex=c7$e8)Q@XAv#+DW&*5u{UrP(6H&^f#W@5d??f2LnJId^*Q3qL>hQDxFRmbCUc&@CP;+e@(o{yXlgNEwoHRVyHA2F z5l|rrDRrb2=|W;?FuBk&zy0$g&XQ@=@me)+4!sYyrcLVZ#W?L}OdCv5eNJz)2i|J)cpuQ*8>K7;w{Og# zwne?%YL30`s6DcJEL~M?;6__w^!6GiT`Dx6TgswX2<>$Y^WP87?wMR4IApW5onVv~ zJb&gQJuKPZ`>C@viMs!JQ1#C~_*sTOf6f~dW+`^t|9YTGmj~j;4(##RgGKI~2i}?z zx=gwEMY0{J0`%bL56Nq`gpTiH-|Ru(xb2~IEO0toV)kB8YyPPU^5>1fUs@w|d6)-% z`3LN~`xI!cnUK0x@J0#5G+;HmjI8;^gGBGx0l8MX7${olBy!XW>#YQi+nWwT4Uj_M zc-3*^^d`UmoBkm0{>h)@gYSPIWh+z)Q}sn|GIO9?5HghxVHz`=TuF_pZmLWw93_8M zQ*a-QNN``Z!VsXSEP|C3#A%LoVwDDu_32`R*t5TqLo2}of*5D z+k~}s$;2ZhHr4ZFdkJWWR3cMRRw;5fPn>iss(N%<&`K=DPy9F#rpigzaXxMN=kI(M zo2hcM>xtcpkVkG0E4HO@y6V_W%IVOvtA!g~?_TG1;HoOe>m#s0E+c9}-wQp7QVV4& z9IaO%MxPRhado*Nbscpo^!=LEs%N(=NDQo^P{u+Z*MwBPH5`;KtvTNmVyFbE-Y5wT zC^r?COPSH7LDP&pjsEDEtBXsY<`<<~ zCCZrTyRdkFyqO@FdiHtO4ORL zTp4A>op8*ZY!r3bd9XqkBVBaA*DA{5`4>}?%>FfBz`Mu-O2MGB>R`QKq~M#$szQyC zTxKYqQbR|mnJPjG0b-zLUn9~Gxv2%axFj@5v~~WkQRRYvn?fJ6bRz z7u8c9b~)3nRy=lk#?4hn*j#!9iz=I1xKv?$#`ufx{1N$eZ~fBG`c3Dnno=YooFZJc zf>%T+t;re9cpP}@$)|YT8(;rfbL^k^;D>x#qekq6K?AdUt^~*i%Ew7?a?lNNa%(kC&Q3n3 zimg|_=2gJ6RBm(KNbC2!=a;ODM+m{8tsG3f+6Jyxx1D(!h%SxIW#gtseu1p?7oVWV zwrkq)n%BJgT1S0xe!RRh5)F zPPoatf9B_T{E5e|aU_b78Mwjc+U<33c!Kx6_x&8j18VC_yuI_s-o^3Bn%G6w-D=6M z*4k2teC+IM4Ao}Y>M&Kh5UEy3F*24NU5w5KsKsMHn*=sWM8xPrhnbP9xSzaPAY~p7 zagfp7cr$ZR7IBWzFTC%4eDK3h5t1P#c=4@u*;A`kg0_7#wb|nAK0>(Q*A;JAIh4)J zi(el!y5D?;i&Zn~BHU(C6C$n>EqE$XnUk9wYkOdCaKM@1#djy}#7-Pcw%fTnsas@C z>yq=npGb#g(oOT`+XDa9rszGGd@q^fvQ%;l8qYk12b(?P`-)8G!hfFkw$v2bXJZDg zq3V=*lLsW#9{uZ*{(U;o9GX|>`#31P=Zt!97U)8dpWF3Ip1mDTsP{f@+GM=8{InSs z=kj|m;%D!-$o-Yw{Q9WQY@34&ZQC>FpI5KWc8>dG?gx=?YWvrIKIX$o_Q9Rb^7Z?C z+MgtP?2{_xo1a{tKf~rwLBD^i@3&?xMfeNV%<}yEI*eG0utf0fC-N_)T_`X9bDv`R z{$LjR3&f>!D3|c$Mrk>FwRvUd4ZZ|A0+GmMzHC&~Wws5BmSW&W2v`h^;*ho!0;zSv zj#5NB*zXP zCdSdHw5C2G#)K$F+3gwuSlNt)QXCT3_mL32@4TBl>TD|W0w!!WV*}vKWCUF#?rPP6 zWEQx(sy>-lm!YL05duz`Z*+LAnJx)gjlL5~u1qZh-hz{#7ae(%?P$y1gxOmi8&Jbo zb&=F0U0n#xWD$Afm9O^P_*$Ltj!)gijG=YInWNBx5IaF*=M!f0eA60wL<>}x)OuT~ zb)qe{&`wu?w6-zPeAbc)?r+lK-nX(P6S*AD*pTRTdKlfR+ zVg48Y@TXzui761fL>I%dJ#?8LoHB6w@U55S@rI)nTwY-p7o>5+PzuqSO(cbDM~vP5 z(8sU=M%<{5n993O5DS~R|e0Ps=dyn0CNv#*;vgJw(_iEv)ZB+A}i)%lx zS3J6AQ${{MU9aevOx=bAtd3TEF5PZEbmsewGW&5~`t9kbKS79*ITmu>B!=0uZRQ1( z(R{FNkkYPef4me#fRlDCT67bJT2cEIz( zV@ysGXD)7`kCE-=IUjxI!yvAx@`$!b=kxkTn)d6gX{A&}Yi&gF)`)6T+FA-zp4d%= zTneR>Ms3#?UFVa87^0K0ZEs&ygIc{|UTXCLOXL59Hg(R$_uNaAn)yx%Rf77ya;nvN zMYUqJ(qTxg)I7OlT)mj}C}f}L)6Q4t9QlAno6*|ZRK#iNb6ui~(mGg0j{=7v;V)8@zsN-W!O8xjFnBbtkfP8xuSqWOtYseU0w4UuC-}ba{5zDq#mbKJ z-3VGKwJ>QV2KNq49R!Pvtr$ucDONC1tU#aALbIGSA|kx%i6?mI<_+HS-uJSK!d0#e zE#7P0t(eNhNf(_QU7Ys5&07K;#YU#;5Rh6Mkz4|bcTYP4^`NUCXGn!9rAd9jH&s<2 zi6BzgV_En7TAVOS;3?_F|o ze1ZzxJ-^4QUojTX#@>xamwJjB>s2Jhz*wtid#(eQSJfx>tH?AdC&xYGcB1P#k5I$V zGI6}>QDB#y%G>wBY5S8es&$tMDWZ)GK5hynfvJ$9GG;%Kx;|12LN!A0iMYBFjbcKF zv90?YMKx=3EPsFdo3#z#K<0VH)qZ7Px`H!UJWE#xW=saq}_#z1B)#@Mn^ zV^t(3Le0dWh^T|xOwchm;74o`Yep1BHTbky6%7ukE4i|*1Cy-SWX~c`5eBvxa1xck zj>|Bx)=UhUEJ6(ltzOtbrKZRVC03>Ofl!Rm0zH|8HXl1^@j|4p6OahOrS6lu9C%xe zN~E(&S3LKAs+sXzFe#8X#HlIk#9VHr$~0xPR;FCosBvMH%TiogCLK3R=A6hQr)wS# zisa_yGlvm=;>l0b31#GV{F=mJq7wFpkX z=qtyMJp8geX3Yg9vN7Y<)rORkPcM2`I>|LJmX{y?1gqEGBE_D&QzA-1%>TEl3@H*? zbdNMv3`Sd0jDHu1BH=a$6dn!D8b^XT8t%p~Xk6$9)^@_%7#(B+*WwXfpK38r> z>!Vp~aqZuI_POWUG|+8P+9X#}_YO`GTVSGd9Jo+4K{n3eNySt=)&FSF0LKSo3#>R;dAYFe0D;uLW<#9 z=la#pBA&lH(lpCs8*5$s?7;Xw7ruz zD>#6w)p;_jw$I5%9O!)D0PiI*YQ=~S+*?TBb81d;_-Jg?{pEfSWaG^i1?6az^j2mJC@z^c~*W-ie;TR_G$30~Ejf0D{mjACWiuvLvY<;beh=WMK{~4Rh9#_ms*YUsi*IDzjXajaUhH8{6Gn?oGUVe zpBpbw>oPfy64(|mpb{)ywa~hc0b#7!NuC6{_MoM;REict2>$>Q001BWNkl7h8$MOVr8Cxy0HIy*1x<+;3blDJi_a%Qu;V(5<+Im~*U_Rt5dPMefS7ldIL7=}a$o%_aj z6Q;1+IVfy3thl<|adNtbU|d~Zu^Nt?)E|Xuf*8E(9HTGVyPZdT5a7;v;pixlLg4Bu zqY61Isq<1K2Bi;hvg+AXrB=@hkI8AJc`Qh3i)(0mm^OXyR~;y%E|QBe4E|o`YK&9$ zNkyA(#}ud)y67L27~I<6^Is#Ykz1tAn3bs-L6lu_ih4D{N@dKIF&kNeZ@AQ@M=80S zNJB(JaPqqvLozz6Sg8~XY>WHF%M5j`Ixsc_ zUTKTat_DI?iU~XE7;16fzCG53_pw39Resx-~UK zZ{?&FsavrcRtzEW_&0p@%l4RgnyBht$V)5iN}&>{S69?~=ZId9f`0niN2#^2j!G^Y zo*N6jJy0ML1oGn2T}(C?*|5*9D_>;v7Sbl6YeU+N3q zMlPGp^xI{KJchvdL9 zGr9)gO3WVexoQe~&Wr>)BGH62OP+%>dt^yB^2x5W!or%|I#*W@$$@>qT0@BDW0!f_ zD*L4CiWiOas%Nt|FbQT zvm7V}d}8gMZfi{i^@%@FYFP}sZl?^5HPBw)rr31Iq_y8OSKq4oW3){7x?E@WM1KFe z9Z1^uvi$jbvxUGX=e}RlIk)`5b*N3E7wDht+W~e^g=(`m1fhBRW$~pissQ$+{rO$o zd&FgFB+(R{`-rqQIe%WB`2N$e&q%kum;9ixpNaX)WPFZ46M4Wpeel;8X*u363a~{* z@#1B%JimUngXV%ga4h$YsvpQFKluLPeRJJ5=@Rb#Now>LpupQfSo?j6^ULcg%WIRQ z(Zkieh!PdVD!m9;rObed)}>1e-Lay@*d_10MTvA;*{HGZI;tAGQW>Ohrz)dnPJ($j zgEdUXoH-7-7~Bi0AVQT&FWu7XD>!ogD8jB5!~*9R7ryb02_-NDrBtJ9v1=~1t>l{N zLZVdlg8d`Uu+y>-9aRe^9=xZ3XM`@!?H zgLfr;@}5bmOuNbu0y-&|+mW-AHHLek-Af9&7!o5@JttFJqpRk^b~~{;icD7pqOe+Z z4rmc0=Sqr3)l39qJ##&{EJ}h)U{yn#xP;bKcA3xAV#4MR{~Y2nbb(UM<+F~J zXVNy^Y`%KfLo(tlKZ-8l$Gp)XZ87eHe|AP)I$ao%Kv`lqg7|Z^3R2WBu+lP1tNZUp zj9eo%1U8|=)bmbr@Er4C#=Y2aq?NvAb|KMObs(PlMIp@{->RGnq&oSz8#}N-5o4_r zllkVu;VKmkM%Lijwxv={=z}uRk*FL8K}$huX?`^*CD7@_WpPO|U5g~i1>F_TFxE^~ z<02-k8yK(dGNhhYpPrI5^HA&pJ5vHb5DMRfAH;p z!7e_258m)7tFzOW<$k~5Eng;;3p2S*5+M=20jWmM99OAK7CGZKKmI-cJ74ohe&4V8 zX89fe+Yi|A$Zd|OoDN4!D)g=OpF$v;5vI)Rf79Duw#VFUN4A?CV^N9Kaxr)*aJ$LV%e@+!!DW#VGtuRk zQv-mF;SgxEEQwdbgdG{%nAHk=XqkVw+`P^L}Zs~N$3 zhpM&Q7n>z5{1R_dseln80OW-W*>9=Q<-DC=4rX!C#j!dWOh)*ih)YyKI-~GVJQbxcBkn#F2T$i zYLjt!e&X1D&U3l1kbQ8>et zg;yLOvB{aoSHj(?U_sF+F5g_bk?y~)>gkhtDs)3|QhyXqRs-Wy8G1h%H&bOC3rE9> z&9-uOa?0i9wgEOOM*~cyWfMoR5?K!&SKEnU7_gRG>*P{DOG|~$ic6`h5xYQEWj9v( z)a2FTlkaio-S1h+puqZcyJ%s3XvE&ea4q6BhDbkugT zdDePG=**~630=abN-|?Z$CVC9?1-wI*XYz~3NZ@RqC;r38jm0CkXD z4DPe5CPWpwP*E)elu>6{pU_q3F`!Oft+hDVPZC9a(-LbXk4hBxnXUbP^Pl)8{=k3#XXVohQohFR zU%cbp{Dc4WFWH;^^tbZxv-ik<`Mc_VajwP~fa=X~%h-*s=#3r1c z@_+n0f8M_4kNm!0Q4iU-hyb z>vnU+RmmJp6PI1_n4S=z9|%XGp-W%-?TKIiMSS?FkD@_X3GB+qL$T*F!=(jILQh9V zYozM{+n_}l479kd%Yq@&5v)*3<~nPc)@zxj`oPxxQ{NQ7#?0H@{iH9niTczwqX(1cxs*4D#K!P1 zBSkskSAOM#*LuAlzWor7@dA!%n>hA+D0)5jGB4$)=ljqHpX9Z#d-aRFaC6bT2Cfdi-s9S+dDhhd zv(n_UeQyOE`*o6&w)zdrtmp=79=f$gRvlM&ODlk0qVU0lH62t#LPA2O?-G+%nElQK z6jySomgcEVEv|ILYW4|rh*)U@B2`Q`3-7?m<6b(3*|%<$sb-|@HRp++X?5DUs@t?M zLi9$#N+OVEB|2zF)pE7BIp=$X-`tVPKfo%n`z6#`c|Gc@0EMK}(DtwJY`jZ5vrGRt{PgtbJn z8ml2PwN6(Ek+G^rs#T+t%2W$gy^9(wP%t`-@!&EOTFgaIV^b<8DWKxr$Rj2MM!%Jj+ob~R_R3PbNCa1n@o zV5&wJoh%vzu6B)N8|OuE@8{`a)B$ZNW{;28@7=cg~I-%5-y`H_DG^%l>0~$In#FNnDk~Y*Q_StZYIhP%RRIZ=@#` zM3p>MXmr*hMl61@ELGX%N;cTyiVqDPMO@atL%Go|hF7}@ed-?n#^3#`@@2p8cYel; zh=2UIet@6)?!Rqse9KpY73$CaGGF+nmye`=`tU=1^;^H5r#|ojj#I~p2!+6|W(qFh zTM_6aP(tBj^`rdofA_!e^?&k@{hAb2ADA z>*3X3@#dH9v7i3HuNv!~^PG98Dt)L8YSbEV*K7s6>8sx+Z~vYjw7a)P&U$B#+#E9F z1kcpKbH%t7g)@PZI4hhdPzW}9OiS#2`QwlUYHWTVdc7_E4& z%NwBwzW5;fNs^iFJ&o72CD0~XbFuCF+t6}?tGE2;{2ccAzw=%uFekQzPy2ItwCebU zpMT%AfA0_d(ci`QeDD9o@wz)GT39w!NL(_FeaNSKK$9s!B9~W}Y_=m-jo06L#kC6T zxpr>|gn z*QV9VWH3Yr6+67oJe@`w2+oWs2?>c9T&WQyIJmeK9H?P;G$5o;E_ zW>;nQn78;Z+soZ&6~H{C)`Q$^J8*5!%XJKqgH-Wl8r&bmiS2bVpyD$2nf(;zwWQJU z7sX7i%V#o%8Fr@oLVi&Ww8{ShZEHsmXy=~ZS4dZdsTN{uqAa-Wp_dG&rc^!x9} z*2#S+^K?hZR!Jc+d3&&(m>Clznv&8H(eNlHtyFQTHX#yl?~R(@@vbUGD;AZ$`O3S{ ziq~3McRkxOF0oNX6%ENLm%%-05yMp2nafkwX~jB3Mgqq~cC8b*Q}@l9DOcHLNIPni zDJfaKyH;BWhEbP@h@E;yq#|^VQzWal#llbSQjI=BCyAX+M1_=%kKeoFn{I?52=_{+ z1ELD+6d9{=ukMJO!m2xAI~mW#$my!*aw>FDkUY}Gj>}RRVxkWn$FXpg0j*2(l16N(nL+a2q{JJ4B-ljAj) zn;p|w>3ie!Y{l*>b9_8-b+sjSfs&P?u<9HdGO2OgC$_monT0@^GONzt_o~Ktnd!Rr zI+lv{iIjY?G!@E}{q^d66PB}43Z&%Swm2{9wS5MFa`mQfgnw6 zZ~mDoL*)|C-NeA!=Irfj{@( z+IxQdCpf)%i>td&6Mmv&*j{kHnJBv*Y4!4uvwzFC{9*aN|KQKsf6{Lr8JQ}!ia_qFoMFZrT>jCk8_8sr0 zyV0LijZw3a8U zrdaiBs-cKUyVf^}Z&S(5@>!8cwW-BJNt2-ZW;TeR)?L?v{(>7cZDyr8H&`AZ|IX{7 zwr2fx$?=De*Y}AZ`-vZPi`6xf1OEMQ`*-Dk{I0)gw{AYt;7%+tNn)X0bq*(HUJ%I~ z_hia$+9GT@|5)KK{FQJ0G}I|uM1)s9bjCY=>fQF+zwT>a@_nR~_zhq3JNW2FpXPXV z=I_5m25F^XgD%Ao=z|vkqQ*!>P3S`B0}V+~DjIR9nz-!0OC7mFh&>pryADHG4;|CE zV={kV%dlH`L@!k}(6uI$l-wlEG_{3-Bq)M8RIC@3q0!9vGwz|mc zgM@|Z9S&s4bJ66`_6AF9UoW#o6sYZ3r8Trdh)#=d1*w=14CokD9&9xi2aa38wJ)#pL$VPURKdTs{~HA~y?8*k75R@51n3U$AdXl@+X0^+;BDmFQQ(Y3$Z z0o-kQ9(IT=5W*)wI!sT|;d{h&vaCS7%uRF$UOp_rAc9ZeYtrJd<22NKU z{m>Kp1^$bsy2HOt}!uh%qjnmG#;i0wFrL0CS>h6eUF`%9}yg1yTx5 zy;Q$QdE?i=?olJ+4TEOK2+`&BGyM>)45|61@GfbovZsL-R{?ZdT|#b3lnN-*=2Xsq<`q35ADQL2hm0LfD;L|Ngo9Zk!pdeeIsQnam*wN z>#ov?HoLU*dXTv)i)WZqV&H z_jWrw7&-=&+x-b+V+w>?Sz9IM9Wjr@dv|%u z@BfCERf>Q2$){PL-C%uk#?i?s$4AEuDbnjmzS?+f;q^4)eB*!qZF2Y7JERtgDH8pB z4e;EQxf0_8Q{`8$l)Jf-32X?|kl0{6z1i{fuJWO$w)~E7`^IZO-=F!3pEKzL>;8n8 zu4$W)NYUbJ&ouGgU;O23{rd0zJzr1e!>E-ueU|1g^)20OtK5h297ycM=P_9;Z~TJS z^O28CTyDn0U1^&vRFXtLMs3nqZJW=w{(gD@`v0G~HxIWhtIE57W6rtO?oM;tYALFe zV4^f?B#7{d!3G2kF}49sqJj_uMvWgqO+;g#g!n~lAP_$hV}}8wM2JRX(9oz5QJM%A z1=T_|_ug~QIlEbF%{fQ@7;~+?&%L*NI7O9uc zFc<`t#wJ;GBcAj8XFY(UKeT5{P~P;0|9R|Jf8|$yv0S`&zYH);!W~OAnUblz=fuvg zZClj@*iLG|7x1cMwQTwHC;UzB{@}b^D)#(rOH6BUmYmZS0>8zWIfAcP#*XFUa8Me2 zY<(3|dN|FrT}vAqR_%gSx1j4*#OBZxiz$*i0}^ZO=rohm4aH&Q1C==Q(W)V7R;i6b0YM^p*Aj&YN9CYkn4jUhvxfj+P62i}oMx4oAfiF?b#XCT8^U4i?hzUh(9$)DeF z)=Sk1Y^X2puO}f#zTJ7u>#Zf0s&Yc3amem8fqVZOW|p}@iu){R5}%Znc}%!u9r9Inco$4kg^fVoZe+?TyfpjjNPfxNVlqrd8>3uSuHxtP#4F<`%=scsj+jIQK}FO zm{!_$F)6D>BskaEr&ar>VMM2aWe#*|a6V1zYc-@6my3Tq!%8YrX+&0)=c9Aaq7zo?*w4-g^Ukre zT{E54j2v`PP^n1ECGEunqW7o^q|p}H*VbH1nZs4X$}4+7s4K3y<~nAhF(G&(yJjW* z=>2-6^g2) zWE;lObrat3y8m{p6L9A2XhbuII>ZE7B- zGku&?pKokF(Fa-f3<^x9*;%d3C!$K>Mq0{W&ea?0^)&jTriQHf+qy7rN3 z&V1;jck%W={%P^vKY(ANGJkIfy!D-b%!h8d?O3n=#uq$?eeOYusM?D|#G6V>EH&rg z;G*q$26e^S*Sc6Hp7=SR$$$8Te=WcA_Ls}qlh<(Z?gc4!!-P0SWoKNoI~k+m*jp^w z@1jMHc~c}FFH%u)bmV4bgQAoM2%2Q`3a?UT5j$3zSf)&?1<2-{d6<2LxDif#QAY|< z%`Vrf(yFpZk!6gmV#i_ZXxol9mBPJ1#d3j3I$6t$`}UXWX49q=^Er~USuSHrbY;Mk zdLnwZf4Z#0bh($UuVH8QZ?Bhv{J69&VzD&NQP|x!qU+$;$ z;`+if7k_(SXUG~bw^5i61#2&}cbf%I9*Ez4Zg^kBYVp;lzCbD|QL<6MIOnYJ?Wfm6 z0mdbflqFYP)C~vV8Kj99{=ofbqiC&7S)im0v1r@2|B@IsE;A;zTa777QMeBfs8J?|gZI zi<(U4s*yHz^zQ#QcP4SsHI*_8Xl~Keut*JETn-(4&f=d5|4; z@l8T)peix>j*~!#(q=g9B26-hqRGsyYnd=IycX|llWmb(Bx?pVv1OSOFG6!S;|a5BOh9nXeCK#;f*6T9Q;%V_NQ0fh+tYR+gE}W@R9tOfvru_d9Mwpus(=d~ zO+u)>Er!Xui-TuJ*M+#N9kET;4Rp4!CN=W=$ipZ=@V!+jQ%PnVN+&Uw4PMzmCLC=N zT6H8GUB-NM$%;rild)@3yr{*exLmYP8F4TQ%66Tp%7#l*!sR&7-nHZ@_vjVL3C(>M z+oIVtLsF9r<}4(2L^04_Eoym_C}TQ&?vRvNWKWwbn(UZs&0NRq>zI9NqIj|hUG_BD zTl8JOm=)y6DuMT6q+1*s6Z54Vw{H`!Gx=NFmhO5=MsaWUk+w1ErVBI#@?>V4n+OTd zWOs+KyUWRIuVs5$bKS`q)7>3HJ;r5ldx@`j=P$`;Jm*;tc@g^7H~e>g;H$n#?`v8v zw3&MjTc(q%n76`(y#pGJwo#~r3%A|*p#JR7d*O5C@T2!3PN=67raRm0UbVySi5Zja zF*~yn)ud*&JK^+IyPUl7di<918{hWbdhe~bKeV#;fAS*zi5Gp7PES0XD9Q;}QztlF zuE=deS{$OWD;q>dny>im7d`($ou1$R{+r1o+qhKX@g4&z)t>RVqMA+c^#haBKj#Pj zxm@$O#}F0^YB3k}$O|X588UL0vE#0odH*u=k!8nW3vCk8#eM$43%-mm|3`oC*gyBb z{KBtO-7vDG16TD1$d7$8SROiE>~LQA=^uS9u{)lM;QN30JLJM%k@zWAkch*1KM;(Y zt{p%`^zu-F58innKlQ4AE!SRmH78E&@Ds22A$iZce^);75jS!BUH6vkXtyzWD4Rl+ zAuHDurpJWDZ8Tz}m#_C5?Ty`w6#2O;8+zTy1QvOqkT{#RZS9PC`72(^a=ALztGBmi z{P8WXm3U#q;;>tv7tDk0rNU-w3^Z!Xq|PjtEg$~K9&i4g*U4iZ`#|k24JrvlxN3HS zSO3)Unhdk8DX;(K*UAU(_z1!IGVNc>sBy4>FOckv5z4u1h7N z(e=s&B{)xzQ0y3mDqTF{LdIz{Pqy<`q>3l?$tZ`K-83>@X?;&PMP)=WsH-acOwG{et2K$ z>EYHqa^4f19|-Hd6zHMsfnE9D-qvaJS{a6OS|ZRcapMjX&Y`C0Y3wB~O^aG2Q<@}V zEulWX&8FrCc1z{o`^hHGBa(~scTHxOp}^I3G@}|ES4&ZEL!M3QIBRl!A;tGGY8Jt# z#A3w?zx=kchng!K%4_!E(JK`q`Cr_R|>Otc6=lp}KS^(b1_7&_gY@NwLE{5gUALU??{nbo>3c8^Py0dwX*ie!7GAQ&6If5?nquNzJZ6>;RM5}}sy&Y6t6T$GxXi3k&HTGr)u@b^aS zH7kuQ;)VK9?~)tJst&MsEoq9*YPC?j!}SFX80 z-!fGp#b==Qj;!V}N+L%V@vgk4cmHdlf!g`5*`A)@U>{b=z4(C17aZMeZ0UWBDdD}4#HQUoE%Vx=$=@f_J zlhHcc)G-mbdbZ6p)J#H6r%+cT&_q)Rm?LpaD{oLARl267Z8PHRvt2Zq*xI7(dkC{A zRb?B)E()nD#hwZ|mBQN#u{E&TD43_)NQ`PS7`G<)4rJ*PDPxy$ zIWcld6`-!AEW&n6#l-p=QESN9dpcqtW8tAm*rHvS1|o{f2}hz*yG|v=zIHFf*G%Qg z(kkoelawZt#zd46Eh91x6-S#L`;2LFV4*dO?AN}=tc=qN(M&+8NT_(4Y`~tFY=bdz zMyFkApwU3zI2Y7d5<+HoyXMSxji^!wyT@wp7}Wvsp6RGIdqFZeX0B#L z7hr3flrpY4L=N}Rps+P%XEtISl-c%-P(n+oTVqadZ}X_@ZzNN(>>M9mIX--@Vcurydc^q;-149%l23NFdCE(^SuWml z7Zujr3L-EW!)!|QLLCy*+A|(asDmX-OkCnE-}Qa^&R==MgMLo#y!jTMbLO%7x|hG4 z@y-oaV7NdK$0}u-rlngP5|>MKen4njWVz(8efDQ@_A!6`K|Pia{^7egxiezR!?h@x4*vH?&M}?s z@WNMppFHy$zv|e{h?o+u`2L?!H?|3t4>haycoXz~ z&2N-DZe7uJX)QU=d63Zd^>9w+^~|b#=#D*pun#3NG%d|8Pp^~c426&CxM92m2{k_5VIBLZO+4{ z+m}3ZC+3;Xh3%3zKvKEKCKOM9A1l}2iz%0wsy;qVw4Cb_^OdYHl~M$(0dpHsVp1~9 zi)`GKdTPXTDN$iL<5+`pT`78d1xzTBwEh2ALwUFfVC#S8CimT%2&rHt(?)?MqUHRR z{Bd1Jm!(pi$81^YNs1K|C6x7*HcX`Ddm1+A}^(>*H7s) zz0#l*qJ0MXFevR|lvvRU=YGcS$^caGN9*9>T)5K6S$AL8YvX2C{ShYKASJ(CNq(gpdT_|= zQCHIyzgIV#J(o|R=|OO1Lau#xZ0JhqMiH$?j@J!Jkt-C(m-xXio6c|k-9}@MjUoS$ zW8ZwKZny~KXfI%$`nK_hP6p$;+uhlrQ>9I9d1?kUCxpP%xuO5n=0w6kTwPAIxh1EF z*JSypITpfsrqRSQx2#fYQC+=USe2l{iE$tls%8x!nM#DZkSCKGcrLhXIg*l*9LXBV zV)SbnI0lL%(UwoWO%^@j3x#x*?S^bqB<2ppfL^i4<;&fHuWSmv6MFxIEB#s>z-5&< zRtl9uOtrqPV#%(xn{<6(ckKyiHN?mh0j?>s3f zhsza*ZNrJ_7Pp<-&8*t4P%;zF3svQrZke1t^syU7ogjlB(n#xwTAp<1Ge4CO zgi&zAlzykS&ZyBkZQ|`woc39?=I?T|n!IPN<`_o1}2U-zTXbCjpsGJE$_fy4OYsj!d z&=6>xXRb=4_OU4546bPCN9S?t5X zKIy)5aPA^IDRb_Zep!F+A3aw;^!xAlxR2xD+<9L9PrpN-{m4h??T7bJZJnUai5V4j z7MhfaDY0rh4x5gqS(4j^cJDs8>pV~RXJ7xI$H3kE=C^5F&2ddfK0GAN7x*?p)7s?0 zd20fF9AAI)OPwjc%}c)N$8>(UIM(}o z;v;V2qx)}`r#}5D{OO0!b9iAvjCr7~YtG~tX%-zf-*z86qf`9en}1%u_1pgO{l4dy z{Jp2sb?=aueD6Qyj*p&Wwdgh^s2eicjm_rqx++KYkAjqE!3M*)k8s+MajKSS;sZj{ z^-f6JBAwjb<;5@lR=sfk_;#1kc*MW^vG0_7?*480mp}MIdGupGnGf8)$DiK*5k7Fo z9v{5x95>&&$BozD$Ul4Wb9vu;e^*}jhF8jU*I)f{-%BeJV8`|amA*qgIhH3saBO4mge^CjnQJK+D^zQ4&Nj5t-;_ua>;?MSIBYRU|`BjwKG(e${VTnn0sMFE*Kf%AB0 zk!4;&0fOZsv+gT`OpFmp5sfXWYsy~UCXPWd$eEZ=IfRPJJAx}J6z9mIP=)n$mRyuM zy-H07n?^3w{#*cV;H`1vdLewlA(mJKC&P%>DCSz4Y{N9Qm!EG)j(Z4OIWtj>k(3mC z(QgECjEn5ri%GJBvuV07BzI?R4vQZHYUhq9c2k+``4V|Hh_Cx;G?)Ug$c3|bk~rF2 zj;R#qU4||dHc#;b=c1TQITzY_f$z$ugIR`2c`x-}19aV>`sk(hl%~X*imj!nE@?iv z&A_@1Y+otzOO%>w9-0gNq~G?XB_W4(9U^5AqFILqkhP7+7IzrYLKN_IjS)7wbK7T$~<}y%#wWmyVldquHbz;p*CR05X40 zCv{W#ebm|AH!hCW$ubs5@!{P=#>f)G1a?N87e6Q_EN!TYgNJS~5bX zk=}*k5Ms8jR4$#bR?|RV?12@P^VB#pLP9yn7U9t#S)W6`z*d?9x`X5~7X z$JYGSWHbj>qD-8}WtcddpElVhr}LOuy+ivVV%@ zk47}Hql=lQ=~ylAWqUm4*7N77M-{;ftJd_9$@BWU5Mt zC3kYUXos6%I;m)yNRAm_S&<%94FFfyXv)NxscOflwgrA`V(HN0gf_u=Z2ujCXXK2k ztR5EgrYxpLV86KEvAsRQJ7w$zUltkjX_b7@(lxKe7B+K#Q)F9W(QRG|n8X&Cu`}tr zIHUbnK$GqL>*8DQqIJ&2_kJR!@=9=Vh_(oQT#yBjA;Adpq)bZCuuZ2zmwPFhNe7*p zN9|$@E-BNI;C*EcE$_)FUaha3UBF9$H!9R3tgZXT1q{aC?x823R{rdk`+vS2V4y;nR;5~{MnZ?;xwmsZeW!& z!8xvS6_s-|IZ*-g3_A$1X_ixg-cG7IbD2!>dCw41x zk)L{}v?bx=Eza1rMmJ14du9J&6jck*vv^h!?MG)A?f z@>`~;S{(2Ai^`UqGgVcgDN>(Ie8-D^SYP(??>$!4yS=@|Prm90<-7jXH}l5-`rG{8 zn}1(#z2y$}E*vl(*F5ZoYxwlfcnqKag-@2pKjG6qruPiN^WyLLCi%MOJd1CC(f8>a ze(g7?M^%~p+xnZ69$(^KWZ^v_xlaXQ1B}E__#5ylZ?K)=h(_^v^*QN?Omh^j~Z9C50cY)m#J4~nJhh*Iom9p7$Ot*y}`O(+# ztY?1-k9+*_GSwgdgwNp3@BUSJ$J^h<|NI~SOW*Z}@8Ryd?xmY|?4I4`uYd9*`Lxe` zj6CC+pU;VtyC2hg-f-hJyy_RfSKj*OKjM47^JRMP-S-W0Xp?EDlAC+1mIN7tw&iTk z-QdkTpK`Xnyf^Q3rJjaFBobUTOe1qPRg)J}yd-B4Us)ZwV4hR9Om30QB0@JNqq!6{ zZny`uOuYxjKmpkr17bs%-R~8gTdQ1(g~K~BYn}6U4D=c3GEC29DAKif%I$uMWq&Sx z1l)$db*Ohr6_LxtTyR$37DmD_3)XdHm=(8Wx-6UgFe&u+0(={q1euu4Le|U6os(hm z+&2r;Iwh?P4P8lOFmQ11mDXX-eTAl`th2zK*dEdsWsu48k4|leI!QD42d`Myyi$gL zmUuxSu-lT8*V*br)s@$;EwH-&_Zzou)bJ_KfgStZBQP-a6SiO{AQl~ z#_teryX#K8dYarZ@pUO6E#}ESj3SfCN>@mgs!(~w`#?%9NMPQs7!^{YIiwO(=ZUO4 zgu8x(=EQ|8oC?Z8Dz)89Swt8ZW}g3vEhd3B=+L>Sa1@wtP)R zX|wg#opY>WX6me&FmZu~Oo~P^tVIW@M-XADipR0Y9aXs@x}-!=$R#czT1LDqa zv|4$DyEM48}ey+?9pHVPcB8b(!3 z^1}Xn$<8!zZlR3DI;F`;iSo$oOlBN5OY7uzk*;Z}M-}7B(~5A|bdWo=5t3J&_p~w6 zHkswB#d%?#6qU@?^_cTri{NMrWGKZ*Re55x&jztVuT5%Jf-W-}2L#XIe8pA)opfEI zs)XR};pYn6B&J^WWy+n(8BITU!Ku)VYQ|1ETy~@^%(iM&)nvgH)WNVpO|;4V#Aim; zn)aJ>FO7#F2K!6iTRhvjLqDdLmaOa=A_dzevPh5W;pZ%lXCvX4uf2$|X z&d6PhYa3GAku_0|YaA`z60e^moB#kI07*naRB{wtToR+wtvt8|SC1w%7Y=y)kN$+d z>0i8rlMnv{uKUzaWBaCu%jt(-&t!W>HJ-3MIAFE6&%GbMLqGJ+ck%9Demx61!t^vJ zPTt6>*i=sXnl0z87?wVMFhPX-BpF033-PWwPx{X1bJNov{Lk%{cfFfjduCfx&dg3T z_Kv1;Xcihxj5E-7gq$COY6ec6+I`U8uInOS^UQy&+c(x^A`>@7RB2;}9; zv=haobO_{#6yO=TjF2)oF(0ujzT=Ub zl`emEBjTusE1l3G=2=WK5* z;M9}>eIeQxA48V7+I~lRTc{)(bPNJ=EhKy$fLlZHTQaGoOsD&FhQ8qS&bT=gzj|M8 zCmdD(f#NsRG#r_lT49rik;qW4Z?G-8lnk8N%irmv8D)ra>rS(x{j zLSJkcY}gO_L(d#BXKxg^YYUN-$~j9PeYqhg-f2^y+Wkhv$wrf4^Y_@yoR`hq z^UHe!{bv^>Lz7x25?JMi-~tm@lf==f$=ZGSnERN4>^ytT$~u_NazAa;Ua{&rR;vZu zlL>cs3udDcox-Rr##0~gI1aPNt1t__2Lq~V#7JP-ESLpHRGZWvq{OsxbXplKqIl&a zwm`MH?;d&sUP-Xn233M6wHF#SC{d^O``~-bzAY-j1rB4!tiVQ`7`A(*i7J%*$dcu5 zMZDmWNnpIHE&NWYffl7wXY%WuNqsSoLF>FIr<~`YS#e^#g{x}Fkqpe$BJK{lNbrH# zSm~lNU$mUsnzC#YS8Y$3Ya+(TEO;))j!8A*7@|T%31gbu5>Wg!#6`36t70t{tn+ z&d=eZWjeB^jyR~iqaJxuryLw+>XD-hB{s*gk?U!Tm`&jALY-Cy{@9sW9%-vWQyT9n zDWd>OwfFL3YKNGF%Azl+Nl5W{HCb>Ds?uhwCkDOCrFrCvhsmyKDH9flmrPVK-&{6Q zy9=d5mdg}I9G+ANs*rQP`Kw57LWkop2ZE*ova=g-t{z`>Eh%PJ$*!g8Brx^RjvW>D zFizqalW0qnO&2rK1-vK+ZDKm|7D}j=0UMn)3r>t3%OcHR^s+Q{G+n0hLKa6IJj?7r zBb9SVS}~n?nnk1<1-h;yRDn_DP*;IOoG8xiFsUIm3j#t)9bHy(gbR0FgTe5-#NMC6+f%dSM1$+ADwC0jBzn_R8vRp9HMrwxyGWIn!^R^ z9M|~~S6zDp7Y^sV_cwl%^XK<9-Mb{lj%v0I+he8^kBmlS=h^uipTt(vuxeI}J9`NE z$l!L-M)((XYNy-H92EgD|a8GIc#=*;yuc zl1Oe6l~evIf2qP(f9dn|+}?Qzf_F9enhvE7#+EqF*Y($2&8I#3G2C*;t&GE%rnBsi zs~>TaZ+!N5=~dU9l_x*tv;NY5??3yqTlmyZeUzTsy@@t91YfUb*}Aa{J*Ir1pPE~N z<%W#sezW3=rK#W9Rh}n**4OCo|IshWY&QE#KgRF-o|oxMU-~K@e(foQ5nf$UGI^G(W|%;KOuuhd zR{hk;k8ruerBtjAVv6-cn=gcAd-j!NS^ zW2~CPE)!~t{~a=?)vnvzry2D235|d+Kck3!dn5Xr;zSyYDNHNg-GmxrwCa?M^QDc%^X%TYCr>$A^`@-T0_-j@O_=e!|ZE)A2M zEcJm+k9x_zZ zCil>Ek)a8y1w_1Q4O$;d9yA9>?CB-`j#BLRYwiH*yd-5!FM+NAdq9N04XXEE@kEJf zyHpvTIcjv@JcdmP#%7tmmK79O=oMZ|9uzG@LuBAFY)ry+QyH+KGSEw86jx9R$i^i9 z$O9;I1Ppgsx9AG$c)9dVhxgDUWW6I1^m-}$UI;+}@wD1dxj>tZBE3i*Rq@BPnn@|+ zou^5bLFv8F#7K6|ie7>FYK0=yRn4MpaZXrvv3zj7eqR`q8Hr=!l|>dLs^t}C<)%-Ff%#)+i^ z*=i>EKui(5#mFS*@nvBR3abLV6A>bPyi7J}su)PbOQQB+0DELi4bb0{#(PcdrG~jW z(o(s4J|$Oy?Rtzaa_ckG9p-U~M_6?$uG-$>-u)$8lfcF03g-6?lrHDvE-xYf1ewkR1o}$h0zPY2_yz9=1$FVBTc2GUP}X6QMHi zRSh&<#*5IF^U<#t4k0BNPXeI$%Ap-5v88`dtpORDm84Q!LH1B;+RUW39P|Dml7X$N zL}TD_0ip5oC(3n^?i3^?MwsV-VzV^S6ssO8w7(0-RHWgh29~|l}E=OyRMnSv8YO#dX5?5F3 zhB2qF-DP>^8qQs_PmGy)jHIfKP6fhi4iX3T(VMO>PV-ft`%m=M zZ~S-iw5NaWU)t~ewzvKvPkhqj_0;aeaV4&;mv{CBbmI6D_d;f}Cx=^`@OnD6l*yH9 zPntG~wDh^h4`_nE>5ANhCOJ-@_(Y!ew5RABfBm=Q>CgDQzuFag-jH$j#a2jM*t?s* z_2@6x-}}R#lhbET{iPk=x4z)}^(TMw7rEiOT_PPJ*Cn62%&GbT=&?RI4<#a3Tt!YD zXhPH>!Q*u8<@K(&ky&vXH4PqNMJz&0*KDa=(u$JyDm>ej_)=SBl!&MHr79@Ja?e4q zn68c4r`Y}}m13?holMzTVt>Vc7gvC4{jfrvE4vOsvZclN4FY)&$&0zzOE>)1RxArH zD_w6XD3tcfm5FOsiwZ7Y`q&#|hl0kQ6Qafk zs0;=8raxK8i27mU*JqXs{ob#5u~kTUt-$Q%^hcc7*{1!@$>8N)D;qW@=SRfQ{aPSH zQQhw)o!f+^ng6{n{@2;_x$xKhQI$Buuz$l)y`E}|tTWVeAIGR0?FrdL_HE{nZ~Cc+ zfU$LxVfajB?Z@x^@|&=>jY7Vk{_3S;U_FvncBD|%OAGl86~_^b`_P<`_4Oc|3)=nr zzmL}KN2m9f9?#|PBS$spmsZ)A{;o%~^jCVHOQ!02KTU}v&5aFt%x2R9E~mivIIvqwhG zsZ4pT31B2pM}S~}4x5&? zgZ(bjr9{B7YFYxd(fDh}iODuQDW`(6-|Vk zI+i)nmfK}Kve>uTxGFixnV8KREl!z~EZiVQ|DQ}kp{{$2)oZd5ToofVr(C`t#sO+) zdAzmjy+=x2$#BE9MH26wL5{p&Fgxrxs$hAlo^1At5{0G;q=ZWe7aKxu8Rdo$8!C-d znwX}BnKn$dW#%KEjMs$E8HoweZ0v!g20al^auye-NolFs%iHYb2@4$=K|NRIt<6Fk zg++2S>goMiO>!h((`8qr)=EwW(W^Dlbjsc`b8+4=UnwzLHnpb0eMpM=Gwd1%80C($ zGoeB`h(eQtWu<2sco8eLML8#>Q-||5Ne9KJj*vUXv0-a|4yPHPBMz82rE=hMUiYwL zOI}5~WlK9>vN)Ktnm6n%S}raZ+;;vPpZItG)?RJTV0Fw5HygqQ0Vy32hprYZ+_e)O44tc208j&MB@N z?{M8{#?{q`-Kd-@i5sd`gpWiRJ8ygk@5(04vruK8Or3P@p8I&rS3ZM(_S>%&aqb~G z2k-dRU#DFj&>hY>*(TDmfyEr(E;(5{#uX$HvfJE5eQaO9@%rEV3n@50a@XBF@l*ef zzU{4lU~8OnjK?QzeXD9#ZA(fWZdM=beOJo`_ny0#RomDEq4!aWZF;Z$Yr8!A8Q-k` z<_BN)m(sd;#ZUhKJn>17*V89I5$7zD%K56Oo{DrgB_7Z)o1Sj7o@MtyxIR{`-_)dj z;!_H)EAD7M=AU}`1Wn6?@Tyav%rm~|%k-uH`X~NE&hPwi&aeO4e|unUhCZV?n`K3F zrmh@^i*uYg`~T?gzx|zm;n&N(_nhPDPx*R%%}>3K>#m)WH8JvIg0HE92l2$5Shn%l z@9n9J$BcM%*7+Z@I%OwY0ZSos@1SVL9 zmi7Dex#hj2y=MwY&obDs7pPvMi|(i48x4$sTAzpZ&=IlF5t3OsRChV7d|fQ|GWX4M zc7&sU&l@3(p@?*Ya z;@)4^S4{ep%YnfxVRZY-WLN{~Qsfo1M!QcFs$k*7tegJ;#Xq#RkcUCEzp zaK0*($3)Dj_&X9^XBp}#CAzev5{t0#&Qm*Ko;r5Up5^)*KY_!P>3~>L-{+}e(J3)z zngoq1IU7Z%4^HSr*w;jm+I+acWaL>inI>f#5pv4xcacRbvh=95Dbp4yafh%-7L9Ni zJEXExb)ky}m2;Mv+jg0NFi)8_B~V9FA*Dnct)TDJsRoUZNITP2oFr0F_hjN~lT&NH2C;mGXIS5%cnvZ1hl*s^LnmMK~sh(M)@ zm|J$o6&DwCs;Xi!Z)^egHL*-~b5drbnv^2W1-7Omx|o?%9u?S~jj1bGHj$j5i}ny4 zg+TU>Dg>NYf)C`%d^p}$fRd6ugt|7<)UwJ+3C$7FOp^^# z7c}8hq{=O0qD2#lpxM0ZW7pxFGH)G?`72O`{Uw}R2^ZU%`?@g~)0D$hv(%b}*4Cv* zNGa2Hi7cKL;oN@9yvw9$*J}u#fYRZpf>4Eu?McO?mNjcw*46ZCjKSi4+qt zD=7=BgCz$S4!F2K=fZr!#pQxq=PPbGIAC(qbr1T+@0Ry|fN%c1f26nHemgFB+MGE# z-r>|_2U6m~a*p#-@@Q>AOe%EI;6P2e|GN|k&YDj9-^_?c_!@w=@Jr6g>sy)mw(wy^p*eqXCBhx z^*_D(m$~u!C+mE%M?I>|w>&*b7b79qS{kaF@py~nygOcj-mHkL*6dIjVm5n|^dKvg z8y!34y1wV#|K~5H(7O5N5AnE9`vU#O7krCeb@~wq3GXY$VP=!Y0^~#$ z7amAs$EI*SgzD(VMp=tK*YZWxG~^50lqfx*p9|H-#7)*4XA76+sxuGcMc@7-`h?GT zrr!GDJ07xg{J}r_5Z7-%Uf=nLe|+rgSDag)FYn3}-H{8R^v3J9c*19WxqkBtzw6kE z01x!*HLrd>S6%(N`kwc`kL}YV1gNWRsxZbmPs|?g0)nF+PdJ`OAp^B}&&xE`=fqTn zswYb2gvXlxDJ8@^obyyc7&$AWy|aeDl)^RUOjn8_oBmqNZ_L>y$k|}R4(PJc$er`J za*w*<+VO>So^S|VKh@2dwgB9GWmY69Hp;QDEU`$0kqgwmBKVqVs2SB2b*QO)FRAYf z5^EeNV(P$>pyRzS#h?A$Sp4J8g>~_+Ho@-@VpiIm=xh#AWcS_({uqNiYc3IHCH_}j zDei1CT{%ba-elO~@XjONt*70&hz+#(gnM~(+3%&YzO4%)XYe@z@4Zp<*#LqSzk08# zNx9e*vR-mOODUXX@JqX48h>=CE3$^Y$wq|P+5)p-2cXzDi(Fis?bm{Lc<+Wat%nNw zKK`$-qf1(mI*w4gy?2GpG$^_)JxY4j!bU|W?g~v#Jpy^_iT1%e#90Qq42%R_ztxfA zy0>2F1|?kvTV{zPxD5U#N9xwHUYGu-I=QT1?d9`Ffbl(Dexp&anM^hmxT;4A@d3N! zuGo<{Qt*rXIkAp%iH1V-itU5Ve#s>PB$p}a*PaoMuv9il?Q8n}mHQCRJI4}Gv7Bcq zUT76(0I_IMX>GQy;3~H4ujXVGnsznl>COKehYQy>^qs|4p>#v$p=J8rb=O_o{*k-N zqtfI$jhcHeVUqVYY01onCMFs+@5@0Us5c@kumyOL4EQC%7SzL(sGO(r=I`yj^P#e= z*_13AZP^%Yy=@b#oQc3HD~C#n>yRQ30xzH*63Ix`BE;hD$f_o(%@k-!Huy!#BuozO zQKFVCb`yJ(MY*S4u(xW4E7glIkBX=eBrq9I7*`dC%gDTothz`w5*AIOX$;p}v0|^QX#I$Yqt!rkm62oBk+K_#Yp0ny8$~;* zO{D_~uNK8dR>TY0c~%;TNeICr(ISCGuIOBiW=P2h@B2|$O2uBTSW>ZHV$)1rV%I{G zq0K_KZ1AhKuxOLHR^P=I-wgvSZu9^pRdSvvPCojR znVj5W_xh{Ye)#o_PMsu7$E0A#q^pK*z9cUjQq$R{1SKZhG&R{gl2UoXDB!(NjVi)q zO0~7abZbI2t4Yq0FhJhUI50VRic@E=!=E_I$uQ!kyu}aw!8_%PzWtjY(%<`iZ+;86 z{I7Q)AyA3uASxHSj`r}7!^4Zr@4b(1?>zC~BJJKGO>?{gckB9^Uwiq_^9^6}E&8YL z`M`sE?7#8G|H%^{^9=nL-}*!PNjIGV597M#xTpTdot&Tm= z%{kKKh~}>Kot-}Rn{^;+wO`~^yW2a`%h_h$@FMDquY+ZKMhyB*td!KXfeZx2Ppzc9|FZE>)ogpS`+Pdx>G4ue;A86eL zgh1%K4t*Dq!XJFC5C=mqMG&AkXT~N*fEZPZ4c-^ku(}Z@qq?E5Y1FC3=CaEwD9l;e zP6ZE~3@w&o-y5Q{8eJRi5+O5{<0(?jP<4~OS@F(5>d3iwf%L7{aSlI zyVfI3JiJCD!BI|EV>GB5IJE3cm98Mai(wKbPQr|?^E8C;Cfx_UDzkS77QoNl;%AyU zY`TDeS&ymeN=I7j#$cEk63*;twdi3; z`un2#bQcJJHw;c!!{qh&fx63Wo@Z$XUCB$I?j9{&=Z^;-@j{Asm5Pjt)OVnRJt(6B ztcpV!!K%omOgDHeMrjrygf?Y?(cELO38|{l-PI4kA!n@ml~2Hc;V=Ly7%XkvlN%K* zHmU3)qY$HH0mlfa;`w6q>X;Ik5;`5QuL2H)J4Ha*@-c(!%=bBC9V05k-Lz$*&ty@A zF1WKeCTGc_f~c->X4`p3zp6aBJ(~WR%p%+#euC$9n;BgqHeEXuQ56x4j?LI5xC474 z#&eQ!K90Buj9W3_Vi26JS2zp@#I!;eB3`)MqLzvjjQW3jm(ejnB;cv%w|LU*2Y9Vk14nsiOv&NiZ&HHMdu-j%36N<6U zkUL<%6NE_U`Uv(|G-99&HqC5{)jip!sq0=DLpANYRQ2Tal$&!#xfeXD10tgdBjVaFanoZgetnRXnjRZV^-;0Cg(Z zJ6&ynn=>kq2XQ)`BGzjA?JxjR%uG>?IWUZhS}d-lNQDdq63k$X`VJT}_Jbap?wkgyvLSoIw! zFz)O}L@&V5QUYLk_c<&2v_|F($OwX>4;^?F5fn(Ph~N0JpVc>hTLm$_{? z8c|?`NQjiMq7`<5!GRH~Vk8FLxQVbjLryF3c8j-s>z~J4zv-_~=sWzY{(8rEe2<2g z-UkBVTDQWE7+r`MLqG@t+cM%NMT`Zw%mp8P|A%q?UB7~%R%Sd zxD_LYixJ=a7yl!D&F8)vU;hnXO>g_6w>YZtNg~JZ`w)Kir{9Uc{k=b^Pk#Iv+Jhx>SKf=ydE=Mpn_v5x_-p^(U!gC3`xoKUKjT$@=zH{` z4}Jtc{nP&pf9HSrLH)Jgd>1zTqqy(JD*-mcr`YwAk!Q8cr-=ed?%VOgb1&e>f9z*b zC3`WBd&>Px9uRPOc?VsWj!*?YqNmcN>z&Z@MQ$)*c6|TQAN@(!P1bu-CtGdD9PnGe z{W}Vrf?&o^t;uAjg&y7RMb{}Fxc}Ao`#lHec7M=!ar1e z+D9Mz82-^ec!&PS|LNbx3J1LOzE@xvDn9VLAHvW4)H_WkIGJB9;(!)Ksy);9fjLao zpSeF1l*Gp!Gd#CB>GA55NAZ2%{rB+g-|_AGwO{@9_?oZ#D*Dnt`vtgh^ZFm^c^QTg zzw`^gf_MDzPw0R9Km91yIKlldS>xu(+L#t`gN_4~qBjDBb(e78CPJk^HR1Fj!Uuoj zecG{5F5~3y1CKdV2Vz>&^wg72<0hR!q=E$)$329tIX{$wK#I^suVE@kyg^}_vbPQ5 zF%olK7;GuAYt!v-V_*;xh-Z^DQiiCx#|xUams?;NiWZR9CsG|mjL{NA7O`6dplfD` ziV47(bBk9*4@jYWh^(EwP$*Z2)aO?%T6=LBZ3cnkurivmsdXW@A=iq}z_oquzy_u*>GV8#+$|A& zEMQk30P?X$TOD3yCPWUl2Pj%NnBN!i_p*2&u9U8hkWrMfo{|eoG`7$jz;ci;{JP@FJ zQP)ZD(v?r6orO#Ly}9ehOaMCOzu(>0%N04n6>xv43OIUO?K=OtH-C);HcQ7`GuCN^ zUA{!lrsb){7V;qv7)vqA40NJ0r_1wRl10!-iE_Yrrt1qcBmg44uiBF8hrK@fxhglLo3d?<)L+mt#H zItErj&?j@?C)hxusx`Sop$WY~L*l&|%Sux`TF2IS_fc_98B$~>?Wm!vIV7kM5)~vC zXssYsREp3PvCR>q`g}Y@m)KR1mDWDceS0%D9P zqhOWTwAViMHaAj1SN7<}0XQ5WRX{a^Aqcf#6kxa%YnPOKaW5fyYCu!@AsVp-!utrmEq6>tMck%D#CVOt0rQmofbQ%j7lR{WX2_HWYb z-u{JuBy`a6&IR80!|%|?{?B(|sD#)BjQa!X;Q+~sdU=WQatjR%4$)AN^$BX1Fs2o{ zbwbD$oHIxjfdpDILd`fJB7?A{6O=q6Y)HsUkC_)Ylozy8&9-=h!yk$lEqf7dVK zzxc|3M^7F;!6^sam)7Rx5IckraTWu>0ew&ClyLiSK&1|k?;@oLE-v=i?soXt<4@v=$3KquzUQ~}m*4eXJn?}iar*EH)+^iFw^X^Eg^L(cI_442J^2DYF+PFocqzX8FT53>^QJf8<*#^@9)9RS+`M%Y zC#M^v)PX=4h7lio;z@k$vB&YD4?U*8@tePcfA~W`1K`5|9>je&PQk%gXaoX>gc#za zwNz)TguswmY(VOK%v>sNUpxh%_;~R5{#vabM2Z11^oANaln^GWrxjCX3fCf!#A(QL zmN5aibNR{8&~M=EbdAV8*b&@iF-sgkj_ezgAwWUsQxDXF$DVo)z~gw!o4*`?`p>)> zuY296)2F@c5#0a4Eu5X5BE*0fp1+OfpML?*KX)5XJ@piR?LEJ#@BF8~fZzJ)ui=dE z$N9}}%43hA2TR1$Pwz3{0tGv->}2LhqQe{>lyU3&{YH^qUAtnC>;*g}NAenX)X;ST zVc75S@n@fd;G_7=m%a_3_r-6*zxIaDqL)7U5FUE?L7bhPqhBS&7_r^%aOd^~ZolvX zKKT9*<3k^MOn>zk-h-e2fBq?e2l3Fg`w;tt5ISod!x|}W{C*jF7&zkkrpI~DP>oPI zxWSsBTn;F=cXP*ks&j~&By+R)P0h=Ghnz>MgK zbu+28hR>SgD%EN8uIqL=roui`;9zFQOcNEG7Y&dsBD_?^SU^m3oSf^_6}FyLbc)*x z&i05LY)Gbx6s+!tm_)UTz=@Wg;cY|0GCQwLLpZCqdCI1T?-lA9-gSXK<3;dQGY4d2 z6?%EUri=DJT~wV63L`ah2S&!!~rr5^hVy4)P!TU47m z7(d0qk-8p#$zY;I4B9-{o@NF93>x8N;pTqT7X4^COh@vmdtl_hM_K6ZV|(s!w|v-txA$;qU$6Kg2j*A`cmdlF`M8!#GUS zsz%WX%l94J3dV2&Z6FW~`WT+D8nf;{4qNo_Owg1OiS^))1-a1LKALfOW^%=ZZC%2}>Y?3i)PU(07b+RB*7TEOEe4 zEIMp=DCoL`K$c^_FRj2dS9++T?*qnM&_xCZ#aMuKPoThv&O(T!SS7~UN)g$RZ3eKd z)|_9Cz;LLzzA-n|UA6;<0wY(SVzNz&Srs7w*ET>X?)WQ;j?ILyifLrltk5b5RG~r^ zi&hwyb=E+p5G}Ta1Be2cdwk*7d=(z}w3p)DKk_5k-+l_)?E!Z#Mx>6xFgJM{sRSLv3R*rD+&aI8`_3Q0=YP}J(VzHB|N0-Qv3uepAIE?4_5XqHhJ!h~ zL2!{yGJqnaTF$W?L3Hn0ri9$1~Co}420 z9YX97nbGAPxKywfq>`ahFs{$APp5eDj)0Z%hOhX1y!GqePWL@>AO0x6zU7O*QJ??# zV;~mX*PpnlXNLnNypRQ9)k8U8T?OkzI7tE9vEVt5xE~o&26Vv+{Y@ZXb%K{X`Wn3S z)t`aKKkyO!f4}o+`(8q`_m{rMlUZ)QnoI?Ng#CVjd4n!DzhR1nY$Q5Gtxs3Y|`AXYCvoRNOrE&US~qJK)YP zV~^1rWdX3pLwFFc`BR^cpZ}#_MMA}9-J$CPx-PW9B$NMebn3JZAOMexno%}#w%DX; zl-Uj0bfGpt&|0Sf_@XkKV|74aPM)Rih47KaQr+ZACv9V+%F2SO2`9=|$ybmOLmtiN ziZKVPF^Hr6u7%MOIkz%!H<@WQ6s(H@o7CIc9}}1>9{@PG z>zfr;n{IMzW@f~YrW&AKyHYc9&5I_>zE{OqGTMd}Fyt|%_0)W{0+2um2-Ewg$q#(i z+p<%PhjBz12JG$(*sEf|7QmfF#mE2>0us)#0tx9j2p~>i?hxY|fmdMih$vz( zjIFroJ3MlJf->w;YlW1(%hLq44yK28DP{45p6YQ~!Ogh1q1Y6VNO3J~?AhmlF%LN8 z0YfbS5o#c;Vm~F_Hv}JWhj#(8ng(5_*gi8R;}EfO9~f0@TpN-fQ<$j3n=auz1Z;Pg zC?!uC_SOuFyR}P3SG?<>3 zSu!{~tT!cdtqQQcCcCtwx>LNs&Ya>B&PEp(sR#sw5COKtn-(h9kW86m1#@H9oY1Ld zhI`Fr!wK8-m>G!~iD)8MPfTK+hYF(})2IQN9Qm>E&!08hn9L*@m?`p0W(shRrj0>l z%`Rw;^Rfornn#624X2na+Rd4{BCSD}iCIupkAUbU(%%qhZTa1SrZl80s_vt#cEcNk zQ|T>UulWEplTEHV_-MV$-ki@B=Tw2Os~a8G(cVVL$?-3k(*c zEQ2GU1tn+X(h%Tci=G%MTBH{F#LXhuD%LTfIwhp4_+kUP5TLa}Tyve(7U<{=X^Z44 zK0J^{e`nLr5P%e|y^Jl+LIs@?4rH2SMn&X=o&v7N69g!3hJ~3!jZ(t0=0Wk(70=i_H zB?fS)ihVY>(M=3aTk_3T6}z$Gd>ydMmc!VwX|J*Zof>6~VnASE9L<@$(IAJSSa&AK z=zBsgh5!dvZ~#&?^N~CV&QBQYs7R5f$>QZfF&4$eL2eISAe6L>P2F37oe4pBH_l(2k<$c`zAc_ zMQ_AoKk+WS|2O{y^5rGAxjMzI0wrUetT?Qt;$pApd!xG^ieMcXeKe1Q{h@;0jkdy~ zXfy_JwF$qJ3W^aN8Cr~maPG_lC0HaP0kskete_jmigh2rEw+O!&pjS4(H-s}j600G zf?*4kA=@U-KtaG*6=f(W!+^_Lfm$J@Vy}W{O2rGJIEw*U3D^1!V%eil5vSMh2c2KT zLpL8r-}iXicYPDxdf7vtR8#lvpZmY@J^$`^X?Lvyg#aeS&C{FMq}4R_We9jOm@(tL zlNihk1B?X4?NYG`5m^dWVTGy#5(V7JBl^%GlVastB(q|*>QPF;=YGkb#Fu^RHxm9B zzMlHkU&Dvr`Oo!L5R__03{=H#F$ptbHody>FaK82wkn{v_kefXjS0! z6t_P2EqM7`-$<|j{5K%P@JIex{Q0~yD4yWA;o6vNJwnsMPN!?Rr zp9cg=)~Ld=h#B6Ph#-WB#1ZN=RHMflWqc?DQVgi2VkigHn$0Ug8|oobvn5V%3=|fH z-|Se!j(m|96jcPZmZ_nxp3JVn*g2xr&N*o`Q=UwitUeI`Ols*L&WaP6X@Iky(QubW zJkv>AYeN#tHYYc@G9(YGP%kcLzT}J+Q8QMWihf6u72Yh}9gakcaRqsKE8d(d#C|Wa zUqg8D?IQv@XA@ zk}-~$P->pA&g$2)O3RF<*#Sv)%0X^ulCjJ9cyPk(SsRQ<2@4&rN$q4Ls7Eh}Ra6-ugavgo$ zSVK@fW-&|i(GJj>o&M~PownNR9^23akGe#^nt$HKsi9n8;leAzjW9Wet zDRx*zV=Rd%autlVV4V_D@Fz$P;&smNzKYtn{#LAK z3qIJ8MtR4QMLu_aVN%%!#F?DgT_(^{yv*+Flf(pfN3BhbIeDi0!YuFiHMP*=m@~RM7|<8#oL{_O zzp5d>x)x~O&X0e3>1Vh2b@Ziw;?Ij}b%Fl1Q37^)zpU{oEoYti|XvAS6vw+S4Z zSyz|Xhi&R{BZSl$&AU{^`FTVh1gkY+6>Sb$)lEyP(bURVv06vVu|8yXiByaS!D_<@ z$!Jo6JOfq409XQQwI-eYa4GHr8r$>Dio-#XI&++Fd&OAoImu(iYMl^5L@mJiwKH5> zuW@I838sp}c8_%u9L9=LtWfSFA&?cf6^xVu@>q<_#LuBS6v$Rz?>J`ojh2u!U|9 z7`7F|q2f@o4QGfk@(QRU4!aTid_agjhV2Coxqt{5Pp{!*)nnDKu%-x6#km6M^c1^5 zxPI#)Y?Uwy@Z4^Xule3@p_jkz)xYn4`NA{LjY%Ps!x z@B0twChr=Qs(X~w__{br4{I>D_+ zAHYMO_8_jm^gepzb)SLzU-s}H>wACl@h9;Of9e}`^S~OS6%U-h1nUrRXTL)iI~=qi zDx)x=>sQ$35z%qS!C6Sv5u;8OFJZy0O%J7tzCXvE(qn+&+5HZul97-xs$kpV83BwS zT$F-CHFL|cT6E&B6kH36^L_b8q2H&DJJAvL1KlvN=2;TRVr_UTj*iQ*Q_!MFYRw<{^~IEJER(IV%4~E*ix#~#(bG$N_C;%B z*!@-7Fd#TPp!E|Wnq&cLQa?DFNF2f3nH853V(iRxir9uA3r(n@f#htug}!z|g`D=< zP*xujh*rO5KI9;el4Au^oSHpDkL?2nbPyfA!PDvbQ#0{$)3G+_P^~Z3)1axZFF5gS%mqr#{#lQuI?3t&oqXCg!@9=v{vGaey_1BP*jQa!hu z?LB6H?xosbn_>LlEC&!9`#>OW^c4Upi!5+QzCjQz$Wl;hHOFcNQc8w|OM5oVgJ+bA zBJH`f+KxhiDzHisC;iILYhcJD_NC(f%?9G8vML6yKomk|I9sZPl#!a_c~_hSMfGc3JF#Svp`5*kyo<&b+DINjH{jOg;ljLE!5cn;EDNJKEzV(`wjh$w0?RL-F?_Dyr{rqIqc=Yl*A=%9cPN!y_rq4KeljuD3 z(7UDu_tLB%qxttl#2dqa7DI&J1C&3KyF2sX_^;i+LGLN^KLYdV3qJcRB#>?UMaAek zb@YYNGRzGj9^1s2!opZ!&hCpLPSFc++G*r~K6QW!x@f3x2nj@tNWl&$4me@Yh7^Hn zL6Olmdk#}hC5S<4RNKyJpZ)sJ#_#^l?_!tt z(5mSBWEAdf<}B6eP|1rH^{G^&>CO4>f*9G7J$!Qz6{KMAH&Osd5o2$Y@{+NRJw~-6 z>rg9JDWYRSDT-G;`Y0}+d>%-rsbbY{$O+qaK4TM7h5-WDQjI>JjTnvIzfe;BPMT|LHlu2(5YRMJ5%L8tn_Q)#W z)S(NEjuI#^Yjd7n-fGZcpShWgsy{x-c0H!;G_EkL;`Ma4|vjn!t=Dyj2}JZjCN;GENf_Lgqa)+GFFytIO#=37Ir$Q7}&0P&HWq+RwRuc}kS`;RAD=%=Scz zHMosh>$HDW1!+cAAdpT?o|(0k>HTs;fc+ksQ+cwyYypU>(fLDkeJvw~4v*eggLcnj zf4KC4lntAd8HgQ1HABqo=47HK7ZIt*TFoU}#F7%6We|v`;eu*KhGHxk@N3y<#a-&r z#pDu9yKWW0SS?McN|`kJ=93e!=~g)JR>=8aj0jQlB=8tat#zUc1L{dD&6!&e9T=!i zd$u}gMvAMm(lL-7MB9> zWK=e-dvDO&e@AYP>+R?ps_j{oxKfu zbb;p6i#M2h6)kTL^uRsP;2-@XVcJyADt|< z0FJDSx|~AqEg)Ya==bc}u@sCin(n)y^QH8C8I^a`fSVFwo)KJ?1>Eb;zTmU}4Iz&m zsAt~dCp-K;3e0u;f2dBQWad|70&t|FTZ*Jq)B=H;tnqJ0lO-= zl#J+yXjVe+$8!QBlVTHsImX9;gH#{@+g$B)JN+dFpkv1EQgJOXDA;xGW59FU0kzu0 zogrAMWj`he?)dbLiLhp1TLlooDjJoufnH_ zQWw10at)0%3Zli0nS=id*G{fsceucIw*#?)6EKd75C~l))2leTPE3$uC`F2lQY;QI zk0#q7Qk2nX<4`g8Z*~Hh7~<6@oA9nWo^nWQRd>L*YQ66WryDbS%VWXeAlPh7JN}91 z4!Cx{LeDnE9;!R7GNIH80%5(1*jL7|ACN1s8!}G%&fZrH=;6)%zk&bv-S5UKC}JI)DlSNeE%>lS-43{XK0_cFnX%V|y4nCCU_XqY zT2Oa8oOcl<5!*;uF))OLkpr|}VZ|Mk6nzM|zTO~lhda4oufPlY5r-nUQ!;XjC?(^u z$3BeRFyf(4e-x|J6`uO&(|G*-Poh5!=+Anrx`?8JJ_Ov_T*p~|YDJBgG>ty23%Vet zSRr7LibQcL46;wCqr2L#h^MAv#R0j|qc|W-LGRCMf`F>xO<(pl{N-=|x9C&t_9yrC zkN?h(;D7mB|3K3wfk1fR+DoAB6;NvitKyUc1}G{q0)TBT(~z)vLj)pJH-xDQtT^JK z*ke$^rBCU%Wsii2eLX;GK>*=25;{jjiW+h}0N8<0l+9N`j5X4lk@K!Y&x8uXSTaKB zFqYAxAw$4e1&J)OuN4*oOZR94Z1w7*)WbwOZ+g+3iDu^(r>PQT67D%fAZ9tO08mb7QH!mQDN=8*{e62~SNA-~sP>V5Px_)J6elAA$ zjRe++w|(B*@a|uFH};1+kZSb$TI)oK*2ziTY6D*AG;N42Q}c98e3UB$#f8%_Eh&?& zrMCo;X0l^6azkKMmlcp6p*yW`oltCUC@&W1G8XhmZ?>8llHasI5ya*nV7bg4QtT$j zYEf|$F|W?a5zN-aPFhhHSLtXn2Ni&$(__nG_@Y-di#k=S6REkS%}{iGBpJvtrN)zM zQ`sr|-fVc|I#c2cn4|>gAs$=!sY_R*vFs^M*K(!(SHl~VLg3Z!!4P?3N#hY*Ow4k;YSY^^-H%dDb zLui(fG;fKjYJM-?Jr&p7)6#IQ4R)sIA}qA5UOzG)A?VEwoF*;0KO>k?*1%4qt}L_-YxbUK zIj%b0-F=;u7N1vtALBSZPOH00|6fY(muBJs5MKR~*M6r(cNm?jxY35XPOa2x8dx#Y z9rD6sX|B@Zso)4!m5LON3YN<_8O~(a=f`6y$n^jY7V}UXIZS@zgBNa81*0gmWXpnVMF_Jsge?|=n|U3B zNoWe?YKR!PLyR2?M`%bkh37T05(?Yv9csZv&KMyWt5GLU&(0A}dfdKq8_zuZ zEVknTCpUUwxx zfN+ru_G%`hU5MzD(Og9pSu4U6o5Rz*Ka1(gyAY=aE``oeL^4!c7UpPWNi+A@Atp;g z!Gs8T1XGLtBX%8Z_19ul9}EjMcjRD5yO*mon%g8`UXaq2#?;?)@uI=d`PP`V0-h(N zK}9j-aV}uJC=#vs@JAm*E{7>9NTE)n*Ew2-^vF&3Si>l@IaP~dS|o2wnsZtN#7^dO zbfw_K5e%^;TeiY{id?%=@{ngFGl%IKsx!BY?g{8}XwY(Q9jPX^=3AZG%HH%a7{Q-c z(*X1I=hhti$ER6y939a<-wYMfde zU!XW&EfqI+vH_2J#y33cdG42H$9!(rfo9j}2K~g)x$F0A+DDCX5+Xny9ysg3haHB| zJQlRpISxt%!D*|RnvT49Q&>%6>1jUVL!U;=Z&80r4HY!KK7kQAxQ>@W)Iljg*rLwN zuO%48n}e}D1LOMU1RIzUt`-|0Hl4Gmph`v2<@;z6Xa>Z@3F)*{A90#-H6jEmS=?B0 zje3zXu@;-bSo1|{sA;>I0vu^-r;O`1Orm2Hc1F8(LQE&*)R~~{zgMQ$oSU{~v(p9y zbQkuhb9=PVeP0DhX|W>NBXgVc_`*VC*|{#7F4q+++(Bbz7ERIJzP~OqALe@m@+hyX znq^*mf@48+GqY>Y{Irj_IdBZYXy=RTyEL4W)bDNDcxL*NHf>C!;{i!SnC1714I?ZhLr31?Zq_${j>VrYc4NA$y`^?< z-4t*NET^LlZ_zla_5+=`3^)qzp(PvZ4BO1NlEr{0^gQwF=%z5+oz7VtD2d<#z zbfhdea%{%}&0qcu(Xp!Q=sLB_!VA`czhHRnr@i4jK^C`V4Qhv42Z##xr8w#<(_~6y zo<+@HGI=Mq{{?U4``F_!j+V(AlSgmBBR~krV{3@m8a%*L(Fxn(V3S%>><$C&>@F>y zMjIW?4eS7DE$I46fv`02%75ZyeH&ZF%&_^?l|sittb$J zO`;E@(O&zGP_o6qi4P~*K!-??$AZ=78b181zlk9X$T?5hqr`-oE5;(O0k!LN*jWRe zv!UjP!AMiJDk9r^a8DAajahTfPBA?UKF3ybWFN9=ph4#NDPmLS7(5EeR~$pp1*7y2 zxk7|6=7Q&Twt{xMYzzP<>;^#>gSn_yLYEj_BqR=#>0$^0t1cpR5ocWlbqOzj;9)%a z=p)!Y`vOADSeHFIm^<&9fx)%eS_M=p#@&drFQ#v00(BiEtx!`3gn*FznPo=4b^{;& z(35x?5hJg#l>mtmks}Ziz!BHhXSfhWj0w+fM{I`y&*h2{Kvuy3F@OE7)~+mSRf?+(EQhb6o{Yf{R?Sj$tZ(_o`S$ zMlDX`Qw4>9UCCgV*Z{y#3%bQ|sR{%&sx=43Z@lZ*@ya*6itc~pflqbce*oS2w|vF7 z>H7W^3JTWi6I@@NLje>r>f}Ky5;4{^HyI2<=O|}&j|vWi3?Nc8LnEp`4jywM=1S21ylh|8{oKR;iOY&jj2<{Cu4Yqh)TlPD8M{ zqL~VFuUe=Lud!_4aTl^PA;(_LxAi?Eak#6AZxmXB+CIgOIKvCwscM>=9-T6o;b><= z_Zg8R7zrGW;%*4J4O)nJQMKtbe;~{#=d8uFIx5D5uIo_CY%Kzv5MvxO z#&Mh|z2xr!G$)?VF7E7gF-@rX>;UchUY>K(=g+EBE;o5Iyd0IZIN1<-Z;qOdzO|77 z0>&z>@RI8%I4hUPqn*#WWMg%x)m!AIgITJZXw5q3$<#`%sgNdN=HRq*dyOLEo=EJh z3Tt{32ShS28Wosf95GU`CTqP~qYI4Q8?F@-P%>)HlNW#`Gqk~n8Q4Jy!uCE8qQwkD z5QAN->G}{uIZTYnEOi?0(PZ0a;6>B4QhUakj!;jF>d<@(bn-o482Vv3bUQZs zYY}QoV7K}(u`y0Sv#}XniI`jJ!gVoJn%vr#O%7dY^myS=-qs2;)=l$2xsKz=ZC&B! zX}G&_QtwHMRM(yBB(>2O#cYoO(PjKuaE49uwxC99XIn???7XVg2O~5wiB$DSpwm`P zYdY)V3W@LQ?d>#|{y^lh$26_U+32o$RQP~OFs%){9@Xhed+M zlC`jGs*jN9i3c@*H!rsFIdGsg)-W1dMcd=gB zH%P{kZa)vECd)m2vFk$jh*+cJeV!L4aI{b=&@nAu??Gdyd)<%4UxZgb^1AN~(Mn(t zVLxtBYDKLhm|bV$bn%v5NJo@5(+al5AA%W&nLBpGp^iu~StMIIAiD!5d{V`1rdym6 zG$jZ#N~H-Ut7Sk45o0Y_g@_`i5ffkN!5z{^WuLaGX~)UQats7F8c_&1CXkpZ-ee{c z!bEL{7ilR3&qyuPL{vpg8bZyFMqI||;prF<82a8&2xdkiU@H|p5pp%;yeJ?-7^-3&487P_U=x7-SkOgl zKAVGoRl$(WkdG$qjG$!Oyf5w?paFR5$tSR_d+aX<ugwb&e8WGc5@TuWc5IOQHn#w;;*jjuc3a|1DAm++pS z{bjuQ?QfyY+2&JS_=|iU_6L0PSADD2us7e2(BUQL4|!aVnS8F?byk>gfS`DjE>7JZ z6^I-WxVgATi_YT&CPp6<2FV}=c4F~iqgGV4CKn6Dk6r(}iNT$cf$Y+y6cRXE6C6T- zI4VCoMblLIG{oFxy*5}~6ea>oc&M&BZs$Q*Gk=^5#y!E6Jr7Ef4e@zYzTLSqUCn&j%ThpG>5#`CeErh8qIs$QMrU^I6fl=Fak`2rVc4I5B z8Kn956v;3wF`6cNG6~cpcj<Ps^d!?o{oR(18JsQG&P-bw7*cV~OJ zIUSRK7g+=R(D@1a7!mUx`@;ca8Qs8(ATpZ!w@=)&>$R(lQ%#OlUSkZE9rDObGt3(OQ~osI2i)^bnT*UkW%{E_IG zrc8HHcV~?{T}4+H^mBnCiyqOZo6GiW_%K0`vv#_5S9+x**5exai1*`vUu%;M%rCKY z3U428VgWe2r6o-?bGRwP`yPB_y@I3b^6k>qrj4dOBCr`y^Fmr6n3=Crez?v_63ZPx z?jljoa)U|Mvlt8pms~VMIQ9KnEGNY4;Uz3O1!?%eo2CJ&&}>>KN6uw;4=LzHxY=|s zZqgOT&yl^_++{DmaUK+14UcPPsI>Tb^(Zp+n?&q}a?;)@Plks}*2g{5&yEa9X{qHn zYO>oNXJ;hcHFRApkR$y#z1aV!IrAO&ko*J3m*>U&0K%&ue(iS#X1wOpUxSO?1zy-* zqUHl~bCYzVg%;H^MRU-+5K5a=`=S$=PN;1ZDa1)f82x5cv1XoFky4M%`V3)x3Jx9i z!yZ)&wAOh7EbZ99lmnZc{>0O!B~z>h2ow5mPVlC0RIvlFEz$sjka`S@MO-kEQKhAT zIXE3i9mz0+Th$s6fSq1dj~}{LjJoD~N#}^n5Lm`6oF*0Ayo}obw3IcD_9w$RInSfl!^8&ATbk=?Wkt% zMvVQTA_Y5)V`S|287oqZxnRf{UB_PV+J;ulAIzuzieWHAKUFnTsp@W^1eb0Qa;}IG z({s?yBXc>$G@adN+c+9)$0U31`srEDL@-UK5447?Qx0VoAptr*No#!`rkQW+8$pn&aRL?J>A zE9B5YQ)fmjs#v9ni(Ig!4Q^+Gh&|)0t_PH{N)d-#vFdu{Yz&P^gl(xv2q**`YQ}|D z+Z)Jqg{B9G7o3(4FFtA%*A5P~f>B_;!|EtiKvi-#TGJ*I>=_~^8#ElD2y@=*Pzxei zqh1>ehzKl-eH{_l+;)T0MunZ!RqIhp>f5cKKOL#3)1n+0XQ$ThJUCCn4egXYbu&b<48*uKyTw zuJwI;?{lq7Us8Q-0Hif0wn^9GCz9_s@Ak_Qr<*+{$LsSh(-iW1k}aY4!$dA{ziDvYqLH*$beA z31pjLFhn*S(%gL1G(?R>Q5q**C&yY$Q;W$Ne4a5JQZEKFGe}MO0r&5+U;o)xwlVmP zq9e{cPN|;<5z7Q?EUITL$3T)=-frKxh=>n4ZQEl*P<2aBbqTgr3KGLgg+IRssW+X; zM0IEbIGX-m>5C7%+7O@4wl1{acI$A4M{&%2Uf}6Yxwm%>QqLQsgc{Ulh88Mc^I+-!TDf92UwL$r)hYt;_tcr zJeTMz>Q1JaDB0^Ic$=VIe%^{o-g!w|TwO?in^K*23J1HkwAmIS^_$!KpKWBi^B7yh zSwToCUgO|D@WW)Tr#GZwMo{i>*d#-&kzaVN&&o%nGT0 z(B+i~${82)Z1K6ZI@Ak5C_>aC1glh(T1y8$8#(**bK{ zARpA!(q-2%h)7Il?{Q#R%3i-55xVM0HE}*@^Qq%JCv9gfy0cNFQIXRX88mL&F~B}S z!?PIsfz8fIaCV^3euKV~cn?DbRejFSkKvqBU{Bm1_2kd3szb5D{rwFyrv6#z8843d z_cW>lhWNO1gE^hw8?+yA?588NSJ@MgKJ&59zP>5&!$0(U@Qq*pCZ0TduaU{S&%$@7 z)TL^%Z#K4!7t7x4CI>ZDt;oKiFal%(vq{NQnlMcnhkU^0#U-vDeFXp3zxl7?wbx$5 z*M9YDIOZ8;+mO=)qPe}Plx9d>8_m)lD3+g?a>n6!fo(lu;)GQM6KAX z1^nZfvPq+*;NElzf#TU_5FkZHk%B8Gl&ZL`6-gEMr-W+(wpzhtjX4EvSQRq}Gh3#l zYNyx|Gj56?lleoh-CrK`&<)8S=;F2kYxolfG|if)3I>u%Q;C2h6I2&4F)|aLEFL?O z6Ao<6eDwokVkgPA4VHaenG8}md8|mJK8psrW9@far9=1lH@uo|zrebM^#@Oc^OnrbX}}DO#~!2Wf2IyqN6N7z!99$kK*Ey;4($ ziD3uW;wF;~X+3$YR5WI>873Vh*;<GhPFvHZcV#}wcVFd4=bP zKchL}6|I5eL7uN8;{oG#e((vOQ?uQUI(hE>M3#9*+8b*P7+}Px4c~{L)7)d3()i;v zy#7Jj9yeRWj2Y8{+Mwf{L(upGgW6z(kvDsQoKweizy4L~@o0FSji#i#=U@-YpgY#o z+LAK%GVSx%KzkJi#whae{D*X$I%=d(3Wg*R>{Vm~-(dIpXg_^u+j8p95NbA_km!%> z1-~wN0Dq$28#?OQ&lq!hCifpH?t~Y^tzy6Cqk0(#xgNWd^Y|W`v zq_j*;ap{k0$e#wevlmV|B?op z?>~JX;0%${q;||R%C@woS)_tfZlu=ZG~u)?K7lm~u4N>%LrIc1lp*;dq@0?>CSow` z^A_{EJ5`vxSzW6g>WSF5bC#!0?eemRn8~|+%i+y;6gRQOCd-vD>wtS84X|cK!;Qik zInG!;XyU+=ZT7v;3(3}eARNf3?L};8vbjg`!C^)w#VYmyTSXm~V{o_%+vwcS2A+6U zt!yi`G#?~4-;B>B)ur>W4Eu>qrq8TkO|5|{5M0hjoQmQyG44&ktpZ8KJj#{&U;@^# zZ`H3A=|ohcyV`Y?QkQvcPGl7&Y@6VCOwHrpus}YQUyFV2Hz&)yjt6dHi(fiq%Uh3! z{#Mip;<4#Xo+nA*%qT*(&2vChPWCy70`Fa~xH(meW8&nm-yV90Y}*)x;xK1$CR|Ox zF#~zdcye7qq?nUnI^~gyS@UU{J0H(~NnZz#`bh@|10-XZOGspqFF9R*Dg* zi7_!_@#jQrs>SyZldCr}5tL1a2)9)jh(}%VU@<7kpK8gUJw+qNhW*zG1&YOCgvFb= zi4!O>PV0)Bb#ZyE;>Zd2iE;AR6S92Iu&_8s0F?w=t@vv{`*Zl+|J?7PtAE&}<8*t% zkN&|Q)o;J~9potkobb^HAAu$-=C)b^wmf=KMI}Ok730RA=ZH5|f%|6$hrao*y33E5w*ERri~uRup9QeI0{S}mIy8h zZmam~K7gJ%qtwmRnW0u?Hv0s3zpl21 zoH~3=BR;CDScpf@vhMb{U18B1h}EHWGQ!>zg96m**87+DG0ij9?KDVyaDwGq|gV*kk?Jmkywg@E~($?K9VT|1t>AN3*q$NM78eD}eO*kALCyC)Xcf znk<&ZJj1ogPQvT0WS}*O7#Z1bXxlimXMb|@Uni$?Da_-^q@51xq;ygs615 zpFaQ(9vLFACRQDUL0&A2!mShbXN!~AlY^RwU_L{1N_)KaQh{Z)>u|~pP7JC2j!9(O z=_w`n$7tX<5Y#u_G0-bQs6wbp$nKj=$r_G}DFzF^$$-VNK|R7NF}7l{fAciq)rSWh zCSYd4^`ba!_8OUh#}8&OOv)eB$>mgWbvfboc15jfaceGnRaLC36+HzE%^8K*npm~% ze5nR=TdP1gqjL7>85@#_DvtL)hIelk9Q^mqX#%N{>TgTIZ7EH3R*7%}#f*gOy5L$? zXfeEn@AhKV_$!N=J_e}AJX7ZE* z@eJIEDOolvir^&flY8aay@4T5Jox?ZI~Odfm^q=;1%LI+{|CPBhrdXVUwQo7wmE<6 z&A0IH{=t7w-+AXP&}7k-uik$ZDW^tWwhUnVwwJnLP8q8zW+H4&Zp%K55lo&Qkfw~} ztdT?+H`@tH_bzAJ$F?rlBHf_${7m6qJm3uB~U0k08Dv;g59!Y z+w`p#mpJ#WM)TwPbBc+*IVT^~64J0OitCpH7)o85sGf`xN+*k-?4C|`NTK;lO{!nS z{PxwOx@s%#6`5@qfB;8nFL|l?$+_W!^tq5$v?c~R)(l;qOM~n$hJ)Hdl z0j6v$1y0UDFl7`qV6OO}+I|1jsla2Tj_B4T??5myh9Ly0!}9uOH4;wZvk4`~2y+W0 zFAv%AOzfHW)a(nbO`zSk(@5%!Xf}rxgI4pqyBvB-^8f?v&^~eXKtaMMUV4P2f^A)J zyPmLaCv4l=*gH}SR^PT)077ETsK*@kt-cS5pxAu9W(de=u-z{|4nVwOp7V_R7c=fB z+YVbIN;N9H7Dy2km^HG9^aj~gSfy47F^Y5&c&J-qK*FQX)(0oaKaUhX_m*i7Z`=pD zvZ6h|`qp~yloqd%L!r|PdfNHP_tI_*ur3>_=(*cBZWT^V;bZjux}YpNFgOGRASopx~@~Z5ea4>!wNfvh%*hzhk2C zcY4j-`tEkWbQJ|Px_TefQhz2l_&4pWN#vCJ_`R&>n|VB+;)V}@>7%wPed;6M_xiG0 zZuqiUJ}kMkhtMSp)({uVxvZtf26%JR6$#qBcyZWdzvvPtmkAHiAc=99FY)C1eN0lI z6}Vkj1AkPT78j|2RG(%EN~x&smCkuWM1_cds1+tjV?UHAJmxTYEl0#k011Yzz$(C; zxaD=n7Z%c73@{A^dtSE7%$SB7HpuawZ3&x>3K*1(Oh4u1FdA~`MVCIY1Kuw4YA6y3C9E!0WNaF>g0VU z!l?=}YlF6lH}q3>KeiX0tZsdVMWX|WIOosa33^bN(R9L6f729XDKl3l2ITSG%8UJcWleVg!wSz>M-FS`_d2KtH1UQteaYc zzIt36G49R4fdv&Aw@XDWz&tZfH!JeY$W!t=+K9NMf~^`5ZX*WGGpOzv-Zhx{Cl%1rR07{8}J<+Fd_qC6!cU0^ z7kM_RIWrDID9IXTf(?y});44f_zl!@!!t7~DX#d4Df@6%1tC;koq9e~!h-7C3@1Qy z@(f}t@`D*NC2Q(KHe0k>7BgFHA2?p04T9hU2_>{aPmsIo;PGtj6(?*`K>nPUDyTy4 zI}Z??NsBEiP&_us!Ea;(@un1_$nEu^l*6(@s$4@k5YprU8r30iQGiyI0M@yDyA!*e zSUjo@RR$^g0MVoF;kJ~MXbyRhpSpLSX{hJOHx%58)fTJj^8euDm?E0`S}gv2qq37V zAe=F=MMA1n|5;|?2?%bhnK*$d`!<=)cEQ%9+4o;f+2?X`YfLJ7gb=`-QDxoJih1C0 zb5~TPP|gkhS3y`MhU!uW*({|YR!DtVAmgsK)z4A*Tcat>mO%bq8&?QDLq25v>=J(( z=+^drr7@dTf%}&iNLo>r)pE?Y4YgKewzPrG^VQXDVoYob8lVsBVb66UasZqRae@KB z;!gRdpll})XUy}AS1zw0TY;61HXJDqe^Yp~9@nght4s`v3+LyEDmZbobPA*mnW~6G z?kZ|6D1O|Ww7T-kNb^OdsSeeFcH;L;5eKLOO-&}~8}qn^q}f2|OxCLn@~1=QvuefE zpmRM>eT-xjNK;Fm&r#6t1f`vSIOhTNctP*} zBE@)%`aC1YJvR&(rU!K9Qy+kq4VH)gJ@sw0p2d+9KJ-QFPW8SYXz24`yT(d6hnkKb z(;0td=Z_!e5kzPTZQu^SIzDWsK+paE^vZ*ezP?oRm&GBPJkA9Q91jPlWZEOdB4nyH zzZ@5qG%x`iyvCl18L~HoZZ$NtfgYzHq^Qzr9K(2 zb+x?lX~xynJ=`w0*rebvU6|*G`|Pgj24*AR9@5-43+#7qt%B_N-Ig7i`sToI$f}A= zFmPQo-Xd6`V0GD3n|!cOeeJuBZ+in&#SplLU2_G?JMIIzjXXCmBGxM#}=z)qDb=@SWSj3OE=>|^ z1B}%(;#4XQ6Jsq7j}i9I(++&QtpXl}CNr!)MJG~x>$`74F=1H+DP>HFz&Rr`aCOX> zS@8aKK`9kC1*oNh|*ms>XPMcy)6HX_mnKR?GN%JY*Y!z4c?&D@P zg9g$86$wNKtmA<=O*q}&V3UGxuh)2Gy2K3#Gf#N0-XekVbX%ch((nc5+g1XWDo)QP z=N-T|n=aM#p@K>N%np-?u@T{lBU!;fTd8eSY%{^c$S?{#h>(41Z9}lg2&U#;tfp6M z+llbgZer;O3X6oBy;#lw*4np7)I5JCPo9nmKmWh{4Sf5}Z{df3|35*$U7GVR|M&kB z{>UHtqk8#x2IY(?Gk(WMKY`gj`?l$}&pW)<Kl;z5%zW!+-`dfQiWYB=+k|Enp(7 zsyGt0O?9*qK@=x%;$|?47A&&)Ho{ao+C1jY9C7njiQI9rXEZydJG5*?9JEH{kxTAz z4}Z6Bph~+gxD}=94YNQSxT^V+*~-IB3#!NJ#FmjAhG&~|dtc6ae`_wJAsy7!V~%VMxDfsE~{6y#$U&+ zZHq7BCNyJLi3C2&K5S|Amt9-hW$LX6PQGxd!*0fHdD{Ky0TBec)IY~P=9|Upi7=rz zRhOJsVLUz_P|Aj~JoUyxnjBlSdJeckJ=LK`!$@HdF*)%j+H>nw0ojbv;4z&I3dcs( z7qS7&EBEfS~lODn`LC&U`HP*!{$)EPU8j+q&85_DfNL!4B^CnehL_)`%70X zHuw|unrr~vxtbKN%o;bZGJ2WAPd+p~GQ_3HZm%p2_LlwG5}-n*t~wA;hlm|}C}4v# zVS_zSKj|zoBX`7oN;up24`O%iok&+>j64OsQ_|XO#guIplm((@=Gb4e;D>GAyt$a2sDtP}*||Y< z`G94;1ye$;n`eS%doZgN{qr=nssFZZHt~(<5#RjV;YrBpVAEbo?SL?88T-PRFotBX z`-?)R(xg94Lh0{|xJ@gS$U#JQB#OFWvS-km;H*u>4t132zZFT)~O(1KMJpb=TibcMu!_L&8LK9iB$sTD{ z2R~x*!m%_7oE=)`X-6x*z((3;2U4hiuA&twWz0-i1xT4=Ao?doPu_xJnaNsW?G!tF}G3DGQLSXjz?jFDZNeBx6&Hq3Q=RHzIve zK`r9S1I0wy2Ye0=3;8DX3AlM=HwcoIn+hD5Thx|iRd;%-unma<+S8ucRZzUKNFW?j z#sY)uEp@}hjFX5B2Z$Qo?9HPm%6|>8aFAw*8 z3#iWauxDk_F3l$2LS=6rYyg_t8N?t=?clcd=B0ZNakFhc93_Jx^5l+pYry+<%k4z~ z4k@*Q)wZnC4%{j>w5>j5XdCQdZf@$S+Pu^<`V;|_DqymB!PqS`+u0KS_4xOc7(e*M z-vvquA3XWMJnGTRsrsMyParn`er=1?z6Bp>v+7N8Y(vABEgl6GF=itLWEB0Eo_%mbPu?ozzeraHp<*+Z(UY?-Mw-PFK( zK1B?61zQuAkh*#h-H)D9Y6->xy^UtQt24W5fN4UY&6-*eu>_zNRc7y%z1ZVk_G@58U|a(l5oT=(+6B_wHmD|rPQ!pnrTD$YAj-`q zFwYlwGy&VXKx*j`dT`5R6(8U^2f@!od!$dzYt3oGJWXyvEB1{Ofz-gHIl0xq@8^5R zOWZ$P;L*he9?u7$6qK#B>~V!aT`q1lwx{xFH2VT#yAL_duEcYxu`f*64F%%YI#Wa{ zDl)mHCL-2qL8%U)a}|UR%~U8VyLBdYwwn(D1J0)&FBK^V8uoy(t_P(*A=gF#9}^1Dm|FX%1U&lD0LIDh2XTvM_{@7|yAN;zh~_+skjY!q&#m!~E{?g! z6voevXMB7~acy-uyS9OaNPkPJFWAC9*JJ-ycLwx#^-F_<`0oFm?|5BwhxC32@YAj? zI2$_9c^7_%YW@rt+5q1>@Z8zJ=DBOxp?^4szujRQ(2=3SLl%0cj3OFex`Vy)LY2V? zcJM|%ed5*6zrNMl8}Ya)HI4ylQ?t}0?_I7X8qKS{Nt+zm#aaiD2)oayxTHFzgq&wD zQW8WqkHD%ZWiddA7u_jmtfgW~sg2Ev6Xq6IR*>^fmgyG&03ZNKL_t)AlV-U|a8k-x zs^xCxJb9cMgUKS{%6TpyQG>k%MX~21+QXgva3v_#=94s1w9#O>eNj~vs{qHGP-BDY z59~Q-JIH-|pV&iHT)OXqD);T#(BzG;{|0{bm%ol}Jvl{twYaHT{o*1lsDS&>Hra4Q zZV&V&rlF)LIe|n`ol3f>;-HEZz>yRms5mgsXQ6(|2ne@Q!QRA&)G4Uht6#V`)nc`- z704POJ7Hbh1!s1PMcpjNO z6B!=Am$*eZ8HA7_;&COM!q(XQ&jN1KYJCq&;mNAlgm5d?qS}Ckoo)*}-A)#1qZJd`VC@Fg{dxt@%8IRSMyiEcG2t@u zANi}RisY0*J}j9j zc~NTHtBEQo9dHEWX1m2jnvjT4Ns*@s$N7L|TTo%oyO0vLEwK-DXgI@y93nwnZg9A0 za``M`NQNYo0KjpVEc!6Kh#=+c$sUR+#WOA82fpzASe6A(Z=T|v@4bWXz4JBz#a4>R z*R|TfZH(8_9(CGlDQQ-i@PfyX9Ij}fG5UzmtUax%4!P*_Sv=75P1?>b+xLWB*4?Ay zx+jx%={&Ua@%4;*=7a|iU&6i12e`R?HWWeB1|_LYz{Ah)l5n0-b@6;mX?fq#>;<8} z?epB$Mii@wf~u?g%IiS+_ia71Z{ibF*ACkArlHe`xxt;dN!Q2Lg?1iJ9wfwy?vHGL ze+#lU4>X%e|fx83!4x*^(-hGALMMQBzA z>XCb~QRZ%QP=AdAa*(549&K|?XTEv8BQt9B6~A!J+J^`N-JzC80HMd6yCMed8ATX9 z`fd-9vzb8y*l}YLVMj}6OY#V-kpufgwJE}yMP%35bAv;|L4C}_Jv9v9xDB|5Ev>6d zdZxOQHAH))S^HenBYpdTSNBMQ7ij$d-!$rX82R}HjrbiCg6B5(?fstTkMZ#OX}DC* z5;(N0)yz+Fsyn{?MV%+#}DGv{{kZ7u_k z=&D2(U355Jpw!JE9ct3YoRV(`T%yz@<_hABx-FRJBi34w$iQP(OsD4iO(Z_)g@ffR z^R{Tg<>A7!G3^^pDGjj?w)J838$ukqA*!BXZgRu~v~6edpC1$Rs@?BiBXWki2bmMM zrcr9eoO7G#ra>mrQH?ai#z=3>CZ> zPykugsNaDNQo$7yZa~OBpi$qbD1eQD18kyiL}v5r3IOqeM)5}Gm@+o9%|0<9vwc?F zX0(jkWknS+S@&?$BqnHh4W(zqR)Ir$*o)i&>=WNRM+0Vg>iY;0Yuu`IuS<2S0YOnb z3s`+hE^3?cR#aCbKVM4>WgJ?RTuH**o&%K>GZRYfuVG4pi`lP9=QONEaedmboUEX) z)td9G`C!f?0X6}y7gGW(1(;`Rv}>_})Us7;woAgPMjRR1_HUjtst}e!0B7H#WUv~T zFLG*&0+lls-K-HOv_j*gE0$VOwc@&LfC?6Gq(ueGR-E!rSl11=%MI4k0wqPMn{yoO zz6=_4Rg2h}vO#^ojO-ipMC_Y9LUtJ4EY*N>?ZC9}=)eh^+ipyf=hlKMnBu$j!X}e# z+b1HU&<6n=z_YsGfQ)Aj-2++D!NqiG5t0698P&HkHjt~j;p*Nc-uS=2hOhkepU3a~ zp)b)(ufF{EePjOJ-}o;6lRxwy>sNm2&*Sju3KuD3N(qney@Ur>k1)Av=FK<1gLPZ6 zZRYXZv{+NBggH%^e0wmt5AS|D;^E;jlo(gj5k=H9g@LLSTU~H39c|#^=Z4gR;`y(l zwNbkh8Mw==4;7ghhkOq#1(i&dv1-LC7O}aV6ubstv@gsTBw+O7r5$(Bv@>QEI!q?Ao7~L6@dp9GmPu6hzwO ziQU6I2JfP6<$~ivh=?$6NCprV)9BKt4svT&MEg1Gy5e@dX_~KSYuOu~So0Gx5?s0t z2FT*J>Tr$QrniE%wivbmz+xD_xdH!%+oKG~6+38@b!0$s>mT0T~M ziBgyM26%|5NxK0IgN*?2Y4bfYrM7jAx=dkejR0;yk-x4K)RqS?+RxCw$+FjzDG!l< zaKNpL?}OoGF2Z__ziFe7;c*bzuw>Em5M&{UeimY*ut^$@pCI4xA^0({sPB4W9Sh z-#4nEy{1ae2ibwb7&~eGd2=3(r22D-W4Y+hi<}cCw(WT&7Ze|Ek}DgyS*g&N7Ks>B zvP7uFVXxGI5r9V4PwlfEa=QJsR)j(!hGwG$rhA^F1_njLs?STuf2)l*^aahk?z_+5 z!d9Km{QtvByD1O|-sGz8$nFh}NdPq>v-aH#&SKn0AYpud0p0}d)gotayGQ%c0Ai#a z2)g~rxL!yB;oW(&gEuxe3JoELuH<7h#Z5~Z&>=eUjGH&U?)2)z*Iu{etlFZ7Ku#SG zxJ}#`9>ZZ1H9H)VPoJRGH^QlT&a3u$SVV_d9Ggo^3CPnGq*kP4gs)P4y6-aS*fxk% zlu|HH8F`wGD4w!4?VbTDzGcga+axq`hJf(s-UHOSB6CJ@Pf~bQL#7^5x1jH8+xR); zE^z~r!M&^!Q8z=ZNyymPKSyg8xqTLiQk#Uw6;nk3Egl7fHX%)^(>Qh8K;rZLT9EVH z8gC)sn0!-TBi~#gX+j7O7F5hIVrO=rd-bPX^#ij2m`HJ3J-3^B?$(O-fGQ%W%FkqWKEKM#d zQroa&2gdQnxjh`!;?c$|Lsic+_swoRC{!Zi4NW~zmD=@O{5pppzU)zaCF~f*KzFJ@ z&Rs4KV*7j8ih)v%5g;vA$P@^ojN>uk=2V<4OORS{vs#{c1p{Oob3wu_T%M7iE*PB$VwV;p>6C4B>!_)x6 zz4!Ek#wjRnGgxau(%M-K1f0r>*(HSvU`iQ@vk|eKRS@t;%RpB5ulJ~`@Y_*?RNCoR zMB9NH4Omf2VA$NU!vMBgJ(pcwmQOfx_CKe%mk;>F=RSkaee#p|&EI^>->b#PmAF3K zCpJwPTM_()|N772Z~fxm#;3pVS$g#H-+!d{ci(y!Klx)nsXz9Ie+;+FGaQdc&RKRceSr^Z%LWn{ND2oy!CZ{@;9l)x$BVwzn2u|SP5iOjZ5UUb7I zi)YYUmheh8SP8ICZrQBrwg}IPmWreEJD9TP+cP#5B(C_zH@=A?8*X$1e4|FbaTAY7 z3g}yfc-jM6?Af9=*pUDs`5-C0>-d#|J{) zHdN6jF^@zb@eR~8+pv@TxepO+)uM4l3z!qG<^wV(9H$9$b|Yo86^N(x02lL&2b55@ zibXb$$TT2abGhHeDgU?t`>y>LA5VfB3U4|+(iqBlS_{Myzpm7^0P8( z-hkc3P^n+>3L6*oP2c$z*@gGM03i3$OUu{Z<#VYH+xgU*>86x%#X}67h`KbK8a136 zyb(jAl*ko*5et0Ykmwo6`wnsP{#Qr(0nVK4^xQs{ef|`-qg}?@xvsg{gy{vKY;Hq_ z*s5mu;%Imky++>?-g#h0B(F)c*PjvR80tW9|M&9+DR$rWK0n(y2GqDkM*P>dq@z*7 zKd2enbK&n=&8{Effj(=6#{tM4-tr;XJtd8FxMqn4Z1Nq=z78G#^MNC+x$aVS zYTS+JO?MDIy6g4P?s?Z=^I1a3jySlVUC_B)15PaVpD*sI$58ji40(4>-ZA|k0n*1` z`qb+p+M3oN9L9~EoU9xFoBAXwE?N%Bclb<0(#Ldm=60}yO{ndGGUW?^sy_e;kPS-e z(qnOwEh8oKR#mYkmVB}a5Hb@^>xL<3-=cf`mj6B?!n$62KDv0!N_d9Z{fR?3JQ4Dg zjgZShYVYzqi3VSX0nQk^5kz@Wmj>B2Hl^efWv7UeHSVJJe#3hce#A}e^<|e;#NeQM zWKQ-C=_-N?W?V8uJUZ@@fWn0PX~HI!89Y;e_^SiGVh}(?NUB&Ja1zmW>djB}$*wj} z7B#;|ar;9NX%~C)=(q%vy-zXORmBurZ;$*e0xV)cyQ1DOiTHPC1!;Cdd-#$jpMGv$ zRHl@g_c-U&z;3qr)^^ixLP+e&jFuJLCtT62FBMMp6l>_&$nE6C&gKOvyVN}acq$t% zIX4e~Y5*#Lll@abkP7g`*fzl?_FhEeyJ5_hWrv%k;{HX(S_r3AvDQ8~u1KhApuSB~ z%L2by3nuqT-MHDm6yUT}Xkrxqp5NJ4oP3xual%tIs11o=&NB+x)_HYX$|=aSoOX&3 z+dkAb;jIv?wOU@edx&Z9WK|K1jiXSdNQJYgtdmiC{ToutcD*H(gq9> zUuaTIZkq)12rL;G8RLd3r(}_VRd6Z=YppmW#3i?vowwh{R#%tfXWOu<;z;l) zF?*$nf&2Hb@Z{|$_~}3SQ~1?i`Wim-h0oEWmmmHAzcGLNJ^bXK_;2-(|J#2IZ+zp| z!1ovvXB^W3kMF;N`Q$RqR`IE_*321c@G zN-Rn#r-Ug@E>}0T1}OP9Dm0{siL?>yB49+(+Z+ER0sU|ABhOtzm%0mjMIT7t% zkW!k@y!!R3U@%XE1T<1cs=-s$?u(9W)8>iOf!s?i^W6OAG4w*q>$a*`6hjeD_|(Tf zgMa$>|5NydU;HZO2~C<$9$yNrJ$iKT^U+A)ZLr;a9&d}gaT$03&Km5E`yc~5=X=nX zV{>kUzQnWFA@=j<$%dIE+B}A7{FzNfk=lS>WNWc(U5bvgAwq0JT{f@ngWI(w%}O;8 z?#Hzs5x4j>#IUKY24j1xZL>ygfysljHVwt7b~$OA5=jRowe3}Eg9@g-eIb-eNO4a*+PMHSOOjm&+y*a133HnL`ZB8?qZjoe+5n2$23=lP zK>ePfwm_H=_Au%4=m_#a(j z-30GoFtQJOQm|dTHqS(DQ`|svZ5XaiAxa|>8+R>SX^2dtbL`nbu{xI;M?@X^`_@6O z&AkT#08Mu^hB8=q$6Ihvw~)pV=ku@Q&|(m8doLZJ;z*O9D{ZKU&(s>U#X&*}zHnEp$L6 zX(vL^p%#dUP3NnuQ^y@}t_iz#diW>4Nv&Vf%Z>Tw@^bv0n z{uh@@XSO)C%Zp2_>+10@;u4jS*hmm9P_}JIOpry2%dV+;zrrWS$tL6C#M?PfC{?hj zNyMRWkE+FzN!^fB?su)K%jmdCZjU)*Q^hgjZ4!gCd9ad0-6Gn>wkz8F>Dg}#YX%*# zw^I%D_zYr#)XhpK_qVo*n;%5k<9|f0aGG-RX|5d}q{#(l?1LqRNWmpf*aTQqkQKOK zcImlEh>I$6_!0wlqymn^$TuMh;0SB%NlgB{dMSL`QL<4 zHwU>?;O2I7Kn~#|+Yn=`re1h3nUwH@>kS>x$2e6hd{$1FJQBn<^{KHBstQ(7JgqAV z6ILy_trbNoj%jjVCfi2b$<0!2cy*%{C>u5{UI5trLWwaEWA$NI-6}X+UhqvRCg60g z0eSIAsHSZ2p-;*d;Zt2=-XP4Hq3qr;-OG&JaAA`Cb|4Z!f|(f9>b~FZ}o)|F85v|M&k3eDiC+hO381R01ZR z@aW!SymIeV9Onay6kNLv>eju}tiY+-W^wb#sH(+)kVx2k6JQWeV}NW@u%Y0P=VoqR z#Rizf_8p2A;};Mk^X!eP;L>B!=9F+G#!?nsP)2qYSSH1sj4ZuI5!+qZxDjJG%#^T_ zV%wIk!We1E{ya=}@HKu4F{8-l4FC<>$i5{GNxOP_$5yRzV`lS@yDUGzc?7#R#JAd_ z1yVOCCEskr^PGDmWJqmIQd^+XN#NwIegl4yv`t$tK4d8PYX>s9M=Z{S7#N2=?SrRJ z@YTQh%Q#%v;Ile|f*RE}yzAJ>S3^}tgUqynl0tIdIFPj4;Ko@jI>=*1pkup0)MVyN z6Nsl_!)+UPW1C2mbTf50Y}>*!yXV@^a8H+rXAwshdE~9r8278#5#}*eh=b5Fu9rk{ zo};VKLhP(Mcx~;RZ?&8Bnz3(%rQKtFP^W0efG7qO=Kc5R$)Lsuq%&ndt3$3! zoklKjD!k(I@*y5w%(x<${Z6VW(zG@Ywt=@Q4$}d1&TS)1M9m&}oDX;~A6njesaAWI zTCu4^<{~wQGd^~4z-c*IteXTIVmR|X<$xnTkZGQ#Mp+pG{5CO6~oO4S2izsOd}w3re>Z#9%|kmKdVi>`SUK*im1?^?_D! z0MJ0uW5#xWJEEqf(qIO#4HG8+4?EOu=G1y8I3pnOF!WU1VV1sQS^h$r_#hP@=XQfc zoCYv?_i*cTNoaJgkx&IChh+|m5ZcMob>AQl_J$#Svkysk_7Qqcoe-R^ku4V2c0^=j z5Eo#$PVUUsc2`gjghSn5@9Cp2efo6~%Z&`FGx>p;oLrna zx5;71QJE5|RG<3VyndntkX@WxDNWPg{VNe$C13_NbvY^LK80{Pp3~$%uQq8vyZ28l2p} zZpAM(2|D@4$O>K~_9tSL$PpF{8whDPAhYf=TB~mdV-r6s;kL;WfsM!-;|grm;=4{F z9?{|uo2mwf2|S>h)gWZN*Bjb<0TjnfD4M{c(8Q=MDU+~5u}HzD_8pfvNXXvj1_GrS zw{<~i}uZ>_qS4PgD>9nv_Y~BWH@_7j+aj@ z&tB06NV6ZH34{Z?N~BthW5i=!IE9-VY25D3;`h=q&G^VGui(A6-oyX)v;Q-G_D}s; zJbC&AFMaf7dic`cyRG_nzx7@G^q>A2{ZIe!AH`q%nXlljx8BCZgCjU6OotgS-+vho zuO4GgGd3@-Hx+Qo4zdh?t9@3R2qw-pbk&MP*{>fPJWkUEid26N*>><$7eCM#GqYJH zh+7kp(*em70gOpto6IZ9*1Sr^g?nq0=lhyXX~KLw*wCqNr~%^g=aND(k#FO}p5!)_ z$lH#lK_V7Ov_a&mH|PDDilGoU0A&E?Y^$cI#Rg+E$R4-l#*|UD`gtP$I@!*yWPr!2 z0JL~f?*d7e^O9H-(U40jL&mt%h?SkbZG+8F7VXl9Rhx1kM7d1E@6sOLo9D1H^hT1D zcjm(=Lc-q~h3;WG*r3B3>X15y%zFo$Ci8Ez+o*b0@ZPJwYeXm^r=uqY2voQBFzWS- zdJLO3zbI3Ja!AQz$2R)ItH+IHFH*h01mR<^ejL{~&swpyUo3XT0=fJ)r#x)E_eH!7 zDw01J**O6cBb8o!dXfw%V@d}n)~B6L$dn;6z>8zMiZ%=ga(}lHYpdrYPLSO$m&0y` z3E~;!DNXqJqldUcfmQ?kZe_LCJo>KHAghOIMoy_g5-IE|+<|E}RD(C@mQYfwd%KyD z=X?0X!w1mi7HcU@0!}T~%HKoEt)aKZyrTLR9Imp6!Lun$1Xal`Q`H+|@x+9XzE`jL zA~grd5fw;AehAfO8mQbhFsMVWGB<`fvrxlY@3li2$6% z7;8$Y$4yfA#?#=3H+w*M<~wOC8b>C2etWqW!tAN^Lqw_FMZ2)fp=les>=$Ko58F=- zVbF8U<{ke%)cb68oq2)7defnBu{Ce z8f27;px&G;rA zH^pcE_K^ojJh13!C)=m9Za4cF@uC#DsARZ>%c0gnYJ>-;4p3N4mamWX}^x!d0>((14sXL6IP1mgmJLH7h zTAXU`zE_o=eeIj|Xvo8VMn0*{iCT1JE3BZjSvyG__@T`V#gpZr)-34Dg!7D001BWNklPI2}dpo$`=4*sd){&Wl9mikltm=Cyr|303bmR6|NP%gA%(^3Gc41Q*{20#XB zS{!yU>`-@hdnSXZ+IN{1yDU zAO9KrrT^i}_|Dhgz;e2w$FIDE!^QFcvxa=zHvH0G|0@2{pZN>=r~lZG<4^oYe-eM| zSAGRK0rQ2CkuT>fy!_y0eCcyvz{PyD@5#TPi#%JtZ!#cX06j;j6#VljaphCSDg~E3 z+eTYXU^PkUrWXH59+$a7Fmu9EH>h(x?D?E6yIWy#eX8I*`8DRCO_)uD-DfCZT$jza zz~*0<_WX0NDFc%dZa0^OyUcyW7%{m@B(}BMC;Qb$xYXdd&?&)x{-1DGset+BeIp&$$SwuWcglR|cuI1b4!4;jNT2oZnw>n`Rg zeg^Xd=4>8yEq*>Ih-Zrh<1|S5`!JkSMk4#ODPr>!J+FL_fR9piCdSp}eOzBZ^+rZ9 z>KrexuJFpMuj1*m>n5}0KzUP_^!AKu?xP5=pKPe@L~S3+M@b%p=q6Y9Kc|kuvq5z3 z1*$iqL0^i&NaWJ*HdrGm+wEF57~9%-WXczKaJ;UYBs^ z2Oyno*-S}LeDf(<8#q_hVZS1+joidl9OCsTySF)PeuOr#XY`m+@^dDd!iWz%cWP_g zYXb*p96+Q7B#hGERA9`Vlf({vWVW+xZW3Sf7REp`u0sV-Jnl9!ozpfkUG%r z{l3?p5+n{6i>&!J^zy&8j@lXRmg=@C59UWa&2&#Hx7nTU;CAEMIvPP4^6n#=O@^WB z__=mpHa#zM?-Vw?Fa&*2Ta0pbU~ethweB?H)4G(Lb~5t8#xQ*D;I06{xTzn%_92e1 zDcVMzJ&n-L@tPgy7qlnWE1g}p{k6B3qSQrFtdTMH8Qq%%$ShNLBz8>$%>xwyCw{og-+eAwPF>)F`uD+o%8Nd4LJWh=7|J-M>Rl%=+{Tn7D zRz;O!M>_#YHXM*pP&_&!BT0oYIl+Si&<&wrM?Z@}k{} ztLhuFTPg1KvI4Sr@nun6gc$<3PtmsL?D2Ad5TjPX_3bT=$>S-!+(@k(ef@xw*lWm}$oO}z( zjN787t!H2+!Es8s-dq{r2iYRk(ilWgL1vo}b4foH!G&khR~0<p^2OIfI@n5M~nyMUAmamW%Aqb4;8D~!;7ARB5)l_9%Qaa(FjSP1nG z`F1h8<>inWlS?8GM5xIQ-BT^NnkG=5P);WohY}{J5&uO1&TV6>DI3&?fURyw(*=IV zqY1A)c!=9_gJ1rue*?epzy9yKJUQXxzw7J#cmAO%%7 zWSWs^25D)s^Gy{Oc|umfGm1f;fHYyN#bKWItdaqgtt~Do3bVeo&=%q3v5-=oJ;D&y z)}*erAX7rA1&RHiDFJF{=eh~zX~HHIkYWiC{@fL@rfsVm;NH_nvPhgzr1*DGJp!&c zlq}LJ!b-K7VomJ27eULZSFJ8#_Tor&x8YC__B9MSq_r1D1mIJj`YgWl?eE~U-s~4z z%+CJcHq>viP}&QA9SZ3Y;bdD6hEm<7+O*ovH@xszM3aza5KXQws2<0dKpfV9;+rIH zLx$LbaB{LQXCJ84E~#gL)Xuu^zV+=^BpaQ#8QXAkbA!|Et=Zioj?+~*P9)Q zJiNTbRhn=s1xqWQsY$OpEuH#c9&Bj`S|g9g&S5eo3A?~u6(}&~%toE6Zi+o^090aj zo0N9wnEyxJwo+U}%{gyduO|f48pHmu zJZWq6r41R<7)j~e0QI5O?tR6AlRMMWQj%<}n$PS7(r|Os)L&@e(DNKN>I2gru`&GI zvp+%~wocb!=yzr%=*8CXcWR5*+(mN|Ky^^rhkV^x7wvl=CozMJysN}Rj^1{6O=Zwk z1l~Kykn$!Yr$G08J+O-^q*4DqTnnRDpXLCB&3$wK4`1)rD_N3U=`AyN-#g-5GPAO(yZSO+ z&0&+m3_yvx3?S%1fG!Vu7(fC9z3VSXzo32{1?XV_L3|;_8TN?7CYyb!uF5>;M8tM? zGd-A@Z;v=xB2cWV&a6Cf+1s~W*7{a#I+=E;+`OSu6>Dakn3*`FSQ+cG;I14|MUZpB zd8;^-YNmVzgRq(+b6sGMaW2E+fuET#6iY%0KzjKgB=% z`(NQFKl%ZF`Kx~bC3|h0GJf#x3w-zV5h5E}GrDN4n|o{HW>$w1h0hR9XtjqDy!>R3 z{Awdk2S_GyHpoX%CsV& zo7_4!tr-S|dD03J85_&)MMB|#5&_v8S8=L##1FB)y$0X8yz~ba!XEmAstlRtFy5hi! zvshdj0cgTV6rd0XBRW}KCMZcJYj{=cLDs|O;191oLzMT8uut^6D2hwGfiz@th81Hx zsoE+Ru7_|iL8Czca{O%+u; zG9?^*3i`ZSTk$3U=A3`R`0>ReOiHB_se^&0`BALslSQwQH-@?kWY_ zJkqo+PdVGeo)~Fe@bYrPW6rqr3KDgDg8jXD%wZzNORYGRgsqABQ+-QsLn~a#C(nHY zbt@qIc-TgSiw?h-F(7~gCp<38PWM&C!VFCdTDxFjM)e^TEEg=am=ec5+%zW1jEh!3BTUlXd&418G}wlK zmO^0HR&i)qQ?+y3+u+A#g|>#5UU4J?*A=xGz#{52c*S`;;TavRZ(v#x3C69R(AWmK zBGnrp!KrV4Pj$afbUaW?$pB|a7o4_j4B2!legt8g(UE`L9U>SpWLp>eETs)^Y407x z$ype1d9s}qwKpVUZ1rqe+YZu{?oc}sh;N3q8${KBd39v-LXAAmp8YGXNK@Y?hH|Eh z4(V=`tFuKVnnRj5Cmtt9p^OQtxhn>U2!$7i{K<926dCBS!L9Z={~MDOJ-qXKEN7ep zKt~+zLGzACMoK349#)6EntBBZ&0&U-wh}BIac{9TVmumF^6gmi?m&D9PCSE~1YP!s z(yO9%hOmW~o%UxEO}@`GJ9a(dO_7jb$r({ag!g_F=mfB5p6}#gR}d)=c%9z(aSd`i zQ1TZRa>7fi(B4p`VQUrDH`7)P zXWXrKcze7@Z534ohn%od!Arjw5Y9Ol;rZQK$ELu#EcoI58$4gmc*qG_U+}zbkZxQ0 zs^(X3A$Rv$zDsqbMDiNc26@qJkA^tz3vcnWa~fE5M5?A_ake{^nQ%zS6oOtCigdHK zbX$mwLEp5Y9V~@$4em(@yBY?g)*13#U;E{hkeN_CfrPv+O3-}l~N&R%rz9u=w6e=#gM zdff;z&8nsw-#JZob*2HEO;@{1xxw=7p>S!ea$V7S6)~qnwIspq7_@=OBBVQiV^@>g zMX3{g9}{C>5@GLKW~PypLwKbOf{n<3ELKq?y8oj_RJpa|_J`bQK6+>1eh+MFwfh`2;s&a5_Lmu$GM0Hyp~nHIL02 z{PT9fVL70Qx4n8nE-QN5z$h-;@mLIq`PWnOSfD=MeN&rs|Lre-i8sdwK>Q)3=5Ot) z_}zEENADLT$`&cqWcZ^&jMkdTA60#8o_w1vp2bY4erP2Cm)NooH9&Vcpvxh>Xv-kW zg(n7)AO%sK7+or!PS&iD0?S&^s`s*&#b7jA7D(1P4@UW8IeN3$%k7O%fSOuHYB?M?m-|CgWlZ%tIMi;Ya@yy zxdcW99$jTX!`^}WQt|ZS(M@cd-xWa48JEim;)9c8O4xeCsWniJCPIewVIjt)8;nu9 z(W-|nLJUlD5#U@qj;lNDQgUCb(es;#ZzMe{T8t?X(6uEc_qj0&`{bGvQp))GkH5k{ z{nM|}+6Iz_b5|^EXUf)${2qWd0`PL)ka9w8JLD<~irQ?;(b=T3N-kd%-E)6^x*r2B zQ5{3TQ>|E%5$lKQM%uVH#J919rfId>wYO4&Ku`fN<+1%I5oA@IrD09U0GDEo{+b_< z?i$`5-+-jzX*=Q4&S<^j<@|zw_;=qxQ^qEWUK_q#GfE*mSH;7zfW5JO_|ZCSA0;IK zN0emBp}UmPX~9M|Nc^r{@X{;5KvqUppb7(ZvyD<0XzyV6mWRK!Ys3BV4!kjXGue9* z9(R*9yqgX7M3KCy33p||hs$N$Z!CgFMr}{7Mp6|l%sAIGGBOTfBy@!m*9u-2 zV{wpRNejAcIQerV`9n`QyLfX!1tY6qlZFn(sn^L$vp<-PNwKp^x)*qnPb^oJAtp6d zhiU^+!KGDXf~hEa9oC#(lB^)gdl60vjOOZr4iOs+)Mmox?q_jq2P3XUy9}8n)WD#2 zuf+D!TMpll5l1B41V5ID|GM@e$KU&8&4SA^gGUqOclPtFN*ly-i@r(YJ|o}MQ-HEm zM-xt0*}l9(r?f*g`DVgTEZukO2=Lh`xRP(i1*92@j+6@2>kS)xbldp3T$~5s$kImy zeOS8o=E~H9De)kL)1IN@yc_l3p9WKgFnJkA125(q_Ce=}!kfNZ4d1JTQgOEnSgfbR zZqY`uJ3y_}0Zju2I2@kN>j{V_f|=lgTtfE0vs)T>2y9oyLLQq4yLKTRtm@#=jGQx4 zt0wIg8RzBBaOr5RLbM~ZC5jw5qt%M!&2d={sA5B1dtPj4t>c(ikb0~v(%@gmFoD$I zyGhMP1;6f6Fphl4sABLui>$rgmi3V+Z`AgLZnSXU99RFIyXb&Eb%-f7VB7;M9Fl)7 zW;qF=dx|;-JA06mgFSKn6 zdW-WQQ7jsa)e~G6_%J%tym6M%c*M|d0Ft9w3K(J7S?0!b%Ak%dq6}aCgqHK#he)p^ z;7Zrc7V#blO!Etw_T_qotDD<$+)(Airjn+-4nIRDz+t?A(HKvd@G%CX;V>Sn)T`#) zttYn)=deA=9=}Ik&vINxe7)!%br3#9U*CV|RSdb#B=qZMIez1{=76pMZc00K|4R0a zTkAW`jKuFr0QiLbe(bMzdcph$jPEiw<1@)UeDiX%jdz7ekk@Ila?zwHpvgNgid|La zb`iM(V_z$}RB!0{liC1NR{ASg;}EwHa^Z}vS1b;WdUJR{SKzX}U~3yvJ^)g&^%FR@ z3?S5YF&k@Aj|pQ3C?zZKAucTk)?5r6lm4N<<^3)vYw&pST+E7k?zlS~Q4(8W=~9Uwef|YL|NIB|tH1dxNH<@$(Qbj(D}MX? z-{I|N?`%WCp7p5$ZJTY6s9SC{sZ*+9-gZ_bv705sB(2RQ?cyZdP!0&$V^7^9>yJ&S z3YashY&h^@MT1yIC)pC+J?meXCWc%Y7h7W6h}^2Ib(x) zroQ?0Z}5CJss858<9%9j)@CwX-$Dd{Ni#ebvtM$#o|$e%#6*c_vHR=h*9hl+0_1|9V=yQ+HpsDY|p!N+@*rr8kCY9 z!r|vn8Cw)L02T#4UkQKut#t7Qb8gP*ccY@=r z9ZJ?@CN5wI&Z;xmc3*Rxh6uoL61Cy{*w^AVNC`#t6fo2wvCosA+po{i_?ddt>DT4N(O36xL@yax2!hgf#BiJWRp1o51H`Q zXDj~cyB9of9rugzK@Lm?M8jgkRtPS_HVpB5@Z4-4x2oA*Af`6y)NPQ{HdG(ZRB0%P zII4n8nq4>A?VuDp8;SxiQpc9<+<-p#AA)o1$ZSp7vz$?DGus0R4r#^KJA_R7+1d%L zjJJ1h&}G4;KB1}Lv;2q!35`v4BTbMF2QUOp1rMnJ)N$?w=oLcdE03Z#`?EsPvGsADyCNkk=u5T%LhB~H zl`d9LIIX?*v#Hktw&)&sh_z$ZNV(@&Z0n`1L8wiIkwtul#iI8*B-w+4E&&X)L1M}U z_xBHIt>O8@ABN{u5KtxemPXOxo6Rov-lBLWf3ij9uwu6Y%v)t0kz@u&jdOTVk~M;2 z)jjgNWHBPw!fHTQ+38Vkr?EX2*`Op?_y}gRV)ptTV^OdlcuZmr`ygUt_hBf)P_7@Wku7Eg@EIh_$MM#4af>1Du7$3pc zh$0=Ci+}1E=@|j}Qx%In98LVfQv?~ ztxOtNx=|@dZ^9mXmS#Ek)YA0s-sE^j1+=e|pO5OPO){FOu5bneOaV8sCIrDCvieFU z_-edHp*Sma`~rE6cGZCCzV)0t!)S^)Qd&miqyywTVjKmpa;bH8I`jUsG`K^r$nn>% zc%4-!G1k(3Grs%2F_crdY4&4F$|He&j>o<3Wo!iZL@WX0CZ%fs|EMCv#>CW zenN__pNKn~Bnfnb0Jw^%!%b}C#LWQFj2xJ_{Zpdc)wp1{hUm>k8za@k&sv_o=PNAe z{C0E<8Lu|$!y+`DzG7x@nj`+E80_m0DHI1blnpg)s4?_=*a!3XuXH7ixcM3;wu>=02i#qQEn$F^`Cw9G|yOE>#psEqlE6j)fVgX0c}C{zfC^*CDfql5cn%szroC z2?;<2aG-=!5GT9f?cE(VQM_S$U7tU^1^I!VRYyK!99F@CO-|VdF&e}`e?(XulA=U7 zOOPl{MwVe2n-bet00*Av45G-9H$J9<)@&DMoCPTCx#Prx=zeb2l*a?HD}kL@KPLBN zLxYoaEM&Rq)n(x9!-*EpOZTJ)3eHIPcBLlYjxpQkd{%>6VaoWn2fq#gjt;ef`^MZ? z3(g+P76fa;yLG|O-U1(942X5NnmqOCVs?OKDcEX5VxXj8I#@yS(i1u?6H}Pb!p}%# zwv~{qi~4;!+j59>{QLj#k2sw^j_g};s&{R5Y>%qT3)yWUbwlM%Yu6DT_ zP@jmDBUhm~tr$QC;nEul3ziI!S{^(5!}7jl6wY|N9`W&9@w+qOqY_^F5z-SrHsIS0 zScviQBKX*8#6l*vLB_MoK{K1x7W7dsr#cpjy!phT7)5c%vLDQ3CgM?m{7>!m$z z$&y?Y#06l&k~7|v;sYA{8MRllM!F3}B1fK_dQA%Dgm-rj)||HuwYMP`b`2t0@3^#% zu51MOT(FcQ7=oe=Tiej3?ULiDpH%E#a?29pdyGwcNNfj&S;xJQ-KPrasP$|Mos`g= zUqGJt!!BvhzCc-19+89`_i4z-2Rb)s;1R{B{yJ#b^x|4hS8SZ@-rHKI#)4{H*ZIM{HP{Vxl7Vd*5JAiP6EZh--~#SV5vWp&KmwocWGv zldhyYe}ZPl*{*V7iaL?@P5exg_Y|NRP=}lR>uYzmzJhawIwb~Kn6{$BelWGTc(YG(L*xdH9z{<-NiUa&ruu651#ojNwTd3Tv$32v)*U@aGuM@-+H>u(a-=ry!{9!AWcW7@%c@N+!<`161MbxH^1 zWOCM=Y+{%w;Zjd%($LxqdT*{K+qDtbkqe5Pi-9FXQPKz?Guf9Dd9!aucN(f8-qWkdfVahLLwuyalubC>4a)z{qoELNv{NWGZ;_chF_`|m!UWL%xflMR5 zru*oLy}9ll(bQYVx8HrVjCMbSij&imzfWeyx-Qt-88kKNI~AXyx_NfIHQVYxCvq%; zeyyUmCGxF8S4H-zw}>Feq%$@<{+(8_ZD5GBFDiCOl!7H^kO&qosOphOy*d?^@oqg} zYerD6FyB?v=5kY$ue*#sgg1u;Uw!@szWMmUvy*WJT@ww^2n~NdE1^<{cEP4LffJ8uVUL2!?%4_!f?$ux z2q`P3NUlQ3W25YvYZ{5ML0&ZRtat?7#X&Ag)^NoGtPLrXUGo7TBUNNpd~xjfaO(K> z)E#=KxIZL(yfl0~DO&4;>9AzN-SLQRYxvE1L*|TJ%wO3R<7b_u+Jx1W1~eUB5i63s z(4Z>=Pvj{jBw@@VfF#8oN9j7i} zCxmw|HpatHfdXsNF1=z&#Zxv4THjDOd&4h7Nx}e@yx7)FcQ+<8;nX+u-cVb`)*Fa- ze=+<)X&LKmez$J+t4adOz!#72@Z&Fkf^SbRP-&(L@-w2tz1TjyX=1U1;+r4^;ob&o-bUqi$txoS$uqDeIxr2v8wY{;aOkdhV4vf$m@H+X(|@!_}S zT}yA?L_1x3WV3rfrtUB}doDJYt;_Hj@6XMB>pkRr@eG{>*J9sw>v9EDQV=K=lQTOd z+@5JhL?=qth;s(}UrNaf)dWUz>j+(~WMTyD5`vL-67=M~IpiUQ4GH-utAqaT1r2c& z@5viEPf&=uhItfU?6@W7G%PQBma|9t%{jKJP-^2@J!)6xX;=||mJbXk^Eb`3@X;93 z)Ns=j5rnBZAndz8y@`~wWEAF|1kO`!9@}ApooD2)_s{<2pW*TT4Zi#EokthS_kj@BAl>u751qUPsikg6F3@PdYfw}*poY3=v* z@J35R6C1dzcC)5*pyYx>B2eGZTlIU&ijau$v>~z#fGd>7HdKj0UIY`Hk2{vMs@>z> zY;c;Gp=`AZ9E?kDQ(?#}D@>_(g-O|}iZCb91+K0C7@tT=qL)}#pqKNezoP29!^iR5cix;IR? zl4$!o@LY2C^Mw(wVZWc}<}^|AzsRkR6QMn#HWQ5+h$&PJbVEkp*-v}?*9|NG${0TD z&98~!M0hpKxXu;01;`Oz=d#gGRlP-v+-#OA0oVp_-nRIw0J3v3AjJ#5;Hgv*1Y$6N>2s5 z&!V1Da+;`?9@&PNsAn(x` zO~k3BCUF<-`xHnU*ta+r-(WKlC^4jY9Tt;?E@Tp&r)d|Egn;I9k z;K&J^OCFoQUv@|zT*4pVFgsj$CrkQOnwhbwVC&8OnFcpg5KiZ3Yofa~f{7UqDWm6t zk1x+C%xL}qNuk)l?AbFaVp95e zxH*7L(7|d4a^KI1@x7j%<#2&u@m%sW#a~nvJSO0w08iDjdDoI~Tp8yLo~5j)LdXf& z8lwYvcb{-r3qHRqsLkTzqPQ%1K`8~9(qJm+6m_xYWu zxURNfwePtEKrdi6>^a^5S`=om2*dD}suw_aMP1sgxb*HrANwokgaZ-Yt$?V#&N~J^ zzq8Ema|7NU78F*HbgTsA1XM}rV&}q`=!39Q#v>Do3JQSL<;BuB8*pKdgdq?PU>{a7 zn#T(PFPbm z8;(b;jp9{g4-=Z24GFc>kofi~9%lCovJV#+a|DgYcQGNQ)xF5#gSTj!v!!y30;r5l z(oXB30cMSj?4Tu+POi_O;>wk0H5fJ{K5wGvgg20gK`Ei6#kN-IfRq>9<$EXzO3pYQ z@63J|(TtS6hOjy!R8Y#^&{Kl+3&6bl$Vo@kS7h0$hF>|h^2xatI?h^8#Jnl|=CGYz z28@Y{Js?^nE+xN)2B_{0Jb6Pl=R(ufI)X+_SH#)=;PWr=`)|In#F&x6%NA1yYg`jC zKn1GRQ*aEHDr)r>CC20Nh&zYnMViN@T_D}^$(!_{FpLJQOUF_QKEHnidPnWe8hLYQ zTeA)xe}M{w7{_(NAqk4BL%Q^lXwV~CbK1}jd3yi~Lp9;HmZ+;t+13WNyvO%S3{Cb7 zg~+G}h|&NRvL^+Vlx_RTdqkgT11OK-c1{KNWwmIyiN_NgeH}Ik0(`hTq{y@zYoLll zSx|De|6cc6jftl%qmIu$&JLY?up{T5_N%4q)aZ=o7sghfv4fwx|5~4Wim8c=O{nhN zumSh|1YdkCTJ#3Cb}RMXL)vIWE{6psfvq0D*$DI8;L;TDMH2(#S|C5g$bDi{i#^=} zv~Rf;xFOfgFg&v{gl#k>0}|g7?q@ADUFB*|eYk#KPB~shk-etlPY&ow%dVdg|Idx} zt)Ki8E&L~6()?ZX)WGV*f{QHoQK!y1dSU4pYJlA~@X7Z-@d5ToK)Q-P{G|VVupzDo z8K3gYe-d!-OPtwbKI%?-{XGUZ0i(yAPVQK$|LFZ!Un>PjA{$Ma2R?F1C}*4{~>waWg*7qjdT^G z4{yCe30P9W>Y49l6qbZqt4)Ahs**UPYxPFR{q|i3j3mH6QS3*fPJZ}T?Ra;0Z=OxS zsi4-J$JSma3DfKaQeq?!EBzIl4a>2kJ4 zTRS!}`t`*lOg1&sSc~Wq56!GTpa4Y#l5M#&Vhb);g3+*u!77}A+r+Tg=vw77) z>b`lKw4Sx&zxv6~k@AX<+snvNE$%&IVz2@mOgh(e*f*S@lq?!z2U?8CAindJ>dlc z!i#J>eccsVG(6W14bg~A!yXr_>mK0Rm?4 zrz_ZO%bCEufYWM{W>tt*5D5$f%i()f?DVcI% zypB&uVx7`HtVfiFOj76ljSsfRR&>apr;X#thU#uN=QWJz)5-khv1wBtn{FNi z_J}dF0n}PnoX#)!yMOof+ILF$;b8?tkhG!oZfEs& zfk-nunQyd>ze8ZoD2F2+4h3)0YVbrAG&S2}L~}tKz{Llu1kr}xH+-=ku+=kKt0s%b z4tf&o5_xqCQ4FX!B`hgpVN2RboOat@N=RG)PRPk*>|u|HXA^O9o&XP<(y$Y_Y{8m! z8s5{K7o^O_ULyZFzBLtBe=rCo9q|6)9k%*1x>_ad)*$Er`9VxMQz#+3uinlN?EE7i zYQqO~P2)MaQ>deP(HZ?sbfrq0IS8yVXi}H$`-wukS|ClN@S&d7A-*ACmv@yTAKWX3u=@Wm_{O>&dPM#$A{%1@>e!He@a~?HpD6Qt$ z$(`uUD9l(AyX6cnXQzFbKMf`ZENt?-+k%5e^xE|neU`M}MypL7;W=3T;VhQL z9%rbyj!@g_^K|1;r%50GI-Xu9WgPQOUK(K529SATkM6Gz*PI8%zOBF7_Uo=Po*VLL(&#!^{Wc7Je@yr}kiK~P zqHefsFA%NRwr2-kCHGqKBt461oktX9#!tWeDb~Y^56>U5aE21$Ze4La9IzgaC=U;~ zI~+{b!3jALTC3LlCqk9#KD1fwlU}0aDTivBYjFZpHDvFCp2wfA)H$? zDd12#5OOKQ<7ROfkz3~zei47rIP}S(D&gr}n6T!I!{Y&H6}?*%lNNsO4KV1? zG4c0La!BOt8*yT)AN)pz_lC099by%vb;9*=7DErQ4G=?7VGWphMg_I^t2^a`Gy_Wk zD4cNi!`}j40_Y<+CYQ6j1dCKnb>Q!*O{81f$Q!HR0$`DS^I6rPD# zEZmg6Y_WzV4Q_!NAynZb!?j>y0Z5=isDdZNNb>MfWW3= z5nj?PLs!kyitU1r@`So|oVE>5Z9^f#rVW`_V~cpfnS5K-wO_$(y4ty=YV`ZV@os;v zO8h>)%IqziT+x&KxlB9A%cuO!!Bf#5i`;;cXqQ7bsnaz?Hh7{v&L^7MA-8j1bYf%0 zC>6UOG)>JpjoejZPRLN2T?f+0z5(|}#LfZZM_AKouk2DU{(ScKntT8#bZ*i2#>zmDt5-43m#|mh_i;oJD&E{3uolJ--+X~X zw?>)$8PIx1ubU55WoYgN0Of+m;{zVn0}|W@6xIsgo(tkypq(q@wzP-CiZ_W+dqr){ zL7jWvxDbu)YNeBOfIO=%!vX&`yM$Ngc&hnFV7W_-nD*D z{&s-U<`aT$_~N%=Ti0=6x2Un}hM%wG(N~I;(R5ZoRcEeBmVv#=a%hRBNXNcgf%zuL8U2mapbKD8HDsrBN8;YG9MA#+rvnA~6dcAfq zU4cTc5>Q?_59oU`0qIi)cBCJ_|Jm0BKsn)5Um)5baxqH#lx@0~DDI~rljFuk$;5bm zddAD?jO_W6B^Ru@;CMXX7eD_K{7?V-U*mVb`5nId_$@>_s#B6x#0!T!${=;ADhA%f ztu%D&)FaG>Lg^J?C(+3<hM!M>Dlvb1)CToZun5hh8meont8atGg-Mm+M5T3q;VsWZx%DU zc8g3?&-C;M=#q3=Ab@<5YTc+3=iwHKT(cQJ zn5-cc@h!Oa5s{XClh9!t0gWx6`&HSeyBcv9acvOc>D*#-e6 zXLJGn`~Ts;!!N%23V-?6e}%%p`O=Vzrz-dc+@_BmO@Ne4`I2+SVO_0c_Cg}Dy??vs zdC1{7tRubyf<|PGOT+y$>@IQKWJ8)gYpR1O5K=e}zbO;C04dXu0;dt{E8_DXsAqi0+-GDCyz`(l7y$;ala-U3L#rI4z{k? z+%9lm2%kS5@mLsZBD^h(FAfD?-YF1V25E*syXLR=OBiFb~ z8x5E>@j*}Sw&e$VKDkSGr9nJWhSLH{4Cd8o(c~G=3xMLpaT@n^Y|i5ux62iiKf7o| z$uOI2UB$N2P*Xg>6?9g_@EBZ}9c3RR1SpfnAlelK{(eJQ7UD)hA6%M2<)(y`Fyd1K zs+IEriSq#SrFiYGY_QkYQS#C6;qYy{!j1D)bPyrF2YqF!%&F$V*IXT+^+|G`S=F!xS$}T*0T?A?N74hM#JWy!u4#d!K?O~kXSef2?B^kAW>+-!7_!k0H?5Z!d^~;hhTr4V zA|qX`#ioF?!fOpBtfk;SFAl}C*A%QGg~uPO4keI!Js_i!-#6m&YGkKZDFQsf$mD9< zOVeg_PuH0x*3CmMF(qS%ker=Ck*27suyfF-nwnRl>D|8__ov*-v%MCO$v$%{>89I8 zo%r>d)4#8HpP6Xz-skLap6o3H3Ok~@{Ixj1PaI?#Rs`|kkIuyZu~8ioUB z^@5xWwstXKOmYvG=YN*vhz}qBfLhH99UD|{D-A~HIk|<3lNJ-+QU*UT{;2;1|Lgz$ zKjSa|;%}gb4Y&w8DaeEPOb#Yka~T<@7I~xCq}re7B-hO&%&LmXh*Gv~9zu$34UdGJ zQJNQg{-6=T@_2_ghdWelXqS#9F)p%$U$)+G?&6u?9nG8KO>DX<5Tvx=K)`qIO&2JR z4u3f$lb#)!A;h>}kCuNL5G9D`fU9rQWc+1Kl95e6NHapb0@WD=-HVahErVXP`JCAf zspMCC^({tnlC0>pzx<4j?$Xee5?&nq)u(uqkioOd0u#(f9$Uc0J#2{>7j$I*b&XBF z4<^oD=+XpMx%BQuwB3W7idzvJEM@=%&+)bfSs-|q6AlaTvKio|38Bgc5aE~^i3K?o z6e@o38bqrs8TXm7CiAS93@l0U;iP!E_@urGo}{_*-#o)4M%ImJq*IdtNtB3~@rPaE zxv6tmV;6AB_^W^WZ}ETq-+zgm38%W*`|(Zk)&$3#(3%w%iP?cwWW;yXk%`^A#c;(j z+*Jk~(L<#i7U@F;vGXB3-RHiUWYOuVE875p05E4HKi4uPFKl!)E8zj_BAznf8K?fB z>s_3<-JR(WCA5xo1?l1s){q%n#835xoe8)!@mRYUgjgV#dV|u?UFyC|>oS`SU~7bP zPxyW7_~HJKhX4Q|07*naRM-Vw2zMSSxFp8$VCU>H8S7(72|1S$^=ENG_L@)4h6ULl z>I(x)GOf?$VgSrblQA6H2xzd_%)$)igd=RAN@ayAa3G7mIp%}G7;_$xM#+aQn?u#^ zIpZ!Re74--4HU<8zzRi51s|GG^H&Eq9hg0_AY!+S;9tC3vq5EbZbwC@6|FaHvVnVmU#RJwSY0Bpce-3K4?V}Zd%;RUjR3iWD(5tMYeLWG>6%>?9<4NBT~D;6k*X*<1q ztTVKaY+I)6>Kyazic5?|nzJ1CoM$V9>^a6{H2j=1{`8Oj3_t&)KgO?r^G|+-70$SCmN;n6onp(UD-3FGSa0R@Zz*gkowTPLzTMK^lc#p?y5sv5$m-7oM zc}>>r`n}YO+N<4nYJT*b7E}e+yjYN%xE+l$zz2X9Q#v40|s028yoQ*g)d@M;r2$?9*n==`c!lAo+e_QtA1PhI|I!cM{j;w{>|eHahV*W78A_9nVs~T&>Yj zt)RBO1K{yA_7E8x=*W^JLiQptvw`GlznEOj3%YN<{RXFP^M|v#58O7hF;+k0zb)_y6Ya@vHV5e0)BmX+zTuDP@T0fNu?`jS|$sdzmv@Ot!qRP>Jm{d#1BT zm3XEnIn0jyfskfb4*`8^`0(R6yM}N!@9Y73SGibX>ex0f0il zsnuPwPX;w9l!x3j4%$oa$Yhh+s@)r5%WqZ)7QpDtVv$sr$$!{e1-kh+O{Qi3QUW8P z;nKz6Yg3LV!>HN^0-pUEw5)U!re9p0aFOOYp%^~8RI&qY%;(N7o94I~*^~zb)?_ot z;@Q*+j_QCgc34@&xP&aXHY+X`5T08bMB+q|hYTmM+V<_pgv^9(~M~$j}S+%zMVD`tU7C@geVB5~Gdl z+=m(32FoC#+(bK8&SPet0dxdGTBkUr{V=u7I8BhZZl~Ks4oO48w-?VTr|}T0KIn;! zhA9uy@a4gy`E1B!uO(>ajnwGGWGWg3p^=P89)R$oKyAQx7n`V8QC!@^_pFMoGoBhO z*Hc|V;E{Bk5)K|$3HR_TfS1jh`<%$h`$lWMOCUV;36Fy~%?}V;CUN-m!H-iU=@&rdlD@UCyi{t_Tp{ z{5Q|b_Du;dU{W+C{K-H61-^Sa;kUp42GHb?I4@YWT67j?P|AQd2PnFdhCO(ld6oySIuT&WN-c44L>x0Q`;H|a#Tw!%$mj{`&eGucL22UsX_hLbBLyyYYv zyby~6PE(waI*d*M<(()WXJ5+r^6?Hw3UwWOGKbkbdha%PVdH&BZzJwiJZ=*NEXk<< zoHBA+u;wEaHYCx0hRE5Pb0sjZU|t8bG_M)&OM1nIM2Pxchh+UV&mEuGmEVfeKiNr4cvpMNvSE%g3xm}VyL&-u_H0+gbW1UeTYCD1HXVMjA z?WwnqTMU(Un=~mC=0aKy3T- z;_XkrCMMgi_RX8RekGA&rx?#Urs{qi#_kzY?G6|r9F`SJE_ieQ7Sc9|3Z7rSgZ73? zt?1qyb)P7b$7`rJgfyhVelsUV$*XM_v_(>(0j*Tc>+15P+BVd7xw`0@Z-z}yd-jLE z4lgLVP|v_T<`Gf^DOE<)igdx&F4*n_PfsVGkoF-p?tWOAH!T1r9evuUEF5yI{cuZO zBvlo>ee;0J<$_CZ6HQV_&SrCIR$NgLgP~&5cpb4R6IBv>uBE{I$rmp^l@w2{1y|Il zr6-IKHTMvS^*aJi;JiA7}U%22@H}hq= z|FlLfs_s(spwqJoo>&2wDa zd;kb?Cfpa32Y+#&QAqLKW)Pj{s`z;B5FwnZZ7rXB1%Xl3zhJ%I!buYz4-wVT5BGYrsF;5Y+g?G341)uL>wOE|DKb@TgBEQvWlz7 z6pums_3ax8jVxw|lO>OzX>JFxVPZEQwM&#~%3e)QEbi_H7m*46WSTPe!=FZsjt%zU zo4{aQw8++u8NP4cQ^-Y~w%n!rCdK}@4T_ZunhF{OPo8c4`^$#22$T~FClqFU*X{Q| zw~h}TxK!ZM4E(0ZINsl(5#zFTv~Hhw?HRRKtl?erhSRtVF3nfPamjd72!}#g65)Xf zD;R|{J|p1$5y;7YJ#)vJ3J8pq%`$Vx84oNtpyPc8esmPvb4P)oa7fMJH2H=@A>1Xw zLIm=L|B(rIq$nE2uHwMiR5a|=f0xXM8h{kW9$`zA2#=yfWR+)*cWpTL&6~8e=bUoF zFaG!!IM<4o^Lh7alV_HbQ7{Z%N5juNB%&fi8a_m#PHz`iXhrdsoG}vjU2FC{6qn_y z#Mvn5+B}mtwk-nU1zk5+gCzX=x4*+5zWo4^hTczT-H6ztG&ZMu;$t@gyE7ul>G~0w zM*g|5Gf+Z4t_p}$`;KyU?{gk(2OCi8@T3M&dt`odeBOgF43;Juye|5yY4rqK3C^{b|gD^6m+ut zJeHJmKNy?=)$akV$B;!Q0NlR+zUrgtd#LOY@5i^zCO)~`zDY^b>*>eXcGDGp;1e%- z2NS;P{;#$8N(2(zMq6C-KqfT4-d?>C2{#W3P!J36a0k;jTXD z^uy17_O+-H(<2)-gV9Nxg4r$Obn=XpoDpFv(G({pWX`xhJ|J_(QWh*Z<8nTsLC|}{ zUAS1UlKjMngui${?yE>&ZH2&9zVM`54dk`FE?H5qswTLAlSng~kD=2sU%;*8n_ zOHQ77>~S$FIM-^EcQLS39}R}pX1#ZS+2gl5h|Tk!FBKnqMU#rHca)U;=kI{3D9T2R z1?_f$%-Nfp%_Mi>U6zh|IU8L)@h-Daav8FCp7@??o7BK=Itm(l9o;wL-N~z6$-Gjk zI1TbA(3^MzrsxhDSQPMNLUKtK=!({&XrU=%iKW6BKnMy!(J z)Eklnw8#kEIY^)|nYwSEOh%h^KilT$Ug#l1;kzd3cBo8sj{=t9`UtT_)$GX+z(WCudR$_BMt6Ax8^ z!V1p-VBV*7q$4ykMa{~LXZM(Q-EA#8MP4-#kAfuUTI}>}Cc;1d?sqsnJt3A|65dEJ zTt_tfkZ@u`P11us(VKLaD2ppgd>iF{;qFam0D_uZw-gD) zCOH-n^wynt?So~-U>hqk60dunIqg^_cDbf85|TeZJR&WZ5lN-8xwqRIIVA=o8z$5( zK-bYSGzMn~Kl>KSEPD2))QW~3T?cHU%7=!Xr9;!}y$nZp_D-{7e3qCgQmWa{7 z{$w?%*WNTv3A_^#+s;=S3Zkh%o_vc#;2V64$OLz=oPXoE)F<>VS717F)#UIN6XC13 z4|wDSxpyRGALIW-S zvNy-sA%aybDz^ITAdI!Q@o*v^n+HI|6?^ZRlk0$hH- zO@4og0h5#D=LLdH&D|^tzs|y@$-Kg!@O6{ zn(Z0BHvbb(F|oLkwL1h=I==np_db}%ZG%sKrl(|e&9(XguYA??iDp{q#>$OP zPevj zqpIa(s~5`WZ8K1oT)P+AbG48UNbTA$q9>Wt`<~}CAzU`W~=R}jUP23SE z9udalqKIg>QcSKJ@njvQV%QWQ9}GzE$V}+{f|XZ`t09x^b!}+Uu~No|?b&PqOnACf ztPGsJaD8drGN~!yMf-@yS^VLD?tPGuEiVZWJ_vUfB;Ff+K0Aa zz<_1=0c`lj7ryg5)T{V0Z1;uX8v_l{0)&Q7Er~6)D3PqHtjfdQYpppWVhmr55iw(~ zy^|M2k;&S**P6$1`2X+!+n@guYb;o+Kv|J%K_HmV*=OlR6k5%TEFRIORs4J?A{dXg z*aq33n#wlG6sJzI1EeT~UPv~-JiG}#1V)m8Z{8=wIN^7H@I(CJ@B9Js<6Dc`0`S#C z#;aSKoF5}h-bTRZ4;BCZt$`3D890Vm@vtc7L|`rWtG93QPR(BpVw7dEoJQXeG)09; z6|ETz-r2V-^Auu#Rm$KkYTu=*bPEAF_#_643S*{zJzZ)6yUflMB;=lUle9Ofm%`-# z{iQ*KL$v!<+D6({8zAFRq?jD~WLwh~?G#$G>-S;wTxBb6W%Fg}=G&~!PG}_0_7?0g zIyI1;$W}}nqpWy?BK>`nt90_TYTc2(dsgQF8_{adQ1w4Zu3f8SMR;IFa(Le@5e_Ng z~_un2tyM&VTR|{OAAUe}QlO?ho-l|EqtEFY^g+3-GAGB}`x>99dDTr%Qxj z)(dYK9~K2`K_Y9tl4|!j22vza!~jewfNSaW>A+^WVF0&Lu`U@8=M`_yio@aHQfbE9 z;>VB_b5NXC#p_kTY|rO8*fDxs&6=?&IrvdPAt524Alv;3LJE^9d^k9nx}{H;YQmo7 zE@dXR``({FW;a6L@JcP7jqDytCXZKiKelhDs>@3i)&IOAv{Fg6a~!PLrs4Vss5zUr zRVqqdP^363g?ppgnsXhRjZ&raF4%bsU?5P$55E6HeE!wv#$X|KlZHXAI50$QkZE}n z(vudfNT^O}kKP@xAd>DyZ$)S1Xzyc+E_dfGHV&a5cWX!hbtqimON{D4MOtZec;;Np6okW!QI^peEiABc=!Ij4d!?&?#yAs+fd#cW;g)QerM^|Pm=}nxWyp_ zEAOCIAAkG_^17hP3ik8G?w4*go-{R}*F9qsrGD)w>0rxLKY!#gfe|-z#5eEm@QvF8 zW@YH|Xtu<>K&0ZBA}R=%VyYwtppsp!<4GsAcJ_*?FMz}07E_vBp=4X(kS3?7SA<|q z@ZIel4)bisiwJ3A+)oK*Sy1!Y)rVFmRZo$r-u&$&x)jjT>%PM@*?rLD@cua%;yj7q zdG~gGn|jKg4KW08_nBJ-pMongXd^_27(LoFcJ)HzU{r6+OO?K%FH-y9PX|>7`#ly@qB@I-M%P^Tve<9ovS6=}VPL-UZBFvQT|I+bAC zZ}O0YqPo3z+rY8iVGXc1lGDf9TzAoEVV8iwgx&s=ToP`NN!mQ*v_&;``ziMS-!6L0 z2YVZMZ7=%nztxqf?RnEP)CbD<7LTq^nlYx09N#``x68C6AB+gk9&O!g32!J(S=M3+ zwcmHmhyHY>O*>!ps1o{6$Q|gK3P8Kz2|x2A{6P}GpUvS1X_R?mKdhh+MU(Y){?%}J zh=`*rA36=uot+$@8+Qge0N}2_gI;ByH-!lg`StviBd`q#=$V0#J{v6`zV-4qKa2}9+cGAd-tOluqk>2=BLRW2$_nZfVDm#3u}v_$)E-bo_Zu=qmiCVEe=#O zfBQ<>C*bBYwIJxnF%L#)~Q9TlWF;6mVVyuOC+Y)qBC4LU>aIua}Hd2F@32 z*uPjZ{`T<#2?2o#rBs*kvU{S*8)CZ#m`t`FV>0=rN4j)RsyA=s`KL^TrFr)pm?pYi zuWKpp*#kH-)?7LSClIhogXFm~!3ptGTl=e}?<$OX-K-zlGIG%*WHzj*zL~b+Ar1Hb zMo`wiDRs#+qi;x>G;}RyyCL7aaRXPnj8uFJGtUR)oIBZBeee*;;BZYAT!$Bb(M^)t zAY)Zc+tOYPwfT;M{p^w#`#hq^ZckWX@q|K*B(^P`73_q||M9Pomx4tqE+qpZNT^6?+3Y}yj5#qT2g!-s1r8|=K+q=5M&A-e2EO&tM|gi( z?bu|?RA#o~_H?Ou%r->1aj>CfH7n)-NL}%`3f@<{9!QOD|6T+SMevnWoLmkrWNd*| zDh>`{3V~7mS(+%|;!QCVnbpQSC35*Bk@;g&NIg2#V$6a^M0F=<_ad?vx!feQzO7)F z;x-vN4UJKYqiRKWl~(SA{$3K_7F$w^Y{h{Wj!gpH;Db7VZqy-}P>*}63%+{$7PVH- zYEB5WZRx8NXg%8iL~AERE2zs{(PM?8k=wn_2raXo)3#wC0wWFaWain9Y;jN=Hl@K< z(eXBjlI-d^afLyzKK$qOnW&%bJ`yn>e^cLhL<>Yp2U@lGMoWdU@4-9IsQ4gtvU4jf zO3j%t-3==pR=VjX>^{zU!TX1YEpz&68$tt|s;LEA9abd|o4_Ws9so$x22Ei&gp6rQ zSk}dJ!qtWVG{os$5rB6vCDNWIvh#hy6p=S6BE^K;!;J6T9C0fZ<#K_n3u-NvhLBfC z$(T~;#b#SrtkoiIvq&GHHZQSIz?5z2Sm$O}Lp4IL=4h+`RaP^$c>0Pph4_PK|Bk!yOJ0 zk!(n616g|pX5+g-RT5yB7|V+JSjxP5HWc@ z%557j24E|GVYT1C#qRnRxdX}MYO{?iSM}&Uf2J?v!AlDreC&I4vPg;t@&~5+PE?b{s z_;drb(R1{6+Hc+I16S130Jp2*hHh$sok`q}Q$LbS7@ZIF+!mj%{?86t)Ao=3@OJ>Z z(G{wmuHE=dPlW@tzmAmf6lSPb|C^pu576`HW?0vUPhNiavqrRyfqWBQ5q-<6L_7ZC z6oSKGoUrXL#K4Ssxxpx)uEjjV`lK=o9 z07*naRP?r$?cv2CDrbOTbs!x3 zW?zS#d21ZKA(z@A7UEtfb`Nb)lZ0E2?~MED26t1&gUle`#AIlP|AoOjqadThgK$B? zN5^|C)uyv|DdJKpW{;989x?Wq1#4Y#Dt7m*BAx?pFOJ7zywD`zh<0r!I!1rbrrl7 z!DAI4FnQDGMTglnsAqOFu@&&H6kyuo(AYD-bC+xTb?5RvgDu(j2psxmubq<=4Q@x` zQvJYQ#H&LR4R}*&8!|N@7b!bup}ppOBU!WALA=pkOZDg+E5b`{9y;>Pl*?Pe8|=+9 zumLNA*oRVeaJg0AytN{Sd5fqbV!U|u5~qh#PX>szVH>4w>Ags*FdDq6bB4)ui{sHT zcYcdoj53^S!uNd8rj=C-gH})L$Zi&cNHh`4xWp7eB@2e8QtYZ|?)+KyHU2 z+ZrV%91<|O)HD*|J_rtTM3&ZEI%~~0CZW@a2@i`gBM_niDnZBvxJwbon~1wWNGzxn zQIzoZlKX&B38+HI>Z%`BTwDP&dBOvjkzfsX5eG=Ry`*$ofi?cKn$qCnZ>q@&;%keH z^k$`W9kmbf{PWa4Aa!*KbI+(>Ju0v9N}4y7N&7}5?z* zH?I)!VPIXYEpzfWkqt%3qt&V>eURrG1J&=l*yD3+7q@|YnvnR_BLQNU5k=u@;M0E8Zlx zC4eG8kpKm9t%%8T&huJK5#&|^jf|V=fGJM+=0Eu^{^LLW5Ao*V9bUhAgS(pZ?#&;_&Ayhrw}#6y3~a?m4T8$dW*$-J#T^^*lsp(N!+^C|4i7=}$# zPFrl+a}D>l{oJVtwx8E$L3GV1u0t~}SD#I}r(Qmx(C_$d!}6xhQ&g|j^1AnH=-8lN z1)(cFCFj#sV}8}Nhwps+{m)t)Pp&3{4qmXP5FNzQWPV;i(*}Qu!Q(z+LW-$RcL>0` zELNOAp<0l&;y6vHVt~0)8vw^2qS!J#=P(;>TnZ9TSY$!ut;9QgN7WI_;WNu;c}oWr09(NC$iI6!UO5Npo-+R1@n&FRsL2 zlvOVMJN?GDzJukmIFL+{%i1Tt?DX=~Po~j(r(kqwb%j7{fV9Wp39~@S2@HPixddi_Ir^Gs@m^^HfcB65LpI$gCBWAUPW+`Vlqu4YY+pO zpVB9JSrrqJlj-gIh2i;A#7M7~T=0*d{}Mm@zy1wGAEDJ!4!%BR{NhybA@qMvV$6@=-07q9@y9#<|6o=Z+#Q*E*CF~YuhEF zx^FeKXOU;&8o!SRnOy*RZgrMwg^U-z#YxgJ*!9sQ|C`E49hPMZ9V^neSSnHAd_H$T zP?K<4gA~w8{Or~YXe-3k*%mfjt38X>h7mGIscegCMcG;_&Q58z857}LtmIj%;$6Am zeXe+01#hI_%dfx0FMjb4I6XXCLEC(m49pZTg@6PCQ^jEpm}A((WkgY#Q7cf2AS3+Y6Ri}MO{Z7=WOo?q>ZgX+G{ zDMTz%`?-%ysFM9%>)W^CebZD$ODzu9_6?gfIp;u^)-9o<)Ql=NWMT)$sb42XbKksm zRir}@m7+aK#G^_*i(IOp$}+&@0AlIIpcQfzstZVJ7c*8ZfLsR2sN2JMHj+ECU*n!% zsDfHo`*X<#(UJ_66{^Lzx|M&4tpC zaO1y5TqURjs#=>-xYxZ;%w?gl@`;-m{P(I zUwwk_q!}@pzz-y`&L7V-W-h!=MP2nbjLy9=~bE4Z0dX}^`dS=OEt6miDIWIk7 zq)P4E_c|+_X!*Z z*i-;b)6=eZaqzLqmX_rB?`L+NvTv)1cMaJle&%0;YO~I#)oun|HJdC*z1F}GZM?N^ zvh$ql)#g=)#B=i4IqHjt;`LQe+|Ygd>|v;Wf8k2HPgjxIL(Cdc=M0VEY)zhT1AX*Z zP7F9RbuexI%R0!1b;C-n)X3tAwCAkHN+wxuq68K=(c}^Mryz6=2$SUpn zMgG9QOTWgVPFGRiyC$D?_#N~N0-d_qyOG(|)4EzNygupCT?OZgBE^1KL3*ti81l!5 zG&kh|e#qM)W$%|Zl>0E;>R<8-u&|RAT+7_?6amkp#QoLxKVuqaJNBTtiBkt+fm$qx zy?9cHzR@6$MKY>u3emk)CLz3;54f3TRM^(-TQ@H(Hmhc2r(DWFt|YRYWTPS1{ut5M6#K?jvk3YLh=Fj+V6wMc@eqzgCC2_#vctkF_XN1QX@%O*_9C{>ZZCh*VH2TnGHC7_6kWL`4 zDmY64srjDH#lA08@itrbY;M3SA1kTLp;x~yT7vJzi-NVu)anuoA?b^ug0X}r3cpmc&ZI!(LLzC(;Qa*PZKz@&^t1rtZxD7(@DswjTk zOP@}PQ~C`iQ~a7LoulCX=<1F_wd{9d>akq#%ykO9l~d9pm?|EY3$y^Q9~b=cobiv3 z1z%>tW3Kq(yyAg??4Gz%#q)VBwkHGv2Pe$>7LI(N6GH4$Wdo+AF2gOQV4jXBHG6~U z8$1f#7uIYjRgF|%{GJWL@^1qNhy2+0Q4T|aOr3nq{bfhpyT(m*;biVa0gr1o2%)MI z+8d%$wwI~`YZk<4(&50th}g9i_A;QB8fZ?t1h0W&OobE7^Y5NIqD^j0J?>~DmS&gJ z*4}74_{WtO4cON~VWK#%Yxhl>_j`h9-Xw-peVFWQj+S8x##+l&3J3K@xebRz%ntB& zUGesE!I$fbwX9f05n&tl06|o>h%?r%nrfRr0q}mUSPSDZD=rnjNhhq;1_HSN*#U-t zu?AEW=W53yKyfLGOI4g#!DA7uCOX1Qf@5Ud9qc}iF2AJa33LWI8tzuH&l0jQ4 zL)m1D&F|^b?ha5>bcINxZUt<{Y-Ymgddggcc)6(0YPM_V<2))?(Xo ztw=H8?&bx~m&?;aP#l=KD-s83BV=fL(fzJ8`WIdLNlx)^@nzJYl7#N--UPgKl{VBO z(0D)ui+jr1eB7$AwsYH*wezA+WR@NM+x@F;VA9`jqWxXINmzNyWOr*rx9WI{(cp7z zvV?7uIM^eYO<~pmg9?XIig99yBW~si|MdP94pp(N7c6DP>anBt9|IQ_vBtc#jkTwb z)GjTzO|?=xe()xg+`Ap2Jb~EMi)o&5f4D`A6OJKZCVPLh9P$dnnhV~YzQSMp#b4sz z|LW&B&NB{LQT%fjf%9dG*FV}pXxgq3=3eaTkV}6^$*e67ViV68Z;iF7v9u3V)MM3Z zt^GLhfy6JADKs&HpYue>$$i&sa zhMq)#>u|oGIR(#ViBqSCj$OH{Bl>w7kb*64Z5TArlg0LCb=YL}zIh+Mpz3ux+%s$a zC+A^5P_+}P*YO5V;^M{&G5q%TfA6z3uO>@%iB2Gw5L#Mx&zppU(AeqO(sDKl_~gaM z=4Gubq!b(vM}OJWK7D$9egFZ8A^PMKoyJQpp-jv@547Fr>KW)n0Tc)yz5EFGxA$Ns zeEsI@?xm|RQ5G$3J!l&__mx$be1_mz+Lnj2Na@pX1E^IL7=*0PUDZAI5bMJsK-eax z&2Of}2)IMWf{7V-$qL-;+2G>RFtEwj)n#;nLvQd5P$T^YY%hrp&GEQ5a9>RVUO+B& zwFfd&XFnjA5@S)p3gB%nSjnR1*0P$ig9gu`KU74TkGwG%ym^i$zn7N9O4PU9D%M=1 zHSt79X?9Ot0XX?a%3yA_)-EA6uc0941;hbGEGCLLbZMig8o1G;5SCK=kgaOnnrK%| zG}>&-fz3|rE*`z4up+4y>D74%A;eycFb`3R1a}JC=E3B!$v}B3CI$CNdh}-k99rj> zeYXbK7IcG$Tx#hsz~WNo(wxW*Oy%EckqoFu8M)k993Iy4($`vD=4?Jsa72b}J=dC1 zP|aeb74GY;C%s>LJ$q!)EL|`3u6^ajtFd@39PuE7*s7z zD_6oQ2GYBYz+0nB;z6o;PTdA1M7SmGn_2kz4oggDeuURweE|*!i_v6skR#lK%q{~D9QMTl9ewj7 zt{^CGm$Hqr=}}}Oj1Rs|YrX@1@ArNmQ=0Jh?bqJuZgq><()futQgs6~d1bHzNOf?N zZ@<{z5$rZOgZXW-X>FrSBP@*x`z2f5hJVfX*oI;L4jdC_FnUBgN!S|~(xL5e`_ll# z1?}=OeDN?;f>(tzdxC%!^#OA_bZ>i;`N3;eI~iJ2&@8%Zf`j zwZ$UVbQiB3l+D|0>ZH1BfTYD2ssVM28rZVBoku<1E>LiukkxFrhXZ1oaF}M?qzSBK z68iOwvRqI^v1ViO2(!iHhJf2SfOE!DGjhq0>fUoHXl#870m`Pp3N3BHZ^zP-5lC=M z^9FS4jg-Atry_VmCmHTITkX6G44^U^ z?x!&yyEo(A)33d4-e~Uo4E3H|GaSM3pE9nuNv~4iW$@4Mfn|Et{_f1+fm zx=S_NUnke<^M0A=ldQ`t@A)&t_^YgSz5XoM;uUN|i16d@{qxUG=Q9prwwDPBmTKiK z`*yTZLA7O;d!BKMgcR*zEHxwYgn2q3OGPa!id4Kj+<};J2nH5e^MWY`q`+88aqqce z5do?6hi1#K7I6Sq&K8wa1#jQI!`E-$;QjmezCG72ohxEqcZDLCOV3XYK9y#dzA^>G zcmxHP4NAdUa(_6-V38}||L*s&p3We$Z7KU^c8&=GZw4qJP4@7QA$t6qVy$N^c|l-B z&KHz&@n{nFym?mm>?HasdCRF6F`B(r%boTHzWOG*)QTx2e-LYjN3_j)4D1x?CIj*; zbC=3{tT(11q#=0olY9UJ3-ZCD*?9zE2%91I9?Pys01*<*3o7n;mkt9$hr5su2SBY! z+v2KJTv+67nOOk&tP*;#UbxYy1{5YM`{hAH^gpCAa%abQs>M}r?SAt2y{y7`~ga^dS* zfiUAy7H|kyvQe2iO$Z@442N9};1hq(76#16Y0Y;;F4cZlh#v1mxa5jMnmc`+`~{zD zF*uY8p!S(_wfiND#VjtR^bLX3f)J9uMr!e6ur<4+K#Z18-=wxJVuqAg9& z1|^c}&4Y0t8W3`=t9_p^88x^Ta7?&d7Q`4$v5+&06t|lc#E`lNGjYJG+U+szCD`)d z+e@JNPkG3I&mx%o=hbCbRpF9JYwp#r<*I}^#w|id#I{d0cd(s^!Jn3rd)k3DK4v!| z2XAe=Kil57$+~TM>fmR0JAhh~1`#aPHeruB z?ml4*8ym)!CeVCyJ2`Y{4NwJB9WYNr!CfzS^X9E_J#=-Jg1^h%lNkd0Kq8u#vt=?5 z)MM2O(zWN5tClY9w?7;D?B3g2qx{QX{u1xry#f6DG+#htV6?)Tn1@sZ9pF{u_p!&9 z`nemM8^xBC&|se&HuApu@E;v$tHV}oqw^ba-hBGZsoOeCLf!`Z+ihQ?s3Z4fo`cv>+ib%^G>YWOj{4`NK1;sM{je}Qk!2ONnoiDJnM zzyVpLTUOf9DpFBOw#5N#aNYC+{#$)@qg^FZb+e$rYa(Js43oV}i{RDq7Ke0$83e7X z{f?Slu3d3iPbd`#Dd0HYAjODSD%QLhq^=aynr%=KJc^45F-CBR2x0R3VzbPVs~Ln$ z;dzr%ukK@DOd%n)h)wcnM$yf8t{9+b;!wc~a6lX)(26T>#1|1&oBGB05yg)cbHw>l z+y<##vfJA@+M0}Jt7y$xTXYE&$6(1X#jQ`~x8GoMF&N)yQ?7aJmN(9Uwt*7!(EJWj zQ{%9xEuF}%hcaGogrPB~4qt80ncAW+p*~a@6Q_r*EU=d(?_|-m+o98ycYfC}>NW7I zhrffs)}*&cNFIpkZuM=SoyJ*(Zc&dTGk~tL#`GDsxpo+!dYS`mB>Ulxl@a)@dpomj zbnRf&QMsd6kTjyJ*f~6NjOdw08oNQWm(JVfe)xO2*?T5?Muo09W38+Cr~R|bKwB0B%yELM`R!ty?1i$P zAT{HBIpN|IRBAcYQZ2u`RvdzD=xepVzlz}Fn>&Y$dDG~Pmnj&~iRnB5Ike?uGVSJT z)qPpPrO8a@i)JUV7Tx^+GUQcigDkN(4Bj|bGQix%JZ@ku>3O0BZg)^cMco`kOxy2L z&-d+K68CA7ZN|=J9pq9b-HtJI$uBpE6OsEx+qNG#Qp5#<0K!TZ^@hlZAsTQk&A6K< zXaHW_e1iY}zx!|StDnEdyX76!rDxTTK^qcOwT8Ed80@R%w))Ge4N#g4TGW~>_9z&l z=+pJaXt4Bw+A>lAR4?w2DWQOHOvwQk+qMe?%p5#(y-DMOs}bxR8*nfH!|b0afZ5a% zAw&nnnN&HYqZbJ#S#F*-1EU4^zk2dQ2u@w!Xw>T4-KOvm-8K;BC)I9IY1b#YkGwS; za0zI8eK%!Bdtr$(vM4wlQN=!Mh!G)8zLC=&kyb%CPDVip9=X_JxI_)Q*W+Q_AFiTE zA-Hm7^AU=+;g5Zm?1h&Ch)Ay1aW)4>l(HDfzS=+kN-IbV5~gm2(B9C9KRcPswxK#^!nwQV#4JG@bP%-%|jOo|o2B7zA9rwhbb!FW>(zFaHbmjxFgtfH2a&Q_=`OToKU zu;hvp7!M>kiQ+(vDF875DFoclj2BbDL!nNWf2@M)*O9uet_tDChc`^^{PDI;YD8b+ zow$cbal#BbS(X0c6h5zA=q4L%7ze)$pRX~MFusN&GlR-&l72ibnt znpdp(pYIJ$H+BhfDL782g`MJEz>UBmmdz|y?-udde;%%c(NFg{=i91hZ z3K7YZLvpRC)#4}Na5+=K$*?7DjeWF%Y6Ft``>eQem0bDhYY30D+=dvcwW~y1_ouwU z0sBDKCF0SoAnfYa6e8v{0j^+ES1nM6?>&;vyu>D3ei<{)Me;R__AC2>;9e{x3ew&Ys|9Wh5>^JzNet5*Xp6u?5`#2 zN?lf9zH;p2v&T+bKV$$O;BIu)zk{9&I@{Cf_W<~(^7P(7@6=4gJAk%y9Nh`zpJM+= zw}h*HY4qc)JM{wT9(ea$qpV}2F3+payB_9>R@greup2bchBO(52R2jURl*GKybD9wt$Te@50NFz*rRasc=K!Y&Qb85Znr)v$2iTG~1yfAs73$R6 ziWm)YSG)m$Z|77NfCP`{Gb+@JWS7YEMuk=toE)?isG&gYAr#mOyu<;Qdqb9;}M zFYfTGFMiyBE>y3Z9AUoA0cGRgEs+qVmkA z9b6j7UG6QNj_ogG_P8`JOmqOv7118eC6XaTheK1ukrf)22up4YMb&R8`!Bd$y@(M_CneOuF~&da;i6| z$?e#8V&WDQ@@>^7Z(}ErX&@9)EV+VW=pI&a5EfKXN&Ch{!Cl!RgO`|?5n{ly*8Vb? z!erN^`>w$eAtv9#*e0z}#cNd`f^7cS>dkxcCa5~tNIg3q4mC@jUeF%-ZOhYm3XK50 zid|2tiYX-IdiDmRbsgHh*4pe8zKtS|n3=J9iokg}8EnoiIAO4a82qknQAbDBCRHym zT6b!GKDA-fDy0upvU`WE`K=algJwUeM(Y(*=~x4)_=lhW9IxMeg(WW=6&*ko2S-Wu zCWL$wvVm)swc1dnw%EE2oYetAFx!eY-*J~y)h&942op12fDs~ZKpP^gN?jhg2;orz z9!i0R;P62*a=0oI5#A|~#lU2&iX#(pJI@d#00$y`6aoSiXE8N_WhC2m{#XR7f7iwD z%@ z-jvt=42S2sQyXBr^`n=(J+8+$`hJ~TnnCg51@}le^u~qzW+w39H)T7P_T0*V7Qdd@ z8uK=sbvd{WS=-n9qaF#Q`XqOm{ByMHS+7c|z!4mi4{0NaBSM%FO@>SRh@SyQ*J>ONyvavflRs=S zdv3bw)v$qfAjfe#rbCO&eQ{#X%9qDmeEV>VP%E$Wze^;)>Q+v!#RBCk~8g?6U z@%--IoHd#6ZoOz$De{2|XfoxTbLIml01;AF1@0BRtw)3Ddx7*X3}AosXP^vJvC(xjfC#q=sp zORr?X16XpuOUKn9jE82*cIR`j@5&fp*W(?fdrJCKp!~KF>w{U*ybtqSV#M!w_WIWH|k122hGMziU}Bg&E{965BcwL{`u2HfhkM zEC^LlmJ8mW-r&uf*XDOD7Xzy~v;*WtI}u7A?^ zE>#gIAZx{{1!2gW4A|f-MOQGzKIjqORIVbJLPS8Y~zJc@O zBjP+E!v@xMKnN{5T>;sfu0C7N?CLE44KK<<<;m3da$9VIr zH~9SRYy9;WU*cC^{sQOA#R?Q}Msr=Q$Y&npruJYKmnjDr^MHKF)qLven{6Us5$)Gm zl>+97k8kc#iaYugeahZE`BUOfDix0>nmq!|Vv9nLjuJH)di92+JyZf2AveV6@lnz( z1vW%58DuHihdBz1yMih%%VN=h)tgH)BEM}&s8fXPpPrmRM(!19(S@yvZ)pL(t&nOg z3RO%oV3F$Ke+1QnlxCCaH~%)Q*(}2LEKx$Pf)E%vuOO;8%s1{iw}y;Eqp)uSs7{O} zHjiMZl`>Kaz4;KI{|J%>IH$KdS1BWCKyrpIKt zue{cZd74n`20~(guC=JIwHU=arijz!gcu`=(}6@bsH76`kTYJTgxg>*go)jjK!PHK z*NYVbr{Y^xae2A;_P(VqsNz&=FGyDhRW8NukLXlmhT@iOh!tRsHb7W~aVFceUzl-T zE>LDfCH(zm!8t~R3S=={g>X<^rT?)7fj9Y)+n5v^CRhV7MJmqH9xm2_K z*g4x5SJma{aMg@>BQJ6tB`e!zz_|?4i$~u^g@7-=`U(#Z56IOe!me(JX*M6Sn%ZZn z8No+=ZSbO=wcMa|4QAE6$IOi1{Egql+jnn<&5-4!2X4_?6`~n|T@|AlOu@J98;c<@ zcmE}~^#!{e)+Ov4q^wbLwjqKIsHGRv7Rz>BCs9|@sBT&ET@UYyuW{ekH)b4N$>Nf9 z70+mAh%AWlV1xDEl<+1I9t%0=adcUeRVvnD_@We1+qS&Pb3r>&w>D}vwzypm2^5Y%za9uF zMym&FMv;sxgf*Y+ogWAk8OOtH%ph&5#)2Xl%s}J>b&`7a?Q_v$U`&w##@U*4=`cn3 zJ;os*x=NFX@hTa+=i&{%w5|3ovwW-6g99KdZNpnpHQo+c3Q2FcwW$t#(n~GQP5WqA zX={I)G`wkix-ZuMZYIlQ-y*+4r=UYDsiq~akVgPpgs!aZxU3N}g4MDvr9)>+NL!@g*mZ0Fyto<~Z>sgiQ6L?M^ZRKGx#D5h&0iq_u0NJO zooD08j2A%o_Q&7*Y?HMO8*TRhw?;07fGOPpU`+E3?vF1qPX`?58x*lj-OIY*#mx=Q zOU8?v8@xQcz}J`eNStt_fY+CINO86Xj2YiN98siVjsd4q@mt^fEhquYa_*DS0L9vJ zWnG%xq%1~!7xyTKEnWrNv^T&cJK__@zyUw_%}?>2Z+#nIzx^6>j0Tj_&8Jp9l7cbaJJ6g<K1iIZ)xiessnwf7Yw(tQ@qxr9WzN-u>AX-crFf1rp?U)iVvu^HuGaXAM;vvj z8=GUTRR383hh(t0EE{Dw#NZ@q-6*a;VsClKEzYOKpNU6F*&xRK-+>)q$9?l3IoL){ zffOekk2hE@7i)Oy3g*ch)MAaTwuTl+^8w5G3<}&mQjw!c+g(Lrez=~b>>26SShcOW zMQV9t-wYAAf$-8L z+!nb;h@`j>%%jK#}Y~yl8Ujka|J^(I$=V*haU4wQH1o=+?4? z+jZ3dW;Dbxi5TlcTgG(Hk@XU_FOXe<=TOk0HSZhX=qwrgJrkIWlMzGO%+rJz zLw{B-#TX*B6x_`RuoRS%ktKJYhuy2&Mz;~YrK?qRkP=hRIWJ1MO#ycyS>lS{qjJTU zb|HLklH2)|Zk3_Un$o1cUMq(l=O-Omz;4}Z_0mY}W*=;CHNJBqhO?idTeP~g+TOvt z@9OLn-o`Bc=h~rTI=IKB}>O?n}Wb0i5-Y+AVDG<;*1wGNIH`SvexY!-X)pLQwuRnmPIkmwcMZHMyM!_cn<1oX_loSwnqjR%wV765J6@~A-94XCK& z5a)WlLC=hFn4TQ>UF7=s9Y)2#&U;S?pMLYx&ss6JdAZohtqz~@7joignaX4koj?{F z#!NlZL_nyp4QYx;D~^yM0PfQbsuuGQH=mzxJX9*a`06#@J-q8g*f_lWS5c(k`HEni z*9Flte8-oZHK-YfG&xjDd~)BstpPW4LWmKs-+bkTtofzMrEznbA+q+!sU}Z~AtI#& zpd#l2MaGn7d~|z{(`sJSmZi&HWHQvixuPAYqWxQhGG)#S6)CH*0pU+V3{`~FBW z6@oXjK&NwGmc=)>EvKGb8pi$MW|3nw$bSO<@W;Q0&%gSzHyy#VN*jwu2@sMlKMb5a z&O`gdw?%n%Ntb&;RSU!$rR){~%OSRA1ge(xDMr=~98o2sO7Q~BB<`nWL5vAyT~Th? z#D}$k+l&O;w*OU4q8y@mv=7G_fo?ISgqxcQ-<@vo=}-PS{>4xJ6!)Lb;PMi0-u(mC ze|U$tR1xZI0I^h1tH0Eha4w5eX~``G(dqv-wI_DMcJuKQI@!J{2z2nbswy5X=dJ03 zNemn0ka(k7w&p_MYx`D&I3&m#0QYiNlVsX05_L9wTPgBEif4%ra;oZn+@j?2 zT~nllV4HRf+h%HouQv(}%woBm+_$~dw?5qck44(~a;^nM-y{&6DLsve&vn`d3E z7~d2QjZI;9>!no7gBOdOYMUnHk4mac~V1Iw>%*M=!d3 z{e89diYQ@aQ|K(!@|ar#WCKv`&0LRU@=c<2e?2+@nR~k=eM?P!ckEI>DkECE85KiX_ufW3aI6^ z!wKiun$#9q+J?uyP^Z@P^FB+s`>wYLv%sM@zb(ZWEiSI9cc~4vw8It~(6FmK#$%8x+&xk*-nb%FqeoZ(UB)0vS$W>PIAhGs$c^JAJRU34Fe7MIU&G_d13*3Q$ zl2KgJE(()m2iw}GkZg!1mM*c@?2DEO-T2y*3xFw3IHZI_nsH1MQViJchQVM!4B`of zi1oyuc<|xqu+ijzT+d);+#F|2DPaz5iGQNqsztB}&AVvJktJuWQlY-MSRKUK(tUQG zcxiX4Jul}tVTuMXPBG%;G=bYNvDWUf?+czGo{Xpw)eQ{WbH$tePHP_wa96_!Sk+UI zHd|qj_-dPHCC>}@+RO)K<89X}gsyfOQVdMuu3LlN$n>Kaw|_Tmji_#Xm9`O=rYcgs z+W21CDRlGi>UHbk13Bp%%Ynh5>Ww!&=^dN0l&+Jf+Vgd{;pgjpZ~xtWy9_(769*)( zixgMh`46nS?B@!QVD{UkxZye(ekOe943_Opz`N6zs8w-$bC2M0VyY{u05MEZ-yA9wFQ$7GEuMKTm|V6q z`-3hpA(3rCdJ~J#!F8LwyIL_O8OQlxn_|yF4NTVjaX|2CtdTEG&fX$kYF%-!2CX-fRrX2;@ro8hrkBwiUEK2yMKf~_`~1FU;O3&gS(q# z5+`giaiIae99*Wbg1pJIdjUCsFsc_~Yn5J56;~0MTy4gwtYB#qNF%7KXEWP?BVZNr zOy5>{ZeH|%{Nfj0RC~GO)BQ%}o*kw~fstZ@*3ze8!Lx_Cc_#z=h2`5lj!^&a#}w@a zr;0hwMiOO$X2PHUi~kIt|Ke+CFe`(>imZvf8LpYE4p@p&mu$YlZ1p9JTnTNuFm2&1 zWd3_q+wu@wGD2WBzrH`eQea&2ib$4vV6lw>py;yn#UI{ns3P6NX-!c@5pkFpVXXz! z5;a5B>6e65dkrjBoEI7L5gGMCULAP@JSVogYqX4xaSjs;CaQYUkPGjC|m! z()^d?@p;vsaqfTB+P7%78R)jzvgWn_Y#&nj^xoyh zaJ3OU#*sQGQTryEId+}{bL_Hk&s^~+z{a~^1k!$=Q($MRG-aM_BLEtHM~`>&B6$}9 z#xBFJ!!|^n4>Cjx?R=8F+v>G6Gwk_5nC3%ID{0C&;t17@D(mKeWQNui93~rTjXC7Q zMyFG`edB*RpYZtj;FbgF14Q<;hgLKO3Me(>)ytPiG2-p}H$xt>_S#6VM7FwL|MfVy z)~q*QF;nn?YUqWVeP0Wbgss)-CXf0h~RXrw@}#T-?iq|>y&^HCIp^rbIQ%CkicQ?Ltr8xq^ZZ` zby;@JI8KCb+~49RB}5QvSy6MrS{H}bS&ei|6M_iVvZBD%cWO(GHLq6p5h2&=v7EKX zwW7x|228HBQ&dbL;20PYK(1oo;UQO>8%@K9xzq=Jwz1a^d39B1laad>f(IT)YZ}`X z(YLPtTt38reZf*zfGEg1$`x%3zySHTefd*@AUU{RKQ&*~CNCKzby*7g2n(Uhq5z!WF7Q4Hb zns4s;A=!Pn?n4RCis|%XbJ1dVaeq^uCD5O^($BXa8MLaug%?pyc6`EP7tn>26mPVdX`qVQQgP5LfsEe!f$-? z-OrdT5-GB6p9Hl}+}NQa$sueX-@ihxixZKn6@_ua9A;!N*7X5XOei&DiZi}`{0bl4 z+~Ib5fjLaJT@uURjNbTz81I&o$#eVU&|dt>rOGl;0#XRpe7HQ8eUlI4wyY=0LqrZ8bC3#N2L&KJv64%VC>4+qO3^}5E#X?=pzSfPOE_mQwXjavCmR{_!XEf&$Jhz&Oz{(Zgpbp z=6yAJufmo2hro4zAG}fA2&rvqD;;8H z$2f+FQgR0oO)+7q3l8yM^nNmy!Ucl2mx|Z9;!RbY3gN3#un=%k!MS7`j%dZnH%Av$ zly?3}ady5398?s1*c82pT|Jlkp(-xL;7to{(5BGn-(D0>Abe*EI0lnl|0>&ac>(an zT5(_kB;%qH$%h#)0zfE;s(3Heo^{bSgn`VaksZ=zF^MhONIG0?DaK0Z1*}R}l#o}8 ztth*_yTkdiptv8l0SiTo!wzkuRvad11DLJ&FQwS?*`K-GfrNdBwLPnM)d*b3+@jGeE0q%d^F!8)(Wf_NY2Q<8Lv`NYO(0g>3|=9`eXc8fBs+M z-~P?dkV~<_L+6h%>#Ek+Qj zNH8G1#Bq#7vGZWqNFD;@A%8-if*{CS|5bU*Lx5b6K!9V!aX?vC>_`etvPt&2?7dc1 z&CAHc81qtV?;}Z|n(98i_qtTg8kg_;eN8P`fw0A($#ml?E+WGQkZ#yybQmTbqfe=2 zsrQX7VbIz1|BSI=!-}_z+GLSqLk4^bvE4VWt~bz3)PlL>#dL2Oz;T1!1|#=^j+y16 zO3>R;f(#Hme8NvYLT3A)A+vps&F=AM^zg}^Cw_?e=3|H22SlS4v0o!2o0DGT#=YG2 zj;`-puXp|Q_*rD!;PpQ=pF}#+;4p2dtV9x6Z=-EaP^oG z?wjsC8n|Y$MiqmDUF!na0&sJ{er#h_{RAgxSP)q5WXEw_$UP@ zi%|mvAL9m3jHj47Y1JgRNpx^fV?I5Nz`n5L;onUqMXp2LD-2W~bQsWjSheX#05 zmwWUM12JtH>qa{5UH2CFjy$|`xP1aWfat#URQ)B|H; zkIkCm;2gFT4Z5R};tDJZ&NpvW#Vqic|7x<*Z-4PEtovrZOO=d@2u{=78jb2RtPv?7 zU|l!+94)`J%IsJVoZ!6r5gC>rEFD-?fg4tZF>LbBu6@3pmW=lIoRM7>4(9RG55L0G zzTwxF1(#g#`m;Cq@bnSh>jqS1{7Oz`7n(%)8lw`orFyg((|p2G7^gHLc^N3_YHbh! z_I=0oa>4$SkNC;sFOf^ZW!rFxx~a@!-q9R20eK8Pxx+g9DL_lrk>s6C4LUM|0Hgm@BM!BbpqOQur)0*%qUmV&3WA{N1pg1yD)Np(eHVXqqwSA-9Ft_z&fV9?nDDO&|JniyND4 z+&1iW%P!O!g8EFRQ94@R{|&{b7uRyg=Pk!TpQaS)pdztnPww7l$*M4s2=C7*k`&%n zz3;bstDz!NtD4zpYE+ytII4^|xD!C7#mkX#FGM=giiXu+hxrXKw@S9I`PuD^tPOkp zeYO!|X;3(^BoMaL<}O!No5cNmhws07i#ZEQ+EMlmdnK}$j94`3gN%Y7Lcp81Z}GFA z{0zVP@HMhQD*-_&JN9Im-DXkjpI1RFN$Whb;ne%7)*oGR>>=zG!p9Embod!_f^z}) zbHMBwpUuGCe2e|Am%BF_ZgauXjo z%Hxkz_2WkU7SKL6>Gk*Xpm2a38|>cy(u)mUtx>32at8=FHNL^{*&kQD&mz?XFF@dj z=Kc2b9h>g@owVij@E+SCUN`(g2MWJ5ux|I=Yp%K+;H+{`3VdR_eUowiEQyUr=wAgi z-Zbg*e3So4UjRMd+>ie9rpzFGazOHo&?lWUF#eDGjc-pQ%(iz{)Blx_V~p^_)T@#UYX-Jsr(1r5gRo848H~MTi;DrgZ3!S5uReQ&bz6I5U;Tqj?Qm1F zD_Yib^U)bN$UDZ&R%9DkXUhqmYNo5oj7v(m#%Kky9#G5VP>RFf{}29sIOp*7yLTo< z%?Qr5LdUa1GKdt~R7yH&(jNA)yh+bBfa zezDPRtJm7IKHQ>tfCWKwz1f=_SuEoha7JH^Rv1=UoqA6yA-T&HD*;-g#WL=~4EJz{ z6f^D~?yz33){Iuc8L9Wr{I_e|5loJlO^)r^A)Fs@M+M)0^(}mmD{|5Yu0XKmT{n#8 z@xRUv`}K---w<Ky1G>t2)1kuo+zkrvgTW5sikGoI^3-)^iB-8?FNK9_`b+mwq?3YK{>ZUG&h z4HabO9b?<~b_VS!qvWLd*=+uKa<AtrAo zz9-N8xTH9FKh~P3}(Z&94DPXq%5|!;@ ztc5MXXF4BXZvx8;B;Yg;Az-_v};58r$f*mi7j!=Cm=Ay3(|!gEsEI1|XJ3UIw% z@!{zaF-C}FWa#b>(8ehZmwwn-GAV^@I%|tuFxqw%v1$PtUE-+4x zF$a&k;PCo9&rt)?9QY?obUs&GUa z{!|BPm8LKm6kj)6VyR~NYP)=%hSi1{4emJnPP&n(_r&%V6W#}pbhBobJ_I}%Yz_qX z=c|2b`(?Qiir+e(2OB}N`S8K=!=qGsgvK)s!>4hxCMS@^^YsA`(@k_8jsX2ygwPE@tu42QVMQ4nK)8AD^1`r3 z#`U@!bM5QT<=cqRXFB^K+)s|l2RM-NN3{sO_)2vV zoO+O5kHcU|*pkM52^df7qfQ9@goo2ToK1Wk3f@1x$NS5Bd$+U7xHn4A@mY;kT*<#tA77L%Zph;1~xL^ZY8p3=Zp*|LiN&HwuoynFYx zHdRzQ5wvu_2n}2PdKj?9&{U%I=XalJ<5Gb!&Z$4uiZL?3A&lh708aC4(hzCcuak4_ zW?5XY!(89DCkNpw_j3rW6!g#TUm?cbBxn}%Bt<_n%ZVLnzK(qZkz|Y9s$QkK^+t+# zeSeS7@7~}ae*1@b`*4px`|7VS3GldWZSzG0+?~#toXS+4c`hA0MB5q?6LMUU+2gD2 z1Ge=eq5xqIjUFLV5Mu(Pz@-_N_+(y3!Q%L-{ z7F<$B(?btSbXOCacqff{8|UlW9| zr`U^lvwhV2fh}`4{_bgS=U%cHITpRYN-5Z~N_vxBm)_#vJahAK zCX<0OTXZ5j-5M7qjf-t*9TV6q{BTb@{_W3yj%~ZDjL`mh$CTOkD8 zpWm8Km27Y%_Boq;-nOsK1+{`0+^xoDX(U|*Vp>6Bu~=2YN0m-%g%c0Mkb0h|yj`C% zx;fXzaBXt`uGpz;fvQ?6L~zc-vyLhm(BUJiJh}M~VKG_E$T8aH)riw}jcA;gH&BUw zzoleMK3Ef;k#aQ7MQ%gP)lv*{N?7+50C3&b#!bj3@1~Y*Y`bw9p@6RY@&uUd``B}W z5F-}B;`FQ{K(AjAGCRE6B2LbsWCd%+l;DN%CTPQ_(pNDHO~G|fc%KB3ftj3T+=^L6 zlwptx0g|;jm!cWZqqJ3e0Fpi@T$7cHB^JaYn%gXzckc~;)uWzFx?P_GH!#q#03SiH zWr)Dsw$=C_dQA#4kUL|b4u31Vph!W?3ErJ<$j>@hP&_VK%u{It2{Ahjk#CK3h178a zZk4uS7W?kOUK?ZSa(oi$5o)Cm5Bes1q_~?;bJ!|3O8emZMrCmbG!m^s{3L^_62SbN zT;s{u+aPL3a1+>_Z0nsHxU=TO@A0blubDa7%{F{>nEeUP7#PI{b48*msHCeMT6|o6 zZ`@ZXBhZVjv0+ySaNdUD%3yFb$W@zy%?;jD8+0*Y2`BtJufK^96V|xf(xYI9%Fs7U z@JLV&gjj^*mJ41lC!{QhDQcrD#jJa({>T;!sKc9@v0Wp`fF7DPNSMEt?PDz8TR+V9{ zugV&VVjPKvj-z7(JZ3Q0XSOb9em!OF3XvjJW^A6m?w#opO_zpz&Gao>{dWGqHw~nK4^4jeq#qkyIV^?`O|?9;tb^z|knK}) zb~?1eM;|@mhLJbI>F9Q&e#3nGq_q78kYD>L!jX+2FGaDn>F{Ad@Qmdr&t>-RcfR|3 zUlK_JRt2vITaB*@p0Vv4?&cF_1|kTT^~oYofV*kIdAZX}S7IzYVUq-*0_P^Yy8j%f z`A*>?M3`9puK|p&(rU87499}=w6sYrS@9c~O}?cM^lbj>WS;qG{%{7P?o_mR?ok-r ziiR4sQ(946n`l7r>)-hfuIp-mJhrKi9+bf(a9O%!T!vf+$JC0CI-O}bsic14FTU{w zLYT0vR|75S#!bDdXi;xew?B2m&njDV<}Hm`w||R)@`_{x4jl>s%}YN`6P~sm-f;uf zR4ILZUt?5*8{#aSDHXi)2$RSC!vjvsg6s8a>IDUyG$o@wM6xv`+0+(9&b&Sf5?fP7 zZ~@*i7IrwDXME@V1OCZ>{h#Ci{-=M2UtWHJv_;S~HE>h#9{au-7^i**TI@^=8ydj~ z+yF|_&!uFO2%;7IO8-~?dP)~Ot(U{&2KKYNtS=kQT~!ac#ck>K+!0~%6ZX8f9Q$lT z9wA)d48ofBW;<9n z&Dh@4YI)9#)xH-ksFV3*(MZ2_C>2Y=;(aU5No;Fx(%A-CF)FmL@oM&T=1tp(_G%9*#YhX#tVLG=%}0qX13kTpJBnpdLZh9x-Q4w6ORmg(#~3 zJrxBzl2j!{()c8*WN3YTF=dU`%*l$)tbj%v{z=KEzENrFmI_2-BZ(Ihc=Y(aD(Us? z^jH$hCx>7$3y0m3SV%qY3RJ{F2A;FvDg}2;xN}NrpY5zoS&R0m62(oH8icSCuoob< z!~kl#&IpSyCpydS*x@Rbs9=svMt|X$1$kFj&VA+(YE2%5X{?H0G=oNtOJ-Q zd$x6Yzf?kY#hHdv-=Lba(#Z(ge6iN>s7I{JBa7(#7u#az$M zu7Yb6Vjkkust?}o(`B0QyKl5Pe_U6UV;7A#R7hGvh7FQxnv0*Ym5hhe9lV>6@{X8x zjrZfuTZlPnvx_RRw#MBCOU0^RsS!)H$sh(e7^skOngc#xCU}bzENMe22~sj*ESf5j zcLkN2y$B$boU8$^@KE21=lxI5Osv7L~+@Uk0 z=PN}rKl7nk$C!bB+*NkfNo|nm1w(Of^-_+5hnQc~*x%C2=|D867kW4z5Oh3z(tK#3 z$$)pRg@p3Z)|>i{M(b4v1J)*fv@_5^As-j=gBb8xblUNAzxh7ohTbR7{n;ZU1NN+V zCOvN(YI^?TQaPtQf>fx5vy1QVTxx`JMn1>j4>?XHlhywWv{;7PZKn$LmFlxq);}CZCRjx6Vj+cbt+WLAYbi9wiD7HQBq*225Wmi0kC z91Nllxf++V#kzr|R)A$IR8-z>kP=dvBC{6ofgFnWxc3w01blt@sF}=d76! z;H;nXmJ__wjrX26h2WJ0DFqu6VitU$NBpz@{{O)Lv;Gy{ts6eZ4O>jwP}!7uv#mDS zmexDXT1`oTOncmQ6D$PB!Q^v-HAbx4YOfm>eU!nj;KRp{Smp)SxFgtv))Qg1=6?1; zy=p18jcZN`ck@YOBdmd1?tdJ31M-|v=_0vL;4 z52+}zJLLpH!5T}8reY&VS7icoFRoK5kh+Nz{c}bu05c=_xecT0CMx9!CfBlQUG>*V zQ6Imk+_ZYm9SNM%d(L@>zxd0aVvj3|F&wI$!c{ph*ElIH=<0)TuOQ;;H*8cL8;*I8 zxJQr(ybqcKTx#r^k({B!Oh=VgfApo5qCAOI(6)x%ZlGtAcLD4a_;;GlNRkzDM;dR) z?BN|Ba_f6Wr?dxuvG~E5V;i=4=dq_`TX$!w2URl=d$o0V-5P3wg*U%zM#?+HvZqT{ zs7Mh$6jyJ$D(bq%>SV zJiz3#JzKhjQY^MgGo(BFLu$O3ox=pQro1+66-cH3PBs;Y>r!hP8(T6tbR7;mlT`|U zsA>$8Zr7rhYtxGwX=b^_CL=~8RNH^Ip+K?C>j z1s{qZ85-UWsJkW1m)`PQgRTws#bUlD=q|G~kQZAa>|B83MH3*Lw=^1o=NVCUQxO#8 zvIAxv==L*f6)i&V*4y7VUG~g1K97SptCmyy-ry^(&sD0|P=iD{{P6QH;A6xVH*C9t z3Zubqg$jg~VNpcXO2PzkldY`+0t-;G0nd75rWp4jm825xQrSPIL>#uR)iX~vmR`nj!^Jd-+=L2yc3~PTtFWH+1VLA;i_~Rd-y67gf zfnXi**PqSQ&s%z_S#Ko5=M0g*E{*K)JqjI^Hbl4Ffm@K>4Iq7t*~3wtPd5RY<6zuo z=@70jzi+e;gNqhlCmlR~OIoMr;KBp5fx0!N*)GQIHxHjLkH3>z&GyVsj~n5N)g10` zeftMr76#tjy#`2dT2A=h!)LgrRAZNM=Vsj;3M0cKIL52#9)yA=oNzzA!o8c39ODu< zI5z58*xm*$a2}L5jn-J}wjaii9og+5eme*n_SD%;Npf#2%l?p9^d8D%y z`s$P2vlWi8n5SVT*z<2Iq1I$vIqTMh3419@T9-Zv^~AksU|{wh!Fj~2Io{<)wpTYv z$tI<7-|_f#!QJ_clJ$Mm12lNwOd83iuh}*{WETXJ!)ZEU3IWlYrrG%h#B&4#l2vtp zzOCWZe?M zytOuD@U~VG5;zLT);KsOoKGj@lvE!05b^bO)0i^bssUQyzXZ>uTc(V!s03ZNKL_t(yRxfe3BnN6k4PXl1YzczX^a}sk zKlw-a@sIx%oGb9GQhsmVT98AG8~nf~n7u}0R*7@A7%?`vaBp_h%0yJ9pvsznxaJKk zMGyjEH+nLRe69JR+Wxj zNO==v`S9c%&Mf%c5xmo){<52#+6uay^=xyJ@mMtB;A2)`)^2A|Zh=T+;h9ki=^(4w z9-T1@q=9M76 zIrP13l?9JmDzWnj`0kt6xEGCwyT(8di!!{Dd^Ecp&VXh67{_j{fd#8y&-l!%;a160SJIMOI9u!5!#%qykB2mHmUq5 z2d7b$V(K{co>v$mwdKjP9AtSpj%#d8OKa)$N{~YqR?le`dEPJFB(NR+%ov~6;=@LX z&?soPp$FXv;;ZGdd$@0@)N~kK@PuyXnI9H=$+)q2`mei>hJ_sGk$ z6nC z8rPY-NBqU?cOJf7J~q7~M}S*F|5??KPLPAW=&#YzAMY%p!@tr<@MYNkyRI@A2PU|s z+{+8FyD?_%cfR|>FHa%Bv%}r_jJst9zq-Tya>jW%;nn>E)^!JQz}cOUIb(6NZmh$C z0*8ASP#`#Sz?!bOY#;D=`G^ne2kh~xkR93K$>Zz;F7bk2Jbs0*FYnF6d({Y!lr0BS zh6%gg%vJ6kjh0*ejbbF|y2*pd*6Xy$yUAjasBP4e(WvW&ulw*6a8ry6kLIIPsiL5I z)}=|s!4ALnt#2a)k9A$!RE^E=tC45kE(nzymQu5ai*4}x#UE4YQW~oC!#>q4l5yE% z+tem0&6l$meVv|_7OWmmm7S{On(*2A9x+FwxT0lGS747LLMF{e4by}#KK~p)`h$ND zzx}uW4nBPN3hU#g`HeL4OkYzkfOKmdrm5vI$CA~zZ_PPFf5)r4S6H_d+qPkn2_JJb z>9Y=4G{!0-c-Qi@!GwK}2+X#9Q&1y|JrXsveKDr(8)BNkkW?9d)?GK4|dv^kGM(Ne>bea~-9B`iR;oM?_ z21c?;<8@o%oyWHCAm=p(Zr{LeK`v^GsBpxbR){dJ+r^~!3G1E=pk#^;0@f6pq<1d~ zlM7g5L^1AyZu2!WJzM-$Hu*epm)>`XPDRNuvLxh!R>*nh8}NxNy`UXlWI#A}?SWlE zTuj!;n`@_VoED`lm4(skSHT2|Xc$$7|ucAA%NMNw4~2?1M7#yIK4Xl0_roDb9cmI5s@_?(NK zi~5?FYofFV|kMlMX(K9HtQ9{Jnw11kNp( z+{tL=D;%Fd@K$e zLTOTLEh$ZjMRlBl!QyCkOtJ0wuz<~nh^}MgS-scV5~``7Zr#l|K3C-iv?b4tx+BS` zwRH3xQ7uEZ*DV@L28=chpGmg@pmC3G$<#uf#WAi-$zF&4Y(7YV+ftlvHt*wN_a@eB z95hf<42>T~cchmh1Awa!{%_mug9?CfkdxQOk8fq;cpm>%uTy%4(IfMp^Kqd!PEALZ zf&H!A42Bp|2GEqd^%=mnTp_z(+0GfRA8{kB51?l025S;}L8gsH@qgB^Qda}ii?d+- zjK-|`Pc*;<&mu-aHx_mJM6*0hi3<%$?o>0-hkNcYJ^N%>+l%pkau`Mp+3dH&3fr2G zDz)SLqC@-}{S7a{{qA>v^ktTe^K!y`3OGHy!ax3p{|JE`K7M?U^|FFEV6*&gA`b`F zExGxKlD*-t*N=Ey-{Bf}0E9j5SVF*?o-|xW-*+()AFp6k##1 zj$Mek7&QCTu(zkpbOmO`s+YiWQIwh>GbZBm)I6IdJY zLVx#Ox?ERE!NNrF)};kqK&b0Y#TN~F`|8s^ z9wNxOG_T zP0~pMrT{5|n0KUHux+bC%gnp4lICJE%iN%ldjHZPCZ)0&ibRM|+fG$kW`+6@>%aq6 zL=rJPJB5-tXVh&TSO{pj${4l|)#I#1fS4Rr)dsG4*ws@U^VYUHIcMuPZJ6mM6r{$k zl>srzCWi(9?+tt<1xuK)ZEKS(17zfV8>U)89c>WjiLjQ8rF!(4@jwpuV4T?D!2`iI z>z~bPfw-&*-m4tx_F0MLFOy}|wjqFR!?_l?CTXm8nfkM;oQ7C%g&{&TMvK+2h5cMw2z>xoAyRo~76Kr45brbm~}}Opo^V(Xuis1&Ha{aX~E-^2nl~7g^)bvnrQkBfim{^}P0HQ0J?Y~c-$XP-T{m|HZJm1{#;h2FMi9i#o`)}3Gd z@Z%*Qhgdp#i5oB|GUP@kf5=y-E)Jq$8!rdA{Ebh8kEG=Z|(+>aG=7Y^TvSZkVCZ=xCUrGfCtB4PcNV#?62+*e)A8$oR$Um9Ps&@ z*ZAJA{}%qs|LVWMo6{Hg7k~Drpvx5>QUr@YWWt6W6VF)F1rvEZ#*0ao8DBsB3Q>T& zX~z4fcR0^8zIpcr7U%I7AAgCz{O}WeSU+|ji4j)E9NIqQ=$XUKZ)TF$EUEcjoxx$M zgv!;eg6Niqh%vbdvFy#4I%+A(4Ro){LW)H=^@uS2va@6?VbV=g&P{(&D)^_^=BP-! zF}h?ucljW=Ne^q$+QAjWgw-wuZ<~<2)1AKVQXk5t(WJRT+MI_7FgtH>uj;u3P&cDR zGm5XJfJNn?5MW}RPA4kZQiiN6`1~4NX8hCt`G3Km{p8=^o7}z4o&U3)8F7J@AV_i3pdAY?lCdGz7%w9L6>@Wq7yXA}z zkMA*03!bhQygHqA+i!=!>?SOJ!pSY|HAi?9+YER&mG4;I=75>C8rxNK?M80wINB$+AvpXaU3?i%Y4Qbcgxu(T-In%vI3AtD#^e* z0V0}fi?)r|^oK2%c7Wzw;GMD?N)b4QdW#J}G;WW41;vq5%SFA9s&AIKoe>3o~O^WN8ZTefl&S9PwBV=naSy>d$1(lmO&#n>llUgxm zv*gsOPrc@@MY~8Fr0Mmt#TAoZY?5#1p$!-JlJWiTeit7@6<$}9;#%m`CR1n3X5O8vO7Vma% zJ5u9mb7lkj|SSw=*jSBp=LlA_aR& z>Up;FnAs^z&x&|Q9ZISS20z%WTq9*yAZnKg_H&KR=ihw5Rqkkomz2&<(C9K%9~>I} zD#K;lY#5J$Y_LHliO=XB;~r;(7He3~b-5{I>%ajvbS^jf=T*H`|7?ApH+iT1Y+^q8 z8taCrSM8&^agg!iAS)LDr{x4lLChl@Gpvv)1#Csbe8R8Y-+@xX zHR(bl14yJ`O07>AsX!(BZc=-b(a*tx&(9BPy)he%17nL3sqBbGaZlA!XmHL_H;5dR zl3~}1qgkyzWKpV?!|gzLHP2YQ+6r?{twt#=!#ual@s@hQ!?uyM7SPXGTAff+4Va336I5%uRAE5!+P&}=}qRPWKTMXAX?c5SuV(>OR|^C$ysu*slD zwXof*sm7@d*Dl-h4tD-GX)~!RmJUwsZZ&B@Upjit=@654*eO46&jkmC4GwC6BXF!X z)U_)i=@iUg=x6=d(Gy%hJ|?~u`adVOo2CpmWbNME^NTY0 zLECfKd~@sJWRS({XFJd)I*q@s631Ax;eo=efH-bROGjUF<5twIIN|Wxc=+oeUbmP+ zqr=;h#gW5gEUFP4SY81= z6EE`-#^#*+rR=`{?ce^=6F|Y^vTaEFj-UMNpW#pbtZoRj zXkvq^NgD=K|0=u5q6oObf0!9H*_ntC%L-4v{Vv^2Lc1rhPDH6axB_b-@9m+tOKC%idJ`9_(jCTbMYC8TOeS|@MkDDvBKQzc46YW~ zS^Sg&yXvIdu`@uIV3}qVhymL4XKnj#1nh#R?E*=nvly?Kqi!opHjrBaIgb=Kgz2Oa zWF=#A3y1`ZpOB3Zece_#uMuKIPR~cDl58bg>ky}M9dx^DgAgm8VoGKqaCU8HlpOKy zdPU$4#E9|9Dim!P_839Vfk~y~uv~a%RoZ|my_JkeuOxgW8T*z=F3>F57H?)L0qSRC zcsE3hnS?6|5KDpQY6&SOr`NLrbqD}h+?Y2hR)ENkQlr)e*_hf=($FxWaWA%5BljF~ zRtar332+u`*vpXW;HUwPfUH^YqT9Sm?#=d@!;0ox?lqH+XLM7^`nBKt1r{kVwW>=|z-!uy!vrC<((r&U346BqdU zIUA@g3xgzKnHe!!c}q$~ht?#6)q`$6^^}vTtSt`D z1~fh_O7^y=Y0d@CPul$2P*fU}k(%Y8#wO()+rX^KD$xcvss!5-fu?(bXk z);-eH(ld_YVKgh-7?Z`sI!rf37n8#r?!gqAm%c`^u{GvmpIuWcSnD~DVgMVU>~e>ZuYyhGkMm5`^Re4MJXlCgj;0JA=$Oclb?BXo&?Y#v+ZJcJnM zDcEx)A&U+img$7a2b|^!C+`p}`9T#%#pCcu+Ww*H-1s+ zk!sJp<_+j32k4p9P4KWy>tH~-$zDfOR}5~Ffo4t5{qHyHKe^QmQ_tX*Mt^_aC5(P? zO}`kWyf^Z4rdGe>MOSvO1&MHYc62tyF-c>b#MSSHK9Sr_hr3@69oO-5qGK%B*f<|P zvEvEG7jx#*hqyq@#G`HoAR?Zlhzj ze8UT*LHGq3k%x5PDLnfbF9ozeo8w4_-iE!mv__j|wbyI*?f=4^2RCP6)?acvDG zSLp|1nJ1M&3mLdWbAy=}-c9Y$@$4GtX(V7)KTW+2y<_A;cy)iT8*%&ei-A&TB>dHf zs>mU~HAy*KuGxy&ECnAwe$+{FL6cL7OW_uj5U^-}vPx3wV(TSG^U ztdG{fd1hSnSiyQ9G50=}Su}qUTkM@3&2=1mc*iHDI-})`Q zd;cDpGL|sovOOXAfK#{wQNlYZIJ*fHCVaenM9Mq1lu=4T-1n9+AVvL#-g~Uu6|9(3Dba4P@Ok z7n8SF6-Z1O(=@|R_bO-KH!$n>0%A;V)`pmZsRAPQ5;6IpvtJ%Fv$^HT+tXMNSm#WV zrJGqr&{0!j=mythVo>;)YyK*wIp?Y3fIT4zIEeXS2Z|*(B;*zumgYu7J5z zY~_a*i3YPlHHA_gBv%U+_aaY*VNmBKm=!4J_F$Hx8+qa$v)JG}=(t{iJI1(x4g(Xk zfwmtu2-2GPx}B-kBr&!_jZM>Gk2KGAmBRuuY#@8&Xw8LvDJ6KMF*oQ_Q;U5aT5+i-m5ducRmiNErdKE>!MT$*ux>((kGeT8`A|F2m@AJmG$`C-gsL~& zWcrSsWu2@4bDkbFg`wuV8-briJ*cggkV*hAwmcyG=G)Kl5IoL)LP-hRzBa$ICfWc( z-YEYbr+GrmQRVMux0$9HpP%oLQZj{%4t}CUq#RW;Uvs;MtokOobzbGOHJM2egs!ZgYlt7fE|Ho?R;)4&@snqT$t6~MvmrSKIUf; zQ`^QLBC+V$e6@{x9iTI}`r>wrTP59elR-rtGj>BhFE{Nu-Tb>~5YtZ?2YO618vA3M zZ@l2xSEW`}h;f&nGc?}M)u^-^f&FNOtHbp=d>`ZSJ|)L5B!h$-&|B;+3DVjQkB_OpR-1`5#GLji>J$_H_Yr*GEKTkzMIav zHJ6OL<&39&Z8ujLqn3?Xzk?*jqL0)gK_&^E<^@~a+h)HNBCM@J zl7h*5^LW9)MEYkvPbggssRbC-*s0t-m6hH(_#^7pt!`9yYv8XXVa*BqzU%i@1WED$ zl+Xv2W_Y5(Tj~bFTP#bFW8BSoJ|h*C*QczS2N7W1H_X#yMO8s?L8bA`P1>AF20O-{ zH%tcM!vN?50ejjpPiJkiO41B)`yI{&Y@5>F**vGtY4fmdS9lk6`{pMsr^N{V>R+A0 z3}(Wzn5WSoWo-rk03ZNKL_t)0co*>1S3iXh`Wj1$aLjNy<1|fppc%__ubHc&62&d= z+6oj+A4qAp_$(lV38e_;;MF^5UgDH@IKS8d#8~&M#Q-LQT3JjUYep?fR`0KyA!XBT z8fiAUn0K%p+N4cemDC0AEU&owF||oMg#~aP>?h>_c-Mwv&SH%~C%cXajvT@$85C;1 z+|eJKtx0C*;M*phtq`)Nml&Q2$sT%9)Z?t#;su#pE6Qpvy#Uzrs!gyJW+^8Hc%>bV zoKdkEdrD32UPRR23C%e7ti!|V(N0ppWZq$G&RT{wGnfQ>mDiE}{os1cTFqr=CY87s z!1)P3{_&5o<_q?iO+Jk#4`VXfta*vO^XBb$<|8-c6f-8LQvGggE!N~JxJn-@4k-G$ zCILJ-D{yt=wwI(0o)vJ_8c+xzWOz6Ah%9CUGLl8$={XdfM#KesjS2x|i?kxVWv`Gy z0QcUtLM{n#4w@-0hs09C9t$2*#s>isJA6nH@1?SeCX00(Hbd-PyB_QJrrTrg?3p@cuVi*Bv~%elak6WRYW!9!VJa<< zK#tYb!rTb|Bm+PUb(kkj9!|EQugMm$L4+QS-nJ*y8g6aQL)#A3@eP3o5_~oQ%HH8>(vlG}9PXAGck^Vmh5miD+vPr>Bmz4^Njj{^x&E`#uDMb2ON7HhrZz{} zo(-i*-#cGI4xaDlYcLuhc2cicpO9fL9Q*-8$}e01$;s3jz*b(sga`N5N~h? z<0dP;enx!oT_3xlGT063>#uOYw%~g~8qXjEgV-J-t!0#s(@oqM9pbtVIoUU)cHHdG zX;26AFyv=TX$i~-Q^2o%>wEZb|GWPN-~84W_#gj={|S@V=TIR4g}Tmzh8RB?KA%Az zP9SZP@q%2bHuUBBV1|Uce_QB~b57JDh@Y~!AOGI?TzE+W|HAZOY|dpgeaq3?{K*$^ z{F#UFzcw%!fNvxRaNaL|^$YyVfBCPFXtfA$8=T2NzrVe#hk26w(Qo|d%P2q~j|9P1 z5;g%oY#*@h8y?q3T=vzP9`(02dAd}9h(}60zWVrcyuZ9d&Kkk9?Gfi`LXv{Oz-CcB zz5+ok+M|Ke%uAxps&1HJ5>jFZ2iGo|jEvwW1h;e;j(Lx2qc1}|LpIMO+g7d~Tx>=6 zho_4bt`#nqTbvqGz>j|Tdnj?oju8q-Cn1|6+okhvKS$| zU_?0FpYKdoOKqwL5DEz<_C`wN=08C@>&$!A@@3g%S{kcXArgC3xpB-HZ(hH}zHc@~ zE^W(&mh-GR$9vkFZ;EY@QmhCkRAUH>^$@^yq#G*~U z#_Xtna4a;6r61^jS9^rm#z;y*DG|l|>+EJYcZO%(Xxz>;SH6 zi3;t|p^Zk96>XZ>cNT>WVqpGHm0v5&$ngx2w+3TU2$P%DH*SSgl|`krH)*mCA+mYR z-C>(vH@}{NT&T;9iy+K1Qi_%ev8p8_CBQ5uLOQsbyf@~8AV$IL)qAA%7aOci8kt=o zg=l_y%@tN?lV@ieT~$ALYX&MDjM{frl?7cP6yuJVs#=K=n2~Z;-f|K9T&>}@Ja06c zPH6?}f8#;kgz0YsAk2FG~q8ATWi)dNYlPt}v+vM@&8{D+|m^}xJ)^m0}_hvizNw;=_!-l(qff%_ePVAZ} zwn29`XyO(DRDq3y;Imo+ntFw9HUxUljM85vXP2H0J^puVrarrSgKvHNTX_HBeP=sh z$P>2s&mP4mc=hlKcMorHy*$B(JLIwfvLmJoB=2?xb_x2iSRT;a=mcuJsWqm}IUOvr z4wggs49j${wk#>F+3y9SGA3%2Aq`}30^wmk;q&`fU@7ofW!Bj&P)Q;Z72GdpBq1zb z&%r$>tZ7$DIRl;o9_9&?SLj8xLDZCv)FktoIPt#te!>U{UTHKVran zI0hb;8D6?t2u%@)HW(^W`5?Vk63HO5_jyw3yEB_(jfez!+on5x78Ao6_%0V|K)NUy z!aAtZL7yoOM39^K+_K4~)6{ht)3=FwJqf&RL5YqVze3&9E6)VTz1S};8ju>Np*QVy zNrGo1t%H!-F{F%Jcj_^N&k$Vl!vB>Q`i`4jGao@+BU?ehEt8#k1PW*XGRjRhwhWEx zm>>RZn_amZa!lrG)&M#tk3SPwKg;zR1^+sDJZ5sg+<_5B@3NS}OI`%^)%`C+2hSs5 zjpztJqhV{@2b#5Ccs`Vrmw zOh}T|#%c9Vvz|z|JoPxZ{yy8|{+WScOtKgOanz#M==m4cZZe`9Is5G_iH=L00WW`q z_K!J(1F3#IzMm1(M^fDj+iMwyEG_fAHsJ?pJl**AZ$PG!wRR{n47(55-B0og-4A~K zhhH)~1%?=TGv^2?z-7N6P{1`th&VVOkjyu?r)Y6y`fIkhVOj35r@hC#xLS|`Ti%tr z?gKu5`vvBC#?$4=nlG{DNxy#YNAN^x8jOtGCW-24oSM8n<`p8ErP;hY_CO#u>6tsL zoDT~BaW1s2_vDPoN!`ED7X$^r`0D3)ysnnpo16crlHbQKMm>B^VRll74%yyAY16nC z3JbuqPKyAFKy|;XpM(Us#@LGHTI4yZaV4G8NhX~llkAIhxs1kHk#5{;0chLjQo39m zCUI2>X|c^qofez4!!|W42lp*1sNLZvarCU)=WMd`+!{a2oHs~JObNMy?wA@}NK6u6 zrEObEMo344a*@T@y?y-|m;x@_)nxw~Y15+F>>*uD>b~wPf)DEFgboKh>jZSq2~32^ z1(0*?a}gWVyuE*eEf>5#-@%6&%lr!8e)TyDtM@JD)f59dKv-?aF|o(VEl7SrmL01U zWJ!qoicO+C`>thC-rkSAh5k%?@pMed$0>O4J3qMvVS;$iZuyrgRyM?OpNMUECan3 zuJ-3GUtKp?scr7ru2<(gV$NC&`@8Pss^2Xt$g4C8Fq1UTwnMSFB;5p6KeP@$*zVC9 zkEWwRJrA=feN-M9Q`D`G6gU<@BAg4#fuKGwcU-6MIvCT8Y1bas!MQd;uJAh|9lS^` zt!O0MvUMq$zQ&Z4Am1g0ePdmkFIw8*E9ImX1hb1wW&x@Xx7tkQA_w4TEzWWUE?INZ z5+p1Oxo&E`K^h?{o%RM4gjs<+3lF_3I!JZ8!XoH1X zRICZujjSCk=1j<8uO4J%{PI`tux}e4*DD+fUU^kfO~go$1_I2YuUog7P+xhG7ByAR zu6phqBAGNCN{1vw;F_(5?(28XLyE;Z*)@hX)a@j2#~qf|H%(>NS}C|MJ{oi&zR?GxmXTl`VonWy&umr~l+u;t2@VoH);UG-vKh7IL0 z3h9umYnGNWL@?btXl0bo+pua+SA6*JzURAFwe7HJ97)h%MotOW>m$N^hin`3l&>gx zGk;jc2ggadJ-fJP^#@K!PT6eO+~5cMHpKuEf-uT>c8bR11#uA7Eh{>MG(v&qYt470% zF=O@~!FwGpC$*;O&^B8WUI2#GfrOOBtVtO6jnNyes%7e6|h{g@6e}<$7`ckz&(XlxtHrq{UG;e7mwBCN#x}Y2y;gK>vo);bT zcJMh9j$kutQ-*`58*;N{uPOlaw4?u@u{Zm*Wy{jLzA@&U5o@iz_i1jQeO9F_s{!Lo z8)OP0V*h#dqu>YWAHG> zY$EnPiA$=wb?<5RTCrly7|r+nzA7ZzMW*SYA9`Z!-}i8%fB*R&hIdze?6Mf_*Afk` zIsOTK&cQ#(C&;5vyH|cxM%$7xRxspli~3yak7pXlL0?WI?fTT)Fgs}(iXCLkV`{225&9u_)v!mTx(ZN>yoTr<1e1S3c zPIoPTg$L4}4F4Q}@DS&>YuKO6ZVsXAJ5>Hh7w3C+!9npwd#MNpyox`=+#X)9vFNim zJ3aHh!oTsupM4EvykD+h0@ku($s67+k01yhmPcqQ$XYx)NepD=l+eTh91_x$FwYV1 zKD=ugx<)Al=49Kp^K`*huDCuv;JQ4v>2YGlrZqd!-f4|8tG+m<;BP#*gm*&Xgd*2g z@Ki|71|adto7XCXGum{?8G@}z(rUi4s!1`Iw+}$7FdmQ9@T_2|^zrt(yh*Q9SV;5I zBd|T3cn?ym1zzCf4}$60nseKX$RL9;-@f+jM{n9Y_@nzfLXFxXG_V?b!`x=7 z1T2d+9f8ObOPdp*Oyli5MG>L;P!k6^=lSU04aFft{&MG zT`sIH@tyD}t4RypgBL?U1mixhc7PTj#)OZvaC%!nLtBF4qftxe2EumSj16;~#} zH^UnQ@3r840fHKtUtK}56|+A*NWltgJWk9mGqj;>ac%_QDj9$8b&I`~3`Ky*=JuTG z_2w=+?1*>UISkwm)lQ=(Cx{z(RC;!NJ-0mmu_~+7QwpjA0!?wnZi{jfe@g%mw;|+y z51_uE$^D-dy5;{pBz*TfKfoXT^pEi8fBEOGF0nyOis!arMr8u~AX!mb6Q!cN0-r1F zuEs(Qa6}zJb%V?nElb`9=6g!d0lr0T7}XUU9i$4THozK7+4|ib3q2Z|g9`jBc|p#L zXQulQE{mOAu7I$Zv20cezxH;` zO|el5L3qZeQ$Su9T-Qge+lJMp?I|U9AZTM22&6?|PBDPNxaJL~DeXM^;z={5h@Wv~ zAh%Ns5u|Vin%CE^Ul?fowRvoWZf4BhL^n9rF!VIlr4K_my40N!e4nS4`@_KoeMGjw z2cdv4r-Uh(@&HUS8O?cu*8R{6MFB5Vjs-8wkRq}vwt7^9`n?0vO0 z6m7RM;(Cdm+2CT}T#Q1H?zh#p74E~d-LPv|VbgOIt^>uI4)IykG?UMGxUnalJKE5F z-?ZCQA6?Wmh{$ye&>HgLt5Ov{AWdX-TNu67gMa+E&~NwBQD!hUqrCTpkFT%wbmJyj z`?F;vd}yr;{5eGqxU9>t-kj^s{#>DLjlHsCdhAK_Jx6|sAEU#dc9gy!7T<(tQ1Lp% zlt0@gAFdzom+T|3%_q6o0nUyMIvuzR^fc^Xr18@bF?i%i;1HiT{<03-1;t_0uNXZ9 z&E8gTH4qSf_WOVT>-+7YPagpku?@PEf@|5Zm4YeGn5G-tU*BQft`H*J&bMF=Shg!l zUhwwATf~sCs@2BJR+bH*=r$Ujn8DU zEh;Fo?dY?hfGOU%e==jss~0f-=e>C_60=WKeGm`=Bj|*m{ab$z-@N@Blk&&Hyw>2$_9?1mZM88m$`X0s^r{r&x>e_ z?hv28X5X@A6~yLmF$r`oRZ46$_mmRO!J3~}^BG^>eS&v+aamVDN;6m$rF5#T4%iJ= zV9|om4sN@y^{_5Ari?n}FuOm0&1=6NYDIV8&_3Tt0ZUmi#TnOpbuXXdG@UJKMg))7 zr56~!4Twy5%qvJ0r*s0KShvNe>KQSbk|H~eo#4C!HmTA%COoW0s$Yw}w<*oIySze5 zC;Z`yuaG(75+W3gm*+FCi{0M>Y5t|e0W&iSS#fc9y2C1ht7M#mk%V&~tlSF5P zF!e$!m>gG|H6&S+PYE1nsAu|WjXf(@&!EqMZeRwY7yINHtE7q;CKS)PH_~W;GMfyN z0YU~~Lk(>nRaeBo$n+Sbk@dUh;uqwy_6=owh*%?(^M+0PY_>v?>bb$S@N^$(7w9=y z%vB)u9j_aXHIzTwqQ{9bF-{Kuv+toz>tl#c`PT;elg`b; zoRa6yTLZl1f)Jc~%WRuG5^QQ4;4=r@XJ=LT)BQc{47l-VxT^w>N?1vu6tQLCCJIVX zT$QnbaIb>*J~;j0)My$aeE`K)3{V&uNJO|%LWJVMZ8I?>q>!AluC1^xV$!ypb340& z%f+=UaUl`duXavIP4!+Z3YiJLh8d)-^8SXSh;_dd8G6(NzrO3$;e_6miByP8={pJs;hS+Bbv?O zw_(Pb6`!AH$hP8sT}+-zggDG1ZM*n5}ueYJtTeaCETTzLwC z11@s{ayF=IwxpF+1e;p4VsVJ(oF?uh&9$Z5tX3 zqvp7`cki=W2JPCc3K^r3P)_^I>|HX3jO1z?+TPsW;pOe8*s=jzO*X4dD!&)jORMt_ zxm7#QwGQ(2o%B}@@UrI`Y4)LkH=in_t#{beAGV(DXC0ckA(PAh?)_OY`nvhqW#<@m zwijVHPk+C2A>+LWI&j-*-}HNG3{n5Af{#d#5H)%N-aAB6KD(u*Au-{p$6o5|rl$>I zw@_1S{&a{^tJc=+_kF$pcS@o93?I@))a&{&6J1BYKVdh(?#((q;W$)ZehYTuof^J3 zrGtZLm#RjG>&N=Ei>E_ZJ<>yun`+Xh|Nh+PLWd#JZW^K3PnD_r&uhBQptRB&%cTQU z&Xsm8<;ZQ_s0utDB<>IEBW9klNx`~ZO_C($mm?9((*;Vic`u7KD|5QVx?Mp|dNyhL zjKC2oOo-DRUR_?|+YjGj&5xcFE|^beNU`R(N{s9w8e6d`(rE+Lw{4A^dA(FT`i#ka z!oU<~%<+shUoA>Qa%;S6wrkA}7XSN7$%qUDj@XJ|J|+C>&wpjp_2K?4?Q`!E&?4H3 zg&J8V@S?iP5}j^d58&e4tVn5>wpgW(H5;~gQN~2bp7{=Spv;&ZSjAC3;j3RVkol<$ z9K|hLQFrnmw~i5^_$DN);KR0hq1BrqxWqckX4`NdprB2_eY@$d=`w6S#GCY5kak*f zwH~m611Wf%mean7jMwd!%N9{F0_Dg;}#mM}we_Sh6VV@?C0(XkgqI#T=^Rp70?p5%clChSnD-;PNM#B`?p8vhsu!zmnEhb=7m{9_8#Jjg|aeuu+N^V7z0aMYe zQ1#g?QU+>&jsBG8YuDDkNlR2X>c5`~5w)|`a9lX_%q1e-N6?V=rkcAy(_n=A%w;+N zy4ogx6w|l+nTKkzHx7Yp>l&E-95sbgAY(sFamJJk_%^fEAyYbA*14+^A_FmA?D`{G zBd!^xtOF1Yu8_LA1uQhctHC=bfMzfW*1Y12yE}ySitD;SnUJQlH@CWjx=GvgqEXyy z#<{{&LXSBEKA;g*WU0C1Wginp5#Y(l?Jc9-214`%SVf(itpaA?GEE3IBOL%&6^5&m zh`GT^RqOUnKpfo~Xiam7vki__z=3cR7)xG3#fF%g&kli3dc9lI3oOF6J2&f$Px}4<-JcjNglLt87 zE{gG)#!m;;!){AmEsFXv|M~u2A|fAr(XhNjvkTYw)cP9p(44o|B8Nc%9oTD(mTZ7$ zw&ml%W#kpu%#n5<|8oq`=V9e&eG=NiRj#A_ z-tW!v?;M9p`V8&v$XRfU>FD{O!FL~i^wl4JEn*F6aqw4B#j>o3krCq^Uf+I-+w&bR z=TBM#6uby2A~>a!(SybOn760bxXibBTpzt5dT4&wQVP~=1XY{POJ%M5##vef0v!VG zs_&Eow=brF&zv?bxqYZ1#9^Aw?QX0^PbpT6vFD)@EIXA+cadK}hrMZoj12HB)q$kg zewsfB001BWNklh^&ZA9(z`${ygL- zw-`Bxo0-?$2MOLVfd=ohNV|ap43ajIf>VrODDKzQHnG$tm(z4cDH$owZ5vvBrcjfS zTk%o+ng=TwR4}KAU%mMTp?bsI>Od3_a0-Np0&-QRlq;^D%YHYX zv1Y*(0zRw{h-9Dhx~;9jNgQyaR{m7}z$U8`;{}&_M&js$o`763K708Ji5N@X9GGZB zohigFyOrW`Kf$Hh1SQ7#biwBQ2#8=#CuHeTY*9fZ#wNu`-+ZwRMw}4isbyI{K7I?< z6;d*;%N1!lfz*88TV9buKtLt<7D&D#P@OuPY@D^U!&?xrmKA{#vSe_GSV~4}P#g!Q zc|%m?p)r~o#(+nqof*;!dh*CaYlcz?ShM|Hj={FT?xnYlwha+-&CzbIKEje0WC2o$ zo|xd!tvii!Tp1EURA*;QJ7>NLWZ>^e@-WW?qzjn2DBi0NdOxFO2Xo}Xv?vrH~; zzX2g2a%@|28-BnyvuX{ytQK$RpJ7)nC8&BsK-TPjc6Wzczy?(uK_Otv8Cx->gyolO z%P`;a)`;i^V2Vu^-C=Iu`y7NsR)g9KX{Q@+c$W(@#KLG(Q@B+8-8xKL)vYAdwn`?Nk^Zx4q@gE zlJ!iR+Aak1K0K@Y)Cq_Ok-- z8}YGq*27x&`FJ!sY-K$blj1Y<_TJL#aK0T8e|==SKVs|lT^h_2Y76z?;e{&=pIf(->>hL2GYe>>`42BW{H?G4@auOEZ=ps){+CJ@c}F!x^Ln@0WL2)(1Q+-}ET8n0lc>gte?zhyWdy zV^4_oxKNjvGuS7r5fBAa^yW0$*2D?k1|oq=*X^#@is$AMuokh>tsPXwid*0ChTt-D zpPV+bY$Y7J?6+?AiK1taQ;WT+lT`7BA^R4q)FP1LFjX=_sEFY8>(5Z~=0&#l#@avK z;v2*e1B#aRmHgrNet_k=cvO%!3U1%9R{#qv5(|tN*bf{w*dIrelQBa}1jjzvCvWm= zA=XHzYWGT$yq7BBx9NQGIHlrJNc-Xj_bOX6V?=HHJyhXR{@Sb8Z}sgQG2!m^6{Ku! z`#^wujcKEz$*Cc?iR1&gl`s$S4*CAe+tG}uxn{sOe0JiE!+xn#gIHTs1jFj2o7-UQy_!P6q{#NTE>hYLc+3L{rnKbXoUK8d+$DSfr?^tiMuHUB814(C;Gi@jkrZjR^^a5AhW2&1)iqa zp^p>PuS*3rG1=fO#n3=<@Mth8W@9O$tuz%+%COw+`cTs0`K#Ul^+sw1ut-0rqVqBc zf~$yIowD;ZfU^&U9~5|3R*yq0mNg4SG!8sS3N}Auqo+PZvU@nIk>mpheBvsAO$*ec z$O2W~g~_VdWijtPg&`Y!Aa6Uzfr(A&;ZVL>Qk9ZhBidmx9YAFyc?E^4|C3h0)`7Kx z9nR(8PJ+vbO#wp)$P_S#+5Gidz|Mpa%@A3UI3mQ^)L1SDE~U8dyjkrVj7@cDm}?wV zg*mDY%}bY8(lc13(Wl##v`SriK7glah+h&N4dmKVsR|4w}-#zObIDwVR-> zmVN3)*Czn7wzVFhYwTwZw}zc|R^Jh(y(hujcbU1W0sM8jWoQH~_Fk2CaMA%#K8!*9 z+@a^dX}TZq4gPRexGcTfEQ6;7aC^sufY6jrGW_fW_I&g1Cw|Hm({beZH0%1X-LDJ1 zIy9Mrjg%{2s{S1^H2&IiGn#Dws2q~R&m3Qs>LBDeC^_m%w=Ex^Ni7?<<@FEKlSX|Q z1|5e;_#I90$adq-M<9A?_1@ipibFbv9wOEb&A8%sH|9F1?%Qk911yhresk@3f+2o% zuyaTgA@_YVk9LsbdD^dE`0CRieH|RAWU@%}%}nXy@<-byZFvEQ*(Hk^59^~*n4u`y zrs{LLK@5y#y$ADbQ6?^x_iP3f0CzXH?p@7|k{&5qgcFK=nOyRr+H;xBqsc>-Gb4rQ zu%b=}<+e$(M*9>ZHnGB=hhnk8JRGXP)FXzn|9;W>`?~MQw!!Z6^zfugXsWh!Tpw;k<#R{$AFL~FbBLkeS&ic`0ji% zPp-qlPQ8(#K8TwU~(UzNXDicKE1iagJh6eZuNy37ZSXh?m$$qIz9SPq`~u2 zN|>jFB7&RK8K-Ep?A14Oi#MelVq-Tz4RFUTg2;aFS~dhHl1tfaU=xz(tePFea-!|J zEv0}+kb=?Qt$)}MjhT)qP8O3EOi7Wq2a_iHQ#=LxJRUy0x4}U%8GVJ^nFXS_rx(6G zwasn){7nW)t{71;vTypdH9kcO@|HnbT%8e}{x1F`l9o~c3U+2VsEOO_Fb_9zYH@7v zVL*)0h6s=*O)F9``8}bqMl$(lHphsy2&NGG28wMcR-XVVgjQIEs*rJjV{kP`N+$zm z=En{Z^9OU{2vOiP-=Y+8%Khf@^&uj~N&4Bh+Ew~yTgBL>?Am^IMA2gE6K^DgH&Vum zu(uGRNq}n_L^YgQGJ?f7RatR8lcQ(KrcU)T8woR3pYR7F2r*VAOk7o61S#e*eZjyf zFdh`Rmu%q6U~>6Q6^}*m;rd`qiFC$9z}lkd6ik9S1WXigridB9rh<)(K;4>2W>>_> zumuq3YB*@lZ4oVotVNMEWRJr?ZKzT_qq%MK(33F;3N3EW5p7lmgUQ8iZ);C`X{i7T zgC{1k<|>8=h9GeADSZJ`@{LB^c`Via^TnZ&)wW^-(g51gm#b>7?Bn*Z_sz?ZMLcBr z)aN=iSF_tpj+>)V=E_z({OEf>GO4u~f2IrrkFmgEBAlj#6xhAU+9c~d#5pnSJ3GZ> z$sg%rybT|2QLtKreRx+JQ3KWrBA+m&JERbOgJr+IO=c6x{w1~ zY@`D`i3*4!rWEkSJVVw6YbnU3nD1N)vSbIK8e2rveoee469-0_U5-(dkc;4c+wibF zBBh8q+NRm$*$(8SVB0)$E6>ik90q3jaU;g9X^no3*J4AjPtPYLXrswX8=9g4If>Xn zrevT@Qti`X7DGiE9&lqo?+6YnO)>uo8 zGpm-XVOUk)Z@m_|Srg&+WQW?yE=c*{2`3#a*z{D*t6gnGm9fl1ZPW6<> zMMpEp0Vt=>Bdtf_%lO~M{O93L>7H!`SHJp@|2%qt$IYvbnd(h}F}@ETIPA0o>$Rq6 zSKCv|t`BUgg8dcH-RDv3!r+6SL^WZhH-Y-3HGl4(v}?ey19G$^3`grtPLYUZK(7M`glbJ>Q4c@%~;qswnzUML)tp*C(-r7@F9w z&;Xtxl{8MnM?3u8_v?3U#pz?WcpnrTW$M)F^=&}Zf&sKa>PTr&PWPde9=GKBJ7*Q` z|ArrY{*$kZOPs2FGRuk-&j?I-T;3z48{FP}0wTi0xdIYap`Nu zZ|~m#ft>U^_-w1-ZGiYJVsz#=%N76P&2OzRaH&+R%|`W>`Jo2_?rvTIPMB0x;|usC zK86G)O8~g$4Ca8mnWTE%3NEKJ2!hS!q=AgsUJpgv8gla)#k!>=hU(J2vF=-|(O@A2 zXe5t^vioF{47iVb(}Gv&g705$@NN+#Qk+A8_#h&RU8W;`O7YTD;=Lu?#q-wJvv3|hSy^REAT~p zh{EpEg7&izHF~TRgcuqSOIlNf7H=2W1{~xpjnrbhv`Oqlfyl;DsFng_ZfvdqD$O4s z)5&W$X?fSd2QanihWZ(b((~*GD+p1*mahOu-@zKwA%@h7qEZz%EsMHS_yJQr!2xKN zKTj^<=SH_RIlOib097KaZzq~1$y5Uq1(yoj`+snSLCtfo&`TRAD#k!vBEnf2itNpk zeNC?dWKpbAFhv`}M2pSN#;60`&0^`gVem9`C5R%yIkeiT}oc#@gg`(CgX0YANCB3 zF@kCG&&Z=QQEYhSm0NcIY;cQ*Y6YRz{OOw4?9E1vKRuL>1}W``tew=IxElZmX>X4( zs{5dBF=$d+e~w+wx415g4S@3YBxR?T#nDOP0zy{8*|+0h8#Hq8S=eVU-WZ6(k1(#;u=B_BZHKm26H}s?oCI$12$!Ar- zoB$~181U76!ozZH>=he~TeDuoV%MrEu+q$>4Ti{-Q?XfMOxI@T*Bqk#9#w1|HQ3Ie zy?nPjulMElAY&ie66R=03ZnSz#Y>#Y)mU!Hs`Z*JM7SxMDurIeRZ<}$1x9okdJ!kJ z51_WJ#fL}K;Gfx(5K8?Fe3&zKnmXXYMxdB@5w6;4DKe&C^B|xm?z_qsxl6#ogDm5A zRd-EH+M zRPcQq-P5?S4rIh3=1@VqcS27b<3}zyH)aeEW?&}r1%MlPXt20YU6wsnL-&04BlZ0G ze~zvAxYHf!1nk_Es=7)k;dOFwF-G{{XoJuuPv3#*ngZDsPPBIr)W2ekr0fnO0$BF6 z^SbeUM&|t#f=)f!azOO8`{m#`P#=2X8JoTChCGLX(5SdLwzPW4fYX6qeGER|ZQVcK z#OwG+M=J%541wCV4wjAvbUhI4b>I2Z$7cfr{oY$>3ndXI<4 zHz@gv%jp#gfH|=H$rG~ARJW3`$?6U3=EbpiA!+%)RmKM}N-kKl`LhIS&6PjAq-N&I z(x|z0!&^ZT1hx%wux2%c$)QITG7~i=pz&cF00K|1@)OX(;s9azWH-DZu`u8 zBwXEaDio&*!SXxw^UuG)lxE!Df3Ry!gVBU6hRUSuF7a3M>joxlCAZ-Swaj+gIvSKG zU|p}iNd~|ZR5!oJ$i3>x8h^S131ncx6lb40+T*%tvG1o!dk9-}TJg+t zBis|gedAV4s(Wl@*d{coaUHCM&<;EC0Ui6$pn<&X!uVzvEq7PczEkXf4}0t$zzEX> zDg|>gXl8wWuv5(|6}_r&VhBS~z-^l@6@)ZMxoo?WT_PTE&8roM4nh)-i{vJKbDy&p z$QB#sXH;GrAjxpj86ljYlD*OH8=%0!A!eoJwCY{(>J1E5I2nI}K5?DJYZzUsPN z-D?fA2yntV`ZKL6bl~YB4g{OPSaid?ZNvL@MS&oNh+M=AejDyqrBNNGX0a$cEu}Rn zm4ndai2fX_4R=*?bc(JtPKj3eagFu#?YC&b=bwFnyBDwU;r_$0eNm5+iaqZec7NyX zY6_8X@&p6hs*6|@p?bPVs~049YO-g-Gwo%*wHgUd`fsh7tJbK=)6sU4F5DjUeR}~q z_6c@gkMQ6*-TP~qfDaGvvE>D&Y&{AT9kf%wi`IDCP;O?Ev$B)HmB^zs*|*gJF`W@O znWaMoo5#`BT7ro|ve>!G1SO;3PvI6R&enLU4P$b?y6sD>!BaueY!Q-7E`!HFGY<&y z*7y)wY;#S7EWl4b{S@a?@VG5l#qKZhVN-D#dv;cfx=pV|DN%2_RLz#4w6=}xwPZZx z4QtN0dX%3wZEHM)Jay0cNN`d@3%{zlE;VkjSr(Y_+2w+hZy?PEW%rp5Nf*k&5wmQ= zaBpf;WSnC%7^v5X70g>2k==*%%#HA${Tx>xEEb>*L0hv;o%zrs<|@2#uvv`F*0}9$ zzH8}ncJIKIpITtcAx29-0-AmTtJ3Z>mZABU!Dl>XnbS`8P0#w8k0Ry~xF|S8OC8+X zNA2@2x4NOlZD3L3Nk?nzeyTRgfd|Pu(X&9YeJmR7lsr~j+dbJJTW+hWZ-A)}Lzs5h zwjs7l2P@8j{c+e+_w&9k67>lpQ1%J*wg7SaT#O0xe233}_!VeAV_C1x5Ync^c*5~G zUdNH&H_mqCFg(y>_o;hr`L0p#O)n3(OP#TjjweLimHP z{`hO=h&3C0Lu3QI5qov}IllPhhq%4@6rY`6;OBqxGyKJ`{|fJ}wnYzW4Wc!Z*3`!^ zyCj5NzG6~99VX%xz?MCZC_6mo6i?Xvu;ZF{+-e2JPk;Xpkivxf`}YQFa@bg4II$T{ z@}=f|v?hRjTg(n7BhQmomsxgS;20gyH{?$zE-i}0*`%=uKw)y5;>mk&jcr|OCo64YmIb!vDlNAfa-+T45*5 z74yNj{=plZ#Fk$^r`WTFxo`Gj3{FJ0hUJvbjcVNnB}ACR$pC&75M#vU#mPo}vLVIU zq?o=T+e*RR>4Y!C4K7qrG^0?$`Q;sgMqJk`KCD+<(isy?0E$V$nK1E;m}Y#C4Kpm( zjiAsFQDnhGAw(_s?)ijIrU)vEh=5mfz!y`%@81P{HV2SQNb?!fe2a*J#fNGDFuSeb zJDoWt25#>Th_2T&qB5NrK#~R?0*9> zz`?+L#Mts`#gc3uQ>e|xlYOHtMcZ{S=-Y(I3B`T>;_3$5s77Sj+AwQX%T&)&S~FVV zFu9;HD{|S|2E9`L;Zk%C(PXG(mIQU0e)Xxt8>0%8Oo0(e8>?gW#&sq}P{qYJ3Wcgf z*XYjYzz7JK{dG?eBv&MDBG|xqECnefyZ#|!nk<`o5tIHtmepj=X>u|;keINl(bqQ- z%>I28-_jLw5P8)B+u^Q?7WFONMYwF)Zt-;gpF`F@oQw~X!gyUt3}8N`$bm- ze=aY}BT6pK`wC)A)7kk9bz5nHSTO_;MIS~}PtqtWL>2(iAPybE4d-KM*69jlV%#Hg)1GVcL?yWE z!C~w?5<`$KLxZJ5ma;a8nbnG+4Mm80A&v+BdS{T8-G5_14JA%=+W+}$DhpiVr0 zJ!_59WQVr-Guazx1!0N-Q?xi!xH^vAdX+_;Q(}YOItYai2#cSICi@?rTV97zRS)d| zWZELgYQdzsr-irraJ&*!>DexyC*AFQJMQf%C7utp<;VI7ZCAyU7chW`TjyXC0udRg z?fPup`XsW8Xtzlpn|<107@rF{>z#`Z+wXq8KC-{x$)kA}4QF-Rep9-{Oyc?+@{3zx-ENuJ2H`(gqy6p8R0B*Czqqc-qvDv12qyH+an4rYGBRI#?-q zXozWu)f>|=={dvrm;+9`AOut_RAn2;_-FYDP^6FeE{-UI!QY_=Xab(0-5qX5(x(yX z2S~(j#72Y1zReLDi{Y6qzS54q-^OFdzh8myZ~v`-|LYKEK!D4XuoaV%pU$uF`tlkH z1cps6fBfU$!}7s4+snFw+~b7Wm}xH}>w%HIfUOeOBH1UziyZ_mU5p60$`zz0ZzRMv zVP?OPUc7jPoAVvMee;cPqfOR2O_M)p(Xz7DqhkU`-};0W!(mh0i5|{E001BWNklAN=i{3QXMwJ`V!9XZ=13pAB)J>rS?W&(M5VdU>4Hb#;0N9%q zYMv;9NvXx9@D()Cv-G-@&|=!e<)fi(I4V`0y_v|On7xp$#SFO|eG>z2Z6%wJviT1g zTggL{P7O3>gmP|33xyW|zJ2NbTW;wARl1|S_iJo{_D~F-%jyk!2nLE#-@I{{A*F7? z(ImWajSn{;-P{)7z07bnK|Uq*a+Nq9Lk zZi3>(g!5z++pQER-6Df=lP1g|qPW+&POXdN#vc_`oKk3h%;(g0+gO+VAsGlcE+e6e{b*smZ=1Y^PM*|Z(%8_p?53is zRAn2M*^kO1I8T$=Rf18n7b#du=~H6BtW5;0I&Q`aG8B`0q3=t^*$^E4wd52A`ww79rUw5h!eWF}5#X9q9ZGoucc z?d)Y|Ye?Jd?$U`>UCLgX7oDhjVI5jUBLz9M!H8LxJcWZ8!i+!p$sZ%cfH&{oA`sf9 z+*lo{5vqeX(;|;xo*-oBxm2|niOcI{2&$Eq3ob`Qh!$QlPf>JaZ_sUNy`U{X)%PChI&XAn8`NpU&d;&Qp*-Mep5%H{(Xj~dkM zTT|2VkZcfHoN!Likf7@HZ<@el8~5{cgT$V7?;Bcxot2UYJ@ZhPK*1q_ctT>fgeBxQ zG#3Cr{`3`I7Qy>v!CDq<&UFwtwY_8;{tW|htqmvDO)?Ek0cpjfG&PT)ht&5-#0s%J zZWkGchN?9PaoBp2Q{X+nJSBX2nh_mdx8-bJaP4etIPh0nO=EY$6eI3VXG8@`*$m)Y z2Nfd7D1J@Q)RWqxaVtE}u9?(S9JdHb?Mu9V)k;0DwhA2^&eCJ3n1^0qYzIACopkW- zj)LED56i9d(i$x`ZmMZNoEqO34GG%}8m{{QvLZe#m2~iTHn4k7^q?rs7aJ#z9uU!`FE#$ewejF%pz7N%RArwd+QUg9#%kg|f~87V~kQ#NY+K0fl0i4IU&WSu4fkmDqC3xkUoiSRHL!-s(MJuJZQKV+ZOh z^-Yk;D2rd0;5p1L4fDo-YaT?;IQDIS^`P-Nxvr^&?a( zkcko98*LueDl_%yDfiQ*z{tK`ul`j7U)h~<-EzAofK9L?*)=K6XWQ)raB?yEz@)-+ z>N{NX)qHq9XsA*{f#)xaMaZ!GhD8;Rn~}aLgeEDm3A#;@BL~Z^md$=9+dXO$d4tsH zC{0te?Dc7yF{c>?ipzAyr#GKqEe{aQE+uu~n7^NrGYSj7if4Rrcfw1##ozhQ{{iAJ z{tExk`X28e-n)+wc$sE|IN@@0hi%)CRk5++^Z6F9FL$7P#my9v%ZiyNTuuoS7nGuy zVnC#VK*mbQ#qOW26kMhYZV>S6V)xN)oWNW#Gb6cjV~P`QQ!?n*wjql(*IU`3P|O6Z z{y9~jjfgn-%hUnt$pGdD48x?Fnj&UO(TZj<+dw2Q_+6Sl#bnZWjy~iv#YGAPQ8qa{ zGak3i^R|QY7hFx@9{d<)qen}te54r3U0fQhuDHOH z^j_f2ySI4v?v1NY{5^z|$=J1W9|S5Zid_A>np%tyRTxppo)ui0f}(Aiv@5~@_Lw*t z0;h(7Li3LwOZ`Lga#goEJget5_adf|q6dv&<;wu@oa=J<322EcbBI3p?hqsq!o&3u zzj^Zwwrq+ODGK6bFg-Gga|osa5ivDG0U^78Ux~nc>M9Z@%+oD$S!^gz3?`$7XFoTK z6e#5$dq|8xldH9YB@6hlL=-qp0iWEyz>iNS++QEC3!$Ex7n$pUW5Iqv!>Co)x6aTvU-=$Ad6KE$} zQ5@ySI7s2gc7mU2+Uu`#<1t~}i0=4ZHAwsLchE;*U4v|yhVH1Q57s3Vw{&-dJ2JtS z03&)$+iM6lTKu`(?B)XYWg%LO-HT$@3^O`A8JeqP&$~|4`la$ z4X)gAFhD~(;pkUa-46%4;(otv?+k$Bd|{uZ9{%KqKmYo}@@QM^FyYhFoyld3`NWGb ze)Qr?TuTN?!MpVV*X15rGfK(W^3@%5vC15(?pZ%Br6@L7K@8cK$_pcCSLktm9 zJo$v#nm!{)R~feg)qmvDy_Ozj;9#72|qbN5hjS76+nZt$J&eIM`MzXPa% z+(RkFB%&jfj=bsbuIJ(qH!Btk6z4ek0bSYzQ;}P8X9G=DP>lO}DVI-OdhVO}uHW37|K7`OqfYC&KTZqgJL#fugzaM@T9D3<3P0+K&4OmfTZVb^k0L-K;y(gBDF91`Y`P>7J6)(#@1m_Y7} zy_s(?rHH#vZ*W~!lb|;)0!)hN(M8v7MMwd%0QV&Li~s#!;Ljf4;+Nn2HHatVZN*Fi zp@1xcTVmXtE|{R8b;T4dC;RT^1p-Z2alscaZt!wueEQ;3eDU%#if7~1Yh3V;FJSy!wJ(g;gi!1E-|7|!l&m~n5Pp$5HH%r<$nc{ z0+zg*nt%yXHV6dse8QTudFD?SsKfXwrz%X>;Q za$W$AC|f~_sXZGQ3Mm^HmOv50gmv2xgAIWQ3W{gjfjaQ270#VA%EX5F?F zkGmtkw#@7n7;7+8eN*LUY8^OojBVJ7>Tw4g@~8-by6m{BSeS7RjQ9D9L;-j6d4PCY zDggm_6jM7*l`-I!pGAr*UEF8vO0C-5Y!KXwLJ0UI&3F|8CPWClIa0TjaKK~PQ2ez; z(gxbO=F(CRiqxno&y;rAJE@WKo&$ ze;?wX9EfOuLIAq8%|BPECkIodxJ=$ae+B01w`Y%tAcSO_e2-Y%`N1pOL#ymGI4rK# zKczKyLzu2_Y@Y{E)$Isj^zUlbn+%ppR{`nJ3=VnBrs@iq<{3GA;W})nw8fvrZll@N zj&3u7F9h0lpApy;GH62$yY`!s=e`qw)V9+BiWCUv=?r5|G9u5`K#E`~ABJRu(0@KN zVq}X?^mC5urYR=;?ax2M=d5_VUUA(vtYyP0-i)e!$G4JQQKX$)pw!Yx#=PoX8gFd? zl^Q+0Z^g6&mz?HarMc|23GI`8QBhnBOTQquS(%$io1*cvn={yNjv`_}I#npmu9RBc zo<olJwgdKVo|hn2DJ*#I~~`)U%$0T%Z} zbAHs2?LnSW7b8cR2w`tYpuIHw30gr9LxCp{INe`{y&iwx$PdFDJ9NC6n=ORKES<=-{;nn6fB*<6^&B1qJtRU*%g z_FG);{LSCI!7qOKOC!t|E3!&aWZ962ed}h$O;NPiAa=@VZAL?tXt7P~I3$NDQQLmi zykGUHY9;UPunPsaK0ccCQ>0Orx#k12)0lk|ABd5mChdg*Ml96Cx6C4-Dnlff9zqaY z?%KdX>N$>V^TrBZh}5OGRXVOUVoZ#L9++%v=hW(+do6fe7o%r8BuFaJ+W~r!dt+`f zZn2T31M}cVvWe&B)aF`PnQWer^MYJ@R8fG%|EUj5*1Tbg36IO#0Uth45hA>d7hKC? z^k#4Tz@f$5Z7N8W)DA@qgp^L0V#H}a<8nFU%NMWk@7;cZ4-jMrja55I76FMtwLk$S z5GIaT6u6xt^7R1^rAB8JyguC`LU0Ne?N+ve7nc*hdU=b92>~`JxH+G2HvvES5X&~MGn<&WFbEwV=Z;KdoYlJVWkgzvt%;0G^1!%ttoz#n}2 zIlkZ-X}-gvipRV(DY6yoipRX#rZWa8j3zB*b1AXKaOI+iK9sVdNI;|r)q+!sEoYvf zNYQh2{fS?pQt-Hm=5gf&Ex7@=*fV}BpTI<)@ZwnZX9abOM{SIl(Lj`1bERw41@%Za zrkeGvH8vG^=3-`fR^R~uG!R9$YumeNYklB4Wdbsxv?#( z$1zL6;#Bg!=xO1v7LzD4SoVuU#kk3_O?@FKWdn1-vM!jXgq#&OQvy-KZJKaPwyE4u z0h*ThPzk$$V)nDE(vi(>(0R%$6pv-aqgpaVh(>Ndak8zL=R#jqvD9}-gsTWnL^!c2 zt`=8Jh-CBZax$2jtAetq4><{I&P~qUH|eJ2s6|YRTytrC_xVaIx~ivC8(q48PpT53 z(%W+>%`&vh**y?<@pliBwv$f=%K*fQxFrd-WT`pAnVQLe#U*jW=2b zZp(gqBrQ#v8W3{0(FWeio>pCNGo`(kwY4VNrOP7HHhq=!PCj%!#mR)Le%{o0oRR^G z0rr5kUkf3++R3-%x|(uAou|P*LT8h1s|X?~Qk=kQgJll0WsPh0ZLR?tYkG=`BF50- zW{FR3(F-;(B>{89PhQ>PH589)#>2W`&70+wJE*YurnP#jvsT3hwNwT)=+gJ|v1V}iczZXR=2wCoG=E+lV_ z4o4pncNGW@5rSis7Cp1W9OJ8=Y+k7j#c1G>4^dh5>J#mxwWG&YcdsA2O&n8g=nsuo zi-dCZl%J`|oa?S4$wPR~SM>5wPiwET95IfEz+vB4jUfYWE7UcE8b zow`7VNXVn4T@O3)U1ZrVrD8NN=t29v*MpCKdZNl#u^X@C6?k)x_wU~u)5M$erp|yq zi~m+SYO)7A+pgp7H@MdOO!t~?+5zL9+8gMQxz zo&c;a^`e$H9x1kPhZ-3ZG;p-lW}3Vhy8vQUkHW^TNTBF+@Iz zHPV3P3U3~{W`%d|U^fwPWF+^NZlyF|xy2A=i}LYnQYqRMB4pbsZeJRcI-5s-;t5;X zAflK&OB)=pWdkJDK}t*$Hc{N2FTRZ_%}Y9`3E6X#>BSK4@L1V zub9J($6~T)DOXIo>agyA_vr7EQMg+ai_*JfK83E&NZ$mM&DjdmTzCLKLp#B+wiS!BG+EeA&MHLY|F*?0>oI?1u;yv>EYOD@kCu!vTnt`7mzlf z07}~uwtQrl;@fvj407okW+OE(2+?BwLYh#vt9h@z`Lf6@?ai})m$cqL-qf>uX{*&I z3?9_r?*XbP{ywEha|pL>l^4(#rA1lVYcwlCXaz$I5h+;i@+m}|VX(ad#kGMwePT?e#0h|b;D~U| z(jwrhG@r=^c)?%Kg@M8W48jYKC%j5V#cbB7ONbyhUowyt7*D9J6#INRwL;=FPq?ne zX{ZW=){GCC;HuJPl>`TGQTw&pRI^)qbntf8&pLn5n?zSu#rtEWid+8&keg5LrM-@G=Ln1fWWhn)WZw~ zVcp(a8iT9Q>cE^RLd9$%7{Ew1N^eT8Fk{&BLJPLM8M`1@+$E_%!;DA?DNT5NdqE`N zW}fls-7UVHPFU9s*KNa+mu4X>HP2g3ZBnzn+h(>7Q^;*S<27Y!IpYlwhcU)(jB=7z zAN7HHY%O{h{02{pm>L(sDEZ>~>7{x9LrD1I<^uNbi4>Ef*ON@N0Ic>L<_P70oB4$E zJR!LE{4sZ+VHQtS5J5(j%I~d%aMnm=S=4Q4168SXByUS`)Lo2M+Xh!OGjms&U_W@q zE@sNE4j=joZsL;Dyrda`w-0T72{QsI-1RCf*1&|T(V&$v4ZPic0(V7%{{lIr{g^}=+7Ix@E~z#9Gdr!`^`W0 zYr{+l2aaLiz$Qg#|J!?d)!~|ZQIGA5o?KU!yqn`UQ219IAWN57VMtL;Emtv$8+|AqRQ^PAYek4 zE9MxHRm|rkxp|YIFuAfSR>=rj@VKp5%m2sNyZuU%W#@h0T6^z^$jo!As_veiOV7mw z$uVy-H3C5av<%CDAM{}Ozp(Wn_)qA+%a1l-K!8EPurQ!#ks?KqGt)KQU3Kc5lbI3w zviz{t-mxRksi6j#>7J@{$&8H4x_sa7Tfi_9GXRmK1+NtB{b;0aE$a5;4&VRchxl;+ zK?_%VAUV>BdW9e%3Ks9IW}3lH2-Bv$)cbHlOOT!{Djd55ED)@TI`g` z)@`Heeb6mk1t|fTQ&Jx>6Ov?YynVnY?sF`p=W-IkXr%w8DB-yj%daLNgkYX!k7=B+ zKV0Ka{_tn`m;d9xz^mW;1Yf-R0_Sg@@QZifSS+CCvP#aa;4VVQOt|6+iGdv)9z|ug zP6`fwP~SMz)BW)DfRqIT5zg}&ch><+&TtNxoHmm&7x45N%jpR}`0O>D_Xu2YN@x7! z)g4|PZm|y&oS*Qc*SB~T7``O@{kIRe*?Z0PEepPPwZki!k>?pV9^k#&@lrY>rW1+{UdUonnL;~f^?X*l8i6%2p+;@lP*K$q z3qHL$;Pcx9KAT3oK3roT27I>L;U}-I@#z%s8VOgy<2%!cH}K$+@fZ`%%L!46HnWmZ zNCyV7sEV+URutQg6|9BmhJ!B^q$!OY-cV&k+ni@$^>4DX4Y?|a#A>6e-f=v(c)#X- z7jwy1U?m);;P7TY!L{f#qlEjNHuS8t_6qhJoX2h&;e>#ca85IF)PyA07*naR2q4!>j9el zw^=u&^Gt6WkIg@+`*V_uaI^l}k_*S~dW~<_aE;8RH7l($?5zNPMQQg9ZJ+wndxZG~ zDAz{7vso?vCjBqAnbk}^UcC+F+0;`^v~7ScQX%@LHFrvh-XC5%AfEKe6(57(ZSuCI zGc3qQRQvow*I2>L-H?{LtZ+Nd^_V+2t2uP2Cry3Wz2&xrzv%ov*38P7Wwwl z=A3@hAZa@okP92TY%=V4V{L!tE~pkSS)bvwA<$DNh%trNkH7nSU(TiA?de-AIRhnP zZ~>!v7T?YHIK@XioZcasZ#9){@jDL7a%>>3>LbgNje0z&Oq4a&VjQk)lNKP7;oaUE zc|!17*!d8YrtBP&!H)ESsvCpgc93kaEr|%s7D2{3K{tO{lZe=roF!v$1F}ST9`O6W z{|9(~|A42KFKh7!S0w zD3xE^%grTLZtz$z#4<5mIU48IEzSI zB`^vQ%NFsJQ^JxL3@&K1bHzd69s9lm$>9en!fYy9cmXZYv;)1Tq>Pu~Co;O{(Mzq-Zk-+#b6Dj4||MJCWb zLt+62g3E;K;ea`xFw%sx0M{U#iW1yY5u~_aKj^T58DLUh{Pn0qkmEUG3WU)mcqSBa zxV!rVSN;qkV040;X@}3RcSthf_5KR42gdc`1_u^!E|7f2FbL)he0P7qZaU!B70gNF z!bW85J>fXd_?8&&PY=y=96Te0L4DtDz#JEC25j@vHleJ#3Eeh%qqzGHV5N~pO@hs= zgn7GmYQgQ)#BXc^-Jryr4GOtR_k#E8skCi+DQmuN{b#Arld`lws~m!oqN;!#H@~Ua zwwT*%FbcPjHPm(!WoKysR-UjRrgImKRgjA2zkmPxKfvGo?cbUdSu@6K)78t+N-;QB z+ji}VdB!;|Y8tGMZ_98N zA&VyMYXxeMGgU#cH8Z7c@N_89ZK=8Aqi_XYPDZ7!#D20o_4S%!0fHaQo^Y~zbOmyj zwM?^P&RW zdBWrdc&_{shNEEj2M3~2&nJJ-*gZFx8Z02C1=n}CC}lxTbBq5134$AJ^C(TdRYbrX z%pSAY7G4JfpWN>8#Ua3#3Rzg-O2JSHmh+6|e8PE-NHHPJiz>B5)H1{#b5Ut~k_;h2 zw5YsX#4^O!*wSM3^(MSEn!Av+A*7ygyxBs^bK2AD-6b~JbM??WJATxn*oaKBPXbW3 zc(r<-tZ{#z9(D+&E@eg43G7hPshtmG>W*m`;n~C6KtI_MqzV9XRz5_w`*$%LLWNW2 z-cnlWJ@r_b>Of>o50_^W=ROSC&U@xtola#a{nh=`3L$0sX`IvThT4}=Id_&sT+RVtlQ?lZZ76{0^m?cG!waIjCXDr z={{#RX{T7hmU8tEYjrrM#lRw^#XNDH$~2F!MUwpGxt)nuvC zNa_`G=L|MihXg4X><_odDYhwI%n`w@41((K$}P`Vi>4?UgV${YoAkIqu=k@i^A9HU0S$$G;|B^f!o9#g{T2c#TZ3{6dgDW#w=BhL{t&iL!U{ss^8 zuaPnk%VJY>yIGq5E=NIL5^~h&q0{n&SY~{+oH5@=eCPT**nP6Y`LB-nKmPZBi+}r9 ze}P|o{TAPT_!`Iax5%-eBteQQu{*~FH{%Wid0aaODca0LA?%4Tdd9<&F*pV!YQtg| zWiAERdk+uI&%SaFd&h{6@35Oj3>-z>lLxs$d z4R6e9Q>Aul?a??Kb`Gz@fDsC0Wf25YDS2+OO#*Trdm$@kR_~y`_7Nf(dxHY8#nlCJ zNYE|au@poRoR=Bo)aSd1Ad0|I!NCnWY$^#eF&5F7#iy7t1|2pG-Wvy_U>`irF*Sd_ z9*exnw@Yc{;-CEZC-`uGkK_EblAS9%KuX(q))LNpOTws;ogGDaC7f5{KH4*=Mr3*$ zw5a0A+rR`J46GQhKz+r7S4gW!M#>9vi7Upc!NCM1MXB1{sRU@1_Ezq}Dyx-l3)AaI zS7248RipDWR;3tq>Hz5yng}&Tg<5moTVAW5dK@MMH-dSg>Af&okB zVD=cp!LI)RfN)+Ok>y)5PROJ-jA&e)R7z9-GD9rW^TsV8 zhM3QGB{!ql&O8Xn2}_DtBq3!3&XF}Sc7M&VXhOS_7aKamDD}0oDV;}Lj3d-Gt=M{Y zr3DRI!CW8K(hHYmL&Zj?4cI0N2zqfsg$<2 zf0eaginis2S5z*19+ZsWc(j@G*2!pPmFS!|cY zzcDB5rKXvl1Mm2m8i1ao0tg%Ke?JkTi;uk_8M-|23qstgNucd;jL`e^Vey;vj0^W0 zK?JxEK=dz?=!H!@Ud~si7XkQ}!i48BYnQ`<7v%fx3z@c5Ib2#e1n!5Q|L&L0PdF_L zhzY}Rz|-j+I52K*U*n(tlRw7K|KneRt*9e*25O1$PH)^59Hk%{Z(FTszM#oKLfTx# ze1>O_;DY%Um0BL$6;KxVVFzX{s{i<({0tu+KH%~Ah$TK@bW;NoIcD=a6_mW76fyd< zN3yK-0tN6W?@1wOj-C3^;qbe^{a@q5{lmK90E}JKDLaMY|T2FM{ z;3?gADNDO2Wy>@V94xkA-3B-&WB@<@-WPcD>NUQ8xYy#l<;nNATW>K@Xq!Yaxt$v2 z?BO8PWWU~uY~65jqb}F2bj^}S^9U9U&LJ0VAe|F~=V&vOvI1Rl$@cpNbB>tf0wTdK zc-vOioL>cErJ@C3Dh1;(;1rdFJy^uvoRWg>h+DqvV!7Pf5UI4JcYTZh>;L?7{6GKx|HU^S?r~bq zC|Ny#DJ9){+UMe#u?QekFbXS-O$v6**abbe_hY~m7?0-#vuJVRYi@7>SPC2qjxnM@ zc?VZshgvs*@w8-+8<6rDdmgZ3;M1D}&Pl-I4!2jY@Y!L4&k>wXc#0WkaTu6zzX)FK z2}uZXiJ)-@IxV;k+|(CYipE~KfH4f%52||EPdkiZz;4{@*0n~S0VdZLYm{?~+N$tH zCiTY8%Yrc|ybIQ_Ij7R}l+JLhn|N+s(qLO^Hh_{I<7#~=vdZ&n_H8NI^3}yW?&}jn z!E^fh96QW0f=vF%6-;Dz-&l%44H?69WwO`|#{oIdIxMj0tLBxpM}3u37fUKA<`vEY zg%{B(I|LYm8)`rzK-u!fE!~!sh7Yq2ReelV*3<~J45N!zh>yuki+u-^#?jP>7B&ee zTV`~wO_*H^sx0VYcZ@NCj!0gCV@m615IdBzfZ3~p$kY~Mn>;`*HqJ4e_t=v|ARq}L zXy=J!uXippGCa7*1|rGkr<9_MjU77$NJ+sN1rrG(0TGN3dBIW&iYYuM^Qb#kGu4$F zu_HpEg2$Y(h#)|)Bf=4a$vFf+fQaxgpAlHoF6LY?r-&uZE9D%0!>YE3Uw-xXI4uj1 z?OfcH>ZWAsHtV_6csl`GWED0=95w&2Gq1i5Witjh>1XUcJdfD#Zcs`@vfOmBp$D@L zj@bo_eupfA6i?lzO;#mD=UXN7{Bi{?nsm6z-;4CoF>3=svfbv?s4Mlj8{@_XKGeyq ztFOFn2Dy{BS6MqOYS3|oAyy8?2=)hfx5F6r*l{JzdrLK_ufT!b0p1NjwxjOUW>?CL z7?1jlX4XipE|G#8OxByPU!y38Kp2J{ymR=;n>&2sA}B_j<^@Ys*xQm8q>{0utdW5^ zX@ZFe77;8dVJQVq_AEWeBeGGCKUqAZ3y7K*8!5*3KvP1 zI3jRf0g@>jfRXDm!$Zh7)<fo;T(rj9IOsL&>zOaTshavoGaGck zz2Nhfjpw;Fh+WKA{~8LdGnLCWzWc`2OMgME7V8G3hg-jZe;{na5L&MKI*X+aK({iS znEE)fUhn(Dc9(j(JFV)3O0q_;j;EGv#Jv>0GOb=%(rgA z!~2p2;2()?d*OHzwr`&JBcA#fxeK(hc5r{55-xo41o!*j`=c)*y7e5xK?`JLEV&@Y zj9Jh4Yi*@{Hhpglzh2?e<*RHs;;dVO$e269HQ?-7GM zur(PVZSJCEqo8Y%az35$bb8ccFA;wFTfYrqMx1B;9VTTI%ly`eh8Zw=H5nnb`dKx8 z#|mf^?56{wk*q}wL}yKrYw#ZZ-lZh$cLyv>+<20*tbSQlEF^1UN;}E2P5E<4$XPd2 zl?jl{e@=EhJUhgqMfh&?NU5|URDF|nGqHvsi!@SnG&vBnLtGNb5y~l{aFx>=#oZn@ z^*v6_6RULOYEy{Ig22GjH}4>S@eBO?KmRBE>ghcm&L_krVaW+GMvcF-Wm6U-d7E5k zp^R&%@liWJ;66u_=0^3xx`j1lKK;_prwfAQ`E-WI}s95FB<3**OE13r2E z2IJ1-tNZu(kP?zT01iH2?+J(L2F~x*s$u*BHfj43BgSE9mW3LDMYhpPB5m3|#~IVK zGcTwSzAY9n=iDNsYLi`W&Bda#*bi-BM3(ZP04;}DVh7o2REYL@R~Scq;ME7KdeX_L z%VIQtQ_G~q*ck-yfaUbmHZ@Q45u+dUJ?(ayKb(yoY_W}=wV7qxY?Bm3$t`Ngku#-` zf{(I9mEzmE?${d}K#xhWYe|nmYKlX7?B9U^4LWLSL1}hj8rm?20C=D5M6z|SxgP)xT!p9k_l07b*S0QR#q42Gm&UR zXh%D*rFl~YFqoqLe&-d?2x-1Ly8+H=!o~Fz z@aEIx0PN-NgMe#*I@|JGwY`GIrbHN+x*e4D`ta1BU9V_G0*_CIvlh( zOcyW(!kfXvi)vgHKA3IbsRLFGW*bbh#c*X) zcN9|q78`Wx5=Ndibab)L?uQgD_q*eNe{@evgiie137+ealsX@49fI<9M9}dW_<{@G z-_O@op>*Eiwdo+EbyL@HQzt9$&lPrdZZK^L^%v}ks`AwOOl?}L?Xj%gw(!~z+2)-c zH|5XPj$Yp{H_>@8$Z?yGQ0GbRJ?V6jwAJ1nUqq>GiTTeK@Ljf7*;owfY=Ih}^x43* zNV}dk3UjlGwezPV>oafV@S7%nMc7~T#k93k@VZGa8*el{@1A>{Y=hI;XDiY^Lw-hW ze}TeJw1GxeB~$4)<`-M`jl-k!tfNbj0CnS+PK`lCZCXVyy*PRyC4fGrD)@*MgXpD? z;-hUgdg47jSD|cT7rVy0aXOir3bOWKeWYmr+uWc2_J8-~Su#A-Pc;xB!eR82HQN!z z=;cZQb{N7QAP4U!lX-dUcUPFtN0gEg!U%Rdm6WrC;{mD6V41-@fZ5@Ccc*e_6672q zVC;qgMRdD8g&mw5Q84R+H7{^H!daYK$x1i{6>E}RYm`Snv=NiW_oyFOr}hcE;b5{t zeSQ0|$1W(0%n>7%WW}*Te`>C*C~(eku*Taa&8=9KRgy<+iZE$%b2bvP3pRaj0!SsZ zrmUMjat^n*w-|>B-@N}uw@IczXx=lMd{^pkgEPQxZk;bCIA_J1*hb1^LnaTcAgB63 zBq*e-(i*iG$v7&W%3c?E0K>%)6f6?ms|!{*D9=IdwH&3-Cr_0bRzylWm(FN92j`yu1QyAOC+ z7Q~p8q?`5g$;E&*R%}m}o$c9SB8L+)CKr%M5J-Kzfe0x@5CeOsp1nX?xOq5u0d~RR z^==2r1%LDITSzWAri7b8$+mM`;M{~ay9xWzgYt|V&qy(Wq~H`I-p>pE=JAA|fBh}y zMbFdg{SLwo_XYUT^&TmV$XP6!>HzW+rf>y##_Oxs`26}ciZFKL4(EK*&(V3?@;ihO zT5-%qG$v*gAq+m)^{)O!lHAlYD(zLmcSb>QMq_8c7M#tu?OLL4sGOZ(cGYj(6r$qg2WBDlVLgOU}FSc+zO>qb5xrjzEfGo$2Wq;v-{ zs&G}Tpbl$LCCK(OIOq_ds6Sb1LV^v=K+gQm6PC2tJ-68`P~z7Q&nt%NVXI6^p}Uz20*wYP}Fy5nnCFX&rQs@hi0N(`(let@*+wwc$wx zNQxEjqa(bsC^ctW|6&nRcb+gY!xxWa6Z6@c^qP?#CBp-n_&^HGjMNp})njj;xfo$O zBWt7~uX6G^XMA%$!4YufHRC-pV<1MfGH&l2m zotN3N%wZf99hlW&qh8CaOu9BnSQ84c(y_OW!)@i{oJWjvE4-zn0QcwSW+Ow}_?{}4 z)^lq0!PXbp>?vMhaGG*4!BYmLMk^}iJc}W`$Qy8y;rs~C8B2V?5>L8RkOhKd_bajl zgaIVF$=>g-@!2)u&FFA7?Xd%c&L`ya38#6%In9uqv6O_I6H?ZDRJP1=h#-;LIub$E za&-`5WbrmYBKlG4?->$9J! zHn7=v?9%y?*M^IoM=6AxG2kvZc#uWprKXCj-gzH17oLjRkE&XrSc=F1!eY-}y~ftK zv6}8dfN@1Er9u=K+$;gzdau0D)?G?{HwNeG88&^ZY__JdIox&Az9QW^r3r6|&3r+W zqE0cgN~rs8g4^)4$-6h8Hol-!Z9U{K^jj}$ajQM2yYJL!P66Ymzw<{3G~hTtHhT=! z4ATnBX0l`w+lt$Ul=(uoOpRy+M`V$%mmoOW`l;n5*SK_>a`Y6B4Qc=KJ48CdKQ1<; zT^hpHFhb9^r?u8Z=`aP-dz5J%nmp?=*G3rq>>OgGQ~KJNFwkk!{5dBGNN)&ez~ zMVQ|f zCV}UXfb{YY>|*XWUiz0%0nT}?Et`U|_F>Ph6Vm-|_Xj`t<1fFndyPd&A|U55a==1@ zT^KP(^@RGc$9}vNz|o-4G6;f6 z^D9XnP${=U$RZY#TrVl<##fSNHA^idSUqMH(o~9_+@&?%`tQL!UHSn|VX!>-f}AxT z%#D+Ju&7i)nl_O_8l_S)zWwkX-`>B6C(=fw`l!txD26K1qS;&KEG(B?n*_(Y<%eam zI`$@cWDHK52hYJ|wMv^V)?6neNfH~_i_l_~YJ<@x%fyIr#<)8m##sSkwyCO9(_FF@ zJXNmgu$cEw&pjDZMd6mgoLFcI+UbHXoAXZ+>cuW+1ajDEy9C;Z@WrH#aHhcEu{r%2zthYOQ7noGex zMZ95$*W(qw*j?lGc!ksaq>*vq3g>u4V6{qUlUXIq)i$D=V`8V&_n^bPSTn{gvzJPL zoGnt#2UDjgr+}F>F@Y2!CYBYRQnZX)z;n>tb5dV*EmW%#q!>KNxzVzoE1{&u$T60f zEQZhFi_gD@_8 zlVy?sv2AR8OLbr(oRTUq)Hhj7>g8HV$h3kbVOy(QN!n&FsSrxVI@mEYy8>GSt6skh z$zpL$I=m_^7R;i%?0WL-8dz+F%1N^!D9PNRy2`71;(8b`u>%V=7DFn)6_e%5t5skD z+q>-fyMQN$y*2z2^F|8eTWpBeU`w20!Y++LT~oo?)f=ui5RY{5Tt% zLs~LGF4jblG-zVI*B436F4Be&BAd;EG{3K4UKSf>KHp=M<}(-LFIc0sVFOV|{gsVZ zkh>-{jJFtFKyVD_4;Xxa$D(p|Vr0?tnUUcAUNg*5;D{3-U7Sw9hV71msN4W0`IE}%99MJ}6$?$uMrBB1`o^Crrzr>{5V87?xasTT}blWtk_KluEI z_}$<89sK;~e+lQXL3gt?<(?Scg8E!#IM}tz#xZhaF}1MtqfN#;Hu>oCQqv{!!hTaO z0@38fjIL)x3+bA}7Y3w^tpr=B*cO^PqIYLWCE7HdZ7E0e!l2;7q99xQev`U)=TsGN zO1CJu1j@^A*ducze&Gd^1-+qq zP00)C0d!#=?~VIH8|dBht@}r$^v{5rFK`d}rM{jvwwNYscU6(XM);=2h1w)v?*8Byh(KP4fguxG3(g`Ui zTwQ&No81BD^CL=582zLh_Wc3N@jZfLZK_JbFN=a}|v$&IOew5O#UaMloAG1R$t5>9y z-Xr2#lUk*vjtJ3YymOijhzHAT)@^D91?i2*qN-p%vZ&->M;`y|XMchc7aZrKHmRcE znihQo;D_J)0hYM5W`(;eDLC7{WA$_@$VqRYWYUYAQ!6N!QuH%5Sw7co2G=4ks~w&F|JtjAorzsjKs$-GCJe=ky#fIShfp59x0-^%RNe2!kDV4Sk$%p_djiyX(_~wud z*hi!wP3K)JZkQRu1th%5=7xFcQ;EuKDAF1LN9vDWU4cgTw?a*z0|)QCaXO?aoa%k3 zHk|u0;MR{AI|6Jegvqauuvqlo`q=W-^*Zw+xaM^$b}p*;iVz%}$4HDoUg3-y#0ho* zDJ~fNpf(OLj#(`TPsI`{h%m8k?xP{vJUMvOJYyZ?o*{UgXB@^+BRhk7W$&)`c>C}O zbO1N)6Qr>|3Npk}P5{9W1|1w(qg=CMd$nyNZh{bFH{O8>HZX!o zv&q1QZmrxb-ftQYssPFhN=YcPn4&}*utxaio|wD((V4{P;AjJ^by?O&`A~z9Ra$

3jb?hq(q7bYC%CwuNBO_w1c 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

RS54&kc0_{+QJ$~ouKG`8sxz_Z6N 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

*iR|h0Xc%e2~!#n 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

jE>xI?aw4V^r=NBf-+ri zx9%eN&}B|me*;sWIJtwNOjjSn7X}LsEn91SklvH+y1~F(Mx1^j51NSsDn=p-ln907 z7LY=W%ixmyf`osJwgLYT8+Q^eO+Sbbl0zWeP{the+R+=WRk6gX(qb!DBKrr%6_g@18DBMH$n_b^cNK4aR29l#}G@3htT972{u=S0NN^?Dvq>1 z4~I`j(K;tu6_HEk3GWcKGlaw}XeP~>v{3v*{A&J6eGj>Idrsn=<1^h-?w6hBEkFJh z@{j++cyqlmjuQ~Un!t5kn0`1MbYi|Q-*0ZejY{^av_Jm%iPy(RCfx6Teql{39Uo)h zbque7^fGx*s*eGjS^jUh9H(J@&4u^-vK4QVKP$QS@^y9X(5l+wg{df?m;AR~8@AGY zNYP$lE9_AzUw5ieilaG_9TqqA!jRRyFB>~QK|L!^!oaQHWv`{|Psvl)%T`?$s_3*k4tR1dm7t%o z-yHcyrTC{Ta~iAXw)(9ZW3b*MW0eGt&eW)C!E%6Y!imPKUhQJongM$i^Pp|IIaK=T zf~~BI-r7FB!}niK|@PdR5ZtO!3_jDJiL;x0M%<>gB=X8xLR6l2q1 z+@iSjGAwt<)mo{Nv9%MyosPoyo@;DH<3yq%ox)ZpF_Kdz8Av?I@UxJ87Yu>>8QU#s z*yjUWijoe_VLICCc_r!rS17FYt)-|$-YmP}J>+BvcL4Jm1vmTQJ5t~_++EZ1P9^G+ z^eo_#0yh;21 z?8$yOBt$P|h`TIh0@~roAbGRN9bYEC+^+F@TuUfHe>am>ItmUh$kDaQ4!@CBG=0e~ zc=flP@4-^hbtH6?0|OqzzL=jIc~Y3%x1BPFB?Z&2-uDZDxjyw3RKBw^RU*i z=)&ZvX=SdYxov%^TaA`Co5(lh9<1c;%}xaH{FaI!B6-Cdt#4ZoH#^X7NsZg|milNQ zaV8FWW_z9k!Cj@}Rvh4VcLXKRz5kei=Xj~dAvb2ky-g-!*m}*VCJqr8E{MIKn|jJI z!ViR*i33;Jb`!DFW-LJcBy)uY-0_a^NYc|Lq)bYM14`G~5!}#b9Ss|)<&#yBubXZ5 zF$jZv*Ljyqlf_u@ zaiO^VN#|O%idHH8iqi4(7sem|75eeZ_;|g&r2BxX;OqT`udgc)Et~Jl5^fn7=YWh4 zE7?8{yk54C{OivzT<v7=X?{%RV(tK)2lyBg9Sq z#%2eltdEHt`bew?} z;YYXy7<{*P1X$LGhUkdOmETR*eYb;MEey^83*36Z)seK<|gyTSy zoH0zY3DGT1d?jx|pOFkhO#+OK?^+IL_nXoP5RJA$`1R|`mTg2p~MWBbqRxnN?$E%=ObiD3*@mem#CtL8s=-mtR%$p{4_V&;?i zN5hkT*k?Zkhsnp=4Kg)pjW?NgdhI;@MisW8kdx6MJn6~SnfMcVM!+VE_n_k8p3=?D zxrAtCL6|_bF+hTqxTjB@OQQ9tBq-F`4a!ot?hQ(;I$y( zo9W&JBy_5Kqx!myu-QM(MVkq1LXMgg)-4%fmzdvf-AOo~{C4V{FqB&mh8iQ_+r0m# z9*Ui6WhcAcy^WFGYJ0NafA;y`0P^^GazUX-JQ0>-k{I%tF)^Al?-jQR$$I@P8*uA4 zR?WjBf&Y@Cn9_5dQ{1JbLdib)qo6}e-SWjME+M!)NbdVF3tk8^4uI6bP7yyfI{=Fn zGRSuN*hI2cQoh-djFkekjo~4CiroGUNnJIb^?moHkLn3c>`4`gb9oHbL5HpdO>^Qw zxhx$iNSX8!@Ds-?iM;O8|TH!azzO&um zNXnweJ|b<5NIM{ot6Bvpn~`v7y6sA)%ZUo;@NSV>Kn0*Hbg{O7gpNcP8JGK{VIH>V z+gnw9t4$Cwl}PNcKBC?3(_8uyd70{s{=2F@S6Pyd)8~v=9A(2GBKmAc&P@8f%tkgw zva#Sj2JyXsGFJYE237lBPlTY(n~;#y-#FmgFnm;#FcbV+_lTzA3+# zz6D{o2c7E$Y-}fK_fYn!bwZ7T?fd6GDNuI)TRuI>PIN|rd)UpVFY&+B7O)Lp0or?qque{=bN92wi250Uu`Nmto5*$_o3x^_n%Rih#wYjVRcz+3 zNJjb3ChEorThu^1$v@ggVm^lWN|!CP!~uh`txuDWQBkA?OY)%8wI(2Ha(9JYjX+!q z{1NY>9>M@xY@(= z?^yZ*UO%9(U-;19&;^|5fw>meeB=G~#`{m(XAT)SUmqy|2MNJi>PecfNWVTZDc5BO z$Hl57bYUC=KYyHd;w%>c{!g^1{YPyKvI9@moKtDgR=EU`WW4(Vv=Wid%<<0d~ zpuoH!VyD^zg1HuC!A^pg12{ea1qSTUIEX;T3pifD^{y55;QcqBn9Ne5L(+x)VaQ0x zWg7B|gM@`1rGu87n{0AL>InTG)&K4z6`J{9mbRl7;D*${I5S~67`o~J#yDioa5BUy zNsl}Xi1<>(ww_?qOBapu9g<`&X;Q+2Y?EXIG+)z*OU}QYueI61vz$5rsbsVa2M@-O zImBhttw{hIcA^sUEn65NMkyh<;ND;rH?yLq%9A^FXRea5WFcv}8SH&gDSCP8P_$Tp{P7 z_@SIqd~A>;Ypu;%nnuB1*HMNdRfA^gpif3rX^;|h9`|X#a+~ask}1ZEl|Ff%&Oz!q zHV^?T(>ugM>PnW-N#Utr$In7lHifO6UE3u*w63s>uDTxZ*Jd<=q#~pb{b7Ff&91Sn zW$Mq)w|L0qnqd-lKEw~2(F%4aS4k(+?lK+ZP9+;hosR?nfIKLrqn%$dgPeVZJf&SK z9#*ea4Ium+ZwI&!cl$et#QWGdi7i)2fm8&<1as;+32Y@p4-+d*&gC%y-6tLRJ7leE zBJeCZ5B(+%*Aiq)eIdszhm-av8x7(6jQAgcN(DSFu z5b4!JqmDDt*_zM~AP=D9HF`}OMs{)^kM8yPJ#Ejl7=mf1@vK&7Ve=Z>v~4>GGS(z( z_eV)@8Z&?0P2mlhl2IYpyA@+ys%>!Mu@``0zACmF8cx1&5_aB+qXG)HYXu!rbZpVA z>$SbwS^RZTRx;{uc|-1p4U(uf!@@K+-wjICsZo$dDlv#OBuE`Ap=ERIzqGYPznpLg zgX>9+N|eTKV(c&u@s+qiV1-Sa_Iybbpz;BH{2fa^@cvqmKmLHee&B~*SZc_uwXm*Vt9ES}a$&n1f z`8x1A?H%MZ4}**3Jm6LgQGC1xK0glQA+3l*1UTV2xB0VmUAV64<^FaEo%OrY?iYsQ z{3~0RYz>aLg#}}vO+fj?!Z_^Nc>XwnaUc$m6DZW*qi&DeGshS>K0mNtC+630%=e$b zd|Q9}R!G<@;A9La4wLa~LWg230mfnPKp!VCzx-r-ft)`e#|N<9!2E6B7|H(@+ibBw z+VGI6Oukd>h>B!$cl%uqr2G)+Q0bHFRF3Qx-E}`=v?xPoeayp~Lw5u=ku_J^U7hOZ z8<0IgOwdu;d~B=MuwSk}WG~ZBqnowB?-o{lr(?3wy^&5IU^;X+o6iRv{5PBbb zsAmjK=M2O)4at*3*NVeQ>U3Q7jeeTXlXu8r11XY?Jav+seBy(2=rPk6&qujm2~|AQ z1Kc*e8>BMi^e6}Qt}AJ50U!<=3beLKgdJIhroIGSB5}C|?j-1J*h4l-G+FVfyj%0} z`lRO}=SXdLbMTDCW>+JDk7Q!&w`)W_zyBTRj=Z`};@vgEO-jj4r}P8hakt3_C`KX7 zSlAg-DO`n~!rn5Px3P#<84NH3_F?_n1wPe64Q5E`Ub>lpr~7n^@_GeSO6Q=fCvElE}P;!-4RO$iv{2hmVvn92~- z0$=sdsP3jd%z<=UyvtcO1{(3$^zHtu4$2d8ROWw!vx2IdCq=+Vz`h12oxA zIrX2-HSS*U(Kop_kd4ia9A!5l72pECoug|Y7?q@p8~qHpN0jh)j@U$sTBRUtOP`f^ z`|6p9pMT?(W`r)wT3EfLk0_S=ut?HxzA0DHtyc^B{actvlJ+SkAmZg zuLPj|tLlUr@JW!t2gDU!I7t};SQDwAF~R9WJMRQ5%9mCBT~j4H3s#Mw+rPkzw>YI7 zlWZs^6esZU4=jA*{f51N`;Y&O_wfrq*Pj`u%}j_Fu1hd=SYnNdSHACC0U0m3yngUZYkzTxN>> zpuIMLCq%<%rrAi}7xWigOKmT?zMXIGm5*U3*vl}y)nq}&VLhyA2fVGz`WwUE%l_*Z zbp3!{U(okIv1ZXXbt;9$PjAPGSOL~KtyWwHFkVo#5_%m*GdX_%$EVR{enICK2Cj^a z_etueW8u#tejz`RkGSBJu67$2c98pzF$AS=dql;_dB)}BTQkY06?9kiuzZLIViIf4 zvMrIA2V6@{Bnp1P8tMlC5@_3}P4qMF+R&|gVanK-gl!}PE#7D+9kc7KfSJaLK)?BC zS~*~-9LTW|i~LL(@{BS9aerix!idzX5>U=08nXf_&A3LBAUyBK>U;C{lJ!W;BnisO z%+yhZ*VR|~P?Q|;CuA|(EW%YA3GN9(kV%mFm~pj#lCPhz z`mp5iL)h)K0#!K#9s21*)vnYtleayXRyT4LRS5NfrZ`4Br8a1eHVkzfe5g+QP+tCy zsCnmIBJyBxf{zB5xD!##iN1Qi3g5o3IS`T>d`mzTey9!&-E>r|ERQ3)94~s;?K$U@ zREIfU$SbR7Q8}(r$sAdzN<@)wNyt|&r7S9xbS7ju;EVouI~1^%EwR38hnoxX2%#d{ z2B`^8GnTc&+6ThcxL>9?zMK`=ISp8~w5J{Z;rIWlWd4?n(DU0!4c4cg@@CMO8fMao zB12h+av6J>zHIO_jCB6g3%ccuVgqdBA9pj)d$9)8ht4jfHBzk3=`g$61GIW1^XN%r+^v zMbMz^IoP)3{mlWI&1uYwh6EoCPWX#*$uMr?mi?m@^Y9f+%bg~KbKCC5S#S&lv-f#! zOOz^0L$j;$hF{}%!};mc?IhcJQS#d4U%# zr#cvy{GB;BtNIjF8G6twPRd6lTir2;zfV0TL=a+on|PX5O26I^ER5rX z94AHy(P40yuQi<4^|tr6j{$joLS7#j>u*@U{|UYRgvQD5p^KRRleM>txvuNZg03;w zT6^#NzMtoPzpp>E_9vw!6s*W^3&kR-_A@|DgHaE}s_~E$k358fFTo&25))%Q)Oeza zA;zRJjTMrnXr(}tl4?tUKnn`hS}Of`-}l~otu==Sfega6Bsi*+(rAzXu#CO8OH|dLcBzTHqS!q>*hLrq?>ZNOj9jDK~pB`h!D7Q z{lJ=VHIaG};jEaJ&sf|tot-C^Y~idUw4ckECi2(wUL@jocfnMlpgrvH%qtva0^tnm zLO~6r;XSN5kwPc=3V*7{_4=T;3T)Os#Zrq44U?A&dlIANn(N4!M4|x1kzW<7lpdXS ziWw=!$t-LPr~4#^2W`)UrTnfM)IvKl#APqj@a+h#ekQ`JFBvQ5x2jQc@UX$r_ff-V zv~t3U51NJP1<{%CD0{Qnh|?4q97#k3o~-UfNU5&|gey5^3Ol1zgdc$|!#pLt>zKri zv>L07ot0JAJUQ7|4jCUQTKeq0tq%z0tX2;-O3E!8T7(+%mCsa+@Rt~i&g{_~;V@*v zP@2dLjuhU$1N#n{-Q9sbwc&J+TwPKenO7(YyJS!}-K1)YAhhv1qOG=@i*bEqf+7nW zpXA_NA35LJqyi&-=TuLUG?WJ@J1$iFe+IpewefEj94_4$sVCr3MxcH%Vd=^~w@6XK zZn!g=1#IG5gFk!~iw@$0&@Iln@w6Kcx$fg+O7tN)*cH^$N8|ZW4o1tIWZ>A>{8puh zOlrlU_3!b&VK@_8lE6yeoFA8ZISpQM!3@U-VLPBeJD69-%iLJuafD$291|s`35k^V z5pim4jQ!_YwRiq!RQzx)0;89n?%UKS*Md0yJo`>bCKWB_mKYu3^+I)geBzO6nM@}K z@nqwY9P8k(lP)FU=e%>m8;0iLP!Pvc=n26&rMHS~OZ^a*v4Q=-P#TyBd1X`6<}A5t zvA@K3am%7LG7pUn2IQH7`2#QtAEX3JX95w!N~CwjJLE_aGLc1)7J|V0$eJ6+7Xz{( zto`oLEbo~Gcw86|+;yUejc>Y^0Hh=2q|RpNR|A+BI#B?0g*Bs)D24621At+>9DIUC z5{QxWfJegUv8e9Y=W!flvEevJMijk6r1Tq;&`fD{zUX2bB%Ig?i2}?S>K)vk(5Ls< zd&B-JA?wTYGjM$!tHL<|03ZNKL_t)KJKb>YcQnYN&ART`HpaGY*w!7~H_Q`ZzTGgX zBhJi{Nxe03QlVMcp%IH+jv=!U^wv6->kZE@Pgs^2)1>e0QfAxqsJ717R+$_yXCLa2 zcLu1n0Ht4W2lT zsR^M@tlK*nd*4-uN*Ee=t3+xrgQ&;3<;*_0Z2z+!Uk7ez(*?{OeZPaK z`#$g~FCG^m6EVgNZm&YAFqRBDPMk)l=n<2`Y;) z?JSWowl%`a<~`tz(s9XTdYf&y>O71Nv28mwzHlCazY3pVX!>a?HmN^{sRNNKEUE25 z1sAzn7&XZPx1*w=7+Wh7aL?mvj<0kglD2t9@1b2cEUP?l=VufelmLX*+>_3!e`h!(C`(9qB4wQGxB!Kp>u_%1vB;ZG)^ERU1 z>f>1$&69$|t4`S>si>Q{4yWT(&N`=?1ot^_1WRa5Wm?Kk%_+ww3e2G4D|xCI~aAO`#$RH)R*ZP(R#Fd|kq<}Fo zA@T@hXTtlCjC&h&*49h^3#O7g?lcra_sI2euQkI{-a}kN&MO`g-gN+vPH`@qA*il; zuHPzV&ZXnwK?J%SiE%YDa*R+6O_10yi8&~u+`y<4a$7cj2Co^sQ6jBGB?V>pvpzWp ziO%YHrG~vPX|>+QIg6*^bt?alldZ>g0%uf8#0olsUR()as3$ctD4xtEc^`fr(@fRl zbr=^iah`jcL6JK>zchH?8Gl0W1~b(PN%23l!dfR4;(Gh?r-?8q{h92bT(3Q&R8CvZ z;q7HIWWcXeP@bvjbmslXS2pJ>CXfX-_Hr^g67-qnIa!AenQsF&udir~;DwMm@PRU; z*_`wuPZ9Vl?>fK~=rFud^erF5YO;>On#G;mLFNwdq!{3Ut?Md#&LI&xAU zG`shJ3cy#ZL9!A?o&79!*Y_3g=&zDPn#~tZA0aD#xkKmgA|!8FgXZK&I}q3=7UY4YfWeV6un-?8g)Y6el8COp4;kDG5}*rQjS^AA{Uv|bW+2e5ZP3LqWjro6p}M9 z&)xT602+2rQwp^J=}A8XYT~pr5q4(m>lS>`B6T_GWCZET`nqnIrWwoa8NBc4>t~9u z8C#BYBu4L2UuO~?hHf80m#BgEr2Eh}V4CC%dk|>3f%+mV^>+N5vkH~H)77Doq{%CV-(}@m;PovgDG|8SrsCLaWrpb;rF@DlG$Cs$od+Kz9%ul9L$X46jg35#9 zA7^|p3`2o2BtU|}()&PT>R-NhBH0dit$X=*aeUMNwu&A6#iwoOXbvlz zXMhhYb5z^$;Vz(#@aL7KsIcr0r&KcO2M=@CGTE>n<#Q&}+}9CjGQOrx-=Nn*=i1yb ztKs?rV$7J%%)l8;6~Ob=Q_5vh{{$y>pu-_W(EHF-g&pC9Cmar}?afB}@}zW~fG|d? zs~$N7P}q#ft14J0uO$JBSg|zc+}?qLl()1A9x@>&$O@xTDmx;`##ey#i4uEVaZx5S zjjB%>o*gDmvdEGbg+QS|4rN^0v*gqnzof3_kkY{4*kvl?;EFWqiSf#|S(bfC41hs? zLQiX^dllz;pOZZjsEQC!Ia6I7`qY30_8FCu(HzI>d~eW2k%2e#&sONlRtaU&tg{c| z3wa_Y(HKG=ake{{()V0L3Z+kQG?#0HNyouYIcm{|aMF!FOr}Zbcb`7S#uwQ?zHaE- z3c9}F^75?byHA*y02b$bT{k@L;xzNV;&Q!UxlF!7{&+};cO$~QEC4h)oF;S=5)6SZMlHvMfZL~qeK}V!_1ms zP3hXl&Yf*_@Qu$&ZD1uF26;Xur;Q}syq8K#H*HaGkooqV3V`XpwUKl% z7oDShNh%`i<+%WgK$I!mETC-Ga@1XBFy$ElUqMeJ>sdryAZ39}nkkdx8Dd4#KF=J- za$~@f8J;mKM$U8?kY|^zym+{`Bl}o)^bfg$^w4icc)Jm6nmHC20ym9}&Vr!Ewpt=o z>yS;Dc$@x`j*%KO*jd9n5ZOaLRwbb`0eBjUIN*-?hz_K3#Rx$35$X3xzjch0Yv|*h zl%IydmEQ?4qiN3w%MkoB>3%t2AGRFWd*-BjNRB0?VB6asCcXDj8RIbfc(_s|1jSf0 z@{fb58m6W-Sd>c$jxdsg_FZ66+NOcC@f+Z>_FeGC*qN~Qx||p6ok4(%Updo;(RZ}# z$I(zYXU3=FUgHp7;k>xRM5z!B%820DdTSyhJNAx4dw1Z0;LhO9)BW65o`$HVp`Kto zl!lJv75F=ob5RE$;2!2i$`>0C9?J~?idi2f%dAn5C)E9G}0tO1v$ z)jGrlW{+;+g!8jO9c%W=$mN*v-Dr>({5xk`n6sm#bTIROrbGK$3@g(isVPcjk9zC) zGxCij**p~G@?U}nrbkmqsdu@$?OC@G2Yfs;8syX8f(!JlQ&N2weh)tD8G@!}g6W#_~=vx2YzhPBe6~vSob6nv|6h$I3FH>3;BT3lsy-F_0#=0+z0~EE}`7uDH@l)P6B}N*cC` zo{e~`H>TX2-je-;1-NjUps#lCm!+%UiYBBpu9x&zBa_BUIG_4Tn~y>D0_4@?bs_t6uk zX+mdN9e+GF5O!SLF_%o{ec!P?9^%B3hFAkYoAL5;#mn;za|4(q>D&5P#X0Z5w(eNh z75gd~-hmEUE|``Z+N8agA67~>|igaBdcKjcIB7riVmyjaYx0?2p3eU*eOFId(Dl=nh~Ov zOHM-=Ip=ORc;*UG51ImTj(elKG@}$)2rdD)TPc! zuEDyS^kN39C6CfqtPNHZOIjx9S;&1)Vzu_&zMCB8RNlXsytgq`$B%-lXl%_YNPOeE zA~|~oSMYx5c(V-(K$X>t?E;v7R9@2hj>;)HoJu3MGHVXFS8LA|9?PlZQ8lI)&A-$s%N5vC61ZG_J9Bval5twl~?sy}n}K zft43rUfyAvCh)#vxm;!c?cz$`t)6*5pOhC7UZ!1_cyKk$6dG~F@zU=@3(`CkVy-D&lYU+^d zAMaq@%WjG1Y=DY1C*jDz4Y_`^^QDA7!nTWgjM)i{?(`%iqsC+-fEJ~H8EDpt0^TG& zKkKSG_XZ}?i3ZJh-)3~~n80#On8|`BiKMtQ+I#^`3;Oy1KRyNbhmJjYpC;@|tC~b^ z+l4OLqG9eEu)YFqhMco57;4KCxLwfKS9Cj0AQ?2|bnU2cnjY;TXH=8nHi_l@g5X&W zo6h9D$XG5kY<^xF{@h6qb`kwZtj8;+ze%_4s*|20s7c!lVWzUh;R*5RSn2ni!ZHj4Q(U89Nr}<2 zfV4JU7|DBLgis*UwP^v%E8!j?V|g#A6tUzC@=FZ9+*t$;GLg99pkkBbnU(kdIm&xN z0u8C425`?apw>@BR?&4|7##PA{026KFV2pGWfOWy*x38L@3tQ(Ef>Y6|-eFB=N8&`5XFC`YyQ3WvuNt$t)vq ztgk$>F-c+W^;P(UC`Gx`HVSSkWHEutwT?rdv)oH7-5H((&1a!i`*MyCN_ahhk?3t; zA*9uJuQE-8aE?SNojUe2kUM~XHj%;Un<$FBB|T3uBw!OC1*G;cCk!3m3+FW35s4ad zA2XgAK9Q3l3fXopq_YiQ%cH#G?VgD*7b09j@5A5@nP7DZY1x-B+(z2Xdg0h^^ucr? zdHAMMw2rZ47$0=8{l(T>m;rB%`E$Kt6D_RHx#&wv5GPytb7(4*f203t9GuR{OsMo{ z<%ThDwkK9Ixwp{ca$H)%H^`5Hqn#mf$I5z%uSNG$UFpk+@{%^LhI5J|SChRVuLX^_ zv;Y-6C(orX>`6M|&biuRL;!X$ChYAaY<-d3INOH4b*#Ms^A)%2&9_nTbi=xn?0w$F z$zJa(+627&;(N4d5+`~OC;aKUsIAKCU~lM;I{?PCTtV~XPUyRr8TZbL&IG?HLSOu9&YE+0Ra5*X`RTanjsnU-Q0s2KYcv)98s|0t=9YY0eZ>>=fHR zVA;B3>4%Zi6$gch!)ZDq^m6lvVA}#up zPx8ZXB)ofSx@J+C9x@BxjkcEI%<^BbO=xhknUdTyS!BLm5qH++sGk$xsc|PwN5bw0eCLw~XIDWoEXy&w zaW{rP9dKZw*s3%EU|ufx_zPb|BgQA6{3^hk%n*Cu0(KjC3x#(^Nq^(`Sus7r5w^F> z4EN~v(Wo+m(2`WRf(OO;A6O`a3go3yGc7%$cX@JBG8dj%#XY8Kbg>|-p z9cI>S>&Adljl!E~M#w%DO5u9W!2W$SDr%>QPgeX+$SQawNvZVA=sHhcjK+DXYq)ql}Cd5B&-nsN)%H=O-{_fn9JA>MuVif{Ftl1%A7o$0COEI7u%ZdCOfhz>i-XwJDz~kajTO8dFx(8bX2{eZBcGJ zuau`TBZ__8TIN)T87(O9dcTapGLU}H^xRP{3=ld06j4>m*YI%vR6HAEV{-L4m}N3- z=x4@xesbLFdJapbGDTJW!*>X7G^s}XB4KC$X6vQg|fIUxoqk!+qk%Jj_+ z=XlSPdnqA?1V#l+|D;=z30+RMVemXS_x2y#$yFx5v=QIm9n@-D*Z9;hMS33vr`A@T z*h}CO^k_1;!)^Dgtzx_b8MIt*xh~*FXl(|eX-3K?r7BU{Bp%|SpWc!b;r6h94)Bia z^@7{e6*Do=ckJ6Lw7?ABcdU;U+hfCeU(xp!)8&HY_LP#(b?lpEUEe*VecvQ=Ic4;l zbDNY;(PwPqmCvVx>uIU0t4t?IoV2}7$bO95U)ksYA*cRkn_ba;;kGwkk|2r|%cMvC z(2h3AzJAiPGuZ|@FEKf-zSB7YJ=(xJ8sW;z_@GB&puJfaA!RD zBFp<|pW|!DhEgd!>0@hpj5L{Eg!8ILKOp$6$OzQ7DBtj^s6O}l6YdmInY83IH<1eR z{Y3sNe&;Y0O%O^8cC;{A5)z78zLu*~#Cj&qL7JD2b&c^i*_vL-`@$B@yYe|5) z69(JskV4oU*;0q^TgejJcy4h1>z8QDqX4vHrMu&-(@4OEctc4C=6S{s{wqI>KlDR? z1mE?~em5?c8vx+-vrqBA|LjlVM}OqM!2kN^e*~Hv4V)$zazYV#T&=9bY7#6^Ch#vx zv@%ClmPW8eH6^86t0yOVH=t~$GS%njWfxdfL1k&%Wj4NwSivrHgt5?W`^IJF-VJOZ zPTA1aZgbZ2*k^{wN4n)zc*{gz423ik6syStqDJq!AS~0$ag}&91zaee(bdTnA%Ast z?(A}5#EFhlY454wY3Qqm8J_}zqCiXCjEJ1cVX_=ypQebi)90`%f9FXQ8~ch2_}Iz9 z!{A~S?XO0EfO6;#6rSV0?O_Gh1c5sg$47^&C=+`>;Nk?MJknJwqfB2J&mnq2pNCOO z3e49vL#QP{Bb?lLcC14sag`TC*ACCO(VgBgf}Y9?F)9r%ayo2 zwt>`6=1Utx8s0w}(o-E_{7u$(y-87bHk8eAEZLOxortuIL5b%&E_Q4bAgj_kIpV9n z*dX%k75CMb0$ud&jy1k9)`dc*XTPV_B|XmT>g#@c?fd+T{j%_eE** z(@!wX4KE+R$L%tK(80Sn*RpqA634BN75CQ_+x>wyPq@AR2yKyl+g(@JCByj+-Z!md zN6t$C5NIN0#v}_ljQ~+P6TQh%ZcUFpQ-)%m(mRRMncYW-2?Bx3uDn&wX#{CuP6!Y( zfzUJoB1dvNK?Z!&BQ9PwG zkuWagmGMN(y+u9VxDAim^cJiTjVB?(p@-|F>tLy-#LFrcLgx>C|7v~BHgVfL6}k3Efp5ra<&;G`CghCIb*QJN%u`qppz4*aSA z^uNJ({KoIXhpxZ$7yd{5Cx7aX;@AG)UvVO}>I~=Bv96O8hL6ypN}Bqtcf!8QQDJ-c zNmYMOQS4zW_UX(s(m}#OR#q7A+gK&zDra#oA(J+y`48m%q6V~?FipTTlVoyCGjFDZ zjZVO_;%;Y{yMvd;%}1FnnFU8VQ?`Z5-112k1!nRX)F-ZI{Kvt88-&D}VVC=wDKu-4 zvaU5aibc4+<-oZ?G~ONhOsVxMCzAmfaFe}eFec02_v2aF^q#dH`vD^lfS-Y$1$Uyo z+9U7T5l_Pv;m=ahSRa?NaCFv5vvW8;kKoUBILVw?=bXyfjjD`-P|je^!&&!gM}dm7 zjGGEirY(Mxw#Kpvp7J~Zwmug45AWwYp)-zG&|KxA7(Y(0_B@bj@Qg0J&J5XUrz=mM zK$V(cr}uFZ=tFdqX}stO7oZ47;;n&6wdI%^9{xZQ#++QgD)$bPCj)=8ZRd)_S;jb)(TwHWNnjtDvTL7wu0Zay?q1rjj?TveG6Cwk5u-sdRs=E zF~ns&oo<1k!!&+oW9(^xAk6_4+qgBcmu;c}U{6GA_E=9wAGZrS-LP|$e)JuC@93DZ zVZk&{n3o&SX2`Z^fv|6zhnep@;dYxaPvRhNd&jx~kJmehH#|LEWCe2X*tZ>h+rZq> zp5LKk!T#w#1h21n`uH85U!E{GiFeqyP2raB+x>yZ{ek;upMg8$^74Yq)3dBJL-xP} z;t22i2G+CBGc9MQxayGxYJ_Q?Ff9wF<)VE}wqx%aMd{gsE8md+=VOzjLPb94`>c}> z`=-h35`82?FWZMi&22J9*QiaK2!OBjf@F(8n`WRj@tO7wM2x9Ty2z!nqwiQ>KSS5E zeH&I_eH~S_%Hv=PvC36?iP;Am(FZo&Yn z$yVS4Lm;$f#t4dIIy&gRlg}!H2s&9R%se6Ohw~9A$Dg^=-2v2<)Kg}b5igIEoYiTLmG%NZ*O1V<_cG|CcBB+}^D1`a zn>?3jOciHwGPDDk?uwDi0jVkXU_lCAHr`LYIRifpIfAm;apek9VarbMaZ)F=l{>cq zQo+-QacPpgjyoT|j6Y*P)>wVeGI@cKevr!``e(+C4J-YP#TOg^5J==F^{ ztAahBL~vuw+dxs2ZWL*Ssmb9>ZE&pfpzYHO=6nMQ&mSU0h0Ay%=T#{p!en&%%Mj#z z89vG4=2As!8HY5+<&Vu#GUIp?<;n4gnz2e^4AV9>3WGP-V-BIq zRIYZD_1xg6UFgc-0Am zX^IJXX+I`K@)7c8#P8PD*0HUS&rM))x}sD^cFALJoVL>po}bX#qWg>q>n@EnB6UzVJ#}UW zwTA0;27$2cEaB|;4cq;W>(X((NfhV4i$l4ucVJo2ryJJ$2LANdFf9|Fzwj}hZwuxo z9p1Mco#nlKeTaj;$@%7OTCm)%ph;HEMHUOvEb&`OY-+Ua$Rnq%MnIcog}k+?n-*7bqy^)uCN4d^S-S4cuTpko5lqUW(2t{A+&g11*CxCWXp zVnh?6wF$g$*dL!2z2dqi%O>Lo?bsH^j!I!4CyXovCeA5GQKCqF%d}RSsg`K~etIPY zpccz*z;KyvL`K(h=00`Kb3v^u<}L^^;ZbRz^uYl;qhwV`h{=;9bO^)VhnCLAY`7?1 zsC^nbnz^hbW6J_98@D`Cy|rikG!!HpX_-+j9J*j+3?zI}!Vg?}7bD*A>7B}151~ ztd(giO~@T)h|aImCqAYM2iI~=C#<%FaW{pdZe?<)_=wNIW7KyBYcZmR=Troa;xdT& z(8*&zhGzt|f6U1Xz(c*t;P_Z+d#-2gR{CpcPh}ZbQd`II9dgojaq4SJT??GRaYrQE z(V9;%)RnK071D}0m=wuu^Yhez*~R{;I@*$P^I>7YB$d|H>@#4Bg@S;QTH6ZPw{Cmw4^&tEOnv0(S9)e=Lvzb=%I)cr%|F0Ho9TOJM6e% z?*#0ttPHmqe0c|2-T}0TliGoeC$veN^KFxmcJ9Dq-7rlJ*V_W1Ve7zS10J8g0^2K| zo)!srheiPCx!kzEV51ARPd^2}eg)U}@A1(WKf?2E#!RgH-WfYfnEbx)*tmhG1z27% zU0*O=ZeW_x6({@Nv8|gp^Ls=L$m%yC!o+8An?ds;$>eTNm~Ky?c>&Yp4jPZ%jS?Dx z#cADFH0qe9j)^)Nx=t)f82mgn`8|`%OaLl_*-1F!p?+giEF*l$$1f zA`5pnhE%!bE~CwGKC@ZfD!W&AxK3lY(VNJt8S$cHZ|)Sx8t?E@5#pxI8nXjF08V8>7ha=JRf;I{1f?=98~{qy~PY%`s<%6+9!k z4^efHTbn_^ssB6uAbgtiq6gkpIuAo*{B*Z0`gPGp=l#Rji-~9)dsgyH@0(o*d&d z>cWU%%(}_8_)zGQRMz`QLp`H@MXJN>G7C#>l+6G@jpwP!>h-KE+0&FKnWfUs5o&JX z=G$f=?ud}{&77nB8ywZybs4TJ*b+|Pv91zUzuBt2I?;w3seg#Z zOSnTC6Zx*Pp-Z3Hvex%xtL*GHgwdMWPd527HKRYnv#gx2ykNr<*50u1EGwkMpydf| zc>>z3{gY$JXqsgOlr&mVonOg_zTxTVifNwF8CW}T|MZUS^;0Z!!|mw?YPw~#ckJsX zHqZ45k5^eWZ!_@xg)c}#xrsomd(71-@4aiAT|?_N-7oJ>Kg>`ga|2*o8&t?~2Vf5H zr1aP@%@f)@gZ1b&u&jNXz0R&(-icwuDE~tiAHm%-0T~$ zOQwAZd0)itX_GkPeFr~2fkhSofH2jJHcfJ`@7UK@PX<0}o9tLyWXrMu%L2?x!?Hy9 zyG(2)Jz`2j<)G1jj`E>YmQxgtJLo+*=pD&nSK3Nju;f}hoB-allqgL(Bv2b ztTX5Agek!bjMm`>$cUQ(Qw8lsA*Scl$NSP@XDUWIBJF781|rzYm*(>^cc?}<=Ypl` zpb6WbOhb3W5kHFPk!j1jHW+V~amqcyd~yZ%#Jm1pX3*5`IX8^8x`e3rU8%hVNvhR( z7qOL}!chu1Oo3SwNf|LBX)qajNXPlc9cg05DmmWPcSvDT0l_zAP zT*ZvF>X>z+f&?2Iq+VXKyka&5)QP21!?CYNP#8g{-Wwxl`%GgLdpq^`v7thD zsDX6cD%VL}mO3-PHS0&+B3t{Px>vkNl8M|4Ym&plV(Ti|mwsO9j#IaZZ%t1GDOmY@Y77e6-GC@U( zXR^Uqy>F7M;fg2udE5sWZz;t~EPgk^Qk@Lx4B};mnW=!)H)F2`8<}_~| zyxq|z!ppm7P;1zja9=yN&prchcRW2UxZbY151ql=iuG{^Pgn5e3H`BR#vRMod;!;| z1-E6!+#reO`i_0;63V{o_q}7=)LW7s)y^|OE%bOJlKrqmXfz?h#er$IBxySF!JErf z82u1~I)i0D0!TMRGcA%Kod^v($hgnEH3neW)zi_rLhme-j7iplEP7%BCc?BV$_}jC zC3c_KV**;6#TM56??LHFNBHJwf@F~=U9oPS%#rtwrpe#rXo7~F2;2yLlU4F*UN9{S zZtp+B_WFwb{wnBiGcYeee}MX3CJks&nis~R>kF{HO7b|6tfucf`mAC0)G%FNus^g6 z878$a=~&IqKx2tg@%`aVxr8{c1351_BV;2h$s51jlSA$kZ1PzW#&!0HS(q(Y^n>Cx zSts6>$(O;tIlD#;S%#O!Qsb2(wL-AE>}tv0nSR~ZPhZZV2oYLrE6_2+2%yd?Cj4f2 zihxA?`Jkw9%fjx^eKVqo>bZC>;>icodTQbK5{l152$m_|=mV@yGxA{?F~d(T6ryc4 z9NxNU47Eb+nrFZ|7)vu54+0>}kS zSv8Rk$VW+nMuAJET2V6Ob^Dm*@T?YNWcW1P8SVH_YkDyF4KHQpjN-drE?WggGivfG z0++rSgG%SQOaJ1inOc8Nh++78+tfSOkw0^oUuh%NmHRm2>8!!x44(0f|EZbiWx{Nv zRCzII{Zd$aG_TQgxJv&XN7jdWKSz8ll)OnKew0dMLON2@S zC}e#*%BT!;ZZpd(kLdJOdU%+>@5iUuYT&rC_7xUN4UXHlY^09c&Z5Z9z)sJ&^Apyc zJi-yn6QXu5)lu&p~D_g4@RuGb3|&BDI#vVy&>JNmYwZ?CxBE_i-<0ces{^ZSbZ z)32am$J4tPTrRUX=i83$@xZ$7!1W#4(=#SwEVHz6xz2dLOqhtVZ)@x?UsvqwipTwd z$7iqD*G-(v2DH{NEf*};E9PavvMgxJjA@!N&9X{6O%vKAE4ocrcBi>vp2a!zs03Sq zadFAAFWW-Br-o&oJY0RA2y>G%hUKPA)YKU!J^Rn*;Ozx_n6R0S+(ut4rZD3AdN7vJU0@SzN3-A+U6N;o&oN< zAHQoz`$eQ;zbg)#%AW>I7XSqAo41QDS@cc0*uHq)x1>u{pQtbidpCT>z(Kli^EFyN zt@{BcH&j+I4=d#wZL_Zy-manT7R|4@CCot&X~0#!B+=ma38lcu!uhkvkMWa?-|NIP zPVWzSPxLP9EWz{*-!OgI_V!FIgmP2CQT79rRmo z2fi6Ws0ioWvW({a^A2;XpSe@n;IT)JEW}K;5oED-?S3ZKOthYWM4tb`-}l%GKu4=xHRH%12HLgN_Z3+ zVGk|$RdhEjZIl^#TyvyCS!qZ_k{NvD1}^pFOY0maBQmqw?=T+pO*gdJ%h-E#+Ez6A z0sFkryYtIH7n!aN?>MP5uQndr9B1r`dHPTHXH_Ze*Ib$8xq z9K~8TL3&b0)Mb6o+XkB73z{q=LxGpmOPvlR86W){#@PB|oj-zJdRJgc?~aaW^oCFG zkIrw_T?JtU&ggu|i_t--V;A!5I5Lh~hke)sepcW3eyOWq3O3;~PJ;OYRPC|$GkrQ; zTR($-vn;6Z8$$Vjld{JgM(e{wLPEbhGsQJZ2W=*GfX_OlQY6EYWkj^{_Flp9qLTuS zFAK|dK0T84*w5^o7ai-lf5Hj&eixw>2D{XweYQ?!U6(%-J{;bhC{1B1(S<4#o4(Up zwi5$D*2x0~Vs*SJud??YssE|pR)0={TiUYvr_|n* z_Wo1C3Be}~3=hsHh1bmhM!@707w?FuWOTY<#WNm##~P`a$MG~_qUC)J$Cf{0rZZScTc!JJ%d5`Z0)#z@`|>-;&NGVeR`HX>g$fj zLlU~RWx?|PBeZ3~gdMcY%JVV?DvlZ^0-u?t>r$tM7)H3>=A{pbQ`5Ki#6MR!Qs z44dR@{@k-E>T{a~B(P?JpX6+LbP&stb-k0Us81IVZ@OJ#(&h=&W+DE*7TJznviD1w z2{d0MB7l3?AVm%qi5Bu-`Nw!<#I{F;GQo~Swr#rH)BbW<;K#n{y-mxcaCQx>s7lWI zqo4WFX)y};PAKD4FZ(kJS(HXea9G~R7P)PCNG%XBed9OIU-p^W7J@em_)wt981ca% z#FS}dU=M+w1Ur%tHDkdZn?UE-zm}CqMn(*%wW|t^%sH1#fOZ7pWV6}ES1IX;9r0xr zL^ezg#Ky;&`Er@Dl6csUUu)UmHj_W)B>EDIl z@w>l-pZ<$~4xjwPulOJr9J$azNtjT>R4ErrI0(A&nf;_Ji$Maw4stc3G))*22KI?c zW2v3|8PqVWk`FT^ZlrK&_S9QEGFTede4+tzcwc?_ElS8nmeT@6fQLa^VK6X*#wmSI z%~HOD!aX9DE(8deD3e4p|!3P=;)KY$syvF!DI5UN@eq2Lawo)C2=r)&(9gvLq(8w?b{I{; z(%~JeH_MCD%1^$Jm_FmvfwfPsBW(t=QFO>Zjo4e87UI5^QPm+xid;7!v>h>DlDunQ~puWw;QZk|2(J zG2pG$^FUS!UiL|I>r0N1ba~*#lU~l4PB;kXE}jno({El`mKgq85i<5H#vtTQZJW~j z*&(&C?9kbv3t1!IGQ6jgDdT0jpD5b&RbuNyD$0rGsXC`V8IWj&LFEJ#-zVzfsPP?I z#+`kNsLm-n(AiOTdt;z&8xsz=9d8WJ!6Py(CMA3Yy>YCfoBW`_Q;d;yIdb$E4I`IL z=r-E4|CkBAJ!8cc+b-PH=O?t=GunIssG)aYT_v1-nkU?D3#MfPqv8JOSl3<7+1^*| z+m2->ynFY8X}Mr$dAHwJ%x%MRyjgwDlD_$oWRZQn5lI3( z!zF8$`!3~rmoR%d=FUzKkQpJ{KVHT@3G_I-KAv+C)Zwg~;ma=XkPT9mhcWQJLaz>V zGi13(*`hoM1apct-pB=-N~V-V&ZMI)O%ueKo|YLPnOLFz3gQ)v2A-}Unn3$!Qko{A z@2R0p7xccujX5F!8dnZTRDp)ROUS!GFv`l%CC#I3gC9bM7+>1SN?AT2<#CXea?RLo z&UrdQtxQ;s9(U0dqAYBTEy_3;H^Clng-#i(M&|iw0{Pu!gOJ0MdEOs z;2II;`2;$Al9oxSs|H$dX4l8UyJ=8tZ<8x(Df9RK&tJfg|4;ute%J5$ethw3zU~Cm zpa1&Wum7j;Fa6-Zjlcbie;xnFFaEU{@bW>i|I8>L{TYo#W>M*r8+$D=8HJ-$6;o=| zE*n#rF#)HK1W+(V8UvrmNrRMExSjPll|@#9`3D`^$hZl3lfjpmz4Y}s1cUL8pauV{ zPR&Sjmc`jbZvB>lRt}}1q7j}qzL$<>AhM7_6-v1dk!dDft)B{e>I0oX5GH-@&)e4l z27^z-r1IWcx5{A8sM01%7Za)0eVmS+w>-8t0CnYa5XLHkDL#X?gC^V|&U8c-UUz#e zR4y9`l>3z5rGX9y+8F7!VB(b0E2gO8ZG5AQ9V5|g8auA_SU<)w7)fRv&$HjwM#x{P zFdx_`3x^oX1K;OybIN`L3F&^(EvXsGh5V+{jDbRF!iR=BIqErs_W;tsv!Jnm>Ans~ z^t0AhCUV_|7(&)P*8!M;Q41={SC}cgDk?Qb>vpSuu*jH7NCKo8= zeBFGLWGW+tmvb`0$&=W+j@3HzB0%0w=QbUnE1EV^d0Y~0a7w_ZwkcfJOC!E2KN${^ z?dW^fl@BTEce!mcAPLeK`zmX~Zs}lLuuad{v0&>BJl(+a4bv=+)!u=1-SD`-Vwz?w z%Y~+kHb=|N%WC?L!ALxC@)6)en@1D`-8CwUo#{;~rXxuR`3#QftC&`GTNseag z>xTXQ!1h?t_m1c11<$t&z^tq0+G+Qo*w6KrOz}PF+Kye@ckKI)eO<*dH?N8$G=08Y zaCyFAxjg~X3}&EF6DPds%JXa?@iS;jVdr+nxQ?ee_HsV>j@~749rv#A$!feG>BjQR zWs!{;3;bR8)k|_Z={r+%XV_NNSsiwBG<|*6eG}U^AJWQK)^$~#yBrVaVf6%Y@^z)! zR@8+C)%amXJ>Q)GKNo`mY7*`afSM$f6Gy!Rm;jnV`yG}v*#sxeS6QNVCrt6JVeFB$#>ROblx>FBXgW0eNe$Ue4LV; zH2GOCx6irih_7~K)3d&35qvgw$J(;omfz9%^ex{!f4Lk_LGjjTtmfI-1dlMchc2n3 zuJ8M7)xjDHN6N~M&iUzOw9f{Uk|n8wK0%I2Xf?@)DL*%=%pwSxS_sS)RC$;*YJ#U2 zIBsc~pcZADpJnCzYyaR^@uNTTU*p$*$8W~BefxKQpyOZtx?HaK{Xg)pV!7V%Ge7<3 z5l$XO4~S5r8aNpwW*|@~$-K`i8zRLyPYV7(@!C$%LePZciD^)9HsL`k>|+d^sYc5z zJuk$Gcs71L;gwR~D4&Xvgm{+pa+DAKSAJW!{V%~%A|4XQQvvq#h>JjhwR2h^Jt^hV z4;o*AjdRd+es45tOK9H-`(%6^z#EM9j4J@1Bf}+=BQy=jKqsXRH!dr&jG}5JrK5W? z0-j+s-lOz5rZ1@c;-M_L;!9|BrB|REX|wzYI`VcCFBe**3KQu-^YCtY(T2P~Aap+lQ*yj9>nR%NTu>@~)C z9Q%CuKD3{|7b?>m0rTKAqt=h~Zl&GS*JPNE^~BL?S#~6OG2p{4(`0JCl=}r4Z~V+h z*yV9P&?(gfaRK2;`NT9th;4JeC{S7~qN(;jibbv*bJneVa(4YM5@uDKDPi1nng34( zX=am%L#jz%o1D+>_F&RWE<3Ex#-EvYQ>V&gdP(G@4*OnVFZM*TP?*6)*5WTxMcR1D2Pcjk+)`<|EJ(dX0gfDFqOq>8f3TSi`F`j&5*zdSk^<^W~k8r9XAToE|+xU=3F7VdtS(MZ3UHQWdhb`&$i0gHr0 z%bz_PL)sdVo@;2wa72<7Q!HuAN1Sfp0yaL0qOZX5$e2tNR9;S48WeF5>X8W6^?^V4 zqyHTkjNkFyzwg`-{+t&P;h*`fzZbvdxBqkaOMl_d;X#nH(j;>n@5aVwLFgBNKAKtU_V51$E#8YrdU(SSW7s8&51$Bq-Oqw0c^ z0>)wh>%Dw@N-;K0#{SKFY=R{drFK3`w^Qmn_U9jon(_q4fhT|if}OVDd=_<4>0g0$ z81wNtf!qii$D!1xzu7>`Oll9TIT}6(Kr$^ckQrp`a`|3Iipn#~yIA>XxCfY+c0)6- zGUxi?cdh?gHiy?`mFoc@I<008ru<=Y4``gldol3 z&oGy*2d3(l#j#uq)@O2u1TJTivM-g)l#JG*~)U|->O>=U~`Qc@R)JV@lJg*ujyb*9n z!e&nT0hho4KLt*Sg0`sIjc(UBUu6nj-Negl1akBt{}nt}O-Heb;u30d!Z$K&;m$799i zGGn=1U}s);5FU??$KB4amc(xJvf%mM3zo|joe0~uV}ESe9}j>C%XN|Z8QApbHA}p< z-8XEnpP{c0&@|)vbio(C_=3wUE3UmWw%#>7eZ}_Ju&webN5AQwaCMN0HJ5%PO!JK8 zdc*bQ8JDLkE;mUQ*QOcsJY$*{P-|}A%NAGjDOjCB&4R8g-?k!d1V)r29c1q}wMj$C zZU6U-vqeaEviDV2*DdT`CKl`qG@)?#=h%k-$}#T<&%?xZej26 z)4OoIc>EcY2Osk1UHv6}d64-y-YjA~$%{|{XF5kdBb&Uz$GITrdwHxxXPBQqm<($Ab*?UjTCh_^V%^YfP9A^^RcJs)b5K#JHW) zZ^f$y!kNKv0z0P{n4=Asj|G-mNmh8nqXa>G;naudn*p!mQ2G&(8ey3~8o`H|f7rLgj%O6Okn)uT(_)?l63?4_rRR!OI|ICw3fb z=e1&(aPV=~ov1Iy1pG*~QD??PaKgE44_N5%>`>|r20idl5+?5C4cowAj$TS`W{#kb zf6;}pa-Fxa+uqsTR+ntmDLJ_{dykDvU}iKP&GDUP&D%3kNv`WQ8VB7Je3&y1s{L-q zMI1W^ec$B#b2|?`C+IR#GFoT}MmwK8611JsgwdBq09Y{F6~YA&!ftXube z-?B0-E8~~z4cFTh^JPJ2V0*0C?sx3_ig~$UUS>&#w#%M#^bSVHwgKC^iDNf4w0XgD znecL3aG43)*0JxKo*}xvVG8FqSuyZ)#ZBMo5dd8i*0CKUX9?xN@X&jh zOqc<($w>zHUBk@DCk|NamN0OsOU|QT-T4(AXwQYXBXpkNQ2v*L^tJNP<$8A;+o{hN?KQHd_|EsZ8F^HP)chSqUyi z*KuVc4ROz{v(3;(*t#Eq5w-YiWZu-j`#aj`py$teTAVnx@SBW4ir>9{Dn^q-ABo|b zDhk6^>>&>YK$)8SW*(@jjAOOB=Q+>nr$5~%I}^%ObirlmbVYTqVK7{3CwB`W_Id zzKZ3>CJnT5hVQ^zes+e2^XQBa9eJ;v+D*rIC}1mNmg*n3bNFk?!u3^`^p_3oQs*0Z z&trt4{?a=dYt^e5o=Jr2_!JTw`1uUclyNY?FpkaHpBaCYk#+_<2Tf)m%Bphubo}{W zrmx|7M)HYA!dmf&n}mRXOL4}?bWj7mLM|r?7`-ft+50hUt*)9y|H$8`Qq_0`tsnW( zo{!u$3{P!4^8gQUQKqWA_H89TOgAGF9QJZ6ewsbYxIF0>Xy{9BQ+YpsFCZ{V+YWo; zlY{>AwxxpTKNp(vNfmu$&rW+nl?F!sv*;1ZH&1Ay$ev95Tqj+Jj>uchXfvL(*Ad~* zMQ_pogW=o4swqu8S%hIuvgUd;Rp95w$GyTG9NiLUKt5Y*C7e8TUGPNcwPX(XyTzM797FSG^bBXmd&6OZg$ge7rBh*M zn;Oj8lPnvV8GB!__7|*uMsHWNWfAARHSC?SJ~liaE2eqI(@h-r&H$@(E*a1}w#NhL zJEjIazk9*$cE!A0z|7blE7sRLS_78r71K1M_b$%%s>#`yv9qkEPnuo4O%txy8SkDJ zEOW!Q?O0cF&i8%C_PAqxtnQox@YV6#^AoPOs~n*=&$4e^Wsv6^@}zCu9VMW{6TGEa zLvuJ!T_hbh84{m8QC`tK;Uq_;*%4|)7L_aQcQ;6bSb`6m>!#pal~5$f(b}|lmh?7D zGyoB4nE4LY@OKM`H;3HLNw-M@nkL!1&g_nP)BWc*VIW7&&6*=1Fv84b3vS-N1JyZf zPMRBWk{!Rb*q1KgEMQ9tr*uuMNe5v(r%?d=8aCoVACEE@g(u90VYT0LE(*!WE}wF) zIeFMS)EA9o_F_`gIeWC}gGGnmbowCcm2CQyGGiN=7Ljx!;(o?TwGnbfss~;7>C}|b zuH?5I0KYtsndPIHv;eJ_xN3#Qz6K5E^do@UA6k;4f~`~-VVX(d+8V8!lB+o zVX&;k?N>JZ%pi>yIK7iUfnR*gw0 z=KeVCR>Sz@_0#IlWa<%6)Hc$KA^eBBGEMvEIX)O6^vk%=2k>chOEJ&?X0I}l^8o(= z|Hq4S9r~6jYaR;{e8zdG;T+d|_A`Zq4$u6)A&89UVkJAn>2Lb@Ck1Oy`8X=<|3@9{ zG&j6#^`=)cS7y>SSn<}F=N=q*GIwXF^mH&Pm?j5CzCE4A$FUqvegjFqAdH0Aw;ug1 zQB5aM282G43$A4y=2N zIkm~jDIY`KqfK}i`84Twy=Sy-_$$s~pv@gyTS4D#A%p=pXokV!^vxC%=mZ{H<(enL zG_mZ(rfQ=Ieo_O8=WNepu=yZ?I%C&8rrX9I&T5M}BhfgF`d5!VBZuE)A&4}ISG*6Z z+be{$x!9{^J0&6Zyi@E$uU!e|FaUvIeY6FRPFO_H)PY_ItP zy?1qVE#VlU1Da&5V_!SghwK}krU~ypeuqU@uGg(&y+5!%?ixBS*~Rz0%c}Q7vTe6% z0=HShh_?sOH#|Mxuv{kGt`|&1Shr2y*}I%^zHNBCJ^;jC?)f4s^X8a^&61JBZu>!T zkK~Mp1DW|9@Kr#kx8X8}&?e24SX|yI&t}KaXd-9&w)S0C*s8OUHN%~H%MLFY$z{(t z8u`S4Q%5ep+wo>Qz)+{y2t-!4(F|^ec%-{eZkWB;d-r5{c1&H@1a4IJqmv}ZbDU}a zzyLK(u%{C;Isv%8ex}FRt+GPSn=_G2CQRUV)yYTQAOHBYj7t`u!!UsW=i5mmU?%g; z?0wy;HzN{AJdd{#Xf5VN@lRJ&@0>Je+UIvEuICOC15;Y_`ZMP594Qk`hUi+{{3H0#z0ze zA<_gf!zwujocX;xdqyS`hChlwB(gU>kC%-yu*^5J4=Oxm2lSD2SogNIA z$wz~8_Eplks^?du%iJ-|IT1xrAjNpVg~IDaES)NFRfED0LlZ zEAz`Z@?&R8|6^>;(Llk-*iHp$q~pV|Q-(*r=jod2m`5om?}5Sd7-zaR{F$68aFS7G z$Te%5QiW&Z9gQa8N8{(wSar_IxOKWuG&bOtw61U##5S`$G-17wsn_eX_pzxRYK|8G(q?>Hq=0!aqjAv_t-yfncP{U z0A%lXB#`rPaouMwzKA@tKiASE_OXo*1G*nMGRfOt>{(}Y?gM=!E+yqJb#m+z1j#7h z$g`mHE@#F=`{j<18$`k@(=?&Y3#R1)Oq1e5_p19!)eb*ankH$3$ngOp$+`~!+%@xi z(|!9;yo57vM>_`FYoa-w^k9VTN<^6E1>F`qr&;0Jy-w2Ktour2Qegq_57VN~J0@t2 z6Ho0ygilab(35j+=sXBfuOP{3s^uIE8`EUd1h13xak&ikH++S&_Hq})en_%2;iA?V zw$f;ivcs!ex4-f0rZ4**Yu$e?djVtxy@XRS`Cce@#5;@Al$|oD&@76vDsdqy1&ku* zzCa83gqk4sWCcZ)o@H^D@8r(XsnR^aGBz8qy94TiJFq_*MdZX^m27^uC~6GGkiF+w zU>QI4lRt)E`ITS7_k8aUV45cUlUyHt{5ALkKlH=+m0$k9@i%_{uOvfTnY4+?B;hte z=w}3*&|>Jo$P6wJeZnOsSx9^tjRTSM2+ad08+ok~zI8!Kjt!_+{He+-2q1GP`$$!;U!XyEx;0@9L~?>WoTA zIIF|K++n$V+$k;XS#q}x`zmLtdkFuwqHimBUv&~d+hCahU|nsr%Kwm=+ zq))fOD@$oV!vVJw9P*xXv*}&sB);;`q0TDo$M1P>z3CYqDoC%KtA(LZ`a3i^L@N3H zko$Ea!}ML2gYYecV1RzxZ@RD-ROIb0NU0oyp<98Z@$4+Y!pN(F{r}i|?`YkYqdxFg z-FtuEcg_tjp$Gy=2oMO7g%Mz6gOCA{VZgR*hA`L|Sd77oG_Z&kAkq*A6AU647@IK0 zmKd@?iySSX0R%`Gp@&d->4tN@y?1xbAJtV|-Fu(=o~$?j%+kK=+%N6jp}KNaSL;aC zRu&_g?4&*!T5HgGhp*YOjNiWc}>9U~g zCgY7mZA={$b~>h@`e-<0fE5*c;|P0AAHr<{V?vh^O1Nsu3vb; zkKHR%3L1$k%nCNdJH%ht~J$R~nJAihc(k2`I zScir9**(+ZE)`>{rg^SYxoexGlkJo&H_h%T2V<{|HA_~$XKvQSE3wu${CDNOJA$ZX zqBUyQDroC**0K7FO&O9t`!w7ZalMglGv=+7aQBpsxuW1|42u7?&pNI~ICOMm&2ihi z_bMx1tl(qJsr{{6Zr=$!Jbbz}Q7IOQ+Fox=QQwqaZk{&D4M6qZH1p6oxJTIySJ$Ay z6;DEd8$-A4U~|wH!>Ogf7H0mA{#%u+Ad$x*(2A*Y8K=tVy2jn)yK!z^>su=asrc-( zDZG-xrkF6d&#rNA15S=*RQ|S(PBeIL_}Wg6{1{ykPnFSjtEX+_p=k9KXP^^Mth;VJ zMXp9IK&FL~T(Klj#b?XNE3m1NCdT4|ChZ<>H)q$m=`dcdQ%yCE5f}zS9DpIZv5_wT zTi`(~6})QX)YE1BUU9F>F{cQk>e6Z|Y%Y_lV^#;f?g`edaEy%0I4&cmyaONw+&~fYoW$s!XNfLoIg>mT#8mic3bs~+*Ys{Pa5ZY}6lq6B zW&}!=^e=Q4OW6)ybN`zdQiAK+%`@y+z3U%NWdX$KDFb4-Yf+V-f^%+Gt zD*drI1WF`5l=JHWvP{&dU9sZB+4d(cgup#_{7>8OU7XW~S#>h1bk=SaHuzZHBcjBRo@1bbC4 zuU2p$hH9W|C3h8$3Iu7trezv%7-pPnO1%gkw@0S2^4;f5&G$Oz)uI^!xXr!T>Czvz?jcX$7l9b-~OmPb@_Oj(H5D!P06 zRN-s_nj1k~c&Qz~$F7YYZgx4&Y`3$2bt)A%O_|*R2h+CJTK%2z7KH(}M%c0W#_(rO(RH}JJ|*&f!OdwbaT+O%WX zo7L30zHHl|Yr}RW%sTw>ZEC;1=W2M@m1&M{DT8_Nee>wS-L&1)>O;92e}tdJEoj#d zvsvrH>-FxwihFgU)X8klSr=f-LD2BVmI@loYaVU)J}aHHzxf&+O$@p*(i*|fRmRqy zJm1j&c@563wd&TF-Dv%fI`+__dn;E}_HTg$%D?z>gNbPHFBWZHX$S4(_mn6n=6#5ex<^*~ph{O7;WI-}*IG{*QXBvy@hKKQ!Ibv^B2tnG%yY(cT(B$?4yQYe zB_&<>DX?U5UZiEgJk3axq!QnD*H!HIJBxOnolPJr`_qeooN^6Tq?8eeF$@9AoG=8! zFhqnHF^pndjYB{nlp=Bo=jW5`RfiakfEa@zjy2lA8hbrb-e}YEr*blpPXWJKE)7fRs0mwe@DC1HGLCu!+ zfdMjj0Ax-H&XO@8I2#Qidc8}F*BQc;;mX+Ns;|(1%o;6Mqh9)v+QZe0M-ZJ{D!yk+ z$2a;YzgIrMO{I6qPyo_AAG~7~^#u(+M_Yw&xptJ58G=f2-7NUOQRUyt?01w_qI?igB)g z1jM^8$FE?99Iuv9&zvjO^S}UixkP>ff?(1?w$-RJHDH&ogT2;tu?9#=Yz48Z&~GR1 zZUYErVEXgVJ>%3=MItrq$^P15v}uhM$t&{c*r?<}aJPVk001BWNklRW)d&6|hCd4D*=FJE23qSjR;*pPj9NzXx?~FI~y6Y_-g=c-lbMQC+{V(vU zSH9c{8C^G3|1>&R=KxT%u653Em`u>~Ewxo|pqet{)}ud|zKaL0-uL=Rui%jDtTCL> z3)X&JbPj90>j}BRGd~6Zpg>>08&v(!ExmTJ{_>)ax8hFCW8~;(du|QxbKAC0_xF9* zRq+luYUWDi<`rqxuIzn%ZmT15jGby*Wi5SF+PD8;?~3iy@Sk=}E^SL;V1iTc^OJMq z^FrN!Rsfq|Uaz``jsLR2H=9f`-4qs(Qy}WN%lkmq_7=UutzD}IDROo~cRlpg)zb<3 zF?*VKFly4Gd&E~5c-peheva*v!L3=#o9kFtV|O>yu{eI=?(;nCLRxjv5AR^w(JyTDnxHs?{0j7+;T1Ev?jl{yYEbpKIn$?b!8$qf)odsCu+H%H*#xc!4dSW6K)Q zeqSRVbwO8{L)|^i_}7@``|C{uHPY=nTFZ2HzxhTRdFLXuMgt8@1#sT*m9xaFgL~^T zd@)?Bc*m|UqH2g+G(EJq)%Ve)y&9a{iWAH!st$c0?bp!-qZ~b!Qo?a&aRw|&=rbn(lFA+8V91b5gkCagC1RaR6psK< z6bC?vv787HU)QR_kgwfZ}w=Ew$# z^gIdk7GF(yx@nV%$KbN#faB+^)zLOs0gL>6`~YjBXMNpWohp(h^%Sx zP=i<74pHC}WZgI~2ogh;`7*f_`;tzNDw`=t5kR7VoR^a3u7pA66ku*?Tr-;haZ-Q) zP6=Vy0Te)auJj2&9Wy8}rC~4S*KvVy-h8v-%mTk&$JRf()l)5d+PX(*y=r#7!@kdA zZr0kpuH=IXHLlO-c+3&qkDp{jjXromFE&I1;vJXbS3nu2?F|Af&Tc$uLy-A*Dv>r2 zS_d1@0zn6}5_)b{UJ<7DCu!FTv$c%1HPq`usQ0O_y6KI58-iEJskRdwkXz+&eXq{K zL#-Fyb8lR=mek;s|CM9M+HqquN8y@pd4l&09vk;6rqxGy z-KgW0tEX$>1K zHgwXnn{Fm`{Jsx|cU>0^8oS?{vtfPKNw>l)dUWkv4BIVy!p7wd%Zd-$M!OBDZNuv& zb_z@xy)QiL^9~xGXZTd{t5FE;uXO-{oIzTGC6ES;sI(j_EQ88P4CQ4y zB26=F9vWJM^M>Mt>=AL z;7c@+y~jrPhg)T8w*i(J>ir>Pk!!k&4w3XXuq(z^^hk4yk{Hx-;xBV{n#xG`*e5YcqFRr;a&s*8sxPh%X8k17jEn!vO3e5QnY^ z3W+iL^k+O9PkY}F#@jsM9dY&cJJGMihM9mj?uL3@zH-|wjiZNrQ9DWMw| zN8M%M*LJ*6CuLOfoC@5&eI=!;K(Q6nYx?kARnBBX+e$mEN|;Nz<~qi1Hcc8)HF{Jy zRYbWct$j66GQN7@wH`;+F=)Y?O(e)jJ?BP=-Er@$L19B=H$LT=%FTDj>5uHRL<|1Chsoka90$gRxi z=BB^7Ni;|Ecs%HyZ45A-xPL7Z-r9QLAX4VTCg&=;RE4)0a~-N{k5)IT<7%BY4xl53 z3Xk@?i*RLAiRtLAl@Ge-Tm^o+=FR0;NY4Yv-BTTNwM~6S+rKEY!jDn!(^mR%ZUJJG zz8p>1=d~)>(ntBw+pm4}ySC-#W^H!c6C4w2oEQLfZ@H|5okN^u*XqvlKp4S5CRPSvru6)Z@mg>UQ zW9Q_!xtiiGSIWB2TepjpGrehtDW4$G0XgiDbC7jgWKVEf66R^aJe}k8@+ItcVg%aR z+{{RcG0h8>^9jpz#4rZjan~I?2b*tB znvyR2)DQcBaTI+qXT~%wn2vG;o9y{zC>m11O_X!L<*ac)Pf@bU9p=!2_381%3c!^j zToS#e@;4NNoE#$r>>PEswqU8kFA;Ecww8V5g&qa}$j*@W#VHsPP3Lli8tFYD08~=h zi(^A-kYpnO&KYT5z|aUfO5bg|JIeX%0+RqjMw-Ro4wR9nBl2`Z@Yc5Z7A_cfQURU< zc)9_Vqub%SDhH`2!SZJgU9`Ws`>vBw2@zd@l@(+GnU5H!B0(-Rrup%vdM05~vjbdS0jQ zLD&7~QZ%kI-e-NpbMUF3{gn?}k+uK7UiZBIHTc>u`82%j4}V=0iVpT>fOpj58C>0m zre1ThJyJD2k4#Z++PD8MR{HvTDnpldTgt8xoEP3JLGCn87~R+T5nG|ds&ttS)7T0& zL`SXtJ?7vwTBB8TMCa8omm;XD(N-ho4cEKq>Me2yuq`8FwFJC|U87?G&HDHmc|(!> z!yCA;rD!>w%JtjrW#@}uSs!vnL1nOxzx8u|BQDwiTJ3TRJ5d!8TN`)Z1`!ynx#IRJ zaOV0mgQKIZ21L$C*GGF0_QPp$o;UAVJ>R{lN|^uE;ku`Fhh)9r_S0|aWowVQpO0dX zyJrI(8-s8)55KNk>!o98*S|3;JD{y_AF`4@if;FHaAIrE8=GYFxD|f;(b>8vukp?z z>gqTceYGIiQbtQnw>WDJ23C?SB2C+4h$gaeof3y;X*GNv?O;Q<`?QrHW? z@i-w+iJVUf%RJ%I<&)MpFK|sMW1bSG^K&fojNN{~U3cAPmD;B{VO|zYbHX$&62;C8 zM%e=nLXJC}oa}Hq2BG#ODe&_mQR>XVVLxIVBSIi7sZ@ub7Emb8lqDOxR9k>8id|_$ z4LwQg4sHRRuHMOqmC}V$pD1OdG}r3tD0mb!3-A_lxKSA#-I`%sczV|3)x_~cVX#5g z;g?kSjxQ*m%UP<Tgs*YIh;PJIDuBqxTb15Nj8gmC}MA z_|C7!PyNWZHN?^@w_ee*)#h@%3YU~THPRV%{~SbEHfY}|XuR>cUcGVe476T@b42Sk z`A%M}aJp}z$PENN&)&WIZrpUx4S6+JrL$Tzj~bfukF8brLfBq;GG^}|tP15ry*$YL z)$7GQjzC(ZxYLTVB0u&F`|%b%Nyp(m-{9>-|36v^u~4% zJ8Scy;H~pET7TVeyiDld=4O2BJ}ZBA19E7w>dTB~AJwH!(X0PCKWx`Mt&Z=~oqp!L zw1-u}mI&K8j)>!kakndbTtnmZUbV^HOoCrNvoC!e+#Ek^2wl+ zw?NqKv5&xUUhu%*-Hr6XUtoIe>o6ZKA)VfiCF~IoCn)9}rt>3+2x-YUPBYT7;Plb~ zyPXt-Te8APk5Q8|&hs*3zmK@-mMjMPoNzo&5;@O$G?x^H8&3~7J=tR* z$dO}7(%>0##yyxN6@82tVnEJ}<8j8aEC%DWq+-YkUYT*8OFXX_l@^5!0f9oLajvP_ zuq~3hu2i;Y>d{{%)!JyI>M?3a=9FMDPew~a%E!DTyAdeWMlgO}=tRfIEU*Kpo^eh> z=tPjh%wE7U+59+XN$)O%tj}x4sl`8wp>23q;F>KJJh(wCA5pLca{>?uTnu-m$D9{X z=2D5B>N({l`W@gLD};J3eM(@U7O4Xj-Lpi6w2u%m1OoAb3##_%?|yun2KtlVYR3hC1dp;Ja+$Vzq8?0WtFTta4wxsm_l!8Sl~+g5%2;b5 zH#p_2O%V{!Pu+K}2D+we1p>bCtG^o`{NexfK?eWkYwP-r7rhYQ{FVO-H?Kb+jJ164 z72PdGj7>qDvRVTH(8ufmJ`ZTBW8at_%RGN*4Kh*`?@i^g}>76P0c6 zo*n<9i0uI3`{M>ct=?W7y~UZKDnmr~YjB0~FZe2cV-c6-)LZDwM z_*^*`$FG>fmU`9+w#ZZazNK+q=D4*bfVvO5o`ec&?a%4$ z+dha+xZL{cq!CP%LqAnc3-~)WTjN$3sBz?-ZEIsodth6h&DM22t&g_f`rCR;>qh$w z7>|(^FI2Ad{-@TMmboA>0mKj>Ok7_Ty6x^}%X)94i4 z{CobaH}(9ZH^zMgqPfTS(=<_)|MudE_Gj2Ie_gv~`*i=6S-U%ct1yMlsr1(%Dh?>G3#YIUbRgjKd+~w%e~_ zzaKFUg!4%v;KxZStV^o3I7URzX6#QdVHhJgG14?+nkEo4hTR}V;Bv-(H((edVhl)` zWe@u#MZGvPmN{W5sqm61UG{$mDn-=>EB3L@gseJKSdIEzk6ZJZesYwZ81B$Q zX5~LEy38e=o>?l|1KBdS8^U$8OeFKM>=AZyq~K7KSwdv=Bzx=uAOm42L6{OLF0^Tz zqZmbB(~876XpvSK*pkV~kf_!!joR$%Qoi@fQlqn~ zg2?4G09FHCg|ZjUz5#h;j2=h$dW zn-;7#-O;%N1!*XHi3Yu_$wINvDJm? zz_A*09fIO;jkNcp@N}e3*CwUc8|Ti5@>Jsox+dBd!r-rG+=EhTWV&* zV9p33VA$_59!@dr_hQIqIZr#Y7~{L$fc<_4c2UNZ8K6?xno2~>;hEt=S-FrM&{ zpNJ=Y{6}Ga%;WH?Fd|UEA1xU#zjh`QSu$vnW4UM?z%c^50mx!}zU>(B4%hBQ{Jmen z-9P^eSW?0~GeC@GS}+|a98UH)oa_)|EU-v2C1y;=1=INv%wj~}e)|;+J8|SJDd9LJ z9FGf*=My-~Uf>udH93TU{iPFt0_J(a`OPD+%ouhN<2ZsyV7wa$;}{WxoQpoq3Cp}7 zE#kyUDPx&tiN?!Wa2O9ejEB7$a3O>a_GB_&b)4!1DO)T;DLP2HbwC^o1gSE=q>Qvk z%Km&@kWw|2#EC_SF&5ktPLVT%(*irrt-}X^>Nvgr;N-O7n~NAzc~xGOOJ)3=xES3* zo~Hl{3b3b52GR_0Mi_U{Y=f##ROLZ4pqNqixJ!D!R)5b0KZw-W)<<16`sR#5sdd09 zNgE-SXuH5J2DXBElGdJ!Q$`GIIlfJ+yGz!K(hUQW8NeCy*^L6OQP$x2K-oj@oW4$3 zreP;DpO0%nRe)!@R^eqP-xSBZKCMq~@cv#;=&xSJ)J)T6$W03uwPrHQO?m4%KV@oO z+W@n^@lJJy8tL+Why&Gg?lOUi>$&9Rm^C}WfLk@9I!)b7b&)oJtP6l<0(yU$aA3qj zD~0NrzXfB8PRXSDr~`NyAe0jS@6J=E*5k9z{Xe#Z+ zXfGYRSJt&Jol&oNDoNL|RSmsH&YLMd_@zR&S&@hInEOx_MFu3rYlCU2l2;yC%|UM> z=<^jyZCZPQgbF_sVA80)qGm}L4BfiA;cqhN-jXt%SKpJr$xr11jk9Bqjh7sN&jw(2 zSnUN_t9vw@zt1%AM*Zf9D)N>-JI3yRcLpMRcwI!-%?GE{I~rHwpyI*y`j+7j*E#vg zY@vF?YK%A4lx|5Atjbk0)Y6~w9^dw=-3FL!c%%HLSsT}#Ws<38r|@$RScK9WBdqC? z9m-uqsNEDBd@qyrys>%j`!vSYV$TZq$}MZwBLHA_+u580$m6KON86&VPx-o?*hM_G z=hKy7Ta@I|V&${?S)*`321ym;I3VsuiB|6S(85f)7}{Kv`!PxiF#w!1z)3Je$1cTD zYtgRE^+=vr0ux#FCRUL7y_}j_E|JF2@k23!LY2=1Aczw=0El29XB;lczV&5Z0A56i zPB+1PR(OxbvRYUMT^Vj21X<84p(IDjx;yNb%i-6&2W*}jpn%rnl;7d&un!uk1(oTh>ZdSn`a5Ny3u zS`Z+#~-2!uF-LquN0NkDNZ zd@tB;gFw-$$x{p%C`c`foQusAOh(y0daxP(aesp4?7GZ3L|}IrI6hEytxoXqat?%@ zc+X7nL#z8V#)Cdyh`QtOj*T1LyVY*)Z)^g6*qYziD(Eh+YxTO~MSN;gU-08?I-usc zc6>@rK9?1scYE@It*W1$Q^_@wUQ(37^;O&TO50X?C{#G3?w`|flK)+eaotm7p8-vC z*IP|_FYRf!wW53Vtp3+uzZ<8Qu53W+&Da0P^$Y*y$MLPt{ann`xxrr%!PA_|Xe|bl zoyR=8d+ODycs_#HM)P0_mMFE`&#$j7GVAKhz!HU z`S<+8D{U3%uJ9}r)?0w^JZXFWcu>^371llIWgLCokZ1IL9~buNg`v(H7sKC!rP5l5 zuNz3@)3J*N4R3&XI$8_5b`Tfir@mP?@?dDW#Vf4uGTw_SwARlTbg;>D+K2u4w#Ul1 z7=J|yHR2okLYw{5C<|qJUQxKH4^vnhUQ^}jYJr-GtkdNiJhn%bp5^{NQn11ooy69N zJ6vsNqp-2zl_C4Rs`cKwy@QdmM6fu@W^5Yvh`SLX4hZ9j!71B7t>zs{RcBiXX4fuC znzyvS%u>j07(zWKT4|I@6g~sYnrf^L>Y}c>3ea4vZ{>rbq|B=^q(`L>Vyg({Og+%=ny%3gFY;8fnHQgwcw*$Tz!ch#V~ zV6jQ%yYh9vz^HP-Zos80m#{zC8*mNgTzQn%6yg$H1|SAsvA?n!>~5J_^~7b#s=gkID<~4iNQgly z%^#-)%OXV)a}wA|(+8`3c|c$tL8V=UvW{DX=OL$0DLr-ZajRW@afRkE=T|aQd6n!4z7%IXP|BwQgvM97z*3Cw2+>_ zs0hLJ!;WDzs`Ep>JmabR!B+Zor7sgg4mMqQTM>Fc5~{TY3yl^x5c@i{|1_DbzKEEU;}3dDQ$7J&)ngtpVvjS z){OO5-h$0rc=g-X+-v;5K1bckD{txh*jz_#S7l{mkhi~kUr)2n?nmE8!*-o=(if^z zn7YF%x&c>iwBrf6weaA6Jtd7 zxRS*PPf3oX%H>RI3dKl@p&0ovUc@YAENFaC#DPa&m&*euof93d$9tn2rmM=SOf# zI6d8Aw;w?WNDR;ru`CH^H;!251%VhRrzZdbOO{NDlfA5K2m#YP<9I&X5p5J0<2d4Q zvcqn-lPGR+2$^h9XOq+Avc7QyCm)^OSCCZq%!=`ztNbX@8BGB<>SRJ%X3V9Qg0eV! zug0xke~5ERkE*B9JU8z=MfsQ>Hr zM|bu5%dPlEnS9l+xXp6&vb2|hO;i0?I$g3i^u7K|7kt;ZuYW$hJp-Vpy~_z#VnS<} zXDRei4R*Hknp@oOyaLUDsU6$~Go`ZcNh`0%NLHDX%8F6nu2GW(@Hby?+I8dlH9YT2KMTM9%Rd9H8343OKLA&?U6ma#1h7pFr6J!|qmbz5o=_jc z)yS8gsNfXQ?t3C-s$3%nrD&WP>H57FJg(2m24v#G>@~%US_9oFfHmFpji0aY@uJ_> zUv%I0hIHqx?C0Avquws@>skx+RU@Nen&x$ONBek%jM~v^?F`EZQk}x%8vEzU+PQVH zn{19PuRGg`RYcb(J68&Ll0=h_;A{oU)m z*kAotTLMyC)8BQm&C0Bed9-ZXu5Y-5{H-$!s-mgm^6*|?E)99ZP;Z600z#FU z_IL7AbnP~d#PXDvy*BOwj0>axZVp=tM6YbDCO}l8MgIE>t~yj3ZNMFXPlSdid$K!?;~mW zkywg_g;0-oi)Dc_V;BeQ#(@10up59~$Ot?G=?J7VEYo%5`KGL%7NR0?Gg>xBSui!u zwTk<)pcv|U?5dY_73nTSJt!X4t5vl7S4O^hj@_ll;+?+X8}UbP`2_s!alsqTC$VAy z@w6`s;xvQKCh*xg?mXFJc=Vlk&HXnJZcK>FjA2@Uln|B$;pP!IpFx?yCnIR`UQs^S5k!YFg(>*H;MfV; zwmOBh$Bw2^e?!;j*3}RhRU=#r(dcMGs0xOry?Yh986E5V)G(KMSmB&@1kD!@rW1Eh zdL6l)E3MvCfOpDLpQc(Qo@>l8xFQ^UA&lNQlCJ7v*n3*9N2O(27yY4@=~W)a7xE~y zQ#8QTw5JV5?}qN9-WOfocG-meEuQlaH%r7F$o&wVF|t~<4xgOuQw<+?UwiMZv90~t zo16U_Z{6=F(ZY!OIiOvY=25C+@9(jnJ+5`_;zBrZ+ep{>_Ig(N)2CG%PlQ`CXKee0 zTnT+^tHq>MA-_%K#WhhHEu_=*<0GOM%&fyRy_9a&{99q~_ckiu_x!z?J-4v;Ys2jC zu05#(v-T-_WH}JR;RNy0WrUMcAdbi&qzt5T?s85U^OBKL!cwYh?{~3Oy_RBCl8!BD z$H|m`KulrAZX7U-(TZAWF*P;X_2{OYQ^61Uonn;g&w8{`NofU3!CT~N#QU^&27sK) zF;TGSn5;pK7Ewug2WeVwaT<_D-e76C6hm=B2!kDCz%G&$8HEog@yZ)VO!r;GcOW?IF=N7BuOzJ4+qSNkmd=uT^jAVn*S>5&ILaCFDH~m@146T( z)Tzm77Ft8$i^U;l4uKHMQFK7g3-UafEL=)tog5rc>P?t!3Q67B+-7+`2=gsR-q1BeqhavAreEg^4 zbH3;sYw?FSUk~f`OE3H>eBGCRrX1m7d$L=FN~=aI7J;e?r$y1Z zIL6=v3*eN`HbT0Z+}0d)dWAt%lB|Mr4Gj6vvMsi6y%_JE5S2pf-mgymwmVyfNzMHH zGfMl$E4qE?HtzyZa|Su%+{qZ&uxyqsw`Di`c1$g-wiqk{IK6Te@AkC!!+Sja@8d83 z^kw+jpZpIScRD51jZ4N>VRb8E(OjyZxP}_%mV5OEdjFgLxHeAL+ak95sqM8?eXFYS z?I=3Tvxho~=+>>>CUMbt+&G-vaC*W9e{pi947RtCsh`lQX9u@VN#O3*p0Zm(?#JTg zUE9_wWq;%H?W)2+cyL_15U#!A6dK!&0lPsA_B~|`Sis9UczOVs?gMy%Bv+FKbe6K$-0wS2 z*&FCiaA75a0JP^3H#FMm0sgT(&$#yuGd}70--0Vo{Q&&-%=oXbxfjEIH!$2hL*DOX z59KstVaDm{9*=s=op{Bo-+@!tDE2~vswIF+Ml7$n{X00iC*Lc7xprU*om z2}VLbjF@&K9{1ZngYnP*0CPCSJSPw{ZolmWC#MHw5T;qyX^2$LI-ikJ#%|E92@&(0 zFdiZhB92EnzHK>9h?Ftx_gFZ9bHeF9;L^#6aU75{W141hY)3Krv{pgN8S|9PDEF|f zdz%#}9RZr|#x0FHole)F(f?H^YV-w{JyBkC&vlj8l1>gY=;gJZpn^u3XIYOFeAjF zIPf5q)XUf;ia%JkK%UO5T6#{Yr0>V3lDqP8F6$RU9TN!1X^~3t!)P@#z^X@S=7KC> znx}HMdZ|6(h%Pe&B;)4k26&l3aR-V6c%Hz=vLC&}DMJC6xUdgi_7GYl9qyU7y;i@d zUvurK`@9O^HM+g4Beia|>6<;0ccXA9*nZaUc>P;#U*^D=gZJ>CcJ`rhx0p;VOp!SDQf{J^(7Q0kkE| ziUQ@H*}9L*eNN?XC+y#Dc%*H`S%r-oIbTGbIu4i0t^av6wLUxqyrS1ms_(0gt(6y+ zcD)I+=1KeB56wSBRNHqp)oL`op=)0%eqbA~tyl7Vufp1*RT~P4wbk{Z%26X%J2~oS zQ2TVhH2l%gPo0u`ws4P5@v`V$=|DM*pQnGO!d{eX3P3oVB3`+IaB>B?>`Prrr2IG~ zOp_GI0?SsJoHGvl5&QiJiUH6qMwv^2Dmj7*$VDfSqtk}5rlJd|$aEO1W8v1uj$z_z zbn9MTgNexmuIuI$I&~^h`9dpH*LAIWjw+rMfLJ}pAgcYU5oQJoXXND8lU5-D(l=!8=j-HNksNkYF9rQphqc~?P3@=e+s~<&)%Kl^U*)Xh z=lezk=9DEeUQ$Up)iWCoYC7G#K9BSUH;$eMxlvl~4%|5ak`9UFSHS4TbV=PXZV z0s>1^tZlYaB20?6?Ry3LRe$wzeDlBk9HhiRh&a7`)d|V&=|%x6ukId)uWgrQSReY4 zpNP->;;+GOx5xh%S4s>1&)`&;asCoI8+?`&_brxfFW_a<|u)>CsvHRSQ#z@rDt8)w97x zcN;K}hV+c$Hq)XVhW4Znk=@kcZUYSz9$Lp-m?6L8>*<8NLP&wjpX0LUI7Q$+A`O1`V12#IhniXN)`H59qQInKQ!`= z9G-JKIOu&vNg!1g+Ii^SOwSVtT4c?s6gEWe=*xd!qP7Y}s+eBs(6`m{7Ui|Z6&j~s zeP?j-?B)XJK?{?kupvvt&X$w+EYFgwF0mqKz}bPjn9 zL}vI1m(fMjD#{+tl^%1+5+W)JcJW%0d;#5yYKT}qQ;fDJI0i2G9RP};Sov+R^_MJ0 z0Hr#-M39%1uuOzwF%EVyAjCVs<88pO0LzS!j^ODgay~;!$C64-g?fq$o2p*rjdPEF zh7}#pR?*_c3IkYf&baz^Z;!Wm=11UV8TjMlf_y#!`yKLrM!azZGht!IWA3^Q@9@am z@GGyq4~JL34#SNzATem3fyBiq7yRLrk>`x*>wx?c10m8hf_ya(DTL%-YIW_vQ$?*H*+XDQ?A=S zI7x;BmFGf)rGhIXq6iwI&>&0kH6alR1Po4q%2OmFbqX5mG+Pk z!+>~1fE{+HaNXt|l3v&1& zg(7Fv*_m^#;I1%<5u6uL5F?!+I6)K;aw-lEAdaK#n~y_TH6c{ixq^T&fQB9Nas*g# z8M{;P`P>kx=U)x++A4QC0k!W{uW95sy2~+#W^0)V^!0s!$|V&}4`JIP*D+mWGqex* z&epuG>Q~D99lZSeXFUB9>wtCtP__aeT=o$$a*|5!dK4QoY7~7_XKR_Re&y5Ei%b#3 zC=v7+eFWVdxmF0aysQ*VYadmh)%_~MP*T>2WmUk0(NqAj{pSRTMm)<+>{qK?Cnu-) zlIMOqKI9`lVG9^<(v?%fi(mBf`1u$7B!1&pe;)VWf3Ga7cTd^aDqecTW&oG3T*do6 z%a`iH-R&;7KI!}-~DVY)gdR3!K7=8;ujKk!3824D7#{~ix^ zz51_TiI02cdsc-|7pJ)hEulKa4^F|P_E++QYc7|*NQ{5*!5@jQ`j#Jh(6RpIpZziZ z#mB!t9BSD*S%rJJ2)fTyC~&L%{B)`B)=-uF^**__{9|7}k_C+XAnJ^W>E&;8CH#`{0>*?6$) z2fqDz_`YxXTB+tta*nV&^`sdWIhggv`PLq)hW9)0HGWIy46ABV8%rV6T#Nk zDs9_=N2oML;ck%bsPN_r)sTyTW8mIwH_-B$J=%{(rBBxIIli+quP{v-B0Y{ zQ00p9Efd~coik)ivmKkR8Qa{+&8b&F7;5NgzvVz4XtcMwEH0V@8~?Uex~WqG$+gne z`_kJ?1}p6BWEQWUk<>j#*dH)nzJl?}738?Xf`DnsSeAtI0_A~2jHUifDBJ4-#$hnSK^-Z~8EL7hx7n(dXSY9Hj3dqrIrp5(F;u$t z*#t~mUx%9G+-l`ze~eN6jSGI%1*-I3?{*4Nxg|3rU=jTa!Y{eItr-)QXdsCq8H!=f zpe6zvVyL`I1%F~Wwkc1b>4-32M?RNxyGhf7sp4N_h*Oor{(E-QHJ%TEbH*E9dxlT? zp0C0FT|XGVzYt!Y8UOvbAYD5{y#G2VGnPwxJo>Q5llKFD^{-xo!|U%wyjk{i=Pa*h zM{tPXg|VESW1eT+`J_kVN$>TJxaaf)4@@)eb~5zNF@hL){hSPtj|jjciSW1pJUS(e z_q-0T{^=jZD_-;>#49J*U%rZ*On!uc^ag;A>lIiUNTs+HrZ!=EPEgTWMO(E>A{djkB!)FT_Eru@fQZ zMJlu>anvZRq{c)UGb`N|-a?_kjLMvn8R-g#5D3J{ct*|%IVFS;z!c0uOe`il%b9Kx zMdvIAdmMmemTfN%cN(>^ll=r~({wz$v3^*|0@v6&6Z!%m6FN){_1tE zzH2Yf0mr}GZ~bU{a>(39&l$jAgVymWL-c>LI-)z0Yq{%GqunMu{9s7aLY!jZ2AEmC zplpqax1J^Chb!GQ?KewOwTAXb0*DDQFanoce;o=FvVy?wuY+$5CKU)|_Y;7}Kj|It zb>I3Uc*2vOjED2OapO9E?D_u=|M5S3A71;KS64)`HKO_*mx%{BhE>K}=681E8h-Hw zKaF2_;ZNhPyB>j$f6k}jIiLEOxN`Njhh@A^efRgqSA5g=;)_1>ImnzXEx8VlR>=RL z-lz*M5z1b5?eqGqV_}v(<0I)7jYQx7T7j=dy6?r`>oqb|npDlKtf9~! znXoqF!3MfDh6Y&Or+%+or+sW$eivMsfy9iMsT2~@R@@4dF}5&8%js`%@x}*LsGv$b zHjMJE_8m_L>;BKagZ8-=V`0Fp*;NkOZm#yeAo(zi_@vMJJbcJUe{v zhhP8YpIhO0Yn)enlG8drX#fBq07*naROKm@w6#=@8Tg`r7bf77Ukji{=_{bg;o%lM zDz9jrERFCk<61?tO97w8LDMJzx{uTGC3g!cJWNq-4WasNH}EossyM8HxkbBv>@|z2 zHr%$i)sE*;H8MtV#Bk**#;dm>>`##6fSG}5&Nw^HI36cVlbr7yNs7G1;>e@G7~+69 zNd1|V5~f)c&%=IKBEwX-*Np>Wh$SLb(`-|cf^sQm+B>TN$j9*&^dy~HfuBGU1CPD&A6={;Am)vRu zmDvOkFHVi((q@$%>|UuFZ;KK1Y8FPU)15HTDVq-jQc;0Ds^0erH@V^8;Z`bhZ6 zSG)!%Z@5p+KR=s6(+rG*%q0`1^CPC4XL#rLdnY{hDNn@jB;YrWGw6Yv2-A$+aYmdM zcw$~7;9Ddw3KJq zBI=ToJ``4&!JH67M2x}qrd!4e`fhyhcYHnWzxSSYZiPX~zPFkjC)`w)k?Sz+)%G|PqPX{-*W-J?{dxG& zANW7;DgWy8@XtQ&vo228eIu^-|A!xffBAV|hVS~OuKWA;P1Qb~8z zXuzgGhvJFDZ|Ko*b)WQw{TnRTdqh{%c)`UiTiBp$##PwaJz$S@4|-K}xFsub_fz+o z>$lz@r*$06d!ymTX^-;7g*GgTqpdmvqme|YOjVs{B;{i(-!1g1hPlxz*Y7)|Z-Mn7 z5K>OJ2~k?Z@KzU>`D9YFBQ(aC_GgYF2LuC4)r?KS+8S4HjBZ(S0zJP+PZG8M=!vXcY5ht<{d)7H+m~}YZqP)surQEvxrY?%#d+PD6ifnbbxh|>pRi$qqx%JvZuUxI)tBwIc zQcsBx4+jjFuOjRZ2>U&9h?to$&5YwT`^u3Ki7@UXBnp|3DPTYDFbrbkEQ_S2 z?nWu>rgMo=BI`pOWCUeiIZ2AEM#kqw_U_8Q;Z)8&m#CVR_Mw0?A6S{W0S@VFN25V-@>7ec8RO{~$jP>y@kuE-qh z4H$()?*`mA1pLzJ0gt(Sf+u~!mw|utm+<=Y%+n7HcoTr+S4V+pAourx?gHoCTlcxok$Y@HLWs+sZe~cbbMR zW78ruil)WjFZwZ&mgAQ{CY1BW)rjV@-!{bF9uoDG*`Z#GtxXG4ucumoJHY_ zAtD6f9}lV%TI>zpMh8gId z>gt~t%FO^uMh8&$Kj{6Y15l^X*z>e{tBb@a-k(5)PDN>C!1gukv21nBsw8#MKW2*@ zn*yQuQ4}E^S}X{v3sr+NWY1Fv#NBR(&-(o5;^Y45zgPkNO}YN$kN+FK{PRB(fA%LY z6=Ew`t7$7i3ud7Z3mAmZC`~&47D5115r0fgRQ)@@;VbdNpZalp+4H^wPkr~Ny~%U< z=b!$+;^lwxQvA{je-bfLjdGWTUU{07Ph3cZ8y5o!k`cj-VqB~ns0O=emSnJW(Q~?W z-`*MI2k=nG%K#Q;JFX;#i|#CIjg}|z>W;qks>WP6q}7AuWwGna$7X2#tT8xlf8S(T z?7mj|t#Nm+?5`MFC1dxVI~rCV)j^8eHvAnA<+7Q%LA)`>xxbVhg4oXYUdKD#-4+2u zkvm-!wI68rR->ks_N&A*MY`Nqg@Z8gy5=swLgC%b34RtEpfMrFzKtp{wT%yW*0b^2 zzkM}+_t#&9fAf{!h!_9j&*Fog{n7ZxAN=?5T|fQ;{Nf9LT9livpURKdaM=P?cSkcD zb@;C5*v_Xa7^~TeAGZ;jU0YP~oK%q{k00GDDx5TCp_5zl0BkXaW(^g8RdjlMuFhZm zTyX^0#y#rZQIVIMPS_5`)eyE>m9xqzjuet{>K{+i*IpA>EVZWEHTTH-iDAm>>h7EzS-wzlDi7e{TvIKU8;&O?S%j{3Non@R=*)9f<7zJKQP~j-w!%DVAvfH#{nQ&)jU-|I3@&g#ELyTj9^q4;A zb8^4bVkmttsTz3}RIvp`5UhdLa8Tp7^2G9<0we{bM%AS$f*SRP+vPXiSD(F+V$Une zvpqvZ;8?gX=Q7U{i7w;k;vh8sUW{_Y;+)Gu?;!}Rmzp8~KnzFYlZnJRp6PcLxUN{ zGXyv`Nv)ka72b?IpJV-u|Pj?nV|#rm7- z&)NrDdwunHuY=eSdZoMAI2ZNibvpv!Bj>(3eI0Bv1-?R8HM%UHYlPXRL``=9xrlco zmHnb@4YyOzosJ{MI^aXXp?vSttkhc)nnlu|$?OE*hreuphWDJTl>hquqaO2?_^NOI zA-vPOyw_?*Z_4%KKlnZPrsw`|nC7VL#;AIV+3LO- z6T9EZt$!V$8DGr}+}_u+yOySF`<0PZM%8KF@Lt=#amVb8CfNbK(x9hpd)g_4b&ZsF zkrDWV8Cn-#y!u2=AoQvK5B0K{FeJmpnamAL`cdeTIYH6m$L6au&b8&nU#)BJkiGJa zD$JV}s1+Q#UM*ho7THxCB9wKiDd_~@U7z-J{L26RpD_$O><hqbo@AmDku6XKvPb}&vK!*L?IpVl{O52iVvAcJ(JJ;ymLP3l2 zwe?axJ=gNM8XfJnr_yLrzKDesjdNlyTOcOCX+h8~xieDsv$^jf1O(WWDW=5~>VQQ+6sxkm5QIZXu_{w8! ztb*4l?no2+`{}*r4lihdX(mm$FKIKiUJJMdX|wa<=Po|) zy=+czYsse9fH*@1NZbW38c@u*R*^S2O$#NH6qT8dQF6C42yG-d5};$g^aNG3KvKpL zfXX|x78#ovtbwI6p6c!fsl5~~X3dqJ`Z9*jfg6AuMoSW;4$gz>F+^WsHuT}zDfHXb z@Nv2mwZ7C$F=RbQFk%~U??)bpJ9?YpQ)`D<>Kt4xY0wZOcG+qPUeF`F=cZL?Zn_;~ ztEWM;Lr@bW1y$3c3540zEAh;yJQS;TSdK5>wg&aeH5gyD4z7_kM8g({fLl8&E1h%> zBBy&Skc?AdkDDEU$9sTUP&w~VubxIAhZ~n}hRIhQkMT3!feP#3swp@}8dD*pNbJ3S zr2tQ(aVt=*dt)%mjH>qO{EIQ74Kcf5gcgy-wWF$Q_)4RWGb1po#fXk8n_GpgSy_Kw zARWX3dX%sb$jN^6sDYjaz*8-LP{3-b!OVh-00FsHk@1LA zatYMc_uM-N_)N!4c*e0+fuRFAFMHb=fHayaRvjVcFo(`pYH<++Oq&G&u>=_;5|}0L z7^@Pb&j|#ST4Gns45eV&jF~WIsuTirT3Vdeg?P5SN9dnY2eV31lXqJ$@?Nr^6FD!5 zxoM13OSM_@JxicFg6VYhSa%r*Jm_GY{GLx>i!HaBXXH1n#1L@m$#1|HKl5>vrWVdQ z)tCb*@B=sjoN|ovxaTq|Xfj5Yitn%pkYI$^;)Cz{Z(Mo#pK;P@AG}LJubcfjVnrP8W-7^EPeHGb4kM_RQQ7noojkFDm_?xw9T9F>_ozm;Rn-ZLV3l68-)d zVvJa`dKH)%Kfd5Qh%utqA44-7%BYmw{sxM@rsQq9r5I}K7 zIwZ46D9)T>yOe7n(esi0)D;ru4(A;>&%BI&vE#J(hdcQ!3l~EpTg~?9++8;3`3^lF zbsdxIY$lvz6L9@;P_HLup1m5r-$R7QFhtDG4lz61BE}X3ab=1zqHQ9AGWovpn3^2N z#CRXR@^IuN_*N@Er(^vJRb7Ed(oeNPG+t4Cc0(J`goxp=5ikt^+;H?rCjdbNl1H7f7bgq-H=pHT)hT6F4k4|MD~(?7jte1T}XiYXv< zjS)wG%KZjpmpKoDe3kz)ms9;RdzE<$pN zTb0CcJ_6i=TOhhxQo8kOwAC2K+I6VgJK$B@pM|6f4z#avhOyQ1?eO;~bYj%N1~=*V-{^WF(|M&Q!@*oX9f*5{giXUV0sUl?1OHJnAu_qgJ-=I zOTT<3$cbyfU?6_t6eH@2Q1^P`H{JwYZ;Y|Ae)7{MC3aEMs~Pw;;Sf2Get#_GLT85l z&U%jun2N}%1esT7^IPt3A;=^9zI%3O0e;^642TPeu1DKPp=}Y{7NYS5h`gpAv#Lpj zVtJ2%IYnZZxD3h%kgZF#+>Ava!coBP2n?Uv5n}^xS^?e~FitS9@is0cw&D=PzrXg` zN}z27TssCJ;9c^HSF(0vMCC<57=kEi7*z^_$$;F?q|Z9#M`iyc&=`fXdh$Ypsy<@d zz;Rm$*)1D{?AgR;*#IVn(LlP(G>0U0SF+M2j~Ih07-SxVuhQS^F~r$5h%u`0&ja;1 z5QcLV8=W&VuO~Z^4VR6wEpxC<_v<|T#>gG|vhEQ%!jJLXCr6bIb1NO?8Q0IEbodLs zIgKT2OA7Xn0aSJvfXl-H8Jz3iX>Ng}ea%2Ik%|0b?Qb$yDEX@n;!viJPyh@huA$Ew2;JRL z+P1-ezVi9F==^Ucrp>7o+)S06lUeLAxRl3uv(2}_mP@zB=36eq;?1_e+SRMD;?|pS z`)#*i<%-)fLr@O<$l4-Da-e&{1>gK4R4hYn#(<2M2<%Z}PXkq1J5 z9U*Na{YW*?=yS*vox6P)C6@Eh`O%u@xn7p_QtI_SOS;CW==*$H>d|)y&E;ZkHKbSTfm z_Kebxl1V`RJ1t*UO;LXl+}Jo=T?2IwsA{;n0(p<7jTkl!27?w&(}H7!heVwo4qLRE z2FiPf#fzt~XsVC8s=xq-O~9}nqN*Jx#>KQfGSCJQ3OIsj-qmz{IBdZXpl-p<L9<~L;2QQGU1{Fwq3V;R zuJC8-8BJ7}J?r=M4z7=D)tUxtA3!0wIX6l1RfW7me5I}=rCS)OIvk4PGv;k-uP}>5PPg+$`!B)Uv z2rQX`zkM~hKZO+sKOIYdat_*ROX(ZSey_*IMHAk%;3#~q_XJ;g-J&vgO%!VY`L4nT z=g}V@6Clqflv2%nRLcsr6!DdZt15VB(fLc$@nRn*;wODKfS)3wcb2=%rSaeys_r-e9AZ`p0#gwj@HR`mPr<)l!IWx1Dc2pWj zQrui3ViciKOAgLeVuoJ4TGHGp-Ea<-_lVx19ZF>Ov^Lc2-Fekg$0*}l5r(YW^jSANlT*sY;AS z(t07Af~I$h$u3#W&Y15rMM!Rq4j#dx^JkzzHe~Wpgqbw~6rs`Ab@QvshP~fFduDRF z;0MJZ*ofG0Gedpg<$&94^QCy}dp?E-J?yZ#Gj|-Cin4WuYD6PJpZd0Os|FG9816=aHg?Hv$JpzG|E|0+Ju>z zBoL~~V{&Q=O*23hxA8fPTj#$3!_L!KM#N^Z>0Kn|*3}$DharDRB&6*BoU~*~^cDv6$*#6&k!II6lL|xZtng;9E ztj3MkUyEz5x&k--^SXH@m0C-8tyud;kt$JPsyDZ9$g=O1phb@+EjYW$$OZT*Gi&ZOE;PzW? z!j0Emi!1*6NBs2}{(UYk=kC{TcT8w7&qHbL&pkNP^`ib|TW^DhKkD(=Yrp$r zt8KSKUH7nR#cf!5#|oTv+DTZs;&uS65#oia|KZm^$Mx4-g~$B+)A6?ddF3*9`47BDAp&Sf7^+?Ry4wl|O_qO_=y?|H;Vx4B&?g_%4$fF+$W+C5K4J5}po zr1nSIjBb1iT#o_160LdN1L{hA9T^zTwiq@InqiB!jj%kf!zN%b7ylDno@Q45}oOv?yUkI1ml+keF(-GAgBs zuFOSV2L(nNDYBTNX-Hu%j9GhMu{vt7nbJ$7@7M-4k&mje1>l_E%>a-SkP@M$(J=tA zQP6Jb)D#d^6^LpDM?Oi*Ww}K0a}N;#Eyy*1?*ZOndTdM7%_PR+DpYYs_q=TY#vlMo zceyui1cPG0lDbCafLn$we%H3Rbj3QfZ$^-$Fk zY);Z9N3=~4@UE60zVhggO`xv3E*sXL%nzG2hiOz9&|rSv`C`!(Rv#*K3ZY316=~W+ zF5V?(*;&X$&Y`OHotBS0T3SQP%`WfHe6jIQ7C26YaY>jUYhPZgn&p^N1d6s@-9wy{ zG-MDByabC3h%F0f)j4nm5t$G|tIrXtUX2(la5GH7)PyDkSmbyE>AhM6k|ByIxqx)l z(G!7P1&%EoyAJIY-*F_XRPQ~SMsu8d0-}O+a}gd9vf8;I?KfUY>W}&k0>L!rCWMF@ z^4^desz{cDuH;k-ZR z9DtnfNlY$vnc5M8I%=JJr)8ddiXliBIJGTbx29GW5W)@=jwaPLB5qGvAe@no!@y3- zj0h6}hJZrK?Ir*KAOJ~3K~!^Byx1Cr0`zE(<1!7R+-wt>dm3#F0o!Xf<5T0 zn_Q#hS2NS13!hlbNrE&`&M(H^`#k`sp7}{^wS2n;%*e)-v(G#gU;ol);7N#qBTe~2 zlEYOti4HY@s;Y6=5l_Xlj(!z(-ffQsy<>-+cEt`m?TV*7`)K^*nk(_SPka#H{rVTs z4hP-EpcCVW)r^O5@rB>UN8bB(yy8Dk-sC=RzU4AJ>jkgCCq8(pGW6XUP19tEc!TgI zKWDRQhF+Dgu>S)ef(IUS81B9I{@7~yHrQ_zrz?=P8=<;!%jQnaZi309`UFrV5ePn zU)Xy_%BqzsaOuSt;=5n}JbruWPgAh1ZdWMn6*>0Mot$(t=X;U0mC5wA*xw9b@siE( z$j3hohd=H}?6J@O8x-=~eB%xHzaM=c-}&0-aoO*FB^L|E)CzM-tdNC-3C)!IKG*Gi zg?$cu5cYe(LvioD_s8}-?TqEy?tor@EZs9Ry$-AHxE;6Nd?T*-^B?e=|NAk1@zWn* zFgpvBechPC$Qq{&+)B>jeh)qvkNo#%;{Fdi42za*zQO8unCIF*ue$~pU-%t-`%9n3 zwO3xAXh(ig{xmwB?kU!R-9XS(I3wUJ4KV}TY`-I3^13(Uup^!f=RM{tpZd^yamVeq zbq+TB^8@cV0S`U=2)yLACt}IwTi}Yn{0Xmq;ZeBxpV#NMa!J9hjQVVi9>Cp8$z`FC zl8u^|j~?_T{mpk_&b{ecdY{cZ>wBaWfphcysXMv$d2!ZghmgmF6Y)Z7aHf&Ab_)xR zPCJV4BhvWHOccxz%`+N2(lR0q{sz$aBwTL{0R-3U!S%<$UNq)#grY z#gyX}h-T20WA6x+lXf`DBAv)QqIp*(3Rv-`C{&^fG(eR6>dXN3>*#e`G=%&9^@@5RNEik4W#ar4a+T9U_JXz$`F`Ac%kjt}iK{e2vyg6nPLy zKJsQAP*tF+#?bZ9&=%;qA^Kq*9A-f}7J~>Znwo$IW0@CW!SzkR&jt-{y?qVpbu*~f z&S-S?pyvvq>5pT}rHk;G2iynmyLkc|if8M4TQUcb4Mw>6JGnA<(_F|sx1(#4hoDxvCy6u{MH>FDl zuO!}%wSW39=Rg!eMdWx=D*|%jJ1%=%qu(5!f6^Z4kCc7Dj>SPnC-`)DTl?B zoRtYQ;M=P_nl>OrT@#^*fKCo@HDYMsDzD>fhjc`WoUSf8&7cK&4^Lt?Z@KJK$1TFV z(tpx(ouRJ1RGcRq5s9=8(Xcv4vW~nj_;@`pG#5I@A!3Md6hP#`^#stY?yi!=x20v1 zc!}a8mju|laV^$&TZcm_Flt((^V~gp&UYHLfg(%FEanO~;t@VH-9N&kc7>i>u4PQgd?$d0f2Y3tXsPV-}>UG@VSq@A1iOa4FKaEXMF|- z9saltK6}oYr{UwLzf)2yk9R%y+z$7H;y!`lCZr#}y`I`N$t8=tt-wmj{S`{DX)|E_7HObe5bi(q?z zb1KMG^w4r{{)U%#ceT4&6X&8ctXp7MMt*Xk(UE8k$eX3~bD z_{XFD9DQkihtY6*pD_;k_k54Fpwo`|`pEf8$=b|WCutrT+#7=%n?NK7b`{+C7*O@V z+0RIPz?*<(*q~`z30&0%3kC_ef?})GIlTQIysJi!sBZ*0V~=7w{ji&x2%Ah zo&`t@ZJ;t$*Isg~=8h^X`CQRIT8)&uxHIh~hvkjkg zdp(b;*8?l?7Vs{htgfpR{7t4#H0Htwixpu?&!=nCz*-0f_=>%2>CQ@mr<;C`JDSy4 zoNs12o`aEf51y#5Dlm&q4HRc}P~k?JNwhFyo%mQ}3c*Z+j4sLFoC3IIjXbBbBv~Bk z*qCX*X^jo2!}e(630bwx8GBgYvv3XKyWIv5#gxV_Q(--KoJSKZ9soex3NUsgf&h-7 zID~VhAHMBbopYs&%~0J$h~d$uz;RGZ1y-SgOb;anFLD+LazQ7&llItrhqg`r`PN?% zW)ip!h{LtgF7H94nKFhmK$u1uFphLSDgE6&3)5V7S=YXNW49f5*wk`iRXM|mldY@M0emi z9VM+9`9@=^O+qKqR_%cH#^_espyi8sv~Esm*iyP0hH9aa+&a48qT5D>MMYQLSeQSFY0kYPI4_N}eFXrI{r6|#)o*$a#>U3) zmiO(XHUQ|4jpNWGj>4fw9ED$BauLox{Uls< z#btS?Ag_CC>iRa?`byXTPHK^nmjCCpo@o3gFC<96J1<8=Ul10Qk-{_Cx0V(V?T zze{aB;6V?=$Ikmc&U(*V@W21_;j}(Sfu#u+OCFdA*lPK9IOg>yQ#8<32(C+C-9zGQVbxH9|g8kEjIY`LLaH<{D!r524h$jGlX1*?P-p z-D$m|+*77;m|M{t3mrSS3N2(h^E=eJ#!tRbtjI=7u?Kni2zc+T;boGLT=J^ zaIQJCV?^{G+?z;a5o4=>cv|?gssg(j%nZ*A@(v9g z90;LWj2=&*b~hn#izSr+;-3u|D*~ft1`meGKb+KT9aW9l@fv&Fe^>l-d<=iRc@@U* zSPhyPfZ70>832PeYuty8Xu{h7um<&Ng8=mq)NEb@mbDrOpILs_i0d}qgs5ij4rmnk zkpnjkXw59*mRn-&eGbO>UoHa179na-Lsy9zdNYtQI;jhDyaE3os3O@kQPEbJmuC6JiB*WMw3(1JlM>CGH@p_m%sJ%&cc zOlgYcj(1*gjQ0etJ;Ja-@D(B|)Rw~0a1w+*P{G^EaM^^~O(q19wMq((ocCzkkYa;D zu^p)pfLsN{7BNH#5kMZOdO&OdWT2l{3q}sWTo~Qk=Tj`q$A_0X^6rBpz}+s9tx-D* zGw;z|=X1e%`FF=vl=2fLZYbY5UrEYQou8@{?sX=t=$F0LgbQ-fxTN9+>dg4a1Kb(l zqzO}6mB4v1@pHHyWmwV%WZ4onzsBS_I}3~@!cnEjQl)r5y>J+ukcP6FlxI&&BIbI^%8)Oa}mU-NS3&bSlOtCh@6{yf2Rf zAmdD#sa$#cZ8-akci?p=y=#--_vB~10H6Bk>4@!6qi1$41Uj7H0?X5%`(nKQq*L!! zKzqQ055-+XsK8w>%dKI4Y;#cEE zuR8H=Tc>s1!*R#I1CxuE;1g$_GIzZIaT)y%v+!fg0XJ^O#L z^|l)puAK1l=VFh2@3&xZ4nRN+2q}Vhk_d4E&<=!X>HM6TVrNiEt~j(u&Z^8*c8duB zIvvG4b3%4^S=RqR=4Z5hriCwVFQxsI{&gEa!Y8G>t+sS`QU83gLq?eZD*nxBK9%3| zd251M=sV19tUSik7ED47yTB5BYboL(>EeWF)4aK_j&T62Og$ zbV)(eEQKiP)dquz*bWey2K})b+iklACMRkMa>c;xFu(%MNy^`dM$=Q3pdeXEk)~23 zlC(%!OCj&)fEYwm-i82H|8@gU2BeJU30=-^hRj(2BLW81?wD&~!El$tSH%QrG95hN zD@nJiT2x8RC9VRN5>+(l4uTi7ABE5vFm0Zu8_P^#;w@5SX%E2FiP15j5!GbdeD-5# zL9qpe85tK}gIo>g3DMVZ1T@q~f45z5-OaaPF)zZ(#mn)}CSnmetQj<@nL%v}S~m?E zHUK#^zDB#tavZU25zbt(4)rZ}zz@}|eP#d*6^yqH(6k`^OqORc3NY7b?E=Qd2Rw~A zST&W!?q(B!DIqhBcI3>94qG$_JQTgle+?R}0bEa`aEs45tHwD536v;l(?sCYwz5Vm z?=^?LOhM@$k%A3s+at52F5?_0ffb~7;75lPa%SE~X$X0AgJ*TAPYTQiHECDC>(q3g zEe%_YXai*3DZO#3eX}Yv&_`PTW(v>hCWCTqrpyBbP?ao3WUV#HL|uZiA)QgN4Tz+^ z*xrjK+qv|9+tcJ-8bcG1ny}dQYH;f$*SV8O=bhLuED_#;sC88Zpx3qGYsA>7yC8#| z7wdo+k`S$`91y))3ONLJ@KkDtDS^&BE7{dcLOfb9Mpid=N@s?-B6A2LJY$_B3Sj3X zqWtVo&u^)V$btPB(5^!Q!TQefT$ytYt_R4DUf$66T=H8=EImh2SX7k3dH4C!O>7j~ zmW`OV9rIjFHB+Y7%s>_GknOev8Q#u608qf3KT&WFMMY^J=h~>ao&b8JUHSVwfs(K_!k zO>s7HlOrVy5e)c@9AD{svR1Jh)8Wsl|5k73C@#5UZBl2x3QnVZU#2nL``7n(uAb9a z@Op+j&6zfwnL)iis6T;}SqF0<(iw&94cFET;6qavsZhS$PLf;>H3%^PK0DY5>wZ+h72M z1#l%`gakTF>s}RRxXGO4%q`I{p!tzYt(@kN(@T+n50T`0XXo=4 z-~gc27*;GAI0ft}1#Yn%MJPrc3`g3S#0#1VkC6l_&O12YleWc(7;23)9>JkSYzJ`d zG}w8#9)PNrzHx)^{_hX4?-4^Z+cwy$zYNpk<5)W!!VT3#eb69IO#tLD>{WQdzio-H ztZUHTz6xW*A!yi0WcHb%L|%_ke9oIjg2e^Z{)GTHm<5`l2r!J8uxim|W2B)=5F~v# z1?gVar3|>-9U{;&XtIx)!=H-r^F9n-La6)WVs(IQehf&!#9C7bHcgAlJ5*IK(GzpG zh(RKvr@zg-Tr&h%nlM=xoRn8)rf!0#&Lo?yx2-`FRGzDI?U~r zY&|L9t}4kR&q341Nr1x0;wvuc>X0)@CBkHGP`Z90ZAuxOR9GiU;cmLX%8!W`Mz}9Ncn$RWg2IBRsa|AM1XF#?2 zB7kyLg?2aqM|0QE`HfNh=&L?BG%1=lIS~SK?1AE-IA4Jr38O@aBNsMM=0C@*GR||3 z%)^(FUAq{fU`XbatP!6JJB*3hxVFz%%Jx?#d2&G4?4dZFOCDhk_fYhsROqMnDM2PG zj2ewt5p+`;VdptLFYg75gg=5(fVdp;-X^@ubfpg(xt8iD^m=`~;&rFu@lScdoebOE zQ_lU^85j;`k}$$Mz)7r}Bv1u_<=gIv<4$=;P=101Xuj^PYI;wa3Iig zsln&Z`5<2V=1te4KlVva$M?T=9!!%4P%^&)uu*-zC*lH(mWLj41m6Ds&*AuEpMl|U zm;(0!#@uX7QHBnAO)p-uInMZ>Z~RNaxt0Fd7*0Lwvv|=_hv9~6uTMrBuj_u*iOrfJ~_STr?;$;mPF$7*DA;5gg&XlN3>2QY-;7 z-zHpuf~hu$3417jkj<(0Vv24&OPWze0xPyYTp<{$61Fv8Q3Y*maf5R7>&QSr4(ep% z(J?zVQ(t|VrRKBuF?kR>IM0BWU|H?3(d{Aalr&Q1>MHoaWR{^m$9f2gFv21FZ1xKx zP!eu9xJZbefmk8*`T{PSA;O@6ZyUI(2G-VCcg=OUF6Xg2N$1Vb+EXFdTy0 zR{GYV69kat_$CJlI+ivhr8OboyaHHEm)Tc9uMh6^z*Cdpi5_sk{s_O?6Yj>#(fSGm z*BNhLc}Y*kjK~rFel5|$!5Xz)^PGp6qHo7Gh>%JFdv-ez;OZ3Md+$1?*d026&VQK} znLt1_@R3ER!jaMH*dz_OX@yOQD(Y%PO-1KZj0%sc_6SbX!L^N8aYVD+&}io+4KsRO zX!^(llCG@0Deosk4n%~i*H`zFplQUi23xArRAf;sMMSVcATKEyVFCBdtULxp4k_iL z^A#c-LOXzSVt($N#lc`8MpRCMT}DR`kS?j(M0+h3MF=gNuar1B#K`azHHD%>)vM7A zB;G=RllTv1B>NnPK7&F&7@(iR(ywLkDV>eH(dA zas1EUL2W`6)oe$FO!;?NWzyvWlqW%1YVc_PIPn@;BhDHgx>)B444A|&1DMhAN~RF{ zI_E5!J10qPnu~HwM=Bc4H3tC8mT!muKIL5OvG;-V4b|ORnr4V^fA#ayBT}DpSNM== zyT}n<`{pw+J~4TZw+RqGnKz&IAw2&nhe^apBc>ya?)Sd_MZEkCZ`ovD_yZpFFicD= z!tBgCDCz6ou<=;o9xn$z_z`&h$?wP8-}q7?S_F{e?poVm=@JQh_JntS7~5{Y(}LR) zL%@%I@LgQ+t*_z_zxyR_UvUfC;Vh;WFTxHx?Tq^!_z)cNq-WsXd+fWQ`X;9q;Vtj| zIF5P7BN3x$hV77FC`i92!MT+!mu-#59r+xb_o)nvn%q>b&{!pj`lTL)worD%p6Gwa!81ugH~Vwz1e`7PdW zT})@N){{R*nC%hd)b+{lvI*tYPQ7yu?SgD@enA3S#s4IuMt~(lM6L3_u0QLyc5;-s`pFx3hV#?k$0j>J$R+VUWf~uWEkZA9-vn_^$ z25sBGJBO)7?Bi7tx}cS+R82qOO-FveHGJOPy~jrTDrkET#|0q4F%?Pv_ZUIhfC}`K_DwTs~a_ z_-S7frMNz6Z75IcdjQ6uTJBb(RSzvI9xVF~GgK3=S~f?3a{_K-KwwdWl5+~mO^cex zQ-aESi7XF?=m=bo!_rMfW^mhpgU7W$`zyxxtub@!05hv+F@_2hD{1h!23MWsYfbNUO#p@iX@3DyEznu+ zrvgWxefm2{!fTtx`r!UJcybcFWCA?iM_kg!aO*8WeUHiKyae&o6G6iPT-{IL9Ek9~ z2XZc1Y5Lny2ff~I3*00zz(-h0$zV`3`rvydd=mx4A#w)j+>hze`Jjn26O zA~AX?C|l6?BMNXw(wh6E5VH3K4V)}^zyuOKu$MgPtbYGQ0z8R~A;|l}riY=Ilcv|| z)hW{(5=aJ8AVdz9Vo+jRIA<`1ihU~9HL-0ZFq-q?ss*uZVFD-yFj!-+h#(}QhuAjo z3ewHuAgU0{lM~l02Y^(QZUMF75ZME*Xwt=BU-&~$4)BtCjq3_+(}I~0qSN4+TrAaW zeVo!PE3M%uX86umh;3At3K0l764$w?{gOFyAg4hyod>E42o^XpYQ_!+xV{98OQ)h) zZ055p?W|GsJ@@MzK0Ke#<;|lNsg(d_H-xUzfY+~pqw;Lc)}~#PcY7s|eD5};yi~p| z*IAgj6KmH@OKqkB#nT<2US23P0Kq^$zYXQU*uJsI-6dmS3nc9=`@8IrR#k%6^xBf~ zDH)EED;d2sQvE|8_E@~}Eg$LR7rrOUPk!)StXjE31FN`f+HVY&-uKW)JPrpu=#YO& zo2=}<$3A%U6OY7qzWVvJU`EG%dU_3h_QUgW*khl(N$=?O`gq{O4#yAA|C%xtCVoNp zMR!w~Ubhx2?^uB)n{9rVe3Bpigs0=@7hi-6zx73shvt3Um>M`OR_o$nk9`UreDI?e zT*Ke~@<*Kbnq%;{zg`9a8VFGs#+m79TzUDQaP^g!;VWPKI1YczQ}Eh1p0=QMz55>b z#gm@#LVWqNABEEcgwn>kYh`+R9j?Cea;&;z1=_a3;>|Y4jyvswEw)^?;fF`cp^tt% zzVOM9qyUgkiR@nc9f%jc^7y;d&Y9_HtXjDO6O&U|yyTvu1%#KMa4HsWw#9;<_}b^s z#p!Q90n^iKwFVPaGS;nGjX(VQ68!PkKgYReosJj3`i*$*F~=2}gY}nZz35nc=PRGb z^;cbym~jCAG69)bY~@431iqbPBw=N_FP*~)yyWvKf47I`??|aM92GuxZyc#AeWy89 zTkFy}RcQLlfA-#vkN_6B+YV{!^shXq=e|+L492u&Ymzx0?AC|!2u|87WoJpuk|QWF zv#f!^tew^%W`?VKpk5yhB)CyEIS${eFcW}bD;ggG=MZ!Drp0h5d43%+`aO?cuU752 zPtlSM3_}@n(PqayTE!ehFxr;Uv;p8C8BjC@5LnH@n`}-W+7{%1%{H6BSYI@S42fVI z8j1KG+7`8Us4Mj+)l4SN5$f8(I}h*8_f>%;Bt2UQ8rV($w%}V)HB#my8#pMChi1(w z19c&3+sgP02JnUNYB|`iQ3j-}n^b;#xJvQFC#NYlU+;VtZ6eh_($PyfA<% zDKZlN@RdY^@AYa8fR_EK)4DJqR}ikQ0$NfiT9CCAWKQXh_?koeD}b)z?CTx_Yug&- zIY@oG2__XPz2Id?CC@mx5&^_;h%n3`)xdYDtYdAbf&ECCwh);Kz7z9#?~=bYAOaQ_ zJ!0D;wnK@wuX{daWPn($fB-}ZJVzQ(&N)Vi4uMEa+BI+*L0oSF^)${x)IJCB0f9*@EkX;Y@dl#vD)0zmLQepxyN_2vm#=GG58@^S@8Ky5=VS*FA<-scPB8-5 zF(L;LIe1TKTN6k)FvR7+S2dbOg6L&e01blZYfx--ZCDCB2X?gx%{b&!ETQp4L9AKn z%X|>e9-E)z$#+usiF`%Kat7zAbQy%?P*f-}y0}t6T*fh0KWCg;9{wY(EZ^99GXYi5 z6le~dq=FXsGlO$uhVp^W<(oJ&3Pl@blNboK-JR|+$%`@-MF&f!$dIcIXelf!wddVp zgvJgxt40oP=RID0?3?hc=f7q_4DH=jF23k|lxloWMku=T_LOJ6^k3GdvOM!YUWRXd z`STLP#H31PVc^oA{9uy-IshE>h)3a~^S=fQY-3F5-BG63t;N^A@M-+)N8iKme)V$< zXJ-M(-1o=EaNqmg9}hnGa6IX#|JZ1u%&Xq`4*c|o=VNx=Y60mR31-SJdSBs%uYA*j zpS|o4zriujeiGKMU7aP>Dm8T)w4)+386^DZg0JF+e_V;vKk`*fPA%SG{V#ag8}Q99 zpNpoM0l+E3^`?}zZE*fK&cl~J_euQe_rFF6Z3ZZiLu8M=_ro!-IUWZ+;!z8(cklfU zL{-;_Z6lVF%Amadt!Hesx%Y4|i*J7EbNKOv-^Fh({TZg$txaw8RfWCxJpd1S_@OxR zIY(oQrCV)O-SZd1ZhPJr4}Z*)7W~9HAAAqade7Sus5HjGC9Q&7X8@rY;3MyT3vRpl zM!fdFPupPK-dA|ZYfi+8$NmSv1g8h{UD<~>-f$hh_x1Dee?Phq*I#=T?zrt%v~7dQ zMT=3@HP)?OQwaRB&$jiVTqAdORLV#82dCn?T`LdBIR|=KF-E#PA|XyD^Lpd_;9MCK z16Nvy(}Smjmqr?6f!IbZ+qwKKnV64aOvwOy**0Y!jKL7z*8#plAg5X${nUAh zdQNgOgU!*y$$9v(9zxv%h!C6yd5@MnW(UftY6qqVZK1hUU{Lv-PSgvS8Aib!K}~U` zsfC|)>bF_j5dyR14vrbr2B_9oO;3lz7VU7TTBL}vu?kaD6Pk{S1R!f4heHt{jQ2cx zeT}jXfa(gBCuMpi->@UmPziI+Xk$PK;!|!KOiOtdQ{M6jQ6RFWCd+`FocMh^O*JKa zi>+OGzN#FEywLt+X;yQH&`_k73C=*8Gexoj>|m+d zZ0#A7N69}=03S(PYsua06|`HLD`_7DsMj?RS|AL;u@QkuAW&6-LQd@l1s#zi;_OfW zjW@xL2YU~8Q;6$kfE%vI7#PEeaqw^mny%n)xf#ulJ79}N6ZlKRpy^ronOX5Sw_*X| z+Ez^2V+4i+1>qVP&ivCY%@ng(1;i-6imDZ6C^&_rw~F2&P=z)Gv~4BAuWc4%e79|| z=RP}Qmo26M5N>E%Ts=LDt-<&Q2-EkyKgKV<2!8rTu^b@DMBu#xM@FLpj?iW>E;-VJ z3N+LY+I#rALS0v4t>P^u8zsRgbI0bW02G<&S)Z~fOitu z&FiC_0>i~1=GQ5Wm+pHL#DYvX=HbT1weR9LZc)~=q}ON(0Bu*QG6|VHq7wi~?31tp zY9V8m9ijCIO@p>=;d{0494bs8UC?ZSu34PO$w^R50ub2C`$JS(BB|aYDhWg!W3mRQ zDlgVRLDH=;<@oSX0l6arsvbfkZW-hp*g>-)goG`sUX2h20H8U{H3VGb>_$TcMPR@* zzZNjq+;2mO@Rd`(optX8-P=xuEOJ&bd628c7u`U*jw|~<(5_1YpV32qlnuEvwq(BF zY^CNdnV7jad<{7A2uL=XY{IH}8kV+20a&{%v(-^tnR8FK@W`iCUvVt_AIzSqFzK=; zQl}>BRF)VcAWe9p+amxy2@(qFut;ApQrjpegU&>D_8~?l%2W~yVv3lhlxjxk<5fR$q0KfUgPw~57UW`wD z^gVdVaVO%)=PXhFL16V5v0B)s8GXKe5tOE%jAk9z#m@U5?$i%69G9XGaI z^Y_2ubuWJ|uDar{sjmi5l7I}1zg+eky!u5?!y8U|H=h2y4M&OxfPQ}ryWD#(T>huu zB>}=AkNJ1(a__x1s*M+&|4qE}#Mk2XTW`*7E85AS#h-qADgN}EOY!NCo`L5b^J*OZ zs{gu6(dikCBac3I!T101H^0C~-}jCT&dG#%%A6-as+vy7e)`I%&&GidJs1x^{D=*{ zkuV_P7E!Y7*$&rRDehdNLxU2=Q>RR#t|3>i^Q(c!y}_2M&&F0vR2sAmJx&ru6q&@zTdAjy08S23$6Nxw*n5T%1O#9H5+!0k}tbckZ!4; zZ0y=LV%P>vdnG2HT;wrNrt8W$OCKDnx`L|;O|)wCj24ODHJkir|M$FVLSPLX&Pn)^ zI5y5^%%O!$Qitct?=HcY@l+-N@ZJ^tA*aaqF^W%fGVfNu;Mhv`a}cRNtj-@vUSx6} zBr6LB?AX@;>Xa9oVmy?0uU&9>hPn{Brx{L}<)W5zTH0gS(`UWb{;;1JOchPZO= zG~(*D@FAc*{K;_tdj@#Qqw0@~#y2uzlyrijZ4)>bmjj6&FT#=vzN%1F9#vJL(x7ss zgx7>&Ss{mFU6dq`^71PL#rZ^HfIXZE+0@jYHA;Le)>YPcBY{1yZLqx(g9LgMUu#*I zlxVp$%vY6~UPr{X6~RD=5>cE8faP=7{&3Xn2u6d0&=_BVnB_U=RA?=lVUUwZ`OYJ@ zLxiRQm^JmC1^Z+1!A`U}nyWXwT2ln7#DtwATvT6f=Md@&&9Fg_VhUd8y+aI=FP=yu zv9qsIP(P=@pQ=7WI}pvdniq16Vpgtwsd}~S7Xo6GF?V1QI;rr>Ks10fHwCGIuN>k~ z=p1Zd_j}BY{5Z3C~#YbLW5aOSs`5S7#9E2%b!P zE}A1pxzG7rWnA#hFW^63{03~b^>!P4&tsnW41D9u=Weo~s=WU#&$^U%#bbR2nlQk7f z1~_hX?Bq;FJDkP;oOv29|Ksm*>PJ7l$$<$cCa3Vw!!`_>Q#;RkpB&`cHm3}`WzHdu zjtd~uJ&6H>&wS*43j*lQdpzoiPs67_ayp!X>5VPZ>(=6RFM0-kb;-qDnvYzceVPQ> z_CZIFS7=SsrIB|D&Cfn#K=&})0QOFxdA+~eyq7|P(RNV&EdK2LGiD?0_SDGvB?`t9 zmrdi5-Y4e8-dmiX?%ax6UGpv`M?#ls~CIryjT9&{}h-lR0-Z?^DIZRFVQP)+9HgB3V zry;1lqcjf!?!~k|sMas1ZL@sdYUZxvK}7I%RapBNW2yo;GgU7Dd9TEVJ^LFKXY{@Y;Ry>^0Mz6Jg`)8Iz~N#W*hS&3@hEd1@O!TkzcIiQlYQ8z98 z%n)wXT50Fj+k$tv4{+O6DldrOCZ|iY;Da$p9(Zz^e$9I^Ew4SQO117vlq`3+xi!fA zW&x1#(ZvEtOq%hqt~lszzZtzEK&&9VFVg6Vrr*|~@cmsBLj;f)t-D9pQp|)205pJ` z@TeewoR{=+EJ6%DX;-^Js@dxDeob zJvA{W5zesCAO{a}3^C3#yD98ZS`86Y0v5NRWv}(&`2dkZMIo7V(jj4l;Azi(DPHlqxBp9`f6rI0yY?z! zFcT32%D&nk_u6&Oe|_VMvfI7)f{iAREI>PqZurO5SiH}s0d(gacDv8s_|tDM&GSQ} zGraI}-e*3B&z*BtV*Y__{!Qr+v1pRW_YrXZ*FJ+C@3kwQ`NHEC^sX(oT!sfc^iW)S z@%Q01SLT9?uJ_(p9|u18kOe>X;|sr|z+B9nB|wKUj`@5m3735a3b^#A7vKp`ef|dD zv(J7H#FEXIV$I6i5I1r%hJ#tW=9p(?10H$^`U?!K*fc|2@}mo343Hzqt1O<3E}ef;7%4F_064UrTy5 zQ)uQ?QD*!>1i(2>cb4a|wLHeH>>`E)SPVvm{svawJ>h|P;~>B$E^Y1%bHzxhQc;1CH23^9O~ zb!a}s=C=)Upb8)sUotU^uQlLUKx7GMT2~&?k!bLufHbZuF-3PGq@q9m9)8dwZ1-=V z6?b6W^em>wCICmE!2mQf3!0gg+~>og27+tA)6*%d5t(l>6zn0VxB>w5#>B+F8pi-0 zgRPce|9yAHl0A0B3I@J5Jw&+fcGS1tfvI7Dn;nWt^Kb|n2B0r_tuu#CVtPLmFiRnz z-RFU*zV|nTu>c;*Xo>(w71vZxq=5v|1saNwA!;Bv%S-RTs459Y7g;s?WFVFRS7{&RtVk|s z&VgeE;&ueU6-#37rE+Byzpo#ncdnMmUmqz=+JSzgYx8Ojy2xry z(~BP~4ymP5Pt1+Uh784XwhcBTmrYHJg1T)?)&ylI(kAM5f;yMv zoB4k^B1|q?jFV3N2p)de$R$>zHC z=6_<3eeSo(@7QUV-SNlY{GuyVnM0g6Tvn}Ife*dst%^}dfuTxYZOz%*R4&>i_l9yl z`Qg*>$j3i@L4XDT9&pg1xa7wdpiPqJ4VSWKd+c)n>Rx}reLV0%2jO13?w&y|iD7dY zbTLQw%rCQjv1|d_b3gHR@f# zClXDu=eYR4yIDqDxXQ;n-!q_3CT?usiz>_K^qzF)bIAZus+WMd8P?{diCUTSqN(#W zqN}C~Cqae}M{K+V&=b?e@9_Rfr9y+789 z$jp7ttvaVJVn_W>-E;QNAu=Ok#roFpEs!WQ7FK&nTgxLI1+F{lJbzsvt=)S-%;j-M8KI zU-}o&jW@s^xe=4QpN~t~f+`I`3j)ZA6DUMrdm&zQBT-&bZ(9H-v$Bt;C|y)sovIq0 zn_#)t;2saW8`kb}K0bZJaV##q7Vg+hsI7tBZeg~UfR9Bd!LkDd54tgeZ5AQ~R2H=K z07vk=ku>&mZwq#7NIodu({acp^&oTHxC*Xvn%0p^{-F>U#*_^($_WrtG5Yi}ZzOER z(31@Zqbd@n6wE>-MCx@EQV>Q1gO@l?G=joaGIwHBX7<$(c=W1rHUeVEU7`^!d9TrY z@cg~e&oVqQ3P|Lh3Bd=+UDpT%sKJz@a&Zw$xOkc83`)J+A~HbTAO(Zab+9qQ)|KXh z%R5ggDzFzY43@Q#!_|EN90E*~`DBcfw201wVwCYz&>tg#wb;2aKulKGlY=EjG7{#G zC}2Ov2xw@09t&GJgf8gyk}4Qc2&-+@e|jvK%cId?N=Qh%%t02f5dg><^r__b8;VFw zNV%jSx*|JDF1o3uA_9$O+(4pbwUKs!+|l>DKy_aEdxxf&#r*0Xn~f5?DffiYoksmb zHkRh=>QUL?Klm(5=wUk` zhcBLx(uI}L%ZkxTm*jHX>wrYK<6Z87U;7__xaSbsf6KPm-bUZqof9ZOm~-{NL+|=# ztsN*Zm$Kif`I~+Aa}r;T`vvGyOyV@!9*<0ri^z03ZNKL_t)$ z$tkqEe9OJ@f%pAy@p|p!ytlsP^}S9-fHh(Wv8HD_-6@47zxJ}Pei_T9d&Cj(q~ zr@QXCubT%C;W@wXqCFpfD$P2FJKgmj`0S@Xwr68gZh!Ql0`%?lPMw@1eS8mNkM$j{N${N7-vhLtg&Ed z=`Sr)7Ng{2Iw$KagjhV)QIE^ztvE$EXEB?Jw~mP*#MeQeO<$TuhsVqs(xs~f@X7`x-u=T8m zF+=wkIiD2*QQt8ENnyA~R_;wyqHW6j&hvm}PNmd)Oj+}ndv9X~J-G~4_6!>RzF#Op zJm+MdYl;CUk6gZ&6*z&cFoQwDuw_md4f)Srd=F%DXR_dAkhGMRyaNNuWOt<33SQW~ zClkp*?pkmc!A|mDiwE?|uftw<9rzCC;I!|22#zi~AVpB;LCaP==w7^enTcD4 z5mtbXJk|M}ZW1Xaj1do^v z5QCHwDp6uNx+JfIKI*5eg$#}vT540CE9$Wzl(LGT31Wx{T?gNF;1qQpCnfL{d14F$ zY7~_8YSSC|D*@%HT_AJ}SJ#>{C($dcCHrLV=6S<#lt9iR1cQVKn)!$yPSM#qni z8spY-qW(K1!!9~R2I+$nRsr?!GPn4}K(7Qt_zHIPbNBAL!fP-}dJu5gPunrV(&LBMQi9dqpzv$(A4lVt+Zozjd zSg3!SgZGt%V7uJ@_v#`5V6nZWePIPJGD3NLMRD_|bdQ6FPs?hz%!3kJe9|euYx{@4 z`x_M?R7#GC<$otr?Z~PH^qdPIeDp(qyJvuY!R_t{K+6B@2cYLCJLlXB_I}bgxt)9d z?Nsqx1=A;Q*Ij!RzH-SIz)(+g$&06cewPI+EwATne7p9lD{%gWceurSSt~9JB7+NV zf2Tb^*FXH-dyAm4hV*9zoMR-nM@^#42p9kT|3Oj<^)0obd^-EQ+wQrq-|Tko1sCEo zpZw^)0Qyyze;xn)iI1Xq&5aUhDG*rQD6^tfBzuoF@@_ELu*Tqd$4)l*U10}A85n?& z(Y^Bq-+SHj&3hX$4+ejgtR{#0<$kV&u&w;t%k=*Gx!bI7Cxhnl&8nuMpOa{ZU^fA5 zjYvkoVHaU42e-ZkwiVh4bb`#4+bV$()Vt_ZPH`y_mP43h6wkC*0-?ID0TK^%=QY0- z+50BSp|y;rc9=}2QkE?NJ|_6ii$^M{k({H1a}&cgHLAMNut-K_#5lxE2wK)mj0cR) zM|7=6*Lj2>Vep0+b!{c`h)9)GfT&UuQ+fL_vDQbHJ;10JblFLnBkKTT9b#g%A!$ju z1V+TPwtx{ZESKrarUeb@4KOU`NV)W^>E(&4t5DMim~%aC38!W@ zLdXXOLIU{hD5|f_f!WPS3~+ zr2{G}A;RcjstPa`DF(!LA@9mnNc|Gc5TbLc+f>|mHd}OJy-YG-8&%}Bxz`gCn zKmdTNO8UVOumlPn7*-j#9o$*xqWj_}kcrQd8aZrL!B$T4%rzQ>6Qhfva197=L&T6t zKhGBs^^D#CIF}GB`nt-`)a$9etQyEp?u|TPAO`Ua4{Mc^?i2m}^!`Ms>Kd-95MxB> zg63WmMi`~Y*TJ5`8C>;fVrV2LlkY20B1u6eAY3ffq=9QH_*MwuSdA!b5)e^5`^l(? zK>ryTjpUh35xNesYk{iK=pGV6G>udsqoL}=fU&S90oI`P1~CG)jj-A$c2cggh5%k; z2oe-8@%h#WK#wdz|Aqv>ldF+JD@G3pd8f5?(dS)NN&dW1qD*5IizQ7*9I$y~u>XI}#p!XhIQ*@Ew^HSk@4C5sU_g9Ru78iA6DONqpbj?|qT?A}ZpV#ixNf4H}ny_Wm^ zeh2o*%E17*mmB40qY}1HSUF^L(}6}b#2~wMbD%R2)PugFHJckALcqp5zt$`?UGm`e1NY zi0`QX{hF&T`{s9U(5|}T>pG5H#!iG}ADytazIjUbIC%K6;RELGtS;y=so~^?$e(r za%>pVJ_6^1HP{;_Q6wfYH%@dbGI{*Z@kg?!q3bwU)#bZM90z#St8(>!>GGWn% z0v1CM=4)41s49n$fDk>JO8y3CP@d3s0bT0>FzTj)wGN5Jqb&J^4DXYAl^B)`CUYs% zl)dv^r=T@@a2zPgDZ)65dRoIeA*f?!G!CdM4a4^lF?48}8bl<7YcZj1g~6KuRF%#x zE$NoMyj7))!AU_Q1l&3U7-gv*4x_8g4o38t5>l6tQov$Czr<^4b!Ql}NrTD>FwYSRIn2E$kyG#=QpwW>v$8yE6of`t4D|z@tjY?TG&Fqmy+lXlD8`h$~lX~ zR_`q&0@=e3s*od;GQpfYmxK~C6gZbfqMk`BjOlU>gM?ZQB}t6Lb{%2sCh@Qi9 zfH!L;ib0J;Q4L8kO1^m>pImlrEVJ|{uU#q{`aqUZA;s-wI91}oF#ZJ;6aIOc>G8d3>kAZ6@NWMc1EkLyr#cA|_R;wuK0*Q(~ z($ofB$M6v#6JSXU-fSiM$03#I8(1Uq(O7Wmjfutzd2lq4)TboJ_~la4dz=GQUM1%c zy%)n8%YFooU={+sJRYq5rSiNeGRay8A3HEh3XC-AAUD5)ePoxzfJ4F^)gnE05m5xFAj#(_8YRS51x0_ z%6(R!BY&jt=Dge94ljSrn{cPQ-hG#=oLal;ipy~JZO*&Z=N~+DxPP{RGPgRxF8TbY zZha@e0qtKt^GVsD(T^n{1{QHTGY%X)bV~PF4QHm^Cf6;rn~ojT{6-X@kXhO@`tKtu zM*1&S=RBu`V@HqR@E-I3=d(4O*e^iWWztOl?}+Hwt*xzZ%A8EMd~9wv9=RR>2J>YQ z?J|%?Irny~T*^@FHD_9{1SZ04zP{%(j@)pqynnUYmx!KWzgO;|4=~V3#H@Do`fK(M z&>4UMCjS?Y`TE9Y;XT@G`|=n51wjA)3VZKhVePs!6qJ_eiF^@Y#NrAJK?CpDuy}muCLKi_P zAi-dIXcM-XqDxRwe;`4k`^ zs5d>vh!{k<2NIkGCXIN6j5SEG@R1R`kUVkJ{Lv_k#LgMG>0AiwSe!FWMW`#8U?DO( zA7I#HGMNBC$?FbYfS9pDDkY5H6(UQxx3z@Inyi|uMb_yUSRJeJnC6!qO1UR3(H0_{ zk#JEE;ieM_h92&i&-G?P6^lCxiHeQ7zJ)u6zIwcZEmLkJO_5Ay!9 zr#8=a7UMG^gLHiY=CUS+>SUg)l;;kBp5#tDoaI{v??mPM5+bavkckcMlT#3QaOd&z z)e#|A_|vkoDAI1km=D0OrL4*jh@+c??o&t?2GX0H%8VVkB&xxCwWB z{DaZl{~mbT^~cfNa3iMM9n7K?@a#P(Xx;Qoyd0x||F#3XFXILuz)Tag%0WWL77zJqn0Lhci0J?<3qj%c4s)lvkR~{(df1PDz&QrhsjnVZN z^}Y;B5ecyvH8>@3CU8^`O(ek6%q*(9M!Q@hw5{Z&n+VCdCoo4C%SuEnQ6W8nT#Xb6 zvFm`kh9zj^kSWFkxtGpkN-)-8(pW5(jMgViwCcGv1|b=Qm{4ck1q8MZF$P#8X&x~N zDO`?ROl%ROm;7_<&@Pskt<6DXU@ajgt0_oOUI(eRVN#Hic6o=)hAHHzn=A`NHfL6M z>ulL8ZDrwGFJbP+fKuMSb!<7QVMXU52yAPKF3HA=6>vV1d82**WO#A+R@QVrZ-}Q? zfcomiIU6WI7<1`ly<_M_=N;b;MCM9gd35WcPl1pDdU*@uaK4*=a4P);RQWZ7e~Ut# zmAJu!WY7l`l8LMbms_t4LRReZSCr2+07u}V4}TP1{2RZA^^H^W?(Wt;@Vy zyAn?^J>cK6O(rwV-2(U|GGMhWm)lUk%S!Fuq1BC$ErmN`a+u+Lx2Jmz#b;K8_tM&B zTLhjN&XpCRIlxvYz5BlS7f3D@Lf98E;5V~1O6;|tcH_|-%7$IuA!X@~orcu>QQqVB zv2aQr;@~VAP6PnV{RgtdcggV@UEt=sursGU`)l;yVz3!rOg3(r?mNLb4}Unr4gH1z zMuMrQKs^P+AcP3tMPUJ-uVcPGMmwRv_gV-o*V(Lwklh4Xtins z*|S?pbg}04XNWj3&Xl<%Y{RU%oQ4DsZ7+l5ii3*+Syjq_J)CeW^T%1KlMZBXq@XM@ zI0bm0L*?Z;Q<9L^eBIGGSwa#a!_v{b&U#Xwb*emNP9H)JA%&JO9f10=1*+FK2Nv>S z9&&F(S~YFyoE_-WCB&HeTvdhT`f=~k&&CZo#`GJtVQ})o?o0usW!B`5Vt`?2&1rDS z@0F6U>etNjod6;W+st6>6458X0{+O2IQ`glNNz4U@mt%V6DI^zgOJI*1UwNad3g_H zkc`9e8*jw-{N$7H@e9twkuP6~`LQk7;|uYI6Dd1;5U?MDj7cs%uvkd0ds6V81w-_p z(8@C=FJ%wQ{+Ynj3P?aio+Ek3l`+rYl~bR>pQAD)g+MPwJxN;`$pWP1+JWN;uxBGr zG4`N0^7Mbl0f2Ia%)*1M=bT<%5+rOo1Ldf5w_3;2hS$k+o(G5T z>_7qMBl+4}0!9w^-Bc}F`IY)ExqPj1Z_0h$)Kj|Ot!W?ryT8G)8;_JR%mu_vnSzWn&b$pa z4<0_H`}nRaN`{KKeP83?A?*XH@h207W8e`OZ8E@)#Wg?^2M(RK=X-B&o!A#RSKSaq zgzfDU`#ABN+g#;};;T>#o)G=<|-*`wL1;V?(M|!n+ET3 z;Js5Ilm?sAXdacnhhK~jT0Lb5aCZ#{L%%KU_#6WubJ9ER-il`i8JG{BH(zDg&%B8b z`Zxgk8GkWU2gpQQ%phxFCL2iP1hD!}l)XuV`T7i|ZqNnB)}qy1QbGt3goO3=DXPk$ z3lZK2%%>GgH-nX+jPS#;cTP({8J%HXLgj^RU1tCh zkP>3qPYBi$tTM7QGHX7Nl7!-iAn5^Rb`W5lg{_=;H+@imAf=o#0bib>Vsz-|U=Olo zGKqM~!aC2hUajpbW^uo26vWDPvN3HSn|03SYsh@pP*h#xV?{IrHy-V=;yD*!l(kaU zi7%e^VL#N9OG-v(Bovo3MY@sja8cehJT@`d5Qro#gHq}l?U{tAPE#n!5U8HP7!Py{ zREBW<-+u%f_kTG2e1htRBLbS29!2nH#(b9f3uBO|!ttw*;;H}nNx1k9x5bf5uEg}l z6EMdYprsm^y%?(yypX{K4~hxAY=!L2i+5fr|D`MzLC6532dIHra{riQB=eA|jQJtd@UBm_XrFbt3dhM_3dIV1u&v|!hOlhM++R*VZA zA&g0SEj3)!jYHcqx-Ot;q|{{P2yL*SAV$*&23dm?f&y-Wk77KnD=5G=h{=IjxJZ24 z!c|fZ&ggguRu<2Dib={6rcRmI^9t2>n%9!<@A+@*EMf>CQ;U-ZB2&Ai*D4D7uF2?2 zlA7)rM&`K&Q-j0yV1LasvH#^H_;@70PX2rU{{quj!03?tvgi&&mwN%Ky%|6voAiMG zp)f?cJu%|$TFj^$18fTnLfm^LW)QYMJT-d_`gq22NM>o*s0J8|;zBi&m~1)9{A+-Nu1Kn9bMrbYJf~9J}%8 zSgF}llQXWn`iecDas72yLFd)Rq)#s>Ls{e8LIE+}F@khyp) z^2Jt)b`3=u#>;=-c7S}|4cA?b_07HIcHic_3vlBN*Oqc+JHR=hh69vmABI@xW}L&B z=bpdM&JwP<^0GarPkiJ9e~b6O=bhTO-N$>kcSfs!oaDt0mjwa1^pY>`JpUG&)CJ(L)CG)GPG;EPq})Us_2I#(h&@&;E7D61ZBj6nMEUNkr^Z>0UES}@G6jWFD3 z=_D^{uwQAuR(_)0zhrN{B@fXW1%b@~RHNh1VTs-Sm1AantS7}gM) zNoHnesh`V0=M&n6kSJA3$Zd>)scJA8bb&z>us(4pIlCbEl+d*a1`%s>ho-8emUv_= z+XzaM2M=QbS0gY1iBTCsRf%^sk8Q{$kw}3x5mJosK@|I3W!XZ|TIC7?RKYUp zZHJyO9x%eP^&k=grn+)4PQro{GuCDe8qKi;P{_)B%0>XzP+}A>zLY2naMq!!ZJAU) zBupj^no0Ritk|WPQ*>F=YiwASL zKy(7s1+9uWY5w+bK2y2waN&?%GXuA=-k6tllqHfG-iyMkK+G60a!qtw6iR0KTyoJP z$Yyz<-dMImNLRw%sjN3<7N&M%K+Ob`knpvS{3FhNz(eq_vl@0|4s+89xn5NNbFQ_S z1XyqYPF#CE9`l?h;EHd#Bfj|Mt1&-z0<^Uh&<$w*x(C`;o+S!EM-+lQgaGoLly%eb zB1oTe7(9{Yt0#RQUjC&3aI1zEg68e^?^xDE9_tltfl$CX^4N3cIuKAU1b`CeosZ8G zB&+M$ND1^ZG4G=#Y9&QU*@l307+q`JhmdEmp8HYwj=;qz&YX%72dZ0?c%`i45dp@+ zSq+(2UI!x0`!)b+$wN+(x6N8ZdWa#Ild!cXhSEmL=PAM{IUB5jnNC60A}p5@tq`N+ zltZ{CASLUC98UKqQl|LOF6DNWQ&Kr6A?BQrDomd#5=oFE00%ZXqFRhH zR>$283@cW>(vLV5OjUACN7ULacEHkG?hW1KI{#@j$ z))m&;oP9oUkb06d_LKf{zxzJ~FL~J?;LzbS@QrR)UjB7F=f|Fc zFMjTy0f>ioK0kF2*mphrQTWjN-qkbP58u(?6@2x}pT}Rn{g3dd$3Nqg@446Z-gmtf zpZV0sFbw1DhpX2xANbHmp7On}zxJ8|GV0rYKmq`K+XKG~fAtq{9-ZIMVD3KzM#_Cr zqV?eWKHwpHzULKR|ElEw(0+R>_q+USU*7ZKRaN8dfAU5EJM9hmf--lwFAp?=LH${@ za$de>UT0a^yHzb4?})~0z5spNA$TwZZ2P!zzRu-e{R(b-hr8_QocllMyYRVBe|&Te z8vZ^)cSYCfA3!aicfQ-b_D}f6jLR)Q7b0r)AMB?{d8L3`4vz_@2i9+i}G_Q}g+Go|HT0u5&-1SGTW|1!MGf>!G)A zaDkOqIhrFY2GrFni-_dDzXOmi|M1E%N&oH0fYGo303ZNKL_t*ArsSGOAaorP4BXl} z$XU$h6C?n~w>;XmL**VCf^euEVUcnAQ>!?tQ>^8_;zD%hsc7JG5;F z2jHADHqlHc2uT>AeHXC3-6DjD+7X93}QscL~Ep; zZ9^pGku;aLu7xzt2VsC`Eq|7cZlbjJM#KW#20I0-R~9)ZGXa!rQHhe~izb#3)-LAU z`B-wyqvrZ2WzEiEvp$OPB9}%dB6N|FQYehpPV+}CfT|WDl)+B{--ByMWmabvWjDs` zDc7}FYD`N?1Lbv<%N`YAByXVCPZca=>5@H0MfDp9_`yQT^F7M|>4zD71HEq#>cn`a z`O(Y*a;i{rSbIrh6cl`^i`qiJ$vhoYLQEZ3|LE^8Ee5D5nxe6K{OFe)gZP@kag(VrB?Q^3e;Ij zuXQbKJw@^fku3-jhFJ|9DDR8W|85M4%pvijhb|zJq%<)zTxHRz;+o5HTKP$R+{_|uym`dZ^$i4_s(teX6 zXy}aoy^r?jCp-=Bdh46}sWiki#p{+8{jnh^TLzhc?|$r)_I%IJ{?jMMYkD7z0Q}28 zeQeLiZ)_gK8E2h?Yp=Odte3momqzbT+U`rfKc-~A<~^H$Q6BH;=)JVv$E^n}LqW76 zP51FKhe!U)r!U5X9{#93o%cPDdm`TOnpYHMPDC0NMX78R1##wRi}5>uV=e`cx z=RWl@Jo0f*-qW#ny67GwL&sbk`RKLgH0E5t<%2`$ZU-%y@N`6I2{%o>W$ zIZs!j5-3I(_F$IKQ>hDYM9(k=#yK!qEPX6QWhr=Je2)p<2l2c)2iOW=hGmh@*5ot-1EN75YgOi6)QEx4Quy6*U9giGoPOm z96%Jyo>TV42$k^x3=1r_!E0-mZMbqGkFrD?*J0Q?wYA^?GWIqy4)khP$D467)azyfU6yv`4rnXZGom0DoSV^ zuxJfzh^U;Djf1r!)|g?9(ejWe4pR$@5Gt7D5krLUTGW%N97|$ojhzQaF9y~KL~C_@ z_0iO0kag z0q1}I=GWtyPkwX{&PgHxoB^)8`tno0{(*ys@yJI%UWExjhI`9?kB>kAwvHdeOP==( zoH%~$l<#|UZR^Bw{M@sijH5T)Q0^@rekw*;RxSLIkA3nfmskUU%fEK%=w6V^V;DxY zmv+zlJ^3a*UquIACbw~IggcY8ke!aLj<-}eJgk!}?LHMA7! z{3uG#s-OkVz3uJsJx|ylK+kIICqDAQJ)d)@i|&qd&bvU?hAI+Hf9&9QM}*24RK~y= z9YaI7@OF2=`4?QMe;XN312{cSe$QiWP#YO{YiRY6PySr1!&Y8bFYD32&XfL|-%~JT&+jqV1t97};TVxm=bbyY*Q!MAbmphaB&GfGhw+F{w5VB9M5* zgO5_rylTJ%tgTNGBjfl90q4dL0!xnN`q~teroxHs7AZumO)E@lhm?S27qGSHu-IT)=Sh zP>d1N*#y>AST0-48jC|4GZ-Q)wp-K=n9dsbC?$iKBj&RjRVA$5Aw;yx4k<=hYf(>U z@a(YI_6Ti<(01@$E1}&o5^QVEB;9jTA7Dlp%_V z5EIY^gQX|59_rm!M)#DEqDKf33hU8AlnCQXIf890*A)2=1s$sYfxm%PY*Q{YJ^hs$Jr$c7LWwsOuP!kHbNRp+o4wrXIi z2IOjh#B(0mC@@?AX$yuIawqHHoJCz*)RpdGMgeD4gUNxZLaD(D28RF;&|Y&ry7#{m zYleYC>p##-T+AQ-fbm6%F@kABRjelDR`t5iVj!2?P?gvUgpe9k2FZ-;LKJZdm{pR z5NWMN-Bf5M4eF+mGOf;)TzjJ+(pXs|##p2vbCaOyyv4v3N*{;}>d6$%d=8_;ZKV%` zA8-zbR`N7gBX}>7Kb&B#fz$Pt*A*bfo6xnWEihf56_QCq2F@BRJ7{T5@swL921R3K zzYj?W@PxS6psGbilC9A#J7tm=P7GrR)ucx37IG~s(L;$1hU9vgON_xXKeH1-r<8FO zz(RzX4S80LvdD%bKnzZKwhoNiz3_7wPvG(Xyb2J8r>?328D{nuLa~GO0H4ad8>4W_ zOJ$`mV~vD&{8sL~voqtajQ(A1S#2-^k}&q#6kyYD8Z;z*Guf((0b~348vP6Eg?cUr znIm}$U?2Rx^Q}s|=x+DHAN~11;-L?F>`5N!*0&Hme)R=EhZjBnr-fmh`r6u7NocOS z>Z)(Zy`J(TKczJT(br@iy#L&YfUjNhIlSMbPThOITif0`f#?0y58*3c`a%K7 zJ=;EjGMO1f7Ek!$XMIENd&Sqk0-#?M#ePaBpRgxuzx1-#;GFYrE6N=62&9ie;yARG z0USK-biCkYzq{vqb?p)#df#6Gc^&MtuYf%B```W6JsaOm$jr=k^ur%wJ#m+GVz&js!#loDo_rVL?d6JIZ(BeC~ zY$wbKOe25waARsx003-odF$6>(&@Mfeix$hp63a!4cG;p^wi5R56MSI! z0B|By%>>i=470TvHa6#2-eA45bnn_;rPhRL+X`qZJa zrsT)Btw+0Tv0QZ6-fFSE-J)$he2yewM%#I`ogBMddUTz~V%ch)bVf*wtz|$LSiD>e zfJ{kCV4|WrMb3O=k{=(u5D1Df0x-5l(=?dPrkJm-VPkV02M%uHz`=EFY_4JR&?eRn ztYhuKCZ_8fn5?a#nN3hnCa_IY3;_U_dhRKP68Vs#5{{*Q9P{Go6=R0fUY29i3TPKy zQ=%8FbpScUul?V%(PI2Vo z&tT&#p96JE@PR3ibHbx|y4&lI;=xb+UVMBzpuX`q+_4idC$>RttH&lO59Na}wMRWS z1Oel%0)Ec9=AE##BY@ch@Bn%F%!-Vl$^f;6=5qk^IbbUZXZD)Q%tA7jf3lZ)v}>#P zx;J`e9wCCMYE-otWsG&|h?BfwYc)sNiQF;Pihkj&f>=|$-_|))b%lD;pqWllPbaXI zgCW+w4WwiN>#pR(gC&|KdNF!sZ*n%iT0NFvLFjY40p)v$sV~140%8z3kaA>+{)hpY z)ZkFTRyBg}ioRmB)SGn{RZ{~*Q=^7*J5V|F-B|T0!DDrq@B%Gl(fz_xcPiKu$k9kz%9bhZl69Q(! zOC^ZQz*?uOat;t+>V7{Sd|x)-)g5=BI%M_Z=Cs#-N@j!lfS+W1WPcSJ{y8Mo9C#zL zJ~8-zIhlK>K(8I2TuKMMi~sRMWqM`i{bI3VDu$Ft|KQVc{)MMjUJU@Qx#|jBbM+O#V49Eq-6vSvJa7oV z{rb1#ju+jdlnBe0q4K}}!ui=QxZR!bir2jjXPkZRp6~TnZ~qf)ow#XGwO}8eWqo1> z-t+c1?YZ{%nP=YyKmXreULJ_7Y$(=J0bc^ZOaXI5gbVL*SN!D9|91eugZ{&#@dtnL z5j^v`zl8aGU9%WuBgnu`=6OHw_S1Ofm$40UC~#<1bd6-io*L!tj`7Yve#4$mo6Xno z%0K)I-2Hw^C|PQJ45wUAE|l9DXP=ATc;h>8;hiqpUq4HCTei{R! z1eU*t%GgPc$L`>ENEBDXdNJOBSB;mM$Y_{9p8;`y%wVG*8tY25*w852gX!Hi_>sY2 z_$)*F01dw=YB9hGwyw}@9z<+8kQwun6JC6`@onF8=i)~NX+G>FqN|c(j3~NLmFxH@{D+ysY4sDRZO%!IU#8?(pRiUmcOr{NHvkB&FbIjLfSX*Di#(@oN96W%{ z!-uhX_%JpO9mLwfgP3n@V6ryDbZv%uI)$q%kabAp)T^J0H#BNlKQ1i$3W&0|J*M(E zDbR2l#+*e`}dDpZpOrfxuuL^;rOhQ&W$ zjOo`efxGcK;KUJ7w+)H`sq?4~t>MVMzXivSoj`rVjliN654;!hH~3h*ATOF{i zKg$;Aq!NN;L8k(&IO*JvQFJ_Olw^)Zf-KM1T=gJio`ck<3CR9odO&Y7Te}5W36HPp z3a)A3np#Vt@(6UxPiE_vn%g-|BZeTX>;q4Hj>rYjM|3JE?|&psD0CgVaGvB&6~<)IP5qM6Q6O==i|^n2Bt&Pf5S z$ThMxf)_buAo8%PtFkxA{>MNH@NI`lZQvRK>77q-PKZDyLR<-3vr@{8%B~Pf8sW{z z?|r#!bzJ3nC32-g=sF+FfSY>Ii@73!lSNzVE^K*oQwT$|?jbTSFRBtP@x*`k(&z zhrc2Beb#e-8ME1%46GJ5Eyj={#58Ek5U#rX5Z`%nxEL?i})d*1Isc=_-BC1&%!@<3HL zc*>9cEdJo_AIBpe`&3c>v(cc~+4Xl`C6(L5vkm?oNQ;vmm)pMIB4@id@Qy$J1022n z+C86o+8JlzRd0SLe(oi&#M$RvukURHxzBgTwS%D;u0DC$ip$ zPf?F8s{UcA}PS~RtAyS6@Jv1k#!kW>c2Ms1zCHs$@IIkkdeqJo1A_Ol%z+ppTxHQlV7aw~hNqZ7kk2W5VUu{_LI?=HLkb;Oe^T%Q^bBJ~P8uS5cQ-h8534k0u ztzp1m5UJro=SqyMCHFkMmuKm^03Q<)MCN2;7J~~hA1mX&=x)PE$SjWtWF`OIDe$Q# zQ#7*~ChHrRZEj-izy{`<2e5YF5ayc)Fx}Whv%Z0Pz798;qG}pgjZR7#oTVtt+u5s{ zvfL7|PR!tB5u=b*iC2B8d;$e`@g9I8nTxCy$>AYFGA?C~S8 z;W)5;6nw+wSbXU-c;GX>AOCc83)8D_5JQ180>Bf4VgPZFQfb+1o`JddK-;NG%!0Bh zd+Q>E5G_Hi4g&JK0B}a4252L&#h@?)A36`|ybBTTa1q)Uyb|s6UWxX(zk&9rUJC#C zpF#MJMKUxVWUsrE`7mkVno7K~8f7s=TE;jSTZ#NvtPn#KqNf)G5iLAaOQnkM0-)_4Y%Ad)qp5hK}nVT?spPf$%K z5+={tz{I`LNs(i(O|@HtZ2vVt>sIr)MN6vn^e@#ldO zm48>$i(F^;R)>tveLwDbzwf~FUie$G7{9^oeeZrJe(||Kj>Xni8Hfy!ZT4mZqj&J> zi$8+ze#{S^^1aVE>o$1yFT4!D`I6@V=oQc5d(D<@O8_N8OfBB{JHLu|{Q2wgq^Ca{ zk9_P?_mvO6y>$Zb`QLwrH@)t+artFmE#pWKV~ov4)uCdo1Npoc{T2@HqfYqEwNHNR zLy))K6HxR|ed}A@G7skyD*l2$dhM%l)fHbW#w)9l4|{HEZp+Kyjlc6!Jm_KHzvuevkNv@?<9-i#Fy8Wp z|A7y`|6M{_IG8iWINbd{_s63j{|tQR!ya?e5`BkHKNHV;$*b|$Cp{CddHFBmbD#cr zPjCeth5e$nffaLfq1}yz{e4cU5tfSuUj5P+;DxXF{XL&$t;1uU@}qe4lb?aFe(4MN z%*7wZ4cA?Rn~vUq>1>Wer=N*C-~B$g>%H!`?~wL!@6gLR0zwFQ<%^$(Uw`eN?dklw znc%;^^i}xYA9ynU^!2}kPk!Ws{LdZV001BWNkl% z+;b4)w8I-XxH-q>+7x66i$y1S=dBRKO)4v%?ZoIpLgynk)(p-(JjanOVL=B+B$gaP zf`B(4BGxx%sGAzgHlpi1V%OpH(>DMjbTPpPuK>70Qa~7jfJW~Frn4H$WU)B0kh;TG zy=nTgCKJhf4T-R|3bW#E}VRP~*q z#)4jN%-<7Vos(t~dg_wUAu!sDL*`$su5XI`ycB0P-5* z?(>f_yhC&CDS}Lr;H{h^#}F?qB$OHnQeMr};CdZ=)_F*09!8orfR!{49NYv9fh{53 z_8gGCAIKU^!!yxb{Eygr*V}-bu7#OaTE2?VyP-2toX9;cQm`){)kkyY^LB|;D$aqv zW;PjuRl>Ezl5!~PTXMXUmipolkUgwLE|UcjYko8fhaMRz-$`8hlIIE+@MM*(Un4Pe z-ZST#^sJ>6CCuAe6n1;pG$3n{LRTUaX|S;n0px^jJ#l_NDtVF=Witl)EC#M>z$`@s zQdD+#0w@7eT27251Yw>Jo?*LA-kWu>&Vo5fgq5yCYY8EC=oSf0GlA~{Y%Rt;BA^X& zZL5P<%9~k(z>*r%SP4P5u0r$?&ed3KZDH=JGM2_l)XZ{g3x2skJ)I#MAh1QnVr&rI zS3rCAq>_fza}vTp>ncefU@PvX3g>K?sy zhjQJveDi@g9$GE3f|Iy}PC`BA9X_K7(IYopiz_bsD!%Z! zPvH|E`+I!&L+?emToy&P2j{&PyV!ZSpQPXalpn)`AMxmK$TKrDKK|hkiok_IX88X` zHZlfxyz4!1$GhI+8+Ogl{nN#G!~c3k5m1Jp%H@9gPW=x9Cyw2S-}vQcKRozQciohjE+pZ;z&#?7260zT189TW@$be&rW_ z93Oc1+e!~_4u{-qTLI}OyVfb)W0#w~@16fYd2b$XTT+z?ek&sO-shY6lp<8 zQ3Nu{Ovw4YckexC4-v8YkF{dh=iY?OH1QvI{$AcQ?6Y@7?1&ZXTf?_rj!*f_&%Q|4WIXN(Plops zZoTP!xOmT9u+HJ!Wmn*;Ywvr?4-w&WzUWza;%9s=zV`Eyt2wJK8+I=j<8w^sodlP`hG&&5I*K1 z=Mi9V;c$a6N&c;rGZV$hhk(7k1(r(z=ff~zy&iDIxg}iVuo)wUA%H`~?y^B-3AG-l zlmi;EvufeViRYX|!A|*-M1;jc%DYX0u~|!=xHA!L>m=0E7q719MX@r!2sn>gGRlyE z7w3!;AxRx*YfUDTa;}kD=a%4_2F6O1!Z-$Wja2*^hKZ&5?R4a6HoV6=?| z187`DxRimijEjIU4awD+X|-Rh#sdLTq~`z*9;;Q0Zqb5>#2BI9#VATYk~cBknbg!? zCcY}}q`;|Cv1>425WVumrM0ZOiF#z20XXT4D9r_oAqrbGlNEzYfcD!1)*`|n26<0f z1KU^u35k(gyr}}{f_2~k^!HrEBfjYK@z#6R*uCcvbhHL9#1lxGD^JrT?CoCI+Bub5 zQx7^N(l^N8lX+icLx|fOqA1d$aU}_XF-<1)`FeI zQRlFJ@~5MH!V|Il&0ojxrq_aYB$qvd5H7j5c|5mdAiy~cK6@|~s3*@gfpR%9qY9*{ zJWWQuj&d(00!+p+1-)S!#VBD7lJq1ik$oxEAw&s}mV1aYOxdHEgg*hWu*T}WlIJ>2 zvqVnk5m>HAEc44cCk7oasVqk8n%BCWbrzID{t=abK{h%m(N>~QTq}G*Qie^8J&{<- zmZgo^+Nrja$T9%q2G#HI(+D8y?_u&n`tq$cTtXq1~I>YAfSYVZ-`W{k0l_*JK_rlBCR z)ICQ;(>nC)Uc$9?wlP@{PdcZPr(SnmO{XG`^NE=-XaeDRv){rl`IJ)>(k(?Gfq2R? zs`@RR6496T`*g=zk(qt4ox;o2H`V|6WTOi1XE@5A$OrWR%#JNZC!Z24%Y{i_7R4!p za<#09r)BH9$*W$C!qdL+OYzX_A8|@U9{90YAL9l8<{R<4SHCj1KY?4ey7XuWJpJh=cohQ~@Ml5SR&n@uok(OMdL%wSNcdCwc1mMmH+e)e=n}R?qPW1Q=WRiuIF5X>mK$9 z01sRy#u)MAFZ@p2dHd~>!XrqwYU%wrYH*r9*h0G5;yl|B>))JyoG^SPS(YaiZJj?i z^3GYq7htz@igE zAg^JceLsOXVrQoXfiO-U{SZNh@bK%-!a0Xq@95F52N())judAxd^oofiB1OROg$sf)p5Zz*}05=t%JPiF~@YY;&=J{lxcoeZ3_U=WN2c9so71o|t`I!Z-@K z-m-H50iK149-1FLY$P0YXVv1&zU1l;lYjwBR7lzqK~!XrVNE3fA`@Cmh(s8D%=5|^ z@pvuv56~`zRLMFc_0}T*|k zv|WeYon7&4LzMDJ7Li1CHCD|b7BJaMUKH2}RLZ8l-m;!?Ei@E}%k$$Lfe9ASfw-m8 zl0~WBV6*}b7tpVD?3p2X;u__EriF2fJU+=uO%Sk6hy`E{M~EI6Hiy{%>`#R~TEp}M zKoPX*L2KcSq0J^cwJ>=B>_I)|{RZ$8V3Y|N8sZEsrIi&j4Q*GR36KM50k%8fb60}T z9spepi1fMo_R9QwTl_?cgVX8!>3wf_~gYb30gV`0|M z=#})zB)wP0LgaMJdJEX+bq13nUlO-T9p=vl$peX+JzLfX$d@Nltewdgtr5LMedJyzgg#&kTMb*7`7UgPjGD~X)@l+{!?*6 z0Y;eWiE?_4)E!R-BhJPiLj*Vp*$;!{dW&q((qd3RNZMlKF!Upcj1U?cg6mpL{g8on zWWZUAX_{c26GNG6#RwIId%+UHsqx3E5vp-UBc&KkBN0)qZ83!a*R&Y>4cbK~<7&i! z=h_z2Fk);11dk`#bwD&7M|Hm-;fQ)CM#0}4T+ z8yG_97kEt&9Z_`O!b^~@xP3>?*AfX;TL$h$PdV{I z!{oWrP3pD+O4zK|Dpd1z)9cP@ID*QZS)G~4h4-S!B@w&{nE-i0Cy#rUgLY_~Y0A?muZu#-Ydee%sgMb-(?a z2&~>nePj>l8R$loeKBcnr|;87M`d2!+uXWWLsumRi+_4yEBY_gT-4rIBy zC2~(TV{32Rj{M1UT;*uHy9?7T5F*1V`*ng#yoKhS6GAj#0?wV?MPvxk+~nabaP?JtU^3VY5|X)SESg5{ zJ52)2yQX-p4-W?z_Hfq1H3G<62b|e$5gFJF9-HFH0M0;^P^&<}HE0)0c`h8VTC`X$y1Xwojnob92gzC8SvG(%@R21nKX~|YLci|C z0I}2Jvh!!KSS>ZIjo^bQ(QRvsAxHHXo2!8TGq-FwZ|)x2oufV1cJaB#4auvj1U zU%=2$7{{Qc;FKkMn&iHQ;G8Z0tc6qXV72Y8aq>^=Yda^e8)wn97OoL41n0Dku7PPB zY}3KEO=b-z(h4mquu26WQub%{K3c1uKxHQfz{*8XFK`(-1BOFH10N0i=*6pPBCfh_ z0X}m8d%OW19s!7;UdyCydSE?(h7sr`-<#GOV0{Gi8&LEhc!_g{deChF=TZlnq!t!A zfEoejSb>{;@Y&12d%F@Ftkz5#Ct%Zq)*H~VR4SnL0J?Mxx^M)#NB?eaagQN>2;-^?MtgAkzf{+w^r>GM$PR7zU!oF^l)NK+z1V4@lmeB@681>MbV@R(jP15 zAd7L3(wT_z`=)D^C{Hngdc-Ub!8A^} z2TBgUAp_Sg#E_$9@S4`DfmHh)g{3|78KejdDv`J;!ZZej?956MC&t+vYOQ(d^Q}}d z4_OqPF#9The&VR?c^Om`we+NpKm~`$@8>!!Woc1=rgu)`oX)JUOkPjb8k_w#{ibej zdRY*^Jd=5COQ7^70jcW8`k$#gz$7^qYMP`OE4y~O8mM%=`MnB;Pm11SANR>8AHV}V z-uCBj#+QHLQ}DO%d`E7BrOJD5wkARzteLR`0PxyZ{e}`tJUGYR{sDgEC9lROJpL(& z8c9H>5RlczXjufMOje`HMh)6?U29uvV>_*(?Hs{6Vg43@%w^Jn@TAXp8ea4>zlr5) z=Rxb|X0yiYU;Epo6Vj{d|MRu<;f_1+xa~nB?K|yw>s#K4ulusk#w|C$SHL+X0zqVE z|N9GUm{ShAc+gez$18s2r|~`C{1v!(&s`sZp3IM--{8l8;JfgH-~G*)CZF~TjUcQy z#`|&6G%2eO<{xce?194a({3#b`sm_CeCtmStJMX{abS z?Mof=)+x}J5YS*uqp~6?5-+^-uljp4LuR3AtoqSZ_C7vuld8{`RLJXqZYTK3_;rtmfU>vO{ z^lK@#)-;4==inOgP(>dQ6;u`ys|izxU?ZSTN=*`+wRul#+73w4l`wew5QVY1b8yz6ZLAV|i7{P3xI_*N{Qxkhprx`7RDTLy8Jdl- zYm*f(z7V~|9OCUL;>_Lx%S8i%)PqlBFpL4i;4$_C`hJ2>dH6nHy%{i#6Z*}FVKZRZ z3>XJVOYq|eKTep&QHZ+o=L7sCKyFeJuW1-DDYy%%%vR9yVURFuvIfRAFiiv7wo2|L z?EX15jG^lX>eV$c&Ph2qYXD{M=cwW4thwsM8X>`8w(x;4jf~~h*W;ac4QRa9HXkQo z9Dtyq>p|&Lo&SB?_RQW)vH>8N2B!|gZ4C{3sYcf)bPI$?b10)7Gry!k7X0cCGr%tOW zRgF@LggOBLUeZo0V&Ey)$&TRq-Se*pf zv-_rgB|U+8c8&V%+TdS6zVbaJ;Lh{XYvm?$Wyo;VndA+TLDLXuI{A-CoQFm)K+G^Y z@4%eB=9B<;)z}O)D}a-Y2Kx8R!8t6K4-Pnd`LDecU-zuf!(DgWAsseVX)5BJKXO8U ztVX$ZlwP>&4*ZYbf6ar^wsb64JNUjI{Wbi{XMByYSjO_0s(^!;N+^*zT-Cp(Vtuy) z$dh;7Dp0pBmDEb*xC)S9$XNVu-|%ht4?ppX=oa@qpZUHVuX*LmaC~$KnT|3!Qmnvy z?%lWR4VyK-^&9>bUh%S@-`Z6EamV4M3;6LL`fhyN*MBiC-hHRcZAuXbrHtAA0z4)Bgaeq4nRED=g zG6N-;tOaU`mMH7$fx+}jsOkMBHI3kh0sq%`d?WtdH$5A-ec+Y{boF~X-uU~k!8bhf zskr?E_pQ`odfp{wiZg&q`FSsW@ekuU&wMK0@t1%0ptdy~oAnwm{mCE2v%la|CAxs- zvKHGi?*Shv>y_um8cN$5vNr;0lfu0L=;}k7gYmpU&bPxnr?x}m>vx%VBU{apN%E?t zP@da;=rd_Mvo}PQmBpC@M6xiA1KJjBjO0=xVt;oD=N!Cegcz{9(}AOar+)BQEuDD4 zm6Xf}M%y|}UObtEh@l@~3~l;CSg4oP2L+G{sCO0(2!vr|ct2s$09T&d z#hJYY?z}MK=x9V^fv%CA&NIPB;Ovpqi6>>K zSJe_i5W=}+6zJLp48r6i_LhVz&n_|fh{NLv(=fqLBNiQDXDOg+nu3tgO{L_M*ILI+ z1lM$u)2N;@XC1_oD&Sna8xhMzi?(e6Qsb(EU9ar*#2!1lD=C%K&L0A6PI&?6AqFiu7O+}4?5-L#&MMal<)t9Z z+Y)MjcxfY{rIpu;bpGeCTLm;Jbxq22M~z4T2x4?tEDBK+KshC%KOUuj251^Bzh;FI zUh-fK0!#XVROk@`u*nBZQ-JrZ#CieV2Qg}lnwl|<2*D$SDL>OND!8A-bLYnaevo|F zTqoOm_25bWjI&~(Xp98f zlp3e=)iy1f#S*Sl9)<*Z^PXbzng=8gI`~P;v&FI}m|`4Ha^G5;(_%PVw4~38v0kE2 z0NY5!Kum^$1SFy0*;)xd55?o3S=;q6WZ;@s!{H-h4jC31lrawhOpt03%193YP1}}K z0WMQ2GSF`ZxTL#TF; zeUH{zfR&VyQ~5(ynUg>}vdl_eX=5#d4*-ed5SkW{2qR?5csuh9m9$rGi3&CExlKM4t5N664BU=;Fz*Ej&r+;)QF#rhO%Rrq~kbqdpvnZXa=jvrwT!Zg;;ZNX`p8Q!4TA$J} zjssr$(?6!~NavCPYXn1Cc;(N1Is7o-KYjo6@dvMcC7%0lzYmZ3gvUSNHr{pT?ReF1 z`~rUcmwp;YhZm$NB>7V*|KA#Of1=v&^+?YpuNeXi3@+Vs7yjc5z8SyzbN?C7`kLqA zsh|H$?CkD+$ZLJLV;ly&@eh9&zxMM#iFf{gf002{;v%p{jaDcd(EUQrTL@i@MJ(@2Ri zd;KH$ZT4NZozu#feH3&XR7=E9;sR=9yK!tJW+Ab^hNiK7YaE(LTHV8pO?Au29 zSvc=Akp&^Ad;kIilV=PYsUzJjJ2YL3MJs#;!c9i-K4f-&L@|QQRz`{f zgjD1f@RdGEYfU)=pn70hKe`8T=8Vw%;Z&w7ui>PVCItv$h#)Ju%aCWpVFEY`80nmr zVjD$2^65emfE7K^HGl)+#k*l{yb0Jl0Pmd<0IpthjFW(jpp5Na06A%N18LrWRxGCg zL;;ns7Klco)m#TOD{!|0cK3kQLcf<<>hR*Jrxfy?$k8St)&jE_4#c`AhJv8%-^^ee zfWd-#0zKj}z+b!`%#-AAGenO}BF<@t`1| zLjVVfW*{ZpGL`^VZNnsaUC#lP`Nm|gYnZ0ueI-t&P5NX9^o3eiLfi6^M@jmiu& zP|!Fj*8*kzA+PHg5Q0V9O4LU5B#{@@K2Cb=v`J*5IHMpY5z&!^ox7&R7*i!gz-o+Q%o5#Lbep#8Fb#u7|Byxo5XRAC={>rpMGpru0n;?1ZA|vG8)M*O5c$X& z-J}jY?;5E_L4;`>U>hfR$Pl{a68+Id5F0IxH=$`Q#woxVhZuY|sH9cPEaRRG2;wxc z@Ej4DV2z~n_@Ng=Uyy1HX!##U|$b-9m;t#&~7x9@-d&UE5--kPn4lm%>e(p!`>fiV| z%@-@MBzbJ=Hq}#RW{Bar%FOi%5V_`TgQq|1>+rSD`>t8t><9Ds&0qb0@uC+#Uj!3? z#+hs&*?tBGmtBtQAO2`CSo3O}yKJ)Yb7d+ufpM~qL zf5e9@rxs(td;j5Gc>7!b6o34N*W#^jej}zyNnl78M@oUPregOmO~Apq%ka?;e+)PZ zsZb1Djd|(S`uGTc^H*<$A!u5Hl0Q^^mwt+%3u>5Y+65l}DNn%@KJBycai8!>c-SL9 z7ERNB$gz$g;Qcqf7k~8^e~Q0+%OB%SZ+IP!4=>FG0)aCdy(QhB6G@M@{-Zwn;kfL| zhag54B`;RQkJbI|xc!58-@D%>90x{HC+2=~c`*+`nhV)SJnFI7J9Ac)a2?0$r}}Z^ECw@elF#x4u~zhk>l{r?iZe z(qpsdx3uNakNadaT_<6_QJArD$}|7_8~z#>?!HrUc~hED-lQl$v)Rr#y9&+0I?vDC z9?m?*wDslo4=7zLU63lkuzk@}cJ%733^TSY0{L1gB0@iv{AkAh)mOulgIg>Rox>Cu zN1F)*kF#fXV6DZ`#)COvZ?(X>mm0&z>jC?_GWO%>VXc(X+1cqZj2`RFfN`2|_k|;z zKeMB`jRL-RmJJT}S8!Hx5$`$dap`El(YnX(LX16^4hL+;fSu0Z+N<_)_2v7x{lb91 zzwvH#4WVnKC;b$0xE}E_*B$`I;o|X#p&u|#9*rSV7fNK}oKrO^nlB3^hn177I{O+vPyia+=l4r^Y0EQ`I*%@?;h1M&U(Cu;b==%}O zjJ>_35XvVr@VN4-vsiR3B1R_!zJs6Ux2td5# zB+`v;&9B^TXCWoTz$8YMR@i1(aG zek&c;YeiOYE*@qW2e1L$0%#V1TYy(f@L~a4NOXb#f31zpLE4@|fflxGX}HvR=B@Mc%W7;x{gn z*QL3~EG18qK9>y<)s~xZ@6lae!%;u+Cw*zmK8!2n;xkl4IXE z&Fd!+M=b$LD(7Se4U_z)YYZX?o5N!m=frpe1H=H!1K7As44Gm&2n_4jC`Le(xtr!j ztru+R2lZ|~bGn`a>GH&zT^MCDW?@sJ%hT!US5yJ*ZCryq%Ra=h-F{MloBRBwcfAwe z@|BacN*S-SNG$4m&R(oABaFin{{z*}d^%s!OyLO4sd&XDdOP>8aTy@QTFV}Q`k8$Yn z#h?8@
I0TE@e;%otO(h=mL<|sV9sdP?asZWn5|0Q8l7oY%$QAh;!V!6WQS6zd> z{j=CRb52W;F%Aze;f~vG#RuMhBL(D4#QPTB2i(a2kW%Qr=r0jm*Q zYX@`G`=I&}=Vxz7j2uq$oKlMk)wrr4i4f0Lnp4)bxbm8h!ev)ojWcJ@gGh414=-N8 z;l+D!#|Ll4(cz_&*s>ZoFoTIQxUQAQ*+D`)>C!x!$rm}Iwl!#3XV9sbP31DN4WL|i|vjAOuN7-7Na77d2M z>v|)!jSz4hZw7cDaN*)1R*M#?oLA#47OlnJZij=tB?3pRHxn)%4Y+XBgG0dXvccie zfTNAa;5{DskTY2AEOD^6zzy%ah{K~b7OlauHRvbC9Tx{&d)X3a_Ley61MayvfWw62 z^@zuR+|?MP!99lq?z;OBm!I9iqI2+(MBy6-j8njRJ!qb$cyJMc$xpcYvK?G@u*3&1 z47le~FP_|m1Ciw8_q}-PITXcGLSrX1-3qpCi@`~iRA zG&1a>#cI(ZY6y0W5z`d0UXQRQVt03e#iB!uz@igUGiFHMI}wJ#D>Ht;-cEyV(P4_f z(Pl!NCcru%3VB}R9NI+#rzMX<@Nm|kTeOAkJ_I-;fOydYtA%hBjGl408FBG&fZuG; zA07(`0&vw;2e|sG^RUigm;yGvlzTfoTnleO(*njI!Xm_oaWi5X1L8DbxomLx zf%m{Ld906mF{&6wyI8;)hII{EOK2L0pb;4Y&<9Mzq{L%E_7Dyrw}9&$x>W~b8ey33 z1@wc(qdHrIge>Qc$)Cz|Q?0CaMi4Z|KLJYua#>&`;3O+GEqhWjzZ{~O6ikTW2SE%i z87zeWG`k3odOUD&1z-%=S|Np-ynL_PcH2hv4C||Y9P)fDJ3S)+~m@0 z<)M|AX(VeMn+Rx}L>Bo7^h40JhzW5VbxvtJEy1}a%c3;tA`1azBCAUeuIA!O^U^wH zSBK1BqxH-PiBCv4D-wrDsu&^osl+A57%}!eq90*gqm1q16)#|)5ki2qgypgoPcs8V zjB%I{Bg1Hzeu#{52sk*mi^Xz*;|Uo15mP^4wRC74Dc=Aj^?(U|AFy0CSadChDWKo< z@S9^yn>BV1&Y)Ya)Cgd3d~}TU-FLt=3z(+GVzmR)wCMW@jsn15O3JAbff!Q#!x#zZ z2tpxPXF!umt)z4Wt`>>G%r9w5m$I=BMR0OG zUR+rjQM|6=)d$!Rh|Ng}$=W+Y2AlPlW$d_E6yTk_n<1*lTvE|id(X4-dueS$fS?6aP8_GCP{H%V&&~na|#1~8gR?|-i@2z_wM}L zGBn96pNvSFm#X&?aGvI}v^urH)a~FjhGjUCF@f{I6xmYF(UC~wSG8+?)VpPxZ(IhL z>u*nvVcz0sYg*JIx*F438m940d*gP?>g#QX>1R*;>~>}D=I0Ap|I0J`FpYz08%!Yh z5HL&=c2+Hn@>dLllp_;QtSE#rM66bg5dDM*BFR~78;41~X?;IPE+7$t%92GZL}IH& zBjM>IFnS4P<`A*$8X@Hgztv!x6BMh;WISUcTn+bbMgM+;V`iZ4J z`#8dffU7TGVX^3N;qDDCT-;#Q2`QZq(6VomJGovDaE*j&2VEZ_Ml=Sv>ii0aeZ=AM z2txs@9jVE_UXPk%AC$S60hZiS2BBTaf7Q=B2 z9zeu$CAFGkEMfU6#J}6?Korq54FF-TPC%Gr)O>1#>rpiwHKk2q&=c;Og3D@nuQXyHeLVT{>;Nq`svBEveuI0kgBgt}Yj5UiAy9LEt& z*X0O+w(GEQt)z)KhyM5&yJw|5WncrN90fT#fw`<75~E6p(jP;FMBr|mMVNdhkxP9S z!%qcW(lyBe7^)pOjXO>r6}H?ueg1iMoOYShKd++$i;kT2?@H&G|9Y;Rly8eplu}BP zLYCzmD?mT_15r*mzeWb!qzk-AN-j!cJs2kjYu;s1YSisteU;e!*8MS__jO;0pZfV% ze^`)y>EZ?a$Pa!Oe(UAGEJLN&%ibeE*yf3pxMf?RL>|D?m2J)&XWjmv`O)vkr+)V5 z;hKj$^h37qAA6j?{3?9Ov!91AdG_;g_g%N+umAE-@HcOJGv0sW-{FHdzZZAhe(Q{! zLWGdR6<0q5SAW#QaNYI)0w4d0pMsBn+!OyvKzTa;_HX_Qzx;DA&hJH$sJ)(gRC{p- zm5{uZv^mHp<+-H|JSjHLkXo;k&5ER2E~j6xf?8epK%USX%k)g_>`8$tnoa>U_54oC zxgi#pd%hoLUf^WiH@PPTEl~Q6-iuNWQG|vIG9t+wW)zUdDku^NNpoL@q2h-Cstt6$ zBx{d&j)X})AOVh<_h5!*Ox`$?fT41!Q;vGgrIV5uo%0PXaSnByn@LaRI4Aj$=S5N? zP>HAXosJ)|L>(4mTRw5N{!{-+FP0SGbVY{DyW|1?5M9n=QyWiDg3n?YOw~uYa{6Q? zbv6!_5`cL`MDm&w%AA%tQO*CY#&Q}{M$S*Kf$xbnLyR|d8@9SWduC}2GMOBTPPu(- z-)pvO)t{xC=~uIDGR;jriE7e^tudJ8h1GYV?GNhpqjH5Kx}6=65>`cH5rcToeF!+W zzkmQ$M(ZqEXV7a6?a2o;jc7H)Al_AGA*2KeFpM!`8Ygs%7BR}*+l2tOuC-Wn4wH`p za(%?;CC}Mf!g}K+5{UyYKeq?#9Gb@BraLxLe|=@qH3oMaPZ+0wM_zLVWGoIhLf8dJ z+R4X!)OlQ7kGOEy!%q_)a^(T6wb%^G+8hGb{eZS@B)TMsOb9;WxF7K7>(7A<;jY6G z1_67k2BVL-bV+Ij`>G5SD`UH{gvD|xU`ijqQCSotqwO4aRvluDSS}hci%~?rkNL5M zCv0wrgdflk`$1%52 zly?{>jvcLE4(76lutBQZo7ZBkCUEtaw&e;5;MDyQoPuIY_%aCMX|@c62o9<|5D8#! zSCZdN5j>8dFo8E4U{yS?Udvg8i1oPlyeLKq&%W&oJN!>37izb7K6Z+ zOwPt(79h_|7zP%DU`)?eL+>Gc2^_rA285hvopt||%TH3o;J}mze4|&2v24g>< z={gV#XN1wZ=jmK(R6c|VKLxmkw9I1CjR=5&YdZ| z_+dcY*@0D{Y^_DJSYkZB1daj562ds3Z98l>6Nn6=_xj8&B1sgW;lc%kz)1D(5hDOe zJMd#ry)Wq%M$$Q8vqxU^0+D+x2}AQucs5TklNDQUEBR3z6sPGN_3ySc&HRg^Th{t; z!YL;s2aCp0+~7RxO9AJzd#YaUYNo?^vx{?Ha$}P+Ud~I~QWmBtO6UyC5~(sxmBVtb zNY`BeK9dYy9x{d<%Z!7hhE@hachN-1#f;>7Vn(`1H^D z;@Rix<3kL+zffm06{I7Mis0Irp@+WDtdSs@p4jDjP-v; z1p%1?f{>mwArqk`Lb2j8uH|o`$W=Io_qSODgjx>B}3TU!<&xNa;mG(hB08ZXfhF5 zQq}}3G8#wdT2XQrozxod$Ek1!_y}hWLSSLk4vfxO?CvZ;Fqk~!Xg#8Jgw?XeFh(41 zJT5wKnnOVcQ1na)sp4%3fk92saLU zyDJ#OXc`H9_dz^$(MxD(UxgBnjfdcTS#>$P>{qI^w)O6Sz;fE=3zK3n{4qVbKxVM!c1r zz>h&r1u_cGQwo;6u4gZ6&1W%wSm&}Hmx%X-xJsh}ECiU%44stk6QV)eEd-Dmf^{yp z%c^lH)tlzXFp#Jv0q924w4-{^Y}VCemDwB;Etg2(vqlswM>41vgkE8@c71A?DQ6q^bX0%h_>*5{hj5fhFe ziBb`?=`f8GtWi^ysrCgTfThf101%ao)0ielu7RY|QrouTAs>1eYt@jhh7uUCX9Q(N zpQeB|<+3wN{<`vDkm_vK85~~PU}tBEW$O?_gK3yBP7!XARu@(BMo2_gje~Pq!q7QP z>q7+ZC2f*rJezKTX>%-w80#?f19le+t#RIfg6t8L$+ji6$XMavFzWrc)?f+&hNE%< z0A)8PFf0hkK&8t*%P<1S0$j?`);O&CF#om|1>1VcsoQ>fj$D2;-{)(+k}I9NTz96d zq3Dk@g&FDk`Bw#l%yg*a9w)e&LSHF|+m}lg!SYEWwUQr;A&O@MMcTGnfYyVUgl1|| zZeq2;^qXV6UqiK^QaiW+ zmP!cm2mk;e07*naRBTkJ7I9JrfMSTADG!3GPZ*)Hr<$tiwe_B~Kgo!kt{sVH*&*$qhJ4FTQReY%^RFI6^>#L{B(=`nqD8yxz|J9hky+y~Vt=PY2;vbnB;@p=NsfP;e_IBSHw&3iPh1t4L(5duC9C1)k)GNyniqR`St)(AMUMhtcW z1fnSTq)}g%5Tkg*oK5Rjq7Kk9oC2X$*WjX^z}5jg!H~f)1^7uB;hlvw5rKrLNgN_6 zqA3w}dD)Vz0Iu|WV~B{6(KrF}q-@nQjucW+tbdIHacixH+>?0Gs}&q0!w(|}ld@Dt zuos4K@w!QVt#JiB%OGl~Y}%NS0NK1JHD1rSMdy^}0p@r*lC$A1K@17{&*HL}0}K*6w zFI`*T2LaC36RVRINMkeXup?El(o;RS_}aKn2nJNOpJ&y z0c2t^0znwayVi*_icG#QDDBA-WkSa1km)duh{1~74kCloq^4y5QO`ozhh?r9P-S>9 z%CZiSMxzjy=(?yfAdT%f1+-+d#~Y9k{l#j5V=s|M)*2XQxU?=gfP%2D55t6}(e^p(62YxKf4bf9HxasznKuDfh;d6p^O+|En%EImJ0Z-b0F6ukucRSj4*pyj+W zO^ARO|IiEY=0E!VQy<(v$K&;{eN_%^Px&&Ld~mh|s&PJT-e(ZaItS~|TfffN*Z$Q$+SBefYkBod>)xFf za9;XRzN?>emU$}UQh(+n!&{x>{19g{cI)+NM?OafWOm-^dsgn(_S)cT{g(aZUN3Xv zn2jB$YtH9qHB0j+c|F~0d*`ckdwVbIu>v9?V6nG@wv*gtCaK{~n&j`CRd11y8j?vw zfYM-NgfvdV9W6p&G){m5GjMb)1UVAz%4i(0T8ctt46t0Z>UGs{Wo2_DB5VdB2Z}M^ z-2Mt6LgOrsH^O4PTsGK@0ewHMW-yu0pk=h!BnbZBQ`_UTunyPNG@qE>BMSSH74s)Ss{xtr;eoZaMqfw z{YWDOAg|lTU}xE&Z5_zD43MWu3@;%BE%he%Pwk+*#xyKn<*zZt%N;efAVmcjYb%oA z?UezPa9yJyTRo5}UpN6`0;XY7ud0#S-_{A3SModoZu(S(cq5Np+5|Wk|CF^s*1^~o zWIJI(cO8stBn2YLWN5g%D*rh@omoIe@Iq+EvFL_bmIy$AZGZrn;|rSW9YB)@X?^NE zK*orNy={S}Rlx2fl)Y_%t^*cJU`IpRmn|}ZLh!-_KaK!LNxSeKluEY+%|#EIPaXr{ zM^GF=@KQ1^j6fU#VqgLAZU=N0(BZ%^f(8#v9^4OVaF%w@a;>xQtc1Jj5*(#QHW>qB z8>GmAxRt_zLWF1%d-hNnA0=~L`IBKyW3A%qud7s<$vfF zo#wG?WTZxc$bKlb=E*o%=U`ofFim+5#FQ?eGCfC(C}CXQCUlOqESwW#1T!%7L*{3Q40N4C+gL=! zVMH>f_uO*?#DKPuP{gQFXaGk|6yp=b5H8Xc`08l)uK3TF5HJ8OY&U z$@(QXpv<(L|EV#s2I@uo;p&2^tw}IY84&$lRz@wsTFsNPvSFrLz8RQPDThUb(|GCf z#cM^j^l|p6PAK{(l+yG7Lc|Nd{W-Y##`oZ#`|)=-yaV6&9p9M2OMW)iWUCr5pQ!b5 z8Xy8H<(o?eNAd)wC7agI3^Ue%B)=~Aub=5@KrBUPqzOg!nbmaNYHdMe`aTKgytivd ziBJF`arJr*T2HnM6?RAp*j%uczbXu=4)WoALY-fp$FDMu+`I6qU{!_ivWK?%SGPxq z8=rB~5((_HPGgwTUB&W~oK2|NR-fYXmxxB3AMs-pK z?X}gXYR`Q+vgMiU_p_#)5Z>EiaWlny{>u|>PQNenZGPtJ%$|@4H7<82gn;F;nJH&A z=F7>k@(bd8q&0hXQQK!?EaQNX!Zk5-I~VHEcA zgT1!Y>W;w0;}JUx3ui6Xn+Z)rxb}*D9B#yu9D~$!K7X*n{%(i6FAcbO)Z^MK_OR$$ z^rOe7A2odmXd0=dEqlK(qdRBN2jI#xi()K@k|TfNo@0p!QRREs3}A9t?d`(09U>Eg zpWsx11Z2MWzyL+;?{=`p2-7-CK5EKCW|lD7^=7~@OgO(UjOiQ53cGtt1^bfEZ!|RlL>X8ck{oNNd?RfmKwLs-WsV4dCnZoSvn466fJ9U{ z^{}T!CLzJ5T4RjH8mtx$ZA$`Bjeuj2^fSbW#u_xv6j?&bi2_tc)TDQnsw0ycbW9bF zoc_z{V|ngMA2KO=GV-#n4JzjbFTf+hg5eP15UE9^7ED6SMz(?NmN4xKF#61cR&9w< zb;zQhl|>r!KAMCMCDJ$RAf9pyh8hOQfnh;paS9@z?*nO5;wYZ)sfQWw0@g=hKPkgH z#2{e`K?qwbK;2rPagtl!w(?omNNKaKkyJXclxj=f^XR4IoBo6Mk_$ggQrmnQ)TrPA z1TarP96)9Q8c3*oXMu$SI!J#1v zhG3oKMF(YFPir2895o=wv(OMTSZm@6!QX|Nu<<4^YCp5Ch5XMPZ(w!wKM`xwJdGIL`Ng@QSwNe%> zMnoOAI8A682PUVV$$p_~Xzpt&Wet=Nd$2|sB*O};Q8%SYGIAdJOlzcS^Q_k_aN={E z@_GH6JpI$-sQzD;2hGmOIt5`aPfyYLivFBrJLh?sgc;8oQ6egN_8u^c&SKG7be%!l z2}o~RLeps3q{irj5}{pB;?y4oodo6@r30=NsWNnx-VKHO+e1~9bwai>8}*PoS$`Tj zSPf{}C@R1(e=sEkp?(ObDPVa_t2Wg~$P*XA*-|ktp-52HeT;B?)8a1}&Y%Swz;XmsSr`KiEsMk^5 zuXO%)muk6KUwjVU=l7N0@x1>ZP8iIavjwEL8=-G$elq3xn|ukU&EdK|Cp%o$4OC8) z-`ciCw_1w#93-Xbh%gQloVA#|&*c?~NI|kFY=#Ky^?=qnSR>wZRs|+TMn6pW|H*r| znAw)?Jm?!$HRtT^zyE)&wb$PJ96LTv?BG}?AP`ajB_aXx;3&kF6_A10284~|ATG)a zJn#bIB~o4tiiE>OA^{07QY0P{af!f2u^nR@aL!3=AC52Qy3am)?REJt-SbjagNHFj zRn7VL+DAeNFzKaU-T&-)smrKQ#s-V|Uw0N!m}p_IQ81cEcgJH=TB5Y`%G@|`gi9g^wg;UDLI)nq6J2M%;>zO zp2*yHp0qR(H2}P#ELh$6;0T8y;O5Xvg#Z=&o%1-4a|yZbH6PyUR2Mmmg$7uG@mP&F zku#Pg5s@J{@yuz?yz?mD?W)A8GDZaRwq};~P*X33xr~!kBd|T#JlC9N_!9}MVkPROdd?4Tu2Cdvo|x#c;yIi z1mzL9e+4|e(6VX?tfj|5RFL}#kdX4e6Hj{IgNGix>%e^gcLweUsnW36+Eiw&9&(E6 zRgbfvx=$mpj0F%5c>(bZ0X|{C5qJo|(E~#k<$R97I0JJ8cOL9A*c0%LueFRA#bRg* z0|Q*@)w{y{Im@w}vS12$@km=pe!*z5l3`iKMe^I_cAUhEYpT5>^vw{bZ>J%Y3JdtPEOl6ddX^->Ox z*@+ULg!WPr&-bo(WnX|A%U->Bg&d;*OZF0Fm6G6yFf>vC68#gG897UYSLgzKHz21d zLV{; zw6-a)H|bB3>+JWHe8(mm*tP4c%?CtwLUW%Y2W|wWb!uEAXX!Pvq%0j|D?DynDCfKV z!C|*|*zG)q!DHAt^u0&di#J{1_~q?F>s6OelbyF6lTQ`w8;P?OZdRS<+5xlPwf#5V zZ24%bd~-(yY=ky~q8$UY&AK_(Kw0x(aha$#pSKl$TLHLwWHbbtfZzK3ui;<)k?+Ur zS0Cf=wATk;{WAXa4}TxN@xfOCh+=NmI!~&A6~%Go1C-ZB-foqNsxh$Uja7xB2mwVH zSSvlPf>7?pb*>qO9YrpUdLAXU60u2yRCa=A}IH6#;1KTYxM*R$=|$n{a$=BUGhq=!x{{muS#he`)@ud)o7=v8@Saqk}%u_ZmLDGBHqnKk6{m^XmiZ8Zuh2c5$-+ z%1jp-Lbr`}eKZ}uFnbrqzd63SuD#wqW!P*W8fMPx^S1YBjTI|}-`4Miv3j)cHBaD+ zy<5Llz%Di)&DfSLokXbIUmvmC@8P=+(;}X~oHG(@sBe-`?_g-y3_;meZ^%3^8h#Bd z>X8Pd4*lK3SZKnT_uPqBrSp=9E@hF$yBw3C!hRX3Af$#9`D@jaJ<^##p@CGrwN~W_Xds#=V`&~+Yu=xt%1%ECz5!R z`d&Pkuinh~mYaa5H+#Ign#G4z zZuSVliw8-CY>o05WdWM6fCv?FfXqbC7{D}Oq zsK5>q<@?FifUdMh_~yfD!V(iQGltHi3u;WT_AnRx6pK1yB1AS>TiZ)O-vt;*ym`fWnY-Kk+FiINYnmQvHU#3Ty&GD}_cX#}Pd5GNIAlX}gg z7(=`RU-ig0J;D`Z*DVN0z{3c9d<)!-VA=sR0OZjHhvnv($iYQG&$6FbZobJ@ibK$b z`y65Ap#pM0!hwFk05i5v?3RBp&jhV2T#qMkFaINx}Lgb;Tn? zl;9HT&Xy8s4owtKeOAMO*YuF!J)XRCjTl8SP8y*hV2=_`PY#%;S@QB#&W$nYdWS}d zIPsW|BVNC`$G#UG&UuHJR03cq`hLaYYfgmX=l6Da~ zDOZ5?{4*~mw$8PVjQ{}uL9J<~kX!v{AIq{}@ zd%K$BT_wY|ACDT<4p6D`a6vjQLK(N6-3ZEPH_3Wk89>vaxDjB1q9knJ)k;hzO|E3_ z^4!u#8+yH}8G_&Vwa?*C|1*CaAAj^A{!V#)@YOHl$A9Qg;DfJxQIs%6b@ci+!-{$Q zfOc$K;%w-X*T;BEOdFbR@e$hV%qv`#_~v(0R2mti)ge~0iT>D#=;g=u_eTqyOo39% zK8$|ta;kKvL`z^4BUHH`)qAxx*S_t!)~>R-)KF?`Yvn(-Sn9x4+hx7i+A5%jw@j>Q zh~2v$q9Sygw_H5unuG38H??wpeWZ<*eY?aL)(7~}A=-RjpM}cFH+OEs{>m7sR=XMk zYb>uDc8MQu>1B29rc*Vt!8U(hKeK%)FRPnFJIjqx3z~kUZEcm|?dua&+P7S{N<~5Q zjMYX!m&c~vcZext*E>yf&G_{5Dq~{WPJGXJ>GxkfG&8=h+Qv&{j2*KaeTnv`#tYrUNTBwEDKH#Go~rQ1&8DH75bq^ zhQqwD)_N8NHgp|)*8wEq?%;^eeCtyLFP^#-Gv*}~q~M%IIX|6dJiQ)p*h}u@c@$6o z5D0-7%RJ#cije*E=71;H2Pt0$l5kZe(x8H=l#7#bq_R2aDrBGwmj7L8!t% z$O)VnpdjVYjP2fj49pXXQm@{6FpGfW)FbGMl7Fa-S?E#E6y7r4E?Z0Bl0r z=LMM6YfffJn8B359>A_cx(aXy##Q%#EB}Cbj99*L2EK6znmynT(tZxey+`jDv3mkM z`*aO8R9Y4pBNPV5iV81j1HRCK?R}z<13q*h?;s$AqQFxDLa_)vbI|27*eOa`zodps z=R!mKgtW|vi_}?n-iyb+>x5@3Z>2B>b$qeVenI2^U&9_yo?)_XdI{a%FG&tZPg-7-mHA5#gHI^7$Vx2 zj&+OEWG~vhi#YZPg3L;9u)8$E34PKH9(e`UPNkJQ$<_oJhQL~Aw{poQIl2AbW4{mB z?LCH_N56CE2VWF(@ubtrn=U!(R6y8jY$}RkP?Zbya1%Ah*T<~ut@6|g_w87wwtu-Dunt2wqQmJJBZmw&MNvo{k7#{>@*{^^%n$V6<}S4WkD;R~h$qA-7_C zJ@+lgH*KJ1{@b&*yyBt~WlCI8bX$UMN5Ght9=FpQMY+;$eb-Y6+Yo#6+LLY~j|RPM z?8U~fPb-YZEMd0cYrAb_Ta!)sb|heTCAxyDXMHs^HWEcADy(zWyh=-(q28?H)$!)Q zCAQm!lv|0(wQ-{QvsZ(n+`Q4_wl^+w`H03ZUZ9aW#t5lC+YxFGXt`g#vxS=3#@as0 zXnn+>a2CO&f_Ae+l~sxlH^)voTrF+ zse)kevj1iV*l8XgfN`!lj#8pX3@=d?_x%vW0FY(3@=gp15LrWqh0#oF`Gvi3m$##jr}~g5*ZOx}7oJ-s5||^9io@176+< z@K6YZ4_}-x%_7WSUtgi$4OkLknIk;Zo5dN2s{uZEBxX$0jKqxJ{cX>1*!3XNaM&o` zAE=UfuVL+d=kVlu2O@{_I3vdyLy+-bmKhHx8Pl8V0neTtK+a)_f@E&gkrnM&C`INl z1a(T7z{nDgDeF)@gbI*G5On3BLO58!?f{8!ko+UU zPGT$qN3^v_+pylsS(Ry}3Qh;h@r|hf#nHR~(*#>$0RSHz;|4*@c;lI07*na zRFcEqdGH>%9`5i?_jS1Zb=-aI2=4hc;)~Z3KK!lU3Z4t2 zkul;dVg9ZweKDg65sLA~a-<3B1!pm$!G+>^FZ|Om05q;OvxK*El72Dg!q>c(QZvPy z8Cd2SX%VDyK^pH6y1ulVP?cw9%Z<*ZjGW}u>)Lb@j_$fa*FXgvGe&Nn;j(k7UgGWf zzAE%x*GW_eOEm)L#7HEdi`5V(&#wB8g!eZJHZwW}*1UOgfbTq(IU+S{FnBKpx!|QV zn^me%9@PaWyo4O!0-W=h#}nSXIpL@ZIyp!*mC6C;(RL1)XF=r;-obS}T-OV3K(zd1 z+phuN1*to){A`{k1oghBOeH_v#9IRpulz^)$Y^3qECX6VLEz5|^orBsg zU0>83(=YAxexm$xNx^?46g9GLIh~tR9^GTj7plb2(j8xH7d2-CfE#v_d*1i;QxtMh z%6*WK53gwN=0!IkIV+7&JnEjb!i1jVn(v+zM6wADrrIzQm`VewZTRDk$i6|=->Q(C z^tKwaDsbBk$>eF1_*80ZL8~r|;N{m!xgdP;3%`bc{`>wge(~pi<}DZe&VT(6f8{UZ z$A0j8@r@6@3iAXTP@h+T9RmX~#ZUxk6V}&?rMwysU}Fi@pJbZeqRh0;vPUTrMJYgQ zKwjU%o}UlwrRuVyX*BA7{3kUC-N0Ceq;xE)fO}xn0?kwK+ds{ww9Ng z;&w?P=l0!r%`Q;L#n+?jZGo+ZiB+Joby%eR$M@u_Ve{N-O|gnQ~9@y=Aj7!F{h&OTQ2)iNR_F=~9?g8KR zd#>@`JJ*OAxIfMC4!FA;@$j&~d&aY8NAyFF7+FIu>m(nq4+^fv;#CLm{Mi9djys)2 zQSL09GNz1&(}eLn!#UvDjVM*~9Fb$h&e{ zHf^EDmfLNMs-ml-EF2TQMNlRItXr01fMZp@$@dE0rs(nJgvNReLOr)F&&r$|1Db6L zdSA#J92ICy5zX@iN}|9cji5Zj@dU@CD)loc%|M#uGcSUSZUzCpH>4S?Ynq6QCFn)^^HK^Tx#+59Er#o{}i#1^~CrqLk0GfWc{=HB!{QYyrIFm9r=0 z!07ff-Wxu^F@JP?Ypu!}qI@W_u^0F*2ayU6ovNUE`*d zbC!8>UOdQc-)u7}fO8s&AZ-*r5tpQfKtW_b91nUtO31uMwuDY{#L0;l-2gl-HD%Z9 zx^MwjBJtJBH*lP=A3As+5ToUxbD1mWC2A{W0j%`i!3_s+R{Rn+K2aSMP9q3F4ry7C z<_TRdl}iMmucG5amT3WrhK{j}T?WGyS*T?#hS$M>&Xdt<^`_VT_${v@;IBp0%coW$ zpvr)aEdI8KnCNm*K%xuZY4yC;&|lipyyb6#GvxNrn?i2NxO&XJg}1wv*SvhHLSB_} zu^tPkW>xAbf*MYlq!P3)xsEbP8H(po;Zzix-S6`CIl zZ)Rv901vk>@n`;pe+qx;Cx2qwh~LpKX2yT?@Bdl+TmSlxVmzJ7LiTE`k#ImWbkrz` z5*j!R5%IAt!xGn(Rs8O~KY5jM((e@cdh_9mi8rXp@&{p$PEAcn4 z+%&!jD7^mUwmnMEW!$LooV%K14w#pB&6HY2m)3*6R`l&U6@sWar>v)DjhOe+gys?& zT<+II&}iEw<)P+KT5hLzjYqi90Ig`G_M=h$+kOF>!eUp{$yrUqW(>If!gxe$b*!XC zD^GOUINBRE0_y*TmyGmd9@U25ZMv{JYehC#vBp*ZDr;?suBCOXhw5!!?`#fDL{SW(Sf%ILceF#>@|Q(mR^^{x-t^#KoK!s)aK=&J6^$EzOScs1f`*9jOJGfrbf zObPEi-DA=4a%MpR?z(`7^MczqC*15EzUBQVczHJhfRr_x7QA}(fL+IU@7-(kLysj& z*ryYT5IN2`9`_|OAhFZ}e)?pOcb`b!IUwGrq=I|OjJt;g<2WOyjO#;>z7N{}h@tng z51$r{(}KS9I2?Ms`|dT!c}z>fvMf?p-a^`0_)ynbJ!1i+m2(uCT?xm8PfOp*3b-b0 zszz-%f`V*iOmhNQyysDs>V5Cgd9O-M8+J~ZrbSBGfpLB4u^Ysr=9FQM=LM(pf_Y8| zLCYpVYNNLtkjQAchPqpr={(DP8h>@B2)B!}5z2(02wf0_nTr=y!qMd}8sZ)XNaTpF z-x?}X_I!|lM6uriPSM_-mQT~YLA;zm&N9yOM)}UrvQO+v>W-n4%h(IZ8uAP-%DE`( z+`J{%Ts`r;i1P0|sPDmF{{sB0ufgR7KnBt*3brZX76p)#lut9ScurE!e4fRFK8s>b z^Riah(+uVa<^^DBD+WRyfa|M!yx)I-X%^gNSqocBm6A1vb!8rg-MwoxNgx}f>%lF#i_$u^XA(+{NW4M3=d<6DV4@l;pvxMANT z<%FpD<^aPhz@6(l1yzg^POnh{eJ|J(mQFzgjQ0|xmFlEu?ggy@^E7FCkAN+5swLV$KyKx6RrH++u8_TE8~#nj5rSg; zE3@N*@{PB>n%_2}`sK5m&yAo#)EsZ*@kescTkgG*(HePm6)}z~imcRyMox03R6A9! zyeimUH*AR{p@N-ZR@YYeugbbA>86;6^>?qo*ZSnOv4*Bt4H}IGU`X&=S&~t%X^#~E zF}u8cr|oz*!W=d;a(Vp4;zsKq*p4^zLDq)_|G}U8xAAZM_z&Tu4?nm(o4$^jhcva?`E*Slk z{-H4nZ3m;7lX7VDcdLhNf9DFwf0HS1%UI4-rO$$XUq|J z@p{5Ei!%TI`%e(M9@8uc-cq_KBgGlRt}h|l&U?hn*msO~Zg$x3dht@T^v&q%JVlJE zz;+&Za=j};azAu%Bw?K6IHL;=eHZZjoojgSG0r0F9(DmlD5(ZuM%OtEoiB*eISZg$ z7hFMMHtNn#|6rxyX=1y%g5?3(1onn&J26?t9+Sgsfy7#KR_uYDf=E1%aq z@dN}0Ckw}qa&D9oX%rVJ&t@glastg0XgcdTvjW3Spd97RQNiH|`6?hB7QAzKfdjw7 zhaYx$^LfC%nL*(i7;b=06muqxRPeSlE(jDcfABFVMbI3<^8&hg29y*)5jsQ`VO@J2 z$n|w^(G9?m(OIk_S_&^A%@dsS0%%6AAggn>Gy?%>bKXlxJOJh_<;OG{h!khT+9Wci zLyA&X4bVCDUUT0`=1RTvUa>g@#7>M`T_HWI}|pOggUO}hxSh$+*U!h^N1QxXSlk$(ojqB zPA_x9k`l&oQH4zD*Fw@2NGNBbrzkqM(sWYO{ngilPLZ#|6j3Ad!yjFwPNd@mn zB@JyCuQEBp%U5T_X~ggQj_1fM*B`G2Je(tzMU?eVzkh?i4>*rf=FLF>uu@9M3|t*{ zFa?(ZP>Oi|>>B%Bzbd%~W^}=0S|ZNp2{}q=@w@LF0TQD{-%Fn3yhLexF93VbpC92v z5QD>c#;*6$7WG`)-1I>(ctm)Xu?tSZ+|3Xzwk^UO86i0AI^7GkZo4BhFLOaOj*BQ2 z(F99mjRo;pQUyyr>ErzI_;mZAW|O*1e~qUdXWJ@PE~ z^o+Q7IPUN8r287qFHiXT=N6jzcN zt5VJu8s7*_RxZuL-2nxH1WMHgZ=vql_;B^4OLVCNmnZ z644Xjh69#)!7|N&@+!`ZUQ?0bLRn9zy9X@ug8dK>dXJQ$e8~1&`995(vJsrq6fLh~ zPz7vq62dP15(5ya!C{$a41-itu<M zXCrLed2bW-Tee4Z`J{S^o@fUeS?H2D@y%Y;u$kt&J+FPY&nW=A47e1z41TEzsP8ov zU0qWJ-=k4bY!XT?x>_>f04p;k_t1wcpaO61)nJFz#^&aH8?4pfR?oZrE(?#LELQU` zNy08xgTw1|^^G82#5r1>u>P($TQ|!>c4{c^`Qa8n@$ded_?Q3GpTOVxm0!m1l-JMx z^nZ^Z`TjqGKmX_c9W2WPKo*mEL0ivg4F#50RUxoGQUqcdyjD>+rnb%NuHP>JvTYY^ z`Q$<{a_Oa6G6BLiEIfM4DYT;bdQW{;Id8o-wuS&SbiO*?w5mO}rFRRpwh&%=G)UHY z0@{0*b~guFMW`7)Q$*I!-Hz^>!IU_MhABDMl-wf)y>%^sV4ZJ6bT8LZ^_H(Z@47)a z@W$)@NZ+bV`4P|9cDat_#UUwbavh=4RWsjGRnqIuq(X8zR5!08xHfmSXP0q6qbxS< zS}}DrJka=SYXG(PhIWlMYa~-cuq^?$4=o!ct&d$AS*jLv8{)RbMrUiKee-IC=FJ#9 zKDSOqjh1Rxs`aN80%||D2E^6#?6j0|{mv5{uCL*Ww@SioV@g`HHft`QhVjbfbE1nq)zz9uTHqXd%*ke>~Op~;N_bU*Lw$K;53OR z=K9d#>M&rO#Ea~Swd9bXIUf!?@e+Aa(3d2h{F~z-%Cd&H$C%Ld9+`o=`w4N8aM0s% zkD(VdWhuL8C4l42w$Dk+Af zzoiVgH0gSCm3O$XmitmdE%+`p1XGOQ%cjV2kt{!cA}4)wt*x!8&*stPoRFi| z!;a#m<*15%5|nn)8xt&SUgS+Zt{#0B!4-fazQY0V00l5Q;CzDrjbA~(ya7=JFOw+g zjaMAYB%bml-g2HN%|n-SDNUfXfYJn{NkZHQ2RsdU=jJ84`w#Ka-^!SN0WL_|%d!X%X4a5*=RJHs6a`WC&{UOl9aVES6dBbFB19$6 z-FY!KWi2t*?z0vpK)P0AR3(;3)`Kdw-pjhMynd@aZ@(R{cL<@wG)@I*E~O;l@j>{# z%^9Z|w{Pxo*aZx`4#bRTVVr6JAcF22S>!73mCl_;ObAgq?Qf1tk&yvXVA6=*69gEL z09KGY1CK0Ys5Hln8tv&+UXgyA%wn>E>~nSBmNb^jaz~Mo)&l8DF|Nk#%dc`-Gk1-B z;G4dT&2ELX^2p}zl{~k4fOO2{NM`^cDN0U6TP}c0Yq^BCu!b|)yC^hzh3Et}p!!)9 zY&JZN%2*Q$qb=TqMoF)-oQ_m(`k~J?PgINQt5aMT9#CqWR?!e;_STb7N4R_|1ksXE zLl3mQz6ygx3W`Mz-Z}iuU;26c=%4(f_*Z`T2k^K5=KqTSufBfem;VNS{7?N8_%lED zgZRR)|7{q>Tbh%26I=tP(Fpp~j#N9o)clXi{7@I!%A-!rySb*7wJ!}%1KGzCI^`-;hwPIdc)O64ofEhw8QlTgj`ejAq z?dzLyn-xRaaFk}OO-U&IY_DauYAZ#7F3A4+-7q-`dJgKmw;pn%3rB0uyD9pGZWz(FS8A$qipp zS{I(T{him>+4u6dF*+I0RSHO9Yov3|<$3jlZGYOot69Syc_GNWw9k0=>UeEx`MX`~ zY?InN|DyU|7zdX>AGM~D1<;J+^KW?Mnyu(`_8M9EP{|J+9K0PkCNt}OAT-Z4`SB+G#4aoF=iHMBzfu}b`f_s|;0QQg^@y3!JI1pod-RHjWArd|;wc-)SwP8B zLgyIIo*cla{37R!(|HDY5jvkeyF%Y}n3sg}xD@cXtaX>gA~@k^^OA6V=){ZeC0tm5{Ty~fz&J}J zz_KI+M|gJJp&P`DXlv;1{)GF}3}#l8cvkQ_IYUD3HUHcbIxpqnoU{F-j&n6M82vVD z#PX3F16|e>ikg4SqWF7Vd(5K9q#4Bs$P5V`*SzIqb)FN9m$E+q;R@6Zpb$V_YCAhG z;p zyDq>xskz;E9$hc>vE#fTEn-w5lCW>zbyC}#tHNug-{Lr8nJ0KhxVqZmaJ2^!5a$_I zC*2VtgbuFPDg&7X;~*#1RZ?aCWD#%m?R0%{PDK{ZOx1&~23eyauOOmnnTvtXc(y6M z#ZajCZo|#@yB_DLHDgeNk<|s0j z&5a9^2e128l~Jm3SdP1r;~#=UVp9DuulNa!H#Dc^kePk%l0TAXz9^=x0RYTXx zHHscr4p(zsp$mTe(^gH?Z`)_Kpl9nfKtPH7AlC0pt9usxfSY6MM_zojwEjsawECLe zyp(lA1%xZ$aHt;hx|Hi`Q*j99ow59NZUO&{Kil-G3|hw@#h6A-XH95+yV0TeYtQPuvxToqJu}(o*!ZIjhdd{mSBa@c(g3$0#k~LkAOJ~3K~#c1%n}-Z z)rS&xP2^zVlK_fQa7hfJjLf87cn#;wvdKDqc#2sN%F~FUmyq|acbFA)h(K^o0Og!6 z(4-)6gJ&G}J4{Q)I7XzH(RIS4KYn?K@pQuP{lUr!K}W|DB~%z{pCjLf{A%8jWm5as2n_>kqo zJ0P7=avar=i^4m%OI-R~4wU81#d8QIkXBBpzpK&+(i~+$>z4ZIop9K)&qlyW*+2gJ zZv!8G4gF`n1L;$rLCgt=EZ%bRA}ea?DkAana-QTkF%W=!6|mo(;qShP7hg)iETyk_ z_YBk>z}+6`c7V6q?gaAU!47t}LFDVP$M8Z z!Zp<+CZsrnIRGIDP@-Z@!7P2nUjNa4%*0pN_?)d77U5W*Esrx7VGAgH0=2L$S5 zx~4OVj*_A}o-%SK^&)F3OlD*PoXZOQ76q+7NdC5Y(X|*wfWL$G5X)t;MDyi$A)yitwuw zR^R_E9ax3q5jrut~s zszo(O+1cDM3aVq%{C4SeN!Mwam~Mlp){?gQ#*7to5|DbCL~9Qd+4xqjXa5&t3yVH( zdA`s!2e$6$m{M&(8^*+DjN9IKC> z62m|-<^EML>N@dQ<;>{3&?YlW>H~?=DxP!ce2Su&r;@wK5^Bm2ke{4Ixip2J3G*Dw zObJ$u2=hx!n5CdK_PY+uRJ`YzGp_m`FW;PTK9AV-!1H&W;?2W^`}+|ee0T@niO1b( ze*Eb)0XY?f&N+wcV-F(2^`S$`gy0xASDk|D$+%n1-<%oe^NbuLrg_1B*I~cw z%an3roX!iDMI#V8kHcXHW~nJY&jN@fk+34ubX_PBJAEKbUgpy~kNptvFijxGn3jYi zq}KT2yeL>9ASK4bc}D0444nr!NvI}^fo4%dM~VqnMor4CWz2KL)uGq%&N%Kn+>aS| zXO9p9=E&F&4*k&K<*NtW-cO={_Z_bHJwDzI$m0osc*XB-Pq;ho&<_J7Ogq7Qk7Y^l z-eI0)p1UB?0)ZLMwfl}L%=8%FgkbVc-P@RfX-vptDs{4znlzeD`DKovywsIPK=rok zo<&ZTfx7+(PLy=-k#hi$6^Wgjv_xL-Z?@1H1G&+7y`;NC+ z9wqddN?sMa0f;l==Rb$=@*D8q@!ePs*T~7~a~bf;v~m)l^yI-gf*qslX7uY+{ax;XBT0F=m#@IxPRhiAt`MH(9pUQch`X1suq+YY_hsGn0how@(1Y>}&WzVDZ}I7G-63=V^KwSa zL8=PKOd)hqlG6I)RAx!J$~vSnFu`P$x&%@NmTAJUJBSe=XSkH4^aYu`jbik3*(znU z#y`nLFHVm}sHC!{v+4q*yvSAF(a3$Rd|zp@ey(I96=6T=Rpfhq{CByuk&wFU9FVyZvI(}8lA0bVp?kmbUZuicivW%%j*0zBa|?WR){c4Nx4+C)}gzW#O<%^b+YaYmp(Qtsr>zDtXI!` z{Jge31Ok^Z3848Zzh5BI*1qz1l|FtwUQ3VXt?hZr8PDH;4g)1JrQA+QisO=0`IzTe zCMg%?+t7DaSuRQn>K+0vKswCT2+BE&@CcpbtSG=FrMcwZ;&DkaAq20WXAV*8u;KV1h>nn!jKfY00x2atoF@e5wDL;8xFjsIczycdF)s;k-aKGDo$=ng*SJ3~xW6Cq z@vA%ZeFs8D-vz`|#KUP6kS7-JO^g|f)*g4>;jj}g1cqauF? zPuTU62PzZDn3sg!J3LruD@&Muj1gCd1NOUs@oasSl!m0`)l5OR2nm4kaGG&-=-~+% zrv*3N4$qD~#$`fIUI3UnCtmd4lJVdvsnf z2Al%yImHz4PD;FWUP5x)2p?l2Qr3?|$yR{D3TtaJ4Rk)Etah#(T)vBP$T>+#Gl)!9 z?!kaX`_#uX)Fg}IL6g}Bg#o;bfa_&^Spna;u9;RaFG^@x-Gy^Bd#kN`dnN;}1G^yi zO9Ubh_B~1z0o9Qpav)8?fbW2Tu)O#X_~1i?_uqs6%(no?r$|14J5rBxl*m$AKkKkQ`iwpR}7aH!bIrP2*mq-eN zgVtGRplBz`TxCzL0#=+K(0h+=f5dcmE5;F!c-JmOddg+0ggS?$iXW>CB!Ft74Ck%` ztC!lcDN@Q{ryzyF7lWRJ#Mjx(%mNMx!1bL}VAv0civvd~xdp@FJ?6t6(|JOM#})@u<&k3WW7bMwIu_X!LE#B2VVfw76pt7W(T%>>Mq zkX<-_oh92=9?h0AyCTqUroLKy#LJkO;aEPu@T;H07k>3~_%HsGKaZ~K@jZXvKY-u+ z`~Gfx`*(a7e$VgyE`0lUeiuIdt)FSiQ(S(f6!En$e-Xd^h0o)+fAjPB;&1*R`24Sa z4xj(k&(?55*mzefnpq1i)Q$8UZfaz=3yFX=*d8}OQzMRZLu1W3)ZBngAm>)dmy@;@ zFS}Q}@aj-+q>b3m5DA?q~|X?S}Laksm*?JhLIbuI~J z&DBj`FK9(8y9p7Ml0SB{8iKy=cS&I=gv8|@#hS@=BifGgcFx;Alri5JSXy2F@txL@ zHSNehGCH--)ssvi2O0xSk$BsAT0i+wG(hQWJ2UI=El;fLt6AHHezZNCQ7ZJVJ2jWF zx3fx|6I#BFX6zodm+Sa$*Ft$+{Gn}ib0h2P*6PTNe(13~9!fY~i&fzWsx%vtI!ewT zNWJAsi!B}tE5f%Km6)Np9$Lp&Eoe1VkaWM}Qd&)(CEruZFt#4N#}YF-r~8mTgNQIq zBH+fDkU68%kovQRv4_E9*NX@KJVwk*QX@dd%U2H=A0CiX!qs7q4?lW?`@0domlAE? z`MaKhh;W_+QP~8tv}8q#m(b#2@CcnR6+lwTc=}`q=Lnjz3b2&c%9(LG&p4eXjMI$c z@qnTC#n_*dc-IB1DPX_r(FKnfC12B#VC}qm^MLoBT>+5tXk90Q>=F}p3ZNGhKV#?} z`d++>L{b{B4^9Ep9O4p@`VKKl*)(s81X-RZ%9!Px05@P>7`q-=QpU0*j8nv6-{Uk! zbX~+@*W<|{;iDxZgJk}K$8Oi-{^1U{cP9*ehrSDVa_I58-vM(hWIoLc#%V?udbkN- zs(I#um@NGw0K496zP-^iz?=&hrzeOyM;XN8cv&;*vsA^>-bUPig}Z5!`t#!PND#Z zy(Y-A0K0jq4Gq$^qr@#kq79Gvb>Uh_@dDPu_*w4RAXGg$1-ox#4^|Bb`Y+ znqd!mG62Z~eh2tL6#j0f%6V73=F)D9B#0n4fv*C5ipXF20JLPV@1P4oLi35hOyCT- ze6C$z3l1=+lF1OxIV|UUbmY)q-C%xD)cjS{fsVTJF%ZMKPQz_ljjr!7&vQ}E$w}n_$IvJo*)hBm<&=mJiNHKbo;eX*@CfL@DIrcLoN~g| zlWXkv9lR&Z)1qb4B>&on9&=0}&6{^2=vXwHh|(7v!LdUo0V52Zc*Udh0!pX`duG7O zQ&ZMTM>$I+1@DWYmXqRu<&0t1!;zMYJ2)(F?lFxs`Ys@NkHhs5uf~tDSSkxi8cPU- zxDeQNASclV<1}G^I3NU%Y0l_6+b=DeDwmW3?|=x@C~~Uv3P3ixD5=iQnXCoO(~N%D zVIDnl(ma1JG$QAs(dpzG83&aVsCv|8zF8XKyzmM#H_b-hX~^*Ls=pxZH}WxzdR6wk zC_8MHN|EK(BHl`30xCN!(!4S#-aO=@JlTjpAu6({O+*nE$%Iu|d?}+MV5;&f@^BSn z-zo?-0`hi1O%Mi9P{7TyR1|cg5?bxZJh@HCCEJEbpadk61!0DTOOvt=a;_oS?HOfo zwx|GOMxyXin@|Df#(05d;mI*x-KO!J z6KzkuFnAjyOtTi=W^!x0VErpJ+{|sEPXaEKr~s|z-X^eIT*jkSpi98Ijroc=)u_{I z$tz1*<4LEgz}F^K`d=9V+WC1rh|N6LKbxs*dR)6%|J1Q4mKa*~eYwuuqU(Dz`Q#@_VNR|2wBFQ0*iL1xHpukIX zn*q8M?}V@8>NLVFrBqPV9T7T3Iv%GefQ%|Uj&s7pX;R>yj5oIvUcY*clrp-$!-p?! zaUOww7?2Y1{OJzI;~wLj6yPX|a-K4#c>!m}cs}FF(;EzZ2S);gwj|(sFZJ0SIYd?B zEsuX%63*ut)3m^m!_(`%jG2bwou^61jBpqN_WK?wNvM8K2`Oj1e)E9oJOfV;7`gx= z0bMix<5dC3EONu457_q|#yO!20_G+%h(p)+I6XWFDs!@OMO2o6g`^fBU5V>NP787t zv}ET9=ktP_;|`3ByN3z;p~Io~2*DwGk2z*|&lrXtLm%+s<2xModmN7rPp$^^yB*SU zD#V>N2mkswAb1BxhVwy`c#`sSL+3DSn#JH4C+`KRU5U_uTcu=b&E>Y7Y1TbQXq{CC zOHc$up;scgSF)0Fs-%;ba!F3|goWYRG0poX58_3X^s=rUT<8QXok&t9O0CaMaLz1O zHCM-jdF3k-y=_!-TsBV)6AcIPR1?9V>hfV0+s6QXVRT;&cilIWPJqD0bX_MQ<(?3R9(g>2oI_e>IZnBx0O`h| zA4_uBmR7SWp((eES z;bpo=i=wkuO6+Z3X`9y!XsBLoO&gG4fKc|tA_oC0VHTy6q{hnX%_TAfKvT{we3r^R z1T;sy6yyL(gqTcTB*P?89@>NzYr9$_K|5o0pvh#*!iQV$YPb`9XUO>{lDp3Gh8jH6`!!4 z&>F1Eb-W(05;lFI4{tkW^kK*COFQHyM9bfeByYdzxaVeYxw+TshATx)PhVO6HZ8p{ z_d*lx{Y?n10{hk|R6?Pfn-~o=dQVwbrN8!DGivo)83a(dCo3Ja3iMlOclAu$;876S z1$EuR+$%VuT`27kW^GeX&WDWQ8L!U%=#SSCN$I57L1A7xtmqPRaI<> zt&T@~b`AHZEdlVFe74uM%__W;us*H_SH>3G8?>Fx)`i-{y71a{v0kUEl?2PtuYjCf zCS)Bg(eS-SxLX;R+CEp}+B~uzTYJ(i`k`&+qXBHT!Sw(bKPuzeGR;d2Wp&?%Ij&w3 z4u1XY1|f9FBq8Waj1so4-Wu7voO-vVzIWCK#$<+Y>!a!fMr~#|1WL&;VIML!Nda1| z*C{2W2AIykmXf;k#S?$I_!FnoJn)KB^Ng?3tqpz zM~qp)y9o1QrOg<}V~dLJ-N2`MdDA|t35{r2t_FJIpybRI+Bx>eQ@xBk)z;*sHvy&V+M*asS1}FQ2{Q7 zBdFwuoB*v+jU1)y7^~-%EL2>=gGKOQ88=nxjsMGL;(;8IIY5j{4x)NHWOl_^wPyZJ z&WUZdh)P zEn8+at7Y$U^0$-_sA2X_%e}$)fTG^h1nd%M9}rHjv3%+4pagITfZv0CFZc7Ie&z@o z2GH#rxFBBVm<+7XMyxVFvVJk(LWkvahyHMl;rbfW-D_zZYsoS)Z9qb%8Q4T;a;@-_ zGN=m(At0*fHmjkA6rmp^Ji|df<6unlvXc3o1Ny$hvMg98@x=R`lt2V}0E{$^1t>Fg zom5fK5cIp(Z*X;Ujoq%t`6QJog8x4$dzU6jawJXcQvmjGk9<^B_smXTxD?@qhZUjl z|NjCWc-s|HR>)nM+2KylOm|mRW<2308si4YJG+Wa;-g0#HbVPzmC&mNfs5xy{D?2$`Ep+%t{ngX@V38l#OsEnF3 zZg20H=ShqaBs_9oW_*8r#Wa0IOaU*SFZlU>*XK&4Pf7vPMA#20g~$28zV9eGBTa_3lLPwi%wyV@rAz z;&4h=E%bQu`KUuby>RM;95SZY29*$;FlOPcU5U19gA*#dG~RMdn?kOCgPszoEzD@j zc@R%PyERKlp&z7R`-z>?9TA&7QDcT~~S3H1!T5#~oE(I|2h z+7U?cMO*-ATRha-!LA+SGfzr@%@e3Cik=Iv>x=)8LcKX_0ZNA1j6+2$_ro^I@BOYebB=WX16 zOIdjS$f2Ra&vmhEk38)x2?5=N)4O3>@v^aU4kddL|4u9tF#5@9Vc;**}k?VjS`bU7Vh7W7ju_ZU#B8 z^b<+Bal^1lkh_m(^}a29e!Ju2C*QL^nS}HComW_1t~A5Ezo#*I(vydFlB1<(o$!~jq!2(MtGgv(iEJA8l zkR%IduNEHb_S6DKVP{JxYM!I2IpQG%@szuoBLc!yHMF~`XG+I}8H|8QQ>H32Vwj*z zNxWT6IWGV))}M7z|X(F zhC9#WGJQ<&e|@h>Rpk zc|wW--`+M%DdN+b@auG7KL}ecNHNxES7-nLAOJ~3K~!N~7hEn2{`fCHQbsrBXx!``^uw1T)M?eV#1StcTE3`Iz!DWuPtP%K@m3uRM398@)VJBb; ztunYOrz)jk`#?){F;`IDK%6DSog@TVJmsn^gVYKG&Fe!R@fs}A7 zZQepm>d2F0T-oi_B;agQ(puP|8R#oBqHZ;Fsh7gdZ2{~s4&#bt;z99qwVcfS2E)^jKM2_HT7XHD&ab(OPf`fU% zJ2r-dxkvRXv-9xBMvL~ZJ09hI91@+hURJFEFQp&jG~@pEj(J^?CJDa}0l0p;;`Yz) zcz?a&^Vb*5Q^In+An!ZUG@&vhC19EYj$DPtIHMLRr@2Hi`jo;T#UB9!AtmH}@6oaf z6e!z?MFsV~!Ht2{ZfCA2$AJ_lWD?^I*9uawIFT!Y>Ue0PR&&~*Ho3=(noMYhx-R4Q z&HPd7@~=_WH8ONRJvm$1(DPIJxevO;Nv3~-KKD#cIzM#KuLua$_*!MHNiMl_CHFFD zCS(8%E2Cs}LYr4R*f9xRh)Lhk-f-Xb9KkYZpZ#x1>yoB+FPW#1q^4)PE(WATNKt?T zW7K-f0m=l>`Hve6c7p;{nhb!fW7jc;5mbV*P$>dFoM;o6o&$G%@M^Sa|5Kw!3p*oF zQ+!g8e8m(9lY(p5d(yERjzzgP1s$(IEl-$;5EVPX7K#~69{b9!m3SD3K>*eb;A{vw z%kJKC)}`<#1Jsl_ZVUV)f%^drZD{rQdUg+P$Grx?BO#44jJuAUpkTDrl^|h$y|?w* zJwF--*WPYuF7@vndShHX1AMZ(KvMn(jJMq#mpxf8PjM@h|aD0+Q>?EOT9b&A895L$k+ z4BTF1+@)xTYtHbYrwoV?neSi=eQ)(J&1_0?OiMz^BThmDk<=b%cH!`o07a?d**GyX z#KX)0qA1}YQC8Cm1o0?RRN*m1y|#kG3`~-1Za#g0@DZjMVYO9kj;RUCmb##6qB>v95}m`P zXgY+(2r(m0V&EwG=nf(hoxwue4T(KyF1jdsrFqgjc9iY^q699>q(Fx@?lRUfMWiXA z)`IPRM=esZV44%svVh`*S_Ek-CJTYD>g+4nZtsGZqb1S=p`Jt$O|$AoS{XyJ61ps| z<3*75#f+SfW)Lubz_rtLlUsuD^0)UL`!3N{F@}~Rz|462{?@$gF_3!AulV-MH$iX@ zz~y?uacHf1T}N{guRB6Snj>n-*!R8ZSZ2^Qd6Ys>Y!Kn?UiR_ZDk~zY$%`tD3td#f z7n!DrN>Pj>8b|8YLjCOq_;dMb4_c-|0d3FSLlJCe3 zS{t}?9<*LTXXW7Inf)+t`RR9Ki2e#5lord^7vs2u21!`)<2#V#0S9tpM`2W+&}nrL z>fJMK44YEedelSS&YkM(!1Y9jk9EKH-ybHVPr{&#S>JJ9a2SQ+;d!kj;P`vH=G17% z7Tgc^Fr_Vbld%owEB)MeZ~AGt9$)UJwrIzT{LxVv^9t~pOSW%LnWx4eeLGJK$o=M( zY@scc3rH38fM!%OMYL^f3o|k7MK}Xhf zI^%55@XsDc66Qp3PwjKl{t?_g$D45b@3c&s(Z|t(-NO{+^AMag)(3#OC*$y&FYa+K zZBt@*U!3T7`21v*_)XguB0cTM!|hI8e$v*DZ!t7SpI>Xm<u0c|lJLifi5(oji=ALsEu%!35n;?;sDCI#FYYxC4e*c2YWx@A%@sbBr zQSg8H6{rRI*qdi4ObK5;uPC~A#u%{Y(op1&tPuq)UY~6%`1RWxjx5IL>nh&}+B{1-xrDig5bYKei_Nf-yy|#S*cMG0bzezq*ZA=vdMOLd!Q+!A-sC z0D=>%N$9ZcwMrAMbeQJ52iUz!|DB=;@$mx8pFn96<-DPptH@psIcHiVEZLq_4hg>x zN%E!18^3LQ+`i#?hTBg8AjAYp3H6shA^h>bBGnBv0a$_n-3JCIQk}knA-E0T9pD?l z@8EO?mW;T*BmDf|QU1%n0B@V-s;@xOob$LyNIukq9!P|qyaV$Dy5F%xAf=$P6E%gb zYYQgLjT;-QH(2U{>p7(N=PF=o2&g3ktm!-&qRuSqEye_9K~EQjGq%!$+>~Gizf{^2 z91AftEz6|UY#MU}k%YRZ7*TS@vCBG5AxQ0WH5ynNMoLL*uMj7F6_E(b zZf_y2y|5r{EQL6>sJk01(?3dG!^WKBTs`VI9> zeb|(t{V380u&ifBGa8soXP1UP5NdSk@rZ0|ihDkJ)7#pk1H2ib%TBhoEm^d!bhjc; zf1R#j@)BvE{@QEMAK-?^X$@Z2sLs*BKv(2J5BIYJ!A=m1zDFN*4{Jg)21QyvJMVey ze?_52`>RGv^=zxsr~I<gnyDd)TZf_td=OUB+Ys+dL_gXbYMfVRVoK3%8d6 z8)Y+YH}RqEZAjV|Kdu8gIA!vq38(WHFFbM=)Vim1B(YA`*5A=eWJ>s;5MkI_WE6;8 zJB?F!LThdH1N6{~v5)>+qgx)oKU2=n3ikNoMnmNKW^~lX&dzo+f<5@Gm_KXpS-~26 zVhoo~gVLYh=UWJ8WE4(mdEQBFg;e`NTc28Q+P*Sa%wz}@FehpkT^(BpYb+){?;CeN;J z+3tdzuqK@p;nX{P*cJ>i}L+`-x1XlzU>7u5Y{;%7g6kq zfV>y{{Ob*$E)zIJ99huPk1XMsf&#CCIICxnf+`xolnBe5FwY6gGU0NWQESFJ2dtN+ zfzj0}_0qq;-ob@2MZ)X*h5!N1t3)W3kW0msX1u&yz_sAdfBuGT+p(sAbzNaNgu*Ww zx7!UCO%Xysh!IB?u(lYaG~Z>m&D7QuH>G@7GEFWZNLaVYD2C9^63Si+!v2nCd1t8w zZr)hin>zDGQ_u;fJm|fH=8C&TY+hMe5EWbxa8)2WngN;sN(jpf;&K%QIYf|_n(jJC|UFrzH_Ii^L5X9?@7*XF}QU9<16Y-D# zfEY7y2|!RUd8}Y6Kr8_6;JAZVM!057>1V|EzeoAUzkvU82a%$^r>mgO#|4NBIL!() zCth@Bh)_Qf5+9)d`kzp$g!os4mfF@7COYTMdSFXYrIsVCDEG0ieR0$SOX^wJ_y2>IAS1T0I!dYRh{#3=L<0%2Yz;it!ek~0{B zu`tau;=IDADoOr)g+-fiWywzuL85#Zs&uOtog^wp5Y{zysclu<)<=x$Ij#jU#78eW zp{0&AEQV5W><6;)mKenQKFwlGyWckK+pcYr=$+3$e8%hdH|+Z%Ui6n0Iaiep1dTmK zF`z>E?0(4jE{ij80c|eJQFZ^(UNMh!toP=&hGiw#K4PWPYAJ%D0*NlO^vI^8IoVkD zP~i`4eUW*`yOhz=M}~CHCp$Qgzl?6ivxLHrcc6ZT_4A`g{no2;Pa8eUOT*jrtFz+n z-eQ1c352T`P9#2a9`90_Y?L8`$_Pqnbc_p)x%%Cu6 zKL%;gUheq7fZT*ML74j6UkcKJw1MX?FuA+5k6m~^3#HG6*oPBbAO3CU>qTArc$8bo zo#GD3Uzf&!QMqfkd?3_Q%iQAi-FSM1A1?0i%H#L`*0$N=2AY8fI0@jVd)n{}_pW%> z{D=0QmA`Xu=ycLKeBE2r{Ph-*F#^CjLr$NVMzf>Qg56c8cUvo6oS?Qq(0&}cHnaod z+-rMjJoVvu4InO9czgGpw{Ubf4tA}5I7e=bp=A9zzCC`th};KT&p5r@xQK8O?e8_}MvU zp86ZFfrSiAs4rsnXZr_w!uTsYCb>a4v#pP=e75$D@y+XkT-oIwPXZ>yEW)5FG_4W= z0*)+(L@1(rjIp!n)=mK@YtyHm8Mj&CcH03$&w12dv4WaOn7x)jt*RukxsgEf%rQK5K?)L+wikD8R3`j4D2!HvuVLvL?G~xTZEkp-T-F)6h>z%^0C}ZFDWatlEK|bme&D_rEQzp85ql+U zhbT;!b;hzT_{%Tf@$K6in5*Q@$3>JV64~iEGD^u>7LGt6i9@wktV_gw6Ja_9$?rDW zEZU%^P^oeuh8dQyag&4K*NuY3aTSm?-Drsg!+?T$scYMS#nBV{TBXjDD3F402DkUI z(m(gqOC|_nxd@DLxgez3!Y~DVtd&M{Tp`Y&7!gvGwF(guxG-p1niAEy2(>mCZ&7mG#+eSZQKFja(af5eh+U`V9p9Gidut3@tb zi8r704JAYi4_E%;YLrtHsLNWi91k{DjF4uj#m!P)F{FrEf#W!^E)hR|T`@138=Vi- zEZ%M5XQFgch&T>0ye-Ql=_1N=rzqay6eH#-0bo_q3y28Ql91*ZtX^G^)jKx=*8J%z zcpN3?#)o4Fh?7M2Na{@1vXMIPO@QIX%K#~D98}S^C@GT_Y0!m8o>{7Rpp~b@l%!e+ zi2}Ucw-$n)V!*u4n3oyffB7!a0x=?J4R_=9(-ef~h6o}vcor=4B#}I9?KTBiqN*&d zKtvud--=O=tCW-^>$}RZVpPeRVjy6Q-1^>z`axFecw)l_-+>X=j9|XRq@{&9LZ+T0*?K>eUyBmc6!5&D)Td0MTLk025Kwz~N zQ0Sgb4MT68XKI#x8I?`|aGFB%1P=nMd3XJIoWd!s4Ju4ODt5+X(S)HclQuXHe^#H2 zCQ*@7s`MW1Ms`zo-IG@f_jq=K-li)V{(I~8aQ_D6L^gWk32j?(1o%Y%bNxFv=`1kY z=80w*^W)Y8IPQb=s5moh1U!(P{MNfE&v5O1V%Z`0*GQ|+ZwH9;Lh&%a-1o8bs~_uY z0kAoLIJ-BEM$kAv_R_AgP3>0HdhV=@wq&0Zl+kW~nESvy9AZDHtaD4;jgm@7ez`oX z2fc-{M^em!o+iN##oeZib<0kFt`B!~x9=W>Gi2Y^#Y&^|Q)y`Nb-Lq$(j}Nrj9>>GkPsgS) zli%p%2d|+mjs_na)rrmz_tVB`TbSLN^xg3EL;ctjiWqoDr%`JfV_IeuX2m-ZMLXx( zygaqlc?QJuA|b`wc7WKSIcP_pD=2J1FE?}Gs<>Hn1(N6wg4$q!J$nB!m+5KpY- zAi9VfGRzAN87+)lx>r5~Dd#4&&=tj-MX|fxcd2Jv3XZ&ci2wp-1^oEK3t~)ozwOw! zI|!=$@8X@_kF1KhylSraa+$O|E+GX0tCFaMsEVOP6y!thbyG!uUPNFmrQ$dw>^}zJ z1Y{OMQxEdr)_!8A{xniUr) zfUC4W%!{Hy@7OaT=PI?=kAlxHGp0n?8qG=AEmT?Xh{Xd=3K)bQ*+FtKJd^6XR|RMS z#|4xo72u&to}hqJN)Tlp5CuLcpVkfDN<2Y~Pv%)?BoIXbR0_f6m|J~!LM!U5HP~xm z&HE0BlSIXEsR*d3C{jB-rY1WH>UB5V7$31Pe`&IY@3-|ET3VLDUkOtQz&xYo4Ep0g zg8u0rfQ3NI3@j_SM%3jKu!$l}LG$M0q{?>!qU7OIoaB2*09h$Eot~)C04Ts5as8M7 z2kWwcnNV1UC`B!;wQnQ*-<_;yGtMbNzR%92NJ zx#HWlsR1rxo)fq%IBxF}ejWlU1<6g10i-$c0AM8{(`WZj} z^S@%-4y@~fd7cFOBM6_KCMg-oRg4CKB*N)(y-L*3y&wd`J(0aaYN|_GN7cvzTkEnA zdc;Zr(JgIMV^&JPBhuMBSo~a?}F<` zvYi)H+_g^TmS0Y?$w@wIMoM=d+0MGmeQraS*G~5vg#U(!F`TdlPs<6_16wz z_l>~a--nlD{(c;vo!JDU$9|4^*M4r;&HJf2Zmz5u?cVNhQ`ph(*W?9;rAxJU<*~cx zb!mo(_T4^oUNAJ_+3QhRe`FM1pl;vQ0P>9GbVb#79*^_h*0wh;xic7UJk66!CWE+k zyLP!-TXltAQjH$p7~3`6!_m~U6cA6N7S%EGld(bLJw6Cgvh{7eM+diD29x{U!m=HdJH8F_f4i)Zcw`!eO+-J;K<8iM#f22Z|!G9Kj{&XV`1o~58bNl?WqrHJD=L;%x# z?-(OeOd9fyW}FVqcqg>m0!7U`ZH6i8JWKM{{a|v#jDZS3U0E*x*H*ot)DA|+taZ~Z z*R%FHDH>WDf+%nIZEqf{eLp1ZJgA3N^BU)Q!OL~Qeb3ll-v#NJD>zHJv79TW5HM>L zTqzZi3cg+^1ogngBq5+d^I&hA2&$!26lNSp#(oIM*gA^>S4zeEp0OPTFV__@MI49Z zTW{NjKmX|)NI{@1RgOqNQBg%G4UurUOzOpF0fE~N5D})BFe@;edAi=;?;rs4JmbC} z0D!`**a8x%FvTE<{2Jm8NGN>sibVkzoaQ8D>l7W?yz%=XbN7DRalapUSrfno+g>oI zh;@pnAY>M$=z3icr-WnA`1zM#@%`HymSq+YJN4wnK*)Iqan*czfGOa%i8pRLs?Eo}=@=6bq9D6Z3ox$$PbhgpePh%;0ULnjGid!0 zoIZo+Pr&pEJbeP@7x1(KX_aznQPM9EEVo}7nMP7D6YR>>OzJK5F%dZfJnmBg%7Znl9Vx=Qo!xDgPAp5gj=*njDqSex!>~q{k{WW$wi+h&@^kU zaYCHLvn+dBX|xfeDUU+;F$Tm*LHw8%Yl7Q`Ldru3sxJ{YBZ1%-gvQ4$l}GqN5Za68 zX_g2BXoz}<5pkYX$<=6>EKw;b66R&b`sowizQ2nRc}{J=XEj2^7ywdQV^%B^#*~ys z5H!SH=(E+EC$#94?%*WlSx)J;J(D3=2D-P%@>1hMb?LX{S+uoATwlWXf6Gz**8J_XGEjZ4~_iaSLWp z-p}9l+)+_*<9m|qeK_1RC4nC<>@(?qvpHyCJzk-Hcrc%?^UtO8DdYIS3m!B~L-5C& zLfVB4%R48ad7$jN!EfD`y2kY#y<44w4N2Bz>u>Tqz*$3Mas4+!hRGS z+m3aaq@2*6v28o{`;KqFdQ%cyg0$0@zsB6)MbBcnxu4Nhk$81kghWt~XUAY^4PvwiIu_Fu+c?Ha z6cZxA>Ss6kLzh1hfk_k(Zs0}d|^l|DSexpOl z3$FIZ-!mC~Oi1P?UR?oW*EB0gT#su*owa1kJ%0OpH=YuaVM%Aq1p(ZeDaSW7{Of z#-ffsef^A_4{V#1pNvV;ZLGB2lp2CIh(yWfS}|*MfH}xbZ>yy!4aq!}+-uoGjW?a% zjo!K}#%xZ~j1*JHNKyGzmJc;u(Kgv&DJbo3-3VaO+)Xc0I-|ym+Ggk4%I%+*!`ka4 zgSBrDvgmU`~RupX!^F(KePqVlsDZB{W*^}b$5JhxYrk&5bu9)i_D$hR3UfACOG~^ z|HhjBLdN|UTCIB@QoM@Ilq&~Nv?Pk`A^J7mmZ>N8Bnyra89?ozvuAROXJ+=BX>EXWp z`Tk=gO)m$WPlOxqzcSp7M0ose{plBwC$sjMPwnUt3{U9Yy-wS~*oenwj%(K&D;`e7 znJ~^zd7u^P>v$_VeIDkBwR8{xsKCDOV2DSC(b3yeOsZV5&MP5JqKH)%gm18h4BI-< z(C-ik2Lrj3CS;ot%mcug*D_blfh>JOMCq6IGLv|+ouFpkDDgOn@@wJb=1~X9zL|3o zK~7P?t7Do30z$x)0+uP_wr3pMy$iIZAW=m<4iN-V@e&wcri#xY&o0OD$u(s>4y_5Oax8YB%V=c*vNQr<3Cp^@wL0)(h}@r>_pI~X0T zmzf(nbs!)m0q1+)ih9O_`QK6%EHn^C16LEwBSG3H?#cnf6nIho$*Lc?J(RUhaLE;wHH>|l0H{G_ zodA|91?K$=DQUGJxd)L7)6A{}Cbi$X(oO&w8vUfXzZ&{YqC|TpWO52q1W}ZF?e=@r zTIT90M-WfB)EZ9u*ESGp?J-^fEC==D zmy3Xivg|kc$ckwq3jX!;Cw%|*2GrVWrUQ%$i3|cHZG~YGR^ovV0pLna5e{k6=ZdgN zsHli-k6}%7co3Wk%~%876KfW%0ogNZTPxIzVWwXSy@X&9DWN@s?5`6-TDpzRpqG*L z)1yn`w`6vcda2Ec*W=Fes2JQj*Tc{{6PHxirGCDD3#hF7&TWP1dptMvPI`3gu9*9Q z=+x*0855$L_9wqcKP|+-6m#wacpA4B;LI}NqNqy6o7_2W#G{xbGtaS|%^q-Wmq^ZbwPd;}mnHF4~WQ%oKdfhX6Ef_ZQ7BZ0g2 z^7G>lO>;thKS39d(a$q$LA!i>kXLqwd!Mxqz$vc2DKyS!GZ?v(O!he4w$tqgFCL%y z00`*m{8@jW(55MW<~;BW=;`%OUT3B4oLXS-!Fz7sse8@MPfJgpZ+9_P5$p=;SDssS zK4uD18>iEvXf%xTaUtw+u6bZq7Wr}>Vn0_swW2fF_898xq|HU!I0p9K8}2NEUh#d_ z@5k4fBVcSF_bbPJW8~B4NSX%{qto}%(l{!y1ONDqb@l-r^6BsS<+qk$P5O4b_V~H! zJ9VFfVf~)I5BGf1u7@@5O1)Qq2;y4Z*N5yivw7hXXHq`AM3kt4_C~}DjQy_dwgtQPc7$GoT ze)v(mNgzy7!r4pC|Dx>UxYcm~WOL?1`N+xw2&k?}Go$*(U}0I>k<1z;jf^Mv)~ zB4M{6Kn*WFqA_#~09MrONZ1c~ZVZyUzf1{Ik~TD&$ffgAA3;b!GgedM4XVIV1j5|F z;5a5DXx;C_Se-{x5RwY>qNoQDF=B`S>$uH}DArt5j!+pBTASQTy4VpGg{u}BSmagPv?z!tU;Bw*o-_$FHszfg7@zH3 zDs<~TX_v5UR92;2Ld}t8HGUY-9;u{ndHm{wBT>+6sQ5a+V)^r5u&xQ8mx${`SQ8MD zv8IYS7A&z~NsQ~nn4_efgw#FfK=)wkj(y9RC#`GFKt3{vfO(QqZ8?i^a#_XDXX!nq8h@2=>6k)Gwar=h zNFZRI64E-W+`uqTc8>y3^@w{}OhA-j389y$fLw%TWbZLKN|%VPJu`a77~2kH9%fli zvQC#d;c{K&d~FkJUHBh9FZkW>Ul8X-`Gb_JE4H5yaJjBv5Z>N5DQ~Og-J}nO*Jok& z68&SPs&i3~JpmLFiuOs!Q)#Jfg$XS}rWwk-T%&xt7|5O(nR}RgAhhd1Zp@n8()kQi zSA%VQ2?Rr?Z@)Cz$N$y0?Tw}=JK^F%5>e-Q_|b} z{5>;rMpM}Bd3{jW^}P3|P1K-ConB7LYbFNv$Dr0yM*!rX-z~q7((?G2E^OMRd~U+y zMcfJVnb+vq=iZnnFK0;U%FN?c_NVmg^`2YEGZG1M0{_Zx^e(cV~^{mJ3*pp{Ho=Z>fQ@KjMBmqnUxMf1ZlmcGX z87UF2OTw2=7e(*tODX%(c}EwlMS%0;V9Vb3Zcp*es|Off%7? zWGc9(iX~)xjTv8)go!RyfR`8o7!{KUjWLR#SQS}5R|)xkf4{-PDn*Dd*tU$zGIapD znt@{zaHTjQM#35xDKRD$LIZ%rjF;<*FV`#bUIY{hfGCWD7qBi82AcDMZI_VSb)6yk z%`%@gOF1M8K>&}c;RgiV?>nk0y{ndwicxYrHw{w{!b~yG5<+Z5xogS+5gHH`R{@Nx zRnYZoWlXWxR<11L`ts=ol>@38S@t3;JEqvUeGCAqgt-T;J6(#<&~`}pJOv34uWTs> z+?1H6R5W43)dmLxg`0v-L2}O}A36eI8rGjhU^UDVr>xr|Brp`iPv$5Fgp`1m1CGX1 z;ep^Fv|_?Ts)Ubgl{UkL`#tC4c z)&tB17WyBjsd-TMBjY#<<|SdhOsGYo9Bx|ra9t$@;mB3h%w-Doss4wX;LaImT~~7`0>+>%cm7qim(>J1+lv6F-E+6e!;8O zMUTPKHZ<2gkpoMVJaj+|8PJHG0Etqu=pA+ciY&!we8BSXxfv6b9Ni|~6!KamFP$y= z!Xi%k9_D2t%jGvN1Gv-^SQMg@?7W=godt}hyNrstcbIh9#Y>AO&wH7~%Vp>sPeah% z`Q&s1f1Q)>D?JHqC(~nmczgdY`;-%1n!*HOQvDa_{eB`=d%?5ikn4mQgRf3GZxUia zTl{{^y9eEQ$crfEC0CR|F|XoPw_LjROm3rQ3PgLiuJjQVW#=?Y{uH84yKX|P-(Ji! z<;*KVe!QNJBy|F?JX>$XIKS-vJtud(>sdi=!`^Or4oBv8l7*(78|m9?pFdU!K4>#J zI&FVRYyJ_N(5CgD$4CZSLwvrVJx>O+GtKd2 zx!*bD+SVgKKKIvv@WD;i_hDthja1_`{w&8RU^^7UID*e#>SHs|T^sMm zdz0&{T=GaXLpK6%YwpLWmU< zFK6U!gVpw3V#Qn!q*U;`YryZOg0D$Eas&`dd7WZ z`?qZaAa%=YDF}JPB~)A~W95PiF%|`5OM$Q?_4X6va|-zS^|OSGh6(!tR7!~Ey`+dK zO-Ko?-}xwrY3e0jRk5r&3ko=)<)9M*?{~=sWfoew%n9?lfMUSDXY9Mweoij$Sg1ts zbB;su!HX&(N0t(4_xp}1lBPh2;Q@enxB*Oa!uoR6GE4% zZ7)bM3Q(NpfRYOKxkyQ-zHfCe7&98kpBZMkfx?MLn>GI&AocjhF4O2<9|+<}ClNwp zl!${6(15-GRz*Y%ABdCYw3@tV?=I(Ap0pX6qz_gLT}tv1Si{-{`JdG|mX=vS3(0b3 zd%7i>C*Fh$MOox3#)veb`Mb2ARoAiPCTkhE##{)^sNnd%BW2ShUI%AZp>?(6&~cXM$oKq^W^kN~pdLX)%7+w+#d2j8T>O7{zc= zZ7%wBHUqxqS63@>*l-lk3?Qw}JAidgxLy|l#IxsD;QRMGZo2*}g%;9ZOE+AE0L;sT z5Ch&{Z!L;Iwj$P$dyUQ@F$RKV?E$uz7*Hq6USdKDJ3vw*P2Di81sA$bU4t|{0=aKg z!$*%eMCj2AUM7~$-7(Yt=-$Ro?r6rsezESE)^h2?85piB*(YJjKDRyfBl*Cq^IiO~ zdGsFh^KWN4x664ZLp{HDvd&rugqHcLRaz6hXO?Z0s+XseA* z<}olHI6izF$g0ED_@#KdIeVC`=y8a9k=*{B9MEw*$1&?1p?4oZ(SLWQ` z)4ki{h6m_TTa)^>#oQLG(U0BI(Z^|Ka2_Lfhv)r$(qTURPW0?NdDb(fq|Fw&`3#-m zcZ!s?(T}lP+`WJPnog$FnVVbv zwx;a2(^f6bR>e6N2_cQTUl};*w(VG^h{A+;nX#@Bx9^A0f0aAt0$ho3&lO4c&lp8< z2hhOcVw$k+hkyW)0Y`Cbc`XH$qE?;|Wlofd(E0!iQ!Yi6Y?d79plHQKL)Hz6I~PJU z)M1@au69lZL4?QI3uQjnJPY0Mp|VRUTXzM!lBN_KoBJ{5jH06ch7g; z4#~sjT2LY0^KIXoux)vtH5Xj2SHu96`+=XX5tR?D0P3_LqvAaiUblit(Tf8CH4rY> z6)%?+l_ceWOGRM9JTE{bL3StL^L4@Zw@sV_>N$)lA!k8drw}kf)FlEM$eNUw40yZk zVr)qXQwm^2FweN(Zg_vcOT>|aTM1@|t|KDcwgcBSA!)w6dFd!0NGV~8g!@qx08mP0 z5i?SZST8H?Z|`6=v>aLLqFcCaj6r~{G|c_j58Unt)@8x_ypGEw=v6K1|tTgPYC#4OBrflQ4G@ZQzFDmc)moGX#M&~$Qh9G zZklJT~jr-kLRc%-RH?Q-NhFfWRfeqw&x8|dv%Kmn*`pd4Tn z)E_?K<(Gd({&vH+m=Jc!Sq`Q&R>od(tCRqfA*@?YHlh%fx(SSkfa3Nk*@v2E6#;|_ zLu&^B(uxluYVwCiQ?TAdoQpvS6LS! zqNR2?Lq?acRTisSQZ5D@Tki6PiRfezBPRPwB#p9DeRW~=VK;A1fyW3;FZm7+KCPee zuH=rp{QsB=bNmf{cLiTKi<0FpK)~nUl<1WmRGHGHh&*lgNmOwAyC@owbg6`O#LG4*yIY)nleCDoH zwBc>iu)JxI-cOMMVE6ar1-FNA7yE_*Z}RO)YJV!TdL_VzcZ{#bPlqe}X4U&X4o25~ zI%hHF+zE=>We6uFvmy2mNnro2MxwP8)ABrhrx^tr>#+)D7+Ot>~a# ze(mr$M2{Wxbl{%b@AIgH+af%#ks-Ci4^&&UezWvr*?oile!iLG`R*A!KCAnJ`|s9h z?`!n5*naVg*I#R#rwL|$e8zbimR@6K#^|7Q|M;?D86M?Y*p~j2v0yJOE5>$~=!1r6m6wnl(pMJO^ zk`s!U6op;xS8LB5OjGJ?g{2N}rO>?H05^{lGft@5LM2s8^02NI=cs8>WYy4tnYE5} z-=9k1PX7wv)cC0WB{LLoMN&{Dss&46tW!hLm!r<3sMv43WJR*O+m_&MXBipCURw^Drymg^%w)LmlqAQ4Rsao%JU#K&N^J&jlqb&#ms;B#bjY|klI|;JqnSsU-=*_uA zx@IjZtML&U_pX-yF|4!L=E0HeuzQcvsvT5?VUVBv6((o7C?T9=AM!03L(%PBSR>b* zTPu4pm9Rm=EBr~dHP5KO(}N2c_oI=H^o`5Y@%2DjT&RuzwE!C zE`?L$&03_3#lISZ<>BHdpQ-(S7Up?8W!zvsxZ6N@_$x0*Bft3$od)Q!p*DaI5A+~_ z!@NHXh0fYp+tOcb9iR4D-CXjG*`0H0{AhC1gKX__L*&jqPt18;!7(}CggP+(Qx9xz$ql|6xQ8iK0;z2i^$f62 z?69;4{?m;)zTdtdoj3Wg^~Rc9A0B7z;o*9}-gbA2GZ38lnBT~)b&XEw^!6?BioHGC z5rn77Gd~zf#4&caemuXo@i^1DHe-CzczpQX)9DAV&Z3N7tPOpEesnbr{qc{rKKuWr zaUp5Yc=D(K03ZNKL_t(&E(qJ}4b!sXzKalA8Mt0n{POLFNtNVJml;2PT>V>C#s}KsV(u0ajmMf&&|6Hh~P*;08@`F3P>QxJz23|bQ~Is)6FJVQ;0O&w<=&0 za~my!fxu}VXN8<+tE6%h%qKaiz>!~1e&jSxqiXRwBXaSU`aD-6`}rmT`_5{E+8_{Jf~J( zwugcWLb~Y9qLAA3`HUNHJjF zcVq?aL#>z-fjKvUmKEbCgn;F;Hks?bS1_@L`ASEoX@Yq$Lk!q=0iz2cVA}Z#9rJM1H zHUApSS}rW1f{>?P>9`7Rkh@y!(~7*k1)EdZ-Pe@#k-l# zJ&Or~(B1?K%_D{y?_^vw3Zv~aGA6_aVDFltYJld0Fpv*`GU}HZ{JRT&`0;1_yW<-^ zYx%Ae1Ewgle2RoQ66Qo$r+{?|ShODc%M!895!Y4LON;>_Cgc*4OGiYvXqu+XA!>gZ z;0(B0;*8J?VqIviZEUUHdOif1{q23nG({}Sr0J?zN~3A$wv}D9a%+bILB~i~)+A-y z0^$AbF8B>B@>NW+qhK>D{RyVSJSD_=Zn}r1a+q<>fbj^V&sEU1D5Vsu@~=Urhta$c ziYee?X!1)%Q5Vwm6-b?fclC)92X;yAS1wYTLtlJNU`?eE3q@ z@m6yuTf6)7_|5gvpGrUn(egC-@Zo7w#@A_V1}4Gnlqb)4GI<|7rR!)k9HYlmfAiHD zN`H1|RAU>PP(gr{!{A3o0ULF0_x9%uPSKK|o_>yCOPI*@zVM}~? zkXg0ex<$K>5<|6PV;`GJLgo?a7kf%0(Rvz%TM0AUL(WreW7fVC2D&(4EwB>{V{XV9Z<*uP+Dy{=-iz{+G*+ zc)8#`5NO}={x$R6J2H#sYf1r?C+w=wR-LavQZIemB@}gleBl1RAs;W8 z(}edddFvvOOGHLg&-9TirfI@`&zj>aKw;pb0KGi$=l}x-?MgOzrlb;QE6rWR z0Wxn`cRu)42~{xAa1W}2FVQ?TAF)PLZ%^1c>bK!at5uF{|gi zFrz4jfus_2W1%P`9RN?{hzepw{x;Al^_)wnx%APH)&oh}f&h|b(GS!xce`gSbHw{Q z;eOjOO%vuhVc#ZjK3e%@0UiVPBa4?k224}Ju|>tvC`eOk6*I)cD``qqbH`H%nCFP& zn1FI19|a*M)Uu(~?lwNDd#OdmL``AIheQC>Nq9LyvzkmCcIfay%S2Wd2dL-_eZP@1 zfQ;*9#_eG2_d7}!3=UDi3BUUyrT6|=D)RkZjKU!7+ky46h#_&Eacr+R_Py0t2h02y z+YdEYe_9gK6mjecIoB4^BX%chYs~f%OUs}TA?4X1Ya{Zo*8(^p+#6e9Et{+S0X=^m z;08?IlmrHtf)lzJ7JXvNY0#mJ$M`vbi9r@E;`e!{XPjg*lP1Op!ci^*PBO8R{mcP7 z=qx9zLz7wkHSGlZP6#Oyrb%n^O#$;nm?u3pN!~?@Qr|BI(gj96MA?PH8xos!q_#mJ zuboI!nt(_{1MOO(er_8GJ=RLV*;t(upwsd8yM?j!el^ljcSgGp;1mk<@ptbI0@&x-k>H6!;KpG5VVJM;MeIMEyx^@? ztf#+DhojMO3^(gdHvwrJ-0>^7c0D64*0$Jp8HcXyQ9DmXLMH&x*vO%OMm=tTADhId zA+lrs?S6hnLfrmIV?}s!M53)>hT@4hM%eB7@uV{-^Z()Tb^ybew>9_$E4w@43y)*G*2kCVwxun`y+h)e8o`!&KX=Q zE|&#Ad|AQBTCFMI{Vu}Rl*D^L5VZ7^v^I1I$T_zhWztepDN1-afm^NX5V`?P1hA?C zpO1GSMQATtrc}LxTs!EGdH@YsxGC=pZKLk1L~Y-nNrbrHuMuA^gs%$&ToIThoL!aR z7^GBFNE0X}#5f_P8FLCc1LhH{*!L_s=0ya-Z4;#YSS3d`hY8my;D7tW7yR%4(-*w_ z?kCW)U=9JO8J`%K(*#Zuikqfc(dL7OHqS^YA;pN05)uM_ynIHD5n)RB+xd#0E?0a( zMBWRIA|a{sJOiwrd=mLb_6-stRS8;<>E*R7GfK|b?i=p+O$A%YhfOgd&Hsb5H}92f zN$$gbk;^$%b?@!&w+)9JE}~>Xh7DU13>z?D__OK5+3+8dVMvf6fuu(?DUqC+H+=7P z_q|nT$qfGy5t)&vs)vB9al4kYn~=_uh+79p?lB;9h&je$E)BF1j&uPEep22 zVAq=7DM0F)qlelv133}mbV49OdA{Cu0fHh!FDJ}XMy&;T$^a3zZO4=dx3wxzAxTIx zq&EA$*On?_N=xS_wz>!n5Uw525oz(6CL?e|ind6OrUWYQi=oQ`QIV|5wqxIRD83aM zU>i&h2z^Z`s$Wdf4?`la`-**Cz;$=1@VO~ub^`;2V+9*F?=_n+!mQs}nNJW;x$*;J zNCuz?Q!)%3kgHQXY``=ZJ&3|iCY+-Huipb@0k8uo;7F*kBBjsx3jY}|zxvPM|MU}H zUm34k#a6^%bDAQaP7yDsgy&Ndq<$xZSPpWEg8IGggzF}Xxq;#-%pJslpt1YnO-wcBLK7oC~0E=vmoH!r&J^HORn115@n7W93BD4#^l%iiu0(D*aJqWdJN!cfZI!Ki^& z{igR%D{KTcdK5A68SaAZLCdmXS$CvJc)4WEXDLV7$~ay(eE;`XPz5fR6EK}&PPKJi zQFP8@Amr(S<$CM#bP#<>>sFw$0BcLxN8K-KfeD1WOF?Ef@Xo!9TOqlx~#y2p`o* zEtFln<0fk!WI20|m#h4v+7B4c8DAZ1iUtAoJf)O#b+`gl7;+x$_!s`v(k9EZ52hC+8kMX7hF&;FaVFB>tJl>&Q7(J$=sOmQn z_i~1b`+&Q^&ZpT+D7!LaKG zvwNqzIqV8G4P(&n-TQkzdjV>g5n}>}gcK6yDXYgYU^<_WLP87)i2|k=@$&8km*)$z@?)M8zP(&< znIb|Zyxn%}g^;HSrzwM=wXaDK;>SH%*48{pE_%ut;wh5SPMeljiv)3UBti1tt*jb! zPiT>egzI(1?Y2Q=69)4J0YZ#d8pv810s?0dEy}|T zgbn~vRPVWW$QVynW@rix$ie~~_p#919zoWsGXNm22+I}t_#4peBT%kL=?A>yU*g?w z{tWo@U!(r8fMUdH4tSX(-d_^loim;$sd>)~iBu`vXev;|RtRe$Y?bh~5k9ZFXHW&m z6mwDolF%Jj8OJ8~as~4a<{cCwLYmt=GgtN2mflz8PD1ZejiFS=G)2ihCt#iu%3g81 ztq2gKQcx^`qIMKgdR%VOmYf2zM#?OURH85%OR9wi#l0v+6i$NRE`&5SMOOHI=?2Rx z#?L11ve7hwQc`+0Y>%G140PS7>a${$Qi!F9aUiB1_Fndax3>lRUNB7wm!}g#in_-N zAOFp7KH}rYHw2b-iZlsAKQs1q1z3zO^ZA5z=e8FGo$tojs~Fw_N$L;?U@ZX(1Wh~O zVZ9dR(F8G0wtZap4vPlSMbQ*^(ZhnI9oWc_rgyOIReB@XygpDU+T!R2!jW@0sm!S+ z?WZP?4LiP*x4bfLe?HgT>!@y@w53k|I}gVmzK(*BU0`3G+!M@WKINc)>Or5xi$0~G z%DMBNM{3@4{|C(@_6oawhbdXAwJg@@DG1mJ8st>N$9RGt;IZ^e3YO~Kv%+a``Uy1 z^W7(7GM(YvpMEFAAAYzaPHI!{degRnu9Ua=eW1Me72WROA4m^=dpMwes@+@2h#qTy;Y`i#BlhAe9+9p>f%{VT(0yc2*~p}cY6o|NntW1No8 zZ9T!=qd#t-^X?gb@HZU4R}QpC?LjTV#?jwkG_mUk27b8Xfobx1Xt3n^?_J}5v{vr# zVal~X*E9Ime*bQqsIfaI>{2%m5G?en8wc-LRhUVNA#@4fMzZDZ+;0-ATt`;HKSO}*r`0>A#d z@3F5th(w4il|-P12!7vw{emfr@D@|VZP{SpX+%JdgtANNt9hP8fZJCQV>OhJ0P#A9 zh!lZk-LaS2%eg@*!svrcpq74!$4Kk$GPnF+NeQEt0|`B5hTz73goRTjMXkSE{1Fi~k4CX!0NI3)3j3=OlsdCY}>>5V7t~*HU@_riv{;MhCU;cN$ z!2k67zlX~_AwzQ8&pF}@!j%({GNRU?KBb71v!EKMB%bpW5ktT!M!d`?Jf#f82#Nu7 zjJTwL=Xt`1_g~?3nE(*pZX1|^X`V6Vgxd0J(e4MLrqJai-aB(v2sj^OSroX6C1-z1 z5g`lkRnhWxrkI;R&kX$V(^bYB95x(0x4E*6rOKU=SERSU$LY6!g{Qyz z3#4EEi0~uha+>h+l<{(kIOiat>OrtBZX4sevQ}}Bby^WnIN)`ySPHOmz_n<_g(kBL zRzpyknv3L@6DlI9S5uX5t~-b#V!j~elYoRNlDmacTUfN;!vM=g2G&K=D{7VUY11U* zvn(6dWkpUw%I<~WN~sAaUpL0GO1}aoENLW4 zU~Xv;-a8IJD_at4MGakkIfnCqw*31uym)aG@SALgE<_y!E^mYgDFq2-_hIe2 z*s~r>iIAe!0#uK+EP9}sK^zjd3^qB1y?YP40B!6Ut>(E|qpD+`UVt+}uSsd-`$pr; z;Lx6?@QuL_bi4@eHq0OgBsb86PHOXF4MXe|33iQi$L$`tzxLP`n@xM(U`)Vp`-4Es z^B-uuxF+PsXK2;Fyayp)cLL?3VSwvLY)4L*0T<2rE99K5M+$&9l-GK^y% z{P&}xZ*4NBbyT7~xCRd6J$#lyX#4$zRy>8-W5KV+QO@#FD(zh^&4xNqOUyXf$P zo$vbPyN+r8ehM>!&8dDUZo~;1)>7T6)mk~P6wlFm059`6TrafG-FeRcw2X&8Rtbi%^8KQ zRG)dIWlyu3$;)lS=hrI$1*Iawx-1R2ZJwgkt7gd!1+#|F0;lsCF-F{O8{Xa|pPwNp z@eJ`$8is=?MLSZAShrn0YC(|4RXGuw;s!cstBP>H$J$QP_Zn{UAJC2PRl~8mD2nmC+-#9PgIrN{%ImNj{P&QN*HL!#uc@Kp~-)u9WwZf(#3j7bYZLasg`d z{7|A7nqZ=+?5MUg8Bft@hi0JA+;J^~D4=qFByB7@>Pg#&0K~n3)`GBBOz|1>(;3rj zAZ!vo%>cH_cv}lrslkVwq|DjQ0iPGfr`wKCOT|*fbG{cVdsJndSb*qQYsrIa1DUg( zRPw3|C?=%SGva)a+TiA4uY%@YD^xKLgWTT~7B#-?+m3asIGr+X*G1BLaz-f?*Xs?z zumnx@_Hqy0a3cm40fU$1^wm4;ps1P|*D+d@|uwMgG zwv1WUX-a|30`vT&w61_HkS?MJMumCR*i-Ufn+{(voyXUfgTd=QFo#F6lG2BMd>X?SRHnAFtP6+03uE4 z7&i8UJP)f_A~aB|gu{acbhNEum+E1=JonWtpUKHDO^!OstbQLuKiTd`US{krStStp zPEJll$3+_c2;E!m@>Ai|z&U2W>UW&C#oQf2v8b{cB$g#?-pddjFh$Q%|TZy1;8;hc>WndcU(P z?iXieYF5HWD~69MCHmfaeD+B|-c@Jr8XIt*xd)|Njhw^ysL4Mi#-~1HbQvwb05e+vm+Vcvi zw91sv$~25Ax8E_8r-1v+rnG_DIqkUH92&!=M|^|Z*x49002mV*wf&*dnR#0KIN3GB z(r5P>WBZ2|=rb*1p!c_j9gjnO=txsiEqbPV+Z+8lx^hDUv!HZ(p|R1_35+p5_88aT zY!r9T`iDqwn7h04J5iEjXdUCjsD7l~yETYT7ykbTt!d+E{AS?Qx)F=FMDh|t2>AHv zO+0c%5RL!szyC|zt~UV|dpe7PQ44C3e8Q*831A7^-KuyjbLt4;QzDpfoTCV$`(8A3 zJ|M)1oD+hgZ-danH!DJ4iz)<%5L91LPiR%JI5-8JJ*jXT+s10#AX5lPB-m6166*d9 z5rB#aDa9HCdj7eBC{aiVsz}G25n@6L5mQPyUtWNmKv9$_wpznnQExZ=;T-V4{_}r^ zfBfCg@K3*a0a2A_Bmrj%Ie}tCi4jwb;+aYjIcCIpLJEW_N1Ue#DGHkPlq2Sx@O(aD zdOG1eWn89&b5592#1sfI1^lD$zQwm8;Au`+wjJwMz#z=0Q}g=QQk~~Xg#8!@r&&s| z&GU?;pnM<_!^5_ve62&#QXCV~m>T9l#Y*PdO0weOEk$)Ofjh%}tOlr63p=8f2TUuFDo( z5j1+Nq2EVRwnjtLr0tnO4P;g(%U*_nU73$tr(54D956IjUX)BOJEUHB07V6Z({<7E zthqFPD@Ll)l+`v*q+pR;(DTs)SVyDv+jy<s2G#c4s`;hXWpwSYy+lJ@o z3Ck)5s5~Vw0+wY%EkywgMKQE8XcUBg_ac}JF^IAqQpCO$EVs4Ijq$^nVnb=SI_6WB zl4}Nj7h-?L5M=>X+SE0iQ*0jlTF3Q3CjaSHTg$FGk*xiF7ck76guhqCHMw0EeEf6; z0hn^cq@~lW4I=t7&ZmeN8SkGmrppNhQt`n`Scm52HlWe&iMQPGQmcaKseFP8sd;a_ zUvUI{**StnmC5`7wU@3og4MY-x8gCh9&cfLQ~p5YetJsH3bI`O-+cE{t%olgVd z*w{U20@>mmpV^wOgg$nQ51$`(>YmsR&FIguIfhvw`-1J(2Og#K@nipP4d>sxAH=%a3wpRS$Qop@F{z&;Ay5g7@r_F@K*-uZ?P+cpD;Fm3>DCyq+Z4JU+*{w* z5w)>t{kkJCYHjUp;fJ4xb}}E@%#O!B+F)!V=+K5f;sd?%@%r&n-$y+7Y~WA);6~K` zxo17%froy7@z`+fDEu`3-S7d3cO>YV3Mt{^Z$D$- zc9dH1?)i*A`|F?Z7ys|yp_GE>%Y-zYaJ>owZzRTfjyRvPDBNUVaq0oB6@dbBL<>)x zr;Hc_uD4Y@(?Jl5qk157N?4Y)mF9uf#a6-3@Fqg+;l={M=23N7gsR3~{Q?ms(s)B8 zQY7rq?mHzh0^}^9u`vV{0>xWLQIzNq1t2cOB(;@;0Hr<66F#JbfG8kx`sh#B6|W)S zU;NAe8o&R|d;H?}K7eLXHm4A9j^ee7NdTdK;t3&UgeCb{LOSriG8NhG|w z6j|H(bOG~@*S7`hCUbvXi{>E%N%LPTNp%1s21vYTp|hD*gSvrzsaMkPY2sbRtrJq-n~MT|!XqMQc}D~EcnsnNZ|D+r>vuZwt;bB@@H zl>Cb+V&8YH>jnX`7CvOaX(G=Hmc^!qtgJQBLrlVliz$i1JAzt1ik30k_YEOPR0*P$ zT$7XqTe~fyOZAANywoaeG?2|kA|`;UE(FC3K@aP%jAn4lIcaIQih0WT;fGIHmQ9R` zr;I#Loj$eg=Q(QR72(6njF2akQq?nWYgka^%@9Iph;RV2@-^$1grN`ep|!OZRk({P zj5qKLH)A!x(9o7uu+atYin>Xf`6@(F>-1un_4nC!V`k-zg{$ zXQF-g0(uX9Hz}D0g3`;Ja_%SElmM^jm?-QOg?@aTa&m3F7eV_@(XVgzyjqj{zuwwIN61~ohdUky$b*A?Nk8l&~Q%rWEnj%L)J6|M{2r^wTFy zIpN*Q2}}v=vSZ&1<|*R4Z=Mh%P|6Mj$wikiYyixN3c6#pzfbdwz0~H>Oex`X%7Es= z1{J2*JVER|m3<`#Wm09^)C=aoUC7O6ql3POi8oU{F<@kp@?|j)roc$h&~`0jlr+pd zLuU4l!UpWh4!8pHoImBJ6&1xnQ0oA3kc?F*7ox5HaP1 zr>|b{-T4Xc5@U)0qc$+WlqN*g97B{+PU}`Qrxf`5!xR4S=kM|U-5K+&^@UZ4*|r_q zzDcNO+uyt=*_q3-8wOVXLPui0j7h7(f9iIc2l+ zFH@xDGd0PEn9=gl(c3S3ir$+=K{tbqZ8)~C$-R5Xsy#Uc$a*9(EC7N>P+2^iwQ3YZ z*%0f7iFV|Omda3>uv7wt41|n=gh~@A&On@`o;(6r5y;EFNmPR7pBvga5kOHQTl|g) zomXpch=dLdsI(r9t_x0c!g5Q&SNDUoRv4CV^NgSL&JlC2~u0<=E#Z)SZ8ZS za3~5wAnbb;Fwjj5Yk`2%lyH8!$UQYBhAU&;3SKS=Z`Td)E*Z}+XH-Pou8SJkWSxa* zWkN}@34^SfDh9wv0$eEydg;CpGJ}vBt%w?s9BBx>b%fPZ-Zy8VU!Q|+YsBnq)VBA& zZfrm5Xkf!`4{*nx@UMrvJN0AiD6+|#pyoMeICS>j(m~p`n+~#;$`+kFd830pliT~~ zRO|ioT3wUjoIVjuaBM=P-OL2H<~g_A%)Yq{%+Vf6a0)sG#k_k(`+I-x==HL82@#+P z_uS${d5Cr){Vh}k<0dGZ!+FN?^StP>199kh+Xb(qJYK8}ZDBDin803-G|NiXGJo|-yz1rt`Skpb6o%(n5aPL7$*FOU;H zcc793E!@BO?!aM_d$b^Ic|TZWkJi=$>eYQ@yzj%dIHB9qmd#>kBcAW|`#d6bZ-svJ zA{^3S9B(svS#E2O8Y9Bn>l>D3Q7=HnH(y;qMEE!V_Ad}na6V0VdAVTO1eN!C+wpWh z;p-0!n&Znsfe)E(8^w9S6X~x@iY4ykFdB&6jzy&D~PV7D-Lu2M8fX}a-~w(hAp1A}bhHV{aG5E3B}BPT(jPeGLQM3T1|4O~i<^{D06 za*|y1IL(MLOIUhNm|~PXQpkABIpLc>_zpjN_k?&kBPifoR>2=pJmMi`Y&C#t08v7X zGbl%-D5(Ia81R$?pe}@jIYpduMv4*dr!0U_8a6He3OD0{{Ax@md7fKN9mRYnxVNJOMU ze?dwK`?fZv!-Uvs)>Jhv+LCs9q=KUvqcVhZIWG`5uqp~c6#`Mj*H&G?crfW0nU~Ze zLbPNWd#-uhsEm+f+Ycm6mtpzTV%VyU%P?~@bV<&+D(Q`>kTVbf;Gp9IuzAthl#K3q zZR=^!6r2d(!W63GU93FGIG$5lD71aRBs=>9$$kw|REn5TLlN15~y5321=UYUjK>w2hXJt_ z1dtl6fRrPC`1_A2d%q*8Mg~2(MfTX5BlDnZ8AJ=A$9?2$lLEbjD9kX>W>%86~?0a<4@Dem;_Z-LyRVIGkZCrfO7 zYOg-aHX*yxi2Y&n$o8ZoagM}um~`Cr+fAqI>R@>3znLP_B$nac-3m!CtboxA*B2z* z`_P^ToozILyGM@CR~oSj0e&r5ghsDWVB3*b5_&@#)3_(yLpynVSL00!L3X-KQ-JI= z@Lhj@7s=Xqj{biP_oTxCqozAXX}klypVj?|g3V1)_2KV#jF!i7`d#TTjh5V)_V>FR^ORrXCp+Fl?xD z#WB1c0QPm$Tt->%>-C23fAa%MDR_B0;raQ5Y0CJ`uRh{8zy2*wQ^e&w>HM~;`LS}uR4?F0uttFLe+dv$xWQFEK+VOq$G8@Axd;$1)1A- zRCILQ5Jc%E$1_79Y}?j6rB(L@p?N9OV?x|9-6;@ZhZ*NZF-gpAKet?H^YBLVq^mbQ zMnOo9NpjG06d`*`3G+NlZas;-k!!_2eVP$Z2{~m6Rn@T6q@Hw9UNb_@SmOjb&&WZ5 z+18M7p@5L0BDe?SSrqX(5}wW{(0r2m$1$j~AEjK9f+bBk;gT|5&J(^mKjEvKa6v&N z4dE?P(^vrg#3Pnd#I`ej`n=%Bj|;AgfUdo~oN<0SgVZz48bV$6UFs(*VFpD_vi+He zFijHrzi$N}Ki$-euHbtVP&B8Vh;f>NdVCce4Kk*AJ|P6)_46CHtsnqc_KJ0veCMpy z0g9F(6SVl!<{juc_KgSWdi4fjfE6^)Xh*}tSrzFfiz$eqrbLK>f>vPwt^eX(x3GFZ zq7}H>%BCg=vU&tZqK#lmAhL2$!e8WmEH%irZ(Ix!+`zX=JAc>&4HSz6=nGsW`m&v+ zBhMD)Dc3cAN32SAbq(!;s3>vV6cX#F$zyUVqp)DP>`YiIuvTEJCZq)1O2AKxtW(sg zi0#0Sw~9|Iq|SNu3Qq;0AZYD%pLcEf*0o6Vjf}s^KBANhtL#cWm|LCk-bHGC=(3aK zkDCK9#(?Fv0swMOxZPF_bC(?N+id~BVz|=Ch)$cL@UH8QT3FJAvha`Fby2=2%2HGh zJZY7J5CnLWSt?bKmUEL-j1Cg#x2F(7QoHM@v?JJ&65#N4&Mwm{x-PR^1B7_#Y z9z)MB2X~OXX0%YO3ZrK8w9E4Ed*nEWhSSjZVp3VS0qp5sQI(cdM;|plrEmG(>EDY$ zMBE%NzC0sqf9&%pYjye06mlj~YDbq1-Ff$d&8T@D~j}(?O0RN-8D^Fwx$glQrEbekmN$#MOl|WFX@`s zz_-hkbMxX`r3)h-3V@;ZH}9yE^qXSB{k^TK!^rpZn%3myOeY35h6lMEj<)>#>pN2yF7ICC#*N3O zvKw!=z5f4tjfNZ*P?err%3WIn7XGB0D~;DnUq`Q}?-?K8kCW0iZN0bsBcnOr*F&!l zL-@rpF&SVW$S+;g8B6mRR~{78<{f8ld5m6R^?5(dgYJ%ZDl?1cu;jbqO-^}l%vL(y zukp+qt(QKu+m!R(yRqX&tB)`jj%`JI4s<;FhEZMr`t7NIb_{n&jd6;TH(vNl#%4Jo z?{N5WgvHn|@-*!4OYYf+HjHD|m@~$+MtV0Z^Bv=`c51)rz(9t!YkNn-4G-h&=i~Sq z?APSmA)dSM|MoXnt_whflr#SNFa8?0>lHaCT;{A@4EVqP-7kT<r_pFIXVrHEg= zJmWb>q(nH+QOcu5!bFl+eyIh|^96elKsdwJv3pD2s(?^Ehlr=s2~Q#5`RN5APA%1M z-vuC$A;yqei(&+baQ)M#H~i|?AMy6KB2vJ7nlQ~10B9a6fmq5wRm}%C{$mO$D;=KB zQZDWFZ4m`KnDc|Ms=);;`RRe6;9QVUZio>j<&14vaa*KJQlNm>t0*KC0+Ln+*tQ)6 zElI|m{sn`Z(3_n&%G$>QhvU=bsTiL zk^;Bs<61j|^84Ca$PA4Nk^4$Wcm>rRs8W`#p_$tz9#kPD4U=!OiYR*Qcm+V}VV2zY z*4HL8s(e#f1&C-P2MHrDZSI>E18X>72zPr0wgSmBFTl35dc-TX!kD9&ElRB@%z`t( z0Xs`7$+{OTo220ETLJGC`?g`<)&U%?$sM}ZDRw6tROSgJcos7F8+czJX#$ize%p5jt2Lk$Pqc>$W3Cl5luMB)?vlMqe>0 zSX_)l%1_yMq!f@Qc^(Cc25R^$rfh0kD{C$BAoyGyl463mUN@tY?lH9}Hi(Zn)Tt%` znaoK%|ML{_(~qCAZM#&{nlkb{L*yEf{eJxM6)6DAR`L3};M;c@FCQ-0w^hC903kH= zbP8SdWk`hy07yaC+^z;2lX*eSKxVzFMboUJ%AziOo!qi-=POVqQBWg~uJJ|>wgS5B z$8a~~(uuGa$>EScRn)Eef(-z=5bML&W1!*BO0M+t(9qINdH2do=Wm{gb+0fR zF}DqYJ>Q3gIuV=)JswIW8Npe2yA7JP!FaA|<>a2>2}uX6hw%(!+ow?lqhbGQT{Xp4 zZ*G?yHbMUec46rUNq%f&A0q#1ByreR{b~0CKOH_F7Dl_TBlDx?aBK>>-`b@>o-~UM)+BK$-#~z{4v2lEKN#E4Qej5wuLp8ixA3d$K z;k2A4-}yfAzU$+jt{ds$xkvj;Ge))lA6fP8U)Dxjv+{lPME2034h%OaCf%dp}Z9kY8MXMWIG?YnzD;wRRQacy@>yx#zap2#SB z3m3U)ns%%U_CvSY`hDEa`)}ib`j!!)fqVe~g@B)a{DhzW{-?I*B^6SC`)R>%fBO@1 zBAlm$^BnQ=e8IcRgu1V&d%-+qJilD9l?^}q^a`#O=Q&|7J3x|WInT3%lR1_a>GQb_P(ChC>XP zf_N-L2z}3wv4uQROyHCRMLZjhk3$$xnNSzrYQyd`0|X)B;NBBEvG$# zQk45eL54?)SZhQG3?|_D`GTj*3D0vt$TOa)N(m~;c$rUlo)f0ageeEiTolaCJpMtI zUd?Y+;W%Ap@MXeF4)~MzPdEnxR9W52t|x1N03W2pWV=`O~GbB zJn5ao${aEpB0MQyX{CfbsGYTzoeUaUJ4Ghy-g8}jU@#&9Ik2FwC&tqRTrzM;6)&0b zZURn`F-68@3OMJ0r^I-dE9M9!R3rcqlD|&Gpui2#*^C!pL+5UTEi!hH(T)*_R1r`R zxPa=Wq5Za(`rW6$OtuO-Nk;!c>!dHY6{Ngwxh|4V9-lN>wC;ZLdeZr?t zZ&E!Xov_>%`1IthSot*qAj+~CID+NT*D5v8ZEcenLEx2N^^VH?WiQ&IMOIVa%Po|j z0tps^t~*;Ewx(>o5~&T1Y+ak|>)kB&<~-$$J2~+_ue>d? zpdP^L*}5ru-2$x_`n$7#AAe@|UC634i@{K2%_cin=~W{wg8MVYeE4p^H9-r*=p0{s zc>i55@_pmGBlk4=;0{Of^y}sRbav;_aL#cqZS3zA@;<-aW84jGjBeTN3OGA&JBD+- zEvx@I#nnFx=vGY6%OCL!x7xj8?z?Fpj*RnY&}z?p@VUC{#nO8m_)aZHx)~H~xB0Xy z8v)z;?rW!w&7rM!+^>&u4G#@4{kZ>hAlyg2v3F{HxZkM06xt=tF}nAo^`IlJKaJ3R zpGT%P^1i#9jbgx&<5|yQiulVv|0_%(;Ja^LKwzvCK!x$=zx*4NeaF*f!sT)Th;TX2 zIL{e@cVORfnlj$KJmYPx`03LdVhEUXK-o9Be+Qa*D$OBin7)N82O#Byr_&klbHEc-e0RzC!*Ac= z`An$1BdfB$Z50v~O6n07nvE%7+lv5H)uK^XVwf2FXPSgIL0Ia}tF5W!bQ8r5Q!nQUOH`pK`+Wx-<{vzV9HT zg9Zv`eANN@wsq&2qHTuY{#92L5i4#1WV{0D{Y0_pUP@Vs#lmMkjw(v_frJP~;)Y1mcd5lRz<<$yrJW$&v`%w@aBwH=2+;&C9aT4z5w4IAW z+ZQQjSZHW705^H9%fpiU?)GKwQL18el68|KVNQgn1UzNJWfDdGl7Y(vygx@gPlPD~ z=Nym~YoW_p46YJYA*l8RQ>a>4t^lU8d0S5gt zrhq(8&1hgq?%ud=dP*sBO;nWnoArt-?V*WdOxd zRS!vL0F@k=FpZw`hI3>~)c9Gu^HEXvN4r!qNH6}hgDlcdFlpm=N$C%wF1?t;j8HAa z-6`s#th3fA?@eopwkqSCoYWOWQVjl9>-Rn3$UP&L6 z-!`Cb84crY%wO*~XvgEQx6p25#CoqT{5esS-G%M`!%t(M4#M%Vvt6msh0vJ&y&`tF z6w39-CMg|#d0f4??Jj@`-F8UF%z3l!+S110@GQ1AJeK@@?(oni7Y1YPVD|v)pV2}u zDbcYB4AH+S?JazrN9CM+aMYHCr{UYI6cn^)aTy!(tGxq>^C&Tf7gB2emjT94Z%NOFKD-+zM}2f$8|GYf0&YE z&T!*DJTm%r%q;h={YHlmHyRTzd=uU0NBh%}5Bcz}{%*YMAk_HJwuaj8p7+_c_D-!o z5BE!0KgV{C^f6vJMw0ZiLyY+K-~I+~pFiT=^9e6cGj;&ARQ&2UAMvZd{WZ32!}*l) z)yoO%wjm}#4o@VYOHZc>Uw`$2*IU8cZN(`|US%nJqsbT(PKxTtVvj-c=tje?CS>0l{8^RLWqOrnCtpd!B^0}2`d;cSv>6_M#98^kp3IQL!`39IK z#7Ox0^96tU%?EtAy=RJ2Z56u$StbpWw+#P%-$kHeGwMsqLMc1IPyydUg*ALV zWo(;xcq^+HJq9e-1#eo@Jg722K|l*lxt+3kdPNZ>W>M&KMA=IV69x#&x;HSnlqX!@ z-cS^&zFKau3cA%&C=}7USZJj!3%Jl&Wf-ja$tG2Gr-L|k2(k$iB;Q=T=>5JSs29Bg z&{9znR&Lp&wn&YE9XBck1brvzBpM`-nw&%eA-`=-MUnC90udxrOFtm0KCi*=PV)iDG6Akt%sN* z@)RZ9J4R%UaCtXL?eu*YdX5l}{G0^O!4z|&Wr-#bkTn~FuA!T})b|7P9@kQ_E*s`q zz}mKL!+gqMMC{v+ZQGQ$Lvf+8+yN=$}U=9T?YL z=S(=9G0G%ePU7*0)=&&|Jig!E<#2;0k+#pRWy;csNEP%QC_BrBvorP`*bC6pG`KA+ zt8JNLx7OtpkRp2767NY@9v4l~pyM--wEF3aHpsRKxGs<5_FypGNrUjJ zi^+SJkFUqVF(1D7tB)eK3C#DWGMd4_HgSI<4R4JZcWaJq{Ae^k2;L8S;)stAV{z9L ze~tCmn;)#rWAEE#0IWb$zrHI&;||Yi$2x(#EvFHOtb2NRgKnLL+W@*(+m!HzM?f@y z2AXnisGDS#enP*V(5`8bA?U)4Jrn+(cQarNd=zlJ0N@AoP67LZ_HnP)q0xpv!tO&G z?Yp*0`&h@G-1xG)$>=ebCvxlKLAloRj=*rnTY6{`HxbO|35ntjn1{Y<`}$CNx@OlV z*I%0(J=?Pt8Ha;HM#G4-v0`xZgpa(zj4dw5%&q;F!$^l2IsA0T*LXbUU(K+|eS?J! zhnuqs-_GOvJyE&K$M5zWzCZ6Fo%w!v2HdXTffo6A&R}7eH8S#GKS%uV^U$~l%zRu^ zeGwa<>xP8h+n5sm{6G9BoTrFi{N6h-fTaj`{@4HRuki85PvS{`I^o-|o>2A`+g7l1 zXl2iyFEhUV_B~#&E0%S~%Vom8SKO8bkp$g1Jq0NxwpGIOi7C^X_?3l)Cn*yaVh5ZvkM~XiITNNxm?`0s z2sI{Laze_CFwZDVNT;kw7I>=@}s;1Ta3F zPx$uR4=A@QUT+&x2nfX3i|{WoPRO0AF5JpeqD>ZfKnNL?33cC5DmURj=r{}TR#d@X z#`tuZv28m(f4)jhb0T$+sN%hDx(^AeydiA|h%%L@34|gj^BUT|ZqjZd3DGTual2iG z+N*flWp6j{wyXmKbZau%+dUr_zNMnKP}Cx6@aT)p-1s?4~q1?$e3bHFJFFh#5!aV>J)uDSk!fRuoFikKrH z_MRg3)?-KDj*&3ugq)%(r$&#YNb9cNw(W1h_==2FP`=^EE2V^>HPML#ReilJf)|k@ zw!&C%D*}mE9clz(H984UA+T))rBqClf=cF$&!0Z`Fn0+0x*3$r%i9fI2_Z@Nb_k(? zJ9?SI=+SDTM_s$Ma_^J0s__@FU@OEJMTR$JUhl`O#xJ7-l6&563v$$yoM}e1+;|c% z{H{j3x)*%@yy4Tw6`=~MJs3zaqD7_z8EaCqDvj9w1TlQn>DqzHYl7!hmx zHB|*SQsjNWc!Nd3xP8WCPzqhc8R$85nM-B7qar@SlXYFC$x(eOTzkC)L-)UrVg`D{ zad;i1o<|;%_QBDKGwArXllC)6u8(@qB??bG=;}o;rLQ%hE8AX4PwF|Bu(W>ssD#UR z19hW8F)#++lqd6e1_C0QqJ%(BK<>h@F|<(uM+2H`R;ms%+9;&6+0k#(=HzZZ2;+lP zkmGydu-%Uj^L!@;dv<>}`{Q%`b#2plEG&=L>i;}8=6JrBkAb6?*SFmhTc2k-bnVez zKD24*&Ow4!2Hxp}4)?GXj$wzAHn3|G4{f6df3lg78Q{n3!l7>_G=PW4_a^ALP=E7d z&Cuz!7>Rp;57!+=%gyB@Ix#N@wUbBjJ%_Yij`>?3;oD=^ zQXBK*TzG>(pIXOH^d?{C1U4S*KBMz|H$I1bU@#!~sT_FULE1A?I|8%~$G`BKTb1qz z9}l);4Xo^r@%B5md2ch}pW=@n9&Pt@-v=YgKfr$hD0tj&@%iH?{K3z^LdsEu;hTh{ z{~!PQKjQPJPpGBh@^r!1?=P4`K_V${v)&f0w*{x1@y*vC@U}@AulLUr0x_0#0})Gz zd74o`*w+=?zJmhc{qqb^z^y_&gnLDf313YSFA12UtcMT<4W2YiSL*HV8vfXYCNQ^f z?>^+LzXxm;cq>4jG6+?`<0z>2ISB4c^GFBvfNPF-w3_Il0ma1>F<;L3y|2E)pL}>m zetyD_%MCw#|0Cj3B;;1TNTea|Q9X5)Md7dWjQKnv&9f*58Mx&W-aWm;91}j|h|`q8 zA%RmsjpB`_q=o~HhB5&qM9W7flsw^QPiK61IiZG#`E*@I8k5_lKYGD4z$f&^kRZZJxv&BLb2oGUTJY_s2LdG#sg-Mk#vK$gE+CFrRH1D z$fCovZSG0r{$~JNVZ1G(yj>>2NT>*Ss{u<9T#ZxMkx(#2#uNz&jJiv1b&(BOuGtuC zkx1A?;x&z^I3?0(njl_xH69rUws-^Y?69#lPqEQSh@!Ob+lGDLG0hpI^=v~m^_cqV%H;od=Nh(joDXIc5#+_}I@^XxVx3?R9 z_~A3CZdkWXtolX`-rIG^6}rzx8_!nqZR|7>!{!{&svF+HV$1 z9*qk^KxMqZ8~e3VBaxp~jK zIi(BKhlw2cetU+oDO>nmd1;nkfTa*V;r+ad3A3Ddq-CgeI-k!!8?#EU7#qPJh zfqm)xzQ_5o$N0N5z|e!acs$0VLghEpBV)1ebWq;y(_uOG%S>LfKkiKR(Bpw^$hD4f zbH9gt?QJv`=X&DizvE&Eprwn3aKq`D{Ll|}zqh_f>xaG_B&R0E84UrB-Y{+iL%;5S zbGIEo)0fX5?$`QoKkkiEl!mCatq;zBk>_!k_+=U*K2Yf5PkQiolHbFEh65iWC9jD!Gs;q5}Bg z<15NmP?)i<0t81P;B=a?RqM%cc=(0XY&rUw5QgJiH(R2f|a1 z2*lWm2rmQ!9LCfzFZ38L5(Mj*wuW zYX;PmQf}qdrW}!SLe3E%&J!p`TnI?}8|ZYwKl$!!oKIQW2g!47K1Yh6eaDaMj$D8^ zCA_;=F4{zufEktdm7**7Fa9}pwsvT6--GS9KfOpp-wloBWgQ7j5$D^keVYA|SWV$ZV&&^&l%+%PY!t?QnS zqtDsJhy`2sr27xEC<+>4%^(eB*L@ufstcq{D+4WLIY^pFkf;xkfDw|8BS_XA5s3%h z(BjS8UU%gg;%#gIb^(Bvc)V6E_8a7 zJ!IT(o3!;bMJdNfs<0!YmWo(6oFW9DLB}bih=PE9m;G=nBKuEC+FvT;lmq5Om~+HA zi&uB80x+4AtXF%M@sOyc5{mjCV`j}Zns4g?m(a*)wnK~69~Y%368_M?%FTu!H2J&fe`WObqDGWt6h$m z)th2Qt-f=HDCt_#-&NLB&>Hn@z;FbhwtZ5@c|%GGd7cns6hKVvpVYNCd<$+BM+imI z4TLz9=mP?Fn4#`o&hfuFf$MH#@6v&nznYHoNZ?|8;a*DYAlJ^{EpgvBhngTK zznlIQfNm+Y`s7BTUUW*0GA+)k2RrHg~D1PVN2B=G{wm&`zpb<&s_oY0=gL# zMya|PC}E%bB5BI`K?0U^W)lplG1a>PeIURc1y(-<#%vkA-fLI(yeH3zz$UOd1<&hx z?#cc6?5-Ox9nN))9uyEe{n+2(fVTP0vZN=q@bjZ`>Dpw&dJHdj&3b$-bbiQ#%+|g-4sFd}+k7Fq-4TUA-aV(Q8;{hlaLi9{8i2I#62we7IR(tPM%UBQJApB*N|W4IjSwfILt5FTeT;To_qXW!^qt@$diRU*Y-Xg!5?v zg@C8ij9LmletN?k7zK>mZN;+eSl1mtetZ)|VFF&S3%FKHNot$3dP4VY6NOL1z^_XY z!5)N=2#KoXY7+!>iNxDPI%cwGazlELWR$A>DD=`iK?g%Ttrh?5yA!^7H-TUVc>*J# zQbG+0Kv3lNgb*`A7GSuX6Q&fAa>SHVuW3GI{Qk=e()%aGb;FNuSNzEz{0!ed&!RM= z8$`@FQum72>kUW&)9Hfumr1;M^Mup$8M#X7sT>Junn275DdKrzJWVG7vI8l&6X1=w zi3phpQzCFoNXi$|1k{f=^oR;l9HFtyov9=OP0<{e~!V&g-`wIuBTk``hH0)muzBj}0}4PS89s@xk@ z(gA8kiUCt1MPRqhjhq}L&&ul2mhl0uJ7cdvNEuIY6K{D8xNVG$1B#YmTX#t{NI};7 zNmE!W1E(yWcynB&05B1jB6B!LLL&LC=pYjy#=4Z?cdi($kMd_;T#MAGwc`AALMavN zvf^}>&ywaNA?_hWY`fq!5VL?ulH7|Km`(}Pl%%X+DOj#IJimLE@OItTBtp2T0Re4o z#u%|~f)`S1RbylK7@Kin-*;8&lS@gk2(T`vGDKkxM2IPwFZFd25&g<@{SFb+rKyImV0aLo*}{+ z1E>vB8!YO=v)#hO>kNMbuLjVtrOYUyE^}8Pz>r*$r45zj)Ug>VnzzE{O?)`Y)_+9gVx$_LT zYwz0n==I=t+kCy>_1JlCqt<4AY$pz3_wH)xhpms^h^Ab-h8h3tYp0K>h4}k1?nc9h ztNy+xY-8Hn_n&atcKn;%DzsL%_Ok$eBRc5m7M5icl7Wy5s#h3xfJ`#lCIgIZGL7 znz7tAl(Hcw!W0846AA*#&UiyWg_JJ)>Ku_!Q5l%?glU?Pvw?GMGHwUvMkE4fU+m(& z`}@}&zx<&fBnPf&pm0qA3kZ<1Wv2C#dZ%L`RSZZ$>BLG)bH*P%J>h@&(?7<4|0jQh z7_uro0hE#`d@-s}uZmQjfDrKhY5M;td$VR)lH@$>^N7gIs&mfWZ?DrmJu?JAkbqG# zgG8ATMwn4FgYZBtKR{1<(F-4?cfRO9$@B=58O}ijY{{C=BO50=(a0++XA6%cmGAN~yf(Pw@9Xe2VvO_E=5_+}+)2six*u zY3`tAHRoElS%lW4$=fVplbsWlPK6+O3p9D#FbyarrF^R=dTxMlmbC#VH6t>IhBxh01x;L!^n(a4r=dXzd<$7?N%G_UL4BAic9t;+j z;F!dfJOoYnRT#@^-UG$muh~e*^;6#%t*nzHGL@(?NT07(bB4T=TFng)18Q8YB|IZS zV=5W2oDWSoP2IUFJq9#h#b~dcYK@MFrPnbBA)#3XLz`c{r^P{gC2u9FO1HhBo|*8l z$k+W4F!BOs!l@A6oq$7@a8VGnj9tN$B)j{~D0!ur8DkKv)9zh)jcx@nNGdfG<2gOz;D8SA`ax05Oc%RFP8#O*v=$+Z|nFfNuP*3LSC!SOI- zNCCUuC`vg3?%&;2G#R4Ms1!quB?o}htP0R!tb4U4JA=svNP`97AaBJ;bvb;V&k%!@yZ#0mdy|m9znK{kao=Y#N z7j%yvCy0bB+k2fVh z`5&pnua$M3&`oqUYKJPGLHC=q-X)jI+UPx zuj`lmHMd_X|FCKH>^r+Z#X?V1&j^+&$SRR*cI-A+K}=8DHwaLf0o!Nw-L4zXsL<}X zoo*ggNVI+(QSK7uT>zJCJzW(*;7d zp&%DO3mx%`&JDLVcekCu?YeKw4t7OC^WbUE_Hz`#_I;ku;ycM?yJ1V5?2xMGn&8pH z?E!ahzs2AG(T6x?$(HE5m+|c`nFnK@38FG5}ryDiKSGSZKh08u8v$ zKmubaEJEx!A&mpTz_P9o4>SU6O<==|m;wq|kbAhG2X11QQ_Jw&Q; zq(quuWt0ZW>VI=>ZI}PTtomJ2}r|E z;@dSteA4>sF*3jbF%4BYkP>4iVamm#3m3XaRY(m{gxOsri9Nrba5an=_Y?Nl172Pa zcs?dPzq!H>Up>Wp(}?3~#@+25PV)*bvX_UD1P6u@jDR(ZHO>Uy7*peo?Ch+371tzM z$AiSq?yhzK2=n2HCODa^xbQtic&a!6Wx=wnRmhr`894(W@qAe4 z6PhBJ0ZmBN1X7k#ahBk&9tegvn&8JNKMIp8&2CoW+yDR|07*naRA$78YgmC8WF#zL z)&yT%3?BN7@sy6Kw*BpCLbtEA4ecT)d-GM)Gs|d`@oi)tDh#u#SKippdzOBQHgl}!nB$ss4230be3wM&R6v+l5z0@i z)SsUs<0=8WA>e8^NSUz|kb;zHTe5)hYS5C6wM>?bV<|F*M2u9k$+TQj0=``9jo0gj zPedw|tKwYuJRoJ&h9M%xgn6DN+$1FA0?hLQa6!^A8j&xuLRrRWNz$(NBSIPxlO&7F zR!2OLBzlao3gD#_tgBQw7{>vC&?}2IHykd^u<8}1WDQ?v{6xp5IbMT{DJw!-GnI)< zDc2N-67Cd;aGEnLQM}Pq6YPm1@{*Cpq~R%AhRpGCL+9U$9_%qX@5vKX4U3jf9eNb->YkA*S7s`2Zle0 z+t?dm&KY7mU;PEz&gbk&YQsBQi>_riW+PU5xoM2f*2EL0)|>&p5u~k*A?U#u_xko) zTW!|0F`{RDKGnP05Iiiq);A15+h^PUyS?n%+6uz0WqY=18{68nySM9jks(48m@6%D z;J2*N<|uGBM)!xR{p{JB9zk1pfLGSYZEnB$90Km|5*+Fp``?sU*Ku_7qO<3`BI2wU zrv$NCHw6TBFlem*zr_2`cX)O+f|)VT86^fR zSu(7LA>i9z-Q)lG-~R-E_5b}G(?Iz2qZjz?zxNS7dhaRbm20x27zxujA`#*C_72CT zV1IRmAw_W|UKfC~xNnCj<&e}uvG zm%x~=_W11eb5MxLA%ImwPnvL!G#)!n6PA>ah&5Y$5FtHi8Mkr3hr1oZZo;dp0f7V7 zln{|{T31buq*mS_Zn;u6Na`y`RWcwDI1G4&f;0^%DdFb%3&c3#<@JOYS0i4&eu57_ zc#i!rU|v?--rnKiv|!Fa2@%&)Np+ov`vdH3 zDy885?q1?=RSA%iWNKd5tg-3h8gJRki3_5}h^~v2+6xpgFRi1_8W=Q`ZA19END?u& z7;hysWanmO5V;CpjF!A>%aA9HTQ=p$x)$VGa?SAuRq0kQpz_BB%$1qq8;lg9?tOupuJMCsC~I_X*3qNVv~9qOc@Wld^z< zH|!F&kt)=4Va&^l{Vquj@j=>I=B46FO;o6RkXej_>#|}@0bv-$NC-Q(obpO>xD=@6 zS_ESWqve{Dz~hZr`b+iHFto}!w>8zodFFXDjtTRUwQ7U_Zy3b;q!h8-&$EQT5J^}Q z7>FU_bdtOkF=|L(v@i-8JGb4_?HjWI*B>2kS}ou~_l@l{=E~c)J>Iq=Jp5{tjTvmL zh6@dFd%npX1(*3xHv{X~4`d_T)sc8+$s;GXK0IgczjwUX=5xm%ZC;J#F|Y0ON!a*; z?~Kh-UNkj_#Fp#3|LcVGGiFMx0zET*&boSjNsZ%`-4)`t zan&^D)ab48hZ~K@<^E71-)o0#?XA)Fu@i`Gs16L!w7bGHs|rt&ZK@0Va?SL%@c@HZfMOmWa%?*z3=&)TY77=;O! zn^89U=B?x0DFr$K(eAg;mE<`~z;>DZgvs6K$65~ubw4xS3-+Dbcbku&Q>DVnPLUg@ zj=%WPZ1xk}IJ#AI8=JaIw7Eg|-X)5@xpR9<0ND2dm4aC zi&gJ^Yz}=_=XlqiXMZD<7l$wl@OQKiXIhvo)?ONKe|x{K-AG`EfH6|X`vV@{-QqXD z{|YMz6bWw*86ipR_n4G%9tQmNUwn>#{~!Je@4dXj_da@om(TY2^uy=4zM5(rGMEuk zz-}O%PDk9`-6N%lS2ufP1kCdSW{rOw295C!2qZ#&2@!D|ft2ue4Y=9w@xikR)5su= zeN4lkW!!*y%~Dbos4l%0?m(2p%T~RD|LX06fAjf*IIkkSuNfuJh}nmv36>Cv5O0Z) z$dX{`evEcM01kvKS!k>49IHi1GPp}D>(!Vrq!Aes4?84Oi+l(XLrQr5{24xba)W8u z;by-BLjg@O=hIt8yware2p1Q~}KnfG5*ssUE#n8rjui&Xc zv?&Fp8Q%caa|+f^J>TSepn!z~?pG0_c9F3kq|Fov`%y~}s!*s)qRGh$4oktaog|#B zuy!NJ3}hw@K@wKh#JajxQifKQ47CQfH4rN8tioz3RjDJiwP2oSjMD^yR3R8mp%4hm zd=kMkDXpmc$P^yH{rwRs2JCi2O{6F_(dAC7i4H=QO8_8eNdTCJ0cIGKP?Y{$v#=h? z9U=4tL6Sbgp-WXRDJ*P_VrntkhOVM#5exZ{0)`=C$>LT{qJS>Y;DMIZJ+dg zRJ;c3-u8$6CC;MZX7{P%`7d!Fe%$Ab=Y|csAj5q3>+z93@>^vivslne zmXd4rb4IOMSs7Pj3|VS_=3-WJiE}C{@HfHSV6hPx`21Z4%MIR+VIuEh$~Ea@83QDcOmRv$5UYpRHpc`wjP7;#c>!NY14aTJld+8 zr>&PCYk#+loZ$iJZrJ=ak1ID|-|*SyBnJogoP*mLMrKom=d zX-vA_0dE&bap53|lL`e)6tLfqc>d}Y(wGp|BVODjeE9MjAHI5m=QlenbH?eEQM4pb ziiG8O#9=vMHw}1teTAHX)A58=ORtT?fc-FvfXTub$25Y*3EwOckt2Ta!3|zs1IPcSjI+~LiI)r5)qC-6c@HBlE)Kq!hwA%fz75F%(ym|_B@gefsnN?N{6toR{DASKY% zBw5R+5m$x4Yr%0jA(dP!>RSIX0C9*Ig3K`nbJq_zg@hNogt$w{oDs%=_uhMjn-^Dj zb`|l-lO0}v@&do{;WJD_!jd!Y4+k9P6-#C;MJ!%1MvS{XO37GPDJxd={H|x30o*GZ zgJ#$+TB3VK@ z>^Cq0Epf2LbQ7Qz5jaQVdu$*Se zxez0mB|ihem_|uN$%^NV-;<jsS@; zT#YVmSt@R?%c2PtH59-W-qxNh?wnD$#f6J*%>-p@o2ufVu$ET~0jJYilgfc)J&m^- z#v}<8trQ_FM8>An6o1}D9ME!liks%CT&?VtGC977U8Z&`v*vif4nh4cJqjF=p-C^_{FV=gvEQz%B-Otvp+;bb2zaO%` zLepBb-4d z4q%>HRU*``I4z@V(+gPjx9*3{tHXxMg34Ggu&b5WIzE!zcsw)oUHy6-vNj3*`d-2l zFRb+lWn*rWI`d{=v_7a^^an^C+~)J&25J2oSNzz#_X6-^n-LpCZ%QLCU_-xGw>%S| zE;Po_gcN>!Xzxui1+gk%BcwT4dh2dwI21;tn*5qP#11$w3tXD+V}fQ|WH*d+L9DI) z`E|F{UE8oZ!d$)2h1M49e3_Uvg5J0)Pq6)#vb}C_GdDS(Vc)XA20c_PJA2F0j_Mvp z8@{R^w~bRexugKmT~f!YpmqBS-Ck&)3?}{WsQvY??UgOgpDAOynfNt0zkfTajb5$c z&H1g}A$7Su`Q3Nu*HnhJg^&ODyt(dWg8!w#ZJ(*c9uDT4L*tQt`zDP-o^Kdqj+f(% z!@FDj=4UT)S_-BVfC8LYn3Ev}@LB{05#Unr&9A=2Pdgsa1hI5klNT|3W!04s+cfL6HX=I zeh&D-2RHciX~9@lVJc&kxb!qgjdXCe;6RfVnN^!f1mRMEKl|A&zBw{pO#^=Pe8NqX zvW2V+BoVZnXtS&b6RzwxrFkia%BcH&ZbE%PbAOF7;yNZgy}6dU%bGMP#)OmvMlnh( zZ5&37S9|P-0_KQeUhuFiIvsKMj3n0g6fPva4dM-fX@{Gu8B$YA z-t6(glPi4hgQxiI53jHr15T#}hx-Fg^9n7qT+6%dudcDoN35<6x>;e3!6(gfEj(~L zH<$?E0xFEME?Ae7Fw2&x58{E5wSKg@>lckh7uVw;HQ1FiU##{rMEF_ext2f!ER@?U z;|!Eiy~*lfLvp&X3Gduo_CqkiMDDe+YIZKi7&N|CLMj-*ti~KGCD-V^;Ehi5{KH`kB}uD}Iq_ARz`5JJVwpqiP~tlgrx=!^;`ka=;&(a|oXj_T=wj)!s& zn6kqZ5q6#uNhz;JliC5frFS!=Y}oY(5wHn3E(I|Jv7DRL7{X9i>Pv#B`;m~MUfS@} za#3R1V=NKrWvG5m@sIh&9(>CU7wi3Ss#RtOO(|sGB=uzaKS=|AKK$HB066DSfj*QzbkdjT7|mlA2yQdJ?{1gz1P{VXP0%u8=OfGv#oZl^PahkK9}dmdY?VB**`{a`p#^O z%~h|) zByU~;QHfd)GHZ!1F1c@&Yd`3&TzHNgTq`|jH-$D(Ckt}@ZUeHOy8U!U+|7dD#>9TD z(f;7y&xoo4)s$_{TF>nq>!H&>Udhn5=?ky6FP(Qt#XZMRZ^qo~(c0SCST6~o9Zlp5 zO1$ly9{YWBxgN+JR`g@rw)I%wy87@3eVg!xDB8T?^JtvE8s0VB@t7j;5N0#Ph%bKj zC4T$&{sF%H`WE+Z9x$eWxv*wV&lmz>p@8MEU@45-+k31zB!n&NWtc>fB@bO2l@l*a9?*(9?f{&LKZw?FY=LanFNy;?sucY?73IiDlSJyYV zefJLgt1ATW?h8m}>s5r5oLAVHES5I0x?8fTvYe2U)RG5@u)7w&ot zAV9q%np-m!2?Z!2Vx6;;5>q-ry1+32YcbbdcNTW%g9_P|OfV=U*|%Chn@p;zF|??M z4KoX(maJ{5h9xNdu#DbzmS3O@j*Zxi#l3XReIj@1Uj-x%l|B)~7$8Z*&YN-~HZr8D z!_{CGYYERAlPJPGUt0=90mP-2KHl#W__oo-c1AaN$01=}Bv~WzQtu%WC0#(nX9I9i zg;Y#~$O>*EAy~a^$&DbNMTxY`Gp5}HdUr3MuWoQUMHF7KEDO>&A*P6RiC~uS3)0N% zh=jvo!PV6syZwmcgShNJ+&y5w6VIU-n_{r!3{V1*#Bz_*gg7LWb#4Yje=M=k52A>9 zo-ytB2!XI>YWV2Zo+#8LYYf6P4OlZHmqk#HWo?2cdCWyZQ>HOWnLMRel4U-Ed9K#% zlp>Z{@-38-m5zy`H72NXM~tU}{-%_$gma$rFdnHHPTF;mz8}YU`SEtp z-U35k$+y}upGC=2V9=`ZC1jjt8-c|iRJZsVXRf8rENl8!B&hrn)&4+&JcS#xW$`4I zD3B@)-4boM=PI1({ooBS;P|!+3V_D!H-R1g`LpS81N>!4r9NBN^%9}2o8JWF?Z)w= zb7Ss^VVlMVERP6L=PkFG5=M0wqwbc?^aa3t33Gkh)Yej4-{2#koo9OXcYW94&}PZd zf4;QW+EQ9a>;d03)w?6h?^UCyx7RtlL;lj5RhVt*T{kxB=FuXPtUs>(Zge>Zdhp zzCGe_Iw7qQ<2YbQ5@#A9 zdpv}M`^@-q4)}0P_%A=3@Na*9z_)Y8X%!dz7$z*sf+cH>T0pIz>W0kH-pY(W`NacD zVf^Qx3@9le64g>z>imcno@Put)g-Xh%f*EBAatmOOz9d@+uJcPo?MT3SMG5+)_G@T4^F?jrC-dVZ^hQaU3VC zv>*T&(ufaUKf_n+f|u)ngkMGSsFw%$T0)g$p7z4v39jSuO~IH`?l1#+9x;#>%aQ zunPrEKqd*{utab6JTtNi{iL`b0gOY!$xg_6HjBu$mQwLF7l`$|SQr7#Uw^q>h70Tr;<3>WqK4^a48wptCs1DMKJe#42mzT0>#QD0 zYW*(E)iXfWQ)pc-mJBr3Gk0AUfc6!I&D(EYTDVHo?FF*rdJ--YbUouZVx1S2ms&W) zx-5t(sTZ?VSpY1@m+hs>*SI8uM8=~VCaLgSa=iz2^;_6UlMxjb7x>t7E}0q+V%(_G z;EK1}m*=mT*?CD{FfcGbx>x$wW!~1E@NLYVO~1sZP7TVTx>jC-`g3W)Jhkv3G32!z0Wqf4|cr( zNF%JYy=cVx(!-B2bqqb!+El%^vs#3xf5^Lg+E|P^`1K5=Z^@xy58!?Z+<%S849%sSlxWrG^d5d>Tvc}74)2kd;DEb{FN4E2S?6wR*No&if!#=v zwNzu@s#IY8og-Sa0yM(Y1euL;#hLp1o!8B>d$NPq1{S?`{=C0!i+uyFAwSn9KXC^y zkAJ%wU_D?`2oVr{{Fu7~dVL3d%9>nX6o{@PCW zvhkdaU^^g(M|CdLzw3|N@R08$D|Ysrk;VS07!%%n{Vl%t*~j=tzx^@(?9YFJ5(sN9 zAOyq&P>@X9w|5UU{&&^VGcT_qe*d5R9`5em;BfzduoUnVF--}(G2!De;iEtNJ^ZtO z&iKin{}uk`Prk&z{`_0KT^VT@u^S^Ejw?>56}P91yk_jCLF(n|BBThMIN~Quz>!b* z(X)VG9x~n?3CB#hn==rlCUaId=vw#OxD$v_y8`&rFHV@#34ieEfP#Q25DEz$GpfLC zML=M6S+}NCXouU~O6Z$~(FAC9|4a!#c>O*eZV!mjYz!tqLagJ=$dFWA*H_p0HZKSa zq{w(X-{KkV!7;YjOLb@W6=TR9;DrL7>?UAf{02|<6HZSu ze&c1q;bF$t#}(@`qvVWbOvr1*G$eos!*0U59B?`wF-#-IAi|~zEW~2nTeAvr#A0Dq zd|}R@A~EHhvnJpRD!hpmE9Z<50#cNk>qJsuslc+P0<8TzaM4M z)^)|e!)6b$u93LSLPueBsih>9rba6O2XrTH`i z%-HRB`1be?%X~sg6HW_ijr3{1M~VTN|Cah;x5i8R$g$!9}{$qg6$%s z`=*pKK2mYfn6tA3)aa1oQIwB-lrO3l197`2wuFrg2~&Y6Nz}YvLw~wnspU8_g;w3` zTTb}`N8uG=9bCHI-8Z9sZO6j?g9DS_+vlJ#=1$&{Fg~nwfhuvRLi)z(YyH2jnv+=koRd0gYe=q3UFl}7j^e(*f_D9>bXY+6EZ8urN zQ~QzYgc?ZO=PkeA#^bPr+HXda{7|Y4a<+4*Uazg$2I=nEBF0vEv>6wB;nWX;>hm5| z$S){4muI_Vntumo;yz4(sI~81jp6RuY_slFWnE_xsf+H$T4(79RPFP^=Q<~c`|QE# z_k(Sl4Ye~jMX$UP#g^X>R25z4tJ4t4V9o8pI%ety^c^qqmUAQOZs1N}f3z)QaP+R8 z$#UzcFM&({-JWf8(ccW;D?s~$#~8kCzKM=}QG;uT0_?kL2jH)@G;inf=zWeXkc>HO z;P@z1A2~lBxvfL%izB}@rwccqE9lyX?pDQ%5Ch(P^#-5*;3K^M-c!7LbBCXQ{SE}+ zSTsxc5O8>y@y*w7aetgag|+et6k#=LBM2oVe8mw{NO*pI z#Px8%JP*iM5%){MuTG44l}yTO7WZPXxvC3>CL*jH@Tb3chbzkXo%dg0-0g%(1PhM< z_g9Y7%hwnYX|MiP+Z~!5%3uZ3JOTZ~Eh=DLoBhvH)_qVrTUc{2A*31wBxQMlz%xzcC zmJmo1_LhRe1p^mxg(m_ane5j!gS1vML5wKT@(Pe-e$T52_9E~Wl%iB8#44l!#gRLP z)v+iwHrws}k^v;O8nRgHYW-y!o4V#JA)@NXN;P2|Gb5ygys8i$3IfNr)oj1H-WlqF zuHPChQwEF~OtI}+&qV1yxBF$?MUW8zQpy*|ry}zV@d6RB!H_a5Jr_YFN;<OKgzHJk;w(oF&gp`9K681j6^hLAb#p&RZ}Sj%rQG7%!AY$4X%Xd^Xvi3>bwVnN1k z9}u`Sey!4WP0R}C=B_j&uPb;=qO5^{R$Q>P?4fyjm_iVQoEh_J!8A!M_%hGf-Rx8$ zRj@7#ru|O5KW26B4w75KV0An$*zG6mcO&L|DK~gJp2T&Yf`nLTpRBnLtlZi#j`};) za|D`8!<0k%4+V9(mvI&}-iQl8RoTPHMZ5!qm$cknqV!_d?Oghz z%5QY-RsdGc4X!DmtUwsteT@e7{7c&m#-~?7wF-yIZ^F63UwU0IMD#s$gYO2+{>De! zzx;b^+s+-iDSq;L`d47u)H#RA>1SM?%{#;DndX+;`RJXiuE|n7&Im2Oh=84Fh}bQsp&3ZKxNxS8nq&=4*?eeq5J^ zL!0+)`~Kr4yxKln+oSU__YN^s*Am>1&kO&TY|`X9B%o=9Jpc%m@#^+^ZNS!(-4DLM z%RBuNeV`5SsB6mu%x!4>zQq@#mkoEeBpi<_47y$_<8c{3+P>M~o_nt}h}!-A8uzf; zw&lNV6+PZU-LqR4=2K}QH3px&ZLnX*em_xbwBgTXnl; z+>MFO?r(j1IJdjc*3GSrLFLjj+t)dEwd-5FxP&J*&Mo7)&9(8^%iyxThuj{u{+Jo} zcMo{={tN7O1Lh*u)K&SQ6rdKUfcu9Tr4-Cd#=E-{{^kGhU*m%pH^|2$LL>~kgg7OH zm|Ox$Mp#$mLKwyYFP~rIY7F?~#U8J3uJGb|#P?oaW6mocPKyZFF=<$>xK~;U=v;t| z0SAoWID*Ov`@ndyPk1#-LaA{WP%`78SPk)@ZrC!Olp<0Z@Wt&BUmQ+&x!++o4d56h z1APh@^mB|6$?BC`U30Zq2a<$lt<+f1>snVCkQ&XFIIa`uss7nQRT`@9q5C1p4xCSz z0K6f>`%^+3BujpbjZqJ}tfUz!z3V+FVB{5<8D%M;A)&;G7=SV_m`ldnyBQ+^SCR1L z;fMgShROLvk~nu+B{|(N;5g49mKybjOD0@POuh3gsSKB7cotV#^>QImdF3pzs1ncI zV39Ke>M_6|aLJ;$0W_|Zk@Ko0ub_gI(Fz&Ex=5SjID$wM&=p2r77?0aR4=*G68|*~ zD=f3R-}|1ADS3sJd9!htm9kl!&1$Rj3#suTR%hN~xm`&$ka#Ds)tWcFGV8VRd7J@O zVVnpY2auF8a&5s0l8sq5exQ0MRco9kx)LHG*TR5W#trVQtMIll%G##@OoNmHG;nsu zcg6oLeLAO|a~1(DY0igS7>Ef|;7!sp#;Wpx><%>N|Yr(_A0<1Gc86|WOL6t%Iq-SSnLSqERX~Mb|a1poq z5P~Ec)r8=J4$k;e3PO;$`*9jniC~^Htu!|n;!#i-l#iegWmg5Q4N#Vb+f_dDpWB)@ z3~nzkq+jMX8ej7G9oR4f?E3Vy&X1tyGroh}falNR_g+o^stPswcTvzWVVBU#W4ICv z0wc>9N{%d0Yg-9ORRY*xP1y0;K^qGLO?#l@(6O{@EXf<)a|9r0og{1Dz<$OwUH}l3 z$a)LhhWSPaXj<@Yy0)zX| z_tieP1GJ5~$|BUm!cD8|qZ=Qc-{S?bU;pwh*_Wr(gmbQk?dA^O+;<2`axZN@)p$$o zjHtbPu?_C~`8XTKy4EU@=||b%dIO4wqG0r9uyJmy(!KdOwZ=#yMKJYZMU;=U}m=6p7 zm;dR%M?Tz3a<3^O4hccaj@90rz*Y()bHKFU;c8v54kOZdg((Gm^6DDz-Rv<;5h1VO zb;Z+Z#C{wAW<)TC7(tM_*-QzXc34BgRRo@2MZDN0yqE}|Jm2B_HzTf)aauFJodF8L zF{^|aN8BG){N&voUhOA5-S0p#AqL5^9#TZqZ1z^(D8_&Ys@cX{X|^UPN8RhBCDek| z01l|xzdNNIS*o{&<07XMuA?OF!QMV9J1*L_&HR&-73Pg7xpQ*B=C)SmZK@?xmoHOlEE2jO%JIm14yJn zv@F#vrZ=ePdkVVdvF8`IG_ESDxI?FQKQmUA$^}u!HLnH3ZbFhe%iVKJk=owl7_sJp z93u9CHOAlfEAIgkqrO6Gg6m!^%ov7*G_(})3@z~oQUQ%6mK)-#Ey)ry_E$S(4mch} zxE`lT$_y$VrC}7;?zM0jrxQxf*kA223n5F?l5%!!#eE>*GEE<1VR!J?E{m02TN!y(E4poR>rB=Xp)?mY^JHrBw8SQPfxPS2 z1Oo2I(m~72{VW}jGS4+BoWaF}bH1=Z)_h&qp6oTNa2w~NtXy|nORLaLkj@`z!qVlI zn{eJ5s5sDJ&jl9}--fmCg)du|%@k}?H9=PelsZJJ#NXOC#A-r^z*O3^`P;$M=DNi{ zzsKq$=*M_A$BI7|`fz!jz3{3tst~=*sI&XGU1ImyTW!p&?uB$CB!2&PoQ7-M;NA&D z8%kk$6{!2c4Q~TG6G8+9dL*ppE7>99#w;7PUmxz**x=aQhPc54o13-+tbpj}>AhDf zgYECsJqB|1!(8rY1k(Lh2<315Hy4G6&N4`1W`51xup{>?o~1|kLA-JbCJdc+(f z*24IsfBYK&Qq9mC%|lHQo&zKkHV9Jtnh~gA1%N`}o2Pi4?r}paruYrkQIN>ly#55udVp)shfLIFt_$OcEH@`jLcR&3A;}roIgt;x^R?RVrt8*#f z6!i?&e-!>T78cwL0_5td+x|3M&MYy;Q4{}adqF8wAO`J5d>F6si&79{!Ts$WZsLe^ zvq#W+;)I6F6_0A1nv88t1NN#odHePqA_!v%c>454tg$77IpZgX8Q%|2aLkOi`GjZ{ zZc5QC@&OO08RL+Uh7s$!AQ75Uz!ViBfU@F%4J-ycECp9PalMX+ShEO_F-4R)LoD1y z>bjFCG}a7t-In;yoENd=LI^|9GJ58wF6V)jMU&X_n!wI6!?H(8{i8U5)fyjS%T#ac zD;6|OxaOq65Ch68HKj|8YKbfw;?~YSA_Bz(&d%Cv=M`sNya@1$ju4a4Aalk7=O*WwiE)WTFu)zQHr{EE1woiYkhv zrQ>PA&5c-ZLmIHoM@c%j-$}W(6fhSnODGBCT3DmF@`oUF2Em`=rcewhB_ro7iRS{L znv&fT2wP=t+Cg+c>Ox;iODtzG+d432UA5l@edI@q{cNl+P>E#6;OA)K`7#%O@@<%9V**b5s zLqokY=lI%Zz4MY#?JseCWXo=!zqb5solYermmjnss2sRj!re1_Mo7Is_6)`lUfJlS-D{ig9&_V$D) z+U;x4)cY}eIJd*Mes>#j3z2#VZ5r$6;eKQXvVPynJE4njK6}ym`0coCBYI?0()lGJ zPP_rCKZ@O)jH9eHY(vc4=^5PTMljkKYTHht+6sSl%m$lEfck^1n^9dh&jhsgySqJn ztFXlmPq^{I+@7oN8)Damj%@C)gxx*j6&Uq9H9FP9ui;?rmKu@k_LQGx;~+X1kvohC z8|4Uk+EpAqxD=1y#(7RHsvd`(tGmI)@Qw%_*fu!sM|8d=w(IO}clR)pAL?e7+m_CI zc>K`16uoxg;DJ}dx zoMx=ciUb0U2{R42Ju=>pgpYSX+zmL*D?$qR%};Ld?0v@n{q+NW_SFIJPAm4~4lHT7 zF)-mjeshoO=_|ax+T*j2K1LYCa%c>1QJRuVK`a5DNwh6$$=d=LK|yYr-$n6HHQd)M z%z*)}p2Q3eQf`jG67#yh#z!w1KR?chhg;k&2V5Tm=*hKh!HOxArPC@{R%VrvDPg~x zFh9S+a(9oA2-k53tShi)g!zE@wFv4zc(uoWWX89L1pzD8B9`a?@d!AcBrY8qo0?Qv zAbgcb12I8jc!@N~J|J_!ye=4rgvbR<5eswm0x%l2<}4+)0wEO1z)w&Y_ME{{aJTYr zF>3iH0j}_Cr7>+DCk^3iO=Zb`9Yd<|?9y*hoEAZhwVpb$#{8-kyt<9&f)FG#_qsBM zfpt}6Os2Rn(r$EAR!9>Nkd8}-C<0SZKFju$QXZWXaS^L_eU`<&owKxQjX8XEq`|?Y zr+tQlEp4J2mFz69mL;Vn^?)^mY$vYqi52t!{8$V)3M?i5m6ELII(aR^Ce6 zE==G8q%nZYib4_lnlFrM+lnd`hf*YjET4;>c_C2E<{ne)hgC2QnqX3d_gtqFLclyP zxVjlJjDsXzTvwzaVqFSKS&^3oA&v+!f>%>0wR7imT5)~N7{`Q|64n`*XG!3gLP81w zD-p~K#gsUiS%mHwu&x2w-2W?|J@SUKn{L2`v~a_3>UklWP^lnb8$n@HVRFtk*JMjX=Rk!qZjM@Q7U<0N+z1YYaj0V_ z1LkG~6;U4%c)Nd{i=$&q{A0Yi-!!cayzPMw*)ELDKXcB$`c5y*>rC=*_grL!{q26d z+Elpk$XPpWFMu69d2>s@ih6ft_Nm@zZB&fz+DEtE{&O#UQh%R2%>Dd?*+frNI<2AW z6R1}7ex94S8t&&#TMBRipDUagb)WjRJf~x3q27#T!@OK3jO-R$v9VEW_jvD)-C5YX z`EBvs_y@J_le6;s(YvhqRq(RmU!vTXF4ZM7L2XO_snQw-H;*El5Be?B@UmZ}O0ViX z&yJn5vGrEri@VRQofC4sg3c>>(2n9xq2(2M7hf9+S7|+atbJsB{)6<{@IcnqqZ4gz z?{H4X(!OgOUNb^H+-eh-=Ei_8OIhbhB=vdc1wZ-AzrhC|y~gYJo?$&@JbZh=RV1un zL<+b`jDPeG|30P^5T*%a8Kvz2FRbyeYTP2Yy1`Pz8!4bH1w#y2$1AKD@I0O{(1b5h zaC-_k%`-RYQ_$S}u^Scw~IUo)@srj7-{P|Z$;Nv|$(Zvw~K1i1LlEGPH zQ%eDfmZ2RN(YczdSFyOjn}w#)1$I_9kSgo}fJu2zs_wW%06lqvkB0}m<$}y7yvYf# zP61(mrF&QRSrp79H9(kUNZgSbnRa+_P54C&Sl`}ai~%<{SK^+z9uWfJxPFVn!-`Lz z?@%b<^iV*^SXkm(NrltQ3?L$BE%PPA7K?Hg9E)mEW(v{=G42lwu6H}60Ely|;7kkw z%ev|oDK*yLcpceyYDHfcgkfy4=P{@&JJ&tl3N%WPk?Ow>xi{+nINf4Ei&)#g=c+(*Cnq-rHH{70;D|ReFtj z_Mon>JFJ*1eauW4TT*T;Yr!-PDDw;p?Xa_^sNfL;Nw_=#1C>e-+xcbVls+YQgn3LU zT_iQ~6-ESFLP1?i%UNJC@HYBD0n5CA0POY??%p0S&nN7zpI|=8nAc^+G)|x>N*z|^ zhrvc_s;}5j32_`jhk%oYBBdc!D|U!c6k&xykt;LQj0WwDtZ=s9sJXj~G6GFMMD3hl zl)A2^6pX5jHTEYEVbQYH>ng(enoDh`sGJi*z*^-bswiLbie(1Uesarie6D54x1@~5 zthDpqPULpRJ47RB_IZ28d(Zjv)vkDKbzWgG+}P}uIvn-has0G-9@rJ5O>BUvn*rU6 z`x(_!+VfM5$Aaram%lP7)^@wAo>#7s#&_r!Zd{u8F!hE!2mZ>I5Lq2`+xUKjc5|Dh z3h2(&Ju}u-4pwAG__%M!7NF+u;C7E`9XlLx)9)s%$1KMEP7RJr1!jhcZ zwh@`FKIUB1Yr-3~sk%)|ZUO5WIO^&d=55<4q=y;ivU zJ>wL5ULf~vk`9zMNYD*g@x2o=-F*C$dQ52l=mg@62M(RXqHp&+wy1lCfUWQOR;zku z;oZ)Fyn88~-PcXh(He5|^v@W~z1yVG3?tB<(etFex+i`YOsMs->9K3~J74u6-mbkv zl=HK$Lr1Q#*c?u1YtYfF?L01ytN+oMB)P0-i3*utefb6-ef$AF`u;0?@zZa>clTIA zL?Ymy{MPqz^ZEmfQv^Z;^QwnskvEbgR*dYDfk~WSAgRNPAQ`+vjEF-<-~j;<`>^7@ zIN;Ucgf|DmU16-uKo}4cV;B?mS2qAJI2~5Jy`S;L2{?_{IIbCQ4)?e{0ry0hIN;lT zz=!)CKD&x|5*RO^?eWp`Yy9<>2mJXr2fTfA!UF^E<`rR_@MoXD!_VFw@cW;-q2aY!OjM3j>l&&NfCAilGYS#N)k5b8Sj~A~BUiFZi@Wsh>KZp8 z3D~45%6M42yuim6BR>=)fL*gp35p1AWZmhWW0L`@SC>)7*{vf$ZNrhh|f5H zV#NRR^#iVtVi5_aBg!hwP%JALv6cYKr66R|`qU1G08Mxov97Dq0b%4Hjx(M;O`rlq z087rWTJFpM7hs+jq%;UqUIJELA@!1@BxZ}b@=qZ~tjnTSbWwhkN*`n_d0}vB`&k4d zacM6(BNRyrWY$pO1Iy8hk#luzmUGDXCu$yb^+1%qg+G;N z*_5|cy3@Ap8aJ9#TpxC}k_yF^+r$HbtHK`5AVm&Hb~o$U8v~qb1xsNJ2@;-x{tS}D zXi391NQCZ^T_4+*JR68GL?B3Vghu&Qm>ven_+JAQNE9Pgnm;!J5whN!b3qys){3KT*(DE!D5e;Mk{LlQ==G$gme8}TD?q?JufR2*=e8kH z^Z^OX;$6fV-)#yT@h(#j20aH&5g?V`B)PX$5vXI<@h}=6A)qW3)dfDVngoJ9%(bki z*JfC*km^Tb3Ip3GN`DX_%EB$C8-7}oRZC*PBZu;B-5dAlyh70oH}&o_{tt~#m~2@2 z#7a%j^Met?%U-m*hV^lb}ax+I(nQS?0AmX_A$OjGO@_niJXMB_Gux~S~H z=br9($PUp>KsID;ZZX}OY#71Db+)e^V{e45KF-~Hd$ebfJLYyXH^2GLgpkcMzGd=l z7~*cF;>TVWG-6ge@{tG5M(p>|nH4qcP=R0e2wYJx5cDIw{Glc1< zU(ZC>)fs%xM&h=a!>#kX`W7n9+5n@`ITdz|zWGD*QP{X1+exhLIt1>uICVVrM7{XB z2vljCTnxHrDsUNcmlTezEwtyAIptDt%o)#~TqA$-0$=}B1jQVH|MYjhk7>U{7?Ut2 znPGWajHWO_vttJ^m6lfl1cV3zBSaCd(l}z^jAdPM0%Ij$O~C#-VE1sqS1V!K@A2l{ zEmkm|4+(Ptc%SgzMED^E9COB-ha*nQ3%tF*N5~m*VjLbaPIm=gJxh4=^cv6a2>W5c zZ+&`=-}qj}PrrVL|M%@Ze(}W#-yUu;(hhId1^?TRPk8;!5r6o@5AgAOFHph`D;SBn z#-j(QyFT0b-5AnZi@nkVs{1P_En$%6%i$UpT+nC;0wGdBU`7fAx_N;Q(}16Sdx!h^ z0sH%at0xH*BdoY=cdR;IA^^3XTS^mN(u!}NK0&xUf>H471)-3YRwArJ#DQ<|`I`*J zgoRfyR;-yde~!4H7P2@{)^i4x7IKljBuV8&U@8RMJ#=6>I6$a-)Xuy>tXb%xW6{jdl};Gb?0JKtLYOg;tO9C=SkI{@CuWP4 zN5e~LgX$RxwTc?lh>borH})EvuJaZpk}%4>Q?bW1Zg8m#ojW`AgR{}$@=M^AAxa$a z0Fe802)Lrs;d&dB@zqiQY_i1Au5Vqi%^4uo7i3Qd2Ny=k65^5+51VzjC5R+dNVbxz zB=yX%_K3rX^>jeVD^f~0EeldC$m@b3jhI6KXD?U_9xO{nBq?!t7Zb{Q#Ja91xge&b zr4A#2rYsCZSaZRkapfjwRPQmzhk*#Jap=RCP#B2p&n;cVqNU16l!_GGIiV{lNz=?) zPvSbSXMlm*=wVqG08>?LF-ro)S`JaqUq9xciQw8f+odIZnYHQufAvS)dx7=EW#=CSh9k1`1x9>-r`&$hZB zsPb7h9&af>Yu^nAoBL@qm*l}xnWApuZlLy=q+~}}NHQksCMu?Qv|J$>odU8kn5uoW zgP07IGFQ?7Po0Jwp}uWb{4F>R-y#{l=a zlSdz6(A%zX+s5D93FjL(cRNshXYO;?mUgAf2JiCS-Bg=!?ZIt#2B3#Q=SIGr6~UJ# z?SNA)wz??WI`Hjo>SNbk+Z`cso1^j=KYd%zu&tp^>C61~PI>!*zZ!@sJ#wVPeV zGj!)0nl)~N2;3g>Yq51@?qb+oe=>JZ>iitFx8lBW_jDVq+pql{O)CjYGfxIzmtxzjg#P&lB!|$=%p>{Ine_+yUhur?wb9G||De z!`Z&?o+0D&b%w#x9U|8J*}ie~zHgx)#O7uAzJ1AFaxmcjK&)1M2b)W6H^JWly8j@} zdu0`G4(p4wqRq?J*j(+mAw6!pogJpX_T^DLMqJ%oW2Jzbr#Dz$&$#{O76UW>#UK7t zq}?8@Rx)O(gI#I3A50ZG0wj{OfH;WdU1O7>wUs4{GC?eH0YnMByTWT_xSzhb#uvu} z-W)Q<2<%b+l3G!vSxm3FE^Zj(oz)mroFrWM=z~T(C#M|5-AA`AtTd zBGQR*Bt}^a2r_>PQ8OO9vya6PnCl=U^LyNE%X$mIuFCFU$&TSw=!pl8-?G;RQE8EDi@rqvBl4mS)2rkU}ex)Vbkwz zJ?;G5$W~3pQF&Ke&B~xyWrl#l89mKy)+-ZW3u}@xJCV$4-X)N+X1F!=|8w?cF}Eex zdDyqA_CDty?!7&+2a1DQY>^Tvk&+V0mWLpgg&2?!`$0)yzc>hx_rOnj$zz@a1Tl~& zKO`^`zyjjLMvNFqAScRTC6Xx#)?iBF%qF|p)4hG~|NrNly{lFp)=;ZzpTDVOlYRel z_Sw5?)v7hus#PxhWFAWe=2V+~Q5o|zfp~3AgdrKE(r?1f&bDqG(Yg&mSq3m=DMTkH zRX_ubtW~@30>3a5^3^UtZ&IC-k!}3ohop z@K9NsTvt4-OTk)JTwP3fJkMB`1J-3l<%$eF7o4QFhpfOWKNbcis0(MyXrGsR}k$yAjPaz%$cQ1y#jB0-*Y0YdITe@JUlH0R|ZDPZkrZQMNd9U zwyi}f$v3*Reu$E0DB0jD06i8jQ9EV9?V*1Z)8-_AbbKF^1 z(0!?&L#yKexZ$Bz2Calu6`~o4DZ>%0r!|O^?e#?%HfIJb!RR(jE^PIH9nc=_O@PSW z^B!R_&8I_*J6@Y+`}bkSt?@vj+kGzdX&k=x(+ zv&+NoS?+kg&0w4D5maSfve3(Cx^jjnZz?yK7yaycp2s% zGBd6oKEQmj$IS_N`s4;Lz4#E{fBzXi`{F}<;tM|s5TULosTNI1-X#X3xU!LBOx4gX zjOFGAAH4qrZ+`E6eDLHMZtjk_D?mvTc$$%?8CNMGQxazlgv?3x5I{NMb7stZ!s%j%i+#c;A70|o@d1v^$VJu`)v!Y=vM^b4_Le7IX z-E32n$n~BqiV`@(vkaX2bkgK#DVCIVVqm|+%dfr)zI}>xvF~(=OCf5K-|PbMvDA_0 zf-+s=x#@(bc!-)3PB+hRd3gnT36N5EjGsF#_?L$jfBJL<<_s#pp{@WlO_!^zPS*(_ z%>XccqF5q8R?4!_LFI(U&kp$beuw7t$QW|UlGa+p$R|A_Dkq>8qh-sIQU{e8Ic43$ zPN15COd**qJ<84SZuSyu4!UGTtVo4`oIA6KndK-itB%il1XYaz5s~?l1)v4vm~|gJ z!;exUG~BQ_k#Q|LN+q#9tIKj|b#784mAJ}rgL+S}827!#7F)2siIgBj?dUYKlK}Y9 zb4cwF7q`(uK;00ksrz!=ptRnnxDP&>RV?B_@3b zd}@GY)0-VB3~76#S8;l4!8~Og7Y+Upz=}Kq;dnS%-eIAH<#g*V6dWGG>wnep10hCpON;%unhPwJr zn#f}?0=oabNQHMbhLawNX@n;-K+m{f*5GB-t)!d|*SSq!OFIl$ZOGYN|d%f$7cY z4kDm^_5pE=9x@+G){|-U!`TodK@{LYhl%pY&a)m8NLPzK6bT+@sR7e`2bLM3fBH zCJao68R$KzW=*s)7~cF@cP!2UA{D#iihgu1SrKavTXZu`$@REzli>I=!Q}WM2PHne zbp%J&XEeuwoksjuZgUZ@D8J~;_=l%4Le{}S?Q@?Wa8EP-LZchsZyDc?S_h4nnluYS zk^tKm=&&`S8pa-1xcgzZZ6@5!{nila1+>WyINg)e0|K4(St8uE#G$=W!F11^RUUDC zy&UwnX&h{Ozo*O}ba4dcEXkZTBRd8R8flQtEgPpd3_Q9%%Bq0k_Iz`shZxCbfZN;T zU*c~z&){u4MAUze9Kno7&p*U8&8UYXj%mi@r$_8}6aKwl`722KN8oaUR1<1SvQ+95 z$d0aZIN^`K^&R|Q|KeZakG}R@eCypO`0(b4QW#~exIGmdjw^1C3zkE{S}ImOGU}xl z9^vE9J;K8W7kKpW8lQjdC4B6KD_mU7m~#St!{ZRk6Hn2(#)zVawY}ZvH~^)AU}K#cI-^d2s6jDkJs3k z8Oc*(D?l?I?J^E1xJ?Q3`WWfaC1{^PdqU0?D2(a5H~8w4iYG{*{e-(kRxo8hZ9?T$ zsyl1JyO|P-!pb0BZI?aiTq>TO3J%8;t}Z6*5>RuJGof>NzK@MT*UV z2(_%p(`-nC1s`0OQj^nQSqUXAo>v{EQoIn5!f#dtj%@R|i6Gs_+&MPB&s?2vh1<6L zSJL!dF0v*_wVjGVtl%o&bA?1}$VG|Gvju?J+V)s zE~iM8XuL})XcTU8K>(_tu8kPHl$>lGtmL5c3xSE#TiO0r96?GL(Y^?bc=>*b=ki@BV{QZ$6PV* zCM<^qGYF-un&zItbyZrl`UVNd#}OadHAuAlt|CGlXupoV47sClvG9$LF#j80Yxo>C?`Ku-P>(b zFIh&@r0@$XNuj%Cl^LKWw?q!=sTNx2W7UoFTxEgPXtU9mRqhOS1R50@KgF5>h7<~? zEQtAG0c$;VBlZ-IY;uR4!a|eElzt_?ylsYKSU+m!yFNe%SIn>8LHG(q$=B*j;#`_rR(n5fo8@2vTlaKDq(chVX@Yfqm0 zLbMr6M8lZ?Qv*yXx?fdxHx@t)j*PHcpYh$;7dagL$eK&Py)3=O9Hj6j(+1XVRX%7d znSO!`j{qMKn2+1DF@hEidZ)3Dc^dO^_qXBDUXCmKTW39{TzBqZ=(GV~tHK-Br(Q_L zzb5eizh2$y`3AoJ>UpHWexEpZGk|;Lp+o8a4*w}|KIYi+Na-DKhwm|P)V<+1i~|B^ zalkd70G>96Zqp8dN2%L>OMAket!=ez|9&Nf<_-jVez*n z9~)JS8JgTNU*A%fVm3^B9|tiUZdK;MR!)kW!5SDtaMgAGneho6)OW@JrHn_kfx+}`>O-w%k3@xw}1X+{7=9Ahxq23@8fhTSk{V@8J9(! zB?ji4@L)e<$rILME2E54S@E^+zl-&|vO4jTu@^z_Jp};lKK=?Wc6*eIE3DIdc=nygc>4YU6A>;z zI4mcuT1c%hkXTkvR|X18CHB)=6t61BG}S$E!UrE7@#x|bAfTj#nxqI+o)YS5MNYQ2 zHAyk9B#SDw${EsSt>QTAabl9{yCS&&W}6&9>Br7|rlN(~pal)NLd*4wPOoT32b%4| zvZoxXr`V5b(>Xv}iKWDn+Fm3$;rmmGA%|Gm0*A?DGajQx3Np)A(iy%F6B6VhJ94eF zG0q4M*A(2o>&m{T^i#AL)FunGYeK7SjWXR3irpEUZu<=+-aHd+BRmrojz89(NtMA~ zmaex!G=!m5_GMr2(6jd&x7V4WLL}KYlLqZvPH;Yo~49Z7nF4Y^A0&>cW`qS z8fJ(LGuV=*m^%(TQQhdJ)C#ZpPwn^im9Z=Z(>#MAtJ#t*A}jEvR+Q2vjToLuA}D6T zA2a4DOJGWcRLU9*3GmtW0#dm*C0UeSR;3%;i(!to)AYum2=*B_8`L5P)|4g3xZPCz z*i0B$+K93kmWA3#%^IMl`?c59@>ApC!jT@$H2E_&J>3%Xk9>-2ik*c8a;6q~|1_o?823-mF(ZUWvo14*C@8u(hs8?V(mt*~ES=%qc8{?3I z0C2(?^+xRKzH4J-H-X$E#Dwb5-Ue^&X2W0)laudRhp`z~4Uz~`v!cDQgcEq^!05@0 zwKKyupiIEpd9Q}o`)bg!G8c!o$KkjA?@jPHpAU*X1_GM$J$H@BAAa(W4L|oe)AwCw zOLWqKps6xHxV0|hp;;K=z2Hy>{2Fe0yTgpK^8&Fqv|6~%=!6I@u-8G*|F!n+m*b{h zWCg_ohi0t%7~<+Vy5Ec*x5~mMz6Do!^IL;q^QHnG)yjq*(euWJRTcT2ad#F24(gwc zs)l(C;G*p>Gu)non;bUFREcwOjveAudnm|_KI)!{GM&(jntQKK!w%y#o)6>pY?d&t z-Ng>Oiwjvrf>!DV@H7AVXK?Z2$5D>YAbU|+&xaPK;IiVkzWlrR?|=RO#<$-50C$H4 zcSmu~iyCs44rzSkJ6jWwq1CsUq%hhpOBH0*`BsGJ@$ECb_w)%Y^Prd!JI{FK#cRCs z;1Z7>T;R2jzl<+_`ep3*mw2lZUgL~umbCk-j`$o_Eszpe3xa_md*yQ$qa5nA=Oo3X z)Oa(;o01fmlKto6sJihU9CT|tS*o6`RGRT&K^=luVfVjA6G~UnsB{$%gnh(1x7x3e zg;raqUgt0oaQO(2_=E!z%Iyw^n`gMVzQ8BvjJa0qCLo{Q#n+x5krHFt&A0(jimWcz zvSP{^yDCShB?2Z$p)WW*5GT0+w`;-U+ao^q@B$MTASWDF-MgA6Fv}58Tq`P(pDAAp zP$4O~rL4%4I7oR)I2|oR%X%~0N~*6*^=QC!TGdL@61b>d3@hir%RwIbUQ)xiZCJKc zdDV?R2PvVhvTr-31aU6aDNd>1r>4hb-|BV8rX06qRKF1@%7g3zBU z%oOAn!e2=0x6udzzS7kx!Jx-9XaKa;QHtF;8T8>u?w}1pQ4Y9Qxt3Fqf8L1NLaIj- zF$xhfMh6|8v+s=rf!NaXbu2`xpT$X^XFbA=tHbB7=F!g}9${Hf}&n=oLy=|Nr=62deB}oTi==tV7 zZW=%%=GC|fs@90c1y4{4wu&QXi;q}4gGhsQ0HhO~;cqhtH4Oye8HIqt?6-Czrr-iz z`VS3v%+=sCbe@Ah48nnL{5(=wn{x`!N8mbo2}&{Ys5@6(`{t+yiR0>|PZ_a3V(M4F zVnXgj#O#gjxZxDu#%z;y7BvW+2N91fSRv{bd0Hvyu*L5lB!SY_hW5h@-6#CwTyw z_-{(Z-~OvVjbHrvFJQmhBeATO*Q&t)XjOF(ILQjQ9i<~`O;^U$flm}2bc7?g)QNXq z4~}_Cu3HH3N^k@LEU_d2VO6sGiObLc@PaYIxbsz^&IJPa`hbu^P*kn2@7MiiY zWV}29Pp)>jn`RuJ-Qv~P67tm*kTcSsyp3-?xy50rnCA&M%vdzd)nK$)ODXBz)kd=f zS@mNO-hTHPp1Yi}pEKq)VM)NcEK;?Zq!8R%3#jUT+f1k|sqXVUX(6`iXHt7`&5B`% zVH@owo3mimibEuh4-tVA0aZ>Juz+Mr1eWSy?i4Vy@4N5zTa#*2BCT^!RBrhiom0G1 zILqWW4X8yJs~ME7g0TdWWX=TVDfrKUMwFH)+6>%i#q9uQP?SNI4!x08?Ekvs75Gr# z(JX?ne|STF4nTm>X{3>86K34W@rJ<$o1}eb5HI?-_1N2lCKE&3dL>Bpg-o({TWkP% zOZHW3;lk7c*-~9xP8UdeVga09fKm(g`w2IB!g@M_xni0otYtw4qbw)P^Bzk|LMKWO zAYCk-ScqST5BK+c3*6Q+5G<8i?>iFmQ}d|v=zqsgW}&Ayc)kF3o> zetiG{AOJ~3K~zV-k&cmcMZ8VBZ6GLGkHLb#g+H)W&MtSzPSamv#ryo2T-fa9^MH4=Zqmb{DF%@xR*w+2l zU=v`%y()^_8cYn0FzenSBCoD`HhI&EjG2ML5V@&G1W-J#jr#sOM-+j0;avFcstY~uo=`_ zLAz;Mtt0N{h5NDUZ!NJc#^AZV<0FaUzI|>VY*M(vZYqyLs`{Y+nnM#6q+KC{A%Nea zgZ5N=zDG-QK)h%s0zUl}Ze!>mp$;5!9C91)pv)ubHAi3&8XRyE{#r8~ZF=?k21n=o zj3~S3A_Y$H*0`;k%@CMqV=}7X@NdKw!tVitF)$F?>*Q3NSA}mQCVEy5zxE&L%ySgr zxzBd5XW(o>!x4V!o*GBR!*`&uL-)ElC+C~%?nSyd_h`p0XK~eE9$`m=!Srq%MJI{| zn3{-+jyQEN!~js}@g5mJ3}_fblTAIZ`FH}7d+8w$j4TrQ?ZLw<%+rizT`_0EkrEEK z3;x}|_E*5H`y?%`mJ8nc)_3rq{eypu?|$*eCcKUg)e>@6ETQ&ORUNuN6VpC{? zHG(frXWLtD>7z+uAlrYQ&aAFe8>7nvElJE!N6|*ejV?@gGG0v-;ygpsvg{k1;Md0av2Ym1$UZ@58H$IN%9$e!0{^V`^(f6O={SOyh z%rmGItW?EuNC}w;t2#5832UutV>H>Gb4J}WzW3e}{P4$L!ai3lAaG4Mtra?R1=TgO6 z=2bFEe1D{HW3A%UM~>D#yyBo(@i?1r%e)|ngSCo8_z*i1W>( zI=Ou2DZl5kGo)pAIPB-b-!%d3hRYcU!rVJHRSBA-A75ccA3gIZUu&SvimTB;^X#2< z?hff3ahR)skw!b(T163Lks@$H2Vk@nWL%zWa@@<3Ihc{#(5L z))O2~D~^k-nje>fR(VVodf zo-*cX!dJfbZTy9wdR-lQohQ;Yd$v8&YK)pe06?u$fZ&DVY%jW`ysLwrW!2RUTHC8^ z297!FieuwPcXR?jauX&#%NqcPKzP4;H)9!2a6YB6woIp^C{UqCjkI-L4mGrMttgc+ zO?$lXT*ftk_b+GU$Imd&6ZRK-yz=e$@VkHfJ^ZT=ZZOX~fD#t6m0}5)FjpjX=xpVT zw0N31=^vg7-u~bYuRhx2nh1H4;#L*1-#>xnyyjXlX>eqkvN*~)p{y%7N%j1kGfv9_ zX4__vEA(sgreo(yU|F^oBR`QJu_nPEt-tQVQsu1`ma?FL3!EhF zy76f9!cMDPE6Cj|o!?qu4j)(9&o5v~db&B(mQu?bUK*sl6%5k{WUsGPfG2qBf$3Z> zO&7Brt7yNP%u^k2@~m>htpP;_Qwu0Lki)i}7_{pY3U75y;{jQjowea?=>*zfToCD$ zsW)VkAgkw@7&(zA$+JE$)!f5)gsimUw-n7xsTDfRIpK6V;qn^T?e|zuM;wnwTeivCuWOZ~4KMaHZYYE6f>rmS6Lhb6mMtEvfzxFB&`IP~iHbLf0I9zq z$Lp0+k#ox=i)cz<%yU9ns|F|~??~!`u;a28S%vqhOTd%}&z_yM8o%(#QVOQ2h&(1B zO_R#vHf=WlX3wm?SH>t?=+&HU_SEG;R(4-)Tp?4=w5YJbgNcn z5ZP7{?Mt`(4Bc+7vw9hU6t}Nznxh83n=$9QGuSFOd$rHnQy%ssavHA8gzNNEd!qY0 zHLQkasWs3V(+-28vv$weDoR>ABy8q#qs-*1rTB?suGCl?uQT_^H|Jn9`b=e# zQgd8+GBMekYtly2@sPI=CM61fpP^-#nR4c*!MameOqc{?aPyk?*IAc3I;iaB zP0#(4{ofuYLX6~g`&6l^jtC2fe>*^R43@s-ApDqzb|)B5eY3s~ zwYw?*_$_M2)EVA~t;~O*b(BMmoPxzY*uH>pgquaQStx7!nG zwe~Ya$&H_)e6;@(ZC{Bplz?srUlS=Da7s-U-gk}UFpSBS`8~gP!13^X_(u97pPN_o zW7`7R9&ASL7VwUx2U=;;xmUZ9rrWIaO7JW^21StF{UEmaHCurB95RP)ns!M!;qvkV z^E6>CMXCma@#A0mERZk3rw;+HU;;k){@eJ^f9)URy?37C?zrOau;AJ43Acw8x2F|{ z+XWX~@o>Mx^Vb>AU(R@Rxx>Xgq2!Ebrv*39jyT>f`2MpSym?!3sIr1wIymZVkP6H?BYtctg_ zXMAR|fh<+hbW~hfGwWEz-tJ26sU=HF)J%A9iOGHk*uADZF*>a=#Ik&`E=W zbdM~+NK&Nj3V2~A>~=FAfAAQex|nhG@De}t@#paR*Wbdw_{!V(;HF}}m~mt&f|eO5 zRmN_00?gqxd;)}dx5N84H@Mi%c;RxwiiF*iaa;>>$^u7aukxQ#lIqo3ER8_AI$c0{ z5@Vlb@Uq}ydu+v_m^iHPCM&KbfdirhE=A8|mTUm7VMWbpu4x^SIa>Ln)b2QB3&J2x z;Br*i-|W(s#yaw-+-s~}V8I+R!jMXKjSEISamLzcBw*Z9-KO2V?T&LhE7n#Jtzm763)+EYuYQpsWj~JmHw+R0^pW zFYlKMtm}e{-41Emfz}o4X_ah{+*a_XX~J?kYB9T(s!lp@j&FAYB;Z(A`C6IfbPC9% zxTsP3zTD3^U{P5kyj0fW0W7K#tg*lta|Llm)^vF}l1=D^AsGl+0|9x;P9Oc^l<;0w zSMbFst!On{fMz!JNcN?egakCWG8BagZ+R!TQ<48I{!V2=nSPs4E zZA9zsM4$)zHN1mv%%YeFKC#~hVdy;ui zFsHKzS&et72;@tJ$wXyd91 zHuG`z4HMNgNG#s=b2b~of9uCSUfcWkNH7E6@Y6&v#(#Z9X(%Sj=Y&hw0d?Q}v(w+X zC;$IlG|0$2PuT4yknSBWrQqpd!N2oY|C~4&wSw!4^>B;7_iO(U?|kP2Jb8A+dr$80 z^yvY&hXY=m6MpWMhxp8^KZH-c^a5UZc#Zk$A@b!Fri+Z35>OIq27rRKRvb?S&u*UK zz4xBrE8l(_U;oZy{L%M6z&p1qrYRxM8MCc?D#UTCL*u-jE<{UKOlbn8N%xWKOX}d- zdrhapoi}Np^gZ&GG6J=u*N8j~UYm1#rZDwn_;yuEe&6|2U+~|p9n+K0)kA&J4eHcJ zZ%o607}K_QGAdRze3}#oS^z;`NQ6`v3|UFe^93GEGp-Wj@y!7#0nef0h21CcBdFplyqE@(AgHg6h zo^z4_M=5d^eAR-9i7@Xb%}^2AB2wPe;|J%7bpN@|VUAHSxZ@sWdC>k&yH;>hzFW}- zXeNn}3s>RQl+5msj#JK9PQ`5@Hag@M9v1hh1{w=I7-+Kj5~Ie7#M-~P`AhIXM4Z)b z=wp){vXi%6^?V!Jk&o2dg`4&nQ!w@X<_LutPShGjjNU~d6`0rbnnjvI1j%}+W~3@D zXu(3)dlcF_Hw1CqsBc$i)f|#WQoTUy93L7I*g8q4zffFBYRaXz^4c`D_IlB}9Yf^t z)`mxgW7O!3QH>6#=h8mmIO;Ii?2dRVz!g(l+x=cm*a~e=WF2wa-yS_3+~aDE&|5%I zGnE4S5x}&`_8`Cy4TxI}oB@*#A+2(&2ihA^ z@DPIYf=+q2q3WCtk#o0C&Wu@g?I5%QKR8>QksSdkvTD_WULBhaZEq$cj8R_eIJO@^ zwd|T+cC>U^^aEA-GxA5gBT9(x$p__5T@P{nIz`?&CYW`PLJB@Z=V+?%N$n2T>jtsQ(0*>a75;%j!^-8G)aghz$(loIgp64S#gJi5NX z>tB5bZ$5d7Z@quOcaGv*Pgbh@-MK)I1D#F;{#;VZvJ zO-ev#Ee4J`nn6-%eSl_tZ1pg!6K4(^1=ySRYI;y(R$JK~7S0%GRid2^dzicH9^=M% zCd0TnJ|1-9#=aOj3-)qC(qeHyA?nh}N-3C;kyFA6re`(F#)57^4-p8dZ2By9u{<65v_af_XQ?GIXZN_Mulu z-iaL<85UB4d-c zudjUplOUDcD{T9P&72rI3;-G|Q+FAy7J|~8)kF!7FZi)mp@#T$4x#IrE?6IWn;kkb)?S(w5->t$|D~cf3fMC)`Jg)dow7?0XvS9 zLO*fMq_aiB7{wzqMSV{pR9G(3Zz z*iE(hAav+G;#Bc->$i&>)3)7)82CJ&Q`!-ZZb~tH4)`3XO^gl0fB1EfeO7{>RTGxK_?X9f{EMDJz(4vdf$%07&;7+)cC{99EvXnoCrNPe!Gvb zdndc~-{5E8>Ja5#!$ki+8SDltg*IJ6ZE1KE_MI6tz;3;6<~RsE8u*m@*=e#I;d$@w zK0-&w1bvqrlx&o5+nj}VA1V(vO}F+j4&NvsDlwab7LfE{p^b_9x!X#O1_KQbj5O9m zGJK{jeDRuc_VaB|tE>Y3{O3P|^ynq<=>}X@JbUjg{FDFvxA4~YZ}8P`eSr1ufWP@u zuj4QN+)v{97hlHZ)eh66E6lqY7qb-jF$Yzbs@zGc5Cdqfatzi|3f2N#6~^<+i+J^w zPvSE__7gZP3zpkwc=GrO{@@S4hClqyWBm5F-odk_Nc&0BqGe@N2(FZZyJNu>6MpH> zeF1ylw+*yaN^@Xs)i}9sHRrq)d=uCyB_yZ=;;E1=NYL2?q&k|g{atxjQkSDV3Hpp9 z)kGQD_>P8OEXdd#^D}bb9D#LoPWFFI;h!XZmjR}VlxNh3&*AFgg#F9T zaE%u~c#2m(c#O-he}K1dWxGS36M$Kg5pu%aX+@<)Q(?UV74kIWt@rMLhkHD{nsK?C zaC_@eEHOIK&oP2Qy>ePfX=RT)WExrJLHoP*lzE7zFLzypIlLo4;WV2+0#03df zc)EGD_nSU4nI_Jky;B4aCRn@~qbvp>jz?8zRAejbsSZK$u%^G?BVT$-Z7?=PM6Mst zy&W}fCkLqo$-3v=k!%j-xooU9hGti1lH(AW4(?qyq zLRnYH8R3RCd)EpRMja-{5?b0kv!%Ev>C&lL)9Bw3gzy!ob(zK(c z-nxN^rerC+=l3I~yYJdYV5X6ldr+Go-&};|(C`+Yp|?iO=y7YjANMJ9{B%w#+nj_p zf$rm(8+}{nqCMRVj41R-oC@~vT)g^~bZSCjB*HNE(8ZQ{qEi%B*W%o=!^EADHQ0B< zKwCmhgl=R~LLlQ&dT_hljR6RR5tOw`!tS^UC!C&fEvnciEE>6OV9iNyz>G)EjaL`Q z@SBnDwwDR^^nMEDPX`Oo2VKk)@T_v!;Ydj1OYeikQ(077!ZDh9#q zsI^q(GcltwA(t#^qm@zTj9O=0m4aF?P@a2;mtT7opL*jB{K9g?@%DuG-+mWg`R4cV zCvQH+2k$+`H^2V`$I}Tl6F&L!HQxCA8~Bxf^NV=pwU0^4DC_D8S+JHoHPvLeoF8nC zyRMj<<4?NBl9cE5RkCMc&5%$1mQsvSsHqHH*_XZIO?g_%r@iLnqsuJ}`2M_(JxqqG z(P9;mJ5j7KSmO~|B^L(yDvr9BF=83ZIvb=S0cAcJ{DEizBF z>fYKgmnN5tUaT-DYcaN_b>+E~^mr$g2jmRQTG1WqpLYffYzfz8oNsi82L|l;;dT+C z-0!89jUy^{f~-KLo@|2+G42Z&Cq2sHhSyQFM*FHG#NMj-NTJq^&t7M%c)lI|Hu4_P zU}KMNt1y{3u5#{rVHSF-MdT8*24EzE#wv>^f^NHzIvQMKp+H7iSL}AOf4wX#%CaC& zdn~0Og*10c8MWxfaCL}l(f6l4D9->Nu$C2-D<+yCr%*|e#Il^Uh+&w7LAN)%vdIZa z`BvS9l%5Iw47Ur>q5e&VC|+Z6RTnLVH3Bgvx~VbZW;|Ds80<00}F~ zpu<^DXJE`Z;h=iKO%7`L5~5#9w^ARcyVX-@tL`x<>e^UWU{lIkk>B2zHVJUq0}V`o zWFLAaSwZiNIP^;ouD|`)o~Hf&Ir`W4y0rFSg4wt`8$mm@`5yaqG2gMr`9;e1Ue7Nv z2sSBRHqnI+CorRq`+<&fDt0~$wA)9lv}~*A0NNRkKocm|NQl;$Ny<9#Xr0 zQ0j~V1U{|~l+D$pFwuP`?siz4A|)N{4b48a6n}^?-Upcp+F@RY$4RwXTPX*?y))j! z69iGm+4iuve5Ruw0DG-_lDwrDv;+b@MI3Ec3ROTwB)iaRnHq7tn<*d@N=?}LV3hzz zS~lSoPRi&|)dq6rv%%o_6yU{~G$oRsD_y&9Y+cDxtUJ7v-Xb;0Z9qFsS_mlhl7jGjP zH21(0XD?T1>ig>EbC~CI%6l_5#{Kr}Xed+Pj|X?XQ})!#oMi9tVOj7C z|HjXQ>p@a}@1EhG{Xf5tKmNwMcyYhOum0^{#AjdsG#(n=LzQ#!2QiIKA4r4qDO^@N&41|()o(}c_Y9(-Bx(n~MmGoO0{|5hnj0F-r= zbVjZ8J*5e|1Wiv*&CsIc>3*QLI_}7_m2slxe5-SA!MNt6hvz&uG~(G`GIERn03ZNK zL_t)XUumC-TB@`?YpQRE#I^h39+cd*;5O@=b^*8OtOb7W3yZeN-%1#j1z=zGC6eNJ zoCs-Zsq?i0xe_ifuaWl`m>({@a6%?R z((}{Tb(NKQWHpW_Jh>~lU2gHQ2RrOimZO?xNsB+77RM(*?o`*cpn{MyBTW;Qx?(?z z(?2K1QW>O9xNMk=>YMc2(yobp-mI|$~h%~3jkfcOS#Qam}{Yf)Y-yjLXo}eCn1C^!0>`U(UVwXNWr}L`I`&xfSJeklsX+iq5t5LA?#%cRk$YwkT%fortm4geen_ zYemkJtb*%vr7|)qrc5|#p*SxfnG)90WWInyIGq;kF0YXCjB>iedOG1^cY#IccwHCl z6EIEL)7%rFK^;Bfjeu#MP>%%EDyI@CE>cR!Q^x5~G1cnUn3E94>6|W)Dm?x!i!IImqQ(*d4UHc=$~Tg?7hDu=4J*wwi8m44g3=n$dd> zyK(Go;EGEGXE(Hd|1eES0%2k>ydFmq=8JmltCHdI{d>63Q;T^}B5G8P(co?mE8}d@ zvvbVeQoU@%+`%@2$@Wy&Hu-!<&^GUx(q`|_;cxUAU=3Z^Oxawt%I&Q)cWmt)1%`rQ z)S<3np*1khb9_%}f!S=Wxax9dLiMBblL_QV>>ly!WtXQ&+Mi&IG#vDNMHp#g9D^!o zZ6iA8TVrOB6P?T(`7iL1IXyNrPQO80bjpZA3+<6G^wuo`s>8j_qz<${kgAE+9y2sN z(#gr7lm>Y04L2A=-|R+hY&lN-_|9O2BB&^RHnuj1fX#c|J+^3ib21&7(a;Tglxs_< zEv4d>>lv^9$YpA0B|AW7QkAMEtxO#YjdDhf$hOCe%y(ES> zbau2Eb6f2M>;76@L8nIZH9QECC!g2K>X0AX1f-~aH4tBW0WIboLw zoCv#l!by$pDN8{^5Nma9sTfZ=12ti-RaODBI1$VRToPEbJe(es|Lq)b`A;)Jv=~+) z*)Q9wNy{odJb*zcnr?0>=WS|R`%-g3+iwD`(<{DO?Zw=xj7jOX*QUW1(dXIpkdp)l zU7|8tGYSNhrtwUVnhm81$O>i+W9kJ`)Ggc$7uOA=jEea0+SH=U!Su1w41(PJ0TT8{HXRwnL@5j)ujZlp%wl(f!?W-Tet zRr3|BSfR?$WwoMiLgThz$(6l`U$4Jg*2%gQWQAd}F$|Q^R^v^+E8buWr`2s6!4E5r z6_r-Yp+%~E8)8E~xo+&BwdY5Y9mziab>+~sS)_6Q6n?VzHrz1+a`v0*ELBX+Txw5E z-HSn?be#uUdC=)HU1#-I;!xB5Lg*q`Mj(dGng%&1b;pFd#^Ei7Q!- zZk5v1#+d7I)zgpNQy8hO?(|rZ(U?$g1=o5b(dSHQ$;NNW-%Z>AdqnF9pQ#WZ1NSeC z(;$v?Q#3xzpo5(mN5UJfNZ*dT;je$TNpZoeI6TKU-QPa_H;5ZQYZt@37#`zz+WY5v zBURwRfC{!r)80cmcXX^3_mS2N-cjDk^oA%kQBQvjO&f)DCdT9scJmkbS$MT&^!!;a zCnXM_wWCc2Q1?akf;D3II=+KGqf&h&q%*?ZWL~e!1|^Oyz9N%yoYU~Y7>I}r)~)h@ zP-_7Za4H3y82{Rr-T?9?usi{3!EgNLm+|cVJNzeq=WpSSFa9X@7n9759=&4)4;7P+ z2Ln{-r~Xeh|WIZm5A&$4Nn8Q=tlcx3N446G>%;wV*x@G{!yA!_ojj!R`Z@z<1ed<;G z=nsDa`T9!m7mt^7)LF14&>!T!8^!)yU@mu%H&*H z1kPCkC`8~Q&siFGnr3|B<>!%R#*P^8-WEK#uJ}~0_`=7Y!;_mMzV+^hc=y>6Z{IMG zfweFS7Tx=vu&#`!w@2*e2^X^*Z3htMwV<%3mu5K^oh;qoQjk-UGneOtlnJ>qR+f4P zRI{WaJJqy#6PapzWg9R1InTr1<5F0Tc_T^nhJY_mlgZBP^DWCSmb(fA&sv)%aGIhG>EdL8TZ7v#Bok6HyG z5FKrVUit;;2?CH&!7)95b44j1Z-=?(rc4bl6ERk@J^NK0NUf|sO)k|1Xlhn7h8W7oQ3nkJl90w7L&o-eh^`RX*Mtwk(4ECHZez){|EkkgU=Ope>U*l&?74df^cBEOiqX?9!3 zCj&2qe_EDn{IPX^?uxR=bYrwr;|^C{LGJ)v zE4=679@~wU=I~eNV(^Jz$ZIrjj86V*yDtqHdAE7&Be$Vh`EyRkZ60rv-?N0>gVrKs zoU^JsRcQ+>e5;OSE(DfG$hJI?V^qahzheBIk? zPjz${?)9^8GbSJHqzwDTozRhh|BP3}L%bShOg6N>h6bEh8v@w*vG@I&X=v$q9NK9H zdEbxItj%SlIBI+1+Vh6TcD83vWYo(q`=MuIe8EGvHYi=PMAMd-6E_-Fs)Z{qL%$NxTl{7awneU?>M z;YceDH(!LpX)3|N1kftqHX3#?$X2a694`hp9-Ev95`h4%GYy7r*OE_QRIsPCD@Dcg z!4X8E8hK*xlJ_HNyFyoH2g)vGs8KHWGC>Q+!Gj+yxXb`E?w&lwum0VCfIs-=cQH>= zA^lU&Kf*8l+o~ae4;>tj+ zNGYRY!cm>6nJd;sj&3SNR@G$OA;{s(CbZKW2w9Otk)z>KuxGL!#l^LN%mI;Tu>=d8 zrd4UBgt{7DYvWI@U{1mxL@lezcvx|472jb_Pp5z*!=Tqk-DU5X3=g`-iaC;SR13rg z;<19tO4|$z+-M4O=sVgaZpQ>DwsuWWNG3yEK{k0A$P{yK&Y0`NbHhd(i*0#nFE-ps zkubtU1x4}Bwo*%kvNGmb_+4TqBGWwWnpF1&UM%a1{eFiV4ce@y6XwgEjFEJeeSaxV zww+j@=>ss{W0!ban8Zap3;*-!X5-04wZAS0%H*D7bftJsF6i`$;|TJ;2kWCa#= z7>qE7QBkt;N6`Q34-_pJ>3WicPq`MPDIBp@g5AMQ$4iZg3g5R+B>T^U{AkOQzO-x9 z-&oY_tN`{oRezuQM`*bre zew<`~w%}2Cp0t;^(kO2=$07XCj6x2B3I<#pN7S*Mk9u|z*?((ObM6Hr6u9o~2S?rb zB1-;DmvR6{CDAd=5GqjF9_girQUyzlI>%>?!709HA1vjByp3p_8$lUlKvNE;@FFdp z9ct^!jW3&f;o$~%VDIj8mr}5e5N-P$m?MrkwoC!9ec)moPMi@!B)(`!+iMYakNEpA zTU-5H7!7yK<4gFu6E_B;0D#)p9)jJ|!vBM6+aoetn|-}tnDEM$Lh z|E|eD$EJTCF(*;;)?Lr{Jqn_1>l%Sc@W?YR*YD%CNaSGv6^-T^J2lMP-sN*XeGE)= z$PM%2ork&J$X)9GY~dY-Z;pA7FGW%3{zn!}r{I23ib z_7Nq3eOX&*%p7wkuA$ntgQk2U&m<%VkwHjOY&j8 zLF#=lOGgZNuxf#(19F0{ILp5Blz@s1K&qs}GKI_z)7lMA zH{2*Br-U-tOI!iYLJ_i)V5sA-PBhZwbd|C+-UYsF{4Bxd-8Abedpl0Ckw_Ix8o^q? z%=*(bgqDchGC{UnpgmKgNqfd@9}3_hv_SETKHt8!7sg%(Gq8KcY=_Z~^XCRN<9e2w z^*41b8eRntW+ecKDU}gKc4FG@X|X@O zlTXfkJsY&I9sv!F1&*c-fYijQ;tcGNVWXqWMP9z+T6MYLwcop5G0Z-4*~v zj`4);rt6^!5Sej4VBVfNw^wkSi*8`Sc|9||Lt9&U*TFE-MxcKW+j29uBfG_ddZWDI z?mh)L%-m*JYMT{J>dkJ0QCskLg9AUnycyi6M+W72_VGCPox>1*8)ObwSrwoJeBy;i zc<|E8sO2Pv%YKg^`|%&cdbpD;jjH<}Z6(2p&rv6ip9eJBS+8K0>>R_piV4-;uvAH8 zNheK73lLegb7oiyp*v;Th&i*O|9Gf9C_Kq-P@>5+d(OAxwC|+-h~1ui)jWmO@@}8H z3oe&R;L5mt@)ZB*H-8@w_j^4rSVv3=^91l|!GHI^ei{GpH+~m?=_lX7-~7d&#p^%% zX-v~j3|O>4W|V8L&r~iciMioqMq@09+E#xWAsL-Fev0#{p(>IU4R=n`S15_$Fz4I8ms|y@Hyu;1yf=Aa0>ER{jiybZ>T;O9b6@1}S&*K+P zE57&m248#k2H*SefSbEJ+}<5+ySBN^_E2#;F66Ro8LVJ<5^- z-)x^2`ixtjI({Wcp}Ss7IFEIK$RNHu*=)G!uGpSaZLh32Jlm(M zxaTtYdiI$BiwE7BI@uJ=465#ZRy=sH7g<{w%W=V!$y4jWdSs^RfYY*I z&axt3s#IYo4Kg{|5h0Nje^Q@b!`}pmI5uIE%y#ty5NQ1;B*v`QY@<$r2_E)r+n-XgJd)O)x>6OPD_JxP< zV~xw0X4h#(@1gt<#n5MH%)xE+3zFydbj~Nv~fNq-H z(n**I8`V8Mo(UijNd1BKVsvub(+qbiIs?sG>DvqxG=l$wuQRi0w<2s&?8nsgLDp|@ zavnu3aio*rfe}1r<;EYi;Djq;a{}E)>-%-XEhh7}bR2uG`#k8c_)x2V(4gmt1&Ua@ z=ZbAof4eb^W^#nF$9d}J!w23t-B(b~5WvX5oq+N&oC7%YeP)PbR8LhoO!oH%w$eBE za8jd)v+#2NI*d;2T`?Zoi*056Ov<|&yUa~68T^2~t31dJ4~;hhp^!zot*-$u3|&>E z9#@-IhqIH4g=_X-0Vr+yqM z_g^JS+^fVX8e_vB^ZhIJ;lOE?|3wNaKW`?si-tcdFi~AZ54u20}CxXxXjaymV zIlpIoVy6+xTCmWHAN$;AfV>AUcR*Qybpe-?oC|HM6D%o~VAkrk&7omH?zvOJUfk8d z@N!(#=-pfoknX-!Jr05&k481QJg!85A3IIZ<9}Tyv}7e}^hu-};C1E{O)^z6jh=O- z2hI2;DSDJhR)rEEY3=eV>|A1(DH@=y41E1hzl+nU0Q=Uw%n!rTinBTezxgl!6#vKX zd#2=|TlK@(t%iMpiE zmdN-CYrrhQ;0mv+~Od!ccAJm6GT z$ZBAfz3n+waiG+3PQgj;xr~C`6Ioi7U(328VUmESnIl9>qp{kwHUZY4jNa4Y>^$mG z$a(JLjELY$4qX%R)#rNnmJe&uk;;j<@mMPUoxr<8Rt$`bf2bN(p5xz|5e!B2xxPPPZ`U z4gg9in5I^+%;+bjgqn(^W}A{CLS1DYd!A<;PIus1u`WmCX^-_J@2#g3c9+)@XyX;Y ztTL3{Vc+j&Ji8MDDy4vVc5<`Y4W$&-EfOeb`YB-6=TT?fJ|nX+LH1W8&gsV+)vB_DKjNm!kL!RAuaD zEtuZv=HblcKk1w1gLJ_zg!#PZb^p$ro|;e8V09aSjgmu`L*rXJRIa{AoLd4-fp)(K zLoA|Y{N4l44T$iD8<-JTA564J`tQD4)Ag+rd(Zhep1y}@REG&3fOdi828LnvjB|mA zrxtjy@r3#R=qn6}wetMN(bINGe1oMH3E(BQNVX#8BhbOvJ+o*G4EbrW%Ld&k@7S0&l3LwZ_4oL&BEkO}X1XZx;^U%)p=>`l%C;M!S-s z5_sJKg)2RC;F|%a)u)31jFI0Ix{mGG?=n2{wj%=iuiTA6b)o;=p8OLPizx*gV+(9q zgBwmCaA!|gX=}GPn3E0!T0z56Bluo#M1??sP7SE4Dbt}TB0^0XC^!DeWGWA!xKiJ;b6n+` z5cNH)420?Q(-Vxawb*yca3>u^jjqgrNBbM`DtcVnIS^16#i2cG<5yi}ZReapDdD%j z`wo8dzx*HY@+%MV*M9a3_{E?95?*`dMNBETNoEEIJbCR9D^R9=xTub4{mB?GwfQtE z@wTJQK%kubxS<3wZc`RSNwK&h#r!5|w}PYs|61|G&p*O9P6uRK@Zr;n+b6e}ry0-h zXQbT@R90}w*wuom6qG3=?=Nui@Cu(?R-8WdF}#0!hqoRdaCdu)+uMRCWyMnl4hdM6 zqICcWJWaAfS1U?^WFjm_6+}t18MN43%0W3dQ>A6= zXrL`Yig6LtSy9;-Y#WnBkxQlNUZ7jX4#G-c;2fW8`Dk-0jWv00|3!LlR|l5q2|X8Z zTr6s~93#7P-kA!$#A6F$4-(Euk@~J1W`a%~`UU8o8a>0%dUSV}@#Rt=SrEGVE!zw# z(wt%Sj{w<=O;E?WwEgK4phzGpDi_*9;kuqM?RMa2(tlYNTwGk?L>ZE1ErmK$%HVbJ zJ=h?rdd^&BZ#WWwRkj*&+h&myVNData)ceNCTEn}oNnd6`zWsO7#I5qAi}czznr~k ztZm749`>!Oea^Y}zWeU$_j;bmZq8~pNs*K#S`tM`7G)c9VoPuaK`{d8M*!PMfJ{PU zBR_%&Nnkqy^ur1eI6#yjh?Ll|Wkr-6OR=m-HYsWdMN*uaY?9sVR*!GI_ny70R(`CZ zR@FJTDQAYPIUA1b}nrqdn1(_hL_EjpkpWQjd$<-5_ofm926J|LDPzxWnET2*; z=DBFlfB@I;Dkq4r6vmXpw1fZ`LF8?%8r=0Fni?2|oCYF+;YsCplVMPPB@)=YK7cA8 z%K%`8=rjo4+g|xbZ>*VO63Z&GYL0e#Nq14Oopy*WejjvmbQU|iE6iJTtOsHaOC^5C zzSGY00~E%}G@3S1B8lD|`PQx}D)X^n6Rc+J?`yynKil)e#pCl741-W(AT#I0um(bDku zv~5VT_Z7w{R1G_w1H=f0ck_$R7i7MI7(YP{8qcf0x=r z)=GHBSD%B;5z*Hin>|PfpF6tS0Vw{|QmPOHt_f0hX{NyHo3Tn{#UO zL4<)ef`J{u-Xp^Sn}QLSEz`hwt8y4V**IfcyxaH=^7`M#m7JYG*rMp7TR4m3Qp zM+B@lYb>SW&gqOl{p}wF$~kITQ06V_vdHRDRp(rC0M${q6!#tm;fr=Wj-Q(x1C{`+ zyr9pO8#r)Bw#z{>0sA(s>v#5P}M35M)uA$ihrY9BL;)r^s~`OXS)Pm(mzRq zCIEq0j@P5yoX-S;; z)r_)QVO+uw4pRkGjgpsJwt`MI}O-R-k#2 zvzyBT)^qM`-!0<&$9ZH;!_}KY|3D4J(bx@^X24jWQhw4?1=zMXG#(}8f@Cs|NfS_v zU)|~vyjO;#+KSWQ1*=^tsNB^6giN~>Otj#ZE7vYSOWuFuNGYrblOK+rUinD%>j1`Z zT_nTC`-2HP{5I%PlM~L%?S*|?n#Q)fOXW&oAm@Z+yUUd}D9BGD-D3@4nh2#z-=1o& z4rsGc0ZIw;cE)K~A|VaUgXS<=Lz<)r*l*3c^Te*F$Pf%G`K5X}>J8 zzt-Rnkoh51?Vjv1y(ncSt#a=x==K-FSDkZi0CaX=lbpWA!-b2%DxZBpkis+A$2TVL z+jrjcaI`DB77V^%wOCL)CVobF=9)D4*4sF*r+5r6H+WPDy}?+co+V`>R-{L?$LFoPVE$m=L`G9H zM$a2;4gNpLTnx=On0)Ni%|nckbwLVl>KNg7hbqwj8&JO651n-v`0Sevi3nlOc_4<) zs5ik91)k2i1xbT+YQDq%H0FIY)};V7GewVhg5%O1bFzI@?3p$(Yak9~pL6P~eue|| z|7?s+r1k^o_{mYyj6aI)s!VFdy4s3l05TBZAS3NhB#d1Q!a(tEpsR^78oaLsGio^g zX5){U5Ju+_D-<91e&N6A#5JuDyS*^bIF8H|oEWp*s*NLbF^oNa3r}m`ZDmNdX3+)0 z^n%NW6h=D|5?W+`UvtNo4#AXg%4IaXRlCuUC^KEv0SDY=WR_G#SP-`D0~X;U#7&mMOysAw+14+Ao(OWTbMiod z@lQLUM;#r#MMYIKFy}pZqHy#ozqdU&KB4onXqro8i!o;f2+| zQlo#$8K{gu_@l4k6QB7C9=^85hu;1qzVic5;qk{F#Ol%|9UnOzn!^VV0>JsM>1A}0 zI&&=i9b*C;ms!)4fD8#HK#cCh1R$ZX9KXg?kQ0G(22XN46E+*X<(_N!aslf04zRrg zUM5gkV{>?boC=mSgJyNGY64S5Ax0%lLCzDFTESHD)(2Kt9jtNBhVaGL?%xt1zvvaF{$v&}p^ zOtdxhY>J0_l-k4i+WvlYGBOH)V*qtD-kuNd-~Z z_`~5b$>sm)bpQpWO8|1@`=R-GH9gp5_qb)ux{U9~ETA0|!=QE4P*gPzV+>q&u(8k< zSIC*)ONG{2Lr$VCQ*JVpD{Gqj1VB=}CtWR;>i-F(MFI)pbkB3a<`90AnstzJ0xtqd z)zta2ZP+6cnkc1UT2IKU44xB~WoZEb(k(J6gJFBt;VbC-GS9S%q~Ns*ZZ_))IcJ=o zFW7Ep+;aetKyJV7vN*QPxOL+e4vsb`OLh9N^gjk;wMue&LapG!INKH+9ISkAyiMqo zCH%o!fX|C7y22H@lT~k(^*c5#YXI2vrp-0`JxjSKlkXgeOZVc)S?ZeBpKY(aS`%a^ zG_*cyVLyJ;3mO|C20h-QdvPs0(E8w?m>oCTAx*V6ecEN-L}rPFvfY7+IeX3aAd0v& zlmz}Zy&1_x?~fJ~fiZPv1%)>PV^jEzhDQx&J<`x)0|rzu&>3NX!-M*#p`qROJz}99 zn>0gN-*wcODnGli1nj`&Z7_h0W*Wm996dVQusbJkcYtqrc}!ts`e8KIwDdP72KOnZ zJp9E7quz|G{=o3eo?weBq6u$wH4itM82o#z6~q&L19dBwJf`z;t8q*hByZ-9?O zcD#7xGtibR^g;MU`1MBXg8D5j)K*l)AL+9Z+Jr=-qs^;xy6LwjOP$5dzuNbH6csTT z5hI|2KY52p_sLQ~o#Ksl7&vgD4}p8U4w_!{r^!rtX}`P-^c{kuU7L}<345II)@O7^ zXcQDa&;8a19>+K0G012CF~HtPao{flct>Fn_NmBDy#+-P(Cv?n;%#s6j%;*el)I(Q zf@lXHHh_bp10+g#*OQL`>jTu=r{Ge+Wf6zGG^bpSoGKkInIjT=Lz*Lk8M;!8ZVLLS zXvm239w?$z)6}UGevVO1<4!i-EWgs|KsYNO93HHEPGz3Cy@wVQWY-l0qJ+&vc=>w8KlqoQ!{7h- z=kd_BV?6!lNAUjlzY}kN>;W8KzJ$rrs|iEENE}3WLThta+_o@>J+dUT&q`~Gr1)6{ zlBJ+BiSwzL$XZm1CqjX&hUN7JZ@T{)o_mdOj)diOi`Vbm!d+>?w3%cjD-qJX$Qj7B zB3DM`j3rf2u1K{YPmHA$yy@Nv_pc`$O|Rp*gEM^bl~Zg>5r;VI8h#eqO_VX^1S%w{ z$zi*IRFh6xkg!p=+W?dVcPQsW-t~`T2n$b5@jGIWH9Dhk*5i^KbZlP!q{wd)wMEK zWU@o%NR9}D%eJyCNCZr)33Xlo$i18=%<~pf2V_B7tp%paA^_A2EQPTKuwJcD6JeQW zcV4AFhMg9WbcNrXQv@1}Xw+;D)3eODbZLWSshF1nuE62Z29<%b6r7!HvEFP@O2Oeq zj&-9_Bsh^1Rx2$IR~Tn!=UA^&vtu-C0E!Mu5@47rYXLW#8?ERLp(RP&vh5ty8ek}o zWtXu+&h1&Uj47QjizSoFS|Vy~Cz7oVBr?hZYi8RxO|gZHgT6r19IcIRjqLAb%=-=JUOqUcy`4d~~DBOt*<+r?^tmiIo;p6R91KHlhI z?gqCr>aH_0)}38F2(yp$!%ezqOf)!a5q9AW7aseD+&wRbbJv0e6$}*a_4h?B#<98r z9-rxiL37R{Vmm=KbnCok)ke0K0P3Jp6B$`cGaeDC|*QEnLT#a9eu zwZBn;(K!E6G3kHzIsP4Fdx2mT0bL@f|HrT&L`np)qb-gPc<{~U{f*?m;haZA4nVgt z?oeQmdyzNMZePrzqx@@DS$mHfr|cWV=5T{bz&+RQMV%KZ3Rh-XHD6|T&^^5!Ec*g& zOq~*8{r^Bm5vKv{?gRlf6y=|yo{WA-97;$!H!E`rZIka0b{z=r=&lil{NCsePLEA) zB}XO5{)%zarz%GM6da^3*Z^b@XjZq2bfC#SFW>8|JrJzs-k~ol=67sbZyz6j6{o~t6OU0LO zoZ_^K0WPcL2{~mcdPpRDfwx=Kc|lrFIztRb1#0qQQ9YXwzzK{37ajyK7xiWBm?{K1 zk9G&Ol}(H6sBUTa_ow@QD+A^q^Zx9kF3w_nNRa)(0K5VP-{~H zO14scN(r?tQs7McNxD)^dUkkA^Y0ftx^bHr12C6WI6K|q@NkWsGq&e5?wrao zaHY!ea+D>I1_;b2*DmWmbw;TLt101pt_`kcSj(vg2`Ed&j0JW~8iD4gZEVEU3IY(Y8vWlGXU<@~#8?fvx(;O{> zWS}^gkCO2TiM5~MRpcz}>2JOzi zVh4=7F^*{7xtvIi2z8D-rZFarLNy4mAZSg!beM_GlTM~L&pjS?be7{OJTz)-ouuB~ z4zEWh0>U`xTGiF3Bx9P&pTC`s7-*No0q19 zRfK}sf*3JTzIZ0=$dd-qZqz(%{y?v2}ev_$G}!ICDA+E~5#N_f-8Hgw@P_EAlg=QP~6 z08iV;9OhyWSdou=KeK6txSHF+!2MrfKw+SVs0jN|ZlmW>1ni8vu~|26=aruM;1-S1 z_@0Y$!n9uDd|vRjw>|-`TX0>(nb8$=aDmSQGf-Ifpeud|kX1I)BVlEw*7>To3?!@Q z-tb25lS|q;bdPj1j$|}mvR#f~18IRYr2(QK^ANTlG-unY!M8GT46k(D1sKEk$Se9* zRX<~qmMp>OLtwyX1&rQ zq(J>Df+4#{MTPLu6gKB-dJrnQl2;bf&F@BA_wO|4hVIqKv z$gNT(OJ)LLQB)hf5hRVgR;lp59tMp-Hy7|w+qy|@6}|Ce zlUo7ZAOMpYzIMkAI)A8l{>&a~MqC(3qk8>17-EJ1dz?u3pSH>Bn&}W2EwwoE=JX@N z>d!^u8QW}Qh~lmD_xq17y8FwbX%|z~t~~5lw#*6S@O=BNn~=`frS@{LE@^0>pRcaa z5k{b<&d|d+xSRh0j&2Z<`1fDYQSc~Z z4^2P0o#E9Czzg-E!_xlt&&9biBXb9WFr?hP7&F`^ecPKp27Q6<^<47Y-p~lqkrd~{*aX+sJau1prP;E@?f*la&MOM!x zJvW6mqkQWnBVz|Sw0qmX(ZKVD-N4=keA^e*VSUF~5F7;t{Qq&8tf4^LaV&96j`J{# zNM|DUdwKuOAnO7_dtZt(-SfPoXe7yql99KAWA09>Hxi!@4wx}8%P~|bWt^YRc>3*+ zgO_vgJfqe{oa|D3zodGD;Y^sR>}z z{LQa$WI(533~q)SRjWMV_}UeG+lM}Y_r2%cc>N2{;1~Y+@8g&L@EP2m87G?wtJTzK zP|@D%Ml?Dxxm00UcJg;KO<=D0@@u#8%%;fp>9;(DZ+Y{h zxaYoW$cGy>cFEHyB~u}A2uJHBFp^h=*Q(XbnoT5odUIkCQd-zAYhXaOI27>a5RcuT z@%2}3Al+QB+<67Bt?$L%X@g}-K$-wj=T{vkQU^UHaiVGgmQt}K!a60~d+jp*^x=fN zk6*=SU%rW#Z`{GzwjwRScFIW83OGFjXP{C>;ws0qC9Qg`MCNSl(MDaN zcVI;8%>^5SkaP=3W67px?7gNJnoP8n!;sCQa$DqXd%m-a2UXqarcQs24K!ig>0}}- zm5?)#STZaalBJVTkSH#RnY<<4hhDqlY*tj#haAV42=jKvYITS^Oh95R%ObW^%0MlC zlv~b(GDDn2$^a26fTa`=WlXCH%Ngk^KI@4Rggi~ixnMrKqbc%+uVK!k4NhW6T)K3C zQY%hRCEfn`c!N@<8vFU_8K%t!G@m0)8MFwD%mr7jUdCJ)btyV1@JwWmvYaeJJj3iIn?Ea%*e-%)h=~1YTcC`e;76p1jgnxi z<0%m+Cl88(WZp`te!ja9N=4P;q{)IJu6P@XH}dYYvm&gWK10`p+ekKTc29n%R#GC` zRo$iXM+v+Iy?VopdeDclu+Nz--tBY|V4s>|-EZ`a!1PyOF8$B$dfXxgai_qEB?K;&>CsF&h2Qd4U(Vc@Hv_eeFw7!I3!Qkfz-BIXv3XsFC8;EV~dP z?`{WHV&AgBLi*T_p3$)Z7Yj@sKmRGDV{?X(l$ zZVDvS#%prSaG0kI0&vgC1_yWDgXQ!(xXij@Pyk+puDIg4=U&9G{l=&8>!0}=KKJr% zT;DF3Ajh=XHrxWj@n*uC?>WZXAGsSJe)D~J&pRFm9bN*n9?t~|M}Y;LqRl&@cZ|EW zH)`0k^V401B?(X=6HKypH5{7Ik(G2uq)rL!2~>exfkFgI6;v5q3NkaM!vnnU$@}oowM+QDXa5+_zH|%oQgJpf z;3*?*4nWHSU@IyAmBC3FOZT8sO1+L?ZrMh5m^m@HlG6j1>f^L$y!cfJ;;^4VA4r;f zRt1y9U}UYBO|bp1K$HFbB8J)p=yNQEBtyEiX{C62h>T;$Rk$?G(mqI15UUD{$qmBLK4Eygi$7>1c!XdcvKv z1*dmTk#fQ^OX|90i3m+j(}bK8&d+9m8E4xWVEN4`&QG{-aiyQD$gHB-HBzmfwa^@p zsG=~VE{w@@s8uFefGo9pP<%1uoOQF)QaDzVycq%)gT4X_Nc@*jGcSUk8NyjeNC~~ zI3x*hP!lo+#NenKuLKI94N~Z~bd?K)7EmPP0d2$i!sLz;dpKo+z`ms@~QH zP)(o;y!84Vy!6^_{NJB?7WhZMg~#u^jCVfv06zHCoAK0>kK*|5t035MMMjjS@s!8Z zX$A;PO)e3nZ%Tx;lB(s2G(Fr=&Hz==w8G=p?!p>`*I&JX+qZAy49B?lI3Z=FNi}jP z{F4>+1gaIOYHD(l@1+v%yZZ`0e7MH%UVaUK`0PtKO2BD7N1-f7gJFg3yoIH!n=?_t z8b}FTXgb?;xTP?IoBRWG534!3;7|Vud1>V z=Tgc9$TpRbjRIqTIa}P0Rs%z;gv)6Mr2wl;sHF(a!}JFHXtM2g!FscSIqvN!OB=P) zLrQ46W{3o-J;b`*h!f-T$q}~8f}1z)U@44~%ZFfSy8E3oqJ`wT}{HnNJG2sr@< z2P@pZeTMD%jP-iL>FqPDR$9rPLMEjJ`zjP}1|Y!+9h!(M&5VJ{$n$MQ;sO=uW_Z9d zdVEIB9wD?sm0|p~Haa!|$y`B6;>1ahWRnaAp*vYU*9-+zRIX|(Gl(lHXLPE!p?CK) zH}GY!rX6~|9Zan-U%@UQ8+mW9G+T<}tJ1bQ=>QF)gA|Vu2%~8U_C_l_no!Z#m|B6P zC}6yIwEe`~{TNfEN8_r4$%+Sb4=Njv2-9Cfk$->Fe(fj8CS%{~egM0dV?HYZJ>hZo zXQC46h86`{n1QIjJ$)hQ-qGH5yJNgJ{0&2Jf^09i1nGhl@aYe0h9;B(UWnr79zYvM zY=6{kYU?#6+CdxFI%(&fi^jVHg!PYprc4R#=v$+$$aziD!mM;l32IJk=r%IGD{I%B@ZNjCbpe;fi@|Z7@ys*dz>oj!zmJnOZDAQ%4bq~< z8T3A-ggZp|_0Rk60Z?#qg*bHEEZT~N?sEp3L*v7X z^B9Dh%UqSe`j_JPtw-}~Y>@VS?7VwDI72Wwo~Ovot>d>3^I zX~3%iRtDZD0;ZIa67c-Xckuj6U&6;e@p-J*8Si=gK799k--4&#@g%O@cefO#Yn0Q7 zP&X~wF_}SB3T;-CiB-?2&q&Y}$^tCRs8hlN_gurl1iX0T7H*uLVv~R?mo|EKx%5#@ z-?)R$02L&v;#egqX2^xXs5m$}#0THJ##`?`!S8vSAmaT8sa2l2aon2nVmzsecWB@f%+=-WeQ<-A~YcHCi;OiV*~gqGYO-AmQxXv>m!_u@Bw!hI4hSek zf}1I2ISGU1__Ul?Shn(RDFu18@_}+~h1Qlu&S%eA$5Y`&&8%G_lB3*~qIz1hJO=a` zZ5$j&fifNUx)QZ<%y@}&XhmsDz zopEq<2#}s*ju=2rx({3`(wC)51$QrNpmi*g1g|PXQ}`vV-{>`FSGXb|NU*F{P+lYD z-ZL2WFNE?vx<4E|LaZiWZ0zi^TXvlCi9XWW6z4_3-8eLM$f_A11 zxd9klZ=vUZL%Cgb6z!cFuLL-gn_LbD}wY z)^Na=D(^<%aHVQMG~x*2YN6?3m5;!MJIpM3XpW(mxho8VN{MKjj*FX9m zyz?!Op|YIeD|zx10LPvD1c+`x_3Uc=X) z`7%EK>}z=Dxfk*E8wH#*HU|?ns|hRJPf01A(tIFWz+@uVc-zDG;Y06!8@~Npp1_s+??$es8;ogfp6s3;NyJdPkO8;jkI0R2o){=V z&5V<~u3<$JzJ^mgd*cosoeQpBJp!jm&n_2&tCn4%h3={w3+l{Zg^UG@&eP?)j`7_` z8+`G_1Kj??3wZg~DL}H?S5pSKiq5g)*SsoUhSfv>Oa|Eb;{fHAYzc{6(OI&jMG|Fo zPF6H6-BwoClOK20c*^Yrpw%{MR-O`2z&RP6>AYHTxR_6G*0w%k!bAEMhsk~;E5OK2 zCba^)RpBRO>TRb69Ma6MC zBkR@>##zazS|C$u4A=sQFiolA$eE4gEA_!JYfctJj^LA3_+=63$}(fxtO2MWVXMgC zP^De_D^-pv)U6+t^$hl$WuB7IbIKWIo^6sFE=}erd`m4jIXOgS#_8=d%yYrX)fMuT zap%?<&Q4E#@BF1pYbj<|CA(xbWxRg<%mX+@9b`6scNq;IPtHlx@F8^{a?1=%6f$Ep zxMXLpyDT?bBlKf-4{I@#G-VJaXU^hpV)%CyY^}O@4j~ZU@lcZ>nQ!#L#;?(Px7*x_IdW8FVty*Q83#wHp& zW9Gm-E+6d-XawFuueCc5`IxeMRdw!00 zz4b{c5LaXsy>i7b{qs-bnXkW!Lp5NCqzIT5*O>@70B}|-ZqGCBI^N)+`|ifoyYIub zyYIr$CSyBSyms?8UVQmAyzlR+Wc?X~R z>I?YtS6{&O*KXkRuinCT0j4SAU^8K}O2}DOpkyz*IbMoOv#p4=5_!`H;;?5dTgIoK z`D6Uv=by)a|BJtY_rB#p{N)dS08c#e7^clyYH+9=;5KOaLrsEMeL=C4lk*@Yox&ph zA6z-X6PpcIuTOaC<{ji$@8I~J6F=t4wmWlG`cS<@U@0;P1|gxy0e1ijR<)qyj7P3s z!JS8D+UC*bK8zBFyy1S>z!h#&(>J57WNil{X2&F; zv46e2Pgb{A;_K}`UGNZsP8$|>wpEB|jpEN%CR9pTW5Kp&S@8xTXF29HdbY2;52eJ?mD0 z%&`AS7J+WMs#Q;CKy%P_CfIl+L4pRsmk`lyev{s_j52K{QOK?9^zrUxr0F1IhrB}r zaZTQk5^T1e?p(Wc&jr@)-f3ArjjqCDT>zPgG*A_wbUR3di#f`v zi$GqGC7Nx9Bq!QEy4;kL5i=pY5U{%+*=<62{2Yw)9Vf`XnSc%m7$=}fwz%RtIF)e) z11_IBhqynEPL*YRb{93>4c_;gj|FZ7?CxD}xa=HE{hS)odt)X@^j1uy4dlY@)KJQ3 zl!|VkQQJXR=t5f&A8|gru{%#ufSp{9bOhJ|B2RKaXt4bPjjFRnA-R{r^9H98l+)2b zur>pGgI8uwG>0#aDJmcDI665In7!Am25&FC$R z#g*vrj6>{rj7>Fx>cS2U|IB9e8|OANEv7cJBcSo!7;uR+Hwv5>?D4dA`$s}m)Il4Z zkx24ktbrAMEaS!Ad3ZKnu9f=lruBV+~p@W6H@izH9 zq{&CEW)F5q+Xr`m7g2;?JXXZ#h8vwM4D!Zh1{y9#9Tnvkk)%{|t+;&GHPq!4To>7Y zNrZp$@z3EvS2@9~DV@6Klz?ric=^UD9)9R<{A(ZiDBkvtw_|;H8PjHilvh$6SKq5; z!7`uW_19j-r$6_3eCE@i#tSdLg8$`HU&SY%dl~=!U-$qXe(XU^Sc6hVLTaa}5JA!r zl?FPdQ-0fV%2fuC>L5fkX=QPB>_JwLyal_B0ix#eEn$xGHX$}2+|fl`(Hxk&sX*Uu z(@HA=$l?^`Btuv(gFNI0Gptr1i_;ULIOYqekQlcZ3zHKN-=@uG;)?y__Utz z@U_dhvRdKA(>u75k~-&_r%{1Q6;y2Q>^MDLb#pDCi#mxhXW-;u!usd{*H&xXna@2a z*iQc=*wIMs{l+y-lJ|5X$7&V$igj?(HS!vTipncxEu%G7-Nn$83Id^6TMUq{S~jCU zuG9ijzz!M>u^@%eIfcHX-9gE;1sV_7m`U!j7m#D;$CQFD#c-*v_TYiDMle|R$XRET zs8>TDrkh+RkGd>86zc;RjtVjg6V}AYRP9I1oRPR-u7tS?KT=*{o)_d*EZu>h2GH9F-9X0Vt;?y(gz=TKLERj(yYYv0Z@)|)lbB*Cg$Ex;#} zoAj7VKvsh7=tegBQKZ$%IKF&{QW$q`-9af8M@JhR9&Ru%1*dn;rT80w&AO$5FNHA? z%XXE@$dim^Sr!FBjv7ov>aVp$!u4jQ0a8X?7OW38I!;M#7ZFOR5a+%w1w_eHG<=R( z&p?lKrRSfgoHf0@f=Euo5OxW+fx@mX7vneVD>= zBgKSuSQ!j|yMgY(y&LbNqccoM{9?Eso%BwWMMbVZ`LDqc8=96!&6!rCpf7;YKWqJu zYsVbgi7UoJ^L^^+zP4lgy8t#})(9pTCul34qxJZP{#}3%90XJRZQt_o_+oDKp4XCD92{rRoS(LA4WA3Xyk3>Rvp$Kw}@j zKjyqZjfeI)VSOsG8Y~85hsBXicE5E&s@+Ed_4Jc4D&xqWq7a6l(5TGG(bVqw`1-!x zhzfwkRSsL(W0No34@3GJ=_%qN&_x_i_`> z+Q(@ocZZiK;I~6QVA)S*B<__dp|>69JboIs2t?lP+S&;g?ioY`wF`8S!)dthM3DXV zhBB-VC_`}`=){ETyOMtJ#@GSxYclox|M-tUT|eIA+ILXx#$ZFl!9lz@k*Pw z0NJ(zI6DJvxA=1({eFD!_k0%)kFQ|0KEP(P#(KTNG);28tQu-!^i1By zg%_^lrPpucnOAOrk+5E892~B&$r+P-TfI_dZgnCO5ptez<=_%tx>fPBANxK0x4-yt zeAheQgdh3A58~Z#c?_$~8k0jUI7o`)s#_r>wOl47TNXAmawd=lXDB6{+;xOYCmYm4 zTG3k0ZKg~}Rn~`Vt*8{wTu#a=}1Zri@ul407|x}ItDiF zcvyfEqRq z-DSPg0XcoFRvCpEWhu%}a^8EZF=jjq%(Dc$QnD59U@KX$i-dk_l{9pkR$y3Wj^NO? z(J@FJY_ot^0>N3}=UUx%;>hI80;6WeFiqe(D|a+~-01=|K5$Vnut0P@cI^-|m~3vN zq0#=jjXJbzZCd*j9x|V)D&e=Z_t3OG&&@fHAVjzc?x5Em91{kri2HO>TR93V==yW- zoCotb8Zc1;8*@iFH6Yd}t9?;BVc+U;5Hm#nNC_bh@BpxBvp7t7^7-uXf%HUWAebW2NE-QQK&fzyF zY!0IhZ4B(+4_rGOX>bPwcZf#0jV7OJ_<=Rb>o{Hwo$ z|Kk7n9sD~l-o%f5&r<*&fmUnDaA!dF8j}-x3;G1q=y(~>G-w+$EBlF)7KJhq;Hi1e zK|QWLy0uYL7( zJoD0ZJYN_SiNk)hn&jMaN3uXsz$gp*{>>}L2RNx4eCjLD2@CG9cIvl3oya3e5&kx*^JtW2myzjPF}l z9&#EZYa?ZHyUnzg?l#t0n?pacQHkQ5266pwDR!Ec*VZ$#+b2njtKx`lZrYXGOigJ zTr1{6KwYqzfRa{N&S&H)V=ll7>Su)dLDlv%Cqk)=JgRw>H8QG%x>T$-t4Qmh{}XpH%{d}BAlEY zVV)~)-#L@wa0E>2m6Es6Z$&_)Rc2BH5klZ+>;c(U2ZJDTl6K zJ@4dmG`7fx3E!jFwPNq}F6S=Ej`P%C{pwXnYYQ)Us&Z$<>1T&;*G+YMl4wN34qiM3 zx`!Z8@@T*}ZO!APMOp@WxO#$U3M4vy%)XDaIdS6vF7DIXXhSt&&;#1A*b)aF#+|PE zz)l<(Bl2A9dSKH)P`mAS{RYyFDBp3`mDfdCH#4hkzd$qWDSYmlhz3M#Bg({pHy54} zH)#9$qwJ%eJ33nE(B03+7%AO{Fdl)x?$}0cWbcdtz&M>z*0mt^$P0mXMxKB>974Ne*PDK73a0$M?d^7WV#EatMD=gUEy{93e@g~ zZ1NGaDwQa^Tu%`Jr2@7}@9nkhqKKzN@apo7Eljf$K(*N=U0_oCObwn6`TB&*4`&1? z{=qhZpAwOXP;&-nV5$k6DwYXY9Y}$tT2`0~V=fiTQm_=Mw9Mx->ayVByYIo1Z+a7M ze{jb6_6&DkdmUf=#*6sWvoGV1p1+P)XU4&lad5Q8;YyAbvemA_019b}GhwsdV6$H1 z=61$U{ZIc4SO3@F#J};;_v6oh=xH3B983CpPOfVJ^0aS)R?27DTEU5=U|Y_ineyXa)JW@?|TAasGuydAH&>2t>67k|QyJ(V{&jU!uaRI!CUU;>)28 zqbFHO>>}PSxNO|bCY4n~0*J9ngmrFY$ZjVQbIjDbz-o+=3ps)huV(xBw|$OlX31PpqF+3 zd97A0pQKo+20)@%vBQ;z001BWNkl@w&*o z{AO1#1)4=5M1pBvqD??p|eHQ4q!C9r`Z?WJ!fo8Kp&e7eCB)5`|e5GXe&VW zn?#vaa3oUz@`|MzCkvz-0eB1QKvrtYoO))U>tbqeNa{qJ&t6iQ8a5#lA=fsXaJYT8 zcXy1%AS41dTKGWQqU7VizR_-laO6;zu`7eo|j0RhI*f$1kLoCz7B9!FsyQ4DM29vya84Xy33DI#0qALLpCIb)zkyV0^A^KV(IBy31eO}+smV&-+g`Wr zWe;dU9tV2A0Ny}2qk;Y>5xSAOqLC3{jx-$SDl!B24eCeB3NpFXe(>`l*U@Yi&A> za(Jn;BpN<#zD&;UNa1}q<5$mpy9ABf`G>-XK{A*7;Yv2A)MT&>OD|vVwQt z`;+w)@zQ9#8L7MD*?)C{u~)j@rv1>rS!%_j58Uepk!WE_1%3FQ6MX(F*D+^tl9p1j zo)qW9oSbix z1l%>dn84}~D$lxYVf3gvng&9QMYoLiJH4*7qpQ9)4>SVgLx=*0#XdxXG5pfzl}L^a zV_C(kNvb@rs~EdWnSfeQmkDKF<9uPvwPI5%E|rS9%qaKVjmIB*4B!5(Tio7maeDnG zo_Y3b`2A;J#UFnCHQXV>!8+q$y^{0T!QdiAlPa5R%D8sr5|&c%cmMIn@jw2`@8UCnw)qjV549FXA~~G&IVYPL~HKU5as4J-b;Ql9YSevOqy>RTW~qN1DAL57dBI3dV-W zu2vJLfz`93t^5XxjdE(xRQq_f)fqzn?4Ii)S&EWw6NzJax^Pnc;Q%YFe(}ceZ0EdTb)XKPd{S-tJ7`XeM%UDXqojd2Kg;AG^ z&EbTU2-~f!j+atVmV#-@+J^*oN|DnQ7Bj?HtGYYB7}H9(J5|PZJ0oS`(Pf?$ZmsSP zcDi)+my(`bD68`lq%@vjq1hyS&H%VnOl#p`UTPDR;WP$70w5_=a3^SbzMh-TfCNaY zyh|iws!}Zjq5k*lFV0p=Z zkUqP3v3{61wj8*3U!f~IUP$FK5;Y-VIw9#EjbsXfqA?>3SZ#l#W?Uhu5Um4*Y-u;t z4cch6jkGBg!8->H8%43fz{=mO&r_3cy{C$1N;G4V$C(L}GWZ@k9I(<*Lo3Rn?VypT zRm=sbIaI;mwpM-o(U8M{YihL4(A90Dk1(H69>*crT=`VM^D4Qy`-aqX2pA-8a3c5e$gJ{O1cYReasAyA%^SN_O30Hvm9=< z$sN@l88Hl%UmV5UxTu?_aLx0AM<0C<#6^lqg%tM>z5NmV^2eW*y~!-j{1Gqs;UE3W zxO#GeRAEWFi;q0UJnKaPpin4>1h zJ$=5XuQBzo4bb(8TkJa9W^jQ6O|}dLgGW~KsmW~}12fzPiQLKnI02|6C91M49ONYH z&#PL&g}^IDJ(zG%7*5P)fzwcE-`R;Obnky>>SqeDo1~`v-30_N_B~{l!=D zi7&r^ufFgyKL6S+Y$)N<(HiS%0tMPlM93-O>g6NUTJhigpP#_b|Fciw$A94c_{fKz z#_G~xJKcmVm?7gxsxQS*Gg_4AGQ^k`2TKg+Y9-*6o}|!Lm7~&F)5EEPQ$pp0#Eg{+)egIo1wQK24`OT=>wjE&!!(a-qs<7#i*cL(Y4!#0Rk#zoEO4|7EI`6;(49K z`Kt?#R?^RwHdxMQOc|(o170d9X~8x_mu~;4dY-wB&74aqmtc4eawx2rGsBNcqa;o* zldJ%HNdg;2A|#Sy%I0~-(WOIBwnA~$_gj<7-FTWnKSLFVhilxtd8+jiGA^GSVYOOG z4U4U8KLW|#^Leg*2K@G1j&932V=1gzFN|fWaA#5_!Oa`D@xZ;uFxz##S;6YNc>HNj`^GzEF#C|zEJ^ufK)?IuqN4kFCl;Vj4tv{6}FJjpStK9(kB z$v2*pL*(PO;l@CWqeMF;XTP=w(hNCxf@a^|9_Dw&9$1s~zmYcgT@hS=Q@P-nTo0Qt2^_}%AH(WY6$f?0)lnGrwc8DWZ zdUzX;j6toDFk0Zl&wlQNdM_h;#*5>UdvVB(&Rd4U4oboeqp1$s5ijw6$Jcw2-3{9U zU_E)8T%_(!r$p>23&ahub%7E^{4QxYR$3n&tZ~JAVCZppHb+5O?vrC(s%LM|gri{lQdkp{i-}q1PlRx#h@Qc6j zTlk@W^Sg0;?JhN9ZIdl|#t_shBS7|;1CX<;!)jnR55j-cO_A}f@{B|Q+>mL+s&bn- zq^!4w`d0e*flRjk#3JRray-T(HcSaP{hp2kyHE?|kdyxOsMlTi0L3AAac@`0TT<;WN*_ zg!5^Iqk}aL*SRfsDgT}E)Y>yD zVJQDvP)q|0vaqC;-&OW>fhF2))0dqLUV_Yo@&FNI%4&C*tO_<$Z$Gr`FBwkHdpn}v z?UW|S2NCVUa9m*U;poGKfpcVRs9=?vG%T#nIRlv&9Il0L7h2(LTamB;d5tY6B%EVP zZN0K`4TZB<9Xx7!!0n;J2-6xkBwxG-XpbJGEK-zKFbjxyvZ#k$NWl_evI zt#$(-mE<*TJ~ccZY*v`(g4;Jw#X-)DtJjWEYQ@>vxuw?wt3378eYH*Oxea?gB)!%4UqbQdO8HO6bZ9tH7JQKvu}Z+x&X-Fv0~DUIA-Rs(l@UA6f*SxWJ$<}XTLrtc=RFox=;x%j`jkzNfk>%I0Ig`1 z-T`2W9WwexL)uyS0WpQV`z<`XyW-iDbcaWmYppee?@gepLWlx4wB2)`e(wY|I+&Ox zMJ$=nW&@{(+=mq28_2}RW{eu0h&axUEV;eY^*8W;?6-B++Ush|M-+5=i^6LhsH&fI z=ikl?-yP>DrCYEocG?5%>z{1%Jf=reH_ir@z1Nx^gNRYSj&mO2cICZKcn>gl+iCRU zt%Y#)o_m3^v?6h(0+n%i*Jb?G?|lpY&aZq1n~88#D?a>@kKp)Nj^kNxCahMH)^2IR zq{VwWscU*wyLMa4oH#&^WFtmqLOR^w-h1!IfB855Q~cGR_(`06@(cLE?|HYJKa3=4 z=tM!za?^c@VI|&4Qyv{x!!chV$TMM|Jq(^UyC6;V<#_(5wK*yUL%Buw$>_`Iur~e} zf+b`_F`U)f=C!i!VJ0Q#rthMo7AclFO z31wMhSr~`gf}`z%Yj$c8M!3+k7{`eqh2MyfP<>CV__|5vBj1WUqSi zXr1meCjb_#RNiIX*Y09M@Uaxu^Vl=C%Odhi2dr$P+dd?iM4sBsSg+T(as3t;K&gzA z6A7}OpKq}&?bx=>M$+JGmHq7Jr(4Y1t@z7b7@7L=1`s?DVr z3zar&tw5Qf>C+-dP*E{~^rVCsVA#0=8;;1PsAo4lgx%;TA_{=CmzW zuGQ40>tqP+-g74%V&f1@{&5#rm=rewpe7iaVAl$Usr@BgA&XO!ac?ytpBJ!+qi5(?ajC^adb?g;Pey|`wWCCw*Az)JT{!5 z{Tr`_YjVJLN7V~_``(Qsz}+DO=xLX_pY~p6z;udX2x9m+yS9R(!ocW14wUUiRa`;e zKcn1D$jFQP^pp40jh#9Kz0rJh<5CW0KvSy%!w>LSKR)k-jz40rQQ7(TH*j=1$8ntJ z(1wkZ!q5H^L!H*4>2b{Q+BZ407s4npFKD-=5t@7naR-+Nu8gcooV5}u z#)|}SfTD?yI0~glb9nzh%HBL`yR51Y{Oxn@eTH}VhWcutijtuykg0$I1Q|r4iD;sR zXgjps4wzm|jIFkvHg;#3<`!qu_0ZPSp5Q$o;SDWD)qLs1k3QVc~^ zQBdw6PPBbP#wA( zRa4HeH`YF`Elt0Rk~+MaMQribP*><7AY+%)G zjaxUFO-s2?B;V{Ae`<%Ws4eMBbvA*$ozL~mSf06n@ycUB8KuIlI_hNrzUNzCjv;k; z`}^-kKH1`hFTMqPYpK@W^&N7SopUj!sslNdA~6Wk@Kjh>OJ~ePpe5nDYp=nZ{^>u# z|MX)&j%S=*;pUe;4_#j9)nkSobH<@gwr05b1>8Yy5w$m16#yxuqU#!>xQFQcq+1Utb%;8FIq=_Q%FEP?&@4Q$Eq>oJF;b$$kuU5c8DHt{bwwpD^VZeoRXK>AR*Wh`#oZy=ueGGr~+0Wv=cio4N z-Tyc^bvV0TAa&CBYPrC*7dpJ}llS5MZ~7H{-`BqyfA_U7#&X%q<_hZC5&?-VQ$g0% z^(c|wOf?XYmCg$E3b#UX%mGvbGn%4W5@X$UKq8#!GcN3}a8i!EAQv&@iYl;NKUjJY z2BlFwBVI^Sg#Eag(m6{)tV?quNq1L$S9hZg?;&_)YwcpA&De_BYF2@~W@B(Th7s(a z7z7d`LUYa|Hm(@-YnY}PVnHZj+0_ie8vL0m9dgmbu^roU)4+FqE=sJre_b>6T6PNa z(boyf&bggAFU&Y88Cy0cI#;DF*0mN}P1bzSv)~&7#xW|f8bC=|ip+WZ zHW7}GrSdx%$Qd|){s7xyz~*F&aTM74J`1jnH;Vj(&GE)aQnOj~R*&rv&>r+)VdRvM zvjiD(>M@Q*s>5qx&N7OlPC8@Kn?wj2Fx8+(wDmnOW;)U)m$FN>lu38_;T7AhpL*3{ zmSX`*-#JS*fb~g$1OdTPg`Pw%q%vUW7IGwPqStp(aUg?+Zu}?O(Pq}LYW{X>v9(M5 zTk{$18=ALiY%{hdF`(OyPQ+f4jUe>JkUWhpP9V-FIu58D(J4T%Zf z8d)}Rbf@j7pM9rx@<yGL3$Dq_rJ!v9{xh%|hHBm1)|AiA7^6Osbt| z17Q5Uc{1>C+^IF*nx|reELt06R50wt$*zV=L5->!8SF$0MY*wS3XwBr6Pw$4)6T677WbiG-%MYn@{GaMhPB;a6KR%WfoCQiK^`>hZnb^b)+}>NS4#gAd`V^M_cj z)H(06RL&*xYV9V!rm6J}BwX8xOsU*!(@ZI$>v}xv88_hvfAk0O=6~@E_?eq;#MM_{ zaNTV_ctwfo=>}7gw<`Dfreqj%j^0X_KAT+;X26~08YpMfd#Wt^O6a5Ig9%K^C@}!k zl46xT9B>GAWJwKrFGkmD;YxUfaQumpbWvC>QCm+2kdVPi3RWdBm=pRwVZACS+a4QU zArA#-h7sq_oyB!ex)v{f;ef{;c?|FV@Ll+WkKK=>i4IcV~LYEx|7PryMY8)`3%fjG_Al6 zrJG-~9R}>}t#Nc&QsiyjNGrC3B!zqQ-E|4Jlu>qmOJN+IJw!?gN0*O0RSqg;0IEn2Er8)uCn@I0JFrF)szVlYp@uyG&N2!h%l3zJos6_XHhVa8;Of_dL89 zNZ87_)K~VJP@;c9llReVYFT~#%g?Y3;fw`5qPpjV(eLN^D< z<}a@j`b!i#LgRR!lDbX+CHD6bp6FsU(1mvH=vyR8&#}8<01*P@(iDMI3@>bQGnfSx z{8MZfOyyMr7)+q?q)lvBe~3jn<^vfVaq_h%Zkx^pC)N45?hk*XS-?PbuqFT*At>vd zikIDUh>lfV3&SrtOcjSu=)CPxVbi%V;7eeHJ#{VvJH}3(T-!! zr9iVWI6&H{**QnZEHNl}j{e!)>{$~Dwq#sCPYI|7Pa5|1{^qZUg-Ta0G$MQm>(V?u zrNG!FO@5-u?_to1NNZ@6;6i`iK^TSu-OS{V{l@j5&w!dnHcu;uQMg{=>{&c!pzHHf z+;I4x_dZ$l7GvaXqwsI(yP56|8Mf;+;e*PoPw+&`N0o+VDktzub(&_h=DagCA!FES zE*obp+4zb4TY>M1)8wam_jF{1U8Z&+s{Z+F$W7C0K&6puFPwqhX$!Dit7b+E7bHE8 z_vp!pr#trqCItY`R0cRnjYa2#+K=3H>FE0KynlU3rRr(-B4Rm?WcK0y@46O&iwVmZ{o3l`jO6X^_$G!}V!g4eq3L^rTi+@KbARa+Q zPJTDGRU+g>=!vjMguct@7af-S3+(OpxNx|{xw9)=xM~mAU3(rkJm~_Sanp_Xj<0(K ze*XJkhadU+7viRKD}3RxOSp8rK>;J@gbQc)@$vf~#^3)JZ^H*ad^a}3h%ph^0zbM~ zcBgV8(j78+pEV6OXT3HlC1jF4%$&qYG25+HkWb*Q$KJBXvR`1m=&@Y%$X%}~wMow8 zm%-~8LF*QQO#UT3A1h~H2~zMdrR)X|!p1`Noc;c|8QK?v6hAL|+ zx5+qUJYx=gqY#|pCA zHOX0zSGdx*khW}M9v-f7bY+9Fj2H@IyWQySIk4SGb@jzcjd0$u^p3#DP_$}mQ3VQEc?V9b_#T4aw_z|8k9&DglBYmHD?)1U5lv7 zndN4XM4n;bL42;3lbktMelmGa33)t1NwSN+Gwd}r8;pGdVMi4{H%umdP3;~x+yl?E z>8W<`N_$Xx3!f`G9KSJJ*xr~_avCH(8V=d$o@57eP#W-*_LShI`>Rue09cr>8da8_ zXo55%FPI8?n@bD^kagh%AMxqumxF9`gf*+l&2Np$_eHD?s6iDP8SP4a(qDWv!3rhZ zX<9Wxlge&3r+J^&c(SHousaeCC!rA})A6Q#J<-b*jvHpDwVN&gT15NT(@t8(Yr3B> zRb?3c-$X<043j8T7uJqFi-TEGkwom8%xW5SHS!+c;QFBNqal%#~or06L zIFcGDkuz9y2mk;e07*naROp^SQBCz-`)ZlLvb-s)z#OQ!I;{t8FfoxbGu{3IDc?X^`AP3P{*;@c|WYS- zT*Fiv9MK$237U*W&YF($y*t{r+gqs)9$4`HN(+zwtQj}1urb z!ArP!`FHRiZu>BfHiKZ1Skp`Eo!^x{|fZVAVchvY-`AGhK#2l96TVENUOt|4#3|J|ndd83Pk8ZZirP2mKcNz0hrK z$=G6nlkJE_Cj*SMz|qkLT^f;cheayr60qg$XT8NXmwr-8URk`c7}X(XMA=+v%j6of zUF!wd&M*U#i9rd4t8+j)WV&mUsD9kJPLfYf2w z4&XxQx{U31l%PP$l19JX3O(DLSmfT(CB4Jk$tvVck?yh=Et;I`aUqxt1(3SrS6dEa zl~bevxoAt&;~Ifaz#2SDew3x}GuVqNhUZKz5TWmr9)+nj1BwMRbbeIg^^0N6F#ue^ z!(@lu*S;aguznTxRwqBf^Y(?a!1VZ?>U64of_C(Y#uA#nTE4XZYHQQAiOe}yT@KU~ z_ZT>!6#l00ncDZ!D6@f>{rH)DA1=}i^ibQ+XV4f?t`-|(H)4q4c>_L&5HzUtGkG(C z2^G%R%t!ShVoIcQE&_c!l%^=^cM(^09@;Vj=N-l9R7F#znA7Afc7N2SyHdg@>!U-= zg445x)0xc&of?ueqL0r_=E06{+o86#4TH?0UhP`eX5ox+W2vik5ZOk*vb8ZG8T|^s zrecb7qm=-|Y*&33UNOU@N06DwL2VkEd{KkYtF|-GntF#-0{hF(y~LOjPnjolG^3%w zS1lJ=Mu2uaaU7kKxLu!meop#`oHBjYH4V$IV!^=<(~aUr9H+?72;w2ETyP@x05!w2lcCsa-{W{V}Ta0kGCgNsphTB%gH+Y~H z|5N&g@v=9l5ZZH1odJq-uboc!=}qc5uZ77+I++I#+rCayy0hQvQac?l_s)-kM%Eoj1%JW2Uexxx?!iQsAF-ri@Te8n|j zV^}cp7T1TJdBYEpP^;KfyOveHLp)cPaQ*2|=8%1ds)6$bU~Dl$_;^ZM@N-(cjbXu<3WI z_$E{Z(Z~Z^C$4niEJ*b)+n6@|G;m0Dt;tgNp}N4RBcbll1C+$cT`#Hb7>B+psn)&? z>MBN63Q&M$Dv-V3imP~@e&?|)oqH*aqd}_quhO7J;Nlqac7p|uHX|0PfSE8*kL@sG z90&A$hrSHx`-G#CaXd1*j&x^RZ-r*vHCI_?#~+yC-=jG8WQj>q+*7|&3@1Ml>&I6~ z=r`pajTZZYd?HEN|z}$<|BgTq?9zySG^_mXvb~JmxI7HAwou?x3rTlfahXY8>=jH<@!T z|B9`3u}p(HnBZD=%ttODO2}O&_K#lYW)z~Rn+7sK=rS%-u49`0CLMWttOc3Uu*;P>m+i9{Ipd5r>6uf2(5$bI? zm5_HKt^H(5&?__R^TitPq>*ayOm1CcCsBtooF2^T4hmD*;gEg zc9<&d8N%vk^1gHsb28&%wP?~ut1{p5vk$imKP`-?&Upl-ou)fi^)CW)q2xFXl}y)i zx(K_)T!nJ}3(YVre@|s6Ypv^NQ*8quZGWc;Ut5xS{7jX^sdK44)ZMbSUog)K#Z~1H z0zct*$5XteV8R&kF(}IBhZbj!C*D09$BiSgemkZ07LP$*89lqh)8Z-GrqgT!#5Oag zp1zL&us;WwBWius&*tNpu8ObvOr2R9K(Ym^AA|im)19| zEd`cQjp=l1M8Wm;7_PQVET~j*Wsi^qG)U6s0o1D0MI|YT;U-6R6F`i;yu@;~!2ZD+ zXV0I-KY04}_`COi5r6RR58_>SJ%F_8ad^1Gzx;Ra0SyVS|64D?vdcpEndBZ~85X0` z9qD={f@B&Hz=`11kST#kD_Mh3G}|)gg#9kzc(K5Kf8<#KIVFtDNGW3+Mx>7Fh7a5N zM=>dD##-&#qB*j&IyxPhj%jqO>AJYy@hzK}GeIya9hBM-)dBIB$@M5B5G0P>k=z8+ zxa#CmQ-h~Ivr|1J6TpBotvD+qLbiCYKPXIY=bc{6j|q0F370{ z50z|v9vjX$8VZ(4t|r?;vL3dh6kj{+8GVvMbDJW?)=IK=tjCGA8@U4=30N$9jD@k?Y&{TXG*L#@f{9fp+Ilbpxy#sY z3l_cX%8y0P?2n_;vhJd%oH33gy1vISsLmmh;&Br%ll_Q=QHEyqbXn}4anLLp;bAKf zM~OVp$OON2M6SnU7N)dtY zSsiuOsD3owV3tNzbPQMs%Ra_tPN-bp`!Sw5t{nr_Sf0M*VMnwn>|HILo|MSv!-V&> zO{;w8aB}lp_!begswQ;US>$cL`*gsFx#I8}iLK4@^h11WsU zPxg#I?@sw7{4#ks;-po|K&7d+0~1y5zj4KwVT9w~7*GiP&yRfA^FD=&=gA~$8%871 zCOEc!s!D6eY~|Aw%z1kQlVL)eU`9}}$R}6)G->e@OghF0)li?U#XKnFPDq2+Q+7IL zliD#;9V}w5u8ob=;=$qL`pgr(v~AbLRiVH{zH=|95?I#RtlE0SO4TLeCWOh zv08Na#kcFuP|wZ#`hml!E0 z>@PAtx5!v(pd$gA1)vqMwV4YqXVs^~dXe;OF&Y^cb%XE; zW)6(88DPIf`Ca9o;D&8F+K17Z&|G7iX#3u-yfZ$axoxuLjN?(9_`(TWmUGv`AQi^X zEEs)aoRkhnBVjuh**zZz6lN>e*4W`j6E>0^yD$gcY#-n3(?{)O4Y-Ibvp(uV_`1a- zhXAnI3|OxEIy)zYy2~He#&p3rj40!Xv*!-L%s9GygfnN>a%G#ZL_)t@N+tJ3rb=I< z?Pfb|39YlH+jo7&SVr_(8={Ow{qmyMv=vb9efOJLvsNg9AVClbMi^bP2QNMLsB~Zj z;s8dc&yAZ=+NUa4S!Gw>bx0{=J6PZ&VH_oGUDk-fWkAaSmFo-_rQqQ~(Ce-^WVf6soI%%r zDnM+9-OPll7$*W^iWiGwa|O%|+Q2jQv#U+{JNax3;s}B<*#s$OR7XZ~0IL&SBaH!L zQ=}s%FNGJywo%YQqs==~8*C^af36GK#@5F0z!Q3;DmcSDeOwCt&}3~+8PHS9Cv)Y1 zm&zNJ#ahEQz-ynX&0#jxPB=^v+POF{W*nK*`=Fk`IViF4zOR)5_qawDY&-vnne6$pysZVHd!54e&&W5vIz05}@EHN=<-O zOuEnsAljN(u%pGv&S`|tH9+9bz^O>6>lt?rPJ=fFXiVrxHUY^*CQfu&Xh(Qgm9aN(app$bb8Sb zfjUB`Z6{SWb2B$vEo<8zPdSBmCK@zw++;o26q_4rhWl2#)IK~26_I0b8oe^(stcW5 zuZ9A<9tx1lmRQGQ0?r-m;nR;l;`9XuC^4!V7Bfdoml1OA@ZLZDBYe+)`;$1je1se) z2XXw~|Nf8STfgf^@Xg=&8oc>UKZU+u$r<>7dQ08@w*TVw_;0@b_wjv?JdV}D5>yzR zP;a$hhUVS}K^qVNgkwRvR?(UVSEISB!2x@kOlT_-JOIN*c_QkP653W6VaEyLt3;U< zI9#k+n0+AK5?IgYm2t#ya)L{bUBvy5Jcb7^UdCrX_c-o5+G2BbjLkUUXk=`Mf@MzV zs37G9x{lDVdmMI4+;H_7Tz9y_)2=?m*>h*GUacUWjwrMo0z!f>RJoj51rRe6jJ%zM z0lDTZ6DW6b-+G_1S}$;L_5eTryl3LhJ3ozo^G6@Tr#|-(e)c!ti5oAh@WNYeM%J^t zwm3<7tO#4^?K%X)DA(jNz+D23MX!Yt=O>Q^B@s9Qt1MTAT}d6fB0SM0!ceHDE`ucq zKrH&mclCk-QOAkds&^}2=;YaHjw|g&C8T(g;E8T7O_iSF*op~C(@DnPL?W~bg2NX| z`4hgV+{?-~W;@$1y{zqe*%qR80NDNn8N`?!4xo%xcG^6)8wr?J5h>q~!zlNy8#z+Eb+RDmgmEl-w>jbDWP{~$(KyR}-%BkE zB5a2ussEHPLNZuv!_?qsyA$4w1jP^OEe`~sUvywlQ*6$jNlECjTl64;7T+VSzCWoQ zG7b|OtjrW`$sOc*D&00tiU00QGkb=bu|lWOXmL3Kqe8XOi_^gr=4dF@XV}}A zf#gxw3GlxAr9>M)$3RJx9i}i_v4ja->{z@&*QYZ31ipxi804rt0l%ZwMOc8t9Ll+H z+mTTcnJc!LF~J-}gGI(>f4Zi<8~ug$sJs%#Z1Gc_{Wu!){K;9zI<7fU6*d8s(aI|( zAoyg`&4gEXqJo+~Izq)b;=9S>&tPZFXBz@S)F8UzInw;BUxPg;oL!{pe;dM{Qmg&I zvv14<@|`}<%XWk=VD zrc}q008j!B50=gd&J==f^&Z?|$P?;j3SLEAIW=gZRiDAIBnqZ2t-a zzw^6)jE~**2E6V6`bC^QbCu$>4XWO7!;^9IOP`C6f8?)l-BYengD(lydy2c_p8;;t zB!g}&>}Sph5F;AUYr2H+XQ}PB=!SN^H$uA@y z0}xgnFQ4G^4_?H(?z{(|{_KPJXpC zCjji6;mv24c-nWFN@s-cN5!YRHHTv~JcDGVeN0@yVKPZ>R%4o{_n$yL4k1|1u zJE35iIxPB({rxqrzUmBaeZe#Fp7(wPzyI#r@zcNhpYUrxc?0$j_ElHOGkyx*WE2sk z^1S?>lAMoBNrFw_gro-ksA&Xa$~au~xHvNQdf>4Q>-ChftCom#*Uq&zJPnhYX$u`h zyT)Xgt$t%@)(sxEozoe;)rXUQ@EQ@JW|GKHd)8<;Z1nj0*fS<8g4IIC6~=ENgT!Ge zax@?j_LkLwYhHegJt&B9A5+y{JDyeJvoVj!^j1ZO^;;dzb~t+sj>|fh9HSvDJ75@r zfq)T&%o_~U;rxn`iE+X`E^lS2yH2mV8-QNZ%Pjz9c9svwtfx&ZV@d>!1e?h#n@h}% zn``V!8PP45*lY<&)4#`I5GPfyQ6rV_&DfCL@>zBS8w}ZZ{pD$K{Lh@(Ln*-JOUFnF z7`CIPluIyXvFtraz-BBEA!({Qhv;dISzBRI$xs=`dS%-2l@s)f-szVTA$M}mdEaMT zJ|2DHiQu<1jC!jGN@;LrdOdfYrm&06+;yF-^H6bCla(YLe$mUF>7_9GK4Iiik1CnW z4i(x-Sw3{*WHr@#{+g?u_96dr2QM_Qxqawz2jb_<3^bQl)4tn0H@Ddwte6R?Qls$= zs?FtiC7`o=%_Y5^n;mqk+F6UqhV&ja(h9e`M2;V3S2lpl2vyR-h*KPB=3nDmnGsw; zZxIj;DAMlhx1SfqqQNRq^`jvbcOpdv7a6qJe(nzJs;A%jp zc5T}96LuWA+H#}5KGfLv6zA{eT!YIPS!<)rsu<$bcRA`4+{i*xZEqtorXvG8@YpQ- z)Jbw@%ghIk=y-zORm?>C@-72E`S%V0szB5cCm_YB3n>Mpeju!yIbLE;_h zcg$`?%-SkE_Wdbnsr*8Q6C0lETL;<F@6z3;nq=CTt~h^ zd_R%f&OpTK7;#OB55^_DSf}Zus?S-Uhh4D-XeP55Ps7m_{N){=#czM$Q+Uta58#o< zE~D==R$bCUU4*^;rTO$V_14zal~T>kB3N~E!ZIZssKZqNJa{zVfqTA)cYW#sy!nql zg4KF~m)&?C-}&Na;O3idM0c=<+;uv32pW5o$z+F(i9mO-Ub1X+-X&=Zil?DYT|Bb`SObxyKVv)}2im}4i5z?MD0V(YAW z!eWseM@?3eF&)bUi6v8kv|lJbqQc=u=eVd=mTCo>phabP6fJJe_ILl82tdMi zWQ-t;oUp_O)Gx3ixvAiI>~Uq3!7>+QNO7~pqQ|fuGy_Q2QPbqqOgB+g{zSH0a)r|%luS#;=s3AFXzS$1Qo!l<69a)e3SX-qP5chCBnEJ;rFjIKxJ&~9dO_e*P#{!1KduUzIG|e#?F|$mDhlH|7~}yCSscLu)gdD+ zq>N$RHS`yp;Re02lL$|oLkw61g%$BM*{7(A(cYSn_t9EvtR%p5&>~EMFI>@0=F_^c zZEwwLo6)gjp!k%l@ri>BOyR5xV^vL?07dgu?A?U?eAYg_AeiE~ww65kmS%mmL>O4k zjW+oq^0G~*#ov71(-)&2Q89|H&bjgU9ETGc3V<>F z2mnup*11g>+xa3qT4S6wK&9ICON={xP2}MzGSX#*hSwXanh1_vAh>j$l4nqfDNfMn zS{<$#Z&3q|>z5el2m@mn8P{L4CrXY~fvZ#KX{jVTI){fVTz=#cFb~k2@S#4ZvM z{_VeiJ6`>juf$7U_9`jN(=G7>|L}+Ko6F7ig3qVn8IHlTz^)dp z2iN}9$ny*={izTDOM&P5n+?v)z{n1Qd|U9){SV@Qf7k7J#|J)z%f}~JEjlc6##Q?Z zpT{tcQkj?xc3C`5S$ChLU6;tOQ_Ca;b;V_X2SKWHDQB?{y9DZ&0D!-^=Mntz-G7Fw z_80h?FMl##|MF+!n(HoLku$PpFq-k-Y$Q10>u!%pW)H&2n5h)li3tXw%N^E>9%m2s zKq}GXthwxnCw<(sgU=0CA%$UBx(G27Qj0Ar*~$xYYK*hSJsH-U7?RH4c|O&Ye5Ja(^Ya z7(g80iL1vg;>&ChRt4mDdPm)6flO*1f~C>J@ECdDaBuTy)n`L;l@2QjZi-y}HB!RJgjL#r z(?SA7g>hUu94Q(WG=PY)9SF;%ob&EdUF%@(^* z07*naRO2uJy7LXz-EvC@FxN`z8IjDtGUji!_x<}&J)bl-% z<=QX%+(O@6Z2|c%1c)>+(B-gmkV0h01h7qtbXM$rV8956DFM{o zbBk)4Ft#PAyL~*t=LCuEtT9=F30^8bIX#9>77KV{kCmZLo+i6j(4qKgl)e5 zZ1jAJ)QJKt_7Zm@o2O3^UiMonu z`8$2t`tPF1Q`hlS@^xlMB`%&M24?ZadZo$%$42UOa$ z$2d*HZP)A7Kz0Vtr)sI0GDhx~I6Gd!l}nG~%()9Y<%rGO0t>(Vmfyh7{oFs-E3w4z z>-zR2m%vF@f5uyLwR&|Eh=eAt!;gx^$rTF;m zpTU!_y9$(ZrBDvMM`~^u4Mr(z4x*ju49i7PP_phIBG8-}1aaYA_J&>X$ce#7RwjfW zhfyb{l!CD^zVO9|@PGc^U*PQ@x*H?)SoR6$mK_p}$dqvPHRo~7ldi$lS6{$2*Ib2z z!$T|Mt5KZr*kei#owdI^^nd1=kBaxgHReFfsv?`C0Nl)n{s>R~;868D}Or*P-1xP633?<%I24Dzh66E>}Ii_`sve@AY`F1t*EbsTO?KBRT>#wA#8>T-0fmL93vXz*)v{ zEw0XMd$RR4&s~Q^34JFi_)@jLmx}J0+y*J5rrDPQ2{u{(a-1b4`wmzxdX%EO?N*1( z^01kaQ$oM!MK+EC6PxT+vjh<-%p9s^=o4wsw_lH`KP|P`4eC`{pI8^ zY0JvzH2`?Z^E05;7STXuoq@J65>w1{xq}ms8HDB#+4-vay*bIJzZy0%t&!pnFvgxjNH&f~Iay&Pg_0V$ZP2PP=5zM;aY5Ko z9h8x1@Lf(+#cx%fp{w~k8hWh%V6*TCCe9XDx@`CvXke>&50T{^ip+qSGV8Q8kueqr ze8g69k;A+xaFiKUjWefoMX_&HMvb2#<%;<+EU&nDGXSb_Mp~duj`zFrMa2tg^R~|#dDpg4XGsfiR-+XVsj_!!`etex9 z86zX^+HIfFt&29QGS?Pv7Y_wd*=Dlvq-UOdfBGxBQbD9P=$^g8@4j+|hbR-L7UOjO z)*ZD*+F$Dx7dI+HpSwa9jWk_DFBWA@(9R{ko6>L|I1r3FjV8ZYis&GVgZ=a;UyaXx z;uCn@S6nSC%t~R6^DEBP1NVIvcij0IoITic2rO;(InVxbA9A#nH~cI7hZ*>1Z~E8x z+u!hYICF3qa0~Frc+IO{j`#iB-^5pc%`E_AH_<>b=ZfM(y8Fs@7beBEHZ^8~yY6PY z?xpZOIM9N@uap;p!aEkZp563&x}~d93dWNye&;{`C4Tllegs4X2g?PR3zivp>P^qY z^IvoeZhG2Nad`F|mdh2ozDM8pV9g`;fH9FCR}iDQlo31*7={7Eu*I-F!o`b^;lVF{ z0r%c>FFyO3dvO039>k?f$6!cR99)q`%PFrbK)(7h7yLL0VWwNalPiKZc zTg+Bb7#j)Ld~Ri))KyAylwpyv>N1wg4u^Y7tow}IB~K4VmW+v<2&)<_PS3Mm0)M9vvJ z44RZvkW!qS1$t7Y7Uu9?hOqPb(r=(O0+!1jC&yb&Ul%=c3Z+oWD0A{e0g-Jf zXHZTUi#Xf4%NRzMn->Cjma~%k9$lAx-Q@Z=DNe_BZ*?i~qMV9U3MhoxH39z(8iH>k z@u-O{5;XeGSFrP%e(TS>nSm@wRAHJeQS2l1#Z$RtpP1!ngXuGqYXMdQ(B+JVAk4`C zVd9)yyJ*}70onk|RY*Vte^Di1V3fk(B4+yXMadq*m{LyTGfXeh)SGaD<)Q&UjBz73WG6~$mfY!w8FNp36d zHpLjl|4k+y)1h5>V*Y9|QduRgL;XeHn<2*$288SU>badri`8hIzue)Ebjj1HDReFz zxB|4JFYkzEtLd|^xUUY6?~?J{RA|Q8#Y9s_WJl0s4hFf=WJbPq+XT5#dD-BP#*hWr zn%&wkgBXOErbjpZM_vth*$H|BO@Ij%yLC1#pYp7qKv)3>K9|XQG=U@3;9`q&{Y~Gp zV<5ILJWj_X9~YA+8k#e>D(dR|BCW!ruiPTdK1OjfS;I=Hkz%7gPw_FAYc#>-|8M6UVXaxIil{B*9L9@JHFAE!z}&Q}c#qu-DJyQR z951UJvc*+BITSqW={Mj#xBn%c|FTy)eN~|AT2V@P+wZ&+O8|F#^e#N-dAC;hSe+eI z(zgqHtW6?Gu}(*q@#bH63x4V+epKuxB_1ZiYrpE1_>DLJ5{Ba=^sAMC9h~Oq6uKee zylQ+PIQ2p}PMB%{Q{W%Be)aiah}(GNS7O=xtP-=lx2;xoTniin7d-sMhw+bo{oT0Z z6Zd1i$jFJ&gYmMjd#oOYy@!6WK$m6riEJ!Y_}&kx^fW@Ja9ig^{G$djypby2kw6W!?vL04ml+dBc$NU zwQhSv*y}sI<%6HZZ-4SW{PZ{6il^Ot19F#2+3ZX5+r z)k_RW`aGa%-KiR0K|mVai_Eea>UD?aEE8do6ZSg7{xYLmbXYGJILrxI3jiV!C`)iA zC-%*%q5+%Ib$I@>Zp4>A;|APy*FE^tKmQo`c!Oo%NosM>m;it>g0w0&JC}&P+joR> zXEGMO?u-&Z;>)=0%sR(XfW+#wGlTWkfl`2G<&9sf=$LAors+(0H$pFdd0v#I!FfQOU1 zm=X8Wp@Uh|(qzbNeF8I}S`5@I*Oz)Rsz^Dd(+Euawep(G;Lym725D9WdE_!NaEzhb9W;} z1|lAUP;dq8MqVIY{m}&=>UsmgdG&~q%=?Mxx9(N$+Fji7%#KDe)yM=vq`zw5%7?hq zEYqxxZCkjC+{kcPnZlKGEtBjt$e+MDus)4Jr$L-)@%dR#VVr3JmkN^^R<$v;4Jza7 z1m-xftc%ulGhprf6}P_xyhb{yGjGkk3P(G{m!=AbkqjVLCE@qlX&uE@pw4?&IAzMS z5oUWk6PC{Ja-_hr6+QCl%TdQ>{Vc(NDw`<0pDHMW1gL!3{2H{jqtif-4V&qN2{jLe z-$So1-{Rac1Jh*=*O^R*c3jTn@NC9AfDZ=dU)Lv}_Y}^vH7!lPw_kbcYy+z4nh^M% z;Kf`SnkIs`0fo6&skBf|BTy^zXJp5^a}~3rf!Auje2)*RP@Si;r*bEsZ&>gCL{@)cL*gzGooBU;%4b%+BqP#oHDz z9=P`*{P6$!`}o}P2CGE|4FjHi%X9Ee-}0@v?z-!-x3|WkUn1pK(?Yzlw`L}?Wr?;hl^J>c;m0V8~^z4ybyom#ZO1>79a~GfMC~-RTKtl zCupHa=uWupl*@Y$Y>-^*;{hG2`o_}bO{Q8Oq|YG{Xe1dR$@NyG9($J)4%aK>qXFml zSNQy+7h!kFNdTTuirI(^aikOK?E&QLUJC2=gOwH{D~c^pkCoJ_)a=#Ti4rma9Wl^n z^nH)6OGvAw-&kaKaTgFeA|%L;y3z6oLPtXL8Nlg=}DO;R8`Cu~jz7jtG; z0z!Xwe%n<=&}}CtgBSiQ4W>!X%%>!QClsyURkK+Dp*^``Ej8v;84FUE(JwmB8etyA z9|_kLGBdiqM@mV~peH#(l9Jp-pC}>ej=L0|E8tNBM^0PCubh+J{{hQoFS=tpy7ec1 zo#H{$+d*;#TT+~k!&p(k1sDgXAzxLF8oJf1Q~S-`82haMuXgR!0%+Sj(JO8!PFp%F znJk(DF|i}ev~WjU`O7w~ql?HUlN+0?cGbyUdh)gL1M4R@k|`h}$N^&@OTnp~o6^-Z zDJCx{SBV{}(86EV+hUC|fWV~ke-V_s7XgeIBY1+>+`X``-0U<*M`c!fcX+#mRdBXn z)bn-GK;I2Z?ITE2|Noru+VQQ`X-WBLjT!-N)=NObj>n%6p`p8A6dTVq@P`b8X_p3q z$yEx5QTT2?ZtcjfqJYt=t(!-z;@Yzc6Z>SeU_vjl3i+HUd~J7zS0=2&z(6yBUYRti zKAI^`$5fD4e!a^K^Y!-@jnf_Yx`sB#gvw{W9By@mAQ^{+X&|h!0+{nfMOc{7Y|-l^ zoew!St17`pGy}(s$Y}6H9*ClUmyr@PXB={Ox-Grg;8wXrTGS7uvZg8)rdDp%q}@6w zQ^Z{xKHxf30HX$d3`h+OS_T5x$5Go@sCenkJv4_MS5+ zt#R{pXYk28K7?n#KByvtT8nry`aVCbG_#q zVDumyu6z8<$Ln8$zF#6|shUiH?8@m5pkvi_FX|dl)mauR5hU6(D$7tM zetk~?tl(S_>pAsshc}7CpNTV4EI*R5y*7dK){8Ih30 zIf3wm%ZEk~wsO&umJRM+z2bEzmD53ly;YBXAwd$-bGjWlm|BBL%E4v8>jC6UASCoe z_{Oh#F8=DeGkE78-;O01eP1iP+xg*2LJZd3$TNo-2m1+KCvx7hONoF~WR7KoS0OK@ z+U9n?UV=KPdMOB_DnBF7puTqkF(os3uT04Rwj;1DKu*9S7hOAb&I{vsU=+&e5@Vf4 zEJ>WSEjk=aWi>3igk=Wua3mx59qNU5roSh_cGnM;_hW&;_?J>b83$Nk)bO1=FqObm zs;&mS7`EIouJpxifKljEdDDu2Rv}og=wl-qxb0_I0al;D^t!F3t!HZU?j_Jz7<>Cm ztk)}CzOq3nmaW6ciS(Se1h%YDTi0i7hk~3SM`DJ8z8A;4jI8}i3OZ5_^dR?tB&`e4 zkzB8~*|J}YnB10N00n*DVX?>#D<#2UDWQ00DZn_k1t6WwY^`<0T?R#42~LCce}d;~ z=k0RV$sc?o6T)rwO74>e6J|2ouuer`iMo!P-)qKU^#sFLGkzt~Yu2hft43er2)VGE za5cx?>t9#W!Km>%mY$j;!R~Qn-$@rjtHQSKga$@1V*;ybhy!R-F%@I6n z#Rxl~YKYHpG<}&+H09CQbyc}UI`ZJfr2US)PU|90bcP3@E<<#n4M~;eCi7q=@h6zx zsgsUabA}4$l}pnwoIOp|E~yUrM8HkfaVnha6u7}dv=HN&?@}7nIlTU92UnL;jZ~fH zEV^f4R)fimx{Ci`!cOR8KBt|v;G9fOL4rLqam>AKpb1kz#&?bI@gPP7v`yu2_uXsP zZ7My-$3sNYx+PzqikM(v7%l_oQ6Seq(1aI4AC08)AKCkYD&~l%hzky*@T4Q=x&$u6 zBGv-xhOJUwYkoey=)twu;*Q{6&QWpJfNMX``7nw$URM{7! z+V6v8dfehPm7U4MXQ-}-VjnyMjga&sI9+<)Kau}Flp1b*%(eg^M(*KN4|N!R0p zANnBfy8W+^Q%mddHA0+p2C&xwe{|b>@W#LYy@;*36uk1~FTn?X?bq>|ukJuYse)N? zV`-RTcreJ)h^-7)Ema>dZbGsC5u`?)z=7d)Wv1^7LsH#&A|n}KxkBm6(FuO&U%nHM zUKz0JfxWKBcmIPQz;mDfeC+S-VX;_X*>_kiG8R2y(Md74oXCy!2v3|v7*WLqD=dO| zanuu-vl!FN7={sLy}))Du^k2svGV}HHI*+h7B96+o=ABdAqqdKKo0*ncS zkw+s)NLB6vy9&&YIz(C%Z(ODvD^)X~Cq6;8Bb`WsGDKJ`fdeYYDTAmWvlZcD6E@A%Juj+4tLSgktQ%_qy?t49-vfIdrld*72~ zKucSaV-H-&Z4~i!tE4S{{Gg>y;mIk%2L87H8H;l4b)ruKotzBfjQ5GrC22D<;e;~? zjLj%_VRt-YokwIHWTcE^?m$@__sk=DE`H@!p$_9n$gEi)EHc6F^mY@0Ofb;`7&{YYDooB(2cC&KQRej^n3q4xG(YPIy7Q&fs z)75UQsC+2}tJSjhOR~GY-AY3qqwhN~fW@N6=46Z9$@OwM0bQ5X<|0 z;zn4FkaGucLAU5+Cw|z#>?{F~C26Y2z3yGkINpj(PKmJHjH>U{(bg1vv#07@CWK}m ziO~3)&F|BVnAnv|y1V0jg>)sItnE6Sz!7YbQ162z1do zi)pF;zTtMGn;py^UlU7Div5vCSZaDZ%eiyrYEZai6;K^))Mh63ZH$CPxEheCE?8rS zg#|SP`SRCEFldtg**YzACwKDKqC!C^nu3pgaaf^Qk%RzI6b2qiRLI+g6$3_nHSJaB zW==uGRv;7~2fJS}l_NqrcbeyDiRg*_SrDe}vt3<_3fq}hX7(b4O@R-Dc$x|qSA?+l z6(4S~w9rrH(njeTMht6gL51dM2v6|IJh@tCKC;`ek(s@ZFu4DzOH$ z+)g-~@r_4F>4AccRM$8vyeSW!N^7UQh+0%kkq~=oBkm?sYX;P=te+M~?JE)C(c>#> z3>w38)!IqU;gY62f`D_^UckZTPTYORhj7y^w|dr0oD6fpmB)^7J_D38&g?Vp{L}Z~ z4hD`2W7Px4dV9|O=>mZ8+-E%#Z+-X@u-yXa+90N{ zf5ih>rhyzZCbbBQK{n@S$Llm>2Y}xH>s9mhURHAq8IaUYs+}`4hV6j=`Tw{LpL+Nb z)_uZivBVGj4?l{VZhj{A)_Yhld+aTH?5#2ueU@}~GQAV#Q={?|858zu8n{(vX|-u) zMv{Brkdvf;F8Yjd%oxTI>qUp{s>ktWz-ry&-1+l(@vXPw;~)PR-u>S9;GR$4i|t70 zvJ`d#0avX%+;;aDuzB12@uT1NQmoc%aTdW+1PcUWyZ4)1j(Pg9@?9`vv{sp%X{|iW zXd_hObbA=o0J4YlH|eibTP-JGBygAUffqE zi7m4e$T{QsYYy>kuYWb(`dfd3&6N$hjK(n+)acIWLBjqzq3_6d>kakx&rofvAb8Or zLeg~i9iD~h_f|$#c^zrM-WYzXT`aUN&bg$tXT7d(%LyZDKtCly&nH-B!#81M;Fx+8 zO6Yh*;)3Qch}=&BtPn!a<#Q301?*@K|cn|MyJoaWQfWJIhPF&ew*WM`oGpW z5`>qr@#+OP5ML!(pvLrw$#pjUX0k{GqEtk;g_J<;FpQF(ULd@wKigUt*gA|zIbprG z#GpIhIVI47upLIp04?HhFII8`0i}e+qQ{jhm%VUd?qpY-*)rw(b(R1CAOJ~3K~yx9 z2OB#@0%1giuFn8T5NjNP!OoT|kXFH+Ai)48$yxx(k&WV62ugqBW}?%TzJh&VSuv+* z?a;o`vOlgo6GN%d7OEs(=X>gFB1eE=1B5w5o51}FY9XK5e4%gChi#E)G#%9VP&4uH zl{-JEJi&Ef{!m9~4p~hS;yc5xWM5-OmYR&F5fc>5O2N zX{Rrn_7nEkbiGsJH}jnKF=fN17@P^qxYnweBCb6nL!EO;FAVhzN(~0tZ2^8`K&hWC zo5Kqh#`avZJS@|LL#+f$&#U@^8+x)eGTJp~h~m6Y!E1vfQ_@`NrZQusOy03jM(4FX z>wBhlOIc4N)(9jDRoP+*`M zL2iJ6NnNg3p8t#TD$=C&{UtQP?AIN_oa=;U;CWzyCy!63G8v-!`(5S&A@e^d+SOzGvcu_8gwehcf5xL!1AW0V|&k47$m z#u1OTh&;`SaB(9=am+X;MlE_5&oWv+DdQElJ`1<~_HSVvhbmW4|CYjj^-4#;B0;L` zW<9^G{iP?sk?3K$7k$wYK6ckf>ryIA2Z!fy@!~~{QkAOFja6rI#kH-MV$^9W7RUoC z4wxs)SY;WRLBRR|Sf6R<;UrwDF*Y#c-5>r8-u0nRVQ-PaBjfx2;eUslpY(BjfBq0JeBlf6hVS`4e8+cwJ8pXVjTnoh z%CqXN{Y8gA`OHK3rFVP~+bfqb496(rMk>me5f<1mBt;vCO3^FRsM>9;XS9RUE0oN7 zWGe`mXcw4`7Ysh<6RLVnu4@72F7tbED;%V`!UE8G$d!hPU0f!w_50?bR zMJWizK)bBxJr_CQFeeNcaM*Ro40L5gQXCeHC_E~@Ms(u{ltF_J1;ho33sNb_%;+Im z1sw>hlyPRg!r%GZuSQz**lb62;@UuGmw~f~*`0F}e<=Yu1Gyvlm*un78T0FpY4R_u zceKU5px>z#unb34&e`*00geU$C|G5=LT_6Vj!VK2eAo=eUK+5>ifOfrPH2e%85p)$ z+ba<=XQ|Hp+%Q9g}&ATK`G#IlB$Y{45>so@ps7E&h49^ukEkN59>#n!uy|y zvMD?<$TNG@C=ps=7h#zz-nj0~6V$>U8yE~-u{C$2yk5#1lJT7k(`LbE$3!gWYhvGlRn&aRHLpT1T$K~IK>5TrDa<`6Ua7ghAP2|+&XUH zqe?3kI@U3~pB=cF;>6iKN|UOycD#7@F|LhFO#u(TRl0ME34i7LxbsYLYCiAEN6q>- z1WHXie}9^=jL&NyQAAay32kkT-ftlArmVN|;SSvTv_kqi1GZyjv}yju%y|x^G+n+* z;I-p=yddJQf_kblXg-I;PBeze#96$JO*z*A2~>-7HIs~ehAwRGkx1N8n9u}B zQ!hD!57)X8K3~%S8?}FVHs4BzbvdTAIpxv1Je6FkF%?mni13AnHh`8#bxBAhHNA!^ z4FP~jXy(EV*W#&n-GleN>p$TYU;B+SbfyG+*;B8<#e42YCOPX)kiu1q%oqycBtfcg zqZa*&FNOrlI}pwg@c(|`cHH{nR|HuIbbXI=H$DZa4BC>=1W*vxBPj+o*h0Y}k1SmQ zs9y>`gQPQypBZr4s=;eHj%E&dai@&*w{&uXfAt^#0;^?*lg)r{`Ofdc3vRguhkGmR zuR2`3_(iL`+(F$isv(0D;T!UekM7Vcn<_65VPiq>Z= zi@{eE7F=<5ZUke<#xYh+60kzfIJ@j|Y1!lQ@fL&C41hSOMc2I)IZsQL9*ZCYxzE=3Hy-z0Wz{>ptvzudjXm zkO5;bU|u!`0|87xRe>ggQXmQ_MEN6))JjoXm8wN0g&Ljw8Oq8V3~1|W~5=O>Caa{%9zs@iy#ONhf7Su2%RQaONM2& zFevUg5YxQwj78gUdNMndY4MaE`q{v@eM{oGa*lstn1>0PaNUFCVw0b+3MK@m- zkUBhU{k=o~gwD|G8LJy8{AGg|&^f=l+ZwV*j@|L^P9D+vYNMApKmMG&#iQq}bP;uy zFi`Jf;JGf(!gF@>P^&T96FL7J-qK`l2P5+ySt`vMm{qH+839#j)V#LHd9+A7ue`mZ zduI>kuDh$>9+`ANHHe*VA$Hvcea(xgzZwk>>bhuaWCMKI|8ZrnOC!B*T&tlsLqXS7 z5%uF*BS|;D&~juDc7<1`Ib1$51r5iWxz@wy#G4~w#Ou^QXDPQ}4qJ}k=(jJGu&;O2 z90-a_oxmdFO|9o7cx}ndYg`am%kQKOSJo{_SqpclQb66y$Q1@_vhkTdeK#agBF>5Q zM^nnUO+|!BoSLI$5{w`$mDc4{}EhR5B+1+@A-rU%qwOtj?$!5v%4IP+_y{*Wr$p-(I!a&;ZV*%Er zML?tDQE&ibfG69}>)GfLJiB2X;WJeDwVPJrqHEXbJtZ%kWJ7O?N_aA^FRdb{h@nb+ z!{<}$)w(l5j8T7DY|K;JECffwS~$mE_RGZJ#?1cV0b>Fve+oH~7#fhRmlh`E#25;&Konig(Ct26 zhKLBef${Orz1Y}%pr>0;J&B4%Dpo5{dd^Ob*=oen3#z*8F=hf^*2Pg~o#H8#;Y_*H zE5hF*+h$q)80HFI3V#0A{uqDs=XY^FCA|Lg-h%gh+h4|Rx52Of#&6;0{`a55hyUb5 z*W$~K8za8uo4x@*{Da?z&;PtPH&T>pnt^Uk3BN$+E506X6y8VxA*<3WhUo}#wv0(C zpAR`n8vbs?yT1CX@cD0g6MpsAe*?e!yYB}mBT>R57ZZN^eILe~UUwT`^5$2mQM-s9 zkOk*j3NH{RnTiN31|(h8Wvt2<->L#_U9f#RdTo^Mjw-PtA;x!A#|2{`;ED4y+`Bs9 z#RmtJ!@>C`rGyNU0$m)U_K5Ee^hW@5Onr`WFHmQ6I$9qI*koYfii>f;E-}ChxF&F7 zWKK5aKmu|FhBjayu@$g2u3}4{20N30C!c&2-~RP)!!Q2x_v6<208n!&+`5r54TM39 z(wV+selTzl(=5v+m{=SLto17>*ybJoY%p*=hq${Oz^Wu_h61oJz)}gDEQZ&izaCP!JAji-(0kb;bqF7)A;IFPE3#G+@bFEJ!Ft^bIO|40%FlIx}1c zsto5t=4Wyrn9hiuhw_7{H$~m7;S8|SQ~VPNQV~Xf{u& zCNC-yfSf60gGzdPli)4d56Cuc)0i+#1Lh)IxRK&Bk9^}gdq<-Yz%x%=%s0B| zf3JARHG-}mpS_c>xgkJ>8&0~Q3d9~+P=j?Rj*R1Xee3XoEY%NU7$k@hkqH5t^VOe^U-%z>8dncrQv7Ok@~S7FK(4?@vJKdjflVfCGO$U6 zA&Fd;?W9g*kWq{44@1Je`wv3Y(Z*VFg~LV8 zQ!*2MC6BmC%g7U(5RntC*-s4@n@;g7zxPp0V+N%Wf9;2V2qOVM_7nd#e&WY}93TFZ z4ASuZsNj3WcKnC#O{*0f0?=R!KzUPPVBmd>!$K`_ulA>;rBgJpWcP2AXVkJ(}>Mx#Krj*kKDe%YhUvOzWrOj72p58{|;{7JVz-D zG6;{K5BSOV{sA7|y^r~Dg)%STxwQ20h@<{f(`rp&^qN^gA}FGgT7VYa^>&mAdbR`? z71#hH`3w>@=Rit?C-2+@rHu1wz(8&G2)f1Dkei{$a0f%5#%u>6Lx7qLv+-I%td0qT zFfik6Ll`n}J`Ugvpf0GqNTI!D0ha~D3v!h&il)04NrNA2#ZW79tw_v3EuQXfa3U=r z_XS_@I(*@qpTgCC7El2gQo+shzBpWDH#Q_^YZT{%+qLeVxD=;fmSb|>#c2eIzffMZ zFS5nlc95v)<(!cdW6CVG0tlE;FrxU98k?UA!htrpEC7`mXS86T3gih~7L-zPNL$QB zbulI!uC8!C62?Ia90v8VO*H^;TUy2TPHxA7Di&>FAFqgJ$@;Zw_t1MOrN+}$4(2Qd zv!r$F+H*ZlshN7GGN&T1GNV_FW?iOL8Oz}y#ppT}NAX$NrsXiPiFkbNO_ zdJ`iUT^_I!i84rqbtGkVgaCvzZ0HelkWjynSrzLX4 z1+Emi3{gXbEZWI_-md|HPt}g+84TJpdRil2rh9bJb1M)MsE#2#tP=3tOAqktfA(3_ zTJZJX^i8;R=P~@qfAOPu=U09y-uvEvjQ{4RejKlT!!saO_&{oyK$qj&<6NVvSGY;5HBet6f z=jU6z`mtO1qAz?4e((pr8=v>g6WGrOw1RZcOKi3| z;26;KHmBFpEF+hsWZ5VjZyS@nHm{C;sy>*^2O@uM8@cb{}>kW6%JBZxj zvy#amPy+Op9V4-C-+H-;nky|&l zfHb;Yxuv*UYCM}hp*mI169b3|5>?jP6efq3rG%}3Wk4R~wcLKyK?%UE+ZUJ@p@o#& zJ*=|gbDw$^<1`@;vIE69X77^?FAw5`km7JfE4Y`&d)cy@gb$6AM9*sl4*OZ?h-_Jh zI#vo}x7#A+jG_hPC?#*XD2IZUKo^U^{4`_9=G2igeT7vh#J<}mN zP~D-rdjAP!z|pQ7Pt*f@{@CeZ>v0Brj`EQKq*|;%7P%4QTL>tSt)eAWl9YCCM!D<@ zDSJa^KNlE@3tDN{3e+?s9p+f~!Loy}jiw#1Mn`QhJpcpY+0AtNnV9m%k!$rFY=tSL zM9hsTbe-zQ(*d62*OdC+fa2Bn_|pdPim14{*bK42NzhVUJgvFI%I1j-!)?t6&(iu` z!S-9cNgNNe2gBjC~CZ%n{$<%Uaf%+_wexi~Ma|JU^|pvmgkCx3O0 z<3$&O?W!=wbqah>)6HnThRS!Ef@+VHOZ1$YK|bb{k)Gw=V6Z2xRGS7Z^5sT6M6odvi|if8{XKoKdbka);qU6w z{Ra;LUPO%}28z|0U>8TGgqlIil_hE4@1JZ%(bI=(Tx`MmSc-eq+QvY7uox2`LkXL6qg~X2SW|26t}Vz-yj* z5%Za)aJk=uxZ?KB!Hc~`y~6fDk2*}R%LKS1@(=)P<9tOZ z%_-hrGDBrQGp0d!3A1daHYDIsC1rY>DuxhFv-dDFW*%|ijQvuPP%vS}q?s}cWS{K= zM%e!048d{mg0o3ds+?N{`4b&5j=pi89UdW2VtA_j$aQP2yI?vlm?dhDHV%UAZ7*%7 zL~0W$|KXqwtA1nryruYCzbo>Xk+Vdl0bo8H)X;7CEO_q=j{pF*6r5jdu-S~5=Ykvx zREln@Tct6u*-h%$%gQ^1_n59O+iqG`sNiYTtLA<}My?St>LE?ephxI%yv}eAtlVbd=$Upab&sC7 zy6RZe_KthcN55BNI`qedZeAp=((IATNWgSFR z=Kb2bv(BZ$Z(~3au5jMb?lpd*&$YIDL(l#a(7~%47ayO9eUE);(cBm@itTiRfk)5X zfVQZ4IJWetF3N2}&f4H0;8e(k$T+Lg*9_liRHHMn8Ldr56qu(X?`puY8SQ#sq%Gzn z7&7|4n8N4GKj1}2PxS=uzy#Sb9&u=^hWB}U0S^Kut+3#F27}cdot?uybBYd#_#jdD zWV>gD&ud~{_i}w98kShyd&EZQ#C4;GfbTvx9qmQBG1YSIh?BuIJ)`GM97aRVBN)+{ zvF0^ChA4Y{B8~-;8x!l+Xf=D>H+A$o0By}nZ0sH6jW;LK>c+~iV+1@CC`(FvQX@RM zF`ngk6eB&UaW2t!xj&6bww4(s?N6c)sTJ%A22#Y<(-OkG`#N)Ga2<`QkBWZmvoDGQ zPlT4+uBn1%4U(+QbRJ34i_DUO`0lTL8*aS#3H;JO`X43g!Gt@HK8AOE#anUE%DQ7F zY%*axB%F;2X9Hn35H?Ap=_H1DB8e1FNsN;*OJ>2v#rZMK0C@D~O{osAilKl&t5qnY zOn@$Vli$tRF86pgmS+qwpjEJ`F`NclR`blBE9}H&dvSLkt8cRu{P9PgLm|T3zU<5J z{y+FbeC~74;oHCU+c69yQWiz|Rj+;$KmKF?v1~PmW*An4rAZ`u%qA1^evcpgTR(z7 z{NNwBvUmzsJwMR#oYDb0JY-3r}PwV1Mv(!w%^jeGN z^!AEUbbB?nep`l@0DZe1u-S}Y5H2qduq>ynd#INpj)C3T2E#C5nPtEDlr!eT3=Y{f zby)yjdPiEPn5>ko22{Dd^B|u7oXQY1TJVg2%XR^Whpm zWNXJyuJpIqEAGxT9ADper1LV|f$4Ixixh_A7D>0JG@1@is0fBR1p?{Yh{lxbJZn{R zWmFt#hJisG1E96hPB&#Nj`aveEW6g)iWGh zRJ(bb-FxcertWT+L_0QHz`#J)F{}x;_W~)1iA9 zPxtK6F*$pQFa)iPtJQOBxYsa-_Ff@^wuB(!R4bSR`)hy-9Y4i?{5bH1b+&}vN1=_z zmpdDvZ#?P&M61h9VD`#(|8add$eWhv+lrucpQDND0no#TTR-)$80-*Z-3;}SkjAX( z@B`8&BX1J&W)`-Kw z4J4}+9FFBuLyD9VXt%?=zwS#ge)dE7Uw-lkg#M#*nw{G9W4}8ye;hCqOzNUNML#(m)Vpq5Pp zKo%B@1WsVoobZ}kXPCBIFk0pUShtcxa9pYtLqS&c@NB8PhCMx;jW0=>^0Xn`oDwcZ z!WIRX55VDox-6*k0cAO0nP=2_2G4ttez6?D^8r|9&^#kmO@A+o7POHy_?bniPF0*& znHk`U4H%o0@XjxN12&^9108c}8GUAm7irG2=*yy6gNx`qPNosbIw8>3r`zb)94!SL zgF1y$YEC>~;|^ex89DGI`J;LdfITKGg^;*_nJ}PWlUR#i0zB<-|G^&FoN~h;J54MV z+mSG#fRT~L0YtJewpKGp;q+m2R-x@RPt=%TuQqIUj_Tp*FQn4InZO7;Vol^w>YAx8FZ82>IJiMBbNGiXl z1XPv^>_zuL9|nP2%2L7I*3Yvdh!m7-(Q~jE&Sdmj8PFni6A&=aZo}L)4mW)yJkWhQT`XeykrVZ_ zt_J{X1Hb7_e0**-Pu7RtPU@ssXoh_l>!G!=@KN7=`^)D2>3_3Z6(*y}1ukg=QZUXy z)I6rd)VzMYHu!tKvQ{Xqtq9!iH^m8x6WJI~Y_clh_R1DIv^e$YuAkB;46xR>m16Di zHKjtY$8=bkR3q*lDDics9QEC3vmH|{jft!^>h4)?k*(Hee^Yze8Wwz=g1a7*;ZgL) z>w7|9TDu~g&5P`*UeU80Qqje*49r$vYVVDGj_G2KH7D?}5f=CT_uu;7DxTSxekQ5z zy%WKV^|-HkVeaANc+}%tTkjsn9nNtuw(VHqY_K*((`g!w9{Y@qHi4}l6nb@}UG{$s zHsKT<-P&4(c~v`H3GD}^E7Ae4XyOQN9M85n?e69VpMD4@#>m2VqGQSFHN3lH%q+a7 zQ>2*teys4bJEq`RSPQr~R_$^m*@&avkYYP4;nT)J;eCNGoRV)H%>-CF%uauzT;a-M z8EN>EO#v+xUX+xe%C)4eTgrOU6!TkYJ9ez2U92n=NsWVk6rT~qh060)cChEyVdd~9iv46Wy>|-(HmPlbLY(Y zr0g-D4Zs@t zZpDv{Sei43F>VIz0G>c$zs_V|`UejWDtFr!ZCndVso3l$jMIR5k@^n9ka0LHy1%;e zh_bXyl8%v|pwj@&UTciDNqo4!Gk$~~`now$mt1R8LDaOju64}yi=xcdr`FosvED|0 zzGXz58bIGtY7^9y3s>+s%Fx!SNGyK-*gfn*)QzkV?Obb;N3x(BmH}<02>QJ(%GB6V zd^nE3QTWkhtV<4f*QQgN2Vj()Jea42p-~L8}*;DaZ6g??m$5<+3(fd3~qdi`)Gi#~my#t^bXgRT1vA;thtkh}{X1Z9>=kH4S&+1v`a^ z6Mf%XyQ-i#?@;HA!!>&4j~xIuyfyqIFLAi7nBg@bzkZNW;>2+~*drYV!aCvwCzb@9 za2TwW!Q<-y6sHAQA;x%NF~*k)7DAEYRz*DZZZ)B){St5{AgjTy5%oM}hr3h)j=ynpr~jUX7+J zC8k;=8aHk5j(5BTU-$Ym{K9|x_wdm_dO!Z=-~L{F=6=Dl=-Nq3UmpoKHwlky6Ygvh zZf_DE+hp8P!~OO|xH%>y2C&=VEpK_N%(xm9#&aGU7$8YQZhNyekommme$}LS(?r zz4Zc(`rP;=Bw81e%NWNIPe1u;eACyz1GN@B`}{*JT(L0A7D=_$M^UQ|7A;&u+d)AF z7=gRj6aWJMYzgTmLZeRP2B9^l?TS$-k@`o@M?7`=+$6EmPQV5I9n-km!HN$ZY1P`9 z1hDF;m4H|6GH#5(neKgFP*K&W9KlkVU8B7zD6XVcw7E*uyetb!Sx|KSd94d9S3T+S z4boz9$snvo-fLg|7*Z-uBhG#`rM)Z_%giWqGuS0iuT{9Kze=+k$cJpW;&TxHnn4Op zyEnt!rWH0nfu~2+O;KWD;F3p(K|W(aMqyy2icErAC5_UZx;Ty>3JWx-Vw(XRE-|MG z(C9Du9Fpwyo>~z>H(?dYEX%TE4?(f^%obMMN|MeEf=AF}V zXs4Vo4jF^iUXdLTY>j%cvJ#3@MV7oQi#jB@?Sl@<8rbfnAmw4M*k4`=KFFSPU_kde zXPjT`Kq+IM7Yw7oWj-tdUtRtpQToVLR=n?64GYM&{)FYRD|^UsbftTq{BiP~*Qijm zy1@^R^`G_z>V6#`N4e4TnRP2s$4aK8bV>s?LDkvP@MJwlOoZPtt=*%DX1pL6TD9?i z=d~+~H!6`PFJiRUWPe*nwPxS}U>%NxR4MK+_qykLnSZ#45D44OH^J&g7SyN;@K_g^ z^7k{k_S(r69K3I**H*ON$1xv&q8IDv8kzt$uOfF3t`wUfoI7OawV}d}Lvc_Z?N#ON zWP!KfF>Y-)oIDcF*9^R+yUWW>0pkGwBUx{7zzHKg^uY~v9ZRVdRzZFViju8OZ#bd2 zf@az^qa0n;dB?5VubKCG(9U_^j864jTSwp8Z|`ceM{)jL@;B&qAn!tKJ?xzH+`#3I zE3N0QX9LttCF{G6m1>=vu5UepcX$~Gw(aRAizRJN$OD!$Q3m#-SKj>=eC(J13F>D*f?JPX;NhZ# ztlb#18tL1FM|Xrf+k_jNgqxd$$94%%Y!fyk;laM(+rQ%*u-$BRq6{;{up7(EK(+MX zm=?>f%&!%rxM#MF)6a2W6~TDR|hpG+X8FOxU_|_1<0(Gfa9v<+ZFs8Fx_{hgU zjv>o-bARtY`4Rlyzxqw=_g6R^uJDPEe*{1Jqd$fmXdz^9`=@e4MW`}1WC=V$;UD<A@+*(H-Wje z?QUEGm}+yzYjl!pZ58g-%|lgcHY5NO7#jeufASgRq3GIUIPVv|hdg6`(G2=U)7z_U z4O^owVDhOAeJwv6Lae^Uq{cZ>h@hJfj&%OQ+`;q*Mj^tUCJb1xp&4_{sFX0&86#CN z6Bd%a!^sUmG1@r|U;=LJ2GG0*Q^taj8L71ng~L?9%fN@Tc}ilPy8@oUM1iH)mzhfYLvWDz9{V@4hZ)FM&%<*@fT zXAf-JGADU-DHV@AdX7?nc`@gP@JQRgyp$r_p^c-|S4kw%b2P_6DHS=j=(#O-sY}s! z8P3S84>{xFVh2#d{Ranyf6K7oTBI1Z0fSt64L!l%E!Hgr=|;eVdP7dFvc< zIu}R5Uf1z^p?TVbX>KVs)I!_c4GrgH}E zGZ;q^?nf|M4c+0kK96gl#BC;B(Q=qZbI3Xy*P_U$HXhtvIga415Ndt?WcS(_`^O9h z?cLN}ZN>Av&uh&>1+6F}3BG9zUx|#$reS`6KrzgL~1XobTR;am#BP$&3y-i53ZuGa4(_idQcc;UWR?nYs zcp2=1=Qh%`C_UVrvwKg?C#}bMLc};+t)Fy!=gP-%QU<-!XH6>2X|m>z%@MeU^m+&l zIuw4dUld`_KoNxq%(5>!1DqM8mai#QAV}0bGC6C;9Lymf(}qGsKvG2mNoW(HSXr_3 zSB=zBdmo$OZs#3v!M*3S8N@-Dm}$)TqmO+CZ+~|NG<}y~ZEFO-gvnH8mYIb95M32!WHKvk7>-~4QkOPu z?pR}^n-!o~qjEMPcCfgdvke;x0lQ(uqqlarcy`3ytAq3-%UsN`y58cvNj@Kj94ZYK;{&K zEWKRuZ&HG{Ce1D_m2k-eCY~{*ib?|t4ai(DrHTr|jO@L-7y1|2yoj^H@ha4)p0puQbCaNfTDU>Emif3w*8qIWRm@p z04n0tO~B!5QHKVwm|PD!mA{wNabmK85~gVa5m0Ky)#YA6Pp;c|0}6WPiXjtj-#W)j z_h)1x5f8BCj<)1y4Wl0Jx$+;y!DlIrtVreUdsszM^V3BKiunULH|MEt=^!@L#4xkR10!N z(<@%ahotoihO9JcmyFO=FVobpwlB3Ew}>G-bL%%2(7_qCP=75BZtZPu{9wCVAj2fZ`H~LyLd=gj9>!!nV@bQJY zT2H3F?$}Xry6FxbubD1h!{2^~p*55+=p0?>@TA?dGK>}1I-}k?$?*iE8*q44Ka4fT z8x{n(Ut^qiV`n?3=He;Dr5^4a8-&eu69C=tv@6|^Rw&Vl5sH!XzW1h78bU^ekdMK( zqSGE;C+D#;H0x1p(MtW4FhrNC&bBOm@G{`Au?;u9}E#N|sbqt1JzS}-I+NtU@FM(Nc7`189n ze&|2=K|K2ClPI-CKLFrUAOADlc>V+6M|a@a*2@J%TH&UF#Emu+!_1nEpbXIzhln$k z`z86@;}HA&$^e>v)^I761xy*Yb|W5EM!A0fq4@gqR&}3sxq^>(#pK*hw;L))QdPRr;d*|t{b>OZrW(53q+Dc^NC3u+ z$HxJ$xpRRRKYdrPkvlJ{8i^?-aADu;+wdzE32YscV35g0;S~fT#uM8Ck8DY3jDf5* z9t2kjj0{i$D-9BCYrU)_tNRpL=8e{tAmsx{;<%u~NLlBUAjJupQ6LV1TnRTXE^zO` zT`-B$LY8T>!WLqbhm1Ls87-49AgaQ_bc9oQZQBVp9Z>e)cH`1zNp!|Gy{mA-WlcEC zvbK0(!kk8AR7|{pk+2{mp@Jw2%dQ0}GY;5-h5?%?V<5)mgZrRq1E!2|%BY;g*#rV0 zYov1`ub-1*;WL$$F_XiJ$}!h4=;S(X&Vy$;*b-aQMU$@ouC)rU)TcL-WC2`W?y;Gq zsyRdOKBt6z2|98xEG>$ih5_5{=+2h?vMtnNp8$-E%&k9*B@<<_6M!u1_=f+J)r`138zEm~u2V-&&+V>wnC1FM%n9YVNI#&UN%} z;DC!hlny>eggx(_-mfP+n#+6pZ&~p z_~@r!#3$|@@Z!r4ak$!Jf3?R|nK2&3a*_^&Q`czxw@u4RbA6%8Z9EzlcwM z=!3ZX;Sb`?k0-qP&CkFhP0ZBTwe=x5G8`tbfvcRb>BB(vOg|za#_l-sAOxO z8IUEd7BOGrgtRPay9rNRY;pJDeT+F_pz4vYrqn|e8ETn1#}cgJRO20TlmJ`|gb)1d z4u)C)?aYcnunnujsrBzB@I==keE3iP5bykwH)Grkn##-|s;Gq= zPX!beR_rSF8SxLdFF}_+89=HSH;s0(bA5U-7zB*u-o3j%MYGKqKz2t0TE3eu(5$K#OBa#_jYnJ;7km%S^7GK zhnNUlNp%#~Xl*p~*`^$qGsHND=(Tg&fVg7h11bmw35g4k1{7utyZ}Tv@Ps;yn8u7D z1D7w~N6HCH8Ze9*lw_SZl2pGp9JR{g%;*Yrt^-z9H^TsLk!I6{i3r1x0hS_|wKU#j zY3}Bzut})QI6K>*RL0fi9v6?@YPhEJTWST7WT&t>e*^EEra@BVnQ?V>(5y=GZQQ_8 z=+xWPKaW0gj(ZOlnU5;j151&@gQbY`f`~$KxQuZ~zUA<;RH-kJ2#2|1o)^h7(k&NB z_qdlWPGvA>XIt!c6Xu2S@WCY#u@|Py1RRzEN{kyfcNnJ;_Z}Q%uXxJ1y4tHO6nri7 z0x&DzU7;ie{OvyL-%)}G!U)E7)0@!K-kLD{^mUy6#3$-b%4<5izEf}5+mD&XolO_} z)pcV<^c2o0A04`}>m7+jIvSDMr!Y%l88;wWGcU-Xb&rr2Jzq0=v!`nS7JRR_@zj2g4<7-&G zBE22Yd`Dq%PL77%V#<*@oF;O1Bv_}oL>^>mwe~f7Fg+pSUVJth>apMESYdsid%ej7 z90IPiamVvcyQ{up^{Q9;tnWERACX2QJiTvmt|`Q$4Tp^JInxYn#Fw!B3Ajk{{f1AW zmjbjf?=5>F0NCuY_KXd{`l#&zVm+z=03ZNKL_t(X1HXI5;m~>`*X?1QBf}*GIIPF( z?FG0wAAl<+;KsKc7_0t}-_QZ1Z+#_CJGpy82zNhE7$1!pQ6q>@a8365Nm^|k8vTME zE5HLkb-d81wc)?h4MChy_8ugFtLlGZs9RdD_mgx=RMGKu=cKbDAt!*?w-L z`;#C43@8=EXs0XmWc^p5=zi$tH1MKH_6}A!nF7iL3?y6cRL0rkkK>tFJ%-nQ;ezGi zC0=;p9_~H7j~AYMh=(uj@sKKJmi3S~?htW4Lis z4C6?=3^%wtWC$N(NCrY+vrw3^J3N&_6E%E@Yiz*s?vAmrY|(_aUfj2p2ZNE3nDNAo zbA0x4j|0n^;vtEGL$-_$j4_b}3FC}a z0)*y#*SU*Js@nZZGbBsAnW;;0_Z@3G;ij)lMmsWwv>9+?%-9YCN?ouRQ%?jc(gs(S zt^RDOgV6(#EtiK&6XI1GzZg=FZ4#b*bdq#w#M%y$Q)9&$#c)ZWG@yCt$d5V20+W&z zLn-N)HEQclDZ$WKXCXlnW^Qb^_-s@P6h4kZ-2Zhbb~=nwfQt@us7gag;V-?n5JJcf zWJV?FPqp)5?YGIOtGVKQRAapYGfh(KqU=#g4D1Z4#*SJs=-$^eP2fBr4++~bf$9P* z2bAdyAX#@_IH4>>(%gOQ+~~s28Q!t=4^8g@+7@S4;7aGmqS>`9GQYei3W<|y$YR_! zz9vu_#*E9W1$AC9PJ`lA-C#gdrXMDzN36yug8tiete2y`L*kSHw0?z+{^+{R0otTBqo6S} zQyx2Kl6_9#`gNamSA}r6$pbWGiZf_1jvnw%uQ;Ww>rK`+cZ1}HxF=@Z8(D{Ty50nu zbIL%QEW+`9*SwFl4Wk$8FL)!-Sn6=ZXq6?o3?Os?IP-EL2q-)6eQS+6?!@c7dZ(V` z@c#9@^mzOK{}YYmjvs}4%&?9)WK7ToWiAZv8nt#qYw^$&9|~YPfoI*i_i(-+aLYpA z0Hn?yaW82crz%O2*PMFSs`GK2?u0{hwm8JRU+eaw+-u8H$OVy1wh2Y=aBJYt34(U9I+krxDxrYX-P>8P1|P8I2Qq$ zE}HTBCvV|>zyIf;K~mVa5QEz210a;ls7z8cjl@ZsytV#gfxArd9f**?_|&JK#j-3I zhSBjw{tr0hhE(GW-EucW&TG7*+?_UpJoH3?(aY8s%UV3f7+lt;LcEDFdkhXJf|Avke~1v(GyL zKIdGe_0+HeO@R*#s5>S1R0b{*FrAM$-;AJ?{SC5WL=5X{QS)T$&$X~4G}1`PWt70w zihHG?&{C^op?wrR*Q#5y>meBgQ0}%7>(L<+T6EMKXV-LkcZRSgBn3XiLYGqKvQ=yk zGaK+zp+c60(B5=DY4Nqnn3JS?Pj!!~%OyrCn2}}22~>axB~O@Z!ZeQ9Z3pZ&8P9&= zbJ%RRIB>#tGod13f4RrBo0@ELM-?js6^B>~pLyS%yln1C@PsQoGgkR>&I$9Os&A;8 z?Jkd{gt`>X5|R6-QE=hGy$2Y^jB&^~l;$;DmZrIp%U(l#0F-dHo50LC9A=bKJPjYv zlzbD!I;S}?9((i#Ub=t4IFPGAMGK|YT2VCmzuAn)gKVvq5~1k&;Zh~iol?SFD)t8{ z-d2kgmopdvk`ty82>ku$+IYF`2?K zMIgkn2FdBdX*I|`u~Eo6ttOtaicWKjend})!yp)0WCzy(y2&!``^W1(9Djntv%UNH zu7=I8^l@PbN7mTkQwS0+2f@F3Rn+cbxX{kIVY43pD;ytP z;FzZ!GnI9J0`rSmA&q$gwDKUvK^#HcXz!G>qkp*d$L={H5IDd$a*IBvha2CCZ8$IP zi|P5a2M5_H4{wj-YvQ`b`4qhv@p0u%1iKwnCGj#z#Dwo`9hO zOP~iwc#Xe7OJ5eu&I?q2m;o%)N-!mry&p0@ zcNh`y%$*%d9+8QhH+Y*CJ*Nav*7S9$6+t;cBJA38hSb4OgB%u7H-2aDCu%QO2R_wy zIw4`i76+bvyR-;;0c{kr4)zY7Ev>c%O^nB+WYyUPf6YL5{T;ggop7{#%UK~BjxNYp zkT5M*QaqA?0R+TH3(@T-k zasV`)8-Bexp`$EixEwV)A}OF~J06%}TBE$Ar2Cx9JEbTZ0gJ5PuFQDw@_pRAb4y^Q zIGqxq6!sK!qeasUB0XYj?iqdi^1>4Pl3SgPVDa#Bjhgm1p(oF=B#&N*S8(WM+ z#@!bldi0!<$lG!)1;ykJSSeZIqt6krzWl!1f^_$lEYqOLBt%8q+*xZkV zp_n%(na8!I3R!=?EXsJA{GU{rp!Fn-PR87Mxh1^j(=1caxoI3 z-aX_UwysQ56a9dt0|h~6N`<@B;MZ)n;`5e5ero#J8wkQTbjo$$(7p?yFaU<@5po=a zs2O|C`{Vn$%>jk`S0L)(6v55h-x!g}6i&N9kud_{?;0MfV}r#J^j4opD64**y7lyh zKzK*bG^PDiKRspnq*KIZZS7cU26k-F+A-X79M*feHEGSPK8km%@j4y=T7xOfH}&_$ zIW-*XTpM~*WI7#vt^36d0zhMIK4nTiaSB~FL5MHWK#;AUSGp&GqBw0ewt#f+rTKKQV z&8xEkT`0UhN?VTeOYu2KF{`Y^#*tF(eYkvxA?sed;nkf&3_4$vKj+=Q_{8Y zFy&4i8n8?##TRt~bdlazT0X*x0q7P|V5l(VZSQPej$m0O zBFG&36LH>*ZEjulF~H}JS(k5-IFYJZ_}uV?Nep75iUdhVzsSInN9=|@4u>d}+Ik7E zg8<`?ith>F(%7G~3#FNX7~_=i#ElU-4MNu(_!bjD+3}xKk`!I>5^9t<$##oq{HU!@ zR|lcepYudCL%GqA$a_+v9@Bu!!vW0Xx^?rzG&(bySmS>;k&*eJ>DM_CGURLg(=%CE zken-g&+KYOT=i+zc}=+D0Xsg3;Y)(s?6aivG>o`BF!D5DvmF4EH1eza_b_bFurNS* zz`PXjazNhhoFAE^Dk*Q0O5+s98W*B)M^l0qH8^l&oK`H&s*H1G&>-up$3fa&Dq~FS za39ABFTQvWWnQq|O>(|eq=6j&IP}*=uTIwa#SS3gaG0fndSTU})mfnQ1Tx(P5$ZHpsm)DmxYvhh^{wf=CPnUW^JsuoIUauQR&>B|LAB-@KwuKa^W2*>04{`2o zo+7sTdrlc^+~4O)nJOBrgyjtK#2ohH4lsFTaJIgk=#~#N7~#GS&imQ!R{k0K3!i)L z8eGx3zYX0p$6YmMk9P0s9X5mp@8j`0m3UtQ{|X;7M9dM^=p1cK;eCD_Ug`oYz%M@2 z_qX+f0vxR`*8g=ne>b|5lYNBc@-_SQy&pWH`9k$mY_-aw7 zHT4Ut91}k8MSg8wiFC~=0~~1qzAm1VY=cC$X4==D!=vg)dqM`-j6=rn{^>{Y?)OZf zJOEjXP^F}sY(r`fUNHMXh7RN$d(@Mh~W0lMe#?Hd!m?TcTB z5B>3rcn%{fDu{qda(t1KtxqOYtF*rWd!0Mdh^0jpGZDrFoM&K@2~#2*3gdNeegksK z@HFl)_3-h*75GT>;R(mh&d_ipINsSCZ_j=DQ~1EI{oi=^+g^{?Jozf{Ql!c=b94Ce zdLBdn3{5mM!*=zEcL(m}eIR1kzPWbR9WZXNvlomKnyQVrljV((;)_N(r;NvT173VE zBGcl_`aqC5G57$wEt++;MO52(Rlc$Kn<^1*oei=#yl!=9TektEbsq$W0NFOG(Sof@ z*J|&Hd|kIh;*(~cHm73K0&12SGj*8)d`#E4O7_V*_s3w#&N{0TW83_fR|pp)abd^Tp?hdE6P$aOapG7?{HWcTnnbnfXk~Q z#<}t-u8d`#g?`K>zSeW1s?`}AaFM$XCpP_rV>4a5(XKuUsEP0h6w+`f9>fr=%dfRA z!)*)}7-{^Ud<@;|5p1F_1j+00ESZ7vf2bONK-HL-PIT`OI3DeAy{9cZY@&i z9Km;mNv5D_q&|U=lckN>Gt`8YrY3QP5TCARb3!z*+1&a&$$bKGw2akXhr0oej~K>=wrM**iAgKEo8Ou=Uy<-l`bOFsODu-@JY=(9g` zeBROV=&F8Jliagj{p-wp)ruL;{xa(4d}WAtrtP)Hxc*~G&+4MQhG7-LBR^?aW8wvX z7g;)f*D0roo$9;R`VuoVPW1lz53>eYubl}1GOj2DUFnJp_7vREpYErDoF6t)G6@#G zy1v|^)TGgK?{^Aq!q{@ae@06iB`eV+SZm8jM6+wgx$n>27-f4yq$PqBGSd`yi`^uX zO+*rrt^ns|+&6>F6i31*Kk^CCIJRhz8vr&4tK1$V(JaP8SJs{l5eL$OrImfAw2&e)|TfR!Pmb^|^}ZCV$L%WFjuGM~8EQrZ|WBU@scPZG5$M zTdx_PVC6Ly#b;|H!p8B7RaR;SOiJL*gge_A=jjUfhlI;z(J_-$v0iKsLglMk;LCB- z_?H^X#=8=PHs5&T44agtFM;l)Xcj3gYg#*5VK_M^%1*CFyB0Z@KgszB5ju_LEL zg6&7ol`+$Vp$6l;!*K0D9;kX?u?h(u66EVgiJDpk(gmjLEA!DiL5R_|)fO#o6 zTt2|rjhjd*VJQ_$Dad)mVQ*`q2vr@XiCQq>?0knrgn22L53|Cswqm2qDnJCcasuw$ zxxh>Judv-@%yZSs?OGU4qvy3SE^bU1rvc-bk#oXf7KB+!5t}MWvA7sLUk*j-L$<$@ zsmj=$ZE$hE#pS->!Tn3wJ}wKr)e0{zckAXBObHL}?=g;YZGUxe#~U+a*-IL~iE~@l z7ICxF6(ZjBCr^C-0<9hMyyV+XluHxB_M?Y7&ahTaww@7Ney*Nonn>( zqg+FZd~Fd0GG&V!0E8B)8`yf}N6Mmu)gcZupxj~LYEtt`M~1jYxFE|qChG#q6sVvc zt$QPA6Br=({ekyt#h3e={o&K;*@*gE_j`@3KF**=o$9p>sEso?B24jB!Fbd}6x^$M zyr%a@V|1s+(dLV5NUa}tHs5IMh^~6LuU>!T;cKI5O|@f%k3VPU6jp0y(;c*STnKf_ zUEkjpb+XAZ{Gg+!n&H^vS-b1R=tKLB*(nA&-XG&sWNtCTz?wYztX~WGT(@<_k>)6z zZ%|*a>1^$hKNlyaqc08z>a64Lo`&~pd~3`-zPLG0j`1^MO*lHiG&-F+46XgO_cU(S zzr)~u+<$lUTw|R35prMtDMH82gV3d|8xTBYR~@)ueV$1S2vGC4 zNxB!fsDbXr4r%lmJxw@x6fP-L_YT67taBiZ#u?to9d5wr!Eb-~GvNI_R}Lv>P+2qz zpMX*{dYhn$*UB$-?kYsiS_hKt@rx#xOg=`33M)-o|02G*p@N3ffy&zwx6odEya7Ys zt*LGaAY^~HrP#Bs;cSL@5TuHB(ZLuq%m9R?Kqe!r<91E}hK#h`fHouk&R_liD0{P5 z>$dAm=o@pcwf5ep`MZ0suN^yyN#i(iFhs;@im0Fjgg~mGs5gYfOHf3;Q;#4~AP_GI zA%R5mz*9-}0ulmIRZ@Wjp;Rd;NkN*5(;3I*YhT;f_is+K_gZVt!NX|gm}~Ff6#L%u zpM6#{n=wZ}#vJoY_{)Fo*YS-LeamN#<(hD?|eV@ix;shAVtRNK+0nI!Ul zoe8^xU2Y^aP(j3H+fO-r}4&*!CJ znlkqL3DcA@3|Wp<)6*H&rQ-T(1VfDVWnKVSR9kS*@Cab+4-=l;9*}azn>Tk@=S2!O z4oM1pFAGYQx*pG;--uX6#W;?*yFY>Js>j*Mnw5D5zDg}qhTkYuxH!#PFM1KJ)#*pg@!j6 z7bL0v>5TlsTsyq#kRwD?{V!^B4I{f~wEO(Sx(CNc^8gUeS`2F!XLW7>Y^F>Nw|e8; z9te_c!!$0nb^g&C)FARb#}hA6gS!!UR4i0x!<6Hy)kmAb8-+Vv7~cg=YCH0{IGqt3 zJkZ@62x}2nGsfH+Ok1Sh=-5#yC@gOW@0lkuEV>tlx-e(f?=GvAw%&@`Kv;Kce}e~5 zHqEV{$XB8fwuSa~e%yU&u8Yn@rX~=g{bP9Gqp+nE2Kht z36VYR&v}~{e}nfK=>y=-mb7)W_o+*dZ-8Ozj2IuS3+_LDg{vnA;6!3zrv#=9K+dIR z6i0s+d&B1=&ni@zP!k1Sq5VZzM;ca`a&U@S^xDARBH&?7L4C2xz@y2le<^q10*T%+ zkTOuKRH0YAV@8XdM`Yh{2pM-H`e>{hN*D()IwgoM0gc2Z8t{{!|1|!qf8(d|U;Oo7 z!ykSm2Ep!jK&FIDz?ca`mZp)V=qMQCSUD{n~Tg}eO38^w)jM|g3)!?Wuh zh_a%vQ?HCftFx1*MH9SO9JOn{okWd$0{p}Y{{*L>$o9%9|D%F&AJz zGKM^Y2EuMkxcm4uI1i{uNW%{E@r1-H1{82UAnA4{A#W)-meoO0^_+17h(v%D7k#S| zgSpCf6|>gK))I0`1tl!?+n9EvVXf&93M~Wc#*XIVVcySU6FG}W#K{3pIzhC-Hd5SnCBJ4sFmAUBInB@5&QjN zhry3klZ=8=tDGj0r6PMNjCo$gxGxp!yuv)Z#!rTEz~O4b&Gilsj|(0ijshE!?H#qU z>{$o!;+92X(E;*oECE7p z_!+}82gzo7o8HhX2dT%@7*SGF`K+9Zz_44Eh4QLKw+)69#=BsJkm*{N&Da9Td|OS8fN8ofAr{T@RQr@bOi0-Z905oPU;M%k;otk! zYy6M@$8X{9{mzHDe{+xh(*bG9zP)Zpj42c5iEvsaJ=D_N#gK2svz7vU^KQjo`49e0 z40+T&n1K-0!1nJtLos$5^;_h;H_Ev=JHdFozsJ{p_wQr*hkuAq9~fW!!e>A?H?rQP zwqi~Cq^|eLAZBlp}p9I8c^d%-VLtBCFMeoq&0A_GeJhDuQ3f*(wI}v($ zMn2{P5!ReBX2L{_8DcCzPdG@boK@RLY|5I>B^hIMbYi@7mGRz-Cm3@E>2V7Ii_ucn zlUJ@*B~`H53p%P%uRO!%VOv&6VK4X5wqDqpn}Kg><4{~zx?qlTG5qGvc4|czircy%62H06?PkrLS)O-g{55tc-P8!HJLusr+7S-+7Vh?I}z2+)~?@wFp8p5&Od-8A9;{sh-Vn8Br>EGIcaRB@`I~h%%~kDPv_8(1En${TcY2$eMCaGf)S{@<2Dk-7aqtH=z+?Dj1uXaJ ze$*J99cj>%E>ohc>waXcV6oyCohE4!e7z>JUfbjrY4@WkQe%8Eir0*_*l!aH9OmHn zo1E(aE%##DU4Yrf=!tmNCIM#0O3k`TdLsKG@@fi>Mo}B@kmDzqHooZvGk+!?F zazaO7G_l2SAB}hPeHx}TI_~CrVf5a6lUtiR?ShYY%O7<79QhTt4fNyh-s?P7jn3Oo z%XsKQH$i4Z5ZO=L{-g$l7FDw7IYE>z*%mHHX8U1{=elBG_@qU{?6=cvZ9D<2(Oz|6 zTM8qAbf3H#=^^D59F7lx%d`D%#BYB2kMOfU{|hK2#yO_|gG3-s^8U)*nx7SLQ=_Fj zM>H7sjqeC78Vzf7aDx@jdp5>qJKC+aEe=(A)(hEr8EqR1$=-PzuRB_#{hCr+5X%~} zY>J$`va~#Ga!@kqI$jh6b;=~qYQzvJA?1Y6{_v;qtM5O@&;Qu(;&1<7-^2&s{5B4! z2TWs)dg^D7CA!dOpyKKjCn5LT5>hlPn4#<~_vlkl=q7{}A1 z$FWr`^AZeMadhks6K-xMTpuR9x?k}4a6-~^(^F1Z=2fzDQ1RmV4N}TDogOg^8A~Zx zmqqv816(WCWf9nAbI`I8nQ(^96Giq#3tZ!n;INi?a8J@F*8tfACv=9fyQlIGFHD3%J>J}@NBVq zh@UY_R}2~g`a=2NJ#ud4r+{$NLF}4)le3FNa{oeHcwbA>cY;B40Z-35kG`i4H+{^l zfi4PY0=f;MYq6WSH6HphaewA>+QLPz@!70mpR~E}i{F4_{ZV_zQ!(QZ*!nY~Z2%T{ zw0>mW6z)tN41Aukdo=J}&9 zjqc8UoYWM_IG(V4VvGff@OuHX8-kqme?CX*uZIEn7MBfS8yi0}XfEI_hlxaLzUZVo z8&??lnb0|Z-r9G6$puh!6z5EjF06(2duGf<9Nh{|?wW8=62T9ZCcw@C*YkhescT{} z;y5!x+LlDit2$@RIEOkIY`!rNExSDRqGgAH>|kr(hiJy2D8gzq)#?$!8pmK$VXF{r zQ`144qgcDLZLE1n`08)`4)Cj^?sFx8GAJ>?tIRtkF+BV@99ztw^ixtpmDQ0Q*MseU zmr-pU&;+>2XHeS~r1Jw&v-`}DA`19xfF>{}T`S$!No_sZNW_;@->b4uovjbYp`vpO z4hE9wJfp1<8DiMP6F{1>o3kD#r|`A2pNHp9@TEWVGx);iU*juZ{v-VQSH6v}eehiz z@84ipX3#*Gk`$#HMrzx+D`Tw^IseX^70-U;`|%(BCx6xH$nekQpj}f~RoiXM%{VvZ z7LYXY*B^fuAO6W7;O?7W!R^C$@qO>z;wL}%3FO@;;h`_qR9|h|f*tICD}2NH0o)&l z2Xm;{l8NVoQaU^Tx+!50Jk;+{TH+e_nM#DbDh@Kxwhl2G-nuf|(pOlE8zK>~0&7mV zxf<~CL&Ed@g!QFVXHqh4 z(41~0hP%mi*fDGROqV&TxM@FbpmZ|R@P>7qEDoUn+xDo9uch6#tsyZ7N`8#RZX@Vc zk=tmh0;sx9RoAURoJB>QMpgN&Btme&^M)S`2ImQL1;zyAk3KP~e zAf*vl7v#DCMGe$E0VyF5gG(<@Vb_#e8KXFEC`lcLAq#FfT1nYg(YU*4?y@Z@HXVaUiiV_wC0 zFQsBx3$AZ>0D$9Z#XQe~SMw4S5XH}&vA^2k*^>j-3cP-O1alF`LY8ew>$;#;;Oc6R zo0|h39#<_smvDb~M6oSOm8Jg0vcS%qHy$IpTH`5Ob?403j*4Hqpu=aUukNkjRE)he zJ>u+^zZR6x@W!SM3T>P@6WVou=Au$ZKK`@$`Y>#LxXDaqXvRASSAe_0QZvjU`;){g z%B=_MM2+(l_T#jOxgN`=0uL<3=#m=QF-6Gm$s)iGP=b3e8Eo}HgjEs)MU$u)V!Rvk zvUxGwljOq6xwIoh+47{U{MB>93-9zWcMw2l6_q68CQo0<98 zE)Aru1nXoC*T79FHH7F_T8O6~brYD>%~2a|(hclJV0e?mdK3K32sbehC|+p*iU5hD z3ez@Rin!W8s}DO2`sO+lH#F>!G{#JA=H2*Vun8E{RXtk2#iJ$xH6~LI5qE_hB``I`&d&5A1zM}BZR2jT%lcuPqit;fMCO_A-p`Y#9u8_`jbp-- z_g>(OFMbN2|KdG9{Mv{3@&_N`kG}OWUVY~cP7n7u%`<8#?m#%MjQbPggO>}w@aKLC z|L(v4S8=t!mQ-6?aJ1-KVDU=djae`x;`Vs|2H*PXALHR0Uq!w97@wj${KUIYaQnG; zP}3*8UD-;@x-?rRuwm-)UgRN~9&FaMMY|(&x4I8CR9-h6^ZGivW~|r5XCTI%x39&j zdhYEBU~niGIR zjKg8TkAC9NPAK4JtV+LD5Ke!{BIz0CSu-QuR>9KREn_tDpGHP4>xrGOw&RIq&$Rr0 ztw&)Q?@vkQnaug7{L7pnjUo1Om()l~WX8FrayOlomDWlXr^m{`swB0t9{EE6H z;kNyqM=VGfhlDggB27C`&e$CWV3Eiyrac$~h*zZb2r4VQ{zc9pf)+ayC{SUxtV5$4<(A?Vxj!6rW2eX~DE6dxvR2%Hnt_>x!~giCP<90Wj@GbzT9r z6x>`*n3n>A$#2Ns?{Sp#$RWp;osLHwAC8!IJAepfT>(7}+qdKbI2?Akx!L2pA3sRe zgB~lluDTz-RIdPkb93<2_R8$VDa*3rFv<0`NKv|4#0j`A7OB^@C&uCGfa|Lr#%aXI zuTEG_C(XE!Op;|SsD+RcVfEvtlz9G9#k%&>M6^Y~K*)%ar<~dBNc=b?? zi)74;E4MT_PlE}{q(W=E3?8*A^S5TfomcM$k%d{v#FVYKDC`C9uO zZ0Y>#7I`RMzOZJ(yjGWeWj!gOVpF-u|h`|!!11GR0QavzcOT(dTJf3Zw|M%A zXQ1!@6p%8KD!nP3|CbQpZahB7xaPoOF=XO)LT;F8{El^BG*;sih7>`TX`hBp7SOGZ z7y2Jdv$}N418%P+FuAEw}&72|$gaQVJ1t z+ncJ<;O1C>A-$h#y)G;6=NWrDn_Ztts>8ah;x^|dZ#I%6;CLn}s@ECk(0Z9_h_B38 zn8jFEqkOIMQ>q2knxXEaV+Qho83V>4BQHmR{j^8Q3At9hdifd{_gHBJYQ<0&aG9Ij zO#^ZwR9H=bMn48u*Fhk>eV*5_qo)x=^FMLpe;_yhQzD5b6M+jO4+%paFlws!x)v}O z%*RLE-d+ps5uqAMu|%@du#-Y)n)PAMf!%%t5n;CRtntNrRFtyQr)yC?%A_ zc>j}6ad$sUdb#4)QsiuLt`*C&pp=3<47k48W1L1LA{>_@JZ6>X`PJ2g%E0loV9|>0 z%W;l%6v36{JohQ%`g+2=RJ?if;Iv0XD3!6!1q{ZMCs&xJ3HJ}PY>xnNdYt8`IMOUO zjqWSP1~lZiq9|{^UsfpsAF~Ax&qiI;slwNI{WYC+r4Smqsd>gD#?vu%i-j<|Wz zP&1x@a^chFZ`(*WTaiq*F=OyO9HH}~`|c`>-+Wtd2aZAA=bj5=#s-Bn~_-$cW1xMHuMcnpA8GxL~8%QoL7`4B9n760wlf15n% z6zv`qn-_fUbha+%-^pL`RyJTeGdg`4J7B01(pDmJjV{9E8YLXwuKaW#Be}?a+MF&e zvoq1Nw$M$|Qg@>dCxRzNQvEK$an}wk<_L}3ZLfMX`na4lu2Alt@7yQc7EL74<5UYxAYL(aCmBbgS&M&|MX%A zFdjl^W7uL+TbCdVOnDAYCD^)zDRz#jIGqjc+d{T=YQWk)*Bl^lWVNDjlnFE>F^aSP z_Gb}h31c??e8fmAZpVbD*L(cHVUL^JYmD#R z0Qo79M3E;wb_uB6e)~4ju(GzdmaE_zg2Y!RRD+e^2nFXywKb18ZVlL_fM`a3En&D} zhwcBh6A>6%t)0*cpmC!eCu2g`%-rUvrZHAEN}o>|-fjO;X7# z)}?4o0x4RzEERdkSk|ge7e!E#ita_D*y038n8pDwUp=5M3-UC{e03jrt>Sc%bo`8` z&#y2|BR+opfKk)@OSR~^{63u)FcY3WyT&jM7{-LtN&57{aMvS-Az@x*&%0NBpI5cd z7)%K%6Lz}^w>LYal=0EWkEmq@fssf~zF6i33=yC2zVih0D$a}je!$)R37Wd5=v`4x zv#taA+3v}_;I>9Vghd=;!}T-y*8bRvUcYs&1_mCsk$Lf*tqi1~r~V^+&ZVp=h}2zT zY>JO>`n7A#Ss3&>@1!~XL4={Qc=<`0Zc2>_Xy8H(^o?Prih?~SC@c+E6&hA>Li8T} zy8xEnL~f)LD6w2mfWN$NmE+??-2k=(yYX!b`Dw;07_9F(Gi3u>eiaiQ3LNXncC%3wM8#D@OiU znD?dk;wL(r+T%8#NIe%`?nbyVHcU}wi{{33ce*^7GeW0k*mPY5(xkuyp#7lriGEc6 zyQ7(K)tSqgmrb%KE-n){MN@v+%G%Y23(=7j4fhyfiNC#7<}joH%Ppe|Yq$ky-)DPw zOJ8j*BaXu#+%odmuMD+OYR{G7wC)kk3qrx6MVhhVjJJ)-e=8la7hptF#x4&pjIn*+ zj)VJu;?<6?IK0oE?fYr_K5Z~P-8&9axZSb01_x8aqcQIeI%iKI>S{!;*;SG;O2ClB zKp!aV!A4jFi(ZGhSmd}us^3~PPWv#@>7wG6)nx_XOcpTu{arri7Bz3@bldV%+rLc^ zrQQG5MhJb{umRB{eFA7^fGY6&U;Zkn&hl!uu}C7Y3o$LCX8gf214901=O{SyqS@q3 zIC;h9&|U<6LuUB#Y3A$!fl#ILx&f}5yS{+}M6Ck0-y=M1lHv>XD*491b~x78;uWf9yHDv!Br z3sE(r3152-Z5d72Dzl7BA)rr?q`50EGhNV{2HG|rAur8v1UKHyvObbzBWcWK$aub+ z@Njhm9)YVo;JsVMSY~`{k^YudbVL+xU`aj@;<0Bc`R)7t_| zYNSVpvN-vi_%+(7*rc>i3BKLU*ib(ncH^8__Z!!WQdWHP?hy$o6o_Lb|E9}SZtdE_ zynhd#001BWNklNl4C6v5Fsle3$)O&SA!tg6$oJNqQwi9Kqh;9xRV_SwzmbGF0 zu*t+$Sj>)Zw3OiDKc=w$rxVt}LAok*MWi3+CzgEmAiGc6+ z18#2)*i8eLrC==;r7)I73MEdXoO^yctyossDlwmCWfN60*Q%tpPt$;#>mBZoD;^$B zGJe?yfl?Nf!eB0V|NR#j#sP2cW(*@?Ss3$4&r(-@S{88z0OlA{n$V*c8{%Q}2{I-; z79CEu#Yz?i%qaJz;?nK7i8QyR9!f3O{>o6I|DMDaq#AvzeWAuaS*=9f#HJs zNVz?A5%19!TAm?q=WPhyO(QC85#tz}`81*1e!B+Iu17CD!4Y2~akgJknQPzO`S9%h z@8R-t!S6%lHu<3A2uv;*eX&zErN*T}7*J~|dhBAvY^$nV9IL*_;II{id+8p!z03ew zi`G``Bc}XVhCuuKyoH{JS@buw$JE`6?NBJh$go{kO~|%F0`h*0JhXON%=oP%-4L{Z zt-(!jx2?Mw(NJEYgAGV-9LD~PxXkW^Y4Kfd?ZQ@i!^peNx)Eeszb}AN)hyy!VcPh$I5fg`DR%ai-dUHIp*z*wKc*iu*(enk5>IQAd8(nB%*h zajGzqud&Q`)u_HNGt7CwT)okhlkBOl8;;K0whAaa?Y0s)>?=4Whb5&9V~mc^V4Z*1 zCK4PqZEhOnv=s3$UpKA{&)pGf*+12*arX+E%2XW!V#;-8F^1Jak+~u}jiQMVbFMg6 zpy&PAD2UO~0^@U<7HrO%U^sZI23NA63pd$m_-v`^zAOwbAv2<|qhDc0O%&#;29@z? zi>h^> zJBIQzNnd{X`VL$ro!XaM>D#$(ORwzN9HYA?OH0nG>=sw{)b^FnjdL;3#qg_Yq_g8w z&?ypOp#j5?FjU=(OavbH*iQq>@e#+!Yjan6*n>-hPA^Ug?j(L=$6iX>cL|7|hw;zxPsWQJ7OC?{i%nPR7 zgq#y{lC#9UntCairoHfG=5WMk#V}+!HDVa>@poP+jV0ui0cO;)qEvP8=@D?xp9_3W z^AWpoK+ahr_!>Q5mQ}{a70;gEV$x!Ek0g5zlhs+?VJ=bn?U z{WOh8L&no5dlV+Tetnceb?TU8u9%OKC9vC#c=qfH505KqT`>$J-rPMl-Ge3bhnEEv zZJl&eQo|MwDi#_p2W!R=*m$_VXrFVns=pLE>b7}Wa~ecxj5g4a_q%D|@W&ntBB0Y5 zuz9a`4?>7RG}MjBFwNgbbG}gDkB^(0F4g~V`@Rvz z9n8glq3=X_1uy}r0ZKMGUe&Q>Z2>lMtpk<9Xn1R05wnvKD3(+{t9I;vF+GZH-RwS`+UH?`XxnUlRa{z7p=2)-{fGj_) z5`23OiNHC5Gl7#-B?jd2Q;-@ zp40ubWP9YvaiE!@P~}(KX10S?daWcFjHHg#E}h&JBNk(N90kA;%Lrn)I!&jL8Vyqb zBG$T7g)^xUY)(RMQaN@PB5x%O?r93z77=EgrIa+ z&PdH7tIX;=t5x=+8!h?J*(2}{Ps0U3Gaiz%eRf)
%p^*dbv`z>|^i`4QbH~Z>N3k(k2_3b2Cp-Y_YbJU0fn-x&*L4MH?{Hh zw42mGt`gxUn=3m!8PxF8={4XSYifHZGb6)H7>1$YxiW;Co4pv{kd$sYjtnTJV1L;A z`Yx+%KRF#|q(qpeLC#9I^nY~>fPnpe!m_NGPqS2;R~+NYSeK$pWRd)gci(%0ySpP2 z2;($rO1tbmFQs5UEvRe7)%6|+J70a4==i!y3VTj0V`AWRnz1gjcYHpc3<L%?YRDQD{X^P+3k3KoY@!|5MKaB$RbQN*NE23rbm4elao_JkQPt2&{Ef z8pk4EkDd;U>${xcrYlAai5k~d1@!BFzY867O>N20MUt`2(DwG@dI-nA`z~mEAzlkA z$!1vX-sVjY2%rVUjHjduF~F^=3VR}8AQ8aCu3+}qY0s(w<&02;FxVApGqG$=-B99O!}o3;rUV$m za%;dVz54g|P+Ny{l;rTQk7o;>K<&JJZiD=n+Ye23%FuZ~!f@z(T=w$zDxE*J#h30H z11-Z^owI8a%apx5{^fX8*v6b z%nWCBDCm$iu=PvM0Ds)L@RqaNbAf|*Y(BuDA5&AtB0RRXb~oLv_~`IaZ`OTK@9;X#}kVoqDH#uDN?=9bS{9Ew_|4z?J*3@U_u^-OGKjQboZ$#ru0#V+Qvg`uXvVmBAGkwCa-3yd&@O_} z?FVjsHiwzEVQK5x0oTR=_#9{);V}Shivcm(Yp^iP;xMUV*I;hzW&rb z1LZ}Io4zL840fP7ca#QtCz~nP3Rg4aQgDQHJfPu_A&;fBH%C!3=*0L`$4CmWq%anv z(ygo_s8I=x%A(;(t=&;(HlHX5&_M1*;z*ecqv4gE%PvvZ;0okMC!GPOg3SzZ`IFmE zHxT)Qlva$!1nxw?bN#f+3Pru{DLGq(u208CK2 z=@ORSUn)CEJDQX1E%zdJ#`Bfta@J$bR32Pi?U08-Va-_9Rf_f1ij*?)kdc$5*Oyfa zK^~6_uCEVLC0+M~Ti-y%FbtrSq+-1YG^8P;Mc)^YM&4@$-hJ;0=0#H2uda3y*lccE|FJdO_4tq_L z-{bLAad-C!fc*%*buAJ+T?^j%#4WB4d)z-R>MQ})S?Pxvi5aOD)UtXOipVJAC)HK1 z@T*5mL>(q*Ud)}bz169V)m>=L6*kXfi~5YSduLY7VZ;46x4$obGjVg_nK-t7{EWa! zBZ~=bZ8E1e;~A;}hx!{$mvq&@l^dToK?DGVP>N;8v3Ocr^xO>LtrF2j!z3wYp6aLo z@v4?Chl7WWErN)^6w#fUWKe6$pej8640Xz%wXt2uD6lqb;!Dv3Uay5IUsUK#K2RH{ z$crFwgYfm-v#^=a`s~|p>3?y*slP^Xefh2Pa>jplFA|W*8XzNgRmZ_^@Cc62?(l$OAcx^WGw2cHH^Gn7S9g=d2um{GE%q zw^_T67d5`#qyAg2qeWh$cZW6IoX>x!zqWoGW#<=FenXGr(F0kZ3cH24$1{9!*hdUYgB_G zAStP7NT6W==B4e^RGp$$TT6|w>QQZC#I@tfEWJMXz1xlBMjYLytiWzS^_0aO?aqk#f0 zD@rZ+lkdF3m~%5WS+;r>vcQJu%S32<-<`F}`j}{o0$b6*x2l?vzN}gp&W&?TYcI7$ z?47}c&olv?K=Tn?ipsKzygQ&S3wS-Dj0emsVH^NDJ)q7f_(>08t;g#FhAHECDj3E| z@WC=+D`On8_n#D3Qc76o739W#;M+}$1XnADA>rm`k9C#rMe(^*2G@dd zn*12EWiFtUaeO=hU`)FK$CIRVXXAN^P}Yk5^@L^CVejhl8Zk&x;u63`QxdM6&@rd3KGft33eSC$AaAwNxC2jA6sM>h`q)%Oa`t(>~+*^QTw~Yr6l47_FvJvf<7%-j!)?c2c64sOdO<21m>K`thwE99YPct^ zGpB=~hP_;>rUdu=@AlitiHPQ6u;={E^UBSIp38s+ppifCneZh@M~w$xF5Kw;qy5f@L3y`fpujZk1NKJ@ME9d;pTS4 z%exc4@VNv2)nEP`p1pXEc{$>1U;PLleDe)X4-5YGuYH6+`O^~~9~qel!$^&`BXbC} z)liBW0NCj65_8|oKGLqug!)?zqbRIl+&>vo+yeoMlGyNHhu-Md-|hJs;(aoFht7+A zL7R73anX$B3f(u(U3)}m+mgKPHc^YnN+2OQJXr;v@He=TVW&UYb;sERkDj&SbMl@m z5o(}>C2jNfqQB;h5QC41@#5-$zxSKJiJ$p}UjT9jN(M-kU(v{cD8oP-!Vkn|ooQNf zEE;EfCGCA5BM20n002qB81n)oz>Pxdr&LhlHZBH;Du^;@Cr1F)qRG#CwlvoYtOX9_ zV&5Uo0u}IPP+KIuMZtA(&9RUJTnx@;tVYjt=af4C?oJ}bsjfDKqp>-+$?=Ma)bO!= z>fDr)NPD*U0rvKc={F{KN$)k8i)Lt>lgN;>YAIeT;;U<8=g2qnoY3J8f>Xuhw=qC& zc+{f3)T(%E#MUB}e)LwAwXW*GjFEG>A?=!BF7Rpd65i8f52qwJkq8M{gB2MARS-th z6@hiv{qAx$HeptqcXjvhp<&T+w6|N0BG%NNtaFtyPXSTMNGR@=*=GM#dG; zW2~ejCh`J>q_l0K1YjiPQzqJ3@zlu3wv4W&Vyz<2B)hfkIgi^y8@>o)NU_6&p)9&p zMev8lz0mq{L>=}xmW+fIJ4&eYNm9HKKFrADfa$QuvJ}`^$QBbbz1!Yv;*61V#yro+ zWDatF308Qpmg2HLk(?=>5}~eQL|$D@AWAsRCtP12a5~K>>M&#mhH=DhHvpt(#FvW4 zha;wGfEP6DHpfZcAw;V|NA zKjF>Yg2xBhOD>rOa)vt?DD#T%`@T;gr;IlbC+rU+j*knD^T|&R%3!22>ppryVQyIv z%AZ;-jmQU0_oFR$>b_C`4`N-AXnePJH@^dW@9*d+RQAUVHMc7-7|za1FX)cepNrto zQ~V=}sPj+b(Z&b2DxLHr&IgI0CwPjSeHUe@&7MH0)}RjCGOk1iiP}p0Y65{0wKO#o zz}NsfC{eDok@U_ww(V>vEcsdSD*cC5@3CXy8Xq!21UDGi=Hdt!_VD@THdh}_H~nBB zSYH%VWO`J!!HUq@F#6c;wzKeOH%^3Z5u!gG+%^}S7^uNAu4O>j3$LAjsP!K=Rd%`* zT($4ov$1u>jW)z|$t%``WgPeS7%ca!!kq}*+HVBK0e3FU+S-cD%9+7-Oo8)sGZ2a) zm_M}lL3aJ}1=u^$2Us~xTwrrfa}0xBqe^2)0Z%*5vtdJlLEC=){)zC{cFGqnUzpmq zt8lG()OFC8NJO6qj;HWWWtOU?bH)4bCHymg<{5tJU-$vM^ZpaDoAMQaf@L{^E3nif z4!1Y>!cV=6pWuR&R{YYRe~qtwZg$CgUf&B5TZ!wR(7@n9UrYeF#^9aWf2hg(rbc$H^?NlMhB`+_tIw&3`+ z@dQs*RIaaw5x@5Pzm1>&=YA2q9AS|=7uH&hRs60Nr8FU6@d zcE%eBUcp|+OM&@0E^&}1wYCffom0<^bgZgu_FS>2KZ|I%Jks$rxlM*$4kNO$RF=N7 z7y#W?pinVihN$bv%~34beIb3?Gd7fdg9E{lPuHItu{GupWrorETFqu^JeA;4N=w!- z$C*dY8E#-eoilEzQ`<$+el)`84h529LK>`}%Y4!b?02ERGRa)~cMv?w`Q3jRq>h`RMu(8~?L*jGa)%kfpF% z1u%^Rh9PT3b;)|Ewq=TCFZMKM?4}XRDn`I^TENU0rxB$}mXB4JxAEp73!ARCwiV+z zfCxA}9*v4nD{#2oVYi>~^7SKb)mgAC#WV9xr&$bU0Pno}1iRfR84kJ~#g244>@tQy zipo7|CH7LJFq}&oVodu3C=;GMIpDMaFJIk*>x!I`J5txRVwo4b^X?69Zm#fnDj1XW z^>BYw`04hUx`JxK%FrwrrCvo^a>ubTN#~kCzy{hPM$`e!L8-*m4C?4Sy71{4cl&NU zj{TJta%-bS zfjhphcZSF85}3VT496}1)&fCONIzdc@YnFo45m=q#X}MLijRU z{*}sp7ep=xP4G8E(wqh1pED%l$Zh>*Z$SGzB21O}`_Vo2P-x$}?cQ~yw42nBk~WKS z>BHOK?98%#C?GwDuLk3d@ZUDR8A41RiSTuzjkN2T0&R+M$Zwk+(RL2o2O2Kh1!Kf) zno0v~*O>L|#(caUr_2||Xai$-if}h5gM=#s{ZCx;xMcpmT{q)g2hg6`vd_(TXPEZA z(JO~BMy3`Wk``5uJ6gK?0Z+H>TzsJ$Q)6UjV|Ut*v_rbis?cr_!uIxk{)cz?v;XwF z__?3^44%Gtf^oV*DUVo|6}2v4%vhFL3;i&Ff`|JV(|Cn6>~a6*C8jCkQ$P3~-uu*o zAO746{KbFq4*%=_{!RSx2S@z(f8!NC^=!m$7cyBG&43K!>S#M$^l5L_{#_P23hx&IU|Ve%L10EcO<~N`o=J`qn}N3gRdY^8{Iq@J&U+(<={OVG-d)03 z6g~0^Npn|V753Cta{{JzFA z%7i=&$SI?$g2^@>{b65QiO?JTfg1QL^ciy|n0%);9>Xp&T|nVQnu^S|t#j$KKH7T9 zeM_g8;HgxLC$zCN{>{GL+uczt2oER3(2p6~2zV!Kh%@CoI7V0uXPx*zc0k?E_#J;%k zhOYPp;M)n@z+*Mk2jpClP@A(ZXHd#obw1(!=La12dwk*hX8hiVGydf3AL1qhKlq6q zZl)2WXFW?wt~oU<6M>u$8ZMcUPWnzFcH_P)1r*l6p{aNbt&dayap;wTx~^EufhT_};9n52%owHtoD$Z#fb)Qy3B{bEYG~(dL7_$) zqIWC&vhiS5Z*c>GuQY2u|BuX>?F{Fe4EHmFIib6r02-u9=U4+Z_(f}X}hV-Tz7?53ME z+b@jWhUM3L%{|%z#C^LivO}*vJ_;{Kco8u4RzUksrhNGvsBw{Sd266_UAR)hZF2hN zr6^w~baaF!kfFleA{<+VsTmV-9I9}NMrZukbsiza48GGQBcn-8l!N2BWY(T?&WJg(C9yLviIH_9N|bKgGbj9XYj4d6c^KKZ6T3 zH12SO+x-Xtf5yfg-jAQS-Z4eJ>pKqW0>|(a;y@eU)1cor7x6Wc3fkH_eMHpSFuLfV zu*>HPf%+vwpA3URKe>)I@b(MNCfoiE5nEFT5F?D%&z9Y6c#Gi&!a819!|n4vCR1+F z)`*-JY#3QLl%8?2j}>QgU`T}F2R<|6U;E|v@iRaFqqx2~fD*7Sk2oGb0(0?b={&Dk z<`YVl)U;Y8$}-Q7s_bN(=2cHz8nN4r*gwC)FaL$l;_+_5Fa6oq_>cedui%4kGv0qO z_%=O{b_J!xMtPAHZN6Xp1YA5z*VTUsa>S1ry44XaIsflugBvC24D)^uE(K3HxA#M} z_PEX-ajOjG+Rw|Xhg&}y$&SXk^UqF}Kr|@Bh2TwQpo<*IxK*xM8%Y`=j&o^KZ0ln@ zXFIjVw9O&G&`6j%DM|u<=MVk?e&WZ!06Lx|wKpeJt^Dizf-Su9hm!zLZSxW6}h6y2SB4*jiDb zi53hoBfhdmV=K!ZZ-q@O3MX^2RCk#Xb!TXwu_}j`eOG>x0A|u7I?Z4oz)6c#(JHME z8KA)<`-uooZYMnF9jL>Jxq@EBdzSNhPv_` z-I1;+Vo~gfQ-)~)m5D{~)IGv$k+Z7T#|7Vb^N4FTB0I4czS~+_qXq0NN~zHKi`i$g z1EQ@PbDz>))F`(t3H|zTKq*36Pf_)C8B*sHm>I`>Kv`y7?-Fo2dM1&NTVYcxV}Eml z_3;SW9i)b6PAJ7t&}iMLq57DJ#Aqh2APk+asuoGNw)41cyG+UwWhRm>3^^qnj|-MX zjtyJaBDJwx#o+2O@C*rASdaO2Bfe7ze}r!m=!Q=bc*+5$2^zb@WB9PB8;v$T87@$}zopIS zEcNR|K3He1A?s*Mll^Rs@9LY;2r{15x@t~|m}0waJ<4D0e{jkV3Kxan(XwFM8|ctz zg3u=Hf=R!a2Si}Rz9s7H!!5-NXiUV$U}xKL5H!AcJ?uI;q^;@ikSZS-kpJ#Dq>kTO z;CPu}hH8f}+=S(}>3DJbj)Afv)rCpKmRM1Q+dT&0$IIBis>FB{Eq*#BEp)f-Q#1y@ z7`iwgUN>oKcP?T1hH~AZu>AlO;)rO!CJ6@R*w^7Bn80&Nt?%4a= zr_Y4>*?MgrgrOokVf)_~#$ryai}%nNLw6_qeUTQ!9HYs|XHV1F^XCKbCt#T+XVC-d zX*fp9yP}YB{L$bv8Vao{k41!wwNoqOG#C8qf9W&$i+|}S@bvjpFc*|^#4^7Dz&M>= z0${AmNsfao1vxQl9sEd1NsmrC#sWgdyaMa|fay4aXaY*Xco^}+KlU#EoB!+<|MTDc zI{wT5`dfJMovF|zKmNB`~&_p}|Q z@gIW8mcF2GV>DbDD3AhHg*`RBYbsQX-yVzEoQ+}bW}HRdsC|lo*zl{(nUJ!|H_}ZCe)3C}b=zKaPVsAh0JvTU-;!3s1Gl_swkkSAgZox0V zBkkGN1RZ-T3Ywr8i8SweIQ}YY-kj}5Q1?`YJ8f}E`}EwuGqU>3a#Q} zSP#qGu)Wf#&`gRzgvMkztbP2%Gw5}_DE#gGE0g!<2uKWQbPIO2yy2_OJ+_T@za5Dd z3>|l7zSsjN*PaYc6hQo;~b&X2X>PK1 zy8pa}aurH`X1Ozmqm!$iWp55L)7b-B0UAVOAhQ;7+~Bx5ijK6Gqre76Vl9AoK~OOR z!*0TQI$^Cqs*Gu$bq-=&*HzNaxk668K+{)pCak3zINWHg>J(xYnPIwfA~_N*YqVWb z>7_cl@I&{A>zXs>;}K=u!<2`0El33H_d8Sura|_VOJO*u(GJwIx~)=c)e7+y^KowS z5rmuDE8IUE@#M)BQp#9L#k{OYmGL;Qn6)t6i+65ub+wbCep(-3T?MxLDPcDasFm^f zI7=o3GnUirtO5XbhXeAE@Z|P@Wo5j2^#JgSoCi=M6g`e@T~<7Qaf`#%0S~8D9VxQ! zKBXk`hZ$50s4Ph05+i+Jg%k_~iY94U9!!wnaF2C#z!vSn`BO|(? zd`JEg5vUo-6y6Peqw%Wl{kMD?Wm1C!_wO|k(&&!DBpAw5>|!UI2}4t;Tvt)%)KA11 zcE^N#6gH}uUKRvgsBGo?3;ca{0Qzw`0p1OCsx+j~C8@VIycU5j(W#a`!wghSS&9Jk zBZmSj?Bp;9#gK!D2?)X4&S5}@E6LG-F+T6G*dSnAmra{njJEOhT?*4OA!LfVwX18@ zh|AvT8)u*IdB0Hryg)<0$7q9~aIL-I1Vx*_dt8K}@mVC?AwuYYxcLxKc-HVJ0Hhyq zPsmQ+Tk<;V#jo@BGS0i~*1TKa?Wbo7opNy2b7=4_V{Lt3Skp_dL-90nkL~2%`uC;V z+ja~$j3(axvNLI4LUP<}xU*SLpYJ6{g13x$srZo}*yAt%+n>Xa{`e1oxnQ0@L@hJc z^(aMYlI-iMz6Yja8fA|

_?vc@{lGaxDbrgjy@cA%U|*(-RgHPB^Uv`)R<{^@v~o zmp+Y8e{#Zq``13iM;}#OT}8)Z9!hy1Mol-xxxL>y_bFQp%N7vFOo`Q&OW0hL6!@xURMIx|TrsN(4=zgQ67h70L-UOI zoH|sB)(BTgZBMb{I=kWJhPvq-X2JS(YqvQH+7*+bE=+p<<+=^Ez5$>X*b5vgR=Z-M z4gj|C6DfSy$H0Klkhg0YD$Uq(J%^P{syzePsR=IgS@AbSU0LVXMbdShy||rQ_i;_+ zr&Z6_HVBYZRHa3u41H6v$8CPkV+OnUqlfpSKs;3(Z)<7$`u{BogPNm6IS zWf(s(x@{HPJ+)mc_4_kW`X$pXHkyJjnFX2(Y7yhSE(=b_8Grin0f(WlaNd0ajs=Y; zs>FIvpjaF=rSqymEK6bUQrp?qv5`WPJ5Zbr2;M6iU`jaggjxy?X+=`}XqiMxk0yhe z0m_&kPnd3>2uyR5x{=%*c0^KOEjr;vcS#aKhKI=f9e1?b5r>e9?|UtxCWmhjj;94> zUDa75Bb1}n5>Ahgpacx#fRqwS1?G9demBZE*D5tOl0@PcDo6>qyL-Smj9?}yzpxfe zQ^w=NQE)#?*2ey7hj}Tu+E3U`gH}mjQA-hn`2G=GD-KtC+&;OMbt`tlf;c#d82iHn z(z(pbB9ZlZLDe(_X2$Mt042ipt)#er^xX$6^Nc*m5uPwQE(OCl;NAD0p;W?rT9Gp0 z;qfRN87woaR`7B%Kc_l*6i1>3`u{TaX0f()*;&{(=3Hwx=bYRAO;u6lF7B`~#Fi;H zq#-y?LgGY7VA7F>Kop5IBH_V8JRmDP)pP=0*sm*GSy^%w1}1Ed z1Mqvk{|Wy6Kl)vG^2RgN<$cW4hnVIG!$9CRV;Bm=8!q95oTS*5gvCLv+)}hC6Hu7Z zDuGc^3gLVqj04H>HjCh2ZNRJZh$r(NH#Y;m{X5>kpZeni{>-2KO}zJh!`*Eh|3mOx zUyY2a?}-41Kr)lQUZAsbNaeCt3aUDb2qKL1OBdis^>Yj5m z&c?Co^Qn8_y-Drg#}PplkjlI)W(){qEiiFKkY)-2;KjWt=>~**8f~h{gn8X-T}Y8h z#9~AMgEGLCK!XUR_qF2x{QEzP@BZW`k(LEaRlIt-=bO>pW}AUE8VZ`zts$Y7Z*P1` zd6qp(S;wAwz@P)clzhO2GqMN-59c;2;{n;A#7MVKz~_61ujFu2HzwHmO}e&HHNW=U zbO2NtpsGfN5dFmL#RU7B13mPOy*QKzElM5Dy&G_Yt1LPJ=y@d#R!~s*Z?C-WP9YQM z&=p(?{L(sdK4HH({wW^A_85UIa=_qil9?7QW3td^ZI6TsbpjZ0!DJ}Kc{jwUpw#zU z*nfsgVFsMEYP$->9@5@eLe~l5T=X7|)ychcy)qUAh|r z6m^MYe>9e&aIIE3D}1@&mp}hLc3(v|x3!G?4KSSQin{gO;T+MP9Zf1DDCSrh(4w*1 z@hy%b$Vm)Q&d+rKP;pF4H#7#L<+=^|T;~fK4MJwLUd62EenaYZXgq9@V zvpR202Z@H)loH0nL8E=xL(=C(D$wUayvVHz-6U?ffB6cJpFKv-87YY|fLq1!_Mj*$ zGurPYygnzw`MjVk1$VbM0+-g%Y5{1#<^H0|x$uUZC42aIy9a|b)j$jrr_)8e^*Q5> zHy>fY8!*ic^DM^9vdI2B-i*jOqtu4edD6V}iZU+_d6qH@ zN;~FRj#OLbg0K6=x3JrdxIfPr#)RpzplOwO%g^U|Mh0s=QC1zQa}Q_DFj}z1W3naF zb=%zVz{}~pwGoU}K}}rI58gawwAQ@#@p=%Le4{boI--waC6tEe0XGy!{V0yS>!1js z{@mrX)i6TDxsBWCU+6cvkb<6Hqig}0B|>Z(9A z29T^0urUCm`-H$G&=DhE*en(y2!(dV1&O}6L_jyX*c^gj9p+&mXYNG5{~p$~%W^@0 zp?F7MLdNQu?FJ>=F@()4?#S43xFc*Fhke{&KBjmd`@VdQsbTe*;+T1TLnIEX;fKuz zJTNt2jYpe)`*WeV ze?IoZ5ywnH+!#-NZxFPo9E2m;DI2=kj5WK0Tv@6{7e-qec)9kTN5$MLw)d6x5%}jQ zG5DA|7}op7kwWQZ*d#FC)&gTuuXRUlO~*6GIuTsiqTUt-m`7Nz z6uYK*z&V4LMbA1{_|^tA!S`OVz4zfBcf%q^Fk)zrRtym4!L&I#(4BQqn4|Dkt2|H6 z<0&V-p1ch<54wgvo3Lj5+q|p_lrHz^J%j{kjRNMXVb0q<*zwI_8N#8-LjnC}fmit2 zOLHrJ7!lUo=GcuHQh3X6BO|EIkGSh$>2MjTr~0$+eKWk6x4glQTI&X|#&lJ+-Cs%R z%@!^)K~LK-*hWbwF@ZP%G=OObq!FNu)(AWf0A(;T8YeMOP(rhi7!qZkD1-8Vl(HCA zQ*xY0DI=#W`MW70Cvuq=8t$G=MIli_s z<>nQYlTM#aa<(;q%fjL%*HCu<&OIeT4MST#pot((p>+_(*3 zx0hqt9z8lDrwnE}>s*xNiup1F%y{F?JKWwL(IA!fjh3}0-uiIO!xSOLFs~e z;N6OfcVQ_4CN65^-wbp?V?A|)T>nO$A2ef(F)V0h)#%totImD${ktu~nw~jxj|A9? zQFyd~2|r=AITj9}(3XzN+Ugr1DcmR;gsxx44B}Bo0j7o!HibTlu(RUs;r_gT2VWs{ z{7PX>R@dV?#Q|?7@X+R6c`EvwhtzaH^(T#hQuyp+kA%e9-@XVNA`~Ja9(dWk@!dzu zaeT^*03`NF_*e}GB7yOZto@s^`x+DoGTOroHKOq_ zyz~Am5E@3_VZR3+A4mMj|M+eEXaD6F@bPB@vPRRajD}m>+%B-+d(Dt+#UqY6YqBlm zo=9!N6n+YQ+J~%b=+&~U+bx0SPFHk%eymwTSJvBxpqMccP*40iY>e5iuTWEHs@>N# z@0;L-r%-J^Z}IL>-y56)k5vBR5dlln@criSY#B%cfd=u| z{pA1q%lOkTzl1ansLLXI&o;p(ciPs?gjWxNH`tr8cO}fM_pTqMitwE6^%Uk|`)pNT zpog#0@dzy9c`;?L*?wdVW!8LVhMkoS5BoP|lLF#J;e?WfbhmC)cE)A@Ff6Z>$ncne zl$|b{!9`f6&Yd*`*?B0|xMACgn~t+X%=7zUlMy5{TGQ+ESbw~N34)ZtwQB#Vr=0lw z43{-@JS);$jRs8-;0`i@S5V+r^w||~>JAKMG|=kEtp-)jVV*PHuS&CBF$ctZNaO}O z*=Oc0Z@#9kUTq1N2_Rk%NQG^N%ZmivZ#Ek}BCvIzy5dFBbx#@wPpX8A*F7QWndXWt zkmDnh8NCeu5tI}V!xMy zZ)Ot@Lbe=I^X7O03gk{z!iPF$$kthxZ zhCy=HA3ZvV_Z`4e1&#jx+oz8ewp_8NrY+^l4f^kRorbicy=0 zG3+I8{q1MB0F8L>y_YCu!7zwXQ3~4uTo&w(Bi?%ZnWh*>uKVTEkAxE&V?(=Ku+Pb{ zp|PAsAY?EZ2iiz~LO6}?0?=9^D~Y3vZvQ{Fzy_7j^@!dNDSwgwdGi>r8;Np;?q`eI zTALd1z$RIL^||;@y!mDGEOk2QqX@hrZmZZ5U<$gKM`GLzTS%LIXP>r(&{>N{`z-Ir zjo{upJJH>OaU}@?y|QM16y7smOAgmph0QW_b5QfTG)8WVefZ{6UJ{ee6Dw?BT5x1MB&w_(GY%{}xo8rFrG z{)@o}4$ruzYlDr^6@evz*Ty)du>@925*!yi#NOIvxDsI+gr!EsaCYm@bDU$2N7x&t zfPf%XwPUe31D4TD3j?=D5(7v*tXlu+5mFmuB<{7kR0*?iH6qsHQsn&7E8(+*2eu-A zb!p;3*E4uQ0A-Re_sXE0(0IWA@Ynwa{=mQXL!hAm%hK^YktO~G1uVcAeb`RXjh}3d zbvjQ5JcJ$S3TdR9cBGJPj`iq0_*{05bRV6orl#F+c6YQsH2VO>fKQ&jh`x+T6 zn=0=W0CNkD|L*N+um}u7v+Cg>uP?yhj$7 zAb}M>#M7QZSrnw~ksulxulrQiRol!azYy%?1hK(|2$q!)&mGsym;~|a0;-&ILv01~ zGGU%C__Z&e@ZvmUm+iQ+E(J&VcEh%vzOApjM6NY0gfmt(0J-6id#?8S5C7Pgjju9r zDj5R`4#PDEioDozg^2=hW;yC?e|yx*`y|CyL*zypWY0CjYZo9I7)g>A!qIyO^I!le zXF;g4VA$_5j$)jf=Na5syvUT0v+Wnj@m{70w~rrr#rN8P-H=gg#k^FEL&jm3(HK~& zoHu@cbw({Sj<=7ID4{huu8d)Y2PL9v+>ID^1CBR)fD+15QI;Y_4G$nhHY!q zaBToJeC*?I0hDmL%pd}$iyZF;<-Z1(%)p?$v8hp`I?)HBH2?r007*naRE~{CyxXPsBYLUumpWQ9z7SIWeE9ohp#*CrGMnC&h0d}Nr;JYGk| zY5TP+xqdC&(~GOv#zVAYBm{(pLbWl0#v0MZTe_%~CCHrUVw@yNPp z(?B3HB=@>tME%B9kOxfp7a~V)(1q^-+qAB*sYMa%;rPZ!@8GndQ>!u2ZNsmNcFGL)tssIE9zp3}UmWn!e z4>}arEhQ|~1R5H4IpKf(>7T(L{C)qbw3P>Nu9B_siLDR(X;4iTfezzXC#i&^`#Z{tPW1mcLx2SQlg3I{~ zZmbpFMfqLk0`ej(gi()VdvtfeIAkzbyxuI;(=X>40Py^c$GEvY0sxjpA_+=WJZcTM zH%EX7=hK9xNKv`zGJ6jDFpkLM00Qvz*&U{-;(UKWN-W29YOXrKXv>5*KKdpO`yDP* z5d+dvQI_g*TB}XQSqcs#X>mB=i434s?y8#_E<4xjxDmg1gyEG8y2A0b*bmx#H@?>0 zZ1Pa26Lw3--A?=EGq0Ko#<766QEoQM73U=Q$38xDQ1`gDbx$28UDhRFReQqJyA@GxpM4+u=bZoLtqo~K*(ACY+e-S zZe6l&1nx3WM^JJC-_ICP} z&14mU9k%^G>L7FF-0^t3UOMJTY_1@*Edm$@9lhrsUC=MVL)^Vx##)5o@LQZ=-*|pK zG|U`txx$4diR=r-X8f8Y^@{RHp})N+quoWQtX7-xVQh!%hf zC{++wF&YA>H34Y>VTY6om>9L}K#5U{;kbHh1_^^$NfG`G(uQ; zvcDC>W~{vKc2PX!q$$PhxS*2~FSSL*SV+Agc;}q@7Fa1mJ$lwT`aG!{2uvwwvUwQJ zYEh=r&`PgP@0H{U>Ln%#?_>fR;cx%i2l(=5K7(h!=-qd3*ve~+VZt$Ei+~c zrs09XxHfQRO`$OQoHbn8Lf#cG6T#CEGIe}J=LMa#qTKuXtn%9-IHR{xBA|uJ`cC()y^fdefG|zU&^ypWR{mnq2F_%e#jv9^pUF_?3vk-TN=9D+B+0$u^B?nkev$x0WaJ6aK8R_zBw%8Kk+{lm?uIWQ78V5Lg zq^5?gTNp0XTP~H`6F4Vuvh%Nd9RgAhhx62+*sXb%!jH()#+$FeWUe;XH|v z{`Pk7giT7M&@jE019NJu&3c(cQPHv*Kf%!|UKVVY*tT5$X5 z29F=#f{5fOHH*He6}2=Rk0XX*z-3x+nHIe+h2i7?iiF)^k6Ihvdg~D?Cw%bXOE4;O zmLjLE6)*#}EZE=O;ra7NSSq8|iq;tSuPzufp|S+SO-scxoiV0_QIDrO*GAjIp1N+hG9yD|1lbyCZ5a&qW+;V<3^_9@yxe2ou~xP6$f5Dx@is z7ro)iK<<15q`z&vC=LZ$t?U|%5u(->0ITvFbH6)axzke@PL$@m#a(f%Id67FNm0|= zAs{RU`!Izu5Yhk)BVpVn41?sP`Vl;eTuu9J&fI#Ao-sj_q_>6!Za|VcJTPl%(}ArN zEdQskY2P5)R)h`W?^|Zky`}E0bzvq5fe35jOwn$k@EPl8!Y+$myDMI095ZgfpiE!g zx}L&XS+O_TKGpkR+P3=eYo#-;klEh1hzp0m+XOcXx&1|OZ$^1Nj;rq+F7|kb7=rd7 zh#&a;aKGK)!@4%>{UMVPe7g=kis7|HfoyZ%ze3bj1t%Pr4Q7U$k=QYdMf$sc`wrjq z?H@&=38fX}VMZR#Xk0}3P8WbC5H-}c16UO!CJ+_Wb_Asf+yXX|Fhv&{&QF zYPx7k8C5TA$VAu^WH8F)^0zPkaR&s{+`ylKs0=}uA|L${Hz6wpcn=gbg`6R~>rHN}I%+B)upu^2og z7tPn+^(RAWV*(C%fsO44Qn)#-O%xhy{ob^OkF=K@*PNy)o;v>lb+2~Zdx7^cdRmL& zoUfZP#mM#o(>5cH(O3q&CV+da73T$)O@SwGs>>Y^r5dJW$u{7W(29iOYbgy?2H6dW zj;0!Q(oBfd#>J%8#{h$q%Ml8DW(9jED#X?{0SlvI1f@YwJkUaalvEj~9x>y>6M5vz zDrL}k7AgMeYLKnN=0#?)A$Fx)wz6#IrpozpKB2UG{K{8e;?-QSA1q?S(3t}|SkxkX z58C)%r>h&TZW(nvp{B2^;>lN2{Hf1&X@r~@J2HVi;96)ROvlG-_z#Ga>gMqyP|m2e z;r_f}7zZqcL8JxFG(v>EL4i}_CL!tEJ-jvt!_U?#>axI~dAQjl5#j!_D7}-&my|5L zT@c&~8{Xa>VWIUY0muD-FMj0}s4xW|lE z3eM*XGLeS1`;vnp(G-p=NqM%fS(Fgh5w+YkXT>qy-_EhtYin%)T~-v*5c&vXi|lai zoU>>9eprbX*G+jV;Q0HR5(JVBWqB?~QJ8;xrmILjz@Aq(^oAu38=h)oY-R9caYzBI zguXUoc$>|wgpbH7ltYyBVWXh4@m}sB?=~}XbwLQ~7Lq1yJMl7L1#5~ESM{E6lynDT zVCb=OXR-xfa`mAe<|oQlkXJm@w`WcDsaeB;-*lgSsGWUZoZacr=+; zwE{X0?uQlhrpLw@50sEN4#JgvWN;(N!E~zM8N{04@~R(SBj&IU0r}WALgWfUqQnMm^W|ig+`|hz=52H`Z*oMCImUO^>+rE4hQE2Fk9&hRt$UgT$U$+~SbdGJr zM@iR?Z4_&G_c=sk(G}vOHO6oIrUC!fzwvSGk4JDjV;F!ueh8#95?6o{)=Cm;V-JbUYHoF6^L=kIqo=NUivdxf#z&kITerg_0!q{{4jg@H<-24p5= z5Xw^V^8OXxedjmunSb;vl9#u_-@srUklcSFU=@31>(19XT10YdD@`&4jW{y8BHQ{q z*gWfN)Zh#cJ(U)MplZ*Ua&Cftz%gR=&ZY*mqO{GcB_fP!YPvJ+;FOh}}*5X#EX3Vxe z(3L$o@6Y=pz;lRw&Eony-J?FwKi$k zw55Rc3}EIg9^O`wGHdv_t#j%-pU^!HouN41vH{ZRU8KB$Z%@o<5%@HfFIeXMu*mu6CZRxwr z(U-Uznwr%MFxdo>2*ANh)|>~i!`x*R+ao&0rf?JGTS5sKtrs=)1wcUF4PSA%LUPL$ z8A=0KJ?Bi~xg=N4lPl$(PY$IqQCWw2Z*6S=--pqFO-9#jJzQ>*&&-U<4UH@6Qc#u| z=jnvgbjIi2e~C}Mcg7({oPzVWzV;ir<(h3i{My{-c-cX>k=rSZd-H5)bIjb01`qTZ z$o;kaVe(;XxPAT<<9-JQa6V0t;{yq`7NnHWTE#FX4V{);XR963YQs2Yl!cKqY1WMN zXso~sXfk6McR1c2G0(-@$q6V$k7y(Fz)N-XVMy2?_5cX2Ry@AjV_F!?G-H30u^R`R z&WkGN70Xg^zCR-7=C=+&BR?}$%i(Uc|wuwT!wl5+OE4tAAG#w)0kn z1yKjZ3N-9|#8Ql$eCvHf3JT;_NHPV|MkNZcdawK>i`2d7>M^#>Y=H0<$azojJZyp3 ztqc@@vZ}tyoZL~6Bv;$6xq=?sXnoPytnST-Z)al0!H5kS%JxZw!oc_7A=4zpUCurv z41;*k$GwHUbOVH(1q&_uAVgEVE~I&fHiWL6_h%6bnsGpIUPVniBw5kMa@MJ1hHr+j zD^@wDu9zb*WydNNj+6zO924PA@bm+0n~ew`Hk9*0?b8H9Q?^%|cGV7f|K9GFy>P!4 zi34r!StH!`voUBq{Mv5hf82}A*koIrR_jeV*cGGYtC?NDHUs2qXgvHF;7%q6Z}unF z0JaCLKOZ!$`tK+co^?q#%+25)*2$}at_C00VvF>S>x{!5_@}=2F+TFOUyGDZXgC4s z3!wB0jb~6Q0HCQi1puSAJt$28NEB~W31@GE?okm68W*%CMPdL(t)qDVNJ7j?9Wag) zS`)!>90(<65CKCXygLJ5c}4h@7q_VIet_ft6^3Cz9y4zCJB(wNJolWDG{21?G$!jF zZmr>bnepm$!B>9si}>)1?_mDY7eL5JdFXIusPNpGpriN-wrg0sE?|ZdQv`%-0|c}s zC=?dKYWc^OYi`fjk|wff)z!;*g*=O{n77V%A|gb3Vv2lE1(ZFNl2dPiLr{v$cwmzy zN?kDLU=*kfP!ofI@{1mMB`E@{Q1U>u)ehcsLs!ymP;?sO0^q0q*5ASJ`saTDlrKO| zNKHcJ%{yt4QnrYm>Sf}TwQEO>fqPN1*6$IzOx=5#jDqTqN=z9VT27?+O`suxmkD*A z!E;&bRouf!MHnWDB&h5m{}$@XT;+kBvzvqQMFs=;D%%O~-Dz-Kpbt zj1h3!6ls@B`7S}B3qWBld%>HYf8MG_7wDs;RNK}B)a8N@_(M5$B|9FAXMHQL>-3#G z6r360stRrp9#?z7@LKO_)_!nELryj_VGaOx1-@BD=oZ>BIe@wI8D_-e z2|@6h3T~hX;5Dg=d|e!B4jV=S3b0g`f@7Re8=+;X+MYNewFE#aaHl+glZL)iEY?e! zB4KDw@X$$S-xo$JY^@CP?saTpXsWHDwguBNq0T3i(-~iQal$WtMU?X{hjtt@W26^! z1EVV_>~klh7YL4$!2UWdu+*21<8GKoTZ2^P)!vQbuD1stSERT0o05iA6X8+ey|)LX zafjW0#E0*{1d-&EACC$3)SU0{b{W%R1tG)Oo6zIN6@#9T;J8>Typ9t~jhVOJeu7#h z`yGa1z%&=!-R>}sBi{YsC5RgK;}MmCd6_Y!orKI!Gng59 z7%}d5sEx7ARcKq1XaNa3AEhuHkw+fm`iPoXU`*js?%(evG?EH@H8G;i6WV zOV(6iZj96_F6Rk}r22eL%HtH~wTX-*^|SMiqE+RKayECTB7Uhuij{y=e+<70wD~Tr*J{C&~$Vgq*UjG=ZpJT(CJ}Nl8rw&E!6y3N)2j< zk%D5CoG*eDt|s*B(Ir=;L4R@<&FEfdHM-vA5&zAP{}le2AN&EdJV2SLj;mYy*101cou%`XoHN{!%~F(jcEY4Zr8Fb^_sFwmxZwCUm%er4I@3}UXDOUWb( zU2SMnLAxxd%Obqb1UKc0EhSKGATv@oVTKHVDuZ*@B5SGh4pp+;xEc7;3b9jy*CBBJ z9pKxBA6$y!@8hGJ^R!Jh!N&~;R!{MDbuq=;|-kO2O?;zj3gGY~_ zX+1&RPaEdt=^T)`cXk!v52I&ImGdh3yh}l=6L`L0KArHz_g>)t`QizOY=ySs8h2a@ zUjna-FMjy*l}uv%GOT?Ze>C8Q{v#bQ`g%q4{n^dP!q$064!V2#7%gSAQgHwB9>*t7 zks*e;l)BeE<%D)=$a#hCF6S67c|={@OG-oU9^WCQgn3y+u~(0FRYM`AK^qiWl!$OR z>_Pg>^Jj11bXhP@6OM-+h@{9|h8P=bl_S|wN*H!K6Vhxoz&DdvyP=a?|x8((#K2z4zGPQx}>zE(V8OJ*TqJwUyrt zD4vGY*6lbt9x@$Y(K?3Srt*dUz7~XSE#pYNeeU?N5)io$s^dwY zR^+*n2*j!T9K8YZFY8MIV)1v!+0LAqzUl^qzWyvcGLj*ZVlm$OK)xse9DB*LMvDn`@kvMR5qi z5i$PlAATE8pY71<2dMRP$Z0}K6G{<}dE=uhM-{CApZN#3_=%r*gctV!4}09-4;XVo zX*c-Vj}kud@fqLs-6y>DbrN1#Ev&i{hG9l218ReKb(nD+3l?s2tPFsZ8fF4^DPc@I zoTeR014?b6+K_6K!gEwrF=X`U8pgcLxLgWO_a~fQUGVaQ_c7KNpv#2$ydaOmdaY^o z7K}I@ZnVMQ)oMDT1o)HEc7|7V^?O;@Bq->sg;NIQVP!xMHE#$5wKr?OD~3XElK<|! z#v+Tj0i3-*({W|LiU-r2m(hT18@k&U8Qe0U`Htc}C+N}38gi~mH$;$p zVF->7gW(pCt0?(NX_}|G7()+H=#d|EvJ;5DwE=Wx-e_CIdN(phqKnQ0qs*cF%<3D6 z;>b#i1Q#VzJ{W{z9_EIAt~3&bw&f8wpHq)?5S~a<7!T;U6nR97%C{-+a7Df;Ornw3 zwqOIh(WSM1OqT_+h@C?X2_|RQ{n}5{edS};`8J@}gV6Q%+;R>}r%JpH+;Y#k0!twj z62-SNAQ{LFxD=#c{}zm&{RDV-gC${0%K!i%07*naRQ+y5dX>TD8}O^UUq$+@KZo!5 zhBq(-Ilzr^U>~P#X%UWXueO!pp|uKba>n*j7L>A}l@q4(J-+zCJ$~*h_c-LViV%8G zEQqfFkG{jZ++h21)kdJr>xSUC!B-D-$`S3g?&$=A!EgOs{4Ev{j^w?yhDXmHqfx@` zagWb``Zq8fZc!RzzZ)^l72}w3e_FI~oABzDbj@Ot5&9tOWT~J{qj*f87z@NvO4oVMyfd|HmZ#};SgK(J^t@@ua7pWyM4pO{M!pmo*VZ<;> zJ?&a5>LMZJ<8DC8gu{No;kXB>cf4s%xhcTUo;{ZE_{LaD1)*UnVgy^}g4>%Bi4xA2 z1=A#jfC8zZj`fp#hXl?9bwU2z0ZTpP-D)91Ehnp8;GxOfvYI|+?k&;aq%Ohs3;+_KV zxHB2Igeb8#t;m{e1h;&XRxkB>JSt$eqQ}oz2sf{W*u8%%0Gd^hIt*2|*wQ9rj;xIK zifp|`=`%#xDjE@>4zyK!R&zv^b8FY<7649~r>|k|oFJZZXOt>{G=qtSB-xlZh-eCX zBBZ?T*NO}@kJ#!AdNmuCv2V-zn{v($tp2-=Nf4)Vx_;L+qB7vZU4&it`d&Ta_xL)o z!C>t@k26|v3IBy`n*F!PT+s32s_)RuYQKh$ydBI{L$D4vKScawzY>BE9voBjc=(#y zZbl4`Q{fFJsP)yS(4KW>bk&se2dAYMogi_zOt_j40QcB3nFZYFAG^}OnqW}U`=w6w z4WBnd^5+d6%#6ER!nc0gBc!wd{0@fU45orsCFD;kx4%N26MpIEpW~;0`hZ{mYzC8_ zVGhafqI$tDjX2MQzw>hqzxtU+_`dI(@jJff9xWx*MbCAuBWhz1RbgP%P(iAIi=a`6 zvCoWhU<~&SrQTqf8>Xd#Rg-MY7%C&>9O0Yv3#d{6Kv*Ab(=W$qF>PvE^yRSYoi1pud*E3X zCQW99FzboZ>sA=^eFMMqE?UmwmA7?gDY{f!!cu`U74T(2nJP*ZZ%iSG2R$XElrWAB z`^I{FTf#6TE%KGXNNVg4;p@&1!y2|YNu}|YK4j}_DH^&i9^TGS${c=cNm zg{BT-#3_`pG{RICq|bf}p1t!4?2fm{yB*Rns7o;8o0o6GJD;k!fBPTe=6EN^Xy$~R zZCmxo1%`y#i*nw4ulih@45qe%Rwk5rL7C6ET<-D3_fGhkckb6=?O|&^_zFKb>tmBq z>}i!4l-HZ|(Bmc)r=r~JxMlY|Jc3wE%OBJAG}^oU%@%7buQX=t4?7%gkJumfc=5pt zEYpI+lO1aC3^uip4nxwa?&O9qlS72uwBDbkIR@p8h;g{N!TEF%=hi`uek448nHQi* zkvKq%2Z;!~-6-_7E_maOH*tTKqsWHcfO)P+EXSb@!(Ix!l_HZ*S&mI(U@1~nJrA0S zHe}r19g(x-epf9HSC$3SdB&4xx45}If>{dAIc|Y5PYWmkhvOd0(lBWreX9-U`!g~F zX+NSd42i z0LIi&#@2yZ9fsMZX;84H%hCiQV|aCiX!h21Nku>yVefWK3XetStv$5nX%F_N7h@Vm z81Sl%PLHg|E3VnOH?teweIG_)I*w@5c@%Niv%u;m;~`E1D4v7vyrg@Ux4< ze-mkVg@gqu&p?{g%hixF@P$u5!e97{cc8>*l#tp5mwLoEe#_TlfAb7&dLN(v+!yig z`|snpOZe~w@YnwO5uf^0#_#*rUx=Vb4agOl7^lmKc@{Z-n$?+_CDLV}1w0Uj$~X=M zH#ZHR{Yt^O%N|0SOT{qAQ9;YH;B+Y{(~R?J2HoGI@&(KE5(hLamj!NB4J#ty%`1@j z9`|)z{oZlM8IcnlSuo$bjusvHn|gm&^!atK5JJ4!2%WUJ(gNRSh=15{IsX%5sf9>; z1#%N6$a1)ac^Y+(dFvG0XSlcB`is1U)SFJ({t{A9(&Hpoq><^5fQ_3%S|EI{pS224jv{jhypq8 zZ!z!oxcsAE!vX^34B~{AGb$%64Op7yoG%rn5elR-eNEyYqm)58VK!x+QZBbh6w4-4m8RVei{^ z90zqMTJ@DvUh~F~C*OIpqq=@+x>rZJ(Af#DyKkG@3cZTTX?$_ zLF@fCa4)!}W3!6w9OnPn=nn1gq9wS9^Odd;AjyNZ5tg1k|EhCF%mDKH2gzxRbNBky)vdx0e1IRWz|FdlLW5lpt$ zk=CFa5OkattpPB0`yE^u<;hcOg}6Xkqa z3a06daX;Y6(?`fTqZAGEC*hmRQn1VwPoLeOR>t`KvY%+u2{Z$b>;#S}Drr2K{>AYg^w%X`>q~F%>U*YE(H|0L+X|`dkU1s0xD-O}U zfq6#*!^+taU;?Lk*DPbR)mE%z^?>2OJ~OO^>fWgUvbW%8oDx(3B4zp(cG!SMZk3mu zCQ;IBH3BYo`k9og<1m6;?n2RONt_Vb`Evk^8Q6-%B=};ipkjGr#`nT|2XKVgmN03* zHo)L*VeN;T6sD*)=fY@%z`)w<$lcsp<#4KuT3RSj*%vxRq0gAIjmP9(7o=HJ);JtN zqsU|Tg&na{nsmdM5MY)xhmj&L=?<}Cb{=hE>t;V(*EONQKGi16H&>BM$* zi#Tc+K2VsKFCbq%+<$Y}y#D3$e~1!nh>BAM6EcF8dX--t8n972Zt1)_3_IL7Jk@(# zE#NvGCC>G$5mDby+o%Zcw1L*JHGHsJI}U6?y57GbestAeT-$BrL!5^dO6&}RU_v>8 z@c3~8^9jSyP&pwDufU=rcq5vga5FYs&Hz%y^G6FV%Z$sE&;TuHldvpjFgMh~I9)E7&NGJPf?a!w)+%mFMJjF0 zt*+qA*C6*>Wz9?uUI}WnVY;SB5UtiT8cQ}^`KLZSz1J0C>Bno8=pQIruR|ETk0nmi zXh@*i3|L_~iZQvwengfD7shU!7Annj;lub3tM`n#S1H#?GkO(zi)JD%l-f6Q=kKOC z`2}+D$$g5N&yNTE*kAms_>uqOk75}TxFqrBC+^CRYFGqYvj7@qP|6bNZ{s!IWAinQ z%cP#jZ_#1n+rKq%?z90pWB0L7pe11b+rNm@dnc4}L>+*o05fH@ltoD*Vb4`9A)FJ4 zGANHoLq^+4t|qD+sYE$}hAe%iBnFqHN|ddmRQ>AMNNe;k95Fm}y3Jbb&$fsnf_ZI0 zaJbrUE1=jli*}2FD}LM`JS{A)Nk{H=#GZIZQ@{c8QVAvq+F0!xRN;ti!S=H$=UW`^ zXroR8DTHYr^2+8=XlT&m0+lB)x1M*dD9nH*tOHO(6Iw*D#$h#Gm)fCkQC24y#F?yyz>p%kGB|xJyIS)Im6=S84Z=?q=EV>VR|-+ z*Rv&+MbKqP7R8)k%6ao*zO@yUTEL~C)q-i7Q07-y&S(7cyC;0+#f0OKHcE9zDSLmA zRKM<~h%#4*7@K*$0UcP6$7ym*{zcy-t6t0?x0=(#0t7e4ljo1nDBm!H~rfJ68Z$Adph!-E;qm~6EW!N=MuYp^|csSzCk322BUU^QMI;Wjs8M?IrIscJ70ao(4afqc`3A+dSk-HiV~5Tb*2!(Dqi;ireMm&hMg{RE`yTrlTs^Ww9gYWLyIhI9phzD&tj-IgpSTRnDtgv<=uKf}mIM zU9C+grsSS<-HI-7_uIRdsG|qwZVLd3o*+c&EHH>b%;a6i0FGpWSS6X{}v6AI=+Jbgrkp^Kj zefzCFXII{1AH-(7p^a-PVLMk|joI)|E_bVTSBv@@IN@G(hJd3Xgnk9n5CU&A50>*4 z-&o;SSg)`qIRnEkVHjsHE>gjr3%DhSlZwC%`1zl^!MpD>8Y=et9sbQf@~7~Tk9{3> zyB$9G;4Ane|Iwes|NRU9(1-cicfWvt?mM63?YD1G8t^~<<&5)bhkx?>_F$Y)>wv}; z)FkA$w2VX&R@PGUu^tD;m>ZtoobY6Shto7*X$`xqhJwmi7AdGz=L@*bsO26xF~){9 zKBDhG7jpdvTz?Ls8U17pT|almhs|OhZ-hPP>#tfnx<#ghKli`=JpS{4=N+WSPe5tW3hFWs4*i;;!Y9&fzgDb?bl@k! z$PNbj+45ma62U{{Ofl5-0XcJ8V88pwC-CfFyahh>EBN3SeiQR)K`lT9;nkQ?Oe|8( zEH{A>rwrbWpm9K(N6>OWt_P_iyvv|E!m7?^ka$+J&fBO&>qe?hvdG~Oi(fiy++YzZ z1Sq-g5ekZ*F~*%j+aSn*Ah19Wxi@*lbI@U3>b^7DX?Q_FhFS4ur-J!J+;!!17_IyuwsLGG*Bm(t@)&;E;w0XfYU2s|Mad}1f`(OGp&UM7i zAi0J{Bd@*HeJjTI2Vb;)U!z2jC-87T2J$jcT?(n#C?@w5H%>~E9C_G#}-jo=l zR)b%PDDJ!6h+$0F??(*7fKnu{yA%nFpUxAWJiW!?cmRVj%~c{Kt31Cf1+6h|Zuh87 zj$Es?f*WHxU64{n&I6{)geOmr825X;csZlY7l8wdGf?(PK^}pRe(V{5j7vCDEgOAe zfNKTSMb;oIZBBajxv#SP+Zv;GEk}=6YmOgAS|8M}5hd7;CysE;j=L-QWwkMs&Ifse z0-gX}HrzfM*@2PXjrwwR?@ClzA^o2ei?>90( z9lI&>{y<=Q+n416%X{~ZbVGuDRvwjbqxLy}& zx<_94FM+`r*1Mg|`n7+&X$RO<9}j_Gvm?X&UiW~Fw!a zf9T)+L-@>RK7)6@@I?^B^8f4aT=3@IfWsl<1j6S(o$+no3hZwy<|VoNfM!B59^BJj}@pPQ!%=5aAnYdm@ zWoX;wv7f@8wa+U9r@>JWvMbT9q;HLT4>WIUX`W8=nkPtMtE}`WljLk?`LpYAdZc}> zh%I$Gq#Z^&U~fi~dZf?_d$!=~?YWYnG`Li7Y4TSacwsN*2Bkwu;X}C>1*O!%X&X2e zNVUr5W&kjye|1)TBBXM`C%@;DXv-uFxS5ea#)xc{$RnND&R5$MX5~uI3Xhnz@;E!Z z&GRX>QECUIFyysyXV~NT#xsm>-r`bboUx$R8HpF9WfHGrS4g25G0hdH(}cr* z2LPBZ6PP86<9OU7<&4W|LL$Pr8!(I+x3_ye*PSuX1+^}?oMtcxZ@&Eu!#Jp1Z-y<# zQW`Gj36CEiF$@`(OYz+E`8=VPiecQLECu`Bh^NmV<1#m#?(f0eG+e&wf>sbQ-gx^> z9B*!MzRVIuFc-91UB>6uz{?D-3z!L|0+k87G2@VXwvh3X+8C9^z>$bm9AL-aYo4=0 zGnnG)3hY+)j5569*lT%MxZ)VSAKw zwAi!BIFU`XGc!gQ8^e$DK$uUQ6J23dUnow-EbL~v-*SVKjT16ITR^8*+NW8gOKr%7 zkP?GPthP|*Rhxyui`U#eHO9nNZ zQg5uTCg&Mm_)&K@#4sj*z#k6lK(ty1>#mdNK>W9DqFbw0g#}4rgbs}D=5v0SWWI4; zS1*3e=ZBan;Wdp=XTs2p&T))^c?RX(yH!CjhPAJM+ddn)J#Cn?8L#N|!_~G{VeUbY z^fAXgR$BiSf`711g|JR}MKq(qZwqpxM_Z7u8H5Qb)PHCV$GsGh$-@inD|Jh11AgIW zpWxHKS@Gz|c`h~>%hH3q z=Ur>>ea^YlH`ZM4*>=0_wlS8$118477(?PH5>^}&DS{9Zg(#ALgeWMAgpwabkrG8w zLIe>Y0{BPlARLSxPhb?|Hh5~gZFkeNySl2nYxt_Z@7{aP9@cvEW36|swa>j}a!U2x zbM~;tcb?XN^k@ETS(7Cy|Liy4;(NdSX?o{){UH3~#*i0W7}V{;nlTp&cn z5ePw){n2{g=v{uXzSZrLR2;8x}$cy#JxHUxc{CRhagT9V|bk>~`HB zsrw{H*53TP;Bq!Xwlt-Eef4Rgp<3*(2By5=iGZd)au~QYih6rgW`Q4(8`kkEzh^j> zG=_8pa8o=fd-5cZ>!`G0TT-ilmqrtZvrVk5+j` zXhO-GwaVW5LXEO9rxe0$_)>Ceq%~AnMSc|c6|f@1REhMs`wVcYexdx8-y<@Tt#!I_(9SkC4*0e0IWFT_o>am2(Y8^DtwGbb~80aFJL{ z4$>r7_kGW>+oAcmFf!bRCFN!d(Dz+2_Qt5(yE72f@%E!fJbdO(Que2Xp?6I4!eN@} zJD*T5rbsq4<$w1>%3Tj3G7KHwJBGf)d5@}=itllWjE4iyz4(x>^C_A(BzW>JMTAU; ziN5dHY-JcCgs7aa_^~g<{(^MT|-MqZkwS>L zkT=-YH$n(86_X1lz|~G*q?rH!AOJ~3K~(iF3a6N7DYaQr-ZDcYSIxJ-B}}!+3kE3+ zwCsqk{~B@A*o4ws73Fp(?fDh3*SzCn2tASmSF-J~`KY|!3Xp|<7s^5Vs@V4|z^ssw zW-arzTW+tE==B&HWGq54N1r32RIX~-hgOMft%_2vHO1k|Al7+`E1|4i)EgA&wofIz zm89JE?0RyQSRP-!tvk(2aja0kb6;!PDC_?8=+z=*+(rS{=jw(_3*e`yBAN}DU@*Vra7|dVd<1-?_Be3U)%A~-+{Lt z&+J?zxX2V2KKP9A=f7{qU;Bx`qqmgJP(-xV2BmyissMx08Yr87gGn~tKQV<|N}hPZ zsTNVnJ{Z_ZS^vtg3UO_|OXBj5>NQ^}=cz*}%z+i&~j0&wu;>=7;{1e~&m#DMUZ>!lFFebWNZ+=8FZFA`(r(*6J;9jXuSTUiVrv zUoPzCa>DjD^EFwa^U0Mx>3WsGSOr51uI&=%>a`5iUKNJ>4dDb-qw zzgd(cIjo&@U_rF%?{-gPE@CM4%zLtOZaCwl@j#bFP0yLJp(xII-9rQO+Q?g)aVF%v z^J(E~UYJ8bP@>K-d7gUn>)7~{;t_Y9L%b(Q+M5`Yk{yKNq?02u8V}Ev!+3i_emrhLt zqDoD%fE8XIbHjpeE18Oh0k^b)BGa^atc4EH%^ur5&=Tooo^iy}3gAzkJGH)6u`l08rU$XZ+ zRa-}e7$SY|5OFNal+#vIxche71CmoP7NSP3ulC%zf6g!rM1>`!K=CdgjXaGLONiXR zb3zD_!<0hKH7Mi$K-cxD9@RYY^mF%sj>{+4ggGY+sixCtV#En-PIlaXcqi3pm?i+m zan3Rjxj=UyOotMw88U6}ywI7RWa(0Q&x6&1a8}!=?ib~$3fj`}h?NZA0@jiEX7E%` z;JW#Iku7co#&y%TWhEW!;B7O8H@a%2;^|6_YKA9A-`29$?c+3ldOY^EoO>*9r#`xv z&-AM^t1qiFH4h1b74@ZkBxcvGRs$bR&6 zgmetbB*c&^3XkF$qY^y~rz2na@G;8^Cw$~;VF(NNE*xKXzvqPuxN|1_@o(w)AO4Sp zr|&@%(r|PATZP#Gt=$l5MWM<_OGta6T~Z@9Z`lIts)Aj`J1IA*z}<$od-7kmCQ@>= zwJptW+iVBY(r?4NV_XTKF+g-lVDY)5))Q+qI!7_ZC55v`3vJJC#S($F;$?-PC7QfM zVqQyA8i^JnY@SJ0N>yphX?BpOHV5bVn?Lhw{HH(oC(zwV8iNbq^4aWGbk!wct&F3! zqGke+rkQJQ$BN&D)^f~oweCYJ9Oay3vX|Aa6Ew{RZFl;Cs~dsP9t#Xij@bih5Gc6-nPQ+U`mb!?K)~l9%_Qrn*%j zsVIwD_o7A$Hat1+$*NFdOx#kl5@O1{kxEMsR3o5m1fd=q)G9J*W(2JQ-tt3JjLcZ1 zNIrMGD(g`zjHRVY3U!S2sI-`-MIaZ4(?(Ix{Y2~>bWq94d5lF~NR{C=N9r~3Qr0zQ zvSaH`vz{4+4F?n(5HY0O-*H+Pr-?Dn%r4+iuI4M=`}lj3v?NSENuQXrhcF3J)E zst3l4-$0~Gxa}8~c|-|`9vexBkuZmxjxn(;6JeZL9yy+T;V#RQbGq$@ZYu?xgj7@E z&egYZdG{YN#SV$Wl6N(>M#f{|;^?!wE6s&td8RORW68!XZ?<2Y0k_CLl}6g-Du0GwN8WhlHTqmN9l&={^GC&M{O2b(~WS(bY2!v_I`N+k^DI%%(T?olSCX&4D zhr`6#c{-A990Sv|pfNB{BNjv+!hyT@FX)FYk1r?YX-f1ar>UeJ3k<#E{=>U~=WsaS zL^&LmWE?TN6C*m0SX6@mzLf2+Vp1?`obd7D;z3{3TDRy?M? zoMG)|)YEzZcEn(TQ{&agV#AI8Yrn07pvX_}^r9S(sbb5`n3;Sx>u1_t)uaAzk((8z zi{Y3d*F{UAK+VjXmP($i5$$HIML9R`U={$&>!$4%iM~Z5D+qR5i#aCXrg!KcX+l=e+m`(|yH{WXEZ1l+Po z25l8wZY$8X3QsTl)Nb{$hn3o2{;@juv+1C=t#$l`OC3GiApdO&NnsFLJ%p7 zFJLS3OA9C7zhE0cPWSo@!6gg->fivgX?1V^UKXu$0l*4|(yM-_dIxo!X z=(*bHDae_tcCPgh^~2AH4;^l*UZNhW>N5hl%E$g=bM{yu1#uIdcI`!m{Gur zs3}D$v!ZntY9CE`x4sJPwS7nnDM(Uw3~_;>>-cLw`qTW!fA$B_B_MuId4UQlO{00@ zT7(~#m#ryOxr|%11}oIl0rLJst@&dB zgXD3vCN`;xImR>d7_zcmj!x5TqlgybUjMcRqo_F@Ats8hs>QfeJm*L5d-)Qn~ry;shyd1LYyEK;`I1@+D`;lT@)= z@HlY@E-9JnIDPUA^SyiEM^dCgg2~wO)mE0Rmj6oA8oV@~R~}!%c6+hCuxhR0o6^V) z>%Na=H&ghF;@{Hxw#&TF+tV$Z(;Y+K^U7ymq8o^bvvJf?ls4iEv{#PN>epCx^wlT?#S*5q%`~>7{tf92e^#)Y-&n0tM%*K+QVcoyf)T%# zNZlY784hUIwe7lnbhU07({$awEsA1f!O|Km?ZakZ^KQ1oK;vkv<#t1uD$#15Y7KME zSjyiOwN6id`qOXm4Zr^!q+^*cQAr_^PL#vcp#%<-Cu-#JTW`XP@5(t}jvx3_|0aL` z@BJ_j&mAX2M~uqbSCJ=&gmVw?d*1V6&&i-XJ}g{1;j^EC=iejvPKnXcN4T1S5IIba z!3DNnxl+e=qinmtG{WIph-Z=B&D`x~^wP16f^&|ip9;M8Rqq5 zw5zqT6&X|7zlJOg_?W_#PZ)7A$uFf{&FdG_Ud1IVYB{p0j3X2Im0`D3gKNfQN|Mb< z>)+B^LyyWei{diAkeOERJb(A6ewjc2{ojq-oDyc2^7vH?4L|08bwK%;GBd1}ux?E< zo>`VdUiD&pqwx6TeaiJ4vSN)=@~Rb6`tw>iaGlZ*5jjyjj?HFCXIPIj^P;&VKw`q` zJ@m;NM(5D3gU%y~=~cSi>}Ro(7^vIc+x(iyWh@6;$mtGAxlHFN=X3*-Vya7kIgoOY zQ$^-DE-)_X>^QH<$m0apclb?5x82ZhH@JKpl0XWfrz$4x|K?FQ*fNE`#_(4@-NIGk z+gk5s+PeyM&)r0Wge;(2^j=cHwWM5_>=3dNZXWa!9*+{L)KJNB9!qTTCqE_GH6Nuh z9FX@7&=PiMyd-a69%EdRVsw}$#%a&2bIJ!-P)44A{EeKrbGmLw1qAb%=LKGs65_su ziLv_>T_1=#;}=DjK$slSlR|e@5hD9(X1_%CzcFz6WJlj`IPE)z&Siz%RB;-IIQNi9--$Ep7PG}jV({BSGNyUZeSg(q=1sDDywH29=xJo zYkK$2Vnc$Ti~ARxU!3sLr@xphv-`F&oO3J@oCoidg*j4bFC-aP%@|amBEgqik=OKu z*;`Zk(USTUSK!_E9k`TwvMeCtxN|Yk4+Ee3!YkZ=c#q4=k;By`Pd&WDG%e}uCFhu? z1Dhd5W6b-Js6q%Kg|s`z64DBUsO)wrC2k3+8oO%ba2Po`-EeZY0a6s5pvCi_Lgpjq zr)Q`N)0ECYk0G6_4xVM6@g1DsIb#m6zq%%drO2N#!)qtX`MnFy&Q3W@shD91k!hS6 zdY9>1BupcoXc;9ry2cSzQ#4aHUKjZ*%bKBx2--Lq$s)&1g>^06R$Z+;+rn_PR_SC#pn8v0TP4D!>7%vl z))$1%(ybD5P%yU$Ma5m$TsJ|?xC10%tP#+~yaQPna3&Nqb6#6z8f&#S3$YSH*9@!( zOm&*;JwB#WtBy-+2G zqT~>Fp9?FQu@jg*s!uJArR3QZL{lXvl&bRpO zPu}BVx8oBZ|4qL7t3HJD9U%rDK77a@|K9KBw?6W7Jpas&z89tl)1q{PaB|}4-NM^f z%JmG45gk2_Ju$%H;0Vz{P;?PGS-3u=qBmXk!mATbhE&b{LIX?hcq1qe`WcCW4=^b7 z8_#e2_QD_fApGj@2-`uZg@QS73{diqC8t$O;OThgcOuyTnSM#Ev*WaDoK&I zBWq%Vs|}GiLvD$LaZMUXGk)v0Aa8D{6vr%=k7-NAL;EcPlXm>gAO11^!$1FL2=fGS zszt;s(&U&wHtUr z-Y3`+YIH&av&Dc!hPiBTOUs@%DK4SO%)OG&B7ubMYqLn;;* zEXO?eZMo@DA&BHq_-x$Jc!U3Fa>YS@kC;xaNTKnukuxf+Xsmlx_ARH6Bw7i}xG+sK z(=-vn%&apG#d~Ep-{tP%y$t=9zU%Q_k9ddAMyL=Y9+ zdkj3DW|k##5aqNJ?r$B)2`5{RcaoHFaVQ?q4zvg9P&%L^7efH?bZ@IK*N5=G#gFpq&bkWRe7Bn*3A(*8g$a;T(m5~pbe(Yj-WmV$Gp}*?{sl1x#_LN?PX<(k>#GANC#fJ?m=}h# z9jY)*ffyG=Q=vJsBAY-tFbpR+CrooP_Jthg{@~$5P{%X}mJoAsXqcykah%wlrlZ+n zP{wIVh2`cs<>qTT%lyuRdmx^x%ROP4lL0Cl+C(FrQ#P9&7k5(L`Tj7aLUhwi-?_Z+ zRunEET}od`+v5l^CfEkNmy`e! zp|@hqD_v4t8X>*3)m%8HCdW!VWh6yjOZ?SUW!SO$6tFN1Gz}{6prt~WIp5gWN)Reg z9FH^It44+D^(#uZOGMQ>@cFNZ+4=2z5{}cc15)l6CE{px&9K{s!6SY)MI?IQ9hBh9Hu6vT79#IYTHsb%jBD?=QPazXeSJ8V%^^6OD6nB^SmuD+Tdu_ zgh&GuRnfD(KhDo6Dq@MFJrZ!WDv{T{8bj|MRZ{fM+Eq?2zmw9^< zzU~_*7VQWD`b}ieg~J3(NaJ-rBH4TU*0tws6B!SV9Rb-z4!z@po521GF5ggkXA1Jn zsbBbqAA#o{z%7Su{~p|-Q)8AdBwja`~(9ZAZ$I4RYvV@MiIs@b6sWX&rhMbWAJL|7d$ zXYEg#HeUFje(GcV+kfoy+Qha28Uf_G}Kv&f%N3t*HQAOxI(`!_<^lk-dnhdA)241EsnVRHeq1V?O=81!kgf8%Cj$Ds{C3?Pm-Scfv zJ>Xlu^8?)7IHH{3#RD-Fk;?02IoTzYK5s>WJmy!-&Lt1x-~{hdfloP`39om#`n3n`QI{0*WSZ#_dPF)>Mcy10 zw0MT`s~I<+KxD%zeJ$fyOY&++>-wV=rHd$Fe-l5hGSs>UnqS#5V)BtG>_VeJ)aO@X z&#~;$HlO!>*bJOpoUs`?KK|RErr!*Wj*?zT4l0j^REW$wVINWi${T$ew{`VTD|4@Z zuUBvVy`yD{wki2{5xyFsj(QuT9?~>jUzbvFs3>VdnSfOeEV)x+mL4fWV%cMDe0tg} zh6!vY*liP$MLjc?sErE}Gg~37eUS z=IZMbYq+{1_1toyhRWx>l9;^<605Rg{faSgr*$Vvtn(T)GTr00eD5{>WOi$Fw5#v% zp6&I`9QFz;v`~`ws`WA}QuXq8;1@Qu*bsx(s8u2I$QNAZQDLc_Q!$xn>w&M#uRd#N zRem({Xiv6?t3{dez3Ber!OHyf$Ufb+9Y?(5%r810WXsFyg zc^M-T8{>p^tj%|$vrXYVO8aSEZ6Q5_-Bg8Z4cCPP<>yzlKfjX%s%9}|l;-5J$GC~oN=sQON%=?HrE4f+LGWz2@X0efk2BNNRq}vu z2V_J$8$192AOJ~3K~xyPx-t=ra3T<6Ic6@!?@>g*fU*U z6AuUWm)Fdf*QtKR&J)f&zW3~<=P<*Qy|UN9M?Q7Q@4WmLKcZjeFZ;ls{{9C9?@DT# z0_AM`W{uloX{NASZGd7Zu>x?VjzYt)A$gmN5}wZ@H{*m*e``|^PMf3Dn(t%<+6u*) z_!MI(5eR8}awD3;?o7t9d04Tx;yqH5N7Rb*QRj!rDNE`w1PF8FFfCk96Cq3p6G))< z>FBc^%F~x$LpN;juETd8>C!pbjwBZaRa{<&7y^6ugxA9r!;+#Jyg&zQgHp!d5R>B4 zU(CcWZ_wnoC*pDHQ9RIr`lOtv^CBs^=%V;1ABN#K5g81{8kuVyU6&BOAr>vouy>Ep zv;Ewn+yDQ2K=W9Vz_EI)RZ6N9wjN>(f%`8!#dbUJyPy67ecw}3Xd0U8T&k?EG1muB zNvH2Mr)O9QyBMC6?sa_%A5TqbsgGbMWPsp&QaF9*^WhUI3TE8z5x+sj@xt?WdHJ<# zf+l0kGEaC9SC#-&^G_MMOTJ9GG-4I`DsCAW( zG>75|A~m5}+YMe#E~~*u3vE@|TN$(!@T9rsBvq63PUyYRbwckQoljnC-&kiww8=p? zFMJ+P4y!B$Rhm5P(niiHFTj!+vz?C{omFV02zKkDb9vClz?)svh8dQ9Y=z#u`BI2- zMx?A{m+(IWt^y9n)*vloF2b*YjqP<~kdYQ{TA;IjR$rQ*Nz!lNZ%51c>p5et5o4g8zC^U#$*87%E9;1Z~9WBKX2-h`Vx281M)zZlH zR@1Z|zXoff(=g(8Jy*IOw+jbrkZRVYF=*JFq_k1u3)aKR|7iKQ9b2YUZMZEQtxZGC zGD5_tI8Qzj<kCya(Cw|Niv<#fvXK&sTiK2Tsx)UdL3WRbRr z*=(o5R&(79AeoM(rEVf$tOwUv+q0s!BC-WYb04K&12UnlHYY`qCxus6#zHywJf<-h zmQsqB=Go-F4X%<-XVU&~(qBWrB^-h{jt;l}t_!$#9wqzUV%&}FfHHO;dq z`OXyfW;Z28Z2iJ#UgkG``J;Tz*MAM#%}7vW$!nCuzKeoX=ol4mZ2ZPQEW)CsAe!;e zq0Z3_9oLthN1uP0C;J1tvs3Qgzr*hAl%aR{u0wn(kS5;MqJPmM8nThvst{RKc3c0T zbg}jL3a$fjX$~3#>hj4VK}%f}&{ALw>e=0@F;(H7ms~wL<%cfg#4<08;nOB3xe)#zTr=J{jnrJHua`;?>hHkr0JA1zx>8@X=4d$tPca!sQss9{rV{ z{v?0;2fmG=OJTN}Eio%PrOEq5!w(KKjpRxuh{7sH?E0c|ge^Qp57g*pwWupJ6LY2yd|%F{U>9qE|86!GC4bk}!T?wz9=NPhg(-&z>2 zH0ns*_&q<>fZ51IQZ6;$b#*V1#HOc0$6LvzX(kiY5?xqmbW;A#?w%(l^6@3(^#R}a zH{=l|Rd8Q|rjs2ypS;qGqG7>hFS12E7$tX-&P?z6ju=B$?2lvvphUms{}S9LMK>9I zl=r;%9^)ds`Q*T+7bGmi7`b|~Cx*awH{|0^JH}ZF^TOuNc~YLoDa|)ThV2INjxa4O z)57kgqw7-T_7GB3N}OlT@17;EdcHdIJP)tbJqaVE}(r!spHnFP>ZHe4^OtLIv6@f z-wA!^=zF2_**e=;`7AHp)~57jR=#|^QWVLRC?$u^=ZcA4Rn*ISnsTRHl&q*X@aw7( z#Eib$tfP63C3AQ?XV*dyYeitnda_#cYMG2$=hAE@CY+agNyoT%EWg_SE~f#wW)F@& zjjWjZiuHODTGP(25abv`nlV)sP|H+pJS9J6Me5u4t7oGfUwzWryh(A6m*$xDuHo0} zk{VHI)?>wF*YB=IBnh=f$dc3I#~KQqq4+zIof^b7O>Haqa0V;q(5{)5U-{0f}z zSkcs0a9b^Hv)=0>adPzdnzk#IDHszgX(&y2TVZI$V@_4hoA#>WOyL)Hzl6XY9rJ=v z3(I9i-I!NBiYzr!;I65$QD_~;ryQl=L#BH-P{G=;dLLP-t_4yXURQITYQ&NeGP|!7 z<=oXgr^=VK8D{s%*aa1GyaTFs)My_ zUl5wQzkr=@j*@bF1@7Fx;OuP6lP|x{mwxLr{Ps`$GV=Tbo_X)nT)gmrvpW}bn+?8m z$xAK{UjeVs4E3hGQ_{)MRA|im8a8PBQb@-YF4{yG0ai@U@~_i5#;JnxG6j|;g{Mx3 ziE)}4$BD!B6^|cZarO2Uj~`!hI2`E1#Bd_`)1L0xb2fKhz;7;a-3Fp3PJ2H8`B(V| z|Kf|_gqPmD;*gJ`O(hkoV75p3%$E;*^#>jz`S_@~GMEUO;nwJ_tx=bdw-m zuIioEwcEd&{pRCDwhe(5HkCHDkiy$!31bh1 zwrjoL$}+z5T->{Z#>gwb`zrlrb3CHbhVPS7E`49dvK<^(*MSq4WZlGH;_9@4sFCTg zXR|w@8iQ(V)8vpeDJ&N0!Z@Na zB*$K}Au%dlXS^!WFK5-E4MC}gULZAESd}$=-Xd?baQ(0P6nH87di21t(kpPYkkVSL zxGDQ;6$njMDNPuCZRc_`#Jt5rW9{h8{z?kyk?cxqw6`y-X`~L?+h`p_c|xFTtE|`j z4_SAZg(}GcL+fFe_ODG;>fhDkHJ&idgxqrX$`P8h?>53ssW9IPdwHU^anUnvNFgMKw%3VQz@bg>k1dgqo_KaZxv%J+SI)85MiHJZ38HPOuV zSny~;_eMLLr3P{Ml8+bvK{IQGh`3s+Ajwx=Gi8|EpdP~6;)=tOjm@A{r6y!7f_ z4tMug?SF_rj>v^m77v{VAB357oXi}1 zF0Ygizgzh7Bc=D1hTit6w{G9XYAChqQo^Z=+|~kotdS*>iJEPZ(WRCT+}izmgyp7W zFG0&Et5@N* zhItzCrYQLKP2R8gc22_w7_Xi z9jKM?RW*D5Av!H^WX6Of&^Yp-6YMXFF3K_mrg`QN4yY`+VF9PO&U2WQfArB;*~gv_ zT)dA>*E9HzuJ`nv%X#9GhbOsmI!RF>vhN-v(oRsK8rYoX_JNNvw^}#E-z?)#FkD@B2fLse(YZ+3GV66VD$Il(X{~8aQ zCr!3!-YfFpZ36o(?dYn{M%ZcJU9sCr$#pf4`i}A9B4MbXhqj{x7Pvk*MJAvj&agD$Wl>GHp zP;X&D+F#8O3Jl#60;B843PHHRNN>)i8Xs;)RzSTa<-Cygc0=+%a%05xc5XvS@cG5p zB5%C5aR2F!WfHm}%_n4mH^d~|ZSE=G_kEA~ksrOo$I$iSNH+vt5Y6(+$deh=kynRoM8L6b%LElJdUoV#<*X z!XiSyg=L?VD+hENV1~{su@kyMxZgW|`k%p9Jr8d_Vde2yr(Wc>C`fMm07Y<3mGKo~ucWir zkC<~&&a;AIbSqVxXPtj;oeAr3b-NxaA}iEm>YU`z@BH2HNoh{i$nV-YIEB!)+aS4Z z$R};GQeqRjDU86Q=iu5RJixz zb9{Jn%2N-Y;?tjag)hGL7XS3q|B^T7$k+bKZ{a>);$pmH*D1s48E5xS+3il)ZhE{I zg7eUI1aVlU{Zi*5{gY^R_KJm=!qy3ad7L=R3;S_I$BE0w2Ohumgyrf9hr_}&&&(W< z&6e)|2|PIG^ebQF{QMsM_MGT;bUvM(t**nl4lgNEKvZElaCP;FU;52Y@}ocZ+w2!z zdGBvf_9}kx{;zx?;i3qbV4Ke$HyEoY3p6x~`rBlpC{+$h%6VQ7&7Sh8rTV(j&X|?? zR0=_ou|~5(p24deuSU%~*UUq5YI%kuYG_-5^ZX2X47q~4T2U%97Q_l?#n3=nU(GUx zX~V45bTX#!mkpK@ts@PtMA@n3(LCpz)_PCv|sEuHV^dr#LphTh}F;WV|k5D7(~ zk3=E|{~Fnkc!Q^FfjiCOrm(0oMrGFrbSxDvNuG0$N~+wRj#~4i;Ce?2&sLmxHq+Co z)<{d0g`DGi3)(fjvpkHh`Ry^#wGxejtcZD$P!cW8sTG!EY|es8&YSD#?R`dNb9%zX zoiiT4`Is0Z-uv}X$`H~T`_3ckK!u@Ca;$}6_Pu93g!Ft&-sTvk6ccpL6*^vMWT8(@ z`b-i;ixRrsb-e4{PxHA~p5S6+J2=8TGl!J#o{l?{f6k{1T%4ZbM3}S3J>|SlDP;b9 z$1n^W4h!>mUH35?B1K^6gtNQ%KspZl0~#aKGUL4z+O>p0oM(_)H_PB7L~=wc#H>Rl z-KO2V$`V~}^N5{Oh4DsGYgmrStT!A~M>4_cj;>)>)u6I%N1A(eUbon_8cC>*(zMT8 zq?4o3$U9uG@7zGwG6yPdt=MRz>*g+wqm)}C$I8%PBv_zJLcSv6XuaunESltAi?Cu% zwnB_ePwHeE6l*1EO7Rmj|2D+i7#UE#2Noh|h2xB==1E}$_#Hl$uV{*-msB8P=n?_x zdtvB(QP7xtHNFve3j?iY+8Z+ER;HD3VJ4PLQ!rL z?`&;7Quo3vw9GL=1T@n)fwBkVO;}1B9%bHv{~dIkx@P z=gW`^t~98sb*-8~=W6&Zp(qe{gKgP7+u~=d{_>Bij5dWQ?6Ib?@=9Tpw|@!_mw&P% z(h#@S(r=iWWQuCwlBSL0oSC+t_1Nk0|5Oy4U7gTc{(CS?a#5UM`y@ zMbJE&P2+Wx^}b3wzPdm5=%csc(HpRgsYqOm4i<;_ITdVMTs~!C=9%{e{?xy7&CmSI zz$ZT0;erri;7hN3k;C`}P{nDYPr-F`i}KCixbUxi*A@4k4oq`CHf4ro@pu>MJMwwk z%1#o_ZXAS^yMfn8KSP*dI8~NQm~$PAjZ5!(4@*#Xrybw)Re`_rQ_9&^cyg^}|BE@} zNJeXEukB@ld1nf(tk%!g)u7GU8q3WsGf#18Ls$caHsdgYq)9W-a%``2y^TV9 zvkr$@9SfBkHAA;8cjm2H zh=61P-ky=Be=rtJS=vB3rcJb5kEH!6UUf}p zE8Fjw;VLSrh+W3}bTpI}!;~Fk7(-;77v|}}Odt%J9VQ)<3;f8hzC>`!$riree~8X^ zIPdYjW7Bzh?{El&G6t%nM;z=(?T|mcp-M2=to)Rb_vj&MtS} zb9H%1G7c(T-_dV2xj0-pZ@uqL5f6m0;9LrMPf-FP=gp_^_bJ!G7wPWwp;f51DZqQJ;}l7gycP+CS#^_jzVKB z|Gf7u?YWpSDMu|e`gGz3| z{;@o3->O4Wnyal;B;~nc2qQ~8Ze4-UctQpZIM@+8?7hZF5aqKHjJZq#H1neBE>RWhFlh;ws3>-7(uZ$ zJc9AswXEJ-eL12qt-^l`jVkCOh95UjN816e2eQJs+lJaO@S`cLem6gE!_d}v(&G6| zV%p&OYO;A}a`BHNG^~SyQV_9P$XB<^yJ|VP##GR{e>WzO-rMgZpZ?6uH+=Je9zr?` zT_PHUFoExrU>u_oLeGO|FZtu&v*F&w37`2~&(&k)$+h5|;-WH-%HBENb3gF4-!StB zzjH*olqc(bvYtP_>e#wHPo^EaZJJjUkd4A55Yn7QQ`of*upLs4{PraB>KmS6D-a`# z3a)cpob^l+xc_v=a}O3i^ODkeD9rG9Gj9k@rnp+Dbmfi~;wgrZh7W}#RV%u+QRx(w zIG4SzIDE`_)s)37Jea|E#aF0wq!h($8e7v-n}@-?G~=Y^q#vay84W8bRcZytw7zF< zQ_FofKG5>QLciLX8iG5D8$%?8%Qr+r&{Q8JrvRqVPA&A?hEo2uy~>}?2|x0&&+~o1 z`cXdkwI9U!IlUi_u|W&HjAems7`AR1;mwu%sX|G?MsjUWC1_rKzOAfyu&Vj#{Fa~OGh98+P2Ipt$|4#YVr zhZ`?!x(@YyQh>Ui=r=^~@!}c$fVdu9mm(CDtaK}JnRxq4pJQ`&%I?l7ogcEdF9TFU zdcrcXEC((x-{hA+`U!sFpMQc+zWk`HTuD(=+ki$Oqwa?&{OAA1H{r&aAP%|?mRRgj zo2w3Vx=Y(T-Kq zs(?o`2;`KAaxZGG?w%F+JU8>&C-kYg?=V17y53&iQ_N@ z{=u)ljO!ftHjdZb!u!Kl)4QIb^KAN#zH>=Fvvf#X>-;4EL1sdGrfNa|qkQlCJwZAE z03ZNKL_t)H3u*lMh`DY_#*=i$4@j=!t{%?<=a_PRjAdDv_6G#z^!xoiaYMU$W}aGKZ5gz&ol+#ONHJ|#x2!$LssGkmPzO?l3N6g+CehLLS2coOf7ko% z21!QB3<9NN%dNr7lkU6RRJ;&%x9t&HRVky z>AYmEn`cC|&Gi-$oGH!g${i?;N4oXcAdth>5)zxTo_5jY@XT_SuNt6JLhqwm74g(w zRjjl8Vw_GX1&gfOa6x;iaBS7(#m3q|MCzC3uO5NGJA|Gbt;LGDG!LomO9i|JXB5-c zZ!;CGnV4FQDZ#aHSd2WHV zTc%l*;q=|S`0F<+iP59UYTa3-SYnSqwtrFAEg!RF8@3@`8#l5xN~~eD@8Ff!_rx&J z_vsiP$yF1hCW5CSht`FesKVW61Ap@S-{$J=j<;Ux_|zwQo_;#;>T4bEdH2Y62PgN0 z)ANaGNhEWcBQbU?(Q|$hh*O7?%*63{jm%LPHp<}uTLLk_6dchj%)G3P9BRY6EC~CkK6=r*ACj_KeXu7cJ?wtY5n(ZF){U z;e>S}a^YQd((5`d77j1#b&U;4x6~#pHkZBX$AnP|ol5w4u^sp?|MrjbWB={@2z?Jr z010*7G$zlbOF|#6$`g{;NMZvt<~6XnWU0D%9jwk?=Wx=~v*qM&Nm1TGufmC|hUH{1Y4BNm+QV$*b2+jv z34ac&pYBOTl0pa&C#L;le(L9cg`fG^kMbnKw}10j@!sd3=Ig)uE7|egeMCtMtag6934K9}VcERL zwh4tV8(flez;l?n70*iEavDXw=8&fxb5p$2_F2wp33t!Mw@O&MRi8FR+StA+kjVzoX=WNH=4D~&1eFC(WIqW%_lYkdy>LEwdLMbw zKj312&fq&XeOKxO6}e2clpM0Owm@WZQ$CY^{i$BKhNBkQB=3QbB=5R;&{Md(mJ05v z7@}k)J;_Na+_)FV$^0JP+J3aE=Y>T!(Pa%exIuCCh_`8->pOia{8n1DqMc3StGYrB z?=I0aXyLit;CcGl`+V_pFQY+|L5#*Yobi`Dzo>#IvKu^eSdy&kB&SojBIBe;)^vR> z$K-vG)L!-Yw|w7VRtRC?*=O(b|IzmDv9@ICebDb)tE%?i=bZcO+cVvh?(w`mV|(z} zOagYmV8;$2jtIm+Ac{a>BCvSK5=baW280w6DRv};gfNc;34$Pj1dL2x#vueF$2M{N z%Gl$vC-d;k^t*5Oz4x45wbuIh<9n>HYVX^298s;?d!O1>t5&V`y`Nus=^<_&J;KGE zJ8DD{ydV%ttxmI>ddE{vxOw;lDM?$m>+2(`hQbfyh;bYMBGgilQo=Y&>C2*NHz@(b zGzPe-04J0p`RcV4iOStgQki`&T2yX9treHIFOh~3$D0|g7FEs}V4-Q;8g_>XS9fot zHpb0y1~&<-A2NxTyERZ-x+teNdikUFB6 zX?vtFuo8SX{;Fk4fVU>-~@E2PQ!LU^|M0CXKkQnyDU;{?H*{!rILV`X9 z`^(wFzzrm^fYSN*HVzxmpP5TP^Uue{&fg^{PtRfr?2vfr5xsKW}PLTVsNBg)b+9YzcT0aUOo zGG47fEeXpEq|88`ka7_wnSlo{T;tyJ3pfq<09}J9qqTyRGnSgss#N_YCM-pY&n+c` z2Er|f0GydIw~RxU{K(5a@Z@H|b!*7XSZ1L?i?)xdbHmlHA~)dJ23!w}feV(l$NgIg z_wUSj?k@1!+o#S_3UIgHT?FEV^}l#y5VEY+Mud*FMZNE%(=c8ANaB4T7{Y8!YcS?y z71@)8;QMa(L5#0(pAD$fXq!8S--N(1$jhmB-r+{RmcU7>N=wTpeHP&LtX7_Ur4Nqx zj1Gq$4rDZiG*>+ZCB4%iN+jXc@jt}_&jEHGV*j(H=}h~LQt*R+=6}N<`h)*8co9Jj zrNNEk4EKz?;TBNuU|;E;GaPABPXTf@U4KtQLc)acf^m4}4sKsxV}3M)7Jw`8k33lL zLtlOiKl``8jGz76Uk313vCqJV-v0o1@7}}Vu)}UQ0+exiIpVqdx3Hgf7F6+pwT0hLDl)E8eu8i2dws6`bQyeEAS14azPfH9{Y-G~HI z&ptR9i}hn6FY0jSh&+D1zsx|%1;h!Agr*94w$OI6PgBm5%8DYdrIc~ioYQo{iPzf!7kAdTpQgDMN_4<{jAs{k3%qvP`70tybK*cMosA{x+^3JwYCZ^}GVl zu`-@_}fy{-MvrR|=jZq(W^ zOcPpJq_|saC`-Zp`?s(Z#_?vxet(cXUn-b^Va(D#G9^*8YsEN8dWGPD{2QlHDy>@x zJV}A)`KSfp<|A%DxCannnX44DEEQ!wVw^^d(+wBY6ooob8!xw|>L$}ajP!AkznwU4>0*~N zqOP!IQo%#hDv^3^1W0RH5x>ZkS(hEbLE@p3^6G9)hb5{$wTbP+;4nWUCr**hOFZLnf zu6BDbj6gxL6NI+*XekSEPu4z-Ix%p8bS3y4bHOvLt7e5W8UwX3meR1y4W)>pZoDREh_{6GS-P<#;RRk@-HO&(-_*=n{j@>y z=P9&YYk-sPfY7#Xt&uXNjve;g#*#-X0tH*DXBP&290F?{--Z}gZF|^}70{5mNX_-B z&VRb3xxyrupSddo&Y!6>&(o0YK9AKC1Lg3`JIMNXdrx0G{|w6JYI$Wt`}ln_Tkoa` z&~{aPf-~Hem2OK?{EbhJQ{oc{3HLchn_654iVLz?gcyb6VnNpiPUwU`VznmcUL`OJ zBE0_kf>&Qnc<(I&Xh508vzT`Tu0XkI7)C-G8B7vCx)fl{MT}gOkcpAU1|Xx%33W-J zfsjxzWkTfv12=F1_EW{OBw!??^ffvTe6~q;3oN#BHaU?Zp zEeX>E6$&eAy8-fmPkd~THy;V#vPF0#R_enyb(IQMVc{p zueueCXAy6sQNTkmNzk$$JfE;&dxjbDdE5Q z=}+T#e#6h;g^zqxy~(X_msNAO3Sbr`)!=;9{wKmb`P@j0<+0)wLHG+G>z0NQdC%Bg z>~VGb5;u1*Fu!%gQYt=n>jFRi`ZZoYE)Iq_75Mqjyb9n|{Qr5J*0K0aUQ6Uk=ZXj^ zUR(_s-~YQlj(cDCJ`Cd|dEOA7S~Wyn48{qbL*Aqj7);59?P~+8#jPv$2+Dc$Fmnci z2fG`-Ef-yaLPDN-ZCai{6!yA$cJP)1u7o#Jz*R=DG0iZ*_O8qx`Cx_uB9SAwYWg4d zInSkHE&`)7#DF#|4P`grQ=fYoH%rCkB%gbbj^hUSuPDp4mwfBt{lML<25J%j=92u>H zn30#jx3{7Br~G++xbhueZ6yc9a~r;@56?r!FlD^*#g|2Cby*^Wr6bfT(IHrs)m9mU zDxaRwq^&?oEMf6oLxCxvRnx6<4n}9NuUUuP&Px?cIvi~QawGS$!t2>Tso z9%PQE!nyunB%ou3{S@=d$=~&9hu7x`GnDr`efs%P*7(Ufhm+Sj8R4AZ)_<{MaDnV8 zvxruNh|k^f6Mi6F4(dzR2@9>|adUF8!CsiCjvM`Orqso@Wt8BGrx6SC4YY8mj^q|% z<8+GP%*cR~t*{$7IPh5tlNhwARweV0>yvpWge&R2o$wIBTc=5XTzN{|po%7~LZyBYKu zvM90X-gB%y=ytinoXEWPfUtj2soVVK%IO*he8yp8`?#%Nyb7J-4&O{`WL{b20>4E# zPvJVd$7a5rJM1|bvj&+C_sJ|P>`Jb~gtg-EM3fmK_>2g0En6Cd4 z=0oVj4?b7LXBDW}=J-_dw;^L|b8MK4d`}5@^$p-lUo3d`-4hO1k1*~VmXa~#ibvNO zd8|NUl$wzT#yn@B9kCn<<36EU&bTUoIT4r_5D(x1SZ2a7EU0ZlnhLliOk+iD126!4 zYB(04A!BX{I3B^{g#9Jq<}JojfSV=bkQQJXF%C0Q%GhzkoCiE}3mEr%ymbxmE+a3% zCqI_(=BtF4UT4fDz{8nOw)J;oAbDsxlzAn!ww>tOwqMG4V?+gQ0YsVGYBvG`L}pH{ z<2omR(3~c9h|ue+grRd~=fXlWt;%l$r3}xdr!WUWW&rHMVaMe_yU}UA4hd&$Ay1wn zB2ySg;h%k-?O*qvdsTYBRbjWW)GBF7ii|cqWQG+=*uz~SnE!{sBC zWyamZ4xhMtfxrCHLx1Nj8H~r%&d1}O?q6NEGHyP7l+{6``wR5@0~Tli{M>MgvzHqD*3 z@k0))$N`Xi^v0;vE5J9!3-7CL-grR0nldl$p^INn?jlkWkuB zRm^k2qCKU@q2Z<^OoxnLdG#S)xjAAQGKNgpjTt!+u8HxU`ZY)-=W`~PIn3bKlww$T zyKNOa5dQ|?WMi#w#NC6=63(6|L;BrHPP!@Sl*}dEU}+ecCJg*8Fg;o+vVnW88TpE% z_Ae%vn%ua8;4}Phdz}4sMaG05z@N?PkBi9qOVPF0z2b&8-@xS5 z+E{CmlG7P6#96e7(1=6~=A`_)`F;c9fgi@L%&8k}5x%@qIlE{LtoT(|j8|J50MOv$ z6i3Zb=eI{V2*S9b)hUek2l5K8!d$mgg27imYqHS_#m+FH?Wu^vwhnyuTARf?(I>q<2? zmc0%T7A^xc^>SxbYFlH})R0^9ummGwDFKZ%*W3sT1KyVxYv43%uJ;{fQ$uyle2>LF>6KmockKdJBYP0XF@5ucbKn_|-nj7%ue??&LehZPQwrqq$aUWAD?WQOl{bAN8vGf4>w zhkfhsX7Asn@jlrtf^RSbeK*$zCihO-bw0?s@b2vyKEa_F31Ak$uH>%oe_BXBZLc}Q z^j!(Znn=cZY{!giw2GkFQh25AagSHCVyqmNkx6%vKz@zU0+fK)-vYky#fFc3tfDnS z9)LWEFrBF)4H5;ym66AaC(8h6G1U3UXhhhj8AGZlHAy>|%pjTp+GE@;SmqJaPU~P~ z#&MC}(aeOUDFY-znivc3kmiE7%7lvxfNmPbTJYouq><5v5fc{>2zL$y8b;7Wc-QS6 z9)FJT^7VqB`o)TIPq?}O-gs+G?tMR%?z@pIhG~bt&6pVF)3n-HUGvC@4ZbMgY20@k zQwQEUx7i3(gN8jp2?>ERdF=~WT1;hdX*ou2A@nQ>9+ z`d5~U|Lo8IF#fea@J~s#;>sFcZ0||3e%fXT+9C^-gnkR4I2Dq$X!<@Kc#P%w`=^9q z8c;8Gn654`-P&PzJmYX+eEiM@{_ocw;YoR?P~#3aP-|~qUaQU(KfhY-=XX(psd7lEi6)4peSILAh zPq@H#OTihyq41i8v4h?F?p4>Dd5Nhj%COM(kfYwD{gipon|c5nsS>Vm&njayh%yhA zgBoc*yM?oB6?eFyfc8>+mNlP>bgRGdOK`736L19RFH^WWl69|d=b8R#!RHU_G^6iF!SHg5g9WF zzPv8-;cIieIf7YDAdxO^C)}B`I;)aIcS3TX%ThoP?=0zc9tjJ0e{SR9=Ik;(grG)p zea@}h2fX#@DEZZiynWg*h@Y`Gt22{Oc>(2ws^RLFmzTJ?IRf0o@IZu88;F2$Op4Fq zfd^Q_-AVhbR}2b=+<;OmEFx@Kq(Xa{XIva63}cp*j#&!BEyrt&`vdYYU_Q=Zu4-&e zXszm+6y&tS)t%d@wPHCI)MjfZ>y`lUa&$Ur#T&ClOX*RYwpC*x;gvn%%LF>VmbE@b zc88}I~;>qvY5#Wf4OR5z+gyh#6uZ|eK!K#mF1O4 zy_%tWB&=d?6b>K(z?MJfx7t6;=`+PY3Zy>89ux2uyV;akW3;6)N|A8%R*ayUg02Kh0hJShnpA~$p$xK~wXuY>FA~=7 zMH>iG+%6}v5|V9&3VozK5%pPS!89h%fc8qa-xQO`JUE24avs(t$1#{a{mZc#GCr1DCFoD?)J(V~NANLLU`VQe;thb!0IsZ*tBhpYAm~^)v@Q zcLw1RUj}A$IDZMPANLMNWNMB|uqpHUj@>Hz8xWkg#LXW7IDEL_ zDbjHJdqa@7;*9ONJ!?y*qiw=S%HR1M&b{)d*HfEpbRW?19lacBjM`m8ejd9+xVNmFykA)Cgacjr5VhGaRTOL z1D8q%dq_JRr=3p5l@0XR@PTxL;!c|ID>-a^cU-wAgmaxH+n$@Z*^9DSB5WdX28hW! zVHk1Y%4kKye_dfz<%(EhXj2ve-Dt%MWY~p4d1$~zTY@Bu)@x*PV8|QRPX?RhIM&-v z0+NSHA;cZ4)FM!{m5@KfTThv%b)}?t!<%_$InaM133dN7zw`xs%TNCtKKylGiWdj*=)+Gz`f53FEB`jJFQhy?KqPHr&44 z;lq~`e&*rz)8v}d<0&-8MVKHhZ6ymkc9d{I35R5Pb2@wFg#=m`NF!hbI==-_Qx6fZ zzJ}6|_}}}c58>NC@jhIA_(i14Tk2U(;!P{kYRN*!Z66yT3b*n$wta;0cs*}MDb_0M zO&kKvL*(Jb-WriR6wIi=Y|07y+$(~T?w)=0B5(TKYR`qJ`{qfn44rRtkGmD5^TKqI zRw!&%Fj|%9yjm+-tyn5!;fiS>q#f|ZHy+`0Z#>5RTYKbHgo*4b?2p6|b=q}ACqtewppWQsYd_gv>-cts@b-*$C}GPd=j zHWTnDy)%ZXE)UUW?kk4Ang2T+jzs<9sFZLV-8P{%B(k9Bjd_17t0)g?tTbJxn1_sI zX+4x(nNlao8hPM*MXIzmFlU`E`Cg_3TwgD+c9mpEtj}UVa>DbVv|jF$D3^w58u0q- zZy^t(v@~NzElpZ0r=h1}wyNRnGIp&}s3fJVe^=PWr8F%JS5aHV<*nNQ66Se9DHU~D z07{sq9crm)t)Mm8*Ct2}1|w0%)t%b_0n4#~S&ala(6Ty%wfLSf-@$b#+cw#&oJd-| znhdR;MS|tWv29(a=YZ=?&btF_#_thqUEc1ap1m%BOMs(@92A~&0`)+E!}YjpSuk>k zDDRv{6k1*9jvuEpVZ4Ap+o59W=2@KNxHisd&rN7EGr)T%xKf0*GT<9(T+Bk2IlPEJ zmz}tfPPhwfScPK~APR8|Gh~QW&rCq^JuJ4J`sIBYNd+EsCAS%Km+OciIogf1YNLG$ zqRDkaAW~57NMu9F^(JV$Sy4G=mZB4hy-K@R zV>Ju6B2H|xA*As*0(4c#IpEkPq%&Fyb{p1<_6R zEEsZ3s5Hgg74$Oz=-(^{-^SJPk{90F=8@29|Ko^8SR%3M-{brd1pE%FvG|PpTOx5n zTx(r6(^f2mSMN6*@ON8TF9Ruwczbr8)`Z5lBNzhKlj_%kO2aFelzx01B`0VF3oKR`5uaPYUxjf`F^TY_mww&G4?csz0Ha(rf zg-W9lM2f8np=Prvv)(yiOzQdPi%zlRus`ek7eMZb!ReZN~ zNCZ*IlG8ULi55uXfazk7X}`y`A2BW!7l#R7d-n=I^YGe1?VZEVpME&VZtm?O8R04q zxHpWrVDYGmHyGY884PbdR%z?awxEvg4JHCAF%mNhGwuut|M5Tk)%e6Gp2z;V`^Yc6 z2MwYgwpvh^0xs1RWLLhG?zh_9AnoaNq@Jg5Z9@41O_su6s@nMIK{4|r>qa}F=q zCI);}&bf!J`@Gncave|SXx@7B*f(gwIqBOb9(+O%y|>VIbY2g|!PKN^ruaD;s4|X) zaYVzkZz!Db>Z_0O*~gD?wcoAq$&QAG`}_iS7%`-*@=ofapFUj8PQgg7HP?AWul`6j zHHAan2Si6wKT^}CMv%5@tYPjT0#lM&Ka_&ei$K)WLoQ0@SoZ)5fN2HC!oD_m5GTf6 zVRkl$)z5IRcR+-5N>C6?R*UP;Cv>m=$+LPGy7Af-Z#a2WPh)f1&fE)tX2LjRzdET< z*`sSk_HLW$cKO>*k|@+;T1lcQB~|RZ?je22lc$`FW5!Y{%Cca8IB00P!1uJ9)BvUP zCh4`FQd0T5BB!KlCtmwnL{ToKfLoJsFLuadMyU<+v4C4as|EXuz32s{dLdBZheAcb zjQ!OujMIo^DF7NuZ5RjJSVn3ET$avjU10345dt)yyqjOZO%!mpJz)nAyKk^_dT>>2k!5=(+{2sm_I-2e9iBiZ-_}E&c1XY) zuQ%({f1;CX*w3!9%WfHb)%5h!tPZA>yFkZw2?tOKFQwy>U(?5Rg5yf6zPJI4g+WFF z76d7#eqYmhxge-YuD!B*NZ9TXCB3P#nv#0ZNi5XK1QI7u(&DrYlFywibTPapGqkEB z)T-97%#2zEnWn)i)3JM3k{K%iby$mayelv4aw4I7-E$`C-9BavBOwoJg-mN8@f3j6 zfx2nlORehZvBFEr6o?3vjZo2wU`Cj*$5=?8zX#htayYTyd>o1l3!P!;g8DoRn#eBP zz)l1Y{U3*C_GJuszCT^LLcb1hhGawQXn~-e&VnKM z_2dZr=wEC2ny;U*-%S{H3-T~)7XwCZgv+Z%7&$6RAzbWK0knQtH3O*F4@b<)ggg?a zG-F-{*10JDQ<%RmmKoLnpC46rMH^PJIobP> ze|rA84QCEFEuxtcq{6XYLq=naZxDHr&RDJ3=fe{9)fzw*u3&oDJb;=Af9!wwVSL|r z|9-Un09;o|59m8C<2uiY#48Zf0$5uVR-~i|RH4Wp5s8An8?ieaaI+t=zgcj#AMv3( z2kdgfvFaS2rkuNhafRYfJ?y$ng!{t|cZU&s00Vc$KrHH7pMWJiyMj=_7}ykB^8};U z!GL(3XNWQArWJhhYQXpXzE9%$2Uoaw<`(u}^&)7P#Hdm#S`opywW=QQQ1u*YR3QRt z)pH#WnAC^_Yq!?()U8sxw2ZPjs@I&+L)W=G1{$j}4u;ybG<>~>yhFp=#bD8ub9rXl zgvm=~dQseUZBO7Q$|Y` z3T-{mZBrHQC}H4dTqX{HE^oQPgSBCG!>uEGGm_wh9{nB8q1ICb30}!Q;a>99@Ntb^ zc2>MLF0A-68{h5Ev};>s?4|+LonT$otbrd9jGPPn<@gL09Ei?1Of;IIHM@qy(>!D^ zcsNcY9=-hpIVIt(Ae6FT7^Nz|=r)SAYJ8M2tJ3tO^iV3d6G%MdrJ~G|Cw_T(0Fks? zlfLH_c^pA0qby<^NUq;Bm3b1T-Gu$&0;LqR(okycx&bqY8@L?fHm!G5dAS(+2$Skm zM3V886GR?&41>#|rAm9X2t#IYbEli{@hTCjuRnrJj1wqc=}9u*Q+ci9^eIfAILA6r zU>yMxbSLU-({YSfsV18@z;<<=CT(Bn48FPX%5mh^mAw6v{8`&;K>cQQqD^9fVb zYeOXg;#B)@P2V{sY0We=5`~zp5_|{ZS{bcE6m!iNUuFT2!798bs0Y0lxbs3~1W2!D z&4UV+T`f0<2G;AM{ODt@g#;S| z{RR9%ItMnw)RiD-qG#|^F~Ns1`1Yw7nuaOcpDUx8E#it=QQt>L`OUG_f`1c-E7;f& zMD)FrcjLYnwnpd*mK$CmK&mw^wwf5J3X)HZ== z)}E&V$F()&q((SpXq13bfN?A+O9C@6O;X{#)`VebC?#PzHjF#Q^)(?4q~j($dS#I{ z7&9I}k-n&NAq)VnE(p0M)RZvoB}IW<%+{N;D&d&NENTVdKV9#`5L73 zCZ2!sbND^KHV6%o0s!kGfMZkPhVXe;HxF#sdZ-WYV^E8ueyvSIq!a0TdP(viUbR=)m)-D>I7X1lNX}TJSG^-E;V!Z~tmc z(+paHr5CUJx zX}Qd^%?)*4KuKUWCE(Xyd4w-KeuOCprtKTw=@Fyiz&q?x!k9_uF%ZJrl}4O_4F-^T z1o5a*H>;aqA-@|a)Nc)`Okf&OF+kFt2Gk_2wm6xxo^-y77;Lmp7)Pc%3cr1Lb^{XP zWWvOGb=u$Qj2z+Iolb6j`Vsi{_VQTF&JA2aZXyI3akFZE&Pj>T!>0Hqfv`=BxNYBG z4Z}Jjse!|cs;1}&T}_}QMjsB(mlPr~#+6bf64kxbeT?NOcsh*IM$PamYejbR{F~U4 ztRj4Ey?zr*xVv>%D5Z+-P%93XI}FpH?bZso7Jw_p-2t^W)MeILXK*Y221E%Lw=Til zhRLr@^8T8WRTL=(@(XCc5TW`$5A`Cx1Uhs@xpLPfo8T z<>3Y^T}JP7`VGOduf&=M#y?y(U5P{IuYcM*bw~5*Z-GU+j^>fu;d+;seU0g4tkbhP zTjfy$!a|wQzp6l~LXR(`aFw9Ya~Kq>&-lz9UPC_lQ_DI#qW1-`TP)X@yvtcbAlz{Q z6b{UTkq44$po!cQZI;wl2?foSa|U3RYk>X!){+2x)?!70K-Ma`;>+AH&x~bZ)FLe& ztkv17>U@?W8m3LK34=^xU5AFfkqUM?cMEHe3i>dR=TIB*icm!$XT-)V%+_LIL3XuQ z#1Dn#%=2S=FN_}&0$EAtEgKPQG5HK4p$%IEuzPe?=U2*kxG#La!a?M zwaQ6%#)+;>>JQHLb>1cjn6DA`uKGP1Fc=WwJaDj+*N*EG6m0ugF=)1Vhx6Nee0FQ5 zzG9$(!JdjH?|ej_ZJ*ZJV6$}H3c67gBoHcU1#azt$1`J>fhQn5VMeI~ zMqVTuc}cj+kMWh4zl2-+D_k5tj%l}Gy8RjatN-Sk__~h{_~U=-0$+Ju3SzlID;jJF zA-7mRbswQZcHAE_XjeRh$WFn#>ZA*fGhxfU_>l4rD@MhZS8gz@-apFZ21N$|wy|w! zhUXqaln5&z=P=MZgY0Q7c7mJ_*+H;1=s+Vz1eP^uKw>H8L!6KXGh!+&*J7XULAUiI z_;sF{McjY$tx)Bh)H!X8OzF8566Hkh6f|R`{7y?{nRhx8{YREKJwKc1Lr2i z%E-JdI>AKR+RLPe!20c)2G3*nhq5W>a&;mGC?V}9Ot&tuyK}(t;WcCHRQok-^*Ex1SnUff^de$LowVXmrsG!6q;6+iQwTNw8Y-9vN_CIMa$uCTyT zVqhs1_j1AyeCyZZANa(3&~ie)dx69IU&Q{wvp^a|HYi8bS(WqB1g@L)C~q3Yr}F@J zud^D%tiTg15B3@aP=&cQSIo^jZiV1#Lszg{|GOBT*}K=^Ew`~q73h%DUc-GJ?rwOX zA-$e!&hT7}NHr92N2{JddB*pXUF5^qDKg7#VZZPHSd%C9E#wexu-M$VOh(KPA zI+$Y>bYlTa`WF1isaHcd!pKu8$c zC8V)chiL0Py?SPlPkM2#r_ZM>VC5@n!OiPrEJ<|e78(E0*I zC`(n{Ac>r>#E-d2t~p6{cB91>*;KVtMyrDFwG^b3us`fTL^vLc7Da5xc@)LFEWQrh zs?Jr678h5S01--+LUzn1>kC~-Xco7Ax|v}^8B%O-XGZA8+LqAvP+>F}UhMv@oa{1O zcn14Zf(#utK8PH8t_?rD0~$n-cX?&I7~%OVGzcQ3MGl@30Je9npC7LA z8J{S-;rZmE)|rud9f+qrlK>1W>%U8KI*g6(f|ic0=P3$nK}hq_`&+H7a}EP+NT))C zxpG?AfG#zzVe`yd5_iGUiZc!2U)Db8W@)nbDot1ehaG%fKD$(#$rH9qdhVXc za9(@J`24Rec=7#j;&`1<<^jW$rDe*%;#FmV6IQxIqPlA`%@b%yLS$xmiNBrRbIN(cfC26~2 z0`G|d)Yegz#S_>JjLZMgDzuyRoU_1yPUiWwzdicO!g?*zrrDMn-G}CslQTRMSdIR4 zz(L@83{!#}!kIT-m`+pdrB@LtPBS~Nvru5M3pGt^Vt^W;6^X@T3yEIn(+ zM!%cQiItAHKQ$C)v&bS}U;SAs^TI=n-Sba`iy`4Z`pZ9t@4vdjyPkg!(4Ih>V9OdM z6R^Yx;ZXth{)yBwEbe+%fz}EKnjwvJEJ}o79I(5*z{TA=s1KhYKRRM0#y3BEg+KrK zSN%n|^k+@`Ps3*;Zog~VUt$|uYZ4FBm8uPhVUdP+lM-1jUr@+lN42>as zcB?*+0=R1UN-#PthwY&e4=WUxh>$oT^8-|T5uxHk0JSa?n3qamX~8A#U`QEFL)@h& zZm-_Xis-I15&%wMYR>mF4;vuwN+oaRv7%2_dqM?U=l$fTx^Q8uzfq?9Gk=}KXcd{P zC=cL1nJ~|_Jh?qi&xm2A)`px&;6Q69F#}#woVwv9&Y3h-Guj6!0j=0}lk*LtqTnkQXV6 z2$p9p%Z%0vtgRVqVK_p2g{rkzw zvN;+JyUsuDoRs~vt8Ag3GWDmvQyf3a_x@{GtHBF_kqKL29+Wwkpn!$Q)kkSVFhIl~q6A@?F{M*P4r=L%r1 zT`_K=&u~M8AsL&n*21HrL>dH;4uD}Z z1J#WrKj~oRnKkCu4M*!wg9!#X*d zQ^MiUaO4pq5w1C*P{y61U~UQ1*iajABnENC+fN!UGU591f}80P?%sPZa=Hx+AHzTN zJ09WU&X@6@|H*57>XnSc)OWlKUkXEEf!DAU;lSuDdN@19J&E_x`re@Mis!snq89!E zH^9Q!hH1kLkC%000JzUZpUDO{3`Dby89O6TM1x;{w*ZX(e4YAAQHLu#ObkxO|KuN0 z(qe6WNYZ!rTvv|2ZJrDB>arU%eGsh=V#Ao=hy?8^FY{~0Uz@B`P6>bHhkgV<@CSY` z^5q5U^(=d@O1v$)7*k!$P}s0_He;;Li{)JzEFDKkgm6lPp#j)W;JdfL58uK#SJWxt zgYUV65B<^spLuk>-oA5(D=Hgh1Hgw5xAEL`z(vj&Q$jYVCMo&`0sI8#PM@+*GANbgZarNH!;l0nj8z0A)@R7SW7?40iM!J6&hxfgR;oe_D*+PE6lqE^}0!B7`vC*#~Tc}`;%92Zon6Crx9?A@2bKrzng6=5w!jruke(>4i`v-eZ7UpX#r@!`iT5; z{Yyo|RlbdR5#>D5u=1Wa5=3V!8mIFzq%kAnX#6@nuSbMN!NZi6&ajO0uQ^{>r_DK| zHGfZs%6FZ18n(Uz!?#-lA2Hr-w5v8LWGQ&T66Kz^c>>d1RCgYqK9D?&n4e4@u zn710ltRqoRbyd%Ok`|bf4g*N;Is3%Lt1X5Y!#!)O#;n!bnNeHS6c?%NzCVm0A}>PM z8Y7Pb$|5x?jN~?LQ^;UUy9wj8^DYNv(L!`d-i2EO>f$urmVcFQ8rG>cVCpUFgMMHe z6eXs$RA55j7S2}duWRhEd4^8MYonxU=^ZfmSZ^#=f(V~J-et^ z`xibtKE++^@Z!Sj3f$N${2SO=9*cQ5DHwlT;0*$7gQO$0rrs_O&F@HQ#QY5Vb39#n za^m*@)vV7BBW{s?CE|``3&0 zIk}A^tS1(XLy?HZJAB)1{~Xt8J&w-Dh@~-a6Utf~)5hcQc!+`PgYO^k_kH4RTt6DX zm4Ng%YONSYVYVrghme9q0IWsVSRc?BXoXP=p%rNRpp2z59=}$x+f|IWMl1>q_ii!f zlF%}vqy+E?E>h0@@}OAk5NN%an8(w9i+^?4kGp=SYo~p;ic`9=*gqEisT+ zm`6!M&)=*SfAoL(i}(-zx!;F2jvy?O`(MSID+!^1$4E28yPlH6N2EtSH{Y}51dc$+ z`vLnqx6vLvLV9wPywh>O@BPp-_<_Im#S<7=6~_v6$Ma70DPMbe5AT}x*yjNwNpwgS z0tikCqZ^!*5|iZkS7t0^R)4`+Iu_h{_kDcmlb^sxzU72tXA>Hh~* zu8}YxA0`aXKfw6h^BAr!WgX^O!r8f^)`D78Szi_@%w`d9s_cr=to*t4aCYYI5Mb84 z@c>@lDh&w&I9E&%bMGZ7j>iXwO97ezM*TF`me ziW5c)|Audy((ZhFBciMpGc4g*d0~C&Q-8BBv32cgYWkk;W>cd?Oc`PQVqzROMjn!G z7h@a}mg3yp`45?(+$=7EJcrs?l(nin>qsB~+OmynLmqn6grq?UeDuO^YU$BD^Pr=< zyw=wcu{&G{9#%$OigpT+ zH6t*%7DjDMAO`HwrEwNG;G){>N!|1(v&t1V|#ws6x`ajDVPyQXN{+AZsl#>w7 zJdhL^S%tLs#qcmM4fQT1gnh;N=sao_D}60e%#+zw001BWNkldq4eZ!E8yc>*m^e{h%$aW zih@4hNUnUDCCuIKC#|S#0tXR1WLyLgsZ8%{X97gC)U@E~45$|IsjtPxBiur`mK78s zprtJuBM>bU%sTNQ73!gL@ep}7K47SE$^iR9iqHq(B(L5O=R`SI@Ldr%f=3&;u;9XI z_*?gF#esM|jxqEYsA9}C$oP7>G0znx@|<0NY?zDVYuJ?Y`R;JRx-*3yrhb+?#B0LO zmeC@`WZ;#f0Z8Rkf3g0y+k+9-R)K~A9It`z{hkK!3|0i2tLxr{h2ghz!iObZ6Kp1Bp%2o%MoApw*V>P-XZj3X27 zjSY`e#y$a0ri=t&$pfYdc(ec)N$6Zlz}zy%44_p!SrT@9#Ldz$(t=|JZjN8Xetd}C za0kP5g^SxS;(Pz~XYtw(`~v>Q&ppC3w@0VT3?N2JMd8ZV(%^wQJ&7CU*i73!mYaNs zTIU(va2J5oKiBYXAZvN%CZIS3FvB`TbkF++Dh#eSNvhGP=Wn8OTFuc|Z*pihC^G^p zNX-d^kXPI!J^^39lsl(wlGN zKm7|of`8+8|6^E2@!q0{V%5~3E%EX#NY!h>!)dauzZPI30vbs;F>(T>jC3nwfB!!6 z;~UI3H%N`}d%pe!{MfI)jvs&P31ao^rx^{+BgP>B7XcU`0_~pPiw5n;akg@E$3uaihCAs#YD)l#axOi(t?-5*BgOo4!}Cs9 zz+fb+bpiD2^!o|K^VZkyr^5{8M^_}n^w06br@!g?pzUu?x3(d|{(3?id>eW5N|M{1 zHP@UW`wte2_b?l30pnw2a^d zngPKHspQW7&m1Qykvr#-hvJL(oSVqXtSKZZiE=Js_7%&#fV5y;EsLvqt~=>F?hgl0 zPSW*hSzPxbgT%Txuj`O|vRkWt8*(t3$9*@s&i4;4K@+g4z8(~Cbbi}+y91^1(6f5_ z%2Cs&-(^yR?#xr54Y$tVr^(%2(hqC+^m(nq^8GqncUs+L(ck@G z=Ys@bumWS@_x`uj!7j*PJ(eplzS-s^Fag74n~o0wq2L17mXP)ZBUj1|nOz2hhK)m% za1U!A60+u?r_@8Gm^I|4ir2bU#!{Lqo}PQoeeoF(1-St&tJ5JjZG|AnCpq0R1^{3P z-gp(hENrb3OEEb0;MdYU=f^pC&nq0M8}~3bQsrEjMN;-SpXLN%*I|4N}?g#|O7R9&a)@5^Wvt9*HmoFjr(_%6W5st~XPw1Gh~AyS!i zO3<)MmiJ_6`?%nVCUhY{i3r(q?RTIHyS&NYGM?GiR=ll|TX|9c9{w-SjRda49(1mL zP2k2dW*^kL7sRDoJ)hZI4XzFy+v+E9xMypP?DA4qw8cwiqQ!Ye-N$e?eL~uN>@Yny z+HL0?ide;QwpLm`^La|vbxsUm0ABpiV;paQ>&J}a;|1dYT;2l`X)7M$Uepf;lR&8i zN)6*^G(%RS)P%wViZuGgVaK?x19p@^tzaB78aGU-fpP`Lgd+`Ts2C^0yl8aKPP_!W z45Zdj>xev7lsq7(iiI+AZs0;#3ZpP#KNc)Egw`_VxnUY+42kgQ<`EvXhFyM$VS0@H z#XbCo|JK*zFaGf7@yGw{=kWZ49TIheX;;$MAQAVKRj};c9^y*(?@{QE)^%21!L{eu zI_}eoEvy6$iu{54ImZJX*W0CR=TC7kJ)z9P?O%Zg3BNK=e-Jo~KilnT1<0C`rFq)i zkT@wnFh*mK+3-v1A(^JjmOGQ-?ZK{l>*8p(<9*FO6Ny!a#k zC%*IBz8N%RHL|l;PX`FlKDo`RUYn82^O#8Ip)+CjB7n`{DgikU$oKCcE!Rk|zKOOh z*gd#~KlZymgg^MhzlbkBo{gRPJGl6@5(I%4_m}uBy907g7_;=l9#V!!(UH73H6T?+ zUK*|;-sGH;q?aW^eX`)&|JmP-@A&S224l)7r2%ih3jXv@g1+!6qP`7~j?dmrPw z9w6Vk660WNXiE|0T>G8ZWl`^W)2O@V%DE`b4J`?7OFw$uC#^p1-fXs(XgxfgHFsP1 zcMoBYJ6XR9EUfo@8nyfg8S_O8WS=PsFc7YEx15g#A+jk8?M731oGBpRjldQxfy^g z0%UEhIFUIRC$uuk^(SQfJaasm!MXeR%oR@?YOB96v)3AEtx4*bMr91c0PqrsFsY)e z#zJoJVtFK?=t#+FhWqb>JZFnzyh;kNWyOOAb5dA!`KYkQ1!gw+H>r-J)LUyRl|>4@ zH4RBmTI_Jz4XThgSev(|%Dl`kxx!6}KzYD8iQ%u-BG1iIA)~Prg=?*1&^7Joqx(R{ zi;W|bSH};_D$!TQ4^|u48QY268=m3IV+MNz4EIDmMKU~rbAH;7{Ti25&yINZJSi>s zb!F6($p!2ufL+#&W0MKYDnY}~08?1(MO0{5 z{R{8PIvH;jW>SMy67-j26}(h9VYAToA=iRpP{uc_*S(}``75c{DJRSG7KJ_LF^T0G z?)lvoN?Ut>!@?@#svP#Msb`m!*;cq`S&HiDT5$9>OP_ShF>a=uTlM!vJm*Wy2~QHz zXWj=hFx1+^Af@`Rv;tzH?v)e;&p2IKfx>`VETUB%MEu{p)DN7j+b(WacB7Ye}g?Y00cB$A${POPEsPV2$PEp zz}7U*v(HzVW+Y794J;?WZJrTNb!%cjJd{1GyA&f*Da2?A0kI{DDg9f5SqkHy{GE*3 zcV^tY1+<05HIsq7%a{%YOF01Ty6>05a4UdC+i0B_`3jzY*8u}%k4!PI zmK#Ta4p_EN?&1iC!`gbE{Wcf+J5BHc8`MK1*UXCpwCSb1evF-WTw)UdlX!XaW}b<} z(`TuCl>Z=(FvCr$%Xb zSRz{0B4L=p6iS!-`Y=Os_?dxB*r$a5`WJo?cWz(eo4?`XNJBzxMf<6D9&bE)wU7S2 zfmsW&5lKZNg5o8EbRRMS*zds)-UY4={Mtk01L4{CJd6MKyDsrx{`6b;??3%H@bQrg z+$$i`ceu(Uo*OSv7T|hjBovI)kgtIKGq>>Ed*6+F&)>r|+ydRX!0l)6Ie8<(|2a8edz|vk9uj&5lDx}2OGkHV4cv;tGNEZpQPSd@hg%n5!0T4z*o2%J ztt}EApdCtJvYzi*m&*G2eqAqw9PTpNX#)u1$swl4znz;`pg2D6i98%1>iL&l9+FGe_;;cgoOwO0?mL~G$=2ixK%F1! z;6u3DMe{gV%nkQp+?coyL0oABaT4(%@dW~s+6z;>ObNFuY%E7ut12mLnJ%)L9 zJtVs}_x{$}^SPp;LKjQ3c?-PJ3MY-t=}WwA^F{+73^{eKkE6KQMWju2h46gmGhft z`b`}TSIkPiXDbV1#I>yy;u`eky0{R55U!Qer{usG0gYov>V2jN$Z8E%6Ya3>R%sU! z)6yEJGVhxNFzbKx?i6i!tw}&`AGP{hZ}CL zfhTWCCH7WI-5&%m*W!H$t|E6uoW6}1c)N5*l^80(Bg88syw_Kc-gl#+4xq9FswTu_lwL70yT zhpFPxaYCX7N`x^paxHjVM@+SWxZuf=Fe0NJufg?Ia6aG<|GWPR-us~+$DjDYAHfUn zzU*vjwKvpHc@=en38gDQq7|!KAF7<2LLQ^7tnxc$+eCP7b`&r+R6{-HdPy=qBu2=j z={VAF)AC6AyEJ>X4UdqiD*ak{`Qv;#U5q@)-&3z|Rf}Y$?6@GLYJ>P}ULM*g<~@33 z^}Ogo=g$9nBwOb#X3&tLamEXvbdqs9kNBg1{U`A6XW;Mux{rb~N&7Wq7m!whm#w1- ztlE;zjA1qbi*AzqZ=el?lm?6!m!SL4U~Ucc=m~~tz%zF*@x9MJz>B~2llXI=ejUI3 zlYbR_d;|b^*E1J*-&ehW+jp;U|K44E^v=8R+}+!_^~`-d_ri0y^Xz@xdT<}(#U+Mm zLZSh!cAhKr%=V*+a{b$X8@{bI>=T1N`*V2m>A!{i#+NV*QYeaUT_NASh4KD9jCZd< z`#oqFfhPH-MZ?(JGNWoSxT>v?N@@BWRjiA)6|)pM-2ziWn(mubRA(*hR$Zy@#oby{ zk9lhzKJOv$gkCTX#*f)+&QvSzR^^$l_-ik8sRXZm+I2q#{;c1*DZ9`J2@Pj&63z~0 zBw}!qf@;!=GNCr$wI`49mE#f9lzZI?ml*?hP#J(6*m))E0l+|nw{Qenb5A*|Vg-;Y z=TO2B9>lE5Apjn5;IBd%eg&kRgrhv&AE`$ElrTKHjav6;X#i<~xCT-sUBW>(P`Ac! z`!5$S1kAPn4wDVhW!-fOSs2fGI-klBF39le@^c3Ncs%`=aIEON@?Ac;B*?dY`zbO|Crf4!bgBcG((&t7}V9@$z2$TD>V!d!MIDbj57CCjHX z5lrVnIk|jolM&}zb$Oj@@0~u5BLH+GT?3kW#et+o-y-|c7`uxLq?9zBwSv6Lf<(e3 zEvz^6*XrAEYhd)6Hfm{<-KoE;;aD#|-1oH$%_es`!fwP{9WPYkB`6ZCZ-BM4NNl8QI@4#x$bHiR|p(C@zoARb}A7v;7@-eV>mL`Sn&F-n{ea+4Z}! zZLmIcr8epJp-&`;!W$Yu=KWBD=F&Bhx8QMQ-DN-vNipebJz-)3hg1;v>i8egg0}`J zTUQ#7NL01PFHMSF z)&GyOH;c7y%MP=?G3Q!)pYu1jt6NpBs_VAOO}UL-w&O%jOo)kP1p*>6iWHELf z!VXaoLNdw=SVSaJ!~+iq9)R!y55d?XgxCRDfhdk`h?|sME?3!g>sI-;e{;^+d#yPK z4`Ylu=UV&R3hZC!Kl|*x)?Bk0eQOGdZkU$scd6l_5k#to%1C2yHh0;2&38c8W%^wX zjdT!*{F?iKTl?|5#|@Ks=n8q!I3w-w7pNONBF@tV*Ffy(>ADe+M;r(^+~H*la}DIt z;dyj;kzw4i4nr7s*zO8JY=WMSit>2%o>Vl#8hd&Vaot?HxoUjH-gT0vZBCtNk9*XJ z;pj~m=NEzq8Y|m9kNXLHjNKM3+b@Pa4?@~z?;j}2-5q@DQ@1S7V7apvB*!DX{2D;^ zkX;KWO+xfxvYHMF7e0OVi^H)fS63@HH#3L1aC4eD99C}E19dfodVV9^o|Re>(+pSB zYVFx-Ww}{6Jf4`QLQx^zE$G#R%oB&Hk`HiOCSJZ?EHw43P!rR%veXGdNoA#~Fz3Q~ zQRZaDZ=RpwkQYwRDrZqnOXl&52j2eDZ}YGGOFzv2__zKMr*g|g-WfGJGjASqkzS-N zjv?@EfQ=@r>h^d>qg@E1DM?SsXcxx&j;ogYE}oCN4QZ@(5UvVn(#DfOyRLBNrSu z$W*mFdF`R@EuY+ZYpdKIVd;Lxn;iCHUUmz)Rf=Hr0&Su4k!N1qL*Kh8coDnEPZ!_k zQk*}l!-IN-b|t&AuFh)}m}<3Ra427_&v}~99Oq=Md;f&<;#@B98Z#Z^pbj*udg$Sx_vjbNRlvkR<-tLRzR_s=N*$7rbG6$Q^OrI;yXNu z8x0V&3e)l6E!Av1QF8{^b-|B{oo8bP4ky8HeNG$YuXQT1^BqB;%VI-xnlqcx>DZ5O zdyd1=_Rj_edmpsv=0clr@T7={(eZncZ4|Aa$wo#Q$7g`~_*RE;nhbc)>t67e-JJUx zY^e9y6FT;9*Wab^ZmbyS-)5K)s0Q4R?}qhHy%>QsS{j7N{_LCCTw|lqBD8S4=*P_{ zT*STF?>7V(lz^t)fQ1V7&1W6vNRF3Ud;#ZKB@GxEE9j1%33KhvMIs;$e-W z7rqsx^Bngy^alrHqNxZeRm)Wsm}X&~g?Z{@sp3^pX%@LU^agH1u+cSZahCd?hUL2g zxf;8wMXeZ}19@^zWXGW$ghDWA#Gr%S$TaY@vmj=Lt@X|Lp@l5%fz4pCkGXFN5d>zy zvUaA89`%nmOsEDF#(70(yMt61jdcIKAP&P+2EN{vbHBEQsLL>!c7NbOT)(4Mj*N|< z!ZwBAD=|qYyu`jWTnM5ZgZNjmmzxX6wFen=gUjZu$1T!f>@6YMTKc&E{Xk4ND{K2X zT(1$1-qC%WzMb}u<>xEm_GE&?c}=8TS<1we?GR>JIWLuGcNx)&Rx$7STDZPe<)j zNLHTQRvtfu^HP{+xVfo3dZ8jYF=v4#1T9H_lyRvIl2)E(d} zz|WDkg(i;oN;ha4A@cJY!r$B34t(W4c#CKub&q4J&sK1=m-QQhUUT8v+pV z$>4p`71Yi_i$GS=lu56?z}1vkMObcbQ6SBkZ&{!6&3Ermr;ouGK2QDd*C5A7jrO92 zXz$kc-Jcyjy`xQ^s=~S=H_uq#d>wt~i}21D(7Wd_B~%mBe9d&2kcSURue?Nh_!zl5 z!jw&@E)`w97jY?+vJgFw&9EKC;5_f#m2<7t&R(@C&e3nY1g}Lr=i7}c`1!dazP2!U zSD06gw)L*4D^X;w<*K(LZ}d?2Xy4}ZD&q7k!tY~_V#;i!b13#(Jl|YBw#Nlq5T&6;pa-uZDn|sJpSJJI0r4^PN+s$^BLRvxBE|SIG))u;M>+zP$vJE3n zl6{-)%&cE~@?Pskw3Xlwg<4C?OBe60oF_AMwRW z>4-NvyM}$A8u5}H(L|_YDcbk_5fhkVGj_sUW4zKA+JrD2g{UrAZ8kIzM*H5nj7Bku z(-{aYhpQW*h7faC&?97QLw>xD9x>sxyEoTj91Ts~HoW33ZuUIwA8tZn+Z;CS{zK z-Pg{mflRXob+O6*Z$kR^6#E}~`F@P92+LZ#ch7tLz6Q%72xu3G@qOPQh0^j)$B_H5 zB8DE+;VKyF;JJ$;Nbz?NNk6|qhHL_|le`Vj?M1DExUsuxyLbD-vpd>&@f!7^?H0a2 zlO28JBMIGHJHqTnFxWEuQ4#NCpWW){d$X(F9XCc4)kuRjHrJgLvL%W3?&%Hu*uQwk z=@!l>&OH7<8>xqwkF!5LZo8RRlFI{(> zbHon(3$Ha2Fdv9-8x&DFp|~~(*<0Zq7PcT4LhS&!Dk0^Yr@nUlY*yv*gm*xsh=&h~ zrSh0I(P2e);{=~FM6@BBVje$c>?>eF{wpS_Jfd)IT2quO>RrHOQPWP0?F^!NqRqld`#(e_;{x-Muf=(3_~ zwKht{a?gues#$o+buHGq%_AqORRPxF^NyuJ1ubWNx$>8%ZJ zSW|J2KjAse9Bv*XFF!O|)2m%JV}69U*L335)7P>tRs-I=BBrF{!PIF-2R7@9f2u&? zW`5g?kQ%+Ldm6d>qbpOo{l2~N$hP3UO{VLL)wqS*r%C%Y-rsO}_%GzCK9zAa-Hg+{ zxC8H%e)egyeD8HB%!kaWRHmGnro?hGPjRV6JHzQEYt$X;e8&n^T2ZT}#IM?nz*gXJ zx8Cu9*q)m->nTp--D959aHiUd#g)Yr?P#l}*gU2=o6*z_r53fKA#__?wIo2|aGb4^ zi>Jm!8b%lrw3P0MTT2MJo&RWGR#%NiIWn%k2L^1yG-uu55thvv_dVZ3P?wWBe~mBp za&Ciz2A3U1L!oW5>h3Yy*MMMb%eBl*G#=Mw zw)ozKm;FB3$NuTCpix#pV8Vrs?y?WXr-uluD6OwFU7!o)oI%Ko;Wt)-ae?9j+A!0> zH$Kn=9e(xuN4+7U(<&wu6~7>=E~|#cx_if(g)KH!%sZB&`g`ugAhn`%rVNMToLt)u zZ@jFv5!#&Hdp;-TS(s)gLd{a*hwSO&Oolg6K^^zv$K}<%-6o*f`X^CD6H}_J*}d(; zCS_oJh4oj&^0_w%JRn>rj3cmiu)0r39|5mh`7VZS?;A?1BTq1lor1k56{E5?{%T5b zSW4+uu3EGcmKZb$MGT-OOm)G0^rQ=;avz5xBJL${q9%RJz}de3O9B()HF7yH$2Lva zSklJlw-GiQz1f=feH&{q%xrGk)3%Z5^bdBQT@se<7;zT+Gh}0AP`|br;daC9z4$s# zV_>M`dLJH@ANt{2-g^`3V#cpmJ_eE=qk2VI-)Svmnp| zdXO{AsWxxXQZqRz)4|{&yRfp7DK+tcm4(cMsqkc(c;!V!Pr|jp-3nLd!jp4ixvM;W zuyR_itiwcwIaO}XLX|{SA?ZRXnS%9c6Vp6%D)92diNEycKgM7C zZ(rv_uN*lZO(ScbppkC3{Bt^<29gNQ;A!;U31};nIg)Tk$f15l+8A-P1S|~nL^{{l z2uD`9J>xe`8(wfp7g5Bxf&}V8CoS{{bH!T^9IR3P?sv^_)$$v!ysl6tpI4 zvP#F(G$9WTO!Ldgg9qfB=ai??^NZMe0gbvG8?2 zkMOFSx4t$*K+$6Gt?t$Lxtdbm6@A=$u6U>~=CMf?glYy$5fAMZrlasyIq~HBL{by` z$F_heJI`vJF+!Q{Myq=X{lH7I%skB}J8#@L6f&rE9uQ&e&5MqCASZv0=*0Eu8#!J7 zCc;(c!xA1@g``aSfUCQgdAA%W-eS$NoD)fsc|;{t9BKTs(FT`e6h&%fIXt&r)JxuU zAH%-zkPoD<(L0}ayI|n2U-7l$vmgJn}pI(d87rE?W;d$Z_F4XSA+E3Ljqtd5_jLqlFz`~ zQjMYIz4eT*Mn-{VP~4h@Aub<03Qi2h{2=vf;@XBd8Vik0QSayKMNvFdRg+ob;`UZ# zrD~f?lL^IoriW5JO@@4}3cM@hxhd!2*_LiG!!5%;v1x*N77hnj&T|yUz!A=lDrZMl z7Yb7qQmxd6AkuOSqa9oE%IhT=(TPHG$p^`vIyFQtwzEOZ5Ru|wWX!3GeU3xyb7+iU zIOBGJvU%G=7-u zXvtjV!g&=$Gs~Tjs-pRhx8KQp_a|TFum9z1e)6w=h7Y`S z#nn;z){C7>YN<)X0W&g^fX|?xE$(|ms#|_&z(aTj!sw`Ry?c*qKmE^N7YcwYYTC=@%lUzS`ku}V4?$y_3=u@8gNsaGM=JqMJQgiucOgU z5paHD^+O!Rs_zZPYg;JvQdyTuS(I|F)Wv^1N4XWd@;z_fZzl{gc}gt8EW&rZ_%c8F zzx;jvle?84{6pVvMU=!t$D@KU(s8v_Ovb#H;)_>o?S8frd!4L!UR_sGo~$j=qbuam z75VXF(%mif?iRVdgVTyiLRHYoJX4|;C8DLY97PM1$dED_+dpzb4;h(e()AI!y0)L= zku+z^nb%5P&mM(f5h`_AX~of23p=HFF}MorpiHmJ+R`Bk-gDe**c7qIgIdNs^6CZQ zq!o*+ZlH;3>Sgenm)ea+RkV53ORatUm``}XV@#K6YE??=?=6(Qd)2F!{&$4PyJ91_ z9O2>E6g2~v*LX-64$Qgo`Fh9myfW26NOjl^TB*W2eopq$3(vlvH;%tdmMiD~VVpEzt#amL^%E?&nNvNW zk|`D|z?^b3OfxJHFyhW0o&xJW> zZt0PWArIA3Nhx<3GbJDju?@7k$p^8$9x6ZVNCP_~hO?@QZkUk@8Eef{GZ zw_|F+(P6ARetKwpeA4*uE|NKv)A89GI2p#IgL#|8CS~a!xViTLS3s!0wO6`zlq(1E zQAgQfE92AT*UcAS`L#v0jdVIX`}Q*Qy#wvPk^Sau&sc`bfYx&F@``?c4;2nUD!vri zcwS@_q~hThmcIV|=y7et!kHMVT>%<|tIZlUf}VVtDrmJ9L~RM7m=E)hF_ITzo7~eq zWwIr7o7d%%+wh-<1yg_?X1JSRFOQ}RvGk0Nhfv5R?*dAkwbBE@s9szm_w(0uiR za#)CLRiuZhna!#jo3G0R=`YSaGf}9xl11}a5?_F&x79Q=qq*) zLKLIxOiVWTBprI%zKu&{M_{(Nxg9GmZ)bVAp#$Q-pH*}(BG3r$X8(1=?irPpF-4+1 zc|RW+8OGk~8SvHD>?2EUti2gXYVV_2RNOS`C$Fg=d9eGS0+~w=h>cBlb}T`m%Iqm9NCMoO5(iQo}Ed& zkSjOK#Bx>+b>Vi&9M20M`mmBp;=q|zCc`;_QiUlO7CmxxJX7w3^BG>qD|e^FwJLg6 z=1I|G;qB+b^^~|i-13Frf5spATF6!Yvb636KUgW4GnFcKKDq1PJ=z!t4SFxrrT01Gid3z-vlr5xXp5bsHO$TJ2!7H?r5*1k170#~Eu9ioxOYul-SE{^lntHEhyEZDRM_#%s ze@rtD0k?708S5_0_}P z#hCx7)T5<8$3IIE)-GK+PYOA!<(nr@8JH54mEXR(L9>@N2zf@G-D;G7aC$PxYwBlc zxYDCMbPy8)gN^1S?&QlTD@vhMAz8E8Zje^v8nGS^+DA|(fvGA}N<6yxF#6&LVY)+^ zwsI@V!VRz8eJ3|dV$lO46O~LRW2^59dQ;S6z8dRLhn-4g;a1)z*SX>Sy)w%9=RmLS zVRsvu^-}oS4j0#7=mn!daVc2!@pohODfjP3$IVW-G5TLB)4|@Kmz7x(ISIE*h%ByheE5GZ$KixG11M(L_FOrql;-3B@Lc;Xhj5@qrst0O{qqlUVEXr zFwDh0Evg8-b(}bknh~iL(u*@EG0l^;wJeoZn9gaLdC%7-4_Z1~?Dr_=ZTC1tTRJ2U zM?3ILHn(l=4cRuFjPDOo+jG4)mwN;opNz8md;>cns|HP{$p!o2?w!iL%7^+G+OFw} zdfTuK>TI4h4moV7Y|db}Y`-pWHssp)zFuy^@TeZu+foZR4;;@#JO@31LShqQ20l3n zKp%vRCiUpW>bH3+{Q$-V+vkI_uk_W{zQJy6+X9mIO)@4oe4n&ga6?5I!#k2Aet5%E zGB0{4>C;ZJju7`z(GH7vFu|rPBz0w&i+jfXX$rf09;`~cg`BEd<<;<-M}_xg=^^e# z!gx{Xcw=lexle&>sAH4ioyzyC{ie+<;(T&_zAkM=ut10B- zaILA;*g`}p)jcXAWT~ukHVb?b?==W6EEl+jfYQJ$U>$6UER37N|CyNPLqITzmZ9Qt`a zlMf%|n?CV6fBirF1b_LbKf}9E;pG>P4wnJ6gU~!G!+4lsT!VCV(&?~An09M(c+NAV zJSe=u7krYRA2_-BgA$*4Nwl4m2F05x))ZK#~ zW4BrYUhAYrmAW8ERiSiSx=3P9!Z$pA%zyqHpXTjn&-k-{><7q)+44Y9M+T1x4MSdp zW+_8%qu6@G!)g5mDboa1DdLEQML;DnzDV|t0*4IqL!w@aps*JEuG%N!1hq;+bG8r{ zKwHyzN9MIO1y@%uu1B1Mg-f?C9uDWVD!Np3PM%Isd%n1byw_@Z*rkTYyLf-|@KEcZ zc!#3yhH})ZyjN2UTIKc{x!@53PtO$(!vGd4AM%EJQK>4P>saef4ymn$TH6OFY+7q; zA)ZSiYqhqAe!P*ttyxK~5>FyL)rH@?xv_JrAqx)lSB-{BP>0bN=d_?l*07B{yN5pu z!WYu(lzc|@Xv%R_AVb4J$EjMq+I9i8Ue%LbiFM#ky!`z8c=z$oF{Kw9KG>eGe4u;` zdGeU0OspwUY68tzn7da1uTD!wF;}(YzMl(PD2EfOx1jSti*%r4eZiyCCN912)jgKl z`Qc8RZ_|qF_haq;?z~)ge_tHN#k{)wCmT5~Qs~yDaC{_aU0IgOVNNEKE_P*3!b*@O zlUKtSoRWBMx~+FD%B-i7Q&+pR6Fx>n%m?dFU(mZBo`Yy zO(r;pepKC?)w@8o12DdOu&|}FEbjUD=5V5@r^w{V>TCo&ZIsJ_$1%Qh{;-j&8Z3<- zZW;J)@O2tX*rYma6lQ5_Y0g;R>nNW3m0)EA%^nRGYhtq^-ECdGYw4;RW!raQFBA{& z$8~MQ%iz7k5na4?dTgIt$F9pL&dX_xSQ#{NGeQ47&d;Inp6Mu4C=d5N4A>T|@)h<0 z<1lqIh>W8+LfuDzBrY4T(d~T=xY%v_W`VBYs(GJNRQVnrG=}j+$a%{tN!^>Cr)?;E za;C!IsVUBIS8tV8idn#N4niWE>v!eLO%O@O=vt(v(%PJ;hP)BVxrOf*Z$A-Hl!zUo zB=gTGmXjy$MK{l`34`YGGXm63M@;4_)rg_ZIl_Jh;zgYA0L1b>XR<*c?HBry%J92w zjRZj&kz_P(ybt#pzZw`uBP`=({?NTbd|_IP-#*bX9O%AVNMaRTr62R4^9;mu|Ck|~ zBQD)xtuk(>UVbM#Lbqq8j+eTpoST{G<~cIF-;s=a-piJ*HEU&z>u@ww4&B1cNOUgG zc(Xg==_~?@;L%Yzsc>2?=hXFVrd(JH98xv!mY7HXbR!(3a#m2a9O3s)!g{J4Djeh; z-um*wSAEsXeDfzJ{_DT`{ro#W{aOCt%|GCkm#?b`MWHUWRc~*KU_2}K)#;pMu@+V}4Qm3+p_eApl!AHBPu6y?49a;_ za5sT4HbSV$z3>>{s@3KxZmUq|GhjWZzvlV@Kl7z8^BaHX7x+tm><2ksA4$p6iNcaH z9*=v`#R*RsQ>jW#30ch&plgWX>PowIrjrHHvxRmFh$O2BpR7BuMRug7hzp7=#iF~` zL=6QUi0H3inh_e>+UF$;6OZ+D1w9ON9yzv#C*1SNqv%@gy^f>0dhdDOQzguvtwTXL zKSR1vs1=dhc(@Ips?Zg19Ta&nPkmi&z$%zwAQX1O2+{M)BOl%4`e`}RYTjj`Aj!Pv zDjsa%wvNdeSv?oN$eA~Fu{{64w=_g8ZWxd>BTL|A^;1&m^@1*Ve^YR}Y;+@q+pAj+ zr)x9FCbhI3H{u08XWefS7}C8x0eg{Ci5OL+!d%j zryTiq{5`&!&ZYg^jROtcPGooQ`xy@i4zFE#saiSA8IhG`Dby(=YDPO%WttLq80?g_ zlBYx{EHQ)YR4(={_MNjrBR&HL(v8QINEKuB8~w2>A5S z_-VEx3#OY}1PX&;Ad1?}hY3l^L)*1KU#rXf1(9UN>t?N@E2ZNZ-7Cw|#>M5UaeZxd z18Zwg72=ro^X;c;gSY)2q|ff4)7Yp>IE&xDk8RYL=D<5Q&(=Qk5&ZYc_GXE3;b>fl zc@D~b;LPS$M!9;JbbPLt-^X4w9K9Iea@5$NzNGj*=02<|?$PbOOzlXO-Y#v}uzj`a z7nCrMH+r1Q_wN{Jww9kSd z_}Wx@UYdKg&r4-pwB;VAqD*rS!;1XC$hk53PtZIZrSzZRb)hh?E9OC8!zh5~s@a%P z{XH*`CnXn}@E&`RQnIkM*}Uhp=k_qlwJ0p+Pp?hj>3g)_^{~Ktd&F)p$MRYj_J8!_q6^2@c-i>t+2{cI&mQ_eMb zf#BX@Uw^=X8wIRooVwSX?maKH%x*9;Vy$RYsGAIv?4plrN>a{Jxjm_D7zas9v z_j9V7jqsT`y>4(&-LS3ZIk#ePG{w8rYN76)>)+d&d4+e4+|}@eN1HT1r>1yoFCZsa zXgwkFShQTcS1f|g%Kk+4^*+8C!fMYiN87)up{#1#@2D9dVpZA&B}*l#<M1^OzSb z%wD@9rXsCKUgUm9!U44)C^J^k{gBKc`pV6Z^6sPmfhoUaavWzqu)N0M-H-6jX<|(? zYt2xtin~^ezDaFG10M~+18hE6F0s~?r-$D{>fB|Xwxkw#+{nPlVmGXQD+TmxntY`f z*KF^<|JA=6WVrhu^-q0`bJ)rXmV=#KX;{}no{v_GLMDse$jJpkQcIHwlE@&CjC;F1pSIg0+(?I)9XQc(5!cgXxV~*?d*AnNL$TZF z0n3}0_;>eMZ#sMod{@Ws#zk$1j*s^>lP+KH1H7Y5AeVS@i9Ng9cfb4Ry4mT?)CSsc ziJu+!@0)v{lfyqW_yCy3MQjjnU<|}>pSclVS9Y9nx1w>|xwZL+Jk{SDU^e|Kz8rUG z*wL_zG-_ppr?hVNYZu=U#Tm0bv*3r*``v~DZvIkDX$-<4hq!73h3SNtc(#tYV=LcLnN zL}3dV3=cM6t6UsKZS153~2FD-y(3xqr%gPz1E6B&nve+#1y~d z)ymEDgh;jW-N`6LU6rfrVjgszc>dnPVS-r_S670}aJn@m`^CKQv{o`Uc0L2Q;qg>7 zS)JpSOZ_)umF)EfH&ZwZbxzh?qLSmIV5DM)zMXE(iMPaz&ZPg-gz7vg@ zkorfd(>`3l3}ayhDIJ%@&rp)^AZ3;X{^HO696$cO-_0NTnyDJZtT ztro$^<5f}jFc$?~ih0R(@Sv;jyY@cjR$bjg*lW}RaL>CytK=@-dv4cP=i8xJ(-rR$ zA?PM+6c0&PAHON$RUwMLq0M`4O00X%)!IW9Dw>pBD{h7}&$;hsMmY#%5vtRXFZ|X! zymu%({OGY8)|y=AFg(al_S^T`VR%FEQ-?pJD{Sn>jWP^09@V%EGyrjGU)rA>=i@%cE>gFSLmn z{Y0|Ene&M@Ic*c}YX})BXsiI0obw>xD_RQ4)6*L5K)g1Ig&k>FX#tT+O5vqfau(`Y zEvJ5|&NC8e%A}MypBBH*r=e~zQ>%j^-g&9sS2eLOW4HGg@t<85(|&Y&Uxztg;*hpy zyoH;+q!v8iz6wX^=DYpvdqDIfy7(%1*A6E(`rPF<>>hOw)Hgw18=css=SutP{a?Gu z1{7}%7yXjXu|SNeB0@HPx) zBP2m*+!`n*8~Lpf|3N9}a~*O{!t>n(D;g#0ykponHRO!KXw{j%g}%o;wJ~#R%T!G{ zU)1seqm`FhhESi>_e~(7RqJg9;Yx(nnQ~r-Gic`@zXekchR@W00un8x z(&Wh$5fVO+s+a&P)rzqc_4aboz5kaayiI7Jk1jV4sRaX5+4A82e*@(p0Dg8Yr*d5C z$Y6p|s|*$npHJ;@6hxp~zs83Sf3)|zy`_g_H^Wj~BICt<{sH(c{BL-6V=|i^xP0{Z zTm7EVHnqh^+3xkQ-&>OC^I{+Z@ADeT%jHNRG{i;Qb!_e(1!ccfhHEaE|HV^x#7?AD zb+%=9qE-sLawVK@EHYMRxcUH;GE>U}YR2lxqa3Tc(rr=U7P*_u>wdh7d~~A&wL+eu zETOC}Nhv=uR@bF4FRSS1N9+a$=oGVZ6td&u|t=yjBsPH&LIl-LZ zToaR8i2H+M<=J`SAPbYgF)Mmdo-Ub36duf##j6dI;Jhf0Qst(=mv7G8o-JJe%{LRj z_nF`1o4)nq2v7NEf8dYvsc-v-{N9&8&-L+O-UHPR=TY(4&mqL1#)Om>9vlbR==L6n zH$c==dpZppe+B@l1Bk^lnlKVXC6K~tRqft7^)ZaF1g_9vvKaxLj*czl?CLZa?1r@k zSh}G#p3y?BcOcv%yc~A5(mehMREZ)%$qc(#ic@&d%j$*6N+<;*YG^;7;Il4tR<*)k zp55g0?8G>!6+1#Ox-al@zT!XozkZ2-;rCzXpa0bNa5W#9rfg|Eeh%W^Co?p7h-!7C zn_B>aF7;pPDFVH2i{g9YEx|fbaF4Ni{etK}8cu)^GdwiC5gtHu>M}?f`Xsxo&_dkR zm1{MFN>$%y=warOJFdza_b%1iqlvgD+$+1|;p(NDevEs?F`7}FFITOosE4`J6zvF6 zkNHr-TU=X6ybA6CjNJFA%I-!WS1i079aAu9$F~%+Qc_Fl6UZcr2sckp{LU}G#e2^y zvu4tl3fCXMV!0NIs1=Y4J|BYHfDaLU*!ds*>;=SI>k0-pqj5Y?TpT=S4!m)Ai&__y zJm6LGJ#x9th)^#Srv=e!-gA^ip%J^xN#3(MWQ|UUEWF)9hqM+dD{H#pE`6Ce_vvKO)*Fuv%HiI<Jp!}O0Y9Yr#}(P6#A z=bNy0-A2ncS>p1MUEXeR+^5@94aT&P`^SkpLY{3WCYKw$JKEhm?0HjHoW~i`^0QjC zlcqd(By9@_;V5#`{FH(ND|L-;@1QZAo{WkeX}+X9|?afzO~Tk_edI+yaNU` z+HUPr5~KoB9GIx25bSV<9UEuNX=BC#kHSy0 z;t6cn$L4$o1~r1C(AOt)Lrc5NnLg6bey?r(e(R@nG1 zXi&FocjDLBm-t>a815G06cj>CS{rHb2A(%_=zm>$(53O!OG@Xnk|34?!Q8IC&3(@+ zE_uZ8j0@`0k#ck2prmi^7(a6wRL2!>Im>Q!Axv~CTB^V~v!2ZZu4mzegN34pF)De> zXD|=DJJ+IymGRGPUiE1L2?eTH6iO*DWjLL!DBIy+qVQTOr^Q0`&IvRriOPHh%Ob4j zmE(1$6nMVagU&Z_TZBjF%ALY`17)^?m1R-Znz%ZwoNqj@*Ps7(NwC0yO0B}GR?u3{ zh4+fJb2~^uA02r2ONpDBNmtisO?=_=@AA$c{3hS@?H}dU>pTAKzwjgc8$aSL{$3bI*=C)!x9m_i%F+7j_#r|WTZ*U~&LQJcg!B@n4s7u`{ z*X}{DuAFy|{IIMzk1c9-I;FTrRa}tm%sCX)G$>TYCxj2nHGlW_zQjL#^MB(n{=oO~ z!lRd%4p!AVJ1GfonJ^g03zjD=g69AbG$`yiqTvOmhn{OY=?+rPGc>~asYK_JPHxm% z?1h0!#Ju-myN@&D#g%v*#1k$`P?< zWO44TzJVFv8mI@lMXknaG&N+R2oD}*KRYhrjr31ArxTfMJ=d!^j$noh!qC!<6cJgV zVoG`v6Z17Cj;BX_?CHNsz5Z2Re)9);rbnJ5taU=j%sEp^^goXXfq|N7&;Vs9Wt?k)2{yP|q;%gc18;2U> zH%H#mHwZSxSP9sojkSTF-4e%@+mL-;JJxpbL6ZZ5FOA>H``)&l!#KBKG51b(H__q3 zQMS3~X#*bi@y0W>xpzZA7z50M9s;rs3^VczLlM`FtYcHr;d#!$R0hGpA2jAMhVnfw za&MVwL{WM;_U=4~<9A~3S2ST^FC2|G`(kQUla6}a5?X~cSx&jonQjz>TNY(GM{zjY zBTKcc$eOIcj)VXNQD`=~6)!`}jW)%>Jm$UNT!giUT_l7_Y5QQg=iZw=(-iJFj;L}y z+E$tZIC30e@V;x`pfGr(u26S$>4uqRqzR0bZe`uT$nGeDXl{%Sk$)brvOB+vs0xIr z9Nh*kGEkO|X&WzCJUaw~5j=JlH={e+h~K@r>$^1(d#nbdjmi6*;@Kg?>v-?5cYZZt zo-tzjevEjSb0bng1op(AHo<00(?Q4{p4$Yh&5n$l!=Qu>blU&u>yqL6`}T40(8li_ zU+ViZM2rluInBy%epZ+=+}*+zX6dfQ6zqt8Ma^@bGUN#l7p8^IDFe<h}f!X9!r8hpZfzpax%3m@<4PQw&rfM2H#QmsIhcu0P< z=G#gdMptS}UlO3YfkS%@mo~25r%u13#(_W!%UAIFUTy?lYYT0U`{Vy!g}=|J6@wFq zqDH=>jaI8hutIHn>s$jwxR#0MPgnlkpZj_K!nc1bfA|}|o^;5}(?kaIUi;Z6apSHE zDz&GXIgK&TdTO*T&M1@(_1xmdU&Ms2=7n*qMBjF0BoOLyNAyRG6pH4da&MI>=x!8O zPZwB=S6Q#FXk#tVh+=TAgtt(0sAAQo6>RR6R`9L*{Azg7m8O&{Q5en+NWGVL^k5EU z+d}6{SKj?VHLkOfT$xbt7Hs0x$t()c7{P?)V_PqCkC4-h6yf&mJO0V9y~W$ltS5Dm z70YccFaGWmzV}Y~Q{V82Kk@KIzMO^MIiLB=-HCV3Cmv?un4>Y-j(6|)!kC91 zJaLTCYxp6;b4h&h@MXSc{V21{R94WzgC4Nsdtu@IeLut>7*!04z~kp1C!apSTS<8D zd8RIjX>P=U!z4^OF?o^TFsKj9Y2I@?_mx^#Qa$tZ!DmPu3@4nhZ20g-pcygP#~zep z*<91E7~Tfs-H+GN!!8+x3K;~l7RyRA&c}`8qjtiE#Ixx%wvXpy3#@D5(P84*-I;Y+ z(W7^V5NVN|HbRYZ`u2H~p;bL;$6BZ5oH*-xvGykA3^GZ;Qpg?cVmFx0S=xS}*EWR}rJb=`gt@W>K+80TNc2)$;0-WXkFV=F;SfrWuvQJp-yr602^YrEUej zP93+aYadHTl+9JL8K=8){O`sZ`yJzXkMOp+ua7?bA3V=bYsZghSLqf+_eQ0ioVfWa zuK0OMP2(T={P28;ES7pLMlSaJ#@Y0gu8;iuGaFblx{7S)vTN?3Qr>f#tXnA-Ge zwUGE_XsH&8dac8@NdIj;+5IO)=(?>VtoHc(kWmbNotXOmwMwyS*BN^U9nNf^=^G-~ z-)mE-f->%KtDmuqfZ%VX(;ch!YJXfPhW7}fk>A$5UZP&SXovC*=KM_CR6O)_V2m4! zV%mAR2WHf~T|0u&S0`=B?Z*X@@RDuE?#tXyyD2-vd9=$KP1q5O0R#F&F3-ls-I6fF zal2bLgV6sN7~%M|POl>*sE^y&csu@K&J8wVDzO;@LGVjtOk?mbr!1T$ar+FUz!}58 z^Eo0-Y>X&AQ{0#%0(h~-bpc7<8@*aid0C;V*Ja2?JLY4x&(Cms5|%YFr;3tT&WW5B zgluh^(nLCD%FW7jNUXKOQTV`V(m zXSyd=rS<)$8Gr&~WtZ|K4R0d{yYn=q7k_geHssov64VU`om7m#sf?W+0_SS;8bfOx zmF#^j&fA>7srC@}>QP!+*wC7hxHNh)F!&Zl6wwHk)_Xgmkm8&Kx*p_rQNVj=BGKqJyl5~7KE zSNPmNevi+6@i{43&UJB5y8@?LDIyjwo`hfg(o;VE!pzqe;p4Ad^S!S;peEsq=apZ1 ze#7T(Z}`mJ9WNdd*CcXIT@DI<8OEw6)A_dzy#fbq|E?tSONZa&wYv|Yvloif($L+4 z_`AWw6A#Rz+7Qz^`lK-SNTQYlNsnAjiSsHPvsLRZ)l(ExBY6!sz>E(`74y;;H-;8e zPiT6^v*~kWebD)4+$()eO|a2zES$#wg8;GT(Tvw@KD+<5;oE^aA!H2{YcDkzS?ONM zQSRwu_q+F37;h~tV`y0mrA{2??yncIw!hX=P1+)4^Awr^6OU#y*zvj)s+&4CF&&$S zw5i$bq~oL;vzo(0EX^m3zMXhTr>nJ)^0DDxcA6UBClT^wsTj3ZBw0&VAtGgfYWeCF zWttPMZp#0!S0mc}u-LADvv7g~#-n5>SMT4?wJJD=^RZ~VUmk|4Y zF{$l!{(d~$;dWpP7yAA~T)binJ5L=(7;std8$`Ez6`&oTZE${wrjvp9jW{PnA9Q6y z*mR%xtfLSw!^Aql=*%5jUZ4ZH-KIOh8W%Qv7v4RuniwvPfwpXRxXWD~oElRY4=#3F zX^b#*fZo^KybWnGAwLL8R5~wFQGK;=Qw?J0j5i4^aVV;fe5})-ZEs4Zi>Hs~^aID}Tc zcJ$usa3-T*elh*Am{q{&uv6m{JLBDsGBU#b6FNu_5e@?`Ud(4WhnT^>0{%vQm;S^y z4H<~{#yWpF_w8zTm-@RsXlxXnHs?4)=`<*vy!b-kD%6!8 z2AvE)4p(?OiPHl$EOFQ%2J z%uM2f>y`5LA6WVDM?b=x-{K3O{kvSvFdb(P#S>n7Wnwvhj(_ftd>jA$-zvQH$Y_fd zuaTythtl5m+2QpbaP226@!x&l27`BAqiVuR3V}8?P}BBoZS&};cpqdr&b2>-KKe~V z;X0s2ss>N6wN>kVjGA&Q(v)0D(dMbcu_BiWouNKBiH=egBam7QS1AT0s(Kavm8Uiop1FygJjGy@1KhK~4 z)^FjDe&TDHrkOk?OOvXCXn~xZ7bg=?tM4J=0(R=~fzK@p%r$rb&2BjLwrI9hN`$*o zbVw^0qbPhE9mhikZoSI!BCO8TOEXSuOJlQeY3JY2L(hFbn(W{Tw<+3n$Tts|h)V6A z=O52;?5XZqQ+3b0Xzw=RYptP(hr%B4rak;#sHRAZT5h?^AqgdC_f~p=hqc0+pMH5I8S&w|-_&ZejqgES85~|W12o|j%9&uoK_r;6k zl`xcuAeu;`9u;9Kw+LL#2j-kR$CG464bfIePY^Qdz195;ooEzYS?U?e4NtE=(<4X* z|af(8?2@&q1yYPCpVm;kpdw=bs4X(C8BdeVvr6-!tKICUwaS&-7PX6H7qhtSlEu)<`rC< zCcGHaw(lw)(zQAg8=u=Kja3;!&$Y=saS%~-(xO;G=!q~7Q;?II;N83@KG#tGmo@sT zS5qiz-^0*PB9yQz=R}Pj!g?2)XAf_;Fq=@&rS_nv8oBT8wO^apz7sjKVD!CI=$1u@3)0@z_PnAV`e zwrO2Zf_SXRhGy)U>*!UNxL{P0_X9TA7@6fg&mIKW@fB;>5{d!OM+!OI6+Hfavep9@ z_vBI07SbPrStuh-ndncsTx^WF1FLbJpZV>|dvDK_#R{sSP#3#CoNlUEerSRD*u&Nv zuXM1s@M6&7k=`C*A^^2=zBPqhR4FO5uJH7fxt>?JTPbtqu4c;J%G*y94|0K3s8i;6 zcOqvf6UYiO!8;1qlRc}rv2J$3eNBR%tdQ8U07>kcYPqCEg{!G@J5AJ8SPOji20nQ8 zVV3ioOvf|%>OfA(YuYFxiR*_)9$po`{S&Y8*0Z-rQH;mpReL4#3qWu@9V1nQ?j74- z9ZE`rAv=hTyyz)a;>1@QqYbnbS}Vg~Si$xJR6_|KnR_VcW2k;0n$M>e``fB<4~!y| z^8`{8lB;{r2jkY@A?Y|tOCOPeJ`IoTyj7t@Z4UQ(M{aek!`ueku_5}}M>@gyjEWg# zhcphvk1ci9D~|jvVy@CXKfa!v60b~G{CEHOxA;GQ_w)R@@Ax)8^y;hRS*(VW0@b|e zNxUb#bk90C5{*TQaKs!%oN9*h*jJ4;Mwq)BD*(5}7(s}<^Dw+u-=pYzkt8=A&NV4YZU7K3b!YTkvwVu=S*EEh>?p?p}OCIk#sO7nsJp^YHT3 zrj*-i!n1CCSes&=#1tJ+vM9-WoJ-_nROR{aKjrmbf66;|%9N}a<$^~7sKMz%D6!gP z8&Iwj{QlEBe&r2#^R)8aYh@MTWw}D<#5+&!c=+JJ*PV}i%kcrvW#x06_(#igp462C zGNgL+^J{#T5ex!<6Ti*pd3t=87fvsdk_XYd%w|FD!s@N)3f10YhJ{uATv7&gMO+Kf zT|kmBCqo2FMRe*erljWtlc+^9;m#{9s8&{8DfNt&6LUTBrR!fK)uT`UUUjL79z8wl zUQuByvs@_DVT|*?oY;hd{k~E_Ba%Ig_TuVMP73_;XN1A73rz8R@o8?yk|f;S-Z4){ z?$*k(8q7|UwY^IoaihiRB&Zipi%n?;UajCo0wqO(Hj{fJ=UgQmP`DzQe7xM+sCMIJ zZTLJpZG!HUGEcgVK{R;O$~VH!ThhD11(>A=PAq7cS6*+mx{)x80uKa zo@>~NRaDsx(@&iyyVdOnp=@6Gp=QUv$teh&o<-ghVGgy(q(al|K^lfYUYp&r<##v;h>6W z;@L7$?vy!Mw+BtilRM$11cymkp24#zkxS($=B3xO8w^&sISX@Dq$szN$Qf=$NN44t z0BdIow^f)@Hqt!{w?&x+KA2aydWE$6EH!l$)gJ#{({g}wa6q#lo1PJ55`x}n+o1m zj}0x?I(p8B`(pL6M+1*(4-Eyb5pFj0B95UO%A5k#{_Kb@h`KZg$SS-z&HTZ$!cYC; zKj4SH`ZfN{C%>8FablW<1`Crw5#iJC-EjvT1#VA;7az`i_|cIkOW|}{ z_`PR$eDv`He)EkdJeo7#S7+Wp`27ck=eaf*4BT(LC4LV)R1jX^$j@K>1OD{gpCD-^ z^2x0yz5T3W74}zG&{y?n0K`%mys~}kzwTBVnpl zwVsiB!}IGeaF^d=;!)>uzJl-+j=EC!jnNQZMx}luHr&HY2O6>aW+UHp-X09!pXD3g zWVOw}x_K+~-y2t17zADE7yqm&C*p-sA6;{|6qcn>)Y8zF)gV46VevGx*q02Mt0aKw=T%flY(N z14u}`j31GB;B_7V3n4Zk0YV5g!wk%r8IO%O?D5ie^;TU=cUAqX{@b@?=DiVzhZ83v z?#=u+BBk%k%$xVda^l3<&k6juKu($9r$q#i5W7g9C4h~#8^38$3r;`Gz~x+^@`FO} z$d02@z37@VaTtw%N$!mNq1FtM0BO$kvoDarqZk8S!uY2ldAnOaQ??ubfy`RC` zwH&z^&&dsEyIjs=Ww@yNthC>C^-Qx8NG-%yPag7h=ujfL)*uJHfw4iOM4oKpXOF_Jv$Lz$e6Gr^c5eAx4u=LtWyD!Va z+F8fq#N8$RLe!nvG=ge*m3!XSsO6q(D=>!< z8)UIMCP-+B2=Jrbw0LaFdT3wOrA)}rk`=*z@07@;#mm>*7JX3xy?l4roqMhciYSz_+8Rd~T}|twP<`FFpC9W!eqb3&_qt(~sDhB^^P6 zhD%H0vE0jT(Cw>~jzzh@6Mp4)2LAa!eq$i9KgK9*}T<=Dapyb>B+*! zMW{+&p5b_~YVq@|q@%DMXO0Ie;&zvW>wVzWF>$dGZl4G4zntMHJlteHxfAwDK>|?| z4^!rDgpe#P=4veA#{!a+!vS=LTZK#PymTpPGPz9ra;Lkz z*pGgR?HG$fBZ$O8L+!kD7gCxH*9E-`|-=QkuE(dt-JG5|j zi+)o`ig?I&310{E$VV6U#XR5vWe~OFC5pi)sCmqT3j0TQeDj^>eDKl4PJ}@d8Ur)J zsCK@tAk0A+6pn?C4%y~@6!RKKgpkX5=~2j2;`T1_jpH@)iL#5rgYCdxgq;R{``zb! z|8Qi?!iW2r9q{sI!`%b8cq?|5w(CY&?r+-9JGT7#@caDv_@^1u7Ae|~Vkw0)%B-15 zGe7MUg{1$`k z+P*3R$cZ9DPkH)HY(&)eN0TeM2P%th^3WPq(8)k_ueE@6GLUW|{6`l+_00XfOyV@q z=h3x4PZL+s?m14$_PJ(a49v5gcI3zTs#syvUVaYI&LIc|mZ3ON6iOxcd7<0((^wdh z3p1#kLX8JIeKtDm^fnb>mnRDxLX3E{pYt7&z&y`{A(|mGCBhKt7e>>J*C45NPlD&r zFPCVckEoBi2fT7x(VdECizO`Uk-gvYy^d_2vtn@zwsC3n?w_tHX~o?eysx3-I+jx85S{K5f6dfnqrvtz#7F z;n>f22_xs-T~`cMiE*USu5m{ZVW~LPxjF!(U0jW^PB5=8uJ-hL6@=x43LvWjd`9E< zs(EW`Y|UNkN8Sl9?uib?Yu-;)={A>p#e)@mbD#@Fc@`l?v%tm>YK|YSn4}gJYRtM= zgG-p5hwu5crG!@o0ld&lC>Gw}R^XO*=*8aLk>lZNJsgb?S>I_c@3|GEQy1n$PICTT zXIFt*ZF!A&E6dQz=Cd-0Kn0+;TNtEEnAM2WttIQF*X~0Z+sSymNR7{>5@LU*#}{e` zhPPTnWbGIb6XHyc>{1%Eg>83YM@KCBah0wsG3omEpF87*UtN*x^4Af!I^T{}^bf3F zj-Ot{rypGHs|;vizv!6663(5Mzn{yxUuTYs2HS{U)WD)Gr;KI(+YS6z|I^4{`dQfD z!acFG#ly}7()n19BvY7*VxAMo*gnT-`_#gNt?86u+Ea>4mW)m{Q`RslWV7R-iKmBw z`(xtSb7j7>cH(FP*yHQK>z6Z!0pcB8WH`=t&v*fHFx0)=3ddXJ4!FpfDZ&0A9FFkt zLYcDV_eO=A9N3UKGB5{0CO9g*6avV=WgK{XoA~-W*S!1Qo>v}5UU_BYt{Zmia3f(^UhYKO70#UZ}qWyOb*^bZ-!Emvj z>d@g#Xo_d$2Yr5^D4L}aS>M;a(V_fMd4kgdY<)E;dCs)}orL1?)oiIyU?JFrPvoT4 zgDz_HG>C;yi%^bwqPFMs*Og#!Z?^57o_8(w8!MQKbhNZ7=?*jN+sRJLA#vK~OJcVd z(O#t}so=&{HzZVcniPp335GauFTnru_3!Y%|M7PKZ$Oa0`HMgPHb425HwhAm(L7a} zARODcLNSu`HEq-ptLsOzDeu~)6KL6!_IobMv8O`_l#1&fc8)3<%30!G^v!vguL(_& zHhx)(vvrC`p0&_|;^i*KKWQp>9H>e?Zs<`g)Lj)N*Sz(ddM|luV=0OVsZ<~jCFCdA zdFB>jp@?LnC}WXDk`zy&2BQ=2-SVApJ?EV#6Auw~L5V0)?8JvE*W_lb4n;m0M3@jV z8PCp+#Z7o2!iXSK=96c8?r#FO83qk}mX3@``Sh3>wt=lGH#rkRM1%1D;lRU31IH&I zUlcYELnHGGS;GGrUWf*^Wd6?8Z}DfJ{B(`zQHu9mGe=j>b!I|2suclDg;Hmt!Luqo zpiK5z%AcLBW6HS{`9q88-P z7J9>VOTn(hs){Tw%k-Lre?8y(>bd@@O563!%}aLNe$q+@m%U0!Da*v5%yS|uR?%IC za@d6tCoM9V7i#m#1^=C9)RY37AqJ+La5IyQy~r%iPpTl@45=RCE`yYm5J$IWC~E|k z)}A!>K`u=AZ6up=dK36$~Su2k! ztpJnXDxPECJumA{mki=XFBjx%-go&Emg7Pifs=|arJtHItA2O)T*r!5ZezjlTGWg< z0z?s(pq0fDXSJJtg0R?A?Wfhu%LJ>1&y{nh6DRk-0A|S9-|DvS2+%sjy~4B_vd#zx z3ue-hSm~o#R)dt*cTYynGL~}xKEcaL`4ewiDfjUq#0&F)OR&7{T*BHlo9Eoq-{zw! z=!d<9vsYyYczzcMYN;=7SljP|os%ZOF&749$24wq=6I**C}@b>+hz{ecC_2|4LrSp zs{ljkm)XuizyGQk51!qc*IzSSPl2nTjHC5EXDc|jpM;cz=gE!+N-7+WFz*xh;(cEG z;*ay}`g@>vTwacBBj{1tU(bB}y&FFI;2HD&8M}vHM$^pgr%!q1!6o~wq(Wa-@(SmO zfk@eN9{L>`ef2f2LW_qJmT>Y`d&Ir=jn^#5(27>nYxq7tMP30bR#wI)CFH0Iq*ckX zcar51YN?D*Zu`^sEgcS5MY}vxN`c$`Wj{7gs^=(EmGf3y&flA{mF}{`w>LEOhR#Lw zg7dS&gTf%SSlA-0^$fk~WKetciIdvBDQLEWs*;6~h$i9JzVivc_RfcF5ng_9VTKMe zNi!*@aLg%gg!UIa_GB zolsKxF5%*SVu=-IE9YlxD-xH)1ofQC8RuoJIY2#gT$>l%j)yDlQQnL9yZ^4Gc$uml zqn5DT)MD-_Iqy!GGOl7q$qd=@%q<^UiBZ`^GY$);#FCUJ@80q4-+0Wo-@WCy&x`^a zfe}NSh%zW71W_@VN#KaG*USv0oH+tVY>q)pu^$Bn%)4ycB=9^b`=mUc%wYK}XYLJw zh;Yc6%Mb|ycPJNPQ8Ujq6CNosfnJ4noTZBnp~+OC$UvJg@Quy;eC_7PNXd)AC1#yD z>dcXeqaHahbI^%BGY1kAh67r$)(tL=f182An6({yt9aCcB~D~BqUcQ4iCHId-ZOBG z=ySgH@NYv11fq-G9p#~1krR4#@^TsK+)sT=3qIWLuKWMwnNty}DJA{B*8bP!IHmjf zKbu+==z9Xtg}5xZgBSh2;NEA34sM@U3S z^UScZ^NuS-%3cm3mb4T*iftGKoimbCEFYXP$xMtR_RC&jqH+mvKv%D^gduW!x^#T@4 zwk<+gN$uvanv*|Mt2}l?s3TdAO^YI-Z2?@6>(3^4S$$`@wpL8Hgvlv|!PxgY_cD!Dfznf0OQUnm;ME>F zOB~DEG|)DK(nuxHd9WLJb+_U1aVAOgc(TR^7j%VAAs5SY@51AI`mIV; zgw^xt3x5z;D4$MqtHR!Us;PU#CoNKe=cd9fc+m-U9o_yT!v7e{d{5>2V4~tJt z>8{>DZ@NziSZ_08vE}vlOQ{C86jTqhUF5DOT86u{XkAYzn;#d^*5_MIDBkxyO z7#UCKgmnq|^ zSK4#7oZ@6>eOnPQQyS~NMY*=;it?Q^Ij0&To+2g!Cl*zt~1 zBJ;K-$2@CbdOq>qA6@g#Z#?0%hy9D}D6h$-ZlqV8&`;&V0+ z{RMD|(;!o#Vu&U~0mrP|kAXQSUJjanMo%OI&oy;z{FlB?wFZKEoFfc9JgTMTK}5D zsyH@KGTb4P^MqnK=SdH2<&JH*=7X!><9hocQ8u=Xr1M&KJgV~U%B%XlwkB4+p>g$V zBcvyLb#;01TItqp42vI@``<8PDf6t`YQJ`d_xjf6J8rj3$z<8xrlh^#5iW)Isanyv zYW%YPk4ICOg9t;6)Rd;I%=3(;B@~4+TiCoC{LPUy6nbTQKFwCkKS&@9ks&q(T{T-3 zcJZ~e0frD6#?j=RY{w2}EwB%z#sS%seVYEG^9(MhYbJ{wXBb03OSN~uSyt;V3tTy~ zf_}Nee3?oCRYY4jT-&3rhA!o>)85V@Rewhx0eN~`jf^@o@Kf1wDMPj=&dj~Lw4QhW z`_dtIW*Fkn%Yr4=@rm}0Efx8dSo9)!AcFK4Tv8H@R}kd=02_1t<)EW2p8NL9LBIgg<+hpI>mHB__Ze=h~R zs-OpBdNG=UZl3gZ{Fzr>??>SUZffzlZo)3?y3$__1`DG>Z!n++h^~(34+f{al@c zEZ^y_udl5~S>~>bA$EJSo6nNCc1*#4T@cE4e`SykXK3MEz9xO&-F_Ef48pH|XXO9* zy$!EjWWMsFnQ>=*WyQj7ODKCR1>&Tuj5Ht0M|bdMxY?VadLWRJFyzd15SVSe1BvHT zWGA`iyN@VO^T1U|#0dK=FeyWZOW=4A4tp_$yLipN@HX5WA&#)0grl1AU=ra{lsub) z9tEzSz}*d~1kzENCgu0v5&qBL9r^h``;YM8{so^s`X(ZoF$zP3IG7cG90R*u-%W~X*jY*vHDjq%EwlTdUtyv#I=BDt*l8saS~ZuWfs!IlsQi`a6HbV|%Flo#)Mnp5?9 z=i+&{s_S;Y&Z$;Bw}H#M(0+C1$f&}7ewEqo>uv*nfNXcgFnOb+&&RbWrHmQfi73q2ozQLHw`$KSZ& zcYgaZfB)+@JbgSdD(uLex7?O#BlfH->0lx8N?;aY7Ne_4s7I=enz;h*sVV0}u;`qP z1g<2op`}w?1!1q5U5FeC9CslyW7oDK%%#%(MF?h0DaG0(!R;p*cEZJ#6|jtb#9(a7 z`WPdhZa(Aw;XW648)hbEW)2)V>cl}N_8hq@%6c+$5XO^R=(LKPC$iV*Hu=ej6RNge zQr=Th&Q&MKMQ(8~S{_%oTKSBTr#os-%>Pr`&k|IBtkB6nWSTfq71dlw2(g zFXAvX<(u4KU*6M{%F(4(ASQT3O`c7bk2Yux(emyAO%t-flny_{)ux{4wso$bdRD|@ z$yO@x4}SFu{90%7A!uO~XCpwgJ?a0O0k@x@boaeL#(aUYz8;sZo;fG`E^ldgudAC> znk?bzyA98m`QS^uf58~KdrtP`i=IC>@N(ba3XcPI;bDMeXOdK{xxM?RoFh1*UGX{BBh|KhUj_?iqK1&&;Rf!4L_a8+Hvugo;`sFSD9x|g#B&i!zaqme(gtj z>7{$j^MNp56Smu$T@s2Bafs!3ED`2>$2?8^gI|4{U;dTfKwi4XD8i52+wsS@dxpxW z%D$`k+6-)=8sDTUj!yGz?K~u0n<8AIdFM$HHkYG@&wDsWiH8zv9Kp zFueHSNAmdPE*w8ZDFsBzQD6R;8!oHI+cv%_-%EpoP-;|!7Pi}Oo0{KTVI!=zXS-JZ z)V<&J+G_8Xe#9fUMi@4MVJIn9*y$h$s0tw|nhO;3l3YuPZ4~u2w47@~DH4=z`pJ0= zrSO{XTeUGo2|T>mkb&QL?^FKPH$LJ|eeMDO_*dWL?YADvR!3WJ0arVHCSv5Ao< zn%RJP$g^@42PREy2ux}@LPLzioH?Y#5G+k73mi1D4UzQC%=O*GgD;O}M3SzSOId%_ zz`c0IZ-4Q3c=hlzbU#hFxbLrN%`NDPDXmj2)@C-A(tLs@TFFG6C-rKsW_c%kYa}XCAmcUyZaux>16%IK-Y6xHv9;I~P?+aojy+^ls zVWn$TgoSe6Dyz%M_?ishA%R_fLlM6EoOlnzubud`Dy}xtSp@?Rq0}BZsD!q+epMC8 zf!vJ&Ifd_n8gxR~V$B?8bxoxEqmIG7<~0%^tnTaof=4jW^4EQ0i;dc?b+_Kj&u#ZE z?prRb9S_*yY?;ggmVSRnx9w+>w^p8W6IMMn;e_64DQNVnRkLKbHkV6g2Cfgk4?p%r<;!oISnAtK zD2llN3DPXYE#@^(aNL6on1YpICKRFCvJ=B(XXHwTF+mawEgcmOw{Wqs9B~hozBxwj zU%~Y;a6w`}xyMq_WQE-h*_48N58yD_UlM^N+#O&RVVJOEj}+1!Aq8EB^5L z=Xmh&4Sx1(e~O3qx7V_ph>F z)fFxEJlFg}G}Z=jL7akhJW1>z+?ubI)7uf|3mk zQmRa*gd{)WD}k6dFkm6<1VuwGXO}xG79>c8jWHIE#LYMyiS z8sam){_-!IgQmnOtOff8t@00@WZu*S=^j|Wq}|ggAPW|X%cV^nSlHl_ zu+ORHUDxYrE_53u3?p+^E_NHs*zr z%%BRjT^qiI!^arvxbmDxX)dyLU>FA^28+5WsWe&?c(^)(P)`F#?MeM2?P5A+@GZJA{*gA~p?(5~Srax)-y}#A3uh6HHzs`(F zXo~zo;p+5_l{omK$?5vuGtbo_N&nkQ*7Ua4h`qS2YOyDstmWq=q|OXTeEj_$)^km_ zaf?R=HeIvs@3lPBwc`fL;iS<56-S7EG1dS=%$zifMV%WVzHa4keb6PiD^n(k+E)@Gij;KL; zJZtOFhsuU-(|CKe^9IX)t-I4vx{Qi$9G#`D?)!#DRL`vMsSdR(yk+(0fb4LgpIG01 z8+(fiF&ffv(624msO^BoWk=XWhgxy0fvn+W!OA*jLlZ9uLJzpsWQQyb-pe-ozTNnC z)+oC$+tSg2_HNIv3H@?07JV+p+T+RT0D9Wu>-yjV{^oBE{M^qSxV#SpbJV8dMW2tw zxHX%1FbM3e{tV$*yxRgXD`SLXw!ZTWcaty%<*+v|mIPpc-6ibrgb!e$G1??U(zYy<2w%#$+CaJ7YFQXWr%mn1VL`1CQ{>;u35e&jd* zQ2F=%&7b7qgG6#v3nAB-fRs*RTI2C ze_A=IfZCITidW|tr|%za4~i6TcMPTSuo5(&WF&-E{o9-B_d)LL`0D(SdfG*OPI_#W z<)_NHp#c%AM^cq`mhatsU4ilZ^H{v*W{4?i2j#x3LYTD~RdSXFHd6Qsq19&i%5?QgKV6RA=`HzOv@xV``B`PT{mf1!yU%S_y2y+bk5%cEP{w z$GiEwwdQaOifOi{i0c5T<(O;4>IpbcQ5aQ_EcJN13peva`;0emh z5_nc*f{P&Bs`5%0IEZpgnTr^>RjcGbW+jI}%84z3s}T51ExlnBVI1N1apv~7_I&Xl z-m<-jg?E^|k;_t!IFcvTo2)R?dsRS=Rl4 zkWe*5Gi5iR<@{8^jIZcK&_ra9hC6=$rC(!;6M+#_YP5tK^VW2z?uUguCUt)-XyxhF zZcTL0uU|FNpss5b4J+?|(X-Z1lN!9=)9yM$qIP_^myzoD4Y38otu!SrE`?))<1{ly z7-Hb)1=)fyj*;mw*>PqrYl)?g2F!?LbIVov4pt?xjKlg=Xf$iV8yqNW(Th>EC zJnDQ|I?Vk6Yq5T|;C+LSZ@Bf{YXZ>s)9J(hZv{BfMw{CY6%VYj-fJ$7=)Kfk@a;t4 zs%^GT8*Ez`ZVi#9tzZR0#?vVbV$!rdLH1;*7QUanQ)t^0!#>C?fqR&U2q zsEwfIN;c)Z!qG$R+hW(9=;$bQq1s%lP+H(r+v)HR^-!YHY#mz%n_7^m5oF`g9kHl4 zFB@1Z>HTdL5OhH5@lP6KGzFqV7+;odI(_K=Vh?vnb$lr#yKM%*rLQkL1Fi*mR zOJ%so9G}7CPvP(+@_&7EE}ySf>K zakTHqmfgjcaon=mZrKH+Ww&=Tn{D91CX%ueW_V>2xY}-*fX}9hJ53#;cYl>^s3|$F zp!*q+D;!iKptk0T<#CU>D8q$rEFLfPwMTBHr498M+nAs|XQM&FML#8wAcGsLfu7B%kyN^Mfm)dpre21*+xCxoTYP4C&g|62**I*&;>^E^WtG@0Zg@?L(CXXKtd(HUxItEdcL+a>E~e{G1^M zzWQLt554h_AAjYN@p4D4*@BXA&s0ej^9Aey?c3j`M@HuYRbkG8 z3=mQ-={1Rs=33iCX!+)fGT3qerEY-xci8^D2$8)g*XhWm1a=ZQ z=FB0Ov5=$k-Cy7H=0CLI-b+JO68cn@Q3kH>J>{EU{Wf3tqc^$HiDR&e_%%gAY`xv; zp=w3nL}x@sL;;)?B|*ti^9EJBgeypd!6gfj;4rDQzGTepr!IZKvACUcs3Ujee-9s3~jts_G$NI zAlBl^_DtuJ^a(6vGAE`vms1#O>QO*EVrGa|Oin_ft-_p3GdU z-PPqidd8{P8fc#vuYR3a;E+FG?^p?=3biiFc^PjxzSczN80)Stf$)N%o({47EbY3d z`O4x6|NRBgA5_-181#$oT@G!XLh=K8SSzk8Yg=2h(>9cH4muP>tJCR0(jm*bA-}aC zxh#Hg0h-G2WCipELTcAwy|2r|fyMJ@=DNVY7b0%~uIX?>7OjjkTMA@DtiCU8fBhgV z{7wi?TvvehvscY~-a}M60V{y$QaLZ}wGc_g4Ie66&x7p+(O&uQy)outDMFn0t2M>2 z16W(fewuFd^P5{0;@ku=mv}3e^mUXvhAMn^V>YJZ?R7(!|60MNQ^kCtSC&5f-r}I5H*T)Oo7^MbyytR4 zdzVygxf#xpqZNqGlbkky`q}V9D>K8#d(OB}zi16r>-YG{6nt(SUb}ALMxf=Omtk9~ zwx2Hr{>p!}<*)w7nOEMhGg)^VAsv;wBZNY~G9_9yVL9Oe@&RUr?Vucwz+_(ZRN_N} zK-j?b1Tw)q2q7q&4csd1XV{HO7_bWO#anwxX4nZIKT#e4atXHwxHrP1r|{+j*iqU% zSkCX0XBP5w^9&w;8u%}Nb<1bha3zViKK~FMBkz9j9k%jSeMbgaEr_aejm}X@N7XJ6}1v+cWb&01rPmRJ^USmqH-&{Qh&^`H3Sx z^v7?YGo+Xi_dJvc7|I%<#e*)ctPh|Ah*t&|vYG)SA|RHwAUSh$@fqK^|GOpfVQe%; zJNXNzB~7kyzqxLiDjs!w(s`Tjo5k+ZDx*~ex#M-Br>aPGK%8afwafZn+s88k zfYNP{D!=;rG$1tRoBb#p4}0QxK}zOTPV-C*p?o*v@vt2+k4uXf6~PL>=}eku7+b)v z2*hC^9VR3W6*eH=%wkTBezuDi0pn9G9`8cus+`)>Y@e$%;0Cc{mFKwwT~=g}>Lkx9 z3{r%;u6nz_w}?UqZ?_0~W-0vMZd@m81xrIwcYX0ymY@4eE04^gtkU8>IFCEh!Rt)- z4Sza5y_!(J$4*}UV=woe?PTqI>h#=h-u>LpbX513&9!@I!B5+cYEt@o9acQ}%mO=1 zhAIS`OW7K$dv67q^(^!pa&(idR+twIMCmLarwip(HyePK{YeM3H4gMNgEQAm0a6Pu zlTLB(W+ z^C}dm-`@k+38_$y*|8yna_?$6mdc|ymQ7IBTqk7hteL_>Ba_a_zr?16g5EQ)euFI* zuDicd3+t~jZr?mb=<-;8p(=xGPE{4d!dy=_cu#0f#=qR|v|Djyx_5$AkB0B94$j`F z;lD0;q4->u!zt70R@#kZJzUq!WifYZKRxjQssP{u#FDYKv9F-DcvzAw7vFrqb_fhHFht?r z#RbE|$h#kWN|b>y1VR*Mg)iLS@?ai#=jN`Xz2#F;&a+oK_u_EAXjM5Z7$MT~*UJ)X zRczF}Ton;l^kIb$%^=v})?)ulC5=Wyv3$mQi)nSDf)MP?_PS@CK(27N3NS8s_1vp` z>J6u%z1Mx=LR%pg%{c1!b>4Yx-t#sk8%zaBE+jm+^S=Egk7kT5^XNS8eIyvAKPjIZ z{dNLh=9TPPMygWW?^i{Pk3e11tki<7s#=fO+5m?+@y^p*-g$nD3T#DqV>9yAm+$jW zf96?7C{eY1jqMmZ&o6|O2FXFm9TK9k1&*}*8ubzC>J=peeyQS$k_AwM*N!K(x<9b?|#>p)7I%Qak0H1XRFLUMqwx(^)Xtp zxELdON(2d{?B|F3amE<`kfw-vp*X!eK-7|sl+M4eNI zhkYk~N8K=9gd_cLov77p4ccVN#aK(9y_8(r@_9&i@ywX&*A<++hebI4N&nh?wpVrU z^|-#er+;47gIdcGC$ATK?N9nRx4yKw4i=YZBdtP4At+9C7PG01IycdiVJ_gj0zzGw zPUx?T=zF6GBdz*LYr_b~ib$QoVkerqq@eibtJbQLFsP!nK$vP_##PuavZGF;tS<1l!Wz8oemxwHV!~22%5)l6 z%P}`VxM1CnqbK;4_>=__#~<~L>xLimnDi*WSQ-HjG3nsu!2Ef+Xx#hHeeQi=za zd3Ly56>(X1ZMx0MS(#&< z#hF(}i#!5B%h*GJhgZt4eLeE@v&d_&B@-5l5|PxxcK0PJ(yie^EL>D3n1E?;?^4NM z2gb>cKf0aaY6l!m$c#bA6Ko>rQIRc#QMu8`aFe(QAUDeW`>;8{w1Ec?lxL4&zJy1Q z;p02lTnf)VfamW8F5XmhQ2x_j-f{1q@ZiFZkGcQglCQk|L!`qkgB}sCY)uDYH^7Ai zD_$rKp3KVhbmEiG_DuUNLwLe}_doyNyzy|uCJ38R*p7i=hq-*p+6lC&2Y3|8Poku9jb5Dyrf=nxowz<{0OXF{j z`<0A&f89KDNdc&|wVR#OEIUV-5*F?JNJ+CD!D)Zms>y?SZ;Q-YbI~g;DG_I7JU#ys zq2;p+%yle~5p{0WgL2NETVF>0epRx3dqX;QyM1?JhS3_hF;VxRSNKFgoHSe&Q5sHepU|M6R&GCth#`bRIZJw#GyF=q8%0r1F& z5*|fB#LOKYb{CnY2RWfm5)+|s&4ib2TwS6f5G>>p2B)H+0vwUx@ypI84y*{JYy!Qz?>D;%yu-M zl(H2_v;kUCK`qWgqpMQS8&4@SM6Hn}Uc}NAz<`&O^wpq)sYeOf_zXXs3{}@(NB6YI zYMs(i>9Hn7qYx;^rWM1Vgc9EF(NUU_7>i-q>t?J{AXaPG)=hocmhzb2w-8P{2a8e) zZZ2<8&Mp4wvP$=^ESFanU)pzBJ)nyL_ei{&$H_o`Kwsnj9q$78ony8Ru+zDEKs!n3J`X)m+p4+yIm%H}+;&Rnjrf0Ew6<|6Gh!q72^l25xVnBv~% zWi)~o*e$9{`}+7;eGTinwQ+QrPB!JDbh{ws48j!g+HDKjR_6!CZdzn-g-XSb`n@MW z9jF#x&k;uLhuXMmk+hBAbig(Co_N)9zKDp@;ahJw z>PXWt!1o^s|Jz^N@Gt%gcZARtpApLypNdc&CsT0tgL&9uZn^Ufw3cg@wMyJ9z#S!hMJl?i7Xx!u_n=Y~Z+qZBXQM z!vFK_k$3)X{uKYkfBQGM+C|3AV8)~j(|$s8LNfY69>RRaJ3AOJ~3K~%ERhC++nGwnY( z%u}&|B(z3QInSumQqU6WDN<^p1e-$$f@URTDTZM|qH?pJ_&5H{Px9~n<$s$HcjVjW zXr9UYJq(dJT=D9cU*YA~-{kY}e2e3?aQESX<8$Ti$t`!9nN+zsz%gbLiT&P+KWbJE z!Mx@PWdcIj@>Bo%PxI`UdGx_2eDdg;c{(zunLKB5fu(z-b5`c;YZglcM2i44<8-4( zs?rST6_=$(}>QQgi=R9u$YUYVkrobOU;ZM;rhwU^aq&-KRhDaB?G8N4h>8fd%pWa z-(z>Pq zTIB$jw-yZ9_t{#;R(qz(puG^^$(>DDY>ic_q066~UiJ~Db--F5Kjq`SECCCrfB$nE zxGP7^2~Y)zP5==Rro#alcL-+m+KyWLNjX*7)#cLGuH~T{4NK@S)HXv1#8~R1gn;B& zGd0}vS-f57es)F;y|gN>*V=94SQmg?EA-81&&itg?}PLI<))I8 z=avG8F5%G+)|=`UT6>RmhfIzDC+pSYrD{D_)=^z7R1^{`K`BNRGe+&G{6|a%?QC^B;ELxIv+P+F7nW!g(t4-#F*LES+mGOBf z9~IwQe&7m)v~1Cw%a$)OS;!d{)Uy+y+Fl=WY=>NQqUq8LM~?xt3sYLX@+^SqGP$x^(@v-7E_di zcJLP|;l)c{<@>P&KOn(R4$=w_r(a2FaP8=+uct4E$8-7^vdgm>u5Sg(pxC5RuaU~meZ@-BgCib5lxlut=K+|l)9ZH@$iUd33Jm+F&_X2col+wk_i?6tN z0xMKX#%u7&C8mEI6}^elD|lrl;~;RG3wHBLwatOgW=5 zFlV?7kt51els7`;xn?#R86JW{40AAb}08WFae{VWlhP87dUDrN+{ng z5+kA@#?vE1TOM%V(Q1`mym=+`os_b_MKHTlm z6u8)L*&VlRr-947Va$S%nd8J>GS6hs-LU7`c*7JY28jeBLl>j42oo;Xs$Ht*Ydu4( zGEB!0rRSNQ)24H4H$#0RfYleEjXg_irlSSrnsnc^^T<05m4=TmdMzR<+pHE)s5(Ng zwRPXMgM)}LA1Ah(T?I|bx!O=uRzeKuEcLJsL@RwU#aBpkCgnsNMw79E5Qjk8PbE*r z0c~D-?P{0Ux(UVz9RQsqRN&_Fu^s2w0563I+Hst^m}_ORA(VNUQXWLfVukHUifCS? zB&_Ia9ZDCm(Fr$;Z)<_<%(cZDu0%Uo;f)*e=@ou|v>SBKax2WNvkFo-!Y<_2MQ;^b zYh%6mxYpBT_2Ja8w7Q_(40LokDrd0KV7&>MZD$1RmtfFSxG@h6KXLs2lNRBI8Zx4sV>g0ZFA}x z?ib0e;9|tgt)7c17Q&MIsdNh{#d2N>!B&}UIqxMBsg|_8vJA%1SHM^yUcKzhqJ-#s zd#=2awR(%HuqQ6`+IY3wShc8=hqf1Qi_fv@MxmROwD$#w%P2K#DPB|rPSJ8)lAdhH z1&kaI%bF{+3(_{)f-$bTm&Nr?NL;L$!`(5of2iP9HqT{N6H05yS$DUC-0{?nBF2nP zw`e~%S+6n6+uLv(?)o#X6jb=P#Vs>fl8@ym3>b~cORZ}lG{vB!bIWnL@yho@rOw`7 z2gCkRUouZG@%fw(H%^}wS*%d!H?N~y8&=0r`J?xQKlu8@U zKqd>PKg`OsvGCS=8}qC`xP%X{;b*>f;3t0g^L+QCfk`&J{q|eD{Mtk2!!tho;CI;U ztXPgSPo)?H3Jz+`d3Nu$Rmv5KNI6#QrI)YZ_LeDTw#jnPV+@RAU>E}1-N0@;Sor%G z8HR{N;fr5-gSWo&le~R#pD+B6e}fMnJ!@x#yZ2ma2EAAm^C+eyyR@b(@-V8O*rP}) zj;%t|MCog6EO1=4R*<*om|S5Xu_7IAJgW59X^tiEw>i*+90@K|+HW4tn5*X1ZD^-g z(s*mb-^O>n|NaiiQbQMOn56H`Y{k1Isa02};k6Xu!i*&r=57WPgR_Gwh((IzWU~vg z(UA(tj_g*@M=8+=1^$!6PHPmF`uG=X(rwo+&CXNnTmj3+1OM5-`V0L0U;ImuX7o6d zZ|~6gNV>T;xE~Jac1sus(sW=xPE5CV6olu%q&>2dvLbMj?9E` zYeCw4|L=Z}Fa673=IZK_%`S3KC6%+(V<{4oOXNimvBF}(JjQh%p3~k5unWe zS2I%(wp#Mt5w3;}lB^I|R>R>~yrv3vW;%)ZzHs+?W_+pbt-61zfz2bNmW``sNG zwdjFDFig%VX=qr=%%&WjRz;D8OwxO+PXr2FYp%xwuNkEC>g7MXrr=ts;`FN_hc27w zzUV@x?0IzSUAoVwuOH;FTFX+M)Uj+a^=7M z*2}|f4|$6<0_GI(l^5DFOIxNMuF|#LH&FHJ(=}_SRV?QuB6dJuReV>2e9_(jhCq40 zt9P#Vv7X)~>%kZ6MYH}%?Z@SrQ~IY%;@2ftyD&fzRO&On*Bp*6xcWG{@25n~m6C4Z zT%kQPRS(UnIIJcd)ynSvY}a8G2DK9iyZ3fq2kQS zu#nWc5CvADU)6p{T}Y9yXAyX69}9s3+VK)~L5tyMghy9VQ-woK?<4^rlzX0io9*^ieqlZ0tn#kABk=>5@`89Iy3JwuH9xBZR z?1-jRf~aSUvD0DFYQ0L%b00MW@p%#e^h!V*kifGjM)rm?_g z08H!wwuC^!#x^dO?W&^6RmS!AnfYG6_il5}*?UJU1}j!X?0s)q;wWFG`5Xd=>nVORay4ReS%8%oV~}%!hwvY>=H=w%%e>;vhnjK**UxNoP;0OhFh_i6Js#5&k75 zk_1Lov>ctaSFiVB278b|A~Adxxu2B%iz;~6^xx4yFB=B+OqpJ1q{K1a{zD8Hn#JX^LXOi8aQox3(#(a`8QmSIfTWfIY7Euz4tAR%5Q~+3U^iVu6tq z*geE{Z*SXvH-puEwQUyw0OMKiI_$(o}=J0IOeozg#C0*m+9 z0O^lC#x8u=D@KzY;C)(!GtUidL{=J|Y#`~!kJUN2^Ps&_0a*(ebq)cB!x(_PV!I(5vO4@bMvUi=fK5B-krx%K1jIBnU z3Y={aYhhNpur(=B*NykX#a1uadZSh7VeL=q3a0J^sYbbK9k&)lY8eyl(;(VlzN-9K z*Qh)k#Liu=bEXPh7dBE9EI(;L3s>`b`W|#~oRemFYkff*!>nYq_V}%ta36(k>%2)~ z>zGJms#|B8vTnQbzHOhY2m1Bt7-s3ezO8FBxjH*4GIVoP$8k4S9&R&P8|U7ZFU%&@ zsEwkoOMlwH^`-K6el_sxzcF(C^vt28p-)}a=`@=`J_KdTVg_@`oLH8kTkmW*y9hDa z^`&Ib#QEMeIL{P$hY%>)7{K4CWc*LH(KPo*4n{})`?J4YLh}B@p51 z)sArtg;&_V;2@!#ODk1r+V2K__{V;PpZwWh;PU!?!e9wm$D4aIhCRK#oGt` z$_Zk7*~J_+$@#KY%IxK(QjD=$N2Hr^CGYmUX9%b?SlV2z^_<)9lnX4iu*qyjx@Qr% zQ_1_ZrPu1X79d2MZBOO+P|I^(;3*X2UQ4yPoR!oW>5BI!0U_Z$@X{7GkQFt7khM?S#Ixt3%{GKBKl5XCthyLBFh#5wmscmH0!9!5tv-yslj!bueGIEXcdnYIjOglx9w~ZH zo~!8V!8EL>K+=vWqMLS_jeZf)?e(y)$Ya`-gjl8t_(d>)`kpY+K*m*z02V z1lqu&JY=9+|8)wEBebtWVZiQvvblGF>X$_PeiJCQpYlH4Ux(AGV3B|>rg-@|;R4mb z^=j=N^{DXaWKajK`heXs_yk!aU5^t|Uy;TXXfUogQawlx=Z1qlUPzFdk}qCZB=kmX zZN1|YQrBkdL~xI7zjxG5p@KPh0ys)Y^7s zTq~tu{ShgOASRF=jgoKQ@fQmM)-+x=>*LJQ=Tf=pN~&}t)pZ|bZW4ON+h|O*1DJp5 zqKrL$x3Ak+x_v@`=Lh9a{ka|gX2{I5@N+*ikyY3a%4xDA&5rkQaRGW(@}(KjDyAgA zo!}~1dg)lE83wpmAY8!d-jZiUAk3yn-$uBK#poH}{vP%d%o%oj2s@Zg@Xk{!RyUpD z?jFc+8LjWLLLPxpc>0lYI(~!qpFH96-LLTWq`dv?HE&Nxh8glvH9H7# zw5*N*L{Y*Z#NjCx^-M@m_WP0jJ~9rW&}pa!lwyvGuYBcwe&%=o9zOc&PciHchy)}I zym|R8e(^W|A$g$`hocoE3zXu?W|T`5WU#-67g7sc_I%9Iuv_r5)SX*O*60C+x4LmY zTQonbQy&Wv%rq4NNui((iOq ziqj_vF_;ltqv)ZEHIofh$!^&m8elk{_zQph-{1#-=g&gU2DfE~^9h|N^mHWO-I0$c zSQgH&K4*CTlo0J)>*1ZJ9ACZVcz-6{PMoJ?nIJ=EKu`jy;A1M2u}bHP*PC8Qyd`sd z^_qBf;PUE<@tBEG8KTuxAlN&>S0tN(7DHXqvZub8ZEgexQ9p^!9OFe-a`p)(ljoc{ zoF=YP18!7aof`}cJHi>Ybl^Kh`U#Rk>813jq4NRhhoOnM5@-XlwCEgKB zML()?6->cC=gc7nZu7!kgad)sWG+kp3zi*pkEPa&SatWH%7}0;a6p*^Cbhy^Bd}Lx z4#KqvQ%amwxek#@Ewvq#O9{+5F$7_ca8_lMz>K1jxjIFrmznuR!@r&{- z)ZF!ojX-WM*M_SB{6JXh!-t(`b&sr74L4ftN+( zQPMA3c~4?62c?Mb9cpXaFM&3_s5(x{7(}08t(2r7wTxJ$WtC{tADW)}D3-0J;5I_zpk=u3B-sL|{posY_gB{*>ArWQ z**bI&ZPC3mYPU`|Wz|R%sxD)4 zpiRfTiK-qQvNh&H%}kfTawgwuj@?nn~0wf^Jp( znn+vMu!3oe3Lw(%)oWDd(1@%TTs=H&EeMn|?0v6~wv?J)u&to>_>#51Wj?`STGqTM zz_s!E)o?0P7b0)JZp@Fa>*k_6Uk-cW^-1|N|I?no z^$&zU|Hl`;^8K0P$&O0P3F6M49m?MEU4*LYq{w;3+?!Wb;NsHGU)-5p%)&3K;#4keTqc5wUBB64>pdp-^@pOoFsoB~%L+`!9U z;hksiv%L5U=d|OK&pzkV*KfGt%x;&t+y^ek$bL5vRoL$$ae#|4Fz!dj{Rbd{`{Ny& zC*po&zZ=-?2Zms0)>4j)dH>7r^Fu%OlYIQOpJd!WBZL9Ud9Zmt@u&aHf5&`IgcMqO zdncbf>g-k2YbAG~?6WTG?7X4yRO3rpJ@wVGU6qxJQUw<~gkjpYyB`r5dZgZ#ef(Mz!^p!Z_P8INH(>o3N|AwWp!VdPHmC zwylDuB|XAwf|l#K&8KbwDv|RQOUor;N+?QO>)Tj`{RkrDU{%9?jmmM`P!zEkP0lRQ z4E0>-B( z<<$U2Zr65I3|W=u;Q>;&4CaLxJ1x$)QOjTL9`wS5VF>_DF?)qLr#3HvZ*#= zG?G*1c*=x15J~I=mXggPSQI?~kw6Z@BEq6-#otuR@oVr-j6{TEu>Is<2K5_No{)JG zgxd^PWpO68{oz9l+-v5Az#FVO{FT}{(^lnOIEr#D!ab%;4p>^fs_j3Y5cb$<lQcF<|3Wqq5R5>eLjgf;2x0<;ZWl!cw3fvr(n+v5knY1dzCSX=F zXDty{3g2S}!&)~iyr>f7BcD!R>mzxaUN?P|_G)mU%w(0#*|LOIdsaQ#-VLc!Jr{j6XcKTU}eph^Tc>q*zfloj}u`hmCsM}!hRRXVo~&* zP3D*ZZTrxTKW9`kQYxPg!@!wy(|59+b*^b4=UleDl)yyr)W_JJT+xhok${prJ*3w? zZEf0K^#=9OrOIR##B+*ECHh zwRty3v+L9kjP#z2WZhWy%zujbF57&neNkkHY~GWu>GnlyW9zPjEpqk_tTg~OW9}~3 zOR}DfEq9l7QN?$+%|M!6Ql0SP8F-zYM6Z6!L%40auRD4-Q|XGtwU2f$SGBs2AkZ;^ z4iF{wv0fCvDQhV8QR&rE_11A6P?>lUkCa~k03ZNKL_t(`kJj3A@{sL%Y62_oK1eO@ zA)?PvRKoZw*zzn=(wL7qfSP30i1c4c2uPv1J*&C8fESopU_fCN&^oZ zy4~0uw%PZ8s*qIc`Ld?WX-$Llz30uKE)f)=Mv#4qtE~au8U&;+b&b$NRe<{xN@v>6 zL@FMy)?SLB>hMBseO0K+Y7-vXlla(Dr+U)wQmfJGs9wnsi>kOc7>c6Xm}N}mvS&-vQNU*_vS z@S_|vyncJ+=Kd`=r+e;~6T_4kc9~sJF2}%=XYaGWy5{ZuJ%8`-|9!462F7S9^5YP> zyxQ^Jd++hV$KS`t-~S^#dFRW-;fhd>nUN471g85N{8Rs{&70XUO}cv<1KpzM_D@Udc@{yUDQCp4U#JH9 z_IS9}*|<*Y?r^Arw&14o=vahYiBAlr&U_OK=%u--DEs1O|IP z2G~XAfB1L*b-wcBKZ)kqsw*xFd6{9Et$O-Alg}sSS8vdq$gkgW|J_&Ym&E?d?-OEV z2>}A=l90p5#ns57%I)b)P~ohZxdaKasAcArs%%>~U9z28z+~@dgmy~(PVEaTH`A~mQbtohf-XUlKD2jKJy+R64(O=g;_I0 zh{lsd7>WUN)XZ6xNtIJ6sP$nOcp_#vp9@x9mHp_lu| zJIaf=Is?4ggp^Lt+ z)SEs?vW`=CqO&yR#&aWB|F#xg+LVE+05zGtYF%5q+YUD>K!ZTfANt8YsxSH@1vU@# ziZ~P-*a)aQo^MB(|zwVDEyvpZE~6NAdDjEHlJK|k2qWZXW$nEVl zMAr^FO{r;~nxelnW2Iw?BJH;iGk5c%C=?pn^X^6Iy%V*?(0;8z;J?%=t!f8XEvwX+ zMwfO%Hx{8qLslZ9TU1|cX)o1cLdCEQ#iLv+kxQ{ZweLtPf%;j9 zZpXo15IRHB9yWb?>U!aNU82EO=&M=xpZ5Mb6>a71Upd4!+?0;-s%P)Ej<)35=GQe) znNa62lJ41+vqM(zJeWt{iLAxJ+9A=%&Xu5^+E$8+<3MFUx*D&b5QJx!%3u1ck>CFH z9slI-oY(<(N5j+I0P_hLATFTMBG{sq@;<8-cgx9)mWu&&fwwpIeNng=geRl&-J?Cb zBp6O`t8lom2)rM^ma%pZ5j0rgrTqx;i5<6=#Uk><0;NuU!N(=`iC(iSkc{!4n8HszIJb%t^A6fLCAODdr^YrO8&)gjuoyC;NUPl!Vi7yst}&42h`{#X3wx4*?j*psJR&pxM~-aw;! zH)@>+6y8v)zj_2c2!6a_hY*EV^+J~igzM37J6_Bp=V?l2py%?IK*#|N2}vH!@8q3Q z_iNYG&Q8cyIb9iDq4BciM!F(kR!EK&@9(KYJGro?ttRwcQPBdgD?y*j>0)j z{1<=pU*Ic0@zbcz~PhTNHSibibb`hNtx8HsVD#XK%xZ4q* zUZUTA!|m&Pmg5X}GeZt!)V6MvtO!IbZMw`TVKTva;+r=&9PiG6FzyG27+53_;y{v& zq_z(gft&*&DA_hPteCrCunp0iK0ztl7L2zbO4dx!a<2K3d2xGUE*T0c!c|uG<&5$e zLa8-pP6rB}o=dkMg~&TGa*uK&Fcw-lip}jpg|jUgZ_kN47QPfCQ;6Kp6Cp;9nz;^< z`<&P#Tw)d4uSGZkH^6n-_a0S=BB)qO`elgR<%MSw*@wujCJqEN1Voi#h|JmExlm;Y zfmz@xM3l^J$UGN2KJB(-s9k7AL^(x3lVuN7{BI7}tz2wTFkS1M-81b+f9J91wb|C) zD{Fby1Z|^N*|hzjvvp%m?@l|No?E}yd97QkfiGE)tM>`uV{Ss5RTQ_IZ&8vS^tW!f zo4YfMIwh&;&hz=qcsQ`%jhv^MSd7^aq-+^ccDsSov|w=$<1az1UPMZn5CcOjI*eGF z`!c5ukE5MH)NWm&lSu*h@}_fG(*7>bp!> zZ8)^S#HKs-L!f)E3ZTcgA~b!vU|ri+RB>YcUd7J3Z0>Zf4(V>TEB&neuV$Unbgc)| z^1qJ_y6-{P7wYrwIQXLXxAdqD29*Mf0pOsO?$xGS@_?qQ)m7VER>!Qh3Aqx~J?#HFBy(k?g{ghArAn$kWC$zBI6$SpNl~i%8MXGSLa{;| z2f#mK<>`5bbWJ{D=(b_laEqd)xu0{@T{a(AWY&mX4VVswRoAQ?Ir6MMxww*84UsN7 zLx+UQ+WXU`G&+)H+CU^zm?G``$pR?HJI4vn;PHYsPR%RNQDIU_Ly6|cXme`m?CZUa zZk*Lj1-FDNaa2~=t`%XG$(1Qp$44DgA-G;BNh4OEr5hECTb;SO7dS{cf{zu0Y>S5cQan2vID~u5mA%b>=_*BmXSF@F zcg?GE8xY zYfQb|?Pbg=MU^Jg8})67NE_bvbm)1Ht4Q-BiuPJ`>E^C~*VeU*)BdTB2d)0vWSN#u{+AjfX+}%b9GZcys${jT6u!3YSmma#RePiA zWRJ{Il~7KE$O^*{2(ca0rdAq$rQ>{O-4;rQ6LjUr#GZhW+7;YWzoc|^!k{^NzW{_CY*xOqj<|ZeY73t#@BBwYgNCEs zJN*!BWaa-`2b4drZ49k{sj$`BlCNps$rr({8}dfqa65768<29RE`5Dj+oV_RcM)^{ zIGtC8xnlxdk(<@z*CS{Z5$O=f0oi-yr0aUcfgUouUP1KnY_=8GhgZ=IZ_g3OD;!p3 zN!DZVfz?h}IrV%Ym5sqrI_a51&mRr`(!ohf^U~m039hAMxwU76qweehFMLq|oIB$2 zz2xoLHa_XkxO<#dZ63wKugot?Ie)!lOxu-GT?-_?T@ZKcS$hEU${-q{Ht;pb)^oR| zbA;4cx$~yWbg1x^npWR3Ox8xBrTK)4at}Uh9@30)-T2*{n{xtUrnw3{S4$&;HMQF0=9nelD@xE5p7NOFDz@EOqmEwqwv{DMzF2%dAaJqx%3Gxx9 zy*>Zl6SzG?UW6$q&0ou9K;lR#V)Y?Hd#}+?+Y;sL=>_vbca8d-5~39>9CreP@<)E; zYy83=_`~RYgb>l?Y{#n23z}w_&lW{LpGj|T$hRkUmwQqP&)XeqLBKM*ehX-UjyTaOR|W3ZD>&KXiLqgvGViboOlIq>$|Gt+Tm7$Ty9aU2MP z5|@(TNwm7rF|eLE~QSxR#xE36#N9c`CKAINW~!^euB^%SvsqY)(asH*N-(|tB?3soNw zPrWhE+RDMOs^R4Nptg7Ia-rLm>~7MqS6^1|ly9zzP(dcGt*Wne{mGh36{V}p&pMm# zjtu(?cDsS|`Ai6wF)~E4z312|A!h|ob9bZDmd>>``x}P{F`zjkaX@pbhLZDRZ=+`Y zWvy!&MDTOcJu2T)vW+_n&X=YBHN!qso|{z|hJeyi+(nlHvohkUcj_8mE@QO2uIm+R z5}-nJwNfIi_K){GTPlwCQmnvKn-kpkXZCGvsiCP|4og=5>d~a0(WT&~Lq(Tf4`}Kh z1z9_Kl=}S2z?TLi3oy3zuC(Vwuz{5s@wq*m05 z|ExYvOkMP;_91Pp`ku5Qyk@T;Ykk(^Av;1e7_`(=(<;}(qyoAQprvl)LNu9@x)hY) zRUUoJLfL=N;N;%v>XCEwQ~#&6f&Tsj0xZuKa;h+>`_^3`Nn2X~yB3lZoH7SP9gH_w zjQurYH&`{4Q{2JQ=HkQJ8oeD6`uH|k0_XNw@7!JAT31a5lV=ZtnJ_>0O;e>=7rz<4V&*VYh{kSF8Rre{m1{6Tknb z64Oy@rRRNy93ULwJXqBFT(-%^2zfC(;;3+SY2U97wnts8EHsxpxSe2sVciY`oF}-S z&49lyeH|_>LVtI*%I}v)&;Z|^;N64ux7(YMK8bKXDNiF@>|hwJYV*i=z&6A>CgC-A2YOypqF{MW4S39g;teX6<&*DM6;r?d%luQT|FvY`daz8 zDtJA!${fSPAPl4MwI?J0_P_eCfoAmn9!&|IPN*jG=Wh^I(mbQ*GfXpbULYmTpS&i0 zazhS690J2QaQ@y6JV1G}<}X z--ut~Yy3lAq+cd-pyV`EI#4>9;e`sD$gG;{N-eC&{Q%iJBG!uA>bk*JwsbVyU3mEE zW#%J;sL{Et!>(zr^>@E5wdXHAG1k%ahF`Bb2wSnrgBkRErh7)W&utqiumVWS%zQet zyF65#D~OPCt}7y94zYQj>mcl`@)AW)mT_XFCk(@ARpL9HSVABtJHbihZ zQ{1f??X0KXFz=Oz6~I@D#unUL#B&7cIbXL*z9@e&iP*6Z6-#R})@ZCU^R|MBTL5g2Vu3jgV!zu=%Je)gvmI@^Bo zbSmwKh64K?yuF7f7na_R!uf<^dJu-$B5L;&ycKx2 zREEDAVLn3K+wUi05xO^TVSj0c{%%pyL{a|Xkm2(?c;^~!?=3Unc(m=juigqblQ|Zn z42;qGe|0+X`FBsq_dYWg5Fo@r7{nByLD+}E)+b97#!DtGYDT&n!bLbcJL$Dw7p#4T z3t5|nQ{E8@%aVqpjmH+ca5~Q|Yo?y_or+=ZmD`iH)b_@X8(ntQ-dA?&kAJbkgsZx{N`#1wqL zQh{z;j}kezO7v!=+DRErruYO6Oj5_A{g)M;HmdaMz(X|S{LIWB`$K<-@$xA;onf~l z-`iMZ^TUmqDXbN|hk#LFFH9GFjMatK_0 z_!PZcn9df()I!4%Atzy0TR>HFN|-;|#`#emdGqZXuAUtVFATQ#I|O17Qj&_Zg73Kx z1t*kDXvN}+jC3KH``R|yJU}H=wL{%=Oq^e^wf6~kcA@WHv;!g;dB0L{1_-vl}QV85* z#qXZSf!myUA%V}y#2_5Rw&ct?^DG1=fuktzjge2&!iU4iOHI5pVEfUNa(_B;o-;R3 z-sQVr{UKgme!zFmw#7%VjD{!+;(H21EQFQOamPQ9e~dT0%l9AuI-^b~yR~jwX-_IP zRwKO=04*vl8%J5=75&9N%JxN$U+c?dXp?^rjo>CDI}$5**7CDGMT1U~n$=Z)L zF{ynM$f}>_aM+Ho`}Dd&r=|A8u6$*4eKmy;m`_J`7kkDbay-pNt&+ks3-b(-7$b`= zW%^} z+qN5*>yUlmtlD6OhpLwCWTwZMPGB%FeZApcop+tU2FIL#7X4slh&xV+!_Z##AX}4R zG~m=)U8V1io_j;O>B7bjG<$xq(NkFIzP&Eewi2wdUf1n+Z}uXw>MvDnd)6J@1NL{Q zXizWqLbi0EYeZ=4*hbTPS!DgC+-uR06>xe5yW&F=dY%^4FIq<<^}8E34^u)m1KjlI zhKAC-8nxh#I*G^%*}NLBgc9+i87;Nn&Du61?3vg@##x{5zO0yKCA+ zUcc6X76~POdaJksCUxMRa--5BUJBJvZw6EcVehLqq-pzrB{xTc1nhSdGyF^8pjsrV zj8oOUa!u*`js zwmOH+e4If)Kn(4=>DHlVHVV>fA5^eZMxbSs{tmLH9m=%^ur~fa%H9O8uo5&Y#$3L) zX|3i}SzNhaN<1_=>M3TmjvL-Vd+Y(T>qgQy;rQ*p1h^RCPyUY={0TYn6F;6fo-L{^ zi-CHYY@hd-EHZU>wmsqk>_%akm8lr#hr!-Es-10~gW02!ZH^Zx!@fo0-<_>Odp-lB z6{kB%G4jRQxGL%PZvaVfne3Rh`^m<4IM`TX8Q+-Tq;NAyD{xr;y1=bOQb3W5AXML@ z9UAY_Y=YRL=VqkmMXTrB&OwzXb?Z@TE`|h%%I>nrBI->Ema;8Si zEoz?8#fYsHd-HV`{4DeSs5N8JQc@+w3f--oa5kJ=D%Ltpg(x*u9ehk1-mSVKsj9E^ zf+{y6@s#iwL#=%7FPJe|YJzP0c@*{6e5%szMv)2P72V64q!z(fPqp<-AR0`)*qIx; zodC6Ymp(!%5ElY`^gm&72B+-wVQ1 zgn-oSwI#s21in|!VZK1P(ZpWt7^@3}S(Kp44)|O%FG6Hi1S zpCG^ZnE&UKx9I&HL2}JV5Y3hrlxCyZ<%I4oj5m~O@SpRP`@`?!YxCdbdif6M#Y#3f zJ-VFh$-;RdH2KqE?}|hoQ@-nUT~SySgWk72=Q3R$8pBRIw#X}S_do{v^`883rp(DnSU@ zaIz#s6yrOT%$z_R0?WKG>?68h$|egbC1eN)QtLVR+PKl>az|)Ilr?KRQbR-}2-kJ%pB4pop*GF--nHlQNk%E*8O_}y$PaZ)}wpc6WS4(`e^zFGzRpdqD;RiPAOE;Ydfw4b|+sY_6GS zWg|OUI&7r_DqC64uHtpO_h9d9|B;sF>%SlWtgmaq{XQY@@lU(IVxY)sT>W_bl@+h? z*R=oLr}XyRqCMDr8p!pvY2Cvmo}BBJeEe@BIgu3b3PTK{ge7H)W5X= zO&O3okG44=>lAP5V0@o@&I*1c+Oi&>7mBmJ=N+;HkSK&9)W7{vYnAWm8Xha&+{4M0 z76m&xv*Tmht8pEE7NBd1IFPla&x^F9$f8(LwP?qYg&?&ETT92J%3sqX3a_bq&pZC* zJVLv%h_9UwQ0l#!W>2Z8T#7bT8C(+}Y~E>sv?$A>EDJT75(30Q7{>sE@<)H}ckw%Z z`%jTiNA&hBx-97ZJ)9?&m!G5aLb^K=cO!a#CJY11n;Y^u!D&X$6M0H7C-j`)oS07w zIzW)X^!7v)VR>^xh5#v}#i1mnsF{(w6)IaqEUI1>+D}#(1fKrHhd@9xmWG>3Bnd%F z#N6e$5XyPWt|3URny1~hmDZv{79nMr&NIXR`-&-Nau99=4k0j;xgJI`@D}(qXYPyC zJOtt05YP}g3nU4g1)gNM$M)0TU`KGZ?NcR{B}O~;qAF@in2(4IEC`kSs~iW*zRZAGquhA zy3qag_SWj1{;No9x6|j^VAvQ9x&~Ii0RC*xFtmTQ8(TH>N}JoXsov{RkLmK6Xdxb? zX?7b8ZzDh2vpzL9Y;wc)S^%OL<{o)|uxWlnAilRkE5g~Ds7N#N%U^9^ZC^^dkj>jI z$<|MAu+kx4-Cyd}Y=e)Xq)$e%s<*KK-z(1-#I@9!T+hC&T4cYT{X%y0)^3dISG0@% zs?>GsnNw@Gtw*;4wAQGFKib+lqiZiUeA8;2*Ez0Nt@m|2{OI~QZ?hm?(ZlU`9c8CI ztothKU%kNPw+n9Fn$xYf&2T!}TMvBoY`;j4X$(FlZ>A#|kDNLm=o+m}s=xVo(_b+1 z?cB`hTc*T$u{ZvsKR@y(b~8Wr1DQNP7GQv3QZz!EO_)rHT9YD-u$--U+!9QPg+Yir zJ10E~Tt}=zJ7(nHpGyS3@Hxci-yNgL0}>9m2>*p{qQ$7>Y5 z(Lvod<*QflqHfW7L?%?HT(-gIR3ee?%u5zIuLgUU(Nht$3ffK@dlpXtX4{8NCGYxI zF|SJYZ9{hak5|lH6Kd(XBc>YPDtt0$=a*WyBl(k3o)1M?sD+X;rC?ZzdUt#-XUb7u z7{AN8TbDMEm2pw{Np=NI4H;aKm%_hO0ap&7_;Rb{tWFG|yJGT?A6*{ON0k7)hry>3%}?0~{wph|EVzRXyL&qzrOS zy#3A%_pgt<_x1M?5q4L5rZ-14`o8mkq6|gZN$ka>!b1XAJiI}vU&d?uMM$DO61 zhrq11&poQ`TYnk`?!@X`JOkdQ%-4pImucqR!6NT>DEk~Z1mSyW;vmB7^O^H9^XZGP z@%jgTlF!Ej)1u^QAD=vna8<^K83PF=lN0}u2vQtQ2{^FHjZ7tqxJdsG*YhnIZ3^J!wdmzP7ox1B&h#q*k==;n?Geug$jIUiSn{hD(#} zeb$HzeJwayx38+Ao{`(7wW@m5yzu!&O9tKAICuHhrrGeMkI+l-c@!B_oD=<)u9ckf zY4+pq$$K+DH-@XSUAFj2*I1?Z(r_r-#QGHXRmO?3T0`;s{$vrf9)xpUtLh@!uC4WD z?f8fZYCrk!QMzC8i;eN(&(wTmZRGQJADuH9;MdwaCSUD1nl9l1i% zFN%S&v6I!<)N!ZV!>tbi;xOsLnh$t!5yDPYTd&I(8xzv1WLCV9HdIf`>=v)*A$`iK zqAPfa8{&bQ*6I7dZA=Z`TIN{CT990z9-;KGFjx<#mRc%EtDc^7^Oh-Ae61Mk{hseW zf?mB%K9$hMSh`vf@mAJVgHxqADJ%fC6N}T6~_n(aX!tec2o`3jd zhVg>0eB~W}+sDsA7k=a0uld!#{>xl`^}w^&{|C=5CUO9IK!(47b1JFo(u_^r2}`yD zZ82C8xqY7ptj$`}BwJHV=Ji%2yH8_gjQ|FJ)<2qj9`@yphO{$SDIG@m_c&U{63W$>3 zs4kfTmVLCQME|}PYmKSqyuRbNrTcDw1dYakv&v>aV(XwUc=v9G!M|g2f zEbAV2ks(Ah*eT_j&8YURTtk2*WspdaQoue6GAsGh8^XsgY_?j`X#+$%%pBFC{0FUD zN2pui%37mn2sK&h&=~h<;0hUfp2T($q={*na}2#7phG2LSo{Z*YES$SAK%<;>*0ay5hqG zug){mF>|j^c>nr>oRpVuZb;*fFiprZ8GeRf$79S3jM12z1Xw218-@|hGcq2C%bAzM zd)#0BJiqPuSJ~$yu&=VlYtgJeE;F1rRQa;5(;!>gUWv3`@N^29xccwB!{zsS5RXt= z?V}p$eQl=J@rs69*ZM$!clO2u+LzTstqqsuy@6j1iTc;a*gdy_&uScWYK#c;@yK{R zG7N#!A_SC_l~}TL@{-UYmPj>d3`jBqD62)??YK6|acn#CVo`L@-VrH$BG{ORabTIs zdFbTFC5?tCiP^?5(flsbLss%?iC&F++O1aRzwwJrV9%#_+e(wy~GFaEayPbpO z?gqe0(`~h7rB>NpQTD%TTZ=AahxEWNhfBCp8%*$^d zpW7!w&CGK7_h_w$4yJCZXmM_MO{;pM41(9g^7o8fJAJnX61R!~RVk^o{J_EH{cH{K zwkN*^8oMn3K9D{e#-TQBJ%j#3DxhT9c#}psErzs+w;y8S{hShp#DcUz;Q(uDW0Sr3(89mo^51D1-ViSyA>;=n_n9< zO5InqLd6PCt+4d>A4})1$hfMGPF2<WJ>e0*qowTO8fn6 ze>Fo$g>0Xu9iJOn(I!${Frq2by3x!S8xM^UI$&457K1m9-MsFAzm5 z(RTWRc7j^XiBg?1!QdV|C2R=D05XWBv=@HkMY<|l6q@a~7KP9moNnB7Deam}$gK_B zW~{HgTnLCc1+}1RaO8G5*2%LfyjN^MD{P$4I47!eC`NJZlJ>l_m0CcrjITN&eO?9L zWYD5uFw zT#B#&Uk)R0Qs#Lan1Sap@G@my#DUjKVjqO(QFy&1p2ff=@G>vFcQG(cnPW^u&3tn? z@y)}#ym|3s{MP=Or~7N(m&hr@cV6Feb93aQ55L6IF>-%@V!Ryq_=D$s_w!q(@e+xn zIdzuFXmLLv=gIIe3c9;M(rnSnIKnca<48;kr{Tb_9DWZ!c={V$rPqYsNbltPitqKG zPF^|gH=|X0)>&?MRH5#)ZF0RSf{l)FeY9O&(;;0SC91a?>thB>9ZsjObZouqxm}-H z+LDd@ujA^}!#akl?QG!bWBvl!EI#borZAr;#>0ZucM#HIr>f*^tq((FI!)|$JKNt~ zPDbcYsR)DMb$5o{z-c;Fx^tVvbT+BiB!qIJQAT1wvFLm%bnJGGXCV1Dg2MId%(Fu9 zbJ;`e48jhU-F(en>6|VFa-rg956iyBK&P9@8jk*al{sBOl#0roU|2=aS2}5txxP*% zlMF3>@m!4Z8bPPZub$V{wjT|o)LB$p&YT8Cciq~PJvP?n*LtWw>$g`nOu6Ck%W6eH zA8e^^!)o7^Rrmcv6-QRQ!h;W%fl#;K7TCE?qFqxDny0Pf&|hdTJ)C@fsl3_e{^Jw$ zYqjGwC&(wvR*Y zOP}tuMv-ptl~UHkTGK>)y4fBP)#BM|_uO`@(Q0>E6av4?rJ=2zb(PiOLcF8DL?55DzX{^XziOZ>IJ{S8j{ z3->oit}jPEyZ(9p@-O`wUw`%)Kk$Cy<-Ju*AI0|G&Xesm4?8oIpIlp|_v>qr8IJcB zsk>84g%5k%S~&xn?07bbaG&jdnk~gWO;}1j%B%+7%K)DSOJ%k zKunpOvzIPxt6yk!>zJfKQr#miR3};GGmh6H)#wgVwyg~9Zy2QV9XG-q$EDjwvc@0d zC8AolwwN-Oo5QZ#!{uXYhI->qQgE!!tCJ7CwDFsodsQfJ;NrZX@!pEQ^?-A~-FV;X zPvLp0w&v=Ftk>))jcQWMjtD{u+IV@50JIc%@M!*KYDGNNtqWUnU9+}Ey7(AvU#D{n zhx0HL1!S=M!K>Mq6!&E=ikv%4MCi}R9W{{8caSo&+mT-1*fxsO%#t$m%ePjD?KE-t z@(XUi^9t@VA;J9So^aS%7R5QSygd^MoNtfBoe8$x*-ja_IovTkdqR-FQe=>ZfRM-t z5&|(n3IR=tSrbc$s4iUSh>CC-0yp1!$$RPR5C@aRA(VZR77ceNiDf|*W2zK?t9Abz zG0KspS{%~moX!N4{mq&CMTmnktF?O&b0FX4#GNK40teu|I6@5EYi1V*7L>~@+>*Hh zW`R?-itKN4W(a|k!Ue_?GM3tICkX^1!WaTKWG;pRN6oxl7TzBQzLRD?8b)5_%!Mc~ z_L0|1;z@)kfxDC#-`%qd@au1m{Nm-0GClt)TwHUw*b#@w=ck3IA@lV5K$gHaK6%Oa zf9W|_Pp|pT%g@>We|)`ZtYz7CnD?!{&$-ikuj*A*S5?nVHoKb?2Z<6XOCmMLk|#y6 z97moc#E7KCPyi>kf!Iy}gGLhAFkrxc;s60`z(9fkjuRkGoCJZ}Ah^hbeDr|XZo|;bd42Xhyxe_->+U{~ijisAlQ3IUVYd21P&`8kEv>?- zd#`*IwMg@VFzQQdNR_H7dS6j3@>q5Ag>P5hELnImQca7YH{nW^KL&ME;ho7Koxp6Y zT}-ZCrLyEcHNVV&3<2No@ODnqSe$c&;BhWs8-umNt_$T@#2iVF%kE=_MexNEu(m;r z#58wnBMm}yzGLy;VQm9q2}FI)0?vnGjO+R2LeOQsmHf--6cUdVBXJL!2zXayO|>(U z^KPujm!ielfY0mX>k2@b;Rmd(R6Nx<1LNf@YMAI>CzDprnmAu6PFFurKaXUvZYNaP zGt%R9ABq2^{;F}V?aQ3iIia_SOjY7LcAYZX;`ThK$04VNq&}lR*LBj6uJY7j96cpPqyW)-{CYJ8h zwGF*kp|+5l2~}~>2JBQxAsa=L!DXH#dXZ3()Essw&WEb?iI~eC7(I{LT4m576q3t~ zQLno8sFroq2DI)+BSBV#$~+Y;^jejQ#niOZiEOG&D5UUUR){Izi`I@p<1$_@r;#Nk zqX;)p$Q`|(nm6XTjnG9Y_rRzI?fEov-=cyE|UE?b%C5qV*dX1hzh&WiA0WJ`GKxzUc?J*ucuhba>wY4j!%H+AJ#A z0&F_rViV{L9N4(;d~oq^6=G#~vyAdKJZ~ zmibY}14B#p!n5mqLWx39jD6<j-YZ?|S_C1}g?>EZ%ueU%!WG8vOY-rltpQ=R3l7z*>W8 z46gH-MT2*N*|No*2WE}s{Cwa%T(G}d(gu$+U|S0=;D;3LAR5(;#Dal7#ABK+7&r(s zjD-j9Z+Z53L;KXVh~qAmE--PQc*c<&k&YYU6J6wE?R=Ky0STc51iVnTk_`7w2o~1X zFgVAdv3LTlF+3U^Gl-Sku^hH7_x!+J({dIZD+GtIA}|2!v=99(z|u4XV)c3nfjwgp zYdH0u1%?A-=t5x62pz(;#4hDZnjf{lBPW$5Ga7Kx;-fNK`K(R>dt@4m)i_Xb8*m*~70 zxmv9WRi`VF_u^$Q>|mt#Oa9dBFklB z?uAg3-^$QpcA{uFT`?>@j_Gza80QlP)qZtCrfbH~ZFV&KGrACIYUo{rxepj)FcJ&H z85?!(rfKN=E@c^ce27Kc1|Ltd3f40BkHI>4Sri>UNM)wMV+1;9ZQc zYvWDjaTeZ~^%U>prtch_3t9h8hGABrG=2eTpJLs58GNb)uT-6oWlA01XbnavHTs-0 z45~d8 zFI=|HCS`fsh`aO-yyTmIzCCO001BWNklPs)2s7^CXr4nl))XW6(;OctT zJ{j%vDPt>erhfhae9(Z@d!{m3*9?zJAY&I##F3D-;4mI%hJ_H1)v*$bI#?6;`{qjS zB~?ldY0|`)$fk9r;+*&fZb>gKO^M$M1e{#0vZ~m2^=NJCNpQk^1O!wqI-SH8-qZ76#=kymHL+|Nb z;C#L2KmQ;98Q=WY5zYnHn;pORhhAmZIsW;te1o^|owB>wa^qmhiy!_l|L|8g{Oa!r z|Ld;@zVfEyIX`2wYjEE1U>mR-I9kUP_RS71dN{j)`zO&5HvxPP=g0B)-ovngVGXAa z_EVa=TLZ^%(nAx7#kqX410<%yOCQv9h%;1Stwf zvm>t{QeN$mz)BD2G%;fmZ;8fI{FCx7jY*YRrDVO7)HYQOyfDLz_km)7hg2-GoD88H zvn4W{3)ZFd_YscfK60%I$#P8nB|0jCSP@KS0U09kExzaMqJra7;Te>&t9e|RUl#gQ z9+Qz<^&PuZDRZg4vcFC7Cts8W8LeC0o+bq@b>Z{JiIcc=pO{VR6R2n2mcoJ2QRMo1 z^d4#qdEHMp8~)Vye;0H;bOZhU$M{`O*!G0M5kwf)TioF2&bG{q!EFbcSrhk(ZXNbu zjvqW>Hx&NHF7CfKLD+T<2<#*leY1_F-}Km7!w{l_%OvgBU{b1hpbH+0t%gewObA@8 z;Nbb&`DAkflD2p#nn3EmBv|ul8x<|4yg8=T4&6ZW)i?1*IQIc#4K0R^5gxc9(rti7 z3@@~E&VuJT+weH}80Gg4FG3KZC$OKq&MPrI4xW7@1hFiPAsE9d;rnf4*$7;3TDBqZ zw6W}Z$DS3=e4s_ZTAX;6jpZ26sx>?bj&1Ptcv@?sv19uQ*t?Jau@D zTdO&m@ z!KBpj1Vdwx5cdZKV`2ooZ41#DYYE=*XnvQau`IfKh%}>hx+I2_3ZvIaAu*$$GG$EU z*r=|DOz+D&hO7E9pILRT`fcrZ`YLSf zQm3I_EP9Wpoi#XwfG`X`>QzZT1c=*LQVNzBgBu0{lq1$eV*0x=F;%qhb{N|z*^;_8 zCK-k%I&`xxh}3Nf3CF~kxMign=mwD#oljf30&(9wzQfFB^u0sk1|9@Iu28N&ixm5z z*i)tsw(w!8;EM8A%6nd)T#>%mC`NzIr+%pIXJ#xHjk!FQyoOP(L`T22H*yqiL^m2Q zDoa%Adi<@7$7VZ3!$?Nk-E?qZgl!YF8>3j(55fo!^KqpaCb@$DB|SDw`cZ~+m9r^J zR?G1c98lYCNe(ZmbBDCG7b)Af6ld!gEDGZd|^f^HN| zR<|g-sUSzLqIU%ks!C42rrR-LWRS<36Efy*i;1hgz}v zD&w5IU?a{I3X$Z!!Bd3SAM^8d-Kcvt^ZFGhg?cs(~c*#RDo@ZIn>_xw#B3RwumG^T_#;Ay->M)a(Bw(}c`Q@McVgB$R z`F(7@^#vND*3yA@0;>S(oc zTPc27qVkHZlt6Kca^xa!Uh$u+_)@OmUcLJ&y*-UFlBvp?nK0SPJ8DE^a@eW#_{Gf zXT=+{(U3W$(ce`=t54nzm-dvq7!}`Z8+P7t zu^u>eYp%;4i)(ZGtxE;kqLOVxjAA<~k4^%-&(s0IArhSc+0cr0B|>zbJE1+@aqNW?ampD|4wGgX2!qaO4K|B%T~`Qv%0cSS}5FA#mJBqqrLe-j<%Pm=y;f|9+Os8<^mDb~WS1 z{*r^`oJC`}f8KHb!4Yq6JU4F~^4!fE937o-@BL%!qG7d~BfZBOI6B{Obb1ahaObIO zJaf3m{RfXyQN|{wZulKVDjAzdH%3!s=n2gn@eV?az~Ve_E-8zQx%g}E-?0m@(z%&*=cn0TaiNTLP zgjC@jjI}gP!;p-0?>)9Lkrn~d7_70l!O_l|G=i8q4+if&jcw9CbYIpgM>b1@Go%a~ z=VQi(TGRZ}TTs2rnFF43(ozr*b>9*S}6ATc1-9namsm76tlNo38nkZFS zP&gYjsuhiIr1vVk%d$9=LBTqyedi`GseYmcsxC#RK>9p&3`oW~3N95yn!JzfNli-B zAgvLqZlC6s`!2d<5ng55YOSRj=026+3QA9a3ZpcTn%i3Efd;PvNFH;P`IL1efL*QZ z7eZO8+;|0k_UhzGDesfDD?^nlno-|tWS0V%sJKa0pz^(W+(~ID z`x+FpD(xx+{@~GTWUtYd5=qYysLaR|ObW{qrQ?Oftuyn>W6Zth@o1P#!JFU^qbEXE zbRnqmrDsdAyx5N^RT3+6sIUzs}@7!be|#kv4)@HpV_ z|FeJ1z4wpV_MXkQm` zbI7&{{Qcj04{L#i#kKHwXXws5PPd+wSax>B>BXFX{Z`AbzSFSwhMRkiqYI#mTP`~X z-8L0`+rk!N4KV=*INL=#c}3g@FHMZX8xOvV@B7a!_}CAAC$ssS{cy}b`^_zlRlHlB z;81x_rBNX-tIiBcbe*E-<;?RGL6;moPZ>M4a;hi<4&_q$N%8l(eJ4lJQGzV~R*Z6| z_*TYWQ+acaHpCwiug7#cB_39d)FuS!ei=_59saWPrKh5viX+_XYa;5?y~ z!Uqb!&wRQTmdjC%QMFj=XKGi)F;hrq#8by~G%{Qixdl|@-{-NAs<$(Vwx|}8gQgL~ z=Hh}M`tb9(iw*tx4$~O?rbn!yKe~X8!vu*{kp=9m#jm^Q$ms^ydD`Wi{%jM^5a0Qj z5}QRuEYS{wXXA#5qX>TEu>r(I^)9EHZxXDB{7#Il2QhdN&d)kl&tGGCfe< zV(0^GgJ;>aoCnVwp(Ah$IQ0X2#^S_qO)R?rFDw^)YuNFWZCDH3X&X+w=RhKF*|QC2 zAEXC8#XDk|vSg_Xe z-jj1)efAJ*8s2|$#?}oiS9@_?MF_@nZ8Zl2!{C|E=B)Pid3@2aTFlsA%-D*lPD22j zitQSS>FtS-1%p@{_Ym1Ou0%Sn?eXjm`+mdRJt0QYsk)_l%Q~C7JR$(8OcAE{$>>gX zeWK5xWLmnbDE8TjGiq-9{8%qV>AgyJXIfA1rEu&>2Mg$q#wvo6trO-n(wmho5vJ{- z+2>Wz%r^y0+oWQbLO=MJftcF&NnS@C*I=w==v}cRVzJz;vYNp z0=TN5GLE8~M4v_kYja(8NlaDBsWL{u&>^vsQY94S~trws$_U2gIGuWFWEvRvahZee36ksZVZS; zMbc9n3RT85kX}Vc9aD#>&*gfCt%8GwxkA5!CqeP?YMiU|b*-nk%W}KSU(YGQBB$~f z+MKMnZp$uL_6xu`<&kPk6(vRiS2Ki5lAEk-TqeX!B3j^&jO8g%sT0U|l*oWq)`EVn zPP`ybYP>MD+_o9_H5zMfH@B@3S|MWb`k3*UOT;w8HB=)kD`~aeaEXy-cuaK4GPZzH z#_}-J17icde$VST?L!-!W#74{80po*i%-N6E8&#aP-HaD+<}NFC&7rTPbOHwliy2w z1^1FSQ6`380z_RdWlR(V(D;;aHLpV<#!!J7>U^`zDH#wN=hAQSR4Jh`W|ZcxaZVxr zME93V#`#F7X@;Eyk%m>pxgxgJI%Gs->>Nn1fr?#zJwcq<`2MY`d5qzY5VnKMQcLV2_uKB67F@|Pz1r)$(U(? zQnW5UC)F4Z(Lr6l`x@cS&iopjue3W(LCou!d9C`b5*LEzw}oPhqah!^<9+>HBMeIE z2$CWY@LP}1_~C0yZan`It_!&PM@VDvy=QZ=iK*)@MTSM_HXYUr-F6oZFd`0^)?#eL zML+l$eHw%?5U&Tr&O3S>HbB!DcHMvx3q!z3h*f=!#ruFwFf+ip2OHu&Z4lO*p6eff zhIwo8Lr0-$d4LU$XKx=en=d%sbu`{{fnnRnf*kAhCKkoAEf@WO4FlFd9}IJA5Z}}H zE+Mo*%s`1YKs2n{7U?@Ac*0^YM$_8`GVG9M2EONH?Cqsz^t&x0 zCOVQKM$3&UYnL+%@cERFG}|PDGBAvUlty2H;C+Zjdpf$zNG$N;lYB8IU`;*KJ8$zE z8_HEiDR7ka6{v=MHquLacAkS;Yo;mOR6|&`QTv+OFftksY2|gcgfZ3lm(gcscO;o_ z%Gj!tqRicZwR*d4{xb>(vre1!`O7#s5?dqjmGN-w2r1F?OUSHbv-(_YO<(b%X2B`` zAL`=f@DO`D>H#^pziHdX9?%?hO;F;b_?z2&l6os(?E@ch%+_HyBLr(wI|H4Q~&+XRi0j6LdO}O@}ZoXuQOrBDB znVDBnK$N<3Wiulssos>xXC%zZ=%!2rc)S_-TYvVa>5fn7JD2vzrYhjY_)Kj`bf~=a z!X2LJ&bZTgKK;y1zIJrM;Duq=v)=VQYXf0#j(34g=egMQoNade%xll_!V51kyK#+w z@>^fy-tj31^A^01g{xY@+7`s%1$r0g+<+L%qD_^H4P5MC*und2!+L9YXMks{r!|83 zSbKSwkD%Hz{r6)!q;@0s7I=iA1=Y<16UxL-fEGW ztQb_|{IX}i8p2ZRRM_J9|g83th?0gtdHU_+D^ zJ_Kd~48no6Ja(RIw#AFE63e;3-PW=Z!`v7Mu(S=<2weyqTFa@6DeryoTyHH8ddID$ z<%u}n-EL^Zz$5SYVmss8vpo);`!u_2FUEc1J={M&<6_sdx4+=-jXhQe*SJ1!5DA>E zFBlLWo?mcwa>B#oQ&!74Hx?_-x}I*gBLrdF2WD1SG=^cf1!LIk9P@V0w%f-2y*W}o zxLVykcx3R<#H!ucrmlc#NLC)w#Ixm3S1)mM{T7CJyjR7{s;}rY?W&_p1?Je8$-j)q zS?RjUQfK;qVHl}}Y4(aJK73W=t$xnfruZ9-GA1>>YrQI;w4~=U;+JHTM?IU~weP*a zasn+q)iKmkWHuQbIbu6_a4r^IvnC#+7_xKOM8|PIxMZh9ULSd1jIw(d@WX&KoDc9g1K{z7#$D;YbIx03*09;2je zgq)F(=U8B0heFoRi(a5oUhQfx6^SbYSBB>3Pfa)@j!0=<$joQLtO~+c;t$0n_*CLQR}MwHXE2$(pAQLzv7v34rOzG9&P1FTOTBuDp?vu~E86#6G^BarbHp zzk;+cy+(oqJx|q58uslu{HZ_ieRQW63_}if8gh*Wm8VslC9Sc1=#`J~+H0?{JUrkZ z{K{9bKJaAS^Be;|^P!u(J$Q~UcARf}`qMRk@`Y=B@}sYE`0TUD%<>QZ)o*anwzL6S z=P-?-wHA?h0?W3Gp7w+J46zm~8K(li@92h}5FDo)p?_@o%+mwcfkP9|M?XkH%?T_8 z=02vpdx>f8-4^KK43O0U&%W|J2RHYbEe%a$xOmv{l{Zh>bi>GzmzAC>;V5*MjdY#9 zR(Mt7IT$H>&hxd9_!5#cL&jT5ieO?Q{0a`K?H-x_QX#l7sPy)d+MakzG0u&05>%d5 z-W7j}{4E=ul`yBqc}{Jwo;t|R)~w9b$%#JdwzNHRE^8FNLS9UXD`e+@;zc>KUcT#w zr3|HZpyi9`Yo+(exDxNldm&YGA;G8?rBiZl(q|b<0FULr{5$_G&p-bWHs0|KTHb!{ z!@Pd`B@UKzns?tNY`4e|jqP36Giw^KhF#Yc1>CLsxc4+wz4yJRZ2EN`f}wW-e3Y1d zipp&_Ju@TpKGNk3!wSRTVK;bAC9p_~>eJ>x=RMoqj_WVo!uptL?}q>;mV|OyX>$P| zVpa9vVh16FST;(eWL^Y`28Da?h|_l-5at#^Xn}PI%n)`W+-_!tZ*4@_5wPAf6T_JV z77~pbYYhD`@Iu@2$a`)!4FTb%7-ZJ44W2~^bk?%8mW>;@W-Ld+GbfI3W`!r-aceo_ z39#vQ44vb39A6qN>-kOQPk)kOaSJDbrfoUz9dqNkbN3e4_ZF;oJ06~0@Z@5{tevr3 z?y>C$?w@T~hb0}BjW@K$F<-P;E8M-lq7N{bhP{R5=E0Ju_6|7fV3%crj&Uu@_IyvZ8EWQRE%L^w=v%H6lO5s#Mp;w2t&CsB64Unc zmVuJ_pvPQsU=*Clav_b$uJ)TRl!+;1s6ZLf&*(h^Pm0o0uV)Mr{hU+)Ya=bsZc4mY z5%Wm7O5aD!BLivVm8l4qKiA>{Qd%mCRlXMTVyiJK_++9f#8Ud75|519RE-yy=SXX* z;#gp>p-`+lM(J21#HV;~^u9}znF=U{QF&{UQLc|xi^?X6*d>P=MtnpgPT4X*g%1io zHCnEif*^jBmYw=sf*GHw-Uu6S0H`_vGfJg?f#$DU-I%TcrPP2#t}H z;+{Qbri_2mP4xCsoAY{3BIW%u%oNfUxMs$b=bMZYsq=_`p#nqU#RR&H@0fhgW1AQQ z#b{7WVmjw|Um8PDZC!eeBm9~=3yziFDy8H{p4p%CQ$O-a`gI=-Kb`AkOgfgX!Z?v6 zMqsvVcU8O#N|jTPeOCK>6wcywD2+&gJ_`4g|O+`qjevS~{@yNbb{jB|>yU-?f+u+6Z}mBmYBab##}BQM>r2tTB) zH#utVlir>ASQhj-?OrqJXbdU5$~;O7wSXeP_*t_S%2>xsugMer&FmGQfh zJYxepwKduvqF@*A7e+s;`(*T0)I9|><<8v1bck6PjKHPjaODG97mzZkQN&MR=z9Lx zFaH96_z(OMoa?!Fan5Nk9B&7H_uTQ;voG`QS6<`k_wLgkA4O%@#Z05E^URti9;vi* z7zeUy@qLW6i4V{xOBEBX(cC$M)V4_FxY48Nmt*@3^(?I9y+__wbm# z^ELi>&4mpNsaksoIo&<3srPX4#=ER9E{LVq4CewJfjxw!v8)U<5~JpA2%M6tv*Wo= zJk{MI1n&^ra2f)0BW!`W2%R+;=UIxuis6QBI35OW7{f&fm=IVr!kS1w^TzUUv&D($ zwCnhFv3zYiAU9rO@ysWX@OGeGUKVG@I5a-&2u+aTwl&uHVv~7kl=Z^jc3vu z8d}@nL(ld33>!M6S!S%jmf( z&Wa(b#Z`VV%fU;}Qa|T)DXG{}vkX7WsNAgFO= z9XFKejP8kW8QWsUa0x~q^wLi3@xJn0^M;Y3o(uAp=khL#^bCw08)7SLG>nQ(Embs@ zafERndHzjB7A=<36CyReS>08hkc?59yG*=_Qoo1#Q0<1wtg`N+*~lf0p|mp@k3u-r zc^ZO@7EKo#8J+PvGW#cdbz&^$U$re#`VV1Lh$ruh%vIW<#J&qc7HZV<6%qk!vm_5^ zY`m4JYY$!NPD&bPW}_j5lFnHxtyhFA_nOOwb{K<6eUZs`3T&%EC*+EX5XW2Rnp`9) z`XMC6TeV%*NDjuYp9~eRuuB%y0?be-Cqde1L9J^QDdE(JWkw;Og((Bjj4>$Prmf!|Lu#ond z2Wz4;Dwhp628LKD#wUD`0Cr~i!5{rBpZ%WK*gQJrd~K20lG}~tJ3skh`tx%x`iA-S zL-wxiGhfc|&5B?Cy`Sg5|Ke}5SULJO7L6HvVAl_X;8-me493K0j|&(d*mXN@&RSmi z10j7bhIte=aXX1*tW*P2yrq(bM#wln zncO}SP?~=$CzLkK%VS~O#E9=CCHU%!(UrFI@Cw@rN-}BLpq`$x?R;{DE>u~@s+C1O z&n&8fT9^Ev9s{)<9Z6h0b(3HE)4zlTPP>k?p=aj|dnWM7y@B^HI^LMy;>l0{Nq*v= z|052+^s9(?%&cKCpRw8Qn0s)ofEc<>$IJ_Z8))VYAq-d#rWLqwJn05z7FNM z$3xHD7>t440FolnYYA+8;3+XY=^dvYo?Tgf{8P`dys@HV;OO{*NADleodn)~{T|={ zgCFDBI|t1AEsZtoY{!G`IoD@1=KVnb`a^;>eBBBA&)y;I&9QBR_b?2O2S*poF&HCk z19<0{8N*J57aIcx-W@!5jp1PkTtnCd#|??myH+e4JhyGj5wLG8oxn0cV=e8n!E78I z!YwNtrwZ*m;-2+GE1V4jdrfpA-1h?}z_*8i-`tyV+8gY(r;)=Cv07R#!oc%SJ;lM^ z9;fRwUcPga)uLrFpR?^9zw!7!Kk<<}*x8bQ^^Hf+U(mS~XS;!;9+urHtNm+SxPYB4 zF}s1#7;Lj((|b1CEgrbB+GD?6aklR0XP(9e<|al;Pr5Tq(?%sfct{cR$b5zvWN#ZV zHu9vtBQ&$f8iK<*%eVGF%-;4}9JmuiW(D^(%@xruRW6PxNfZHI@si6QwceSV8q@h$ z@G2Fcww3z~m!7D4b^cZyjblA>I)zZi5k?}ui~)+nH3wHhm^M^(hr%%wf7UwOl)XYo zPL&ubZY&H|a7kYkV`#0R8ywykOi1UX8)0w(V?)_59;4`nW;UbStP4*vX-fp;G<93_ zta!?Z#FTeqAo#d%JsJHa4PK&g?|qR-3O6PyV>ZsSI)oZSDtu22^VMR@8rMPqQG2v=aI4HWR6Lo= zktyk9y}n#ZI$pLvF42>WT8w%{y+s+|6>br~Yt~uv+l`HNZ7!(i$Mc!+i8RKt-#opK zbm&m;4Vp&PoPrP?8hZ!L7qX#U0WaB(EcC1wvb-x-Oby+K`oJWeL))SC`FxL{F|kB> zry53SBd?T<7+XP1Lpk$1dv-+Mg930?LX=Tc7;=6n@A)tZKDsY8rZP6E46+ngDpN?~ z)}AM+?T#=X^*QyI3(tufQ%y6e?V{IFJ0ixGTQT0Xp{~bKqTzZ21$7U4x=DRz^wLq> zy39J}S+RF?;+9lc#x&^6c3M%dH4PajzHh{bn6Xb8xB`<1Q=XE zhMtp)Er*@skN@ar_|&T}bNkLU4i1*=?JrsE-yq!hFzwAxuz%w&-Og}yyk@r@kU==^ zU;{jU0?P)TtYO>3w>H8zHgo3J_BdEwqufgZrk(Af8o#b1K;;K*4>8tn+wjiaJ(IO zxZd)$Q^$EQoOC^3KkN9Hd$+ju)D50_?_F%S<8-~DF>TtdA~as&$*Ka4F}T68>piv) z3`0Dnp*5DqL$DxtcHYwv_dBn9VMVMXvI`C)mSZ>27{j9;zUOepXMXQ%>^*;oEG@_H z9CObI#v<7mn5( z&04nBawA9Vt%qFf0$;s<%&^=XWI=Q zxpSM7!EkLREDaoQdhi3Yw!ynCezsy59NW`V7IsHxAOu6-ZRs~>G>c^{D7ZOA`T=J1 z=&7_8u`#V*tcg5+*us2?3^;k^v#+N;#%6@K6^|!)&E?mnXQp2= ztI2e%W$Q>$ubd>xqpO^Bm&Y|VUUc8F)hRQox>S+vnyuwKrZk=ZyDaXn9DrQ9=koKV zO{I86ojso7(7RYr45DMdPg|OD;Wh!f!P7Rj7@9t%xr;Fj{Xhu$Jo9K+XPBA<|9VuJ ziNT!Y{E`q)01%1&1+q*_63y768n;by^oNK?)<$D3zp|{WhIy^GQgqo+Q?V;tas#=Y zu@0{twj;O-HM2GAe@TXDo}BHFx@TGnUgrJdF@kQ!d+K$DGN`@oYR`FYN2(K`=vV3C zpyx7)SZk!IDniwHtK+Q7`Rwq>PK;bmpy(O}_DZiv?k=a>_sXUDUNxD^ut@`)^r;OElR7O-<^z~; z9%NC@M5VYRxZ?Az#z`7DrL`N@zuKpUgWHWm>~#f;OSHHCipnYVDW_ zjpiciNA-A(x=A58Q4NR`;Hti^dMO8tWSkrQeC31FTO>r^Vy@zDjiJd*O{#I3pG*8B zGLFFHFIWAgGA_o(xmJ1$9#`bM{9Sjh;5X%em!=vjZHx?qkQq$9Q7h)-BjezBz2krR zYd^GT9%5Qy>vrSLim^z@uLuyn# zqkUy`6U?e>V_2$$^I;BO`Z(8ay-43%e0R?I#lX!LaPZa! zzIe~@?sma>XSi`V=l1PG4h|0Rp+UT3c`yenxb2QFz43@9Md)gZx{NoCDc)6hnC6AW zxKH186h7S(3PCG=6Byx5!PT;NZj8&LBOsG;j>{ub3WNH~G^A;+3h0WblJS@^K54wG zd?)^w<%zw|{E|Czosy`I{>q?}}YSFoS5$LY~G<70fJ-k;i2t6BM=9$*1lg@raO z^u5}-JAI9mKms?Odyc>MH~u=#2aa|d9&J6RyFll9j@H{$85w-v6P)Ma0RQ@#4{`SC z?_~b+hdKD>>&%YM@w0|GLLWTMtYx?BX^o}tJqv5-wm#MmbD`|pvklrr$5Hx*&9gPuzq~bS=aICe1jOz5B=09x%1)M3};*V(~d`P9`oim zpKx;0^YCQH8%G_k53KuvceHWgR@XmUB*q0SPcO!g=tl5LS($pEU#m zL*jq$^;>qqb7X{n>OJo;<8XJz%|*}V=Ew2vf@hw(LDRG})-vFE>(L_uuyJs)8+hx% zIgi%S8L)N2+VsGUb{lRDAi?z++zd#s(b4DSihJ$%XM^q(%uK?hSSTVu_|U(bOlIE=IWj#4x|Jh z>ew##qxp%3LD3h~vsaJ)im~Sb7+Yk75!m%UrkFzsObvs>2cfZ%@eMOOwpwz zqH804BwAGfC>xVGJw5CHiGfE$9c7eH^nOJP zy25-dOjkP=H8-6JTbva>8n9Xb*SXctQ{ea##aAba1RWiZ3mcNXlh2#ahJ3z$S^iGG z=Zl(8SiCpla=_aYrV%Wdyr-t@%Qg%_E8~1DF~G+5prFf(Bl>ew87s zl!nrkQgXBscvWvRwFTj z9u0LFF~A(D59+Vf;|uDuUa`#dN;_kEoJ!X`_x$Apbmk8<5bAWV7OVP-_aUB9oc4Q- zjr92b7Q4sySwDEh`GX_2C+7@DCu|-*p?`G5$6tAoU;No0Vl|xb zfBwd|m|Ihv5;>wcA>pbBNTmZk#>N=F^!5{e`a7SC=ZB_3P2Q(U?!l)5PEI|SHW*}s zU4W21!09|xF{RExV+?I;Xj@C$SenKltwmbPi_hKVqc@j)$DKR;@=yH`zwgIB%ZFaL z!>@e#cXGpbL+;6r*7Zn*4>-*ooBo4XiVV$ z`SRO*`(%xcDIb$4`jqAq0f~B7263s9nEBJlSg!`)g#C;Jp+N25rnXF7^(?q!1lj9`juC?^XNBd-rKS6dz$4O zFHyF+;0Z3ol-Pwu`Vdjk2Lyv7rlMj3TM6_bFbM1+TzF_P3{9N-`8M#`KlC9EUwe+( z-5ao&FrtwX~+;;rpjtYe+}?%a8r*lNgT31rUeGmkLHBpz)fw0kW(P|%bl_QbY z%N5Yl5%+lN+;}bZe(Zb-;mCoK@i1Sf^5{Ik}0_qIr^HHt~bh z$*IW}Wh*nx^zV{&l5t6M*F0}Ee$krR2r2U1rOb0Wk&p_Q1L45YoxMj6Vfl@RH1 z6xkVhj{*d>Upo)E>>~*zCY+G}5|&MvHraUdY7`aODaxQx&yRpGZdo*l^1ei^kyfEF zexLL`TJv;|ki2QBZPBs!%w!plNfJCXltmMtk~(DIQ}<$)4bQ3=)JTigk_hyso@p>C zey)Sdzm>=aDm=MFIFKXnWUfvC!^NSt- z_V0O-yC453d;5nRUY`RE4*>0l)6>T^Gx)im`u%+8cYP;+=D+$M*!BbS)|PQ=Y|Ja4kHU%c6GqJ6 z0JatW>;LK3_{%@@T{P_+u`{G;i`QF>iF?w#kCk4njn}eaXbc34NZzQD)>xvj#tPC{ zB>>}k%#09%@ME9O=QrCj^kO@0pp{R zZPOe{3cU9LnL+R#`|L;g)YBj3L5xr{%LCWJ~?NnT_nDUxy=ulG^e3wm6NHx#8V^CL+R zDKaF>zVdLL>qH^YuaH?$;TuYr<)J90T8pcZvNS66Q$H8pm!8RdM#u$*G*1Xgrq&zt z_{yq{JsE|ejZ1vw9!O*)hy?Du^ddj;=Eb*tQte-97o=B$67Yt zF+*@BunhwX5guLaICVWjU?+wzyPo%~p_wm%IoGe>;_2I`Tnt+-*83bRXKed{^|r%F zixtCZFzn9GF~?iByMf#Ldo*;gSnzNS+w*hoKU(8}cCp0yRJ0GzyfwIs6a3;S*1L{= zw`OzUzzIXY!y2KtE!*~xbqBZhVRmp0i?H4Gi1#$}1>R+1XbBPviVdk?T(B)7GbQ*F z@;C9wOYuF>;DYdI@eT>$GsH=WJXGHp~dQ(t*Zk$0^4cy8zGW_y=TE8oU4r=z^ zA=qT>!eVYYx#(i0cGB@xL#&YQoQwN)tzp;oIBzi4U>if%_ZV+6)-qcx8MbRM21)g3 zDE-B@LaBok2VDY^w!wRcHx?Um6g`6?YSAVLgEv^~Fvi1>oD4yb;ERJ8q!u9yL`QQ4 zbKvx*T5DaUKZMA+qu^yJ^N{_JBr-hAm}jkge@ zLRrq`4rSuSgQ@0H>7rf;Y3w?^C=@tBJ_|}RQEGUU`-5V1YW*^zW(Ghef{Oeal*OS? zx*vN?GoEBz8!bVo9=_^h2;|~p6~+qRLTyj~UVx^eG$9XAJ3AET*E~liZRxcbI|E zjyT4P0<}=rNDIwSU{Y!LlF$u--@vN@gMQ; z{TF|ill7(;>2*F-V@l$hnII#fN<=r7O~bFf_k{oXUwng~`ra3?%>vuZ5HZEGZmfw$ zb~4b7wWaV(^qTuz&;r3E;SexQI!?t(DvXx6a`16n#>72n-ebjJ8({$a;u;5g2e_1j z6?}?LX6H~=TYRE~#7KQY!0HVKPdsKu9L&Y?(#Kxm7eDb)e7E6uzV79H%rOoTI1)`r--$NCqB~3o@jKbecrj($Es_3ltogdNy zH;NR|)KzG#fJ4bjB`xFH76xABM^U0j(ww>$lJ~Yn*sB8y%4r@`$7SI|Ne1}bu5t>h z;ZOMZSca7wHJmO9^Srj;`+tpw_fM)zxmtjou6GCPYUeK~Y4A z6p8Q;Qluye0TC1hjI2yVAdnpzoNjm9?)K~Fe*MO8y5pJlt}!&*5dZ)n07*naRH~If zYTW03&quoV47+yis#R-Vt5!|B5w;#Z>d(|cHcLpV95Sd}%-RmoCn9!iS9Rk1539dIBFC1fc z7r(gA^7exD_=tyB4i-Jz{YaRSAuwL^@mKC~@8kqR;F}*P=cAz;0uRnFSuO^iKVI?Z zsACrfj;v?B9|?U2;~q4ynH-^8@ZfU8omJ1?3(KQ{{bq_s){T24jL6=>>WGWo9*e{8 zb_f8RFULPN5UD8vroD^TFz4JiqxmmZ4Ce zNGlg-q*3Bm)p)PQbm1#ATA7nq)*%mdlC1v}-K8i4hp(r_S6iK{8SKH>b@na)rt_Z) zxyw^pw-2bid8TI;-RckyEsv{uZ+lr&@wS|TEJj!kHXglZ;&Euk#7Mdiib?HJn4G8U zY|M7E2Ja?%Yw3D}pSsky0Zcri4-*TgtxEBW{`QpCGfOfS!uI8bx2omG`$aA%m5zI>C? z-i)O<(^|R+QGV7Uw++onol~7eoxc>c&5fmAlQ|4$FU`Y9c^FOQzna+)zuV(m-IvJ^ zDv+&zZR&Dc(YvX8P=xqw<)A1V<#%le_F(im)uMhs7;tGLWpXnOFU`EP%K%C#?iJ8f z%x$03dO?t~ptF4z*=r_M5u2rqw{y*X7v@`mTL^%Im(B?`d)L~uB80l1GXt|}+bUF& z$CbnL)X4p8&tU$OMsTzQT`0Vno;Gcf<{l|XTgRfMT}yp)SFKwoznDTA&ptQtcr{bchs=@BIk=-z9u&N4jEWXRy(Om> z7wg)zk@OZeN>#M$%x|W;AHuRI=Ve{%wX5qbfA4Sn1^)0K`(yas$n@kPArOIr}H)?4LYgJU?T)zCfktv)}tE-1QZ1v%}}~(v*JWCr@ymFgZdp+P(9HDKM@l zhTifgzw}%AhyVOn*lc&{EK=A{p1lwJ!QcIHKJ)oc^ZG|l`PSP{cyu+g5YqXkd3B-$ zS#^eg@!lB^A8z>MX%D>+Mu(pq8a!e0gy7NO5TDZCRjW%Dinmv+DQhgmsB=9sBpvOQ z@wlWLCjpw$^Q;J6GDtd`>J`|iO!U^!Swr6mwu_&QG4W`VE~dI$8x@3ny)!~@47RtJ z-qKlNSS)zu)tC5z&wh^2eDq`B3>rKq_wO()kFZ8~_Qdi}zVRJ`C?@YimlP3ClC;ob zO9sL$y*;OtXI@e8HKo1hJ$ouOFGP!ktrcHO&5&|-XvOvHS=UmzUCVwDNklA(N0)6t zrlBQ`sc*R$%e)|_78+<3nN}hl($3tW+rWh?n=;qW3PhzlsW6a~VMQjo)-hHIu}$%- zMtCyLO*PJ~t>?>To|HeEl4e+CDrKJ4SsBM(;d3Y}T3FnWVXYi3X@7A%cG2^f{_20h z$?}-T+cg(^&mhXXmun^r?_6y`l_TJ;^~iq1ue|q!`?pWHYlX*~J$<+2p$eZ}3c)G= z^7b>_`5ixm{noFrxLjj`Mv$-w^0E?nNJM;5>LFXrxJ^qs#~4Xn;@r z_-E*jm*{q4jbVCp!S$OLxT`%!cULSHmg@)Cy!GZKo5wr0s$6-;y{_Y^vz&I8N7Km8 zMCaKq1f~$UBZf!bvDLsy*R!5R_Q2cT@q)G71MVJKrd{A|0%M3-0U;=hl)1AZ9-k&+ z=uixXxZ9gj1sgoqK{>UCy+-H5=+cT8&NmPhorPx<8iQ|>H!cH^FJy?=%Ztgm;-@|dk!u6835M%*;9 zTkql#RU%-)NMLCd9oH;YORlz#$vONuvRn-~H3aVw8BpmF6AO0@eIJjf32cyknK z1OhQtp2O5~Noy8ms2EfDuBu|vf*KR+AoQK4NU{(@a`NL2BHs}NC5nEqwguV;_q6v` zz}LOu#%B4DWtlRsMZy8T##i*uSN*xgx-yUql@@pNM{U+#m=%kw%bf#36nP#3@hD8M z$*|S}lf!HPQznpwY3i>S`IM~>Gk;I=#nj{D=5oE2&(%C>fx>xX*yyu4H4J79*yaL>}zS!`LvNVnpZPTZlBqV)lqMQz%1$ zn-hepG~{EhO606z95U~&0=_7Id2S7xX`oh84|2PvVq_*-bWY3%peW5|?>c{0#JK|{ zC{(1;+^CfZ`B@46Mez)j;|?n9wZfx8uBY0r0y86UhG5OaGFBbHH?MPcGCbWrzDeP2 zs@P0+9Y~6DG&zVyXU^K*auukuHK;!FI#-}T%0y}#p& z{Mn!SQU1+8@V#7IZ~4l%-lI$DYAp;G*6{Y#mY@5z_n9u&Jhuqg&J)IwFgessh@X() zQSVaj;Ibi!PIw!_G zUw!8Ty57{i*Ng~Hc8*lui#v;s`~I45UO2x0{)%JUu~3IImb*twzICgkMO94E{K!3i51CzNHc3(6Y9k;e1GI}HSD z2%<;`JoC9%x&Qj}xa%E=u)p53{nlf=DvvHVOeoLXJz~0^*jpLb|GL0j6vxU z?hGBh2pjL|6s#(j3ipiVfp;j1L)dtS^MO_0@w~OTNm+|BYBa>Xk6Ac^phh_s*aa9& zs_HHf2<$>&X$%jXV*wnC<%xQZQMMs)yzF_n+wxZMd}Z`}&{;ZB7N@sR+hgv10)KK! zFov*f4Vc?tO0Az)26+yB)v$-S?RsFvQHH#bUrsvGVm8l;9`q z(J?v83?IW>lT!jcpjh=7V>s%21`F17?Dr!Ehm31R-f5Q}{opzGqkbekA=Hxzyq%!+%vZzIwtjj1R*Hx=!S^qOhm28G<9 zm;7aRbOH&2E|xo(!b+_uO}*K;#D+ zY42-MeeSC(8}}Q=TMYZ6$WWf&Rx>ON2x-p5ZO73XP14wtEv#xnVNx$=o~2n8&HPyo z2)DG4_vG_e4)1RvkjE)aA9Kmk2%>i0jS@CngvPKg!_}tmh6x?6z0N`MRF1A`)2TKv zFJmjX*&u3Wtqoz!QFf*}Hvl@o%XGusV=2^9?_Gr+@7G z_&fjj>-?$T{{#H>zy23t^o*C6?9ZOCJHKMQxL_d*e%B9vmY@FppXZz3d5d5D&J&h> zmryFMR6P35@U8O=|L~2+yz!kg`u)fYVUO4VVZu8X&ppkRN<`xK%&3gSsCkN<=l6(@ z2I+|D@)$g!%kVqS7!R7ECl(s`=!v*}`FJ{KO05p`1xa+8d5oAI!-rJszG z0*D5T*cd=U=xk4CCcg6Rw;8>soYZiLH!Z0M)f=4*^qDc;l2T(#&aLH?=TZ0?#V|Kk zny7nKBBF5)8bUq$yxk)$70&q#(Tv-(YN`8HCXi_zHpy_{l_qbb?>QSUz-Lw>$yKG!g#wz)L0X&ncJ zHMepzjcvX5{0INpU*`E|p69!p4UaaS53VUP`@FP%ga7^Li~PjPclnLC-{o(8=C^V0nY$c)^P4PH z@!OH^bb$%NDjk{RHZVHJUOX1Xq*Tih!A>#DW52e1-F7JlbH@w%#SwkiFjLTJa_*FKpk!(BO0V zUv-CB#%FU$+Ut#8c}QkYp}eM}d8c^`+MHV)=wnZL>4 zijWI#9LxFV?S!kon+o3sCFdX)BR8FkE=h)KP()G&nu%E&opAv8U}6+qBp%n64fCv5 zrdQU=TRvJ~JRc#s4ocQN#k7vtYMfVDU3qlHq$VgTlq&1_@#aJE<0+!P9ZT~U*S)5o z$4GZJ##6-HgNC?OA-Bo+lKZi3=61HrD6BBTPovB#PK-ugn&zJNRcxo?v~O`+sJ2jq z_lzuP;A%*QdCFk3RiB#yYgVJBI!{cIlQ}pe1-GJs-a@@GeDXNwGs>USFlX9Zz2_kC z$&E(2@5Yd8`)$d$HKGrVsmf5Af)2tQacB@LD-&d;qFww01WU-@Mu=v^XreL9KvMy6 z$ed?zX_cQ6si+-ko>vhObpWj=E-SAj0%)BV0#Si1TIJ#5C@p|QC= zqE$AfIcEZk#(CZ+9mf=xDefZm4pg**i{-dJ|K^L&%ZMkZ?G}BS;2~QR-xbyD1P{jGVj4 z<9syWgAXY^DPT}+2pFg6c4Q?zf9OYF=U@M~{s`{uiv4=eI6B;Cfs^td~(Gi zbbRSYzQ8lLkNNpu_$JHV+)zfW2!p`cSlTR3E6f|ku)aZdo zql~rkgof=E8I@)oAU;GbQ?{_>IJ?lxi7#$+Q*PmST z&h-}Udp^0eTwY#dh9xJR@?@thd&~EntT-CC{L=fE93LO?#zy#&JMVC|_$;?1aQEbh zfAVMcFSV9ARu5A*<8f>D@HM<=cxBgo6AB>5~h@< z-bGy_1dfka&YxYw zVcshL*tC*EBe!{=9^7)+MSh=mS8i|rW^Lu*_f{{TJ+}qMO~t>C;VHAs2E5#yoj}4* zPz_ik1Pson47tQ-d`WW;F-lI{N++UpeTN|IcP<)x)*>d}6VsNX!CuZDM`GpwviCb3 z-STN`J#Zta>t(I+I`RCsZb zpUrn+t~+Lie5eOAkG|h{X^S5+!{I6$Y9J(EZ5`QpZ+NM&p7)(6hr5PqJLO@P^!&!j zkgvDsH`i-Zo05_El8lcsJnA>>S3svqxK$7w#!dz)d3Xy`gX#yF~Gqg<}RW>YU zT(VItv;Z$Z%dFZIaEi|_@XPZH2W!)6R)=7%ysE-84YnHO z4Z>#_NYnq*7k!WxW_(WaN*eVc>OsYS?Qr(hF{C^Er<8Vzz?kke$Pkm^4q`vIaarC(RB;t z=ooXNSS+G~34k$J4E?IdtWNRXfllBPT$1$4>Em$?8X_@SSf_EB$QOfDUXqRcQen3d zRhiKg zjUtfgqVTE4v6UR!h{N$`j){Yfxs7Iq0{@ssn)K(Yw*}lRy1aOhI|HA9=L#j6r#@n+PhL z_Lkjpz=*J0Z+YW<&lg|3!_r^!^0TLW&z%$g>HqgFbh+a8ox5z^#1jV2-g<+}`&+(3 z$Mn{FgyDo2mJ9wBr@U(&zyG-772JQo`HW(Yh=1s8{*XIL&7_d85~gbYURXwfGh>q(ZI^yTi;g{WvK~I?@P~#?9bPMr{5ptY zY#h$1Ort1d2VA%{wr=pe#9M>Mm=5pL?u6uY)KDGYeP+mzaWLm1@FepKyf!jK@%lK12 zIT6FW~{jfΠ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�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%0Aeq<^o9z0ctzd9y}RRC9kAmu$N}8K4)wQ~ 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 From bfd2921d38b3ae29999ed5c44558b337661d2705 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 27 Oct 2021 18:44:28 +0200 Subject: [PATCH 1316/1500] Revert "Blender 3.0 bcon3 (beta)" This reverts commit f7a3450e63d129b680aa5fd8330d54c17efdf495. --- source/blender/blenkernel/BKE_blender_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index b5e860f8c32..6fc2fa37d9f 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -35,7 +35,7 @@ extern "C" { /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION From 3e32a68f384d380ea9ce0a0a6fbf47c9ad2a78e4 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 15:50:14 +0200 Subject: [PATCH 1317/1500] UI: Refactor how dragging onto text buttons works, fixing issues There was a bunch of special handling to support dropping data-blocks onto string or search-menu buttons, to change the value of these. This refactor makes that case use the normal drop-box design, where an operator is executed on drop that gets input properties set by the drop-box. This should also make it easier to add support for dragging assets into these buttons. In addition this fixes an issue: Two tooltips were shown when dragging assets over text buttons. None should be shown, because this isn't supported. --- source/blender/editors/include/UI_interface.h | 3 +- .../editors/interface/interface_dropboxes.cc | 23 +++++ .../editors/interface/interface_handlers.c | 85 +++++++------------ .../editors/interface/interface_intern.h | 1 + .../blender/editors/interface/interface_ops.c | 34 ++++++++ .../windowmanager/intern/wm_dragdrop.c | 6 +- 6 files changed, 93 insertions(+), 59 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 39d70f15a3a..725c9921d13 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -788,7 +788,8 @@ void UI_but_drag_set_value(uiBut *but); void UI_but_drag_set_image( uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free); -bool UI_but_active_drop_name(struct bContext *C); +uiBut *UI_but_active_drop_name_button(const struct bContext *C); +bool UI_but_active_drop_name(const struct bContext *C); bool UI_but_active_drop_color(struct bContext *C); void UI_but_flag_enable(uiBut *but, int flag); diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index ab0c7e088e2..81a1354cbe7 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -24,6 +24,8 @@ #include "MEM_guardedalloc.h" +#include "RNA_access.h" + #include "WM_api.h" #include "UI_interface.h" @@ -59,6 +61,21 @@ static char *ui_tree_view_drop_tooltip(bContext *C, return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag); } +/* ---------------------------------------------------------------------- */ + +static bool ui_drop_name_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event)) +{ + return UI_but_active_drop_name(C) && (drag->type == WM_DRAG_ID); +} + +static void ui_drop_name_copy(wmDrag *drag, wmDropBox *drop) +{ + const ID *id = WM_drag_get_local_ID(drag, 0); + RNA_string_set(drop->ptr, "string", id->name + 2); +} + +/* ---------------------------------------------------------------------- */ + void ED_dropboxes_ui() { ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0); @@ -69,4 +86,10 @@ void ED_dropboxes_ui() nullptr, nullptr, ui_tree_view_drop_tooltip); + WM_dropbox_add(lb, + "UI_OT_drop_name", + ui_drop_name_poll, + ui_drop_name_copy, + WM_drag_free_imported_drag_ID, + nullptr); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 6dd6f9eb359..51ebe5399b3 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2442,39 +2442,6 @@ static void ui_apply_but( /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Button Drop Event - * \{ */ - -/* only call if event type is EVT_DROP */ -static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleButtonData *data) -{ - ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */ - - LISTBASE_FOREACH (wmDrag *, wmd, drags) { - /* TODO: asset dropping. */ - if (wmd->type == WM_DRAG_ID) { - /* align these types with UI_but_active_drop_name */ - if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - ID *id = WM_drag_get_local_ID(wmd, 0); - - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - - ui_textedit_string_set(but, data, id->name + 2); - - if (ELEM(but->type, UI_BTYPE_SEARCH_MENU)) { - but->changed = true; - ui_searchbox_update(C, data->searchbox, but, true); - } - - button_activate_state(C, but, BUTTON_STATE_EXIT); - } - } - } -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Button Copy & Paste * \{ */ @@ -2672,15 +2639,9 @@ static void ui_but_copy_text(uiBut *but, char *output, int output_len_max) static void ui_but_paste_text(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste) { - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - ui_textedit_string_set(but, but->active, buf_paste); - - if (but->type == UI_BTYPE_SEARCH_MENU) { - but->changed = true; - ui_searchbox_update(C, data->searchbox, but, true); - } - - button_activate_state(C, but, BUTTON_STATE_EXIT); + BLI_assert(but->active == data); + UNUSED_VARS_NDEBUG(data); + ui_but_set_string_interactive(C, but, buf_paste); } static void ui_but_copy_colorband(uiBut *but) @@ -3024,6 +2985,24 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], /** \name Button Text Selection/Editing * \{ */ +/** + * Use handling code to set a string for the button. Handles the case where the string is set for a + * search button while the search menu is open, so the results are updated accordingly. + * This is basically the same as pasting the string into the button. + */ +void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value) +{ + button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); + ui_textedit_string_set(but, but->active, value); + + if (but->type == UI_BTYPE_SEARCH_MENU && but->active) { + but->changed = true; + ui_searchbox_update(C, but->active->searchbox, but, true); + } + + button_activate_state(C, but, BUTTON_STATE_EXIT); +} + void ui_but_active_string_clear_and_exit(bContext *C, uiBut *but) { if (!but->active) { @@ -7949,7 +7928,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* Only hard-coded stuff here, button interactions with configurable * keymaps are handled using operators (see #ED_keymap_ui). */ - if ((data->state == BUTTON_STATE_HIGHLIGHT) || (event->type == EVT_DROP)) { + if (data->state == BUTTON_STATE_HIGHLIGHT) { /* handle copy and paste */ bool is_press_ctrl_but_no_shift = event->val == KM_PRESS && IS_EVENT_MOD(event, ctrl, oskey) && @@ -7998,11 +7977,6 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * return WM_UI_HANDLER_BREAK; } - /* handle drop */ - if (event->type == EVT_DROP) { - ui_but_drop(C, event, but, data); - } - if ((data->state == BUTTON_STATE_HIGHLIGHT) && ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, EVT_PADENTER, EVT_RETKEY) && (event->val == KM_RELEASE) && @@ -11716,20 +11690,25 @@ void UI_screen_free_active_but(const bContext *C, bScreen *screen) } } -/* returns true if highlighted button allows drop of names */ -/* called in region context */ -bool UI_but_active_drop_name(bContext *C) +uiBut *UI_but_active_drop_name_button(const bContext *C) { ARegion *region = CTX_wm_region(C); uiBut *but = ui_region_find_active_but(region); if (but) { if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - return true; + return but; } } - return false; + return NULL; +} + +/* returns true if highlighted button allows drop of names */ +/* called in region context */ +bool UI_but_active_drop_name(const bContext *C) +{ + return UI_but_active_drop_name_button(C) != NULL; } bool UI_but_active_drop_color(bContext *C) diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 28227c2331a..f766bb1465f 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -680,6 +680,7 @@ extern bool ui_but_string_eval_number(struct bContext *C, extern int ui_but_string_get_max_length(uiBut *but); /* Clear & exit the active button's string. */ extern void ui_but_active_string_clear_and_exit(struct bContext *C, uiBut *but) ATTR_NONNULL(); +extern void ui_but_set_string_interactive(struct bContext *C, uiBut *but, const char *value); extern uiBut *ui_but_drag_multi_edit_get(uiBut *but); void ui_def_but_icon(uiBut *but, const int icon, const int flag); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 1a1d52b0425..c962a1107ae 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1863,6 +1863,39 @@ static void UI_OT_drop_color(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Drop Name Operator + * \{ */ + +static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + uiBut *but = UI_but_active_drop_name_button(C); + char *str = RNA_string_get_alloc(op->ptr, "string", NULL, 0, NULL); + + if (str) { + ui_but_set_string_interactive(C, but, str); + MEM_freeN(str); + } + + return OPERATOR_FINISHED; +} + +static void UI_OT_drop_name(wmOperatorType *ot) +{ + ot->name = "Drop Name"; + ot->idname = "UI_OT_drop_name"; + ot->description = "Drop name to button"; + + ot->poll = ED_operator_regionactive; + ot->invoke = drop_name_invoke; + ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_string( + ot->srna, "string", NULL, 0, "String", "The string value to drop into the button"); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name UI List Search Operator * \{ */ @@ -2025,6 +2058,7 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_copy_to_selected_button); WM_operatortype_append(UI_OT_jump_to_target_button); WM_operatortype_append(UI_OT_drop_color); + WM_operatortype_append(UI_OT_drop_name); #ifdef WITH_PYTHON WM_operatortype_append(UI_OT_editsource); WM_operatortype_append(UI_OT_edittranslation_init); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index df6d3d5e9e7..37a101cc31d 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -821,10 +821,7 @@ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const const char *tooltip = NULL; bool free_tooltip = false; - if (UI_but_active_drop_name(C)) { - tooltip = IFACE_("Paste name"); - } - else if (drag->active_dropbox) { + if (drag->active_dropbox) { tooltip = dropbox_tooltip(C, drag, xy, drag->active_dropbox); free_tooltip = true; } @@ -907,7 +904,6 @@ void wm_drags_draw(bContext *C, wmWindow *win) xy[1] = win->eventstate->xy[1]; } - /* Set a region. It is used in the `UI_but_active_drop_name`. */ bScreen *screen = CTX_wm_screen(C); ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, UNPACK2(xy)); ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, UNPACK2(xy)); From 39c11c03d0d7e53d7b4f8203cdc07938cb9b6a16 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 18:56:41 +0200 Subject: [PATCH 1318/1500] Asset Browser: Increase size of search button a bit Before this, the search button was quite small really, not much text would fit into it. Increase the size a bit, but not too much to still make the layout work in smaller area sizes. --- release/scripts/startup/bl_ui/space_filebrowser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 086df46d799..a33e2665b58 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -52,7 +52,9 @@ class FILEBROWSER_HT_header(Header): icon_only=True, ) - layout.prop(params, "filter_search", text="", icon='VIEWZOOM') + sub = layout.row() + sub.ui_units_x = 8 + sub.prop(params, "filter_search", text="", icon='VIEWZOOM') layout.popover( panel="ASSETBROWSER_PT_filter", From 78c9c1012bc9a0c0dfd56e54ce5ddbe97e6e9324 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 18:56:41 +0200 Subject: [PATCH 1319/1500] Asset Browser: Increase size of search button a bit Before this, the search button was quite small really, not much text would fit into it. Increase the size a bit, but not too much to still make the layout work in smaller area sizes. --- release/scripts/startup/bl_ui/space_filebrowser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 086df46d799..a33e2665b58 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -52,7 +52,9 @@ class FILEBROWSER_HT_header(Header): icon_only=True, ) - layout.prop(params, "filter_search", text="", icon='VIEWZOOM') + sub = layout.row() + sub.ui_units_x = 8 + sub.prop(params, "filter_search", text="", icon='VIEWZOOM') layout.popover( panel="ASSETBROWSER_PT_filter", From a8a1f4847967f211e03b1e162e75043eabe91029 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 27 Oct 2021 20:51:50 +0200 Subject: [PATCH 1320/1500] Build: set correct buildbot submodule and library branches for 3.0 --- build_files/config/pipeline_config.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml index 8222f2ff0b9..a56d7c4a85c 100644 --- a/build_files/config/pipeline_config.yaml +++ b/build_files/config/pipeline_config.yaml @@ -5,38 +5,38 @@ update-code: git: submodules: - - branch: master + - branch: blender-v3.0-release commit_id: HEAD path: release/scripts/addons - - branch: master + - branch: blender-v3.0-release commit_id: HEAD path: release/scripts/addons_contrib - - branch: master + - branch: blender-v3.0-release commit_id: HEAD path: release/datafiles/locale - - branch: master + - branch: blender-v3.0-release commit_id: HEAD path: source/tools svn: libraries: darwin-arm64: - branch: trunk + branch: tags/blender-3.0-release commit_id: HEAD path: lib/darwin_arm64 darwin-x86_64: - branch: trunk + branch: tags/blender-3.0-release commit_id: HEAD path: lib/darwin linux-x86_64: - branch: trunk + branch: tags/blender-3.0-release commit_id: HEAD path: lib/linux_centos7_x86_64 windows-amd64: - branch: trunk + branch: tags/blender-3.0-release commit_id: HEAD path: lib/win64_vc15 tests: - branch: trunk + branch: tags/blender-3.0-release commit_id: HEAD path: lib/tests benchmarks: From 10789c53290eb004ba2f254d200dcfff8dbf107e Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 27 Oct 2021 16:36:56 +0200 Subject: [PATCH 1321/1500] Cycles: tweak scrambling distance UI grouping, and improve tooltip --- intern/cycles/blender/addon/properties.py | 2 +- intern/cycles/blender/addon/ui.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index e5853529d1c..1e98e6d0a7c 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -346,7 +346,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): name="Scrambling Distance", default=1.0, min=0.0, max=1.0, - description="Lower values give faster rendering with GPU rendering and less noise with all devices at the cost of possible artifacts if set too low", + description="Lower values give faster rendering with GPU rendering and less noise with all devices at the cost of possible artifacts if set too low. Only works when not using adaptive sampling", ) preview_scrambling_distance: BoolProperty( name="Scrambling Distance viewport", diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index a0bf6c1758f..0c9179b4ccf 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -289,13 +289,16 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): col = layout.column(align=True) col.active = not (cscene.use_adaptive_sampling and cscene.use_preview_adaptive_sampling) col.prop(cscene, "sampling_pattern", text="Pattern") + + layout.separator() + col = layout.column(align=True) col.active = not (cscene.use_adaptive_sampling and cscene.use_preview_adaptive_sampling) - col.prop(cscene, "scrambling_distance", text="Scrambling Distance Strength") - col.prop(cscene, "adaptive_scrambling_distance", text="Adaptive Scrambling Distance") - col = layout.column(align=True) - col.active = not cscene.use_preview_adaptive_sampling - col.prop(cscene, "preview_scrambling_distance", text="Viewport Scrambling Distance") + col.prop(cscene, "scrambling_distance", text="Scrambling Distance") + col.prop(cscene, "adaptive_scrambling_distance", text="Adaptive") + sub = col.row(align=True) + sub.active = not cscene.use_preview_adaptive_sampling + sub.prop(cscene, "preview_scrambling_distance", text="Viewport") layout.separator() From b02c8a40ffbc835ff12f9e7adf6a2f935caa2f6e Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 27 Oct 2021 21:31:08 +0200 Subject: [PATCH 1322/1500] Fix broken Python API doc generation after addition of selected_ids --- doc/python_api/sphinx_doc_gen.py | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index afc84834dd1..903136ea9f2 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1108,6 +1108,7 @@ context_type_map = { "selected_editable_keyframes": ("Keyframe", True), "selected_editable_objects": ("Object", True), "selected_editable_sequences": ("Sequence", True), + "selected_ids": ("ID", True), "selected_files": ("FileSelectEntry", True), "selected_nla_strips": ("NlaStrip", True), "selected_movieclip_tracks": ("MovieTrackingTrack", True), From e4a5fd4298b5443aa572e0ae066e9c150b661529 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 27 Oct 2021 21:31:08 +0200 Subject: [PATCH 1323/1500] Fix broken Python API doc generation after addition of selected_ids --- doc/python_api/sphinx_doc_gen.py | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index afc84834dd1..903136ea9f2 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1108,6 +1108,7 @@ context_type_map = { "selected_editable_keyframes": ("Keyframe", True), "selected_editable_objects": ("Object", True), "selected_editable_sequences": ("Sequence", True), + "selected_ids": ("ID", True), "selected_files": ("FileSelectEntry", True), "selected_nla_strips": ("NlaStrip", True), "selected_movieclip_tracks": ("MovieTrackingTrack", True), From 8f02de3de7b77af82d75b9b10e5d0fa20034650c Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 27 Oct 2021 18:11:47 -0300 Subject: [PATCH 1324/1500] Cleanup: remove redundant variable `free_tooltip` is no longer needed. --- source/blender/windowmanager/intern/wm_dragdrop.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 37a101cc31d..b9f0e09d106 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -819,11 +819,9 @@ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const int iconsize = UI_DPI_ICON_SIZE; int padding = 4 * UI_DPI_FAC; - const char *tooltip = NULL; - bool free_tooltip = false; + char *tooltip = NULL; if (drag->active_dropbox) { tooltip = dropbox_tooltip(C, drag, xy, drag->active_dropbox); - free_tooltip = true; } if (!tooltip && !drag->disabled_info) { @@ -855,9 +853,7 @@ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const if (tooltip) { wm_drop_operator_draw(tooltip, x, y); - if (free_tooltip) { - MEM_freeN((void *)tooltip); - } + MEM_freeN(tooltip); } else if (drag->disabled_info) { wm_drop_redalert_draw(drag->disabled_info, x, y); From b69195dd1398bea4aca5fd5ea633991fd05bee9d Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 27 Oct 2021 19:01:12 -0300 Subject: [PATCH 1325/1500] Fix memory leak in cursor snap deactivation Missed in rB32cc9ff03746 --- source/blender/editors/space_view3d/view3d_cursor_snap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 937ac98acb8..75d2ff8b1ce 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -923,6 +923,7 @@ void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state) SnapStateIntern *state_intern = STATE_INTERN_GET(state); BLI_remlink(&data_intern->state_intern, state_intern); + MEM_freeN(state_intern); if (BLI_listbase_is_empty(&data_intern->state_intern)) { v3d_cursor_snap_free(); } From b838eaf2b960af5877d77eed1ef1ad4ede1071f0 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 27 Oct 2021 19:03:14 -0300 Subject: [PATCH 1326/1500] Cleanup: remove 'ED_view3d_cursor_snap_exit' The callers of `ED_view3d_cursor_snap_active` that must handle the snapping of the cursor. Forcing release at the end can hide leaks. --- source/blender/editors/include/ED_view3d.h | 1 - .../editors/space_view3d/view3d_cursor_snap.c | 13 ++++--------- source/blender/windowmanager/intern/wm_init_exit.c | 1 - 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index a0c733b2ebf..6d20044d8cf 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -304,7 +304,6 @@ void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d, const uchar color_line[4], const uchar color_point[4], const short snap_elem_type); -void ED_view3d_cursor_snap_exit(void); /* view3d_iterators.c */ diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 75d2ff8b1ce..e8f1e011364 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -883,16 +883,16 @@ static void v3d_cursor_snap_activate(void) static void v3d_cursor_snap_free(void) { SnapCursorDataIntern *data_intern = &g_data_intern; - if (data_intern->handle && G_MAIN->wm.first) { - WM_paint_cursor_end(data_intern->handle); + if (data_intern->handle) { + if (G_MAIN->wm.first) { + WM_paint_cursor_end(data_intern->handle); + } data_intern->handle = NULL; } if (data_intern->snap_context_v3d) { ED_transform_snap_object_context_destroy(data_intern->snap_context_v3d); data_intern->snap_context_v3d = NULL; } - - BLI_freelistN(&data_intern->state_intern); } void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state) @@ -974,8 +974,3 @@ struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(Scene *scene) v3d_cursor_snap_context_ensure(scene); return data_intern->snap_context_v3d; } - -void ED_view3d_cursor_snap_exit(void) -{ - v3d_cursor_snap_free(); -} diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index d0693d37ef4..c382af03c4a 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -548,7 +548,6 @@ void WM_exit_ex(bContext *C, const bool do_python) ED_preview_free_dbase(); /* frees a Main dbase, before BKE_blender_free! */ ED_assetlist_storage_exit(); - ED_view3d_cursor_snap_exit(); if (wm) { /* Before BKE_blender_free! - since the ListBases get freed there. */ From 16f468fb140770a9ffa277860efc8f2fad8183e5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 28 Oct 2021 14:07:12 +1100 Subject: [PATCH 1327/1500] Fix T92514: Edit-mode bone snap to cursor uses the center of the bone Regression in 2bcf93bbbeb9e32f680c37a1e0054ff16cb00ef0. --- source/blender/editors/space_view3d/view3d_snap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 55ec6652495..67b61ed77d8 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -615,7 +615,7 @@ static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op) const float *snap_target_global = scene->cursor.location; const int pivot_point = scene->toolsettings->transform_pivot_point; - if (snap_selected_to_location(C, snap_target_global, pivot_point, use_offset, true)) { + if (snap_selected_to_location(C, snap_target_global, use_offset, pivot_point, true)) { return OPERATOR_CANCELLED; } return OPERATOR_FINISHED; From e1fb7740f8866cf2179783c2b13c1d15b3a0e6ec Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 28 Oct 2021 14:26:37 +1100 Subject: [PATCH 1328/1500] Cleanup: use sections for key-map definition --- .../keyconfig/keymap_data/blender_default.py | 350 ++++++++++-------- 1 file changed, 196 insertions(+), 154 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 3601da03cd4..fbc85b2164b 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -208,7 +208,6 @@ class Params: # ------------------------------------------------------------------------------ # Constants - # Physical layout. NUMBERS_1 = ('ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE', 'ZERO') # Numeric order. @@ -846,8 +845,156 @@ def km_user_interface(_params): # ------------------------------------------------------------------------------ -# Editors +# Shared Between Editors (Mask, Time-Line) +def km_mask_editing(params): + items = [] + keymap = ( + "Mask Editing", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + if params.select_mouse == 'RIGHTMOUSE': + # mask.slide_point performs mostly the same function, so for the left + # click select keymap it's fine to have the context menu instead. + items.extend([ + ("mask.select", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, + {"properties": [("deselect_all", not params.legacy)]}), + ("transform.translate", {"type": 'EVT_TWEAK_R', "value": 'ANY'}, None), + ]) + + items.extend([ + ("mask.new", {"type": 'N', "value": 'PRESS', "alt": True}, None), + op_menu("MASK_MT_add", {"type": 'A', "value": 'PRESS', "shift": True}), + *_template_items_proportional_editing( + params, connected=False, toggle_data_path='tool_settings.use_proportional_edit_mask'), + ("mask.add_vertex_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None), + ("mask.add_feather_vertex_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None), + ("mask.delete", {"type": 'X', "value": 'PRESS'}, None), + ("mask.delete", {"type": 'DEL', "value": 'PRESS'}, None), + ("mask.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, + {"properties": [("toggle", True)]}), + *_template_items_select_actions(params, "mask.select_all"), + ("mask.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), + ("mask.select_linked_pick", {"type": 'L', "value": 'PRESS'}, + {"properties": [("deselect", False)]}), + ("mask.select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True}, + {"properties": [("deselect", True)]}), + ("mask.select_box", {"type": 'B', "value": 'PRESS'}, None), + ("mask.select_circle", {"type": 'C', "value": 'PRESS'}, None), + ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, + {"properties": [("mode", 'ADD')]}), + ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, + {"properties": [("mode", 'SUB')]}), + ("mask.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), + ("mask.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), + *_template_items_hide_reveal_actions("mask.hide_view_set", "mask.hide_view_clear"), + ("clip.select", {"type": params.select_mouse, "value": 'PRESS', "ctrl": True}, None), + ("mask.cyclic_toggle", {"type": 'C', "value": 'PRESS', "alt": True}, None), + ("mask.slide_point", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), + ("mask.slide_spline_curvature", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), + ("mask.handle_type_set", {"type": 'V', "value": 'PRESS'}, None), + ("mask.normals_make_consistent", + {"type": 'N', "value": 'PRESS', "ctrl" if params.legacy else "shift": True}, None), + ("mask.parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None), + ("mask.parent_clear", {"type": 'P', "value": 'PRESS', "alt": True}, None), + ("mask.shape_key_insert", {"type": 'I', "value": 'PRESS'}, None), + ("mask.shape_key_clear", {"type": 'I', "value": 'PRESS', "alt": True}, None), + ("mask.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), + ("mask.copy_splines", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), + ("mask.paste_splines", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), + ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), + ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), + ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), + ("transform.tosphere", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None), + ("transform.shear", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), + ("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True}, + {"properties": [("mode", 'MASK_SHRINKFATTEN')]}), + ]) + + # 3D cursor + if params.cursor_tweak_event: + items.extend([ + ("uv.cursor_set", params.cursor_set_event, None), + ("transform.translate", params.cursor_tweak_event, + {"properties": [("release_confirm", True), ("cursor_transform", True)]}), + ]) + else: + items.extend([ + ("uv.cursor_set", params.cursor_set_event, None), + ]) + + return keymap + + +def km_markers(params): + items = [] + keymap = ( + "Markers", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + items.extend([ + ("marker.add", {"type": 'M', "value": 'PRESS'}, None), + ("marker.move", {"type": params.select_tweak, "value": 'ANY'}, + {"properties": [("tweak", True)]}), + ("marker.duplicate", {"type": 'D', "value": 'PRESS', "shift": True}, None), + ("marker.select", {"type": params.select_mouse, "value": 'PRESS'}, None), + ("marker.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, + {"properties": [("extend", True)]}), + ("marker.select", {"type": params.select_mouse, "value": 'PRESS', "ctrl": True}, + {"properties": [("camera", True)]}), + ("marker.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "ctrl": True}, + {"properties": [("extend", True), ("camera", True)]}), + ("marker.select_box", {"type": params.select_tweak, "value": 'ANY'}, + {"properties": [("tweak", True)]}), + ("marker.select_box", {"type": 'B', "value": 'PRESS'}, None), + *_template_items_select_actions(params, "marker.select_all"), + ("marker.delete", {"type": 'X', "value": 'PRESS'}, None), + ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None), + ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), + ("marker.move", {"type": 'G', "value": 'PRESS'}, None), + ("marker.camera_bind", {"type": 'B', "value": 'PRESS', "ctrl": True}, None), + ]) + + return keymap + + +def km_time_scrub(_params): + items = [] + keymap = ( + "Time Scrub", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + items.extend([ + ("anim.change_frame", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), + ]) + + return keymap + + +def km_time_scrub_clip(_params): + items = [] + keymap = ( + "Clip Time Scrub", + {"space_type": 'CLIP_EDITOR', "region_type": 'PREVIEW'}, + {"items": items}, + ) + + items.extend([ + ("clip.change_frame", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), + ]) + + return keymap + + +# ------------------------------------------------------------------------------ +# Editor (Property Editor) def km_property_editor(_params): items = [] @@ -894,6 +1041,9 @@ def km_property_editor(_params): return keymap +# ------------------------------------------------------------------------------ +# Editor (Outliner) + def km_outliner(params): items = [] keymap = ( @@ -983,6 +1133,9 @@ def km_outliner(params): return keymap +# ------------------------------------------------------------------------------ +# Editor (UV Editor) + def km_uv_editor(params): items = [] keymap = ( @@ -1114,6 +1267,9 @@ def km_uv_editor(params): return keymap +# ------------------------------------------------------------------------------ +# Editor (3D View) + # 3D View: all regions. def km_view3d_generic(_params): items = [] @@ -1478,151 +1634,8 @@ def km_view3d(params): return keymap -def km_mask_editing(params): - items = [] - keymap = ( - "Mask Editing", - {"space_type": 'EMPTY', "region_type": 'WINDOW'}, - {"items": items}, - ) - - if params.select_mouse == 'RIGHTMOUSE': - # mask.slide_point performs mostly the same function, so for the left - # click select keymap it's fine to have the context menu instead. - items.extend([ - ("mask.select", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, - {"properties": [("deselect_all", not params.legacy)]}), - ("transform.translate", {"type": 'EVT_TWEAK_R', "value": 'ANY'}, None), - ]) - - items.extend([ - ("mask.new", {"type": 'N', "value": 'PRESS', "alt": True}, None), - op_menu("MASK_MT_add", {"type": 'A', "value": 'PRESS', "shift": True}), - *_template_items_proportional_editing( - params, connected=False, toggle_data_path='tool_settings.use_proportional_edit_mask'), - ("mask.add_vertex_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None), - ("mask.add_feather_vertex_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None), - ("mask.delete", {"type": 'X', "value": 'PRESS'}, None), - ("mask.delete", {"type": 'DEL', "value": 'PRESS'}, None), - ("mask.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, - {"properties": [("toggle", True)]}), - *_template_items_select_actions(params, "mask.select_all"), - ("mask.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), - ("mask.select_linked_pick", {"type": 'L', "value": 'PRESS'}, - {"properties": [("deselect", False)]}), - ("mask.select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True}, - {"properties": [("deselect", True)]}), - ("mask.select_box", {"type": 'B', "value": 'PRESS'}, None), - ("mask.select_circle", {"type": 'C', "value": 'PRESS'}, None), - ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, - {"properties": [("mode", 'ADD')]}), - ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, - {"properties": [("mode", 'SUB')]}), - ("mask.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), - ("mask.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), - *_template_items_hide_reveal_actions("mask.hide_view_set", "mask.hide_view_clear"), - ("clip.select", {"type": params.select_mouse, "value": 'PRESS', "ctrl": True}, None), - ("mask.cyclic_toggle", {"type": 'C', "value": 'PRESS', "alt": True}, None), - ("mask.slide_point", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), - ("mask.slide_spline_curvature", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), - ("mask.handle_type_set", {"type": 'V', "value": 'PRESS'}, None), - ("mask.normals_make_consistent", - {"type": 'N', "value": 'PRESS', "ctrl" if params.legacy else "shift": True}, None), - ("mask.parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None), - ("mask.parent_clear", {"type": 'P', "value": 'PRESS', "alt": True}, None), - ("mask.shape_key_insert", {"type": 'I', "value": 'PRESS'}, None), - ("mask.shape_key_clear", {"type": 'I', "value": 'PRESS', "alt": True}, None), - ("mask.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), - ("mask.copy_splines", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), - ("mask.paste_splines", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), - ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), - ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), - ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), - ("transform.tosphere", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None), - ("transform.shear", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), - ("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True}, - {"properties": [("mode", 'MASK_SHRINKFATTEN')]}), - ]) - - # 3D cursor - if params.cursor_tweak_event: - items.extend([ - ("uv.cursor_set", params.cursor_set_event, None), - ("transform.translate", params.cursor_tweak_event, - {"properties": [("release_confirm", True), ("cursor_transform", True)]}), - ]) - else: - items.extend([ - ("uv.cursor_set", params.cursor_set_event, None), - ]) - - return keymap - - -def km_markers(params): - items = [] - keymap = ( - "Markers", - {"space_type": 'EMPTY', "region_type": 'WINDOW'}, - {"items": items}, - ) - - items.extend([ - ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.move", {"type": params.select_tweak, "value": 'ANY'}, - {"properties": [("tweak", True)]}), - ("marker.duplicate", {"type": 'D', "value": 'PRESS', "shift": True}, None), - ("marker.select", {"type": params.select_mouse, "value": 'PRESS'}, None), - ("marker.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, - {"properties": [("extend", True)]}), - ("marker.select", {"type": params.select_mouse, "value": 'PRESS', "ctrl": True}, - {"properties": [("camera", True)]}), - ("marker.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "ctrl": True}, - {"properties": [("extend", True), ("camera", True)]}), - ("marker.select_box", {"type": params.select_tweak, "value": 'ANY'}, - {"properties": [("tweak", True)]}), - ("marker.select_box", {"type": 'B', "value": 'PRESS'}, None), - *_template_items_select_actions(params, "marker.select_all"), - ("marker.delete", {"type": 'X', "value": 'PRESS'}, None), - ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), - ("marker.move", {"type": 'G', "value": 'PRESS'}, None), - ("marker.camera_bind", {"type": 'B', "value": 'PRESS', "ctrl": True}, None), - ]) - - return keymap - - -def km_time_scrub(_params): - items = [] - keymap = ( - "Time Scrub", - {"space_type": 'EMPTY', "region_type": 'WINDOW'}, - {"items": items}, - ) - - items.extend([ - ("anim.change_frame", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), - ]) - - return keymap - - -def km_time_scrub_clip(_params): - items = [] - keymap = ( - "Clip Time Scrub", - {"space_type": 'CLIP_EDITOR', "region_type": 'PREVIEW'}, - {"items": items}, - ) - - items.extend([ - ("clip.change_frame", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), - ]) - - return keymap - +# ------------------------------------------------------------------------------ +# Editor (Graph Editor) def km_graph_editor_generic(_params): items = [] @@ -1777,6 +1790,9 @@ def km_graph_editor(params): return keymap +# ------------------------------------------------------------------------------ +# Editor (Image) + def km_image_generic(params): items = [] keymap = ( @@ -1899,6 +1915,9 @@ def km_image(params): return keymap +# ------------------------------------------------------------------------------ +# Editor (Node) + def km_node_generic(_params): items = [] keymap = ( @@ -2072,6 +2091,9 @@ def km_node_editor(params): return keymap +# ------------------------------------------------------------------------------ +# Editor (Info) + def km_info(params): items = [] keymap = ( @@ -2098,6 +2120,9 @@ def km_info(params): return keymap +# ------------------------------------------------------------------------------ +# Editor (File Browser) + def km_file_browser(params): items = [] keymap = ( @@ -2165,7 +2190,7 @@ def km_file_browser_main(params): if not params.use_file_single_click: items.extend([ ("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, - {"properties": [("open", True), ("deselect_all", not params.legacy)]}), + {"properties": [("open", True), ("deselect_all", not params.legacy)]}), ]) items.extend([ @@ -2248,6 +2273,9 @@ def km_file_browser_buttons(_params): return keymap +# ------------------------------------------------------------------------------ +# Editor (Dope Sheet) + def km_dopesheet_generic(_params): items = [] keymap = ( @@ -2382,6 +2410,9 @@ def km_dopesheet(params): return keymap +# ------------------------------------------------------------------------------ +# Editor (NLA) + def km_nla_generic(_params): items = [] keymap = ( @@ -2511,6 +2542,9 @@ def km_nla_editor(params): return keymap +# ------------------------------------------------------------------------------ +# Editor (Text) + def km_text_generic(_params): items = [] keymap = ( @@ -2672,6 +2706,9 @@ def km_text(params): return keymap +# ------------------------------------------------------------------------------ +# Editor (Sequencer) + def km_sequencercommon(params): items = [] keymap = ( @@ -2893,6 +2930,9 @@ def km_sequencerpreview(params): return keymap +# ------------------------------------------------------------------------------ +# Editor (Console) + def km_console(_params): items = [] keymap = ( @@ -2958,6 +2998,9 @@ def km_console(_params): return keymap +# ------------------------------------------------------------------------------ +# Editor (Clip) + def km_clip(_params): items = [] keymap = ( @@ -3187,6 +3230,9 @@ def km_clip_dopesheet_editor(_params): return keymap +# ------------------------------------------------------------------------------ +# Editor (Spreadsheet) + def km_spreadsheet_generic(_params): items = [] keymap = ( @@ -3208,7 +3254,6 @@ def km_spreadsheet_generic(_params): # ------------------------------------------------------------------------------ # Animation - def km_frames(params): items = [] keymap = ( @@ -3372,8 +3417,7 @@ def km_animation_channels(params): # ------------------------------------------------------------------------------ -# Modes - +# Object Modes def km_grease_pencil(params): items = [] @@ -4686,7 +4730,7 @@ def _template_sequencer_preview_select(*, type, value, legacy): (("center",), ("ctrl",)), # TODO: # (("enumerate",), ("alt",)), - (("toggle", "center"), ("shift", "ctrl")), + (("toggle", "center"), ("shift", "ctrl")), # (("center", "enumerate"), ("ctrl", "alt")), # (("toggle", "enumerate"), ("shift", "alt")), # (("toggle", "center", "enumerate"), ("shift", "ctrl", "alt")), @@ -5486,7 +5530,6 @@ def km_object_non_modal(params): # ------------------------------------------------------------------------------ # Modal Maps and Gizmos - def km_eyedropper_modal_map(_params): items = [] keymap = ( @@ -6164,7 +6207,6 @@ def km_popup_toolbar(_params): # # Named are auto-generated based on the tool name and it's toolbar. - def km_generic_tool_annotate(params): return ( "Generic Tool: Annotate", From f0d20198b290338a9f58392f98ee43957b0bad61 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 28 Oct 2021 14:31:17 +1100 Subject: [PATCH 1329/1500] Sequencer: support basic selection & delete from previews Expose select & strip menus and shortcuts for sequencer preview. --- .../keyconfig/keymap_data/blender_default.py | 9 + .../scripts/startup/bl_ui/space_sequencer.py | 189 +++++++++++------- .../editors/space_sequencer/sequencer_edit.c | 16 +- .../space_sequencer/sequencer_select.c | 121 ++++++++--- source/blender/makesdna/DNA_sequence_types.h | 6 +- source/blender/sequencer/SEQ_iterator.h | 5 + source/blender/sequencer/intern/iterator.c | 26 +++ 7 files changed, 267 insertions(+), 105 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index fbc85b2164b..3a1ba3e22ff 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2868,7 +2868,10 @@ def km_sequencerpreview(params): value=params.select_mouse_value_fallback, legacy=params.legacy, ), + *_template_items_select_actions(params, "sequencer.select_all"), + ("sequencer.select_box", {"type": 'B', "value": 'PRESS'}, None), + # View. ("sequencer.view_all_preview", {"type": 'HOME', "value": 'PRESS'}, None), ("sequencer.view_all_preview", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None), ("sequencer.view_ghost_border", {"type": 'O', "value": 'PRESS'}, None), @@ -2886,6 +2889,8 @@ def km_sequencerpreview(params): {"properties": [("ratio", 0.25)]}), ("sequencer.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS'}, {"properties": [("ratio", 0.125)]}), + + # Edit. ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), op_tool_optional( ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), @@ -2902,6 +2907,10 @@ def km_sequencerpreview(params): {"properties": [("property", 'SCALE')]}), ("sequencer.strip_transform_clear", {"type": 'R', "alt": True, "value": 'PRESS'}, {"properties": [("property", 'ROTATION')]}), + + ("sequencer.delete", {"type": 'X', "value": 'PRESS'}, None), + ("sequencer.delete", {"type": 'DEL', "value": 'PRESS'}, None), + *_template_items_context_menu("SEQUENCER_MT_preview_context_menu", params.context_menu_event), ]) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index cdfe4f4068f..120b2d7c13a 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -37,6 +37,14 @@ from bl_ui.space_toolsystem_common import ( from rna_prop_ui import PropertyPanel +def _space_view_types(st): + view_type = st.view_type + return ( + view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}, + view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}, + ) + + def selected_sequences_len(context): selected_sequences = getattr(context, "selected_sequences", None) if selected_sequences is None: @@ -228,15 +236,17 @@ class SEQUENCER_MT_editor_menus(Menu): def draw(self, context): layout = self.layout st = context.space_data + has_sequencer, _has_preview = _space_view_types(st) layout.menu("SEQUENCER_MT_view") + layout.menu("SEQUENCER_MT_select") - if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: - layout.menu("SEQUENCER_MT_select") + if has_sequencer: if st.show_markers: layout.menu("SEQUENCER_MT_marker") layout.menu("SEQUENCER_MT_add") - layout.menu("SEQUENCER_MT_strip") + + layout.menu("SEQUENCER_MT_strip") layout.menu("SEQUENCER_MT_image") @@ -561,8 +571,14 @@ class SEQUENCER_MT_select_linked(Menu): class SEQUENCER_MT_select(Menu): bl_label = "Select" - def draw(self, _context): + def draw(self, context): layout = self.layout + st = context.space_data + has_sequencer, has_preview = _space_view_types(st) + + # FIXME: this doesn't work for both preview + window region. + if has_preview: + layout.operator_context = 'INVOKE_REGION_PREVIEW' layout.operator("sequencer.select_all", text="All").action = 'SELECT' layout.operator("sequencer.select_all", text="None").action = 'DESELECT' @@ -571,17 +587,20 @@ class SEQUENCER_MT_select(Menu): layout.separator() layout.operator("sequencer.select_box", text="Box Select") - props = layout.operator("sequencer.select_box", text="Box Select (Include Handles)") - props.include_handles = True + if has_sequencer: + props = layout.operator("sequencer.select_box", text="Box Select (Include Handles)") + props.include_handles = True layout.separator() - layout.operator_menu_enum("sequencer.select_side_of_frame", "side", text="Side of Frame...") - layout.menu("SEQUENCER_MT_select_handle", text="Handle") - layout.menu("SEQUENCER_MT_select_channel", text="Channel") - layout.menu("SEQUENCER_MT_select_linked", text="Linked") + if has_sequencer: + layout.operator_menu_enum("sequencer.select_side_of_frame", "side", text="Side of Frame...") + layout.menu("SEQUENCER_MT_select_handle", text="Handle") + layout.menu("SEQUENCER_MT_select_channel", text="Channel") + layout.menu("SEQUENCER_MT_select_linked", text="Linked") + + layout.separator() - layout.separator() layout.operator_menu_enum("sequencer.select_grouped", "type", text="Grouped") @@ -792,23 +811,40 @@ class SEQUENCER_MT_add_effect(Menu): class SEQUENCER_MT_strip_transform(Menu): bl_label = "Transform" - def draw(self, _context): + def draw(self, context): layout = self.layout + st = context.space_data + has_sequencer, has_preview = _space_view_types(st) - layout.operator("transform.seq_slide", text="Move") - layout.operator("transform.transform", text="Move/Extend from Current Frame").mode = 'TIME_EXTEND' - layout.operator("sequencer.slip", text="Slip Strip Contents") + if has_preview: + layout.operator_context = 'INVOKE_REGION_PREVIEW' + else: + layout.operator_context = 'INVOKE_REGION_WIN' - layout.separator() - layout.operator("sequencer.snap") - layout.operator("sequencer.offset_clear") + # FIXME: mixed preview/sequencer views. + if has_preview: + layout.operator("transform.translate", text="Move") + layout.operator("transform.rotate", text="Rotate") + layout.operator("transform.resize", text="Scale") + else: + layout.operator("transform.seq_slide", text="Move") + layout.operator("transform.transform", text="Move/Extend from Current Frame").mode = 'TIME_EXTEND' + layout.operator("sequencer.slip", text="Slip Strip Contents") - layout.separator() - layout.operator_menu_enum("sequencer.swap", "side") + # TODO (for preview) + if has_sequencer: + layout.separator() + layout.operator("sequencer.snap") + layout.operator("sequencer.offset_clear") - layout.separator() - layout.operator("sequencer.gap_remove").all = False - layout.operator("sequencer.gap_insert") + layout.separator() + + if has_sequencer: + layout.operator_menu_enum("sequencer.swap", "side") + + layout.separator() + layout.operator("sequencer.gap_remove").all = False + layout.operator("sequencer.gap_insert") class SEQUENCER_MT_strip_input(Menu): @@ -878,68 +914,79 @@ class SEQUENCER_MT_strip(Menu): def draw(self, context): layout = self.layout + st = context.space_data + has_sequencer, has_preview = _space_view_types(st) - layout.operator_context = 'INVOKE_REGION_WIN' + # FIXME: this doesn't work for both preview + window region. + if has_preview: + layout.operator_context = 'INVOKE_REGION_PREVIEW' + else: + layout.operator_context = 'INVOKE_REGION_WIN' - layout.separator() layout.menu("SEQUENCER_MT_strip_transform") - layout.separator() - layout.operator("sequencer.split", text="Split").type = 'SOFT' - layout.operator("sequencer.split", text="Hold Split").type = 'HARD' - layout.separator() - layout.operator("sequencer.copy", text="Copy") - layout.operator("sequencer.paste", text="Paste") - layout.operator("sequencer.duplicate_move") + if has_sequencer: + + layout.operator("sequencer.split", text="Split").type = 'SOFT' + layout.operator("sequencer.split", text="Hold Split").type = 'HARD' + layout.separator() + + if has_sequencer: + layout.operator("sequencer.copy", text="Copy") + layout.operator("sequencer.paste", text="Paste") + layout.operator("sequencer.duplicate_move") + layout.operator("sequencer.delete", text="Delete") strip = context.active_sequence_strip - if strip: - strip_type = strip.type + if has_sequencer: + if strip: + strip_type = strip.type - if strip_type != 'SOUND': - layout.separator() - layout.operator_menu_enum("sequencer.strip_modifier_add", "type", text="Add Modifier") - layout.operator("sequencer.strip_modifier_copy", text="Copy Modifiers to Selection") + if strip_type != 'SOUND': + layout.separator() + layout.operator_menu_enum("sequencer.strip_modifier_add", "type", text="Add Modifier") + layout.operator("sequencer.strip_modifier_copy", text="Copy Modifiers to Selection") - if strip_type in { - 'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER', - 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'WIPE', 'GLOW', - 'TRANSFORM', 'COLOR', 'SPEED', 'MULTICAM', 'ADJUSTMENT', - 'GAUSSIAN_BLUR', - }: - layout.separator() - layout.menu("SEQUENCER_MT_strip_effect") - elif strip_type == 'MOVIE': - layout.separator() - layout.menu("SEQUENCER_MT_strip_movie") - elif strip_type == 'IMAGE': - layout.separator() - layout.operator("sequencer.rendersize") - layout.operator("sequencer.images_separate") - elif strip_type == 'TEXT': - layout.separator() - layout.menu("SEQUENCER_MT_strip_effect") - elif strip_type == 'META': - layout.separator() - layout.operator("sequencer.meta_make") - layout.operator("sequencer.meta_separate") - layout.operator("sequencer.meta_toggle", text="Toggle Meta") - if strip_type != 'META': - layout.separator() - layout.operator("sequencer.meta_make") - layout.operator("sequencer.meta_toggle", text="Toggle Meta") + if strip_type in { + 'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER', + 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'WIPE', 'GLOW', + 'TRANSFORM', 'COLOR', 'SPEED', 'MULTICAM', 'ADJUSTMENT', + 'GAUSSIAN_BLUR', + }: + layout.separator() + layout.menu("SEQUENCER_MT_strip_effect") + elif strip_type == 'MOVIE': + layout.separator() + layout.menu("SEQUENCER_MT_strip_movie") + elif strip_type == 'IMAGE': + layout.separator() + layout.operator("sequencer.rendersize") + layout.operator("sequencer.images_separate") + elif strip_type == 'TEXT': + layout.separator() + layout.menu("SEQUENCER_MT_strip_effect") + elif strip_type == 'META': + layout.separator() + layout.operator("sequencer.meta_make") + layout.operator("sequencer.meta_separate") + layout.operator("sequencer.meta_toggle", text="Toggle Meta") + if strip_type != 'META': + layout.separator() + layout.operator("sequencer.meta_make") + layout.operator("sequencer.meta_toggle", text="Toggle Meta") - layout.separator() - layout.menu("SEQUENCER_MT_color_tag_picker") + if has_sequencer: + layout.separator() + layout.menu("SEQUENCER_MT_color_tag_picker") - layout.separator() - layout.menu("SEQUENCER_MT_strip_lock_mute") + layout.separator() + layout.menu("SEQUENCER_MT_strip_lock_mute") - layout.separator() - layout.menu("SEQUENCER_MT_strip_input") + layout.separator() + layout.menu("SEQUENCER_MT_strip_input") class SEQUENCER_MT_image(Menu): diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 2a29125af19..8c70f4e3f7a 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -1709,16 +1709,24 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene); + ListBase *seqbasep = SEQ_active_seqbase_get(SEQ_editing_get(scene)); SEQ_prefetch_stop(scene); - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(seqbasep, scene->r.cfra, 0); + } + + LISTBASE_FOREACH (Sequence *, seq, seqbasep) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->flag & SELECT) { - SEQ_edit_flag_for_removal(scene, ed->seqbasep, seq); + SEQ_edit_flag_for_removal(scene, seqbasep, seq); } } - SEQ_edit_remove_flagged_sequences(scene, ed->seqbasep); + SEQ_edit_remove_flagged_sequences(scene, seqbasep); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); DEG_relations_tag_update(bmain); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 1cc7507e5f6..8a8a24f08ff 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -416,9 +416,17 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op) Editing *ed = SEQ_editing_get(scene); Sequence *seq; + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(ed->seqbasep, scene->r.cfra, 0); + } + if (action == SEL_TOGGLE) { action = SEL_SELECT; for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->flag & SEQ_ALLSEL) { action = SEL_DESELECT; break; @@ -427,6 +435,9 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } switch (action) { case SEL_SELECT: seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL); @@ -483,7 +494,15 @@ static int sequencer_select_inverse_exec(bContext *C, wmOperator *UNUSED(op)) Editing *ed = SEQ_editing_get(scene); Sequence *seq; + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(ed->seqbasep, scene->r.cfra, 0); + } + for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->flag & SELECT) { seq->flag &= ~SEQ_ALLSEL; } @@ -1748,11 +1767,17 @@ static const EnumPropertyItem sequencer_prop_select_grouped_types[] = { #define SEQ_CHANNEL_CHECK(_seq, _chan) (ELEM((_chan), 0, (_seq)->machine)) -static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_type(ListBase *seqbasep, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbasep) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) { seq->flag |= SELECT; changed = true; @@ -1762,12 +1787,18 @@ static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel return changed; } -static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_type_basic(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; const bool is_sound = SEQ_IS_SOUND(actseq); - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) { seq->flag |= SELECT; changed = true; @@ -1777,12 +1808,18 @@ static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int c return changed; } -static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_type_effect(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; const bool is_effect = SEQ_IS_EFFECT(actseq); - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && (is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq))) { seq->flag |= SELECT; @@ -1793,7 +1830,10 @@ static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int return changed; } -static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_data(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; const char *dir = actseq->strip ? actseq->strip->dir : NULL; @@ -1803,7 +1843,10 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } if (SEQ_HAS_PATH(actseq) && dir) { - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip && STREQ(seq->strip->dir, dir)) { seq->flag |= SELECT; @@ -1813,7 +1856,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } else if (actseq->type == SEQ_TYPE_SCENE) { Scene *sce = actseq->scene; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) { seq->flag |= SELECT; changed = true; @@ -1822,7 +1865,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } else if (actseq->type == SEQ_TYPE_MOVIECLIP) { MovieClip *clip = actseq->clip; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP && seq->clip == clip) { seq->flag |= SELECT; @@ -1832,7 +1875,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } else if (actseq->type == SEQ_TYPE_MASK) { struct Mask *mask = actseq->mask; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) { seq->flag |= SELECT; changed = true; @@ -1843,7 +1886,10 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel return changed; } -static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_effect(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; bool effects[SEQ_TYPE_MAX + 1]; @@ -1852,14 +1898,20 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann effects[i] = false; } - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) && ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) { effects[seq->type] = true; } } - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) { if (seq->seq1) { seq->seq1->flag |= SELECT; @@ -1877,11 +1929,14 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann return changed; } -static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq) +static bool select_grouped_time_overlap(ListBase *seqbase, const bool is_preview, Sequence *actseq) { bool changed = false; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) { seq->flag |= SELECT; changed = true; @@ -1910,12 +1965,11 @@ static void query_lower_channel_strips(Sequence *seq_reference, /* Select all strips within time range and with lower channel of initial selection. Then select * effect chains of these strips. */ -static bool select_grouped_effect_link(Editing *ed, +static bool select_grouped_effect_link(ListBase *seqbase, + const bool is_preview, Sequence *UNUSED(actseq), const int UNUSED(channel)) { - ListBase *seqbase = SEQ_active_seqbase_get(ed); - /* Get collection of strips. */ SeqCollection *collection = SEQ_query_selected_strips(seqbase); const int selected_strip_count = BLI_gset_len(collection->set); @@ -1928,6 +1982,9 @@ static bool select_grouped_effect_link(Editing *ed, /* Actual logic. */ Sequence *seq; SEQ_ITERATOR_FOREACH (seq, collection) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } seq->flag |= SELECT; } @@ -1943,9 +2000,17 @@ static bool select_grouped_effect_link(Editing *ed, static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); Sequence *actseq = SEQ_select_active_get(scene); + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(seqbase, scene->r.cfra, 0); + if (actseq && actseq->tmp_tag == false) { + actseq = NULL; + } + } + if (actseq == NULL) { BKE_report(op->reports, RPT_ERROR, "No active sequence!"); return OPERATOR_CANCELLED; @@ -1958,7 +2023,7 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) bool changed = false; if (!extend) { - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { seq->flag &= ~SELECT; changed = true; } @@ -1966,25 +2031,25 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) switch (type) { case SEQ_SELECT_GROUP_TYPE: - changed |= select_grouped_type(ed, actseq, channel); + changed |= select_grouped_type(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_TYPE_BASIC: - changed |= select_grouped_type_basic(ed, actseq, channel); + changed |= select_grouped_type_basic(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_TYPE_EFFECT: - changed |= select_grouped_type_effect(ed, actseq, channel); + changed |= select_grouped_type_effect(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_DATA: - changed |= select_grouped_data(ed, actseq, channel); + changed |= select_grouped_data(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_EFFECT: - changed |= select_grouped_effect(ed, actseq, channel); + changed |= select_grouped_effect(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_EFFECT_LINK: - changed |= select_grouped_effect_link(ed, actseq, channel); + changed |= select_grouped_effect_link(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_OVERLAP: - changed |= select_grouped_time_overlap(ed, actseq); + changed |= select_grouped_time_overlap(seqbase, is_preview, actseq); break; default: BLI_assert(0); diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index af01bb76680..fc23d3c69a3 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -179,7 +179,9 @@ typedef struct Sequence { /** Starting and ending points of the strip in the sequence. */ int startdisp, enddisp; float sat; - float mul, handsize; + float mul; + char tmp_tag; + char _pad[3]; short anim_preseek; /* UNUSED. */ /** Streamindex for movie or sound files with several streams. */ @@ -250,7 +252,7 @@ typedef struct Sequence { /* Multiview */ char views_format; - char _pad[3]; + char _pad1[3]; struct Stereo3dFormat *stereo3d_format; struct IDProperty *prop; diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index d2a47a13db3..4de7c09640b 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -104,6 +104,11 @@ void SEQ_query_strip_effect_chain(struct Sequence *seq_reference, SeqCollection *collection); void SEQ_filter_selected_strips(SeqCollection *collection); +/* Utilities to access these as tags. */ +int SEQ_query_rendered_strips_to_tag(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index a12a5cbdc61..68f632ddb28 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -520,3 +520,29 @@ void SEQ_filter_selected_strips(SeqCollection *collection) } } } + +static void seq_collection_to_tag(ListBase *seqbase, SeqCollection *collection) +{ + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + seq->tmp_tag = false; + } + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + seq->tmp_tag = true; + } +} + +/* Utilities to access these as tags. */ +int SEQ_query_rendered_strips_to_tag(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel) +{ + SeqCollection *collection = SEQ_query_rendered_strips( + seqbase, timeline_frame, displayed_channel); + + seq_collection_to_tag(seqbase, collection); + + const int len = SEQ_collection_len(collection); + SEQ_collection_free(collection); + return len; +} From 5aeecc0a290160aa5f7d30ead9f637c380ea2342 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 28 Oct 2021 17:11:23 +1100 Subject: [PATCH 1330/1500] Fix fallback tools for the sequence editor Only regions with gizmos were checking for fallback tools. --- .../startup/bl_ui/space_toolsystem_toolbar.py | 1 + source/blender/editors/screen/area.c | 2 +- source/blender/windowmanager/WM_api.h | 8 ++-- .../windowmanager/intern/wm_event_system.c | 46 ++++++++----------- 4 files changed, 25 insertions(+), 32 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 721c6bdb99c..bf5372f5ecf 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2462,6 +2462,7 @@ class _defs_sequencer_generic: widget=None, keymap="Sequencer Tool: Blade", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 4309359bd91..80c14371c16 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1744,7 +1744,7 @@ static void ed_default_handlers( if (flag & ED_KEYMAP_TOOL) { if (flag & ED_KEYMAP_GIZMO) { WM_event_add_keymap_handler_dynamic( - ®ion->handlers, WM_event_get_keymap_from_toolsystem_fallback, area); + ®ion->handlers, WM_event_get_keymap_from_toolsystem_with_gizmos, area); } else { WM_event_add_keymap_handler_dynamic( diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 59e03f472f0..112d76a3e65 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -276,10 +276,10 @@ typedef void(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm, struct wmEventHandler_Keymap *handler, struct wmEventHandler_KeymapResult *km_result); -void WM_event_get_keymap_from_toolsystem_fallback(struct wmWindowManager *wm, - struct wmWindow *win, - struct wmEventHandler_Keymap *handler, - wmEventHandler_KeymapResult *km_result); +void WM_event_get_keymap_from_toolsystem_with_gizmos(struct wmWindowManager *wm, + struct wmWindow *win, + struct wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result); void WM_event_get_keymap_from_toolsystem(struct wmWindowManager *wm, struct wmWindow *win, struct wmEventHandler_Keymap *handler, diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index a426841b49c..6092689138d 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -4009,10 +4009,12 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap * * Follow #wmEventHandler_KeymapDynamicFn signature. */ -void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, - wmWindow *win, - wmEventHandler_Keymap *handler, - wmEventHandler_KeymapResult *km_result) +void wm_event_get_keymap_from_toolsystem_ex(wmWindowManager *wm, + wmWindow *win, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result, + /* Extra arguments. */ + const bool with_gizmos) { memset(km_result, 0x0, sizeof(*km_result)); @@ -4045,7 +4047,8 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, if (tref_rt->flag & TOOLREF_FLAG_FALLBACK_KEYMAP) { add_keymap = true; } - if (tref_rt->gizmo_group[0] != '\0') { + + if (with_gizmos && (tref_rt->gizmo_group[0] != '\0')) { wmGizmoMap *gzmap = NULL; wmGizmoGroup *gzgroup = NULL; LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { @@ -4069,6 +4072,7 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, } } } + if (add_keymap) { keymap_id_list[keymap_id_list_len++] = tref_rt->keymap_fallback; } @@ -4096,32 +4100,20 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, } } +void WM_event_get_keymap_from_toolsystem_with_gizmos(wmWindowManager *wm, + wmWindow *win, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result) +{ + wm_event_get_keymap_from_toolsystem_ex(wm, win, handler, km_result, true); +} + void WM_event_get_keymap_from_toolsystem(wmWindowManager *wm, - wmWindow *UNUSED(win), + wmWindow *win, wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result) { - memset(km_result, 0x0, sizeof(*km_result)); - - ScrArea *area = handler->dynamic.user_data; - handler->keymap_tool = NULL; - bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL; - if (tref_rt && tref_rt->keymap[0]) { - const char *keymap_id = tref_rt->keymap; - { - wmKeyMap *km = WM_keymap_list_find_spaceid_or_empty( - &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW); - /* We shouldn't use keymaps from unrelated spaces. */ - if (km != NULL) { - handler->keymap_tool = area->runtime.tool; - km_result->keymaps[km_result->keymaps_len++] = km; - } - else { - printf( - "Keymap: '%s' not found for tool '%s'\n", tref_rt->keymap, area->runtime.tool->idname); - } - } - } + wm_event_get_keymap_from_toolsystem_ex(wm, win, handler, km_result, false); } struct wmEventHandler_Keymap *WM_event_add_keymap_handler_dynamic( From 2a2d873124111b5fcbc2c3c59f73fd1f946c3548 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 28 Oct 2021 17:42:59 +1100 Subject: [PATCH 1331/1500] Fix T92467: Path Selection broken when Drag is set to Tweak Ctrl-RMB was getting used by "object" selection in edit-mode. Remove selection key-map from fallback tools, rely on the editors key-map for selection. --- .../keyconfig/keymap_data/blender_default.py | 60 ++----------------- 1 file changed, 4 insertions(+), 56 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 3a1ba3e22ff..2ba7a7edb89 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4660,17 +4660,6 @@ def _template_view3d_select(*, type, value, legacy): )] -def _template_view3d_select_for_fallback(params, fallback): - if (not fallback) and params.use_fallback_tool_rmb: - # Needed so we have immediate select+tweak when the default select tool is active. - return _template_view3d_select( - type=params.select_mouse, - value=params.select_mouse_value, - legacy=params.legacy, - ) - return [] - - def _template_view3d_gpencil_select(*, type, value, legacy, use_select_mouse=True): return [ *([] if not use_select_mouse else [ @@ -4686,17 +4675,6 @@ def _template_view3d_gpencil_select(*, type, value, legacy, use_select_mouse=Tru ] -def _template_view3d_gpencil_select_for_fallback(params, fallback): - if (not fallback) and params.use_fallback_tool_rmb: - # Needed so we have immediate select+tweak when the default select tool is active. - return _template_view3d_gpencil_select( - type=params.select_mouse, - value=params.select_mouse_value, - legacy=params.legacy, - ) - return [] - - def _template_uv_select(*, type, value, legacy): return [ ("uv.select", {"type": type, "value": value}, @@ -4706,17 +4684,6 @@ def _template_uv_select(*, type, value, legacy): ] -def _template_uv_select_for_fallback(params, fallback): - if (not fallback) and params.use_fallback_tool_rmb: - # Needed so we have immediate select+tweak when the default select tool is active. - return _template_uv_select( - type=params.select_mouse, - value=params.select_mouse_value, - legacy=params.legacy, - ) - return [] - - def _template_sequencer_generic_select(*, type, value, legacy): return [( "sequencer.select", @@ -4762,17 +4729,6 @@ def _template_sequencer_timeline_select(*, type, value, legacy): )] -def _template_sequencer_select_for_fallback(params, fallback): - if (not fallback) and params.use_fallback_tool_rmb: - # Needed so we have immediate select+tweak when the default select tool is active. - return _template_sequencer_generic_select( - type=params.select_mouse, - value=params.select_mouse_value, - legacy=params.legacy, - ) - return [] - - def km_image_paint(params): items = [] keymap = ( @@ -6312,7 +6268,6 @@ def km_image_editor_tool_uv_select_box(params, *, fallback): "uv.select_box", # Don't use `tool_maybe_tweak_event`, see comment for this slot. **(params.select_tweak_event if fallback else params.tool_tweak_event))), - *_template_uv_select_for_fallback(params, fallback), ]}, ) @@ -6340,7 +6295,6 @@ def km_image_editor_tool_uv_select_lasso(params, *, fallback): *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "uv.select_lasso", **(params.select_tweak_event if fallback else params.tool_tweak_event))), - *_template_uv_select_for_fallback(params, fallback), ]}, ) @@ -6504,7 +6458,6 @@ def km_3d_view_tool_select_box(params, *, fallback): "view3d.select_box", # Don't use `tool_maybe_tweak_event`, see comment for this slot. **(params.select_tweak_event if fallback else params.tool_tweak_event))), - *_template_view3d_select_for_fallback(params, fallback), ]}, ) @@ -6534,7 +6487,6 @@ def km_3d_view_tool_select_lasso(params, *, fallback): *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "view3d.select_lasso", **(params.select_tweak_event if fallback else params.tool_tweak_event))), - *_template_view3d_select_for_fallback(params, fallback), ]} ) @@ -7390,7 +7342,6 @@ def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback): "gpencil.select_box", # Don't use `tool_maybe_tweak_event`, see comment for this slot. **(params.select_tweak_event if fallback else params.tool_tweak_event))), - *_template_view3d_gpencil_select_for_fallback(params, fallback), ]}, ) @@ -7420,7 +7371,6 @@ def km_3d_view_tool_edit_gpencil_select_lasso(params, *, fallback): *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions( "gpencil.select_lasso", **(params.select_tweak_event if fallback else params.tool_tweak_event))), - *_template_view3d_gpencil_select_for_fallback(params, fallback), ]} ) @@ -7546,12 +7496,11 @@ def km_sequencer_editor_tool_select(params, *, fallback): _fallback_id("Sequencer Tool: Tweak", fallback), {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - *([] if fallback else - _template_items_tool_select(params, "sequencer.select", "sequencer.cursor_set", extend="toggle") - ), - *([] if (not params.use_fallback_tool_rmb) else _template_sequencer_generic_select( - type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), + *([] if fallback else _template_items_tool_select( + params, "sequencer.select", "sequencer.cursor_set", extend="toggle")), + *([] if (not params.use_fallback_tool_rmb) else _template_sequencer_preview_select( + type=params.select_mouse, value=params.select_mouse_value_fallback, legacy=params.legacy)), # Ignored for preview. *_template_items_change_frame(params), ]}, @@ -7568,7 +7517,6 @@ def km_sequencer_editor_tool_select_box(params, *, fallback): "sequencer.select_box", **(params.select_tweak_event if fallback else params.tool_tweak_event), properties=[("tweak", params.select_mouse == 'LEFTMOUSE')])), - *_template_sequencer_select_for_fallback(params, fallback), # RMB select can already set the frame, match the tweak tool. # Ignored for preview. From 8761699eab62eaba0f25f3d3ca1ce5d722c9fa45 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 28 Oct 2021 17:47:56 +1100 Subject: [PATCH 1332/1500] Cleanup: warning in 2a2d873124111b5fcbc2c3c59f73fd1f946c3548 --- .../blender/windowmanager/intern/wm_event_system.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 6092689138d..798f60fba3d 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -4009,12 +4009,12 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap * * Follow #wmEventHandler_KeymapDynamicFn signature. */ -void wm_event_get_keymap_from_toolsystem_ex(wmWindowManager *wm, - wmWindow *win, - wmEventHandler_Keymap *handler, - wmEventHandler_KeymapResult *km_result, - /* Extra arguments. */ - const bool with_gizmos) +static void wm_event_get_keymap_from_toolsystem_ex(wmWindowManager *wm, + wmWindow *win, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result, + /* Extra arguments. */ + const bool with_gizmos) { memset(km_result, 0x0, sizeof(*km_result)); From 45439dfe4c05eabaa83d0c1b75463966b5ba896d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 28 Oct 2021 17:57:18 +1100 Subject: [PATCH 1333/1500] Preferences: remove special case for copying previous settings This was only needed for skipping version numbers when the numbering scheme changed for 3.0. --- release/scripts/startup/bl_operators/userpref.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 67a02f6e1f4..1363bcf60e4 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -100,14 +100,6 @@ class PREFERENCES_OT_copy_prev(Operator): version_new = ((version[0] * 100) + version[1]) version_old = ((version[0] * 100) + version[1]) - 1 - # Special case, remove when the version is > 3.0. - if version_new == 300: - version_new = 294 - version_old = 293 - else: - print("TODO: remove exception!") - # End special case. - # Ensure we only try to copy files from a point release. # The check below ensures the second numbers match. while (version_new % 100) // 10 == (version_old % 100) // 10: From 2501d002683d5bbe9ea82c0c3f58e47cf97d3b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 28 Oct 2021 10:30:13 +0200 Subject: [PATCH 1334/1500] Python doc generator: raise explanatory error when context key is missing When a new key is added to the context, it also needs to be added to the `sphinx_doc_gen.py` file for generating the Python API documentation. When this isn't done, the script would raise a generic `KeyError`. Now it explains what needs to be updated to solve the problem. No functional changes to Blender. --- doc/python_api/sphinx_doc_gen.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 903136ea9f2..3fdb4ca0bc3 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1223,7 +1223,10 @@ def pycontext2sphinx(basepath): while char_array[i] is not None: member = ctypes.string_at(char_array[i]).decode(encoding="ascii") fw(".. data:: %s\n\n" % member) - member_type, is_seq = context_type_map[member] + try: + member_type, is_seq = context_type_map[member] + except KeyError: + raise SystemExit("Error: context key %r not found in context_type_map; update %s" % (member, __file__)) from None fw(" :type: %s :class:`bpy.types.%s`\n\n" % ("sequence of " if is_seq else "", member_type)) unique.add(member) i += 1 From 4adde62f6097da9ca8dbec3d7e970d30e1f48cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 28 Oct 2021 10:31:29 +0200 Subject: [PATCH 1335/1500] Python doc generator: add missing `selected_ids` context key Add the context key I introduced in rB03c0581c6ed to the Python API docs generator. No functional changes to Blender. --- doc/python_api/sphinx_doc_gen.py | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 3fdb4ca0bc3..04efe49f778 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1110,6 +1110,7 @@ context_type_map = { "selected_editable_sequences": ("Sequence", True), "selected_ids": ("ID", True), "selected_files": ("FileSelectEntry", True), + "selected_ids": ("ID", True), "selected_nla_strips": ("NlaStrip", True), "selected_movieclip_tracks": ("MovieTrackingTrack", True), "selected_nodes": ("Node", True), From 4b57d5a9a04da340c6392b3c31d5d3ef9103edc6 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Thu, 28 Oct 2021 17:12:57 +0800 Subject: [PATCH 1336/1500] LineArt: Fix(unreported) depsgraph camera error This fixes unintentional line art error when custom camera doesn't exist, now not adding custom camera relation in this case. --- source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 06dea6cd4d2..4411762aeea 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -264,13 +264,13 @@ static void updateDepsgraph(GpencilModifierData *md, else { add_this_collection(ctx->scene->master_collection, ctx, mode); } - if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA) { + if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA && lmd->source_camera) { DEG_add_object_relation( ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); DEG_add_object_relation( ctx->node, lmd->source_camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier"); } - if (ctx->scene->camera) { + else if (ctx->scene->camera) { DEG_add_object_relation( ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); DEG_add_object_relation( From c1cfb475b32ddef3a3f1d06e758cc1b940fb742f Mon Sep 17 00:00:00 2001 From: YimingWu Date: Thu, 28 Oct 2021 17:17:48 +0800 Subject: [PATCH 1337/1500] LineArt: Fix(unreported) Material mask panel logic The logic should be: show material mask panel if in_front is on, it was inverted unintentionally. --- source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 4411762aeea..fa31aec2b5b 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -478,7 +478,7 @@ static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *pa const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); uiLayoutSetEnabled(layout, !is_baked); - uiLayoutSetActive(layout, (!show_in_front) && anything_showing_through(ptr)); + uiLayoutSetActive(layout, show_in_front && anything_showing_through(ptr)); uiItemR(layout, ptr, "use_material_mask", 0, IFACE_("Material Mask"), ICON_NONE); } From be7ce7cb4d7f4b28fffc626e6ac0b3588a5824e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 28 Oct 2021 11:22:47 +0200 Subject: [PATCH 1338/1500] View3D Context: use correct data type `CTX_data_selected_objects()` returns a `ListBase` of `CollectionPointerLink`, not `PointerRNA`. This caused an alignment issue, resulting in `owner_id == NULL` reported in T92507. Correcting the pointer type fixed this. In the end, the same pointer is used as before this commit, but the way it is obtained is actually correct. --- source/blender/editors/space_view3d/space_view3d.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 37d013e7bd9..6acf51aec6e 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1821,8 +1821,8 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes if (CTX_data_equals(member, "selected_ids")) { ListBase selected_objects; CTX_data_selected_objects(C, &selected_objects); - LISTBASE_FOREACH (PointerRNA *, object_ptr, &selected_objects) { - ID *selected_id = object_ptr->data; + LISTBASE_FOREACH (CollectionPointerLink *, object_ptr_link, &selected_objects) { + ID *selected_id = object_ptr_link->ptr.owner_id; CTX_data_id_list_add(result, selected_id); } BLI_freelistN(&selected_objects); From 289843119d96dd1a68ada25e4b357a5b8080f132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 28 Oct 2021 10:31:29 +0200 Subject: [PATCH 1339/1500] Python doc generator: add missing `selected_ids` context key Add the context key I introduced in rB03c0581c6ed to the Python API docs generator. No functional changes to Blender. --- doc/python_api/sphinx_doc_gen.py | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 903136ea9f2..f8638b97270 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1110,6 +1110,7 @@ context_type_map = { "selected_editable_sequences": ("Sequence", True), "selected_ids": ("ID", True), "selected_files": ("FileSelectEntry", True), + "selected_ids": ("ID", True), "selected_nla_strips": ("NlaStrip", True), "selected_movieclip_tracks": ("MovieTrackingTrack", True), "selected_nodes": ("Node", True), From aebb3d3062babd9da7c89a7acd4126f4339d201f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 28 Oct 2021 11:57:20 +0200 Subject: [PATCH 1340/1500] Fix (unreported) potential issue in new `BKE_libblock_relink_to_newid_new` Remapping code could call collection resync code while processing remapping, which is a good way to crash by accessing no-more-valid pointers. Similar issue as with liboverrides resync, fixed the same way by preventing any collection resync until whole remapping has been done. This was probably not an issue in practice in current code, since this is only used by append code currently, which should not affect layers/collections in current scene yet. --- source/blender/blenkernel/intern/lib_remap.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index b5c45c0902b..905ac5af512 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -716,6 +716,7 @@ void BKE_libblock_relink_to_newid(ID *id) * FIXME: Port all usages of #BKE_libblock_relink_to_newid to this * #BKE_libblock_relink_to_newid_new new code and remove old one. ************************** */ +static void libblock_relink_to_newid_new(Main *bmain, ID *id); static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) { const int cb_flag = cb_data->cb_flag; @@ -739,12 +740,22 @@ static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) } if (id->tag & LIB_TAG_NEW) { id->tag &= ~LIB_TAG_NEW; - BKE_libblock_relink_to_newid_new(bmain, id); + libblock_relink_to_newid_new(bmain, id); } } return IDWALK_RET_NOP; } +static void libblock_relink_to_newid_new(Main *bmain, ID *id) +{ + if (ID_IS_LINKED(id)) { + return; + } + + id->tag &= ~LIB_TAG_NEW; + BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0); +} + /** * Remaps ID usages of given ID to their `id->newid` pointer if not None, and proceeds recursively * in the dependency tree of IDs for all data-blocks tagged with `LIB_TAG_NEW`. @@ -762,6 +773,8 @@ void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id) /* We do not want to have those cached relationship data here. */ BLI_assert(bmain->relations == NULL); - id->tag &= ~LIB_TAG_NEW; - BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0); + BKE_layer_collection_resync_forbid(); + libblock_relink_to_newid_new(bmain, id); + BKE_layer_collection_resync_allow(); + BKE_main_collection_sync_remap(bmain); } From ddb4eb8a897eeb78c3eb9b975ca77a693a964ac1 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Thu, 28 Oct 2021 17:12:57 +0800 Subject: [PATCH 1341/1500] LineArt: Fix(unreported) depsgraph camera error This fixes unintentional line art error when custom camera doesn't exist, now not adding custom camera relation in this case. --- source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 06dea6cd4d2..4411762aeea 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -264,13 +264,13 @@ static void updateDepsgraph(GpencilModifierData *md, else { add_this_collection(ctx->scene->master_collection, ctx, mode); } - if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA) { + if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA && lmd->source_camera) { DEG_add_object_relation( ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); DEG_add_object_relation( ctx->node, lmd->source_camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier"); } - if (ctx->scene->camera) { + else if (ctx->scene->camera) { DEG_add_object_relation( ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); DEG_add_object_relation( From e96b7c0092ea4d193ded1c1e16e5ff72009088e2 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Thu, 28 Oct 2021 17:17:48 +0800 Subject: [PATCH 1342/1500] LineArt: Fix(unreported) Material mask panel logic The logic should be: show material mask panel if in_front is on, it was inverted unintentionally. --- source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 4411762aeea..fa31aec2b5b 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -478,7 +478,7 @@ static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *pa const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); uiLayoutSetEnabled(layout, !is_baked); - uiLayoutSetActive(layout, (!show_in_front) && anything_showing_through(ptr)); + uiLayoutSetActive(layout, show_in_front && anything_showing_through(ptr)); uiItemR(layout, ptr, "use_material_mask", 0, IFACE_("Material Mask"), ICON_NONE); } From 43f97393bb5e394c2a7320a0236f1d64f12fa9af Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 28 Oct 2021 15:51:40 +0200 Subject: [PATCH 1343/1500] Fix 92550: GPencil Vertex Group is not apply as expected when is inverted When use Invert option, the weight must be inverted not omitted. This change invert the value if the point had assigned weight to get the right result. --- source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index 595a0c1cc5e..9ea146c77f2 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -156,7 +156,7 @@ float get_modifier_point_weight(MDeformVert *dvert, bool inverse, int def_nr) MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr); weight = dw ? dw->weight : -1.0f; if ((weight >= 0.0f) && (inverse)) { - return -1.0f; + return 1.0f - weight; } if ((weight < 0.0f) && (!inverse)) { From cefb0122b4973d8311955a163de4cf16b475edc1 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 28 Oct 2021 15:44:50 +0200 Subject: [PATCH 1344/1500] Fix T92526: Cycles viewport denoiser red tint after recent changes --- intern/cycles/scene/film.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/intern/cycles/scene/film.cpp b/intern/cycles/scene/film.cpp index b6480fa64f1..591f309384e 100644 --- a/intern/cycles/scene/film.cpp +++ b/intern/cycles/scene/film.cpp @@ -194,6 +194,7 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) /* Mark passes as unused so that the kernel knows the pass is inaccessible. */ kfilm->pass_denoising_normal = PASS_UNUSED; kfilm->pass_denoising_albedo = PASS_UNUSED; + kfilm->pass_denoising_depth = PASS_UNUSED; kfilm->pass_sample_count = PASS_UNUSED; kfilm->pass_adaptive_aux_buffer = PASS_UNUSED; kfilm->pass_shadow_catcher = PASS_UNUSED; From 3620ce7f67874b8d891a41b66714112bed7a23a2 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 28 Oct 2021 15:46:20 +0200 Subject: [PATCH 1345/1500] Fix T92503: Cycles OSL crash with material previews --- intern/cycles/scene/osl.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/intern/cycles/scene/osl.cpp b/intern/cycles/scene/osl.cpp index 09626cb48bb..c5f38d4f270 100644 --- a/intern/cycles/scene/osl.cpp +++ b/intern/cycles/scene/osl.cpp @@ -326,17 +326,22 @@ bool OSLShaderManager::osl_compile(const string &inputfile, const string &output string stdosl_path; string shader_path = path_get("shader"); - /* specify output file name */ + /* Specify output file name. */ options.push_back("-o"); options.push_back(outputfile); - /* specify standard include path */ + /* Specify standard include path. */ string include_path_arg = string("-I") + shader_path; options.push_back(include_path_arg); stdosl_path = path_join(shader_path, "stdcycles.h"); - /* compile */ + /* Compile. + * + * Mutex protected because the OSL compiler does not appear to be thread safe, see T92503. */ + static thread_mutex osl_compiler_mutex; + thread_scoped_lock lock(osl_compiler_mutex); + OSL::OSLCompiler *compiler = new OSL::OSLCompiler(&OSL::ErrorHandler::default_handler()); bool ok = compiler->compile(string_view(inputfile), options, string_view(stdosl_path)); delete compiler; From db8be0cdfbaf9346af931a56da8df92f84845db3 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 28 Oct 2021 15:51:40 +0200 Subject: [PATCH 1346/1500] Cleanup: compiler warnings in with Cycles OSL and clang --- intern/cycles/util/defines.h | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/intern/cycles/util/defines.h b/intern/cycles/util/defines.h index 9b1698d461a..a778bef52b2 100644 --- a/intern/cycles/util/defines.h +++ b/intern/cycles/util/defines.h @@ -33,21 +33,14 @@ /* Qualifiers for kernel code shared by CPU and GPU */ #ifndef __KERNEL_GPU__ -# define ccl_device static inline -# define ccl_device_noinline static -# define ccl_device_noinline_cpu ccl_device_noinline -# define ccl_global -# define ccl_static_constant static const -# define ccl_constant const -# define ccl_local -# define ccl_local_param -# define ccl_private -# define ccl_restrict __restrict -# define ccl_optional_struct_init -# define ccl_loop_no_unroll -# define ccl_attr_maybe_unused [[maybe_unused]] -# define __KERNEL_WITH_SSE_ALIGN__ +/* Leave inlining decisions to compiler for these, the inline keyword here + * is not about performance but including function definitions in headers. */ +# define ccl_device static inline +# define ccl_device_noinline static inline +# define ccl_device_noinline_cpu ccl_device_noinline + +/* Forced inlining. */ # if defined(_WIN32) && !defined(FREE_WINDOWS) # define ccl_device_inline static __forceinline # define ccl_device_forceinline static __forceinline @@ -75,6 +68,18 @@ # define ccl_never_inline __attribute__((noinline)) # endif /* _WIN32 && !FREE_WINDOWS */ +/* Address spaces for GPU. */ +# define ccl_global +# define ccl_static_constant static const +# define ccl_constant const +# define ccl_private + +# define ccl_restrict __restrict +# define ccl_optional_struct_init +# define ccl_loop_no_unroll +# define ccl_attr_maybe_unused [[maybe_unused]] +# define __KERNEL_WITH_SSE_ALIGN__ + /* Use to suppress '-Wimplicit-fallthrough' (in place of 'break'). */ # ifndef ATTR_FALLTHROUGH # if defined(__GNUC__) && (__GNUC__ >= 7) /* gcc7.0+ only */ From 690300eb4acd01ecada000c5ce6162c2437f9f6b Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Thu, 28 Oct 2021 21:03:47 +0200 Subject: [PATCH 1347/1500] Fix install paths for blender thumbnailer when not building a portable install When doing a non portable build of blender, the executable blender-thumbnailer would be installed in two locations: /usr/bin/ /usr/ While cleaning up, also make the blender thumbnailer dll optional on windows to bring the logic in line with what it is on linux and mac. Reviewed By: Campbell Barton, Ray molenkamp Differential Revision: http://developer.blender.org/D13014 --- CMakeLists.txt | 3 +-- source/blender/blendthumb/CMakeLists.txt | 11 ----------- source/blender/blenlib/CMakeLists.txt | 4 ++++ source/blender/blenlib/intern/winstuff.c | 2 ++ source/creator/CMakeLists.txt | 7 +++++++ 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e975c18aaf..62e7d9b2941 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,8 +160,7 @@ if(APPLE) # Currently this causes a build error linking, disable. set(WITH_BLENDER_THUMBNAILER OFF) elseif(WIN32) - # Building the thumbnail extraction DLL could be made optional. - set(WITH_BLENDER_THUMBNAILER ON) + option(WITH_BLENDER_THUMBNAILER "Build \"BlendThumb.dll\" helper for Windows explorer integration" ON) else() option(WITH_BLENDER_THUMBNAILER "Build \"blender-thumbnailer\" thumbnail extraction utility" ON) endif() diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt index 4bcd27082c0..4c2e72418a0 100644 --- a/source/blender/blendthumb/CMakeLists.txt +++ b/source/blender/blendthumb/CMakeLists.txt @@ -56,11 +56,6 @@ if(WIN32) target_link_libraries(BlendThumb bf_blenlib dbghelp.lib Version.lib) set_target_properties(BlendThumb PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:msvcrt") - install( - FILES $ - COMPONENT Blender - DESTINATION "." - ) else() # ----------------------------------------------------------------------------- # Build `blender-thumbnailer` executable @@ -68,10 +63,4 @@ else() add_executable(blender-thumbnailer ${SRC} src/blender_thumbnailer.cc) target_link_libraries(blender-thumbnailer bf_blenlib) target_link_libraries(blender-thumbnailer ${PTHREADS_LIBRARIES}) - - install( - FILES $ - COMPONENT Blender - DESTINATION "." - ) endif() diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 72087a12767..7db984aef5c 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -367,6 +367,10 @@ if(WITH_GMP) endif() if(WIN32) + if (WITH_BLENDER_THUMBNAILER) + # Needed for querying the thumbnailer .dll in winstuff.c + add_definitions(-DWITH_BLENDER_THUMBNAILER) + endif() list(APPEND INC ../../../intern/utfconv ) diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index d5c9c5cd5e6..3001b25bc1e 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -172,12 +172,14 @@ bool BLI_windows_register_blend_extension(const bool background) return false; } +# ifdef WITH_BLENDER_THUMBNAILER BLI_windows_get_executable_dir(InstallDir); GetSystemDirectory(SysDir, FILE_MAXDIR); ThumbHandlerDLL = "BlendThumb.dll"; snprintf( RegCmd, MAX_PATH * 2, "%s\\regsvr32 /s \"%s\\%s\"", SysDir, InstallDir, ThumbHandlerDLL); system(RegCmd); +# endif RegCloseKey(root); printf("success (%s)\n", usr_mode ? "user" : "system"); diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index de560e39606..816d3a60fc3 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -990,6 +990,13 @@ elseif(WIN32) DESTINATION "." ) + if(WITH_BLENDER_THUMBNAILER) + install( + TARGETS BlendThumb + DESTINATION "." + ) + endif() + if(WITH_DRACO) install( PROGRAMS $ From eda8065afcf27a8dc4df9b273815a39eea347415 Mon Sep 17 00:00:00 2001 From: Leon Leno Date: Thu, 28 Oct 2021 14:24:42 -0500 Subject: [PATCH 1348/1500] Fix: Improve node socket icon scaling group input/output list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch makes `widget_nodesocket` base the size of the drawn socket icon on the rectangle that’s passed in to allow it to scale with the rest of the interface. Differential Revision: https://developer.blender.org/D11734 --- source/blender/editors/interface/interface_widgets.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index acff14488a6..3e9042d29a0 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -3686,7 +3686,7 @@ static void widget_datasetrow( static void widget_nodesocket( uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) { - const int radi = 5; + const int radi = 0.25f * BLI_rcti_size_y(rect); uiWidgetBase wtb; widget_init(&wtb); From 673984b222dbb33239ae89fd32f0d2a1de586b71 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 28 Oct 2021 21:33:02 +0200 Subject: [PATCH 1349/1500] Fix T92158: Cycles crash with Fast GI and area light MIS --- intern/cycles/kernel/integrator/intersect_closest.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h index d5a9df9669b..7fb88fc2804 100644 --- a/intern/cycles/kernel/integrator/intersect_closest.h +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -159,9 +159,11 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg, IntegratorState s if (path_state_ao_bounce(kg, state)) { ray.t = kernel_data.integrator.ao_bounces_distance; - const float object_ao_distance = kernel_tex_fetch(__objects, last_isect_object).ao_distance; - if (object_ao_distance != 0.0f) { - ray.t = object_ao_distance; + if (last_isect_object != OBJECT_NONE) { + const float object_ao_distance = kernel_tex_fetch(__objects, last_isect_object).ao_distance; + if (object_ao_distance != 0.0f) { + ray.t = object_ao_distance; + } } } From 049510f42580b7948ddca5eb36fd30dbe0143626 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 28 Oct 2021 21:42:20 +0200 Subject: [PATCH 1350/1500] Fix T92491: Cycles panoramic camera inside volume fails with near clipping --- intern/cycles/scene/camera.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/intern/cycles/scene/camera.cpp b/intern/cycles/scene/camera.cpp index 5877b82ead5..5bafe736fb5 100644 --- a/intern/cycles/scene/camera.cpp +++ b/intern/cycles/scene/camera.cpp @@ -592,22 +592,26 @@ BoundBox Camera::viewplane_bounds_get() if (camera_type == CAMERA_PANORAMA) { if (use_spherical_stereo == false) { - bounds.grow(make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w)); + bounds.grow(make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w), nearclip); } else { float half_eye_distance = interocular_distance * 0.5f; - bounds.grow(make_float3( - cameratoworld.x.w + half_eye_distance, cameratoworld.y.w, cameratoworld.z.w)); + bounds.grow( + make_float3(cameratoworld.x.w + half_eye_distance, cameratoworld.y.w, cameratoworld.z.w), + nearclip); - bounds.grow(make_float3( - cameratoworld.z.w, cameratoworld.y.w + half_eye_distance, cameratoworld.z.w)); + bounds.grow( + make_float3(cameratoworld.z.w, cameratoworld.y.w + half_eye_distance, cameratoworld.z.w), + nearclip); - bounds.grow(make_float3( - cameratoworld.x.w - half_eye_distance, cameratoworld.y.w, cameratoworld.z.w)); + bounds.grow( + make_float3(cameratoworld.x.w - half_eye_distance, cameratoworld.y.w, cameratoworld.z.w), + nearclip); - bounds.grow(make_float3( - cameratoworld.x.w, cameratoworld.y.w - half_eye_distance, cameratoworld.z.w)); + bounds.grow( + make_float3(cameratoworld.x.w, cameratoworld.y.w - half_eye_distance, cameratoworld.z.w), + nearclip); } } else { From f2cc38a62b50b0819cd23a44399c40d5d8b466d1 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 28 Oct 2021 21:49:32 +0200 Subject: [PATCH 1351/1500] Fix T92255: Cycles Christensen-Burley render errors with scaled objects --- .../cycles/kernel/integrator/subsurface_disk.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/intern/cycles/kernel/integrator/subsurface_disk.h b/intern/cycles/kernel/integrator/subsurface_disk.h index e1cce13fb30..6146b8c41fc 100644 --- a/intern/cycles/kernel/integrator/subsurface_disk.h +++ b/intern/cycles/kernel/integrator/subsurface_disk.h @@ -119,9 +119,6 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, float sum_weights = 0.0f; for (int hit = 0; hit < num_eval_hits; hit++) { - /* Quickly retrieve P and Ng without setting up ShaderData. */ - const float3 hit_P = ray.P + ray.D * ss_isect.hits[hit].t; - /* Get geometric normal. */ const int object = ss_isect.hits[hit].object; const int object_flag = kernel_tex_fetch(__object_flag, object); @@ -131,11 +128,24 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, } if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + /* Transform normal to world space. */ Transform itfm; - object_fetch_transform_motion_test(kg, object, time, &itfm); + Transform tfm = object_fetch_transform_motion_test(kg, object, time, &itfm); hit_Ng = normalize(transform_direction_transposed(&itfm, hit_Ng)); + + /* Transform t to world space, except for OptiX where it already is. */ +#ifdef __KERNEL_OPTIX__ + (void)tfm; +#else + float3 D = transform_direction(&itfm, ray.D); + D = normalize(D) * ss_isect.hits[hit].t; + ss_isect.hits[hit].t = len(transform_direction(&tfm, D)); +#endif } + /* Quickly retrieve P and Ng without setting up ShaderData. */ + const float3 hit_P = ray.P + ray.D * ss_isect.hits[hit].t; + /* Probability densities for local frame axes. */ const float pdf_N = pick_pdf_N * fabsf(dot(disk_N, hit_Ng)); const float pdf_T = pick_pdf_T * fabsf(dot(disk_T, hit_Ng)); From 35f4d254fd85cec475a00dfc019947b60d6c702d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 28 Oct 2021 22:38:07 +0200 Subject: [PATCH 1352/1500] Fix T92513: Cycles stereo pole merge not rotating along with camera --- intern/cycles/kernel/camera/camera.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/intern/cycles/kernel/camera/camera.h b/intern/cycles/kernel/camera/camera.h index e966e9e1596..4f3931583de 100644 --- a/intern/cycles/kernel/camera/camera.h +++ b/intern/cycles/kernel/camera/camera.h @@ -306,15 +306,15 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, } #endif - P = transform_point(&cameratoworld, P); - D = normalize(transform_direction(&cameratoworld, D)); - /* Stereo transform */ bool use_stereo = cam->interocular_offset != 0.0f; if (use_stereo) { spherical_stereo_transform(cam, &P, &D); } + P = transform_point(&cameratoworld, P); + D = normalize(transform_direction(&cameratoworld, D)); + ray->P = P; ray->D = D; @@ -325,19 +325,19 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, * and simply take their differences. */ float3 Pcenter = Pcamera; float3 Dcenter = panorama_to_direction(cam, Pcenter.x, Pcenter.y); - Pcenter = transform_point(&cameratoworld, Pcenter); - Dcenter = normalize(transform_direction(&cameratoworld, Dcenter)); if (use_stereo) { spherical_stereo_transform(cam, &Pcenter, &Dcenter); } + Pcenter = transform_point(&cameratoworld, Pcenter); + Dcenter = normalize(transform_direction(&cameratoworld, Dcenter)); float3 Px = transform_perspective(&rastertocamera, make_float3(raster_x + 1.0f, raster_y, 0.0f)); float3 Dx = panorama_to_direction(cam, Px.x, Px.y); - Px = transform_point(&cameratoworld, Px); - Dx = normalize(transform_direction(&cameratoworld, Dx)); if (use_stereo) { spherical_stereo_transform(cam, &Px, &Dx); } + Px = transform_point(&cameratoworld, Px); + Dx = normalize(transform_direction(&cameratoworld, Dx)); differential3 dP, dD; dP.dx = Px - Pcenter; @@ -345,11 +345,11 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, float3 Py = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y + 1.0f, 0.0f)); float3 Dy = panorama_to_direction(cam, Py.x, Py.y); - Py = transform_point(&cameratoworld, Py); - Dy = normalize(transform_direction(&cameratoworld, Dy)); if (use_stereo) { spherical_stereo_transform(cam, &Py, &Dy); } + Py = transform_point(&cameratoworld, Py); + Dy = normalize(transform_direction(&cameratoworld, Dy)); dP.dy = Py - Pcenter; dD.dy = Dy - Dcenter; From fc36772b068834435f0061f8b95ee95fedc374b5 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 24 Oct 2021 22:13:30 +0200 Subject: [PATCH 1353/1500] Tests: minor updates to benchmark script for running on buildbot * graph command accepts folder of json files as input * reset command clears log files --- tests/performance/benchmark | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/performance/benchmark b/tests/performance/benchmark index a58c339e9f8..80556674dcc 100755 --- a/tests/performance/benchmark +++ b/tests/performance/benchmark @@ -4,6 +4,7 @@ import api import argparse import fnmatch +import glob import pathlib import shutil import sys @@ -228,6 +229,9 @@ def cmd_reset(env: api.TestEnvironment, argv: List): config.queue.write() + if args.test == '*': + shutil.rmtree(config.logs_dir) + def cmd_run(env: api.TestEnvironment, argv: List, update_only: bool): # Run tests. parser = argparse.ArgumentParser() @@ -274,7 +278,17 @@ def cmd_graph(argv: List): parser.add_argument('-o', '--output', type=str, required=True) args = parser.parse_args(argv) - graph = api.TestGraph([pathlib.Path(path) for path in args.json_file]) + # For directories, use all json files in the directory. + json_files = [] + for path in args.json_file: + path = pathlib.Path(path) + if path.is_dir(): + for filepath in glob.iglob(str(path / '*.json')): + json_files.append(pathlib.Path(filepath)) + else: + json_files.append(path) + + graph = api.TestGraph(json_files) graph.write(pathlib.Path(args.output)) def main(): From b43077ba3a2991096aa6484fcccd94d68998fb11 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 28 Oct 2021 18:23:55 -0500 Subject: [PATCH 1354/1500] Fix T92552: Spline evaluation with all points at the origin In this case, the uniform index sampling loop would fail to assign any data to the samples, so fill the rest with the largest value possible, corresponding to the end of the spline. Animation Nodes has the same fix for this case. --- source/blender/blenkernel/intern/spline_base.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 663c1951ba3..bbe4e0aab7b 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -486,6 +486,12 @@ Array Spline::sample_uniform_index_factors(const int samples_size) const prev_length = length; } + /* Zero lengths or float innacuracies can cause invalid values, or simply + * skip some, so set the values that weren't completed in the main loop. */ + for (const int i : IndexRange(i_sample, samples_size - i_sample)) { + samples[i] = float(samples_size); + } + if (!is_cyclic_) { /* In rare cases this can prevent overflow of the stored index. */ samples.last() = lengths.size(); From c1c016b9a4d215a0d43fdf80731cc9f479c9db72 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 29 Oct 2021 10:48:12 +1100 Subject: [PATCH 1355/1500] Fix T92468: Annotation Drag option broken in Node Editors Also enable fallback tool for link-cut. --- .../keyconfig/keymap_data/blender_default.py | 13 ++++++++----- .../startup/bl_ui/space_toolsystem_toolbar.py | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 2ba7a7edb89..8cb1c2542ae 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6382,7 +6382,8 @@ def km_node_editor_tool_select_box(params, *, fallback): {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( "node.select_box", - type=params.tool_maybe_tweak, value=params.tool_maybe_tweak_value, + # Don't use `tool_maybe_tweak_event`, see comment for this slot. + **(params.select_tweak_event if fallback else params.tool_tweak_event), properties=[("tweak", True)], )), ]}, @@ -6395,7 +6396,7 @@ def km_node_editor_tool_select_lasso(params, *, fallback): {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( - "node.select_lasso", type=params.tool_mouse, value='PRESS', + "node.select_lasso", **(params.select_tweak_event if fallback else params.tool_tweak_event), properties=[("tweak", True)])) ]}, ) @@ -6407,7 +6408,11 @@ def km_node_editor_tool_select_circle(params, *, fallback): {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple( - "node.select_circle", type=params.tool_mouse, value='PRESS', + "node.select_circle", + # Why circle select should be used on tweak? + # So that RMB or Shift-RMB is still able to set an element as active. + type=params.select_tweak if fallback else params.tool_mouse, + value='ANY' if fallback else 'PRESS', properties=[("wait_for_input", False)])), ]}, ) @@ -6474,7 +6479,6 @@ def km_3d_view_tool_select_circle(params, *, fallback): type=params.select_tweak if fallback else params.tool_mouse, value='ANY' if fallback else 'PRESS', properties=[("wait_for_input", False)])), - # No selection fallback since this operates on press. ]}, ) @@ -7358,7 +7362,6 @@ def km_3d_view_tool_edit_gpencil_select_circle(params, *, fallback): type=params.select_tweak if fallback else params.tool_mouse, value='ANY' if fallback else 'PRESS', properties=[("wait_for_input", False)])), - # No selection fallback since this operates on press. ]}, ) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index bf5372f5ecf..1a448046f7a 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2429,6 +2429,7 @@ class _defs_node_edit: icon="ops.node.links_cut", widget=None, keymap="Node Tool: Links Cut", + options={'KEYMAP_FALLBACK'}, ) From bbb5a77896293934f44157b325986608365b8ea9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 29 Oct 2021 11:34:20 +1100 Subject: [PATCH 1356/1500] Cleanup: add sections for tool key-maps --- .../keyconfig/keymap_data/blender_default.py | 88 ++++++++++++++----- 1 file changed, 68 insertions(+), 20 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 8cb1c2542ae..2289e8200a6 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6168,7 +6168,7 @@ def km_popup_toolbar(_params): # ------------------------------------------------------------------------------ -# Tool System Keymaps +# Tool System (Generic) # # Named are auto-generated based on the tool name and it's toolbar. @@ -6234,6 +6234,9 @@ def km_image_editor_tool_generic_sample(params): ) +# ------------------------------------------------------------------------------ +# Tool System (UV Editor) + def km_image_editor_tool_uv_cursor(params): return ( "Image Editor Tool: Uv, Cursor", @@ -6362,6 +6365,9 @@ def km_image_editor_tool_uv_scale(params): ) +# ------------------------------------------------------------------------------ +# Tool System (Node Editor) + def km_node_editor_tool_select(params, *, fallback): return ( _fallback_id("Node Tool: Tweak", fallback), @@ -6428,6 +6434,9 @@ def km_node_editor_tool_links_cut(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Generic) + def km_3d_view_tool_cursor(params): return ( "3D View Tool: Cursor", @@ -6571,6 +6580,9 @@ def km_3d_view_tool_measure(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Pose Mode) + def km_3d_view_tool_pose_breakdowner(params): return ( "3D View Tool: Pose, Breakdowner", @@ -6601,6 +6613,9 @@ def km_3d_view_tool_pose_relax(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Edit Armature) + def km_3d_view_tool_edit_armature_roll(params): return ( "3D View Tool: Edit Armature, Roll", @@ -6656,6 +6671,9 @@ def km_3d_view_tool_edit_armature_extrude_to_cursor(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Object Mode) + def km_3d_view_tool_interactive_add(params): return ( "3D View Tool: Object, Add Primitive", @@ -6672,6 +6690,9 @@ def km_3d_view_tool_interactive_add(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Edit Mesh) + def km_3d_view_tool_edit_mesh_extrude_region(params): return ( "3D View Tool: Edit Mesh, Extrude Region", @@ -6938,6 +6959,9 @@ def km_3d_view_tool_edit_mesh_rip_edge(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Edit Curve) + def km_3d_view_tool_edit_curve_draw(params): return ( "3D View Tool: Edit Curve, Draw", @@ -7005,6 +7029,9 @@ def km_3d_view_tool_edit_curve_extrude_to_cursor(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Sculpt) + def km_3d_view_tool_sculpt_box_hide(params): return ( "3D View Tool: Sculpt, Box Hide", @@ -7160,6 +7187,9 @@ def km_3d_view_tool_sculpt_face_set_edit(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Weight Paint) + def km_3d_view_tool_paint_weight_sample_weight(params): return ( "3D View Tool: Paint Weight, Sample Weight", @@ -7190,6 +7220,9 @@ def km_3d_view_tool_paint_weight_gradient(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Grease Pencil, Paint) + def km_3d_view_tool_paint_gpencil_line(params): return ( "3D View Tool: Paint Gpencil, Line", @@ -7324,6 +7357,9 @@ def km_3d_view_tool_paint_gpencil_interpolate(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Grease Pencil, Edit) + def km_3d_view_tool_edit_gpencil_select(params, *, fallback): return ( _fallback_id("3D View Tool: Edit Gpencil, Tweak", fallback), @@ -7459,6 +7495,9 @@ def km_3d_view_tool_edit_gpencil_interpolate(params): ) +# ------------------------------------------------------------------------------ +# Tool System (3D View, Grease Pencil, Sculpt) + def km_3d_view_tool_sculpt_gpencil_select(params): return ( "3D View Tool: Sculpt Gpencil, Tweak", @@ -7494,7 +7533,10 @@ def km_3d_view_tool_sculpt_gpencil_select_lasso(params): ) -def km_sequencer_editor_tool_select(params, *, fallback): +# ------------------------------------------------------------------------------ +# Tool System (Sequencer, Generic) + +def km_sequencer_editor_tool_generic_select(params, *, fallback): return ( _fallback_id("Sequencer Tool: Tweak", fallback), {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, @@ -7510,7 +7552,7 @@ def km_sequencer_editor_tool_select(params, *, fallback): ) -def km_sequencer_editor_tool_select_box(params, *, fallback): +def km_sequencer_editor_tool_generic_select_box(params, *, fallback): return ( _fallback_id("Sequencer Tool: Select Box", fallback), {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, @@ -7529,17 +7571,7 @@ def km_sequencer_editor_tool_select_box(params, *, fallback): ) -def km_sequencer_editor_tool_generic_sample(params): - return ( - "Sequencer Tool: Sample", - {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, - {"items": [ - ("sequencer.sample", {"type": params.tool_mouse, "value": 'PRESS'}, None), - ]}, - ) - - -def km_sequencer_editor_tool_cursor(params): +def km_sequencer_editor_tool_generic_cursor(params): return ( "Sequencer Tool: Cursor", {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, @@ -7552,6 +7584,9 @@ def km_sequencer_editor_tool_cursor(params): ) +# ------------------------------------------------------------------------------ +# Tool System (Sequencer, Timeline) + def km_sequencer_editor_tool_blade(_params): return ( "Sequencer Tool: Blade", @@ -7568,6 +7603,19 @@ def km_sequencer_editor_tool_blade(_params): ) +# ------------------------------------------------------------------------------ +# Tool System (Sequencer, Preview) + +def km_sequencer_editor_tool_sample(params): + return ( + "Sequencer Tool: Sample", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("sequencer.sample", {"type": params.tool_mouse, "value": 'PRESS'}, None), + ]}, + ) + + def km_sequencer_editor_tool_move(params): return ( "Sequencer Tool: Move", @@ -7850,14 +7898,14 @@ def generate_keymaps(params=None): km_3d_view_tool_sculpt_gpencil_select_box(params), km_3d_view_tool_sculpt_gpencil_select_circle(params), km_3d_view_tool_sculpt_gpencil_select_lasso(params), - *(km_sequencer_editor_tool_select(params, fallback=fallback) for fallback in (False, True)), - *(km_sequencer_editor_tool_select_box(params, fallback=fallback) for fallback in (False, True)), + *(km_sequencer_editor_tool_generic_select(params, fallback=fallback) for fallback in (False, True)), + *(km_sequencer_editor_tool_generic_select_box(params, fallback=fallback) for fallback in (False, True)), + km_sequencer_editor_tool_generic_cursor(params), km_sequencer_editor_tool_blade(params), - km_sequencer_editor_tool_generic_sample(params), - km_sequencer_editor_tool_cursor(params), - km_sequencer_editor_tool_scale(params), - km_sequencer_editor_tool_rotate(params), + km_sequencer_editor_tool_sample(params), km_sequencer_editor_tool_move(params), + km_sequencer_editor_tool_rotate(params), + km_sequencer_editor_tool_scale(params), ] # ------------------------------------------------------------------------------ From 59534dbee2effd6a4880a0f26329e0571f943207 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Thu, 28 Oct 2021 18:20:34 -0700 Subject: [PATCH 1357/1500] BLF Refactor: blf_kerning_step_fast Simplification of BLF Kerning See D13015 for more details. Differential Revision: https://developer.blender.org/D13015 Reviewed by Campbell Barton --- source/blender/blenfont/intern/blf_font.c | 146 +++++++++------------- 1 file changed, 62 insertions(+), 84 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 27478bd7f8e..dd133e75f49 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -298,43 +298,39 @@ static void blf_batch_draw_end(void) */ BLI_INLINE GlyphBLF *blf_utf8_next_fast( - FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p, uint *r_c) + FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p) { GlyphBLF *g; - if ((*r_c = str[*i_p]) < GLYPH_ASCII_TABLE_SIZE) { - g = (gc->glyph_ascii_table)[*r_c]; + uint charcode = (uint)str[*i_p]; + if (charcode < GLYPH_ASCII_TABLE_SIZE) { + g = (gc->glyph_ascii_table)[charcode]; if (UNLIKELY(g == NULL)) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c); - gc->glyph_ascii_table[*r_c] = g; + g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, charcode), charcode); + gc->glyph_ascii_table[charcode] = g; } (*i_p)++; } else { - *r_c = BLI_str_utf8_as_unicode_step(str, str_len, i_p); - g = blf_glyph_search(gc, *r_c); + charcode = BLI_str_utf8_as_unicode_step(str, str_len, i_p); + g = blf_glyph_search(gc, charcode); if (UNLIKELY(g == NULL)) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c); + g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, charcode), charcode); } } return g; } -BLI_INLINE void blf_kerning_step_fast(FontBLF *font, - const GlyphBLF *g_prev, - const GlyphBLF *g, - const uint c_prev, - const uint c, - int *pen_x_p) +BLI_INLINE int blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g) { if (!FT_HAS_KERNING(font->face) || g_prev == NULL) { - return; + return 0; } FT_Vector delta = {KERNING_ENTRY_UNSET}; /* Get unscaled kerning value from our cache if ASCII. */ - if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - delta.x = font->kerning_cache->ascii_table[c][c_prev]; + if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { + delta.x = font->kerning_cache->ascii_table[g->c][g_prev->c]; } /* If not ASCII or not found in cache, ask FreeType for kerning. */ @@ -344,14 +340,16 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, } /* If ASCII we save this value to our cache for quicker access next time. */ - if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x; + if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { + font->kerning_cache->ascii_table[g->c][g_prev->c] = (int)delta.x; } if (delta.x != 0) { /* Convert unscaled design units to pixels and move pen. */ - *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x); + return blf_unscaled_F26Dot6_to_pixels(font, delta.x); } + + return 0; } /** \} */ @@ -367,7 +365,6 @@ static void blf_font_draw_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0; @@ -380,22 +377,21 @@ static void blf_font_draw_ex(FontBLF *font, blf_batch_draw_begin(font); while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_utf8_next_fast(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + if (UNLIKELY(g->c == BLI_UTF8_ERR)) { + break; + } + pen_x += blf_kerning(font, g_prev, g); /* do not return this loop if clipped, we want every character tested */ blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); pen_x += g->advance_i; g_prev = g; - c_prev = c; } blf_batch_draw_end(); @@ -415,7 +411,6 @@ void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct /* use fixed column width, but an utf8 character may occupy multiple columns */ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int cwidth) { - unsigned int c; GlyphBLF *g; int col, columns = 0; int pen_x = 0, pen_y = 0; @@ -426,19 +421,19 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int blf_batch_draw_begin(font); while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_utf8_next_fast(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } + if (UNLIKELY(g->c == BLI_UTF8_ERR)) { + break; + } /* do not return this loop if clipped, we want every character tested */ blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); - col = BLI_wcwidth((char32_t)c); + col = BLI_wcwidth((char32_t)g->c); if (col < 0) { col = 1; } @@ -467,7 +462,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = (int)font->pos[0]; int pen_y_basis = (int)font->pos[1] + pen_y; @@ -483,15 +477,15 @@ static void blf_font_draw_buffer_ex(FontBLF *font, /* another buffer specific call for color conversion */ while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_utf8_next_fast(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + if (UNLIKELY(g->c == BLI_UTF8_ERR)) { + break; + } + pen_x += blf_kerning(font, g_prev, g); chx = pen_x + ((int)g->pos[0]); chy = pen_y_basis + g->dims[1]; @@ -588,7 +582,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, pen_x += g->advance_i; g_prev = g; - c_prev = c; } if (r_info) { @@ -617,22 +610,16 @@ void blf_font_draw_buffer(FontBLF *font, * - #BLF_width_to_rstrlen * \{ */ -static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, - const uint c_prev, - const uint c, - GlyphBLF *g_prev, - GlyphBLF *g, - int *pen_x, - const int width_i) +static bool blf_font_width_to_strlen_glyph_process( + FontBLF *font, GlyphBLF *g_prev, GlyphBLF *g, int *pen_x, const int width_i) { - if (UNLIKELY(c == BLI_UTF8_ERR)) { - return true; /* break the calling loop. */ - } if (UNLIKELY(g == NULL)) { return false; /* continue the calling loop. */ } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, pen_x); - + if (UNLIKELY(g->c == BLI_UTF8_ERR)) { + return true; /* break the calling loop. */ + } + *pen_x += blf_kerning(font, g_prev, g); *pen_x += g->advance_i; return (*pen_x >= width_i); @@ -641,7 +628,6 @@ static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, size_t blf_font_width_to_strlen( FontBLF *font, const char *str, const size_t str_len, float width, float *r_width) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev; int pen_x, width_new; size_t i, i_prev; @@ -649,11 +635,11 @@ size_t blf_font_width_to_strlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < str_len) && str[i]; - i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL; (i < str_len) && str[i]; + i_prev = i, width_new = pen_x, g_prev = g) { + g = blf_utf8_next_fast(font, gc, str, str_len, &i); - if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) { break; } } @@ -669,7 +655,6 @@ size_t blf_font_width_to_strlen( size_t blf_font_width_to_rstrlen( FontBLF *font, const char *str, const size_t str_len, float width, float *r_width) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev; int pen_x, width_new; size_t i, i_prev, i_tmp; @@ -685,19 +670,19 @@ size_t blf_font_width_to_rstrlen( i_prev = (size_t)(s_prev - str); i_tmp = i; - g = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp, &c); + g = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp); for (width_new = pen_x = 0; (s != NULL); - i = i_prev, s = s_prev, c = c_prev, g = g_prev, g_prev = NULL, width_new = pen_x) { + i = i_prev, s = s_prev, g = g_prev, g_prev = NULL, width_new = pen_x) { s_prev = BLI_str_find_prev_char_utf8(s, str); i_prev = (size_t)(s_prev - str); if (s_prev != NULL) { i_tmp = i_prev; - g_prev = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp, &c_prev); + g_prev = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp); BLI_assert(i_tmp == i); } - if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) { break; } } @@ -724,7 +709,6 @@ static void blf_font_boundbox_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0; @@ -736,15 +720,15 @@ static void blf_font_boundbox_ex(FontBLF *font, box->ymax = -32000.0f; while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_utf8_next_fast(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + if (UNLIKELY(g->c == BLI_UTF8_ERR)) { + break; + } + pen_x += blf_kerning(font, g_prev, g); gbox.xmin = (float)pen_x; gbox.xmax = (float)pen_x + g->advance; @@ -767,7 +751,6 @@ static void blf_font_boundbox_ex(FontBLF *font, pen_x += g->advance_i; g_prev = g; - c_prev = c; } if (box->xmin > box->xmax) { @@ -896,7 +879,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0, i_curr; @@ -909,15 +891,15 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, while ((i < str_len) && str[i]) { i_curr = i; - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_utf8_next_fast(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + if (UNLIKELY(g->c == BLI_UTF8_ERR)) { + break; + } + pen_x += blf_kerning(font, g_prev, g); gbox.xmin = pen_x; gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); @@ -931,7 +913,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, } g_prev = g; - c_prev = c; } if (r_info) { @@ -978,7 +959,6 @@ static void blf_font_wrap_apply(FontBLF *font, void *userdata), void *userdata) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0, pen_y = 0; size_t i = 0; @@ -999,15 +979,15 @@ static void blf_font_wrap_apply(FontBLF *font, size_t i_curr = i; bool do_draw = false; - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_utf8_next_fast(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + if (UNLIKELY(g->c == BLI_UTF8_ERR)) { + break; + } + pen_x += blf_kerning(font, g_prev, g); /** * Implementation Detail (utf8). @@ -1047,14 +1027,12 @@ static void blf_font_wrap_apply(FontBLF *font, pen_x = 0; pen_y -= gc->glyph_height_max; g_prev = NULL; - c_prev = BLI_UTF8_ERR; lines += 1; continue; } pen_x = pen_x_next; g_prev = g; - c_prev = c; } // printf("done! lines: %d, width, %d\n", lines, pen_x_next); From 70947ebc65a618f2d5e156427aae59780926653c Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Thu, 28 Oct 2021 18:49:21 -0700 Subject: [PATCH 1358/1500] BLF Refactor: blf_utf8_next_fast Simplification of BLF glyph loading See D13026 for details. Differential Revision: https://developer.blender.org/D13026 Reviewed by Campbell Barton --- source/blender/blenfont/intern/blf_font.c | 26 +--- source/blender/blenfont/intern/blf_glyph.c | 114 +++++++++--------- source/blender/blenfont/intern/blf_internal.h | 7 +- 3 files changed, 63 insertions(+), 84 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index dd133e75f49..52c412a42bb 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -300,24 +300,8 @@ static void blf_batch_draw_end(void) BLI_INLINE GlyphBLF *blf_utf8_next_fast( FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p) { - GlyphBLF *g; - uint charcode = (uint)str[*i_p]; - if (charcode < GLYPH_ASCII_TABLE_SIZE) { - g = (gc->glyph_ascii_table)[charcode]; - if (UNLIKELY(g == NULL)) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, charcode), charcode); - gc->glyph_ascii_table[charcode] = g; - } - (*i_p)++; - } - else { - charcode = BLI_str_utf8_as_unicode_step(str, str_len, i_p); - g = blf_glyph_search(gc, charcode); - if (UNLIKELY(g == NULL)) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, charcode), charcode); - } - } - return g; + uint charcode = BLI_str_utf8_as_unicode_step(str, str_len, i_p); + return blf_glyph_ensure(font, gc, charcode); } BLI_INLINE int blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g) @@ -388,7 +372,7 @@ static void blf_font_draw_ex(FontBLF *font, pen_x += blf_kerning(font, g_prev, g); /* do not return this loop if clipped, we want every character tested */ - blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); + blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); pen_x += g->advance_i; g_prev = g; @@ -431,7 +415,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int } /* do not return this loop if clipped, we want every character tested */ - blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); + blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); col = BLI_wcwidth((char32_t)g->c); if (col < 0) { @@ -857,7 +841,7 @@ float blf_font_fixed_width(FontBLF *font) GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); GlyphBLF *g = blf_glyph_search(gc, c); if (!g) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); + g = blf_glyph_ensure(font, gc, FT_Get_Char_Index(font->face, c)); /* if we don't find the glyph. */ if (!g) { diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 6cdf5fc5996..e077cc91f33 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -175,33 +175,8 @@ GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c) return NULL; } -GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, unsigned int c) +static bool blf_glyph_render(FontBLF *font, FT_UInt glyph_index) { - FT_GlyphSlot slot; - GlyphBLF *g; - FT_Error err; - FT_Bitmap bitmap, tempbitmap; - FT_BBox bbox; - unsigned int key; - - g = blf_glyph_search(gc, c); - if (g) { - return g; - } - - /* glyphs are dynamically created as needed by font rendering. this means that - * to make font rendering thread safe we have to do locking here. note that this - * must be a lock for the whole library and not just per font, because the font - * renderer uses a shared buffer internally */ - BLI_spin_lock(font->ft_lib_mutex); - - /* search again after locking */ - g = blf_glyph_search(gc, c); - if (g) { - BLI_spin_unlock(font->ft_lib_mutex); - return g; - } - int load_flags; int render_mode; @@ -228,7 +203,10 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } } - err = FT_Load_Glyph(font->face, (FT_UInt)index, load_flags); + FT_Error err = FT_Load_Glyph(font->face, glyph_index, load_flags); + if (err != 0) { + return false; + } /* Do not oblique a font that is designed to be italic! */ if (((font->flags & BLF_ITALIC) != 0) && !(font->face->style_flags & FT_STYLE_FLAG_ITALIC) && @@ -243,9 +221,8 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } /* Do not embolden an already bold font! */ - if (((font->flags & BLF_BOLD) != 0) && - !(font->face->style_flags & FT_STYLE_FLAG_BOLD) & - (font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)) { + if (((font->flags & BLF_BOLD) != 0) && !(font->face->style_flags & FT_STYLE_FLAG_BOLD) && + (font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)) { /* Strengthen the width more than the height. */ const FT_Pos extra_x = FT_MulFix(font->face->units_per_EM, font->face->size->metrics.x_scale) / 14; @@ -263,15 +240,12 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } } - if (err) { - BLI_spin_unlock(font->ft_lib_mutex); - return NULL; - } - /* get the glyph. */ - slot = font->face->glyph; + FT_GlyphSlot slot = font->face->glyph; err = FT_Render_Glyph(slot, render_mode); + FT_Bitmap tempbitmap; + if (font->flags & BLF_MONOCHROME) { /* Convert result from 1 bit per pixel to 8 bit per pixel */ /* Accum errors for later, fine if not interested beyond "ok vs any error" */ @@ -284,45 +258,69 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } if (err || slot->format != FT_GLYPH_FORMAT_BITMAP) { - BLI_spin_unlock(font->ft_lib_mutex); + return false; + } + + return true; +} + +GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) +{ + GlyphBLF *g = (charcode < GLYPH_ASCII_TABLE_SIZE) ? (gc->glyph_ascii_table)[charcode] : + blf_glyph_search(gc, charcode); + if (g) { + return g; + } + + FT_UInt glyph_index = FT_Get_Char_Index(font->face, charcode); + + if (!blf_glyph_render(font, glyph_index)) { return NULL; } - g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_add"); - g->c = c; - g->idx = (FT_UInt)index; - bitmap = slot->bitmap; - g->dims[0] = (int)bitmap.width; - g->dims[1] = (int)bitmap.rows; + FT_GlyphSlot slot = font->face->glyph; - const int buffer_size = g->dims[0] * g->dims[1]; - - if (buffer_size != 0) { - if (font->flags & BLF_MONOCHROME) { - /* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */ - for (int i = 0; i < buffer_size; i++) { - bitmap.buffer[i] = bitmap.buffer[i] ? 255 : 0; - } - } - - g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap"); - memcpy(g->bitmap, bitmap.buffer, (size_t)buffer_size); - } + /* glyphs are dynamically created as needed by font rendering. this means that + * to make font rendering thread safe we have to do locking here. note that this + * must be a lock for the whole library and not just per font, because the font + * renderer uses a shared buffer internally */ + BLI_spin_lock(font->ft_lib_mutex); + g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_get"); + g->c = charcode; + g->idx = glyph_index; g->advance = ((float)slot->advance.x) / 64.0f; g->advance_i = (int)g->advance; g->pos[0] = slot->bitmap_left; g->pos[1] = slot->bitmap_top; + g->dims[0] = slot->bitmap.width; + g->dims[1] = slot->bitmap.rows; g->pitch = slot->bitmap.pitch; + FT_BBox bbox; FT_Outline_Get_CBox(&(slot->outline), &bbox); g->box.xmin = ((float)bbox.xMin) / 64.0f; g->box.xmax = ((float)bbox.xMax) / 64.0f; g->box.ymin = ((float)bbox.yMin) / 64.0f; g->box.ymax = ((float)bbox.yMax) / 64.0f; - key = blf_hash(g->c); + const int buffer_size = slot->bitmap.width * slot->bitmap.rows; + if (buffer_size != 0) { + if (font->flags & BLF_MONOCHROME) { + /* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */ + for (int i = 0; i < buffer_size; i++) { + slot->bitmap.buffer[i] = slot->bitmap.buffer[i] ? 255 : 0; + } + } + g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap"); + memcpy(g->bitmap, slot->bitmap.buffer, (size_t)buffer_size); + } + + unsigned int key = blf_hash(g->c); BLI_addhead(&(gc->bucket[key]), g); + if (charcode < GLYPH_ASCII_TABLE_SIZE) { + gc->glyph_ascii_table[charcode] = g; + } BLI_spin_unlock(font->ft_lib_mutex); @@ -419,7 +417,7 @@ static void blf_glyph_calc_rect_shadow(rctf *rect, GlyphBLF *g, float x, float y blf_glyph_calc_rect(rect, g, x + (float)font->shadow_x, y + (float)font->shadow_y); } -void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y) +void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y) { if ((!g->dims[0]) || (!g->dims[1])) { return; diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 6fd5e8b7503..ba871ea2496 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -140,13 +140,10 @@ void blf_glyph_cache_clear(struct FontBLF *font); void blf_glyph_cache_free(struct GlyphCacheBLF *gc); struct GlyphBLF *blf_glyph_search(struct GlyphCacheBLF *gc, unsigned int c); -struct GlyphBLF *blf_glyph_add(struct FontBLF *font, - struct GlyphCacheBLF *gc, - unsigned int index, - unsigned int c); +struct GlyphBLF *blf_glyph_ensure(struct FontBLF *font, struct GlyphCacheBLF *gc, uint charcode); void blf_glyph_free(struct GlyphBLF *g); -void blf_glyph_render( +void blf_glyph_draw( struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, float x, float y); #ifdef WIN32 From 0e71162e68939d90ff92e7ada3bf4fc11e0953ea Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 29 Oct 2021 13:13:45 +1100 Subject: [PATCH 1359/1500] Cleanup: resolve cast warnings --- source/blender/blenfont/intern/blf_glyph.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index e077cc91f33..9170a1c0ac4 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -293,8 +293,8 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) g->advance_i = (int)g->advance; g->pos[0] = slot->bitmap_left; g->pos[1] = slot->bitmap_top; - g->dims[0] = slot->bitmap.width; - g->dims[1] = slot->bitmap.rows; + g->dims[0] = (int)slot->bitmap.width; + g->dims[1] = (int)slot->bitmap.rows; g->pitch = slot->bitmap.pitch; FT_BBox bbox; @@ -304,7 +304,7 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) g->box.ymin = ((float)bbox.yMin) / 64.0f; g->box.ymax = ((float)bbox.yMax) / 64.0f; - const int buffer_size = slot->bitmap.width * slot->bitmap.rows; + const int buffer_size = (int)(slot->bitmap.width * slot->bitmap.rows); if (buffer_size != 0) { if (font->flags & BLF_MONOCHROME) { /* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */ From 1e2589bfa580aaa087dd9a6dffd5e0785bb91322 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 29 Oct 2021 13:15:39 +1100 Subject: [PATCH 1360/1500] Cleanup: remove redundant BLI_UTF8_ERR check --- source/blender/blenfont/intern/blf_font.c | 26 ++++------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 52c412a42bb..4125dde1fd1 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -301,6 +301,9 @@ BLI_INLINE GlyphBLF *blf_utf8_next_fast( FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p) { uint charcode = BLI_str_utf8_as_unicode_step(str, str_len, i_p); + /* Invalid unicode sequences return the byte value, stepping forward one. + * This allows `latin1` to display (which is sometimes used for file-paths). */ + BLI_assert(charcode != BLI_UTF8_ERR); return blf_glyph_ensure(font, gc, charcode); } @@ -366,9 +369,6 @@ static void blf_font_draw_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (UNLIKELY(g->c == BLI_UTF8_ERR)) { - break; - } pen_x += blf_kerning(font, g_prev, g); /* do not return this loop if clipped, we want every character tested */ @@ -410,10 +410,6 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int if (UNLIKELY(g == NULL)) { continue; } - if (UNLIKELY(g->c == BLI_UTF8_ERR)) { - break; - } - /* do not return this loop if clipped, we want every character tested */ blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); @@ -466,9 +462,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (UNLIKELY(g->c == BLI_UTF8_ERR)) { - break; - } pen_x += blf_kerning(font, g_prev, g); chx = pen_x + ((int)g->pos[0]); @@ -600,12 +593,10 @@ static bool blf_font_width_to_strlen_glyph_process( if (UNLIKELY(g == NULL)) { return false; /* continue the calling loop. */ } - if (UNLIKELY(g->c == BLI_UTF8_ERR)) { - return true; /* break the calling loop. */ - } *pen_x += blf_kerning(font, g_prev, g); *pen_x += g->advance_i; + /* When true, break the calling loop. */ return (*pen_x >= width_i); } @@ -709,9 +700,6 @@ static void blf_font_boundbox_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (UNLIKELY(g->c == BLI_UTF8_ERR)) { - break; - } pen_x += blf_kerning(font, g_prev, g); gbox.xmin = (float)pen_x; @@ -880,9 +868,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (UNLIKELY(g->c == BLI_UTF8_ERR)) { - break; - } pen_x += blf_kerning(font, g_prev, g); gbox.xmin = pen_x; @@ -968,9 +953,6 @@ static void blf_font_wrap_apply(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (UNLIKELY(g->c == BLI_UTF8_ERR)) { - break; - } pen_x += blf_kerning(font, g_prev, g); /** From 38fc19d643f742657908a7a6a6ecb13d73045071 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 29 Oct 2021 13:49:31 +1100 Subject: [PATCH 1361/1500] Cleanup: rename blf_utf8_next_fast to blf_glyph_from_utf8_and_step Calling this 'fast' no longer made sense as the slower code-path has been removed. --- source/blender/blenfont/intern/blf_font.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 4125dde1fd1..90c8d6357de 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -297,7 +297,7 @@ static void blf_batch_draw_end(void) * characters. */ -BLI_INLINE GlyphBLF *blf_utf8_next_fast( +BLI_INLINE GlyphBLF *blf_glyph_from_utf8_and_step( FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p) { uint charcode = BLI_str_utf8_as_unicode_step(str, str_len, i_p); @@ -364,7 +364,7 @@ static void blf_font_draw_ex(FontBLF *font, blf_batch_draw_begin(font); while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); if (UNLIKELY(g == NULL)) { continue; @@ -405,7 +405,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int blf_batch_draw_begin(font); while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); if (UNLIKELY(g == NULL)) { continue; @@ -457,7 +457,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, /* another buffer specific call for color conversion */ while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); if (UNLIKELY(g == NULL)) { continue; @@ -612,7 +612,7 @@ size_t blf_font_width_to_strlen( for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL; (i < str_len) && str[i]; i_prev = i, width_new = pen_x, g_prev = g) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) { break; @@ -645,7 +645,7 @@ size_t blf_font_width_to_rstrlen( i_prev = (size_t)(s_prev - str); i_tmp = i; - g = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i_tmp); for (width_new = pen_x = 0; (s != NULL); i = i_prev, s = s_prev, g = g_prev, g_prev = NULL, width_new = pen_x) { s_prev = BLI_str_find_prev_char_utf8(s, str); @@ -653,7 +653,7 @@ size_t blf_font_width_to_rstrlen( if (s_prev != NULL) { i_tmp = i_prev; - g_prev = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp); + g_prev = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i_tmp); BLI_assert(i_tmp == i); } @@ -695,7 +695,7 @@ static void blf_font_boundbox_ex(FontBLF *font, box->ymax = -32000.0f; while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); if (UNLIKELY(g == NULL)) { continue; @@ -863,7 +863,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, while ((i < str_len) && str[i]) { i_curr = i; - g = blf_utf8_next_fast(font, gc, str, str_len, &i); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); if (UNLIKELY(g == NULL)) { continue; @@ -948,7 +948,7 @@ static void blf_font_wrap_apply(FontBLF *font, size_t i_curr = i; bool do_draw = false; - g = blf_utf8_next_fast(font, gc, str, str_len, &i); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); if (UNLIKELY(g == NULL)) { continue; From c8aaa00e0070e23ec5c4e949595a1af554639b99 Mon Sep 17 00:00:00 2001 From: Gilberto Rodrigues Date: Fri, 29 Oct 2021 13:59:04 +1100 Subject: [PATCH 1362/1500] Report "Startup file saved" in info editor This is consistent with saving the preferences. Ref D12896 --- source/blender/windowmanager/intern/wm_files.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 8fcc30dfed7..67222cc07f9 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -2142,7 +2142,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) } printf("ok\n"); - + BKE_report(op->reports, RPT_INFO, "Startup file saved"); G.save_over = 0; BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_POST); From b546202a9afa5ee358893d82e6a1ba0f69cca254 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 29 Oct 2021 14:58:15 +1100 Subject: [PATCH 1363/1500] Gizmo: support showing transform gizmos in pose + weight-paint mode These can be enabled in the gizmos popover. --- source/blender/editors/transform/transform_gizmo_3d.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index e4c20fa0be1..466c4202dbd 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -646,10 +646,8 @@ int ED_transform_calc_gizmo_stats(const bContext *C, Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = area->spacedata.first; - Object *obedit = CTX_data_edit_object(C); RegionView3D *rv3d = region->regiondata; Base *base; - Object *ob = OBACT(view_layer); bGPdata *gpd = CTX_data_gpencil_data(C); const bool is_gp_edit = GPENCIL_ANY_MODE(gpd); const bool is_curve_edit = GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); @@ -660,6 +658,15 @@ int ED_transform_calc_gizmo_stats(const bContext *C, (params->orientation_index - 1) : BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); + Object *ob = OBACT(view_layer); + Object *obedit = OBEDIT_FROM_OBACT(ob); + if (ob && ob->mode & OB_MODE_WEIGHT_PAINT) { + Object *obpose = BKE_object_pose_armature_get(ob); + if (obpose != NULL) { + ob = obpose; + } + } + /* transform widget matrix */ unit_m4(rv3d->twmat); From cf771807b7997949611dbf76b43150592c9977cb Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 29 Oct 2021 09:28:31 +0200 Subject: [PATCH 1364/1500] Geometry Nodes: do cache invalidation after writing attributes This is a better and more general fix for T92511 and T92508 than the ones that I committed before. Previously, we tagged caches dirty when first accessing attributes. This led to incorrect caches when under some circumstances. Now cache invalidation is part of `OutputAttribute.save()`. A nice side benefit of this change is that it may make things more efficient in some cases, because we don't invalidate caches when they don't have to be invalidated. Differential Revision: https://developer.blender.org/D13009 --- .../blenkernel/BKE_attribute_access.hh | 2 + source/blender/blenkernel/BKE_spline.hh | 2 + .../blenkernel/intern/attribute_access.cc | 43 ++++++++++--- .../intern/attribute_access_intern.hh | 4 +- .../blender/blenkernel/intern/curve_eval.cc | 7 +++ .../intern/geometry_component_curve.cc | 63 +++++++++++-------- .../intern/geometry_component_instances.cc | 18 +++--- .../intern/geometry_component_mesh.cc | 2 +- 8 files changed, 98 insertions(+), 43 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 38f5497ed92..cf19eb29a0e 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -183,6 +183,8 @@ struct WriteAttributeLookup { GVMutableArrayPtr varray; /* Domain the attributes lives on in the geometry. */ AttributeDomain domain; + /* Call this after changing the attribute to invalidate caches that depend on this attribute. */ + std::function tag_modified_fn; /* Convenience function to check if the attribute has been found. */ operator bool() const diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 97e0d8415a5..8509b730709 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -570,6 +570,8 @@ struct CurveEval { blender::Array evaluated_point_offsets() const; blender::Array accumulated_spline_lengths() const; + void mark_cache_invalid(); + void assert_valid_point_attributes() const; }; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 1ea7f522bef..01b53baf237 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -360,7 +360,7 @@ GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( return as_read_attribute_(data, domain_size); } -GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( +WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( GeometryComponent &component) const { if (writable_ != Writable) { @@ -397,10 +397,14 @@ GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( data = new_data; } + std::function tag_modified_fn; if (update_on_write_ != nullptr) { - update_on_write_(component); + tag_modified_fn = [component = &component, update = update_on_write_]() { + update(*component); + }; } - return as_write_attribute_(data, domain_size); + + return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)}; } bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const @@ -925,7 +929,7 @@ blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_writ const BuiltinAttributeProvider *builtin_provider = providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); if (builtin_provider != nullptr) { - return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; + return builtin_provider->try_get_for_write(*this); } } for (const DynamicAttributesProvider *dynamic_provider : @@ -1249,6 +1253,20 @@ static void save_output_attribute(OutputAttribute &output_attribute) varray.get(i, buffer); write_attribute.varray->set_by_relocate(i, buffer); } + if (write_attribute.tag_modified_fn) { + write_attribute.tag_modified_fn(); + } +} + +static std::function get_simple_output_attribute_save_method( + const blender::bke::WriteAttributeLookup &attribute) +{ + if (!attribute.tag_modified_fn) { + return {}; + } + return [tag_modified_fn = attribute.tag_modified_fn](OutputAttribute &UNUSED(attribute)) { + tag_modified_fn(); + }; } static OutputAttribute create_output_attribute(GeometryComponent &component, @@ -1293,14 +1311,21 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Builtin attribute is on different domain. */ return {}; } + GVMutableArrayPtr varray = std::move(attribute.varray); if (varray->type() == *cpp_type) { /* Builtin attribute matches exactly. */ - return OutputAttribute(std::move(varray), domain, {}, ignore_old_values); + return OutputAttribute(std::move(varray), + domain, + get_simple_output_attribute_save_method(attribute), + ignore_old_values); } /* Builtin attribute is on the same domain but has a different data type. */ varray = conversions.try_convert(std::move(varray), *cpp_type); - return OutputAttribute(std::move(varray), domain, {}, ignore_old_values); + return OutputAttribute(std::move(varray), + domain, + get_simple_output_attribute_save_method(attribute), + ignore_old_values); } const int domain_size = component.attribute_domain_size(domain); @@ -1324,7 +1349,11 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, } if (attribute.domain == domain && attribute.varray->type() == *cpp_type) { /* Existing generic attribute matches exactly. */ - return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values); + + return OutputAttribute(std::move(attribute.varray), + domain, + get_simple_output_attribute_save_method(attribute), + ignore_old_values); } /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 5cedcf69953..140498bdb01 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -87,7 +87,7 @@ class BuiltinAttributeProvider { } virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0; - virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0; + virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component) const = 0; virtual bool try_delete(GeometryComponent &component) const = 0; virtual bool try_create(GeometryComponent &UNUSED(component), const AttributeInit &UNUSED(initializer)) const = 0; @@ -267,7 +267,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { } GVArrayPtr try_get_for_read(const GeometryComponent &component) const final; - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final; + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final; bool try_delete(GeometryComponent &component) const final; bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final; bool exists(const GeometryComponent &component) const final; diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 8eec7f5dfab..0e3da9e0789 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -160,6 +160,13 @@ blender::Array CurveEval::accumulated_spline_lengths() const return spline_lengths; } +void CurveEval::mark_cache_invalid() +{ + for (SplinePtr &spline : splines_) { + spline->mark_cache_invalid(); + } +} + static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) { switch (dna_handle_type) { diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index d2a404b860a..d3c3fcc1e67 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -433,7 +433,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { return as_read_attribute_(*curve); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final { if (writable_ != Writable) { return {}; @@ -442,7 +442,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { if (curve == nullptr) { return {}; } - return as_write_attribute_(*curve); + return {as_write_attribute_(*curve), domain_}; } bool try_delete(GeometryComponent &UNUSED(component)) const final @@ -1122,7 +1122,7 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu return point_data_gvarray(spans, offsets); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { @@ -1133,25 +1133,30 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu return {}; } + std::function tag_modified_fn; + if (update_on_write_ != nullptr) { + tag_modified_fn = [curve, update = update_on_write_]() { + for (SplinePtr &spline : curve->splines()) { + update(*spline); + } + }; + } + MutableSpan splines = curve->splines(); if (splines.size() == 1) { - if (update_on_write_) { - update_on_write_(*splines[0]); - } - return std::make_unique( - get_mutable_span_(*splines.first())); + return {std::make_unique( + get_mutable_span_(*splines.first())), + domain_, + std::move(tag_modified_fn)}; } Array offsets = curve->control_point_offsets(); Array> spans(splines.size()); for (const int i : splines.index_range()) { spans[i] = get_mutable_span_(*splines[i]); - if (update_on_write_) { - update_on_write_(*splines[i]); - } } - return point_data_gvarray(spans, offsets); + return {point_data_gvarray(spans, offsets), domain_, tag_modified_fn}; } bool try_delete(GeometryComponent &component) const final @@ -1223,7 +1228,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider::try_get_for_write(component); } - /* Changing the positions requires recalculation of cached evaluated data in many cases. - * This could set more specific flags in the future to avoid unnecessary recomputation. */ - for (SplinePtr &spline : curve->splines()) { - spline->mark_cache_invalid(); - } + auto tag_modified_fn = [curve]() { + /* Changing the positions requires recalculation of cached evaluated data in many cases. + * This could set more specific flags in the future to avoid unnecessary recomputation. */ + curve->mark_cache_invalid(); + }; Array offsets = curve->control_point_offsets(); - return std::make_unique< - fn::GVMutableArray_For_EmbeddedVMutableArray>( - offsets.last(), curve->splines(), std::move(offsets)); + return {std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray>( + offsets.last(), curve->splines(), std::move(offsets)), + domain_, + tag_modified_fn}; } }; @@ -1281,7 +1289,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { offsets.last(), curve->splines(), std::move(offsets), is_right_); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { @@ -1292,10 +1300,15 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { return {}; } + auto tag_modified_fn = [curve]() { curve->mark_cache_invalid(); }; + Array offsets = curve->control_point_offsets(); - return std::make_unique< - fn::GVMutableArray_For_EmbeddedVMutableArray>( - offsets.last(), curve->splines(), std::move(offsets), is_right_); + return { + std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray>( + offsets.last(), curve->splines(), std::move(offsets), is_right_), + domain_, + tag_modified_fn}; } bool try_delete(GeometryComponent &UNUSED(component)) const final diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index d02121b44a6..5fe77000519 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -398,15 +398,16 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider transforms); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final { InstancesComponent &instances_component = static_cast(component); MutableSpan transforms = instances_component.instance_transforms(); - return std::make_unique>( - transforms); + return { + std::make_unique>(transforms), + domain_}; } bool try_delete(GeometryComponent &UNUSED(component)) const final @@ -443,13 +444,14 @@ class InstanceIDAttributeProvider final : public BuiltinAttributeProvider { return std::make_unique>(instances.instance_ids()); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final { InstancesComponent &instances = static_cast(component); if (instances.instance_ids().is_empty()) { return {}; } - return std::make_unique>(instances.instance_ids()); + return {std::make_unique>(instances.instance_ids()), + domain_}; } bool try_delete(GeometryComponent &component) const final diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 6091d3f3dab..c3e39c0b2cb 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -1210,7 +1210,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { return std::make_unique>>(std::move(normals)); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &UNUSED(component)) const final { return {}; } From 2887d872320efc50d377ebe299c7e0beaedb67d8 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 29 Oct 2021 10:10:58 +0200 Subject: [PATCH 1365/1500] Fix T92324: crash caused by recursive instancing This fixes one (of possibly multiple) root issues. The collection passed into the Collection Info node must not contain the current object, because that would result in a dependency cycle and recursive instancing. --- .../nodes/geometry/nodes/node_geo_collection_info.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index eca4e3d2d14..18fc09daf01 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -71,6 +71,14 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set_out); return; } + const Object *self_object = params.self_object(); + const bool is_recursive = BKE_collection_has_object_recursive_instanced(collection, + (Object *)self_object); + if (is_recursive) { + params.error_message_add(NodeWarningType::Error, "Collection contains current object"); + params.set_output("Geometry", geometry_set_out); + return; + } const bNode &bnode = params.node(); NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage; @@ -79,8 +87,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) InstancesComponent &instances = geometry_set_out.get_component_for_write(); - const Object *self_object = params.self_object(); - const bool separate_children = params.get_input("Separate Children"); if (separate_children) { const bool reset_children = params.get_input("Reset Children"); From be0d5da341cf3f33ba2d7cca8c979abf9d94f683 Mon Sep 17 00:00:00 2001 From: Ankit Meel Date: Fri, 29 Oct 2021 10:11:04 +0200 Subject: [PATCH 1366/1500] Fix T88877: 2.93: Crash on recent OSX with a non-English locale. Looks like OSX changed the default format of its locale, which is not valid anymore for gettext/boost::locale. Solution based on investigations and patch by Kieun Mun (@kieuns), with some further tweaks by Ankit Meel (@ankitm), many thanks. Also add an exception catcher on `std::runtime_error` in `bl_locale_set()`, since in OSX catching the ancestor `std::exception` does not work with `boost::locale::conv::conversion_error` and the like for some reasons. Reviewed By: #platform_macos, brecht Maniphest Tasks: T88877 Differential Revision: https://developer.blender.org/D13019 --- intern/locale/boost_locale_wrapper.cpp | 7 +++++++ intern/locale/osx_user_locale.mm | 12 +++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/intern/locale/boost_locale_wrapper.cpp b/intern/locale/boost_locale_wrapper.cpp index 73433fe7c5e..ede9377b38f 100644 --- a/intern/locale/boost_locale_wrapper.cpp +++ b/intern/locale/boost_locale_wrapper.cpp @@ -117,6 +117,13 @@ void bl_locale_set(const char *locale) #undef LOCALE_INFO } + // Extra catch on `std::runtime_error` is needed for macOS/Clang as it seems that exceptions + // like `boost::locale::conv::conversion_error` (which inherit from `std::runtime_error`) are + // not caught by their ancestor `std::exception`. See + // https://developer.blender.org/T88877#1177108 . + catch (std::runtime_error const &e) { + std::cout << "bl_locale_set(" << locale << "): " << e.what() << " \n"; + } catch (std::exception const &e) { std::cout << "bl_locale_set(" << locale << "): " << e.what() << " \n"; } diff --git a/intern/locale/osx_user_locale.mm b/intern/locale/osx_user_locale.mm index e2f65d39df9..ce694b5fc1e 100644 --- a/intern/locale/osx_user_locale.mm +++ b/intern/locale/osx_user_locale.mm @@ -14,7 +14,17 @@ const char *osx_user_locale() CFLocaleRef myCFLocale = CFLocaleCopyCurrent(); NSLocale *myNSLocale = (NSLocale *)myCFLocale; [myNSLocale autorelease]; - NSString *nsIdentifier = [myNSLocale localeIdentifier]; + + // This produces gettext-invalid locale in recent macOS versions (11.4), + // like `ko-Kore_KR` instead of `ko_KR`. See T88877. + // NSString *nsIdentifier = [myNSLocale localeIdentifier]; + + const NSString *nsIdentifier = [myNSLocale languageCode]; + const NSString *const nsIdentifier_country = [myNSLocale countryCode]; + if ([nsIdentifier length] != 0 && [nsIdentifier_country length] != 0) { + nsIdentifier = [NSString stringWithFormat:@"%@_%@", nsIdentifier, nsIdentifier_country]; + } + user_locale = ::strdup([nsIdentifier UTF8String]); [pool drain]; From 7c860ab9d4b54f9c394d3c2769927f55c6737b5f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 29 Oct 2021 10:29:38 +0200 Subject: [PATCH 1367/1500] Fix T92499: duplicating geometry nodes modifier causes crash Calling `remove_unused_references` inside the `modify_geometry_sets` loop was known to be not entirely reliable before. Now I just moved it out of the loop which fixes the bug. --- .../geometry/nodes/node_geo_instance_on_points.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index f4a127faf43..1e94a4a6a50 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -202,12 +202,15 @@ static void geo_node_instance_on_points_exec(GeoNodeExecParams params) instances, *geometry_set.get_component_for_read(), instance, params); geometry_set.remove(GEO_COMPONENT_TYPE_CURVE); } - /* Unused references may have been added above. Remove those now so that other nodes don't - * process them needlessly. */ - /** \note: This currently expects that all originally existing instances were used. */ - instances.remove_unused_references(); }); + /* Unused references may have been added above. Remove those now so that other nodes don't + * process them needlessly. + * This should eventually be moved into the loop above, but currently this is quite tricky + * because it might remove references that the loop still wants to iterate over. */ + InstancesComponent &instances = geometry_set.get_component_for_write(); + instances.remove_unused_references(); + params.set_output("Instances", std::move(geometry_set)); } From 2d42dc4182ffe6fbb8bd48af2e1a4576efcaf4af Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 29 Oct 2021 10:39:05 +0200 Subject: [PATCH 1368/1500] Cleanup: remove unused function --- release/scripts/startup/bl_operators/spreadsheet.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/release/scripts/startup/bl_operators/spreadsheet.py b/release/scripts/startup/bl_operators/spreadsheet.py index b5098d63dac..ba0c9969356 100644 --- a/release/scripts/startup/bl_operators/spreadsheet.py +++ b/release/scripts/startup/bl_operators/spreadsheet.py @@ -50,19 +50,6 @@ class SPREADSHEET_OT_toggle_pin(Operator): space.is_pinned = False space.context_path.guess() - def find_geometry_node_editors(self, context): - editors = [] - for window in context.window_manager.windows: - for area in window.screen.areas: - space = area.spaces.active - if space.type != 'NODE_EDITOR': - continue - if space.edit_tree is None: - continue - if space.edit_tree.type == 'GEOMETRY': - editors.append(space) - return editors - classes = ( SPREADSHEET_OT_toggle_pin, From 43bc494892c3551222601122c880a4da87354634 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 28 Oct 2021 11:22:43 +0200 Subject: [PATCH 1369/1500] IDManagement: Remove deprecated `BKE_libblock_relink_to_newid` usages. Move all usages to new `BKE_libblock_relink_to_newid_new`, and rename that one to `BKE_libblock_relink_to_newid`. Fix T91413. --- source/blender/blenkernel/BKE_lib_remap.h | 3 +- source/blender/blenkernel/intern/collection.c | 2 +- source/blender/blenkernel/intern/lib_remap.c | 59 ++----------------- source/blender/blenkernel/intern/object.c | 2 +- source/blender/blenkernel/intern/scene.c | 2 +- source/blender/editors/object/object_add.c | 6 +- .../blender/editors/object/object_relations.c | 14 +++-- .../windowmanager/intern/wm_files_link.c | 2 +- 8 files changed, 22 insertions(+), 68 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index c70521f9593..8df6a803358 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -111,8 +111,7 @@ void BKE_libblock_relink_ex(struct Main *bmain, void *new_idv, const short remap_flags) ATTR_NONNULL(1, 2); -void BKE_libblock_relink_to_newid(struct ID *id) ATTR_NONNULL(); -void BKE_libblock_relink_to_newid_new(struct Main *bmain, struct ID *id) ATTR_NONNULL(); +void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id) ATTR_NONNULL(); typedef void (*BKE_library_free_notifier_reference_cb)(const void *); typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 2dca5dcb75d..14097ecd8a7 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -716,7 +716,7 @@ Collection *BKE_collection_duplicate(Main *bmain, collection_new->id.tag &= ~LIB_TAG_NEW; /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(&collection_new->id); + BKE_libblock_relink_to_newid(bmain, &collection_new->id); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 905ac5af512..248d85bcae0 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -669,55 +669,8 @@ void BKE_libblock_relink_ex( DEG_relations_tag_update(bmain); } +static void libblock_relink_to_newid(Main *bmain, ID *id); static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data) -{ - const int cb_flag = cb_data->cb_flag; - if (cb_flag & IDWALK_CB_EMBEDDED) { - return IDWALK_RET_NOP; - } - - ID **id_pointer = cb_data->id_pointer; - ID *id = *id_pointer; - if (id) { - /* See: NEW_ID macro */ - if (id->newid) { - BKE_library_update_ID_link_user(id->newid, id, cb_flag); - id = id->newid; - *id_pointer = id; - } - if (id->tag & LIB_TAG_NEW) { - id->tag &= ~LIB_TAG_NEW; - BKE_libblock_relink_to_newid(id); - } - } - return IDWALK_RET_NOP; -} - -/** - * Similar to #libblock_relink_ex, - * but is remapping IDs to their newid value if non-NULL, in given \a id. - * - * Very specific usage, not sure we'll keep it on the long run, - * currently only used in Object/Collection duplication code... - * - * WARNING: This is a deprecated version of this function, should not be used by new code. See - * #BKE_libblock_relink_to_newid_new below. - */ -void BKE_libblock_relink_to_newid(ID *id) -{ - if (ID_IS_LINKED(id)) { - return; - } - - BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0); -} - -/* ************************ - * FIXME: Port all usages of #BKE_libblock_relink_to_newid to this - * #BKE_libblock_relink_to_newid_new new code and remove old one. - ************************** */ -static void libblock_relink_to_newid_new(Main *bmain, ID *id); -static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) { const int cb_flag = cb_data->cb_flag; if (cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { @@ -740,20 +693,20 @@ static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) } if (id->tag & LIB_TAG_NEW) { id->tag &= ~LIB_TAG_NEW; - libblock_relink_to_newid_new(bmain, id); + libblock_relink_to_newid(bmain, id); } } return IDWALK_RET_NOP; } -static void libblock_relink_to_newid_new(Main *bmain, ID *id) +static void libblock_relink_to_newid(Main *bmain, ID *id) { if (ID_IS_LINKED(id)) { return; } id->tag &= ~LIB_TAG_NEW; - BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0); + BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper, NULL, 0); } /** @@ -765,7 +718,7 @@ static void libblock_relink_to_newid_new(Main *bmain, ID *id) * Very specific usage, not sure we'll keep it on the long run, * currently only used in Object/Collection duplication code... */ -void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id) +void BKE_libblock_relink_to_newid(Main *bmain, ID *id) { if (ID_IS_LINKED(id)) { return; @@ -774,7 +727,7 @@ void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id) BLI_assert(bmain->relations == NULL); BKE_layer_collection_resync_forbid(); - libblock_relink_to_newid_new(bmain, id); + libblock_relink_to_newid(bmain, id); BKE_layer_collection_resync_allow(); BKE_main_collection_sync_remap(bmain); } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index a55bb32f927..ed4b4e90690 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2858,7 +2858,7 @@ Object *BKE_object_duplicate(Main *bmain, if (!is_subprocess) { /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(&obn->id); + BKE_libblock_relink_to_newid(bmain, &obn->id); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index d7fc2005ab2..b8f985e7d01 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1853,7 +1853,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) if (!is_subprocess) { /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(&sce_copy->id); + BKE_libblock_relink_to_newid(bmain, &sce_copy->id); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 90caeecd91f..d22ae5bc804 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2145,7 +2145,7 @@ static void copy_object_set_idnew(bContext *C) Main *bmain = CTX_data_main(C); CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - BKE_libblock_relink_to_newid(&ob->id); + BKE_libblock_relink_to_newid(bmain, &ob->id); } CTX_DATA_END; @@ -2378,7 +2378,7 @@ static void make_object_duplilist_real(bContext *C, Object *ob_dst = BLI_ghash_lookup(dupli_gh, dob); /* Remap new object to itself, and clear again newid pointer of orig object. */ - BKE_libblock_relink_to_newid(&ob_dst->id); + BKE_libblock_relink_to_newid(bmain, &ob_dst->id); DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY); @@ -3375,7 +3375,7 @@ Base *ED_object_add_duplicate( ob = basen->object; /* link own references to the newly duplicated data T26816. */ - BKE_libblock_relink_to_newid(&ob->id); + BKE_libblock_relink_to_newid(bmain, &ob->id); /* DAG_relations_tag_update(bmain); */ /* caller must do */ diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index aa15ce36582..acd3f058554 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1685,18 +1685,20 @@ static bool single_data_needs_duplication(ID *id) return (id != NULL && (id->us > 1 || ID_IS_LINKED(id))); } -static void libblock_relink_collection(Collection *collection, const bool do_collection) +static void libblock_relink_collection(Main *bmain, + Collection *collection, + const bool do_collection) { if (do_collection) { - BKE_libblock_relink_to_newid(&collection->id); + BKE_libblock_relink_to_newid(bmain, &collection->id); } for (CollectionObject *cob = collection->gobject.first; cob != NULL; cob = cob->next) { - BKE_libblock_relink_to_newid(&cob->ob->id); + BKE_libblock_relink_to_newid(bmain, &cob->ob->id); } LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - libblock_relink_collection(child->collection, true); + libblock_relink_collection(bmain, child->collection, true); } } @@ -1766,10 +1768,10 @@ static void single_object_users( single_object_users_collection(bmain, scene, master_collection, flag, copy_collections, true); /* Will also handle the master collection. */ - BKE_libblock_relink_to_newid(&scene->id); + BKE_libblock_relink_to_newid(bmain, &scene->id); /* Collection and object pointers in collections */ - libblock_relink_collection(scene->master_collection, false); + libblock_relink_collection(bmain, scene->master_collection, false); /* We also have to handle runtime things in UI. */ if (v3d) { diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 97e610b797d..7d74ac9605b 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -812,7 +812,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, BLI_assert(!ID_IS_LINKED(id)); - BKE_libblock_relink_to_newid_new(bmain, id); + BKE_libblock_relink_to_newid(bmain, id); } /* Remove linked IDs when a local existing data has been reused instead. */ From f13826a572f76fe7a359e84f003c667d3b801725 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 29 Oct 2021 11:04:37 +0200 Subject: [PATCH 1370/1500] Fix T92476: wrong context in spreadsheet when area is maximized Now, if a spreadsheet editor is maximized, it looks for its context in the unmaximized screen "below". --- .../space_spreadsheet/spreadsheet_context.cc | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc index c38e765caee..e55a7cae6a6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc @@ -373,6 +373,21 @@ void ED_spreadsheet_context_path_set_evaluated_object(SpaceSpreadsheet *sspreads BLI_addtail(&sspreadsheet->context_path, context); } +static bScreen *find_screen_to_search_for_context(wmWindow *window, + SpaceSpreadsheet *current_space) +{ + bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + if (ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL)) { + /* If the spreadsheet is maximized, try to find the context in the unmaximized screen. */ + ScrArea *main_area = (ScrArea *)screen->areabase.first; + SpaceLink *sl = (SpaceLink *)main_area->spacedata.first; + if (sl == (SpaceLink *)current_space) { + return main_area->full; + } + } + return screen; +} + void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspreadsheet) { ED_spreadsheet_context_path_clear(sspreadsheet); @@ -385,9 +400,12 @@ void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspr if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { - bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet); LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { SpaceLink *sl = (SpaceLink *)area->spacedata.first; + if (sl == nullptr) { + continue; + } if (sl->spacetype == SPACE_NODE) { SpaceNode *snode = (SpaceNode *)sl; if (snode->edittree != nullptr) { @@ -466,9 +484,12 @@ bool ED_spreadsheet_context_path_is_active(const bContext *C, SpaceSpreadsheet * } LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { - bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet); LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { SpaceLink *sl = (SpaceLink *)area->spacedata.first; + if (sl == nullptr) { + continue; + } if (sl->spacetype != SPACE_NODE) { continue; } From eae59645def368a46c546a5d88db4b885f707b9b Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 26 Oct 2021 17:22:10 +0200 Subject: [PATCH 1371/1500] Cleanup: Add some comments to some sub-function of `foreach_id` process. --- source/blender/blenkernel/intern/lib_query.c | 4 ++++ source/blender/blenkernel/intern/screen.c | 18 ++++-------------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 4165452801c..317621e1149 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -210,6 +210,10 @@ static void library_foreach_ID_link(Main *bmain, flag |= IDWALK_READONLY; flag &= ~IDWALK_DO_INTERNAL_RUNTIME_POINTERS; + /* NOTE: This function itself should never be called recursively when IDWALK_RECURSE is set, + * see also comments in #BKE_library_foreach_ID_embedded. + * This is why we can always create this data here, and do not need to try and re-use it from + * `inherit_data`. */ data.ids_handled = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); BLI_LINKSTACK_INIT(data.ids_todo); diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 3e1a2a9bbe9..1ddfd4e47ac 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -97,6 +97,10 @@ static void screen_foreach_id_dopesheet(LibraryForeachIDData *data, bDopeSheet * } } +/** + * Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of + * `IDTypeInfo` structure). + */ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, area->full, IDWALK_CB_NOP); @@ -107,10 +111,8 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area switch (sl->spacetype) { case SPACE_VIEW3D: { View3D *v3d = (View3D *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->camera, IDWALK_CB_NOP); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->ob_center, IDWALK_CB_NOP); - if (v3d->localvd) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->localvd->camera, IDWALK_CB_NOP); } @@ -124,7 +126,6 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_PROPERTIES: { SpaceProperties *sbuts = (SpaceProperties *)sl; - BKE_LIB_FOREACHID_PROCESS_ID(data, sbuts->pinid, IDWALK_CB_NOP); break; } @@ -132,14 +133,12 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area break; case SPACE_ACTION: { SpaceAction *saction = (SpaceAction *)sl; - screen_foreach_id_dopesheet(data, &saction->ads); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, saction->action, IDWALK_CB_NOP); break; } case SPACE_IMAGE: { SpaceImage *sima = (SpaceImage *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->image, IDWALK_CB_USER_ONE); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->mask_info.mask, IDWALK_CB_USER_ONE); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->gpd, IDWALK_CB_USER); @@ -147,7 +146,6 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_SEQ: { SpaceSeq *sseq = (SpaceSeq *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sseq->gpd, IDWALK_CB_USER); break; } @@ -159,21 +157,17 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_TEXT: { SpaceText *st = (SpaceText *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, st->text, IDWALK_CB_NOP); break; } case SPACE_SCRIPT: { SpaceScript *scpt = (SpaceScript *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scpt->script, IDWALK_CB_NOP); break; } case SPACE_OUTLINER: { SpaceOutliner *space_outliner = (SpaceOutliner *)sl; - BKE_LIB_FOREACHID_PROCESS_ID(data, space_outliner->search_tse.id, IDWALK_CB_NOP); - if (space_outliner->treestore != NULL) { TreeStoreElem *tselem; BLI_mempool_iter iter; @@ -187,13 +181,11 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_NODE: { SpaceNode *snode = (SpaceNode *)sl; - const bool is_private_nodetree = snode->id != NULL && ntreeFromID(snode->id) == snode->nodetree; BKE_LIB_FOREACHID_PROCESS_ID(data, snode->id, IDWALK_CB_NOP); BKE_LIB_FOREACHID_PROCESS_ID(data, snode->from, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, snode->nodetree, is_private_nodetree ? IDWALK_CB_EMBEDDED : IDWALK_CB_USER_ONE); @@ -219,14 +211,12 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_CLIP: { SpaceClip *sclip = (SpaceClip *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->clip, IDWALK_CB_USER_ONE); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE); break; } case SPACE_SPREADSHEET: { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { if (context->type == SPREADSHEET_CONTEXT_OBJECT) { BKE_LIB_FOREACHID_PROCESS_IDSUPER( From 51c1c1cd938f990333b09d89fb063bb28864b302 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 26 Oct 2021 17:23:38 +0200 Subject: [PATCH 1372/1500] Fix potential early-return in WM foreach_id process. Add a function to check if iteration over ID usages should stop (using internal `IDWALK_STOP` status flag). Use it in `BKE_LIB_FOREACHID_PROCESS_` macros, and in `window_manager_foreach_id` to handle properly the active workspace case (previous code could skip the call to `BKE_workspace_active_set` in case iteration over ID usages was stopped by callback on that specific ID usage). Part of T90922: Fix return policy inconsistency in `scene_foreach_id`. --- source/blender/blenkernel/BKE_lib_query.h | 7 +++++-- source/blender/blenkernel/intern/lib_query.c | 7 +++++++ source/blender/windowmanager/intern/wm.c | 6 +++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index 957a623577e..093d3fb12a6 100644 --- a/source/blender/blenkernel/BKE_lib_query.h +++ b/source/blender/blenkernel/BKE_lib_query.h @@ -143,6 +143,7 @@ enum { typedef struct LibraryForeachIDData LibraryForeachIDData; +bool BKE_lib_query_foreachid_iter_stop(struct LibraryForeachIDData *data); bool BKE_lib_query_foreachid_process(struct LibraryForeachIDData *data, struct ID **id_pp, int cb_flag); @@ -154,7 +155,8 @@ int BKE_lib_query_foreachid_process_callback_flag_override(struct LibraryForeach #define BKE_LIB_FOREACHID_PROCESS_ID(_data, _id, _cb_flag) \ { \ CHECK_TYPE_ANY((_id), ID *, void *); \ - if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id), (_cb_flag))) { \ + BKE_lib_query_foreachid_process((_data), (ID **)&(_id), (_cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ return; \ } \ } \ @@ -163,7 +165,8 @@ int BKE_lib_query_foreachid_process_callback_flag_override(struct LibraryForeach #define BKE_LIB_FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag) \ { \ CHECK_TYPE(&((_id_super)->id), ID *); \ - if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag))) { \ + BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ return; \ } \ } \ diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 317621e1149..79b093bedbe 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -76,6 +76,13 @@ typedef struct LibraryForeachIDData { BLI_LINKSTACK_DECLARE(ids_todo, ID *); } LibraryForeachIDData; +/** Check whether current iteration over ID usages should be stopped or not. + * \return true if the iteration should be stopped, false otherwise. */ +bool BKE_lib_query_foreachid_iter_stop(LibraryForeachIDData *data) +{ + return (data->status & IDWALK_STOP) != 0; +} + bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int cb_flag) { if (!(data->status & IDWALK_STOP)) { diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 4458b386ab6..aa439f619be 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -91,10 +91,14 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data) /* This pointer can be NULL during old files reading, better be safe than sorry. */ if (win->workspace_hook != NULL) { ID *workspace = (ID *)BKE_workspace_active_get(win->workspace_hook); - BKE_LIB_FOREACHID_PROCESS_ID(data, workspace, IDWALK_CB_NOP); + BKE_lib_query_foreachid_process(data, &workspace, IDWALK_CB_USER); /* Allow callback to set a different workspace. */ BKE_workspace_active_set(win->workspace_hook, (WorkSpace *)workspace); + if (BKE_lib_query_foreachid_iter_stop(data)) { + return; + } } + if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) { LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) { BKE_screen_foreach_id_screen_area(data, area); From e3b2f0fd6ff912bac69a94e35ac2f617c720328e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 27 Oct 2021 11:30:43 +0200 Subject: [PATCH 1373/1500] LibQuery: Add macro to help break looping when requested. The new `BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL` execute the given statement and then check status of `LibraryForeachIDData` data, and return in case stop of iteration is requested. This is very similar to the other `BKE_LIB_FOREACHID_PROCESS_` existing macros, and allows us to properly break iteration when a sub-function has requested it. Part of T90922: Fix return policy inconsistency in `scene_foreach_id`. --- source/blender/blenkernel/BKE_lib_query.h | 9 ++ source/blender/blenkernel/intern/action.c | 2 +- source/blender/blenkernel/intern/anim_data.c | 2 +- source/blender/blenkernel/intern/armature.c | 20 ++-- source/blender/blenkernel/intern/brush.c | 5 +- source/blender/blenkernel/intern/fcurve.c | 12 +- source/blender/blenkernel/intern/light.c | 3 +- source/blender/blenkernel/intern/linestyle.c | 6 +- source/blender/blenkernel/intern/material.c | 5 +- source/blender/blenkernel/intern/nla.c | 4 +- source/blender/blenkernel/intern/node.cc | 22 ++-- source/blender/blenkernel/intern/object.c | 48 +++++--- source/blender/blenkernel/intern/particle.c | 3 +- source/blender/blenkernel/intern/scene.c | 108 ++++++++++++------ source/blender/blenkernel/intern/screen.c | 19 +-- .../blender/blenkernel/intern/simulation.cc | 3 +- source/blender/blenkernel/intern/texture.c | 3 +- source/blender/blenkernel/intern/world.c | 3 +- source/blender/windowmanager/intern/wm.c | 3 +- 19 files changed, 186 insertions(+), 94 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index 093d3fb12a6..364a9056dd4 100644 --- a/source/blender/blenkernel/BKE_lib_query.h +++ b/source/blender/blenkernel/BKE_lib_query.h @@ -172,6 +172,15 @@ int BKE_lib_query_foreachid_process_callback_flag_override(struct LibraryForeach } \ ((void)0) +#define BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(_data, _func_call) \ + { \ + _func_call; \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ + return; \ + } \ + } \ + ((void)0) + bool BKE_library_foreach_ID_embedded(struct LibraryForeachIDData *data, struct ID **id_pp); void BKE_lib_query_idpropertiesForeachIDLink_callback(struct IDProperty *id_prop, void *user_data); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 7403b7f2109..cae72ddf68c 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -175,7 +175,7 @@ static void action_foreach_id(ID *id, LibraryForeachIDData *data) bAction *act = (bAction *)id; LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { - BKE_fcurve_foreach_id(fcu, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 23d2c4fe55f..21887d514d9 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -294,7 +294,7 @@ bool BKE_animdata_id_is_animated(const struct ID *id) void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data) { LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { - BKE_fcurve_foreach_id(fcu, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->action, IDWALK_CB_USER); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index b64b050f4e7..b830c9de5f5 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -161,30 +161,36 @@ static void armature_free_data(struct ID *id) static void armature_foreach_id_bone(Bone *bone, LibraryForeachIDData *data) { - IDP_foreach_property( - bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property( + bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data)); LISTBASE_FOREACH (Bone *, curbone, &bone->childbase) { - armature_foreach_id_bone(curbone, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_bone(curbone, data)); } } static void armature_foreach_id_editbone(EditBone *edit_bone, LibraryForeachIDData *data) { - IDP_foreach_property( - edit_bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(edit_bone->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); } static void armature_foreach_id(ID *id, LibraryForeachIDData *data) { bArmature *arm = (bArmature *)id; LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { - armature_foreach_id_bone(bone, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_bone(bone, data)); } if (arm->edbo != NULL) { LISTBASE_FOREACH (EditBone *, edit_bone, arm->edbo) { - armature_foreach_id_editbone(edit_bone, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_editbone(edit_bone, data)); } } } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 7d217d6907d..dc3c2a8e55e 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -213,8 +213,9 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data) if (brush->gpencil_settings) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material, IDWALK_CB_USER); } - BKE_texture_mtex_foreach_id(data, &brush->mtex); - BKE_texture_mtex_foreach_id(data, &brush->mask_mtex); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_texture_mtex_foreach_id(data, &brush->mtex)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + BKE_texture_mtex_foreach_id(data, &brush->mask_mtex)); } static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 1564eb3aa7b..bbf61c51bfb 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -205,12 +205,16 @@ void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data) FMod_Python *fcm_py = (FMod_Python *)fcm->data; BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fcm_py->script, IDWALK_CB_NOP); - IDP_foreach_property(fcm_py->prop, - IDP_TYPE_FILTER_ID, - BKE_lib_query_idpropertiesForeachIDLink_callback, - data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(fcm_py->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); break; } + default: + break; } } } diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index a6150028f46..05e8d4fe978 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -129,7 +129,8 @@ static void light_foreach_id(ID *id, LibraryForeachIDData *data) Light *lamp = (Light *)id; if (lamp->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&lamp->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&lamp->nodetree)); } } diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index a1c93920731..3c305d1fb3f 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -155,12 +155,14 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) for (int i = 0; i < MAX_MTEX; i++) { if (linestyle->mtex[i]) { - BKE_texture_mtex_foreach_id(data, linestyle->mtex[i]); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_texture_mtex_foreach_id(data, linestyle->mtex[i])); } } if (linestyle->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&linestyle->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&linestyle->nodetree)); } LISTBASE_FOREACH (LineStyleModifier *, lsm, &linestyle->color_modifiers) { diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index d3b34639d8a..5f726defb1a 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -166,9 +166,8 @@ static void material_foreach_id(ID *id, LibraryForeachIDData *data) { Material *material = (Material *)id; /* Nodetrees **are owned by IDs**, treat them as mere sub-data and not real ID! */ - if (!BKE_library_foreach_ID_embedded(data, (ID **)&material->nodetree)) { - return; - } + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&material->nodetree)); if (material->texpaintslot != NULL) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->texpaintslot->ima, IDWALK_CB_NOP); } diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index ef84afd8668..124db07298d 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -491,11 +491,11 @@ void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, strip->act, IDWALK_CB_USER); LISTBASE_FOREACH (FCurve *, fcu, &strip->fcurves) { - BKE_fcurve_foreach_id(fcu, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } LISTBASE_FOREACH (NlaStrip *, substrip, &strip->strips) { - BKE_nla_strip_foreach_id(substrip, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_nla_strip_foreach_id(substrip, data)); } } diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3297bf29ee4..55e8bdb2483 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -307,8 +307,10 @@ static void ntree_free_data(ID *id) static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket *sock) { - IDP_foreach_property( - sock->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property( + sock->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data)); switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { @@ -360,21 +362,25 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data) LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { BKE_LIB_FOREACHID_PROCESS_ID(data, node->id, IDWALK_CB_USER); - IDP_foreach_property( - node->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(node->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } } LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index ed4b4e90690..3b0825fb8db 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -389,7 +389,8 @@ static void library_foreach_modifiersForeachIDLink(void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data, @@ -398,7 +399,8 @@ static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_shaderfxForeachIDLink(void *user_data, @@ -407,7 +409,8 @@ static void library_foreach_shaderfxForeachIDLink(void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), @@ -417,7 +420,8 @@ static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; const int cb_flag = is_reference ? IDWALK_CB_USER : IDWALK_CB_NOP; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(psys), @@ -426,7 +430,8 @@ static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(p int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void object_foreach_id(ID *id, LibraryForeachIDData *data) @@ -493,10 +498,18 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( data, proxy_cb_flag, false); LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { - IDP_foreach_property( - pchan->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(pchan->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pchan->custom, IDWALK_CB_USER); - BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_constraints_id_loop( + &pchan->constraints, library_foreach_constraintObjectLooper, data)); } BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); } @@ -508,14 +521,21 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); } - BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data); - BKE_gpencil_modifiers_foreach_ID_link( - object, library_foreach_gpencil_modifiersForeachIDLink, data); - BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, data); - BKE_shaderfx_foreach_ID_link(object, library_foreach_shaderfxForeachIDLink, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_gpencil_modifiers_foreach_ID_link( + object, library_foreach_gpencil_modifiersForeachIDLink, data)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, data)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_shaderfx_foreach_ID_link(object, library_foreach_shaderfxForeachIDLink, data)); LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { - BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data)); } if (object->soft) { diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index feb997a4c5d..5b62761bd91 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -180,7 +180,8 @@ static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data) for (int i = 0; i < MAX_MTEX; i++) { if (psett->mtex[i]) { - BKE_texture_mtex_foreach_id(data, psett->mtex[i]); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + BKE_texture_mtex_foreach_id(data, psett->mtex[i])); } } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index b8f985e7d01..c0fd0423475 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -471,7 +471,8 @@ static void scene_foreach_rigidbodyworldSceneLooper(struct RigidBodyWorld *UNUSE int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } /** @@ -629,16 +630,28 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, IDWALK_CB_USER); if (toolsett->vpaint) { - scene_foreach_paint( - data, &toolsett->vpaint->paint, do_undo_restore, reader, &toolsett_old->vpaint->paint); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + scene_foreach_paint(data, + &toolsett->vpaint->paint, + do_undo_restore, + reader, + &toolsett_old->vpaint->paint)); } if (toolsett->wpaint) { - scene_foreach_paint( - data, &toolsett->wpaint->paint, do_undo_restore, reader, &toolsett_old->wpaint->paint); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + scene_foreach_paint(data, + &toolsett->wpaint->paint, + do_undo_restore, + reader, + &toolsett_old->wpaint->paint)); } if (toolsett->sculpt) { - scene_foreach_paint( - data, &toolsett->sculpt->paint, do_undo_restore, reader, &toolsett_old->sculpt->paint); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + scene_foreach_paint(data, + &toolsett->sculpt->paint, + do_undo_restore, + reader, + &toolsett_old->sculpt->paint)); BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, toolsett->sculpt->gravity_object, do_undo_restore, @@ -648,33 +661,47 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, IDWALK_CB_NOP); } if (toolsett->uvsculpt) { - scene_foreach_paint( - data, &toolsett->uvsculpt->paint, do_undo_restore, reader, &toolsett_old->uvsculpt->paint); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + scene_foreach_paint(data, + &toolsett->uvsculpt->paint, + do_undo_restore, + reader, + &toolsett_old->uvsculpt->paint)); } if (toolsett->gp_paint) { - scene_foreach_paint( - data, &toolsett->gp_paint->paint, do_undo_restore, reader, &toolsett_old->gp_paint->paint); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + scene_foreach_paint(data, + &toolsett->gp_paint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_paint->paint)); } if (toolsett->gp_vertexpaint) { - scene_foreach_paint(data, - &toolsett->gp_vertexpaint->paint, - do_undo_restore, - reader, - &toolsett_old->gp_vertexpaint->paint); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + scene_foreach_paint(data, + &toolsett->gp_vertexpaint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_vertexpaint->paint)); } if (toolsett->gp_sculptpaint) { - scene_foreach_paint(data, - &toolsett->gp_sculptpaint->paint, - do_undo_restore, - reader, - &toolsett_old->gp_sculptpaint->paint); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + scene_foreach_paint(data, + &toolsett->gp_sculptpaint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_sculptpaint->paint)); } if (toolsett->gp_weightpaint) { - scene_foreach_paint(data, - &toolsett->gp_weightpaint->paint, - do_undo_restore, - reader, - &toolsett_old->gp_weightpaint->paint); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + scene_foreach_paint(data, + &toolsett->gp_weightpaint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_weightpaint->paint)); } BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, @@ -746,15 +773,18 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->r.bake.cage_object, IDWALK_CB_NOP); if (scene->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&scene->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&scene->nodetree)); } if (scene->ed) { - SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_member_id_cb, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_member_id_cb, data)); } /* This pointer can be NULL during old files reading, better be safe than sorry. */ if (scene->master_collection != NULL) { - BKE_library_foreach_ID_embedded(data, (ID **)&scene->master_collection); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&scene->master_collection)); } LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { @@ -765,7 +795,8 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) data, base->object, IDWALK_CB_NOP | IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE); } - scene_foreach_layer_collection(data, &view_layer->layer_collections); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, scene_foreach_layer_collection(data, &view_layer->layer_collections)); LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) { if (fmc->script) { @@ -786,18 +817,25 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, marker->camera, IDWALK_CB_NOP); - IDP_foreach_property( - marker->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(marker->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); } ToolSettings *toolsett = scene->toolsettings; if (toolsett) { - scene_foreach_toolsettings(data, toolsett, false, NULL, toolsett); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, scene_foreach_toolsettings(data, toolsett, false, NULL, toolsett)); } if (scene->rigidbody_world) { - BKE_rigidbody_world_id_loop( - scene->rigidbody_world, scene_foreach_rigidbodyworldSceneLooper, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_rigidbody_world_id_loop( + scene->rigidbody_world, scene_foreach_rigidbodyworldSceneLooper, data)); } } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 1ddfd4e47ac..69e926caeae 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -120,8 +120,8 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_GRAPH: { SpaceGraph *sipo = (SpaceGraph *)sl; - - screen_foreach_id_dopesheet(data, sipo->ads); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + screen_foreach_id_dopesheet(data, sipo->ads)); break; } case SPACE_PROPERTIES: { @@ -151,8 +151,8 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_NLA: { SpaceNla *snla = (SpaceNla *)sl; - - screen_foreach_id_dopesheet(data, snla->ads); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + screen_foreach_id_dopesheet(data, snla->ads)); break; } case SPACE_TEXT: { @@ -233,12 +233,13 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area static void screen_foreach_id(ID *id, LibraryForeachIDData *data) { - if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) { - bScreen *screen = (bScreen *)id; + if ((BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) == 0) { + return; + } + bScreen *screen = (bScreen *)id; - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - BKE_screen_foreach_id_screen_area(data, area); - } + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_screen_foreach_id_screen_area(data, area)); } } diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 1d297b3ced9..98e7405bde6 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -103,7 +103,8 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) Simulation *simulation = (Simulation *)id; if (simulation->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree)); } } diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 74e8db039f2..0811e6cb675 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -142,7 +142,8 @@ static void texture_foreach_id(ID *id, LibraryForeachIDData *data) Tex *texture = (Tex *)id; if (texture->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&texture->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&texture->nodetree)); } BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, texture->ima, IDWALK_CB_USER); } diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index fe03c5b817a..2f0a282a298 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -131,7 +131,8 @@ static void world_foreach_id(ID *id, LibraryForeachIDData *data) if (world->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&world->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&world->nodetree)); } } diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index aa439f619be..47ee296823b 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -101,7 +101,8 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data) if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) { LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) { - BKE_screen_foreach_id_screen_area(data, area); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + BKE_screen_foreach_id_screen_area(data, area)); } } } From 259731909c8013fa6d8cdaa221ad7785cabd48ce Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 27 Oct 2021 12:04:36 +0200 Subject: [PATCH 1374/1500] LibQuery: Fix potential memleak in recursive case. In case `library_foreach_ID_link` would return early in recursive process, it would not properly free its utils data. Also add proper iteration break in case some sub-calls requested it. Finally, make this function return a boolean to know whether iteration should be stopped or not (will be used in future commit to fix this handling in embedded IDs case). Part of T90922: Fix return policy inconsistency in `scene_foreach_id`. --- source/blender/blenkernel/intern/lib_query.c | 56 ++++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 79b093bedbe..f0011a3b533 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -146,7 +146,7 @@ int BKE_lib_query_foreachid_process_callback_flag_override(LibraryForeachIDData return cb_flag_backup; } -static void library_foreach_ID_link(Main *bmain, +static bool library_foreach_ID_link(Main *bmain, ID *id_owner, ID *id, LibraryIDLinkCallback callback, @@ -200,7 +200,16 @@ bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp) return true; } -static void library_foreach_ID_link(Main *bmain, +static void library_foreach_ID_data_cleanup(LibraryForeachIDData *data) +{ + if (data->ids_handled != NULL) { + BLI_gset_free(data->ids_handled, NULL); + BLI_LINKSTACK_FREE(data->ids_todo); + } +} + +/** \return false in case iteration over ID pointers must be stopped, true otherwise. */ +static bool library_foreach_ID_link(Main *bmain, ID *id_owner, ID *id, LibraryIDLinkCallback callback, @@ -235,10 +244,26 @@ static void library_foreach_ID_link(Main *bmain, data.user_data = user_data; #define CALLBACK_INVOKE_ID(check_id, cb_flag) \ - BKE_LIB_FOREACHID_PROCESS_ID(&data, check_id, cb_flag) + { \ + CHECK_TYPE_ANY((check_id), ID *, void *); \ + BKE_lib_query_foreachid_process(&data, (ID **)&(check_id), (cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop(&data)) { \ + library_foreach_ID_data_cleanup(&data); \ + return false; \ + } \ + } \ + ((void)0) #define CALLBACK_INVOKE(check_id_super, cb_flag) \ - BKE_LIB_FOREACHID_PROCESS_IDSUPER(&data, check_id_super, cb_flag) + { \ + CHECK_TYPE(&((check_id_super)->id), ID *); \ + BKE_lib_query_foreachid_process(&data, (ID **)&(check_id_super), (cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop(&data)) { \ + library_foreach_ID_data_cleanup(&data); \ + return false; \ + } \ + } \ + ((void)0) for (; id != NULL; id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL) { data.self_id = id; @@ -280,6 +305,10 @@ static void library_foreach_ID_link(Main *bmain, to_id_entry = to_id_entry->next) { BKE_lib_query_foreachid_process( &data, to_id_entry->id_pointer.to, to_id_entry->usage_flag); + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; + } } continue; } @@ -303,26 +332,33 @@ static void library_foreach_ID_link(Main *bmain, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, &data); + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; + } AnimData *adt = BKE_animdata_from_id(id); if (adt) { BKE_animdata_foreach_id(adt, &data); + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; + } } const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (id_type->foreach_id != NULL) { id_type->foreach_id(id, &data); - if (data.status & IDWALK_STOP) { - break; + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; } } } - if (data.ids_handled) { - BLI_gset_free(data.ids_handled, NULL); - BLI_LINKSTACK_FREE(data.ids_todo); - } + library_foreach_ID_data_cleanup(&data); + return true; #undef CALLBACK_INVOKE_ID #undef CALLBACK_INVOKE From c8c53ceecc3022c7a8e5f84e619ac9ea6994ed5c Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 27 Oct 2021 12:16:31 +0200 Subject: [PATCH 1375/1500] LibQuery: Remove last 'bool returns' from public API. Those were used in a very few places to detect whether iteration should be stopped or not, but one can use `BKE_lib_query_foreachid_iter_stop` now for that. Also fix early break handling in embedded IDs processing. Fix T90922: Fix return policy inconsistency in `scene_foreach_id`. --- source/blender/blenkernel/BKE_lib_query.h | 4 +- source/blender/blenkernel/intern/lib_query.c | 97 ++++++++++---------- source/blender/blenkernel/intern/scene.c | 3 +- 3 files changed, 54 insertions(+), 50 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index 364a9056dd4..30c742e3af6 100644 --- a/source/blender/blenkernel/BKE_lib_query.h +++ b/source/blender/blenkernel/BKE_lib_query.h @@ -144,7 +144,7 @@ enum { typedef struct LibraryForeachIDData LibraryForeachIDData; bool BKE_lib_query_foreachid_iter_stop(struct LibraryForeachIDData *data); -bool BKE_lib_query_foreachid_process(struct LibraryForeachIDData *data, +void BKE_lib_query_foreachid_process(struct LibraryForeachIDData *data, struct ID **id_pp, int cb_flag); int BKE_lib_query_foreachid_process_flags_get(struct LibraryForeachIDData *data); @@ -181,7 +181,7 @@ int BKE_lib_query_foreachid_process_callback_flag_override(struct LibraryForeach } \ ((void)0) -bool BKE_library_foreach_ID_embedded(struct LibraryForeachIDData *data, struct ID **id_pp); +void BKE_library_foreach_ID_embedded(struct LibraryForeachIDData *data, struct ID **id_pp); void BKE_lib_query_idpropertiesForeachIDLink_callback(struct IDProperty *id_prop, void *user_data); /* Loop over all of the ID's this datablock links to. */ diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index f0011a3b533..74750a9b61a 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -83,48 +83,45 @@ bool BKE_lib_query_foreachid_iter_stop(LibraryForeachIDData *data) return (data->status & IDWALK_STOP) != 0; } -bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int cb_flag) +void BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int cb_flag) { - if (!(data->status & IDWALK_STOP)) { - const int flag = data->flag; - ID *old_id = *id_pp; - - /* Update the callback flags with the ones defined (or forbidden) in `data` by the generic - * caller code. */ - cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear); - - /* Update the callback flags with some extra information regarding overrides: all 'loopback', - * 'internal', 'embedded' etc. ID pointers are never overridable. */ - if (cb_flag & - (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { - cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE; - } - - const int callback_return = data->callback( - &(struct LibraryIDLinkCallbackData){.user_data = data->user_data, - .bmain = data->bmain, - .id_owner = data->owner_id, - .id_self = data->self_id, - .id_pointer = id_pp, - .cb_flag = cb_flag}); - if (flag & IDWALK_READONLY) { - BLI_assert(*(id_pp) == old_id); - } - if (old_id && (flag & IDWALK_RECURSE)) { - if (BLI_gset_add((data)->ids_handled, old_id)) { - if (!(callback_return & IDWALK_RET_STOP_RECURSION)) { - BLI_LINKSTACK_PUSH(data->ids_todo, old_id); - } - } - } - if (callback_return & IDWALK_RET_STOP_ITER) { - data->status |= IDWALK_STOP; - return false; - } - return true; + if (BKE_lib_query_foreachid_iter_stop(data)) { + return; } - return false; + const int flag = data->flag; + ID *old_id = *id_pp; + + /* Update the callback flags with the ones defined (or forbidden) in `data` by the generic + * caller code. */ + cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear); + + /* Update the callback flags with some extra information regarding overrides: all 'loopback', + * 'internal', 'embedded' etc. ID pointers are never overridable. */ + if (cb_flag & (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { + cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE; + } + + const int callback_return = data->callback( + &(struct LibraryIDLinkCallbackData){.user_data = data->user_data, + .bmain = data->bmain, + .id_owner = data->owner_id, + .id_self = data->self_id, + .id_pointer = id_pp, + .cb_flag = cb_flag}); + if (flag & IDWALK_READONLY) { + BLI_assert(*(id_pp) == old_id); + } + if (old_id && (flag & IDWALK_RECURSE)) { + if (BLI_gset_add((data)->ids_handled, old_id)) { + if (!(callback_return & IDWALK_RET_STOP_RECURSION)) { + BLI_LINKSTACK_PUSH(data->ids_todo, old_id); + } + } + } + if (callback_return & IDWALK_RET_STOP_ITER) { + data->status |= IDWALK_STOP; + } } int BKE_lib_query_foreachid_process_flags_get(LibraryForeachIDData *data) @@ -165,19 +162,24 @@ void BKE_lib_query_idpropertiesForeachIDLink_callback(IDProperty *id_prop, void BKE_LIB_FOREACHID_PROCESS_ID(data, id_prop->data.pointer, cb_flag); } -bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp) +/** Process embedded ID pointers (root nodetrees, master collections, ...). + * + * Those require specific care, since they are technically sub-data of their owner, yet in some + * cases they still behave as regular IDs. */ +void BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp) { /* Needed e.g. for callbacks handling relationships. This call shall be absolutely read-only. */ ID *id = *id_pp; const int flag = data->flag; - if (!BKE_lib_query_foreachid_process(data, id_pp, IDWALK_CB_EMBEDDED)) { - return false; + BKE_lib_query_foreachid_process(data, id_pp, IDWALK_CB_EMBEDDED); + if (BKE_lib_query_foreachid_iter_stop(data)) { + return; } BLI_assert(id == *id_pp); if (id == NULL) { - return true; + return; } if (flag & IDWALK_IGNORE_EMBEDDED_ID) { @@ -193,11 +195,12 @@ bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp) } } else { - library_foreach_ID_link( - data->bmain, data->owner_id, id, data->callback, data->user_data, data->flag, data); + if (!library_foreach_ID_link( + data->bmain, data->owner_id, id, data->callback, data->user_data, data->flag, data)) { + data->status |= IDWALK_STOP; + return; + } } - - return true; } static void library_foreach_ID_data_cleanup(LibraryForeachIDData *data) diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index c0fd0423475..3853687a8f1 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -734,7 +734,8 @@ static bool seq_foreach_member_id_cb(Sequence *seq, void *user_data) #define FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag) \ { \ CHECK_TYPE(&((_id_super)->id), ID *); \ - if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag))) { \ + BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag)); \ + if (!BKE_lib_query_foreachid_iter_stop((_data))) { \ return false; \ } \ } \ From e1db6dc11b56497561181a18d35db0badda4e84a Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 29 Oct 2021 12:54:32 +0200 Subject: [PATCH 1376/1500] Fix crash on undo after recent lib_query refactor. Forgot that scene uses part of its ID looping code for specific undo handling. Caused by rBe3b2f0fd6ff9. --- source/blender/blenkernel/intern/scene.c | 255 +++++++++++++---------- 1 file changed, 144 insertions(+), 111 deletions(-) diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 3853687a8f1..a827e1c32a2 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -523,7 +523,10 @@ static void scene_foreach_toolsettings_id_pointer_process( } } -#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS( \ +/* Special handling is needed here, as `scene_foreach_toolsettings` (and its dependency + * `scene_foreach_paint`) are also used by `scene_undo_preserve`, where `LibraryForeachIDData + * *data` is NULL. */ +#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER( \ __data, __id, __do_undo_restore, __action, __reader, __id_old, __cb_flag) \ { \ if (__do_undo_restore) { \ @@ -531,24 +534,38 @@ static void scene_foreach_toolsettings_id_pointer_process( (ID **)&(__id), __action, __reader, (ID **)&(__id_old), __cb_flag); \ } \ else { \ + BLI_assert((__data) != NULL); \ BKE_LIB_FOREACHID_PROCESS_IDSUPER(__data, __id, __cb_flag); \ } \ } \ (void)0 +#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( \ + __data, __do_undo_restore, __func_call) \ + { \ + if (__do_undo_restore) { \ + __func_call; \ + } \ + else { \ + BLI_assert((__data) != NULL); \ + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(__data, __func_call); \ + } \ + } \ + (void)0 + static void scene_foreach_paint(LibraryForeachIDData *data, Paint *paint, const bool do_undo_restore, BlendLibReader *reader, Paint *paint_old) { - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - paint->brush, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - paint_old->brush, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + paint->brush, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + paint_old->brush, + IDWALK_CB_USER); for (int i = 0; i < paint_old->tool_slots_len; i++) { /* This is a bit tricky. * - In case we do not do `undo_restore`, `paint` and `paint_old` pointers are the same, so @@ -560,21 +577,21 @@ static void scene_foreach_paint(LibraryForeachIDData *data, */ Brush *brush_tmp = NULL; Brush **brush_p = i < paint->tool_slots_len ? &paint->tool_slots[i].brush : &brush_tmp; - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - *brush_p, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - paint_old->brush, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + *brush_p, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + paint_old->brush, + IDWALK_CB_USER); } - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - paint->palette, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - paint_old->palette, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + paint->palette, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + paint_old->palette, + IDWALK_CB_USER); } static void scene_foreach_toolsettings(LibraryForeachIDData *data, @@ -583,102 +600,113 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, BlendLibReader *reader, ToolSettings *toolsett_old) { - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->particle.scene, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->particle.scene, - IDWALK_CB_NOP); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->particle.object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->particle.object, - IDWALK_CB_NOP); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->particle.shape_object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->particle.shape_object, - IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->particle.scene, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->particle.scene, + IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->particle.object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->particle.object, + IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->particle.shape_object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->particle.shape_object, + IDWALK_CB_NOP); scene_foreach_paint( data, &toolsett->imapaint.paint, do_undo_restore, reader, &toolsett_old->imapaint.paint); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->imapaint.stencil, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - toolsett_old->imapaint.stencil, - IDWALK_CB_USER); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->imapaint.clone, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - toolsett_old->imapaint.clone, - IDWALK_CB_USER); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->imapaint.canvas, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - toolsett_old->imapaint.canvas, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->imapaint.stencil, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + toolsett_old->imapaint.stencil, + IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->imapaint.clone, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + toolsett_old->imapaint.clone, + IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->imapaint.canvas, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + toolsett_old->imapaint.canvas, + IDWALK_CB_USER); if (toolsett->vpaint) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, - scene_foreach_paint(data, - &toolsett->vpaint->paint, - do_undo_restore, - reader, - &toolsett_old->vpaint->paint)); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->vpaint->paint, + do_undo_restore, + reader, + &toolsett_old->vpaint->paint)); } if (toolsett->wpaint) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, - scene_foreach_paint(data, - &toolsett->wpaint->paint, - do_undo_restore, - reader, - &toolsett_old->wpaint->paint)); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->wpaint->paint, + do_undo_restore, + reader, + &toolsett_old->wpaint->paint)); } if (toolsett->sculpt) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, - scene_foreach_paint(data, - &toolsett->sculpt->paint, - do_undo_restore, - reader, - &toolsett_old->sculpt->paint)); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->sculpt->gravity_object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->sculpt->gravity_object, - IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->sculpt->paint, + do_undo_restore, + reader, + &toolsett_old->sculpt->paint)); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->sculpt->gravity_object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->sculpt->gravity_object, + IDWALK_CB_NOP); } if (toolsett->uvsculpt) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, - scene_foreach_paint(data, - &toolsett->uvsculpt->paint, - do_undo_restore, - reader, - &toolsett_old->uvsculpt->paint)); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->uvsculpt->paint, + do_undo_restore, + reader, + &toolsett_old->uvsculpt->paint)); } if (toolsett->gp_paint) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, - scene_foreach_paint(data, - &toolsett->gp_paint->paint, - do_undo_restore, - reader, - &toolsett_old->gp_paint->paint)); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->gp_paint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_paint->paint)); } if (toolsett->gp_vertexpaint) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( data, + do_undo_restore, scene_foreach_paint(data, &toolsett->gp_vertexpaint->paint, do_undo_restore, @@ -686,8 +714,9 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, &toolsett_old->gp_vertexpaint->paint)); } if (toolsett->gp_sculptpaint) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( data, + do_undo_restore, scene_foreach_paint(data, &toolsett->gp_sculptpaint->paint, do_undo_restore, @@ -695,8 +724,9 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, &toolsett_old->gp_sculptpaint->paint)); } if (toolsett->gp_weightpaint) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( data, + do_undo_restore, scene_foreach_paint(data, &toolsett->gp_weightpaint->paint, do_undo_restore, @@ -704,15 +734,18 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, &toolsett_old->gp_weightpaint->paint)); } - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->gp_sculpt.guide.reference_object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->gp_sculpt.guide.reference_object, - IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->gp_sculpt.guide.reference_object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->gp_sculpt.guide.reference_object, + IDWALK_CB_NOP); } +#undef BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER +#undef BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL + static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase *lb) { LISTBASE_FOREACH (LayerCollection *, lc, lb) { From d18d87d3e7d381124f91c2230eb447ff5b001c27 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 29 Oct 2021 15:59:03 +0200 Subject: [PATCH 1377/1500] Fix T92576: Crash switching from Asset Browser to File Brower The asset catalog filtering data needs to be cleared when with the other asset library data of the file list. This is done when changing between asset and file browser (and in other cases). --- source/blender/editors/space_file/filelist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index a1b1c8cc363..a73fa2b9740 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1911,6 +1911,7 @@ static void filelist_clear_asset_library(FileList *filelist) { /* The AssetLibraryService owns the AssetLibrary pointer, so no need for us to free it. */ filelist->asset_library = NULL; + file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter); } void filelist_clear_ex(struct FileList *filelist, @@ -2010,7 +2011,6 @@ void filelist_free(struct FileList *filelist) filelist->selection_state = NULL; } - file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter); MEM_SAFE_FREE(filelist->asset_library_ref); memset(&filelist->filter_data, 0, sizeof(filelist->filter_data)); From 7e94499bb3bb64170f826471a86ce0f957eab3a3 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 29 Oct 2021 11:23:56 -0300 Subject: [PATCH 1378/1500] Fix snap cursor not working in regions with transparency Use `BKE_area_find_region_type` instead of using the context region. --- .../editors/space_view3d/view3d_cursor_snap.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index e8f1e011364..baf61befcba 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -37,6 +37,7 @@ #include "BKE_main.h" #include "BKE_object.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "GPU_immediate.h" #include "GPU_matrix.h" @@ -764,16 +765,12 @@ static bool v3d_cursor_snap_pool_fn(bContext *C) return false; } - ARegion *region = CTX_wm_region(C); - if (region->regiontype != RGN_TYPE_WINDOW) { - return false; - } - ScrArea *area = CTX_wm_area(C); if (area->spacetype != SPACE_VIEW3D) { return false; } + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); RegionView3D *rv3d = region->regiondata; if (rv3d->rflag & RV3D_NAVIGATING) { /* Don't draw the cursor while navigating. It can be distracting. */ @@ -790,7 +787,8 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust V3DSnapCursorData *snap_data = &data_intern->snap_data; wmWindowManager *wm = CTX_wm_manager(C); - ARegion *region = CTX_wm_region(C); + ScrArea *area = CTX_wm_area(C); + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); x -= region->winrct.xmin; y -= region->winrct.ymin; if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) { @@ -955,7 +953,8 @@ V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state, if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = DEG_get_input_scene(depsgraph); - ARegion *region = CTX_wm_region(C); + ScrArea *area = CTX_wm_area(C); + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); View3D *v3d = CTX_wm_view3d(C); if (!state) { From 2383628ee11b6e90d34cd010f742c6d0356f5e11 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 29 Oct 2021 09:41:08 -0500 Subject: [PATCH 1379/1500] Nodes: Add translation markers to new socket names and descriptions As part of the refactor to the node declaration builders, we had hoped to add a regular expression specifically for these socket names, but recent discussions have revealed that using the translation marker macros is the preferred solution. If the names and descriptions were exposed to RNA, these would not be necessary. However, that may be quite complicated, since sockets are all instances of the same RNA types. Differential Revision: https://developer.blender.org/D13033 --- .../nodes/node_composite_bokehimage.cc | 2 +- .../nodes/node_composite_brightness.cc | 8 +- .../nodes/node_composite_colorbalance.cc | 6 +- .../nodes/node_composite_colorcorrection.cc | 6 +- .../nodes/node_composite_composite.cc | 6 +- .../composite/nodes/node_composite_curves.cc | 13 +-- .../nodes/node_composite_exposure.cc | 6 +- .../composite/nodes/node_composite_gamma.cc | 11 ++- .../nodes/node_composite_hueSatVal.cc | 16 ++-- .../nodes/node_composite_huecorrect.cc | 6 +- .../composite/nodes/node_composite_idMask.cc | 4 +- .../composite/nodes/node_composite_invert.cc | 6 +- .../composite/nodes/node_composite_levels.cc | 6 +- .../composite/nodes/node_composite_mask.cc | 2 +- .../nodes/node_composite_movieclip.cc | 12 +-- .../nodes/node_composite_premulkey.cc | 4 +- .../composite/nodes/node_composite_rgb.cc | 2 +- .../nodes/node_composite_sepcombHSVA.cc | 20 ++--- .../nodes/node_composite_sepcombRGBA.cc | 20 ++--- .../nodes/node_composite_sepcombYCCA.cc | 20 ++--- .../nodes/node_composite_sepcombYUVA.cc | 20 ++--- .../nodes/node_composite_setalpha.cc | 6 +- .../composite/nodes/node_composite_texture.cc | 8 +- .../composite/nodes/node_composite_tonemap.cc | 4 +- .../nodes/node_composite_trackpos.cc | 6 +- .../nodes/node_composite_valToRgb.cc | 10 +-- .../composite/nodes/node_composite_value.cc | 2 +- .../composite/nodes/node_composite_viewer.cc | 6 +- .../nodes/legacy/node_fn_random_float.cc | 8 +- .../nodes/node_fn_align_euler_to_vector.cc | 12 ++- .../function/nodes/node_fn_boolean_math.cc | 6 +- .../function/nodes/node_fn_float_compare.cc | 8 +- .../function/nodes/node_fn_float_to_int.cc | 4 +- .../function/nodes/node_fn_input_bool.cc | 2 +- .../function/nodes/node_fn_input_color.cc | 2 +- .../nodes/function/nodes/node_fn_input_int.cc | 2 +- .../nodes/node_fn_input_special_characters.cc | 4 +- .../function/nodes/node_fn_input_string.cc | 2 +- .../function/nodes/node_fn_input_vector.cc | 2 +- .../function/nodes/node_fn_random_value.cc | 26 +++--- .../function/nodes/node_fn_replace_string.cc | 9 +- .../function/nodes/node_fn_rotate_euler.cc | 10 +-- .../function/nodes/node_fn_slice_string.cc | 8 +- .../function/nodes/node_fn_string_length.cc | 4 +- .../function/nodes/node_fn_value_to_string.cc | 6 +- .../node_geo_align_rotation_to_vector.cc | 12 +-- .../nodes/legacy/node_geo_attribute_clamp.cc | 24 +++--- .../legacy/node_geo_attribute_color_ramp.cc | 8 +- .../legacy/node_geo_attribute_combine_xyz.cc | 18 ++-- .../legacy/node_geo_attribute_compare.cc | 24 +++--- .../legacy/node_geo_attribute_convert.cc | 8 +- .../legacy/node_geo_attribute_curve_map.cc | 8 +- .../nodes/legacy/node_geo_attribute_fill.cc | 16 ++-- .../legacy/node_geo_attribute_map_range.cc | 30 +++---- .../nodes/legacy/node_geo_attribute_math.cc | 18 ++-- .../nodes/legacy/node_geo_attribute_mix.cc | 26 +++--- .../legacy/node_geo_attribute_proximity.cc | 10 +-- .../legacy/node_geo_attribute_randomize.cc | 20 ++--- .../node_geo_attribute_sample_texture.cc | 10 +-- .../legacy/node_geo_attribute_separate_xyz.cc | 14 ++-- .../legacy/node_geo_attribute_transfer.cc | 10 +-- .../legacy/node_geo_attribute_vector_math.cc | 22 ++--- .../node_geo_attribute_vector_rotate.cc | 28 +++---- .../nodes/legacy/node_geo_curve_endpoints.cc | 6 +- .../nodes/legacy/node_geo_curve_reverse.cc | 6 +- .../node_geo_curve_select_by_handle_type.cc | 6 +- .../legacy/node_geo_curve_set_handles.cc | 6 +- .../legacy/node_geo_curve_spline_type.cc | 6 +- .../nodes/legacy/node_geo_curve_subdivide.cc | 8 +- .../nodes/legacy/node_geo_curve_to_points.cc | 8 +- .../nodes/legacy/node_geo_delete_geometry.cc | 8 +- .../nodes/legacy/node_geo_edge_split.cc | 10 +-- .../nodes/legacy/node_geo_material_assign.cc | 8 +- .../nodes/legacy/node_geo_mesh_to_curve.cc | 6 +- .../nodes/legacy/node_geo_point_distribute.cc | 12 +-- .../nodes/legacy/node_geo_point_instance.cc | 12 +-- .../nodes/legacy/node_geo_point_rotate.cc | 18 ++-- .../nodes/legacy/node_geo_point_scale.cc | 10 +-- .../nodes/legacy/node_geo_point_separate.cc | 8 +- .../nodes/legacy/node_geo_point_translate.cc | 8 +- .../nodes/legacy/node_geo_points_to_volume.cc | 14 ++-- .../geometry/nodes/legacy/node_geo_raycast.cc | 26 +++--- .../legacy/node_geo_select_by_material.cc | 8 +- .../legacy/node_geo_subdivision_surface.cc | 8 +- .../nodes/legacy/node_geo_volume_to_mesh.cc | 14 ++-- .../nodes/node_geo_attribute_capture.cc | 24 +++--- .../nodes/node_geo_attribute_remove.cc | 6 +- .../nodes/node_geo_attribute_statistic.cc | 38 ++++----- .../nodes/geometry/nodes/node_geo_boolean.cc | 10 +-- .../geometry/nodes/node_geo_bounding_box.cc | 8 +- .../nodes/node_geo_collection_info.cc | 14 ++-- .../geometry/nodes/node_geo_convex_hull.cc | 4 +- .../node_geo_curve_endpoint_selection.cc | 13 +-- .../geometry/nodes/node_geo_curve_fill.cc | 4 +- .../geometry/nodes/node_geo_curve_fillet.cc | 10 +-- .../node_geo_curve_handle_type_selection.cc | 2 +- .../geometry/nodes/node_geo_curve_length.cc | 4 +- .../nodes/node_geo_curve_parameter.cc | 2 +- ...node_geo_curve_primitive_bezier_segment.cc | 18 ++-- .../nodes/node_geo_curve_primitive_circle.cc | 18 ++-- .../nodes/node_geo_curve_primitive_line.cc | 10 +-- ...de_geo_curve_primitive_quadratic_bezier.cc | 18 ++-- .../node_geo_curve_primitive_quadrilateral.cc | 38 ++++++--- .../nodes/node_geo_curve_primitive_spiral.cc | 18 ++-- .../nodes/node_geo_curve_primitive_star.cc | 16 ++-- .../geometry/nodes/node_geo_curve_resample.cc | 13 +-- .../geometry/nodes/node_geo_curve_reverse.cc | 6 +- .../geometry/nodes/node_geo_curve_sample.cc | 15 ++-- .../nodes/node_geo_curve_set_handles.cc | 6 +- .../nodes/node_geo_curve_spline_type.cc | 6 +- .../nodes/node_geo_curve_subdivide.cc | 6 +- .../geometry/nodes/node_geo_curve_to_mesh.cc | 10 +-- .../nodes/node_geo_curve_to_points.cc | 14 ++-- .../geometry/nodes/node_geo_curve_trim.cc | 15 ++-- .../nodes/node_geo_delete_geometry.cc | 8 +- .../node_geo_distribute_points_on_faces.cc | 20 ++--- .../geometry/nodes/node_geo_edge_split.cc | 6 +- .../geometry/nodes/node_geo_image_texture.cc | 13 +-- .../nodes/node_geo_input_curve_handles.cc | 4 +- .../nodes/node_geo_input_curve_tilt.cc | 2 +- .../nodes/geometry/nodes/node_geo_input_id.cc | 2 +- .../geometry/nodes/node_geo_input_index.cc | 2 +- .../geometry/nodes/node_geo_input_material.cc | 2 +- .../nodes/node_geo_input_material_index.cc | 2 +- .../geometry/nodes/node_geo_input_normal.cc | 2 +- .../geometry/nodes/node_geo_input_position.cc | 2 +- .../geometry/nodes/node_geo_input_radius.cc | 2 +- .../nodes/node_geo_input_shade_smooth.cc | 2 +- .../nodes/node_geo_input_spline_cyclic.cc | 2 +- .../nodes/node_geo_input_spline_length.cc | 2 +- .../nodes/node_geo_input_spline_resolution.cc | 2 +- .../geometry/nodes/node_geo_input_tangent.cc | 2 +- .../nodes/node_geo_instance_on_points.cc | 25 +++--- .../nodes/node_geo_instances_to_points.cc | 10 +-- .../geometry/nodes/node_geo_is_viewport.cc | 2 +- .../geometry/nodes/node_geo_join_geometry.cc | 4 +- .../nodes/node_geo_material_replace.cc | 8 +- .../nodes/node_geo_material_selection.cc | 4 +- .../nodes/node_geo_mesh_primitive_circle.cc | 6 +- .../nodes/node_geo_mesh_primitive_cone.cc | 17 ++-- .../nodes/node_geo_mesh_primitive_cube.cc | 13 +-- .../nodes/node_geo_mesh_primitive_cylinder.cc | 22 ++--- .../nodes/node_geo_mesh_primitive_grid.cc | 10 +-- .../node_geo_mesh_primitive_ico_sphere.cc | 6 +- .../nodes/node_geo_mesh_primitive_line.cc | 12 +-- .../node_geo_mesh_primitive_uv_sphere.cc | 8 +- .../geometry/nodes/node_geo_mesh_subdivide.cc | 6 +- .../geometry/nodes/node_geo_mesh_to_curve.cc | 6 +- .../geometry/nodes/node_geo_mesh_to_points.cc | 10 +-- .../geometry/nodes/node_geo_object_info.cc | 16 ++-- .../nodes/node_geo_points_to_vertices.cc | 6 +- .../nodes/node_geo_points_to_volume.cc | 12 +-- .../geometry/nodes/node_geo_proximity.cc | 11 +-- .../nodes/geometry/nodes/node_geo_raycast.cc | 38 +++++---- .../nodes/node_geo_realize_instances.cc | 4 +- .../nodes/node_geo_rotate_instances.cc | 12 +-- .../nodes/node_geo_scale_instances.cc | 15 ++-- .../nodes/node_geo_separate_components.cc | 12 +-- .../nodes/node_geo_separate_geometry.cc | 14 ++-- .../nodes/node_geo_set_curve_handles.cc | 8 +- .../nodes/node_geo_set_curve_radius.cc | 13 +-- .../geometry/nodes/node_geo_set_curve_tilt.cc | 8 +- .../nodes/geometry/nodes/node_geo_set_id.cc | 8 +- .../geometry/nodes/node_geo_set_material.cc | 8 +- .../nodes/node_geo_set_material_index.cc | 8 +- .../nodes/node_geo_set_point_radius.cc | 13 +-- .../geometry/nodes/node_geo_set_position.cc | 10 +-- .../nodes/node_geo_set_shade_smooth.cc | 8 +- .../nodes/node_geo_set_spline_cyclic.cc | 8 +- .../nodes/node_geo_set_spline_resolution.cc | 8 +- .../geometry/nodes/node_geo_string_join.cc | 6 +- .../nodes/node_geo_string_to_curves.cc | 30 +++++-- .../nodes/node_geo_subdivision_surface.cc | 8 +- .../nodes/geometry/nodes/node_geo_switch.cc | 82 ++++++++++--------- .../nodes/node_geo_transfer_attribute.cc | 29 +++---- .../geometry/nodes/node_geo_transform.cc | 10 +-- .../nodes/node_geo_translate_instances.cc | 10 +-- .../geometry/nodes/node_geo_triangulate.cc | 6 +- .../nodes/geometry/nodes/node_geo_viewer.cc | 12 +-- .../geometry/nodes/node_geo_volume_to_mesh.cc | 12 +-- .../nodes/shader/nodes/node_shader_clamp.cc | 8 +- .../nodes/shader/nodes/node_shader_curves.cc | 22 +++-- .../shader/nodes/node_shader_map_range.cc | 14 ++-- .../nodes/shader/nodes/node_shader_math.cc | 14 +++- .../nodes/shader/nodes/node_shader_mixRgb.cc | 8 +- .../shader/nodes/node_shader_sepcombRGB.cc | 16 ++-- .../shader/nodes/node_shader_sepcombXYZ.cc | 16 ++-- .../shader/nodes/node_shader_tex_brick.cc | 24 +++--- .../shader/nodes/node_shader_tex_checker.cc | 12 +-- .../shader/nodes/node_shader_tex_gradient.cc | 6 +- .../shader/nodes/node_shader_tex_image.cc | 6 +- .../shader/nodes/node_shader_tex_magic.cc | 10 +-- .../shader/nodes/node_shader_tex_musgrave.cc | 18 ++-- .../shader/nodes/node_shader_tex_noise.cc | 16 ++-- .../shader/nodes/node_shader_tex_voronoi.cc | 22 ++--- .../shader/nodes/node_shader_tex_wave.cc | 18 ++-- .../nodes/node_shader_tex_white_noise.cc | 8 +- .../shader/nodes/node_shader_valToRgb.cc | 10 +-- .../nodes/shader/nodes/node_shader_value.cc | 2 +- .../shader/nodes/node_shader_vector_math.cc | 12 +-- .../shader/nodes/node_shader_vector_rotate.cc | 12 +-- 201 files changed, 1161 insertions(+), 1045 deletions(-) diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc index 3a4bf94d256..3f8a7606d94 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b) { - b.add_output("Image"); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc index ad4b09c69d0..028afad3cf8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc +++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc @@ -29,10 +29,10 @@ namespace blender::nodes { static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Bright").min(-100.0f).max(100.0f); - b.add_input("Contrast").min(-100.0f).max(100.0f); - b.add_output("Image"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Bright")).min(-100.0f).max(100.0f); + b.add_input(N_("Contrast")).min(-100.0f).max(100.0f); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc index 440e37fe741..ef8af5f81a6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b) { - b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Image"); + b.add_input(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc index 0682c66f1e8..095fbef826a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Mask").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output("Image"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Mask")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc index 170fecb251c..4247e81e9b2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.cc +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_composite_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_input("Alpha").default_value(1.0f).min(0.0f).max(1.0f); - b.add_input("Z").default_value(1.0f).min(0.0f).max(1.0f); + b.add_input(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input(N_("Z")).default_value(1.0f).min(0.0f).max(1.0f); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc index 88d96e1ca4a..5f99bb57768 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.cc +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void cmp_node_time_declare(NodeDeclarationBuilder &b) { - b.add_output("Fac"); + b.add_output(N_("Fac")); } } // namespace blender::nodes @@ -90,11 +90,12 @@ namespace blender::nodes { static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b) { - b.add_input("Fac").default_value(1.0f).min(-1.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Black Level").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_input("White Level").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Image"); + b.add_input(N_("Fac")).default_value(1.0f).min(-1.0f).max(1.0f).subtype( + PROP_FACTOR); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Black Level")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input(N_("White Level")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc index fd959376afe..c1e64065f7e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc +++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_exposure_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Exposure").min(-10.0f).max(10.0f); - b.add_output("Image"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Exposure")).min(-10.0f).max(10.0f); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc index a29a001688a..74152a27485 100644 --- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc +++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc @@ -29,10 +29,13 @@ namespace blender::nodes { static void cmp_node_gamma_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Gamma").default_value(1.0f).min(0.001f).max(10.0f).subtype( - PROP_UNSIGNED); - b.add_output("Image"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Gamma")) + .default_value(1.0f) + .min(0.001f) + .max(10.0f) + .subtype(PROP_UNSIGNED); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc index 07746918a94..21430035465 100644 --- a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc +++ b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc @@ -29,16 +29,20 @@ namespace blender::nodes { static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Hue").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Saturation") + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Saturation")) .default_value(1.0f) .min(0.0f) .max(2.0f) .subtype(PROP_FACTOR); - b.add_input("Value").default_value(1.0f).min(0.0f).max(2.0f).subtype(PROP_FACTOR); - b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output("Image"); + b.add_input(N_("Value")) + .default_value(1.0f) + .min(0.0f) + .max(2.0f) + .subtype(PROP_FACTOR); + b.add_input(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc index 39014896a7b..83743bbed18 100644 --- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc +++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b) { - b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Image"); + b.add_input(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_idMask.cc b/source/blender/nodes/composite/nodes/node_composite_idMask.cc index de011dd6274..5121370567c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_idMask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_idMask.cc @@ -29,8 +29,8 @@ namespace blender::nodes { static void cmp_node_idmask_declare(NodeDeclarationBuilder &b) { - b.add_input("ID value").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output("Alpha"); + b.add_input(N_("ID value")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Alpha")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.cc b/source/blender/nodes/composite/nodes/node_composite_invert.cc index 57b7ed36ccd..dabf0452628 100644 --- a/source/blender/nodes/composite/nodes/node_composite_invert.cc +++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_invert_declare(NodeDeclarationBuilder &b) { - b.add_input("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Color"); + b.add_input(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Color")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc index aaab8dcc874..54064f24e0d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_levels.cc +++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_levels_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_output("Mean"); - b.add_output("Std Dev"); + b.add_input(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_output(N_("Mean")); + b.add_output(N_("Std Dev")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc index 8b415bb8b63..6428fadaa5f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc @@ -31,7 +31,7 @@ namespace blender::nodes { static void cmp_node_mask_declare(NodeDeclarationBuilder &b) { - b.add_output("Mask"); + b.add_output(N_("Mask")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc index ae91212f811..5d63a1b8002 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -30,12 +30,12 @@ namespace blender::nodes { static void cmp_node_movieclip_declare(NodeDeclarationBuilder &b) { - b.add_output("Image"); - b.add_output("Alpha"); - b.add_output("Offset X"); - b.add_output("Offset Y"); - b.add_output("Scale"); - b.add_output("Angle"); + b.add_output(N_("Image")); + b.add_output(N_("Alpha")); + b.add_output(N_("Offset X")); + b.add_output(N_("Offset Y")); + b.add_output(N_("Scale")); + b.add_output(N_("Angle")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc index e557854c611..49068429a8d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc +++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc @@ -29,8 +29,8 @@ namespace blender::nodes { static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Image"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc index 332e56e26b1..abe69d6a756 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void cmp_node_rgb_declare(NodeDeclarationBuilder &b) { - b.add_output("RGBA").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc index aa719a99b36..83c54069658 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc @@ -29,11 +29,11 @@ namespace blender::nodes { static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("H"); - b.add_output("S"); - b.add_output("V"); - b.add_output("A"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("H")); + b.add_output(N_("S")); + b.add_output(N_("V")); + b.add_output(N_("A")); } } // namespace blender::nodes @@ -53,11 +53,11 @@ namespace blender::nodes { static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b) { - b.add_input("H").min(0.0f).max(1.0f); - b.add_input("S").min(0.0f).max(1.0f); - b.add_input("V").min(0.0f).max(1.0f); - b.add_input("A").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output("Image"); + b.add_input(N_("H")).min(0.0f).max(1.0f); + b.add_input(N_("S")).min(0.0f).max(1.0f); + b.add_input(N_("V")).min(0.0f).max(1.0f); + b.add_input(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc index b29af1359f5..049e798af0a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc @@ -28,11 +28,11 @@ namespace blender::nodes { static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("R"); - b.add_output("G"); - b.add_output("B"); - b.add_output("A"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("R")); + b.add_output(N_("G")); + b.add_output(N_("B")); + b.add_output(N_("A")); } } // namespace blender::nodes @@ -53,11 +53,11 @@ namespace blender::nodes { static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b) { - b.add_input("R").min(0.0f).max(1.0f); - b.add_input("G").min(0.0f).max(1.0f); - b.add_input("B").min(0.0f).max(1.0f); - b.add_input("A").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output("Image"); + b.add_input(N_("R")).min(0.0f).max(1.0f); + b.add_input(N_("G")).min(0.0f).max(1.0f); + b.add_input(N_("B")).min(0.0f).max(1.0f); + b.add_input(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc index 526d6b4eb5b..eaf6ba5e9b2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc @@ -29,11 +29,11 @@ namespace blender::nodes { static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Y"); - b.add_output("Cb"); - b.add_output("Cr"); - b.add_output("A"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Y")); + b.add_output(N_("Cb")); + b.add_output(N_("Cr")); + b.add_output(N_("A")); } } // namespace blender::nodes @@ -60,11 +60,11 @@ namespace blender::nodes { static void cmp_node_combycca_declare(NodeDeclarationBuilder &b) { - b.add_input("Y").min(0.0f).max(1.0f); - b.add_input("Cb").default_value(0.5f).min(0.0f).max(1.0f); - b.add_input("Cr").default_value(0.5f).min(0.0f).max(1.0f); - b.add_input("A").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output("Image"); + b.add_input(N_("Y")).min(0.0f).max(1.0f); + b.add_input(N_("Cb")).default_value(0.5f).min(0.0f).max(1.0f); + b.add_input(N_("Cr")).default_value(0.5f).min(0.0f).max(1.0f); + b.add_input(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc index 4619b0c97f1..bc7710122d1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc @@ -29,11 +29,11 @@ namespace blender::nodes { static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Y"); - b.add_output("U"); - b.add_output("V"); - b.add_output("A"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Y")); + b.add_output(N_("U")); + b.add_output(N_("V")); + b.add_output(N_("A")); } } // namespace blender::nodes @@ -54,11 +54,11 @@ namespace blender::nodes { static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b) { - b.add_input("Y").min(0.0f).max(1.0f); - b.add_input("U").min(0.0f).max(1.0f); - b.add_input("V").min(0.0f).max(1.0f); - b.add_input("A").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output("Image"); + b.add_input(N_("Y")).min(0.0f).max(1.0f); + b.add_input(N_("U")).min(0.0f).max(1.0f); + b.add_input(N_("V")).min(0.0f).max(1.0f); + b.add_input(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc index 07a7ffcb426..f59ba76f0c5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input("Alpha").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output("Image"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc index eff008b4b41..55ae6a4185e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_texture.cc +++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc @@ -29,14 +29,14 @@ namespace blender::nodes { static void cmp_node_texture_declare(NodeDeclarationBuilder &b) { - b.add_input("Offset").min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION); - b.add_input("Scale") + b.add_input(N_("Offset")).min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION); + b.add_input(N_("Scale")) .default_value({1.0f, 1.0f, 1.0f}) .min(-10.0f) .max(10.0f) .subtype(PROP_XYZ); - b.add_output("Value"); - b.add_output("Color"); + b.add_output(N_("Value")); + b.add_output(N_("Color")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc index 85fd240ce2e..33d6f98201c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc +++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc @@ -27,8 +27,8 @@ namespace blender::nodes { static void cmp_node_tonemap_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Image"); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc index cb5c9468daa..537f7e661db 100644 --- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc +++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b) { - b.add_output("X"); - b.add_output("Y"); - b.add_output("Speed").subtype(PROP_VELOCITY); + b.add_output(N_("X")); + b.add_output(N_("Y")); + b.add_output(N_("Speed")).subtype(PROP_VELOCITY); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc index 9e4f1329fbd..a0ab056e657 100644 --- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b) { - b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output("Image"); - b.add_output("Alpha"); + b.add_input(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output(N_("Image")); + b.add_output(N_("Alpha")); } } // namespace blender::nodes @@ -60,8 +60,8 @@ namespace blender::nodes { static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_output("Val"); + b.add_input(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output(N_("Val")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_value.cc b/source/blender/nodes/composite/nodes/node_composite_value.cc index 5459801bcc7..51214d23472 100644 --- a/source/blender/nodes/composite/nodes/node_composite_value.cc +++ b/source/blender/nodes/composite/nodes/node_composite_value.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void cmp_node_value_declare(NodeDeclarationBuilder &b) { - b.add_output("Value").default_value(0.5f); + b.add_output(N_("Value")).default_value(0.5f); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc index 7234d4d8eb2..b86ae57f664 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -32,9 +32,9 @@ namespace blender::nodes { static void cmp_node_viewer_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_input("Alpha").default_value(1.0f).min(0.0f).max(1.0f); - b.add_input("Z").default_value(1.0f).min(0.0f).max(1.0f); + b.add_input(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input(N_("Z")).default_value(1.0f).min(0.0f).max(1.0f); } } // namespace blender::nodes diff --git a/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc index 7f6f554ba93..d98d49c7273 100644 --- a/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc @@ -23,10 +23,10 @@ namespace blender::nodes { static void fn_node_legacy_random_float_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Min").min(-10000.0f).max(10000.0f); - b.add_input("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_input("Seed").min(-10000).max(10000); - b.add_output("Value"); + b.add_input(N_("Min")).min(-10000.0f).max(10000.0f); + b.add_input(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_input(N_("Seed")).min(-10000).max(10000); + b.add_output(N_("Value")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc index ae41cdfca5a..4088fa24ca7 100644 --- a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc @@ -28,10 +28,14 @@ namespace blender::nodes { static void fn_node_align_euler_to_vector_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Rotation").subtype(PROP_EULER).hide_value(); - b.add_input("Factor").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Vector").default_value({0.0, 0.0, 1.0}); - b.add_output("Rotation").subtype(PROP_EULER); + b.add_input(N_("Rotation")).subtype(PROP_EULER).hide_value(); + b.add_input(N_("Factor")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input(N_("Vector")).default_value({0.0, 0.0, 1.0}); + b.add_output(N_("Rotation")).subtype(PROP_EULER); } static void fn_node_align_euler_to_vector_layout(uiLayout *layout, diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index 09caf12e425..b44e8d54ff1 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Boolean", "Boolean"); - b.add_input("Boolean", "Boolean_001"); - b.add_output("Boolean"); + b.add_input(N_("Boolean"), "Boolean"); + b.add_input(N_("Boolean"), "Boolean_001"); + b.add_output(N_("Boolean")); }; static void fn_node_boolean_math_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index bdc4a3c1e02..2e1f2aaeeef 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -31,10 +31,10 @@ namespace blender::nodes { static void fn_node_float_compare_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("A").min(-10000.0f).max(10000.0f); - b.add_input("B").min(-10000.0f).max(10000.0f); - b.add_input("Epsilon").default_value(0.001f).min(-10000.0f).max(10000.0f); - b.add_output("Result"); + b.add_input(N_("A")).min(-10000.0f).max(10000.0f); + b.add_input(N_("B")).min(-10000.0f).max(10000.0f); + b.add_input(N_("Epsilon")).default_value(0.001f).min(-10000.0f).max(10000.0f); + b.add_output(N_("Result")); }; static void geo_node_float_compare_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index 5dccd26158b..e6ec925f945 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -30,8 +30,8 @@ namespace blender::nodes { static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Float"); - b.add_output("Integer"); + b.add_input(N_("Float")); + b.add_output(N_("Integer")); }; static void fn_node_float_to_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_input_bool.cc b/source/blender/nodes/function/nodes/node_fn_input_bool.cc index 58f8969f1b6..1358bf8a223 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_bool.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_bool.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void fn_node_input_bool_declare(NodeDeclarationBuilder &b) { - b.add_output("Boolean"); + b.add_output(N_("Boolean")); }; static void fn_node_input_bool_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc index b6079835eae..43bb654b776 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_color.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc @@ -23,7 +23,7 @@ namespace blender::nodes { static void fn_node_input_color_declare(NodeDeclarationBuilder &b) { - b.add_output("Color"); + b.add_output(N_("Color")); }; static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_input_int.cc b/source/blender/nodes/function/nodes/node_fn_input_int.cc index db52d569ac5..ddbb86e2661 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_int.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void fn_node_input_int_declare(NodeDeclarationBuilder &b) { - b.add_output("Integer"); + b.add_output(N_("Integer")); }; static void fn_node_input_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc index 11c64d3f694..c61af419e50 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc @@ -20,8 +20,8 @@ namespace blender::nodes { static void fn_node_input_special_characters_declare(NodeDeclarationBuilder &b) { - b.add_output("Line Break"); - b.add_output("Tab"); + b.add_output(N_("Line Break")); + b.add_output(N_("Tab")); }; class MF_SpecialCharacters : public fn::MultiFunction { diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index 0982096eaea..dd2d1292601 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void fn_node_input_string_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_output("String"); + b.add_output(N_("String")); }; static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index f64fd182282..1e5fd186b5a 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void fn_node_input_vector_declare(NodeDeclarationBuilder &b) { - b.add_output("Vector"); + b.add_output(N_("Vector")); }; static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_random_value.cc b/source/blender/nodes/function/nodes/node_fn_random_value.cc index 53ca77aab0c..d48b9f3461a 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_value.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_value.cc @@ -26,29 +26,29 @@ namespace blender::nodes { static void fn_node_random_value_declare(NodeDeclarationBuilder &b) { - b.add_input("Min").supports_field(); - b.add_input("Max").default_value({1.0f, 1.0f, 1.0f}).supports_field(); - b.add_input("Min", "Min_001").supports_field(); - b.add_input("Max", "Max_001").default_value(1.0f).supports_field(); - b.add_input("Min", "Min_002").min(-100000).max(100000).supports_field(); - b.add_input("Max", "Max_002") + b.add_input(N_("Min")).supports_field(); + b.add_input(N_("Max")).default_value({1.0f, 1.0f, 1.0f}).supports_field(); + b.add_input(N_("Min"), "Min_001").supports_field(); + b.add_input(N_("Max"), "Max_001").default_value(1.0f).supports_field(); + b.add_input(N_("Min"), "Min_002").min(-100000).max(100000).supports_field(); + b.add_input(N_("Max"), "Max_002") .default_value(100) .min(-100000) .max(100000) .supports_field(); - b.add_input("Probability") + b.add_input(N_("Probability")) .min(0.0f) .max(1.0f) .default_value(0.5f) .subtype(PROP_FACTOR) .supports_field(); - b.add_input("ID").implicit_field(); - b.add_input("Seed").default_value(0).min(-10000).max(10000).supports_field(); + b.add_input(N_("ID")).implicit_field(); + b.add_input(N_("Seed")).default_value(0).min(-10000).max(10000).supports_field(); - b.add_output("Value").dependent_field(); - b.add_output("Value", "Value_001").dependent_field(); - b.add_output("Value", "Value_002").dependent_field(); - b.add_output("Value", "Value_003").dependent_field(); + b.add_output(N_("Value")).dependent_field(); + b.add_output(N_("Value"), "Value_001").dependent_field(); + b.add_output(N_("Value"), "Value_002").dependent_field(); + b.add_output(N_("Value"), "Value_003").dependent_field(); } static void fn_node_random_value_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_replace_string.cc b/source/blender/nodes/function/nodes/node_fn_replace_string.cc index 1ec4979176e..881a3c68e7d 100644 --- a/source/blender/nodes/function/nodes/node_fn_replace_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_replace_string.cc @@ -22,10 +22,11 @@ namespace blender::nodes { static void fn_node_replace_string_declare(NodeDeclarationBuilder &b) { - b.add_input("String"); - b.add_input("Find").description("The string to find in the input string"); - b.add_input("Replace").description("The string to replace each match with"); - b.add_output("String"); + b.add_input(N_("String")); + b.add_input(N_("Find")).description(N_("The string to find in the input string")); + b.add_input(N_("Replace")) + .description(N_("The string to replace each match with")); + b.add_output(N_("String")); }; static std::string replace_all(std::string str, const std::string &from, const std::string &to) diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc index a01cc6b58dd..fc4c3d8221f 100644 --- a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc +++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc @@ -29,11 +29,11 @@ namespace blender::nodes { static void fn_node_rotate_euler_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Rotation").subtype(PROP_EULER).hide_value(); - b.add_input("Rotate By").subtype(PROP_EULER); - b.add_input("Axis").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); - b.add_input("Angle").subtype(PROP_ANGLE); - b.add_output("Rotation"); + b.add_input(N_("Rotation")).subtype(PROP_EULER).hide_value(); + b.add_input(N_("Rotate By")).subtype(PROP_EULER); + b.add_input(N_("Axis")).default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); + b.add_input(N_("Angle")).subtype(PROP_ANGLE); + b.add_output(N_("Rotation")); }; static void fn_node_rotate_euler_update(bNodeTree *UNUSED(ntree), bNode *node) diff --git a/source/blender/nodes/function/nodes/node_fn_slice_string.cc b/source/blender/nodes/function/nodes/node_fn_slice_string.cc index 08e17da0d92..5cb753e8f34 100644 --- a/source/blender/nodes/function/nodes/node_fn_slice_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_slice_string.cc @@ -22,10 +22,10 @@ namespace blender::nodes { static void fn_node_slice_string_declare(NodeDeclarationBuilder &b) { - b.add_input("String"); - b.add_input("Position"); - b.add_input("Length").min(0).default_value(10); - b.add_output("String"); + b.add_input(N_("String")); + b.add_input(N_("Position")); + b.add_input(N_("Length")).min(0).default_value(10); + b.add_output(N_("String")); }; static void fn_node_slice_string_build_multi_function(NodeMultiFunctionBuilder &builder) diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc index d882280b566..63429d35993 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_length.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc @@ -24,8 +24,8 @@ namespace blender::nodes { static void fn_node_string_length_declare(NodeDeclarationBuilder &b) { - b.add_input("String"); - b.add_output("Length"); + b.add_input(N_("String")); + b.add_output(N_("Length")); }; static void fn_node_string_length_build_multi_function(NodeMultiFunctionBuilder &builder) diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc index 112726f98dc..96a56760664 100644 --- a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc @@ -21,9 +21,9 @@ namespace blender::nodes { static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b) { - b.add_input("Value"); - b.add_input("Decimals").min(0); - b.add_output("String"); + b.add_input(N_("Value")); + b.add_input(N_("Decimals")).min(0); + b.add_output(N_("String")); }; static void fn_node_value_to_string_build_multi_function(NodeMultiFunctionBuilder &builder) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc index d0bb906e8af..b92d4704d63 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc @@ -26,18 +26,18 @@ namespace blender::nodes { static void geo_node_align_rotation_to_vector_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Factor"); - b.add_input("Factor", "Factor_001") + b.add_input(N_("Geometry")); + b.add_input(N_("Factor")); + b.add_input(N_("Factor"), "Factor_001") .default_value(1.0f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); - b.add_input("Vector"); - b.add_input("Vector", "Vector_001") + b.add_input(N_("Vector")); + b.add_input(N_("Vector"), "Vector_001") .default_value({0.0, 0.0, 1.0}) .subtype(PROP_ANGLE); - b.add_output("Geometry"); + b.add_output(N_("Geometry")); } static void geo_node_align_rotation_to_vector_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc index 2e931a2da98..91ff114a480 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc @@ -24,18 +24,18 @@ namespace blender::nodes { static void geo_node_attribute_clamp_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Result"); - b.add_input("Min"); - b.add_input("Max").default_value({1.0f, 1.0f, 1.0f}); - b.add_input("Min", "Min_001"); - b.add_input("Max", "Max_001").default_value(1.0f); - b.add_input("Min", "Min_002").min(-100000).max(100000); - b.add_input("Max", "Max_002").default_value(100).min(-100000).max(100000); - b.add_input("Min", "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input("Max", "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Result")); + b.add_input(N_("Min")); + b.add_input(N_("Max")).default_value({1.0f, 1.0f, 1.0f}); + b.add_input(N_("Min"), "Min_001"); + b.add_input(N_("Max"), "Max_001").default_value(1.0f); + b.add_input(N_("Min"), "Min_002").min(-100000).max(100000); + b.add_input(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000); + b.add_input(N_("Min"), "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input(N_("Max"), "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output(N_("Geometry")); } static void geo_node_attribute_clamp_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc index aa054af3acd..ab4b6aad545 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc @@ -27,10 +27,10 @@ namespace blender::nodes { static void geo_node_attribute_color_ramp_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_color_ramp_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc index 569d5a824ca..d4c23380b4e 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc @@ -23,15 +23,15 @@ namespace blender::nodes { static void geo_node_attribute_combine_xyz_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("X"); - b.add_input("X", "X_001"); - b.add_input("Y"); - b.add_input("Y", "Y_001"); - b.add_input("Z"); - b.add_input("Z", "Z_001"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("X")); + b.add_input(N_("X"), "X_001"); + b.add_input(N_("Y")); + b.add_input(N_("Y"), "Y_001"); + b.add_input(N_("Z")); + b.add_input(N_("Z"), "Z_001"); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_combine_xyz_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc index 0b9708dae14..e4e43a7b724 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc @@ -25,18 +25,18 @@ namespace blender::nodes { static void geo_node_attribute_compare_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("A"); - b.add_input("A", "A_001"); - b.add_input("A", "A_002"); - b.add_input("A", "A_003").default_value({0.5, 0.5, 0.5, 1.0}); - b.add_input("B"); - b.add_input("B", "B_001"); - b.add_input("B", "B_002"); - b.add_input("B", "B_003").default_value({0.5, 0.5, 0.5, 1.0}); - b.add_input("Threshold").default_value(0.01f).min(0.0f); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("A")); + b.add_input(N_("A"), "A_001"); + b.add_input(N_("A"), "A_002"); + b.add_input(N_("A"), "A_003").default_value({0.5, 0.5, 0.5, 1.0}); + b.add_input(N_("B")); + b.add_input(N_("B"), "B_001"); + b.add_input(N_("B"), "B_002"); + b.add_input(N_("B"), "B_003").default_value({0.5, 0.5, 0.5, 1.0}); + b.add_input(N_("Threshold")).default_value(0.01f).min(0.0f); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_compare_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc index a2382aa9d25..dc05fa2c125 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc @@ -23,10 +23,10 @@ namespace blender::nodes { static void geo_node_attribute_convert_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_convert_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc index b9621b4ae92..669ac21436f 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_attribute_curve_map_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_curve_map_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc index 1458b6df9ba..5cb49dd83d0 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc @@ -23,14 +23,14 @@ namespace blender::nodes { static void geo_node_attribute_fill_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute").is_attribute_name(); - b.add_input("Value", "Value"); - b.add_input("Value", "Value_001"); - b.add_input("Value", "Value_002"); - b.add_input("Value", "Value_003"); - b.add_input("Value", "Value_004"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")).is_attribute_name(); + b.add_input(N_("Value"), "Value"); + b.add_input(N_("Value"), "Value_001"); + b.add_input(N_("Value"), "Value_002"); + b.add_input(N_("Value"), "Value_003"); + b.add_input(N_("Value"), "Value_004"); + b.add_output(N_("Geometry")); } static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc index 0ea3bbe1e45..978c75187fe 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc @@ -26,21 +26,21 @@ namespace blender::nodes { static void geo_node_attribute_map_range_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Result"); - b.add_input("From Min"); - b.add_input("From Max").default_value(1.0f); - b.add_input("To Min"); - b.add_input("To Max").default_value(1.0f); - b.add_input("Steps").default_value(4.0f); - b.add_input("From Min", "From Min_001"); - b.add_input("From Max", "From Max_001").default_value({1.0f, 1.0f, 1.0f}); - b.add_input("To Min", "To Min_001"); - b.add_input("To Max", "To Max_001").default_value({1.0f, 1.0f, 1.0f}); - b.add_input("Steps", "Steps_001").default_value({4.0f, 4.0f, 4.0f}); - b.add_input("Clamp"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Result")); + b.add_input(N_("From Min")); + b.add_input(N_("From Max")).default_value(1.0f); + b.add_input(N_("To Min")); + b.add_input(N_("To Max")).default_value(1.0f); + b.add_input(N_("Steps")).default_value(4.0f); + b.add_input(N_("From Min"), "From Min_001"); + b.add_input(N_("From Max"), "From Max_001").default_value({1.0f, 1.0f, 1.0f}); + b.add_input(N_("To Min"), "To Min_001"); + b.add_input(N_("To Max"), "To Max_001").default_value({1.0f, 1.0f, 1.0f}); + b.add_input(N_("Steps"), "Steps_001").default_value({4.0f, 4.0f, 4.0f}); + b.add_input(N_("Clamp")); + b.add_output(N_("Geometry")); } static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc index efa09215b45..55d35f87cda 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc @@ -29,15 +29,15 @@ namespace blender::nodes { static void geo_node_attribute_math_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("A"); - b.add_input("A", "A_001"); - b.add_input("B"); - b.add_input("B", "B_001"); - b.add_input("C"); - b.add_input("C", "C_001"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("A")); + b.add_input(N_("A"), "A_001"); + b.add_input(N_("B")); + b.add_input(N_("B"), "B_001"); + b.add_input(N_("C")); + b.add_input(N_("C"), "C_001"); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static bool operation_use_input_c(const NodeMathOperation operation) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc index 74e05cb997d..b4205bc91b7 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc @@ -29,23 +29,23 @@ namespace blender::nodes { static void geo_node_mix_attribute_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Factor"); - b.add_input("Factor", "Factor_001") + b.add_input(N_("Geometry")); + b.add_input(N_("Factor")); + b.add_input(N_("Factor"), "Factor_001") .default_value(0.5f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); - b.add_input("A"); - b.add_input("A", "A_001"); - b.add_input("A", "A_002"); - b.add_input("A", "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input("B"); - b.add_input("B", "B_001"); - b.add_input("B", "B_002"); - b.add_input("B", "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("A")); + b.add_input(N_("A"), "A_001"); + b.add_input(N_("A"), "A_002"); + b.add_input(N_("A"), "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input(N_("B")); + b.add_input(N_("B"), "B_001"); + b.add_input(N_("B"), "B_002"); + b.add_input(N_("B"), "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc index 6120118f611..9e3a7984c53 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc @@ -30,11 +30,11 @@ namespace blender::nodes { static void geo_node_attribute_proximity_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Target"); - b.add_input("Distance"); - b.add_input("Position"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Target")); + b.add_input(N_("Distance")); + b.add_input(N_("Position")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_proximity_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc index 2e6ba456725..2901472d661 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc @@ -27,16 +27,16 @@ namespace blender::nodes { static void geo_node_legacy_attribute_randomize_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Min"); - b.add_input("Max").default_value({1.0f, 1.0f, 1.0f}); - b.add_input("Min", "Min_001"); - b.add_input("Max", "Max_001").default_value(1.0f); - b.add_input("Min", "Min_002").min(-100000).max(100000); - b.add_input("Max", "Max_002").default_value(100).min(-100000).max(100000); - b.add_input("Seed").min(-10000).max(10000); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Min")); + b.add_input(N_("Max")).default_value({1.0f, 1.0f, 1.0f}); + b.add_input(N_("Min"), "Min_001"); + b.add_input(N_("Max"), "Max_001").default_value(1.0f); + b.add_input(N_("Min"), "Min_002").min(-100000).max(100000); + b.add_input(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000); + b.add_input(N_("Seed")).min(-10000).max(10000); + b.add_output(N_("Geometry")); } static void geo_node_legacy_attribute_random_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc index 52f97475941..19d6ced6eb6 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc @@ -32,11 +32,11 @@ namespace blender::nodes { static void geo_node_attribute_sample_texture_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Texture").hide_label(); - b.add_input("Mapping"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Texture")).hide_label(); + b.add_input(N_("Mapping")); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static AttributeDomain get_result_domain(const GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc index de0090406c6..809e75e73a3 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc @@ -23,13 +23,13 @@ namespace blender::nodes { static void geo_node_attribute_separate_xyz_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Vector"); - b.add_input("Vector", "Vector_001"); - b.add_input("Result X"); - b.add_input("Result Y"); - b.add_input("Result Z"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Vector")); + b.add_input(N_("Vector"), "Vector_001"); + b.add_input(N_("Result X")); + b.add_input(N_("Result Y")); + b.add_input(N_("Result Z")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_separate_xyz_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc index d7a66dac3ad..3a9cd52661a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc @@ -33,11 +33,11 @@ namespace blender::nodes { static void geo_node_attribute_transfer_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Source Geometry"); - b.add_input("Source"); - b.add_input("Destination"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Source Geometry")); + b.add_input(N_("Source")); + b.add_input(N_("Destination")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_transfer_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc index 59903050f88..4c351846243 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc @@ -30,17 +30,17 @@ namespace blender::nodes { static void geo_node_attribute_vector_math_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("A"); - b.add_input("A", "A_001"); - b.add_input("B"); - b.add_input("B", "B_001"); - b.add_input("B", "B_002"); - b.add_input("C"); - b.add_input("C", "C_001"); - b.add_input("C", "C_002"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("A")); + b.add_input(N_("A"), "A_001"); + b.add_input(N_("B")); + b.add_input(N_("B"), "B_001"); + b.add_input(N_("B"), "B_002"); + b.add_input(N_("C")); + b.add_input(N_("C"), "C_001"); + b.add_input(N_("C"), "C_002"); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static bool operation_use_input_b(const NodeVectorMathOperation operation) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc index 0c515fa63fb..9ab8ec25fb6 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc @@ -25,21 +25,21 @@ namespace blender::nodes { static void geo_node_attribute_vector_rotate_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Vector"); - b.add_input("Vector", "Vector_001").min(0.0f).max(1.0f).hide_value(); - b.add_input("Center"); - b.add_input("Center", "Center_001").subtype(PROP_XYZ); - b.add_input("Axis"); - b.add_input("Axis", "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); - b.add_input("Angle"); - b.add_input("Angle", "Angle_001").subtype(PROP_ANGLE); - b.add_input("Rotation"); - b.add_input("Rotation", "Rotation_001").subtype(PROP_EULER); - b.add_input("Invert"); - b.add_input("Result"); + b.add_input(N_("Geometry")); + b.add_input(N_("Vector")); + b.add_input(N_("Vector"), "Vector_001").min(0.0f).max(1.0f).hide_value(); + b.add_input(N_("Center")); + b.add_input(N_("Center"), "Center_001").subtype(PROP_XYZ); + b.add_input(N_("Axis")); + b.add_input(N_("Axis"), "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); + b.add_input(N_("Angle")); + b.add_input(N_("Angle"), "Angle_001").subtype(PROP_ANGLE); + b.add_input(N_("Rotation")); + b.add_input(N_("Rotation"), "Rotation_001").subtype(PROP_EULER); + b.add_input(N_("Invert")); + b.add_input(N_("Result")); - b.add_output("Geometry"); + b.add_output(N_("Geometry")); } static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc index 85d1392aa35..8b81008ff34 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void geo_node_curve_endpoints_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_output("Start Points"); - b.add_output("End Points"); + b.add_input(N_("Geometry")); + b.add_output(N_("Start Points")); + b.add_output(N_("End Points")); } /** diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc index d1c81333c30..ba76fafe3e6 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc @@ -24,9 +24,9 @@ namespace blender::nodes { static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_input("Selection"); - b.add_output("Curve"); + b.add_input(N_("Curve")); + b.add_input(N_("Selection")); + b.add_output(N_("Curve")); } static void geo_node_curve_reverse_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc index 0d3de7ac5f5..40d827ae141 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_select_by_handle_type_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Selection"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")); + b.add_output(N_("Geometry")); } static void geo_node_curve_select_by_handle_type_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc index 339029336d9..4bac9cb976e 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc @@ -25,9 +25,9 @@ namespace blender::nodes { static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_input("Selection"); - b.add_output("Curve"); + b.add_input(N_("Curve")); + b.add_input(N_("Selection")); + b.add_output(N_("Curve")); } static void geo_node_curve_set_handles_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc index 44522e990d9..df53c96e6ca 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_legacy_curve_spline_type_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_input("Selection"); - b.add_output("Curve"); + b.add_input(N_("Curve")); + b.add_input(N_("Selection")); + b.add_output(N_("Curve")); } static void geo_node_legacy_curve_spline_type_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc index 61165902028..f9b0a9d128e 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc @@ -33,10 +33,10 @@ namespace blender::nodes { static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Cuts"); - b.add_input("Cuts", "Cuts_001").default_value(1).min(0).max(1000); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Cuts")); + b.add_input(N_("Cuts"), "Cuts_001").default_value(1).min(0).max(1000); + b.add_output(N_("Geometry")); } static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc index 2936c150376..c171d485a6a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc @@ -30,10 +30,10 @@ namespace blender::nodes { static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Count").default_value(10).min(2).max(100000); - b.add_input("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Count")).default_value(10).min(2).max(100000); + b.add_input(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_output(N_("Geometry")); } static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc index 2d9b4da4c83..1d76a0532a1 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc @@ -47,10 +47,10 @@ namespace blender::nodes { static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Selection"); - b.add_input("Invert"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")); + b.add_input(N_("Invert")); + b.add_output(N_("Geometry")); } template static void copy_data(Span data, MutableSpan r_data, IndexMask mask) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc index d7e908edf61..8f2bf05d2b4 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc @@ -26,15 +26,15 @@ namespace blender::nodes { static void geo_node_edge_split_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Edge Angle").default_value(true); - b.add_input("Angle") + b.add_input(N_("Geometry")); + b.add_input(N_("Edge Angle")).default_value(true); + b.add_input(N_("Angle")) .default_value(DEG2RADF(30.0f)) .min(0.0f) .max(DEG2RADF(180.0f)) .subtype(PROP_ANGLE); - b.add_input("Sharp Edges"); - b.add_output("Geometry"); + b.add_input(N_("Sharp Edges")); + b.add_output(N_("Geometry")); } static void geo_node_edge_split_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc index 7d3481c1067..333a17aa4e9 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_legacy_material_assign_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Material").hide_label(true); - b.add_input("Selection"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Material")).hide_label(true); + b.add_input(N_("Selection")); + b.add_output(N_("Geometry")); } static void assign_material_to_faces(Mesh &mesh, const VArray &face_mask, Material *material) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc index 7a27e856cef..9167096fd3d 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc @@ -22,9 +22,9 @@ namespace blender::nodes { static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh"); - b.add_input("Selection"); - b.add_output("Curve"); + b.add_input(N_("Mesh")); + b.add_input(N_("Selection")); + b.add_output(N_("Curve")); } static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc index f30feb48734..210757f986d 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc @@ -42,16 +42,16 @@ namespace blender::nodes { static void geo_node_point_distribute_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Distance Min").min(0.0f).max(100000.0f).subtype(PROP_DISTANCE); - b.add_input("Density Max") + b.add_input(N_("Geometry")); + b.add_input(N_("Distance Min")).min(0.0f).max(100000.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Density Max")) .default_value(1.0f) .min(0.0f) .max(100000.0f) .subtype(PROP_NONE); - b.add_input("Density Attribute"); - b.add_input("Seed").min(-10000).max(10000); - b.add_output("Geometry"); + b.add_input(N_("Density Attribute")); + b.add_input(N_("Seed")).min(-10000).max(10000); + b.add_output(N_("Geometry")); } static void geo_node_point_distribute_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc index b68dfe44984..ffb2a0dd7ac 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc @@ -28,12 +28,12 @@ namespace blender::nodes { static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Object").hide_label(); - b.add_input("Collection").hide_label(); - b.add_input("Instance Geometry"); - b.add_input("Seed").min(-10000).max(10000); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Object")).hide_label(); + b.add_input(N_("Collection")).hide_label(); + b.add_input(N_("Instance Geometry")); + b.add_input(N_("Seed")).min(-10000).max(10000); + b.add_output(N_("Geometry")); } static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc index 60c82360007..54d36dab98d 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc @@ -25,14 +25,16 @@ namespace blender::nodes { static void geo_node_point_rotate_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Axis"); - b.add_input("Axis", "Axis_001").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); - b.add_input("Angle"); - b.add_input("Angle", "Angle_001").subtype(PROP_ANGLE); - b.add_input("Rotation"); - b.add_input("Rotation", "Rotation_001").subtype(PROP_EULER); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Axis")); + b.add_input(N_("Axis"), "Axis_001") + .default_value({0.0, 0.0, 1.0}) + .subtype(PROP_XYZ); + b.add_input(N_("Angle")); + b.add_input(N_("Angle"), "Angle_001").subtype(PROP_ANGLE); + b.add_input(N_("Rotation")); + b.add_input(N_("Rotation"), "Rotation_001").subtype(PROP_EULER); + b.add_output(N_("Geometry")); } static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc index 99adce149e9..934442ee8a3 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc @@ -25,13 +25,13 @@ namespace blender::nodes { static void geo_node_point_scale_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Factor"); - b.add_input("Factor", "Factor_001") + b.add_input(N_("Geometry")); + b.add_input(N_("Factor")); + b.add_input(N_("Factor"), "Factor_001") .default_value({1.0f, 1.0f, 1.0f}) .subtype(PROP_XYZ); - b.add_input("Factor", "Factor_002").default_value(1.0f).min(0.0f); - b.add_output("Geometry"); + b.add_input(N_("Factor"), "Factor_002").default_value(1.0f).min(0.0f); + b.add_output(N_("Geometry")); } static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc index 48b6676c1dd..accdaf78439 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc @@ -27,10 +27,10 @@ namespace blender::nodes { static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Mask"); - b.add_output("Geometry 1"); - b.add_output("Geometry 2"); + b.add_input(N_("Geometry")); + b.add_input(N_("Mask")); + b.add_output(N_("Geometry 1")); + b.add_output(N_("Geometry 2")); } template diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc index f2fce45c57b..34f7641995f 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc @@ -23,10 +23,10 @@ namespace blender::nodes { static void geo_node_point_translate_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Translation"); - b.add_input("Translation", "Translation_001").subtype(PROP_TRANSLATION); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Translation")); + b.add_input(N_("Translation"), "Translation_001").subtype(PROP_TRANSLATION); + b.add_output(N_("Geometry")); } static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc index 68d3f232ce1..cf7f466c2a6 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc @@ -32,13 +32,13 @@ namespace blender::nodes { static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Density").default_value(1.0f).min(0.0f); - b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input("Radius"); - b.add_input("Radius", "Radius_001").default_value(0.5f).min(0.0f); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Density")).default_value(1.0f).min(0.0f); + b.add_input(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input(N_("Radius")); + b.add_input(N_("Radius"), "Radius_001").default_value(0.5f).min(0.0f); + b.add_output(N_("Geometry")); } static void geo_node_points_to_volume_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc index 6641e622362..e6a81fc9627 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc @@ -28,23 +28,23 @@ namespace blender::nodes { static void geo_node_raycast_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Target Geometry"); - b.add_input("Ray Direction"); - b.add_input("Ray Direction", "Ray Direction_001") + b.add_input(N_("Geometry")); + b.add_input(N_("Target Geometry")); + b.add_input(N_("Ray Direction")); + b.add_input(N_("Ray Direction"), "Ray Direction_001") .default_value({0.0f, 0.0f, 1.0f}); - b.add_input("Ray Length"); - b.add_input("Ray Length", "Ray Length_001") + b.add_input(N_("Ray Length")); + b.add_input(N_("Ray Length"), "Ray Length_001") .default_value(100.0f) .min(0.0f) .subtype(PROP_DISTANCE); - b.add_input("Target Attribute"); - b.add_input("Is Hit"); - b.add_input("Hit Position"); - b.add_input("Hit Normal"); - b.add_input("Hit Distance"); - b.add_input("Hit Attribute"); - b.add_output("Geometry"); + b.add_input(N_("Target Attribute")); + b.add_input(N_("Is Hit")); + b.add_input(N_("Hit Position")); + b.add_input(N_("Hit Normal")); + b.add_input(N_("Hit Distance")); + b.add_input(N_("Hit Attribute")); + b.add_output(N_("Geometry")); } static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc index eabdd2bcd5a..a8d6f33a5fd 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc @@ -30,10 +30,10 @@ namespace blender::nodes { static void geo_node_legacy_select_by_material_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Material").hide_label(); - b.add_input("Selection"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Material")).hide_label(); + b.add_input(N_("Selection")); + b.add_output(N_("Geometry")); } static void select_mesh_by_material(const Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc index 101c915eb77..295cd05fd01 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc @@ -27,10 +27,10 @@ namespace blender::nodes { static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Level").default_value(1).min(0).max(6); - b.add_input("Use Creases"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Level")).default_value(1).min(0).max(6); + b.add_input(N_("Use Creases")); + b.add_output(N_("Geometry")); } static void geo_node_subdivision_surface_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc index 45f55dcf92e..39af5bf1fd2 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc @@ -39,13 +39,13 @@ namespace blender::nodes { static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Density"); - b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input("Threshold").default_value(0.1f).min(0.0f); - b.add_input("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Density")); + b.add_input(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input(N_("Threshold")).default_value(0.1f).min(0.0f); + b.add_input(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output(N_("Geometry")); } static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 8ad70cd6d3f..5cc8f1476f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -25,19 +25,19 @@ namespace blender::nodes { static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Value").supports_field(); - b.add_input("Value", "Value_001").supports_field(); - b.add_input("Value", "Value_002").supports_field(); - b.add_input("Value", "Value_003").supports_field(); - b.add_input("Value", "Value_004").supports_field(); + b.add_input(N_("Geometry")); + b.add_input(N_("Value")).supports_field(); + b.add_input(N_("Value"), "Value_001").supports_field(); + b.add_input(N_("Value"), "Value_002").supports_field(); + b.add_input(N_("Value"), "Value_003").supports_field(); + b.add_input(N_("Value"), "Value_004").supports_field(); - b.add_output("Geometry"); - b.add_output("Attribute").field_source(); - b.add_output("Attribute", "Attribute_001").field_source(); - b.add_output("Attribute", "Attribute_002").field_source(); - b.add_output("Attribute", "Attribute_003").field_source(); - b.add_output("Attribute", "Attribute_004").field_source(); + b.add_output(N_("Geometry")); + b.add_output(N_("Attribute")).field_source(); + b.add_output(N_("Attribute"), "Attribute_001").field_source(); + b.add_output(N_("Attribute"), "Attribute_002").field_source(); + b.add_output(N_("Attribute"), "Attribute_003").field_source(); + b.add_output(N_("Attribute"), "Attribute_004").field_source(); } static void geo_node_attribute_capture_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index 65a137a028d..f80b8ccc971 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -20,9 +20,9 @@ namespace blender::nodes { static void geo_node_attribute_remove_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute").multi_input(); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")).multi_input(); + b.add_output(N_("Geometry")); } static void remove_attribute(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 1b7d2fe28a1..155bd8c8c28 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -28,27 +28,27 @@ namespace blender::nodes { static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute").hide_value().supports_field(); - b.add_input("Attribute", "Attribute_001").hide_value().supports_field(); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")).hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_001").hide_value().supports_field(); - b.add_output("Mean"); - b.add_output("Median"); - b.add_output("Sum"); - b.add_output("Min"); - b.add_output("Max"); - b.add_output("Range"); - b.add_output("Standard Deviation"); - b.add_output("Variance"); + b.add_output(N_("Mean")); + b.add_output(N_("Median")); + b.add_output(N_("Sum")); + b.add_output(N_("Min")); + b.add_output(N_("Max")); + b.add_output(N_("Range")); + b.add_output(N_("Standard Deviation")); + b.add_output(N_("Variance")); - b.add_output("Mean", "Mean_001"); - b.add_output("Median", "Median_001"); - b.add_output("Sum", "Sum_001"); - b.add_output("Min", "Min_001"); - b.add_output("Max", "Max_001"); - b.add_output("Range", "Range_001"); - b.add_output("Standard Deviation", "Standard Deviation_001"); - b.add_output("Variance", "Variance_001"); + b.add_output(N_("Mean"), "Mean_001"); + b.add_output(N_("Median"), "Median_001"); + b.add_output(N_("Sum"), "Sum_001"); + b.add_output(N_("Min"), "Min_001"); + b.add_output(N_("Max"), "Max_001"); + b.add_output(N_("Range"), "Range_001"); + b.add_output(N_("Standard Deviation"), "Standard Deviation_001"); + b.add_output(N_("Variance"), "Variance_001"); } static void geo_node_attribute_statistic_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 63b88c57694..516f07b7ad3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -27,13 +27,13 @@ namespace blender::nodes { static void geo_node_boolean_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh 1") + b.add_input(N_("Mesh 1")) .only_realized_data() .supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Mesh 2").multi_input().supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Self Intersection"); - b.add_input("Hole Tolerant"); - b.add_output("Mesh"); + b.add_input(N_("Mesh 2")).multi_input().supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Self Intersection")); + b.add_input(N_("Hole Tolerant")); + b.add_output(N_("Mesh")); } static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index e34b65e6c94..e7c9715934a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_bounding_box_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_output("Bounding Box"); - b.add_output("Min"); - b.add_output("Max"); + b.add_input(N_("Geometry")); + b.add_output(N_("Bounding Box")); + b.add_output(N_("Min")); + b.add_output(N_("Max")); } static void geo_node_bounding_box_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index 18fc09daf01..f068e621596 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -31,15 +31,15 @@ namespace blender::nodes { static void geo_node_collection_info_declare(NodeDeclarationBuilder &b) { - b.add_input("Collection").hide_label(); - b.add_input("Separate Children") + b.add_input(N_("Collection")).hide_label(); + b.add_input(N_("Separate Children")) .description( - "Output each child of the collection as a separate instance, sorted alphabetically"); - b.add_input("Reset Children") + N_("Output each child of the collection as a separate instance, sorted alphabetically")); + b.add_input(N_("Reset Children")) .description( - "Reset the transforms of every child instance in the output. Only used when Separate " - "Children is enabled"); - b.add_output("Geometry"); + N_("Reset the transforms of every child instance in the output. Only used when Separate " + "Children is enabled")); + b.add_output(N_("Geometry")); } static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 4377d32210d..3cf682e161c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -32,8 +32,8 @@ namespace blender::nodes { static void geo_node_convex_hull_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_output("Convex Hull"); + b.add_input(N_("Geometry")); + b.add_output(N_("Convex Hull")); } using bke::GeometryInstanceGroup; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc index fbe5af3bb18..42d88cdb1e7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -25,19 +25,20 @@ namespace blender::nodes { static void geo_node_curve_endpoint_selection_declare(NodeDeclarationBuilder &b) { - b.add_input("Start Size") + b.add_input(N_("Start Size")) .min(0) .default_value(1) .supports_field() - .description("The amount of points to select from the start of each spline"); - b.add_input("End Size") + .description(N_("The amount of points to select from the start of each spline")); + b.add_input(N_("End Size")) .min(0) .default_value(1) .supports_field() - .description("The amount of points to select from the end of each spline"); - b.add_output("Selection") + .description(N_("The amount of points to select from the end of each spline")); + b.add_output(N_("Selection")) .field_source() - .description("The selection from the start and end of the splines based on the input sizes"); + .description( + N_("The selection from the start and end of the splines based on the input sizes")); } static void select_by_spline(const int start, const int end, MutableSpan r_selection) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 75459a97816..219effadec4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -35,8 +35,8 @@ namespace blender::nodes { static void geo_node_curve_fill_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_output("Mesh"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_output(N_("Mesh")); } static void geo_node_curve_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 5b6dfb01f38..27d7d22b106 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -29,16 +29,16 @@ namespace blender::nodes { static void geo_node_curve_fillet_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Count").default_value(1).min(1).max(1000).supports_field(); - b.add_input("Radius") + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Count")).default_value(1).min(1).max(1000).supports_field(); + b.add_input(N_("Radius")) .min(0.0f) .max(FLT_MAX) .subtype(PropertySubType::PROP_DISTANCE) .default_value(0.25f) .supports_field(); - b.add_input("Limit Radius"); - b.add_output("Curve"); + b.add_input(N_("Limit Radius")); + b.add_output(N_("Curve")); } static void geo_node_curve_fillet_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index 3ae330fd5cd..165f5da5f71 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void geo_node_curve_handle_type_selection_declare(NodeDeclarationBuilder &b) { - b.add_output("Selection").field_source(); + b.add_output(N_("Selection")).field_source(); } static void geo_node_curve_handle_type_selection_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index ef617ed3742..0d0dc0ec89c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -21,8 +21,8 @@ namespace blender::nodes { static void geo_node_curve_length_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_output("Length"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_output(N_("Length")); } static void geo_node_curve_length_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc index 938f5f22a03..4c89aba2e6d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b) { - b.add_output("Factor").field_source(); + b.add_output(N_("Factor")).field_source(); } /** diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index 313473e3442..a755d47cc6a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -25,14 +25,20 @@ namespace blender::nodes { static void geo_node_curve_primitive_bezier_segment_declare(NodeDeclarationBuilder &b) { - b.add_input("Resolution").default_value(16).min(1).max(256).subtype(PROP_UNSIGNED); - b.add_input("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input("Start Handle") + b.add_input(N_("Resolution")) + .default_value(16) + .min(1) + .max(256) + .subtype(PROP_UNSIGNED); + b.add_input(N_("Start")) + .default_value({-1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION); + b.add_input(N_("Start Handle")) .default_value({-0.5f, 0.5f, 0.0f}) .subtype(PROP_TRANSLATION); - b.add_input("End Handle").subtype(PROP_TRANSLATION); - b.add_input("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_output("Curve"); + b.add_input(N_("End Handle")).subtype(PROP_TRANSLATION); + b.add_input(N_("End")).default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + b.add_output(N_("Curve")); } static void geo_node_curve_primitive_bezier_segment_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index 8d5f4855512..bf4f22d6578 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -25,15 +25,19 @@ namespace blender::nodes { static void geo_node_curve_primitive_circle_declare(NodeDeclarationBuilder &b) { - b.add_input("Resolution").default_value(32).min(3).max(512); - b.add_input("Point 1") + b.add_input(N_("Resolution")).default_value(32).min(3).max(512); + b.add_input(N_("Point 1")) .default_value({-1.0f, 0.0f, 0.0f}) .subtype(PROP_TRANSLATION); - b.add_input("Point 2").default_value({0.0f, 1.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input("Point 3").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Curve"); - b.add_output("Center"); + b.add_input(N_("Point 2")) + .default_value({0.0f, 1.0f, 0.0f}) + .subtype(PROP_TRANSLATION); + b.add_input(N_("Point 3")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION); + b.add_input(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output(N_("Curve")); + b.add_output(N_("Center")); } static void geo_node_curve_primitive_circle_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index a3d2ada612f..5b215797052 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -25,11 +25,11 @@ namespace blender::nodes { static void geo_node_curve_primitive_line_declare(NodeDeclarationBuilder &b) { - b.add_input("Start").subtype(PROP_TRANSLATION); - b.add_input("End").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_input("Direction").default_value({0.0f, 0.0f, 1.0f}); - b.add_input("Length").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_output("Curve"); + b.add_input(N_("Start")).subtype(PROP_TRANSLATION); + b.add_input(N_("End")).default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); + b.add_input(N_("Direction")).default_value({0.0f, 0.0f, 1.0f}); + b.add_input(N_("Length")).default_value(1.0f).subtype(PROP_DISTANCE); + b.add_output(N_("Curve")); } static void geo_node_curve_primitive_line_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc index a54fd971ac4..6041ddee02d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc @@ -21,11 +21,19 @@ namespace blender::nodes { static void geo_node_curve_primitive_quadratic_bezier_declare(NodeDeclarationBuilder &b) { - b.add_input("Resolution").default_value(16).min(3).max(256).subtype(PROP_UNSIGNED); - b.add_input("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input("Middle").default_value({0.0f, 2.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_output("Curve"); + b.add_input(N_("Resolution")) + .default_value(16) + .min(3) + .max(256) + .subtype(PROP_UNSIGNED); + b.add_input(N_("Start")) + .default_value({-1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION); + b.add_input(N_("Middle")) + .default_value({0.0f, 2.0f, 0.0f}) + .subtype(PROP_TRANSLATION); + b.add_input(N_("End")).default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + b.add_output(N_("Curve")); } static std::unique_ptr create_quadratic_bezier_curve(const float3 p1, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index 07ddaa8f61e..7260da05a8d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -23,18 +23,32 @@ namespace blender::nodes { static void geo_node_curve_primitive_quadrilateral_declare(NodeDeclarationBuilder &b) { - b.add_input("Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Height").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Bottom Width").default_value(4.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Top Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Offset").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input("Bottom Height").default_value(3.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Top Height").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input("Point 1").default_value({-1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input("Point 2").default_value({1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input("Point 3").default_value({1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input("Point 4").default_value({-1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_output("Curve"); + b.add_input(N_("Width")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Height")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Bottom Width")) + .default_value(4.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Top Width")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Offset")).default_value(1.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Bottom Height")) + .default_value(3.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Top Height")).default_value(1.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Point 1")) + .default_value({-1.0f, -1.0f, 0.0f}) + .subtype(PROP_DISTANCE); + b.add_input(N_("Point 2")) + .default_value({1.0f, -1.0f, 0.0f}) + .subtype(PROP_DISTANCE); + b.add_input(N_("Point 3")) + .default_value({1.0f, 1.0f, 0.0f}) + .subtype(PROP_DISTANCE); + b.add_input(N_("Point 4")) + .default_value({-1.0f, 1.0f, 0.0f}) + .subtype(PROP_DISTANCE); + b.add_output(N_("Curve")); } static void geo_node_curve_primitive_quadrilateral_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index 7292fafc8b0..1dc9cd7f107 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -22,13 +22,17 @@ namespace blender::nodes { static void geo_node_curve_primitive_spiral_declare(NodeDeclarationBuilder &b) { - b.add_input("Resolution").default_value(32).min(1).max(1024).subtype(PROP_UNSIGNED); - b.add_input("Rotations").default_value(2.0f).min(0.0f); - b.add_input("Start Radius").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input("End Radius").default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input("Height").default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input("Reverse"); - b.add_output("Curve"); + b.add_input(N_("Resolution")) + .default_value(32) + .min(1) + .max(1024) + .subtype(PROP_UNSIGNED); + b.add_input(N_("Rotations")).default_value(2.0f).min(0.0f); + b.add_input(N_("Start Radius")).default_value(1.0f).subtype(PROP_DISTANCE); + b.add_input(N_("End Radius")).default_value(2.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Height")).default_value(2.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Reverse")); + b.add_output(N_("Curve")); } static std::unique_ptr create_spiral_curve(const float rotations, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index 6261146562d..b5bafce17c6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -22,11 +22,17 @@ namespace blender::nodes { static void geo_node_curve_primitive_star_declare(NodeDeclarationBuilder &b) { - b.add_input("Points").default_value(8).min(3).max(256).subtype(PROP_UNSIGNED); - b.add_input("Inner Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Outer Radius").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Twist").subtype(PROP_ANGLE); - b.add_output("Curve"); + b.add_input(N_("Points")).default_value(8).min(3).max(256).subtype(PROP_UNSIGNED); + b.add_input(N_("Inner Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Outer Radius")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Twist")).subtype(PROP_ANGLE); + b.add_output(N_("Curve")); } static std::unique_ptr create_star_curve(const float inner_radius, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index c0134e802cf..945dac5650b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -34,11 +34,14 @@ namespace blender::nodes { static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Count").default_value(10).min(1).max(100000).supports_field(); - b.add_input("Length").default_value(0.1f).min(0.001f).supports_field().subtype( - PROP_DISTANCE); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Count")).default_value(10).min(1).max(100000).supports_field(); + b.add_input(N_("Length")) + .default_value(0.1f) + .min(0.001f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output(N_("Curve")); } static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index f23d409741f..745012c1851 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -24,9 +24,9 @@ namespace blender::nodes { static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output(N_("Curve")); } static void geo_node_curve_reverse_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 02d1e35cd07..31b38c0dce7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -27,14 +27,15 @@ namespace blender::nodes { static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").only_realized_data().supported_type( - GEO_COMPONENT_TYPE_CURVE); - b.add_input("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); - b.add_input("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field(); + b.add_input(N_("Curve")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Factor")).min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); + b.add_input(N_("Length")).min(0.0f).subtype(PROP_DISTANCE).supports_field(); - b.add_output("Position").dependent_field(); - b.add_output("Tangent").dependent_field(); - b.add_output("Normal").dependent_field(); + b.add_output(N_("Position")).dependent_field(); + b.add_output(N_("Tangent")).dependent_field(); + b.add_output(N_("Normal")).dependent_field(); } static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc index 8ee2ee76780..8b0a6ca840c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -25,9 +25,9 @@ namespace blender::nodes { static void geo_node_curve_set_handles_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output(N_("Curve")); } static void geo_node_curve_set_handles_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index ddbd2d74dd6..ae4453929ac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output(N_("Curve")); } static void geo_node_curve_spline_type_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index a0a62270b2d..b52de822c22 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -33,9 +33,9 @@ namespace blender::nodes { static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Cuts").default_value(1).min(0).max(1000).supports_field(); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Cuts")).default_value(1).min(0).max(1000).supports_field(); + b.add_output(N_("Curve")); } static Array get_subdivided_offsets(const Spline &spline, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index b6aa550cf2e..1977b465de4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -27,14 +27,14 @@ namespace blender::nodes { static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Profile Curve") + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Profile Curve")) .only_realized_data() .supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Fill Caps") + b.add_input(N_("Fill Caps")) .description( - "If the profile spline is cyclic, fill the ends of the generated mesh with N-gons"); - b.add_output("Mesh"); + N_("If the profile spline is cyclic, fill the ends of the generated mesh with N-gons")); + b.add_output(N_("Mesh")); } static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 0f74a9edcf9..38d7fb99e87 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -30,13 +30,13 @@ namespace blender::nodes { static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Count").default_value(10).min(2).max(100000); - b.add_input("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); - b.add_output("Points"); - b.add_output("Tangent").field_source(); - b.add_output("Normal").field_source(); - b.add_output("Rotation").field_source(); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Count")).default_value(10).min(2).max(100000); + b.add_input(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_output(N_("Points")); + b.add_output(N_("Tangent")).field_source(); + b.add_output(N_("Normal")).field_source(); + b.add_output(N_("Rotation")).field_source(); } static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 44a403b80a5..4e1a2910c7c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -28,21 +28,24 @@ namespace blender::nodes { static void geo_node_curve_trim_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); - b.add_input("End") + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Start")).min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); + b.add_input(N_("End")) .min(0.0f) .max(1.0f) .default_value(1.0f) .subtype(PROP_FACTOR) .supports_field(); - b.add_input("Start", "Start_001").min(0.0f).subtype(PROP_DISTANCE).supports_field(); - b.add_input("End", "End_001") + b.add_input(N_("Start"), "Start_001") + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_input(N_("End"), "End_001") .min(0.0f) .default_value(1.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output("Curve"); + b.add_output(N_("Curve")); } static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index e293cd9b8fe..e0a3faaefb0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -50,13 +50,13 @@ namespace blender::nodes { static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Selection") + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")) .default_value(true) .hide_value() .supports_field() - .description("The parts of the geometry to be deleted"); - b.add_output("Geometry"); + .description(N_("The parts of the geometry to be deleted")); + b.add_output(N_("Geometry")); } static void geo_node_delete_geometry_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 21e6004347e..fa439b04da0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -42,22 +42,22 @@ namespace blender::nodes { static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Distance Min").min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Density Max").default_value(10.0f).min(0.0f); - b.add_input("Density").default_value(10.0f).supports_field(); - b.add_input("Density Factor") + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Distance Min")).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Density Max")).default_value(10.0f).min(0.0f); + b.add_input(N_("Density")).default_value(10.0f).supports_field(); + b.add_input(N_("Density Factor")) .default_value(1.0f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR) .supports_field(); - b.add_input("Seed"); + b.add_input(N_("Seed")); - b.add_output("Points"); - b.add_output("Normal").field_source(); - b.add_output("Rotation").subtype(PROP_EULER).field_source(); + b.add_output(N_("Points")); + b.add_output(N_("Normal")).field_source(); + b.add_output(N_("Rotation")).subtype(PROP_EULER).field_source(); } static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 2f9d3a60ba3..f562fb29e90 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -26,9 +26,9 @@ namespace blender::nodes { static void geo_node_edge_split_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_output("Mesh"); + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output(N_("Mesh")); } static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection) diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc index 02f99b6cf94..e1c72fbd438 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc @@ -36,12 +36,13 @@ namespace blender::nodes { static void geo_node_image_texture_declare(NodeDeclarationBuilder &b) { - b.add_input("Image").hide_label(); - b.add_input("Vector").implicit_field().description( - "Texture coordinates from 0 to 1"); - b.add_input("Frame").min(0).max(MAXFRAMEF); - b.add_output("Color").no_muted_links().dependent_field(); - b.add_output("Alpha").no_muted_links().dependent_field(); + b.add_input(N_("Image")).hide_label(); + b.add_input(N_("Vector")) + .implicit_field() + .description(("Texture coordinates from 0 to 1")); + b.add_input(N_("Frame")).min(0).max(MAXFRAMEF); + b.add_output(N_("Color")).no_muted_links().dependent_field(); + b.add_output(N_("Alpha")).no_muted_links().dependent_field(); } static void geo_node_image_texture_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc index 604b181918d..b8df545d073 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -20,8 +20,8 @@ namespace blender::nodes { static void geo_node_input_curve_handles_declare(NodeDeclarationBuilder &b) { - b.add_output("Left").field_source(); - b.add_output("Right").field_source(); + b.add_output(N_("Left")).field_source(); + b.add_output(N_("Right")).field_source(); } static void geo_node_input_curve_handles_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc index 5a24b7f3f07..f32db3842db 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_curve_tilt_declare(NodeDeclarationBuilder &b) { - b.add_output("Tilt").field_source(); + b.add_output(N_("Tilt")).field_source(); } static void geo_node_input_curve_tilt_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc index d267325c26b..37d5bac0325 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_id_declare(NodeDeclarationBuilder &b) { - b.add_output("ID").field_source(); + b.add_output(N_("ID")).field_source(); } static void geo_node_input_id_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc index 7fcbaf429dd..6200ac5e7a8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_index_declare(NodeDeclarationBuilder &b) { - b.add_output("Index").field_source(); + b.add_output(N_("Index")).field_source(); } static void geo_node_input_index_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc index 8e805bd1359..fc41188dee5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc @@ -23,7 +23,7 @@ namespace blender::nodes { static void geo_node_input_material_declare(NodeDeclarationBuilder &b) { - b.add_output("Material"); + b.add_output(N_("Material")); } static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc index 702c83daea0..5d5d9e40032 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_material_index_declare(NodeDeclarationBuilder &b) { - b.add_output("Material Index").field_source(); + b.add_output(N_("Material Index")).field_source(); } static void geo_node_input_material_index_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc index 3387f8d6c6e..92b89313d23 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -28,7 +28,7 @@ namespace blender::nodes { static void geo_node_input_normal_declare(NodeDeclarationBuilder &b) { - b.add_output("Normal").field_source(); + b.add_output(N_("Normal")).field_source(); } static GVArrayPtr mesh_face_normals(const Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc index 44874259e20..a8477d4bc4f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_position_declare(NodeDeclarationBuilder &b) { - b.add_output("Position").field_source(); + b.add_output(N_("Position")).field_source(); } static void geo_node_input_position_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc index 586005511ad..6d2c4c38cbe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_radius_declare(NodeDeclarationBuilder &b) { - b.add_output("Radius").default_value(1.0f).min(0.0f).field_source(); + b.add_output(N_("Radius")).default_value(1.0f).min(0.0f).field_source(); } static void geo_node_input_radius_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc index de520787e78..dcd14b1c054 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_shade_smooth_declare(NodeDeclarationBuilder &b) { - b.add_output("Smooth").field_source(); + b.add_output(N_("Smooth")).field_source(); } static void geo_node_input_shade_smooth_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc index 44a1bb62de8..a8ee6dd8b12 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_spline_cyclic_declare(NodeDeclarationBuilder &b) { - b.add_output("Cyclic").field_source(); + b.add_output(N_("Cyclic")).field_source(); } static void geo_node_input_spline_cyclic_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index ec502f7f1bc..895efa6f0ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -22,7 +22,7 @@ namespace blender::nodes { static void geo_node_input_spline_length_declare(NodeDeclarationBuilder &b) { - b.add_output("Length").field_source(); + b.add_output(N_("Length")).field_source(); } static const GVArray *construct_spline_length_gvarray(const CurveComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc index eab95ebc46e..75fb8a13d38 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_spline_resolution_declare(NodeDeclarationBuilder &b) { - b.add_output("Resolution").field_source(); + b.add_output(N_("Resolution")).field_source(); } static void geo_node_input_spline_resolution_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index f746edbbca2..6b1736fe2ac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b) { - b.add_output("Tangent").field_source(); + b.add_output(N_("Tangent")).field_source(); } static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan tangents) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 1e94a4a6a50..aff29d973d4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -28,28 +28,29 @@ namespace blender::nodes { static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b) { - b.add_input("Points").description("Points to instance on"); - b.add_input("Selection").default_value(true).supports_field().hide_value(); - b.add_input("Instance").description("Geometry that is instanced on the points"); - b.add_input("Pick Instance") + b.add_input(N_("Points")).description(N_("Points to instance on")); + b.add_input(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input(N_("Instance")) + .description(N_("Geometry that is instanced on the points")); + b.add_input(N_("Pick Instance")) .supports_field() .description("Place different instances on different points"); - b.add_input("Instance Index") + b.add_input(N_("Instance Index")) .implicit_field() - .description( + .description(N_( "Index of the instance that used for each point. This is only used when Pick Instances " - "is on. By default the point index is used"); - b.add_input("Rotation") + "is on. By default the point index is used")); + b.add_input(N_("Rotation")) .subtype(PROP_EULER) .supports_field() - .description("Rotation of the instances"); - b.add_input("Scale") + .description(N_("Rotation of the instances")); + b.add_input(N_("Scale")) .default_value({1.0f, 1.0f, 1.0f}) .subtype(PROP_XYZ) .supports_field() - .description("Scale of the instances"); + .description(N_("Scale of the instances")); - b.add_output("Instances"); + b.add_output(N_("Instances")); } static void add_instances_from_component(InstancesComponent &dst_component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index 618d85d9c2f..c3955426e69 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -23,15 +23,15 @@ namespace blender::nodes { static void geo_node_instances_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input("Instances").only_instances(); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Position").implicit_field(); - b.add_input("Radius") + b.add_input(N_("Instances")).only_instances(); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Position")).implicit_field(); + b.add_input(N_("Radius")) .default_value(0.05f) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output("Points"); + b.add_output(N_("Points")); } template diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc index f8a1c764f61..8e0e98f7bd5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc @@ -22,7 +22,7 @@ namespace blender::nodes { static void geo_node_is_viewport_declare(NodeDeclarationBuilder &b) { - b.add_output("Is Viewport"); + b.add_output(N_("Is Viewport")); } static void geo_node_is_viewport_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index bbba8635b88..cd385f364e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -33,8 +33,8 @@ namespace blender::nodes { static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").multi_input(); - b.add_output("Geometry"); + b.add_input(N_("Geometry")).multi_input(); + b.add_output(N_("Geometry")); } static Mesh *join_mesh_topology_and_builtin_attributes(Span src_components) diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc index 7181b438282..e4a62bd5267 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_material_replace_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Old"); - b.add_input("New"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Old")); + b.add_input(N_("New")); + b.add_output(N_("Geometry")); } static void geo_node_material_replace_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index 7dffc7793e5..06c770820ee 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -30,8 +30,8 @@ namespace blender::nodes { static void geo_node_material_selection_declare(NodeDeclarationBuilder &b) { - b.add_input("Material").hide_label(true); - b.add_output("Selection").field_source(); + b.add_input(N_("Material")).hide_label(true); + b.add_output(N_("Selection")).field_source(); } static void select_mesh_by_material(const Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 2374d28523c..685a8faff5c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void geo_node_mesh_primitive_circle_declare(NodeDeclarationBuilder &b) { - b.add_input("Vertices").default_value(32).min(3); - b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Mesh"); + b.add_input(N_("Vertices")).default_value(32).min(3); + b.add_input(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output(N_("Mesh")); } static void geo_node_mesh_primitive_circle_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index bac859c6461..206d48d40c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -29,13 +29,16 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b) { - b.add_input("Vertices").default_value(32).min(3).max(512); - b.add_input("Side Segments").default_value(1).min(1).max(512); - b.add_input("Fill Segments").default_value(1).min(1).max(512); - b.add_input("Radius Top").min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Mesh"); + b.add_input(N_("Vertices")).default_value(32).min(3).max(512); + b.add_input(N_("Side Segments")).default_value(1).min(1).max(512); + b.add_input(N_("Fill Segments")).default_value(1).min(1).max(512); + b.add_input(N_("Radius Top")).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Radius Bottom")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Depth")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output(N_("Mesh")); } static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index f450e310384..3a211993bdc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -26,11 +26,14 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b) { - b.add_input("Size").default_value(float3(1)).min(0.0f).subtype(PROP_TRANSLATION); - b.add_input("Vertices X").default_value(2).min(2).max(1000); - b.add_input("Vertices Y").default_value(2).min(2).max(1000); - b.add_input("Vertices Z").default_value(2).min(2).max(1000); - b.add_output("Mesh"); + b.add_input(N_("Size")) + .default_value(float3(1)) + .min(0.0f) + .subtype(PROP_TRANSLATION); + b.add_input(N_("Vertices X")).default_value(2).min(2).max(1000); + b.add_input(N_("Vertices Y")).default_value(2).min(2).max(1000); + b.add_input(N_("Vertices Z")).default_value(2).min(2).max(1000); + b.add_output(N_("Mesh")); } struct CuboidConfig { diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index a9c8592d8b6..3bcf42b40b1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -29,32 +29,32 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) { - b.add_input("Vertices") + b.add_input(N_("Vertices")) .default_value(32) .min(3) .max(512) - .description("The number of vertices around the circumference"); - b.add_input("Side Segments") + .description(N_("The number of vertices around the circumference")); + b.add_input(N_("Side Segments")) .default_value(1) .min(1) .max(512) - .description("The number of segments along the side"); - b.add_input("Fill Segments") + .description(N_("The number of segments along the side")); + b.add_input(N_("Fill Segments")) .default_value(1) .min(1) .max(512) - .description("The number of concentric segments of the fill"); - b.add_input("Radius") + .description(N_("The number of concentric segments of the fill")); + b.add_input(N_("Radius")) .default_value(1.0f) .min(0.0f) .subtype(PROP_DISTANCE) - .description("The radius of the cylinder"); - b.add_input("Depth") + .description(N_("The radius of the cylinder")); + b.add_input(N_("Depth")) .default_value(2.0f) .min(0.0f) .subtype(PROP_DISTANCE) - .description("The height of the cylinder on the Z axis"); - b.add_output("Mesh"); + .description(N_("The height of the cylinder on the Z axis")); + b.add_output(N_("Mesh")); } static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 056b88c0cc8..c4e476981c1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -29,11 +29,11 @@ namespace blender::nodes { static void geo_node_mesh_primitive_grid_declare(NodeDeclarationBuilder &b) { - b.add_input("Size X").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Size Y").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Vertices X").default_value(3).min(2).max(1000); - b.add_input("Vertices Y").default_value(3).min(2).max(1000); - b.add_output("Mesh"); + b.add_input(N_("Size X")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Size Y")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Vertices X")).default_value(3).min(2).max(1000); + b.add_input(N_("Vertices Y")).default_value(3).min(2).max(1000); + b.add_output(N_("Mesh")); } static void calculate_uvs( diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index 08da2f458d4..da3dfef3aea 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -28,9 +28,9 @@ namespace blender::nodes { static void geo_node_mesh_primitive_ico_sphere_declare(NodeDeclarationBuilder &b) { - b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Subdivisions").default_value(1).min(1).max(7); - b.add_output("Mesh"); + b.add_input(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Subdivisions")).default_value(1).min(1).max(7); + b.add_output(N_("Mesh")); } static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 9bf2df4aa4d..6515afe5966 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -29,11 +29,13 @@ namespace blender::nodes { static void geo_node_mesh_primitive_line_declare(NodeDeclarationBuilder &b) { - b.add_input("Count").default_value(10).min(1).max(10000); - b.add_input("Resolution").default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE); - b.add_input("Start Location").subtype(PROP_TRANSLATION); - b.add_input("Offset").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_output("Mesh"); + b.add_input(N_("Count")).default_value(10).min(1).max(10000); + b.add_input(N_("Resolution")).default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE); + b.add_input(N_("Start Location")).subtype(PROP_TRANSLATION); + b.add_input(N_("Offset")) + .default_value({0.0f, 0.0f, 1.0f}) + .subtype(PROP_TRANSLATION); + b.add_output(N_("Mesh")); } static void geo_node_mesh_primitive_line_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 400930d0394..54a762fc15d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -29,10 +29,10 @@ namespace blender::nodes { static void geo_node_mesh_primitive_uv_shpere_declare(NodeDeclarationBuilder &b) { - b.add_input("Segments").default_value(32).min(3).max(1024); - b.add_input("Rings").default_value(16).min(2).max(1024); - b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Mesh"); + b.add_input(N_("Segments")).default_value(32).min(3).max(1024); + b.add_input(N_("Rings")).default_value(16).min(2).max(1024); + b.add_input(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output(N_("Mesh")); } static int sphere_vert_total(const int segments, const int rings) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index 2a243d6b8e3..d99c0c851a8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Level").default_value(1).min(0).max(6); - b.add_output("Mesh"); + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Level")).default_value(1).min(0).max(6); + b.add_output(N_("Mesh")); } static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 761581ca5d6..11865c635b8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -22,9 +22,9 @@ namespace blender::nodes { static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_output("Curve"); + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output(N_("Curve")); } static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 0a4c0638e27..92911e89f59 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -30,15 +30,15 @@ namespace blender::nodes { static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Selection").default_value(true).supports_field().hide_value(); - b.add_input("Position").implicit_field(); - b.add_input("Radius") + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input(N_("Position")).implicit_field(); + b.add_input(N_("Radius")) .default_value(0.05f) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output("Points"); + b.add_output(N_("Points")); } static void geo_node_mesh_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index e61709ed86a..3ba32c4b674 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -25,15 +25,15 @@ namespace blender::nodes { static void geo_node_object_info_declare(NodeDeclarationBuilder &b) { - b.add_input("Object").hide_label(); - b.add_input("As Instance") + b.add_input(N_("Object")).hide_label(); + b.add_input(N_("As Instance")) .description( - "Output the entire object as single instance. " - "This allows instancing non-geometry object types"); - b.add_output("Location"); - b.add_output("Rotation"); - b.add_output("Scale"); - b.add_output("Geometry"); + N_("Output the entire object as single instance. " + "This allows instancing non-geometry object types")); + b.add_output(N_("Location")); + b.add_output(N_("Rotation")); + b.add_output(N_("Scale")); + b.add_output(N_("Geometry")); } static void geo_node_object_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index d16af8481f0..3e0096824d3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b) { - b.add_input("Points").supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); - b.add_input("Selection").default_value(true).supports_field().hide_value(); - b.add_output("Mesh"); + b.add_input(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_output(N_("Mesh")); } template diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 6f68be439b3..fcd82170a8a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -32,16 +32,16 @@ namespace blender::nodes { static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) { - b.add_input("Points").supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); - b.add_input("Density").default_value(1.0f).min(0.0f); - b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input("Radius") + b.add_input(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input(N_("Density")).default_value(1.0f).min(0.0f); + b.add_input(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input(N_("Radius")) .default_value(0.5f) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output("Volume"); + b.add_output(N_("Volume")); } static void geo_node_points_to_volume_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index f3840df52aa..c05476b982b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -31,11 +31,12 @@ namespace blender::nodes { static void geo_node_proximity_declare(NodeDeclarationBuilder &b) { - b.add_input("Target").only_realized_data().supported_type( - {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); - b.add_input("Source Position").implicit_field(); - b.add_output("Position").dependent_field(); - b.add_output("Distance").dependent_field(); + b.add_input(N_("Target")) + .only_realized_data() + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); + b.add_input(N_("Source Position")).implicit_field(); + b.add_output(N_("Position")).dependent_field(); + b.add_output(N_("Distance")).dependent_field(); } static void geo_node_proximity_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 7040b47db1c..34946b1115c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -31,34 +31,36 @@ namespace blender::nodes { static void geo_node_raycast_declare(NodeDeclarationBuilder &b) { - b.add_input("Target Geometry") + b.add_input(N_("Target Geometry")) .only_realized_data() .supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Attribute").hide_value().supports_field(); - b.add_input("Attribute", "Attribute_001").hide_value().supports_field(); - b.add_input("Attribute", "Attribute_002").hide_value().supports_field(); - b.add_input("Attribute", "Attribute_003").hide_value().supports_field(); - b.add_input("Attribute", "Attribute_004").hide_value().supports_field(); + b.add_input(N_("Attribute")).hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_002").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_003").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_004").hide_value().supports_field(); - b.add_input("Source Position").implicit_field(); - b.add_input("Ray Direction").default_value({0.0f, 0.0f, -1.0f}).supports_field(); - b.add_input("Ray Length") + b.add_input(N_("Source Position")).implicit_field(); + b.add_input(N_("Ray Direction")) + .default_value({0.0f, 0.0f, -1.0f}) + .supports_field(); + b.add_input(N_("Ray Length")) .default_value(100.0f) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output("Is Hit").dependent_field(); - b.add_output("Hit Position").dependent_field(); - b.add_output("Hit Normal").dependent_field(); - b.add_output("Hit Distance").dependent_field(); + b.add_output(N_("Is Hit")).dependent_field(); + b.add_output(N_("Hit Position")).dependent_field(); + b.add_output(N_("Hit Normal")).dependent_field(); + b.add_output(N_("Hit Distance")).dependent_field(); - b.add_output("Attribute").dependent_field({1, 2, 3, 4, 5, 6}); - b.add_output("Attribute", "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6}); - b.add_output("Attribute", "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6}); - b.add_output("Attribute", "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6}); - b.add_output("Attribute", "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output(N_("Attribute")).dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output(N_("Attribute"), "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output(N_("Attribute"), "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output(N_("Attribute"), "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output(N_("Attribute"), "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6}); } static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc index 3be79d5ba3b..6c51c1f738f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc @@ -23,8 +23,8 @@ namespace blender::nodes { static void geo_node_realize_instances_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_output(N_("Geometry")); } static void geo_node_realize_instances_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 69d96e0e84e..26814e27d33 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -22,12 +22,12 @@ namespace blender::nodes { static void geo_node_rotate_instances_declare(NodeDeclarationBuilder &b) { - b.add_input("Instances").only_instances(); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Rotation").subtype(PROP_EULER).supports_field(); - b.add_input("Pivot Point").subtype(PROP_TRANSLATION).supports_field(); - b.add_input("Local Space").default_value(true).supports_field(); - b.add_output("Instances"); + b.add_input(N_("Instances")).only_instances(); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Rotation")).subtype(PROP_EULER).supports_field(); + b.add_input(N_("Pivot Point")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input(N_("Local Space")).default_value(true).supports_field(); + b.add_output(N_("Instances")); }; static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index eeaefcdc26e..ea2b458410e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -22,12 +22,15 @@ namespace blender::nodes { static void geo_node_scale_instances_declare(NodeDeclarationBuilder &b) { - b.add_input("Instances").only_instances(); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Scale").subtype(PROP_XYZ).default_value({1, 1, 1}).supports_field(); - b.add_input("Center").subtype(PROP_TRANSLATION).supports_field(); - b.add_input("Local Space").default_value(true).supports_field(); - b.add_output("Instances"); + b.add_input(N_("Instances")).only_instances(); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Scale")) + .subtype(PROP_XYZ) + .default_value({1, 1, 1}) + .supports_field(); + b.add_input(N_("Center")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input(N_("Local Space")).default_value(true).supports_field(); + b.add_output(N_("Instances")); }; static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc index dafd10cee2d..a16fb712b13 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc @@ -20,12 +20,12 @@ namespace blender::nodes { static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_output("Mesh"); - b.add_output("Point Cloud"); - b.add_output("Curve"); - b.add_output("Volume"); - b.add_output("Instances"); + b.add_input(N_("Geometry")); + b.add_output(N_("Mesh")); + b.add_output(N_("Point Cloud")); + b.add_output(N_("Curve")); + b.add_output(N_("Volume")); + b.add_output(N_("Instances")); } static void geo_node_separate_components_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc index 970d49e0626..28e214c0ccc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc @@ -23,16 +23,16 @@ namespace blender::nodes { static void geo_node_separate_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Selection") + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")) .default_value(true) .hide_value() .supports_field() - .description("The parts of the geometry that go into the first output"); - b.add_output("Selection") - .description("The parts of the geometry in the selection"); - b.add_output("Inverted") - .description("The parts of the geometry not in the selection"); + .description(N_("The parts of the geometry that go into the first output")); + b.add_output(N_("Selection")) + .description(N_("The parts of the geometry in the selection")); + b.add_output(N_("Inverted")) + .description(N_("The parts of the geometry not in the selection")); } static void geo_node_separate_geometry_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index a744754382a..b64aa266330 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -25,10 +25,10 @@ namespace blender::nodes { static void geo_node_set_curve_handles_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Position").implicit_field(); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Position")).implicit_field(); + b.add_output(N_("Curve")); } static void geo_node_set_curve_handles_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index bad81c54553..e47ce7dea30 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -20,11 +20,14 @@ namespace blender::nodes { static void geo_node_set_curve_radius_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Radius").min(0.0f).default_value(1.0f).supports_field().subtype( - PROP_DISTANCE); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Radius")) + .min(0.0f) + .default_value(1.0f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output(N_("Curve")); } static void set_radius_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index 670d396dbc6..dde6d0bab92 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_set_curve_tilt_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Tilt").subtype(PROP_ANGLE).supports_field(); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Tilt")).subtype(PROP_ANGLE).supports_field(); + b.add_output(N_("Curve")); } static void set_tilt_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc index 7a3b91a17b0..77d8e786501 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_set_id_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("ID").implicit_field(); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("ID")).implicit_field(); + b.add_output(N_("Geometry")); } static void set_id_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc index dab872a0440..040509ad6d1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_set_material_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Material").hide_label(); - b.add_output("Geometry"); + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Material")).hide_label(); + b.add_output(N_("Geometry")); } static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Material *material) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index d77f4f4fbaa..a8bb1bd8644 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_set_material_index_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Material Index").supports_field().min(0); - b.add_output("Geometry"); + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Material Index")).supports_field().min(0); + b.add_output(N_("Geometry")); } static void set_material_index_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index 791bc8cde6c..9ff299542b4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -20,11 +20,14 @@ namespace blender::nodes { static void geo_node_set_point_radius_declare(NodeDeclarationBuilder &b) { - b.add_input("Points").supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Radius").default_value(0.05f).min(0.0f).supports_field().subtype( - PROP_DISTANCE); - b.add_output("Points"); + b.add_input(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Radius")) + .default_value(0.05f) + .min(0.0f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output(N_("Points")); } static void set_radius_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index a20f14e1ee3..4e564386a28 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -22,11 +22,11 @@ namespace blender::nodes { static void geo_node_set_position_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Position").implicit_field(); - b.add_input("Offset").supports_field().subtype(PROP_TRANSLATION); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Position")).implicit_field(); + b.add_input(N_("Offset")).supports_field().subtype(PROP_TRANSLATION); + b.add_output(N_("Geometry")); } static void set_position_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index c88264ebe94..06e25c2ed55 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_set_shade_smooth_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Shade Smooth").supports_field().default_value(true); - b.add_output("Geometry"); + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Shade Smooth")).supports_field().default_value(true); + b.add_output(N_("Geometry")); } static void set_smooth_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index 03eb67ac021..ec751ae1d2b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_set_spline_cyclic_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Cyclic").supports_field(); - b.add_output("Geometry"); + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Cyclic")).supports_field(); + b.add_output(N_("Geometry")); } static void set_cyclic_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index c0608c36c2e..ccf419975ca 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -22,10 +22,10 @@ namespace blender::nodes { static void geo_node_set_spline_resolution_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Resolution").default_value(12).supports_field(); - b.add_output("Geometry"); + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Resolution")).default_value(12).supports_field(); + b.add_output(N_("Geometry")); } static void set_resolution_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc index 515f072e976..98d0aca084a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc @@ -20,9 +20,9 @@ namespace blender::nodes { static void geo_node_string_join_declare(NodeDeclarationBuilder &b) { - b.add_input("Delimiter"); - b.add_input("Strings").multi_input().hide_value(); - b.add_output("String"); + b.add_input(N_("Delimiter")); + b.add_input(N_("Strings")).multi_input().hide_value(); + b.add_output(N_("String")); }; static void geo_node_string_join_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 83526df3ac2..95e94a22d81 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -34,18 +34,30 @@ namespace blender::nodes { static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b) { - b.add_input("String"); - b.add_input("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Character Spacing") + b.add_input(N_("String")); + b.add_input(N_("Size")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Character Spacing")) .default_value(1.0f) .min(0.0f) .subtype(PROP_DISTANCE); - b.add_input("Word Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Line Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Text Box Width").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Text Box Height").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Curves"); - b.add_output("Remainder"); + b.add_input(N_("Word Spacing")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Line Spacing")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Text Box Width")) + .default_value(0.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Text Box Height")) + .default_value(0.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_output(N_("Curves")); + b.add_output(N_("Remainder")); } static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 57c0dfc5b09..2b3430a5ed0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -31,15 +31,15 @@ namespace blender::nodes { static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Level").default_value(1).min(0).max(6); - b.add_input("Crease") + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Level")).default_value(1).min(0).max(6); + b.add_input(N_("Crease")) .default_value(0.0f) .min(0.0f) .max(1.0f) .supports_field() .subtype(PROP_FACTOR); - b.add_output("Mesh"); + b.add_output(N_("Mesh")); } static void geo_node_subdivision_surface_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 28faf217f64..7e07a552650 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -30,51 +30,57 @@ namespace blender::nodes { static void geo_node_switch_declare(NodeDeclarationBuilder &b) { - b.add_input("Switch").default_value(false).supports_field(); - b.add_input("Switch", "Switch_001").default_value(false); + b.add_input(N_("Switch")).default_value(false).supports_field(); + b.add_input(N_("Switch"), "Switch_001").default_value(false); - b.add_input("False").supports_field(); - b.add_input("True").supports_field(); - b.add_input("False", "False_001").min(-100000).max(100000).supports_field(); - b.add_input("True", "True_001").min(-100000).max(100000).supports_field(); - b.add_input("False", "False_002").default_value(false).hide_value().supports_field(); - b.add_input("True", "True_002").default_value(true).hide_value().supports_field(); - b.add_input("False", "False_003").supports_field(); - b.add_input("True", "True_003").supports_field(); - b.add_input("False", "False_004") + b.add_input(N_("False")).supports_field(); + b.add_input(N_("True")).supports_field(); + b.add_input(N_("False"), "False_001").min(-100000).max(100000).supports_field(); + b.add_input(N_("True"), "True_001").min(-100000).max(100000).supports_field(); + b.add_input(N_("False"), "False_002") + .default_value(false) + .hide_value() + .supports_field(); + b.add_input(N_("True"), "True_002") + .default_value(true) + .hide_value() + .supports_field(); + b.add_input(N_("False"), "False_003").supports_field(); + b.add_input(N_("True"), "True_003").supports_field(); + b.add_input(N_("False"), "False_004") .default_value({0.8f, 0.8f, 0.8f, 1.0f}) .supports_field(); - b.add_input("True", "True_004") + b.add_input(N_("True"), "True_004") .default_value({0.8f, 0.8f, 0.8f, 1.0f}) .supports_field(); - b.add_input("False", "False_005").supports_field(); - b.add_input("True", "True_005").supports_field(); + b.add_input(N_("False"), "False_005").supports_field(); + b.add_input(N_("True"), "True_005").supports_field(); - b.add_input("False", "False_006"); - b.add_input("True", "True_006"); - b.add_input("False", "False_007"); - b.add_input("True", "True_007"); - b.add_input("False", "False_008"); - b.add_input("True", "True_008"); - b.add_input("False", "False_009"); - b.add_input("True", "True_009"); - b.add_input("False", "False_010"); - b.add_input("True", "True_010"); - b.add_input("False", "False_011"); - b.add_input("True", "True_011"); + b.add_input(N_("False"), "False_006"); + b.add_input(N_("True"), "True_006"); + b.add_input(N_("False"), "False_007"); + b.add_input(N_("True"), "True_007"); + b.add_input(N_("False"), "False_008"); + b.add_input(N_("True"), "True_008"); + b.add_input(N_("False"), "False_009"); + b.add_input(N_("True"), "True_009"); + b.add_input(N_("False"), "False_010"); + b.add_input(N_("True"), "True_010"); + b.add_input(N_("False"), "False_011"); + b.add_input(N_("True"), "True_011"); - b.add_output("Output").dependent_field(); - b.add_output("Output", "Output_001").dependent_field(); - b.add_output("Output", "Output_002").dependent_field(); - b.add_output("Output", "Output_003").dependent_field(); - b.add_output("Output", "Output_004").dependent_field(); - b.add_output("Output", "Output_005").dependent_field(); - b.add_output("Output", "Output_006"); - b.add_output("Output", "Output_007"); - b.add_output("Output", "Output_008"); - b.add_output("Output", "Output_009"); - b.add_output("Output", "Output_010"); - b.add_output("Output", "Output_011"); + b.add_output(N_("Output")).dependent_field(); + b.add_output(N_("Output"), "Output_001").dependent_field(); + b.add_output(N_("Output"), "Output_002").dependent_field(); + b.add_output(N_("Output"), "Output_003").dependent_field(); + b.add_output(N_("Output"), "Output_004").dependent_field(); + b.add_output(N_("Output"), "Output_005").dependent_field(); + b.add_output(N_("Output"), "Output_006"); + b.add_output(N_("Output"), "Output_007"); + b.add_output(N_("Output"), "Output_008"); + b.add_output(N_("Output"), "Output_009"); + b.add_output(N_("Output"), "Output_010"); + b.add_output(N_("Output"), "Output_011"); } static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index d52f028c38c..a889678537f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -40,23 +40,24 @@ namespace blender::nodes { static void geo_node_transfer_attribute_declare(NodeDeclarationBuilder &b) { - b.add_input("Target").only_realized_data().supported_type( - {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); + b.add_input(N_("Target")) + .only_realized_data() + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); - b.add_input("Attribute").hide_value().supports_field(); - b.add_input("Attribute", "Attribute_001").hide_value().supports_field(); - b.add_input("Attribute", "Attribute_002").hide_value().supports_field(); - b.add_input("Attribute", "Attribute_003").hide_value().supports_field(); - b.add_input("Attribute", "Attribute_004").hide_value().supports_field(); + b.add_input(N_("Attribute")).hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_002").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_003").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_004").hide_value().supports_field(); - b.add_input("Source Position").implicit_field(); - b.add_input("Index").implicit_field(); + b.add_input(N_("Source Position")).implicit_field(); + b.add_input(N_("Index")).implicit_field(); - b.add_output("Attribute").dependent_field({6, 7}); - b.add_output("Attribute", "Attribute_001").dependent_field({6, 7}); - b.add_output("Attribute", "Attribute_002").dependent_field({6, 7}); - b.add_output("Attribute", "Attribute_003").dependent_field({6, 7}); - b.add_output("Attribute", "Attribute_004").dependent_field({6, 7}); + b.add_output(N_("Attribute")).dependent_field({6, 7}); + b.add_output(N_("Attribute"), "Attribute_001").dependent_field({6, 7}); + b.add_output(N_("Attribute"), "Attribute_002").dependent_field({6, 7}); + b.add_output(N_("Attribute"), "Attribute_003").dependent_field({6, 7}); + b.add_output(N_("Attribute"), "Attribute_004").dependent_field({6, 7}); } static void geo_node_transfer_attribute_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 005714a9580..2c55a255b5d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -37,11 +37,11 @@ namespace blender::nodes { static void geo_node_transform_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Translation").subtype(PROP_TRANSLATION); - b.add_input("Rotation").subtype(PROP_EULER); - b.add_input("Scale").default_value({1, 1, 1}).subtype(PROP_XYZ); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Translation")).subtype(PROP_TRANSLATION); + b.add_input(N_("Rotation")).subtype(PROP_EULER); + b.add_input(N_("Scale")).default_value({1, 1, 1}).subtype(PROP_XYZ); + b.add_output(N_("Geometry")); } static bool use_translate(const float3 rotation, const float3 scale) diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index d4aa7a327da..fa05d858a07 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -22,11 +22,11 @@ namespace blender::nodes { static void geo_node_translate_instances_declare(NodeDeclarationBuilder &b) { - b.add_input("Instances").only_instances(); - b.add_input("Selection").default_value(true).hide_value().supports_field(); - b.add_input("Translation").subtype(PROP_TRANSLATION).supports_field(); - b.add_input("Local Space").default_value(true).supports_field(); - b.add_output("Instances"); + b.add_input(N_("Instances")).only_instances(); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Translation")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input(N_("Local Space")).default_value(true).supports_field(); + b.add_output(N_("Instances")); }; static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 1b2201c791a..c869846e1f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -31,9 +31,9 @@ namespace blender::nodes { static void geo_node_triangulate_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); - b.add_input("Minimum Vertices").default_value(4).min(4).max(10000); - b.add_output("Mesh"); + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Minimum Vertices")).default_value(4).min(4).max(10000); + b.add_output(N_("Mesh")); } static void geo_node_triangulate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index 920a6e1ed5d..194d1a751ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -22,12 +22,12 @@ namespace blender::nodes { static void geo_node_viewer_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Value").supports_field().hide_value(); - b.add_input("Value", "Value_001").supports_field().hide_value(); - b.add_input("Value", "Value_002").supports_field().hide_value(); - b.add_input("Value", "Value_003").supports_field().hide_value(); - b.add_input("Value", "Value_004").supports_field().hide_value(); + b.add_input(N_("Geometry")); + b.add_input(N_("Value")).supports_field().hide_value(); + b.add_input(N_("Value"), "Value_001").supports_field().hide_value(); + b.add_input(N_("Value"), "Value_002").supports_field().hide_value(); + b.add_input(N_("Value"), "Value_003").supports_field().hide_value(); + b.add_input(N_("Value"), "Value_004").supports_field().hide_value(); } static void geo_node_viewer_init(bNodeTree *UNUSED(tree), bNode *node) diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index a8c92eb0f07..416d502dc59 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -39,12 +39,12 @@ namespace blender::nodes { static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input("Volume").supported_type(GEO_COMPONENT_TYPE_VOLUME); - b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input("Threshold").default_value(0.1f).min(0.0f); - b.add_input("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output("Mesh"); + b.add_input(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME); + b.add_input(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input(N_("Threshold")).default_value(0.1f).min(0.0f); + b.add_input(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output(N_("Mesh")); } static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index e8d4239937f..57a992a4275 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void sh_node_clamp_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Value").min(0.0f).max(1.0f).default_value(1.0f); - b.add_input("Min").default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_output("Result"); + b.add_input(N_("Value")).min(0.0f).max(1.0f).default_value(1.0f); + b.add_input(N_("Min")).default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_output(N_("Result")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index 875e6fa0c35..f8f0ee97eae 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -28,9 +28,9 @@ namespace blender::nodes { static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); - b.add_input("Vector").min(-1.0f).max(1.0f); - b.add_output("Vector"); + b.add_input(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Vector")).min(-1.0f).max(1.0f); + b.add_output(N_("Vector")); }; } // namespace blender::nodes @@ -175,9 +175,9 @@ namespace blender::nodes { static void sh_node_curve_rgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); - b.add_input("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output("Color"); + b.add_input(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Color")); }; } // namespace blender::nodes @@ -352,9 +352,13 @@ namespace blender::nodes { static void sh_node_curve_float_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Factor").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); - b.add_input("Value").default_value(1.0f); - b.add_output("Value"); + b.add_input(N_("Factor")) + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR); + b.add_input(N_("Value")).default_value(1.0f); + b.add_output(N_("Value")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index 5ea194ddc83..c866a154e8c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -30,13 +30,13 @@ namespace blender::nodes { static void sh_node_map_range_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Value").min(-10000.0f).max(10000.0f).default_value(1.0f); - b.add_input("From Min").min(-10000.0f).max(10000.0f); - b.add_input("From Max").min(-10000.0f).max(10000.0f).default_value(1.0f); - b.add_input("To Min").min(-10000.0f).max(10000.0f); - b.add_input("To Max").min(-10000.0f).max(10000.0f).default_value(1.0f); - b.add_input("Steps").min(-10000.0f).max(10000.0f).default_value(4.0f); - b.add_output("Result"); + b.add_input(N_("Value")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input(N_("From Min")).min(-10000.0f).max(10000.0f); + b.add_input(N_("From Max")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input(N_("To Min")).min(-10000.0f).max(10000.0f); + b.add_input(N_("To Max")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input(N_("Steps")).min(-10000.0f).max(10000.0f).default_value(4.0f); + b.add_output(N_("Result")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 96d1be49c04..284a5f1189f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -32,10 +32,16 @@ namespace blender::nodes { static void sh_node_math_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Value").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_input("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_input("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_output("Value"); + b.add_input(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_input(N_("Value"), "Value_001") + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f); + b.add_input(N_("Value"), "Value_002") + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f); + b.add_output(N_("Value")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index d4d02e80ada..06fafff578e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void sh_node_mix_rgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Color1").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input("Color2").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output("Color"); + b.add_input(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Color1")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input(N_("Color2")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output(N_("Color")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index 24c5dcf7ba3..08a9e01786e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void sh_node_seprgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_output("R"); - b.add_output("G"); - b.add_output("B"); + b.add_input(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output(N_("R")); + b.add_output(N_("G")); + b.add_output(N_("B")); }; } // namespace blender::nodes @@ -121,10 +121,10 @@ namespace blender::nodes { static void sh_node_combrgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("R").min(0.0f).max(1.0f); - b.add_input("G").min(0.0f).max(1.0f); - b.add_input("B").min(0.0f).max(1.0f); - b.add_output("Image"); + b.add_input(N_("R")).min(0.0f).max(1.0f); + b.add_input(N_("G")).min(0.0f).max(1.0f); + b.add_input(N_("B")).min(0.0f).max(1.0f); + b.add_output(N_("Image")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index 8ca8fc19521..1bbfa629462 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").min(-10000.0f).max(10000.0f); - b.add_output("X"); - b.add_output("Y"); - b.add_output("Z"); + b.add_input(N_("Vector")).min(-10000.0f).max(10000.0f); + b.add_output(N_("X")); + b.add_output(N_("Y")); + b.add_output(N_("Z")); }; } // namespace blender::nodes @@ -105,10 +105,10 @@ namespace blender::nodes { static void sh_node_combxyz_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("X").min(-10000.0f).max(10000.0f); - b.add_input("Y").min(-10000.0f).max(10000.0f); - b.add_input("Z").min(-10000.0f).max(10000.0f); - b.add_output("Vector"); + b.add_input(N_("X")).min(-10000.0f).max(10000.0f); + b.add_input(N_("Y")).min(-10000.0f).max(10000.0f); + b.add_input(N_("Z")).min(-10000.0f).max(10000.0f); + b.add_output(N_("Vector")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc index ab5199c5fac..b840bd75e42 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc @@ -27,34 +27,34 @@ namespace blender::nodes { static void sh_node_tex_brick_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").min(-10000.0f).max(10000.0f).implicit_field(); - b.add_input("Color1").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input("Color2").default_value({0.2f, 0.2f, 0.2f, 1.0f}); - b.add_input("Mortar").default_value({0.0f, 0.0f, 0.0f, 1.0f}).no_muted_links(); - b.add_input("Scale") + b.add_input(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input(N_("Color1")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input(N_("Color2")).default_value({0.2f, 0.2f, 0.2f, 1.0f}); + b.add_input(N_("Mortar")).default_value({0.0f, 0.0f, 0.0f, 1.0f}).no_muted_links(); + b.add_input(N_("Scale")) .min(-1000.0f) .max(1000.0f) .default_value(5.0f) .no_muted_links(); - b.add_input("Mortar Size") + b.add_input(N_("Mortar Size")) .min(0.0f) .max(0.125f) .default_value(0.02f) .no_muted_links(); - b.add_input("Mortar Smooth").min(0.0f).max(1.0f).no_muted_links(); - b.add_input("Bias").min(-1.0f).max(1.0f).no_muted_links(); - b.add_input("Brick Width") + b.add_input(N_("Mortar Smooth")).min(0.0f).max(1.0f).no_muted_links(); + b.add_input(N_("Bias")).min(-1.0f).max(1.0f).no_muted_links(); + b.add_input(N_("Brick Width")) .min(0.01f) .max(100.0f) .default_value(0.5f) .no_muted_links(); - b.add_input("Row Height") + b.add_input(N_("Row Height")) .min(0.01f) .max(100.0f) .default_value(0.25f) .no_muted_links(); - b.add_output("Color"); - b.add_output("Fac"); + b.add_output(N_("Color")); + b.add_output(N_("Fac")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc index 7ba468a93e0..7c1223a6a32 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc @@ -24,16 +24,16 @@ namespace blender::nodes { static void sh_node_tex_checker_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").min(-10000.0f).max(10000.0f).implicit_field(); - b.add_input("Color1").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input("Color2").default_value({0.2f, 0.2f, 0.2f, 1.0f}); - b.add_input("Scale") + b.add_input(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input(N_("Color1")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input(N_("Color2")).default_value({0.2f, 0.2f, 0.2f, 1.0f}); + b.add_input(N_("Scale")) .min(-10000.0f) .max(10000.0f) .default_value(5.0f) .no_muted_links(); - b.add_output("Color"); - b.add_output("Fac"); + b.add_output(N_("Color")); + b.add_output(N_("Fac")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc index 4796af02361..33832c42b3c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc @@ -24,9 +24,9 @@ namespace blender::nodes { static void sh_node_tex_gradient_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").hide_value().implicit_field(); - b.add_output("Color").no_muted_links(); - b.add_output("Fac").no_muted_links(); + b.add_input(N_("Vector")).hide_value().implicit_field(); + b.add_output(N_("Color")).no_muted_links(); + b.add_output(N_("Fac")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc index df1051c07b4..f20fc85cbe0 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc @@ -24,9 +24,9 @@ namespace blender::nodes { static void sh_node_tex_image_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").implicit_field(); - b.add_output("Color").no_muted_links(); - b.add_output("Alpha").no_muted_links(); + b.add_input(N_("Vector")).implicit_field(); + b.add_output(N_("Color")).no_muted_links(); + b.add_output(N_("Alpha")).no_muted_links(); }; }; // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc index b6cdcf86528..62e68d53d03 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc @@ -24,11 +24,11 @@ namespace blender::nodes { static void sh_node_tex_magic_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").implicit_field(); - b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input("Distortion").min(-1000.0f).max(1000.0f).default_value(1.0f); - b.add_output("Color").no_muted_links(); - b.add_output("Fac").no_muted_links(); + b.add_input(N_("Vector")).implicit_field(); + b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(1.0f); + b.add_output(N_("Color")).no_muted_links(); + b.add_output(N_("Fac")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index 61c26d07e2f..3bf4e24ed53 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -26,15 +26,15 @@ namespace blender::nodes { static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").hide_value().implicit_field(); - b.add_input("W").min(-1000.0f).max(1000.0f); - b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input("Detail").min(0.0f).max(16.0f).default_value(2.0f); - b.add_input("Dimension").min(0.0f).max(1000.0f).default_value(2.0f); - b.add_input("Lacunarity").min(0.0f).max(1000.0f).default_value(2.0f); - b.add_input("Offset").min(-1000.0f).max(1000.0f); - b.add_input("Gain").min(0.0f).max(1000.0f).default_value(1.0f); - b.add_output("Fac").no_muted_links(); + b.add_input(N_("Vector")).hide_value().implicit_field(); + b.add_input(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f); + b.add_input(N_("Dimension")).min(0.0f).max(1000.0f).default_value(2.0f); + b.add_input(N_("Lacunarity")).min(0.0f).max(1000.0f).default_value(2.0f); + b.add_input(N_("Offset")).min(-1000.0f).max(1000.0f); + b.add_input(N_("Gain")).min(0.0f).max(1000.0f).default_value(1.0f); + b.add_output(N_("Fac")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index 6ffc8979815..72892c32795 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -26,18 +26,18 @@ namespace blender::nodes { static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").implicit_field(); - b.add_input("W").min(-1000.0f).max(1000.0f); - b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input("Detail").min(0.0f).max(16.0f).default_value(2.0f); - b.add_input("Roughness") + b.add_input(N_("Vector")).implicit_field(); + b.add_input(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f); + b.add_input(N_("Roughness")) .min(0.0f) .max(1.0f) .default_value(0.5f) .subtype(PROP_FACTOR); - b.add_input("Distortion").min(-1000.0f).max(1000.0f).default_value(0.0f); - b.add_output("Fac").no_muted_links(); - b.add_output("Color").no_muted_links(); + b.add_input(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_output(N_("Fac")).no_muted_links(); + b.add_output(N_("Color")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index 9fcc331e486..422268b98c3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -26,25 +26,25 @@ namespace blender::nodes { static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").hide_value().implicit_field(); - b.add_input("W").min(-1000.0f).max(1000.0f); - b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input("Smoothness") + b.add_input(N_("Vector")).hide_value().implicit_field(); + b.add_input(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input(N_("Smoothness")) .min(0.0f) .max(1.0f) .default_value(1.0f) .subtype(PROP_FACTOR); - b.add_input("Exponent").min(0.0f).max(32.0f).default_value(0.5f); - b.add_input("Randomness") + b.add_input(N_("Exponent")).min(0.0f).max(32.0f).default_value(0.5f); + b.add_input(N_("Randomness")) .min(0.0f) .max(1.0f) .default_value(1.0f) .subtype(PROP_FACTOR); - b.add_output("Distance").no_muted_links(); - b.add_output("Color").no_muted_links(); - b.add_output("Position").no_muted_links(); - b.add_output("W").no_muted_links(); - b.add_output("Radius").no_muted_links(); + b.add_output(N_("Distance")).no_muted_links(); + b.add_output(N_("Color")).no_muted_links(); + b.add_output(N_("Position")).no_muted_links(); + b.add_output(N_("W")).no_muted_links(); + b.add_output(N_("Radius")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc index 25e65e3d3f0..144aa1885bd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc @@ -26,19 +26,19 @@ namespace blender::nodes { static void sh_node_tex_wave_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").implicit_field(); - b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input("Distortion").min(-1000.0f).max(1000.0f).default_value(0.0f); - b.add_input("Detail").min(0.0f).max(16.0f).default_value(2.0f); - b.add_input("Detail Scale").min(-1000.0f).max(1000.0f).default_value(1.0f); - b.add_input("Detail Roughness") + b.add_input(N_("Vector")).implicit_field(); + b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_input(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f); + b.add_input(N_("Detail Scale")).min(-1000.0f).max(1000.0f).default_value(1.0f); + b.add_input(N_("Detail Roughness")) .min(0.0f) .max(1.0f) .default_value(0.5f) .subtype(PROP_FACTOR); - b.add_input("Phase Offset").min(-1000.0f).max(1000.0f).default_value(0.0f); - b.add_output("Color").no_muted_links(); - b.add_output("Fac").no_muted_links(); + b.add_input(N_("Phase Offset")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_output(N_("Color")).no_muted_links(); + b.add_output(N_("Fac")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index 445b201e419..43ee9400551 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -26,10 +26,10 @@ namespace blender::nodes { static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").min(-10000.0f).max(10000.0f).implicit_field(); - b.add_input("W").min(-10000.0f).max(10000.0f); - b.add_output("Value"); - b.add_output("Color"); + b.add_input(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input(N_("W")).min(-10000.0f).max(10000.0f); + b.add_output(N_("Value")); + b.add_output(N_("Color")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 2544ea1921c..e4f1b2c76f0 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -34,9 +34,9 @@ namespace blender::nodes { static void sh_node_valtorgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output("Color"); - b.add_output("Alpha"); + b.add_input(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output(N_("Color")); + b.add_output(N_("Alpha")); }; } // namespace blender::nodes @@ -192,8 +192,8 @@ namespace blender::nodes { static void sh_node_rgbtobw_declare(NodeDeclarationBuilder &b) { - b.add_input("Color").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output("Val"); + b.add_input(N_("Color")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output(N_("Val")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 1344ce5c5d9..b0f152d8526 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -27,7 +27,7 @@ namespace blender::nodes { static void sh_node_value_declare(NodeDeclarationBuilder &b) { - b.add_output("Value"); + b.add_output(N_("Value")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index f49ff06cef1..ca5aeea9a7d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -30,12 +30,12 @@ namespace blender::nodes { static void sh_node_vector_math_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").min(-10000.0f).max(10000.0f); - b.add_input("Vector", "Vector_001").min(-10000.0f).max(10000.0f); - b.add_input("Vector", "Vector_002").min(-10000.0f).max(10000.0f); - b.add_input("Scale").default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_output("Vector"); - b.add_output("Value"); + b.add_input(N_("Vector")).min(-10000.0f).max(10000.0f); + b.add_input(N_("Vector"), "Vector_001").min(-10000.0f).max(10000.0f); + b.add_input(N_("Vector"), "Vector_002").min(-10000.0f).max(10000.0f); + b.add_input(N_("Scale")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_output(N_("Vector")); + b.add_output(N_("Value")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index c9b26fa5199..1ab643bc3fa 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -28,12 +28,12 @@ namespace blender::nodes { static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").min(0.0f).max(1.0f).hide_value(); - b.add_input("Center"); - b.add_input("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); - b.add_input("Angle").subtype(PROP_ANGLE); - b.add_input("Rotation").subtype(PROP_EULER); - b.add_output("Vector"); + b.add_input(N_("Vector")).min(0.0f).max(1.0f).hide_value(); + b.add_input(N_("Center")); + b.add_input(N_("Axis")).min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); + b.add_input(N_("Angle")).subtype(PROP_ANGLE); + b.add_input(N_("Rotation")).subtype(PROP_EULER); + b.add_output(N_("Vector")); }; } // namespace blender::nodes From dcdbaf89bd114aaf066cec57bb619b828c159719 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 29 Oct 2021 16:45:08 +0200 Subject: [PATCH 1380/1500] Asset Browser: Correct name & tooltip for asset list refresh operator The name and tooltip were talking about file-lists, which exposes the fact that the Asset Browser uses the File Browser code in the UI, which we shouldn't do. This can confuse users. Instead have a dedicated operator for the Asset Browser with a proper name and tooltip. --- .../keyconfig/keymap_data/blender_default.py | 2 ++ .../keymap_data/industry_compatible_data.py | 4 +++ .../startup/bl_ui/space_filebrowser.py | 2 +- .../blender/editors/space_file/file_intern.h | 1 + source/blender/editors/space_file/file_ops.c | 32 +++++++++++++++++-- .../blender/editors/space_file/file_panels.c | 2 +- .../blender/editors/space_file/space_file.c | 1 + 7 files changed, 40 insertions(+), 4 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 2289e8200a6..9f921bd2b70 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2140,7 +2140,9 @@ def km_file_browser(params): ("file.parent", {"type": 'UP_ARROW', "value": 'PRESS', "alt": True}, None), ("file.previous", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True}, None), ("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True}, None), + # The two refresh operators have polls excluding each other (so only one is available depending on context). ("file.refresh", {"type": 'R', "value": 'PRESS'}, None), + ("file.asset_library_refresh", {"type": 'R', "value": 'PRESS'}, None), ("file.parent", {"type": 'P', "value": 'PRESS'}, None), ("file.previous", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), ("file.next", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, None), diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 8200aad1091..0ae64dbc62e 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1227,7 +1227,9 @@ def km_file_browser(params): ("file.previous", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True}, None), ("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True}, None), ("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True}, None), + # The two refresh operators have polls excluding each other (so only one is available depending on context). ("file.refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), + ("file.asset_library_refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ("file.previous", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), ("file.next", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, None), ("wm.context_toggle", {"type": 'H', "value": 'PRESS'}, @@ -1272,7 +1274,9 @@ def km_file_browser_main(params): items.extend([ ("file.mouse_execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), + # The two refresh operators have polls excluding each other (so only one is available depending on context). ("file.refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), + ("file.asset_library_refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, {"properties": [("open", False), ("deselect_all", True)]}), diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index a33e2665b58..05f505c518d 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -792,7 +792,7 @@ class ASSETBROWSER_MT_context_menu(AssetBrowserMenu, Menu): st = context.space_data params = st.params - layout.operator("file.refresh", text="Refresh") + layout.operator("file.asset_library_refresh") layout.separator() diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index f6b5f0f47cd..4be5d6d8008 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -79,6 +79,7 @@ void FILE_OT_directory_new(struct wmOperatorType *ot); void FILE_OT_previous(struct wmOperatorType *ot); void FILE_OT_next(struct wmOperatorType *ot); void FILE_OT_refresh(struct wmOperatorType *ot); +void FILE_OT_asset_library_refresh(struct wmOperatorType *ot); void FILE_OT_filenum(struct wmOperatorType *ot); void FILE_OT_delete(struct wmOperatorType *ot); void FILE_OT_rename(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index f647e1d4e4f..4eb10e65867 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1950,8 +1950,36 @@ void FILE_OT_refresh(struct wmOperatorType *ot) /* api callbacks */ ot->exec = file_refresh_exec; - /* Operator works for file or asset browsing */ - ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ + ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */ +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Refresh Asset Library Operator + * \{ */ + +static int file_asset_library_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) +{ + wmWindowManager *wm = CTX_wm_manager(C); + SpaceFile *sfile = CTX_wm_space_file(C); + + ED_fileselect_clear(wm, sfile); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); + + return OPERATOR_FINISHED; +} + +void FILE_OT_asset_library_refresh(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Refresh Asset Library"; + ot->description = "Reread assets and asset catalogs from the asset library on disk"; + ot->idname = "FILE_OT_asset_library_refresh"; + + /* api callbacks */ + ot->exec = file_asset_library_refresh_exec; + ot->poll = ED_operator_asset_browsing_active; } /** \} */ diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index 51d0581d6a4..0e468718a04 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -247,7 +247,7 @@ static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *pane uiItemR(row, ¶ms_ptr, "asset_library_ref", 0, "", ICON_NONE); if (params->asset_library_ref.type != ASSET_LIBRARY_LOCAL) { - uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_refresh"); + uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_asset_library_refresh"); } uiItemS(col); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index ef503708335..b115c63a569 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -688,6 +688,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_previous); WM_operatortype_append(FILE_OT_next); WM_operatortype_append(FILE_OT_refresh); + WM_operatortype_append(FILE_OT_asset_library_refresh); WM_operatortype_append(FILE_OT_bookmark_add); WM_operatortype_append(FILE_OT_bookmark_delete); WM_operatortype_append(FILE_OT_bookmark_cleanup); From b5f42029b887be8d475ef471c97d4a84768d1f09 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 29 Oct 2021 15:56:44 +0200 Subject: [PATCH 1381/1500] Fix T92592: Cycles stereo render not rendering right view --- intern/cycles/session/buffers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/session/buffers.cpp b/intern/cycles/session/buffers.cpp index 51d9c1e5d8f..5c9e097e5b1 100644 --- a/intern/cycles/session/buffers.cpp +++ b/intern/cycles/session/buffers.cpp @@ -257,7 +257,7 @@ bool BufferParams::modified(const BufferParams &other) const } if (layer != other.layer || view != other.view) { - return false; + return true; } if (exposure != other.exposure || From fcf1ba18f0c41187bc7e34e1cdba8d59970a632c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 29 Oct 2021 13:17:33 -0500 Subject: [PATCH 1382/1500] Fix: Incorrect component type warning in points to volume node This node doesn't only support point clouds, it supports any geometry component type with points, so meshes, curves, and point clouds. We could explicitly list them to add a warning for volumes, but that wouldn't really have any practical benefit, and isn't done elsewhere. --- .../blender/nodes/geometry/nodes/node_geo_points_to_volume.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index fcd82170a8a..312ea7df919 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -32,7 +32,7 @@ namespace blender::nodes { static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input(N_("Points")); b.add_input(N_("Density")).default_value(1.0f).min(0.0f); b.add_input(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); b.add_input(N_("Voxel Amount")).default_value(64.0f).min(0.0f); From 02a9377da0da185685896138316c3bdb0623e021 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Fri, 29 Oct 2021 17:15:22 -0700 Subject: [PATCH 1383/1500] UI: Default Fonts Folder for Mac and Linux Initial defaults for userdef->fontdir for Mac and Linux. See D12802 for more details. Differential Revision: https://developer.blender.org/D12802 Reviewed by Campbell Barton --- source/blender/blenkernel/intern/appdir.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index ce4ab8a4ba1..fb6656a4b1c 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -259,23 +259,24 @@ bool BKE_appdir_folder_caches(char *r_path, const size_t path_len) /** * Gets a good default directory for fonts. */ -bool BKE_appdir_font_folder_default( - /* This parameter can only be `const` on non-windows platforms. - * NOLINTNEXTLINE: readability-non-const-parameter. */ - char *dir) +bool BKE_appdir_font_folder_default(char *dir) { - bool success = false; #ifdef WIN32 wchar_t wpath[FILE_MAXDIR]; - success = SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0); - if (success) { + if (SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0)) { wcscat(wpath, L"\\"); BLI_strncpy_wchar_as_utf8(dir, wpath, FILE_MAXDIR); + return (BLI_exists(dir)); } + return false; +#elif defined(__APPLE__) + const char *home = BLI_getenv("HOME"); + BLI_snprintf(dir, FILE_MAXDIR, "%s/Library/Fonts/", home); + return (BLI_exists(dir)); +#else + BLI_strncpy(dir, "/usr/share/fonts/", FILE_MAXDIR); + return (BLI_exists(dir)); #endif - /* TODO: Values for other platforms. */ - UNUSED_VARS(dir); - return success; } /** \} */ From 945a99386b8c86ee45bac83329c98c9034a23ff7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 30 Oct 2021 16:44:17 +0200 Subject: [PATCH 1384/1500] Fix T92256: rotate Instances node does not take scaling into account correctly Differential Revision: https://developer.blender.org/D13031 --- .../nodes/node_geo_rotate_instances.cc | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 26814e27d33..abf44b1aaf8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -55,22 +55,43 @@ static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &inst for (const int i_selection : range) { const int i = selection[i_selection]; const float3 pivot = pivots[i]; + const float3 euler = rotations[i]; float4x4 &instance_transform = instance_transforms[i]; - const float4x4 rotation_matrix = float4x4::from_loc_eul_scale( - {0, 0, 0}, rotations[i], {1, 1, 1}); + + float4x4 rotation_matrix; + float3 used_pivot; if (local_spaces[i]) { - instance_transform *= float4x4::from_location(pivot); - instance_transform *= rotation_matrix; - instance_transform *= float4x4::from_location(-pivot); + /* Find rotation axis from the matrix. This should work even if the instance is skewed. */ + const float3 rotation_axis_x = instance_transform.values[0]; + const float3 rotation_axis_y = instance_transform.values[1]; + const float3 rotation_axis_z = instance_transform.values[2]; + + /* Create rotations around the individual axis. This could be optimized to skip some axis + * when the angle is zero. */ + float rotation_x[3][3], rotation_y[3][3], rotation_z[3][3]; + axis_angle_to_mat3(rotation_x, rotation_axis_x, euler.x); + axis_angle_to_mat3(rotation_y, rotation_axis_y, euler.y); + axis_angle_to_mat3(rotation_z, rotation_axis_z, euler.z); + + /* Combine the previously computed rotations into the final rotation matrix. */ + float rotation[3][3]; + mul_m3_series(rotation, rotation_z, rotation_y, rotation_x); + copy_m4_m3(rotation_matrix.values, rotation); + + /* Transform the passed in pivot into the local space of the instance. */ + used_pivot = instance_transform * pivot; } else { - const float4x4 orgiginal_transform = instance_transform; - instance_transform = float4x4::from_location(pivot); - instance_transform *= rotation_matrix; - instance_transform *= float4x4::from_location(-pivot); - instance_transform *= orgiginal_transform; + used_pivot = pivot; + eul_to_mat4(rotation_matrix.values, euler); } + /* Move the pivot to the origin so that we can rotate around it. */ + sub_v3_v3(instance_transform.values[3], used_pivot); + /* Perform the actual rotation. */ + mul_m4_m4_pre(instance_transform.values, rotation_matrix.values); + /* Undo the pivot shifting done before. */ + add_v3_v3(instance_transform.values[3], used_pivot); } }); } From 9cfffe84681bfa4dd43d0d2338feabf2ad9cfd29 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Sat, 30 Oct 2021 14:02:34 -0700 Subject: [PATCH 1385/1500] UI: Open File Browser with Thumbnails for Fonts When browsing to open a font file, open File Browser in Thumbnail View and sorting by name, instead of using the last-used states. See D13040 for more details. Differential Revision: https://developer.blender.org/D13040 Reviewed by Julian Eisel --- source/blender/editors/curve/editfont.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 1b44cf88db1..6f18798bd2a 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -2155,8 +2155,8 @@ void FONT_OT_open(wmOperatorType *ot) FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, - FILE_DEFAULTDISPLAY, - FILE_SORT_DEFAULT); + FILE_IMGDISPLAY, + FILE_SORT_ALPHA); } /** \} */ From ae9052a33e646376b199be44eac26ca5dbe30e87 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sat, 30 Oct 2021 17:26:18 -0500 Subject: [PATCH 1386/1500] Cleanup: Simplify logic for adding grid in points to volume node Instead of creating a separate grid first and then merging the points to volume grid, use the recently added `BKE_volume_grid_add_vdb` helper function for this purpose. --- .../nodes/geometry/nodes/node_geo_points_to_volume.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 312ea7df919..18d674a38a4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -222,16 +222,12 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); BKE_volume_init_grids(volume); - VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT); - openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast( - BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false)); - const float density = params.get_input("Density"); convert_to_grid_index_space(voxel_size, positions, radii); openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii, density); - /* This merge is cheap, because the #density_grid is empty. */ - density_grid->merge(*new_grid); - density_grid->transform().postScale(voxel_size); + new_grid->transform().postScale(voxel_size); + BKE_volume_grid_add_vdb(*volume, "density", std::move(new_grid)); + r_geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES}); r_geometry_set.replace_volume(volume); } From a06abbac92f2029269375ced69a2c4f75ecce5e1 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Sun, 31 Oct 2021 11:08:57 +0000 Subject: [PATCH 1387/1500] fix: pose slider consistent color MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the way the pose slider gets its color so it is consistent between editors Previously the highlight color would be different between the 3D viewport and the graph editor. Reviewed by: Sybren A. Stüvel, Pablo Vazquez Differential Revision: https://developer.blender.org/D11878 Ref: D11878 --- source/blender/editors/util/ed_draw.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c index 2412939e859..721e32a3051 100644 --- a/source/blender/editors/util/ed_draw.c +++ b/source/blender/editors/util/ed_draw.c @@ -223,7 +223,7 @@ static void draw_main_line(const rctf *main_line_rect, static void draw_backdrop(const int fontid, const rctf *main_line_rect, - const float color_bg[4], + const uint8_t color_bg[4], const short region_y_size, const float base_tick_height) { @@ -241,7 +241,7 @@ static void draw_backdrop(const int fontid, .ymin = pad[1], .ymax = region_y_size - pad[1], }; - UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg); + UI_draw_roundbox_3ub_alpha(&backdrop_rect, true, 4.0f, color_bg, color_bg[3]); } /** @@ -260,19 +260,19 @@ static void slider_draw(const struct bContext *UNUSED(C), ARegion *region, void uint8_t color_line[4]; uint8_t color_handle[4]; uint8_t color_overshoot[4]; - float color_bg[4]; + uint8_t color_bg[4]; /* Get theme colors. */ - UI_GetThemeColor4ubv(TH_TEXT, color_text); - UI_GetThemeColor4ubv(TH_TEXT, color_line); - UI_GetThemeColor4ubv(TH_TEXT, color_overshoot); - UI_GetThemeColor4ubv(TH_ACTIVE, color_handle); - UI_GetThemeColor3fv(TH_BACK, color_bg); + UI_GetThemeColor4ubv(TH_HEADER_TEXT_HI, color_handle); + UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_text); + UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_line); + UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_overshoot); + UI_GetThemeColor4ubv(TH_HEADER, color_bg); - color_bg[3] = 0.5f; - color_overshoot[0] = color_overshoot[0] * 0.7; - color_overshoot[1] = color_overshoot[1] * 0.7; - color_overshoot[2] = color_overshoot[2] * 0.7; + color_overshoot[0] = color_overshoot[0] * 0.8; + color_overshoot[1] = color_overshoot[1] * 0.8; + color_overshoot[2] = color_overshoot[2] * 0.8; + color_bg[3] = 160; /* Get the default font. */ const uiStyle *style = UI_style_get(); From 1b6daa871da9acc7c17aae9965633a4da604ba63 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Sun, 31 Oct 2021 11:19:40 +0000 Subject: [PATCH 1388/1500] Cleanup: Extract keyframe filter to constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An int flag is used to filter animation channels for operators to work on. The flag was duplicated multiple times. This patch removes the duplication by creating a constant Reviewed by: Sybren A. Stüvel Differential Revision: https://developer.blender.org/D12486 Ref: D12486 --- .../editors/space_graph/graph_slider_ops.c | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index f04336cab84..958745ff281 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -48,6 +48,11 @@ #include "graph_intern.h" +/* Used to obtain a list of animation channels for the operators to work on. */ +#define OPERATOR_DATA_FILTER \ + (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_SEL | \ + ANIMFILTER_NODUPLIS) + /* ******************** GRAPH SLIDER OPERATORS ************************* */ /* This file contains a collection of operators to modify keyframes in the graph editor. All * operators are modal and use a slider that allows the user to define a percentage to modify the @@ -59,12 +64,9 @@ static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float erro { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; - int filter; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); /* Loop through filtered data and clean curves. */ for (ale = anim_data.first; ale; ale = ale->next) { @@ -116,14 +118,11 @@ static void decimate_reset_bezts(tDecimateGraphOp *dgo) ListBase anim_data = {NULL, NULL}; LinkData *link_bezt; bAnimListElem *ale; - int filter; bAnimContext *ac = &dgo->ac; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); /* Loop through filtered data and reset bezts. */ for (ale = anim_data.first, link_bezt = dgo->bezt_arr_list.first; ale; ale = ale->next) { @@ -242,12 +241,8 @@ static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent bAnimContext *ac = &dgo->ac; bAnimListElem *ale; - int filter; - /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); /* Loop through filtered data and copy the curves. */ for (ale = anim_data.first; ale; ale = ale->next) { From b2e9f35c5eb21f63f5c20e06e910ef03965556ce Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Sun, 31 Oct 2021 11:28:10 +0000 Subject: [PATCH 1389/1500] Cleanup: Extract function to store bezt arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code to store an original bezt array previously lived in `graphkeys_decimate_invoke`. Since future graph slider operators will need this function as well, it has been extracted. No functional changes. Reviewed by: Sybren A. Stüvel Differential Revision: https://developer.blender.org/D12487 Ref: D12487 --- .../editors/space_graph/graph_slider_ops.c | 84 ++++++++++--------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 958745ff281..ab3c6a89730 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -58,6 +58,48 @@ * operators are modal and use a slider that allows the user to define a percentage to modify the * operator. */ +/* ******************** Utility Functions ************************* */ + +/* Construct a list with the original bezt arrays so we can restore them during modal operation. + * The data is stored on the struct that is passed.*/ +static void store_original_bezt_arrays(tDecimateGraphOp *dgo) +{ + ListBase anim_data = {NULL, NULL}; + bAnimContext *ac = &dgo->ac; + bAnimListElem *ale; + + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); + + /* Loop through filtered data and copy the curves. */ + for (ale = anim_data.first; ale; ale = ale->next) { + FCurve *fcu = (FCurve *)ale->key_data; + + if (fcu->bezt == NULL) { + /* This curve is baked, skip it. */ + continue; + } + + const int arr_size = sizeof(BezTriple) * fcu->totvert; + + tBeztCopyData *copy = MEM_mallocN(sizeof(tBeztCopyData), "bezts_copy"); + BezTriple *bezts_copy = MEM_mallocN(arr_size, "bezts_copy_array"); + + copy->tot_vert = fcu->totvert; + memcpy(bezts_copy, fcu->bezt, arr_size); + + copy->bezt = bezts_copy; + + LinkData *link = NULL; + + link = MEM_callocN(sizeof(LinkData), "Bezt Link"); + link->data = copy; + + BLI_addtail(&dgo->bezt_arr_list, link); + } + + ANIM_animdata_freelist(&anim_data); +} + /* ******************** Decimate Keyframes Operator ************************* */ static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float error_sq_max) @@ -228,52 +270,14 @@ static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent dgo->area = CTX_wm_area(C); dgo->region = CTX_wm_region(C); + store_original_bezt_arrays(dgo); + dgo->slider = ED_slider_create(C); ED_slider_init(dgo->slider, event); ED_slider_allow_overshoot_set(dgo->slider, false); decimate_draw_status(C, dgo); - /* Construct a list with the original bezt arrays so we can restore them during modal operation. - */ - { - ListBase anim_data = {NULL, NULL}; - bAnimContext *ac = &dgo->ac; - bAnimListElem *ale; - - /* Filter data. */ - ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); - - /* Loop through filtered data and copy the curves. */ - for (ale = anim_data.first; ale; ale = ale->next) { - FCurve *fcu = (FCurve *)ale->key_data; - - if (fcu->bezt == NULL) { - /* This curve is baked, skip it. */ - continue; - } - - const int arr_size = sizeof(BezTriple) * fcu->totvert; - - tBeztCopyData *copy = MEM_mallocN(sizeof(tBeztCopyData), "bezts_copy"); - BezTriple *bezts_copy = MEM_mallocN(arr_size, "bezts_copy_array"); - - copy->tot_vert = fcu->totvert; - memcpy(bezts_copy, fcu->bezt, arr_size); - - copy->bezt = bezts_copy; - - LinkData *link = NULL; - - link = MEM_callocN(sizeof(LinkData), "Bezt Link"); - link->data = copy; - - BLI_addtail(&dgo->bezt_arr_list, link); - } - - ANIM_animdata_freelist(&anim_data); - } - if (dgo->bezt_arr_list.first == NULL) { WM_report(RPT_WARNING, "Fcurve Decimate: Can't decimate baked channels. Unbake them and try again."); From c312c7196944b095fbd1b74c085a3af1e64409ce Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Sun, 31 Oct 2021 11:52:24 -0600 Subject: [PATCH 1390/1500] Fix: Build error on all platforms Types were used before being declared. --- .../editors/space_graph/graph_slider_ops.c | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index ab3c6a89730..22e03cfa46c 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -53,6 +53,36 @@ (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_SEL | \ ANIMFILTER_NODUPLIS) +/* ------------------- */ + +/* This data type is only used for modal operation. */ +typedef struct tDecimateGraphOp { + bAnimContext ac; + Scene *scene; + ScrArea *area; + ARegion *region; + + /** A 0-1 value for determining how much we should decimate. */ + PropertyRNA *percentage_prop; + + /** The original bezt curve data (used for restoring fcurves). */ + ListBase bezt_arr_list; + + struct tSlider *slider; + + NumInput num; +} tDecimateGraphOp; + +typedef struct tBeztCopyData { + int tot_vert; + BezTriple *bezt; +} tBeztCopyData; + +typedef enum tDecimModes { + DECIM_RATIO = 1, + DECIM_ERROR, +} tDecimModes; + /* ******************** GRAPH SLIDER OPERATORS ************************* */ /* This file contains a collection of operators to modify keyframes in the graph editor. All * operators are modal and use a slider that allows the user to define a percentage to modify the @@ -124,36 +154,6 @@ static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float erro ANIM_animdata_freelist(&anim_data); } -/* ------------------- */ - -/* This data type is only used for modal operation. */ -typedef struct tDecimateGraphOp { - bAnimContext ac; - Scene *scene; - ScrArea *area; - ARegion *region; - - /** A 0-1 value for determining how much we should decimate. */ - PropertyRNA *percentage_prop; - - /** The original bezt curve data (used for restoring fcurves). */ - ListBase bezt_arr_list; - - struct tSlider *slider; - - NumInput num; -} tDecimateGraphOp; - -typedef struct tBeztCopyData { - int tot_vert; - BezTriple *bezt; -} tBeztCopyData; - -typedef enum tDecimModes { - DECIM_RATIO = 1, - DECIM_ERROR, -} tDecimModes; - /* Overwrite the current bezts arrays with the original data. */ static void decimate_reset_bezts(tDecimateGraphOp *dgo) { From d4d38e8a34e59c8762034073b084ed3c5ee09af8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 1 Nov 2021 12:38:12 +1100 Subject: [PATCH 1391/1500] BLI_path_util: assert to ensure BLI_join_dirfile is used correctly --- source/blender/blenlib/intern/path_util.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 2403d34e5d7..182fe211110 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1730,6 +1730,9 @@ void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__re /** * Simple appending of filename to dir, does not check for valid path! * Puts result into `dst`, which may be same area as `dir`. + * + * \note Consider using #BLI_path_join for more general path joining + * that de-duplicates separators and can handle an arbitrary number of paths. */ void BLI_join_dirfile(char *__restrict dst, const size_t maxlen, @@ -1741,9 +1744,13 @@ void BLI_join_dirfile(char *__restrict dst, #endif size_t dirlen = BLI_strnlen(dir, maxlen); - /* args can't match */ + /* Arguments can't match. */ BLI_assert(!ELEM(dst, dir, file)); + /* Files starting with a separator cause a double-slash which could later be interpreted + * as a relative path where: `dir == "/"` and `file == "/file"` would result in "//file". */ + BLI_assert(file[0] != SEP); + if (dirlen == maxlen) { memcpy(dst, dir, dirlen); dst[dirlen - 1] = '\0'; From a0633e2484f21c9f80842c10b46b3af98b54d25a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 1 Nov 2021 13:09:02 +1100 Subject: [PATCH 1392/1500] Fix crash when "HOME" environment variable isn't defined Accessing the default directory in the file selector would crash if HOME was undefined. Add BKE_appdir_folder_default_or_root which never returns NULL. --- source/blender/blenkernel/BKE_appdir.h | 6 +++++- source/blender/blenkernel/intern/appdir.c | 20 ++++++++++++++++++++ source/blender/blenlib/BLI_winstuff.h | 2 +- source/blender/blenlib/intern/winstuff.c | 2 +- source/blender/editors/space_file/file_ops.c | 6 ++++-- source/blender/editors/space_file/fsmenu.c | 19 ++++++++++--------- 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h index 07132201e87..65485058dd7 100644 --- a/source/blender/blenkernel/BKE_appdir.h +++ b/source/blender/blenkernel/BKE_appdir.h @@ -21,6 +21,8 @@ #include +#include "BLI_compiler_attrs.h" + #ifdef __cplusplus extern "C" { #endif @@ -32,7 +34,9 @@ void BKE_appdir_exit(void); /* note on naming: typical _get() suffix is omitted here, * since its the main purpose of the API. */ -const char *BKE_appdir_folder_default(void); +const char *BKE_appdir_folder_default(void) ATTR_WARN_UNUSED_RESULT; +const char *BKE_appdir_folder_root(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; +const char *BKE_appdir_folder_default_or_root(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; const char *BKE_appdir_folder_home(void); bool BKE_appdir_folder_documents(char *dir); bool BKE_appdir_folder_caches(char *r_path, size_t path_len); diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index ce4ab8a4ba1..08a3b7d0bbb 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -170,6 +170,26 @@ const char *BKE_appdir_folder_default(void) #endif /* WIN32 */ } +const char *BKE_appdir_folder_root(void) +{ +#ifndef WIN32 + return "/"; +#else + static char root[4]; + BLI_windows_get_default_root_dir(root); + return root; +#endif +} + +const char *BKE_appdir_folder_default_or_root(void) +{ + const char *path = BKE_appdir_folder_default(); + if (path == NULL) { + path = BKE_appdir_folder_root(); + } + return path; +} + /** * Get the user's home directory, i.e. * - Unix: `$HOME` diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h index bf09b56c779..cbf1716602a 100644 --- a/source/blender/blenlib/BLI_winstuff.h +++ b/source/blender/blenlib/BLI_winstuff.h @@ -106,7 +106,7 @@ const char *dirname(char *path); /* Windows utility functions. */ bool BLI_windows_register_blend_extension(const bool background); -void BLI_windows_get_default_root_dir(char *root_dir); +void BLI_windows_get_default_root_dir(char root_dir[4]); int BLI_windows_get_executable_dir(char *str); #ifdef __cplusplus diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index 3001b25bc1e..11345fc7242 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -193,7 +193,7 @@ bool BLI_windows_register_blend_extension(const bool background) return true; } -void BLI_windows_get_default_root_dir(char *root) +void BLI_windows_get_default_root_dir(char root[4]) { char str[MAX_PATH + 1]; diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index f647e1d4e4f..a83e1974baf 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -2463,12 +2463,14 @@ static void file_expand_directory(bContext *C) if (BLI_path_is_rel(params->dir)) { /* Use of 'default' folder here is just to avoid an error message on '//' prefix. */ BLI_path_abs(params->dir, - G.relbase_valid ? BKE_main_blendfile_path(bmain) : BKE_appdir_folder_default()); + G.relbase_valid ? BKE_main_blendfile_path(bmain) : + BKE_appdir_folder_default_or_root()); } else if (params->dir[0] == '~') { char tmpstr[sizeof(params->dir) - 1]; BLI_strncpy(tmpstr, params->dir + 1, sizeof(tmpstr)); - BLI_join_dirfile(params->dir, sizeof(params->dir), BKE_appdir_folder_default(), tmpstr); + BLI_path_join( + params->dir, sizeof(params->dir), BKE_appdir_folder_default_or_root(), tmpstr, NULL); } else if (params->dir[0] == '\0') diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 091c2d5f434..97f22ca7d89 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -769,21 +769,22 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) FS_INSERT_LAST); const char *home = BLI_getenv("HOME"); - + if (home) { # define FS_MACOS_PATH(path, name, icon) \ BLI_snprintf(line, sizeof(line), path, home); \ fsmenu_insert_entry(fsmenu, FS_CATEGORY_OTHER, line, name, icon, FS_INSERT_LAST); - FS_MACOS_PATH("%s/", NULL, ICON_HOME) - FS_MACOS_PATH("%s/Desktop/", N_("Desktop"), ICON_DESKTOP) - FS_MACOS_PATH("%s/Documents/", N_("Documents"), ICON_DOCUMENTS) - FS_MACOS_PATH("%s/Downloads/", N_("Downloads"), ICON_IMPORT) - FS_MACOS_PATH("%s/Movies/", N_("Movies"), ICON_FILE_MOVIE) - FS_MACOS_PATH("%s/Music/", N_("Music"), ICON_FILE_SOUND) - FS_MACOS_PATH("%s/Pictures/", N_("Pictures"), ICON_FILE_IMAGE) - FS_MACOS_PATH("%s/Library/Fonts/", N_("Fonts"), ICON_FILE_FONT) + FS_MACOS_PATH("%s/", NULL, ICON_HOME) + FS_MACOS_PATH("%s/Desktop/", N_("Desktop"), ICON_DESKTOP) + FS_MACOS_PATH("%s/Documents/", N_("Documents"), ICON_DOCUMENTS) + FS_MACOS_PATH("%s/Downloads/", N_("Downloads"), ICON_IMPORT) + FS_MACOS_PATH("%s/Movies/", N_("Movies"), ICON_FILE_MOVIE) + FS_MACOS_PATH("%s/Music/", N_("Music"), ICON_FILE_SOUND) + FS_MACOS_PATH("%s/Pictures/", N_("Pictures"), ICON_FILE_IMAGE) + FS_MACOS_PATH("%s/Library/Fonts/", N_("Fonts"), ICON_FILE_FONT) # undef FS_MACOS_PATH + } /* Get mounted volumes better method OSX 10.6 and higher, see: * https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFURLRef/Reference/reference.html From b99d6e1bed5f238151abfc613448bbbfe67f78d0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 1 Nov 2021 13:42:12 +1100 Subject: [PATCH 1393/1500] Fix errors in BKE_appdir_font_folder_default - Missing NULL check for the HOME environment variable. - The user preference path was written to even when the path didn't exist. --- source/blender/blenkernel/intern/appdir.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index a43b708db52..d872dc67dcb 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -281,22 +281,29 @@ bool BKE_appdir_folder_caches(char *r_path, const size_t path_len) */ bool BKE_appdir_font_folder_default(char *dir) { + char test_dir[FILE_MAXDIR]; + test_dir[0] = '\0'; + #ifdef WIN32 wchar_t wpath[FILE_MAXDIR]; if (SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0)) { wcscat(wpath, L"\\"); - BLI_strncpy_wchar_as_utf8(dir, wpath, FILE_MAXDIR); - return (BLI_exists(dir)); + BLI_strncpy_wchar_as_utf8(test_dir, wpath, sizeof(test_dir)); } - return false; #elif defined(__APPLE__) const char *home = BLI_getenv("HOME"); - BLI_snprintf(dir, FILE_MAXDIR, "%s/Library/Fonts/", home); - return (BLI_exists(dir)); + if (home) { + BLI_path_join(test_dir, sizeof(test_dir), home, "Library", "Fonts", NULL); + } #else - BLI_strncpy(dir, "/usr/share/fonts/", FILE_MAXDIR); - return (BLI_exists(dir)); + STRNCPY(test_dir, "/usr/share/fonts"); #endif + + if (test_dir[0] && BLI_exists(test_dir)) { + BLI_strncpy(dir, test_dir, FILE_MAXDIR); + return true; + } + return false; } /** \} */ From 1e749d0602700f175d70eac75f510512fc3b8b42 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 1 Nov 2021 14:00:58 +1100 Subject: [PATCH 1394/1500] Cleanup: spelling, use C comments --- intern/locale/boost_locale_wrapper.cpp | 21 +++++++++---------- .../blender/blenkernel/intern/spline_base.cc | 2 +- .../draw/engines/overlay/overlay_engine.c | 2 +- source/blender/nodes/NOD_node_tree_ref.hh | 2 +- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/intern/locale/boost_locale_wrapper.cpp b/intern/locale/boost_locale_wrapper.cpp index ede9377b38f..444b51b5e04 100644 --- a/intern/locale/boost_locale_wrapper.cpp +++ b/intern/locale/boost_locale_wrapper.cpp @@ -26,8 +26,8 @@ static std::string messages_path; static std::string default_domain; static std::string locale_str; -/* Note: We cannot use short stuff like boost::locale::gettext, because those return - * std::basic_string objects, which c_ptr()-returned char* is no more valid +/* NOTE: We cannot use short stuff like `boost::locale::gettext`, because those return + * `std::basic_string` objects, which c_ptr()-returned char* is no more valid * once deleted (which happens as soons they are out of scope of this func). */ typedef boost::locale::message_format char_message_facet; static std::locale locale_global; @@ -63,7 +63,7 @@ static void bl_locale_global_cache() void bl_locale_init(const char *_messages_path, const char *_default_domain) { - // Avoid using ICU backend, we do not need its power and it's rather heavy! + /* Avoid using ICU backend, we do not need its power and it's rather heavy! */ boost::locale::localization_backend_manager lman = boost::locale::localization_backend_manager::global(); #if defined(_WIN32) @@ -81,7 +81,7 @@ void bl_locale_set(const char *locale) { boost::locale::generator gen; std::locale _locale; - // Specify location of dictionaries. + /* Specify location of dictionaries. */ gen.add_messages_path(messages_path); gen.add_messages_domain(default_domain); // gen.set_default_messages_domain(default_domain); @@ -99,12 +99,12 @@ void bl_locale_set(const char *locale) #endif } std::locale::global(_locale); - // Note: boost always uses "C" LC_NUMERIC by default! + /* NOTE: boost always uses "C" LC_NUMERIC by default! */ bl_locale_global_cache(); - // Generate the locale string - // (useful to know which locale we are actually using in case of "default" one). + /* Generate the locale string + * (useful to know which locale we are actually using in case of "default" one). */ #define LOCALE_INFO std::use_facet(_locale) locale_str = LOCALE_INFO.language(); @@ -117,10 +117,9 @@ void bl_locale_set(const char *locale) #undef LOCALE_INFO } - // Extra catch on `std::runtime_error` is needed for macOS/Clang as it seems that exceptions - // like `boost::locale::conv::conversion_error` (which inherit from `std::runtime_error`) are - // not caught by their ancestor `std::exception`. See - // https://developer.blender.org/T88877#1177108 . + /* Extra catch on `std::runtime_error` is needed for macOS/Clang as it seems that exceptions + * like `boost::locale::conv::conversion_error` (which inherit from `std::runtime_error`) are + * not caught by their ancestor `std::exception`. See T88877#1177108 */ catch (std::runtime_error const &e) { std::cout << "bl_locale_set(" << locale << "): " << e.what() << " \n"; } diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index bbe4e0aab7b..c2c9d178171 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -486,7 +486,7 @@ Array Spline::sample_uniform_index_factors(const int samples_size) const prev_length = length; } - /* Zero lengths or float innacuracies can cause invalid values, or simply + /* Zero lengths or float inaccuracies can cause invalid values, or simply * skip some, so set the values that weren't completed in the main loop. */ for (const int i : IndexRange(i_sample, samples_size - i_sample)) { samples[i] = float(samples_size); diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index 7f9e37f58d5..d63a50bcc03 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -465,7 +465,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) break; case OB_LATTICE: { /* Unlike the other types above, lattices actually have a bounding box defined, so hide the - * lattice wires if only the boundingbox is requested. */ + * lattice wires if only the bounding-box is requested. */ if (ob->dt > OB_BOUNDBOX) { OVERLAY_lattice_cache_populate(vedata, ob); } diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index e04dd7f41bd..26a5dc9d60f 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -290,7 +290,7 @@ class NodeTreeRef : NonCopyable, NonMovable { struct ToposortResult { Vector sorted_nodes; /** - * There can't be a correct topologycal sort of the nodes when there is a cycle. The nodes will + * There can't be a correct topological sort of the nodes when there is a cycle. The nodes will * still be sorted to some degree. The caller has to decide whether it can handle non-perfect * sorts or not. */ From d052169e7e4b84b5d621fcbf15b187a4951f1f70 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 1 Nov 2021 14:15:33 +1100 Subject: [PATCH 1395/1500] Fix scale cage gizmo in pose-mode The active objects matrix was ignored when calculating the cage. --- source/blender/editors/include/ED_transform.h | 7 +++++++ .../editors/transform/transform_gizmo_3d.c | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index bedd0e2fa35..b132e559baa 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -181,6 +181,13 @@ struct TransformBounds { /* Normalized axis */ float axis[3][3]; float axis_min[3], axis_max[3]; + + /** + * When #TransformCalcParams.use_local_axis is used. + * This is the local space matrix the caller may need to access. + */ + bool use_matrix_space; + float matrix_space[4][4]; }; struct TransformCalcParams { diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 466c4202dbd..e79fdc4890a 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -667,6 +667,8 @@ int ED_transform_calc_gizmo_stats(const bContext *C, } } + tbounds->use_matrix_space = false; + /* transform widget matrix */ unit_m4(rv3d->twmat); @@ -689,13 +691,16 @@ int ED_transform_calc_gizmo_stats(const bContext *C, reset_tw_center(tbounds); copy_m3_m4(tbounds->axis, rv3d->twmat); - if (params->use_local_axis && (ob && ob->mode & OB_MODE_EDIT)) { + if (params->use_local_axis && (ob && ob->mode & (OB_MODE_EDIT | OB_MODE_POSE))) { float diff_mat[3][3]; copy_m3_m4(diff_mat, ob->obmat); normalize_m3(diff_mat); invert_m3(diff_mat); mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat); normalize_m3(tbounds->axis); + + tbounds->use_matrix_space = true; + copy_m4_m4(tbounds->matrix_space, ob->obmat); } if (is_gp_edit) { @@ -970,8 +975,10 @@ int ED_transform_calc_gizmo_stats(const bContext *C, if (totsel_iter) { float mat_local[4][4]; - if (use_mat_local) { - mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat); + if (params->use_local_axis) { + if (use_mat_local) { + mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat); + } } /* use channels to get stats */ @@ -2117,10 +2124,8 @@ static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgr WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true); } else { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - if (ob && ob->mode & OB_MODE_EDIT) { - copy_m4_m4(gz->matrix_space, ob->obmat); + if (tbounds.use_matrix_space) { + copy_m4_m4(gz->matrix_space, tbounds.matrix_space); } else { unit_m4(gz->matrix_space); From 3cd398f16fbe4c5be05561ba47acd06121ee568d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 1 Nov 2021 17:02:01 +1100 Subject: [PATCH 1396/1500] Fix T92694: Auto Perspective is inconsistent with axis aligned views Snapping to an axis aligned view with a 45 degree rotation would set the view orthographic without setting it back to perspective when orbiting the view as you would expect with auto-perspective. Now orthographic is only set for views with rotation of 0, 90, 180, -90. Notes: - Partially reverts logic from cebd025e02f1147c48cd658816ad835f94128a4a at the time RegionView3D.view_axis_roll had not been added, so only setting the orthographic with one particular rotation was a bigger limitation than it is now. - Auto-perspective could be supported when snapping the viewport to diagonal angles, however that's a larger project. --- source/blender/editors/space_view3d/view3d_edit.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 44652fdaf5a..b85b424405e 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -673,7 +673,6 @@ static void viewrotate_apply_snap(ViewOpsData *vod) float zaxis_best[3]; int x, y, z; bool found = false; - bool is_axis_aligned = false; invert_qt_qt_normalized(viewquat_inv, vod->curr.viewquat); @@ -691,10 +690,6 @@ static void viewrotate_apply_snap(ViewOpsData *vod) if (angle_normalized_v3v3(zaxis_test, zaxis) < axis_limit) { copy_v3_v3(zaxis_best, zaxis_test); found = true; - - if (abs(x) + abs(y) + abs(z) == 1) { - is_axis_aligned = true; - } } } } @@ -767,7 +762,7 @@ static void viewrotate_apply_snap(ViewOpsData *vod) viewrotate_apply_dyn_ofs(vod, rv3d->viewquat); if (U.uiflag & USER_AUTOPERSP) { - if (is_axis_aligned) { + if (RV3D_VIEW_IS_AXIS(rv3d->view)) { if (rv3d->persp == RV3D_PERSP) { rv3d->persp = RV3D_ORTHO; } From 346a812d7e1d15ad90bd568b181139929d4df50b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 1 Nov 2021 14:15:33 +1100 Subject: [PATCH 1397/1500] Fix scale cage gizmo in pose-mode The active objects matrix was ignored when calculating the cage. --- source/blender/editors/include/ED_transform.h | 7 +++++++ .../editors/transform/transform_gizmo_3d.c | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index bedd0e2fa35..b132e559baa 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -181,6 +181,13 @@ struct TransformBounds { /* Normalized axis */ float axis[3][3]; float axis_min[3], axis_max[3]; + + /** + * When #TransformCalcParams.use_local_axis is used. + * This is the local space matrix the caller may need to access. + */ + bool use_matrix_space; + float matrix_space[4][4]; }; struct TransformCalcParams { diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 466c4202dbd..e79fdc4890a 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -667,6 +667,8 @@ int ED_transform_calc_gizmo_stats(const bContext *C, } } + tbounds->use_matrix_space = false; + /* transform widget matrix */ unit_m4(rv3d->twmat); @@ -689,13 +691,16 @@ int ED_transform_calc_gizmo_stats(const bContext *C, reset_tw_center(tbounds); copy_m3_m4(tbounds->axis, rv3d->twmat); - if (params->use_local_axis && (ob && ob->mode & OB_MODE_EDIT)) { + if (params->use_local_axis && (ob && ob->mode & (OB_MODE_EDIT | OB_MODE_POSE))) { float diff_mat[3][3]; copy_m3_m4(diff_mat, ob->obmat); normalize_m3(diff_mat); invert_m3(diff_mat); mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat); normalize_m3(tbounds->axis); + + tbounds->use_matrix_space = true; + copy_m4_m4(tbounds->matrix_space, ob->obmat); } if (is_gp_edit) { @@ -970,8 +975,10 @@ int ED_transform_calc_gizmo_stats(const bContext *C, if (totsel_iter) { float mat_local[4][4]; - if (use_mat_local) { - mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat); + if (params->use_local_axis) { + if (use_mat_local) { + mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat); + } } /* use channels to get stats */ @@ -2117,10 +2124,8 @@ static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgr WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true); } else { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - if (ob && ob->mode & OB_MODE_EDIT) { - copy_m4_m4(gz->matrix_space, ob->obmat); + if (tbounds.use_matrix_space) { + copy_m4_m4(gz->matrix_space, tbounds.matrix_space); } else { unit_m4(gz->matrix_space); From 6e26c615af3a12facd6e77b79c54da14ce53325f Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Mon, 1 Nov 2021 17:22:03 +1100 Subject: [PATCH 1398/1500] Fix T92663: Snap Selected to Active not working properly Wrong values were passed for use_offset and pivot_point in 2bcf93bbbeb9e32f680c37a1e0054ff16cb00ef0 Maniphest Tasks: T92663 Ref D13056 --- source/blender/editors/space_view3d/view3d_snap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 67b61ed77d8..583a9ad75c2 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -659,7 +659,7 @@ static int snap_selected_to_active_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (!snap_selected_to_location(C, snap_target_global, -1, false, true)) { + if (!snap_selected_to_location(C, snap_target_global, false, -1, true)) { return OPERATOR_CANCELLED; } return OPERATOR_FINISHED; From 154a06077773a5236d388cc47cdaee9d312abd0a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 1 Nov 2021 17:43:21 +1100 Subject: [PATCH 1399/1500] Fix sequencer preview selection drawing ignoring meta-strips All selection operations respected the currently displayed meta-strip except for drawing. --- source/blender/editors/space_sequencer/sequencer_draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 644d2cbfedb..09cb21eca1e 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -2234,7 +2234,7 @@ void sequencer_draw_preview(const bContext *C, } if (!draw_backdrop && scene->ed != NULL) { - SeqCollection *collection = SEQ_query_rendered_strips(&scene->ed->seqbase, timeline_frame, 0); + SeqCollection *collection = SEQ_query_rendered_strips(scene->ed->seqbasep, timeline_frame, 0); Sequence *seq; Sequence *active_seq = SEQ_select_active_get(scene); SEQ_ITERATOR_FOREACH (seq, collection) { From 806521f7037a5a50bba9d332ab5de3b0172c5a22 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 31 Oct 2021 13:18:28 +0100 Subject: [PATCH 1400/1500] Fix T92671: confusing Cycles debug logs about CPU architecture Instead of printing debug flags listing various CPU and GPU settings that may or may not be used, print when we are using them. This include CPU kernel types, OptiX debugging and CUDA and HIP adaptive compilation. BVH type was already printed. --- intern/cycles/blender/python.cpp | 5 ----- intern/cycles/device/cpu/device_impl.cpp | 3 +-- intern/cycles/device/cuda/device_impl.cpp | 10 ++++++--- intern/cycles/device/hip/device_impl.cpp | 12 ++++++---- intern/cycles/device/optix/device_impl.cpp | 1 + .../cycles/integrator/path_trace_work_gpu.cpp | 4 ++-- intern/cycles/util/debug.cpp | 22 ------------------- intern/cycles/util/debug.h | 2 -- 8 files changed, 19 insertions(+), 40 deletions(-) diff --git a/intern/cycles/blender/python.cpp b/intern/cycles/blender/python.cpp index 20bf6385999..bb9b0a74424 100644 --- a/intern/cycles/blender/python.cpp +++ b/intern/cycles/blender/python.cpp @@ -157,8 +157,6 @@ static PyObject *init_func(PyObject * /*self*/, PyObject *args) DebugFlags().running_inside_blender = true; - VLOG(2) << "Debug flags initialized to:\n" << DebugFlags(); - Py_RETURN_NONE; } @@ -885,8 +883,6 @@ static PyObject *debug_flags_update_func(PyObject * /*self*/, PyObject *args) debug_flags_sync_from_scene(b_scene); - VLOG(2) << "Debug flags set to:\n" << DebugFlags(); - debug_flags_set = true; Py_RETURN_NONE; @@ -896,7 +892,6 @@ static PyObject *debug_flags_reset_func(PyObject * /*self*/, PyObject * /*args*/ { debug_flags_reset(); if (debug_flags_set) { - VLOG(2) << "Debug flags reset to:\n" << DebugFlags(); debug_flags_set = false; } Py_RETURN_NONE; diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp index dbad332f896..d494b40f71d 100644 --- a/intern/cycles/device/cpu/device_impl.cpp +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -68,8 +68,7 @@ CPUDevice::CPUDevice(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_ { /* Pick any kernel, all of them are supposed to have same level of microarchitecture * optimization. */ - VLOG(1) << "Will be using " << kernels.integrator_init_from_camera.get_uarch_name() - << " kernels."; + VLOG(1) << "Using " << kernels.integrator_init_from_camera.get_uarch_name() << " CPU kernels."; if (info.cpu_threads == 0) { info.cpu_threads = TaskScheduler::num_threads(); diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp index 2f9a1394ad8..2bb0592bcc5 100644 --- a/intern/cycles/device/cuda/device_impl.cpp +++ b/intern/cycles/device/cuda/device_impl.cpp @@ -378,7 +378,9 @@ string CUDADevice::compile_kernel(const uint kernel_features, cubin.c_str(), common_cflags.c_str()); - printf("Compiling CUDA kernel ...\n%s\n", command.c_str()); + printf("Compiling %sCUDA kernel ...\n%s\n", + (use_adaptive_compilation()) ? "adaptive " : "", + command.c_str()); # ifdef _WIN32 command = "call " + command; @@ -405,13 +407,15 @@ string CUDADevice::compile_kernel(const uint kernel_features, bool CUDADevice::load_kernels(const uint kernel_features) { - /* TODO(sergey): Support kernels re-load for CUDA devices. + /* TODO(sergey): Support kernels re-load for CUDA devices adaptive compile. * * Currently re-loading kernel will invalidate memory pointers, * causing problems in cuCtxSynchronize. */ if (cuModule) { - VLOG(1) << "Skipping kernel reload, not currently supported."; + if (use_adaptive_compilation()) { + VLOG(1) << "Skipping CUDA kernel reload for adaptive compilation, not currently supported."; + } return true; } diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp index 31b7b07383b..1ea387513d5 100644 --- a/intern/cycles/device/hip/device_impl.cpp +++ b/intern/cycles/device/hip/device_impl.cpp @@ -360,7 +360,9 @@ string HIPDevice::compile_kernel(const uint kernel_features, source_path.c_str(), fatbin.c_str()); - printf("Compiling HIP kernel ...\n%s\n", command.c_str()); + printf("Compiling %sHIP kernel ...\n%s\n", + (use_adaptive_compilation()) ? "adaptive " : "", + command.c_str()); # ifdef _WIN32 command = "call " + command; @@ -387,13 +389,15 @@ string HIPDevice::compile_kernel(const uint kernel_features, bool HIPDevice::load_kernels(const uint kernel_features) { - /* TODO(sergey): Support kernels re-load for HIP devices. + /* TODO(sergey): Support kernels re-load for CUDA devices adaptive compile. * * Currently re-loading kernel will invalidate memory pointers, - * causing problems in hipCtxSynchronize. + * causing problems in cuCtxSynchronize. */ if (hipModule) { - VLOG(1) << "Skipping kernel reload, not currently supported."; + if (use_adaptive_compilation()) { + VLOG(1) << "Skipping HIP kernel reload for adaptive compilation, not currently supported."; + } return true; } diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index e9164cc0a76..9b9a5ac0de7 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -91,6 +91,7 @@ OptiXDevice::OptiXDevice(const DeviceInfo &info, Stats &stats, Profiler &profile }; # endif if (DebugFlags().optix.use_debug) { + VLOG(1) << "Using OptiX debug mode."; options.validationMode = OPTIX_DEVICE_CONTEXT_VALIDATION_MODE_ALL; } optix_assert(optixDeviceContextCreate(cuContext, &options, &context)); diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 251bec0dc8f..dfc1362ab09 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -807,10 +807,10 @@ bool PathTraceWorkGPU::should_use_graphics_interop() interop_use_ = device->should_use_graphics_interop(); if (interop_use_) { - VLOG(2) << "Will be using graphics interop GPU display update."; + VLOG(2) << "Using graphics interop GPU display update."; } else { - VLOG(2) << "Will be using naive GPU display update."; + VLOG(2) << "Using naive GPU display update."; } interop_use_checked_ = true; diff --git a/intern/cycles/util/debug.cpp b/intern/cycles/util/debug.cpp index b49df3d42bc..7d5b6d4e54e 100644 --- a/intern/cycles/util/debug.cpp +++ b/intern/cycles/util/debug.cpp @@ -99,26 +99,4 @@ void DebugFlags::reset() optix.reset(); } -std::ostream &operator<<(std::ostream &os, DebugFlagsConstRef debug_flags) -{ - os << "CPU flags:\n" - << " AVX2 : " << string_from_bool(debug_flags.cpu.avx2) << "\n" - << " AVX : " << string_from_bool(debug_flags.cpu.avx) << "\n" - << " SSE4.1 : " << string_from_bool(debug_flags.cpu.sse41) << "\n" - << " SSE3 : " << string_from_bool(debug_flags.cpu.sse3) << "\n" - << " SSE2 : " << string_from_bool(debug_flags.cpu.sse2) << "\n" - << " BVH layout : " << bvh_layout_name(debug_flags.cpu.bvh_layout) << "\n"; - - os << "CUDA flags:\n" - << " Adaptive Compile : " << string_from_bool(debug_flags.cuda.adaptive_compile) << "\n"; - - os << "OptiX flags:\n" - << " Debug : " << string_from_bool(debug_flags.optix.use_debug) << "\n"; - - os << "HIP flags:\n" - << " HIP streams : " << string_from_bool(debug_flags.hip.adaptive_compile) << "\n"; - - return os; -} - CCL_NAMESPACE_END diff --git a/intern/cycles/util/debug.h b/intern/cycles/util/debug.h index 58b2b047261..548c67600e5 100644 --- a/intern/cycles/util/debug.h +++ b/intern/cycles/util/debug.h @@ -160,8 +160,6 @@ inline DebugFlags &DebugFlags() return DebugFlags::get(); } -std::ostream &operator<<(std::ostream &os, DebugFlagsConstRef debug_flags); - CCL_NAMESPACE_END #endif /* __UTIL_DEBUG_H__ */ From 6bc54cddfbf78fd880d5183e8949f19236ad0aaa Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 31 Oct 2021 13:30:56 +0100 Subject: [PATCH 1401/1500] Fix Cycles logging some excessive detail with default --debug-cycles --- intern/cycles/blender/camera.cpp | 2 +- intern/cycles/blender/curves.cpp | 25 ++----------------------- intern/cycles/blender/sync.cpp | 4 +++- intern/cycles/scene/constant_fold.cpp | 8 ++++---- intern/cycles/scene/shader_nodes.cpp | 12 ++++++------ intern/cycles/scene/svm.cpp | 2 +- 6 files changed, 17 insertions(+), 36 deletions(-) diff --git a/intern/cycles/blender/camera.cpp b/intern/cycles/blender/camera.cpp index f87ebe39d21..b5afcfa7fda 100644 --- a/intern/cycles/blender/camera.cpp +++ b/intern/cycles/blender/camera.cpp @@ -639,7 +639,7 @@ void BlenderSync::sync_camera_motion( /* TODO(sergey): De-duplicate calculation with camera sync. */ float fov = 2.0f * atanf((0.5f * sensor_size) / bcam.lens / aspectratio); if (fov != cam->get_fov()) { - VLOG(1) << "Camera " << b_ob.name() << " FOV change detected."; + VLOG(3) << "Camera " << b_ob.name() << " FOV change detected."; if (motion_time == 0.0f) { cam->set_fov(fov); } diff --git a/intern/cycles/blender/curves.cpp b/intern/cycles/blender/curves.cpp index fb2b329e61d..ffe0c553738 100644 --- a/intern/cycles/blender/curves.cpp +++ b/intern/cycles/blender/curves.cpp @@ -304,10 +304,6 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa } } - if (num_curves > 0) { - VLOG(1) << "Exporting curve segments for mesh " << hair->name; - } - hair->reserve_curves(hair->num_curves() + num_curves, hair->get_curve_keys().size() + num_keys); num_keys = 0; @@ -356,7 +352,7 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa /* check allocation */ if ((hair->get_curve_keys().size() != num_keys) || (hair->num_curves() != num_curves)) { - VLOG(1) << "Allocation failed, clearing data"; + VLOG(1) << "Hair memory allocation failed, clearing data."; hair->clear(true); } } @@ -412,16 +408,11 @@ static void export_hair_motion_validate_attribute(Hair *hair, if (num_motion_keys != num_keys || !have_motion) { /* No motion or hair "topology" changed, remove attributes again. */ if (num_motion_keys != num_keys) { - VLOG(1) << "Hair topology changed, removing attribute."; - } - else { - VLOG(1) << "No motion, removing attribute."; + VLOG(1) << "Hair topology changed, removing motion attribute."; } hair->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION); } else if (motion_step > 0) { - VLOG(1) << "Filling in new motion vertex position for motion_step " << motion_step; - /* Motion, fill up previous steps that we might have skipped because * they had no motion, but we need them anyway now. */ for (int step = 0; step < motion_step; step++) { @@ -437,16 +428,12 @@ static void export_hair_motion_validate_attribute(Hair *hair, static void ExportCurveSegmentsMotion(Hair *hair, ParticleCurveData *CData, int motion_step) { - VLOG(1) << "Exporting curve motion segments for hair " << hair->name << ", motion step " - << motion_step; - /* find attribute */ Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); bool new_attribute = false; /* add new attribute if it doesn't exist already */ if (!attr_mP) { - VLOG(1) << "Creating new motion vertex position attribute"; attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); new_attribute = true; } @@ -682,10 +669,6 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) const int num_keys = b_hair.points.length(); const int num_curves = b_hair.curves.length(); - if (num_curves > 0) { - VLOG(1) << "Exporting curve segments for hair " << hair->name; - } - hair->reserve_curves(num_curves, num_keys); /* Export curves and points. */ @@ -743,15 +726,11 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_step) { - VLOG(1) << "Exporting curve motion segments for hair " << hair->name << ", motion step " - << motion_step; - /* Find or add attribute. */ Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); bool new_attribute = false; if (!attr_mP) { - VLOG(1) << "Creating new motion vertex position attribute"; attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); new_attribute = true; } diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index e0a13625962..cf0b77a9b16 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -366,7 +366,9 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background) if ((preview && !preview_scrambling_distance) || use_adaptive_sampling) scrambling_distance = 1.0f; - VLOG(1) << "Used Scrambling Distance: " << scrambling_distance; + if (scrambling_distance != 1.0f) { + VLOG(3) << "Using scrambling distance: " << scrambling_distance; + } integrator->set_scrambling_distance(scrambling_distance); if (get_boolean(cscene, "use_fast_gi")) { diff --git a/intern/cycles/scene/constant_fold.cpp b/intern/cycles/scene/constant_fold.cpp index a1fa34e7628..a5fb68bf229 100644 --- a/intern/cycles/scene/constant_fold.cpp +++ b/intern/cycles/scene/constant_fold.cpp @@ -43,7 +43,7 @@ bool ConstantFolder::all_inputs_constant() const void ConstantFolder::make_constant(float value) const { - VLOG(1) << "Folding " << node->name << "::" << output->name() << " to constant (" << value + VLOG(3) << "Folding " << node->name << "::" << output->name() << " to constant (" << value << ")."; foreach (ShaderInput *sock, output->links) { @@ -56,7 +56,7 @@ void ConstantFolder::make_constant(float value) const void ConstantFolder::make_constant(float3 value) const { - VLOG(1) << "Folding " << node->name << "::" << output->name() << " to constant " << value << "."; + VLOG(3) << "Folding " << node->name << "::" << output->name() << " to constant " << value << "."; foreach (ShaderInput *sock, output->links) { sock->set(value); @@ -112,7 +112,7 @@ void ConstantFolder::bypass(ShaderOutput *new_output) const { assert(new_output); - VLOG(1) << "Folding " << node->name << "::" << output->name() << " to socket " + VLOG(3) << "Folding " << node->name << "::" << output->name() << " to socket " << new_output->parent->name << "::" << new_output->name() << "."; /* Remove all outgoing links from socket and connect them to new_output instead. @@ -131,7 +131,7 @@ void ConstantFolder::discard() const { assert(output->type() == SocketType::CLOSURE); - VLOG(1) << "Discarding closure " << node->name << "."; + VLOG(3) << "Discarding closure " << node->name << "."; graph->disconnect(output); } diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 14d051350fb..94c96d473b6 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -2397,7 +2397,7 @@ void GlossyBsdfNode::simplify_settings(Scene *scene) * Note: Keep the epsilon in sync with kernel! */ if (!roughness_input->link && roughness <= 1e-4f) { - VLOG(1) << "Using sharp glossy BSDF."; + VLOG(3) << "Using sharp glossy BSDF."; distribution = CLOSURE_BSDF_REFLECTION_ID; } } @@ -2406,7 +2406,7 @@ void GlossyBsdfNode::simplify_settings(Scene *scene) * benefit from closure blur to remove unwanted noise. */ if (roughness_input->link == NULL && distribution == CLOSURE_BSDF_REFLECTION_ID) { - VLOG(1) << "Using GGX glossy with filter glossy."; + VLOG(3) << "Using GGX glossy with filter glossy."; distribution = CLOSURE_BSDF_MICROFACET_GGX_ID; roughness = 0.0f; } @@ -2490,7 +2490,7 @@ void GlassBsdfNode::simplify_settings(Scene *scene) * Note: Keep the epsilon in sync with kernel! */ if (!roughness_input->link && roughness <= 1e-4f) { - VLOG(1) << "Using sharp glass BSDF."; + VLOG(3) << "Using sharp glass BSDF."; distribution = CLOSURE_BSDF_SHARP_GLASS_ID; } } @@ -2499,7 +2499,7 @@ void GlassBsdfNode::simplify_settings(Scene *scene) * benefit from closure blur to remove unwanted noise. */ if (roughness_input->link == NULL && distribution == CLOSURE_BSDF_SHARP_GLASS_ID) { - VLOG(1) << "Using GGX glass with filter glossy."; + VLOG(3) << "Using GGX glass with filter glossy."; distribution = CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID; roughness = 0.0f; } @@ -2583,7 +2583,7 @@ void RefractionBsdfNode::simplify_settings(Scene *scene) * Note: Keep the epsilon in sync with kernel! */ if (!roughness_input->link && roughness <= 1e-4f) { - VLOG(1) << "Using sharp refraction BSDF."; + VLOG(3) << "Using sharp refraction BSDF."; distribution = CLOSURE_BSDF_REFRACTION_ID; } } @@ -2592,7 +2592,7 @@ void RefractionBsdfNode::simplify_settings(Scene *scene) * benefit from closure blur to remove unwanted noise. */ if (roughness_input->link == NULL && distribution == CLOSURE_BSDF_REFRACTION_ID) { - VLOG(1) << "Using GGX refraction with filter glossy."; + VLOG(3) << "Using GGX refraction with filter glossy."; distribution = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; roughness = 0.0f; } diff --git a/intern/cycles/scene/svm.cpp b/intern/cycles/scene/svm.cpp index 6da0df302ad..d121765bbdd 100644 --- a/intern/cycles/scene/svm.cpp +++ b/intern/cycles/scene/svm.cpp @@ -64,7 +64,7 @@ void SVMShaderManager::device_update_shader(Scene *scene, compiler.background = (shader == scene->background->get_shader(scene)); compiler.compile(shader, *svm_nodes, 0, &summary); - VLOG(2) << "Compilation summary:\n" + VLOG(3) << "Compilation summary:\n" << "Shader name: " << shader->name << "\n" << summary.full_report(); } From cedc80c08d8340cb7db8af52051f9a6f9469fcb7 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 31 Oct 2021 21:59:21 +0100 Subject: [PATCH 1402/1500] Fix Cycles denoising depth pass missing in compositor --- intern/cycles/blender/addon/engine.py | 1 + 1 file changed, 1 insertion(+) diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index c402df12ba9..e5bb77a834a 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -233,6 +233,7 @@ def list_render_passes(scene, srl): if crl.denoising_store_passes: yield ("Denoising Normal", "XYZ", 'VECTOR') yield ("Denoising Albedo", "RGB", 'COLOR') + yield ("Denoising Depth", "Z", 'VALUE') # Custom AOV passes. for aov in srl.aovs: From 0ab1b19de4da2db53548d82d66c8ea5ed40e0f6c Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 31 Oct 2021 22:11:36 +0100 Subject: [PATCH 1403/1500] Fix T92684: Cycles does not fall back to OIDN if OptiX is not available --- intern/cycles/integrator/denoiser.cpp | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp index b6ca96faebf..b89024e0c85 100644 --- a/intern/cycles/integrator/denoiser.cpp +++ b/intern/cycles/integrator/denoiser.cpp @@ -29,23 +29,11 @@ unique_ptr Denoiser::create(Device *path_trace_device, const DenoisePa { DCHECK(params.use); - switch (params.type) { - case DENOISER_OPTIX: - return make_unique(path_trace_device, params); - - case DENOISER_OPENIMAGEDENOISE: - return make_unique(path_trace_device, params); - - case DENOISER_NUM: - case DENOISER_NONE: - case DENOISER_ALL: - /* pass */ - break; + if (params.type == DENOISER_OPTIX && Device::available_devices(DEVICE_MASK_OPTIX).size()) { + return make_unique(path_trace_device, params); } - LOG(FATAL) << "Unhandled denoiser type " << params.type << ", should never happen."; - - return nullptr; + return make_unique(path_trace_device, params); } Denoiser::Denoiser(Device *path_trace_device, const DenoiseParams ¶ms) From 0b060905d98cf4b266e73d9be5471dc0b3e71ecc Mon Sep 17 00:00:00 2001 From: William Leeson Date: Sun, 31 Oct 2021 22:13:30 +0100 Subject: [PATCH 1404/1500] Fix T92575: Cycles black pixels when rendering with > 65k samples Differential Revision: https://developer.blender.org/D13039 --- intern/cycles/kernel/integrator/shadow_state_template.h | 2 +- intern/cycles/kernel/integrator/state_template.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/intern/cycles/kernel/integrator/shadow_state_template.h b/intern/cycles/kernel/integrator/shadow_state_template.h index 1fbadde2642..da25e58ff1b 100644 --- a/intern/cycles/kernel/integrator/shadow_state_template.h +++ b/intern/cycles/kernel/integrator/shadow_state_template.h @@ -20,7 +20,7 @@ KERNEL_STRUCT_BEGIN(shadow_path) /* Index of a pixel within the device render buffer. */ KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, render_pixel_index, KERNEL_FEATURE_PATH_TRACING) /* Current sample number. */ -KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, sample, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, sample, KERNEL_FEATURE_PATH_TRACING) /* Random number generator seed. */ KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, rng_hash, KERNEL_FEATURE_PATH_TRACING) /* Random number dimension offset. */ diff --git a/intern/cycles/kernel/integrator/state_template.h b/intern/cycles/kernel/integrator/state_template.h index b1a6fd36fae..e082424fabd 100644 --- a/intern/cycles/kernel/integrator/state_template.h +++ b/intern/cycles/kernel/integrator/state_template.h @@ -25,7 +25,7 @@ KERNEL_STRUCT_BEGIN(path) * The multiplication is delayed for later, so that state can use 32bit integer. */ KERNEL_STRUCT_MEMBER(path, uint32_t, render_pixel_index, KERNEL_FEATURE_PATH_TRACING) /* Current sample number. */ -KERNEL_STRUCT_MEMBER(path, uint16_t, sample, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(path, uint32_t, sample, KERNEL_FEATURE_PATH_TRACING) /* Current ray bounce depth. */ KERNEL_STRUCT_MEMBER(path, uint16_t, bounce, KERNEL_FEATURE_PATH_TRACING) /* Current transparent ray bounce depth. */ From 28eaa81f1e9a59c41b58834fcef6b5f3581a2581 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 31 Oct 2021 22:23:37 +0100 Subject: [PATCH 1405/1500] Fix Cycles Python warnings when removed OpenCL device was enabled --- intern/cycles/blender/addon/properties.py | 21 ++++++++++++------- intern/cycles/blender/addon/version_update.py | 8 ++++++- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 1e98e6d0a7c..c92e0ec65af 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1360,7 +1360,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): elif entry.type == 'CPU': cpu_devices.append(entry) # Extend all GPU devices with CPU. - if compute_device_type != 'CPU': + if len(devices) and compute_device_type != 'CPU': devices.extend(cpu_devices) return devices @@ -1378,12 +1378,18 @@ class CyclesPreferences(bpy.types.AddonPreferences): self.refresh_devices() return None + def get_compute_device_type(self): + if self.compute_device_type == '': + return 'NONE' + return self.compute_device_type + def get_num_gpu_devices(self): import _cycles - device_list = _cycles.available_devices(self.compute_device_type) + compute_device_type = self.get_compute_device_type() + device_list = _cycles.available_devices(compute_device_type) num = 0 for device in device_list: - if device[1] != self.compute_device_type: + if device[1] != compute_device_type: continue for dev in self.devices: if dev.use and dev.id == device[2]: @@ -1425,15 +1431,16 @@ class CyclesPreferences(bpy.types.AddonPreferences): row = layout.row() row.prop(self, "compute_device_type", expand=True) - if self.compute_device_type == 'NONE': + compute_device_type = self.get_compute_device_type() + if compute_device_type == 'NONE': return row = layout.row() - devices = self.get_devices_for_type(self.compute_device_type) - self._draw_devices(row, self.compute_device_type, devices) + devices = self.get_devices_for_type(compute_device_type) + self._draw_devices(row, compute_device_type, devices) import _cycles has_peer_memory = 0 - for device in _cycles.available_devices(self.compute_device_type): + for device in _cycles.available_devices(compute_device_type): if device[3] and self.find_existing_device_entry(device).use: has_peer_memory += 1 if has_peer_memory > 1: diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py index b3e8e755903..90ed8873c02 100644 --- a/intern/cycles/blender/addon/version_update.py +++ b/intern/cycles/blender/addon/version_update.py @@ -86,7 +86,7 @@ def do_versions(self): # Device might not currently be available so this can fail try: if system.legacy_compute_device_type == 1: - prop.compute_device_type = 'OPENCL' + prop.compute_device_type = 'NONE' # Was OpenCL elif system.legacy_compute_device_type == 2: prop.compute_device_type = 'CUDA' else: @@ -97,6 +97,12 @@ def do_versions(self): # Init device list for UI prop.get_devices(prop.compute_device_type) + if bpy.context.preferences.version <= (3, 0, 40): + # Disable OpenCL device + prop = bpy.context.preferences.addons[__package__].preferences + if prop['compute_device_type'] == 4: + prop.compute_device_type = 'NONE' + # We don't modify startup file because it assumes to # have all the default values only. if not bpy.data.is_saved: From 5327413b37760db2fd1b4457c4dd2db7eee50a8b Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Mon, 1 Nov 2021 10:18:30 +0100 Subject: [PATCH 1406/1500] Cleanup: Remove Cycles device checks for half float. All supported devices support half float now, so we can remove the check. Differential Revision: https://developer.blender.org/D13021 --- intern/cycles/device/cpu/device.cpp | 1 - intern/cycles/device/cuda/device.cpp | 1 - intern/cycles/device/device.cpp | 2 -- intern/cycles/device/device.h | 2 -- intern/cycles/device/hip/device.cpp | 1 - intern/cycles/scene/image.cpp | 3 --- intern/cycles/scene/image.h | 1 - intern/cycles/scene/image_oiio.cpp | 2 +- 8 files changed, 1 insertion(+), 12 deletions(-) diff --git a/intern/cycles/device/cpu/device.cpp b/intern/cycles/device/cpu/device.cpp index f11b49ef65f..5aabed8702a 100644 --- a/intern/cycles/device/cpu/device.cpp +++ b/intern/cycles/device/cpu/device.cpp @@ -38,7 +38,6 @@ void device_cpu_info(vector &devices) info.id = "CPU"; info.num = 0; info.has_osl = true; - info.has_half_images = true; info.has_nanovdb = true; info.has_profiling = true; if (openimagedenoise_supported()) { diff --git a/intern/cycles/device/cuda/device.cpp b/intern/cycles/device/cuda/device.cpp index af2bdc6e29c..0d9e6c72466 100644 --- a/intern/cycles/device/cuda/device.cpp +++ b/intern/cycles/device/cuda/device.cpp @@ -144,7 +144,6 @@ void device_cuda_info(vector &devices) info.description = string(name); info.num = num; - info.has_half_images = (major >= 3); info.has_nanovdb = true; info.denoisers = 0; diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index 69e959b6f7b..3756fc251c0 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -285,7 +285,6 @@ DeviceInfo Device::get_multi_device(const vector &subdevices, info.description = "Multi Device"; info.num = 0; - info.has_half_images = true; info.has_nanovdb = true; info.has_osl = true; info.has_profiling = true; @@ -332,7 +331,6 @@ DeviceInfo Device::get_multi_device(const vector &subdevices, } /* Accumulate device info. */ - info.has_half_images &= device.has_half_images; info.has_nanovdb &= device.has_nanovdb; info.has_osl &= device.has_osl; info.has_profiling &= device.has_profiling; diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index 3cb177adde7..fdd78b3aa4c 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -73,7 +73,6 @@ class DeviceInfo { int num; bool display_device; /* GPU is used as a display device. */ bool has_nanovdb; /* Support NanoVDB volumes. */ - bool has_half_images; /* Support half-float textures. */ bool has_osl; /* Support Open Shading Language. */ bool has_profiling; /* Supports runtime collection of profiling info. */ bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */ @@ -90,7 +89,6 @@ class DeviceInfo { num = 0; cpu_threads = 0; display_device = false; - has_half_images = false; has_nanovdb = false; has_osl = false; has_profiling = false; diff --git a/intern/cycles/device/hip/device.cpp b/intern/cycles/device/hip/device.cpp index f71732d14bb..41b4f3e41fd 100644 --- a/intern/cycles/device/hip/device.cpp +++ b/intern/cycles/device/hip/device.cpp @@ -141,7 +141,6 @@ void device_hip_info(vector &devices) info.description = string(name); info.num = num; - info.has_half_images = (major >= 3); info.has_nanovdb = true; info.denoisers = 0; diff --git a/intern/cycles/scene/image.cpp b/intern/cycles/scene/image.cpp index 80091e01b8c..8bb2d87fd1e 100644 --- a/intern/cycles/scene/image.cpp +++ b/intern/cycles/scene/image.cpp @@ -303,7 +303,6 @@ ImageManager::ImageManager(const DeviceInfo &info) animation_frame = 0; /* Set image limits */ - features.has_half_float = info.has_half_images; features.has_nanovdb = info.has_nanovdb; } @@ -357,8 +356,6 @@ void ImageManager::load_image_metadata(Image *img) metadata.detect_colorspace(); - assert(features.has_half_float || - (metadata.type != IMAGE_DATA_TYPE_HALF4 && metadata.type != IMAGE_DATA_TYPE_HALF)); assert(features.has_nanovdb || (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT || metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3)); diff --git a/intern/cycles/scene/image.h b/intern/cycles/scene/image.h index 6447b028ebf..7cf09dd6d8f 100644 --- a/intern/cycles/scene/image.h +++ b/intern/cycles/scene/image.h @@ -100,7 +100,6 @@ class ImageMetaData { /* Information about supported features that Image loaders can use. */ class ImageDeviceFeatures { public: - bool has_half_float; bool has_nanovdb; }; diff --git a/intern/cycles/scene/image_oiio.cpp b/intern/cycles/scene/image_oiio.cpp index feafae035a1..68ab9b7a997 100644 --- a/intern/cycles/scene/image_oiio.cpp +++ b/intern/cycles/scene/image_oiio.cpp @@ -76,7 +76,7 @@ bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMe } /* check if it's half float */ - if (spec.format == TypeDesc::HALF && features.has_half_float) { + if (spec.format == TypeDesc::HALF) { is_half = true; } From 8379eefafbaed059c210c6ba429e48bcae4cf2ab Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Mon, 1 Nov 2021 11:38:03 +0200 Subject: [PATCH 1407/1500] Cycles: Enable debug symbols for Clang Debug symbols were disabled for Clang at some point due to link issues. This is no longer the case for any reasonably modern version of Clang. So this patch removes the check in question. Differential Revision: https://developer.blender.org/D13045 Reviewed By: brecht --- intern/cycles/blender/CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index 149967ad331..f0540486656 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -138,11 +138,6 @@ endif() blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") -# avoid link failure with clang 3.4 debug -if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT ${CMAKE_C_COMPILER_VERSION} VERSION_LESS '3.4') - string(APPEND CMAKE_CXX_FLAGS_DEBUG " -gline-tables-only") -endif() - add_dependencies(bf_intern_cycles bf_rna) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${ADDON_FILES}" ${CYCLES_INSTALL_PATH}) From a50f8b3fd8e74d7cc0f2224f110c85493acee1cc Mon Sep 17 00:00:00 2001 From: Jarrett Johnson Date: Mon, 1 Nov 2021 11:24:50 +0100 Subject: [PATCH 1408/1500] Pointcloud selection support This patch adds support for selecting pointclouds. Since pointclouds were not properly drawn to the selection buffer (as diagonsed by output from `glReadPixels` and Renderdoc), they were not able to be selectable by depth picking or occlusion queries. In `basic_engine`, objects were rendered with a shader which draws to a depth buffer but only assumes a single position vertex attribute. Pointclouds, though, require at least another vertex attribute `pos_inst` which provides the instance offsets. Thus, this patch adds another shader variant for pointclouds which supports these two attributes and renders the points appropriately. {F11652666} Addresses T92415 Reviewed By: fclem Differential Revision: https://developer.blender.org/D13059 --- release/scripts/addons | 2 +- .../blender/draw/engines/basic/basic_engine.c | 62 ++++++++++++++++++- .../engines/basic/shaders/depth_vert.glsl | 7 +++ 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/release/scripts/addons b/release/scripts/addons index f2a08d80ccd..67f1fbca148 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit f2a08d80ccd3c13af304525778df3905f95bd44d +Subproject commit 67f1fbca1482d9d9362a4001332e785c3fd5d230 diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index 87f5c6f5857..f4fdb9d0912 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -45,6 +45,7 @@ extern char datatoc_depth_vert_glsl[]; extern char datatoc_conservative_depth_geom_glsl[]; extern char datatoc_common_view_lib_glsl[]; +extern char datatoc_common_pointcloud_lib_glsl[]; /* *********** LISTS *********** */ @@ -56,6 +57,7 @@ typedef struct BASIC_StorageList { typedef struct BASIC_PassList { struct DRWPass *depth_pass[2]; + struct DRWPass *depth_pass_pointcloud[2]; struct DRWPass *depth_pass_cull[2]; } BASIC_PassList; @@ -70,7 +72,9 @@ typedef struct BASIC_Data { typedef struct BASIC_Shaders { /* Depth Pre Pass */ struct GPUShader *depth; + struct GPUShader *pointcloud_depth; struct GPUShader *depth_conservative; + struct GPUShader *pointcloud_depth_conservative; } BASIC_Shaders; /* *********** STATIC *********** */ @@ -83,6 +87,7 @@ typedef struct BASIC_PrivateData { DRWShadingGroup *depth_shgrp[2]; DRWShadingGroup *depth_shgrp_cull[2]; DRWShadingGroup *depth_hair_shgrp[2]; + DRWShadingGroup *depth_pointcloud_shgrp[2]; bool use_material_slot_selection; } BASIC_PrivateData; /* Transient data */ @@ -106,6 +111,20 @@ static void basic_engine_init(void *UNUSED(vedata)) .defs = (const char *[]){sh_cfg->def, NULL}, }); + sh_data->pointcloud_depth = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_pointcloud_lib_glsl, + datatoc_depth_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, + "#define POINTCLOUD\n", + "#define INSTANCED_ATTR\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL}, + }); + sh_data->depth_conservative = GPU_shader_create_from_arrays({ .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, @@ -118,6 +137,25 @@ static void basic_engine_init(void *UNUSED(vedata)) .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, .defs = (const char *[]){sh_cfg->def, "#define CONSERVATIVE_RASTER\n", NULL}, }); + + sh_data->pointcloud_depth_conservative = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_pointcloud_lib_glsl, + datatoc_depth_vert_glsl, + NULL}, + .geom = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_conservative_depth_geom_glsl, + NULL}, + .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, + "#define CONSERVATIVE_RASTER\n", + "#define POINTCLOUD\n", + "#define INSTANCED_ATTR\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL}, + }); } } @@ -150,9 +188,17 @@ static void basic_cache_init(void *vedata) DRW_shgroup_uniform_vec2(grp, "sizeViewport", DRW_viewport_size_get(), 1); DRW_shgroup_uniform_vec2(grp, "sizeViewportInv", DRW_viewport_invert_size_get(), 1); + sh = DRW_state_is_select() ? sh_data->pointcloud_depth_conservative : sh_data->pointcloud_depth; + DRW_PASS_CREATE(psl->depth_pass_pointcloud[i], state | clip_state | infront_state); + stl->g_data->depth_pointcloud_shgrp[i] = grp = DRW_shgroup_create(sh, psl->depth_pass_pointcloud[i]); + DRW_shgroup_uniform_vec2(grp, "sizeViewport", DRW_viewport_size_get(), 1); + DRW_shgroup_uniform_vec2(grp, "sizeViewportInv", DRW_viewport_invert_size_get(), 1); + stl->g_data->depth_hair_shgrp[i] = grp = DRW_shgroup_create(sh_data->depth, psl->depth_pass[i]); + + sh = DRW_state_is_select() ? sh_data->depth_conservative : sh_data->depth; state |= DRW_STATE_CULL_BACK; DRW_PASS_CREATE(psl->depth_pass_cull[i], state | clip_state | infront_state); stl->g_data->depth_shgrp_cull[i] = grp = DRW_shgroup_create(sh, psl->depth_pass_cull[i]); @@ -231,8 +277,16 @@ static void basic_cache_populate(void *vedata, Object *ob) !DRW_state_is_image_render(); const bool do_cull = (draw_ctx->v3d && (draw_ctx->v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)); - DRWShadingGroup *shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : - stl->g_data->depth_shgrp[do_in_front]; + + DRWShadingGroup *shgrp = NULL; + + if (ob->type == OB_POINTCLOUD) { + shgrp = stl->g_data->depth_pointcloud_shgrp[do_in_front]; + } + else { + shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : + stl->g_data->depth_shgrp[do_in_front]; + } if (use_sculpt_pbvh) { DRW_shgroup_call_sculpt(shgrp, ob, false, false); @@ -273,8 +327,10 @@ static void basic_draw_scene(void *vedata) BASIC_PassList *psl = ((BASIC_Data *)vedata)->psl; DRW_draw_pass(psl->depth_pass[0]); + DRW_draw_pass(psl->depth_pass_pointcloud[0]); DRW_draw_pass(psl->depth_pass_cull[0]); DRW_draw_pass(psl->depth_pass[1]); + DRW_draw_pass(psl->depth_pass_pointcloud[1]); DRW_draw_pass(psl->depth_pass_cull[1]); } @@ -284,6 +340,8 @@ static void basic_engine_free(void) BASIC_Shaders *sh_data = &e_data.sh_data[i]; DRW_SHADER_FREE_SAFE(sh_data->depth); DRW_SHADER_FREE_SAFE(sh_data->depth_conservative); + DRW_SHADER_FREE_SAFE(sh_data->pointcloud_depth); + DRW_SHADER_FREE_SAFE(sh_data->pointcloud_depth_conservative); } } diff --git a/source/blender/draw/engines/basic/shaders/depth_vert.glsl b/source/blender/draw/engines/basic/shaders/depth_vert.glsl index 318d0acef6f..be2a7d56d19 100644 --- a/source/blender/draw/engines/basic/shaders/depth_vert.glsl +++ b/source/blender/draw/engines/basic/shaders/depth_vert.glsl @@ -3,7 +3,9 @@ RESOURCE_ID_VARYING #endif +#ifndef POINTCLOUD in vec3 pos; +#endif void main() { @@ -12,7 +14,12 @@ void main() PASS_RESOURCE_ID #endif +#ifdef POINTCLOUD + vec3 world_pos = pointcloud_get_pos(); +#else vec3 world_pos = point_object_to_world(pos); +#endif + gl_Position = point_world_to_ndc(world_pos); #ifdef USE_WORLD_CLIP_PLANES From fe4400121551e06706cdf06f05b8d8d30f54c64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Mon, 1 Nov 2021 11:40:34 +0100 Subject: [PATCH 1409/1500] Revert "Pointcloud selection support" This reverts commit a50f8b3fd8e74d7cc0f2224f110c85493acee1cc. --- release/scripts/addons | 2 +- .../blender/draw/engines/basic/basic_engine.c | 62 +------------------ .../engines/basic/shaders/depth_vert.glsl | 7 --- 3 files changed, 3 insertions(+), 68 deletions(-) diff --git a/release/scripts/addons b/release/scripts/addons index 67f1fbca148..f2a08d80ccd 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 67f1fbca1482d9d9362a4001332e785c3fd5d230 +Subproject commit f2a08d80ccd3c13af304525778df3905f95bd44d diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index f4fdb9d0912..87f5c6f5857 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -45,7 +45,6 @@ extern char datatoc_depth_vert_glsl[]; extern char datatoc_conservative_depth_geom_glsl[]; extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_common_pointcloud_lib_glsl[]; /* *********** LISTS *********** */ @@ -57,7 +56,6 @@ typedef struct BASIC_StorageList { typedef struct BASIC_PassList { struct DRWPass *depth_pass[2]; - struct DRWPass *depth_pass_pointcloud[2]; struct DRWPass *depth_pass_cull[2]; } BASIC_PassList; @@ -72,9 +70,7 @@ typedef struct BASIC_Data { typedef struct BASIC_Shaders { /* Depth Pre Pass */ struct GPUShader *depth; - struct GPUShader *pointcloud_depth; struct GPUShader *depth_conservative; - struct GPUShader *pointcloud_depth_conservative; } BASIC_Shaders; /* *********** STATIC *********** */ @@ -87,7 +83,6 @@ typedef struct BASIC_PrivateData { DRWShadingGroup *depth_shgrp[2]; DRWShadingGroup *depth_shgrp_cull[2]; DRWShadingGroup *depth_hair_shgrp[2]; - DRWShadingGroup *depth_pointcloud_shgrp[2]; bool use_material_slot_selection; } BASIC_PrivateData; /* Transient data */ @@ -111,20 +106,6 @@ static void basic_engine_init(void *UNUSED(vedata)) .defs = (const char *[]){sh_cfg->def, NULL}, }); - sh_data->pointcloud_depth = GPU_shader_create_from_arrays({ - .vert = (const char *[]){sh_cfg->lib, - datatoc_common_view_lib_glsl, - datatoc_common_pointcloud_lib_glsl, - datatoc_depth_vert_glsl, - NULL}, - .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, - .defs = (const char *[]){sh_cfg->def, - "#define POINTCLOUD\n", - "#define INSTANCED_ATTR\n", - "#define UNIFORM_RESOURCE_ID\n", - NULL}, - }); - sh_data->depth_conservative = GPU_shader_create_from_arrays({ .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, @@ -137,25 +118,6 @@ static void basic_engine_init(void *UNUSED(vedata)) .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, .defs = (const char *[]){sh_cfg->def, "#define CONSERVATIVE_RASTER\n", NULL}, }); - - sh_data->pointcloud_depth_conservative = GPU_shader_create_from_arrays({ - .vert = (const char *[]){sh_cfg->lib, - datatoc_common_view_lib_glsl, - datatoc_common_pointcloud_lib_glsl, - datatoc_depth_vert_glsl, - NULL}, - .geom = (const char *[]){sh_cfg->lib, - datatoc_common_view_lib_glsl, - datatoc_conservative_depth_geom_glsl, - NULL}, - .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, - .defs = (const char *[]){sh_cfg->def, - "#define CONSERVATIVE_RASTER\n", - "#define POINTCLOUD\n", - "#define INSTANCED_ATTR\n", - "#define UNIFORM_RESOURCE_ID\n", - NULL}, - }); } } @@ -188,17 +150,9 @@ static void basic_cache_init(void *vedata) DRW_shgroup_uniform_vec2(grp, "sizeViewport", DRW_viewport_size_get(), 1); DRW_shgroup_uniform_vec2(grp, "sizeViewportInv", DRW_viewport_invert_size_get(), 1); - sh = DRW_state_is_select() ? sh_data->pointcloud_depth_conservative : sh_data->pointcloud_depth; - DRW_PASS_CREATE(psl->depth_pass_pointcloud[i], state | clip_state | infront_state); - stl->g_data->depth_pointcloud_shgrp[i] = grp = DRW_shgroup_create(sh, psl->depth_pass_pointcloud[i]); - DRW_shgroup_uniform_vec2(grp, "sizeViewport", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_vec2(grp, "sizeViewportInv", DRW_viewport_invert_size_get(), 1); - stl->g_data->depth_hair_shgrp[i] = grp = DRW_shgroup_create(sh_data->depth, psl->depth_pass[i]); - - sh = DRW_state_is_select() ? sh_data->depth_conservative : sh_data->depth; state |= DRW_STATE_CULL_BACK; DRW_PASS_CREATE(psl->depth_pass_cull[i], state | clip_state | infront_state); stl->g_data->depth_shgrp_cull[i] = grp = DRW_shgroup_create(sh, psl->depth_pass_cull[i]); @@ -277,16 +231,8 @@ static void basic_cache_populate(void *vedata, Object *ob) !DRW_state_is_image_render(); const bool do_cull = (draw_ctx->v3d && (draw_ctx->v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)); - - DRWShadingGroup *shgrp = NULL; - - if (ob->type == OB_POINTCLOUD) { - shgrp = stl->g_data->depth_pointcloud_shgrp[do_in_front]; - } - else { - shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : - stl->g_data->depth_shgrp[do_in_front]; - } + DRWShadingGroup *shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : + stl->g_data->depth_shgrp[do_in_front]; if (use_sculpt_pbvh) { DRW_shgroup_call_sculpt(shgrp, ob, false, false); @@ -327,10 +273,8 @@ static void basic_draw_scene(void *vedata) BASIC_PassList *psl = ((BASIC_Data *)vedata)->psl; DRW_draw_pass(psl->depth_pass[0]); - DRW_draw_pass(psl->depth_pass_pointcloud[0]); DRW_draw_pass(psl->depth_pass_cull[0]); DRW_draw_pass(psl->depth_pass[1]); - DRW_draw_pass(psl->depth_pass_pointcloud[1]); DRW_draw_pass(psl->depth_pass_cull[1]); } @@ -340,8 +284,6 @@ static void basic_engine_free(void) BASIC_Shaders *sh_data = &e_data.sh_data[i]; DRW_SHADER_FREE_SAFE(sh_data->depth); DRW_SHADER_FREE_SAFE(sh_data->depth_conservative); - DRW_SHADER_FREE_SAFE(sh_data->pointcloud_depth); - DRW_SHADER_FREE_SAFE(sh_data->pointcloud_depth_conservative); } } diff --git a/source/blender/draw/engines/basic/shaders/depth_vert.glsl b/source/blender/draw/engines/basic/shaders/depth_vert.glsl index be2a7d56d19..318d0acef6f 100644 --- a/source/blender/draw/engines/basic/shaders/depth_vert.glsl +++ b/source/blender/draw/engines/basic/shaders/depth_vert.glsl @@ -3,9 +3,7 @@ RESOURCE_ID_VARYING #endif -#ifndef POINTCLOUD in vec3 pos; -#endif void main() { @@ -14,12 +12,7 @@ void main() PASS_RESOURCE_ID #endif -#ifdef POINTCLOUD - vec3 world_pos = pointcloud_get_pos(); -#else vec3 world_pos = point_object_to_world(pos); -#endif - gl_Position = point_world_to_ndc(world_pos); #ifdef USE_WORLD_CLIP_PLANES From 7dd84f05aa8a8bea9f640f64c8b92846b2c21046 Mon Sep 17 00:00:00 2001 From: Jarrett Johnson Date: Mon, 1 Nov 2021 11:24:50 +0100 Subject: [PATCH 1410/1500] Pointcloud selection support This patch adds support for selecting pointclouds. Since pointclouds were not properly drawn to the selection buffer (as diagonsed by output from `glReadPixels` and Renderdoc), they were not able to be selectable by depth picking or occlusion queries. In `basic_engine`, objects were rendered with a shader which draws to a depth buffer but only assumes a single position vertex attribute. Pointclouds, though, require at least another vertex attribute `pos_inst` which provides the instance offsets. Thus, this patch adds another shader variant for pointclouds which supports these two attributes and renders the points appropriately. {F11652666} Addresses T92415 Reviewed By: fclem Differential Revision: https://developer.blender.org/D13059 --- .../blender/draw/engines/basic/basic_engine.c | 62 ++++++++++++++++++- .../engines/basic/shaders/depth_vert.glsl | 7 +++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index 87f5c6f5857..f4fdb9d0912 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -45,6 +45,7 @@ extern char datatoc_depth_vert_glsl[]; extern char datatoc_conservative_depth_geom_glsl[]; extern char datatoc_common_view_lib_glsl[]; +extern char datatoc_common_pointcloud_lib_glsl[]; /* *********** LISTS *********** */ @@ -56,6 +57,7 @@ typedef struct BASIC_StorageList { typedef struct BASIC_PassList { struct DRWPass *depth_pass[2]; + struct DRWPass *depth_pass_pointcloud[2]; struct DRWPass *depth_pass_cull[2]; } BASIC_PassList; @@ -70,7 +72,9 @@ typedef struct BASIC_Data { typedef struct BASIC_Shaders { /* Depth Pre Pass */ struct GPUShader *depth; + struct GPUShader *pointcloud_depth; struct GPUShader *depth_conservative; + struct GPUShader *pointcloud_depth_conservative; } BASIC_Shaders; /* *********** STATIC *********** */ @@ -83,6 +87,7 @@ typedef struct BASIC_PrivateData { DRWShadingGroup *depth_shgrp[2]; DRWShadingGroup *depth_shgrp_cull[2]; DRWShadingGroup *depth_hair_shgrp[2]; + DRWShadingGroup *depth_pointcloud_shgrp[2]; bool use_material_slot_selection; } BASIC_PrivateData; /* Transient data */ @@ -106,6 +111,20 @@ static void basic_engine_init(void *UNUSED(vedata)) .defs = (const char *[]){sh_cfg->def, NULL}, }); + sh_data->pointcloud_depth = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_pointcloud_lib_glsl, + datatoc_depth_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, + "#define POINTCLOUD\n", + "#define INSTANCED_ATTR\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL}, + }); + sh_data->depth_conservative = GPU_shader_create_from_arrays({ .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, @@ -118,6 +137,25 @@ static void basic_engine_init(void *UNUSED(vedata)) .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, .defs = (const char *[]){sh_cfg->def, "#define CONSERVATIVE_RASTER\n", NULL}, }); + + sh_data->pointcloud_depth_conservative = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_pointcloud_lib_glsl, + datatoc_depth_vert_glsl, + NULL}, + .geom = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_conservative_depth_geom_glsl, + NULL}, + .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, + "#define CONSERVATIVE_RASTER\n", + "#define POINTCLOUD\n", + "#define INSTANCED_ATTR\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL}, + }); } } @@ -150,9 +188,17 @@ static void basic_cache_init(void *vedata) DRW_shgroup_uniform_vec2(grp, "sizeViewport", DRW_viewport_size_get(), 1); DRW_shgroup_uniform_vec2(grp, "sizeViewportInv", DRW_viewport_invert_size_get(), 1); + sh = DRW_state_is_select() ? sh_data->pointcloud_depth_conservative : sh_data->pointcloud_depth; + DRW_PASS_CREATE(psl->depth_pass_pointcloud[i], state | clip_state | infront_state); + stl->g_data->depth_pointcloud_shgrp[i] = grp = DRW_shgroup_create(sh, psl->depth_pass_pointcloud[i]); + DRW_shgroup_uniform_vec2(grp, "sizeViewport", DRW_viewport_size_get(), 1); + DRW_shgroup_uniform_vec2(grp, "sizeViewportInv", DRW_viewport_invert_size_get(), 1); + stl->g_data->depth_hair_shgrp[i] = grp = DRW_shgroup_create(sh_data->depth, psl->depth_pass[i]); + + sh = DRW_state_is_select() ? sh_data->depth_conservative : sh_data->depth; state |= DRW_STATE_CULL_BACK; DRW_PASS_CREATE(psl->depth_pass_cull[i], state | clip_state | infront_state); stl->g_data->depth_shgrp_cull[i] = grp = DRW_shgroup_create(sh, psl->depth_pass_cull[i]); @@ -231,8 +277,16 @@ static void basic_cache_populate(void *vedata, Object *ob) !DRW_state_is_image_render(); const bool do_cull = (draw_ctx->v3d && (draw_ctx->v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)); - DRWShadingGroup *shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : - stl->g_data->depth_shgrp[do_in_front]; + + DRWShadingGroup *shgrp = NULL; + + if (ob->type == OB_POINTCLOUD) { + shgrp = stl->g_data->depth_pointcloud_shgrp[do_in_front]; + } + else { + shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : + stl->g_data->depth_shgrp[do_in_front]; + } if (use_sculpt_pbvh) { DRW_shgroup_call_sculpt(shgrp, ob, false, false); @@ -273,8 +327,10 @@ static void basic_draw_scene(void *vedata) BASIC_PassList *psl = ((BASIC_Data *)vedata)->psl; DRW_draw_pass(psl->depth_pass[0]); + DRW_draw_pass(psl->depth_pass_pointcloud[0]); DRW_draw_pass(psl->depth_pass_cull[0]); DRW_draw_pass(psl->depth_pass[1]); + DRW_draw_pass(psl->depth_pass_pointcloud[1]); DRW_draw_pass(psl->depth_pass_cull[1]); } @@ -284,6 +340,8 @@ static void basic_engine_free(void) BASIC_Shaders *sh_data = &e_data.sh_data[i]; DRW_SHADER_FREE_SAFE(sh_data->depth); DRW_SHADER_FREE_SAFE(sh_data->depth_conservative); + DRW_SHADER_FREE_SAFE(sh_data->pointcloud_depth); + DRW_SHADER_FREE_SAFE(sh_data->pointcloud_depth_conservative); } } diff --git a/source/blender/draw/engines/basic/shaders/depth_vert.glsl b/source/blender/draw/engines/basic/shaders/depth_vert.glsl index 318d0acef6f..be2a7d56d19 100644 --- a/source/blender/draw/engines/basic/shaders/depth_vert.glsl +++ b/source/blender/draw/engines/basic/shaders/depth_vert.glsl @@ -3,7 +3,9 @@ RESOURCE_ID_VARYING #endif +#ifndef POINTCLOUD in vec3 pos; +#endif void main() { @@ -12,7 +14,12 @@ void main() PASS_RESOURCE_ID #endif +#ifdef POINTCLOUD + vec3 world_pos = pointcloud_get_pos(); +#else vec3 world_pos = point_object_to_world(pos); +#endif + gl_Position = point_world_to_ndc(world_pos); #ifdef USE_WORLD_CLIP_PLANES From a96b2f39b82373d05f543bba325b2dd21baabf63 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 1 Nov 2021 12:00:34 +0100 Subject: [PATCH 1411/1500] Geometry Nodes: improve check if object has geometry set instances The improves playback speed in my instance heavy scene from ~3.7 fps to ~3.9 fps. --- .../blender/blenkernel/intern/geometry_set.cc | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index cd1bafe445a..c250c14f1d7 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -610,24 +610,32 @@ bool BKE_object_has_geometry_set_instances(const Object *ob) if (geometry_set == nullptr) { return false; } - if (geometry_set->has_instances()) { - return true; - } - const bool has_mesh = geometry_set->has_mesh(); - const bool has_pointcloud = geometry_set->has_pointcloud(); - const bool has_volume = geometry_set->has_volume(); - const bool has_curve = geometry_set->has_curve(); - if (ob->type == OB_MESH) { - return has_pointcloud || has_volume || has_curve; - } - if (ob->type == OB_POINTCLOUD) { - return has_mesh || has_volume || has_curve; - } - if (ob->type == OB_VOLUME) { - return has_mesh || has_pointcloud || has_curve; - } - if (ELEM(ob->type, OB_CURVE, OB_FONT)) { - return has_mesh || has_pointcloud || has_volume; + for (const GeometryComponent *component : geometry_set->get_components_for_read()) { + if (component->is_empty()) { + continue; + } + const GeometryComponentType type = component->type(); + bool is_instance = false; + switch (type) { + case GEO_COMPONENT_TYPE_MESH: + is_instance = ob->type != OB_MESH; + break; + case GEO_COMPONENT_TYPE_POINT_CLOUD: + is_instance = ob->type != OB_POINTCLOUD; + break; + case GEO_COMPONENT_TYPE_INSTANCES: + is_instance = true; + break; + case GEO_COMPONENT_TYPE_VOLUME: + is_instance = ob->type != OB_VOLUME; + break; + case GEO_COMPONENT_TYPE_CURVE: + is_instance = !ELEM(ob->type, OB_CURVE, OB_FONT); + break; + } + if (is_instance) { + return true; + } } return false; } From 894096a5287f7d25a02338dd5188b67cb9328851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Mon, 1 Nov 2021 12:08:46 +0100 Subject: [PATCH 1412/1500] GPUState: Fix enum max value for enum operator macro Simple oversight. --- source/blender/gpu/GPU_state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index a338728804c..1c1211d2a3e 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -42,7 +42,7 @@ typedef enum eGPUBarrier { GPU_BARRIER_SHADER_STORAGE = (1 << 2), } eGPUBarrier; -ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_TEXTURE_FETCH) +ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_SHADER_STORAGE) /** * Defines the fixed pipeline blending equation. From 81bd49d4fea41f6e569f3778724b33f15e95f853 Mon Sep 17 00:00:00 2001 From: Jarrett Johnson Date: Mon, 1 Nov 2021 11:24:50 +0100 Subject: [PATCH 1413/1500] Pointcloud selection support This patch adds support for selecting pointclouds. Since pointclouds were not properly drawn to the selection buffer (as diagonsed by output from `glReadPixels` and Renderdoc), they were not able to be selectable by depth picking or occlusion queries. In `basic_engine`, objects were rendered with a shader which draws to a depth buffer but only assumes a single position vertex attribute. Pointclouds, though, require at least another vertex attribute `pos_inst` which provides the instance offsets. Thus, this patch adds another shader variant for pointclouds which supports these two attributes and renders the points appropriately. {F11652666} Addresses T92415 Reviewed By: fclem Differential Revision: https://developer.blender.org/D13059 --- .../blender/draw/engines/basic/basic_engine.c | 62 ++++++++++++++++++- .../engines/basic/shaders/depth_vert.glsl | 7 +++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index 87f5c6f5857..f4fdb9d0912 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -45,6 +45,7 @@ extern char datatoc_depth_vert_glsl[]; extern char datatoc_conservative_depth_geom_glsl[]; extern char datatoc_common_view_lib_glsl[]; +extern char datatoc_common_pointcloud_lib_glsl[]; /* *********** LISTS *********** */ @@ -56,6 +57,7 @@ typedef struct BASIC_StorageList { typedef struct BASIC_PassList { struct DRWPass *depth_pass[2]; + struct DRWPass *depth_pass_pointcloud[2]; struct DRWPass *depth_pass_cull[2]; } BASIC_PassList; @@ -70,7 +72,9 @@ typedef struct BASIC_Data { typedef struct BASIC_Shaders { /* Depth Pre Pass */ struct GPUShader *depth; + struct GPUShader *pointcloud_depth; struct GPUShader *depth_conservative; + struct GPUShader *pointcloud_depth_conservative; } BASIC_Shaders; /* *********** STATIC *********** */ @@ -83,6 +87,7 @@ typedef struct BASIC_PrivateData { DRWShadingGroup *depth_shgrp[2]; DRWShadingGroup *depth_shgrp_cull[2]; DRWShadingGroup *depth_hair_shgrp[2]; + DRWShadingGroup *depth_pointcloud_shgrp[2]; bool use_material_slot_selection; } BASIC_PrivateData; /* Transient data */ @@ -106,6 +111,20 @@ static void basic_engine_init(void *UNUSED(vedata)) .defs = (const char *[]){sh_cfg->def, NULL}, }); + sh_data->pointcloud_depth = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_pointcloud_lib_glsl, + datatoc_depth_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, + "#define POINTCLOUD\n", + "#define INSTANCED_ATTR\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL}, + }); + sh_data->depth_conservative = GPU_shader_create_from_arrays({ .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, @@ -118,6 +137,25 @@ static void basic_engine_init(void *UNUSED(vedata)) .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, .defs = (const char *[]){sh_cfg->def, "#define CONSERVATIVE_RASTER\n", NULL}, }); + + sh_data->pointcloud_depth_conservative = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_pointcloud_lib_glsl, + datatoc_depth_vert_glsl, + NULL}, + .geom = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_conservative_depth_geom_glsl, + NULL}, + .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, + "#define CONSERVATIVE_RASTER\n", + "#define POINTCLOUD\n", + "#define INSTANCED_ATTR\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL}, + }); } } @@ -150,9 +188,17 @@ static void basic_cache_init(void *vedata) DRW_shgroup_uniform_vec2(grp, "sizeViewport", DRW_viewport_size_get(), 1); DRW_shgroup_uniform_vec2(grp, "sizeViewportInv", DRW_viewport_invert_size_get(), 1); + sh = DRW_state_is_select() ? sh_data->pointcloud_depth_conservative : sh_data->pointcloud_depth; + DRW_PASS_CREATE(psl->depth_pass_pointcloud[i], state | clip_state | infront_state); + stl->g_data->depth_pointcloud_shgrp[i] = grp = DRW_shgroup_create(sh, psl->depth_pass_pointcloud[i]); + DRW_shgroup_uniform_vec2(grp, "sizeViewport", DRW_viewport_size_get(), 1); + DRW_shgroup_uniform_vec2(grp, "sizeViewportInv", DRW_viewport_invert_size_get(), 1); + stl->g_data->depth_hair_shgrp[i] = grp = DRW_shgroup_create(sh_data->depth, psl->depth_pass[i]); + + sh = DRW_state_is_select() ? sh_data->depth_conservative : sh_data->depth; state |= DRW_STATE_CULL_BACK; DRW_PASS_CREATE(psl->depth_pass_cull[i], state | clip_state | infront_state); stl->g_data->depth_shgrp_cull[i] = grp = DRW_shgroup_create(sh, psl->depth_pass_cull[i]); @@ -231,8 +277,16 @@ static void basic_cache_populate(void *vedata, Object *ob) !DRW_state_is_image_render(); const bool do_cull = (draw_ctx->v3d && (draw_ctx->v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)); - DRWShadingGroup *shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : - stl->g_data->depth_shgrp[do_in_front]; + + DRWShadingGroup *shgrp = NULL; + + if (ob->type == OB_POINTCLOUD) { + shgrp = stl->g_data->depth_pointcloud_shgrp[do_in_front]; + } + else { + shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : + stl->g_data->depth_shgrp[do_in_front]; + } if (use_sculpt_pbvh) { DRW_shgroup_call_sculpt(shgrp, ob, false, false); @@ -273,8 +327,10 @@ static void basic_draw_scene(void *vedata) BASIC_PassList *psl = ((BASIC_Data *)vedata)->psl; DRW_draw_pass(psl->depth_pass[0]); + DRW_draw_pass(psl->depth_pass_pointcloud[0]); DRW_draw_pass(psl->depth_pass_cull[0]); DRW_draw_pass(psl->depth_pass[1]); + DRW_draw_pass(psl->depth_pass_pointcloud[1]); DRW_draw_pass(psl->depth_pass_cull[1]); } @@ -284,6 +340,8 @@ static void basic_engine_free(void) BASIC_Shaders *sh_data = &e_data.sh_data[i]; DRW_SHADER_FREE_SAFE(sh_data->depth); DRW_SHADER_FREE_SAFE(sh_data->depth_conservative); + DRW_SHADER_FREE_SAFE(sh_data->pointcloud_depth); + DRW_SHADER_FREE_SAFE(sh_data->pointcloud_depth_conservative); } } diff --git a/source/blender/draw/engines/basic/shaders/depth_vert.glsl b/source/blender/draw/engines/basic/shaders/depth_vert.glsl index 318d0acef6f..be2a7d56d19 100644 --- a/source/blender/draw/engines/basic/shaders/depth_vert.glsl +++ b/source/blender/draw/engines/basic/shaders/depth_vert.glsl @@ -3,7 +3,9 @@ RESOURCE_ID_VARYING #endif +#ifndef POINTCLOUD in vec3 pos; +#endif void main() { @@ -12,7 +14,12 @@ void main() PASS_RESOURCE_ID #endif +#ifdef POINTCLOUD + vec3 world_pos = pointcloud_get_pos(); +#else vec3 world_pos = point_object_to_world(pos); +#endif + gl_Position = point_world_to_ndc(world_pos); #ifdef USE_WORLD_CLIP_PLANES From 2a4dfaa0e962d69345398833ea2f1b831fc6c202 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 29 Oct 2021 16:45:08 +0200 Subject: [PATCH 1414/1500] Asset Browser: Correct name & tooltip for asset list refresh operator The name and tooltip were talking about file-lists, which exposes the fact that the Asset Browser uses the File Browser code in the UI, which we shouldn't do. This can confuse users. Instead have a dedicated operator for the Asset Browser with a proper name and tooltip. --- .../keyconfig/keymap_data/blender_default.py | 2 ++ .../keymap_data/industry_compatible_data.py | 4 +++ .../startup/bl_ui/space_filebrowser.py | 2 +- .../blender/editors/space_file/file_intern.h | 1 + source/blender/editors/space_file/file_ops.c | 32 +++++++++++++++++-- .../blender/editors/space_file/file_panels.c | 2 +- .../blender/editors/space_file/space_file.c | 1 + 7 files changed, 40 insertions(+), 4 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 2289e8200a6..9f921bd2b70 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2140,7 +2140,9 @@ def km_file_browser(params): ("file.parent", {"type": 'UP_ARROW', "value": 'PRESS', "alt": True}, None), ("file.previous", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True}, None), ("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True}, None), + # The two refresh operators have polls excluding each other (so only one is available depending on context). ("file.refresh", {"type": 'R', "value": 'PRESS'}, None), + ("file.asset_library_refresh", {"type": 'R', "value": 'PRESS'}, None), ("file.parent", {"type": 'P', "value": 'PRESS'}, None), ("file.previous", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), ("file.next", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, None), diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 8200aad1091..0ae64dbc62e 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1227,7 +1227,9 @@ def km_file_browser(params): ("file.previous", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True}, None), ("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True}, None), ("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True}, None), + # The two refresh operators have polls excluding each other (so only one is available depending on context). ("file.refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), + ("file.asset_library_refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ("file.previous", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), ("file.next", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, None), ("wm.context_toggle", {"type": 'H', "value": 'PRESS'}, @@ -1272,7 +1274,9 @@ def km_file_browser_main(params): items.extend([ ("file.mouse_execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), + # The two refresh operators have polls excluding each other (so only one is available depending on context). ("file.refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), + ("file.asset_library_refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, {"properties": [("open", False), ("deselect_all", True)]}), diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index a33e2665b58..05f505c518d 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -792,7 +792,7 @@ class ASSETBROWSER_MT_context_menu(AssetBrowserMenu, Menu): st = context.space_data params = st.params - layout.operator("file.refresh", text="Refresh") + layout.operator("file.asset_library_refresh") layout.separator() diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index f6b5f0f47cd..4be5d6d8008 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -79,6 +79,7 @@ void FILE_OT_directory_new(struct wmOperatorType *ot); void FILE_OT_previous(struct wmOperatorType *ot); void FILE_OT_next(struct wmOperatorType *ot); void FILE_OT_refresh(struct wmOperatorType *ot); +void FILE_OT_asset_library_refresh(struct wmOperatorType *ot); void FILE_OT_filenum(struct wmOperatorType *ot); void FILE_OT_delete(struct wmOperatorType *ot); void FILE_OT_rename(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index a83e1974baf..844514759f3 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1950,8 +1950,36 @@ void FILE_OT_refresh(struct wmOperatorType *ot) /* api callbacks */ ot->exec = file_refresh_exec; - /* Operator works for file or asset browsing */ - ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ + ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */ +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Refresh Asset Library Operator + * \{ */ + +static int file_asset_library_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) +{ + wmWindowManager *wm = CTX_wm_manager(C); + SpaceFile *sfile = CTX_wm_space_file(C); + + ED_fileselect_clear(wm, sfile); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); + + return OPERATOR_FINISHED; +} + +void FILE_OT_asset_library_refresh(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Refresh Asset Library"; + ot->description = "Reread assets and asset catalogs from the asset library on disk"; + ot->idname = "FILE_OT_asset_library_refresh"; + + /* api callbacks */ + ot->exec = file_asset_library_refresh_exec; + ot->poll = ED_operator_asset_browsing_active; } /** \} */ diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index 51d0581d6a4..0e468718a04 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -247,7 +247,7 @@ static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *pane uiItemR(row, ¶ms_ptr, "asset_library_ref", 0, "", ICON_NONE); if (params->asset_library_ref.type != ASSET_LIBRARY_LOCAL) { - uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_refresh"); + uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_asset_library_refresh"); } uiItemS(col); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index ef503708335..b115c63a569 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -688,6 +688,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_previous); WM_operatortype_append(FILE_OT_next); WM_operatortype_append(FILE_OT_refresh); + WM_operatortype_append(FILE_OT_asset_library_refresh); WM_operatortype_append(FILE_OT_bookmark_add); WM_operatortype_append(FILE_OT_bookmark_delete); WM_operatortype_append(FILE_OT_bookmark_cleanup); From 3df587b798a4dc33df0311ca3793f3c4aa441c72 Mon Sep 17 00:00:00 2001 From: Martijn Versteegh Date: Mon, 1 Nov 2021 21:43:18 +1100 Subject: [PATCH 1415/1500] Fix T92681: resolve use of freed filedescriptor in debug builds Ref D13053 --- source/blender/blenloader/intern/readfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 600abcca818..e4fe3e8da00 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1351,7 +1351,6 @@ FileData *blo_filedata_from_memfile(MemFile *memfile, void blo_filedata_free(FileData *fd) { if (fd) { - fd->file->close(fd->file); /* Free all BHeadN data blocks */ #ifndef NDEBUG @@ -1365,6 +1364,7 @@ void blo_filedata_free(FileData *fd) MEM_freeN(new_bhead); } #endif + fd->file->close(fd->file); if (fd->filesdna) { DNA_sdna_free(fd->filesdna); From 9111ea78acf457c27655dbdd7e7fd9d221db67e0 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 29 Oct 2021 12:23:36 +0200 Subject: [PATCH 1416/1500] Localize image mutex lock into runtime field of Image datablock Allows to avoid a global lock being held while reading files from disk, solving performance issues when Cycles needs to read a lot of packed images. Simple test file F11597666 Differential Revision: https://developer.blender.org/D13032 --- source/blender/blenkernel/BKE_image.h | 3 - source/blender/blenkernel/intern/blender.c | 1 - source/blender/blenkernel/intern/image.c | 128 +++++++++++------- .../tests/blendfile_loading_base_test.cc | 1 - source/blender/makesdna/DNA_image_types.h | 8 ++ .../windowmanager/intern/wm_playanim.c | 1 - source/creator/creator.c | 1 - 7 files changed, 88 insertions(+), 55 deletions(-) diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 82d1f299b4b..2d590ec668b 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -47,9 +47,6 @@ struct anim; #define IMA_MAX_SPACE 64 #define IMA_UDIM_MAX 2000 -void BKE_images_init(void); -void BKE_images_exit(void); - void BKE_image_free_packedfiles(struct Image *image); void BKE_image_free_views(struct Image *image); void BKE_image_free_buffers(struct Image *image); diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 97f8bddc043..fb65a9bec7e 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -90,7 +90,6 @@ void BKE_blender_free(void) IMB_exit(); BKE_cachefiles_exit(); - BKE_images_exit(); DEG_free_node_types(); BKE_brush_system_exit(); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 3800cbec94b..cdc8b15f744 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -112,12 +112,26 @@ #include "DNA_view3d_types.h" static CLG_LogRef LOG = {"bke.image"}; -static ThreadMutex *image_mutex; static void image_init(Image *ima, short source, short type); static void image_free_packedfiles(Image *ima); static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src); +/* Reset runtime image fields when datablock is being initialized. */ +static void image_runtime_reset(struct Image *image) +{ + memset(&image->runtime, 0, sizeof(image->runtime)); + image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); + BLI_mutex_init(image->runtime.cache_mutex); +} + +/* Reset runtime image fields when datablock is being copied. */ +static void image_runtime_reset_on_copy(struct Image *image) +{ + image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); + BLI_mutex_init(image->runtime.cache_mutex); +} + static void image_init_data(ID *id) { Image *image = (Image *)id; @@ -167,6 +181,8 @@ static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c else { image_dst->preview = NULL; } + + image_runtime_reset_on_copy(image_dst); } static void image_free_data(ID *id) @@ -194,6 +210,9 @@ static void image_free_data(ID *id) BLI_freelistN(&image->tiles); BLI_freelistN(&image->gpu_refresh_areas); + + BLI_mutex_end(image->runtime.cache_mutex); + MEM_freeN(image->runtime.cache_mutex); } static void image_foreach_cache(ID *id, @@ -327,6 +346,8 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id) ima->lastused = 0; ima->gpuflag = 0; BLI_listbase_clear(&ima->gpu_refresh_areas); + + image_runtime_reset(ima); } static void image_blend_read_lib(BlendLibReader *UNUSED(reader), ID *id) @@ -454,16 +475,6 @@ static struct ImBuf *imagecache_get(Image *image, int index) return NULL; } -void BKE_images_init(void) -{ - image_mutex = BLI_mutex_alloc(); -} - -void BKE_images_exit(void) -{ - BLI_mutex_free(image_mutex); -} - /* ***************** ALLOC & FREE, DATA MANAGING *************** */ static void image_free_cached_frames(Image *image) @@ -516,7 +527,7 @@ static void image_free_anims(Image *ima) void BKE_image_free_buffers_ex(Image *ima, bool do_lock) { if (do_lock) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); } image_free_cached_frames(ima); @@ -534,7 +545,7 @@ void BKE_image_free_buffers_ex(Image *ima, bool do_lock) } if (do_lock) { - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } } @@ -574,6 +585,8 @@ static void image_init(Image *ima, short source, short type) } } + image_runtime_reset(ima); + BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo Format"); } @@ -647,7 +660,9 @@ void BKE_image_merge(Main *bmain, Image *dest, Image *source) { /* sanity check */ if (dest && source && dest != source) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(source->runtime.cache_mutex); + BLI_mutex_lock(dest->runtime.cache_mutex); + if (source->cache != NULL) { struct MovieCacheIter *iter; iter = IMB_moviecacheIter_new(source->cache); @@ -659,7 +674,9 @@ void BKE_image_merge(Main *bmain, Image *dest, Image *source) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + + BLI_mutex_unlock(dest->runtime.cache_mutex); + BLI_mutex_unlock(source->runtime.cache_mutex); BKE_id_free(bmain, source); } @@ -1260,7 +1277,8 @@ static uintptr_t image_mem_size(Image *image) return 0; } - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); + if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -1292,7 +1310,8 @@ static uintptr_t image_mem_size(Image *image) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + + BLI_mutex_unlock(image->runtime.cache_mutex); return size; } @@ -1370,11 +1389,11 @@ static bool imagecache_check_free_anim(ImBuf *ibuf, void *UNUSED(userkey), void /* except_frame is weak, only works for seqs without offset... */ void BKE_image_free_anim_ibufs(Image *ima, int except_frame) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); if (ima->cache != NULL) { IMB_moviecache_cleanup(ima->cache, imagecache_check_free_anim, &except_frame); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } void BKE_image_all_free_anim_ibufs(Main *bmain, int cfra) @@ -3293,7 +3312,7 @@ void BKE_image_ensure_viewer_views(const RenderData *rd, Image *ima, ImageUser * } if (do_reset) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); image_free_cached_frames(ima); BKE_image_free_views(ima); @@ -3301,7 +3320,7 @@ void BKE_image_ensure_viewer_views(const RenderData *rd, Image *ima, ImageUser * /* add new views */ image_viewer_create_views(rd, ima); - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } BLI_thread_unlock(LOCK_DRAW_IMAGE); @@ -3568,7 +3587,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) return; } - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); switch (signal) { case IMA_SIGNAL_FREE: @@ -3703,7 +3722,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) break; } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); /* don't use notifiers because they are not 100% sure to succeeded * this also makes sure all scenes are accounted for. */ @@ -5230,11 +5249,11 @@ ImBuf *BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) { ImBuf *ibuf; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); ibuf = image_acquire_ibuf(ima, iuser, r_lock); - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); return ibuf; } @@ -5253,9 +5272,9 @@ void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock) } if (ibuf) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); IMB_freeImBuf(ibuf); - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } } @@ -5269,7 +5288,7 @@ bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser) return false; } - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); ibuf = image_get_cached_ibuf(ima, iuser, NULL, NULL); @@ -5277,7 +5296,7 @@ bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser) ibuf = image_acquire_ibuf(ima, iuser, NULL); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); IMB_freeImBuf(ibuf); @@ -5297,6 +5316,7 @@ typedef struct ImagePoolItem { typedef struct ImagePool { ListBase image_buffers; BLI_mempool *memory_pool; + ThreadMutex mutex; } ImagePool; ImagePool *BKE_image_pool_new(void) @@ -5304,22 +5324,28 @@ ImagePool *BKE_image_pool_new(void) ImagePool *pool = MEM_callocN(sizeof(ImagePool), "Image Pool"); pool->memory_pool = BLI_mempool_create(sizeof(ImagePoolItem), 0, 128, BLI_MEMPOOL_NOP); + BLI_mutex_init(&pool->mutex); + return pool; } void BKE_image_pool_free(ImagePool *pool) { /* Use single lock to dereference all the image buffers. */ - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(&pool->mutex); for (ImagePoolItem *item = pool->image_buffers.first; item != NULL; item = item->next) { if (item->ibuf != NULL) { + BLI_mutex_lock(item->image->runtime.cache_mutex); IMB_freeImBuf(item->ibuf); + BLI_mutex_unlock(item->image->runtime.cache_mutex); } } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(&pool->mutex); BLI_mempool_destroy(pool->memory_pool); MEM_freeN(pool); + + BLI_mutex_end(&pool->mutex); } BLI_INLINE ImBuf *image_pool_find_item( @@ -5350,28 +5376,34 @@ ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool } if (pool == NULL) { - /* pool could be NULL, in this case use general acquire function */ + /* Pool could be NULL, in this case use general acquire function. */ return BKE_image_acquire_ibuf(ima, iuser, NULL); } image_get_entry_and_index(ima, iuser, &entry, &index); + /* Use double-checked locking, to avoid locking when the requested image buffer is already in the + * pool. */ + ibuf = image_pool_find_item(pool, ima, entry, index, &found); if (found) { return ibuf; } - BLI_mutex_lock(image_mutex); + /* Lock the pool, to allow thread-safe modification of the content of the pool. */ + BLI_mutex_lock(&pool->mutex); ibuf = image_pool_find_item(pool, ima, entry, index, &found); - /* will also create item even in cases image buffer failed to load, - * prevents trying to load the same buggy file multiple times - */ + /* Will also create item even in cases image buffer failed to load, + * prevents trying to load the same buggy file multiple times. */ if (!found) { ImagePoolItem *item; - ibuf = image_acquire_ibuf(ima, iuser, NULL); + /* Thread-safe acquisition of an image buffer from the image. + * The acquisition does not use image pools, so there is no risk of recursive or out-of-order + * mutex locking. */ + ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); item = BLI_mempool_alloc(pool->memory_pool); item->image = ima; @@ -5382,7 +5414,7 @@ ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool BLI_addtail(&pool->image_buffers, item); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(&pool->mutex); return ibuf; } @@ -5783,7 +5815,7 @@ bool BKE_image_is_dirty_writable(Image *image, bool *r_is_writable) bool is_dirty = false; bool is_writable = false; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5798,7 +5830,7 @@ bool BKE_image_is_dirty_writable(Image *image, bool *r_is_writable) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); if (r_is_writable) { *r_is_writable = is_writable; @@ -5827,7 +5859,7 @@ bool BKE_image_buffer_format_writable(ImBuf *ibuf) void BKE_image_file_format_set(Image *image, int ftype, const ImbFormatOptions *options) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5839,14 +5871,14 @@ void BKE_image_file_format_set(Image *image, int ftype, const ImbFormatOptions * } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); } bool BKE_image_has_loaded_ibuf(Image *image) { bool has_loaded_ibuf = false; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5856,7 +5888,7 @@ bool BKE_image_has_loaded_ibuf(Image *image) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); return has_loaded_ibuf; } @@ -5869,7 +5901,7 @@ ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name) { ImBuf *ibuf = NULL; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5884,7 +5916,7 @@ ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); return ibuf; } @@ -5902,7 +5934,7 @@ ImBuf *BKE_image_get_first_ibuf(Image *image) { ImBuf *ibuf = NULL; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5913,7 +5945,7 @@ ImBuf *BKE_image_get_first_ibuf(Image *image) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); return ibuf; } diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc index 8afa631ffc5..f06f6f7d329 100644 --- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc +++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc @@ -65,7 +65,6 @@ void BlendfileLoadingBaseTest::SetUpTestCase() BKE_idtype_init(); BKE_appdir_init(); IMB_init(); - BKE_images_init(); BKE_modifier_init(); DEG_register_node_types(); RNA_init(); diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 30ca9540735..319e1aa75f6 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -147,6 +147,12 @@ typedef enum eImageTextureResolution { IMA_TEXTURE_RESOLUTION_LEN } eImageTextureResolution; +typedef struct Image_Runtime { + /* Mutex used to guarantee thread-safe access to the cached ImBuf of the corresponding image ID. + */ + void *cache_mutex; +} Image_Runtime; + typedef struct Image { ID id; @@ -213,6 +219,8 @@ typedef struct Image { /** ImageView. */ ListBase views; struct Stereo3dFormat *stereo3d_format; + + Image_Runtime runtime; } Image; /* **************** IMAGE ********************* */ diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index fa21dbcb4bb..a41fa94e8c2 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -1845,7 +1845,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv) } IMB_exit(); - BKE_images_exit(); DEG_free_node_types(); totblock = MEM_get_memory_blocks_in_use(); diff --git a/source/creator/creator.c b/source/creator/creator.c index 7c99f954bfc..4171d60b5b6 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -414,7 +414,6 @@ int main(int argc, BKE_idtype_init(); BKE_cachefiles_init(); - BKE_images_init(); BKE_modifier_init(); BKE_gpencil_modifier_init(); BKE_shaderfx_init(); From d07e3bde2090661aeca3c770e8818430ac7f9ad4 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 1 Nov 2021 12:46:28 +0100 Subject: [PATCH 1417/1500] Fix Cycles tests after recent logging changes The constant folding tests rely on logging sync. --- intern/cycles/test/render_graph_finalize_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index 4207b437a41..390d3a2d5f8 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -179,7 +179,7 @@ class RenderGraph : public testing::Test { virtual void SetUp() { util_logging_start(); - util_logging_verbosity_set(1); + util_logging_verbosity_set(3); device_cpu = Device::create(device_info, stats, profiler); scene = new Scene(scene_params, device_cpu); From d5e343be2752afa34fcbcca235e2eddf46836ad5 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 1 Nov 2021 12:58:33 +0100 Subject: [PATCH 1418/1500] Fix T92593: Object preview not re-rendered after "Mark as Asset" When using "Mark as Asset" the second time on an object (after having done a "Mark as Asset" and then a "Clear Asset"), the old preview would be re-used, even if the object was changed meanwhile. This is a bit of a papercut, so always force previews to be re-rendered on "Mark as Asset". --- .../blender/editors/asset/intern/asset_mark_clear.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc index eb254dcd28b..a0a2c63b407 100644 --- a/source/blender/editors/asset/intern/asset_mark_clear.cc +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -23,18 +23,17 @@ #include #include +#include "DNA_ID.h" + #include "BKE_asset.h" #include "BKE_context.h" +#include "BKE_icons.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BLO_readfile.h" -#include "DNA_ID.h" -#include "DNA_asset_types.h" -#include "DNA_space_types.h" - #include "UI_interface_icons.h" #include "RNA_access.h" @@ -66,6 +65,11 @@ bool ED_asset_mark_id(ID *id) void ED_asset_generate_preview(const bContext *C, ID *id) { + PreviewImage *preview = BKE_previewimg_id_get(id); + if (preview) { + BKE_previewimg_clear(preview); + } + UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); } From 7150f919d3ca6638b6c0e0f2389b88f15db5f171 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 1 Nov 2021 13:21:07 +0100 Subject: [PATCH 1419/1500] Cleanup: Remove unused headers in asset files Also move system includes first, like we have it elsewhere in Blender. --- source/blender/blenkernel/intern/asset.cc | 2 -- source/blender/blenkernel/intern/asset_catalog.cc | 11 +++-------- source/blender/blenkernel/intern/asset_library.cc | 8 ++------ .../blenkernel/intern/asset_library_service.cc | 5 ----- source/blender/editors/asset/intern/asset_catalog.cc | 1 - source/blender/editors/asset/intern/asset_filter.cc | 1 - source/blender/editors/asset/intern/asset_handle.cc | 1 - .../asset/intern/asset_library_reference_enum.cc | 1 - source/blender/editors/asset/intern/asset_list.cc | 2 -- .../blender/editors/asset/intern/asset_mark_clear.cc | 5 ----- source/blender/editors/asset/intern/asset_ops.cc | 7 ------- .../editors/asset/intern/asset_temp_id_consumer.cc | 1 - 12 files changed, 5 insertions(+), 40 deletions(-) diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc index 7bea089b9bf..59e402b6680 100644 --- a/source/blender/blenkernel/intern/asset.cc +++ b/source/blender/blenkernel/intern/asset.cc @@ -21,14 +21,12 @@ #include #include "DNA_ID.h" -#include "DNA_asset_types.h" #include "DNA_defaults.h" #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_string_ref.hh" #include "BLI_string_utils.h" -#include "BLI_utildefines.h" #include "BLI_uuid.h" #include "BKE_asset.h" diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 13f66445c46..03043f3b784 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -18,25 +18,20 @@ * \ingroup bke */ +#include +#include + #include "BKE_asset_catalog.hh" #include "BKE_asset_library.h" -#include "BKE_preferences.h" #include "BLI_fileops.h" #include "BLI_path_util.h" -#include "BLI_set.hh" -#include "BLI_string_ref.hh" - -#include "DNA_userdef_types.h" /* For S_ISREG() and S_ISDIR() on Windows. */ #ifdef WIN32 # include "BLI_winstuff.h" #endif -#include -#include - namespace blender::bke { const CatalogFilePath AssetCatalogService::DEFAULT_CATALOG_FILENAME = "blender_assets.cats.txt"; diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index aae8a289d32..68e43852a21 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -18,9 +18,9 @@ * \ingroup bke */ -#include "BKE_asset_catalog.hh" +#include + #include "BKE_asset_library.hh" -#include "BKE_callbacks.h" #include "BKE_main.h" #include "BKE_preferences.h" @@ -29,12 +29,8 @@ #include "DNA_asset_types.h" #include "DNA_userdef_types.h" -#include "MEM_guardedalloc.h" - #include "asset_library_service.hh" -#include - bool blender::bke::AssetLibrary::save_catalogs_when_file_is_saved = true; /** diff --git a/source/blender/blenkernel/intern/asset_library_service.cc b/source/blender/blenkernel/intern/asset_library_service.cc index 7cf95ee4cc1..4619ecb2e42 100644 --- a/source/blender/blenkernel/intern/asset_library_service.cc +++ b/source/blender/blenkernel/intern/asset_library_service.cc @@ -20,16 +20,11 @@ #include "asset_library_service.hh" -#include "BKE_asset_library.hh" #include "BKE_blender.h" -#include "BKE_callbacks.h" -#include "BLI_fileops.h" #include "BLI_path_util.h" #include "BLI_string_ref.hh" -#include "MEM_guardedalloc.h" - #include "CLG_log.h" static CLG_LogRef LOG = {"bke.asset_service"}; diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index 8e1e5be2e47..9634665be7b 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -19,7 +19,6 @@ */ #include "BKE_asset_catalog.hh" -#include "BKE_asset_catalog_path.hh" #include "BKE_asset_library.hh" #include "BKE_main.h" diff --git a/source/blender/editors/asset/intern/asset_filter.cc b/source/blender/editors/asset/intern/asset_filter.cc index 329342a30cd..c22bbc923eb 100644 --- a/source/blender/editors/asset/intern/asset_filter.cc +++ b/source/blender/editors/asset/intern/asset_filter.cc @@ -22,7 +22,6 @@ #include "BLI_listbase.h" -#include "DNA_ID.h" #include "DNA_asset_types.h" #include "ED_asset_filter.h" diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index 5c8d0b1349c..363bd9226da 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -26,7 +26,6 @@ #include -#include "DNA_asset_types.h" #include "DNA_space_types.h" #include "BLO_readfile.h" diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc index c57d121a18f..1a2d3f5837a 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -27,7 +27,6 @@ #include "BKE_preferences.h" -#include "DNA_asset_types.h" #include "DNA_userdef_types.h" #include "UI_resources.h" diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index 4bc15e842fc..b5ea054fb5d 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -32,7 +32,6 @@ #include "BLI_path_util.h" #include "BLI_utility_mixins.hh" -#include "DNA_asset_types.h" #include "DNA_space_types.h" #include "BKE_preferences.h" @@ -40,7 +39,6 @@ #include "ED_fileselect.h" #include "WM_api.h" -#include "WM_types.h" /* XXX uses private header of file-space. */ #include "../space_file/filelist.h" diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc index a0a2c63b407..2e5bdb63359 100644 --- a/source/blender/editors/asset/intern/asset_mark_clear.cc +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -20,9 +20,6 @@ * Functions for marking and clearing assets. */ -#include -#include - #include "DNA_ID.h" #include "BKE_asset.h" @@ -32,8 +29,6 @@ #include "BKE_lib_id.h" #include "BKE_main.h" -#include "BLO_readfile.h" - #include "UI_interface_icons.h" #include "RNA_access.h" diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index d2fd8ab88a4..f7c567c89f6 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -18,19 +18,13 @@ * \ingroup edasset */ -#include "BKE_asset.h" -#include "BKE_asset_catalog.hh" #include "BKE_asset_library.hh" #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_report.h" -#include "BLI_string_ref.hh" -#include "BLI_vector.hh" - #include "ED_asset.h" -#include "ED_asset_catalog.hh" /* XXX needs access to the file list, should all be done via the asset system in future. */ #include "ED_fileselect.h" @@ -38,7 +32,6 @@ #include "RNA_define.h" #include "WM_api.h" -#include "WM_types.h" using namespace blender; diff --git a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc index f664eab5cbb..f136c08f129 100644 --- a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc +++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc @@ -23,7 +23,6 @@ #include -#include "DNA_asset_types.h" #include "DNA_space_types.h" #include "BKE_report.h" From fc01801df719f3a2fa669ae39e512529ca19baf7 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 1 Nov 2021 13:14:23 +0100 Subject: [PATCH 1420/1500] Fix Python error running regression tests after recent changes --- intern/cycles/blender/addon/version_update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py index 90ed8873c02..76bc765ec1d 100644 --- a/intern/cycles/blender/addon/version_update.py +++ b/intern/cycles/blender/addon/version_update.py @@ -100,7 +100,7 @@ def do_versions(self): if bpy.context.preferences.version <= (3, 0, 40): # Disable OpenCL device prop = bpy.context.preferences.addons[__package__].preferences - if prop['compute_device_type'] == 4: + if prop.is_property_set("compute_device_type") and prop['compute_device_type'] == 4: prop.compute_device_type = 'NONE' # We don't modify startup file because it assumes to From f85c58ab65e55f6a8006fcc1140c73fe10c2c364 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 1 Nov 2021 13:35:54 +0100 Subject: [PATCH 1421/1500] Fix Cycles unit test failing after recent changes --- intern/cycles/test/render_graph_finalize_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index 4207b437a41..390d3a2d5f8 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -179,7 +179,7 @@ class RenderGraph : public testing::Test { virtual void SetUp() { util_logging_start(); - util_logging_verbosity_set(1); + util_logging_verbosity_set(3); device_cpu = Device::create(device_info, stats, profiler); scene = new Scene(scene_params, device_cpu); From 9de4f64197b8daa24e74745c1a74bfc94cc79eb2 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 1 Nov 2021 14:44:18 +0100 Subject: [PATCH 1422/1500] Fix compile error on Windows Caused by 7150f919d3ca. This undid part of 79a88b5e919d. Added a comment for why this include is needed, to avoid this error from happening again. --- source/blender/blenkernel/intern/asset_library_service.cc | 1 + source/blender/blenkernel/intern/asset_library_service_test.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/asset_library_service.cc b/source/blender/blenkernel/intern/asset_library_service.cc index 4619ecb2e42..d202d6462cf 100644 --- a/source/blender/blenkernel/intern/asset_library_service.cc +++ b/source/blender/blenkernel/intern/asset_library_service.cc @@ -22,6 +22,7 @@ #include "BKE_blender.h" +#include "BLI_fileops.h" /* For PATH_MAX (at least on Windows). */ #include "BLI_path_util.h" #include "BLI_string_ref.hh" diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index e26ae05301e..ee910cab945 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -19,7 +19,7 @@ #include "asset_library_service.hh" -#include "BLI_fileops.h" +#include "BLI_fileops.h" /* For PATH_MAX (at least on Windows). */ #include "BLI_path_util.h" #include "BKE_appdir.h" From bb3de31f84e350540720d8f52fed472ee5d65644 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 1 Nov 2021 15:08:25 +0100 Subject: [PATCH 1423/1500] Revert "T78995: Enable keylist threaded drawing." This reverts commit 7f1fe10595065128aab2a4aea4bc9c46e155053c. Fixes: T92549 Root cause hasn't been discovered but assert failed at keyframes_keylist.cc#793. Like some data is shared between threads. --- .../editors/animation/keyframes_draw.c | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index e3ea8f0ab21..6fba2d9c258 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -30,7 +30,6 @@ #include "BLI_dlrbTree.h" #include "BLI_listbase.h" #include "BLI_rect.h" -#include "BLI_task.h" #include "DNA_anim_types.h" #include "DNA_gpencil_types.h" @@ -505,25 +504,12 @@ AnimKeylistDrawList *ED_keylist_draw_list_create(void) return MEM_callocN(sizeof(AnimKeylistDrawList), __func__); } -static void ED_keylist_draw_list_elem_build_task(void *__restrict UNUSED(userdata), - void *item, - int UNUSED(index), - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - AnimKeylistDrawListElem *elem = item; - ED_keylist_draw_list_elem_build_keylist(elem); - ED_keylist_draw_list_elem_prepare_for_drawing(elem); -} - static void ED_keylist_draw_list_build_keylists(AnimKeylistDrawList *draw_list) { - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - /* Create a task per item, a single item is complex enough to deserve its own task. */ - settings.min_iter_per_thread = 1; - - BLI_task_parallel_listbase( - &draw_list->channels, NULL, ED_keylist_draw_list_elem_build_task, &settings); + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + ED_keylist_draw_list_elem_build_keylist(elem); + ED_keylist_draw_list_elem_prepare_for_drawing(elem); + } } static void ED_keylist_draw_list_draw_blocks(AnimKeylistDrawList *draw_list, View2D *v2d) From 2fb725ea3085b4a8ca6eb6417a3372f7af26bc39 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 1 Nov 2021 15:14:49 +0100 Subject: [PATCH 1424/1500] Cleanup: Unused argument Fixes strict compiler warnings. --- intern/cycles/scene/image_oiio.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intern/cycles/scene/image_oiio.cpp b/intern/cycles/scene/image_oiio.cpp index 68ab9b7a997..4cea7fbfb01 100644 --- a/intern/cycles/scene/image_oiio.cpp +++ b/intern/cycles/scene/image_oiio.cpp @@ -30,7 +30,8 @@ OIIOImageLoader::~OIIOImageLoader() { } -bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) +bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures & /*features*/, + ImageMetaData &metadata) { /* Perform preliminary checks, with meaningful logging. */ if (!path_exists(filepath.string())) { From 64de6ad4fe4ada417869c936106f184e455b848d Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 1 Nov 2021 15:36:09 +0100 Subject: [PATCH 1425/1500] Fix use-after-free in image code --- source/blender/blenkernel/intern/image.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index cdc8b15f744..5d53fe3e5fe 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -5343,9 +5343,10 @@ void BKE_image_pool_free(ImagePool *pool) BLI_mutex_unlock(&pool->mutex); BLI_mempool_destroy(pool->memory_pool); - MEM_freeN(pool); BLI_mutex_end(&pool->mutex); + + MEM_freeN(pool); } BLI_INLINE ImBuf *image_pool_find_item( From c0fbbc53e8fe6e58a1d6029754a2e1da1f9acfcc Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 1 Nov 2021 11:41:58 -0300 Subject: [PATCH 1426/1500] Fix T92605: Snapping not aligning to face extension Regression indroduced in rB69d6222481b4342dc2a153e62752145aa37ea101 --- .../ED_transform_snap_object_context.h | 24 +++++++++---------- .../editors/transform/transform_snap_object.c | 24 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index 62d1dfbf0b1..c4da1588117 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -142,18 +142,18 @@ short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, struct Object **r_ob, float r_obmat[4][4], float r_face_nor[3]); -bool ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, - struct Depsgraph *depsgraph, - const ARegion *region, - const View3D *v3d, - const unsigned short snap_to, - const struct SnapObjectParams *params, - const float mval[2], - const float prev_co[3], - float *dist_px, - /* return args */ - float r_loc[3], - float r_no[3]); +short ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, + struct Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, + const unsigned short snap_to, + const struct SnapObjectParams *params, + const float mval[2], + const float prev_co[3], + float *dist_px, + /* return args */ + float r_loc[3], + float r_no[3]); bool ED_transform_snap_object_project_all_view3d_ex(SnapObjectContext *sctx, struct Depsgraph *depsgraph, diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index c779fbe4a33..3254d56d795 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -3254,17 +3254,17 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, * \param r_no: hit normal (optional). * \return Snap success */ -bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, - Depsgraph *depsgraph, - const ARegion *region, - const View3D *v3d, - const ushort snap_to, - const struct SnapObjectParams *params, - const float mval[2], - const float prev_co[3], - float *dist_px, - float r_loc[3], - float r_no[3]) +short ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, + Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, + const ushort snap_to, + const struct SnapObjectParams *params, + const float mval[2], + const float prev_co[3], + float *dist_px, + float r_loc[3], + float r_no[3]) { return ED_transform_snap_object_project_view3d_ex(sctx, depsgraph, @@ -3280,7 +3280,7 @@ bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, NULL, NULL, NULL, - NULL) != 0; + NULL); } /** From e85e126e3f52723da62b258cc9b196a9f15e34fd Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 1 Nov 2021 14:42:56 +0100 Subject: [PATCH 1427/1500] IDRemap: Add option to force remapping obdata in edit mode. In theory we should never allow remapping of Objects' obdata ID pointer when the object is in Edit mode. But there are some cases were this is needed, so adding yet another exception option to remapping flags. Preliminary change to fix T92629. --- source/blender/blenkernel/BKE_lib_remap.h | 6 ++++++ source/blender/blenkernel/intern/lib_remap.c | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index 8df6a803358..b05f3ef60df 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -89,6 +89,12 @@ enum { * dealing with IDs temporarily out of Main, but which will be put in it ultimately). */ ID_REMAP_FORCE_USER_REFCOUNT = 1 << 8, + /** + * Force obdata pointers to also be processed, even when object (`id_owner`) is in Edit mode. + * This is required by some tools creating/deleting IDs while operating in Edit mode, like e.g. + * the 'separate' mesh operator. + */ + ID_REMAP_FORCE_OBDATA_IN_EDITMODE = 1 << 9, }; /* NOTE: Requiring new_id to be non-null, this *may* not be the case ultimately, diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 248d85bcae0..15cc9d3c653 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -132,7 +132,8 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) const bool is_obj = (GS(id_owner->name) == ID_OB); const bool is_obj_proxy = (is_obj && (((Object *)id_owner)->proxy || ((Object *)id_owner)->proxy_group)); - const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id_owner)); + const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id_owner) && + (id_remap_data->flag & ID_REMAP_FORCE_OBDATA_IN_EDITMODE) == 0); const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) && (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0; From 8fbbd699467885c89920524be39dee50ae42bf80 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 1 Nov 2021 15:19:17 +0100 Subject: [PATCH 1428/1500] Fix T92629: Crash on mesh separate after rB43bc494892c3. rB43bc494892c3 switched `BKE_libblock_relink_to_newid` to use new ID remapping and libquery code. However, that new code does protect by default against remapping an objects's data pointer when that object is in Edit mode, since this is not a behavior that generic BKE code can handle (due to required editing data for most obdata types when in edit mode). So specific code that does create new IDs and need remapping in Edit mode has to pass specific exception flags to remaping code. This commit adds those remapping flags to `BKE_libblock_relink_to_newid` and add said exception flag to the remapping call from `ED_object_add_duplicate` when the object is in edit mode. --- source/blender/blenkernel/BKE_lib_remap.h | 3 ++- source/blender/blenkernel/intern/collection.c | 2 +- source/blender/blenkernel/intern/lib_remap.c | 22 +++++++++---------- source/blender/blenkernel/intern/object.c | 2 +- source/blender/blenkernel/intern/scene.c | 2 +- source/blender/editors/object/object_add.c | 11 ++++++---- .../blender/editors/object/object_relations.c | 6 ++--- .../windowmanager/intern/wm_files_link.c | 2 +- 8 files changed, 27 insertions(+), 23 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index b05f3ef60df..5e154459a6c 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -117,7 +117,8 @@ void BKE_libblock_relink_ex(struct Main *bmain, void *new_idv, const short remap_flags) ATTR_NONNULL(1, 2); -void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id) ATTR_NONNULL(); +void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id, const int remap_flag) + ATTR_NONNULL(); typedef void (*BKE_library_free_notifier_reference_cb)(const void *); typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 14097ecd8a7..22b939d3cf9 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -716,7 +716,7 @@ Collection *BKE_collection_duplicate(Main *bmain, collection_new->id.tag &= ~LIB_TAG_NEW; /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(bmain, &collection_new->id); + BKE_libblock_relink_to_newid(bmain, &collection_new->id, 0); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 15cc9d3c653..014c923f04f 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -670,7 +670,7 @@ void BKE_libblock_relink_ex( DEG_relations_tag_update(bmain); } -static void libblock_relink_to_newid(Main *bmain, ID *id); +static void libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag); static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data) { const int cb_flag = cb_data->cb_flag; @@ -683,31 +683,31 @@ static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data) ID **id_pointer = cb_data->id_pointer; ID *id = *id_pointer; if (id) { + const int remap_flag = POINTER_AS_INT(cb_data->user_data); /* See: NEW_ID macro */ if (id->newid != NULL) { - BKE_libblock_relink_ex(bmain, - id_owner, - id, - id->newid, - ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); + const int remap_flag_final = remap_flag | ID_REMAP_SKIP_INDIRECT_USAGE | + ID_REMAP_SKIP_OVERRIDE_LIBRARY; + BKE_libblock_relink_ex(bmain, id_owner, id, id->newid, (short)remap_flag_final); id = id->newid; } if (id->tag & LIB_TAG_NEW) { id->tag &= ~LIB_TAG_NEW; - libblock_relink_to_newid(bmain, id); + libblock_relink_to_newid(bmain, id, remap_flag); } } return IDWALK_RET_NOP; } -static void libblock_relink_to_newid(Main *bmain, ID *id) +static void libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag) { if (ID_IS_LINKED(id)) { return; } id->tag &= ~LIB_TAG_NEW; - BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper, NULL, 0); + BKE_library_foreach_ID_link( + bmain, id, id_relink_to_newid_looper, POINTER_FROM_INT(remap_flag), 0); } /** @@ -719,7 +719,7 @@ static void libblock_relink_to_newid(Main *bmain, ID *id) * Very specific usage, not sure we'll keep it on the long run, * currently only used in Object/Collection duplication code... */ -void BKE_libblock_relink_to_newid(Main *bmain, ID *id) +void BKE_libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag) { if (ID_IS_LINKED(id)) { return; @@ -728,7 +728,7 @@ void BKE_libblock_relink_to_newid(Main *bmain, ID *id) BLI_assert(bmain->relations == NULL); BKE_layer_collection_resync_forbid(); - libblock_relink_to_newid(bmain, id); + libblock_relink_to_newid(bmain, id, remap_flag); BKE_layer_collection_resync_allow(); BKE_main_collection_sync_remap(bmain); } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 3b0825fb8db..e263985ce05 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2878,7 +2878,7 @@ Object *BKE_object_duplicate(Main *bmain, if (!is_subprocess) { /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(bmain, &obn->id); + BKE_libblock_relink_to_newid(bmain, &obn->id, 0); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index a827e1c32a2..426410755ef 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1925,7 +1925,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) if (!is_subprocess) { /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(bmain, &sce_copy->id); + BKE_libblock_relink_to_newid(bmain, &sce_copy->id, 0); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index d22ae5bc804..8b5894923ad 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2145,7 +2145,7 @@ static void copy_object_set_idnew(bContext *C) Main *bmain = CTX_data_main(C); CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - BKE_libblock_relink_to_newid(bmain, &ob->id); + BKE_libblock_relink_to_newid(bmain, &ob->id, 0); } CTX_DATA_END; @@ -2378,7 +2378,7 @@ static void make_object_duplilist_real(bContext *C, Object *ob_dst = BLI_ghash_lookup(dupli_gh, dob); /* Remap new object to itself, and clear again newid pointer of orig object. */ - BKE_libblock_relink_to_newid(bmain, &ob_dst->id); + BKE_libblock_relink_to_newid(bmain, &ob_dst->id, 0); DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY); @@ -3374,8 +3374,11 @@ Base *ED_object_add_duplicate( ob = basen->object; - /* link own references to the newly duplicated data T26816. */ - BKE_libblock_relink_to_newid(bmain, &ob->id); + /* Link own references to the newly duplicated data T26816. + * Note that this function can be called from edit-mode code, in which case we may have to + * enforce remapping obdata (by default this is forbidden in edit mode). */ + const int remap_flag = BKE_object_is_in_editmode(ob) ? ID_REMAP_FORCE_OBDATA_IN_EDITMODE : 0; + BKE_libblock_relink_to_newid(bmain, &ob->id, remap_flag); /* DAG_relations_tag_update(bmain); */ /* caller must do */ diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index acd3f058554..556ddb78653 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1690,11 +1690,11 @@ static void libblock_relink_collection(Main *bmain, const bool do_collection) { if (do_collection) { - BKE_libblock_relink_to_newid(bmain, &collection->id); + BKE_libblock_relink_to_newid(bmain, &collection->id, 0); } for (CollectionObject *cob = collection->gobject.first; cob != NULL; cob = cob->next) { - BKE_libblock_relink_to_newid(bmain, &cob->ob->id); + BKE_libblock_relink_to_newid(bmain, &cob->ob->id, 0); } LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { @@ -1768,7 +1768,7 @@ static void single_object_users( single_object_users_collection(bmain, scene, master_collection, flag, copy_collections, true); /* Will also handle the master collection. */ - BKE_libblock_relink_to_newid(bmain, &scene->id); + BKE_libblock_relink_to_newid(bmain, &scene->id, 0); /* Collection and object pointers in collections */ libblock_relink_collection(bmain, scene->master_collection, false); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 7d74ac9605b..c3e0764f6c2 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -812,7 +812,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, BLI_assert(!ID_IS_LINKED(id)); - BKE_libblock_relink_to_newid(bmain, id); + BKE_libblock_relink_to_newid(bmain, id, 0); } /* Remove linked IDs when a local existing data has been reused instead. */ From 06b183d1ca48ea638b8c54102c0c9adf55cef76f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 1 Nov 2021 16:30:40 +0100 Subject: [PATCH 1429/1500] Fix T91507: Crash when calling context menu from confirmation popup A typical issue with popup handling: We have to respect the menu-region and give it priority over the regular region for handling. In this case there isn't a regular region in context even. --- source/blender/editors/interface/interface_context_menu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 516fd4fc7fc..72e7203c6ea 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -926,7 +926,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev } { - const ARegion *region = CTX_wm_region(C); + const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C); uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, event->xy); if (treerow_but) { BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW); @@ -1212,7 +1212,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev } /* UI List item context menu. Scripts can add items to it, by default there's nothing shown. */ - ARegion *region = CTX_wm_region(C); + const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C); const bool is_inside_listbox = ui_list_find_mouse_over(region, event) != NULL; const bool is_inside_listrow = is_inside_listbox ? ui_list_row_find_mouse_over(region, event->xy) != NULL : From 1704a394d88c9da2c892f24843acbec559c1a7e6 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 1 Nov 2021 16:42:50 +0100 Subject: [PATCH 1430/1500] Fix T92689: Assert loading file with a sound. Dummy mistake in rBc8c53ceecc30 (boolean inversion). --- source/blender/blenkernel/intern/scene.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 426410755ef..04106e6f42a 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -768,7 +768,7 @@ static bool seq_foreach_member_id_cb(Sequence *seq, void *user_data) { \ CHECK_TYPE(&((_id_super)->id), ID *); \ BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag)); \ - if (!BKE_lib_query_foreachid_iter_stop((_data))) { \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ return false; \ } \ } \ From 69e504225804c0ce6ad60ffc90a78bbba93c5c1c Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Mon, 1 Nov 2021 08:42:07 -0700 Subject: [PATCH 1431/1500] Fix T92655: spreadsheet_duplicate Split Exception Check SpaceSpreadsheet's runtime is not null when trying to duplicate the data when doing an area split. See D13047 for further details. Differential Revision: https://developer.blender.org/D13047 Reviewed by Jacques Lucke --- .../blender/editors/space_spreadsheet/space_spreadsheet.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 73e0be76466..d54af7ffe2c 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -137,7 +137,12 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) { const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl; SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old); - sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime); + if (sspreadsheet_old->runtime) { + sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime); + } + else { + sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(); + } BLI_listbase_clear(&sspreadsheet_new->row_filters); LISTBASE_FOREACH (const SpreadsheetRowFilter *, src_filter, &sspreadsheet_old->row_filters) { From adc540cf7ccfa665fda4c387c26760cf5d60ebba Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Mon, 1 Nov 2021 08:42:07 -0700 Subject: [PATCH 1432/1500] Fix T92655: spreadsheet_duplicate Split Exception Check SpaceSpreadsheet's runtime is not null when trying to duplicate the data when doing an area split. See D13047 for further details. Differential Revision: https://developer.blender.org/D13047 Reviewed by Jacques Lucke --- .../blender/editors/space_spreadsheet/space_spreadsheet.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 73e0be76466..d54af7ffe2c 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -137,7 +137,12 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) { const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl; SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old); - sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime); + if (sspreadsheet_old->runtime) { + sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime); + } + else { + sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(); + } BLI_listbase_clear(&sspreadsheet_new->row_filters); LISTBASE_FOREACH (const SpreadsheetRowFilter *, src_filter, &sspreadsheet_old->row_filters) { From 2f667c2bc949b31e7e80a67905394e1e5a0bd762 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 1 Nov 2021 17:28:07 +0100 Subject: [PATCH 1433/1500] Fix UI messages, typos, etc. --- release/scripts/modules/bl_i18n_utils/settings.py | 2 ++ .../scripts/modules/bl_i18n_utils/utils_spell_check.py | 8 ++++++++ release/scripts/startup/bl_ui/space_sequencer.py | 2 +- source/blender/editors/space_file/file_draw.c | 2 +- source/blender/windowmanager/xr/intern/wm_xr_operators.c | 2 +- 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index 51b326fb338..4825992b8e2 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -400,6 +400,8 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "verts only", "view", "virtual parents", + "and NVIDIA driver version 470 or newer", + "and AMD driver version ??? or newer", } WARN_MSGID_NOT_CAPITALIZED_ALLOWED |= set(lng[2] for lng in LANGUAGES) diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index c40b4593a19..6baf5129dd7 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -66,6 +66,7 @@ class SpellChecker: "ons", # add-ons "pong", # ping pong "resumable", + "runtimes", "scalable", "shadeless", "shouldn", # shouldn't @@ -76,6 +77,12 @@ class SpellChecker: "vertices", "wasn", # wasn't + # Brands etc. + "htc", + "huawei", + "vive", + "xbox", + # Merged words "antialiasing", "antialias", "arcsine", "arccosine", "arctangent", @@ -131,6 +138,7 @@ class SpellChecker: "forcefield", "forcefields", "fulldome", "fulldomes", "fullscreen", + "gamepad", "gridline", "gridlines", "hardlight", "hemi", diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 120b2d7c13a..6a4babfff8a 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1004,7 +1004,7 @@ class SEQUENCER_MT_image(Menu): class SEQUENCER_MT_image_transform(Menu): - bl_label = "Transfrom" + bl_label = "Transform" def draw(self, _context): layout = self.layout diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 2e2f0c146d6..66aabe39e44 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -1103,7 +1103,7 @@ static void file_draw_invalid_library_hint(const bContext *C, const char *suggestion = TIP_( "Asset Libraries are local directories that can contain .blend files with assets inside.\n" - "Manage Asset Libraries from the File Paths section in Preferences."); + "Manage Asset Libraries from the File Paths section in Preferences"); file_draw_string_multiline( sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, &sy); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_operators.c b/source/blender/windowmanager/xr/intern/wm_xr_operators.c index 36af0147cb8..112312bab7b 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_operators.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c @@ -1164,7 +1164,7 @@ static void WM_OT_xr_navigation_fly(wmOperatorType *ot) "lock_direction", false, "Lock Direction", - "Limit movement to viewer's intial direction"); + "Limit movement to viewer's initial direction"); RNA_def_boolean(ot->srna, "speed_frame_based", true, From 4ed1e19d2f30f2b4ba4313feeaa7c4e56f96b891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 1 Nov 2021 18:50:34 +0100 Subject: [PATCH 1434/1500] Cleanup: extract versioning code for remapping node socket animation When node input sockets are animated, they target the socket by index. As a result, animation data needs to be updated whenever new sockets are added (except when they're added at the end of the list). The code for this is now extracted into its own versioning function, so that it can be used for other versioning steps as well. No functional changes. --- .../blenloader/intern/versioning_290.c | 28 +-------- .../blenloader/intern/versioning_common.cc | 60 +++++++++++++++++++ .../blenloader/intern/versioning_common.h | 8 +++ 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index def14768ec6..f82b7970a60 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -605,34 +605,8 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) * * To play safe we move all the inputs beyond 18 to their rightful new place. * In case users are doing unexpected things with not-really supported keyframeable channels. - * - * The for loop for the input ids is at the top level otherwise we lose the animation - * keyframe data. */ - for (int input_id = 21; input_id >= 18; input_id--) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_SHADER) { - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type != SH_NODE_BSDF_PRINCIPLED) { - continue; - } - - const size_t node_name_length = strlen(node->name); - const size_t node_name_escaped_max_length = (node_name_length * 2); - char *node_name_escaped = MEM_mallocN(node_name_escaped_max_length + 1, - "escaped name"); - BLI_str_escape(node_name_escaped, node->name, node_name_escaped_max_length); - char *rna_path_prefix = BLI_sprintfN("nodes[\"%s\"].inputs", node_name_escaped); - - BKE_animdata_fix_paths_rename_all_ex( - bmain, id, rna_path_prefix, NULL, NULL, input_id, input_id + 1, false); - MEM_freeN(rna_path_prefix); - MEM_freeN(node_name_escaped); - } - } - } - FOREACH_NODETREE_END; - } + version_node_socket_index_animdata(bmain, NTREE_SHADER, SH_NODE_BSDF_PRINCIPLED, 18, 1, 22); } /* Convert all Multires displacement to Catmull-Clark subdivision limit surface. */ diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index ecc944defba..745bb7e8222 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -28,8 +28,10 @@ #include "BLI_listbase.h" #include "BLI_string.h" +#include "BKE_animsys.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_node.h" #include "MEM_guardedalloc.h" @@ -149,3 +151,61 @@ void version_node_id(bNodeTree *ntree, const int node_type, const char *new_name } } } + +/** + * Adjust animation data for newly added node sockets. + * + * Node sockets are addressed by their index (in their RNA path, and thus FCurves/drivers), and + * thus when a new node is added in the middle of the list, existing animation data needs to be + * adjusted. + * + * Since this is about animation data, it only concerns input sockets. + * + * \param node_tree_type node tree type that has these nodes, for example NTREE_SHADER. + * \param node_type node type to adjust, for example SH_NODE_BSDF_PRINCIPLED. + * \param socket_index_orig the original index of the moved socket; when socket 4 moved to 6, + * pass 4 here. + * \param socket_index_offset the offset of the nodes, so when socket 4 moved to 6, + * pass 2 here. + * \param total_number_of_sockets the total number of sockets in the node. + */ +void version_node_socket_index_animdata(Main *bmain, + const int node_tree_type, + const int node_type, + const int socket_index_orig, + const int socket_index_offset, + const int total_number_of_sockets) +{ + + /* The for loop for the input ids is at the top level otherwise we lose the animation + * keyframe data. Not sure what causes that, so I (Sybren) moved the code here from + * versioning_290.c as-is (structure-wise). */ + for (int input_index = total_number_of_sockets - 1; input_index >= socket_index_orig; + input_index--) { + FOREACH_NODETREE_BEGIN (bmain, ntree, owner_id) { + if (ntree->type != node_tree_type) { + continue; + } + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != node_type) { + continue; + } + + const size_t node_name_length = strlen(node->name); + const size_t node_name_escaped_max_length = (node_name_length * 2); + char *node_name_escaped = (char *)MEM_mallocN(node_name_escaped_max_length + 1, + "escaped name"); + BLI_str_escape(node_name_escaped, node->name, node_name_escaped_max_length); + char *rna_path_prefix = BLI_sprintfN("nodes[\"%s\"].inputs", node_name_escaped); + + const int new_index = input_index + socket_index_offset; + BKE_animdata_fix_paths_rename_all_ex( + bmain, owner_id, rna_path_prefix, NULL, NULL, input_index, new_index, false); + MEM_freeN(rna_path_prefix); + MEM_freeN(node_name_escaped); + } + } + FOREACH_NODETREE_END; + } +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index 8697e8e2639..396927229c6 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -52,6 +52,14 @@ void version_node_output_socket_name(struct bNodeTree *ntree, const char *old_name, const char *new_name); +void version_node_socket_index_animdata( + Main *bmain, + int node_tree_type, /* NTREE_....., e.g. NTREE_SHADER */ + int node_type, /* SH_NODE_..., e.g. SH_NODE_BSDF_PRINCIPLED */ + int socket_index_lowest_modified, + int socket_index_offset, + int total_number_of_sockets); + void version_node_id(struct bNodeTree *ntree, const int node_type, const char *new_name); #ifdef __cplusplus From 6321dd3d400738d5015c53af593fd25b09e79209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 1 Nov 2021 18:48:25 +0100 Subject: [PATCH 1435/1500] Fix T92405: Emission Strength Animation of 2.93.5 inherited wrongly in 3.0 CyclesX introduced two new input sockets for the Principled BSDF node in rB08031197250a. This change is now handled in the versioning code so that Animation data targeting those sockets are now updated. Files created with the last Blender release (2.93) or earlier are now correctly versioned. Files created with 3.0 alpha/beta may need to be manually updated. --- source/blender/blenloader/intern/versioning_300.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 68faa4c0672..47d919c151f 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -779,6 +779,8 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) */ { /* Keep this block, even when empty. */ + + version_node_socket_index_animdata(bmain, NTREE_SHADER, SH_NODE_BSDF_PRINCIPLED, 4, 2, 25); } } From b6c2deef05cbaa015c74a0c5b1b0451686de49f6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 1 Nov 2021 13:13:44 -0500 Subject: [PATCH 1436/1500] Fix T92662: Curve to mesh start cap invalid topology rBbe3e09ecec5372f switched the order for vertices referenced by the start cap's corners, but it failed to account for the offset necessary for edge indices, since the order changed. --- source/blender/blenkernel/intern/curve_to_mesh_convert.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index cd40d5e8a41..ed4af2bbb08 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -202,7 +202,9 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, const int i_inv = info.profile_edge_len - i - 1; MLoop &loop_start = r_loops[cap_loop_offset + i]; loop_start.v = info.vert_offset + i_inv; - loop_start.e = profile_edges_start + i_inv; + loop_start.e = profile_edges_start + ((i == (info.profile_edge_len - 1)) ? + (info.profile_edge_len - 1) : + (i_inv - 1)); MLoop &loop_end = r_loops[cap_loop_offset + info.profile_edge_len + i]; loop_end.v = last_ring_vert_offset + i; loop_end.e = last_ring_edge_offset + i; From 85176c86f0c54db13c2ae4270ff6f34811de0dd6 Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Mon, 1 Nov 2021 19:21:12 +0100 Subject: [PATCH 1437/1500] Fix T92722: Error when saving new render preset The preset menu name was not renamed in 4ddad5a7ee5d. --- release/scripts/startup/bl_operators/presets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index 3189f3b3376..514d61e239d 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -280,7 +280,7 @@ class AddPresetRender(AddPresetBase, Operator): """Add or remove a Render Preset""" bl_idname = "render.preset_add" bl_label = "Add Render Preset" - preset_menu = "RENDER_PT_presets" + preset_menu = "RENDER_PT_format_presets" preset_defines = [ "scene = bpy.context.scene" From b8c573c9cd7a0ea3d20970aa2a2fdf15ed135e8c Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Mon, 1 Nov 2021 19:21:12 +0100 Subject: [PATCH 1438/1500] Fix T92722: Error when saving new render preset The preset menu name was not renamed in 4ddad5a7ee5d. --- release/scripts/startup/bl_operators/presets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index 3189f3b3376..514d61e239d 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -280,7 +280,7 @@ class AddPresetRender(AddPresetBase, Operator): """Add or remove a Render Preset""" bl_idname = "render.preset_add" bl_label = "Add Render Preset" - preset_menu = "RENDER_PT_presets" + preset_menu = "RENDER_PT_format_presets" preset_defines = [ "scene = bpy.context.scene" From 1c0be7da4c368e8c626edcf24b5a1f1edd62dbf5 Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Mon, 1 Nov 2021 19:52:27 +0100 Subject: [PATCH 1439/1500] Fix Cycles integrator presets. New presets couldn't be added. --- intern/cycles/blender/addon/presets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/intern/cycles/blender/addon/presets.py b/intern/cycles/blender/addon/presets.py index 37c39904e30..50093438bda 100644 --- a/intern/cycles/blender/addon/presets.py +++ b/intern/cycles/blender/addon/presets.py @@ -40,10 +40,10 @@ class AddPresetIntegrator(AddPresetBase, Operator): "cycles.transparent_max_bounces", "cycles.caustics_reflective", "cycles.caustics_refractive", - "cycles.blur_glossy" - "cycles.use_fast_gi" - "cycles.ao_bounces" - "cycles.ao_bounces_render" + "cycles.blur_glossy", + "cycles.use_fast_gi", + "cycles.ao_bounces", + "cycles.ao_bounces_render", ] preset_subdir = "cycles/integrator" From 55e68f1b70fe19ab919b52c61964979a5592a51e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 1 Nov 2021 14:42:19 -0500 Subject: [PATCH 1440/1500] Fix T92640: Crash with instance input to reverse curve node `extract_input` can only run once. --- source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index 745012c1851..b1dc45a426a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -38,7 +38,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) return; } - Field selection_field = params.extract_input>("Selection"); + Field selection_field = params.get_input>("Selection"); CurveComponent &component = geometry_set.get_component_for_write(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); From 348d7c35a9f9ffb56e716430100a3d167884889c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 1 Nov 2021 14:46:48 -0500 Subject: [PATCH 1441/1500] Geometry Nodes: Support volumes in the set material node Even though volumes can only have one material, it is still necessary to allow assigning a material to a prodecurally created volume. This commit adds volume support and a warning for when a non-constant selection is used with a volume. Fixes T92485 Differential Revision: https://developer.blender.org/D13037 --- .../geometry/nodes/node_geo_set_material.cc | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc index 040509ad6d1..3817de02a38 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -21,6 +21,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_volume_types.h" #include "BKE_material.h" @@ -28,7 +29,8 @@ namespace blender::nodes { static void geo_node_set_material_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Geometry")) + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_VOLUME}); b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); b.add_input(N_("Material")).hide_label(); b.add_output(N_("Geometry")); @@ -64,6 +66,7 @@ static void geo_node_set_material_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Geometry"); + bool volume_selection_warning = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has()) { MeshComponent &mesh_component = geometry_set.get_component_for_write(); @@ -79,8 +82,24 @@ static void geo_node_set_material_exec(GeoNodeExecParams params) assign_material_to_faces(*mesh, selection, material); } } + if (geometry_set.has_volume()) { + Volume &volume = *geometry_set.get_volume_for_write(); + + if (selection_field.node().depends_on_input()) { + volume_selection_warning = true; + } + + BKE_id_material_eval_assign(&volume.id, 1, material); + } }); + if (volume_selection_warning) { + /* Only add the warning once, even if there are many unique volume instances. */ + params.error_message_add( + NodeWarningType::Info, + TIP_("Volumes only support a single material; selection input can not be a field")); + } + params.set_output("Geometry", std::move(geometry_set)); } From 24310441ddc85ecc8dc857ccc75b508db59bd844 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 2 Nov 2021 00:41:41 -0300 Subject: [PATCH 1442/1500] Fix snap cursor still active even when gizmo is not available The snap cursor continued to appear even when the workspace is changed for example. So add the region to check in the cursor pool. --- .../editors/gizmo_library/gizmo_types/snap3d_gizmo.c | 5 ++++- source/blender/editors/include/ED_view3d.h | 1 + source/blender/editors/space_view3d/view3d_cursor_snap.c | 7 +++++++ source/blender/editors/space_view3d/view3d_placement.c | 3 ++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index 93ee6ec2d81..60642158820 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -241,6 +241,10 @@ static void snap_gizmo_draw(const bContext *UNUSED(C), wmGizmo *UNUSED(gz)) static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2]) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; + ARegion *region = CTX_wm_region(C); + + /* Make sure the cursor is only drawn in the gizmo region. */ + snap_gizmo->snap_state->region = region; /* Snap Elements can change while the gizmo is active. Need to be updated somewhere. */ snap_gizmo_snap_elements_update(snap_gizmo); @@ -251,7 +255,6 @@ static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2]) wmWindowManager *wm = CTX_wm_manager(C); const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; if (event) { - ARegion *region = CTX_wm_region(C); x = event->xy[0] - region->winrct.xmin; y = event->xy[1] - region->winrct.ymin; } diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 6d20044d8cf..ebf3316a509 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -277,6 +277,7 @@ typedef struct V3DSnapCursorState { uchar color_line[4]; uchar color_point[4]; uchar color_box[4]; + struct ARegion *region; /* Forces the cursor to be drawn only in this specific region. */ float *prevpoint; float box_dimensions[3]; short snap_elem_force; /* If zero, use scene settings. */ diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index baf61befcba..479acb3cb1a 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -777,6 +777,13 @@ static bool v3d_cursor_snap_pool_fn(bContext *C) return false; }; + V3DSnapCursorState *state = ED_view3d_cursor_snap_state_get(); + if (state->region && (state->region != region)) { + /* Some gizmos are still available even when the region is not available. + * We need to disable the cursor in these cases. */ + return false; + } + return true; } diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 572fc8e3156..7ad512fd029 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -1515,10 +1515,11 @@ static void preview_plane_free_fn(void *customdata) ED_view3d_cursor_snap_deactive(snap_state); } -static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +static void WIDGETGROUP_placement_setup(const bContext *C, wmGizmoGroup *gzgroup) { V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_active(); if (snap_state) { + snap_state->region = CTX_wm_region(C); snap_state->draw_plane = true; gzgroup->customdata = snap_state; From a0edddaa0cbb5fae16809c79ce7b47bf8688470a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 2 Nov 2021 16:02:13 +1100 Subject: [PATCH 1443/1500] Cleanup: clang-tidy --- source/blender/blenloader/intern/versioning_common.cc | 2 +- source/blender/blenloader/intern/versioning_common.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 745bb7e8222..c7ff496fa20 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -201,7 +201,7 @@ void version_node_socket_index_animdata(Main *bmain, const int new_index = input_index + socket_index_offset; BKE_animdata_fix_paths_rename_all_ex( - bmain, owner_id, rna_path_prefix, NULL, NULL, input_index, new_index, false); + bmain, owner_id, rna_path_prefix, nullptr, nullptr, input_index, new_index, false); MEM_freeN(rna_path_prefix); MEM_freeN(node_name_escaped); } diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index 396927229c6..ed1cafdca33 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -56,7 +56,7 @@ void version_node_socket_index_animdata( Main *bmain, int node_tree_type, /* NTREE_....., e.g. NTREE_SHADER */ int node_type, /* SH_NODE_..., e.g. SH_NODE_BSDF_PRINCIPLED */ - int socket_index_lowest_modified, + int socket_index_orig, int socket_index_offset, int total_number_of_sockets); From 4511964594e0e73dfb24f4698efebbc76debc5a1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 2 Nov 2021 15:33:42 +1100 Subject: [PATCH 1444/1500] Fix T92721: "Alt Cursor Access" option fails to drag the cursor --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 9f921bd2b70..344600ddcdd 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -141,10 +141,11 @@ class Params: # Use the "cursor" functionality for RMB select. if use_alt_tool_or_cursor: self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True} + self.cursor_tweak_event = {"type": 'EVT_TWEAK_L', "value": 'ANY', "alt": True} else: self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'} + self.cursor_tweak_event = None - self.cursor_tweak_event = None self.use_fallback_tool = use_fallback_tool self.tool_modifier = {} else: From 2fb43f08e74cf1b209ba3f69d9e5e657beccc37f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 2 Nov 2021 16:02:54 +1100 Subject: [PATCH 1445/1500] Fix "Alt Tool Access" key-map preference with sequencer preview --- .../presets/keyconfig/keymap_data/blender_default.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 344600ddcdd..d6032a3ecce 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -7624,7 +7624,7 @@ def km_sequencer_editor_tool_move(params): "Sequencer Tool: Move", {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.translate", params.tool_maybe_tweak_event, + ("transform.translate", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -7635,7 +7635,7 @@ def km_sequencer_editor_tool_rotate(params): "Sequencer Tool: Rotate", {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.rotate", params.tool_maybe_tweak_event, + ("transform.rotate", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) @@ -7646,7 +7646,7 @@ def km_sequencer_editor_tool_scale(params): "Sequencer Tool: Scale", {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("transform.resize", params.tool_maybe_tweak_event, + ("transform.resize", {**params.tool_maybe_tweak_event, **params.tool_modifier}, {"properties": [("release_confirm", True)]}), ]}, ) From 47d12268e35f3b0e9f73984033d5405626487136 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 2 Nov 2021 09:16:33 +0100 Subject: [PATCH 1446/1500] Cleanup: Change image engine to CPP. Added namespace blender::draw::image_engine. Code is still C-a-like. Only changed the obvious code style to CPP (structs, nullptr, casts). --- source/blender/draw/CMakeLists.txt | 6 +- .../image/{image_engine.c => image_engine.cc} | 76 +++++++++++-------- .../blender/draw/engines/image/image_engine.h | 9 +++ .../{image_private.h => image_private.hh} | 27 +++---- .../image/{image_shader.c => image_shader.cc} | 21 +++-- source/blender/draw/tests/shaders_test.cc | 4 +- 6 files changed, 83 insertions(+), 60 deletions(-) rename source/blender/draw/engines/image/{image_engine.c => image_engine.cc} (90%) rename source/blender/draw/engines/image/{image_private.h => image_private.hh} (84%) rename source/blender/draw/engines/image/{image_shader.c => image_shader.cc} (86%) diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 0baf994d978..3577b836d77 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -112,8 +112,8 @@ set(SRC intern/draw_view_data.cc intern/smaa_textures.c engines/basic/basic_engine.c - engines/image/image_engine.c - engines/image/image_shader.c + engines/image/image_engine.cc + engines/image/image_shader.cc engines/eevee/eevee_bloom.c engines/eevee/eevee_cryptomatte.c engines/eevee/eevee_data.c @@ -220,7 +220,7 @@ set(SRC engines/eevee/eevee_private.h engines/external/external_engine.h engines/image/image_engine.h - engines/image/image_private.h + engines/image/image_private.hh engines/workbench/workbench_engine.h engines/workbench/workbench_private.h engines/select/select_engine.h diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.cc similarity index 90% rename from source/blender/draw/engines/image/image_engine.c rename to source/blender/draw/engines/image/image_engine.cc index 662238df270..72315d10ee8 100644 --- a/source/blender/draw/engines/image/image_engine.c +++ b/source/blender/draw/engines/image/image_engine.cc @@ -39,7 +39,9 @@ #include "GPU_batch.h" #include "image_engine.h" -#include "image_private.h" +#include "image_private.hh" + +namespace blender::draw::image_engine { #define IMAGE_DRAW_FLAG_SHOW_ALPHA (1 << 0) #define IMAGE_DRAW_FLAG_APPLY_ALPHA (1 << 1) @@ -115,14 +117,14 @@ static void space_image_gpu_texture_get(Image *image, BKE_image_multiview_index(image, &sima->iuser); } - if (ibuf == NULL) { + if (ibuf == nullptr) { return; } - if (ibuf->rect == NULL && ibuf->rect_float == NULL) { + if (ibuf->rect == nullptr && ibuf->rect_float == nullptr) { /* This code-path is only supposed to happen when drawing a lazily-allocatable render result. - * In all the other cases the `ED_space_image_acquire_buffer()` is expected to return NULL as - * an image buffer when it has no pixels. */ + * In all the other cases the `ED_space_image_acquire_buffer()` is expected to return nullptr + * as an image buffer when it has no pixels. */ BLI_assert(image->type == IMA_TYPE_R_RESULT); @@ -150,7 +152,7 @@ static void space_image_gpu_texture_get(Image *image, } else if (image->source == IMA_SRC_TILED) { *r_gpu_texture = BKE_image_get_gpu_tiles(image, iuser, ibuf); - *r_tex_tile_data = BKE_image_get_gpu_tilemap(image, iuser, NULL); + *r_tex_tile_data = BKE_image_get_gpu_tilemap(image, iuser, nullptr); *r_owns_texture = false; } else { @@ -168,7 +170,7 @@ static void space_node_gpu_texture_get(Image *image, { *r_gpu_texture = BKE_image_get_gpu_texture(image, iuser, ibuf); *r_owns_texture = false; - *r_tex_tile_data = NULL; + *r_tex_tile_data = nullptr; } static void image_gpu_texture_get(Image *image, @@ -204,7 +206,7 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser const char space_type = draw_ctx->space_data->spacetype; const Scene *scene = draw_ctx->scene; - GPUTexture *tex_tile_data = NULL; + GPUTexture *tex_tile_data = nullptr; image_gpu_texture_get(image, iuser, ibuf, &pd->texture, &pd->owns_texture, &tex_tile_data); if (pd->texture) { @@ -218,7 +220,7 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser } const bool use_premul_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(image, ibuf); - const bool is_tiled_texture = tex_tile_data != NULL; + const bool is_tiled_texture = tex_tile_data != nullptr; int draw_flags = 0; if (space_type == SPACE_IMAGE) { @@ -307,11 +309,11 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser GPUShader *shader = IMAGE_shader_image_get(is_tiled_texture); DRWShadingGroup *shgrp = DRW_shgroup_create(shader, psl->image_pass); if (is_tiled_texture) { - DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, 0); + DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, GPU_SAMPLER_DEFAULT); DRW_shgroup_uniform_texture(shgrp, "imageTileData", tex_tile_data); } else { - DRW_shgroup_uniform_texture_ex(shgrp, "imageTexture", pd->texture, 0); + DRW_shgroup_uniform_texture_ex(shgrp, "imageTexture", pd->texture, GPU_SAMPLER_DEFAULT); } DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", far_near); DRW_shgroup_uniform_vec4_copy(shgrp, "color", color); @@ -332,13 +334,13 @@ static void IMAGE_engine_init(void *ved) IMAGE_Data *vedata = (IMAGE_Data *)ved; IMAGE_StorageList *stl = vedata->stl; if (!stl->pd) { - stl->pd = MEM_callocN(sizeof(IMAGE_PrivateData), __func__); + stl->pd = static_cast(MEM_callocN(sizeof(IMAGE_PrivateData), __func__)); } IMAGE_PrivateData *pd = stl->pd; - pd->ibuf = NULL; - pd->lock = NULL; - pd->texture = NULL; + pd->ibuf = nullptr; + pd->lock = nullptr; + pd->texture = nullptr; } static void IMAGE_cache_init(void *ved) @@ -352,14 +354,14 @@ static void IMAGE_cache_init(void *ved) { /* Write depth is needed for background overlay rendering. Near depth is used for * transparency checker and Far depth is used for indicating the image size. */ - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | - DRW_STATE_BLEND_ALPHA_PREMUL; + DRWState state = static_cast(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | + DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL); psl->image_pass = DRW_pass_create("Image", state); } const SpaceLink *space_link = draw_ctx->space_data; const char space_type = space_link->spacetype; - pd->view = NULL; + pd->view = nullptr; if (space_type == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; Image *image = ED_space_image(sima); @@ -372,15 +374,15 @@ static void IMAGE_cache_init(void *ved) ARegion *region = draw_ctx->region; Main *bmain = CTX_data_main(draw_ctx->evil_C); Image *image = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &pd->lock); + ImBuf *ibuf = BKE_image_acquire_ibuf(image, nullptr, &pd->lock); { /* Setup a screen pixel view. The backdrop of the node editor doesn't follow the region. */ float winmat[4][4], viewmat[4][4]; orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0); unit_m4(winmat); - pd->view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); + pd->view = DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr); } - image_cache_image(vedata, image, NULL, ibuf); + image_cache_image(vedata, image, nullptr, ibuf); pd->image = image; pd->ibuf = ibuf; } @@ -405,14 +407,14 @@ static void image_draw_finish(IMAGE_Data *ved) else if (space_type == SPACE_NODE) { BKE_image_release_ibuf(pd->image, pd->ibuf, pd->lock); } - pd->image = NULL; - pd->ibuf = NULL; + pd->image = nullptr; + pd->ibuf = nullptr; if (pd->texture && pd->owns_texture) { GPU_texture_free(pd->texture); pd->owns_texture = false; } - pd->texture = NULL; + pd->texture = nullptr; } static void IMAGE_draw_scene(void *ved) @@ -428,11 +430,11 @@ static void IMAGE_draw_scene(void *ved) DRW_view_set_active(pd->view); DRW_draw_pass(psl->image_pass); - DRW_view_set_active(NULL); + DRW_view_set_active(nullptr); image_draw_finish(vedata); } -static void IMAGE_engine_free(void) +static void IMAGE_engine_free() { IMAGE_shader_free(); } @@ -441,19 +443,27 @@ static void IMAGE_engine_free(void) static const DrawEngineDataSize IMAGE_data_size = DRW_VIEWPORT_DATA_SIZE(IMAGE_Data); +} // namespace blender::draw::image_engine + +extern "C" { + +using namespace blender::draw::image_engine; + DrawEngineType draw_engine_image_type = { - NULL, /* next */ - NULL, /* prev */ + nullptr, /* next */ + nullptr, /* prev */ N_("UV/Image"), /* idname */ &IMAGE_data_size, /* vedata_size */ &IMAGE_engine_init, /* engine_init */ &IMAGE_engine_free, /* engine_free */ &IMAGE_cache_init, /* cache_init */ &IMAGE_cache_populate, /* cache_populate */ - NULL, /* cache_finish */ + nullptr, /* cache_finish */ &IMAGE_draw_scene, /* draw_scene */ - NULL, /* view_update */ - NULL, /* id_update */ - NULL, /* render_to_image */ - NULL, /* store_metadata */ + nullptr, /* view_update */ + nullptr, /* id_update */ + nullptr, /* render_to_image */ + nullptr, /* store_metadata */ }; +} + diff --git a/source/blender/draw/engines/image/image_engine.h b/source/blender/draw/engines/image/image_engine.h index 0098d863ef9..ba1e87088f1 100644 --- a/source/blender/draw/engines/image/image_engine.h +++ b/source/blender/draw/engines/image/image_engine.h @@ -22,4 +22,13 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + extern DrawEngineType draw_engine_image_type; + +#ifdef __cplusplus +} +#endif + diff --git a/source/blender/draw/engines/image/image_private.h b/source/blender/draw/engines/image/image_private.hh similarity index 84% rename from source/blender/draw/engines/image/image_private.h rename to source/blender/draw/engines/image/image_private.hh index 76a94e68da1..434e27eff65 100644 --- a/source/blender/draw/engines/image/image_private.h +++ b/source/blender/draw/engines/image/image_private.hh @@ -20,10 +20,6 @@ * \ingroup draw_engine */ -#ifdef __cplusplus -extern "C" { -#endif - /* Forward declarations */ struct GPUTexture; struct ImBuf; @@ -31,13 +27,15 @@ struct Image; /* *********** LISTS *********** */ +namespace blender::draw::image_engine { + /* GPUViewport.storage * Is freed every time the viewport engine changes. */ -typedef struct IMAGE_PassList { +struct IMAGE_PassList { DRWPass *image_pass; -} IMAGE_PassList; +}; -typedef struct IMAGE_PrivateData { +struct IMAGE_PrivateData { void *lock; struct ImBuf *ibuf; struct Image *image; @@ -45,25 +43,24 @@ typedef struct IMAGE_PrivateData { struct GPUTexture *texture; bool owns_texture; -} IMAGE_PrivateData; +}; -typedef struct IMAGE_StorageList { +struct IMAGE_StorageList { IMAGE_PrivateData *pd; -} IMAGE_StorageList; +}; -typedef struct IMAGE_Data { +struct IMAGE_Data { void *engine_type; DRWViewportEmptyList *fbl; DRWViewportEmptyList *txl; IMAGE_PassList *psl; IMAGE_StorageList *stl; -} IMAGE_Data; +}; /* image_shader.c */ GPUShader *IMAGE_shader_image_get(bool is_tiled_image); void IMAGE_shader_library_ensure(void); void IMAGE_shader_free(void); -#ifdef __cplusplus -} -#endif +} // namespace blender::draw::image_engine + diff --git a/source/blender/draw/engines/image/image_shader.c b/source/blender/draw/engines/image/image_shader.cc similarity index 86% rename from source/blender/draw/engines/image/image_shader.c rename to source/blender/draw/engines/image/image_shader.cc index 691c0d7029a..bea263b9234 100644 --- a/source/blender/draw/engines/image/image_shader.c +++ b/source/blender/draw/engines/image/image_shader.cc @@ -27,7 +27,7 @@ #include "GPU_batch.h" #include "image_engine.h" -#include "image_private.h" +#include "image_private.hh" extern char datatoc_common_colormanagement_lib_glsl[]; extern char datatoc_common_globals_lib_glsl[]; @@ -36,18 +36,20 @@ extern char datatoc_common_view_lib_glsl[]; extern char datatoc_engine_image_frag_glsl[]; extern char datatoc_engine_image_vert_glsl[]; -typedef struct IMAGE_Shaders { +namespace blender::draw::image_engine { + +struct IMAGE_Shaders { GPUShader *image_sh[2]; -} IMAGE_Shaders; +}; static struct { IMAGE_Shaders shaders; DRWShaderLibrary *lib; -} e_data = {{{0}}}; /* Engine data */ +} e_data = {{{nullptr}}}; /* Engine data */ void IMAGE_shader_library_ensure(void) { - if (e_data.lib == NULL) { + if (e_data.lib == nullptr) { e_data.lib = DRW_shader_library_create(); /* NOTE: These need to be ordered by dependencies. */ DRW_SHADER_LIB_ADD(e_data.lib, common_colormanagement_lib); @@ -60,13 +62,13 @@ GPUShader *IMAGE_shader_image_get(bool is_tiled_image) { const int index = is_tiled_image ? 1 : 0; IMAGE_Shaders *sh_data = &e_data.shaders; - if (!sh_data->image_sh[index]) { + if (sh_data->image_sh[index] == nullptr) { sh_data->image_sh[index] = DRW_shader_create_with_shaderlib( datatoc_engine_image_vert_glsl, - NULL, + nullptr, datatoc_engine_image_frag_glsl, e_data.lib, - is_tiled_image ? "#define TILED_IMAGE\n" : NULL); + is_tiled_image ? "#define TILED_IMAGE\n" : nullptr); } return sh_data->image_sh[index]; } @@ -80,3 +82,6 @@ void IMAGE_shader_free(void) DRW_SHADER_LIB_FREE_SAFE(e_data.lib); } + +} // namespace blender::draw::image_engine + diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index a9810b4cc77..1abd056502d 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -15,13 +15,15 @@ #include "engines/eevee/eevee_private.h" #include "engines/gpencil/gpencil_engine.h" -#include "engines/image/image_private.h" +#include "engines/image/image_private.hh" #include "engines/overlay/overlay_private.h" #include "engines/workbench/workbench_private.h" #include "intern/draw_shader.h" namespace blender::draw { +using namespace blender::draw::image_engine; + static void test_workbench_glsl_shaders() { workbench_shader_library_ensure(); From 69a7734b75c3044b760061a1141ba6dc95e419a6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 2 Nov 2021 19:35:17 +1100 Subject: [PATCH 1447/1500] UI: always show the cursor while transforming the cursor --- source/blender/blenkernel/BKE_global.h | 6 ++++++ source/blender/draw/intern/draw_view.c | 5 +++++ .../editors/space_sequencer/space_sequencer.c | 16 ++++++++++++++-- .../editors/transform/transform_convert.c | 3 +++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 7696b5c0189..bacdd4adc76 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -211,6 +211,12 @@ enum { G_TRANSFORM_SEQ = (1 << 2), G_TRANSFORM_FCURVES = (1 << 3), G_TRANSFORM_WM = (1 << 4), + /** + * Set when transforming the cursor it's self. + * Used as a hint to draw the cursor (even when hidden). + * Otherwise it's not possible to see whats being transformed. + */ + G_TRANSFORM_CURSOR = (1 << 5), }; /** Defined in blender.c */ diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c index bbd345271b1..de43d2aba0f 100644 --- a/source/blender/draw/intern/draw_view.c +++ b/source/blender/draw/intern/draw_view.c @@ -40,6 +40,7 @@ #include "WM_types.h" +#include "BKE_global.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -63,6 +64,10 @@ void DRW_draw_region_info(void) static bool is_cursor_visible(const DRWContextState *draw_ctx, Scene *scene, ViewLayer *view_layer) { + if (G.moving & G_TRANSFORM_CURSOR) { + return true; + } + View3D *v3d = draw_ctx->v3d; if ((v3d->flag2 & V3D_HIDE_OVERLAYS) || (v3d->overlay.flag & V3D_OVERLAY_HIDE_CURSOR)) { return false; diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 7b763ac8540..ed8305cd311 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -775,6 +775,19 @@ static void sequencer_preview_region_view2d_changed(const bContext *C, ARegion * sseq->flag &= ~SEQ_ZOOM_TO_FIT; } +static bool is_cursor_visible(const SpaceSeq *sseq) +{ + if (G.moving & G_TRANSFORM_CURSOR) { + return true; + } + + if ((sseq->flag & SEQ_SHOW_OVERLAY) && + (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_2D_CURSOR) != 0) { + return true; + } + return false; +} + static void sequencer_preview_region_draw(const bContext *C, ARegion *region) { ScrArea *area = CTX_wm_area(C); @@ -809,8 +822,7 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) } /* No need to show the cursor for scopes. */ - if (draw_overlay && (is_playing == false) && (sseq->mainb == SEQ_DRAW_IMG_IMBUF) && - (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_2D_CURSOR) != 0) { + if ((is_playing == false) && (sseq->mainb == SEQ_DRAW_IMG_IMBUF) && is_cursor_visible(sseq)) { GPU_color_mask(true, true, true, true); GPU_depth_mask(false); GPU_depth_test(GPU_DEPTH_NONE); diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 781bf221dd2..ff20f569a71 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -965,6 +965,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t) int special_transform_moving(TransInfo *t) { + if (t->options & CTX_CURSOR) { + return G_TRANSFORM_CURSOR; + } if (t->spacetype == SPACE_SEQ) { return G_TRANSFORM_SEQ; } From a5b996df88c586d77d083cbbbd2299dc84cb1584 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 1 Nov 2021 11:03:58 +0100 Subject: [PATCH 1448/1500] Fix T92608: Image Editor does not display stereo images Caused by own {rB5aa3167e48b2}. Related commit: {rBebaa3fcedd23}. For stereo renders, `BKE_image_is_multilayer` is true, however we seem to get to down to `space_image_gpu_texture_get` [where this is called] from `IMAGE_cache_init` with a NULL Image->RenderResult. So what then happens is that `BKE_image_multilayer_index` is called and even though it has an appropriate codepath for stereo, it earlies out and does not set multi_index correctly. Still a bit puzzled why RenderResult is NULL for a render, but since other places also check for a valid RenderResult before going down the _multilayer_ route (and doing _multiview_ instead), now do the same thing, BKE_image_multiview_index is now called in these cases (and seems to behave correctly, checked with layers and passes and all seems to display correctly, either in stereo or choosing individual eyes). thx @jbakker & @brecht for double-checking. Maniphest Tasks: T92608 Differential Revision: https://developer.blender.org/D13063 --- source/blender/draw/engines/image/image_engine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.c index 662238df270..8b4acfbd5e3 100644 --- a/source/blender/draw/engines/image/image_engine.c +++ b/source/blender/draw/engines/image/image_engine.c @@ -107,7 +107,7 @@ static void space_image_gpu_texture_get(Image *image, { const DRWContextState *draw_ctx = DRW_context_state_get(); SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; - if (BKE_image_is_multilayer(image)) { + if (image->rr != NULL) { /* Update multi-index and pass for the current eye. */ BKE_image_multilayer_index(image->rr, &sima->iuser); } From 8ca6e51adea3d99819711e91998244d122f03b56 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 2 Nov 2021 19:50:53 +1100 Subject: [PATCH 1449/1500] Cleanup: clang-tidy --- source/blender/draw/engines/image/image_shader.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/image/image_shader.cc b/source/blender/draw/engines/image/image_shader.cc index bea263b9234..f5128fa00c7 100644 --- a/source/blender/draw/engines/image/image_shader.cc +++ b/source/blender/draw/engines/image/image_shader.cc @@ -47,7 +47,7 @@ static struct { DRWShaderLibrary *lib; } e_data = {{{nullptr}}}; /* Engine data */ -void IMAGE_shader_library_ensure(void) +void IMAGE_shader_library_ensure() { if (e_data.lib == nullptr) { e_data.lib = DRW_shader_library_create(); @@ -73,7 +73,7 @@ GPUShader *IMAGE_shader_image_get(bool is_tiled_image) return sh_data->image_sh[index]; } -void IMAGE_shader_free(void) +void IMAGE_shader_free() { GPUShader **sh_data_as_array = (GPUShader **)&e_data.shaders; for (int i = 0; i < (sizeof(IMAGE_Shaders) / sizeof(GPUShader *)); i++) { From 2b3becf2be27286d68a49328a13c327d2fa94aeb Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Tue, 2 Nov 2021 11:07:06 +0100 Subject: [PATCH 1450/1500] Fix typo in Cycles PMJ enum define. Reported by Raimund58 in the chat, thanks! --- intern/cycles/blender/addon/properties.py | 4 ++-- intern/cycles/blender/addon/version_update.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index c92e0ec65af..3c90ea07cc6 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -87,7 +87,7 @@ enum_use_layer_samples = ( enum_sampling_pattern = ( ('SOBOL', "Sobol", "Use Sobol random sampling pattern", 0), - ('PROGRESSIVE_MUTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern", 1), + ('PROGRESSIVE_MULTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern", 1), ) enum_volume_sampling = ( @@ -339,7 +339,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): name="Sampling Pattern", description="Random sampling pattern used by the integrator. When adaptive sampling is enabled, Progressive Multi-Jitter is always used instead of Sobol", items=enum_sampling_pattern, - default='PROGRESSIVE_MUTI_JITTER', + default='PROGRESSIVE_MULTI_JITTER', ) scrambling_distance: FloatProperty( diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py index 76bc765ec1d..dab62713525 100644 --- a/intern/cycles/blender/addon/version_update.py +++ b/intern/cycles/blender/addon/version_update.py @@ -243,7 +243,7 @@ def do_versions(self): cscene.use_preview_denoising = False if not cscene.is_property_set("sampling_pattern") or \ cscene.get('sampling_pattern') >= 2: - cscene.sampling_pattern = 'PROGRESSIVE_MUTI_JITTER' + cscene.sampling_pattern = 'PROGRESSIVE_MULTI_JITTER' # Removal of square samples. cscene = scene.cycles From 0c3b215e7d5456878b155d13440864f49ad1f230 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 2 Nov 2021 11:15:05 +0100 Subject: [PATCH 1451/1500] Images: refactor how failed image load attempts are remembered Previously, `ImageTile->ok` and `ImageUser->ok` were used to indicate whether an image failed to load. There were three possible values which (probably) had the following meanings: * `0`: There was an error while loading the image. Don't try to load again. * `1`: Default value. Try to load the image. * `2`: The image was loaded successfully. This image-wide flag did not make sense unfortunately, because loading may work for some frames of an image sequence but not for others. Remember than an image data block can also contain a movie. The purpose of the `->ok` flag was to serve as an optimization to avoid trying to load a file over and over again when there is an error (e.g. the file does not exist or is invalid). To get the optimization back, the patch is changing `MovieCache` so that it can also cache failed load attempts. As a consequence, `ibuf` is allowed to be `NULL` in a few more places. I added the appropriate null checks. This also solves issues when image sequences are used with the Image Texture node in Geometry nodes (also see D12827). Differential Revision: https://developer.blender.org/D12957 --- source/blender/blenkernel/BKE_image.h | 4 - source/blender/blenkernel/intern/camera.c | 2 - source/blender/blenkernel/intern/image.c | 267 ++++-------------- source/blender/blenkernel/intern/image_gpu.c | 7 +- source/blender/blenkernel/intern/movieclip.c | 2 +- source/blender/blenkernel/intern/node.cc | 4 - source/blender/blenkernel/intern/object.c | 1 - source/blender/blenkernel/intern/screen.c | 1 - source/blender/blenkernel/intern/texture.c | 1 - .../blenloader/intern/versioning_260.c | 1 - .../blenloader/intern/versioning_280.c | 1 - .../blenloader/intern/versioning_legacy.c | 2 - .../operations/COM_ViewerOperation.cc | 2 - .../draw/engines/gpencil/gpencil_draw_data.c | 1 - .../blender/editors/object/object_bake_api.c | 7 +- .../blender/editors/render/render_internal.c | 1 - source/blender/editors/render/render_opengl.c | 1 - .../blender/editors/render/render_preview.c | 8 +- .../editors/sculpt_paint/paint_image_2d.c | 3 - .../editors/space_image/image_buttons.c | 4 - .../blender/editors/space_image/image_ops.c | 4 +- .../blender/editors/space_image/image_undo.c | 1 - source/blender/imbuf/IMB_moviecache.h | 2 +- source/blender/imbuf/intern/colormanagement.c | 2 +- source/blender/imbuf/intern/moviecache.c | 27 +- source/blender/makesdna/DNA_image_types.h | 9 +- .../blender/makesrna/intern/rna_image_api.c | 1 - .../composite/nodes/node_composite_image.cc | 2 - .../nodes/node_composite_splitViewer.cc | 1 - .../composite/nodes/node_composite_viewer.cc | 1 - .../nodes/texture/nodes/node_texture_image.c | 1 - 31 files changed, 97 insertions(+), 274 deletions(-) diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 82d1f299b4b..7a8c55071df 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -153,10 +153,6 @@ struct RenderData; struct RenderPass; struct RenderResult; -/* ima->ok */ -#define IMA_OK 1 -#define IMA_OK_LOADED 2 - /* signals */ /* reload only frees, doesn't read until image_get_ibuf() called */ #define IMA_SIGNAL_RELOAD 0 diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 9455eed7f3f..d355de73170 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -140,7 +140,6 @@ static void camera_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_list(reader, &ca->bg_images); LISTBASE_FOREACH (CameraBGImage *, bgpic, &ca->bg_images) { - bgpic->iuser.ok = 1; bgpic->iuser.scene = NULL; } } @@ -1128,7 +1127,6 @@ CameraBGImage *BKE_camera_background_image_new(Camera *cam) bgpic->scale = 1.0f; bgpic->alpha = 0.5f; - bgpic->iuser.ok = 1; bgpic->iuser.flag |= IMA_ANIM_ALWAYS; bgpic->flag |= CAM_BGIMG_FLAG_EXPANDED; diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 3800cbec94b..c9ac1f32804 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -321,9 +321,7 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &ima->preview); BKE_previewimg_blend_read(reader, ima->preview); BLO_read_data_address(reader, &ima->stereo3d_format); - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - tile->ok = IMA_OK; - } + ima->lastused = 0; ima->gpuflag = 0; BLI_listbase_clear(&ima->gpu_refresh_areas); @@ -443,12 +441,12 @@ static void imagecache_remove(Image *image, int index) IMB_moviecache_remove(image->cache, &key); } -static struct ImBuf *imagecache_get(Image *image, int index) +static struct ImBuf *imagecache_get(Image *image, int index, bool *r_is_cached_empty) { if (image->cache) { ImageCacheKey key; key.index = index; - return IMB_moviecache_get(image->cache, &key); + return IMB_moviecache_get(image->cache, &key, r_is_cached_empty); } return NULL; @@ -529,10 +527,6 @@ void BKE_image_free_buffers_ex(Image *ima, bool do_lock) BKE_image_free_gputextures(ima); - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - tile->ok = IMA_OK; - } - if (do_lock) { BLI_mutex_unlock(image_mutex); } @@ -564,7 +558,6 @@ static void image_init(Image *ima, short source, short type) } ImageTile *tile = MEM_callocN(sizeof(ImageTile), "Image Tiles"); - tile->ok = IMA_OK; tile->tile_number = 1001; BLI_addtail(&ima->tiles, tile); @@ -597,25 +590,25 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ * call IMB_freeImBuf to de-reference the image buffer after * it's done handling it. */ -static ImBuf *image_get_cached_ibuf_for_index_entry(Image *ima, int index, int entry) +static ImBuf *image_get_cached_ibuf_for_index_entry(Image *ima, + int index, + int entry, + bool *r_is_cached_empty) { if (index != IMA_NO_INDEX) { index = IMA_MAKE_INDEX(entry, index); } - return imagecache_get(ima, index); + return imagecache_get(ima, index, r_is_cached_empty); } -/* no ima->ibuf anymore, but listbase */ static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int entry) { - if (ibuf) { - if (index != IMA_NO_INDEX) { - index = IMA_MAKE_INDEX(entry, index); - } - - imagecache_put(ima, index, ibuf); + if (index != IMA_NO_INDEX) { + index = IMA_MAKE_INDEX(entry, index); } + + imagecache_put(ima, index, ibuf); } static void image_remove_ibuf(Image *ima, int index, int entry) @@ -888,11 +881,6 @@ Image *BKE_image_load_exists_ex(Main *bmain, const char *filepath, bool *r_exist if (BLI_path_cmp(strtest, str) == 0) { if ((BKE_image_has_anim(ima) == false) || (ima->id.us == 0)) { id_us_plus(&ima->id); /* officially should not, it doesn't link here! */ - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - if (tile->ok == 0) { - tile->ok = IMA_OK; - } - } if (r_exists) { *r_exists = true; } @@ -1080,9 +1068,6 @@ Image *BKE_image_add_generated(Main *bmain, image_add_view(ima, names[view_id], ""); } - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; - return ima; } @@ -1105,8 +1090,6 @@ Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) if (ima) { STRNCPY(ima->filepath, ibuf->name); image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; } return ima; @@ -1157,7 +1140,7 @@ bool BKE_image_memorypack(Image *ima) int i; for (i = 0, iv = ima->views.first; iv; iv = iv->next, i++) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0, NULL); if (!ibuf) { ok = false; @@ -1177,7 +1160,7 @@ bool BKE_image_memorypack(Image *ima) ima->views_format = R_IMF_VIEWS_INDIVIDUAL; } else { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, NULL); if (ibuf) { ok = ok && image_memorypack_imbuf(ima, ibuf, ibuf->name); @@ -1266,6 +1249,10 @@ static uintptr_t image_mem_size(Image *image) while (!IMB_moviecacheIter_done(iter)) { ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); + IMB_moviecacheIter_step(iter); + if (ibuf == NULL) { + continue; + } ImBuf *ibufm; int level; @@ -1287,8 +1274,6 @@ static uintptr_t image_mem_size(Image *image) } } } - - IMB_moviecacheIter_step(iter); } IMB_moviecacheIter_free(iter); } @@ -3487,7 +3472,6 @@ static void image_tag_frame_recalc(Image *ima, ID *iuser_id, ImageUser *iuser, v if (ima == changed_image && BKE_image_is_animated(ima)) { iuser->flag |= IMA_NEED_FRAME_RECALC; - iuser->ok = 1; if (iuser_id) { /* Must copy image user changes to CoW datablock. */ @@ -3501,7 +3485,6 @@ static void image_tag_reload(Image *ima, ID *iuser_id, ImageUser *iuser, void *c Image *changed_image = customdata; if (ima == changed_image) { - iuser->ok = 1; if (iuser->scene) { image_update_views_format(ima, iuser); } @@ -3515,7 +3498,6 @@ static void image_tag_reload(Image *ima, ID *iuser_id, ImageUser *iuser, void *c void BKE_imageuser_default(ImageUser *iuser) { memset(iuser, 0, sizeof(ImageUser)); - iuser->ok = 1; iuser->frames = 100; iuser->sfra = 1; } @@ -3575,7 +3557,6 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) BKE_image_free_buffers(ima); if (iuser) { - iuser->ok = 1; if (iuser->scene) { image_update_views_format(ima, iuser); } @@ -3590,7 +3571,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) if (ima->source == IMA_SRC_GENERATED) { if (ima->gen_x == 0 || ima->gen_y == 0) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, NULL); if (ibuf) { ima->gen_x = ibuf->x; ima->gen_y = ibuf->y; @@ -3630,10 +3611,6 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) */ BKE_image_free_buffers(ima); - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - tile->ok = 1; - } - if (iuser) { image_tag_frame_recalc(ima, NULL, iuser, ima); } @@ -3681,7 +3658,6 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) break; case IMA_SIGNAL_USER_NEW_IMAGE: if (iuser) { - iuser->ok = 1; if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { if (ima->type == IMA_TYPE_MULTILAYER) { BKE_image_init_imageuser(ima, iuser); @@ -3691,15 +3667,6 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) break; case IMA_SIGNAL_COLORMANAGE: BKE_image_free_buffers(ima); - - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - tile->ok = 1; - } - - if (iuser) { - iuser->ok = 1; - } - break; } @@ -3799,7 +3766,6 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } ImageTile *tile = MEM_callocN(sizeof(ImageTile), "image new tile"); - tile->ok = IMA_OK; tile->tile_number = tile_number; if (next_tile) { @@ -3864,14 +3830,14 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu if (BKE_image_is_multiview(ima)) { const int totviews = BLI_listbase_count(&ima->views); for (int i = 0; i < totviews; i++) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, old_tile_number); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, old_tile_number, NULL); image_remove_ibuf(ima, i, old_tile_number); image_assign_ibuf(ima, ibuf, i, new_tile_number); IMB_freeImBuf(ibuf); } } else { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, 0, old_tile_number); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, 0, old_tile_number, NULL); image_remove_ibuf(ima, 0, old_tile_number); image_assign_ibuf(ima, ibuf, 0, new_tile_number); IMB_freeImBuf(ibuf); @@ -3930,7 +3896,6 @@ bool BKE_image_fill_tile(struct Image *ima, if (tile_ibuf != NULL) { image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number); BKE_image_release_ibuf(ima, tile_ibuf, NULL); - tile->ok = IMA_OK; return true; } return false; @@ -4208,9 +4173,7 @@ static void image_init_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ib /* Images should never get loaded if the corresponding tile does not exist, * but we should at least not crash if it happens due to a bug elsewhere. */ BLI_assert(tile != NULL); - if (tile != NULL) { - tile->ok = IMA_OK_LOADED; - } + UNUSED_VARS_NDEBUG(tile); } static int imbuf_alpha_flags_for_image(Image *ima) @@ -4245,8 +4208,7 @@ static int image_num_files(Image *ima) return BLI_listbase_count(&ima->views); } -static ImBuf *load_sequence_single( - Image *ima, ImageUser *iuser, int frame, const int view_id, bool *r_assign) +static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, const int view_id) { struct ImBuf *ibuf; char name[FILE_MAX]; @@ -4296,19 +4258,11 @@ static ImBuf *load_sequence_single( } else { image_init_after_load(ima, iuser, ibuf); - *r_assign = true; } #else image_init_after_load(ima, iuser, ibuf); - *r_assign = true; #endif } - else { - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - if (tile != NULL) { - tile->ok = 0; - } - } return ibuf; } @@ -4318,13 +4272,10 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, struct ImBuf *ibuf = NULL; const bool is_multiview = BKE_image_is_multiview(ima); const int totfiles = image_num_files(ima); - bool assign = false; if (!is_multiview) { - ibuf = load_sequence_single(ima, iuser, frame, 0, &assign); - if (assign) { - image_assign_ibuf(ima, ibuf, 0, entry); - } + ibuf = load_sequence_single(ima, iuser, frame, 0); + image_assign_ibuf(ima, ibuf, 0, entry); } else { const int totviews = BLI_listbase_count(&ima->views); @@ -4333,7 +4284,7 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); for (int i = 0; i < totfiles; i++) { - ibuf_arr[i] = load_sequence_single(ima, iuser, frame, i, &assign); + ibuf_arr[i] = load_sequence_single(ima, iuser, frame, i); } if (BKE_image_is_stereo(ima) && ima->views_format == R_IMF_VIEWS_STEREO_3D) { @@ -4343,10 +4294,8 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, /* return the original requested ImBuf */ ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)]; - if (assign) { - for (int i = 0; i < totviews; i++) { - image_assign_ibuf(ima, ibuf_arr[i], i, entry); - } + for (int i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, entry); } /* "remove" the others (decrease their refcount) */ @@ -4366,7 +4315,6 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int entry, int frame) { struct ImBuf *ibuf = NULL; - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); /* either we load from RenderResult, or we have to load a new one */ @@ -4408,13 +4356,6 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int e } // else printf("pass not found\n"); } - else { - tile->ok = 0; - } - - if (iuser) { - iuser->ok = tile->ok; - } return ibuf; } @@ -4426,8 +4367,6 @@ static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const i ia = BLI_findlink(&ima->anims, view_id); - ImageTile *tile = BKE_image_get_tile(ima, 0); - if (ia->anim == NULL) { char str[FILE_MAX]; int flags = IB_rect; @@ -4469,12 +4408,6 @@ static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const i if (ibuf) { image_init_after_load(ima, iuser, ibuf); } - else { - tile->ok = 0; - } - } - else { - tile->ok = 0; } return ibuf; @@ -4485,7 +4418,6 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) struct ImBuf *ibuf = NULL; const bool is_multiview = BKE_image_is_multiview(ima); const int totfiles = image_num_files(ima); - ImageTile *tile = BKE_image_get_tile(ima, 0); if (totfiles != BLI_listbase_count_at_most(&ima->anims, totfiles + 1)) { image_free_anims(ima); @@ -4516,12 +4448,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) } for (int i = 0; i < totviews; i++) { - if (ibuf_arr[i]) { - image_assign_ibuf(ima, ibuf_arr[i], i, frame); - } - else { - tile->ok = 0; - } + image_assign_ibuf(ima, ibuf_arr[i], i, frame); } /* return the original requested ImBuf */ @@ -4538,19 +4465,11 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) MEM_freeN(ibuf_arr); } - if (iuser) { - iuser->ok = tile->ok; - } - return ibuf; } -static ImBuf *load_image_single(Image *ima, - ImageUser *iuser, - int cfra, - const int view_id, - const bool has_packed, - bool *r_assign) +static ImBuf *load_image_single( + Image *ima, ImageUser *iuser, int cfra, const int view_id, const bool has_packed) { char filepath[FILE_MAX]; struct ImBuf *ibuf = NULL; @@ -4612,7 +4531,6 @@ static ImBuf *load_image_single(Image *ima, #endif { image_init_after_load(ima, iuser, ibuf); - *r_assign = true; /* Make packed file for auto-pack. */ if ((has_packed == false) && (G.fileflags & G_FILE_AUTOPACK)) { @@ -4625,10 +4543,6 @@ static ImBuf *load_image_single(Image *ima, } } } - else { - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - tile->ok = 0; - } return ibuf; } @@ -4639,7 +4553,6 @@ static ImBuf *load_image_single(Image *ima, static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) { struct ImBuf *ibuf = NULL; - bool assign = false; const bool is_multiview = BKE_image_is_multiview(ima); const int totfiles = image_num_files(ima); bool has_packed = BKE_image_has_packedfile(ima); @@ -4656,10 +4569,8 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) } if (!is_multiview) { - ibuf = load_image_single(ima, iuser, cfra, 0, has_packed, &assign); - if (assign) { - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); - } + ibuf = load_image_single(ima, iuser, cfra, 0, has_packed); + image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); } else { struct ImBuf **ibuf_arr; @@ -4669,7 +4580,7 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); for (int i = 0; i < totfiles; i++) { - ibuf_arr[i] = load_image_single(ima, iuser, cfra, i, has_packed, &assign); + ibuf_arr[i] = load_image_single(ima, iuser, cfra, i, has_packed); } /* multi-views/multi-layers OpenEXR files directly populate ima, and return NULL ibuf... */ @@ -4682,10 +4593,8 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) int i = (iuser && iuser->multi_index < totviews) ? iuser->multi_index : 0; ibuf = ibuf_arr[i]; - if (assign) { - for (i = 0; i < totviews; i++) { - image_assign_ibuf(ima, ibuf_arr[i], i, 0); - } + for (i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, 0); } /* "remove" the others (decrease their refcount) */ @@ -4699,11 +4608,6 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) MEM_freeN(ibuf_arr); } - if (iuser) { - ImageTile *tile = BKE_image_get_tile(ima, 0); - iuser->ok = tile->ok; - } - return ibuf; } @@ -4736,14 +4640,6 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) } } - ImageTile *tile = BKE_image_get_tile(ima, 0); - if (ibuf == NULL) { - tile->ok = 0; - } - if (iuser) { - iuser->ok = tile->ok; - } - return ibuf; } @@ -4861,7 +4757,7 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc } } - ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, NULL); /* make ibuf if needed, and initialize it */ if (ibuf == NULL) { @@ -4938,9 +4834,6 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc ibuf->dither = dither; - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; - return ibuf; } @@ -4997,7 +4890,8 @@ static void image_get_entry_and_index(Image *ima, ImageUser *iuser, int *r_entry * call IMB_freeImBuf to de-reference the image buffer after * it's done handling it. */ -static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, int *r_index) +static ImBuf *image_get_cached_ibuf( + Image *ima, ImageUser *iuser, int *r_entry, int *r_index, bool *r_is_cached_empty) { ImBuf *ibuf = NULL; int entry = 0, index = image_get_multiview_index(ima, iuser); @@ -5005,42 +4899,30 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { entry = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); ima->lastframe = entry; } else if (ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_IMAGE) { entry = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); ima->lastframe = entry; - - /* counter the fact that image is set as invalid when loading a frame - * that is not in the cache (through image_acquire_ibuf for instance), - * yet we have valid frames in the cache loaded */ - if (ibuf) { - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; - - if (iuser) { - iuser->ok = tile->ok; - } - } } else if (ima->type == IMA_TYPE_MULTILAYER) { entry = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); } } else if (ima->source == IMA_SRC_FILE) { if (ima->type == IMA_TYPE_IMAGE) { - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); } else if (ima->type == IMA_TYPE_MULTILAYER) { - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); } } else if (ima->source == IMA_SRC_GENERATED) { - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); } else if (ima->source == IMA_SRC_VIEWER) { /* always verify entirely, not that this shouldn't happen @@ -5050,17 +4932,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, else if (ima->source == IMA_SRC_TILED) { if (ELEM(ima->type, IMA_TYPE_IMAGE, IMA_TYPE_MULTILAYER)) { entry = image_get_tile_number_from_iuser(ima, iuser); - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); - - if ((ima->type == IMA_TYPE_IMAGE) && ibuf != NULL) { - ImageTile *tile = BKE_image_get_tile(ima, entry); - tile->ok = IMA_OK_LOADED; - - /* iuser->ok is useless for tiled images because iuser->tile changes all the time. */ - if (iuser != NULL) { - iuser->ok = 1; - } - } + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); } } @@ -5081,19 +4953,10 @@ BLI_INLINE bool image_quick_test(Image *ima, const ImageUser *iuser) return false; } - if (iuser) { - if (iuser->ok == 0) { - return false; - } - } - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); if (tile == NULL) { return false; } - if (tile->ok == 0) { - return false; - } return true; } @@ -5116,7 +4979,11 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) return NULL; } - ibuf = image_get_cached_ibuf(ima, iuser, &entry, &index); + bool is_cached_empty = false; + ibuf = image_get_cached_ibuf(ima, iuser, &entry, &index, &is_cached_empty); + if (is_cached_empty) { + return NULL; + } if (ibuf == NULL) { /* we are sure we have to load the ibuf, using source and type */ @@ -5178,8 +5045,6 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) ima->gen_color, &ima->colorspace_settings); image_assign_ibuf(ima, ibuf, index, 0); - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; } else if (ima->source == IMA_SRC_VIEWER) { if (ima->type == IMA_TYPE_R_RESULT) { @@ -5196,7 +5061,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) /* XXX anim play for viewer nodes not yet supported */ entry = 0; // XXX iuser ? iuser->framenr : 0; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, NULL); if (!ibuf) { /* Composite Viewer, all handled in compositor */ @@ -5271,7 +5136,7 @@ bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser) BLI_mutex_lock(image_mutex); - ibuf = image_get_cached_ibuf(ima, iuser, NULL, NULL); + ibuf = image_get_cached_ibuf(ima, iuser, NULL, NULL, NULL); if (!ibuf) { ibuf = image_acquire_ibuf(ima, iuser, NULL); @@ -5492,18 +5357,6 @@ void BKE_image_user_frame_calc(Image *ima, ImageUser *iuser, int cfra) ima->gpuframenr = iuser->framenr; } - if (iuser->ok == 0) { - iuser->ok = 1; - } - - if (ima) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - if (tile->ok == 0) { - tile->ok = IMA_OK; - } - } - } - iuser->flag &= ~IMA_NEED_FRAME_RECALC; } } @@ -5789,7 +5642,7 @@ bool BKE_image_is_dirty_writable(Image *image, bool *r_is_writable) while (!IMB_moviecacheIter_done(iter)) { ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); - if (ibuf->userflags & IB_BITMAPDIRTY) { + if (ibuf != NULL && ibuf->userflags & IB_BITMAPDIRTY) { is_writable = BKE_image_buffer_format_writable(ibuf); is_dirty = true; break; @@ -5833,8 +5686,10 @@ void BKE_image_file_format_set(Image *image, int ftype, const ImbFormatOptions * while (!IMB_moviecacheIter_done(iter)) { ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); - ibuf->ftype = ftype; - ibuf->foptions = *options; + if (ibuf != NULL) { + ibuf->ftype = ftype; + ibuf->foptions = *options; + } IMB_moviecacheIter_step(iter); } IMB_moviecacheIter_free(iter); @@ -5875,7 +5730,7 @@ ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name) while (!IMB_moviecacheIter_done(iter)) { ImBuf *current_ibuf = IMB_moviecacheIter_getImBuf(iter); - if (STREQ(current_ibuf->name, name)) { + if (current_ibuf != NULL && STREQ(current_ibuf->name, name)) { ibuf = current_ibuf; IMB_refImBuf(ibuf); break; @@ -5908,7 +5763,9 @@ ImBuf *BKE_image_get_first_ibuf(Image *image) while (!IMB_moviecacheIter_done(iter)) { ibuf = IMB_moviecacheIter_getImBuf(iter); - IMB_refImBuf(ibuf); + if (ibuf != NULL) { + IMB_refImBuf(ibuf); + } break; } IMB_moviecacheIter_free(iter); diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c index 9712e912bed..330af1cc505 100644 --- a/source/blender/blenkernel/intern/image_gpu.c +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -373,15 +373,14 @@ static GPUTexture *image_get_gpu_texture(Image *ima, /* Check if image has been updated and tagged to be updated (full or partial). */ ImageTile *tile = BKE_image_get_tile(ima, 0); if (((ima->gpuflag & IMA_GPU_REFRESH) != 0) || - ((ibuf == NULL || tile == NULL || !tile->ok) && - ((ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) != 0))) { + ((ibuf == NULL || tile == NULL) && ((ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) != 0))) { image_free_gpu(ima, true); BLI_freelistN(&ima->gpu_refresh_areas); ima->gpuflag &= ~(IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH); } else if (ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) { BLI_assert(ibuf); - BLI_assert(tile && tile->ok); + BLI_assert(tile); ImagePartialRefresh *refresh_area; while ((refresh_area = BLI_pophead(&ima->gpu_refresh_areas))) { const int tile_offset_x = refresh_area->tile_x * IMA_PARTIAL_REFRESH_TILE_SIZE; @@ -417,7 +416,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, /* Check if we have a valid image. If not, we return a dummy * texture with zero bind-code so we don't keep trying. */ - if (tile == NULL || tile->ok == 0) { + if (tile == NULL) { *tex = image_gpu_texture_error_create(textarget); return *tex; } diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 002f370cdcb..f4db81fffc5 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -842,7 +842,7 @@ static ImBuf *get_imbuf_cache(MovieClip *clip, const MovieClipUser *user, int fl key.render_flag = 0; } - return IMB_moviecache_get(clip->cache->moviecache, &key); + return IMB_moviecache_get(clip->cache->moviecache, &key, NULL); } return NULL; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3297bf29ee4..ae9cddacc85 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -748,13 +748,11 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) } case SH_NODE_TEX_IMAGE: { NodeTexImage *tex = (NodeTexImage *)node->storage; - tex->iuser.ok = 1; tex->iuser.scene = nullptr; break; } case SH_NODE_TEX_ENVIRONMENT: { NodeTexEnvironment *tex = (NodeTexEnvironment *)node->storage; - tex->iuser.ok = 1; tex->iuser.scene = nullptr; break; } @@ -763,7 +761,6 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) case CMP_NODE_VIEWER: case CMP_NODE_SPLITVIEWER: { ImageUser *iuser = (ImageUser *)node->storage; - iuser->ok = 1; iuser->scene = nullptr; break; } @@ -777,7 +774,6 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) } case TEX_NODE_IMAGE: { ImageUser *iuser = (ImageUser *)node->storage; - iuser->ok = 1; iuser->scene = nullptr; break; } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index a55bb32f927..933e83d4f7c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -4150,7 +4150,6 @@ void BKE_object_empty_draw_type_set(Object *ob, const int value) if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { if (!ob->iuser) { ob->iuser = MEM_callocN(sizeof(ImageUser), "image user"); - ob->iuser->ok = 1; ob->iuser->flag |= IMA_ANIM_ALWAYS; ob->iuser->frames = 100; ob->iuser->sfra = 1; diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 3e1a2a9bbe9..233a4f344b5 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -1624,7 +1624,6 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) SpaceImage *sima = (SpaceImage *)sl; sima->iuser.scene = NULL; - sima->iuser.ok = 1; sima->scopes.waveform_1 = NULL; sima->scopes.waveform_2 = NULL; sima->scopes.waveform_3 = NULL; diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 74e8db039f2..c878abf0dff 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -184,7 +184,6 @@ static void texture_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &tex->preview); BKE_previewimg_blend_read(reader, tex->preview); - tex->iuser.ok = 1; tex->iuser.scene = NULL; } diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 55252210a78..c4d04392cba 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -1384,7 +1384,6 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) tex->iuser.frames = 1; tex->iuser.sfra = 1; - tex->iuser.ok = 1; } } } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index e809d580fd2..4333bdb851c 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -4517,7 +4517,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!DNA_struct_elem_find(fd->filesdna, "Image", "ListBase", "tiles")) { for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { ImageTile *tile = MEM_callocN(sizeof(ImageTile), "Image Tile"); - tile->ok = 1; tile->tile_number = 1001; BLI_addtail(&ima->tiles, tile); } diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 62cc2aa3662..37bf4898cb3 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -391,7 +391,6 @@ static void do_version_ntree_242_2(bNodeTree *ntree) iuser->sfra = nia->sfra; iuser->offset = nia->nr - 1; iuser->cycl = nia->cyclic; - iuser->ok = 1; node->storage = iuser; MEM_freeN(nia); @@ -399,7 +398,6 @@ static void do_version_ntree_242_2(bNodeTree *ntree) else { ImageUser *iuser = node->storage = MEM_callocN(sizeof(ImageUser), "node image user"); iuser->sfra = 1; - iuser->ok = 1; } } } diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index be8fe1416d0..205596b46d1 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -173,8 +173,6 @@ void ViewerOperation::init_image() if (ibuf->x > 0 && ibuf->y > 0) { imb_addrectfloatImBuf(ibuf); } - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; } diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index e3e84dd4c8c..4636b9e949a 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -59,7 +59,6 @@ static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_ struct GPUTexture *gpu_tex = NULL; void *lock; - iuser.ok = true; ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); if (ibuf != NULL && ibuf->rect != NULL) { diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 98f85823bb3..0de34e21462 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -314,11 +314,8 @@ static void bake_targets_refresh(BakeTargets *targets) if (ima) { LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - if (tile->ok == IMA_OK_LOADED) { - BKE_image_free_gputextures(ima); - DEG_id_tag_update(&ima->id, 0); - break; - } + BKE_image_free_gputextures(ima); + DEG_id_tag_update(&ima->id, 0); } } } diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 2ff47edabf4..49e7ebf6340 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -976,7 +976,6 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even rj->anim = is_animation; rj->write_still = is_write_still && !is_animation; rj->iuser.scene = scene; - rj->iuser.ok = 1; rj->reports = op->reports; rj->orig_layer = 0; rj->last_layer = 0; diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 4c539bdea90..1e1a95f2965 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -836,7 +836,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) BKE_image_backup_render(oglrender->scene, oglrender->ima, true); oglrender->iuser.scene = scene; - oglrender->iuser.ok = 1; /* create render result */ RE_InitState(oglrender->re, NULL, &scene->r, &scene->view_layers, NULL, sizex, sizey, NULL); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 6f49b03f07f..4bf250b9d4f 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -1476,14 +1476,8 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat return; } - ImageTile *tile = BKE_image_get_tile(ima, 0); - /* tile->ok is zero when Image cannot load */ - if (tile->ok == 0) { - return; - } - /* setup dummy image user */ - iuser.ok = iuser.framenr = 1; + iuser.framenr = 1; iuser.scene = sp->scene; /* elubie: this needs to be changed: here image is always loaded if not diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index a35e248a78c..63f61b6c5c1 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -916,8 +916,6 @@ static bool paint_2d_ensure_tile_canvas(ImagePaintState *s, int i) s->tiles[i].cache.lastdiameter = -1; - s->tiles[i].iuser.ok = true; - ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, NULL); if (ibuf != NULL) { if (ibuf->channels != 4) { @@ -1683,7 +1681,6 @@ void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode) for (int i = 0; i < s->num_tiles; i++) { s->tiles[i].iuser = sima->iuser; } - s->tiles[0].iuser.ok = true; zero_v2(s->tiles[0].uv_origin); diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 86349a64681..5b83f681d17 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -729,10 +729,6 @@ static void rna_update_cb(bContext *C, void *arg_cb, void *UNUSED(arg)) { RNAUpdateCb *cb = (RNAUpdateCb *)arg_cb; - /* ideally this would be done by RNA itself, but there we have - * no image user available, so we just update this flag here */ - cb->iuser->ok = 1; - /* we call update here on the pointer property, this way the * owner of the image pointer can still define its own update * and notifier */ diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index ae56238d73f..478e484924a 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -2227,7 +2227,7 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op) iter = IMB_moviecacheIter_new(image->cache); while (!IMB_moviecacheIter_done(iter)) { ibuf = IMB_moviecacheIter_getImBuf(iter); - if (ibuf->userflags & IB_BITMAPDIRTY) { + if (ibuf != NULL && ibuf->userflags & IB_BITMAPDIRTY) { if (first_ibuf == NULL) { first_ibuf = ibuf; } @@ -2251,7 +2251,7 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op) while (!IMB_moviecacheIter_done(iter)) { ibuf = IMB_moviecacheIter_getImBuf(iter); - if (ibuf->userflags & IB_BITMAPDIRTY) { + if (ibuf != NULL && ibuf->userflags & IB_BITMAPDIRTY) { char name[FILE_MAX]; BLI_strncpy(name, ibuf->name, sizeof(name)); diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index cc6effd0f71..3fbcc8348c8 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -668,7 +668,6 @@ static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image, ImageU uh->image_ref.ptr = image; uh->iuser = *iuser; uh->iuser.scene = NULL; - uh->iuser.ok = 1; BLI_addtail(undo_handles, uh); return uh; } diff --git a/source/blender/imbuf/IMB_moviecache.h b/source/blender/imbuf/IMB_moviecache.h index 8e5f15a64a0..d32346a8418 100644 --- a/source/blender/imbuf/IMB_moviecache.h +++ b/source/blender/imbuf/IMB_moviecache.h @@ -59,7 +59,7 @@ void IMB_moviecache_set_priority_callback(struct MovieCache *cache, void IMB_moviecache_put(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf); bool IMB_moviecache_put_if_possible(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf); -struct ImBuf *IMB_moviecache_get(struct MovieCache *cache, void *userkey); +struct ImBuf *IMB_moviecache_get(struct MovieCache *cache, void *userkey, bool *r_is_cached_empty); void IMB_moviecache_remove(struct MovieCache *cache, void *userkey); bool IMB_moviecache_has_frame(struct MovieCache *cache, void *userkey); void IMB_moviecache_free(struct MovieCache *cache); diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 119ef3ff971..c8c6f39507f 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -342,7 +342,7 @@ static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf, *cache_handle = NULL; - cache_ibuf = IMB_moviecache_get(moviecache, key); + cache_ibuf = IMB_moviecache_get(moviecache, key, NULL); *cache_handle = cache_ibuf; diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index 6cc1932eff6..8d0f55f5007 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -87,6 +87,8 @@ typedef struct MovieCacheItem { ImBuf *ibuf; MEM_CacheLimiterHandleC *c_handle; void *priority_data; + /* Indicates that #ibuf is null, because there was an error during load. */ + bool added_empty; } MovieCacheItem; static unsigned int moviecache_hashhash(const void *keyv) @@ -141,11 +143,16 @@ static void check_unused_keys(MovieCache *cache) while (!BLI_ghashIterator_done(&gh_iter)) { const MovieCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); const MovieCacheItem *item = BLI_ghashIterator_getValue(&gh_iter); - bool remove; BLI_ghashIterator_step(&gh_iter); - remove = !item->ibuf; + if (item->added_empty) { + /* Don't remove entries that have been added empty. Those indicate that the image couldn't be + * loaded correctly. */ + continue; + } + + bool remove = !item->ibuf; if (remove) { PRINT("%s: cache '%s' remove item %p without buffer\n", __func__, cache->name, item); @@ -309,7 +316,9 @@ static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, boo IMB_moviecache_init(); } - IMB_refImBuf(ibuf); + if (ibuf != NULL) { + IMB_refImBuf(ibuf); + } key = BLI_mempool_alloc(cache->keys_pool); key->cache_owner = cache; @@ -324,6 +333,7 @@ static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, boo item->cache_owner = cache; item->c_handle = NULL; item->priority_data = NULL; + item->added_empty = ibuf == NULL; if (cache->getprioritydatafp) { item->priority_data = cache->getprioritydatafp(userkey); @@ -365,7 +375,7 @@ bool IMB_moviecache_put_if_possible(MovieCache *cache, void *userkey, ImBuf *ibu size_t mem_in_use, mem_limit, elem_size; bool result = false; - elem_size = get_size_in_memory(ibuf); + elem_size = (ibuf == NULL) ? 0 : get_size_in_memory(ibuf); mem_limit = MEM_CacheLimiter_get_maximum(); BLI_mutex_lock(&limitor_lock); @@ -389,7 +399,7 @@ void IMB_moviecache_remove(MovieCache *cache, void *userkey) BLI_ghash_remove(cache->hash, &key, moviecache_keyfree, moviecache_valfree); } -ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) +ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey, bool *r_is_cached_empty) { MovieCacheKey key; MovieCacheItem *item; @@ -398,6 +408,10 @@ ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) key.userkey = userkey; item = (MovieCacheItem *)BLI_ghash_lookup(cache->hash, &key); + if (r_is_cached_empty) { + *r_is_cached_empty = false; + } + if (item) { if (item->ibuf) { BLI_mutex_lock(&limitor_lock); @@ -408,6 +422,9 @@ ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) return item->ibuf; } + if (r_is_cached_empty) { + *r_is_cached_empty = true; + } } return NULL; diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 30ca9540735..2464eb05a6d 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -51,16 +51,13 @@ typedef struct ImageUser { /** Offset within movie, start frame in global time. */ int offset, sfra; /** Cyclic flag. */ - char _pad0, cycl; - char ok; + char cycl; /** Multiview current eye - for internal use of drawing routines. */ char multiview_eye; short pass; - char _pad1[2]; int tile; - int _pad2; /** Listbase indices, for menu browsing or retrieve buffer. */ short multi_index, view, layer; @@ -112,9 +109,7 @@ typedef struct ImageTile { struct ImageTile_Runtime runtime; - char ok; - char _pad[3]; - + char _pad[4]; int tile_number; char label[64]; } ImageTile; diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 06ff1918040..4b52ceb6241 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -75,7 +75,6 @@ static void rna_Image_save_render( void *lock; iuser.scene = scene; - iuser.ok = 1; ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc index 3cef3a7625f..79cb0bd0f8c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.cc +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -141,7 +141,6 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, * So we manually construct image user to be sure first * image from sequence (that one which is set as filename * for image data-block) is used for sockets detection. */ - load_iuser.ok = 1; load_iuser.framenr = offset; /* make sure ima->type is correct */ @@ -411,7 +410,6 @@ static void node_composit_init_image(bNodeTree *ntree, bNode *node) node->storage = iuser; iuser->frames = 1; iuser->sfra = 1; - iuser->ok = 1; iuser->flag |= IMA_ANIM_ALWAYS; /* setup initial outputs */ diff --git a/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc index 54a1c59fca6..68c5ecdf48e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc @@ -38,7 +38,6 @@ static void node_composit_init_splitviewer(bNodeTree *UNUSED(ntree), bNode *node ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->sfra = 1; - iuser->ok = 1; node->custom1 = 50; /* default 50% split */ node->id = (ID *)BKE_image_ensure_viewer(G.main, IMA_TYPE_COMPOSITE, "Viewer Node"); diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc index b86ae57f664..969e2409898 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -44,7 +44,6 @@ static void node_composit_init_viewer(bNodeTree *UNUSED(ntree), bNode *node) ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->sfra = 1; - iuser->ok = 1; node->custom3 = 0.5f; node->custom4 = 0.5f; diff --git a/source/blender/nodes/texture/nodes/node_texture_image.c b/source/blender/nodes/texture/nodes/node_texture_image.c index 0d10b3270d7..a85b963286b 100644 --- a/source/blender/nodes/texture/nodes/node_texture_image.c +++ b/source/blender/nodes/texture/nodes/node_texture_image.c @@ -101,7 +101,6 @@ static void init(bNodeTree *UNUSED(ntree), bNode *node) ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->sfra = 1; - iuser->ok = 1; iuser->flag |= IMA_ANIM_ALWAYS; } From 52f4a908f7117a3e2a0d5b3de78c6c72f2a599e5 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 2 Nov 2021 12:00:07 +0100 Subject: [PATCH 1452/1500] Removed compilation warning nullptr check in image engine. --- source/blender/draw/engines/image/image_engine.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/image/image_engine.cc b/source/blender/draw/engines/image/image_engine.cc index d09c4223410..776639f5224 100644 --- a/source/blender/draw/engines/image/image_engine.cc +++ b/source/blender/draw/engines/image/image_engine.cc @@ -109,7 +109,7 @@ static void space_image_gpu_texture_get(Image *image, { const DRWContextState *draw_ctx = DRW_context_state_get(); SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; - if (image->rr != NULL) { + if (image->rr != nullptr) { /* Update multi-index and pass for the current eye. */ BKE_image_multilayer_index(image->rr, &sima->iuser); } From 9bd97e62ade417f6b4025acbad46802c3e7e5683 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 2 Nov 2021 21:57:23 +1100 Subject: [PATCH 1453/1500] Fix T92464: Operators fail after opening blend files via an operator Operators such as setting the object mode failed after calling WM_OT_open_mainfile from Python. Keep the window after loading a file outside the main event loop. --- source/blender/windowmanager/intern/wm_files.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 67222cc07f9..ff9b56d7859 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -769,6 +769,18 @@ static void wm_file_read_post(bContext *C, const struct wmFileReadPost_Params *p WM_toolsystem_init(C); } } + + /* Keep last. */ + if (use_data) { + if (!G.background) { + /* Special case, when calling indirectly (from a Python script for example), + * the event loop wont run again to set the active window. + * Set the window here to allow scripts to continue running other operations, see: T92464. */ + if (wm->op_undo_depth > 0) { + CTX_wm_window_set(C, wm->windows.first); + } + } + } } /** \} */ From b1bf8848895bf4b35a14f89806f1f5807198e321 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 2 Nov 2021 12:26:42 +0100 Subject: [PATCH 1454/1500] Images: fix error in previous refactor Some compositor tests (e.g. `compositor_color_test`) broke because of rB0c3b215e7d5456878b155d13440864f49ad1f230. The issue was a heap-use-after-free bug caused by a missing call to `MEM_CacheLimiter_unmanage`. --- source/blender/imbuf/intern/moviecache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index 8d0f55f5007..6e7b85a300a 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -122,8 +122,8 @@ static void moviecache_valfree(void *val) PRINT("%s: cache '%s' free item %p buffer %p\n", __func__, cache->name, item, item->ibuf); + MEM_CacheLimiter_unmanage(item->c_handle); if (item->ibuf) { - MEM_CacheLimiter_unmanage(item->c_handle); IMB_freeImBuf(item->ibuf); } From 2f0f08bc9817ae7e7f9390fca3644454af7d72ed Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 2 Nov 2021 23:31:19 +1100 Subject: [PATCH 1455/1500] Fix T92733: Error moving a completely locked bone --- source/blender/editors/transform/transform_mode.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 7f92c96d25f..0e632e5f82d 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -1085,7 +1085,14 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) case TFM_RESIZE: { float mouse_dir_constraint[3]; if (op) { - RNA_float_get_array(op->ptr, "mouse_dir_constraint", mouse_dir_constraint); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "mouse_dir_constraint"); + if (prop) { + RNA_property_float_get_array(op->ptr, prop, mouse_dir_constraint); + } + else { + /* Resize is expected to have this property. */ + BLI_assert(!STREQ(op->idname, "TRANSFORM_OT_resize")); + } } else { zero_v3(mouse_dir_constraint); From efcf36f2e9773f30fa6d4cce8fd0e793b9694b78 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 2 Nov 2021 07:59:10 -0500 Subject: [PATCH 1456/1500] Fix T92532: Missing null checks in IDPropertyManager.update_from Calling it with a None argument, or no arguments, or with a property that is missing UI data for some reason would fail. There is no particular reason why ensuring those things don't happen is helpful, so just add null checks for safety. Differential Revision: https://developer.blender.org/D13024 --- source/blender/python/generic/idprop_py_ui_api.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/python/generic/idprop_py_ui_api.c b/source/blender/python/generic/idprop_py_ui_api.c index 7827bd48dfe..37ba27f7315 100644 --- a/source/blender/python/generic/idprop_py_ui_api.c +++ b/source/blender/python/generic/idprop_py_ui_api.c @@ -622,7 +622,9 @@ static PyObject *BPy_IDPropertyUIManager_update_from(BPy_IDPropertyUIManager *se IDP_ui_data_free(property); } - property->ui_data = IDP_ui_data_copy(ui_manager_src->property); + if (ui_manager_src->property && ui_manager_src->property->ui_data) { + property->ui_data = IDP_ui_data_copy(ui_manager_src->property); + } Py_RETURN_NONE; } From a7e92843f72efe0f395eb67d24a419f83f60dc70 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Tue, 2 Nov 2021 07:43:44 -0600 Subject: [PATCH 1457/1500] Fix: Build error on windows. External symbols in C files need to be marked as such otherwise the linker will not find them. --- source/blender/draw/engines/image/image_shader.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/image/image_shader.cc b/source/blender/draw/engines/image/image_shader.cc index f5128fa00c7..1c6abf36505 100644 --- a/source/blender/draw/engines/image/image_shader.cc +++ b/source/blender/draw/engines/image/image_shader.cc @@ -29,12 +29,14 @@ #include "image_engine.h" #include "image_private.hh" +extern "C" { extern char datatoc_common_colormanagement_lib_glsl[]; extern char datatoc_common_globals_lib_glsl[]; extern char datatoc_common_view_lib_glsl[]; extern char datatoc_engine_image_frag_glsl[]; extern char datatoc_engine_image_vert_glsl[]; +} namespace blender::draw::image_engine { @@ -84,4 +86,3 @@ void IMAGE_shader_free() } } // namespace blender::draw::image_engine - From 698b05fc58f65ffe997fd18958c7d075277021d7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 2 Nov 2021 15:07:35 +0100 Subject: [PATCH 1458/1500] BLI: avoid passing nullptr to strncmp This resulted in an ASAN warning. --- source/blender/blenlib/BLI_string_ref.hh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index 34baf94c448..dc73208350f 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -610,6 +610,10 @@ constexpr bool operator==(StringRef a, StringRef b) if (a.size() != b.size()) { return false; } + if (a.data() == b.data()) { + /* This also avoids passing null to the call below, which would results in an ASAN warning. */ + return true; + } return STREQLEN(a.data(), b.data(), (size_t)a.size()); } From 1b2342b4d3641a5b438cba86a9378be944cec103 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 2 Nov 2021 15:33:30 +0100 Subject: [PATCH 1459/1500] Tests: Add basic unittest for library reload and relocate operators. --- tests/python/bl_blendfile_liblink.py | 59 ++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index ab26059e944..6017d3f1a1e 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -370,10 +370,69 @@ class TestBlendLibAppendReuseID(TestBlendLibLinkHelper): assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here +class TestBlendLibLibraryReloadRelocate(TestBlendLibLinkHelper): + + def __init__(self, args): + self.args = args + + def test_link_reload(self): + output_dir = self.args.output_dir + output_lib_path = self.init_lib_data_basic() + + # Simple link of a single Object, and reload. + self.reset_blender() + + link_dir = os.path.join(output_lib_path, "Object") + bpy.ops.wm.link(directory=link_dir, filename="LibMesh") + + assert(len(bpy.data.meshes) == 1) + assert(len(bpy.data.objects) == 1) + assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here + + orig_data = self.blender_data_to_tuple(bpy.data, "orig_data") + + bpy.ops.wm.lib_reload(library=bpy.data.objects[0].name) + + reload_data = self.blender_data_to_tuple(bpy.data, "reload_data") + + print(orig_data) + print(reload_data) + assert(orig_data == reload_data) + + def test_link_relocate(self): + output_dir = self.args.output_dir + output_lib_path = self.init_lib_data_basic() + + # Simple link of a single Object, and reload. + self.reset_blender() + + link_dir = os.path.join(output_lib_path, "Object") + bpy.ops.wm.link(directory=link_dir, filename="LibMesh") + + assert(len(bpy.data.meshes) == 1) + assert(len(bpy.data.objects) == 1) + assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here + + orig_data = self.blender_data_to_tuple(bpy.data, "orig_data") + + lib_path, lib_ext = os.path.splitext(output_lib_path) + new_lib_path = lib_path + "_relocate" + lib_ext + os.rename(output_lib_path, new_lib_path) + + bpy.ops.wm.lib_relocate(library=bpy.data.objects[0].name, directory="", filename=new_lib_path) + + relocate_data = self.blender_data_to_tuple(bpy.data, "relocate_data") + + print(orig_data) + print(relocate_data) + assert(orig_data == relocate_data) + + TESTS = ( TestBlendLibLinkSaveLoadBasic, TestBlendLibAppendBasic, TestBlendLibAppendReuseID, + TestBlendLibLibraryReloadRelocate, ) From 89c9fa8b737e07c84c1ff35919d211699592e6b5 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 2 Nov 2021 11:05:29 +0100 Subject: [PATCH 1460/1500] Fix T92462: Cycles crash calculating hair transparency Need to make sure images needed for hair shaders are loaded before running the shader. The naming is a bit misleading, but this is an internal API and we can change it easily. Submitting minimal patch needed to fix logic in the code to make it safer to review for 3.0. Differential Revision: https://developer.blender.org/D13067 --- intern/cycles/scene/geometry.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index 5141e1f8358..8a3fc522d22 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -1588,9 +1588,20 @@ void GeometryManager::device_update_displacement_images(Device *device, set bump_images; foreach (Geometry *geom, scene->geometry) { if (geom->is_modified()) { + /* Geometry-level check for hair shadow transparency. + * This matches the logic in the `Hair::update_shadow_transparency()`, avoiding access to + * possible non-loaded images. */ + bool need_shadow_transparency = false; + if (geom->geometry_type == Geometry::HAIR) { + Hair *hair = static_cast(geom); + need_shadow_transparency = hair->need_shadow_transparency(); + } + foreach (Node *node, geom->get_used_shaders()) { Shader *shader = static_cast(node); - if (!shader->has_displacement || shader->get_displacement_method() == DISPLACE_BUMP) { + const bool is_true_displacement = (shader->has_displacement && + shader->get_displacement_method() != DISPLACE_BUMP); + if (!is_true_displacement && !need_shadow_transparency) { continue; } foreach (ShaderNode *node, shader->graph->nodes) { From 6981bee2c77f826570df36e9e7cf90adfae4d409 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Tue, 2 Nov 2021 15:58:13 +0000 Subject: [PATCH 1461/1500] Fix T92736: Hole in mesh after Set Position The geometry node port of voronoi_smooth_f1 function has a division by zero when smoothness is set to zero. Using a safe_divide within the function causes issues and was noted in the original patch D12725. Solution in this case is to clamp zero smoothness to FLT_EPSILON. Reviewed By: JacquesLucke Maniphest Tasks: T92736 Differential Revision: https://developer.blender.org/D13069 --- source/blender/blenlib/intern/noise.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index 4259237af6e..bc78ded63a0 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -1548,6 +1548,7 @@ void voronoi_smooth_f1(const float w, { const float cellPosition = floorf(w); const float localPosition = w - cellPosition; + const float smoothness_clamped = max_ff(smoothness, FLT_MIN); float smoothDistance = 8.0f; float smoothPosition = 0.0f; @@ -1558,7 +1559,7 @@ void voronoi_smooth_f1(const float w, hash_float_to_float(cellPosition + cellOffset) * randomness; const float distanceToPoint = voronoi_distance(pointPosition, localPosition); const float h = smoothstep( - 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness_clamped); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; if (r_color != nullptr || r_w != nullptr) { @@ -1752,6 +1753,7 @@ void voronoi_smooth_f1(const float2 coord, { const float2 cellPosition = float2::floor(coord); const float2 localPosition = coord - cellPosition; + const float smoothness_clamped = max_ff(smoothness, FLT_MIN); float smoothDistance = 8.0f; float3 smoothColor = float3(0.0f, 0.0f, 0.0f); @@ -1764,7 +1766,7 @@ void voronoi_smooth_f1(const float2 coord, const float distanceToPoint = voronoi_distance( pointPosition, localPosition, metric, exponent); const float h = smoothstep( - 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness_clamped); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; if (r_color != nullptr || r_position != nullptr) { @@ -1999,6 +2001,7 @@ void voronoi_smooth_f1(const float3 coord, { const float3 cellPosition = float3::floor(coord); const float3 localPosition = coord - cellPosition; + const float smoothness_clamped = max_ff(smoothness, FLT_MIN); float smoothDistance = 8.0f; float3 smoothColor = float3(0.0f, 0.0f, 0.0f); @@ -2012,7 +2015,7 @@ void voronoi_smooth_f1(const float3 coord, const float distanceToPoint = voronoi_distance( pointPosition, localPosition, metric, exponent); const float h = smoothstep( - 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness_clamped); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; if (r_color != nullptr || r_position != nullptr) { @@ -2262,6 +2265,7 @@ void voronoi_smooth_f1(const float4 coord, { const float4 cellPosition = float4::floor(coord); const float4 localPosition = coord - cellPosition; + const float smoothness_clamped = max_ff(smoothness, FLT_MIN); float smoothDistance = 8.0f; float3 smoothColor = float3(0.0f, 0.0f, 0.0f); @@ -2277,7 +2281,7 @@ void voronoi_smooth_f1(const float4 coord, const float distanceToPoint = voronoi_distance( pointPosition, localPosition, metric, exponent); const float h = smoothstep( - 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness_clamped); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; if (r_color != nullptr || r_position != nullptr) { From cde982d672322ab816e90baaa2865ac54d23598d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 2 Nov 2021 17:00:23 +0100 Subject: [PATCH 1462/1500] I18n: Fix all new cpp files not being parsed by UI message extractor. The 'new' `.cc`/`.hh` extensions were never added to UI message extractor. Related to T43295. --- release/scripts/modules/bl_i18n_utils/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index 4825992b8e2..57aeef895b9 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -195,7 +195,7 @@ DOMAIN = "blender" # Our own "gettext" stuff. # File type (ext) to parse. -PYGETTEXT_ALLOWED_EXTS = {".c", ".cpp", ".cxx", ".hpp", ".hxx", ".h"} +PYGETTEXT_ALLOWED_EXTS = {".c", ".cc", ".cpp", ".cxx", ".hh", ".hpp", ".hxx", ".h"} # Max number of contexts into a BLT_I18N_MSGID_MULTI_CTXT macro... PYGETTEXT_MAX_MULTI_CTXT = 16 From dabfac37e35274bd68d74d4edb18b2827a6eec4e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 2 Nov 2021 17:02:15 +0100 Subject: [PATCH 1463/1500] Fix more UI message/i18n issues. --- source/blender/editors/space_file/asset_catalog_tree_view.cc | 2 +- source/blender/modifiers/intern/MOD_nodes.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index e6b76e05e16..b3b81c5e07f 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -370,7 +370,7 @@ std::string AssetCatalogDropController::drop_tooltip_asset_catalog(const wmDrag AssetCatalog *src_catalog = catalog_service->find_catalog(catalog_drag->drag_catalog_id); return std::string(TIP_("Move Catalog")) + " '" + src_catalog->path.name() + "' " + - IFACE_("into") + " '" + catalog_item_.get_name() + "'"; + TIP_("into") + " '" + catalog_item_.get_name() + "'"; } std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &drag) const diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index c88940c00c2..29bfa382674 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1409,7 +1409,7 @@ static void panel_draw(const bContext *C, Panel *panel) if (has_legacy_node) { uiLayout *row = uiLayoutRow(layout, false); - uiItemL(row, IFACE_("Node tree has legacy node"), ICON_ERROR); + uiItemL(row, N_("Node tree has legacy node"), ICON_ERROR); uiLayout *sub = uiLayoutRow(row, false); uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT); uiItemO(sub, "", ICON_VIEWZOOM, "NODE_OT_geometry_node_view_legacy"); @@ -1438,7 +1438,7 @@ static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel) } } if (!has_output_attribute) { - uiItemL(layout, IFACE_("No group output attributes connected."), ICON_INFO); + uiItemL(layout, N_("No group output attributes connected"), ICON_INFO); } } From 980bc5a707551f6db18deeeb29c5f59f7a7c98bc Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Tue, 2 Nov 2021 18:29:35 +0200 Subject: [PATCH 1464/1500] UI: Use socket type info color to draw links Currently, colored links overlay only supports standard sockets defined by Blender. Some add-ons like Animation Nodes defines custom sockets for everything and hence doesn't get colored sockets. This patch uses the draw color from the socket type info to draw links in order to support custom sockets. Differential Revision: https://developer.blender.org/D13044 Reviewed By: Hans Goudey --- source/blender/editors/space_node/drawnode.cc | 18 +++++++++------ .../blender/editors/space_node/node_draw.cc | 23 +++++++++++-------- .../blender/editors/space_node/node_intern.h | 12 ++++++---- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 24f5decacdf..83b7c119b62 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -4268,7 +4268,8 @@ static void nodelink_batch_add_link(const SpaceNode *snode, } /* don't do shadows if th_col3 is -1. */ -void node_draw_link_bezier(const View2D *v2d, +void node_draw_link_bezier(const bContext *C, + const View2D *v2d, const SpaceNode *snode, const bNodeLink *link, int th_col1, @@ -4310,18 +4311,21 @@ void node_draw_link_bezier(const View2D *v2d, snode->overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS && ((link->fromsock == nullptr || link->fromsock->typeinfo->type >= 0) && (link->tosock == nullptr || link->tosock->typeinfo->type >= 0))) { + PointerRNA from_node_ptr, to_node_ptr; + RNA_pointer_create((ID *)snode->edittree, &RNA_Node, link->fromnode, &from_node_ptr); + RNA_pointer_create((ID *)snode->edittree, &RNA_Node, link->tonode, &to_node_ptr); if (link->fromsock) { - copy_v4_v4(colors[1], std_node_socket_colors[link->fromsock->typeinfo->type]); + node_socket_color_get(C, snode->edittree, &from_node_ptr, link->fromsock, colors[1]); } else { - copy_v4_v4(colors[1], std_node_socket_colors[link->tosock->typeinfo->type]); + node_socket_color_get(C, snode->edittree, &to_node_ptr, link->tosock, colors[1]); } if (link->tosock) { - copy_v4_v4(colors[2], std_node_socket_colors[link->tosock->typeinfo->type]); + node_socket_color_get(C, snode->edittree, &to_node_ptr, link->tosock, colors[2]); } else { - copy_v4_v4(colors[2], std_node_socket_colors[link->fromsock->typeinfo->type]); + node_socket_color_get(C, snode->edittree, &from_node_ptr, link->fromsock, colors[2]); } } else { @@ -4392,7 +4396,7 @@ void node_draw_link_bezier(const View2D *v2d, } /* NOTE: this is used for fake links in groups too. */ -void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) +void node_draw_link(const bContext *C, View2D *v2d, SpaceNode *snode, bNodeLink *link) { int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE; @@ -4436,7 +4440,7 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) } } - node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3); + node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3); } void ED_node_draw_snap(View2D *v2d, const float cent[2], float size, NodeBorder border, uint pos) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index a6496294f96..f76c29ef0d9 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -725,12 +725,15 @@ int node_get_colorid(bNode *node) } } -static void node_draw_mute_line(const View2D *v2d, const SpaceNode *snode, const bNode *node) +static void node_draw_mute_line(const bContext *C, + const View2D *v2d, + const SpaceNode *snode, + const bNode *node) { GPU_blend(GPU_BLEND_ALPHA); LISTBASE_FOREACH (const bNodeLink *, link, &node->internal_links) { - node_draw_link_bezier(v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE); + node_draw_link_bezier(C, v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE); } GPU_blend(GPU_BLEND_NONE); @@ -825,13 +828,13 @@ static void node_socket_outline_color_get(const bool selected, /* Usual convention here would be node_socket_get_color(), but that's already used (for setting a * color property socket). */ void node_socket_color_get( - bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, bNodeSocket *sock, float r_color[4]) + const bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, bNodeSocket *sock, float r_color[4]) { PointerRNA ptr; BLI_assert(RNA_struct_is_a(node_ptr->type, &RNA_Node)); RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); - sock->typeinfo->draw_color(C, &ptr, node_ptr, r_color); + sock->typeinfo->draw_color((bContext *)C, &ptr, node_ptr, r_color); } struct SocketTooltipData { @@ -1049,7 +1052,7 @@ static void node_socket_draw_nested(const bContext *C, float color[4]; float outline_color[4]; - node_socket_color_get((bContext *)C, ntree, node_ptr, sock, color); + node_socket_color_get(C, ntree, node_ptr, sock, color); node_socket_outline_color_get(selected, sock->type, outline_color); node_socket_draw(sock, @@ -1464,7 +1467,7 @@ void node_draw_sockets(const View2D *v2d, float color[4]; float outline_color[4]; - node_socket_color_get((bContext *)C, ntree, &node_ptr, socket, color); + node_socket_color_get(C, ntree, &node_ptr, socket, color); node_socket_outline_color_get(selected, socket->type, outline_color); node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy); @@ -1762,7 +1765,7 @@ static void node_draw_basis(const bContext *C, /* Wire across the node when muted/disabled. */ if (node->flag & NODE_MUTED) { - node_draw_mute_line(v2d, snode, node); + node_draw_mute_line(C, v2d, snode, node); } /* Body. */ @@ -1891,7 +1894,7 @@ static void node_draw_hidden(const bContext *C, /* Wire across the node when muted/disabled. */ if (node->flag & NODE_MUTED) { - node_draw_mute_line(v2d, snode, node); + node_draw_mute_line(C, v2d, snode, node); } /* Body. */ @@ -2202,7 +2205,7 @@ void node_draw_nodetree(const bContext *C, LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { if (!nodeLinkIsHidden(link)) { - node_draw_link(®ion->v2d, snode, link); + node_draw_link(C, ®ion->v2d, snode, link); } } nodelink_batch_end(snode); @@ -2387,7 +2390,7 @@ void node_draw_space(const bContext *C, ARegion *region) GPU_line_smooth(true); LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) { LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - node_draw_link(v2d, snode, (bNodeLink *)linkdata->data); + node_draw_link(C, v2d, snode, (bNodeLink *)linkdata->data); } } GPU_line_smooth(false); diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index c0d50e753ff..383fe5afdf9 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -121,7 +121,7 @@ void node_draw_sockets(const struct View2D *v2d, void node_update_default(const struct bContext *C, struct bNodeTree *ntree, struct bNode *node); int node_select_area_default(struct bNode *node, int x, int y); int node_tweak_area_default(struct bNode *node, int x, int y); -void node_socket_color_get(struct bContext *C, +void node_socket_color_get(const struct bContext *C, struct bNodeTree *ntree, struct PointerRNA *node_ptr, struct bNodeSocket *sock, @@ -186,8 +186,12 @@ void NODE_OT_backimage_sample(struct wmOperatorType *ot); void nodelink_batch_start(struct SpaceNode *snode); void nodelink_batch_end(struct SpaceNode *snode); -void node_draw_link(struct View2D *v2d, struct SpaceNode *snode, struct bNodeLink *link); -void node_draw_link_bezier(const struct View2D *v2d, +void node_draw_link(const struct bContext *C, + struct View2D *v2d, + struct SpaceNode *snode, + struct bNodeLink *link); +void node_draw_link_bezier(const struct bContext *C, + const struct View2D *v2d, const struct SpaceNode *snode, const struct bNodeLink *link, int th_col1, @@ -348,4 +352,4 @@ extern const char *node_context_dir[]; namespace blender::ed::space_node { Vector context_path_for_space_node(const bContext &C); } -#endif \ No newline at end of file +#endif From 20b163b53309fb6fbbb5aab78cde0d14e163e3b4 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 2 Nov 2021 17:20:24 +0100 Subject: [PATCH 1465/1500] UIMessages/i18n: Fix incorrect part of rBdabfac37e35274b. My bad, forgot lower-level UI code does not handle translations itself. Thanks to Hans Goudey (@HooglyBoogly) for the heads up. --- source/blender/modifiers/intern/MOD_nodes.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 29bfa382674..f87ff844acf 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1409,7 +1409,7 @@ static void panel_draw(const bContext *C, Panel *panel) if (has_legacy_node) { uiLayout *row = uiLayoutRow(layout, false); - uiItemL(row, N_("Node tree has legacy node"), ICON_ERROR); + uiItemL(row, TIP_("Node tree has legacy node"), ICON_ERROR); uiLayout *sub = uiLayoutRow(row, false); uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT); uiItemO(sub, "", ICON_VIEWZOOM, "NODE_OT_geometry_node_view_legacy"); @@ -1438,7 +1438,7 @@ static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel) } } if (!has_output_attribute) { - uiItemL(layout, N_("No group output attributes connected"), ICON_INFO); + uiItemL(layout, TIP_("No group output attributes connected"), ICON_INFO); } } From 29dff8f84423852f01570334abf08fb922ffb51e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 2 Nov 2021 17:50:18 +0100 Subject: [PATCH 1466/1500] Fix lots of missing messages i18n handling in `uiItemL` calls. Also fix several wrong usages of `IFACE_` (as a reminder, error/info messages should use `TIP_`, not `IFACE_`). --- .../blender/editors/animation/fmodifier_ui.c | 12 ++++----- source/blender/editors/interface/interface.c | 4 +-- .../editors/interface/interface_layout.c | 2 +- .../editors/interface/interface_templates.c | 17 +++++++----- source/blender/editors/interface/tree_view.cc | 2 +- .../blender/editors/screen/screen_user_menu.c | 6 ++--- .../editors/space_graph/graph_buttons.c | 26 ++++++++++--------- .../editors/space_image/image_buttons.c | 2 +- source/blender/editors/space_node/drawnode.cc | 2 +- .../intern/MOD_gpencillineart.c | 14 +++++----- source/blender/modifiers/intern/MOD_cloth.c | 2 +- .../blender/modifiers/intern/MOD_collision.c | 2 +- .../blender/modifiers/intern/MOD_decimate.c | 4 +-- .../modifiers/intern/MOD_dynamicpaint.c | 2 +- source/blender/modifiers/intern/MOD_fluid.c | 2 +- source/blender/modifiers/intern/MOD_ocean.c | 2 +- .../modifiers/intern/MOD_particlesystem.c | 2 +- source/blender/modifiers/intern/MOD_remesh.c | 2 +- .../blender/modifiers/intern/MOD_softbody.c | 2 +- source/blender/modifiers/intern/MOD_subsurf.c | 9 +++++-- source/blender/modifiers/intern/MOD_surface.c | 2 +- .../blender/modifiers/intern/MOD_ui_common.c | 2 +- .../blender/shader_fx/intern/FX_ui_common.c | 2 +- .../windowmanager/intern/wm_splash_screen.c | 2 +- 24 files changed, 67 insertions(+), 57 deletions(-) diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 105bb54cee3..b94ee68e276 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -403,10 +403,10 @@ static void generator_panel_draw(const bContext *C, Panel *panel) char xval[32]; /* The first value gets a "Coefficient" label. */ - BLI_strncpy(xval, "Coefficient", sizeof(xval)); + BLI_strncpy(xval, N_("Coefficient"), sizeof(xval)); for (int i = 0; i < data->arraysize; i++) { - uiItemFullR(col, ptr, prop, i, 0, 0, N_(xval), ICON_NONE); + uiItemFullR(col, ptr, prop, i, 0, 0, IFACE_(xval), ICON_NONE); BLI_snprintf(xval, sizeof(xval), "x^%d", i + 1); } break; @@ -420,17 +420,17 @@ static void generator_panel_draw(const bContext *C, Panel *panel) uiLayoutColumn(split, false); uiLayout *title_col = uiLayoutColumn(split, false); uiLayout *title_row = uiLayoutRow(title_col, true); - uiItemL(title_row, N_("A"), ICON_NONE); - uiItemL(title_row, N_("B"), ICON_NONE); + uiItemL(title_row, IFACE_("A"), ICON_NONE); + uiItemL(title_row, IFACE_("B"), ICON_NONE); } uiLayout *first_row = uiLayoutRow(col, true); - uiItemFullR(first_row, ptr, prop, 0, 0, 0, N_("y = (Ax + B)"), ICON_NONE); + uiItemFullR(first_row, ptr, prop, 0, 0, 0, IFACE_("y = (Ax + B)"), ICON_NONE); uiItemFullR(first_row, ptr, prop, 1, 0, 0, "", ICON_NONE); for (int i = 2; i < data->arraysize - 1; i++) { /* \u2715 is the multiplication symbol. */ uiLayout *row = uiLayoutRow(col, true); - uiItemFullR(row, ptr, prop, i, 0, 0, N_("\u2715 (Ax + B)"), ICON_NONE); + uiItemFullR(row, ptr, prop, i, 0, 0, IFACE_("\u2715 (Ax + B)"), ICON_NONE); uiItemFullR(row, ptr, prop, i + 1, 0, 0, "", ICON_NONE); } break; diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 62c84ed38ff..dc9eaed5731 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -4450,7 +4450,7 @@ static void ui_def_but_rna__panel_type(bContext *C, uiLayout *layout, void *but_ } else { char msg[256]; - SNPRINTF(msg, "Missing Panel: %s", panel_type); + SNPRINTF(msg, TIP_("Missing Panel: %s"), panel_type); uiItemL(layout, msg, ICON_NONE); } } @@ -4479,7 +4479,7 @@ static void ui_def_but_rna__menu_type(bContext *C, uiLayout *layout, void *but_p } else { char msg[256]; - SNPRINTF(msg, "Missing Menu: %s", menu_type); + SNPRINTF(msg, TIP_("Missing Menu: %s"), menu_type); uiItemL(layout, msg, ICON_NONE); } } diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 25ba0e13487..20e95ef4e9c 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -1339,7 +1339,7 @@ static void ui_item_menu_hold(struct bContext *C, ARegion *butregion, uiBut *but UI_menutype_draw(C, mt, layout); } else { - uiItemL(layout, "Menu Missing:", ICON_NONE); + uiItemL(layout, TIP_("Menu Missing:"), ICON_NONE); uiItemL(layout, menu_id, ICON_NONE); } UI_popup_menu_end(C, pup); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 755a0fce7bc..1d349aa0596 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2743,7 +2743,7 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co uiItemR(row, &ptr, "name", 0, "", ICON_NONE); } else { - uiItemL(row, con->name, ICON_NONE); + uiItemL(row, IFACE_(con->name), ICON_NONE); } /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */ @@ -6142,8 +6142,8 @@ void uiTemplateInputStatus(uiLayout *layout, struct bContext *C) uiLayout *row = uiLayoutRow(col, true); uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT); - const char *msg = WM_window_cursor_keymap_status_get(win, i, 0); - const char *msg_drag = WM_window_cursor_keymap_status_get(win, i, 1); + const char *msg = TIP_(WM_window_cursor_keymap_status_get(win, i, 0)); + const char *msg_drag = TIP_(WM_window_cursor_keymap_status_get(win, i, 1)); if (msg || (msg_drag == NULL)) { uiItemL(row, msg ? msg : "", (ICON_MOUSE_LMB + i)); @@ -6498,12 +6498,15 @@ void uiTemplateCacheFile(uiLayout *layout, row = uiLayoutRow(layout, false); /* For Cycles, verify that experimental features are enabled. */ if (BKE_scene_uses_cycles(scene) && !BKE_scene_uses_cycles_experimental_features(scene)) { - uiItemL(row, - "The Cycles Alembic Procedural is only available with the experimental feature set", - ICON_INFO); + uiItemL( + row, + TIP_( + "The Cycles Alembic Procedural is only available with the experimental feature set"), + ICON_INFO); } else { - uiItemL(row, "The active render engine does not have an Alembic Procedural", ICON_INFO); + uiItemL( + row, TIP_("The active render engine does not have an Alembic Procedural"), ICON_INFO); } } diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index c08fa51d5a5..fcc878c440c 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -660,7 +660,7 @@ void BasicTreeViewItem::add_label(uiLayout &layout, StringRefNull label_override if (icon == ICON_NONE && !is_collapsible()) { uiItemS_ex(&layout, 0.8f); } - uiItemL(&layout, label.c_str(), icon); + uiItemL(&layout, IFACE_(label.c_str()), icon); } void BasicTreeViewItem::on_activate() diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c index 733e8b694a6..bc370c64b0c 100644 --- a/source/blender/editors/screen/screen_user_menu.c +++ b/source/blender/editors/screen/screen_user_menu.c @@ -234,7 +234,7 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu) } else { if (show_missing) { - SNPRINTF(label, "Missing: %s", umi_op->op_idname); + SNPRINTF(label, TIP_("Missing: %s"), umi_op->op_idname); uiItemL(menu->layout, label, ICON_NONE); } } @@ -248,7 +248,7 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu) } else { if (show_missing) { - SNPRINTF(label, "Missing: %s", umi_mt->mt_idname); + SNPRINTF(label, TIP_("Missing: %s"), umi_mt->mt_idname); uiItemL(menu->layout, label, ICON_NONE); } } @@ -290,7 +290,7 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu) } if (!ok) { if (show_missing) { - SNPRINTF(label, "Missing: %s.%s", umi_pr->context_data_path, umi_pr->prop_id); + SNPRINTF(label, TIP_("Missing: %s.%s"), umi_pr->context_data_path, umi_pr->prop_id); uiItemL(menu->layout, label, ICON_NONE); } } diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index f4c4b6cafcd..275616f3bcb 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -593,17 +593,17 @@ static void graph_panel_key_properties(const bContext *C, Panel *panel) else { if ((fcu->bezt == NULL) && (fcu->modifiers.first)) { /* modifiers only - so no keyframes to be active */ - uiItemL(layout, IFACE_("F-Curve only has F-Modifiers"), ICON_NONE); - uiItemL(layout, IFACE_("See Modifiers panel below"), ICON_INFO); + uiItemL(layout, TIP_("F-Curve only has F-Modifiers"), ICON_NONE); + uiItemL(layout, TIP_("See Modifiers panel below"), ICON_INFO); } else if (fcu->fpt) { /* samples only */ uiItemL(layout, - IFACE_("F-Curve doesn't have any keyframes as it only contains sampled points"), + TIP_("F-Curve doesn't have any keyframes as it only contains sampled points"), ICON_NONE); } else { - uiItemL(layout, IFACE_("No active keyframe on F-Curve"), ICON_NONE); + uiItemL(layout, TIP_("No active keyframe on F-Curve"), ICON_NONE); } } @@ -688,28 +688,30 @@ static void driver_dvar_invalid_name_query_cb(bContext *C, void *dvar_v, void *U DriverVar *dvar = (DriverVar *)dvar_v; if (dvar->flag & DVAR_FLAG_INVALID_EMPTY) { - uiItemL(layout, "It cannot be left blank", ICON_ERROR); + uiItemL(layout, TIP_("It cannot be left blank"), ICON_ERROR); } if (dvar->flag & DVAR_FLAG_INVALID_START_NUM) { - uiItemL(layout, "It cannot start with a number", ICON_ERROR); + uiItemL(layout, TIP_("It cannot start with a number"), ICON_ERROR); } if (dvar->flag & DVAR_FLAG_INVALID_START_CHAR) { uiItemL(layout, - "It cannot start with a special character," - " including '$', '@', '!', '~', '+', '-', '_', '.', or ' '", + TIP_("It cannot start with a special character," + " including '$', '@', '!', '~', '+', '-', '_', '.', or ' '"), ICON_NONE); } if (dvar->flag & DVAR_FLAG_INVALID_HAS_SPACE) { - uiItemL(layout, "It cannot contain spaces (e.g. 'a space')", ICON_ERROR); + uiItemL(layout, TIP_("It cannot contain spaces (e.g. 'a space')"), ICON_ERROR); } if (dvar->flag & DVAR_FLAG_INVALID_HAS_DOT) { - uiItemL(layout, "It cannot contain dots (e.g. 'a.dot')", ICON_ERROR); + uiItemL(layout, TIP_("It cannot contain dots (e.g. 'a.dot')"), ICON_ERROR); } if (dvar->flag & DVAR_FLAG_INVALID_HAS_SPECIAL) { - uiItemL(layout, "It cannot contain special (non-alphabetical/numeric) characters", ICON_ERROR); + uiItemL(layout, + TIP_("It cannot contain special (non-alphabetical/numeric) characters"), + ICON_ERROR); } if (dvar->flag & DVAR_FLAG_INVALID_PY_KEYWORD) { - uiItemL(layout, "It cannot be a reserved keyword in Python", ICON_INFO); + uiItemL(layout, TIP_("It cannot be a reserved keyword in Python"), ICON_INFO); } UI_popup_menu_end(C, pup); diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 5b83f681d17..6a50f6d42e8 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -1056,7 +1056,7 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma if (imf->imtype == R_IMF_IMTYPE_CINEON) { #if 1 - uiItemL(col, IFACE_("Hard coded Non-Linear, Gamma:1.7"), ICON_NONE); + uiItemL(col, TIP_("Hard coded Non-Linear, Gamma:1.7"), ICON_NONE); #else uiItemR(col, imfptr, "use_cineon_log", 0, NULL, ICON_NONE); uiItemR(col, imfptr, "cineon_black", 0, NULL, ICON_NONE); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 83b7c119b62..a6ee7d1e5b6 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -993,7 +993,7 @@ static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, Pointer } } else { - uiItemL(layout, "No mesh in active object.", ICON_ERROR); + uiItemL(layout, TIP_("No mesh in active object"), ICON_ERROR); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index fa31aec2b5b..b35ebd4be9a 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -388,7 +388,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetEnabled(layout, !is_baked); if (use_cache && !is_first) { - uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); return; } @@ -439,7 +439,7 @@ static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetEnabled(layout, !is_baked); if (!show_in_front) { - uiItemL(layout, IFACE_("Object is not in front"), ICON_INFO); + uiItemL(layout, TIP_("Object is not in front"), ICON_INFO); } layout = uiLayoutColumn(layout, false); @@ -568,7 +568,7 @@ static void face_mark_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetEnabled(layout, !is_baked); if (use_cache && !is_first) { - uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); return; } @@ -596,7 +596,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetEnabled(layout, !is_baked); if (use_cache && !is_first) { - uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); return; } @@ -633,7 +633,7 @@ static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetEnabled(layout, !is_baked); if (use_cache && !is_first) { - uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); return; } @@ -666,7 +666,7 @@ static void bake_panel_draw(const bContext *UNUSED(C), Panel *panel) if (is_baked) { uiLayout *col = uiLayoutColumn(layout, false); uiLayoutSetPropSep(col, false); - uiItemL(col, IFACE_("Modifier has baked data"), ICON_NONE); + uiItemL(col, TIP_("Modifier has baked data"), ICON_NONE); uiItemR( col, ptr, "is_baked", UI_ITEM_R_TOGGLE, IFACE_("Continue Without Clearing"), ICON_NONE); } @@ -696,7 +696,7 @@ static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "use_image_boundary_trimming", 0, NULL, ICON_NONE); if (show_in_front) { - uiItemL(layout, IFACE_("Object is shown in front"), ICON_ERROR); + uiItemL(layout, TIP_("Object is shown in front"), ICON_ERROR); } uiLayout *col = uiLayoutColumn(layout, false); diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index cf0658d4b39..c9d5ef73c49 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -285,7 +285,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index 521a93b199f..02e1f61b824 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -263,7 +263,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 56fcbbd8b7c..975f80a04f8 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -236,8 +236,8 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); int decimate_type = RNA_enum_get(ptr, "decimate_type"); - char count_info[32]; - snprintf(count_info, 32, "%s: %d", IFACE_("Face Count"), RNA_int_get(ptr, "face_count")); + char count_info[64]; + snprintf(count_info, 32, TIP_("Face Count: %d"), RNA_int_get(ptr, "face_count")); uiItemR(layout, ptr, "decimate_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index 77ae5c4b6f1..a696ce216c7 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -193,7 +193,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index e087b8411f8..a21eb603300 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -247,7 +247,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index ff1055eff3b..4566cf93dd7 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -552,7 +552,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) modifier_panel_end(layout, ptr); #else /* WITH_OCEANSIM */ - uiItemL(layout, IFACE_("Built without Ocean modifier"), ICON_NONE); + uiItemL(layout, TIP_("Built without Ocean modifier"), ICON_NONE); #endif /* WITH_OCEANSIM */ } diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index 71fc7f3e424..2a4cc1c2747 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -277,7 +277,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) ModifierData *md = (ModifierData *)ptr->data; ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; - uiItemL(layout, IFACE_("Settings are in the particle tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are in the particle tab"), ICON_NONE); if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) { if (ELEM(psys->part->ren_as, PART_DRAW_GR, PART_DRAW_OB)) { diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index fef1f76c051..937a73fddd9 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -273,7 +273,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) modifier_panel_end(layout, ptr); #else /* WITH_MOD_REMESH */ - uiItemL(layout, IFACE_("Built without Remesh modifier"), ICON_NONE); + uiItemL(layout, TIP_("Built without Remesh modifier"), ICON_NONE); #endif /* WITH_MOD_REMESH */ } diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index 4187f9087a0..46e960e10d4 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -92,7 +92,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index db0b769684e..7470f2abb15 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -27,6 +27,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -414,8 +415,12 @@ static void panel_draw(const bContext *C, Panel *panel) float preview = MAX2(RNA_float_get(&cycles_ptr, "preview_dicing_rate") * RNA_float_get(&ob_cycles_ptr, "dicing_rate"), 0.1f); - char output[64]; - snprintf(output, 64, "Final Scale: Render %.2f px, Viewport %.2f px", render, preview); + char output[256]; + BLI_snprintf(output, + sizeof(output), + TIP_("Final Scale: Render %.2f px, Viewport %.2f px"), + render, + preview); uiItemL(layout, output, ICON_NONE); uiItemS(layout); diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index 3f2d0a06db8..c8be2bd2829 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -207,7 +207,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 5d564464e20..9937a2342c3 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -108,7 +108,7 @@ void modifier_panel_end(uiLayout *layout, PointerRNA *ptr) ModifierData *md = ptr->data; if (md->error) { uiLayout *row = uiLayoutRow(layout, false); - uiItemL(row, IFACE_(md->error), ICON_ERROR); + uiItemL(row, TIP_(md->error), ICON_ERROR); } } diff --git a/source/blender/shader_fx/intern/FX_ui_common.c b/source/blender/shader_fx/intern/FX_ui_common.c index 86240171bf9..de5bef5d8d5 100644 --- a/source/blender/shader_fx/intern/FX_ui_common.c +++ b/source/blender/shader_fx/intern/FX_ui_common.c @@ -101,7 +101,7 @@ void shaderfx_panel_end(uiLayout *layout, PointerRNA *ptr) ShaderFxData *fx = ptr->data; if (fx->error) { uiLayout *row = uiLayoutRow(layout, false); - uiItemL(row, IFACE_(fx->error), ICON_ERROR); + uiItemL(row, TIP_(fx->error), ICON_ERROR); } } diff --git a/source/blender/windowmanager/intern/wm_splash_screen.c b/source/blender/windowmanager/intern/wm_splash_screen.c index 99c6bc39207..a4ec9e8fe6e 100644 --- a/source/blender/windowmanager/intern/wm_splash_screen.c +++ b/source/blender/windowmanager/intern/wm_splash_screen.c @@ -323,7 +323,7 @@ static uiBlock *wm_block_create_about(bContext *C, ARegion *region, void *UNUSED uiLayout *col = uiLayoutColumn(layout, true); - uiItemL_ex(col, N_("Blender"), ICON_NONE, true, false); + uiItemL_ex(col, IFACE_("Blender"), ICON_NONE, true, false); MenuType *mt = WM_menutype_find("WM_MT_splash_about", true); if (mt) { From 3a454beae73cdfe9c07e7308f78b00c79b502e19 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 2 Nov 2021 18:12:43 +0100 Subject: [PATCH 1467/1500] Fix T91094: missing update after collection changed Since rBb67fe05d4bea2d3c9efbd127e9d9dc3a897e89e6 collections have a geometry component that depends on all the geometries inside the collection. Contrary to what I originally thought `ID_RECALC_COPY_ON_WRITE` does not trigger a collection geometry update. This makes sense because a collection may change in ways that do not require a geometry update. Instead, we have to trigger the geometry update manually now by passing `ID_RECALC_GEOMETRY` when appropriate. --- source/blender/editors/space_outliner/outliner_dragdrop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index a391d032d7e..ed52eeab98c 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -1311,7 +1311,7 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE } if (from) { - DEG_id_tag_update(&from->id, ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&from->id, ID_RECALC_COPY_ON_WRITE | ID_RECALC_GEOMETRY); } } From 978f2cb900b75110a3bc9dbcec9d4aeae5df4565 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 2 Nov 2021 18:36:24 +0100 Subject: [PATCH 1468/1500] Fix T89487: Crash adding Rigid Body to object with shared mesh data Not sure why this bug was only discovered by such an elaborate steps and why it took so long to be discovered. The root of the issue is that in the 956c539e597a the typical flow of tag+flush+evaluate was violated and tagging for visibility change happened after flush. --- source/blender/depsgraph/intern/depsgraph_eval.cc | 2 ++ source/blender/depsgraph/intern/eval/deg_eval.cc | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index cc7ce871419..0314219b725 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -44,6 +44,7 @@ #include "intern/node/deg_node_time.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_tag.h" namespace deg = blender::deg; @@ -54,6 +55,7 @@ static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph) BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->frame); } + deg::graph_tag_ids_for_visible_update(deg_graph); deg::deg_graph_flush_updates(deg_graph); deg::deg_evaluate_on_refresh(deg_graph); } diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index c816c7b8db5..d6877adb66b 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -370,8 +370,6 @@ static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) */ void deg_evaluate_on_refresh(Depsgraph *graph) { - graph_tag_ids_for_visible_update(graph); - /* Nothing to update, early out. */ if (graph->entry_tags.is_empty()) { return; From 48e2a15160d276c8080cd8d4f6dc0ba752dbb410 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 2 Nov 2021 18:27:54 +0100 Subject: [PATCH 1469/1500] Fix T77681, T92634: noise texture artifacts with high detail We run into float precision issues here, clamp the number of octaves to one less, which has little to no visual difference. This was empirically determined to work up to 16 before, but with additional inputs like roughness only 15 appears to work. Also adds misisng clamp for the geometry nodes implementation. --- .../osl/shaders/node_musgrave_texture.osl | 2 +- intern/cycles/kernel/osl/shaders/node_noise.h | 8 +- intern/cycles/kernel/svm/fractal_noise.h | 8 +- intern/cycles/kernel/svm/musgrave.h | 2 +- source/blender/blenlib/intern/noise.cc | 94 ++++++++++++++----- .../gpu_shader_material_fractal_noise.glsl | 8 +- .../gpu_shader_material_tex_musgrave.glsl | 40 ++++---- .../shader/nodes/node_shader_tex_musgrave.cc | 2 +- .../shader/nodes/node_shader_tex_noise.cc | 2 +- .../shader/nodes/node_shader_tex_wave.cc | 2 +- 10 files changed, 109 insertions(+), 59 deletions(-) diff --git a/intern/cycles/kernel/osl/shaders/node_musgrave_texture.osl b/intern/cycles/kernel/osl/shaders/node_musgrave_texture.osl index 0e71ce74c29..6a688d654c9 100644 --- a/intern/cycles/kernel/osl/shaders/node_musgrave_texture.osl +++ b/intern/cycles/kernel/osl/shaders/node_musgrave_texture.osl @@ -697,7 +697,7 @@ shader node_musgrave_texture( output float Fac = 0.0) { float dimension = max(Dimension, 1e-5); - float octaves = clamp(Detail, 0.0, 16.0); + float octaves = clamp(Detail, 0.0, 15.0); float lacunarity = max(Lacunarity, 1e-5); vector3 s = Vector; diff --git a/intern/cycles/kernel/osl/shaders/node_noise.h b/intern/cycles/kernel/osl/shaders/node_noise.h index ab4cd7792cc..e8a71032171 100644 --- a/intern/cycles/kernel/osl/shaders/node_noise.h +++ b/intern/cycles/kernel/osl/shaders/node_noise.h @@ -90,7 +90,7 @@ float fractal_noise(float p, float details, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - float octaves = clamp(details, 0.0, 16.0); + float octaves = clamp(details, 0.0, 15.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); @@ -119,7 +119,7 @@ float fractal_noise(vector2 p, float details, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - float octaves = clamp(details, 0.0, 16.0); + float octaves = clamp(details, 0.0, 15.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); @@ -148,7 +148,7 @@ float fractal_noise(vector3 p, float details, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - float octaves = clamp(details, 0.0, 16.0); + float octaves = clamp(details, 0.0, 15.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); @@ -177,7 +177,7 @@ float fractal_noise(vector4 p, float details, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - float octaves = clamp(details, 0.0, 16.0); + float octaves = clamp(details, 0.0, 15.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); diff --git a/intern/cycles/kernel/svm/fractal_noise.h b/intern/cycles/kernel/svm/fractal_noise.h index b955d626dde..8256a24c751 100644 --- a/intern/cycles/kernel/svm/fractal_noise.h +++ b/intern/cycles/kernel/svm/fractal_noise.h @@ -27,7 +27,7 @@ ccl_device_noinline float fractal_noise_1d(float p, float octaves, float roughne float amp = 1.0f; float maxamp = 0.0f; float sum = 0.0f; - octaves = clamp(octaves, 0.0f, 16.0f); + octaves = clamp(octaves, 0.0f, 15.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_1d(fscale * p); @@ -56,7 +56,7 @@ ccl_device_noinline float fractal_noise_2d(float2 p, float octaves, float roughn float amp = 1.0f; float maxamp = 0.0f; float sum = 0.0f; - octaves = clamp(octaves, 0.0f, 16.0f); + octaves = clamp(octaves, 0.0f, 15.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_2d(fscale * p); @@ -85,7 +85,7 @@ ccl_device_noinline float fractal_noise_3d(float3 p, float octaves, float roughn float amp = 1.0f; float maxamp = 0.0f; float sum = 0.0f; - octaves = clamp(octaves, 0.0f, 16.0f); + octaves = clamp(octaves, 0.0f, 15.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_3d(fscale * p); @@ -114,7 +114,7 @@ ccl_device_noinline float fractal_noise_4d(float4 p, float octaves, float roughn float amp = 1.0f; float maxamp = 0.0f; float sum = 0.0f; - octaves = clamp(octaves, 0.0f, 16.0f); + octaves = clamp(octaves, 0.0f, 15.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_4d(fscale * p); diff --git a/intern/cycles/kernel/svm/musgrave.h b/intern/cycles/kernel/svm/musgrave.h index 85e32eee638..a37ca9eb8eb 100644 --- a/intern/cycles/kernel/svm/musgrave.h +++ b/intern/cycles/kernel/svm/musgrave.h @@ -737,7 +737,7 @@ ccl_device_noinline int svm_node_tex_musgrave(KernelGlobals kg, float gain = stack_load_float_default(stack, gain_stack_offset, defaults2.z); dimension = fmaxf(dimension, 1e-5f); - detail = clamp(detail, 0.0f, 16.0f); + detail = clamp(detail, 0.0f, 15.0f); lacunarity = fmaxf(lacunarity, 1e-5f); float fac; diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index bc78ded63a0..959385bff31 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -582,7 +582,7 @@ template float perlin_fractal_template(T position, float octaves, fl float amp = 1.0f; float maxamp = 0.0f; float sum = 0.0f; - octaves = CLAMPIS(octaves, 0.0f, 16.0f); + octaves = CLAMPIS(octaves, 0.0f, 15.0f); int n = static_cast(octaves); for (int i = 0; i <= n; i++) { float t = perlin(fscale * position); @@ -771,12 +771,16 @@ float3 perlin_float3_fractal_distorted(float4 position, * from "Texturing and Modelling: A procedural approach" */ -float musgrave_fBm(const float co, const float H, const float lacunarity, const float octaves) +float musgrave_fBm(const float co, + const float H, + const float lacunarity, + const float octaves_unclamped) { float p = co; float value = 0.0f; float pwr = 1.0f; const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); for (int i = 0; i < (int)octaves; i++) { value += perlin_signed(p) * pwr; @@ -802,12 +806,13 @@ float musgrave_fBm(const float co, const float H, const float lacunarity, const float musgrave_multi_fractal(const float co, const float H, const float lacunarity, - const float octaves) + const float octaves_unclamped) { float p = co; float value = 1.0f; float pwr = 1.0f; const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); for (int i = 0; i < (int)octaves; i++) { value *= (pwr * perlin_signed(p) + 1.0f); @@ -831,12 +836,16 @@ float musgrave_multi_fractal(const float co, * offset: raises the terrain from `sea level' */ -float musgrave_hetero_terrain( - const float co, const float H, const float lacunarity, const float octaves, const float offset) +float musgrave_hetero_terrain(const float co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset) { float p = co; const float pwHL = powf(lacunarity, -H); float pwr = pwHL; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); /* first unscaled octave of function; later octaves are scaled */ float value = offset + perlin_signed(p); @@ -869,7 +878,7 @@ float musgrave_hetero_terrain( float musgrave_hybrid_multi_fractal(const float co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset, const float gain) { @@ -881,6 +890,8 @@ float musgrave_hybrid_multi_fractal(const float co, float weight = gain * value; p *= lacunarity; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { if (weight > 1.0f) { weight = 1.0f; @@ -912,7 +923,7 @@ float musgrave_hybrid_multi_fractal(const float co, float musgrave_ridged_multi_fractal(const float co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset, const float gain) { @@ -925,6 +936,8 @@ float musgrave_ridged_multi_fractal(const float co, float value = signal; float weight = 1.0f; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; i < (int)octaves; i++) { p *= lacunarity; weight = CLAMPIS(signal * gain, 0.0f, 1.0f); @@ -947,12 +960,16 @@ float musgrave_ridged_multi_fractal(const float co, * from "Texturing and Modelling: A procedural approach" */ -float musgrave_fBm(const float2 co, const float H, const float lacunarity, const float octaves) +float musgrave_fBm(const float2 co, + const float H, + const float lacunarity, + const float octaves_unclamped) { float2 p = co; float value = 0.0f; float pwr = 1.0f; const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); for (int i = 0; i < (int)octaves; i++) { value += perlin_signed(p) * pwr; @@ -978,12 +995,13 @@ float musgrave_fBm(const float2 co, const float H, const float lacunarity, const float musgrave_multi_fractal(const float2 co, const float H, const float lacunarity, - const float octaves) + const float octaves_unclamped) { float2 p = co; float value = 1.0f; float pwr = 1.0f; const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); for (int i = 0; i < (int)octaves; i++) { value *= (pwr * perlin_signed(p) + 1.0f); @@ -1010,7 +1028,7 @@ float musgrave_multi_fractal(const float2 co, float musgrave_hetero_terrain(const float2 co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset) { float2 p = co; @@ -1021,6 +1039,8 @@ float musgrave_hetero_terrain(const float2 co, float value = offset + perlin_signed(p); p *= lacunarity; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; i < (int)octaves; i++) { float increment = (perlin_signed(p) + offset) * pwr * value; value += increment; @@ -1048,7 +1068,7 @@ float musgrave_hetero_terrain(const float2 co, float musgrave_hybrid_multi_fractal(const float2 co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset, const float gain) { @@ -1060,6 +1080,8 @@ float musgrave_hybrid_multi_fractal(const float2 co, float weight = gain * value; p *= lacunarity; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { if (weight > 1.0f) { weight = 1.0f; @@ -1091,7 +1113,7 @@ float musgrave_hybrid_multi_fractal(const float2 co, float musgrave_ridged_multi_fractal(const float2 co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset, const float gain) { @@ -1104,6 +1126,8 @@ float musgrave_ridged_multi_fractal(const float2 co, float value = signal; float weight = 1.0f; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; i < (int)octaves; i++) { p *= lacunarity; weight = CLAMPIS(signal * gain, 0.0f, 1.0f); @@ -1126,13 +1150,18 @@ float musgrave_ridged_multi_fractal(const float2 co, * from "Texturing and Modelling: A procedural approach" */ -float musgrave_fBm(const float3 co, const float H, const float lacunarity, const float octaves) +float musgrave_fBm(const float3 co, + const float H, + const float lacunarity, + const float octaves_unclamped) { float3 p = co; float value = 0.0f; float pwr = 1.0f; const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 0; i < (int)octaves; i++) { value += perlin_signed(p) * pwr; pwr *= pwHL; @@ -1157,13 +1186,15 @@ float musgrave_fBm(const float3 co, const float H, const float lacunarity, const float musgrave_multi_fractal(const float3 co, const float H, const float lacunarity, - const float octaves) + const float octaves_unclamped) { float3 p = co; float value = 1.0f; float pwr = 1.0f; const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 0; i < (int)octaves; i++) { value *= (pwr * perlin_signed(p) + 1.0f); pwr *= pwHL; @@ -1189,7 +1220,7 @@ float musgrave_multi_fractal(const float3 co, float musgrave_hetero_terrain(const float3 co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset) { float3 p = co; @@ -1200,6 +1231,8 @@ float musgrave_hetero_terrain(const float3 co, float value = offset + perlin_signed(p); p *= lacunarity; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; i < (int)octaves; i++) { float increment = (perlin_signed(p) + offset) * pwr * value; value += increment; @@ -1227,7 +1260,7 @@ float musgrave_hetero_terrain(const float3 co, float musgrave_hybrid_multi_fractal(const float3 co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset, const float gain) { @@ -1239,6 +1272,8 @@ float musgrave_hybrid_multi_fractal(const float3 co, float weight = gain * value; p *= lacunarity; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { if (weight > 1.0f) { weight = 1.0f; @@ -1270,7 +1305,7 @@ float musgrave_hybrid_multi_fractal(const float3 co, float musgrave_ridged_multi_fractal(const float3 co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset, const float gain) { @@ -1283,6 +1318,8 @@ float musgrave_ridged_multi_fractal(const float3 co, float value = signal; float weight = 1.0f; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; i < (int)octaves; i++) { p *= lacunarity; weight = CLAMPIS(signal * gain, 0.0f, 1.0f); @@ -1305,13 +1342,18 @@ float musgrave_ridged_multi_fractal(const float3 co, * from "Texturing and Modelling: A procedural approach" */ -float musgrave_fBm(const float4 co, const float H, const float lacunarity, const float octaves) +float musgrave_fBm(const float4 co, + const float H, + const float lacunarity, + const float octaves_unclamped) { float4 p = co; float value = 0.0f; float pwr = 1.0f; const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 0; i < (int)octaves; i++) { value += perlin_signed(p) * pwr; pwr *= pwHL; @@ -1336,13 +1378,15 @@ float musgrave_fBm(const float4 co, const float H, const float lacunarity, const float musgrave_multi_fractal(const float4 co, const float H, const float lacunarity, - const float octaves) + const float octaves_unclamped) { float4 p = co; float value = 1.0f; float pwr = 1.0f; const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 0; i < (int)octaves; i++) { value *= (pwr * perlin_signed(p) + 1.0f); pwr *= pwHL; @@ -1368,7 +1412,7 @@ float musgrave_multi_fractal(const float4 co, float musgrave_hetero_terrain(const float4 co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset) { float4 p = co; @@ -1379,6 +1423,8 @@ float musgrave_hetero_terrain(const float4 co, float value = offset + perlin_signed(p); p *= lacunarity; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; i < (int)octaves; i++) { float increment = (perlin_signed(p) + offset) * pwr * value; value += increment; @@ -1406,7 +1452,7 @@ float musgrave_hetero_terrain(const float4 co, float musgrave_hybrid_multi_fractal(const float4 co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset, const float gain) { @@ -1418,6 +1464,8 @@ float musgrave_hybrid_multi_fractal(const float4 co, float weight = gain * value; p *= lacunarity; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { if (weight > 1.0f) { weight = 1.0f; @@ -1449,7 +1497,7 @@ float musgrave_hybrid_multi_fractal(const float4 co, float musgrave_ridged_multi_fractal(const float4 co, const float H, const float lacunarity, - const float octaves, + const float octaves_unclamped, const float offset, const float gain) { @@ -1462,6 +1510,8 @@ float musgrave_ridged_multi_fractal(const float4 co, float value = signal; float weight = 1.0f; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + for (int i = 1; i < (int)octaves; i++) { p *= lacunarity; weight = CLAMPIS(signal * gain, 0.0f, 1.0f); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl index f25691c1a83..95f2be4bd44 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl @@ -5,7 +5,7 @@ float fractal_noise(float p, float octaves, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - octaves = clamp(octaves, 0.0, 16.0); + octaves = clamp(octaves, 0.0, 15.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); @@ -34,7 +34,7 @@ float fractal_noise(vec2 p, float octaves, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - octaves = clamp(octaves, 0.0, 16.0); + octaves = clamp(octaves, 0.0, 15.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); @@ -63,7 +63,7 @@ float fractal_noise(vec3 p, float octaves, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - octaves = clamp(octaves, 0.0, 16.0); + octaves = clamp(octaves, 0.0, 15.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); @@ -92,7 +92,7 @@ float fractal_noise(vec4 p, float octaves, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - octaves = clamp(octaves, 0.0, 16.0); + octaves = clamp(octaves, 0.0, 15.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl index 7ecca286acd..586385b7e86 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl @@ -19,7 +19,7 @@ void node_tex_musgrave_fBm_1d(vec3 co, { float p = w * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 0.0; @@ -59,7 +59,7 @@ void node_tex_musgrave_multi_fractal_1d(vec3 co, { float p = w * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 1.0; @@ -100,7 +100,7 @@ void node_tex_musgrave_hetero_terrain_1d(vec3 co, { float p = w * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -146,7 +146,7 @@ void node_tex_musgrave_hybrid_multi_fractal_1d(vec3 co, { float p = w * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -196,7 +196,7 @@ void node_tex_musgrave_ridged_multi_fractal_1d(vec3 co, { float p = w * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -241,7 +241,7 @@ void node_tex_musgrave_fBm_2d(vec3 co, { vec2 p = co.xy * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 0.0; @@ -281,7 +281,7 @@ void node_tex_musgrave_multi_fractal_2d(vec3 co, { vec2 p = co.xy * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 1.0; @@ -322,7 +322,7 @@ void node_tex_musgrave_hetero_terrain_2d(vec3 co, { vec2 p = co.xy * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -368,7 +368,7 @@ void node_tex_musgrave_hybrid_multi_fractal_2d(vec3 co, { vec2 p = co.xy * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -418,7 +418,7 @@ void node_tex_musgrave_ridged_multi_fractal_2d(vec3 co, { vec2 p = co.xy * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -463,7 +463,7 @@ void node_tex_musgrave_fBm_3d(vec3 co, { vec3 p = co * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 0.0; @@ -503,7 +503,7 @@ void node_tex_musgrave_multi_fractal_3d(vec3 co, { vec3 p = co * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 1.0; @@ -544,7 +544,7 @@ void node_tex_musgrave_hetero_terrain_3d(vec3 co, { vec3 p = co * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -590,7 +590,7 @@ void node_tex_musgrave_hybrid_multi_fractal_3d(vec3 co, { vec3 p = co * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -640,7 +640,7 @@ void node_tex_musgrave_ridged_multi_fractal_3d(vec3 co, { vec3 p = co * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -685,7 +685,7 @@ void node_tex_musgrave_fBm_4d(vec3 co, { vec4 p = vec4(co, w) * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 0.0; @@ -725,7 +725,7 @@ void node_tex_musgrave_multi_fractal_4d(vec3 co, { vec4 p = vec4(co, w) * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 1.0; @@ -766,7 +766,7 @@ void node_tex_musgrave_hetero_terrain_4d(vec3 co, { vec4 p = vec4(co, w) * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -812,7 +812,7 @@ void node_tex_musgrave_hybrid_multi_fractal_4d(vec3 co, { vec4 p = vec4(co, w) * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -862,7 +862,7 @@ void node_tex_musgrave_ridged_multi_fractal_4d(vec3 co, { vec4 p = vec4(co, w) * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index 3bf4e24ed53..81f9cd735eb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -29,7 +29,7 @@ static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b) b.add_input(N_("Vector")).hide_value().implicit_field(); b.add_input(N_("W")).min(-1000.0f).max(1000.0f); b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f); + b.add_input(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); b.add_input(N_("Dimension")).min(0.0f).max(1000.0f).default_value(2.0f); b.add_input(N_("Lacunarity")).min(0.0f).max(1000.0f).default_value(2.0f); b.add_input(N_("Offset")).min(-1000.0f).max(1000.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index 72892c32795..d28095edb96 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -29,7 +29,7 @@ static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b) b.add_input(N_("Vector")).implicit_field(); b.add_input(N_("W")).min(-1000.0f).max(1000.0f); b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f); + b.add_input(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); b.add_input(N_("Roughness")) .min(0.0f) .max(1.0f) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc index 144aa1885bd..fe534c605e9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc @@ -29,7 +29,7 @@ static void sh_node_tex_wave_declare(NodeDeclarationBuilder &b) b.add_input(N_("Vector")).implicit_field(); b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); b.add_input(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f); - b.add_input(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f); + b.add_input(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); b.add_input(N_("Detail Scale")).min(-1000.0f).max(1000.0f).default_value(1.0f); b.add_input(N_("Detail Roughness")) .min(0.0f) From 7aa311e5396bca5d65ba9cd00060d61bafbf28e9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 2 Nov 2021 13:43:54 -0500 Subject: [PATCH 1470/1500] Cleanup: Add function to get attribute ID from custom data layer --- .../blenkernel/intern/attribute_access.cc | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 01b53baf237..aeb66dc5a33 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -234,6 +234,14 @@ OutputAttribute::~OutputAttribute() } } +static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer &layer) +{ + if (layer.anonymous_id != nullptr) { + return layer.anonymous_id; + } + return layer.name; +} + static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data, const CustomDataType data_type, const int domain_size, @@ -595,13 +603,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com const CustomDataType data_type = (CustomDataType)layer.type; if (this->type_is_supported(data_type)) { AttributeMetaData meta_data{domain_, data_type}; - AttributeIDRef attribute_id; - if (layer.anonymous_id != nullptr) { - attribute_id = layer.anonymous_id; - } - else { - attribute_id = layer.name; - } + const AttributeIDRef attribute_id = attribute_id_from_custom_data_layer(layer); if (!callback(attribute_id, meta_data)) { return false; } @@ -825,13 +827,7 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call { for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { AttributeMetaData meta_data{domain, (CustomDataType)layer.type}; - AttributeIDRef attribute_id; - if (layer.anonymous_id != nullptr) { - attribute_id = layer.anonymous_id; - } - else { - attribute_id = layer.name; - } + const AttributeIDRef attribute_id = attribute_id_from_custom_data_layer(layer); if (!callback(attribute_id, meta_data)) { return false; } From 18392cef17ddb40857fdb2462d8bad50f389029b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 2 Nov 2021 15:16:52 -0500 Subject: [PATCH 1471/1500] Fix T92652: Joining curves breaks point attribute order Currently the curve to mesh node relies on the order of attributes being the same on every spline. This is a worthwhile assumption, because it will allow removing the attribute name storage duplication on every spline in the future. However, the join geometry node broke this order, since it just created new attributes without any regard to the order. To fix this, I added a "reorder" step after all the merged attributes have been created, the types have been joined, etc. It should be possible to change this code so that the attributes are added with the right order in the first place, but I would like to do that after refactoring spline attribute storage, and not for 3.0. Differential Revision: https://developer.blender.org/D13074 --- .../blenkernel/BKE_attribute_access.hh | 5 +++++ .../blenkernel/intern/attribute_access.cc | 20 +++++++++++++++++++ .../intern/geometry_set_instances.cc | 19 ++++++++++++++++++ .../geometry/nodes/node_geo_join_geometry.cc | 20 +++++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index cf19eb29a0e..6a87375e5e2 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -372,6 +372,11 @@ class CustomDataAttributes { void *buffer); bool remove(const AttributeIDRef &attribute_id); + /** + * Change the order of the attributes to match the order of IDs in the argument. + */ + void reorder(Span new_order); + bool foreach_attribute(const AttributeForeachCallback callback, const AttributeDomain domain) const; }; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index aeb66dc5a33..cd394a4ca42 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -835,6 +835,26 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call return true; } +void CustomDataAttributes::reorder(Span new_order) +{ + BLI_assert(new_order.size() == data.totlayer); + + Map old_order; + old_order.reserve(data.totlayer); + Array old_layers(Span(data.layers, data.totlayer)); + for (const int i : old_layers.index_range()) { + old_order.add_new(attribute_id_from_custom_data_layer(old_layers[i]), i); + } + + MutableSpan layers(data.layers, data.totlayer); + for (const int i : layers.index_range()) { + const int old_index = old_order.lookup(new_order[i]); + layers[i] = old_layers[old_index]; + } + + CustomData_update_typemap(&data); +} + } // namespace blender::bke /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 35c6a7df2c7..82fc14e7772 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -530,6 +530,24 @@ static void join_instance_groups_volume(Span set_groups, } } +/** + * Curve point domain attributes must be in the same order on every spline. The order might have + * been different on separate instances, so ensure that all splines have the same order. Note that + * because #Map is used, the order is not necessarily consistent every time, but it is the same for + * every spline, and that's what matters. + */ +static void sort_curve_point_attributes(const Map &info, + MutableSpan splines) +{ + Vector new_order; + for (const AttributeIDRef attribute_id : info.keys()) { + new_order.append(attribute_id); + } + for (SplinePtr &spline : splines) { + spline->attributes.reorder(new_order); + } +} + static void join_instance_groups_curve(Span set_groups, GeometrySet &result) { CurveEval *curve = join_curve_splines_and_builtin_attributes(set_groups); @@ -550,6 +568,7 @@ static void join_instance_groups_curve(Span set_groups, G {GEO_COMPONENT_TYPE_CURVE}, attributes, static_cast(dst_component)); + sort_curve_point_attributes(attributes, curve->splines()); } GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index cd385f364e9..1422bf2e475 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -356,6 +356,24 @@ static void ensure_control_point_attribute(const AttributeIDRef &attribute_id, } } +/** + * Curve point domain attributes must be in the same order on every spline. The order might have + * been different on separate instances, so ensure that all splines have the same order. Note that + * because #Map is used, the order is not necessarily consistent every time, but it is the same for + * every spline, and that's what matters. + */ +static void sort_curve_point_attributes(const Map &info, + MutableSpan splines) +{ + Vector new_order; + for (const AttributeIDRef attribute_id : info.keys()) { + new_order.append(attribute_id); + } + for (SplinePtr &spline : splines) { + spline->attributes.reorder(new_order); + } +} + /** * Fill data for an attribute on the new curve based on all source curves. */ @@ -409,6 +427,8 @@ static void join_curve_attributes(const Map & ensure_control_point_attribute(attribute_id, meta_data.data_type, src_components, result); } } + + sort_curve_point_attributes(info, result.splines()); } static void join_curve_components(MutableSpan src_geometry_sets, GeometrySet &result) From a72ed0bb7fafddd87ccfb0ad7acc8a6e6fe223b0 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 2 Nov 2021 15:22:16 -0500 Subject: [PATCH 1472/1500] Cleanup: Improve curve point attribute assert This properly checks the order of point domain attributes on each spline, and avoids the map, which makes the code easier to understand. The assert is also added to realizing instances and the join node. Differential Revision: https://developer.blender.org/D13071 --- .../blender/blenkernel/intern/curve_eval.cc | 46 +++++++++++++------ .../intern/geometry_set_instances.cc | 1 + .../geometry/nodes/node_geo_join_geometry.cc | 1 + 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 0e3da9e0789..bb745d5b20d 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -348,6 +348,7 @@ std::unique_ptr curve_eval_from_dna_curve(const Curve &dna_curve) * because attributes are stored on splines rather than in a flat array on the curve: * - The same set of attributes exists on every spline. * - Attributes with the same name have the same type on every spline. + * - Attributes are in the same order on every spline. */ void CurveEval::assert_valid_point_attributes() const { @@ -356,25 +357,40 @@ void CurveEval::assert_valid_point_attributes() const return; } const int layer_len = splines_.first()->attributes.data.totlayer; - Map map; - for (const SplinePtr &spline : splines_) { - BLI_assert(spline->attributes.data.totlayer == layer_len); - spline->attributes.foreach_attribute( + + Array ids_in_order(layer_len); + Array meta_data_in_order(layer_len); + + { + int i = 0; + splines_.first()->attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - map.add_or_modify( - attribute_id, - [&](AttributeMetaData *map_data) { - /* All unique attribute names should be added on the first spline. */ - BLI_assert(spline == splines_.first()); - *map_data = meta_data; - }, - [&](AttributeMetaData *map_data) { - /* Attributes on different splines should all have the same type. */ - BLI_assert(meta_data == *map_data); - }); + ids_in_order[i] = attribute_id; + meta_data_in_order[i] = meta_data; + i++; return true; }, ATTR_DOMAIN_POINT); } + + for (const SplinePtr &spline : splines_) { + /* All splines should have the same number of attributes. */ + BLI_assert(spline->attributes.data.totlayer == layer_len); + + int i = 0; + spline->attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + /* Attribute names and IDs should have the same order and exist on all splines. */ + BLI_assert(attribute_id == ids_in_order[i]); + + /* Attributes with the same ID different splines should all have the same type. */ + BLI_assert(meta_data == meta_data_in_order[i]); + + i++; + return true; + }, + ATTR_DOMAIN_POINT); + } + #endif } diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 82fc14e7772..a56c7ffb295 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -569,6 +569,7 @@ static void join_instance_groups_curve(Span set_groups, G attributes, static_cast(dst_component)); sort_curve_point_attributes(attributes, curve->splines()); + curve->assert_valid_point_attributes(); } GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 1422bf2e475..110b4a30dc8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -469,6 +469,7 @@ static void join_curve_components(MutableSpan src_geometry_sets, Ge dst_curve->attributes.reallocate(dst_curve->splines().size()); join_curve_attributes(info, src_components, *dst_curve); + dst_curve->assert_valid_point_attributes(); dst_component.replace(dst_curve); } From f674176d7765079614d3c326616a98f9e6135207 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 2 Nov 2021 21:07:37 +0100 Subject: [PATCH 1473/1500] Fix T85676: Cycles EXR merging not working with some single layer EXRs If there is only a layer without a name, use metadata from the first cycles layer in the metadata, if any. --- intern/cycles/session/merge.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/intern/cycles/session/merge.cpp b/intern/cycles/session/merge.cpp index 5890c15f48c..9044a8c4809 100644 --- a/intern/cycles/session/merge.cpp +++ b/intern/cycles/session/merge.cpp @@ -163,8 +163,26 @@ static bool parse_channels(const ImageSpec &in_spec, file_layers[layername].passes.push_back(pass); } - /* Loop over all detected render-layers, check whether they contain a full set of input channels. - * Any channels that won't be processed internally are also passed through. */ + /* If file contains a single unnamed layer, name it after the first layer metadata we find. */ + if (file_layers.size() == 1 && file_layers.find("") != file_layers.end()) { + for (const ParamValue &attrib : in_spec.extra_attribs) { + const string attrib_name = attrib.name().string(); + if (string_startswith(attrib_name, "cycles.") && string_endswith(attrib_name, ".samples")) { + /* Extract layer name. */ + const size_t start = strlen("cycles."); + const size_t end = attrib_name.size() - strlen(".samples"); + const string layername = attrib_name.substr(start, end - start); + + /* Reinsert as named instead of unnamed layer. */ + const MergeImageLayer layer = file_layers[""]; + file_layers.clear(); + file_layers[layername] = layer; + } + } + } + + /* Loop over all detected render-layers, check whether they contain a full set of input + * channels. Any channels that won't be processed internally are also passed through. */ for (auto &i : file_layers) { const string &name = i.first; MergeImageLayer &layer = i.second; From c4b73847d31add1a433acba8423982b288c8e7d9 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Tue, 2 Nov 2021 14:27:29 -0700 Subject: [PATCH 1474/1500] BLF: Remove Thread Locking For Font Metrics This patch removes the need to lock the thread just to get to some generic (not glyph-specific) font metrics. See D12976 for more details. Differential Revision: https://developer.blender.org/D12976 Reviewed by Campbell Barton --- source/blender/blenfont/intern/blf_font.c | 84 +++++++++---------- source/blender/blenfont/intern/blf_glyph.c | 23 +---- source/blender/blenfont/intern/blf_internal.h | 1 - .../blenfont/intern/blf_internal_types.h | 10 +-- source/blender/blenfont/intern/blf_thumbs.c | 2 +- 5 files changed, 46 insertions(+), 74 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 90c8d6357de..a2c778fcf16 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -34,6 +34,7 @@ #include FT_FREETYPE_H #include FT_GLYPH_H +#include FT_ADVANCES_H /* For FT_Get_Advance */ #include "MEM_guardedalloc.h" @@ -824,22 +825,7 @@ float blf_font_height(FontBLF *font, float blf_font_fixed_width(FontBLF *font) { - const unsigned int c = ' '; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF *g = blf_glyph_search(gc, c); - if (!g) { - g = blf_glyph_ensure(font, gc, FT_Get_Char_Index(font->face, c)); - - /* if we don't find the glyph. */ - if (!g) { - blf_glyph_cache_release(font); - return 0.0f; - } - } - - blf_glyph_cache_release(font); - return g->advance; + return (float)font->fixed_width; } static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, @@ -991,7 +977,7 @@ static void blf_font_wrap_apply(FontBLF *font, wrap.start = wrap.last[0]; i = wrap.last[1]; pen_x = 0; - pen_y -= gc->glyph_height_max; + pen_y -= blf_font_height_max(font); g_prev = NULL; lines += 1; continue; @@ -1114,45 +1100,41 @@ int blf_font_count_missing_chars(FontBLF *font, int blf_font_height_max(FontBLF *font) { int height_max; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - height_max = gc->glyph_height_max; - - blf_glyph_cache_release(font); - return height_max; + if (FT_IS_SCALABLE(font->face)) { + height_max = (int)((float)(font->face->ascender - font->face->descender) * + (((float)font->face->size->metrics.y_ppem) / + ((float)font->face->units_per_EM))); + } + else { + height_max = (int)(((float)font->face->size->metrics.height) / 64.0f); + } + /* can happen with size 1 fonts */ + return MAX2(height_max, 1); } int blf_font_width_max(FontBLF *font) { int width_max; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - width_max = gc->glyph_width_max; - - blf_glyph_cache_release(font); - return width_max; + if (FT_IS_SCALABLE(font->face)) { + width_max = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) * + (((float)font->face->size->metrics.x_ppem) / + ((float)font->face->units_per_EM))); + } + else { + width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f); + } + /* can happen with size 1 fonts */ + return MAX2(width_max, 1); } float blf_font_descender(FontBLF *font) { - float descender; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - descender = gc->descender; - - blf_glyph_cache_release(font); - return descender; + return ((float)font->face->size->metrics.descender) / 64.0f; } float blf_font_ascender(FontBLF *font) { - float ascender; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - ascender = gc->ascender; - - blf_glyph_cache_release(font); - return ascender; + return ((float)font->face->size->metrics.ascender) / 64.0f; } char *blf_display_name(FontBLF *font) @@ -1383,6 +1365,22 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) } blf_glyph_cache_release(font); + + /* Set fixed-width size for monospaced output. */ + FT_UInt gindex = FT_Get_Char_Index(font->face, U'0'); + if (gindex) { + FT_Fixed advance = 0; + FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance); + /* Use CSS 'ch unit' width, advance of zero character. */ + font->fixed_width = (int)(advance >> 16); + } + else { + /* Font does not contain "0" so use CSS fallback of 1/2 of em. */ + font->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6); + } + if (font->fixed_width < 1) { + font->fixed_width = 1; + } } /** \} */ diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 9170a1c0ac4..c4ffb3f87f1 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -86,27 +86,6 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table)); memset(gc->bucket, 0, sizeof(gc->bucket)); - gc->ascender = ((float)font->face->size->metrics.ascender) / 64.0f; - gc->descender = ((float)font->face->size->metrics.descender) / 64.0f; - - if (FT_IS_SCALABLE(font->face)) { - gc->glyph_width_max = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) * - (((float)font->face->size->metrics.x_ppem) / - ((float)font->face->units_per_EM))); - - gc->glyph_height_max = (int)((float)(font->face->bbox.yMax - font->face->bbox.yMin) * - (((float)font->face->size->metrics.y_ppem) / - ((float)font->face->units_per_EM))); - } - else { - gc->glyph_width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f); - gc->glyph_height_max = (int)(((float)font->face->size->metrics.height) / 64.0f); - } - - /* can happen with size 1 fonts */ - CLAMP_MIN(gc->glyph_width_max, 1); - CLAMP_MIN(gc->glyph_height_max, 1); - BLI_addhead(&font->cache, gc); return gc; } @@ -159,7 +138,7 @@ void blf_glyph_cache_free(GlyphCacheBLF *gc) MEM_freeN(gc); } -GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c) +static GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c) { GlyphBLF *p; unsigned int key; diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index ba871ea2496..a715d5df692 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -139,7 +139,6 @@ void blf_glyph_cache_release(struct FontBLF *font); void blf_glyph_cache_clear(struct FontBLF *font); void blf_glyph_cache_free(struct GlyphCacheBLF *gc); -struct GlyphBLF *blf_glyph_search(struct GlyphCacheBLF *gc, unsigned int c); struct GlyphBLF *blf_glyph_ensure(struct FontBLF *font, struct GlyphCacheBLF *gc, uint charcode); void blf_glyph_free(struct GlyphBLF *g); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index e90f82da7f3..aae666fa182 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -86,13 +86,6 @@ typedef struct GlyphCacheBLF { int bitmap_len_landed; int bitmap_len_alloc; - /* and the bigger glyph in the font. */ - int glyph_width_max; - int glyph_height_max; - - /* ascender and descender value. */ - float ascender; - float descender; } GlyphCacheBLF; typedef struct GlyphBLF { @@ -214,6 +207,9 @@ typedef struct FontBLF { /* font size. */ unsigned int size; + /* Column width when printing monospaced. */ + int fixed_width; + /* max texture size. */ int tex_size_max; diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c index 3153a55b697..12a83f7634e 100644 --- a/source/blender/blenfont/intern/blf_thumbs.c +++ b/source/blender/blenfont/intern/blf_thumbs.c @@ -106,7 +106,7 @@ void BLF_thumb_preview(const char *filename, font_size_curr -= (font_size_curr / font_shrink); font_shrink += 1; - font->pos[1] -= gc->ascender * 1.1f; + font->pos[1] -= blf_font_ascender(font) * 1.1f; /* We fallback to default english strings in case not enough chars are available in current * font for given translated string (useful in non-latin i18n context, like Chinese, From 11392829adfebd95286586362323ed6a39c31a5c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 2 Nov 2021 17:38:58 -0500 Subject: [PATCH 1475/1500] Fix T92316: Inconsistent name for Set Curve Tilt node --- source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index dde6d0bab92..a861c35f738 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -71,7 +71,7 @@ void register_node_type_geo_set_curve_tilt() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_SET_CURVE_TILT, "Set Tilt", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SET_CURVE_TILT, "Set Curve Tilt", NODE_CLASS_GEOMETRY, 0); ntype.geometry_node_execute = blender::nodes::geo_node_set_curve_tilt_exec; ntype.declare = blender::nodes::geo_node_set_curve_tilt_declare; nodeRegisterType(&ntype); From 8dbca01531f2f225cdb85a84dd3d52cde29d8121 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 3 Nov 2021 11:14:22 +1100 Subject: [PATCH 1476/1500] Fix T92515: Incorrect translation when scaling pose bones --- source/blender/editors/transform/transform_mode.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 0e632e5f82d..5e0abbc1a08 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -1054,6 +1054,11 @@ void ElementResize(const TransInfo *t, } if (t->options & (CTX_OBJECT | CTX_POSE_BONE)) { + if (t->options & CTX_POSE_BONE) { + /* Without this, the resulting location of scaled bones aren't correct, + * especially noticeable scaling root or disconnected bones around the cursor, see T92515. */ + mul_mat3_m4_v3(tc->poseobj->obmat, vec); + } mul_m3_v3(td->smtx, vec); } From 7996b49cb050eb22fcc4305952d6c972c6c16f24 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 3 Nov 2021 11:42:07 +1100 Subject: [PATCH 1477/1500] Cleanup: spelling --- source/blender/blenkernel/intern/spline_base.cc | 2 +- source/blender/draw/engines/overlay/overlay_engine.c | 10 +++++----- source/blender/nodes/NOD_node_tree_ref.hh | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index bbe4e0aab7b..c2c9d178171 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -486,7 +486,7 @@ Array Spline::sample_uniform_index_factors(const int samples_size) const prev_length = length; } - /* Zero lengths or float innacuracies can cause invalid values, or simply + /* Zero lengths or float inaccuracies can cause invalid values, or simply * skip some, so set the values that weren't completed in the main loop. */ for (const int i : IndexRange(i_sample, samples_size - i_sample)) { samples[i] = float(samples_size); diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index 7f9e37f58d5..2bdceb5f3db 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -56,7 +56,7 @@ static void OVERLAY_engine_init(void *vedata) OVERLAY_shader_library_ensure(); if (!stl->pd) { - /* Alloc transient pointers */ + /* Allocate transient pointers. */ stl->pd = MEM_callocN(sizeof(*stl->pd), __func__); } @@ -235,7 +235,7 @@ BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bo *do_init = true; } else if ((*dupli_data)->base_flag != ob->base_flag) { - /* Select state might have change, reinit. */ + /* Select state might have change, reinitialize. */ *do_init = true; } return *dupli_data; @@ -465,7 +465,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) break; case OB_LATTICE: { /* Unlike the other types above, lattices actually have a bounding box defined, so hide the - * lattice wires if only the boundingbox is requested. */ + * lattice wires if only the bounding-box is requested. */ if (ob->dt > OB_BOUNDBOX) { OVERLAY_lattice_cache_populate(vedata, ob); } @@ -478,7 +478,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) OVERLAY_particle_cache_populate(vedata, ob); } - /* Relationship, object center, bounbox ... */ + /* Relationship, object center, bounding-box... etc. */ if (!pd->hide_overlays) { OVERLAY_extra_cache_populate(vedata, ob); } @@ -581,7 +581,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_extra_blend_draw(vedata); OVERLAY_volume_draw(vedata); - /* These overlays are drawn here to avoid artifacts with wireframe opacity. */ + /* These overlays are drawn here to avoid artifacts with wire-frame opacity. */ switch (pd->ctx_mode) { case CTX_MODE_SCULPT: OVERLAY_sculpt_draw(vedata); diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index e04dd7f41bd..26a5dc9d60f 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -290,7 +290,7 @@ class NodeTreeRef : NonCopyable, NonMovable { struct ToposortResult { Vector sorted_nodes; /** - * There can't be a correct topologycal sort of the nodes when there is a cycle. The nodes will + * There can't be a correct topological sort of the nodes when there is a cycle. The nodes will * still be sorted to some degree. The caller has to decide whether it can handle non-perfect * sorts or not. */ From 68759625b113cb190e96024fec5f397da61be0d0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 3 Nov 2021 15:43:32 +1100 Subject: [PATCH 1478/1500] Fix T92053: Industry compatible key-map fallback tools don't work Caused by c9d9bfa84ad5cb985e3feccffa702b2f3cc2adf8. Support for fallback tools with right-click select needed the industry compatible key-map to be updated. --- .../keymap_data/industry_compatible_data.py | 47 +++++++++++++------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 0ae64dbc62e..37cd554e872 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -4203,20 +4203,29 @@ def keymap_transform_tool_mmb(keymap): km_items_new = [] for kmi in km_items: ty = kmi[1]["type"] - if ty == 'LEFTMOUSE': - kmi = (kmi[0], kmi[1].copy(), kmi[2]) - kmi[1]["type"] = 'MIDDLEMOUSE' - km_items_new.append(kmi) - elif ty == 'EVT_TWEAK_L': - kmi = (kmi[0], kmi[1].copy(), kmi[2]) - if kmi[1]["value"] == 'ANY': + if km_name.endswith(" (fallback)"): + if ty == 'RIGHTMOUSE': + kmi = (kmi[0], kmi[1].copy(), kmi[2]) + kmi[1]["type"] = 'LEFTMOUSE' + km_items_new.append(kmi) + elif ty == 'EVT_TWEAK_R': + kmi = (kmi[0], kmi[1].copy(), kmi[2]) + kmi[1]["type"] = 'EVT_TWEAK_L' + km_items_new.append(kmi) + else: + if ty == 'LEFTMOUSE': + kmi = (kmi[0], kmi[1].copy(), kmi[2]) kmi[1]["type"] = 'MIDDLEMOUSE' - kmi[1]["value"] = 'PRESS' - else: - # Directional tweaking can't be replaced by middle-mouse. - kmi[1]["type"] = 'EVT_TWEAK_M' - - km_items_new.append(kmi) + km_items_new.append(kmi) + elif ty == 'EVT_TWEAK_L': + kmi = (kmi[0], kmi[1].copy(), kmi[2]) + if kmi[1]["value"] == 'ANY': + kmi[1]["type"] = 'MIDDLEMOUSE' + kmi[1]["value"] = 'PRESS' + else: + # Directional tweaking can't be replaced by middle-mouse. + kmi[1]["type"] = 'EVT_TWEAK_M' + km_items_new.append(kmi) km_items.extend(km_items_new) @@ -4227,9 +4236,17 @@ def generate_keymaps(params=None): # Combine the key-map to support manipulating it, so we don't need to manually # define key-map here just to manipulate them. - blender_default = execfile( + blender_default_mod = execfile( os.path.join(os.path.dirname(__file__), "blender_default.py"), - ).generate_keymaps() + ) + + blender_default = blender_default_mod.generate_keymaps( + # Use the default key-map with only minor changes to default arguments. + blender_default_mod.Params( + # Needed so the fallback key-map items are populated. + use_fallback_tool=True, + ), + ) keymap_existing_names = {km[0] for km in keymap} keymap.extend([km for km in blender_default if km[0] not in keymap_existing_names]) From 2b12b4cd7dccfa51cec53d5ae885a2142563fb17 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 3 Nov 2021 10:42:48 +0100 Subject: [PATCH 1479/1500] Fix: make sure geometry owns mesh before taking ownership Differential Revision: https://developer.blender.org/D13075 --- source/blender/blenkernel/intern/DerivedMesh.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 3ec0ab9c512..4b1332d5b84 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -921,6 +921,10 @@ static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md, /* Release the mesh from the geometry set again. */ if (geometry_set.has()) { MeshComponent &mesh_component = geometry_set.get_component_for_write(); + if (mesh_component.get_for_read() != input_mesh) { + /* Make sure the mesh component actually owns the mesh before taking over ownership. */ + mesh_component.ensure_owns_direct_data(); + } mesh_output = mesh_component.release(); } From b55bddde40db3eda3531d98caa99be9a8e88a8ee Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 3 Nov 2021 10:54:17 +0100 Subject: [PATCH 1480/1500] Fix T91862: do type conversion when data enters or exists node group The geometry node evaluator now has access to the entire socket path from the node that produces a value to the node that uses it. This allows the evaluator to make decisions about at which points in the path the value should be converted. Multiple conversions may be necessary under some circumstances with nested node groups. Differential Revision: https://developer.blender.org/D13034 --- .../modifiers/intern/MOD_nodes_evaluator.cc | 111 +++++++++--------- source/blender/nodes/NOD_derived_node_tree.hh | 15 ++- .../blender/nodes/intern/derived_node_tree.cc | 95 +++++++++++---- 3 files changed, 137 insertions(+), 84 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index a312872f5d9..70d2bd9c7f5 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -568,15 +568,15 @@ class GeometryNodesEvaluator { } /* Count the number of potential users for this socket. */ socket.foreach_target_socket( - [&, this](const DInputSocket target_socket) { + [&, this](const DInputSocket target_socket, + const DOutputSocket::TargetSocketPathInfo &UNUSED(path_info)) { const DNode target_node = target_socket.node(); if (!this->node_states_.contains_as(target_node)) { /* The target node is not computed because it is not computed to the output. */ return; } output_state.potential_users += 1; - }, - {}); + }); if (output_state.potential_users == 0) { /* If it does not have any potential users, it is unused. It might become required again in * `schedule_initial_nodes`. */ @@ -1257,43 +1257,61 @@ class GeometryNodesEvaluator { { BLI_assert(value_to_forward.get() != nullptr); - Vector sockets_to_log_to; - sockets_to_log_to.append(from_socket); - - Vector to_sockets; - auto handle_target_socket_fn = [&, this](const DInputSocket to_socket) { - if (this->should_forward_to_socket(to_socket)) { - to_sockets.append(to_socket); - } - }; - auto handle_skipped_socket_fn = [&](const DSocket socket) { - sockets_to_log_to.append(socket); - }; - from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn); - LinearAllocator<> &allocator = local_allocators_.local(); - const CPPType &from_type = *value_to_forward.type(); - Vector to_sockets_same_type; - for (const DInputSocket &to_socket : to_sockets) { - const CPPType &to_type = *get_socket_cpp_type(to_socket); - if (from_type == to_type) { - /* All target sockets that do not need a conversion will be handled afterwards. */ - to_sockets_same_type.append(to_socket); - /* Multi input socket values are logged once all values are available. */ - if (!to_socket->is_multi_input_socket()) { - sockets_to_log_to.append(to_socket); - } - continue; - } - this->forward_to_socket_with_different_type( - allocator, value_to_forward, from_socket, to_socket, to_type); - } - - this->log_socket_value(sockets_to_log_to, value_to_forward); + Vector log_original_value_sockets; + Vector forward_original_value_sockets; + log_original_value_sockets.append(from_socket); + from_socket.foreach_target_socket( + [&](const DInputSocket to_socket, const DOutputSocket::TargetSocketPathInfo &path_info) { + if (!this->should_forward_to_socket(to_socket)) { + return; + } + BLI_assert(to_socket == path_info.sockets.last()); + GMutablePointer current_value = value_to_forward; + for (const DSocket &next_socket : path_info.sockets) { + const DNode next_node = next_socket.node(); + const bool is_last_socket = to_socket == next_socket; + const bool do_conversion_if_necessary = is_last_socket || + next_node->is_group_output_node() || + (next_node->is_group_node() && + !next_node->is_muted()); + if (do_conversion_if_necessary) { + const CPPType &next_type = *get_socket_cpp_type(next_socket); + if (*current_value.type() != next_type) { + void *buffer = allocator.allocate(next_type.size(), next_type.alignment()); + this->convert_value(*current_value.type(), next_type, current_value.get(), buffer); + if (current_value.get() != value_to_forward.get()) { + current_value.destruct(); + } + current_value = {next_type, buffer}; + } + } + if (current_value.get() == value_to_forward.get()) { + /* Log the original value at the current socket. */ + log_original_value_sockets.append(next_socket); + } + else { + /* Multi-input sockets are logged when all values are available. */ + if (!(next_socket->is_input() && next_socket->as_input().is_multi_input_socket())) { + /* Log the converted value at the socket. */ + this->log_socket_value({next_socket}, current_value); + } + } + } + if (current_value.get() == value_to_forward.get()) { + /* The value has not been converted, so forward the original value. */ + forward_original_value_sockets.append(to_socket); + } + else { + /* The value has been converted. */ + this->add_value_to_input_socket(to_socket, from_socket, current_value); + } + }); + this->log_socket_value(log_original_value_sockets, value_to_forward); this->forward_to_sockets_with_same_type( - allocator, to_sockets_same_type, value_to_forward, from_socket); + allocator, forward_original_value_sockets, value_to_forward, from_socket); } bool should_forward_to_socket(const DInputSocket socket) @@ -1312,27 +1330,6 @@ class GeometryNodesEvaluator { return target_input_state.usage != ValueUsage::Unused; } - void forward_to_socket_with_different_type(LinearAllocator<> &allocator, - const GPointer value_to_forward, - const DOutputSocket from_socket, - const DInputSocket to_socket, - const CPPType &to_type) - { - const CPPType &from_type = *value_to_forward.type(); - - /* Allocate a buffer for the converted value. */ - void *buffer = allocator.allocate(to_type.size(), to_type.alignment()); - GMutablePointer value{to_type, buffer}; - - this->convert_value(from_type, to_type, value_to_forward.get(), buffer); - - /* Multi input socket values are logged once all values are available. */ - if (!to_socket->is_multi_input_socket()) { - this->log_socket_value({to_socket}, value); - } - this->add_value_to_input_socket(to_socket, from_socket, value); - } - void forward_to_sockets_with_same_type(LinearAllocator<> &allocator, Span to_sockets, GMutablePointer value_to_forward, diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index e903e3c9255..895f7ef6d5b 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -158,8 +158,19 @@ class DOutputSocket : public DSocket { DInputSocket get_corresponding_group_node_input() const; DInputSocket get_active_corresponding_group_output_socket() const; - void foreach_target_socket(FunctionRef target_fn, - FunctionRef skipped_fn) const; + struct TargetSocketPathInfo { + /** All sockets on the path from the current to the final target sockets, excluding `this`. */ + Vector sockets; + }; + + using ForeachTargetSocketFn = + FunctionRef; + + void foreach_target_socket(ForeachTargetSocketFn target_fn) const; + + private: + void foreach_target_socket(ForeachTargetSocketFn target_fn, + TargetSocketPathInfo &path_info) const; }; class DerivedNodeTree { diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index f7279cf7524..fb12157f147 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -231,45 +231,90 @@ void DInputSocket::foreach_origin_socket(FunctionRef origin_fn) c /* Calls `target_fn` for every "real" target socket. "Real" means that reroutes, muted nodes * and node groups are handled by this function. Target sockets are on the nodes that use the value - * from this socket. The `skipped_fn` function is called for sockets that have been skipped during - * the search for target sockets (e.g. reroutes). */ -void DOutputSocket::foreach_target_socket(FunctionRef target_fn, - FunctionRef skipped_fn) const + * from this socket. */ +void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn) const { - for (const SocketRef *skipped_socket : socket_ref_->logically_linked_skipped_sockets()) { - skipped_fn.call_safe({context_, skipped_socket}); - } - for (const InputSocketRef *linked_socket : socket_ref_->as_output().logically_linked_sockets()) { - const NodeRef &linked_node = linked_socket->node(); - DInputSocket linked_dsocket{context_, linked_socket}; + TargetSocketPathInfo path_info; + this->foreach_target_socket(target_fn, path_info); +} - if (linked_node.is_group_output_node()) { +void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn, + TargetSocketPathInfo &path_info) const +{ + for (const LinkRef *link : socket_ref_->as_output().directly_linked_links()) { + if (link->is_muted()) { + continue; + } + const DInputSocket &linked_socket{context_, &link->to()}; + if (!linked_socket->is_available()) { + continue; + } + const DNode linked_node = linked_socket.node(); + if (linked_node->is_reroute_node()) { + const DInputSocket reroute_input = linked_socket; + const DOutputSocket reroute_output = linked_node.output(0); + path_info.sockets.append(reroute_input); + path_info.sockets.append(reroute_output); + reroute_output.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); + path_info.sockets.pop_last(); + } + else if (linked_node->is_muted()) { + for (const InternalLinkRef *internal_link : linked_node->internal_links()) { + if (&internal_link->from() != linked_socket.socket_ref()) { + continue; + } + /* The internal link only forwards the first incoming link. */ + if (linked_socket->is_multi_input_socket()) { + if (linked_socket->directly_linked_links()[0] == link) { + continue; + } + } + const DInputSocket mute_input = linked_socket; + const DOutputSocket mute_output{context_, &internal_link->to()}; + path_info.sockets.append(mute_input); + path_info.sockets.append(mute_output); + mute_output.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); + path_info.sockets.pop_last(); + break; + } + } + else if (linked_node->is_group_output_node()) { if (context_->is_root()) { /* This is a group output in the root node group. */ - target_fn(linked_dsocket); + path_info.sockets.append(linked_socket); + target_fn(linked_socket, path_info); + path_info.sockets.pop_last(); } else { /* Follow the links going out of the group node in the parent node group. */ - DOutputSocket socket_in_parent_group = - linked_dsocket.get_corresponding_group_node_output(); - skipped_fn.call_safe(linked_dsocket); - skipped_fn.call_safe(socket_in_parent_group); - socket_in_parent_group.foreach_target_socket(target_fn, skipped_fn); + const DOutputSocket socket_in_parent_group = + linked_socket.get_corresponding_group_node_output(); + path_info.sockets.append(linked_socket); + path_info.sockets.append(socket_in_parent_group); + socket_in_parent_group.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); + path_info.sockets.pop_last(); } } - else if (linked_node.is_group_node()) { + else if (linked_node->is_group_node()) { /* Follow the links within the nested node group. */ - Vector sockets_in_group = - linked_dsocket.get_corresponding_group_input_sockets(); - skipped_fn.call_safe(linked_dsocket); - for (DOutputSocket socket_in_group : sockets_in_group) { - skipped_fn.call_safe(socket_in_group); - socket_in_group.foreach_target_socket(target_fn, skipped_fn); + path_info.sockets.append(linked_socket); + const Vector sockets_in_group = + linked_socket.get_corresponding_group_input_sockets(); + for (const DOutputSocket &socket_in_group : sockets_in_group) { + path_info.sockets.append(socket_in_group); + socket_in_group.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); } + path_info.sockets.pop_last(); } else { /* The normal case: just use the linked input socket as target. */ - target_fn(linked_dsocket); + path_info.sockets.append(linked_socket); + target_fn(linked_socket, path_info); + path_info.sockets.pop_last(); } } } From 5095e4fc22cedd9289e403d841e9561bc99236ab Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 1 Nov 2021 16:57:09 +0100 Subject: [PATCH 1481/1500] UI: Display disabled-hint when dragging material outside object mode Display a "disabled hint" (text explaining why something isn't possible) when dragging a material over the 3D View, while being in edit mode or so (anything that isn't object mode). --- source/blender/editors/object/object_relations.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 556ddb78653..b51644eebf3 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2656,7 +2656,7 @@ void OBJECT_OT_drop_named_material(wmOperatorType *ot) /* api callbacks */ ot->invoke = drop_named_material_invoke; - ot->poll = ED_operator_objectmode; + ot->poll = ED_operator_objectmode_poll_msg; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; From 42d0107ee5802d3e473c7af8880021078e6d13e3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 3 Nov 2021 22:12:42 +1100 Subject: [PATCH 1482/1500] Fix crash dissolving overlapping faces In rare cases disolving faces would crash, caused by iterator variable reuse in b29a8a5dfe3d6eb2fbbdecd0d5dffb3d709b9b91. --- source/blender/bmesh/operators/bmo_dissolve.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c index efba0ec99ec..360dcc2c79e 100644 --- a/source/blender/bmesh/operators/bmo_dissolve.c +++ b/source/blender/bmesh/operators/bmo_dissolve.c @@ -233,8 +233,8 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op) * This could optionally do a partial merge, where some faces are joined. */ /* Prevent these faces from being removed. */ - for (i = 0; i < faces_len; i++) { - BMO_face_flag_disable(bm, faces[i], FACE_ORIG); + for (int j = 0; j < faces_len; j++) { + BMO_face_flag_disable(bm, faces[j], FACE_ORIG); } } } From f81190f85f65b292a0f0dfaa0a8d5b0f7fd72c58 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 3 Nov 2021 10:21:56 -0300 Subject: [PATCH 1483/1500] Fix T92760: Crash erasing GPencil when occlusion test is enabled `pt0` was read when `NULL` and `is_occluded_pt1` could be read even if it is not initialized. --- source/blender/editors/gpencil/gpencil_paint.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 070470b3734..3e9f22f25d3 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1582,10 +1582,12 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, */ if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) { - bool is_occluded_pt0, is_occluded_pt1, is_occluded_pt2 = true; - is_occluded_pt0 = (pt0 && ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0)) ? - ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) : - gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]); + bool is_occluded_pt0 = true, is_occluded_pt1 = true, is_occluded_pt2 = true; + if (pt0) { + is_occluded_pt0 = ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0) ? + ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) : + gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]); + } if (is_occluded_pt0) { is_occluded_pt1 = ((pt1->flag & GP_SPOINT_TEMP_TAG) != 0) ? ((pt1->flag & GP_SPOINT_TEMP_TAG2) != 0) : From 0d8f1414a21a9c7a3498fd65ce5cd02673582eed Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 3 Nov 2021 14:17:26 +0100 Subject: [PATCH 1484/1500] Fix access to current preferences when version patching read preferences The version patch for 0cf9794c7ef0 was checking and setting a data name using the macros for translation. These would access the current preferences which can mismatch the ones currently being version patched. See discussion in 0cf9794c7ef0 for details. Don't handle translation in this version patch, which is more of a "nice-to-have" version patch, no functionality depends on it. --- source/blender/blenkernel/BKE_preferences.h | 4 ++-- source/blender/blenkernel/intern/preferences.c | 2 +- source/blender/blenloader/intern/versioning_userdef.c | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index fce2d3178aa..fd4d13f4125 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -29,8 +29,8 @@ extern "C" { struct UserDef; struct bUserAssetLibrary; -/** Name of the asset library added by default. */ -#define BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME DATA_("User Library") +/** Name of the asset library added by default. Needs translation with `DATA_()` still. */ +#define BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME N_("User Library") struct bUserAssetLibrary *BKE_preferences_asset_library_add(struct UserDef *userdef, const char *name, diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c index 79a8b591f72..0a10601f751 100644 --- a/source/blender/blenkernel/intern/preferences.c +++ b/source/blender/blenkernel/intern/preferences.c @@ -121,7 +121,7 @@ void BKE_preferences_asset_library_default_add(UserDef *userdef) } bUserAssetLibrary *library = BKE_preferences_asset_library_add( - userdef, BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME, NULL); + userdef, DATA_(BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME), NULL); /* Add new "Default" library under '[doc_path]/Blender/Assets'. */ BLI_path_join( diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 4234570af6c..3c470a7d3df 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -927,9 +927,13 @@ void blo_do_versions_userdef(UserDef *userdef) } if (!USER_VERSION_ATLEAST(300, 40)) { - /* Rename the default asset library from "Default" to "User Library" */ + /* Rename the default asset library from "Default" to "User Library". This isn't bullet proof + * since it doesn't handle translations and ignores user changes. But this was an alpha build + * (experimental) feature and the name is just for display in the UI anyway. So it doesn't have + * to work perfectly at all. */ LISTBASE_FOREACH (bUserAssetLibrary *, asset_library, &userdef->asset_libraries) { - if (STREQ(asset_library->name, DATA_("Default"))) { + /* Ignores translations, since that would depend on the current preferences (global `U`). */ + if (STREQ(asset_library->name, "Default")) { BKE_preferences_asset_library_name_set( userdef, asset_library, BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME); } From b73993bcc942cafb8ed4a4610d64286505bb4e11 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 27 Oct 2021 15:50:14 +0200 Subject: [PATCH 1485/1500] UI: Refactor how dragging onto text buttons works, fixing issues There was a bunch of special handling to support dropping data-blocks onto string or search-menu buttons, to change the value of these. This refactor makes that case use the normal drop-box design, where an operator is executed on drop that gets input properties set by the drop-box. This should also make it easier to add support for dragging assets into these buttons. In addition this fixes an issue: Two tooltips were shown when dragging assets over text buttons. None should be shown, because this isn't supported. --- source/blender/editors/include/UI_interface.h | 3 +- .../editors/interface/interface_dropboxes.cc | 23 +++++ .../editors/interface/interface_handlers.c | 85 +++++++------------ .../editors/interface/interface_intern.h | 1 + .../blender/editors/interface/interface_ops.c | 34 ++++++++ .../windowmanager/intern/wm_dragdrop.c | 6 +- 6 files changed, 93 insertions(+), 59 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 39d70f15a3a..725c9921d13 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -788,7 +788,8 @@ void UI_but_drag_set_value(uiBut *but); void UI_but_drag_set_image( uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free); -bool UI_but_active_drop_name(struct bContext *C); +uiBut *UI_but_active_drop_name_button(const struct bContext *C); +bool UI_but_active_drop_name(const struct bContext *C); bool UI_but_active_drop_color(struct bContext *C); void UI_but_flag_enable(uiBut *but, int flag); diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index ab0c7e088e2..81a1354cbe7 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -24,6 +24,8 @@ #include "MEM_guardedalloc.h" +#include "RNA_access.h" + #include "WM_api.h" #include "UI_interface.h" @@ -59,6 +61,21 @@ static char *ui_tree_view_drop_tooltip(bContext *C, return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag); } +/* ---------------------------------------------------------------------- */ + +static bool ui_drop_name_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event)) +{ + return UI_but_active_drop_name(C) && (drag->type == WM_DRAG_ID); +} + +static void ui_drop_name_copy(wmDrag *drag, wmDropBox *drop) +{ + const ID *id = WM_drag_get_local_ID(drag, 0); + RNA_string_set(drop->ptr, "string", id->name + 2); +} + +/* ---------------------------------------------------------------------- */ + void ED_dropboxes_ui() { ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0); @@ -69,4 +86,10 @@ void ED_dropboxes_ui() nullptr, nullptr, ui_tree_view_drop_tooltip); + WM_dropbox_add(lb, + "UI_OT_drop_name", + ui_drop_name_poll, + ui_drop_name_copy, + WM_drag_free_imported_drag_ID, + nullptr); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 6dd6f9eb359..51ebe5399b3 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2442,39 +2442,6 @@ static void ui_apply_but( /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Button Drop Event - * \{ */ - -/* only call if event type is EVT_DROP */ -static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleButtonData *data) -{ - ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */ - - LISTBASE_FOREACH (wmDrag *, wmd, drags) { - /* TODO: asset dropping. */ - if (wmd->type == WM_DRAG_ID) { - /* align these types with UI_but_active_drop_name */ - if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - ID *id = WM_drag_get_local_ID(wmd, 0); - - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - - ui_textedit_string_set(but, data, id->name + 2); - - if (ELEM(but->type, UI_BTYPE_SEARCH_MENU)) { - but->changed = true; - ui_searchbox_update(C, data->searchbox, but, true); - } - - button_activate_state(C, but, BUTTON_STATE_EXIT); - } - } - } -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Button Copy & Paste * \{ */ @@ -2672,15 +2639,9 @@ static void ui_but_copy_text(uiBut *but, char *output, int output_len_max) static void ui_but_paste_text(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste) { - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - ui_textedit_string_set(but, but->active, buf_paste); - - if (but->type == UI_BTYPE_SEARCH_MENU) { - but->changed = true; - ui_searchbox_update(C, data->searchbox, but, true); - } - - button_activate_state(C, but, BUTTON_STATE_EXIT); + BLI_assert(but->active == data); + UNUSED_VARS_NDEBUG(data); + ui_but_set_string_interactive(C, but, buf_paste); } static void ui_but_copy_colorband(uiBut *but) @@ -3024,6 +2985,24 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], /** \name Button Text Selection/Editing * \{ */ +/** + * Use handling code to set a string for the button. Handles the case where the string is set for a + * search button while the search menu is open, so the results are updated accordingly. + * This is basically the same as pasting the string into the button. + */ +void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value) +{ + button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); + ui_textedit_string_set(but, but->active, value); + + if (but->type == UI_BTYPE_SEARCH_MENU && but->active) { + but->changed = true; + ui_searchbox_update(C, but->active->searchbox, but, true); + } + + button_activate_state(C, but, BUTTON_STATE_EXIT); +} + void ui_but_active_string_clear_and_exit(bContext *C, uiBut *but) { if (!but->active) { @@ -7949,7 +7928,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* Only hard-coded stuff here, button interactions with configurable * keymaps are handled using operators (see #ED_keymap_ui). */ - if ((data->state == BUTTON_STATE_HIGHLIGHT) || (event->type == EVT_DROP)) { + if (data->state == BUTTON_STATE_HIGHLIGHT) { /* handle copy and paste */ bool is_press_ctrl_but_no_shift = event->val == KM_PRESS && IS_EVENT_MOD(event, ctrl, oskey) && @@ -7998,11 +7977,6 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * return WM_UI_HANDLER_BREAK; } - /* handle drop */ - if (event->type == EVT_DROP) { - ui_but_drop(C, event, but, data); - } - if ((data->state == BUTTON_STATE_HIGHLIGHT) && ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, EVT_PADENTER, EVT_RETKEY) && (event->val == KM_RELEASE) && @@ -11716,20 +11690,25 @@ void UI_screen_free_active_but(const bContext *C, bScreen *screen) } } -/* returns true if highlighted button allows drop of names */ -/* called in region context */ -bool UI_but_active_drop_name(bContext *C) +uiBut *UI_but_active_drop_name_button(const bContext *C) { ARegion *region = CTX_wm_region(C); uiBut *but = ui_region_find_active_but(region); if (but) { if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - return true; + return but; } } - return false; + return NULL; +} + +/* returns true if highlighted button allows drop of names */ +/* called in region context */ +bool UI_but_active_drop_name(const bContext *C) +{ + return UI_but_active_drop_name_button(C) != NULL; } bool UI_but_active_drop_color(bContext *C) diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 28227c2331a..f766bb1465f 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -680,6 +680,7 @@ extern bool ui_but_string_eval_number(struct bContext *C, extern int ui_but_string_get_max_length(uiBut *but); /* Clear & exit the active button's string. */ extern void ui_but_active_string_clear_and_exit(struct bContext *C, uiBut *but) ATTR_NONNULL(); +extern void ui_but_set_string_interactive(struct bContext *C, uiBut *but, const char *value); extern uiBut *ui_but_drag_multi_edit_get(uiBut *but); void ui_def_but_icon(uiBut *but, const int icon, const int flag); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 1a1d52b0425..c962a1107ae 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1863,6 +1863,39 @@ static void UI_OT_drop_color(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Drop Name Operator + * \{ */ + +static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + uiBut *but = UI_but_active_drop_name_button(C); + char *str = RNA_string_get_alloc(op->ptr, "string", NULL, 0, NULL); + + if (str) { + ui_but_set_string_interactive(C, but, str); + MEM_freeN(str); + } + + return OPERATOR_FINISHED; +} + +static void UI_OT_drop_name(wmOperatorType *ot) +{ + ot->name = "Drop Name"; + ot->idname = "UI_OT_drop_name"; + ot->description = "Drop name to button"; + + ot->poll = ED_operator_regionactive; + ot->invoke = drop_name_invoke; + ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_string( + ot->srna, "string", NULL, 0, "String", "The string value to drop into the button"); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name UI List Search Operator * \{ */ @@ -2025,6 +2058,7 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_copy_to_selected_button); WM_operatortype_append(UI_OT_jump_to_target_button); WM_operatortype_append(UI_OT_drop_color); + WM_operatortype_append(UI_OT_drop_name); #ifdef WITH_PYTHON WM_operatortype_append(UI_OT_editsource); WM_operatortype_append(UI_OT_edittranslation_init); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index df6d3d5e9e7..37a101cc31d 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -821,10 +821,7 @@ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const const char *tooltip = NULL; bool free_tooltip = false; - if (UI_but_active_drop_name(C)) { - tooltip = IFACE_("Paste name"); - } - else if (drag->active_dropbox) { + if (drag->active_dropbox) { tooltip = dropbox_tooltip(C, drag, xy, drag->active_dropbox); free_tooltip = true; } @@ -907,7 +904,6 @@ void wm_drags_draw(bContext *C, wmWindow *win) xy[1] = win->eventstate->xy[1]; } - /* Set a region. It is used in the `UI_but_active_drop_name`. */ bScreen *screen = CTX_wm_screen(C); ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, UNPACK2(xy)); ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, UNPACK2(xy)); From 8b516d8712024af9380fe3f7559c336042d612f5 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 3 Nov 2021 12:05:43 +0100 Subject: [PATCH 1486/1500] Include node name for socket animation channel UI The channel names were often indistingushable in animation editors. Now include the node _name_ (unfortunately, getting the _label_ could result in bad performance in some circustances -- see previous version of D13085). Similar to what rB77744b581d08 did for some VSE strip properties. ref. T91917 Maniphest Tasks: T91917 Differential Revision: https://developer.blender.org/D13085 --- .../blender/editors/animation/anim_ipo_utils.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index 6fe32699907..05837ed17b9 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -145,6 +145,22 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) } } } + /* For node sockets, it is useful to include the node name as well (multiple similar nodes + * are not distinguishable otherwise). Unfortunately, the node label cannot be retrieved + * from the rna path, for this to work access to the underlying node is needed (but finding + * the node iterates all nodes & sockets which would result in bad performance in some + * circumstances). */ + if (RNA_struct_is_a(ptr.type, &RNA_NodeSocket)) { + char nodename[256]; + if (BLI_str_quoted_substr(fcu->rna_path, "nodes[", nodename, sizeof(nodename))) { + const char *structname_all = BLI_sprintfN("%s : %s", nodename, structname); + if (free_structname) { + MEM_freeN((void *)structname); + } + structname = structname_all; + free_structname = 1; + } + } } /* Property Name is straightforward */ From aa0ac0035a0d3601672a0c732e3f8f932a36fc04 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 2 Nov 2021 12:33:28 -0300 Subject: [PATCH 1487/1500] GPencil and Annotation: Use cached depth to perform depth testing operations Operations such as erasing with occlusion and drawing on the surface require reading the depth buffer. However, this is being done with minimal efficiency. Currently, to read the depth corresponding to each point of the new stroke, a ReadPixel is called to send a message to the GPU and read the depth of the corresponding pixel in the VRAM. The communication between GPU and CPU is known to be a slow operation so it is good to be avoided. Therefore, save the entire depth buffer in a cache to be read directly from the RAM. (Also the `ED_view3d_autodist_depth` and `ED_view3d_autodist_depth_seg` have been removed since they are no longer used). Reviewed By: antoniov, fclem Differential Revision: https://developer.blender.org/D10894 --- .../blender/editors/gpencil/annotate_paint.c | 20 ++++++++++++---- source/blender/editors/gpencil/gpencil_fill.c | 18 ++++++++++----- .../blender/editors/gpencil/gpencil_intern.h | 2 ++ .../blender/editors/gpencil/gpencil_paint.c | 20 ++++++++++++---- .../editors/gpencil/gpencil_primitive.c | 14 +++++++---- .../blender/editors/gpencil/gpencil_utils.c | 2 +- source/blender/editors/include/ED_view3d.h | 8 ++----- .../editors/space_view3d/view3d_utils.c | 23 +++++++------------ 8 files changed, 65 insertions(+), 42 deletions(-) diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index ba603cdd6ec..bdb4e485373 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -123,6 +123,8 @@ typedef struct tGPsdata { ARegion *region; /** needed for GP_STROKE_2DSPACE. */ View2D *v2d; + /** For operations that require occlusion testing. */ + ViewDepths *depths; /** for using the camera rect within the 3d view. */ rctf *subrect; rctf subrect_data; @@ -972,12 +974,13 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p) depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_used, "depth_points"); + const ViewDepths *depths = p->depths; for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used; i++, ptc++, pt++) { round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(p->region, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg( - p->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1086,7 +1089,10 @@ static bool annotation_stroke_eraser_is_occluded(tGPsdata *p, const int mval_i[2] = {x, y}; float mval_3d[3]; - if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) { + float p_depth; + if (ED_view3d_depth_read_cached(p->depths, mval_i, 0, &p_depth)) { + ED_view3d_depth_unproject_v3(p->region, mval_i, (double)p_depth, mval_3d); + const float depth_mval = ED_view3d_calc_depth_for_comparison(rv3d, mval_3d); const float depth_pt = ED_view3d_calc_depth_for_comparison(rv3d, &pt->x); @@ -1211,7 +1217,8 @@ static void annotation_stroke_doeraser(tGPsdata *p) if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) { View3D *v3d = p->area->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); + ED_view3d_depth_override( + p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, &p->depths); } } @@ -1499,6 +1506,9 @@ static void annotation_session_cleanup(tGPsdata *p) static void annotation_session_free(tGPsdata *p) { + if (p->depths) { + ED_view3d_depths_free(p->depths); + } MEM_freeN(p); } diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 1b69947b294..9860c75f290 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -127,6 +127,8 @@ typedef struct tGPDfill { struct bGPDstroke *gps_mouse; /** Pointer to report messages. */ struct ReportList *reports; + /** For operations that require occlusion testing. */ + struct ViewDepths *depths; /** flags */ short flag; /** avoid too fast events */ @@ -1374,7 +1376,7 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) /* need to restore the original projection settings before packing up */ view3d_region_operator_needs_opengl(tgpf->win, tgpf->region); ED_view3d_depth_override( - tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); + tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, &tgpf->depths); /* Since strokes are so fine, when using their depth we need a margin * otherwise they might get missed. */ @@ -1385,6 +1387,7 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) int interp_depth = 0; int found_depth = 0; + const ViewDepths *depths = tgpf->depths; tgpf->depth_arr = MEM_mallocN(sizeof(float) * totpoints, "depth_points"); for (i = 0, ptc = tgpf->sbuffer; i < totpoints; i++, ptc++) { @@ -1392,11 +1395,9 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) int mval_i[2]; round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(tgpf->region, mval_i, depth_margin, tgpf->depth_arr + i) == - 0) && - (i && - (ED_view3d_autodist_depth_seg( - tgpf->region, mval_i, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, tgpf->depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1771,6 +1772,11 @@ static void gpencil_fill_exit(bContext *C, wmOperator *op) ED_region_draw_cb_exit(tgpf->region->type, tgpf->draw_handle_3d); } + /* Remove depth buffer in cache. */ + if (tgpf->depths) { + ED_view3d_depths_free(tgpf->depths); + } + /* finally, free memory used by temp data */ MEM_freeN(tgpf); } diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index b6730cb123b..3f3fd4fff39 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -155,6 +155,8 @@ typedef struct tGPDprimitive { struct Material *material; /** current brush */ struct Brush *brush; + /** For operations that require occlusion testing. */ + struct ViewDepths *depths; /** Settings to pass to gp_points_to_xy(). */ GP_SpaceConversion gsc; diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 3e9f22f25d3..f37f2cd549f 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -161,6 +161,8 @@ typedef struct tGPsdata { ARegion *region; /** needed for GP_STROKE_2DSPACE. */ View2D *v2d; + /** For operations that require occlusion testing. */ + ViewDepths *depths; /** for using the camera rect within the 3d view. */ rctf *subrect; rctf subrect_data; @@ -1090,14 +1092,16 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) int found_depth = 0; depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_used, "depth_points"); + + const ViewDepths *depths = p->depths; int i; for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used; i++, ptc++, pt++) { round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(p->region, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg( - p->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1346,7 +1350,10 @@ static bool gpencil_stroke_eraser_is_occluded( /* calculate difference matrix if parent object */ BKE_gpencil_layer_transform_matrix_get(p->depsgraph, obact, gpl, diff_mat); - if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) { + float p_depth; + if (ED_view3d_depth_read_cached(p->depths, mval_i, 0, &p_depth)) { + ED_view3d_depth_unproject_v3(p->region, mval_i, (double)p_depth, mval_3d); + const float depth_mval = ED_view3d_calc_depth_for_comparison(rv3d, mval_3d); mul_v3_m4v3(fpt, diff_mat, &pt->x); @@ -1733,7 +1740,7 @@ static void gpencil_stroke_doeraser(tGPsdata *p) if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { View3D *v3d = p->area->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); + ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, &p->depths); } /* loop over all layers too, since while it's easy to restrict editing to @@ -2087,6 +2094,9 @@ static void gpencil_session_free(tGPsdata *p) if (p->rng != NULL) { BLI_rng_free(p->rng); } + if (p->depths != NULL) { + ED_view3d_depths_free(p->depths); + } MEM_freeN(p); } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index f8cfc130e35..7382aca9a87 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -795,15 +795,16 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? V3D_DEPTH_GPENCIL_ONLY : V3D_DEPTH_NO_GPENCIL, - NULL); + &tgpi->depths); depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points"); + const ViewDepths *depths = tgpi->depths; tGPspoint *ptc = &points2D[0]; for (int i = 0; i < gps->totpoints; i++, ptc++) { round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(tgpi->region, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg( - tgpi->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1154,6 +1155,11 @@ static void gpencil_primitive_exit(bContext *C, wmOperator *op) BLI_rng_free(tgpi->rng); } + /* Remove depth buffer in cache. */ + if (tgpi->depths) { + ED_view3d_depths_free(tgpi->depths); + } + MEM_freeN(tgpi); } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index d3640c6eebd..c4319fd7ee2 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -979,7 +979,7 @@ bool gpencil_point_xy_to_3d(const GP_SpaceConversion *gsc, * to 3D coordinates. * * \param point2D: The screen-space 2D point data to convert. - * \param depth: Depth array (via #ED_view3d_autodist_depth()). + * \param depth: Depth array (via #ED_view3d_depth_read_cached()). * \param r_out: The resulting 2D point data. */ void gpencil_stroke_convertcoords_tpoint(Scene *scene, diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index ebf3316a509..f9ccd916327 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -609,12 +609,8 @@ bool ED_view3d_autodist_simple(struct ARegion *region, float mouse_worldloc[3], int margin, const float *force_depth); -bool ED_view3d_autodist_depth(struct ARegion *region, const int mval[2], int margin, float *depth); -bool ED_view3d_autodist_depth_seg(struct ARegion *region, - const int mval_sta[2], - const int mval_end[2], - int margin, - float *depth); +bool ED_view3d_depth_read_cached_seg( + const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth); /* select */ #define MAXPICKELEMS 2500 diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index e09453b9957..dab1b55072a 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -1094,17 +1094,10 @@ bool ED_view3d_autodist_simple(ARegion *region, return ED_view3d_unproject_v3(region, centx, centy, depth, mouse_worldloc); } -bool ED_view3d_autodist_depth(ARegion *region, const int mval[2], int margin, float *depth) -{ - *depth = view_autodist_depth_margin(region, mval, margin); - - return (*depth != FLT_MAX); -} - static bool depth_segment_cb(int x, int y, void *userData) { struct { - ARegion *region; + const ViewDepths *vd; int margin; float depth; } *data = userData; @@ -1114,27 +1107,25 @@ static bool depth_segment_cb(int x, int y, void *userData) mval[0] = x; mval[1] = y; - depth = view_autodist_depth_margin(data->region, mval, data->margin); - - if (depth != FLT_MAX) { + if (ED_view3d_depth_read_cached(data->vd, mval, data->margin, &depth)) { data->depth = depth; return false; } return true; } -bool ED_view3d_autodist_depth_seg( - ARegion *region, const int mval_sta[2], const int mval_end[2], int margin, float *depth) +bool ED_view3d_depth_read_cached_seg( + const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth) { struct { - ARegion *region; + const ViewDepths *vd; int margin; float depth; } data = {NULL}; int p1[2]; int p2[2]; - data.region = region; + data.vd = vd; data.margin = margin; data.depth = FLT_MAX; @@ -1691,6 +1682,8 @@ bool ED_view3d_depth_read_cached(const ViewDepths *vd, return true; } + /* GPencil and Anotations also need the returned depth value to be high so that it is invalid. */ + *r_depth = FLT_MAX; return false; } From 2ecaa971f090810d08bee468d25c5ea0ab2563d3 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 3 Nov 2021 15:56:39 +0100 Subject: [PATCH 1488/1500] I18n: Fix several issues with UI messages extraction script. * Fix systematic skipping of labels when they are the same as the identifier (Some cases are valid, like `RGB` or `HSV` e.g.). * Add instead heuristics checks to skip non-UI properties (non-capitalized, or same name as identifier and Operator properties, mainly). * Skip `bl_icon` and `icon` properties. * Properly search for properties in all parent classes (some cases with e.g. `Panel` would break due to intermediary utils classes, leading to those internal UI properties not being skipped as expected). Related to T43295. --- .../bl_i18n_utils/bl_extract_messages.py | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 49dbac1d502..00edd7d523d 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -252,27 +252,50 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): # Function definitions def walk_properties(cls): + # This handles properties whose name is the same as their identifier. + # Usually, it means that those are internal properties not exposed in the UI, however there are some cases + # where the UI label is actually defined and same as the identifier (color spaces e.g., `RGB` etc.). + # So we only exclude those properties in case they belong to an operator for now. + def prop_name_validate(cls, prop_name, prop_identifier): + if prop_name != prop_identifier: + return True + # Heuristic: A lot of operator's HIDDEN properties have no UI label/description. + # While this is not ideal (for API doc purposes, description should always be provided), + # for now skip those properties. + # NOTE: keep in sync with C code in ui_searchbox_region_draw_cb__operator(). + if issubclass(cls, bpy.types.OperatorProperties) and "_OT_" in cls.__name__: + return False + # Heuristic: If UI label is not capitalized, it is likely a private (undocumented) property, + # that can be skipped. + if prop_name and not prop_name[0].isupper(): + return False + return True + bl_rna = cls.bl_rna # Get our parents' properties, to not export them multiple times. bl_rna_base = bl_rna.base + bl_rna_base_props = set() if bl_rna_base: - bl_rna_base_props = set(bl_rna_base.properties.values()) - else: - bl_rna_base_props = set() + bl_rna_base_props |= set(bl_rna_base.properties.values()) + for cls_base in cls.__bases__: + bl_rna_base = getattr(cls_base, "bl_rna", None) + if not bl_rna_base: + continue + bl_rna_base_props |= set(bl_rna_base.properties.values()) props = sorted(bl_rna.properties, key=lambda p: p.identifier) for prop in props: # Only write this property if our parent hasn't got it. if prop in bl_rna_base_props: continue - if prop.identifier == "rna_type": + if prop.identifier in {"rna_type", "bl_icon", "icon"}: continue reports["rna_props"].append((cls, prop)) msgsrc = "bpy.types.{}.{}".format(bl_rna.identifier, prop.identifier) msgctxt = prop.translation_context or default_context - if prop.name and (prop.name != prop.identifier or msgctxt != default_context): + if prop.name and prop_name_validate(cls, prop.name, prop.identifier): process_msg(msgs, msgctxt, prop.name, msgsrc, reports, check_ctxt_rna, settings) if prop.description: process_msg(msgs, default_context, prop.description, msgsrc, reports, check_ctxt_rna_tip, settings) @@ -282,7 +305,7 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): for item in prop.enum_items: msgsrc = "bpy.types.{}.{}:'{}'".format(bl_rna.identifier, prop.identifier, item.identifier) done_items.add(item.identifier) - if item.name and item.name != item.identifier: + if item.name and prop_name_validate(cls, item.name, item.identifier): process_msg(msgs, msgctxt, item.name, msgsrc, reports, check_ctxt_rna, settings) if item.description: process_msg(msgs, default_context, item.description, msgsrc, reports, check_ctxt_rna_tip, @@ -292,7 +315,7 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): continue msgsrc = "bpy.types.{}.{}:'{}'".format(bl_rna.identifier, prop.identifier, item.identifier) done_items.add(item.identifier) - if item.name and item.name != item.identifier: + if item.name and prop_name_validate(cls, item.name, item.identifier): process_msg(msgs, msgctxt, item.name, msgsrc, reports, check_ctxt_rna, settings) if item.description: process_msg(msgs, default_context, item.description, msgsrc, reports, check_ctxt_rna_tip, From ef30a876b5ef02ec875eba14683b9751abc71381 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 3 Nov 2021 16:11:36 +0100 Subject: [PATCH 1489/1500] I18n: Add some more acronyms to the spellchecker. --- release/scripts/modules/bl_i18n_utils/utils_spell_check.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index 6baf5129dd7..62186655326 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -689,11 +689,13 @@ class SpellChecker: "ctrl", "cw", "ccw", "dev", + "dls", "djv", "dpi", "dvar", "dx", "eo", + "ewa", "fh", "fk", "fov", @@ -733,6 +735,7 @@ class SpellChecker: "rhs", "rv", "sdl", + "sdls", "sl", "smpte", "ssao", @@ -767,6 +770,7 @@ class SpellChecker: "svbvh", # Files types/formats + "aac", "avi", "attrac", "autocad", @@ -789,6 +793,7 @@ class SpellChecker: "ico", "jpg", "jpeg", "jpegs", "json", + "lzw", "matroska", "mdd", "mkv", @@ -798,6 +803,7 @@ class SpellChecker: "openjpeg", "osl", "oso", + "pcm", "piz", "png", "pngs", "po", From ec5d2e687232887acde5cda9352828c59438da35 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 3 Nov 2021 16:12:13 +0100 Subject: [PATCH 1490/1500] Add documentation for some 'hidden' RNA properties. Even never-shown RNA properties should have at least a description, as this is used by API doc generation scripts. NOTE: this is more of an opportunistic set of changes than a proper complete fix of that loack of documentation. --- release/scripts/startup/bl_operators/file.py | 8 ++++++++ source/blender/makesrna/intern/rna_nodetree.c | 2 ++ source/blender/makesrna/intern/rna_object_force.c | 6 +++++- source/blender/makesrna/intern/rna_ui.c | 12 ++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/file.py b/release/scripts/startup/bl_operators/file.py index 672a4170325..4c53279427a 100644 --- a/release/scripts/startup/bl_operators/file.py +++ b/release/scripts/startup/bl_operators/file.py @@ -43,22 +43,30 @@ class WM_OT_previews_batch_generate(Operator): files: CollectionProperty( type=OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'}, + name="", + description="Collection of file paths with common `directory` root", ) directory: StringProperty( maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'}, + name="", + description="Root path of all files listed in `files` collection", ) # Show only images/videos, and directories! filter_blender: BoolProperty( default=True, options={'HIDDEN', 'SKIP_SAVE'}, + name="", + description="Show Blender files in the File Browser", ) filter_folder: BoolProperty( default=True, options={'HIDDEN', 'SKIP_SAVE'}, + name="", + description="Show folders in the File Browser", ) # ----------- diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 6a36ef07dee..01889a1b0a9 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -12773,6 +12773,8 @@ static void rna_def_nodetree(BlenderRNA *brna) prop = RNA_def_property(srna, "view_center", PROP_FLOAT, PROP_XYZ); RNA_def_property_array(prop, 2); RNA_def_property_float_sdna(prop, NULL, "view_center"); + RNA_def_property_ui_text( + prop, "", "The current location (offset) of the view for this Node Tree"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* AnimData */ diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 7f997109920..186222d2ca0 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -985,10 +985,12 @@ static void rna_def_pointcache_common(StructRNA *srna) prop = RNA_def_property(srna, "is_baked", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_BAKED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "", "The cache is baked"); prop = RNA_def_property(srna, "is_baking", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_BAKING); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "", "The cache is being baked"); prop = RNA_def_property(srna, "use_disk_cache", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_DISK_CACHE); @@ -999,11 +1001,13 @@ static void rna_def_pointcache_common(StructRNA *srna) prop = RNA_def_property(srna, "is_outdated", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_OUTDATED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Cache is outdated", ""); + RNA_def_property_ui_text(prop, "Cache Is Outdated", ""); prop = RNA_def_property(srna, "is_frame_skip", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_FRAMES_SKIPPED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "", "Some frames were skipped while baking/saving that cache"); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "name"); diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 21ffba074fa..c73599c19ac 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -1451,6 +1451,11 @@ static void rna_def_panel(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->translation_context"); RNA_def_property_string_default(prop, BLT_I18NCONTEXT_DEFAULT_BPYRNA); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text(prop, + "", + "Specific translation context, only define when the label needs to be " + "disambiguated from others using the exact same label"); + RNA_define_verify_sdna(true); prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE); @@ -1460,14 +1465,21 @@ static void rna_def_panel(BlenderRNA *brna) // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */ + RNA_def_property_ui_text(prop, "", "The panel tooltip"); prop = RNA_def_property(srna, "bl_category", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->category"); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text( + prop, + "", + "The category (tab) in which the panel will be displayed, when applicable"); prop = RNA_def_property(srna, "bl_owner_id", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->owner_id"); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text( + prop, "", "The ID owning the data displayed in the panel, if any"); prop = RNA_def_property(srna, "bl_space_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "type->space_type"); From debf4b70db81cff0dc22dc018c84cdcfd3eedbb7 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 3 Nov 2021 17:20:20 +0100 Subject: [PATCH 1491/1500] Cleanup: Avoid redundant template parameter in BLI serializing API The `ContainerValue` template can obtain the type of the contained value via the given `Container` type, simply using `Container::value_type`. Use this as the default way to determine the value type which simplifies using the template. If necessary the value type can be passed explicitly still. --- source/blender/blenlib/BLI_serialize.hh | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/source/blender/blenlib/BLI_serialize.hh b/source/blender/blenlib/BLI_serialize.hh index 7b8aa03b807..088243b3a30 100644 --- a/source/blender/blenlib/BLI_serialize.hh +++ b/source/blender/blenlib/BLI_serialize.hh @@ -103,11 +103,11 @@ using IntValue = PrimitiveValue; using DoubleValue = PrimitiveValue; using BooleanValue = PrimitiveValue; -template class ContainerValue; +template +class ContainerValue; /* ArrayValue stores its items as shared pointer as it shares data with a lookup table that can * be created by calling `create_lookup`. */ -using ArrayValue = - ContainerValue>, std::shared_ptr, eValueType::Array>; +using ArrayValue = ContainerValue>, eValueType::Array>; /** * Class containing a (de)serializable value. @@ -234,11 +234,11 @@ template< /** The container type where the elements are stored in. */ typename Container, - /** Type of the data inside the container. */ - typename ContainerItem, - /** ValueType representing the value (object/array). */ - eValueType V> + eValueType V, + + /** Type of the data inside the container. */ + typename ContainerItem> class ContainerValue : public Value { public: using Items = Container; @@ -275,8 +275,7 @@ using ObjectElementType = std::pair>; * Object is a key-value container where the key must be a std::string. * Internally it is stored in a blender::Vector to ensure the order of keys. */ -class ObjectValue - : public ContainerValue, ObjectElementType, eValueType::Object> { +class ObjectValue : public ContainerValue, eValueType::Object> { public: using LookupValue = std::shared_ptr; using Lookup = Map; From de2988ea1bb8692ec35e9e61d55e150ebf543d53 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 3 Nov 2021 17:28:52 +0100 Subject: [PATCH 1492/1500] Cleanup: Remove effect-less const Using `const` on an enum type returned by value doesn't have an effect. --- source/blender/blenlib/BLI_serialize.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/BLI_serialize.hh b/source/blender/blenlib/BLI_serialize.hh index 088243b3a30..051731ab801 100644 --- a/source/blender/blenlib/BLI_serialize.hh +++ b/source/blender/blenlib/BLI_serialize.hh @@ -138,7 +138,7 @@ class Value { public: virtual ~Value() = default; - const eValueType type() const + eValueType type() const { return type_; } From c5d08aa0a36608b318d80ba948b33d13d4d0f0d3 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 3 Nov 2021 17:47:05 +0100 Subject: [PATCH 1493/1500] Fix: muted nodes not handled correctly This was an error in rBb55bddde40db3eda3531d98caa99be9a8e88a8ee. --- source/blender/nodes/intern/derived_node_tree.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index fb12157f147..14d6c77299b 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -266,7 +266,7 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn, } /* The internal link only forwards the first incoming link. */ if (linked_socket->is_multi_input_socket()) { - if (linked_socket->directly_linked_links()[0] == link) { + if (linked_socket->directly_linked_links()[0] != link) { continue; } } From 1e590234f7a601ebb5ef93058ae9d4523624a614 Mon Sep 17 00:00:00 2001 From: Yevgeny Makarov Date: Wed, 3 Nov 2021 17:54:03 +0100 Subject: [PATCH 1494/1500] UI: Fix padding of version label in splash screen Differential Revision: D13018 --- source/blender/windowmanager/intern/wm_splash_screen.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_splash_screen.c b/source/blender/windowmanager/intern/wm_splash_screen.c index a4ec9e8fe6e..7513e4d31cc 100644 --- a/source/blender/windowmanager/intern/wm_splash_screen.c +++ b/source/blender/windowmanager/intern/wm_splash_screen.c @@ -212,8 +212,10 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *region, void *UNUSE UI_but_func_set(but, wm_block_close, block, NULL); - wm_block_splash_add_label( - block, BKE_blender_version_string(), splash_width, splash_height - 13.0 * U.dpi_fac); + wm_block_splash_add_label(block, + BKE_blender_version_string(), + splash_width - 8.0 * U.dpi_fac, + splash_height - 13.0 * U.dpi_fac); const int layout_margin_x = U.dpi_fac * 26; uiLayout *layout = UI_block_layout(block, From a827864e6b1ee34af759dea61f832076c0e67c44 Mon Sep 17 00:00:00 2001 From: Colin Date: Wed, 3 Nov 2021 17:53:41 +0100 Subject: [PATCH 1495/1500] Fix T89709: avoid double node links after delete and reconnect Differential Revision: https://developer.blender.org/D13062 --- source/blender/blenkernel/intern/node.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index ae9cddacc85..6b409ae656e 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -2601,6 +2601,17 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node) bNodeLink *fromlink = link->fromsock->link->fromsock->link; /* skip the node */ if (fromlink) { + if (link->tosock->flag & SOCK_MULTI_INPUT) { + /* remove the link that would be the same as the relinked one */ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link_to_compare, &ntree->links) { + if (link_to_compare->fromsock == fromlink->fromsock && + link_to_compare->tosock == link->tosock) { + adjust_multi_input_indices_after_removed_link( + ntree, link_to_compare->tosock, link_to_compare->multi_input_socket_index); + nodeRemLink(ntree, link_to_compare); + } + } + } link->fromnode = fromlink->fromnode; link->fromsock = fromlink->fromsock; From a7672caeb255e3c47071edfa42ef9805b64fca0b Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Wed, 3 Nov 2021 11:55:24 -0500 Subject: [PATCH 1496/1500] Geometry Nodes: Add Selection Input to Resample Curves Node Add a boolean input to the resample curve node that indicates which splines should be resampled and which should be unchanged. Differential Revision: https://developer.blender.org/D13064 --- .../geometry/nodes/node_geo_curve_resample.cc | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 945dac5650b..fb2021c307b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -41,6 +41,7 @@ static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) .min(0.001f) .supports_field() .subtype(PROP_DISTANCE); + b.add_input(N_("Selection")).default_value(true).supports_field(); b.add_output(N_("Curve")); } @@ -74,6 +75,7 @@ struct SampleModeParam { GeometryNodeCurveResampleMode mode; std::optional> length; std::optional> count; + Field selection; }; static SplinePtr resample_spline(const Spline &src, const int count) @@ -183,42 +185,64 @@ static std::unique_ptr resample_curve(const CurveComponent *component if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.add(*mode_param.count); + evaluator.add(mode_param.selection); evaluator.evaluate(); const VArray &cuts = evaluator.get_evaluated(0); + const VArray &selections = evaluator.get_evaluated(1); threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { BLI_assert(mode_param.count); - output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1)); + if (selections[i]) { + output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1)); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.add(*mode_param.length); + evaluator.add(mode_param.selection); evaluator.evaluate(); const VArray &lengths = evaluator.get_evaluated(0); + const VArray &selections = evaluator.get_evaluated(1); threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - /* Don't allow asymptotic count increase for low resolution values. */ - const float divide_length = std::max(lengths[i], 0.0001f); - const float spline_length = input_splines[i]->length(); - const int count = std::max(int(spline_length / divide_length) + 1, 1); - output_splines[i] = resample_spline(*input_splines[i], count); + if (selections[i]) { + /* Don't allow asymptotic count increase for low resolution values. */ + const float divide_length = std::max(lengths[i], 0.0001f); + const float spline_length = input_splines[i]->length(); + const int count = std::max(int(spline_length / divide_length) + 1, 1); + output_splines[i] = resample_spline(*input_splines[i], count); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_EVALUATED) { + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(mode_param.selection); + evaluator.evaluate(); + const VArray &selections = evaluator.get_evaluated(0); + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - output_splines[i] = resample_spline_evaluated(*input_splines[i]); + if (selections[i]) { + output_splines[i] = resample_spline_evaluated(*input_splines[i]); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } - output_curve->attributes = input_curve->attributes; - return output_curve; } @@ -244,6 +268,8 @@ static void geo_node_resample_exec(GeoNodeExecParams params) SampleModeParam mode_param; mode_param.mode = mode; + mode_param.selection = params.extract_input>("Selection"); + if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { Field count = params.extract_input>("Count"); if (count < 1) { From d17128520d17733073b87b184d09cbd6057d28d5 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 3 Nov 2021 17:56:25 +0100 Subject: [PATCH 1497/1500] Simplification: Use generic `BKE_object_materials_test` in object liblink code. Better avoid own specific logic here, when we already have a proper 'API' function for that. --- source/blender/blenkernel/intern/object.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 933e83d4f7c..dc6ef580408 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -849,6 +849,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) { Object *ob = (Object *)id; + Main *bmain = BLO_read_lib_get_main(reader); BlendFileReadReport *reports = BLO_read_lib_reports(reader); /* XXX deprecated - old animation system <<< */ @@ -945,12 +946,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) /* When the object is local and the data is library its possible * the material list size gets out of sync. T22663. */ if (ob->data && ob->id.lib != ((ID *)ob->data)->lib) { - const short *totcol_data = BKE_object_material_len_p(ob); - /* Only expand so as not to lose any object materials that might be set. */ - if (totcol_data && (*totcol_data > ob->totcol)) { - // printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); - BKE_object_material_resize(BLO_read_lib_get_main(reader), ob, *totcol_data, false); - } + BKE_object_materials_test(bmain, ob, ob->data); } BLO_read_id_address(reader, ob->id.lib, &ob->gpd); From c29f9e14e4180af0fbba83d66e08c9852da7fa9c Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 3 Nov 2021 17:57:45 +0100 Subject: [PATCH 1498/1500] Fix T92780: Lost material in case of local object, and missing linked obdata. By default, when syncing materials slots between object and its obdata, the amount of slots in obdata is the reference. Missing linked obdata is replaced by an empty placeholder that has no material. In that specific case, if we have a valid object ID, we want to update the (placeholder) obdata's material count from the object one, and not the other way around. --- source/blender/blenkernel/intern/material.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index d3b34639d8a..d82559a27cb 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -900,7 +900,17 @@ void BKE_object_materials_test(Main *bmain, Object *ob, ID *id) return; } - BKE_object_material_resize(bmain, ob, *totcol, false); + if ((ob->id.tag & LIB_TAG_MISSING) == 0 && (id->tag & LIB_TAG_MISSING) != 0) { + /* Exception: In case the object is a valid data, but its obdata is an empty place-holder, + * use object's material slots amount as reference. + * This avoids loosing materials in a local object when its linked obdata gets missing. + * See T92780. */ + BKE_id_material_resize(bmain, id, (short)ob->totcol, false); + } + else { + /* Normal case: the use the obdata amount of materials slots to update the object's one. */ + BKE_object_material_resize(bmain, ob, *totcol, false); + } } void BKE_objects_materials_test_all(Main *bmain, ID *id) From 4e5537d841e426c9d7fd731b7b6d5033c7e5fb65 Mon Sep 17 00:00:00 2001 From: Nikhil Shringarpurey Date: Wed, 3 Nov 2021 13:25:44 -0500 Subject: [PATCH 1499/1500] Geometry Nodes: Add tooltips to primitive node inputs Building on the work in rBef45399f3be0, this commits adds tooltips to the inputs for the default primitives nodes. Differential Revision: https://developer.blender.org/D12640 --- ...node_geo_curve_primitive_bezier_segment.cc | 22 +++++++-- .../nodes/node_geo_curve_primitive_circle.cc | 27 +++++++++-- .../nodes/node_geo_curve_primitive_line.cc | 19 ++++++-- ...de_geo_curve_primitive_quadratic_bezier.cc | 14 ++++-- .../node_geo_curve_primitive_quadrilateral.cc | 48 ++++++++++++++----- .../nodes/node_geo_curve_primitive_spiral.cc | 28 ++++++++--- .../nodes/node_geo_curve_primitive_star.cc | 17 +++++-- .../nodes/node_geo_mesh_primitive_circle.cc | 11 ++++- .../nodes/node_geo_mesh_primitive_cone.cc | 32 ++++++++++--- .../nodes/node_geo_mesh_primitive_cube.cc | 21 ++++++-- .../nodes/node_geo_mesh_primitive_cylinder.cc | 8 ++-- .../nodes/node_geo_mesh_primitive_grid.cc | 24 ++++++++-- .../node_geo_mesh_primitive_ico_sphere.cc | 12 ++++- .../nodes/node_geo_mesh_primitive_line.cc | 21 ++++++-- .../node_geo_mesh_primitive_uv_sphere.cc | 18 +++++-- 15 files changed, 254 insertions(+), 68 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index a755d47cc6a..673a5095044 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -29,15 +29,27 @@ static void geo_node_curve_primitive_bezier_segment_declare(NodeDeclarationBuild .default_value(16) .min(1) .max(256) - .subtype(PROP_UNSIGNED); + .subtype(PROP_UNSIGNED) + .description(N_("The number of evaluated points on the curve")); b.add_input(N_("Start")) .default_value({-1.0f, 0.0f, 0.0f}) - .subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description(N_("Position of the start control point of the curve")); b.add_input(N_("Start Handle")) .default_value({-0.5f, 0.5f, 0.0f}) - .subtype(PROP_TRANSLATION); - b.add_input(N_("End Handle")).subtype(PROP_TRANSLATION); - b.add_input(N_("End")).default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description( + N_("Position of the start handle used to define the shape of the curve. In Offset mode, " + "relative to Start point")); + b.add_input(N_("End Handle")) + .subtype(PROP_TRANSLATION) + .description( + N_("Position of the end handle used to define the shape of the curve. In Offset mode, " + "relative to End point")); + b.add_input(N_("End")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the end control point of the curve")); b.add_output(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index bf4f22d6578..ffede480c75 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -25,17 +25,34 @@ namespace blender::nodes { static void geo_node_curve_primitive_circle_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Resolution")).default_value(32).min(3).max(512); + b.add_input(N_("Resolution")) + .default_value(32) + .min(3) + .max(512) + .description(N_("Number of points on the circle")); b.add_input(N_("Point 1")) .default_value({-1.0f, 0.0f, 0.0f}) - .subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); b.add_input(N_("Point 2")) .default_value({0.0f, 1.0f, 0.0f}) - .subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); b.add_input(N_("Point 3")) .default_value({1.0f, 0.0f, 0.0f}) - .subtype(PROP_TRANSLATION); - b.add_input(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); + b.add_input(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance of the points from the origin")); b.add_output(N_("Curve")); b.add_output(N_("Center")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index 5b215797052..37a5989d3f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -25,10 +25,21 @@ namespace blender::nodes { static void geo_node_curve_primitive_line_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Start")).subtype(PROP_TRANSLATION); - b.add_input(N_("End")).default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_input(N_("Direction")).default_value({0.0f, 0.0f, 1.0f}); - b.add_input(N_("Length")).default_value(1.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Start")) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first control point")); + b.add_input(N_("End")) + .default_value({0.0f, 0.0f, 1.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the second control point")); + b.add_input(N_("Direction")) + .default_value({0.0f, 0.0f, 1.0f}) + .description( + N_("Direction the line is going in. The length of this vector does not matter")); + b.add_input(N_("Length")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance between the two points")); b.add_output(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc index 6041ddee02d..27bf4a310df 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc @@ -25,14 +25,20 @@ static void geo_node_curve_primitive_quadratic_bezier_declare(NodeDeclarationBui .default_value(16) .min(3) .max(256) - .subtype(PROP_UNSIGNED); + .subtype(PROP_UNSIGNED) + .description(N_("The number of edges on the curve")); b.add_input(N_("Start")) .default_value({-1.0f, 0.0f, 0.0f}) - .subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first control point")); b.add_input(N_("Middle")) .default_value({0.0f, 2.0f, 0.0f}) - .subtype(PROP_TRANSLATION); - b.add_input(N_("End")).default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description(N_("Position of the middle control point")); + b.add_input(N_("End")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the last control point")); b.add_output(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index 7260da05a8d..e00a502bf32 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -23,31 +23,57 @@ namespace blender::nodes { static void geo_node_curve_primitive_quadrilateral_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Width")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input(N_("Height")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Width")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input(N_("Height")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The Y axis size of the shape")); b.add_input(N_("Bottom Width")) .default_value(4.0f) .min(0.0f) - .subtype(PROP_DISTANCE); - b.add_input(N_("Top Width")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input(N_("Offset")).default_value(1.0f).subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input(N_("Top Width")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input(N_("Offset")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description( + N_("For Parallelogram, the relative X difference between the top and bottom edges. For " + "Trapezoid, the amount to move the top edge in the positive X axis")); b.add_input(N_("Bottom Height")) .default_value(3.0f) .min(0.0f) - .subtype(PROP_DISTANCE); - b.add_input(N_("Top Height")).default_value(1.0f).subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The distance between the bottom point and the X axis")); + b.add_input(N_("Top Height")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description(N_("The distance between the top point and the X axis")); b.add_input(N_("Point 1")) .default_value({-1.0f, -1.0f, 0.0f}) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); b.add_input(N_("Point 2")) .default_value({1.0f, -1.0f, 0.0f}) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); b.add_input(N_("Point 3")) .default_value({1.0f, 1.0f, 0.0f}) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); b.add_input(N_("Point 4")) .default_value({-1.0f, 1.0f, 0.0f}) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); b.add_output(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index 1dc9cd7f107..2a872fd82cb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -26,12 +26,28 @@ static void geo_node_curve_primitive_spiral_declare(NodeDeclarationBuilder &b) .default_value(32) .min(1) .max(1024) - .subtype(PROP_UNSIGNED); - b.add_input(N_("Rotations")).default_value(2.0f).min(0.0f); - b.add_input(N_("Start Radius")).default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input(N_("End Radius")).default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input(N_("Height")).default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input(N_("Reverse")); + .subtype(PROP_UNSIGNED) + .description(N_("Number of points in one rotation of the spiral")); + b.add_input(N_("Rotations")) + .default_value(2.0f) + .min(0.0f) + .description(N_("Number of times the spiral makes a full rotation")); + b.add_input(N_("Start Radius")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description( + N_("Horizontal Distance from the Z axis at the start of the spiral")); + b.add_input(N_("End Radius")) + .default_value(2.0f) + .subtype(PROP_DISTANCE) + .description( + N_("Horizontal Distance from the Z axis at the end of the spiral")); + b.add_input(N_("Height")) + .default_value(2.0f) + .subtype(PROP_DISTANCE) + .description(N_("The height perpendicular to the base of the spiral")); + b.add_input(N_("Reverse")) + .description(N_("Switch the direction from clockwise to counterclockwise")); b.add_output(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index b5bafce17c6..7f682b198b3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -22,16 +22,25 @@ namespace blender::nodes { static void geo_node_curve_primitive_star_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Points")).default_value(8).min(3).max(256).subtype(PROP_UNSIGNED); + b.add_input(N_("Points")) + .default_value(8) + .min(3) + .max(256) + .subtype(PROP_UNSIGNED) + .description(N_("Number of points on each of the circles")); b.add_input(N_("Inner Radius")) .default_value(1.0f) .min(0.0f) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("Radius of the inner circle; can be larger than outer radius")); b.add_input(N_("Outer Radius")) .default_value(2.0f) .min(0.0f) - .subtype(PROP_DISTANCE); - b.add_input(N_("Twist")).subtype(PROP_ANGLE); + .subtype(PROP_DISTANCE) + .description(N_("Radius of the outer circle; can be smaller than inner radius")); + b.add_input(N_("Twist")) + .subtype(PROP_ANGLE) + .description(N_("The counterclockwise rotation of the inner set of points")); b.add_output(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 685a8faff5c..f1f95be107a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -29,8 +29,15 @@ namespace blender::nodes { static void geo_node_mesh_primitive_circle_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Vertices")).default_value(32).min(3); - b.add_input(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Vertices")) + .default_value(32) + .min(3) + .description(N_("Number of vertices on the circle")); + b.add_input(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance of the vertices from the origin")); b.add_output(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 206d48d40c8..77dfeed6cbb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -29,15 +29,35 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Vertices")).default_value(32).min(3).max(512); - b.add_input(N_("Side Segments")).default_value(1).min(1).max(512); - b.add_input(N_("Fill Segments")).default_value(1).min(1).max(512); - b.add_input(N_("Radius Top")).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Vertices")) + .default_value(32) + .min(3) + .max(512) + .description(N_("Number of points on the circle at the top and bottom")); + b.add_input(N_("Side Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("The number of edges running vertically along the side of the cone")); + b.add_input(N_("Fill Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("Number of concentric rings used to fill the round face")); + b.add_input(N_("Radius Top")) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Radius of the top circle of the cone")); b.add_input(N_("Radius Bottom")) .default_value(1.0f) .min(0.0f) - .subtype(PROP_DISTANCE); - b.add_input(N_("Depth")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("Radius of the bottom circle of the cone")); + b.add_input(N_("Depth")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Height of the generated cone")); b.add_output(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 3a211993bdc..b5903f7b71e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -29,10 +29,23 @@ static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b) b.add_input(N_("Size")) .default_value(float3(1)) .min(0.0f) - .subtype(PROP_TRANSLATION); - b.add_input(N_("Vertices X")).default_value(2).min(2).max(1000); - b.add_input(N_("Vertices Y")).default_value(2).min(2).max(1000); - b.add_input(N_("Vertices Z")).default_value(2).min(2).max(1000); + .subtype(PROP_TRANSLATION) + .description(N_("Side length along each axis")); + b.add_input(N_("Vertices X")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the X side of the shape")); + b.add_input(N_("Vertices Y")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the Y side of the shape")); + b.add_input(N_("Vertices Z")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the Z side of the shape")); b.add_output(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index 3bcf42b40b1..6edc9714f42 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -33,17 +33,17 @@ static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) .default_value(32) .min(3) .max(512) - .description(N_("The number of vertices around the circumference")); + .description(N_("The number of vertices on the top and bottom circles")); b.add_input(N_("Side Segments")) .default_value(1) .min(1) .max(512) - .description(N_("The number of segments along the side")); + .description(N_("The number of rectangular segments along each side")); b.add_input(N_("Fill Segments")) .default_value(1) .min(1) .max(512) - .description(N_("The number of concentric segments of the fill")); + .description(N_("The number of concentric rings used to fill the round faces")); b.add_input(N_("Radius")) .default_value(1.0f) .min(0.0f) @@ -53,7 +53,7 @@ static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) .default_value(2.0f) .min(0.0f) .subtype(PROP_DISTANCE) - .description(N_("The height of the cylinder on the Z axis")); + .description(N_("The height of the cylinder")); b.add_output(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index c4e476981c1..73c679e18f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -29,10 +29,26 @@ namespace blender::nodes { static void geo_node_mesh_primitive_grid_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Size X")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input(N_("Size Y")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input(N_("Vertices X")).default_value(3).min(2).max(1000); - b.add_input(N_("Vertices Y")).default_value(3).min(2).max(1000); + b.add_input(N_("Size X")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Side length of the plane in the X direction")); + b.add_input(N_("Size Y")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Side length of the plane in the Y direction")); + b.add_input(N_("Vertices X")) + .default_value(3) + .min(2) + .max(1000) + .description(N_("Number of vertices in the X direction")); + b.add_input(N_("Vertices Y")) + .default_value(3) + .min(2) + .max(1000) + .description(N_("Number of vertices in the Y direction")); b.add_output(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index da3dfef3aea..f49cc5e7e18 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -28,8 +28,16 @@ namespace blender::nodes { static void geo_node_mesh_primitive_ico_sphere_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input(N_("Subdivisions")).default_value(1).min(1).max(7); + b.add_input(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance from the generated points to the origin")); + b.add_input(N_("Subdivisions")) + .default_value(1) + .min(1) + .max(7) + .description(N_("Number of subdivisions on top of the basic icosahedron")); b.add_output(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 6515afe5966..df4efb2427c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -29,12 +29,25 @@ namespace blender::nodes { static void geo_node_mesh_primitive_line_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Count")).default_value(10).min(1).max(10000); - b.add_input(N_("Resolution")).default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE); - b.add_input(N_("Start Location")).subtype(PROP_TRANSLATION); + b.add_input(N_("Count")) + .default_value(10) + .min(1) + .max(10000) + .description(N_("Number of vertices on the line")); + b.add_input(N_("Resolution")) + .default_value(1.0f) + .min(0.1f) + .subtype(PROP_DISTANCE) + .description(N_("Length of each individual edge")); + b.add_input(N_("Start Location")) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first vertex")); b.add_input(N_("Offset")) .default_value({0.0f, 0.0f, 1.0f}) - .subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description(N_( + "In offset mode, the distance between each socket on each axis. In end points mode, the " + "position of the final vertex")); b.add_output(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 54a762fc15d..3197a94c27b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -29,9 +29,21 @@ namespace blender::nodes { static void geo_node_mesh_primitive_uv_shpere_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Segments")).default_value(32).min(3).max(1024); - b.add_input(N_("Rings")).default_value(16).min(2).max(1024); - b.add_input(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Segments")) + .default_value(32) + .min(3) + .max(1024) + .description(N_("Horizontal resolution of the sphere")); + b.add_input(N_("Rings")) + .default_value(16) + .min(2) + .max(1024) + .description(N_("The number of horizontal rings")); + b.add_input(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance from the generated points to the origin")); b.add_output(N_("Mesh")); } From ccead2ed9c6121c42a516712da38a2faec877e2f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 3 Nov 2021 13:45:51 -0500 Subject: [PATCH 1500/1500] Spreadsheet: Display geometry volume component grids This shows a geometry's volume grids in the spreadsheet. Three columns are displayed: - Name: The text name of each grid - Data type: Float, Vector, etc. - Class: Fog volume, Level Set, or unkown In the future, values of the voxels themselves could be displayed, but that is a much more complex problem, with important performance implications, etc. Differential Revision: https://developer.blender.org/D13049 --- .../editors/space_spreadsheet/CMakeLists.txt | 10 ++ .../spreadsheet_cell_value.hh | 1 + .../spreadsheet_data_source_geometry.cc | 95 +++++++++++++++++++ .../spreadsheet_data_source_geometry.hh | 20 ++++ .../spreadsheet_dataset_draw.cc | 17 +++- .../spreadsheet_dataset_layout.cc | 6 ++ .../space_spreadsheet/spreadsheet_layout.cc | 17 ++++ source/blender/makesdna/DNA_space_types.h | 1 + source/blender/makesrna/RNA_enum_items.h | 2 + source/blender/makesrna/intern/rna_volume.c | 42 ++++---- 10 files changed, 188 insertions(+), 23 deletions(-) diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt index 91fe1bc01b7..192b80881ee 100644 --- a/source/blender/editors/space_spreadsheet/CMakeLists.txt +++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt @@ -67,4 +67,14 @@ set(SRC set(LIB ) +if(WITH_OPENVDB) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${OPENVDB_LIBRARIES} + ) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) +endif() + blender_add_lib(bf_editor_space_spreadsheet "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index 97170693cb3..c11b4a2b23d 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -58,6 +58,7 @@ class CellValue { std::optional value_object; std::optional value_collection; std::optional value_geometry_set; + std::optional value_string; }; } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index c1d345d1861..04784dc3e2b 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -20,6 +20,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" +#include "BKE_volume.h" #include "DNA_ID.h" #include "DNA_mesh_types.h" @@ -33,6 +34,11 @@ #include "NOD_geometry_nodes_eval_log.hh" +#include "BLT_translation.h" + +#include "RNA_access.h" +#include "RNA_enum_types.h" + #include "FN_field_cpp_type.hh" #include "bmesh.h" @@ -112,6 +118,9 @@ std::unique_ptr ExtraColumns::get_column_values( r_cell_value.value_color = *( const ColorGeometry4f *)value; break; + case SPREADSHEET_VALUE_TYPE_STRING: + r_cell_value.value_string = *(const std::string *)value; + break; case SPREADSHEET_VALUE_TYPE_INSTANCES: break; } @@ -487,6 +496,89 @@ int InstancesDataSource::tot_rows() const return component_->instances_amount(); } +void VolumeDataSource::foreach_default_column_ids( + FunctionRef fn) const +{ + if (component_->is_empty()) { + return; + } + + for (const char *name : {"Grid Name", "Data Type", "Class"}) { + SpreadsheetColumnID column_id{(char *)name}; + fn(column_id, false); + } +} + +std::unique_ptr VolumeDataSource::get_column_values( + const SpreadsheetColumnID &column_id) const +{ + const Volume *volume = component_->get_for_read(); + if (volume == nullptr) { + return {}; + } + +#ifdef WITH_OPENVDB + const int size = this->tot_rows(); + if (STREQ(column_id.name, "Grid Name")) { + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_STRING, + IFACE_("Grid Name"), + size, + [volume](int index, CellValue &r_cell_value) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index); + r_cell_value.value_string = BKE_volume_grid_name(volume_grid); + }, + 6.0f); + } + if (STREQ(column_id.name, "Data Type")) { + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_STRING, + IFACE_("Type"), + size, + [volume](int index, CellValue &r_cell_value) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index); + const VolumeGridType type = BKE_volume_grid_type(volume_grid); + const char *name = nullptr; + RNA_enum_name_from_value(rna_enum_volume_grid_data_type_items, type, &name); + r_cell_value.value_string = IFACE_(name); + }, + 5.0f); + } + if (STREQ(column_id.name, "Class")) { + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_STRING, + IFACE_("Class"), + size, + [volume](int index, CellValue &r_cell_value) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index); + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + openvdb::GridClass grid_class = grid->getGridClass(); + if (grid_class == openvdb::GridClass::GRID_FOG_VOLUME) { + r_cell_value.value_string = IFACE_("Fog Volume"); + } + else if (grid_class == openvdb::GridClass::GRID_LEVEL_SET) { + r_cell_value.value_string = IFACE_("Level Set"); + } + else { + r_cell_value.value_string = IFACE_("Unkown"); + } + }, + 5.0f); + } +#endif + + return {}; +} + +int VolumeDataSource::tot_rows() const +{ + const Volume *volume = component_->get_for_read(); + if (volume == nullptr) { + return 0; + } + return BKE_volume_num_grids(volume); +} + GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspreadsheet, Object *object_eval, const GeometryComponentType used_component_type) @@ -682,6 +774,9 @@ std::unique_ptr data_source_from_geometry(const bContext *C, Object if (component_type == GEO_COMPONENT_TYPE_INSTANCES) { return std::make_unique(geometry_set, std::move(extra_columns)); } + if (component_type == GEO_COMPONENT_TYPE_VOLUME) { + return std::make_unique(geometry_set); + } return std::make_unique( object_eval, geometry_set, component_type, domain, std::move(extra_columns)); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh index 6c88a94f585..a4114dd1f6a 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -116,6 +116,26 @@ class InstancesDataSource : public DataSource { int tot_rows() const override; }; +class VolumeDataSource : public DataSource { + const GeometrySet geometry_set_; + const VolumeComponent *component_; + + public: + VolumeDataSource(GeometrySet geometry_set) + : geometry_set_(std::move(geometry_set)), + component_(geometry_set_.get_component_for_read()) + { + } + + void foreach_default_column_ids( + FunctionRef fn) const override; + + std::unique_ptr get_column_values( + const SpreadsheetColumnID &column_id) const override; + + int tot_rows() const override; +}; + std::unique_ptr data_source_from_geometry(const bContext *C, Object *object_eval); } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index 4cf6d14cbda..b9a508d7764 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -20,6 +20,7 @@ #include "DNA_windowmanager_types.h" #include "BKE_context.h" +#include "BKE_volume.h" #include "BLF_api.h" @@ -48,7 +49,7 @@ static int is_component_row_selected(struct uiBut *but, const void *arg) const bool is_domain_selected = (AttributeDomain)sspreadsheet->attribute_domain == domain; bool is_selected = is_component_selected && is_domain_selected; - if (component == GEO_COMPONENT_TYPE_INSTANCES) { + if (ELEM(component, GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES)) { is_selected = is_component_selected; } @@ -141,6 +142,14 @@ static int element_count_from_instances(const GeometrySet &geometry_set) return 0; } +static int element_count_from_volume(const GeometrySet &geometry_set) +{ + if (const Volume *volume = geometry_set.get_volume_for_read()) { + return BKE_volume_num_grids(volume); + } + return 0; +} + static int element_count_from_component_domain(const GeometrySet &geometry_set, GeometryComponentType component, AttributeDomain domain) @@ -191,6 +200,10 @@ void DatasetRegionDrawer::draw_dataset_row(const int indentation, BLI_str_format_attribute_domain_size( element_count, element_count_from_instances(draw_context.current_geometry_set)); } + if (component == GEO_COMPONENT_TYPE_VOLUME) { + BLI_str_format_attribute_domain_size( + element_count, element_count_from_volume(draw_context.current_geometry_set)); + } else { BLI_str_format_attribute_domain_size( element_count, @@ -237,7 +250,7 @@ void DatasetRegionDrawer::draw_dataset_row(const int indentation, void DatasetRegionDrawer::draw_component_row(const DatasetComponentLayoutInfo &component_info) { - if (component_info.type == GEO_COMPONENT_TYPE_INSTANCES) { + if (ELEM(component_info.type, GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES)) { draw_dataset_row( 0, component_info.type, std::nullopt, component_info.icon, component_info.label, true); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc index abbad8c7088..f15af2e4d32 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc @@ -74,6 +74,12 @@ constexpr DatasetComponentLayoutInfo DATASET_layout_hierarchy[] = { ATTR_INFO(ATTR_DOMAIN_POINT, N_("Point"), ICON_PARTICLE_POINT), }, }, + { + GEO_COMPONENT_TYPE_VOLUME, + N_("Volume Grids"), + ICON_VOLUME_DATA, + {}, + }, { GEO_COMPONENT_TYPE_INSTANCES, N_("Instances"), diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index 355899be279..202523c0e64 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -228,6 +228,23 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { 0, nullptr); } + else if (cell_value.value_string.has_value()) { + uiDefIconTextBut(params.block, + UI_BTYPE_LABEL, + 0, + ICON_NONE, + cell_value.value_string->c_str(), + params.xmin, + params.ymin, + params.width, + params.height, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + } } void draw_float_vector(const CellDrawParams ¶ms, const Span values) const diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 5ebc81fff4f..671cc182d7e 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -2006,6 +2006,7 @@ typedef enum eSpreadsheetColumnValueType { SPREADSHEET_VALUE_TYPE_FLOAT3 = 4, SPREADSHEET_VALUE_TYPE_COLOR = 5, SPREADSHEET_VALUE_TYPE_INSTANCES = 6, + SPREADSHEET_VALUE_TYPE_STRING = 7, } eSpreadsheetColumnValueType; /** diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index f3e15d08fa3..fb18802483d 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -211,6 +211,8 @@ DEF_ENUM(rna_enum_attribute_domain_items) DEF_ENUM(rna_enum_attribute_domain_without_corner_items) DEF_ENUM(rna_enum_attribute_domain_with_auto_items) +DEF_ENUM(rna_enum_volume_grid_data_type_items) + DEF_ENUM(rna_enum_collection_color_items) DEF_ENUM(rna_enum_strip_color_items) diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c index f6b8b55688c..3100c1195f4 100644 --- a/source/blender/makesrna/intern/rna_volume.c +++ b/source/blender/makesrna/intern/rna_volume.c @@ -33,6 +33,26 @@ #include "BLI_math_base.h" +const EnumPropertyItem rna_enum_volume_grid_data_type_items[] = { + {VOLUME_GRID_BOOLEAN, "BOOLEAN", 0, "Boolean", "Boolean"}, + {VOLUME_GRID_FLOAT, "FLOAT", 0, "Float", "Single precision float"}, + {VOLUME_GRID_DOUBLE, "DOUBLE", 0, "Double", "Double precision"}, + {VOLUME_GRID_INT, "INT", 0, "Integer", "32-bit integer"}, + {VOLUME_GRID_INT64, "INT64", 0, "Integer 64-bit", "64-bit integer"}, + {VOLUME_GRID_MASK, "MASK", 0, "Mask", "No data, boolean mask of active voxels"}, + {VOLUME_GRID_STRING, "STRING", 0, "String", "Text string"}, + {VOLUME_GRID_VECTOR_FLOAT, "VECTOR_FLOAT", 0, "Float Vector", "3D float vector"}, + {VOLUME_GRID_VECTOR_DOUBLE, "VECTOR_DOUBLE", 0, "Double Vector", "3D double vector"}, + {VOLUME_GRID_VECTOR_INT, "VECTOR_INT", 0, "Integer Vector", "3D integer vector"}, + {VOLUME_GRID_POINTS, + "POINTS", + 0, + "Points (Unsupported)", + "Points grid, currently unsupported by volume objects"}, + {VOLUME_GRID_UNKNOWN, "UNKNOWN", 0, "Unknown", "Unsupported data type"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "DEG_depsgraph.h" @@ -244,30 +264,10 @@ static void rna_def_volume_grid(BlenderRNA *brna) prop, "rna_VolumeGrid_name_get", "rna_VolumeGrid_name_length", NULL); RNA_def_property_ui_text(prop, "Name", "Volume grid name"); - static const EnumPropertyItem data_type_items[] = { - {VOLUME_GRID_BOOLEAN, "BOOLEAN", 0, "Boolean", "Boolean"}, - {VOLUME_GRID_FLOAT, "FLOAT", 0, "Float", "Single precision float"}, - {VOLUME_GRID_DOUBLE, "DOUBLE", 0, "Double", "Double precision"}, - {VOLUME_GRID_INT, "INT", 0, "Integer", "32-bit integer"}, - {VOLUME_GRID_INT64, "INT64", 0, "Integer 64-bit", "64-bit integer"}, - {VOLUME_GRID_MASK, "MASK", 0, "Mask", "No data, boolean mask of active voxels"}, - {VOLUME_GRID_STRING, "STRING", 0, "String", "Text string"}, - {VOLUME_GRID_VECTOR_FLOAT, "VECTOR_FLOAT", 0, "Float Vector", "3D float vector"}, - {VOLUME_GRID_VECTOR_DOUBLE, "VECTOR_DOUBLE", 0, "Double Vector", "3D double vector"}, - {VOLUME_GRID_VECTOR_INT, "VECTOR_INT", 0, "Integer Vector", "3D integer vector"}, - {VOLUME_GRID_POINTS, - "POINTS", - 0, - "Points (Unsupported)", - "Points grid, currently unsupported by volume objects"}, - {VOLUME_GRID_UNKNOWN, "UNKNOWN", 0, "Unknown", "Unsupported data type"}, - {0, NULL, 0, NULL, NULL}, - }; - prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_enum_funcs(prop, "rna_VolumeGrid_data_type_get", NULL, NULL); - RNA_def_property_enum_items(prop, data_type_items); + RNA_def_property_enum_items(prop, rna_enum_volume_grid_data_type_items); RNA_def_property_ui_text(prop, "Data Type", "Data type of voxel values"); prop = RNA_def_property(srna, "channels", PROP_INT, PROP_UNSIGNED);